[FEATURE] Stable, documented schema for ~/.claude/projects/<encoded-cwd>/<uuid>.jsonl line types
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
Context
We are building a sibling VS Code / Cursor / Windsurf extension (aims.dashboard) that runs alongside the official anthropic.claude-code extension. The extension provides a four-pane operational shell over Claude Code: a persistent always-on chat pane that we drive ourselves through the Claude Agent SDK, and a per-task tab pane that is owned by the official extension. For the per-task pane we cannot use SDK in-process hooks, because we did not spawn those sessions; the official extension did. Our only viable observation channel for those sessions is to file-watch the append-only JSONL transcripts under ~/.claude/projects/<encoded-cwd>/<uuid>.jsonl.
Direct inspection of extension.js v2.1.119 confirms there is no public VS Code API exported from the official extension, no extensionDependencies hook, no command surface for observing session state, and the IDE MCP bridge under ~/.claude/ide/<port>.lock exposes driver-side tools (manipulate the IDE) rather than observer-side events (watch what the CLI is doing). The JSONL transcript is the only durable, cross-process observation channel for sessions we did not spawn ourselves.
Several community dashboards already depend on this surface (examples in the references section). We expect more to appear as VS Code 1.109's multi-agent positioning takes hold.
What we observed
A spot-check of current local transcripts on Claude Code v2.1.118 / v2.1.119 surfaces the following top-level type values in a single mid-length project session:
agent-name
assistant
attachment
custom-title
file-history-snapshot
last-prompt
permission-mode
queue-operation
system
user
type=system carries a subtype field. The values we have observed are:
away_summary
bridge_status
compact_boundary
local_command
scheduled_task_fire
stop_hook_summary
turn_duration
When the CLI is launched with --include-hook-events, we additionally see system/hook_callback lines.
Several of these (agent-name, custom-title, queue-operation, local_command, scheduled_task_fire, pr-link, system/bridge_status) are recent additions that did not appear in older transcripts and are not described in the public docs. We have no way to know whether new types are additive (safe) or whether existing types may be renamed or removed (breaking).
Why I think this benefits Anthropic
- More durable third-party integrations. A documented and stable schema lets the ecosystem of dashboards, statusline scripts, and observability tools track Claude Code releases without breaking on every upgrade.
- Reduced support load. Tool-builders today reverse-engineer the schema from minified
extension.jsand a sample of live transcripts. A single documentation page absorbs the questions that currently end up on issues, Discord, and community blog posts. - Aligns with VS Code 1.109 multi-agent direction. The official editorial line ("running Claude and Codex agents directly alongside Copilot is the future") implies a richer ecosystem of co-resident extensions. A stable observation contract is a load-bearing piece of that ecosystem.
- Existing community dependence. Several community dashboards already parse the format (
jspw/Claude-Code-Dashboard,nateherkai/token-dashboard,withLinda/claude-JSONL-browser,phuryn/claude-usage, statusline scripts viaccusageandClaudeCodeStatusLine). Documenting the contract recognises a surface the ecosystem is already treating as public.
Proposed Solution
We would like one or more of the following:
- Documented schema for each line type and
system.subtypevalue. A single page (e.g. undercode.claude.com/docs/en/) listing eachtype/subtypeand its required and optional fields. This need not be exhaustive; the core types (user,assistant,system/*,attachment,permission-mode) cover most observer use cases.
- A stability commitment. A statement of the form "additions are safe at any minor release; removals or renames are announced in the changelog one minor release ahead." Even a SemVer-style versioning of the transcript schema (separate from the CLI SemVer if necessary) would be enough for tool-builders to pin a known-good schema and detect drift.
- Changelog entry per minor release listing JSONL additions or changes. The current changelog at
anthropics/claude-agent-sdk-typescriptand the Claude Code release notes do not mention JSONL line-type changes. A one-line note when a newtypeorsystem.subtypeis introduced would let tool-builders update their parsers in step.
Alternative Solutions
A public observation API. If the schema is too volatile to commit to, an alternative is a public event surface -- either via the existing IDE MCP bridge (additional MCP tools whose role is observation, e.g. mcp__ide__subscribeToolEvents), via a published cross-extension API on the official VS Code extension (an EventEmitter returned from activate()), or via a Language Model Chat Participant interface that other extensions can subscribe to. Any of these would let us drop the file-watch path entirely.
Priority
Critical - Blocking my work
Feature Category
Developer tools/SDK
Use Case Example
Use case
aims.dashboard provides a four-pane operational shell:
- Pane 1 (always-on CoS chat): SDK-driven; we own these sessions and use the SDK's in-process hook surface (
SessionStart,PreToolUse,PostToolUse,Notification,Stop,PreCompact). This pane is unaffected by JSONL schema changes. - Pane 2 (per-task session tabs): owned by the official extension via the URI handler
vscode://anthropic.claude-code/open?prompt=...&session=.... We file-watch the JSONL to surface tool-call status, permission prompts, and session-end signals in a sidebar tray. This pane breaks silently when an unknowntypeor new required field appears. - Pane 3 (status tray): Supabase-backed projection of state across cockpits. Receives events from both Pane 1 (SDK callbacks) and Pane 2 (JSONL tail).
- Pane 4 (preview): unaffected.
The hybrid model lets us keep the polished UX of the official extension for the per-task pane (auto-accept diffs, native diff viewer, plan-mode markdown editing, sessions list, checkpoints) while still surfacing per-tool-call activity to operators in Pane 3 within a sub-second budget.
If the JSONL schema is volatile, our Pane 2 observability degrades silently on every CLI upgrade. We currently mitigate this by pinning the CLI version via pathToClaudeCodeExecutable and writing a tolerant parser that ignores unknown type values, but a stability contract from Anthropic would let us decouple from the pinned version and keep up with upstream improvements automatically.
Additional Context
References
Related issues we found while scoping this request (these illustrate the broader observation gap and are not duplicates of this request):
- anthropics/claude-code#22968 -- high CPU/memory in long sessions; relevant because tools that observe CLI state often need to detect this condition.
- anthropics/claude-code#34640 -- feature request for folder-scoped launches via URI handler.
- anthropics/claude-code#36949 -- feature request for
claudeCode.workingDirectorysetting. - anthropics/claude-code#23119 -- JetBrains plugin discovery via the lock-file pattern (related observation surface).
- anthropics/claude-code#24594 --
--input-format stream-jsonis undocumented; tool-builders depend on undocumented surfaces. - Datadog Security Labs CVE-2025-52882 -- IDE MCP bridge auth-token model.
- Community dashboards depending on the JSONL:
jspw/Claude-Code-Dashboard,nateherkai/token-dashboard,withLinda/claude-JSONL-browser,phuryn/claude-usage. - Statusline references that show how the JSON-on-stdin schema (already documented at
code.claude.com/docs/en/statusline) became a community-stable contract -- a similar treatment of JSONL would be welcome.
Environment
- Claude Code CLI: v2.1.118 / v2.1.119 (Linux + macOS spot-checks)
- Claude Agent SDK (TypeScript): @anthropic-ai/claude-agent-sdk >= 0.2.86
- VS Code 1.109+, Cursor 3.x, Windsurf
- Operating systems: Linux (primary), macOS, Windows
Happy to provide further sample transcripts or specific spot-check evidence if useful.
8 Comments
I too would love to see this
+1, same constraint from a different downstream. Building maury (multi-host claude code config sync with local-only transcript mining) — sessions aren't owned by maury; file-watching the JSONL is the only channel. Reader lives at
src/maury/mining/transcripts.py.Fields the reader consumes today:
type,message.role,message.content[](text and tool_use blocks),message.model,sessionId,cwd,timestamp. Everything else is opportunistic.A versioned schema (e.g.
schema_versionon each record + a public types reference) would let downstream readers strict-validate and fail loud on drift. Today the failure mode is silent: a CC upgrade quietly changes a field, and the mining pass silently mis-parses transcripts before anyone notices. Strict-mode parsing + version pinning closes that hole.Hi all — small contribution to this thread. Over in vade-app/tjsonl we wrote up an observation-grounded v0.1 spec for the Claude Code transcript JSONL format and shipped a zero-dependency Python library +
tjCLI that extracts and validates against it. It's grounded in an empirical 174-session sample plus the type enumeration from this issue (#53516), and theattachment.hook_successpayload is fully specified.Not asking Anthropic to adopt anything — just leaving a pointer in case it's useful for the folks here, and as an existence proof that the de-facto format is stable enough to spec from observation:
spec/transcript-schema-spec.mdspec/transcript-schema.jsonsrc/tjsonl/Both spec and tool are MIT-licensed. Empirical answer to the falsifier we held the spec to — does SessionStart fire on every session resume? — is yes (5 of 195 sessionIds in the v0 sample showed >1 distinct
toolUseIDfor SessionStart).Two RFC threads on the tracker are deliberately open for outside-expert input:
Peer-parser maintainers (
ccusage,claude-code-log,simonw/claude-code-transcripts) — if you've been re-deriving this schema independently, the bigger win is if we converge. Happy to talk.https://claude.ai/code/session_01PbL8tp361sjebPo78ov2AD
How will you handle versions of claude code? For example, earlier versions have different keys you're missing.
Cross-link for thread tracking: filed #61998 just now requesting that
the existing recap /
post_turn_summaryoverlay also land as a JSONLentry (proposed
type: "recap"). Falls inside the lane this schemaissue already opened — different motivation (downstream consumption of
the CLI-generated summary), shared concern (a parseable, stable line
type). Flagging it here so anyone landing on this issue from a JSONL
schema angle sees both.
A stable schema also needs an unknown-record policy.
For downstream watchers, the useful contract is: record type, schema version, session id, timestamp, parent/uuid fields where applicable, content-block shape, usage shape, and whether unknown top-level fields must be preserved. Add line-level source offsets and a fixture corpus, and third-party dashboards can be strict where it matters without breaking on new event types.
Generated with ax - https://github.com/Necmttn/ax
Second integrator hitting this, from a different direction, with concrete drift instances that may be useful evidence.
I maintain Claudictive, an Overwolf in-game overlay that lets developers talk to their existing Claude Code session without alt-tabbing out of a game. Like the original poster, we did not spawn the sessions we need to observe, so file-watching
~/.claude/projects/<encoded-cwd>/<uuid>.jsonlis our only option. We write viaclaude -p --resume <id>, but everything we render comes from parsing those transcripts.The docs are explicit that this is unsupported: "The entry format is internal to Claude Code and changes between versions, so scripts that parse these files directly can break on any release." That is a fair warning, and it is exactly the problem. There is no supported alternative for reading a session you did not start, so the choice is parse-and-hope or do not build the integration.
Drift and undocumented behaviour we currently depend on
Verified against CLI 2.1.216 through 2.1.220 on Windows:
Agent, notTask. The docs describe the subagent tool asTask; the value that actually appears intool_useblocks isAgent. We found this empirically after our subagent handling silently stopped matching. This is the exact failure mode the warning predicts, and it is silent: nothing errors, features just quietly stop working.-. We decode--back to/plus a drive-letter fixup, which is guesswork. We now prefer thecwdfield from the first few lines of the transcript, but that field is itself undocumented.AskUserQuestionin atool_useblock is our only signal that a session is paused waiting for a human. Undocumented, empirically derived, and load-bearing: it drives whether our UI shows the user that their session needs them.-pis behaviour we rely on and cannot find documented.message.role,message.content[]block shapes,type: 'user' | 'result',isSidechain,timestamp,cwd, andtype: 'permission-mode'/type: 'mode'entries.One concrete consequence, from this week
We read permission mode from the transcript so the overlay can inherit the posture the user set in their editor. Because we cannot know where in the file a
permission-modeentry lives, and because reading whole multi-megabyte transcripts is not viable, we read a bounded trailing window. A user who set plan mode early in a long session has that entry fall outside the window, so we silently fail to inherit it. That is our bug to fix, but it exists because the file is an append-only log with no index and no documented way to ask "what is the current mode of this session".What would actually help, in priority order
role, text content, tool name, timestamp,cwd, and session state would cover most of what integrations do.claude session show <id> --json) would remove the need to parse files at all.Happy to supply real transcripts or test against pre-release builds if that is useful.
A downstream mitigation, not a substitute for the requested contract: I maintain Traceplain, and the concrete drift cases in this thread prompted us to make unknown-shape handling fail loud.
The v1.2.0 GitHub Action now:
The implementation and regression test are public in commit 114efc3, and v1.2.0 is green in hosted CI.
This catches the “feature quietly disappears” failure mode, but it cannot tell a benign new envelope from a consequential new session-state event. A per-line schema version, documented subset, and fixture corpus—or a read-only session JSON interface—are still the real fixes.