Claude Code 源码架构深度解析:1884 个文件背后的 AI 编程工具设计哲学

Claude Code 源码架构深度解析:1884 个文件背后的 AI 编程工具设计哲学

近日 Claude Code 源码意外泄漏,作为一个深度使用 Claude Code 的开发者,我对这份 33MB、1884 个 TypeScript 文件的代码库做了一次完整的架构分析。本文将从宏观到微观,拆解这个当前最强 AI 编程助手的内部设计。

一、项目概览

指标数值
文件总数1,884 个 (.ts/.tsx)
代码体积33 MB
核心入口文件main.tsx (4,683 行)
查询引擎QueryEngine.ts (1,295 行) + query.ts (1,729 行)
内置工具43 个独立模块
斜杠命令100+ 个
React Hooks85 个
服务模块35+ 个
技术栈TypeScript + React + Ink (终端 UI) + Zod + Bun

二、整体架构:五层设计

先上全局架构图,后面逐层拆解:

architecture_overview.svg

startup_flow.svg

query_dataflow.svg

三、入口层:毫秒级的启动优化

3.1 快速路径(Fast Path)

cli.tsx 是整个应用的入口,只有 302 行,但信息密度极高。

typescript
复制代码
// cli.tsx — 第一行就是 feature flag import { feature } from 'bun:bundle'; // 顶层副作用:环境准备 process.env.COREPACK_ENABLE_AUTO_PIN = '0'; // 防止 corepack 污染 package.json // CCR 远程环境设置 8GB 堆上限 if (process.env.CLAUDE_CODE_REMOTE === 'true') { process.env.NODE_OPTIONS = existing ? `${existing} --max-old-space-size=8192` : '--max-old-space-size=8192'; }

关键设计:所有 import 都是动态的--version 路径零模块加载:

