add claude code analysis 20260404

This commit is contained in:
Chauncygu
2026-04-04 09:43:49 -07:00
parent 23ae8ec64b
commit 7f33e40f22
14 changed files with 5033 additions and 1 deletions

Submodule docs/cc_analysis/analysis_20260401 deleted from 185359d451

View File

@@ -0,0 +1,38 @@
# Claude Code 默认 Effort Level 设置为 Max
## 问题
`/effort max` 命令只对当前会话生效,重启 Claude Code 后会恢复为默认的 `medium`
## 解决方案
`~/.zshrc` 中添加环境变量:
```bash
export CLAUDE_CODE_EFFORT_LEVEL=max
```
新开终端窗口后自动生效。
## 为什么不能用 settings.json
Claude Code 的 `~/.claude/settings.json` 支持 `effortLevel` 字段,但只接受 `low``medium``high` 三个值。`max` 是 Opus 4.6 专属的深度推理模式,只能通过环境变量持久化。
## Effort Level 一览
| 级别 | 说明 |
|------|------|
| `low` | 快速响应,适合简单问答 |
| `medium` | 默认级别 |
| `high` | 更深入的推理 |
| `max` | 最深度推理,无 token 限制(仅 Opus 4.6 |
## 其他设置方式
- **启动参数**(单次):`claude --effort max`
- **会话内**(单次):`/effort max`
- **环境变量**(永久):`export CLAUDE_CODE_EFFORT_LEVEL=max` ← 推荐
---
*记录于 2026-03-30*

View File

@@ -0,0 +1,150 @@
# Claude Code 自动回滚机制解析与版本切换指南
> 日期2026-03-31
## 背景
Claude Code 从 v2.1.88 自动回滚到 v2.1.87,本文记录了完整的排查过程和解决方案。
## 回滚原因
**Anthropic 在 npm 做了服务端回滚**,不是本地触发的。
### npm Registry 状态
| dist-tag | 版本 |
|----------|------|
| `stable` | 2.1.81 |
| `latest` | 2.1.87 |
| `next` | 2.1.89 |
- v2.1.88 **已被完全从 npm 删除**registry 中不存在)
- `latest` tag 从 2.1.88 退回到 2.1.87
### 时间线
| 时间 | 事件 |
|------|------|
| Mar 28 19:25 | 自动更新下载 v2.1.87 |
| Mar 30 18:31 | 自动更新下载 v2.1.88 |
| Mar 30 ~ Mar 31 | Anthropic 从 npm 撤掉 v2.1.88,将 `latest` tag 退回 v2.1.87 |
| Mar 31 05:59 | 自动更新器检查 npm registry发现 `latest` 是 2.1.87,将 symlink 切回 |
## 自动更新机制
### 核心架构
```
~/.local/bin/claude → symlink → ~/.local/share/claude/versions/{version}
```
- 包名:`@anthropic-ai/claude-code`
- 版本二进制存储:`~/.local/share/claude/versions/`
- 入口 symlink`~/.local/bin/claude`
### AutoUpdater 工作流程
1. 每次 Claude Code 启动时AutoUpdater 检查 npm registry 的 `latest` dist-tag
2. 如果本地版本与 `latest` 不匹配,下载目标版本二进制到 `versions/` 目录
3. 更新 symlink 指向新版本
4. 逻辑是**跟随 `latest` tag**,不是单调递增 — 所以 Anthropic 退 tag 就等于回滚
### 关键发现
- 二进制是 Bun 编译的 Mach-O arm64 可执行文件
- 内部包含 `auto_updater_disabled``AutoUpdater``autoUpdaterStatus` 等标识
- 启动遥测会上报 `auto_updater_disabled` 状态
- 并发更新有互斥锁保护("Another instance is currently performing an update"
## 禁用自动更新的正确方式
通过逆向二进制中的 `h1H()` / `isAutoUpdaterDisabled` 函数,确认自动更新器的检查逻辑:
```javascript
// 反编译后的禁用检查逻辑(简化)
function getAutoUpdaterDisabledReason() {
if (process.env.DISABLE_AUTOUPDATER) return { type: "env" };
if (config.autoUpdates === false) return { type: "config" };
return null; // 未禁用,自动更新正常运行
}
```
### 踩坑记录
`autoUpdaterDisabled: true` 是**错误的 key**,写了不生效,自动更新器仍会在启动时抢先将 symlink 切回 `latest` 指向的版本。
### 方法 A环境变量推荐最可靠
```bash
# 加到 ~/.zshrc每次 shell 启动自动生效
echo 'export DISABLE_AUTOUPDATER=1' >> ~/.zshrc
source ~/.zshrc
```
### 方法 Bsettings.json
编辑 `~/.claude/settings.json`,注意 key 是 `autoUpdates`
```json
"autoUpdates": false
```
> 注意:对于 native 安装方式,如果同时设置了 `autoUpdatesProtectedForNative: true`,则 `autoUpdates: false` 会被覆盖,此时只能用环境变量方式。
## 解决方案:切回 v2.1.88
### 步骤 1禁用自动更新
```bash
# 确保环境变量已生效
export DISABLE_AUTOUPDATER=1
```
### 步骤 2切换 symlink
```bash
ln -sf ~/.local/share/claude/versions/2.1.88 ~/.local/bin/claude
```
### 步骤 3验证
```bash
claude --version
# 输出2.1.88 (Claude Code)
```
### 步骤 4使用
```bash
claude --dangerously-skip-permissions
```
## 恢复自动更新
```bash
# 1. 从 ~/.zshrc 删掉 export DISABLE_AUTOUPDATER=1
# 2. 如果用了方法 B从 settings.json 删掉 "autoUpdates": false
# 3. 重启终端,自动更新器会在下次启动时恢复工作
```
## 其他版本切换方式
```bash
# 切到 next channel (v2.1.89)
claude update --channel next
# 切到 stable channel (v2.1.81)
claude update --channel stable
# 查看本地已有的版本
ls ~/.local/share/claude/versions/
# 手动切换到任意本地版本
ln -sf ~/.local/share/claude/versions/<版本号> ~/.local/bin/claude
```
## 注意事项
- v2.1.88 被 Anthropic 从 npm 删除,可能存在已知问题
- 禁用自动更新后不会收到安全修复,需定期手动检查
- 本地残留的 v2.1.88 二进制不会被自动清理

View File

@@ -0,0 +1,297 @@
# Claude Code 上下文管理算法学习笔记
> 来源Claude Code 源码快照2026-03-31
> 核心文件:`src/query.ts`, `src/services/compact/`, `src/utils/toolResultStorage.ts`
---
## 架构总览7 层递进式防御
每轮 query loop 按以下顺序执行,从便宜到昂贵,每一层减轻压力,可能阻止下一层触发:
```
1. Tool Result Budget ← 单轮预算(同步,零 API 调用)
2. Snip Compact ← 历史裁剪(零 API 调用)
3. Microcompact ← 工具结果精细清理(零或极低 API 成本)
4. Context Collapse ← 增量投影摘要(零 API 调用)
5. Auto-Compact ← LLM 全量摘要(昂贵)
6. Blocking Limit ← 硬停(所有自动措施关闭时)
7. Reactive Compact ← 413 错误后紧急摘要(最后手段)
```
互斥门控防止昂贵操作竞争Context Collapse 启用时抑制 Auto-CompactReactive Compact 实验模式抑制主动 Auto-Compact。
---
## 第 1 层Tool Result Budget — 单轮聚合预算
**问题**N 个并行工具可能在一轮中集体产生超大上下文。
**三分区算法**
| 分区 | 含义 | 处理 |
|------|------|------|
| mustReapply | 之前已替换过的结果 | 直接重用缓存的替换字符串(零 I/O字节级一致 |
| frozen | 之前见过但没替换 | 永不替换(保护 prompt cache |
| fresh | 新结果 | 参与预算分配 |
**阈值**
- 总预算200K 字符/消息(远程可配置)
- 单工具上限50K 字符(默认)
- 超出时:保留前 ~2KB 预览 + 持久化到磁盘,模型可用 Read 按需读取
**核心洞察**frozen 分区 — 宁可浪费空间也不破坏缓存命中率。**Prompt cache 稳定性优先于空间效率**。
---
## 第 2 层Snip Compact — 历史裁剪
**特点**:零 API 调用,直接删除旧消息。
**协调机制**snipTokensFreed 传递给下游层,因为 tokenCountWithEstimation 读的是上一轮 API 返回的 input_tokens反映 snip 前的数值),需要手动减去 snip 释放的量。
**双视图**REPL 保留被 snip 的消息用于 UI 回滚,但投影层在发送 API 前过滤掉它们。
---
## 第 3 层Microcompact — 三种精细清理路径
### 3a. 基于时间的清理
```
触发条件:距上次助手消息 > 60 分钟(= 服务端 cache TTL
逻辑cache 已过期 → 全量前缀会被重写 → 趁机清理旧工具结果
行为:替换为 "[Old tool result content cleared]"
保留:最近 5 个结果
```
**洞察**:利用缓存 TTL 过期的"免费窗口"搭车做清理。
### 3b. 缓存编辑Cache Editing— 最精妙的设计
```
触发条件:可压缩工具结果数超过阈值
关键创新:不修改本地消息!使用 API 的 cache_edits / cache_reference 机制
效果:服务端缓存被精确编辑,本地保持不变
确认:用 API 返回的 cache_deleted_input_tokens非客户端估算
范围仅主线程仅特定工具FileRead, Bash, Grep, Glob 等)
```
**洞察**:读写分离 — 本地消息不变(保证重放一致性),服务端缓存被精确编辑(节省空间)。
### 3c. API 原生上下文管理
```
策略clear_tool_uses + clear_thinking
触发:输入超 180K tokens
目标:保留最后 40K tokens
思考块cache 冷(>1h时仅保留最后一轮思考
```
---
## 第 4 层Context Collapse — 增量投影式摘要CQRS 思想)
**核心思想**:不破坏性替换消息,而是维护一个 commit log每轮查询时 project 出压缩视图。
**阈值**
- 90% 上下文窗口 → 开始提交 collapse
- 95% 上下文窗口 → 阻塞新 spawn
- ~93%auto-compact 位置)→ 被抑制避免竞争
**关键 API**
- `applyCollapsesIfNeeded()` — 投影压缩视图 + 可选提交新 collapse
- `recoverFromOverflow()` — 413 时排空所有暂存 collapse第一道防线
- `projectView()` — 每轮重放提交日志
**设计亮点**
- Collapse 摘要存在 commit store 中,而非 REPL 消息数组 → 跨轮持久化
- REPL 保留完整历史UI 回滚API 调用看到投影视图(节省 tokens
- 会话恢复时从 commits + snapshot 重建
---
## 第 5 层Auto-Compact — 带熔断的 LLM 摘要
**触发**`tokenCount > contextWindow - 13,000`
### 摘要 prompt 结构
```xml
<analysis>(内部草稿,生成后丢弃)</analysis>
<summary>
1. Primary Request / Intent
2. Key Technical Concepts
3. Files / Code
4. Errors / Fixes
5. Problem Solving
6. All User Messages用户说过的每句话都保留
7. Pending Tasks
8. Current Work
9. Optional Next Step
</summary>
```
### Prompt Cache 共享优化
摘要子 agent 复用主对话的缓存前缀(通过 runForkedAgent
没有此优化cache miss 率 98%,浪费全局 ~38B tok/天的 cache_creation。
### PTLPrompt-Too-Long重试
```
最多重试 3 次:
→ 按 API 轮次分组groupMessagesByApiRound
→ 丢弃最老的组以覆盖 token 缺口
→ 无法精确计算时 fallback 丢弃 20% 的组
图片:所有图片/文档块替换为 [image]/[document] 标记后再发送
```
### 熔断器
```
MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3
触发后:本会话剩余时间停止尝试
背景数据:~1,279 个会话曾出现 50+ 次连续失败(最高 3,272 次),
每天浪费 ~250K API 调用
```
### Post-Compact 恢复清单
| 恢复项 | 预算 |
|--------|------|
| 最近读取的文件 | 最多 5 个,每个 5K tokens总计 50K |
| Skill 附件 | 每个 5K总计 25K |
| Plan 状态 | 完整恢复 |
| Deferred tools | 完整恢复 |
| Agent 列表 | 完整恢复 |
| MCP 指令 | 完整恢复 |
### Session Memory Compact替代路径
在 auto-compact 中**优先尝试**,失败回退到完整 LLM 摘要:
```
用预提取的 session memory 替代 LLM 摘要
保留范围:从最后摘要消息向后扩展
满足 minTokens(10K) 且 minMessages(5)
不超过 maxTokens(40K)
不变量tool_use / tool_result 对永不拆分
```
---
## 第 6-7 层Reactive Compact + 错误恢复级联
### 5 层错误恢复(从便宜到昂贵)
```
API 返回 prompt_too_long 或媒体尺寸被拒
|
v
Layer 1: Context Collapse drain排空所有暂存 collapse最便宜
| 失败
v
Layer 2: Reactive Compact完整 LLM 摘要)
| 失败
v
Layer 3: Max Output 升级8K → 64K tokens
| 失败
v
Layer 4: Multi-turn Recovery注入 nudge 消息,最多 3 次)
| 失败
v
Layer 5: Model Fallback切换到备用模型
```
### 错误扣留Error Withholding模式
```
流式传输期间,可恢复错误不 yield 给调用者
→ 推入 assistantMessages 供恢复检查
→ 防止 SDK 消费者终止会话
→ 所有恢复手段都失败后才暴露给用户
```
防循环守卫:`hasAttemptedReactiveCompact` 防止同一轮无限重试。
---
## Token Budget Continuation — 输出预算跟踪(附加机制)
```
用途:长任务自动续跑(不是上下文管理,是输出预算控制)
完成阈值:输出 < 预算 90% → 继续
递减检测:连续 3+ 次续跑 且 最近两次增量 < 500 tokens → 停止
每次续跑注入 nudge 消息告知进度百分比
```
---
## 值得借鉴的设计原则
### 1. Prompt Cache 稳定性高于空间效率
- frozen 分区:进入 cache 的内容永不修改
- 字节级替换一致性mustReapply 重用完全相同的替换字符串
- Tool pool 排序稳定:`assembleToolPool()` 排序防止 MCP 变化破坏缓存
- 缓存编辑不改本地消息:读写分离保证重放一致性
### 2. 分层防御,从便宜到昂贵
7 层递进每层有可能阻止下一层触发。互斥门控防止昂贵操作竞争collapse 抑制 autocompact。snipTokensFreed 显式传递确保下游层看到真实 token 数。
### 3. 错误扣留 + 延迟恢复
流式传输中不立即暴露可恢复错误。给恢复机制留出空间后再决定是否暴露。这个模式可以推广到任何有多层 fallback 的系统。
### 4. CQRS 式双视图
REPL 保留完整历史UI 回滚 + 会话恢复API 看到投影视图(节省 tokens。Context Collapse 的 commit log 和 projectView 就是这个思想的实现。
### 5. 熔断器模式
连续失败 3 次后停止重试。用真实数据驱动1,279 个会话的失控循环 → 250K API 调用/天的浪费。
### 6. 利用缓存 TTL 做"免费清理"
cache 自然过期60min时搭车执行清理。既然前缀要重建就把脏活一起干了。
### 7. Post-Compact 不从零开始
摘要后精心恢复关键上下文最近文件、plan、skills。有明确的 token 预算分配,不是全量恢复也不是什么都不恢复。
### 8. 不变量保护无处不在
- tool_use / tool_result 对永不拆分
- Compact 边界消息记录 pre-compact 状态供恢复重链
- Partial compact 的 "up_to" 变体清除旧边界防止级联剪枝 bug
- 子 agent compact 不重置主线程的模块级状态
---
## Query Loop 状态机一览
```typescript
State = {
messages,
toolUseContext,
autoCompactTracking, // 熔断计数
maxOutputTokensRecoveryCount, // 输出恢复计数
hasAttemptedReactiveCompact, // 防循环守卫
maxOutputTokensOverride, // 8K → 64K 升级
pendingToolUseSummary,
stopHookActive,
turnCount,
transition // 状态转移原因
}
transition 类型:
collapse_drain_retry
reactive_compact_retry
max_output_tokens_escalate
max_output_tokens_recovery
stop_hook_blocking
token_budget_continuation
```
每轮不是递归而是 `while(true)` + 显式状态转移,避免长会话栈溢出。

View File

@@ -0,0 +1,710 @@
# Claude Code 值得借鉴的算法与设计模式
> 来源Claude Code 源码快照2026-03-31
> 上下文管理部分见另一份笔记 `Claude_Code_Context_Management_Notes.md`
---
## 目录
1. [权限系统:多层安全级联](#1-权限系统多层安全级联)
2. [流式工具执行:模型推理与工具执行重叠](#2-流式工具执行模型推理与工具执行重叠)
3. [并发控制与重试策略](#3-并发控制与重试策略)
4. [终端渲染引擎:双缓冲 + Diff 写入](#4-终端渲染引擎双缓冲--diff-写入)
5. [纯 TypeScript 原生模块移植](#5-纯-typescript-原生模块移植)
6. [FileEditTool12 步验证链](#6-filedittool12-步验证链)
7. [记忆系统:提取与召回](#7-记忆系统提取与召回)
8. [多 Agent 协调Swarm 架构](#8-多-agent-协调swarm-架构)
9. [启动优化:并行预取](#9-启动优化并行预取)
10. [其他精巧设计](#10-其他精巧设计)
---
## 1. 权限系统:多层安全级联
### 1.1 Bash 命令权限解析8 步级联)
文件:`src/tools/BashTool/bashPermissions.ts`
```
Step 0: Tree-sitter AST 解析
→ simple干净命令/ too-complex无法静态分析/ parse-unavailable降级正则
+ Shadow 模式Tree-sitter 观察性运行,记录与 legacy 路径的分歧
Step 1: 沙盒自动放行
→ sandboxing 启用 + autoAllowBashIfSandboxed → 允许
→ 复合命令拆分,逐子命令检查 deny 规则
Step 2: 精确匹配权限检查
→ 优先级deny > ask > allow > passthrough
Step 3: LLM 分类器 deny/ask 规则
→ Haiku 模型并行分类 deny 和 ask 描述列表
→ 仅高置信度结果触发
Step 4: 命令操作符拆分
→ 对 |, &&, ||, ; 递归调用权限检查
→ 即使管道段被允许,原始命令仍重新验证
Step 5: Legacy 误解析门控
→ 仅当 Tree-sitter 不可用时运行
Step 6: 逐子命令检查
→ splitCommand → 过滤 cd ${cwd} → checkCommandAndSuggestRules
Step 7: 8 步子级联
→ 精确匹配 → 前缀 deny → ask 规则 → 路径约束
→ 精确 allow → 前缀/通配符 allow → sed 约束
→ 模式权限 → 只读检查 → passthrough
```
### 1.2 规则匹配中的安全技巧
**复合命令防护**:前缀和通配符规则拒绝匹配复合命令。防止 `cd /path && python3 evil.py``cd:*` 规则放行。
**环境变量剥离的不对称设计**
- Deny 规则:使用激进的 `stripAllLeadingEnvVars`(固定点循环剥离所有环境变量),防止 `FOO=bar denied_cmd` 绕过
- Allow 规则:使用保守的 `stripSafeWrappers`,只接受白名单中的 ~60 个安全环境变量
**安全环境变量白名单**SAFE_ENV_VARS
- 包含:`NODE_ENV`, `RUST_LOG`, `CGO_ENABLED`
- 排除:`PATH`, `LD_PRELOAD`, `LD_LIBRARY_PATH`, `DYLD_*`, `PYTHONPATH`, `NODE_OPTIONS`, `BASH_ENV`
### 1.3 Shell 安全分析23 个验证器)
文件:`src/tools/BashTool/bashSecurity.ts`
按安全优先级排序的验证链:
| 验证器 | 检测内容 |
|--------|---------|
| `validateJqCommand` | `system()`, `-f`, `--from-file`, `-L` |
| `validateObfuscatedFlags` | 引号内隐藏的标志(如 `"-rf"` |
| `validateShellMetacharacters` | 参数中的 `;`, `\|`, `&` |
| `validateDangerousVariables` | `BASH_ENV`, `PROMPT_COMMAND`, `PS1`, `BASH_FUNC_*` |
| `validateCommentQuoteDesync` | 引号内的 `#`(注释跟踪混淆) |
| `validateCarriageReturn` | CR 字符shell-quote/bash 分词差异) |
| `validateIFSInjection` | `IFS=` 赋值 |
| `validateDangerousPatterns` | 反引号, `$()`, `${}`, zsh 扩展 (`=cmd`, `~[`, `(e:`) |
| `validateUnicodeWhitespace` | 非 ASCII 空白字符 |
| `validateBraceExpansion` | `{...,...}` 模式 |
| `validateZshDangerousCommands` | 20+ 个 zsh 内建命令(`zmodload`, `syswrite`, `ztcp` 等) |
**关键排序技巧**:非误解析验证器的 `ask` 结果被延迟返回。循环继续运行误解析验证器;只有没有误解析验证器触发时,延迟的非误解析结果才被返回。防止非误解析的 `ask` 掩盖应该设置 `isBashSecurityCheckForMisparsing` 的误解析 `ask`
### 1.4 只读命令分类(双层判定)
文件:`src/tools/BashTool/readOnlyValidation.ts`
**Tier 1 — Flag 级别白名单**:每个命令的每个 flag 都有类型标注(`none`, `number`, `string`, 特定字面量)。特例:
- `xargs``-i`/`-e` 被移除GNU `getopt_long` 可选参数语义漏洞)
- `tree``-R` 被移除(它会写文件)
- `fd`/`fdfind``-x`/`--exec` 被排除
**Tier 2 — 正则匹配**`cat`, `head`, `tail`, `wc`, `jq`, `echo`, `pwd` 等简单命令。
**复合命令安全**`cd && git` 组合被阻止(沙盒逃逸 — 通过恶意 git hooks。检测写入 `.git/hooks/``objects/``refs/` 后运行 git 的命令链。
### 1.5 Auto-Mode (YOLO) 分类器
文件:`src/utils/permissions/yoloClassifier.ts`
**不是传统 ML — 是 LLM 即分类器**
**两阶段 XML 分类**
```
Stage 1快速判定
max_tokens=64, stop_sequences=['</block>']
→ "no"(允许):立即返回
→ "yes" 或无法解析:升级到 Stage 2
Stage 2深度推理
max_tokens=4096, 启用 chain-of-thought
→ 用 <thinking> 标签推理后再决定
→ 解析时先剥离 <thinking> 块再匹配 <block> 标签
```
**200ms 竞赛模式**interactiveHandler.ts
```
5 个参赛者同时启动:
1. 用户权限对话框
2. Hooks 异步执行
3. Bash 分类器异步执行
4. Bridge 权限响应claude.ai
5. Channel 权限中继Telegram 等)
createResolveOnce 原子 claim() — 第一个到达的赢,其他 no-op
200ms 宽限期:
前 200ms 内忽略用户交互(防止意外按键取消分类器)
200ms 后任何用户交互都会杀死分类器的自动批准机会
```
**投机性分类器检查**:在权限对话框出现之前就启动分类器(`startSpeculativeClassifierCheck`),与 deny/ask 分类器、hooks、对话框设置并行运行。
---
## 2. 流式工具执行:模型推理与工具执行重叠
文件:`src/services/tools/StreamingToolExecutor.ts`
### 核心思想
工具在模型**还在生成 token 时就开始执行**,而非等完整响应结束。
### 并发控制算法
```typescript
canExecuteTool(isConcurrencySafe: boolean): boolean {
const executing = this.tools.filter(t => t.status === 'executing')
return (
executing.length === 0 ||
(isConcurrencySafe && executing.every(t => t.isConcurrencySafe))
)
}
```
- 没有正在执行的工具 → 可以执行
- 自身并发安全 且 所有正在执行的也并发安全 → 可以执行
- 否则 → 等待
### 工具状态机
```
queued → executing → completed → yielded
```
队列有序处理。非并发工具遇到无法执行时直接 break保持严格顺序并发工具可跳过。
### 错误级联
**只有 Bash 错误才取消兄弟工具**。原因Bash 命令有隐式依赖链mkdir 失败 → 后续命令无意义。Read/WebFetch 等独立工具不取消。
`siblingAbortController` 是 parent 的子控制器 — 兄弟子进程立即死亡,但父 query loop 不中断。
### 进度流式传输
使用 Promise 信号机制(`progressAvailableResolve`)。`getRemainingResults()` async generator 用 `Promise.race` 在工具完成和新进度到达之间等待,确保进度消息实时传递而非缓冲。
### 与 query.ts 的集成
```
流式循环中:
API content_block_stop 事件
→ 立即 yield AssistantMessage
→ query.ts 喂给 StreamingToolExecutor.addTool()
事件间隙:
→ getCompletedResults() 收割已完成的工具
流式结束后:
→ getRemainingResults() 排空所有待处理工具
```
---
## 3. 并发控制与重试策略
### 3.1 并发 Generator 合并all 函数)
文件:`src/utils/generators.ts`
```
维护 waiting 队列 + active Promise 集合
1. 初始填充 concurrencyCap 个 generator
2. Promise.race 所有活跃 generator
3. 某个 yield → 立即 .next() 推进它
4. 某个完成 → waiting 中取一个填补
5. 公平交错 + 有界并行(默认上限 10
```
### 3.2 工具并发安全分类
| 分类 | 工具 | 依据 |
|------|------|------|
| 始终安全 | FileRead, LSP, TaskCreate, TaskGet, Brief | 返回 `true` |
| 输入依赖 | BashTool | 委托给 `isReadOnly(input)`,只读命令并发,写命令独占 |
| 默认不安全 | FileEdit, FileWrite 等 | 使用默认 `false`fail-closed |
### 3.3 指数退避 + 抖动重试
文件:`src/services/api/withRetry.ts`
```
baseDelay = min(500ms * 2^(attempt-1), 32s)
jitter = random(0, 25% * baseDelay) // 加性抖动,非乘性
finalDelay = baseDelay + jitter
// server retry-after header 存在时覆盖
```
**529过载处理 — 级联放大防护**
```
非前台查询(摘要、标题、分类器)→ 收到 529 立即放弃(不重试)
前台查询 → 重试最多 3 次
3 次 529 后 → FallbackTriggeredError → 切换备用模型
```
**Fast Mode 降级**
```
429/529 + fast mode 激活:
retry-after < 20s → 继续 fast mode 重试(保持 prompt cache
retry-after ≥ 20s → 冷却(最少 10min切换标准速度
```
**无人值守模式**`CLAUDE_CODE_UNATTENDED_RETRY`
```
429/529 无限重试,最大退避 5 分钟
长休眠拆为 30s 心跳间隔(防止主机标记会话空闲)
6 小时硬上限重置
```
### 3.4 流式看门狗
文件:`src/services/api/claude.ts`
```
空闲超时默认 90s可配置
50% 时间 → 警告
100% 时间 → 硬中止挂起的流
每个 chunk 重置计时器
```
---
## 4. 终端渲染引擎:双缓冲 + Diff 写入
### 4.1 双缓冲帧交换
文件:`src/ink/ink.tsx`, `src/ink/output.ts`, `src/ink/screen.ts`
```
每次渲染后:
backFrame = frontFrame
frontFrame = newFrame
```
**Screen 缓冲区**packed `Int32Array`,每个单元格 2 个 word8 bytes
- Word 0: char pool ID
- Word 1: style ID + hyperlink ID + cell width位打包
- `resetScreen()` 复用同一 buffer只增不缩`BigInt64Array` 视图批量清零
**charCache**16384 上限):跨帧缓存 grapheme 聚类结果,大多数行不变时直接命中。
### 4.2 Diff 算法
文件:`src/ink/screen.ts` (diffEach), `src/ink/log-update.ts`
```
1. 计算两帧 damage rectangle 的并集
2. 仅在 damage 区域内逐 word 比较 Int32Array
3. findNextDiff() 是纯函数,设计为 JIT 内联
4. VirtualScreen 跟踪光标位置,只在目标不一致时发移动指令
```
**关键优化**
- **DECSTBM 硬件滚动**ScrollBox 的 scrollTop 变化时用终端硬件滚动(`CSI top;bot r + CSI n S/T`),而非重写整个区域。先对 prev.screen 执行 `shiftRows()` 模拟硬件位移,后续 diff 自然只找到新滚入的行。
- **StylePool.transition()**:按 (fromId, toId) 对缓存 ANSI 样式转换字符串 — 预热后零分配
- **fg-only 空格跳过**:只有前景色的空格单元格视为不可见,跳过写入
### 4.3 Blit 优化
文件:`src/ink/render-node-to-output.ts`
```
节点干净not dirty且布局位置不变
→ 直接从 prevScreen 复制单元格blit
→ blitRegion() 使用 TypedArray.set() 批量内存拷贝
→ 每行一次调用,连续全宽区域只需一次
→ 跳过整个子树的重新渲染
```
### 4.4 渲染器 Peephole 优化
文件:`src/ink/optimizer.ts`
单趟扫描 Diff 数组:
- 合并连续 `cursorMove`(加 dx/dy
- 折叠连续 `cursorTo`(只保留最后一个)
- 拼接相邻 `styleStr`
- 取消 cursor hide/show 对
- 去重相同 URI 的 hyperlink patch
- 移除 count=0 的 clear patch
---
## 5. 纯 TypeScript 原生模块移植
### 5.1 Yoga 布局引擎C++ → TypeScript
文件:`src/native-ts/yoga-layout/index.ts`~2400 行)
完整的 Flexbox 布局实现,消除了 native binary 依赖。
**多层缓存策略**
| 缓存 | 机制 | 效果 |
|------|------|------|
| Dirty-flag | 干净子树 + 匹配输入 → 跳过 | 最基本的剪枝 |
| 双槽缓存 | 分别缓存 layout 和 measure 结果 | 同一节点两种调用模式 |
| 4 槽环形缓存 | packed Float64Array | 500 消息 scrollbox: 76k→4k layoutNode 调用 |
| flex-basis 缓存 | generation-stamped | 短路递归 computeFlexBasis |
| 快速路径标志 | `_hasAutoMargin` 等 | 全零情况单分支跳过 |
**`resolveEdges4Into()`**:一次遍历解析全部 4 条物理边到预分配元组,提升共享 fallback 查找。
### 5.2 模糊搜索Rust nucleo/fzf → TypeScript
文件:`src/native-ts/file-index/index.ts`
**逐步过滤架构**
```
Step 1: 字符位图过滤O(1) 拒绝)
→ 每个路径一个 26-bit charBits 掩码
→ (charBits & needleBitmap) !== needleBitmap → 跳过
Step 2: 融合 indexOf 扫描
→ String.indexOf()V8/JSC 中 SIMD 加速)
→ 同时找到匹配位置 + 累积 gap/consecutive 分数
→ 无需第二次评分遍历
Step 3: Gap-bound 拒绝
→ 计算分数上限(所有边界奖励)减去已知 gap 惩罚
→ 无法超过当前 top-k 阈值 → 跳过昂贵的边界评分
Step 4: 边界/驼峰评分
→ 路径分隔符 (/\-_.) 匹配奖励
→ 驼峰转换匹配奖励
→ 首字符匹配奖励
→ 常数近似 nucleo/fzf-v2 权重
Top-k 维护:升序数组 + 二分插入(避免全量 O(n log n) 排序)
```
**其他特性**
- 异步构建:每 ~4ms yield 事件循环,`readyCount` 支持构建中的部分索引搜索
- 智能大小写:全小写查询 = 大小写不敏感;有大写 = 敏感
- 测试文件惩罚:路径包含 "test" → 1.05x 分数惩罚
### 5.3 语法高亮 + Word-Level DiffRust syntect/bat → TypeScript
文件:`src/native-ts/color-diff/index.ts`
- highlight.js 延迟加载(避免 ~200ms 的 190+ 语法注册启动成本)
- `diff` npm 包的 `diffArrays` 做词级 diff
- RGB → ANSI-256 颜色近似:移植 `ansi_colours` Rust crate 的立方体 vs 灰阶感知最近索引算法
- Monokai Extended / GitHub-light 作用域到颜色映射
- Storage 关键字重分割highlight.js 把 `const`/`function`/`class` 归为 "keyword";端口重分割以匹配 syntect 的 cyan storage 颜色)
---
## 6. FileEditTool12 步验证链
文件:`src/tools/FileEditTool/FileEditTool.ts`
```
1. 密钥检测 → 阻止向 team memory 文件写入密钥
2. 空操作检测 → old_string === new_string 直接拒绝
3. Deny 规则检查 → 文件路径匹配 deny 权限规则
4. UNC 路径安全 → 跳过 \\server\share防止 NTLM 凭证泄露)
5. 文件大小守卫 → > 1 GiB 拒绝
6. 编码检测 → UTF-16LE BOM (0xFF 0xFE) / UTF-8, \r\n → \n
7. 文件存在检查 → 不存在时建议相似文件findSimilarFile
8. 空 old_string → 仅在文件为空时允许(创建文件场景)
9. Notebook 重定向 → .ipynb 必须使用 NotebookEditTool
10. 陈旧性检测 → mtime 比较,失败时回退到内容比较(避免云同步/杀毒软件时间戳干扰的误报)
11. 引号规范化 → 弯引号→直引号搜索;写入时用启发式恢复弯引号样式
12. 歧义匹配 → 多处匹配 + 非 replace_all → 拒绝并要求更多上下文
```
### 引号规范化算法utils.ts
```
搜索阶段:
1. 精确匹配 old_string → 找到则使用
2. normalizeQuotes(old_string) → 弯引号转直引号
3. 在 normalizeQuotes(fileContent) 中搜索
4. 返回 fileContent 中的原始子串(保留弯引号)
写入阶段preserveQuoteStyle
检测到规范化被应用时:
→ 将 new_string 中的直引号转回弯引号
→ 启发式:空白/行首/开括号后 = 开引号;字母间 = 撇号
```
### 反序列化映射desanitizeMatchString
模型不会看到某些 XML 标签(发送给 API 前被清理)。当模型在编辑中输出清理后的形式时,反向映射:
- `<fnr>``<function_results>`
- `\n\nH:``\n\nHuman:`
### call() 写入路径中的双重陈旧性检查
```
validateInput 时检查一次陈旧性
→ 通过
→ call() 中重新同步读取文件,再次检查
→ 防止 validate 和 call 之间的 TOCTOU 竞态
```
---
## 7. 记忆系统:提取与召回
### 7.1 记忆提取extractMemories
文件:`src/services/extractMemories/`
**架构**:每个完整 query loop 结束时fork 一个子 agent共享父级 prompt cache执行提取。
```
提取流程:
1. 门控:仅主 agent非子 agent非远程模式
2. 重叠守卫:已有提取在运行 → 暂存当前上下文(最新覆盖旧的)
3. 轮次节流:合格轮次未达阈值 → 跳过
4. 互斥:主 agent 已手动写入记忆 → 跳过并推进游标
5. 注入记忆清单:扫描目录 + 读 frontmatter → 预格式化
6. Fork agent 执行:最多 5 轮,受限工具访问
7. 游标推进:仅在成功后;失败时留在原位以重新考虑
8. 尾随运行:完成后检查暂存的待处理上下文
```
**工具限制**Read/Grep/Glob 无限制;只读 BashEdit/Write 仅限记忆目录内。
### 7.2 记忆召回findRelevantMemories
文件:`src/memdir/findRelevantMemories.ts`
**不是启发式评分 — 是 LLM 评分**
```
Phase 1: 扫描
→ 读取记忆目录所有 .md 文件(排除 MEMORY.md
→ 每个文件读前 30 行提取 frontmattername, description, type
→ 按 mtime 降序排列,上限 200 个文件
→ 单遍设计:读取后排序(而非 stat-排序-读取syscall 减半
Phase 2: LLM 选择
→ 发送查询 + 格式化清单 + 最近使用工具列表给 Sonnet
→ 结构化 JSON 输出
→ "只包含你确定有帮助的记忆。不确定就不包含。最多 5 个。"
→ 最近使用工具列表防止为已活跃使用的工具推荐 API 文档
→ 但关于这些工具的警告/陷阱仍然会被选中
Phase 3: 新鲜度处理
→ 超过 1 天的记忆注入 <system-reminder> 告警
→ "此记忆已 N 天。关于代码行为的声明可能过时。"
```
**already-surfaced 过滤**过滤掉之前轮次已展示的路径5 个名额全部花在新候选上。
---
## 8. 多 Agent 协调Swarm 架构
### 8.1 协调者模式Coordinator
文件:`src/coordinator/coordinatorMode.ts`
```
任务工作流阶段:
Research并行 workers→ Synthesis协调者→ Implementationworkers→ Verificationworkers
并发规则:
只读任务 → 自由并行
写密集任务 → 按文件区域串行
验证可与不同文件的实现重叠
Worker prompt 必须自包含:
Worker 看不到协调者对话 → 每个 prompt 需要完整上下文(文件路径、行号等)
Continue vs Spawn 决策:
高上下文重叠 → continue复用已加载上下文
低上下文重叠 → spawn 新 worker
```
### 8.2 两种后端策略
| 后端 | 隔离方式 | 通信 | 特点 |
|------|---------|------|------|
| In-Process | `AsyncLocalStorage` 上下文隔离 | 基于文件的 mailbox | 共享 API client + MCP 连接;独立 AbortControllerleader 中断不杀 worker |
| Pane-Based (tmux/iTerm2) | 独立 OS 进程 | 基于文件的 mailbox | CLI flag 传播(`--agent-id`, `--agent-name`, `--team-name`, `--agent-color`leader 的模型/权限/环境变量全部传播 |
### 8.3 权限桥接
In-process teammate 通过 `leaderPermissionBridge` 路由权限提示到 leader 的 UI复用 BashPermissionRequest、FileEditToolDiff 等对话框。Bridge 不可用时回退到 mailbox 权限同步。
### 8.4 Fork Subagent
文件:`src/tools/AgentTool/forkSubagent.ts`
```
子进程继承父级完整对话上下文 + system prompt
递归 fork 防护:
isInForkChild() 检查对话历史中的 <fork_boilerplate> 标签
Cache 共享设计:
保留完整父级 assistant 消息(所有 tool_use 块)
构建 tool_result 块(占位文本:"Fork started -- processing in background"
只有最后的 text 块不同 → 最大化 prompt cache 命中
子进程 10 条严格规则:
不生成子 agent、不评论、只用工具、提交变更、
结构化输出Scope/Result/Key files/Files changed/Issues、最多 500 字
```
---
## 9. 启动优化:并行预取
文件:`src/main.tsx` 前 20 行
```typescript
// 这些副作用必须在所有其他 import 之前运行:
profileCheckpoint('main_tsx_entry') // 标记入口(在 ~135ms import 之前)
startMdmRawRead() // 并行MDM 子进程读取plutil/reg query
startKeychainPrefetch() // 并行macOS 钥匙串双读取
// 无此优化:~65ms 同步阻塞(每次 macOS 启动)
```
**启动分析器**`startupProfiler.ts`
- 采样日志:内部 100%,外部 0.5%。记录 import_time, init_time, settings_time, total_time
- 详细分析:`CLAUDE_CODE_PROFILE_STARTUP=1`,带 `process.memoryUsage()` 快照的完整时间线
- 非采样用户零开销(`profileCheckpoint` 立即返回)
---
## 10. 其他精巧设计
### 10.1 35 行状态管理
文件:`src/state/store.ts`
```typescript
createStore<T>(initialState, onChange?) => {
getState() // 返回当前状态
setState(updater) // updater: (prev) => next
// Object.is() 相等检查(引用相同则跳过)
// 触发 onChange + 通知 Set<Listener>
subscribe(listener) // 返回 unsubscribe
}
```
无中间件,无选择器,无 devtools。配合 `useSyncExternalStore` 实现最小化重渲染。
### 10.2 ToolSearchTool — 延迟工具发现
文件:`src/tools/ToolSearchTool/ToolSearchTool.ts`
**两种查询模式**
直接选择(`select:ToolA,ToolB`):精确查找,返回 `tool_reference` 块。
关键词搜索评分:
| 匹配类型 | 分数 |
|---------|------|
| 名称部分精确匹配 | +10MCP: +12 |
| 名称部分子串匹配 | +5MCP: +6 |
| 全名回退 | +3 |
| searchHint 词边界匹配 | +4 |
| 描述词边界匹配 | +2 |
`+` 前缀标记必需词(全部必须匹配才入围)。
工具名解析MCP 工具去 `mcp__` 前缀后按 `__``_` 分割;普通工具按驼峰转换和 `_` 分割。
### 10.3 Token 估算(无 API 调用)
文件:`src/utils/tokens.ts`
```
粗略估算content.length / bytesPerToken
文件类型感知:
JSON/JSONL/JSONC: 2 bytes/token密集单字符 token
其他: 4 bytes/token
区块级估算:
text/thinking: length / 4
image/document: 固定 2000 tokens
tool_use: (name + JSON.stringify(input)).length / 4
上下文窗口估算tokenCountWithEstimation
1. 从后向前找到最后一条有 API usage 数据的消息
2. 处理并行工具调用:跨越共享 message.id 的兄弟记录
3. 返回 usage.input_tokens + 粗略估算(后续消息)
4. 无 usage 数据时全量粗略估算
```
### 10.4 错误扣留模式Error Withholding
```
流式传输中可恢复错误不暴露给调用者:
→ prompt_too_long, media_size, max_output_tokens
→ 推入 assistantMessages 供恢复检查
→ 所有恢复失败后才 yield 给用户
→ 防止 SDK 消费者在中间错误时终止会话
```
### 10.5 Prompt Cache 稳定性设计集锦
| 技术 | 位置 | 效果 |
|------|------|------|
| 工具池排序 | `assembleToolPool()` | 防止 MCP 变更破坏缓存前缀 |
| 系统 prompt 分界标记 | `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` | 静态部分 `scope: global` 跨组织缓存 |
| Agent 列表附件注入 | AgentTool prompt.ts | 从工具描述中移出动态列表(减少 10.2% cache_creation |
| Fork 消息构造 | forkSubagent.ts | 所有子进程共享相同 tool_result 占位符,仅最后文本块不同 |
| Tool result 替换一致性 | toolResultStorage.ts | mustReapply 重用完全相同的替换字符串(字节级一致) |
| frozen 分区 | toolResultStorage.ts | 进入 cache 的内容永不修改 |
### 10.6 文本选择算法
文件:`src/ink/selection.ts`
- anchor + focus 点用屏幕缓冲区坐标col, row
- 双击/三击选词/行模式:`anchorSpan` 启用拖拽时按词/行扩展
- 滚动捕获:`scrolledOffAbove`/`scrolledOffBelow` 累加器捕获拖拽滚动时离开视口的行
- 词边界检测匹配 iTerm2 默认行为(路径字符 `/-+\~_.` 视为词字符)
- `getSelectedText()` 合并离屏和在屏行,尊重软换行标记重建逻辑行
### 10.7 鼠标 Hit Testing
文件:`src/ink/hit-test.ts`
递归深度优先遍历 DOM 树。**子节点逆序遍历**(后绘制的在上层),确保正确 z-order。
- `dispatchClick()`:从最深命中节点沿 parentNode 冒泡
- `dispatchHover()`:类 DOM mouseEnter/mouseLeave非冒泡diff hovered-node 集合
### 10.8 GrepTool 分页
```
默认限制250 条(未指定时)
head_limit=0无限制谨慎使用
offset 参数:跳过前 N 条
分页在 ripgrep 返回后、路径相对化之前应用(节省 CPU
appliedLimit 仅在实际截断时报告(让模型知道有更多结果)
```
### 10.9 Partial Compact 方向性
```
from默认
摘要 pivot 之后的消息,保留之前的
→ prompt cache 保留(保留的消息在前)
up_to
摘要 pivot 之前的消息,保留之后的
→ prompt cache 失效(摘要在保留消息之前)
→ 剥离旧 compact 边界和摘要(防止陈旧边界混淆扫描器)
```
### 10.10 API 消息轮次分组
文件:`src/services/compact/grouping.ts`
```
groupMessagesByApiRound
按 API 轮次边界分组(不同 message.id 标记新轮次)
比之前的 human-turn 分组更细粒度
→ 支持单 human turn 的 SDK/CCR/eval 会话中的精确 compact
流式 chunk 共享 id → 同一响应内的交错 tool_result 保持正确分组
```

View File

@@ -0,0 +1,871 @@
# Claude Code 上下文压缩算法深度分析
> 基于 Claude Code 源码2026-03-31 快照512K 行 TypeScript逆向分析
> 核心文件:`src/services/compact/` 目录下 11 个文件
---
## 目录
1. [架构总览](#1-架构总览)
2. [第1层微压缩Microcompact](#2-第1层微压缩microcompact)
3. [第2层自动压缩Auto-Compact](#3-第2层自动压缩auto-compact)
4. [第3层传统压缩Full Compact](#4-第3层传统压缩full-compact)
5. [第4层Session Memory 压缩](#5-第4层session-memory-压缩)
6. [消息分组算法](#6-消息分组算法)
7. [Token 估算算法](#7-token-估算算法)
8. [压缩提示词工程](#8-压缩提示词工程)
9. [5层错误恢复中的压缩角色](#9-5层错误恢复中的压缩角色)
10. [各层对比与设计哲学](#10-各层对比与设计哲学)
---
## 1. 架构总览
Claude Code 的上下文压缩不是单一算法,而是一个 **4层递进的压缩体系**,每层解决不同层面的问题:
```
用户消息 → [第1层微压缩] → [第2层自动压缩] → API 调用
↓ ↓
细粒度清理旧工具输出 上下文即将超限时触发
(不丢语义,<1ms) (调用 LLM 或 Session Memory)
┌──────────────┴──────────────┐
[第4层SM压缩] [第3层传统压缩]
(用已有摘要,<10ms) (Fork Agent 生成摘要5-30s)
```
**核心原则:** 尽可能用廉价的规则操作延迟昂贵的 LLM 调用,只在不得已时丢弃信息。
### 涉及的源文件
| 文件 | 行数 | 职责 |
|------|------|------|
| `microCompact.ts` | ~400 | 微压缩:规则清理旧工具结果 |
| `apiMicrocompact.ts` | — | API 层缓存编辑集成 |
| `timeBasedMCConfig.ts` | — | 时间触发微压缩配置 |
| `autoCompact.ts` | ~350 | 自动压缩:阈值判断 + 断路器 |
| `compact.ts` | ~600+ | 传统压缩Fork Agent 摘要 |
| `prompt.ts` | ~375 | 压缩提示词模板 |
| `sessionMemoryCompact.ts` | ~630 | Session Memory 压缩路径 |
| `grouping.ts` | ~63 | 消息按 API 轮次分组 |
| `postCompactCleanup.ts` | — | 压缩后清理 |
| `compactWarningHook.ts` | — | 压缩警告钩子 |
| `compactWarningState.ts` | — | 压缩警告状态 |
---
## 2. 第1层微压缩Microcompact
**源文件:** `microCompact.ts`
### 核心思想
不调用 LLM纯规则操作——清理旧的、大块的工具输出结果保留语义信息。这是每轮查询前都会执行的最轻量操作。
### 可压缩工具白名单
```typescript
const COMPACTABLE_TOOLS = new Set([
'Read', // 文件读取结果可能很大
'Bash', // Shell 输出可能很长
'Grep', // 搜索结果
'Glob', // 文件列表
'WebSearch', // 网页搜索结果
'WebFetch', // 网页抓取结果
'Edit', // 文件编辑的 diff
'Write', // 文件写入确认
])
```
不在白名单中的工具(如 Agent、Skill、MCP 等)的结果不会被微压缩。
### 两个子路径
#### 子路径 A时间触发微压缩
```
触发条件距上次助手消息的时间间隔超过阈值API 缓存已过期)
执行逻辑:
1. 收集所有可压缩工具的 tool_use ID
2. 保留最近 N 个工具结果
3. 将更早的 tool_result 内容替换为:
"[Old tool result content cleared]"
4. 不修改 tool_use 块(保持 API 配对完整性)
特点:
- 缓存已过期,所以无需保护缓存
- 直接修改本地消息内容
- 减少重传时的 token 消耗
```
#### 子路径 B缓存编辑微压缩Cached MC
这是更精巧的路径——在缓存仍然有效时工作:
```
触发条件:
- 特性开关 CACHED_MICROCOMPACT 开启
- 模型支持缓存编辑 API
- 当前是主线程查询(非 fork agent
执行逻辑:
1. collectCompactableToolIds(): 收集所有可压缩的 tool_use ID
2. registerToolResult(): 注册每个工具结果(按用户消息分组)
3. registerToolMessage(): 记录工具消息组
4. getToolResultsToDelete(): 根据 count/keep 阈值决定删除哪些
5. createCacheEditsBlock(): 生成 cache_edits API 块
关键区别:
- 不修改本地消息内容!
- 通过 API 的 cache_edits 字段告诉服务端删除特定工具结果的缓存
- 保持 prompt cache 命中率
- 状态通过 pendingCacheEdits / pinnedCacheEdits 管理
```
**缓存编辑的状态管理:**
```typescript
// 全局状态
let cachedMCState: CachedMCState | null = null
let pendingCacheEdits: CacheEditsBlock | null = null
// consumePendingCacheEdits():
// 返回待插入的缓存编辑块,然后清空 pending 状态
// 调用者在 API 请求后必须调用 pinCacheEdits() 固定它们
// getPinnedCacheEdits():
// 返回之前已固定的缓存编辑,需要在后续请求中重新发送
// markToolsSentToAPIState():
// 标记工具已发送给 API成功响应后调用
```
### Token 估算辅助
```typescript
function calculateToolResultTokens(block: ToolResultBlockParam): number {
if (typeof block.content === 'string') {
return roughTokenCountEstimation(block.content) // 字符数 / 4
}
// 数组:逐项计算
return block.content.reduce((sum, item) => {
if (item.type === 'text') return sum + roughTokenCountEstimation(item.text)
if (item.type === 'image' || item.type === 'document') return sum + 2000
return sum
}, 0)
}
function estimateMessageTokens(messages: Message[]): number {
let totalTokens = 0
for (const message of messages) {
for (const block of message.message.content) {
switch (block.type) {
case 'text': totalTokens += roughTokenCountEstimation(block.text); break
case 'tool_result': totalTokens += calculateToolResultTokens(block); break
case 'image':
case 'document': totalTokens += 2000; break // 固定估算
case 'thinking': totalTokens += roughTokenCountEstimation(block.thinking); break
case 'redacted_thinking': totalTokens += roughTokenCountEstimation(block.data); break
case 'tool_use': totalTokens += roughTokenCountEstimation(block.name + JSON.stringify(block.input ?? {})); break
default: totalTokens += roughTokenCountEstimation(JSON.stringify(block)); break
}
}
}
return Math.ceil(totalTokens * (4 / 3)) // × 4/3 保守填充
}
```
---
## 3. 第2层自动压缩Auto-Compact
**源文件:** `autoCompact.ts`
### 阈值计算
```typescript
// 有效上下文窗口 = 模型上下文窗口 - 摘要输出预留
const MAX_OUTPUT_TOKENS_FOR_SUMMARY = 20_000 // p99.99 的摘要输出是 17,387 tokens
function getEffectiveContextWindowSize(model: string): number {
const reservedTokensForSummary = Math.min(
getMaxOutputTokensForModel(model),
MAX_OUTPUT_TOKENS_FOR_SUMMARY,
)
let contextWindow = getContextWindowForModel(model)
// 支持环境变量覆盖(用于测试)
if (process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW) {
contextWindow = Math.min(contextWindow, parsed)
}
return contextWindow - reservedTokensForSummary
}
// 自动压缩阈值 = 有效上下文窗口 - 13K 缓冲
const AUTOCOMPACT_BUFFER_TOKENS = 13_000
function getAutoCompactThreshold(model: string): number {
return getEffectiveContextWindowSize(model) - AUTOCOMPACT_BUFFER_TOKENS
}
// 举例Opus 200K 上下文):
// 有效窗口 = 200,000 - 20,000 = 180,000
// 自动压缩阈值 = 180,000 - 13,000 = 167,000 tokens
```
### 其他阈值
```typescript
const WARNING_THRESHOLD_BUFFER_TOKENS = 20_000 // 警告阈值
const ERROR_THRESHOLD_BUFFER_TOKENS = 20_000 // 错误阈值
const MANUAL_COMPACT_BUFFER_TOKENS = 3_000 // 手动 /compact 的阻塞限制
```
### 断路器机制
```typescript
const MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3
// BQ 数据分析发现1,279 个会话连续失败 50+ 次(最多 3,272 次),
// 每天浪费约 250K 次 API 调用。所以加了断路器。
if (tracking.consecutiveFailures >= MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES) {
return { wasCompacted: false } // 直接跳过,不再尝试
}
```
### shouldAutoCompact 决策树
```
shouldAutoCompact(messages, model, querySource):
1. querySource 是 'session_memory' 或 'compact'
→ 返回 false防止递归死锁——压缩代理自己不能触发压缩
2. querySource 是 'marble_origami'(上下文折叠代理)?
→ 返回 false防止破坏主线程的已提交日志
3. isAutoCompactEnabled() 返回 false
→ 返回 false
(检查 DISABLE_COMPACT、DISABLE_AUTO_COMPACT 环境变量和用户配置)
4. 响应式压缩模式开启tengu_cobalt_raccoon gate
→ 返回 false让 API 的 prompt-too-long 错误触发响应式压缩)
5. 上下文折叠模式开启?
→ 返回 false上下文折叠是 90%/95% 流程,自动压缩在 93% 会干扰它)
6. tokenCountWithEstimation(messages) - snipTokensFreed >= threshold
→ 返回 true
```
### autoCompactIfNeeded 执行流程
```
autoCompactIfNeeded(messages, context, ...):
1. DISABLE_COMPACT 环境变量?→ 跳过
2. 断路器检查consecutiveFailures >= 3→ 跳过
3. shouldAutoCompact() 返回 true
↓ 是
4. 优先尝试 Session Memory 压缩
↓ 成功 → 返回结果
↓ 失败
5. 回退到传统压缩compactConversation
↓ 成功 → 重置 consecutiveFailures = 0返回结果
↓ 失败
6. consecutiveFailures++
↓ 达到 3 次
7. 日志:断路器触发,本次会话不再尝试自动压缩
```
---
## 4. 第3层传统压缩Full Compact
**源文件:** `compact.ts` + `prompt.ts`
### 核心机制Fork Agent
传统压缩使用一个 **Fork Agent**——创建当前会话的一个分支,让它生成摘要。关键优势是**共享主会话的 prompt cache**。
```
主会话消息: [user1, assistant1, user2, assistant2, ...]
↓ 全部传入
Fork Agent共享 prompt cache
↓ 单轮回复
生成结构化摘要(<analysis> + <summary>
后处理 → 替换原消息
```
### 预处理管线
```
原始消息
stripImagesFromMessages() ← 图片 → "[image]",文档 → "[document]"
↓ (防止压缩请求自身超过上下文限制)
stripReinjectedAttachments() ← 删除技能发现/列表附件
↓ (压缩后会自动重新注入)
normalizeMessagesForAPI() ← 规范化消息格式
发送给 Fork Agent
```
### 摘要输出格式
Fork Agent 被要求生成两个 XML 块:
```xml
<analysis>
[思考草稿——用于提高摘要质量的中间推理过程]
[这部分最终会被删除,不会进入压缩后的上下文]
</analysis>
<summary>
1. Primary Request and Intent:
[详细描述用户的所有请求和意图]
2. Key Technical Concepts:
- [概念1]
- [概念2]
3. Files and Code Sections:
- [文件名1]
- [为什么这个文件重要]
- [代码片段]
- [文件名2]
- [代码片段]
4. Errors and fixes:
- [错误描述]:
- [修复方式]
- [用户反馈]
5. Problem Solving:
[问题解决过程]
6. All user messages:
- [逐条列出所有非工具结果的用户消息]
7. Pending Tasks:
- [待办事项1]
- [待办事项2]
8. Current Work:
[精确描述当前工作内容,包含文件名和代码片段]
9. Optional Next Step:
[下一步计划,包含最近对话的直接引用]
</summary>
```
### 防止工具调用的强力前导词
```typescript
const NO_TOOLS_PREAMBLE = `CRITICAL: Respond with TEXT ONLY. Do NOT call any tools.
- Do NOT use Read, Bash, Grep, Glob, Edit, Write, or ANY other tool.
- You already have all the context you need in the conversation above.
- Tool calls will be REJECTED and will waste your only turn — you will fail the task.
- Your entire response must be plain text: an <analysis> block followed by a <summary> block.
`
```
为什么需要这么强力?注释解释了:
```
// Sonnet 4.6+ 自适应思考模型有时会忽略较弱的尾部指令并尝试调用工具。
// 在 maxTurns: 1 的情况下,被拒绝的工具调用意味着没有文本输出
// → 回退到流式备用路径4.6 上 2.79% vs 4.5 上 0.01%)。
// 把这个放在最前面并明确说明拒绝后果,可以防止浪费轮次。
```
### 后处理formatCompactSummary
```typescript
function formatCompactSummary(summary: string): string {
// 1. 删除 <analysis> 块(草稿,已无价值)
summary = summary.replace(/<analysis>[\s\S]*?<\/analysis>/, '')
// 2. 提取 <summary> 内容,替换为可读标题
const match = summary.match(/<summary>([\s\S]*?)<\/summary>/)
if (match) {
summary = summary.replace(/<summary>[\s\S]*?<\/summary>/,
`Summary:\n${match[1].trim()}`)
}
// 3. 清理多余空行
summary = summary.replace(/\n\n+/g, '\n\n')
return summary.trim()
}
```
### 压缩后消息序列
```
[CompactBoundaryMessage] ← 标记压缩边界(含 token 统计、trigger 类型)
[SummaryUserMessage] ← 格式化后的摘要
[messagesToKeep] ← 保留的最近消息(如果有)
[Attachments] ← 重新注入的附件
- 最近读取的文件(前 5 个,每个 ≤ 5K tokens总预算 50K
- Plan 文件(如果有活跃计划)
- MCP 指令增量
- 技能发现增量
- 代理列表增量
[HookResults] ← PreCompact/PostCompact 钩子结果
```
### Prompt-Too-Long 重试机制
当压缩请求本身超过上下文限制时:
```typescript
const MAX_PTL_RETRIES = 3
function truncateHeadForPTLRetry(messages, ptlResponse): Message[] | null {
// 1. 移除上一次重试的标记消息(防止重试停滞)
// 2. 按 API 轮次分组
const groups = groupMessagesByApiRound(messages)
if (groups.length < 2) return null // 无法再裁剪
// 3. 决定删除多少组
const tokenGap = getPromptTooLongTokenGap(ptlResponse)
if (tokenGap !== undefined) {
// 能解析出 token 缺口 → 精确删除
let acc = 0, dropCount = 0
for (const g of groups) {
acc += roughTokenCountEstimationForMessages(g)
dropCount++
if (acc >= tokenGap) break
}
} else {
// 不能解析 → 删除 20% 最旧的组
dropCount = Math.max(1, Math.floor(groups.length * 0.2))
}
// 4. 保证至少保留 1 组
dropCount = Math.min(dropCount, groups.length - 1)
// 5. 如果裁剪后第一条是 assistant 消息,补一个 user marker
// API 要求第一条消息必须是 user
const sliced = groups.slice(dropCount).flat()
if (sliced[0]?.type === 'assistant') {
return [createUserMessage({ content: PTL_RETRY_MARKER, isMeta: true }), ...sliced]
}
return sliced
}
```
### 部分压缩Partial Compact
支持两个方向:
| 方向 | 提示词 | 用途 |
|------|--------|------|
| `from`(默认) | `PARTIAL_COMPACT_PROMPT` | 保留旧消息,仅摘要"最近的消息" |
| `up_to` | `PARTIAL_COMPACT_UP_TO_PROMPT` | 摘要旧消息,保留新消息。摘要会放在开头,后续消息跟在后面 |
`up_to` 模式的摘要包含一个特殊的第 9 章节 "Context for Continuing Work",专门为后续消息提供上下文。
---
## 5. 第4层Session Memory 压缩
**源文件:** `sessionMemoryCompact.ts`
### 核心思想
不调用 LLM 生成新摘要,而是直接使用已经通过后台记忆提取(`extractMemories`)积累的 Session Memory 作为"摘要"。
### 优势
- **速度快**:不需要 API 调用,<10ms
- **质量可预测**Session Memory 是在每轮查询后渐进更新的不是一次性压缩
- **保留近期消息**不像传统压缩那样替换所有消息
### 配置参数
```typescript
const DEFAULT_SM_COMPACT_CONFIG = {
minTokens: 10_000, // 至少保留 10K tokens 的最近消息
minTextBlockMessages: 5, // 至少保留 5 条含文本的消息
maxTokens: 40_000, // 最多保留 40K tokens硬上限
}
// 这些值通过 GrowthBook 远程配置,可动态调整
```
### calculateMessagesToKeepIndex 算法
这是 Session Memory 压缩的核心算法——决定保留哪些最近的消息
```
输入messages[], lastSummarizedIndexSession Memory 已覆盖到哪条消息)
算法:
1. startIndex = lastSummarizedIndex + 1
(即:从 Session Memory 尚未覆盖的消息开始)
2. 计算当前 [startIndex, end] 范围的 token 总量和含文本消息数
3. 如果已经超过 maxTokens (40K) → 直接返回(不再扩展)
4. 如果同时满足 ≥ minTokens (10K) AND ≥ minTextBlockMessages (5)
→ 直接返回(已足够)
5. 否则,从 startIndex 往前逐条扩展:
- 每加入一条消息,更新 token 和消息计数
- 停止条件:
a. 达到 maxTokens (40K)
b. 同时满足 minTokens 和 minTextBlockMessages
c. 到达上一个 CompactBoundary不跨越旧的压缩边界
6. adjustIndexToPreserveAPIInvariants(messages, startIndex)
→ 确保不切断 tool_use/tool_result 配对
→ 确保不分离共享 message.id 的 thinking 块
```
### adjustIndexToPreserveAPIInvariants 算法
这个算法解决一个棘手的问题——流式传输时一个 API 响应会产生多条消息thinkingtool_use 它们共享同一个 `message.id`如果在中间切断`normalizeMessagesForAPI` 合并时会丢失 thinking
```
输入messages[], startIndex
步骤 1修复 tool_use/tool_result 配对
1a. 收集 [startIndex, end] 范围内所有 tool_result 的 tool_use_id
1b. 收集范围内已有的 tool_use_id
1c. 找出缺失的 tool_use_id在范围外
1d. 向前搜索,把包含缺失 tool_use 的 assistant 消息纳入范围
步骤 2修复 thinking 块分离
2a. 收集范围内所有 assistant 消息的 message.id
2b. 向前搜索,把共享同一 message.id 的 assistant 消息纳入范围
返回:调整后的 startIndex
```
**源码注释中的真实 bug 场景:**
```
Session 存储(压缩前):
Index N: assistant, message.id: X, content: [thinking]
Index N+1: assistant, message.id: X, content: [tool_use: ORPHAN_ID]
Index N+2: assistant, message.id: X, content: [tool_use: VALID_ID]
Index N+3: user, content: [tool_result: ORPHAN_ID, tool_result: VALID_ID]
如果 startIndex = N+2
旧代码:只检查 N+2 的 tool_results找不到返回 N+2
normalizeMessagesForAPI 合并后:
msg[1]: assistant with [tool_use: VALID_ID] ← ORPHAN tool_use 被排除!
msg[2]: user with [tool_result: ORPHAN_ID, tool_result: VALID_ID]
API 报错:孤立的 tool_result 引用了不存在的 tool_use
```
### trySessionMemoryCompaction 完整流程
```
trySessionMemoryCompaction(messages, agentId, autoCompactThreshold):
1. shouldUseSessionMemoryCompaction()
→ 检查 tengu_session_memory AND tengu_sm_compact 特性开关
→ 支持 ENABLE_CLAUDE_CODE_SM_COMPACT / DISABLE_CLAUDE_CODE_SM_COMPACT 环境变量
2. 初始化远程配置(仅首次)
3. 等待正在进行的 Session Memory 提取完成(带超时)
4. 获取 lastSummarizedMessageId 和 sessionMemory 内容
5. Session Memory 文件不存在?→ 返回 null
6. Session Memory 是空模板?→ 返回 null
7. 确定 lastSummarizedIndex
a. 正常情况:在 messages 中查找 lastSummarizedMessageId 的索引
b. 恢复的会话:设为 messages.length - 1从末尾开始
8. calculateMessagesToKeepIndex() → startIndex
9. 过滤掉 messagesToKeep 中的旧 CompactBoundary
10. 执行 SessionStart 钩子(恢复 CLAUDE.md 等上下文)
11. 创建压缩结果:
- 截断过大的 Session Memory 章节
- 生成摘要用户消息
- 附加 Plan 文件(如果有)
12. 检查压缩后 token 是否仍超过阈值
→ 是:返回 null回退到传统压缩
→ 否:返回 CompactionResult
```
---
## 6. 消息分组算法
**源文件:** `grouping.ts`
```typescript
function groupMessagesByApiRound(messages: Message[]): Message[][] {
const groups: Message[][] = []
let current: Message[] = []
let lastAssistantId: string | undefined
for (const msg of messages) {
// 当出现新的 assistant message.id 时,开始新的一组
if (
msg.type === 'assistant' &&
msg.message.id !== lastAssistantId &&
current.length > 0
) {
groups.push(current)
current = [msg]
} else {
current.push(msg)
}
if (msg.type === 'assistant') {
lastAssistantId = msg.message.id
}
}
if (current.length > 0) {
groups.push(current)
}
return groups
}
```
**设计细节:**
- 同一 API 请求的流式块共享同一个 `message.id`
- `StreamingToolExecutor` 在流式输出期间交错插入 `tool_result`但它们属于同一轮次
- 只要 `message.id` 不变所有消息都在同一组内
- 不跟踪未解决的 `tool_use` ID——让分组边界自然形成 `ensureToolResultPairing` API 层修复残留的配对问题
---
## 7. Token 估算算法
贯穿所有压缩路径的核心辅助
### 粗略估算
```typescript
function roughTokenCountEstimation(text: string): number {
return text.length / 4 // 经验法则4 字符 ≈ 1 token
}
```
### 消息级估算
```typescript
function estimateMessageTokens(messages: Message[]): number {
let totalTokens = 0
for (const message of messages) {
if (message.type !== 'user' && message.type !== 'assistant') continue
for (const block of message.message.content) {
switch (block.type) {
case 'text':
totalTokens += roughTokenCountEstimation(block.text)
break
case 'tool_result':
totalTokens += calculateToolResultTokens(block)
break
case 'image':
case 'document':
totalTokens += 2000 // 固定值,不论实际大小
break
case 'thinking':
totalTokens += roughTokenCountEstimation(block.thinking)
// 注意不计算签名signature 是元数据,不被模型 tokenize
break
case 'redacted_thinking':
totalTokens += roughTokenCountEstimation(block.data)
break
case 'tool_use':
totalTokens += roughTokenCountEstimation(
block.name + JSON.stringify(block.input ?? {})
)
// 不计算 JSON wrapper 和 id 字段
break
default:
totalTokens += roughTokenCountEstimation(JSON.stringify(block))
break
}
}
}
return Math.ceil(totalTokens * (4 / 3)) // 保守填充 33%
}
```
### 精确计算
```typescript
// 当有 API 响应的 usage 数据时,使用精确值
function tokenCountFromLastAPIResponse(messages: Message[]): number | undefined {
// 从最后一条 assistant 消息的 usage 字段读取
// usage.input_tokens 包含了 API 看到的实际 token 数
}
// 混合策略
function tokenCountWithEstimation(messages: Message[]): number {
// 优先使用 API 返回的精确值
// 不可用时回退到估算
}
```
---
## 8. 压缩提示词工程
**源文件:** `prompt.ts`
### 三种提示词模板
| 模板 | 变量名 | 用途 |
|------|--------|------|
| 完整压缩 | `BASE_COMPACT_PROMPT` | 摘要整个对话 |
| 部分压缩from | `PARTIAL_COMPACT_PROMPT` | 只摘要最近的消息 |
| 部分压缩up_to | `PARTIAL_COMPACT_UP_TO_PROMPT` | 摘要旧消息作为后续消息的前导 |
### 提示词结构
```
[NO_TOOLS_PREAMBLE] ← 强力禁止工具调用
[DETAILED_ANALYSIS_INSTRUCTION] ← 要求 <analysis> 思考草稿
[MAIN_PROMPT] ← 9 章节结构化摘要要求 + 示例
[Custom Instructions] ← 用户自定义指令(如果有)
[NO_TOOLS_TRAILER] ← 再次强调不要调用工具
```
### <analysis> 块的作用
```
提示词要求模型先在 <analysis> 中"打草稿"
1. 按时间顺序分析每条消息
2. 识别用户意图、技术决策、代码模式
3. 特别关注用户反馈("用户让你做不同的事情"
4. 双重检查技术准确性和完整性
这类似于 Chain-of-Thought但最终会被 formatCompactSummary() 删除,
只保留 <summary> 部分进入压缩后的上下文。
```
### 压缩后的摘要消息
```typescript
function getCompactUserSummaryMessage(summary, suppressFollowUp, transcriptPath, recentPreserved): string {
let msg = `This session is being continued from a previous conversation that ran out of context.
The summary below covers the earlier portion of the conversation.
${formatCompactSummary(summary)}`
// 如果有 transcript 路径,提供回溯指引
if (transcriptPath) {
msg += `\n\nIf you need specific details from before compaction
(like exact code snippets, error messages, or content you generated),
read the full transcript at: ${transcriptPath}`
}
// 如果保留了最近消息
if (recentMessagesPreserved) {
msg += `\n\nRecent messages are preserved verbatim.`
}
// 如果是自动压缩suppressFollowUp = true
if (suppressFollowUpQuestions) {
msg += `\nContinue the conversation from where it left off
without asking the user any further questions.
Resume directly — do not acknowledge the summary,
do not recap what was happening,
do not preface with "I'll continue" or similar.
Pick up the last task as if the break never happened.`
}
// 如果是 Proactive/KAIROS 模式
if (proactiveModule?.isProactiveActive()) {
msg += `\n\nYou are running in autonomous/proactive mode.
This is NOT a first wake-up — you were already working autonomously before compaction.
Continue your work loop: pick up where you left off based on the summary above.
Do not greet the user or ask what to work on.`
}
return msg
}
```
---
## 9. 5层错误恢复中的压缩角色
`query.ts` 5 层错误恢复机制中压缩系统承担了前 2
```
API 调用失败
第1层上下文折叠排水Context Collapse Drain
↓ 失败
第2层响应式压缩Reactive Compact ← 使用 compact.ts
↓ 失败
第3层最大输出升级8K → 64K tokens
↓ 失败
第4层多轮恢复注入"请继续"消息,最多 3 次)
↓ 失败
第5层模型回退切换到备用模型剥离 thinking 签名块)
↓ 失败
暴露错误给用户
```
**响应式压缩** vs **主动压缩**
| 属性 | 主动压缩Auto-Compact | 响应式压缩Reactive Compact |
|------|--------------------------|-------------------------------|
| 触发 | token 数超过阈值 | API 返回 prompt-too-long 错误 |
| 时机 | API 调用前 | API 调用失败后 |
| 路径 | `autoCompact.ts` `compact.ts` | `query.ts` 错误恢复层 |
| 回退 | SM 传统压缩 放弃 | 传统压缩 裁剪最旧消息 |
---
## 10. 各层对比与设计哲学
### 横向对比
| 属性 | 微压缩 | Session Memory | 传统压缩 | PTL Recovery |
|------|--------|---------------|----------|-------------|
| **调用 LLM** | | | Fork Agent | |
| **信息损失** | 最小仅删工具输出 | 中等靠已有摘要 | 中等9章摘要 | 丢弃最旧消息 |
| **延迟** | <1ms | <10ms | 5-30s | ~0 |
| **触发** | 每轮自动 | 自动压缩优先路径 | 自动/手动 | 压缩自身超限 |
| **prompt cache** | 保留缓存编辑 | 破坏 | 共享Fork | 破坏 |
| **最大输出** | | | 20K tokens | |
| **断路器** | | | 3次 | 3次 |
| **token 预算** | | 40K 最大保留 | 50K 文件重注入 | 按组裁剪 |
### 设计哲学总结
1. **渐进式降级**从无损操作微压缩到有损操作传统压缩到丢弃操作PTL Recovery每一层都比上一层代价更高
2. **缓存优先**微压缩的缓存编辑路径专门设计为不破坏 prompt cache传统压缩通过 Fork Agent 共享 cache
3. **安全性保证**
- 永远不切断 tool_use/tool_result 配对
- 永远不分离共享 message.id 的消息块
- 断路器防止失败时无限重试
- 递归保护防止压缩代理触发自身压缩
4. **可观测性**每个压缩操作都记录 `logEvent` `tengu_compact`, `tengu_cached_microcompact`包含压缩前后的 token 触发原因重试次数等
5. **可配置性**几乎所有阈值都支持环境变量覆盖用于测试 GrowthBook 远程配置用于动态调整
---
*来自AI超元域 | B站频道https://space.bilibili.com/3493277319825652*
*基于 Claude Code 源码逆向分析2026-03-31*

View File

@@ -0,0 +1,441 @@
# Claude Code 是否偷换模型?源码级逆向分析
> 基于 Claude Code 源码2026-03-31 快照512K 行 TypeScript逆向分析
> 核心文件:`src/utils/model/model.ts`、`src/services/api/withRetry.ts`、`src/query.ts`、`src/services/api/claude.ts`
---
## 结论先行
**不存在"偷换模型"。你的主对话模型完全由你控制,不会被偷偷降级。**
但 Claude Code 确实在后台使用 Haiku弱模型执行辅助任务配额检查、摘要生成等这些 Haiku 调用不会出现在你的对话中,也不会替代你与 Opus 的交互。
唯一可能改变主对话模型的场景是 **529 连续过载回退**Opus → Sonnet此时会显示明确的系统消息通知用户。
---
## 1. 主对话模型选择:完全由用户控制
### 模型选择优先级(源码确认)
```typescript
// src/utils/model/model.ts:92
function getMainLoopModel(): ModelName {
const model = getUserSpecifiedModelSetting()
if (model !== undefined && model !== null) {
return parseUserSpecifiedModel(model)
}
return getDefaultMainLoopModel()
}
// src/utils/model/model.ts:61
function getUserSpecifiedModelSetting(): ModelSetting | undefined {
// 优先级从高到低:
// 1. /model 命令(会话内切换)— 最高优先级
const modelOverride = getMainLoopModelOverride()
if (modelOverride !== undefined) return modelOverride
// 2. --model 启动参数 / ANTHROPIC_MODEL 环境变量 / settings.json
return process.env.ANTHROPIC_MODEL || settings.model || undefined
}
```
**你设了 Opus主对话就用 Opus。没有任何代码在你不知情的情况下把它换掉。**
### 默认模型
```typescript
// src/utils/model/model.ts:105
function getDefaultOpusModel(): ModelName {
// 支持环境变量覆盖
if (process.env.ANTHROPIC_DEFAULT_OPUS_MODEL) {
return process.env.ANTHROPIC_DEFAULT_OPUS_MODEL
}
return getModelStrings().opus46 // 默认 Opus 4.6
}
function getDefaultSonnetModel(): ModelName {
if (process.env.ANTHROPIC_DEFAULT_SONNET_MODEL) {
return process.env.ANTHROPIC_DEFAULT_SONNET_MODEL
}
// 3P 提供商默认 Sonnet 4.5(可能尚未支持 4.6
if (getAPIProvider() !== 'firstParty') return getModelStrings().sonnet45
return getModelStrings().sonnet46
}
function getDefaultHaikuModel(): ModelName {
if (process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL) {
return process.env.ANTHROPIC_DEFAULT_HAIKU_MODEL
}
return getModelStrings().haiku45 // Haiku 4.5(所有平台通用)
}
```
---
## 2. 唯一会改变主对话模型的场景529 过载回退
### 触发条件(极其严格)
```typescript
// src/services/api/withRetry.ts:326-350
// 必须同时满足以下所有条件:
// 1. 收到 529 错误(服务器过载)
// 2. 连续 529 次数 >= MAX_529_RETRIES
// 3. 配置了 fallbackModel
// 4. 以下二选一:
// a. 设置了 FALLBACK_FOR_ALL_PRIMARY_MODELS 环境变量
// b. 非 Claude.ai 订阅用户 且 使用非自定义 Opus 模型
if (is529Error(error) &&
(process.env.FALLBACK_FOR_ALL_PRIMARY_MODELS ||
(!isClaudeAISubscriber() && isNonCustomOpusModel(options.model)))) {
consecutive529Errors++
if (consecutive529Errors >= MAX_529_RETRIES) {
if (options.fallbackModel) {
// 触发回退
throw new FallbackTriggeredError(options.model, options.fallbackModel)
}
}
}
```
**关键限制:**
| 条件 | 说明 |
|------|------|
| `!isClaudeAISubscriber()` | **Claude.ai 订阅用户默认不触发回退** |
| `isNonCustomOpusModel()` | 仅对标准 Opus 模型生效(自定义模型 ID 不触发) |
| `MAX_529_RETRIES` | 需要连续多次 529 才触发 |
### 回退执行过程(透明可见)
```typescript
// src/query.ts:894-946
if (innerError instanceof FallbackTriggeredError && fallbackModel) {
// 1. 切换模型
currentModel = fallbackModel // Opus → Sonnet不是 Haiku
// 2. 清理孤立的 assistant 消息
yield* yieldMissingToolResultBlocks(assistantMessages, 'Model fallback triggered')
assistantMessages.length = 0
// 3. 剥离 thinking 签名块(签名绑定原模型,发给回退模型会导致 400
// 源码注释:
// "Strip before retry so the fallback model gets clean history."
// 4. 更新工具上下文中的模型引用
toolUseContext.options.mainLoopModel = fallbackModel
// 5. 记录分析事件
logEvent('tengu_api_opus_fallback_triggered', {
original_model: options.model,
fallback_model: options.fallbackModel,
})
// 6. 显示系统消息通知用户(在 UI 中可见)
`Switched to ${renderModelName(fallbackModel)} due to high demand
for ${renderModelName(originalModel)}`
}
```
### 回退目标
```typescript
// src/main.tsx:1336-1337
// 回退模型必须与主模型不同
if (fallbackModel && options.model && fallbackModel === options.model) {
// 验证失败,不设置回退
}
// 回退目标是 Sonnet不是 Haiku
// 通过 getDefaultSonnetModel() 获取
```
### 用户如何知道发生了回退
回退时UI 中会显示一条系统消息:
```
Switched to Claude Sonnet 4.6 due to high demand for Claude Opus 4.6
```
同时在 `AssistantTextMessage.tsx:178` 中提供操作建议:
```
To continue immediately, use /model to switch to Sonnet and continue coding.
```
---
## 3. 后台辅助任务使用 Haiku — 不影响主对话
Claude Code 在后台使用 Haiku`getSmallFastModel()`)执行多种轻量辅助任务。这些调用**完全独立于你的对话**,不会出现在消息流中。
### 所有 Haiku 使用场景(逐一列出)
#### 3.1 配额检查
```typescript
// src/services/claudeAiLimits.ts:199-218
async function makeTestQuery() {
const model = getSmallFastModel() // → Haiku
const anthropic = await getAnthropicClient({ maxRetries: 0, model, source: 'quota_check' })
// 发送 max_tokens: 1 的最小请求,仅读取响应头的配额信息
return anthropic.beta.messages.create({
model,
max_tokens: 1,
messages: [{ role: 'user', content: 'quota' }],
}).asResponse()
}
```
**用途:** 检测用户是否已达配额限制。只读取 HTTP 响应头,不使用模型输出。
#### 3.2 离开摘要Away Summary
```typescript
// src/services/awaySummary.ts:44-52
// 当用户长时间离开后返回时,生成一个简短摘要
{
thinkingConfig: { type: 'disabled' }, // 关闭思考
tools: [], // 不给工具
model: getSmallFastModel(), // → Haiku
}
```
**用途:** 用户长时间不操作后返回时,快速生成"你离开期间发生了什么"的简要摘要。
#### 3.3 Token 计数
```typescript
// src/services/api/claude.ts:540-543
// WARNING: if you change this to use a non-Haiku model,
// this request will fail in 1P unless it uses getCLISyspromptPrefix.
const model = getSmallFastModel() // → Haiku
// 调用 countTokens API 获取精确 token 数
```
**用途:** 调用 Anthropic 的 `countTokens` 端点获取精确 token 计数,用于自动压缩阈值判断。
#### 3.4 工具使用摘要
```typescript
// src/services/api/claude.ts:3273-3280
// 在主模型流式输出期间,并行用 Haiku 生成工具调用的简短描述
{
model: getSmallFastModel(), // → Haiku
enablePromptCaching: false,
}
```
**用途:** 当模型连续调用多个工具时,生成简短的工具使用描述(如"读取了 config.ts 并修改了第 42 行")。
源码注释明确说明了并行关系:
```typescript
// src/query.ts:1054
// Yield tool use summary from previous turn —
// haiku (~1s) resolved during model streaming (5-30s)
```
Haiku 在后台 1 秒内完成摘要,同时 Opus 还在处理你的主对话。
#### 3.5 记忆相关性评分
```typescript
// src/memdir/findRelevantMemories.ts
// 用 Sonnet非 Haiku评估记忆文件的相关性
// 从标题中选择最多 5 条与当前查询相关的记忆
```
#### 3.6 Web 搜索预处理
```typescript
// src/tools/WebSearchTool/WebSearchTool.ts:280
model: useHaiku ? getSmallFastModel() : context.options.mainLoopModel,
toolChoice: useHaiku ? { type: 'tool', name: 'web_search' } : undefined,
```
**用途:** 当条件满足时(`useHaiku` 标志),用 Haiku 预处理搜索查询。
#### 3.7 其他辅助调用
| 调用者 | 模型 | 用途 |
|--------|------|------|
| `tokenEstimation.ts` | Haiku | Vertex 全局区域 token 计数回退 |
| `analyzeContext.ts` | Haiku | 上下文分析的 countTokens 回退 |
| `claudeAiLimits.ts` | Haiku | 配额状态检查 |
| Bedrock `client.ts` | Haiku | 小模型可指定不同 AWS 区域 |
### 辅助任务 vs 主对话的隔离
```
你的对话:
用户 → [Opus 4.6] → 助手响应 → [Opus 4.6] → 助手响应 → ...
后台并行:
[Haiku] → 配额检查1 token
[Haiku] → 工具使用摘要(~1s
[Haiku] → token 计数
两条线完全隔离Haiku 的输出不进入你的对话消息流。
```
---
## 4. Fast Mode同模型加速不换模型
这是一个常见误解,源码中有明确声明:
```typescript
// src/constants/prompts.ts:702
`Fast mode for Claude Code uses the same ${FRONTIER_MODEL_NAME} model
with faster output. It does NOT switch to a different model.
It can be toggled with /fast.`
```
Fast Mode 通过 API 的 speed 参数请求更快的输出,模型不变。如果 API 拒绝 fast mode如组织未启用会自动降回标准速度
```typescript
// src/services/api/withRetry.ts:310-313
if (wasFastModeActive && isFastModeNotEnabledError(error)) {
handleFastModeRejectedByAPI()
retryContext.fastMode = false
continue // 重试,同一模型,标准速度
}
```
---
## 5. Plan Mode 临时模型切换 — 透明可见
```typescript
// src/commands/model/model.tsx:214
// Do not update fast mode in settings since this is an automatic downgrade
// src/commands/model/model.tsx:256
onDone(`Current model: ${chalk.bold(renderModelLabel(mainLoopModelForSession))}
(session override from plan mode)\nBase model: ${displayModel}`)
```
Plan Mode 可能临时使用 Sonnet 执行实现步骤,但:
- UI 显示 "session override from plan mode"
- 显示 Base model 让你知道原始模型
- 不修改 settings 中的模型设置
---
## 6. Effort 降级 — 不换模型,换参数
```typescript
// src/utils/effort.ts:162
// API rejects 'max' on non-Opus-4.6 models — downgrade to 'high'.
```
如果设置了 `effort: max` 但模型不支持,会降级 effort 参数max → high不换模型。
---
## 7. 全局搜索:不存在的场景
通过对 512K 行源码的全局搜索,以下场景**被确认不存在**
| 假设的偷换场景 | 搜索结果 | 证据 |
|----------------|----------|------|
| 根据订阅层级偷换弱模型 | **不存在** | `getMainLoopModel()` 不检查订阅类型 |
| 根据额度用量偷换弱模型 | **不存在** | 额度用完返回 429 错误,不换模型 |
| 随机/概率性降级到弱模型 | **不存在** | 无 `Math.random()` 与模型选择相关的代码 |
| A/B 测试使用弱模型回答 | **不存在** | GrowthBook 控制功能开关,不控制模型 |
| 静默替换后不通知用户 | **不存在** | 529 回退有系统消息,其他场景不换主模型 |
| 把 Opus 对话路由到 Haiku | **不存在** | Haiku 仅用于辅助任务 |
| 根据问题复杂度选择弱模型 | **不存在** | 主循环始终使用用户指定的模型 |
| 根据时间段降级(如高峰期) | **不存在** | 无时间相关的模型选择逻辑 |
| 首次用户用弱模型 | **不存在** | 默认模型是 Opus 4.6 |
---
## 8. 模型使用全景图
```
┌─────────────────────────────────────────────────────────┐
│ 你的主对话 │
│ │
│ 模型:你指定的(默认 Opus 4.6
│ 控制:/model 命令 > --model 参数 > 环境变量 > 设置 │
│ 唯一例外529 连续过载 → Sonnet 回退(有通知) │
│ │
├─────────────────────────────────────────────────────────┤
│ 后台辅助任务Haiku
│ │
│ 配额检查 → 1 token 请求,只读响应头 │
│ Token 计数 → countTokens API 调用 │
│ 工具摘要 → 并行于主对话,~1s 完成 │
│ 离开摘要 → 用户回来时显示简要信息 │
│ Web 搜索 → 搜索查询预处理(条件触发) │
│ │
│ 这些不进入你的对话消息流 │
│ │
├─────────────────────────────────────────────────────────┤
│ Fork Agent 任务 │
│ │
│ 上下文压缩 → 使用你的主模型(共享 prompt cache
│ 记忆提取 → 使用你的主模型 │
│ 权限分类器 → Haikuauto 模式下的命令安全分类) │
│ │
│ 压缩结果进入上下文,但不替代你的对话 │
└─────────────────────────────────────────────────────────┘
```
---
## 9. 如何验证你正在使用的模型
### 方法 1查看 API 响应
每条助手消息的内部结构中包含模型信息:
```typescript
// AssistantMessage.message.model 字段包含实际使用的模型 ID
// 例如:"claude-opus-4-6-20260301"
```
### 方法 2使用 /cost 命令
`/cost` 命令显示按模型分类的 token 使用量,你可以看到每个模型消耗了多少 token。
### 方法 3检查 status line
状态栏显示当前模型名称。如果发生回退,会更新为回退后的模型。
### 方法 4环境变量调试
```bash
# 查看所有 API 调用的模型信息
export CLAUDE_CODE_DEBUG=1
# 强制禁用回退
unset FALLBACK_FOR_ALL_PRIMARY_MODELS
```
---
## 10. 总结
| 问题 | 答案 | 依据 |
|------|------|------|
| Opus 会被偷偷换成 Haiku 吗? | **不会** | Haiku 仅用于辅助任务,不进入主对话 |
| Opus 会被偷偷换成 Sonnet 吗? | **仅在 529 连续过载时,且会通知** | `withRetry.ts:347` + `query.ts:946` |
| Claude.ai 订阅用户会被降级吗? | **默认不会** | `!isClaudeAISubscriber()` 条件排除 |
| 后台有 Haiku 调用吗? | **有,但不影响你的对话** | 6 种辅助任务,全部隔离 |
| Fast Mode 换模型吗? | **不换** | 源码明确声明 "does NOT switch to a different model" |
| 有没有根据问题难度选模型? | **没有** | 主循环始终使用用户指定模型 |
**一句话:你付费用 Opus对话就用 Opus。Haiku 只在幕后干杂活。**
---
*来自AI超元域 | B站频道https://space.bilibili.com/3493277319825652*
*基于 Claude Code 源码逆向分析2026-03-31*

View File

@@ -0,0 +1,764 @@
# Claude Code Memory 记忆系统深度分析
> 基于 Claude Code 源码2026-03-31 快照512K 行 TypeScript逆向分析
> 核心文件:`src/memdir/`8个文件、`src/services/extractMemories/`2个、`src/services/SessionMemory/`3个、`src/services/teamMemorySync/`5个
---
## 1. 三层记忆架构总览
Claude Code 的记忆系统不是一个简单的文件存储,而是一个**三层递进的知识管理体系**
```
┌─────────────────────────────────────────────────────────────┐
│ 第3层团队记忆Team Memory
│ 跨用户、按仓库 │ 服务端同步 │ REST API + 乐观锁 │
│ 秘密扫描保护 │ 文件监听器 │ 30种凭证检测规则 │
├─────────────────────────────────────────────────────────────┤
│ 第2层持久记忆Persistent Memory / extractMemories
│ 跨会话、按项目 │ 本地文件 │ 后台 Fork Agent 自动提取 │
│ 4种类型分类 │ MEMORY.md 索引 │ AI 相关性召回 │
├─────────────────────────────────────────────────────────────┤
│ 第1层会话记忆Session Memory
│ 仅当前会话 │ 单个文件 │ 10个固定章节 │ 服务于压缩 │
└─────────────────────────────────────────────────────────────┘
```
| 层级 | 范围 | 持久性 | 触发方式 | 存储位置 |
|------|------|--------|----------|----------|
| 会话记忆 | 当前会话 | 临时(会话结束即停用) | 后采样钩子token+工具调用阈值) | 会话目录下单文件 |
| 持久记忆 | 跨会话、按项目 | 永久(直到手动删除) | 每轮查询结束时Stop Hook | `~/.claude/projects/<项目>/memory/` |
| 团队记忆 | 跨用户、按仓库 | 远程服务器 + 本地镜像 | 文件监听器2秒去抖 | `memory/team/` + Anthropic API |
---
## 2. 持久记忆系统(核心)
### 2.1 记忆文件格式
每条记忆是一个独立的 Markdown 文件,带 YAML frontmatter
```markdown
---
name: 用户偏好-简洁回复
description: 用户不希望在每次回复末尾加总结
type: feedback
---
不要在回复末尾总结刚做了什么,用户能看到 diff。
**Why:** 用户明确要求过"stop summarizing what you just did"
**How to apply:** 所有回复结束时,直接结束,不加回顾性总结。
```
### 2.2 四种记忆类型
```typescript
const MEMORY_TYPES = ['user', 'feedback', 'project', 'reference'] as const
```
| 类型 | 用途 | 保存时机 | 作用域(团队模式) |
|------|------|----------|:------------------:|
| **user** | 用户角色、目标、知识背景 | 了解到用户的身份信息时 | 始终私有 |
| **feedback** | 用户对工作方式的指导 | 用户纠正或确认做法时 | 默认私有 |
| **project** | 项目进展、目标、事件 | 了解到工作计划、截止日期时 | 偏向团队 |
| **reference** | 外部系统指针 | 了解到 Linear/Grafana/Slack 等资源时 | 通常团队 |
**每种类型要求的结构:**
- feedback 和 project 类型必须包含 `**Why:**``**How to apply:**`
- 相对日期必须转换为绝对日期(如"周四"→"2026-03-05"
- user 类型的示例:"深度 Go 经验React 新手——用后端类比解释前端概念"
### 2.3 明确不保存的内容
源码中硬编码了 6 类排除项(即使用户明确要求也不保存):
1. 代码模式、架构、文件路径、项目结构——可从代码推导
2. Git 历史、最近变更——`git log`/`git blame` 是权威来源
3. 调试方案或修复步骤——修复在代码中commit message 有上下文
4. CLAUDE.md 中已有的内容——不重复
5. 临时任务细节:进行中的工作、当前对话上下文
6. 如果用户要求保存 PR 列表或活动摘要,要追问"什么是令人意外的或不明显的?"——只保存那部分
### 2.4 MEMORY.md 索引机制
```
MEMORY.md 是索引,不是记忆本身
```
**格式:** 纯 Markdown无 frontmatter。每条一行约 150 字符以内:
```markdown
- [用户偏好-简洁回复](feedback_concise.md) — 不在回复末尾加总结
- [项目-合并冻结](project_freeze.md) — 3月5日起移动端发布冻结
```
**限制:**
- 最大 200 行(`MAX_ENTRYPOINT_LINES`
- 最大 25,000 字节(`MAX_ENTRYPOINT_BYTES`
- 超出时在末尾追加截断警告
- 截断按行边界执行(不会切断一行的中间)
**两步保存流程:**
1. 写入记忆文件(如 `feedback_concise.md`
2. 在 MEMORY.md 中添加索引行
可选的 `skipIndex` 模式(通过 `tengu_moth_copse` 特性开关可跳过第2步。
### 2.5 存储目录结构
```
~/.claude/
projects/
-Users-charlesqin-Desktop-myproject/ ← sanitizePath(项目根目录)
memory/ ← AUTO_MEM_DIRNAME
MEMORY.md ← 索引文件
user_role.md ← 私有记忆文件
feedback_testing.md
project_deadline.md
reference_linear.md
team/ ← 团队记忆子目录
MEMORY.md ← 团队索引
project_api_migration.md
reference_oncall_board.md
logs/ ← KAIROS 每日日志
2026/
03/
2026-03-31.md
```
### 2.6 路径安全机制
`paths.ts` 包含多层安全验证:
| 安全层 | 检查内容 | 防御目标 |
|--------|----------|----------|
| 绝对路径检查 | 拒绝相对路径 | 路径遍历 |
| 根路径检查 | 拒绝长度 < 3 的路径 | 写入系统根目录 |
| UNC 路径检查 | 拒绝 `\\server\share` `//server/share` | NTLM 凭证泄露 |
| Null 字节检查 | 拒绝包含 `\0` 的路径 | 路径截断攻击 |
| Tilde 展开限制 | 拒绝 `~``~/``~/.``~/..` | 匹配整个 HOME |
| **项目设置排除** | `.claude/settings.json` 不能设 `autoMemoryDirectory` | **恶意仓库重定向写入到 `~/.ssh`** |
| NFC 规范化 | Unicode NFC 标准化 | macOS 路径不一致 |
| Git 根规范化 | worktree 共享主仓库的记忆 | 避免重复记忆目录 |
**关键安全设计:** `projectSettings`提交到仓库的 `.claude/settings.json`被有意排除在 `autoMemoryDirectory` 的信任来源之外源码注释明确说明原因
```typescript
// SECURITY: projectSettings is intentionally excluded —
// a malicious repo could set autoMemoryDirectory: "~/.ssh"
// and gain write access to sensitive directories
```
---
## 3. 自动记忆提取extractMemories
### 3.1 触发时机
```
用户输入 → 模型响应 → 工具执行 → 循环... → 模型最终响应(无工具调用)
Stop Hooks 执行
executeExtractMemories() ← 即发即忘
```
**前置条件(全部满足才执行):**
1. `EXTRACT_MEMORIES` 编译时特性开关开启
2. `isExtractModeActive()` = true`tengu_passport_quail` GrowthBook 开关
3. `isAutoMemoryEnabled()` = true
4. 非子代理`!toolUseContext.agentId`
5. bare 模式
6. 自上次提取以来有新的模型可见消息
### 3.2 互斥机制
```typescript
// 如果主代理在对话中已经直接写了记忆文件,跳过自动提取
if (hasMemoryWritesSince(lastMemoryMessageUuid)) {
// 推进游标但不执行提取——避免覆盖用户显式保存的记忆
lastMemoryMessageUuid = messages[messages.length - 1].uuid
return
}
```
这防止了自动提取和用户手动保存之间的冲突
### 3.3 Fork Agent 执行
```typescript
const result = await runForkedAgent({
messages: conversationMessages,
system: extractionPrompt,
maxTurns: 5, // 硬上限5轮读1轮 + 写1-4轮
canUseTool: createAutoMemCanUseTool(autoMemPath),
// ... 共享主会话的 prompt cache
})
```
**工具权限白名单:**
| 工具 | 权限 |
|------|------|
| FileRead | 允许无限制 |
| Grep | 允许无限制 |
| Glob | 允许无限制 |
| Bash | 仅只读命令ls, find, grep, cat, stat, wc, head, tail |
| FileEdit | 仅限 `autoMemPath` |
| FileWrite | 仅限 `autoMemPath` |
| MCP / Agent / 写入型 Bash | **全部禁止** |
### 3.4 提取节流
```typescript
let turnsSinceLastExtraction = 0
const turnsBeforeExtraction = getFeatureValue('tengu_bramble_lintel', 1)
// 每 N 轮查询才执行一次提取(默认每轮都执行)
if (turnsSinceLastExtraction < turnsBeforeExtraction) {
turnsSinceLastExtraction++
return
}
```
### 3.5 提取后通知
成功提取后将写入的文件路径列表作为 `SystemMemorySavedMessage` 追加到主对话中这样主代理知道记忆已更新
---
## 4. AI 驱动的记忆召回findRelevantMemories
### 4.1 召回流程
```
用户发送消息
scanMemoryFiles(memoryDir)
↓ 递归读取所有 .md 文件(排除 MEMORY.md
↓ 解析前 30 行 frontmatterdescription + type
↓ 按 mtime 降序排序,上限 200 个文件
过滤掉 alreadySurfaced之前轮次已展示的路径
selectRelevantMemories(query, memories, signal, recentTools)
↓ 构建文本清单:每行 "- [type] filename (timestamp): description"
↓ 附加 "Recently used tools: ..." 部分
sideQuery → Sonnet 模型
↓ 系统提示SELECT_MEMORIES_SYSTEM_PROMPT
↓ 用户消息Query + Available memories
↓ 结构化输出:{ selected_memories: string[] }
↓ max_tokens: 256
返回最多 5 条 RelevantMemory路径 + mtime
```
### 4.2 选择提示词的关键规则
```
- 返回最多 5 个文件名
- 要有选择性和辨别力;如果不确定,不要包含
- 如果提供了最近使用的工具列表:
✗ 不要选择这些工具的使用参考/API 文档(已经在用了)
✓ 仍然选择这些工具的警告/陷阱/已知问题
```
### 4.3 记忆新鲜度标注
```typescript
function memoryAge(mtimeMs: number): string {
const days = memoryAgeDays(mtimeMs)
if (days === 0) return 'today'
if (days === 1) return 'yesterday'
return `${days} days ago`
}
function memoryFreshnessText(mtimeMs: number): string {
if (memoryAgeDays(mtimeMs) <= 1) return '' // 新鲜记忆不加警告
return `This memory is ${days} days old.
Memories are point-in-time observations, not live state —
claims about code behavior or file:line citations may be outdated.
Verify against current code before asserting as fact.`
}
```
**设计动机(源码注释):** 用户报告过过期的记忆被模型当作事实断言。"47 days ago" ISO 时间戳更能触发模型的过期推理
### 4.4 召回前的验证要求
源码中注入的系统提示明确要求模型在使用记忆前验证
```
- 如果记忆提到一个文件路径 → 检查文件是否存在
- 如果记忆提到一个函数或标志 → grep 搜索它
- 如果用户即将根据你的建议行动 → 先验证
- "记忆说 X 存在"不等于"X 现在存在"
- 活动日志、架构快照是冻结在某个时间点的,优先使用 git log
```
---
## 5. 会话记忆Session Memory
### 5.1 与持久记忆的关键区别
| 维度 | 会话记忆 | 持久记忆 |
|------|----------|----------|
| 生命周期 | 当前会话 | 跨会话永久 |
| 文件数量 | 1 | 多个 |
| 触发频率 | 每次后采样需满足阈值 | 每轮查询结束 |
| 主要用途 | **服务于上下文压缩** | 跨会话知识保留 |
| 记忆提取者 | Fork Agent FileEdit | Fork AgentRead/Write/Edit/Grep |
| 格式 | 10个固定章节 | 自由格式 + frontmatter |
### 5.2 触发阈值
```typescript
// 默认配置(可通过 GrowthBook tengu_sm_config 远程调整)
const defaults = {
minimumMessageTokensToInit: 10_000, // 上下文达到 10K tokens 才激活
minimumTokensBetweenUpdate: 5_000, // 每增长 5K tokens 更新一次
toolCallsBetweenUpdates: 3, // 且至少 3 次工具调用
}
// 触发条件:
// (token 阈值满足 AND 工具调用阈值满足)
// OR (token 阈值满足 AND 最后一轮助手消息无工具调用)
```
### 5.3 会话记忆模板10 个固定章节)
```markdown
# Session Memory
## Session Title
_Brief description of what this session is about_
## Current State
_What is the current status of the work_
## Task specification
_Detailed description of the current task requirements_
## Files and Functions
_Key files and functions being worked on_
## Workflow
_Steps being followed or processes in use_
## Errors & Corrections
_Errors encountered and how they were resolved_
## Codebase and System Documentation
_Important codebase patterns, conventions, and system behavior_
## Learnings
_Insights gained during this session_
## Key results
_Important outputs, measurements, or achievements_
## Worklog
_Chronological log of actions taken_
```
### 5.4 大小限制
```typescript
const MAX_SECTION_LENGTH = 2_000 // 每个章节最大 2K tokens
const MAX_TOTAL_SESSION_MEMORY_TOKENS = 12_000 // 整个文件最大 12K tokens
```
### 5.5 与压缩的集成
Session Memory 是上下文压缩的优先路径参见 `sessionMemoryCompact.ts`
- 自动压缩触发时优先尝试用 Session Memory 作为摘要
- 保留最近 10K-40K tokens 的消息
- 比传统压缩快得多不调用 LLM且摘要质量更可预测
### 5.6 自定义
用户可以自定义模板和提示词
- 模板`~/.claude/session-memory/config/template.md`
- 提示词`~/.claude/session-memory/config/prompt.md`支持 `{{variableName}}` 替换
---
## 6. 团队记忆同步
### 6.1 前置条件
同时满足才启用
1. `TEAMMEM` 编译时特性开关开启
2. `isTeamMemoryEnabled()` = true`tengu_herring_clock` GrowthBook 开关
3. 第一方 OAuth + 同时拥有 `INFERENCE_SCOPE` `PROFILE_SCOPE`
4. Git remote github.com GitHub 仓库不同步
### 6.2 API 端点
```
基础 URL: {baseUrl}/api/claude_code/team_memory?repo={owner/repo}
```
| 方法 | 参数 | 用途 | 关键状态码 |
|------|------|------|:----------:|
| GET | `repo={slug}` | 拉取全部团队记忆 | 200 / 304 / 404 |
| GET | `repo={slug}&view=hashes` | 仅拉取哈希轻量探测 | 200 / 404 |
| PUT | `repo={slug}` | 上传记忆条目upsert | 200 / 412 / 413 |
**认证头:** `Authorization: Bearer {oauthToken}`
### 6.3 同步协议
#### 拉取Pull
```
1. GET 请求,携带 ETag条件请求
2. 304 = 未变化,跳过
3. 200 = 有更新:
a. 对每个条目验证路径(防遍历攻击)
b. 跳过 >250KB 的文件
c. 跳过本地内容已匹配的文件
d. 并行写入本地文件系统
e. 刷新 serverChecksums
```
#### 推送Push — Delta 上传 + 乐观锁)
```
1. readLocalTeamMemory(): 遍历 team/ 目录
→ 每个文件扫描秘密30种规则
→ 跳过 >250KB 的文件
→ 计算 SHA-256 哈希
2. 计算 delta仅本地哈希与 serverChecksums 不同的 key
3. 分批上传:贪心装箱,每批 ≤ 200KB (MAX_PUT_BODY_BYTES)
4. 每批携带 If-Match 头(乐观锁)
5. 冲突处理:
412 Conflict → 探测 ?view=hashes → 刷新 serverChecksums → 重算 delta → 重试(最多 2 次)
413 Too Many → 学习 serverMaxEntries 上限,截断后重试
6. 冲突策略本地优先local-wins
→ 当前用户正在编辑的内容覆盖服务端
```
### 6.4 文件监听器
```typescript
// watcher.ts
fs.watch(teamMemDir, { recursive: true })
// macOS: FSEventsO(1) 文件描述符)
// Linux: inotifyO(子目录数)
// 2 秒去抖:最后一次变更后 2 秒才触发推送
```
**推送抑制:** 永久失败no_oauthno_repo4xx 409/429抑制后续推送直到发生文件删除恢复动作或会话重启
### 6.5 团队 vs 私有的作用域划分
源码注入的系统提示中每种记忆类型都带有 XML 作用域标签
```xml
<type>
<name>user</name>
<scope>always private</scope>
...
</type>
<type>
<name>project</name>
<scope>strongly bias toward team</scope>
...
</type>
```
额外安全要求
```
MUST avoid saving sensitive data within shared team memories
```
---
## 7. 秘密扫描保护
### 7.1 双层防护
| | 时机 | 文件 | 行为 |
|----|------|------|------|
| 写入时拦截 | FileWrite/FileEdit 调用 | `teamMemSecretGuard.ts` | 阻止写入返回错误 |
| 上传前扫描 | pushTeamMemory 读取本地文件 | `secretScanner.ts` | 跳过该文件不上传 |
### 7.2 检测的 30 种秘密模式
**云服务商:**
- AWS Access TokenA3T/AKIA/ASIA/ABIA/ACCA 前缀
- GCP API KeyAIza 前缀
- Azure AD Client Secret
- DigitalOcean PAT / Access Token
**AI API**
- Anthropic API Key`sk-ant-api03-` 前缀运行时拼接避免自身匹配
- Anthropic Admin API Key`sk-ant-admin01-`
- OpenAI API Key`sk-proj`/`svcacct`/`admin` + `T3BlbkFJ` 标记
- HuggingFace Access Token`hf_`
**版本控制:**
- GitHub PAT / Fine-grained PAT / App Token / OAuth / Refresh Token
- GitLab PAT / Deploy Token
**通信平台:**
- Slack Bot/User/App Token`xoxb-`/`xoxp-`/`xapp-`
- Twilio API Key
- SendGrid API Token
**开发工具:**
- npm Access Token
- PyPI Upload Token
- Databricks API Token
- HashiCorp Terraform API Token
- Pulumi API Token
- Postman API Token
**可观测性:**
- Grafana API Key / Cloud API Token / Service Account Token
- Sentry User/Org Token
**支付:**
- Stripe Access Token`sk_test`/`live`/`prod` `rk_`
- Shopify Access Token / Shared Secret
**密码学:**
- PEM 格式私钥`BEGIN/END PRIVATE KEY`
### 7.3 设计原则
```typescript
// 匹配到的秘密值永远不被记录或返回——只返回规则 ID 和人类可读标签
// Anthropic API key 前缀在运行时拼接,避免匹配自身的 excluded-strings 检查:
const prefix = ['sk', 'ant', 'api'].join('-')
```
---
## 8. KAIROS 模式的记忆(每日日志)
KAIROSAI 助手模式激活时记忆范式完全改变
```
普通模式:写独立文件 + 更新 MEMORY.md 索引
KAIROS 模式:追加到 logs/YYYY/MM/YYYY-MM-DD.md 日志文件
```
- 只追加不编辑已有内容
- 每日一个文件按日期路径组织
- 由单独的 `/dream` 技能在夜间蒸馏日志为主题文件和 MEMORY.md
- 提示词中使用 `YYYY-MM-DD` 占位符而非实际日期因为提示词被缓存日期变化不会触发失效
---
## 9. 记忆系统注入的完整系统提示词
### 9.1 个人模式auto memory
```
# auto memory
You have a persistent, file-based memory system at `<memoryDir>`.
This directory already exists — write to it directly with the Write tool.
You should build up this memory system over time so that future conversations
can have a complete picture of who the user is, how they'd like to collaborate
with you, what behaviors to avoid or repeat, and the context behind the work.
[如果用户要求记住 → 立即保存]
[如果用户要求忘记 → 找到并删除]
## Types of memory
[4种类型每种含 description/when_to_save/how_to_use/examples]
## What NOT to save in memory
[6类排除项]
## How to save memories
Step 1: 写入文件(带 frontmatter
Step 2: 在 MEMORY.md 中添加索引行
## When to access memories
- 看起来相关时
- 用户明确要求时(必须访问)
- 用户说"忽略记忆"时,假装 MEMORY.md 为空
## Before recommending from memory
- 提到文件路径 → 检查文件是否存在
- 提到函数/标志 → grep 搜索
- 用户要行动 → 先验证
## Memory and other forms of persistence
- 实施方案 → 用 Plan不用记忆
- 当前对话步骤 → 用 Tasks不用记忆
## Searching past context
[grep 记忆文件,或搜索会话 transcript]
## MEMORY.md
[实际的 MEMORY.md 内容,或"当前为空"]
```
### 9.2 团队模式额外内容
在个人模式基础上增加
```
## Memory scope
- 私有记忆:<privateDir> — 你的个人偏好、反馈
- 团队记忆:<teamDir> — 项目上下文、外部引用
[类型定义中增加 <scope> XML 标签]
额外安全规则:不得在团队记忆中保存敏感数据
```
---
## 10. 团队记忆的路径安全(深度防御)
`teamMemPaths.ts` 是整个记忆系统中安全措施最密集的文件
### 10.1 自定义错误类
```typescript
class PathTraversalError extends Error {
name = 'PathTraversalError'
}
```
### 10.2 sanitizePathKey — 服务端提供的相对路径清理
```typescript
function sanitizePathKey(key: string): string {
// 1. 拒绝 null 字节
// 2. URL 编码遍历检测:解码 %2e%2e%2f 等
// 3. Unicode 规范化攻击:全角字符 \uFF0E\uFF0E\uFF0F → ../
// 4. 拒绝反斜杠Windows 路径遍历)
// 5. 拒绝绝对路径
}
```
### 10.3 realpathDeepestExisting — 符号链接解析
```typescript
async function realpathDeepestExisting(path: string): Promise<string> {
// 逐级向上 realpath() 直到成功
// 检测悬挂符号链接link 存在但 target 不存在)
// → 安全威胁writeFile 会跟随链接在 team 目录外创建文件
// 检测符号链接循环ELOOP→ PathTraversalError
// 不可恢复的错误EACCES, EIO→ fail-closed
}
```
### 10.4 validateTeamMemWritePath — 双重验证
```
写入团队记忆文件前的完整验证链:
第1遍字符串级
1. Null 字节检查
2. path.resolve() 解析
3. startsWith(teamDir) 验证
第2遍符号链接级
4. realpathDeepestExisting() 解析实际路径
5. isRealPathWithinTeamDir() 验证实际路径在团队目录内
通过 → 返回解析后的路径
失败 → 抛出 PathTraversalError
```
### 10.5 前缀攻击防护
```typescript
// 团队目录路径以分隔符结尾:/foo/team/
// 这样 /foo/team-evil/ 不会通过 startsWith 检查
```
---
## 11. 记忆的生命周期全景
```
会话开始
├─ loadMemoryPrompt()
│ → 读取 MEMORY.md → 注入系统提示
├─ initSessionMemory()
│ → 注册后采样钩子
├─ startTeamMemoryWatcher()
│ → 拉取服务端 → 启动文件监听
每轮对话
├─ findRelevantMemories(query)
│ → 扫描文件 → Sonnet 选择 → 返回最多 5 条
│ → 新鲜度标注(>1天 → 加过期警告)
├─ [模型可能直接读/写记忆文件]
│ → 写入团队记忆 → checkTeamMemSecrets() 拦截秘密
│ → 文件监听触发 → 2秒后推送到服务端
├─ Session Memory 后采样钩子
│ → 检查阈值 → Fork Agent 更新单文件
├─ 模型最终响应(无工具调用)
│ → Stop Hooks → executeExtractMemories()
│ → 互斥检查 → Fork Agent 提取 → 更新持久记忆文件
自动压缩触发时
├─ trySessionMemoryCompaction()(优先路径)
│ → 用 Session Memory 内容作为摘要
│ → 保留最近 10K-40K tokens 的消息
├─ compactConversation()(回退路径)
│ → Fork Agent 生成 9 章节结构化摘要
会话结束
├─ drainPendingExtraction()
│ → 等待进行中的记忆提取完成
└─ 团队记忆推送最后一批变更
```
---
## 12. 关键设计决策总结
| 决策 | 原因 |
|------|------|
| 记忆文件是独立的 .md 文件不是数据库 | 用户可以直接编辑Git 追踪跨工具使用 |
| MEMORY.md 是索引不是记忆 | 防止单文件膨胀支持截断不丢核心内容 |
| 自动提取在 Stop Hook 中即发即忘 | 不阻塞主对话循环 |
| 互斥检查主代理 vs 自动提取 | 防止覆盖用户显式保存的内容 |
| 召回用 Sonnet 不用 Haiku | 需要理解语义相关性Haiku 不够准确 |
| 新鲜度用"X days ago"不用 ISO 时间 | 模型对相对时间的推理比绝对时间好 |
| 项目设置排除 autoMemoryDirectory | 防止恶意仓库劫持写入路径 |
| 团队记忆双重路径验证 | 符号链接攻击需要在文件系统层面检查 |
| 秘密模式在运行时拼接 | 避免自身的 excluded-strings 检查 |
| 本地优先的冲突策略 | 用户正在编辑 = 最新意图 |
| 断路器MAX_CONFLICT_RETRIES = 2 | 防止推送冲突的无限重试 |
---
*来自AI超元域 | B站频道https://space.bilibili.com/3493277319825652*
*基于 Claude Code 源码逆向分析2026-03-31*

View File

@@ -0,0 +1,805 @@
# Claude Code 隐藏高级功能全景图
> 基于 Claude Code 源码2026-03-31 快照512K 行 TypeScript逆向分析
> 发现 55+ 个特性开关、20+ 个隐藏命令、以及多个未公开系统
---
## 目录
1. [你现在就能用的隐藏功能](#1-你现在就能用的隐藏功能)
2. [55+ 个编译时特性开关完整清单](#2-55-个编译时特性开关完整清单)
3. [KAIROS — AI 助手守护进程](#3-kairos--ai-助手守护进程)
4. [CHICAGO_MCP — Computer Use 电脑控制](#4-chicago_mcp--computer-use-电脑控制)
5. [投机执行系统Speculation](#5-投机执行系统speculation)
6. [Undercover 隐身模式](#6-undercover-隐身模式)
7. [Dream Mode 记忆梦境整合](#7-dream-mode-记忆梦境整合)
8. [Voice Mode 语音输入](#8-voice-mode-语音输入)
9. [Proactive 自主代理模式](#9-proactive-自主代理模式)
10. [伴侣精灵系统Buddy](#10-伴侣精灵系统buddy)
11. [多代理团队/Swarm 系统](#11-多代理团队swarm-系统)
12. [Anthropic 内部专用命令](#12-anthropic-内部专用命令)
13. [隐藏键盘快捷键](#13-隐藏键盘快捷键)
14. [隐藏环境变量](#14-隐藏环境变量)
15. [其他彩蛋与冷知识](#15-其他彩蛋与冷知识)
---
## 1. 你现在就能用的隐藏功能
这些功能在当前公开版本中存在,但很少被提及:
| 功能 | 触发方式 | 说明 |
|------|----------|------|
| 实体贴纸 | `/stickers` | 打开 Claude Code 实体贴纸购买页 |
| 堆转储 | `/heapdump` | 把 JS 堆转储到 `~/Desktop`(隐藏命令,不在 /help 中) |
| 裸模式 | `--bare``CLAUDE_CODE_SIMPLE=1` | 极简模式:只保留 Bash、Read、Edit 三个工具 |
| 自定义加载词 | `settings.json``spinnerVerbs` | 替换或追加 200+ 个花式加载词("Clauding"、"Flibbertigibbeting"…) |
| 输出风格切换 | `/config` → output style | **Explanatory**(教学模式,附 Insight 解说)或 **Learning**(动手练习,含 TODO(human) 标记) |
| Vim 模式 | `/vim` | 完整状态机d/c/y 操作符、h/l/w/b/e/$ 移动、f/t 查找、文本对象、dot-repeat |
| 消息动作选择器 | `shift+up` | 进入消息导航j/k 上下移动c 复制消息p 固定消息 |
| 年度回顾 | `/think-back` | "Your 2025 Claude Code Year in Review" 动画回顾 |
| 转储系统提示 | `--dump-system-prompt` | 打印完整系统提示词后退出(需特性开关) |
| 多代理团队 | `--agent-teams``CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` | 多 Claude 实例在 tmux/iTerm 分屏中并行工作 |
| 后台任务 | `ctrl+b` | 把当前运行中的任务转入后台 |
| 历史搜索 | `ctrl+r` | 反向搜索命令历史 |
| Transcript 模式 | `ctrl+o` | 查看完整对话记录,支持搜索 |
| Todo 面板 | `ctrl+t` | 任务追踪面板 |
| 速率限制选项 | `/rate-limit-options` | 隐藏命令,速率限制时显示选项 |
| 使用洞察 | `/insights` | 分析你的 Claude Code 使用历史113KB 延迟加载模块) |
| 全局文件搜索 | `ctrl+shift+f` | 跨文件搜索(需 QUICK_SEARCH 开关) |
| 快速打开 | `ctrl+shift+p` | 快速打开文件 |
---
## 2. 55+ 个编译时特性开关完整清单
所有开关通过 `feature('FLAG')` 在 Bun 编译时评估。外部构建中为 `false` 的分支会被彻底删除(死代码消除)。
### 核心功能开关
| 开关 | 功能 | 状态 |
|------|------|:----:|
| `VOICE_MODE` | 语音输入push-to-talk | 内部 |
| `KAIROS` | AI 助手守护进程模式 | 内部 |
| `KAIROS_DREAM` | 梦境记忆整合 | 内部 |
| `KAIROS_BRIEF` | Brief 摘要工具 | 内部 |
| `KAIROS_CHANNELS` | 频道通知系统 | 内部 |
| `KAIROS_PUSH_NOTIFICATION` | 推送通知工具 | 内部 |
| `KAIROS_GITHUB_WEBHOOKS` | GitHub PR 订阅 | 内部 |
| `CHICAGO_MCP` | Computer Use 电脑控制 | 付费用户 |
| `PROACTIVE` | 自主代理模式Sleep + 自动唤醒) | 内部 |
| `BRIDGE_MODE` | Remote Control 桥接 | 公开 |
| `DAEMON` | 后台守护进程 | 内部 |
### 上下文管理开关
| 开关 | 功能 |
|------|------|
| `CONTEXT_COLLAPSE` | 智能上下文折叠(替代暴力压缩) |
| `REACTIVE_COMPACT` | 响应式压缩API 报 prompt-too-long 时触发) |
| `CACHED_MICROCOMPACT` | 缓存感知微压缩(不破坏 prompt cache |
| `HISTORY_SNIP` | 历史裁剪SnipTool + /force-snip |
| `TOKEN_BUDGET` | Token 预算追踪和递减收益检测 |
| `EXTRACT_MEMORIES` | 自动记忆提取 |
| `TEAMMEM` | 团队记忆同步 |
### 工具和代理开关
| 开关 | 功能 |
|------|------|
| `COORDINATOR_MODE` | 多代理协调者模式 |
| `AGENT_TRIGGERS` | 定时触发器Cron |
| `AGENT_TRIGGERS_REMOTE` | 远程触发器 |
| `MONITOR_TOOL` | 监控工具和任务 |
| `WEB_BROWSER_TOOL` | 网页浏览器自动化工具 |
| `TERMINAL_PANEL` | 终端面板meta+j |
| `WORKFLOW_SCRIPTS` | 工作流脚本系统 |
| `BG_SESSIONS` | 后台会话claude ps/logs/attach/kill |
| `OVERFLOW_TEST_TOOL` | 上下文溢出测试工具 |
| `BUDDY` | 伴侣精灵系统 |
### UI 和体验开关
| 开关 | 功能 |
|------|------|
| `MESSAGE_ACTIONS` | 消息动作选择器shift+up |
| `QUICK_SEARCH` | 全局搜索和快速打开 |
| `AUTO_THEME` | 自动主题检测 |
| `STREAMLINED_OUTPUT` | 流式 JSON 模式优化输出 |
| `CONNECTOR_TEXT` | 连接器文本摘要 beta |
### 安全和调试开关
| 开关 | 功能 |
|------|------|
| `BASH_CLASSIFIER` | Bash 命令 ML 安全分类器 |
| `TRANSCRIPT_CLASSIFIER` | 基于 transcript 的权限模式分类 |
| `NATIVE_CLIENT_ATTESTATION` | 客户端证明头cch= |
| `HARD_FAIL` | 硬失败模式(任何错误都崩溃) |
| `DUMP_SYSTEM_PROMPT` | --dump-system-prompt 隐藏 CLI 标志 |
| `ABLATION_BASELINE` | A/B 测试消融基线 |
| `PROMPT_CACHE_BREAK_DETECTION` | Prompt 缓存失效检测 |
### 基础设施开关
| 开关 | 功能 |
|------|------|
| `LODESTONE` | `cc://` 深度链接协议注册 |
| `DIRECT_CONNECT` | 通过 cc:// 深度链接直连 |
| `SSH_REMOTE` | SSH 远程会话 |
| `BYOC_ENVIRONMENT_RUNNER` | 自带计算环境运行器 |
| `SELF_HOSTED_RUNNER` | 自托管运行器 |
| `CCR_REMOTE_SETUP` | /remote-setup 命令 |
| `FORK_SUBAGENT` | /fork 子代理分叉命令 |
| `MCP_SKILLS` | MCP 提供的技能 |
| `EXPERIMENTAL_SKILL_SEARCH` | 实验性技能模糊搜索 |
| `REVIEW_ARTIFACT` | 审查工件工具 |
| `BUILDING_CLAUDE_APPS` | /claude-api 技能 |
| `RUN_SKILL_GENERATOR` | 技能生成器 |
| `ULTRAPLAN` | 超级计划模式 |
| `TORCH` | /torch 命令 |
| `TEMPLATES` | 任务分类器/模板系统 |
| `VERIFICATION_AGENT` | 验证代理 |
| `COMMIT_ATTRIBUTION` | 提交归属Co-Authored-By |
| `AWAY_SUMMARY` | 离开摘要生成 |
| `FILE_PERSISTENCE` | 跨轮次文件持久化追踪 |
| `MEMORY_SHAPE_TELEMETRY` | 记忆文件形状遥测 |
| `COWORKER_TYPE_TELEMETRY` | Coworker 类型遥测 |
| `DOWNLOAD_USER_SETTINGS` | 设置下载同步 |
| `UPLOAD_USER_SETTINGS` | 设置上传同步 |
| `BREAK_CACHE_COMMAND` | /break-cache 命令 |
---
## 3. KAIROS — AI 助手守护进程
**代号含义:** KAIROS希腊语"决定性时刻"
### 架构
```
claude assistant ← 启动入口
永驻守护进程
├── 每日日志: logs/YYYY/MM/YYYY-MM-DD.md仅追加
├── SleepTool: 休眠 → 定时自主唤醒
├── PushNotificationTool: 向用户推送通知
├── SendUserFileTool: 发送文件给用户
├── SubscribePRTool: 订阅 GitHub PR webhook
├── BriefTool: 简短状态报告ctrl+shift+b
├── 频道通知: 通过 MCP 接收外部消息
└── /dream: 夜间记忆蒸馏
```
### 关键特性
- **记忆范式不同**:不写独立记忆文件,而是追加到每日日志
- **夜间梦境**`/dream` 技能自动将日志蒸馏为主题文件
- **自主循环**SleepTool + 自动唤醒 = 完全自主的工作循环
- **压缩后行为**:提示词包含 "你正在自主模式中运行,这不是首次唤醒——继续工作循环"
- **涉及 6 个子特性开关**KAIROS + KAIROS_DREAM + KAIROS_BRIEF + KAIROS_CHANNELS + KAIROS_PUSH_NOTIFICATION + KAIROS_GITHUB_WEBHOOKS
---
## 4. CHICAGO_MCP — Computer Use 电脑控制
**代号:** Chicago子门控以芝加哥地标命名`tengu_malort_pedway`
### 功能
- 创建**进程内 MCP 服务器**控制屏幕
- macOS 上使用**原生 Swift 执行器**
- 支持 `pixels``normalized` 两种坐标模式
- 自动列出已安装应用供模型参考
- **ESC 热键**紧急中断电脑控制
- 可通过 `--computer-use-mcp` 独立启动 MCP 服务器
### 子功能门控
| 子门控 | 功能 |
|--------|------|
| `pixelValidation` | 像素坐标验证 |
| `clipboardPasteMultiline` | 多行剪贴板粘贴 |
| `mouseAnimation` | 鼠标移动动画 |
| `hideBeforeAction` | 操作前隐藏 UI |
| `autoTargetDisplay` | 自动目标显示器选择 |
| `clipboardGuard` | 剪贴板保护 |
### 访问控制
- 需要 **Max 或 Pro 订阅**(外部用户)
- Anthropic 员工可直接使用(绕过订阅检查)
- 有专用权限 UI`ComputerUseApproval.tsx`
---
## 5. 投机执行系统Speculation
**仅 Anthropic 内部**`USER_TYPE === 'ant'`
### 工作原理
```
1. 系统生成"下一步建议"提示词
2. 用户看到建议的同时,后台 Fork Agent 已开始执行
3. 文件写入到 overlay 目录copy-on-write 隔离)
~/.claude/tmp/speculation/<pid>/<id>/
4. 用户接受建议 → 投机结果注入对话 + overlay 文件复制到真实文件系统
用户拒绝/修改 → 丢弃投机结果
```
### 安全约束
| 工具类型 | 投机执行中的权限 |
|----------|:----------------:|
| 只读工具Read, Glob, Grep | 允许 |
| 写入工具Edit, Write | 仅在权限模式允许自动接受时 |
| Bash 命令 | 必须通过只读验证 |
| 未知工具 | 遇到即暂停boundary |
### 限制
- 最多 **20 轮**、**100 条消息**
- 可以**流水线化**:当前投机完成后预生成下一步建议
### 用户反馈(内部可见)
```
Speculated 5 tool uses — +12s saved (47s this session)
```
---
## 6. Undercover 隐身模式
**仅 Anthropic 员工**,自动激活条件:在公开/开源仓库工作。
### 激活后的行为
| 行为 | 说明 |
|------|------|
| 剥除归属 | 所有 `Co-Authored-By` 行被移除 |
| 禁止提及内部代号 | Capybara、Tengu 等动物代号 |
| 禁止提及版本号 | 未发布的模型版本 |
| 禁止提及内部仓库 | Anthropic 内部仓库名 |
| 禁止提及 Slack 频道 | 内部 Slack 频道名 |
| 禁止提及 "Claude Code" | commit 消息必须看起来像人类写的 |
### 控制
- 自动检测:仓库 remote 不在内部白名单 → 自动开启
- 强制开启:`CLAUDE_CODE_UNDERCOVER=1`
- 无强制关闭选项
- 首次激活有说明对话框
---
## 7. Dream Mode 记忆梦境整合
### 自动触发条件
同时满足:
- 距上次整合 ≥ **24 小时**`minHours`,可配置)
- 且触及 ≥ **5 个会话**`minSessions`,可配置)
- 无其他进程正在整合(锁机制)
### 四阶段执行
```
阶段 1 — 定向
读取记忆目录和 MEMORY.md 索引
阶段 2 — 收集
从每日日志和会话 transcript 中提取新信号
阶段 3 — 整合
将新信息合并到已有主题文件
转换相对日期为绝对日期
删除被新事实矛盾的旧事实
阶段 4 — 修剪
保持索引在 25KB 以内
清理过时条目
```
### 工具限制
Dream 运行期间Bash 被限制为只读命令。
### 手动触发
`/dream` 技能在前台执行同样的整合流程(需 KAIROS 或 KAIROS_DREAM 开关)。
---
## 8. Voice Mode 语音输入
### 激活方式
**按住空格键说话**push-to-talk通过 `/voice` 命令开启。
### 技术栈
```
原生音频捕获
macOS → CoreAudio (audio-capture-napi / cpal)
Linux → ALSA
回退 → SoX rec / arecord
录音参数
采样率: 16kHz
声道: 单声道
静音检测: 2.0秒3% 阈值SoX 回退路径)
STT 流式传输
→ claude.ai voice_stream 端点
→ 需要 Anthropic OAuthAPI key/Bedrock/Vertex 不支持)
```
### UI 元素
- 音频电平指示器
- 语音状态显示
- 语言选择器(`LanguagePicker.tsx`
- 自定义关键词词汇表(`voiceKeyterms.ts`
### 控制
- GrowthBook 杀开关:`tengu_amber_quartz_disabled`
- 默认 fail-open新安装时启用
---
## 9. Proactive 自主代理模式
### 核心工具SleepTool
```typescript
// 代理可以休眠指定时间后自主唤醒
SleepTool.call({ duration: '5m' })
// 5 分钟后自动恢复执行
```
### 与 KAIROS 结合
```
claude assistant
[工作] → [SleepTool 5m] → [自动唤醒] → [继续工作] → [SleepTool 10m] → ...
↑ |
└──────────────────────────── 无限循环 ────────────────────────────────┘
```
### 压缩后的特殊行为
普通模式压缩后:提示用户是否有问题
自主模式压缩后:
```
你正在自主/主动模式中运行。这不是首次唤醒——你在压缩前已经在自主工作。
继续你的工作循环:根据上面的摘要从离开的地方接续。不要问候用户或询问要做什么。
```
---
## 10. 伴侣精灵系统Buddy
`feature('BUDDY')` — 一个完整的收藏型宠物系统。
### 物种18种
duck、goose、blob、cat、dragon、octopus、owl、penguin、turtle、snail、ghost、axolotl、capybara、cactus、robot、rabbit、mushroom、chonk
### 外观组合
```
18 种物种 × 6 种眼睛 × 8 种帽子 = 864 种外观组合
× 5 种稀有度 = 4,320 种可能的精灵
```
### 稀有度
| 等级 | 概率 | 显示 | 属性底值 |
|------|:----:|------|:--------:|
| Common | 60% | 1星 | 低 |
| Uncommon | 25% | 2星 | 略高 |
| Rare | 10% | 3星 | 中等 |
| Epic | 4% | 4星 | 高 |
| Legendary | 1% | 5星 | 最高 |
### 5种属性
- **DEBUGGING** — 调试力
- **PATIENCE** — 耐心
- **CHAOS** — 混沌
- **WISDOM** — 智慧
- **SNARK** — 吐槽力
每只精灵有一个巅峰属性和一个低谷属性。
### 确定性生成
```typescript
// 种子 = hash(userId + 'friend-2026-401')
// PRNG = Mulberry32
// 无论何时重新生成,同一用户始终得到同一只精灵
// 编辑配置文件无法伪造稀有度——bones 从 hash 重新计算
```
### ASCII 动画
```
每种物种 3 帧动画5行×12字符
500ms tick 的闲置动画
包含眨眼帧和特殊效果(烟雾、天线发光、墨水泡泡等)
```
### 互动
```
/buddy pet → 触发 2.5 秒心形粒子特效(❤ 字符向上飘浮)
模型可以让精灵"说话"(通过 speech bubble 系统)
用户对精灵说话时,模型会自动退让
```
### 反作弊
```
物种名中某些与模型代号冲突的名称用 String.fromCharCode 十六进制编码
配置文件编辑不影响生成结果bones 从 hash 重算)
```
### 启动引导
首次未孵化时显示彩虹色 `/buddy` 提示文本。
---
## 11. 多代理团队/Swarm 系统
### 激活方式
```bash
# 外部用户
claude --agent-teams
# 或
export CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
```
### 三种后端
| 后端 | 实现 | 特点 |
|------|------|------|
| **TmuxBackend** | 隔离 tmux socket `claude-swarm-<PID>` | 彩色边框,不影响用户的 tmux |
| **ITermBackend** | iTerm2 的 `it2` CLI + Python API | 原生分屏 |
| **InProcessBackend** | 进程内运行 | 无终端 UI |
自动检测选择最佳可用后端。
### Tmux 隔离
```typescript
// Claude 创建自己的 tmux socket防止命令影响用户会话
const SWARM_SESSION_NAME = 'claude-swarm'
const HIDDEN_SESSION_NAME = 'claude-hidden'
// socket 名: claude-<PID>
```
### Coordinator 模式
```bash
export CLAUDE_CODE_COORDINATOR_MODE=1
```
Claude 变为协调者,通过 `AgentTool``SendMessageTool``TaskStopTool` 编排 worker 代理。Worker 拥有独立工具访问权限。
### 团队工具
| 工具 | 功能 |
|------|------|
| `TeamCreateTool` | 创建代理团队 |
| `TeamDeleteTool` | 删除代理团队 |
| `SendMessageTool` | 代理间消息传递 |
| `TaskStopTool` | 停止子任务 |
| `TungstenTool`(内部) | tmux 会话管理 |
### 共享便签本
`tengu_scratch` 门控开启时worker 获得共享 scratchpad 目录,跨 worker 知识交换绕过权限提示。
---
## 12. Anthropic 内部专用命令20+个)
全部在外部构建中被替换为 `{ isEnabled: false, isHidden: true }` 存根。
### 调试和诊断
| 命令 | 用途 |
|------|------|
| `/bridge-kick` | 注入 bridge 故障close/poll/register/heartbeat 等 10+ 子命令) |
| `/mock-limits` | 模拟各种速率限制场景 |
| `/reset-limits` | 重置速率限制状态 |
| `/ctx-viz` | 上下文窗口可视化 |
| `/debug-tool-call` | 工具调用调试 |
| `/ant-trace` | Anthropic 内部追踪 |
| `/perf-issue` | 性能问题报告 |
| `/env` | 显示环境变量 |
| `/stuck` | 诊断冻结/慢会话(扫描 CPU、进程状态、内存泄漏 |
| `/force-snip` | 强制历史裁剪 |
| `/break-cache` | 使 prompt cache 失效 |
### 工作流和自动化
| 命令 | 用途 |
|------|------|
| `/autofix-pr` | 自动修复 PR 问题 |
| `/bughunter` | 远程审查的 bug 猎人模式 |
| `/teleport` | 远程传送到其他环境 |
| `/backfill-sessions` | 回填会话数据 |
| `/agents-platform` | 代理平台管理 |
| `/ultraplan` | 超级计划模式 |
| `/share` | 分享对话 |
### 开发辅助
| 命令 | 用途 |
|------|------|
| `/lorem-ipsum` | 精确 token 计数的填充文本生成(用 1-token 词汇实现精确计数) |
| `/oauth-refresh` | OAuth token 手动刷新 |
| `/good-claude` | (已存根化) |
| `/onboarding` | 引导流程 |
| `/init-verifiers` | 初始化验证器 |
### 内部专用工具
| 工具 | 用途 |
|------|------|
| `ConfigTool` | 直接修改配置 |
| `TungstenTool` | tmux 会话管理 |
| `REPLTool` | 沙盒 REPL VM |
| `SuggestBackgroundPRTool` | 建议后台 PR |
| `CtxInspectTool` | 上下文检查 |
| `OverflowTestTool` | 溢出测试 |
| `SnipTool` | 历史裁剪 |
| `MonitorTool` | 监控 |
### Bridge Kick 详细子命令
```
/bridge-kick close <code> — 触发 WebSocket close
/bridge-kick poll 404 [type] — 下次轮询 404
/bridge-kick poll transient — 下次轮询 5xx
/bridge-kick register fail [N] — 下 N 次注册失败
/bridge-kick register fatal — 注册 403终端失败
/bridge-kick reconnect-session fail — 重连失败
/bridge-kick heartbeat <status> — 心跳致命错误
/bridge-kick reconnect — 直接触发重连
/bridge-kick status — 打印 bridge 状态
```
支持**组合故障序列**,复现真实生产故障链。
---
## 13. 隐藏键盘快捷键
### 全局快捷键
| 快捷键 | 动作 |
|--------|------|
| `ctrl+t` | 切换 Todo 面板 |
| `ctrl+o` | 切换 Transcript 查看器 |
| `ctrl+r` | 历史反向搜索 |
| `ctrl+l` | 重绘屏幕 |
| `ctrl+b` | 后台运行当前任务 |
| `shift+tab` | 循环切换权限模式 |
| `meta+p` | 模型选择器 |
| `meta+o` | Fast Mode 切换 |
| `meta+t` | Thinking 模式切换 |
### Chord 快捷键(两步组合)
| 快捷键 | 动作 |
|--------|------|
| `ctrl+x ctrl+k` | 终止所有代理 |
| `ctrl+x ctrl+e``ctrl+g` | 打开外部编辑器 |
### 编辑快捷键
| 快捷键 | 动作 |
|--------|------|
| `ctrl+_` / `ctrl+shift+-` | 撤销文本输入 |
| `ctrl+s` | 暂存当前输入 |
| `ctrl+v` / `alt+v`(Win) | 粘贴图片 |
### 特性门控快捷键
| 快捷键 | 动作 | 需要 |
|--------|------|------|
| `shift+up` | 消息动作选择器 | `MESSAGE_ACTIONS` |
| `ctrl+shift+f` / `cmd+shift+f` | 全局文件搜索 | `QUICK_SEARCH` |
| `ctrl+shift+p` / `cmd+shift+p` | 快速打开 | `QUICK_SEARCH` |
| `meta+j` | 终端面板 | `TERMINAL_PANEL` |
| `ctrl+shift+b` | Brief 模式切换 | `KAIROS` |
| `ctrl+shift+o` | 队友预览切换 | 团队模式 |
| `space`(按住) | Push-to-talk 语音 | `VOICE_MODE` |
### 滚动快捷键
| 快捷键 | 动作 |
|--------|------|
| `pageup` / `pagedown` | 翻页滚动 |
| `wheelup` / `wheeldown` | 鼠标滚轮滚动 |
| `ctrl+shift+c` / `cmd+c` | 复制选中内容(滚动模式中) |
### 保留快捷键(不可重绑定)
- `ctrl+c` — 中断
- `ctrl+d` — 退出
---
## 14. 隐藏环境变量
### 核心配置
| 变量 | 效果 |
|------|------|
| `CLAUDE_CODE_SIMPLE=1` | 极简模式3个工具 |
| `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` | 禁用自动记忆提取 |
| `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1` | 禁用所有非必要网络流量 |
| `CLAUDE_CODE_DISABLE_THINKING=1` | 禁用 thinking 模式 |
| `CLAUDE_CODE_DISABLE_FAST_MODE=1` | 禁用 fast mode |
| `CLAUDE_CODE_DISABLE_CLAUDE_MDS=1` | 禁用 CLAUDE.md 加载 |
| `CLAUDE_CODE_DISABLE_COMMAND_INJECTION_CHECK=1` | 禁用命令注入检查 |
| `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1` | 禁用后台任务 |
### 性能调优
| 变量 | 效果 |
|------|------|
| `CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY=N` | 最大并发工具数默认10 |
| `CLAUDE_CODE_AUTO_COMPACT_WINDOW=N` | 覆盖自动压缩窗口 |
| `CLAUDE_CODE_MAX_OUTPUT_TOKENS=N` | 覆盖最大输出 token |
| `CLAUDE_CODE_MAX_RETRIES=N` | 覆盖最大重试次数 |
| `CLAUDE_CODE_BLOCKING_LIMIT_OVERRIDE=N` | 覆盖阻塞限制 |
| `CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=80` | 在 80% 上下文时触发压缩 |
### API 控制
| 变量 | 效果 |
|------|------|
| `ANTHROPIC_MODEL=model-id` | 覆盖默认模型 |
| `ANTHROPIC_SMALL_FAST_MODEL=model-id` | 覆盖后台辅助任务模型 |
| `ANTHROPIC_BASE_URL=url` | 自定义 API 基础 URL |
| `ANTHROPIC_CUSTOM_HEADERS='{"k":"v"}'` | 注入自定义 HTTP 头 |
| `CLAUDE_CODE_EXTRA_BODY='{"k":"v"}'` | 注入额外 API 请求体 |
| `CLAUDE_CODE_EXTRA_METADATA='{"k":"v"}'` | 注入额外元数据 |
| `CLAUDE_CODE_USE_BEDROCK=1` | 使用 AWS Bedrock |
| `CLAUDE_CODE_USE_VERTEX=1` | 使用 Google Vertex |
| `CLAUDE_CODE_USE_FOUNDRY=1` | 使用 Azure Foundry |
| `CLAUDE_CODE_UNATTENDED_RETRY=1` | 无人值守无限重试 |
### 调试
| 变量 | 效果 |
|------|------|
| `CLAUDE_CODE_DEBUG=1` | 启用调试日志 |
| `CLAUDE_CODE_FRAME_TIMING_LOG=/path` | 记录帧渲染时间 |
| `CLAUDE_CODE_BASH_SANDBOX_SHOW_INDICATOR=1` | 显示沙盒指示器 |
| `CLAUDE_CODE_SYNTAX_HIGHLIGHT=1` | 启用语法高亮 |
| `OTEL_LOG_TOOL_DETAILS=1` | 遥测中记录工具详情 |
| `OTEL_LOG_USER_PROMPTS=1` | 遥测中记录用户提示词(默认 REDACTED |
| `DISABLE_COMPACT=1` | 完全禁用压缩 |
| `DISABLE_AUTO_COMPACT=1` | 仅禁用自动压缩 |
### 代理团队
| 变量 | 效果 |
|------|------|
| `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` | 启用多代理团队 |
| `CLAUDE_CODE_COORDINATOR_MODE=1` | 协调者模式 |
| `CLAUDE_CODE_TEAMMATE_COMMAND=cmd` | 队友启动命令 |
| `CLAUDE_CODE_AGENT_COLOR=color` | 代理颜色 |
| `CLAUDE_CODE_PLAN_MODE_REQUIRED=1` | 强制 plan 模式 |
---
## 15. 其他彩蛋与冷知识
### 200+ 花式加载词
`spinnerVerbs.ts` 中的精选:
```
Clauding, Boondoggling, Canoodling, Flibbertigibbeting,
Hullaballooing, Moonwalking, Prestidigitating, Razzmatazzing,
Shenaniganing, Tomfoolering, Whatchamacalliting, Beboppin'
```
完成时的过去式:
```
Baked for 5s, Cogitated for 12s, Noodled for 8s
```
### VCR 录制/回放模式
`FORCE_VCR=1`(内部):录制和回放 API 交互,用于测试。
### Lorem Ipsum 精确 token 生成
`/lorem-ipsum 50000`(内部):使用经过验证的 1-token 单词生成精确 token 数的填充文本。
### Fennec 代号
内部模型迁移中有 `migrateFennecToOpus.ts`:将 `fennec-latest` 映射到 `opus``fennec-fast-latest` 映射到 `opus[1m]` + fast mode。
### 消融基线模式
`CLAUDE_CODE_ABLATION_BASELINE`:降级功能用于 A/B 测试,验证各功能的实际贡献。
### 深度链接协议
`feature('LODESTONE')`:注册 `cc://` URL 协议处理器,其他应用可直接打开 Claude Code 会话。
### 物种名编码
```typescript
// 伴侣精灵的某个物种名与内部模型代号冲突
// 用 String.fromCharCode 十六进制编码避免 excluded-strings 检查
```
### 权限模式循环
`shift+tab` 在以下模式间循环:
```
default → acceptEdits → plan → auto → default → ...
```
### Anthropic API Key 自检回避
```typescript
// 秘密扫描器中 Anthropic API key 前缀在运行时拼接
// 避免匹配自身源码的 excluded-strings 检查
const prefix = ['sk', 'ant', 'api'].join('-')
```
---
## 总结:冰山模型
```
╭───────────────╮
公开版本 │ CLI + 43工具 │ ← 你看到的
│ /命令 + UI │
╰───────┬───────╯
╭───────────────┼───────────────╮
编译时剥离 │ KAIROS守护进程 │ Computer Use │ ← 源码中存在
│ Voice Mode │ Speculation │ 但外部构建被删除
│ Dream Mode │ Undercover │
│ Buddy Pet │ Swarm/Teams │
│ Workflows │ BG Sessions │
│ Deep Links │ Cron Triggers │
╰───────────────┴───────────────╯
╭───────────────┼───────────────╮
内部专用 │ 20+ 调试命令 │ bridge-kick │ ← 仅 Anthropic 员工
│ mock-limits │ VCR 录放 │
│ lorem-ipsum │ 消融基线 │
│ stuck 诊断 │ ctx-viz │
╰───────────────┴───────────────╯
```
**Claude Code 不只是一个 CLI 工具——它的源码中隐藏着一个完整的 AI 助手操作系统的雏形。** 守护进程、语音输入、电脑控制、投机执行、梦境记忆整合、多代理协作、隐身模式——当前公开版本只暴露了冰山一角。
---
*来自AI超元域 | B站频道https://space.bilibili.com/3493277319825652*
*基于 Claude Code 源码逆向分析2026-03-31*

View File

@@ -0,0 +1 @@
<img width="616" height="500" alt="Image" src="https://github.com/user-attachments/assets/1d9bc809-b743-4b79-a3f1-6d84edea9195" />

View File

@@ -0,0 +1,682 @@
# Claude Code 多 Agent 架构深度分析
> 基于源码约 **13,700 行、37+ 文件**的深度探索,涵盖 `src/utils/swarm/`、`src/tools/AgentTool/`、`src/coordinator/` 等核心模块。
---
## 一、整体架构
系统采用 **Leader-Worker 模型**,核心有三种执行后端和两种编排模式:
```
┌─────────────────────────────┐
│ 用户请求 (REPL/CLI) │
└──────────┬──────────────────┘
┌──────────▼──────────────────┐
│ Leader Agent (team-lead) │
│ QueryEngine → query() │
└──┬────────┬────────┬────────┘
│ │ │
┌────────────▼┐ ┌────▼─────┐ ┌▼────────────┐
│ InProcess │ │ Tmux │ │ iTerm2 │
│ (同进程) │ │ (分屏) │ │ (分屏) │
└──────────────┘ └──────────┘ └─────────────┘
│ │ │
AsyncLocalStorage 独立进程 独立进程
隔离上下文 文件邮箱通信 文件邮箱通信
```
**两种编排模式:**
- **Team Mode** — 通过 `TeamCreate` 创建团队,手动管理 Worker
- **Coordinator Mode** — 自动化编排Coordinator 只能用 Agent/TaskStop/SendMessage 三个工具
---
## 二、Agent Tool — 统一入口
`src/tools/AgentTool/AgentTool.tsx` 是所有子 Agent 的统一入口。
### 2.1 输入参数
| 参数 | 类型 | 作用 |
|------|------|------|
| `prompt` | string | 任务描述 |
| `subagent_type` | string | Agent 类型 (Explore, Plan, 自定义等) |
| `model` | `'sonnet'\|'opus'\|'haiku'` | 模型覆盖 |
| `run_in_background` | boolean | 后台异步执行 |
| `name` | string | 命名,使其可通过 SendMessage 寻址 |
| `team_name` | string | 团队名 |
| `mode` | string | 权限模式 (plan, acceptEdits 等) |
| `isolation` | `'worktree'` | Git worktree 隔离 |
### 2.2 路由决策树
```
call() 入口 (AgentTool.tsx:284)
├── team_name + name → spawnTeammate() → 生成持久化队友
├── isolation === 'remote' → Cloud Code Runner
├── run_in_background === true → runAsyncAgentLifecycle() (fire-and-forget)
└── 默认 → runAgent() 同步执行,阻塞父 Agent
```
### 2.3 关键常量
```typescript
// src/tools/AgentTool/constants.ts
AGENT_TOOL_NAME = 'Agent'
LEGACY_AGENT_TOOL_NAME = 'Task' // 向后兼容
ONE_SHOT_BUILTIN_AGENT_TYPES = {'Explore', 'Plan'}
```
---
## 三、Agent 定义系统
`src/tools/AgentTool/loadAgentsDir.ts` (755行) 负责从多来源加载 Agent 定义。
### 3.1 AgentDefinition 类型
```typescript
type AgentDefinition = {
agentType: string // 唯一标识
whenToUse: string // 何时使用的描述
tools: string[] // 可用工具列表,['*'] 表示全部
disallowedTools: string[] // 禁用工具
model: string // 模型选择
permissionMode: string // 权限模式
maxTurns: number // 最大对话轮次
background: boolean // 是否后台运行
isolation: 'worktree' // 隔离方式
getSystemPrompt() // 系统提示词生成函数
}
```
### 3.2 三种来源
- `BuiltInAgentDefinition` — 内置 Agent (source: 'built-in')
- `CustomAgentDefinition` — 用户自定义 `.claude/agents/*.md` 文件
- `PluginAgentDefinition` — 插件提供的 Agent
### 3.3 内置 Agent 类型
| Agent | 模型 | 工具 | 特点 |
|-------|------|------|------|
| `general-purpose` | 默认 | `['*']` | 全能通用 |
| `Explore` | haiku | 只读工具 | 代码探索,禁止编辑/写入/嵌套 Agent |
| `Plan` | inherit | 只读工具 | 架构规划,禁止编辑 |
| `verification` | inherit | 只读 | 后台对抗性测试,红色标识 |
| `statusline-setup` | sonnet | Read, Edit | 状态栏配置专用 |
| `fork` (合成) | inherit | `['*']` | 继承父上下文,权限冒泡 |
| `claude-code-guide` | — | 有限工具 | Claude Code 使用指南 |
### 3.4 加载优先级
```
built-in < plugin < userSettings < projectSettings < flagSettings < policySettings
```
后加载的同名 Agent 覆盖先加载的。
---
## 四、三大执行后端
### 4.1 InProcess 后端 (同进程)
**文件**: `src/utils/swarm/backends/InProcessBackend.ts` (339行)
最轻量的方式——所有 Worker 运行在**同一个 Node.js 进程**中:
```
Leader 进程
├── AsyncLocalStorage<TeammateContext> #1 → Worker A
├── AsyncLocalStorage<TeammateContext> #2 → Worker B
└── AsyncLocalStorage<TeammateContext> #3 → Worker C
```
**核心流程:**
1. `spawn()``spawnInProcessTeammate()` 注册到 AppState
2. `startInProcessTeammate()` fire-and-forget 启动 Agent 循环
3. 通过 `runWithTeammateContext(context, fn)` 包裹执行,实现上下文隔离
4. 权限请求直接使用 Leader 的 `ToolUseConfirm` 对话框(通过 `leaderPermissionBridge.ts`
**上下文隔离** (`src/utils/teammateContext.ts`, 96行)
```typescript
// AsyncLocalStorage 实现进程内隔离
const teammateContextStorage = new AsyncLocalStorage<TeammateContext>()
function runWithTeammateContext(context: TeammateContext, fn: () => T): T {
return teammateContextStorage.run(context, fn)
}
function isInProcessTeammate(): boolean {
return teammateContextStorage.getStore() !== undefined
}
```
### 4.2 Tmux 后端 (终端分屏)
**文件**: `src/utils/swarm/backends/TmuxBackend.ts` (764行)
每个 Worker 是一个**独立的 Claude CLI 进程**,运行在 tmux 分屏中。
**两种模式:**
**Leader 在 tmux 内:**
```
┌─────────────────┬──────────────────┐
│ │ Worker A │
│ Leader ├──────────────────┤
│ (30%) │ Worker B │
│ ├──────────────────┤
│ │ Worker C │
└─────────────────┴──────────────────┘
main-vertical 布局
```
**Leader 在 tmux 外:** 创建 `claude-swarm-{pid}` 独立 sessiontiled 布局。
**关键细节:**
- `acquirePaneCreationLock()` 序列化并行 spawn防止竞态
- 200ms shell 初始化延迟等待 rc 文件加载
- 通过 `tmux send-keys` 向 pane 发送命令
- 支持 `hidePane()` (break-pane) / `showPane()` (join-pane)
### 4.3 iTerm2 后端
**文件**: `src/utils/swarm/backends/ITermBackend.ts` (370行)
通过 `it2` CLI (Python 自动化工具) 控制 iTerm2
- 首个 Worker 垂直分割,后续水平分割
- 死会话恢复split 失败时验证 session list 并重试
- `setPaneBorderColor`/`rebalancePanes` 为 no-op避免频繁启动 Python 进程)
- `supportsHideShow = false`(无等效的 tmux break-pane
### 4.4 后端选择逻辑
`src/utils/swarm/backends/registry.ts``detectAndGetBackend()` (line 136)
```
优先级瀑布:
1. 在 tmux 内? → TmuxBackend (原生)
2. 在 iTerm2 且有 it2 CLI → ITermBackend
3. 在 iTerm2 无 it2 → 回退 tmux提示安装 it2
4. 有 tmux → TmuxBackend (外部 session)
5. 非交互模式 (-p) → InProcessBackend
6. teammateMode 配置为 'in-process' → InProcessBackend
7. 都没有 → 报错并给出平台特定安装指引
```
---
## 五、核心通信机制 — 文件邮箱
### 5.1 邮箱结构
`src/utils/teammateMailbox.ts` (1,183行)
每个 Agent 拥有一个 JSON 文件邮箱:
```
~/.claude/teams/{team_name}/inboxes/{agent_name}.json
```
```typescript
type TeammateMessage = {
from: string // 发送者名称
text: string // 消息内容
timestamp: string // 时间戳
read: boolean // 是否已读
color?: string // 发送者 UI 颜色
summary?: string // 5-10 字摘要
}
```
### 5.2 核心操作
| 函数 | 作用 |
|------|------|
| `writeToMailbox()` | 原子写入 + `proper-lockfile` 文件锁10次重试5-100ms 退避) |
| `readMailbox()` | 读取所有消息 |
| `readUnreadMessages()` | 过滤未读消息 |
| `markMessagesAsRead()` | 标记已读 |
### 5.3 结构化消息类型
邮箱不仅传递文本,还定义了一套协议消息:
- `createShutdownRequestMessage` / `createShutdownApprovedMessage` — 关闭协议
- `createPermissionRequestMessage` / `createPermissionResponseMessage` — 权限请求/响应
- `createSandboxPermissionRequestMessage` / `createSandboxPermissionResponseMessage` — 沙箱网络权限
- `createIdleNotification` — Worker 空闲通知
### 5.4 轮询机制
| Agent 类型 | 轮询方式 |
|------------|----------|
| Pane 类 Worker | `useInboxPoller` React hook 定期轮询 |
| InProcess Worker | `waitForNextPromptOrShutdown()` 直接读邮箱 |
| Leader | `useInboxPoller` 处理权限请求、空闲通知等 |
---
## 六、SendMessage Tool — Agent 间通信
`src/tools/SendMessageTool/SendMessageTool.ts` (917行)
### 6.1 输入参数
```typescript
{
to: string // 收件人名称、"*" 广播、"uds:<socket>"、"bridge:<session-id>"
summary: string // 5-10 字摘要
message: string | ShutdownRequest | ShutdownResponse | PlanApprovalResponse
}
```
### 6.2 路由逻辑
```
SendMessage.call() (line 741)
├── to: "bridge:<session-id>" → postInterClaudeMessage (跨机器 Remote Control)
├── to: "uds:<socket>" → sendToUdsSocket (Unix Domain Socket 本地通信)
├── to: "*" → 广播到所有团队成员邮箱
├── to: "<name>" → 查 agentNameRegistry:
│ ├── InProcess Agent running → queuePendingMessage (内存队列)
│ ├── InProcess Agent stopped → resumeAgentBackground (自动唤醒!)
│ └── Pane Agent → writeToMailbox (文件邮箱)
└── 结构化消息 → 分发到对应处理器
```
**关键设计Agent 的纯文本输出对其他 Agent 不可见,必须通过 SendMessage 通信。**
---
## 七、权限委托系统
### 7.1 完整流程
`src/utils/swarm/permissionSync.ts` (928行)
```
Worker 遇到需要权限的操作
检查 bash classifier 能否自动批准 (swarmWorkerHandler.ts:52)
↓ 不能
创建 PermissionRequest 对象 (line 71)
注册回调 BEFORE 发送请求 (避免竞态, line 79)
发送 permission_request 到 Leader 邮箱 (line 123)
显示 "waiting for leader" 状态 (line 126)
Leader 轮询检测到请求
在 Leader UI 展示权限对话框(带 worker badge
用户批准/拒绝
Leader 发送 permission_response 到 Worker 邮箱
Worker 轮询获取响应(每 500ms继续执行
```
### 7.2 InProcess 优化路径
通过 `leaderPermissionBridge.ts` (54行)InProcess Worker 直接使用 Leader 的 `ToolUseConfirm` 队列:
```typescript
// REPL 注册权限 UI
registerLeaderPermissionQueue(setToolUseConfirmQueue, setToolPermissionContext)
// Worker 推送权限请求到 Leader UI带 worker badge
pushToLeaderPermissionQueue(request, workerBadge)
```
跳过文件邮箱的 I/O 开销,提供与 Leader 自身工具相同的 UI 体验。
### 7.3 SwarmPermissionRequest 结构
```typescript
type SwarmPermissionRequest = {
id: string
workerId: string
workerName: string
toolName: string
toolUseId: string
description: string
input: any
permissionSuggestions: any
status: 'pending' | 'approved' | 'rejected'
resolvedBy?: string
feedback?: string
updatedInput?: any
permissionUpdates?: any
}
```
### 7.4 权限模式继承
当 Leader 批准 Worker 的 plan 时:
```typescript
const modeToInherit = leaderMode === 'plan' ? 'default' : leaderMode
```
团队文件中每个成员有独立的 `mode` 字段,支持通过 `setMemberMode()` / `setMultipleMemberModes()` 动态调整。
---
## 八、Agent 执行引擎
### 8.1 runAgent() — 子 Agent 核心驱动
`src/tools/AgentTool/runAgent.ts` (973行)
`runAgent()` 是一个 `AsyncGenerator<Message, void>`
```
runAgent() (line 248)
1. 解析模型 → getAgentModel()
2. 创建隔离上下文 → createSubagentContext()
- fork 父消息到独立上下文
- 隔离的文件状态缓存
3. 初始化 Agent 专属 MCP servers → initializeAgentMcpServers()
4. 解析工具集 → resolveAgentTools()
5. 构建系统提示词 (Agent 自身 + 环境信息)
6. 注册 Perfetto 追踪, hooks
7. 调用 query() — 与主 REPL 完全相同的 agentic 循环
8. yield 每条消息回父 Agent
9. 清理 MCP servers, shell tasks, agent tracking
```
**核心设计:子 Agent 复用完全相同的 `query()` 循环**,递归地拥有完整的工具执行能力。
### 8.2 异步 Agent 生命周期
`agentToolUtils.ts``runAsyncAgentLifecycle()` (line 508)
```
registerAsyncAgent() → 创建 task entry
注册 name 到 agentNameRegistry (用于 SendMessage 路由)
void runWithAgentContext(...) → fire-and-forget
迭代 runAgent() 的消息流
ProgressTracker 追踪:
- tool use 次数
- token 消耗
- 最近活动
完成时 finalizeAgentTool():
- 提取最终文本内容
- 计算 totalDurationMs, totalTokens, totalToolUseCount
completeAsyncAgent() → 标记 task 完成
发送 <task-notification> XML 通知父 Agent
```
**多个异步 Agent 是真正并行的**——每个都是独立的 async 操作。
### 8.3 工具过滤
`agentToolUtils.ts``filterToolsForAgent()` (line 70) 使用多层禁用列表:
| 禁用列表 | 适用范围 | 禁用的工具 |
|----------|----------|-----------|
| `ALL_AGENT_DISALLOWED_TOOLS` | 所有 Agent | TaskOutput, ExitPlanMode, EnterPlanMode, AskUserQuestion, TaskStop |
| `CUSTOM_AGENT_DISALLOWED_TOOLS` | 非内置 Agent | 同上 + AgentTool |
| `ASYNC_AGENT_ALLOWED_TOOLS` | 后台 Agent | 白名单制Read, Grep, Glob, Edit, Write, WebSearch 等 |
| `COORDINATOR_MODE_ALLOWED_TOOLS` | Coordinator | 仅 Agent, TaskStop, SendMessage, SyntheticOutput |
---
## 九、Coordinator Mode — 自动化编排
`src/coordinator/coordinatorMode.ts` (369行)
### 9.1 激活条件
```typescript
function isCoordinatorMode(): boolean {
return feature('COORDINATOR_MODE') && process.env.CLAUDE_CODE_COORDINATOR_MODE === '1'
}
```
### 9.2 工作流程
```
Coordinator (只能用 Agent + TaskStop + SendMessage)
├── Research Phase → 并行派出多个 Worker 调研
│ ├── Worker A: 搜索代码
│ ├── Worker B: 读取文档
│ └── Worker C: 分析依赖
├── Synthesis Phase → Coordinator 综合分析研究结果
├── Implementation Phase → Worker 执行代码实现
└── Verification Phase → Worker 验证实现结果
```
### 9.3 Coordinator 系统提示词要点
`getCoordinatorSystemPrompt()` (line 111) 的 369 行提示词规定:
- Coordinator 通过 `Agent` 工具派出 Worker通过 `SendMessage` 继续对话,通过 `TaskStop` 终止
- Worker 完成时发送 `<task-notification>` XML
- 明确的 spawn vs continue 决策指导
- 并发管理策略
---
## 十、Team Mode — 持久化协作
### 10.1 团队文件结构
`~/.claude/teams/{team-name}/config.json`
```typescript
type TeamFile = {
name: string
description: string
createdAt: string
leadAgentId: string
leadSessionId: string
hiddenPaneIds: string[]
teamAllowedPaths: string[]
members: [{
agentId: string // 格式: name@teamName
name: string
agentType: string
model: string
color: string
planModeRequired: boolean
joinedAt: string
tmuxPaneId?: string
cwd: string
worktreePath?: string
sessionId: string
subscriptions: string[]
backendType: 'tmux' | 'iterm2' | 'in-process'
isActive: boolean
mode: string
}]
}
```
### 10.2 团队工作流
```
1. TeamCreate → 创建 config.json + tasks 目录
2. TaskCreate → 在 ~/.claude/tasks/{team-name}/ 创建任务
3. Agent tool (team_name + name) → 派出 teammate
4. TaskUpdate → 分配任务 owner
5. Teammate 工作 → 完成后自动发送 idle notification
6. SendMessage(shutdown_request) → 优雅关闭 teammate
```
### 10.3 Teammate 系统提示词附加
`src/utils/swarm/teammatePromptAddendum.ts`
> "You are running as an agent in a team. To communicate with anyone on your team, use the SendMessage tool. Just writing a response in text is not visible to others."
---
## 十一、Worktree 隔离
`src/utils/worktree.ts``createAgentWorktree()` (line 902)
### 11.1 创建流程
```
Agent spawn 时 isolation='worktree'
→ 生成 slug: agent-{agentId前8字符}
→ validateWorktreeSlug() — 拒绝路径穿越限制64字符
→ 路径: .claude/worktrees/<slug>
→ 分支: worktree-<slug>
→ git worktree add -B worktree-<slug>
→ performPostCreationSetup():
- 复制 settings.local.json
- 配置 git hooks
- 符号链接目录
- 复制 .worktreeinclude 文件
```
### 11.2 清理流程
```
Agent 完成时:
→ hasWorktreeChanges()?
├── 有改动 → 保留 worktree在 <task-notification> 中报告路径和分支
└── 无改动 → removeAgentWorktree() 自动清理
```
---
## 十二、Worker 完整生命周期
### 12.1 诞生
```
Leader 调用 Agent tool
├── InProcess: spawnInProcessTeammate()
│ 1. 生成 agentId = name@teamName
│ 2. 创建独立 AbortController不链接到 Leader
│ 3. 创建 TeammateContext for AsyncLocalStorage
│ 4. 注册 InProcessTeammateTaskState 到 AppState
│ 5. startInProcessTeammate() → runWithTeammateContext() → runAgent()
└── Pane: PaneBackendExecutor.spawn()
1. assignTeammateColor() 分配颜色
2. createTeammatePaneInSwarmView() 创建分屏
3. 构建 CLI 命令:
claude --agent-id X --agent-name Y --team-name Z
--agent-color C --parent-session-id P
4. sendCommandToPane() 发送命令
5. writeToMailbox() 写入初始 prompt
```
### 12.2 执行
```
InProcess Runner (inProcessRunner.ts, 1552行):
runWithTeammateContext() 包裹:
→ runAgent() → query() 循环
→ 轮询邮箱获取新消息(注入为 user-turn messages
→ 权限请求转发到 Leader UI
→ ProgressTracker 追踪工具使用和 token
→ 支持 auto-compact上下文过大时自动压缩
```
### 12.3 通信
```
Worker → Leader: writeToMailbox() 或 queuePendingMessage (InProcess)
Leader → Worker: writeToMailbox() 或 SendMessage
Worker → Worker: SendMessage tool → writeToMailbox()
广播: SendMessage(to: "*") → 写入所有成员邮箱
```
### 12.4 终止
三条终止路径:
| 方式 | 触发 | 流程 |
|------|------|------|
| 优雅关闭 | `shutdown_request` via mailbox | Worker 处理请求 → 退出 → idle notification |
| 强制终止 (InProcess) | `killInProcessTeammate()` | abort AbortController → 更新 task 状态为 'killed' → 移出团队文件 |
| 强制终止 (Pane) | `backend.killPane(paneId)` | 杀死 tmux/iTerm2 pane |
### 12.5 会话清理
`cleanupSessionTeams()` 在 Leader 退出时运行:
- 杀死所有孤立 pane
- 移除团队和任务目录
- 销毁 git worktree
---
## 十三、Feature Gates 汇总
| Feature Flag | 控制内容 | 激活方式 |
|-------------|---------|---------|
| `COORDINATOR_MODE` | Coordinator 自动编排 | 编译时 + `CLAUDE_CODE_COORDINATOR_MODE=1` |
| `FORK_SUBAGENT` | Fork 模式子 Agent | 编译时 feature gate |
| `BUILTIN_EXPLORE_PLAN_AGENTS` | Explore/Plan 内置 Agent | 编译时 + A/B test `tengu_amber_stoat` |
| `VERIFICATION_AGENT` | 验证 Agent | 编译时 + gate `tengu_hive_evidence` |
| Agent Teams (Swarm) | 团队协作模式 | `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` + GrowthBook gate |
---
## 十四、通信协议总览
| 通道 | 机制 | 方向 | 用途 |
|------|------|------|------|
| 文件邮箱 `inboxes/{name}.json` | JSON + 文件锁 | 任意 Agent → 任意 Agent | 所有 teammate 消息、权限、关闭协议 |
| 内存队列 `pendingUserMessages` | AppState | 父 → InProcess 子 | 运行中 Agent 的即时消息 |
| Leader Permission Bridge | 模块级函数注册 | InProcess Worker → Leader UI | 权限弹窗 |
| UDS Socket | Unix Domain Socket | Session → Session (本地) | 跨 session 消息 |
| Bridge (Remote Control) | HTTP via Anthropic | Session → Session (跨机器) | 远程控制消息 |
| Task 系统 | 文件 `~/.claude/tasks/` | 所有团队成员 | 工作项追踪、状态更新 |
| `<task-notification>` XML | 注入对话 | 后台 Agent → 父 Agent | 完成/失败通知 |
---
## 十五、架构规模统计
| 层次 | 核心组件 | 代码规模 |
|------|---------|---------|
| 入口 | AgentTool | ~800行 |
| 执行引擎 | runAgent + query() 循环 | ~2,700行 |
| Agent 定义 | loadAgentsDir + builtInAgents | ~850行 |
| 后端管理 | registry + 3个后端实现 | ~1,800行 |
| 通信 | teammateMailbox + SendMessageTool | ~2,100行 |
| 权限委托 | permissionSync + leaderPermissionBridge | ~1,000行 |
| 团队管理 | teamHelpers + spawnMultiAgent | ~1,800行 |
| 上下文隔离 | agentContext + teammateContext | ~300行 |
| Coordinator | coordinatorMode + workerAgent | ~400行 |
| InProcess Runner | inProcessRunner | ~1,550行 |
| **总计** | **37+ 文件** | **~13,700行** |
---
## 十六、核心设计理念
1. **query() 循环复用** — 子 Agent 使用与主 REPL 完全相同的 agentic 循环,天然具备完整工具能力
2. **AsyncLocalStorage 隔离** — InProcess 模式下零 IPC 开销,通过 Node.js 原生机制隔离上下文
3. **文件邮箱统一通信** — 跨进程、跨后端的统一消息协议,用文件锁保证并发安全
4. **权限始终由人类把控** — 无论多少层 Agent 嵌套,危险操作最终都路由到 Leader 的 UI 由用户决定
5. **优雅降级** — 从 iTerm2 → tmux → InProcess自动选择最佳可用后端
6. **Fire-and-forget 异步** — 后台 Agent 通过 `<task-notification>` XML 非侵入式通知父 Agent

View File

@@ -0,0 +1,274 @@
# macOS 上清除 Claude Code 追踪数据指南
> 基于 Claude Code 源码2026-03-31 泄露快照512K 行 TypeScript的逆向分析。
> 所有文件路径和字段名均从源码中确认。
---
## 背景Claude Code 追踪了什么?
Claude Code **不收集硬件指纹**(无 MAC 地址、CPU 型号、内存大小、GPU 信息),但通过以下机制实现用户追踪:
| 追踪标识 | 持久性 | 存储位置 | 说明 |
|----------|--------|----------|------|
| `userID` | 永久(直到手动删除) | `~/.claude.json` | 随机生成的 64 位十六进制字符串,跨会话追踪主键 |
| `anonymousId` | 永久 | `~/.claude.json` | 格式 `claudecode.v1.<uuid>`,备用追踪 ID |
| `accountUuid` | 永久(绑定账号) | `~/.claude.json``oauthAccount` | OAuth 登录后直接关联身份 |
| `emailAddress` | 永久 | `~/.claude.json``oauthAccount` | 登录邮箱 |
| `rh` (仓库哈希) | 按仓库 | 每次 API 请求 Header | git remote URL 的 SHA256 前 16 位 |
| Statsig Stable ID | 永久 | `~/.claude/statsig/` | 特性开关系统的设备标识 |
**数据流向:**
- **Anthropic 1P** → `/api/event_logging/batch`(完整环境数据 + Auth
- **Datadog** → `https://http-intake.logs.us5.datadoghq.com`(白名单事件,已脱敏)
- **OTLP**(可选)→ 用户自配的端点(默认关闭)
---
## 第一级:重置设备标识(最重要)
这是跨会话追踪的主键。清除后你对 Anthropic 来说就是一台"全新设备"。
```bash
# 查看当前的追踪 ID
grep -E '"userID"|"anonymousId"|"firstStartTime"|"claudeCodeFirstTokenDate"' ~/.claude.json
```
```bash
# 删除追踪标识(保留其他配置不变)
python3 -c "
import json, os
p = os.path.expanduser('~/.claude.json')
with open(p, 'r') as f: d = json.load(f)
removed = []
for k in ['userID', 'anonymousId', 'firstStartTime', 'claudeCodeFirstTokenDate']:
if k in d:
removed.append(k)
del d[k]
with open(p, 'w') as f: json.dump(d, f, indent=2)
print(f'已删除: {removed}')
print('下次启动 Claude Code 会自动生成新的 userID')
"
```
**效果:** 下次启动时会生成全新的 `userID`,之前的使用记录无法与你关联。
---
## 第二级:清除遥测和分析数据
```bash
# 未成功上报的分析事件(包含完整的环境信息、会话数据)
rm -rf ~/.claude/telemetry/
# Statsig/GrowthBook 特性开关缓存(包含 stable_id 设备标识)
rm -rf ~/.claude/statsig/
# 统计缓存
rm -f ~/.claude/stats-cache.json
```
**效果:** 清除本地缓存的遥测数据和特性开关系统的设备标识。
---
## 第三级:清除会话和历史记录
```bash
# 完整命令历史(你输入过的所有提示词)
rm -f ~/.claude/history.jsonl
# 会话快照
rm -rf ~/.claude/sessions/
# 大段粘贴内容的哈希缓存
rm -rf ~/.claude/paste-cache/
# Shell 环境快照
rm -rf ~/.claude/shell-snapshots/
# 会话环境变量
rm -rf ~/.claude/session-env/
# 文件编辑历史Claude 做过的每次文件修改)
rm -rf ~/.claude/file-history/
# 调试日志
rm -rf ~/.claude/debug/
```
**效果:** 清除所有本地会话痕迹。不影响 Claude Code 正常使用。
---
## 第四级:清除 OAuth 账号关联
```bash
# 查看当前关联的账号信息
python3 -c "
import json, os
with open(os.path.expanduser('~/.claude.json')) as f: d = json.load(f)
oa = d.get('oauthAccount', {})
print(f\"账号 UUID: {oa.get('accountUuid', '无')}\")
print(f\"邮箱: {oa.get('emailAddress', '无')}\")
"
```
```bash
# 从 macOS Keychain 删除 OAuth Token
security delete-generic-password -s "claude-code" 2>/dev/null
security delete-generic-password -s "claude-code-credentials" 2>/dev/null
# 清除配置文件中的账号缓存
python3 -c "
import json, os
p = os.path.expanduser('~/.claude.json')
with open(p, 'r') as f: d = json.load(f)
removed = []
for k in ['oauthAccount', 's1mAccessCache', 'groveConfigCache',
'passesEligibilityCache', 'clientDataCache',
'cachedExtraUsageDisabledReason', 'githubRepoPaths']:
if k in d:
removed.append(k)
del d[k]
with open(p, 'w') as f: json.dump(d, f, indent=2)
print(f'已删除: {removed}')
"
```
**效果:** 断开与 Claude.ai 账号的本地关联。下次使用需要重新登录。
---
## 第五级:完全重置(核弹选项)
```bash
# 1. 备份你的自定义配置
mkdir -p ~/Desktop/claude-backup
cp ~/.claude/CLAUDE.md ~/Desktop/claude-backup/ 2>/dev/null
cp ~/.claude/settings.json ~/Desktop/claude-backup/ 2>/dev/null
cp -r ~/.claude/skills ~/Desktop/claude-backup/ 2>/dev/null
cp -r ~/.claude/hooks ~/Desktop/claude-backup/ 2>/dev/null
echo "已备份到 ~/Desktop/claude-backup/"
# 2. 删除所有 Claude Code 数据
rm -rf ~/.claude/
rm -f ~/.claude.json
# 3. 清除 Keychain 中的 Token
security delete-generic-password -s "claude-code" 2>/dev/null
security delete-generic-password -s "claude-code-credentials" 2>/dev/null
echo "已完全重置。下次启动 Claude Code 会重新初始化。"
```
**效果:** 等同于全新安装。所有配置、历史、记忆、技能、插件设置全部清除。
---
## 防止未来追踪
### 方法 1禁用非必要网络流量
`~/.claude/settings.json` 中添加:
```json
{
"env": {
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1"
}
}
```
**效果:** 禁用分析上报、GrowthBook 特性开关拉取、配额预检等非必要请求。
### 方法 2使用第三方云后端
源码确认:使用 Bedrock 或 Vertex 时,`isAnalyticsDisabled()` 返回 `true`,完全跳过所有分析代码。
```bash
# 使用 AWS Bedrock需要 AWS 凭证)
export CLAUDE_CODE_USE_BEDROCK=1
# 或使用 Google Vertex AI需要 GCP 凭证)
export CLAUDE_CODE_USE_VERTEX=1
```
### 方法 3定期自动清理
创建一个定时清理脚本 `~/claude-privacy-clean.sh`
```bash
#!/bin/bash
# 清除 Claude Code 追踪数据(保留配置)
rm -rf ~/.claude/telemetry/
rm -rf ~/.claude/statsig/
rm -f ~/.claude/stats-cache.json
rm -f ~/.claude/history.jsonl
rm -rf ~/.claude/sessions/
rm -rf ~/.claude/paste-cache/
rm -rf ~/.claude/shell-snapshots/
rm -rf ~/.claude/session-env/
rm -rf ~/.claude/debug/
# 重置 device ID
python3 -c "
import json, os
p = os.path.expanduser('~/.claude.json')
if os.path.exists(p):
with open(p,'r') as f: d = json.load(f)
for k in ['userID','anonymousId','firstStartTime','claudeCodeFirstTokenDate']:
d.pop(k, None)
with open(p,'w') as f: json.dump(d,f,indent=2)
"
echo "[$(date)] Claude Code 追踪数据已清除"
```
```bash
# 添加执行权限
chmod +x ~/claude-privacy-clean.sh
# 可选:添加到 crontab 每天自动执行
# crontab -e
# 0 3 * * * ~/claude-privacy-clean.sh >> ~/claude-clean.log 2>&1
```
---
## 各级清理的影响对比
| 操作 | 对 Anthropic 的效果 | 对你的影响 |
|------|---------------------|------------|
| **重置 Device ID** | 无法关联历史使用数据 | 无感知,自动生成新 ID |
| **清除遥测** | 本地缓存的分析事件不会补发 | 无感知 |
| **清除历史** | — | 丢失命令历史和 ctrl+r 搜索 |
| **清除 OAuth** | 断开账号关联 | 需要重新登录 |
| **完全重置** | 等同全新用户 | 丢失所有自定义配置 |
| **禁用非必要流量** | 不再接收分析数据 | 特性开关可能不更新 |
| **使用 Bedrock/Vertex** | 分析代码完全不执行 | 需要云厂商凭证 |
---
## 附:数据文件速查表
| 文件/目录 | 包含的追踪数据 |
|-----------|----------------|
| `~/.claude.json` | userID, anonymousId, oauthAccount, 首次使用时间, GitHub 仓库映射 |
| `~/.claude/telemetry/` | 未上报的 1P 分析事件JSON含完整 EnvContext |
| `~/.claude/statsig/` | Statsig stable_id, 特性开关缓存 |
| `~/.claude/stats-cache.json` | 统计数据缓存 |
| `~/.claude/history.jsonl` | 完整命令历史(你输入的每一条提示词) |
| `~/.claude/sessions/` | 会话元数据 |
| `~/.claude/paste-cache/` | 粘贴内容的哈希地址缓存 |
| `~/.claude/file-history/` | Claude 做过的文件修改记录 |
| `~/.claude/debug/` | 调试日志 |
| `~/.claude/shell-snapshots/` | Shell 环境快照 |
| macOS Keychain → `claude-code` | OAuth access/refresh token |
---
*来自AI超元域 | B站频道https://space.bilibili.com/3493277319825652*
*基于 Claude Code 源码逆向分析2026-03-31*