Skip to content

Commit 9bf7040

Browse files
committed
fix(agents): use currentAttemptAssistant for incomplete-turn classification
Fixes #80918
1 parent 65aec96 commit 9bf7040

2 files changed

Lines changed: 72 additions & 1 deletion

File tree

src/agents/embedded-agent-runner/run.incomplete-turn.test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,6 +1381,77 @@ describe("runEmbeddedAgent incomplete-turn safety", () => {
13811381
).toBe(true);
13821382
});
13831383

1384+
it("uses currentAttemptAssistant over stale lastAssistant for incomplete-turn classification (#80918)", () => {
1385+
expect(
1386+
isIncompleteTerminalAssistantTurn({
1387+
hasAssistantVisibleText: true,
1388+
lastAssistant: { stopReason: "stop" } as unknown as EmbeddedRunAttemptResult["currentAttemptAssistant"],
1389+
}),
1390+
).toBe(false);
1391+
expect(
1392+
isIncompleteTerminalAssistantTurn({
1393+
hasAssistantVisibleText: false,
1394+
lastAssistant: { stopReason: "toolUse" },
1395+
}),
1396+
).toBe(true);
1397+
});
1398+
1399+
it("does not flag stale lastAssistant=toolUse when currentAttemptAssistant=stop exists (#80918)", () => {
1400+
const incompleteTurnText = resolveIncompleteTurnPayloadText({
1401+
payloadCount: 1,
1402+
aborted: false,
1403+
timedOut: false,
1404+
attempt: makeAttemptResult({
1405+
assistantTexts: ["Analysis...", "Here is the final answer after update_plan."],
1406+
toolMetas: [{ toolName: "update_plan" }],
1407+
lastAssistant: {
1408+
role: "assistant",
1409+
stopReason: "toolUse",
1410+
provider: "openai",
1411+
model: "gpt-5.5",
1412+
content: [
1413+
{ type: "text", text: "Analysis..." },
1414+
{ type: "tool_use", id: "tool_1", name: "update_plan", input: {} },
1415+
],
1416+
} as unknown as EmbeddedRunAttemptResult["lastAssistant"],
1417+
currentAttemptAssistant: {
1418+
role: "assistant",
1419+
stopReason: "stop",
1420+
provider: "openai",
1421+
model: "gpt-5.5",
1422+
content: [{ type: "text", text: "Here is the final answer after update_plan." }],
1423+
} as unknown as EmbeddedRunAttemptResult["currentAttemptAssistant"],
1424+
}),
1425+
});
1426+
1427+
expect(incompleteTurnText).toBeNull();
1428+
});
1429+
1430+
it("still flags incomplete-turn when currentAttemptAssistant is absent and lastAssistant=toolUse (#76477 regression)", () => {
1431+
const incompleteTurnText = resolveIncompleteTurnPayloadText({
1432+
payloadCount: 1,
1433+
aborted: false,
1434+
timedOut: false,
1435+
attempt: makeAttemptResult({
1436+
assistantTexts: ["Let me update the file..."],
1437+
toolMetas: [{ toolName: "write" }],
1438+
lastAssistant: {
1439+
role: "assistant",
1440+
stopReason: "toolUse",
1441+
provider: "openai",
1442+
model: "gpt-5.4",
1443+
content: [
1444+
{ type: "text", text: "Let me update the file..." },
1445+
{ type: "tool_use", id: "tool_1", name: "write", input: {} },
1446+
],
1447+
} as unknown as EmbeddedRunAttemptResult["lastAssistant"],
1448+
currentAttemptAssistant: undefined,
1449+
}),
1450+
});
1451+
1452+
expect(incompleteTurnText).toContain("couldn't generate a response");
1453+
});
1454+
13841455
it("surfaces no-visible-answer recovery for app-server interrupted tool-only output", () => {
13851456
const interruptedToolOnlyAttempt = makeAttemptResult({
13861457
assistantTexts: [],

src/agents/embedded-agent-runner/run/incomplete-turn.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ export function resolveIncompleteTurnPayloadText(params: {
285285
const incompleteTerminalAssistant = isIncompleteTerminalAssistantTurn({
286286
hasAssistantVisibleText: params.payloadCount > 0,
287287
hasTerminalOutput,
288-
lastAssistant: params.attempt.lastAssistant,
288+
lastAssistant: assistant,
289289
});
290290
const reasoningOnlyAssistant = isReasoningOnlyAssistantTurn(assistant);
291291
const emptyResponseAssistant = isEmptyResponseAssistantTurn({

0 commit comments

Comments
 (0)