fix(background): replay persisted updates before launch order

This commit is contained in:
pi
2026-04-12 14:43:14 +01:00
parent 0f60ecdb49
commit 48c9ba6126
2 changed files with 84 additions and 5 deletions

View File

@@ -110,7 +110,9 @@ export default function subagentsExtension(pi: ExtensionAPI, deps: any = {}) {
}, { triggerTurn: false }); }, { triggerTurn: false });
} catch (e) {} } catch (e) {}
} 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 // replay persisted runs if session manager provides entries
try { try {
const entries = ctx.sessionManager?.getEntries?.() ?? []; const entries = ctx.sessionManager?.getEntries?.() ?? [];
// clear existing registry and rebuild from session entries const launches: any[] = [];
registry.replay([]); const updates: any[] = [];
for (const e of entries) { for (const e of entries) {
const type = (e.type ?? e.customType) as string | undefined; const type = (e.type ?? e.customType) as string | undefined;
if (type === "pi-subagents:bg-run") { if (type === "pi-subagents:bg-run") {
const run = e.data?.run ?? e.data; 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") { } else if (type === "pi-subagents:bg-update") {
const data = e.data; 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) {} } catch (e) {}
updateStatus(); updateStatus();

View File

@@ -535,3 +535,73 @@ test("background completion updates footer status, appends session updates, noti
else process.env.PI_SUBAGENTS_CHILD = original; 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> | 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;
}
});