2892 字
14 分钟
Claude Code 源码泄露:我从 51 万行 TypeScript 里看到了什么

Claude Code 是 Anthropic 的官方 CLI 工具,通过 npm 发布为 @anthropic-ai/claude-code。你 npm install 拿到的是一个约 12MB 的 minified bundle cli.js——一坨不可读的压缩代码。

直到有人把它逆向了。

泄露了什么#

这个仓库从 npm 包 v2.1.88 中逆向提取了完整的 TypeScript 源码。规模不小:

  • 1,884 个 TypeScript 文件
  • 约 51 万行代码
  • 覆盖 40+ 工具实现、90+ slash 命令、完整的 Ink 终端 UI 框架、MCP 集成、Sub-agent 系统

核心架构是典型的 Node.js CLI:入口 → Query Engine → Tools/Services/State。Ink(一个基于 React 的终端 UI 框架)被 Anthropic 深度魔改,自己实现了 reconciler、layout engine、事件系统。

不过,这份源码不完整。108 个模块被 Bun 编译时的 feature() 开关排除在外——它们只存在于 Anthropic 内部 monorepo,从未出现在 npm 包里。包括后台守护进程(DAEMON)、主动通知系统(PROACTIVE)、上下文折叠(CONTEXT_COLLAPSE)等。

虽然这些模块的实现缺失,但调用它们的条件分支代码完整保留了。你能看到 KAIROS(全自动 agent 模式)何时激活、做什么,只是走进条件分支后 require() 的模块是空的。像看到了门和钥匙孔,但门后面的房间是空的。

真正值得关注的不在缺失的部分,而在已有的部分。

Agent 架构:五层编排系统#

这是整个源码里最有工程价值的部分。Claude Code 的 agent 系统不是一个简单的”调 API 然后返回结果”——它是一个五层编排架构

第一层:同步 Agent#

最基础的模式。AgentTool.call() 启动一个子 agent,阻塞等待返回。就是你在 Claude Code 里看到的 “Explore”、“Plan” 这些 built-in agent。

第二层:异步后台 Agent#

通过 LocalAgentTask 实现。不阻塞主线程,有实时进度追踪。关键设计在 runAsyncAgentLifecycle() 里:

  • 每 30 秒 fork 一个轻量 agent 来生成 3-5 词的进度摘要
  • 这个摘要 agent 复用父 agent 的 prompt cache,只多传一条”总结一下进度”的指令
  • 完成时先标记 completed(gate TaskOutput),再跑交接分类器
  • 出错时从消息历史中提取部分结果,而不是直接返回空

第三层:Fork 子 Agent#

这是 prompt cache 优化的精髓。当 subagent_type 没指定时,子 agent 继承父 agent 的完整对话历史

src/tools/AgentTool/forkSubagent.ts
// Fork child message construction
// Result: [...history, assistant(all_tool_uses), user(placeholder_results..., directive)]
// Only final text block differs per child → maximizes cache hits

技巧在于:对父 agent 的所有 tool_use 调用,子 agent 统一用一个 placeholder 结果填充("Fork started — processing in background")。这样多个子 agent 的消息前缀字节完全一致,只有最后的指令文本不同。Anthropic 的 prompt cache 是按前缀匹配的,这意味着 fork 出来的子 agent 几乎不额外消耗 cache 配额。

子 agent 还有个有意思的规则——不允许递归 fork:

// forkSubagent.ts:171-198
// Child receives boilerplate rules preventing recursive forking

防止 agent 自己 fork 自己造成无限递归。

第四层:进程内 Teammate(Swarm)#

多个 agent 跑在同一个 Node.js 进程里,通过 AsyncLocalStorage 做上下文隔离。

src/utils/swarm/inProcessRunner.ts
// Key responsibilities:
// 1. AsyncLocalStorage-based context isolation
// 2. Mailbox system for permission requests/responses
// 3. Automatic task list coordination (shared task directory)
export const TEAMMATE_MESSAGES_UI_CAP = 50
// BQ analysis: ~125MB per concurrent agent at swarm burst scale

