Skip to content

Commit e885cc1

Browse files
committed
Surface interrupted workers that still have tools running
interrupt_agent does not hard-stop in-flight tools. The strip still painted those lanes as busy. The board now says interrupted and names the leftover tool so the parent can tell a live turn from a stopped one.
1 parent 55a590b commit e885cc1

5 files changed

Lines changed: 44 additions & 0 deletions

File tree

src/tui/agent-progress.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,23 @@ describe("agentProgress", () => {
3232
expect(agentProgress({ ...base, status: "cancelled" }, 1000)).toBeNull();
3333
});
3434

35+
test("an interrupted running session names leftover tools instead of looking busy", () => {
36+
const progress = agentProgress(
37+
{
38+
...base,
39+
lifecycleStatus: "interrupted",
40+
currentToolName: "run_shell",
41+
currentToolPreview: "bun test",
42+
currentToolStartedAt: 1_000,
43+
lastActivityAt: 1_000,
44+
},
45+
91_000,
46+
);
47+
expect(progress?.stat).toBe("interrupted · bun test still running");
48+
expect(progress?.working).toBe(false);
49+
expect(progress?.stalled).toBe(false);
50+
});
51+
3552
test("a running session reports elapsed time and its current tool", () => {
3653
const progress = agentProgress({ ...base, lastActivityAt: 42_000 }, 42_000);
3754
expect(progress).toEqual({

src/tui/agent-progress.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616
/** Minimal session shape this module reads — avoids a hard dep on the store. */
1717
export interface AgentProgressSession {
1818
readonly status: "running" | "done" | "failed" | "cancelled";
19+
/** Present when the strip knows lifecycle independently of TUI status. */
20+
readonly lifecycleStatus?:
21+
| "pending_init"
22+
| "running"
23+
| "interrupted"
24+
| "completed"
25+
| "shutdown"
26+
| "not_found";
1927
readonly currentToolName: string | null;
2028
/**
2129
* Bounded subject of the oldest outstanding call (command, path, pattern…),
@@ -146,6 +154,19 @@ export function agentProgress(
146154
const hasSubject = subject !== null;
147155
const state = laneState(session, nowMs, stallMs);
148156

157+
if (session.lifecycleStatus === "interrupted") {
158+
const toolBit =
159+
hasSubject && session.currentToolName !== null
160+
? ` · ${subject} still running`
161+
: " · tools still running";
162+
return {
163+
stat: `interrupted${toolBit}`,
164+
state,
165+
working: false,
166+
stalled: false,
167+
};
168+
}
169+
149170
const base = hasSubject ? `${elapsed} · ${subject}` : elapsed;
150171
// Never render "quiet" — operator chrome only shows motion (elapsed / tool).
151172
// Internal `state` still carries stalled for recovery consumers.

src/tui/chrome-state.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export interface ChromeAgentSession {
5454
readonly agentId: string;
5555
readonly description: string;
5656
readonly status: "running" | "done" | "failed" | "cancelled";
57+
readonly lifecycleStatus?: AgentProgressSession["lifecycleStatus"];
5758
/** Current tool while running (optional detail). */
5859
readonly currentToolName?: string | null;
5960
/**
@@ -319,6 +320,7 @@ function toProgressSession(session: ChromeAgentSession): AgentProgressSession |
319320
if (session.startedAt === undefined) return null;
320321
return {
321322
status: session.status,
323+
...(session.lifecycleStatus !== undefined ? { lifecycleStatus: session.lifecycleStatus } : {}),
322324
currentToolName: session.currentToolName ?? null,
323325
currentToolPreview: session.currentToolPreview ?? null,
324326
currentToolStartedAt: session.currentToolStartedAt,
@@ -493,6 +495,7 @@ export interface ChromeSessionAgent {
493495
readonly id?: string;
494496
readonly description: string;
495497
readonly status: "running" | "done" | "failed" | "cancelled";
498+
readonly lifecycleStatus?: AgentProgressSession["lifecycleStatus"];
496499
readonly currentToolName?: string | null;
497500
readonly currentToolPreview?: string | null;
498501
readonly currentToolStartedAt: number | null;
@@ -554,6 +557,7 @@ function mapSessionAgents(
554557
agentId: agentId.length > 0 ? agentId : "agent",
555558
description: a.description,
556559
status: a.status,
560+
...(a.lifecycleStatus !== undefined ? { lifecycleStatus: a.lifecycleStatus } : {}),
557561
...(a.currentToolName !== undefined ? { currentToolName: a.currentToolName } : {}),
558562
...(a.currentToolPreview !== undefined ? { currentToolPreview: a.currentToolPreview } : {}),
559563
currentToolStartedAt: a.currentToolStartedAt,

src/tui/runner-host.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ export async function mountRunnerHost(deps: RunnerHostDeps): Promise<RunnerHost>
279279
deps.subAgentSessions().map((s) => ({
280280
id: s.id,
281281
status: s.status,
282+
lifecycleStatus: s.lifecycleStatus,
282283
currentToolName: s.currentToolName,
283284
currentToolPreview: s.currentToolPreview,
284285
currentToolStartedAt: s.currentToolStartedAt,

src/tui/runner.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2413,6 +2413,7 @@ export async function runTUI(initialConfig: Config): Promise<number> {
24132413
id: s.id,
24142414
description: s.description,
24152415
status: s.status,
2416+
lifecycleStatus: s.lifecycleStatus,
24162417
currentToolName: s.currentToolName,
24172418
currentToolPreview: s.currentToolPreview,
24182419
currentToolStartedAt: s.currentToolStartedAt,

0 commit comments

Comments
 (0)