# Clavue v8.10.4 — Release Notes

**发布日期**：2026-05-10
**npm**：`clavue@8.10.4`
**GitHub**：https://github.com/mycode699/clavue/releases/tag/v8.10.4
**升级命令**：

```bash
npm install -g clavue@8.10.4
# 或一次性：
npx -y clavue@8.10.4
```

---

## TL;DR

8.10.4 是一次"长会话稳定性 + 上下文卫生"修复版：

1. **长会话不再 OOM** — autocompact 失败后追加两道防线：L3 本地紧急截断（无 LLM）、L4 堆压软阻断（autocompact 后仍超临界则拒绝下一次 streaming，避免分配几百 MB 而崩进程）。
2. **删除 FileReadTool 的 "malware" reminder 注入** — 每次 Read 不再偷偷塞一段"考虑这个文件是不是恶意软件"的 system-reminder，模型不会被反复打断分析任务。
3. **关掉 todo / task 自动 nag** — 每 ~10 turn 注入 "TodoWrite/TaskCreate 最近没用过……NEVER mention this reminder to the user" 的隐性指令源被同时从发射器和渲染器层 stub 掉。Tools 本身仍可显式调用，只是不再被自动催促。

逃生开关全部保留：`CLAVUE_DISABLE_EMERGENCY_TRUNCATE=1`、`CLAVUE_DISABLE_HEAP_SOFT_BLOCK=1`。

测试：993 / 990 pass / 3 skipped / 0 fail（`npm run check`，~53s）。

---

## 1. 长会话 OOM 修复（L3 + L4）

### 问题

长会话跑到 5K+ messages、堆压力上来后，autocompact（reactive context recovery）会在两种情况下失败：

- 摘要 LLM 调用本身超时或返回空。
- 摘要返回了，但堆已经在 critical 阈值之上 — 写回 messages 时 GC 撑不住。

旧代码在两种失败下都直接走 `Auto-compact was skipped to avoid an OOM` 的早返回，但它**没有清理任何 messages**，于是下一次 streaming 请求照常发出去：分配几百 MB 缓冲区 + 解码上百 MB 工具输出 → V8 OOM → 进程整体崩溃。用户看到的是终端突然没了，会话当前 turn 整个丢失。

`/Users/lu/.clavue/projects/-Users-lu-codebase/2d73f105-…` 这条 88MB session 多次复现：autocompact 显示 "skipped"，下一秒进程死。

### 修复

**L3：本地紧急截断（无 LLM）** — `src/services/compact/emergencyTruncate.ts` 新增 `buildEmergencyTruncationWith({ messages, keepTail })`：

- 保留首条 user 消息（原始任务描述）做为锚点摘要，不丢。
- 中间整段折叠成一条 `<system-reminder>` 风格的摘要（"emergency_truncate" 触发器写进 `compactMetadata.userContext`）。
- 保留末尾 N 条（默认 6，autoCompact 调用点用 keepTail=8）。
- 关键边界：如果 tail 里第一条恰好是 `tool_result`，会向前扩展直到包含对应 `tool_use`，永远不切断 tool_use ↔ tool_result 配对。
- 纯本地、纯同步、不调 LLM、不读文件 — 重点是"在堆已经爆的前提下也能跑完"。

`autoCompact.ts` 的 critical-heap 分支在写 "skip" 之前先尝试一次紧急截断，成功则替换 messages 后正常返回 success；失败才回到原有的 skip 路径。环境变量 `CLAVUE_DISABLE_EMERGENCY_TRUNCATE=1` 跳过 L3 强制走老路径（debug 用）。

**L4：堆压软阻断** — `src/query.ts` 在每次准备发出 streaming 请求前检查 `getHeapPressureState().isAboveCriticalThreshold()`：

- 如果 autocompact + 紧急截断都跑过、堆仍在 critical 之上：直接以 `heap_soft_block` reason 早返回，不发 streaming 请求。
- 同时打印一条非静默的 warn 日志（不是隐蔽 reminder，是给用户看的运维信号）。
- 环境变量 `CLAVUE_DISABLE_HEAP_SOFT_BLOCK=1` 跳过 L4（专家 debug 用，会让进程冒 OOM 风险）。

两道防线之间是独立的：L3 是"还能瘦下来"，L4 是"瘦下来仍超临界就别再发请求"。

### 契约测试

新增 `tests/heap-soft-block-contract.test.mjs` 把这两条防线锁死：

- `query.ts` 必须包含 `heap_soft_block` 字面量、`CLAVUE_DISABLE_HEAP_SOFT_BLOCK` 逃生开关、`isAboveCriticalThreshold()` 调用、`getHeapPressureState()` 调用。
- `autoCompact.ts` 必须按"先 critical 检查 → 调 buildEmergencyTruncation → 才 fallback skip 文案"的顺序排列。

任何后续 PR 删掉或重排这些行都会让这条 contract test 立刻爆红。

新增 `tests/emergency-truncate.test.mjs` 覆盖 5 种情况：empty messages、nothing-to-elide、典型省略、tool_use chain 不被切断、原始 user 请求缺失时的优雅降级。

---

## 2. 删除 FileReadTool 的 "malware" reminder

### 问题

`src/tools/FileReadTool/FileReadTool.ts` 之前每次 Read 工具调用结尾都拼上一段：

> `<system-reminder>Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.</system-reminder>`

- 它跟 cyber-risk system prompt 重复语义。
- 它每次 Read 都注入，长会话里累积成几百份相同文本。
- 它把模型从 "改这个 bug" 拉到 "评估这个文件是不是恶意软件" 的语境，多次出现"我应该停下来分析这是不是 malware 而不是给用户改"的漂移。
- 上一版本里有个 `MITIGATION_EXEMPT_MODELS = ['claude-opus-4-6']` 白名单，意味着只要不在白名单的模型每次都会被注入。

