# Clavue v8.13.10 — Release Notes

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

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

---

## TL;DR

8.13.10 是一次"权限与上下文卫生"治本修复版，针对 v8.13.9 用户实战中暴露的 4 个底层痛点逐一治根：

1. **skill 目录不再被当作 config 整体保护** — 只有 `<root>/skills/<name>/SKILL.md`（唯一承载 `hooks:` 的清单文件）受 bypass-immune 保护。`.claude/skills/baidu-seo/scripts/keyword_inventory.php` 这类用户业务代码不再每次 Edit 都触发 safetyCheck ask。
2. **文件权限弹窗新增 "Always allow for this project" 选项** — 把信任写入 `.clavue/settings.local.json`，跨重启生效。再也不用每次冷启动重新放行同一个目录。
3. **reactive compact 可配置保留尾窗 N 轮 + warning/error 阈值真正分级** — `CLAUDE_REACTIVE_COMPACT_PRESERVE_GROUPS` 让多步 agent 场景下不会在压缩后只剩最后一条 assistant 消息。warning buffer 拉到 40k、error 仍是 20k，UI 提示有了真正的提前预警。
4. **critical-heap cooldown 不再 silent 失败** — autocompact 因冷却被抑制时，主动触发一次 GC 并向用户回传带 `/clear` 提示的 failureReason，session 不再"无声崩盘"。

逃生开关：`CLAUDE_REACTIVE_COMPACT_PRESERVE_GROUPS=<1-10>`、`CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=<1-100>`、`CLAVUE_DISABLE_EMERGENCY_TRUNCATE=1`。

测试：1634 tests / 1628 pass / 6 skipped / 0 fail（`npm run check`，~54s）。新增 15 个 subtest（4 个 contract test 文件）。

---

## 1. skill 目录从 bypass-immune 保护中解放

### 问题

v8.13.9 把整个 `.clavue/skills/` 和 `.claude/skills/` 都纳入 `isClaudeConfigFilePath` → safetyCheck ask 分支。意图是防止 `hooks:` frontmatter 被偷偷注入，但代价是：

- 用户在 `~/.claude/skills/baidu-seo/scripts/keyword_inventory.php` 上做的每次 surgical edit 都触发"危险目录"弹窗。
- 这条 ask 是 bypass-immune 的，连 `bypassPermissions`、`acceptEdits`、`trustedOps` 都关不掉，自动化 workflow 直接断在弹窗上。
- 真正能授予 hook 执行权的只有 `<root>/skills/<name>/SKILL.md`（被 `loadSkillsDir.ts:parseHooksFromFrontmatter` 解析）。深层 SKILL.md、scripts/、references/、assets 全是用户内容，不是 config。

### 修复

`src/utils/permissions/filesystem.ts` 新增 `isSkillManifestPath()`：

```ts
function isSkillManifestPath(filePath: string): boolean {
  return /(?:^|[/\\])\.cla(?:vue|ude)[/\\]skills[/\\][^/\\]+[/\\]skill\.md$/
    .test(normalizeCaseForComparison(filePath))
}
```

`isClaudeConfigFilePath` 删除 `legacySkillsDir`/`skillsDir` 两个变量及其 `pathInWorkingPath` 调用，末尾改为 `return isSkillManifestPath(filePath)`。`commandsDir/agentsDir/legacyCommandsDir/legacyAgentsDir` 仍受保护——slash command 与 agent 仍能在下次调用时授予执行权。

### 契约测试

`tests/skill-manifest-narrow-protection.test.mjs` 锁死：

- `isSkillManifestPath` 函数存在 + regex 精确到一级 `skills/<name>/SKILL.md`（大小写不敏感）。
- `isClaudeConfigFilePath` body 不再出现 `legacySkillsDir`/`skillsDir`，且 commands/agents 保护未动。
- mirror regex 跑 8 个正反路径用例：3 个 MUST match、5 个 MUST NOT match（含真实用户场景 `baidu-seo/scripts/keyword_inventory.php`）。

---

## 2. 文件权限弹窗：Always allow for this project

### 问题

旧弹窗 3 个选项：(1) Yes — 单次放行；(2) Yes, for this session — 关进程就丢；(3) No。

