skills

Claude Code session schema

Claude Code writes each session as a JSON Lines file under a per-project directory. This page describes the record shapes, the sidecar files around them, and the joins a parser needs, strictly as observed in one evidence snapshot. It is a reference for building readers, not a specification: nothing here is a vendor contract, and the format is free to change between client versions. Related pages: session schema index, Codex, Cursor, and shared transcript-core.

1. Observation basis

All statements below come from one structure-only reconnaissance run on one machine on 2026-09-18. The evidence snapshot lives at .oat/repo/reference/research/session-schemas-2026-09-18/claude-code/ (a findings document plus a field/type/count inventory; no message text, paths or identifiers were retained).

DimensionValue
Census2,732 .jsonl files under the project directories
Sample336 files: 159 readable parent transcripts, 171 subagent transcripts, 6 workflow journals
Records parsed220,566
Date range2026-07-27 – 2026-09-18
Client versions2.1.220 – 2.1.276 (36 distinct values in the sample)

Subagent transcripts were sampled stratified (up to five per client version) plus the 60 most recent, the 15 largest, and every file matching a rare-feature marker. The largest version buckets are 2.1.263 (43 files), 2.1.251 (30) and 2.1.273 (23); seven files carry no version at all (workflow journals). Every record class observed spans essentially the whole version range: no version-gated record shape was found. Three points were independently re-checked after the initial pass and corrected where needed: records are one per LF-terminated line (the "multi-line record" report was a reader artifact); an absent is_error flag is not success evidence; and subagent results arrive on user records with a top-level origin.kind, not on attachments.

"Not observed" never means "does not exist." Every absence stated on this page is an absence of evidence on one machine over one date range. A parser should tolerate the shapes described here and degrade gracefully on everything else.

2. File layout

Transcripts live under a per-project slug directory (<slug>/). The census of file shapes:

