diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 76ebcad4845..d41a447a2f0 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -4,7 +4,7 @@ ### Changed -- Changed `pi --mode json` to output a compact transcript instead of streaming session events. +- Changed `pi --mode json` to output a compact complete-message transcript instead of streaming session events. - Changed Pi version checks to identify Pi with a `pi/` user agent. ### Fixed diff --git a/packages/coding-agent/docs/json.md b/packages/coding-agent/docs/json.md index d411c53d21e..4ec88038e29 100644 --- a/packages/coding-agent/docs/json.md +++ b/packages/coding-agent/docs/json.md @@ -10,7 +10,8 @@ Outputs a compact transcript as JSON lines. Streaming deltas, tool execution eve - `{"type":"agent_start"}` when a run starts. - Complete `UserMessage` objects when user messages are accepted. -- Complete `AssistantMessage` objects after each assistant turn completes. +- Complete `AssistantMessage` objects when assistant messages finish. +- Complete `ToolResultMessage` objects when tool results are available. - `{"type":"agent_end"}` when a run ends. ## Output Format @@ -21,10 +22,11 @@ Each line is one transcript record: {"type":"agent_start"} {"role":"user","content":"Hello","timestamp":...} {"role":"assistant","content":[{"type":"text","text":"Hello"}],"provider":"openai","model":"gpt-4o-mini","stopReason":"stop",...} +{"role":"toolResult","toolCallId":"call_123","toolName":"read","content":[{"type":"text","text":"..."}],"isError":false,"timestamp":...} {"type":"agent_end"} ``` -Runs with multiple assistant turns output one completed assistant-turn message per turn. +Runs with tool calls output the assistant message that requested the tool, followed by the resulting tool-result message. For a persistent protocol with streaming events and command responses, use [`--mode rpc`](rpc.md). diff --git a/packages/coding-agent/src/modes/print-mode.ts b/packages/coding-agent/src/modes/print-mode.ts index 27eb0ac3a48..872776116f8 100644 --- a/packages/coding-agent/src/modes/print-mode.ts +++ b/packages/coding-agent/src/modes/print-mode.ts @@ -30,9 +30,10 @@ export interface PrintModeOptions { function writeJsonTranscriptRecord(event: AgentSessionEvent): void { if (event.type === "agent_start") { writeRawStdout(serializeJsonLine({ type: "agent_start" })); - } else if (event.type === "message_end" && event.message.role === "user") { - writeRawStdout(serializeJsonLine(event.message)); - } else if (event.type === "turn_end" && event.message.role === "assistant") { + } else if ( + event.type === "message_end" && + (event.message.role === "user" || event.message.role === "assistant" || event.message.role === "toolResult") + ) { writeRawStdout(serializeJsonLine(event.message)); } else if (event.type === "agent_end") { writeRawStdout(serializeJsonLine({ type: "agent_end" })); diff --git a/packages/coding-agent/test/print-mode.test.ts b/packages/coding-agent/test/print-mode.test.ts index 3db594785b5..2ca7db56502 100644 --- a/packages/coding-agent/test/print-mode.test.ts +++ b/packages/coding-agent/test/print-mode.test.ts @@ -1,4 +1,4 @@ -import type { AssistantMessage, ImageContent, UserMessage } from "@mariozechner/pi-ai"; +import type { AssistantMessage, ImageContent, ToolResultMessage, UserMessage } from "@mariozechner/pi-ai"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { AgentSessionEvent, AgentSessionEventListener, SessionShutdownEvent } from "../src/index.js"; import { runPrintMode } from "../src/modes/print-mode.js"; @@ -63,6 +63,17 @@ function createUserMessage(text: string): UserMessage { }; } +function createToolResultMessage(): ToolResultMessage { + return { + role: "toolResult", + toolCallId: "call-1", + toolName: "read", + content: [{ type: "text", text: "tool output" }], + isError: false, + timestamp: Date.now(), + }; +} + function captureStdout(): string[] { const chunks: string[] = []; vi.spyOn(process.stdout, "write").mockImplementation((( @@ -157,10 +168,11 @@ describe("runPrintMode", () => { }); it("outputs only transcript records in json mode", async () => { - const assistantMessage = createAssistantMessage({ text: "done" }); - const messageEndAssistantMessage = createAssistantMessage({ text: "message end" }); + const assistantMessage = createAssistantMessage({ text: "message end" }); + const turnEndAssistantMessage = createAssistantMessage({ text: "turn end" }); const partialMessage = createAssistantMessage({ text: "do" }); const userMessage = createUserMessage("hello"); + const toolResultMessage = createToolResultMessage(); const runtimeHost = createRuntimeHost(assistantMessage, [ { type: "agent_start" }, { type: "turn_start" }, @@ -172,9 +184,12 @@ describe("runPrintMode", () => { assistantMessageEvent: { type: "text_delta", contentIndex: 0, delta: "ne", partial: partialMessage }, }, { type: "message_end", message: userMessage }, - { type: "message_end", message: messageEndAssistantMessage }, - { type: "turn_end", message: assistantMessage, toolResults: [] }, - { type: "agent_end", messages: [userMessage, assistantMessage] }, + { type: "message_end", message: assistantMessage }, + { type: "tool_execution_start", toolCallId: "call-1", toolName: "read", args: {} }, + { type: "message_start", message: toolResultMessage }, + { type: "message_end", message: toolResultMessage }, + { type: "turn_end", message: turnEndAssistantMessage, toolResults: [toolResultMessage] }, + { type: "agent_end", messages: [userMessage, assistantMessage, toolResultMessage] }, ]); const stdout = captureStdout(); @@ -189,6 +204,7 @@ describe("runPrintMode", () => { { type: "agent_start" }, userMessage, assistantMessage, + toolResultMessage, { type: "agent_end" }, ]); });