Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/coding-agent/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<version>` user agent.

### Fixed
Expand Down
6 changes: 4 additions & 2 deletions packages/coding-agent/docs/json.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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).

Expand Down
7 changes: 4 additions & 3 deletions packages/coding-agent/src/modes/print-mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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" }));
Expand Down
28 changes: 22 additions & 6 deletions packages/coding-agent/test/print-mode.test.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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(((
Expand Down Expand Up @@ -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" },
Expand All @@ -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();

Expand All @@ -189,6 +204,7 @@ describe("runPrintMode", () => {
{ type: "agent_start" },
userMessage,
assistantMessage,
toolResultMessage,
{ type: "agent_end" },
]);
});
Expand Down
Loading