文章目录
- 这是最主要的原因。 JSONL 是恢复会话的唯一数据源。 loadFullLog() 读取 JSONL 文件 buildConversationChain() 沿 parentUuid 链表重建完整对话树 REPL 原样恢复——所有消息、工具调用、结果都被还原 没有这个文件,每次 Ctrl+C 或关闭终端都会丢失整个对话。
- /context — 从 transcript 可视化 token 使用量 /export — 导出对话到文件 Transcript 搜索(Ctrl+R)— 索引和高亮匹配 统计追踪 — token 计数、turn 计数
- JSONL 存储不属于对话消息的元数据条目: mode — normal / plan 模式 permission-mode — default / accept-edits / plan / bypass ai-title — 自动生成的会话标题 custom-title — 用户通过 /rename 设置的名称 tag — 会话标签 last-prompt — 在 –resume 选择器中展示 pr-link — 关联的 GitHub PR
- 当 AgentTool 生成子 agent 时,子 agent 的对话写入独立的侧链 JSONL: <sessionId>/agents/<agentId>.jsonl isSidechain 标志区分主线程和 agent 侧链消息: // src/utils/sessionStorage.ts:1224-1228 const isAgentSidechain = entry.isSidechain && entry.agentId !== undefined const targetFile = isAgentSidechain ? getAgentTranscriptPath(asAgentId(entry.agentId!)) : sessionFile
- 当启用 ENABLE_SESSION_PERSISTENCE 时,每条条目也通过 persistToRemote() 发送到后端,实现跨机器会话连续性。
- 这些是 TranscriptMessage 的子类型——它们携带 parentUuid 形成链表,buildConversationChain() 依靠此链表在 –resume 时重建对话。
- 属性 值 触发行为 用户在 TUI 中输入 prompt 并回车 代码路径 handlePromptSubmit() → processUserInput() → createUserMessage() → recordTranscript() 包含字段 message.content(文本/图片块)、permissionMode、parentUuid、origin.kind(human/bridge/queued_command)、promptSource { “type”: “user”, “parentUuid”: “…”, “uuid”: “750ccbae-…”, “message”: { “role”: “user”, “content”: “explain the architecture…” }, “permissionMode”: “default”, “origin”: { “kind”: “human” }, “promptSource”: “typed”, “userType”: “external”, “entrypoint”: “cli”, “cwd”: “/Users/zhanghongkui/Documents/cc-haha-main”, “sessionId”: “964aa807-…”, “version”: “2.1.183”, “gitBranch”: “HEAD” } 本质:你的每一次提问。
- 属性 值 触发行为 LLM 返回的每一次响应(包括中间 tool_use 步骤和最终文本回复) 代码路径 query() 循环 → API streaming → setMessages() → useLogMessages() → recordTranscript() 包含字段 message.content(text、tool_use 块)、message.usage(token 消耗)、message.stop_reason、message.model { “type”: “assistant”, “parentUuid”: “750ccbae-…”, “uuid”: “b917be23-…”, “message”: { “role”: “assistant”, “content”: [ { “type”: “text”, “text”: “Let me explore the codebase…” }, { “type”: “tool_use”, “id”: “toolu_…”, “name”: “codegraph_explore”, “input”: {…} } ], “usage”: { “input_tokens”: 50000, “output_tokens”: 1200 }, “stop_reason”: “tool_use”, “model”: “deepseek-v4-pro” } } 本质:AI 的每一次回复。一个 turn 可能产生多条 assistant 条目(每轮 API 调用一条)。
- 属性 值 触发行为 系统注入的上下文信息,用于告知模型状态变化而非响应用户 代码路径 createSystemMessage() → recordTranscript() 包含字段 message.content 包含 <system-reminder> 标签 { “type”: “system”, “parentUuid”: “…”, “message”: { “role”: “user”, “content”: “<system-reminder>Content was truncated to fit context limit…</system-reminder>” } } 本质:系统注入的提醒信息——内容截断通知、权限变更、工具搜索结果持久化提示等。
- 属性 值 触发行为 Agent/Tool/MCP 列表有变化时,增量或全量通告给模型 代码路径 getDeferredToolsDeltaAttachment() / getAgentListingDeltaAttachment() / getMcpInstructionsDeltaAttachment() → createAttachmentMessage() 包含字段 attachment.type(agent_listing_delta / tool_listing_delta / mcp_instructions_delta)、attachment.addedTypes、attachment.addedLines、attachment.removedTypes、attachment.isInitial { “type”: “attachment”, “parentUuid”: “750ccbae-…”, “attachment”: { “type”: “agent_listing_delta”, “addedTypes”: [“claude”, “Explore”, “Plan”, “general-purpose”, …], “addedLines”: [“- claude: Catch-all for any task…”, …], “isInitial”: true, “showConcurrencyNote”: true } } 本质:向模型通告可用工具、Agent 类型、MCP 指令的增量变更。
- 这些条目没有 parentUuid——不参与对话链表。由 reAppendSessionMetadata() 反复追加到 JSONL 末尾,保证它们在文件尾部 16KB 窗口内,以便 –resume 快速读取。
- 属性 值 触发行为 Claude Code 自动基于首条用户消息生成会话标题 代码路径 AI 标题生成 → saveAiGeneratedTitle() → appendEntryToFile() 优先级 低于 custom-title(用户重命名会覆盖 AI 标题) { “type”: “ai-title”, “sessionId”: “964aa807-…”, “aiTitle”: “Understanding Claude Code Architecture” }
- 属性 值 触发行为 用户执行 /rename "新标题" 代码路径 /rename 命令 → saveCustomTitle() → appendEntryToFile() { “type”: “custom-title”, “sessionId”: “964aa807-…”, “customTitle”: “My Architecture Study” }
- 属性 值 触发行为 每次用户提交新 prompt 后写入,覆盖前一个值 代码路径 insertMessageChain() → this.currentSessionLastPrompt = … → reAppendSessionMetadata() → 写入 展示位置 claude –resume 选择器中展示(截断到 200 字符) { “type”: “last-prompt”, “sessionId”: “964aa807-…”, “lastPrompt”: “explain the architecture of this project” }
- 属性 值 触发行为 用户执行 /tag <标签名> 代码路径 /tag 命令 → saveTag() → appendEntryToFile()
- 属性 值 触发行为 为 Agent 设置自定义名称/颜色/配置 代码路径 /rename(agent 模式)或 Agent 设置变更
-
- 属性 值 触发行为 会话模式变更:normal ↔ coordinator 代码路径 模式切换 → reAppendSessionMetadata() { “type”: “mode”, “sessionId”: “964aa807-…”, “mode”: “normal” }
- 属性 值 触发行为 权限模式变更,每次写入一条新记录 模式值 default(每次需确认)、acceptEdits(自动接受编辑)、bypassPermissions(跳过所有权限)、plan(计划模式) { “type”: “permission-mode”, “sessionId”: “964aa807-…”, “permissionMode”: “default” } 注意:此类型随 user/assistant 消息序列化,因此一次对话中会出现多次。
- 属性 值 触发行为 每个 turn 结束时,对当前文件状态做快照 代码路径 executeUserInput() → fileHistoryMakeSnapshot() → insertFileHistorySnapshot() 用途 –resume 时恢复文件编辑历史;/context 展示文件变更 { “type”: “file-history-snapshot”, “messageId”: “750ccbae-…”, “snapshot”: { “messageId”: “…”, “trackedFileBackups”: {}, “timestamp”: “2026-06-19T04:21:13.170Z” }, “isSnapshotUpdate”: false }
- 属性 值 触发行为 大文件内容被替换为 stub(占位符)以节省 context window 代码路径 Content replacement 强制执行 → insertContentReplacement() 用途 Resume 时重放以保持 prompt cache 稳定性
- 属性 值 触发行为 追踪 Claude Code 对每个文件贡献的字符数 用途 Git commit 归属追踪(哪些字符是 Claude 写的)
-
- 属性 值 触发行为 手动 /compact 或自动 compact 时生成对话摘要 代码路径 compactConversation() → 生成 summary → appendEntry() 用途 存储 compact 时生成的摘要
- 属性 值 触发行为 后台定期 fork 主线程,生成当前任务的摘要 触发频率 每 5 步或每 2 分钟 用途 claude ps 展示当前 agent 正在做什么
- 属性 值 触发行为 命令队列有操作变更(入队/出队/取消) 代码路径 insertQueueOperation()
- 属性 值 触发行为 推测性 token 生成被接受(性能优化) 用途 追踪推测加速节省的时间
- 属性 值 触发行为 进入/退出 git worktree 时持久化状态 代码路径 EnterWorktree / ExitWorktree → saveWorktreeState() { “type”: “worktree-state”, “sessionId”: “…”, “worktreeSession”: { “originalCwd”: “…”, “worktreePath”: “…”, “worktreeName”: “…”, “worktreeBranch”: “…”, “sessionId”: “…” } }
- 属性 值 触发行为 将会话关联到 GitHub Pull Request 代码路径 PR 关联操作 → linkSessionToPR()
- 属性 值 触发行为 Context Collapse(上下文折叠)系统的持久化 代码路径 recordContextCollapseCommit() / recordContextCollapseSnapshot() 用途 恢复折叠的上下文段——commit 是追加的(回放所有),snapshot 是最后写入生效
- 每条 user、assistant、system、attachment 消息都携带 parentUuid,指向前一条链参与者消息: user (uuid: A, parentUuid: null) ↑ assistant (uuid: B, parentUuid: A) ↑ assistant (uuid: C, parentUuid: B) ← tool_use 响应 ↑ user (uuid: D, parentUuid: C) ← tool_result ↑ assistant (uuid: E, parentUuid: D) ← 继续 ↑ … –resume 时 buildConversationChain() 沿链表从叶子走到根,重建完整对话。
- appendEntry() 在写入前检查 UUID 是否已存在: // src/utils/sessionStorage.ts:1242-1243 const isNewUuid = !messageSet.has(entry.uuid) if (isAgentSidechain || isNewUuid) { void this.enqueueWrite(targetFile, entry) if (!isAgentSidechain) { messageSet.add(entry.uuid) } }
- 并非所有消息都参与链表。isChainParticipant() 过滤掉进度消息和临时事件,只有 user、assistant、attachment、system 以及 compact boundary 消息才更新 parentUuid 指针。
目录
- 1. JSONL 文件是什么
- 2. 写入流程:在 TUI 交互的哪一步发生
- 缓冲策略
- 3. JSONL 存在的五个目的
- 目的 1:会话恢复 (–resume/–continue)
- 目的 2:Transcript 展示与搜索
- 目的 3:会话元数据与选择器
- 目的 4:子 Agent 侧链持久化
- 目的 5:远程持久化与多设备同步
- 4. 所有 22 种 JSONL Entry 类型详解
- 5. 分类详解
- 第一类:对话消息(4 种子类型)
- user
- assistant
- system
- attachment
- 第二类:会话元数据(6 种类型)
- ai-title
- custom-title
- last-prompt
- tag
- agent-name/agent-color/agent-setting
- 第三类:工具与权限状态(5 种类型)
- mode
- permission-mode
- file-history-snapshot
- content-replacement
- attribution-snapshot
- 第四类:高级功能(6 种类型,大部分仅 ant 内网可见)
- summary
- task-summary
- queue-operation
- speculation-accept
- worktree-state
- pr-link
- marble-origami-commit/marble-origami-snapshot
- 6. 实际 Transcript 样本分析
- 7. 去重与链表机制
- parentUuid 链表
- UUID 去重
- isChainParticipant 过滤
- 8. 写入优先级总结
- 9. 关键源文件索引
基于 src/types/logs.ts、src/utils/sessionStorage.ts、src/hooks/useLogMessages.ts 源码分析。 实际 transcript 样本:~/.claude/projects/-Users-zhanghongkui-Documents-cc-haha-main/964aa807-*.jsonl
每次与 Claude Code agent 对话,都会在以下路径产生一个 JSONL 文件:
~/.claude/projects/<project-dir-hash>/<sessionId>.jsonl
- 文件名 = sessionId(UUID,如 964aa807-99e0-46c8-a293-a9aed7702390)
- 格式 = 每行一个 JSON 对象(JSON Lines)
- 写入时机 = 每次 REPL 渲染新消息后,通过 React useEffect 异步写入
用户输入 prompt 并回车
│
▼
handlePromptSubmit() src/utils/handlePromptSubmit.ts:120
│
▼
executeUserInput() src/utils/handlePromptSubmit.ts:396
│
▼
processUserInput() src/utils/processUserInput/processUserInput.ts:85
├─ 斜杠命令 → processSlashCommand()
├─ Bash 模式 → processBashCommand()
└─ 普通文本 → processTextPrompt() → createUserMessage()
│
▼
onQuery() REPL.tsx 中定义
│
▼
query() 生成器循环 src/query.ts:219
├─ API 调用(通过 copilot-api 代理)
├─ 流式接收 text / thinking / tool_use
├─ 执行工具 → 发回结果
└─ Turn 完成
│
▼
setMessages(updatedMessages) React state 更新
│
▼
React 重新渲染 REPL
│
▼
useLogMessages() useEffect 触发 src/hooks/useLogMessages.ts:19 ← ★ 关键触发点
│
▼
recordTranscript(newMessages) src/utils/sessionStorage.ts:1408
├─ 1. 去重:跳过已在 messageSet 中的 UUID
├─ 2. 清洗:cleanMessagesForLogging() 剥离仅 UI 的数据
│
▼
Project.insertMessageChain() src/utils/sessionStorage.ts:993
├─ 3. 首次 user/assistant 消息 → materializeSessionFile()
│ 创建 JSONL 文件,刷新缓冲的前导条目
├─ 4. 每条消息:加盖元数据戳(sessionId, cwd, version, gitBranch)
│
▼
Project.appendEntry() src/utils/sessionStorage.ts:1128
├─ 5. 去重:UUID 已存在则跳过
├─ 6. 远程持久化:persistToRemote()(如果启用)
│
▼
Project.enqueueWrite() src/utils/sessionStorage.ts:606
├─ 7. 推入内存写队列(最多缓冲 50 条或 1 秒后刷新)
│
▼
drainWriteQueue() → appendToFile() src/utils/sessionStorage.ts:645
│
▼
appendEntryToFile() src/utils/sessionStorage.ts:2572
└─ 8. fs.appendFileSync(path, jsonLine + 'n') ← 实际磁盘写入
enqueueWrite() ← 立即返回 Promise(不阻塞 UI)
│
▼
scheduleDrain() ← 调度微任务(合并多次快速写入为一次刷新)
│
▼
drainWriteQueue() ← 条目批量打包成块(MAX_CHUNK_BYTES)
│ 每个块 = 一次 fs.appendFileSync 调用
▼
appendToFile() ← 实际的同步磁盘 I/O(在微任务上运行,不阻塞渲染帧)
配置参数:
- 内存缓冲上限:50 条
- 刷新间隔:1 秒
- 条目先合并成大块再写入,减少 fs.appendFileSync 调用次数
这是最主要的原因。 JSONL 是恢复会话的唯一数据源。
- loadFullLog() 读取 JSONL 文件
- buildConversationChain() 沿 parentUuid 链表重建完整对话树
- REPL 原样恢复——所有消息、工具调用、结果都被还原
没有这个文件,每次 Ctrl+C 或关闭终端都会丢失整个对话。
- /context — 从 transcript 可视化 token 使用量
- /export — 导出对话到文件
- Transcript 搜索(Ctrl+R)— 索引和高亮匹配
- 统计追踪 — token 计数、turn 计数
JSONL 存储不属于对话消息的元数据条目:
- mode — normal / plan 模式
- permission-mode — default / accept-edits / plan / bypass
- ai-title — 自动生成的会话标题
- custom-title — 用户通过 /rename 设置的名称
- tag — 会话标签
- last-prompt — 在 –resume 选择器中展示
- pr-link — 关联的 GitHub PR
当 AgentTool 生成子 agent 时,子 agent 的对话写入独立的侧链 JSONL:
<sessionId>/agents/<agentId>.jsonl
isSidechain 标志区分主线程和 agent 侧链消息:
// src/utils/sessionStorage.ts:1224-1228 const isAgentSidechain = entry.isSidechain && entry.agentId !== undefined const targetFile = isAgentSidechain ? getAgentTranscriptPath(asAgentId(entry.agentId!)) : sessionFile
当启用 ENABLE_SESSION_PERSISTENCE 时,每条条目也通过 persistToRemote() 发送到后端,实现跨机器会话连续性。
完整的 Entry 联合类型(src/types/logs.ts:297):
export type Entry = | TranscriptMessage // 4 种子类型:user, assistant, attachment, system | SummaryMessage // type: 'summary' | CustomTitleMessage // type: 'custom-title' | AiTitleMessage // type: 'ai-title' | LastPromptMessage // type: 'last-prompt' | TaskSummaryMessage // type: 'task-summary' | TagMessage // type: 'tag' | AgentNameMessage // type: 'agent-name' | AgentColorMessage // type: 'agent-color' | AgentSettingMessage // type: 'agent-setting' | PRLinkMessage // type: 'pr-link' | FileHistorySnapshotMessage // type: 'file-history-snapshot' | AttributionSnapshotMessage // type: 'attribution-snapshot' | QueueOperationMessage // type: 'queue-operation' | SpeculationAcceptMessage // type: 'speculation-accept' | ModeEntry // type: 'mode' | WorktreeStateEntry // type: 'worktree-state' | ContentReplacementEntry // type: 'content-replacement' | ContextCollapseCommitEntry // type: 'marble-origami-commit' | ContextCollapseSnapshotEntry // type: 'marble-origami-snapshot'
外加一种在实际 transcript 中观察到但不在上述 union 中的类型:
- permission-mode — 权限模式变更通知
这些是 TranscriptMessage 的子类型——它们携带 parentUuid 形成链表,buildConversationChain() 依靠此链表在 –resume 时重建对话。
| 属性 | 值 |
|---|---|
| 触发行为 | 用户在 TUI 中输入 prompt 并回车 |
| 代码路径 | handlePromptSubmit() → processUserInput() → createUserMessage() → recordTranscript() |
| 包含字段 | message.content(文本/图片块)、permissionMode、parentUuid、origin.kind(human/bridge/queued_command)、promptSource |
{
"type": "user",
"parentUuid": "...",
"uuid": "750ccbae-...",
"message": {
"role": "user",
"content": "explain the architecture..."
},
"permissionMode": "default",
"origin": { "kind": "human" },
"promptSource": "typed",
"userType": "external",
"entrypoint": "cli",
"cwd": "/Users/zhanghongkui/Documents/cc-haha-main",
"sessionId": "964aa807-...",
"version": "2.1.183",
"gitBranch": "HEAD"
}
本质:你的每一次提问。
| 属性 | 值 |
|---|---|
| 触发行为 | LLM 返回的每一次响应(包括中间 tool_use 步骤和最终文本回复) |
| 代码路径 | query() 循环 → API streaming → setMessages() → useLogMessages() → recordTranscript() |
| 包含字段 | message.content(text、tool_use 块)、message.usage(token 消耗)、message.stop_reason、message.model |
{
"type": "assistant",
"parentUuid": "750ccbae-...",
"uuid": "b917be23-...",
"message": {
"role": "assistant",
"content": [
{ "type": "text", "text": "Let me explore the codebase..." },
{ "type": "tool_use", "id": "toolu_...", "name": "codegraph_explore", "input": {...} }
],
"usage": { "input_tokens": 50000, "output_tokens": 1200 },
"stop_reason": "tool_use",
"model": "deepseek-v4-pro"
}
}
本质:AI 的每一次回复。一个 turn 可能产生多条 assistant 条目(每轮 API 调用一条)。
| 属性 | 值 |
|---|---|
| 触发行为 | 系统注入的上下文信息,用于告知模型状态变化而非响应用户 |
| 代码路径 | createSystemMessage() → recordTranscript() |
| 包含字段 | message.content 包含 <system-reminder> 标签 |
{
"type": "system",
"parentUuid": "...",
"message": {
"role": "user",
"content": "<system-reminder>Content was truncated to fit context limit...</system-reminder>"
}
}
本质:系统注入的提醒信息——内容截断通知、权限变更、工具搜索结果持久化提示等。
| 属性 | 值 |
|---|---|
| 触发行为 | Agent/Tool/MCP 列表有变化时,增量或全量通告给模型 |
| 代码路径 | getDeferredToolsDeltaAttachment() / getAgentListingDeltaAttachment() / getMcpInstructionsDeltaAttachment() → createAttachmentMessage() |
| 包含字段 | attachment.type(agent_listing_delta / tool_listing_delta / mcp_instructions_delta)、attachment.addedTypes、attachment.addedLines、attachment.removedTypes、attachment.isInitial |
{
"type": "attachment",
"parentUuid": "750ccbae-...",
"attachment": {
"type": "agent_listing_delta",
"addedTypes": ["claude", "Explore", "Plan", "general-purpose", ...],
"addedLines": ["- claude: Catch-all for any task...", ...],
"isInitial": true,
"showConcurrencyNote": true
}
}
本质:向模型通告可用工具、Agent 类型、MCP 指令的增量变更。
这些条目没有 parentUuid——不参与对话链表。由 reAppendSessionMetadata() 反复追加到 JSONL 末尾,保证它们在文件尾部 16KB 窗口内,以便 –resume 快速读取。
| 属性 | 值 |
|---|---|
| 触发行为 | Claude Code 自动基于首条用户消息生成会话标题 |
| 代码路径 | AI 标题生成 → saveAiGeneratedTitle() → appendEntryToFile() |
| 优先级 | 低于 custom-title(用户重命名会覆盖 AI 标题) |
{
"type": "ai-title",
"sessionId": "964aa807-...",
"aiTitle": "Understanding Claude Code Architecture"
}
| 属性 | 值 |
|---|---|
| 触发行为 | 用户执行 /rename "新标题" |
| 代码路径 | /rename 命令 → saveCustomTitle() → appendEntryToFile() |
{
"type": "custom-title",
"sessionId": "964aa807-...",
"customTitle": "My Architecture Study"
}
| 属性 | 值 |
|---|---|
| 触发行为 | 每次用户提交新 prompt 后写入,覆盖前一个值 |
| 代码路径 | insertMessageChain() → this.currentSessionLastPrompt = … → reAppendSessionMetadata() → 写入 |
| 展示位置 | claude –resume 选择器中展示(截断到 200 字符) |
{
"type": "last-prompt",
"sessionId": "964aa807-...",
"lastPrompt": "explain the architecture of this project"
}
| 属性 | 值 |
|---|---|
| 触发行为 | 用户执行 /tag <标签名> |
| 代码路径 | /tag 命令 → saveTag() → appendEntryToFile() |
| 属性 | 值 |
|---|---|
| 触发行为 | 为 Agent 设置自定义名称/颜色/配置 |
| 代码路径 | /rename(agent 模式)或 Agent 设置变更 |
| 属性 | 值 |
|---|---|
| 触发行为 | 会话模式变更:normal ↔ coordinator |
| 代码路径 | 模式切换 → reAppendSessionMetadata() |
{
"type": "mode",
"sessionId": "964aa807-...",
"mode": "normal"
}
| 属性 | 值 |
|---|---|
| 触发行为 | 权限模式变更,每次写入一条新记录 |
| 模式值 | default(每次需确认)、acceptEdits(自动接受编辑)、bypassPermissions(跳过所有权限)、plan(计划模式) |
{
"type": "permission-mode",
"sessionId": "964aa807-...",
"permissionMode": "default"
}
注意:此类型随 user/assistant 消息序列化,因此一次对话中会出现多次。
| 属性 | 值 |
|---|---|
| 触发行为 | 每个 turn 结束时,对当前文件状态做快照 |
| 代码路径 | executeUserInput() → fileHistoryMakeSnapshot() → insertFileHistorySnapshot() |
| 用途 | –resume 时恢复文件编辑历史;/context 展示文件变更 |
{
"type": "file-history-snapshot",
"messageId": "750ccbae-...",
"snapshot": {
"messageId": "...",
"trackedFileBackups": {},
"timestamp": "2026-06-19T04:21:13.170Z"
},
"isSnapshotUpdate": false
}
| 属性 | 值 |
|---|---|
| 触发行为 | 大文件内容被替换为 stub(占位符)以节省 context window |
| 代码路径 | Content replacement 强制执行 → insertContentReplacement() |
| 用途 | Resume 时重放以保持 prompt cache 稳定性 |
| 属性 | 值 |
|---|---|
| 触发行为 | 追踪 Claude Code 对每个文件贡献的字符数 |
| 用途 | Git commit 归属追踪(哪些字符是 Claude 写的) |
| 属性 | 值 |
|---|---|
| 触发行为 | 手动 /compact 或自动 compact 时生成对话摘要 |
| 代码路径 | compactConversation() → 生成 summary → appendEntry() |
| 用途 | 存储 compact 时生成的摘要 |
| 属性 | 值 |
|---|---|
| 触发行为 | 后台定期 fork 主线程,生成当前任务的摘要 |
| 触发频率 | 每 5 步或每 2 分钟 |
| 用途 | claude ps 展示当前 agent 正在做什么 |
| 属性 | 值 |
|---|---|
| 触发行为 | 命令队列有操作变更(入队/出队/取消) |
| 代码路径 | insertQueueOperation() |
| 属性 | 值 |
|---|---|
| 触发行为 | 推测性 token 生成被接受(性能优化) |
| 用途 | 追踪推测加速节省的时间 |
| 属性 | 值 |
|---|---|
| 触发行为 | 进入/退出 git worktree 时持久化状态 |
| 代码路径 | EnterWorktree / ExitWorktree → saveWorktreeState() |
{
"type": "worktree-state",
"sessionId": "...",
"worktreeSession": {
"originalCwd": "...",
"worktreePath": "...",
"worktreeName": "...",
"worktreeBranch": "...",
"sessionId": "..."
}
}
| 属性 | 值 |
|---|---|
| 触发行为 | 将会话关联到 GitHub Pull Request |
| 代码路径 | PR 关联操作 → linkSessionToPR() |
| 属性 | 值 |
|---|---|
| 触发行为 | Context Collapse(上下文折叠)系统的持久化 |
| 代码路径 | recordContextCollapseCommit() / recordContextCollapseSnapshot() |
| 用途 | 恢复折叠的上下文段——commit 是追加的(回放所有),snapshot 是最后写入生效 |
你的 transcript 964aa807-…jsonl 的类型分布:
类型 出现次数 说明 ───────────────────────────────────────────────────── assistant 64 AI 的每次响应(含工具调用的中间步骤) user 35 你的每次输入 permission-mode 15 权限模式(随消息序列化写入) mode 15 会话模式(随元数据刷新重复写入) ai-title 15 AI 生成的标题(每次元数据刷新重写) last-prompt 14 最近一次 prompt(每次元数据刷新重写) system 7 系统注入提醒 file-history-snapshot 7 每个 turn 结束的文件快照 attachment 6 工具/Agent/MCP 列表通告
未出现在你 transcript 中的类型(需要特定操作触发):
- custom-title — 需要执行 /rename
- tag — 需要执行 /tag
- agent-name/color/setting — 需要 Agent 配置
- summary — 需要 compact
- task-summary — 需要 ant 内网
- queue-operation — 需要 ant 内网
- speculation-accept — 需要 ant 内网
- worktree-state — 需要 worktree 操作
- pr-link — 需要关联 PR
- attribution-snapshot — 需要 ant 内网
- content-replacement — 需要 ant 内网
- marble-origami-* — 需要 ant 内网
每条 user、assistant、system、attachment 消息都携带 parentUuid,指向前一条链参与者消息:
user (uuid: A, parentUuid: null) ↑ assistant (uuid: B, parentUuid: A) ↑ assistant (uuid: C, parentUuid: B) ← tool_use 响应 ↑ user (uuid: D, parentUuid: C) ← tool_result ↑ assistant (uuid: E, parentUuid: D) ← 继续 ↑ ...
–resume 时 buildConversationChain() 沿链表从叶子走到根,重建完整对话。
appendEntry() 在写入前检查 UUID 是否已存在:
// src/utils/sessionStorage.ts:1242-1243
const isNewUuid = !messageSet.has(entry.uuid)
if (isAgentSidechain || isNewUuid) {
void this.enqueueWrite(targetFile, entry)
if (!isAgentSidechain) {
messageSet.add(entry.uuid)
}
}
并非所有消息都参与链表。isChainParticipant() 过滤掉进度消息和临时事件,只有 user、assistant、attachment、system 以及 compact boundary 消息才更新 parentUuid 指针。
┌──────────────────────────────────────────────────────┐
│ 高频写入(每条消息都写) │
│ ├─ user / assistant / system / attachment │
│ ├─ permission-mode(随消息序列化) │
│ └─ file-history-snapshot(每个 turn 结束) │
│ │
│ 中频写入(每个 turn / compact 时写) │
│ ├─ last-prompt(覆盖写入) │
│ └─ mode(切换时写) │
│ │
│ 低频写入(用户主动操作时写) │
│ ├─ custom-title, tag(/rename, /tag) │
│ ├─ agent-name, agent-color, agent-setting │
│ ├─ summary(compact 时) │
│ ├─ pr-link(关联 PR 时) │
│ └─ worktree-state(进入/退出 worktree 时) │
│ │
│ 仅在 ant 内网(USER_TYPE === 'ant') │
│ ├─ task-summary(后台 fork 生成) │
│ ├─ queue-operation(命令队列变更) │
│ ├─ speculation-accept(推测加速追踪) │
│ ├─ content-replacement(内容替换持久化) │
│ ├─ attribution-snapshot(代码归属追踪) │
│ └─ marble-origami-commit / snapshot(上下文折叠) │
└──────────────────────────────────────────────────────┘
| 文件 | 内容 |
|---|---|
| src/types/logs.ts | 所有 Entry 类型定义(22 种) |
| src/utils/sessionStorage.ts | JSONL 写入核心逻辑:appendEntry()、insertMessageChain()、recordTranscript()、drainWriteQueue()、appendEntryToFile() |
| src/hooks/useLogMessages.ts | React hook —— 监听 messages[] 变化并触发 recordTranscript() |
| src/screens/REPL.tsx | REPL 组件 —— 调用 useLogMessages() |
| src/utils/handlePromptSubmit.ts | 用户输入处理 —— handlePromptSubmit() → executeUserInput() |
| src/utils/processUserInput/processUserInput.ts | 输入路由 —— text / slash / bash 命令 |
| src/query.ts | 核心 query 生成器循环 |
到此这篇关于Claude Code JSONL 的使用小结的文章就介绍到这了,更多相关Claude Code JSONL内容请搜索风君子博客以前的文章或继续浏览下面的相关文章,希望大家以后多多支持风君子博客!