flavor-code 1.4.3-beta.2 → 1.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +25 -0
  2. package/README.zh-CN.md +25 -0
  3. package/dist/agent/loop.d.ts +1 -0
  4. package/dist/{app-G35YBHPR.js → app-TVY2HM5L.js} +342 -112
  5. package/dist/{chunk-FAJ6YDZK.js → chunk-36IDJRLQ.js} +1 -1
  6. package/dist/{chunk-HGTCIHWA.js → chunk-5BDKRA6U.js} +30 -1
  7. package/dist/{chunk-T6RE76D2.js → chunk-IKGT4MDT.js} +35 -23
  8. package/dist/{chunk-K7UGGWJV.js → chunk-JQM2L2YM.js} +1 -1
  9. package/dist/{chunk-6X62CGMA.js → chunk-ONRY3C2L.js} +3 -0
  10. package/dist/{chunk-IDJAHEG6.js → chunk-QACFKPZP.js} +391 -188
  11. package/dist/{chunk-SJUIIHIA.js → chunk-X6U6BO5D.js} +1 -1
  12. package/dist/{claude-ink-BE56DB6N.js → claude-ink-F554I3GJ.js} +1 -1
  13. package/dist/{cli-3MF6EL2H.js → cli-MAWCV2YR.js} +7 -7
  14. package/dist/cli-main.js +16 -16
  15. package/dist/config/protected-file.d.ts +2 -0
  16. package/dist/config/schema.d.ts +3 -0
  17. package/dist/context/global-instructions.d.ts +12 -0
  18. package/dist/context/manager.d.ts +2 -0
  19. package/dist/desktop/main.js +651 -419
  20. package/dist/desktop-renderer/assets/{index-DCqIrF1S.js → index-CjC0Ibiq.js} +2 -2
  21. package/dist/desktop-renderer/assets/{interactive-terminal-B6GbpzIn.js → interactive-terminal-IjIT3Xoa.js} +1 -1
  22. package/dist/desktop-renderer/index.html +1 -1
  23. package/dist/{doctor-OHCGK52S.js → doctor-SEL32R7M.js} +3 -3
  24. package/dist/harness/local.d.ts +1 -0
  25. package/dist/{load-QK6CMLDF.js → load-RHWXNUCS.js} +2 -2
  26. package/dist/{manager-5SI4MPES.js → manager-IYBNNKUP.js} +2 -2
  27. package/dist/{production-6DIZ6YLQ.js → production-TQROFEPV.js} +6 -6
  28. package/dist/sdk/index.js +6 -6
  29. package/dist/{store-ZIHN6363.js → store-MZZERW7K.js} +2 -2
  30. package/dist/ui/commands.d.ts +11 -3
  31. package/dist/ui/session.d.ts +2 -1
  32. package/package.json +1 -1
  33. package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +649 -1
@@ -1,6 +1,6 @@
1
1
  # flavor-code 技术方案报告
2
2
 
3
- > 版本:1.2.14 | 语言:TypeScript | 运行时:Node.js ≥20 | 包管理器:npm
3
+ > 版本:1.4.3-beta.2 | 语言:TypeScript | 运行时:Node.js ≥20 | 包管理器:npm
4
4
 
5
5
  ---
6
6
 
