thincoder 0.12.60 → 0.12.61
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 +24 -1
- package/README.md +8 -6
- package/bin/thincoder.mjs +27 -122
- package/package.json +3 -2
- package/src/abort-provenance.mjs +116 -0
- package/src/acp/bridge.mjs +38 -17
- package/src/acp.mjs +6 -1
- package/src/advisor/citations.mjs +83 -21
- package/src/advisor/compaction.mjs +174 -0
- package/src/advisor/loop.mjs +293 -0
- package/src/advisor/messages.mjs +36 -134
- package/src/advisor/project-context.mjs +194 -0
- package/src/advisor/repos.mjs +17 -40
- package/src/advisor/run.mjs +124 -329
- package/src/advisor/truncate.mjs +57 -0
- package/src/advisor.mjs +3 -2
- package/src/agent/completion.mjs +1 -1
- package/src/agent/dispatch.mjs +47 -12
- package/src/agent/helpers.mjs +71 -13
- package/src/agent/record-results.mjs +13 -5
- package/src/agent/relay-prefix.mjs +39 -0
- package/src/agent/run-stages.mjs +21 -6
- package/src/agent/setup-reminders.mjs +16 -9
- package/src/agent/setup.mjs +92 -128
- package/src/agent/spawn-child.mjs +26 -9
- package/src/agent-tools/advisor-async.mjs +70 -180
- package/src/agent-tools/advisor-settle.mjs +231 -0
- package/src/agent-tools/advisor.mjs +69 -20
- package/src/agent-tools/batch-segment.mjs +195 -0
- package/src/agent-tools/consult.mjs +23 -10
- package/src/agent-tools/design-token.mjs +14 -1
- package/src/agent-tools/digest-budget.mjs +76 -0
- package/src/agent-tools/eng.mjs +3 -3
- package/src/agent-tools/escalate-async.mjs +16 -13
- package/src/agent-tools/read-history.mjs +13 -3
- package/src/agent-tools/review-streak.mjs +93 -0
- package/src/agent-tools/settings.mjs +130 -17
- package/src/agent-tools/subagent-actions.mjs +15 -6
- package/src/agent-tools/subagent-async.mjs +66 -14
- package/src/agent-tools/subagent-panel.mjs +22 -15
- package/src/agent-tools/subagent-run.mjs +9 -6
- package/src/agent-tools/subagent-scheduler.mjs +57 -8
- package/src/agent-tools/subagent-spawn.mjs +63 -16
- package/src/agent-tools/subagent.mjs +175 -49
- package/src/agent-tools/verify.mjs +13 -34
- package/src/agent-tools.mjs +1 -0
- package/src/agent.mjs +38 -21
- package/src/cli/distill-command.mjs +2 -2
- package/src/cli/make-agent.mjs +23 -7
- package/src/cli/memory-command.mjs +2 -2
- package/src/cli/setup-wizard.mjs +29 -9
- package/src/completions.mjs +114 -0
- package/src/config-migrate.mjs +70 -0
- package/src/config.mjs +132 -63
- package/src/conventions.mjs +223 -0
- package/src/crash-reports.mjs +7 -2
- package/src/expand-home.mjs +16 -0
- package/src/generate-title.mjs +1 -1
- package/src/hooks.mjs +7 -3
- package/src/memory/code-index.mjs +9 -3
- package/src/memory/code-sync.mjs +70 -31
- package/src/memory/delete.mjs +2 -0
- package/src/memory/docs.mjs +10 -6
- package/src/memory/file-walk.mjs +109 -0
- package/src/memory/schema.mjs +15 -3
- package/src/model-ref.mjs +66 -0
- package/src/model-specs.mjs +42 -8
- package/src/prompt-overlays.mjs +73 -16
- package/src/prompts/advisor-design.md +18 -8
- package/src/prompts/advisor-round1.md +7 -1
- package/src/prompts/advisor-round2.md +13 -2
- package/src/prompts/advisor-round3.md +13 -2
- package/src/prompts/common.md +115 -0
- package/src/prompts/consult-base.md +2 -0
- package/src/prompts/discipline-engineering.md +217 -0
- package/src/prompts/discipline-normal.md +179 -0
- package/src/prompts/persona-coder.md +21 -0
- package/src/prompts/persona-eng-coder.md +37 -0
- package/src/prompts/persona-eng-designer.md +55 -0
- package/src/prompts/persona-engineering.md +54 -0
- package/src/prompts/persona-explore.md +15 -0
- package/src/prompts/persona-normal.md +27 -0
- package/src/prompts/persona-plan.md +26 -0
- package/src/provider/anthropic.mjs +4 -4
- package/src/provider/core.mjs +13 -32
- package/src/provider/errors.mjs +26 -1
- package/src/provider/google.mjs +5 -6
- package/src/provider/index.mjs +2 -1
- package/src/provider/list-models.mjs +93 -0
- package/src/provider/rate.mjs +2 -1
- package/src/provider/responses.mjs +5 -3
- package/src/provider/sse.mjs +3 -4
- package/src/proxy.mjs +9 -14
- package/src/session-slots.mjs +7 -2
- package/src/session.mjs +59 -38
- package/src/token-ttl.mjs +2 -1
- package/src/tools/{system.mjs → bash.mjs} +6 -243
- package/src/tools/file.mjs +30 -10
- package/src/tools/git.md +1 -1
- package/src/tools/git.mjs +15 -34
- package/src/tools/index.mjs +4 -2
- package/src/tools/ops.mjs +20 -7
- package/src/tools/question.md +1 -0
- package/src/tools/question.mjs +26 -0
- package/src/tools/read.md +1 -1
- package/src/tools/read_image.md +1 -1
- package/src/tools/search.mjs +236 -0
- package/src/tui/agent-turn.mjs +32 -13
- package/src/tui/ansi.mjs +2 -0
- package/src/tui/clipboard.mjs +7 -1
- package/src/tui/cmd-advisor.mjs +3 -2
- package/src/tui/cmd-config.mjs +108 -37
- package/src/tui/cmd-eng.mjs +11 -27
- package/src/tui/cmd-exit.mjs +6 -8
- package/src/tui/cmd-model.mjs +14 -12
- package/src/tui/cmd-reindex.mjs +7 -0
- package/src/tui/cmd-submodel.mjs +8 -5
- package/src/tui/cmd-undo.mjs +4 -3
- package/src/tui/index.mjs +38 -37
- package/src/tui/key-handler.mjs +61 -17
- package/src/tui/key-modes.mjs +86 -8
- package/src/tui/layout.mjs +18 -10
- package/src/tui/model-catalog.mjs +89 -0
- package/src/tui/model-picker.mjs +498 -0
- package/src/tui/mouse.mjs +47 -10
- package/src/tui/pickers.mjs +28 -410
- package/src/tui/render-frame.mjs +32 -16
- package/src/tui/render-loop.mjs +2 -0
- package/src/tui/render-segments.mjs +5 -19
- package/src/tui/render.mjs +37 -5
- package/src/tui/slash-commands.mjs +2 -2
- package/src/tui/subagent-blocks.mjs +69 -90
- package/src/tui/subagent-children.mjs +50 -64
- package/src/tui/subagent-freeze.mjs +40 -43
- package/src/tui/subagent-panel.mjs +21 -66
- package/src/tui/suspension-drive.mjs +28 -82
- package/src/tui/tool-args.mjs +4 -2
- package/src/tui/tool-display.mjs +4 -3
- package/src/tui/tool-events.mjs +23 -16
- package/src/tui/tui-lifecycle.mjs +18 -5
- package/src/tui/wizard.mjs +52 -18
- package/src/tui/wrapped-spawn.mjs +38 -0
- package/src/prompts/coder.md +0 -13
- package/src/prompts/discipline.md +0 -84
- package/src/prompts/eng-coder.md +0 -19
- package/src/prompts/engineering-sub.md +0 -14
- package/src/prompts/engineering.md +0 -87
- package/src/prompts/explore.md +0 -12
- package/src/prompts/main.md +0 -34
- package/src/prompts/methodology-template.md +0 -38
- package/src/prompts/plan.md +0 -9
- package/src/prompts/system.md +0 -44
package/src/agent/setup.mjs
CHANGED
|
@@ -10,15 +10,13 @@ import { search as memorySearch, docSearch } from "../memory.mjs"
|
|
|
10
10
|
import { pushReal } from "../context.mjs"
|
|
11
11
|
import { toOpenAISchema } from "../tools/index.mjs"
|
|
12
12
|
import { loadSkills, formatSkillListing } from "../skills.mjs"
|
|
13
|
+
import { assemblePrompt } from "../prompt-overlays.mjs"
|
|
13
14
|
import {
|
|
14
15
|
escapeXml, repairHistory, listWorkDir,
|
|
15
16
|
collectGitContext, loadProjectInstructions, OUTLINE_INJECT_PREFIX,
|
|
16
17
|
DEFAULT_MAX_TURNS, ensureAutoReminder,
|
|
17
18
|
} from "./helpers.mjs"
|
|
18
19
|
import { pushEnvStateReminder, pushPeerReminder } from "./setup-reminders.mjs"
|
|
19
|
-
import { readFileSync, existsSync } from "node:fs"
|
|
20
|
-
import { resolve, dirname } from "node:path"
|
|
21
|
-
import { fileURLToPath } from "node:url"
|
|
22
20
|
|
|
23
21
|
const DEFAULT_COMPACT_THRESHOLD = 100_000
|
|
24
22
|
const DOC_SEARCH_LIMIT = 5
|
|
@@ -36,45 +34,12 @@ function safeSliceUTF16(text, max) {
|
|
|
36
34
|
}
|
|
37
35
|
const MEMORY_SEARCH_LIMIT = 3
|
|
38
36
|
|
|
39
|
-
/** Build engineering-mode system prompt by reading METHODOLOGY.md and wrapping it in the engineering template */
|
|
40
|
-
export async function buildEngineeringPrompt(cwd, role) {
|
|
41
|
-
const engFile = role === "eng-coder" ? "engineering-sub.md" : "engineering.md"
|
|
42
|
-
const engTemplatePath = resolve(dirname(fileURLToPath(import.meta.url)), "..", "prompts", engFile)
|
|
43
|
-
let engTemplate = ""
|
|
44
|
-
let templateMissing = false
|
|
45
|
-
try { engTemplate = readFileSync(engTemplatePath, "utf8") } catch {
|
|
46
|
-
templateMissing = true
|
|
47
|
-
if (role === "eng-coder") console.warn(`[setup] engineering-sub.md missing — eng-coder will run with degraded engineering constraints. Path: ${engTemplatePath}`)
|
|
48
|
-
else console.warn(`[setup] engineering.md missing — engineering mode will use template-only constraints. Path: ${engTemplatePath}`)
|
|
49
|
-
}
|
|
50
|
-
const methodologyPath = resolve(cwd, "METHODOLOGY.md")
|
|
51
|
-
if (!existsSync(methodologyPath)) {
|
|
52
|
-
// Template-only — engineering constraints stay active, minus project rules.
|
|
53
|
-
// The caller injects a warning into the history. Resolve the built-in
|
|
54
|
-
// methodology template to an absolute path (same-source join as the
|
|
55
|
-
// engineering template above — the packaged path is unreachable from the
|
|
56
|
-
// user's cwd) and carry its body so the warning can embed it verbatim
|
|
57
|
-
// (2026-09-02 D-M1/D-M2: template reachability for the model).
|
|
58
|
-
const methodologyTemplatePath = resolve(dirname(fileURLToPath(import.meta.url)), "..", "prompts", "methodology-template.md")
|
|
59
|
-
let methodologyTemplateBody = null
|
|
60
|
-
try { methodologyTemplateBody = readFileSync(methodologyTemplatePath, "utf8") } catch {
|
|
61
|
-
// Template unreadable (packaging) — degraded: base warning only (no path/body injected), same as VS Code.
|
|
62
|
-
}
|
|
63
|
-
return { prompt: engTemplate || null, templateMissing, methodologyMissing: true, methodologyTemplatePath, methodologyTemplateBody }
|
|
64
|
-
}
|
|
65
|
-
const methodology = readFileSync(methodologyPath, "utf8")
|
|
66
|
-
const prompt = engTemplate
|
|
67
|
-
? `${engTemplate}\n\n---\n\n## Project METHODOLOGY.md\n\n${methodology}`
|
|
68
|
-
: `[ENGINEERING MODE]\n\nFollow this methodology strictly:\n\n${methodology}`
|
|
69
|
-
return { prompt, templateMissing, methodologyMissing: false }
|
|
70
|
-
}
|
|
71
|
-
|
|
72
37
|
/**
|
|
73
38
|
* Prepare an agent run: inject context, build system prompt, inject tools.
|
|
74
39
|
* Returns all state needed by the main loop, and writes initialization messages into agent.history.
|
|
75
40
|
*/
|
|
76
41
|
export async function prepareRun(agent, input, callbacks, {
|
|
77
|
-
depth = 0, signal, overrideTurns, resume,
|
|
42
|
+
depth = 0, signal, overrideTurns, resume,
|
|
78
43
|
} = {}) {
|
|
79
44
|
const maxTurns = overrideTurns ?? agent.config?.agent?.maxTurns ?? DEFAULT_MAX_TURNS
|
|
80
45
|
const threshold = agent.config?.agent?.compactThreshold ?? DEFAULT_COMPACT_THRESHOLD
|
|
@@ -84,21 +49,21 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
84
49
|
agent.history = repairHistory(agent.history)
|
|
85
50
|
|
|
86
51
|
if (!resume) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
52
|
+
// Git context: branch, recent commits, uncommitted changes
|
|
53
|
+
// GIT-ASYNC L21:collectGitContext → async(3×execFile 并行 + 失败冷却)——await
|
|
54
|
+
if (depth === 0) {
|
|
55
|
+
const gitCtx = await collectGitContext(agent.cwd)
|
|
56
|
+
if (gitCtx) {
|
|
57
|
+
agent.history.push({
|
|
58
|
+
role: "user",
|
|
59
|
+
content: `[System reminder: git context:\n${escapeXml(gitCtx)}]`,
|
|
60
|
+
transient: true,
|
|
61
|
+
})
|
|
97
62
|
}
|
|
63
|
+
}
|
|
98
64
|
if (depth === 0) {
|
|
99
65
|
const tree = listWorkDir(agent.cwd)
|
|
100
66
|
const platform = { win32: 'Windows', darwin: 'macOS', linux: 'Linux' }[process.platform] ?? process.platform
|
|
101
|
-
const wasRestored = agent._sessionStart != null
|
|
102
67
|
agent._sessionStart ??= new Date().toISOString()
|
|
103
68
|
// Inject OS/cwd + cwd-tree only ONCE per process (restored sessionStart keeps the
|
|
104
69
|
// content byte-identical across restarts — 2026-08-16 cache audit). Without the guard
|
|
@@ -112,11 +77,14 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
112
77
|
}
|
|
113
78
|
|
|
114
79
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
80
|
+
// SESSION.md §11.2(2026-09-08——N6 评审 🔴 修复:注入句解耦——双信号独立消费):
|
|
81
|
+
// process restarted 句 = 进程级信号 _processRestartPending——仅 TUI 启动 resume 路径
|
|
82
|
+
// (bin/thincoder.mjs resumeSlot→applySession)设一次;/session 切换与 ACP 加载不设。
|
|
83
|
+
// 发句即清——进程内只发一次。resumed:yes 走 _envResumed(applySession 载入历史非空
|
|
84
|
+
// 时武装——每次恢复一次——pushEnvStateReminder 读即清)——互不绑门:切槽恢复只发
|
|
85
|
+
// resumed:yes、不误报进程重启(F3 伪触发消除——不再用 _sessionStart != null 推断)。
|
|
86
|
+
if (agent._processRestartPending) {
|
|
87
|
+
agent._processRestartPending = false
|
|
120
88
|
agent.history.push({ role: "user", content: `[System reminder: process restarted at ${new Date().toISOString()}.]`, transient: true })
|
|
121
89
|
}
|
|
122
90
|
if (agent.memory && !agent.history.some((m) => typeof m.content === "string" && m.content.startsWith(OUTLINE_INJECT_PREFIX))) {
|
|
@@ -201,9 +169,8 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
201
169
|
|
|
202
170
|
// task/plan tools are injected with the main loop; subagent/skill/goal/verify only at top level
|
|
203
171
|
// eng-coder subagents get advisor for mandatory design review before coding
|
|
204
|
-
const { planTool, subagentTool, taskTool, skillTool, goalTool, verifyTool, recentChangesTool, timerTool, advisorTool, engTool, readHistoryTool } = await import("../agent-tools.mjs")
|
|
172
|
+
const { planTool, subagentTool, taskTool, skillTool, goalTool, verifyTool, recentChangesTool, timerTool, advisorTool, engTool, readHistoryTool, batchSegmentTool } = await import("../agent-tools.mjs")
|
|
205
173
|
const { consultStartTool, consultStopTool } = await import("../agent-tools/consult.mjs")
|
|
206
|
-
const { CONSULT_BASE } = await import("../agent.mjs")
|
|
207
174
|
// withPool: decorate the consult_start description with the CURRENT candidate pool
|
|
208
175
|
// so the model knows which models it can pick (CLI parity with the plugin). The
|
|
209
176
|
// retired escalate tool surface is now the subagent action:"escalate" — its pool
|
|
@@ -214,12 +181,12 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
214
181
|
if (!list) return tool
|
|
215
182
|
return { ...tool, description: tool.description + `\nCurrently configured consultants (this tool's pool): ${list}` }
|
|
216
183
|
}
|
|
217
|
-
// Role enum is mutually exclusive: normal mode has "coder", engineering mode has "eng-coder"
|
|
184
|
+
// Role enum is mutually exclusive: normal mode has "coder", engineering mode has "eng-coder"/"eng-designer"
|
|
218
185
|
const subagentRoles = (depth === 0 && agent.config?.agent?.engineering)
|
|
219
186
|
? {
|
|
220
|
-
enum: ["explore", "plan", "eng-coder"],
|
|
187
|
+
enum: ["explore", "plan", "eng-designer", "eng-coder"],
|
|
221
188
|
description: "The sub-agent role — see the tool description for the role capability matrix. Exact spelling required.",
|
|
222
|
-
suffix: " In engineering mode, use role='eng-coder' for implementation (coder is disabled).",
|
|
189
|
+
suffix: " In engineering mode, use role='eng-coder' for implementation (coder is disabled) and role='eng-designer' for writing the requirements/design documents.",
|
|
223
190
|
}
|
|
224
191
|
: {
|
|
225
192
|
enum: ["explore", "plan", "coder"],
|
|
@@ -248,39 +215,49 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
248
215
|
},
|
|
249
216
|
} : subagentTool
|
|
250
217
|
|
|
251
|
-
// §18 D-E3
|
|
252
|
-
//
|
|
253
|
-
// to
|
|
254
|
-
//
|
|
255
|
-
//
|
|
256
|
-
//
|
|
257
|
-
|
|
218
|
+
// §18 D-E3 + ENGINEERING-MODE.md §2.15 D(第 2 批——参数化复用,不并列第二个 IIFE):
|
|
219
|
+
// 工程子代理(depth>0;eng-coder = 偏差审计 / eng-designer = 自己勘察)get a restricted
|
|
220
|
+
// spawn channel — role enum limited to explore, NO async parameter (sync only) and action
|
|
221
|
+
// pinned to spawn(§19 D-M3 restricted-variant action gate——escalate/check/status are
|
|
222
|
+
// refused here at the schema level too;the mechanical re-check lives in subagent.mjs
|
|
223
|
+
// execute → the §19 action gate + gateEngCoderSpawn (spawn-child.mjs) — schema enums are
|
|
224
|
+
// advisory, providers don't enforce them)。描述文案按父角色分流(审计 vs 勘察)。
|
|
225
|
+
const engChildRole = depth > 0 && (agent._role === "eng-coder" || agent._role === "eng-designer") ? agent._role : null
|
|
226
|
+
const engChildSubagent = engChildRole
|
|
258
227
|
? (() => {
|
|
259
228
|
const props = { ...subagentTool.parameters.properties }
|
|
260
|
-
// §19 review hygiene: the
|
|
261
|
-
// async, the check/status params (id/n)
|
|
262
|
-
// (designToken/designId are meaningless for a read-only
|
|
263
|
-
//
|
|
229
|
+
// §19 review hygiene: the child channel is spawn-only sync explore — drop
|
|
230
|
+
// async, the check/status params (id/n), the eng-coder token params
|
|
231
|
+
// (designToken/designId are meaningless for a read-only spawn; the parent
|
|
232
|
+
// spawn already carried the token) and batchDoc (an audit child derives no
|
|
233
|
+
// batch parameter — its task book rides the mechanical summary of the
|
|
234
|
+
// parent's _engTaskInput instead). Schema noise would invite the
|
|
264
235
|
// model to pass irrelevant args.
|
|
265
|
-
delete props.async // sync only — the
|
|
236
|
+
delete props.async // sync only — the parent blocks on the child's report
|
|
266
237
|
delete props.id
|
|
267
238
|
delete props.n
|
|
268
239
|
delete props.designToken
|
|
269
240
|
delete props.designId
|
|
241
|
+
delete props.batchDoc
|
|
242
|
+
const designer = engChildRole === "eng-designer"
|
|
270
243
|
props.role = {
|
|
271
244
|
type: "string",
|
|
272
245
|
enum: ["explore"],
|
|
273
|
-
description:
|
|
246
|
+
description: designer
|
|
247
|
+
? "explore only — the eng-designer's internal spawn channel is reserved for read-only surveys of the current state (ENGINEERING-MODE.md §2.15 D; ≤6 spawns per batch)."
|
|
248
|
+
: "explore only — the eng-coder's internal spawn channel is reserved for read-only divergence audits (AGENT-LOOP.md §18 D-E3).",
|
|
274
249
|
}
|
|
275
250
|
props.action = {
|
|
276
251
|
type: "string",
|
|
277
252
|
enum: ["spawn"],
|
|
278
|
-
description:
|
|
253
|
+
description: `spawn only — the ${engChildRole}'s internal spawn channel is read-only (escalate/status/cancel/panel/consume-design/observe/send are refused: escalate spawns a coder+WRITE child, and the pool/panel actions have no async pool or panel mirror in a child context).`,
|
|
279
254
|
}
|
|
280
255
|
return {
|
|
281
256
|
...subagentTool,
|
|
282
257
|
name: "subagent",
|
|
283
|
-
description:
|
|
258
|
+
description: designer
|
|
259
|
+
? "Spawn a read-only `explore` sub-agent to SURVEY the current state for the design (ENGINEERING-MODE.md §2.15 D): it reads code / docs / existing designs and reports evidence with file:line. BLOCKING ONLY (no async) and action:'spawn' ONLY — the survey channel is read-only; escalate/status/cancel/panel/consume-design/observe/send are not available (AGENT-LOOP.md §19). Survey budget: ≤6 explore spawns per batch — the main agent's survey result is reference only; do your own."
|
|
260
|
+
: "Spawn a read-only `explore` sub-agent to AUDIT your delivery against the design (AGENT-LOOP.md §18 D-E2 ③): it compares the delivered code with the design for divergence — partially implemented acceptance criteria, silent simplifications, doc drift, changes outside the approved file list. BLOCKING ONLY (no async — the audit report decides your next protocol step). action:'spawn' ONLY — the audit channel is a read-only spawn; escalate/status/cancel/panel/consume-design/observe/send are not available (AGENT-LOOP.md §19). The audit task book is appended MECHANICALLY — your own spawn task (docs involved / acceptance criteria / file list) plus the files you actually touched; never hand the audit a self-written file list (a self-report could omit exactly the out-of-scope file it must catch).",
|
|
284
261
|
parameters: { ...subagentTool.parameters, properties: props },
|
|
285
262
|
}
|
|
286
263
|
})()
|
|
@@ -304,70 +281,57 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
304
281
|
// escalate hit "unknown tool" and fell back to bash node --check / npm test to
|
|
305
282
|
// self-verify (2026-08-16 deepseek escalate diagnosis; plugin parity).
|
|
306
283
|
// eng-coder: advisor + verify + the §18 audit-only subagent channel (D-E3).
|
|
307
|
-
|
|
284
|
+
// eng-designer (§2.15 D): the survey-only subagent channel alone — no advisor
|
|
285
|
+
// (it does not fire reviews) and no verify (its deliverable is documents, not code).
|
|
286
|
+
// §2.20.3(第 4 批):两分支各追加 batch_segment——目标档 = spawn 时绑定的
|
|
287
|
+
// `agent._batchDoc`(§2.20.2);主 agent 不挂载(§1/§4/§6 走普通文档写)。
|
|
288
|
+
: engChildRole === "eng-coder" ? [advisorTool, verifyTool, batchSegmentTool(agent._batchDoc ?? null), ...(engChildSubagent ? [engChildSubagent] : [])]
|
|
289
|
+
: engChildRole === "eng-designer" ? [batchSegmentTool(agent._batchDoc ?? null), ...(engChildSubagent ? [engChildSubagent] : [])]
|
|
308
290
|
: agent._role === "coder" ? [verifyTool, advisorTool]
|
|
309
291
|
: agent._role === "consult" ? [recentChangesTool]
|
|
310
292
|
: []
|
|
311
|
-
// NOTE: every depth-0 tool schema is estimated into the compaction overhead per turn
|
|
312
|
-
// (context.mjs extras.tools) — a tool-schema change shifts the compaction fixture
|
|
313
|
-
// knife-edges (agent.test T3b: read_history's schema +~470 tokens once crossed its
|
|
314
|
-
// 11000 threshold; fixture adjusted to 12500 — rationale in the test comment).
|
|
315
293
|
const tools = [...agent.tools, taskTool, planTool, timerTool, ...depthOnly]
|
|
316
294
|
const toolSchemas = tools.map(toOpenAISchema)
|
|
317
295
|
const toolByName = new Map(tools.map((t) => [t.name, t]))
|
|
318
296
|
agent._onTaskUpdate = callbacks.onTaskUpdate
|
|
319
297
|
|
|
320
|
-
// system prompt
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (engResult.methodologyTemplateBody) {
|
|
352
|
-
warning += `\n\nbuilt-in template(可 read ${engResult.methodologyTemplatePath} 或直接参考以下内容):\n\n${engResult.methodologyTemplateBody}`
|
|
353
|
-
}
|
|
354
|
-
warnings.push(warning)
|
|
355
|
-
}
|
|
356
|
-
if (warnings.length > 0) {
|
|
357
|
-
agent.history.push({
|
|
358
|
-
role: "user",
|
|
359
|
-
content: `[System reminder: ENGINEERING MODE is active but ${warnings.join(" ")}]`,
|
|
360
|
-
})
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
} else {
|
|
364
|
-
base = needsDiscipline ? `${corePrompt}\n\n${disciplineRules}` : corePrompt
|
|
298
|
+
// ── system prompt ── PROMPT-SYSTEM 施工② G2/G3(2026-09-10):四槽位装配函数
|
|
299
|
+
// assemblePrompt({scenario}) 表驱动(D1 场景表 = 蓝图 §3.2 装配矩阵)——取代旧
|
|
300
|
+
// consult/工程/普通三分支 + overlay 前缀。固定序 人格→common→纪律(§3.1);[4] 层
|
|
301
|
+
// (AGENTS + skills)由下方既有尾部逻辑承担。降级链(蓝图 §3.4):人格/纪律/common
|
|
302
|
+
// 槽文件缺失 → 该槽空缺跳过 + 醒目警告(不 fallback 其他槽——层间隔离);AGENTS.md
|
|
303
|
+
// 缺失 → loadProjectInstructions 静默跳过(无警告——既有语义)。
|
|
304
|
+
// consult = 特殊模块(§3.3)——CONSULT_BASE 自含基底直接返回,不入主链、无四槽。
|
|
305
|
+
// eng-coder 场景即工程纪律(G6——subagent-spawn 的 childConfig engineering=true
|
|
306
|
+
// 强制语义由此表行承载:scenario=eng-coder → discipline-engineering 槽)。
|
|
307
|
+
const { prompt: base, warnings: slotWarnings } = assemblePrompt(
|
|
308
|
+
agent._role === "consult"
|
|
309
|
+
? "consult"
|
|
310
|
+
: (depth === 0 || engChildRole) && agent.config?.agent?.engineering
|
|
311
|
+
? (engChildRole ?? "engineering")
|
|
312
|
+
: (depth === 0 ? "normal" : agent._role ?? "normal"),
|
|
313
|
+
)
|
|
314
|
+
// D2 警告通道 = 既有 setup 警告通道(history 注入)——不新增机制。深度 0 才注入
|
|
315
|
+
// (子代理警告不打扰主会话历史——旧工程警告块同款深度门)。
|
|
316
|
+
// ── Q1 审计收敛(蓝图 §3.4 第 4 款):特殊模块基底缺失 → 该模块不可用报错(不自降级
|
|
317
|
+
// ——空基底绝不可静默上岗)。consult 场景在外部消费点收口:入历史后抛错(Agent 循环
|
|
318
|
+
// → 历史已含错误句可见——不静默、不降级)。四槽场景维持跳过+警告降级链(AC-2 三款)。
|
|
319
|
+
if (agent._role === "consult" && !base) {
|
|
320
|
+
const msg = "[Consult module unavailable: prompts/consult-base.md missing — the consultation module refuses to degrade (蓝图 §3.4 特殊模块不自降级). Check the installation's prompts directory.]"
|
|
321
|
+
agent.history.push({ role: "user", content: msg })
|
|
322
|
+
throw new Error(`consult-base.md missing — consultation module unavailable (no degraded fallback per PROMPT-SYSTEM §3.4)`)
|
|
323
|
+
}
|
|
324
|
+
if (depth === 0 && slotWarnings.length > 0) {
|
|
325
|
+
agent.history.push({
|
|
326
|
+
role: "user",
|
|
327
|
+
content: `[System reminder: prompt slots degraded — ${slotWarnings.join(" ")}]`,
|
|
328
|
+
})
|
|
365
329
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
330
|
+
// G3(施工②审计收敛):overlay(人格)前缀分支退役——CLI/VSC 同构(人格槽由场景表
|
|
331
|
+
// 承载,spawn 侧 overlay 恒空——createAgent 的 overlay 参数留空兼容位)。
|
|
332
|
+
// let——下方按 projectRules / skills listing 尾部追加(L326/L331 +=)再赋值;
|
|
333
|
+
// const 声明会 TypeError: Assignment to constant variable(每次 run 必炸)。
|
|
334
|
+
let systemPrompt = base
|
|
371
335
|
|
|
372
336
|
// Time injection deliberately does NOT live here: system prompts must be byte-identical
|
|
373
337
|
// across runs (provider prefix caches). The time rides a transient user reminder per turn
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { specForModel } from "../config.mjs"
|
|
18
18
|
import { ContinueError } from "../agent.mjs"
|
|
19
|
+
import { relayPrefixOf } from "./relay-prefix.mjs"
|
|
20
|
+
// 第 27 批 §12.3①:relay 前缀文法单一权威模块(`src/agent/relay-prefix.mjs`)——
|
|
21
|
+
// 生成侧枢纽再导出(TUI/ACP 消费方可经此导入;消费方按 §12.3① 直连模块亦可)。
|
|
22
|
+
export { RELAY_PREFIX_RE, parseRelayPath, relayPrefixOf } from "./relay-prefix.mjs"
|
|
19
23
|
|
|
20
24
|
/** 事件 token 哨兵串(D1)——LLM 正常内容混淆概率极低;字段分隔用 RS (\x1e)。 */
|
|
21
25
|
export const EVENT_SENTINEL = "⟦ev⟧"
|
|
@@ -26,23 +30,36 @@ const RS = "\x1e"
|
|
|
26
30
|
* 单源化(2026-08-30 评审):文案演进只改这里,消除文案与检测正则的漂移面。 */
|
|
27
31
|
export const TURN_CAP_MARK = "stopped: turn cap reached"
|
|
28
32
|
|
|
33
|
+
/** SYNC-CANCEL(L52):sync spawn ⏹ 定向中止折叠报告的公共锚点——sync 折叠文案必含
|
|
34
|
+
* 此子串(buildSyncStoppedReport);消费端(TUI tool-events onToolResult)用
|
|
35
|
+
* includes() 检测"用户定向中止——工作可能不完整"语义(块冻结标 stopped 而非 done——
|
|
36
|
+
* R6)。与 TURN_CAP_MARK 同族单源纪律。 */
|
|
37
|
+
export const STOPPED_MARK = "stopped by user"
|
|
38
|
+
|
|
29
39
|
/**
|
|
30
|
-
* §18 D-E3
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
40
|
+
* §18 D-E3 工程子代理内部 spawn 机械门(AGENT-LOOP.md §18 D-E2 round5 #2 后备 +
|
|
41
|
+
* ENGINEERING-MODE.md §2.15 D):eng-coder(偏差审计)与 **eng-designer(自己勘察)**
|
|
42
|
+
* 同为受限通道——depth>0 且父角色 ∈ 集合时,内部 spawn 只允许 role='explore'、
|
|
43
|
+
* async 强制同步。
|
|
44
|
+
* **审计预算(6)只计 eng-coder**(designer 勘察非审计——designer 父路径校验通过后
|
|
45
|
+
* 返回 null:不计数、且调用方以 `engAuditAttempt !== null` 为审计任务书注入开关——
|
|
46
|
+
* 非 null 会把审计范围误注进勘察任务书)。
|
|
47
|
+
* 返回 null = 非工程子代理上下文(不加限制)或 designer 勘察路径(无须计数);
|
|
48
|
+
* 返回审计尝试序号 = eng-coder 审计通过。
|
|
35
49
|
* schema 层过滤(setup.mjs 受限变体)只是给模型的参数提示——本函数是机械强制。
|
|
36
50
|
*/
|
|
37
51
|
export const ENG_AUDIT_SPAWN_LIMIT = 6 // 允许 6 次审计 spawn;第 7 次拒绝
|
|
38
52
|
export function gateEngCoderSpawn(parent, depth, role, async) {
|
|
39
|
-
|
|
53
|
+
const parentRole = parent?._role
|
|
54
|
+
if ((depth ?? 0) <= 0 || (parentRole !== "eng-coder" && parentRole !== "eng-designer")) return null
|
|
40
55
|
if (role !== "explore") {
|
|
41
|
-
throw new Error(
|
|
56
|
+
throw new Error(`${parentRole} subagents may only spawn role='explore' — internal spawns exist solely for read-only work (the eng-coder divergence audit / the designer's own survey) (AGENT-LOOP.md §18 D-E3, ENGINEERING-MODE.md §2.15 D)`)
|
|
42
57
|
}
|
|
43
58
|
if (async === true) {
|
|
44
|
-
throw new Error(
|
|
59
|
+
throw new Error(`${parentRole} internal spawns are sync-only — the child's report must return before the next protocol step; async spawn is only available at the top level (AGENT-LOOP.md §18 D-E3)`)
|
|
45
60
|
}
|
|
61
|
+
// designer 勘察路径:校验通过即返回 null(非审计——不计数、不触发审计任务书注入)
|
|
62
|
+
if (parentRole === "eng-designer") return null
|
|
46
63
|
const attempt = (parent._engAuditSpawns ?? 0) + 1
|
|
47
64
|
if (attempt > ENG_AUDIT_SPAWN_LIMIT) {
|
|
48
65
|
throw new Error("correction-round limit exceeded — deliver a stalled report (AGENT-LOOP.md §18: max 5 fix rounds; the 7th audit spawn is refused mechanically)")
|
|
@@ -58,7 +75,7 @@ export function gateEngCoderSpawn(parent, depth, role, async) {
|
|
|
58
75
|
*/
|
|
59
76
|
export function makeRelay(parent, label, emit, model) {
|
|
60
77
|
parent._subAgentCounter = (parent._subAgentCounter ?? 0) + 1
|
|
61
|
-
const relayPrefix =
|
|
78
|
+
const relayPrefix = relayPrefixOf(label, parent._subAgentCounter)
|
|
62
79
|
emit?.(relayPrefix + "[model]" + (model ?? ""))
|
|
63
80
|
return relayPrefix
|
|
64
81
|
}
|