Stream JSON
May 14, 2026 ยท View on GitHub
Claude's legacy print-mode stream:
claude -p --verbose --output-format stream-json "..."
emits runtime JSONL events. In local samples from Claude Code 2.1.140, a simple turn produced:
system/init, with runtime metadata such ascwd, tools, model, plugins, and Claude Code version.rate_limit_event, when available.assistant, containing a full Claude message object.result, containing the final text, session id, turn count, timing, usage, cost, and terminal status.
A tool-use turn produced the same shape plus:
assistantrecords withmessage.contentblocks such astool_use.userrecords withmessage.contentblocks such astool_resultand optionaltool_use_resultmetadata.- A final
assistanttext message followed byresult.
claude-pty-wrapper --session-jsonl is different: it emits the raw durable
session records from Claude's ~/.claude/projects/.../<session-id>.jsonl file.
Those records use Claude's persisted history shape, often including camel-case
fields such as sessionId and terminal records such as
{"type":"system","subtype":"turn_duration"}.
claude-pty-wrapper -p --output-format stream-json translates the durable
session records into a legacy-like stream:
- It emits a synthetic
system/initevent. If Claude persisted init metadata, the wrapper forwards known metadata fields; otherwise it suppliescwdandsession_id. - It suppresses the initial prompt
userrecord, matching legacy print-mode stream output. - It forwards
assistantmessage records with their fullmessage.contentblocks intact, including text, tool calls, reasoning-style blocks, and other content types Claude persists. - It forwards
usertool-result records so consumers can observe tool outputs. - It emits a synthetic
resultrecord after the durableturn_durationcompletion marker. - It accepts
--include-partial-messagesfor CLI compatibility but silently ignores it because durable session files do not contain runtime partial message deltas.
The translation is compatibility-oriented, not byte-for-byte identical. The wrapper cannot synthesize data that is not present in the durable session file, such as rate-limit metadata, exact API timings, full cost accounting, or every runtime-only init field.
Local Durable Session Findings
A local review of recent ~/.claude/projects/**/*.jsonl files and
~/.local/state/task-runner run state found these durable-session patterns:
- Task-runner stores Claude
backendSessionIdvalues and resolved~/.claude/projects/.../<session-id>.jsonlpaths inrun.jsonandrun-events.jsonl, so those files are representative wrapper inputs. - Durable session records use
sessionId; legacy print-mode stream JSON usessession_id. The translator normalizes tosession_id. - Durable sessions commonly include non-conversation metadata records such as
last-prompt,custom-title,agent-name,attachment,ai-title,permission-mode,queue-operation, andfile-history-snapshot. The translator ignores these. - Durable
systemrecords observed includeturn_duration,stop_hook_summary,away_summary, andlocal_command. Onlyturn_durationterminates the translated stream and createsresult. - Durable sessions often do not persist
system/init; the translator emits a minimal synthetic init event when no persisted init is observed before the turn begins. - Assistant content block shapes observed include
text,thinking, andtool_use. Tool-use blocks includecaller,id,input,name, andtype. - User tool-result blocks include
content,tool_use_id,type, and sometimesis_error. - Durable user records commonly store tool result metadata as
toolUseResult; legacy stream JSON usestool_use_result. The translator emitstool_use_result.