对于"我就是信任这个项目的子目录"的场景，用户必须每次冷启动都重新放行，或者手动去编辑 `.clavue/settings.local.json`。前者反复打断、后者门槛太高。

### 修复

`src/components/permissions/FilePermissionDialog/permissionOptions.tsx`：

- `PermissionOption` 类型新增 `{ type: 'accept-always' }` 变体。
- write/create 操作 + 非 `.clavue/.claude` 子树时，在 session 选项之后插入一个 `Yes, always allow edits in <dirName>/ for this project` 选项。

`usePermissionHandler.ts` 新增 `handleAcceptAlways`：调 `generateSuggestions(path, op, ctx, undefined, 'localSettings')` 把目录级 Edit allow rule 写到项目本地配置。

`src/utils/permissions/filesystem.ts` 的 `generateSuggestions` 多加一个可选 destination 参数：

- destination === 'session'：保持原行为（setMode + addDirectories）。
- destination !== 'session'：产出真正的 `addRules / Edit allow / <dir>/**` PermissionUpdate，因为 `addDirectories` 单独加进 workingDirectories 仍会被 step 9 ask 拦下，必须配 allow rule 才能跨重启生效。

### 契约测试

`tests/file-permission-always-allow.test.mjs` 锁死：

- 选项类型存在 `accept-always` 变体。
- options 列表 push `yes-always` 且 label 含 "for this project"。
- handler 字典注册 `accept-always` 且调用 `generateSuggestions(..., 'localSettings')`。
- `generateSuggestions` 签名暴露 destination 参数、默认 'session'。

---

## 3. Reactive compact 保留尾窗 + 真正分级预警

### 问题

**尾窗**：`planReactiveCompaction` 在压缩时硬编码"保留最后 1 个 API round group"。对于单 user 提示驱动的 agent 任务，最后一个 group 可能只是一条 assistant 文本，没有上下文，模型恢复后基本"失忆"。

**分级**：`WARNING_THRESHOLD_BUFFER_TOKENS` 与 `ERROR_THRESHOLD_BUFFER_TOKENS` 都是 20_000，`calculateTokenWarningState` 的两个布尔同时翻转，TokenWarning 组件的 `color={isAboveErrorThreshold ? 'error' : 'warning'}` 永远在 error 色——压根没有"提前预警"。

### 修复

`src/services/compact/reactiveCompactPlan.ts`：

```ts
function readPreserveCount(): number {
  const raw = process.env.CLAUDE_REACTIVE_COMPACT_PRESERVE_GROUPS
  if (!raw) return 1
  const parsed = parseInt(raw, 10)
  if (!Number.isFinite(parsed) || parsed < 1) return 1
  return Math.min(parsed, 10)  // 上限保护
}
```

`groups.slice(-preserveCount).flat()` 替代 `groups.at(-1)`。默认 1 保持向后兼容；多步 agent 场景设 2 或 3 即可恢复足够上下文。

`src/services/compact/autoCompact.ts`：

```ts
export const WARNING_THRESHOLD_BUFFER_TOKENS = 40_000  // was 20_000
export const ERROR_THRESHOLD_BUFFER_TOKENS = 20_000
```

约 2 个 Sonnet 工具轮的预警窗。warningThreshold = auto-compact - 40k < errorThreshold = auto-compact - 20k < autoCompactThreshold，三档严格单调。

### 契约测试

`tests/compact-trailing-window-and-ladder.test.mjs` 锁死：

- `planReactiveCompaction` 读 env 变量、默认 1、上限被 `Math.min(..., 10)` 钳制。
- 数值校验：warning buffer > error buffer > 0。
- 比较运算保持 `>=` 形式（防止后续 PR 改成 `>` 漏掉边界）。

---

## 4. Critical-heap cooldown 不再 silent 失败

### 问题

`decideAutoCompactFromUsage` 的 critical-heap 冷却逻辑（60s 时间窗 + 10% heap-relief gate）正确地阻止了 v8.12.0 报告的"每 50-120s 反复 autocompact 但 heap 不降"病态。但 `shouldAutoCompact` 返回 false 后，`autoCompactIfNeeded` 直接 `return { wasCompacted: false }`，没有 failureReason、没有用户可见信号。

结果：session 已经在 critical heap 上苟延残喘，模型质量持续下滑，用户毫不知情，直到进程 OOM。

### 修复

