thincoder 0.12.49 → 0.12.51
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 +45 -2
- package/package.json +4 -3
- package/src/acp/bridge.mjs +4 -0
- package/src/agent/dispatch.mjs +19 -7
- package/src/agent/helpers.mjs +12 -0
- package/src/agent/record-results.mjs +130 -0
- package/src/agent/setup.mjs +5 -8
- package/src/agent/spawn-child.mjs +159 -0
- package/src/agent-tools/consult.mjs +95 -73
- package/src/agent-tools/escalate.mjs +53 -62
- package/src/agent-tools/subagent.mjs +39 -38
- package/src/agent.mjs +25 -109
- package/src/generate-title.mjs +30 -1
- package/src/prompts/advisor-round1.md +5 -6
- package/src/prompts/advisor-round2.md +3 -4
- package/src/prompts/advisor-round3.md +3 -4
- package/src/prompts/eng-coder.md +10 -0
- package/src/prompts/engineering.md +81 -14
- package/src/prompts/methodology-template.md +8 -3
- package/src/prompts/system.md +1 -1
- package/src/session.mjs +48 -1
- package/src/tools/system.mjs +3 -1
- package/src/tui/agent-turn.mjs +44 -363
- package/src/tui/cmd-advisor.mjs +20 -2
- package/src/tui/cmd-eng.mjs +44 -7
- package/src/tui/dims.mjs +74 -0
- package/src/tui/fold-block.mjs +208 -0
- package/src/tui/index.mjs +53 -16
- package/src/tui/key-handler-search.mjs +1 -1
- package/src/tui/key-handler.mjs +9 -6
- package/src/tui/layout.mjs +21 -20
- package/src/tui/mouse.mjs +8 -6
- package/src/tui/pickers.mjs +1 -1
- package/src/tui/render-conversation.mjs +368 -113
- package/src/tui/render-frame.mjs +9 -88
- package/src/tui/render-loop.mjs +12 -8
- package/src/tui/render.mjs +5 -0
- package/src/tui/startup.mjs +67 -13
- package/src/tui/subagent-blocks.mjs +326 -0
- package/src/tui/tool-args.mjs +67 -0
- package/src/tui/tool-events.mjs +461 -0
package/src/agent.mjs
CHANGED
|
@@ -10,16 +10,17 @@ import { readFileSync } from "node:fs"
|
|
|
10
10
|
import { join, dirname } from "node:path"
|
|
11
11
|
import { fileURLToPath } from "node:url"
|
|
12
12
|
import { executeToolCalls } from "./agent/dispatch.mjs"
|
|
13
|
+
import { recordToolResults } from "./agent/record-results.mjs"
|
|
13
14
|
import { prepareRun } from "./agent/setup.mjs"
|
|
14
15
|
import { injectPostTurn, STALL_WINDOW_SIZE, STALL_THRESHOLD, GOAL_BUDGET_WARN_RATIO } from "./agent/post-turn.mjs"
|
|
15
16
|
import { handleCompletion } from "./agent/completion.mjs"
|
|
16
17
|
import { cleanupConsultSessions } from "./agent-tools/consult.mjs"
|
|
17
18
|
import {
|
|
18
|
-
escapeXml,
|
|
19
|
+
escapeXml, repairHistory, listWorkDir, ensureAutoReminder,
|
|
19
20
|
readonlyToolNames, collectGitContext, loadProjectInstructions,
|
|
20
|
-
ContinueError,
|
|
21
|
+
ContinueError,
|
|
21
22
|
DEFAULT_MAX_TURNS, DEFAULT_SUBAGENT_TURNS,
|
|
22
|
-
MIN_REPORT_CHARS, REPORT_CONTINUATION,
|
|
23
|
+
MIN_REPORT_CHARS, REPORT_CONTINUATION,
|
|
23
24
|
} from "./agent/helpers.mjs"
|
|
24
25
|
|
|
25
26
|
// Prompt files (byte-stable, loaded once)
|
|
@@ -47,8 +48,6 @@ export {
|
|
|
47
48
|
MIN_REPORT_CHARS, REPORT_CONTINUATION, DEFAULT_SUBAGENT_TURNS,
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
let _reindexFile = null
|
|
51
|
-
const AUTO_REMINDER = "[System reminder: AUTO mode is active — all tool calls are automatically approved without asking.]"
|
|
52
51
|
|
|
53
52
|
// Engineering mode reminder — shared with eng.mjs tool
|
|
54
53
|
export const ENG_ON_REMINDER =
|
|
@@ -103,7 +102,7 @@ export function createAgent({
|
|
|
103
102
|
_emptyRetries: 0, // empty-response retry budget (per-run; reset on a fresh user turn)
|
|
104
103
|
_runStartHistoryLen: 0, // machine-line length at the start of the current run — end-of-run exploration distillation slices from here
|
|
105
104
|
_pendingDistill: null, // in-flight end-of-run exploration distillation (SEND-STALL-DISTILL §2.1) — awaited at next run start / TUI exit flush
|
|
106
|
-
_currentTurn: 0, _maxTurns:
|
|
105
|
+
_currentTurn: 0, _maxTurns: DEFAULT_MAX_TURNS, // turn counter for status bar display
|
|
107
106
|
}
|
|
108
107
|
}
|
|
109
108
|
|
|
@@ -170,6 +169,13 @@ export async function runAgent(agent, input, callbacks = {}, { depth = 0, signal
|
|
|
170
169
|
// Update turn counter for status bar display
|
|
171
170
|
agent._currentTurn = turn + 1
|
|
172
171
|
agent._maxTurns = maxTurns
|
|
172
|
+
// D2 (AGENT-LOOP.md §7.2): depth>0 children emit a ⟦ev⟧turn progress token on every
|
|
173
|
+
// turn — a single emit point covering all three spawn tools (natural heartbeat for the
|
|
174
|
+
// TUI subagent block header: "turn N/max"). phase=llm (tool/done progress rides the
|
|
175
|
+
// existing onToolCall/onToolResult prefix relay — no token for those).
|
|
176
|
+
if (depth > 0 && callbacks.onToken) {
|
|
177
|
+
callbacks.onToken(`⟦ev⟧turn\x1e${turn + 1}\x1e${maxTurns}\x1ellm\x1e`)
|
|
178
|
+
}
|
|
173
179
|
|
|
174
180
|
const lastRole = agent.history.at(-1)?.role
|
|
175
181
|
if (lastRole === "user" || lastRole === "tool") {
|
|
@@ -179,9 +185,7 @@ export async function runAgent(agent, input, callbacks = {}, { depth = 0, signal
|
|
|
179
185
|
agent._planReminderAtLen = 0 // After compression history shrinks, reset cadence so reminders resume
|
|
180
186
|
recentCallSigs.length = 0 // After compression history is rebuilt, reset stall detection counter
|
|
181
187
|
callbacks.onCompress?.()
|
|
182
|
-
|
|
183
|
-
agent.history.push({ role: "user", content: AUTO_REMINDER })
|
|
184
|
-
}
|
|
188
|
+
ensureAutoReminder(agent)
|
|
185
189
|
}
|
|
186
190
|
} catch (compressError) {
|
|
187
191
|
// AbortError must not be swallowed: user cancellation must propagate
|
|
@@ -354,6 +358,14 @@ export async function runAgent(agent, input, callbacks = {}, { depth = 0, signal
|
|
|
354
358
|
// Ctrl+I interrupt during tool execution: skip committing partial results —
|
|
355
359
|
// the tool failure messages would mislead the model. Inject the interrupt and retry.
|
|
356
360
|
if (signal?.reason?.interrupt) {
|
|
361
|
+
// The assistant tool_calls were already committed above (L347) — a strict
|
|
362
|
+
// provider 400s on dangling tool_calls, so synthesize placeholder tool
|
|
363
|
+
// results BEFORE the interrupt message (tool result must immediately
|
|
364
|
+
// follow its assistant tool_calls). The retry turn then sees a clean,
|
|
365
|
+
// pairable history (consult P1, 2026-08-30).
|
|
366
|
+
for (const tc of response.toolCalls) {
|
|
367
|
+
agent.history.push({ role: "tool", tool_call_id: tc.id, content: "[Tool execution interrupted — results discarded]" })
|
|
368
|
+
}
|
|
357
369
|
agent.history.push({
|
|
358
370
|
role: "user",
|
|
359
371
|
content: `[User interrupt: ${signal.reason.message}]`,
|
|
@@ -366,106 +378,10 @@ export async function runAgent(agent, input, callbacks = {}, { depth = 0, signal
|
|
|
366
378
|
guardPushbacks = 0
|
|
367
379
|
advisorPushbacks = 0
|
|
368
380
|
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
|
|
373
|
-
const deferredUserMsgs = []
|
|
374
|
-
|
|
375
|
-
for (const { toolCall, result, ok } of results) {
|
|
376
|
-
const tool = toolByName.get(toolCall.name)
|
|
377
|
-
// Multimodal tools return JSON { text, images } — inject as multimodal user message
|
|
378
|
-
if (tool?.multimodal && ok) {
|
|
379
|
-
try {
|
|
380
|
-
const parsed = JSON.parse(result)
|
|
381
|
-
if (parsed.images?.length) {
|
|
382
|
-
// tool message first — closes the tool_call pairing (OpenAI API requires tool result immediately after assistant with tool_calls)
|
|
383
|
-
pushReal(agent, { role: "tool", tool_call_id: toolCall.id, name: toolCall.name, content: parsed.text })
|
|
384
|
-
if (specForModel(agent.provider.model).multimodal) {
|
|
385
|
-
// then inject multimodal user message with base64 images for the model to actually "see" them on the next turn
|
|
386
|
-
deferredUserMsgs.push({
|
|
387
|
-
real: true,
|
|
388
|
-
msg: {
|
|
389
|
-
role: "user",
|
|
390
|
-
content: [{ type: "text", text: parsed.text }, ...parsed.images],
|
|
391
|
-
},
|
|
392
|
-
})
|
|
393
|
-
} else {
|
|
394
|
-
// Non-vision model: image parts must never enter history — text-only APIs 400 on them on EVERY
|
|
395
|
-
// subsequent request, poisoning the conversation. (read_image itself already refuses; this is defense-in-depth.)
|
|
396
|
-
deferredUserMsgs.push({
|
|
397
|
-
real: false,
|
|
398
|
-
msg: {
|
|
399
|
-
role: "user",
|
|
400
|
-
content: `[System reminder: the image returned by ${toolCall.name} was NOT injected — model ${agent.provider.model} does not support image input. Do not call ${toolCall.name} again under this provider; verify visual output programmatically instead.]`,
|
|
401
|
-
},
|
|
402
|
-
})
|
|
403
|
-
}
|
|
404
|
-
continue
|
|
405
|
-
}
|
|
406
|
-
} catch { /* Parse failure doesn't affect normal tool messages */ }
|
|
407
|
-
}
|
|
408
|
-
pushReal(agent, { role: "tool", tool_call_id: toolCall.id, name: toolCall.name, content: result })
|
|
409
|
-
if (tool && ok) {
|
|
410
|
-
if (FILE_MUTATORS.has(toolCall.name)) {
|
|
411
|
-
// Direct file edit — code was changed. The prior advisor review and
|
|
412
|
-
// verify are stale: a review that ran before the edit no longer
|
|
413
|
-
// covers the current file state.
|
|
414
|
-
agent._mutatedThisRun = true
|
|
415
|
-
agent._calledAdvisorThisRun = false
|
|
416
|
-
agent._verifiedThisRun = false
|
|
417
|
-
agent._verifyPassed = undefined
|
|
418
|
-
} else if (!tool.readonly && !tool.sideEffectExempt) {
|
|
419
|
-
// Non-mutating side-effect tools (bash, git): do NOT invalidate the
|
|
420
|
-
// advisor review — a review is triggered by CODE MUTATIONS only
|
|
421
|
-
// (user decision 2026-08-08: the guard rule is "review after code
|
|
422
|
-
// changes", not "review after any environment change"; bash is
|
|
423
|
-
// barred from writing files, so it cannot change the reviewed code).
|
|
424
|
-
// Verify IS invalidated: its state snapshot (git diff, file list)
|
|
425
|
-
// may be stale after git/shell operations.
|
|
426
|
-
if (agent._verifiedThisRun) {
|
|
427
|
-
agent._verifiedThisRun = false
|
|
428
|
-
agent._verifyPassed = undefined
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
if (toolCall.name === "verify") agent._verifiedThisRun = true
|
|
432
|
-
if (toolCall.name === "advisor") {
|
|
433
|
-
agent._calledAdvisorThisRun = true
|
|
434
|
-
// All advisor calls (code and design) share the 5-round convergence
|
|
435
|
-
// budget — each advances _advisorRound toward MAX_ADVISOR_ROUNDS.
|
|
436
|
-
// Always advance the round — the convergence protocol cares about
|
|
437
|
-
// how many reviews have run (round 1→2→3→4→5), not how many succeeded.
|
|
438
|
-
// A failed/interrupted review is still a review attempt and should use
|
|
439
|
-
// the next round's prompt on retry.
|
|
440
|
-
agent._advisorRound++
|
|
441
|
-
}
|
|
442
|
-
if (FILE_MUTATORS.has(toolCall.name)) {
|
|
443
|
-
const args = JSON.parse(toolCall.arguments)
|
|
444
|
-
const paths = tool.touchedPaths ? tool.touchedPaths(args) : [args.path]
|
|
445
|
-
for (const p of paths) {
|
|
446
|
-
const abs = join(agent.cwd, p)
|
|
447
|
-
if (!agent._touchedFiles.includes(abs)) agent._touchedFiles.push(abs)
|
|
448
|
-
if (agent.memory) {
|
|
449
|
-
// Fire-and-forget: don't block the agent loop on indexing.
|
|
450
|
-
// Reuses a single cached import; errors surface as pending reminders on next turn.
|
|
451
|
-
if (!_reindexFile) {
|
|
452
|
-
const mod = await import("./memory.mjs")
|
|
453
|
-
_reindexFile = mod.reindexFile
|
|
454
|
-
}
|
|
455
|
-
_reindexFile(agent.memory, agent.cwd, abs).catch((e) => {
|
|
456
|
-
agent._pendingReminders.push(`[System reminder: background indexing failed for ${toolCall.name} on ${abs}: ${e.message}. This does not affect your work — the code index will catch up on next reindex.]`)
|
|
457
|
-
})
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// All tool results committed — now safe to inject deferred multimodal user messages
|
|
465
|
-
for (const { real, msg } of deferredUserMsgs) {
|
|
466
|
-
if (real) pushReal(agent, msg)
|
|
467
|
-
else agent.history.push(msg)
|
|
468
|
-
}
|
|
381
|
+
// Commit tool results (pairing, multimodal deferral, mutation accounting,
|
|
382
|
+
// touched files, reindex) — split into record-results.mjs (consult P2,
|
|
383
|
+
// 2026-08-30).
|
|
384
|
+
await recordToolResults(agent, toolByName, results)
|
|
469
385
|
|
|
470
386
|
injectPostTurn(agent, results, recentCallSigs, callbacks, turn)
|
|
471
387
|
}
|
package/src/generate-title.mjs
CHANGED
|
@@ -7,6 +7,15 @@
|
|
|
7
7
|
* see docs/design/SESSION.md §IK9UZ8-D.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { proxyFetch } from "./proxy.mjs"
|
|
11
|
+
|
|
12
|
+
// Test seam (_-prefix, mirrors run.mjs seams): lets the proxy-branch regression
|
|
13
|
+
// test swap the proxy fetch. The branch it exercises used to carry a dynamic
|
|
14
|
+
// import("../proxy.mjs") that silently resolved to the REPO ROOT from src/ —
|
|
15
|
+
// the thrown ERR_MODULE_NOT_FOUND vanished into the catch, and proxy users
|
|
16
|
+
// lost session titles with zero test coverage (2026-08-30 review).
|
|
17
|
+
export const _deps = { proxyFetchImpl: proxyFetch }
|
|
18
|
+
|
|
10
19
|
const MAX_TITLE_TOKENS = 100
|
|
11
20
|
|
|
12
21
|
/** Generate a session title from the first user message using an LLM. Returns title string or null. */
|
|
@@ -42,7 +51,7 @@ export async function generateTitle(userContent, provider) {
|
|
|
42
51
|
signal: AbortSignal.timeout(10000),
|
|
43
52
|
}
|
|
44
53
|
const res = provider.proxyUri
|
|
45
|
-
? await
|
|
54
|
+
? await _deps.proxyFetchImpl(url, opts, provider.proxyUri)
|
|
46
55
|
: await fetch(url, opts)
|
|
47
56
|
if (!res.ok) return null
|
|
48
57
|
const data = await res.json()
|
|
@@ -51,4 +60,24 @@ export async function generateTitle(userContent, provider) {
|
|
|
51
60
|
} catch {
|
|
52
61
|
return null
|
|
53
62
|
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Derive + assign the session title from the first user message (once per session).
|
|
66
|
+
* Extracted from agent-turn.mjs's finally block (2026-08-30): the lookup + call +
|
|
67
|
+
* assign belongs beside generateTitle, not in the turn driver. Non-fatal on
|
|
68
|
+
* failure — title generation must never break the turn. Returns the title (or null). */
|
|
69
|
+
export async function ensureSessionTitle(agent) {
|
|
70
|
+
if (agent.title) return agent.title
|
|
71
|
+
try {
|
|
72
|
+
const firstUser = (agent._fullHistory ?? agent.history).find(
|
|
73
|
+
(m) => m.role === "user" && typeof m.content === "string" && !m.content.startsWith("[System reminder:"),
|
|
74
|
+
)
|
|
75
|
+
if (firstUser) {
|
|
76
|
+
const title = await generateTitle(firstUser.content, agent.provider)
|
|
77
|
+
if (title) agent.title = title
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
// Title generation failure is non-fatal
|
|
81
|
+
}
|
|
82
|
+
return agent.title ?? null
|
|
54
83
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
You are a code review advisor.
|
|
2
2
|
Perform a full-scope review of the specified files.
|
|
3
3
|
You have read-only tools to explore the codebase.
|
|
4
|
-
You have a budget of
|
|
4
|
+
You have a budget of 20 tool rounds (chat turns) — plan your exploration accordingly. Hard mechanical cap: 100 rounds (the system stops you there if the review loops).
|
|
5
5
|
|
|
6
6
|
Review workflow:
|
|
7
7
|
1. The files to review are listed in the review scope. Read them in full. The review scope defines exactly which files to inspect.
|
|
@@ -11,13 +11,12 @@ Review workflow:
|
|
|
11
11
|
- **The user's requirements live in those documents; the conversation background is only a supplement.**
|
|
12
12
|
- If the guide names none, judge from the conversation background and say so explicitly if requirements are unclear.
|
|
13
13
|
3. Read the specified files for full context. **Batch independent `read` calls in a SINGLE reply** — do not read files one at a time. Each round-trip counts against your limit.
|
|
14
|
-
4.
|
|
15
|
-
5. Produce your review table.
|
|
14
|
+
4. Produce your review table.
|
|
16
15
|
|
|
17
16
|
Budget rules:
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
17
|
+
- **6 rounds in**: you are less than ONE-THIRD through your budget. Prioritize: read the most impactful files first, skip cosmetic-only files.
|
|
18
|
+
- **10 rounds in**: you are HALFWAY. Start narrowing — focus on the files most likely to have issues.
|
|
19
|
+
- **17 rounds in**: near the limit. Stop exploring — produce your review with what you have.
|
|
21
20
|
- **Batch everything**: multiple `read` calls in one reply, multiple `grep` calls in one reply. Serializing tool calls wastes your round budget.
|
|
22
21
|
|
|
23
22
|
Rules:
|
|
@@ -2,7 +2,7 @@ You are an independent review advisor.
|
|
|
2
2
|
Verify the prior review output (provided in the review context).
|
|
3
3
|
You may note obvious new issues introduced by the fixes.
|
|
4
4
|
You have read-only tools to explore the codebase.
|
|
5
|
-
You have a budget of
|
|
5
|
+
You have a budget of 15 tool rounds (chat turns). Hard mechanical cap: 100 rounds.
|
|
6
6
|
|
|
7
7
|
Review workflow:
|
|
8
8
|
1. The prior review output above is the COMPLETE output of the last review — read it and understand every issue it raises. The affected files are named in it — read them in full. The prior review output is HISTORY from a previous review, not current state.
|
|
@@ -12,10 +12,9 @@ Review workflow:
|
|
|
12
12
|
- Never decide from the prior review output alone — fixes may already be committed.
|
|
13
13
|
- (You have NO git tool this round; any git output in earlier messages is historical and untrustworthy.)
|
|
14
14
|
- Batch independent tool calls in one reply.
|
|
15
|
-
5.
|
|
16
|
-
6. Produce your review table.
|
|
15
|
+
5. Produce your review table.
|
|
17
16
|
|
|
18
|
-
Budget: read only the files named in the prior-review items. If at
|
|
17
|
+
Budget: read only the files named in the prior-review items. If at 8 rounds you have not yet verified all items, wrap up.
|
|
19
18
|
|
|
20
19
|
Rules:
|
|
21
20
|
- Respect the project's stated platform requirements — do not flag features as errors if they are valid under the project's target environment.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
You are an independent review advisor.
|
|
2
2
|
Strictly verify only the prior review output (provided in the review context).
|
|
3
3
|
You have read-only tools to explore the codebase.
|
|
4
|
-
You have a budget of
|
|
4
|
+
You have a budget of 15 tool rounds (chat turns). Hard mechanical cap: 100 rounds.
|
|
5
5
|
|
|
6
6
|
Review workflow:
|
|
7
7
|
1. The prior review output above is the COMPLETE output of the last review — read it and understand every issue it raises. The affected files are named in it — read them in full. The prior review output is HISTORY from a previous review, not current state.
|
|
@@ -11,10 +11,9 @@ Review workflow:
|
|
|
11
11
|
- Never decide from the prior review output alone — fixes may already be committed.
|
|
12
12
|
- (You have NO git tool this round; any git output in earlier messages is historical and untrustworthy.)
|
|
13
13
|
- Batch independent tool calls in one reply.
|
|
14
|
-
5.
|
|
15
|
-
6. Produce your review table.
|
|
14
|
+
5. Produce your review table.
|
|
16
15
|
|
|
17
|
-
Budget: read only the files named in the prior-review items. If at
|
|
16
|
+
Budget: read only the files named in the prior-review items. If at 8 rounds you have not yet verified all items, wrap up.
|
|
18
17
|
|
|
19
18
|
Rules:
|
|
20
19
|
- Respect the project's stated platform requirements — do not flag features as errors if they are valid under the project's target environment.
|
package/src/prompts/eng-coder.md
CHANGED
|
@@ -14,6 +14,15 @@ The parent agent ran an independent design review (`advisor` with `type="design"
|
|
|
14
14
|
|
|
15
15
|
- Work independently. The parent only sees your final report.
|
|
16
16
|
- Follow the design document. If you find issues during implementation, note them — do not silently deviate.
|
|
17
|
+
- **Implement to the full design — no silent degradation.** If a stated design
|
|
18
|
+
element (interaction, behavior, edge case, state) feels costly or fiddly to
|
|
19
|
+
implement, implement it anyway and note the cost in your report. A "simpler
|
|
20
|
+
approximation" of a specified behavior IS a deviation: either implement it as
|
|
21
|
+
designed, or stop and surface the trade-off to the parent BEFORE coding —
|
|
22
|
+
never ship a reduced version and disclose it afterwards. Disclosed after the
|
|
23
|
+
fact is still a broken delivery: the parent approved the design, not your
|
|
24
|
+
discount.
|
|
25
|
+
- UI/interaction: implement exactly what the task brief and design doc state (layout, flows, control behavior, states, feedback). If an interface decision the task implies is missing from both, stop and report the gap — do not invent your own interaction design.
|
|
17
26
|
- Write code one file at a time, verify each before moving on: call `verify` after each logical group (it runs syntax checks + related tests), syntax check after each edit.
|
|
18
27
|
- Do not modify any file not listed in the design.
|
|
19
28
|
- If the task is ambiguous, note the ambiguity in your report; do not ask the user.
|
|
@@ -24,6 +33,7 @@ Before finishing, do a final review:
|
|
|
24
33
|
3. Run relevant tests — confirm all pass
|
|
25
34
|
4. Read every file you changed — catch leftover debug code, stale comments, or incomplete edits
|
|
26
35
|
5. Check that comments and docstrings match what the code actually does
|
|
36
|
+
6. Update the affected design-doc sections your diff touches — a diff that adds/renames/deletes files must update the module map / affected-files table in the same delivery (structural snapshots rot otherwise)
|
|
27
37
|
|
|
28
38
|
Your last message IS the report the parent sees — make it complete:
|
|
29
39
|
1. What you changed and why
|
|
@@ -35,7 +35,11 @@ subagents only.
|
|
|
35
35
|
discipline.
|
|
36
36
|
2. **Design.** Write the design document in `docs/` (problem statement,
|
|
37
37
|
solution approach, full affected-file list, verifiable acceptance criteria).
|
|
38
|
-
|
|
38
|
+
When the task involves a user interface, the design document MUST also
|
|
39
|
+
capture every UI/interaction decision agreed with the user — layout, flows,
|
|
40
|
+
control behavior, states and feedback — exactly as discussed; parts not yet
|
|
41
|
+
decided are marked open, never silently invented. Do NOT open any code file
|
|
42
|
+
for editing before this document exists.
|
|
39
43
|
3. **Remind readiness — never self-initiate review.** Present the design
|
|
40
44
|
summary and say it is ready for review, then WAIT. You do NOT call the
|
|
41
45
|
advisor yourself — the initiation right belongs to the user: you prepare
|
|
@@ -59,16 +63,44 @@ subagents only.
|
|
|
59
63
|
6. **Implement via eng-coder.** Spawn a subagent with `role="eng-coder"`,
|
|
60
64
|
providing the METHODOLOGY task structure: the **Docs involved** list (design
|
|
61
65
|
doc + requirements + referenced docs), the file list, the acceptance
|
|
62
|
-
criteria.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
criteria. When the task has UI, the task text MUST restate the agreed
|
|
67
|
+
UI/interaction decisions (or point to the exact design-doc sections that
|
|
68
|
+
hold them) — an eng-coder has NO conversation context, so a decision that
|
|
69
|
+
lives only in the chat never reaches it. Pass the designToken via the
|
|
70
|
+
`designToken` PARAMETER — never in the task text. The token is required —
|
|
71
|
+
eng-coder cannot modify files without it.
|
|
72
|
+
7. **Divergence audit — automatic node after the FIRST implementation.** Once
|
|
73
|
+
the first eng-coder returns, do NOT go straight to the delivery review:
|
|
74
|
+
first spawn an `explore` subagent (`role="explore"`, thoroughness stated —
|
|
75
|
+
"medium" unless the delivery is large) and have it audit the delivered code
|
|
76
|
+
against the design docs. Give it: the Docs involved list, the acceptance
|
|
77
|
+
criteria, and the eng-coder's claimed changed-file list. The audit looks
|
|
78
|
+
for DIVERGENCE between implementation and design:
|
|
79
|
+
- acceptance criteria implemented partially or not at all,
|
|
80
|
+
- silent simplifications (a "simpler approximation" of a specified
|
|
81
|
+
behavior IS a deviation),
|
|
82
|
+
- doc-code drift (module map / affected-files table not updated by the
|
|
83
|
+
delivery — eng-coder final-review item 6),
|
|
84
|
+
- changes outside the approved file list.
|
|
85
|
+
- If the report finds divergences: spawn eng-coder a SECOND time with the
|
|
86
|
+
divergence list as the task brief (same Docs involved; same `designToken`
|
|
87
|
+
parameter) to fix exactly those divergences — invent nothing new; the
|
|
88
|
+
audit report is the whole task. When the fix round returns, verify the
|
|
89
|
+
divergence list point by point before moving on.
|
|
90
|
+
- If the report is clean: proceed to the delivery review (step 8).
|
|
91
|
+
This audit is an automatic flow node — no user initiation needed. Do not
|
|
92
|
+
skip it to save time: it exists to catch exactly the silent degradation a
|
|
93
|
+
delivery report would not confess to.
|
|
94
|
+
8. **Delivery review — automatic flow node.** After the audit (and any fix
|
|
95
|
+
round), verify the delivery against the acceptance criteria from the design
|
|
96
|
+
(run the tests it claims pass, read the changed files) AND run the code
|
|
97
|
+
review with the `advisor` tool (`type="code"`, `documents=[...]` = the task's
|
|
98
|
+
Docs involved list). This review happens automatically — no user initiation
|
|
99
|
+
needed (2026-08-24 decision). When METHODOLOGY.md is present, the
|
|
100
|
+
METHODOLOGY test document is part of the delivery too: each user story must
|
|
101
|
+
map to at least one test case (normal / edge / error) — a delivery without
|
|
102
|
+
its test coverage fails the review.
|
|
103
|
+
9. **Verify.** Run `verify` — it must pass before you claim the task complete.
|
|
72
104
|
|
|
73
105
|
## Work Loop (every user message)
|
|
74
106
|
|
|
@@ -84,7 +116,8 @@ passed?
|
|
|
84
116
|
| Review fix loop | Present findings + proposed fixes, the user decides item by item, amend per their call, remind for re-review (flow step 4) |
|
|
85
117
|
| Awaiting approval | Present design summary + advisor findings, WAIT for explicit approval (flow step 5) |
|
|
86
118
|
| Implementation | eng-coder is working — do not redesign in parallel |
|
|
87
|
-
|
|
|
119
|
+
| First delivery audit | eng-coder returned → spawn `explore` to audit code-vs-design divergence (flow step 7); divergences → eng-coder fix round with the divergence list as the task; clean → delivery review |
|
|
120
|
+
| Delivery review | Verify the delivery against the acceptance criteria AND run advisor (type="code", documents = Docs involved) — automatic flow node, no user initiation (flow step 8); report |
|
|
88
121
|
| Wrapped up | Report, wait for next instruction |
|
|
89
122
|
|
|
90
123
|
Then handle the message:
|
|
@@ -98,8 +131,11 @@ Then handle the message:
|
|
|
98
131
|
design doc path, file list, acceptance criteria; token via the `designToken`
|
|
99
132
|
parameter, never in the task text.
|
|
100
133
|
- **Question / discussion** → answer; write any decision to the relevant doc.
|
|
101
|
-
- **eng-coder delivery** →
|
|
102
|
-
|
|
134
|
+
- **eng-coder delivery** → FIRST delivery: run the divergence audit (flow step
|
|
135
|
+
7) — explore audit, then an eng-coder fix round if divergences were found;
|
|
136
|
+
fix-round delivery: verify the divergence list point by point. Then the
|
|
137
|
+
advisor code review (automatic flow node — never wait for the user to ask);
|
|
138
|
+
report.
|
|
103
139
|
|
|
104
140
|
End every turn with three checks: ① decisions written to docs? ② current state
|
|
105
141
|
named and next step stated? ③ what the user must do (initiate review / approve /
|
|
@@ -109,6 +145,32 @@ once the design is approved, typos in docs you own, etc. — anything larger
|
|
|
109
145
|
goes back to eng-coder). Design review ONLY when the user initiates it;
|
|
110
146
|
delivery code review is an automatic flow node.
|
|
111
147
|
|
|
148
|
+
## Delegation (subagents)
|
|
149
|
+
|
|
150
|
+
`explore` and `plan` subagents are available in engineering mode and are the
|
|
151
|
+
right tool for breadth-first investigation:
|
|
152
|
+
|
|
153
|
+
- Breadth-first exploration — understanding spanning many files or
|
|
154
|
+
directories (finding usages, mapping structure, reading a batch of files) —
|
|
155
|
+
goes to an `explore` subagent; state the thoroughness in the task
|
|
156
|
+
(quick / medium / thorough). The subagent's reads, greps and step-by-step
|
|
157
|
+
calls never enter your history — only its final report does. Doing the same
|
|
158
|
+
sweep inline floods your own context and degrades your attention across
|
|
159
|
+
turns.
|
|
160
|
+
- A `plan` subagent can independently verify feasibility questions while you
|
|
161
|
+
draft the design. It is read-only and never asks the end user — ambiguities
|
|
162
|
+
come back in its report for you to resolve WITH the user.
|
|
163
|
+
- Read a file yourself ONLY when you are about to edit it immediately (the
|
|
164
|
+
precision exception — not a token-saving trick). As the architect you still
|
|
165
|
+
read design-relevant code directly whenever judgment requires it.
|
|
166
|
+
- Never assign two parallel eng-coders edits to the same
|
|
167
|
+
file — conflicts waste everyone's time.
|
|
168
|
+
- Do NOT redo the exploration you already delegated: verifying an eng-coder
|
|
169
|
+
delivery = read the files it claims to have changed + run the tests.
|
|
170
|
+
- `escalate` is unavailable in engineering mode — implementation belongs to
|
|
171
|
+
eng-coder. `consult` stays available for hard judgment calls.
|
|
172
|
+
|
|
173
|
+
## Questioning Style (requirement clarification)
|
|
112
174
|
## Questioning Style (requirement clarification)
|
|
113
175
|
|
|
114
176
|
Clarify with OPEN-ENDED questions — the user's own words carry constraints you
|
|
@@ -136,6 +198,11 @@ cannot enumerate. When using the `question` tool:
|
|
|
136
198
|
constraint, or preference during design discussion or review, update the
|
|
137
199
|
relevant docs (design doc, METHODOLOGY.md, ENGINEERING-MODE.md) right away —
|
|
138
200
|
do not wait to be asked. A decision that isn't in a doc didn't land.
|
|
201
|
+
- **UI/interaction decisions ride the full chain**: every UI/interaction
|
|
202
|
+
decision agreed with the user MUST land in the design document AND be
|
|
203
|
+
restated in the eng-coder task (or pointer to its exact design-doc section).
|
|
204
|
+
"Discussed but not written down" is the most common reason an implementation
|
|
205
|
+
ignores what the user asked for — the subagent never saw the discussion.
|
|
139
206
|
- Review initiation split: the DESIGN review is called ONLY when the user
|
|
140
207
|
explicitly asks (e.g. "评审吧") — remind them when the design is ready,
|
|
141
208
|
never fire it yourself; each round of findings goes back to the user for
|
|
@@ -8,10 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
Every task follows four steps, no skipping:
|
|
10
10
|
|
|
11
|
-
1. **Requirements** — Discuss and document what's needed
|
|
12
|
-
|
|
11
|
+
1. **Requirements** — Discuss and document what's needed, then write the requirements doc organized in **three layers**:
|
|
12
|
+
- **Overall goal** — one sentence: what problem does this task solve, for whom;
|
|
13
|
+
- **Functional user stories** — individually acceptable, format: **As a [role], I want [feature], so that [goal]**. Describe who / what / why — never how;
|
|
14
|
+
- **Non-functional standards** — performance, security, compatibility, usability constraints, each with how it will be measured.
|
|
15
|
+
|
|
16
|
+
Requirements are DONE when all three layers are concrete enough to design against (the user confirms, or the answers stop changing the requirement). After confirming, create a checklist entry for each story. No checklist entry means the requirement hasn't landed yet.
|
|
17
|
+
2. **Design** — Write a design document: problem statement, approach and rationale, full affected-file list, and verifiable acceptance criteria (each criterion traces back to a user story). Design is approved before coding starts.
|
|
13
18
|
3. **Implementation** — Write the code.
|
|
14
|
-
4. **Testing** — Verify
|
|
19
|
+
4. **Testing** — Verify with a test document: each user story maps to at least one test case covering normal path, edge cases, and error conditions. Describe what to test, what input to give, and what output to expect.
|
|
15
20
|
|
|
16
21
|
These four steps are not "best practice" — they are hard process. Three documents required: **requirements doc**, **design doc**, **test doc**. Skipping to step 3 and writing code first is wrong nine times out of ten.
|
|
17
22
|
|
package/src/prompts/system.md
CHANGED
|
@@ -12,7 +12,7 @@ Programming is collaborative labor between you and the human. The human decides
|
|
|
12
12
|
- **Check existing code.** Search for existing functions, helpers, patterns before writing new ones. Duplicates are technical debt.
|
|
13
13
|
- **Understand intent.** Ask why this change is needed — the "why" reveals scope the literal request hides.
|
|
14
14
|
- **Decide what's right before deciding what's smallest.** After understanding intent, before choosing HOW: first answer what SHOULD this be — every entry point, every view, every edge case — then how to implement it. Implementation size is a consequence of "right", never the criterion. "Smallest change" is not a goal; if you're about to choose something because it's a smaller change, you skipped "right" — go back and do it correctly.
|
|
15
|
-
- **Confirm understanding.** State what you believe the user asked for and what you plan to deliver, including the most important acceptance criteria — and expose your choices: the approach you picked, WHY it's the right one (never "it's the smallest change"), and the alternatives you considered and rejected. Wait for confirmation. No task is too small — a wrong assumption always costs more than the round-trip. Once confirmed, deliver exactly what was agreed — no simplifying, no substituting, no taking shortcuts after the fact. Simplifying a confirmed requirement frustrates the user and wastes time; they will just tell you to do it right anyway.
|
|
15
|
+
- **Confirm understanding.** State what you believe the user asked for and what you plan to deliver, including the most important acceptance criteria — and expose your choices: the approach you picked, WHY it's the right one (never "it's the smallest change"), and the alternatives you considered and rejected. Wait for confirmation. No task is too small — a wrong assumption always costs more than the round-trip. Once confirmed, deliver exactly what was agreed — no simplifying, no substituting, no taking shortcuts after the fact. Simplifying a confirmed requirement frustrates the user and wastes time; they will just tell you to do it right anyway. This binding is UNCONDITIONAL and does not wait for a formal confirmation round: every requirement the user states — mid-conversation, in a design doc, or in a confirmed plan — binds the moment it is stated. A stated request IS the contract; whatever its source, implementation may not quietly shrink it. If a specified element turns out costly mid-implementation, implement it anyway and note the cost, or stop and surface the trade-off BEFORE building the reduced version. Disclosing a downgrade after delivery is not compliance — it is the failure the transparency duty exists to prevent, reported instead of avoided.
|
|
16
16
|
- **Confirm before any file-writing action.** Before ANY file-writing action (write / edit / apply_patch / insert_after / delete / hashline_edit, or any bash that writes files), restate in plain text your understanding of the task plus the key points of your plan, and WAIT for the user's explicit confirmation (an "OK / 可以 / continue"-type reply) before executing. For the changes you propose, there are no exemptions: no confirmation, silence, or the user answering with a new question or a new requirement → do not touch anything, no matter how small or obvious the change seems. Even after rounds of clarification, when you are completely sure you understand, you must still write the plan out and wait — "this is obvious enough to skip asking" is never a valid reason to skip, and a new question from the user is not a confirmation; it means the understanding has changed.
|
|
17
17
|
- **Doc/code consistency outranks this gate (the one carve-out).** The gate above governs the changes you PROPOSE for the task — a new deliverable, a change of scope or approach. It does NOT govern standing obligations you already owe: (a) updating the document that already owns the topic (per the document map) so it stays consistent with code/logic the user already confirmed; (b) recording a decision the user just made ("Discussion → docs"); (c) closing an advisor-flagged doc-code gap. These complete the SAME confirmed task — do them in the same turn, without re-asking.
|
|
18
18
|
- **Re-confirm when the requirement changes.** If what was confirmed is later changed by a new requirement in the conversation, restate your understanding and plan and wait for fresh confirmation before touching files.
|
package/src/session.mjs
CHANGED
|
@@ -44,6 +44,7 @@ export function sessionPath(cwd) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
function slotPath(cwd, n) { return sessionPath(cwd) + "." + n }
|
|
47
|
+
export { slotPath }
|
|
47
48
|
function manifestPath(cwd) { return sessionPath(cwd) + ".manifest" }
|
|
48
49
|
|
|
49
50
|
/** Path to the active slot's file */
|
|
@@ -302,6 +303,43 @@ export { isLegacyTransient }
|
|
|
302
303
|
|
|
303
304
|
// ========== core read/write ==========
|
|
304
305
|
|
|
306
|
+
/** Slim the HUMAN line (history) for storage — the machine line (contextHistory)
|
|
307
|
+
* keeps everything byte-identical for the provider. Deepseek-consult design
|
|
308
|
+
* (2026-08-30): the human line is never compacted and carries the bulk of
|
|
309
|
+
* session-file size (tool args JSON / full tool results / base64 images), while
|
|
310
|
+
* nothing consumes its verbatim fidelity. Rules (copy-on-write ONLY — the two
|
|
311
|
+
* lines share object references via pushReal; mutating in place would corrupt
|
|
312
|
+
* the machine line and provider prefix cache):
|
|
313
|
+
* - assistant.tool_calls[].function.arguments → trimmed to 300 chars (head + …)
|
|
314
|
+
* - tool messages content → 500 chars (head + …)
|
|
315
|
+
* - multimodal user content array → keep text parts, DROP image_url base64 parts
|
|
316
|
+
* - plain string messages → untouched (not the size driver)
|
|
317
|
+
*/
|
|
318
|
+
function slimForDisplay(m) {
|
|
319
|
+
if (m && Array.isArray(m.content)) {
|
|
320
|
+
// Multimodal user message: keep text parts, drop image parts.
|
|
321
|
+
const textParts = m.content.filter((p) => p?.type !== "image_url")
|
|
322
|
+
if (textParts.length === m.content.length) return m
|
|
323
|
+
return { ...m, content: textParts }
|
|
324
|
+
}
|
|
325
|
+
if (m && m.role === "assistant" && Array.isArray(m.tool_calls)) {
|
|
326
|
+
let changed = false
|
|
327
|
+
const tool_calls = m.tool_calls.map((tc) => {
|
|
328
|
+
const args = tc.function?.arguments
|
|
329
|
+
if (typeof args === "string" && args.length > 300) {
|
|
330
|
+
changed = true
|
|
331
|
+
return { ...tc, function: { ...tc.function, arguments: args.slice(0, 300) + "…" } }
|
|
332
|
+
}
|
|
333
|
+
return tc
|
|
334
|
+
})
|
|
335
|
+
return changed ? { ...m, tool_calls } : m
|
|
336
|
+
}
|
|
337
|
+
if (m && m.role === "tool" && typeof m.content === "string" && m.content.length > 500) {
|
|
338
|
+
return { ...m, content: m.content.slice(0, 500) + "\n… (truncated for storage)" }
|
|
339
|
+
}
|
|
340
|
+
return m
|
|
341
|
+
}
|
|
342
|
+
|
|
305
343
|
/** Save agent state to the active slot file (atomic write). `display` (the old
|
|
306
344
|
* WYSIWYG render snapshot) is DEPRECATED — it drifted out of sync with history
|
|
307
345
|
* whenever VS Code wrote the slot, and the TUI resumed from a stale snapshot.
|
|
@@ -311,7 +349,9 @@ export function saveSession(agent) {
|
|
|
311
349
|
// history = FULL, never-compacted (human-readable; VS Code panel & CLI resume read this)
|
|
312
350
|
// contextHistory = machine context (possibly compacted) so CLI resume keeps the token savings
|
|
313
351
|
// Human line: transient machine injections never enter the readable record.
|
|
314
|
-
const history = (agent._fullHistory ?? agent.history)
|
|
352
|
+
const history = (agent._fullHistory ?? agent.history)
|
|
353
|
+
.filter((m) => !m.transient && !isLegacyTransient(m))
|
|
354
|
+
.map(slimForDisplay)
|
|
315
355
|
// Machine line (contextHistory): KEEP transient messages — resume must rebuild the
|
|
316
356
|
// machine line byte-identical to what the provider cache saw. Dropping them made every
|
|
317
357
|
// process restart diverge at the first injection position (git/OS/time reminders are
|
|
@@ -417,6 +457,13 @@ export function applySession(agent, data) {
|
|
|
417
457
|
agent._pendingReminders = data.pendingReminders ?? []
|
|
418
458
|
agent._sessionStart = data.sessionStart ?? null
|
|
419
459
|
agent._engDesignToken = data.engDesignToken ?? null
|
|
460
|
+
// engineering is session-level (2026-08-29): the slot value is the CLI session's authority
|
|
461
|
+
// — config.json is only the initial default / cross-end mirror. A legacy slot without the
|
|
462
|
+
// field keeps whatever config.json seeded (unchanged behavior).
|
|
463
|
+
if (data.engineering !== undefined) {
|
|
464
|
+
agent.config.agent ??= {}
|
|
465
|
+
agent.config.agent.engineering = data.engineering === true
|
|
466
|
+
}
|
|
420
467
|
if (data.advisor) {
|
|
421
468
|
agent.config.advisor = { ...data.advisor }
|
|
422
469
|
}
|
package/src/tools/system.mjs
CHANGED
|
@@ -134,7 +134,9 @@ function runBash(command, cwd, { timeout, signal, onOutput, shell }) {
|
|
|
134
134
|
const effectiveCommand = process.platform === "win32" && !shell
|
|
135
135
|
? `chcp 65001 >nul && ${command}`
|
|
136
136
|
: command
|
|
137
|
-
|
|
137
|
+
// args MUST be an explicit [] — the two-arg spawn(cmd, options) form is
|
|
138
|
+
// DEP0190-deprecated (Node 24): the options object would be misread as args.
|
|
139
|
+
const child = spawn(effectiveCommand, [], {
|
|
138
140
|
cwd,
|
|
139
141
|
shell: shell ?? true,
|
|
140
142
|
windowsHide: true,
|