authorgravatar for 76637177+SunkenInTime@users.noreply.github.comDara Adedeji <76637177+SunkenInTime@users.noreply.github.com> 2026-10-03 10:54:44-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-03 09:54:44-05:00
logce90eec1ffc2087395227a610843091f4b260cf7
treeae249fcb8a710e80715615a0ba5e47d3280c68f4
parenta0bf1eee48d1537ec38e0f1fa5243f10ffd4e870
signature Signed by PGP key B5690EEEBB952194

fix(server): a Claude command you stop shows as interrupted (#14896)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

4 files changed, 8 insertions(+), 6 deletions(-)

apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts+4-2
...@@ -3673,7 +3673,7 @@ export function makeClaudeAdapterV2(...@@ -3673,7 +3673,7 @@ export function makeClaudeAdapterV2(
3673 readonly output: ClaudeNativeToolOutput;3673 readonly output: ClaudeNativeToolOutput;
3674 readonly status: Extract<3674 readonly status: Extract<
3675 OrchestrationV2TurnItem["status"],3675 OrchestrationV2TurnItem["status"],
3676 "running" | "completed" | "failed"3676 "running" | "completed" | "failed" | "interrupted" | "cancelled"
3677 >;3677 >;
3678 readonly startedAt: DateTime.Utc;3678 readonly startedAt: DateTime.Utc;
3679 readonly updatedAt: DateTime.Utc;3679 readonly updatedAt: DateTime.Utc;
...@@ -4740,7 +4740,9 @@ export function makeClaudeAdapterV2(...@@ -4740,7 +4740,9 @@ export function makeClaudeAdapterV2(
4740 parentNodeId: toolCall.parentNodeId,4740 parentNodeId: toolCall.parentNodeId,
4741 ordinal: toolCall.ordinal,4741 ordinal: toolCall.ordinal,
4742 output: NO_CLAUDE_NATIVE_TOOL_OUTPUT,4742 output: NO_CLAUDE_NATIVE_TOOL_OUTPUT,
4743 status: "failed",4743 // A stopped turn cuts its open tools short; only a turn that
4744 // ended on its own leaves them failed.
4745 status: input.status === "completed" ? "failed" : input.status,
4744 startedAt: toolCall.startedAt,4746 startedAt: toolCall.startedAt,
4745 updatedAt: input.completedAt,4747 updatedAt: input.completedAt,
4746 presentation: toolCall.presentation,4748 presentation: toolCall.presentation,
apps/server/src/orchestration-v2/testkit/fixtures/claude_background_task_interrupt/output.ts+2-2
...@@ -44,7 +44,7 @@ export function assertClaudeBackgroundTaskInterruptOutput(...@@ -44,7 +44,7 @@ export function assertClaudeBackgroundTaskInterruptOutput(
44 projection.turnItems.flatMap((item) =>44 projection.turnItems.flatMap((item) =>
45 item.type === "command_execution" ? [item.status] : [],45 item.type === "command_execution" ? [item.status] : [],
46 ),46 ),
47 ["completed", "failed"],47 ["completed", "interrupted"],
48 "the background launch completed; the interrupted foreground command did not",48 "the background launch completed; the foreground command was interrupted",
49 );49 );
50}50}
apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt_mid_tool/claude_output.ts+1-1
...@@ -81,7 +81,7 @@ export function assertTurnInterruptMidToolClaudeOutput(...@@ -81,7 +81,7 @@ export function assertTurnInterruptMidToolClaudeOutput(
81 assert.isDefined(commandItem);81 assert.isDefined(commandItem);
82 assert.isDefined(interruptRequest);82 assert.isDefined(interruptRequest);
83 assert.isDefined(interruptResult);83 assert.isDefined(interruptResult);
84 assert.equal(commandItem.status, "failed");84 assert.equal(commandItem.status, "interrupted");
85 assert.include(commandItem.input, "node -e");85 assert.include(commandItem.input, "node -e");
86 assert.equal(interruptRequest.status, "completed");86 assert.equal(interruptRequest.status, "completed");
87 assert.equal(interruptResult.status, "interrupted");87 assert.equal(interruptResult.status, "interrupted");
apps/server/src/orchestration-v2/testkit/fixtures/turn_interrupt_restart/claude_output.ts+1-1
...@@ -111,7 +111,7 @@ export function assertTurnInterruptRestartClaudeOutput(...@@ -111,7 +111,7 @@ export function assertTurnInterruptRestartClaudeOutput(
111 assert.equal(projection.providerThreads[0]?.status, "idle");111 assert.equal(projection.providerThreads[0]?.status, "idle");
112 const commandItem = projection.turnItems.find((item) => item.type === "command_execution");112 const commandItem = projection.turnItems.find((item) => item.type === "command_execution");
113 assert.isDefined(commandItem);113 assert.isDefined(commandItem);
114 assert.equal(commandItem.status, "failed");114 assert.equal(commandItem.status, "interrupted");
115 assert.include(commandItem.input, "node -e");115 assert.include(commandItem.input, "node -e");
116116
117 const outboundFrames = transcript.entries.flatMap((entry) =>117 const outboundFrames = transcript.entries.flatMap((entry) =>