packages/coding-agent/docs/json.md
pi --mode json "Your prompt"
Outputs all session events as JSON lines to stdout. Useful for integrating pi into other tools or custom UIs.
Wire events use JsonAgentSessionEvent. It matches
AgentSessionEvent
except that streaming message updates omit cumulative snapshots:
type WithoutPartial<T> = T extends { partial: unknown } ? Omit<T, "partial"> : T;
type JsonAgentSessionEvent =
| Exclude<AgentSessionEvent, { type: "message_update" }>
| {
type: "message_update";
assistantMessageEvent: WithoutPartial<AssistantMessageEvent>;
};
queue_update emits the full pending steering and follow-up queues whenever they change. compaction_start and compaction_end cover both manual and automatic compaction.
Other base events come from
AgentEvent:
type AgentEvent =
// Agent lifecycle
| { type: "agent_start" }
| { type: "agent_end"; messages: AgentMessage[] }
// Turn lifecycle
| { type: "turn_start" }
| { type: "turn_end"; message: AgentMessage; toolResults: ToolResultMessage[] }
// Message lifecycle
| { type: "message_start"; message: AgentMessage }
| { type: "message_update"; message: AgentMessage; assistantMessageEvent: AssistantMessageEvent }
| { type: "message_end"; message: AgentMessage }
// Tool execution
| { type: "tool_execution_start"; toolCallId: string; toolName: string; args: any }
| { type: "tool_execution_update"; toolCallId: string; toolName: string; args: any; partialResult: any }
| { type: "tool_execution_end"; toolCallId: string; toolName: string; result: any; isError: boolean };
Base messages from packages/ai/src/types.ts:
UserMessage (line 134)AssistantMessage (line 140)ToolResultMessage (line 152)Extended messages from packages/coding-agent/src/core/messages.ts:
BashExecutionMessage (line 29)CustomMessage (line 46)BranchSummaryMessage (line 55)CompactionSummaryMessage (line 62)Each line is a JSON object. The first line is the session header:
{"type":"session","version":3,"id":"uuid","timestamp":"...","cwd":"/path"}
Followed by events as they occur:
{"type":"agent_start"}
{"type":"turn_start"}
{"type":"message_start","message":{"role":"assistant","content":[],...}}
{"type":"message_update","assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":"Hello"}}
{"type":"message_end","message":{...}}
{"type":"turn_end","message":{...},"toolResults":[]}
{"type":"agent_end","messages":[...]}
message_update records are delta-only. They omit both the cumulative message field and
assistantMessageEvent.partial to keep stream size linear. Use contentIndex and delta
to assemble live text, thinking, or tool-call arguments if needed. message_end contains
the final authoritative message.
pi --mode json "List files" 2>/dev/null | jq -c 'select(.type == "message_end")'