diff --git a/packages/server/src/server/agent/providers/pi/extensions/gotgenes-pi-subagents/fixtures/refused-resume.json b/packages/server/src/server/agent/providers/pi/extensions/gotgenes-pi-subagents/fixtures/refused-resume.json new file mode 100644 index 0000000000..b275677a29 --- /dev/null +++ b/packages/server/src/server/agent/providers/pi/extensions/gotgenes-pi-subagents/fixtures/refused-resume.json @@ -0,0 +1,68 @@ +{ + "provenance": { + "package": "@gotgenes/pi-subagents", + "version": "23.4.0", + "sourceCommit": "63476ff869efd8ea339040f496fa734048b89e70", + "piVersion": "0.84.4", + "captureDate": "2026-10-19" + }, + "events": [ + { + "type": "tool_execution_start", + "toolCallId": "resume-call", + "toolName": "subagent", + "args": { + "subagent_type": "general-purpose", + "description": "resume", + "prompt": "Continue", + "resume": "expired-child", + "run_in_background": true + } + }, + { + "type": "tool_execution_end", + "toolCallId": "resume-call", + "toolName": "subagent", + "result": { + "content": [ + { + "type": "text", + "text": "Agent \"expired-child\" had its session released after its retention window; resume is unavailable, but its result is still retrievable via get_subagent_result." + } + ] + }, + "isError": false + } + ], + "messages": [ + { + "role": "assistant", + "content": [ + { + "type": "toolCall", + "id": "resume-call", + "name": "subagent", + "arguments": { + "subagent_type": "general-purpose", + "description": "resume", + "prompt": "Continue", + "resume": "expired-child", + "run_in_background": true + } + } + ] + }, + { + "role": "toolResult", + "toolCallId": "resume-call", + "toolName": "subagent", + "content": [ + { + "type": "text", + "text": "Agent \"expired-child\" had its session released after its retention window; resume is unavailable, but its result is still retrievable via get_subagent_result." + } + ], + "isError": false + } + ] +} diff --git a/packages/server/src/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.test.ts b/packages/server/src/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.test.ts index 6bbe1b3b06..f89a9aefd3 100644 --- a/packages/server/src/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.test.ts +++ b/packages/server/src/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.test.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from "vitest"; import { fileURLToPath } from "node:url"; +import { ProviderSubagentStore } from "../../../../provider-subagents/store.js"; import { createPiExtensionHost } from "../index.js"; import { readSubagentFixture, verifySubagentFixture } from "../subagent-fixture-test.js"; import { streamPiHistory } from "../../history-mapper.js"; @@ -25,6 +26,31 @@ function publishService(agents: Array<{ id: string; outputFile?: string }>): voi } describe("@gotgenes/pi-subagents adapter", () => { + test.each([true, false])( + "removes a refused resume from live and replayed subagents (background=%s)", + async (background) => { + // The result came from 23.4.0's real AgentTool and SubagentManager after releaseSession(). + // The child session was a fake; releaseSession replaced waiting for the retention sweep. + const fixture = readSubagentFixture( + new URL("./fixtures/refused-resume.json", import.meta.url), + ); + const start = fixture.events[0]!; + if (start.type !== "tool_execution_start") throw new Error("Expected a tool start"); + start.args = { ...(start.args as object), run_in_background: background }; + const assistant = fixture.messages[0]!; + if (assistant.role !== "assistant") throw new Error("Expected an assistant tool call"); + for (const block of assistant.content) { + if (block.type === "toolCall") block.arguments = start.args; + } + const events = await verifySubagentFixture(fixture); + const store = new ProviderSubagentStore(); + store.apply("parent", "pi", { type: "upsert", id: "previous-child", status: "completed" }); + for (const event of events) store.apply("parent", "pi", event.event); + expect(events.map(({ event }) => event.type)).toEqual(["upsert", "remove"]); + expect(store.list("parent")).toMatchObject([{ id: "previous-child", status: "completed" }]); + }, + ); + test("reports a child's file after the child republishes the gotgenes service", async () => { const notifications: string[] = []; const emit = loadRuntimeBridge(); diff --git a/packages/server/src/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.ts b/packages/server/src/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.ts index 62db93c79e..e1869705f8 100644 --- a/packages/server/src/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.ts +++ b/packages/server/src/server/agent/providers/pi/extensions/gotgenes-pi-subagents/index.ts @@ -86,7 +86,7 @@ export const gotgenesPiSubagents: PiExtension = { detail, subagents: [{ type: "upsert" as const, id: call.callId, status: "failed" as const }], } - : { detail }; + : { detail, subagents: [{ type: "remove" as const, id: call.callId }] }; callsByAgent.set(details.data.agentId, call.callId); const file = outputFileFromToolResult(call.result) ?? pendingFilesByAgent.get(details.data.agentId);