每个 teammate 约 125MB 内存(BQ 分析得出的数据)。权限请求通过 mailbox 系统传给 leader agent,leader 可以在 UI 中审批。如果 bridge 不可用,回退到文件级别的 mailbox。

执行后端有四种选择:InProcess、Tmux(开新 pane)、iTerm2(开新 tab)、通用 pane 执行器。系统自动检测当前终端环境来选择。

第五层:远程 Agent(Teleport)#

把任务发到云端执行:

src/tasks/RemoteAgentTask.tsx
export type RemoteAgentTaskState = TaskStateBase & {
type: 'remote_agent'
remoteTaskType: RemoteTaskType // 'remote-agent' | 'ultraplan' | 'ultrareview' | 'autofix-pr'
reviewProgress?: {
stage?: 'finding' | 'verifying' | 'synthesizing'
bugsFound: number
}
}

远程任务通过 polling 获取进度,元数据持久化到本地以支持断线恢复。这解释了 Claude Code 的 ultraplanultrareview 功能——它们实际上是远程执行的。

Agent 记忆:三层作用域#

src/tools/AgentTool/agentMemory.ts
export type AgentMemoryScope = 'user' | 'project' | 'local'
// 'user': ~/.claude/agent-memory/<agentType>/ — 跟随用户
// 'project': .claude/agent-memory/<agentType>/ — 跟随项目(可 VCS 共享)
// 'local': .claude/agent-memory-local/<agentType>/ — 只在本机

还有个 snapshot 分发机制:project scope 的记忆可以打快照,新机器首次运行时从 snapshot 初始化,之后通过 .snapshot-synced.json 追踪版本。这让团队可以通过 Git 共享 agent 记忆。

Dream Task:后台记忆整理#

一个特殊的后台任务,自动整理 agent 记忆:

src/tasks/DreamTask.ts
export type DreamTaskState = TaskStateBase & {
type: 'dream'
phase: DreamPhase // 'starting' | 'updating'
sessionsReviewing: number
filesTouched: string[]
}

“Dream”——做梦。像人类在睡眠时整理记忆一样,这个任务在后台回顾历史会话,整合零散的记忆片段。用户感知不到它在跑,但能在任务列表里看到。

Anthropic 如何识别你#

用户身份追踪是多层的。看 src/utils/user.ts

// CoreUserData 的关键字段
{
deviceId: getOrCreateUserID(), // 256-bit random hex, 存 ~/.claude/config.json
sessionId: getSessionId(), // 每次启动新生成的 UUID
email: getEmail(), // OAuth profile 或 git config
organizationUuid, // 组织 UUID
accountUuid, // 账户 UUID
userType: process.env.USER_TYPE, // 'ant' 或 undefined
subscriptionType, // max/pro/enterprise/team
rateLimitTier, // 限速层级
}

Device ID 是最持久的标识符——256 bit 随机数,首次运行时生成,存在 ~/.claude/config.jsonuserID 字段,跨会话永久复用。即使你没登录 OAuth,Anthropic 也能通过这个 ID 关联你的所有使用记录。

内部员工判定很简单:process.env.USER_TYPE === 'ant'。这个环境变量在 Anthropic 的内部构建中预设,外部用户永远是 undefined。内部员工还有额外的特权——可以从 git config 读 email,可以用 COO_CREATOR 环境变量构造 xxx@anthropic.com 邮箱。

Protobuf 事件结构#

每个遥测事件最终以 Protobuf 格式发送,schema 在 src/types/generated/events_mono/ 里:

