* docs(coding-agent): improve getting started documentation * docs(coding-agent): correct getting started details * docs(coding-agent): clarify SDK entry point * docs(coding-agent): restructure guides and references * docs(coding-agent): improve getting started guides * docs(coding-agent): refresh integration guides * docs(coding-agent): improve terminal and CLI guides * docs(coding-agent): refresh customisation guides * docs(coding-agent): clarify project trust terminology * docs(coding-agent): simplify customisation guidance * docs(coding-agent): improve runtime and reference guidance * Fix settings reference * feat(coding-agent): add Crowdin documentation sync * docs(coding-agent): separate CLI and slash command references * docs(coding-agent): correct compaction reference * docs(coding-agent): streamline package documentation * docs(coding-agent): split RPC reference documentation * Update configuration docs * Shorten config docs * docs(coding-agent): refine configuration references * docs(coding-agent): streamline settings reference * docs(coding-agent): clarify configuration reference * docs(coding-agent): clarify project trust exception * docs(coding-agent): simplify keybindings reference * docs(coding-agent): remove Crowdin integration * docs(coding-agent): turn themes reference into guide * docs(coding-agent): consolidate model and authentication docs * docs(tui): require Component.invalidate() (fixes #9358) * docs(coding-agent): document offline catalog behavior (fixes #8684) * docs(coding-agent): preserve established documentation routes * docs(coding-agent): reorganize documentation navigation * docs(coding-agent): correct audited behavior Clarify provider, session, local-model, Termux, TUI, SDK, debug, and extension behavior. Simplify the documentation audit to report only clear user-visible contradictions. * docs(coding-agent): fix broken documentation links
11 KiB
JSON Event Stream
JSON mode emits structured progress for one invocation:
pi --mode json "Review this repository"
Pi writes one session header followed by session events, then exits after the supplied prompts finish. RPC mode emits the same session-event shapes but has no session header because it is a bidirectional, long-lived protocol. See RPC Mode.
This page is the canonical reference for events shared by JSON and RPC mode. Message values use the shared message types.
Framing and process I/O
The stream uses strict JSONL framing. Each record is one JSON object terminated by LF (\n). Split records only on LF and strip an optional preceding carriage return. Unicode line and paragraph separators are valid inside JSON strings and are not record boundaries.
Node.js readline is not suitable for this stream because it also recognizes those Unicode separators. Use a byte or UTF-8 stream decoder and split on LF.
Read stdout continuously. A reader that stops consuming records can stall Pi when the pipe buffer fills. Stdout is reserved for JSONL; diagnostics and application logging go to stderr.
Session header
The first JSON-mode record is the current session header:
{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path"}
RPC mode does not emit this record. Use get_state for its current session ID and file.
Event sequence
A basic run produces records like these:
{"type":"agent_start"}
{"type":"turn_start"}
{"type":"message_start","message":{"role":"user","content":"Review this repository","timestamp":1733234401000}}
{"type":"message_end","message":{"role":"user","content":"Review this repository","timestamp":1733234401000}}
{"type":"message_start","message":{"role":"assistant","content":[],"stopReason":"pending","...":"..."}}
{"type":"message_update","usage":{"...":"..."},"assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":"Hello"}}
{"type":"message_end","message":{"role":"assistant","...":"..."}}
{"type":"turn_end","message":{"role":"assistant","...":"..."},"toolResults":[]}
{"type":"agent_end","messages":[{"...":"..."}],"willRetry":false}
{"type":"agent_settled"}
agent_end closes one low-level agent run. Automatic retry, overflow recovery, compaction retry, steering, or follow-up work can still continue. agent_settled means Pi has no remaining automatic work for that session-level run.
Agent and turn events
| Event | Fields | Meaning |
|---|---|---|
agent_start |
None | A low-level agent run started. |
agent_end |
messages, willRetry |
That low-level run ended. messages contains messages generated by the run. |
agent_settled |
None | Pi will not continue automatically through retries, compaction recovery, or queued messages. |
turn_start |
None | One assistant turn started. |
turn_end |
message, toolResults |
One assistant response and its resulting tool calls finished. |
A turn is one assistant response plus any tool calls and tool results produced by that response.
Message events
| Event | Fields | Meaning |
|---|---|---|
message_start |
message |
A message started. |
message_update |
usage, assistantMessageEvent |
An assistant message emitted a content-block update. |
message_end |
message |
A message completed. This is the authoritative final message. |
Reconstruct streaming messages
Wire message_update records are delta-only. They omit the SDK event's cumulative message field and every assistantMessageEvent.partial snapshot so stream size remains linear.
The nested event is one of:
| Type | Fields in addition to type |
Meaning |
|---|---|---|
start |
None | The provider stream started; its cumulative partial field is removed on the wire. |
text_start |
contentIndex |
A text block started. |
text_delta |
contentIndex, delta |
Append text to the block. |
text_end |
contentIndex, content |
The text block ended with authoritative content. |
thinking_start |
contentIndex |
A thinking block started. |
thinking_delta |
contentIndex, delta |
Append thinking text to the block. |
thinking_end |
contentIndex, content |
The thinking block ended with authoritative content. |
toolcall_start |
contentIndex, id, toolName |
A tool-call block started. |
toolcall_delta |
contentIndex, delta |
Append serialized argument data. |
toolcall_end |
contentIndex, toolCall |
The tool call ended with the complete ToolCall. |
done |
reason, message |
The provider stream completed successfully. |
error |
reason, error |
The provider stream ended with an error or abort message. |
The normal agent loop translates provider-level start, done, and error into message_start and message_end session events rather than emitting them as message_update. They remain admitted by the exported JsonAgentSessionEvent transformation for callers that construct a matching session event.
Use contentIndex to identify the content block. Buffer delta fields for a live display, but replace reconstructed data with the completed content in text_end, thinking_end, or toolcall_end. Replace the whole partial message with message_end.message when it arrives.
The top-level usage is the latest cumulative provider-reported usage for the assistant response. It can remain zero until completion when a provider does not report usage while streaming.
{"type":"message_update","usage":{"input":100,"output":1,"cacheRead":0,"cacheWrite":0,"totalTokens":101,"cost":{"input":0,"output":0,"cacheRead":0,"cacheWrite":0,"total":0}},"assistantMessageEvent":{"type":"text_delta","contentIndex":0,"delta":"Hello "}}
Tool execution events
| Event | Fields | Meaning |
|---|---|---|
tool_execution_start |
toolCallId, toolName, args |
Tool execution started. |
tool_execution_update |
toolCallId, toolName, args, partialResult |
The tool reported a partial result. |
tool_execution_end |
toolCallId, toolName, result, isError |
Tool execution finished. |
Use toolCallId to correlate the lifecycle. partialResult is the latest partial result supplied by the tool. Whether it replaces or extends an earlier update depends on that tool's result contract.
{"type":"tool_execution_start","toolCallId":"call_abc123","toolName":"bash","args":{"command":"ls -la"}}
{"type":"tool_execution_update","toolCallId":"call_abc123","toolName":"bash","args":{"command":"ls -la"},"partialResult":{"content":[{"type":"text","text":"partial output"}],"details":{}}}
{"type":"tool_execution_end","toolCallId":"call_abc123","toolName":"bash","result":{"content":[{"type":"text","text":"complete output"}],"details":{}},"isError":false}
Queue and state events
| Event | Fields | Meaning |
|---|---|---|
queue_update |
steering, followUp |
The pending steering or follow-up queue changed. Both fields contain the complete current queue. |
entry_appended |
entry |
An extension appended a custom session entry through pi.appendEntry(). |
session_info_changed |
name |
The session display name changed. An absent name means it was cleared. |
thinking_level_changed |
level |
The active thinking level changed. |
The entry value uses a persisted session entry type.
Compaction events
compaction_start reports why compaction began:
{"type":"compaction_start","reason":"threshold"}
reason is "manual", "threshold", or "overflow".
compaction_end contains the result when compaction succeeds:
{
"type": "compaction_end",
"reason": "threshold",
"result": {
"summary": "Summary of conversation...",
"firstKeptEntryId": "abc123",
"tokensBefore": 150000,
"estimatedTokensAfter": 32000,
"usage": {"...": "..."},
"details": {}
},
"aborted": false,
"willRetry": false
}
If compaction was aborted, result is absent and aborted is true. If it failed, result is absent, aborted is false, and errorMessage describes the failure. Successful overflow recovery sets willRetry to true before Pi retries the prompt.
See Compaction and Branch Summaries for result semantics.
Retry events
Assistant-turn retry emits:
{"type":"auto_retry_start","attempt":1,"maxAttempts":3,"delayMs":2000,"errorMessage":"529 overloaded"}
{"type":"auto_retry_end","success":true,"attempt":2}
On final failure, auto_retry_end has success: false and a finalError string.
Compaction and branch-summary retry emit:
{"type":"summarization_retry_scheduled","attempt":1,"maxAttempts":3,"delayMs":2000,"errorMessage":"terminated"}
{"type":"summarization_retry_attempt_start","source":"compaction","reason":"threshold"}
{"type":"summarization_retry_finished"}
For a branch summary, source is "branchSummary" and reason is absent. The reason on a compaction retry is "manual", "threshold", or "overflow".
RPC-only events
A direct RPC bash command emits one bash_execution_update for each output chunk. Its optional id matches the command ID. The final command response can contain truncated output, but these events stream all output:
{"type":"bash_execution_update","id":"req-1","delta":"total 48\n"}
RPC also adds extension_error when an extension handler throws:
{"type":"extension_error","extensionPath":"/path/to/extension.ts","event":"tool_call","error":"Error message"}
Extension UI records are a separate RPC subprotocol, not AgentSessionEvent values. See RPC Extension UI.
TypeScript types
The SDK's AgentSessionEvent contains cumulative streaming snapshots for in-process consumers. JSON and RPC transform only message_update:
type WithoutPartial<T> = T extends { partial: unknown } ? Omit<T, "partial"> : T;
type JsonAssistantMessageEvent<T> = T extends { type: "toolcall_start"; partial: unknown }
? WithoutPartial<T> & { id: string; toolName: string }
: WithoutPartial<T>;
type JsonAgentSessionEvent =
| Exclude<AgentSessionEvent, { type: "message_update" }>
| {
type: "message_update";
usage: Usage;
assistantMessageEvent: JsonAssistantMessageEvent<AssistantMessageEvent>;
};
Use the exported JsonAgentSessionEvent type from @earendil-works/pi-coding-agent. Its implementation is in json-event.ts.
Example
Print completed messages from a one-shot run:
pi --mode json "List files" 2>/dev/null | jq -c 'select(.type == "message_end")'