Source Map 进阶子页 Agent Runtime

终端 Agent Runtime
从重建源码看 Claude Code 到底重在哪

这页不重复讲 Source Map 事件本身,而是把重建出来的 TypeScript 树当成证据,回答一个更实在的问题:Claude Code 为什么已经不像普通 CLI,而更像一个终端 Agent Runtime

核心判断

如果只看表面,Claude Code 像是“终端里调模型 + 读写文件 + 跑命令”。把源码层真正重的部分摊开看,重心却明显在模型外面:启动路径、trust 边界、会话状态、query loop、工具协议、权限裁决、MCP 扩展、子代理隔离、上下文治理与终端渲染。这些拼起来,才构成一个能长期跑下去的本地 Agent 运行时。

普通 CLI 关注什么

解析命令、执行动作、打印结果。一次调用结束后,状态可以直接扔掉。

Agent Runtime 关注什么

维护长期会话、处理权限与恢复、管理工具与扩展、压缩上下文、在终端里持续交互。

这页的阅读角度

我们不抄新闻,不只讲“泄露了什么”,而是结合源码锚点理解这套运行时为什么能比较稳定地扛住本地 coding agent。

六层动态分层图

下面这块不是实时遥测,而是把运行时最该先抓住的六层做成一个可切换的讲解面。先看顺序,再回头点源码路径,会比直接淹进 1900+ 文件里稳得多。

Mermaid 总架构图

如果把这页压成一张图,主链就是:启动与 trust 把边界立起来,会话状态核 持有上下文,query loop 推进一轮轮对话,工具控制面 负责能力与权限,扩展隔离层 把 MCP / Skills / Plugins / Subagent 接进来,最后由 memory / compact / renderer 兜住长期运行体验。

Claude Code 终端 Agent Runtime 从启动边界到上下文治理的一条完整运行时脊柱

从源码里拉一条主脊柱

脊柱段 代表文件 这里解决什么
启动与可信目录 src/main.tsx · src/entrypoints/init.ts · src/components/interactiveHelpers.tsx 决定什么能先并行,什么必须等 trust 过后再继续。
会话状态核 src/services/AppStateStore.ts · src/query/QueryEngine.ts 把工具权限、MCP、remote、usage、notifications 全收进一个会话对象。
主循环 src/query/query.ts 把 prefetch、streaming、tool execution、compact、continuation 串成一个长期状态机。
控制面 src/tools/Tool.ts · src/commands/commands.ts · src/tools/toolOrchestration.ts 区分用户命令与模型能力,处理并发安全、只读/破坏性和权限裁决。
扩展与隔离 src/services/mcp/client.ts · src/skills/loadSkillsDir.ts · src/plugins/pluginLoader.ts · src/tools/AgentTool.tsx 把协议接入、提示词扩展、组件分发和子代理隔离分成不同平面。
上下文与终端体验 src/memory/memdir.ts · src/services/compact/compact.ts · src/screens/REPL.tsx · src/ink/renderer.ts 解决记忆索引、压缩、交互总线和终端渲染技术债。

Source Map rebuild 版关键片段

下面不是源码截图,而是直接从本站当前用于阅读与实验的 ccsource/CC/claude-code-rebuild 里摘出的短片段。每块都只保留最能证明判断的几行,方便你把“运行时结论”重新对回真实代码位置。

01

启动不是顺序脚本

src/main.tsx + src/entrypoints/init.ts

关键判断:启动阶段一开始就在做“哪些事可以先并行,哪些事必须等 trust 之后再继续”。

// src/main.tsx
// These side-effects must run before all other imports:
profileCheckpoint('main_tsx_entry')
startMdmRawRead()
startKeychainPrefetch()

// src/entrypoints/init.ts
// Apply only safe environment variables before trust dialog
// Full environment variables are applied after trust is established
applySafeConfigEnvironmentVariables()
02

QueryEngine 就是会话内核

src/QueryEngine.ts

关键判断:这里维护的已经不是页面 state,而是一个完整 conversation 的持续状态。

/**
 * One QueryEngine per conversation.
 * State (messages, file cache, usage, etc.) persists across turns.
 */
export class QueryEngine {
  private mutableMessages: Message[]
  private permissionDenials: SDKPermissionDenial[]
  private totalUsage: NonNullableUsage
  private loadedNestedMemoryPaths = new Set<string>()
}
03

主循环在隐藏并行

src/query.ts

关键判断:prefetch 被埋进模型流式与工具执行期间,目标不是“多写一步”,而是把等待隐藏到主链下面。

// Skill discovery prefetch — per-iteration ...
// Discovery runs while the model streams and tools execute
const pendingSkillPrefetch = skillPrefetch?.startSkillDiscoveryPrefetch(
  null,
  messages,
  toolUseContext,
)

yield { type: 'stream_request_start' }
04

工具先是协议,再是函数

src/Tool.ts

关键判断:并发安全、只读/破坏性、中断行为、UI 展示都被做进了 Tool 协议,默认值还刻意走 fail-closed。

isConcurrencySafe(input): boolean
isReadOnly(input): boolean
interruptBehavior?(): 'cancel' | 'block'
renderToolUseMessage(...)

// Defaults (fail-closed where it matters):
// - isConcurrencySafe → false
// - isReadOnly → false
05

MCP 是一等扩展平面

src/services/mcp/client.ts

关键判断:这里不是“发个 HTTP 请求”,而是在认真维护 SSE / HTTP / stdio / auth provider / OAuth token / timeout 的 transport 体系。

if (serverRef.type === 'sse') {
  const authProvider = new ClaudeAuthProvider(name, serverRef)
  const transportOptions = { authProvider, fetch: wrapFetchWithTimeout(...) }
  transport = new SSEClientTransport(new URL(serverRef.url), transportOptions)
} else if (serverRef.type === 'http') {
  const authProvider = new ClaudeAuthProvider(name, serverRef)
  const transportOptions = { authProvider, fetch: wrapFetchWithTimeout(...) }
  transport = new StreamableHTTPClientTransport(new URL(serverRef.url), transportOptions)
}
06

上下文治理是系统级能力

src/memdir/memdir.ts + src/interactiveHelpers.tsx

关键判断:`MEMORY.md` 被严格压成索引,trust dialog 也被定义成 workspace trust boundary,这两件事都说明系统很在意长期会话的边界感。

// src/memdir/memdir.ts
`${ENTRYPOINT_NAME}` is an index, not a memory
lines after ${MAX_ENTRYPOINT_LINES} will be truncated

// src/interactiveHelpers.tsx
// The trust dialog is the workspace trust boundary
// ... bypassPermissions mode only affects tool execution permissions

和普通 CLI 的分水岭

命令壳

一条输入对应一次动作,失败就报错,结束后状态基本散掉。

终端 Agent Runtime

会话必须跨 turn 维持,工具与权限有协议,失败要可恢复,MCP 与子代理要能接进来。

为什么这个差异重要

它解释了为什么很多 AI CLI 只能“调模型 + 调工具”,而 Claude Code 更像在系统化处理本地 agent 的长期运行问题。

最值得先读的源码锚点

这页和站内其他专题怎么配合

最稳的阅读顺序是:先看 Source Map 源码专题 建立入口,再读 源码反推思想 训练“从实现抽设计”的方法,最后进这页理解为什么这些点会汇成一个终端 Agent Runtime。若你想继续往产品图层走,可以接 Claude Code 解构;若想看跨 harness 样本,再接 Everything Claude Code 解构

回 Source Map 专题 回源码反推思想