Files
pi/packages/coding-agent/docs/json.md
T
Christian Klotz 25cc5c7bf4 docs(coding-agent): refresh documentation (#9898)
* 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
2026-09-22 16:48:19 +02:00

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")'