@thincoder/core 0.9.5 → 0.10.2
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 +49 -0
- package/README.md +1 -1
- package/abort-provenance.mjs +10 -10
- package/advisor/citations.mjs +5 -5
- package/advisor/compaction.mjs +16 -15
- package/advisor/loop.mjs +12 -12
- package/advisor/messages.mjs +6 -6
- package/advisor/project-context.mjs +5 -5
- package/advisor/repos.mjs +3 -3
- package/advisor/run.mjs +22 -12
- package/advisor.mjs +4 -4
- package/agent/assemble.mjs +111 -0
- package/agent/chat-call.mjs +68 -0
- package/agent/child-marks.mjs +2 -2
- package/agent/completion.mjs +9 -2
- package/agent/continue-decision.mjs +25 -0
- package/agent/dispatch-gates.mjs +140 -0
- package/agent/dispatch-run.mjs +167 -0
- package/agent/dispatch.mjs +54 -296
- package/agent/family-tools.mjs +5 -5
- package/agent/helpers.mjs +53 -3
- package/agent/live-beat.mjs +49 -0
- package/agent/post-turn.mjs +4 -8
- package/agent/record-results.mjs +23 -20
- package/agent/relay-prefix.mjs +2 -2
- package/agent/run-stages.mjs +15 -11
- package/agent/run-start.mjs +111 -0
- package/agent/setup-reminders.mjs +9 -9
- package/agent/setup.mjs +32 -4
- package/agent/spawn-child.mjs +9 -9
- package/agent/suspension.mjs +89 -29
- package/agent/timers.mjs +113 -0
- package/agent/turn-loop.mjs +244 -0
- package/agent/write-gate.mjs +103 -6
- package/agent-tools/advisor-async.mjs +9 -9
- package/agent-tools/advisor-settle.mjs +12 -12
- package/agent-tools/advisor.mjs +36 -41
- package/agent-tools/async-discard.mjs +44 -1
- package/agent-tools/async-settle.mjs +21 -18
- package/agent-tools/audit-block.mjs +7 -7
- package/agent-tools/bash-async.mjs +281 -0
- package/agent-tools/batch-lifecycle.mjs +57 -18
- package/agent-tools/batch-paths.mjs +52 -8
- package/agent-tools/batch-skeleton.mjs +19 -1
- package/agent-tools/batch.mjs +12 -65
- package/agent-tools/checkpoint.mjs +121 -0
- package/agent-tools/child-permission.mjs +5 -3
- package/agent-tools/consult.mjs +56 -56
- package/agent-tools/context.mjs +4 -20
- package/agent-tools/design-token.mjs +3 -3
- package/agent-tools/digest-budget.mjs +1 -1
- package/agent-tools/eng.mjs +5 -5
- package/agent-tools/escalate-async.mjs +16 -11
- package/agent-tools/goal.mjs +3 -10
- package/agent-tools/panel-blocks.mjs +2 -2
- package/agent-tools/parent-channel.mjs +3 -13
- package/agent-tools/plan.mjs +3 -4
- package/agent-tools/read-history.mjs +12 -28
- package/agent-tools/recent-changes.mjs +4 -5
- package/agent-tools/review-facts.mjs +1 -1
- package/agent-tools/settings.mjs +47 -20
- package/agent-tools/skill.mjs +3 -5
- package/agent-tools/spawn-gates.mjs +3 -3
- package/agent-tools/subagent-actions-query.mjs +270 -0
- package/agent-tools/subagent-actions.mjs +21 -265
- package/agent-tools/subagent-async.mjs +25 -20
- package/agent-tools/subagent-panel.mjs +33 -4
- package/agent-tools/subagent-run.mjs +36 -21
- package/agent-tools/subagent-scheduler.mjs +28 -27
- package/agent-tools/subagent-spawn.mjs +45 -14
- package/agent-tools/subagent.mjs +33 -53
- package/agent-tools/task.mjs +3 -9
- package/agent-tools/timer.mjs +12 -12
- package/agent-tools/verify.mjs +14 -13
- package/agent-tools.mjs +0 -1
- package/agent.mjs +18 -355
- package/attachments.mjs +125 -0
- package/auto-think.mjs +1 -1
- package/config-presets.mjs +11 -6
- package/config-watch.mjs +83 -0
- package/config.mjs +29 -12
- package/context-degrade.mjs +81 -0
- package/context-echo.mjs +88 -0
- package/context-push.mjs +51 -0
- package/context.mjs +8 -187
- package/conventions.mjs +110 -161
- package/declaration.mjs +194 -0
- package/embedding.mjs +3 -1
- package/env-ref.mjs +81 -0
- package/explore-distill.mjs +2 -3
- package/file-links.mjs +53 -0
- package/file-refs.mjs +103 -0
- package/generate-title.mjs +28 -4
- package/history-window.mjs +17 -2
- package/i18n.mjs +2 -0
- package/index-discover.mjs +7 -7
- package/ledger-cmd.mjs +34 -126
- package/ledger-db.mjs +10 -2
- package/ledger-tools.mjs +188 -0
- package/ledger.mjs +9 -4
- package/log.mjs +12 -7
- package/manifest-discovery.mjs +131 -0
- package/manifest-schema.mjs +166 -0
- package/manifest.mjs +18 -217
- package/mcp.mjs +4 -1
- package/memory/code-search.mjs +149 -0
- package/memory/code-sync.mjs +89 -230
- package/memory/core.mjs +47 -32
- package/memory/docs.mjs +63 -262
- package/memory/file-list.mjs +89 -0
- package/memory/file-walk.mjs +7 -4
- package/memory/memory-tool.mjs +229 -0
- package/memory/schema.mjs +18 -3
- package/memory/sweep.mjs +289 -0
- package/memory/sync-tail.mjs +81 -0
- package/memory-status.mjs +69 -0
- package/memory.mjs +3 -0
- package/model-ref.mjs +96 -3
- package/model-specs.mjs +16 -7
- package/notify-policy.mjs +51 -0
- package/package.json +1 -1
- package/peer-claims.mjs +2 -2
- package/peer-domains.mjs +7 -11
- package/peer-instances.mjs +9 -10
- package/process-probe-exec.mjs +187 -0
- package/process-probe.mjs +27 -175
- package/prompt-overlays.mjs +11 -10
- package/prompts/advisor-design.md +2 -2
- package/prompts/advisor-round2.md +1 -1
- package/prompts/advisor-round3.md +1 -1
- package/prompts/common.md +16 -8
- package/prompts/discipline-engineering.md +57 -16
- package/prompts/discipline-normal.md +6 -3
- package/prompts/persona-coder.md +0 -1
- package/prompts/persona-eng-coder.md +1 -5
- package/prompts/persona-eng-designer.md +2 -3
- package/prompts/persona-engineering.md +32 -11
- package/prompts/persona-explore.md +0 -1
- package/prompts/persona-normal.md +1 -1
- package/prompts/persona-plan.md +3 -4
- package/prompts/subagent-base.md +14 -0
- package/provider/core.mjs +11 -48
- package/provider/list-models.mjs +3 -0
- package/provider/responses-request.mjs +237 -0
- package/provider/responses.mjs +6 -228
- package/provider-flows.mjs +248 -0
- package/queued.mjs +90 -0
- package/rules.mjs +209 -2
- package/session-guard.mjs +27 -7
- package/session-lifecycle.mjs +108 -27
- package/session-rename.mjs +5 -1
- package/session-slot-claims.mjs +230 -0
- package/session-slot-scan.mjs +299 -0
- package/session-slot-verify.mjs +304 -0
- package/session-slot-write.mjs +74 -8
- package/session-slots-manifest.mjs +163 -241
- package/session-slots.mjs +99 -71
- package/session.mjs +30 -10
- package/shell-candidates.mjs +78 -0
- package/text-budget.mjs +2 -2
- package/think-off.mjs +49 -0
- package/token-ttl.mjs +15 -4
- package/tool-args.mjs +73 -0
- package/tool-docs/advisor.md +9 -0
- package/tool-docs/batch.md +1 -0
- package/tool-docs/code_search.md +1 -0
- package/tool-docs/consult_start.md +5 -0
- package/tool-docs/consult_stop.md +3 -0
- package/tool-docs/context.md +7 -0
- package/tool-docs/doc_search.md +1 -0
- package/tool-docs/edit.md +2 -13
- package/tool-docs/eng.md +1 -0
- package/tool-docs/execute.md +1 -9
- package/tool-docs/git.md +25 -51
- package/tool-docs/goal.md +6 -0
- package/tool-docs/ledger_add.md +1 -0
- package/tool-docs/ledger_close.md +1 -0
- package/tool-docs/ledger_count.md +1 -0
- package/tool-docs/ledger_query.md +1 -0
- package/tool-docs/ledger_update.md +1 -0
- package/tool-docs/main_history.md +1 -0
- package/tool-docs/memory.md +10 -0
- package/tool-docs/notify_parent.md +9 -0
- package/tool-docs/peer_instances.md +1 -0
- package/tool-docs/plan.md +5 -0
- package/tool-docs/process.md +7 -3
- package/tool-docs/read_history.md +7 -0
- package/tool-docs/recent_changes.md +1 -0
- package/tool-docs/repo_outline.md +1 -0
- package/tool-docs/settings.md +6 -0
- package/tool-docs/skill.md +5 -0
- package/tool-docs/subagent.md +31 -0
- package/tool-docs/task.md +7 -0
- package/tool-docs/timer.md +6 -0
- package/tool-docs/verify.md +1 -0
- package/tool-docs/wait_for.md +2 -1
- package/tools/bash.mjs +28 -4
- package/tools/edit-batch.mjs +4 -4
- package/tools/edit-diff.mjs +4 -23
- package/tools/exec-run.mjs +102 -4
- package/tools/execute.mjs +5 -2
- package/tools/file.mjs +4 -9
- package/tools/git-run.mjs +1 -1
- package/tools/git.mjs +0 -24
- package/tools/index.mjs +2 -1
- package/tools/linter.mjs +2 -1
- package/tools/node-child.mjs +21 -0
- package/tools/ops.mjs +50 -13
- package/tools/patch.mjs +4 -2
- package/tools/repomap.mjs +20 -9
- package/tools/shared.mjs +8 -6
- package/tools/web.mjs +5 -2
- package/tools/write-path.mjs +1 -1
- package/traces/trace-store.mjs +17 -14
- package/vision-reader.mjs +76 -0
- package/agent-tools/batch-segment.mjs +0 -18
package/agent/write-gate.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* v2 唯一增量(模块设计 §2.1):评审对象 / 被审文件路径来源改读 manifest `docRoot`
|
|
5
5
|
* (去硬编码 `docs/`),token 门(`anyLiveDesignSlot`)与冻结窗口谓词本体继承 v1 零改
|
|
6
|
-
* (KD-M4-3
|
|
6
|
+
* (KD-M4-3)——本档承载三个单点:
|
|
7
7
|
*
|
|
8
8
|
* 1. `resolveReviewTargetPaths(agent)` —— 评审目标解析单点:读 manifest `docRoot`
|
|
9
9
|
* 产出评审对象 / 被审文件的绝对路径集合(目录级;子键值形态 = 串 | 多根数组,
|
|
@@ -15,19 +15,28 @@
|
|
|
15
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
|
+
* **按用点形(2026-10-02 收口 · 台账 #828)**:`resolveReviewRootsFor(dir)`——同单点
|
|
19
|
+
* 非第二实现(按目标目录 ∥ 所属项目根解析;多档锚下各文档按各自项目判定)。
|
|
18
20
|
* 2. `freezeWindowConflict(agent, absPaths)` —— D5 冻结窗口判据组装:被审文件集 =
|
|
19
21
|
* 声明文档集(`inflightDesignReviewConflict` 同源 docAbs 腿)+ 批次档(`run.batchDoc`
|
|
20
|
-
* 腿——评审绑定批档在途时,写批档同样致 stale)。dispatch.mjs
|
|
22
|
+
* 腿——评审绑定批档在途时,写批档同样致 stale)。dispatch.mjs
|
|
21
23
|
* 只 import 消费,不再各自直连 `advisor-settle.mjs`。
|
|
24
|
+
* 3. `batchRecordWriteConflict(agent, depth, absPaths)` —— 批次档写门判据组装(#309 ·
|
|
25
|
+
* `AGENT-LOOP-SUBAGENT.md` §6.29.1):判据与拒绝文案同址(文案字面只出本档)。
|
|
26
|
+
* 2026-09-29 收窄(台账 #545):自身批次伴随件(非 `.md` · 同目录 · 词干族 = 三合取)放行——
|
|
27
|
+
* 批次档本体 ∕ 他批伴随件 ∕ 异目录伴随件照拒;两端取用 = 核 `agent/dispatch.mjs` Phase 1(两端同径)。
|
|
22
28
|
*
|
|
23
29
|
* 继承零改面(本档不碰):token 门资格判据(anyLiveDesignSlot / validateDesignToken)、
|
|
24
30
|
* 代码路径判定(conventions.mjs 单一权威分类——KD-M4-1 保留为代码路径判定用)。
|
|
25
31
|
*/
|
|
32
|
+
import { basename, dirname, resolve } from "node:path"
|
|
26
33
|
import { DEFAULT_MANIFEST, docRootPaths, readManifest } from "../manifest.mjs"
|
|
27
34
|
// advisor-settle 无上游依赖本簇(dispatch → write-gate → advisor-settle 单向)。normAbs
|
|
28
35
|
// 权威本体 = review-facts.mjs(第 33 批迁入),advisor-settle re-export;本档再 re-export
|
|
29
36
|
// (指针链非副本),M6 经本档一行 import 取齐。
|
|
30
37
|
import { inflightDesignReviewConflict, normAbs } from "../agent-tools/advisor-settle.mjs"
|
|
38
|
+
// 批次档路径单源(叶档——`BATCH-RECORD.md` §4.15):批次档写门基底集取 `batchDocBases`。
|
|
39
|
+
import { batchDocBases } from "../agent-tools/batch-paths.mjs"
|
|
31
40
|
export { normAbs }
|
|
32
41
|
|
|
33
42
|
/** 评审目标解析读的 docRoot 键集(评审对象/被审文件可住的所有文档层——M6 分类据此判定)。 */
|
|
@@ -44,14 +53,21 @@ const REVIEW_ROOT_KEYS = ["requirements", "specs", "design", "modules", "batches
|
|
|
44
53
|
* @returns {string[]} 评审目标目录的绝对路径(去重)
|
|
45
54
|
*/
|
|
46
55
|
export function resolveReviewTargetPaths(agent) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
return resolveReviewRootsFor(agent?.cwd ?? process.cwd())
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** 同单点之**按用点解析**增量(2026-09-21「参数按用点解析」之裁收口 · 台账 #828):按**目标目录**
|
|
60
|
+
* (其所属项目根——最近带档祖先)解析评审目标根集——多档锚下各文档按各自项目判定。
|
|
61
|
+
* @param {string} dir 目标目录(项目根——最近带档祖先)
|
|
62
|
+
* @returns {string[]} 评审目标目录绝对路径(去重)
|
|
63
|
+
*/
|
|
64
|
+
export function resolveReviewRootsFor(dir) {
|
|
65
|
+
const man = readManifest(dir)
|
|
50
66
|
const root = man.ok && man.manifest?.docRoot && typeof man.manifest.docRoot === "object"
|
|
51
67
|
? man.manifest.docRoot
|
|
52
68
|
: DEFAULT_MANIFEST.docRoot
|
|
53
69
|
const out = []
|
|
54
|
-
for (const key of REVIEW_ROOT_KEYS) out.push(...docRootPaths(root?.[key],
|
|
70
|
+
for (const key of REVIEW_ROOT_KEYS) out.push(...docRootPaths(root?.[key], dir))
|
|
55
71
|
return [...new Set(out)]
|
|
56
72
|
}
|
|
57
73
|
|
|
@@ -85,3 +101,84 @@ export function freezeWindowConflict(agent, absPaths) {
|
|
|
85
101
|
}
|
|
86
102
|
return null
|
|
87
103
|
}
|
|
104
|
+
|
|
105
|
+
/** 批次档写门基底集**按 run 记忆化**(每 run 一次 manifest 读——§6.29.1;键 = agent 对象)。
|
|
106
|
+
* 载体 = WeakMap(agent 回收即随 GC——无跨 run 残留);cwd 变更即失效重读。
|
|
107
|
+
* **歧义锚**(多项目并存 · #828)⇒ 基底 = 候选项目基底**并集**(`batchDocBases` 解析单点——覆盖随之对齐)。 */
|
|
108
|
+
const batchGateBasesMemo = new WeakMap()
|
|
109
|
+
|
|
110
|
+
/** 比较键(绝对形态归一:`.` 段归位 + 分隔符归一 `/` + win32 大小写归一——§6.29.1 判据)。 */
|
|
111
|
+
const gateKey = (p) => {
|
|
112
|
+
const s = resolve(String(p)).replace(/\\/g, "/")
|
|
113
|
+
return process.platform === "win32" ? s.toLowerCase() : s
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function memoizedBatchBases(agent, cwd) {
|
|
117
|
+
const memo = agent && typeof agent === "object" ? batchGateBasesMemo.get(agent) : null
|
|
118
|
+
if (memo && memo.cwd === cwd) return memo.bases
|
|
119
|
+
const bases = batchDocBases(cwd)
|
|
120
|
+
if (agent && typeof agent === "object") batchGateBasesMemo.set(agent, { cwd, bases })
|
|
121
|
+
return bases
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** 目标是否落基底根内(按路径段——win32 大小写归一;同 `batch-paths.mjs` 越基底判据口径)。 */
|
|
125
|
+
function underBase(targetKey, baseAbs, cwd) {
|
|
126
|
+
const root = gateKey(normAbs(baseAbs, cwd))
|
|
127
|
+
return targetKey === root || targetKey.startsWith(root.endsWith("/") ? root : root + "/")
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** 自身批次伴随件判据(三合取——`AGENT-LOOP-SUBAGENT.md` §6.29.1 · 2026-09-29 收窄 · 台账 #545):
|
|
131
|
+
* ① 目标非 `.md`(批次档本体永在门内——含近词干 `<词干>-v2.md`;`.md` 判定大小写不敏感 = fail-closed 侧);
|
|
132
|
+
* ② 目标与绑定档同目录;③ 目标基名以 `<词干>.` 或 `<词干>-` 打头(词干 = 绑定档基名剥 `.md`)。
|
|
133
|
+
* 入参 = 比较键(gateKey 归一后——win32 大小写归一同覆盖 ②③ 的字面比较)。
|
|
134
|
+
* @returns {boolean} 三合取全中 = true(放行面);余者 false(照拒) */
|
|
135
|
+
function isOwnBatchCompanion(targetKey, boundKey) {
|
|
136
|
+
const targetBase = basename(targetKey)
|
|
137
|
+
if (targetBase.toLowerCase().endsWith(".md")) return false // ① 批次档本体(任何 `.md`)永在门内
|
|
138
|
+
if (dirname(targetKey) !== dirname(boundKey)) return false // ② 同目录(按比较键)
|
|
139
|
+
const boundBase = basename(boundKey)
|
|
140
|
+
const stem = boundBase.toLowerCase().endsWith(".md") ? boundBase.slice(0, -3) : boundBase
|
|
141
|
+
return targetBase.startsWith(stem + ".") || targetBase.startsWith(stem + "-") // ③ 词干族(`.` ∕ `-` 前缀)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 批次档写门判据(#309 · `docs/core/design/AGENT-LOOP-SUBAGENT.md` §6.29.1——containment;
|
|
146
|
+
* 2026-09-29 收窄 · 台账 #545):
|
|
147
|
+
* 工程角色子代理(`depth > 0` ∧ `agent._batchDoc` 在场)对**批次档文件**的写 ⇒ 目标 ≠ 绑定档
|
|
148
|
+
* **且非绑定批次的伴随件**(见 `isOwnBatchCompanion`)⇒ 冲突(fail-closed;写自己绑定的档 ∥
|
|
149
|
+
* 自身批次伴随件 = 正常面放行)。比较键 = 绝对路径 + win32 大小写归一。
|
|
150
|
+
* 界面:depth 0(主 agent——跨档处置是父侧职责)· 无 `_batchDoc`(非工程绑定族)· 主基底外的
|
|
151
|
+
* 普通文件写 —— 三种形态均不进本门(返回 null,行为零变)。判据集由调用方限定 = `FILE_MUTATORS`
|
|
152
|
+
* (`file_ops` / 读类不在门内——§6.29.1 边界)。
|
|
153
|
+
* 拒绝文案**单源 = 本档**(两端直取 `message`——零字面副本)。
|
|
154
|
+
* 恒零抛(形照 `freezeWindowConflict` 先例——全判据带形态守卫)。
|
|
155
|
+
* @param {Object} agent — 子代理 agent(读 `_batchDoc` / `cwd`)
|
|
156
|
+
* @param {number} depth — 执行深度(> 0 = 子代理)
|
|
157
|
+
* @param {string[]} absPaths — 本次写将触碰的绝对路径集(调用方已 resolve)
|
|
158
|
+
* @returns {{bound: string, target: string, message: string}|null} 命中 = 冲突信息 / 否则 null
|
|
159
|
+
*/
|
|
160
|
+
export function batchRecordWriteConflict(agent, depth, absPaths) {
|
|
161
|
+
if (!(depth > 0)) return null
|
|
162
|
+
const bound = agent?._batchDoc
|
|
163
|
+
if (typeof bound !== "string" || bound.length === 0) return null
|
|
164
|
+
const wanted = (absPaths ?? []).filter((p) => typeof p === "string" && p.length > 0)
|
|
165
|
+
if (wanted.length === 0) return null
|
|
166
|
+
const cwd = typeof agent?.cwd === "string" && agent.cwd.length > 0 ? agent.cwd : process.cwd()
|
|
167
|
+
const bases = memoizedBatchBases(agent, cwd)
|
|
168
|
+
const boundAbs = normAbs(bound, cwd)
|
|
169
|
+
const boundKey = gateKey(boundAbs)
|
|
170
|
+
for (const p of wanted) {
|
|
171
|
+
const target = normAbs(p, cwd)
|
|
172
|
+
const targetKey = gateKey(target)
|
|
173
|
+
if (targetKey === boundKey) continue // 写自己绑定的档 = 正常面
|
|
174
|
+
if (!bases.some((b) => underBase(targetKey, b, cwd))) continue // 非批次档文件(含缺 manifest 的基底外盘面)
|
|
175
|
+
if (isOwnBatchCompanion(targetKey, boundKey)) continue // 自身批次伴随件 = 放行面(三合取——见判据)
|
|
176
|
+
return {
|
|
177
|
+
bound: boundAbs,
|
|
178
|
+
target,
|
|
179
|
+
message: `write refused — cross-batch batch-record write: this child is bound to ${basename(boundAbs)}; ${basename(target)} belongs to a different batch. Write only your own bound record and its companion files (the batch tool targets your bound record); the parent agent handles other batch records.`,
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return null
|
|
183
|
+
}
|
|
184
|
+
|
|
@@ -110,7 +110,7 @@ function openDesignRun(agent, key) {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
|
-
* Resolve the review instance a launch continues (§
|
|
113
|
+
* Resolve the review instance a launch continues (AGENT-LOOP-ASYNC-POOL.md §6.10 ③ — per-review
|
|
114
114
|
* rounds/prior; reviewId = designId for design reviews, a random id for code).
|
|
115
115
|
* - design: continue the OPEN instance of the same document set (fix rounds of
|
|
116
116
|
* a review thread keep its designId and its round/prior); none → new instance.
|
|
@@ -172,7 +172,7 @@ export function effectiveAdvisorRound(agent) {
|
|
|
172
172
|
|
|
173
173
|
export const ADVISOR_POOL_LIMIT = 4
|
|
174
174
|
|
|
175
|
-
/** §
|
|
175
|
+
/** AGENT-LOOP-ASYNC-POOL.md §6.10 advisor 池生效上限(纯——读 agent.poolLimits.advisor——合法 ≥1 整数生效——
|
|
176
176
|
* 非法/缺省回退 ADVISOR_POOL_LIMIT——与 subagent 域 resolvePoolLimits 独立不共享
|
|
177
177
|
* (subagent 两键表不含本键——键表语义不同——POOL-CONFIG-UNIFIED F-2))。 */
|
|
178
178
|
export function resolveAdvisorPoolLimit(raw) {
|
|
@@ -185,7 +185,7 @@ export function resolveAdvisorPoolLimit(raw) {
|
|
|
185
185
|
export function advisorPoolLimitFor(agent) {
|
|
186
186
|
return resolveAdvisorPoolLimit(agent?.config?.agent?.poolLimits)
|
|
187
187
|
}
|
|
188
|
-
/** §
|
|
188
|
+
/** §6.10 同 scope 守卫(F-5——用户裁①):同 reviewType+scope 有 running 评审 → true
|
|
189
189
|
* (拒——running 并行多实例歧义放大——settled 续跑语义不变)。design scope = 文档集键
|
|
190
190
|
* (docSetKey);code = 单 code 线程(记录不记路径键——与 openCodeRun 语义一致)。
|
|
191
191
|
* 与池容量守卫独立:容量 = 全局 ≤N——scope = 同 scope ≤1——两关都过才启动。 */
|
|
@@ -257,7 +257,7 @@ export function cancelAsyncAdvisor(agent, id, onToken) {
|
|
|
257
257
|
return { id: key, status: "cancelled", was: "queued" }
|
|
258
258
|
}
|
|
259
259
|
entry.cancelled = true
|
|
260
|
-
// §
|
|
260
|
+
// AGENT-LOOP-SUBAGENT.md §6.12 站点 #11(第 24 批):定向中止 = cancel(reason 载荷)
|
|
261
261
|
entry.controller?.abort?.({ abortTrigger: "cancel", abortDetail: "advisor-cancel" })
|
|
262
262
|
return { id: key, status: "cancelled" }
|
|
263
263
|
}
|
|
@@ -352,13 +352,13 @@ export function refillAdvisorQueue(parent, onToken) {
|
|
|
352
352
|
* digest/panel/freeze consumers route it like any other settled block.
|
|
353
353
|
* @returns {{ok: true, id: string}} — ack; or {{ok: true, id: string, queued: true,
|
|
354
354
|
* position: number}} — ED-4 排队 ack(池满 + 异 scope——slot 释放自动起跑); or
|
|
355
|
-
* {{error: string}} — scope guard (same reviewType+scope running — §
|
|
355
|
+
* {{error: string}} — scope guard (same reviewType+scope running — §6.10 F-5)
|
|
356
356
|
* or invalid ctx.
|
|
357
357
|
*/
|
|
358
358
|
export function launchAsyncAdvisor(parent, ctx, launch) {
|
|
359
359
|
const { reviewType, documents, paths, object, designToken, designId, run } = launch
|
|
360
360
|
const limit = advisorPoolLimitFor(parent)
|
|
361
|
-
// §
|
|
361
|
+
// §6.10 same-scope guard(F-5——用户裁①):同 type+scope 有 running → 拒——与池容量
|
|
362
362
|
// 守卫独立两关都过才启动——同 scope 不等不排(settled 续跑不变;排队仅异 scope 面)。
|
|
363
363
|
if (runningAdvisorOfScope(parent, reviewType, run?.docSetKey ?? null)) {
|
|
364
364
|
const scopeNote = reviewType === "design"
|
|
@@ -398,7 +398,7 @@ export function launchAsyncAdvisor(parent, ctx, launch) {
|
|
|
398
398
|
// D6 buildChildSignal 单点(ASYNC-RESULT-CONTAINER.md)。
|
|
399
399
|
const ctrl = new AbortController()
|
|
400
400
|
entry.controller = ctrl
|
|
401
|
-
// §
|
|
401
|
+
// §6.12 站点 #10(第 24 批):hop 逐跳保 reason;#98 链结单点(interrupt 豁免面——
|
|
402
402
|
// Ctrl+I 不逐链中止飞行评审)。
|
|
403
403
|
bindChildController(ctrl, buildChildSignal(parent, ctx))
|
|
404
404
|
entry.promise = new Promise((res) => { entry._settle = res })
|
|
@@ -416,7 +416,7 @@ export function launchAsyncAdvisor(parent, ctx, launch) {
|
|
|
416
416
|
signal: entry.controller.signal,
|
|
417
417
|
}, designToken, documents, paths, object, designId)
|
|
418
418
|
.then((report) => { entry.report = report })
|
|
419
|
-
// §
|
|
419
|
+
// §6.12 第 3 条合成器(第 24 批):原 message 前缀逐字保留 + 来源后缀
|
|
420
420
|
.catch((err) => { entry.error = deathLine(err, entry.controller?.signal) })
|
|
421
421
|
.finally(() => {
|
|
422
422
|
// settle 公共收尾单点(ASYNC-RESULT-CONTAINER.md D3——settleAsyncEntry):日志三连
|
|
@@ -430,7 +430,7 @@ export function launchAsyncAdvisor(parent, ctx, launch) {
|
|
|
430
430
|
ctx,
|
|
431
431
|
onAccounting: () => {
|
|
432
432
|
const settled = settleAdvisorRun(parent, entry)
|
|
433
|
-
//
|
|
433
|
+
// fix B:settle 分支输出(清洗/未签发提示)写回 entry.report——digest 原样进。
|
|
434
434
|
if (settled.report != null) entry.report = settled.report
|
|
435
435
|
},
|
|
436
436
|
})
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
*
|
|
7
7
|
* 内容 = 迁入(逐字搬移含注释)+ 本批两处接线:
|
|
8
8
|
* - `settleAdvisorRun` —— 记账本体零改(轮次 / 陈旧 / token D1 落盘 / prior);A 族
|
|
9
|
-
* 未完成判定改用单谓词 `advisorIncompleteMarker
|
|
9
|
+
* 未完成判定改用单谓词 `advisorIncompleteMarker`(ADVISOR-GUARDS.md §1 消费点 1/2):design 结算经
|
|
10
10
|
* `settleDesignReview(…, { incomplete })` 未完成即不签发;code 完成守卫同谓词
|
|
11
11
|
* (旧 `^` 锚首行失败正则退役——六形态语义零丢,含 review_failed 字符串
|
|
12
12
|
* resolve 形态);
|
|
13
|
-
* - `inflightDesignReviewConflict` —— E 族(F17
|
|
13
|
+
* - `inflightDesignReviewConflict` —— E 族(F17/ADVISOR-GUARDS.md §5)D5 冻结窗口冲突检测:与
|
|
14
14
|
* `reviewIsStale` 同族(同 docAbs / 同 normAbs)、仅扫 running 且未取消的设计条目
|
|
15
15
|
* (dispatch Phase-1 预闸消费)。
|
|
16
16
|
*
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
import { persistEngTokens } from "../token-ttl.mjs"
|
|
24
24
|
import { settleDesignReview, makeDesignTokenRegex, stripApprovedSuffix } from "./design-token.mjs"
|
|
25
25
|
import { looksLikeReviewOutput, advisorIncompleteMarker, ADVISOR_LAUNCH_REFUSAL_PREFIX } from "../advisor/run.mjs"
|
|
26
|
-
import { isCodePath,
|
|
26
|
+
import { isCodePath, loadProjectDeclaration } from "../conventions.mjs"
|
|
27
27
|
import { logEvent } from "../log.mjs"
|
|
28
28
|
// 第 33 批(评审失败护栏):`normAbs` 迁 `review-facts.mjs`(中立模块——陈旧判定与事实面共用同一归一);
|
|
29
29
|
// 2026-09-18:结算分类 / 结论文案单源迁 `advisor/notice.mjs`(纯函数、零状态、零计数载体)。
|
|
@@ -40,7 +40,7 @@ export function mutationSeqOf(agent) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/** Record a file-mutation commit — bounded ring feeding the stale scan. paths = ABSOLUTE.
|
|
43
|
-
*
|
|
43
|
+
* 唯一记账点(fix A):dispatch runOne 写执行成功即刻调用(取代批后段 + 中断分支——
|
|
44
44
|
* 不双计);mergeChildMutations(子代理合入)是独立事件面。 */
|
|
45
45
|
export function noteMutations(agent, paths) {
|
|
46
46
|
const list = (paths ?? []).filter((p) => typeof p === "string" && p.length > 0)
|
|
@@ -68,14 +68,14 @@ export function reviewIsStale(agent, entry) {
|
|
|
68
68
|
const set = new Set(scope)
|
|
69
69
|
return since.some((m) => (m.paths ?? []).some((p) => set.has(normAbs(p, agent?.cwd))))
|
|
70
70
|
}
|
|
71
|
-
// Code face = the shared classifier (
|
|
71
|
+
// Code face = the shared classifier (thincoder-core/conventions.mjs) — one authority with
|
|
72
72
|
// the design gate and the mutation guard (PORTABILITY FR12 / PO-10).
|
|
73
|
-
const conv =
|
|
73
|
+
const conv = loadProjectDeclaration(agent?.cwd)
|
|
74
74
|
return since.some((m) => (m.paths ?? []).some((p) => isCodePath(normAbs(p, agent?.cwd), conv)))
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
|
-
* E 族(F17
|
|
78
|
+
* E 族(F17/ADVISOR-GUARDS.md §5 E-3d)——D5 冻结窗口写前拦截的冲突检测:设计评审**在途**(点火 → 结算)
|
|
79
79
|
* 期间,父侧对被审文件集(`docAbs` = 声明文档集,含批次档)的写入会被 dispatch 预闸拒绝。
|
|
80
80
|
* 判据与 `reviewIsStale` **同源**(同一 docAbs / 同一 normAbs);仅扫 **running 且未取消**
|
|
81
81
|
* 的设计条目(已结算 / 已取消条目不拦——结算后写不再致 stale、取消的结算早退不判)。
|
|
@@ -112,7 +112,7 @@ export function inflightDesignReviewConflict(agent, absPaths) {
|
|
|
112
112
|
* mirror retired per DESIGN-TOKEN-SETTLEMENT D3) + D1 settle-time slot persist
|
|
113
113
|
* (persistEngTokens — write failure = settle failure: no registration, no
|
|
114
114
|
* Approved echo, re-review — 评审 #1);
|
|
115
|
-
* 4.
|
|
115
|
+
* 4. fix B — branch-shaped report output (the caller writes it back to
|
|
116
116
|
* entry.report; digest injects the CLEANED form): pass → stripped +
|
|
117
117
|
* Approved/designId suffix (sync 参照形态); persist-failed → stripped +
|
|
118
118
|
* "D1: …token could NOT be durably written…" re-review notice (no Approved
|
|
@@ -140,7 +140,7 @@ export function settleAdvisorRun(agent, entry) {
|
|
|
140
140
|
let report = result
|
|
141
141
|
let passed = false
|
|
142
142
|
let persistFailed = false
|
|
143
|
-
// 宿主尾族判定(单谓词——design 结算与 code
|
|
143
|
+
// 宿主尾族判定(单谓词——design 结算与 code 守卫共用;ADVISOR-GUARDS.md §1 消费点 1/2)。
|
|
144
144
|
const incomplete = result != null ? advisorIncompleteMarker(String(result)) : null
|
|
145
145
|
// 启动拒绝报告(未发起请求——无评审产出):第 33 批上移为本结算段**单点**——消费 = ① design
|
|
146
146
|
// 失败分类(neutral——无尝试发生;`ADVISOR-GUARDS.md` §7 失败结算表行 1)② code 守卫 failureVerdict(既有语义零变)。
|
|
@@ -199,7 +199,7 @@ export function settleAdvisorRun(agent, entry) {
|
|
|
199
199
|
// 第 11 批(F16/A3):失败判定 = 单谓词六 kind(含 review_failed 字符串 resolve 形态)
|
|
200
200
|
// ——旧 `^` 锚正则只认首行形态,漏「时间线 + 尾」;语义零丢(六形态全覆盖)。
|
|
201
201
|
// 另:设计评审的**启动拒绝报告**(未发起请求——无评审产出)同样不得计为评审覆盖
|
|
202
|
-
//
|
|
202
|
+
// (异步结算面判据;与同步工具面 _advisorRefusals 同源前缀;正常链不可达=防御纵深)
|
|
203
203
|
// ——判定已上移为本结算段单点(launchRefused,上方)。
|
|
204
204
|
const failureVerdict = (run.reviewType !== "design" && (
|
|
205
205
|
incomplete !== null ||
|
|
@@ -209,7 +209,7 @@ export function settleAdvisorRun(agent, entry) {
|
|
|
209
209
|
agent._calledAdvisorThisRun = true
|
|
210
210
|
}
|
|
211
211
|
} else if (run.reviewType === "design" && entry.designToken && result != null) {
|
|
212
|
-
//
|
|
212
|
+
// fix B(stale 分支):陈旧评审不签发——digest 不得展示未注册 token——先剥
|
|
213
213
|
// 方括号回显 + 前置 "评审目标已变更——token 未签发"(不变式——两分支都清洗)。
|
|
214
214
|
const stripped = String(result)
|
|
215
215
|
.replace(makeDesignTokenRegex(entry.designToken, "g"), "")
|
|
@@ -217,7 +217,7 @@ export function settleAdvisorRun(agent, entry) {
|
|
|
217
217
|
report = `评审目标已变更——token 未签发 (review target changed after launch — this review judged a stale state; no design token was issued — re-run the review on the current state)\n\n${stripped}`.trim()
|
|
218
218
|
}
|
|
219
219
|
// Prior of round 2+ = the last REVIEW-LOOKING output (mirror of run.mjs's guard).
|
|
220
|
-
// F2e
|
|
220
|
+
// F2e: strip the engine-approved suffix FIRST — the prior must never
|
|
221
221
|
// carry the raw token / designId (exact truncation — zero collateral).
|
|
222
222
|
// 结论块(下方)刻意晚于 prior 归一:prior 只承载评审正文,不携带机制文案。
|
|
223
223
|
if (report && looksLikeReviewOutput(report)) {
|
package/agent-tools/advisor.mjs
CHANGED
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
* The agent calls this explicitly to get an independent review.
|
|
4
4
|
* The top-level `type` is REQUIRED and must be exactly "code" or "design"
|
|
5
5
|
* (F30 fail-closed — no silent default; ADVISOR-GUARDS.md §2.4).
|
|
6
|
-
* §
|
|
6
|
+
* AGENT-LOOP-ASYNC-POOL.md §6.10 (R13 — async advisor): at depth 0 the review launches into the
|
|
7
7
|
* background pool by DEFAULT (async:true / omitted; async:false forces the
|
|
8
8
|
* blocking review); depth>0 (eng-coder self-review) stays synchronous always.
|
|
9
9
|
*/
|
|
10
10
|
import { runAdvisorReview, advisorIncompleteMarker, ADVISOR_LAUNCH_REFUSAL_PREFIX } from "../advisor/run.mjs"
|
|
11
11
|
import { resolveBatchDocPath } from "./batch.mjs"
|
|
12
12
|
// M6(模块设计 §2.1 F3):评审对象来源读 manifest docRoot(声明面)——复用 M4 的
|
|
13
|
-
// write-gate.mjs 单一权威源(KD-M6-1),替代 v1
|
|
13
|
+
// write-gate.mjs 单一权威源(KD-M6-1),替代 v1 的逐档分类面(isDocPath);
|
|
14
14
|
// normAbs 同源 re-export(指针非副本)。不 import dispatch.mjs(簇间回边,环风险)。
|
|
15
|
-
import { resolveReviewTargetPaths, normAbs } from "../agent/write-gate.mjs"
|
|
15
|
+
import { resolveReviewTargetPaths, resolveReviewRootsFor, normAbs } from "../agent/write-gate.mjs"
|
|
16
|
+
import { owningProject } from "../manifest-discovery.mjs"
|
|
16
17
|
import { sep } from "node:path"
|
|
17
18
|
import {
|
|
18
19
|
generateDesignToken,
|
|
@@ -24,6 +25,8 @@ import {
|
|
|
24
25
|
// F30/F31(2026-09-18 顾问面治理批):类型门判定 / 拒发串 / 对象标识行——单源 `advisor/notice.mjs`
|
|
25
26
|
// (文案逐字 = ADVISOR-GUARDS.md §2.4 / §2.5);零计数载体(计数护栏随撤 cap 整体退场)。
|
|
26
27
|
import { typeGateCriterion, buildTypeGateRefusal, scopeSummary, withIdentityLine } from "../advisor/notice.mjs"
|
|
28
|
+
// #15 描述外置:描述文本单点 = tool-docs/advisor.md(DESC 单解析面,缺档抛错语义不变)
|
|
29
|
+
import { DESC } from "../tools/shared.mjs"
|
|
27
30
|
|
|
28
31
|
// Design-token utilities moved to advisor-async.mjs (the async settle shares
|
|
29
32
|
// them — no wrapper↔runner module cycle); validateDesignToken stays exported
|
|
@@ -32,36 +35,14 @@ export { validateDesignToken } from "./advisor-async.mjs"
|
|
|
32
35
|
|
|
33
36
|
export const advisorTool = {
|
|
34
37
|
name: "advisor",
|
|
35
|
-
description:
|
|
36
|
-
"Run an independent review on your work. " +
|
|
37
|
-
"type is REQUIRED — exactly one of the two legal values: type='design' reviews design / requirement documents before implementation (pass documents=[...] with the explicit list of doc paths; plus batchDoc when a batch record is in flight); type='code' reviews the code you changed after implementation (pass paths=[...] to scope files/directories — documents=[...] adds acceptance-criteria context). " +
|
|
38
|
-
"A call without a type (or with a conflicting object.type) is refused — there is no default and no silent fallback. " +
|
|
39
|
-
"The advisor is an independent read-only sub-agent that explores the codebase, " +
|
|
40
|
-
"reads files, and traces callers via grep/lsp. " +
|
|
41
|
-
"For code review: round 1 does a full review, round 2 verifies the agent's fix claims, " +
|
|
42
|
-
"round 3+ strictly checks only the fix claims — convergence, not divergence. " +
|
|
43
|
-
"For design review: single-pass review against methodology and requirements. " +
|
|
44
|
-
"Review criteria come from .thincoder/advisor.md (if present) or sensible defaults. " +
|
|
45
|
-
"After the review, you MUST produce a response table (see discipline rules for format). " +
|
|
46
|
-
"If advisor says all clear, call verify. " +
|
|
47
|
-
"Optionally pass object={type,target,status,reason,exclude} to anchor the review target " +
|
|
48
|
-
" (the review-object declaration is mechanically injected into the review message); " +
|
|
49
|
-
"absent → legacy behavior (no injection). " +
|
|
50
|
-
"ASYNC: at depth 0 the review runs in the BACKGROUND by default " +
|
|
51
|
-
"(async:true or omitted) — the call returns an ack immediately, the turn ends, and the report " +
|
|
52
|
-
"arrives automatically in a digest turn when the review finishes; background reviews share one pool — " +
|
|
53
|
-
"at most agent.poolLimits.advisor concurrent reviews (default 4 — configurable via /config 并发池 or " +
|
|
54
|
-
"config.json; pool-full and same-scope refusals state the current limit) — launch reviews one at a time. " +
|
|
55
|
-
"Inside a child (depth>0 — eng-coder self-review) " +
|
|
56
|
-
"reviews are always synchronous; async:true is rejected there. " +
|
|
57
|
-
"Returns the review report — the advisor's findings verdict: all-clear (call verify) or a findings list to fix.",
|
|
38
|
+
description: DESC("advisor"),
|
|
58
39
|
parameters: {
|
|
59
40
|
type: "object",
|
|
60
41
|
properties: {
|
|
61
42
|
type: { type: "string", enum: ["code", "design"], description: "Review type (required): 'design' for design doc review, 'code' for code review. Omitting it is refused — there is no default." },
|
|
62
43
|
async: {
|
|
63
44
|
type: "boolean",
|
|
64
|
-
description: "Background review: default at depth 0 = true (
|
|
45
|
+
description: "Background review: default at depth 0 = true (ack now, report via digest); async:false forces the blocking review. depth>0 → always sync.",
|
|
65
46
|
},
|
|
66
47
|
object: {
|
|
67
48
|
type: "object",
|
|
@@ -72,21 +53,21 @@ export const advisorTool = {
|
|
|
72
53
|
reason: { type: "string", description: "Why this review runs: user-initiated / delivery verification" },
|
|
73
54
|
exclude: { type: "string", description: "Explicit exclusion list — approved/implemented items NOT in this review" },
|
|
74
55
|
},
|
|
75
|
-
description: "Review-object declaration
|
|
56
|
+
description: "Review-object declaration — mechanically injected at the start of the review message. Absent → no injection (legacy behavior).",
|
|
76
57
|
},
|
|
77
58
|
paths: {
|
|
78
59
|
type: "array",
|
|
79
60
|
items: { type: "string" },
|
|
80
|
-
description: "Code files or directories to review (for code review). Required unless documents is provided. The advisor reads
|
|
61
|
+
description: "Code files or directories to review (for code review). Required unless documents is provided. The advisor reads exactly these — it has no git tool and never inspects diffs.",
|
|
81
62
|
},
|
|
82
63
|
documents: {
|
|
83
64
|
type: "array",
|
|
84
65
|
items: { type: "string" },
|
|
85
|
-
description: "
|
|
66
|
+
description: "Doc paths to review (design / requirements / referenced docs). The advisor reviews ONLY these — no git diff scan. Use for both review types to pass the task's docs list.",
|
|
86
67
|
},
|
|
87
68
|
batchDoc: {
|
|
88
69
|
type: "string",
|
|
89
|
-
description: "Design review only:
|
|
70
|
+
description: "Design review only: the in-flight batch record path — validated whenever passed (unreadable ⇒ refused, not ignored); the reviewer then also gets the `batch` write channel to record its findings + verdict. Omit when no batch record is in flight (no write channel — zero regression).",
|
|
90
71
|
},
|
|
91
72
|
},
|
|
92
73
|
required: ["type"],
|
|
@@ -96,7 +77,7 @@ export const advisorTool = {
|
|
|
96
77
|
outputPanel: true,
|
|
97
78
|
async execute(args, ctx) {
|
|
98
79
|
const agent = ctx.agent
|
|
99
|
-
// Review-object declaration (§18
|
|
80
|
+
// Review-object declaration (AGENT-LOOP-ASYNC-POOL.md §6.18 D-OA3): the PARENT constructs it and the
|
|
100
81
|
// advisor tool passes it through — mechanical anchoring, not model inference.
|
|
101
82
|
// Any non-object value (string/array/primitive, possibly from a malformed
|
|
102
83
|
// tool call) degrades to null = no injection (legacy calls unchanged).
|
|
@@ -137,9 +118,23 @@ export const advisorTool = {
|
|
|
137
118
|
// elsewhere declares them in PROJECT-MANIFEST.json docRoot).
|
|
138
119
|
if (reviewType === "design" && documents) {
|
|
139
120
|
const roots = resolveReviewTargetPaths(agent).map((r) => r.replace(/[\\/]/g, sep))
|
|
121
|
+
// 按用点解析(2026-09-21 之裁收口 · 台账 #828):逐文档按**其所属项目**(最近带档祖先)
|
|
122
|
+
// 的 docRoot 判定;无主档文档回退会话根集判定(原行为零变)。
|
|
123
|
+
const ownerRootsCache = new Map()
|
|
124
|
+
const ownRoots = (abs) => {
|
|
125
|
+
const owner = owningProject(abs)
|
|
126
|
+
if (!owner) return []
|
|
127
|
+
let r = ownerRootsCache.get(owner)
|
|
128
|
+
if (!r) {
|
|
129
|
+
r = resolveReviewRootsFor(owner).map((x) => x.replace(/[\\/]/g, sep))
|
|
130
|
+
ownerRootsCache.set(owner, r)
|
|
131
|
+
}
|
|
132
|
+
return r
|
|
133
|
+
}
|
|
140
134
|
const invalidDocs = documents.filter((doc) => {
|
|
141
135
|
const n = normAbs(doc, agent.cwd).replace(/[\\/]/g, sep)
|
|
142
|
-
|
|
136
|
+
const under = (r) => n === r || n.startsWith(r + sep)
|
|
137
|
+
return !roots.some(under) && !ownRoots(n).some(under)
|
|
143
138
|
})
|
|
144
139
|
if (invalidDocs.length > 0) {
|
|
145
140
|
if (ctx._toolCallId !== undefined) (agent._advisorRefusals ??= new Set()).add(ctx._toolCallId)
|
|
@@ -150,7 +145,7 @@ export const advisorTool = {
|
|
|
150
145
|
}
|
|
151
146
|
}
|
|
152
147
|
|
|
153
|
-
// §
|
|
148
|
+
// §6.10 (R13 — ruling ②-3 A): async gate. Depth-0 defaults to the
|
|
154
149
|
// background pool; depth>0 (eng-coder internal self-review) is ALWAYS sync —
|
|
155
150
|
// an explicit async:true there is rejected, the default never flips.
|
|
156
151
|
// depth 显式校验(归一形态——判定单点:`depth` 取一次 + `asyncRequested` 显式布尔,
|
|
@@ -168,13 +163,13 @@ export const advisorTool = {
|
|
|
168
163
|
}
|
|
169
164
|
const isAsync = asyncRequested || (depth === 0 && args.async !== false)
|
|
170
165
|
|
|
171
|
-
// Per-review instance resolution (§
|
|
166
|
+
// Per-review instance resolution (§6.10 ③ — ruling ②-5 A): fix rounds
|
|
172
167
|
// continue the same reviewId (design = the doc-set instance's designId —
|
|
173
168
|
// slot/spawn continuity; code = the newest OPEN instance). The resolution
|
|
174
169
|
// scopes agent._advisorRound/_lastAdvisorOutput so the message builder and
|
|
175
170
|
// the run.mjs cap read THIS instance's round/prior (multi-review isolation).
|
|
176
171
|
const resolved = resolveAdvisorLaunch(agent, reviewType, { documents })
|
|
177
|
-
// BATCH-RECORD.md §4.2 评审侧批次档门禁 +
|
|
172
|
+
// BATCH-RECORD.md §4.2 评审侧批次档门禁 + 实例键绑定(评审实例的唯一路径来源):
|
|
178
173
|
// 口径 = **「若传则须可读」**(空/不可读 → throw;不强制必传——无批次档的在途设计评审
|
|
179
174
|
// 零回归,N5);绑定落在 resolved.run(评审实例键,与 reviewType/round/designId 同族)——
|
|
180
175
|
// 并发设计评审各绑各档,不用单值会话态(BATCH-RECORD.md §4.7 #6)。
|
|
@@ -185,7 +180,7 @@ export const advisorTool = {
|
|
|
185
180
|
// a clean pass; on pass it is slotted under the instance's designId at settle
|
|
186
181
|
// (sync: right here; async: the settle callback — fix #2). A NEW instance
|
|
187
182
|
// gets a fresh designId; a continued fix round keeps the original one — and a
|
|
188
|
-
// same-scope re-review after a pass REUSES the session's id (F2h
|
|
183
|
+
// same-scope re-review after a pass REUSES the session's id (F2h).
|
|
189
184
|
const designToken = reviewType === "design" ? generateDesignToken(agent) : null
|
|
190
185
|
const designId = reviewType === "design" ? resolved.designId : null
|
|
191
186
|
|
|
@@ -209,7 +204,7 @@ export const advisorTool = {
|
|
|
209
204
|
if (ctx._toolCallId !== undefined) {
|
|
210
205
|
(agent._advisorAsyncAcks ??= new Set()).add(ctx._toolCallId)
|
|
211
206
|
}
|
|
212
|
-
// E(F17
|
|
207
|
+
// E(F17/ADVISOR-GUARDS.md §5 E-3c):设计评审点火回执追加冻结句(代码评审 ack 零改)——窗口下界以
|
|
213
208
|
// 可观察信号表达:报告送达 / 取消前,被审文档(含批次档)零写入。
|
|
214
209
|
const freezeNote = reviewType === "design"
|
|
215
210
|
? ";D5 冻结窗口:被审文档(含批次档)在报告送达前零写入——在途写入会被拒绝,写入将使本轮结算为陈旧 (pass 不发 token)"
|
|
@@ -244,7 +239,7 @@ export const advisorTool = {
|
|
|
244
239
|
batchDoc: resolved.run.batchDoc ?? null,
|
|
245
240
|
}, designToken, documents, paths, reviewObject, designId)
|
|
246
241
|
|
|
247
|
-
// B
|
|
242
|
+
// B 启动拒绝(稳定前缀):拒发登记(同池满 / cap 款——不置 called、不耗轮次),
|
|
248
243
|
// 可见报错照常返回(record-results 的 REFUSED 契约)。第 33 批:判定单点——同时供设计
|
|
249
244
|
// 失败分类复用(launchRefused ⇒ neutral——无尝试发生;`ADVISOR-GUARDS.md` §7 失败结算表行 1)。
|
|
250
245
|
const launchRefused = String(result).startsWith(ADVISOR_LAUNCH_REFUSAL_PREFIX)
|
|
@@ -265,12 +260,12 @@ export const advisorTool = {
|
|
|
265
260
|
const settled = settleDesignReview(agent, resolved.run, designToken, result, { incomplete })
|
|
266
261
|
// 撤计数(2026-09-18 用户裁定):原同步面“分类落账”随会话级计数器整体退场(零载体——F28③);
|
|
267
262
|
// 失败结论块由**结算出口**产出(异步结算 `settleAdvisorRun`——两轨共用,ADVISOR-GUARDS.md §7)。
|
|
268
|
-
// F2e
|
|
263
|
+
// F2e: the sync prior mirror must not carry the raw echo the runner
|
|
269
264
|
// stored — overwrite with the clean settled form (exact-suffix truncation).
|
|
270
265
|
if (settled.passed) {
|
|
271
266
|
agent._lastAdvisorOutput = stripApprovedSuffix(settled.output, resolved.run.approvedSuffix)
|
|
272
267
|
} else if (incomplete) {
|
|
273
|
-
// 未完成 ⇒ 同步 prior 镜像覆写为清洗后输出(防未注册 token 进 prior
|
|
268
|
+
// 未完成 ⇒ 同步 prior 镜像覆写为清洗后输出(防未注册 token 进 prior——ADVISOR-GUARDS.md §1)。
|
|
274
269
|
agent._lastAdvisorOutput = settled.output
|
|
275
270
|
}
|
|
276
271
|
return settled.output
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
* - 保留 ⟺ 其余——存活条目(controller 未中止——报告沿自动通道到达)/ done-in-pool(回合尾
|
|
14
14
|
* 收集 / 挂起 sweep 消化)/ cancelled(settle cancelled 分支收尾)。
|
|
15
15
|
*
|
|
16
|
+
* #9 后台 bash 任务族(三族同面并入——D9-7 收尾档②):bg 条目的「已死」不由 controller 杀达
|
|
17
|
+
* ——其控制器链只作**判据**(杀 ⟺ 控制器已中止),杀树动作经本收尾面 `spec.dispose` 钩子落
|
|
18
|
+
* `bash-async.mjs` 杀单点(`killBgTree`);回合中断(Ctrl+I)经 `bindChildController`
|
|
19
|
+
* interrupt 豁免不中止控制器 ⇒ 本面零动作(F2 同款豁免——池保留)。
|
|
20
|
+
*
|
|
16
21
|
* 判据口径(D-AD6):两接线点**均不传 `ctx`** ⇒ 实走 controller 支(`parentAborted(null, entry)`)
|
|
17
22
|
* ——与对侧有效判据同判(对侧传入的 `ctx` 为死参);`ctx` 保留为签名备用面(直调用例 / 对侧收敛)。
|
|
18
23
|
* 「controller 已中止 = 条目真死」由 `bindChildController` 单点保证(`async-settle.mjs:88-98`
|
|
@@ -36,6 +41,8 @@ import { carrierField, getAsyncPool, parentAborted, writeTombstone } from "./asy
|
|
|
36
41
|
import { pushReal } from "../context.mjs"
|
|
37
42
|
import { escapeXml } from "../agent/helpers.mjs"
|
|
38
43
|
import { logEvent } from "../log.mjs"
|
|
44
|
+
// #9(后台 bash 任务族——收尾档②):杀单点(树杀)经 `bash-async.mjs` 落面。
|
|
45
|
+
import { killBgTree } from "./bash-async.mjs"
|
|
39
46
|
|
|
40
47
|
/** 提醒模板(verbatim 同源 = 对侧 `async-discard.mjs:37-44`;测试断关键子串,不逐行断)。 */
|
|
41
48
|
const subagentReminderText = (n, list) =>
|
|
@@ -47,6 +54,11 @@ const advisorReminderText = (n, list) =>
|
|
|
47
54
|
`[System reminder: ${n} background advisor review(s) were discarded by the user's Stop — their reports will NOT arrive: ${list}.\n` +
|
|
48
55
|
"No design token was issued for a discarded design review; launch the review again if it is still needed.]"
|
|
49
56
|
|
|
57
|
+
/** #9 后台 bash 任务族提醒模板(同族形态——报告不可达的可视化:进程已杀,全量 log 仍在盘上)。 */
|
|
58
|
+
const bgReminderText = (n, list) =>
|
|
59
|
+
`[System reminder: ${n} background bash task(s) were killed by the user's Stop — their processes are gone and no digest will arrive: ${list}. ` +
|
|
60
|
+
"Full output logs remain on disk (paths were given in the start ack).]"
|
|
61
|
+
|
|
50
62
|
/** 列表词(wasStatus 数据源):queued → "(was queued — never started)";其余 "(was running)"。 */
|
|
51
63
|
const wasPhrase = (wasStatus) => (wasStatus === "queued" ? " (was queued — never started)" : " (was running)")
|
|
52
64
|
|
|
@@ -87,6 +99,7 @@ function discardRole(parent, spec, ctx) {
|
|
|
87
99
|
if (!discardable(entry, ctx)) continue
|
|
88
100
|
out.discarded.push(spec.describe(entry))
|
|
89
101
|
ids.add(String(entry.id))
|
|
102
|
+
spec.dispose?.(entry) // 族特有处置(#9 bg:杀树——判据成立即条目真死;其余族无此钩子)
|
|
90
103
|
writeTombstone(parent, entry.id, "discarded", spec.roleOf(entry))
|
|
91
104
|
removeFromPool(map, entry.id)
|
|
92
105
|
}
|
|
@@ -94,7 +107,11 @@ function discardRole(parent, spec, ctx) {
|
|
|
94
107
|
if (out.discarded.length === 0) return out // 零丢弃 = 零噪音(零注入 / 零事件)
|
|
95
108
|
spec.pruneQueue?.(parent, ids)
|
|
96
109
|
const list = out.discarded.map(spec.listPhrase).join(", ")
|
|
97
|
-
|
|
110
|
+
// 注入面(#94 载体两形吸收):agent 形(run-stages ∕ 挂起中止接线点——`parent.history` 在)走
|
|
111
|
+
// `pushReal`(机器 + 人读双线);**history 数组本体形**(VSC 挂起载体——池挂数组)无注入目标 ⇒
|
|
112
|
+
// 跳过(与对侧 VSC copy 的 `parent?.history` 守卫同判;§6.20.1「carrier 形无注入目标」先例)
|
|
113
|
+
// ——墓碑与 `ev:discarded` 照发,仅模型可见提醒缺席(数组形无人读线可达面)。
|
|
114
|
+
if (parent?.history) pushReal(parent, { role: "user", content: spec.reminder(out.discarded.length, escapeXml(list)) })
|
|
98
115
|
logEvent("ev:discarded", { n: out.discarded.length, ids: out.discarded.map((d) => `${d.role}#${d.id}`).join(", ") })
|
|
99
116
|
return out
|
|
100
117
|
}
|
|
@@ -120,6 +137,20 @@ const ADVISOR_SPEC = {
|
|
|
120
137
|
pruneQueue: (parent, ids) => pruneQueue(parent, ids, "_asyncAdvisorQueue"),
|
|
121
138
|
}
|
|
122
139
|
|
|
140
|
+
/** #9 后台 bash 任务族 spec(收尾档②):dispose = 杀树(杀单点 `killBgTree`)+ 标记 discarded
|
|
141
|
+
* (settle 面据此不注入——报告不可达);无队列面(超限 = 起跑显式拒,无排队语义)。 */
|
|
142
|
+
const BG_SPEC = {
|
|
143
|
+
pool: "bg",
|
|
144
|
+
describe: (entry) => ({ id: entry.id, role: "bg", command: entry.command }),
|
|
145
|
+
roleOf: () => "bg",
|
|
146
|
+
listPhrase: (d) => `bash#${d.id} (\`${d.command}\`)`,
|
|
147
|
+
reminder: bgReminderText,
|
|
148
|
+
dispose: (entry) => {
|
|
149
|
+
entry.discarded = true
|
|
150
|
+
killBgTree(entry)
|
|
151
|
+
},
|
|
152
|
+
}
|
|
153
|
+
|
|
123
154
|
/**
|
|
124
155
|
* 中止收尾(F1–F3):只清已死子代理条目——写 `discarded` 墓碑 + 出池 + 队列剔除 + 整批一次提醒。
|
|
125
156
|
* @param parent agent 形态(载体双形经 `getAsyncPool` / `writeTombstone` 吸收)
|
|
@@ -141,3 +172,15 @@ export function discardAbortedPool(parent, ctx = null) {
|
|
|
141
172
|
export function discardAbortedAdvisors(parent, ctx = null) {
|
|
142
173
|
return discardRole(parent, ADVISOR_SPEC, ctx)
|
|
143
174
|
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 中止收尾(#9 后台 bash 任务族——收尾档②):只清已死条目——逐条**杀树**(判据成立 ⟺ 条目
|
|
178
|
+
* 控制器已中止;杀单点经 `killBgTree`)+ `discarded` 墓碑 + 出池 + 整批一次提醒;回合中断
|
|
179
|
+
* (Ctrl+I)不中止控制器 ⇒ 本面零动作(池保留——F2 同款豁免)。
|
|
180
|
+
* @param parent agent 形态
|
|
181
|
+
* @param ctx 判据 ctx(接线点不传——controller 支,同上)
|
|
182
|
+
* @returns {{discarded: Array<{id, role: "bg", command}>, kept: number}}
|
|
183
|
+
*/
|
|
184
|
+
export function discardAbortedBgTasks(parent, ctx = null) {
|
|
185
|
+
return discardRole(parent, BG_SPEC, ctx)
|
|
186
|
+
}
|