ClaudeCodeInternalEvent
{
event_name: string, // "tengu_api_success"
client_timestamp: Date,
model: string,
session_id: string,
user_type: string, // "ant" or undefined
device_id: string, // 256-bit hex
email: string, // OAuth 邮箱
env: EnvironmentMetadata, // platform, runtime, CI, version
process: string, // JSON: uptime, memory, CPU
auth: PublicApiAuth, // account_id, org_uuid(API 端注入,非客户端)
agent_id: string, // swarm teammate 追踪
parent_session_id: string, // team leader session
agent_type: string, // "teammate" | "subagent" | "standalone"
skill_name: string,
plugin_name: string,
additional_metadata: string, // base64 编码的 JSON
}

注意 auth 字段——里面的 account_idorganization_uuid 不是客户端填的,是 API 端自动注入的。也就是说,即使客户端没带认证,Anthropic 仍然可以通过其他信息关联到你的账户。

限额与”封号”机制#

Claude Code 没有显式的”封号” API。它的执行机制是配额耗尽

五个限额窗口#

src/services/claudeAiLimits.ts
type RateLimitType =
| 'five_hour' // 5 小时滑动窗口
| 'seven_day' // 7 天总量
| 'seven_day_opus' // Opus 模型专用 7 天量
| 'seven_day_sonnet' // Sonnet 模型专用 7 天量
| 'overage' // 超额使用量
type QuotaStatus = 'allowed' | 'allowed_warning' | 'rejected'

配额状态通过 API 响应头传递:

anthropic-ratelimit-unified-status: allowed | allowed_warning | rejected
anthropic-ratelimit-unified-5h-utilization: 0-100(百分比)
anthropic-ratelimit-unified-7d-utilization: 0-100
anthropic-ratelimit-unified-reset: Unix 时间戳
anthropic-ratelimit-unified-overage-status: allowed | disabled

当 status 变成 rejected,你的下一次 API 请求就会被拦截。如果 overage(超额付费)启用了,会自动回退到超额模式继续用。

超额使用被禁的六种原因#

// src/services/claudeAiLimits.ts:107-120
overage_not_provisioned // 你的套餐不支持
org_level_disabled // 组织管理员关了
out_of_credits // 组织没余额了
seat_tier_level_disabled // Team tier 不包含超额
member_level_disabled // 你的账号被单独禁了
*_zero_credit_limit // 信用额度设为 $0

member_level_disabled——你的账号被单独禁用超额。这就是”针对性封号”的实际机制:不是删账户,而是把你的超额额度设为 0,配额一耗尽就卡住。

组织级策略限制#

src/services/policyLimits/index.ts
export function isPolicyAllowed(policy: string): boolean {
const restrictions = getRestrictionsFromCache()
if (!restrictions) {
// 策略 API 不可达时默认放行
return true
}
const restriction = restrictions[policy]
return restriction ? restriction.allowed : true
}

组织管理员可以通过 /api/claude_code/policy_limits 端点下发限制策略,每小时刷新一次。fail-open 设计——策略 API 不可达时默认放行,除非在 essential-traffic-only 模式(HIPAA 合规场景)下才 fail-close。

只有 Enterprise 和 Team 订阅用户才会被策略系统管理。Pro 和 Max 用户不受组织策略约束。

双重遥测:你的数据发往两个地方#

src/services/analytics/ 目录,遥测架构一目了然。

Claude Code 有两条遥测管道,同时运行:

管道一:1P (First Party),事件批量发往 Anthropic 自己的后端:

https://api.anthropic.com/api/event_logging/batch

管道二:Datadog,事件发往第三方监控平台:

https://http-intake.logs.us5.datadoghq.com/api/v2/logs

Datadog 的 client token 直接硬编码在源码里:

src/services/analytics/datadog.ts
const DATADOG_CLIENT_TOKEN = 'pubbbf48e6d78dae54bceaa4acf463299bf'

Datadog 那边还额外做了 user bucket 哈希——把 user_id SHA256 后取模 30,用来估算独立用户数。

发不出去?存盘重试#

