@thincoder/core 0.9.5 → 0.10.1
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 +36 -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 +17 -4
- 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-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 +90 -18
- 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-tools/subagent.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* 2026-09-05 拆分轮: status/escalate/panel 动作执行器 → ./subagent-actions.mjs;§20
|
|
15
15
|
* 调度器全套 → ./subagent-scheduler.mjs——本文件 import 源随之改写。
|
|
16
16
|
* 2026-09-05 模块拆分轮(726 > 500 硬限): spawn 前置 helpers(summarizeEngTaskBook/
|
|
17
|
-
* effectiveSubagentModel/resolveDesignSlot)+ §
|
|
17
|
+
* effectiveSubagentModel/resolveDesignSlot)+ AGENT-LOOP-SUBAGENT.md §6.9 准入(prepareScheduling)+ child
|
|
18
18
|
* 装配(buildSpawnChild)→ ./subagent-spawn.mjs;async 分支(executeAsyncSpawn)
|
|
19
19
|
* → ./subagent-run.mjs——execute 只保留动作分流 + 装配调用 + 阻塞路径。
|
|
20
20
|
*/
|
|
@@ -33,6 +33,8 @@ import { executeStatusAction, executeEscalateAction, executePanelAction, execute
|
|
|
33
33
|
import { prepareScheduling, buildSpawnChild, executeConsumeDesignAction } from "./subagent-spawn.mjs"
|
|
34
34
|
import { executeAsyncSpawn } from "./subagent-run.mjs"
|
|
35
35
|
import { ROUND_VALUES } from "./spawn-gates.mjs"
|
|
36
|
+
// #15 描述外置:描述文本单点 = tool-docs/subagent.md(DESC 单解析面,缺档抛错语义不变)
|
|
37
|
+
import { DESC } from "../tools/shared.mjs"
|
|
36
38
|
|
|
37
39
|
// ─── SYNC-CANCEL 纯函数(可测——无 io)──────────────────────────────────────────
|
|
38
40
|
|
|
@@ -71,7 +73,7 @@ export function classifySyncAbort(ctxSignal, baseSignal, ctrlSignal, err) {
|
|
|
71
73
|
export function armSyncChildAbort(parent, key, baseSignal, announce = null) {
|
|
72
74
|
const ctrl = new AbortController()
|
|
73
75
|
if (baseSignal) {
|
|
74
|
-
// §
|
|
76
|
+
// AGENT-LOOP-SUBAGENT.md §6.12 站点 #10(第 24 批):hop 逐跳保 reason(下游可判定「谁杀的」)
|
|
75
77
|
if (baseSignal.aborted) ctrl.abort(baseSignal.reason)
|
|
76
78
|
else baseSignal.addEventListener("abort", () => ctrl.abort(baseSignal.reason), { once: true })
|
|
77
79
|
}
|
|
@@ -102,9 +104,9 @@ export function buildSyncStoppedReport(role, capturedOutput, designId) {
|
|
|
102
104
|
/**
|
|
103
105
|
* subagent tool — ONE tool, EIGHT actions (AGENT-LOOP-SUBAGENT.md §6.7 +
|
|
104
106
|
* SUBAGENT-OBSERVE-SEND): spawn (default) / status (non-blocking pool query) / observe
|
|
105
|
-
* (inspect a running/queued/done async child's recent activity + current tool — §
|
|
107
|
+
* (inspect a running/queued/done async child's recent activity + current tool — docs/cli/design/TUI.md §6.8) /
|
|
106
108
|
* send (inject a direction into a RUNNING async child — consumed at its next turn
|
|
107
|
-
* boundary as an ordinary instruction — §
|
|
109
|
+
* boundary as an ordinary instruction — §6.8) / escalate (飞刀 — hand implementation to
|
|
108
110
|
* a stronger model) / cancel (stop ONE background subagent — AGENT-LOOP-SUBAGENT.md §6.7.2) / panel (view + fix
|
|
109
111
|
* the live subagent panel — AGENT-LOOP-SUBAGENT.md §6.7.2) / consume-design (parent-side chain-terminal token
|
|
110
112
|
* consumption — ENGINEERING-MODE.md §2.6, 2026-09-07). The check
|
|
@@ -117,56 +119,34 @@ export function buildSyncStoppedReport(role, capturedOutput, designId) {
|
|
|
117
119
|
* - no role specified — invalid by design since the 2026-08-25 fail-closed gate
|
|
118
120
|
* (role is mandatory; "no role → same tool set as parent" was removed with the
|
|
119
121
|
* coder-leak fix and the header text above predates it)
|
|
120
|
-
* - non-recursive: child agents do not get the subagent tool (depth > 0 is not
|
|
122
|
+
* - non-recursive for non-eng children: child agents do not get the subagent tool (depth > 0 is not
|
|
123
|
+
* injected); eng-coder / eng-designer children get a spawn-only variant (`agent/family-tools.mjs`)
|
|
124
|
+
* - 描述文本单点 = `thincoder-core/tool-docs/subagent.md`(模型可见文本不在此复述——本块只留实现注记)
|
|
121
125
|
*/
|
|
122
126
|
|
|
123
127
|
export const subagentTool = {
|
|
124
128
|
name: "subagent",
|
|
125
|
-
description:
|
|
126
|
-
"ONE tool, EIGHT actions — pick by what you need:\n" +
|
|
127
|
-
"- action:'spawn' (DEFAULT): spawn a sub-agent to handle an independent subtask in an isolated context; the sub-agent returns only its final report. Spawn MULTIPLE subagents in the SAME response for parallel work—they run concurrently.\n" +
|
|
128
|
-
"- action:'status': NON-BLOCKING progress query — returns immediately and consumes nothing. Give the spawn's id for one child ({id, role, status: running|queued|done, model, elapsedSec, turn, maxTurns, position?), or omit it for an overview of the whole pool ({overview: {running: [{id, role, model, elapsedSec, turn, maxTurns}], queued: [{id, role, position}], done: [{id, role}]}}). Touched-files summary: running entries also carry touchedFiles (first 5, relative to your cwd), touchedMore (count beyond 5) and, when nothing was touched yet, the placeholder touched (\"—(尚无改动)\"); queued (not yet started) entries carry the placeholder touched (\"—(未启动)\") — see what a running child has changed BEFORE deciding to cancel it. Use THIS to see progress — it never blocks and never consumes a result (async results are delivered to you automatically). Background advisor reviews share this status surface: pass a review id and it answers in the same shape with role:'advisor' plus reviewType (design|code) / round / elapsedSec — and the overview lists reviews alongside subagents.\n" +
|
|
129
|
-
"- action:'observe': SEE what a running async subagent is DOING right now (progress vs stuck) — pass the spawn id. Returns {id, role, status, turn, maxTurns, touched…, currentTool? — array of tool name(s) currently executing (read from its dispatch state; omitted when none in flight — present when stuck in a long tool call), recentTurns: [last N one-line turn summaries, newest-first; default 5, parameterizable via recent]}. Readonly — observable on running/queued/done: running shows live activity, queued (not started) returns a placeholder, done (settled, report auto-delivered) returns the activity summary only — NOT the full report (that rides the auto channel; observe stays terse to keep your context lean). Use it to judge whether a long-running child is stuck vs progressing BEFORE deciding to cancel or steer it.\n" +
|
|
130
|
-
"- action:'send': STEER a running async subagent mid-flight — pass the spawn id + message (a direction like \"check X, don't fixate on Y\"). The message queues and the child consumes it at its next turn boundary as an ORDINARY user instruction (non-interrupting — its current tool finishes first; its convergence/audit discipline is unchanged — injection is guidance, not a deviation waiver). Returns {id, status:'delivered', queued}. Only a RUNNING async subagent is targetable — sync (you're waiting on it, no relay), queued (not started), settled or unknown ids error clearly. If the child settles before its next turn boundary, its settle report carries an 'undelivered' note so you don't assume the guidance landed.\n" +
|
|
131
|
-
"- action:'escalate' (飞刀 — a flown-in expert): hand an implementation task to a STRONGER model from your consult models (agent.consultModels). It gets WRITE access and does the work itself — reads, edits, runs tests — then returns a post-op report (what changed, why, verification). Use it when YOU judge the task calls for stronger hands (complex multi-file refactoring, an intractable bug, intricate algorithm work — or work beyond your comfortable ability); escalate EARLY, not after burning attempts. model: pick a candidate as 'provider:model' (default = the first consult model). Not available in engineering mode (implementation goes through eng-coder spawns there). DEFAULT-ASYNC at depth 0: the launch returns an ack {id, role:'escalate', status:'running'} and the flight runs in the background (pooled with the other role-domain spawns) — its post-op report is delivered to you automatically with its mutations merged into your bookkeeping.\n" +
|
|
132
|
-
"- action:'cancel': STOP one background subagent — pass the id from the async spawn return (REQUIRED — omitting it errors; a blanket cancel is unsupported, Ctrl+C stops everything). Running target aborts immediately ({id, status:'cancelled'}); a queued target is removed from the queue ({id, status:'cancelled', was:'queued'} and later queue positions shift forward). Other children and the session keep running — cancellation is targeted. Use it when a background child is going the wrong way (e.g. burning turns) and you must stop it before its report arrives. Cancel is a last resort: verify alarming signals with reliable checks (git/node — not guesses) first; prefer scoped recovery (restore a single affected file) over killing the child — a running child's in-flight work dies with it, partial changes stay unmerged and unaudited. Background advisor reviews are cancelable on this same action: pass the review id — its controller aborts, no token is issued for a cancelled review.\n" +
|
|
133
|
-
"- action:'consume-design' (engineering mode, parent side — chain-terminal token consumption): after the delivery is verified and the chain closes out, consume this design's token slot — pass the designId (optional for a single-design session). The slot is consumed; a further spawn for the same designId is mechanically rejected, and any new work (including deviation fixes) requires a fresh design review and token. Idempotent: an unknown designId / already-consumed slot is a no-op notice, never an error. Do NOT call it while the chain is still open — fix rounds reuse the same slot (same designId + designToken).\n" +
|
|
134
|
-
"- action:'panel': DIAGNOSE + fix the subagent panel — the collapsible blocks under the conversation the user sees (CLI TUI panel mirror; headless/VS Code degrade to a 'no panel' pool view). view (default — call it with no params or view:true): returns the live panel blocks [{key, role, status: running|done|awaitingDigest} — running entries also carry elapsedSec; awaitingDigest entries whose report is ALREADY digested carry digested:true (stuck blocks — the freezable ones — explain odd panel states here)] exactly as the user sees them. freeze: pass the block key of a digested-stuck block ({action:'panel', freeze:'role#N'}) to reclaim it into the conversation — the freeze ONLY passes for awaitingDigest blocks with no live pool entry and no pending report (gated); freezing a block whose report is still pending would break the digestion order and is refused with a clear error.\n\n" +
|
|
135
|
-
"Why delegate? A sub-agent runs in its own isolated context — its reads, searches, tool calls and edits never enter your history or pollute your window; only its final report comes back. Delegation keeps your working context lean (you see the whole session, not the child's noise) and the child single-mindedly focused on one task. Parallel children run concurrently, saving wall-clock time. Every coder/eng-coder child carries its own verify + advisor self-review discipline — handed-off work is already verified before you read a word of it.\n\n" +
|
|
136
|
-
"Available roles (which roles are exposed depends on the active mode — see Mode filtering below):\n" +
|
|
137
|
-
"- explore — read-only search & analysis. Toolset: the read/search family (grep, read, glob, code_search, doc_search, repo_outline, lsp, tree...). No git context injected—evidence from read/glob/grep and the task book. Its report must list what it searched and what it did NOT find. Fast — specify thoroughness in the task: quick / medium / thorough (default medium).\n" +
|
|
138
|
-
"- plan — read-only implementation planning. Same read/search toolset; NEVER edits files. Returns a step-by-step plan for the parent to execute.\n" +
|
|
139
|
-
"- coder — full implementation. The parent's complete read/write/execute toolset plus verify and advisor for self-review. Its final report must include a delivery transparency table with one row per task requirement (Done / Simplified / Not done — no deferred column).\n" +
|
|
140
|
-
"- eng-coder — engineering-mode coder (available only in engineering mode, replacing coder). Same full toolset as coder plus the design-driven methodology overlay; REQUIRES a valid designToken arg obtained from a passed advisor(type='design') review. The advisor's Approved reply also echoes a designId — pass it as the designId arg: required to pick between designs when several approved reviews are active, optional for a single design. The delivery report echoes the designId back for the audit fix round. ALSO REQUIRES a batchDoc arg — the batch record path (docs/batches/<batch>-<topic>.md), the batch §2 task book this spawn implements: a spawn without it, or with a path that does not resolve to a readable file, is mechanically refused.\n" +
|
|
141
|
-
"- eng-designer — engineering-mode design writer (available only in engineering mode): the SOLE author of the requirements + design documents and of the batch record §2 (the batch task book) — revisions included. Writes no implementation code, does not edit prompt files, does not fire reviews, and needs NO designToken (its authorization is the confirmed requirements). It surveys on its own, but may only spawn read-only 'explore' children (sync, ≤6 per batch). ALSO REQUIRES a batchDoc arg — the batch record path (docs/batches/<batch>-<topic>.md); the same mechanical gate as eng-coder: a spawn without it, or with a path that does not resolve to a readable file, is mechanically refused.\n" +
|
|
142
|
-
"Mode filtering: normal mode exposes explore/plan/coder (eng-coder and eng-designer are refused); engineering mode exposes explore/eng-designer/eng-coder (plan and coder are refused — plan is a normal-mode role, coder is replaced by eng-coder). The role enum is filtered per mode at assembly, and the spawn gate refuses out-of-mode roles mechanically.\n\n" +
|
|
143
|
-
"Async spawn: pass async:true to spawn WITHOUT waiting — returns {id, role, status:\"running\"} immediately so you can keep working in your own turn (read/check files, run other tools) while the child runs in the background. The child's report is delivered to you automatically — there is no fetch action; use action:'status' only to see progress, never to wait for the result. Top-level spawns are ALWAYS async — never pass `async:false` at depth-0 (the report arrives automatically; if your next step needs it, end the turn and let the digest deliver it). Inside subagents (depth>0) spawns are always synchronous (platform rule). Eng-coder's delivery protocol runs fully inside the child (implementation → audit → self-fix → advisor re-review → converged delivery). Async spawns are pooled per role domain: at most 4 concurrent eng-coders and 4 concurrent other-role spawns by default (agent.poolLimits overrides both) — a full domain queues further spawns with a position while the other domain keeps starting (domains never block each other), and top-level only. After an async spawn the turn winds down normally — nothing expects you to wait for it: the child runs in the background and its report is delivered to you automatically — before your next turn, or digested in the suspension session — so end the turn; do not poll or wait for the result.\n\n" +
|
|
144
|
-
"Task scheduling: declare the scheduling metadata to let the SCHEDULER order your spawns — files: the file paths this task will modify, dependsOn: ids from prior async spawn returns whose outcome this task needs. Overlapping-file tasks are serialized and dependent tasks are started in order automatically: a spawn that would conflict, or whose dependencies have not settled, queues instead of running ({id, status:\"queued\", position, reason} — the waiting task auto-starts when the conflict clears / its dependency settles; cancel a queued task to drop it). A spawn whose dependency was cancelled or failed stays queued and marked \"dependency cancelled\" until you decide (cancel it) — in an AUTO session it starts by itself. Referencing an unknown id errors; an id already consumed (auto-delivered by the auto channel) counts as satisfied. Omit both parameters for the plain immediate spawn (no scheduler involvement).\n\n" +
|
|
145
|
-
"Writing the prompt:\n" +
|
|
146
|
-
"- The sub-agent starts with zero context — it has not seen this conversation. Brief it like a colleague who just walked into the room: state the goal, list what you already know, hand over the specifics.\n" +
|
|
147
|
-
"- Put exact paths and commands in the prompt when you know them. The sub-agent should not search for things you already know.\n" +
|
|
148
|
-
"- Do not delegate understanding: if the task hinges on a file path or line number, find it yourself first and write it into the prompt.\n" +
|
|
149
|
-
"- Once a sub-agent is running, leave that scope to it: don't redo its searches in parallel, and don't abandon it midway to finish manually.",
|
|
129
|
+
description: DESC("subagent"),
|
|
150
130
|
parameters: {
|
|
151
131
|
type: "object",
|
|
152
132
|
properties: {
|
|
153
|
-
action: { type: "string", enum: ["spawn", "status", "escalate", "cancel", "panel", "consume-design", "observe", "send"], description: "Which
|
|
154
|
-
view: { type: "boolean", description: "action:'panel' only: true (default) =
|
|
155
|
-
freeze: { type: "string", description: "action:'panel' only: block key of a digested-stuck
|
|
156
|
-
task: { type: "string", description: "Required for action:'spawn' (the self-contained task brief) and action:'escalate' (goal, constraints, entry files, acceptance criteria).
|
|
133
|
+
action: { type: "string", enum: ["spawn", "status", "escalate", "cancel", "panel", "consume-design", "observe", "send"], description: "Which action — spawn (default) / status / observe / send / escalate / cancel / panel / consume-design; semantics per action in the tool description." },
|
|
134
|
+
view: { type: "boolean", description: "action:'panel' only: true (default) = the live panel blocks as the user sees them; false without freeze = error. Mutually exclusive with freeze (freeze wins)." },
|
|
135
|
+
freeze: { type: "string", description: "action:'panel' only: block key of a digested-stuck block — reclaims it into the conversation; refused for running/done/unknown blocks or a still-pending report; requires the CLI TUI panel mirror." },
|
|
136
|
+
task: { type: "string", description: "Required for action:'spawn' (the self-contained task brief) and action:'escalate' (goal, constraints, entry files, acceptance criteria)." },
|
|
157
137
|
context: { type: "string", description: "Optional background the sub-agent needs (it cannot see this conversation); action:'spawn' only." },
|
|
158
|
-
role: { type: "string", enum: ["explore", "plan", "coder", "eng-coder", "eng-designer"], description: "The sub-agent role — see the
|
|
159
|
-
model: { type: "string", description: "action:'spawn': provider/model override
|
|
160
|
-
designToken: { type: "string", description: "Required when role='eng-coder': the token
|
|
161
|
-
designId: { type: "string", description: "
|
|
162
|
-
batchDoc: { type: "string", description: "REQUIRED for role='eng-coder'
|
|
163
|
-
round: { type: "string", enum: ROUND_VALUES, description: "REQUIRED for role='eng-coder'
|
|
164
|
-
async: { type: "boolean", description: "action:'spawn':
|
|
165
|
-
files: { type: "array", items: { type: "string" }, description: "action:'spawn' only: the file write-domain this task declares
|
|
166
|
-
dependsOn: { type: "array", items: { type: "string" }, description: "action:'spawn' only: ids from prior async
|
|
167
|
-
id: { type: "string", description: "action:'status'/'observe'/'send'/'cancel': the subagent id from the
|
|
168
|
-
message: { type: "string", description: "action:'send' only (REQUIRED there): the direction to inject — the running
|
|
169
|
-
recent: { type: "integer", description: "action:'observe' only (optional, default 5): how many recent-turn summaries
|
|
138
|
+
role: { type: "string", enum: ["explore", "plan", "coder", "eng-coder", "eng-designer"], description: "The sub-agent role — see the role matrix in the tool description. Exact spelling required. action:'spawn' only (escalate spawns its own expert internally)." },
|
|
139
|
+
model: { type: "string", description: "action:'spawn': provider/model override ('provider:model', a provider, or a model name on the parent's provider) — defaults config.agent.subagentModels[role] → config.agent.subagentModel → the parent's provider; \"default\" inherits. action:'escalate': a consult candidate (default = the first)." },
|
|
140
|
+
designToken: { type: "string", description: "Required when role='eng-coder': the token from advisor(type='design') after the review passed — without it, eng-coder cannot modify files." },
|
|
141
|
+
designId: { type: "string", description: "role='eng-coder': the designId echoed with the approved token — required to pick between concurrent designs (optional for one). action:'consume-design': the slot to close out (required to pick between several; the gate refuses to guess)." },
|
|
142
|
+
batchDoc: { type: "string", description: "REQUIRED for role='eng-coder'/'eng-designer': the batch record path (docs/batches/<batch>-<topic>.md) — the task book this spawn implements/writes. Refused when absent or when the path does not resolve to a readable file; the content is never validated." },
|
|
143
|
+
round: { type: "string", enum: ROUND_VALUES, description: "REQUIRED for role='eng-coder'/'eng-designer': initial (first round for this design) or fix (a correction round reusing the same designId+designToken; docs FIRST). No default — missing = refused." },
|
|
144
|
+
async: { type: "boolean", description: "action:'spawn'/'escalate': default at depth 0 = true (async; the report arrives automatically); depth>0 always sync. async:false forces the blocking flight." },
|
|
145
|
+
files: { type: "array", items: { type: "string" }, description: "action:'spawn' only: the file write-domain this task declares — file-level paths only (cwd-relative or absolute; directories rejected — they bypass the conflict detector). Overlapping-file tasks serialize automatically. Omit = no conflict detection." },
|
|
146
|
+
dependsOn: { type: "array", items: { type: "string" }, description: "action:'spawn' only: ids from prior async returns whose outcome this task needs — queues until every dependency settles, then starts. Consumed ids count as satisfied; a cancelled/failed dependency stays queued marked 'dependency cancelled' until you decide (AUTO sessions auto-start). Unknown ids error." },
|
|
147
|
+
id: { type: "string", description: "action:'status'/'observe'/'send'/'cancel': the subagent id from the spawn return. status: omit = pool overview; observe/send/cancel: REQUIRED." },
|
|
148
|
+
message: { type: "string", description: "action:'send' only (REQUIRED there): the direction to inject — consumed by the running child at its next turn boundary as an ordinary instruction (non-interrupting)." },
|
|
149
|
+
recent: { type: "integer", description: "action:'observe' only (optional, default 5): how many recent-turn summaries (1..20)." },
|
|
170
150
|
},
|
|
171
151
|
required: [],
|
|
172
152
|
},
|
|
@@ -239,7 +219,7 @@ export const subagentTool = {
|
|
|
239
219
|
if (!ROLES.has(role)) {
|
|
240
220
|
throw new Error(`Unknown subagent role: ${JSON.stringify(role)}. Valid roles: explore, plan, coder, eng-coder, eng-designer (exact spelling).`)
|
|
241
221
|
}
|
|
242
|
-
// §
|
|
222
|
+
// AGENT-LOOP-SUBAGENT.md §6.7.6 D-E3 internal-spawn gate: an eng-coder sub-agent may only spawn sync
|
|
243
223
|
// explore (audit) children — non-explore roles and async are refused here
|
|
244
224
|
// (mechanical), the audit budget is enforced (7th audit spawn refused), and
|
|
245
225
|
// the returned attempt number marks this spawn as an audit for the task-book
|
|
@@ -300,7 +280,7 @@ export const subagentTool = {
|
|
|
300
280
|
// stopped 折叠语义丢失:无 ⟦ev⟧stopped/无 STOPPED_MARK——块冻结标 done 而非
|
|
301
281
|
// stopped——评审 🟡#2)。stopped 分支改抛 AbortError——runWithContinue 只捕
|
|
302
282
|
// ContinueError——原样上抛 → 阻塞 catch 三分支②折叠("child 随即在 abort 检出点
|
|
303
|
-
// 解绕折叠"——AGENT-LOOP §7.2 机制文)。abort 恒已在途(stopped 只由
|
|
283
|
+
// 解绕折叠"——AGENT-LOOP-SUBAGENT.md §6.7.2 机制文)。abort 恒已在途(stopped 只由
|
|
304
284
|
// cancelSyncChild 与 ctrl.abort 同时置位)——信号语义真实。
|
|
305
285
|
if (parent._syncChildAborts?.get(key)?.stopped) throw abortError(ctrl.signal, "settle", "sync-stopped")
|
|
306
286
|
const go = await ctx.onPermissionRequest("continue", { turns: e.turn, agent: key })
|
|
@@ -348,14 +328,14 @@ export const subagentTool = {
|
|
|
348
328
|
const cls = classifySyncAbort(ctx.signal, baseSignal, ctrl.signal, e)
|
|
349
329
|
if (cls === "base") {
|
|
350
330
|
// ① 整回合停(现状逐字保留——挂起场景 base 命中而 ctx.signal 未 abort——R2):
|
|
351
|
-
// §
|
|
331
|
+
// docs/cli/design/TUI.md §6.8.2 R23:外层 abort 传播的中断——内层开块随之外层冻结前先收尾定格
|
|
352
332
|
// (D-R23c1 stopped——T-R23c.2a 生成侧路径;TUI 冻结兜底仍在 freezeSubTaskLines)
|
|
353
333
|
emitNestedChildEvent(ctx, relayPrefix, "stopped")
|
|
354
334
|
throw e // 用户停——不落错误事件
|
|
355
335
|
}
|
|
356
336
|
if (cls === "error") {
|
|
357
337
|
// ③ 其他错误(现状逐字保留——:249-253):
|
|
358
|
-
// §
|
|
338
|
+
// §6.8.2 R23 error-run 映射(实现批补一行):run 错误(非 abort)→ 同样发 stopped
|
|
359
339
|
// ——内层子块定格不悬空(T-R23a.3——工具错/运行错误路径)。
|
|
360
340
|
emitNestedChildEvent(ctx, relayPrefix, "stopped")
|
|
361
341
|
logEvent("child:error", { role, id: child._logId, ms: Date.now() - blockT0, err: errText(deathLine(e, ctrl?.signal), 200) })
|
|
@@ -370,7 +350,7 @@ export const subagentTool = {
|
|
|
370
350
|
// 块冻结标 stopped(R6——非 done):⏹ 定向中止的 TUI 顶层块立即定格 stopped
|
|
371
351
|
// (async settle cancelled 分支同款直发——async-settle.mjs settleAsyncEntry);
|
|
372
352
|
// 嵌套(eng-coder 内 explore 审计)经 emitNestedChildEvent 定格子块——stopped
|
|
373
|
-
// 幂等无害(重复/迟到 done 由 §
|
|
353
|
+
// 幂等无害(重复/迟到 done 由 docs/cli/design/TUI.md §6.8.2 F2 done 子块定格丢弃兜底)。
|
|
374
354
|
ctx.callbacks?.onToken?.(`${relayPrefix}⟦ev⟧stopped\x1e0\x1e0\x1estopped\x1e`)
|
|
375
355
|
emitNestedChildEvent(ctx, relayPrefix, "stopped")
|
|
376
356
|
} finally {
|
|
@@ -378,7 +358,7 @@ export const subagentTool = {
|
|
|
378
358
|
// 口径 = 成功/折叠/整回合停——错误③ 同样 rethrow 经 finally——同归本注销)
|
|
379
359
|
disarm()
|
|
380
360
|
}
|
|
381
|
-
// §
|
|
361
|
+
// §6.8.2 R23 D-R23c1(评审 #1 🅰——生成侧补发射):sync spawn 同步收尾——若本 spawn
|
|
382
362
|
// 处于嵌套上下文(ctx.callbacks 已是嵌套 wrapper——eng-coder 内 explore 审计)→
|
|
383
363
|
// 发内层 ⟦ev⟧done(完整嵌套前缀——wrapper 链自动补外层)→ 主 TUI 路由子块定格
|
|
384
364
|
// (T-R23c.1)。非嵌套(depth-0)零变化——冻结仍由 dispatch subKey 精确冻承接。
|
|
@@ -386,7 +366,7 @@ export const subagentTool = {
|
|
|
386
366
|
// 冻结管线复用——迟到 done 对已定格 stopped 块被丢弃——幂等无害)。
|
|
387
367
|
emitNestedChildEvent(ctx, relayPrefix, "done")
|
|
388
368
|
logEvent("child:done", { role, id: child._logId, ms: Date.now() - blockT0, kind: String(pipelineReport).includes(TURN_CAP_MARK) || String(pipelineReport).includes(STOPPED_MARK) ? "partial" : "ok" })
|
|
389
|
-
// §
|
|
369
|
+
// docs/cli/design/TUI.md §6.8 sync spawn 完成精确冻结(方案 e):execute 返回前 ctx 留子代理 key
|
|
390
370
|
// (relayPrefix 去尾 = `role#N`)——dispatch runOne 读它作 onToolResult 第 4 参 →
|
|
391
371
|
// TUI finishSubTaskKey 按 key 精确冻(async eng-coder 先启动时不再误冻其块——
|
|
392
372
|
// T-F2)。仅成功/折叠路径设置:async 分支不设(round2 #2——ack 带 status:running 由
|
package/agent-tools/task.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// F-CC4(CONTEXT-COMPACTION.md §6.16.5):task 列表变更 ⇒ 方向转换轻推(本档只调用;文案/去重单源 = agent-tools/context.mjs)
|
|
2
2
|
import { pushContextNudge } from "./context.mjs"
|
|
3
|
+
import { DESC } from "../tools/shared.mjs"
|
|
3
4
|
|
|
4
5
|
/** Common synonyms LLMs tend to use — normalize to canonical values */
|
|
5
6
|
const STATUS_ALIASES = {
|
|
@@ -26,18 +27,11 @@ function normalizeStatus(raw) {
|
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* task tool: multi-step task planning and progress tracking (Claude Code's todo mode).
|
|
29
|
-
*
|
|
30
|
-
* Accesses the caller agent via ctx.agent (injected by runAgent).
|
|
30
|
+
* 描述面 = `tool-docs/task.md`(DESC() 加载);仅改 agent 内部状态(readonly);ctx.agent 由 runAgent 注入。
|
|
31
31
|
*/
|
|
32
32
|
export const taskTool = {
|
|
33
33
|
name: "task",
|
|
34
|
-
description:
|
|
35
|
-
"Plan and track a task list for complex multi-step work. Each call replaces the entire list. " +
|
|
36
|
-
"Keep exactly one item in_progress at a time; mark items done as you complete them; never mark done if tests fail or work is partial. " +
|
|
37
|
-
"Statuses: pending | in_progress | done — synonyms (completed/finished/complete, todo/open/waiting, active/running/working, …) are accepted and normalized with a warning. " +
|
|
38
|
-
"IMPORTANT: title is required and must be a non-empty string — items with empty titles are silently dropped. " +
|
|
39
|
-
"For cross-session / project-level tracking, use `/ledger`. " +
|
|
40
|
-
"Returns the updated task list (or the new item's ID on add).",
|
|
34
|
+
description: DESC("task"),
|
|
41
35
|
parameters: {
|
|
42
36
|
type: "object",
|
|
43
37
|
properties: {
|
package/agent-tools/timer.mjs
CHANGED
|
@@ -1,18 +1,13 @@
|
|
|
1
|
+
import { TIMER_MAX_PENDING } from "../agent/timers.mjs"
|
|
2
|
+
import { DESC } from "../tools/shared.mjs"
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* timer tool: set a time budget for thinking before the agent insists on action.
|
|
3
|
-
*
|
|
4
|
-
* thinking window. When the timer fires, a system reminder is injected
|
|
5
|
-
* suggesting the model try running code, adding logs, or otherwise acting
|
|
6
|
-
* instead of continuing to think.
|
|
6
|
+
* 描述面 = `tool-docs/timer.md`(DESC() 加载;机制 = AGENT-LOOP-ASYNC-POOL.md §6.30.3 D-TW8)。
|
|
7
7
|
*/
|
|
8
8
|
export const timerTool = {
|
|
9
9
|
name: "timer",
|
|
10
|
-
description:
|
|
11
|
-
"Set a timer before you start analyzing code. When the timer fires, " +
|
|
12
|
-
"a system reminder will be injected suggesting you try running code or " +
|
|
13
|
-
"adding debug logs. Use this to enforce a thinking budget: you get " +
|
|
14
|
-
"N seconds to reason, then the timer reminds you to act. " +
|
|
15
|
-
"Returns the set confirmation — the reminder fires at the deadline.",
|
|
10
|
+
description: DESC("timer"),
|
|
16
11
|
parameters: {
|
|
17
12
|
type: "object",
|
|
18
13
|
properties: {
|
|
@@ -38,8 +33,13 @@ export const timerTool = {
|
|
|
38
33
|
const expiresAt = Date.now() + seconds * 1000
|
|
39
34
|
const message = args.message || `⏰ Time's up (${seconds}s). Have you tried running the code, adding a console.log, or checking the output? Thinking more without data is guessing.`
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
// §6.30.3 门三件②(D-TW6):在途帽 8——超限显式拒(不静默丢 / 不静默清;形态先例 = 队满
|
|
37
|
+
// 「拒 + 提示 + 保留」)。拒点时在途表零变动(本次不推入)。
|
|
38
|
+
const pending = (ctx.agent._pendingTimers = ctx.agent._pendingTimers ?? [])
|
|
39
|
+
if (pending.length >= TIMER_MAX_PENDING) {
|
|
40
|
+
throw new Error(`timer: ${pending.length} timers already pending (max ${TIMER_MAX_PENDING}) — wait for one to fire before setting another`)
|
|
41
|
+
}
|
|
42
|
+
pending.push({ id: Date.now(), expiresAt, message })
|
|
43
43
|
|
|
44
44
|
return `Timer set for ${seconds} seconds. A reminder will appear when time is up.`
|
|
45
45
|
},
|
package/agent-tools/verify.mjs
CHANGED
|
@@ -11,11 +11,13 @@
|
|
|
11
11
|
* path (doc-only changes return early) — the task list and self-review checklist.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { isCodePath, isDocPath,
|
|
14
|
+
import { isCodePath, isDocPath, loadProjectDeclaration } from "../conventions.mjs"
|
|
15
15
|
// #96(信息段与执行方式按端注入——CORE-UNIFICATION §2.13.4):git / node --check 执行
|
|
16
16
|
// 全部经核内执行面单点(默认径 = execFileSync 等价——CLI 语义零行为变;端侧 injected ⇒
|
|
17
17
|
// 可中断执行器);编辑器诊断段经 `configureVerifyDiagnostics` 注入(缺省不注入)。
|
|
18
18
|
import { runCommand } from "../tools/exec-run.mjs"
|
|
19
|
+
// #15 描述外置:描述文本单点 = tool-docs/verify.md(DESC 单解析面,缺档抛错语义不变)
|
|
20
|
+
import { DESC } from "../tools/shared.mjs"
|
|
19
21
|
import { existsSync } from "node:fs"
|
|
20
22
|
import { resolve } from "node:path"
|
|
21
23
|
|
|
@@ -34,7 +36,7 @@ export function configureVerifyDiagnostics(impl) {
|
|
|
34
36
|
export function resetVerifyDiagnostics() { injectedDiagnostics = null }
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
|
-
*
|
|
39
|
+
* D-VR1 path normalization — mirrors the file-domain handling (AGENT-LOOP-SUBAGENT.md §6.9):
|
|
38
40
|
* resolved to an absolute path with forward slashes; dedup uses the win32
|
|
39
41
|
* lowercase comparison key (changedFileKey) so "D:/x/src/a.mjs" and
|
|
40
42
|
* "d:\\x\SRC\a.mjs" count as the same file.
|
|
@@ -74,28 +76,27 @@ function appendBlockGuidance(lines, codeFiles) {
|
|
|
74
76
|
*/
|
|
75
77
|
export const verifyTool = {
|
|
76
78
|
name: "verify",
|
|
77
|
-
description:
|
|
78
|
-
"Generic pre-completion verification gate — language/framework/project agnostic. verify does NOT run any test/verification command for you and does NOT require a test per change. You DECLARE your verification status via the verification argument: { status: 'passed' | 'failed' | 'skipped', command?, summary? }. 'passed' → allowed; 'failed' → blocked (cannot claim done); 'skipped' → allowed but requires a summary reason. Whether/how to verify is described in natural language in the project's AGENTS.md and is decided + executed by you — verify only mechanically gates on your declaration. Also reports changed files (git diff), an advisory node --check hint on changed .js/.mjs (not gating), the task list, and a self-review checklist. Call this BEFORE declaring a coding task complete — do not say 'done' until verify passes.",
|
|
79
|
+
description: DESC("verify"),
|
|
79
80
|
parameters: {
|
|
80
81
|
type: "object",
|
|
81
82
|
properties: {
|
|
82
83
|
verification: {
|
|
83
84
|
type: "object",
|
|
84
|
-
description: "
|
|
85
|
+
description: "Your declaration of whether you already ran the project's verification and its outcome — self-reported evidence (verify does not execute the command).",
|
|
85
86
|
properties: {
|
|
86
87
|
status: { type: "string", enum: ["passed", "failed", "skipped"], description: "The verification outcome you declare: 'passed' → allowed; 'failed' → blocked; 'skipped' → allowed but requires a summary reason." },
|
|
87
|
-
command: { type: "string", description: "Optional: the verification command you ran (self-reported
|
|
88
|
-
summary: { type: "string", description: "Optional:
|
|
88
|
+
command: { type: "string", description: "Optional: the verification command you ran (self-reported)." },
|
|
89
|
+
summary: { type: "string", description: "Optional: the result. Required when status='skipped' — a concrete reason (e.g. \"project has no automated tests — verified by manual review\")." },
|
|
89
90
|
},
|
|
90
91
|
required: ["status"],
|
|
91
92
|
},
|
|
92
|
-
workdir: { type: "string", description: "Optional: run verify in this subdirectory (relative to cwd or absolute) —
|
|
93
|
+
workdir: { type: "string", description: "Optional: run verify in this subdirectory (relative to cwd or absolute) — monorepos" },
|
|
93
94
|
},
|
|
94
95
|
},
|
|
95
96
|
readonly: true,
|
|
96
97
|
async execute(args, ctx) {
|
|
97
98
|
const cwd = ctx.agent.cwd
|
|
98
|
-
// Changed-file resolution (
|
|
99
|
+
// Changed-file resolution (D-VR3): _touchedFiles (per-run bookkeeping,
|
|
99
100
|
// absolute paths) ∪ git diff fallback — git is tried at testCwd
|
|
100
101
|
// (workdir-resolved) first, then at ctx.agent.cwd; first success wins.
|
|
101
102
|
// git-diff paths are repo-root-relative — resolved against the discovered
|
|
@@ -105,7 +106,7 @@ export const verifyTool = {
|
|
|
105
106
|
lines.push("=== VERIFICATION REPORT ===")
|
|
106
107
|
lines.push("")
|
|
107
108
|
|
|
108
|
-
// 1. Changed files — _touchedFiles ∪ git diff (
|
|
109
|
+
// 1. Changed files — _touchedFiles ∪ git diff (D-VR1)
|
|
109
110
|
let gitOk = false
|
|
110
111
|
let gitStat = ""
|
|
111
112
|
let gitFiles = []
|
|
@@ -131,7 +132,7 @@ export const verifyTool = {
|
|
|
131
132
|
lines.push("Changed files: (not a git repo or git unavailable)")
|
|
132
133
|
}
|
|
133
134
|
// _touchedFiles: files written by the tools this run — captured regardless
|
|
134
|
-
// of git state (
|
|
135
|
+
// of git state (F-VR1: subagent cwd ≠ git root must still locate).
|
|
135
136
|
const touchedFiles = (ctx.agent._touchedFiles ?? []).map((p) => normalizeChangedPath(p, cwd))
|
|
136
137
|
const gitKeys = new Set(gitFiles.map(changedFileKey))
|
|
137
138
|
const touchedOnly = touchedFiles.filter((p) => !gitKeys.has(changedFileKey(p)))
|
|
@@ -173,10 +174,10 @@ export const verifyTool = {
|
|
|
173
174
|
// for doc changes, and the task list/self-review checklist add nothing either.
|
|
174
175
|
// Paths inside a declared code segment (default: src — incl. prompts/*.md) are
|
|
175
176
|
// product code — excluded from the fast path, consistent with the design gate.
|
|
176
|
-
// The project's own layout is declarable (
|
|
177
|
+
// The project's own layout is declarable (the manifest's codePaths).
|
|
177
178
|
// Empty list (no changes / git unavailable)
|
|
178
179
|
// intentionally falls through to the normal path below.
|
|
179
|
-
const conv =
|
|
180
|
+
const conv = loadProjectDeclaration(cwd)
|
|
180
181
|
if (changedFiles.length > 0 && changedFiles.every((f) => isDocPath(f, conv))) {
|
|
181
182
|
lines.push("")
|
|
182
183
|
lines.push("Documentation-only changes — skipping syntax checks and tests.")
|
package/agent-tools.mjs
CHANGED
|
@@ -21,7 +21,6 @@ export { batchTool } from "./agent-tools/batch.mjs"
|
|
|
21
21
|
// 批次档生命周期工具(BATCH-RECORD §4.3 挂载表——主名 `batch` 单工具四 action:depth-0 主 agent
|
|
22
22
|
// create/close + append §1/§4/§6 + status §1(轮 2 裁定②:§4/§6 状态面走普通文档写)(D-BR18);
|
|
23
23
|
// eng 子代理 eng 分支绑定段写入;评审 §3。
|
|
24
|
-
// 过渡别名 batchSegmentTool 不入登记册(§4.14——生产挂载面全切主名;别名仅供 shim 导出面)。
|
|
25
24
|
// CORE-UNIFICATION TOOLS #83(统一登记册——VSC `agent-tools/index.mjs:15` 含 consult 家族;
|
|
26
25
|
// CLI 原把 consult 另挂 `agent/setup.mjs:173` ⇒ 归位:登记册即单一来源,装配方只读本档)。
|
|
27
26
|
export { consultStartTool, consultStopTool } from "./agent-tools/consult.mjs"
|