# Clavue v8.10.6 — Release Notes

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

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

---

## TL;DR

8.10.6 是一次**专项 bug 修复**：把 L4 堆压软门从"只拦截，没出口"改成"拦截 + 真实恢复路径"。8.10.5 及之前版本在长会话堆压触顶后，`/clear` 是**无效逃生口**——下面解释为什么以及怎么修。

零破坏性改动，零行为回归，长会话用户从此可以靠 `/clear` 真正脱困而不必重启 CLI。

测试：1050 / 1047 pass / 0 fail / 3 skipped（`npm test`，~55s）。

---

## 1. 背景：为什么 `/clear` 在 8.10.5 之前是死的

8.10.4 引入的 L4 堆压软门（`src/query.ts:680`）逻辑：

```
heapUsed >= 80% × heapSizeLimit
  → 拒绝下一次 API 调用（避免 OOM 硬崩）
  → 提示 "Run /compact, /clear, or /exit"
```

**但 V8 的 GC 是 lazy 的**：`/clear` 在 `src/commands/clear/conversation.ts:109` 调用 `setMessages(() => [])` + 清空 `readFileState` / `loadedNestedMemoryPaths` / `fileHistory` / mcp 状态——**所有引用都正确丢弃了**——但 V8 不会立即回收。下一次 query 进来时 `process.memoryUsage().heapUsed` 仍然是 9886MB，软门继续拦截，用户看到一模一样的错误，唯一逃生路径变成"重启 + 加堆"，等于把"机制"打了折扣。

实测验证（`node --expose-gc`）：

| 阶段 | heapUsed |
|---|---|
| 长会话累积 5M messages | 2228 MB |
| `setMessages(() => [])` | **2228 MB**（drop ref 但 GC 没触发） |
| `global.gc()` | **7 MB**（回收 99.7%） |

## 2. 修复方案

三处 surgical 改动 + 一组单元测试：

### A. `src/utils/nodeHeapBootstrap.ts`：reexec 时配对 `--expose-gc`

启动时已经通过 reexec 加 `--max-old-space-size=12288`；这一轮顺手把 `--expose-gc` 一起加上。这是从 JS 端强制 major GC 的**唯一手段**，副作用为零（仅暴露 `global.gc()` 函数，不调用就什么也不发生）。

幂等：检测 `NODE_OPTIONS` / `execArgv` 是否已含 `--expose-gc`，已存在不重复添加。

```ts
// nodeHeapBootstrap.ts (new)
export function hasExposeGcOption({ nodeOptions, execArgv }) {...}

// resolveHeapBootstrapPlan now emits:
//   --max-old-space-size=12288 --expose-gc
```

### B. `src/utils/forceMajorGc.ts`：可复用 helper

```ts
export function isForceMajorGcAvailable(): boolean
export function tryForceMajorGc(reason: string): number | null
```

两个特性：

1. **静默 fallback**：当 `global.gc` 不存在（用户用 `CLAVUE_DISABLE_HEAP_REEXEC=1` 或直接 `node` 启动），返回 `null`，调用方据此降级。
2. **零依赖**：刻意不引入 logger，避免 ESM `.js` 后缀重写在 Node `--test` strip-types 下挂掉（同一 trade-off 此前在 `tokenBudget.ts` 测试里已经处理过）。

### C. `src/commands/clear/conversation.ts`：`/clear` 末尾触发 GC

```ts
// 所有引用清完后：
tryForceMajorGc('clear-conversation')
```

效果：`/clear` 完成后下一次 query 进入 L4 软门时，`heapUsed` 已经回落到正常水位，软门直接放行。`/clear` 终于成为真实的恢复路径。

### D. `src/query.ts:680`：软门多分支诊断

软门触发前先尝试 `tryForceMajorGc()` + 重测；如果回收后跌破临界，放行。如果 GC 后**仍然**超阈值，按三种情况给出**可执行**的指引：

| 情况 | 诊断消息 |
|---|---|
| 会话已空 + GC 已尝试 + 仍 ≥80% | "Conversation is empty but Xmb still resident after a forced GC. This points to a leaked large object (open MCP transport, background task buffer, or skill keeping references). Restart with CLAVUE_MAX_OLD_SPACE_SIZE_MB=…" |
| GC 不可用 | "Run /clear or /exit. (Tip: Clavue normally launches with --expose-gc so /clear can free memory immediately, but CLAVUE_DISABLE_HEAP_REEXEC appears to be set or you launched node directly. Restart without it to enable in-session recovery.)" |
| 普通情况 | "Run /clear to free memory in-session, or /exit and restart with CLAVUE_MAX_OLD_SPACE_SIZE_MB=… for a larger heap." |