Path shapeCountMeaning
<slug>/<session-id>.jsonl160Parent (main) session transcript
<slug>/<session-id>/subagents/agent-<agent-id>.jsonl717Subagent transcript
<slug>/<session-id>/subagents/agent-<agent-id>.meta.json717Subagent spawn metadata
<slug>/<session-id>/subagents/workflows/wf_<id>/agent-<agent-id>.jsonl1,801Workflow-spawned subagent transcript
<slug>/<session-id>/subagents/workflows/wf_<id>/agent-<agent-id>.meta.json1,801…its spawn metadata
<slug>/<session-id>/subagents/workflows/wf_<id>/journal.jsonl60Workflow journal (no sessionId, no version)
<slug>/<session-id>/tool-results/<id>.txt684Persisted (externalized) tool output
<slug>/<session-id>/tool-results/pdf-<uuid>/<n>.jpg46Rendered PDF pages
<slug>/<session-id>/workflows/wf_<id>.json, workflows/scripts/*.js60 / 34Workflow definitions
<slug>/memory/*.md83Project auto-memory (not a transcript)

Only 160 of the 2,732 .jsonl files are parent transcripts — under 6%; the other ~2,570 are subagent or workflow transcripts nested deeper. A reader that globs <slug>/*.jsonl sees parents only: a reasonable default that sees no subagent work.

3. Identity and lineage

FieldWhereObservation
sessionIdall message recordsExactly one distinct value per file, in 336/336 files. For parent files it always equals the filename stem (159/159). Resume and fork do not append to an existing file.
uuid / parentUuidall message recordsForm a DAG within a file. parentUuid is null at roots (348 in sample). No duplicate uuid in any file (0 in 220,566 records).
forkedFromassistant 735 / user 419 (1,485 records){ sessionId, messageUuid } — the resume/fork link, naming the ancestor session and the exact message forked from. Cross-file lineage is built from this, never from multiple sessionIds in one file.
leafUuidlast-prompt records (7,798)Resume pointer to the conversation leaf.
isSidechainall message recordstrue in 0 parent-file records (false 121,406, absent 48,928); true in 50,154 of 50,154 subagent-file records.
agentIdsubagent files onlyOn 100% of subagent records, absent from 100% of parent records (170,334). Format: a plus 16 hex characters, uniform.
cwd, gitBranch, versionall message recordsPresent on 171,560 / 171,560 / 100% of message records. Session-level context travels per record, not in a header.
requestId / slugassistantAPI request id (50,145); slug on assistant 18,872 and user 10,784.
promptId, promptSource, permissionMode, origin.kinduserpromptId 28,352; promptSource in typed 1,858 / system 2,220 / queued 244 / sdk 538; permissionMode 2,428; origin.kind in human 1,109 / task-notification 1,130 / coordinator 21 / peer 2. Distinguishes human input from injected or automatic prompts.

There is no file header record: identity fields repeat on every message record, so a streaming reader can establish sessionId, cwd, gitBranch and version from the first record it parses.

Lineage join through .meta.json

Each subagent transcript has a sibling agent-<agent-id>.meta.json, a flat object; key presence was censused over all 2,518 meta files, typing sampled over 400.

KeyTypeNotes
agentTypestringSubagent type name; always present
spawnDepthnumberAlways present; gives nesting depth
modelstringResolved model
descriptionstringShort task description; may be absent
toolUseIdstringtoolu_* id of the parent's Agent tool_use. The join key for directly spawned subagents; a structure-only re-check found it on every plain subagents/*.meta.json and on no workflow-spawned meta file
parentAgentIdstringPresent for nested subagents (agent spawns agent)
isFork, requestNonInteractive, requestShape, spawnedWithWorktree, worktreeBranch, worktreePath, stoppedByUsermixedOptional

For directly spawned subagents the parent-to-child join is parent Agent tool_use.id → meta.toolUseId → that meta file's sibling agent-<agent-id>.jsonl; parentAgentId chains nested spawns. Workflow-spawned subagents carry no toolUseId, so they join to their parent by directory containment only.

4. Record taxonomy

Every line is a JSON object with a top-level type; system records additionally carry subtype, and user / assistant records carry message.role.

type (subtype)RecordsFilesVersionsNotes
attachment87,914320.220–.276Largest class; 44 distinct attachment.type values (section 8)
assistant50,178321.220–.276One record per content block, not per API response (section 8)
user28,413326.220–.276Carries tool_result blocks and toolUseResult
last-prompt7,798157.220–.276{type, sessionId, leafUuid, lastPrompt?} — resume pointer
atis-latch7,241150.220–.276Host/UI state latch
mode / permission-mode6,346 / 6,00857 / 56.220–.276
ai-title5,84463.220–.276Generated session title
queue-operation5,566137.220–.276operation in enqueue 2,790 / dequeue 1,495 / remove 1,286 / popAll 1
bridge-session4,82051.220–.270Not seen at or above .272 in the sample
pr-link3,44621.220–.273Host-specific
cost-state3221.233–.270Host-specific
system stop_hook_summary2,26966.220–.276level: "suggestion", no content
system turn_duration2,12147.220–.276No level, no content
file-history-snapshot1,10054.220–.276snapshot.trackedFileBackups keyed by absolute file path
system away_summary35542.220–.275content: string
custom-title / agent-name206 / 2064 / 4.220–.270
file-history-delta19926.220–.276
frame-link761.247Single session; host-specific
system informational4319.220–.261level in warning 38 / notice 5
system local_command4013.220–.266level: "info", content: string
started / result39 / 396 / 6(journal)Workflow journal.jsonl only; no sessionId, version or timestamp
system compact_boundary209.220–.273level: "info", plus compactMetadata
artifact-autoreact-ledger / artifact-comment-monitor15 / 41 / 1.247Host-specific
system scheduled_task_fire42.261–.272
system agents_killed22.251–.258No content
system bridge_status11.259

Several classes (atis-latch, bridge-session, pr-link, frame-link, ai-title, custom-title, agent-name, artifact-*, cost-state) look host- or wrapper-injected rather than core Claude Code. Treat the set above as open: a reader must tolerate unknown top-level type values rather than failing or dropping the file. The common envelope on user / assistant / attachment records is uuid, parentUuid, sessionId, timestamp, version, cwd, gitBranch, isSidechain, userType (only external observed), and entrypoint (cli 141,596 / sdk-cli 19,447 / sdk-ts 10,620).

5. Tool calls and results

A tool call is a tool_use block inside assistant.message.content; its result is a tool_result block in a following user.message.content, correlated by tool_use_id.

MetricValue
tool_use blocks25,149 (all ids toolu_*)
Paired with at least one tool_result in the same file25,146
tool_use with no tool_result in the same file3
tool_result with no matching tool_use in the same file0
tool_use receiving more than one tool_result0

Within a file, tool_use_id → tool_result is effectively a total 1:1 function, and correlation needs no cross-file join: a subagent's tool calls live entirely inside that subagent's file. Two extra aids appear on user records: sourceToolAssistantUUID (25,106 — the uuid of the assistant record that emitted the call) and sourceToolUseID (135).

tool_result content

user.message.content[type == "tool_result"].content is polymorphic: a string in 23,871 cases, otherwise an array of text (773), image (420) or tool_reference (82) blocks. tool_reference blocks carry a tool_name field — a second, block-level place a tool name appears.

{
  "type": "user",
  "uuid": "<uuid>",
  "message": {
    "role": "user",
    "content": [
      { "type": "tool_result", "tool_use_id": "<tool-use-id>", "content": "…" }
    ]
  },
  "toolUseResult": { "…": "…" }
}

The sibling toolUseResult carrier

A toolUseResult field sits beside message on 22,551 user records and holds the structured form of the result. It is not always an object: about 480 cases are a bare string (Bash 395, Agent 34, Read 13, Write 8, Edit 6, plus several MCP tools, Skill, Monitor, AskUserQuestion, StructuredOutput, TaskStop), some an array, and some a JSON-encoded string. A reader must type-check before destructuring.

TooltoolUseResult keys
Bashstdout, stderr, interrupted, isImage, noOutputExpected, plus optional bashEditDiff, gitOperation, backgroundTaskId, backgroundCwdHint, persistedOutputPath, persistedOutputSize, returnCodeInterpretation, staleReadFileStateHint, timedOutAfterMs, dangerouslyDisableSandbox
Readtype in text 811 / image 114 / parts 16 / file_unchanged 1; file (object)
EditfilePath, oldString, newString, replaceAll, originalFile, structuredPatch, userModified, plus staleRecovered, memdirStamped
Writetype in create 231 / update 54; filePath, content, originalFile, structuredPatch, userModified
AgentagentId, status, description, prompt, resolvedModel, isAsync, outputFile, canReadOutputFile (553×); a rare synchronous variant adds agentType, content, usage, toolStats, totalTokens, totalToolUseCount, totalDurationMs, harnessNoteCount, harnessSectionHash, harnessTailCount (1×)
WebFetch / WebSearchurl, code, codeText, bytes, durationMs, result / query, results, searchCount, durationSeconds
WorkflowworkflowName, runId, taskId, taskType, scriptPath, status, summary, transcriptDir
Skill / Monitor / ToolSearch / TaskOutputcommandName, success, allowedTools? / taskId, timeoutMs, persistent / query, matches, total_deferred_tools / task, retrieval_status
SendMessage / Artifactmessage, success, pin?, msg_id?, resumedAgentId?, display? / artifact_id, url, version, title, path, updated, liveSubscription
AskUserQuestionquestions, answers (keyed by full question text — a privacy hazard), annotations?

Tool-call volume in the sample is dominated by Bash 20,603, then Read 1,431, Edit 1,002, Agent 593, Write 337, WebFetch 286, SendMessage 188, Skill 138, WebSearch 136, AskUserQuestion 132, ToolSearch 84, Workflow 60, Monitor 47, StructuredOutput 32, and a long tail below 15 calls each, plus 39 MCP calls.

MCP

MCP tool calls use the flat name mcp__<server>__<tool> in tool_use.name. There is no separate server or namespace field on the block, so a reader that wants the server must split the name. Thirty-nine MCP calls appear in the sample, across a handful of servers (names redacted from the snapshot). A separate mcpMeta object appears on 12 user records with structuredContent and _meta (the MCP structured-content channel), and MCP tool definitions appear inside attachment records at attachment.tools[].name and attachment.entries[].name.

Denials, rejections, interruptions

SignalWhereObservation
toolDenialKindtop level on user records48 occurrences, sole value "user-rejected". The only structured permission-denial signal; a sibling of message, not inside the tool_result.
interruptedMessageIdtop level on user records71 occurrences — the assistant message whose turn the user interrupted.
toolUseResult.interruptedBash onlyfalse in all 18,597 sampled observations; true appears in 1 of 2,732 files corpus-wide.
Hook blockingattachment.typehook_success 56,349, hook_non_blocking_error 45, hook_blocking_error 1. There is no per-tool-call hook-block field: the block is a separate attachment record that must be correlated by position or parentUuid.

Free-text matching on denial phrasing is unreliable: "rejected" and "permission" occur in hundreds of ordinary tool outputs carrying is_error: false.

6. Subagent results

Subagent transcripts are separate files for every client version observed (2.1.220 – 2.1.276); none are inlined into the parent. The parent's Agent tool_use.input carries description (593), prompt (593), subagent_type (572), model (532), run_in_background (121) and isolation (7). The paired toolUseResult carries agentId, status, resolvedModel, isAsync, outputFile and canReadOutputFile.

status is async_launched in 553 of 554 cases (completed once), so the parent's tool result records the launch, not the outcome: no result text, no usage, no tool counts, no duration. outputFile points outside the project transcript directory.

A parent-only read can therefore name subagent activity — type, description, model and agentId per spawn — but cannot see it. To see the work, follow agentId (or meta.toolUseId) into the subagent file; 503 of 554 agentIds in the sample resolve to an existing <session-dir>/subagents/agent-<id>.jsonl and 51 do not, from older or pruned sessions, so a reader must tolerate dangling agent ids. Results reach the parent later as separate records — principally user records whose top-level origin.kind == "task-notification" (1,130), and SendMessage tool results. In 553 of 554 observed cases the Agent tool result itself carries only launch metadata; one synchronous exemplar carried the result inline.

7. Outcome evidence

There is no exit code anywhere. No numeric exitCode / returnCode field was found on any of the 22,551 toolUseResult carriers. Bash records only stdout, stderr, interrupted, isImage and noOutputExpected; returnCodeInterpretation is a free-text string (148 occurrences), not a number. The only machine-readable outcome bit is is_error on the tool_result block:

SignalCount
is_error present and false20,269 (Bash 20,206, Workflow 60, SendFeedback 3)
is_error present and true496
is_error absent~4,380 (Read, Edit, Write, Agent, WebFetch, WebSearch, Skill, MCP, …)

is_error is emitted as an explicit boolean essentially only by Bash. Every other tool omitted it on success and set it to true on failure in this sample. That is an observation about this sample, not a recorded success signal. The evidence rule is:

is_error === true is recorded failure evidence. is_error === false is recorded non-failure evidence. An absent is_error records nothing: report the outcome as unknown unless another recorded field (for example a toolUseResult status) establishes it. Never treat a missing flag as proof of success.

Failure counts in the sample: Bash 395, Agent 34, Read 13, Write 8, Edit 6, Skill 4, AskUserQuestion 4, Monitor 2, and 3 MCP calls. Other outcome fields on assistant records: isAbortedMidStream (3), truncatedAfterOutput (4), isApiErrorMessage (38), apiErrorStatus (5, number) and error (26, string).

A reader must report as unknown: exit codes; whether a Bash failure was a non-zero exit, a timeout or a sandbox denial (only free text distinguishes them); whether a non-Bash tool succeeded semantically; and whether a denial was a permission prompt or a hook block, except for the 48 toolDenialKind cases.

8. Session metadata

Models. message.model values: claude-opus-5 28,522, claude-fable-5-1 11,359, claude-fable-5 7,544, claude-sonnet-5 2,798, <synthetic> 38 (error/placeholder records). Mid-session model changes are real: of 159 parent files, 133 use one model, 12 use two, 5 use three, 9 have none; all 171 sampled subagent files use exactly one. effort is a top-level assistant field (high 35,819, medium 7,564, xhigh 6,834, low 8), with a nullable perTurnEffort (5,821).

Streaming duplication — a double-counting hazard. Assistant records are written one per content block, not one per API response: each carries apiBlockIndex (25,303) and repeats the same message.id. Of 23,715 (file, message.id) groups, 17,027 have more than one record and only 6,688 are singletons, while message.usage is present on all 50,261 assistant records, so summing usage naively over-counts by roughly 2×. Deduplicate on (sessionId, message.id) before aggregating usage.

The activity reader follows that boundary across content-block records. Equal copies collapse; conflicting copies produce a diagnostic and only one copy is retained. A usage carrier without message.id remains explicitly uncertain and is not deduplicated. message.model is the only model attached to that sample. Only token-valued fields are reported; service tier, geography, speed, and server-tool request counts are excluded.

message.usage fields (n = 50,261): input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens and cache_creation are present on 100%; service_tier (50,223 / null 38), inference_geo (same split), output_tokens_details (36,837 / null 33), server_tool_use (39,791), iterations (39,753 / null 38) and speed (39,753 / null 38) are partial. message.diagnostics (object or null, 50,173) carries cache_miss_reason.{type, cache_missed_input_tokens} (620).

stop_reason: tool_use 36,164, null 10,470, end_turn 3,589, stop_sequence 38. requestId appears on 50,145 assistant records and identifies the API request.

Thinking blocks. 16,748 thinking blocks exist, but only 830 (5.0%) carry non-empty thinking text; 15,918 (95.0%) are signature-only. A reader must not assume reasoning text is available. attachment.type == "thinking_stripped" (6) marks removal points.

Compaction. isCompactSummary: true on 20 user records (9 of 2,732 files corpus-wide), paired with a system record whose subtype is compact_boundary (20), whose compactMetadata keys are trigger, preTokens, postTokens, durationMs, preservedMessages, preservedSegment, preCompactDiscoveredTools and cumulativeDroppedTokens. Compacted records also carry isVisibleInTranscriptOnly: true (20), and a compact_file_reference attachment (41) points at externalized pre-compaction content. system records always have a subtype; level (info / warning / notice / suggestion) appears only on some subtypes; content is always a string when present and is absent entirely on stop_hook_summary, turn_duration and agents_killed.

attachment.type is the richest metadata channel: 44 distinct values over 87,914 records, and the set should be treated as open. The largest are hook_success 56,349, total_tokens_reminder 17,211, batching_reminder_sent 5,232, bash_output_audience_note 3,372, queued_command 738 and environment 696; the rare tail carries the structural signals cited elsewhere on this page, including compact_file_reference 41, read_truncation_notice 31, thinking_stripped 6 and hook_blocking_error 1. Full counts are in the snapshot inventory.

Attribution fields on assistant records: attributionAgent 16,101, attributionSkill 9,800, attributionMcpServer / attributionMcpTool 82, attributionPlugin 22, advisorModel 373. Slash commands have no structured record type: they appear as user.message.content strings containing <command-name> (130 files) and <local-command-stdout> (70), and as system records with subtype: local_command (40).

attributionSkill is a top-level assistant-record field, not a member of message. A tool_use whose native name is Skill is separate structural invocation evidence, and its full caller-supplied structured input remains ordinary bounded call input. It is distinct from attachment instruction content, which is not copied into skill metadata. Names-only source metadata also appears in attachment.type == "skill_listing" at attachment.names[] (availability) and attachment.type == "invoked_skills" at attachment.skills[].name (recorded invocation). Attachment content and path bodies are not needed for these names. Available names are deduplicated within the captured source using the latest recorded locator; invoked names remain per occurrence. source-skill-names coverage counts both source carriers and distinguishes a valid empty names or skills array from absent carriers. Its captured count is the deduplicated available names plus every invoked occurrence. None of these carriers records a skill version.

9. Externally persisted output and sizes

Large Bash output is written to a sidecar file and referenced two ways that do not carry the same information. In the transcript text, the tool_result content contains a bare <persisted-output> marker (270 occurrences in the sample; no attributes observed on the tag). The path appears only in the sibling toolUseResult.persistedOutputPath, alongside persistedOutputSize — 254 carriers, all on Bash. A reader consuming only message.content[type == "tool_result"] sees the marker and cannot resolve it. Every observed path has the shape <slug>/<session-id>/tool-results/<9-char-id>.txt, and 251 of the 254 targets still exist on disk (3 missing), so resolution must tolerate a missing file. Corpus-wide the literal token persisted-output appears in 444 of 2,732 files. Other sidecars: tool-results/pdf-<uuid>/*.jpg (46 rendered PDF pages), one tool-results/*.pdf, and workflow definitions under <session-id>/workflows/.

Payload sizes, characters per tool_result block; overall (n = 25,146) p50 889, p90 7,879, p99 162,338, max 681,794.

Toolnp50p90p99max
Read1,43120,706201,762416,559681,794
Bash20,6018846,08020,94229,937
WebFetch2851,0472,55745,03845,038
mcp__<server>__<tool> (one MCP read tool)59,28024,660—24,660
WebSearch1362,4943,0433,8746,017
Agent5931,0841,0991,1193,248
Edit1,002210254356490

Bash output is effectively capped near 30 KB, with overflow going to tool-results/. Read is not capped and dominates memory cost: a reader that materializes whole records should stream or cap Read results itself. The word "truncated" appears in 189 tool_result payloads, but no structured truncation field or marker token was found; the structured truncation signals that do exist are attachment.type == "read_truncation_notice" (31) and truncatedAfterOutput (4).

10. Known errors in earlier research

The earlier research packet (2026-09-10) carried a Claude Code record schema. It claims three things not observed here in 2,732 files across 2.1.220 – 2.1.276:

  • sessionID and session_id — only sessionId exists.
  • A summary property and type: "summary" records — zero corpus-wide.
  • model as a top-level record property — the model lives at message.model; a top-level model exists only on attachment records whose attachment.type is model.

It correctly flagged toolUseResult.answers being keyed by full question text as a privacy hazard, and its other listed fields (toolUseResult.annotations and .agentId, queue-operation, isCompactSummary, leafUuid, isMeta, permissionMode, slug, entrypoint, userType, cwd, gitBranch) were all confirmed.

It omits these, all load-bearing for correlating activity: isSidechain, agentId, subtype on system records, forkedFrom, toolDenialKind, interruptedMessageId, sourceToolAssistantUUID, sourceToolUseID, requestId, apiBlockIndex, effort / perTurnEffort, the attribution* family, promptSource, promptId, origin, mcpMeta, message.diagnostics, isApiErrorMessage / apiErrorStatus, isAbortedMidStream, truncatedAfterOutput, queuePriority and scheduledTaskId.

Two structural facts it does not cover at all, each changing a reader's design: subagent transcripts are separate files joined through .meta.json toolUseId (the packet describes only a toolUseResult.agentId extraction path), and Agent tool results are launches, not outcomes (553 of 554).

11. Not observed / not determined

Each item was searched for and not found in the stated corpus — an absence of evidence on one machine, not a statement that the shape cannot exist.

Searched forCorpus searched
type: "summary" or a summary top-level property; type: "progress"2,732 files
isSidechain: true, or any agentId, inside a parent <session-id>.jsonl159 parent files / 170,334 parent records
A file containing more than one sessionId336 files
Duplicate uuid within a file220,566 records
A tool_use receiving two tool_results, or a tool_result with no tool_use in the same file25,149 tool uses
A numeric exitCode / returnCode field22,551 toolUseResult carriers
redacted_thinking blocks16,748 thinking blocks
stop_reason: "max_tokens"50,261 assistant records
A structured truncation field or <truncated>-style marker25,146 tool_result payloads
toolDenialKind values other than user-rejected48 occurrences
A server/namespace field on MCP tool_use blocks39 MCP calls
toolUseResult.interrupted: true18,597 sampled Bash results (observed in 1 of 2,732 files corpus-wide)
Version-gated record shapeswhole sample; narrower ranges only on low-frequency host-specific classes, which sampling explains

Withdrawn worker claim: multi-line records. An initial scanner reported records spanning 2–9 physical lines. That abandoned scanner treated U+2028 and U+2029 inside transcript string values as record boundaries. Splitting on the LF byte only yields 0 malformed lines across 300 recent files (256,296 lines). Records are one per LF-terminated line. Do not build multi-line record recovery. Split on \n bytes only, and keep a fixture whose string values contain U+2028, U+2029 and \r.

12. Fixture checklist

Shapes worth a fixture, with a predicate selecting an exemplar record (usable as jq -c 'select(<predicate>)' <file>). Sanitize before committing; the last row holds private keys by design.

GroupShapesStructural predicate
Streaming duplicationAssistant turn split across content blocks.type=="assistant" and .apiBlockIndex>0
Bash carrier variantsSuccess with structured carrier; background task; git-operation enrichment(.toolUseResult|type)=="object" and (.toolUseResult|has("stdout")); .toolUseResult.backgroundTaskId? != null; .toolUseResult.gitOperation? != null
FailureBash failure (and any is_error failure).type=="user" and ([.message.content[]?|select(.type=="tool_result" and .is_error==true)]|length>0)
Persisted outputBash result externalized to tool-results/.toolUseResult.persistedOutputPath? != null
Carrier type mismatchtoolUseResult as a bare string; as an array(.toolUseResult|type)=="string"; (.toolUseResult|type)=="array"
Edit/Read/Write variantsEdit with structuredPatch; Read image / parts / file_unchanged.toolUseResult.structuredPatch? != null; .toolUseResult.type? | IN("image","parts","file_unchanged")
Agent launch vs resultAsync launch (common); synchronous result with usage and tool stats; the parent's Agent call.toolUseResult.status?=="async_launched"; .toolUseResult.toolStats? != null; .type=="assistant" and ([.message.content[]?|select(.type=="tool_use" and .name=="Agent")]|length>0)
Subagent lineageSubagent record; nested subagent; spawn-metadata join key.isSidechain==true and (.agentId|type)=="string"; on agent-*.meta.json: .parentAgentId != null; .toolUseId != null and .agentType != null
Subagent result deliveryTask-notification user record.type=="user" and .origin.kind=="task-notification"
Session lineageFork/resume link; resume leaf pointer; mid-session model change.forkedFrom? != null; .type=="last-prompt"; two distinct .message.model values within one sessionId
Interruption and denialUser tool denial; interrupted turn.toolDenialKind? != null; .interruptedMessageId? != null
Assistant-level errorsAborted mid-stream or truncated; API error record.isAbortedMidStream==true or .truncatedAfterOutput==true; .isApiErrorMessage==true
CompactionSummary record and its boundary.isCompactSummary==true; .type=="system" and .subtype=="compact_boundary"
Attachment signalsHook blocking error; read truncation notice.type=="attachment" and .attachment.type=="hook_blocking_error"; .type=="attachment" and .attachment.type=="read_truncation_notice"
MCPMCP call and its structured content([.message.content[]?|select(.type=="tool_use" and (.name|startswith("mcp__")))]|length>0); .mcpMeta? != null
Block-array resultstool_result with text / image / tool_reference blocks([.message.content[]?|select(.type=="tool_result" and (.content|type)=="array")]|length>0)
ThinkingSignature-only block; block with text([.message.content[]?|select(.type=="thinking" and (.thinking|length)==0)]|length>0); same with >0
Host/UI recordsSlash-command turn; queue-operation lifecycle.type=="user" and (.message.content|type)=="string" (text contains <command-name>); .type=="queue-operation" and .operation=="dequeue"
WorkflowWorkflow subagent and its journal.toolUseResult.workflowName? != null; in the journal: .type=="started" or .type=="result"
Line-splitting guardRecord whose string values contain U+2028 / U+2029 / \rNot selectable with jq; construct synthetically and assert the reader splits on \n bytes only
Private-key shapes (sanitize)File-history snapshot (absolute-path keys); AskUserQuestion answers (question-text keys).type=="file-history-snapshot"; .toolUseResult.answers? != null

On this page