@thincoder/core 0.9.1 → 0.9.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 (75) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/LICENSE +21 -0
  3. package/README.md +90 -0
  4. package/advisor/loop.mjs +2 -2
  5. package/advisor/run.mjs +1 -1
  6. package/agent/completion.mjs +3 -1
  7. package/agent/family-tools.mjs +24 -11
  8. package/agent/helpers.mjs +11 -2
  9. package/agent/run-stages.mjs +27 -5
  10. package/agent/setup-reminders.mjs +67 -11
  11. package/agent/setup.mjs +6 -0
  12. package/agent/write-gate.mjs +5 -5
  13. package/agent-tools/advisor-async.mjs +4 -4
  14. package/agent-tools/advisor.mjs +3 -3
  15. package/agent-tools/async-discard.mjs +1 -1
  16. package/agent-tools/audit-block.mjs +106 -0
  17. package/agent-tools/batch-lifecycle.mjs +301 -0
  18. package/agent-tools/batch-segment.mjs +16 -263
  19. package/agent-tools/batch-skeleton.mjs +156 -0
  20. package/agent-tools/batch.mjs +410 -0
  21. package/agent-tools/context.mjs +174 -0
  22. package/agent-tools/eng.mjs +4 -0
  23. package/agent-tools/goal.mjs +7 -0
  24. package/agent-tools/parent-channel.mjs +18 -1
  25. package/agent-tools/plan.mjs +39 -5
  26. package/agent-tools/read-history.mjs +122 -24
  27. package/agent-tools/settings.mjs +4 -2
  28. package/agent-tools/subagent-async.mjs +3 -3
  29. package/agent-tools/subagent-spawn.mjs +29 -101
  30. package/agent-tools/task.mjs +11 -0
  31. package/agent-tools.mjs +9 -2
  32. package/agent.mjs +10 -4
  33. package/config.mjs +1 -1
  34. package/context.mjs +66 -121
  35. package/fts-text.mjs +41 -0
  36. package/generate-title.mjs +6 -6
  37. package/i18n.mjs +4 -4
  38. package/ledger-cmd.mjs +30 -7
  39. package/ledger-db.mjs +22 -2
  40. package/ledger-executors.mjs +103 -0
  41. package/ledger-surface.mjs +15 -8
  42. package/ledger.mjs +15 -4
  43. package/manifest.mjs +172 -60
  44. package/memory/core.mjs +4 -18
  45. package/memory/schema.mjs +4 -11
  46. package/package.json +22 -1
  47. package/prompts/advisor-design.md +1 -1
  48. package/prompts/advisor-round2.md +1 -1
  49. package/prompts/advisor-round3.md +1 -1
  50. package/prompts/common.md +3 -3
  51. package/prompts/discipline-engineering.md +17 -2
  52. package/prompts/persona-eng-coder.md +1 -1
  53. package/prompts/persona-eng-designer.md +1 -1
  54. package/prompts/persona-engineering.md +9 -6
  55. package/session-gc.mjs +129 -74
  56. package/session-index-build.mjs +298 -0
  57. package/session-index-cmd.mjs +61 -0
  58. package/session-index-pass.mjs +95 -0
  59. package/session-index-query.mjs +102 -0
  60. package/session-index.mjs +285 -0
  61. package/session-lifecycle.mjs +18 -5
  62. package/session-slots-manifest.mjs +55 -3
  63. package/session-stale.mjs +247 -0
  64. package/token-window.mjs +188 -0
  65. package/tools/bash.mjs +4 -15
  66. package/tools/execute.mjs +5 -13
  67. package/tools/git-checkpoint.mjs +1 -1
  68. package/tools/git-ext.mjs +23 -20
  69. package/tools/git-run.mjs +141 -0
  70. package/tools/git.mjs +56 -45
  71. package/tools/index.mjs +3 -1
  72. package/tools/process-tree.mjs +20 -0
  73. package/tools/shared.mjs +8 -4
  74. package/traces/trace-cleanup.mjs +109 -0
  75. package/traces/trace-store.mjs +32 -36
