Skip to content

Commit fcdd49d

Browse files
committed
Move send_input interrupt leftover tests out of resume_agent
The leftover interrupted stop_reason coverage is send_input behavior. Keep it next to the other send_input cases so resume_agent does not own that contract.
1 parent 31f1f14 commit fcdd49d

1 file changed

Lines changed: 76 additions & 76 deletions

File tree

src/subagent/lifecycle-tools.test.ts

Lines changed: 76 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -422,82 +422,6 @@ 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-
470-
test("followup throw after interrupt wait still has stop_reason interrupted", async () => {
471-
const sessions = createSubAgentSessionStore();
472-
const fleetRecords = createFleetMailbox(sessions);
473-
const worker = sessions.start({
474-
description: "worker",
475-
agentId: "a",
476-
brief: "b",
477-
retained: true,
478-
});
479-
sessions.markRunning(worker.id);
480-
sessions.registerInterrupt(worker.id, () => {});
481-
sessions.registerFollowup(worker.id, async () => {
482-
throw new Error("send failed");
483-
});
484-
fleetRecords.register(worker.id);
485-
486-
const sendInput = createSendInputTool({ sessions, fleetRecords });
487-
const wait = createWaitAgentsTool({ sessions, fleetRecords });
488-
489-
await callTool(sendInput, { target: worker.id, message: "stop that", interrupt: true });
490-
await new Promise((resolve) => setTimeout(resolve, 0));
491-
const collected = await callTool(wait, { targets: [worker.id], timeout_ms: 1000 });
492-
expect(collected.timed_out).toBe(false);
493-
const results = collected.results as {
494-
status: string;
495-
stop_reason?: string;
496-
}[];
497-
expect(results[0]!.status).toBe("interrupted");
498-
expect(results[0]!.stop_reason).toBe("interrupted");
499-
});
500-
501425
test("resume followup rejection invokes close; close_agent tears down leftover", async () => {
502426
const sessions = createSubAgentSessionStore();
503427
const fleetRecords = createFleetMailbox(sessions);
@@ -670,6 +594,82 @@ describe("interrupt_agent", () => {
670594
});
671595

672596
describe("send_input", () => {
597+
test("send_input interrupt then successful follow-up wait is done without leftover interrupted stop_reason", async () => {
598+
const sessions = createSubAgentSessionStore();
599+
const fleetRecords = createFleetMailbox(sessions);
600+
const worker = sessions.start({
601+
description: "worker",
602+
agentId: "a",
603+
brief: "b",
604+
retained: true,
605+
});
606+
sessions.markRunning(worker.id);
607+
sessions.registerInterrupt(worker.id, () => {});
608+
let finish: (reply: string) => void = () => {};
609+
sessions.registerFollowup(
610+
worker.id,
611+
() =>
612+
new Promise<string>((resolve) => {
613+
finish = resolve;
614+
}),
615+
);
616+
fleetRecords.register(worker.id);
617+
618+
const sendInput = createSendInputTool({ sessions, fleetRecords });
619+
const wait = createWaitAgentsTool({ sessions, fleetRecords });
620+
621+
await callTool(sendInput, { target: worker.id, message: "stop that", interrupt: true });
622+
const inflight = fleetRecords.peek(worker.id);
623+
expect(inflight?.status).toBe("interrupted");
624+
expect(inflight?.stopReason).toBe("interrupted");
625+
expect(sessions.get(worker.id)?.stopReason).toBe("interrupted");
626+
expect(sessions.get(worker.id)?.lifecycleStatus).toBe("running");
627+
628+
finish("followup report");
629+
await new Promise((resolve) => setTimeout(resolve, 0));
630+
const collected = await callTool(wait, { targets: [worker.id], timeout_ms: 1000 });
631+
expect(collected.timed_out).toBe(false);
632+
const results = collected.results as {
633+
status: string;
634+
report?: string;
635+
stop_reason?: string;
636+
}[];
637+
expect(results[0]!.status).toBe("done");
638+
expect(results[0]!.report).toBe("followup report");
639+
expect(results[0]!.stop_reason).toBeUndefined();
640+
});
641+
642+
test("followup throw after interrupt wait still has stop_reason interrupted", async () => {
643+
const sessions = createSubAgentSessionStore();
644+
const fleetRecords = createFleetMailbox(sessions);
645+
const worker = sessions.start({
646+
description: "worker",
647+
agentId: "a",
648+
brief: "b",
649+
retained: true,
650+
});
651+
sessions.markRunning(worker.id);
652+
sessions.registerInterrupt(worker.id, () => {});
653+
sessions.registerFollowup(worker.id, async () => {
654+
throw new Error("send failed");
655+
});
656+
fleetRecords.register(worker.id);
657+
658+
const sendInput = createSendInputTool({ sessions, fleetRecords });
659+
const wait = createWaitAgentsTool({ sessions, fleetRecords });
660+
661+
await callTool(sendInput, { target: worker.id, message: "stop that", interrupt: true });
662+
await new Promise((resolve) => setTimeout(resolve, 0));
663+
const collected = await callTool(wait, { targets: [worker.id], timeout_ms: 1000 });
664+
expect(collected.timed_out).toBe(false);
665+
const results = collected.results as {
666+
status: string;
667+
stop_reason?: string;
668+
}[];
669+
expect(results[0]!.status).toBe("interrupted");
670+
expect(results[0]!.stop_reason).toBe("interrupted");
671+
});
672+
673673
test("soft-delivers without flipping lifecycle or awaiting a reply", async () => {
674674
const sessions = createSubAgentSessionStore();
675675
const worker = sessions.start({

0 commit comments

Comments
 (0)