From f20ccfb38ae7e96d288b5852e55af70d66b93a72 Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 18 Sep 2026 22:12:32 -0500 Subject: [PATCH] fix(runner): clear failed tool items before governed suspension Co-Authored-By: Paperclip --- .../native-runtime/native-session-executor.test.ts | 5 +++++ .../services/native-runtime/native-session-executor.ts | 9 ++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/server/src/services/native-runtime/native-session-executor.test.ts b/server/src/services/native-runtime/native-session-executor.test.ts index 7ab7238f8c..9b62a06ec8 100644 --- a/server/src/services/native-runtime/native-session-executor.test.ts +++ b/server/src/services/native-runtime/native-session-executor.test.ts @@ -4128,6 +4128,11 @@ describe("native governed waits", () => { payload: { kind: "dynamicToolCall" }, }; + // A failed tool is terminal too, even when its error event omits kind. + // It must not block the later approval tool from parking this run. + await observation.observe({ ...replayedEvent, eventType: "item.started", itemId: "failed-command", payload: { kind: "commandExecution" } }, false); + await observation.observe({ ...replayedEvent, eventType: "item.failed", itemId: "failed-command", payload: { error: "Command exited with status 1" } }, false); + // A usage event must not park while the card-creation response is held. await observation.observe({ ...replayedEvent, eventType: "item.started", itemId: "approval-tool" }, false); const usage = { ...replayedEvent, payload: { kind: "usage" } }; diff --git a/server/src/services/native-runtime/native-session-executor.ts b/server/src/services/native-runtime/native-session-executor.ts index 593c573d90..2f456d5b38 100644 --- a/server/src/services/native-runtime/native-session-executor.ts +++ b/server/src/services/native-runtime/native-session-executor.ts @@ -998,9 +998,12 @@ export function createGovernedWaitEventObservation( observation = null; const kind = record(event.payload).kind; const tool = ["dynamicToolCall", "mcpToolCall", "commandExecution"].includes(String(kind)); - if (tool && event.itemId) { - if (event.eventType === "item.started") pendingTools.add(event.itemId); - if (event.eventType === "item.completed") pendingTools.delete(event.itemId); + if (event.itemId) { + if (tool && event.eventType === "item.started") pendingTools.add(event.itemId); + // Terminal error events can omit kind; the tracked ID owns cleanup. + if (event.eventType === "item.completed" || event.eventType === "item.failed") { + pendingTools.delete(event.itemId); + } } // Usage/model messages can arrive while the tool creating the card is // still awaiting its response. Parking then interrupts that in-flight