@@ -158,6 +158,15 @@
158
158
  - [40.6 配置与命令](#406-配置与命令)
159
159
  - [40.7 安全边界](#407-安全边界)
160
160
  - [40.8 文件地图与验证](#408-文件地图与验证)
161
+ - [41. 1.2.15 至 1.2.20 增量打磨:Git 工作流、护栏规则与插件沙箱](#41-1215-至-1220-增量打磨git-工作流护栏规则与插件沙箱)
162
+ - [42. 1.3.0 Durable Harness 与可靠性地基](#42-130-durable-harness-与可靠性地基)
163
+ - [43. 1.3.1 至 1.3.2 Electron 多项目与 Agent 工作台](#43-131-至-132-electron-多项目与-agent-工作台)
164
+ - [44. 1.3.3 至 1.3.15 控制通道、Hook 协议 v2 与新能力](#44-133-至-1315-控制通道hook-协议-v2-与新能力)
165
+ - [45. 1.3.16 至 1.3.21 长会话内存防护与退出纪律](#45-1316-至-1321-长会话内存防护与退出纪律)
166
+ - [46. 1.4.0 内置工具可靠性与跨进程堆轮换](#46-140-内置工具可靠性与跨进程堆轮换)
167
+ - [47. 1.4.1 至 1.4.2 终端交互与输出呈现升级](#47-141-至-142-终端交互与输出呈现升级)
168
+ - [48. 1.4.3 内置浏览器(Browser)](#48-143-内置浏览器browser)
169
+ - [49. 无头 CLI、命令行子命令族与 Prompt Cache 增强](#49-无头-cli命令行子命令族与-prompt-cache-增强)
161
170
 
162
171
  ---
163
172
 
@@ -280,6 +289,20 @@
280
289
  - **结构化输出强健化** — 模型以纯文本(含 ```json 代码块)返回 JSON 时自动提取;按 JSON Schema 对字符串形式的数字/布尔字段做类型强制转换后再校验,减少无效的修复模型调用
281
290
  - **只读工具声明** — 工具可声明 `readOnly`,只读工具在默认模式与 plan 模式下自动放行,与 Read/Glob/Grep 同等对待
282
291
 
292
+ **1.2.15 至 1.4.3 新增(摘要,各详见第 41–49 章):**
293
+
294
+ - **Git 工作流命令** — `/commit`、`/review`、GitHistory 只读工具、检查点附带 `branch@sha` git 快照(41)
295
+ - **evolve 护栏规则** — `kind=prompt_rule` 把重复失败直接沉淀为注入系统提示词的学习规则;`/evolve rule|trends`(41)
296
+ - **插件沙箱** — Worker + VM capability 隔离、RPC 贡献、资源限制;Task 节点 `files` 写入冲突防护;全局崩溃守护(41)
297
+ - **Durable Harness 与可靠性地基(1.3.0)** — fsync + 哈希链执行日志与崩溃恢复、Context Epoch、五层权限策略、Goal fail-closed 验收、session v4(42)
298
+ - **Electron 多项目与 Agent 工作台** — 多项目并行、git worktree 隔离任务、Session Time Machine、Review Workbench、loopback 预览、Context Inspector、代码图浏览器、Pals 可视化(43)
299
+ - **Flavor Island 控制通道与 Hook 协议 v2** — token 认证本机 IPC(abort/steer/follow_up/focus)、`toolCallId`、TaskSnapshot 推送、`/explain`、扩展思考流式展示、Maglev 轻量启动器(44)
300
+ - **长会话内存防护** — 七类无界增长逐条封顶、堆水位受控重启、3000 轮压测证据、Stop 钩子否决与守护插件、会话写租约、`flavor update`(45)
301
+ - **1.4.0 可靠性专项** — `flavor doctor`、Shell 结构化 argv 与诊断、内置工具地毯式审计、跨进程堆轮换与 `/loop`/`/goal` 自动续跑、OpenAI schema 兼容层(46)
302
+ - **终端交互升级** — `Esc` 中断、历史持久化与 `Ctrl+R`、`Ctrl+O` 完整输出模式、折叠语义解耦、macOS 复制粘贴(47)
303
+ - **内置浏览器(1.4.3)** — 快照引用 + 精准操作闭环、可视化覆盖层、SSRF/脱敏安全策略、八个 Browser 工具(48)
304
+ - **无头 CLI 与子命令族** — `--print` 三种输出格式与工具白名单、`sessions`/`config`/`usage` 命令、冷启动优化、ApplyPatch 重定位、Anthropic/OpenAI 缓存协议增强(49)
305
+
283
306
  ### 2.2 非目标(明确排除)
284
307
 
285
308
  - 全局用户画像、云端记忆同步和团队自动同步
@@ -5658,6 +5681,8 @@ CLI 侧对应 `/ast` 命令:`/ast init | sync | status | search <query> | call
5658
5681
 
5659
5682
  ### 39.5 插件形态与懒加载
5660
5683
 
5684
+ > **注意**:自 1.4.0-beta.2 起,astgraph 已从「`flavor init` 自动复制的内置插件」改为**手动安装的可选插件**:`flavor init` 不再自动复制、构建不再把该目录(含约 9MB 的 tree-sitter WASM 语法与 vendored zod)打进发行包;需要代码图能力的工作区自行将 `src/init/astgraph/` 复制到 `.flavor/plugins/astgraph/` 安装,`FLAVOR.md` 的 Search 章节仍按 `.flavor/astgraph/index.db` 是否存在自动生成。以下按 1.2.13 当时的形态描述。
5685
+
5661
5686
  - `src/init/astgraph/flavor-plugin.json`:`apiVersion: 1`,声明 1 个命令、5 个工具、SessionStart 与 PostToolUse 两个钩子,`permissions: []`
5662
5687
  - `index.js` 的 `activate(context)` 只注册 handler;`grammars.mjs` 通过 `import.meta.url` 解析 vendor 目录,使其在源码树(`src/init/astgraph`)与复制后的插件根(`.flavor/plugins/astgraph`)下都能工作
5663
5688
  - WASM grammar(typescript / tsx / javascript)与 `tree-sitter.js` 运行时按语言缓存,首次使用时加载一次
@@ -5848,3 +5873,626 @@ flowchart LR
5848
5873
  | `src/tools/shell.ts` | 弱类型模型命令分词(整行命令拆为 command + args),保证 Shell 失败旁路在 Windows 下稳定 |
5849
5874
 
5850
5875
  对应测试位于 `tests/evolve/{store,service,loader}.test.ts`、`tests/plugins/host.test.ts`、`tests/tools/shell.test.ts`、`tests/cli/commands.test.ts`、`tests/cli/session.test.ts` 与 `tests/config/load.test.ts`。
5876
+
5877
+ ---
5878
+
5879
+ ## 41. 1.2.15 至 1.2.20 增量打磨:Git 工作流、护栏规则与插件沙箱
5880
+
5881
+ 1.2.14 之后的六个补丁版本没有引入全新子系统,而是围绕三条线收紧体验与安全:**把 Git 日常操作变成一条命令**、**让 evolve 的教训沉淀成规则而不只是插件**、**给插件执行加上真正的隔离层**。同时修复了 Windows 退出链路、macOS 剪贴板等一批平台性问题。
5882
+
5883
+ ### 41.1 原生 Git 工作流命令(1.2.15)
5884
+
5885
+ - **`/commit [hint]`** — 为暂存变更生成 Conventional Commits 风格的提交信息,确认后执行提交。无暂存内容时先询问是否 `git add -A`;`hint` 作为方向性提示传给模型
5886
+ - **`/review [focus]`** — 对未提交变更做结构化审查,输出 verdict(`ship` / `ship-with-fixes` / `needs-work`)、按严重级别分组的 finding(`critical` / `warning` / `nit`)与修复建议,并额外提示 diff 之外的未跟踪文件
5887
+ - 两者都使用**廉价子代理模型**,模型不可用时优雅降级:`/commit` 回退为确定性消息(`chore: update <scope>`),`/review` 明确报错而非静默通过
5888
+ - **GitHistory 只读工具** — 查看仓库或单文件提交历史(跟随重命名,默认 20 条、上限 50),Agent 无需拼写裸 git 命令
5889
+ - **会话检查点附带 git 状态标记**(`branch@sha`,工作区脏时加 `-dirty`),`/tree` 各节点可直接看到对应的工作区 git 快照
5890
+
5891
+ ### 41.2 evolve 学习型护栏规则与趋势仪表盘(1.2.15)
5892
+
5893
+ 修复插件(第 40 章)适合「工具行为本身有问题」的场景,但很多重复失败其实是**模型的使用习惯问题**——用护栏规则一句话就能约束,不需要写代码。1.2.15 为此给 evolve 补上第二种落地形态:
5894
+
5895
+ - `/evolve rule list|add <text>|remove <id>` — 管理规则,持久化到 `.flavor/evolve/rules.json`,按文本指纹去重(上限 20 条),以 `# learned guardrails (evolve)` 章节注入未来的系统提示词
5896
+ - `evolve_improve` 工具新增 `kind=prompt_rule`:把重复失败建议直接沉淀为护栏规则并标记完成,无需编写修复插件
5897
+ - `/evolve trends [n]` — 跨运行仪表盘:最近 n 次运行的模型/工具调用数、失败数与 signalDelta,以及按工具拆分的移动明细(默认 5 次、最多 50 次)
5898
+
5899
+ ### 41.3 并发安全与全局崩溃防护(1.2.15)
5900
+
5901
+ - **子代理写入冲突防护** — Task 节点可声明 `files`(该任务拥有的文件,路径分隔符归一化比较),**文件重叠的任务绝不并行调度**,防止并发写坏同一文件
5902
+ - **全局崩溃守护** — CLI 与桌面端安装 `unhandledRejection` / `uncaughtException` 处理器:写脱敏崩溃日志 `.flavor/crash-*.log`(仅当前用户可读)、尽力恢复终端(光标与主屏)后带诊断信息退出
5903
+ - 安全侧将 `.npmrc` 加入 `.gitignore`,避免项目级 npm 认证令牌被误提交
5904
+
5905
+ ### 41.4 桌面端与 Shell 通道增强(1.2.16)
5906
+
5907
+ - **E2E 交付运行状态查看** — 工作台按七节点流水线(需求 → PRD → 交互设计 → D2C → API 联调 → 自主验收 → 成果交付)实时展示节点状态(`done` / `active` / `waiting` / `stale` / `failed`),经新增 `getE2eDeliveryRun` IPC 通道以只读快照读取状态机
5908
+ - API 联调完成记录改存**真实文件内容指纹**(`artifactRef`)而非文件名
5909
+ - **Windows Shell 通道重构** — 按能力自动选择 shell(PowerShell 7 → Windows PowerShell → cmd.exe 回退),参数经 `-EncodedCommand`(UTF-16LE base64)原样传递含空格、引号与 `&` 的内容,`exit $LASTEXITCODE` 保留真实退出码,`chcp 65001` 统一 UTF-8 解码
5910
+ - OAuth 授权与令牌 URL 从局域网地址改为回环地址 `127.0.0.1`
5911
+
5912
+ ### 41.5 登录生命周期与会话恢复正确性(1.2.17)
5913
+
5914
+ - **`/logout`** — 清除本地 OAuth 凭据(`auth.json`),注销所有 PKCE 托管的认证提供者与模型注册,回退到 apiKey/env 配置的模型并同步更新 main/subagent 模型、幻觉防护与目标编排器;未登录时给出明确提示
5915
+ - `/login` 切换为「单凭据」语义:登录成功后仅保留当前服务的令牌
5916
+ - 模型选择持久化改为**保存解析后的决策而非 harness 快照**——登出后从旧会话恢复时,不再把已注销服务的模型当作当前模型,旧服务的刷新令牌随登出一并清除
5917
+
5918
+ ### 41.6 退出纪律与渲染修复(1.2.18)
5919
+
5920
+ Windows 上 Ctrl+C 偶发无法退出的根因是「优雅关闭 → 退出」链路中任一清理步骤挂起(MCP stdio、pals 命名管道、IDE 会话、持久化等)就会吞掉后续按键。修复建立了一套**退出预算**模式,成为后续版本的基础设施:
5921
+
5922
+ - 优雅关闭仍在进行时,**二次 Ctrl+C 立即强制退出**
5923
+ - **关闭看门狗**:优雅关闭整体限时 8 秒,超时先恢复终端模式再强退
5924
+ - **清理步骤逐步超时**:dispose 的每个异步步骤(协作事件泵、睡眠调度、记忆刷新、持久化、IDE 会话、执行环境、后台任务、插件卸载、MCP 关闭等)各自限时 3 秒,超时即放弃该步并记录诊断,其余步骤继续
5925
+ - 修复终端 Markdown 单行代码块在滚动/重绘时偶发丢失(渲染区域标记 opaque,防止 ScrollBox 增量渲染器用空白单元格覆盖)
5926
+
5927
+ ### 41.7 插件沙箱:Worker + VM capability 隔离(1.2.19)
5928
+
5929
+ 回应第 12 章遗留的信任问题——插件此前以宿主进程内普通 ESM 模块运行,拥有完整 Node.js 能力。1.2.19 在 `PluginHost` 新增 `sandbox` 选项:
5930
+
5931
+ - 开启后插件在**独立 V8 isolate 的受限 module realm** 中激活,仅允许加载插件根目录内的相对模块
5932
+ - 宿主能力通过**校验后的 RPC** 调用贡献(协议见 `src/plugins/sandbox-protocol.ts`,worker 入口 `src/plugins/plugin-worker-entry.ts`)
5933
+ - 内置资源限制:128MB 老年代内存上限、10s 激活超时;超时或崩溃安全降级为失败报告
5934
+ - `/evolve verify` 的影子干跑同步升级:在受限 realm 中阻止 Node.js 内置模块、包依赖、宿主文件系统与网络 API
5935
+
5936
+ ### 41.8 Skill 组合语义与 SessionStart 上下文持久化(1.2.20)
5937
+
5938
+ - **只读 `Skill` 工具** — 运行中的组合 Skill 可按名称继续加载依赖 Skill,接受 `superharness:test-driven-development` 这类插件限定别名,恢复跨宿主工作流的 Skill 组合语义(这也是本条消息开头 superharness 指令「用 Skill 工具加载子技能」能成立的原因)
5939
+ - **Claude 风格 Skill 参数展开** — `$ARGUMENTS`、`$ARGUMENTS[N]`、`$N`,含引号与转义解析;未声明占位符时以 `ARGUMENTS:` 兜底追加
5940
+ - **`SessionStart` Hook 的 `additionalContext`** 现在在首轮模型调用前写入持久会话上下文,恢复会话时按内容去重——插件注入的工程规则不再被静默丢弃
5941
+
5942
+ ### 41.9 文件地图与验证
5943
+
5944
+ | 路径 | 职责 |
5945
+ |------|------|
5946
+ | `src/git/{service,tools,insights}.ts` | GitHistory 工具与 git 状态标记服务 |
5947
+ | `src/ui/commands.ts` | `/commit`、`/review`、`/logout`、`/evolve rule|trends` 命令 |
5948
+ | `src/evolve/{store,service}.ts` | `rules.json` 护栏规则持久化与注入、`evolve_improve kind=prompt_rule` |
5949
+ | `src/plugins/{host,worker-host,plugin-worker-entry,sandbox-protocol}.ts` | Worker + VM 沙箱、RPC 协议、资源限制 |
5950
+ | `src/skills/` | `Skill` 工具、限定别名与参数展开 |
5951
+ | `src/desktop/` | E2E 七节点流水线状态与 `getE2eDeliveryRun` |
5952
+ | `src/session/tree.ts` | 检查点节点的 git 快照标记 |
5953
+
5954
+ 对应测试覆盖 evolve 规则、Task `files` 冲突防护、崩溃守护、插件沙箱加载与 Skill 参数展开(`tests/plugins/`、`tests/evolve/`、`tests/skills/`)。
5955
+
5956
+ ---
5957
+
5958
+ ## 42. 1.3.0 Durable Harness 与可靠性地基
5959
+
5960
+ 1.3.0 是一次**地基级重构**:此前崩溃恢复、Prompt Cache、权限合并与 Goal 验收各自依赖松散约定,1.3 把它们统一成可验证的可靠性契约(见 `docs/specs/2026-08-24-v1.3-reliability-contract.md`)。四个新支柱:
5961
+
5962
+ ```mermaid
5963
+ flowchart TD
5964
+ J["Durable Harness\nfsync + 顺序号 + SHA-256 哈希链"] --> R["崩溃恢复\n未确认 steering/follow-up 重放\n非幂等工具只中断不重放"]
5965
+ E["Context Epoch\n稳定前缀逐字节不变"] --> C["Prompt Cache\n动态上下文只在断点后追加"]
5966
+ P["五层权限策略\n托管/用户/项目/本机项目/session"] --> D["最严格决策合并\n内置硬拒绝不可降级"]
5967
+ V["Goal Verification\ncontract hash + diff hash"] --> F["fail-closed 验收\n验证缺失不算完成"]
5968
+ ```
5969
+
5970
+ ### 42.1 Durable Harness:崩溃可恢复的执行日志
5971
+
5972
+ `src/harness/journal.ts` 把运行时的关键事实——消息队列、turn、模型请求、工具调用、savepoint——以 **fsync 落盘 + 单调顺序号 + SHA-256 哈希链**写入本地日志(`src/harness/local.ts`)。三条设计原则:
5973
+
5974
+ 1. **恢复语义分级**:崩溃后,未确认的 steering / follow-up 消息可以安全重放(幂等);**非幂等工具只中断、绝不重放**——已经改过文件、执行过命令的操作宁可停在半途,也不重复执行
5975
+ 2. **哈希链防篡改**:每条记录携带前一条的摘要,`src/harness/invariants.ts` 在加载时校验链完整性,发现前缀轮转或损坏可识别
5976
+ 3. **有界增长**(1.3.4 修复):journal 曾把完整模型消息、turn prompt、工具输入与结果全量落盘,长会话很快撞 32 MiB 上限且 `/compact` 无法恢复。修复后大对象**只记 SHA-256 哈希 + 崩溃恢复所需元数据**;达到容量边界时自动压缩已完成历史,仅保留待处理队列、未完成 turn、模型请求、工具调用与最后 savepoint;旧格式与前缀轮转过的 journal 在加载时自动校验、迁移、压缩
5977
+
5978
+ ### 42.2 Context Epoch:缓存友好的上下文布局
5979
+
5980
+ 第 6 章的缓存优化在 1.3.0 升级为显式的 **Context Epoch** 概念:
5981
+
5982
+ - 一个 epoch 内,稳定前缀(system sections、FLAVOR.md、工作区指令、用户记忆)**逐字节不变**——这是 Prompt Cache 命中的充要条件
5983
+ - 动态上下文(任务记忆、Task state、hook/skill 更新)一律在 cache 断点**之后按时间追加**,不再插入前缀
5984
+ - **压缩显式开启新 epoch**:压缩必然改写前缀,与其让它意外发生,不如把它建模为 epoch 边界
5985
+ - 动态上下文刷新采用 **stale-while-revalidate**:本轮先用旧值继续请求,后台刷新新值供下一轮使用;本轮实际发给模型的 Hook/Skill 内容进入**持久 visibility log**,保证「模型看见了什么」可审计、不污染后续轮次(1.3.4/1.3.17 为其加上 1000 条 / 2048 字符的双维限界,见第 45 章)
5986
+
5987
+ ### 42.3 五层权限策略
5988
+
5989
+ `src/permissions/policy.ts` 把此前单一的权限配置拆为五层,自上而下:**托管层(managed)→ 用户层 → 项目层 → 本机项目层(local project)→ session 层**。要点:
5990
+
5991
+ - 每层支持 token 前缀匹配与**自测样例**(配置里带一条样例命令,加载时验证规则确实匹配它,防止写出永远不命中的死规则)
5992
+ - **遮蔽诊断**:某层的规则被上层同名规则覆盖时给出诊断,不静默失效
5993
+ - 多层决策按**最严格合并**(deny 永远赢);内置硬拒绝(如凭据文件读取)**不可被任何层降级放行**
5994
+
5995
+ ### 42.4 Goal Verification 与 fail-closed 验收
5996
+
5997
+ 第 28 章的 `/goal` 对抗性审查在 1.3.0 获得不可变证据锚点:每轮验收记录 **contract hash**(目标契约的摘要)、**Git diff hash**(本轮实现的实际差异)、宿主验证结果与持久 evidence rounds。验收改为 **fail closed**:宿主测试失败、缺少验证命令、分类器故障或无效 skeptic 输出,任何一种都**不能**误报完成——「不确定」一律按未完成处理。
5998
+
5999
+ ### 42.5 安全默认与工程化
6000
+
6001
+ - 产品插件**默认启用沙箱**(1.2.19 能力的推广);加载元数据携带**内容指纹与能力声明**,进程内兼容模式需要 capability + 指纹信任双门槛才放行
6002
+ - 默认沙箱在 1.3.3 回滚为显式 opt-in:astgraph、superharness 等依赖 Node.js API 的既有插件无法在 Worker/vm 沙箱中加载,导致 `/ast` 与插件 Skill 消失。文件系统代理完成前,沙箱默认值不再切换——这是一次「安全改进破坏存量兼容」的教科书案例,处理方式(快速回滚默认值、保留机制、记录切换前提)比回滚本身更有参考价值
6003
+ - **Session schema 升级至 v4**:v1/v2/v3 迁移前保留**独占原始备份**;事件日志随会话删除与保留策略同步管理
6004
+ - CI 扩展至 Windows、macOS、Ubuntu × Node.js 20/24 矩阵,为 durable journal、Prompt Cache、迁移、权限与验收各增设独立可靠性门禁
6005
+
6006
+ ### 42.6 文件地图与验证
6007
+
6008
+ | 路径 | 职责 |
6009
+ |------|------|
6010
+ | `src/harness/journal.ts` | fsync + 顺序号 + 哈希链的持久化日志、容量压缩与迁移 |
6011
+ | `src/harness/invariants.ts` | 恢复不变量:幂等重放 / 非幂等中断的判定 |
6012
+ | `src/harness/local.ts` | 本地存储适配 |
6013
+ | `src/permissions/policy.ts` | 五层策略、自测样例、遮蔽诊断、最严格合并 |
6014
+ | `src/context/manager.ts` | Context Epoch 布局、stale-while-revalidate、visibility log |
6015
+ | `src/goal/{store,types}.ts` | contract hash、evidence rounds 持久化 |
6016
+ | `src/session/store.ts` | v4 schema 与迁移备份 |
6017
+ | `docs/specs/2026-08-24-v1.3-reliability-contract.md` | 恢复、缓存、安全与发布不变量的成文契约 |
6018
+
6019
+ 对应测试覆盖 journal 大对象去重、容量自动压缩、未完成非幂等工具恢复、前缀轮转兼容、epoch 前缀字节稳定性、权限层合并与 Goal fail-closed 验收。
6020
+
6021
+ ---
6022
+
6023
+ ## 43. 1.3.1 至 1.3.2 Electron 多项目与 Agent 工作台
6024
+
6025
+ 1.3.1 与 1.3.2 把桌面端从「一个项目一个会话的聊天窗」升级为**多项目、多任务并行、全过程可视化**的 Agent 工作台。所有新能力都遵守两条铁律:逻辑保持在 `src/desktop/`,共享运行时只做**加法式**读取钩子(CLI 的命令、默认值、输出与持久化行为完全不变);一切 IPC 走严格 Zod 契约 + typed preload。
6026
+
6027
+ ### 43.1 多项目与并行任务(1.3.1)
6028
+
6029
+ - 同时打开并切换多个项目,每个项目保留**独立运行时、会话与对话缓存**;切换项目**不中止**后台执行
6030
+ - 同一项目最多 **4 个任务并行**,可独立切换、中止、继续;后台输出按项目与会话缓存
6031
+ - 侧栏按项目分组展示会话:运行中显示活动动效,完成后保留蓝色未读圆点;支持项目置顶、重命名显示、关闭、资源管理器定位、复制路径,任务搜索/重命名/置顶/归档
6032
+ - 持久活动中心 + 原生系统通知,区分完成 / 失败 / 等待确认 / 意外中断,点击通知定位到对应项目与任务
6033
+ - 前进/后退导航、`Ctrl+P` 项目切换器、`Ctrl+K` 命令面板、`Ctrl+N` 新建任务
6034
+ - **异常退出恢复条**:可恢复/查看或忽略上次被中断的任务
6035
+ - **Git 变更中心**:逐文件 Diff、暂存/取消暂存/还原、提交、一键交给 `/review`;重做后的 Diff 工作台有双行号、整行红绿色块、hunk 分隔、文件状态徽标与工作区/已暂存层切换
6036
+
6037
+ ### 43.2 Git worktree 隔离任务(1.3.2)
6038
+
6039
+ 新任务可选「当前检出」或**应用托管的隔离 worktree**:隔离任务使用 `flavor/desktop-*` 分支,界面可查看 dirty/merged 状态、显式合并交付、保留分支或安全移除;**工作树脏且未确认时拒绝清理**。这让「让 Agent 大胆改」与「别弄脏我的工作区」第一次在桌面端同时成立。
6040
+
6041
+ ### 43.3 Agent 工作台
6042
+
6043
+ 把散落在 CLI 各处的执行状态集中可视化的单一视图:
6044
+
6045
+ | 面板 | 内容 |
6046
+ |------|------|
6047
+ | 执行轨迹 / 持久 Goal | 阶段进度与验证缺口 |
6048
+ | TaskPlan / 子 Agent / 后台 Job | 计划状态、子代理活动、Job 输出 |
6049
+ | Session Time Machine | 命名 checkpoint、回退、撤销回退、从任意节点 fork |
6050
+ | 项目 PTY 终端 | xterm.js 按需加载连接现有 `node-pty`:逐键输入、方向键、Ctrl 组合、粘贴、全屏 TUI、自适应 resize,可在桌面终端里跑交互式 `flavor` 本身 |
6051
+ | Review Workbench | working / staged / commit / base / last-turn 五种范围,文件与 hunk 导航、P0/P1/P2 审查提示、一键把选定文件交给 Agent 审查 |
6052
+ | 应用预览 | **仅 loopback** 的 HTTP(S) 预览,从终端与后台 Job 输出自动发现本地 URL,内嵌查看/刷新/复制/外部打开 |
6053
+ | Context & Safety Inspector | Context Epoch、visibility log、用量记录、项目指令、权限层、诊断与脱敏审计记录 |
6054
+ | 代码图浏览器 | AST 索引状态、符号搜索、可点击关系图(callers/callees/多跳影响范围),精确文件行号一键插入输入框 |
6055
+ | Pals / Co-work 可视化 | 本地 Pal 发现、Chat/Task 发送、共同目标启动与取消;在线实例轨道 + Pal 身份头 + 并排操作卡,区分直接沟通、异步委托与共同执行 |
6056
+
6057
+ 长期记忆卡片同步升级:distinct-task 调用次数、enamel 风格 hot/normal/cold 热度标签、按热度筛选与一次确认清理所有 cold 记忆。
6058
+
6059
+ ### 43.4 安全边界与关键修复
6060
+
6061
+ - 预览只接受 loopback HTTP(S);AST/审计读取有大小上限;敏感字段进入 renderer 前脱敏
6062
+ - 终端严格绑定当前任务目录与会话所有者
6063
+ - 修复 Electron 恢复会话时误用桌面主入口拉起 Pals broker 导致 `desktop:select-session` 超时:改用独立 broker entry,broker 暂不可用时不阻断任务,后续操作时自动重连而非永久丢弃
6064
+
6065
+ ### 43.5 验证
6066
+
6067
+ 1.3.2 配有 SDD 文档(架构边界、视觉方向、安全不变量、发布门禁);测试覆盖工作树生命周期/交付、Git 审查范围、时间机/终端/Pals 控制、VT 输出投影、逐键 PTY 交互、嵌套 Flavor CLI、AST 查询、上下文边界与记忆热度清理,含 Electron E2E(双项目打开、切换与视觉快照)。
6068
+
6069
+ ---
6070
+
6071
+ ## 44. 1.3.3 至 1.3.15 控制通道、Hook 协议 v2 与新能力
6072
+
6073
+ 这一段是 1.3 中期的能力扩展:对外接上了 **Flavor Island** 宿主控制通道,对内把 Hook 协议升级到 v2,同时给模型侧(思考流式展示)与用户侧(`/explain`、`/tool` 直调、长转录渲染窗口)各添了硬功能。
6074
+
6075
+ ### 44.1 Flavor Island 本地控制通道(1.3.7)
6076
+
6077
+ 加载 `flavor-island` 插件时自动启动**经 token 认证的本机 IPC 服务**(Windows named pipe / Unix socket),外部宿主可向运行中的会话发送 `abort`、`steer`、`follow_up` 与 `focus` 四类命令(`focus` 由宿主提供,Electron 已接入窗口聚焦)。发现机制:Hook 事件上下文新增 `islandControlEndpoint` / `islandControlToken` / `islandControlCapabilities` 元数据,宿主侧插件可安全连接而无需猜端点。配套观测扩展:
6078
+
6079
+ - `model-completed` 事件携带 `durationMs` 与 token 用量(`inputTokens`/`outputTokens`,可选 `cacheReadTokens`/`cacheCreationTokens`),可直接统计成本与延迟
6080
+ - `Stop` 事件新增 `summary`(最近助手文本摘录)与 `deliverables`(最多 100 个交付文件),宿主可在任务结束时展示成果概览
6081
+
6082
+ ### 44.2 Hook 协议 v2 与中继(1.3.5、1.3.6)
6083
+
6084
+ - **运行时元数据**:Hook 处理器收到稳定 `sessionId`、唯一 `eventId`、会话内 `sequence`、`timestamp` 与 `workspace`;元数据在 payload 校验后附加,**不会泄漏进 hook 修改后的工具输入**(防伪造)
6085
+ - **`toolCallId`**:工具生命周期四事件(`PreToolUse` / `PermissionRequest` / `PostToolUse` / `PostToolUseFailure`)携带稳定调用 id,外部 UI 得以正确匹配并行、乱序完成的只读工具调用
6086
+ - **审批分类**:`PermissionRequest` 新增宿主判定的 `toolCategory` 与 `allowAlways`——审批 UI 只对可缓存类别展示「本会话总是允许」,破坏性与不可缓存协作操作继续逐次确认
6087
+ - **`QuestionBridge` 中继**:`AskUserQuestion` 类交互先经 `PermissionRequest + AskUserQuestion` hook 中继,Flavor Island 可代答(当前使用者:`/commit` 与 `/go` loop budget);中继不可用或未给出完整答案时回退 TUI
6088
+ - **实时状态推送**:TaskPlan、Todo 与子 Agent 图的 `TaskSnapshot` 经 `Notification` hook 推送;`/go` 结束发 `LoopEnd`(loop outcome、终止原因、最后一次宿主验证证据)
6089
+
6090
+ ### 44.3 托管工具的斜杠直调与参数标准化下沉(1.3.11)
6091
+
6092
+ `RegisterTool` 注册的托管工具支持 `/<工具名> [JSON 对象]` 直接调用,另有不受名称冲突影响的稳定入口 `/tool <工具名> [JSON 对象]`;工具注册/删除后 CLI 与桌面端即时刷新斜杠补全。关键安全决策:斜杠调用**复用现有权限审批、Hook、审计日志、执行 journal 与输出限制**,不形成绕过安全边界的独立执行路径。同时把弱类型参数标准化(数组包装、JSON 字符串解码、字符串数字/布尔转换、可选 `null` 清理)**统一下沉到 `ToolRuntime`**——模型调用与斜杠调用共享同一防线,新增调用入口无法绕过。
6093
+
6094
+ ### 44.4 扩展思考的流式展示(1.3.12)
6095
+
6096
+ 模型「想给你看」的推理过程第一次可以安全地呈现:
6097
+
6098
+ - Anthropic 协议默认请求 **8192 token 思考预算**(`thinkingBudget`,0 表示关闭请求参数);OpenAI Responses 协议支持 `thinkingEffort`(`low`/`medium`/`high`,默认不下发避免不兼容网关报错)
6099
+ - CLI 状态行下方出现独立的固定宽度**思考行**,以打字机方式滚动最新推理文本;增量按动画节奏批量合并,本地累积上限 4000 字符只保留尾部
6100
+ - **边界纪律**:思考内容仅用于实时展示,不进入正文文本、不混入最终回复(`assistantText` 仍只累积 `text` 事件)
6101
+ - **签名回显**:思考块带提供商 signature 随助手消息保留;与 tool_use 同轮时按 Anthropic 要求逐字回显;上下文压缩过滤消息时同步保留 `thinkingBlocks`
6102
+ - **网关自动降级**:识别到 thinking 参数被拒(400 且错误指向该参数)后移除该参数重试一次,后续请求不再下发,并清理历史中已无签名要求的思考块——端点不支持时退化为普通流式而非报错
6103
+
6104
+ (1.4.0 系列又把 `thinkingEffort` 扩展为 `minimal`~`ultra` 六档并修复 OAuth 覆盖丢失,见第 46 章。)
6105
+
6106
+ ### 44.5 `/explain`:面向新人的符号讲解(1.3.13)
6107
+
6108
+ `/explain <符号 | file.ts#符号> [关注点]` 用**三路证据**生成五段式讲解(它是做什么的 / 关键实现点 / 调用关系 / 为什么这样写 / 新人注意事项):AST 代码图的 callers/callees 关系、符号真实源码切片(上限 20000 字符)、所在文件近期 Git 提交历史;由廉价子代理模型生成。工程细节:
6109
+
6110
+ - 多符号命中时弹出与 AskUserQuestion 同款的终端选择卡片(top 3 候选 + Cancel,以唯一 node-id 标注 kind 与文件:行号);自由输入更精确名称可原地重解析,最多追问 2 轮
6111
+ - **全链路优雅降级为提示文本**(对齐 `/review` 语义,不抛错):代码图未建立提示 `/ast init`、查询无命中提示 `/ast sync`、非 git 仓库跳过历史证据、模型不可用返回错误文本、含 `#` 的非法 node-id 自动回退为名称搜索
6112
+
6113
+ 实现位于 `src/explain/service.ts`,24+3 个单测覆盖解析、卡片回路、prompt 构造与全部降级路径。
6114
+
6115
+ ### 44.6 长会话渲染与流式合并(1.3.14)
6116
+
6117
+ 1.3.14 修复了三类「会话越长越卡」的问题,方法是把**有界窗口**引入展示层:
6118
+
6119
+ - 运行时转录不再为每个流式 token 复制完整回答,改为在模型、工具与持久化边界前**合并相邻文本/思考增量**,消除长输出的二次方分配
6120
+ - CLI 转录区增加**实时渲染窗口**:限制同时挂载的历史轮次、输出块与超长文本量,Ink/Yoga 节点树不随长任务无限增长;被隐藏的旧内容仍完整保存在会话中
6121
+ - Electron 按 50ms 合并相邻流式事件、缓存已完成轮次、限制实时 DOM 历史规模
6122
+ - `/explain` 讲解改为流式输出(增量渲染,不等完整生成)
6123
+ - 修复 CLI 输入草稿在同一批终端按键内的同步推进(连续退格偶发只删一个字符)与桌面端 mention 标签失效判断
6124
+
6125
+ 同期 superharness 升至 1.0.3(SessionEnd 检查点钩子、Before/AfterPlan 计划边界、SubagentStart/Stop 生命周期记录、原子写唯一命名、脑图端口 OS 分配)。
6126
+
6127
+ ### 44.7 搜索与杂项增强
6128
+
6129
+ - **`includeIgnored` 参数**(1.3.9)— Glob/Grep 可显式包含被 `.gitignore`/`.ignore` 排除的文件;`.git` 元数据目录两种后端下仍始终排除;1.3.10 补充字符串布尔(`"true"`/`"false"`)在工具边界的标准化,与 Shell/Terminal 输入约定一致
6130
+ - **长期记忆提取后台化**(1.3.8)— 任务结束后不再同步等待记忆模型调用,排队请求立即继续;收尾按任务隔离,旧任务的后台完成不会复活已失效的 review 卡片或覆盖新任务的生命周期槽;评估失败保留 `/finish` 手动重试
6131
+ - **错误分类收紧**(1.3.10)— HTTP 5xx 统一归类 `network` 按网络错误路径重试;`network` / `rate_limit` 不再附加「请配置 API key」的误导性提示
6132
+
6133
+ ### 44.8 Maglev 崩溃防护与轻量启动器(1.3.15)
6134
+
6135
+ Windows 11 build 26200 + Node.js ≥24 长会话存在 V8 Maglev **原生崩溃**(绕过一切 JavaScript 异常处理直接掉栈)。修复分两层:
6136
+
6137
+ 1. **CLI 入口拆分为轻量启动器与主程序**——受影响环境在任何 Ink、模型、插件或工具代码加载**之前**,自动用 `--no-maglev` 重新拉起主程序,用户无感
6138
+ 2. 启动器在规避进程运行期间**保留终端所有权**并把 Ctrl+C 交给真实 CLI,避免父进程提前退出、终端模式未恢复或子进程残留
6139
+
6140
+ 注意 1.3.17 依据真实 fatal report 推翻了部分判断(那次闪退实为 JavaScript heap OOM 而非 Maglev),移除了会降低优化的自动 `--no-maglev`——但启动器架构保留下来,成为后续轻量命令免重启与堆轮换的基础(第 45、46 章)。测试覆盖 Node 版本矩阵、已显式禁用 Maglev 与非受影响系统,保证升级 Node 不会静默绕过保护。
6141
+
6142
+ ### 44.9 文件地图与验证
6143
+
6144
+ | 路径 | 职责 |
6145
+ |------|------|
6146
+ | `src/island/control-server.ts` | token 认证本机 IPC 控制服务(abort/steer/follow_up/focus) |
6147
+ | `src/hooks/types.ts` | 协议 v2 元数据、`toolCallId`、`islandControl*`、`LoopEnd` |
6148
+ | `src/tools/runtime.ts` | 弱类型参数标准化下沉、斜杠直调复用安全管线 |
6149
+ | `src/models/` | thinking 参数下发、签名回显、网关降级 |
6150
+ | `src/explain/service.ts` | `/explain` 三路证据与降级链路 |
6151
+ | `src/ui/`、`src/desktop/` | 渲染窗口、流式合并、xterm 终端 |
6152
+ | `src/launcher.ts` | 轻量启动器与 Maglev 规避 |
6153
+
6154
+ 对应测试:Island 控制服务器(认证/非法命令/分派/能力声明)、Hook 元数据稳定性与不可伪造、QuestionBridge 中继回退、思考回归、explain 全降级路径、journal 容量、渲染窗口与合并、启动器参数矩阵。
6155
+
6156
+ ---
6157
+
6158
+ ## 45. 1.3.16 至 1.3.21 长会话内存防护与退出纪律
6159
+
6160
+ 1.3.16–1.3.17 是一次**由真实崩溃报告驱动的根因围剿**:线上出现长会话堆 OOM 闪退(崩溃时约 4.28GB/4.50GB),复盘确认不是单点泄漏,而是**多条无界增长路径同时叠加**。这一段的修复确立了两条长期原则:一切展示与日志结构必须有硬上限;崩溃不可免时,把它降级为无感重启。
6161
+
6162
+ ### 45.1 无界增长路径逐条封顶
6163
+
6164
+ | 增长源 | 症状 | 封顶措施 |
6165
+ |--------|------|----------|
6166
+ | 自动压缩触发太晚 | 会话贴着 ~92.8% 临界点运行 | 触发缓冲 13,000 → **27,000 tokens**(200K 窗口约 85% 处触发) |
6167
+ | provider usage 被动态刷新冲掉 | 162,047 token 的真实请求退回约 97K 本地低估值,持续绕过压缩 | usage 锚点**保留到完整压缩成功** |
6168
+ | 上下文可见性审计日志 | 每次会话保存全量序列化,长任务膨胀到数百 MB | **1,000 条 / 单条 2,048 字符**双维限界,恢复旧快照同样施加 |
6169
+ | Context update 公告堆积 | 新版取代旧公告仍留在窗口 | 每次模型调用前自动清理;固定暴露完整状态的动态源只推**追加增量**,`system-baseline` 保留完整最新值 |
6170
+ | UI timeline 永久保留所有 turn | 撞 5MB session 上限后**停止保存** | presentation history 限制最近 **200 turn / 约 200 万字符**,完成新 turn 与恢复旧 session 时立即收敛 |
6171
+ | Shell 输出截断 O(n²) 拼接 | 每块全量拼接产生巨额瞬时分配 | 分块队列、翻倍窗口才合并 |
6172
+ | 自动压缩连续失败 3 次永久熔断 | 长会话再也压不动 | 取消熔断;prompt-too-long 恢复默认**二分裁减**完整 API 轮次直到压缩请求可进窗口 |
6173
+
6174
+ 同期把 **checkpoint 改为显式创建**:取消普通 turn 完成后自动生成 rewind tree 与全工作区 checkpoint(`/checkpoint`、桌面端按钮或 `/tree` 系列操作才生成),普通路径不再产生 `tree.json` 与 `.flavor/checkpoints` 的持续开销。
6175
+
6176
+ ### 45.2 堆水位受控重启:把 OOM 降级为无感重启
6177
+
6178
+ 无法保证零泄漏,于是给进程装上**保险丝**(`src/utils/memory-restart.ts` + `src/launcher.ts`):
6179
+
6180
+ 1. 在迭代入口、上下文压缩后、模型请求物化后检查堆占用
6181
+ 2. 达到 V8 堆上限 **80%** 时以 `memory_pressure` 干净中断当前轮次并保存会话(**当前轮不自动重放**,避免重复执行有副作用的工具)
6182
+ 3. 进程按约定退出码结束,启动器立即以 `--resume` 在**全新堆**上恢复同一会话(30 分钟窗口内最多 3 次)
6183
+ 4. 取证配套:`--report-on-fatalerror`(V8 致命错误落 Node 诊断报告)与 `--heapsnapshot-near-heap-limit=1`(逼近上限自动落堆快照,可做 retainer 分析)
6184
+
6185
+ ### 45.3 压测证据链
6186
+
6187
+ - `npm run stress:production-memory`:构建后 CLI 独立进程 + 本地 Anthropic 兼容 SSE 服务,走真实 provider SDK、RPC、`Task` 子代理、1MB `Read` 输出、压缩、持久化与受限 V8 堆
6188
+ - 根因回归隔离复现原始崩溃会话:真实请求由 **851 条消息收敛为 14 条**
6189
+ - **3,000 轮实跑**:3,514 次主模型请求、100 次真实子代理、414 次大文件读取、342 次完整压缩;384MB old-space 下峰值 164.4MB(28.5%),强制 GC 后 102.5MB,前后段 P95 稳定,第 3,000 轮成功落盘
6190
+ - 851-message 大上下文上连续 120 个显式 checkpoint:峰值 29.0%,历史按字符上限收敛为 4 节点,随后照常完成子代理与压缩
6191
+ - `npm run stress:context`、`npm run evidence:crash-fix` 分别覆盖上下文内存场景与基于真实报告数字的可重复回归
6192
+
6193
+ ### 45.4 Stop 钩子否决权与守护插件(1.3.18)
6194
+
6195
+ 「Agent 宣称完成但没验证」的老问题,这次给了插件**硬否决权**:
6196
+
6197
+ - 会话每轮结束发射 `Stop` 钩子;钩子返回 `{ decision: "deny", reason }` 即阻止回合收尾,理由以 `[stop-guard]` 前缀的 follow-up 注入回模型,要求先完成验证再结束(`src/ui/session.ts`,`MAX_STOP_DENIALS = 2`:每条用户提交链最多续延 2 次防死循环,新提交重置计数)
6198
+ - 三个消费该机制的守护插件(部署在工作区 `.flavor/plugins/` 下):
6199
+ - **verify-gate** — 有源码改动但尚无成功验证(测试/构建)时否决 `Stop`;文档与生成文件改动不触发;不否决已取消回合
6200
+ - **secret-guard** — AWS 密钥/私钥/JWT/GitHub token/`.env` 写入触发询问且**不回显机密**
6201
+ - **edit-doctor** — 未闭合括号/字符串与非法 JSON 触发询问,损坏标记修复后自动清除
6202
+
6203
+ ### 45.5 升级与会话归属(1.3.19、1.3.20)
6204
+
6205
+ - **`flavor update`** — 查询 registry 最新版,高于当前则自动全局安装(Windows 用 `npm.cmd` 避免 spawn 失败);已是最新 / registry 不可达 / 安装失败三种输出分明,失败附 `npm i -g flavor-code` 兜底;欢迎卡片的更新公告改为提示 `flavor update`
6206
+ - **会话写租约(writer lease)** — 会话打开为可写运行时以**独占锁文件**登记归属进程;第二个实例打开或 `--resume` 同一会话立即报错(含占用方 PID),杜绝两进程互写同一 session 文件。回收路径完整:正常 dispose / 启动失败回滚 / 崩溃释放;遗留自死亡进程的锁按存活探测回收;损坏锁 30 秒宽限期后可回收;删除与 prune 跳过仍有活跃写进程的子会话;`/new` 切换先取新租约再释放旧租约(无保护窗口为零);崩溃守护新增 `registerCrashCleanup`,限时(默认 1 秒 + 看门狗)跑完清理,异常崩溃不会把写锁永久悬置
6207
+
6208
+ ### 45.6 Windows 子进程启动统一修复(1.3.20)
6209
+
6210
+ Windows 下钩子 shell 命令、LSP 服务器与 `flavor update` 的 npm 安装三类子进程启动失败/参数被破坏,统一收敛到新函数 **`prepareSpawnInvocation`**:按 `PATH`/`PATHEXT` 定位真实可执行文件;原生 `.exe`/`.com` 保留原始 argv 边界直接启动;**只有** `.cmd`/`.bat` 批处理(如 npm 的 `.bin` shim)经 `cmd.exe /d /s /c` 桥接并转义元字符;非 Windows 行为完全不变。回归测试真实拉起含空格与 `&` 元字符参数的 cmd shim 逐字透传验证。
6211
+
6212
+ ### 45.7 收尾杂项(1.3.21)
6213
+
6214
+ - macOS 粘贴图片在部分系统版本硬报错的根因很隐蔽:某些版本把 ObjC 的 nil 桥接成 **truthy 的 `$.nil` 代理对象**,`if (!data)` 判空防线失效。修复为三个环节(取 PNG、TIFF→PNG、base64 编码)先探测实际桥方法再调用,整段 fail-soft;CI 在 Windows/Linux 上以模拟 ObjC 桥**实际执行** JXA 脚本
6215
+ - CLI Markdown 围栏代码块边框/正文宽度改由 **Yoga 按父容器实际宽度计算**(不再用全局 `stdout.columns` 手工拼接),列表缩进、终端缩放、增量滚动后不再折行丢内容;仅改列数且全用百分比宽度时补做布局提交
6216
+
6217
+ ### 45.8 文件地图与验证
6218
+
6219
+ | 路径 | 职责 |
6220
+ |------|------|
6221
+ | `src/utils/memory-restart.ts` | 堆水位判定、`memoryRotationActive` 标志、受控重启协议 |
6222
+ | `src/launcher.ts` | `--resume` 自动恢复、诊断 flag 注入 |
6223
+ | `src/context/{manager,compaction}.ts` | usage 锚点、公告清理、二分裁减恢复 |
6224
+ | `src/ui/session.ts` | Stop 否决续延预算 `[stop-guard]` |
6225
+ | `src/session/store.ts` | 会话写租约获取/释放/回收、prune 保护 |
6226
+ | `src/update/{check,apply}.ts` | `flavor update` 的版本查询与安装执行 |
6227
+ | `.flavor/plugins/{verify-gate,secret-guard,edit-doctor}/` | 守护插件(Stop 否决与 PreToolUse 询问的消费者) |
6228
+
6229
+ 对应测试见各节「验证」;`npm run stress:production-memory` 与 `stress:context` 为常驻压测脚本。
6230
+
6231
+ ---
6232
+
6233
+ ## 46. 1.4.0 内置工具可靠性与跨进程堆轮换
6234
+
6235
+ 1.4.0-beta 系列(beta.1–beta.12)是 flavor-code 迄今最大的一次**可靠性专项**:先用 `doctor` 与结构化诊断把「工具为什么挂了」说清楚,再对全部内置工具做地毯式排查修复,最后把第 45 章的堆水位升级为**跨进程轮换**,让 `/loop`、`/goal` 这类可运行数日的任务获得工程意义上的长跑保障。
6236
+
6237
+ ### 46.1 本地诊断:`flavor doctor` 与 `/doctor`(beta.1)
6238
+
6239
+ 一次性体检环境:Node.js 版本、工作区与状态目录权限、配置有效性、Provider 凭据存在性、内置 ripgrep、命令 Shell、插件 manifest 与入口文件、npm registry 连通性。失败项令 CLI 非零退出;离线 registry 与未配置 Provider 记警告;`flavor doctor --json` 输出不含 API Key 的机器可读报告。诊断走**只读配置加载路径**(`seedGlobalEnv: false`),排查问题时绝不向全局 `.env` 写任何东西。
6240
+
6241
+ ### 46.2 Shell 工具重构与结构化诊断(beta.1、beta.3)
6242
+
6243
+ **执行链**:普通可执行程序直接按结构化 argv 启动,不再经 `shell: true` 与多层命令行重解析;只有内建命令、管道、重定向等操作符才进入由 doctor、系统提示与执行器**共同解析出的同一个运行时 Shell**(`resolveRuntimeShell`)。Windows `.cmd`/`.bat` 经受控 `cmd.exe` 桥接;可执行文件名解析统一为 Windows 优先当前目录、POSIX 校验 `X_OK` 可执行位。
6244
+
6245
+ **失败回执**:Shell 失败结果携带结构化诊断类型——命令不存在、路径不存在、权限不足、Shell 语法、超时、取消、普通非零退出——并附可操作提示,原始 stdout/stderr 不改写。evolve 信号(第 40 章)优先采用结构化诊断,错误码按 `shell_<诊断类型>` 记录(如 `shell_command-not-found`)。1.4.3-beta.1 又补上 zh-CN Windows 下 PowerShell 7 报错文案的识别。
6246
+
6247
+ 同期:Docker 执行环境新增 `maxOutputBytes`(默认 1MB,「头部+省略号+尾部」截断且不拆断 UTF-8,携带截断元数据)与升级式终止(SIGTERM → 250ms 宽限 SIGKILL → 5 秒强制结算);macOS 桌面端启动时以 2 秒预算继承登录 shell 的 PATH(解决 GUI 启动找不到 Homebrew 工具)。
6248
+
6249
+ ### 46.3 astgraph 转为可选插件(beta.2)
6250
+
6251
+ `flavor init` 不再自动复制 astgraph 插件,构建也不再将其约 9MB 的 WASM 语法与 vendored zod 打进发行包——显著减小 npm 安装包体积。需要代码图能力的工作区自行复制 `src/init/astgraph/` 到 `.flavor/plugins/astgraph/`;已安装工作区不受影响(第 39.5 节的批注即指向这里)。
6252
+
6253
+ ### 46.4 内置工具地毯式排查(beta.3)
6254
+
6255
+ 一次系统性的全工具审计,修复散布在十余个内置工具与共享执行链路上:
6256
+
6257
+ - **迭代轮次扩容并修复配置传递**:主 Agent 80→300、子 Agent 40→100、`extendBy` 20→50、loop `maxCycles` 20→100;主 Agent 标准模式有进展时自动延长至 450 轮、D2C 模式最高 800。`maxIterations.softLimitFactor` 与 `extendBy` 此前**根本没接进执行链路**(改了不生效),本版本真正打通
6258
+ - Grep 路径严格限定到文件、新增 `fixedStrings`、区分「路径不存在」与「零匹配」;Edit 双向兼容 LF/CRLF、拒绝重叠重复匹配;Read/Edit/ApplyPatch 拒绝文件末尾不完整 UTF-8
6259
+ - TerminalRead 保留游标/状态/退出信息,退出后的终端拒写;后台任务数量上限真正生效;`JobWait` 释放取消监听、`JobKill` 等待异步取消结果
6260
+ - LSP 支持数组悬浮、Windows 文件 URI、正确的 Python/Rust/Go 语言标识,服务不可用返回明确原因,支持取消,会话结束等待语言服务进程树退出
6261
+ - WebFetch 总超时覆盖 DNS 与重定向链、超大小立即停读;WebSearch 遵守结果数限制并响应取消
6262
+ - TaskPlan/TaskUpdate 阻止依赖未完成时提前开始,允许失败/阻塞任务重新进入执行;AskUserQuestion 取消后不因延迟返回重新弹出
6263
+ - 展示、上下文更新或结果落盘失败不再把已完成的操作误报为执行失败
6264
+
6265
+ ### 46.5 跨进程堆轮换与长任务续跑(beta.4、beta.5)
6266
+
6267
+ 第 45.2 节的单点保险丝升级为**体系**:
6268
+
6269
+ ```mermaid
6270
+ flowchart LR
6271
+ W["工作单元边界检查\nturn / loop 周期 / goal 轮次"] -->|"GC 核实后 >60% 软水位"| SAVE["持久化状态 + continuation 标记"]
6272
+ SAVE --> EXIT["退出码 75"]
6273
+ EXIT --> L["launcher 携 --resume\n在全新堆重启"]
6274
+ L --> RESUME["/loop 按已完成周期数续跑\n/goal 用 verifyRounds/workerRounds 消歧\n绝不重放已完成轮"]
6275
+ ```
6276
+
6277
+ - 水位判定 **GC 核实**(`--expose-gc` 可用时先完整 GC 再判,排除未回收垃圾误报)并同时覆盖进程 RSS:V8 堆硬/软 80%/60%,RSS 硬/软 75%/60%;launcher 默认注入 `--expose-gc`、`--report-on-fatalerror`、`--heapsnapshot-near-heap-limit=1`,堆 ≥12GB 时注入 `--max-old-space-size=8192`
6278
+ - 轮换预算防自旋:每小时窗口最多 24 次 + 5 分钟最小间隔冷却;continuation 有效期 10 分钟 → **24 小时**(覆盖慢启动、主机重启、无人值守唤醒)
6279
+ - 每次轮换自动留存**保留普查**(各内存持有者自报 entries/chars/bytes、V8 堆、RSS、external、ArrayBuffer 水位);CLI 子进程启用 1MB 间隔稀疏 allocation sampling,正常退出删除,轮换/异常退出保留 `.flavor/tmp/*.heapprofile`
6280
+ - `/loop`、`/goal` worker 完成后持久化「待验证」检查点——验证器/审查阶段发生轮换时,新进程**只重做可重复的验证,不重放已完成的工具工作**
6281
+ - 终止哲学改变:取消 `/loop` cycle/token 人工确认与 `/goal` 轮数/重复 gap 数字终止,长任务持续到宿主验证通过、用户显式取消,或遇到认证失败/模型不存在等**重试无法解决**的外部阻塞;限流、断流、输出/上下文限制等可恢复错误只是**执行段边界**(worker 单段 300 迭代上限到达时,已完成效果作为检查点持久化,由宿主验证决定成功或携带差距进入下一段)
6282
+ - 普通 turn 达到迭代/token/上下文边界写入**持久化自动续跑**;若同时触发堆轮换,续跑保持未领取、由新进程恢复(刷新后不再停在输入提示符)
6283
+ - 长任务状态自身也有界:只保留最近 64 个 cycle 明细,总计数单独累计;每条宿主验证命令的 stdout/stderr 各限最近 1KB 并显式标记截断(防止高日志测试撑破 1MB/5MB 状态文件)
6284
+
6285
+ ### 46.6 泄漏根因三连修(beta.5、beta.6)
6286
+
6287
+ 1. **模型请求级**:同一 turn 级 `AbortSignal` 曾被复用于数百次 SDK 请求,请求控制器与流对象存活到整轮结束。改为每请求可释放的派生信号,请求结束即断开父监听并清理 SDK 一次性监听
6288
+ 2. **展示层**:单个展示 turn(可运行数日的 `/loop`、`/goal`)严格有界——仅保留最近 160 个展示块、64K assistant 文本,裁掉超大工具输入/结果/diff;模型上下文与落盘执行证据不受影响;轮换普查新增 transcript blocks/chars 维度
6289
+ 3. **最隐蔽的一个**:发行 CLI 曾错误打包 `react-reconciler.development.js`,React 19 每次 commit 调 `performance.measure()`,Node User Timing 缓冲区不自动清空,**普通 turn 中累积约 7GB**(beta.5 真实 OOM 的 allocation profile 定位)。修复:CLI 构建固定折叠 `NODE_ENV=production`;launcher 强制子进程生产环境;交互 TUI 每 250ms 清理 marks/measures;构建后扫描全部发行 bundle,再出现 development reconciler 标记**直接构建失败**;census 新增 `userTiming` entry 数
6290
+
6291
+ ### 46.7 CLI 双轨工作台与嵌套项目 LSP(beta.7)
6292
+
6293
+ - 任务进度改为**自适应双轨**:宽终端左侧主任务计划、右侧子 Agent 探索,从首项同步可见;只有一类工作时占满整行;窄终端纵向回落;长内容流式输出期间动作行移到最新正文下方并切换为 `Writing response`,不再提前移除活动提示
6294
+ - LSP 修复 macOS/monorepo/sandbox 场景误报缺 `tsconfig.json`:从目标文件**向上选择最近项目根**,按「语言 + 项目根」隔离复用连接;销毁等待服务进程真正退出(Windows 目录锁);TypeScript LSP 补 `.js/.jsx/.mjs/.cjs` 支持
6295
+ - beta.11/12 继续打磨双栏:完全独立滚动容器(滚轮按鼠标所在栏路由,左右不再互带、高度互不撑挤)、44:56 非对称布局、单条文字按栏宽换行最多 3 行(CJK/emoji 按终端字符宽度处理)
6296
+
6297
+ ### 46.8 OpenAI 工具 Schema 兼容性专项(beta.8、beta.9、beta.10)
6298
+
6299
+ OpenAI `/v1/responses` 对 function schema 的严格限制连续拒绝了 `WebFetch.url`、`RegisterTool.inputSchema` 等请求(400)。最终方案是在**出站层**做递归归一化,只影响发给模型的关键字提示,本地校验仍按原始 Zod/JSON Schema 完整执行:
6300
+
6301
+ - 递归剔除 `format`、`default`、`propertyNames`、`patternProperties`、条件 schema;`oneOf`、对象 `allOf`、tuple `prefixItems`、旧版 `definitions` 转换为兼容表示;关闭无 `properties` 的对象避免 `z.record(..., z.unknown())` 留下缺 `type` 的 `additionalProperties: {}`
6302
+ - **覆盖所有来源**:内置严格工具、`RegisterTool`、持久化动态工具、MCP 工具、插件工具统一过最终归一化;`RegisterTool` 因必须接收任意 JSON Schema 改用合法的非严格 provider schema(`additionalProperties: true`)
6303
+ - 所有 HTTP 400/422 确定性请求错误统一归类为**不可重试**的 `invalid_request`——普通会话、自动续跑、`/loop`、`/goal` 不再对同一无效 schema 反复请求;并修复 schema 400 被误判为 `context_overflow` 触发无意义自动续跑的问题(溢出识别改为匹配明确的 `context_length` / `context_window` / token 超限措辞)
6304
+ - 新增**生产运行时全工具 schema 守门测试**:逐层检查每个 OpenAI 工具的根对象、类型、数组 items、组合分支、严格必填、开放属性与禁用关键字
6305
+ - `thinkingEffort` 扩展为 `minimal`/`low`/`medium`/`high`/`xhigh`/`ultra` 六档(未配置默认 `high`,明确同时作用于主/子 Agent);修复 OAuth `llm_config` 整体替换 provider 配置时**丢弃本地思考控制项**的问题——`thinkingEffort`/`thinkingBudget`/`claudeClient` 在替换后保留
6306
+
6307
+ ### 46.9 运行中多模态队列(beta.12)
6308
+
6309
+ 运行中的 pending 输入从单条槽位升级为**完整多模态队列**:连续暂存文字、图片、Skill 与插件命令按 FIFO 提交;运行中可继续粘贴图片;每按一次 `Esc` 取回最近加入的一条及其附件逐条修改。输出期间 `/` 补全保持开放,Skill/插件候选优先显示并带类型标记,选中后随普通输入进入队列,不打断当前任务。
6310
+
6311
+ ### 46.10 文件地图与验证
6312
+
6313
+ | 路径 | 职责 |
6314
+ |------|------|
6315
+ | `src/doctor.ts` | 本地诊断检查器与 `--json` 报告 |
6316
+ | `src/launcher.ts` | 轻量命令免重启、V8 flag 注入、退出码 75 恢复、`isLightCommand` |
6317
+ | `src/utils/memory-restart.ts` | GC 核实水位、轮换预算、continuation 标记、保留普查 |
6318
+ | `src/tools/shell.ts` | 结构化 argv、运行时 Shell 路由、结构化诊断 |
6319
+ | `src/execution/` | Docker 输出限幅与升级式终止 |
6320
+ | `src/loop/orchestrator.ts`、`src/goal/orchestrator.ts` | 工作单元边界轮换检查(`memoryRotationActive`)、执行段边界、待验证检查点 |
6321
+ | `src/models/` | OpenAI schema 归一化、thinkingEffort 透传、`invalid_request` 分类 |
6322
+ | `src/init/astgraph/` | 转为可选插件后的唯一源码位置(构建不再打包) |
6323
+ | `src/ui/` | 双轨工作台、独立滚动容器、多模态 pending 队列 |
6324
+
6325
+ 对应测试见 CHANGELOG 各节「测试与维护」;代表性规模:beta.3 的 `docs/internal-tools-audit-beta.3.md` 审计记录、beta.5 的 240 轮完整 GC 堆稳定压测、beta.6 的真实 Ink 10 万次 commit 低堆压测与构建期 development reconciler 扫描门禁、beta.10 的全工具 schema 守门测试。
6326
+
6327
+ ---
6328
+
6329
+ ## 47. 1.4.1 至 1.4.2 终端交互与输出呈现升级
6330
+
6331
+ 1.4.1 与 1.4.2 聚焦「在终端里长时间盯着 Agent 干活」的体验:中断与恢复的掌控感、历史可追溯、输出可完整回看,以及跨平台的复制粘贴一致性。
6332
+
6333
+ ### 47.1 中断、历史与编辑(1.4.1)
6334
+
6335
+ - **`Esc` 直接中断运行中的回合**;若存在 pending 输入仍优先逐条取回最近一条,队列为空再按 `Esc` 才停止回合(与 46.9 的多模态队列语义一致)
6336
+ - **prompt 历史按工作区私密持久化**到用户状态目录,保留最近 200 条;`↑` 浏览历史前暂存当前完整草稿,回到历史末端时恢复文字、光标、粘贴块与图片附件——不再覆盖未提交输入
6337
+ - **反向历史搜索**:Windows/Linux `Ctrl+R`,macOS 同时支持 `Command+R` 与传统 `Ctrl+R`,连续按键继续查找更早结果
6338
+ - 输入框**撤销/重做**:`Ctrl+Z` / `Ctrl+Shift+Z`(macOS 为 `Command` 组合)
6339
+ - **审批卡片增强**:`v` 展开/收起具体工具参数(命令、参数、工作目录、目标路径与工具输入);`e` 快捷切换到 `acceptEdits` 权限模式(当前请求仍需明确允许或拒绝)
6340
+
6341
+ ### 47.2 完整输出模式与折叠体系(1.4.1、1.4.2、1.4.3-beta.2)
6342
+
6343
+ 输出呈现经历三层演进:
6344
+
6345
+ 1. **1.4.1 引入完整输出模式**(`Ctrl+O`,macOS 同持 `Command+O`):展开被压缩的早期回合、命令输出、changeset 文件、后台任务日志/列表与 Web 搜索结果,再按一次恢复紧凑视图
6346
+ 2. **1.4.2 解耦折叠语义**:所有带明细的工具回执统一可折叠(命令输出、文件差异、changeset、Web 搜索、后台任务、通用工具详情同一行为);工具输出的展开状态与普通用户/助手对话**彻底解耦**——切换时不会连带显示或隐藏正常对话内容;回执默认预览从 16 行收紧为 8 行(略向头部倾斜并保留结尾,显示已展示/已隐藏行数),折叠态隐藏提示直接指向 `Ctrl+O to view all`
6347
+ 3. **1.4.3-beta.2 解除展开态上限**:`Ctrl+O` 展开后 CLI 视口渲染上限完全解除(回合数、输出项总量、每回合输出项与文本字符均不截断),长会话也能完整回看;收起后恢复按终端尺寸的响应性预算(45/46 章的渲染窗口机制仍然生效于折叠态)
6348
+
6349
+ 配套修复:长流式输出期间按终端尺寸限制实时渲染量并复用未变化的补全/历史子树(1.4.1-beta.2);斜杠菜单把多行/超长描述归一化为有界单行,防止菜单高度超过预留空间裁掉输入行;上方阅读时切换展开/收起**保留阅读位置**,只有原本贴底才继续跟随最新输出。
6350
+
6351
+ ### 47.3 滚轮路由与渲染正确性
6352
+
6353
+ 「滚不动 / 滚不到底」的根因很典型:鼠标悬停任务面板时若面板整体重建布局(双栏随子代理数量切单栏),旧节点被卸载就再不补发 `onMouseLeave`,滚轮路由状态永久卡在任务面板上,之后所有滚动都进了没有溢出内容的任务栏。修复:hover 集合清理时对**已卸载节点同样补发 leave**。回归覆盖五种场景(长会话上滚后下滚到底、CJK 宽字符换行、流式期间追底、批量滚轮合并、触控板抖动);压测 5000 轮「悬停中卸载重建」enter/leave 计数完全平衡零残留,572 行 transcript 上 1500 个随机滚轮事件 `scrollTop` 始终合法。另外:模型输出期间即使终端把滚轮降级为上下方向键序列,也只滚动内容、不误触输入历史(1.4.1-beta.1)。
6354
+
6355
+ ### 47.4 macOS 复制粘贴与斜杠回退(1.4.2)
6356
+
6357
+ - macOS 终端无法把 `Command+C` 交给全屏 CLI——改为**选区完成后直接写系统剪贴板**;支持扩展键协议的终端仍可用显式 `Command+C`;`Ctrl+C` 始终保留停止/退出语义(Windows/Linux 有选区时复制、无选区时中断)
6358
+ - macOS 粘贴严格 `Command+V`:普通粘贴优先采用终端/系统剪贴板文字,**只有没有文字时**才添加图片;新增 `/paste-image` 在文字图片同时存在时明确贴图
6359
+ - **未命中的 `/文本` 作为普通消息发送**且可携带图片;只有已注册的命令、插件、Skill 或托管工具按斜杠命令处理——消除了「想打 /home 结果被当命令」一类摩擦
6360
+
6361
+ ---
6362
+
6363
+ ## 48. 1.4.3 内置浏览器(Browser)
6364
+
6365
+ 1.4.3-beta.1 给 Electron 桌面端装上一台 **Agent 可直接操作的真实浏览器**:不再是「截图给模型看」的单帧交互,而是快照引用 → 精准操作 → 观察结果的闭环,用于 D2C/E2E 的页面验证、Web 资料的多步浏览与交互审阅。
6366
+
6367
+ ### 48.1 总体架构
6368
+
6369
+ ```mermaid
6370
+ flowchart LR
6371
+ U["用户标签页"] --> S["浏览器空间\nBrowserSpace / BrowserTab"]
6372
+ A["Agent 工具调用"] --> S
6373
+ S --> H["BrowserHost\nWebContentsView 粘合"]
6374
+ H --> CDP["CDP 传输层\n(Chrome DevTools Protocol)"]
6375
+ CDP --> SNAP["BrowserSnapshot\n结构化快照 + 元素引用 @N"]
6376
+ SNAP --> RES["元素解析器\n引用 → 视口内目标"]
6377
+ RES --> ACT["BrowserAct\n点击 / 输入 / 选择"]
6378
+ ACT --> OVL["可视化覆盖层\n实时高亮 Agent 操作"]
6379
+ POL["安全策略\n协议/私网/脱敏"] --> H
6380
+ ```
6381
+
6382
+ 浏览器面板与空间管理:Agent 与用户**分别持有标签页**,互不抢占;每个标签页经 `WebContentsView` 挂载,由 CDP 传输层驱动。
6383
+
6384
+ ### 48.2 八个工具
6385
+
6386
+ | 工具 | 职责 |
6387
+ |------|------|
6388
+ | `BrowserTabs` | 列出/管理标签页与空间归属 |
6389
+ | `BrowserNavigate` | 地址导航(协议受限) |
6390
+ | `BrowserControl` | 前进/后退/刷新/关闭等页级控制 |
6391
+ | `BrowserSnapshot` | 生成带元素引用(`@12` 形式)的结构化页面快照 |
6392
+ | `BrowserAct` | 按引用点击、输入、选择(引用经元素解析器映射回视口内真实目标) |
6393
+ | `BrowserRead` | 读取页面内容 |
6394
+ | `BrowserMouse` | 坐标级鼠标操作 |
6395
+ | `BrowserWait` | 等待页面状态 |
6396
+
6397
+ **快照-引用模式**是关键设计:模型拿到的是带稳定引用的结构化 DOM 摘要而非原始 HTML,操作按引用发起,避免模型拼 CSS 选择器或猜坐标;页面变化后重新快照即可,旧引用自然失效。
6398
+
6399
+ **可视化覆盖层**:Agent 的点击、输入等操作在页面上实时高亮标注,用户直观看到 Agent 正在做什么——把「自动驾驶」变成「看得见司机在开」。
6400
+
6401
+ ### 48.3 安全策略
6402
+
6403
+ | 层面 | 措施 |
6404
+ |------|------|
6405
+ | 导航协议 | 仅允许 `http` / `https` |
6406
+ | SSRF 防护 | 默认阻止私网地址(可显式放行);**云元数据域名始终拒绝**,无放行通道 |
6407
+ | 快照脱敏 | 输出自动脱敏密码、token、cookie、OTP、CVV 等敏感字段 |
6408
+ | IPC 边界 | 渲染进程传入的 URL、ID 与坐标均经严格 schema 校验 |
6409
+
6410
+ ### 48.4 shell-doctor 守护插件与测试
6411
+
6412
+ 新增 **shell-doctor** 守护插件:分类 Shell 失败(Windows/POSIX 命令误用等)并维护失败台账,接入 `PreToolUse` / `PostToolUse` / `PostToolUseFailure` / `UserPromptSubmit` 四个钩子——是 45.4 守护插件模式(Stop 否决)之外的第二个消费者范例。
6413
+
6414
+ 浏览器交付随附契约、CDP 传输、BrowserHost、安全策略、快照/act/overlay、面板等全套回归测试(`tests/desktop/browser-*.test.ts` 共 14 个文件),当期全仓 240 个测试文件、2244 用例通过;zh-CN Windows 全量测试修复(Vitest 固定注入 `NODE_ENV=test`,避免继承打包环境的 `NODE_ENV=production` 加载 React 生产内部实现导致套件整体失败)。
6415
+
6416
+ ### 48.5 文件地图
6417
+
6418
+ | 路径 | 职责 |
6419
+ |------|------|
6420
+ | `src/desktop/browser/browser-host.ts` | 浏览器宿主与 WebContentsView 粘合 |
6421
+ | `src/desktop/browser/browser-space.ts` / `browser-tab.ts` | 空间与标签页归属(Agent/用户分持) |
6422
+ | `src/desktop/browser/browser-security.ts` | 导航策略、SSRF 阻断、快照脱敏 |
6423
+ | `src/desktop/browser/browser-tools.ts` | 八个工具的注册与装配 |
6424
+ | `src/desktop/renderer/browser-panel.tsx` | 桌面端浏览器面板 |
6425
+ | `.flavor/plugins/shell-doctor/` | Shell 失败分类守护插件 |
6426
+
6427
+ ---
6428
+
6429
+ ## 49. 无头 CLI、命令行子命令族与 Prompt Cache 增强
6430
+
6431
+ 最后两条线:让 flavor **在脚本与 CI 里可用**(无头模式与命令行子命令族),并让 **Prompt Cache 在长间隔工作流里真正省钱**(1.4.3-beta.2 与未发布段)。
6432
+
6433
+ ### 49.1 无头模式(`flavor --print`)
6434
+
6435
+ 1.4.3-beta.2 把 `--print` 升级为可编排的无头入口:
6436
+
6437
+ - **`--output-format text | json | stream-json`** — `json` 结束时输出单个 result 对象(`sessionId`、`result`、`usage`、`errors`、`exitCode`);`stream-json` 每事件一行 JSON,适合增量消费
6438
+ - **`--permission-mode` / `--model`** — 单次运行覆盖权限模式与模型
6439
+ - **`--allowed-tools`** — 自动放行白名单,支持三类模式:`Read`(精确)、`mcp__docs__*`(前缀)、`Shell(npm test:*)`(命令前缀);编译与匹配由 `compileHeadlessToolAllowlist` / `matchesHeadlessAllowlist`(`src/production.ts`)完成,**非法标识符直接拒绝**而非静默忽略
6440
+ - 提示词改为可选参数:未提供时从 **stdin 管道**读取,`echo ... | flavor --print` 成立
6441
+ - 交互 CLI 退出后打印 `Resume later with: flavor --resume <sessionId>`(仅本次运行确实生成了 sessionId 时);`shutdownRuntime` 接受 `onSessionEnd` 回调,优雅关闭与看门狗强退都只触发一次,回调抛错只上报不阻断退出
6442
+
6443
+ e2e 冒烟用真实模型验证了 `json` 与 `stream-json` 两种输出。
6444
+
6445
+ ### 49.2 命令行子命令族:`sessions` / `config` / `usage`(未发布)
6446
+
6447
+ 把已有运行时能力暴露到命令行,全部**复用既有内部实现**而非另写一套:
6448
+
6449
+ | 命令 | 能力 | 复用的内部件 |
6450
+ |------|------|--------------|
6451
+ | `flavor sessions` | `list`(最新在前,`--json`)、`show [id]`、`delete <id>`、`export <id> [--format md\|json] [--output <path>]`、`path` | `SessionStore` 的 `list/load/delete`,摘要与导出从时间线轮次提取 prompt/assistantText |
6452
+ | `flavor config` | `list` / `--json` / `--sources`、`get <key>`(点路径如 `context.windowTokens`)、`set <key> <value>`、`unset <key>`、`path` | `loadConfig` + `redactConfig`(密钥自动脱敏);`set` 值支持 JSON 字面量自动解析(`50`/`true`/`["a"]`) |
6453
+ | `flavor usage` | 汇总当前会话 token 与缓存命中率,`--json` / `--path` | `parseUsageEntries` / `summarizeUsage` / `formatUsageSummary`(第 2 章 1.1.9 的 usage 日志体系) |
6454
+
6455
+ 写侧新增 `setProjectConfigValue` / `unsetProjectConfigValue`:写 `flavor.json` 前先用合并态 `loadConfig` 做**完整 Schema 校验**,非法值(错误枚举、类型、越界)在写盘前抛错,原文件与备份保持不变;`configPathSegments` 拒绝未知顶层键防拼写错误。
6456
+
6457
+ 三条命令均注册为**轻量入口**(`LIGHT_CLI_COMMANDS`,`src/launcher.ts`),跳过带 V8 诊断 flag 的 relaunch,与 `init`/`doctor`/`mcp` 一样保持快速冷启动;入口静态导入均为轻量模块,重活在 action 内按需 `await import()`。
6458
+
6459
+ ### 49.3 CLI 冷启动专项(1.4.3-beta.2)
6460
+
6461
+ Windows 实测冷态启动 17 秒+、暖态 0.35 秒,主因是 Node ESM loader 对 node_modules 数百个小文件的逐个 stat/read/parse 被 Windows Defender 逐文件扫描放大。对策组合:
6462
+
6463
+ 1. **纯 JS 依赖打包进 dist chunk**(tsup `noExternal`),消除逐文件加载;`node-pty`、`@vscode/ripgrep`、`web-tree-sitter`、`tree-sitter-wasms` 等需从自身目录解析原生二进制/WASM 的模块保持 `external`
6464
+ 2. **轻量命令免进程重启**:`init`/`update`/`doctor`/`skills`/`memory`/`mcp`/`eval`/`help`/`completion` 与 `--version`/`--help` 跳过带 V8 诊断 flag 的 relaunch 直接执行——`--version` 与 `doctor --json` 约 **107ms** 返回,且不再生成 heapprofile 产物
6465
+ 3. **重模块懒加载**:入口顶部 type-only 导入,`production`/`doctor`/`update`/`skills`/`eval`/`rpc`/`trace` 等在命令 action 内按需 `await import()`
6466
+
6467
+ ### 49.4 ApplyPatch 强健化(1.4.3-beta.2)
6468
+
6469
+ 模型输出补丁的现实形态远多于教科书 unified diff,ApplyPatch 新增三条容忍(第 9 章工具系统的补丁应用逻辑升级):
6470
+
6471
+ - **裸 `@@` 头**(无行号):按上下文在整个文件内唯一精确匹配重定位;匹配歧义则在**写入前**报错并列出候选行号
6472
+ - **越半径重定位**:带行号的 hunk 声明行漂移超过 100 行搜索半径后,仍依据唯一精确上下文自动重定位(此前直接失败);错误信息区分「漂移后歧义」与「不匹配」
6473
+ - **围栏剥离**:自动剥离包裹补丁的 ```` ```diff ```` Markdown 围栏(含围栏后多余空行),CRLF 补丁体规范化;应用后回写的 hunk 行号基于实际匹配位置重算,回执行号与文件真实位置一致
6474
+
6475
+ 压测:10 万行文件 800 个漂移 hunk 重定位 197ms、5000 hunk 补丁 70ms;600 个随机畸形补丁 fuzz 全部保持「失败不写入」的原子性;路径逃逸补丁被拒绝。
6476
+
6477
+ ### 49.5 Prompt Cache 增强(未发布)
6478
+
6479
+ 配合 42.2 的 Context Epoch,本段把两个 provider 的缓存协议用到极限:
6480
+
6481
+ - **Anthropic**:可选 `cacheTtl: "5m" | "1h"`(省略时维持兼容性更广的服务商默认 TTL,长间隔工作流显式选 1 小时);从**首轮单消息**开始设置滚动尾部缓存标记;历史中追加的动态 system 更新保持原始时间顺序,不再被提升到顶层 system 而破坏既有缓存前缀
6482
+ - **OpenAI Responses**:发送由稳定前缀与**排序后工具定义**派生的 `prompt_cache_key`;支持新缓存协议的端点同时启用 30 分钟 implicit cache,并把 provider-neutral 边界与请求尾部映射为最多三个 explicit breakpoints;兼容端点若拒绝新字段,按「仅路由键 → 无缓存扩展」两级自动降级,不影响请求可用性
6483
+ - **修复前缀失效根因**:动态 task/runtime/memory source 更新时曾重建消息头,导致整个会话缓存前缀**实际失效**——epoch 现在持久化不可变的初始 source 快照,后续变化只在历史尾部追加(呼应 42.2「逐字节不变」铁律)
6484
+ - **usage 口径修正**:OpenAI `input_tokens` 按服务商定义作为总输入量,cached/write token 是其**子集拆分**而非相加项;DeepSeek 风格 miss token 归入普通输入
6485
+
6486
+ ### 49.6 文件地图与验证
6487
+
6488
+ | 路径 | 职责 |
6489
+ |------|------|
6490
+ | `src/launcher.ts` | `LIGHT_CLI_COMMANDS` 轻量判定、`isLightCommand` / `isStaticUsageError` |
6491
+ | `src/production.ts` | `compileHeadlessToolAllowlist` / `matchesHeadlessAllowlist`、`shutdownRuntime` + `onSessionEnd` |
6492
+ | `src/session/cli.ts` | `flavor sessions` 子命令族 |
6493
+ | `src/config/cli.ts` + `src/config/schema.ts` | `flavor config` 子命令族、`setProjectConfigValue` / `unsetProjectConfigValue` / `configPathSegments` |
6494
+ | `src/usage/cli.ts` | `flavor usage` 汇总 |
6495
+ | `src/tools/`(ApplyPatch)+ `tsup` 构建配置 | 补丁重定位与冷启动打包策略 |
6496
+ | `src/models/`(Anthropic/OpenAI 适配器)+ `src/context/manager.ts` | cacheTtl、prompt_cache_key、滚动标记、epoch append-only |
6497
+
6498
+ 对应测试:`tests/session/cli.test.ts`、`tests/config/cli.test.ts`、`tests/usage/cli.test.ts` 共 17 项单测(注入 fake store/deps);`tests/config/load.test.ts` 的真实落盘集成;`tests/cli/launcher.test.ts` 轻量判定;`tests/cli/print.test.ts` 无头格式与白名单;缓存侧覆盖 OpenAI 稳定路由键、显式断点数量上限、兼容端点降级、官方 usage 口径,以及 Anthropic 首轮滚动标记、动态 system 顺序、1 小时 TTL、context epoch append-only 恢复的回归。当期全仓 **2282 个用例通过**、`tsc --noEmit` 通过。