@thincoder/core 0.9.3 → 0.9.5
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 +40 -0
- package/advisor/loop.mjs +2 -2
- package/advisor/run.mjs +1 -1
- package/agent/dispatch.mjs +21 -18
- package/agent/family-tools.mjs +6 -6
- package/agent/helpers.mjs +12 -2
- package/agent/run-stages.mjs +5 -5
- package/agent/suspension.mjs +1 -1
- package/agent-tools/advisor-async.mjs +2 -2
- package/agent-tools/advisor-settle.mjs +5 -5
- package/agent-tools/advisor.mjs +3 -3
- package/agent-tools/async-settle.mjs +2 -2
- package/agent-tools/batch-lifecycle.mjs +8 -16
- package/agent-tools/batch-paths.mjs +142 -0
- package/agent-tools/batch.mjs +10 -54
- package/agent-tools/consult.mjs +2 -2
- package/agent-tools/escalate-async.mjs +2 -2
- package/agent-tools/panel-blocks.mjs +1 -1
- package/agent-tools/subagent-actions.mjs +21 -21
- package/agent-tools/subagent-async.mjs +11 -11
- package/agent-tools/subagent-panel.mjs +5 -5
- package/agent-tools/subagent-run.mjs +8 -8
- package/agent-tools/subagent-scheduler.mjs +1 -1
- package/agent-tools/subagent-spawn.mjs +13 -12
- package/agent-tools/subagent.mjs +13 -13
- package/agent.mjs +13 -7
- package/config-presets.mjs +3 -3
- package/git/checkpoint.mjs +4 -8
- package/ledger-db.mjs +25 -5
- package/ledger-migrate.mjs +299 -0
- package/ledger.mjs +2 -1
- package/model-specs.mjs +131 -60
- package/package.json +1 -1
- package/peer-claims.mjs +263 -0
- package/peer-domains.mjs +93 -60
- package/prompts/common.md +4 -0
- package/prompts/persona-coder.md +1 -1
- package/prompts/persona-eng-coder.md +2 -2
- package/prompts/persona-eng-designer.md +2 -2
- package/prompts/persona-engineering.md +9 -0
- package/prompts/persona-explore.md +1 -1
- package/prompts/persona-normal.md +9 -0
- package/prompts/persona-plan.md +3 -3
- package/session-gc.mjs +67 -6
- package/session-slots-manifest.mjs +5 -0
- package/tool-docs/question.md +1 -0
- package/tools/edit-batch.mjs +6 -7
- package/tools/edit-diff.mjs +29 -2
- package/tools/file.mjs +7 -3
- package/tools/glob-dialect.mjs +3 -3
- package/tools/search.mjs +2 -2
- package/tools/shared.mjs +1 -1
package/agent-tools/batch.mjs
CHANGED
|
@@ -27,16 +27,15 @@
|
|
|
27
27
|
* 语义零行为变;端装配经 `configureBatchSegment({ onWrite })` 覆盖(VSC 侧现形 = `_touchedFiles`
|
|
28
28
|
* 记账)。核内零端名分支(契约 5——只认 `onWrite` 函数名)。
|
|
29
29
|
*/
|
|
30
|
-
import {
|
|
31
|
-
import { resolve } from "node:path"
|
|
30
|
+
import { readFileSync, writeFileSync } from "node:fs"
|
|
32
31
|
|
|
33
|
-
import { docRootBase, docRootPaths, readManifest } from "../manifest.mjs"
|
|
34
32
|
import { SEGMENT_BY_ROLE, readBatchStatusLine, sectionHeaderRe, findPlaceholderResidue, placeholderResidueError } from "./batch-skeleton.mjs"
|
|
33
|
+
import { batchDocBases, resolveBatchDocPath } from "./batch-paths.mjs"
|
|
35
34
|
import { closeBatchRecord, createBatchRecord, findInFlightBatch, statusBatchRecord } from "./batch-lifecycle.mjs"
|
|
36
35
|
|
|
37
|
-
export { SEGMENT_BY_ROLE }
|
|
36
|
+
export { SEGMENT_BY_ROLE, batchDocBases, resolveBatchDocPath }
|
|
38
37
|
|
|
39
|
-
/** text
|
|
38
|
+
/** text 单次上限(BATCH-RECORD.md §4.1——超出引导分段追加,不承诺"不新盖戳")。append 迁移域私有区常量
|
|
40
39
|
* (随执行体自 batch-segment.mjs 迁入——骨架档只住骨架/状态行单源,lifecycle 档头枚举口径);
|
|
41
40
|
* export = shim 导出全超集(旧 `batch-segment.mjs` 导出面等价保持)。 */
|
|
42
41
|
export const MAX_TEXT_CHARS = 20000
|
|
@@ -60,58 +59,15 @@ export function configureBatchSegment(impl) {
|
|
|
60
59
|
/** 撤销注入(测试与端装配生命周期用——缺省态 = 零记账)。 */
|
|
61
60
|
export function resetBatchSegment() { injectedOnWrite = null }
|
|
62
61
|
|
|
63
|
-
/** 可读文件判据(存在且为文件——目录/缺失同判不可读;与 lifecycle 档同名 helper 同型——
|
|
64
|
-
* KD-4 单向依赖下各自持有,三行谓词不构成第二权威源)。 */
|
|
65
|
-
function readableFile(abs) {
|
|
66
|
-
try { return existsSync(abs) && statSync(abs).isFile() } catch { return false }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* 批次档路径门禁(评审侧 §2.20.2 口径 = **「若传则须可读」**):空/非字符串/不可读 → throw。
|
|
71
|
-
* 非空且可读 → 返回绝对路径(`\` 归一——照 `files`/`batchDoc` spawn 门先例)。
|
|
72
|
-
* v2:cwd 不可读 → manifest `docRoot.batches` 复判(M3 模块设计 §2.1#4——N3 可迁移;
|
|
73
|
-
* 值形态 = 串 | 多根数组——逐基底按序复判,首个可读者胜)。
|
|
74
|
-
*/
|
|
75
|
-
export function resolveBatchDocPath(cwd, given) {
|
|
76
|
-
const base = cwd ?? process.cwd()
|
|
77
|
-
const raw = typeof given === "string" ? given.trim() : ""
|
|
78
|
-
if (!raw) {
|
|
79
|
-
throw new Error("batchDoc must be a non-empty path to the batch record — pass the batch record currently in flight, or omit the parameter entirely when no batch record is in flight.")
|
|
80
|
-
}
|
|
81
|
-
const abs = resolve(base, raw.replace(/\\/g, "/"))
|
|
82
|
-
if (readableFile(abs)) return abs
|
|
83
|
-
// 双基底(M3 模块设计 §2.1#4):cwd 不可读 → manifest docRoot.batches 复判(逐基底按序,
|
|
84
|
-
// 首个可读者胜);manifest 缺失 / 非法 / 读错 → 无第二基底(v1 单基底语义零变),
|
|
85
|
-
// 全不可读 → throw。
|
|
86
|
-
let man = { ok: false }
|
|
87
|
-
try { man = readManifest(base) } catch { /* 权限等读错——按无 manifest 处理(v1 语义) */ }
|
|
88
|
-
if (man.ok) {
|
|
89
|
-
for (const root of docRootPaths(man.manifest?.docRoot?.batches, base)) {
|
|
90
|
-
const alt = resolve(root, raw.replace(/\\/g, "/"))
|
|
91
|
-
if (readableFile(alt)) return alt
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
throw new Error(`batchDoc is not a readable file: ${raw} — pass the path of the batch record currently in flight (a path that resolves to an existing file), or omit the parameter when no batch record is in flight.`)
|
|
95
|
-
}
|
|
96
|
-
|
|
97
62
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* @param {string} cwd
|
|
103
|
-
* @returns {string[]}
|
|
63
|
+
* 批次档路径解析单源(`BATCH-RECORD.md` §4.15 · 台账 #287——实现住 `batch-paths.mjs`,本档 re-export):
|
|
64
|
+
* 候选序 cwd → 项目根 → 逐基底 · 读面取首个**可读**(口径仍「若传则须可读」,错误文案逐字零变)·
|
|
65
|
+
* create 取首个落基底内 · 锚定防嵌套(按路径段判)。四处调用点(create / 读面 / 评审门 / spawn 门)
|
|
66
|
+
* 均经该叶档——本档不再持第二份解析实现。
|
|
104
67
|
*/
|
|
105
|
-
export function batchDocBases(cwd) {
|
|
106
|
-
const base = cwd ?? process.cwd()
|
|
107
|
-
let man = { ok: false }
|
|
108
|
-
try { man = readManifest(base) } catch { /* 读错 → 按无 manifest 处理(回退默认基底) */ }
|
|
109
|
-
const roots = man.ok ? docRootPaths(man.manifest?.docRoot?.batches, base) : []
|
|
110
|
-
return roots.length ? roots : [resolve(docRootBase(base), "docs/batches")]
|
|
111
|
-
}
|
|
112
68
|
|
|
113
69
|
/**
|
|
114
|
-
*
|
|
70
|
+
* 设计评审的实例绑定解析(BATCH-RECORD.md §4.2/§4.3——评审实例的唯一路径来源):
|
|
115
71
|
* - 同步路径:调用方(advisor 工具)把实例绑定显式放进 callbacks(**带 batchDoc 键**,
|
|
116
72
|
* 未绑定即 null)——以此为准,不再回看池条目(防同步/异步混跑时串档);
|
|
117
73
|
* - 异步路径:本评审所在**池条目**的 `run.batchDoc`(文档集 = 实例键,各评审各取各条)。
|
|
@@ -143,7 +99,7 @@ function allowedSegment(agent, review) {
|
|
|
143
99
|
}
|
|
144
100
|
|
|
145
101
|
/**
|
|
146
|
-
* 凭证剥除 +
|
|
102
|
+
* 凭证剥除 + 伪造轮次标题丢弃(BATCH-RECORD.md §4.1 F6/AC30/AC34)。
|
|
147
103
|
* 逐行处理:含凭证形态的行 → 剥除该子串,剥后为空则整行丢弃(其余内容逐字保留)。
|
|
148
104
|
* `dropStamp`(仅 §3 目标段):调用方自带的 `### 轮次 N(评审子代理)` 行被丢弃——
|
|
149
105
|
* 工具生成才是唯一来源戳(否则伪造戳会污染 N 计数)。
|
package/agent-tools/consult.mjs
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* Each child settles its own TUI block with a ⟦ev⟧done event at settle (R17 —
|
|
23
23
|
* the old in-turn check consumption is gone).
|
|
24
24
|
*/
|
|
25
|
-
import { createAgent, runAgent, readonlyToolNames } from "../agent.mjs"
|
|
25
|
+
import { createAgent, runAgent, readonlyToolNames, excludeSubagentTools } from "../agent.mjs"
|
|
26
26
|
import { resolveChildProvider } from "./subagent.mjs"
|
|
27
27
|
import { pushReal } from "../context.mjs"
|
|
28
28
|
import { offloadToolResult, escapeXml } from "../agent/helpers.mjs"
|
|
@@ -268,7 +268,7 @@ async function runConsultChild(ctx, session, id, m, problem, ctrl) {
|
|
|
268
268
|
// Read-only consultant: filter the parent tool set down to readonly tools + main_history.
|
|
269
269
|
const allowed = readonlyToolNames(agent.tools ?? [])
|
|
270
270
|
const tools = [
|
|
271
|
-
...(agent.tools ?? []).filter((t) => allowed.has(t.name)),
|
|
271
|
+
...excludeSubagentTools((agent.tools ?? []).filter((t) => allowed.has(t.name))), // TOOLS.md §6.16:继承面过谓词
|
|
272
272
|
makeMainHistoryTool(agent),
|
|
273
273
|
]
|
|
274
274
|
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* domain still follows the consuming turn's tier — no family exception).
|
|
26
26
|
*/
|
|
27
27
|
import { relative, isAbsolute } from "node:path"
|
|
28
|
-
import { runAgent, createAgent, DEFAULT_SUBAGENT_TURNS } from "../agent.mjs"
|
|
28
|
+
import { runAgent, createAgent, DEFAULT_SUBAGENT_TURNS, excludeSubagentTools } from "../agent.mjs"
|
|
29
29
|
import { runWithContinue, TURN_CAP_MARK, wrapChildCallbacks } from "../agent/spawn-child.mjs"
|
|
30
30
|
// TUI-OOM-ROOTCAUSE §23.3.1:子代理人读线窗口常量(单源——store 零依赖)。
|
|
31
31
|
import { RECORD_WINDOW_MESSAGES } from "../session-store.mjs"
|
|
@@ -192,7 +192,7 @@ export function launchEscalateAsync(parent, ctx, launch) {
|
|
|
192
192
|
ctx.callbacks?.onToken?.(relayPrefix + "[model]" + (provider.model ?? ""))
|
|
193
193
|
const child = createAgent({
|
|
194
194
|
provider,
|
|
195
|
-
tools: parent.tools,
|
|
195
|
+
tools: excludeSubagentTools(parent.tools), // TOOLS.md §6.16:子代面按面排除(行内改——本档 Δ±0)
|
|
196
196
|
config: parent.config,
|
|
197
197
|
cwd: parent.cwd,
|
|
198
198
|
memory: parent.memory,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* panel-blocks.mjs — §
|
|
2
|
+
* panel-blocks.mjs — AGENT-LOOP-SUBAGENT.md §6.7.2 D-P1 面板块列表(读时现算,纯函数)。
|
|
3
3
|
*
|
|
4
4
|
* 来源 = `thincoder-cli/src/tui/subagent-freeze.mjs` 的 `computePanelBlocks`——**逐字随迁**
|
|
5
5
|
* (CORE-UNIFICATION §2.5 #159:`panel` 动作以 CLI 为准;#99:VSC 无 panel = **有意端差**
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* subagent-actions.mjs — subagent
|
|
2
|
+
* subagent-actions.mjs — subagent 动作执行器(AGENT-LOOP-SUBAGENT.md §6.7——2026-09-05 自
|
|
3
3
|
* subagent-async.mjs 拆分——Module Split Policy §20.9——纯迁移零行为变化)。
|
|
4
|
-
* 内容:executeStatusAction
|
|
4
|
+
* 内容:executeStatusAction(AGENT-LOOP-SUBAGENT.md §6.7.2 D-M5——非阻塞池查询——touchedSummary/
|
|
5
5
|
* shortTouchedPath/statusFields 摘要助手随行)/ executeObserveAction + executeSendAction
|
|
6
|
-
* (SUBAGENT-OBSERVE-SEND——observe 只读/send 控制豁免注入)/ executeEscalateAction
|
|
6
|
+
* (SUBAGENT-OBSERVE-SEND——observe 只读/send 控制豁免注入)/ executeEscalateAction(AGENT-LOOP-SUBAGENT.md §6.7
|
|
7
7
|
* D-M4——飞刀——touchedFilesNote 随行)。
|
|
8
|
-
* §
|
|
8
|
+
* AGENT-LOOP-SUBAGENT.md §6.7.5 check 删除后仅 cancel 动作执行器与共享 post-spawn 管线留在 subagent-async.mjs;
|
|
9
9
|
* §20 调度器 + 文件域组在 ./subagent-scheduler.mjs(describeBlockers 由此导入)。
|
|
10
|
-
* 2026-09-08
|
|
10
|
+
* 2026-09-08 二次拆分:AGENT-LOOP-SUBAGENT.md §6.7.2 面板段迁至 ./subagent-panel.mjs(601 > 500 硬限跨档——
|
|
11
11
|
* 尾部 re-export executePanelAction 保 subagent.mjs 既有 import 面)+ ASYNC-RESULT-
|
|
12
12
|
* CONTAINER.md D1 落地(池访问点改 getAsyncPool accessor)。
|
|
13
13
|
*/
|
|
14
14
|
import { isAbsolute, relative } from "node:path"
|
|
15
|
-
import { runAgent, createAgent, DEFAULT_SUBAGENT_TURNS } from "../agent.mjs"
|
|
15
|
+
import { runAgent, createAgent, DEFAULT_SUBAGENT_TURNS, excludeSubagentTools } from "../agent.mjs"
|
|
16
16
|
import {
|
|
17
17
|
runWithContinue, TURN_CAP_MARK, makeRelay, wrapChildCallbacks,
|
|
18
18
|
ensureChildApiKey, clampEffort,
|
|
@@ -27,11 +27,11 @@ import { RECORD_WINDOW_MESSAGES } from "../session-store.mjs"
|
|
|
27
27
|
import { getAsyncPool } from "./async-settle.mjs"
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
-
* subagent action:"status" (§
|
|
30
|
+
* subagent action:"status" (AGENT-LOOP-SUBAGENT.md §6.7 D-M2, new): NON-BLOCKING async-pool query —
|
|
31
31
|
* returns immediately and never consumes a result (results belong to the auto
|
|
32
|
-
* channel — §
|
|
32
|
+
* channel — AGENT-LOOP-SUBAGENT.md §6.7.5: no check action any more). Source of truth = the pool
|
|
33
33
|
* (_asyncSubagents): entries moved to _pendingAsyncResults during a suspension
|
|
34
|
-
* (§
|
|
34
|
+
* (AGENT-LOOP-ASYNC-POOL.md §6.8 D-S3 ② — injected at the next run start) are no longer in the pool and
|
|
35
35
|
* are NOT counted as done-waiting.
|
|
36
36
|
* - id given → { id, role, status, model?, elapsedSec?, turn?, maxTurns?,
|
|
37
37
|
* touchedFiles?/touchedMore?/touched? ... } for that entry; unknown id → error
|
|
@@ -39,20 +39,20 @@ import { getAsyncPool } from "./async-settle.mjs"
|
|
|
39
39
|
* - id omitted → { overview: { running: [{id, role, model, elapsedSec, turn,
|
|
40
40
|
* maxTurns, touchedFiles?/touched?}], queued: [{id, role, position, touched?}],
|
|
41
41
|
* done: [{id, role}] } } — live queue positions (index in _asyncQueue + 1).
|
|
42
|
-
* §
|
|
42
|
+
* AGENT-LOOP-SUBAGENT.md §6.7.2: running 条目带 touched files 摘要(touchedFiles 前 5 + touchedMore 超出
|
|
43
43
|
* 计数——相对查询方 cwd;0 改动 → touched 占位);queued 条目带 touched 占位
|
|
44
44
|
* "—(未启动)";done/error/取消条目无 touched 字段(round3 #9)。
|
|
45
45
|
* A settled-but-unconsumed entry (settled during a NORMAL turn) reports done
|
|
46
46
|
* with a "not yet consumed" note — the auto channel (turn-end collection / the
|
|
47
|
-
* suspension digest) still delivers it afterwards (§
|
|
47
|
+
* suspension digest) still delivers it afterwards (AGENT-LOOP-SUBAGENT.md §6.7.5: sole consumption path).
|
|
48
48
|
*/
|
|
49
|
-
/** §
|
|
49
|
+
/** AGENT-LOOP-SUBAGENT.md §6.7.2 D-M5 decision-field assembly (F9): running entries report
|
|
50
50
|
* {id, role, model, elapsedSec, turn, maxTurns} — the data needed to decide
|
|
51
51
|
* WHO to cancel. Model is recorded at spawn (childProvider), startedAt at
|
|
52
52
|
* ACTUAL start (queued waits don't count), turn/maxTurns mirrored from the
|
|
53
53
|
* child's ⟦ev⟧turn events at the callbacks-wrap layer (subagent.mjs tracker).
|
|
54
54
|
* elapsedSec computed at call time from startedAt. */
|
|
55
|
-
/** §
|
|
55
|
+
/** AGENT-LOOP-SUBAGENT.md §6.7.2 D-SF2/N-SF1 摘要形态:status 调用时实时读 entry.childAgent._touchedFiles
|
|
56
56
|
* (绝对路径——per-run 记账——§18.12)→ 相对查询方 cwd;cwd 之外保留绝对形态 +
|
|
57
57
|
* "../" 前缀;>80 字符截尾(不超行);前 5 个 + 独立截断字段 touchedMore(超出
|
|
58
58
|
* 计数——不混入数组——消费方按类型区分)。占位:running 0 改动 → "—(尚无改动)"
|
|
@@ -94,10 +94,10 @@ function statusFields(entry, cwd) {
|
|
|
94
94
|
base.elapsedSec = entry.startedAt ? Math.max(0, Math.floor((Date.now() - entry.startedAt) / 1000)) : 0
|
|
95
95
|
base.turn = entry.turn ?? 0
|
|
96
96
|
base.maxTurns = entry.maxTurns ?? 0
|
|
97
|
-
// §
|
|
97
|
+
// AGENT-LOOP-SUBAGENT.md §6.7.2:touched files 摘要(T-SF1..4——新字段追加——既有字段零破坏)
|
|
98
98
|
Object.assign(base, touchedSummary(entry, cwd))
|
|
99
99
|
} else if (entry.status === "queued") {
|
|
100
|
-
// §
|
|
100
|
+
// AGENT-LOOP-SUBAGENT.md §6.7.2 T-SF2b:未启动——确定性占位(不崩;无对象可读)
|
|
101
101
|
base.touched = "—(未启动)"
|
|
102
102
|
}
|
|
103
103
|
return base
|
|
@@ -151,7 +151,7 @@ export function executeStatusAction(args, ctx) {
|
|
|
151
151
|
return JSON.stringify(out)
|
|
152
152
|
}
|
|
153
153
|
// done = settled during this turn and not yet consumed — the auto channel still
|
|
154
|
-
// delivers it (§
|
|
154
|
+
// delivers it (AGENT-LOOP-SUBAGENT.md §6.7.5: turn-end collection / the suspension digest — no fetch action).
|
|
155
155
|
target.status = "done"
|
|
156
156
|
target.done = true
|
|
157
157
|
if (entry.error) target.error = entry.error
|
|
@@ -164,7 +164,7 @@ export function executeStatusAction(args, ctx) {
|
|
|
164
164
|
if (entry.status === "running") overview.running.push(statusFields(entry, agent.cwd))
|
|
165
165
|
else if (entry.status === "queued") {
|
|
166
166
|
// §20:queued 条目补 waiting/reason(F-SD4——依赖/冲突原因模型可见);
|
|
167
|
-
// §
|
|
167
|
+
// AGENT-LOOP-SUBAGENT.md §6.7.2 T-SF2b:未启动占位(确定性——不崩)。
|
|
168
168
|
const blk = describeBlockers(agent, entry)
|
|
169
169
|
const row = statusFields(entry, agent.cwd)
|
|
170
170
|
row.position = queuedPosition(String(entry.id)) ?? entry.position
|
|
@@ -327,16 +327,16 @@ export function executeSendAction(args, ctx) {
|
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
330
|
-
//
|
|
330
|
+
// subagent panel 检查工具(AGENT-LOOP-SUBAGENT.md §6.7.2——F-P1..P3/D-P1..P4)
|
|
331
331
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
332
|
-
// 2026-09-08 拆分(Module Split Policy——601 > 500
|
|
332
|
+
// 2026-09-08 拆分(Module Split Policy——601 > 500 硬限跨档):AGENT-LOOP-SUBAGENT.md §6.7.2 面板段迁至
|
|
333
333
|
// ./subagent-panel.mjs(executePanelAction/panelFreezeGate/blockKeyIn——verbatim +
|
|
334
334
|
// ASYNC-RESULT-CONTAINER D1/D2 落地);本面保留 re-export 保 subagent.mjs 既有
|
|
335
335
|
// import 面(subagent-async 尾部 re-export 先例)。
|
|
336
336
|
export { executePanelAction } from "./subagent-panel.mjs"
|
|
337
337
|
|
|
338
338
|
/**
|
|
339
|
-
* subagent action:"escalate"
|
|
339
|
+
* subagent action:"escalate"(AGENT-LOOP-SUBAGENT.md §6.7 D-M4——退役 escalate 工具语义原样,ESCALATE.md;
|
|
340
340
|
* §25 D-R17b——R17:缺省 async——后台飞刀 + settle 三分类 digest——async:false 保
|
|
341
341
|
* 同步旧路径)。飞刀——交给 consultModels 池里更强模型(WRITE + 术后报告)。约束全
|
|
342
342
|
* 保留:depth-0 only / 工程模式拒 / consultModels 空拒 / relay 前缀 `escalate#N/`
|
|
@@ -408,7 +408,7 @@ export async function executeEscalateAction(args, ctx) {
|
|
|
408
408
|
// G3(施工②):overlay 摘除——coder 人格槽由 assemblePrompt 场景表承载(G3 映射)。
|
|
409
409
|
child = createAgent({
|
|
410
410
|
provider,
|
|
411
|
-
tools: parent.tools,
|
|
411
|
+
tools: excludeSubagentTools(parent.tools), // TOOLS.md §6.16:子代面按面排除(行内改——本档 Δ±0)
|
|
412
412
|
config: parent.config,
|
|
413
413
|
cwd: parent.cwd,
|
|
414
414
|
memory: parent.memory,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* subagent-async.mjs — async subagent 机械 + 共享 post-spawn 管线 + cancel 动作执行器
|
|
3
|
-
* (AGENT-LOOP.md §
|
|
4
|
-
* consume-design + observe/send(SUBAGENT-OBSERVE-SEND
|
|
5
|
-
* 工具面在 subagent.mjs;cancel 动作执行器与机械、管线在本模块——check 执行器随 §
|
|
3
|
+
* (AGENT-LOOP-SUBAGENT.md §6.7:subagent 单工具多动作——spawn/status/escalate/cancel/panel/
|
|
4
|
+
* consume-design + observe/send(SUBAGENT-OBSERVE-SEND)——AGENT-LOOP-SUBAGENT.md §6.7.5 check 已删;spawn 路径与
|
|
5
|
+
* 工具面在 subagent.mjs;cancel 动作执行器与机械、管线在本模块——check 执行器随 AGENT-LOOP-SUBAGENT.md §6.7.5 删除)。
|
|
6
6
|
* 内容:resolveChildProvider / async 池常量与域助手(ASYNC_POOL_LIMITS/poolDomainOf/
|
|
7
7
|
* resolvePoolLimits/poolLimitsFor/runningPoolCount——§11.1)/ executeCancelAction +
|
|
8
|
-
* cancelAsyncSubagent
|
|
8
|
+
* cancelAsyncSubagent(AGENT-LOOP-SUBAGENT.md §6.7.2 D-M6——工具与 TUI ⏹ 共用)/ runChildPipeline /
|
|
9
9
|
* injectAsyncResult / buildChildRunOpts / mergeChildMutations / enqueueAsk(批 6——
|
|
10
10
|
* _permQueue 审批/继续弹窗串行收 helper——消费位 subagent.mjs/subagent-spawn.mjs/
|
|
11
11
|
* escalate-async.mjs)。
|
|
@@ -48,7 +48,7 @@ export function enqueueAsk(owner, key, ask) {
|
|
|
48
48
|
// independent pools — eng-coder 4 / other roles 4 (user ruling "eng-coder 四路,
|
|
49
49
|
// 其他 4 路") — a full engCoder pool never blocks an explore spawn and vice versa
|
|
50
50
|
// (same-domain cap still 4, cross-domain total up to 8). The per-turn check
|
|
51
|
-
// budget was deleted with the check action (§
|
|
51
|
+
// budget was deleted with the check action (AGENT-LOOP-SUBAGENT.md §6.7.5 — results arrive only via the
|
|
52
52
|
// auto channel; no loop guard needed).
|
|
53
53
|
// ⚠ 与 config.mjs DEFAULTS.agent.poolLimits 的 SUBAGENT 两键(engCoder/other)逐键同值
|
|
54
54
|
// (loadConfig/settings 默认源)——耦合锚 T-24a4 断言锁住——勿单侧改默认。advisor 第三键
|
|
@@ -163,7 +163,7 @@ export function resolveChildProvider(parent, modelArg) {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
/**
|
|
166
|
-
* §
|
|
166
|
+
* AGENT-LOOP-SUBAGENT.md §6.7.2 D-M6 cancel 核心(工具路径与 TUI ⏹ 共用):定向中止单个后台子代理,id 必填。
|
|
167
167
|
* - queued 目标(未启动无 controller):出队 + 后续 position 前移 + settle waiter
|
|
168
168
|
* → { id, status:"cancelled", was:"queued" }——无 abort(T-M27)
|
|
169
169
|
* - running 目标:置 entry.cancelled + 条目 controller abort(child runAgent signal)
|
|
@@ -183,7 +183,7 @@ export function cancelAsyncSubagent(agent, id) {
|
|
|
183
183
|
}
|
|
184
184
|
if (entry.cancelled) return { id: key, status: "cancelled" } // abort already in flight — idempotent
|
|
185
185
|
if (entry.status === "queued") {
|
|
186
|
-
// 出队 + position
|
|
186
|
+
// 出队 + position 释放/前移(AGENT-LOOP-SUBAGENT.md §6.7.2 D-M6——无 abort)
|
|
187
187
|
const queue = agent._asyncQueue ?? []
|
|
188
188
|
const qi = queue.indexOf(entry)
|
|
189
189
|
if (qi >= 0) {
|
|
@@ -237,7 +237,7 @@ export function cancelSyncChild(agent, key) {
|
|
|
237
237
|
return { id: k, status: "cancelled" }
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
/** subagent action:"cancel" (§
|
|
240
|
+
/** subagent action:"cancel" (AGENT-LOOP-SUBAGENT.md §6.7.2 D-M6): depth-0 main-session control only.
|
|
241
241
|
* §20 D-SD5/round1 #4(queued 依赖取消——无 settle 事件):出队后**返回即**重估
|
|
242
242
|
* 依赖者——依赖者留 queued 标 dependency cancelled(refreshQueuedTokens 发更新块头
|
|
243
243
|
* token)+ 工具结果内注依赖者(模型可见——工具结果内——round1 #4 明示通道)+ 补位
|
|
@@ -349,7 +349,7 @@ export async function runChildPipeline(child, input, childOpts, childRunOpts, {
|
|
|
349
349
|
|
|
350
350
|
/**
|
|
351
351
|
* Inject one settled async entry into the parent history as a user-role reminder
|
|
352
|
-
* (§
|
|
352
|
+
* (AGENT-LOOP-ASYNC-POOL.md §6.8 D-S3 — the auto channel, sole consumption path since AGENT-LOOP-SUBAGENT.md §6.7.5: turn-end
|
|
353
353
|
* collection (collectSettledAsync, agent.mjs) and the run-start _pendingAsyncResults
|
|
354
354
|
* injection; the message shape is identical to the §15 collector's). Consumed =
|
|
355
355
|
* the caller removes the entry from its container; no double-inject across the
|
|
@@ -386,7 +386,7 @@ export async function injectAsyncResult(agent, entry) {
|
|
|
386
386
|
})
|
|
387
387
|
// §20 D-SD5 终态墓碑:本函数是全部自动注入路径的共享形态(回合尾 collect + 挂起
|
|
388
388
|
// digest 首行注入)——注入即消费(调用方随即从容器移除)——dependsOn 引用该 id 的
|
|
389
|
-
// 后续 spawn 视为已满足(T-SD14
|
|
389
|
+
// 后续 spawn 视为已满足(T-SD14 消费终态语义——AGENT-LOOP-SUBAGENT.md §6.7.5 后 check 消费路径删除,本自动
|
|
390
390
|
// 通道为唯一消费方;error 条目记 failed——依赖取消/失败分支照旧,不误标成功)。
|
|
391
391
|
writeTombstone(agent, entry.id, entry.error != null ? "failed" : "consumed", entry.role)
|
|
392
392
|
}
|
|
@@ -396,7 +396,7 @@ export async function injectAsyncResult(agent, entry) {
|
|
|
396
396
|
* child: without it, Ctrl+C aborts the parent's controller but the child keeps
|
|
397
397
|
* running its full turn budget (up to subagentTurns) while the parent awaits —
|
|
398
398
|
* the interrupt appears to do nothing.
|
|
399
|
-
* §
|
|
399
|
+
* AGENT-LOOP-ASYNC-POOL.md §6.8 D-S9: during a suspension session children share the SESSION signal instead
|
|
400
400
|
* (agent._sessionSignal) — a digest's own Ctrl+I/Ctrl+C must not abort the whole
|
|
401
401
|
* pool; the session driver aborts the session controller to stop everything.
|
|
402
402
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* subagent-panel.mjs — subagent action:"panel"
|
|
3
|
-
* subagent-actions.mjs 拆分——Module Split Policy:601 > 500
|
|
2
|
+
* subagent-panel.mjs — subagent action:"panel" 执行器(AGENT-LOOP-SUBAGENT.md §6.7.2——2026-09-08 自
|
|
3
|
+
* subagent-actions.mjs 拆分——Module Split Policy:601 > 500 硬限跨档,AGENT-LOOP-SUBAGENT.md §6.7.2 面板段
|
|
4
4
|
* verbatim 迁入 + ASYNC-RESULT-CONTAINER.md D1/D2 落地(池 accessor getAsyncPool +
|
|
5
5
|
* pending 单容器 _pendingAsyncResults 四族统一——escalate/consult 独立族退役))。
|
|
6
6
|
* 内容:executePanelAction(D-P2——readonly 视图面 + 门控 freeze)+ panelFreezeGate
|
|
@@ -21,7 +21,7 @@ function blockKeyIn(container, key) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
|
-
* §
|
|
24
|
+
* AGENT-LOOP-SUBAGENT.md §6.7.2 D-P3 冻结门控(安全):仅允许冻结 awaitingDigest 且池(_asyncSubagents/
|
|
25
25
|
* _asyncAdvisors——经 getAsyncPool accessor——D1)无对应运行条目 + pending 单容器
|
|
26
26
|
* (_pendingAsyncResults——D2)无对应条目的块(= 已消化驻留块——报告已入模型上下文
|
|
27
27
|
* ——pending 已消费——状态滞后——补发冻结不破坏任何顺序)。
|
|
@@ -63,7 +63,7 @@ function panelFreezeGate(ctx, key) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
* §
|
|
66
|
+
* AGENT-LOOP-SUBAGENT.md §6.7.2 subagent action:"panel"(D-P2——readonly 视图面 + 门控干预面——单动作双参,
|
|
67
67
|
* freeze 优先):
|
|
68
68
|
* - view(缺省——返回面板块列表):ctx.state(= agent._tuiState——CLI TUI 装配)经
|
|
69
69
|
* computePanelBlocks **读时现算**(F-3 单账本——与用户所见一致)。awaitingDigest 条目
|
|
@@ -72,7 +72,7 @@ function panelFreezeGate(ctx, key) {
|
|
|
72
72
|
* = 报告已消化但块仍驻留——异常块——freeze 候选;模型可定位解释 UI 怪相)。
|
|
73
73
|
* - freeze:key(D-P3 门控通过 → 发 key + "/" + ⟦ev⟧done 哨兵字面 token——
|
|
74
74
|
* onToken——TUI routeSubToken 冻结回收——落位复用 sub._freezeAt settle 锚点
|
|
75
|
-
* splice
|
|
75
|
+
* splice,无锚点尾推兜底——AGENT-LOOP-ASYNC-POOL.md §6.8 同口径——round1 #2)。
|
|
76
76
|
* 无 TUI state(headless/VS Code——D-P2 round1 #1:webview 无 state.subTasks 对应物——
|
|
77
77
|
* 7.2.3.2 #8 先例)→ view 恒降级池视图(双池经 getAsyncPool + pending 单容器
|
|
78
78
|
* 合成)+ no panel 注;freeze 报不可用。CLI-only 完整能力(AC-P4)。
|
|
@@ -36,7 +36,7 @@ export function drainInjectedQueue(entry, agent) {
|
|
|
36
36
|
* The child runs the EXACT blocking pipeline (runChildPipeline — relay /
|
|
37
37
|
* turn-cap / permission / MIN_REPORT_CHARS / mergeChildMutations all unchanged),
|
|
38
38
|
* but the parent does not await it: the promise is parked in _asyncSubagents and
|
|
39
|
-
* consumed by the auto channel (§
|
|
39
|
+
* consumed by the auto channel (AGENT-LOOP-SUBAGENT.md §6.7.5 — turn-end collection / suspension digest;
|
|
40
40
|
* the check action is gone). Slot queue (§11.1 D-24a/R14 — 分域): the entry carries a
|
|
41
41
|
* pool domain (_pool = poolDomainOf(role)); running count < limit[its domain]
|
|
42
42
|
* (agent.poolLimits — default engCoder 4 / other 4) → start now; ≥ → enqueue
|
|
@@ -64,13 +64,13 @@ export function executeAsyncSpawn(parent, ctx, role, args, child, input, childOp
|
|
|
64
64
|
position: undefined,
|
|
65
65
|
report: null, error: null, done: false, cancelled: false,
|
|
66
66
|
promise: null, _settle: null, _settleSeq: 0,
|
|
67
|
-
// §
|
|
67
|
+
// AGENT-LOOP-SUBAGENT.md §6.7.2 D-M5 可决策字段(status 数据装配锚点):model 在 spawn 时记录;
|
|
68
68
|
// startedAt 在 ACTUAL start(queued 等待不计 elapsed);turn/maxTurns 由
|
|
69
69
|
// 下方 onToken 拦截层从子代理 ⟦ev⟧turn 事件镜像(T-M18 正确性断言)。
|
|
70
70
|
model: childProvider?.model ?? null,
|
|
71
71
|
startedAt: null,
|
|
72
72
|
turn: 0, maxTurns: 0,
|
|
73
|
-
// §
|
|
73
|
+
// AGENT-LOOP-SUBAGENT.md §6.7.2 D-M6 (round2 #2):条目级 AbortController——cancel 定向 abort 本
|
|
74
74
|
// 条目(runAgent signal 链);Ctrl+C 全停语义不变(基信号 abort 逐链传播)。
|
|
75
75
|
controller: null,
|
|
76
76
|
// §20 D-SD2 域元数据(AGENT-LOOP.md §20):running ∪ queued 条目全带——
|
|
@@ -97,15 +97,15 @@ export function executeAsyncSpawn(parent, ctx, role, args, child, input, childOp
|
|
|
97
97
|
}
|
|
98
98
|
// The settle signal — resolves when the run chain settles (never rejects).
|
|
99
99
|
entry.promise = new Promise((res) => { entry._settle = res })
|
|
100
|
-
// §
|
|
101
|
-
// §
|
|
100
|
+
// AGENT-LOOP-SUBAGENT.md §6.7.2 D-M6:条目 controller 链到会话/回合基信号(_sessionSignal 优先——
|
|
101
|
+
// AGENT-LOOP-ASYNC-POOL.md §6.8 挂起会话内 children 持会话 signal,digest 自身 Ctrl+C 不误伤)。
|
|
102
102
|
// D6 buildChildSignal 单点(ASYNC-RESULT-CONTAINER.md)。
|
|
103
103
|
const ctrl = new AbortController()
|
|
104
104
|
entry.controller = ctrl
|
|
105
105
|
// §20.3 站点 #10(第 24 批):hop 逐跳保 reason;#98 链结单点(interrupt 豁免面——
|
|
106
106
|
// Ctrl+I 不逐链中止池内子代理)。
|
|
107
107
|
bindChildController(ctrl, buildChildSignal(parent, ctx))
|
|
108
|
-
// §
|
|
108
|
+
// AGENT-LOOP-SUBAGENT.md §6.7.2 D-M5:turn 镜像拦截层(callbacks 包装层——在既有
|
|
109
109
|
// wrapChildCallbacks 之外再包一层,只解析 ⟦ev⟧turn 更新条目,其余原样转发)。
|
|
110
110
|
const trackOpts = { ...childOpts }
|
|
111
111
|
const parentOnToken = trackOpts.onToken
|
|
@@ -127,7 +127,7 @@ export function executeAsyncSpawn(parent, ctx, role, args, child, input, childOp
|
|
|
127
127
|
entry.status = "running"
|
|
128
128
|
entry.position = undefined
|
|
129
129
|
entry.startedAt = Date.now()
|
|
130
|
-
// §
|
|
130
|
+
// AGENT-LOOP-SUBAGENT.md §6.7.2 D-SF1(round3 #1):绑定子代理对象引用——绑定时刻 = 实际启动时
|
|
131
131
|
// (queued 条目 spawn-ack 时刻尚无子代理对象——§20 D-SD3b);绑定对象 = 子代理
|
|
132
132
|
// 对象(不是 _touchedFiles 数组引用——per-run 记账在 prepareRun 重置——数组
|
|
133
133
|
// 引用会陈旧——对象引用保证 status 查询时实时读——杀前一刻最新)。
|
|
@@ -139,7 +139,7 @@ export function executeAsyncSpawn(parent, ctx, role, args, child, input, childOp
|
|
|
139
139
|
// 持 entry + child;空队列 no-op(每轮尝试——零开销)。N2:不打断在跑工具——入队
|
|
140
140
|
// 消息只在下一回合边界(当前工具完成后的下轮 chat 前)进上下文。
|
|
141
141
|
const consumeInjected = (ag) => { drainInjectedQueue(entry, ag ?? child) }
|
|
142
|
-
// §
|
|
142
|
+
// AGENT-LOOP-SUBAGENT.md §6.7.2 D-M7b ①: async 标记事件——零字段 ⟦ev⟧async token(sync 不发)。
|
|
143
143
|
// 锚点 = 实际启动(与 [model] 同步——queued 入队不 paint,补位启动才发);
|
|
144
144
|
// 先于 [model] 发出——区块创建即知 sub.async(routeSubToken 解析——
|
|
145
145
|
// ⏹ 门控与头标 async 的判定源)。父级直接 emit(depth-0 专属路径——
|
|
@@ -191,7 +191,7 @@ export function describeBlockers(parent, entry) {
|
|
|
191
191
|
* 文件冲突只阻断后入者——id 序)——混合环仅人工注入可构造(T-SD5 同族防御断言)——
|
|
192
192
|
* 但状态既可能成环即机械可检——不依赖可达性论证。maybeRefillAsync 空转处不调用:refill
|
|
193
193
|
* 嵌在 settle/cancel 链上无模型可见输出通道(抛错即破坏池操作)——status 视图(actions
|
|
194
|
-
* 模块)即 F-SL2
|
|
194
|
+
* 模块)即 F-SL2 的报错/标记落点(AGENT-LOOP-SUBAGENT.md §6.7.5:check 守卫随 check 删除)。 */
|
|
195
195
|
export const STALL_NOTE = "cancel one task in the loop (action:'cancel') to break the cycle, then re-spawn it"
|
|
196
196
|
|
|
197
197
|
export function detectStall(parent) {
|
|
@@ -11,11 +11,9 @@
|
|
|
11
11
|
* (`child._spawnSystemBlock` 单点绑定——消费点 = 核 `prepareRun`),`input` 只留任务书。
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { resolve } from "node:path"
|
|
15
|
-
import { existsSync, statSync } from "node:fs"
|
|
16
14
|
import {
|
|
17
15
|
createAgent,
|
|
18
|
-
readonlyToolNames, escapeXml,
|
|
16
|
+
readonlyToolNames, escapeXml, excludeSubagentTools,
|
|
19
17
|
} from "../agent.mjs"
|
|
20
18
|
import { allocRelay, wrapChildCallbacks, relayPrefixOf } from "../agent/spawn-child.mjs"
|
|
21
19
|
// TUI-OOM-ROOTCAUSE(AGENT-LOOP.md §23.3.1):子代理人读线窗口常量单源(store 零依赖)。
|
|
@@ -33,6 +31,9 @@ import { validateTaskBookFields } from "./spawn-gates.mjs"
|
|
|
33
31
|
// 台账 #23(AGENT-LOOP-SUBAGENT.md §6.26 · D23-3):审计块构造器外提(`summarizeEngTaskBook`
|
|
34
32
|
// + `buildAuditBlock` 纯函数)——spawn 档只做「收集 + 绑定」;块文本逐字搬移(D23-4)。
|
|
35
33
|
import { buildAuditBlock } from "./audit-block.mjs"
|
|
34
|
+
// 批次档路径解析单源(BATCH-RECORD.md §4.15 · 台账 #287):门内解析改经叶档(读面非抛形),
|
|
35
|
+
// 与 append / create 同源——门判据仍「参数在 + 路径可读」,错误文案逐字零变。
|
|
36
|
+
import { resolveBatchReadPath } from "./batch-paths.mjs"
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* Effective subagent model override for a role (CLI parity shared with VS Code):
|
|
@@ -206,20 +207,17 @@ export function buildSpawnChild(parent, ctx, args, role, wantAsync, files, depen
|
|
|
206
207
|
// 批次档路径(需求 §1.11 铁律 #5 随件传递的机械面)。落点 = 本装配点(token 门之前):
|
|
207
208
|
// sync 与 async 两条 spawn 路径都经过 buildSpawnChild——一处校验双路生效,错误出口与
|
|
208
209
|
// token 门一致(§2.13.1 选型 A)。
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
// 越界文案参数化——designer 撞门时不误导)。
|
|
210
|
+
// 路径语义照 `files` 先例:cwd 相对或绝对均可,`\` 归一为 `/`;解析序单源 = §4.15(cwd → 项目根 →
|
|
211
|
+
// 逐基底,读面取首个可读)——cwd 非项目根时根相对串不再必拒(BR-29/BR-33)。错误文案带
|
|
212
|
+
// **实际角色名**(§2.15 越界文案参数化——designer 撞门时不误导)与逐字不可读后缀。
|
|
213
213
|
let batchDocAbs = null
|
|
214
214
|
if (role === "eng-coder" || role === "eng-designer") {
|
|
215
215
|
const given = typeof args.batchDoc === "string" ? args.batchDoc.trim() : ""
|
|
216
216
|
const refusal = (suffix = "") => new Error(
|
|
217
217
|
`batchDoc is required for role='${role}' — pass the batch record path (docs/batches/<batch>-<topic>.md); spawn refused without it.` + suffix)
|
|
218
218
|
if (!given) throw refusal()
|
|
219
|
-
batchDocAbs =
|
|
220
|
-
|
|
221
|
-
try { readable = existsSync(batchDocAbs) && statSync(batchDocAbs).isFile() } catch { readable = false }
|
|
222
|
-
if (!readable) throw refusal(" (given path is not a readable file)")
|
|
219
|
+
batchDocAbs = resolveBatchReadPath(parent.cwd ?? process.cwd(), given)
|
|
220
|
+
if (!batchDocAbs) throw refusal(" (given path is not a readable file)")
|
|
223
221
|
}
|
|
224
222
|
|
|
225
223
|
// M5 F2(ENGINEERING-MODE-V2-MODULE-DELEGATION §2.2):任务书六强制字段门——
|
|
@@ -258,6 +256,9 @@ export function buildSpawnChild(parent, ctx, args, role, wantAsync, files, depen
|
|
|
258
256
|
} else {
|
|
259
257
|
tools = parent.tools
|
|
260
258
|
}
|
|
259
|
+
// Then the depth>0 exclusion (TOOLS.md §6.16): role selection above, exclusion LAST — both
|
|
260
|
+
// branches converge here (`question` = interactive main-session tool; a child has no user to ask).
|
|
261
|
+
tools = excludeSubagentTools(tools)
|
|
261
262
|
|
|
262
263
|
// PROMPT-SYSTEM 施工② G3(2026-09-10):overlay(人格)装载随装配改造退役——人格槽
|
|
263
264
|
// 由 assemblePrompt 的 D1 场景表按 role 承载(persona-{role},explore/coder/plan =
|
|
@@ -312,7 +313,7 @@ export function buildSpawnChild(parent, ctx, args, role, wantAsync, files, depen
|
|
|
312
313
|
// 每个 child 原先各自一份永不压缩的 _fullHistory(勘察 C2 乘数面)。
|
|
313
314
|
child._historyWindow = RECORD_WINDOW_MESSAGES
|
|
314
315
|
|
|
315
|
-
// §2
|
|
316
|
+
// BATCH-RECORD.md §2 批次档段写入通道绑定(第 4 批):工程角色(eng-coder/eng-designer)把批次档
|
|
316
317
|
// 绝对路径记在 child 上——setup 挂载 batch_segment 时读它。无路径参数的工具靠这条
|
|
317
318
|
// 绑定决定目标档(spawn 门已保证「参数在 + 路径可读」)。
|
|
318
319
|
if (engineeringRole) child._batchDoc = batchDocAbs
|
package/agent-tools/subagent.mjs
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* 2026-09-03 拆分轮: subagent.mjs 超 500 硬顶——async 常量、共享 post-spawn 管线
|
|
7
7
|
*(runChildPipeline)与队列/注入/并账机械迁至 ./subagent-async.mjs。execute
|
|
8
8
|
*(async 分支 + 阻塞路径)原样保留于本文件;导出面由文末 re-export shim 兜住。
|
|
9
|
-
* 2026-09-03 §
|
|
9
|
+
* 2026-09-03 合体轮(AGENT-LOOP-SUBAGENT.md §6.7): subagent_check/escalate 工具退役——status/escalate
|
|
10
10
|
* 动作执行器并入 ./subagent-async.mjs,本文件只承载工具面(action schema)与
|
|
11
11
|
* spawn 路径 + 动作分流。
|
|
12
|
-
* 2026-09-06
|
|
12
|
+
* 2026-09-06 删 check 轮(AGENT-LOOP-SUBAGENT.md §6.7.5): check 动作删除——工具面五动作(spawn/status/
|
|
13
13
|
* escalate/cancel/panel)——async 结果仅自动通道送达。
|
|
14
14
|
* 2026-09-05 拆分轮: status/escalate/panel 动作执行器 → ./subagent-actions.mjs;§20
|
|
15
15
|
* 调度器全套 → ./subagent-scheduler.mjs——本文件 import 源随之改写。
|
|
@@ -100,15 +100,15 @@ export function buildSyncStoppedReport(role, capturedOutput, designId) {
|
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
|
-
* subagent tool — ONE tool, EIGHT actions (AGENT-LOOP.md §
|
|
103
|
+
* subagent tool — ONE tool, EIGHT actions (AGENT-LOOP-SUBAGENT.md §6.7 +
|
|
104
104
|
* SUBAGENT-OBSERVE-SEND): spawn (default) / status (non-blocking pool query) / observe
|
|
105
105
|
* (inspect a running/queued/done async child's recent activity + current tool — §7.2) /
|
|
106
106
|
* send (inject a direction into a RUNNING async child — consumed at its next turn
|
|
107
107
|
* boundary as an ordinary instruction — §7.2) / escalate (飞刀 — hand implementation to
|
|
108
|
-
* a stronger model) / cancel (stop ONE background subagent — §
|
|
109
|
-
* the live subagent panel — §
|
|
108
|
+
* a stronger model) / cancel (stop ONE background subagent — AGENT-LOOP-SUBAGENT.md §6.7.2) / panel (view + fix
|
|
109
|
+
* the live subagent panel — AGENT-LOOP-SUBAGENT.md §6.7.2) / consume-design (parent-side chain-terminal token
|
|
110
110
|
* consumption — ENGINEERING-MODE.md §2.6, 2026-09-07). The check
|
|
111
|
-
* action was deleted (§
|
|
111
|
+
* action was deleted (AGENT-LOOP-SUBAGENT.md §6.7.5): async results reach the model only via the auto channel.
|
|
112
112
|
* - action:"spawn" roles: "explore" — read-only tools, search/read/analyze
|
|
113
113
|
* (suitable for codebase exploration); "coder" — full tool set, self-contained
|
|
114
114
|
* implementation tasks; "plan" — read-only planning; "eng-coder" —
|
|
@@ -174,20 +174,20 @@ export const subagentTool = {
|
|
|
174
174
|
sideEffectExempt: true, // child agent may write files; parent can't introspect its _mutatedThisRun
|
|
175
175
|
parallel: true,
|
|
176
176
|
async execute(args, ctx) {
|
|
177
|
-
// §
|
|
177
|
+
// AGENT-LOOP-SUBAGENT.md §6.7 action dispatch: default spawn keeps every legacy call unchanged
|
|
178
178
|
// (no action parameter → the spawn path below, byte-identical semantics).
|
|
179
179
|
const action = args?.action !== undefined && args?.action !== null && String(args.action) !== ""
|
|
180
180
|
? String(args.action)
|
|
181
181
|
: "spawn"
|
|
182
182
|
if (action !== "spawn") {
|
|
183
|
-
// §
|
|
183
|
+
// AGENT-LOOP-SUBAGENT.md §6.7 restricted-variant action gate (round2 #3): the engineering-child channel
|
|
184
184
|
// (depth>0, role eng-coder or eng-designer) is spawn-only — escalate spawns a
|
|
185
185
|
// coder+WRITE child (violates explore-only intent) and status/panel/observe/send
|
|
186
186
|
// have no async pool / panel mirror to query in a child context.
|
|
187
187
|
if ((ctx.depth ?? 0) > 0 && (ctx.agent?._role === "eng-coder" || ctx.agent?._role === "eng-designer")) {
|
|
188
188
|
throw new Error(`only action:'spawn' (sync explore children) is available inside an ${ctx.agent._role} — escalate/status/cancel/panel/consume-design/observe/send are not`)
|
|
189
189
|
}
|
|
190
|
-
// §
|
|
190
|
+
// AGENT-LOOP-ASYNC-POOL.md §6.8 N3/D-S6 spawn gate (manual tier): auto-turn digests may not spawn —
|
|
191
191
|
// async OR blocking — the digest must stay organize-only. The escalate
|
|
192
192
|
// action spawns a write child too, so the same mechanical refusal applies
|
|
193
193
|
// (AUTO tier exempt — user authorized unattended continuation).
|
|
@@ -196,14 +196,14 @@ export const subagentTool = {
|
|
|
196
196
|
}
|
|
197
197
|
if (action === "status") return executeStatusAction(args, ctx)
|
|
198
198
|
if (action === "escalate") return await executeEscalateAction(args, ctx)
|
|
199
|
-
// §
|
|
199
|
+
// AGENT-LOOP-SUBAGENT.md §6.7.2 控制类动作:digest 内放行(D-S7 分类——控制/自省;dispatch 控制类
|
|
200
200
|
// 豁免同批生效——19.5.2b round2 #4;escalate 的 digest 拒绝在上一分支)
|
|
201
201
|
if (action === "cancel") return executeCancelAction(args, ctx)
|
|
202
202
|
// 2026-09-07 token 链终消费制(ENGINEERING-MODE.md §2.6 F1):父侧核销消费——
|
|
203
203
|
// 非只读控制动作——depth-0 + 工程模式限定(本分流已过受限变体门;工程模式门在
|
|
204
204
|
// 执行器内)——planMode 拒绝(dispatch 不豁免)——不入批审批分组(dispatch 免审)。
|
|
205
205
|
if (action === "consume-design") return executeConsumeDesignAction(args, ctx)
|
|
206
|
-
// §
|
|
206
|
+
// AGENT-LOOP-SUBAGENT.md §6.7.2 panel 动作:view(readonly 面——digest 内放行——自省类)与 freeze
|
|
207
207
|
// (控制类——同 cancel——digest 内放行)。深度/门控检查在 executePanelAction 内。
|
|
208
208
|
// CLI-ACTIVITY-DEBLOAT F-3(2026-09-10)接线:executePanelAction 经 ctx.state
|
|
209
209
|
// (= agent._tuiState——startTUI 反向挂载)读时现算面板块(computePanelBlocks)——
|
|
@@ -263,7 +263,7 @@ export const subagentTool = {
|
|
|
263
263
|
throw new Error("Engineering mode is not active — role='eng-designer' is engineering-mode only (it writes the requirements/design documents inside the engineering workflow); use role='explore' or role='plan' for read-only work.")
|
|
264
264
|
}
|
|
265
265
|
|
|
266
|
-
// §
|
|
266
|
+
// AGENT-LOOP-ASYNC-POOL.md §6.8 N3/D-S6 spawn gate (manual tier): auto-turn digests may not spawn — async
|
|
267
267
|
// OR blocking — the digest must stay organize-only. AUTO tier (autoApprove) is
|
|
268
268
|
// exempt (推进型 — user authorized unattended continuation). Mechanical refusal
|
|
269
269
|
// so the digest never pops a permission panel or chains new background work.
|
|
@@ -397,7 +397,7 @@ export const subagentTool = {
|
|
|
397
397
|
},
|
|
398
398
|
}
|
|
399
399
|
|
|
400
|
-
// Re-export shim (2026-09-03 拆分轮 +
|
|
400
|
+
// Re-export shim (2026-09-03 拆分轮 + 合体轮 + 2026-09-05 拆分轮): 机械与动作
|
|
401
401
|
// 执行器迁至 ./subagent-async.mjs、./subagent-actions.mjs、./subagent-scheduler.mjs
|
|
402
402
|
// ——本文件保留导出面,消费点(agent.mjs / agent-turn.mjs / consult.mjs / 测试)导入
|
|
403
403
|
// 路径零改动;池逻辑/准入见 subagent-run.mjs(executeAsyncSpawn)与 subagent-scheduler.mjs
|