Skip to content

Commit 0e58096

Browse files
committed
Clear interrupted stamp after follow-up succeeds
send_input interrupt restamps interrupted for in-flight wait; a completed follow-up must not keep that stamp on done.
1 parent b2f24bd commit 0e58096

2 files changed

Lines changed: 46 additions & 0 deletions

File tree

src/subagent/lifecycle-tools.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,51 @@ describe("resume_agent", () => {
422422
expect(results[0]!.stop_reason).toBeUndefined();
423423
});
424424

425+
test("send_input interrupt then successful follow-up wait is done without leftover interrupted stop_reason", async () => {
426+
const sessions = createSubAgentSessionStore();
427+
const fleetRecords = createFleetMailbox(sessions);
428+
const worker = sessions.start({
429+
description: "worker",
430+
agentId: "a",
431+
brief: "b",
432+
retained: true,
433+
});
434+
sessions.markRunning(worker.id);
435+
sessions.registerInterrupt(worker.id, () => {});
436+
let finish: (reply: string) => void = () => {};
437+
sessions.registerFollowup(
438+
worker.id,
439+
() =>
440+
new Promise<string>((resolve) => {
441+
finish = resolve;
442+
}),
443+
);
444+
fleetRecords.register(worker.id);
445+
446+
const sendInput = createSendInputTool({ sessions, fleetRecords });
447+
const wait = createWaitAgentsTool({ sessions, fleetRecords });
448+
449+
await callTool(sendInput, { target: worker.id, message: "stop that", interrupt: true });
450+
const inflight = fleetRecords.peek(worker.id);
451+
expect(inflight?.status).toBe("interrupted");
452+
expect(inflight?.stopReason).toBe("interrupted");
453+
expect(sessions.get(worker.id)?.stopReason).toBe("interrupted");
454+
expect(sessions.get(worker.id)?.lifecycleStatus).toBe("running");
455+
456+
finish("followup report");
457+
await new Promise((resolve) => setTimeout(resolve, 0));
458+
const collected = await callTool(wait, { targets: [worker.id], timeout_ms: 1000 });
459+
expect(collected.timed_out).toBe(false);
460+
const results = collected.results as {
461+
status: string;
462+
report?: string;
463+
stop_reason?: string;
464+
}[];
465+
expect(results[0]!.status).toBe("done");
466+
expect(results[0]!.report).toBe("followup report");
467+
expect(results[0]!.stop_reason).toBeUndefined();
468+
});
469+
425470
test("followup throw after interrupt wait still has stop_reason interrupted", async () => {
426471
const sessions = createSubAgentSessionStore();
427472
const fleetRecords = createFleetMailbox(sessions);

src/subagent/session-store.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,7 @@ export function createSubAgentSessionStore(
818818
s.lifecycle = { state: "completed", report: reply };
819819
s.finishedAt = now();
820820
s.report = reply;
821+
delete s.stopReason;
821822
pushEntry(s, { kind: "report", content: capText(reply, maxEntryChars) });
822823
});
823824
runInFlight.delete(id);

0 commit comments

Comments
 (0)