@zooique/memora 3.0.1 → 4.0.0
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.
- package/CHANGELOG.md +720 -14
- package/README.en.md +4 -4
- package/README.md +303 -303
- package/config.example.json +1 -8
- package/dist/agent/agent.d.ts +48 -4
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +130 -12
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/assembler.d.ts +19 -5
- package/dist/agent/assembler.d.ts.map +1 -1
- package/dist/agent/assembler.js +76 -13
- package/dist/agent/assembler.js.map +1 -1
- package/dist/agent/backgroundTasks.d.ts +187 -0
- package/dist/agent/backgroundTasks.d.ts.map +1 -0
- package/dist/agent/backgroundTasks.js +307 -0
- package/dist/agent/backgroundTasks.js.map +1 -0
- package/dist/agent/builtinToolHandlers.d.ts +2 -1
- package/dist/agent/builtinToolHandlers.d.ts.map +1 -1
- package/dist/agent/builtinToolHandlers.js +8 -4
- package/dist/agent/builtinToolHandlers.js.map +1 -1
- package/dist/agent/builtinTools.d.ts +15 -1
- package/dist/agent/builtinTools.d.ts.map +1 -1
- package/dist/agent/builtinTools.js +107 -27
- package/dist/agent/builtinTools.js.map +1 -1
- package/dist/agent/constants.d.ts +31 -0
- package/dist/agent/constants.d.ts.map +1 -1
- package/dist/agent/constants.js +31 -0
- package/dist/agent/constants.js.map +1 -1
- package/dist/agent/guardRail.d.ts +11 -0
- package/dist/agent/guardRail.d.ts.map +1 -1
- package/dist/agent/guardRail.js +36 -1
- package/dist/agent/guardRail.js.map +1 -1
- package/dist/agent/loop.d.ts +274 -22
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +587 -87
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/managers/archiveCoordinator.d.ts +5 -2
- package/dist/agent/managers/archiveCoordinator.d.ts.map +1 -1
- package/dist/agent/managers/archiveCoordinator.js +17 -2
- package/dist/agent/managers/archiveCoordinator.js.map +1 -1
- package/dist/agent/managers/loopMetrics.d.ts +14 -0
- package/dist/agent/managers/loopMetrics.d.ts.map +1 -1
- package/dist/agent/managers/loopMetrics.js +15 -0
- package/dist/agent/managers/loopMetrics.js.map +1 -1
- package/dist/agent/managers/memoryInspector.d.ts +1 -8
- package/dist/agent/managers/memoryInspector.d.ts.map +1 -1
- package/dist/agent/managers/memoryInspector.js +6 -30
- package/dist/agent/managers/memoryInspector.js.map +1 -1
- package/dist/agent/managers/roundSummaryGenerator.d.ts +28 -0
- package/dist/agent/managers/roundSummaryGenerator.d.ts.map +1 -1
- package/dist/agent/managers/roundSummaryGenerator.js +67 -2
- package/dist/agent/managers/roundSummaryGenerator.js.map +1 -1
- package/dist/agent/managers/sessionArchiver.d.ts +21 -3
- package/dist/agent/managers/sessionArchiver.d.ts.map +1 -1
- package/dist/agent/managers/sessionArchiver.js +20 -3
- package/dist/agent/managers/sessionArchiver.js.map +1 -1
- package/dist/agent/managers/sessionManager.d.ts.map +1 -1
- package/dist/agent/managers/sessionManager.js +17 -0
- package/dist/agent/managers/sessionManager.js.map +1 -1
- package/dist/agent/managers/toolCallHelpers.d.ts +109 -28
- package/dist/agent/managers/toolCallHelpers.d.ts.map +1 -1
- package/dist/agent/managers/toolCallHelpers.js +93 -37
- package/dist/agent/managers/toolCallHelpers.js.map +1 -1
- package/dist/agent/messageHistory.d.ts.map +1 -1
- package/dist/agent/messageHistory.js +9 -5
- package/dist/agent/messageHistory.js.map +1 -1
- package/dist/agent/seed/orchestrator.d.ts +12 -1
- package/dist/agent/seed/orchestrator.d.ts.map +1 -1
- package/dist/agent/seed/orchestrator.js +12 -1
- package/dist/agent/seed/orchestrator.js.map +1 -1
- package/dist/agent/textSanitize.d.ts +36 -0
- package/dist/agent/textSanitize.d.ts.map +1 -0
- package/dist/agent/textSanitize.js +55 -0
- package/dist/agent/textSanitize.js.map +1 -0
- package/dist/agent/toolExecutor.d.ts +48 -22
- package/dist/agent/toolExecutor.d.ts.map +1 -1
- package/dist/agent/toolExecutor.js +372 -71
- package/dist/agent/toolExecutor.js.map +1 -1
- package/dist/agent/toolLedger.d.ts +93 -41
- package/dist/agent/toolLedger.d.ts.map +1 -1
- package/dist/agent/toolLedger.js +114 -42
- package/dist/agent/toolLedger.js.map +1 -1
- package/dist/agent/toolResultOffload.d.ts +8 -0
- package/dist/agent/toolResultOffload.d.ts.map +1 -1
- package/dist/agent/toolResultOffload.js +10 -3
- package/dist/agent/toolResultOffload.js.map +1 -1
- package/dist/agent/toolRunner.d.ts +14 -7
- package/dist/agent/toolRunner.d.ts.map +1 -1
- package/dist/agent/toolRunner.js +71 -29
- package/dist/agent/toolRunner.js.map +1 -1
- package/dist/agent/tracer.d.ts +13 -1
- package/dist/agent/tracer.d.ts.map +1 -1
- package/dist/agent/types.d.ts +66 -5
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/code-exec/types.d.ts +9 -0
- package/dist/code-exec/types.d.ts.map +1 -1
- package/dist/index.d.ts +21 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +36 -28
- package/dist/index.js.map +1 -1
- package/dist/memory/roundStore.d.ts +67 -81
- package/dist/memory/roundStore.d.ts.map +1 -1
- package/dist/memory/roundStore.js +4 -15
- package/dist/memory/roundStore.js.map +1 -1
- package/dist/memory/types.d.ts +7 -0
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +4 -0
- package/dist/memory/types.js.map +1 -1
- package/dist/role-pack/rolePackManager.d.ts +42 -10
- package/dist/role-pack/rolePackManager.d.ts.map +1 -1
- package/dist/role-pack/rolePackManager.js +65 -12
- package/dist/role-pack/rolePackManager.js.map +1 -1
- package/dist/role-pack/strategyKeys.d.ts +36 -0
- package/dist/role-pack/strategyKeys.d.ts.map +1 -1
- package/dist/role-pack/strategyKeys.js +45 -0
- package/dist/role-pack/strategyKeys.js.map +1 -1
- package/dist/security/confirmEntries.d.ts +44 -0
- package/dist/security/confirmEntries.d.ts.map +1 -0
- package/dist/security/confirmEntries.js +59 -0
- package/dist/security/confirmEntries.js.map +1 -0
- package/dist/security/pathGuard.d.ts +41 -5
- package/dist/security/pathGuard.d.ts.map +1 -1
- package/dist/security/pathGuard.js +125 -4
- package/dist/security/pathGuard.js.map +1 -1
- package/dist/skill/skillScriptRunner.d.ts +147 -15
- package/dist/skill/skillScriptRunner.d.ts.map +1 -1
- package/dist/skill/skillScriptRunner.js +479 -68
- package/dist/skill/skillScriptRunner.js.map +1 -1
- package/dist/utils/configResourceManager.d.ts +6 -2
- package/dist/utils/configResourceManager.d.ts.map +1 -1
- package/dist/utils/configResourceManager.js +8 -2
- package/dist/utils/configResourceManager.js.map +1 -1
- package/dist/utils/errors.d.ts +9 -0
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +9 -0
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/eventEmitter.d.ts +57 -2
- package/dist/utils/eventEmitter.d.ts.map +1 -1
- package/dist/utils/eventEmitter.js +23 -0
- package/dist/utils/eventEmitter.js.map +1 -1
- package/dist/utils/time.d.ts +5 -2
- package/dist/utils/time.d.ts.map +1 -1
- package/dist/utils/time.js +5 -2
- package/dist/utils/time.js.map +1 -1
- package/docs/memora-api-reference.md +121 -20
- package/docs/memora-/346/216/245/345/205/245/346/214/207/345/215/227.md +6 -5
- package/package.json +1 -1
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/persona.md +46 -46
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/rules.md +16 -15
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/emotion-wave.md +46 -46
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/foreshadow.md +29 -29
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/outline-expand.md +48 -46
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/project-init/scripts/init-novel-project.mjs +441 -441
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/story-bible.md +62 -60
package/CHANGELOG.md
CHANGED
|
@@ -1,14 +1,720 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
本文件记录 **@zooique/memora(内核 npm 包)**的版本变更。**宿主**(`hosts/memora-vscode`)不在发布包 `files` 白名单内(见 `package.json
|
|
3
|
+
本文件记录 **@zooique/memora(内核 npm 包)**的版本变更。**宿主**(`hosts/memora-vscode`)不在发布包 `files` 白名单内(见 `package.json`),不占内核版本号。`[Unreleased]` 按 ADR-033 双版本线拆分:内核变更定档独立版本号(当前 **[4.0.0]**),宿主变更在 `[Unreleased] · 宿主`(随做随用,攒批随 `vsce package` 定版),不占内核版本号。
|
|
4
4
|
|
|
5
5
|
格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),版本号遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
|
|
6
6
|
|
|
7
|
-
> **版本定位(v3.0.0)**:**Node.js 专属 · 零第三方运行时依赖的 Agent 内核**(依赖 `node:*` 内置模块,不引第三方运行时依赖 / native / 宿主 API)。早期版本(2.1.0 及以下)为探索性迭代;**3.0.0 是架构收敛后的第一个稳定基线**,API 与结构以 3.0.0
|
|
7
|
+
> **版本定位(v3.0.0)**:**Node.js 专属 · 零第三方运行时依赖的 Agent 内核**(依赖 `node:*` 内置模块,不引第三方运行时依赖 / native / 宿主 API)。早期版本(2.1.0 及以下)为探索性迭代;**3.0.0 是架构收敛后的第一个稳定基线**,API 与结构以 3.0.0 为准;**4.0.0 起导出面收敛(-54)+ `search()` 退役,API 以 4.0.0 为准**。内核不内置 agent 级行为评估(eval)/ 独立验证——由宿主基于内核可观测性(ITracer / 事件 / 指纹)自行承担;内核对接口契约与单测(2700+ 用例)负责,不对"agent 整体行为稳定"作承诺。
|
|
8
8
|
|
|
9
|
-
## [
|
|
9
|
+
## [4.0.0] · 2026-10-08
|
|
10
|
+
|
|
11
|
+
> **本区归属**:仅**内核**(`@zooique/memora`)变更。本版按 SemVer 定为 **major**:删 54 个公共导出 + 退役 `agent.memory.search()`,对外部消费者是编译期破坏。宿主变更在下方 `[Unreleased] · 宿主` 区,不占内核版本号。
|
|
12
|
+
|
|
13
|
+
### Fixed(内核 · 摘要写入前三态:判断权归还 + 原料指纹精确去重 · 审查 P1-5a)
|
|
14
|
+
|
|
15
|
+
- **`summary` 允许 `null`**(`SUMMARY_JSON_CONTRACT`):摘要判断器可判「本轮无值得沉淀的信息」
|
|
16
|
+
(纯工具/后台轮),`generate()` 收到 null 不写入——此前契约强制非空,纯后台轮被挤出流水账摘要
|
|
17
|
+
(记忆库噪音根源)。null 是合法结论非错误,日志与「无效 JSON」警告分态可辨。
|
|
18
|
+
- **前置精确去重**:新增 `materialFingerprint`(sha256(输入原文 + NUL + 回复原文),顶层持久化字段,
|
|
19
|
+
同 `supersededBy` 先例)。`generate()` 入口比对存量摘要,原料全等 → 跳过(不调 LLM、零写入);
|
|
20
|
+
判据取原文全文 + 字符串全等,仅铁重复时跳过(截断面外不同 / 回复略异一律放行),
|
|
21
|
+
近似重复仍归下游 `supersedeSimilar`(未动:阈值 / 打标不删 / 同 session 语义原样)。
|
|
22
|
+
- **存量条目零动作**:无指纹字段不参与匹配,只拦新增;已落盘数据一字不改。
|
|
23
|
+
- 守卫:三态用例(全等跳过零 LLM 调用 / 同输入异回复放行 / null 不写入且 memoryAdded 不误发 /
|
|
24
|
+
指纹随沉淀落盘 / 分隔符防拼接歧义)+ `parseMemory` 白名单与类型校验同步。
|
|
25
|
+
|
|
26
|
+
### ⚠️ Breaking(内核 · 记忆搜索唯一入口收敛:删除已退役的 `MemoryInspector.search()` · `f552695e`)
|
|
27
|
+
|
|
28
|
+
- 删除 `agent.memory.search(query, limit?)`(同步),含其 5 条测试。唯一入口 = `searchByKeyword()`
|
|
29
|
+
(返回 `Promise`,需 `await`;能力超集:superseded 过滤 / `excludeRoundIds` 互斥 / `accessedAt` 刷新 + 溯源揭示)。
|
|
30
|
+
- 迁移:`agent.memory.search(q, n)` → `await agent.memory.searchByKeyword(q, n)`。
|
|
31
|
+
|
|
32
|
+
### ⚠️ Breaking(内核 · 工具错误码收口为单一构造点,重试分类改读结构化字段 · `6ce81139`)
|
|
33
|
+
|
|
34
|
+
- `ToolOutcome` 新增 `errorCode?: ToolErrorCodeValue`;带码失败一律走 `failedOutcomeWithCode(code, detail)`,
|
|
35
|
+
文本 `[ERR:TOOL:XXX]` 前缀由 `errorCode` 派生,单一构造点同源产出一次。
|
|
36
|
+
- `isRetryableToolError` 优先读 `outcome.errorCode`,无码才回退解析文本前缀(兼容路径)。
|
|
37
|
+
- 新增枚举值 `NOT_AVAILABLE`(不可重试:缺的是宿主装配,重试空转)。
|
|
38
|
+
|
|
39
|
+
### Added(内核 · 公共导出面双向机器对账守卫 · `PUBLIC-API-SURFACE-1` · `536c54fa`)
|
|
40
|
+
|
|
41
|
+
- 向 A:公开面 ⊆ 宿主真实消费 ∪ README/手册 §十六 承诺(防未登记公开);
|
|
42
|
+
向 B:手册 §十六 承诺符号在 `src` 真实存在(防幻觉文档);
|
|
43
|
+
向 C:手册 §十六 承诺的模块级符号必须在公开面(防「文档承诺但未导出」,消费者具名
|
|
44
|
+
import 得到 undefined)。C 向为发版前审查补建,豁免集冻结(import 示例噪声 9 + 类型/叙述词 4)。
|
|
45
|
+
|
|
46
|
+
### Added(内核 · 公开导出 `resolveShellCommand`:宿主环境快照 shell 字段单一真源派生 · 发版前审查 H8)
|
|
47
|
+
|
|
48
|
+
- `src/skill/skillScriptRunner.ts` 的 shell 派发映射函数挂公共面。VS Code 宿主环境快照的
|
|
49
|
+
`shell` 字段改为经内核公开导出直接派生(此前镜像手写平台三元——内核改映射宿主不知情,
|
|
50
|
+
上报值与实际派发值漂移即「假事实」;2026-10-03 审查 P0 同源教训的收口)。
|
|
51
|
+
- 守卫保留:宿主侧同源守卫改读内核源码提取真源(重算 = 第二份判据,写错即红的形态不变)。
|
|
52
|
+
|
|
53
|
+
### Added(内核 · 会议组员消费端缺员过滤:名单输出 = 实际可参会者)
|
|
54
|
+
|
|
55
|
+
- `effectiveMembers`(截断 + 缺员过滤,单一实现)收口 `activeTeamMembers` / `getTeam`:
|
|
56
|
+
组员引用的角色包已卸载(幽灵引用)从参会名单输出中剔除——`tryBuildMeetingPlan` 不再为
|
|
57
|
+
幽灵组员建空转发言项,宿主 UI 所见与会议实际消费一致(SSOT 消费端视图,ADR-028 收敛补记二)。
|
|
58
|
+
- 存储仍原样不裁切:受损名单的「提示清理/修复」由宿主读期检测承担,内核只保证输出即实际参会。
|
|
59
|
+
- 守卫 3 用例:缺员过滤 + getTeam 快照 / 存储原样不裁切 / 截断在过滤前不回补。
|
|
60
|
+
|
|
61
|
+
### ⚠️ Breaking(内核 · 导出面收敛:54 个无消费、无文档承诺的公共导出降级为内部实现 · `REL-3.1`,已定档 4.0.0)
|
|
62
|
+
|
|
63
|
+
**收敛判据(2026-10-06 定案)**:公共导出只保留 ① 宿主(`hosts/memora-vscode`)经 `@zooique/memora`
|
|
64
|
+
实际 import 的符号;② README / API 手册 §十六 明文承诺的公共 API。两条都不占的符号从 `src/index.ts`
|
|
65
|
+
公共面移除。**收编 ≠ 删除**:类型定义仍在原模块、随函数签名/结构推断可用,只是不再可显式 import;
|
|
66
|
+
判定前全量重盘(历史快照「约 70 个」重盘实测 53 个;终盘对账 3.0.1→4.0.0 实移 **55** 个、终盘后回归摘出 1 个(`ProcessMetricsPayload` 重新挂面,见分账注记)= 发布面 **54** 个——误报 2、补登 4,见下方清单分账注记)。
|
|
67
|
+
|
|
68
|
+
**移除清单(54 个,按域分组)**:
|
|
69
|
+
|
|
70
|
+
- agent 内部类型/值(15):`AbortStopReason` / `IdempotencyLevel` / `ToolExecutionRecord` /
|
|
71
|
+
`PauseMeta` / `PlanItemOutcome` / `PlanItem` /
|
|
72
|
+
`DefaultDuplicateCallInterceptor` / `DuplicateCallInterceptor` / `DuplicateCheckVerdict` /
|
|
73
|
+
`DuplicateCheckContext` / `RoundSummaryGenerator` / `EstimateOccupancyInput` /
|
|
74
|
+
`estimateTokensText` / `EstimableMessage` / `WorkProjectionManager`
|
|
75
|
+
- LLM / 事件 / 记忆(3):`TaskType` / `AGENT_EVENTS` / `SummaryType`
|
|
76
|
+
- Round 存储内部件(6):`RoundEvidenceEvent` / `generateRoundId` / `generateMessageId` /
|
|
77
|
+
`createPendingRound` / `completeRound` / `InMemoryRoundStore`
|
|
78
|
+
- 工具定义常量(2):`SEARCH_PROJECT_TOOL` / `TRACE_SUMMARY_TOOL`
|
|
79
|
+
- 角色包子类型与装配内部件(24):`RolePack` / `RolePackTeam` / `RolePackCapability` /
|
|
80
|
+
`RolePackManifestSkill` / `BehaviorStrategy` / `PrepareStrategy` / `ActStrategy` /
|
|
81
|
+
`ReflectStrategy` / `GlobalStrategy` / `ToolReadonly` / `ProviderRouting` /
|
|
82
|
+
`MultiStepReasoning` / `SelfReviewRounds` / `UserFollowup` / `ErrorHandling` /
|
|
83
|
+
`DEFAULT_BEHAVIOR_STRATEGY` / `mergeStrategy` / `assembleRolePack` /
|
|
84
|
+
`checkCompanionContentRedline` / `RolePackValidationIssue` / `RolePackValidationResult` /
|
|
85
|
+
`RolePackIssueSeverity` / `RolePackAssembly` / `validateManifest`
|
|
86
|
+
- 杂项工具(4):`safeSearch` / `isValidConfigName` / `parseConfigId` / `MAX_CONFIG_NAME_LENGTH`
|
|
87
|
+
|
|
88
|
+
**分账注记(2026-10-08 终盘对账)**:初版清单(53)含 **误报 2**——`BackgroundTaskStatus` /
|
|
89
|
+
`COMPRESS_TARGETS` 在 3.0.1 导出面上**不存在**(本开发周期内新增,旋经 `b69f85ff` 收敛收回),
|
|
90
|
+
不构成 3.0.1→4.0.0 对外破坏,已从上方清单移出(`BackgroundTaskStatus` 的挂面史见下方后台任务
|
|
91
|
+
可见性条目后注);另 **漏登 4** 已补入上方分组——`WorkProjectionManager`(类值导出)、
|
|
92
|
+
`validateManifest`(值导出)、`RolePackAssembly`、`ProcessMetricsPayload`,其中前两个是值导出,
|
|
93
|
+
外部显式 import 在 4.0.0 直接编译红。
|
|
94
|
+
|
|
95
|
+
**终盘后回归 1(计数 55 → 54)**:`ProcessMetricsPayload` 在终盘后因「多流续跑 metrics 同轮合并」
|
|
96
|
+
修复(见下方 metrics 合并 Fixed 条)需要重新挂面——宿主 chatPanel 的段级合并助手以其为参数类型
|
|
97
|
+
(判据①「宿主真实 import」成立),从移除清单摘出;其余 54 个维持收敛不变。
|
|
98
|
+
|
|
99
|
+
**文档面同步修正(API 手册 §十六 5 处失真)**:删 4 个**幻影承诺**(`AgentProjectEntry` /
|
|
100
|
+
`BOOST_INCREMENT` / `SCORE_CEILING` / `SCORE_FLOOR` / `SkillMatch`——src 无对应导出,属文档单方面
|
|
101
|
+
承诺了不存在的 API)+ 1 处**滞后承诺**(`extractKeywords`——代码已定案收回,文档未跟);
|
|
102
|
+
`SummaryType` 字段描述改行内联合类型写法。手册 §十六 收敛口径改写为与 `src/index.ts` 同判据。
|
|
103
|
+
|
|
104
|
+
**影响**:宿主零改动(tsc 双绿实证,收编清单与宿主消费面零交集);外部消费者若显式 import 被收
|
|
105
|
+
符号,改用类型推断或自有定义。
|
|
106
|
+
|
|
107
|
+
### ⚠️ Breaking(内核 · 工具结果三值契约判据切换:执行层闸门 blocked 不再计入 toolFailureCount · `SCRIPT-2` B4,已定档 4.0.0)
|
|
108
|
+
|
|
109
|
+
**口径变更(Breaking)**:执行层闸门(宿主审批拒绝 `permission_denied` / 只读拒绝 `readonly_denied` /
|
|
110
|
+
幂等跳过 `idempotent_skip`)此前被文本判据算作**失败**(`[ERR:TOOL:PERMISSION_DENIED]` 等前缀命中
|
|
111
|
+
`isToolFailure`)计入 `toolFailureCount`——「我们主动挡下的」与「工具真的跑失败了」混在一个数字里。
|
|
112
|
+
B4 判据切换后编排层 / 计数层一律读 `ToolOutcome.status` 三值契约:**blocked 归入
|
|
113
|
+
`toolBlockedCount`(主动挡下),不再计入 `toolFailureCount`**(方案 §六决策二)。
|
|
114
|
+
|
|
115
|
+
**数字变化(历史不可比,跨版本比较该字段须先对齐此边界)**:
|
|
116
|
+
|
|
117
|
+
- `toolFailureCount` / `AgentMetrics.tools.failureCount` **下降**(执行层闸门剔除 + run_command
|
|
118
|
+
cwd 越界 `[ERR:PATH_DENIED]` 转 blocked);
|
|
119
|
+
- `toolBlockedCount` **上升**(执行层闸门 + 旁路拒绝 `no_compress_target` / `invalid_intel_note` 计入);
|
|
120
|
+
- `compress_context` 摘要生成失败 / 压缩异常**重归类为 failed**(原无失败前缀、被静默算成功)⇒ 计入 failureCount;
|
|
121
|
+
- chunk 呈现:执行层拒绝 / 幂等跳过从「失败」变「已拦截」(`tool_result` chunk `blocked: true`);
|
|
122
|
+
- `guardier.notifyExec` 收到执行层拒绝从 `'failed'` 变 `'blocked'`(write_loop 自持不再把用户拒绝当失败升级文案)。
|
|
123
|
+
|
|
124
|
+
**双轨设施同批整体退役**:`ToolRunner.runOne` 返回值 `string` → `Promise<ToolOutcome>`(结构化事实
|
|
125
|
+
直通编排层);`onToolSettled` 旁路通道、`stepOutcomes` 注册表、`_crossCheckOutcome` 双轨核对、
|
|
126
|
+
`stepBlockedDisagreements` / `stepUnreportedOutcomes` 观测集及其 metrics 出闸字段
|
|
127
|
+
(`toolBlockedDisagreementCount` / `toolOutcomeUnreportedCount`)、`toolOutcomeConsistency.test.ts`
|
|
128
|
+
一致性测试**全部删除**——status 是唯一判据,不再有第二轨道。
|
|
129
|
+
|
|
130
|
+
**判据桥收窄(非退役)**:`isToolFailure` 降格为**未切 19 族文本 → status 的边界转换器**(唯一生产
|
|
131
|
+
消费点 = `toolRunner.runOne` 回落),由前缀契约测试 + `toolFailurePrefixGuard` 守卫锁定;其物理删除
|
|
132
|
+
随 B5(最终消亡批次:19 族 outcome 化)执行。B1 缺口补齐:run_command 后台模式未装配注册表出口补
|
|
133
|
+
failedOutcome emit;kill_command 已终态分支的行首归属判据改读结构化进程终态(`timedOut` / `exitCode`),
|
|
134
|
+
不再扫文本前缀。
|
|
135
|
+
|
|
136
|
+
### ⚠️ Breaking(内核 · 判据桥消亡:失败语义全面结构化,文本前缀判据退役 · `SCRIPT-2` B5,已定档 4.0.0)
|
|
137
|
+
|
|
138
|
+
**B5(最终消亡批次)收口**:未切族全部 outcome 化,文本前缀判据(`isToolFailure` +
|
|
139
|
+
`TOOL_FAILURE_PATTERNS`)及其守卫(`toolFailurePrefixGuard.test.ts` / 前缀契约测试段)**物理删除**。
|
|
140
|
+
`SCRIPT-2` 全案闭合:失败语义唯一承载 = `ToolOutcome.status`,文本只喂 LLM / 渲染。
|
|
141
|
+
|
|
142
|
+
**落地内容(B5-a~e 五子批)**:
|
|
143
|
+
|
|
144
|
+
- **全族 outcome 化**:信息获取族(web_search / web_fetch / search_project)、任务表族
|
|
145
|
+
(task_table_write / update)、渐进披露/回调族(read_skill / read_resource / list_resources /
|
|
146
|
+
list_skills / register_work / run_team_meeting)共 **21 个失败点**显式 emit failedOutcome;
|
|
147
|
+
kill_command 六出口全 emit(NOT_AVAILABLE / TASK_NOT_FOUND = failed,KILLED / SETTLED = ok);
|
|
148
|
+
`assembler.planManager.updatePlanItem` 任务项寻址失败改抛结构化 `toolError`
|
|
149
|
+
(经 runOne catch → failedOutcome,不再裸文本 return)。
|
|
150
|
+
- **runOne 兜底改写**:`nativeOutcome ?? okOutcome(result)`——执行器未 emit = 工具正常完成
|
|
151
|
+
(裸文本含软降级说明),不再扫文本前缀派生 status;abort 两出口(已 abort 提前返回 / abort 胜出)
|
|
152
|
+
显式 emit failedOutcome(中断 = 失败事实,防兜底误判 ok)。
|
|
153
|
+
- **残留守卫固化**:新增「生产代码零裸失败前缀 return」守卫测试(扫 toolExecutor / toolRunner /
|
|
154
|
+
assembler 三文件,失败必须显式 emit),把方案的 grep 证明固化为永久测试。
|
|
155
|
+
- **list_sessions 裁决登记**:两处软降级(未配置存储 / 读取失败返回说明文本)是文档化的
|
|
156
|
+
「降级优先」刻意设计(JSDoc 明示不抛错、不阻塞对话),非伪装失败,不改 throw。
|
|
157
|
+
|
|
158
|
+
**数字变化(历史不可比)**:kill_command 终止「已终态的失败任务」时,失败已在后台完成点
|
|
159
|
+
(B3 `setBackgroundTasks` 回调)单点结算;B5 前该场景被 B4-c 行首 `[COMMAND_ERROR]` 桥判**重复
|
|
160
|
+
计 1 次 failed(双计伤,2026-10-06 实测)**——B5-d 起 kill 回传 status 恒 ok(文本层
|
|
161
|
+
`[COMMAND_ERROR]` 保留给 LLM 看结局),`toolFailureCount` 对该场景**下降**(去重)。
|
|
162
|
+
|
|
163
|
+
**测试口径(B5-e 契约同构)**:loop 层 mock toolExecutor 返回失败文本时必须伴随显式 emit
|
|
164
|
+
(新增 `mockExecutorReturning` 辅助函数统一 7 处 mock 形态);「执行器未 emit → 兜底 ok(即使
|
|
165
|
+
文本长得像失败前缀)」由测试锁定——失败判据唯一来源 = emit,无第二判据。
|
|
166
|
+
|
|
167
|
+
### ⚠️ Breaking(内核 · 工具失败判据收敛单点:脚本/代码/命令三态族不再漏计 · `SCRIPT-1` + `METRICS-PREFIX-1`①②,已定档 4.0.0)
|
|
168
|
+
|
|
169
|
+
**口径缺陷(静默失败 = 假阴性)**:失败判据此前散落三处、各自手写 `result.startsWith('[ERR')`
|
|
170
|
+
(`toolRunner` 执行层 / `loop` 编排层 / `loop` `toolFailureCount` 计数)。而脚本族失败产出的是
|
|
171
|
+
`formatExecutionResult` 的**三态前缀** —— `[SCRIPT_ERROR]` / `[CODE_ERROR]` / `[COMMAND_ERROR]` /
|
|
172
|
+
`[SCRIPT_TIMEOUT]` / `[CODE_TIMEOUT]` / `[COMMAND_TIMEOUT]`(`kind: 'SCRIPT' | 'CODE' | 'COMMAND'`),
|
|
173
|
+
**一个都不以 `[ERR` 开头** ⇒ `run_skill_script` / `run_code` / `run_command`
|
|
174
|
+
的**全部失败与超时恒判为成功**,`toolFailureCount` / `AgentMetrics.tools.failureCount` **恒少计**。
|
|
175
|
+
|
|
176
|
+
**⚠️ 口径边界(2026-10-06 二次审查订正,勿夸大)**:后台命令**自然终局**(`backgroundTasks` 的
|
|
177
|
+
`formatBackgroundTaskNotice` → `enqueueCommandResult` → `appendSystemMessage`)**结构上不产生
|
|
178
|
+
`tool_result` chunk**,因此其失败**不经过本判据、也不计入 `toolFailureCount`** —— 这不是判据漏计,
|
|
179
|
+
而是该路径**没有工具结果承载点**。台账已拆独立条目 `METRICS-BGTASK-1` 待排(修法与判据字段化强耦合)。
|
|
180
|
+
本条闭合范围 = **三个工具族的同步执行面**(`run_skill_script` / `run_code` / `run_command` 同步路径
|
|
181
|
+
+ `kill_command` 回传),**不含后台自然终局**。
|
|
182
|
+
|
|
183
|
+
**变更**:新增 `toolCallHelpers.isToolFailure()` 为失败判据**唯一真源**(两族正则),三处散点全部改调它;
|
|
184
|
+
生产代码 `startsWith('[ERR')` 零残留。`_processToolResults` 签名新增 `blockedFlags?`(调用点传
|
|
185
|
+
`toolExecs.map(e => e.blocked)`),使「策略拦截不算失败」由**隐性约定**(靠护栏文案刻意避开 `[ERR`)
|
|
186
|
+
转为**显式排除** —— 原为脆弱不变量:新护栏文案一旦用 `[ERR` 前缀即静默误计拦截为失败。
|
|
187
|
+
`tracer.ts` 的 `failureCount` JSDoc 原以「结果以 `[ERR` 开头」定义字段,已改指判据函数(防文档层留失效定义)。
|
|
188
|
+
|
|
189
|
+
**⚠️ 对下游的口径影响(诚实登记)**:`toolFailureCount` / `failureCount` 的**历史数字与修复后不可比**
|
|
190
|
+
——修复后**上升**。已落盘 round 的 metrics 属旧口径,跨版本比较该字段须先对齐此边界。
|
|
191
|
+
**宿主落盘侧同受影响**:宿主收场的 `toolFailureCount` 自行从 processEvents 数(`!ok && !blocked`),
|
|
192
|
+
`ok` 来源统一后**两侧口径首次自动一致**(此前内核漏三态族、宿主跟着一起漏,两侧同错故看似一致),
|
|
193
|
+
故**宿主已落盘 round 的该字段数字同样上升**。
|
|
194
|
+
实测影响面(12 案例):**精确翻转 5 条**(三族的非零退出码 + 超时),**零误伤**
|
|
195
|
+
(策略拦截族 `[SEARCH_LIMIT_REACHED]` / `[ALREADY_READ]` / `[ASK_LIMIT]`、`[KILLED]` 主动终止、
|
|
196
|
+
正常输出、旧 `[ERR` 族共 7 条判定均不变)。
|
|
197
|
+
|
|
198
|
+
**前缀约定已由测试锁定**:`toolCallHelpers.test.ts` 的「isToolFailure · 前缀约定契约」段逐条断言
|
|
199
|
+
失败族 10 条 + **豁免族 9 条**(策略拦截 3 / 未执行·幂等 4 / 护栏提示 2)—— 新增护栏文案未登记归属即转红,
|
|
200
|
+
「记得避开」的隐性约定转为可执行纪律。豁免清单由**全量 grep 生产代码 `[XXX]` 标签逐个过判据**得出
|
|
201
|
+
(共 31 个标签,判失败 6 个全为执行三态族、无一误判),非仅照抄既有台账。
|
|
202
|
+
**未闭合**:`METRICS-XFLOW-1`(跨流 metrics 低估)独立待排 —— 判据收敛只修「单流内漏计」,跨流漏计照旧。
|
|
203
|
+
|
|
204
|
+
### ⚠️ Breaking(内核 · `background` 语义修订:turn 终态不再杀后台进程 · CMD-1 阶段 3)
|
|
205
|
+
|
|
206
|
+
**真机实锤的缺陷**(`round-1791074929352.json`):用户起 `ping -n 90`(`background=true`),
|
|
207
|
+
turn 仅 **6.286 秒**结束就触发 `reapAll` 把 90 秒的进程杀了,UI 底部条从「运行中」变「已终止」
|
|
208
|
+
而**用户从未点过终止**;助手正文还说「后台运行中,完成后结果会自动回到上下文」。
|
|
209
|
+
⇒ **内核文案在骗模型,模型在骗用户。**
|
|
210
|
+
|
|
211
|
+
**行为反转(两句话)**:**一、别杀它。二、别扔它的结果。**
|
|
212
|
+
|
|
213
|
+
- `BackgroundTaskRegistry.reapAll()` → **`detachAll()`**(脱管不杀)。旧方法**直接删除、不留兼容**——
|
|
214
|
+
旧行为已被真机证伪,留着等于留一个陷阱。脱管实际是**纯读**:不杀进程、不改 status、不清注册表
|
|
215
|
+
- `loop.finalizeBackgroundTasksOnTurnEnd()` **删除** `command-result` 丢弃行;`background_report`
|
|
216
|
+
报告文案改「已转为后台常驻(未被终止)」,**不再宣称「已全部终止」**
|
|
217
|
+
- ⚠️ **撤销「回流不跨 turn」定案**:该定案只封杀 `command-result` 却放行 `interject`,而两者性质完全
|
|
218
|
+
相同(外部产生的事实、显式一条消息、分别经 `appendSystem` / `appendUser`)⇒ **同一现象两套标准 =
|
|
219
|
+
违反 SSOT**;且 turn 内跑的 `command-result` 走的就是同一通路(`_handleInterrupt` →
|
|
220
|
+
`appendSystem`),**机制在 turn 边界突然失效是一致性缺口,不是保护**。本条**不是推翻定案,
|
|
221
|
+
是补齐一条没贯彻到底的判据**
|
|
222
|
+
- **零新机制**:`loop` 是 Agent 级单例(`agent.ts:1350` 装配时创建一次,`:1907` close 才置 null)
|
|
223
|
+
⇒ `interruptQueue` 天然跨 turn 存活;`clearPendingInterjections()` 只清 `interject` 不动
|
|
224
|
+
`command-result` ⇒ 两个清理点本就分流、无冲突。**零新工具 / 零新队列 / 零新状态机**
|
|
225
|
+
- **诚实口径修订**(文案层是真机问题的源头①):`[BACKGROUND_STARTED]` 回执与工具描述删去
|
|
226
|
+
「结果会自动回到你的上下文」承诺,改为显式告知「不会自动进上下文,需要时用 `kill_command` 取回」+
|
|
227
|
+
「本轮结束不会终止它」
|
|
228
|
+
- **诚实边界**(2026-10-04 审查订正;⑧ 已闭环见下方 Fixed「退出杀树」条):进程活过 turn;
|
|
229
|
+
宿主寿命边界经观察点 ⑧ 真机定类(OS 层孤儿实锤)后已补退出杀树——正常退出 VS Code 终止,
|
|
230
|
+
崩溃/被强杀不保证。
|
|
231
|
+
超时同理:`BACKGROUND_MAX_TIMEOUT_MS`(30min)只是**显式传 timeoutMs 时的钳制上限**,
|
|
232
|
+
缺省不传 = 永不超时,脱管窗口**无内核侧时限**
|
|
233
|
+
- **回归守卫翻转 1 条**:`aborted 收场 → 回流被丢弃,不跨 turn` 与新定案直接冲突,改为
|
|
234
|
+
「跨 turn 留存 + 下一轮被吸收」。该守卫原始意图(陈旧结果不该凭空注入)**仍成立**,但旧实现手段
|
|
235
|
+
(turn 边界一刀切丢弃)过宽;改后模型仍是**显式看到**该 system 消息,不吞声、不偷注
|
|
236
|
+
- **测试**:`detachAll` 脱管不杀(3 处断言:status 仍 `running` / 仍可 `kill` 取回 / 再脱管仍能列出)
|
|
237
|
+
+ turn 终态脱管 + 报告文案不得含「已全部终止」+ 纯问答不产 `background_report`(反向守卫)
|
|
238
|
+
- **变异验证 2 组**(逐组精确转红):`detachAll` 改回 `killNow`+置 `killed` → 3 红;
|
|
239
|
+
恢复 `command-result` 丢弃行 → `aborted` 留存守卫红
|
|
240
|
+
- **方案文档**:`docs/方案-后台任务跨轮存活-20261004.md`(含主流对标实锤、SSOT 论证、刻意不做清单)
|
|
241
|
+
|
|
242
|
+
### Added(内核 · 用户角色包目录重扫对账 `resyncUserPacks` · 角色包「刷新」按钮内核落点)
|
|
243
|
+
|
|
244
|
+
- **背景**:`loadExtraDir` 仅在 Agent 装配时执行一次,面板折叠展开读内存快照——用户在用户
|
|
245
|
+
角色包目录新增/改名/删除/修改角色包后,不重启宿主不生效(真机 RP-EDIT-1 冒烟实证)。
|
|
246
|
+
- **语义(净差对账,对用户诚实)**:摘除全部运行时注入项(`runtimeNames` 记账名单)→ 重新
|
|
247
|
+
`loadExtraDir` → 同名重注入 = 内容更新生效(`updated`),不算新增/移除;真消失(目录已删 /
|
|
248
|
+
改名旧名)才进 `removed`,全新名字才计 `added`。
|
|
249
|
+
- **激活悬空走 §4.1 单链兜底**:原激活包被摘除时复用 `activateFallback()`(与 reload 激活包被删
|
|
250
|
+
同源同判据),经 `onActiveChanged` 同步宿主 UI;不自造第二套回退规则。
|
|
251
|
+
- **边界**:内置包不参与对账(磁盘真理源 = dist,构建期同步,运行中恒定);manifest.name 撞内置
|
|
252
|
+
的用户包继承重名跳过判据。
|
|
253
|
+
- **测试**:5 用例锁行为(新增注入 / 删除摘除 + 记账连续 / 改名 / 悬空兜底 / 内容更新净差)。
|
|
254
|
+
|
|
255
|
+
### Fixed(内核 · 读取防重分支②死路回放:台账替身回显补「原文仍在上下文」前置 · 数据源取证 round-1791449684099)
|
|
256
|
+
|
|
257
|
+
**真机死循环实锤**:一次 turn 内「整读文件 → 上下文压缩挤掉原文 → 中段重读」时,台账分支②
|
|
258
|
+
(替身回显)在**原文已不在上下文**的情况下仍以「已有覆盖」为由把请求拦截成摘要替身——模型拿不到
|
|
259
|
+
原文、重读又被拦,整读→压缩→重读→替身拦死循环(生产轮复现数据 round-1791449684099)。
|
|
260
|
+
|
|
261
|
+
- **契约前置**:`shouldEchoLedgerStub(subj, cov, stillInContext)` 增第三必填参数,前置不过则
|
|
262
|
+
一切区间判定短路为放行(放行真读,新结果入上下文后由分支① `read_dedup` 接手防重复)——
|
|
263
|
+
宁可多读一次不可误拦(两个失败方向不对称,防永动机语义不变)。
|
|
264
|
+
- **数据面收口**:`FileCoverage` 新增必填 `lastToolCallId` / `fingerprint`(定位锚 + 进上下文
|
|
265
|
+
内容指纹,与 toolResultCache 同口径同源计算)——禁 optional 兜底:缺锚则回退语义恒为「在」,
|
|
266
|
+
死路回放复发,宁编译期红。
|
|
267
|
+
- **判据单一收口**:新抽 `AgentLoop.isMessageStillUnmodified(toolCallId, fingerprint)`,
|
|
268
|
+
分支①(`isCachedResultStillInContext` 委托)与分支②(回显前置)共用同一「原文是否仍在
|
|
269
|
+
上下文」判据,不双轨。
|
|
270
|
+
- 测试:死路回放生产序列复现用例(整读→压缩→中段重读应放行 3 次真读、零回显)+ 压缩语境
|
|
271
|
+
放行组重写 + 归一拦截组重构保留;变异验证(短路放行分支 → 4 用例精确转红 → 恢复全绿)已做。
|
|
272
|
+
|
|
273
|
+
### Fixed(内核 · Windows 命令输出编码事实缺失:模型对 cmd OEM/GBK 乱码失明 · CMD-2 补宣告)
|
|
274
|
+
|
|
275
|
+
**实证坐实**:`run_command` 经 `cmd /c` 执行时,cmd 写管道输出用系统 OEM 代码页(简中系统为
|
|
276
|
+
GBK;实测 `chcp 65001` 下仍输出 GBK 字节),而内核执行器恒按 UTF-8 解码收流——非 ASCII 输出
|
|
277
|
+
必显示为乱码(ASCII 不受影响,故英文命令无感、中文场景模型反复踩坑且无法自查归因)。
|
|
278
|
+
|
|
279
|
+
- **裁决 = 事实宣告(非解码改造)**:`buildEnvironmentContextBlock` 新增 win32 平台条件行
|
|
280
|
+
「命令输出编码:cmd 管道输出按系统 OEM 代码页写出(简中系统为 GBK),内核按 UTF-8 解码——
|
|
281
|
+
非 ASCII 输出会显示为乱码(ASCII 不受影响);Node 脚本输出为 UTF-8」。纯事实链零行为引导
|
|
282
|
+
(对齐环境段「零解释转发」纪律);平台参数化与 `resolveShellCommand` 同先例,POSIX 不加噪音。
|
|
283
|
+
- 三方对照:内核执行路径(spawn `cmd /c` + UTF-8 收流)↔ 工具描述(无编码声明、不冲突)↔
|
|
284
|
+
环境宣告段(本条补齐)。
|
|
285
|
+
|
|
286
|
+
### Fixed(内核 · 后台任务回流通知首标签穷尽化:`timedOut` 不再谎报「完成」· 发版前审查 H1)
|
|
287
|
+
|
|
288
|
+
**行为修正**:`formatBackgroundTaskNotice` 旧首标签由二元三元推导(`killed ? '已终止' : '完成'`),
|
|
289
|
+
`timedOut` 产出「[后台命令完成] · 超时被终止」自相矛盾、`running` 被谎报完成。
|
|
290
|
+
|
|
291
|
+
- 新增穷尽表 `BACKGROUND_TASK_NOTICE_TAGS: Record<BackgroundTaskStatus, string>`(内核内部,
|
|
292
|
+
不挂公共面——消费面是内核回流通知文本,宿主无 import 需求;`Record` 穷尽键 = 状态枚举增补时
|
|
293
|
+
编译期红,同 `COMPRESS_TARGET_LABELS` 范式),四态各给诚实首标签;
|
|
294
|
+
状态文案渲染仍以既有 `BACKGROUND_TASK_STATUS_LABELS` 为唯一真源(不双轨)。
|
|
295
|
+
- 测试:`timedOut` 首标签不得谎报完成(回归锁)+ 四态逐条与穷尽表同源断言;
|
|
296
|
+
变异验证(表值串档 → 精确转红)已做。
|
|
297
|
+
|
|
298
|
+
### Fixed(内核 · 宿主退出孤儿进程:补实例终态杀树 · 观察点 ⑧ 真机定类闭环)
|
|
299
|
+
|
|
300
|
+
**真机实锤**:完全退出 VS Code 约 10 秒后,后台 `ping -n 300`(PID 15456)仍在运行,
|
|
301
|
+
其父进程(扩展宿主)已不存在——Windows 不回收脱离控制台的子进程,Job Object 兜底在此
|
|
302
|
+
环境不成立。旧代码 `Agent.close` 不处置后台注册表,每次退出留一批 OS 孤儿(dev server
|
|
303
|
+
类更会永久占端口)。
|
|
304
|
+
|
|
305
|
+
- **两个生命周期终点分开**(勿折叠):turn 终态 `detachAll` **不杀**(跨轮存活);
|
|
306
|
+
Agent 实例终态新增 `BackgroundTaskRegistry.killAllRunning()` **真杀**——注册表按
|
|
307
|
+
实例隔离是硬不变量,实例销毁后任务不可寻址,不杀即孤儿
|
|
308
|
+
- **接线**:`AgentLoop.shutdownBackgroundTasks()` 纯透传(未装配返 0);`Agent.close()`
|
|
309
|
+
在 loop null 化前调用;宿主 `deactivate` 本就 await close,**宿主侧零行为改动**。
|
|
310
|
+
主动终止逻辑抽单点 `terminate`(`kill` / `killAllRunning` 共用),不回调 listener
|
|
311
|
+
(退出时回流队列已无消费方),不推 UI 快照
|
|
312
|
+
- **诚实边界**:只覆盖扩展正常停用;扩展崩溃 / 被 OS 强杀时 `close` 来不及执行,该场景
|
|
313
|
+
依旧不承诺清理。文案三处(工具描述 / `[BACKGROUND_STARTED]` 回执 / turn 收尾报告)
|
|
314
|
+
统一为「正常退出 VS Code 时终止;崩溃 / 被强杀不保证」
|
|
315
|
+
⚠️ 本条的「正常停用」承诺当时仍是谎(见下一条 31ms 窗口实锤),由下一条补第二触发通路后才成真
|
|
316
|
+
- **测试**:注册表混合态收割(running 真杀 / completed、killed 不碰 / 不回调 listener)+
|
|
317
|
+
loop 透传(含未装配返 0)+ `Agent.close` 调用点 spy 守卫;**变异验证 2 组**(断 close
|
|
318
|
+
接线 → spy 红;收割改纯读 → 状态断言红)
|
|
319
|
+
- **定案沉淀**:ADR-036(后台命令任务双生命周期边界 + 回流留存;S2 固化触发 1 = src 引用
|
|
320
|
+
≥1),src 定案注释锚改指 ADR
|
|
321
|
+
- **顺带修测试夹具假活(同批实锤的既有缺陷)**:`backgroundTasks.test.ts` 的
|
|
322
|
+
`SLEEP_CMD` 原写法 `node -e "setTimeout(()=>{},120000)"` 在 Windows `cmd /c` 下
|
|
323
|
+
**引号被原样传给 node,eval 的是字符串字面量(空语句)→ 秒退 exit 0**,而既有用例
|
|
324
|
+
只在 start 占位 `running` 的窗口内断言,从未真验进程存活。改为平台分叉
|
|
325
|
+
(win32 无引号 function 版 / POSIX 双引号版,引号在两个 shell 命运相反),
|
|
326
|
+
`loop.test.ts` 同型夹具一并修;新用例以「起后 300ms 复查仍 running」挡占位假活
|
|
327
|
+
|
|
328
|
+
### Fixed(内核 · 关窗 31ms 窗口实锤:exit 钩子同步兜底收割 · 观察点 ⑧ 第二轮)
|
|
329
|
+
|
|
330
|
+
**真机实锤(第二轮,新 dist 含杀树重测)**:renderer 发 terminate 到扩展宿主进程消失仅
|
|
331
|
+
**31ms**(16:15:28.817 → 16:15:28.848,exit 0),Memora 日志零收割痕迹,`ping -n 300`
|
|
332
|
+
成孤儿跑至自然结束。async `close()` 深处的杀树在 Windows 正常关窗的清理窗口里根本
|
|
333
|
+
跑不到 ⇒ 上一条的「正常退出时终止」当时是谎(诚实边界承诺跑不到代码路径)。
|
|
334
|
+
|
|
335
|
+
- **exit 钩子同步兜底**:注册表构造时 `process.on('exit')`,钩子内遍历 running 用**同步**
|
|
336
|
+
杀树原语 `killProcessTreeSync`(Windows `execFileSync('taskkill', …)`;POSIX
|
|
337
|
+
`process.kill(-pgid)` 本就同步)。为何必须同步:异步 spawn 的 fork 由 libuv 线程池派发,
|
|
338
|
+
exit 钩子返回后 Node 即退出,fork 可能来不及完成 ⇒ 杀树静默失败(假阴性同族)
|
|
339
|
+
- **同源防双真理源**:同步 / 异步原语共享 `buildTaskkillArgs` 参数构造(改一处漏一处无测试
|
|
340
|
+
报警 = 第二真理源反模式);terminate 单点加 `mode` 档位且**必传**(缺省值语义 = 语境含糊
|
|
341
|
+
静默回落),close 通路走异步、exit 通路走同步,共用同一终止单点
|
|
342
|
+
- **注册/注销配对**:`shutdown()` = `killAllRunning()` + 注销钩子——钩子是**进程级**监听、
|
|
343
|
+
注册表是**实例级**对象,不配对 = 多实例钩子堆积 + 闭包泄漏;注销后经 `disposed` 判空转(幂等)
|
|
344
|
+
- **测试 +6**:同步原语机制级(taskkill 参数同源断言 / 128 静默达成 / 非 128 降级单杀 /
|
|
345
|
+
POSIX `kill(-pgid)`)+ 钩子注册恰一个、注销回原数(配对守卫)+ 兜底收割行为(置 killed、
|
|
346
|
+
不回调 listener、重复触发幂等)+ 注销后空转;存量用例收尾统一改 `shutdown()`
|
|
347
|
+
(测试自身不得复制「注册不注销」的堆积模式,防 Node MaxListeners 告警)
|
|
348
|
+
- **诚实边界更新**:「正常退出 VS Code 时终止」自本条起**成真**(close 通路一 + exit 钩子
|
|
349
|
+
通路二双触发);扩展崩溃 / 被 OS 强杀 / 断电时两通路都来不及执行,**依旧不承诺清理**。
|
|
350
|
+
定案锚 ADR-036 决策 1 已同步双通路口径
|
|
351
|
+
- **真机复测第三轮(2026-10-04 16:58)✅ 通过**:新 dist(16:56:39 构建,exitHook 符号确认)→
|
|
352
|
+
16:57 起后台 `ping -n 300`(自然寿命应到 ~17:02)→ 16:58:00 关 VS Code(**26ms** 窗口,
|
|
353
|
+
比第二轮更紧)→ 16:59 查证**零 ping 残留**。判定:ping 在自然寿命内消失 = 被杀树收割,
|
|
354
|
+
非自然结束;同轮「turn 终态脱管不杀」(ping 活过 turn)与「实例终态杀树」两个定案同机验证
|
|
355
|
+
⚠️ **通路归因存灰区(如实登记)**:OutputChannel 日志在退出瞬间来不及 flush,杀树日志未落盘,
|
|
356
|
+
无法从日志直接指认 exit 钩子还是 deactivate 恰好跑完;26ms 窗口下后者概率极低,
|
|
357
|
+
结果层验收锚(退出零孤儿)达成即本轮验收目标
|
|
358
|
+
|
|
359
|
+
### Fixed(内核+宿主 · 后台任务终态 UI 不准 + 终态行可手动收起 · CMD-1 阶段 3 补漏)
|
|
360
|
+
|
|
361
|
+
**缺口(阶段一自查漏判)**:脱管落地后,任务自然跑完时**宿主完全收不到通知**——
|
|
362
|
+
`setCompletionListener` 的回调只做 `enqueueCommandResult`(入队给模型),无任何出宿主的面。
|
|
363
|
+
而宿主推 UI 只有三个时机(`step_boundary` / `background_report` / kill 后回推),脱管后 turn 已结束,
|
|
364
|
+
三个全不覆盖 ⇒ **条上一律写「运行中」,实际早已跑完**。
|
|
365
|
+
|
|
366
|
+
> 比原缺陷更坏:原缺陷是「进程死了却显示已终止」(假终态),本漏是「进程活着却显示运行中」
|
|
367
|
+
> (**假活跃**)——让用户以为还有事在跑,而真相是结果早已躺在队列里。
|
|
368
|
+
|
|
369
|
+
- **新增内核事件 `backgroundTaskSettled`**(入 `AgentEventMap` + `AGENT_EVENTS`):挂在注册表
|
|
370
|
+
`notify` 单点链上(**仅自然终态 settle 经此;主动 kill 刻意不回调**——输出已直接返回,回调会
|
|
371
|
+
造成双份消费;UI 终止路径由宿主 kill 后自行回拉快照)⇒ `loop.setBackgroundTaskSettledEmitter` →
|
|
372
|
+
`agent.emit` → 宿主 `bindAgentNoticeEvents` 订阅。**不新增注册表 listener**
|
|
373
|
+
(`setCompletionListener` 是单点 setter 且已被回流占用,加第二个即第二真理源);
|
|
374
|
+
**走既有 `TypedEventEmitter`** 而非独立回调字段(与宿主 `off`/`on` 幂等模式对齐)。
|
|
375
|
+
载荷只带 `taskId` / `command` / `status`——**刻意不含输出**(同 `BackgroundTaskView`
|
|
376
|
+
不含 `result` 的口径)
|
|
377
|
+
- **终态行可手动收起**:webview 终态行给 `×` → `background_kill` + `reason:'dismiss'`;宿主记入
|
|
378
|
+
`Set` 并在投影出口过滤 ⇒ **只关掉「眼睛」**,内核条目与输出都留着(`kill_command` 随时取回)。
|
|
379
|
+
**可逆 > 不可逆**。**「记住了」放宿主不放 webview**(放 webview 则面板一折叠即忘,刚收起又冒出来)
|
|
380
|
+
- ⚠️ **running 行刻意不给 `×`**:只有「终止」(真杀)。进程在跑必须始终可见——不能让它能被藏起来,
|
|
381
|
+
那正是本缺口最初的样子(「派了人出去办事却不知道他还在不在」)
|
|
382
|
+
- **单通道复用**:`background_kill` 加 `reason?: 'terminate' | 'dismiss'`(缺省 `terminate`
|
|
383
|
+
兼容旧 webview)⇒ **协议通道 107 → 107 不变**(治理阈值 130)。一个通道两个明确动作优于开第二条
|
|
384
|
+
- **踩坑并已修(真缺陷,非测试问题)**:dismiss 后把签名哨兵置 `''` ⇒ **最后一条被收起时过滤后
|
|
385
|
+
`items` 为空、签名也是空串** ⇒ 去重判成 no-op ⇒ **条根本收不起来**。改用 `null` 哨兵
|
|
386
|
+
(`_backgroundSignature: string | null`)。这是「去重守卫 + 空集合」交互的典型陷阱
|
|
387
|
+
- **踩坑并已修**:回滚变异时误插成两行 `a.on(...)` ⇒ 一次终态推两遍相同快照(签名去重在处理器
|
|
388
|
+
内部拦不住,那是两条独立调用)。已加**订阅幂等守卫**(同一事件只 `on` 一次)并用变异验证其有效
|
|
389
|
+
- **测试**:内核 +2(终态触发发射回调 / 未注入回调时不抛);宿主 +5(dismiss 只收视图不碰内核 /
|
|
390
|
+
记在宿主侧面板重建不复活 / 终态事件推快照且桩 `on` 真转发 / 订阅幂等 / `×` 载荷带 `dismiss`
|
|
391
|
+
且 running 行无 `×`)。测试**桩的 `on` 必须是真转发**(`vi.fn()` 空实现会让「事件名错配」测不出来);
|
|
392
|
+
**不暴露 `emit`**(真实 Agent 上是 `protected`,测试直调会绕过事件名检查 ⇒ 改用捕获的订阅处理器)
|
|
393
|
+
- **变异验证 4 组**:拆终态发射 → 出口用例红 / 拆宿主订阅 → 事件用例红 / 拆 dismiss 过滤 → 2 条红 /
|
|
394
|
+
注入重复订阅 → 幂等守卫红
|
|
395
|
+
|
|
396
|
+
### Added(内核 · 后台任务宿主出口 `listBackgroundTasks` / `killBackgroundTask` · CMD-1 阶段 2 缺口)
|
|
397
|
+
|
|
398
|
+
**缺口**:后台任务运行态对用户零可见、无 kill 入口——`BackgroundTaskRegistry` 的 `list()` / `kill()` 能力早已齐备,但 `src/index.ts` **零导出**,宿主拿不到数据通道(不是「没画 UI」,是「取不到数」)。
|
|
399
|
+
|
|
400
|
+
- **新增门面方法**(纯委托,零新逻辑):`agent.listBackgroundTasks(): readonly BackgroundTask[]`、`agent.killBackgroundTask(taskId): BackgroundTask | null`;委托链 = agent → loop → 注册表,**不复制任何判据**(复制即第二真理源)
|
|
401
|
+
- **只出方法不出类**:注册表是 **Agent 实例级**(单例会让 `kill_command` 杀掉别的会话起的进程),导出类 = 把实例化权交给宿主 = 破坏隔离不变量
|
|
402
|
+
- **投影不泄漏句柄**:返回对象的键只有 `taskId / command / startedAt / status / result?`,`killNow` / `peek` 已由 `projection()` 剥离(有守卫测试钉死)
|
|
403
|
+
- **新增类型导出**:`BackgroundTask` / `BackgroundTaskStatus`(只读投影类型;**后注**:`BackgroundTaskStatus` 旋经 `REL-3.1` 导出面收敛收回——它在 3.0.1 导出面上不存在、本开发周期内曾短暂挂面,现版本已不挂面,本条保留真实历史)
|
|
404
|
+
- **状态词表单点**:`BACKGROUND_TASK_STATUS_LABELS`(`Record` 穷尽键 = 新增状态忘补文案即**编译期红**,同 `COMPRESS_TARGET_LABELS` 定案)。**2026-10-04 自审补**:方案文档 §2.3 曾写「复用内核 `STATUS_LABELS`」——该标识符当时是模块私有、内核零导出,属**引用不存在导出的文档失真**(与旧台账 `inlinePlanBoard` 同型)⇒ 改为公开导出,宿主 UI 直接消费、**禁自建第二套**
|
|
405
|
+
- **测试**:loop 层 5 条(未装配不抛 / 运行态可见 / kill 返回终态 / 未知 id 返回 null / 不泄漏句柄)+ 变异验证 2 组有效。**变异副产品**:原 `kill` 后的二次判空是**冗余分支**(`get()` 对未知 id 同为 null,删早退行为完全等价)→ 按无僵尸分支纪律删除,不留不可测代码
|
|
406
|
+
- **方案文档**:`docs/方案-后台任务可见性与kill入口-20261004.md`(实施规格真源;宿主侧协议 + UI 见下方宿主区同批条目)
|
|
407
|
+
|
|
408
|
+
### ⚠️ Breaking(内核 · 确认载荷与 chunk/事件变体 · 已定档 4.0.0)
|
|
409
|
+
|
|
410
|
+
**升级即需改代码**(TS 集成方必读):
|
|
411
|
+
|
|
412
|
+
- `WriteConfirmationInfo`(`onWriteConfirmation` 载荷)新增**必填**字段 `hasDiff: boolean`——**编译期破坏**(自建该对象的集成方需补字段;只消费不构造的集成方不受影响)。语义 = 「本次确认卡是否有 diff 对比域」:写入场景恒 `true`,命令 / 代码 / 脚本执行三类恒 `false`。**判据归内核单点**(`confirmGate` 的 `diff !== undefined`),宿主不再用 `beforeContent`/`afterContent` 的 truthy 自猜——那属「措辞折叠型带伤」(非写入场景被归一为 `beforeContent: null`,truthy 与 `!== undefined` 判据都会把命令确认渲染成假的「写入前/写入后」对比)
|
|
413
|
+
- `AgentChunk` 新增变体 `{ type: 'background_report'; content: string }`(turn 终态对存活后台进程**脱管**后 emit,报告「已转后台常驻(未被终止)」;无存活进程则不产。语义随阶段 3 脱管定案修订,见上方 Breaking)。消费方 `for await` 的 switch/exhaustive 检查需处理新变体
|
|
414
|
+
- `ProcessEvent` 新增同名变体 `{ type: 'background_report'; seq; ts; payload: { content: string } }`(落盘重放可见,与 `plan_snapshot` 同型)
|
|
415
|
+
|
|
416
|
+
### Added(内核+宿主 · 命令行执行能力 `run_command` / `kill_command` · `CMD-1`,已定档 4.0.0)
|
|
417
|
+
|
|
418
|
+
**能力缺口**:此前执行 shell 命令须「`write_file` 写脚本 → `run_project_script` 执行 → `delete_file` 清理」三步,且 `.sh` 在 Windows 上静默空跑(退出码 0 但输出全空)。
|
|
419
|
+
|
|
420
|
+
- **工具层**:`run_command`(任意 shell 命令字符串 + `cwd` + `timeoutMs` + `background`;`diskWrite:'opaque'` 与一切写互斥)+ `kill_command`(按 `taskId` 终止并取回截至当时的输出;只能杀本会话自起任务,非任意 pid)。两者入 `DEFAULT_EXPOSED_TOOLS` 常驻开放
|
|
421
|
+
- **安全边界**:`SecurityGuard.classifyCommand` 三档裁决(deny 黑名单恒拦 / always-ask 恒确认 / normal)+ `confirmCommandRun`(**复用既有 `confirmGate`**,零新确认通道)。⚠️ **已知边界(如实登记)**:deny 是**命令字面量护栏**,凡执行内容由间接载体承载处(脚本文件 / `run_code` 内联代码)结构性失效——裁决面登记见 `src/security/confirmEntries.ts`,绕过成本 = 一次工具调用
|
|
422
|
+
- **后台执行**:`background: true` 显式发起(**不做超时自动转后台**,拒绝隐式状态切换)→ 立即返回 `taskId`,完成后经 step 边界气口以 system 消息回流(role 语义隔离,不占插话满员);`LOOP_CONSTANTS.MAX_PENDING_COMMAND_RESULTS` 限量注入(超量留队续投,不截半丢弃);turn 终态(done/interrupted/error)对存活进程**脱管**(不杀)并出常驻报告,完成回流**跨 turn 留存**(阶段 3 修订见上方 Breaking 区)
|
|
423
|
+
- **进程治理**:`skillScriptRunner.buildSpawnOptions` + `attachGovernance` 收为单点(同步/后台共用),Windows 走 `taskkill /T /F` 杀树、POSIX 走 `kill(-pgid)`;`SYNC_MAX_TIMEOUT_MS`(600s) / `BACKGROUND_MAX_TIMEOUT_MS`(1800s=30min) 双常量分治(30min 只钳制**显式传入**的 timeoutMs,缺省不传 = 不限时,见上方诚实边界),`normalizeTimeoutMs(timeoutMs, maxMs)` 参数化取上限(不给 SSOT 函数开 mode 分支)
|
|
424
|
+
- **诚实口径**:「被主动终止」不是「执行失败」——强杀态独立 `formatKilledCommandOutput`,不声称任何退出状态(防 `[COMMAND_ERROR] 退出码 -1` 谎报);命令结果定长真源上移到格式化层(脚本/命令/后台回流三面共用,杜绝第二消费面看不到上限)
|
|
425
|
+
- **宿主**:工具中文名两键 + 行动叙述(叙述命令原文 + 后台标记)+ 设置面板「执行二次确认」补 `run_command` 并显式声明 git 写操作/发布不受开关影响(ALWAYS_ASK 恒询问);后台收尾报告上屏(「后台任务收尾」小节,实时/重放同源)
|
|
426
|
+
- **未做(如实登记)**:命令前缀 allow 白名单(阶段 2)、`.ps1` 运行时档(后台任务运行态列表与用户侧 kill 入口阶段 1 未做,已由同日阶段 2/3 补齐,见下方宿主区 Added 与阶段 3 Fixed 条目)
|
|
427
|
+
|
|
428
|
+
### Added(内核+宿主 · 环境能力宣告 · `CMD-2`,已定档 4.0.0)
|
|
429
|
+
|
|
430
|
+
**缺口**:`run_code` 描述曾写「如 python、node、shell」而宿主执行器只支持 JS(对模型说了假话);且模型对运行环境一无所知。
|
|
431
|
+
|
|
432
|
+
- **消灭谎言(声明列举)**:`ICodeExecutionProvider` 增**可选** `supportedLanguages?: readonly string[]`;`RUN_CODE_TOOL` 改工厂 `buildRunCodeTool(supportedLanguages?)`——声明时按实际生成语言清单,未声明退化为去承诺文案(零感知向后兼容)。工具描述与「暂不支持语言」回知文案**同源自宿主声明**,杜绝双写漂移
|
|
433
|
+
- **环境事实注入**:新增 `IEnvironmentProvider`(**同步** `getEnvironment(): HostEnvironmentInfo | null`)+ `HostEnvironmentInfo`(`os`/`shell`/`runtimes` 全可选),公开导出;`buildEnvironmentContextBlock` 纯函数格式化为 system prompt「## 运行环境」段(插作品投影后、时间戳前;全空字段不产生空壳标题段)。**零解释转发**——只做字段→文本机械映射,不加行为引导语
|
|
434
|
+
- ⚠️ **`shell` 字段语义 = 本会话执行器实际派发的 shell**(不是集成终端/登录 shell):内核 `resolveShellCommand` 恒定映射 win32 → `cmd /c`、其余 → `sh -c`,不读环境变量。两侧互为镜像,改一侧必须同批改另一侧(宿主测试从内核源码文本提取真源比对,防单边漂移)
|
|
435
|
+
- **刻意不做**:内核内置探测(派生判定律:环境事实不得升级为内核判据)、执行期自动语言切换(抢模型决策权)、cwd/git 分支注入(阶段 2 观察)
|
|
436
|
+
|
|
437
|
+
### Fixed(内核+宿主 · `compress_context` 单轮长 turn 压缩缺口 + 崩溃修复 · `CTX-WIN-2`,已定档 4.0.0)
|
|
438
|
+
|
|
439
|
+
**问题(诊断实锤,非推断)**:长单轮任务(一个提问跑几十个 step)把上下文顶满时,`compress_context` 结构性无可压目标——台账原描述「只能靠换区间重读硬扛」,实测**比这更严重**:
|
|
440
|
+
|
|
441
|
+
- `target=earliest_round` 在单轮 turn 内**恒返回 null**:锚点是「第一个 user vs 最后一个 user」,而单轮内只有一个 user(当前触发输入)⇒ 必然重合
|
|
442
|
+
- `target=largest_tool_result` **用了就崩**:原实现只压单条 tool 消息,其所属 `assistant.toolCalls` 留在上下文 ⇒ 发送边界守卫 `unpairedAssistantCall` fail-fast 抛错,**整个 turn 中断**
|
|
443
|
+
|
|
444
|
+
**根因**:不是「压缩能力不足」,而是**锚点粒度选错**——单轮内的历史是 **step 序列**,而既有定位器的锚点是 **user 消息**。
|
|
445
|
+
|
|
446
|
+
- **新增 `target=earliest_steps`**(step 粒度,长任务首选):step 边界 = 一条 `assistant` 消息 → 下一条 `assistant` 之前;「已回齐」判据 = 区间内每个 `assistant.toolCalls.id` 都有配对 tool 消息(未回齐不压,避免丢未完成语义)
|
|
447
|
+
- **`largest_tool_result` 改整段替换**:该 tool 消息 + 所属 assistant 消息同进同出 ⇒ 配对恒成立,修掉崩溃
|
|
448
|
+
- **target 枚举单点化并公开导出**:`CompressTarget` / `COMPRESS_TARGETS` / `COMPRESS_TARGET_LABELS`。标签表用 `Record<CompressTarget, string>` **穷尽键** ⇒ 日后加 target 忘改宿主文案 = **编译期红闸**(原宿主三元 `target === 'earliest_round' ? A : B` 会把新值静默显示成「最大工具结果摘要」= 对用户说谎)
|
|
449
|
+
- 工具描述补 target 选型指引;「无可压目标」的回传串列出合法 target 值(此前只说「目标不存在」,模型无法自纠)
|
|
450
|
+
|
|
451
|
+
**兼容性**:`target` 仍为可选参数,缺省/非法值仍降级 `earliest_round`——既有集成方零改动。新增导出为纯增量。
|
|
452
|
+
|
|
453
|
+
### Changed(内核 · 待注入插话上限裁决收口内核 · `KERNEL-QUEUE-LIMIT-1`,已定档 4.0.0)
|
|
454
|
+
|
|
455
|
+
**债务清偿**:INPUT-LIMIT-1 落地时插话条数上限只做在宿主两层(webview 预检 + extension 闸门),内核 `loop.interruptQueue` 本体 push 前无上限——第三方集成方绕过宿主直调 `agent.interject()` 仍可塞爆队列(队列是内核资源不变量,该内核自己堵,不能指望每个集成方自带限额)。
|
|
456
|
+
|
|
457
|
+
- **上限裁决单点收口内核**:`LOOP_CONSTANTS` 新增 `MAX_PENDING_INTERJECTIONS = 5`(值真源);`loop.interject` 返回 `boolean`——达上限**整条拒收**返回 false 不入队(插话是原子思路不截半条);`agent.interject` 改**先入队后持久化**(顺序即语义:拒收时零入队、零落盘,防「队列没进、落盘却写」数据不一致)
|
|
458
|
+
- **宿主消费真源**:extension 闸门改消费 `interject()` 返回值(快照预检自写判据退役,宿主零裁决逻辑);webview 预检仍用 `shared/constants.ts` 镜像常量(webview 无法 import 内核包),同值关系由宿主守卫测试锁定(漂移即红);pause 条目不受上限管辖(内部控制语义非用户输入洪流)
|
|
459
|
+
- **测试**:内核 loop 上限用例(满员拒收不入队 / 删一条可再入 / pause 不受限)+ 变异闭环(判据 off-by-one 精准红);`constants.test` 键数守卫 24→25 同步;agent 层 +4 用例(未初始化断言 / 受理入队返回 true / 满员拒收返回 false 队列不增 / 删一腾位再入——返回值透传与队列状态链路贯通;落盘内容无公开读口,闭环节点持久化由 messageHistory.test 既有覆盖);宿主 chatPanelInput 11 用例(桩默认受理态 / 满员拒收不上屏 / 防误伤上屏 / 跨包同值守卫)
|
|
460
|
+
- **版本**:内核语义变更(接口返回值 void→boolean + 新增常量),随已定档的 4.0.0 一并发布,不单独发版
|
|
461
|
+
|
|
462
|
+
### Fixed(宿主 · 轮次摘要失败可见化 + 摘要事件 handler 泄漏 · `MEM-1`)
|
|
463
|
+
|
|
464
|
+
**现象**:complete 轮缺少 round-summary 摘要记忆(记忆库无条目),而**用户与排查者都无从得知原因**。
|
|
465
|
+
|
|
466
|
+
**前提订正(原文「无任何可观测信号」只对一半)**:内核 `orchestrator.runSummary` **早已**在三条路径上
|
|
467
|
+
`emit('roundSummaryGenerated', { roundId, success })` —— 异步 reject / 同步 catch 均发 `success: false`,
|
|
468
|
+
「摘要关闭」发 `success: true`。**信号在事件层一直存在**;断点在**宿主消费点** ——
|
|
469
|
+
`chatPanel` 的 `onSummary` 只拿 `roundId` 解锁 UI 定时器,把 `success` **整个丢弃**。
|
|
470
|
+
|
|
471
|
+
- **观测出口接线**:`onSummary` 增 `if (!info.success)` → `post({ type:'notice', level:'error' })`,
|
|
472
|
+
走本文件既有的**错误级 `notice` 单通道**(`protocol.ts` 的 `level:'info'|'error'` 已有承载)——
|
|
473
|
+
**零协议变更、零内核改动、零判据改动**
|
|
474
|
+
- **⚠️ 对抗式审查推翻「记日志、不弹 UI」的原建议**:静默失败正是本缺陷的成因;低频(仅真失败时出现)
|
|
475
|
+
的错误提示条才真正闭合可观测链(用户可自证「这轮没沉淀」),仅日志留痕等于把问题继续藏着
|
|
476
|
+
- **顺带修同处第二个独立缺陷**:`agent.on('roundSummaryGenerated', …)` **非 `.once`**,此前仅
|
|
477
|
+
`setTimeout` 分支 `off`、**事件触发分支不解绑** ⇒ 每轮正常收场遗留一个 handler 累积;已补**对称解绑**
|
|
478
|
+
- **测试**:新增 `chatPanelSummaryFailure.test.ts` 4 用例(失败 → 错误 notice / 成功 → 不误报 /
|
|
479
|
+
notice 不阻断 UI 解锁 / 连跑三轮 handler 零残留)+ **变异双闸门已实证红→绿**(删 `if` → ①红;
|
|
480
|
+
删 `off` → ④红且报「3 handler 累积」)
|
|
481
|
+
- **⏳ 真机复验**:构造摘要失败(如摘要 LLM 返无效 JSON)时错误提示条应可见,且连跑多轮无 handler 累积
|
|
482
|
+
|
|
483
|
+
### Added(内核 · 会话离场自动归档链路接通 + 归档成功事件 · `HIST-INFO-1`,已定档 4.0.0)
|
|
484
|
+
|
|
485
|
+
**根因(注释谎报实锤)**:头注释与工具路标声称「会话切换前自动归档」,但 `switchToSession` 的归档
|
|
486
|
+
链路**从未接线**——所有会话的 `summary`/`keyTopics` 恒空,历史列表悬停 tooltip 假显宿主容器的
|
|
487
|
+
「当前会话」(真机 HIST-INFO-1 验收抓出)。三类自动触发(**切走 / 分叉离场 / 暂停超时**)统一由
|
|
488
|
+
Agent 归档监听器接通,fire-and-forget 不阻塞切换主流程。
|
|
489
|
+
|
|
490
|
+
**新增事件(2)**:
|
|
491
|
+
|
|
492
|
+
- `sessionSwitched`:旧会话切走时广播(载荷指向被切走的旧会话)。**内核自消费事件**——agent 归档
|
|
493
|
+
监听器收;宿主零消费 = 刻意设计(宿主是切换发起方,无增量信息),protocolGuard V-2 已登记
|
|
494
|
+
`KERNEL_SELF_CONSUMED_EVENTS` 豁免(⚠ 分表触发器:内核自消费事件增多时拆独立内部事件通道)。
|
|
495
|
+
- `sessionArchived`:归档写入成功(`updatedFields` 非空才发)发射,与 `archiveFailed` 对称,
|
|
496
|
+
手动触发同样经 ArchiveCoordinator 发射;宿主据此重拉会话列表。
|
|
497
|
+
|
|
498
|
+
**防重**:`SessionArchiver` 新增 `skipIfUnchanged`(仅自动触发透传):以上次**实际完成归档**的
|
|
499
|
+
消息数快照为判据,消息数未变即跳过 LLM 调用。快照为进程内内存态(重启清零,首次多归档一次可接受);
|
|
500
|
+
**写入成功才记账**——LLM 判无价值/写入失败不记,下次自动触发保守重试。手动触发无条件重新归档。
|
|
501
|
+
|
|
502
|
+
**注释清算(措辞折叠同族,5 处一次改齐)**:sessionArchiver 头注释 / archiveCoordinator 注释 /
|
|
503
|
+
builtinToolHandlers 路标 / agent.archiveSession JSDoc / ArchiveMode 注释中「切换前自动归档」
|
|
504
|
+
谎报全部订正为三类触发的真实枚举。
|
|
505
|
+
|
|
506
|
+
> 以下条目为 **4.0.0 定档(2026-10-08)之后、发布之前**落地的内核变更(此前沿提交散落、未在本文件归集),随 4.0.0 一并发布。
|
|
507
|
+
|
|
508
|
+
### Added(内核 · ask 超时后要求 LLM 代选推荐方案并声明 · `c2477907`)
|
|
509
|
+
|
|
510
|
+
- `ASK_TIMEOUT_NOTICE` 常量改写:ask 超时自动继续时,除陈述事实外,要求模型**从它自己给出的选项里
|
|
511
|
+
选一项推荐方案继续推进,并在答复中说明这是代选的及理由**;不可逆操作仍需向用户确认。
|
|
512
|
+
- **不改超时语义**:`ASK_TIMEOUT_MS`(120s 自动继续)与 `CONFIRM_TIMEOUT_MS`(30min 自动拒绝)的分级
|
|
513
|
+
刻意不同,本笔只补可观测性——不动时长、不动 fail-open/fail-closed 分级。
|
|
514
|
+
- 病灶(真机 round-1791449684099 实证):旧文案只说「已自动继续」⇒ 模型据此**暗中**替用户决策
|
|
515
|
+
(写完第 4 章后直接写死「下一步默认开工第 5 章」),用户回来只看到既定事实、无从否决。
|
|
516
|
+
- 一个字符串服务两个语境(渲染气泡给用户看 + 作 user 消息进模型上下文)⇒ 措辞两端都必须成立;
|
|
517
|
+
宿主 `chatPanel` 同名常量为镜像,两侧注释互相要求同值(防运行时 / 重放文案分叉)。
|
|
518
|
+
|
|
519
|
+
### Fixed(内核 · 收场取轮翻转 store-first:stale pending 快照不再覆盖已落盘事件 · `01388b1a`)
|
|
520
|
+
|
|
521
|
+
- `MessageHistory` 两处取轮顺序翻转:优先 `RoundStore`(SSOT,含宿主 step 检查点已落盘的
|
|
522
|
+
`processEvents`),pending 缓存降为 fallback。旧写法 pending 优先会用**无 processEvents 的
|
|
523
|
+
stale 快照覆盖 store** ⇒ 前段事件 / metrics 被静默清零、merge 结构性不可达(`METRICS-XFLOW-1`)。
|
|
524
|
+
- 订正 `appendInterrupted` 注释谎报(pending 优先时 `processEvents` 无从保留)。
|
|
525
|
+
|
|
526
|
+
### Fixed(内核 · 整读记账按请求起点 + 台账替身回显改为请求区间原文 · `912c1e5b` / `5cd77fdb`)
|
|
527
|
+
|
|
528
|
+
- **整读记账改按请求起点**:`loop` 依 handler 不变量(无脚注 = 从请求 offset 读到末尾)记
|
|
529
|
+
`coverStart=offset`、`totalLines=offset+正文行数-1`(`offset≤1` 与旧值等价)。旧写法把
|
|
530
|
+
`offset=54` 的续读(尾段无脚注)无条件记成 1–95 ⇒ ① `offset=1` 早段读(1–53 从未覆盖)被**假拦**;
|
|
531
|
+
② 回显按错误 `coverStart` 平移,把 L54+ 内容标成「第 1–53 行 / 共 95 行」——**行号与内容错位的
|
|
532
|
+
假事实**误导模型(真机 round-1791507775404 实锤)。
|
|
533
|
+
- 新增 `READ_FILE_NOTICE_PREFIX`(生成侧 / 识别侧同引):越界提示串带该前缀且**不进覆盖度台账**
|
|
534
|
+
(原会被记成覆盖,二次越界读命中归一判据被假拦)。
|
|
535
|
+
- 台账替身回显改为**请求区间原文**:防重拦截不再把模型逼进「拿不回整份视角」的死路(防绕道复发)。
|
|
536
|
+
|
|
537
|
+
### Fixed(内核 · denied 通道 PERMISSION_DENIED 前缀收口到单一构造点 · `0c7b6247`)
|
|
538
|
+
|
|
539
|
+
- `toolRunner` 的 denied 通道此前手写 `[ERR:TOOL:PERMISSION_DENIED]` 字面量,与 `builtinToolHandlers`
|
|
540
|
+
走 `failedOutcomeWithCode()` 的枚举派生构成第二真源漂移风险;改走
|
|
541
|
+
`failedOutcomeWithCode(ToolErrorCode.PERMISSION_DENIED, reason).text`,发射字符串逐字符不变,
|
|
542
|
+
denied 的机器维度 `reason` 保留。
|
|
543
|
+
- `READONLY_DENIED` 经 tsc 实锤**并非**枚举成员(仅为 `toolFailurePrefixGuard` 独立登记标签)
|
|
544
|
+
⇒ 保持手写字面量并补注释,避免误改成不存在的符号。
|
|
545
|
+
- 变异验证:`PERMISSION_DENIED → UNKNOWN` 使新增断言转红后还原(证断言非空洞)。
|
|
546
|
+
|
|
547
|
+
### Added(内核 · 发版前 Go/No-Go 中优先项清偿:文法盲区登记 + Reflection 设计口径显式声明 + 死分支不变式守卫 + 决策 ADR 补录 + §十六 限定句修正 · M3/M4/M6/M8/M10)
|
|
548
|
+
|
|
549
|
+
- **`toolFailurePrefixGuard` 新增文法盲区对账**(`GRAMMAR_BLIND_TAGS`):首字符即 `[` 但逃出
|
|
550
|
+
「大写蛇形」与「插值前置」两种文法的标记(上下文注入分节头 / 工具结果正文软提示 / 提取器
|
|
551
|
+
已知边界产物)原先对扫描面与排除清单**双双不可见**(双重静默),现按文件登记、全库双向钉死
|
|
552
|
+
——新增未登记盲区即红,登记项消失(如升级文法)即僵尸红。变异验证:删登记 / 生产新增盲区
|
|
553
|
+
标记均转红后还原。
|
|
554
|
+
- **api-ref §十五 显式声明「执行类失败不进自动 Reflection」(设计口径,非回归)**:
|
|
555
|
+
`run_command` / `run_code` / `run_project_script` / `run_skill_script` 四条执行链路失败返回
|
|
556
|
+
不带 `errorCode` 的失败结果,不满足 retryable 触发条件 ⇒ AgentLoop 不注入
|
|
557
|
+
`[REFLECTION_HINT]`;结局事实由返回文本承载,LLM 自行决定是否重试(错误码全集见 §十四)。
|
|
558
|
+
- **`strategyKeys` 死分支不变式守卫**:`describeStrategyKeys` 的 else 分支(check 无 range
|
|
559
|
+
且无专属分支的数值键)当前不可达——注释从「防御分支」改为「已登记的未来路径,由不变式测试
|
|
560
|
+
守卫」。新增全量不变式测试:遍历 `STRATEGY_KEY_RULES` 所有 check 键,断言「无专属分支的
|
|
561
|
+
check 键必带 range」。变异验证:删 `temperature` 的 `range` 后测试转红(精确点名该键),还原后全绿。
|
|
562
|
+
- **决策 ADR 补录**(`ADR-038` 工具结果三值契约 / `ADR-039` 公共导出面挂面判据):4.0.0 两条
|
|
563
|
+
Breaking 的决策 rationale 原只在不随包发布的过程稿里,现沉淀为仓库内定案锚;对外 rationale
|
|
564
|
+
入口不变(本文件上方 Breaking 条)。零代码、零消费面变更。
|
|
565
|
+
- **api-ref §十六 限定句修正**:⚠️ 注主句由全称「可具名导入的公开面以本节清单为准」收窄为
|
|
566
|
+
「正文表格里的类型名以本节清单为准(本节非全集,未列出的导出以 `src/index.ts` 为准)」——
|
|
567
|
+
原全称与同节首注「清单外导出以 index.ts 为准」当面矛盾(措辞折叠同族)。语义未变,只消除
|
|
568
|
+
误读面:§十六 本就是精选子集而非全集。
|
|
569
|
+
|
|
570
|
+
### Changed(示例包 · 共鸣小说家「纲是骨架、章是装箱」:正文落地前禁止预排章节)
|
|
571
|
+
|
|
572
|
+
- **规范本体**(`rules.md`):任何纲(大纲/细纲/场景清单)只限定故事骨架——情节序列、事件、
|
|
573
|
+
情绪走向;正文落地前不写"第N章"、不做逐章切分,章是成文装箱时才产生的弹性容器(核心框架间
|
|
574
|
+
随时可插支线/番外而章号不错位)。成文章号回填(时间线/伏笔台账)与逐章写作纪律(章末钩子/
|
|
575
|
+
单章篇幅)豁免。
|
|
576
|
+
- **消费面同步**:persona 阶段5 三幕骨架定情节节点(不预排章节)、outline-expand ③ 章纲→细纲
|
|
577
|
+
(事件序列,展开纪律 + 校验清单双判据)、foreshadow / story-bible 伏笔表头章节→事件/场景、
|
|
578
|
+
emotion-wave 落点列章节→事件、init 模板 `T_BIBLE_4` / `T_OUTLINE` 同构(顺手修正五级展开
|
|
579
|
+
描述与真源的既有漂移)。
|
|
580
|
+
- **验证**:role-pack 258 passed;prettier / terminology(71+82 载体)/ docs:links(45 随包 md
|
|
581
|
+
0 死链)/ lint 全绿;规划态章节限定词(「章纲」等)全包残留扫描 0。
|
|
582
|
+
|
|
583
|
+
## [Unreleased] · 宿主(随做随用 · 不占内核版本号)
|
|
584
|
+
|
|
585
|
+
> **本区归属**:仅**宿主**(`hosts/memora-vscode`)变更——不在内核发布包 `files` 白名单内、无独立 marketplace 节奏 ⇒ 不编内核版本号,随做随用,攒批随下次 `vsce package` 定版(`vscode@x.y.z` tag,ADR-033)。
|
|
586
|
+
|
|
587
|
+
### Fixed(宿主 · 多流续跑 metrics 同轮合并:前段指标不再被静默清零 · 零 schema 变更)
|
|
588
|
+
|
|
589
|
+
**真机实锤**:一次 turn 分多段流完成(软暂停续跑 / ask 回答后续跑)时,每段流尾各自 emit 一条
|
|
590
|
+
段级 metrics;`mergeProcessEvents` 旧实现把 prior 的 metrics **恒剔除** → 前段全部指标丢失,
|
|
591
|
+
回合终态只剩末段(前段 7 次工具失败被静默清零成 0,「不算成功收尾」诚实信号随之失真)。
|
|
592
|
+
|
|
593
|
+
- **同轮合并**(新私有助手 `mergeMetricsPayload`):`durationMs` / `tokenIn` / `tokenOut` /
|
|
594
|
+
`toolFailureCount` 求和;`success` 取末段(语义是「这段流程跑完没有」,终局收尾以最后一段
|
|
595
|
+
为准);三个可选诚实信号任一段携带即求和(缺段按 0)、两段都缺保持缺省(不伪造信号面)。
|
|
596
|
+
- **检查点路径保留**:incoming 无 metrics(step 检查点常态)时 prior metrics 原样保留待流尾
|
|
597
|
+
合并,防双计与丢失并存;emit 点与存储 schema 零变更。
|
|
598
|
+
- 测试:合并求和 + success 取末段 / 可选字段三态 / 检查点保留 3 用例;变异验证(恒剔回退 →
|
|
599
|
+
检查点用例红、合并分支拆除 → 求和双用例红 → 恢复全绿)已做;宿主全量 57 文件回归绿。
|
|
600
|
+
|
|
601
|
+
### Fixed(宿主 · 队伍对账语义变更:清理写回 → 只检测标注,删除权交还用户 · 含内核消费端同口径收口)
|
|
602
|
+
|
|
603
|
+
**真机实锤**:2026-10-07 构建中窗口(compile 先 `rmSync dist` 后重建)启动扩展,包池扫描为 0;启动对账把「扫描失败」当成「全部卸载」,用户所有队伍被静默物理删除写回(仅 console.warn,无 UI 通知,不可恢复)。
|
|
10
604
|
|
|
11
|
-
|
|
605
|
+
- **只检测不删除**:`reconcileTeams` / `reconcileRolePackTeams`(清理写回)退役 → `inspectTeamsHealth` / `inspectRolePackTeams`(只读派生,零写回零内核调用);启动接线点随之退役,唯一接线点 = `loadRoles` 读期检测。受损队伍保留在存储(SSOT:存储即真源,检测是视图)。
|
|
606
|
+
- **空池护栏**:包池为空 = 扫描失败窗口,跳过检测防误报(把瞬时状态当永久事实正是伤例根因)。
|
|
607
|
+
- **角色页持久标注**:遗留队伍独立区块(组长已卸载的组无卡片可挂)+「清理」按钮(复用 `roles_team_delete`);缺员组员名后缀「(已卸载)」,经组队弹窗编辑保存即修复;参会计数与内核先截断后过滤口径一致(不虚报)。
|
|
608
|
+
- **内核同口径收口**:`effectiveMembers`(截断 + 缺员过滤,单一实现)收口 `activeTeamMembers` / `getTeam`——名单输出 = 实际可参会者,会议任务项不为幽灵组员建发言项;存储原样不裁切。守卫:内核 3 用例(缺员过滤 / 存储原样 / 截断不回补)+ 宿主检测 8 用例 + 角色页渲染 3 用例。
|
|
609
|
+
- **语义变更记录**:ADR-028 收敛补记二(2026-10-08)。
|
|
610
|
+
|
|
611
|
+
### Added(宿主 · 角色包「刷新」按钮:用户角色包目录改动免重启生效)
|
|
612
|
+
|
|
613
|
+
- 角色页头部新增刷新按钮(对齐技能列表刷新形态):webview 发 `roles_refresh` → extension 调内核
|
|
614
|
+
`resyncUserPacks(userRolePacksDir)` 对账内存池 → notice 反馈净差结果(新增/更新/移除/兜底回退,
|
|
615
|
+
零变化明示「无变化」)+ `roles_loaded` 下发最新列表。
|
|
616
|
+
- **测试**:webview 按钮绑定(postMessage 契约)+ extension handler(净差文案 / 同名重注入报
|
|
617
|
+
「更新」不谎报 / 零变化明示 / 悬空回退提示 / 未就绪拒绝)共 6 用例锁定。
|
|
618
|
+
|
|
619
|
+
### Fixed(宿主 · 历史条目悬停假显「当前会话」+ 归档后条目不刷新 · `HIST-INFO-1`)
|
|
620
|
+
|
|
621
|
+
- **title 泄漏**:会话标题条容器级 `title="当前会话"` 被所有无 title 的后代继承——历史下拉任何条目
|
|
622
|
+
悬停都显示「当前会话」。title 移至内层 `#sessionTitleText`;条目无摘要时诚实无 tooltip。
|
|
623
|
+
骨架 title 防泄漏守卫测试锁定(测试夹具同步)。
|
|
624
|
+
- **归档完成无信号**:切走时归档异步执行,列表快照永远拿不到后到的 summary/keyTopics(悬停恒空)。
|
|
625
|
+
订阅内核 `sessionArchived` 重拉会话列表,条目摘要/keyTopics 即时更新。
|
|
626
|
+
- **守卫反噬两处**:iconLanguage 守卫头注释声明「注释里的符号不管」但只剥 JS 注释、未剥 HTML 注释
|
|
627
|
+
(补 `<!-- -->` 跨行剥离状态机,对齐自身声明;HTML 注释被浏览器解析为不可见注释节点,剥离不藏
|
|
628
|
+
真 offender);protocolGuard V-2 为内核自消费事件(`sessionSwitched`)增设显式豁免清单,
|
|
629
|
+
防造假订阅(订阅无真实语义 = 假账)。
|
|
630
|
+
|
|
631
|
+
### Fixed(宿主 · 异常终态轮缺「复制/分叉/删除 + 时间戳」底部栏 · settleRoundFooter 收口)
|
|
632
|
+
|
|
633
|
+
**真机实锤**:生成中点停止(尤其首 chunk 前的思考/工具相位),中断轮只剩停止行——无复制整链/分叉/删除/时间戳底部栏;重启渲染(重放)则正常显示。
|
|
634
|
+
|
|
635
|
+
- **根因**:容器 footer 显形由 `finalizeStreaming` 顺带移除 is-pending,前提 = 正文块已入容器——早相位中断时骨架未归位,`closest('.round-group')` 命中 null,显形信号静默丢失;done 恒在正文定稿后,故正常轮不受影响。附带伤:运行时建 footer 时首段 `dataset.ts` 恒空 → 时间戳元素从未创建
|
|
636
|
+
- **修法 = 显形真源回归终态事实**:新增 `settleRoundFooter(roundId, hostEl)` 单点收口(锚 = done/interrupted 终态消息携带的 roundId,不依赖 DOM 连通性):容器已建 → 移 is-pending + 时间戳补建;未建(早相位中断)→ hostEl 作首段建容器(与重放 `renderInterruptedRound` 同构);roundId 缺失诚实降级不建栏。`finalizeStreaming` 只保留段级 footer 职责,容器显形不再双写
|
|
637
|
+
- **复制源同源化**:中断 = 半截正文作废,rawText 覆盖为过程叙述(narrate)拼接——抽 `narrateChainText` 单点与重放共用(旧注释「运行时同源同形」只对「不显示正文」成立、rawText 拼接运行时缺失,属措辞折叠订正)
|
|
638
|
+
- error 不接入:error→done 两连发是既定架构(error 为末条属登记过的可接受残留),不扩面
|
|
639
|
+
- **测试**:`chatViewInterruptedFooter` 三相位用例(正文流中断/早相位中断/done 回归)+ 变异 2 组逐组精确转红(删 settle 调用 → 两中断用例红;删 narrate rawText → 复制源断言红)
|
|
640
|
+
|
|
641
|
+
### Fixed(宿主 · 插话丢弃信号「信号帧 → 快照帧」成对契约三路径补齐)
|
|
642
|
+
|
|
643
|
+
**变异验证暴露**:修复停止路径补 `syncPendingQueue` 时,「全部清空」存量用例红——`clear_pending_queue` 路径在「宿主从未投影过队列」场景(如 resume 恢复检查点带队列后 webview 重建)发信号但无成对快照帧。
|
|
644
|
+
|
|
645
|
+
- `handleStop` paused 分支补 `syncPendingQueue()`(三条丢弃路径中唯一漏跟契约的一条)
|
|
646
|
+
- **根因**:`_lastPendingQueueLen` 初始值 `0` 把「从未投影过」与「已投影为空」折叠成同一值——清空后长度未变被守卫误拦。改 `undefined`(从未投影 → 首次 sync 恒推),单点修正三条路径同时受益
|
|
647
|
+
- **测试**:停止/清空两用例补「信号先于新快照 turn_update」FIFO 时序断言(与删除单条路径同款);变异 2 组逐组转红(删停止路径 sync → 停止用例红;恢复折叠 0 → 清空用例红)
|
|
648
|
+
|
|
649
|
+
### Fixed(宿主 · 消息区自动吸底脆弱——MutationObserver 收口「高度变更不派发 scroll 事件」整类缺口)
|
|
650
|
+
|
|
651
|
+
**真机实锤(用户反馈)**:点开思考折叠块后拉到底部不吸底;部分工具运行期也不吸底——且「可能还有其他情况」。
|
|
652
|
+
|
|
653
|
+
- **根因**:吸底标记 `stickyMap` 仅由 `scroll` 事件经 `trackScroll` 刷新;展开/收起 `<details>`(思考块、工具行)、工具流式增删、长气泡展开、中断轮重建等**高度变更不派发 scroll 事件**却改写「是否真在底部」真值 → 标记与实际位置脱节(可双向错)。原注释「吸底判定在 DOM 变更前记录避开 tall-message 陷阱」只覆盖「新增内容前先记录状态」一侧,漏了「高度变更不刷新状态」一侧
|
|
654
|
+
- **修法(SSOT 收口)**:`helpers/scrollToBottom.ts` 新增 `followIfPinned(container)`——只读**既有**吸底意图(由 trackScroll 在用户滚动时记录、默认吸底),吸底则 rAF 滚到底、返回意图供「一键到底」按钮显隐;webview 在消息区挂 rAF 去抖 `MutationObserver`(`childList + characterData + attributes:['open'|'class'|'style'|'hidden']` subtree)delegate 到 `followIfPinned`。逻辑全收口于 helper,webview 只接线(不复制吸底逻辑、不碰 DOM 模板、不新增长度常量)。闭合整类而非两个症状
|
|
655
|
+
- **测试**:`scrollToBottom.test.ts` 2 条(jsdom 无布局,Object.defineProperty 注入几何);变异 2 组转红(删 `if (sticky)` 分支 → 吸底用例 scrollTop 断言红;删 `return sticky` → 非吸底用例返回值断言红)
|
|
656
|
+
|
|
657
|
+
### Fixed(宿主 · 渲染异常用户侧静默——onMessage 兜底补非阻断红条 · 观察点③)
|
|
658
|
+
|
|
659
|
+
**缺口(上一轮审计坐实)**:`onMessage` catch 仅 `console.error`,用户侧零呈现 → 渲染抛异常时看到「冻住/半成品 UI + 无报错」的可见性黑洞。
|
|
660
|
+
|
|
661
|
+
- **修法**:catch 内补 `showActivity('error', '界面渲染异常,详情见开发者控制台')` 非阻断红条,与 `console.error` 互补(根因定位不掩盖);`showActivity` 为既有活动条通道(P0 错误优先)
|
|
662
|
+
- **测试**:`chatViewRenderErrorBanner.test.ts` 1 条(dispatch 一条缺 `event` 字段的 `process_event` → `ev.type` 抛真实 TypeError 被兜 → 活动条 `data-level==='error'`);变异 1 组转红(删 showActivity 调用 → 断言红)
|
|
663
|
+
|
|
664
|
+
### Changed(宿主 · 终态 footer 显形与 commitTurnTs 前置防御性收口为单操作)
|
|
665
|
+
|
|
666
|
+
- **复核订正(2026-10-05)**:上一轮将「settleRoundFooter 顺序契约」诊断成「时间戳补建依赖 commitTurnTs 前置」——**实测不成立**:footer 时间戳元素由流式期 `ensureRoundGroupFooter` 按首段 `dataset.ts` 直接生成,`commitTurnTs` 缺位时三相位用例时间戳仍齐全。故该组合**不修复已复现的 bug**
|
|
667
|
+
- **保留价值(防御性)**:`settleRoundWithFooter(roundId, hostEl)` 把 `commitTurnTs`(删除按钮 ts 锚,`ensureRoundGroupFooter` 内 `deleteBtn.disabled = !firstSeg?.dataset.ts`)与 footer 显形收口为单操作,对任意「终态」路径保证 commitTurnTs 随 footer 显形恒执行,消除调用方需记的隐式顺序,防未来某终态路径未在建栏前回填 ds 致删除按钮恒禁用。属防御性收口,注释已对齐真源
|
|
668
|
+
|
|
669
|
+
### Added(宿主 · 后台任务可见性与 kill 入口 · CMD-1 阶段 2 · 协议 + 接线)
|
|
670
|
+
|
|
671
|
+
**缺口**:后台任务运行态对用户零可见、无 kill 入口(内核能力齐备但零导出,宿主取不到数——详见内核区同批条目)。
|
|
672
|
+
|
|
673
|
+
- **协议 +2 通道**(105 → 107,远低于 130 治理阈值):下行 `background_tasks`(快照)、上行 `background_kill`(taskId)
|
|
674
|
+
- **投影约束**:`BackgroundTaskView = Pick<BackgroundTask, 'taskId'|'command'|'status'> & { statusLabel }`——改内核字段名即编译报错,禁另立第二套字段;**刻意不含 `result`**(命令输出归模型消费,不搬进 UI 通道)。`statusLabel` 由 extension 侧读内核 `BACKGROUND_TASK_STATUS_LABELS` 填充后下发(webview 只做 type import,值导入内核会把整包打进 bundle)
|
|
675
|
+
- **刀 3 · webview UI**:StatusDock **第四条**(`DOCK_PRIORITY` += `background: 0`,优先级垫底 ⇒ 不顶掉 activity 主状态条;轮次收尾后 activity 归位、后台任务成为唯一活跃条时自动升主位,「终止」按钮恰在最需要它的时刻直接可见)。行 = 命令原文(过长截断 + `title` 全文)+ 状态标签 + 「终止」按钮(**仅 `running` 行有**,终态行无按钮——已结束再点即无反应 = 僵尸交互)。终止按钮**只 postMessage 回宿主**,杀进程不自实现(执行只能落 host)
|
|
676
|
+
- **UI 形态实施修订(相对方案文档 §2.3 原定案)**:初稿定「新增『后台 N』chip」,实施读穿 `resolveDock()` 后否决——既有 `+N` chip / `status-dock__panel` 已完备,再造第二个 chip = 两套 chip、两套展开态、两套显隐裁决,纯负收益。代价如实登记:收纳时先看到无语义的 `+1`,需点开才知是后台任务(与 fileChanges / pendingQueue 同一取舍,不为其破例)
|
|
677
|
+
- **推送点四个(零定时器、零轮询)**:`step_boundary`(任务刚启动的唯一气口——step 含一次 LLM 迭代 + 其工具执行,边界到达时快照已含它)/ `background_report`(终态批量变化)/ `background_kill` 处理后回推 / **`webview ready` 全量对齐**。三点共用**内容签名去重**(`taskId:status` 拼接串,变化才推)——用签名而非条数,因 kill 后 `running → killed` **条数恒为 1 而内容已变**,长度守卫会把这次关键刷新判成 no-op。⚠️ **阶段 3 补漏增补第五点**:内核 `backgroundTaskSettled` 事件(turn 结束后自然终态通知),现行推送点以代码 `postBackgroundTasks` 注释 / `protocol.ts` 五枚举为准,见上方阶段 3 Fixed 条目
|
|
678
|
+
- **⓿ ready 对齐必须走 `force` 绕去重(自审带伤第 4 条)**:三个周期性推送点漏了**时间面**(本项目已踩四次)——面板折叠/关闭再打开时新 webview 从未收到 `background_tasks` ⇒ **条凭空消失而进程还在跑、用户也砍不掉**。而签名存于 extension 实例、**跨重建残留** ⇒ 光加补推会被自己的守卫吞掉。故拆 `dedup`(周期降噪)/ `force`(视图重建后对齐)两语义,不合并成一条判据
|
|
679
|
+
- **接线**:`chatPanel` 增 `postBackgroundTasks()` / `killBackgroundTask()`;agent 未就绪时不推(否则「未就绪」被 UI 读成「没有后台任务」)
|
|
680
|
+
- **测试**:`chatPanelBackground.test.ts` 8 条(快照只下发 4 键不含 result / agent 未装配不推 / kill 后回推终态 / 签名未变不重推 / **签名降级为长度则吞掉 kill** / **force 绕过去重** / **ready 补推接线** / 经 `resolveWebviewView` 真实注册回调驱动的 `background_kill` 路由跳)+ `chatView.test.ts` 6 条(空快照零占用 / running 行三要素 / 终态行无按钮 / 终止只回宿主载荷带 taskId / 长命令截断 + title 全文 / 优先级垫底不顶 activity)
|
|
681
|
+
- **变异验证 7 组**(逐组确认对应用例转红):终态行按钮判据去掉 / 后台条优先级改 9 / 签名降级为长度 / 去掉签名去重守卫 / 路由跳改传常量 taskId / force 也走去重 / ready 补推改走 dedup
|
|
682
|
+
- ⚠️ **变异验证中抓到两处「验证本身不锐」**:① `force` 用例初版断言「总共推 N 条」,被两种不同错误实现同时满足(dedup 退化成不设限 / force 也被去重)⇒ 变异下仍绿,改为断言**增量恰为 1** 才变锐;② 变异写成 `'force' as 'dedup'`——**类型断言不改运行时值**⇒ 变异根本没生效,差点误判成「守卫无效」
|
|
683
|
+
- ⚠️ **已知取舍**:状态文案走协议下发而非 webview 自建词表(真源仍是内核那张表,宿主零自建);面板内色条复用 `--accent`(`--status-running` 在 `tokens.ts` 无定义,写了就恒走 fallback、不跟随主题)
|
|
684
|
+
|
|
685
|
+
### Fixed(宿主 · 任务项组层级与折叠箭头右置 · `TOP-UI-2` / `TOP-UI-3`)
|
|
686
|
+
|
|
687
|
+
**缺口(用户真机反馈)**:对话流任务表四级内容左对齐糊成一片;折叠箭头左置压迫行首,标题无法顶格。
|
|
688
|
+
|
|
689
|
+
- **定案形态**:组标题(`summary`)**顶格最外侧**、与叙述/工具行同列(组本身零 padding、无边线——边线恒在最左,与「标题最外侧」结构性冲突,分组语义由下方组内缩进独立表达);**组内过程行缩进一档**(`--sp-6` = 16px,与正文 markdown 列表缩进同档)
|
|
690
|
+
- **折叠箭头分槽**:chevron 移 `::after` 绝对定位右缘(收起「>」/ 展开「⌄」),状态环保留 `::before` 紧跟工具名——原「高特异性覆盖」方案的两条 `transform: none` 防漂移 hack 随分槽退役;summary 加 `position: relative` + `padding-right: 14px`;运行中工具行右缘 chevron 压掉(环即状态即类型,防双标识)
|
|
691
|
+
- **落地过程**:`bf2b8f0d` → `6b86973b`(推翻首刀「组整体右移」方案)→ `b82d7a21`(chevron 右置)三刀;运行时与重放共用 `getOrCreatePlanItemGroup` 唯一构造点,改样式即两路径同逻辑生效
|
|
692
|
+
- ⚠️ **零自动化测试覆盖**:纯 CSS 单文件改动(`chatStyles.ts`),DOM 断言不覆盖 ⇒ 验收口径为「点验前 `npm run compile` 重建 dist + 真机对照」(宿主 dist 已随本批过期)
|
|
693
|
+
|
|
694
|
+
### Fixed(宿主 · 角色页详情/编辑弹窗永不出现 · `RP-EDIT-1` 功能不可达回归)
|
|
695
|
+
|
|
696
|
+
**根因**:`roles_detail_data` 的重挂判据写成「弹窗已开才重挂」(`if (detailOverlay) renderDetailView()`)。该消息只有两个发送点——① `roles_detail` 应答(用户点卡片,**此时弹窗未开** → 首挂查看态)、② `roles_save` 成功回发(弹窗必然开着 → 先摘后挂回显)。判据把场景①整体挡死 ⇒ **详情/编辑功能从未弹出过**。
|
|
697
|
+
|
|
698
|
+
- **修法**:`roles_detail_data` 到达即无条件 `renderDetailView()`(`mountDetailModal` 幂等,`detailOverlay?.remove()` 先摘后挂,两场景同函数通吃)
|
|
699
|
+
- **纪律**:RP-EDIT-1 销项时判定为「✅ 已落地 · ⏳ 待真机点验」,真机一验即现**功能整体不可达**。同型于 `CMD-1-BYPASS-FIX` 已立纪律——新增判定分支必须同时补「该分支被走到」的用例,fixture 取生产真实形态;「单侧单测全绿」只证明没跑到
|
|
700
|
+
- **测试**:`rolesView.test.ts` +93 行 / 18 passed(2026-10-04 实测)
|
|
701
|
+
|
|
702
|
+
### Fixed(宿主 · 待发送区空条常驻 + 队伍幽灵引用清理 · `STATUS-DOCK-FIX` / `TEAM-GHOST-1`)
|
|
703
|
+
|
|
704
|
+
- **待发送区空条常驻**(`5aaff721`):空态判断**提前至懒创建前**(与文件改动条范式同构)——先判空再建 DOM,杜绝「有容器无内容」的空条常驻;新增 2 条测试锁定
|
|
705
|
+
- **组员侧标注退役 + 启动/面板读期队伍对账**(`b9c93eba`):`reconcileTeams` 纯函数 + 双接线点写回 `globalState`,清幽灵引用(队伍已删但标注仍在)
|
|
706
|
+
|
|
707
|
+
### Added(宿主 · 角色包编辑 UI `RP-EDIT-1`:键面查看与编辑——键面单源内核,保存全链路 extension 侧)
|
|
708
|
+
|
|
709
|
+
**缺口**:角色页此前全只读(卡片/来源徽章/策略 chips/健康区俱全),真缺口只有「改的入口」(先例同型 = FD-2 技能)。键面清单若放宿主 = 第二真相源,且未知策略键在内核校验中只 warning + 忽略 ⇒ UI 写错键名时保存不报错、该键静默不生效(健康徽章兜不住)。
|
|
710
|
+
|
|
711
|
+
- **内核键面单源**:`strategyKeys.ts` 新增 `describeStrategyKeys()` 导出(`StrategyKeyFace`:stage/key/kind/values?/options?/range?)——`check` 是闭包不可过协议、`askOn` 可选值藏于 `ASK_TRIGGERS`,键面派生收内核单点,宿主零清单维护;0 哨兵键按 `check(0)` 探测折算 UI 区间下限(0=不干预是合法填写,直接用 validator 区间会让用户填不了 0)
|
|
712
|
+
- **协议 +3 通道(102→105)**:上行 `roles_detail` / `roles_save`、下行 `roles_detail_data`、`roles_loaded` 扩 `keyface`(键面为静态派生与装配时序无关,rpm 未就绪分支也下发)
|
|
713
|
+
- **保存链路全在 extension 侧**:内置真拒绝(`rolePackSource` 单点判据,webview 隐藏编辑按钮只是呈现层,直接发消息也被拒)→ 读 manifest 原文**全量保真**只换 strategy 段(skills/displayName 等原样保留;空对象删键保持「未声明」语义)→ `validateManifestText` 首个 error 拦截不写盘 → 写盘必 `rpm.reload()` 失效装配缓存(下个 turn 立即用新配置)
|
|
714
|
+
- **表单初值 = manifest 文件原文,禁用装配值**:`rpm.get().strategy` 是合并默认值后的完整策略,回填保存会把默认值固化成显式声明,破坏「未声明」语义
|
|
715
|
+
- **webview 渲染层**:卡片点击开详情弹窗(复用 team-modal 令牌)+ 16 键中文表单(`STRATEGY_KEY_META` 标签/tooltip/选项文案,键集与内核双向一致守卫锁定)+ selfReview 做开关(默认 0=关,未勾=不写入不固化 0)+ askOn 多选 + **preserved 机制**(键面未覆盖的原文键保存时原样并入,防整段替换静默丢数据)+ enum 原文越界值回填「原文值」选项(防回填即改值)
|
|
716
|
+
- **测试**:内核键面派生 5 用例(含 0 哨兵折算与 askLimit 例外);宿主键面双向一致守卫(内核键集 ↔ META 键集,双向漂移都红 + 防假绿前置自证)+ 表单收集语义 5 用例;保存链路 6 用例——变异 M1 内置拒绝(不读不写不 reload)/ M2 全量保真(skills 不丢)/ M3 校验失败不写盘 / M4 reload 缺失全部锁定
|
|
717
|
+
- **验证**:内核全量 2840 passed | 4 skipped;宿主全量 883 passed | 2 skipped;宿主 tsc 0 / eslint 0 / prettier 0
|
|
12
718
|
|
|
13
719
|
### Fixed(宿主 · 挂起/插话补充输入渲染根治:骨架不删 + 正文单块续写统一)
|
|
14
720
|
|
|
@@ -238,15 +944,15 @@
|
|
|
238
944
|
- **自纠(本轮差点造的假注释)**:初稿把上述风险写成「已明确告知」——**不实**:模态文案只在 dirty 分支出现。已改为如实描述并同步登记台账。
|
|
239
945
|
- **验证(实测)**:宿主 `tsc --noEmit -p ./` = 0;`eslint --config eslint.config.mjs` = 0;fileChange 三套件 **88 passed**。
|
|
240
946
|
|
|
241
|
-
## [3.0.1] · 2026-09-30
|
|
242
|
-
|
|
243
|
-
### Changed(开源归口翻转:GitHub 升主仓库)
|
|
244
|
-
|
|
245
|
-
- **README / README.en**:反馈、宿主项目链接、ADR 位置共 6 处归口从 Gitee 翻转为 GitHub 主仓库(Gitee 降为同步镜像,保留国内访问提及)
|
|
246
|
-
- **package.json**:`repository` / `homepage` / `bugs` 指向 `github.com/zooique/memora`(npm 页面导流、dependents 与搜索入口随 GitHub 生态)
|
|
247
|
-
|
|
248
|
-
> 纯元数据与文档归口变更,`dist` 产物与 3.0.0 无差异。
|
|
249
|
-
|
|
947
|
+
## [3.0.1] · 2026-09-30
|
|
948
|
+
|
|
949
|
+
### Changed(开源归口翻转:GitHub 升主仓库)
|
|
950
|
+
|
|
951
|
+
- **README / README.en**:反馈、宿主项目链接、ADR 位置共 6 处归口从 Gitee 翻转为 GitHub 主仓库(Gitee 降为同步镜像,保留国内访问提及)
|
|
952
|
+
- **package.json**:`repository` / `homepage` / `bugs` 指向 `github.com/zooique/memora`(npm 页面导流、dependents 与搜索入口随 GitHub 生态)
|
|
953
|
+
|
|
954
|
+
> 纯元数据与文档归口变更,`dist` 产物与 3.0.0 无差异。
|
|
955
|
+
|
|
250
956
|
## [3.0.0] · 2026-09-30
|
|
251
957
|
|
|
252
958
|
### Added(内核+宿主 · 截断救回计数 truncationRecoveryCount:换策略重试效力的观测面 + 文案口径订正)
|
|
@@ -257,7 +963,7 @@
|
|
|
257
963
|
- **文案 off-by-one 订正**:截断兜底文案「已自动**重试** N 次」传入的实为**尝试总数**(1 首试 + N 重试,恒多报 1)→ 中英双文案统一「尝试 N 次」口径(`empty after N attempts` / 「已自动尝试 N 次仍为空」),`UIMessages` jsdoc 钉死口径防改回
|
|
258
964
|
- **注释口径订正(HOST-S11 同族注释漂移)**:meta 取证包 `reasoningEffort` 原注释「与内核 llmCaller 转达口径同源」已被 T2 打破(`'low'` 有策略键 manual / `truncatedRetry` 运行时置入**两条**发送路径,meta 只镜像配置面)→ 改述「流首**配置面**快照,实际发送面以 `truncationRecoveryCount` 佐证」(chatPanel + roundStore 两处);`foldThoughtEvents` jsdoc 补**前提不变量**(seq 幂等依赖「检查点只在 step 边界触发」,破坏即静默丢内容——T4-FOLD-1 修法 A)
|
|
259
965
|
- **测试**:llmCaller 救回计数正反面(救回 +1 / 截断耗尽·瞬态救回·正常产出均 0)+ metrics 出闸守卫(镜像 `pairingGuardFires` 先例:只自增不外露 = 僵尸计数)+ roundEvidence 默认文案口径守卫(禁写 `retried N`)+ chatView 指标行/缺省面 2 例。**变异 4 方向**(摘计数行 / 去 `truncatedRetry` 条件致瞬态误计 / 摘指标行 / 文案改回 off-by-one)各恰红 → 恢复复绿。门禁:内核 `typecheck` / `eslint` 0 / 全量 **2830 passed | 4 skipped**、宿主 `tsc` / `eslint` 0 / 全量 **835 passed | 2 skipped**、format 0、`terminology:check` 零漂移(测试游标命名蹭了 step 族载体被门禁拦下,改 cursor 前缀消漂移)
|
|
260
|
-
- **真机复核收口(2026-09-29 · 小说项目)**:用户配置 maxTokens=64K 后连跑 2 轮(round-1790686368607 / round-1790686698903)——meta 取证包 `maxTokens: 64000`(T1 配置链路端到端实证);round-...6368607 `truncationRecoveryCount: 1`(真机截断被换策略重试**实战救活**,收场真实正文——T2 效力实证);两轮 `emptyResponseCount: 0`;round 文件 26KB / 37KB < 200KB(T4 折叠真机复核过)。T2 真机验证清单三件全答(组合策略有效 / 尾部注入被中转接受 / 救回计数可读),`EMPTY-RESP-1` 观察行核销(收据:`tasks
|
|
966
|
+
- **真机复核收口(2026-09-29 · 小说项目)**:用户配置 maxTokens=64K 后连跑 2 轮(round-1790686368607 / round-1790686698903)——meta 取证包 `maxTokens: 64000`(T1 配置链路端到端实证);round-...6368607 `truncationRecoveryCount: 1`(真机截断被换策略重试**实战救活**,收场真实正文——T2 效力实证);两轮 `emptyResponseCount: 0`;round 文件 26KB / 37KB < 200KB(T4 折叠真机复核过)。T2 真机验证清单三件全答(组合策略有效 / 尾部注入被中转接受 / 救回计数可读),`EMPTY-RESP-1` 观察行核销(收据:`tasks/archive/completed-history.md` 2026-09-29 收口条)
|
|
261
967
|
|
|
262
968
|
### Fixed(内核 · maxTokens 上限不再静默丢弃:上限不裁决,交服务端可见报错)
|
|
263
969
|
|