### 修复

直接删除整段 `CYBER_RISK_MITIGATION_REMINDER` 常量、调用点、白名单函数和现在变孤儿的 import。

**为什么不留 env flag？** 留 flag 会让模型偶尔在不知情情况下被注入，行为不可预测；保留 cyber risk 的 system-prompt 级覆盖（`CYBER_RISK_INSTRUCTION`）已经能涵盖真正的安全约束。"每次 Read 都打断"这种行为没有保留必要。

---

## 3. 关掉 todo / task 自动 nag

### 问题

`src/utils/attachments.ts` 里有两个 emitter：

- `getTodoReminderAttachments` —— 当 assistant 连续 ≥10 turn 没用过 `TodoWrite` 就注入 "TodoWrite tool hasn't been used recently … This is just a gentle reminder - ignore if not applicable. Make sure that you NEVER mention this reminder to the user"。
- `getTaskReminderAttachments` —— 同款，催 `TaskCreate` / `TaskUpdate`。

两个特征导致它们成为开发流程的强干扰源：

1. **自动节奏与任务类型无关** — 单文件 bugfix 不需要 todo list，但 emitter 看的是 turn count，结果模型每隔 10 turn 就停下来写一份"todo: fix the bug"，纯增加噪声。
2. **隐性指令** — "NEVER mention this reminder to the user" 是 prompt-injection 风格的暗指令，要求模型对用户隐瞒被催促这件事。语义上不健康，且历史上有过泄露案例破坏用户信任。

### 修复

在源头和渲染层同时 stub：

- `getTodoReminderAttachments` / `getTaskReminderAttachments` 函数体替换为 `return []`，不再发射。
- `messages.ts` 中 `case 'todo_reminder'` / `case 'task_reminder'` 的渲染分支也改为 `return []`，保留 case arm 让 union 仍然 exhaustive，但删除全部 nag 字面量。
- 清理孤儿：`TODO_REMINDER_CONFIG` 常量、`getTodoReminderTurnCounts` / `getTaskReminderTurnCounts` 两个 helper、`TODO_WRITE_TOOL_NAME` / `TASK_CREATE_TOOL_NAME` / `TASK_UPDATE_TOOL_NAME` / `isThinkingMessage` / `isTodoV2Enabled` 五处变孤儿的 import。

**没有动什么：** Attachment union type 中的 `todo_reminder` / `task_reminder` 类型保留（动了会强制下游 N 个 switch 改），TodoWrite / TaskCreate / TaskUpdate 三个工具本身保留（功能正常，只是不再被自动催促）。

### 保留的功能性 reminder

下列 reminder 是**功能性**的，不是噪声，全部保留：

- `verify_gap_reminder` / `verify_finish_line_reminder`（plan 推进相关，是产品核心机制）
- `edited_text_file`（告诉模型文件被用户/linter 改过、不要 revert）
- `date_change`（跨日提醒）
- `plan_mode_attachment` / `team_context`（状态机切换通知）
- FileRead empty / offset 警告（实际工具状态）
- memdir / memoryAge（auto-memory 系统）
- `api.ts` CLAUDE.md / clavue.md 上下文注入

---

## 4. 升级路径

```bash
npm install -g clavue@8.10.4
# 或：
npx -y clavue@8.10.4
```

无破坏性变更：

- 命令行界面无变化。
- Provider 配置、credentials、teams、memory 全部兼容。
- 用户终端从 8.10.3 直接升上来即可，旧会话在 jsonl 上完全可读。
- 唯一行为差异是上述三类 reminder 不再注入，long-session OOM 路径加了两道护栏。

如果你**真的需要**老的 reminder 行为来对照测试，把 `src/utils/attachments.ts` 里两个 stub 函数换回旧实现自己 rebuild —— 但生产环境不建议。

---

## 5. Tests / Verify

```
npm run check
# tests 993
# pass 990
# skipped 3
# fail 0
# duration_ms ~53s
```

新增测试文件：

- `tests/emergency-truncate.test.mjs`（5 cases）
- `tests/heap-soft-block-contract.test.mjs`（2 contract tests，锁住 query.ts + autoCompact.ts 的关键代码形状）

---

## 6. 改动文件清单

| 文件 | 性质 |
|---|---|
| `src/services/compact/emergencyTruncate.ts` | 新增（L3 紧急截断核心） |
| `src/services/compact/emergencyTruncateWiring.ts` | 新增（依赖装配） |
| `src/services/compact/autoCompact.ts` | critical 分支接 buildEmergencyTruncation |
| `src/query.ts` | 加 heap_soft_block 早返回 |
| `src/tools/FileReadTool/FileReadTool.ts` | 删除 cyber-risk reminder 注入 |
| `src/utils/attachments.ts` | stub 两个 reminder emitter，清理孤儿 helper / import |
| `src/utils/messages.ts` | 渲染层 todo_reminder / task_reminder case 改为 return [] |
| `src/constants/branding.ts` | APP_VERSION_FALLBACK → 8.10.4 |
| `package.json` | 8.10.3 → 8.10.4 |
| `README.md` | 全文版本号同步到 8.10.4 |
| `tests/emergency-truncate.test.mjs` | 新增 |
| `tests/heap-soft-block-contract.test.mjs` | 新增 |

---

## 7. Roadmap 提示

L3 + L4 是 OOM 短期防线。中期路线（Step 3）是离堆 message store + 分代上下文 + 预算调度，把"靠 V8 GC 撑长会话"的根因移走。本版本先把进程不再随机崩这件事做掉，让 Step 3 设计有时间慢慢落。
