基于 Claude Code v2.x 源码的完整技术分析文档。涵盖入口启动、代理循环、工具系统、权限安全、上下文管理、记忆系统、多代理架构、MCP 协议集成、遥测安全等九大模块。
昨天刚刚update完分析Claude code架构的文章,就爆出来Claude code源码泄露,趁这个热度,我们借助Claude 4.6 opus的力量,剖析一下技术架构
一、入口与启动流程
Claude Code 的启动链路从 src/entrypoints/cli.tsx 开始,经过多层快速路径分发、初始化、认证、配置加载,最终进入 REPL 交互循环或 headless 执行模式。整个设计的核心原则是延迟加载 ——只在需要时才加载模块,最小化冷启动时间。
启动入口:cli.tsx 的快速路径分发
src/entrypoints/cli.tsx 是整个应用的 bootstrap 入口。它的设计哲学是零导入快速路径 ——所有 import 都是动态的(await import(...)),确保不匹配的路径不会加载任何多余模块。
顶层副作用 (在 main() 之前执行):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 process.env .COREPACK_ENABLE_AUTO_PIN = '0' if (process.env .CLAUDE_CODE_REMOTE === 'true' ) { process.env .NODE_OPTIONS = `${existing} --max-old-space-size=8192` } if (feature ('ABLATION_BASELINE' ) && process.env .CLAUDE_CODE_ABLATION_BASELINE ) { for (const k of ['CLAUDE_CODE_SIMPLE' , 'CLAUDE_CODE_DISABLE_THINKING' , ...]) { process.env [k] ??= '1' } }
消融基线必须在 cli.tsx 而非 init.ts 中设置 ——因为 BashTool/AgentTool/PowerShellTool 在模块导入时就将 DISABLE_BACKGROUND_TASKS 捕获为模块级常量,init() 运行时已经太晚。
快速路径分发树
main() 函数按优先级检查命令行参数,匹配到的路径立即执行并返回,不加载完整 CLI:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 cli.tsx main() ├── --version / -v / -V → 打印版本号,零导入 ├── --dump-system-prompt → 输出渲染后的 system prompt(Ant-only) ├── --claude-in-chrome-mcp → Chrome 扩展 MCP 服务器 ├── --chrome-native-host → Chrome Native Messaging Host ├── --computer-use-mcp → Computer Use MCP 服务器(CHICAGO_MCP) ├── --daemon-worker=<kind> → Daemon Worker 进程(DAEMON) ├── remote-control / rc / bridge → Bridge 远程控制模式(BRIDGE_MODE) │ ├── OAuth 认证检查 │ ├── GrowthBook 门控检查 │ ├── 最低版本检查 │ └── 企业策略 allow_remote_control 检查 ├── daemon [subcommand] → Daemon 长驻进程(DAEMON) ├── ps / logs / attach / kill → 后台会话管理(BG_SESSIONS) │ └── --bg / --background ├── new / list / reply → 模板任务命令(TEMPLATES) ├── environment-runner → BYOC 环境运行器(BYOC_ENVIRONMENT_RUNNER) ├── self-hosted-runner → 自托管运行器(SELF_HOSTED_RUNNER) ├── --worktree --tmux → Tmux Worktree 快速路径 ├── --bare → 设置 CLAUDE_CODE_SIMPLE=1 └── [默认] → 加载完整 CLI(main.tsx) ├── startCapturingEarlyInput() // 捕获用户在加载期间的输入 └── import('../main.js').main()
设计要点 :
--version 零导入 :只使用编译时内联的 MACRO.VERSION,不加载任何模块。这是最快的路径。
Bridge 模式的认证前置 :OAuth 检查必须在 GrowthBook 门控之前——没有认证,GrowthBook 没有用户上下文,会返回过期的默认值 false。
--bare 的早期设置 :在加载完整 CLI 之前设置 CLAUDE_CODE_SIMPLE=1,确保模块求值期间的门控就能生效。
startCapturingEarlyInput() :在加载 main.tsx(~789KB)期间捕获用户的键盘输入,避免输入丢失。
init() 初始化函数
src/entrypoints/init.ts 中的 init() 是核心初始化函数,使用 memoize 确保只执行一次:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 init() 执行序列 │ ├── enableConfigs() // 验证并启用配置系统 ├── applySafeConfigEnvironmentVariables() // 应用安全的环境变量(trust 之前) ├── applyExtraCACertsFromConfig() // 加载自定义 CA 证书(必须在首次 TLS 握手前) ├── setupGracefulShutdown() // 注册优雅退出处理 │ ├── [异步] initialize1PEventLogging() // 初始化第一方事件日志 │ └── onGrowthBookRefresh() → reinitialize1PEventLoggingIfConfigChanged() ├── [异步] populateOAuthAccountInfoIfNeeded() // 填充 OAuth 账户信息 ├── [异步] initJetBrainsDetection() // JetBrains IDE 检测 ├── [异步] detectCurrentRepository() // GitHub 仓库检测 │ ├── initializeRemoteManagedSettingsLoadingPromise() // 远程托管设置 ├── initializePolicyLimitsLoadingPromise() // 策略限制 ├── recordFirstStartTime() // 记录首次启动时间 │ ├── configureGlobalMTLS() // mTLS 配置 ├── configureGlobalAgents() // HTTP 代理配置 ├── preconnectAnthropicApi() // 预连接 Anthropic API(TCP+TLS 握手重叠) │ ├── [CCR] initUpstreamProxy() // 上游代理(容器环境) ├── setShellIfWindows() // Windows git-bash 设置 ├── registerCleanup(shutdownLspServerManager) // LSP 清理注册 ├── registerCleanup(cleanupSessionTeams) // Swarm 团队清理注册 └── ensureScratchpadDir() // 创建 Scratchpad 临时目录
关键设计细节 :
安全环境变量分层 :applySafeConfigEnvironmentVariables() 只应用不需要 trust 的变量(如 NODE_EXTRA_CA_CERTS)。完整的环境变量(applyConfigEnvironmentVariables())在 trust dialog 之后才应用——防止不受信任的项目配置在用户同意前生效。
CA 证书的时序约束 :applyExtraCACertsFromConfig() 必须在 configureGlobalAgents() 之前执行。Bun 通过 BoringSSL 在启动时缓存 TLS 证书存储,如果在首次 TLS 握手之后才加载自定义 CA,连接会失败。
API 预连接 :preconnectAnthropicApi() 在 CA 证书和代理配置完成后执行,将 TCP+TLS 握手(~100-200ms)与后续的 action handler 工作(~100ms)重叠。对于 proxy/mTLS/unix/cloud-provider 场景跳过——SDK 的 dispatcher 不会复用全局连接池。
1P 事件日志的延迟初始化 :通过 Promise.all([import('firstPartyEventLogger'), import('growthbook')]) 延迟加载 OpenTelemetry sdk-logs 模块。由于 growthbook.js 此时已在模块缓存中(被 firstPartyEventLogger 导入过),第二次动态导入零成本。
main.tsx 的完整启动序列
src/main.tsx(789KB,最大文件)包含完整的 CLI 启动逻辑。以下是从 main() 到 REPL 渲染的关键阶段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 main.tsx main() 启动序列 │ ├── 1. 早期解析 │ ├── eagerLoadSettings() // 解析 --settings、--setting-sources │ ├── initializeEntrypoint() // 设置 CLAUDE_CODE_ENTRYPOINT │ └── eagerParseCliFlag() // 提取关键 CLI 标志 │ ├── 2. Commander 参数解析 │ ├── program.option('--model') │ ├── program.option('--permission-mode') │ ├── program.option('--mcp-config') │ ├── program.option('--agents') │ └── ... (~40 个选项) │ ├── 3. init() 调用 │ └── 见上文 init() 序列 │ ├── 4. 并行预取(用户还在输入时) │ ├── initUser() // 用户信息 │ ├── getUserContext() // 用户上下文 │ ├── prefetchSystemContextIfSafe() // 系统上下文 │ ├── getRelevantTips() // 提示信息 │ ├── countFilesRoundedRg() // 文件计数(ripgrep) │ ├── initializeAnalyticsGates() // 分析门控 │ ├── prefetchOfficialMcpUrls() // MCP URL 预取 │ ├── refreshModelCapabilities() // 模型能力刷新 │ ├── settingsChangeDetector.initialize() // 设置变更检测 │ └── skillChangeDetector.initialize() // 技能变更检测 │ ├── 5. MCP 配置加载(与 setup 并行) │ ├── getClaudeCodeMcpConfigs() // 本地 MCP 配置 │ ├── fetchClaudeAIMcpConfigsIfEligible() // claude.ai MCP 配置 │ └── filterMcpServersByPolicy() // 企业策略过滤 │ ├── 6. Setup 流程 │ ├── showSetupScreens() // 首次运行引导 │ │ ├── Trust Dialog // 工作区信任确认 │ │ ├── OAuth / API Key 认证 // 认证流程 │ │ └── Onboarding // 新用户引导 │ ├── initializeTelemetryAfterTrust() // Trust 后初始化遥测 │ └── applyConfigEnvironmentVariables() // 应用完整环境变量 │ ├── 7. 后 Trust 初始化 │ ├── loadRemoteManagedSettings() // 加载远程托管设置 │ ├── initializeLspServerManager() // LSP 服务器管理器 │ ├── launchInvalidSettingsDialog() // 设置验证错误对话框 │ └── 后台预取(quota、passes、fastMode、bootstrap) │ ├── 8. 模型与命令加载 │ ├── getCommands() // 加载 ~80 个斜杠命令 │ ├── getAgentDefinitionsWithOverrides() // 加载代理定义 │ └── 模型解析(--model → alias resolution) │ ├── 9. MCP 连接 │ ├── prefetchAllMcpResources() // 预取 MCP 资源 │ └── 连接所有配置的 MCP 服务器 │ └── 10. 渲染 ├── createRoot() // 创建 Ink 渲染根 └── root.render(<REPL />) // 渲染 REPL 组件 或 runHeadless() // headless 模式执行
入口点类型识别
initializeEntrypoint() 根据运行环境设置 CLAUDE_CODE_ENTRYPOINT:
入口点
条件
说明
'mcp'
claude mcp serve
MCP 服务器模式
'claude-code-github-action'
CLAUDE_CODE_ACTION=true
GitHub Action
'local-agent'
预设的环境变量
本地代理模式
'sdk-cli'
非交互式(-p 参数)
SDK/headless 模式
'cli'
交互式(默认)
终端 REPL 模式
四种运行模式
REPL 模式 :完整的 React/Ink TUI,包括输入框、消息列表、虚拟滚动、权限对话框、状态栏等。通过 root.render(<REPL />) 启动。
Headless 模式 (-p):QueryEngine(src/QueryEngine.ts)管理查询生命周期。不渲染 UI,直接输出结果。支持 --output-format json 结构化输出。
SDK 模式 :通过 structuredIO(src/cli/structuredIO.ts)与宿主进程通信。VS Code 扩展、Claude Desktop 等通过此模式集成。
MCP Server 模式 :src/entrypoints/mcp.ts 将 Claude Code 自身作为 MCP 服务器暴露,允许其他 MCP 客户端调用其工具。
配置加载层级
配置文件按优先级从低到高加载:
层级
路径
说明
1. 默认值
硬编码
内置默认配置
2. 全局
~/.claude/settings.json
用户全局设置
3. 本地全局
~/.claude/settings.local.json
用户本地设置(gitignored)
4. 企业
/etc/claude-code/settings.json
企业管理员设置
5. 项目
.claude/settings.json
项目级设置(checked in)
6. 项目本地
.claude/settings.local.json
项目本地设置(gitignored)
7. CLI 参数
--settings <path>
命令行指定的设置文件
8. 远程托管
API 获取
远程托管设置(每小时轮询)
--setting-sources 参数可以控制加载哪些来源——企业环境中可以只加载 managed 来源,忽略用户和项目设置。
认证流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 认证检查 ├── OAuth Token 存在? │ ├── 是 → 检查 token 有效性 │ │ ├── 有效 → 使用 OAuth │ │ └── 过期 → 刷新 token │ │ ├── 刷新成功 → 使用新 token │ │ └── 刷新失败 → 重新授权 │ └── 否 → 检查 API Key │ ├── ANTHROPIC_API_KEY 存在 → 使用 API Key │ ├── Bedrock 配置 → AWS 认证 │ ├── Vertex 配置 → GCP 认证 │ └── 无认证 → 引导用户登录 │ ├── /login 命令 │ └── OAuth 浏览器授权流程
OAuth 优先于 API Key :OAuth token 支持 user:profile scope,可以获取用户信息用于 GrowthBook 定向和遥测。API Key 用户缺少这些能力。
--bare 模式的性能优化
--bare(或 CLAUDE_CODE_SIMPLE=1)是性能优化模式,跳过所有非必要的启动工作:
跳过的内容
节省
后台预取(quota、passes、fastMode)
~200ms 网络
claude.ai MCP 代理服务器
6-14s/服务器
自动发现的 MCP(.mcp.json、用户设置)
~100ms I/O
Skill 目录遍历
~50ms I/O
设置/技能变更检测器
~30ms
Auto Memory
后台 LLM 调用
Hooks
子进程开销
LSP 服务器
进程开销
Plugin 同步
网络 I/O
实测到达首次 API 请求的时间减少约 14% 。适用于脚本化的 -p 调用——这些调用没有"用户正在输入"的窗口来隐藏预取工作。
启动性能追踪
profileCheckpoint() 在关键节点记录时间戳,用于启动性能分析:
1 2 3 4 5 6 7 8 9 10 cli_entry → CLI 入口 init_function_start → init() 开始 init_configs_enabled → 配置系统就绪 init_safe_env_vars_applied → 安全环境变量已应用 init_after_graceful_shutdown → 优雅退出已注册 init_after_1p_event_logging → 1P 日志已初始化 init_network_configured → 网络(mTLS + proxy)已配置 init_function_end → init() 完成 action_commands_loaded → 命令和代理已加载 cli_after_main_complete → main() 完成
tengu_timer 事件在 Ink 根创建后立即记录启动时间——在任何阻塞对话框(trust/OAuth/onboarding/resume-picker)之前。旧的实现在 REPL 首次渲染时记录,p99 达到 ~70s(被对话框等待时间主导)。
事件循环阻塞检测
启动后注册一个事件循环阻塞检测器——当主线程被阻塞超过 500ms 时记录日志。这用于诊断启动卡顿和运行时性能问题。
1 2 void initializeEventLoopStallDetector ()
二、代理循环(Agent Loop)核心
Claude Code 的心脏是 src/query.ts 中的 query() 异步生成器函数。它实现了一个工具增强的 ReAct 循环 :用户输入 → 组装 system prompt → 调用 Claude API → 流式解析响应 → 如果包含 tool_use 则执行工具并将结果追加到消息列表 → 循环回 API 调用,直到模型返回纯文本。
整个循环被封装为一个 AsyncGenerator,通过 yield 向调用者(TUI 渲染层)逐条推送事件流。这个设计使得 UI 可以在流式传输过程中实时渲染模型输出和工具执行进度,而不需要等待完整响应。
循环状态机
query() 函数的核心是一个 while (true) 无限循环,每次迭代代表一次完整的 API 请求-响应-工具执行周期。循环通过一个 State 类型管理跨迭代的可变状态:
1 2 3 4 5 6 7 8 9 10 11 12 type State = { messages : Message [] toolUseContext : ToolUseContext autoCompactTracking : AutoCompactTrackingState maxOutputTokensRecoveryCount : number hasAttemptedReactiveCompact : boolean maxOutputTokensOverride : number | undefined pendingToolUseSummary : Promise <...> stopHookActive : boolean | undefined turnCount : number transition : Continue | undefined }
设计哲学 :State 在每次迭代开头被解构为局部变量(只读),在 continue 站点通过 state = { ... } 整体替换(而非 9 个独立赋值)。这保证了状态转换的原子性——不会出现半更新的中间状态。
循环的终止条件由 Terminal 类型表示,包含 reason 字段标识退出原因:
Terminal Reason
触发条件
'completed'
模型返回纯文本,无 tool_use
'aborted_streaming'
用户在流式传输中按 Ctrl+C
'aborted_tools'
用户在工具执行中按 Ctrl+C
'model_error'
API 调用抛出异常(非 413/max_output_tokens)
'prompt_too_long'
413 错误且所有恢复策略均失败
'image_error'
图片大小/格式错误
'max_turns'
达到 maxTurns 限制
'hook_stopped'
PreToolUse Hook 返回 preventContinuation
'stop_hook_prevented'
Stop Hook 返回 preventContinuation
'blocking_limit'
上下文硬上限阻塞
每次迭代的完整流程
迭代流程较长,拆分为三个阶段展示:预处理 → 流式调用 → 后处理 。
阶段一:消息预处理与 API 调用
阶段二:流式解析与错误恢复
阶段三:Hook 检查与循环决策
循环入口的双层封装
query() 实际上是一个薄包装器,真正的循环逻辑在 queryLoop() 中:
1 2 3 4 5 6 7 8 9 export async function * query (params : QueryParams ): AsyncGenerator <...> { const consumedCommandUuids : string [] = [] const terminal = yield * queryLoop (params, consumedCommandUuids) for (const uuid of consumedCommandUuids) { notifyCommandLifecycle (uuid, 'completed' ) } return terminal }
这个双层设计的目的是命令生命周期追踪 :consumedCommandUuids 收集本轮消费的排队命令(slash commands、task notifications),只有循环正常完成时才标记为 'completed'。如果循环因错误或中断退出,命令保持未完成状态,下次可以重试。
queryLoop 的初始化
循环开始前,queryLoop() 执行以下初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 const { systemPrompt, userContext, systemContext, canUseTool, fallbackModel, querySource, maxTurns, skipCacheWrite } = params let state : State = { messages : params.messages , toolUseContext : params.toolUseContext , maxOutputTokensOverride : params.maxOutputTokensOverride , } const budgetTracker = feature ('TOKEN_BUDGET' ) ? createBudgetTracker () : null let taskBudgetRemaining : number | undefined = undefined const config = buildQueryConfig ()using pendingMemoryPrefetch = startRelevantMemoryPrefetch ( state.messages , state.toolUseContext )
using 关键字 (TC39 Explicit Resource Management 提案)确保 pendingMemoryPrefetch 在生成器退出时(无论正常、异常还是 .return())都会被 dispose,触发遥测记录和资源释放。
System Prompt 的动态组装
src/constants/prompts.ts 中的 getSystemPrompt() 函数负责组装完整的系统提示。它返回一个 string[],每个元素是一个独立的 prompt section。组装分为静态部分 和动态部分 ,中间由 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 标记分隔——这个边界是 Prompt Cache 优化的关键:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 getSystemPrompt() 组装顺序 │ ├── 🔒 静态内容(scope: global,跨用户可缓存) │ ├── ① getSimpleIntroSection() — 身份声明 + CYBER_RISK_INSTRUCTION │ ├── ② getSimpleSystemSection() — 系统行为规则(工具调用规范、XML 标签使用等) │ ├── ③ getSimpleDoingTasksSection() — 编码任务指导(文件编辑策略、测试要求等) │ ├── ④ getActionsSection() — 操作安全指导(可逆性评估、爆炸半径控制) │ ├── ⑤ getUsingYourToolsSection() — 工具使用指导(并行调用、搜索策略等) │ ├── ⑥ getSimpleToneAndStyleSection() — 语气风格(简洁、专业、不道歉) │ └── ⑦ getOutputEfficiencySection() — 输出效率(避免冗余、倒金字塔结构) │ ├── ═══ SYSTEM_PROMPT_DYNAMIC_BOUNDARY ═══ ← Prompt Cache 分界线 │ └── 🔄 动态内容(scope: session,每次可能变化) ├── ⑧ getSessionSpecificGuidanceSection() — 会话特定指导(恢复会话时的上下文提示) ├── ⑨ loadMemoryPrompt() — CLAUDE.md 四级加载 + memdir 结构化记忆 ├── ⑩ getAntModelOverrideSection() — Ant 用户模型覆盖(内部员工专用 prompt) ├── ⑪ computeSimpleEnvInfo() — 环境信息(CWD / OS / Git / 模型名 / 知识截止日期) ├── ⑫ getLanguageSection() — 用户语言偏好 │ + getOutputStyleSection() — 输出风格(Markdown / 代码块偏好) ├── ⑬ getMcpInstructionsSection() — MCP 服务器指令(服务器级 + 工具级) └── ⑭ getScratchpadInstructions() — Scratchpad 临时目录指引 + getFunctionResultClearingSection() — 工具结果自动清理提示 + SUMMARIZE_TOOL_RESULTS — 模型侧结果摘要指令 + getProactiveSection() — KAIROS 自主代理行为指令(feature-gated)
关键设计细节 :
CYBER_RISK_INSTRUCTION 被硬编码在 getSimpleIntroSection 的最前面,由 Safeguards 团队维护,定义了安全边界——允许授权安全测试、CTF 挑战,拒绝破坏性技术、DoS 攻击、供应链攻击。这是整个 system prompt 中唯一不可被任何配置覆盖的部分。
Ant 用户差异化 :process.env.USER_TYPE === 'ant' 分支为 Anthropic 内部员工提供了更详细的 prompt。例如代码风格部分增加了"默认不写注释"、"完成前验证"等指令;输出效率部分替换为更详细的"用户沟通"指南(流畅散文、倒金字塔结构、避免语义回溯)。这个环境变量是 Bun 的 --define 编译时常量——在外部构建中被替换为 false,相关分支被 DCE 消除。
模型代号体系 :FRONTIER_MODEL_NAME = 'Claude Opus 4.6',模型 ID 映射为 { opus: 'claude-opus-4-6', sonnet: 'claude-sonnet-4-6', haiku: 'claude-haiku-4-5-20251001' }。源码中有 @[MODEL LAUNCH] 注释标记需要在新模型发布时更新的位置。getKnowledgeCutoff() 为每个模型返回不同的知识截止日期(Opus 4.6: May 2025, Sonnet 4.6: August 2025)。
Undercover 模式 :当 isUndercover() 返回 true 时,computeSimpleEnvInfo() 中所有模型名称/ID 引用被抑制,getAntModelOverrideSection() 返回 null。Undercover 模式在非内部仓库中自动激活,防止 Anthropic 员工在公开仓库中泄露内部模型代号。DCE 注释强调 process.env.USER_TYPE === 'ant' 必须在每个调用点内联(不能提升为 const),否则 bundler 无法常量折叠。
Scratchpad 目录 :getScratchpadInstructions() 为每个会话提供一个隔离的临时目录,替代 /tmp。这避免了多会话间的文件冲突,且不需要权限提示。
Function Result Clearing :getFunctionResultClearingSection() 告知模型旧的工具结果会被自动清理(Cached Micro Compact),最近 N 个结果始终保留。这引导模型在响应中记录重要信息,而非依赖工具结果的持久存在。
KAIROS 自主代理模式 :当 feature('KAIROS') 或 feature('PROACTIVE') 激活时,getProactiveSection() 注入完整的自主代理行为指令——包括 tick 处理、Sleep 节奏控制、首次唤醒行为、偏向行动原则。这是一个完全不同的 system prompt 范式。
消息预处理管线
在 API 调用之前,消息经过一条完整的预处理管线。每一层都是独立的、可组合的变换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 消息预处理管线(按执行顺序) 📋 原始消息 messages[] │ ├─ 1. applyToolResultBudget() 裁剪大工具结果 → 持久化到磁盘,替换为预览 + 文件路径引用 │ 豁免:FileReadTool(Infinity)、最近一轮结果 │ ├─ 2. snipCompact() 从最旧消息开始替换为 "[message snipped]" [feature: HISTORY_SNIP] │ 保留最近 4 轮对话,< 1000 tokens 的消息不裁剪 │ ├─ 3. microcompact() 合并冗余工具结果:FILE_UNCHANGED_STUB、空结果移除、相似结果合并 │ Cached MC 按 tool_use_id 操作,对内容替换透明 │ ├─ 4. contextCollapse() 折叠旧的工具调用+结果对为摘要占位符 [feature: CONTEXT_COLLAPSE] │ 保留最近 N 轮、错误结果、文件修改结果不折叠 │ ├─ 5. autoCompact() token 使用率 > 80% 时触发 LLM 摘要压缩,目标降至 50% │ 熔断器:压缩后至少 3 轮才能再次压缩 │ ├─ 6. prependUserContext() 注入用户上下文(打开的文件、git 状态、选中的代码等) │ ├─ 7. normalizeMessages() 合并连续同角色消息、移除空消息、注入 cache_control 标记 │ └─▶ 📡 Claude API 设计原理:轻量级操作在前(裁剪、合并),重量级操作在后(LLM 摘要)。 如果前面的操作已将 token 数降到阈值以下,后面的操作就不会触发。
applyToolResultBudget 的详细机制:
当工具结果总大小超过预算时,按优先级裁剪最旧的大结果,将其替换为磁盘文件引用。关键细节:
maxResultSizeChars 为 Infinity 的工具(如 Read)被豁免,因为持久化 Read 结果会创建循环引用(Read→file→Read)
替换记录通过 contentReplacementState 持久化,支持会话恢复时重放
只有 agent:* 和 repl_main_thread 来源的查询持久化替换记录,临时的 runForkedAgent 调用不持久化
运行在 microcompact 之前——Cached MC 按 tool_use_id 操作(不检查内容),所以内容替换对它透明
管线执行顺序的设计原理 :轻量级操作在前(裁剪、合并),重量级操作在后(LLM 摘要)。如果前面的操作已经将 token 数降到阈值以下,后面的操作就不会触发。Context Collapse 在 autoCompact 之前运行——如果 Collapse 已经释放了足够空间,就不需要昂贵的 LLM 摘要。
流式响应解析与工具执行
API 调用通过 queryModelWithStreaming() 发起,返回一个异步迭代器。循环逐条处理流式消息,同时进行工具执行:
backfillObservableInput 的精妙设计:
在 yield 给调用者之前,tool_use block 的 input 会被 backfillObservableInput() 处理——添加派生字段(如文件工具展开 file_path)。但这个操作在克隆的 message 上进行,原始 message 不被修改。原因是原始 message 会被 push 到 assistantMessages 并在下一次迭代中发送给 API——如果修改了原始 message,字节会不匹配,破坏 prompt cache。
withheld 错误机制 :
可恢复的错误(prompt-too-long、max_output_tokens、media-size-error)在流式传输中被"扣留"(withheld),不立即 yield 给调用者。它们仍然被 push 到 assistantMessages,以便后续的恢复检查可以找到它们。只有当所有恢复策略都失败时,错误才浮出。这个设计避免了 UI 闪烁——用户不会看到一个错误消息然后立即消失。
StreamingToolExecutor(src/services/tools/StreamingToolExecutor.ts)是并行工具执行的核心。它实现了一个带并发控制的执行队列:
并发控制规则 :
1 2 3 4 5 6 7 private canExecuteTool (isConcurrencySafe : boolean ): boolean { const executingTools = this .tools .filter (t => t.status === 'executing' ) return ( executingTools.length === 0 || (isConcurrencySafe && executingTools.every (t => t.isConcurrencySafe )) ) }
并发安全工具 (isConcurrencySafe=true,如 Read、Glob、Grep)可以与其他并发安全工具并行执行
非并发安全工具 (如 Write、Edit、Bash)必须独占执行——队列中遇到非并发安全工具时停止调度
isConcurrencySafe 的判断需要先 safeParse 输入——如果 parse 失败,保守地假设不安全
Bash 错误级联取消 :
1 2 3 4 5 if (tool.block .name === BASH_TOOL_NAME ) { this .hasErrored = true this .erroredToolDescription = this .getToolDescription (tool) this .siblingAbortController .abort ('sibling_error' ) }
Bash 工具错误会通过 siblingAbortController 级联取消所有兄弟工具。原因是 Bash 命令常有隐式依赖链(如 mkdir 失败 → 后续命令无意义)。但 Read/WebFetch 等独立工具的错误不会级联——一个文件读取失败不应该取消其他文件的读取。
中断行为分层 :
1 2 3 4 5 6 7 8 9 10 11 12 private getAbortReason (tool): 'sibling_error' | 'user_interrupted' | 'streaming_fallback' | null { if (this .discarded ) return 'streaming_fallback' if (this .hasErrored ) return 'sibling_error' if (this .toolUseContext .abortController .signal .aborted ) { if (signal.reason === 'interrupt' ) { return this .getToolInterruptBehavior (tool) === 'cancel' ? 'user_interrupted' : null } return 'user_interrupted' } return null }
interruptBehavior 为 'cancel' 的工具(如 Read)在用户中断时被取消,'block' 的工具(如 Write)继续运行直到完成。这防止了文件写入被中途打断导致数据损坏。
Progress 消息的即时传递 :
工具执行过程中的 progress 类型消息(如 Bash 命令的实时输出)被存储在 pendingProgress 数组中,通过 progressAvailableResolve 信号唤醒 getRemainingResults() 的等待循环。这保证了用户可以实时看到长时间运行的命令输出,而不需要等待命令完成。
结果顺序保证 :
getCompletedResults() 按工具接收顺序遍历——如果一个非并发安全工具还在执行,即使后面的并发安全工具已完成,也不会 yield 后面的结果。这保证了消息顺序与模型的 tool_use 顺序一致。
错误恢复机制
循环内置了多层错误恢复,形成一个优先级递减的恢复链:
prompt-too-long 恢复的三级策略 :
Context Collapse drain (最优先):释放已暂存的上下文折叠。如果上一次迭代的 transition.reason 已经是 'collapse_drain_retry'(说明 drain 后重试仍然 413),则跳过直接进入下一级。
Reactive Compact (次优先):完整的 LLM 压缩摘要。hasAttemptedReactiveCompact 标志防止无限重试。
错误浮出 :两者都失败时,yield 扣留的错误消息,执行 StopFailure Hooks,返回 Terminal: prompt_too_long。
关键防护 :恢复失败时不执行 Stop Hooks——模型从未产生有效响应,Hooks 没有有意义的内容可评估。运行 Stop Hooks 会创建死亡螺旋:error → hook blocking → retry → error → …(Hook 每次注入更多 token)。
max_output_tokens 的两阶段恢复 :
升级重试 (tengu_otk_slot_v1 特性标志):如果使用了默认的 8K 上限且被截断,直接以 64K(ESCALATED_MAX_TOKENS)重试同一请求——无 meta 消息、无多轮对话。这是一次性的(由 maxOutputTokensOverride === undefined 守卫)。
多轮恢复 :如果升级后仍被截断,注入 "Output token limit hit. Resume directly—no apology, no recap. Pick up mid-thought if that is where the cut happened. Break remaining work into smaller pieces." 消息,最多 3 次。
模型降级的完整清理 :
降级时需要清理所有与旧模型相关的状态:
清空 assistantMessages、toolResults、toolUseBlocks
丢弃旧的 StreamingToolExecutor,创建新的
为孤立的 assistant messages yield tombstone 事件(UI 移除这些消息)
Ant 用户额外执行 stripSignatureBlocks()——thinking 签名是模型绑定的,将 Capybara 的签名发送给 Opus 会导致 400 错误
Token Budget 与 Task Budget
Claude Code 支持两种不同层面的 token 预算机制:
维度
Token Budget
Task Budget
层面
客户端级别
API 级别
触发方式
用户指定(如 "+500k")
output_config.task_budget
实现
createBudgetTracker()
task-budgets-2026-03-13 beta
检查时机
每次迭代结束
API 请求参数
跨压缩
重置(新的追踪周期)
taskBudgetRemaining 递减
自动继续
注入 nudge 消息
服务端控制
Token Budget 的 diminishing returns 检测 :当连续多次继续但输出 token 增长率下降时,提前停止——避免模型陷入重复输出的死循环。
Task Budget 的跨压缩追踪 :taskBudgetRemaining 在循环外声明(不在 State 上),避免触碰 7 个 continue 站点。未压缩时为 undefined(服务端可以看到完整历史自行计算),压缩后递减(服务端只看到摘要,无法计算已消耗量)。
轮次间的附件注入
每次工具执行完成后、进入下一次迭代前,循环注入多种附件消息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 const queuedCommandsSnapshot = getCommandsByMaxPriority (sleepRan ? 'later' : 'next' ) .filter (cmd => { if (isSlashCommand (cmd)) return false if (isMainThread) return cmd.agentId === undefined return cmd.mode === 'task-notification' && cmd.agentId === currentAgentId }) for await (const attachment of getAttachmentMessages (...)) { yield attachment } if (pendingMemoryPrefetch?.settledAt !== null && consumedOnIteration === -1 ) { const memoryAttachments = filterDuplicateMemoryAttachments ( await pendingMemoryPrefetch.promise , toolUseContext.readFileState ) } if (pendingSkillPrefetch) { const skillAttachments = await skillPrefetch.collectSkillDiscoveryPrefetch (pendingSkillPrefetch) } if (updatedToolUseContext.options .refreshTools ) { const refreshedTools = updatedToolUseContext.options .refreshTools () }
命令队列的代理隔离 :队列是进程全局单例,被协调器和所有进程内子代理共享。每个循环只消费属于自己的命令——主线程消费 agentId === undefined 的命令,子代理消费 agentId === currentAgentId 的命令。用户 prompt(mode: 'prompt')始终只发给主线程。
Sleep 工具的队列刷新 :当 SleepTool 在本轮执行过(sleepRan = true),命令队列的优先级阈值从 'next' 提升到 'later'——这允许 KAIROS 自主代理在 Sleep 后消费低优先级的任务通知。
Feature Gate 系统
源码大量使用 feature('FLAG_NAME') 进行编译时门控。这是 Bun 的 bun:bundle 提供的编译时常量折叠机制——在 npm 发布的外部构建中,所有 feature() 调用被替换为 false,相关代码被死代码消除(DCE)。这意味着 108 个内部模块在外部构建中完全不存在:
1 2 3 4 5 6 7 8 const reactiveCompact = feature ('REACTIVE_COMPACT' ) ? require ('./services/compact/reactiveCompact.js' ) : null const contextCollapse = feature ('CONTEXT_COLLAPSE' ) ? require ('./services/contextCollapse/index.js' ) : null
DCE 约束 :feature() 调用必须出现在 if 或三元表达式的条件位置——不能赋值给变量后再判断,否则 Bun 的 tree-shaking 无法识别。同样,process.env.USER_TYPE === 'ant' 必须在每个调用点内联,不能提升为 const。
已识别的 Feature Flags 按功能分类:
类别
Flags
上下文管理
REACTIVE_COMPACT, CONTEXT_COLLAPSE, HISTORY_SNIP, CACHED_MICROCOMPACT, TOKEN_BUDGET
多代理
FORK_SUBAGENT, COORDINATOR_MODE, BG_SESSIONS, VERIFICATION_AGENT
自主代理 (KAIROS)
KAIROS, KAIROS_BRIEF, KAIROS_CHANNELS, KAIROS_PUSH_NOTIFICATION, KAIROS_GITHUB_WEBHOOKS, PROACTIVE
工具
MONITOR_TOOL, WEB_BROWSER_TOOL, OVERFLOW_TEST_TOOL, BASH_CLASSIFIER, TRANSCRIPT_CLASSIFIER
扩展
EXPERIMENTAL_SKILL_SEARCH, MCP_SKILLS, WORKFLOW_SCRIPTS, AGENT_TRIGGERS, AGENT_TRIGGERS_REMOTE, TEMPLATES
基础设施
BRIDGE_MODE, CHICAGO_MCP, TERMINAL_PANEL, UDS_INBOX, PROMPT_CACHE_BREAK_DETECTION, TEAMMEM
Claude Code 内置 40+ 工具,通过统一的 Tool 接口和 buildTool() 工厂函数构建。工具系统的设计核心是fail-closed 安全默认值 和并发感知的执行模型 。
所有工具都通过 src/Tool.ts 中的 Tool 接口定义,并通过 buildTool() 工厂函数构建。Tool 接口定义了 40+ 个方法和属性,核心包括:
方法/属性
类型
说明
name
string
工具名称,API 中的 tool name
aliases
string[]
向后兼容别名(旧名称仍可匹配权限规则)
inputSchema
z.ZodType
Zod schema,定义输入参数
call()
async generator
工具执行逻辑,yield 中间结果和最终结果
description()
async
动态生成工具描述(给用户看,显示在 UI 中)
prompt()
async
生成 AI-facing prompt(给模型看,注入 system prompt)
checkPermissions()
async
工具级权限检查(返回 allow/deny/ask + updatedInput)
validateInput()
async
输入验证(在权限检查之前执行)
isConcurrencySafe()
(input) => boolean
是否可并行执行(接收 parsed input)
isReadOnly()
boolean
是否只读(影响权限默认值)
isDestructive()
boolean
是否不可逆(影响 UI 警告级别)
interruptBehavior()
() => 'cancel'|'block'
用户中断时的行为
maxResultSizeChars
number
结果超此大小则持久化到磁盘(Infinity 表示豁免)
shouldDefer
boolean
是否延迟加载(ToolSearch 模式下不在初始 schema 中)
alwaysLoad
boolean
是否始终加载(不受 ToolSearch 影响)
searchHint
string
ToolSearch 关键词匹配提示(3-10 词)
strict
boolean
严格模式(API 层面更严格遵守 schema)
backfillObservableInput()
(input) => void
回填可观察输入(不修改原始输入,保护 prompt cache)
preparePermissionMatcher()
async
准备权限匹配器(如 Bash(git *) 模式匹配)
toAutoClassifierInput()
(input) => string
生成分类器输入(Auto Mode 安全判断)
isSearchOrReadCommand()
(input) => {...}
判断是否为搜索/读取操作(UI 折叠显示)
getActivityDescription()
(input) => string
生成活动描述(Spinner 显示)
renderToolUseMessage()
React 组件
渲染工具调用消息
renderToolResultMessage()
React 组件
渲染工具结果消息
renderGroupedToolUse()
React 组件
渲染并行工具组
buildTool() 提供安全的默认值(fail-closed 原则):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 const TOOL_DEFAULTS = { isEnabled : () => true , isConcurrencySafe : (_input? ) => false , isReadOnly : (_input? ) => false , isDestructive : (_input? ) => false , checkPermissions : (input, _ctx? ) => Promise .resolve ({ behavior : 'allow' , updatedInput : input }), toAutoClassifierInput : (_input? ) => '' , userFacingName : (_input? ) => '' , } export function buildTool<D extends AnyToolDef >(def : D): BuiltTool <D> { return { ...TOOL_DEFAULTS , userFacingName : () => def.name , ...def, } as BuiltTool <D> }
类型级别的精确性 :BuiltTool<D> 是一个条件映射类型——对于每个 defaultable key,如果 D 提供了该方法(required),使用 D 的类型;如果 D 省略了(inherited from Partial<>),使用默认值的类型。这保证了 60+ 个工具的零类型错误。
工具生命周期
每次工具调用经过以下生命周期:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 工具调用生命周期 │ ├── 1. inputSchema.safeParse(input) // Zod schema 验证 │ └── 失败 → 返回错误给模型 │ ├── 2. validateInput(input, context) // 工具级输入验证 │ └── 失败 → 返回验证错误给模型 │ ├── 3. PreToolUse Hooks // 用户定义的前置 Hook │ ├── Command Hook(shell 命令) │ ├── Prompt Hook(LLM 判断) │ └── HTTP Hook(远程端点) │ └── 结果:allow / deny / ask / updatedInput │ ├── 4. hasPermissionsToUseTool() // 规则匹配引擎 │ ├── deny 规则 → 直接拒绝 │ ├── ask 规则 → 进入交互式确认 │ ├── allow 规则 → 检查 Auto Mode 分类器 │ └── 无匹配 → checkPermissions() │ ├── 5. checkPermissions(input, context) // 工具级权限检查 │ └── Bash: 命令解析 + 路径验证 + 沙箱检查 │ └── File: 路径权限 + 工作目录验证 │ ├── 6. [交互式确认] // 如果需要用户确认 │ ├── Terminal UI / Bridge UI / Channel UI │ └── Allow Once / Allow Always / Deny │ ├── 7. call(input, context, ...) // 工具执行 │ ├── yield progress 消息 // 实时进度 │ └── return ToolResult<Output> // 最终结果 │ ├── 8. PostToolUse Hooks // 后置 Hook │ └── 可注入 systemMessage │ └── 9. mapToolResultToToolResultBlockParam() // 格式化为 API 响应
工具注册与过滤
src/tools.ts 中的 getAllBaseTools() 是所有工具的注册中心。工具列表的组装考虑了多个条件:
Prompt Cache 稳定性 是工具排序的核心考量——内置工具必须作为连续前缀,MCP 工具排在后面。如果混合排序,MCP 工具的增减会导致内置工具的位置变化,破坏所有下游的 cache key。uniqBy('name') 保证内置工具在名称冲突时优先——如果 MCP 服务器提供了与内置工具同名的工具,内置版本胜出。
完整工具清单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 FILE OPERATIONS SEARCH & DISCOVERY EXECUTION ═════════════════ ══════════════════════ ══════════ FileReadTool GlobTool BashTool FileEditTool GrepTool PowerShellTool FileWriteTool ToolSearchTool NotebookEditTool INTERACTION ═══════════ WEB & NETWORK AGENT / TASK AskUserQuestionTool ════════════════ ══════════════════ BriefTool WebFetchTool AgentTool WebSearchTool SendMessageTool PLANNING & WORKFLOW TeamCreateTool ════════════════════ MCP PROTOCOL TeamDeleteTool EnterPlanModeTool ══════════════ TaskCreateTool ExitPlanModeTool MCPTool TaskGetTool EnterWorktreeTool ListMcpResourcesTool TaskUpdateTool ExitWorktreeTool ReadMcpResourceTool TaskListTool TodoWriteTool TaskStopTool TaskOutputTool SYSTEM ════════ SKILLS & EXTENSIONS ConfigTool ═════════════════════ SkillTool SkillTool ScheduleCronTool LSPTool SleepTool TungstenTool
BashTool(src/tools/BashTool/BashTool.tsx,157.88KB)是最复杂的工具,包含完整的命令安全分析系统。
命令执行流程 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 BashTool.call(input) │ ├── 1. 命令解析 │ ├── parseCommand() → AST │ ├── 提取命令名、参数、管道、重定向 │ └── 识别 shell 特殊语法(&&, ||, ;, |) │ ├── 2. 安全分析(bashSecurity.ts, 102.69KB) │ ├── classifyCommand() → 命令语义分类 │ │ ├── read-only(ls, cat, grep, find, git log) │ │ ├── write(mkdir, cp, mv, touch) │ │ ├── destructive(rm, git push --force) │ │ └── network(curl, wget, ssh) │ ├── validatePaths() → 路径沙箱验证 │ │ ├── 检查是否在工作目录内 │ │ ├── 检查 additionalWorkingDirectories │ │ └── 拒绝 /etc, /usr, ~ 等敏感路径 │ ├── validateSedCommand() → sed 命令解析 │ │ ├── 提取 sed 表达式 │ │ ├── 验证目标文件路径 │ │ └── 检查是否为 in-place 编辑(-i) │ └── checkDestructivePatterns() → 破坏性模式检测 │ ├── rm -rf / │ ├── git push --force │ ├── chmod 777 │ └── > /dev/sda │ ├── 3. 沙箱执行 │ ├── macOS: seatbelt sandbox-exec │ │ └── 限制文件系统、网络、进程访问 │ ├── Linux: 容器隔离 │ └── Windows: 无沙箱(PowerShellTool 替代) │ ├── 4. 进程管理 │ ├── spawn 子进程 │ ├── 实时 stdout/stderr 流式传输(yield progress) │ ├── 超时控制(默认 120s,可配置) │ └── 信号处理(SIGTERM → SIGKILL) │ └── 5. 结果处理 ├── exit code 检查 ├── 输出截断(超过 maxResultSizeChars) └── 错误级联(通知 StreamingToolExecutor)
Bash 错误级联 :Bash 工具错误会通过 siblingAbortController 级联取消所有兄弟工具。原因是 Bash 命令常有隐式依赖链(如 mkdir 失败 → 后续命令无意义)。但 Read/WebFetch 等独立工具的错误不会级联。
FileEditTool(src/tools/FileEditTool/FileEditTool.ts)使用 string-replace 编辑策略而非 diff/patch:
1 2 3 4 5 6 7 { file_path : string , old_string : string , new_string : string , replace_all?: boolean , }
设计理由 :
精确匹配 :old_string 必须精确匹配文件中的文本(包括空白),避免 diff 的模糊匹配导致的错误编辑
原子性 :如果 old_string 不匹配或匹配多处(且 replace_all=false),操作失败——不会产生部分编辑
Prompt Cache 友好 :模型只需要生成变更的部分,不需要重复整个文件内容
可审计 :用户可以清楚看到"什么被替换为什么"
当工具数量过多时(特别是 MCP 工具),所有工具的 schema 会占用大量上下文窗口。ToolSearch 机制解决这个问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ToolSearch 工作流程 │ ├── 启动时 │ ├── 标记 shouldDefer=true 的工具为延迟加载 │ ├── 标记 alwaysLoad=true 的工具为始终加载 │ ├── 计算所有 MCP 工具描述的总大小 │ └── 如果总大小 > 上下文窗口的 10% → 自动启用 ToolSearch │ ├── 初始 API 请求 │ ├── 只包含非延迟工具的完整 schema │ ├── 延迟工具以 defer_loading: true 发送(只有名称和 searchHint) │ └── ToolSearchTool 本身始终加载 │ ├── 模型需要延迟工具时 │ ├── 调用 ToolSearchTool(query="...") │ ├── 关键词匹配 searchHint + 工具名称 │ └── 返回匹配工具的完整 schema │ └── 后续请求 └── 已搜索到的工具 schema 持久化在上下文中
MCP 工具的 alwaysLoad :MCP 工具可以通过 _meta['anthropic/alwaysLoad'] 标记为始终加载——用于模型在第一轮就必须看到的关键工具,无需 ToolSearch 往返。
REPL 模式
当 isReplModeEnabled() 为 true 时(Ant-only),REPL_ONLY_TOOLS(包括 Bash、Read、Edit、Glob、Grep 等原始工具)被隐藏,所有操作通过 REPLTool 的 VM 上下文执行。这是一种更高级的工具封装模式——模型不再直接调用文件系统工具,而是在一个持久化的 REPL 环境中执行代码。
工具结果持久化
当工具结果超过 maxResultSizeChars 时,结果被持久化到磁盘,模型收到一个预览 + 文件路径引用:
1 2 3 4 5 6 MCPTool : 100_000 SkillTool : 100_000 LSPTool : 100_000 GrepTool : 30_000 FileReadTool : Infinity
FileReadTool 的 maxResultSizeChars = Infinity 是关键设计——如果 Read 结果被持久化到文件,模型需要再次 Read 该文件来获取内容,形成 Read→file→Read 的循环引用。Read 工具通过自身的 fileReadingLimits(maxTokens、maxSizeBytes)自行限制输出大小。
工具 Prompt 的动态生成
每个工具的 prompt() 方法生成 AI-facing 的使用指南,注入到 system prompt 中。Prompt 内容是动态的——根据当前环境、权限模式、可用工具等条件变化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 async prompt ({ getToolPermissionContext, tools } ) { const ctx = await getToolPermissionContext () let prompt = BASE_BASH_PROMPT if (ctx.mode === 'bypassPermissions' ) { prompt += YOLO_MODE_ADDENDUM } if (isSandboxingEnabled ()) { prompt += SANDBOX_ADDENDUM } if (hasGrepTool (tools)) { prompt += "对于搜索,优先使用 GrepTool 而非 bash grep" } return prompt }
中断行为分层
'block' 行为防止文件写入被中途打断导致数据损坏。当用户按 Escape 时,'cancel' 工具被取消,'block' 工具继续运行。当用户提交新消息时(软中断),只有 interruptBehavior === 'cancel' 的工具被取消。
四、权限与安全系统
Claude Code 实现了一个多层纵深防御的权限模型,从 Hook 前置检查到规则匹配到交互式确认到工具级沙箱,确保每次工具调用都经过严格的安全审查。
四层权限检查流程
src/hooks/useCanUseTool.tsx 中的 useCanUseTool Hook 是权限检查的入口。完整的权限检查流程:
规则匹配引擎
src/utils/permissions/permissions.ts 中的 hasPermissionsToUseTool() 实现了规则匹配。规则来源按优先级排列(高 → 低):
1 2 3 4 5 6 7 8 9 10 规则优先级(高 → 低) │ ├── managed-settings // 企业管理员设置(最高优先级) ├── user // 用户全局设置(~/.claude/settings.json) ├── userLocal // 用户本地设置(~/.claude/settings.local.json) ├── project // 项目设置(.claude/settings.json) ├── projectLocal // 项目本地设置(.claude/settings.local.json) ├── cliArg // CLI 参数(--allowedTools, --disallowedTools) ├── command // 斜杠命令设置 └── session // 会话内用户决策(Allow Always / Deny)
规则值支持多种模式 :
模式
示例
说明
精确匹配
Bash、Read、Write
匹配工具名称
带内容匹配
Bash(git:*)
匹配工具名称 + 命令前缀
通配符
Bash(npm *)
通过 preparePermissionMatcher() 预编译
MCP 工具
mcp__github__create_issue
匹配特定 MCP 工具
MCP 服务器前缀
mcp__github
匹配该服务器所有工具
deny 规则的绝对优先级 :deny 规则具有最高优先级,任何其他规则都无法覆盖。managed 来源的 ask 规则不能被用户的 allow 规则覆盖——这保证了企业管理员可以强制要求某些工具必须经过审批。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 async function hasPermissionsToUseToolInner (tool, input, context ) { const denyRule = getDenyRuleForTool (appState.toolPermissionContext , tool) if (denyRule) return { behavior : 'deny' , ... } const askRule = getAskRuleForTool (appState.toolPermissionContext , tool) if (askRule) { const canSandboxAutoAllow = tool.name === BASH_TOOL_NAME && SandboxManager .isSandboxingEnabled () && SandboxManager .isAutoAllowBashIfSandboxedEnabled () && shouldUseSandbox (input) if (!canSandboxAutoAllow) return { behavior : 'ask' , ... } } const toolPermissionResult = await tool.checkPermissions (parsedInput, context) const allowRule = getAllowRuleForTool (appState.toolPermissionContext , tool, input) if (allowRule) return { behavior : 'allow' , ... } if (toolPermissionResult.behavior === 'allow' ) return { behavior : 'allow' , ... } return { behavior : 'ask' , ... } }
Auto Mode 分类器
当权限模式为 auto 时,classifyYoloAction() 使用 LLM 分类器判断工具调用是否安全:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Auto Mode 分类器流程 │ ├── 输入构建 │ ├── tool.toAutoClassifierInput(input) // 工具特定的分类器输入 │ │ ├── Bash: 完整命令字符串 │ │ ├── Edit: "path: content" │ │ └── Write: "path: content" │ └── 对话上下文(最近的消息摘要) │ ├── 分类器类型 │ ├── Bash Classifier(feature: BASH_CLASSIFIER) │ │ └── 专门分析 Bash 命令的安全性 │ └── Transcript Classifier(feature: TRANSCRIPT_CLASSIFIER) │ └── 分析完整对话上下文 │ ├── 分类结果 │ ├── { matches: true, confidence: 'high' } → 自动放行 │ ├── { matches: true, confidence: 'low' } → 交互式确认 │ └── { matches: false } → 交互式确认 │ └── Denial Tracking 防死锁 ├── recordDenial() 记录每次拒绝 ├── 连续拒绝达到 DENIAL_LIMITS 阈值 └── shouldFallbackToPrompting() → 降级为交互式确认
Speculative Classifier :对于 Bash 工具,在显示权限对话框之前,先启动一个 2 秒超时的推测性分类。如果分类器在 2 秒内返回 high-confidence match,直接放行,用户不会看到权限提示。这显著改善了 Auto Mode 的用户体验——大多数安全命令(ls、cat、git status)可以在用户无感知的情况下通过。
Hook 系统
src/utils/hooks.ts(160.62KB,5023 行)实现了完整的 Hook 执行引擎。
Hook 事件类型 :
事件
触发时机
可控制
PreToolUse
工具执行前
approve/deny/modify input
PostToolUse
工具执行后
inject systemMessage
PostToolUseFailure
工具执行失败后
通知
PermissionRequest
权限请求时
allow/deny
PermissionDenied
权限被拒绝后
通知
Notification
通用通知
通知
Stop
循环即将停止
blocking/preventContinuation
SubagentStop
子代理即将停止
blocking/preventContinuation
PreCompact
压缩前
customInstructions
PostCompact
压缩后
systemMessage
SessionStart
会话开始
systemMessage
SessionEnd
会话结束
通知
InstructionsLoaded
CLAUDE.md 加载后
通知
FileChanged
文件变更后
通知
TaskCreated
任务创建后
通知
Elicitation
MCP 请求输入
自动回答
三种 Hook 类型 :
类型
实现
通信方式
超时
适用场景
Command
spawn 子进程
stdin JSON → stdout JSON/text
10 分钟
本地脚本、CI 集成
Prompt
execPromptHook()
模板化 prompt → LLM 响应
取决于 LLM
复杂判断、上下文感知
HTTP
execHttpHook()
POST JSON → JSON 响应
10 分钟
远程服务、Webhook
Command Hook 的输出解析 :
1 2 3 4 5 6 7 8 9 function parseHookOutput (stdout: string ) { const trimmed = stdout.trim () if (!trimmed.startsWith ('{' )) { return { plainText : stdout } } }
异步 Hook :registerPendingAsyncHook() 支持后台执行的异步 Hook。asyncRewake 模式下,Hook 完成后如果 exit code 为 2(blocking error),通过 enqueuePendingNotification() 唤醒模型。
Hook 来源控制 :
shouldDisableAllHooksIncludingManaged():禁用所有 Hook(调试模式)
shouldAllowManagedHooksOnly():只允许 managed Hook(企业管理)
isSourceAdminTrusted():判断 Hook 来源是否为 admin-trusted
resolveHookPermissionDecision 的不变量
src/services/tools/toolHooks.ts 中的 resolveHookPermissionDecision() 封装了一个关键不变量:Hook 的 allow 不能绕过 settings.json 的 deny/ask 规则 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 if (hookPermissionResult?.behavior === 'allow' ) { const hookInput = hookPermissionResult.updatedInput ?? input const interactionSatisfied = requiresInteraction && hookPermissionResult.updatedInput !== undefined const ruleCheck = await checkRuleBasedPermissions (tool, hookInput, toolUseContext) if (ruleCheck === null ) { return { decision : hookPermissionResult, input : hookInput } } if (ruleCheck.behavior === 'deny' ) { return { decision : ruleCheck, input : hookInput } } }
这个设计防止了安全漏洞:恶意 Hook 不能通过返回 allow 来绕过管理员设置的 deny 规则。
Bash 安全模型
src/tools/BashTool/bashSecurity.ts(102.69KB)实现了 Bash 命令的深度安全分析:
命令语义分类 :
分类
示例
权限
只读
ls, cat, grep, find, git log, wc
自动放行
写入
mkdir, cp, mv, touch, git add
需要确认
破坏性
rm -rf, git push --force, chmod 777
强制确认 + 警告
网络
curl, wget, ssh, nc
需要确认
包管理
npm install, pip install, apt-get
需要确认
路径验证 (pathValidation.ts,43.93KB):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 路径验证规则 │ ├── 工作目录检查 │ ├── 命令中的所有路径必须在 CWD 或 additionalWorkingDirectories 内 │ ├── 符号链接解析后重新检查 │ └── 相对路径解析为绝对路径后检查 │ ├── 敏感路径拒绝 │ ├── /etc, /usr, /bin, /sbin │ ├── ~/.ssh, ~/.gnupg, ~/.aws │ ├── /dev, /proc, /sys │ └── Windows: C:\Windows, C:\Program Files │ ├── sed -i 特殊处理 │ ├── 解析 sed 表达式 │ ├── 提取目标文件路径 │ ├── 验证文件路径在工作目录内 │ └── 拒绝对系统文件的 in-place 编辑 │ └── 只读验证(readOnlyValidation.ts, 68.66KB) ├── Plan Mode 下所有写入操作被拒绝 ├── 只读工作目录中的写入被拒绝 └── 只读 MCP 工具的写入被拒绝
沙箱模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 沙箱执行模型 │ ├── macOS: sandbox-exec (seatbelt) │ ├── 限制文件系统访问(只允许工作目录) │ ├── 限制网络访问(可配置) │ ├── 限制进程创建 │ └── 排除命令列表(某些命令不适合沙箱) │ ├── Linux: 容器隔离 │ ├── namespace 隔离 │ ├── cgroup 资源限制 │ └── seccomp 系统调用过滤 │ ├── autoAllowBashIfSandboxed │ ├── 沙箱启用时,Bash 命令自动放行 │ ├── 不适合沙箱的命令(排除列表)仍需确认 │ └── dangerouslyDisableSandbox 时不生效 │ └── Windows: 无沙箱 └── PowerShellTool 作为替代
权限模式
模式
说明
行为
default
默认模式
按规则匹配,未匹配则交互式确认
auto
自动模式
LLM 分类器判断,高置信度自动放行
bypassPermissions
绕过模式
所有工具自动放行(危险)
plan
计划模式
只允许只读工具,写入工具被拒绝
Plan Mode 的进入/退出 :
进入:EnterPlanModeTool 或模型自主决定
退出:ExitPlanModeTool
保存:prePlanMode 字段记录进入前的模式,退出时恢复
Workspace Trust
所有 Hook 执行都需要 workspace trust——因为 Hook 执行来自 .claude/settings.json 的任意命令。
历史安全漏洞 :
SessionEnd hooks 在用户拒绝 trust dialog 时执行
SubagentStop hooks 在子代理完成前执行
这些都已修复,现在所有 Hook 都严格检查 trust 状态
ToolPermissionContext 的不可变性
ToolPermissionContext 使用 DeepImmutable<> 类型包装,确保权限上下文在传递过程中不被意外修改:
1 2 3 4 5 6 7 8 9 10 11 12 export type ToolPermissionContext = DeepImmutable <{ mode : PermissionMode additionalWorkingDirectories : Map <string , AdditionalWorkingDirectory > alwaysAllowRules : ToolPermissionRulesBySource alwaysDenyRules : ToolPermissionRulesBySource alwaysAskRules : ToolPermissionRulesBySource isBypassPermissionsModeAvailable : boolean isAutoModeAvailable?: boolean shouldAvoidPermissionPrompts?: boolean awaitAutomatedChecksBeforeDialog?: boolean prePlanMode?: PermissionMode }>
权限更新通过 setAppState 的函数式更新实现——创建新的 context 对象,而非修改现有对象。这保证了并发安全——多个工具可能同时检查权限。
五、上下文管理与压缩(Compaction)
Claude Code 的上下文管理系统解决了 LLM 的核心限制——有限的上下文窗口。系统通过五层渐进式管线,在保留关键信息的同时最大化可用上下文空间。
五层管线总览
每次 API 调用前,消息列表经过以下五层管线处理(按执行顺序):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 原始消息 messages[] │ ├── Layer 1: applyToolResultBudget() // 大工具结果裁剪 │ ├── 计算所有工具结果的总 token 数 │ ├── 超过 toolResultBudget 的结果 → 持久化到磁盘 │ └── 替换为预览 + 文件路径引用 │ ├── Layer 2: snipCompact() // 历史裁剪 [feature: HISTORY_SNIP] │ ├── 从最旧的消息开始 │ ├── 替换为 "[message snipped]" 占位符 │ └── 保留最近 N 条消息不裁剪 │ ├── Layer 3: microcompact() // 微压缩 │ ├── 合并连续的 FILE_UNCHANGED 结果 │ ├── 折叠重复的工具调用模式 │ └── 替换为 FILE_UNCHANGED_STUB │ ├── Layer 4: contextCollapse() // 上下文折叠 [feature: CONTEXT_COLLAPSE] │ ├── 识别可折叠的上下文块 │ ├── 替换为摘要占位符 │ └── 保留关键的工具结果不折叠 │ └── Layer 5: autoCompact() // 自动压缩 ├── 检查 token 使用率是否超过阈值 ├── 超过 → 调用 LLM 生成摘要 └── 替换历史消息为压缩摘要
src/utils/messages/applyToolResultBudget.ts 实现了工具结果的预算裁剪。
触发条件 :当所有工具结果的总 token 数超过 toolResultBudget 时触发。
裁剪策略 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 applyToolResultBudget 裁剪策略 │ ├── 1. 计算每个工具结果的 token 数 │ └── 使用 countTokens() 精确计算 │ ├── 2. 按 token 数降序排列 │ ├── 3. 从最大的结果开始裁剪 │ ├── 将完整结果写入磁盘文件 │ ├── 替换为预览(前 N 行 + 后 N 行) │ └── 附加文件路径引用 │ ├── 4. 重复直到总 token 数 < toolResultBudget │ └── 5. 特殊豁免 ├── FileReadTool 结果(maxResultSizeChars = Infinity) ├── 最近一轮的工具结果(保留完整性) └── 已经被裁剪过的结果(避免重复裁剪)
Layer 2: snipCompact
src/utils/messages/snipCompact.ts 实现了基于历史位置的裁剪。
核心逻辑 :从最旧的消息开始,将消息内容替换为 [message snipped] 占位符。保留最近的消息不裁剪——这些消息包含当前任务的关键上下文。
1 2 3 const SNIP_PRESERVE_RECENT = 4 const SNIP_MIN_TOKENS = 1000
Feature Gate :HISTORY_SNIP 门控。未启用时跳过此层。
Layer 3: microcompact
src/utils/messages/microcompact.ts 实现了细粒度的冗余消除。
三种微压缩策略 :
策略
触发条件
替换内容
FILE_UNCHANGED
连续多次 Read 同一文件且内容未变
FILE_UNCHANGED_STUB
TOOL_RESULT_MERGE
连续相同工具的相似结果
合并为单条
EMPTY_RESULT_STRIP
空的工具结果
移除
1 2 3 4 const FILE_UNCHANGED_STUB = '<file_unchanged />'
Layer 4: contextCollapse
src/utils/messages/contextCollapse.ts 实现了上下文块的折叠。
折叠策略 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 contextCollapse 折叠策略 │ ├── 识别可折叠块 │ ├── 连续的工具调用 + 结果对 │ ├── 长的 assistant 消息 │ └── 旧的 user 消息 │ ├── 折叠规则 │ ├── 保留最近 N 轮不折叠 │ ├── 保留包含错误的工具结果 │ ├── 保留包含文件修改的工具结果 │ └── 保留用户明确引用的内容 │ ├── 折叠格式 │ ├── 工具调用 → "[Tool: {name}({summary})]" │ ├── 工具结果 → "[Result: {brief_summary}]" │ └── 长消息 → "[Message: {first_line}... (collapsed)]" │ └── drain 机制 ├── prompt-too-long 错误时触发 ├── 逐步展开已折叠的块 └── 为 reactive compact 腾出空间
Feature Gate :CONTEXT_COLLAPSE 门控。
drain 机制 是 contextCollapse 的关键特性——当 API 返回 prompt-too-long(413)错误时,系统不是立即触发昂贵的 LLM 压缩,而是先尝试"排水"已折叠的上下文块。这是一个零成本的恢复策略。
Layer 5: autoCompact
src/utils/autoCompact.ts 是最重量级的压缩层——调用 LLM 生成对话摘要。
触发条件 :
1 2 3 4 5 6 7 8 9 10 11 12 const AUTO_COMPACT_THRESHOLD = 0.8 const AUTO_COMPACT_TARGET = 0.5 const currentUsage = countTokens (messages) / contextWindowSizeif (currentUsage > AUTO_COMPACT_THRESHOLD ) { await compactConversation (messages, { targetUsage : AUTO_COMPACT_TARGET , ...options }) }
熔断器 :
1 2 3 4 5 6 7 8 const COMPACT_COOLDOWN = 3 let turnsSinceLastCompact = 0 if (turnsSinceLastCompact < COMPACT_COOLDOWN ) { }
compactConversation 完整流程
src/utils/compactConversation.ts 实现了 LLM 驱动的对话压缩:
压缩 Prompt 的关键指令 :
1 2 3 4 5 6 7 8 9 10 11 12 你正在压缩一段对话历史。请生成一个简洁的摘要,保留以下关键信息: 1. 用户的原始请求和目标 2. 已完成的操作和结果 3. 当前的工作状态 4. 未完成的任务 5. 重要的文件路径和代码片段 6. 错误信息和解决方案 不要保留: - 冗余的工具调用细节 - 重复的文件内容 - 中间的探索性尝试(除非结论重要)
手动压缩命令
用户可以通过 /compact 斜杠命令手动触发压缩:
1 2 3 4 5 6 7 8 /compact [instructions] │ ├── 无参数 → 使用默认压缩指令 ├── 有参数 → 将参数作为额外的压缩指令 │ 例如:/compact 保留所有文件路径和错误信息 │ └── 执行 compactConversation() └── 与 autoCompact 相同的流程
Token 计数与预算
src/utils/countTokens.ts 提供精确的 token 计数:
1 2 3 4 5 6 7 8 9 10 11 countTokens (messages) countToolResultTokens (result) estimateTokenCount (text) const TOKEN_BUDGET = { systemPrompt : 0.15 , toolSchemas : 0.10 , conversation : 0.75 , }
normalizeMessages
src/utils/messages/normalizeMessages.ts 是管线的最后一步——将内部消息格式转换为 API 格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 normalizeMessages 转换规则 │ ├── 合并连续的同角色消息 │ └── API 要求 user/assistant 交替 │ ├── 移除空消息 │ └── 压缩后可能产生空消息 │ ├── 处理 cache_control │ ├── 标记 ephemeral breakpoint │ └── 用于 Prompt Cache 优化 │ ├── 处理 tool_result 格式 │ ├── 内部格式 → API 格式 │ ├── 截断超长结果 │ └── 添加 is_error 标记 │ └── 注入 prependUserContext ├── 用户上下文(文件列表、git 状态等) └── 附加到第一条 user 消息
Prompt-Too-Long 三级恢复
当 API 返回 413 prompt-too-long 错误时,系统按以下顺序尝试恢复:
当 SUMMARIZE_TOOL_RESULTS feature flag 启用时,system prompt 中注入额外指令,要求模型在工具结果过长时自行生成摘要。这是一种"模型侧压缩"——不依赖后处理管线,而是让模型在生成响应时就进行信息压缩。
1 2 3 SUMMARIZE_TOOL_RESULTS 指令(注入 system prompt): "当工具结果超过 2000 tokens 时,在你的响应中只引用关键信息, 不要逐字复述工具输出。用 <summary> 标签包裹你的摘要。"
这与五层管线互补——管线处理历史消息,SUMMARIZE_TOOL_RESULTS 处理当前轮次的新结果。
Session Memory 与压缩的协作
src/services/compact/sessionMemoryCompact.ts(21.18KB)实现了压缩过程中的会话记忆保留。当 autoCompact 触发时,大量对话细节被摘要替代,但某些信息对当前任务至关重要。Session Memory 在压缩前提取这些关键信息,压缩后重新注入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Session Memory 与 Compact 的协作 │ ├── 压缩前 │ ├── sessionMemoryCompact 分析即将被压缩的消息 │ ├── 提取关键上下文: │ │ ├── 用户的原始请求和目标 │ │ ├── 当前工作状态(正在做什么、进度如何) │ │ ├── 本次会话中用户提出的特殊约束 │ │ ├── 未完成的任务列表 │ │ └── 关键的文件路径和代码位置 │ └── 存储为 SessionMemory 对象 │ ├── 压缩中 │ ├── compactConversation() 正常执行 LLM 摘要 │ └── 旧消息被替换为压缩摘要 │ └── 压缩后 ├── Session Memory 作为独立消息注入 ├── 位置:紧跟在压缩摘要之后 ├── 格式:<session_memory> XML 标签 └── 确保模型在压缩后仍然知道关键上下文
这解决了一个核心矛盾:压缩需要丢弃细节以节省空间,但当前任务需要某些细节才能继续。Session Memory 是这个矛盾的折中方案——只保留对当前任务最关键的信息。
compact_boundary 标记
压缩后的摘要消息带有 compact_boundary 标记,用于标识压缩边界。这个标记有多个用途:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 compact_boundary 的作用 │ ├── 1. 防止重复压缩 │ └── autoCompact 不会压缩 compact_boundary 之后的消息 │ (这些是压缩后新产生的消息,不应该被再次压缩) │ ├── 2. 消息分区 │ ├── compact_boundary 之前 = 压缩摘要(不可展开) │ └── compact_boundary 之后 = 原始消息(可压缩) │ ├── 3. Token 计数基准 │ └── autoCompact 只计算 compact_boundary 之后的消息 token 数 │ (摘要部分的 token 数是固定的,不需要重复计算) │ └── 4. Session Memory 注入点 └── Session Memory 注入在 compact_boundary 之后的第一条消息之前
Prompt Cache 断裂检测
src/services/api/promptCacheBreakDetection.ts(26.38KB)实现了 Prompt Cache 命中率的实时监控。当检测到 cache 命中率异常下降时,系统会尝试诊断原因并自动修复。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Prompt Cache 断裂检测 │ ├── 监控指标 │ ├── cache_creation_input_tokens(新创建的缓存 token 数) │ ├── cache_read_input_tokens(从缓存读取的 token 数) │ └── cache_hit_rate = read / (read + creation) │ ├── 断裂检测 │ ├── 连续 N 次 API 调用的 cache_hit_rate < 阈值 │ ├── 或 cache_creation_input_tokens 突然大幅增加 │ └── → 触发断裂告警 │ ├── 常见断裂原因 │ ├── System Prompt 动态部分变化(记忆更新、MCP 工具变化) │ ├── 工具 Schema 顺序变化(MCP 工具增减) │ ├── 消息格式变化(normalizeMessages 的行为变化) │ └── API 端侧缓存过期 │ └── 自动修复 ├── 重新排序工具 Schema(恢复稳定顺序) ├── 冻结动态 Prompt 部分(延迟更新到下一轮) └── 记录诊断信息到遥测
Token 估算算法
src/services/tokenEstimation.ts(16.97KB)提供了多种精度的 token 估算方法:
方法
精度
速度
适用场景
countTokens()
精确
慢(~1ms/消息)
压缩决策、预算检查
estimateTokenCount()
近似(±10%)
快(~0.01ms)
快速预检、UI 显示
estimateToolResultTokens()
近似
快
工具结果预算
estimateTokenCount() 使用简单的字符数/4 启发式——对英文文本误差约 ±10%,对代码误差约 ±15%。这个精度足够用于快速预检(如判断是否需要触发 autoCompact),但不够用于精确的预算计算。
精确计数的缓存 :countTokens() 的结果会被缓存——对于未变化的消息,不需要重复计数。缓存 key 是消息内容的 hash。这在长会话中显著减少了 token 计数的开销——只有新增的消息需要计数,历史消息使用缓存值。
contentReplacementState 持久化
applyToolResultBudget 的替换记录通过 contentReplacementState 持久化到会话文件中。这支持了会话恢复——当用户恢复一个之前的会话时,系统可以重放替换记录,将磁盘文件引用正确地映射回原始工具结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 contentReplacementState 持久化规则 │ ├── 持久化条件 │ ├── 只有 agent:* 和 repl_main_thread 来源的查询持久化 │ └── 临时的 runForkedAgent 调用不持久化 │ ├── 持久化内容 │ ├── 替换映射(tool_use_id → 磁盘文件路径) │ ├── 原始结果的 token 数 │ └── 替换时间戳 │ └── 恢复流程 ├── 加载会话文件 ├── 重建 contentReplacementState └── 后续的 applyToolResultBudget 可以识别已替换的结果
六、记忆系统(Memory System)
Claude Code 的记忆系统实现了跨会话的知识持久化,是整个产品"越用越懂你"体验的核心。系统由五个子模块组成:CLAUDE.md 文件层级 (项目约定)、memdir 结构化记忆 (用户偏好与知识)、Session Memory 会话记忆 (单次会话内的短期记忆)、团队记忆同步 (跨设备/跨成员共享)、Auto Dream 记忆整固 (后台记忆优化)。
核心文件
文件
大小
说明
src/utils/claudemd.ts
46.75 KB
CLAUDE.md 加载、解析、@include 展开
src/memdir/memdir.ts
21.17 KB
memdir 记忆目录核心操作
src/memdir/memoryTypes.ts
22.59 KB
记忆类型定义(schema、标签、元数据)
src/memdir/findRelevantMemories.ts
5.32 KB
相关记忆检索(语义匹配)
src/memdir/paths.ts
10.69 KB
记忆路径管理(项目哈希、目录结构)
src/memdir/teamMemPaths.ts
11.70 KB
团队记忆路径
src/memdir/teamMemPrompts.ts
5.96 KB
团队记忆 prompt 模板
src/services/extractMemories/extractMemories.ts
21.78 KB
自动记忆提取引擎
src/services/extractMemories/prompts.ts
7.64 KB
提取 prompt 模板
src/services/teamMemorySync/index.ts
44.34 KB
团队记忆同步服务
src/services/teamMemorySync/secretScanner.ts
9.55 KB
秘密扫描器(防泄漏)
src/services/autoDream/autoDream.ts
11.31 KB
Auto Dream 记忆整固
src/services/SessionMemory/sessionMemory.ts
16.66 KB
会话记忆管理
src/services/SessionMemory/prompts.ts
12.65 KB
会话记忆 prompt
src/services/compact/sessionMemoryCompact.ts
21.18 KB
会话记忆压缩
记忆文件按作用域分为四级,从全局到局部逐级加载。src/utils/claudemd.ts(46.75KB)是这个体系的核心实现。
级别
路径
作用域
说明
1. 用户级
~/.claude/CLAUDE.md
全局
用户个人偏好,跨所有项目生效
2. 项目根级
{projectRoot}/CLAUDE.md
项目
项目级约定,团队共享(checked in)
3. 子目录级
{cwd}/CLAUDE.md
目录
子模块/子目录特定指令
4. 父目录级
{parent}/.claude/CLAUDE.md
祖先
向上遍历直到文件系统根
加载顺序 :用户级 → 父目录级(从根到当前目录)→ 项目根级 → 子目录级。后加载的内容优先级更高——子目录的 CLAUDE.md 可以覆盖项目根的指令。
claudemd.ts 的完整解析流程 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 loadClaudeMd(projectRoot, cwd, userHome) │ ├── 1. 发现阶段 │ ├── findClaudeMdFiles(userHome) // ~/.claude/CLAUDE.md │ ├── findClaudeMdFiles(projectRoot) // {projectRoot}/CLAUDE.md │ ├── findClaudeMdFiles(cwd) // {cwd}/CLAUDE.md(如果 cwd ≠ projectRoot) │ ├── walkAncestors(projectRoot → /) // 向上遍历所有祖先目录 │ │ └── 每个目录检查 .claude/CLAUDE.md 和 CLAUDE.md │ └── 去重(同一文件不加载两次) │ ├── 2. 读取阶段 │ ├── 对每个发现的文件: │ │ ├── readFile(path, 'utf-8') │ │ ├── 检查文件大小 ≤ MAX_CLAUDEMD_SIZE(100KB) │ │ └── 超过大小限制 → 截断 + 警告 │ └── Trust 检查 │ ├── 用户级文件 → 始终信任 │ ├── 项目级文件 → 需要 workspace trust │ └── 未信任 → 跳过(不加载) │ ├── 3. @include 展开阶段 │ ├── 正则匹配 /^@(.+)$/gm │ ├── 对每个 @include: │ │ ├── 解析路径(相对于当前 CLAUDE.md 所在目录) │ │ ├── 安全检查 │ │ │ ├── 路径必须在工作目录内 │ │ │ ├── 不能是符号链接指向外部 │ │ │ └── 不能包含 .. 逃逸 │ │ ├── 递归深度检查(最大 5 层) │ │ ├── 读取文件内容 │ │ └── 递归展开嵌套的 @include │ └── 文件不存在 → 静默忽略(不报错,不中断) │ ├── 4. 格式化阶段 │ ├── 每个来源用 XML 标签包裹 │ │ ├── <user_instructions source="~/.claude/CLAUDE.md"> │ │ ├── <project_instructions source="{projectRoot}/CLAUDE.md"> │ │ └── <directory_instructions source="{cwd}/CLAUDE.md"> │ ├── 标签中包含来源路径(便于模型引用) │ └── 拼接为最终字符串 │ └── 5. Token 预算检查 ├── 计算总 token 数 ├── 超过预算 → 按优先级裁剪 └── 返回最终的记忆 prompt
@include 的实际使用场景 :
1 2 3 4 5 6 7 8 9 # 项目 CLAUDE.md 示例 @docs/coding-standards.md # 引用编码规范文档 @.claude/prompts/review-checklist.md # 引用代码审查清单 @scripts/README.md # 引用脚本说明 ## 项目特定规则 - 使用 TypeScript strict mode- 所有 API 端点必须有 OpenAPI 注释- 提交消息遵循 Conventional Commits
CLAUDE.md 的安全模型 :
项目级 CLAUDE.md 是一个潜在的攻击向量——恶意仓库可以在 CLAUDE.md 中注入指令,让模型执行危险操作。Claude Code 通过以下机制防御:
Workspace Trust :项目级 CLAUDE.md 只在用户确认信任工作区后才加载。首次打开项目时,Trust Dialog 会显示 CLAUDE.md 的内容预览。
@include 沙箱 :@include 只能引用工作目录内的文件,不能逃逸到系统目录。
大小限制 :单个文件 100KB,防止通过超大文件消耗上下文窗口。
InstructionsLoaded Hook :企业可以通过 Hook 审计加载的指令内容。
Memdir 结构化记忆
src/memdir/memdir.ts(21.17KB)实现了结构化的记忆存储系统。与 CLAUDE.md 的纯文本记忆不同,memdir 提供了类型化、可检索、可同步的记忆条目。
目录结构 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ~/.claude/memory/ ├── user/ // 用户级记忆(全局) │ ├── preferences.md // 用户偏好 │ ├── coding-style.md // 编码风格 │ └── tools.md // 工具使用习惯 │ ├── project-{hash}/ // 项目级记忆(按项目哈希隔离) │ ├── architecture.md // 架构决策 │ ├── conventions.md // 项目约定 │ ├── common-errors.md // 常见错误与解决方案 │ └── dependencies.md // 依赖选择理由 │ ├── team/ // 团队记忆(跨成员共享) │ ├── shared-conventions.md // 团队共享约定 │ └── onboarding.md // 新成员引导 │ └── .index.json // 记忆索引(元数据缓存)
项目哈希计算 :src/memdir/paths.ts 中的 getProjectHash() 使用项目根目录的绝对路径计算 SHA-256 哈希的前 12 位。这保证了不同项目的记忆完全隔离,即使项目名称相同。
记忆类型系统 :
src/memdir/memoryTypes.ts(22.59KB)定义了记忆条目的完整类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 interface MemoryEntry { id : string type : MemoryType scope : 'user' | 'project' | 'team' title : string content : string tags : string [] createdAt : string updatedAt : string lastAccessedAt : string source : 'auto' | 'manual' | 'dream' | 'team-sync' sessionId?: string confidence : number } type MemoryType = | 'preference' | 'convention' | 'architecture' | 'error-solution' | 'workflow' | 'context'
记忆的 CRUD 操作 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 memdir CRUD 操作 │ ├── createMemory(entry) │ ├── 生成 UUID │ ├── 写入 Markdown 文件(frontmatter + content) │ ├── 更新 .index.json │ └── 触发团队同步(如果 scope === 'team') │ ├── readMemory(id) │ ├── 从 .index.json 查找路径 │ ├── 读取文件内容 │ ├── 解析 frontmatter │ └── 更新 lastAccessedAt │ ├── updateMemory(id, updates) │ ├── 读取现有内容 │ ├── 合并更新 │ ├── 写入文件 │ ├── 更新 .index.json │ └── 触发团队同步 │ ├── deleteMemory(id) │ ├── 删除文件 │ ├── 从 .index.json 移除 │ └── 触发团队同步(标记为已删除) │ └── listMemories(filter?) ├── 读取 .index.json ├── 按 filter 过滤(scope、type、tags) └── 按 updatedAt 降序排列
findRelevantMemories 相关性检索
src/memdir/findRelevantMemories.ts(5.32KB)实现了基于当前对话上下文的记忆检索——不是加载所有记忆,而是只加载与当前任务相关的记忆。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 findRelevantMemories(context) 检索流程 │ ├── 1. 构建查询向量 │ ├── 提取当前对话的关键词 │ ├── 提取最近工具调用涉及的文件路径 │ ├── 提取用户最近的问题/指令 │ └── 组合为查询字符串 │ ├── 2. 候选记忆收集 │ ├── 加载 user/ 目录下所有记忆 │ ├── 加载 project-{hash}/ 目录下所有记忆 │ └── 加载 team/ 目录下所有记忆 │ ├── 3. 相关性评分 │ ├── 标签匹配得分(tags 与查询关键词的交集) │ ├── 标题匹配得分(title 与查询的模糊匹配) │ ├── 内容匹配得分(content 中关键词出现频率) │ ├── 时间衰减因子(最近访问的记忆得分更高) │ └── 来源权重(manual > auto > dream) │ ├── 4. 排序与截断 │ ├── 按综合得分降序排列 │ ├── 取 top-K 条记忆(K 由 token 预算决定) │ └── 确保总 token 数不超过 MEMORY_TOKEN_BUDGET │ └── 5. 返回 └── 格式化为 <relevant_memories> XML 标签
与 Memory Prefetch 的集成 :
在代理循环中,findRelevantMemories() 通过 prefetch 机制异步执行——在工具执行期间后台检索相关记忆,不阻塞主循环。检索结果作为附件消息注入到下一次 API 调用中:
1 2 3 4 5 6 7 8 9 10 11 12 13 using pendingMemoryPrefetch = startRelevantMemoryPrefetch ( state.messages , state.toolUseContext ) if (pendingMemoryPrefetch?.settledAt !== null && consumedOnIteration === -1 ) { const memoryAttachments = filterDuplicateMemoryAttachments ( await pendingMemoryPrefetch.promise , toolUseContext.readFileState ) }
去重逻辑 :filterDuplicateMemoryAttachments() 过滤掉模型已经通过 Read/Write/Edit 工具访问过的文件——如果模型刚刚读取了 CLAUDE.md,就不需要再通过记忆系统注入相同的内容。
Auto Memory 自动记忆提取
src/services/extractMemories/extractMemories.ts(21.78KB)实现了自动记忆提取——在会话结束时,LLM 分析对话历史,提取值得跨会话记住的信息。
触发条件 :
1 2 3 4 5 6 7 8 Auto Memory 触发条件(全部满足才触发) │ ├── 会话结束时(用户退出、/exit、或 headless 模式完成) ├── CLAUDE_CODE_SIMPLE !== '1'(bare 模式禁用) ├── feature('AUTO_MEMORY') 启用 ├── 对话长度 > MIN_CONVERSATION_LENGTH(避免短对话触发) ├── 距离上次提取 > EXTRACT_COOLDOWN(防止频繁提取) └── 用户未禁用自动记忆(settings.json 中的 autoMemory 配置)
提取流程 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 extractMemories(conversationHistory) │ ├── 1. 对话预处理 │ ├── 过滤系统消息和工具调用细节 │ ├── 保留用户消息和模型的关键响应 │ ├── 截断到最大 token 限制(避免超长对话的提取成本过高) │ └── 标记用户明确表达偏好的消息("我喜欢..."、"请总是..."、"不要...") │ ├── 2. LLM 提取调用 │ ├── 使用 Haiku 模型(成本低、速度快) │ ├── 注入提取 prompt(src/services/extractMemories/prompts.ts) │ └── 要求返回结构化 JSON │ ├── 3. 提取 Prompt 的关键指令 │ ├── "分析对话历史,提取值得跨会话记住的信息" │ ├── 关注类别: │ │ ├── 用户明确表达的偏好("我喜欢..."、"请总是..."、"不要...") │ │ ├── 项目特定的约定(命名规范、目录结构、依赖选择) │ │ ├── 反复出现的工作模式(用户总是先写测试、总是要求代码审查) │ │ ├── 重要的错误及其解决方案(特别是环境特定的问题) │ │ └── 架构决策及其理由 │ ├── 不要提取: │ │ ├── 临时性的任务细节("修复这个 bug") │ │ ├── 具体的代码片段(除非是通用模板) │ │ ├── 一次性的操作步骤 │ │ └── 已经在 CLAUDE.md 中记录的内容 │ └── 输出格式:JSON 数组,每条包含 type、title、content、tags、confidence │ ├── 4. 去重与冲突检测 │ ├── 加载现有记忆的 .index.json │ ├── 对每条候选记忆: │ │ ├── 标题相似度检查(Levenshtein 距离 < 阈值 → 视为重复) │ │ ├── 内容相似度检查(关键词重叠率 > 阈值 → 视为重复) │ │ ├── 标签完全匹配 → 可能是同一主题的更新 │ │ └── 冲突检测(新记忆与旧记忆矛盾 → 标记为更新) │ ├── 重复 → 跳过 │ ├── 更新 → 合并到现有记忆 │ └── 新信息 → 创建新记忆条目 │ └── 5. 写入 ├── 调用 createMemory() 或 updateMemory() ├── source 标记为 'auto' ├── confidence 使用 LLM 返回的置信度 └── 后台执行,不阻塞用户退出
Session Memory 会话记忆
src/services/SessionMemory/sessionMemory.ts(16.66KB)实现了单次会话内的短期记忆——与 memdir 的跨会话长期记忆不同,Session Memory 只在当前会话内有效,用于在压缩(compact)后保留关键的工作上下文。
Session Memory 的核心问题 :当 autoCompact 压缩对话历史时,大量细节被摘要替代。但某些信息对当前任务至关重要——例如"用户要求所有函数都加 JSDoc"、“当前正在重构 auth 模块”。Session Memory 在压缩前提取这些关键信息,压缩后重新注入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Session Memory 生命周期 │ ├── 1. 提取(压缩前) │ ├── sessionMemoryCompact.ts 在 compactConversation() 中调用 │ ├── 分析即将被压缩的消息 │ ├── 提取关键上下文: │ │ ├── 用户的原始请求和目标 │ │ ├── 当前的工作状态(正在做什么、做到哪里了) │ │ ├── 重要的约束和偏好(本次会话中用户提出的) │ │ ├── 未完成的任务列表 │ │ └── 关键的文件路径和代码位置 │ └── 存储为 SessionMemory 对象 │ ├── 2. 注入(压缩后) │ ├── 在压缩摘要之后注入 Session Memory │ ├── 格式:<session_memory> XML 标签 │ ├── 位置:紧跟在压缩摘要消息之后 │ └── 确保模型在压缩后仍然知道关键上下文 │ ├── 3. 更新(每次压缩时) │ ├── 新的压缩 → 新的 Session Memory 提取 │ ├── 与旧的 Session Memory 合并 │ ├── 去除已完成的任务 │ └── 更新工作状态 │ └── 4. 过期 └── 会话结束时自动丢弃(不持久化到 memdir)
Session Memory Prompt (src/services/SessionMemory/prompts.ts,12.65KB):
1 2 3 4 5 6 7 8 9 10 11 12 提取 Session Memory 的 prompt 指令: "你正在分析一段即将被压缩的对话历史。请提取以下关键信息, 这些信息将在压缩后注入,帮助你继续工作: 1. 用户的核心目标(一句话总结) 2. 当前工作状态(正在做什么、进度如何) 3. 重要的约束(用户在本次会话中提出的特殊要求) 4. 未完成的任务(列表形式) 5. 关键的文件路径(正在编辑或需要关注的文件) 6. 重要的错误和解决方案(如果有) 格式要求:简洁、结构化、可直接作为上下文使用。"
团队记忆同步
src/services/teamMemorySync/index.ts(44.34KB)实现了跨设备、跨团队成员的记忆同步。
同步架构 :
同步流程 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 teamMemorySync() 完整流程 │ ├── 1. 触发条件 │ ├── 会话开始时(拉取远程变更) │ ├── 记忆变更时(推送本地变更) │ ├── 定期轮询(每 5 分钟检查远程变更) │ └── 手动触发(/memory sync 命令) │ ├── 2. 拉取(Pull) │ ├── GET /v1/memory-sync?since={lastSyncTimestamp} │ ├── 获取自上次同步以来的远程变更 │ ├── 对每条远程变更: │ │ ├── 本地不存在 → 创建 │ │ ├── 本地存在且远程更新 → 合并 │ │ ├── 本地已删除 → 跳过 │ │ └── 冲突(双方都修改) → 使用 LWW(Last Writer Wins)策略 │ └── 更新 lastSyncTimestamp │ ├── 3. 推送(Push) │ ├── 收集自上次同步以来的本地变更 │ ├── 对每条变更执行 Secret Scanner │ │ └── 检测到秘密 → 阻止推送 + 警告用户 │ ├── POST /v1/memory-sync │ │ ├── 发送变更集(创建/更新/删除) │ │ └── 附带本地时间戳 │ └── 更新 lastSyncTimestamp │ └── 4. 冲突解决 ├── LWW(Last Writer Wins):时间戳更新的版本胜出 ├── 如果时间戳相同 → 内容更长的版本胜出 └── 删除操作优先级最低(防止误删)
Secret Scanner 秘密扫描器
src/services/teamMemorySync/secretScanner.ts(9.55KB)在记忆同步前扫描内容,防止敏感信息泄漏到远程存储。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 Secret Scanner 检测规则 │ ├── API Key 模式 │ ├── AWS: AKIA[0-9A-Z]{16} │ ├── GitHub: ghp_[a-zA-Z0-9]{36} │ ├── Anthropic: sk-ant-[a-zA-Z0-9-]+ │ ├── OpenAI: sk-[a-zA-Z0-9]{48} │ └── 通用: [a-zA-Z0-9]{32,} 在 key/token/secret 上下文中 │ ├── 私钥模式 │ ├── -----BEGIN RSA PRIVATE KEY----- │ ├── -----BEGIN EC PRIVATE KEY----- │ └── -----BEGIN OPENSSH PRIVATE KEY----- │ ├── 连接字符串模式 │ ├── postgresql://user:password@host │ ├── mongodb://user:password@host │ └── redis://user:password@host │ ├── 环境变量模式 │ ├── DATABASE_URL=... │ ├── SECRET_KEY=... │ └── PASSWORD=... │ └── 处理策略 ├── 检测到秘密 → 阻止同步 ├── 记录警告日志 ├── 通知用户("记忆中包含敏感信息,已阻止同步") └── 建议用户移除敏感信息后重试
Auto Dream 记忆整固
src/services/autoDream/autoDream.ts(11.31KB)实现了一个后台"做梦"机制——在空闲时间运行一个子代理,对现有记忆进行整理、合并、优化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Auto Dream 流程 │ ├── 触发条件 │ ├── 会话空闲超过阈值(用户长时间未输入) │ ├── 或会话结束后的后台任务 │ ├── 且 memdir 中记忆条目数 > MIN_MEMORIES_FOR_DREAM │ └── 且距离上次 Dream > DREAM_COOLDOWN │ ├── Dream 子代理 │ ├── 使用 Haiku 模型(低成本) │ ├── 加载所有现有记忆 │ ├── 分析记忆质量和组织结构 │ └── 执行以下操作: │ ├── 整固操作 │ ├── 合并重复记忆 │ │ └── 多条关于同一主题的记忆 → 合并为一条更全面的 │ ├── 更新过时记忆 │ │ └── 与最近的对话历史对比,更新不再准确的信息 │ ├── 提升记忆质量 │ │ └── 模糊的记忆 → 添加更多细节和上下文 │ ├── 调整标签 │ │ └── 统一标签命名,添加缺失的标签 │ └── 淘汰低价值记忆 │ └── 长时间未访问 + 低置信度 → 标记为候选删除 │ └── 结果 ├── 更新后的记忆写回 memdir ├── source 标记为 'dream' └── 记录 Dream 日志(用于审计)
loadMemoryPrompt 的完整组装
src/utils/memory.ts 中的 loadMemoryPrompt() 将所有记忆源组装为 system prompt 的一部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 loadMemoryPrompt() 组装顺序 │ ├── 1. CLAUDE.md 层级 │ ├── 用户级 ~/.claude/CLAUDE.md(含 @include 展开) │ ├── 父目录链 CLAUDE.md(从根到当前目录) │ ├── 项目根级 {projectRoot}/CLAUDE.md(含 @include 展开) │ └── 子目录级 {cwd}/CLAUDE.md(如果 cwd ≠ projectRoot) │ ├── 2. Memdir 结构化记忆 │ ├── user/ 目录下的相关记忆(findRelevantMemories 筛选) │ ├── project-{hash}/ 目录下的相关记忆 │ └── team/ 目录下的相关记忆 │ ├── 3. Session Memory(如果存在) │ └── 上一次压缩后保留的会话记忆 │ ├── 4. 格式化输出 │ ├── <user_instructions>...</user_instructions> │ ├── <project_instructions>...</project_instructions> │ ├── <relevant_memories>...</relevant_memories> │ └── <session_memory>...</session_memory> │ └── 5. Token 预算裁剪 ├── 总预算:~16K tokens ├── 超过预算时按优先级裁剪: │ ├── 结构化记忆(最旧的条目先裁剪) │ ├── 父目录链记忆(最远的祖先先裁剪) │ ├── 项目级记忆(尾部裁剪) │ └── 用户级记忆(尾部裁剪) └── CLAUDE.md 内容优先级高于 memdir 记忆
记忆的 Token 预算
记忆内容有严格的 token 预算限制,防止过大的记忆文件占满上下文窗口:
1 2 3 4 5 6 7 const MEMORY_TOKEN_BUDGET = { userInstructions : 4000 , projectInstructions : 8000 , relevantMemories : 4000 , sessionMemory : 2000 , total : 16000 , }
预算分配策略 :CLAUDE.md 的预算是固定的(用户和项目各自有上限),memdir 记忆的预算是弹性的——如果 CLAUDE.md 使用的 token 较少,剩余的预算会分配给 memdir 记忆。
/memory 命令
用户可以通过 /memory 斜杠命令管理记忆:
1 2 3 4 5 6 7 8 9 10 11 /memory → 显示当前加载的所有记忆来源和 token 使用情况 /memory edit → 打开编辑器编辑项目级 CLAUDE.md /memory edit --global → 编辑用户级 ~/.claude/CLAUDE.md /memory edit --project → 编辑项目级 CLAUDE.md /memory list → 列出 memdir 中的所有记忆条目 /memory list --project → 只列出项目级记忆 /memory list --tags typescript → 按标签过滤 /memory clear → 清除所有结构化记忆 /memory clear --project → 只清除项目级结构化记忆 /memory sync → 手动触发团队记忆同步 /memory dream → 手动触发 Auto Dream 整固
InstructionsLoaded Hook
当 CLAUDE.md 加载完成后,触发 InstructionsLoaded Hook,允许企业审计和控制记忆内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 { event : 'InstructionsLoaded' , data : { sources : [ { path : '~/.claude/CLAUDE.md' , content : '...' , tokens : 1200 }, { path : '/project/CLAUDE.md' , content : '...' , tokens : 2300 }, ], totalTokens : 3500 , memoryEntries : 12 , teamMemories : 3 , } } { suppressOutput : true , systemMessage : '...' , }
记忆与 Prompt Cache 的交互
记忆内容位于 system prompt 的动态区域 (SYSTEM_PROMPT_DYNAMIC_BOUNDARY 之后),这意味着记忆内容的变化不会破坏静态区域的 Prompt Cache。但记忆内容本身的变化会导致动态区域的 cache miss。
优化策略 :
会话内缓存 :记忆内容在会话内缓存,只在 settingsChangeDetector 检测到 CLAUDE.md 文件变化时重新加载
@include 缓存 :@include 文件的内容通过文件 mtime 检测变化,未变化时使用缓存
memdir 索引缓存 :.index.json 的 hash 用于快速检测记忆变化,避免每次都遍历目录
Memory Prefetch 去重 :filterDuplicateMemoryAttachments() 避免注入模型已经通过工具访问过的文件内容
稳定排序 :记忆条目按 ID 排序注入,保证相同的记忆集合产生相同的 prompt 文本,最大化 cache 命中率
记忆有两种注入方式,适用于不同场景:
注入方式
时机
内容
适用场景
System Prompt
每次 API 调用
CLAUDE.md + 高优先级记忆
始终需要的项目约定和用户偏好
Tool Result
工具执行后的附件
相关记忆(findRelevantMemories)
与当前任务相关的上下文记忆
System Prompt 注入的记忆是"始终可见"的——模型在每次响应中都能看到。Tool Result 注入的记忆是"按需可见"的——只在相关时才出现,节省上下文空间。
这种双轨策略平衡了记忆覆盖率 和上下文效率 :核心约定通过 System Prompt 始终生效,长尾知识通过 Tool Result 按需注入。
七、多代理架构(Multi-Agent)
Claude Code 支持多种代理生成模式,从简单的子代理(AgentTool)到完整的团队协作(Swarm),实现了从单任务委派到多代理并行协作的完整谱系。
四种代理生成模式
模式
进程模型
上下文共享
通信方式
适用场景
AgentTool
同进程
共享 prompt cache
函数调用
简单子任务委派
Fork
子进程
继承父上下文
IPC
需要隔离的并行任务
Swarm
独立进程
无共享
消息队列
多代理协作
Task
后台进程
无共享
轮询/通知
长时间异步任务
src/tools/AgentTool/AgentTool.ts 是最常用的子代理模式。主代理通过 AgentTool 工具调用创建子代理,子代理在同一进程内执行。
子代理的 Prompt 构建 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 AgentTool 子代理 Prompt │ ├── System Prompt │ ├── 继承主代理的 system prompt(静态部分) │ ├── 添加子代理特定指令 │ │ ├── "你是一个子代理,专注于完成特定任务" │ │ ├── "完成后使用 TaskOutput 返回结果" │ │ └── "不要偏离分配的任务" │ └── 工具集限制(可能比主代理少) │ ├── 初始消息 │ ├── 主代理的任务描述 │ ├── 相关上下文(文件路径、代码片段) │ └── 期望的输出格式 │ └── 约束 ├── maxTurns: 受限(防止无限循环) ├── 权限: 继承主代理的权限模式 └── 工具: 可配置的工具子集
Prompt Cache 共享 :AgentTool 子代理与主代理共享 system prompt 的静态部分,这意味着子代理的首次 API 调用可以命中主代理的 Prompt Cache——节省约 15% 的 token 成本。
递归防护 :
1 2 3 4 5 6 7 8 9 10 const MAX_AGENT_DEPTH = 3 if (currentDepth >= MAX_AGENT_DEPTH ) { return { type : 'error' , error : `Maximum agent nesting depth (${MAX_AGENT_DEPTH} ) exceeded` } }
内置代理类型
通过 --agents 参数或 .claude/agents/ 目录定义的代理模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 代理定义格式(.claude/agents/*.md 或 .claude/agents/*.yml) │ ├── Markdown 格式 │ ├── 文件名 = 代理名称 │ ├── 文件内容 = 代理的 system prompt 补充 │ └── 通过 @include 引用其他文件 │ └── YAML 格式 ├── name: 代理名称 ├── description: 代理描述 ├── prompt: 代理的 system prompt 补充 ├── tools: 可用工具列表 ├── maxTurns: 最大迭代次数 └── model: 使用的模型(可覆盖)
代理发现 :getAgentDefinitionsWithOverrides() 在启动时扫描 .claude/agents/ 目录,将每个文件解析为代理定义。用户可以通过 @agent-name 在对话中引用特定代理。
Fork 子代理
Fork 模式通过 child_process.fork() 创建独立的 Node.js 进程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Fork 子代理生命周期 │ ├── 创建 │ ├── fork() 创建子进程 │ ├── 传递父代理的上下文快照 │ │ ├── 当前消息历史 │ │ ├── 工作目录 │ │ ├── 环境变量 │ │ └── 权限配置 │ └── 子进程独立运行 query() 循环 │ ├── 通信 │ ├── IPC channel(process.send / process.on('message')) │ ├── 进度报告(子 → 父) │ ├── 权限请求(子 → 父 → 用户 → 父 → 子) │ └── 结果返回(子 → 父) │ ├── 隔离 │ ├── 独立的内存空间 │ ├── 独立的文件句柄 │ ├── 独立的 API 连接 │ └── 崩溃不影响父进程 │ └── 终止 ├── 正常完成 → 返回结果 ├── 超时 → SIGTERM → SIGKILL └── 父进程退出 → 子进程自动终止
Swarm 团队协作
src/utils/swarm/ 实现了多代理团队协作模式(feature: AGENT_SWARMS)。
团队创建 :
SendMessageTool :团队成员之间通过 SendMessageTool 通信。消息通过共享的消息队列传递,每个代理在自己的循环中轮询新消息。
TeamDeleteTool :销毁团队,终止所有成员进程,清理资源。
协调器模式 :当 awaitAutomatedChecksBeforeDialog 为 true 时,权限请求先经过协调器的自动化检查(classifier + hooks),只有未通过的请求才提交给用户。这减少了多代理场景下的权限提示频率。
Task 后台任务
Task 系统(feature: TASKS)支持长时间运行的后台任务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Task 生命周期 │ ├── TaskCreateTool │ ├── 创建任务定义 │ ├── 分配唯一 ID │ └── 启动后台进程 │ ├── 执行中 │ ├── 独立的 query() 循环 │ ├── 定期写入进度到 TaskOutput │ ├── 可通过 TaskGetTool 查询状态 │ └── 可通过 TaskStopTool 停止 │ ├── 状态 │ ├── pending → 等待执行 │ ├── running → 执行中 │ ├── completed → 已完成 │ ├── failed → 失败 │ └── stopped → 被停止 │ └── 结果 ├── TaskOutputTool 写入最终结果 ├── TaskGetTool 读取结果 └── TaskListTool 列出所有任务
Todo V2 :当 isTodoV2Enabled() 为 true 时,Task 系统升级为 Todo V2——支持任务依赖、优先级、标签等更丰富的任务管理功能。
后台会话管理
src/entrypoints/cli.tsx 中的 ps、logs、attach、kill 命令(feature: BG_SESSIONS)管理后台运行的代理会话:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 后台会话命令 │ ├── claude ps → 列出所有后台会话 │ └── 显示 ID、状态、运行时间、任务描述 │ ├── claude logs <id> → 查看会话日志 │ └── 实时流式输出 │ ├── claude attach <id> → 附加到后台会话 │ └── 恢复交互式控制 │ ├── claude kill <id> → 终止后台会话 │ └── 发送 SIGTERM → 等待 → SIGKILL │ └── --bg / --background → 以后台模式启动 └── 立即返回会话 ID
SubagentStop Hook
子代理完成时触发 SubagentStop Hook,允许父代理或 Hook 系统检查子代理的输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 { event : 'SubagentStop' , data : { agentId : string , agentType : 'agent' | 'fork' | 'swarm_member' | 'task' , result : ToolResult , turnCount : number , stopReason : string , } } { blocking : string [], preventContinuation : boolean , }
Worktree 模式
Worktree 模式(feature: WORKTREE_MODE)允许代理在 Git worktree 中工作,实现真正的文件系统隔离:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Worktree 模式 │ ├── EnterWorktreeTool │ ├── git worktree add <path> <branch> │ ├── 切换工作目录到 worktree │ └── 所有后续文件操作在 worktree 中 │ ├── 工作流程 │ ├── 主代理在主工作目录 │ ├── 子代理在 worktree 中 │ ├── 互不干扰的文件修改 │ └── 通过 git merge 合并结果 │ └── ExitWorktreeTool ├── 切换回主工作目录 ├── 可选:合并 worktree 的修改 └── 可选:删除 worktree
Tmux 集成 :--worktree --tmux 快速路径在 cli.tsx 中处理——创建 worktree 并在新的 tmux 窗口中启动 Claude Code,实现真正的并行开发体验。
内置代理类型
源码中定义了多种内置代理类型(src/tools/AgentTool/built-in/),每种针对特定任务优化:
代理类型
用途
工具集
特殊行为
explore
代码探索和理解
Read, Glob, Grep, Bash(只读)
只读模式,不修改文件
plan
任务规划和分析
Read, Glob, Grep
生成结构化计划,不执行
verification
代码验证和测试
Read, Bash, Grep
运行测试、lint、类型检查
general-purpose
通用子任务
继承父代理工具集
默认类型,最灵活
claude-code-guide
Claude Code 使用指导
Read
回答关于 Claude Code 本身的问题
每种内置代理都有专门的 system prompt 补充,指导模型在特定角色下的行为。例如 verification 代理的 prompt 强调"运行所有相关测试"、“检查类型错误”、“不要修复问题,只报告发现”。explore 代理的 prompt 强调"广泛搜索"、“理解代码结构”、“不要修改任何文件”。
代理选择逻辑 :主代理在调用 AgentTool 时,通过 agentType 参数指定子代理类型。如果未指定,默认使用 general-purpose。模型会根据任务性质自主选择合适的代理类型——例如用户要求"帮我理解这个模块的架构"时,模型倾向于选择 explore 类型。
协调器模式(Coordinator Mode)
src/coordinator/coordinatorMode.ts(18.93KB)实现了协调器模式——一种更高级的多代理编排方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 协调器模式工作流程 │ ├── 角色 │ ├── Coordinator(协调器):主代理,负责任务分配和结果汇总 │ └── Workers(工作者):子代理,负责执行具体任务 │ ├── 工作流程 │ ├── 1. 用户提交任务给 Coordinator │ ├── 2. Coordinator 分析任务,拆分为子任务 │ ├── 3. Coordinator 为每个子任务创建 Worker │ │ ├── 选择合适的代理类型 │ │ ├── 分配工具权限 │ │ └── 提供任务描述和上下文 │ ├── 4. Workers 并行执行子任务 │ │ ├── 通过 SendMessageTool 向 Coordinator 报告进度 │ │ └── 完成后返回结果 │ ├── 5. Coordinator 汇总所有 Worker 结果 │ └── 6. Coordinator 生成最终响应给用户 │ ├── Idle Cycle(空闲循环) │ ├── Coordinator 在等待 Workers 时进入空闲循环 │ ├── 定期检查 Workers 的状态 │ ├── 处理 Workers 的权限请求 │ └── 响应用户的中断信号 │ ├── Auto-Claim(自动认领) │ ├── 当新任务到达时,Coordinator 自动认领 │ ├── 无需用户手动分配 │ └── 基于任务优先级和 Worker 可用性决策 │ └── 权限代理 ├── awaitAutomatedChecksBeforeDialog = true ├── Workers 的权限请求先经过 Coordinator 的自动化检查 │ ├── classifier 判断 │ └── hooks 检查 └── 只有未通过自动化检查的请求才提交给用户
远程代理任务
src/tasks/RemoteAgentTask/RemoteAgentTask.tsx(124.26KB)和 src/tasks/LocalAgentTask/LocalAgentTask.tsx(81.63KB)实现了远程和本地代理任务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 代理任务类型 │ ├── LocalAgentTask(本地代理任务) │ ├── 在本地进程中执行 │ ├── 共享本地文件系统 │ ├── 可以使用所有本地工具 │ └── 适用于需要文件系统访问的任务 │ ├── RemoteAgentTask(远程代理任务) │ ├── 在远程环境中执行(BYOC、Self-Hosted Runner) │ ├── 通过 API 通信 │ ├── 独立的文件系统和环境 │ ├── 适用于 CI/CD 集成和云端执行 │ └── 支持 GitHub Action 集成 │ └── InProcessTeammateTask(进程内队友任务) ├── 在同一进程中执行 ├── 共享内存空间 ├── 最低开销 └── 适用于轻量级协作
Swarm 模式详细
Swarm 模式(src/utils/swarm/)实现了 Lead Agent + Teammates 的协作模式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Swarm 模式架构 │ ├── Lead Agent(领导代理) │ ├── 接收用户任务 │ ├── 分析任务并创建团队 │ ├── 分配角色和职责 │ ├── 监控团队进度 │ └── 汇总最终结果 │ ├── Teammates(队友代理) │ ├── 接收 Lead Agent 分配的子任务 │ ├── 独立执行(独立进程、独立上下文) │ ├── 通过 SendMessageTool 与其他队友通信 │ ├── 通过 TaskBoard 共享任务状态 │ └── 完成后向 Lead Agent 报告 │ ├── TaskBoard(任务板) │ ├── 共享的任务状态存储 │ ├── 每个任务有状态(pending/in-progress/done/blocked) │ ├── 队友可以认领未分配的任务 │ └── 支持任务依赖关系 │ ├── Mailbox(邮箱) │ ├── 每个代理有独立的消息邮箱 │ ├── SendMessageTool 将消息投递到目标邮箱 │ ├── 代理在循环中轮询自己的邮箱 │ └── 支持广播消息(发送给所有队友) │ └── 生命周期 ├── TeamCreateTool → 创建团队 + 生成代理进程 ├── 执行阶段 → 代理并行工作 + 消息通信 ├── 汇总阶段 → Lead Agent 收集所有结果 └── TeamDeleteTool → 终止所有进程 + 清理资源
代理间的权限传播
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 权限传播规则 │ ├── AgentTool 子代理 │ ├── 继承父代理的权限模式 │ ├── 继承父代理的规则集 │ └── 会话级权限决策不继承(每个子代理独立) │ ├── Fork 子代理 │ ├── 继承创建时的权限快照 │ ├── 运行期间的权限变更不同步 │ └── shouldAvoidPermissionPrompts 可设置 │ ├── Swarm 成员 │ ├── 继承团队创建时的权限配置 │ ├── 协调器可以代理权限决策 │ └── shouldAvoidPermissionPrompts = true(默认) │ └── Task ├── 继承创建时的权限配置 ├── 后台执行时无法交互式确认 └── shouldAvoidPermissionPrompts = true
八、MCP 协议集成
Claude Code 实现了完整的 MCP(Model Context Protocol)客户端,支持五种传输层、OAuth 2.0 认证、会话管理和工具注册。同时,Claude Code 自身也可以作为 MCP 服务器暴露给其他客户端。
MCP 架构总览
五种传输层
传输层
实现
通信方式
适用场景
stdio
StdioClientTransport
子进程 stdin/stdout
本地工具(最常用)
SSE
SSEClientTransport
HTTP Server-Sent Events
远程服务器
Streamable HTTP
StreamableHTTPClientTransport
HTTP 双向流
新一代远程服务器
Named Pipe
自定义实现
Windows Named Pipe
Windows 本地 IPC
Unix Socket
自定义实现
Unix Domain Socket
Unix 本地 IPC
传输层选择逻辑 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 传输层选择 │ ├── 配置中指定 transport? │ ├── "stdio" → StdioClientTransport │ ├── "sse" → SSEClientTransport │ └── "streamable-http" → StreamableHTTPClientTransport │ ├── 配置中有 command? │ └── → StdioClientTransport(子进程模式) │ ├── 配置中有 url? │ ├── 尝试 Streamable HTTP │ ├── 失败 → 降级到 SSE │ └── SSE 也失败 → 报错 │ └── 配置中有 pipeName / socketPath? └── → Named Pipe / Unix Socket
MCP 配置来源
MCP 服务器配置从多个来源加载,按优先级合并:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 MCP 配置来源(优先级高 → 低) │ ├── 1. CLI 参数 │ └── --mcp-config <path> │ ├── 2. 项目级配置 │ └── .mcp.json(项目根目录) │ ├── 3. 用户级配置 │ └── ~/.claude/mcp_servers.json │ ├── 4. 设置文件中的 mcpServers │ ├── settings.json(各层级) │ └── managed settings(企业) │ ├── 5. claude.ai 代理服务器 │ └── fetchClaudeAIMcpConfigsIfEligible() │ └── 需要 OAuth 认证 + 网络请求 │ └── 6. 企业策略过滤 └── filterMcpServersByPolicy() └── 可以禁止特定服务器
配置格式 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 { "mcpServers" : { "github" : { "command" : "npx" , "args" : [ "-y" , "@modelcontextprotocol/server-github" ] , "env" : { "GITHUB_TOKEN" : "ghp_..." } } , "remote-api" : { "url" : "https://api.example.com/mcp" , "transport" : "streamable-http" , "headers" : { "Authorization" : "Bearer ..." } } } }
连接生命周期
Session Expired 处理
MCP 协议中的 Session Expired 错误需要特殊处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Session Expired 恢复流程 │ ├── 检测到 Session Expired 错误 │ ├── 1. 关闭当前连接 │ └── 清理所有待处理的请求 │ ├── 2. 重新建立连接 │ └── 使用相同的配置参数 │ ├── 3. 重新初始化 │ ├── initialize() 协议握手 │ ├── 交换能力 │ └── 重新发现工具 │ ├── 4. 重新注册工具 │ ├── 检查工具列表是否变化 │ ├── 新增工具 → 添加到工具池 │ ├── 移除工具 → 从工具池删除 │ └── 变更工具 → 更新 schema │ └── 5. 重试失败的请求 └── 使用新的 session ID
工具注册与命名
MCP 工具在 Claude Code 中的命名遵循严格的约定:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 MCP 工具命名约定 │ ├── 格式: mcp__{serverName}__{toolName} │ ├── 双下划线分隔 │ ├── serverName: 配置中的服务器名称 │ └── toolName: 服务器报告的工具名称 │ ├── 示例 │ ├── mcp__github__create_issue │ ├── mcp__filesystem__read_file │ └── mcp__sentry__search_issues │ ├── 权限规则匹配 │ ├── 精确匹配: "mcp__github__create_issue" │ ├── 服务器前缀: "mcp__github" → 匹配该服务器所有工具 │ └── 通配符: "mcp__*__read_*" → 匹配所有服务器的 read 工具 │ └── 名称冲突处理 ├── 内置工具优先(uniqBy('name')) └── MCP 工具之间:先注册的优先
src/tools/MCPTool/MCPTool.ts 将 MCP 工具包装为 Claude Code 的 Tool 接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 MCPTool 包装逻辑 │ ├── inputSchema │ ├── 从 MCP 工具的 JSON Schema 转换为 Zod schema │ └── 支持 $ref 解引用 │ ├── call() │ ├── 序列化输入为 JSON │ ├── 调用 MCP tools/call │ ├── 反序列化结果 │ └── 处理错误(超时、连接断开等) │ ├── prompt() │ ├── 使用 MCP 工具的 description │ ├── 添加服务器级别的指令(getMcpInstructionsSection) │ └── 添加 _meta['anthropic/instructions'] 中的指令 │ ├── isConcurrencySafe() │ └── MCP 工具默认 true(假设服务器处理并发) │ ├── isReadOnly() │ └── 从 _meta['anthropic/readOnly'] 读取 │ ├── shouldDefer │ └── 从 _meta['anthropic/shouldDefer'] 读取 │ ├── alwaysLoad │ └── 从 _meta['anthropic/alwaysLoad'] 读取 │ └── maxResultSizeChars └── 100_000(MCP 工具统一限制)
MCP 资源系统
除了工具,MCP 还支持资源(Resources)和资源模板(Resource Templates):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 MCP 资源系统 │ ├── ListMcpResourcesTool │ ├── 列出服务器提供的所有资源 │ ├── 资源 URI + 描述 │ └── 支持分页 │ ├── ReadMcpResourceTool │ ├── 读取特定资源的内容 │ ├── 支持 URI 模板参数 │ └── 结果作为工具结果返回 │ ├── 资源预取 │ ├── prefetchAllMcpResources() │ ├── 在启动时预取所有资源列表 │ └── 缓存资源元数据 │ └── 资源类型 ├── text/* → 文本内容 ├── application/json → JSON 数据 └── image/* → Base64 编码图片
OAuth 2.0 认证流程
远程 MCP 服务器支持 OAuth 2.0 认证:
PKCE(Proof Key for Code Exchange) :所有 OAuth 流程都使用 PKCE 扩展,防止授权码拦截攻击。code_verifier 是一个随机字符串,code_challenge 是其 SHA-256 哈希。
Token 存储 :OAuth token 存储在 ~/.claude/mcp_tokens.json 中,按服务器 URL 索引。Token 包含 access_token、refresh_token、expires_at 等字段。
Elicitation 机制
MCP 服务器可以通过 Elicitation 请求用户输入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Elicitation 流程 │ ├── MCP 服务器发送 elicitation 请求 │ ├── 请求类型(text、select、confirm) │ ├── 提示信息 │ └── 可选的默认值 │ ├── Claude Code 处理 │ ├── 检查 Elicitation Hook │ │ └── Hook 可以自动回答(无需用户交互) │ ├── 无 Hook → 显示 UI 提示 │ │ ├── Terminal UI │ │ ├── Bridge UI │ │ └── Channel UI │ └── 收集用户输入 │ └── 返回结果给 MCP 服务器 └── 用户的输入值
fetchClaudeAIMcpConfigsIfEligible() 从 claude.ai 获取 MCP 服务器配置——这些是 Anthropic 托管的 MCP 代理服务器,通过 SSE 连接:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 claude.ai MCP 代理 │ ├── 资格检查 │ ├── 需要 OAuth 认证(有用户上下文) │ ├── 需要 feature gate 启用 │ └── CLAUDE_CODE_SIMPLE !== '1'(bare 模式禁用) │ ├── 配置获取 │ ├── GET /api/mcp-configs │ ├── 返回可用的代理服务器列表 │ └── 每个服务器包含 URL + 认证信息 │ ├── 连接 │ ├── 使用 SSE 传输层 │ ├── 附加 OAuth token 作为认证 │ └── 连接超时:6-14 秒/服务器 │ └── 性能影响 ├── 每个代理服务器 6-14 秒连接时间 ├── bare 模式跳过(节省启动时间) └── 连接失败不阻塞启动
MCP 服务器模式
Claude Code 自身也可以作为 MCP 服务器运行(src/entrypoints/mcp.ts):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Claude Code 作为 MCP 服务器 │ ├── 启动方式 │ └── claude mcp serve │ ├── 暴露的工具 │ ├── 所有内置工具 │ ├── 所有已连接的 MCP 工具(代理转发) │ └── 自定义工具(通过配置) │ ├── 传输层 │ ├── stdio(默认) │ └── SSE(通过 --port 参数) │ └── 使用场景 ├── Claude Desktop 集成 ├── 其他 MCP 客户端调用 Claude Code 的能力 └── 工具链组合(Claude Code 作为中间层)
Chrome 扩展 MCP
--claude-in-chrome-mcp 启动 Chrome 扩展专用的 MCP 服务器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Chrome MCP 服务器 │ ├── 通信方式 │ └── Chrome Native Messaging Host │ └── --chrome-native-host 参数 │ ├── 功能 │ ├── 浏览器页面内容读取 │ ├── 浏览器操作执行 │ └── 与 Claude Code 主进程通信 │ └── 安全 ├── 只允许来自 Chrome 扩展的连接 └── 受 Chrome 扩展权限模型保护
MCP 指令注入
getMcpInstructionsSection() 将 MCP 服务器的指令注入到 system prompt 中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 `## MCP Server Instructions ### ${serverName} ${serverInstructions} ### Tool: ${toolName} ${toolInstructions} `
这些指令位于 system prompt 的动态区域,不影响静态区域的 Prompt Cache。
XAA 跨应用访问
src/services/mcp/xaa.ts(18.36KB)和 src/services/mcp/xaaIdpLogin.ts(16.37KB)实现了跨应用访问(Cross-Application Access)机制——允许 Claude Code 代表用户访问其他 Anthropic 应用的 MCP 服务器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 XAA 跨应用访问流程 │ ├── 场景 │ ├── Claude Code 需要访问 claude.ai 的 MCP 代理服务器 │ ├── 这些服务器需要验证用户身份 │ └── 用户已经在 Claude Code 中通过 OAuth 认证 │ ├── 认证流程 │ ├── 1. 检查本地是否有有效的 XAA token │ ├── 2. 如果没有 → 发起 XAA IDP 登录 │ │ ├── 使用 Claude Code 的 OAuth token 作为身份证明 │ │ ├── 向 Anthropic IDP 请求 XAA token │ │ └── XAA token 包含跨应用访问权限 │ ├── 3. 使用 XAA token 连接 MCP 代理服务器 │ └── 4. 缓存 XAA token(有效期内复用) │ ├── 安全模型 │ ├── XAA token 的权限范围受限于用户的 OAuth scope │ ├── 每个 MCP 服务器有独立的 XAA token │ ├── Token 过期后自动刷新 │ └── 用户可以随时撤销 XAA 授权 │ └── 与 McpAuthTool 的关系 ├── McpAuthTool 处理标准的 MCP OAuth 认证 └── XAA 处理 Anthropic 生态内的跨应用认证
src/tools/McpAuthTool/McpAuthTool.ts(7.90KB)提供了 MCP 服务器认证的交互式工具——当 MCP 服务器需要认证但自动认证失败时,模型可以调用此工具引导用户完成认证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 McpAuthTool 工作流程 │ ├── 触发条件 │ ├── MCP 服务器返回 401/403 错误 │ ├── 或连接时要求认证但无有效 token │ └── 自动认证(XAA、缓存 token)失败 │ ├── 执行流程 │ ├── 1. 识别服务器的认证方式 │ │ ├── OAuth 2.0 → 启动 OAuth 流程 │ │ ├── API Key → 提示用户输入 │ │ └── Bearer Token → 提示用户输入 │ ├── 2. 引导用户完成认证 │ │ ├── 打开浏览器(OAuth) │ │ ├── 或显示输入提示(API Key) │ │ └── 等待用户完成 │ ├── 3. 存储认证信息 │ │ └── ~/.claude/mcp_tokens.json │ └── 4. 重新连接 MCP 服务器 │ └── 与权限系统的交互 ├── McpAuthTool 需要用户确认(ask 权限) └── 防止恶意 MCP 服务器自动触发认证流程
MCP 连接的健康检查
MCP 连接管理器(src/services/mcp/MCPConnectionManager.tsx,8.06KB)实现了连接的健康检查和自动恢复:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 连接健康检查 │ ├── 心跳检测 │ ├── 定期发送 ping 请求 │ ├── 超时未响应 → 标记为不健康 │ └── 连续 N 次不健康 → 触发重连 │ ├── 工具列表刷新 │ ├── 定期调用 tools/list │ ├── 检测工具变化(新增/移除/修改) │ ├── 变化 → 更新 assembleToolPool() │ └── 通知 UI 更新工具列表 │ ├── 错误恢复策略 │ ├── 网络错误 → 指数退避重试(1s, 2s, 4s, 8s, 16s) │ ├── 服务器崩溃 → 重启子进程(stdio 传输层) │ ├── Session Expired → 重新初始化 │ └── 认证过期 → 刷新 token 后重连 │ └── 连接池管理 ├── 每个 MCP 服务器一个连接 ├── 连接复用(同一服务器的多个工具共享连接) ├── 空闲连接超时关闭(节省资源) └── 最大连接数限制(防止资源耗尽)
九、遥测、安全与隐藏功能
Claude Code 内置了完整的遥测系统、A/B 测试框架、PII 隔离机制,以及多个未公开的隐藏功能。这些系统共同支撑了产品的数据驱动迭代和安全合规。
双层遥测管道
遥测系统分为两层——第一方事件日志 (1P Event Logging)和第三方分析 (Sentry/Statsig),各自有不同的数据范围和隐私级别。
1P Event Logging
src/services/firstPartyEventLogger.ts 实现了第一方事件日志系统:
初始化流程 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 initialize1PEventLogging() │ ├── 延迟加载 OpenTelemetry SDK │ └── Promise.all([import('sdk-logs'), import('growthbook')]) │ ├── 创建 LoggerProvider │ ├── BatchLogRecordExporter │ │ ├── 批量大小: 50 条 │ │ ├── 发送间隔: 5 秒 │ │ └── 队列上限: 1000 条 │ └── 目标端点: Anthropic 遥测 API │ ├── 注册 GrowthBook 刷新回调 │ └── onGrowthBookRefresh() → reinitialize1PEventLoggingIfConfigChanged() │ └── 配置变更时重新初始化(如端点 URL 变更) │ └── 注册清理函数 └── registerCleanup(() => loggerProvider.shutdown())
事件类型 :
事件
触发时机
包含数据
tengu_timer
启动完成
启动耗时、各阶段时间戳
tool_use
工具调用
工具名称、耗时、成功/失败
api_call
API 请求
模型、token 数、延迟
permission_decision
权限决策
工具、决策、来源
compact
压缩触发
压缩前后 token 数
session_end
会话结束
总轮次、总 token、总耗时
error
错误发生
错误类型、堆栈(脱敏)
PII 隔离:PROTO * 字段
遥测数据中的 PII(个人可识别信息)通过 _PROTO_* 前缀字段隔离:
1 2 3 4 5 6 7 8 9 10 11 12 { event : 'tool_use' , tool_name : 'BashTool' , duration_ms : 1234 , success : true , _PROTO_command : 'git commit -m "fix: user login"' , _PROTO_file_path : '/home/user/project/auth.ts' , _PROTO_error_message : 'Connection refused to db.internal.company.com' , }
隔离机制 :
_PROTO_* 字段在传输前被分离到独立的数据流
非 PII 字段进入标准分析管道(可聚合、可查询)
PII 字段进入受限管道(更短的保留期、更严格的访问控制)
客户端可以通过设置完全禁用 PII 字段的发送
GrowthBook A/B 测试
src/services/growthbook.ts 集成了 GrowthBook SDK,用于 feature flag 和 A/B 测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 GrowthBook 集成 │ ├── 初始化 │ ├── 加载 GrowthBook SDK │ ├── 设置用户属性(user ID、组织、计划等) │ ├── 获取 feature flag 配置 │ └── 注册刷新回调 │ ├── Feature Flag 评估 │ ├── feature('FLAG_NAME') → boolean │ ├── 编译时 DCE(Dead Code Elimination) │ │ └── 未启用的 feature 代码在构建时被移除 │ └── 运行时评估(基于用户属性) │ ├── A/B 测试 │ ├── 基于用户 ID 的确定性分桶 │ ├── 支持多变体实验 │ └── 曝光事件自动记录 │ └── 刷新机制 ├── 定期轮询(默认 5 分钟) ├── 配置变更 → 触发回调 └── 回调可以重新初始化依赖系统
Feature Flag 的编译时门控 :
1 2 3 4 5 6 if (feature ('AGENT_SWARMS' )) { tools.push (TeamCreateTool , TeamDeleteTool ) }
已知的 Feature Flag 清单 (部分):
Flag
功能
状态
AGENT_SWARMS
多代理团队协作
实验性
BG_SESSIONS
后台会话管理
实验性
BRIDGE_MODE
Bridge 远程控制
实验性
CONTEXT_COLLAPSE
上下文折叠
渐进推出
HISTORY_SNIP
历史裁剪
渐进推出
TOOL_SEARCH
工具搜索
渐进推出
TASKS
后台任务系统
实验性
WORKTREE_MODE
Git Worktree 模式
实验性
DAEMON
守护进程模式
实验性
KAIROS
定时任务/推送通知
实验性
TEMPLATES
模板任务
实验性
BYOC_ENVIRONMENT_RUNNER
BYOC 环境运行器
实验性
SELF_HOSTED_RUNNER
自托管运行器
实验性
AUTO_MEMORY
自动记忆提取
渐进推出
BASH_CLASSIFIER
Bash 命令分类器
渐进推出
TRANSCRIPT_CLASSIFIER
对话分类器
渐进推出
ABLATION_BASELINE
L0 消融基线
Ant-only
CHICAGO_MCP
Computer Use MCP
实验性
Sentry 错误追踪
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 Sentry 集成 │ ├── 初始化 │ ├── initializeTelemetryAfterTrust() │ │ └── 必须在 Trust Dialog 之后(用户同意遥测) │ ├── 配置 DSN、环境、版本 │ └── 设置采样率 │ ├── 错误捕获 │ ├── 未捕获异常 → 自动上报 │ ├── 未处理的 Promise rejection → 自动上报 │ ├── 手动 captureException() → 主动上报 │ └── 面包屑(breadcrumbs)→ 上下文追踪 │ ├── 数据脱敏 │ ├── 文件路径 → 相对路径 │ ├── 用户名 → 哈希 │ ├── API Key → 移除 │ └── 自定义 beforeSend 过滤器 │ └── 性能监控 ├── Transaction 追踪 ├── API 调用延迟 └── 工具执行耗时
Undercover 模式
Undercover 模式是一个隐藏功能——当启用时,Claude Code 伪装为其他 AI 编码助手:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Undercover 模式 │ ├── 触发方式 │ └── 环境变量或内部配置(未公开) │ ├── 行为变化 │ ├── System Prompt 中移除 "Claude" 相关引用 │ ├── 替换为通用的 "AI Assistant" 描述 │ ├── 移除 Anthropic 特定的指令 │ └── 调整语气和风格 │ ├── 用途 │ ├── 竞品分析和基准测试 │ ├── 避免模型在输出中自我引用 │ └── 第三方集成中的白标需求 │ └── 实现 ├── getSimpleIntroSection() 中的条件分支 ├── 检查 undercover 配置 └── 替换身份声明文本
CYBER_RISK_INSTRUCTION
System Prompt 中包含一段安全指令 CYBER_RISK_INSTRUCTION,用于防止模型被利用进行恶意操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 CYBER_RISK_INSTRUCTION 内容(概要) │ ├── 禁止生成恶意代码 │ ├── 恶意软件、病毒、蠕虫 │ ├── 漏洞利用代码 │ └── 社会工程攻击脚本 │ ├── 禁止协助攻击 │ ├── 网络入侵 │ ├── 数据窃取 │ └── 权限提升 │ ├── 安全编码指导 │ ├── 输入验证 │ ├── 输出编码 │ └── 安全默认值 │ └── 注入位置 └── getSimpleIntroSection() 的身份声明之后 └── 位于 system prompt 的最前面(最高优先级)
性能追踪系统
profileCheckpoint() :
1 2 3 4 5 6 7 8 9 profileCheckpoint ('cli_entry' )profileCheckpoint ('init_function_start' )profileCheckpoint ('init_configs_enabled' )profileCheckpoint ('cli_after_main_complete' )
Event Loop Stall Detector :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 事件循环阻塞检测 │ ├── 原理 │ ├── 注册一个 setInterval(间隔 500ms) │ ├── 记录每次回调的实际时间 │ └── 如果实际间隔 > 预期间隔 + 阈值 → 记录阻塞 │ ├── 阈值 │ └── 500ms(主线程被阻塞超过 500ms 时触发) │ ├── 记录内容 │ ├── 阻塞持续时间 │ ├── 阻塞发生时的调用栈(如果可用) │ └── 当前正在执行的操作 │ └── 用途 ├── 诊断启动卡顿 ├── 识别同步 I/O 操作 └── 发现 CPU 密集型计算
Prompt Cache 四层优化
Prompt Cache 是 Claude Code 最重要的性能优化之一——通过缓存 system prompt 和工具 schema,避免每次 API 调用都重新处理这些内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 Prompt Cache 四层优化 │ ├── Layer 1: System Prompt 静态/动态分区 │ ├── 静态部分(SYSTEM_PROMPT_DYNAMIC_BOUNDARY 之前) │ │ └── 跨用户、跨会话可缓存 │ ├── 动态部分(BOUNDARY 之后) │ │ └── 会话级缓存 │ └── 分区保证静态部分的 cache key 稳定 │ ├── Layer 2: 工具 Schema 排序稳定性 │ ├── 内置工具按名称排序(前缀) │ ├── MCP 工具按名称排序(后缀) │ └── 增减 MCP 工具不影响内置工具的位置 │ ├── Layer 3: cache_control 标记 │ ├── ephemeral breakpoint 标记 │ ├── 告诉 API 哪些内容可以缓存 │ └── normalizeMessages() 中注入 │ └── Layer 4: 子代理 Prompt Cache 共享 ├── AgentTool 子代理继承主代理的静态 prompt ├── 子代理首次调用命中主代理的缓存 └── 节省约 15% 的 token 成本
内存管理
Claude Code 作为长时间运行的 Node.js 进程,内存管理至关重要:
已知的内存泄漏场景 :
场景
原因
缓解措施
消息历史无限增长
长会话积累大量消息
autoCompact 定期压缩
工具结果未释放
大文件内容保留在内存
applyToolResultBudget 持久化到磁盘
MCP 连接泄漏
服务器崩溃后连接未清理
Session Expired 检测 + 重连
Sentry 面包屑
面包屑队列无限增长
设置 maxBreadcrumbs 上限
GrowthBook 缓存
Feature flag 缓存未过期
定期刷新 + 内存上限
子进程句柄
Fork 子进程未正确终止
registerCleanup + SIGKILL 兜底
CCR 容器环境 :CLAUDE_CODE_REMOTE=true 时设置 --max-old-space-size=8192(8GB),防止容器 OOM。
优雅退出
setupGracefulShutdown() 注册了多层退出处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 优雅退出流程 │ ├── 触发信号 │ ├── SIGINT(Ctrl+C) │ ├── SIGTERM(进程管理器) │ └── SIGHUP(终端关闭) │ ├── 退出序列 │ ├── 1. 执行 SessionEnd Hook │ ├── 2. 停止所有后台任务 │ ├── 3. 关闭 MCP 连接 │ ├── 4. 关闭 LSP 服务器 │ ├── 5. 清理 Swarm 团队 │ ├── 6. 刷新遥测缓冲区 │ ├── 7. 关闭 Sentry │ └── 8. 清理临时文件(Scratchpad) │ ├── 超时保护 │ ├── 每个清理步骤有独立超时 │ ├── 总超时 10 秒 │ └── 超时后强制退出(process.exit(1)) │ └── registerCleanup() 注册的清理函数 ├── shutdownLspServerManager ├── cleanupSessionTeams ├── loggerProvider.shutdown() └── 自定义清理函数
安全审计日志
所有安全相关的操作都记录审计日志:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 审计日志记录的事件 │ ├── 权限决策 │ ├── 工具名称、输入摘要 │ ├── 决策结果(allow/deny/ask) │ ├── 决策来源(rule/classifier/user/hook) │ └── 规则匹配详情 │ ├── Hook 执行 │ ├── Hook 类型(command/prompt/http) │ ├── Hook 来源(managed/user/project) │ ├── 执行结果 │ └── 执行耗时 │ ├── 认证事件 │ ├── OAuth 登录/刷新/失败 │ ├── API Key 使用 │ └── Token 过期 │ ├── 配置变更 │ ├── 设置文件修改 │ ├── 权限规则变更 │ └── MCP 服务器增减 │ └── 存储位置 ├── 1P Event Logging(远程) ├── 本地日志文件(~/.claude/logs/) └── Sentry(错误相关)
企业管控能力
企业管理员通过 managed settings 和策略控制 Claude Code 的行为:
管控项
配置路径
说明
工具权限
managed.alwaysDeny
禁止特定工具
MCP 服务器
managed.mcpServers
强制/禁止特定服务器
Hook
managed.hooks
强制执行的 Hook
模型
managed.model
强制使用特定模型
遥测
managed.telemetry
控制遥测数据范围
远程控制
managed.allow_remote_control
允许/禁止 Bridge 模式
权限模式
managed.permissionMode
强制权限模式
网络
managed.proxy
强制代理配置
策略限制 :initializePolicyLimitsLoadingPromise() 在启动时加载企业策略限制——这些限制不能被用户或项目设置覆盖。
隐藏的调试功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 隐藏调试功能 │ ├── --dump-system-prompt │ ├── 输出完整的渲染后 system prompt │ ├── Ant-only(需要内部认证) │ └── 用于调试 prompt 组装问题 │ ├── CLAUDE_CODE_DEBUG=1 │ ├── 启用详细日志输出 │ ├── 显示 API 请求/响应详情 │ └── 显示工具执行详情 │ ├── CLAUDE_CODE_ABLATION_BASELINE │ ├── L0 消融基线模式 │ ├── 禁用所有高级功能 │ ├── 用于 A/B 测试基线对比 │ └── Ant-only │ ├── profileCheckpoint 输出 │ ├── 启动性能详细时间线 │ └── 通过遥测事件上报 │ └── Event Loop Stall 日志 ├── 主线程阻塞检测 └── 输出到本地日志
Datadog 集成
src/services/analytics/datadog.ts(9.19KB)实现了 Datadog APM 集成,提供更细粒度的性能监控:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 Datadog 集成 │ ├── 指标类型 │ ├── 计数器(Counter) │ │ ├── api_calls_total // API 调用总数 │ │ ├── tool_executions_total // 工具执行总数 │ │ ├── permission_decisions // 权限决策次数 │ │ └── compact_triggers // 压缩触发次数 │ │ │ ├── 直方图(Histogram) │ │ ├── api_latency_ms // API 延迟分布 │ │ ├── tool_execution_ms // 工具执行时间分布 │ │ ├── startup_time_ms // 启动时间分布 │ │ └── compact_duration_ms // 压缩耗时分布 │ │ │ └── 仪表盘(Gauge) │ ├── context_tokens_used // 当前上下文 token 使用量 │ ├── active_mcp_connections // 活跃 MCP 连接数 │ └── memory_entries_count // 记忆条目数 │ ├── 标签(Tags) │ ├── model: claude-opus-4-6 │ ├── entrypoint: cli / sdk / mcp │ ├── user_type: ant / external │ └── permission_mode: default / auto / bypass │ └── 与 1P Event Logging 的关系 ├── 1P 记录产品事件(用户行为分析) └── Datadog 记录性能指标(运维监控)
环境指纹收集
src/services/analytics/metadata.ts(32.80KB)收集详细的环境元数据,用于遥测分析和问题诊断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 环境指纹收集的字段 │ ├── 平台信息 │ ├── os: 'darwin' | 'linux' | 'win32' │ ├── arch: 'x64' | 'arm64' │ ├── platform_version: '14.2.1'(macOS 版本) │ ├── kernel_version: '6.5.0-44-generic'(Linux 内核) │ └── is_wsl: boolean(Windows Subsystem for Linux) │ ├── 终端信息 │ ├── terminal: 'iTerm2' | 'Terminal.app' | 'Windows Terminal' | ... │ ├── shell: 'zsh' | 'bash' | 'fish' | 'powershell' │ ├── term_program: $TERM_PROGRAM │ ├── term_colors: $COLORTERM │ └── columns / rows(终端尺寸) │ ├── IDE 信息 │ ├── ide: 'vscode' | 'jetbrains' | 'cursor' | 'windsurf' | ... │ ├── ide_version: '1.85.0' │ └── is_remote: boolean(SSH / Remote Desktop) │ ├── CI/CD 信息 │ ├── is_ci: boolean │ ├── ci_provider: 'github-actions' | 'gitlab-ci' | 'jenkins' | ... │ ├── ci_job_id: string │ └── ci_branch: string │ ├── 运行时信息 │ ├── node_version: '20.10.0' │ ├── bun_version: '1.1.0'(如果使用 Bun) │ ├── claude_code_version: '2.1.88' │ └── npm_version: '10.2.3' │ ├── 项目信息 │ ├── git_remote_url_hash: SHA-256(隐私保护) │ ├── file_count: 1234(项目文件数) │ ├── has_package_json: boolean │ ├── has_tsconfig: boolean │ └── primary_language: 'typescript' | 'python' | ... │ └── 用户信息 ├── user_id_hash: SHA-256(隐私保护) ├── organization_id: string(OAuth 用户) ├── plan: 'free' | 'pro' | 'team' | 'enterprise' └── account_age_days: number
隐私保护 :所有可能包含 PII 的字段都经过哈希处理——git_remote_url_hash 使用 SHA-256 哈希仓库 URL,user_id_hash 哈希用户 ID。这允许聚合分析(“有多少不同的仓库使用了 Claude Code”)而不暴露具体的仓库 URL。
模型代号体系
源码中使用动物代号指代不同的模型系列:
代号
模型
说明
Capybara
Claude Sonnet 系列
主力模型,平衡性能和成本
Tengu
Claude Opus 系列
最强模型,用于复杂任务
Fennec
Claude Haiku 系列
轻量模型,用于分类器和摘要
Numbat
下一代模型
开发中,源码中有占位符
模型 ID 映射 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const MODEL_IDS = { opus : 'claude-opus-4-6' , sonnet : 'claude-sonnet-4-6' , haiku : 'claude-haiku-4-5-20251001' , } const FRONTIER_MODEL_NAME = 'Claude Opus 4.6' function getKnowledgeCutoff (model: string ): string { if (model.includes ('opus-4-6' )) return 'May 2025' if (model.includes ('sonnet-4-6' )) return 'August 2025' if (model.includes ('haiku-4-5' )) return 'Early 2025' return 'Early 2025' }
@[MODEL LAUNCH] 标记 :源码中有 @[MODEL LAUNCH] 注释标记需要在新模型发布时更新的位置——包括模型 ID、知识截止日期、默认参数等。
模型迁移脚本
src/migrations/ 目录包含模型迁移脚本,用于在模型更新时自动迁移用户配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 模型迁移脚本 │ ├── 迁移场景 │ ├── 模型 ID 变更(如 claude-3-opus → claude-opus-4) │ ├── 默认模型切换(如 Sonnet 4.5 → Sonnet 4.6) │ ├── 模型参数变更(如 max_tokens 默认值变化) │ └── 模型能力变更(如新增 thinking 支持) │ ├── 迁移流程 │ ├── 1. 检查用户配置中的模型设置 │ ├── 2. 如果使用了旧模型 ID → 自动替换为新 ID │ ├── 3. 如果使用了已废弃的参数 → 迁移到新参数 │ ├── 4. 记录迁移日志 │ └── 5. 通知用户(如果有破坏性变更) │ ├── 已知迁移 │ ├── Fennec → Haiku 4.5(模型代号到正式名称) │ ├── Sonnet 4.5 → Sonnet 4.6(默认模型升级) │ └── Opus 4.5 → Opus 4.6(默认模型升级) │ └── 迁移版本追踪 ├── ~/.claude/migration_version ├── 记录已执行的迁移版本 └── 防止重复执行
远程托管设置
src/services/remoteManagedSettings/index.ts(21.04KB)实现了远程托管设置——企业管理员可以通过 API 远程控制 Claude Code 的行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 远程托管设置 │ ├── 加载流程 │ ├── 启动时发起 API 请求获取远程设置 │ ├── 每小时轮询更新 │ ├── 设置缓存在本地(离线时使用缓存) │ └── 设置变更触发 settingsChangeDetector │ ├── 可控制的设置 │ ├── 工具权限(alwaysDeny、alwaysAsk) │ ├── MCP 服务器白名单/黑名单 │ ├── 模型限制(强制使用特定模型) │ ├── 功能开关(禁用特定功能) │ └── 安全策略(强制沙箱、禁止 bypass 模式) │ ├── Killswitch 机制 │ ├── 远程设置可以包含 killswitch 标志 │ ├── 激活时显示阻塞对话框 │ ├── 用户无法绕过(必须等待管理员解除) │ └── 用于紧急安全事件响应 │ └── 安全检查 ├── securityCheck.tsx(10.34KB) ├── 验证远程设置的签名 ├── 防止中间人篡改设置 └── 设置过期检测(过期的设置不生效)
Beta 功能管理
src/utils/betas.ts(15.74KB)管理 API 级别的 beta 功能:
1 2 3 4 5 6 7 8 9 10 11 12 const BETAS = { 'task-budgets-2026-03-13' : true , 'prompt-caching-2024-07-31' : true , 'max-tokens-3-5-sonnet-2024-07-15' : true , 'output-128k-2025-02-19' : true , 'interleaved-thinking-2025-05-14' : true , 'token-efficient-tools-2025-02-19' : true , }
未来路线图线索
源码中包含多个未来功能的线索:
线索
来源
推测
KAIROS
Feature flags、system prompt
完全自主代理模式(tick 心跳、推送通知、PR 订阅)
Numbat
模型代号映射
下一代模型(可能是 Claude 5 系列)
语音模式
useVoiceIntegration.tsx(97.79KB)
Push-to-talk 语音输入,WebSocket STT
WebBrowserTool
Feature flag、工具注册
浏览器自动化(代号 bagel)
Buddy 系统
源码中的类型定义
虚拟宠物伙伴(18 物种、5 稀有度)
Opus 4.7 / Sonnet 4.8
@[MODEL LAUNCH] 标记
下一代模型版本
Plugin 系统
src/plugins/(多文件)
第三方插件生态
Skills 系统
src/skills/(多文件)
可复用的技能模板