typescript
复制代码
async function main(): Promise<void> { const args = process.argv.slice(2); // 快速路径 1: --version — 零 import,直接输出 if (args.length === 1 && (args[0] === '--version' || args[0] === '-v')) { console.log(`${MACRO.VERSION} (Claude Code)`); // 编译时内联 return; } // 快速路径 2: --dump-system-prompt (Ant-only, 编译时消除) if (feature('DUMP_SYSTEM_PROMPT') && args[0] === '--dump-system-prompt') { ... } // 快速路径 3: Chrome 原生宿主 if (process.argv[2] === '--chrome-native-host') { ... } // 快速路径 4: Daemon worker (内部 supervisor 用) if (feature('DAEMON') && args[0] === '--daemon-worker') { ... } // 快速路径 5: Bridge 远程控制 if (feature('BRIDGE_MODE') && (args[0] === 'remote-control' || args[0] === 'rc')) { ... } }

每个分支都有 profileCheckpoint() 调用,方便 Anthropic 内部做启动性能分析。

3.2 反调试机制

外部构建包含一个反调试检查,检测到 --inspect 或 Node Inspector 活跃时直接退出:

typescript
复制代码
// 外部构建检测调试器 if ("external" !== 'ant' && isBeingDebugged()) { process.exit(1); // 静默退出,无错误信息 }

这里 "external" 是编译时替换的字符串——Anthropic 内部构建替换为 'ant',外部构建保持 'external'。所以只有外部用户会被拦截,内部开发者不受影响。

3.3 main.tsx:4683 行的超级编排器

main.tsx 是我见过最复杂的单文件初始化逻辑。它的前 20 行就包含了三个并行的副作用:

typescript
复制代码
// 第 1 行:标记入口时间戳 import { profileCheckpoint } from './utils/startupProfiler.js'; profileCheckpoint('main_tsx_entry'); // 第 2 行:火速启动 MDM 子进程(macOS plutil / Windows reg query) import { startMdmRawRead } from './utils/settings/mdm/rawRead.js'; startMdmRawRead(); // 第 3 行:并行读取 macOS Keychain(OAuth token + 旧版 API key) import { startKeychainPrefetch } from './utils/secureStorage/keychainPrefetch.js'; startKeychainPrefetch();

这三个操作在模块加载阶段(import 副作用)就开始执行,比 main() 函数早 135ms 以上。这是因为后续 135ms 花在加载其余 100+ 个 import 上——这段时间内 MDM 和 Keychain 的子进程已经在后台跑完了。

3.4 迁移系统

main.tsx 包含一个版本化的迁移系统,当前版本号是 11:

typescript
复制代码
const CURRENT_MIGRATION_VERSION = 11; function runMigrations(): void { if (getGlobalConfig().migrationVersion !== CURRENT_MIGRATION_VERSION) { migrateAutoUpdatesToSettings(); migrateSonnet45ToSonnet46(); // 模型名升级 migrateOpusToOpus1m(); // Opus → Opus 1M migrateBypassPermissionsAcceptedToSettings(); // 权限状态迁移 // ... 更多迁移 saveGlobalConfig(prev => ({ ...prev, migrationVersion: CURRENT_MIGRATION_VERSION })); } }

每次版本号变更时,所有迁移函数幂等执行——已经迁移过的会 early return。

3.5 REPL 启动

最终通过 launchRepl() 启动交互界面:

typescript
复制代码
// replLauncher.tsx — 动态 import App 和 REPL export async function launchRepl(root, appProps, replProps, renderAndRun) { const { App } = await import('./components/App.js'); const { REPL } = await import('./screens/REPL.js'); await renderAndRun(root, <App {...appProps}><REPL {...replProps} /></App>); }

App 组件包裹了 ThemeProvider(在 ink.ts 中注入),提供亮/暗主题支持。Ink 本身是主题无关的——Claude Code 通过全局包装层注入主题。


四、状态管理:34 行的极简 Store

整个应用的状态管理只有 34 行代码,不依赖任何第三方库

typescript
复制代码
// state/store.ts — 完整源码 type Listener = () => void type OnChange<T> = (args: { newState: T; oldState: T }) => void export type Store<T> = { getState: () => T setState: (updater: (prev: T) => T) => void subscribe: (listener: Listener) => () => void } export function createStore<T>( initialState: T, onChange?: OnChange<T>, ): Store<T> { let state = initialState const listeners = new Set<Listener>() return { getState: () => state, setState: (updater: (prev: T) => T) => { const prev = state const next = updater(prev) if (Object.is(next, prev)) return // 核心:引用相等检查 state = next onChange?.({ newState: next, oldState: prev }) for (const listener of listeners) listener() }, subscribe: (listener: Listener) => { listeners.add(listener) return () => listeners.delete(listener) }, } }

配合 React 18 的 useSyncExternalStore 实现最小化重渲染:

typescript
复制代码
// state/AppState.tsx export function useAppState<T>(selector: (state: AppState) => T): T { const store = useAppStore(); return useSyncExternalStore( store.subscribe, () => selector(store.getState()), ); }

为什么不用 Zustand 或 Redux? 因为 Claude Code 的状态更新模式非常简单——没有中间件、没有 devtools、没有异步 action。一个 Object.is() + Set<Listener> 就够了。34 行代码 vs Zustand 的 1000+ 行,性能更好,依赖更少。

AppState 的形状

typescript
复制代码
export type AppState = DeepImmutable<{ settings: SettingsJson mainLoopModel: ModelSetting toolPermissionContext: ToolPermissionContext verbose: boolean statusLineText: string | undefined isBriefOnly: boolean spinnerTip?: string kairosEnabled: boolean // Bridge/Remote 状态 remoteSessionUrl: string | undefined remoteConnectionStatus: 'connecting' | 'connected' | 'reconnecting' | 'disconnected' replBridgeEnabled: boolean replBridgeConnected: boolean // ... }> & { // 可变部分 tasks: { [taskId: string]: TaskState } agentNameRegistry: Map<string, AgentId> foregroundedTaskId?: string mcp: { clients: MCPServerConnection[] tools: Tool[] commands: Command[] resources: Record<string, ServerResource[]> } }

注意:大部分字段是 DeepImmutable(只读递归类型),但 tasksmcp 是可变的——因为它们需要高频更新。


五、查询引擎:AsyncGenerator 驱动的对话循环

5.1 QueryEngine 类

QueryEngine 管理一次对话的完整生命周期,核心是一个异步生成器:

typescript
复制代码
export type QueryEngineConfig = { cwd: string tools: Tools commands: Command[] mcpClients: MCPServerConnection[] agents: AgentDefinition[] canUseTool: CanUseToolFn getAppState: () => AppState setAppState: (f: (prev: AppState) => AppState) => void readFileCache: FileStateCache customSystemPrompt?: string appendSystemPrompt?: string thinkingConfig?: ThinkingConfig maxTurns?: number // SDK 模式下限制轮数 maxBudgetUsd?: number // 成本预算 // ... } export class QueryEngine { private mutableMessages: Message[] = [] // 跨 turn 持久化 private fileStateCache: FileStateCache // 文件读取缓存 private totalUsage: NonNullableUsage // 累计 token private permissionDenials: SDKPermissionDenial[] = [] async *submitMessage( prompt: string | ContentBlockParam[], options?: { uuid?: string; isMeta?: boolean } ): AsyncGenerator<SDKMessage, void, unknown> { // 逐条 yield 消息,实现真正的流式响应 } }

5.2 数据流详解

text
复制代码
用户输入 "帮我写一个 TODO 应用" │ ▼ processUserInput() │ 解析斜杠命令 (/commit, /review 等) │ 展开粘贴内容 ([Pasted text #1 +10 lines]) │ 处理附件(图片、PDF) ▼ fetchSystemPromptParts() │ ① getSystemContext() — Git状态(memoized, 一次对话只算一次) │ ├─ getBranch() │ ├─ getDefaultBranch() │ ├─ git status --short (截断至2000字符) │ ├─ git log --oneline -n 5 │ └─ git config user.name │ ② getUserContext() — claude.md 发现 + 当前日期 │ ③ getCoordinatorUserContext() — Coordinator 模式专用 │ ④ 技能前端matter + 工具描述 ▼ query() — 核心查询函数 (1729行) │ ├─ normalizeMessagesForAPI() │ 过滤掉 progress/system 消息 │ 将内部消息格式转为 API 格式 │ ├─ queryModelWithStreaming() │ 调用 Claude API (带 prompt cache) │ yield AssistantMessage (流式) │ ├─ 提取 tool_use blocks │ ├─ StreamingToolExecutor.execute() │ ├─ 并发安全工具 → Promise.all() 并行执行 │ └─ 非安全工具 → 顺序执行 │ ├─ applyToolResultBudget() │ 大结果截断,防止上下文膨胀 │ ├─ executePostSamplingHooks() │ Stop hook / PostToolUse hook │ ├─ calculateTokenWarningState() │ 检查是否接近上下文窗口上限 │ └─ autoCompact / snipCompact (如果启用) 压缩旧消息,释放上下文空间

5.3 上下文压缩策略

当对话接近 token 上限时,有三种压缩策略(通过 Feature Gate 控制):

typescript
复制代码
// services/compact/autoCompact.ts export const AUTOCOMPACT_BUFFER_TOKENS = 13_000 // 触发阈值缓冲 export const WARNING_THRESHOLD_BUFFER_TOKENS = 20_000 // 警告阈值 export function getAutoCompactThreshold(model: string): number { const effectiveContextWindow = getEffectiveContextWindowSize(model) return effectiveContextWindow - AUTOCOMPACT_BUFFER_TOKENS } // 有效上下文窗口 = 总窗口 - 预留输出空间(20000) export function getEffectiveContextWindowSize(model: string): number { const reservedTokensForSummary = Math.min( getMaxOutputTokensForModel(model), 20_000 ) return contextWindow - reservedTokensForSummary }

三种策略:

策略Feature Gate说明
Auto Compact默认启用到达阈值时压缩旧消息为摘要
Reactive CompactREACTIVE_COMPACT主动式压缩,更激进
History SnipHISTORY_SNIP直接截断历史,仅保留最近消息
Context CollapseCONTEXT_COLLAPSE上下文折叠,语义压缩

六、工具系统:43 个模块的插件式架构

6.1 工具注册

tools.ts 是工具的注册中心,390 行代码管理所有工具的生命周期:

typescript
复制代码
// tools.ts — 所有工具的注册 export function getAllBaseTools(): Tools { return [ AgentTool, TaskOutputTool, BashTool, // Ant 内部构建嵌入了 bfs/ugrep,不需要独立的 Glob/Grep ...(hasEmbeddedSearchTools() ? [] : [GlobTool, GrepTool]), FileReadTool, FileEditTool, FileWriteTool, NotebookEditTool, WebFetchTool, TodoWriteTool, WebSearchTool, SkillTool, // ... 更多工具 // Feature-gated 工具 (编译时消除) ...(WebBrowserTool ? [WebBrowserTool] : []), ...(SleepTool ? [SleepTool] : []), ...(SnipTool ? [SnipTool] : []), ...(WorkflowTool ? [WorkflowTool] : []), // 仅在 Ant 内部构建启用 ...(process.env.USER_TYPE === 'ant' ? [ConfigTool, TungstenTool] : []), ...(REPLTool ? [REPLTool] : []), // 仅在测试环境 ...(process.env.NODE_ENV === 'test' ? [TestingPermissionTool] : []), ] }

6.2 工具池组装

内置工具和 MCP 工具通过 assembleToolPool() 合并:

typescript
复制代码
export function assembleToolPool( permissionContext: ToolPermissionContext, mcpTools: Tools, ): Tools { const builtInTools = getTools(permissionContext) const allowedMcpTools = filterToolsByDenyRules(mcpTools, permissionContext) // 关键设计:按 name 排序,保证 prompt cache 稳定性 // 如果 MCP 工具插入到内置工具之间,会导致所有下游 cache key 失效 const byName = (a: Tool, b: Tool) => a.name.localeCompare(b.name) // uniqBy 保留插入顺序 → 内置工具同名时优先 return uniqBy( [...builtInTools].sort(byName).concat(allowedMcpTools.sort(byName)), 'name', ) }

6.3 Simple 模式

CLAUDE_CODE_SIMPLE=1 时,工具池缩减到最小:

typescript
复制代码
if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) { const simpleTools: Tool[] = [BashTool, FileReadTool, FileEditTool] // Coordinator 模式额外加入 Agent + TaskStop if (coordinatorModeModule?.isCoordinatorMode()) { simpleTools.push(AgentTool, TaskStopTool, getSendMessageTool()) } return filterToolsByDenyRules(simpleTools, permissionContext) }

6.4 工具接口

每个工具实现统一的 Tool 接口(Tool.ts, 792 行):

typescript
复制代码
type Tool<Input, Output> = { name: string aliases?: string[] // 别名 searchHint?: string // ToolSearch 匹配用 shouldDefer?: boolean // 是否延迟加载(省token) alwaysLoad?: boolean // 是否总是加载 // 核心 call(args, context, canUseTool, parentMessage, onProgress?) description(): Promise<string> prompt(): Promise<string> // 给模型看的使用说明 inputJSONSchema: ToolInputJSONSchema // Zod → JSON Schema // 权限 checkPermissions(): Promise<PermissionResult> validateInput(): Promise<ValidationResult> // 属性 isConcurrencySafe(): boolean // 能否并行 isReadOnly(): boolean // 只读? isDestructive(): boolean // 破坏性? // UI 渲染 renderToolUseMessage() renderToolResultMessage() renderToolUseProgressMessage() }

6.5 BashTool 实现细节

typescript
复制代码
// BashTool 输入 Schema const fullInputSchema = z.strictObject({ command: z.string(), timeout: z.number().optional(), description: z.string().optional(), run_in_background: z.boolean().optional(), dangerouslyDisableSandbox: z.boolean().optional(), _simulatedSedEdit: z.object({ // 隐藏字段:sed 编辑模拟 filePath: z.string(), newContent: z.string() }).optional() }) // 命令分类(用于 UI 折叠展示) const BASH_SEARCH_COMMANDS = new Set([ 'find', 'grep', 'rg', 'ag', 'ack', 'locate', 'which', 'whereis' ]) const BASH_READ_COMMANDS = new Set([ 'cat', 'head', 'tail', 'less', 'more', 'wc', 'stat', 'file', 'jq', 'awk' ]) const BASH_SILENT_COMMANDS = new Set([ 'mv', 'cp', 'rm', 'mkdir', 'rmdir', 'chmod', 'chown', 'touch', 'ln', 'cd' ])

6.6 FileEditTool 的原子写入

文件编辑实现了无 async 间隙的原子写入,防止竞态条件:

typescript
复制代码
// 关键:staleness 检查和写入之间不能有 async 操作 const lastWriteTime = getFileModificationTime(absoluteFilePath) if (lastWriteTime > readTimestamp.timestamp) { // 文件在读取后被外部修改了 throw new Error(FILE_UNEXPECTEDLY_MODIFIED_ERROR) } // 同步读取 + 同步写入,中间无 async yield const { content, encoding, lineEndings } = readFileForEdit(absoluteFilePath) const { patch, updatedFile } = getPatchForEdit({ filePath, originalFileContents: content, oldString: actualOldString, newString, replaceAll }) writeTextContent(absoluteFilePath, updatedFile, encoding, lineEndings) // 原子写入 // 立即更新 readFileState 缓存 readFileState.set(absoluteFilePath, { content: updatedFile, timestamp: getFileModificationTime(absoluteFilePath), })

6.7 FileReadTool 的安全防护

typescript
复制代码
// 设备文件黑名单 — 防止读取无限流 const BLOCKED_DEVICE_PATHS = new Set([ '/dev/zero', '/dev/random', '/dev/urandom', '/dev/stdin', '/dev/tty', '/dev/console', ]) // UNC 路径拦截 — 防止 NTLM 凭据泄漏 if (fullFilePath.startsWith('\\\\') || fullFilePath.startsWith('//')) { return { result: true } // 静默跳过 } // 文件读取去重 — 同一文件同一范围不重复读取 const existingState = readFileState.get(fullFilePath) if (existingState && existingState.offset === offset && existingState.limit === limit) { const mtimeMs = await getFileModificationTimeAsync(fullFilePath) if (mtimeMs === existingState.timestamp) { return { data: { type: 'file_unchanged' } } // 返回stub,不重发内容 } }

6.8 GrepTool 的 Ripgrep 集成

typescript
复制代码
// VCS 目录排除 const VCS_DIRECTORIES_TO_EXCLUDE = ['.git', '.svn', '.hg', '.bzr', '.jj', '.sl'] for (const dir of VCS_DIRECTORIES_TO_EXCLUDE) { args.push('--glob', `!${dir}`) } args.push('--max-columns', '500') // 防止 base64/minified 内容膨胀 // 默认分页限制 — 防止上下文膨胀 const DEFAULT_HEAD_LIMIT = 250 // 负数 pattern 需要 -e 前缀,否则 rg 当作 flag 解析 if (pattern.startsWith('-')) { args.push('-e', pattern) }

6.9 ToolSearchTool:延迟加载机制

当工具总数超过阈值时,部分工具只发送 name,不发送完整 schema:

typescript
复制代码
// ToolSearch 的 select 语法 // "select:ToolA,ToolB" — 精确选择 const selectMatch = query.match(/^select:(.+)$/i) // 关键词搜索评分 if (parsed.parts.includes(term)) { score += parsed.isMcp ? 12 : 10 // MCP 工具名额外加权 } else if (parsed.parts.some(part => part.includes(term))) { score += parsed.isMcp ? 6 : 5 } if (pattern.test(hintNormalized)) { score += 4 // searchHint 匹配 } if (pattern.test(descNormalized)) { score += 2 // 描述匹配 }

6.10 并发执行策略

typescript
复制代码
class StreamingToolExecutor { async execute(toolUses, context) { // Read/Grep/Glob 标记为 concurrencySafe → Promise.all() 并行 const safe = toolUses.filter(t => findTool(t).isConcurrencySafe()) const unsafe = toolUses.filter(t => !findTool(t).isConcurrencySafe()) const safeResults = await Promise.all( safe.map(t => this.executeSingle(t, context)) ) // Bash/Write/Edit 等有副作用的工具 → 严格串行 const unsafeResults = [] for (const t of unsafe) { unsafeResults.push(await this.executeSingle(t, context)) } } }

七、权限系统:6 层纵深防御

7.1 权限模式

typescript
复制代码
const PERMISSION_MODE_CONFIG = { default: { title: 'Default' }, // 交互式提示 plan: { title: 'Plan Mode' }, // 只读+规划,不执行 acceptEdits: { title: 'Accept edits' }, // 自动接受编辑 bypassPermissions: { title: 'Bypass' }, // 全部放行 dontAsk: { external: 'dontAsk' }, // 不提示 auto: { title: 'Auto mode' } // ML 分类器自动判断 }

7.2 完整检查链

text
复制代码
用户提交工具调用(如 Bash: "rm -rf /") │ ▼ ① validateInput() — 工具自定义校验 │ 例:FileEditTool 检查文件大小是否超过 1GiB ▼ ② alwaysDenyRules 检查 — 黑名单直接拦截 │ 匹配: 工具名 + ruleContent (glob pattern) │ 例:Deny "Bash(rm -rf *)" ▼ ③ alwaysAllowRules 检查 — 白名单直接放行 │ 例:Allow "Bash(git *)" ▼ ④ Auto Mode Classifier (Feature-gated: TRANSCRIPT_CLASSIFIER) │ ML 模型分析整个对话上下文 │ 判断操作是否安全 │ ├→ 安全 → 放行 │ ├→ 危险 → 拒绝 │ └→ 不确定 → 继续到 ⑤ ▼ ⑤ PreToolUse Hook 检查 │ 执行用户定义的 Shell 脚本 │ exitCode=0 → 放行 │ exitCode=2 → 拦截 ▼ ⑥ 交互式提示 — 弹出终端 UI 询问用户 │ Allow / Deny / Always Allow ▼ canUseTool() — 最终裁决

7.3 拒绝追踪与降级

typescript
复制代码
// 连续被拒绝 3 次后,从自动拒绝降级为弹窗询问 // 防止 AI 陷入 "尝试 → 被拒 → 再尝试" 的死循环 permissionDenials.push({ tool, input, reason }) if (denialCount >= 3) { fallbackToPrompt = true // 弹窗让用户手动决定 }

7.4 危险路径检测

typescript
复制代码
// utils/permissions/filesystem.ts (62KB) export const DANGEROUS_FILES = [ '.gitconfig', '.bashrc', '.zshrc', '.mcp.json', '.claude.json' ] export const DANGEROUS_DIRECTORIES = [ '.git', '.vscode', '.idea', '.claude' ] // bypassPermissions 模式下,检测 root 权限 + 沙箱状态 if (process.getuid() === 0 && process.env.IS_SANDBOX !== '1') { console.error('--dangerously-skip-permissions cannot be used with root/sudo') process.exit(1) }

八、Hook 系统:8 种事件类型

8.1 事件类型

typescript
复制代码
type HookEvent = | 'PreToolUse' // 工具执行前(可拦截) | 'PostToolUse' // 工具执行后 | 'PostToolUseFailure' // 工具失败后 | 'PermissionDenied' // 权限拒绝 | 'Notification' // 通知时 | 'UserPromptSubmit' // 用户提交消息 | 'SessionStart' // 会话启动 | 'Stop' // Claude 停止前 | 'StopFailure' // 停止失败时

8.2 Hook 配置 Schema

Hook 支持 4 种执行方式(Zod 辨别联合类型):

typescript
复制代码
const HookCommandSchema = z.discriminatedUnion('type', [ // ① Shell 命令 z.object({ type: z.literal('command'), command: z.string(), if: z.string().optional(), // 过滤条件 timeout: z.number().optional(), once: z.boolean().optional(), // 只执行一次 async: z.boolean().optional(), // 异步执行 }), // ② Prompt(交给 Claude 判断) z.object({ type: z.literal('prompt'), prompt: z.string(), model: z.string().optional(), }), // ③ HTTP 调用 z.object({ type: z.literal('http'), url: z.string().url(), headers: z.record(z.string()).optional(), }), // ④ Agent(另起一个 Claude 验证) z.object({ type: z.literal('agent'), prompt: z.string(), model: z.string().optional(), }), ])

九、MCP 集成:5 种传输协议

9.1 配置 scope

MCP 配置支持 7 个层级,按优先级合并:

typescript
复制代码
type ConfigScope = | 'local' // 项目本地 (.mcp.json) | 'user' // 用户级 (~/.claude/mcp.json) | 'project' // 项目级 (.claude/mcp.json) | 'dynamic' // 运行时动态添加 | 'enterprise' // 企业策略 | 'claudeai' // claude.ai 云端配置 | 'managed' // 远程管理

9.2 传输方式

typescript
复制代码
type Transport = 'stdio' | 'sse' | 'sse-ide' | 'http' | 'ws' | 'sdk'

9.3 连接状态机

typescript
复制代码
type MCPServerConnection = | { type: 'connected', client: Client, capabilities, cleanup() } | { type: 'failed', error: string } | { type: 'needs-auth', authUrl: string } // 需要 OAuth | { type: 'pending' } // 连接中 | { type: 'disabled' } // 已禁用

9.4 原子配置写入

MCP 配置文件写入使用原子 rename 模式:

typescript
复制代码
async function writeMcpjsonFile(config) { const tempPath = `${mcpJsonPath}.tmp.${process.pid}.${Date.now()}` const handle = await open(tempPath, 'w', existingMode ?? 0o644) try { await handle.writeFile(JSON.stringify(config, null, 2)) await handle.datasync() // 强制刷盘 } finally { await handle.close() } // 原子 rename if (existingMode !== undefined) await chmod(tempPath, existingMode) await rename(tempPath, mcpJsonPath) }

十、终端 UI:深度 Fork 的 Ink

Claude Code 不是简单使用 Ink 库,而是 fork 并深度定制 了整个终端渲染引擎(50+ 文件):

text
复制代码
ink/ ├── root.ts # 渲染引擎(入口) ├── render-to-screen.ts # ANSI 序列生成 + diff 优化 ├── layout/engine.ts # Yoga 布局引擎包装 ├── dom.ts # 虚拟 DOM ├── frame.ts # 帧管理(FlickerReason 追踪) ├── focus.ts # 焦点管理 ├── hit-test.ts # 鼠标点击测试 ├── selection.ts # 文本选择 ├── bidi.ts # 双向文本(RTL 支持) ├── wrap-text.ts # 文本换行 ├── Ansi.tsx # ANSI 转义序列组件 ├── events/ │ ├── click-event.ts # 鼠标事件 │ ├── input-event.ts # 键盘输入(含 Key 类型) │ ├── terminal-focus-event.ts # 终端焦点 │ └── emitter.ts # 事件发射器 ├── hooks/ │ ├── use-input.ts # 键盘输入 hook │ ├── use-animation-frame.ts # 动画帧 │ ├── use-interval.ts # 定时器 │ ├── use-selection.ts # 文本选择 │ ├── use-tab-status.ts # Tab 状态 (OSC) │ ├── use-terminal-viewport.ts # 视口感知 │ └── use-terminal-focus.ts # 终端焦点 └── components/ ├── Box.tsx, Text.tsx # 基础组件 ├── Button.tsx # 按钮(含 ButtonState) ├── Link.tsx # 终端超链接 ├── AlternateScreen.tsx # 备用屏幕缓冲区 ├── NoSelect.tsx # 不可选文本 └── RawAnsi.tsx # 原始 ANSI 输出

虚拟滚动

typescript
复制代码
// hooks/useVirtualScroll.ts (35KB) const DEFAULT_ESTIMATE = 3 // 未测量项的预估高度(故意偏低) const OVERSCAN_ROWS = 80 // 视口外额外渲染行数 const SCROLL_QUANTUM = 40 // 重渲染阈值(半个 overscan) const MAX_MOUNTED_ITEMS = 300 // 最大挂载项数 const SLIDE_STEP = 25 // 每次提交的最大新增项(防止 290ms 同步阻塞)

主题系统

typescript
复制代码
// ink.ts — 全局主题注入 function withTheme(node: ReactNode): ReactNode { return createElement(ThemeProvider, null, node) } export async function createRoot(options?: RenderOptions): Promise<Root> { const root = await inkCreateRoot(options) return { ...root, render: node => root.render(withTheme(node)), // 包装每次渲染 } }

十一、上下文管理:Memoized 的系统提示词

11.1 Git 状态收集

typescript
复制代码
// context.ts export const getGitStatus = memoize(async (): Promise<string | null> => { // 并行执行所有 git 命令 const [branch, mainBranch, status, log, userName] = await Promise.all([ getBranch(), getDefaultBranch(), execFileNoThrow(gitExe(), ['--no-optional-locks', 'status', '--short']), execFileNoThrow(gitExe(), ['--no-optional-locks', 'log', '--oneline', '-n', '5']), execFileNoThrow(gitExe(), ['config', 'user.name']), ]) // 状态超过 2000 字符时截断 const truncatedStatus = status.length > MAX_STATUS_CHARS ? status.substring(0, MAX_STATUS_CHARS) + '\n... (truncated. Run "git status" using BashTool for full output)' : status })

11.2 CCR 跳过 Git

typescript
复制代码
export const getSystemContext = memoize(async () => { // CCR 远程模式跳过 git 状态(不需要,也避免开销) const gitStatus = isEnvTruthy(process.env.CLAUDE_CODE_REMOTE) ? null : await getGitStatus() return { ...(gitStatus && { gitStatus }), // Ant-only: cache breaker 注入 ...(feature('BREAK_CACHE_COMMAND') && injection ? { cacheBreaker: `[CACHE_BREAKER: ${injection}]` } : {}), } })

11.3 注入变更清除缓存

typescript
复制代码
// 当系统提示词注入内容变化时,立即清除 memoize 缓存 export function setSystemPromptInjection(value: string | null): void { systemPromptInjection = value getUserContext.cache.clear?.() getSystemContext.cache.clear?.() }

十二、成本追踪:每模型粒度的会话管理

typescript
复制代码
// cost-tracker.ts type StoredCostState = { totalCostUSD: number totalAPIDuration: number totalAPIDurationWithoutRetries: number totalToolDuration: number totalLinesAdded: number totalLinesRemoved: number modelUsage: { [modelName: string]: ModelUsage } }

会话恢复

成本数据通过 sessionId 匹配恢复,防止不同会话的成本混淆:

typescript
复制代码
export function getStoredSessionCosts(sessionId: string): StoredCostState | undefined { const projectConfig = getCurrentProjectConfig() // 只有 sessionId 匹配才恢复 if (projectConfig.lastSessionId !== sessionId) { return undefined } return { totalCostUSD: projectConfig.lastCost ?? 0, ... } }

Advisor 递归成本追踪

Claude Code 支持 "Advisor" 模式(另一个模型辅助当前模型),成本递归累加:

typescript
复制代码
export function addToTotalSessionCost(cost, usage, model) { // 递归追踪 advisor 用量 for (const advisorUsage of getAdvisorUsage(usage)) { const advisorCost = calculateUSDCost(advisorUsage.model, advisorUsage) logEvent('tengu_advisor_tool_token_usage', { advisor_model: advisorUsage.model, cost_usd_micros: Math.round(advisorCost * 1_000_000), }) totalCost += addToTotalSessionCost(advisorCost, advisorUsage, advisorUsage.model) } }

十三、会话历史:JSONL + 反向读取

存储格式

typescript
复制代码
type LogEntry = { display: string // 显示文本 pastedContents: Record<number, StoredPastedContent> // 粘贴内容引用 timestamp: number project: string // 项目路径 sessionId?: string } // 粘贴内容分流 type StoredPastedContent = { id: number type: 'text' | 'image' content?: string // 小于 1024 字符 → 内联 contentHash?: string // 大于 1024 字符 → hash 引用,异步写入磁盘 }

反向读取(Up 键历史)

typescript
复制代码
async function* makeLogEntryReader(): AsyncGenerator<LogEntry> { // 1. 先 yield 未刷盘的 pending 条目 for (let i = pendingEntries.length - 1; i >= 0; i--) { yield pendingEntries[i]! } // 2. 从磁盘反向读取 JSONL for await (const line of readLinesReverse(historyPath)) { const entry = deserializeLogEntry(line) // 跳过已删除的条目 if (skippedTimestamps.has(entry.timestamp)) continue yield entry } }

刷盘与文件锁

typescript
复制代码
async function immediateFlushHistory() { const historyPath = join(getClaudeConfigHomeDir(), 'history.jsonl') // 文件锁(10s stale, 3 次重试) release = await lock(historyPath, { stale: 10000, retries: { retries: 3, minTimeout: 50 }, }) // 批量追加 const jsonLines = pendingEntries.map(entry => jsonStringify(entry) + '\n') pendingEntries = [] await appendFile(historyPath, jsonLines.join(''), { mode: 0o600 }) }

十四、Bridge:远程执行引擎

Bridge 让 claude.ai 网页端可以远程执行本地代码。

架构

text
复制代码
claude.ai 网页 ←HTTP→ Anthropic API ←轮询→ Bridge 进程 ←spawn→ 本地 Claude Code

三种 Spawn 模式

typescript
复制代码
type SpawnMode = 'single-session' | 'worktree' | 'same-dir'
模式说明适用场景
single-session一个目录一个会话,完成即销毁一次性任务
worktree持久服务器,每个会话用 git worktree 隔离多人协作
same-dir持久服务器,共享目录简单场景(有干扰风险)

WorkSecret 协议

typescript
复制代码
type WorkSecret = { version: number session_ingress_token: string // JWT token api_base_url: string sources: Array<{ // Git 源 type: string git_info?: { repo: string; ref?: string; token?: string } }> auth: Array<{ type: string; token: string }> claude_code_args?: Record<string, string> mcp_config?: unknown // MCP 服务器配置 environment_variables?: Record<string, string> }

退避策略

typescript
复制代码
const DEFAULT_BACKOFF = { connInitialMs: 2_000, // 初始连接重试 connCapMs: 120_000, // 最大连接重试(2分钟) connGiveUpMs: 600_000, // 放弃连接(10分钟) generalInitialMs: 500, generalCapMs: 30_000, generalGiveUpMs: 600_000, }

十五、Coordinator 模式:多智能体编排

Feature-gated 的多智能体模式(COORDINATOR_MODE):

typescript
复制代码
// coordinator/coordinatorMode.ts export function getCoordinatorSystemPrompt(): string { return `You are Claude Code, an AI assistant that orchestrates software engineering tasks across multiple workers. ## Your Role You are a **coordinator**. Your job is to: - Help the user achieve their goal - Direct workers to research, implement and verify code changes - Synthesize results and communicate with the user - Answer questions directly when possible ## Your Tools - **Agent** - Spawn a new worker - **SendMessage** - Continue an existing worker - **TaskStop** - Stop a running worker` } // Worker 工具集过滤(Simple 模式下只给 Bash/Read/Edit) export function getCoordinatorUserContext(mcpClients, scratchpadDir?) { const workerTools = isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE) ? [BASH_TOOL_NAME, FILE_READ_TOOL_NAME, FILE_EDIT_TOOL_NAME] : Array.from(ASYNC_AGENT_ALLOWED_TOOLS) .filter(name => !INTERNAL_WORKER_TOOLS.has(name)) }

十六、快捷键系统:和弦 + 平台自适应

平台检测

typescript
复制代码
// keybindings/defaultBindings.ts const IMAGE_PASTE_KEY = getPlatform() === 'windows' ? 'alt+v' : 'ctrl+v' // VT 模式检测(影响 Shift+Tab 是否可用) const SUPPORTS_TERMINAL_VT_MODE = getPlatform() !== 'windows' || (isRunningWithBun() ? satisfies(process.versions.bun, '>=1.2.23') : satisfies(process.versions.node, '>=22.17.0 <23.0.0 || >=24.2.0')) const MODE_CYCLE_KEY = SUPPORTS_TERMINAL_VT_MODE ? 'shift+tab' : 'meta+m'

和弦(Chord)支持

typescript
复制代码
// 例:ctrl+x ctrl+k → 杀死所有 Agent export const DEFAULT_BINDINGS = [ { context: 'Chat', bindings: { 'ctrl+x ctrl+k': 'chat:killAgents', // 两键和弦 'ctrl+x ctrl+e': 'chat:externalEditor', }, }, ]

十七、Vim 模式:完整的状态机

typescript
复制代码
// vim/types.ts — 状态机定义 export type VimState = | { mode: 'INSERT'; insertedText: string } | { mode: 'NORMAL'; command: CommandState } export type CommandState = | { type: 'idle' } // 等待输入 | { type: 'count'; digits: string } // 数字前缀 (3dd) | { type: 'operator'; op: Operator; count: number } // d/c/y 等待 motion | { type: 'operatorCount'; op: Operator; count: number; digits: string } | { type: 'operatorFind'; op: Operator; count: number; find: FindType } | { type: 'operatorTextObj'; op: Operator; count: number; scope: 'inner'|'around' } | { type: 'find'; find: 'f'|'F'|'t'|'T'; count: number } | { type: 'g'; count: number } // gg/G | { type: 'replace'; count: number } // r 等待字符 | { type: 'indent'; dir: '>'|'<'; count: number } // >> / <<

支持 dot-repeat (.)、register ("a)、text objects (ciw, da") 等完整 Vim 操作。


十八、彩蛋:Buddy 宠物系统

typescript
复制代码
// buddy/types.ts export const SPECIES = [ 'duck', 'goose', 'blob', 'cat', 'dragon', 'octopus', 'owl', 'penguin', 'turtle', 'snail', 'ghost', 'axolotl', 'capybara', 'cactus', 'robot', 'rabbit', 'mushroom', 'chonk', ] as const // 18 种物种 export const RARITIES = ['common', 'uncommon', 'rare', 'epic', 'legendary'] as const export type CompanionBones = { rarity: Rarity species: Species eye: Eye hat: Hat shiny: boolean // 闪光版 stats: Record<StatName, number> // Debugging, Patience, Chaos, Wisdom, Snark } export type CompanionSoul = { name: string // AI 生成的名字 personality: string // AI 生成的性格 }

hash(userId) 确定性生成,保证同一用户在所有会话中看到同一只宠物。首次 "孵化" 时由 Claude 生成名字和性格。还有一个 CompanionSprite.tsx (45KB) 实现了帧动画。


十九、Feature Gate:编译时 vs 运行时

Claude Code 使用两层特性开关:

编译时(Bun DCE)

typescript
复制代码
import { feature } from 'bun:bundle'; // 编译时完全消除 — 外部构建中这段代码不存在 if (feature('COORDINATOR_MODE')) { const module = require('./coordinatorMode.js') }

已发现的 Feature Gate:

Flag说明
COORDINATOR_MODE多智能体编排
VOICE_MODE语音输入
HISTORY_SNIP历史截断压缩
TRANSCRIPT_CLASSIFIERML 权限分类器
REACTIVE_COMPACT主动上下文压缩
CONTEXT_COLLAPSE语义上下文折叠
KAIROSAssistant 模式
BRIDGE_MODE远程桥接
PROACTIVE主动提示
AGENT_TRIGGERSCron 触发器
UDS_INBOXUnix Socket 收件箱
DAEMON守护进程模式
ABLATION_BASELINE消融实验基线
DUMP_SYSTEM_PROMPT导出系统提示词
WORKFLOW_SCRIPTS工作流脚本
WEB_BROWSER_TOOL浏览器工具
TERMINAL_PANEL终端面板
EXPERIMENTAL_SKILL_SEARCH技能搜索
KAIROS_GITHUB_WEBHOOKSGitHub Webhook 订阅
CHICAGO_MCPComputer Use MCP
OVERFLOW_TEST_TOOL溢出测试

运行时(GrowthBook)

typescript
复制代码
// 基于用户/环境动态开关 const isEnabled = getFeatureValue_CACHED_MAY_BE_STALE('tengu_some_feature', false)

用户类型区分

typescript
复制代码
// 编译时字符串替换 if ("external" !== 'ant' && isBeingDebugged()) { process.exit(1) // 外部构建禁止调试 } // 运行时环境变量 if (process.env.USER_TYPE === 'ant') { // Anthropic 内部工具:ConfigTool, TungstenTool, REPLTool }

二十、配额限制与"封号"机制

很多人关心:Claude Code 会不会封号?源码里有什么限制机制?

20.1 没有传统"封号",但有严格的配额执行

Claude Code 不在客户端做任何封号判断。所有限制通过 API 响应头 anthropic-ratelimit-unified-* 由服务端下发,客户端只是忠实执行。

typescript
复制代码
// services/claudeAiLimits.ts type QuotaStatus = 'allowed' | 'allowed_warning' | 'rejected' type RateLimitType = | 'five_hour' // 5 小时滑动窗口 | 'seven_day' // 7 天窗口 | 'seven_day_opus' // Opus 专用 7 天限制 | 'seven_day_sonnet' // Sonnet 专用 7 天限制 | 'overage' // 超额使用(付费后仍有上限)

20.2 服务端响应头解析

每次 API 调用后,客户端解析以下响应头:

typescript
复制代码
function computeNewLimitsFromHeaders(headers: Headers): ClaudeAILimits { // 核心状态:allowed / allowed_warning / rejected const status = headers.get('anthropic-ratelimit-unified-status') as QuotaStatus || 'allowed' // 重置时间(Unix 秒) const resetsAt = Number(headers.get('anthropic-ratelimit-unified-reset')) // 命中的限制类型 const rateLimitType = headers.get('anthropic-ratelimit-unified-representative-claim') // 超额使用状态 const overageStatus = headers.get('anthropic-ratelimit-unified-overage-status') // 超额被禁用的原因(12 种可能) const overageDisabledReason = headers.get( 'anthropic-ratelimit-unified-overage-disabled-reason' ) // 使用率(0~1) const utilization5h = Number(headers.get('anthropic-ratelimit-unified-5h-utilization')) const utilization7d = Number(headers.get('anthropic-ratelimit-unified-7d-utilization')) }

status === 'rejected' 时,所有 API 调用立即被阻止,用户必须等到 resetsAt 时间。

20.3 超额禁用原因(12 种)

typescript
复制代码
type OverageDisabledReason = | 'overage_not_provisioned' // 未开通超额 | 'org_level_disabled' // 组织级别禁用 | 'org_level_disabled_until' // 组织级别临时禁用 | 'out_of_credits' // 余额不足 | 'seat_tier_level_disabled' // 席位等级禁用 | 'member_level_disabled' // 个人账户禁用 | 'seat_tier_zero_credit_limit' // 席位等级信用额度为零 | 'group_zero_credit_limit' // 组信用额度为零 | 'member_zero_credit_limit' // 个人信用额度为零 | 'org_service_level_disabled' // 组织服务级别禁用 | 'org_service_zero_credit_limit'// 组织服务信用额度为零 | 'no_limits_configured' // 未配置限制 | 'unknown'

20.4 预飞检查(Pre-flight)

每次会话启动时,Claude Code 发一个最小请求来探测配额状态:

typescript
复制代码
async function makeTestQuery() { const model = getSmallFastModel() // 用最小模型 const anthropic = await getAnthropicClient({ maxRetries: 0, model, source: 'quota_check', }) return anthropic.beta.messages.create({ model, max_tokens: 1, // 只请求 1 个 token messages: [{ role: 'user', content: 'quota' }], }).asResponse() }

用最便宜的模型、最少的 token 做探测,只为拿到响应头中的配额信息。

20.5 早期预警系统

Claude Code 会预判你的消耗速度,在你还没被限制时就发出警告:

typescript
复制代码
const EARLY_WARNING_CONFIGS = [ { rateLimitType: 'five_hour', windowSeconds: 5 * 60 * 60, thresholds: [ { utilization: 0.9, timePct: 0.72 }, // 用了90%但时间只过72% ], }, { rateLimitType: 'seven_day', windowSeconds: 7 * 24 * 60 * 60, thresholds: [ { utilization: 0.75, timePct: 0.6 }, // 用了75%但时间只过60% { utilization: 0.5, timePct: 0.35 }, // 用了50%但时间只过35% { utilization: 0.25, timePct: 0.15 }, // 用了25%但时间只过15% ], }, ]

翻译一下:如果你在 7 天窗口的前 35% 时间里就用掉了 50% 的配额,系统会警告你正在 "燃烧" 配额

20.6 429 错误处理

收到 HTTP 429 时,状态被强制设为 rejected

typescript
复制代码
export function extractQuotaStatusFromError(error: APIError): void { if (error.status !== 429) return let newLimits = { ...currentLimits } if (error.headers) { newLimits = computeNewLimitsFromHeaders(error.headers) } // 无论 headers 是否存在,429 = 立刻 rejected newLimits.status = 'rejected' emitStatusChange(newLimits) }

20.7 用户看到的限制信息

typescript
复制代码
// 实际展示给用户的消息 "You've hit your session limit · resets in 4 hours" "You've hit your weekly limit" "You've hit your Opus limit" "You're out of extra usage"

20.8 反调试机制

外部构建中,检测到 Node.js Inspector 活跃时直接退出:

typescript
复制代码
// 检测 --inspect、--inspect-brk、--debug、--debug-brk // 以及 inspector.url() 是否活跃 if ("external" !== 'ant' && isBeingDebugged()) { process.exit(1) // 静默退出,无任何错误信息 }

"external" 是编译时替换的字符串——Anthropic 内部构建替换为 'ant',外部构建保持 'external'所以只有外部用户被拦截调试

20.9 客户端无法绕过

源码中没有任何绕过机制

  • 不能伪造响应头
  • 不能修改重置时间
  • 不能欺骗授权
  • 所有限制执行都是服务端驱动的
  • 429 错误是 fatal,不可重试(超过限制后)

结论:Claude Code 的限制完全是服务端控制的。客户端只是一个忠实的执行者,没有任何后门或绕过逻辑。


二十一、数据收集与遥测:你的数据去了哪里

这是很多开发者关心的问题。源码揭示了完整的遥测架构。

21.1 三大数据出口

text
复制代码
Claude Code CLI │ ├─ logEvent() → 事件队列 │ ├──→ Datadog (30+ 种事件) │ │ POST https://http-intake.logs.us5.datadoghq.com/api/v2/logs │ │ Token: pubbbf48e6d78dae54bceaa4acf463299bf │ │ │ └──→ Anthropic 1P (完整事件+用户元数据) │ POST https://api.anthropic.com/api/event_logging/batch │ 失败时持久化到 ~/.claude/telemetry/1p_failed_events.*.json │ └─ Metrics → BigQuery POST https://api.anthropic.com/api/claude_code/metrics

21.2 发送到 Datadog 的事件(30+ 种)

text
复制代码
API 相关: tengu_api_error, tengu_api_success 认证相关: tengu_oauth_success, tengu_oauth_error, token refresh 事件 工具使用: tengu_tool_use_success, tengu_tool_use_error 会话相关: tengu_session_file_read, tengu_exit, tengu_init 语音相关: tengu_voice_toggled, tengu_voice_recording_started Chrome: chrome_bridge_connection_succeeded, chrome_bridge_tool_call_completed 团队同步: tengu_team_mem_sync_pull, tengu_team_mem_sync_push 配额变化: tengu_claudeai_limits_status_changed

21.3 收集的用户数据

每个事件附带的元数据:

typescript
复制代码
// firstPartyEventLogger.ts — 1P 事件元数据 { event_id: UUID, event_name: '事件名', client_timestamp: '时间戳', device_id: '持久化设备ID', // getOrCreateUserID() user_id: '同 device_id', email: '用户邮箱', // OAuth 登录后可用 session_id: '会话ID', core_metadata: { service_name: 'claude-code', version: '版本号', platform: 'darwin/linux/win32', os_info: 'OS版本' }, user_metadata: { organization_uuid: '组织ID', account_uuid: '账户ID', subscription_type: '订阅类型', rate_limit_tier: 'API限制等级', first_token_time: '首次使用时间' }, auth: { account_uuid: '账户UUID', organization_uuid: '组织UUID' } }

21.4 GitHub CI 环境下的额外收集

当在 GitHub Actions 中运行时,额外收集:

typescript
复制代码
// GrowthBook 用户属性 { github: { actor: 'GitHub 用户名', actorId: 'GitHub 用户ID', repository: '仓库名', repositoryId: '仓库ID', repositoryOwner: '仓库所有者', repositoryOwnerId: '所有者ID' } }

21.5 代码内容保护

源码中有显式的代码泄漏防护

typescript
复制代码
// 元数据类型名本身就是防线——类型名强制开发者确认 type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS = string // metadata.ts — 截断和清洗规则 - 字符串截断到 512 字符(只展示前 128 字符) - JSON 输出上限 4KB - 集合元素上限 20 个 - 嵌套深度上限 2 层 - 以 _ 开头的内部字段自动剥离 // 用户提示词默认 redacted userPrompt = '<REDACTED>' // 除非显式开启 OTEL_LOG_USER_PROMPTS=1 // MCP 工具名默认匿名化 mcpToolName = 'mcp_tool' // 除非是官方 MCP 注册表中的工具

21.6 用户桶哈希

为了在 Datadog 中统计独立用户数同时保护隐私:

typescript
复制代码
// datadog.ts — 用户 ID 哈希到 30 个桶 function getUserBucket(userId: string): number { const hash = SHA256(userId) return hash % 30 // 30 个桶,无法反推用户 ID }

21.7 关闭遥测

Claude Code 提供了两个级别的关闭方式:

bash
复制代码
# 级别 1:关闭遥测(Datadog + 1P 事件 + 反馈调查) export DISABLE_TELEMETRY=1 # 级别 2:关闭所有非必要网络请求(最彻底) # 包括:遥测、自动更新、release notes、模型能力刷新 export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1

此外,组织管理员可以通过 API 在组织级别禁用指标收集:

text
复制代码
GET /api/claude_code/organizations/metrics_enabled → false

21.8 事件采样

并非每个事件都会被发送——GrowthBook 配置了采样率:

typescript
复制代码
// firstPartyEventLogger.ts // tengu_event_sampling_config 配置每种事件的采样率 // 如果被采样掉,sample_rate 字段会被添加到元数据中

21.9 失败事件持久化

如果事件发送失败,会写入本地磁盘:

text
复制代码
~/.claude/telemetry/1p_failed_events.<sessionId>.<uuid>.json

下次启动时重试发送(二次退避,最多 8 次尝试,最大间隔 30 秒)。

21.10 遥测小结

类别收集内容是否包含代码
身份信息deviceId, email, accountUuid, orgUuid
使用数据模型选择, token 计数, 成本, 工具使用
性能数据API 延迟, 启动时间, FPS
错误数据错误类型, HTTP 状态码
代码内容N/A显式阻止
用户提示词默认 <REDACTED>默认否
文件路径工作区路径(非文件路径)工作区级别

结论:Claude Code 收集的是使用行为数据,不是代码内容。有明确的类型系统防止开发者意外发送代码。可以通过环境变量完全关闭。


二十二、设计哲学总结

通读 1884 个文件后,总结 Claude Code 的 8 条设计原则:

1. 启动性能是一等公民

  • 快速路径 + 动态 import + 并行预取 + 编译时 DCE
  • profileCheckpoint() 在每个分支点标记时间
  • MDM + Keychain 读取在 import 副作用中并行启动

2. 安全性纵深防御

  • 6 层权限检查 + Hook 拦截 + ML 分类器
  • 反调试机制(外部构建检测 Inspector 直接退出)
  • UNC 路径拦截防止 NTLM 凭据泄漏
  • 设备文件黑名单防止无限读取
  • 原子写入防止竞态条件

3. 流式一切

  • AsyncGenerator 贯穿整个消息流
  • 工具执行流式上报 progress
  • 历史读取用异步生成器反向遍历
  • REPL 渲染异步于查询执行

4. 状态管理极简

  • 34 行自研 Store,Object.is() 变更检测
  • 不引入任何第三方状态库
  • useSyncExternalStore 对接 React

5. 可扩展优先

  • 43 个工具通过统一接口注册
  • MCP 让外部工具无缝接入(5 种传输协议)
  • Hook 系统让用户自定义行为(4 种执行方式)
  • 技能系统让社区贡献能力

6. 渐进式发布

  • 20+ 个 Feature Gate,编译时消除未启用代码
  • GrowthBook 运行时灰度
  • USER_TYPE 区分内部/外部构建

7. 上下文即生命线

  • Memoized 系统上下文(一次对话只计算一次)
  • 三种压缩策略应对上下文窗口限制
  • 文件读取 LRU 缓存 + 去重
  • 工具结果预算控制
  • ToolSearch 延迟加载省 token

8. 测试友好

  • 依赖注入贯穿核心模块
  • QueryDeps 允许注入 mock
  • Feature Gate 在 bun test 下返回 false
  • TestingPermissionTool 仅测试环境启用

声明:本文仅用于技术学习和架构分析。源码分析基于公开泄漏的代码,所有商标和版权归 Anthropic 所有。


如果觉得有收获,欢迎点赞收藏。关于 Claude Code 的架构细节,欢迎评论区交流。

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
yosoo
下载 APP