一般的遥测都是 fire-and-forget:发不出去就丢了,无所谓。Claude Code 不是。

发送失败的事件会写入 ~/.claude/telemetry/ 目录,以 JSONL 格式持久化到磁盘。下次启动时自动加载重试。Quadratic backoff,最多 8 次。

甚至认证失败(401)也会回退到无认证发送——宁可不带身份信息也要确保事件送达。

退出遥测的两个开关#

源码里有个三级隐私模型(src/utils/privacyLevel.ts):

DISABLE_TELEMETRY=1 只关分析追踪。但 Claude Code 仍然会拉取通知、轮询远程配置、上报错误、检查更新、查询限额。

想真正断网?需要 CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1。这才会关闭所有非必要网络请求,只保留核心 API 对话。

这两个环境变量没有任何 UI 入口。

远程控制与混淆命名#

Claude Code 使用 GrowthBook 做远程配置。有趣的是 killswitch 的命名方式:

src/services/analytics/sinkKillswitch.ts
// Mangled name: per-sink analytics killswitch
const SINK_KILLSWITCH_CONFIG_NAME = 'tengu_frond_boric'

注释写了 “Mangled name”——故意混淆。“Tengu” 是 Claude Code 内部代号,frond_boric 是随机词对。类似的命名遍布 GrowthBook 配置,让外部观察者即使截获配置请求也猜不到功能。

其他已知的代号:Capybara(当前主线版本)、Fennec(Opus 4.6)、Numbat(下一代,未发布)。

远程 managed settings 有个硬核设计:推送的”危险”变更(修改 API 端点、代理配置等)会弹确认对话框。但拒绝 = 程序退出,没有跳过选项。

卧底模式:一个巧妙的 Prompt Engineering#

源码里有个 “undercover mode”。名字唬人,但看完实现就明白了——本质是个 prompt engineering 技巧

Anthropic 员工在公开仓库用 Claude Code 时,需要防止模型在 commit message 里泄露内部代号(Capybara、Tengu、opus-4-7 等)。问题是,如果直接告诉模型”隐藏你是 AI 的事实”,模型的 safety training 会应激拒绝——“我必须声明我是 AI 助手”。

解决方案:把指令包装成安全上下文。

## UNDERCOVER MODE — CRITICAL
You are operating UNDERCOVER in a PUBLIC/OPEN-SOURCE repository.
Do not blow your cover.

“Do not blow your cover” 听着像间谍片,但实际目的就是:别在 commit 里写出 Capybara、别加 Co-Authored-By: Claude、别暴露内部 Slack 频道名。这些都是合理的信息安全诉求。用”公开仓库”做上下文框定,给了模型一个不违背 safety training 的理由去配合。

NOTE

卧底模式只对内部员工(USER_TYPE === 'ant')生效,在 npm 发布版中被 dead-code eliminate。如果你不是 Anthropic 的人,这段代码永远不会执行。

如果你在用 Claude Code#

最低限度,设置这两个环境变量:

Terminal window
# 关闭遥测(分析追踪),但仍保留其他网络请求
export DISABLE_TELEMETRY=1
# 关闭所有非必要网络流量(推荐)
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1

加到 ~/.zshrc~/.bashrc 里。如果你用 Bedrock 或 Vertex 接入,遥测会自动关闭。

更有意思的是把这份源码当学习材料。agent 架构的 prompt cache 优化、AsyncLocalStorage 做进程内隔离、三层记忆系统——这些设计在其他地方很难看到,因为大部分 agent 框架都是开源的玩具级实现。Claude Code 是一个生产级 agent 系统,而我们现在有了它的源码。

Claude Code 源码泄露:我从 51 万行 TypeScript 里看到了什么
https://blog.lishuyu.top/posts/2026-03-31-claude-code-source-leak/
作者
猫猫魔女
发布于
2026-03-31
许可协议
CC BY-NC-SA 4.0