Skip to content

Commit af5b277

Browse files
committed
fix(agents): use currentAttemptAssistant for incomplete-turn classification
1 parent 13d4741 commit af5b277

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
@@ -1379,6 +1379,77 @@ describe("runEmbeddedAgent incomplete-turn safety", () => {
13791379
).toBe(true);
13801380
});
13811381

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