From 3479370ec7c946a3232c4920c84413fd2e48e6cd Mon Sep 17 00:00:00 2001 From: Dotta Date: Tue, 9 Jun 2026 23:06:49 +0000 Subject: [PATCH] Thread run error codes into interrupt labels --- server/src/services/activity.ts | 1 + ui/src/api/activity.ts | 1 + ui/src/lib/issue-chat-messages.test.ts | 33 ++++++++++++++++++++++++++ ui/src/lib/issue-chat-messages.ts | 8 ++++--- 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/server/src/services/activity.ts b/server/src/services/activity.ts index 266a448c3ca..260d501dd3d 100644 --- a/server/src/services/activity.ts +++ b/server/src/services/activity.ts @@ -387,6 +387,7 @@ export function activityService(db: Db) { finishedAt: heartbeatRuns.finishedAt, createdAt: heartbeatRuns.createdAt, invocationSource: heartbeatRuns.invocationSource, + errorCode: heartbeatRuns.errorCode, usageJson: summarizedUsageJson, resultJson: summarizedResultJson, logBytes: heartbeatRuns.logBytes, diff --git a/ui/src/api/activity.ts b/ui/src/api/activity.ts index b245ad31ece..e5eaf070127 100644 --- a/ui/src/api/activity.ts +++ b/ui/src/api/activity.ts @@ -12,6 +12,7 @@ export interface RunForIssue { finishedAt: string | null; createdAt: string; invocationSource: string; + errorCode?: string | null; usageJson: Record | null; resultJson: Record | null; logBytes?: number | null; diff --git a/ui/src/lib/issue-chat-messages.test.ts b/ui/src/lib/issue-chat-messages.test.ts index 24e53b221dd..786e1b3ab20 100644 --- a/ui/src/lib/issue-chat-messages.test.ts +++ b/ui/src/lib/issue-chat-messages.test.ts @@ -947,6 +947,39 @@ describe("buildIssueChatMessages", () => { }); }); + it("labels error-code-only operator interruptions as interrupted by board", () => { + const messages = buildIssueChatMessages({ + comments: [], + timelineEvents: [], + linkedRuns: [ + { + runId: "run-interrupted", + status: "cancelled", + agentId: "agent-1", + agentName: "CodexCoder", + createdAt: new Date("2026-04-06T12:01:00.000Z"), + startedAt: new Date("2026-04-06T12:01:00.000Z"), + finishedAt: new Date("2026-04-06T12:02:00.000Z"), + errorCode: "operator_interrupted", + resultJson: null, + }, + ], + liveRuns: [], + transcriptsByRunId: new Map([ + ["run-interrupted", [{ kind: "assistant", ts: "2026-04-06T12:01:05.000Z", text: "Working on it." }]], + ]), + hasOutputForRun: (runId) => runId === "run-interrupted", + currentUserId: "user-1", + }); + + expect(messages).toHaveLength(1); + expect(messages[0]?.metadata.custom).toMatchObject({ + chainOfThoughtLabel: "Interrupted by board after 1 minute", + runOperatorInterrupted: true, + runStatus: "cancelled", + }); + }); + it("can keep succeeded runs without transcript output for embedded run feeds", () => { const messages = buildIssueChatMessages({ comments: [], diff --git a/ui/src/lib/issue-chat-messages.ts b/ui/src/lib/issue-chat-messages.ts index baa065aa7cd..fe2d0c37c04 100644 --- a/ui/src/lib/issue-chat-messages.ts +++ b/ui/src/lib/issue-chat-messages.ts @@ -46,6 +46,7 @@ export interface IssueChatLinkedRun { finishedAt?: Date | string | null; hasStoredOutput?: boolean; logBytes?: number | null; + errorCode?: string | null; resultJson?: Record | null; } @@ -596,6 +597,7 @@ function runDurationLabel(run: { createdAt: Date | string; startedAt: Date | string | null; finishedAt?: Date | string | null; + errorCode?: string | null; resultJson?: Record | null; }) { const start = run.startedAt ?? run.createdAt; @@ -612,7 +614,7 @@ function runDurationLabel(run: { case "timed_out": return durationText ? `Timed out after ${durationText}` : "Run timed out"; case "cancelled": - if (isOperatorInterruptedRun(run.resultJson)) { + if (isOperatorInterruptedRun(run.resultJson, run.errorCode)) { return durationText ? `Interrupted by board after ${durationText}` : "Interrupted by board"; } if (stopReason === "paused") { @@ -643,7 +645,7 @@ function createHistoricalRunMessage(run: IssueChatLinkedRun, agentMap?: Map