package/CHANGELOG.md ADDED
@@ -0,0 +1,63 @@
1
+ # Changelog(@thincoder/core)
2
+
3
+ All notable changes to the core package are documented here.
4
+ Format: Keep a Changelog · 中文 · 号在发布时定(CalVer——见 `docs/RELEASE.md` §4)。
5
+
6
+ ## [0.9.3] — 2026-09-22
7
+
8
+ > 0.9.2 → 0.9.3(月内序号——发布时定号)
9
+
10
+ ### Added
11
+
12
+ - **派生会话索引库**(`node:sqlite` · 零新依赖):`read_history` 超大会话不再整档拒(>50k 消息经索引可查)+ `path:"all"` 跨会话检索(行携 `session.file` 回查锚)+ `tool_calls` 携参数;索引从记录段增量建 · 可重建(丢/坏自愈)· 主存(会话文件)零改。
13
+ - **子代理「系统固块」**:机制性指令(审计五锚 + 批档行)改由 spawn 单点写入并拼进 system 面(前缀缓存契约——同 child 连跑 `systemPrompt` 逐字节相等)。
14
+ - **进程族单源**:`killProcessTree` 收口为 `tools/process-tree.mjs`(核 / VSC 本地副本全消除)。
15
+ - **退出即释放会话认领**:正常退出释放本进程认领(端标记保留作路标)——重启恢复不再依赖进程探测兜底。
16
+ - **认领释放扩面**:ACP `session/close` · 跨 cwd 释放 · 被占槽返回可区分信号(零写)。
17
+
18
+ ### Changed
19
+
20
+ - **批档工具词面协议结构化**:`batch status` enum 化(散文走 `note`)+ `create` 补 `source` / `prev` 前缀归一 / 占位自动;append/status 占位残留机检(fail-closed)。
21
+ - **工程模式 `task` 工具机械停用**(追踪面 = 批档 + 台账;普通模式零改)。
22
+ - **设置面**:敏感键谓词导出 + 非敏感父对象 JSON 化渲染(`[object Object]` 瑕疙消除)。
23
+ - FTS 语言面外提 `fts-text.mjs`(`segmentCJK` / `buildFtsQuery` 单源 + re-export 面)。
24
+
25
+ ### Fixed
26
+
27
+ - 会话索引自愈链(坏库 / 删库现场保留 + 重建)· 段轮转与等长改写的水位判别 · 源消失级联清行。
28
+
29
+ ## [0.9.2] — 2026-09-21
30
+
31
+ > 0.9.1 → 0.9.2(月内序号——发布时定号)
32
+
33
+ ### Added
34
+
35
+ - **PROJECT-MANIFEST 模型(按用点解析)**:项目发现五级梯(会话锚 → 带档祖先 → 一层子档 → 工作区……)+ owning-ancestor 归属 + 建档流;整档缺失不再静默(启动降级 + 明示提示)。
36
+ - **批档生命周期工具** `batch`(create / append / status / close——原 `batch_segment` 更名,别名保留)。
37
+ - **台账执行人列**(F-LX1):executor 归属 + 判存活显示。
38
+ - **信号行两档标签**(digest / ask)+ ask 参数 + 起始行(F-UC8)。
39
+ - **会话认领释放**(F-CR1/CR2):释放 + 拒绝零写入。
40
+
41
+ ### Changed
42
+
43
+ - **非阻塞启动**:异步会话 GC · 陈旧清扫 · traces 清理(启动不再被大扫除卡住)。
44
+ - **工程模式排除 `plan` 角色**(FR31——装配 / 命令 / 残留三面一致)。
45
+ - **包面**:README 补建(static 工具口径收正)+ LICENSE + npm 元数据;`files` 白名单不变。
46
+
47
+ ### Fixed
48
+
49
+ - 批别名残留(测试断言 / 提示词文档引用 / 错误串)· VSC 子代理块标题行与 CLI 对齐(标题段补全)。
50
+
51
+ ## [0.9.1] — 2026-09-21
52
+
53
+ > 核首发(2026-09 当月推导——registry 无号 ⇒ `0.<当月>.1`;首发即本段 ✗ 无更早历史——核内容此前散在两产品仓 ✗ 无独立版本号可考 ✗)。
54
+
55
+ ### Added
56
+
57
+ - **核包首发 `@thincoder/core@0.9.1`**:两产品共享机制收敛后的单一权威源——agent 执行环 / 子代理调度(spawn-gate · async 池 · notify_parent)· advisor 评审 · provider 层(SSE 流式 · thinking 映射 · 重试)· 记忆三层 · 工具系统 · 会话存储 · MCP 客户端 · git 集成 · 台账。
58
+ - **提示词与工具文档随包分发**:`prompts/` 15 档 + `tool-docs/` 24 档(一致性断言 D 对象——与 CLI 装机目录 / vsix 三处逐字相等)。
59
+ - **发布门禁**:`prepublishOnly` = `npm test`(与 CLI/VSC 门禁对称——2026-09-21 首发 补上)。
60
+
61
+ ### Changed
62
+
63
+ - 两产品 `dependencies` 同步收正 `^0.1.0 → ^0.9.1`(声明面与 registry 实发对齐——断言 A)。
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ThinCoder contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # @thincoder/core
2
+
3
+ **The shared core of the ThinCoder products — the mechanisms and the prompt content behind the ThinCoder CLI and the ThinCoder VS Code extension.**
4
+
5
+ Both products are thin shells: this package owns everything they have in common — the agent loop and subagent
6
+ scheduling, the advisor review system, the provider layer (SSE streaming, thinking-mode mapping, retries, rate
7
+ gating), the three-layer memory with its code and document indexes, the built-in tool system, session storage,
8
+ the MCP client, git and checkpoint integration, the ledger — plus the prompt texts and tool descriptions the
9
+ two products assemble their model context from.
10
+
11
+ The core has **zero third-party dependencies**: Node.js standard library only, pure ESM (`.mjs`), no build step.
12
+
13
+ ## Requirements
14
+
15
+ - **Node.js >= 22.13.0** — the floor comes from the built-in `node:sqlite` module (usable without a flag since 22.13.0), which the memory and ledger modules use.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ npm install @thincoder/core
21
+ ```
22
+
23
+ The package is a library — it ships no CLI of its own. Most users never install it directly: the two products
24
+ declare it as a dependency (`thincoder`, the CLI, and `thincoder-vscode`, the VS Code extension). Install it
25
+ directly when you build your own tooling on the shared core.
26
+
27
+ ## Importing
28
+
29
+ There is no bare-root entry point — `import "@thincoder/core"` does not resolve (`ERR_PACKAGE_PATH_NOT_EXPORTED`).
30
+ Every import is a subpath, and subpaths map 1:1 onto files in the package (`exports` is `{ "./*": "./*" }`), so
31
+ include the `.mjs` extension:
32
+
33
+ ```js
34
+ import { loadConfig } from "@thincoder/core/config.mjs"
35
+ import { createAgent } from "@thincoder/core/agent.mjs"
36
+ import { chat } from "@thincoder/core/provider/index.mjs"
37
+ import { put, putMarkdown } from "@thincoder/core/memory.mjs"
38
+ import { runAdvisorReview } from "@thincoder/core/advisor/run.mjs"
39
+ ```
40
+
41
+ Nested paths work the same way (`@thincoder/core/git/checkpoint.mjs`, `@thincoder/core/mcp/transport-stdio.mjs`).
42
+
43
+ ## What's inside
44
+
45
+ | Area | Modules |
46
+ |---|---|
47
+ | Agent loop, subagents | `agent.mjs` · `agent/` · `agent-tools/` |
48
+ | Advisor review, consultation, escalate | `advisor/` · `agent-tools/consult.mjs` · `agent-tools/escalate-async.mjs` |
49
+ | Provider layer (streaming, thinking mapping, retries, rate gate) | `provider/` |
50
+ | Memory, embedding, code/doc indexes | `memory.mjs` · `memory/` · `embedding.mjs` |
51
+ | Built-in tools | `tools/` |
52
+ | Sessions | `session.mjs` · `session-*.mjs` |
53
+ | MCP client | `mcp.mjs` · `mcp/` |
54
+ | Git, checkpoints, team-memory sync | `git/` |
55
+ | Ledger, traces | `ledger.mjs` · `traces/` |
56
+
57
+ ## Shared prompt content
58
+
59
+ The two products author no slot prompts or tool-description bodies of their own: both assemble their model
60
+ context from the texts in this package. `prompt-files.mjs` resolves them relative to the package itself, so the
61
+ same files are loaded in every delivery state (local link, npm install, packaged extension).
62
+
63
+ - `prompts/` — the slot-based prompt texts: personas, the common layer, the discipline layers, and the advisor
64
+ and consultation modules.
65
+ - `tool-docs/` — one file per static built-in tool; these texts are what the model reads as tool descriptions.
66
+
67
+ A prompt rule or a tool-description body is edited in exactly one place, and both products pick it up with the
68
+ next release. The exception is two tool-face anchors — the bash terminal face in `tool-docs/bash.md` and the
69
+ question-panel availability line in `tool-docs/question.md`: each product supplies those two values from its own
70
+ `src/prompt-injections.mjs`, because they describe behavior that differs between the CLI and the extension.
71
+
72
+ ## Versioning
73
+
74
+ Version numbers are calendar-based (CalVer): `year.month.monthly-count`, where the year segment counts from
75
+ 2026 (`0` = 2026). `0.9.1` is therefore the first core release of September 2026.
76
+
77
+ The number marks release time, not API compatibility — read `CHANGELOG.md` for what changed.
78
+ Per-release notes and artifacts: `CHANGELOG.md` in this package and the [GitHub Releases](https://github.com/xinbo-tech/thincoder/releases) page.
79
+
80
+ ## Contributing
81
+
82
+ The core lives in `thincoder-core/` of the ThinCoder repository; bug reports and questions go to the
83
+ repository issue tracker. Two conventions shape every change: pure ESM `.mjs` with no build step, and
84
+ zero third-party dependencies — if the Node.js standard library can do it, no third-party package is
85
+ allowed. `npm test` runs the offline unit suite; it runs again on release through `prepublishOnly`.
86
+ Mechanism design documents live in the repository under `docs/core/design/`.
87
+
88
+ ## License
89
+
90
+ MIT — see `LICENSE`.
package/advisor/loop.mjs CHANGED
@@ -11,7 +11,7 @@ import { chat } from "../provider/core.mjs"
11
11
  import { providerSpec, assistantToolCallMessage } from "../config.mjs"
12
12
  import { toOpenAISchema } from "../tools/index.mjs"
13
13
  import { truncateAdvisorResult } from "./truncate.mjs"
14
- import { batchSegmentTool } from "../agent-tools/batch-segment.mjs"
14
+ import { batchTool } from "../agent-tools/batch.mjs"
15
15
  import {
16
16
  estimateTokens, compactMessages, shouldBudgetNudge, budgetNudgeText, timeoutTail, renderTimeline,
17
17
  MAX_ADVISOR_TURNS, advisorContextBudget, TOOL_TIMEOUT_MS, REVIEW_TIMEOUT_MS, MAX_RESULT_CHARS,
@@ -42,7 +42,7 @@ function advisorToolsFor(agent, reviewType = "code", batchDoc = null) {
42
42
  const tools = [readTool, globTool, grepTool, lsTool, lspTool, advisorCodeSearchTool(agent)]
43
43
  // §2.20.3(第 4 批):**只有绑定了批次档的设计评审**额外拿到写通道——代码评审工具集
44
44
  // 逐字节不变(零 git + 只读不变量,§2.20.8 #1);未绑定 → 不挂载(fail-closed)。
45
- if (reviewType === "design" && batchDoc) tools.push(batchSegmentTool(batchDoc, { review: true }))
45
+ if (reviewType === "design" && batchDoc) tools.push(batchTool(batchDoc, { review: true }))
46
46
  return { schemas: tools.map(toOpenAISchema), byName: new Map(tools.map((t) => [t.name, t])) }
47
47
  }
48
48
  // Test seam: the tool set is pure(恒在六工具 + 批次档绑定條件)。
package/advisor/run.mjs CHANGED
@@ -10,7 +10,7 @@ import { buildObjectDeclarationBlock, buildDesignApprovalBlock } from "./message
10
10
  import { appendCitationReport } from "./citations.mjs"
11
11
  import { runAdvisorToolLoop } from "./loop.mjs"
12
12
  import { advisorIncompleteMarker, estimateTokens } from "./compaction.mjs"
13
- import { batchDocForReview } from "../agent-tools/batch-segment.mjs"
13
+ import { batchDocForReview } from "../agent-tools/batch.mjs"
14
14
 
15
15
  // 拆分后 import 面(既有导出名逐一保面——re-export;谓词为本批新增)。
16
16
  export { ADVISOR_THINKING_PLACEHOLDER, MAX_RESULT_CHARS, renderTimeline as _renderTimeline } from "./compaction.mjs"
@@ -53,7 +53,9 @@ export function handleCompletion(agent, response, depth, turn, guardPushbacks, h
53
53
  // pending item can't be resolved). After the single reminder the model is free
54
54
  // to finish — updating the task list (task tool) resets the budget, so a fresh
55
55
  // list state earns one fresh reminder.
56
- if (depth === 0 && agent.tasks.some((t) => t.status === "pending") && (agent._taskPushbacks ?? 0) < 1) {
56
+ // F10(催更门连带):工程模式下 task 工具机械停用(装配摘除 + execute 拒)⇒ 不再发指向被拒
57
+ // 工具的提醒(死胡同提醒防线);列表本体与 `_taskPushbacks` 预算语义零改。
58
+ if (depth === 0 && !agent.config?.agent?.engineering && agent.tasks.some((t) => t.status === "pending") && (agent._taskPushbacks ?? 0) < 1) {
57
59
  agent._taskPushbacks = (agent._taskPushbacks ?? 0) + 1
58
60
  const pending = agent.tasks.filter((t) => t.status === "pending").map((t) => t.title).join(", ")
59
61
  pushReal(agent, { role: "assistant", content: response.content })
@@ -6,7 +6,9 @@
6
6
  * (消灭「同一角色矩阵两份实现」= 本批缺陷的类根因);端差(VSC 装饰链 / settings 追加 /
7
7
  * consult 池来源)经 `decorate` 注入——**不传 = 核默认形态**(CLI = 迁出前逐字)。
8
8
  *
9
- * 返回 = 家族段数组(`[task, plan, timer, ...depth 家族 / 角色段]`)——**不含** `agent.tools`
9
+ * 返回 = 家族段数组(**非**工程模式:`[task, plan, timer, ...depth 家族 / 角色段]`;工程模式:
10
+ * 固定段 = `[timer]`——plan 不入表(FR31 ① / KD8)· task 不入表(F10——工程模式机械停用,
11
+ * 同 KD8 卸载处置))——**不含** `agent.tools`
10
12
  * 展开与 `extraTools`(调用点各自展开:核 `agent/setup.mjs` 两段式)。
11
13
  *
12
14
  * 登记册**动态**载入:`agent-tools.mjs` 静态图经 consult/subagent 族可达核 agent 栈
@@ -20,11 +22,11 @@ export async function assembleFamilyTools({
20
22
  role = null, // string 子代理角色(eng-coder / eng-designer / coder / consult / explore / …)
21
23
  engineering = false, // boolean depth-0 role enum 注入用(工程模式)
22
24
  consultModels = [], // array consult 池([] ⇒ consult 工具不注册)
23
- batchDoc = null, // string batchSegment 绑定路径(eng 角色)
25
+ batchDoc = null, // string 批次档绑定路径(eng 角色 → batchTool 绑定)
24
26
  decorate = null, // object 端差面:{ subagent?, consultStart?, consultStop?, settings? }
25
27
  } = {}) {
26
28
  // CORE-UNIFICATION TOOLS #83:consult 家族随统一登记册自 `../agent-tools.mjs` 取用(单一来源)
27
- const { planTool, subagentTool, taskTool, skillTool, goalTool, verifyTool, recentChangesTool, timerTool, advisorTool, engTool, readHistoryTool, batchSegmentTool, consultStartTool, consultStopTool, parentChannelTool } = await import("../agent-tools.mjs")
29
+ const { planTool, subagentTool, taskTool, skillTool, goalTool, verifyTool, recentChangesTool, timerTool, advisorTool, engTool, readHistoryTool, contextTool, batchTool, consultStartTool, consultStopTool, parentChannelTool } = await import("../agent-tools.mjs")
28
30
  // 写命令(主 agent 专用)——动态 import 且**仅 depth===0 载入**(ledger 链静态达 node:sqlite——
29
31
  // W8 契约②;子代理路径不注册 = 零载入——depth>0 解构得空、不引用即无副作用)
30
32
  const { ledgerAddTool, ledgerUpdateTool, ledgerCloseTool } = depth === 0 ? await import("../ledger.mjs") : {}
@@ -137,7 +139,7 @@ export async function assembleFamilyTools({
137
139
  : []
138
140
 
139
141
  const depthOnly = depth === 0
140
- ? [decorate?.subagent ?? filteredSubagent, skillTool, goalTool, engTool, verifyTool, recentChangesTool, readHistoryTool, advisorTool,
142
+ ? [decorate?.subagent ?? filteredSubagent, skillTool, goalTool, engTool, verifyTool, recentChangesTool, readHistoryTool, contextTool, advisorTool, batchTool(null),
141
143
  ...consultTools,
142
144
  // 台账写命令(M2——仅主 agent;查询面 ledger_count 住基础集 tools/index.mjs)。
143
145
  // fail-closed:子代理不挂载 = 写面机械不可达。
@@ -156,19 +158,30 @@ export async function assembleFamilyTools({
156
158
  // eng-coder: advisor + verify + the §18 audit-only subagent channel (D-E3).
157
159
  // eng-designer (§2.15 D): the survey-only subagent channel alone — no advisor
158
160
  // (it does not fire reviews) and no verify (its deliverable is documents, not code).
159
- // §2.20.3(第 4 批):两分支各追加 batch_segment——目标档 = spawn 时绑定的
160
- // `batchDoc`(§2.20.2);主 agent 不挂载(§1/§4/§6 走普通文档写)。
161
- // SUBAGENT-UPSTREAM-CHANNEL(AGENT-LOOP-SUBAGENT.md §6.27.4 装配接线):子代理上行通道
161
+ // BATCH-RECORD §4.3 挂载表(批次档生命周期工具化批):eng 两分支各挂主名 `batch`
162
+ // (绑定段 append/status;目标 = spawn 绑定 `child._batchDoc`)——§2.20.3 batch_segment
163
+ // 挂载形态已随单名化收口退役(过渡别名 §4.14 不入生产挂载面);depth-0 主 agent 同表挂载
164
+ // `batch`(D-BR18 扩权——create/close + append §1/§4/§6 + status §1(轮 2 裁定②:§4/§6
165
+ // 状态面走普通文档写),目标走可选 path / 在飞扫描)。
166
+ // SUBAGENT-UPSTREAM-CHANNEL(AGENT-LOOP-UPSTREAM.md §6.27.4 装配接线):子代理上行通道
162
167
  // (`notify_parent`)随 depth>0 段**前置**——4 处携带 = eng-coder / eng-designer / coder / 兜底段
163
168
  // (未列名 depth>0 role 落同一兜底段 ⇒ 亦装配;语义 =「depth>0 且非 consult 皆装配」);
164
169
  // 计数口径:`consult` 分支不入 ⇒ 「5 个插入点」读法已作废(实读 `thincoder-core/agent/family-tools.mjs:165-169`)。
165
170
  // consult 段不入(其角色语义 = 父发起的一次性会诊,父在其 settle 前不期望中途对话)。
166
- : engChildRole === "eng-coder" ? [parentChannelTool, advisorTool, verifyTool, batchSegmentTool(batchDoc), ...(engChildSubagent ? [engChildSubagent] : [])]
167
- : engChildRole === "eng-designer" ? [parentChannelTool, batchSegmentTool(batchDoc), ...(engChildSubagent ? [engChildSubagent] : [])]
171
+ : engChildRole === "eng-coder" ? [parentChannelTool, advisorTool, verifyTool, batchTool(batchDoc), ...(engChildSubagent ? [engChildSubagent] : [])]
172
+ : engChildRole === "eng-designer" ? [parentChannelTool, batchTool(batchDoc), ...(engChildSubagent ? [engChildSubagent] : [])]
168
173
  : role === "coder" ? [parentChannelTool, verifyTool, advisorTool]
169
174
  : role === "consult" ? [recentChangesTool]
170
175
  : [parentChannelTool]
171
176
 
172
- // task/plan/timer 固定段(所有面都有):装配序 = agent.tools → 固定段 → 家族段 → extraTools
173
- return [taskTool, planTool, timerTool, ...depthOnly]
177
+ // 固定段(装配序 = agent.tools → 固定段 → 家族段 → extraTools):timer 全模式全深度;
178
+ // plan 随模式位——ENG-PLAN-EXCLUSION(FR31 · KD8 卸载而非「注册 + 报错」· KD11 全深度两端);
179
+ // task 随模式位——F10(task 工程模式机械停用;同 KD8 处置:**不入表**——模型不可见、零 token 税;
180
+ // execute 层另有机械拒兜底,见 `agent-tools/task.mjs`)。
181
+ // 工程模式固定段 = plan / task 皆**不入表**;普通模式固定段逐字不变(FR31 边界 / F10 边界)。
182
+ // 判据 = 单一模式位 `engineering`(不带深度分支——子代理面随同排除,role enum
183
+ // `:44-49` + spawn 门 / task execute 门已同向)。
184
+ return engineering
185
+ ? [timerTool, ...depthOnly]
186
+ : [taskTool, planTool, timerTool, ...depthOnly]
174
187
  }
package/agent/helpers.mjs CHANGED
@@ -386,13 +386,22 @@ export const ENG_OFF_REMINDER =
386
386
  "Changes go through the normal workflow: you may edit files directly, advisor/verify " +
387
387
  "guards apply per config.]"
388
388
 
389
- /** Manual-tier auto-turn digest domain (AGENT-LOOP.md §17 D-S6): organize-only.
389
+ /** Manual-tier auto-turn digest domain (normal-mode base engineering variant =
390
+ * AUTO_TURN_DIGEST_DOMAIN_ENG below): organize-only. Mechanism = AGENT-LOOP-ASYNC-POOL.md §6.8;
391
+ * mode variant text = TOOLS.md §6.15.3.
390
392
  * Injected per manual auto-turn run — writes/execute/spawns/questions are also
391
393
  * mechanically denied (no permission handler + spawn gate); this steers first. */
392
394
  export const AUTO_TURN_DIGEST_DOMAIN =
393
395
  "[System reminder: auto-turn — background async subagents finished while there was no user message, and this turn runs automatically to digest their reports (the finished-report reminders above). No one is waiting for this reply, so organize only: 1) summarize each finished report's key points into this conversation for the user to read later; 2) update the task list with the task tool (allowed) to mark finished work done; 3) write decision points with a suggested next step as text — do not execute it. FORBIDDEN this turn (mechanically enforced): modifying files, bash/execute/verify, spawning subagents, asking questions — those need a real user message. End the turn once the summaries are written.]"
394
396
 
395
- /** Up-stream wake-turn domain (AGENT-LOOP-SUBAGENT.md §6.27.12.8): a running subagent sent
397
+ /** Engineering-mode auto-turn digest domain variant (mode = `agent.config.agent.engineering`;
398
+ * clause 2 drops the task-tool pointer — F10: the task tool is disabled in engineering mode,
399
+ * the batch record + ledger are the tracking authority). Single line, verbatim from the
400
+ * design doc (TOOLS.md §6.15.3); mechanism = AGENT-LOOP-ASYNC-POOL.md §6.8. Content authority = parent side. */
401
+ export const AUTO_TURN_DIGEST_DOMAIN_ENG =
402
+ "[System reminder: auto-turn — background async subagents finished while there was no user message, and this turn runs automatically to digest their reports (the finished-report reminders above). No one is waiting for this reply, so organize only: 1) summarize each finished report's key points into this conversation for the user to read later; 2) (engineering mode: the task tool is disabled — no task-list update is expected this turn; the batch record + ledger are the tracking authority and are updated in real user turns); 3) write decision points with a suggested next step as text — do not execute it. FORBIDDEN this turn (mechanically enforced): modifying files, bash/execute/verify, spawning subagents, asking questions — those need a real user message. End the turn once the summaries are written.]"
403
+
404
+ /** Up-stream wake-turn domain (AGENT-LOOP-UPSTREAM.md §6.27.12.8): a running subagent sent
396
405
  * an in-flight message and is waiting for the reply — the digest domain's "No one is waiting
397
406
  * for this reply" is the opposite of the truth, so the wake turn gets its own text.
398
407
  * Verbatim from the design doc (single line — no newlines). Content authority = parent side. */
@@ -11,10 +11,10 @@ import { compressIfNeeded, compressFallback, COMPRESS_FAILURE_LIMIT } from "../c
11
11
  import { ensureAutoReminder, injectEngineeringReminder, ContinueError, snapshotGuard } from "./helpers.mjs"
12
12
  import { pushManifestStateReminder } from "./setup-reminders.mjs"
13
13
  import { cleanupConsultSessions } from "../agent-tools/consult.mjs"
14
- import { logEvent } from "../log.mjs"
14
+ import { logEvent, errText } from "../log.mjs"
15
15
  // ASYNC-RESULT-CONTAINER.md D1:池 accessor(absorb 双池——advisor 独立池无队列)
16
16
  import { getAsyncPool, releaseSettledEntry } from "../agent-tools/async-settle.mjs"
17
- // 批 4 CLI-ASYNC-DISCARD(AGENT-LOOP-SUBAGENT.md §6.20):中止分支「只清已死」收尾单点
17
+ // 批 4 CLI-ASYNC-DISCARD(AGENT-LOOP-ASYNC-POOL.md §6.20):中止分支「只清已死」收尾单点
18
18
  import { discardAbortedPool, discardAbortedAdvisors } from "../agent-tools/async-discard.mjs"
19
19
  // R10 L3 (MULTI-INSTANCE-COLLAB §2a.5 D-L3a):回合末域登记 flush(写工具钩子累积 →
20
20
  // 整写一次本实例 peers 文件——无写入跳过;失败容忍不抛)
@@ -59,11 +59,28 @@ export function injectResponseReminders(agent, response) {
59
59
  * 计数复位(recentCallSigs——runAgent 回合级局部——经 ctx 传对象引用)/压缩完成事件
60
60
  * /AUTO 提醒重注入。失败:AbortError 透传;计数 + Q3 onCompressFail;
61
61
  * COMPRESS_FAILURE_LIMIT 连续失败降级 compressFallback(确定性截断——不发 LLM)。
62
+ * 模型主动压缩(`context` 工具)由本点消费(§6.16.2——取用即清槽;no-op 与失败各落一行注记)。
62
63
  */
63
64
  export async function runCompactionCheck(agent, ctx) {
64
65
  const { threshold, callbacks, compactionOverhead, signal, recentCallSigs } = ctx
66
+ // F-CC2(CONTEXT-COMPACTION.md §6.16.2):模型主动压缩(`context` 工具)在**本安全点**消费——
67
+ // 取用即清槽(失败不重放;重发由模型再调);`force` 只跳过阈值早退(模型判定优先于阈值),
68
+ // 其余全同(同 splitHistory / 同尾部预算 / 同摘要链 / 同回注 / 同基线失效)。
69
+ const pending = agent._pendingCompact
70
+ agent._pendingCompact = null
65
71
  try {
66
- if (await compressIfNeeded(agent, threshold, callbacks, compactionOverhead, signal)) {
72
+ let compacted = await compressIfNeeded(
73
+ agent, threshold, callbacks,
74
+ pending ? { ...compactionOverhead, force: true, focus: pending.focus } : compactionOverhead,
75
+ signal,
76
+ )
77
+ if (!compacted && pending) {
78
+ // 强制面无可压(短历史无中段)⇒ 记 no-op 注记后**照常**跑阈值面(阈值面不因模型请求让位)
79
+ const { compactNoopNote } = await import("../agent-tools/context.mjs")
80
+ agent.history.push({ role: "user", content: compactNoopNote(), transient: true })
81
+ compacted = await compressIfNeeded(agent, threshold, callbacks, compactionOverhead, signal)
82
+ }
83
+ if (compacted) {
67
84
  agent._compressFailures = 0
68
85
  agent._planReminderAtLen = 0 // After compression history shrinks, reset cadence so reminders resume
69
86
  recentCallSigs.length = 0 // After compression history is rebuilt, reset stall detection counter
@@ -79,6 +96,11 @@ export async function runCompactionCheck(agent, ctx) {
79
96
  // Q3 (CONTEXT-COMPACTION §7 D-C1): a failed compression is surfaced to the panel;
80
97
  // COMPRESS_FAILURE_LIMIT consecutive failures still degrade to compressFallback.
81
98
  callbacks?.onCompressFail?.(compressError)
99
+ if (pending) {
100
+ // 失败注记(§6.16.2 回执面②):模型面告知缺口——既有失败链 / 面板面零改,仅补一行机器注记
101
+ const { compactFailureNote } = await import("../agent-tools/context.mjs")
102
+ agent.history.push({ role: "user", content: compactFailureNote(errText(compressError), agent._compressFailures), transient: true })
103
+ }
82
104
  if (agent._compressFailures >= COMPRESS_FAILURE_LIMIT) {
83
105
  agent._compressFailures = 0
84
106
  if (compressFallback(agent)) callbacks.onCompress?.(agent._lastCompressInfo ?? {})
@@ -151,7 +173,7 @@ export async function finalizeAgentTurn(agent, ctx) {
151
173
  // driver aborts them on its own abort unwind.
152
174
  // Async subagent turn-end handling (AGENT-LOOP.md §15 D-A3 + §17 D-S1). Lifecycle:
153
175
  // - Ctrl+C (plain abort): children were aborted with the parent signal — discard dead
154
- // entries (tombstone/out-of-pool/notice — AGENT-LOOP-SUBAGENT.md §6.20; no stale
176
+ // entries (tombstone/out-of-pool/notice — AGENT-LOOP-ASYNC-POOL.md §6.20; no stale
155
177
  // errors injected — user explicitly stopped); consultation sessions are cross-turn
156
178
  // background work since R17 — the abort branch is the ONLY normal-path place that
157
179
  // aborts them (cleanupConsultSessions marks stopped → they never reach the digest).
@@ -205,7 +227,7 @@ export async function finalizeAgentTurn(agent, ctx) {
205
227
  }
206
228
 
207
229
  /**
208
- * Turn-end async subagent collection (AGENT-LOOP.md §17 D-S1 + §17.5 supersede):
230
+ * Turn-end async subagent collection (AGENT-LOOP-ASYNC-POOL.md §6.8 D-S1 + suspension supersede):
209
231
  * two modes, selected by the caller's driver context (17.5.2/17.5.4 #2):
210
232
  * - suspDriven=false (fallback — headless/direct runAgent callers without a
211
233
  * suspension driver): inject every entry that SETTLED during this run
@@ -19,16 +19,20 @@
19
19
  * pushManifestStateReminder carries the project phase as a self-healing single live line.
20
20
  * #34 adds the per-turn value refresh (mtime-gated re-read — §2.6 ③b / KD-M1-17..M1-19):
21
21
  * the line always carries the CURRENT on-disk phase, not the assembly-time snapshot.
22
+ * 2026-09-21(#188):**状态选行**(③' `projectView(agent.cwd)` —— KD-M1-26)——`ok` ⇒ 相位行
23
+ * (逐字零改);`no-project` / `ambiguous` ⇒ 报明行;`missing` / `invalid` ⇒ 有既往好值保守沿用、
24
+ * 无 ⇒ 报明行;无锚 ⇒ **零 I/O / 零报明**(KD-M1-27)。报明行构造 = `manifestReportLine` 四态。
22
25
  *
23
26
  * #113(S1 续轮第二批——VSC 侧并入 ④ 段):`pushInjections`(机器行专用注入——编辑器上下文
24
27
  * 等端侧采集内容;同文去重)与 `appendImagePointer`(粘贴图指引——非多模态模型可见报错)
25
28
  * 两档并入——内容由端采集 / 传入(核内零端名),核供注入纪律单点。
26
29
  */
27
30
  import { statSync } from "node:fs"
31
+ import { resolve } from "node:path"
28
32
  import { END } from "../session-slots.mjs"
29
33
  import { peerInstances } from "../peer-instances.mjs"
30
34
  import { specForModel } from "../config.mjs"
31
- import { manifestFilePath, readManifest } from "../manifest.mjs"
35
+ import { manifestFilePath, readManifest, projectView } from "../manifest.mjs"
32
36
 
33
37
  /** env-state line builder — pure, unit-testable.
34
38
  * §6.11(F1):slot 字段入行——位置在 model 后 resumed 前(N3:无绑定 → 显式 null——
@@ -71,6 +75,39 @@ export function manifestStateLine({ phase }) {
71
75
  return `${MANIFEST_LINE_PREFIX}phase: ${phase}${discipline ? ` (discipline: ${discipline})` : ""}.]`
72
76
  }
73
77
 
78
+ /**
79
+ * 报明行构造 — pure, unit-testable(**本批新增**——`docs/core/design/MANIFEST.md` §2.6 条 1b /
80
+ * KD-M1-26):`projectView` 四非 ok 态各自的行文,**逐字**(同族前缀 `MANIFEST_LINE_PREFIX`——
81
+ * 报明行与相位行共用单活体机制)。歧义行按 `matched` **两变体**(`manifest` = 带档级「marker
82
+ * directories」/ `git` = 裸仓档「repositories … (none carries …)」——同口径 = `TOOLS.md` §6.13 A22);
83
+ * `candidates` 按名排序(判据同 `discoverProjects`)、`<abs…>` 以「、」连接;`errors` 逐条以「;」连接。
84
+ * `ok` / 未知态 ⇒ `null`(无报明行——相位行归 `manifestStateLine`)。
85
+ * @param {{state?:string, cwd?:string, root?:string|null, path?:string|null, candidates?:string[], errors?:string[], matched?:string|null}} p 行素材(= `projectView` 返回面 + 锚)
86
+ * @returns {string|null} 行文 / null(非报明态)
87
+ */
88
+ export function manifestReportLine({ state, cwd, root, path, candidates, errors, matched } = {}) {
89
+ if (state === "no-project") {
90
+ return `${MANIFEST_LINE_PREFIX}none — no project at ${cwd} (no manifest on the ancestor chain, none below). ` +
91
+ `Parameters fall back to defaults. Create PROJECT-MANIFEST.json here to land a project (git optional).]`
92
+ }
93
+ if (state === "ambiguous") {
94
+ const list = candidates ?? []
95
+ const head = matched === "git"
96
+ ? `ambiguous — ${list.length} candidate repositories under ${cwd} (none carries PROJECT-MANIFEST.json):`
97
+ : `ambiguous — ${list.length} candidate marker directories under ${cwd}:`
98
+ return `${MANIFEST_LINE_PREFIX}${head} ${list.join("、")} — target the intended one explicitly (the mechanism never picks).]`
99
+ }
100
+ if (state === "missing") {
101
+ return `${MANIFEST_LINE_PREFIX}missing — the resolved project root ${root} has no PROJECT-MANIFEST.json; ` +
102
+ `project parameters fall back to defaults until the file is generated.]`
103
+ }
104
+ if (state === "invalid") {
105
+ return `${MANIFEST_LINE_PREFIX}invalid — ${path} is not a usable declaration: ${(errors ?? []).join(";")}; ` +
106
+ `project parameters fall back to defaults until fixed.]`
107
+ }
108
+ return null // ok / 未知态——无报明行(相位行归 manifestStateLine)
109
+ }
110
+
74
111
  /**
75
112
  * ③b 取值前置步(#34——`docs/core/design/MANIFEST.md` §2.6 条 3 · KD-M1-17–M1-19):数据档
76
113
  * mtime 门控重读——盘上 mtime ≠ `agent._manifestMtime`(含缓存未设 = 首次观测)⇒ `readManifest`
@@ -79,7 +116,7 @@ export function manifestStateLine({ phase }) {
79
116
  * 沿用上次已知好值,缓存不推进(⇒ 下回合重试,自愈)。路径 = `manifestFilePath(agent.cwd)`
80
117
  * (项目根解析与读点同源——KD-M1-18,非「锚目录 + 档名」);缓存载体 = `agent._manifestMtime`
81
118
  * (per-agent——KD-M1-19,同族先例 `agent._slotMtime`)。本步失败只降级注入面(运行期),
82
- * E2 启动门槛(会话起点)两分——见 §2.5「运行期失败退化 vs 启动门槛」。
119
+ * 与**会话起点动作**两分——见 §2.5「运行期失败退化 vs 会话起点动作」(本批收正条名)。
83
120
  * @param {object} agent 主 agent(`agent.cwd` / `agent.manifest`)
84
121
  */
85
122
  function refreshManifest(agent) {
@@ -104,26 +141,45 @@ function refreshManifest(agent) {
104
141
 
105
142
  /**
106
143
  * 情境行注入(#28——`docs/core/design/MANIFEST.md` §2.6 模块契约):manifest 的
107
- * `phase` 逐回合进模型上下文(判据序 ①–⑥ + 取值前置步 ③b = §2.6 表;#34 起值变由盘面驱动)。**自愈单活体**——
108
- * 同文活体在 幂等(零历史变更);值变 就地摘旧行 + 落新行(保 `history` 数组引用);
109
- * 压缩 / 会话重建吞行 → 下一回合重推(不落 system 槽——架构 E5.1 #5,`context.mjs`
110
- * 压缩面零触碰)。`transient:true` ⇒ 不进人读线(`_fullHistory`)。
111
- * @param {object} agent 主 agent(`agent.manifest` / `agent.history` / `agent.cwd`——③b 取值)
144
+ * `phase` 逐回合进模型上下文(判据序 ①–⑥ + 取值前置步 ③b = §2.6 表;#34 起值变由盘面驱动;
145
+ * 本批 + ③' 状态选行 / 报明行入口)。**自愈单活体**——同文活体在 幂等(零历史变更);值变
146
+ * 就地摘旧行 + 落新行(保 `history` 数组引用);压缩 / 会话重建吞行 → 下一回合重推(不落 system
147
+ * 槽——架构 E5.1 #5,`context.mjs` 压缩面零触碰)。`transient:true` ⇒ 不进人读线(`_fullHistory`)。
148
+ * @param {object} agent 主 agent(`agent.manifest` / `agent.history` / `agent.cwd`——③'/③b
112
149
  * @param {{depth?: number}} [opts] 注入深度(仅 depth-0)
113
150
  * @returns {boolean} 是否落新行(测试断言用)
114
151
  */
115
152
  export function pushManifestStateReminder(agent, { depth = 0 } = {}) {
116
153
  if (depth !== 0) return false // ① 仅 depth-0(子代理读任务书——架构 §2.4 M5 零 manifest 读面)
117
154
  if (agent.config?.agent?.engineering !== true) return false // ② 模式门:仅工程模式
118
- if (!agent.manifest) return false // ③ 无 manifest(normal 路径 / 装配缺失)→ 零注入
119
- refreshManifest(agent) // ③b 取值前置步(#34):mtime 门控重读——行构造须取刷新后的值
155
+ let line
156
+ if (!agent.cwd) {
157
+ // ③ 无锚门(KD-M1-27——**零 I/O / 零报明**):状态无从解析 ⇒ 沿用内存值(有 ⇒ 相位行;
158
+ // 无既往好值 ⇒ 零注入)。既有无 cwd 夹具用例零改(§2.6 条 3 边界同源)。
159
+ if (!agent.manifest) return false
160
+ line = manifestStateLine({ phase: agent.manifest.phase })
161
+ } else {
162
+ // ③' 状态选行(KD-M1-26——**每回合实读**,成本两轴见 §2.6 条 3):projectView 五态 ⇒ 选行。
163
+ const view = projectView(agent.cwd)
164
+ refreshManifest(agent) // ③b 取值前置步(#34):mtime 门控重读——相位行须取刷新后的值
165
+ if (view.state === "ok") {
166
+ const phase = (agent.manifest ?? view.manifest)?.phase
167
+ line = phase === undefined ? null : manifestStateLine({ phase })
168
+ } else if (view.state === "no-project" || view.state === "ambiguous") {
169
+ line = manifestReportLine({ ...view, cwd: resolve(agent.cwd) }) // 本批新报明格(无项目 / 歧义)
170
+ } else if (agent.manifest) {
171
+ line = manifestStateLine({ phase: agent.manifest.phase }) // missing / invalid:有既往好值 ⇒ 保守沿用
172
+ } else {
173
+ line = manifestReportLine({ ...view, cwd: resolve(agent.cwd) }) // 首观即失败 ⇒ 至少可见一次(KD-M1-26)
174
+ }
175
+ }
176
+ if (!line) return false
120
177
  const history = agent.history
121
- const line = manifestStateLine({ phase: agent.manifest.phase })
122
178
  // ④ 同文 user 行已在(活体)→ 幂等——零历史变更(同文口径 = pushInjections 的 history.some)
123
179
  if (history.some((m) => m.content === line)) return false
124
180
  // ⑤ 值已变 → 就地 splice 摘旧行(`role === "user"` 全匹配),保 history 数组引用。会话重建后
125
181
  // `_manifestLine` 不随历史回来 → 先按行族前缀从 history 认领现存活体(否则旧行残留 +
126
- // 新行入列 = 双活体,违 §2.6 定案「单活体」)。
182
+ // 新行入列 = 双活体,违 §2.6 定案「单活体」)。相位行 ↔ 报明行**同族前缀**⇒ 互相换位同机制。
127
183
  let old = agent._manifestLine
128
184
  if (!old) {
129
185
  for (const m of history) {
package/agent/setup.mjs CHANGED
@@ -212,6 +212,12 @@ export async function prepareRun(agent, input, callbacks, {
212
212
  // let——下方按 projectRules / skills listing 尾部追加(L326/L331 +=)再赋值;
213
213
  // const 声明会 TypeError: Assignment to constant variable(每次 run 必炸)。
214
214
  let systemPrompt = base
215
+ // 派单固块(台账 #23 · AGENT-LOOP-SUBAGENT.md §6.26):spawn 级固定的机制性指令(批次档路径行 /
216
+ // 审计模板)随 system 面下发——固块值在 child 生命周期内恒定 ⇒ run 内前缀逐字节稳定(前缀缓存
217
+ // 不破),且不在 `agent.history` 内 ⇒ 压缩只重建 history(吞不掉)。拼接位 = 槽位装配之后、
218
+ // 项目指令之前;`_spawnSystemBlock` 为 null/undefined ⇒ 分支不进(depth-0 / explore / plan /
219
+ // consult 的 system 逐字节同改前)。
220
+ if (agent._spawnSystemBlock) systemPrompt += `\n\n${agent._spawnSystemBlock}`
215
221
 
216
222
  // Time injection deliberately does NOT live here: system prompts must be byte-identical
217
223
  // across runs (provider prefix caches). The time rides a transient user reminder per turn
@@ -10,9 +10,9 @@
10
10
  * 展开走 `docRootPaths`)。缺 `docRoot` 键 → `readManifest`
11
11
  * 的 fillDefaults 已补默认(架构 §2.3 E2 便利 fallback 落点);整档缺失 / 非法 JSON →
12
12
  * `{ok:false}` → 回退 DEFAULT_MANIFEST.docRoot(本函数是壳门之下的便利层——E2 的
13
- * 「整档缺失 拒进正常循环」由壳面 requireManifest 在启动期拦(**工程模式会话**口径——
14
- * 普通会话装配钩子零 manifest I/O,KD-M1-12):CLI make-agent.mjs / VSC
15
- * setup.mjs fail-closed);读错(权限等)→ 上抛不伪装成缺失(manifest.mjs 契约)。
13
+ * 「整档缺失」由壳面 requireManifest 按用点处置(判 + 报明 / 建档,**不抛**——
14
+ * 启动侧恒不拒,KD-M1-25;**工程模式会话**口径——普通会话装配钩子零 manifest I/O,
15
+ * KD-M1-12):CLI make-agent.mjs / VSC setup.mjs 同判);读错(权限等)→ 上抛不伪装成缺失(manifest.mjs 契约)。
16
16
  * M6 的 `advisor.mjs` design-review 分类分支消费本导出
17
17
  * (KD-M6-1——同源不重复实现;落 dispatch.mjs 会让 advisor 反向 import 门禁簇成环)。
18
18
  * 2. `freezeWindowConflict(agent, absPaths)` —— D5 冻结窗口判据组装:被审文件集 =
@@ -37,8 +37,8 @@ const REVIEW_ROOT_KEYS = ["requirements", "specs", "design", "modules", "batches
37
37
  * 评审目标解析单点(M4 §2.1#1):读 manifest `docRoot` → 评审对象 / 被审文件的绝对路径
38
38
  * 集合(目录级,反斜杠归一;子键值形态 = 串 | 多根数组,逐键经 `docRootPaths` 展开)。
39
39
  * 缺 `docRoot` 键 → readManifest 已补默认;整档缺失 / 非法 →
40
- * `{ok:false}` → 回退 DEFAULT_MANIFEST.docRoot(便利层——E2 的「拒进正常循环」由壳面
41
- * requireManifest 在启动期拦(**工程模式会话**口径——KD-M1-12),本函数不重复拦);
40
+ * `{ok:false}` → 回退 DEFAULT_MANIFEST.docRoot(便利层——E2 的「整档缺失」由壳面
41
+ * requireManifest 按用点处置(判 + 报明 / 建档,**不抛**——启动侧恒不拒,KD-M1-25;**工程模式会话**口径——KD-M1-12),本函数不重复拦);
42
42
  * 读错(权限等)→ 上抛 fail-closed。
43
43
  * @param {Object} agent
44
44
  * @returns {string[]} 评审目标目录的绝对路径(去重)
@@ -1,5 +1,5 @@
1
1
  /**
2
- * advisor-async.mjs — async advisor reviews (AGENT-LOOP-SUBAGENT.md §6.10 — R13, 2026-09-06).
2
+ * advisor-async.mjs — async advisor reviews (AGENT-LOOP-ASYNC-POOL.md §6.10 — R13, 2026-09-06).
3
3
  *
4
4
  * The advisor tool runs reviews in a background pool at depth 0 (default async —
5
5
  * ruling ②-3 A): the launch returns an ack, the turn ends naturally, the session
@@ -20,7 +20,7 @@
20
20
  * 3. the async pool (agent._asyncAdvisors — ADVISOR_POOL_LIMIT = 4 default,
21
21
  * agent.poolLimits.advisor configurable — POOL-CONFIG-UNIFIED; same-scope
22
22
  * running launches are refused at once — never queued (F-5); over-limit +
23
- * DIFFERENT scope launches QUEUE (ED-4 2026-09-16 — AGENT-LOOP-SUBAGENT.md §6.10:
23
+ * DIFFERENT scope launches QUEUE (ED-4 2026-09-16 — AGENT-LOOP-ASYNC-POOL.md §6.10:
24
24
  * agent._asyncAdvisorQueue, ack {queued, position}, slot release auto-start,
25
25
  * queued cancel dequeue+renumber — the former over-limit refusal ②-6a is
26
26
  * retired); cancel (ruling ②-6b — directed abort → cancelled settle: no
@@ -280,7 +280,7 @@ function relayAdvisorOutput(callbacks, prefix, chunk) {
280
280
  }
281
281
  }
282
282
 
283
- // ─── ED-4(2026-09-16 · AGENT-LOOP-SUBAGENT.md §6.10):评审池排队(槽释放自动起跑)────────
283
+ // ─── ED-4(2026-09-16 · AGENT-LOOP-ASYNC-POOL.md §6.10):评审池排队(槽释放自动起跑)────────
284
284
 
285
285
  /** 排队出队单点(ED-4):`_asyncAdvisorQueue` 剔除目标条目 + 余项 position 按 `1..n`
286
286
  * 重编号;读面经**载体吸收**(`carrierField`——#21⑤b):部分 parent(只携池 + `history`)
@@ -368,7 +368,7 @@ export function launchAsyncAdvisor(parent, ctx, launch) {
368
368
  const idFields = { type: reviewType, scope: scopeSummary(documents?.length ? documents : paths), round: `${(run?.round ?? 0) + 1}/uncapped`, criterion: "scope-in-flight" }
369
369
  return { error: withIdentityLine(`Advisor: 此 scope 已有评审在跑——settle 后逐个发起 — ${scopeNote}; round/prior continuation would be ambiguous while it is in flight — wait for it to settle, then launch the next review.`, idFields) }
370
370
  }
371
- // ED-4(AGENT-LOOP-SUBAGENT.md §6.10):池满 + 异 scope ⇒ 排队(非拒)——ack 含 queued +
371
+ // ED-4(AGENT-LOOP-ASYNC-POOL.md §6.10):池满 + 异 scope ⇒ 排队(非拒)——ack 含 queued +
372
372
  // position;running 计数只算 running 条目(排队不占槽)。原 ②-6a 拒发退役。
373
373
  const queued = runningAdvisorCount(parent) >= limit
374
374
  parent._asyncAdvisors ??= new Map()