`src/services/compact/autoCompact.ts` 的 `autoCompactIfNeeded`：

```ts
if (!shouldCompact) {
  const heapPressure = getHeapPressureState()
  if (heapPressure.isAboveCriticalThreshold) {
    const freed = tryForceMajorGc('autocompact-cooldown-suppressed')
    return {
      wasCompacted: false,
      failureReason:
        `Memory pressure is critical (${usedMb}MB / ${limitMb}MB) but ` +
        `auto-compact is in cooldown to avoid thrashing. ` +
        (freedMb > 0 ? `A forced GC reclaimed ~${freedMb}MB. ` : '') +
        `If responses slow down, run /clear to free context manually.`,
    }
  }
  return { wasCompacted: false }
}
```

side effect 故意没有塞进 `decideAutoCompactFromUsage`——那个函数刻意保持纯函数测试友好。orchestration 副作用归 `autoCompactIfNeeded`，这是自然的边界。

### 契约测试

`tests/autocompact-cooldown-visibility.test.mjs` 锁死：

- `autoCompact.ts` 必须从 `../../utils/forceMajorGc(.js)` import `tryForceMajorGc`。
- 必须调用 `tryForceMajorGc('autocompact-cooldown-suppressed')`（唯一可识别 reason）。
- 必须返回带 `/clear` 字面量的 failureReason。
- 必须在 `isAboveCriticalThreshold` guard 内（无 false positive 风险）。

---

## Tests / Verify

```
$ npm run check
...
# tests 1634
# suites 0
# pass 1628
# fail 0
# cancelled 0
# skipped 6
# duration_ms 53870.569834
```

完整门禁通过：vendor:ripgrep → validate:repo → verify:dist → verify:source-build → check:source-purity → test。

dist 入口烟雾测试：

```
$ npm run rebuild:dist
✅ Dist build succeeded: dist/cli.js
   Entry smoke: 8.13.10 (Clavue)
```

---

## Changed files

| 文件 | 改动概述 |
|---|---|
| `package.json` | 8.13.9 → 8.13.10 |
| `src/constants/branding.ts` | `APP_VERSION_FALLBACK` 8.13.9 → 8.13.10 |
| `README.md` | 标题与所有 `clavue@8.13.x` 文本 |
| `content-artifacts/llms.txt` | 版本号 + 3 个 install 命令 |
| `content-artifacts/llms-full.txt` | 版本字段 + URLs |
| `src/utils/permissions/filesystem.ts` | `isSkillManifestPath` 新增；`isClaudeConfigFilePath` 收敛；`generateSuggestions` 接 destination |
| `src/components/permissions/FilePermissionDialog/permissionOptions.tsx` | `PermissionOption` 加 `accept-always`；新选项 push |
| `src/components/permissions/FilePermissionDialog/usePermissionHandler.ts` | `handleAcceptAlways` + handler 字典注册 |
| `src/services/compact/reactiveCompactPlan.ts` | `CLAUDE_REACTIVE_COMPACT_PRESERVE_GROUPS` env + 尾窗 N 实现 |
| `src/services/compact/autoCompact.ts` | warning/error 阈值分级；cooldown-suppressed 分支 GC + failureReason |
| `tests/skill-manifest-narrow-protection.test.mjs` | 新增（5 subtests） |
| `tests/file-permission-always-allow.test.mjs` | 新增（4 subtests） |
| `tests/compact-trailing-window-and-ladder.test.mjs` | 新增（3 subtests） |
| `tests/autocompact-cooldown-visibility.test.mjs` | 新增（3 subtests） |
| `tests/clavue-skills-paths.test.mjs` | 调整旧 anchor 兼容新结构 |
| `src/tools/FileEditTool/constants.ts` | 注释更新（指明 narrow carve-out） |

---

## 升级建议

- 老用户：直接 `npm install -g clavue@8.13.10` 升级，无破坏性变更。
- 多步 agent / 长会话用户：在 `~/.clavue/settings.json` 或环境变量加 `CLAUDE_REACTIVE_COMPACT_PRESERVE_GROUPS=2`，可显著改善 reactive compact 后的"模型失忆"。
- 频繁手动放行同一目录的用户：升级后选择新出现的 `Yes, always allow edits in <dir>/ for this project` 选项即可一次写入 `.clavue/settings.local.json`。
