diff --git a/packages/server/src/server/agent/providers/pi/agent.test.ts b/packages/server/src/server/agent/providers/pi/agent.test.ts index 76589743d..5b4eb5838 100644 --- a/packages/server/src/server/agent/providers/pi/agent.test.ts +++ b/packages/server/src/server/agent/providers/pi/agent.test.ts @@ -97,6 +97,18 @@ class SessionEvents { .map((event) => event.item); } + timelineAndCompletionEvents() { + return this.events.flatMap((event) => { + if (event.type === "timeline") { + return [{ type: "timeline" as const, item: event.item }]; + } + if (event.type === "turn_completed") { + return [{ type: "turn_completed" as const }]; + } + return []; + }); + } + nextTurnCompletion(): Promise> { return this.nextEvent( (event): event is Extract => @@ -459,6 +471,28 @@ describe("PiRpcAgentSession", () => { ]); }); + test("surfaces Pi extension command messages and completes when no agent turn starts", async () => { + const { pi, session, events } = await createSession(); + const fakeSession = pi.latestSession(); + + await session.startTurn("/show-status"); + fakeSession.emit({ + type: "message_end", + message: { + role: "custom", + content: [{ type: "text", text: "Extension command output" }], + }, + }); + + expect(events.timelineAndCompletionEvents()).toEqual([ + { + type: "timeline", + item: { type: "assistant_message", text: "Extension command output" }, + }, + { type: "turn_completed" }, + ]); + }); + test("adds Pi assistant context to generic provider finish errors", async () => { const { pi, session, events } = await createSession(); diff --git a/packages/server/src/server/agent/providers/pi/agent.ts b/packages/server/src/server/agent/providers/pi/agent.ts index eb0e1de5e..b76ab5c7b 100644 --- a/packages/server/src/server/agent/providers/pi/agent.ts +++ b/packages/server/src/server/agent/providers/pi/agent.ts @@ -1499,6 +1499,20 @@ export class PiRpcAgentSession implements AgentSession { event: Extract, turnId: string | undefined, ): void { + if (event.message.role === "custom") { + const text = getUserMessageText(event.message.content); + if (text) { + this.emit({ + type: "timeline", + provider: PI_PROVIDER, + turnId, + item: { type: "assistant_message", text }, + }); + } + this.completeTurn(turnId, []); + return; + } + if (event.message.role !== "user") { return; } diff --git a/packages/server/src/server/agent/providers/pi/rpc-types.ts b/packages/server/src/server/agent/providers/pi/rpc-types.ts index aab5f9424..90f9aa4e1 100644 --- a/packages/server/src/server/agent/providers/pi/rpc-types.ts +++ b/packages/server/src/server/agent/providers/pi/rpc-types.ts @@ -30,6 +30,10 @@ export type PiAgentMessage = role: "user"; content: string | Array; } + | { + role: "custom"; + content: string | Array; + } | { role: "assistant"; content: PiAssistantContent[];