`Math.max(16384, limitMb * 2)` 给出基于当前堆大小的**自适应建议值**，而不是硬编码的 16384。

## 3. 测试

新增 `tests/force-major-gc.test.mjs`：

- `isForceMajorGcAvailable()` 与 `typeof globalThis.gc` 一致。
- `tryForceMajorGc()` 在两条分支（`--expose-gc` 在/不在）都通过。
- 实测可用分支真的回收了 ~80MB 不可达内存。

更新 `tests/node-heap-bootstrap.test.mjs`：

- 新增 `hasExposeGcOption()` 双形式（`--expose-gc` / `--expose_gc` / execArgv）测试。
- `nodeOption` 断言更新为 `--max-old-space-size=12288 --expose-gc`。
- 新增"NODE_OPTIONS 已含 --expose-gc 时不重复添加"用例。

## 4. 行为对比

**8.10.5 / 8.10.4**：长会话踩临界 → 软门拦截 → 用户 `/clear` → 看上去成功 → 下一条消息**还是被拦截** → 只能重启 CLI（丢上下文）。

**8.10.6**：长会话踩临界 → 软门拦截 → 用户 `/clear` → V8 立即回收 → 下一条消息**正常发出** ✅。

极端情况下（真有 leak / 巨大 resident 对象）→ 软门给出**针对性诊断**，告诉用户具体怎么处理（自适应堆大小建议）。

## 5. 文件变更摘要

| 文件 | 变更 | 说明 |
|---|---|---|
| `package.json` | `8.10.5 → 8.10.6` | |
| `package-lock.json` | 版本号 bump | |
| `src/constants/branding.ts` | `APP_VERSION_FALLBACK → 8.10.6` | |
| `README.md` / `clavue.md` / 上级 workspace `clavue.md` | 版本字符串 | |
| `src/utils/nodeHeapBootstrap.ts` | reexec 配对 `--expose-gc` + `hasExposeGcOption()` 导出 | |
| `src/utils/forceMajorGc.ts` | 新增 helper：`isForceMajorGcAvailable()` / `tryForceMajorGc()` | |
| `src/commands/clear/conversation.ts` | `/clear` 末尾调用 `tryForceMajorGc()` | |
| `src/query.ts` | L4 软门：拦截前 GC + 重测；多分支可执行诊断 | |
| `tests/node-heap-bootstrap.test.mjs` | 更新断言 + 新增 `hasExposeGcOption` 测试 | |
| `tests/force-major-gc.test.mjs` | 新增 | helper 单元测试 |
| `dist/cli.js` | rebuild | |

无运行时新功能，无破坏性变更。

## 6. 升级路径

```bash
# 全局升级
npm install -g clavue@8.10.6

# 或一次性
npx -y clavue@8.10.6

# 验证
clavue --version  # 输出: 8.10.6 (Clavue)
```

- 用户终端从 8.10.5 / 8.10.4 直接升上来即可，配置文件、jsonl 会话、`~/.clavue/`、`~/.ccjk/` 全部保持兼容。
- 没有数据库迁移，没有 schema 变化。
- 环境变量逃生开关全保留（`CLAVUE_DISABLE_HEAP_SOFT_BLOCK=1`、`CLAVUE_DISABLE_HEAP_REEXEC=1`、`CLAVUE_MAX_OLD_SPACE_SIZE_MB=…`）。
- 升级后启动会自动 reexec 加 `--max-old-space-size=12288 --expose-gc`；如需关闭仅设 `CLAVUE_DISABLE_HEAP_REEXEC=1`。

## 7. 升级建议

如果你之前因为 heap soft-block 频繁重启 CLI：8.10.6 会显著改善。如果你跑超长 retro / world-class eval / 大量并行 sub-agent，建议同时设：

```bash
export CLAVUE_MAX_OLD_SPACE_SIZE_MB=24576  # 24GB，物理内存允许的话
```

8.10.6 之后重启不再是恢复堆压的唯一手段，但仍然是清理疑似 leak 的最干净方式。
