Claude Code UI

从 claude-code v2.1.88 npm 源码中提取的完整架构解析
1,902 files · 98 MB · TypeScript + React Ink · 24 板块
21
解析板块
1,902
源码文件
561
Feature Flags
665
遥测事件
5 层
Agent 架构
20+
语音语言

1 Clawd 吉祥物

用 Unicode 方块字符绘制的品牌形象,3行高,有 4 个姿势 + 动画。点击会触发蹲跳或左顾右盼动画 (60ms/帧)

▐▛███▜▌ ▝▜█████▛▘ ▘▘ ▝▝
default 默认
▗▟▛███▜▙▖ ▜█████▛ ▘▘ ▝▝
arms-up 举手
▐▟███▟▌ ▝▜█████▛▘ ▘▘ ▝▝
look-left 看左
▐▙███▙▌ ▝▜█████▛▘ ▘▘ ▝▝
look-right 看右

2 Buddy 虚拟宠物系统

18 种物种 × 6 种眼型 × 8 种帽子 × 5 个稀有度 × 1% 闪光。基于 userId 哈希确定性生成,不可作弊

__ <(· )___ ( ._> `--´
duck
鸭子
(·> || _(__)_ ^^^^
goose
.----. ( · · ) ( ) `----´
blob
果冻
/\_/\ ( · ·) ( ω ) (")_(")
cat
/^\ /^\ < · · > ( ~~ ) `-vvvv-´
dragon
.----. ( · · ) (______) /\/\/\/\
octopus
章鱼
/\ /\ ((·)(·)) ( >< ) `----´
owl
猫头鹰
.---. (·>·) /( )\ `---´
penguin
企鹅
_,--._ ( · · ) /[______]\ `` ``
turtle
海龟
· .--. \ ( @ ) \_`--´ ~~~~~~~
snail
蜗牛
.----. / · · \ | | ~`~``~`~
ghost
幽灵
=~(______)~= =~(· .. ·)~= ( .--. ) (_/ \_)
axolotl
蝾螈
n______n ( · · ) ( oo ) `------´
capybara
水豚
n ____ n | |· ·| | |_| |_| | |
cactus
仙人掌
.[||]. [ · · ] [ ==== ] `------´
robot
机器人
(\__/) ( · · ) =( .. )= (")__(")
rabbit
兔子
.-o-OO-o-. (__________) |· ·| |____|
mushroom
蘑菇
/\ /\ ( · · ) ( .. ) `------´
chonk
胖猫

稀有度 & 概率

★ Common 60%
★★ Uncommon 25%
★★★ Rare 10%
★★★★ Epic 4%
★★★★★ Legendary 1%

帽子配件

 
none
\^^^/
crown 皇冠
[___]
tophat 礼帽
-+-
propeller
( )
halo 光环
/^\
wizard 巫师
(___)
beanie 毛线帽
,>
tinyduck 小鸭

眼型

· × @ °

属性系统(源码深度解析)

稀有度分布
Common 60%
Uncommon 25%
Rare 10%
Epic 4%
Legendary 1%
Shiny 1%
5 项属性(纯装饰)
DEBUGGING: 1-100
PATIENCE: 1-100
CHAOS: 1-100
WISDOM: 1-100
SNARK: 1-100
设计巧思
Bones: 从 userId 每次重算,不存配置
Soul: AI 生成名字+性格,存一次
PRNG: Mulberry32 确定性随机
精灵图: 3 帧 × 5行 × 12字符

宠物由什么决定(源码逐行验证)

companion.ts — companionUserId() + roll()
function companionUserId(): string { const config = getGlobalConfig() return config.oauthAccount?.accountUuid // 优先级 1: OAuth 账户 UUID ?? config.userID // 优先级 2: 本地设备 ID ?? 'anon' // 优先级 3: 兜底 } // 种子 = hash(userId + "friend-2026-401") // → Mulberry32 PRNG → 确定性生成所有属性 // 同一个 userId 永远得到同一个宠物
ID 来源 什么时候有 换设备会变? 存在哪
accountUuid(优先) /login 登录了 Claude.ai 不变——同账号同宠物 ~/.claude.json → oauthAccount.accountUuid
userID(备选) 首次启动自动生成 UUID 变——新设备新 ID 新宠物 ~/.claude.json → userID
'anon'(兜底) 都没有(极少见) 全球同一个宠物 不存储
关键:登录了 Claude.ai 的用户(有 accountUuid),在任何设备上得到同一个宠物。没登录的用户(只有 userID),换电脑/删 ~/.claude.json 就变了。不是 IP、不是硬件 ID、不是邮箱。

属性分配算法(源码)

companion.ts — rollStats() + rollRarity()
稀有度抽取(加权随机,种子确定 → 结果确定) roll = prng() × 100 0-60 → Common 帽子: none(Common 永远没帽子) 60-85 → Uncommon 帽子: 8 选 1 85-95 → Rare 帽子: 8 选 1 95-99 → Epic 帽子: 8 选 1 99-100 → Legendary 帽子: 8 选 1 属性分配(每个宠物有 1 项巅峰 + 1 项垃圾) peak stat = floor + 50 + random(0-30) ← 一项特别高 dump stat = floor - 10 + random(0-15) ← 一项特别低 其他 3 项 = floor + random(0-40) ← 正常范围 floor 由稀有度决定: Common=5 Uncommon=15 Rare=25 Epic=35 Legendary=50 示例(Rare, floor=25): DEBUGGING: 87 ← peak (25+50+12) PATIENCE: 41 ← normal (25+16) CHAOS: 58 ← normal (25+33) WISDOM: 18 ← dump (25-10+3) SNARK: 39 ← normal (25+14) Shiny: 独立 1% 概率,rng() < 0.01 → true(纯装饰)

防作弊设计

Bones 不存配置 每次从 userId 重算 → 编辑 config 没用
Soul 只存一次 AI 生成名字+性格 → 首次孵化后存入,不重生成
合并时 bones 覆盖 getCompanion(): { ...stored, ...bones } → bones 最后覆盖
唯一漏洞 新建账号 → 新 accountUuid → 重新抽。但名字性格要重新孵化

3 宠物交互效果

对话气泡 (AI 评论你的代码) + 抚摸爱心特效 + 待机动画序列

Buddy — 对话气泡 + Pet 爱心
这段代码写得真整洁~
(\__/) ( ✦ ✦ ) =( .. )= (")__(") Mochi

待机动画序列

15 步循环 @ 500ms/tick:大部分时间静止(frame 0),偶尔扭动(frame 1-2),极少眨眼

[0, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 2, 0, 0, 0]
↑fidget ↑blink ↑fidget

4 加载动画系统

4 种加载效果:标准 Spinner → 微光扫光 → 超时变红 → 减少动画模式

Spinner 动画演示
标准 Spinner: · · ✢ ✳ ✶ ✻ ✽ ✻ ✶ ✳ ✢ (循环)
微光扫光: Claude is thinking...
超时变红: 颜色插值 rgb(claude) → rgb(171,43,63)
减少动画: 2秒周期:1秒亮 / 1秒暗

5 输入模式 & 提示符

3 种交互模式 + Vim + Agent 染色。每种模式有独立的提示符颜色和行为

输入你的消息... Normal 模式
PLAN 描述你想实现的功能... Plan 模式
AUTO AI 自动执行,跳过权限... Auto 模式
cursor -- NORMAL --

Agent 颜色染色

Red
Blue
Green
Yellow
Purple
Orange
Pink
Cyan

6 欢迎屏

启动时显示 Clawd + 版本信息 + 多列 Feed 卡片(最近活动、更新日志、新手引导)

Claude Code — 启动欢迎屏
▐▛███▜▌ ▝▜█████▛▘ ▘▘ ▝▝
Claude Code v1.0.33 (claude-opus-4-6)
~/Projects/my-project

Recent activity

修复法考数据导入2h ago
部署 AutoScan v21d ago
百陶会网站更新3d ago

What's new

Voice mode support3d ago
Buddy companion1d ago
Status line hooks5d ago

Tips for getting started

✓ Create CLAUDE.md
  Set up MCP server
  Try /plan for tasks

7 全屏模式布局

Ctrl+O 切换。三层结构:ScrollBox(消息区) + Bottom(输入+宠物) + StatusLine(状态栏)

帮我修复这个 bug,用户登录后 token 没有刷新
让我先看看相关代码...
▸ bash
grep -rn "refreshToken" src/auth/
找到了问题。在 src/auth/session.ts:47 里,token 刷新逻辑缺少了 await...
shift+tab mode · /help · esc cancel
.----. ( ✦ ✦ ) ( ω ) (")_(") Mochi
opus-4-6 default ~/Projects 12.3k↑ 4.2k↓ $0.052

8 主题色板

6 套主题 (dark/light/daltonized/ansi) + auto 跟随系统。70+ 语义色彩变量

claude
rgb(215,119,87)
permission
rgb(87,105,247)
autoAccept
rgb(135,0,255)
bashBorder
rgb(255,0,135)
success
rgb(44,122,57)
error
rgb(171,43,63)
warning
rgb(150,108,30)
planMode
rgb(0,102,102)

彩虹色 (UltraThink)

Agent 专用色 (8色)

R
B
G
Y
P
O
K
C

/buddy 彩虹预告 (4月1-7日)

/buddy

启动时状态栏闪现 15 秒

9 Diff 高亮

行级 + 词级 Diff 着色,用于 Edit 工具结果展示

src/auth/session.ts
- const token = getToken(session)
+ const token = await refreshToken(session)
词级 Diff:
const token = getToken(session)
const token = await refreshToken(session)

10 权限系统 — 7 层决策管道

不是简单的弹窗问 y/n。每个工具调用经过 7 层检查,越靠近危险操作的层级越不可绕过。即使用户选"全部放行",改 .bashrc 仍然要确认

▸ bash
rm -rf node_modules && npm install
Run this command?
[y] Yes [n] No [a] Always [?] Explain

决策管道(源码 hasPermissionsToUseToolInner)

1a 整个工具被拒绝 → deny settings.json 配了 deny 规则
1b 整个工具必须询问 → ask 除非沙箱可以自动放行
1c 工具自己的 checkPermissions() → 每个工具有自己的逻辑 Bash 最复杂
1f 内容级 ask 规则 → bypass-immune 即使全放行模式也必须问
1g 安全检查 — 不可绕过 → bypass-immune .git/.claude/.bashrc 保护
2a bypassPermissions 模式 → allow 跳过后续检查(但 1f/1g 不可跳)
3 没有匹配规则 → ask 弹出确认框给用户

受保护的文件和目录(即使 bypass 也要确认)

危险文件 DANGEROUS_FILES
.gitconfig .gitmodules .bashrc .bash_profile .zshrc .zprofile .profile .mcp.json .claude.json
原因:改这些文件可以让攻击者在 shell 启动/git 操作时执行任意代码
危险目录 DANGEROUS_DIRECTORIES
.git/ .vscode/ .idea/ .claude/
原因:git hooks 可执行代码、IDE 配置可自动运行任务

Bash 危险命令模式(Auto 模式永不自动放行)

python node ruby perl npx npm run yarn run bash sh eval exec sudo ssh xargs deno php lua

这些命令可以执行任意代码。即使用户之前允许了 npm install:*npm run build 仍然要重新确认

Windows 路径攻击防御

\\\\server\\share UNC 路径 → NTLM 凭证泄漏
file.txt::$DATA NTFS 备用数据流 → 绕过检查
CLAUDE~1 8.3 短文件名 → 绕过 .claude 保护
.git. 尾随点号 → 绕过精确匹配

所有路径检查大小写不敏感:.cLauDe/Settings.locaL.json 无法绕过

7 个规则来源(按优先级)

policySettings 组织策略(管理员设置) 不可删除
flagSettings GrowthBook 远程配置 不可删除
cliArg 命令行参数 --allowedTools 会话内有效
userSettings ~/.claude/settings.json(全局) 用户可编辑
projectSettings .claude/settings.json(项目级) 提交到 git
localSettings .claude/settings.local.json(本地) 不提交 git
session 本次会话中点了 "Always allow" 关闭即失效
设计哲学:同心圆分层防御
最外层:模式检查 — bypassPermissions 可以跳过
中间层:规则匹配 — 用户配置可以覆盖
内层:工具自检 — 每个工具自己判断(Bash 解析命令 AST)
核心层:安全检查 — 不可绕过(.git / .claude / shell 配置 / Windows 路径攻击)
外层可以放松,核心层永远不能。改 .bashrc 影响的是用户的整个系统,不是项目。

11 多 Agent 团队协作体系

5 层 Agent 架构:从单体主 Agent 到多 Worker 团队编排,包含 Fork 分身、Team Swarm、Coordinator 模式。源码级完整解析

5 层架构总览

Agent 架构层级
Layer 5 Coordinator Mode 纯编排者,自己不用工具,只指挥 Worker Layer 4 Team / Swarm 共享任务列表 + SendMessage 消息总线 Layer 3 Fork Subagent 继承父上下文的分身,共享 prompt cache Layer 2 Specialized Agents 6 种内置专用子代理(Explore/Plan/Verify...) Layer 1 Main Agent 你对话的主体,拥有全部工具

Layer 2: 内置专用 Agent(6 种)

GP
general-purpose
默认子代理模型 | 全部工具 *
通用任务:搜索代码、多步骤执行。不指定 subagent_type 时的默认选择
Ex
Explore
Haiku 模型(快+便宜) | 只读
代码探索专家。跳过 CLAUDE.md,标记 one-shot(用完即弃不保留 ID)
Pl
Plan
继承主模型 | 只读
架构规划师。输出步骤方案 + 关键文件清单 + 权衡取舍
Ve
Verification
继承主模型 | 只读 | 对抗性
不是确认能用,而是试图打破它。必须包含对抗性探测,输出 VERDICT
Gu
claude-code-guide
Glob/Grep/Read/Web
回答关于 Claude Code 本身的使用问题(功能、设置、MCP)
SL
statusline-setup
Read/Edit
配置状态栏显示内容

Verification Agent 的对抗性设计(源码摘录)

verificationAgent.ts — System Prompt
核心理念:
"Your job is not to confirm the implementation works — it's to try to break it."
自我警告的两个失败模式:
1. 验证回避 — 读代码后直接写 PASS,不实际运行
2. 被前 80% 迷惑 — 看到 UI 漂亮就通过,没注意一半按钮不工作
内置的借口识别器:
"The code looks correct based on my reading" → Run it.
"The implementer's tests already pass" → Verify independently.
"This is probably fine" → Run it.
"I don't have a browser" → Check for playwright MCP tools.
"This would take too long" → Not your call.
必须对抗性探测(至少一个):
并发竞争 | 边界值(0,-1,空串,MAX_INT) | 幂等性 | 孤儿操作
输出格式强制:
### Check: [what you're verifying] **Command run:** [exact command] **Output observed:** [actual output] **Result: PASS** (or FAIL) ... VERDICT: PASS | FAIL | PARTIAL

Layer 3: Fork Subagent — 自我分身

省略 subagent_type 参数时触发。继承父对话完整上下文 + system prompt,共享 prompt cache(关键优化)

Fork 分身
上下文:继承全部对话历史
Prompt:指令式(做什么)
缓存:共享父 prompt cache
模型:必须继承(否则缓存失效)
用途:调研、不想污染主上下文的工作
普通 Agent
上下文:从零开始
Prompt:描述式(背景+做什么)
缓存:独立缓存
模型:可指定不同模型
用途:专业任务(探索/规划/验证)
forkSubagent.ts — Fork 子进程硬规则
STOP. READ THIS FIRST. You are a forked worker process.
1. 你是分身,不是主体。不要再 fork 自己(防递归) 2. 不要对话、问问题、建议下一步 3. 安静地用工具,最后报告一次 4. 严格限定在指令范围内 5. 报告 < 500 字,必须以 Scope: 开头
输出格式: Scope: <回显你的范围> Result: <关键发现> Key files: <相关文件路径> Files changed: <修改列表 + commit hash> Issues: <问题清单>

Layer 4: Team / Swarm — 团队协作

通过 TeamCreate / SendMessage / TaskCreate 实现多 Agent 长期协作

Team Workflow — 协作流程
1 TeamCreate → 创建 ~/.claude/teams/{name}/config.json + 任务目录
2 TaskCreate → 创建任务到共享任务列表 ~/.claude/tasks/{name}/
3 Agent(team_name, name) → 派遣 teammate,加入团队
4 TaskUpdate(owner) → teammate 认领/完成任务
5 SendMessage → 互发消息(自动投递,不用轮询)
6 shutdown_request → 任务完成,优雅关闭 teammate

通信协议

SendMessage(to:"researcher") 按名字发给 teammate
SendMessage(to:"*") 广播所有 teammate
SendMessage(to:"uds:/tmp/cc.sock") 跨进程 (Unix Socket)
SendMessage(to:"bridge:session_...") 跨机器 (Remote)

Layer 5: Coordinator Mode — 最高级编排

Coordinator 是纯编排者,自己不用工具,只指挥 Worker。4 阶段流水线

Coordinator Mode — 4 阶段工作流
Phase 1 Research — 并行
├─ Worker-A 调查 auth 模块 bug exploring
├─ Worker-B 调查测试覆盖 reading
└─ Worker-C 查看相关 PR 历史 git log
Phase 2 Synthesis — Coordinator 自己做
读取所有 Worker 报告 → 理解问题 → 写出精确的修复方案
"based on your findings, fix the bug"
"修复 src/auth/validate.ts:42 空指针,Session.user 过期时为 undefined,加 null check 返回 401"
Phase 3 Implementation — 单 Worker 串行
└─ Worker-A 续用(已有文件上下文) editing validate.ts
Phase 4 Verification — 必须新建 Worker
└─ Worker-D 独立验证(新鲜眼光) running tests

Continue vs Spawn 决策表

情况 选择 原因
调研者探索的文件正好要改Continue文件已在上下文
调研范围广但实现范围窄Spawn避免噪音
纠正失败或扩展工作Continue保留错误上下文
验证别人写的代码Spawn新鲜眼光,避免先入为主
方向完全错误的重试Spawn错误上下文会锚定思维
完全无关的新任务Spawn无可复用上下文

通知协议(源码格式)

task-notification XML
<task-notification>
<task-id>agent-a1b</task-id>
<status>completed</status>
<summary>Agent "Investigate auth bug" completed</summary>
<result>Found null pointer in src/auth/validate.ts:42...</result>
<usage>
<total_tokens>12847</total_tokens>
<tool_uses>8</tool_uses>
<duration_ms>34200</duration_ms>
</usage>
</task-notification>

隔离模式

默认
共享工作目录
worktree
Git worktree 隔离副本,改完返回 diff
remote
远程 CCR 沙箱 (Ant-only)

并发管理规则

只读调研 随便并行
写入实现 同批文件串行
验证 可和不同区域并行

深度解析 A:Agent 任务的完整生命周期

从主 Agent 决定派活到子 Agent 返回结果,每一步的源码位置与数据流

Agent 生命周期 — 8 步完整流程(源码路径标注)
Step 1 触发 tool_use 主 Claude 返回 tool_use block → name: "Agent", input: { prompt, subagent_type?, model? } // AgentTool.tsx:196 — buildTool({ name: "Agent" }) Step 2 路由决策 — 选哪种 Agent IF subagent_type 指定 → 查 activeAgents 列表匹配 ELSE IF fork 实验开启 → Fork 分身(继承上下文) ELSE → general-purpose(默认兜底) // AgentTool.tsx:318-356 — effectiveType = subagent_type ?? ... Step 3 模型解析 agent.model ?? input.model ?? 父模型继承 Explore → Haiku (外部用户) | 继承 (内部) Plan/Verify/GP → 继承父模型 // model/agent.ts:37-95 — getAgentModel() Step 4 构建 System Prompt Fork: 直接复制父 renderedSystemPrompt(字节级一致 → 命中 prompt cache) Normal: agent.getSystemPrompt() → enhanceSystemPromptWithEnvDetails() // AgentTool.tsx:483-541 — buildEffectiveSystemPrompt() Step 5 组装工具池 tools: ['*'] → 全部可用工具(过滤后) tools: ['Read','Grep'] → 只给指定工具 disallowedTools 黑名单后移除 异步 Agent 额外限制:只允许 ASYNC_AGENT_ALLOWED_TOOLS 里的 // agentToolUtils.ts:62-225 — resolveAgentTools() Step 6 创建隔离上下文 createSubagentContext() → 克隆文件缓存、新 AbortController、独立 agentId setAppState: () => {} — 子 Agent 不能修改父 UI 状态 queryTracking.depth: parent.depth + 1 — 追踪嵌套层级 // forkedAgent.ts:345-462 — createSubagentContext() Step 7 执行 — 同步 or 异步 Sync: 阻塞主循环 → query() → 拿到完整结果 Async: 注册 LocalAgentTask → runAsyncAgentLifecycle() → 立即返回 task ID 判断依据: run_in_background | agent.background | isCoordinator | proactiveMode // AgentTool.tsx:552-567, agentToolUtils.ts:508-686 Step 8 结果回收 finalizeAgentTool() → 提取最后 assistant message 的 text 内容 统计: totalTokens + totalToolUseCount + totalDurationMs 异步完成时: 注入 <task-notification> XML 到父对话 // agentToolUtils.ts:276-357 — finalizeAgentTool()

子 Agent 上下文隔离细节

隔离(clone/新建)
readFileState — 克隆 LRU 文件缓存
abortController — 新建子控制器(链接到父)
agentId — 新 UUID
toolDecisions — 清空(or Fork 克隆)
setAppState — 空函数(不影响父 UI)
queryTracking — 新 chainId + depth+1
可选共享(opt-in)
shareSetAppState — 共享 UI 状态(进程内队友用)
shareSetResponseLength — 贡献到父指标
shareAbortController — 继承父 abort(交互式)
注意: Fork 子进程看得到父对话全文;
普通 Agent 只看到自己的 prompt,看不到父对话

3 种 Spawn 执行模式

Split-Pane(默认)
Leader 左 + Teammate 右
共享 tmux 窗口
spawnMultiAgent.ts:305-539
In-Process(同进程)
AsyncLocalStorage 隔离
500ms 轮询 mailbox
inProcessRunner.ts:471+
Separate Window(独立)
每个 Teammate 独立 tmux 窗口
Legacy 模式
spawnMultiAgent.ts:545-753

SendMessage 消息路由机制

SendMessageTool.ts — 路由决策树
收到 SendMessage(to, message) │ ├─ to === "*"广播: 发给所有 teammate(除自己) │ ├─ to === "teammate-name" │ ├─ 在 agentNameRegistry 查找 │ ├─ 运行中 → queuePendingMessage() 直接入队 │ └─ 已停止 → resumeAgentBackground() 唤醒它 │ ├─ to === "user"发给用户(AskUserQuestion 语义) │ └─ Swarm 模式 → 写入文件邮箱 ~/.claude/teams/{teamName}/mailbox/{recipientName}/ 每条消息 = JSON { from, text, timestamp, color } 结构化消息类型: shutdown_request — Leader 请求 Teammate 退出 shutdown_response — Teammate 同意/拒绝(带原因) plan_approval_response — Leader 批准/拒绝 Teammate 的方案

工具过滤规则(谁能用什么)

规则 筛选内容 源码
ALL_AGENT_DISALLOWED AgentTool(防递归)、TaskOutputTool、EnterPlanMode、TaskStopTool、WorkflowTool constants/tools.ts
CUSTOM_AGENT_DISALLOWED 自定义 Agent 额外禁用的工具(安全边界) constants/tools.ts
ASYNC_AGENT_ALLOWED Read/Glob/Grep/WebSearch/WebFetch + Edit/Write/Bash + Skill/ToolSearch constants/tools.ts:55-71
IN_PROCESS_TEAMMATE 额外允许 TaskCreate/Get/List/Update + SendMessage + Cron 工具 constants/tools.ts:77-88
mcp__* 前缀 MCP 工具始终放行(不受上述规则限制) agentToolUtils.ts:82

深度解析 B:模型选择决策链

5 层优先级覆盖 + 每种 Agent 的默认模型 + Fast Mode 机制

model/model.ts — getMainLoopModel() 优先级
优先级 1 会话内 /model 切换 ← 最高 优先级 2 启动参数 --model 优先级 3 ANTHROPIC_MODEL 环境变量 优先级 4 用户 settings 配置 优先级 5 订阅等级默认值 ← 最低 ├── Max / Team Premium → Opus 4.6 (+ [1m] if enabled) └── Pro / Team Standard / Enterprise / PAYG → Sonnet 4.6

各 Agent 默认模型

Agent 默认模型 原因 可覆盖
Explore Haiku 4.5 快 + 便宜,只需读取/搜索 ANTHROPIC_SMALL_FAST_MODEL
Plan inherit(继承父) 需要强推理能力 input.model
Verification inherit(继承父) 需要同等能力才能有效对抗 input.model
general-purpose inherit(继承父) 通用,需完整能力 input.model
claude-code-guide inherit(继承父) 需要理解复杂文档 input.model
statusline-setup inherit(继承父) 简单配置 input.model

Fast Mode(快速模式)

原理
API 参数 speed: 'fast'
Opus 4.6 支持
同模型,更快输出,不是换模型
/fast 开关
定价(per 1M tokens)
Haiku 4.5 $1 / $5
Sonnet 4.6 $3 / $15
Opus 4.6 $5 / $25
Opus 4.6 (fast) $30 / $150
Fast = 6 倍价格

Plan Mode 自动升级规则

modelOptions.ts — Plan Mode 模型提升
// 进入 Plan Mode 时自动提升模型能力 IF userModel === 'opusplan' && permissionMode === 'plan'使用 Opus(只在 plan 模式用贵模型,执行时用便宜的) IF userModel === 'haiku' && permissionMode === 'plan'升级到 Sonnet(Haiku 推理太弱,规划必须升级) // 1M 上下文后缀在切换时保留 // Bedrock 地域前缀在子 Agent 间继承(数据驻留合规)

环境变量覆盖表

变量 作用
ANTHROPIC_MODEL主循环模型
ANTHROPIC_DEFAULT_OPUS_MODELOpus 别名对应的实际模型 ID
ANTHROPIC_DEFAULT_SONNET_MODELSonnet 别名对应的实际模型 ID
ANTHROPIC_DEFAULT_HAIKU_MODELHaiku 别名对应的实际模型 ID
CLAUDE_CODE_SUBAGENT_MODEL所有子 Agent 统一覆盖
ANTHROPIC_SMALL_FAST_MODELExplore Agent 专用模型
CLAUDE_CODE_DISABLE_FAST_MODE禁用 Fast Mode

深度解析 C:Claude 如何判断派哪个 Agent

不是规则引擎,是 LLM 自己通过 tool_use 选择。Agent 工具的 prompt 里嵌入了各子代理的描述

核心机制:不是路由表,是 tool_use
Claude 的 system prompt 里包含 Agent 工具的描述。每个可用的子代理类型及其 whenToUse 字段都被拼接进这个描述文本中。当 Claude 判断需要委派任务时,它返回一个 tool_use block,参数里包含 subagent_type 字段 —— 这不是程序逻辑选择的,是 模型自己决定的
prompt.ts — Agent 工具描述的动态生成
// prompt.ts: getPrompt(agentDefinitions, isCoordinator, allowedAgentTypes) 工具描述 = 静态头部 + 动态 Agent 列表 静态头部: "Launch a new agent to handle complex, multi-step tasks autonomously." "Available agent types and the tools they have access to:" 动态列表 (遍历 agentDefinitions): - general-purpose: General-purpose agent for researching... - Explore: Fast agent specialized for exploring codebases... - Plan: Software architect agent for designing... 每个 Agent 条目 = agentType + whenToUse + tools 列表 ↓ Claude 读完这些描述后,自行决定 subagent_type 的值

决策流程图

从用户消息到子 Agent 启动
用户: "帮我调研一下这个 repo 的目录结构" │ ▼ Claude API 返回 type: "tool_use" name: "Agent" input: { description: "explore repo structure", prompt: "Explore the directory structure of...", subagent_type: "Explore" ← 模型自选 } │ ▼ AgentTool.tsx 处理 1. effectiveType = "Explore" 2. selectedAgent = agents.find(a => a.agentType === "Explore") 3. 检查权限规则(deny rules) 4. 检查 MCP server 可用性 │ ▼ 模型解析 Explore.model = 'haiku' → getAgentModel() → Haiku 4.5 │ ▼ 构建 + 执行 getSystemPrompt() → 组装工具池 → createSubagentContext() → 发送 API 请求 → 子 Agent 开始工作

Claude 实际看到的工具定义(Agent 参数 schema)

API tools[] 里的 Agent 工具 input_schema
{ "description": string // 3-5 word summary "prompt": string // 完整任务描述 "subagent_type": enum // "general-purpose" | "Explore" | "Plan" | ... "model": enum // "sonnet" | "opus" | "haiku" (optional) "run_in_background": bool // 后台运行 "isolation": enum // "worktree" (optional) "name": string // Swarm 用,给 teammate 起名 "team_name": string // Swarm 用,加入哪个 team "mode": string // 多 Agent spawn 参数 } // subagent_type 和 model 是 enum —— 值域在编译时确定 // Claude 不能"发明"新的 agent 类型,只能选列表里有的
关键洞察
1. 不是 if-else 路由 — 选择权在模型,不在代码。代码只负责验证选择是否合法。
2. 工具描述是决策依据 — prompt.ts 把每个 Agent 的 whenToUse 拼进工具描述,Claude 读完自己选。
3. 可扩展 — 用户自定义 Agent(.claude/agents/ 目录下 .md 文件)会被自动加入列表,Claude 下次就能选它。
4. 安全边界 — 即使 Claude 选了某个 Agent,代码仍然检查权限规则(deny rules)和 MCP 可用性,不合法会拒绝。

Coordinator 深度:4 工具 vs Worker 全部工具

Coordinator 自己只有 4 个工具,不能读文件、不能执行命令。所有实际工作通过 Worker 完成

Coordinator(编排者)
Agent — 派遣新 Worker
SendMessage — 继续已有 Worker
TaskStop — 中止 Worker
SyntheticOutput — 格式化输出
不能读文件、不能执行命令、不能搜索
Worker(执行者)
Read / Edit / Write / Glob / Grep
Bash + 所有 Shell 工具
WebSearch / WebFetch
NotebookEdit / Skill / ToolSearch
Worktree 操作 + MCP 工具
不能 SendMessage(Worker 之间不能通信)

最重要的原则:必须自己消化再指挥

绝对不能写 "based on your findings, fix the bug"
错误示范 ✗
"Based on your findings, fix the auth bug"
→ 把"理解"的责任甩给 Worker
正确示范 ✓
"修复 src/auth/validate.ts:42 空指针。Session.user 过期时为 undefined,加 null check 返回 401"
→ 精确到文件:行号:原因:修法

Scratchpad — 跨 Worker 知识共享

功能:tengu_scratch flag 门控的共享文件目录
用途:研究 Worker 写入发现 → 实现 Worker 读取 → 验证 Worker 参考。无需权限确认。
省钱:完整调研 ~500 tokens,读 scratchpad ~20 tokens。多个 Worker 复用同一份研究结果。

12 Prompt Cache — 真正的技术壁垒

Anthropic 服务端前缀缓存机制。不是"砍什么上下文",而是"砍了之后怎么保证缓存不崩"。这是 Claude Code 多 Agent 成本比其他框架低 4 倍的核心原因

工作原理

每次 API 请求,Anthropic 服务器对请求内容的前缀做哈希。如果这次请求的前面部分和上次完全一样(byte-identical),那部分直接用缓存,只计算新增部分

Prompt Cache 命中原理
Round 1: [system prompt + msg1 + msg2 + msg3] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 全量计算,同时写入缓存 Round 2: [system prompt + msg1 + msg2 + msg3 + msg4] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 缓存命中!跳过(省 90% 费用) ^^^^ 只算这部分 Round 3: [system prompt + msg1 + msg2 + msg3 + msg4 + msg5] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 缓存命中! ^^^^ 只算新增 如果中间改了旧内容: Round 3: [system + msg1 + msg2_被截断 + msg3 + msg4 + msg5] ^^^^^^^^^^ 前缀从这里开始不匹配! 后面全部 cache miss → 全量重算 → 成本翻 4 倍

两级缓存时效(源码实证)

源码 promptCacheBreakDetection.ts 中明确定义了两个 TTL 阈值

promptCacheBreakDetection.ts — TTL 常量
// Anthropic's server-side prompt cache TTL thresholds to test. // Cache breaks after these durations are likely due to TTL expiration // rather than client-side changes. const CACHE_TTL_5MIN_MS = 5 * 60 * 1000 // 5 分钟 — 标准缓存 export const CACHE_TTL_1HOUR_MS = 60 * 60 * 1000 // 1 小时 — 白名单缓存
5 分钟缓存
适用:所有用户,默认行为
场景:一个 Agent 连续 10 轮工具调用(每轮间隔 < 5分钟)
限制:停下来思考超过 5 分钟,缓存过期
1 小时缓存
适用:服务端白名单控制
配置:tengu_prompt_cache_1h_config.allowlist
场景:主对话线程、长时间运行的 Agent

40 种 querySource 的缓存分级(完整源码映射)

源码中有 40 个 querySource 标识不同类型的 API 调用。每种调用的缓存策略不同,分三个级别

Tier 1 — 1 小时缓存(长期对话)

白名单控制。典型配置:{ allowlist: ["repl_main_thread*", "sdk", "agent:*"] }

querySource 场景 为什么需要 1h
repl_main_thread你和 Claude 的主对话你可能中间切出去查资料再回来,5分钟会过期
repl_main_thread:outputStyle:*主对话 + 自定义输出风格同上,不同风格是不同的缓存键
sdkSDK / API 集成调用应用集成场景,请求间隔可能较长
agent:builtin:ExploreExplore 搜索 Agent多轮搜索,每轮缓存前面的查询结果
agent:builtin:PlanPlan 规划 Agent规划多步骤,前面的分析不重算
agent:builtin:forkFork 分身继承父缓存,必须长 TTL 才有意义
agent:builtin:verificationVerification 验证 Agent多步骤验证(build→test→curl→adversarial)
agent:custom用户自定义 Agent / Skill自定义 Agent 的多轮循环
agent:default默认通用 Agent通用多步骤任务

Tier 2 — 5 分钟缓存(短期任务)

有缓存但不在白名单里,用标准 5 分钟 TTL

compact web_fetch_apply web_search_tool bash_extract_prefix side_question hook_agent hook_prompt auto_mode auto_mode_critique

Tier 3 — 不跟踪缓存(一次性任务)

源码注释:"short-lived forked agents where cache break detection provides no value — they run 1-3 turns with a fresh agentId each time"

speculation session_memory prompt_suggestion generate_session_title memdir_relevance agent_summary auto_dream away_summary extract_memories model_validation permission_explainer teleport_generate_title tool_use_summary_generation rename_generate_name marble_origami insights feedback skill_improvement_apply chrome_mcp magic_docs

谁有资格拿 1 小时缓存

claude.ts — should1hCacheTTL() 资格判断
userEligible = process.env.USER_TYPE === 'ant' // Anthropic 内部员工 → 直接有 || ( isClaudeAISubscriber() // 付费订阅用户(Pro/Team) && !currentLimits.isUsingOverage // 且没超额使用 )
Pro/Team 订阅 1h 缓存
Pro 超额中 降级到 5min
免费用户 只有 5min
Bedrock 需手动开启环境变量
翻译成人话
你正在用的这个对话repl_main_thread):Pro 订阅 → 1h 缓存。中间出去泡咖啡回来,之前的上下文还在缓存里,继续聊只付新增 token 的钱
你让 Claude 派的 Agentagent:builtin:*):同样 1h 缓存。Agent 跑 10 轮工具调用,每轮只付增量
背后的小任务(生成标题、提取记忆、预测建议):5min 或不缓存。跑完就扔
超额使用时:1h 缓存降级到 5min。每次暂停超过 5 分钟缓存就没了,成本显著上升

成本结构

API 响应里的 usage 字段揭示了缓存的三种 token 计费

API Response — usage 字段
{ "usage": { "input_tokens": 1000, // 常规输入 — 100% 价格 "cache_creation_input_tokens": 8000, // 创建缓存 — 125% 价格(贵 25%) "cache_read_input_tokens": 15000 // 命中缓存 — 10% 价格(省 90%) } }

10 轮 Agent 对话的成本对比

轮次 cache_creation cache_read input_tokens 等效成本
Round 110,0000012,500
Round 22,00010,00003,500
Round 32,00012,00003,700
...............
Round 102,00026,00005,100
合计 有缓存 ~45K token
无缓存 (全量计算) ~190K token

缓存省 ~75% 成本

核心机制:ContentReplacementState 状态机

这是整个 Agent 系统里最精妙的设计。不是简单的"大的截断",而是一个三态状态机,追踪每个 tool_result 的生命周期,确保缓存前缀永远 byte-identical

toolResultStorage.ts — 三态生命周期
type ContentReplacementState = { replacements: Map<string, string> // 已替换的 → 替换后内容(永久缓存) seenIds: Set<string> // 见过但没替换的 → 冻结名单 }
fresh
新的 tool_result
可以决定:保留原文 或 持久化到磁盘后替换为预览
预算超限时,最大的 fresh 先被替换
frozen
见过但没替换的
永远不能再替换了
改它 = 缓存前缀变了 = 所有后续 cache miss
mustReapply
之前替换过的
每轮必须用完全相同的替换内容重放
byte-identical → 缓存前缀稳定
每轮 API 调用前的处理流程
1 遍历所有 tool_result 消息
2 mustReapply → 用缓存的替换内容原样重放(零 I/O,byte-identical)
3 frozen → 不管多大都不动(改了缓存就废了)
4 fresh → 如果本消息总体超预算,最大的 fresh 持久化到磁盘并替换为预览
5 所有 fresh 的 ID 加入 seenIds → 下轮变成 frozen 或 mustReapply

大结果持久化到磁盘

超预算的 tool_result 被写到磁盘文件,模型收到的是固定格式的预览(确保所有后续轮次 byte-identical)

替换后模型看到的内容
<persisted-output> Tool output was 342.5 KB. Full output saved to: ~/.claude/projects/.../tool-results/toolu_xxx.json Preview: [前 ~2K 字符的预览内容] </persisted-output> // 这段替换文本在所有后续轮次 byte-identical 地重放 // 模型可以用 Read 工具去读完整文件

预算限制(源码常量)

constants/toolLimits.ts
export const DEFAULT_MAX_RESULT_SIZE_CHARS = 50_000 // 单个工具结果最大 50K 字符,超出持久化到磁盘 export const MAX_TOOL_RESULT_TOKENS = 100_000 // 单个工具结果最大 100K token(~400KB 文本) export const MAX_TOOL_RESULTS_PER_MESSAGE_CHARS = 200_000 // 单条消息里所有 tool_result 合计最大 200K 字符 // 防止 N 个并行工具每个 40K = 总共 400K 撑爆上下文

4 层递进压缩

上下文超限时不是直接报错,而是 4 层压缩依次触发。每层解决不同类型的膨胀

Layer 1 snipCompact 用户手动 /compact 剪切旧对话
Layer 2 microcompact 替换大 tool_result 为磁盘引用(ContentReplacementState)
Layer 3 contextCollapse 折叠历史消息为摘要,保留结构但压缩内容
Layer 4 autocompact 调 AI 生成全量摘要,最后手段

Layer 3 在 Layer 4 之前跑——如果折叠就够了,就不触发代价更高的 AI 摘要

Fork Subagent 为什么必须继承一切

Fork 的核心优势是共享父进程的 prompt cache。任何差异都会导致缓存前缀不匹配

runAgent.ts — Fork 必须 byte-identical 的配置
// Fork 时继承父的 thinking 配置 thinkingConfig: useExactTools // fork 路径 ? toolUseContext.options.thinkingConfig // 继承父的 : { type: 'disabled' } // 普通 agent 关掉 // Fork 必须继承的: 模型(换模型 = 缓存 miss) 工具列表(useExactTools = true) thinking 配置 system prompt(直接传 renderedSystemPrompt 字节,不重新生成) ContentReplacementState(继承父的替换映射) 任何一个变了 → API 请求前缀变了 → 缓存全废 → Fork 变得和新建一样贵

为什么其他 API 无法复制这个优势

Anthropic API
Prompt Cache:有(服务端自动)
缓存命中价格:原价 10%
Fork 子进程:共享缓存,几乎免费
10 轮 Agent 成本:~45K token
OpenAI / xAI / 其他
Prompt Cache:
每次请求:全量计算
Fork 子进程:和新建一样贵
10 轮 Agent 成本:~190K token

这不是代码优化,是基础设施优势。同样的架构跑在不同 API 上,成本差 4 倍

缓存崩溃检测(源码级防御)

Claude Code 会主动监控 cache hit 率,发现异常立即上报分析。源码 promptCacheBreakDetection.ts 有完整的检测逻辑

缓存崩溃检测流程
1. 每次 API 响应后,比较 cache_read_input_tokens 和预期值
2. 如果 cache miss > 2,000 tokens,标记为异常
3. 排除 TTL 过期(间隔 > 5分钟/1小时)的正常 miss
4. 确认是客户端导致的 → 上报 tengu_prompt_cache_break 事件
5. Haiku 模型排除检测(缓存行为不同)
为什么这个重要
源码注释提到:动态 Agent 列表曾占全舰队 10.2% 的 cache_creation tokens。因为 MCP 连接、插件加载、权限变化会改变 Agent 列表 → 工具描述变了 → system prompt 前缀变了 → 缓存全部重建。修复方案是把 Agent 列表从 tool description 移到 attachment message(独立于缓存前缀),避免了 10% 的全局缓存浪费。

中转站 API 的缓存情况

Prompt Cache 是 Anthropic 服务端机制。中转站能否利用取决于它怎么转发请求

claude.ts — cache_control 在请求体中的位置
// 每条消息的最后一个 content block 上标记 cache_control { role: 'user', content: [{ type: 'text', text: '消息内容...', cache_control: { type: 'ephemeral', ttl: '1h' // ← Anthropic API 专有字段 } }] }
纯透传中转
你的请求 → 中转站 → api.anthropic.com → 返回
缓存:正常工作
原因:中转站只是管道,不改请求内容,cache_control 原样传到 Anthropic 服务器
条件:中转站不插入/修改任何请求字节
篡改请求的中转
你的请求 → 中转站(注入/过滤) → api.anthropic.com
缓存:完全失效
原因:改了任何一个字节 → 前缀 hash 不匹配 → cache miss
常见操作:插入 system prompt 前缀、添加审计字段、过滤参数
转发到其他 API
Anthropic 格式 → 中转站 → 转 OpenAI 格式 → api.openai.com
缓存:不存在
原因:OpenAI / xAI 没有 prompt cache 机制
cache_control 字段被丢弃(其他 API 不认)
中转站自建缓存层
你的请求 → 中转站(本地缓存) → 命中返回 / 不命中转发
缓存:有但几乎无用
区别:中转站缓存的是完整响应(要求请求完全相同)
Anthropic 缓存的是推理中间状态(只要前缀相同)
对话每轮都多一条新消息 → 请求不同 → 响应缓存永远不命中

怎么验证你的中转站有没有缓存

检查 API 响应中的 usage 字段
// 缓存正常工作 ✓ { "usage": { "input_tokens": 500, "cache_creation_input_tokens": 2000, // 有值 → 在写入缓存 "cache_read_input_tokens": 15000 // 有值 → 在命中缓存 } } // 缓存不工作 ✗ { "usage": { "input_tokens": 17500, "cache_creation_input_tokens": 0, // 零或不存在 "cache_read_input_tokens": 0 // 零或不存在 } } // 如果中转站根本不返回这两个字段 → 它在转发到非 Anthropic API
成本影响有多大
同样一个 10 轮 Agent 任务:
~45K
直连 Anthropic
(缓存正常)
~45K
纯透传中转
(缓存正常)
~190K
篡改/转发其他 API
(无缓存,4.2 倍)
加上中转站的加价(通常 1.2x-2x),实际成本可能是直连的 5-8 倍
实操建议
如果你用中转站调 Claude:在响应里检查 cache_read_input_tokens。第二轮开始如果不是 0,说明缓存在工作
如果中转站转到了 OpenAI:Prompt Cache 优势完全不存在。Agent 多轮对话的成本会高很多
如果你做多 Agent 编排:强烈建议直连 Anthropic API。缓存对多 Agent 场景的成本降低效果最大(每个 Agent 10 轮 = 节省 75%)
环境变量:Claude Code 通过 ANTHROPIC_BASE_URL 支持自定义 API 端点。设成中转站地址即可,但要确保中转站纯透传

三层记忆体系 — 缓存过期后怎么办

Prompt Cache 最多 1 小时,但对话可能持续几个小时。Claude Code 用三层协作解决这个问题:服务端缓存省钱、本地记忆持久化、智能压缩兜底

三层协作架构
时间轴 → Layer 1: Prompt Cache (服务端) ├── 5min/1h TTL ──┤ 过期 ├── 重建 ──┤ 过期 ├── ... → 解决:省钱(每轮只算新增 token) Layer 2: Session Memory (本地 .md) ├── 后台提取笔记越来越完整 ──────────→ → 解决:记忆持久化(不受 TTL 限制) Layer 3: Auto Compact (上下文压缩) ├── 正常对话 ──┤ 超限用 Session Memory 做摘要 ├── 继续→ 解决:上下文溢出(200K 放不下时压缩)

Layer 2: Session Memory — 本地持久记忆(核心)

后台 Fork 子进程定期从对话中提取笔记,写到本地 ~/.claude/projects/<project>/<session>/session-memory.md

触发条件(两个都要满足)
// sessionMemory.ts shouldExtract = (hasMetTokenThreshold && hasMetToolCallThreshold) || (hasMetTokenThreshold && !hasToolCallsInLastTurn) // 翻译: // token 够多 + 工具调用够多 // 或 token 够多 + 对话自然暂停
提取方式
1. 派 Fork 子进程(共享 prompt cache)
2. Fork 读当前 session-memory.md
3. Fork 读对话中的新内容
4. Fork 用 FileEdit 把笔记写入 .md
5. 标记 lastSummarizedMessageId
sessionMemory.ts — Fork 提取(关键:共享缓存)
// 用 runForkedAgent — 继承父上下文的 prompt cache,几乎免费 await runForkedAgent({ promptMessages: [createUserMessage({ content: userPrompt })], cacheSafeParams: createCacheSafeParams(context), // 共享缓存! canUseTool: createMemoryFileCanUseTool(memoryPath), querySource: 'session_memory', forkLabel: 'session_memory', })

Layer 3: Auto Compact — 用 Session Memory 做智能压缩

上下文接近 200K 时触发。不从零调 AI 做摘要——直接用已有的 session-memory.md

sessionMemoryCompact.ts — 压缩逻辑
trySessionMemoryCompaction(messages, agentId, threshold) { // 1. 读取已有的 session memory 文件(之前后台已提取好) sessionMemory = await getSessionMemoryContent() // 2. 计算保留多少最近消息 startIndex = calculateMessagesToKeepIndex(messages, lastSummarizedIndex) // 保留 config: minTokens=10K 或 minTextBlockMessages=5 // 但不超过 maxTokens=40K // 3. 组合:session memory 做摘要 + 最近 N 条原文保留 return createCompactionResultFromSessionMemory( messages, sessionMemory, messagesToKeep) }
压缩后的消息结构:
[System] 延续消息
这是之前对话的延续。以下是之前内容的摘要:
{session-memory.md 内容}
最近的消息原文保留如下。
直接继续对话,不要复述摘要。
[最近 5 条消息原文] — 10K-40K tokens
[新的用户消息]

真实场景:2 小时编码会话

1
0:00 — 开始
Layer 1: Prompt Cache 开始积累
2
0:15 — Token 达到初始化阈值
Layer 2: Session Memory 首次提取 → 写入 session-memory.md
Layer 1: 缓存正常,每轮只付增量
3
0:30 — 又调了 20 次工具
Layer 2: 第二次提取(token + 工具调用都达标)
!
0:45 — 你出去泡咖啡 15 分钟
Layer 1: 5min 缓存过期(1h 缓存如果有则还在)
Layer 2: session-memory.md 在磁盘上,不受任何影响
4
1:00 — 你回来继续
Layer 1: 缓存可能需要重建(首次请求贵一点)
但对话内容完好,Layer 2 笔记最新到 0:30
5
1:30 — 上下文接近 200K 限制
Layer 3: Auto Compact 触发
→ 不调 AI 重新摘要(传统做法,慢且贵)
→ 直接用 session-memory.md 做摘要(快且准)
→ 保留最近 5 条消息(10K-40K tokens)
→ 上下文从 200K 降到 ~50K
Layer 1: 压缩后新前缀开始积累新缓存
6
2:00 — 再次接近上限
Layer 3: 再次压缩
session-memory.md 现在包含 0:00-1:45 的笔记
即使压缩了两次,2 小时前的关键信息依然在笔记里
三层反馈回路
Prompt Cache 让 Session Memory 的 Fork 提取几乎免费(共享父缓存)
Session Memory 让 Auto Compact 更好(已有现成摘要,不用调 AI 重做)
Auto Compact 让 Prompt Cache 重新高效(压缩后前缀变短,缓存命中更快)
三层之间互相加速,形成正反馈循环。去掉任何一层,其他两层的效果都会退化

Session Memory 配置参数(源码默认值)

sessionMemoryUtils.ts + sessionMemoryCompact.ts
// Session Memory 提取阈值 minimumMessageTokensToInit // 对话到多少 token 才开始首次提取 minimumTokensBetweenUpdate // 每次提取之间至少新增多少 token toolCallsBetweenUpdates // 每次提取之间至少多少次工具调用 // Auto Compact 保留参数 DEFAULT_SM_COMPACT_CONFIG = { minTokens: 10_000, // 压缩后至少保留 10K tokens minTextBlockMessages: 5, // 至少保留 5 条有文本的消息 maxTokens: 40_000, // 但不超过 40K tokens } // Auto Compact 触发阈值 AUTOCOMPACT_BUFFER_TOKENS = 13_000 // 距上限 13K 时触发压缩 MAX_CONSECUTIVE_FAILURES = 3 // 连续失败 3 次后停止尝试(熔断) MAX_OUTPUT_TOKENS_SUMMARY = 20_000 // 摘要输出上限 20K tokens
为什么这比"简单做摘要"好
传统做法:上下文满了 → 调 AI 摘要整个对话 → 慢(要读完整对话)、贵(全量 token)、可能丢信息
Claude Code 做法:后台渐进式提取笔记 → 上下文满了直接用笔记做摘要 → 快(O(1) 读文件)、便宜(Fork 共享缓存)、不丢信息(渐进提取覆盖全程)
关键差异:传统摘要是事后补救,Session Memory 是提前准备。等到要用的时候,笔记已经在那了

多窗口并行缓存 — 客户端隔离,云端按内容共享

开 3 个终端窗口跑 Claude Code,缓存怎么算?答案是分层的:客户端完全隔离,但云端可以跨会话命中

多窗口缓存架构 — 两层分离
═══ 客户端层(完全隔离)═══ 窗口 A sessionId: a1b2c3... │ 对话历史 A │ 文件缓存 A │ 缓存断裂检测 A 窗口 B sessionId: d4e5f6... │ 对话历史 B │ 文件缓存 B │ 缓存断裂检测 B 窗口 C sessionId: g7h8i9... │ 对话历史 C │ 文件缓存 C │ 缓存断裂检测 C ↑ 每个窗口启动时生成唯一 UUID,零共享 // bootstrap/state.ts — sessionId: SessionId ═══ 云端层(按内容哈希 + scope 共享)═══ Anthropic API 服务器 ├── global scope 缓存池 ← 静态 system prompt(所有用户共享) ├── org scope 缓存池 ← 有 MCP 工具时的 system prompt(同账号共享) └── 会话级缓存 ← 对话内容(哈希匹配,不看 sessionId)

System Prompt 的 4 块切分

关键函数 splitSysPromptPrefix() 把 system prompt 切成不同 scope 的块

Attribution header
scope = null(不缓存)
System prompt prefix
scope = null(不缓存)
静态内容
规则 / 工具说明 / 风格
scope = 'global'
所有用户、所有窗口共享!
这是 prompt 的大头,可能占 80%+
__DYNAMIC_BOUNDARY__
CWD / git / env / MCP
scope = null
每个窗口不同(工作目录、git 状态)→ 不缓存
api.ts — splitSysPromptPrefix() 核心逻辑
// 三种模式取决于 globalCacheStrategy IF globalCacheStrategy === 'system_prompt' // 1P + 无 MCP → 用 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 分割 → 边界前: scope='global'(全球共享) → 边界后: scope=null(不缓存) IF 有 MCP 工具(工具列表每个窗口可能不同) → 全部 scope='org'(同账号共享,比 global 窄) → 原因: MCP 工具列表变化会改变 prompt → 不能 global ELSE // 3P provider (Bedrock/Vertex) → 全部 scope='org' // betas.ts:227 — shouldUseGlobalCacheScope() // 只有 firstParty API + 未禁用实验特性 → global

实战场景:开 3 个窗口的缓存命中情况

3 窗口并行 — 缓存命中时序
t=0s 窗口 A 首次请求 cache_creation: 8000 (静态 system prompt 写入 global 缓存) cache_read: 0 t=2s 窗口 B 首次请求(不同项目目录) cache_creation: 500 (只有动态部分需要写入) cache_read: 7500 (静态部分命中 A 写的 global 缓存!) t=5s 窗口 C 首次请求(又一个不同目录) cache_creation: 500 cache_read: 7500 (同样命中 global 缓存) t=10s 窗口 A 第 2 轮(用户回复后) cache_creation: 1000 (新增的对话内容) cache_read: 8000 (上一轮的全部成为缓存前缀) t=12s 窗口 B 第 2 轮 cache_read: 8500 (B 自己的前缀,不会命中 A 的对话内容) // 对话内容不同 → 各窗口的对话部分独立缓存

跨窗口缓存共享总结

缓存内容 scope 跨窗口共享? 跨用户共享? 占比
静态 System Prompt global 是(1P) ~80%
有 MCP 的 System Prompt org 是(同账号) ~80%
动态环境信息 null ~10%
对话内容 会话级 ~10% 起递增

TTL 资格锁定(防缓存雪崩)

问题:如果你用到一半超额了,TTL 从 1h 降到 5min —— 下次请求 system prompt 的 cache_control.ttl'1h' 变成无 —— 前缀字节变了 —— 所有缓存全崩(~2 万 token 白付)
解法:资格在会话启动时 latch(锁定)。一旦判定你有 1h 资格,整个会话期间不会降级,即使中途超额。
// claude.ts:393-434 — should1hCacheTTL() 查 bootstrap 锁定状态
多窗口使用的关键洞察
1. 多开不浪费 — System Prompt 占 prompt 大头,global scope 跨窗口甚至跨用户共享。你开 5 个窗口,static prompt 只在第一个窗口付全价,后面都命中缓存(10% 价格)。
2. 对话内容各算各的 — 你在窗口 A 讨论的内容不会变成窗口 B 的缓存。每个窗口的对话前缀独立增长、独立命中。
3. MCP 是个变量 — 如果不同窗口加载了不同的 MCP 工具,缓存从 global 降级到 org scope。工具列表不同 = prompt 前缀不同 = 不能共享。
4. 每次命中都续期 — TTL 不是一次性倒计时。只要有窗口在用,缓存就不会过期。3 个窗口轮流交互 = 缓存永远温热。

云端缓存的物理形态 — KV Cache

Prompt cache 不是存文本,是存 Transformer 注意力层的 Key-Value 矩阵。跳过的是计算,不是传输

KV Cache 原理 — 没缓存 vs 有缓存
没缓存(每次全量计算) [system prompt: 8K tokens] + [历史对话: 15K tokens] + [新消息: 1K tokens] │ ▼ Transformer 每层计算 Key 和 Value Layer 1: Q×K^T → attention → V → output × 24K tokens Layer 2: Q×K^T → attention → V → output × 24K tokens ...× 所有层(Opus 有 100+ 层) │ ▼ 总计算量: 24K × 全部层 ≈ 巨大 有缓存(只算新增部分) [system prompt: 8K] + [历史: 15K] + [新消息: 1K] ──── 已缓存的 KV ────────── ── 新算 ── │ ▼ Layer 1: 直接读取 23K 的 K,V + 只计算 1K 的新 K,V Layer 2: 直接读取 23K 的 K,V + 只计算 1K 的新 K,V ...× 所有层 │ ▼ 总计算量: 1K × 全部层 ≈ 极小 这就是为什么 cache_read 只收 10% 价格

缓存容量 = 上下文窗口

200K
tokens
Opus 4.6 / Sonnet 4.6(默认)
1M
tokens
Opus 4.6 [1m] / Sonnet 4.6 [1m]
没有独立的"缓存容量" — KV Cache 存的就是你 prompt 的计算结果。上下文有多少 token,最多就缓存多少。源码中也没有最小缓存门槛,只要标了 cache_control 就缓存。

缓存存储细节(源码线索)

存储位置
type: 'ephemeral' — 服务端 GPU 显存/内存
不持久化到磁盘,过期即释放
Mycro 的 page_manager/index.rs 管理 KV 页
token 位置索引,不是哈希查找
命中条件
请求的 token 前缀必须完全一致
一个字节的差异 = 从差异点开始全部重算
scope 匹配:global 跨用户 / org 跨同账号窗口
TTL 内 + 前缀一致 = 命中

cache_edits — 不重建整个缓存的局部修改

cache_reference + cache_edits 协议
// 场景:对话中间的一个大 tool_result 被压缩了(microcompact) // 不能改已缓存前缀的字节 → 但可以用 cache_edits 告诉服务端"删掉这段" 请求里: tool_result block 上标记 cache_reference: "toolu_xxx" ↑ 告诉服务端:这个 block 在缓存前缀里,我要引用它 另外附带 cache_edits: [{ type: "delete", reference: "toolu_xxx" }] ↑ 告诉服务端:把这个 block 从 KV cache 里删掉 效果: 服务端在已缓存的 KV 矩阵里挖掉对应 token 区间 不需要重新计算整个前缀 promptCacheBreakDetection.ts 检测到 cache_read 下降时 → 检查是否有 cacheDeletionsPending → 是则标记为"预期行为"不告警

上传内容支持 — 图片 / PDF / 视频

Claude Code 支持 4 种图片格式 + PDF 文档,不支持视频和音频

API 消息内容块类型

text
文本消息
image
base64 图片
document
PDF 文档
tool_use
工具调用
tool_result
工具返回
thinking
思考过程
redacted_thinking
被遮蔽的思考
video / audio
不支持

图片处理流水线(6 级递进压缩)

imageResizer.ts — maybeResizeAndDownsampleImageBuffer()
输入: 用户粘贴/拖入的原始图片 │ ▼ Step 1 检查尺寸和大小 ≤ 2000×2000 且 base64 ≤ 5MB?直接用原图 ✓ │ 超限Step 2 PNG 压缩 compressionLevel: 9, palette: true(色板量化) ≤ 5MB?用压缩后的 PNG ✓ │ 还是太大Step 3 JPEG 逐级降质 quality: 80 → 60 → 40 → 20(每级检查是否 ≤ 5MB) 任一级 ≤ 5MB?用该级 JPEG ✓ │ 全部超限Step 4 缩小尺寸(保持比例) 按比例缩到 2000×2000 以内 │ ▼ Step 5 缩小后再试 JPEG 降质 quality: 80 → 60 → 40 → 20还是不行Step 6 最终兜底 强制缩到 1000px + JPEG quality 20 如果还超 → 报错(实际几乎不可能到这步)

图片限制常量

API_IMAGE_MAX_BASE64_SIZE 5 MB(base64 编码后)
IMAGE_MAX_WIDTH 2000 px
IMAGE_MAX_HEIGHT 2000 px

截图读取(平台差异)

平台 方法 速度 源码
macOS 原生 NSPasteboard(image-processor-napi) ~5ms 冷启 / <1ms 热 imagePaste.ts
macOS (fallback) osascript ~1.5s imagePaste.ts
Linux xclip / wl-paste imagePaste.ts
Windows PowerShell Get-Clipboard -Format Image imagePaste.ts

图片在 API 请求中的格式

API 请求体 — image content block
{ "type": "image", "source": { "type": "base64", "media_type": "image/png", // | "image/jpeg" | "image/gif" | "image/webp" "data": "iVBORw0KGgo..." // base64 编码后的图片数据 } } // BMP 格式会被自动转换为 PNG(检测 magic bytes 0x42 0x4d) // 图片附带坐标元数据: originalWidth/Height, displayWidth/Height // 用于 screenshot → 点击坐标映射(Playwright MCP 等)

PDF 文档支持

支持情况
格式:application/pdf
编码:base64
分页:parsePDFPageRange("5-10")
兼容:所有 provider(1P/Vertex/Bedrock/Foundry)
不支持的媒体类型
视频(MP4/MOV/WebM)
音频(MP3/WAV/OGG)
Office 文档(DOCX/XLSX)
源码中无任何视频/音频处理代码

图片占多少 Token?

tokenEstimation.ts — 图片 token 公式
tokens = (width × height) / 750 举例: 800×600 (普通截图) → 640 tokens 占 1M 的 0.064% 1920×1080 (全屏截图) → 2,764 tokens 占 1M 的 0.28% 2000×2000 (最大允许) → 5,333 tokens 占 1M 的 0.53% 预算估计值(保守): IMAGE_MAX_TOKEN_SIZE = 2,000 // microCompact.ts:38 IMAGE_TOKEN_ESTIMATE = 1,600 // mcpValidation.ts:15(MCP 工具用) → 偏保守,确保 auto-compact 不会触发太晚
1M 能装多少图?
全屏截图 (1920×1080):~360 张
最大尺寸 (2000×2000):~187 张
还要减去 system prompt (~10K) + 对话文本 + 工具输出
实际典型对话里 20 张截图只占 ~55K(1M 的 5.5%)
真正的 token 大户
一次 git diff 输出:可达 50K+
一个大文件 Read:可达 100K
10 轮对话累积:可达 200K+
文本才是吃上下文的主力
图片看着大,token 其实不多

图片对缓存的影响

图片 = 大量 token — 一张 2000×2000 的图片可能占 ~1600 tokens(高分辨率模式)。多张截图会快速填满上下文。
缓存友好 — 图片作为 base64 嵌入消息体,成为缓存前缀的一部分。只要后续轮次不改这条消息,图片数据被缓存后每轮只付 10%。
但是 — ContentReplacementState 可能把大的 tool_result(含截图)替换为磁盘引用。此时图片从缓存前缀里消失,模型只看到预览文本 + 文件路径。

13 Vim 模式状态机

聊天输入框里的完整 Vim 编辑器。用 /vim 命令开关。15 种移动 + 3 种操作符 + 20 种文本对象 + 4 种查找 + 数字前缀组合。写长 prompt 时效率差异巨大

这是什么

普通模式(默认)
打字就是打字
方向键一个字一个字移动
跟任何输入框一样
Vim 模式(/vim 开启)
INSERT 模式打字,ESC 进 NORMAL
w/b 按词跳、ciw 改词、d$ 删到行尾
对 Vim 用户来说效率差异巨大
作用范围:只在聊天输入框里生效,不是编辑代码文件用的。类似终端里 set -o vi开关方式:/vim 命令切换,配置存在 editorMode: 'vim' | 'normal'

12 态命令状态机

vim/types.ts — CommandState 状态机
两个主模式 INSERT(ESC)NORMAL INSERT→NORMAL: 光标左移 1 字符(Vim 标准行为) NORMAL→INSERT: i/I/a/A/o/O 六种进入方式 NORMAL 模式下的 12 个命令状态 idle ──┬─ [d/c/y] ──→ operator ─┬─ [motion] ──→ 执行 │ ├─ [0-9] ────→ operatorCount → motion → 执行 │ ├─ [i/a] ────→ operatorTextObj → 类型 → 执行 │ ├─ [fFtT] ───→ operatorFind → 字符 → 执行 │ └─ [dd/cc/yy] → 整行操作 → 执行 │ ├─ [1-9] ──→ count → 数字累积 → 任何命令(乘法) ├─ [fFtT] ─→ find → 等待字符 → 跳转 ├─ [g] ────→ g-prefix → g/j/k → 执行 ├─ [r] ────→ replace → 等待字符 → 替换 └─ [>/<] ──→ indent → >>/<< → 缩进 // 数字前缀可以组合乘法: 2d3w = 删除 2×3=6 个词 // MAX_VIM_COUNT = 10000(防止误操作)

移动命令(15 种)

h 左 1 字符
l 右 1 字符
j 下 1 逻辑行
k 上 1 逻辑行
w 下一个词首
b 上一个词首
e 当前词尾
W 下一个 WORD 首(空格分隔)
B 上一个 WORD 首
E 当前 WORD 尾
0 行首
^ 行首非空字符
$ 行尾
G 末行(5G = 第 5 行)
gg 首行(5gg = 第 5 行)
方向键在 NORMAL 模式下映射到 hjkl。gj/gk 按视觉行(折行)移动。

操作符(3 种 × 可组合)

d
Delete 删除
dw 删一词 d$ 删到行尾
dd 删整行 3dd 删 3 行
diw 删词 da" 删含引号
删除内容存入寄存器
c
Change 替换
cw 改一词 c$ 改到行尾
cc 改整行 ci" 改引号内
删除后自动进入 INSERT
cw/cW 特殊:延伸到词尾
y
Yank 复制
yw 复制一词 y$ 复制到行尾
yy 复制整行
存入寄存器,用 p/P 粘贴
只有匿名寄存器(无命名)

文本对象(2 种范围 × 10 种类型 = 20 种)

i — inner(内部内容)
不含分隔符/周围空格
例: ci" → 改引号的文本
a — around(包含边界)
含分隔符/周围空格
例: da" → 删引号连同引号本身
w vim word
W WORD(空格分隔)
" 双引号
' 单引号
` 反引号
( ) b 圆括号
[ ] 方括号
{ } B 花括号
< > 尖括号

查找移动(4 种 + 重复)

fx 向前找 x,光标落在 x 上
Fx 向后找 x,光标落在 x 上
tx 向前找 x,光标落在 x 前
Tx 向后找 x,光标落在 x 后
; 重复上次查找(同方向)
, 重复上次查找(反方向)
查找可与操作符组合:df, 删到逗号、ct) 改到右括号前。

