From 48c9ba6126133dc13c22e755a50aa37a0100980c Mon Sep 17 00:00:00 2001 From: pi Date: Sun, 12 Apr 2026 14:43:14 +0100 Subject: [PATCH] fix(background): replay persisted updates before launch order --- index.ts | 19 ++++++++---- src/extension.test.ts | 70 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/index.ts b/index.ts index 6adaf1b..eace13a 100644 --- a/index.ts +++ b/index.ts @@ -110,7 +110,9 @@ export default function subagentsExtension(pi: ExtensionAPI, deps: any = {}) { }, { triggerTurn: false }); } catch (e) {} } catch (e) { - // monitorRun errors are non-fatal here + // Monitoring runs happens in the background. Failures here should not + // block tool registration or the foreground session; monitorRun errors + // are best-effort and final failures are logged via result.json. } } @@ -178,18 +180,25 @@ export default function subagentsExtension(pi: ExtensionAPI, deps: any = {}) { // replay persisted runs if session manager provides entries try { const entries = ctx.sessionManager?.getEntries?.() ?? []; - // clear existing registry and rebuild from session entries - registry.replay([]); + const launches: any[] = []; + const updates: any[] = []; + for (const e of entries) { const type = (e.type ?? e.customType) as string | undefined; if (type === "pi-subagents:bg-run") { const run = e.data?.run ?? e.data; - if (run && run.runId) registry.recordLaunch(run as any); + if (run && run.runId) launches.push(run); } else if (type === "pi-subagents:bg-update") { const data = e.data; - if (data && data.runId) registry.recordUpdate(data.runId, data as any); + if (data && data.runId) updates.push(data); } } + + // Rebuild in two passes so out-of-order persisted entries cannot drop + // terminal updates that happen to appear before their launch entry. + registry.replay([]); + for (const run of launches) registry.recordLaunch(run as any); + for (const update of updates) registry.recordUpdate(update.runId, update as any); } catch (e) {} updateStatus(); diff --git a/src/extension.test.ts b/src/extension.test.ts index aefcacf..3296446 100644 --- a/src/extension.test.ts +++ b/src/extension.test.ts @@ -535,3 +535,73 @@ test("background completion updates footer status, appends session updates, noti else process.env.PI_SUBAGENTS_CHILD = original; } }); + +test("session_start replay applies bg-update entries even when they appear before bg-run", async () => { + const original = process.env.PI_SUBAGENTS_CHILD; + if (original !== undefined) delete process.env.PI_SUBAGENTS_CHILD; + + try { + const handlers: any = {}; + const registeredTools: any[] = []; + + subagentsExtension({ + on(event: string, handler: (event: any, ctx: any) => Promise | void) { + handlers[event] = handler; + }, + registerTool(tool: any) { + registeredTools.push(tool); + }, + registerProvider() {}, + appendEntry() {}, + sendMessage() {}, + } as any); + + await handlers.session_start?.( + { reason: "startup" }, + { + modelRegistry: { + getAvailable: () => [{ provider: "openai", id: "gpt-5" }], + }, + ui: { + notify() {}, + setStatus() {}, + }, + sessionManager: { + getEntries: () => [ + { + type: "pi-subagents:bg-update", + data: { runId: "run-1", status: "completed", exitCode: 0, finalText: "done" }, + }, + { + type: "pi-subagents:bg-run", + data: { + run: { + runId: "run-1", + preset: "repo-scout", + task: "inspect auth", + status: "running", + startTime: Date.now(), + paths: { + eventsPath: "/tmp/run-1/events.jsonl", + resultPath: "/tmp/run-1/result.json", + }, + }, + }, + }, + ], + }, + }, + ); + + const statusTool = registeredTools.find((tool) => tool.name === "background_agent_status"); + assert(statusTool, "background_agent_status registered"); + + const result: any = await statusTool.execute("tool-1", { runId: "run-1" }, undefined, undefined, undefined); + assert.equal(result.details.runs.length, 1); + assert.equal(result.details.runs[0].status, "completed"); + assert.equal(result.details.counts.completed, 1); + } finally { + if (original === undefined) delete process.env.PI_SUBAGENTS_CHILD; + else process.env.PI_SUBAGENTS_CHILD = original; + } +});