thincoder 0.12.62 → 0.12.63
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 +9 -0
- package/README.md +13 -12
- package/bin/thincoder.mjs +53 -27
- package/package.json +6 -5
- package/src/acp/bridge.mjs +35 -15
- package/src/acp/client-caps.mjs +86 -0
- package/src/acp/ext.mjs +86 -0
- package/src/acp/handlers-session.mjs +240 -0
- package/src/acp/handlers-slots.mjs +196 -0
- package/src/acp/login.mjs +48 -0
- package/src/acp/session.mjs +6 -4
- package/src/acp.mjs +67 -379
- package/src/cli/distill-command.mjs +3 -3
- package/src/cli/make-agent.mjs +59 -17
- package/src/cli/memory-command.mjs +3 -3
- package/src/cli/permission.mjs +4 -48
- package/src/cli/setup-wizard.mjs +1 -1
- package/src/completions.mjs +3 -1
- package/src/crash-reports.mjs +1 -1
- package/src/distill.mjs +4 -4
- package/src/heap-watch.mjs +1 -1
- package/src/prompt-injections.mjs +20 -0
- package/src/tui/agent-turn.mjs +40 -9
- package/src/tui/cmd-advisor.mjs +5 -5
- package/src/tui/cmd-config.mjs +8 -8
- package/src/tui/cmd-eng.mjs +25 -9
- package/src/tui/cmd-mcp.mjs +9 -8
- package/src/tui/cmd-model.mjs +1 -1
- package/src/tui/cmd-new.mjs +6 -5
- package/src/tui/cmd-reindex.mjs +1 -1
- package/src/tui/cmd-restore.mjs +2 -2
- package/src/tui/cmd-session.mjs +24 -1
- package/src/tui/cmd-skills.mjs +1 -1
- package/src/tui/cmd-think.mjs +22 -9
- package/src/tui/config-helpers.mjs +1 -1
- package/src/tui/display-budget.mjs +33 -11
- package/src/tui/index.mjs +9 -9
- package/src/tui/interaction.mjs +16 -7
- package/src/tui/key-modes.mjs +9 -4
- package/src/tui/ledger-surface.mjs +26 -10
- package/src/tui/model-catalog.mjs +4 -4
- package/src/tui/model-picker.mjs +8 -7
- package/src/tui/mouse.mjs +11 -6
- package/src/tui/pickers.mjs +15 -2
- package/src/tui/render-conversation.mjs +1 -1
- package/src/tui/render-frame.mjs +10 -5
- package/src/tui/render-loop.mjs +1 -1
- package/src/tui/render-segments.mjs +3 -1
- package/src/tui/slash-commands.mjs +1 -1
- package/src/tui/startup.mjs +14 -14
- package/src/tui/subagent-blocks.mjs +20 -3
- package/src/tui/subagent-freeze.mjs +73 -2
- package/src/tui/suspension-drive.mjs +46 -22
- package/src/tui/tool-events.mjs +11 -8
- package/src/tui/wizard.mjs +3 -3
- package/src/abort-provenance.mjs +0 -116
- package/src/advisor/citations.mjs +0 -139
- package/src/advisor/compaction.mjs +0 -174
- package/src/advisor/convergence.mjs +0 -80
- package/src/advisor/history.mjs +0 -77
- package/src/advisor/loop.mjs +0 -293
- package/src/advisor/messages.mjs +0 -299
- package/src/advisor/project-context.mjs +0 -194
- package/src/advisor/repos.mjs +0 -150
- package/src/advisor/run.mjs +0 -293
- package/src/advisor/truncate.mjs +0 -57
- package/src/advisor.mjs +0 -290
- package/src/agent/completion.mjs +0 -146
- package/src/agent/dispatch.mjs +0 -489
- package/src/agent/helpers.mjs +0 -384
- package/src/agent/post-turn.mjs +0 -70
- package/src/agent/record-results.mjs +0 -174
- package/src/agent/relay-prefix.mjs +0 -39
- package/src/agent/run-stages.mjs +0 -244
- package/src/agent/setup-reminders.mjs +0 -69
- package/src/agent/setup.mjs +0 -354
- package/src/agent/spawn-child.mjs +0 -243
- package/src/agent-tools/advisor-async.mjs +0 -346
- package/src/agent-tools/advisor-settle.mjs +0 -231
- package/src/agent-tools/advisor.mjs +0 -260
- package/src/agent-tools/async-settle.mjs +0 -204
- package/src/agent-tools/batch-segment.mjs +0 -195
- package/src/agent-tools/consult.mjs +0 -473
- package/src/agent-tools/design-token.mjs +0 -117
- package/src/agent-tools/digest-budget.mjs +0 -76
- package/src/agent-tools/eng.mjs +0 -67
- package/src/agent-tools/escalate-async.mjs +0 -295
- package/src/agent-tools/goal.mjs +0 -119
- package/src/agent-tools/plan.mjs +0 -81
- package/src/agent-tools/read-history.mjs +0 -309
- package/src/agent-tools/recent-changes.mjs +0 -24
- package/src/agent-tools/review-streak.mjs +0 -93
- package/src/agent-tools/settings.mjs +0 -265
- package/src/agent-tools/skill.mjs +0 -47
- package/src/agent-tools/subagent-actions.mjs +0 -482
- package/src/agent-tools/subagent-async.mjs +0 -434
- package/src/agent-tools/subagent-panel.mjs +0 -160
- package/src/agent-tools/subagent-run.mjs +0 -205
- package/src/agent-tools/subagent-scheduler.mjs +0 -392
- package/src/agent-tools/subagent-spawn.mjs +0 -459
- package/src/agent-tools/subagent.mjs +0 -404
- package/src/agent-tools/task.mjs +0 -87
- package/src/agent-tools/timer.mjs +0 -46
- package/src/agent-tools/verify.mjs +0 -271
- package/src/agent-tools.mjs +0 -17
- package/src/agent.mjs +0 -417
- package/src/auto-think.mjs +0 -115
- package/src/config-migrate.mjs +0 -70
- package/src/config.mjs +0 -496
- package/src/context.mjs +0 -392
- package/src/conventions.mjs +0 -223
- package/src/embedding.mjs +0 -120
- package/src/escape.mjs +0 -152
- package/src/expand-home.mjs +0 -16
- package/src/explore-distill.mjs +0 -155
- package/src/generate-title.mjs +0 -88
- package/src/git/checkpoint.mjs +0 -448
- package/src/git/gitmem.mjs +0 -100
- package/src/hooks.mjs +0 -97
- package/src/ledger.mjs +0 -227
- package/src/log.mjs +0 -195
- package/src/markdown.mjs +0 -106
- package/src/mcp/helpers.mjs +0 -51
- package/src/mcp/transport-http.mjs +0 -248
- package/src/mcp/transport-stdio.mjs +0 -140
- package/src/mcp/transport-ws.mjs +0 -122
- package/src/mcp.mjs +0 -295
- package/src/memory/code-index.mjs +0 -219
- package/src/memory/code-sync.mjs +0 -415
- package/src/memory/core.mjs +0 -299
- package/src/memory/delete.mjs +0 -236
- package/src/memory/docs.mjs +0 -419
- package/src/memory/file-walk.mjs +0 -109
- package/src/memory/scan.mjs +0 -95
- package/src/memory/schema.mjs +0 -452
- package/src/memory.mjs +0 -21
- package/src/model-ref.mjs +0 -66
- package/src/model-specs.mjs +0 -179
- package/src/peer-domains.mjs +0 -265
- package/src/peer-instances.mjs +0 -231
- package/src/prompt-overlays.mjs +0 -82
- package/src/prompts/advisor-design.md +0 -41
- package/src/prompts/advisor-round1.md +0 -41
- package/src/prompts/advisor-round2.md +0 -46
- package/src/prompts/advisor-round3.md +0 -42
- package/src/prompts/common.md +0 -115
- package/src/prompts/consult-base.md +0 -19
- package/src/prompts/discipline-engineering.md +0 -258
- package/src/prompts/discipline-normal.md +0 -185
- package/src/prompts/persona-coder.md +0 -21
- package/src/prompts/persona-eng-coder.md +0 -37
- package/src/prompts/persona-eng-designer.md +0 -60
- package/src/prompts/persona-engineering.md +0 -55
- package/src/prompts/persona-explore.md +0 -15
- package/src/prompts/persona-normal.md +0 -27
- package/src/prompts/persona-plan.md +0 -26
- package/src/provider/anthropic.mjs +0 -225
- package/src/provider/core.mjs +0 -476
- package/src/provider/errors.mjs +0 -101
- package/src/provider/google.mjs +0 -257
- package/src/provider/index.mjs +0 -7
- package/src/provider/list-models.mjs +0 -93
- package/src/provider/normalize.mjs +0 -81
- package/src/provider/rate.mjs +0 -108
- package/src/provider/responses.mjs +0 -495
- package/src/provider/retry.mjs +0 -88
- package/src/provider/sse.mjs +0 -264
- package/src/proxy.mjs +0 -261
- package/src/rules.mjs +0 -53
- package/src/session-gc.mjs +0 -221
- package/src/session-guard.mjs +0 -59
- package/src/session-migrate.mjs +0 -48
- package/src/session-rename.mjs +0 -38
- package/src/session-segments.mjs +0 -100
- package/src/session-slots.mjs +0 -492
- package/src/session-store.mjs +0 -441
- package/src/session.mjs +0 -492
- package/src/skills.mjs +0 -153
- package/src/text-budget.mjs +0 -46
- package/src/token-ttl.mjs +0 -274
- package/src/tools/apply_patch.md +0 -15
- package/src/tools/bash.md +0 -37
- package/src/tools/bash.mjs +0 -268
- package/src/tools/checklist-sync.mjs +0 -181
- package/src/tools/checklist.md +0 -13
- package/src/tools/checklist.mjs +0 -299
- package/src/tools/delete.md +0 -13
- package/src/tools/edit-batch.mjs +0 -191
- package/src/tools/edit-diff.mjs +0 -348
- package/src/tools/edit.md +0 -30
- package/src/tools/execute.md +0 -21
- package/src/tools/execute.mjs +0 -228
- package/src/tools/fetch.md +0 -12
- package/src/tools/file.mjs +0 -469
- package/src/tools/file_ops.md +0 -17
- package/src/tools/get_current_time.md +0 -8
- package/src/tools/git-checkpoint.mjs +0 -143
- package/src/tools/git-ext.mjs +0 -173
- package/src/tools/git.md +0 -54
- package/src/tools/git.mjs +0 -356
- package/src/tools/glob-dialect.mjs +0 -130
- package/src/tools/glob.md +0 -11
- package/src/tools/grep.md +0 -19
- package/src/tools/hashline_edit.md +0 -14
- package/src/tools/index.mjs +0 -36
- package/src/tools/insert_after.md +0 -15
- package/src/tools/lint.md +0 -10
- package/src/tools/linter.mjs +0 -128
- package/src/tools/ls.md +0 -12
- package/src/tools/lsp.md +0 -10
- package/src/tools/lsp.mjs +0 -316
- package/src/tools/ops.mjs +0 -299
- package/src/tools/patch.mjs +0 -282
- package/src/tools/process.md +0 -10
- package/src/tools/question.md +0 -16
- package/src/tools/question.mjs +0 -26
- package/src/tools/read.md +0 -20
- package/src/tools/read_image.md +0 -8
- package/src/tools/repomap.mjs +0 -314
- package/src/tools/search.mjs +0 -236
- package/src/tools/shared.mjs +0 -446
- package/src/tools/tree.md +0 -14
- package/src/tools/tree.mjs +0 -66
- package/src/tools/wait_for.md +0 -22
- package/src/tools/web.mjs +0 -224
- package/src/tools/websearch.md +0 -16
- package/src/tools/write.md +0 -11
- package/src/traces/trace-store.mjs +0 -355
package/src/advisor/truncate.mjs
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* truncate.mjs — advisor 工具结果的行感知双端截断(DUAL-END-TRUNCATION F-2,
|
|
3
|
-
* DUAL-END-TRUNCATION.md,2026-09-09——CLI/VSC 双端逐字同构镜像)。
|
|
4
|
-
*
|
|
5
|
-
* 原 run.mjs 内联截断为纯头向(line-aware 从首行累加至 64K break)——评审尾部
|
|
6
|
-
* 结论/裁决被切。现改头尾双保:头行累加至预算 ~60% → 中段省略注 → 尾行累加至
|
|
7
|
-
* 剩余 ~40%(保尾结论——与 read 工具 C 方案 / offload 双端预览同构的
|
|
8
|
-
* "头+尾保留、中段截断"策略——共享设计语言、分别实现)。
|
|
9
|
-
*
|
|
10
|
-
* 纯函数无导入:maxChars 由调用方传入(run.mjs 传 MAX_RESULT_CHARS)。
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
/** advisor 截断头占比——头行累加至此份额后切中段,尾自动取剩余预算(设计 §4 ≈0.6)。 */
|
|
14
|
-
export const ADVISOR_HEAD_RATIO = 0.6
|
|
15
|
-
|
|
16
|
-
/** 尾部预算预留(字符):中段省略注 + offset 续读提示的保守预留——注的位数只减不增
|
|
17
|
-
* (middle ≤ totalChars——超大结果位数可达 ~10 位——200 字符仍宽松覆盖注+提示 ≤ ~140;
|
|
18
|
-
* 差额留给尾行)。 */
|
|
19
|
-
const NOTE_RESERVE_CHARS = 200
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 行感知双端截断:result > maxChars → 头 + `… (truncated: K more lines, TOTAL chars
|
|
23
|
-
* total)` + 尾 + offset 续读提示;≤ maxChars 原样返回(无假截断注)。K = 头尾之间
|
|
24
|
-
* 的中段行数——头(≤60%) + 尾(≤余预算) 均行级累加、绝不半行切开;中段存在时 K ≥ 1
|
|
25
|
-
* (防御:K ≤ 0 时静默返回头尾拼合——不谎报截断)。
|
|
26
|
-
*/
|
|
27
|
-
export function truncateAdvisorResult(result, maxChars) {
|
|
28
|
-
if (result.length <= maxChars) return result
|
|
29
|
-
const totalChars = result.length
|
|
30
|
-
const lines = result.split("\n")
|
|
31
|
-
const headBudget = Math.floor(maxChars * ADVISOR_HEAD_RATIO)
|
|
32
|
-
let head = ""
|
|
33
|
-
let headLines = 0
|
|
34
|
-
let chars = 0
|
|
35
|
-
while (headLines < lines.length && chars + lines[headLines].length + 1 <= headBudget) {
|
|
36
|
-
chars += lines[headLines].length + 1
|
|
37
|
-
head += lines[headLines] + "\n"
|
|
38
|
-
headLines++
|
|
39
|
-
}
|
|
40
|
-
const tailBudget = Math.max(0, maxChars - chars - NOTE_RESERVE_CHARS)
|
|
41
|
-
let tail = ""
|
|
42
|
-
let tailChars = 0
|
|
43
|
-
let tailStart = lines.length
|
|
44
|
-
while (tailStart > headLines && tailChars + lines[tailStart - 1].length + 1 <= tailBudget) {
|
|
45
|
-
tailChars += lines[tailStart - 1].length + 1
|
|
46
|
-
tailStart--
|
|
47
|
-
tail = lines[tailStart] + "\n" + tail
|
|
48
|
-
}
|
|
49
|
-
const middle = tailStart - headLines
|
|
50
|
-
if (middle <= 0) return head + tail // 头尾已覆盖全部行——绝不产生假截断注
|
|
51
|
-
return (
|
|
52
|
-
head +
|
|
53
|
-
`\n… (truncated: ${middle} more lines, ${totalChars} chars total)\n` +
|
|
54
|
-
tail +
|
|
55
|
-
`To see more content, use: read(path, offset=${headLines + 1}, limit=200)`
|
|
56
|
-
)
|
|
57
|
-
}
|
package/src/advisor.mjs
DELETED
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* advisor.mjs — advisor system-prompt selection, follow-up building, session assembly.
|
|
3
|
-
* User-message building lives in advisor/messages.mjs; execution (tool loop, provider
|
|
4
|
-
* resolution, review entry) in advisor/run.mjs; history extraction in advisor/history.mjs.
|
|
5
|
-
* Path classification (code / doc / temp) and the project declaration surface live in
|
|
6
|
-
* ../conventions.mjs — the single authority every gate consumes (repos.mjs included).
|
|
7
|
-
*
|
|
8
|
-
* The advisor runs as a read-only exploration sub-agent with tools
|
|
9
|
-
* (read, glob, grep, ls, lsp, code_search) — ZERO git, every round. The change
|
|
10
|
-
* surface comes from the review scope (paths / _touchedFiles), never from git;
|
|
11
|
-
* verification is `read`-only with quoted-line evidence (7d49a52, d3be613).
|
|
12
|
-
*
|
|
13
|
-
* Config:
|
|
14
|
-
* { advisor: { enabled: true, provider: "deepseek", model: "deepseek-chat" } }
|
|
15
|
-
* provider + model are optional — defaults to the main agent's provider/model.
|
|
16
|
-
*
|
|
17
|
-
* Convergence protocol:
|
|
18
|
-
* Round 1: full review → produces a numbered issue table.
|
|
19
|
-
* Agent responds with a response table per issue (fix claims).
|
|
20
|
-
* Round 2: semi-convergence — verifies the prior table + can flag obvious new issues.
|
|
21
|
-
* Round 3+: strict convergence — only checks the prior issue table.
|
|
22
|
-
* The prior issue table IS injected into rounds 2+ (decision 2026-08-05,
|
|
23
|
-
* reversed) — it is the ONLY complete verification list: the agent response
|
|
24
|
-
* table covers only issues the agent chose to answer, so skipped issues would
|
|
25
|
-
* silently escape convergence without it. The fix-claim table travels as a
|
|
26
|
-
* focus reference only. Restatement risk is handled mechanically:
|
|
27
|
-
* host-verified citations reject references that do not match the current
|
|
28
|
-
* disk state, and fresh sessions exclude old read data.
|
|
29
|
-
* Each round replaces the system prompt (ROUND1 → ROUND2 → ROUND3) so the
|
|
30
|
-
* round-1 full-scope mandate can't bleed into later rounds, plus a mechanical
|
|
31
|
-
* cap (MAX_ADVISOR_ROUNDS in run.mjs) refuses a 6th review call outright.
|
|
32
|
-
* Rounds 2+ also declare all earlier diffs STALE and require read-verified
|
|
33
|
-
* file:line evidence for any unfixed/new finding — see docs/design/ADVISOR-CONVERGENCE.md.
|
|
34
|
-
*
|
|
35
|
-
* Session memory (agent._advisorSession):
|
|
36
|
-
* RETAINED for initialization compatibility but NEVER read (decision d698434):
|
|
37
|
-
* every review round builds a fresh [system, user] session — round 2+ must not
|
|
38
|
-
* reuse round 1's messages, because the old read outputs are the anchoring
|
|
39
|
-
* source of re-review false reports and a token sink. Convergence data (prior
|
|
40
|
-
* issue table + agent response table) travels via buildAdvisorFollowUp.
|
|
41
|
-
* The field is reset by runAgent; the write sites are harmless leftovers.
|
|
42
|
-
*
|
|
43
|
-
* Project customisation: .thincoder/advisor.md in the project root.
|
|
44
|
-
*/
|
|
45
|
-
import { readFileSync } from "node:fs"
|
|
46
|
-
import { join, dirname } from "node:path"
|
|
47
|
-
import { fileURLToPath } from "node:url"
|
|
48
|
-
import { extractAgentResponseTable } from "./advisor/history.mjs"
|
|
49
|
-
import { buildAdvisorUserMessage, resolveScopeFiles, buildObjectDeclarationBlock, buildDesignApprovalBlock } from "./advisor/messages.mjs"
|
|
50
|
-
import { buildConvergenceBody } from "./advisor/convergence.mjs"
|
|
51
|
-
import { escapeLiteralEscapes } from "./escape.mjs"
|
|
52
|
-
// Re-export for run.mjs and tests (keeps their imports from "../advisor.mjs" stable)
|
|
53
|
-
export { ADVISOR_MD_PATH, extractAgentResponseTable, extractConversationBackground } from "./advisor/history.mjs"
|
|
54
|
-
export { buildAdvisorUserMessage } from "./advisor/messages.mjs"
|
|
55
|
-
export { buildObjectDeclarationBlock, buildDesignApprovalBlock } from "./advisor/messages.mjs"
|
|
56
|
-
|
|
57
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
58
|
-
|
|
59
|
-
// ────────────────────────────────────────
|
|
60
|
-
// Prompt files — loaded at module init
|
|
61
|
-
// ────────────────────────────────────────
|
|
62
|
-
|
|
63
|
-
function loadPrompt(file, name) {
|
|
64
|
-
try {
|
|
65
|
-
return readFileSync(join(__dirname, "prompts", file), "utf8")
|
|
66
|
-
} catch {
|
|
67
|
-
throw new Error(`${name} missing from the installation (prompts/${file}) — reinstall thincoder or restore the file`)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const ADVISOR_ROUND1 = loadPrompt("advisor-round1.md", "advisor-round1.md")
|
|
72
|
-
// ROUND2/3 are used whenever a convergence round (round 2+) is being built:
|
|
73
|
-
// in-run session continuation replaces the system prompt with them, and a
|
|
74
|
-
// rebuilt fresh session (e.g. after a failed review) also selects them via
|
|
75
|
-
// buildAdvisorSystemPrompt when _advisorRound > 0.
|
|
76
|
-
const ADVISOR_ROUND2 = loadPrompt("advisor-round2.md", "advisor-round2.md")
|
|
77
|
-
const ADVISOR_ROUND3 = loadPrompt("advisor-round3.md", "advisor-round3.md")
|
|
78
|
-
// Design-review prompt — hard-loaded like the round prompts (decision
|
|
79
|
-
// 2026-08-21): a missing file means a broken installation, and silently
|
|
80
|
-
// degrading to a lesser in-code prompt would quietly strip the approval-signal
|
|
81
|
-
// and citation rules, disabling design approval entirely. loadPrompt throws.
|
|
82
|
-
const ADVISOR_DESIGN = loadPrompt("advisor-design.md", "advisor-design.md")
|
|
83
|
-
|
|
84
|
-
// ────────────────────────────────────────
|
|
85
|
-
// System prompt building
|
|
86
|
-
// ────────────────────────────────────────
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Build the system prompt for an advisor review session.
|
|
90
|
-
* @param {Object} agent — the parent agent
|
|
91
|
-
* @param {Object|null} [prior] — prior review output (full text; decision 2026-08-08)
|
|
92
|
-
* @param {string} [reviewType] — "design" for design review, undefined/"code" for code review
|
|
93
|
-
* @returns {string} the system prompt
|
|
94
|
-
*/
|
|
95
|
-
/** Append local time so the reviewer knows "now" (same grounding as the agent loop). */
|
|
96
|
-
function withTime(prompt) {
|
|
97
|
-
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "local"
|
|
98
|
-
return prompt + `\n\nCurrent time: ${new Date().toLocaleString("sv-SE")} (${timeZone}).`
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function buildAdvisorSystemPrompt(agent, prior, reviewType) {
|
|
102
|
-
// Round decision is DETERMINISTIC (decision 2026-08-08): _advisorRound > 0
|
|
103
|
-
// with a stored review output means convergence (round 2+); 0 means round 1.
|
|
104
|
-
// No prior-table parsing, no all-clear phrase matching — the round counter
|
|
105
|
-
// and the stored output are the only inputs. A restarted process has
|
|
106
|
-
// _advisorRound 0 → conservative full re-review.
|
|
107
|
-
const hasPrior = (agent._advisorRound || 0) > 0 && (prior ?? agent._lastAdvisorOutput)
|
|
108
|
-
// Design review: round 1 uses the dedicated design-review prompt (full scope +
|
|
109
|
-
// approval token); rounds 2+ converge like code reviews (verify agent fix claims).
|
|
110
|
-
if (reviewType === "design") {
|
|
111
|
-
if (!hasPrior) {
|
|
112
|
-
return ADVISOR_DESIGN
|
|
113
|
-
}
|
|
114
|
-
const round = (agent._advisorRound || 0) + 1
|
|
115
|
-
if (round === 2) return ADVISOR_ROUND2
|
|
116
|
-
return ADVISOR_ROUND3
|
|
117
|
-
}
|
|
118
|
-
if (!hasPrior) return ADVISOR_ROUND1
|
|
119
|
-
const round = (agent._advisorRound || 0) + 1
|
|
120
|
-
if (round === 2) return ADVISOR_ROUND2
|
|
121
|
-
return ADVISOR_ROUND3
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// ────────────────────────────────────────
|
|
125
|
-
// Follow-up building (round 2+)
|
|
126
|
-
// ────────────────────────────────────────
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Build a follow-up user message for round 2+ — the agent's response table +
|
|
130
|
-
* round-aware instructions, without re-sending the full round-1 context.
|
|
131
|
-
* Deliberately NO git information injected (no diff snapshot, no git context):
|
|
132
|
-
* git output misled re-reviews — committed fixes never show in `git diff HEAD`,
|
|
133
|
-
* so the model read "no changes" as "no fixes". Verification is `read`-only.
|
|
134
|
-
* NOTE: the caller (prepareAdvisorMessages) applies escapeLiteralEscapes to
|
|
135
|
-
* the return value — direct callers must do the same (the prior table and
|
|
136
|
-
* agent response can quote literal "\x"/"\u" sequences).
|
|
137
|
-
* @param {Object} agent — the parent agent (history used for the response table)
|
|
138
|
-
* @param {Object|null} prior — prior issue table (extracted from history when null)
|
|
139
|
-
* @param {string[]|null} [scopeFiles] — review surface for the no-response fallback (cwd-relative)
|
|
140
|
-
* @param {Object|null} [object] — review-object declaration (§18.8): mechanically
|
|
141
|
-
* prepended to the round-2+ follow-up so every round stays anchored (T-OA2).
|
|
142
|
-
* @returns {string} the follow-up user message — or a plain "System reminder: …"
|
|
143
|
-
* fresh-review fallback (NO brackets — some OpenAI-compatible servers parse
|
|
144
|
-
* '['-prefixed content as structured data / expand escapes) when no prior
|
|
145
|
-
* review exists at all (caller misuse; the response-table extraction would
|
|
146
|
-
* otherwise scan history from index 0 and could match an unrelated stale table)
|
|
147
|
-
*/
|
|
148
|
-
export function buildAdvisorFollowUp(agent, prior, scopeFiles = null, object = null) {
|
|
149
|
-
// Convergence follow-up REQUIRES a prior review record — the full output of
|
|
150
|
-
// the last review, injected VERBATIM (decision 2026-08-08: the model
|
|
151
|
-
// understands the review output; no table/header/phrase parsing). The caller
|
|
152
|
-
// usually passes it; fall back to the stored agent._lastAdvisorOutput.
|
|
153
|
-
const p = prior ?? agent._lastAdvisorOutput
|
|
154
|
-
if (!p) {
|
|
155
|
-
// Plain "System reminder:" prefix (no brackets) — same convention as the
|
|
156
|
-
// round-1 path (some OpenAI-compatible servers parse '['-prefixed content
|
|
157
|
-
// as structured data / expand escapes; see prepareAdvisorMessages).
|
|
158
|
-
return "System reminder: convergence follow-up requested without a prior review — perform a fresh full review."
|
|
159
|
-
}
|
|
160
|
-
// Convergence semantics require round >= 2 (round 1 is the full review, not
|
|
161
|
-
// verification). A direct caller with _advisorRound 0 would otherwise get a
|
|
162
|
-
// meaningless "Round 1 — Strict Verification".
|
|
163
|
-
if ((agent._advisorRound || 0) < 1) {
|
|
164
|
-
return "System reminder: convergence follow-up requested at round 1 — a full review is already in progress; no prior verification exists yet."
|
|
165
|
-
}
|
|
166
|
-
const noResponseFallback = scopeFiles?.length
|
|
167
|
-
? "(Agent did not provide a response table — perform a fresh review of: " + scopeFiles.slice(0, 10).join(", ") + ")"
|
|
168
|
-
: "(Agent did not provide a response table — perform a fresh full review; the review surface is unknown, ask the user for the file list)"
|
|
169
|
-
const response = extractAgentResponseTable(agent.history) || noResponseFallback
|
|
170
|
-
const round = (agent._advisorRound || 0) + 1
|
|
171
|
-
// Review-object declaration FIRST (T-OA2 — round 2+ stays anchored, no re-archaeology).
|
|
172
|
-
const declaration = buildObjectDeclarationBlock(object)
|
|
173
|
-
return (declaration ? declaration + "\n" : "") + buildConvergenceBody(p, response, round, scopeFiles)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Resolve the review surface for the convergence fallback — moved to
|
|
178
|
-
* messages.mjs so the legacy path shares it (see there).
|
|
179
|
-
*/
|
|
180
|
-
|
|
181
|
-
// escapeLiteralEscapes 已抽到 ./escape.mjs(advisor 与主 agent 发送路径共用),
|
|
182
|
-
// 这里 re-export 保持既有 import 稳定。
|
|
183
|
-
export { escapeLiteralEscapes }
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Build the advisor conversation for this run.
|
|
188
|
-
* EVERY call builds a fresh [system, user] session (decision d698434) — no
|
|
189
|
-
* session reuse across rounds: round 1 = full scope (ROUND1 prompt), rounds
|
|
190
|
-
* 2+ = convergence (ROUND2/ROUND3 prompt + fix-claims follow-up).
|
|
191
|
-
* @param {Object} agent — the parent agent
|
|
192
|
-
* @param {string} [reviewType] — "design" or "code" (default)
|
|
193
|
-
* @param {string|null} [designToken] — design-review approval token (design only)
|
|
194
|
-
* @param {string[]|null} [documents] — design review only: explicit list of doc paths to review (passed through to buildAdvisorUserMessage)
|
|
195
|
-
* @param {string[]|null} [paths] — code review only: explicit list of file/dir paths to review
|
|
196
|
-
* @param {Object|null} [object] — review-object declaration (§18.8 D-OA1): passed through
|
|
197
|
-
* to the user-message builders; mechanically injected at the start of every review
|
|
198
|
-
* round (round 1 design/code + round 2+ follow-up). Absent → legacy behavior.
|
|
199
|
-
* @param {string|null} [designId] — §29.1 F2a: injected next to the design token in
|
|
200
|
-
* the Approval Signal (round 1 + round 2+ — both values, verbatim anchor).
|
|
201
|
-
*/
|
|
202
|
-
export function prepareAdvisorMessages(agent, reviewType, designToken = null, documents = null, paths = null, priorParam = null, object = null, designId = null) {
|
|
203
|
-
// Deterministic convergence state (decision 2026-08-08): round 2+ requires
|
|
204
|
-
// _advisorRound > 0 AND a stored prior review output. No history parsing.
|
|
205
|
-
// priorParam (direct callers) wins over the stored output — same derivation
|
|
206
|
-
// as buildAdvisorSystemPrompt (single source of truth for round semantics).
|
|
207
|
-
const prior = (agent._advisorRound || 0) > 0 ? (priorParam ?? agent._lastAdvisorOutput) : null
|
|
208
|
-
|
|
209
|
-
// Design review round 1: the dedicated full-scope review with the approval
|
|
210
|
-
// token (an independent gate — it runs even when a prior review exists, e.g.
|
|
211
|
-
// after a failed design review). Fresh session.
|
|
212
|
-
if (reviewType === "design" && (agent._advisorRound || 0) === 0) {
|
|
213
|
-
return [
|
|
214
|
-
{ role: "system", content: withTime(buildAdvisorSystemPrompt(agent, prior, reviewType)) },
|
|
215
|
-
{ role: "user", content: escapeLiteralEscapes(buildAdvisorUserMessage(agent, prior, reviewType, designToken, documents, paths, object, designId)) },
|
|
216
|
-
]
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Every round is a FRESH session (decision d698434): round 2+ must NOT reuse
|
|
220
|
-
// round 1's messages — the old read outputs are the top anchoring source of
|
|
221
|
-
// re-review false reports (the model quoted pre-fix file content instead of
|
|
222
|
-
// re-reading) and a token sink. The agent response table (fix claims) is
|
|
223
|
-
// injected through buildAdvisorFollowUp instead; the system prompt carries
|
|
224
|
-
// the round (ROUND2/ROUND3) via buildAdvisorSystemPrompt.
|
|
225
|
-
// No prior review output: reset ONLY when this run made no code changes (user
|
|
226
|
-
// decision 2026-08-05: any loop that modified code must NOT reset — the
|
|
227
|
-
// advisor guard WILL push back, so the convergence round must keep advancing
|
|
228
|
-
// toward the cap; a run with no mutations has no push-back risk and a reset
|
|
229
|
-
// is safe). Deterministic runtime state (`_mutatedThisRun`) decides — never
|
|
230
|
-
// model output (phrases/table headers drift; three rounds of false reports
|
|
231
|
-
// proved it). Either way the message is a fresh full review (no prior output
|
|
232
|
-
// exists without a completed review) — only the round counter differs.
|
|
233
|
-
if (!prior || (agent._advisorRound || 0) === 0) {
|
|
234
|
-
if (!(agent._mutatedThisRun ?? false)) {
|
|
235
|
-
// New review cycle (first review, all-clear, or no code changes): reset
|
|
236
|
-
// the round so the cycle gets its own 5-round budget.
|
|
237
|
-
agent._advisorRound = 0
|
|
238
|
-
}
|
|
239
|
-
// Mutations exist → KEEP the round (cap keeps advancing through retries).
|
|
240
|
-
const user = buildAdvisorUserMessage(agent, prior, reviewType, designToken, documents, paths, object, designId)
|
|
241
|
-
return [
|
|
242
|
-
{ role: "system", content: withTime(buildAdvisorSystemPrompt(agent, prior, reviewType)) },
|
|
243
|
-
{
|
|
244
|
-
role: "user",
|
|
245
|
-
// NOTE (2026-08-06): the leading prefix is a PLAIN "System reminder:",
|
|
246
|
-
// NOT "[System reminder: ...]" — some OpenAI-compatible servers try to
|
|
247
|
-
// parse content that STARTS with '[' as structured content (or expand
|
|
248
|
-
// escape sequences in it). A literal "\x" inside the conversation
|
|
249
|
-
// background (e.g. the parent agent quoting escape sequences) then
|
|
250
|
-
// fails server-side as "unexpected end of hex escape" → 400. Plain
|
|
251
|
-
// prefix keeps the review message a plain string everywhere.
|
|
252
|
-
// The whole content also passes through escapeLiteralEscapes (below)
|
|
253
|
-
// so literal "\x"/"\u" quoted by the parent agent can never form an
|
|
254
|
-
// invalid escape when the server expands them.
|
|
255
|
-
content: escapeLiteralEscapes(`System reminder: no prior issue table is being carried into this review (first review, app restart, or session clear) — start with a fresh full review.\n\n${user}`),
|
|
256
|
-
},
|
|
257
|
-
]
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Convergence rounds (2+): fresh [system(ROUND2/3), user(prior table + fix
|
|
261
|
-
// claims)]. buildAdvisorFollowUp carries BOTH the prior issue table (the
|
|
262
|
-
// only complete verification list — decision 2026-08-05, reversed) and the
|
|
263
|
-
// agent's fix-claim table (focus reference). buildAdvisorSystemPrompt
|
|
264
|
-
// selects ROUND2 for round 2, ROUND3 for rounds 3+ — a failed review retry
|
|
265
|
-
// keeps _advisorRound so the convergence prompt matches the attempt count.
|
|
266
|
-
// scopeFiles gives the fallback (agent gave no response table) a concrete
|
|
267
|
-
// review surface.
|
|
268
|
-
const scopeFiles = resolveScopeFiles(agent, paths)
|
|
269
|
-
const followUp = buildAdvisorFollowUp(agent, prior, scopeFiles, object)
|
|
270
|
-
// §11.2 D-24b (design round 2+ — async fix-round continuations must be able to
|
|
271
|
-
// re-approve): re-anchor the review scope (the convergence follow-up carries no
|
|
272
|
-
// document list) and inject the round's design token with the approval signal.
|
|
273
|
-
if (reviewType === "design") {
|
|
274
|
-
const docList = Array.isArray(documents)
|
|
275
|
-
? documents.filter((d) => typeof d === "string" && d.trim())
|
|
276
|
-
: []
|
|
277
|
-
const scopeBlock = docList.length > 0
|
|
278
|
-
? `\n\n## Documents to Review\nThe documents below are the review scope. Review ONLY these files — do not scan git diff or read any other files.\n${docList.map((d) => `- ${d} — Read this file in full`).join("\n")}`
|
|
279
|
-
: ""
|
|
280
|
-
const tokenBlock = designToken ? `\n\n${buildDesignApprovalBlock(designToken, designId)}` : ""
|
|
281
|
-
return [
|
|
282
|
-
{ role: "system", content: withTime(buildAdvisorSystemPrompt(agent, prior, reviewType)) },
|
|
283
|
-
{ role: "user", content: escapeLiteralEscapes(followUp + scopeBlock + tokenBlock) },
|
|
284
|
-
]
|
|
285
|
-
}
|
|
286
|
-
return [
|
|
287
|
-
{ role: "system", content: withTime(buildAdvisorSystemPrompt(agent, prior, reviewType)) },
|
|
288
|
-
{ role: "user", content: escapeLiteralEscapes(followUp) },
|
|
289
|
-
]
|
|
290
|
-
}
|
package/src/agent/completion.mjs
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* agent/completion.mjs — handle model response with no tool calls
|
|
3
|
-
*
|
|
4
|
-
* Checks: pending tasks, verify guard, advisor guard.
|
|
5
|
-
* Returns { action: 'continue' | 'done', content?, guardPushbacks, honestReminderInjected, advisorPushbacks }
|
|
6
|
-
*/
|
|
7
|
-
import { hasCodeMutations } from "../advisor/repos.mjs"
|
|
8
|
-
import { pushReal } from "../context.mjs"
|
|
9
|
-
import { MAX_ADVISOR_ROUNDS } from "../advisor/run.mjs"
|
|
10
|
-
import { advisorReviewPending, effectiveAdvisorRound } from "../agent-tools/advisor-async.mjs"
|
|
11
|
-
|
|
12
|
-
const MAX_VERIFY_PUSHBACKS = 2
|
|
13
|
-
const MAX_VERIFY_RETRIES = 3
|
|
14
|
-
const MAX_ADVISOR_PUSHBACKS = 3
|
|
15
|
-
const MAX_EMPTY_RETRIES = 2
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Handle a model turn with zero tool calls. May push back (verify/advisor/pending tasks)
|
|
19
|
-
* or accept the completion.
|
|
20
|
-
*
|
|
21
|
-
* @param {object} agent
|
|
22
|
-
* @param {object} response - chat response with .content, .toolCalls
|
|
23
|
-
* @param {number} depth - agent nesting depth (0 = top-level)
|
|
24
|
-
* @param {number} turn - current turn index
|
|
25
|
-
* @param {number} guardPushbacks - verify guard pushback count (mutated)
|
|
26
|
-
* @param {boolean} honestReminderInjected - whether exhausted-verify reminder was already sent (mutated)
|
|
27
|
-
* @param {number} advisorPushbacks - advisor guard pushback count (mutated)
|
|
28
|
-
* @param {object} callbacks - { onTurnEnd }
|
|
29
|
-
*/
|
|
30
|
-
export function handleCompletion(agent, response, depth, turn, guardPushbacks, honestReminderInjected, advisorPushbacks, callbacks) {
|
|
31
|
-
if (!response.content) {
|
|
32
|
-
// Transient empty response (reasoning exhausted / output truncated): instead of
|
|
33
|
-
// aborting the whole turn, inject a reminder and let the model respond again.
|
|
34
|
-
// Bounded — after MAX_EMPTY_RETRIES consecutive empties, surface the original error.
|
|
35
|
-
const retries = agent._emptyRetries ?? 0
|
|
36
|
-
if (retries < MAX_EMPTY_RETRIES) {
|
|
37
|
-
agent._emptyRetries = retries + 1
|
|
38
|
-
agent.history.push({
|
|
39
|
-
role: "user",
|
|
40
|
-
content: "[System reminder: your last response was empty — the provider returned no content (likely reasoning was exhausted or output was truncated). Respond again, continuing your work from where you left off.]",
|
|
41
|
-
})
|
|
42
|
-
callbacks.onTurnEnd?.(agent, turn)
|
|
43
|
-
return { action: "continue", guardPushbacks, honestReminderInjected, advisorPushbacks }
|
|
44
|
-
}
|
|
45
|
-
throw new Error(
|
|
46
|
-
"LLM returned empty response (likely reasoning exhausted or output truncated). " +
|
|
47
|
-
"Try lowering reasoning effort if this persists (/think in TUI). " +
|
|
48
|
-
`Provider: ${agent.provider.model}`
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Pending tasks: remind the model ONCE before it declares itself done.
|
|
53
|
-
// Deliberately capped at one pushback (reported pain: unbounded looping when a
|
|
54
|
-
// pending item can't be resolved). After the single reminder the model is free
|
|
55
|
-
// to finish — updating the task list (task tool) resets the budget, so a fresh
|
|
56
|
-
// list state earns one fresh reminder.
|
|
57
|
-
if (depth === 0 && agent.tasks.some((t) => t.status === "pending") && (agent._taskPushbacks ?? 0) < 1) {
|
|
58
|
-
agent._taskPushbacks = (agent._taskPushbacks ?? 0) + 1
|
|
59
|
-
const pending = agent.tasks.filter((t) => t.status === "pending").map((t) => t.title).join(", ")
|
|
60
|
-
pushReal(agent, { role: "assistant", content: response.content })
|
|
61
|
-
agent.history.push({
|
|
62
|
-
role: "user",
|
|
63
|
-
content: `[System reminder: you still have pending tasks: ${pending}. Update their status with the task tool before finishing — if they're done, mark them done; if they're not applicable, remove them. (This is your only reminder — if you choose not to, finish anyway.)]`,
|
|
64
|
-
})
|
|
65
|
-
callbacks.onTurnEnd?.(agent, turn)
|
|
66
|
-
return { action: "continue", guardPushbacks, honestReminderInjected, advisorPushbacks }
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// --- verify guard: push model to verify mutated files before completion ---
|
|
70
|
-
// OPT-IN ONLY (verifyGuard: true). Engineering mode is excluded because it
|
|
71
|
-
// uses flow-driven review, not per-turn mechanical pushback (ENGINEERING-MODE.md §2.3).
|
|
72
|
-
// Backward compat: also accept root-level verifyGuard
|
|
73
|
-
const verifyGuard = agent.config?.agent?.verifyGuard ?? agent.config?.verifyGuard
|
|
74
|
-
if (depth === 0 && verifyGuard === true && !agent.config?.agent?.engineering) {
|
|
75
|
-
// Not verified yet → pushback to run verify
|
|
76
|
-
if (agent._mutatedThisRun && !agent._verifiedThisRun && hasCodeMutations(agent) && guardPushbacks < MAX_VERIFY_PUSHBACKS) {
|
|
77
|
-
guardPushbacks++
|
|
78
|
-
pushReal(agent, { role: "assistant", content: response.content })
|
|
79
|
-
agent.history.push({
|
|
80
|
-
role: "user",
|
|
81
|
-
content: "[System reminder: you modified files in this run but have not verified the changes. Before finishing: run the project's verification yourself (per its AGENTS.md test method), then call verify declaring the outcome via verification.status. verify mechanically gates on your declaration. If verification is genuinely impossible here, say so explicitly in your reply.]",
|
|
82
|
-
})
|
|
83
|
-
callbacks.onTurnEnd?.(agent, turn)
|
|
84
|
-
return { action: "continue", guardPushbacks, honestReminderInjected, advisorPushbacks }
|
|
85
|
-
}
|
|
86
|
-
// Verified but not passed → pushback to fix/complete (up to MAX_VERIFY_RETRIES).
|
|
87
|
-
// _verifyPassed === false means the declaration was failed, skipped without a
|
|
88
|
-
// reason, or not declared — not necessarily that a test failed.
|
|
89
|
-
if (agent._verifiedThisRun && agent._verifyPassed === false && agent._verifyRetries < MAX_VERIFY_RETRIES) {
|
|
90
|
-
agent._verifyRetries++
|
|
91
|
-
pushReal(agent, { role: "assistant", content: response.content })
|
|
92
|
-
agent.history.push({
|
|
93
|
-
role: "user",
|
|
94
|
-
content: `[System reminder: (retry ${agent._verifyRetries}/${MAX_VERIFY_RETRIES}) verify was not passed — either your verification declared failed, was skipped without a reason, or was not declared. Fix or complete your verification, then call verify again declaring the outcome. If you cannot fix after ${MAX_VERIFY_RETRIES} attempts, explain honestly what's blocking you.]`,
|
|
95
|
-
})
|
|
96
|
-
callbacks.onTurnEnd?.(agent, turn)
|
|
97
|
-
return { action: "continue", guardPushbacks, honestReminderInjected, advisorPushbacks }
|
|
98
|
-
}
|
|
99
|
-
// Exhausted retries — inject honesty reminder once
|
|
100
|
-
if (agent._verifiedThisRun && agent._verifyPassed === false && agent._verifyRetries >= MAX_VERIFY_RETRIES) {
|
|
101
|
-
if (honestReminderInjected) {
|
|
102
|
-
pushReal(agent, { role: "assistant", content: response.content })
|
|
103
|
-
return { action: "done", content: response.content, guardPushbacks, honestReminderInjected, advisorPushbacks }
|
|
104
|
-
}
|
|
105
|
-
honestReminderInjected = true
|
|
106
|
-
pushReal(agent, { role: "assistant", content: response.content })
|
|
107
|
-
agent.history.push({
|
|
108
|
-
role: "user",
|
|
109
|
-
content: `[System reminder: ${MAX_VERIFY_RETRIES} verify attempts exhausted. You have not passed verification. Either state explicitly that your verification could not be completed, or run verify again once it is. If your verification could not be completed, say so explicitly in your reply to the user — state what you tried and what you believe is blocking you, and do not present the work as complete; the user needs to know it is unfinished.]`,
|
|
110
|
-
})
|
|
111
|
-
callbacks.onTurnEnd?.(agent, turn)
|
|
112
|
-
return { action: "continue", guardPushbacks, honestReminderInjected, advisorPushbacks }
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// --- advisor guard: review of mutated files before completion ---
|
|
117
|
-
// OPT-IN ONLY (advisor.guard === true, default OFF — 2026-08-21 semantic
|
|
118
|
-
// refactor), and NEVER in engineering mode. The advisor tool itself is always
|
|
119
|
-
// available; this guard only controls whether completion is pushed back.
|
|
120
|
-
const cfg = agent.config?.advisor
|
|
121
|
-
const advisorReview = cfg?.guard === true
|
|
122
|
-
if (depth === 0 && advisorReview && !agent.config?.agent?.engineering) {
|
|
123
|
-
// §11.2 D-24b (T-24b4 — guard timing): an async review that is still in flight
|
|
124
|
-
// (or queued in the advisor pool) means the review was launched — the guard
|
|
125
|
-
// does NOT push back while it is pending (未决不算未评审); once it settles
|
|
126
|
-
// non-stale it marks _calledAdvisorThisRun, and a STALE settle leaves the
|
|
127
|
-
// mark unset so the guard pushes back here again (fix #2 — no silent skip).
|
|
128
|
-
const pending = advisorReviewPending(agent)
|
|
129
|
-
const rounds = effectiveAdvisorRound(agent)
|
|
130
|
-
if (!pending && agent._mutatedThisRun && !agent._calledAdvisorThisRun && hasCodeMutations(agent)
|
|
131
|
-
&& advisorPushbacks < MAX_ADVISOR_PUSHBACKS
|
|
132
|
-
&& rounds < MAX_ADVISOR_ROUNDS) {
|
|
133
|
-
advisorPushbacks++
|
|
134
|
-
pushReal(agent, { role: "assistant", content: response.content })
|
|
135
|
-
agent.history.push({
|
|
136
|
-
role: "user",
|
|
137
|
-
content: `[System reminder: you changed code in this run and MUST get an advisor review before finishing (round ${rounds + 1}). Call the \`advisor\` tool now. This is required, not optional — do not skip it even if you believe the changes are trivial — the review will be quick either way. After the review, produce a response table for every issue found (see discipline rules for format).]`,
|
|
138
|
-
})
|
|
139
|
-
callbacks.onTurnEnd?.(agent, turn)
|
|
140
|
-
return { action: "continue", guardPushbacks, honestReminderInjected, advisorPushbacks }
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
pushReal(agent, { role: "assistant", content: response.content })
|
|
145
|
-
return { action: "done", content: response.content, guardPushbacks, honestReminderInjected, advisorPushbacks }
|
|
146
|
-
}
|