Data | 序列图(精选时序)¶
配合:architecture-diagrams.md 看整体架构。 这里:聚焦"几个关键流程的时序"。
1. 完整 LLM 工具调用时序¶
sequenceDiagram
autonumber
actor U as User
participant PI as PromptInput
participant R as REPL
participant S as AppState
participant Q as query()
participant QE as QueryEngine
participant CTX as ToolUseContext
participant PERM as canUseTool
participant T as Tool (e.g. BashTool)
participant API as Anthropic API
U->>PI: 输入 "run npm test"
PI->>R: onSubmit(text)
R->>S: setState({ messages: [...prev, userMsg] })
S-->>R: 通知 listeners
R->>Q: for await (event of query(...))
Q->>API: POST messages.stream
API-->>Q: content_block_delta (text)
Q-->>R: yield StreamEvent
R->>S: 更新 assistantMessage
S-->>R: re-render
API-->>Q: content_block_start (tool_use)
Q->>QE: record tool use
API-->>Q: content_block_stop
API-->>Q: message_stop (finish_reason: tool_use)
Q->>Q: 准备执行工具
Q->>PERM: canUseTool(tool, input, ctx)
PERM->>CTX: checkPermission
CTX-->>PERM: 'ask'
PERM-->>Q: { behavior: 'ask' }
Q-->>R: yield { type: 'permission_request' }
R->>U: 弹 PermissionRequest
U->>R: 批准
R-->>Q: 继续
Q->>T: tool.call(input, ctx)
T->>T: validate(input)
T->>T: checkPermission
T-->>Q: yield { type: 'start' }
Q-->>R: yield { type: 'tool_use' }
R->>S: 追加 tool_use message
S-->>R: re-render
T->>T: spawn child_process
loop 进程运行
T-->>Q: yield { type: 'stdout', data }
Q-->>R: 透传
R->>S: 追加 progress message
end
T-->>Q: return ToolResult(content, exitCode)
Q->>S: 追加 tool_result message
Q->>Q: 循环继续
Q->>API: POST messages.stream (第二轮)
Note over Q,API: 现在 messages 包含<br/>user + assistant(tool_use) + tool_result
API-->>Q: content_block_delta
API-->>Q: message_stop (end_turn)
Q-->>R: 循环结束
2. 启动期 prefetch 优化时序¶
sequenceDiagram
autonumber
participant Bun as Bun Runtime
participant M as main.tsx
participant Profiler
participant MDM as startMdmRawRead
participant KC as startKeychainPrefetch
participant Init as init.ts
participant Repl as replLauncher
Bun->>M: 加载 main.tsx
Note over M: 头部 18 行:<br/>import + 立即调用
M->>Profiler: profileCheckpoint('main_tsx_entry')
M->>MDM: startMdmRawRead()
activate MDM
MDM-->>MDM: spawn plutil/reg query
Note right of MDM: 并行跑 ~135ms 导入时间
M->>KC: startKeychainPrefetch()
activate KC
KC-->>KC: 读 macOS keychain
Note right of KC: 并行跑 ~65ms
M->>M: import 其余 100+ 模块
Note over M: ~135ms 同步导入
M->>Init: init()
activate Init
Init->>MDM: 等 plutil 完成
deactivate MDM
Init->>KC: 等 keychain 完成
deactivate KC
Init->>Init: 加载 settings + MCP
Init-->>M: init() 完成
deactivate Init
M->>Repl: launchRepl()
Repl->>Repl: <REPL /> mount
Repl-->>Bun: 渲染第一帧
3. 异步权限检查时序¶
sequenceDiagram
autonumber
participant Q as query()
participant CTX as ToolUseContext
participant Rule as matchingRuleForInput
participant R as REPL
actor U as User
participant Setting as ~/.claude/settings.json
Q->>CTX: tool call
CTX->>Rule: matchingRuleForInput(toolName, input)
Rule->>Setting: read permissions
Setting-->>Rule: { allow: [...], deny: [...], ask: [...] }
Rule-->>CTX: PermissionRule
alt rule = allow
CTX-->>Q: continue
else rule = deny
CTX-->>Q: denial ToolResult
else rule = ask or no rule
CTX->>R: yield { type: 'permission_request' }
R->>U: 弹 PermissionRequest Dialog
alt 用户批准
U->>R: 按 Enter
R->>CTX: continue
else 用户拒绝
U->>R: 按 Esc
R->>CTX: denial
else 用户更新规则
U->>R: "always allow this"
R->>Setting: saveGlobalConfig({ allow: [...prev, newRule] })
R->>CTX: continue
end
end
4. 上下文压缩时序¶
sequenceDiagram
autonumber
participant Q as query()
participant Calc as calculateTokenWarningState
participant AC as autoCompact
participant Group as grouping
participant LLM as LLM (small)
participant S as AppState
participant CC as postCompactCleanup
Q->>Q: 累计 messages 的 token
Q->>Calc: calculateTokenWarningState(total, max)
Calc-->>Q: 'ok' / 'warning' / 'critical' / 'auto_compact'
alt state != 'auto_compact'
Q->>Q: 继续正常流程
else state = 'auto_compact'
Q->>AC: runAutoCompact(messages)
AC->>Group: groupMessagesForCompaction(messages)
Group->>Group: 按时间分组
Group->>Group: 评分(user-related, recent, tool-heavy)
Group-->>AC: [{ messages, priority, tokens }]
AC->>AC: 选低分组的 N 个
AC->>LLM: 摘要(用 haiku 小模型)
LLM-->>AC: summary
AC->>AC: buildPostCompactMessages(origMsgs, summary)
Note over AC: 加 compact_boundary 标记
AC-->>Q: newMessages
Q->>S: setState({ messages: newMessages })
S-->>Q: 通知 listeners
S->>CC: postCompactCleanup (onChange hook)
CC->>CC: 清 fileStateCache
CC->>CC: 清 toolResultCache
CC->>CC: 清理 orphan attachments
Q->>Q: 继续下一轮 LLM 调用
end
5. MCP 工具调用时序¶
sequenceDiagram
autonumber
actor U as User
participant R as REPL
participant T as MCPTool
participant Mgr as MCPConnectionManager
participant Conn as MCPConnection
participant T2 as Transport (stdio/HTTP/InProcess)
participant Server as External MCP Server
U->>R: "在 GitHub 创建 issue"
R->>T: mcp__github__create_issue
T->>T: 拆解 { server: 'github', tool: 'create_issue' }
T->>Mgr: callTool('github', 'create_issue', input)
Mgr->>Conn: get('github')
Conn-->>Mgr: MCPConnection
alt transport = InProcess
Conn->>T2: request('tools/call', { name, arguments })
T2->>Server: 直接调方法
Server-->>T2: ToolResult
else transport = stdio
Conn->>T2: 写 JSON-RPC 到 stdin
T2->>Server: spawn 进程
Server-->>T2: 写 JSON-RPC 到 stdout
else transport = HTTP+SSE
Conn->>T2: POST /tools/call
T2->>Server: HTTP request
Server-->>T2: SSE 响应
end
T2-->>Conn: ToolResult
Conn-->>Mgr: ToolResult
Mgr-->>T: ToolResult
T-->>R: yield ToolResult
6. 主题切换时序¶
sequenceDiagram
autonumber
actor U as User
participant R as REPL
participant TP as ThemeProvider
participant G as GlobalConfig
participant Files as ~/.claude/settings.json
U->>R: /config → 选主题
R->>TP: setPreviewTheme('light')
TP->>TP: ThemeContext.themeSetting = 'light'
TP-->>R: re-render with 'light'
R->>U: 显示新主题
alt 用户确认
U->>R: Enter 确认
R->>TP: savePreview()
TP->>G: saveGlobalConfig({ theme: 'light' })
G->>Files: 写文件
Files-->>G: ok
else 用户取消
U->>R: Esc 取消
R->>TP: cancelPreview()
TP->>TP: themeSetting = 原值
end
7. 错误重试时序¶
sequenceDiagram
autonumber
participant Q as query()
participant WR as withRetry
participant API as Anthropic API
participant Cat as categorizeRetryableAPIError
Q->>WR: withRetry(async () => streamApi(...))
WR->>API: POST messages.stream
API-->>WR: 429 Too Many Requests
WR->>Cat: categorizeRetryableAPIError(err)
Cat-->>WR: RetryableError(429, 'rate_limited')
alt 重试次数 < MAX
WR->>WR: sleep(backoff(attempt))
Note over WR: 指数退避<br/>attempt 1: 1s<br/>attempt 2: 2s<br/>attempt 3: 4s
WR->>API: POST messages.stream (重试)
else 重试次数 >= MAX
WR-->>Q: throw MaxRetriesExceeded
end
API-->>WR: 200 OK + stream
WR-->>Q: AsyncGenerator<StreamEvent>
8. BashTool 安全检查时序¶
sequenceDiagram
autonumber
actor U as User
participant R as REPL
participant T as BashTool
participant CS as commandSemantics
participant PV as pathValidation
participant BS as bashSecurity
participant DCW as destructiveCommandWarning
participant SUS as shouldUseSandbox
participant PR as PermissionRequest
participant OS as OS (sandbox-exec / bwrap)
U->>R: "rm -rf /tmp/old.log"
R->>T: call(input={ command: 'rm -rf /tmp/old.log' }, ctx)
T->>CS: classifyCommand
CS-->>T: { hasSideEffects: true, writesFiles: true }
T->>PV: validatePath('/tmp/old.log', ctx)
PV-->>T: ok
T->>BS: checkCommandSecurity
BS->>BS: 解析 AST
BS-->>T: { safe: false, reason: 'rm -rf' }
T->>DCW: getDestructiveWarning
DCW-->>T: { severity: 'medium' }
T->>SUS: shouldUseSandbox
SUS-->>T: true
T-->>R: yield { type: 'permission_request' }
R->>U: 弹 PermissionRequest (含警告)
U->>R: 批准
R-->>T: continue
T->>OS: spawn('rm -rf /tmp/old.log', { sandbox: profile })
activate OS
OS-->>T: stdout/stderr
T-->>R: yield { type: 'stdout', data }
R->>U: 实时显示
OS-->>T: exit code 0
deactivate OS
T-->>R: return ToolResult
9. 多 Agent 协调时序¶
sequenceDiagram
autonumber
actor U as User
participant L as Leader
participant Coord as coordinator
participant T1 as Teammate1
participant T2 as Teammate2
participant M as Mailbox
U->>L: "让 2 个 agent 并行做 X 和 Y"
L->>Coord: spawn(team=[T1, T2])
activate Coord
Coord->>T1: sendMessage(do X)
activate T1
Coord->>T2: sendMessage(do Y)
activate T2
par T1
T1->>M: post progress
and T2
T2->>M: post progress
end
L->>M: poll inbox
M-->>L: [progress1, progress2, ...]
alt T1 完成
T1-->>Coord: done
Coord->>M: notify
end
L->>M: poll inbox
M-->>L: [T1 done, T2 progress]
T2-->>Coord: done
deactivate T2
Coord->>M: notify
deactivate Coord
deactivate T1
L->>M: poll inbox
M-->>L: [T1 done, T2 done]
L->>U: 合并结果
10. REPL 状态订阅时序¶
sequenceDiagram
autonumber
participant C as Component
participant USES as useSyncExternalStore
participant Store as AppState store
participant OnChg as onChange hook
participant Disk as ~/.claude/
C->>USES: useAppState(selector)
USES->>Store: subscribe(listener)
USES->>Store: getState()
Store-->>USES: currentState
USES-->>C: selectedState
Note over C,Disk: 后续 setState
Store->>Store: setState(updater)
Store->>OnChg: onChange({ new, old })
OnChg->>Disk: saveGlobalConfig(new)
Store->>USES: notify listeners
USES->>Store: getState()
Store-->>USES: newState
USES-->>C: newSelectedState (if changed)
C->>C: re-render
关键洞察¶
1. 时序图暴露"系统骨架"¶
看时序图 = 看谁先谁后,比看代码快 10 倍。
2. 关键模式:"yield 透传 + 拦截"¶
所有跨层通信都是"上层 yield,下层 for-await 透传"。
3. 错误处理的统一性¶
不管什么错误,都先分类(categorizeRetryableAPIError 风格),再决定重试 / 跳过 / 抛。
4. 权限决策的"显式"¶
权限检查是显式函数调用(canUseTool),不是装饰器/注解。
便于测试和审计。