@thincoder/core 0.10.4 → 0.10.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 +31 -1
- package/README.md +1 -1
- package/advisor/compaction.mjs +11 -6
- package/advisor/run.mjs +4 -4
- package/agent/assemble.mjs +1 -1
- package/agent/chat-call.mjs +1 -1
- package/agent/child-marks.mjs +2 -2
- package/agent/digest-account.mjs +5 -4
- package/agent/dispatch-gates.mjs +8 -0
- package/agent/dispatch.mjs +39 -18
- package/agent/family-tools.mjs +22 -11
- package/agent/helpers.mjs +4 -3
- package/agent/light-round.mjs +66 -0
- package/agent/post-turn.mjs +8 -4
- package/agent/run-stages.mjs +5 -3
- package/agent/run-start.mjs +3 -2
- package/agent/setup-reminders.mjs +1 -1
- package/agent/setup.mjs +18 -3
- package/agent/subagent-event.mjs +1 -1
- package/agent/suspension.mjs +8 -5
- package/agent/timers.mjs +56 -5
- package/agent/turn-loop.mjs +5 -3
- package/agent/write-gate.mjs +7 -7
- package/agent-tools/advisor-async.mjs +2 -2
- package/agent-tools/advisor-settle.mjs +2 -2
- package/agent-tools/advisor.mjs +1 -1
- package/agent-tools/async-discard.mjs +34 -0
- package/agent-tools/async-settle.mjs +3 -2
- package/agent-tools/audit-block.mjs +1 -1
- package/agent-tools/bash-async.mjs +1 -1
- package/agent-tools/batch-lifecycle.mjs +35 -14
- package/agent-tools/batch-paths.mjs +4 -4
- package/agent-tools/batch-skeleton.mjs +29 -11
- package/agent-tools/batch.mjs +28 -15
- package/agent-tools/browser-async.mjs +198 -0
- package/agent-tools/checkpoint.mjs +1 -1
- package/agent-tools/design-slots.mjs +232 -0
- package/agent-tools/digest-budget.mjs +1 -1
- package/agent-tools/manifest.mjs +193 -0
- package/agent-tools/parent-channel.mjs +7 -4
- package/agent-tools/review-facts.mjs +13 -13
- package/agent-tools/settings.mjs +1 -1
- package/agent-tools/skill.mjs +1 -1
- package/agent-tools/subagent-async.mjs +21 -12
- package/agent-tools/subagent-scheduler.mjs +8 -5
- package/agent-tools/subagent-spawn.mjs +29 -55
- package/agent-tools/subagent.mjs +21 -8
- package/agent-tools/timer.mjs +186 -12
- package/agent-tools/zero-write-watch.mjs +2 -2
- package/agent-tools.mjs +2 -1
- package/agent.mjs +1 -1
- package/attachments.mjs +1 -1
- package/config-io.mjs +18 -13
- package/config-migrate.mjs +13 -29
- package/config-presets.mjs +33 -29
- package/config.mjs +68 -25
- package/context-degrade.mjs +1 -1
- package/context-echo.mjs +1 -1
- package/context-push.mjs +2 -2
- package/context.mjs +1 -1
- package/conventions.mjs +2 -2
- package/declaration.mjs +18 -2
- package/file-links.mjs +1 -1
- package/file-refs.mjs +1 -1
- package/history-window.mjs +1 -1
- package/i18n.mjs +2 -0
- package/ledger-cmd.mjs +62 -16
- package/ledger-db.mjs +6 -0
- package/ledger-surface.mjs +49 -5
- package/ledger-tools.mjs +16 -8
- package/ledger.mjs +9 -16
- package/log.mjs +1 -1
- package/manifest-discovery.mjs +1 -1
- package/manifest-schema.mjs +1 -1
- package/mcp/transport-stdio.mjs +1 -1
- package/mcp.mjs +1 -1
- package/memory/code-index.mjs +1 -1
- package/memory/code-search.mjs +1 -1
- package/memory/code-sync.mjs +1 -1
- package/memory/docs.mjs +6 -2
- package/memory/file-list.mjs +1 -1
- package/memory/file-walk.mjs +17 -5
- package/memory/maintain.mjs +410 -0
- package/memory/memory-tool.mjs +21 -6
- package/memory/schema.mjs +19 -7
- package/memory/sweep.mjs +8 -5
- package/memory/sync-tail.mjs +16 -10
- package/model-ref.mjs +16 -13
- package/model-specs.mjs +21 -9
- package/package.json +1 -1
- package/peer-instances.mjs +1 -1
- package/permission.mjs +33 -0
- package/process-probe.mjs +2 -2
- package/prompts/advisor-design.md +4 -2
- package/prompts/advisor-round1.md +1 -1
- package/prompts/advisor-round2.md +1 -1
- package/prompts/advisor-round3.md +1 -1
- package/prompts/common.md +36 -2
- package/prompts/consult-base.md +1 -1
- package/prompts/discipline-engineering.md +32 -15
- package/prompts/discipline-normal.md +46 -2
- package/prompts/persona-eng-designer.md +6 -6
- package/prompts/persona-engineering.md +14 -6
- package/prompts/persona-normal.md +2 -0
- package/prompts/subagent-base.md +3 -0
- package/provider/core.mjs +6 -0
- package/provider/errors.mjs +5 -3
- package/provider/google.mjs +9 -8
- package/provider/list-models.mjs +3 -2
- package/provider/sse.mjs +3 -0
- package/provider-flows.mjs +14 -15
- package/proxy-chunked.mjs +102 -0
- package/proxy-target.mjs +54 -0
- package/proxy-transport.mjs +298 -0
- package/proxy.mjs +17 -248
- package/session-gc.mjs +2 -2
- package/session-lifecycle.mjs +18 -14
- package/session-slot-claims.mjs +2 -2
- package/session-slots-manifest.mjs +1 -1
- package/session-slots.mjs +3 -3
- package/team-ledger.mjs +282 -0
- package/team.mjs +223 -0
- package/tool-docs/bash.md +1 -0
- package/tool-docs/batch.md +1 -1
- package/tool-docs/browser.md +48 -0
- package/tool-docs/ledger.md +3 -3
- package/tool-docs/manifest.md +16 -0
- package/tool-docs/memory.md +1 -0
- package/tool-docs/notify_parent.md +5 -0
- package/tool-docs/process.md +4 -4
- package/tool-docs/question.md +1 -0
- package/tool-docs/subagent.md +4 -3
- package/tool-docs/team_ledger.md +9 -0
- package/tool-docs/timer.md +6 -0
- package/tool-docs/wait_for.md +1 -0
- package/tools/browser.mjs +161 -0
- package/tools/git.mjs +1 -1
- package/tools/index.mjs +3 -0
- package/tools/ops.mjs +26 -10
- package/tools/repomap.mjs +1 -1
- package/tools/write-path.mjs +2 -2
- package/traces/trace-cleanup.mjs +1 -1
- package/traces/trace-store.mjs +1 -1
- package/vision-reader.mjs +10 -16
package/agent/suspension.mjs
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* 载体注入(端差面 = 池 / pending / 标志挂谁):`ctx.carrier` = 字段集按核内异步面
|
|
12
12
|
* 现行口径的对象(`_asyncSubagents` · `_asyncAdvisors` · `_consultSessions` ·
|
|
13
|
-
* `_pendingAsyncResults` · `_suspended`;#9 后台 bash 任务池 `_bgTasks` 同列;
|
|
13
|
+
* `_pendingAsyncResults` · `_suspended`;#9 后台 bash 任务池 `_bgTasks` ∥ 批 browser-async-fix 池 `_browserTasks` 同列;
|
|
14
14
|
* 消化账务批 · 2026-10-05 同列 `_daSession`(挂起会话标记——置位 / 复位单点)与
|
|
15
15
|
* `_unsettledDigests`(升级账本——中止清场))
|
|
16
16
|
* ——CLI 形 = `agent` 对象;VSC 形 = depth-0
|
|
@@ -59,12 +59,14 @@ function consultRunningChildren(carrier) {
|
|
|
59
59
|
/** 后台池存活判据(D-S2/F5 口径——CLI 为准):running/queued 的池条目,或已 settle 未注入
|
|
60
60
|
* (pending 非空 = D-S3「未注入」),或运行中 consult children(会诊跨回合)。回合尾与每次
|
|
61
61
|
* 轮末都评估退出。**#9 三域并入**:`_bgTasks`(后台 bash 任务)在途亦为 live——settle 经
|
|
62
|
-
* pending 停靠驱动消化轮(`bash-async.mjs`
|
|
62
|
+
* pending 停靠驱动消化轮(`bash-async.mjs` 恒停靠);**批 browser-async-fix 第四域**:
|
|
63
|
+
* `_browserTasks` 同判(`browser-async.mjs` 恒停靠)。 */
|
|
63
64
|
export function poolLive(carrier) {
|
|
64
65
|
const sub = carrier?._asyncSubagents
|
|
65
66
|
const adv = carrier?._asyncAdvisors
|
|
66
67
|
const bg = carrier?._bgTasks
|
|
67
|
-
|
|
68
|
+
const br = carrier?._browserTasks
|
|
69
|
+
return (sub && sub.size > 0) || (adv && adv.size > 0) || (bg && bg.size > 0) || (br && br.size > 0)
|
|
68
70
|
|| (carrier?._pendingAsyncResults?.length ?? 0) > 0
|
|
69
71
|
|| consultRunningChildren(carrier) > 0
|
|
70
72
|
}
|
|
@@ -111,7 +113,7 @@ function consultLiveCount(carrier) {
|
|
|
111
113
|
}
|
|
112
114
|
|
|
113
115
|
/**
|
|
114
|
-
* 退出清场(单点——驱动 finally
|
|
116
|
+
* 退出清场(单点——驱动 finally 与单元测试共用):
|
|
115
117
|
* - abort:**只清已死**(§6.20 口径——增补 D:`discardAbortedPool` ∕ `discardAbortedAdvisors`
|
|
116
118
|
* 单点,与核 run-stages 回合尾中止同式;存活 ∕ 已 settle 条目留池——settled 报告沿后续
|
|
117
119
|
* 回合边界注入到达,不静默丢)+ pending 单容器清 + 会诊会话清理标记;
|
|
@@ -128,10 +130,11 @@ function consultLiveCount(carrier) {
|
|
|
128
130
|
*/
|
|
129
131
|
export async function finishSuspension(carrier, { aborted = false, injectResidual = null } = {}) {
|
|
130
132
|
if (aborted) {
|
|
131
|
-
const { discardAbortedPool, discardAbortedAdvisors, discardAbortedBgTasks } = await import("../agent-tools/async-discard.mjs")
|
|
133
|
+
const { discardAbortedPool, discardAbortedAdvisors, discardAbortedBgTasks, discardAbortedBrowserTasks } = await import("../agent-tools/async-discard.mjs")
|
|
132
134
|
discardAbortedPool(carrier)
|
|
133
135
|
discardAbortedAdvisors(carrier)
|
|
134
136
|
discardAbortedBgTasks(carrier) // #9 收尾档②:后台 bash 任务逐条杀树 + 出池 + 墓碑(杀 ⟺ 控制器已中止)
|
|
137
|
+
discardAbortedBrowserTasks(carrier) // 批 browser-async-fix:后台浏览器动作同面(在飞中止 + 出池 + 墓碑——不杀浏览器)
|
|
135
138
|
carrier._pendingAsyncResults = []
|
|
136
139
|
carrier._unsettledDigests = [] // §6.31.5 清账面(消化账务批):会话中止 ⇒ 升级账本随 pending 一并清(旧账不续)
|
|
137
140
|
if (carrier._consultSessions instanceof Map) {
|
package/agent/timers.mjs
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
* agent/timers.mjs — timer 到期件(载体①:在途 timer 的单一权威读面)+ 空闲唤醒机制(闩 ∕ 派发 ∕ 火策略)。
|
|
3
3
|
*
|
|
4
4
|
* 机制单源 = `docs/core/design/AGENT-LOOP-ASYNC-POOL.md` §6.30.2(载体定形 D-TW1):
|
|
5
|
-
*
|
|
5
|
+
* 读面纯函数住本档——最近到期时点 / 到期出列(幂等 · 含周期重排)/ 历史注入单点 / 下一到期时点
|
|
6
|
+
* (`nextFireAt`——§6.30.18)。消费面三处共用
|
|
6
7
|
* 同一读点(零第二份过滤语义):① 回合边界轮询(`agent/post-turn.mjs`——行为零变);
|
|
7
8
|
* ② 空闲闩 deadline;③ 挂起窗 deadline;显示面(状态行标记 / `/timers`)同读源。
|
|
8
9
|
*
|
|
9
|
-
* 载体仍在 agent 字段 `_pendingTimers`(条目形 `{ id, expiresAt, message }
|
|
10
|
-
* timer 工具 `thincoder-core/agent-tools/timer.mjs
|
|
10
|
+
* 载体仍在 agent 字段 `_pendingTimers`(条目形 `{ id, expiresAt, message }` + 可选 `everyMs` ∥ `daily`——§6.30.18;
|
|
11
|
+
* 绝对时刻条目另携 `at` 来源标记位(非载重——父侧裁定 2026-10-12);一次性条目形逐字零变);写点两处 = timer 工具 `thincoder-core/agent-tools/timer.mjs`
|
|
12
|
+
* (设 ∕ 取消)+ 本档 `takeExpiredTimers` 内**到点重排**(出列+重排原子——§6.30.18 D-TW10);
|
|
13
|
+
* 跨 run 存活 = 规范语义(D-TW3,无 per-run 复位)。
|
|
11
14
|
*
|
|
12
15
|
* 空闲唤醒族(2026-09-28 流程批 R4 **上提核件** · 纯搬 + 转口 · 零语义改;上提源 = VSC
|
|
13
16
|
* `thincoder-vscode/src/extension/timer-watch.mjs:46` ∕ `:73` ∕ `:94`——as-of 2026-09-28 R4 读数;两端现址(B3 收编后,实读 2026-09-29)=
|
|
@@ -21,6 +24,13 @@
|
|
|
21
24
|
* 形态先例 = 队满「拒 + 提示 + 保留」)。与 CLI 排队容量 `QUEUED_MAX_ITEMS`(8)同值同形。 */
|
|
22
25
|
export const TIMER_MAX_PENDING = 8
|
|
23
26
|
|
|
27
|
+
/** `every` 间隔下限(毫秒——§6.30.18 D-TW16):工具面**硬拒**低于此值(不静默钳制);
|
|
28
|
+
* 核重排面不设界(防御 = 非有限正数 ⇒ `nextFireAt` 返 `null` 照消费——下限校验在工具面)。 */
|
|
29
|
+
export const TIMER_MIN_EVERY_MS = 60_000
|
|
30
|
+
|
|
31
|
+
/** 日历式 `HH:MM` 形判据(防御面——工具面为唯一生产者,两处同形)。 */
|
|
32
|
+
const DAILY_RE = /^(?:[01]\d|2[0-3]):[0-5]\d$/
|
|
33
|
+
|
|
24
34
|
/** 最近到期时点(毫秒)或 `null`(空在途)——端闩 / 挂起窗 deadline / 显示面共用同源(每帧活读)。 */
|
|
25
35
|
export function pendingTimerDeadline(agent) {
|
|
26
36
|
const timers = agent?._pendingTimers ?? []
|
|
@@ -33,13 +43,54 @@ export function pendingTimerDeadline(agent) {
|
|
|
33
43
|
return min
|
|
34
44
|
}
|
|
35
45
|
|
|
46
|
+
/** 下一到期时点(毫秒)或 `null`(无重排)——§6.30.18「载体扩展 ∥ 重排」单源:
|
|
47
|
+
* ① 固定间隔(`everyMs`):基准 = `entry.expiresAt`(首设刻);`k = max(1, floor((now − base) / everyMs) + 1)`;
|
|
48
|
+
* 返 `base + k × everyMs`——**零漂移** ∥ 错过任意拍直落下一格(**跳过不补投**——一次出列对一条至多一投);
|
|
49
|
+
* ② 日历式(`daily` = `HH:MM`):宿主本地**严格 `> now`** 的下一 occurrence(今日该刻未过 ⇒ 今日,已过 ⇒
|
|
50
|
+
* 明日;秒位归零——DST 按宿主日历规则走,不作特例);
|
|
51
|
+
* ③ 一次性 ∥ 绝对时刻(无两字段)⇒ `null`;守卫:`everyMs` 非有限正数 ∥ 基准非有限 ∥ `daily` 形不合
|
|
52
|
+
* ⇒ `null`(照消费——防御面;下限校验在工具面)。
|
|
53
|
+
* 首设换算同函数:`every` ⇒ `nextFireAt({ everyMs, expiresAt: now }, now)`(= 设时 + N)∥ `daily` ⇒
|
|
54
|
+
* `nextFireAt({ daily }, now)`;`at` ⇒ 直解算(不经本函数)。 */
|
|
55
|
+
export function nextFireAt(entry, now) {
|
|
56
|
+
const everyMs = entry?.everyMs
|
|
57
|
+
if (everyMs !== undefined && everyMs !== null) {
|
|
58
|
+
const base = entry.expiresAt
|
|
59
|
+
if (typeof everyMs !== "number" || !Number.isFinite(everyMs) || everyMs <= 0) return null
|
|
60
|
+
if (typeof base !== "number" || !Number.isFinite(base)) return null
|
|
61
|
+
const k = Math.max(1, Math.floor((now - base) / everyMs) + 1)
|
|
62
|
+
return base + k * everyMs
|
|
63
|
+
}
|
|
64
|
+
const daily = entry?.daily
|
|
65
|
+
if (daily !== undefined && daily !== null) {
|
|
66
|
+
if (typeof daily !== "string" || !DAILY_RE.test(daily)) return null
|
|
67
|
+
const [hh, mm] = daily.split(":").map(Number)
|
|
68
|
+
const at = new Date(now)
|
|
69
|
+
at.setHours(hh, mm, 0, 0) // 秒位归零(该刻整分投递)
|
|
70
|
+
if (at.getTime() <= now) {
|
|
71
|
+
at.setDate(at.getDate() + 1) // 已过 ⇒ 明日(跨日 = `Date` 日历运算承载——零特例代码)
|
|
72
|
+
at.setHours(hh, mm, 0, 0) // 跨日归一后再归零(DST 走宿主日历规则)
|
|
73
|
+
}
|
|
74
|
+
return at.getTime()
|
|
75
|
+
}
|
|
76
|
+
return null
|
|
77
|
+
}
|
|
78
|
+
|
|
36
79
|
/** 到期出列(**幂等**):取走 `expiresAt <= now` 的条目并返回(在途表内移除)——二次调用零返
|
|
37
|
-
* (§6.30.3 成本闸②「到期即消费」——绝不重复投递)。未到期项原序保留。
|
|
80
|
+
* (§6.30.3 成本闸②「到期即消费」——绝不重复投递)。未到期项原序保留。
|
|
81
|
+
* 携 `everyMs`/`daily` 的到期项**同 id 克隆回插**(新 `expiresAt = nextFireAt(t, now)`,严格 `> now`——
|
|
82
|
+
* §6.30.18 重排:出列+重排原子 ∥ 净零不触帽;重排先于交付 ⇒「到点即重排」与「交付幂等」互不依赖);
|
|
83
|
+
* 一次性 ∥ 绝对时刻照旧被消费(无重排)。 */
|
|
38
84
|
export function takeExpiredTimers(agent, now = Date.now()) {
|
|
39
85
|
const timers = agent?._pendingTimers ?? []
|
|
40
86
|
const expired = timers.filter((t) => t.expiresAt <= now)
|
|
41
87
|
if (expired.length === 0) return []
|
|
42
|
-
|
|
88
|
+
const rest = timers.filter((t) => t.expiresAt > now)
|
|
89
|
+
for (const t of expired) {
|
|
90
|
+
const next = nextFireAt(t, now)
|
|
91
|
+
if (next !== null) rest.push({ ...t, expiresAt: next })
|
|
92
|
+
}
|
|
93
|
+
agent._pendingTimers = rest
|
|
43
94
|
return expired
|
|
44
95
|
}
|
|
45
96
|
|
package/agent/turn-loop.mjs
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
|
|
23
23
|
export async function runTurnLoop(agent, {
|
|
24
24
|
maxTurns, threshold, toolSchemas, toolByName, systemPrompt,
|
|
25
|
-
depth, signal, autoTurn, streamOutput,
|
|
25
|
+
depth, signal, autoTurn, suspDriven, streamOutput,
|
|
26
26
|
consumeInjected, consumeQueuedInput, callbacks, distillSignal,
|
|
27
27
|
}) {
|
|
28
28
|
let guardPushbacks = 0
|
|
@@ -105,7 +105,9 @@ export async function runTurnLoop(agent, {
|
|
|
105
105
|
// consumeInjected 回调承载(异步子代理专属——缺省 null:主会话/阻塞子代理零开销)。
|
|
106
106
|
consumeInjected?.(agent)
|
|
107
107
|
// 子代理在飞消息(子 → 父;§6.27):空队列 no-op;非空 ⇒ 恰一条合并 user 消息注入。
|
|
108
|
-
|
|
108
|
+
// 批 async-delivery-formalize(§6.34.2 行 2):驱动面收窄「交付轮轮首」(`turn === 0` ∧ `autoTurn`——
|
|
109
|
+
// 用户回合零 drain);回退面(`!suspDriven`)逐字保持(每轮每步)。
|
|
110
|
+
if (!suspDriven || (turn === 0 && autoTurn === true)) drainChildUpstream(agent)
|
|
109
111
|
// ③ 用户 → 主会话投递(步边界 pickup——queue-visible 批 fix 轮 2026-09-24 · F16):循环头
|
|
110
112
|
// 回合边界缝(核只给缝、不给策略——取批 / pushReal / 呈现归端侧闭包);缺省 null,系统轮不传
|
|
111
113
|
// (`AGENT-LOOP-ASYNC-POOL.md` §6.8「步边界 pickup」· 适用面 = 用户回合)。
|
|
@@ -260,7 +262,7 @@ export async function runTurnLoop(agent, {
|
|
|
260
262
|
// Commit tool results (pairing, multimodal deferral, mutation accounting, reindex)
|
|
261
263
|
await recordToolResults(agent, toolByName, results)
|
|
262
264
|
|
|
263
|
-
injectPostTurn(agent, results, recentCallSigs, callbacks, turn)
|
|
265
|
+
injectPostTurn(agent, results, recentCallSigs, callbacks, turn, { suspDriven })
|
|
264
266
|
}
|
|
265
267
|
|
|
266
268
|
// #417(撞帽载荷「本段零落盘轮数」):收尾轮(撞帽轮)若零落盘同样计入——轮顶计数只
|
package/agent/write-gate.mjs
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* M6 的 `advisor.mjs` design-review 分类分支消费本导出
|
|
19
19
|
* (KD-M6-1——同源不重复实现;落 dispatch.mjs 会让 advisor 反向 import 门禁簇成环)。
|
|
20
20
|
* 2. `freezeWindowConflict(agent, absPaths)` —— D5 冻结窗口判据组装:被审文件集 =
|
|
21
|
-
* 声明文档集(`inflightDesignReviewConflict` 同源 docAbs
|
|
22
|
-
*
|
|
21
|
+
* 声明文档集(`inflightDesignReviewConflict` 同源 docAbs 分支)+ 批次档(`run.batchDoc`
|
|
22
|
+
* 分支——评审绑定批档在途时,写批档同样致 stale)。dispatch.mjs
|
|
23
23
|
* 只 import 消费,不再各自直连 `advisor-settle.mjs`。
|
|
24
24
|
* 3. `batchRecordWriteConflict(agent, depth, absPaths)` —— 批次档写门判据组装(#309 ·
|
|
25
25
|
* `AGENT-LOOP-SUBAGENT.md` §6.29.1):判据与拒绝文案同址(文案字面只出本档)。
|
|
@@ -62,9 +62,9 @@ export function resolveReviewTargetPaths(agent) {
|
|
|
62
62
|
/**
|
|
63
63
|
* D5 冻结窗口判据组装(M4 §2.1#2):评审在途(点火 → 结算)期间父侧对被审文件集
|
|
64
64
|
* (声明文档集 + 批次档)的写入 → 返回冲突 { id, path };无冲突 → null。
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* 同样致 stale
|
|
65
|
+
* 声明文档集分支 = `inflightDesignReviewConflict`(同 docAbs / 同 normAbs,仅 running
|
|
66
|
+
* 未取消的设计条目);批次档分支 = 同池条目 `run.batchDoc`(评审绑定批档在途时写批档
|
|
67
|
+
* 同样致 stale——被审文件集合流点)。两分支同形返回,消费端拒绝文案零改。
|
|
68
68
|
* @param {Object} agent
|
|
69
69
|
* @param {string[]} absPaths — 本次写将触碰的路径(ABS——调用方 resolve(agent.cwd, p))
|
|
70
70
|
* @returns {{id: string, path: string}|null} 命中评审 id + 冲突路径(ABS),无冲突 null
|
|
@@ -132,10 +132,10 @@ function isOwnBatchCompanion(targetKey, boundKey) {
|
|
|
132
132
|
/**
|
|
133
133
|
* 批次档写门判据(#309 · `docs/core/design/AGENT-LOOP-SUBAGENT.md` §6.29.1——containment;
|
|
134
134
|
* 2026-09-29 收窄 · 台账 #545):
|
|
135
|
-
*
|
|
135
|
+
* 携绑定的子代理(`depth > 0` ∧ `agent._batchDoc` 在场——工程角色 ∥ 普通面 coder,`BATCH-RECORD.md` §4.16)对**批次档文件**的写 ⇒ 目标 ≠ 绑定档
|
|
136
136
|
* **且非绑定批次的伴随件**(见 `isOwnBatchCompanion`)⇒ 冲突(fail-closed;写自己绑定的档 ∥
|
|
137
137
|
* 自身批次伴随件 = 正常面放行)。比较键 = 绝对路径 + win32 大小写归一。
|
|
138
|
-
* 界面:depth 0(主 agent——跨档处置是父侧职责)· 无 `_batchDoc
|
|
138
|
+
* 界面:depth 0(主 agent——跨档处置是父侧职责)· 无 `_batchDoc`(未携绑定族——普通面 coder 亦入绑定面)· 主基底外的
|
|
139
139
|
* 普通文件写 —— 三种形态均不进本门(返回 null,行为零变)。判据集由调用方限定 = `FILE_MUTATORS`
|
|
140
140
|
* (`file_ops` / 读类不在门内——§6.29.1 边界)。
|
|
141
141
|
* 拒绝文案**单源 = 本档**(两端直取 `message`——零字面副本)。
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
* non-stale code review marks _calledAdvisorThisRun; a passing design review
|
|
32
32
|
* issues its token under the designId slot at settle time.
|
|
33
33
|
* 2026-09-11 第 11 批拆分:settle 记账 + 变更日志 + 陈旧/冻结判定迁 `advisor-settle.mjs`
|
|
34
|
-
* (本文件 500
|
|
34
|
+
* (本文件 500 行——2026-09-11 拆分时);既有 import 面经本文件 re-export 保持不变。
|
|
35
35
|
*
|
|
36
36
|
* The sync path (depth>0 eng-coder self-review / explicit async:false) reuses
|
|
37
37
|
* the same instance resolution (rounds/prior/cap) — its accounting still lands
|
|
@@ -392,7 +392,7 @@ export function launchAsyncAdvisor(parent, ctx, launch) {
|
|
|
392
392
|
id, role: "advisor", reviewType, run,
|
|
393
393
|
reviewId: run.reviewId, designId, designToken, documents, paths, object,
|
|
394
394
|
launchSeq: mutationSeqOf(parent),
|
|
395
|
-
// docAbs 产出面同源归一(2026-10-04 · #921 · A
|
|
395
|
+
// docAbs 产出面同源归一(2026-10-04 · #921 · A 随件):四分支单源 resolved(裸形/前缀形
|
|
396
396
|
// 命中候选项目根即归一为绝对路径)——冻结窗/陈旧判定对**裸形受理档**照护成立(否则 A
|
|
397
397
|
// 开冻结窗保护洞);绝对形与旧 normAbs 面逐字等价。
|
|
398
398
|
docAbs: reviewType === "design" && Array.isArray(documents)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* advisor-settle.mjs — advisor settle accounting + mutation log + stale / freeze
|
|
3
|
-
* determination(2026-09-11 第 11 批自 advisor-async.mjs
|
|
4
|
-
*
|
|
3
|
+
* determination(2026-09-11 第 11 批自 advisor-async.mjs 拆出;
|
|
4
|
+
* 既有 import 面经 advisor-async.mjs **re-export 保持不变**
|
|
5
5
|
* (noteMutations / mutationSeqOf / reviewIsStale / settleAdvisorRun)。
|
|
6
6
|
*
|
|
7
7
|
* 内容 = 迁入(逐字搬移含注释)+ 本批两处接线:
|
package/agent-tools/advisor.mjs
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
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(声明面)——单一权威源 =
|
|
13
|
-
// review-facts.mjs 的 resolveReviewDocPaths(2026-10-04 · #921 · KD-2
|
|
13
|
+
// review-facts.mjs 的 resolveReviewDocPaths(2026-10-04 · #921 · KD-2——分类四分支 + B 文案
|
|
14
14
|
// 诊断数据单源;write-gate 同名再出口保 normAbs 面)。不 import dispatch.mjs(簇间回边,环风险)。
|
|
15
15
|
import { normAbs } from "../agent/write-gate.mjs"
|
|
16
16
|
import { resolveReviewDocPaths } from "./review-facts.mjs"
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
* ——其控制器链只作**判据**(杀 ⟺ 控制器已中止),杀树动作经本收尾面 `spec.dispose` 钩子落
|
|
18
18
|
* `bash-async.mjs` 杀单点(`killBgTree`);回合中断(Ctrl+I)经 `bindChildController`
|
|
19
19
|
* interrupt 豁免不中止控制器 ⇒ 本面零动作(F2 同款豁免——池保留)。
|
|
20
|
+
* 批 browser-async-fix(2026-10-07):browser 后台动作族同面并入(`BROWSER_SPEC`——dispose = 条目
|
|
21
|
+
* 控制器 abort ⇒ 动作展开 / 排队丢队;**不杀浏览器**)。
|
|
20
22
|
*
|
|
21
23
|
* 判据口径(D-AD6):两接线点**均不传 `ctx`** ⇒ 实走 controller 支(`parentAborted(null, entry)`)
|
|
22
24
|
* ——与对侧有效判据同判(对侧传入的 `ctx` 为死参);`ctx` 保留为签名备用面(直调用例 / 对侧收敛)。
|
|
@@ -59,6 +61,11 @@ const bgReminderText = (n, list) =>
|
|
|
59
61
|
`[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
62
|
"Full output logs remain on disk (paths were given in the start ack).]"
|
|
61
63
|
|
|
64
|
+
/** browser 后台动作族提醒模板(同族形态——取消无摘要:终止凭据 = 墓碑 + 杀点确认;浏览器本体不杀)。 */
|
|
65
|
+
const browserReminderText = (n, list) =>
|
|
66
|
+
`[System reminder: ${n} background browser task(s) were cancelled by the user's Stop — no digest will arrive: ${list}. ` +
|
|
67
|
+
"The browser session itself was not killed (run `close` to reset it).]"
|
|
68
|
+
|
|
62
69
|
/** 列表词(wasStatus 数据源):queued → "(was queued — never started)";其余 "(was running)"。 */
|
|
63
70
|
const wasPhrase = (wasStatus) => (wasStatus === "queued" ? " (was queued — never started)" : " (was running)")
|
|
64
71
|
|
|
@@ -151,6 +158,21 @@ const BG_SPEC = {
|
|
|
151
158
|
},
|
|
152
159
|
}
|
|
153
160
|
|
|
161
|
+
/** browser 后台动作族 spec(批 browser-async-fix——收尾档同面):dispose = 中止在飞(条目控制器
|
|
162
|
+
* abort ⇒ running 动作展开 / queued 丢队从未运行)+ 标记 discarded(settle 面据此不注入——摘要不可达);
|
|
163
|
+
* 无队列面(帽 = 起跑显式拒)。**不杀浏览器**(会话重置是独立动作 `close`)。 */
|
|
164
|
+
const BROWSER_SPEC = {
|
|
165
|
+
pool: "browser",
|
|
166
|
+
describe: (entry) => ({ id: entry.id, role: "browser", action: entry.action }),
|
|
167
|
+
roleOf: () => "browser",
|
|
168
|
+
listPhrase: (d) => `browser#${d.id} (${d.action})`,
|
|
169
|
+
reminder: browserReminderText,
|
|
170
|
+
dispose: (entry) => {
|
|
171
|
+
entry.discarded = true
|
|
172
|
+
entry.controller?.abort?.({ abortTrigger: "discard" })
|
|
173
|
+
},
|
|
174
|
+
}
|
|
175
|
+
|
|
154
176
|
/**
|
|
155
177
|
* 中止收尾(F1–F3):只清已死子代理条目——写 `discarded` 墓碑 + 出池 + 队列剔除 + 整批一次提醒。
|
|
156
178
|
* @param parent agent 形态(载体双形经 `getAsyncPool` / `writeTombstone` 吸收)
|
|
@@ -184,3 +206,15 @@ export function discardAbortedAdvisors(parent, ctx = null) {
|
|
|
184
206
|
export function discardAbortedBgTasks(parent, ctx = null) {
|
|
185
207
|
return discardRole(parent, BG_SPEC, ctx)
|
|
186
208
|
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* 中止收尾(browser 后台动作族——同面并入):只清已死条目——逐条中止在飞(running ⇒ 动作展开;
|
|
212
|
+
* queued ⇒ 丢队从未运行)+ `discarded` 墓碑 + 出池 + 整批一次提醒;回合中断(Ctrl+I)不中止控制器
|
|
213
|
+
* ⇒ 本面零动作(池保留——与 bg 同豁免)。
|
|
214
|
+
* @param parent agent 形态
|
|
215
|
+
* @param ctx 判据 ctx(接线点不传——controller 支,同上)
|
|
216
|
+
* @returns {{discarded: Array<{id, role: "browser", action}>, kept: number}}
|
|
217
|
+
*/
|
|
218
|
+
export function discardAbortedBrowserTasks(parent, ctx = null) {
|
|
219
|
+
return discardRole(parent, BROWSER_SPEC, ctx)
|
|
220
|
+
}
|
|
@@ -113,11 +113,12 @@ export function bindChildController(ctrl, baseSignal) {
|
|
|
113
113
|
|
|
114
114
|
/** 池 accessor(D1):role "advisor" → `_asyncAdvisors`(独立评审池——无队列独立调度)·
|
|
115
115
|
* role "consult" → `_consultSessions`(会话池)· role "bg" → `_bgTasks`(#9 后台 bash
|
|
116
|
-
* 任务池)·
|
|
116
|
+
* 任务池)· role "browser" → `_browserTasks`(批 browser-async-fix 后台浏览器动作池)·
|
|
117
|
+
* 其余 → `_asyncSubagents`(子代理/飞刀共享槽位队列池)。载体经 `carrierField`
|
|
117
118
|
* 吸收(#94);未初始化返 null——调用方以 `?? new Map()` / 可选链处置。 */
|
|
118
119
|
export function getAsyncPool(parent, role) {
|
|
119
120
|
const field = role === "advisor" ? "_asyncAdvisors" : role === "consult" ? "_consultSessions"
|
|
120
|
-
: role === "bg" ? "_bgTasks" : "_asyncSubagents"
|
|
121
|
+
: role === "bg" ? "_bgTasks" : role === "browser" ? "_browserTasks" : "_asyncSubagents"
|
|
121
122
|
return carrierField(parent, field) ?? null
|
|
122
123
|
}
|
|
123
124
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* audit-block.mjs — 审计块构造器(AGENT-LOOP-SUBAGENT.md §6.26 · 台账 #23)。
|
|
3
3
|
*
|
|
4
4
|
* 自 `subagent-spawn.mjs` **逐字搬移**(D23-3 外提:spawn 档越 300 软线 + 块构造成纯函数
|
|
5
|
-
* ⇒
|
|
5
|
+
* ⇒ 判据可直测;D23-4:块内文本零改——含块内自指词,整块搬移不改真值)。本档承载
|
|
6
6
|
* spawn 级固定机制性指令的第二块(审计模板 + A2 任务书摘要 + touched 快照):
|
|
7
7
|
* 装配侧只收集(`thincoder-core/agent-tools/subagent-spawn.mjs`),下发面 = system 固块
|
|
8
8
|
* (`child._spawnSystemBlock` → 核 `prepareRun` 拼接——`thincoder-core/agent/setup.mjs`)。
|
|
@@ -56,7 +56,7 @@ const TAIL_KEEP_CHARS = 32 * 1024
|
|
|
56
56
|
/** exit→close 宽限(同同步径后台注记面:孙进程握管道时 close 不来——`bash.mjs` 1500ms 同值)。 */
|
|
57
57
|
const EXIT_GRACE_MS = 1500
|
|
58
58
|
|
|
59
|
-
/** 测试缝:log
|
|
59
|
+
/** 测试缝:log 目录重定向(单元测试沙箱——生产从不调用;`_setDigestOffloadDirForTest` 同式)。 */
|
|
60
60
|
let _bgTaskLogDir = null
|
|
61
61
|
export function _setBgTaskLogDirForTest(dir) { _bgTaskLogDir = dir }
|
|
62
62
|
function bgTaskLogDir() { return _bgTaskLogDir ?? join(configDir, "tool-results") }
|
|
@@ -12,14 +12,15 @@
|
|
|
12
12
|
* 身份判据(D-BR17/D-BR18/D-BR21):主 agent 的工具调用 ctx 带 `depth === 0`(dispatch 装配);
|
|
13
13
|
* eng 子代理 `ctx.depth > 0`;评审实例 ctx 无 depth 但工具以 `review: true` 绑定。create/close
|
|
14
14
|
* 仅 depth-0 放行(BR-19/BR-24「… is main-agent-only」);status 写域 = 调用者自己段
|
|
15
|
-
* (eng-designer → §2 · 评审 → §3 · eng-coder → §5 ·
|
|
16
|
-
* 状态面走普通文档写);depth-0 的 status/path
|
|
15
|
+
* (eng-designer → §2 · 评审 → §3 · eng-coder → §5 · coder → §5【普通模式分支增表】· 主 agent →
|
|
16
|
+
* 工程 §1 ∥ 普通 §1/§2/§5【取段——§4.16】——§4/§6 状态面走普通文档写);depth-0 的 status/path 面
|
|
17
|
+
* 按 D-BR21(可选 path,缺省 = 在飞批唯一时取)。
|
|
17
18
|
*/
|
|
18
19
|
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs"
|
|
19
20
|
import { dirname, resolve } from "node:path"
|
|
20
21
|
|
|
21
22
|
import {
|
|
22
|
-
SEGMENT_BY_ROLE, STATUS_WORDS, STATUS_LINE_RE, readBatchStatusLine, sectionHasStatusLine, sectionHeaderRe, batchSkeleton,
|
|
23
|
+
SEGMENT_BY_ROLE, SEGMENT_BY_ROLE_NORMAL, DEPTH0_SEGMENTS, STATUS_WORDS, STATUS_LINE_RE, readBatchStatusLine, sectionHasStatusLine, sectionHeaderRe, batchSkeleton,
|
|
23
24
|
TEMPLATE_PLACEHOLDERS, findPlaceholderResidue, placeholderResidueError,
|
|
24
25
|
} from "./batch-skeleton.mjs"
|
|
25
26
|
import { resolveBatchCreatePath } from "./batch-paths.mjs"
|
|
@@ -122,6 +123,8 @@ export function findInFlightBatch(cwd, bases) {
|
|
|
122
123
|
* ⇒ create 即拒,不留死锁);`prev` 传入值**幂等剥**「前情 = 」前缀(值规范化;默认值零变)。
|
|
123
124
|
* 2026-10-05(A-2):`ledger` / `board` 可选参**建即填**台账行(归一契约 = §4.11;回执两态 =
|
|
124
125
|
* 两参皆给 ⇒ 标注已填 ∥ 未给(任一)⇒ 填法句)。
|
|
126
|
+
* 2026-10-09(§4.16):骨架**按模式位取形**(`batchSkeleton({…, mode})`——执行期实读;普通面
|
|
127
|
+
* 作者标签四处替换,工程面骨架产出逐字不变)。
|
|
125
128
|
* @returns {string} 成功消息(含落盘绝对路径)
|
|
126
129
|
*/
|
|
127
130
|
export function createBatchRecord({ args, ctx, review, cwd, bases, onWritten }) {
|
|
@@ -161,7 +164,9 @@ export function createBatchRecord({ args, ctx, review, cwd, bases, onWritten })
|
|
|
161
164
|
const prevRaw = typeof args?.prev === "string" ? args.prev.trim() : ""
|
|
162
165
|
const prev = prevRaw ? (prevRaw.replace(/^(?:前情\s*[=::]\s*)+/g, "").trim() || "无(独立批)") : "无(独立批)"
|
|
163
166
|
mkdirSync(dirname(abs), { recursive: true })
|
|
164
|
-
|
|
167
|
+
// 模式位执行期实读(§4.16——骨架取形时点 = 建档即固;翻转不回写既有骨架)
|
|
168
|
+
const mode = ctx?.agent?.config?.agent?.engineering ? "engineering" : "normal"
|
|
169
|
+
writeFileSync(abs, batchSkeleton({ date, topic, source, prev, ledger, board, mode }))
|
|
165
170
|
onWritten?.(ctx?.agent ?? {}, abs)
|
|
166
171
|
// 回执两态(`TOOLS.md` §6.20 逐字):两参皆给 ⇒ 标注已填;未给(任一)⇒ 填法句(填法 = 文件编辑)
|
|
167
172
|
const headerNote = ledger !== null && board !== null
|
|
@@ -281,24 +286,40 @@ function assertStatusNote(note) {
|
|
|
281
286
|
}
|
|
282
287
|
|
|
283
288
|
/**
|
|
284
|
-
* status——状态行流转(§4.12
|
|
285
|
-
* §
|
|
286
|
-
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
289
|
+
* status——状态行流转(§4.12,段属主;§4.16 模式感知)。写域 = 调用者自己段内 `**状态行**:`
|
|
290
|
+
* 行(评审 → §3 · eng-designer → §2 · eng-coder / coder → §5 · 主 agent:工程 §1 ∥ 普通
|
|
291
|
+
* §1/§2/§5 取段——缺省 §1);值域 = STATUS_WORDS 该段项恰一词(**余核 = 关键词**——F11-A
|
|
292
|
+
* 谓词收紧),散文说明走独立 `note` 字段(落状态行括注);冻结真值不变(gate 只读 §1)。
|
|
293
|
+
* path 参数 = 仅 depth-0(D-BR21)——子代理/评审传 path ⇒ 拒(目标 = spawn/实例注入,语法上
|
|
294
|
+
* 写不到别处)。F11-C 挂点 = 写盘前(assertStatusValue 后):档头结构行含骨架死占位 ⇒ 拒
|
|
295
|
+
* (段体引用豁免;close 不拦——收口是主 agent 终态动作)。
|
|
290
296
|
* @returns {string} 成功消息
|
|
291
297
|
*/
|
|
292
298
|
export function statusBatchRecord({ args, ctx, review, pickTarget, onWritten }) {
|
|
293
299
|
const depth0 = isDepthZero(ctx)
|
|
294
|
-
|
|
300
|
+
// 模式位执行期实读(§4.16——同款先例 = task.mjs execute 门;翻转即时生效——D-BR26)。
|
|
301
|
+
// 写域:评审 → §3 · 子代理 → 身份段(普通模式分支合并 coder 增表——工程模式分支不查)·
|
|
302
|
+
// depth-0 工程 → §1 ∥ 普通 → §1/§2/§5(取段——缺省 §1)。
|
|
303
|
+
const engineering = !!ctx?.agent?.config?.agent?.engineering
|
|
304
|
+
let seg = review ? 3 : depth0 ? 1 : SEGMENT_BY_ROLE[ctx?.agent?._role] ?? (engineering ? null : SEGMENT_BY_ROLE_NORMAL[ctx?.agent?._role] ?? null)
|
|
295
305
|
if (seg === null) {
|
|
296
306
|
throw new Error("batch: no segment is writable by this caller — status writes the caller's OWN section (eng-designer → §2, design review → §3, eng-coder → §5, main agent → §1).")
|
|
297
307
|
}
|
|
298
|
-
//
|
|
299
|
-
//
|
|
308
|
+
// 段域判据(BR-22「eng-designer 对 §1 调 status ⇒ 段白名单拒」——镜像 append 面):
|
|
309
|
+
// depth-0 普通面(§4.16)= **取段**——写域集 §1/§2/§5、缺省 §1、越出集合 ⇒ 拒(模式化新串);
|
|
310
|
+
// depth-0 工程面 ∥ 子代理 ∥ 评审 = **声明段核对**(声明段 ≠ 身份写域段 ⇒ 拒;身份写域段可省略)。
|
|
300
311
|
const declared = args?.segment === undefined || args?.segment === null ? null : segmentNumber(args.segment)
|
|
301
|
-
|
|
312
|
+
// 传值而解析不可得 ⇒ 显式拒(#1116——镜像 append 面「unknown segment」串形;未传 = 缺省语义零变)。
|
|
313
|
+
if (declared === null && args?.segment !== undefined && args?.segment !== null) {
|
|
314
|
+
throw new Error(`batch: unknown segment ${JSON.stringify(args?.segment ?? null)} — pass the section number you write (e.g. "§${seg}"), or omit it. Nothing was written.`)
|
|
315
|
+
}
|
|
316
|
+
if (depth0 && !review && !engineering) {
|
|
317
|
+
const target = declared ?? 1
|
|
318
|
+
if (!DEPTH0_SEGMENTS.status.normal.includes(target)) {
|
|
319
|
+
throw new Error(`batch: §${declared} is not yours to write — in normal mode status writes §1/§2/§5 only (一段一作者: §3 = design review; §4/§6 carry no status word list). Nothing was written.`)
|
|
320
|
+
}
|
|
321
|
+
seg = target
|
|
322
|
+
} else if (declared !== null && declared !== seg) {
|
|
302
323
|
throw new Error(`batch: §${declared} is not yours to write — status writes YOUR OWN section §${seg} only (一段一作者: eng-designer → §2, design review → §3, eng-coder → §5, main agent → §1). Nothing was written.`)
|
|
303
324
|
}
|
|
304
325
|
if (args?.path !== undefined && args?.path !== null && !depth0) {
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* 契约(§4.15 逐字):**候选序** = cwd → 项目根 → 逐基底(基底 = `batchDocBases` 声明面单源);
|
|
8
8
|
* **判据分野** = 读面取首个**可读**文件 / create 取首个**落基底根内**者;**锚定防嵌套** = 串以任一基底的
|
|
9
9
|
* 「项目根相对前缀」打头且前缀后为**路径段边界** ⇒ 只解析项目根形(不二次拼接;不落基底内 ⇒ fail-closed);
|
|
10
|
-
* **歧义锚**(条 5 · 2026-10-02 · #828)= create 按目标所属项目落基底 ∥
|
|
10
|
+
* **歧义锚**(条 5 · 2026-10-02 · #828)= create 按目标所属项目落基底 ∥ 无所属显式拒;读面候选分支追加;
|
|
11
11
|
* 在飞 ∥ 写门基底集 = 候选并集(`batchDocBases`);**零变面** = 绝对路径照旧、三个错误文案逐字、
|
|
12
12
|
* 「参数在 + 路径可读」判据句、`ok` / `none` 两态。
|
|
13
|
-
*
|
|
13
|
+
* **基底自有项目根分支 + 基底相对单段收窄**(条 6 · 2026-10-05 · #942)= 逐基底 `owningProject` 恰一落点收 ∥ 多落点显式拒(`ambiguousLandingError`);基底相对分支限单段(多段串不二次拼接)。
|
|
14
14
|
*
|
|
15
15
|
* 依赖(KD-4 单向):本档为**叶档**——只 import `node:*` 与 `../manifest.mjs`;主档 re-export 保既有面。
|
|
16
16
|
*/
|
|
@@ -101,7 +101,7 @@ const anchoredEscapeError = (raw) => new Error(`batch: create path is anchored a
|
|
|
101
101
|
const ambiguousCreateError = (raw, candidates) => new Error(`batch: ambiguous session anchor — create target "${raw}" belongs to no candidate project; pass an explicit project path (e.g. "${candidates[0]}/docs/batches/<file>.md"). Candidates: ${candidates.join(", ")}`)
|
|
102
102
|
const ambiguousLandingError = (raw, landings) => new Error(`batch: create path resolves under more than one declared batch-record base — refusing to pick one (fail-closed); pass an explicit path (absolute preferred). Path: ${raw}. Candidates: ${landings.join(", ")}`)
|
|
103
103
|
|
|
104
|
-
/**
|
|
104
|
+
/** 歧义锚读面候选分支(条 5):逐候选项目——项目根形(`resolve(candidate, p)`)∥ 声明基底形
|
|
105
105
|
* (候选按名排序、并集保序——「首个可读」在此即为确定判读)。 */
|
|
106
106
|
function ambiguousReadLegs(cwd, p) {
|
|
107
107
|
const amb = ambiguousCandidates(cwd)
|
|
@@ -194,7 +194,7 @@ export function resolveBatchCreatePath(cwd, given, bases) {
|
|
|
194
194
|
}
|
|
195
195
|
if (owned.length > 1) throw ambiguousLandingError(raw, owned)
|
|
196
196
|
if (owned.length === 1) return owned[0]
|
|
197
|
-
// ④ 基底相对形收窄单段(归一后无 `/`
|
|
197
|
+
// ④ 基底相对形收窄单段(归一后无 `/` 才走原基底分支——多段串不二次拼接)
|
|
198
198
|
const leafRel = roots.length ? relative(roots[0], resolve(roots[0], p)) : ""
|
|
199
199
|
if (leafRel && !leafRel.startsWith("..") && !leafRel.includes(sep)) {
|
|
200
200
|
for (const b of roots) {
|
|
@@ -29,7 +29,7 @@ export const sectionHeaderRe = (seg) => new RegExp(`^## §${seg}(?=\\s|$)`, "m")
|
|
|
29
29
|
* 内嵌另一冻结词的值(含「进行中…已收口」双词 ⇒ 已收口优先误冻结——2026-09-20 词面纪律)。
|
|
30
30
|
* emoji / 括注 / 日期后缀容忍(**读侧** gate 判定 = 子串包含,同 gate——写入面 value 谓词
|
|
31
31
|
* 自本批起收紧为「剥装饰白名单后余核 = 关键词」,见 batch-lifecycle.mjs);
|
|
32
|
-
* 全词表并集 = 各段项去重集合(note
|
|
32
|
+
* 全词表并集 = 各段项去重集合(note 字段零命中的判据——括注永不误触 gate)。
|
|
33
33
|
*/
|
|
34
34
|
export const STATUS_WORDS = Object.freeze({
|
|
35
35
|
1: Object.freeze({ open: "进行中", closed: "已收口" }),
|
|
@@ -39,16 +39,29 @@ export const STATUS_WORDS = Object.freeze({
|
|
|
39
39
|
})
|
|
40
40
|
|
|
41
41
|
/** 身份 → 可写段号(append 段白名单的**子代理面**——原 batch-segment.mjs 逐字迁移;主 agent
|
|
42
|
-
* 的 depth-0
|
|
43
|
-
* batch.mjs / batch-lifecycle.mjs 分支,不经本表。旧 batch-segment.mjs
|
|
44
|
-
*
|
|
42
|
+
* 的 depth-0 白名单(模式感知——工程 §1/§4/§6 · §1 ∥ 普通 §1/§2/§4/§5/§6 · §1/§2/§5,§4.16)
|
|
43
|
+
* 由身份判据在 batch.mjs / batch-lifecycle.mjs 分支,不经本表。旧 batch-segment.mjs 导出面等价
|
|
44
|
+
* 保持——本符号经 batch.mjs re-export(过渡 shim 面零触)。**表零字面改**——普通模式另立增表
|
|
45
|
+
* `SEGMENT_BY_ROLE_NORMAL`(合并查只在普通模式分支);工程面「逐字不变」判据据此成立。 */
|
|
45
46
|
export const SEGMENT_BY_ROLE = { "eng-designer": 2, "eng-coder": 5 }
|
|
46
47
|
|
|
48
|
+
/** 普通模式增表(§4.16——**合并查只在普通模式分支**,工程模式分支不消费本表):coder 承办 ⇒ §5
|
|
49
|
+
* 实施记录(可选绑定自写——未绑定由主 agent 代记、行内注明来源)。 */
|
|
50
|
+
export const SEGMENT_BY_ROLE_NORMAL = { "coder": 5 }
|
|
51
|
+
|
|
52
|
+
/** depth-0 段白名单(§4.16——**模式键两集**;工程面判定字面逐字保持、普通面新增):
|
|
53
|
+
* append:工程 = §1/§4/§6 ∥ 普通 = §1/§2/§4/§5/§6(§2 = 设计师席并入主 agent;§5 = 直做实施记录);
|
|
54
|
+
* status:工程 = §1 ∥ 普通 = §1/§2/§5(写域仍「调用者自己段」;`segment` 缺省 = §1)。 */
|
|
55
|
+
export const DEPTH0_SEGMENTS = Object.freeze({
|
|
56
|
+
append: Object.freeze({ engineering: Object.freeze([1, 4, 6]), normal: Object.freeze([1, 2, 4, 5, 6]) }),
|
|
57
|
+
status: Object.freeze({ engineering: Object.freeze([1]), normal: Object.freeze([1, 2, 5]) }),
|
|
58
|
+
})
|
|
59
|
+
|
|
47
60
|
/** §1 状态行前缀形态(`**状态行**:` 独立行——表格行 / 块引用行不命中;判定只认关键字)。 */
|
|
48
61
|
export const STATUS_LINE_RE = /^\s*\*\*状态行\*\*[::]\s*(.*)$/
|
|
49
62
|
|
|
50
63
|
/**
|
|
51
|
-
* 骨架死占位**枚举单源**(F11-C
|
|
64
|
+
* 骨架死占位**枚举单源**(F11-C 判据——create 骨架产出过的全部死占位字面):
|
|
52
65
|
* - `#<编号>` / `<板块>` = 现行骨架仍在产出的两处(台账行——填充人 = 主 agent,时点 =
|
|
53
66
|
* 建档后、本档首个 append/status 之前);
|
|
54
67
|
* - `<BATCH-ID>` / `<讨论来源>` = 改形**删除**的两处旧占位(create 不再产出;旧档残留同判)。
|
|
@@ -178,22 +191,27 @@ export function sectionHasStatusLine(src, seg) {
|
|
|
178
191
|
* 留存时(未给参)由主 agent 文件编辑填(台账登记 = 主 agent 既有义务,create 不代建——填充
|
|
179
192
|
* 时点 = 建档后、本档首个 append/status 之前)。
|
|
180
193
|
* 本批改形删除(F11-B):旧 `# …(<BATCH-ID>)` 后缀与 `来源 = <讨论来源>` 占位实参化。
|
|
194
|
+
* 模式取形(§4.16——普通模式变体**四处替换**、余行逐字同;`mode` 缺省 = "engineering" ⇒
|
|
195
|
+
* 工程产出逐字不变):① 汇总行 §2/§5 标签;② `## §2` 段头;③ §2 状态占位句;④ `## §5` 段头。
|
|
181
196
|
*/
|
|
182
|
-
export function batchSkeleton({ date, topic, source, prev, ledger = null, board = null }) {
|
|
197
|
+
export function batchSkeleton({ date, topic, source, prev, ledger = null, board = null, mode = "engineering" }) {
|
|
198
|
+
const normal = mode === "normal"
|
|
199
|
+
const designerSeat = normal ? "主 agent" : "eng-designer" // ① 汇总行 §2 标签 ∥ ② §2 段头
|
|
200
|
+
const implementerSeat = normal ? "实施者:主 agent ∥ coder" : "eng-coder" // ① 汇总行 §5 标签 ∥ ④ §5 段头
|
|
183
201
|
return [
|
|
184
202
|
`# ${date} · ${topic}`,
|
|
185
|
-
|
|
203
|
+
`> 六段 append-only,一段一作者:§1 讨论(主 agent)· §2 批次任务与设计(${designerSeat})· §3 设计评审(评审子代理)· §4 用户批准(主 agent)· §5 实施记录(${implementerSeat})· §6 验证与收口(父代理)。`,
|
|
186
204
|
`> 编制:主 agent · ${date} · 来源 = ${source}。`,
|
|
187
205
|
`> 台账 = ${ledger ?? "#<编号>"}(${board ?? "<板块>"} · 归批)。前情 = ${prev}。`,
|
|
188
206
|
"## §1 讨论(主 agent)",
|
|
189
207
|
`**状态行**:🔄 ${STATUS_WORDS[1].open}(…)`,
|
|
190
208
|
"<§1 模板占位:本批条目 / 关键判据 / 授权口径>",
|
|
191
|
-
|
|
192
|
-
"
|
|
193
|
-
"<§2 模板占位:本批条目(覆盖) / 设计档落点 / 机制设计 /
|
|
209
|
+
`## §2 批次任务与设计(${designerSeat})`,
|
|
210
|
+
`**状态行**:(${normal ? "主 agent" : "eng-designer"} 写入时更新)`, // ③ §2 状态占位句
|
|
211
|
+
"<§2 模板占位:本批条目(覆盖) / 设计档落点 / 机制设计 / 受影响文件与测试覆盖 / 验收对照 / 关键决策 / 上抛项>",
|
|
194
212
|
"## §3 设计评审(评审子代理)",
|
|
195
213
|
"## §4 用户批准(主 agent)",
|
|
196
|
-
|
|
214
|
+
`## §5 实施记录(${implementerSeat})`,
|
|
197
215
|
"## §6 验证与收口(父代理)",
|
|
198
216
|
"",
|
|
199
217
|
].join("\n")
|