CC

ClaudeCode Source Analysis

核心执行流

执行流

理解这套系统,关键不是“能调模型”,而是看清输入如何在 query、工具、权限、任务之间闭环

这页聚焦真正会影响维护和改动安全性的执行链。最重要的一个事实是:REPL 路径和 headless/SDK 路径并不完全相同,但它们共享同一个 query() 执行核心。

本页回答什么问题

  • REPL 路径与 QueryEngine 路径有什么差别?
  • 一次 turn 内,query.ts 的状态机做了哪些阶段性处理?
  • tool use 到底经过哪些层才真正执行?
  • 权限拒绝是在哪些环节被决定和反馈的?

端到端执行图

这张图把交互式路径串起来,重点强调 REPL 并不会先进入 QueryEngine,而是经由输入预处理后直接进入 query()

文件映射:src/screens/REPL.tsxsrc/utils/handlePromptSubmit.tssrc/query.tssrc/services/api/claude.tssrc/services/tools/*

关键观察:REPL 路径在真正执行前会刷新 tools/MCP,这是为了避免长生命周期组件持有过期能力集合。

流 1:REPL 提交路径

src/screens/REPL.tsx 通过提交处理逻辑收集当前消息、system prompt、上下文和 UI 状态,然后直接调用 query({...})。一个容易被忽视但很重要的细节是:REPL 会在提交前刷新 tools/MCP,避免长生命周期组件捕获过期能力集合。

  • 输入来源:终端输入框、slash command、交互式辅助逻辑。
  • 关键状态:当前 transcript、可见工具、MCP tools/resources、permission context。
  • 主要副作用:消息追加、UI loading、任务更新、通知与成本追踪。

流 2:headless / SDK 路径

src/QueryEngine.ts 更像会话外壳。它会先处理用户输入、写入 transcript、维护 usage 和 abort controller,然后再进入 query()。其中一个关键实现细节是:transcript 会在真正进入模型循环前先写盘,这意味着即使进程在首个 API 响应前崩溃,恢复也更容易成立。

结论:QueryEngine 不是主执行器,而是为非 REPL 路径提供恢复性、可重放性和会话语义的外壳。

流 3:query() 的 turn 状态机

src/query.ts 不是“发请求再收结果”的薄封装,而是一个包含多阶段治理的状态机。当前可见代码显示它至少涉及 memory prefetch、skill prefetch、query tracking、tool result budget、snip compact、microcompact、context collapse、autocompact、API 调用以及 tool loop 回写。

  • 输入:messages、system prompt、tools、MCP、permission context、budget、settings。
  • 变换:上下文压缩、提示重写、预算裁剪、工具结果拼接。
  • 输出:assistant 消息、tool requests、tool results、usage、compact 事件、状态更新。
  • 错误恢复:compact、fallback、终止与中断传播都在这里交汇。

关键观察:query.ts 的复杂度很大一部分来自长会话上下文治理,而不是模型 API 本身。

流 4:tool use 执行链

模型返回 tool_use 后,不是直接调用某个函数,而是进入工具编排系统:StreamingToolExecutor 负责流式场景下边收边执行,toolOrchestration.ts 负责并发安全分批,toolExecution.ts 负责 schema 校验、权限、hooks、telemetry、结果封装和回写。

  • 工具定义在 Tool.ts / tools.ts,执行真正落在 services/tools/*
  • 并发不是默认允许的;工具元数据会影响调度批次。
  • 执行结果不仅影响模型上下文,还会影响任务、UI、通知和日志。

流 5:权限审批链

权限判定并不只在 UI 层。src/hooks/useCanUseTool.tsx 显示它是一个异步判定管线,可能结合 permission rules、interactive handler、coordinator / swarm 路径、speculative classifier 和 sandbox 状态。真正的执行阶段还会再次把这些结论带入工具执行上下文。

  • 输入:工具类型、参数、用户模式、settings、permission rules、上下文状态。
  • 分支:自动允许、自动拒绝、需要 UI 审批、需要 classifier/hook。
  • 输出:decision reason、UI prompt、上下文更新、拒绝消息或继续执行。

文件映射:src/services/tools/toolOrchestration.tssrc/services/tools/toolExecution.tssrc/hooks/useCanUseTool.tsxsrc/utils/permissions/*src/components/permissions/*

关键观察:tool use 不是“模型直接调函数”,而是一条包含调度、权限、UI、任务与状态回写的执行协议。

流 6:agent 与任务链

src/tools/AgentTool/runAgent.ts 表明子 agent 的创建会触发技能预加载、agent 特定 MCP 配置、subagent context 构造、sidechain transcript 记录以及最终清理。这使 agent 更像“带资源边界的子运行时”,而不是单纯函数调用。

当你修改 agent 或 task 相关代码时,要同时问:transcript、MCP、hooks、任务列表和清理路径是否还闭合?

数据流图

这张图从数据对象角度解释一次 turn:消息、上下文、工具结果、持久化和 UI 状态是怎样同时流动的。

文件映射:src/utils/handlePromptSubmit.tssrc/query.tssrc/services/api/claude.tssrc/services/tools/*src/state/*src/QueryEngine.ts 中 transcript 相关逻辑。

关键观察:工具结果不仅回到模型,还回到任务、通知和 UI;这就是为什么工具调用的副作用面远大于普通函数返回值。

建议一起打开的文件

src/screens/REPL.tsx

看 REPL 路径如何直达 query(),以及为什么提交前要刷新 tools/MCP。

src/QueryEngine.ts

看会话级语义、transcript 预写和 headless 路径的职责。

src/query.ts

看 turn 级状态机如何处理 compact、budget、tool loop 和恢复。

src/services/tools/toolExecution.ts

看工具执行为什么会同时牵涉权限、日志、结果存储和进度消息。