22 JSON Event Stream Mode
22.1 Overview
JSON Event Stream mode outputs all Pi events as newline-delimited JSON objects. It is designed for logging, piping to other tools, and building custom integrations without the bidirectional complexity of RPC mode.
22.2 Usage
pi --mode json "Summarize this codebase"Or with a pipe:
echo "Explain this function" | pi --mode jsonIn JSON mode, Pi:
- Reads the initial prompt from command-line arguments or stdin
- Processes the prompt through the full agent loop
- Outputs every event as a JSON line to stdout
- Exits when the agent turn completes
22.3 Output Format
Each line is a JSON object with this structure:
{
"type": "event_type",
"timestamp": "2026-07-31T12:00:00.000Z",
"data": { ... }
}22.3.1 Example Session
$ pi --mode json "What files are in this project?"
{"type":"session_start","timestamp":"...","data":{"sessionId":"550e8400","model":"claude-sonnet-4"}}
{"type":"model_start","timestamp":"...","data":{"model":"claude-sonnet-4","provider":"anthropic"}}
{"type":"assistant_message","timestamp":"...","data":{"type":"delta","delta":"I'll"}}
{"type":"assistant_message","timestamp":"...","data":{"type":"delta","delta":" check"}}
{"type":"assistant_message","timestamp":"...","data":{"type":"delta","delta":" the"}}
{"type":"tool_call","timestamp":"...","data":{"toolName":"bash","input":{"command":"ls -la"},"toolCallId":"tc_001"}}
{"type":"tool_result","timestamp":"...","data":{"toolCallId":"tc_001","output":{"content":[{"type":"text","text":"total 48\ndrwxr-xr-x ..."}]}}
{"type":"assistant_message","timestamp":"...","data":{"type":"complete","content":"Here are the files...","usage":{"inputTokens":1500,"outputTokens":45}}}
{"type":"model_end","timestamp":"...","data":{"usage":{"inputTokens":1500,"outputTokens":45},"cost":0.0023}}
{"type":"turn_end","timestamp":"...","data":{"totalTokens":3200,"totalCost":0.015}}
{"type":"session_end","timestamp":"...","data":{}}22.4 Event Types
22.4.1 Session Events
22.4.1.1 session_start
{
"type": "session_start",
"data": {
"sessionId": "550e8400-e29b-41d4-a716-446655440000",
"sessionPath": "~/.pi/agent/sessions/.../550e8400.jsonl",
"model": "claude-sonnet-4-20250514",
"provider": "anthropic"
}
}22.4.1.2 session_end
{
"type": "session_end",
"data": {}
}22.4.2 Model Events
22.4.2.1 model_start
{
"type": "model_start",
"data": {
"model": "claude-sonnet-4-20250514",
"provider": "anthropic"
}
}22.4.2.2 model_end
{
"type": "model_end",
"data": {
"usage": {
"inputTokens": 1500,
"outputTokens": 45,
"cacheReadTokens": 200,
"cacheWriteTokens": 0
},
"cost": 0.0023,
"durationMs": 1200
}
}22.4.3 Message Events
22.4.3.1 assistant_message (delta)
Streamed content delta:
{
"type": "assistant_message",
"data": {
"type": "delta",
"delta": "Let me "
}
}22.4.3.2 assistant_message (complete)
Full message when generation is complete:
{
"type": "assistant_message",
"data": {
"type": "complete",
"content": "Let me look at the files in this project.",
"usage": {
"inputTokens": 1500,
"outputTokens": 45
}
}
}22.4.3.3 assistant_thinking (delta)
Thinking/reasoning content delta (for reasoning models):
{
"type": "assistant_thinking",
"data": {
"type": "delta",
"delta": "The user wants to know about files..."
}
}22.4.3.4 assistant_thinking (complete)
Full thinking content:
{
"type": "assistant_thinking",
"data": {
"type": "complete",
"content": "The user wants to know about files. I should use ls..."
}
}22.4.4 Tool Events
22.4.4.1 tool_call
{
"type": "tool_call",
"data": {
"toolName": "bash",
"input": { "command": "ls -la" },
"toolCallId": "tc_001"
}
}22.4.4.2 tool_result
{
"type": "tool_result",
"data": {
"toolCallId": "tc_001",
"output": {
"content": [
{ "type": "text", "text": "total 48\ndrwxr-xr-x ..." }
]
},
"details": {}
}
}22.4.4.3 tool_error
{
"type": "tool_error",
"data": {
"toolCallId": "tc_002",
"toolName": "bash",
"error": "Command failed with exit code 1",
"stderr": "Error: file not found"
}
}22.4.5 Turn Events
22.4.5.1 turn_end
{
"type": "turn_end",
"data": {
"totalTokens": 3200,
"totalCost": 0.015,
"durationMs": 4500
}
}22.4.6 Error Events
22.4.6.1 error
{
"type": "error",
"data": {
"message": "Rate limit exceeded",
"code": "rate_limit",
"retryable": true
}
}22.4.7 Compaction Events
22.4.7.1 compaction
{
"type": "compaction",
"data": {
"entriesSummarized": 15,
"freedTokens": 8000,
"summary": "Previous conversation discussed..."
}
}22.5 Message Types
The data.type field in assistant_message events distinguishes between:
| Type | Description |
|---|---|
delta |
Streaming text fragment |
complete |
Full message after generation completes |
22.6 Processing JSON Output
22.6.1 Using jq
Filter and format events:
# Get only assistant messages
pi --mode json "Explain this code" | jq 'select(.type == "assistant_message" and .data.type == "delta") | .data.delta' -r
# Get tool calls only
pi --mode json "Fix the bug" | jq 'select(.type == "tool_call") | .data.toolName'
# Get token usage
pi --mode json "Review code" | jq 'select(.type == "model_end") | .data.usage'
# Pretty-print full output
pi --mode json "Write tests" | jq .22.6.2 Using Node.js
const { spawn } = require("node:child_process");
const pi = spawn("pi", ["--mode", "json", "Summarize this codebase"]);
let buffer = "";
pi.stdout.on("data", (data) => {
buffer += data.toString();
const lines = buffer.split("\n");
buffer = lines.pop();
for (const line of lines) {
if (!line.trim()) continue;
const event = JSON.parse(line);
switch (event.type) {
case "assistant_message":
if (event.data.type === "delta") {
process.stdout.write(event.data.delta);
}
break;
case "tool_call":
console.log(`\n[Tool: ${event.data.toolName}]`);
break;
case "turn_end":
console.log(`\n\nTokens: ${event.data.totalTokens}, Cost: $${event.data.totalCost}`);
break;
}
}
});22.6.3 Using Python
import subprocess
import json
proc = subprocess.Popen(
["pi", "--mode", "json", "Explain this code"],
stdout=subprocess.PIPE,
text=True,
)
for line in proc.stdout:
event = json.loads(line)
if event["type"] == "assistant_message":
data = event["data"]
if data["type"] == "delta":
print(data["delta"], end="", flush=True)
elif event["type"] == "tool_call":
print(f"\n[Tool: {data['toolName']}]")
elif event["type"] == "turn_end":
print(f"\n\nTotal: {data['totalTokens']} tokens, ${data['totalCost']}")22.7 Comparison with RPC Mode
| Feature | JSON Mode | RPC Mode |
|---|---|---|
| Communication | One-way (stdout only) | Bidirectional (stdin/stdout) |
| Use case | Logging, piping | Interactive control |
| Steering messages | Not supported | Supported |
| Tool result injection | Not supported | Supported |
| Model switching | Via CLI args only | At any time |
| Session management | Via CLI args only | Full control |
| Complexity | Simple | Complex |
| Best for | Scripts, CI/CD | IDE integrations, bots |
Tip
Use JSON mode when you need a simple one-shot pipeline. Use RPC mode when you need ongoing bidirectional communication with Pi.
22.8 Use Cases
22.8.1 CI/CD Integration
# Generate a PR summary
pi --mode json "Summarize changes since main" | \
jq 'select(.type == "assistant_message" and .data.type == "complete") | .data.content' -r | \
gh pr comment --body-file -22.8.2 Automated Code Review
# Run code review and save report
pi --mode json "Review code for security issues" | \
jq 'select(.type == "assistant_message" and .data.type == "complete") | .data.content' -r \
> security-review.md22.8.3 Streaming to a File
# Stream output to a file in real-time
pi --mode json "Write documentation" | \
tee raw-events.jsonl | \
jq 'select(.type == "assistant_message" and .data.type == "delta") | .data.delta' -r \
> documentation.md