diff --git a/docs/agents/formats/claude-code/README.md b/docs/agents/formats/claude-code/README.md index a8773776..2a600acc 100644 --- a/docs/agents/formats/claude-code/README.md +++ b/docs/agents/formats/claude-code/README.md @@ -96,15 +96,20 @@ entry type are grouped under it. | Field | Defined in | |--------------------------------|------------| | `agentId` | [jsonl-envelope.md](jsonl-envelope.md), [entry-types.md §Sidechains](entry-types.md#sidechains) | +| `agentName` | [entry-types.md](entry-types.md) | +| `aiTitle` | [entry-types.md](entry-types.md) | | `attachment` | [jsonl-envelope.md](jsonl-envelope.md), [entry-types.md](entry-types.md) | +| `atis` | [entry-types.md](entry-types.md) | | `cache_creation` / `cache_*_input_tokens` | [usage.md](usage.md) | | `caller` (on `tool_use`) | [messages.md](messages.md) | | `compactMetadata` | [entry-types.md](entry-types.md), [session-chains.md](session-chains.md) | | `content` (envelope, on `queue-operation`) | [entry-types.md](entry-types.md) | | `content` (inside `message` / `tool_result`) | [messages.md](messages.md) | +| `customTitle` | [entry-types.md](entry-types.md) | | `cwd` | [jsonl-envelope.md](jsonl-envelope.md), [writing-compatible-jsonl.md](writing-compatible-jsonl.md) | | `durationMs` | [entry-types.md](entry-types.md) | | `entrypoint` | [jsonl-envelope.md](jsonl-envelope.md) | +| `frameUrl` / `path` / `title` (on `frame-link`) | [entry-types.md](entry-types.md) | | `gitBranch` | [jsonl-envelope.md](jsonl-envelope.md) | | `hookCount` / `hookInfos` / `hookErrors` | [jsonl-envelope.md](jsonl-envelope.md) | | `id` (inside `message`) | [messages.md](messages.md) | @@ -124,11 +129,14 @@ entry type are grouped under it. | `message` | [messages.md](messages.md) | | `messageCount` (envelope) | [entry-types.md](entry-types.md) | | `messageId` | [jsonl-envelope.md](jsonl-envelope.md), [entry-types.md](entry-types.md), [known-issues.md](known-issues.md) | +| `mode` | [entry-types.md](entry-types.md) | | `model` (inside `message`) | [messages.md](messages.md) | | `operation` | [entry-types.md](entry-types.md) | | `parentUuid` | [jsonl-envelope.md](jsonl-envelope.md), [known-issues.md](known-issues.md) | | `permissionMode` | [entry-types.md](entry-types.md), [writing-compatible-jsonl.md](writing-compatible-jsonl.md) | +| `prNumber` / `prUrl` / `prRepository` | [entry-types.md](entry-types.md) | | `preventedContinuation` | [jsonl-envelope.md](jsonl-envelope.md) | +| `relocatedCwd` | [entry-types.md](entry-types.md), [writing-compatible-jsonl.md](writing-compatible-jsonl.md) | | `requestId` | [jsonl-envelope.md](jsonl-envelope.md) | | `role` | [messages.md](messages.md) | | `server_tool_use` | [usage.md](usage.md) | @@ -156,6 +164,7 @@ entry type are grouped under it. | `userType` | [jsonl-envelope.md](jsonl-envelope.md) | | `uuid` | [jsonl-envelope.md](jsonl-envelope.md) | | `version` | [jsonl-envelope.md](jsonl-envelope.md) | +| `worktreeSession` | [entry-types.md](entry-types.md), [writing-compatible-jsonl.md](writing-compatible-jsonl.md) | For the mapping from these JSON keys to Rust fields in `ConversationEntry`, see the parser-surface table in diff --git a/docs/agents/formats/claude-code/entry-types.md b/docs/agents/formats/claude-code/entry-types.md index b139aac6..f469b751 100644 --- a/docs/agents/formats/claude-code/entry-types.md +++ b/docs/agents/formats/claude-code/entry-types.md @@ -17,6 +17,15 @@ a large fraction of the file. | `permission-mode` | no | no | Records the active permission mode. | | `queue-operation` | no | no | Enqueue/dequeue of a typed-ahead message while an assistant turn is in flight. | | `last-prompt` | no | no | Cached last user prompt. | +| `ai-title` | no | no | Generated session title. | +| `custom-title` | no | no | Explicitly set session title. | +| `agent-name` | no | no | Agent name for the session. | +| `mode` | no | no | Active mode. Only `"normal"` observed. | +| `atis-latch` | no | no | Opaque latch value. Purpose unknown. | +| `pr-link` | no | no | Pull request linked to the session. | +| `frame-link` | no | no | Frame URL linked to a local file. | +| `relocated` | no | no | New working directory for the session. | +| `worktree-state` | no | no | Git worktree the session is in; `null` after it leaves. | | `summary` | no | no | Conversation summary. May live in a different JSONL file than the conversation it describes. | | `compact_boundary` | no (usually) | yes | Marks an autocompaction event. Also appears as `system.subtype` in some versions. | | `progress` | no | yes | Long-running tool progress event. Should be skipped when reconstructing a transcript. | @@ -297,6 +306,131 @@ Cached last user prompt, for resume / history purposes: --- +## Session metadata lines + +The line types below share one shape: `type`, `sessionId`, and one +payload key. None carries `uuid`, `timestamp` (except where noted), +`cwd`, or `parentUuid`. All are observed in a local store spanning +client versions 2.1.215 – 2.1.245; the first version that writes each +one is not known. + +### `ai-title` + +Generated session title. Rewritten as the session progresses. + +```json +{"type": "ai-title", "aiTitle": "Fix the flaky test", "sessionId": "..."} +``` + +### `custom-title` + +Explicitly set session title. + +```json +{"type": "custom-title", "customTitle": "config-export", "sessionId": "..."} +``` + +### `agent-name` + +Agent name for the session. In observed data it carries the same value +as the session's `custom-title`. + +```json +{"type": "agent-name", "agentName": "config-export", "sessionId": "..."} +``` + +### `mode` + +Active mode. Only `"normal"` is observed. + +```json +{"type": "mode", "mode": "normal", "sessionId": "..."} +``` + +### `atis-latch` + +Opaque latch value. `atis` is an empty string, a 16-character hex +string, or a ~190-character string. Its purpose is unknown. + +```json +{"type": "atis-latch", "atis": "", "sessionId": "..."} +``` + +### `pr-link` + +Pull request linked to the session. Carries a `timestamp`. + +```json +{ + "type": "pr-link", + "sessionId": "...", + "prNumber": 233, + "prUrl": "https://github.com/owner/repo/pull/233", + "prRepository": "owner/repo", + "timestamp": "2026-08-25T15:10:16.000Z" +} +``` + +### `frame-link` + +Frame URL linked to a local file. Carries a `timestamp`. One sample +observed. + +```json +{ + "type": "frame-link", + "sessionId": "...", + "path": "/home/user/project/design.html", + "frameUrl": "https://...", + "title": "...", + "timestamp": "2026-08-25T15:10:16.000Z" +} +``` + +### `relocated` + +New working directory for the session. `relocatedCwd` is an absolute +path. + +```json +{"type": "relocated", "relocatedCwd": "/home/user/project", "sessionId": "..."} +``` + +### `worktree-state` + +Git worktree the session is in. `worktreeSession` is an object on +entry and `null` after the session leaves the worktree. + +```json +{ + "type": "worktree-state", + "sessionId": "...", + "worktreeSession": { + "originalCwd": "/home/user/project", + "preEnterOriginalCwd": "/home/user/project", + "worktreePath": "/home/user/project/.claude/worktrees/topic", + "worktreeName": "topic", + "worktreeBranch": "user/topic", + "originalBranch": "main", + "originalHeadCommit": "b31f2c5...", + "sessionId": "..." + } +} +``` + +`originalBranch` and `originalHeadCommit` appear when the worktree was +created for the session. `enteredExisting: true` replaces them when +the session entered a worktree that already existed. The three path +fields are absolute. `worktreeSession.sessionId` equals the line's +`sessionId` in every observed sample. + +A writer that moves a session to another directory or renames it must +decide what to do with `relocatedCwd`, the three `worktreeSession` +paths, and the inner `sessionId`. See +[writing-compatible-jsonl.md](writing-compatible-jsonl.md). + +--- + ## `summary` Conversation summary entries. Minimal shape: diff --git a/docs/agents/formats/claude-code/format-changelog.md b/docs/agents/formats/claude-code/format-changelog.md index 78e27ed3..a6e9fc7d 100644 --- a/docs/agents/formats/claude-code/format-changelog.md +++ b/docs/agents/formats/claude-code/format-changelog.md @@ -24,8 +24,8 @@ later than") unless the note says otherwise. ## Format-revision stamp This reference tracks Claude Code **2.1.x**. First-hand samples span -client versions 2.1.37, 2.1.90, 2.1.110, and 2.1.112. Reference -revision: **2026-04-23**. +client versions 2.1.37, 2.1.90, 2.1.110, and 2.1.112, plus a local +store spanning 2.1.215 – 2.1.245. Reference revision: **2026-08-25**. --- @@ -58,6 +58,11 @@ See [jsonl-envelope.md](jsonl-envelope.md) for field definitions. | `queue-operation` (typed-ahead message enqueue/dequeue)| 2.1.x+ (origin unclear) | | `progress` (streaming tool output) | 2.1.x+ (origin unclear) | | `last-prompt` | 2.1.x+ (origin unclear) | +| `ai-title`, `mode` | Observed across 2.1.215 – 2.1.245; origin unclear | +| `relocated`, `worktree-state` | Observed from 2.1.218 | +| `custom-title`, `agent-name`, `pr-link` | Observed from 2.1.220 | +| `frame-link` | Observed at 2.1.231 (1 sample) | +| `atis-latch` | Observed from 2.1.235 | | `compact_boundary` as top-level `type` | Newer variant; coexists with older `type: "system"` + `subtype: "compact_boundary"` | | `system.subtype` values: `turn_duration`, `stop_hook_summary`, `task_started`/`task_progress`/`task_notification` | 2.1.x+ (origin unclear) | diff --git a/docs/agents/formats/claude-code/jsonl-envelope.md b/docs/agents/formats/claude-code/jsonl-envelope.md index 0a049963..64d15862 100644 --- a/docs/agents/formats/claude-code/jsonl-envelope.md +++ b/docs/agents/formats/claude-code/jsonl-envelope.md @@ -28,7 +28,7 @@ Every envelope field we have observed, in rough order of prominence: | Field | Shape | Notes | |----------------|----------------------------|-------| | `type` | string | Discriminant. Values in [entry-types.md](entry-types.md). Present on **every** line. | -| `uuid` | UUIDv4 string | Per-entry ID. Empty string (`""`) or absent on some metadata entries (`permission-mode`, `queue-operation`, `last-prompt`, `file-history-snapshot`). | +| `uuid` | UUIDv4 string | Per-entry ID. Empty string (`""`) or absent on some metadata entries (`permission-mode`, `queue-operation`, `last-prompt`, `file-history-snapshot`, and the session metadata lines in [entry-types.md](entry-types.md)). | | `timestamp` | ISO-8601 string | e.g. `"2026-04-02T13:59:26.313Z"`. Millisecond precision. Absent on pure-metadata entries. | | `sessionId` | UUIDv4 string | Usually equals the filename stem. On continuation files, the first real entry carries the **previous** session's ID — see [session-chains.md](session-chains.md). | | `parentUuid` | UUIDv4 string \| `null` | Prior entry in the conversation DAG. `null` for the first entry of a session and for `compact_boundary` entries (which use `logicalParentUuid` instead). | @@ -77,6 +77,15 @@ Every envelope field we have observed, in rough order of prominence: | `operation` | string | `queue-operation` | `"enqueue"` / `"dequeue"`. | | `content` | string | `queue-operation` | Queued message text. **Conflicts in name with `message.content`** — distinguish by entry `type`. | | `lastPrompt` | string | `last-prompt` | Cached last user prompt. | +| `aiTitle` | string | `ai-title` | Generated session title. | +| `customTitle` | string | `custom-title` | Explicitly set session title. | +| `agentName` | string | `agent-name` | Agent name for the session. | +| `mode` | string | `mode` | Only `"normal"` observed. | +| `atis` | string | `atis-latch` | Opaque. Empty, 16 hex characters, or ~190 characters. | +| `prNumber` / `prUrl` / `prRepository` | number / URL / `owner/repo` | `pr-link` | Pull request linked to the session. | +| `frameUrl` / `path` / `title` | URL / absolute path / string | `frame-link` | Frame URL linked to a local file. | +| `relocatedCwd` | absolute path | `relocated` | New working directory for the session. | +| `worktreeSession` | object \| `null` | `worktree-state` | `{originalCwd, preEnterOriginalCwd, worktreePath, worktreeName, worktreeBranch, sessionId, …}`. See [entry-types.md](entry-types.md). | | `subtype` | string | `system`, `compact_boundary` | Discriminant within metadata entries. See [entry-types.md](entry-types.md). | | `durationMs` | number | `system` (turn_duration) | Milliseconds the assistant turn took. | | `messageCount` | number | `system` (turn_duration) | Number of messages in the turn. | diff --git a/docs/agents/formats/claude-code/writing-compatible-jsonl.md b/docs/agents/formats/claude-code/writing-compatible-jsonl.md index adb47ce8..e2b008ff 100644 --- a/docs/agents/formats/claude-code/writing-compatible-jsonl.md +++ b/docs/agents/formats/claude-code/writing-compatible-jsonl.md @@ -66,6 +66,12 @@ directory name under `projects/`. If you place a file in entries, Claude Code's behavior is inconsistent — the session may appear but tool invocations may fail. +Two metadata lines carry absolute paths under other keys: +`relocated.relocatedCwd`, and `originalCwd`, `preEnterOriginalCwd`, +and `worktreePath` inside `worktree-state.worktreeSession`. How the +loader uses them on resume is not known. A writer that moves a session +to another directory decides for each whether to rewrite it. + ## Strong conventions Real Claude Code entries always carry these envelope fields. The @@ -79,7 +85,7 @@ downstream tools may behave oddly without them: | `cwd` | Used for git/branch context and to validate project alignment. | | `version` | Claude Code client version string. | | `gitBranch` | Current branch; **empty string**, not null, when cwd isn't a repo. | -| `sessionId` | Must match the file's session UUID (or be a bridge entry — see below). | +| `sessionId` | Must match the file's session UUID (or be a bridge entry — see below). `worktree-state` lines carry a second copy in `worktreeSession.sessionId`; keep both equal. | | `uuid` | UUIDv4 per entry. | | `timestamp` | ISO-8601 with millisecond precision. | | `parentUuid` | The prior entry's `uuid` (or `null` for the first entry). |