Skip to content

Commit e6325e1

Browse files
committed
fix(agents): preserve run-mode keep subagents past session sweep TTL
The sweeper applied SESSION_RUN_TTL_MS (5 minutes) to any registry entry without archiveAtMs. Run-mode subagents spawned with cleanup:"keep" also have archiveAtMs=undefined by design, so they were pruned five minutes after cleanup completed — directly contradicting the keep semantic the user requested. Gate the absolute-TTL prune on spawnMode === "session" so run-mode keep entries stay registered. Session-mode runs retain the existing five-minute window. Refs #83132
1 parent 833f1ce commit e6325e1

2 files changed

Lines changed: 30 additions & 2 deletions

File tree

src/agents/subagent-registry.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,33 @@ describe("subagent registry seam flow", () => {
767767
expect(run?.cleanupCompletedAt).toBeTypeOf("number");
768768
});
769769

770+
it("preserves run-mode keep entries past SESSION_RUN_TTL_MS sweep", async () => {
771+
mod.registerSubagentRun({
772+
runId: "run-keep-survives-ttl",
773+
childSessionKey: "agent:main:subagent:child",
774+
requesterSessionKey: "agent:main:main",
775+
requesterDisplayKey: "main",
776+
task: "keep me past the session ttl",
777+
cleanup: "keep",
778+
spawnMode: "run",
779+
});
780+
781+
await waitForFast(() => {
782+
const run = mod
783+
.listSubagentRunsForRequester("agent:main:main")
784+
.find((entry) => entry.runId === "run-keep-survives-ttl");
785+
expect(run?.cleanupCompletedAt).toBeTypeOf("number");
786+
});
787+
788+
vi.setSystemTime(new Date(Date.parse("2026-03-24T12:00:00Z") + 10 * 60_000));
789+
await mod.__testing.sweepOnceForTests();
790+
791+
const run = mod
792+
.listSubagentRunsForRequester("agent:main:main")
793+
.find((entry) => entry.runId === "run-keep-survives-ttl");
794+
expect(run?.runId).toBe("run-keep-survives-ttl");
795+
});
796+
770797
it("retries completion hooks before resuming ended cleanup", async () => {
771798
mocks.ensureRuntimePluginsLoaded.mockRejectedValueOnce(new Error("runtime unavailable"));
772799

src/agents/subagent-registry.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -863,8 +863,9 @@ async function sweepSubagentRuns() {
863863
}
864864
}
865865

866-
// Session-mode runs have no archiveAtMs — apply absolute TTL after cleanup completes.
867-
// Use cleanupCompletedAt (not endedAt) to avoid interrupting deferred cleanup flows.
866+
if (!entry.archiveAtMs && entry.cleanup === "keep" && entry.spawnMode !== "session") {
867+
continue;
868+
}
868869
if (!entry.archiveAtMs) {
869870
if (
870871
typeof entry.cleanupCompletedAt === "number" &&

0 commit comments

Comments
 (0)