单字符命令

x 删光标下字符(3x = 删 3 个)
~ 切换大小写
rx 替换光标字符为 x
J 合并当前行和下一行
p 在光标后粘贴
P 在光标前粘贴
D 删到行尾(= d$)
C 改到行尾(= c$)
Y 复制整行(= yy)
. 重复上一个修改操作(dot-repeat)
u 撤销
>> 缩进(3>> = 缩进 3 行)
<< 反缩进

进入 INSERT 模式的 6 种方式

i 在光标处进入
I 在行首非空字符处进入
a 在光标后进入
A 在行尾进入
o 下方新建行进入
O 上方新建行进入

源码架构(6 个文件)

文件结构 — 纯函数 + React Hook
src/ ├── vim/ │ ├── types.ts 200 行 — 状态机类型定义、CommandState 联合类型 │ ├── transitions.ts 491 行 — 状态转移逻辑(纯函数,无副作用) │ ├── motions.ts 83 行 — 光标移动计算(纯函数) │ ├── operators.ts 557 行 — 文本操作执行(纯函数) │ └── textObjects.ts 187 行 — 文本对象范围查找(纯函数) ├── hooks/ │ └── useVimInput.ts 316 行 — React Hook,连接键盘输入和 Vim 引擎 ├── components/ │ ├── VimTextInput.tsx Vim 输入组件包装器 │ └── PromptInput/ │ ├── PromptInput.tsx 根据 isVimModeEnabled() 选择组件 │ └── utils.ts isVimModeEnabled() 配置检查 └── commands/vim/ └── vim.ts /vim 命令,切换 editorMode 配置 核心设计:vim/ 目录全是纯函数,可独立单元测试 hooks/ 和 components/ 是 React 粘合层

按键处理流程

useVimInput.ts — handleVimInput()
按键输入 │ ├─ Ctrl+任意键 → 绕过 Vim,直接传给基础 handler │ ├─ INSERT 模式 │ ├─ ESC → 进入 NORMAL(光标左移 1) │ └─ 其他 → 记录 insertedText + 传给基础输入 │ └─ NORMAL 模式 └─ transition(state, key) → 新状态 or 命令执行 ├─ setText() 修改文本 ├─ setOffset() 移动光标 ├─ enterInsert() 切到 INSERT └─ setRegister() 存储剪切内容

持久状态(跨命令保留)

register 剪切/复制缓冲区(只有匿名寄存器)
lastChange 上一次修改操作(. 重放用)
lastFind 上一次查找(; 和 , 重复用)
registerIsLinewise 粘贴时是否按行粘贴

不支持的 Vim 功能

VISUAL 模式(v/V/Ctrl+V)
命名寄存器("a, "b 等)
宏录制/播放(q/Q)
搜索(/pattern, ?pattern)
替换命令(:s/old/new/)
标记(m/`/')
跳转列表(Ctrl+O/I)
滚动(Ctrl+D/U/F/B)
这是聊天输入框的 Vim,不是完整编辑器。覆盖了日常编辑最高频的操作,放弃了文件导航类功能。

14 快捷键

分层设计,用户可通过 ~/.claude/keybindings.json 覆盖

Ctrl+C 中断当前操作
Ctrl+D 退出 Claude Code
Ctrl+L 重绘终端
Ctrl+O 切换全屏模式
Ctrl+T 切换 Todo 面板
Ctrl+R 历史搜索
Shift+Tab Normal → Plan → Auto
Ctrl+V 粘贴图片
Ctrl+Shift+F 全局搜索
Ctrl+Shift+P 快速打开
Meta+J 终端面板
Space 语音 Push-to-Talk (Vim)

15 Feature Flags + 沙箱 + 远程控制

96 个 feature flag 控制功能灰度。双层开关(编译时消除 + 运行时 GrowthBook)。内置沙箱隔离 Bash 命令。GrowthBook 可按用户属性远程控制功能开关

BUDDY
VOICE_MODE
KAIROS
KAIROS_DREAM
COORDINATOR_MODE
UDS_INBOX
ULTRAPLAN
WEB_BROWSER_TOOL
CONTEXT_COLLAPSE
REACTIVE_COMPACT
WORKFLOW_SCRIPTS
TERMINAL_PANEL
BASH_CLASSIFIER
TREE_SITTER_BASH
PROACTIVE
AGENT_TRIGGERS
MCP_SKILLS
QUICK_SEARCH
DIRECT_CONNECT
TOKEN_BUDGET

展示 20 / 96 个。完整列表包含遥测、安全、A/B 实验等分类

双层开关机制:内外有别

Layer 1: 编译时消除 feature()
// 打包时 feature('PROACTIVE') 被替换为 false // SleepTool 的代码从包里物理删除 const SleepTool = false ? require('./SleepTool.js') // ← 被 tree-shake 掉 : null
结果:代码不在 npm 包里。反编译也看不到
Layer 2: 运行时检查 USER_TYPE
// 代码在包里,但外部用户永远走 null 分支 process.env.USER_TYPE === 'ant' ? require('./REPLTool.js') // 员工能用 : null // 外部用不了
结果:代码在包里但不执行。改环境变量也没用(需要内部 API)

GrowthBook A/B 测试 + 远程控制

Claude Code 启动时带着你的用户属性去问 GrowthBook 服务器:"我应该得到什么值?"。你完全不知道自己在被测试

growthbook.ts — 发送给 GrowthBook 的用户属性
GrowthBookUserAttributes = { id: string, // 用户 ID(哈希后用于分组) platform: 'darwin'|'win32'|'linux', apiBaseUrlHost?: string, // API 端点域名(可识别中转站) organizationUUID?: string, // 组织 ID(可按公司控制) accountUUID?: string, // 账户 ID(可按个人控制) subscriptionType?: string, // 订阅类型(free/pro/team) rateLimitTier?: string, // 速率限制层级 email?: string, appVersion?: string, }

GrowthBook 可以基于这些属性做什么

控制维度 属性字段 可以做的事
按订阅类型subscriptionTypePro 用户有 Coordinator,免费用户没有
按组织organizationUUID某公司的所有员工开启/关闭特定功能
按 API 端点apiBaseUrlHost检测中转站用户,调整功能
按平台platformmacOS 开沙箱,Linux 不开
按用户 IDid (deviceId)A/B 测试:50% 用户有功能 X,50% 没有
按版本appVersion旧版本禁用有 bug 的功能
回答你的问题:能不能按区域限制功能?
源码里没有直接的地区/IP 字段。但有间接方式:
- apiBaseUrlHost — 不同区域的用户可能用不同的 API 端点(如 Bedrock 的 us-east-1 vs eu-west-1)
- organizationUUID — 按公司控制,间接实现区域限制
- accountUUID — 精确到个人,可以单独开关
实际上 Anthropic 不需要 IP 地理位置 — 它可以通过 accountUUID 精确控制到每个用户。要限制某个区域,只需要在 GrowthBook 后台给那个区域的账户 ID 设一个规则
而且 GrowthBook 值缓存在本地磁盘(~/.claude.json)。服务器改了值,你下次启动才生效。但长时间运行的会话会定期刷新

紧急下线开关(Kill Switch)

源码中的 Kill Switch 模式
// Voice 模式的紧急关闭 function isVoiceGrowthBookEnabled(): boolean { return !getFeatureValue('tengu_amber_quartz_disabled', false) // 默认 false(不禁用)= 功能正常 // Anthropic 改成 true = 全球所有用户立即失去语音功能 // 不需要发版,不需要用户更新 } // Cron 调度的紧急关闭 // "flipping it to false stops already-running sessions" // 翻转后,正在运行的会话也会停止

Bash 沙箱隔离

Claude Code 内置 @anthropic-ai/sandbox-runtime 包,在你电脑上创建一个隔离环境执行 Bash 命令。macOS 用 sandbox-exec,Linux 用 bwrap(bubblewrap)

沙箱内可以做的
读项目目录内的文件
写项目目录内的文件
执行项目内的命令(build/test/lint)
访问白名单域名的网络
沙箱内不能做的
读/写项目目录外的文件
访问非白名单的网络(防数据泄漏)
修改系统配置文件
访问其他用户的文件
sandbox-adapter.ts — 沙箱配置
// 沙箱限制来自三层配置 network: { allowedDomains: ['registry.npmjs.org', ...], // 网络白名单 deniedDomains: ['evil.com', ...] // 网络黑名单 } filesystem: { allowWrite: ['/project/**'], // 可写路径 denyWrite: ['/.ssh/**'], // 禁写路径 } // 企业可以强制只用管理员指定的域名 policySettings.sandbox.network.allowManagedDomainsOnly: true // 平台支持 macOS: sandbox-exec (Apple 原生沙箱) Linux: bwrap (bubblewrap, 用户态容器) WSL2: bwrap (通过 Linux 子系统) WSL1: 不支持 Windows 原生: 不支持
沙箱和权限系统的关系
// BashTool 的权限检查中: if (SandboxManager.isSandboxingEnabled() && SandboxManager.isAutoAllowBashIfSandboxedEnabled() && shouldUseSandbox(input)) { // 沙箱内的命令 → 自动放行,不需要用户确认 // 因为沙箱已经限制了它能做的事 } // 但以下情况仍然不能自动放行: - dangerouslyDisableSandbox: true // 模型要求关沙箱 - 被 excludedCommands 排除的命令 // 用户配了排除列表 - 沙箱依赖缺失 // bwrap 没装
沙箱的实际效果
开启沙箱后,大部分 Bash 命令(build、test、git 操作)自动放行,不再弹确认框
只有需要突破沙箱限制的命令(访问外部网络、修改系统文件)才需要用户手动确认
对用户来说:更少的弹窗打扰,同等的安全保障
开启方式:settings.json → sandbox.enabled: true/sandbox-toggle

16 遥测、隐私与远程控制

Claude Code 内置 665 种遥测事件。源码分析:它收集什么、不收集什么、怎么关闭、以及远程控制能做到什么程度

什么是遥测

遥测(Telemetry)= 软件自动给厂商"打小报告"。你用 Claude Code 的时候,它在后台默默记录使用数据,定期发回给 Anthropic。你看不到、感觉不到
不记录的(类比:你在超市买了什么)
你的代码内容、对话内容、文件路径、命令内容
记录的(类比:几点去的、逛了多久、结账几次)
用了什么工具、跑了多久、花了多少 token、成功还是失败
例:你跑了 npm test,遥测记录的是:
{ 工具名: "Bash", 耗时: 3200ms, 成功: true, token: 1500 } // 不记录 "npm test" 本身

三级关闭机制(源码 privacyLevel.ts)

三个隐私等级,由低到高。每级关闭不同的东西

Level 0 default — 全部开启 (不设任何环境变量)
ON 665 种遥测事件 → Datadog + 1P 分析
ON GrowthBook 远程配置刷新
ON 自动更新检查
ON 版本发布日志获取
ON 模型能力刷新
ON 反馈调查
ON 邀请码 / 额度检查
ON 设备信任注册
Level 1 no-telemetry — 关闭遥测 export DISABLE_TELEMETRY=1
OFF 665 种遥测事件(Datadog + 1P 全停)
OFF GrowthBook(远程配置 + A/B 测试)
OFF 反馈调查
ON 自动更新检查
ON 版本发布日志
ON 模型能力刷新
ON API 调用(对话本身)
ON 邀请码 / 额度检查
注意:GrowthBook 关了意味着功能开关退回到代码默认值。某些默认 false 的功能(如 Session Memory)不会开启
Level 2 essential-traffic — 只保留必要流量 export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
OFF 665 种遥测事件
OFF GrowthBook 远程配置
OFF 反馈调查
OFF 自动更新检查
OFF 版本发布日志获取
OFF 模型能力刷新
OFF 邀请码 / 额度检查
OFF 设备信任注册
OFF Fast Mode 状态预取
OFF 用量配额检查
OFF Grove 隐私政策检查
OFF 策略限制检查
ON 只剩 API 调用(对话本身)— 这是唯一无法关闭的网络请求

自动关闭的情况(不需要手动设环境变量)

条件 效果 源码位置
使用 AWS Bedrock遥测自动关闭(isAnalyticsDisabled)analytics/config.ts
使用 Google Vertex遥测自动关闭analytics/config.ts
使用 Foundry遥测自动关闭analytics/config.ts
NODE_ENV=test遥测 + 反馈调查关闭analytics/config.ts
遥测关闭(Level 1/2)GrowthBook 连带关闭(依赖 1P 事件日志)growthbook.ts:422
连锁反应:关闭遥测 → GrowthBook 跟着关 → 所有 flag 退回代码默认值
这意味着默认 false 的功能(Session Memory、Verification Agent、SM Compact)不会开启
默认 true 的功能(Explore/Plan Agent、子 Agent 跳过 CLAUDE.md)继续正常
代价:关遥测 = 放弃 GrowthBook = 放弃一些需要远程开启的优化功能。这是隐私和功能之间的权衡

每个事件自动附带的元数据

源码 metadata.ts — 665 种事件类型,每个都携带以下字段

发送的(EventMetadata + EnvContext)
sessionId — 会话 ID
accountUUID — 账户 ID
organizationUUID — 组织 ID
subscriptionType — 订阅类型
model — 使用的模型
inputTokens / outputTokens — token 消耗
platform / arch — 操作系统和架构
apiBaseUrlHost — API 端点域名
rh — 仓库 remote URL 的 SHA256 哈希前 16 字符
version / buildTime — 版本和构建时间
terminal — 终端类型
rss / heapUsed / cpuPercent — 进程资源占用
不发送的
代码内容
对话/提问内容
文件路径(有类型标记强制审查)
IP 地址(客户端不主动发)
地理位置
源码用 AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS 类型标记,强制开发者确认元数据不含代码或文件路径
但注意:API 调用本身(你的对话内容)是通过 HTTP 发给 Anthropic 服务器的。遥测系统不发代码 ≠ Anthropic 看不到你的代码。服务端 IP 日志也是标准操作

665 种事件类型(完整标注版,按分类折叠)

每个事件附带用途说明。点击分类展开。所有事件发送到 Datadog + Anthropic 内部 1P 分析系统。事件本身是被动观察(传感器),不直接控制功能。但观察数据会反馈到 GrowthBook 决策

其他 杂项事件 185 个
analytics_sink_attached analytics sink attached
tengu_accept_submitted accept submitted
tengu_advisor_tool_call advisor tool call
tengu_advisor_tool_interrupted advisor tool interrupted
tengu_advisor_tool_token_usage advisor tool token usage
tengu_at_mention_extracting_directory_success at mention extracting directory success
tengu_at_mention_extracting_filename_error at mention extracting filename error
tengu_attachment_compute_duration attachment compute duration
tengu_attachments attachments
tengu_auto_connect_ide_changed auto connect ide changed
tengu_auto_mem_tool_denied auto mem tool denied
tengu_auto_mode_decision auto mode decision
tengu_auto_mode_denial_limit_exceeded auto mode denial limit exceeded
tengu_auto_mode_opt_in_dialog_decline auto mode opt in dialog decline
tengu_auto_mode_outcome auto mode outcome
tengu_binary_download_attempt binary download attempt
tengu_binary_download_failure binary download failure
tengu_binary_download_success binary download success
tengu_binary_manifest_fetch_failure binary manifest fetch failure
tengu_binary_platform_not_found binary platform not found
tengu_bug_report_submitted bug report submitted
tengu_cancel cancel
tengu_chain_parallel_tr_recovered chain parallel tr recovered
tengu_chain_parent_cycle chain parent cycle
tengu_claudeai_limits_status_changed claudeai limits status changed
tengu_code_prompt_ignored code prompt ignored
tengu_concurrent_onquery_detected concurrent onquery detected
tengu_concurrent_onquery_enqueued concurrent onquery enqueued
tengu_concurrent_sessions 并发会话数
tengu_continue continue
tengu_continue_print continue print
tengu_conversation_rewind conversation rewind
tengu_copy copy
tengu_cost_threshold_acknowledged cost threshold acknowledged
tengu_cost_threshold_reached cost threshold reached
tengu_deep_link_opened deep link opened
tengu_deep_link_registered deep link registered
tengu_default_view_setting_changed default view setting changed
tengu_deferred_tool_schema_not_sent deferred tool schema not sent
tengu_deferred_tools_pool_change deferred tools pool change
tengu_diff_tool_changed diff tool changed
tengu_doctor_command doctor command
tengu_dynamic_skills_changed 动态技能变更
tengu_edit_string_lengths edit string lengths
tengu_editor_mode_changed editor mode changed
tengu_effort_command effort command
tengu_exit exit
tengu_exit_plan_mode_called_outside_plan exit plan mode called outside plan
tengu_ext_at_mentioned ext at mentioned
tengu_ext_diff_accepted ext diff accepted
tengu_ext_diff_rejected ext diff rejected
tengu_ext_ide_command ext ide command
tengu_ext_will_show_diff ext will show diff
tengu_external_editor_used external editor used
tengu_extract_memories_coalesced extract memories coalesced
tengu_extract_memories_error extract memories error
tengu_extract_memories_extraction extract memories extraction
tengu_extract_memories_gate_disabled extract memories gate disabled
tengu_extract_memories_skipped_direct_write extract memories skipped direct write
tengu_fast_mode_fallback_triggered fast mode fallback triggered
tengu_fast_mode_overage_rejected fast mode overage rejected
tengu_fast_mode_picker_shown fast mode picker shown
tengu_fast_mode_toggled fast mode toggled
tengu_filtered_orphaned_thinking_message filtered orphaned thinking message
tengu_filtered_trailing_thinking_block filtered trailing thinking block
tengu_filtered_whitespace_only_assistant filtered whitespace only assistant
tengu_fixed_empty_assistant_content fixed empty assistant content
tengu_git_index_lock_error git index lock error
tengu_git_operation git operation
tengu_grove_policy_exited grove policy exited
tengu_grove_policy_toggled grove policy toggled
tengu_grove_print_viewed grove print viewed
tengu_guest_passes_link_copied guest passes link copied
tengu_guest_passes_upsell_shown guest passes upsell shown
tengu_guest_passes_visited guest passes visited
tengu_heap_dump heap dump
tengu_hooks_command hooks command
tengu_idle_return_action idle return action
tengu_image_compress_failed image compress failed
tengu_image_resize_failed image resize failed
tengu_image_resize_fallback image resize fallback
tengu_immediate_command_executed immediate command executed
tengu_input_bash input bash
tengu_input_command input command
tengu_input_prompt input prompt
tengu_input_slash_invalid input slash invalid
tengu_input_slash_missing input slash missing
tengu_language_changed language changed
tengu_legacy_opus_migration legacy opus migration
tengu_message_actions_enter message actions enter
tengu_message_selector_cancelled message selector cancelled
tengu_message_selector_opened message selector opened
tengu_message_selector_restore_option_selected message selector restore option selected
tengu_message_selector_selected message selector selected
tengu_mode_cycle mode cycle
tengu_model_command_inline model command inline
tengu_model_command_inline_help model command inline help
tengu_model_fallback_triggered model fallback triggered
tengu_model_picker_hotkey model picker hotkey
tengu_model_whitespace_response model whitespace response
tengu_node_warning node warning
tengu_off_switch_query off switch query
tengu_opus_to_opus1m_migration opus to opus1m migration
tengu_org_penguin_mode_fetch_failed org penguin mode fetch failed
tengu_orphaned_messages_tombstoned orphaned messages tombstoned
tengu_output_style_changed output style changed
tengu_overage_credit_upsell_shown overage credit upsell shown
tengu_paste_image paste image
tengu_paste_text paste text
tengu_pasted_image_resize_attempt pasted image resize attempt
tengu_pdf_page_extraction pdf page extraction
tengu_pdf_reference_attachment pdf reference attachment
tengu_plan_exit plan exit
tengu_plan_external_editor_used plan external editor used
tengu_post_tool_failure_hook_error post tool failure hook error
tengu_post_tool_failure_hooks_cancelled post tool failure hooks cancelled
tengu_post_tool_hook_error post tool hook error
tengu_post_tool_hooks_cancelled post tool hooks cancelled
tengu_pr_status_footer_setting_changed pr status footer setting changed
tengu_pre_stop_hooks_cancelled pre stop hooks cancelled
tengu_pre_tool_hook_error pre tool hook error
tengu_pre_tool_hooks_cancelled pre tool hooks cancelled
tengu_preflight_check_failed preflight check failed
tengu_prompt_cache_break 缓存崩溃 — 记录 10 个变化维度(systemPrompt/toolSchemas/model/...)
tengu_prompt_suggestion prompt suggestion
tengu_query_after_attachments query after attachments
tengu_query_before_attachments query before attachments
tengu_reduce_motion_setting_changed reduce motion setting changed
tengu_reject_submitted reject submitted
tengu_relink_walk_broken relink walk broken
tengu_reset_pro_to_opus_default reset pro to opus default
tengu_respect_gitignore_setting_changed respect gitignore setting changed
tengu_review_overage_dialog_shown review overage dialog shown
tengu_review_overage_low_balance review overage low balance
tengu_review_overage_not_enabled review overage not enabled
tengu_shell_completion_failed shell completion failed
tengu_shell_set_cwd shell set cwd
tengu_shell_snapshot_error shell snapshot error
tengu_shell_snapshot_failed shell snapshot failed
tengu_shell_unknown_error shell unknown error
tengu_show_turn_duration_setting_changed show turn duration setting changed
tengu_single_word_prompt single word prompt
tengu_sonnet45_to_46_migration sonnet45 to 46 migration
tengu_speculation speculation
tengu_speculation_setting_changed speculation setting changed
tengu_started started
tengu_status_line_mount status line mount
tengu_stdin_interactive stdin interactive
tengu_stop_hook_error stop hook error
tengu_stream_loop_exited_after_watchdog stream loop exited after watchdog
tengu_stream_no_events stream no events
tengu_structured_output_enabled structured output enabled
tengu_structured_output_failure structured output failure
tengu_sysprompt_block sysprompt block
tengu_sysprompt_boundary_found sysprompt boundary found
tengu_sysprompt_missing_boundary_marker sysprompt missing boundary marker
tengu_sysprompt_using_tool_based_cache sysprompt using tool based cache
tengu_terminal_progress_bar_setting_changed terminal progress bar setting changed
tengu_terminal_tab_status_setting_changed terminal tab status setting changed
tengu_thinking_toggled thinking toggled
tengu_thinking_toggled_hotkey thinking toggled hotkey
tengu_timer timer
tengu_tip_shown tip shown
tengu_tips_setting_changed tips setting changed
tengu_toggle_todos toggle todos
tengu_toggle_transcript 切换转录视图
tengu_tree_sitter_load tree sitter load
tengu_tree_sitter_parse_abort tree sitter parse abort
tengu_tree_sitter_shadow tree sitter shadow
tengu_ultraplan_approved ultraplan approved
tengu_ultraplan_awaiting_input ultraplan awaiting input
tengu_ultraplan_create_failed ultraplan create failed
tengu_ultraplan_failed ultraplan failed
tengu_ultraplan_keyword ultraplan keyword
tengu_ultraplan_launched ultraplan launched
tengu_ultrathink ultrathink
tengu_unary_event unary event
tengu_uncaught_exception uncaught exception
tengu_unhandled_rejection unhandled rejection
tengu_unknown_model_cost unknown model cost
tengu_worktree_cleanup worktree cleanup
tengu_worktree_created worktree created
tengu_worktree_detection worktree detection
tengu_worktree_kept worktree kept
tengu_worktree_removed worktree removed
配置 & 设置 追踪安装/更新/配置变更 69 个
tengu_auto_install_ide_extension_changed auto install ide extension changed
tengu_auto_updater_fail auto updater fail
tengu_auto_updater_lock_contention auto updater lock contention
tengu_auto_updater_success auto updater success
tengu_auto_updater_windows_npm_in_wsl auto updater windows npm in wsl
tengu_autoupdate_channel_changed autoupdate channel changed
tengu_autoupdate_enabled autoupdate enabled
tengu_began_setup began setup
tengu_claude_install_command claude install command
tengu_claudemd__initial_load claudemd initial load
tengu_config_cache_stats config cache stats
tengu_config_changed config changed
tengu_config_lock_contention config lock contention
tengu_config_model_changed config model changed
tengu_config_parse_error config parse error
tengu_config_stale_write config stale write
tengu_config_tool_changed config tool changed
tengu_ext_install_error ext install error
tengu_ext_installed ext installed
tengu_init init
tengu_install_github_app_completed install github app completed
tengu_install_github_app_error install github app error
tengu_install_github_app_started install github app started
tengu_install_github_app_step_completed install github app step completed
tengu_install_slack_app_clicked install slack app clicked
tengu_managed_settings_loaded managed settings loaded
tengu_migrate_autoupdates_error migrate autoupdates error
tengu_migrate_autoupdates_to_settings migrate autoupdates to settings
tengu_migrate_reset_auto_opt_in_for_default_offer migrate reset auto opt in for default offer
tengu_native_auto_updater_fail native auto updater fail
tengu_native_auto_updater_lock_contention native auto updater lock contention
tengu_native_auto_updater_start native auto updater start
tengu_native_auto_updater_success native auto updater success
tengu_native_auto_updater_up_to_date native auto updater up to date
tengu_native_install_binary_failure native install binary failure
tengu_native_install_binary_success native install binary success
tengu_native_install_package_failure native install package failure
tengu_native_install_package_success native install package success
tengu_native_staging_cleanup native staging cleanup
tengu_native_stale_locks_cleanup native stale locks cleanup
tengu_native_temp_files_cleanup native temp files cleanup
tengu_native_update_complete native update complete
tengu_native_update_lock_failed native update lock failed
tengu_native_update_skipped_max_version native update skipped max version
tengu_native_update_skipped_minimum_version native update skipped minimum version
tengu_native_version_cleanup native version cleanup
tengu_npm_cache_cleanup npm cache cleanup
tengu_prompt_suggestion_init prompt suggestion init
tengu_settings_sync_download_empty settings sync download empty
tengu_settings_sync_download_error settings sync download error
tengu_settings_sync_download_fetch_failed settings sync download fetch failed
tengu_settings_sync_download_skipped settings sync download skipped
tengu_settings_sync_download_success settings sync download success
tengu_settings_sync_upload_failed settings sync upload failed
tengu_settings_sync_upload_fetch_failed settings sync upload fetch failed
tengu_settings_sync_upload_skipped settings sync upload skipped
tengu_settings_sync_upload_skipped_ineligible settings sync upload skipped ineligible
tengu_settings_sync_upload_success settings sync upload success
tengu_setup_github_actions_completed setup github actions completed
tengu_setup_github_actions_failed setup github actions failed
tengu_setup_github_actions_started setup github actions started
tengu_setup_token_command setup token command
tengu_startup_manual_model_config startup manual model config
tengu_startup_telemetry startup telemetry
tengu_update_check update check
tengu_version_check_failure version check failure
tengu_version_check_success version check success
tengu_version_lock_acquired version lock acquired
tengu_version_lock_failed version lock failed
Bridge & 远程连接 监控远程会话稳定性 64 个
tengu_bridge_fatal_error Bridge 致命错误
tengu_bridge_heartbeat_error Bridge 心跳错误
tengu_bridge_heartbeat_mode_entered Bridge 进入心跳模式
tengu_bridge_heartbeat_mode_exited Bridge 退出心跳模式
tengu_bridge_message_received Bridge 收到消息
tengu_bridge_poll_give_up Bridge 轮询放弃
tengu_bridge_reconnected Bridge 重连成功
tengu_bridge_registration_failed Bridge 注册失败
tengu_bridge_repl_ccr_v2_init_failed Bridge REPL CCR v2 初始化失败
tengu_bridge_repl_connect_timeout Bridge REPL 连接超时
tengu_bridge_repl_env_expired_fresh_session Bridge REPL 环境过期(新会话)
tengu_bridge_repl_env_lost Bridge REPL 环境丢失
tengu_bridge_repl_env_registered Bridge REPL 环境注册
tengu_bridge_repl_fatal_error Bridge REPL 致命错误
tengu_bridge_repl_history_capped Bridge REPL 历史上限
tengu_bridge_repl_poll_error Bridge REPL 轮询错误
tengu_bridge_repl_poll_give_up Bridge REPL 轮询放弃
tengu_bridge_repl_reconnect_failed Bridge REPL 重连失败
tengu_bridge_repl_reconnected_in_place Bridge REPL 原地重连
tengu_bridge_repl_session_failed Bridge REPL 会话失败
tengu_bridge_repl_skipped Bridge REPL 跳过
tengu_bridge_repl_started Bridge REPL 启动
tengu_bridge_repl_suspension_detected Bridge REPL 挂起检测
tengu_bridge_repl_teardown Bridge REPL 拆除
tengu_bridge_repl_work_received Bridge REPL 收到工作
tengu_bridge_repl_work_secret_failed Bridge REPL 工作密钥失败
tengu_bridge_repl_ws_closed Bridge REPL WebSocket 关闭
tengu_bridge_repl_ws_connected Bridge REPL WebSocket 连接
tengu_bridge_session_done Bridge 会话结束
tengu_bridge_session_started Bridge 会话开始
tengu_bridge_session_timeout Bridge 会话超时
tengu_bridge_shutdown Bridge 关闭
tengu_bridge_spawn_mode_chosen Bridge 启动模式选择
tengu_bridge_spawn_mode_toggled Bridge 启动模式切换
tengu_bridge_started Bridge 启动
tengu_bridge_token_refreshed Bridge Token 刷新
tengu_bridge_work_secret_failed Bridge 工作密钥失败
tengu_ccr_bundle_upload CCR 代码包上传
tengu_ccr_mirror_started CCR 镜像启动
tengu_ccr_unsupported_default_mode_ignored CCR 不支持的默认模式被忽略
tengu_plugin_remote_fetch 插件远程获取
tengu_remote_create_session 远程会话创建
tengu_remote_create_session_error 远程会话创建失败
tengu_remote_create_session_success 远程会话创建成功
tengu_remote_setup_result 远程设置结果
tengu_remote_setup_started 远程设置开始
tengu_review_remote_launched 远程代码审查启动
tengu_review_remote_precondition_failed 远程审查前置条件失败
tengu_review_remote_teleport_failed 远程审查 Teleport 失败
tengu_teleport_bundle_mode Teleport 包模式
tengu_teleport_error_branch_checkout_failed Teleport 分支切换失败
tengu_teleport_error_git_not_clean Teleport Git 不干净
tengu_teleport_error_repo_mismatch_sessions_api Teleport 仓库不匹配
tengu_teleport_error_repo_not_in_git_dir_sessions_api Teleport 不在 Git 目录
tengu_teleport_error_session_not_found_404 Teleport 会话未找到
tengu_teleport_errors_detected Teleport 错误检测
tengu_teleport_errors_resolved Teleport 错误解决
tengu_teleport_first_message_error Teleport 首条消息失败
tengu_teleport_first_message_success Teleport 首条消息成功
tengu_teleport_interactive_mode Teleport 交互模式
tengu_teleport_print Teleport 打印
tengu_teleport_resume_error Teleport 恢复错误
tengu_teleport_resume_session Teleport 恢复会话
tengu_teleport_source_decision Teleport 来源决策
MCP 服务 追踪 MCP 连接/工具加载/认证状态 43 个
tengu_at_mention_mcp_resource_error at mention mcp resource error
tengu_at_mention_mcp_resource_success at mention mcp resource success
tengu_builtin_mcp_toggle 内置 MCP 开关切换
tengu_claudeai_mcp_auth_completed Claude.ai MCP 认证完成
tengu_claudeai_mcp_auth_started Claude.ai MCP 认证开始
tengu_claudeai_mcp_clear_auth_completed Claude.ai MCP 清除认证完成
tengu_claudeai_mcp_clear_auth_started Claude.ai MCP 清除认证开始
tengu_claudeai_mcp_eligibility Claude.ai MCP 资格检查
tengu_claudeai_mcp_reconnect Claude.ai MCP 重连
tengu_claudeai_mcp_toggle Claude.ai MCP 开关切换
tengu_mcp_add MCP 服务器添加
tengu_mcp_auth_config_authenticate MCP 认证配置
tengu_mcp_auth_config_clear MCP 认证清除
tengu_mcp_channel_enable MCP Channel 启用
tengu_mcp_channel_flags MCP Channel 标记
tengu_mcp_channel_gate MCP Channel 门控
tengu_mcp_channel_message MCP Channel 消息
tengu_mcp_claudeai_proxy_401 Claude.ai MCP 代理 401 错误
tengu_mcp_delete MCP 服务器删除
tengu_mcp_elicitation_response MCP 信息收集响应
tengu_mcp_elicitation_shown MCP 信息收集界面显示
tengu_mcp_get MCP 服务器信息获取
tengu_mcp_ide_server_connection_failed MCP IDE 服务器连接失败
tengu_mcp_ide_server_connection_succeeded MCP IDE 服务器连接成功
tengu_mcp_instructions_pool_change MCP 指令池变化
tengu_mcp_large_result_handled MCP 大结果处理 — 记录大小/处理方式(截断/持久化)
tengu_mcp_list MCP 服务器列表查看
tengu_mcp_list_changed MCP 工具列表变化 — 记录前后数量(影响缓存)
tengu_mcp_oauth_flow_error MCP OAuth 流程错误
tengu_mcp_oauth_flow_failure MCP OAuth 流程失败
tengu_mcp_oauth_flow_start MCP OAuth 流程开始
tengu_mcp_oauth_flow_success MCP OAuth 流程成功
tengu_mcp_reset_mcpjson_choices MCP JSON 选择重置
tengu_mcp_server_connection_failed MCP 服务器连接失败 — 记录耗时/失败原因
tengu_mcp_server_connection_succeeded MCP 服务器连接成功 — 记录耗时/传输类型/服务器统计
tengu_mcp_server_needs_auth MCP 服务器需要认证
tengu_mcp_servers MCP 服务器统计 — stdio/sse/http/ide 各多少
tengu_mcp_session_expired MCP 会话过期
tengu_mcp_start MCP 模式启动
tengu_mcp_tool_call_auth_error MCP 工具调用认证失败(token过期)
tengu_mcp_tools_commands_loaded MCP 工具和命令加载完成 — 记录工具数/命令数
tengu_migrate_mcp_approval_fields_error MCP 审批字段迁移失败
tengu_migrate_mcp_approval_fields_success MCP 审批字段迁移成功
Agent & 多Agent协作 追踪 Agent 使用模式和效果 38 个
tengu_agent_color_set Agent 颜色设置
tengu_agent_created Agent 定义创建 — 记录生成方式(手动/AI)/工具数/是否自定义模型
tengu_agent_definition_generated AI 自动生成 Agent 定义
tengu_agent_flag Agent 功能标记检查
tengu_agent_memory_loaded Agent 记忆加载 — 记录范围(user/project/local)
tengu_agent_name_set Agent 名称设置
tengu_agent_parse_error Agent 定义解析失败
tengu_agent_stop_hook_error Agent 停止 Hook 执行失败
tengu_agent_stop_hook_max_turns Agent 达到最大轮次限制
tengu_agent_stop_hook_success Agent 停止 Hook 成功执行
tengu_agent_tool_completed Agent 完成 — 记录类型/模型/prompt长度/响应长度/工具调用数/耗时/token数
tengu_agent_tool_remote_launched Agent 在远程 CCR 环境启动
tengu_agent_tool_selected Agent 被选中 — 记录类型/模型/来源/是否内置/是否异步/是否fork
tengu_agent_tool_terminated Agent 被终止 — 记录终止原因
tengu_at_mention_agent_not_found @提及的 Agent 未找到
tengu_at_mention_agent_success @提及 Agent 成功
tengu_conversation_forked 对话被 Fork
tengu_coordinator_mode_switched Coordinator 模式切换 — 记录切换方向
tengu_fork_agent_query Fork 子进程查询 — 记录标签/耗时/消息数/token(含缓存命中量)
tengu_session_forked_branches_fetched Fork 分支信息获取
tengu_slash_command_forked 斜杠命令触发 Fork
tengu_subagent_at_mention 子 Agent 被 @提及
tengu_team_created 团队创建 — 记录团队名/teammate数/lead类型/模式
tengu_team_deleted 团队删除
tengu_team_mem_accessed 团队记忆访问
tengu_team_mem_entries_capped 团队记忆条目达到上限
tengu_team_mem_file_edit 团队记忆文件编辑
tengu_team_mem_file_read 团队记忆文件读取
tengu_team_mem_file_write 团队记忆文件写入
tengu_team_mem_push_suppressed 团队记忆推送被抑制
tengu_team_mem_secret_skipped 团队记忆跳过了含密钥的内容
tengu_team_mem_sync_pull 团队记忆同步拉取
tengu_team_mem_sync_push 团队记忆同步推送
tengu_team_mem_sync_started 团队记忆同步开始
tengu_team_memdir_disabled 团队记忆目录禁用
tengu_teammate_default_model_changed Teammate 默认模型变更
tengu_teammate_mode_changed Teammate 模式变更
tengu_transcript_input_to_teammate 转录输入发送给 Teammate
上下文 & 压缩 优化压缩策略和缓存效率 34 个
tengu_auto_compact_setting_changed 自动压缩设置变更
tengu_auto_compact_succeeded 自动压缩成功 — 记录原始消息数/压缩后消息数/前后 token/压缩API的token消耗
tengu_cache_eviction_hint 缓存淘汰提示 — 通知服务端子 Agent 的缓存可以释放
tengu_cached_microcompact 缓存微压缩 — 记录删除的工具结果数/活跃工具数/触发阈值
tengu_compact 上下文压缩执行 — 记录压缩前后 token 数/是否自动/是否会再触发
tengu_compact_cache_sharing_fallback 压缩缓存共享降级
tengu_compact_cache_sharing_success 压缩缓存共享成功
tengu_compact_failed 压缩失败
tengu_compact_ptl_retry 压缩 prompt-too-long 重试
tengu_context_size 上下文大小 — 记录 git status/CLAUDE.md/总大小/MCP工具数和token
tengu_context_window_exceeded 上下文窗口超限 — 记录 max_tokens 和实际输出
tengu_max_tokens_context_overflow_adjustment 上下文溢出调整
tengu_max_tokens_escalate 最大 token 数升级
tengu_max_tokens_reached 最大 token 数达到
tengu_partial_compact 部分压缩
tengu_partial_compact_failed 部分压缩失败
tengu_post_autocompact_turn 自动压缩后的轮次 — 记录压缩后的运行状态
tengu_session_memory_accessed Session Memory 访问
tengu_session_memory_extraction Session Memory 提取 — 记录 token 消耗/缓存命中/配置参数
tengu_session_memory_file_read Session Memory 文件读取
tengu_session_memory_gate_disabled Session Memory 被门控禁用
tengu_session_memory_init Session Memory 初始化
tengu_session_memory_loaded Session Memory 加载
tengu_session_memory_manual_extraction Session Memory 手动提取(/summary)
tengu_sm_compact_empty_template SM 压缩模板为空 — 退回传统压缩
tengu_sm_compact_error SM 压缩错误
tengu_sm_compact_flag_check SM 压缩功能标记检查
tengu_sm_compact_no_session_memory SM 压缩无 session memory — 退回传统压缩
tengu_sm_compact_resumed_session SM 压缩恢复会话
tengu_sm_compact_summarized_id_not_found SM 压缩摘要 ID 未找到
tengu_sm_compact_threshold_exceeded SM 压缩后仍超阈值 — 退回传统压缩
tengu_snip_resume_filtered Snip 恢复时过滤
tengu_time_based_microcompact 基于时间的微压缩
tengu_token_budget_completed Token 预算用完
OAuth & 认证 监控认证流程成功率 34 个
tengu_config_auth_loss_prevented config auth loss prevented
tengu_login_from_refresh_token login from refresh token
tengu_oauth_401_recovered_from_keychain oauth 401 recovered from keychain
tengu_oauth_auth_code_received oauth auth code received
tengu_oauth_automatic_redirect oauth automatic redirect
tengu_oauth_automatic_redirect_error oauth automatic redirect error
tengu_oauth_claudeai_forced oauth claudeai forced
tengu_oauth_console_forced oauth console forced
tengu_oauth_error oauth error
tengu_oauth_flow_start oauth flow start
tengu_oauth_manual_entry oauth manual entry
tengu_oauth_profile_fetch_success oauth profile fetch success
tengu_oauth_roles_stored oauth roles stored
tengu_oauth_storage_warning oauth storage warning
tengu_oauth_success oauth success
tengu_oauth_token_exchange_error oauth token exchange error
tengu_oauth_token_exchange_success oauth token exchange success
tengu_oauth_token_refresh_failure oauth token refresh failure
tengu_oauth_token_refresh_lock_acquired oauth token refresh lock acquired
tengu_oauth_token_refresh_lock_acquiring oauth token refresh lock acquiring
tengu_oauth_token_refresh_lock_error oauth token refresh lock error
tengu_oauth_token_refresh_lock_released oauth token refresh lock released
tengu_oauth_token_refresh_lock_releasing oauth token refresh lock releasing
tengu_oauth_token_refresh_lock_retry oauth token refresh lock retry
tengu_oauth_token_refresh_lock_retry_limit_reached oauth token refresh lock retry limit reached
tengu_oauth_token_refresh_race_recovered oauth token refresh race recovered
tengu_oauth_token_refresh_race_resolved oauth token refresh race resolved
tengu_oauth_token_refresh_starting oauth token refresh starting
tengu_oauth_token_refresh_success oauth token refresh success
tengu_oauth_tokens_inference_only oauth tokens inference only
tengu_oauth_tokens_not_claude_ai oauth tokens not claude ai
tengu_oauth_tokens_save_exception oauth tokens save exception
tengu_oauth_tokens_save_failed oauth tokens save failed
tengu_oauth_tokens_saved oauth tokens saved
Bash & 工具执行 追踪工具使用频率和错误率 32 个
tengu_auto_mode_malformed_tool_input auto mode malformed tool input
tengu_bash_ast_too_complex Bash AST 解析过于复杂
tengu_bash_command_explicitly_backgrounded Bash 命令被手动后台化
tengu_bash_tool_command_executed Bash 命令执行 — 记录执行情况(不记录命令内容)
tengu_bash_tool_reset_to_original_dir Bash 工具重置到原始目录
tengu_bash_tool_simple_echo Bash 简单 echo 命令
tengu_code_indexing_tool_used 代码索引工具使用
tengu_duplicate_tool_use_id 重复的工具调用 ID
tengu_file_suggestions_ripgrep 文件建议 ripgrep
tengu_message_level_tool_result_budget_enforced message level tool result budget enforced
tengu_powershell_command_explicitly_backgrounded PowerShell 命令后台化
tengu_powershell_tool_command_executed PowerShell 命令执行
tengu_ripgrep_availability ripgrep 可用性检查
tengu_ripgrep_eagain_retry ripgrep EAGAIN 重试
tengu_tool_empty_result 工具返回空结果
tengu_tool_input_json_parse_fail 工具输入 JSON 解析失败
tengu_tool_result_pairing_repaired 工具结果配对修复
tengu_tool_result_persisted 工具结果持久化到磁盘
tengu_tool_result_persisted_message_budget 工具结果持久化(消息预算触发)
tengu_tool_search_mode_decision 工具搜索模式决策
tengu_tool_search_outcome 工具搜索结果
tengu_tool_use_can_use_tool_allowed 工具使用权限允许 — 记录允许原因
tengu_tool_use_can_use_tool_rejected 工具使用权限拒绝
tengu_tool_use_cancelled 工具调用取消
tengu_tool_use_diff_computed 工具调用 diff 计算
tengu_tool_use_error 工具调用错误
tengu_tool_use_progress 工具调用进度
tengu_tool_use_rejected_in_prompt 工具调用在 prompt 中被拒绝
tengu_tool_use_success 工具调用成功
tengu_tool_use_tool_result_mismatch_error 工具调用结果不匹配错误
tengu_unexpected_tool_result unexpected tool result
tengu_web_fetch_host Web 抓取目标域名(只记录域名不记录内容)
API & 网络请求 监控 API 调用质量和性能 31 个
tengu_api_529_background_dropped 后台任务遇到 529 超载 — 直接丢弃不重试
tengu_api_after_normalize API 请求消息规范化后的状态
tengu_api_before_normalize API 请求消息规范化前的状态
tengu_api_cache_breakpoints 缓存断点位置 — 记录 system prompt 和 tool 的缓存标记位
tengu_api_custom_529_overloaded_error 自定义 529 超载错误处理
tengu_api_error API 调用失败 — 记录错误类型
tengu_api_key_keychain_error 钥匙串读写 API Key 失败
tengu_api_key_saved_to_config API Key 存入配置文件
tengu_api_key_saved_to_keychain API Key 存入系统钥匙串
tengu_api_opus_fallback_triggered Opus 模型降级触发 — 记录降级原因
tengu_api_persistent_retry_wait 持久重试等待 — 后台无人值守模式的长时间重试
tengu_api_query API 查询发起 — 记录 querySource
tengu_api_retry API 重试 — 记录重试次数和原因
tengu_api_success 每次 API 调用成功 — 记录模型、token 消耗、缓存命中量
tengu_compact_streaming_retry 压缩流式重试
tengu_image_api_validation_failed image api validation failed
tengu_nonstreaming_fallback_error nonstreaming fallback error
tengu_nonstreaming_fallback_started nonstreaming fallback started
tengu_oauth_api_key oauth api key
tengu_query_error query error
tengu_refusal_api_response refusal api response
tengu_streaming_error streaming error
tengu_streaming_fallback_to_non_streaming streaming fallback to non streaming
tengu_streaming_idle_timeout streaming idle timeout
tengu_streaming_stall streaming stall
tengu_streaming_stall_summary streaming stall summary
tengu_streaming_tool_execution_not_used streaming tool execution not used
tengu_streaming_tool_execution_used streaming tool execution used
tengu_ws_transport_closed ws transport closed
tengu_ws_transport_reconnected ws transport reconnected
tengu_ws_transport_reconnecting ws transport reconnecting
Session & 历史 监控会话持久化和恢复 28 个
tengu_file_history_backup_deleted_file 文件历史备份删除
tengu_file_history_backup_file_created 文件历史备份创建
tengu_file_history_backup_file_failed 文件历史备份失败
tengu_file_history_resume_copy_failed 文件历史恢复复制失败
tengu_file_history_rewind_failed 文件历史回退失败
tengu_file_history_rewind_restore_file_failed 文件历史回退恢复文件失败
tengu_file_history_rewind_success 文件历史回退成功
tengu_file_history_snapshot_failed 文件历史快照失败
tengu_file_history_snapshot_success 文件历史快照成功
tengu_file_history_snapshots_setting_changed 文件历史快照设置变更
tengu_file_history_track_edit_failed 文件历史追踪编辑失败
tengu_file_history_track_edit_success 文件历史追踪编辑成功
tengu_history_picker_select 历史选择器选择
tengu_resume_consistency_delta 恢复一致性差异
tengu_resume_print 恢复打印
tengu_session_file_read 会话文件读取
tengu_session_linked_to_pr 会话关联到 PR
tengu_session_persistence_failed 会话持久化失败
tengu_session_renamed 会话重命名
tengu_session_resumed 会话恢复
tengu_session_tagged 会话标记
tengu_session_title_generated 会话标题生成
tengu_transcript_accessed 转录访问
tengu_transcript_exit 转录退出
tengu_transcript_parent_cycle 转录父循环
tengu_transcript_toggle_show_all 转录切换显示全部
tengu_transcript_view_enter 进入转录视图
tengu_transcript_view_exit 退出转录视图
Plugin 插件 追踪插件生态使用情况 26 个
tengu_headless_plugin_install 无界面插件安装
tengu_marketplace_added 市场添加
tengu_marketplace_background_install 市场后台安装
tengu_marketplace_removed 市场移除
tengu_marketplace_updated 市场更新
tengu_marketplace_updated_all 市场全部更新
tengu_official_marketplace_auto_install 官方市场自动安装
tengu_plugin_command_failed 插件命令失败
tengu_plugin_disable_command 插件禁用命令
tengu_plugin_disabled_all_cli 所有插件通过 CLI 禁用
tengu_plugin_disabled_cli 插件通过 CLI 禁用
tengu_plugin_enable_command 插件启用命令
tengu_plugin_enabled_cli 插件通过 CLI 启用
tengu_plugin_enabled_for_session 插件为会话启用
tengu_plugin_hint_detected 插件提示检测
tengu_plugin_install_command 插件安装命令
tengu_plugin_installed 插件安装
tengu_plugin_installed_cli 插件通过 CLI 安装
tengu_plugin_list_command 插件列表命令
tengu_plugin_load_failed 插件加载失败
tengu_plugin_uninstall_command 插件卸载命令
tengu_plugin_uninstalled_cli 插件通过 CLI 卸载
tengu_plugin_update_command 插件更新命令
tengu_plugin_updated_cli 插件通过 CLI 更新
tengu_plugins_loaded 插件加载完成
tengu_sync_plugin_install_timeout 同步插件安装超时
权限 & 安全 监控安全事件和权限决策 23 个
tengu_apiKeyHelper_missing_trust11 API Key Helper 缺少信任
tengu_awsAuthRefresh_missing_trust AWS 认证刷新缺少信任
tengu_awsCredentialExport_missing_trust AWS 凭证导出缺少信任
tengu_bash_security_check_triggered Bash 安全检查触发(.git/.bashrc 等保护)
tengu_claude_md_permission_error claude md permission error
tengu_claude_rules_md_permission_error claude rules md permission error
tengu_gcpAuthRefresh_missing_trust GCP 认证刷新缺少信任
tengu_internal_bash_classifier_result Bash 分类器结果(AI判断命令是否安全)
tengu_internal_bash_tool_use_permission_request Bash 工具权限请求(内部)
tengu_internal_record_permission_context 记录权限上下文
tengu_internal_tool_use_permission_request_no_always_allow 工具权限请求(无always allow选项)
tengu_managed_settings_security_dialog_accepted 托管设置安全对话框接受
tengu_managed_settings_security_dialog_rejected 托管设置安全对话框拒绝
tengu_managed_settings_security_dialog_shown 托管设置安全对话框显示
tengu_mcp_headersHelper_missing_trust MCP Headers Helper 缺少信任
tengu_migrate_bypass_permissions_accepted bypass 权限迁移接受
tengu_permission_explainer_error 权限解释生成失败
tengu_permission_explainer_generated 权限解释生成(AI 解释命令为什么需要权限)
tengu_permission_request_escape 权限请求按 Esc 取消
tengu_permission_request_option_selected 权限请求选项选择 — 记录用户选了 y/n/a
tengu_tool_use_granted_by_permission_hook 权限由 Hook 授予
tengu_tool_use_show_permission_request 显示权限确认框
tengu_tree_sitter_security_divergence Tree-sitter 安全解析分歧
文件操作 追踪文件读写和记忆持久化 23 个
tengu_atomic_write_error 原子写入错误
tengu_attachment_file_too_large attachment file too large
tengu_binary_content_persisted 二进制内容持久化
tengu_file_changed 文件变更
tengu_file_list_failed 文件列表失败
tengu_file_operation 文件操作
tengu_file_persistence_completed 文件持久化完成
tengu_file_persistence_limit_exceeded 文件持久化超限
tengu_file_persistence_started 文件持久化开始
tengu_file_read_dedup 文件读取去重(避免重复读同一文件)
tengu_file_read_limits_override 文件读取限制覆盖
tengu_file_suggestions_git_ls_files 文件建议 git ls-files
tengu_file_suggestions_query 文件建议查询
tengu_file_upload_failed 文件上传失败
tengu_memdir_accessed 记忆目录访问
tengu_memdir_disabled 记忆目录禁用
tengu_memdir_file_edit 记忆文件编辑
tengu_memdir_file_read 记忆文件读取
tengu_memdir_file_write 记忆文件写入
tengu_memdir_loaded 记忆目录加载
tengu_memdir_prefetch_collected 记忆目录预取收集
tengu_watched_file_compression_failed 监视文件压缩失败
tengu_write_claudemd 写入 CLAUDE.md
UI & 用户体验 追踪功能采用率和用户满意度 20 个
tengu_accept_feedback_mode_collapsed 接受反馈模式折叠
tengu_accept_feedback_mode_entered 进入接受反馈模式
tengu_claude_in_chrome_setting_changed Chrome 扩展设置变更
tengu_claude_in_chrome_setup Chrome 扩展设置
tengu_claude_in_chrome_setup_failed Chrome 扩展设置失败
tengu_custom_keybindings_loaded 自定义快捷键加载
tengu_feedback_survey_event 反馈调查事件
tengu_flicker flicker
tengu_help_toggled help toggled
tengu_keybinding_fallback_used 快捷键降级使用
tengu_notification_method_used 通知方式使用
tengu_onboarding_step 新手引导步骤
tengu_reject_feedback_mode_collapsed 拒绝反馈模式折叠
tengu_reject_feedback_mode_entered 进入拒绝反馈模式
tengu_skill_improvement_survey 技能改进调查
tengu_voice_recording_completed 语音录制完成
tengu_voice_recording_started 语音录制开始
tengu_voice_silent_drop_replay 语音静默丢弃重放
tengu_voice_stream_early_retry 语音流提前重试
tengu_voice_toggled 语音模式切换
Kairos & 主动代理 追踪主动功能使用 9 个
tengu_auto_dream_completed AI 做梦完成
tengu_auto_dream_failed AI 做梦失败
tengu_auto_dream_fired AI 做梦(记忆整理)触发
tengu_brief_mode_enabled Brief 模式启用
tengu_brief_mode_toggled Brief 模式切换
tengu_brief_send Brief 发送
tengu_scheduled_task_expired 定时任务过期
tengu_scheduled_task_fire 定时任务触发
tengu_scheduled_task_missed 定时任务错过
Skill 技能 追踪技能使用和改进 6 个
tengu_skill_descriptions_truncated 技能描述被截断
tengu_skill_file_changed 技能文件变更
tengu_skill_improvement_detected 技能改进检测
tengu_skill_loaded 技能加载
tengu_skill_tool_invocation 技能工具调用
tengu_skill_tool_slash_prefix 技能工具斜杠前缀

元数据足以画出完整用户画像

虽然不发代码内容,但 665 种事件的元数据组合起来可以知道:

什么时间活跃(事件时间戳)
用什么模型、多频繁(model + 事件频率)
每次消耗多少 token(inputTokens/outputTokens)
在什么仓库工作(rh 哈希)
用什么工具、多少次(tool 事件)
Agent 创建/完成模式(agent 事件)
权限被拒绝多少次(permission 事件)
直连还是中转站(apiBaseUrlHost)

GrowthBook 远程控制能力分析

基于源码中 GrowthBook 的用户属性字段,以下操作在技术上完全可行

操作 依赖的字段 效果
精确封禁单个用户accountUUID所有功能静默关闭
封禁整个组织organizationUUID公司所有人失去特定功能
限制中转站用户apiBaseUrlHost检测到非官方端点 → 降级功能
成本惩罚(软围剿)subscriptionType关闭 1h 缓存 → 成本涨 4 倍
免费用户降级subscriptionType=free关闭 Agent/Coordinator/Voice 等
按平台限制platform比如只在 macOS 开沙箱

GrowthBook 控制链路深度分析(源码级)

完整控制链路:从服务器到你的功能开关
1 启动 — Claude Code 带着你的 12 个属性去问 GrowthBook 服务器
2 服务器决策 — 根据属性匹配规则,返回每个 flag 的值(true/false/配置对象)
3 本地缓存 — 值写入 ~/.claude.json,下次启动不依赖网络
4 运行时读取getFeatureValue_CACHED_MAY_BE_STALE('flag_name', default)
5 定期刷新 — 长时间运行的会话会后台 refreshFeatures() 拉取最新值
6 实验记录 — 如果 flag 来自 A/B 实验,首次读取时 logExposureForFeature() 上报
growthbook.ts — A/B 实验数据结构
// 每个 feature flag 可能是 A/B 实验的一部分 type StoredExperimentData = { experimentId: string, // 实验名称 variationId: number, // 你被分到哪组(0=对照组, 1=实验组) inExperiment?: boolean, // 是否在实验中 hashAttribute?: string, // 用什么属性做分组哈希(通常是 id) } // 当你读取一个 flag 值时,如果它来自实验: function logExposureForFeature(feature) { // 上报到 Anthropic 的 1P 分析系统: // "用户 X 在实验 Y 中看到了 variationId=1 的版本" logGrowthBookExperimentTo1P({ experimentId, variationId, userAttributes }) } // 去重:每个 feature 每个会话只上报一次 if (loggedExposures.has(feature)) return

源码中已确认被 GrowthBook 控制的功能(真实 flag 名)

GrowthBook Flag 名 控制什么 影响
tengu_prompt_cache_1h_config1 小时缓存白名单关掉 → 成本涨 4 倍
tengu_amber_stoatExplore/Plan Agent 是否可用false → 没有快速搜索和规划 Agent
tengu_amber_quartz_disabledVoice 模式紧急下线true → 全球语音功能立即关闭
tengu_session_memorySession Memory 是否启用false → 无本地记忆持久化
tengu_sm_compactSession Memory 压缩false → 退回传统全量 AI 摘要
tengu_hive_evidenceVerification Agentfalse → 无对抗性验证
tengu_agent_list_attachAgent 列表放在 attachment 而非 tool description影响缓存命中率
tengu_cobalt_raccoon抑制主动 autocompacttrue → 只靠 reactive compact
tengu_satin_quoll工具结果持久化阈值覆盖按工具名设不同的截断阈值
tengu_max_version_config版本锁 — 强制升级或禁止旧版旧版本直接不能用
tengu_sandbox_disabled_commands沙箱排除的命令列表远程控制哪些命令不进沙箱
tengu_sm_compact_configSession Memory 压缩参数远程调整 minTokens/maxTokens

以上 flag 名均从源码中提取,不是猜测。Anthropic 可以随时在 GrowthBook 后台修改这些值

GrowthBook 决策机制详解(源码级)

源码中有 214 个 GrowthBook 读取点,使用 4 种不同的读取 API。每种 API 的行为不同,决定了决策的实时性和可靠性

4 种读取 API

getFeatureValue_CACHED_MAY_BE_STALE
最常用(约 180 处)。立即返回,不等网络
优先级:环境变量覆盖 → 配置文件覆盖 → 内存缓存 → 磁盘缓存(~/.claude.json) → 默认值
值可能是旧的——"MAY_BE_STALE"就是这个意思
getDynamicConfig_BLOCKS_ON_INIT
阻塞式。等 GrowthBook 初始化完成才返回
用于必须拿到最新值的场景(如 Session Memory 配置参数)
会阻塞启动——只用在非热路径上
getDynamicConfig_CACHED_MAY_BE_STALE
复杂配置对象(非 bool)。立即返回,不等网络
返回 JSON 对象(如 { allowlist: [...], minTokens: 10000 })
checkStatsigFeatureGate_CACHED
迁移用。从旧的 Statsig 系统迁移到 GrowthBook
先查 GrowthBook 缓存,没有再查 Statsig 缓存
标记为 @deprecated,新代码不再使用

决策的 5 级回退链

growthbook.ts — getFeatureValue 的决策链
function getFeatureValue(feature, defaultValue) { // Level 1: 环境变量覆盖(开发/测试用) overrides = getEnvOverrides() // CLAUDE_CODE_GB_OVERRIDES='{"flag":true}' if (feature in overrides) return overrides[feature] // Level 2: 配置文件覆盖(eval harness 用) configOverrides = getConfigOverrides() if (feature in configOverrides) return configOverrides[feature] // Level 3: GrowthBook 内存缓存(最新一次 API 刷新的值) if (remoteEvalFeatureValues.has(feature)) return remoteEvalFeatureValues.get(feature) // Level 4: 磁盘缓存 ~/.claude.json(跨进程存活) cached = getGlobalConfig().cachedGrowthBookFeatures?.[feature] if (cached !== undefined) return cached // Level 5: 代码里写死的默认值 return defaultValue }
这意味着什么
GrowthBook 服务器挂了? → Level 4 磁盘缓存兜底,用上次的值。Claude Code 不会挂
网络断了? → 同上。磁盘缓存在 ~/.claude.json 里
Anthropic 改了值? → 正在运行的会话每 20 分钟或 6 小时(看配置)刷新一次。下次启动立即生效
你想本地覆盖? → Level 1 的环境变量覆盖优先级最高。CLAUDE_CODE_GB_OVERRIDES='{"tengu_amber_stoat":true}' 可以强制开启任何 flag

完整 Flag 清单(65 个独立 Flag,源码提取)

所有 flag 名使用代号(如 tengu_amber_stoat),故意不用可读名称——防止外部猜测功能含义

Flag 名 类型 控制什么 默认值 影响
tengu_amber_stoatboolExplore/Plan Agenttruefalse→无搜索/规划Agent
tengu_amber_quartz_disabledboolVoice 模式紧急下线falsetrue→全球语音关闭
tengu_session_memoryboolSession Memoryfalsetrue→启用记忆持久化
tengu_sm_compactboolSM 压缩falsetrue→用 SM 做压缩
tengu_hive_evidenceboolVerification Agentfalsetrue→启用对抗性验证
tengu_agent_list_attachboolAgent 列表放 attachmentfalsetrue→减少10%缓存浪费
tengu_prompt_cache_1h_configobject1h 缓存白名单{}{allowlist:[...]}控制谁有1h缓存
tengu_satin_quollobject工具结果持久化阈值{}{tool_name:chars}按工具设截断
tengu_sm_compact_configobjectSM 压缩参数{}远程调整 minTokens/maxTokens
tengu_cobalt_raccoonbool抑制主动 autocompactfalsetrue→只靠 reactive compact
tengu_slim_subagent_claudemdbool子 Agent 跳过 CLAUDE.mdtruefalse→子Agent加载全量CLAUDE.md
tengu_scratchboolScratchpad 共享目录Worker 间共享文件的临时目录
tengu_ultraplan_modelstringUltraPlan 使用的模型opus46远程指定云端规划用什么模型
tengu_sandbox_disabled_commandsobject沙箱排除的命令{commands:[],substrings:[]}远程控制哪些命令不进沙箱
tengu_max_version_configobject版本锁强制升级或禁止旧版运行
tengu_auto_background_agentsboolAgent 自动后台化falsetrue→120s后自动转后台
tengu_destructive_command_warningbool危险命令警告额外的危险命令提示
tengu_thinkbackboolThinkBack 年度回顾控制 /think-back 命令可用性
tengu_lodestone_enabledboolLodestone 功能内部功能门控
tengu_chrome_auto_enableboolChrome 扩展自动启用控制 Claude in Chrome 自动连接
tengu_desktop_upsellbool桌面版推广是否显示桌面 App 推广
tengu_immediate_model_commandbool/model 命令即时执行模型切换命令是否立即生效
tengu_willow_modebool未知内部功能代号功能,源码中无明确注释
tengu_marble_foxbool附件处理实验attachments.ts 中使用
tengu_amber_flintboolAmber 模型变体 AA/B 模型实验
tengu_amber_prismboolAmber 模型变体 BA/B 模型实验
tengu_amber_json_toolsboolJSON 工具格式实验工具调用格式变体
tengu_cobalt_harborboolCobalt 实验 AA/B 实验
tengu_cobalt_lanternboolCobalt 实验 BA/B 实验
tengu_bridge_repl_v2boolREPL Bridge v2bridgeEnabled.ts 新版远程协议
tengu_bridge_system_initboolBridge 系统初始化桥接模式启动参数
tengu_ccr_bridgegateCCR 远程桥接云端远程执行入口(blocking gate)
tengu_ccr_mirrorboolCCR 镜像模式远程执行镜像功能
tengu_ccr_bundle_seed_enabledgateCCR Bundle Seed远程沙箱预打包
tengu_coral_fernboolMemory 目录功能memdir.ts 记忆目录
tengu_herring_clockboolMemory 定时功能memdir.ts 记忆定时提取
tengu_terminal_panelbool终端面板 UIREPL.tsx 终端分屏
tengu_terminal_sidebarbool终端侧边栏REPL.tsx 侧边栏布局
tengu_kairos_briefboolKAIROS 简洁模式主动式 AI 的简洁版
tengu_remote_backendbool远程执行后端UltraPlan 云端后端
tengu_attribution_headerbool署名头system prompt 中的署名行
tengu_pebble_leaf_prunebool会话存储清理sessionStorage.ts 过期清理
tengu_moth_copsebool附件实验 Battachments.ts 变体
tengu_copper_bridgeboolCopper 桥接实验桥接模式变体
tengu_copper_pandaboolCopper 实验 B实验变体
tengu_glacier_2xrboolGlacier 实验代号功能
tengu_basalt_3krboolBasalt 实验代号功能
tengu_jade_anvil_4boolJade 实验 v4代号功能
tengu_lapis_finchboolLapis 实验代号功能
tengu_slate_prismboolSlate 实验 A代号功能
tengu_slate_thimbleboolSlate 实验 B代号功能
tengu_quartz_lanternboolQuartz 实验代号功能
tengu_surreal_daliboolSurreal 实验代号功能
tengu_birch_trellisboolBirch 实验代号功能
tengu_bramble_lintelboolBramble 实验代号功能
tengu_chomp_inflectionboolChomp 实验代号功能
tengu_collage_kaleidoscopeboolCollage 实验代号功能
tengu_marble_sandcastleboolMarble 实验 B代号功能
tengu_miraculo_the_bardboolMiraculo 实验代号功能
tengu_strap_foyerboolStrap 实验代号功能
tengu_turtle_carbonboolTurtle 实验代号功能
tengu_passport_quailbool认证相关OAuth/认证实验
tengu_otk_slot_v1boolOTK 插槽 v1一次性密钥实验
tengu_cicada_nap_msnumberCicada 休眠毫秒数远程调轮询间隔
tengu_harborgateHarbor 入口blocking gate,启动时检查
tengu_harbor_permissionsboolHarbor 权限Harbor 功能的权限控制
tengu_trace_lanternboolBeta Tracing 门控控制 OTLP 详细追踪
tengu_fgtsboolFGTS 功能缩写代号
tengu_trusted_device_gatestring可信设备门控"cached"|"blocking" 变体
tengu_event_sampling_configobject事件采样率配置{}远程调整每种事件的采样率
tengu_auto_mode_configobjectAuto Mode 参数{}自动模式行为配置
tengu_chair_sermongateChair 实验Statsig 迁移 flag
tengu_tool_peargateTool 实验Statsig 迁移 flag
enhanced_telemetry_betabool增强遥测 BetaOTLP 追踪开关

编译时 feature() 开关(110 个,死代码消除)

与 GrowthBook 不同,这些在构建时决定。feature('FLAG') 返回 false 时,整个代码块被编译器移除(tree shaking),npm 发布版不包含这些功能的代码

KAIROS KAIROS_BRIEF KAIROS_DREAM KAIROS_CHANNELS KAIROS_PUSH_NOTIFICATION KAIROS_GITHUB_WEBHOOKS ULTRAPLAN ULTRATHINK BUDDY VOICE_MODE COORDINATOR_MODE FORK_SUBAGENT VERIFICATION_AGENT BUILTIN_EXPLORE_PLAN_AGENTS CONTEXT_COLLAPSE CACHED_MICROCOMPACT REACTIVE_COMPACT FILE_PERSISTENCE EXTRACT_MEMORIES AGENT_MEMORY_SNAPSHOT BRIDGE_MODE CCR_MIRROR CCR_AUTO_CONNECT CCR_REMOTE_SETUP DIRECT_CONNECT SSH_REMOTE DAEMON TEAMMEM AGENT_TRIGGERS AGENT_TRIGGERS_REMOTE MCP_SKILLS MCP_RICH_OUTPUT WEB_BROWSER_TOOL MONITOR_TOOL WORKFLOW_SCRIPTS TEMPLATES HISTORY_PICKER HISTORY_SNIP TERMINAL_PANEL TOKEN_BUDGET PROMPT_CACHE_BREAK_DETECTION BASH_CLASSIFIER TRANSCRIPT_CLASSIFIER TREE_SITTER_BASH TREE_SITTER_BASH_SHADOW CONNECTOR_TEXT COMPACTION_REMINDERS STREAMLINED_OUTPUT MESSAGE_ACTIONS REVIEW_ARTIFACT AUTO_THEME NEW_INIT NATIVE_CLIPBOARD_IMAGE NATIVE_CLIENT_ATTESTATION ANTI_DISTILLATION_CC COMMIT_ATTRIBUTION BUILDING_CLAUDE_APPS EXPERIMENTAL_SKILL_SEARCH SKILL_IMPROVEMENT HOOK_PROMPTS LODESTONE TORCH QUICK_SEARCH BG_SESSIONS UDS_INBOX AWAY_SUMMARY PERFETTO_TRACING SLOW_OPERATION_LOGGING MEMORY_SHAPE_TELEMETRY COWORKER_TYPE_TELEMETRY SHOT_STATS ABLATION_BASELINE HARD_FAIL OVERFLOW_TEST_TOOL DUMP_SYSTEM_PROMPT BREAK_CACHE_COMMAND RUN_SKILL_GENERATOR ALLOW_TEST_VERSIONS SELF_HOSTED_RUNNER BYOC_ENVIRONMENT_RUNNER DOWNLOAD_USER_SETTINGS UPLOAD_USER_SETTINGS ENHANCED_TELEMETRY_BETA CHICAGO_MCP POWERSHELL_AUTO_MODE UNATTENDED_RETRY IS_LIBC_GLIBC IS_LIBC_MUSL
紫色 = 隐藏的高级功能(KAIROS/BUDDY/VOICE)  蓝色 = 云端执行(ULTRAPLAN/ULTRATHINK)  绿色 = Agent 系统  橙色 = 缓存/压缩/记忆  青色 = 远程连接  灰色 = 其他

重要区分:两条数据通道

通道 1: 遥测事件 (logEvent)
去向:Datadog / Anthropic 1P 分析
内容:只有元数据(工具名/耗时/token 数/成功失败)
不含:代码、对话内容、文件路径
可关:DISABLE_TELEMETRY=1
665 种事件走这条路
通道 2: API 调用 (对话本身)
去向:api.anthropic.com(或中转站)
内容:全部对话内容(你的提问 + Claude 的回答 + 工具输入输出)
包含:你的代码、文件内容、命令输出
不可关:关了就没法用 Claude
这就是对话本身的必要通道

遥测系统"不发代码" ≠ Anthropic 看不到你的代码。你的代码已经通过 API 调用(通道 2)发过去了。两条通道是独立的

关于区域限制
源码里没有 IP 地址、国家代码、地理位置字段发给 GrowthBook。不能直接按"中国区"设规则
但可以间接实现:
1. accountUUID — 注册时 Anthropic 后台已有用户信息,可按账户批量设规则
2. apiBaseUrlHost — 检测中转站域名,按接入点区分
3. 服务端 IP 日志 — HTTP 请求到 API 服务器时,服务端自然能看到客户端 IP。这不在 GrowthBook 里,但在服务端完全可用
4. API 层面直接拒绝 — 最彻底的方式。不需要客户端配合,服务端直接返回 403
所有限制都是静默的 — 功能消失,没有报错,没有"你的区域不支持"提示。用户只会觉得"这个功能好像没有"

中转站能防多少

中转站能遮蔽的
你的真实 IP(服务端看到的是中转站 IP)
你的 accountUUID(如果用中转站的共享 Key)
你的 organizationUUID(同上)
中转站遮不住的
apiBaseUrlHost 暴露你在用中转站
中转站运营者能看到你的全部请求内容(代码)
如果 DISABLE_TELEMETRY 没设,遥测数据照常发给 Anthropic
Prompt Cache 可能失效(成本涨 4 倍)

关闭遥测的方法(源码实证)

privacyLevel.ts — 三级隐私控制
// Level 1: 默认 — 全部开启 // (不设任何环境变量) // Level 2: 关闭遥测 — 分析/Datadog/反馈调查停止 export DISABLE_TELEMETRY=1 // 665 种事件不再发送 // 但 GrowthBook 远程控制、自动更新、API 调用仍然正常 // Level 3: 只保留必要流量 — 最严格 export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 // 遥测 + 自动更新 + 版本检查 + GrowthBook 刷新 全部关闭 // 只保留 API 调用本身 // 注意:API 调用(你的对话内容)不受以上设置影响 // 你的代码和对话始终发给 API 服务器(Anthropic 或中转站)

深度验证:推理管道 vs 遥测管道 vs 反馈管道

你的对话内容走哪条路?源码级逐行验证

三条完全独立的数据管道
管道 1: API 推理(必须的) 本地 messages[] JSON ─→ api.anthropic.com/v1/messages ─→ 回复 发送:完整对话内容(system prompt + 历史 + 新消息) 云端:计算 KV → 返回回复 → KV 缓存(ephemeral, TTL 后释放) 不可关闭 — 这是产品核心功能 管道 2: 遥测(可关闭) 本地事件 ─→ Datadog / 1P Event / BigQuery 发送:元数据(token 数、耗时、模型名、工具名、错误码) 不发送:对话内容、代码、文件路径、用户输入 DISABLE_TELEMETRY=1 关闭 管道 3: Bug 反馈(用户主动触发) 用户按 /bug ─→ api.anthropic.com/api/claude_cli_feedback 发送:完整对话记录 + 子 Agent 记录 + 原始 JSONL 会话文件 仅在用户主动提交时触发,不静默发送

实证 1:logging.ts 发送的字段(逐行验证)

logging.ts — logAPISuccessAndDuration() 实际 payload
// 发送到 Datadog + 1P 的完整字段列表 logEvent('tengu_api_success', { model, // "claude-opus-4-6" inputTokens, // 15000 outputTokens, // 2000 cachedInputTokens, // 12000 uncachedInputTokens, // 3000 durationMs, // 4500 ttftMs, // 890 requestId, // "req_xxx" stop_reason, // "end_turn" costUSD, // 0.0234 textContentLength, // 1500 ← 文本长度,不是文本本身! toolUseContentLengths, // {"Bash":200,"Read":500} ← 长度映射 gateway, // "litellm" | null globalCacheStrategy, // "system_prompt" }) // 注意:没有 prompt, content, message, text, code 这些字段 // 只有 LENGTH(长度)不是 CONTENT(内容)

实证 2:类型系统防呆(编译级强制)

analytics/index.ts — 防止开发者误传代码/路径
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS = never // 这个类型名就是警告: // "我已验证这不是代码或文件路径" // 类型是 never → 不能直接赋值 string // 开发者必须显式 cast 并在代码审查中解释为什么 // MCP 工具名脱敏(非官方工具名变成 "mcp_tool") // 第三方插件名脱敏(变成 "third-party") // 仓库 URL → SHA256 哈希前 16 字符(不可逆)

实证 3:用户输入默认遮蔽

events.ts — redactIfDisabled()
function isUserPromptLoggingEnabled() { return isEnvTruthy(process.env.OTEL_LOG_USER_PROMPTS) } export function redactIfDisabled(content: string): string { return isUserPromptLoggingEnabled() ? content : '<REDACTED>' } // 默认:用户输入在 OTLP span 里显示为 <REDACTED> // 必须手动设 OTEL_LOG_USER_PROMPTS=1 才会发送原文 // 同理:OTEL_LOG_TOOL_CONTENT=1 控制工具输入/输出

唯一例外:/bug 反馈提交

这是唯一发送完整对话内容的路径
Feedback.tsx — /bug 提交的 payload
POST https://api.anthropic.com/api/claude_cli_feedback { transcript: Message[], // ← 完整对话记录 subagentTranscripts: {...}, // ← 所有子 Agent 对话 rawTranscriptJsonl: string, // ← 原始 JSONL 会话文件 description: string, // 用户写的 bug 描述 errors: [...], // 错误日志(API key 已脱敏) platform, version, gitRepo // 环境信息 }
触发条件:用户手动输入 /bug 命令并确认提交
不会静默发送:必须用户主动操作
包含内容:你的完整对话、所有子 Agent 记录、原始会话文件
如果你在对话中讨论了敏感代码/密钥,提交 /bug 会把它们全部发给 Anthropic
常见疑问澄清
Q: 包含完整代码? — 对话里有的全发。tool_result 里的文件内容、git diff、命令输出、截图全部包含 Q: 会自动发送? — 必须手动输入 /bug + 写描述 + 确认。没有自动上报机制 Q: 后台偷偷发? — 只在你点确认那一刻发一次 POST 请求 Q: 代码被"检测/审核"? — 不是审核你的代码内容。是发给 Anthropic 工程团队做 bug 排查的日志 Q: 有脱敏? 部分 — API Key 会被脱敏(errors 里),但代码内容、密码、内部 URL 不会脱敏 Q: 能关掉? 不用 /bug 就永远不会触发。没有配置项,因为它本来就是用户主动行为 Q: 能选择发什么? 不能 — 默认发整个对话记录 + 子 Agent 记录。不能选"只发最后 3 条消息"
实操建议:如果对话中涉及敏感信息(密钥、内部 API、商业逻辑),提交 /bug 前考虑开一个新对话重现问题再提交,避免敏感对话被整体发送

Beta Tracing(高级遥测,默认关闭)

开启条件
ENABLE_BETA_TRACING_DETAILED=1
+ BETA_TRACING_ENDPOINT=...
或 GrowthBook gate tengu_trace_lantern
普通用户默认关闭
开启后发什么
模型输出文本 → 发送(外部用户可见)
thinking 内容 → 仅 Ant 内部员工
系统 prompt → 发送(SHA256 去重)
工具调用详情 → 发送

Protobuf Schema — 服务端的数据合约

之前的分析是从客户端 logEvent() 调用推断发了什么。Protobuf schema 是服务端声明想要接收什么——这是 Anthropic 后端数据库的表结构定义

为什么 Protobuf Schema 比客户端代码更重要
客户端代码说:我今天只填了 model, inputTokens, durationMs
Protobuf 说:我有 67 个字段位置等着你填
客户端今天不填 ≠ 明天不填。Protobuf 有字段 = 服务端已经准备好接收和存储。

ClaudeCodeInternalEvent — 主事件(26 个顶层字段)

claude_code_internal_event.proto — 每个遥测事件的完整信封
interface ClaudeCodeInternalEvent { // ── 事件本体 ── event_name: string // "tengu_api_success" 等 665 种 client_timestamp: Date // 精确到毫秒的操作时间 server_timestamp: Date // ← 服务端注入,客户端控制不了 event_id: string // ← 服务端生成,客户端控制不了 // ── 身份标识(精确到个人)── email: string // 邮箱地址 device_id: string // 设备永久 ID(跨会话不变) auth.account_id: number // 账户数字 ID auth.account_uuid: string // 账户 UUID auth.organization_uuid: string // 组织 UUID session_id: string // 会话追踪 user_type: string // ant/api/consumer // ── 使用上下文 ── model: string // 模型名 entrypoint: string // CLI/IDE/桌面/Web client_type: string // 客户端类型 is_interactive: boolean // 交互式 or 脚本 betas: string // 启用的 beta 功能 // ── Agent 追溯链 ── agent_id: string // 哪个 Agent agent_type: string // Agent 类型 parent_session_id: string // 父会话(子 Agent 追溯链) team_name: string // Swarm 团队名 // ── 插件/技能 ── skill_name: string // 用了什么 Skill plugin_name: string // 用了什么插件 marketplace_name: string // 插件来源市场 // ── 评测(SWE-bench)── swe_bench_run_id: string swe_bench_instance_id: string swe_bench_task_id: string // ── Slack 集成 ── slack: { slack_team_id, is_enterprise_install, trigger, creation_method } // ── 环境指纹(33 个子字段,见下方)── env: EnvironmentMetadata // ── 万能字段 ── additional_metadata: string // base64(JSON) — 什么都能塞 process: string // 进程指标 JSON(Ant-only) agent_sdk_version: string }

EnvironmentMetadata — 设备指纹(33 个字段)

嵌套在每个事件里。这些字段组合起来可以精确识别一台设备——即使换了账号

platform + platform_raw 操作系统
arch CPU 架构
linux_distro_id + version + kernel Linux 精确识别
node_version Node.js 版本
terminal 终端类型
package_managers 包管理器列表
runtimes 运行时列表
version + version_base + build_time 精确版本 + 构建时间
wsl_version WSL 版本
vcs 版本控制类型
is_ci + is_github_action CI/CD 环境
github_actions_metadata.* actor_id + repository_id + owner_id
github_event_name + runner_os GitHub Actions 详情
is_claude_code_remote 是否远程执行
remote_environment_type 远程环境类型
claude_code_container_id 容器 ID
claude_code_remote_session_id 远程会话 ID
is_conductor Coordinator 模式
coworker_type 协作者类型
is_local_agent_mode 本地 Agent 模式
tags[] 标签数组(可装任何分类标记)
deployment_environment 部署环境

additional_metadata — 无 Schema 约束的万能字段

firstPartyEventLoggingExporter.ts — additional_metadata 构建过程
// 1. logEvent() 调用时传入的所有 metadata // 2. 顶层 proto 字段(email, model 等)被提取走 // 3. _PROTO_* 前缀字段被提升到顶层(skill_name 等) // 4. 剩下的全部 → JSON.stringify() → Base64 → additional_metadata 当前已知会塞进去的键: rh // 仓库 URL 的 SHA256 哈希前 16 字符 is_assistant_mode // KAIROS 主动模式是否激活 skill_mode // Skill 发现机制 observer_mode // 观察者分类器 inputTokens // token 数量 outputTokens // token 数量 durationMs // 请求耗时 costUSD // 花费 toolUseContentLengths // 工具输出长度映射 gateway // AI 网关检测(litellm/helicone 等) querySource // 查询来源 errorType // 错误分类 ... 50+ 其他事件特定字段 为什么说它是"黑洞": 1. 类型是 string — 什么 JSON 都能塞 2. 不需要改 .proto 文件就能新增字段 3. 客户端加一行 metadata 代码,服务端自动入库 4. 今天塞 token 数,明天可以塞 fileCount/repoSize 5. 但它不能传二进制(是 JSON string,不是 Buffer) 6. LogEventMetadata 类型只允许 boolean|number|undefined(不允许 string 值) → 类型系统层面防止意外塞入代码/文件路径

GrowthbookExperimentEvent — 实验追踪(12 个字段)

experiment_id 你被分到了哪个实验
variation_id 0=对照组, 1+=实验组
user_attributes 完整 GrowthBook 属性 JSON(含 email/org/订阅)
anonymous_id 匿名 ID——未登录也追踪
device_id 设备永久 ID
session_id 会话 ID
auth.* account_id + account_uuid + organization_uuid
environment 固定 'production'

Protobuf Schema 的 4 个关键发现

1. anonymous_id 说明未登录也追踪
GrowthBook 实验事件有专门的 anonymous_id 字段。即使不登录 Claude.ai,只用 API Key,设备仍被追踪。
2. server_timestamp 是服务端注入
客户端发事件时这两个字段是空的。服务端收到后自动填充 server_timestamp 和 event_id。客户端看不到服务端往事件里加了什么。
3. additional_metadata 绕过类型安全
正规字段加一个需要改 .proto → 重新生成 → 部署。这个字段加什么都不需要改任何东西。虽然有 LogEventMetadata 类型限制(只允许 bool/number),但字段本身是 string。
4. Slack 集成已准备好
SlackContext 结构(slack_team_id, is_enterprise_install, trigger)说明遥测系统已为 Claude-in-Slack 准备好数据通道。

PublicApiAuth — 认证上下文(服务端自动注入)

源码注释:"Authentication context automatically injected by the API"
3 个字段:account_id(数字)+ organization_uuid + account_uuid
这些是 API 服务端在收到请求后自动从 OAuth token 解析并注入的——客户端不需要填,也无法伪造。即使客户端代码不发 org_uuid,服务端也会从你的 token 里提取并填上。

完整端点清单(源码验证)

端点 用途 含对话? 可关?
api.anthropic.com/v1/messages Claude API 推理
api.anthropic.com/api/claude_cli_feedback /bug 反馈 用户主动
api.anthropic.com/api/event_logging/batch 1P 事件日志
http-intake.logs.us5.datadoghq.com Datadog 分析
api.anthropic.com/api/claude_code/metrics BigQuery 指标
api.anthropic.com/api/claude_code/* GrowthBook flags Level 2
api.anthropic.com/api/oauth/account/* Grove 隐私设置 Level 2
OTEL endpoint (自定义) Beta Tracing 可选 默认关

Datadog 公开 API Key

datadog.ts — 硬编码的客户端 token
DD-API-KEY: pubbbf48e6d78dae54bceaa4acf463299bf // pub 前缀 = 公开客户端 token(只能写入,不能读取) // 区域: us5.datadoghq.com // 白名单事件: tengu_api_error, tengu_api_success, // tengu_init, tengu_exit, tengu_tool_use_* 等 // 不在白名单里的事件不会发到 Datadog

GrowthBook 发送的用户画像数据

用于 feature flag 分桶的属性(发送给 Anthropic):
user_id — UUID
session_id — 会话 UUID
device_id — 设备 UUID
platform — darwin/linux/win32
org_uuid — 组织 ID
account_uuid — 账户 ID
user_type — ant/api/consumer
subscription_type — pro/team/...
rate_limit_tier — 限速等级
app_version — 版本号
email — 邮箱地址
api_base_url_host — API 域名
缓存策略:磁盘 24h + 内存 1h → 不是每次请求都发
不含:对话内容、代码、文件路径
源码结论
Claude Code 的源码构建了完整的基础设施:用户身份识别 + 远程功能控制 + 遥测数据收集
这些基础设施是为合法的产品运营(A/B 测试、灰度发布、紧急下线)设计的
但同样的工具也可以用于合规限制 — 技术上完全可以精确控制任何用户的任何功能,且用户不会收到任何通知
区分"能做"和"在做"很重要。源码证明了能做,但没有证据表明在做针对特定区域的限制
数据流总结:常规使用只发元数据(数量、耗时)→ 遥测可关闭 → 推理 API 的对话内容用完即弃(KV Cache TTL 后释放)→ 唯一持久发送完整对话的路径是 /bug 反馈(用户主动触发)

内容审核 — 本地零审核,100% 云端决策

Claude Code 本地没有任何内容检测器。没有 NSFW 过滤、没有暴力分类、没有关键词黑名单。所有安全决策由 Anthropic API 服务端(即 Claude 模型本身)做出

审核架构 — 透明管道

内容审核数据流
用户输入 │ ▼ Unicode 清理(sanitization.ts — 唯一的本地"过滤") 删除隐形字符(Tag chars、格式控制符、私用区) 目的:防 prompt injection,不是内容审查 │ ▼ 可选 Hook 拦截(用户自定义,非自动) PreToolUse hook 可返回 blockingError 拦截 这是用户配的规则,不是内置审核 │ ▼ 原样发给 API(api.anthropic.com/v1/messages) ← 用户输入不做任何内容检查就发出去了 │ ▼ 云端 Claude 模型决策 模型自身的训练决定了什么能回答、什么拒绝 │ ├─ stop_reason: "end_turn" → 正常回复,原样显示 │ └─ stop_reason: "refusal" → 拒绝 │ ▼ 本地处理拒绝(errors.ts:1184-1207) 记录 tengu_refusal_api_response 事件 显示:"This response was flagged by Anthropic's Usage Policy" 建议:换模型试试

源码验证:本地不存在的审核机制

NSFW / 成人内容检测
没有本地分类器、没有图片扫描、没有关键词匹配
暴力内容分类器
没有 ML 模型、没有正则、没有规则引擎
关键词黑名单
没有硬编码的敏感词列表、没有正则匹配
发送前拦截
用户输入不经过任何内容扫描就直接发给 API
接收后过滤
API 回复原样显示给用户,不做二次审查
本地 AUP 执行
使用策略违规完全由服务端判定

本地实际存在的安全机制(2 个,都不是内容审核)

Unicode 清理(自动)
partiallySanitizeUnicode(text) 删除: Tag characters (U+E0000-E007F) Format controls (U+200B-200F, U+2060-206F) Private use area characters 应用于: MCP 工具定义 (client.ts) Deep link 查询 (parseDeepLink.ts) Tag 名称 (tag.tsx) 目的:防止隐形字符注入恶意指令 不是内容审查
Hook 拦截(用户配置)
// settings.json 里用户自定义 { "hooks": { "PreToolUse": [{ "command": "your-check-script.sh" }] } } 如果 hook 返回 blockingError → 阻止工具执行 这是用户自己设的规则 不是 Anthropic 内置的审核

云端拒绝的本地处理(源码)

errors.ts — getErrorMessageIfRefusal()
function getErrorMessageIfRefusal(stopReason, model) { if (stopReason !== 'refusal') return null // 记录遥测事件 logEvent('tengu_refusal_api_response') // 返回用户可见的错误信息 return `This response was flagged as potentially violating Anthropic's Acceptable Use Policy (AUP). If you believe this is in error, try rephrasing. You can also try switching models with /model.` } // 注意:只有两种 stop_reason // "end_turn" — 正常完成 // "refusal" — 服务端拒绝 // 没有 "content_filter" 或其他过滤类 stop_reason // 本地不做任何判断,只是转达服务端的决定

与其他产品的审核架构对比

产品 本地审核 云端审核 架构
Claude Code 模型内置 + AUP 透明管道 — 本地不参与安全决策
ChatGPT App 有(Moderation API 预检) 模型内置 + Moderation Endpoint 双层 — 本地预检 + 云端判定
Cursor / Copilot 有(关键词过滤) 各家 API 的内置审核 混合 — 本地预检 + 云端判定
为什么 Claude Code 选择零本地审核
1. 定位不同 — Claude Code 是开发者工具,不是消费者聊天应用。用户是程序员,可能需要处理安全研究、漏洞分析等敏感内容。本地关键词过滤会产生大量误报。
2. 安全决策集中化 — 所有安全逻辑在模型训练层面和 API 服务端实现。更新安全策略只需更新云端模型,不需要推客户端更新。
3. 开源透明 — 源码可审计(npm sourcemap 还原)。如果有本地审核,会被逆向发现并绕过,反而给人虚假安全感。不如明确告诉你:安全在云端。
4. Hook 可扩展 — 如果企业需要自定义内容策略,可以通过 Hook 系统自行实现,而不是 Anthropic 替你决定什么该拦什么不该拦。

隐私深水区 — 推理 API 本身就是最大的数据通道

遥测不发内容 ≠ Anthropic 看不到你的内容。每次对话,你的代码、文件、架构、决策过程全部通过推理 API 发给服务端。这套数据的用户画像精度远超任何广告平台

推理 API:被忽视的数据通道

每一轮对话发给 api.anthropic.com/v1/messages 的内容
你主动发送的一切: system prompt ← 包含你的 CLAUDE.md(团队规范、项目规则) 用户消息 ← 你说的每一句话、每一个需求 assistant 回复 ← Claude 给你写的所有代码 tool_result: Read ← 你项目里的源代码文件内容 tool_result: Bash ← 命令输出(git log, npm test, env 变量) tool_result: Grep ← 搜索结果(代码片段 + 文件路径) tool_result: Diff ← 代码改动(你在写什么功能) image blocks ← 截图(UI 设计、终端输出、报错信息) 这不是"被收集",是你主动、完整地发给了 API 遥测能关,这个关不了——关了 Claude 就不能回话了

通过推理 API 可以构建的用户画像

画像维度 传统广告平台 推理 API 天然知道的
技术栈 Cookie 追踪访问的技术网站 精确到框架版本(tool_result 里的代码)
技术水平 不知道 问题深度 + 代码质量 + 是否需要解释
项目内容 搜索关键词推测 完整代码库、数据库 schema、API 设计
公司/团队 LinkedIn 自填 CLAUDE.md 团队规范 + git remote URL
消费力 浏览/购买历史推断 订阅档位 + 项目规模 + 使用强度
真实需求 搜索关键词猜测 你直接告诉 Claude 你需要什么
决策风格 不知道 每次选方案 A 还是 B 的对话记录
痛点 不知道 反复 debug 什么、什么功能搞不定
商业机密 不可能知道 产品规划、未发布功能、内部 API

元数据侧信道 — "只发长度"也能推断内容

rh: "a3f8b2c1e9d04567"
仓库 URL 的 SHA256 前 16 字符
公开仓库可彩虹表反查
toolUseContentLengths: {"Bash":50000}
工具输出长度映射
长输出 = 编译/测试大项目
inputTokens: 800000, model: opus[1m]
token 用量 + 模型选择
巨型代码库 + 愿意付费
email + org_uuid + subscription
GrowthBook 用户标识
精确到个人 + 公司 + 付费档

GrowthBook 分桶系统 — 已具备的定向投放基础设施

GrowthBook 的本质是 A/B 测试 + 功能开关。今天控制 feature flags,但同一套基础设施换个 payload 就是精准推送系统

growthbook.ts — GrowthBookUserAttributes 完整定义
type GrowthBookUserAttributes = { id: string // 设备 ID(永久,跨会话不变) sessionId: string // 当前会话 ID deviceID: string // 同 id(冗余字段) platform: string // 'darwin' | 'linux' | 'win32' apiBaseUrlHost?: string // 企业代理部署的域名 organizationUUID?: string // OAuth 组织 ID → 知道你在哪个公司 accountUUID?: string // OAuth 账户 ID → 知道你是谁 userType?: string // 'ant'(内部) | 'api' | 'consumer' subscriptionType?: string // 'pro' | 'team' | 'enterprise' | 'max' rateLimitTier?: string // 限速等级 → 推断使用强度 firstTokenTime?: number // 首次使用时间戳 → 用户生命周期阶段 email?: string // 邮箱地址 → 精确个人标识 appVersion?: string // 版本号 → 更新频率 github?: { // GitHub Actions CI 环境 actor, repository, // GitHub 用户名 + 仓库名 workflow, run_id, ... // 工作流详情 } } // 数据来源: // email ← OAuth 账户 or git config user.email // org/account ← getOauthAccountInfo() // deviceId ← getOrCreateUserID()(首次生成后永久保存)

分桶机制 — 服务端远程评估

growthbook.ts — SDK 初始化配置
const client = new GrowthBook({ apiHost: 'https://api.anthropic.com/', clientKey, attributes, // ← 上面的完整用户属性 remoteEval: true, // ← 关键:服务端做所有评估 cacheKeyAttributes: ['id', 'organizationUUID'], apiHostRequestHeaders: authHeaders, }) // remoteEval: true 的含义: // 客户端把完整用户属性发给服务端 // 服务端做哈希分桶 + 实验分配 // 返回预评估的 feature values // → 本地不做任何分桶逻辑 // → Anthropic 服务端完全掌控分桶结果 // 刷新频率: // 外部用户: 每 6 小时 // 内部员工: 每 20 分钟 // 缓存: 磁盘 ~/.claude.json + 内存 // 启动时如果网络超时(5s),用磁盘缓存兜底

分桶能力矩阵 — 可以按什么维度区分用户

维度 属性 可做的定向
个人 email, accountUUID, deviceID 精确到个人的功能开关
公司 organizationUUID 按公司推不同功能/定价
付费档 subscriptionType, rateLimitTier 按付费能力分层运营
平台 platform (darwin/linux/win32) Mac vs Linux vs Windows 差异化
生命周期 firstTokenTime, appVersion 新用户 vs 老用户不同体验
部署方式 apiBaseUrlHost, userType 直连 vs 企业代理 vs API 用户
CI/CD github.actor, github.repository 知道哪个仓库在用 Claude Code CI

A/B 实验追踪 — 500+ 功能开关

实验曝光事件 — 每个 flag 首次命中时上报
// 每个 feature flag 首次使用时记录曝光 logGrowthBookExperimentTo1P({ event_type: 'GrowthbookExperimentEvent', experiment_id: 'tengu_amber_flint', // 实验名称 variation_id: 1, // 你被分到哪个变体 device_id: 'uuid-xxx', // 你的设备 ID account_uuid: 'uuid-yyy', // 你的账户 ID organization_uuid: 'uuid-zzz', // 你的组织 ID user_attributes: '{ 完整属性 JSON }', // 全部用户属性 }) // 去重:每个 feature 每个会话最多上报 1 次 // 500+ 个 flag 意味着每次会话可能上报 500+ 次曝光 // 部分已知实验名: // tengu_amber_flint/prism/stoat/wren — Amber 模型变体 // tengu_cobalt_harbor/lanterns/raccoon — Cobalt 模型变体 // tengu_session_memory — 会话记忆实验 // tengu_desktop_upsell — 桌面版推广实验

隐私幻觉 — 你能验证的 vs 你不能验证的

你能验证的(源码可审计)
遥测不发对话内容
用户 prompt 默认遮蔽为 <REDACTED>
MCP 工具名脱敏
仓库 URL 用 SHA256 哈希
DISABLE_TELEMETRY 确实关闭遥测
类型系统防止误传代码
你不能验证的(服务端黑盒)
? API 请求体是否被日志记录
? KV Cache 是否真的 TTL 后删除
? 推理过程中是否有旁路存储
? 内部员工能否查看你的对话
? 法律传票下是否会交出数据
? 数据是否用于模型训练

商业化潜力分析

如果未来推出广告,命中率将远超传统平台
数据资产:推理 API 天然积累了用户的技术栈、项目类型、工作模式、决策偏好、痛点需求。这不是推断,是用户主动告知。
投放基础设施:GrowthBook 分桶系统(500+ flags + 实验追踪)已具备按个人/公司/付费档/平台精准推送的能力。今天推 feature,明天可以推广告。
人格画像:沟通风格、耐心程度、对错误的反应、技术品味 —— 一个月的对话数据就能构建比任何问卷都精准的人格模型。
历史参照:Google 当年"我们只做搜索"→ 全球最大广告公司。数据资产在那里,商业模式转型只需要一个董事会决议。
客观说明
Anthropic 目前的商业模式是订阅 + API 按量收费,不是广告。上述分析讨论的是技术能力和数据资产,不是指控。
Anthropic 的 隐私政策使用条款 是法律约束,比源码更有约束力。
但技术分析的价值在于:了解边界。知道什么是策略承诺(可变),什么是技术保证(可审计),做出知情的使用决策。

中转站流量分析 — 谁看到什么

设了 ANTHROPIC_BASE_URL 后,只有推理 API 走中转,所有遥测硬编码直连 Anthropic/Datadog

ANTHROPIC_BASE_URL=https://中转站.com 时的流量路径
经过中转站的(唯一一条) /v1/messages 推理 API → 完整对话内容 直连 Anthropic 的(全部硬编码,绕过中转) /api/event_logging/batch 1P 事件日志 硬编码 api.anthropic.com /api/claude_code/metrics BigQuery 指标 硬编码 /api/claude_code/* GrowthBook flags 硬编码 /api/oauth/account/* Grove 设置 OAuth 配置硬编码 /api/claude_cli_feedback /bug 反馈 硬编码 直连第三方的 http-intake.logs.us5.datadoghq.com Datadog 日志 硬编码 + DD-API-KEY

源码验证:端点硬编码证据

各端点的 URL 构建方式
// datadog.ts:12 — 硬编码第三方 const DATADOG_LOGS_ENDPOINT = 'https://http-intake.logs.us5.datadoghq.com/api/v2/logs' // growthbook.ts:503-506 — 硬编码,只有内部员工能改 const baseUrl = process.env.USER_TYPE === 'ant' ? process.env.CLAUDE_CODE_GB_BASE_URL || 'https://api.anthropic.com/' : 'https://api.anthropic.com/' // ← 外部用户写死 // bigqueryExporter.ts:46 — 硬编码 const defaultEndpoint = 'https://api.anthropic.com/api/claude_code/metrics' // Feedback.tsx:543 — 硬编码 axios.post('https://api.anthropic.com/api/claude_cli_feedback', ...) // grove.ts:65 — 走 OAuth 配置(也是硬编码) const url = `${getOauthConfig().BASE_API_URL}/api/oauth/account/settings` // BASE_API_URL = 'https://api.anthropic.com'(oauth.ts:84-104) // 注意:ANTHROPIC_BASE_URL 只影响 /v1/messages 推理调用 // 所有遥测/分析/设置端点全部绕过中转站

三方可见性矩阵

数据 中转站 Anthropic Datadog
对话内容 能看 能看 看不到
遥测元数据 看不到 能看 能看
GrowthBook 用户属性 看不到 能看 看不到
/bug 反馈记录 看不到 能看 看不到
API Key / OAuth Token 能看 能看 看不到
中转站的双重角色
看得到最敏感的 — 对话内容(你的代码、需求、架构)全部经过中转站,而且中转站拿到了你的 API Key
拦不住遥测 — 即使你想用中转站"隔离"所有流量,遥测、GrowthBook、反馈全部直连 Anthropic,绕过中转站
中转站自身无审计 — Anthropic 有隐私政策约束,中转站通常没有。你的对话内容在中转站那里的安全性完全取决于中转站运营者的自觉
用中转站 ≠ 更安全。反而多了一个能看到你所有对话的第三方,且没有 Anthropic 级别的法律约束。唯一合理的使用场景是企业内网合规审计。

17 隐藏功能:语音模式 — 完整的语音对话输入系统

Claude Code 内置了完整的语音输入系统:按住 Space 录音 → Deepgram STT 实时转文字 → 注入输入框。6 平台原生音频模块、20+ 语言、静默丢包重放、领域词汇增强。代码完整存在于 npm 发布版中,只需 OAuth 登录 + GrowthBook 未远程关闭即可激活

编译后验证:语音代码完整保留在 npm 包中

之前说的"代码被 tree shaking 删除"是错的
我们在编译后的 cli.js(12MB,npm 发布版 v2.1.88)中搜索语音相关字符串,全部存在
搜索项 cli.js 中出现次数 说明
voice28语音相关代码
voiceEnabled7设置开关
voice_stream2WebSocket 端点路径
speech_to_text1STT API 路径
deepgram-nova31Deepgram STT 引擎
amber_quartz1紧急下线 flag
TranscriptText1STT 响应类型
startRecording3录音 API
audio-capture2原生音频模块
KeepAlive10WebSocket 心跳
CloseStream1流结束信号
VOICE_MODE0编译时被替换为字面量,字符串消失

编译时到底发生了什么

源码 vs 编译后的 cli.js 对比
// ── 源码(voiceModeEnabled.ts)── function isVoiceGrowthBookEnabled(): boolean { return feature('VOICE_MODE') ? !getFeatureValue('tengu_amber_quartz_disabled', false) : false } // ── 编译后(cli.js,混淆后还原)── function gK6() { return !g8("tengu_amber_quartz_disabled", false) } 发生了什么: feature('VOICE_MODE') 被替换为 true(不是 false!) 三元表达式被编译器优化 → 只保留 true 分支 false 分支(return false)被删除 结果:isVoiceGrowthBookEnabled() 直接返回 GrowthBook 检查结果 语音功能完全可用!只看 GrowthBook flag + OAuth 之前的分析完全反了: ❌ 以为 feature('VOICE_MODE') = false → 代码删除 ✅ 实际 feature('VOICE_MODE') = true → false 分支删除 → 功能激活

实际激活条件(只需两步)

条件 1: Claude.ai OAuth 登录
/login 登录 Claude.ai 账号
需要有效的 OAuth access token
API Key 用户不行——语音走 claude.ai 专有端点
条件 2: GrowthBook 未远程关闭
tengu_amber_quartz_disabled = false
默认就是 false(= 不禁用 = 可用)
Anthropic 可随时远程设为 true 关闭全球语音
结论:如果你用 /login 登录了 Claude.ai 账号(Pro/Max 订阅),语音模式现在就可以用。输入 /voice 开启,按住 Space 说话。

完整数据流

语音模式全链路:麦克风 → 文字输入
Step 1 用户按住 Space(5 次快速按键后激活) useVoiceKeybindingHandler 检测到持续按键 前 2 次按键正常输入(防误触),第 5 次激活语音 Step 2 音频录制(WebSocket 连接前就开始) audio-capture-napi → 16kHz / 16-bit / mono PCM ~20ms 一个 chunk(320 bytes),缓存到 audioBuffer 同时计算 RMS 音量 → 16 格波形条可视化 Step 3 WebSocket 连接(与录音并行) wss://api.anthropic.com/api/ws/speech_to_text/voice_stream 参数: encoding=linear16 sample_rate=16000 language=en keyterms=TypeScript&keyterms=localhost&keyterms=项目名 认证: OAuth Bearer token(Claude.ai 账号) Step 4 流式 STT(Deepgram Nova 3) → 发送: 二进制 PCM 音频帧 + KeepAlive(每 8s) ← 接收: TranscriptText(实时预览) → TranscriptEndpoint(确认) 输入框实时显示正在识别的文字 Step 5 松开按键 → 结束 200ms 无按键 → 判定松开 → stopRecording() 发送 CloseStream → 等待最终结果(300ms-5s) 转写文字注入输入框光标位置

音频技术规格

录音参数
采样率:16,000 Hz
位深:16-bit signed (S16_LE)
声道:1 (mono)
编码:Raw PCM(无 WAV 头)
数据率:~32 KB/s
录音后端(优先级降序)
1. audio-capture-napi — 原生 CPAL 模块
2. arecord — Linux ALSA 工具
3. SoX rec — 终极兜底
Windows 只支持原生模块
原生模块(6 平台)
arm64-darwin / x64-darwin
arm64-linux / x64-linux
arm64-win32 / x64-win32
共 ~3MB(npm 包里的 .node 文件)

语音识别 & 语言支持

STT 引擎
引擎:Deepgram Nova 3
协议:WebSocket 流式传输
端点检测:300ms 静音 → 端点
语句结束:1000ms 静音 → 结束
不是 Anthropic 自研,是第三方 Deepgram 服务
通过 Anthropic 服务器代理转发
支持语言(20+)
en 英语 es 西班牙语 fr 法语 ja 日本語 de 德语 pt 葡萄牙语 it 意大利语 ko 한국어 hi हिंदी ru 俄语 id 印尼语 zh 中文 pl tr nl uk el cs da sv no
不支持的语言自动回退到英语,并提示用户

语言切换系统 — 三层分离架构

没有 i18n 框架。语言切换只是往 system prompt 里注入一句话。UI 永远英文。

语言系统三层分离
用户设置 /config → Language → "japanese"(自由输入任何字符串) │ ├──→ 模型回复语言 │ prompts.ts → getLanguageSection() │ 注入 system prompt: │ "Always respond in japanese. Technical terms and code identifiers remain in original form."→ 任何语言都行,甚至 "Pirate English" │ ├──→ 语音 STT 语言 │ useVoice.ts → normalizeLanguageForSTT("japanese") │ "japanese" → 查 LANGUAGE_NAME_TO_CODE 映射表 → "ja" │ → 传给 Deepgram voice_stream?language=ja │ 只支持 20 种,不支持的回退 "en" + 警告 │ └──→ UI 界面 不受影响,永远英文 没有翻译系统、没有 i18n 框架

STT 语言归一化(normalizeLanguageForSTT)

useVoice.ts — 40+ 语言名映射到 20 个 BCP-47 码
// 查找优先级: 1. 直接是 BCP-47 码? "ja" → "ja" ✓ 2. 在名称映射表里? "japanese" → "ja" ✓ "日本語" → "ja" ✓ "español" → "es" ✓ 3. 取基础子标签? "ja-JP" → split('-') → "ja" ✓ 4. 都不匹配? "粤语" → 回退 "en" + fellBackFrom: "粤语" // 名称映射表(部分): "english" → "en" "spanish"/"español"/"espanol" → "es" "french"/"français" → "fr" "japanese"/"日本語" → "ja" "german"/"deutsch" → "de" "korean"/"한국어" → "ko" "chinese"/"中文" → "zh" "hindi"/"हिंदी" → "hi" "portuguese"/"português" → "pt" "russian"/"русский" → "ru" ... 共 40+ 个映射条目

系统语言检测(有但不自动应用)

检测函数:getSystemLocaleLanguage()(intl.ts)
实现:Intl.DateTimeFormat().resolvedOptions().locale → 提取语言子标签
但只用于遥测统计——不自动设置模型语言或 STT 语言
结果:即使你的系统是中文 macOS,Claude Code 不会自动用中文回复。必须手动 /config → Language → 输入 "中文"

语言能力对比

语言来源 支持范围 机制
模型回复 settings.language 任何语言(模型能力决定) system prompt 注入一句话
语音 STT settings.language → 归一化 20 种(Deepgram 限制) WebSocket 参数 language=xx
UI 界面 硬编码 仅英文 无 i18n 框架
系统语言检测 Intl.DateTimeFormat 任何系统语言 仅遥测统计,不自动应用
缓存影响:语言段在 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 之后(动态区),不影响 global scope 缓存。切换语言只影响当前会话的 prompt 前缀。

领域词汇增强(voiceKeyterms.ts)

每次录音会发送最多 50 个关键词给 Deepgram 做识别增强:
全局固定词:MCP, symlink, grep, regex, localhost, codebase, TypeScript, JSON, OAuth, webhook, gRPC, dotfiles, subagent, worktree
项目名:当前项目目录名
Git 分支:拆分为词(feat/voice-keyterms → feat, voice, keyterms)
最近文件:文件名拆词(camelCase/kebab-case/snake_case 智能分割)

按住说话的交互设计

useVoiceKeybindingHandler — 按键状态机
默认按键: Space(可通过 /keybindings 自定义) 按下 Space 第 1 次 → 正常输入空格(防误触) 按下 Space 第 2 次 → 正常输入空格(<120ms 间隔 = 持续按住) 按下 Space 第 3-4 次 → 显示 "keep holding..." 按下 Space 第 5 次激活语音!删除泄漏的空格,开始录音 ... 持续按住 ... 松开 Space(>200ms 无按键) → 停止录音 → 等待最终结果 // 修饰键组合(Ctrl+X, Meta+K)第 1 次按下就激活(无歧义) // 防误触阈值: HOLD_THRESHOLD=5, WARMUP_THRESHOLD=2 // 快速按键间隔: RAPID_KEY_GAP_MS=120ms

错误恢复机制

静默丢包重放
问题:~1% 的会话命中有 bug 的服务端 pod,接收了音频但不返回转写
检测:有音频信号 + WebSocket 连着 + 1.5s 内无 TranscriptText
修复:缓存全部音频(~32KB/s × 最长 60s ≈ 2MB),新建 WebSocket 重放一次
Bug 编号: anthropics/anthropic#287008
早期错误重试
问题:服务器在返回任何转写前就断开
条件:非 4xx 错误 + 用户仍在按住
修复:250ms 退避后自动重连一次
不重试:4xx HTTP(认证拒绝)标记为 fatal

WebSocket 协议细节

voiceStreamSTT.ts — 通信协议
端点: wss://api.anthropic.com/api/ws/speech_to_text/voice_stream 发送 → [binary] Raw PCM 音频帧(16-bit LE, 16kHz, mono) [json] {"type":"KeepAlive"} 每 8 秒 [json] {"type":"CloseStream"} 录音结束时 接收 ← [json] {"type":"TranscriptText","data":"你说的话"} 实时预览 [json] {"type":"TranscriptEndpoint"} 一句话结束 [json] {"type":"TranscriptError","error_code":"..."} 错误 超时策略: CloseStream 后等最终结果: 300ms(快速路径) 无数据超时: 1.5s(静默丢包检测) 安全超时: 5.0s(兜底)

TTS(文字转语音)

原生模块有 startPlayback() / writePlaybackData() / stopPlayback() 播放接口——说明硬件层已准备好语音输出
但源码中没有 TTS 调用。当前语音模式是纯输入(Speech-to-Text),Claude 的回复不会朗读出来。
播放接口的存在暗示 TTS 可能是下一步计划。

源码文件清单

文件 功能
vendor/audio-capture-src/index.ts原生音频模块 TS 包装:录音、播放、麦克风权限
vendor/audio-capture/{platform}/6 平台预编译 .node 二进制(CPAL 跨平台音频库)
services/voice.ts录音后端检测、依赖检查、麦克风权限、静音过滤
services/voiceStreamSTT.tsWebSocket 流式 STT 连接、协议实现、丢包重放
services/voiceKeyterms.ts领域词汇收集(全局 + 项目名 + git 分支 + 文件名)
hooks/useVoice.ts核心 Hook:录音 + WebSocket + STT + 转写累积
hooks/useVoiceIntegration.tsx桥接 STT 到输入框:实时预览 + 文字插入
context/voice.tsxReact 语音状态上下文(idle/recording/processing)
commands/voice/voice.ts/voice 命令:开关 + 5 步前置检查
voice/voiceModeEnabled.ts资格判断:OAuth + 编译时 flag + GrowthBook 运行时 flag

18 KAIROS — 主动式后台 AI + 做梦机制

不是你问它才回答的助手,是一个自主运行的后台 AI。接收 tick 心跳自主决定做什么、通过 MCP 接收 Slack/Discord 消息、定期做"梦"整理记忆、GitHub webhook 触发自动响应。完整的自主 Agent 框架

核心架构:Tick 驱动的自主循环

KAIROS 自主工作循环
启动 --assistant 启动 or CLAUDE_CODE_PROACTIVE=1 │ ▼ 首次唤醒 → 问候用户,问"做什么?" │ ▼ 工作循环 ├─ 收到 <tick> 心跳 → 检查有没有事做 │ ├─ 有用户消息 → 处理 │ ├─ 有 channel 消息(Slack/Discord)→ 处理 │ ├─ 有 GitHub webhook → 处理 │ ├─ 有可做的事 → 主动做(读文件、跑命令、探索) │ └─ 没事做 → 必须立即 Sleep(不发状态消息,省 API 钱) │ ├─ Sleep 期间: │ ├─ 等待指定时间 │ ├─ 用户可随时打断 │ └─ prompt cache 5 分钟后过期 │ └─ 醒来后 → 回到 tick 检查

6 个子系统

KAIROS_DREAM — AI 做梦
后台记忆整理(autoDream.ts)
触发条件:24h + 5 个会话后
4 阶段:定位 → 收集 → 整理 → 剪枝
只读操作(ls/find/grep/cat)
KAIROS_CHANNELS — 消息接入
MCP 服务器推送消息到对话
协议:notifications/claude/channel
来源:Slack, Discord, SMS 等
包裹在 <channel source="..."> XML 里
KAIROS_PUSH_NOTIFICATION
移动推送通知
任务完成时推送
需要权限时推送
Claude 可主动推送
KAIROS_GITHUB_WEBHOOKS
SubscribePRTool:订阅 PR 事件
push/PR 变更自动触发
主动响应代码审查
KAIROS_BRIEF — 简洁模式
SendUserMessage 工具结构化输出
纯文本对用户隐藏
只显示工具调用结果
模式:确认(1行) → 工作 → 结果
Sleep Tool — 节奏控制
等待指定时间
轮询 hasCommandsInQueue()
有事就醒,没事继续睡
每次唤醒花一次 API 调用

源码深度:自主工作 System Prompt(逐字摘录)

prompts.ts:860-914 — Autonomous Work Section(原文)
## Pacing
Use Sleep to control wait time. Each wake-up costs an API call, prompt cache expires after 5 min.
If you have nothing to do on a tick, you MUST call Sleep.
Never respond with only "still waiting" — wastes a turn and burns tokens.
## First wake-up
Greet the user briefly. Ask what to work on. Do NOT start exploring the codebase unprompted.
## Subsequent wake-ups
Look for useful work. A good colleague faced with ambiguity doesn't just stop — they investigate, reduce risk.
Do not spam the user. If you already asked and they haven't responded, do not ask again.
## Terminal focus
Unfocused: User is away → lean into autonomous action, commit, push.
Focused: User is watching → be collaborative, surface choices, ask before big changes.

KAIROS_DREAM 做梦引擎(源码)

autoDream.ts — 5 层门控(从廉价到昂贵)
Gate 1 isGateOpen() KAIROS 激活? → skip(KAIROS 用 disk-skill dream) 远程模式? → skip auto-memory 启用? → 必须 true auto-dream 启用? → GrowthBook tengu_onyx_plover Gate 2 时间门控(cost: 1 次 stat) 读 .consolidate-lock 的 mtime 距上次 ≥ 24h? → 通过 Gate 3 扫描节流(cost: 0) 距上次扫描 < 10min? → skip(防止高频 stat) Gate 4 会话门控(cost: N 次 stat) listSessionsTouchedSince(lastAt) → stat 所有 *.jsonl 排除当前会话 ≥ 5 个新会话? → 通过 Gate 5 锁获取(cost: 读+写+验证) tryAcquireConsolidationLock() → 原子 CAS 写 PID 到锁文件 → 重读验证自己赢了 已有活进程? → return null(另一个 Claude Code 在做梦) 死进程/过期锁(>1h)? → 夺取

做梦 Prompt(4 阶段,逐字摘录)

consolidationPrompt.ts — Dream Prompt(原文)
# Dream: Memory Consolidation
You are performing a dream — a reflective pass over your memory files. Synthesize what you've learned recently into durable, well-organized memories.
Phase 1 — Orient
ls memory dir → read _index.md → skim existing topic files
Phase 2 — Gather recent signal
1. Daily logs (logs/YYYY/MM/YYYY-MM-DD.md)
2. Existing memories that drifted (facts contradicting codebase)
3. Transcript search — grep narrowly, don't read whole files
grep -rn "<narrow term>" transcripts/ --include="*.jsonl" | tail -50
Phase 3 — Consolidate
Merge new signal into existing files (not create duplicates)
Convert relative dates to absolute dates
Delete contradicted facts at the source
Phase 4 — Prune and index
Update _index.md: under max lines, each entry <150 chars
Remove stale pointers, demote verbose entries
Tool constraints: Bash 限制为只读命令(ls/find/grep/cat/stat/wc/head/tail)
但 Write/Edit 文件工具不受限 — 用于写入和更新 memory/*.md 记忆文件

Tick 机制实现(源码)

cli/print.ts — Tick 调度器
// 每轮执行完后检查: if (proactiveActive && !paused && queue.empty) { // 队列空了 + 没人等 → 安排下一个 tick setTimeout(() => { // 构造 tick 消息 const tickContent = `<tick>${new Date().toLocaleTimeString()}</tick>` // 入队,优先级 = 'later'(低于用户消息 'next') enqueue({ mode: 'prompt', value: tickContent, priority: 'later', isMeta: true, }) }, 0) // setImmediate 语义,不阻塞 } // 优先级排序: 'now'(0) > 'next'(1) > 'later'(2) // 用户消息 = 'next',tick = 'later' // → 用户打字时 tick 永远排在后面,不会抢占

消息队列(messageQueueManager.ts)

now
优先级 0(最高)
中断级消息
next
优先级 1
用户输入(默认)
later
优先级 2(最低)
tick / 任务通知
同优先级内 FIFO。dequeue() 总是取最高优先级的第一个。

做梦引擎到底做什么(人话版)

Claude Code 趁你不用的时候,自己回顾最近几天的对话,把有价值的信息整理成笔记。下次对话直接读笔记,不用你重复说项目背景

没有做梦引擎
会话 1: "我们用 React + Prisma, 部署 Coolify"
会话 2: Claude 完全不知道(新对话=失忆)
会话 3: 你又得重复一遍项目背景
每次都从零开始
有做梦引擎
会话 1-5: 正常工作,对话记录存 JSONL
24h + 5 会话后: 做梦自动触发
→ 提取 "React + Prisma + PostgreSQL" 写入笔记
会话 6: 自动读笔记,直接知道项目背景
越用越懂你

做梦的实际产出

做梦前 vs 做梦后
做梦前: ~/.claude/projects/-Users-kangwan-Projects-foo/memory/ (空) 做梦后: ~/.claude/projects/-Users-kangwan-Projects-foo/memory/ _index.md ← 索引(每条 <150 字符) project_stack.md ← "React + Prisma + PostgreSQL, 部署 Coolify" known_issues.md ← "连接池耗尽问题,已通过 pgbouncer 解决" user_preferences.md ← "喜欢用中文回复,代码风格偏简洁" 下次新对话启动 → 自动读这些文件 → 不用你重复说了
为什么叫"做梦":来自认知科学——人在睡眠时大脑整理白天的记忆(记忆巩固)。KAIROS_DREAM 做的事本质一样:醒着(对话中)记录原始会话,睡着(对话之间)提取精华、整理归档。做梦时 Bash 只读(ls/grep/cat,不能 npm/git/rm),但 Write/Edit 可用(写记忆文件)。不会改你的源代码,只会在 memory/ 目录下增减 .md 文件。

多项目记忆隔离

记忆按项目目录隔离。每个项目有独立的记忆目录、独立的做梦锁、独立的会话计数。项目 A 的对话不会跑到项目 B 的记忆里

记忆目录结构 — 项目级隔离 + 全局共享
~/.claude/ ├── memory/ ← 全局记忆(所有项目共享) │ ├── MEMORY.md 你的工作偏好、通用规则 │ ├── feedback_*.md 行为反馈 │ └── user_*.md 个人信息 │ ├── projects/ │ ├── -Users-kangwan-Projects-business-20260226-法考/ │ │ └── memory/ ← 法考项目的记忆 │ │ ├── _index.md │ │ └── project.md "68,759 题,Coolify 部署" │ │ │ ├── -Users-kangwan-Projects-code-20260319-gitnexus/ │ │ └── memory/ ← GitNexus 的记忆 │ │ └── project.md "代码图谱,端口 xxxx" │ │ │ └── -Users-kangwan-Projects-research-claude-code-ui-showcase/ │ └── memory/ ← 当前项目的记忆 │ └── project.md "源码解析,21 板块" 路径规则: ~/.claude/projects/ + 工作目录路径(/ 替换为 -) + /memory/

隔离细节

维度 项目级 memory/ 全局 ~/.claude/memory/
存什么 项目技术栈、架构、已知问题、进度 个人偏好、通用工作规则、凭证位置
谁能读 只有在该项目目录下启动的 Claude Code 所有项目都能读
做梦范围 只读该项目的 JSONL 会话,只写该项目的记忆 做梦不直接写全局记忆
项目级 .consolidate-lock(项目 A 做梦不影响 B) 无锁(手动维护)
跨项目 完全隔离——法考项目的记忆不会出现在 GitNexus 里 共享——"不要猜端口" 所有项目都遵守
多项目同时做梦:两个项目可以同时触发做梦引擎(锁是项目级的,互不阻塞)。你白天在 3 个项目间切换,晚上 3 个项目各自独立整理自己的记忆。

触发时机和用户提示

什么时候做梦 + 你能看到什么
触发条件(两个都满足) 距上次做梦 ≥ 24 小时 ← GrowthBook 可远程调整 minHours 期间有 ≥ 5 个新会话 ← GrowthBook 可远程调整 minSessions 两个都不够 → 不触发。一天用 2 次不够,用 10 次但才过 12h 也不够 触发时机 不是定时任务,不是后台守护进程 是每轮对话结束后(stopHooks 回调)检查一次门控 全部通过 → fork 一个后台 Agent 做梦 不通过 → 什么都不发生(检查 cost <1ms) 你能看到的提示 1. 底栏小药丸:显示 "dreaming" 任务 Shift+Down 查看详情(阶段/审查会话数/触碰文件) 2. 完成后:对话里出现系统消息 "Improved memory: project_stack.md, known_issues.md" verb 是 'Improved' 不是 'Saved'(暗示更新,不是新建) 3. 不打断你:fork 独立 Agent 后台运行,你继续正常对话

没有"计时器"——是文件时间戳比较

做梦判断逻辑(每轮对话结束后执行一次,<1ms)
没有倒计时器、没有 cron、没有定时任务。 每轮对话结束后"顺便"算一下: 1. 读 .consolidate-lock 的 mtime → 上次做梦是什么时候 2. Date.now() - mtime ≥ 24h3. sessions/ 里 mtime > 上次做梦的 JSONL ≥ 5 个4. 都满足 → 做梦 示例: 上次做梦 = 周一 10:00(锁文件 mtime) 周一 10:30-周二 10:00 用了 4 次 → 4 个 JSONL 周二 10:01 开第 5 次,聊一轮结束 → now - 周一10:00 = 24h01min ≥ 24h ✓ 新会话 = 4 个(第 5 个是当前的不算)→ < 5 ✗ 不触发 周二 11:00 开第 6 次,聊一轮结束 → 新会话 = 5 个 → ≥ 5 ✓ 触发做梦!
误解:有 24h 倒计时 实际没有计时器,每次算 now-mtime
误解:会话都停了才计时 对话中每轮结束都检查
误解:24h 后自动触发 24h 后的下一次对话结束后才检查
实际:搭便车设计 寄生在 stopHooks 回调里,无独立执行时机

关键澄清:关了就不跑

Claude Code 不是后台服务。关闭终端 = 进程退出 = 什么都不跑。做梦不会在你关了之后偷偷执行。
做梦只在下次你打开 Claude Code 的某轮对话结束后顺便检查门控 → 满足条件 → 后台 fork。不开就永远不做梦。

终端版 vs VS Code 版的做梦差异

场景 终端版 VS Code 扩展版
进程生命周期 Ctrl+C 退出 = 进程死 关面板 ≠ 退出(VS Code 开着就活着)
做梦检查时机 只在下次启动后的对话里 每轮对话结束后都检查(进程一直活着)
会话计数方式 每次 Ctrl+C 再进 = 新会话 +1 每次 /clear 或重启扩展 = +1
不关不算新会话
一天不关 VS Code 只算 1 个会话(不满 5,不触发)
除非中间 /clear 了 5 次
跨客户端计数 终端和 VS Code 的会话共同计数——看的是同一个 sessions/ 目录下的 JSONL 文件数
跨客户端示例:终端用了 3 次 + VS Code 开关了 2 次 = 5 个会话 = 达到阈值。下次在任一客户端对话结束后触发做梦。

Channel 协议(Slack/Discord 消息接入)

channelNotification.ts — 7 层门控 + 消息格式
// MCP 服务器推送消息: { method: "notifications/claude/channel", params: { content: "Alice: can you review PR #42?", meta: { chat_id: "C123", user: "alice", thread_ts: "1234.56" } } } // 7 层门控(全部通过才接收): 1. 服务器声明 experimental['claude/channel'] 能力 2. tengu_harbor flag 开启 3. 有 OAuth token(API Key 用户不行) 4. 组织策略允许(Team/Enterprise 需 channelsEnabled:true) 5. 在 --channels 会话白名单里 6. 插件市场验证(防恶意插件) 7. 在 allowedChannelPlugins 批准列表里 // 包裹后注入对话: <channel source="slack" chat_id="C123" user="alice"> Alice: can you review PR #42? </channel>

Channel 到底是什么(人话版)

Channel 不是什么
Claude 用你的 Slack 账号替你发消息
多设备同步 / 远程访问工作空间
手机上看 Claude Code 对话
云端工作空间
Channel 是什么
外部消息推到本机 Claude Code 进程
单向收件箱(外面进来,本机处理)
回复通过 MCP 插件 bot 身份(不是你的账号)
进程关了就收不到(不是后台服务)
Channel 消息流
你的电脑(本机进程,必须在运行) ┌────────────────────────────┐ Slack 消息 ──→ │ │ Discord 通知 ──→ │ Claude Code 进程 │ → 读代码、跑测试 GitHub webhook ──→ │ (KAIROS 主动模式运行中) │ → 分析结果 │ │ └────────────┬───────────────┘ │ ↓ 通过 MCP 插件的 bot 身份回复 (不是你的 Slack 账号)

容易混淆的三个功能对比

功能 消息方向 你需要 实际作用
Channel 外部 → 本机 本机进程在运行 Slack/Discord 消息推到 Claude 处理
ULTRAPLAN/CCR 本机 ↔ 云端 浏览器(任何设备) 代码在云端运行,浏览器审批方案
Push Notification 本机 → 手机 手机 任务完成/需确认时推送提醒到手机
当前状态:Channel 对普通用户不可用。需要 KAIROS 主动模式 + tengu_harbor flag + 配套 MCP 插件(没有公开的 Slack/Discord MCP)+ 企业管理员配置。它是为 KAIROS 自主 Agent 设计的——一个持续运行的 AI 才需要收件箱。

19 ULTRAPLAN — 云端远程执行系统

把代码推到 Anthropic 云端容器执行,用 Opus 做多 Agent 规划。浏览器审批 → 本地执行 or 远程继续。Git Bundle 代码同步 + SSE 事件流 + JWT Worker 认证

完整执行链路

ULTRAPLAN 工作流
1 用户执行 /ultraplan <需求描述> 2 代码同步到云端 Git Bundle(54%): git bundle create → 上传 Files API (≤100MB) GitHub 直连(46%): 远程容器直接 git clone(需 GitHub App) 3 云端创建会话 POST /v1/code/sessions → 获取 session_id POST /v1/code/sessions/{id}/bridge → 获取 Worker JWT 4 远程 Opus 多 Agent 规划(≤30 分钟) SSE 事件流 ← {api_base_url}/worker/events/stream 心跳 → /worker/heartbeat(每 20s) 5 浏览器审批(PlanModal) 远程调用 ExitPlanMode → 浏览器弹出方案 用户三选一: ✓ 批准 → 远程继续执行 ✎ 编辑后批准 → 远程执行修改版 ✗ 拒绝 + Teleport → 方案传回本地执行 6 完成后归档 POST /v1/sessions/{id}/archive

CCR v2 通信协议

入站(远程 → 本地)
协议:SSE (Server-Sent Events)
序列号重放:断线重连续接
内容:assistant 回复、工具调用、权限请求
出站(本地 → 远程)
协议:HTTP POST 批量
批次上限:100 事件/POST
内容:用户消息、权限响应、送达确认

核心理解:云端只规划,代码不回传

你可能以为的
云端完成全部任务
代码改动自动同步回本地
本地直接就有改好的代码
实际是
云端用 Opus 分析你的代码库
生成详细修改方案文本
你审批后:本地执行方案 or 云端继续

数据流方向(单向上传,方案回传)

本地 → 云端(上传) 云端 → 本地(回传)
完整 git 仓库(bundle) 只有方案文本
未 commit 的改动(stash) 不同步修改后的文件
OAuth token 不传回 git commit
自定义 system prompt 不下载任何代码

代码上传:Git Bundle 三级降级(源码)

gitBundle.ts — 三级降级策略
Tier 1: --all(默认,54% 的会话) git stash create → refs/seed/stash(捕获未 commit 的改动) git bundle create --all refs/seed/stash 包含:全量历史 + 所有分支 + tags + WIP 上限:100MB(tengu_ccr_bundle_max_bytes) Tier 2: HEAD only(如果 --all 超限) git bundle create HEAD 包含:当前分支历史 + WIP stash 丢弃:旁支分支、tags Tier 3: squashed-root(最后手段) git commit-tree <tree> → 创建无父 commit git bundle create refs/seed/root 包含:仅当前文件快照,零历史 如果有 WIP:用 stash 的 tree(含未 commit 改动) 注意:未 track 的文件(新建但没 git add 的)不会被带上

浏览器审批流程(源码)

ccrSession.ts — ExitPlanModeScanner 审批状态机
云端 Claude 分析完成 → 调用 ExitPlanMode 工具浏览器弹出 PlanModal(claude.ai 上)用户点"批准" → tool_result is_error=false → 提取 "## Approved Plan:" 后的文本 → poll 返回 kind='approved' → 云端继续执行方案 用户点"编辑后批准" → "## Approved Plan (edited by user):" 标记 → 云端执行用户修改后的方案 用户点"传回本地" → tool_result is_error=true + __ULTRAPLAN_TELEPORT_LOCAL__ 哨兵 → poll 返回 kind='teleport' → 本地终端弹出选择框: "Execute here" → 归档远程,本地执行方案 "Continue on web" → 远程继续 用户点"拒绝" → tool_result is_error=true(无哨兵) → 云端 Claude 继续迭代新方案 → 再次弹出 PlanModal

云端改的代码去哪了

出路 1: 推到 GitHub
如果装了 GitHub App(app.claude)
云端 Claude 可以 git push 到你的仓库
→ 你本地 git pull 拉回来
通过 session.outcomes.git_info.branches 可见
出路 2: 随容器销毁
如果没推到 GitHub
改动留在云端容器里
30 分钟超时 or 手动归档后
→ 容器销毁,代码消失

费用

谁付钱:你的账号。云端用你的 OAuth token 调 Opus 4.6,token 用量计入你的配额。
模型:Opus 4.6(tengu_ultraplan_model 控制,可远程切换)
时间上限:30 分钟。超时自动归档。
没有额外收费:容器计算包含在服务里,只按推理 token 计费。
当前状态:ULTRAPLAN 受 feature flag 门控(ULTRAPLAN 编译时 + tengu_ccr_bridge/tengu_bridge_repl_v2 运行时)。需要 Claude.ai OAuth + 远程环境可用。公开版 /ultraplan 命令可能未注册。

20 用量限制机制 — 云端拒绝 + 本地展示

不是本地关闭你的程序,是 API 服务器直接返回 429 拒绝。本地只是展示"用量已满"的 UI。即使魔改本地代码去掉所有限制提示,API 依然会拒绝你

两层限制机制

用量限制流程
配额内: 你的请求 → api.anthropic.com → 200 OK + headers 带用量信息 ↓ Claude Code 本地解析 headers 更新状态栏进度条: ████████████ 60% 配额满: 你的请求 → api.anthropic.com → 429 Too Many Requests ↓ Claude Code 本地收到拒绝 status = 'rejected' 显示提示: "用量已满,X小时后重置" 真正的墙在云端,不在你的电脑上
第一层:云端 API 拒绝(真正的墙)
Anthropic 服务器看你的 API Key / OAuth Token
发现配额用完 → 直接返回 HTTP 429
你的请求根本没被处理
你改不了、绕不开、骗不了
第二层:本地 UI 展示(提示层)
解析 429 响应的 headers
更新本地状态 currentLimits.status = 'rejected'
显示"用量已满"消息 + 重置倒计时
建议升级套餐 / 开启超额使用
这层可以被魔改去掉,但 API 照样拒绝你

每次 API 响应都带用量信息(headers)

API Response Headers — 用量追踪
// 每次 API 响应的 headers 里都有这些字段 anthropic-ratelimit-unified-5h-utilization: 0.85 ← 5小时配额用了85% anthropic-ratelimit-unified-5h-reset: 1743580800 ← 重置的 Unix 时间戳 anthropic-ratelimit-unified-7d-utilization: 0.42 ← 7天配额用了42% anthropic-ratelimit-unified-7d-reset: 1744099200 ← 7天重置时间 // 接近上限时额外的警告 header anthropic-ratelimit-unified-5h-surpassed-threshold: 0.9 ← 超过90%阈值

Claude Code 本地解析这些 headers,在状态栏画进度条。用量数据是云端告诉本地的,不是本地自己计算的

配额类型(源码 claudeAiLimits.ts)

类型 显示名 窗口 说明
five_hoursession limit5 小时短期速率限制
seven_dayweekly limit7 天周配额
seven_day_opusOpus limit7 天Opus 模型专属周配额
seven_day_sonnetSonnet limit7 天Sonnet 模型专属周配额
overageextra usage limit超额使用(另外付费)

本地预警机制(提前告诉你"用太快了")

claudeAiLimits.ts — 预警阈值
// 5小时窗口:用了90%但时间才过72% → 你用太快了 five_hour: { utilization: 0.9, timePct: 0.72 } // 7天窗口:三级预警 seven_day: [ { utilization: 0.75, timePct: 0.60 }, ← 用了75%但时间才过60% { utilization: 0.50, timePct: 0.35 }, ← 用了50%但时间才过35% { utilization: 0.25, timePct: 0.15 }, ← 用了25%但时间才过15% ] // 这些都是本地计算的提前预警 // 不阻断你的使用,只是弹出通知提醒

启动时的配额预检

Claude Code 启动时会主动发一个最小请求试探配额:
makeTestQuery() → { role: 'user', content: 'quota', max_tokens: 1 }
只花 1 个 output token,纯粹为了拿 headers 里的用量数据。这样在你开始工作前就能显示当前配额状态

超额使用(Overage)的 12 种拒绝原因

配额用完后可以选择超额使用(额外付费)。但超额也可能被拒绝,源码列出了 12 种原因:

overage_not_provisioned — 未开通 org_level_disabled — 组织禁用 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 — 未配置限制 org_level_disabled_until — 临时禁用
结论
限制在云端执行:Anthropic 的 API 服务器通过 429 状态码拒绝你的请求
本地只做展示:Claude Code 解析 429 的 headers,在 UI 上显示用量信息和重置倒计时
本地有预警:在你还没到限额时提前告诉你"用太快了",但不阻断
启动时预检:发一个 1-token 的测试请求探测当前配额状态
改本地代码没用:你可以去掉所有限制 UI、删掉 checkQuotaStatus、忽略 currentLimits.status——API 照样返回 429

21 状态栏

底部信息栏,通过 Hook 可自定义内容

opus-4-6 │ default │ ~/Projects │ 12.3k↑ 4.2k↓ │ $0.052
速率限制进度条:
60%
claude-code-source.zip 源码提取 · 1,914 files · 58 MB
React Ink TUI · TypeScript · Bun Runtime