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/agent.mjs
DELETED
|
@@ -1,417 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* agent.mjs — Agent main loop
|
|
3
|
-
* LLM ↔ tool-call loop, until the task is done.
|
|
4
|
-
*/
|
|
5
|
-
import { chat } from "./provider/index.mjs"
|
|
6
|
-
import { abortError, annotateAbort } from "./abort-provenance.mjs"
|
|
7
|
-
import { pushReal, summarizeRunExplorations } from "./context.mjs"
|
|
8
|
-
import { specForModel } from "./config.mjs"
|
|
9
|
-
import { resolve } from "node:path"
|
|
10
|
-
import { executeToolCalls } from "./agent/dispatch.mjs"
|
|
11
|
-
import { recordToolResults } from "./agent/record-results.mjs"
|
|
12
|
-
import { FILE_MUTATORS } from "./agent/helpers.mjs"
|
|
13
|
-
import { prepareRun } from "./agent/setup.mjs"
|
|
14
|
-
import { injectPostTurn } from "./agent/post-turn.mjs"
|
|
15
|
-
import { handleCompletion } from "./agent/completion.mjs"
|
|
16
|
-
// PROMPT-SYSTEM 施工② G1(2026-09-10):六件槽位常量装载收口 prompt-overlays.mjs
|
|
17
|
-
// (与子代理角色常量同源——单一权威锚 D1);旧三件文件读取随本批退役。
|
|
18
|
-
// 槽位常量通过下方 re-export 面(mod: rel — re-export 保 import 面);本文件自身零直接消费。
|
|
19
|
-
// 主循环阶段函数(压缩检查/注入组/回合收尾)2026-09-05 实践轮迁 agent/run-stages.mjs
|
|
20
|
-
import { runCompactionCheck, injectTurnReminders, finalizeAgentTurn, injectResponseReminders } from "./agent/run-stages.mjs"
|
|
21
|
-
import {
|
|
22
|
-
escapeXml, repairHistory, listWorkDir,
|
|
23
|
-
readonlyToolNames, collectGitContext, loadProjectInstructions,
|
|
24
|
-
ContinueError,
|
|
25
|
-
turnFrame, // 第 19 批(TURN-ACROSS-SEGMENTS):跨段累计编号帧(设计 §19.3)
|
|
26
|
-
DEFAULT_MAX_TURNS, DEFAULT_SUBAGENT_TURNS,
|
|
27
|
-
MIN_REPORT_CHARS, REPORT_CONTINUATION,
|
|
28
|
-
AUTO_TURN_DIGEST_DOMAIN,
|
|
29
|
-
} from "./agent/helpers.mjs"
|
|
30
|
-
// ENG 提醒族 + auto-turn domain 2026-09-05 迁 agent/helpers.mjs(agent.mjs 530 > 500 硬限)
|
|
31
|
-
// PROMPT-SYSTEM 施工② G1(2026-09-10):六件槽位常量装载收口 prompt-overlays.mjs
|
|
32
|
-
// (与子代理角色常量同源——单一权威锚 D1);本文件 re-export 保 import 面。
|
|
33
|
-
export {
|
|
34
|
-
PERSONA_ENGINEERING, PERSONA_NORMAL, COMMON,
|
|
35
|
-
DISCIPLINE_ENGINEERING, DISCIPLINE_NORMAL,
|
|
36
|
-
CONSULT_BASE,
|
|
37
|
-
} from "./prompt-overlays.mjs"
|
|
38
|
-
export { ENG_ON_REMINDER, ENG_OFF_REMINDER } from "./agent/helpers.mjs"
|
|
39
|
-
|
|
40
|
-
// Role → persona slot mapping note(施工② G3): 子代理人格由 assemblePrompt 场景表按
|
|
41
|
-
// role 承载(persona-explore/coder/plan/eng-coder.md——D1 表 = 蓝图 §3.2 1:1);
|
|
42
|
-
// spawn 侧 overlay 概念已退役(prompt-overlays.mjs 不再导出 OVERLAY 别名常量)。
|
|
43
|
-
|
|
44
|
-
// exported for consumption by agent-tools.mjs
|
|
45
|
-
export {
|
|
46
|
-
ContinueError,
|
|
47
|
-
listWorkDir, loadProjectInstructions,
|
|
48
|
-
readonlyToolNames, collectGitContext, escapeXml,
|
|
49
|
-
MIN_REPORT_CHARS, REPORT_CONTINUATION, DEFAULT_SUBAGENT_TURNS,
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// Re-exported for API compatibility (single source of truth: advisor/repos.mjs)
|
|
54
|
-
export { hasCodeMutations } from "./advisor/repos.mjs"
|
|
55
|
-
|
|
56
|
-
/** Create a new agent state object with all fields initialized to defaults */
|
|
57
|
-
export function createAgent({
|
|
58
|
-
provider, tools, config, cwd, memory, overlay, role,
|
|
59
|
-
tasks = [], history = [],
|
|
60
|
-
planMode = false, autoApprove = false,
|
|
61
|
-
goal = null, sessionStart = null,
|
|
62
|
-
}) {
|
|
63
|
-
return {
|
|
64
|
-
provider, tools, config, cwd, memory, _role: role,
|
|
65
|
-
overlay, tasks, history,
|
|
66
|
-
planMode, autoApprove, goal,
|
|
67
|
-
_mutatedThisRun: false, _verifiedThisRun: false, _verifyPassed: undefined, _calledAdvisorThisRun: false,
|
|
68
|
-
_engDesignReviewed: false, // eng-coder: design review gate passed (hard gate in dispatch.mjs)
|
|
69
|
-
// DESIGN-TOKEN-SETTLEMENT D3 (2026-09-08): single-value `_engDesignToken` mirror retired
|
|
70
|
-
// (AC3 零写) — no field initializer; the multi-slot Map `_engDesignTokens` is the
|
|
71
|
-
// authoritative ledger (hydrated by restoreEngTokens / written by settle).
|
|
72
|
-
_touchedFiles: [], _verifyRetries: 0, _advisorRound: 0, _advisorSession: null,
|
|
73
|
-
_advisorRuns: new Map(), // §11.2 D-24b: per-review convergence instances (rounds/prior/designId)
|
|
74
|
-
_mutationSeq: 0, _mutLog: [], // §11.2 D-24b: mutation log (in-flight review staleness scan)
|
|
75
|
-
_lastAdvisorOutput: null, // full review output from the most recent advisor call (convergence rounds inject it verbatim)
|
|
76
|
-
_lastEngState: false,
|
|
77
|
-
_pendingReminders: [],
|
|
78
|
-
_pendingTimers: [],
|
|
79
|
-
_sessionStart: sessionStart,
|
|
80
|
-
_lastPromptTokens: null, _usageAtLen: null,
|
|
81
|
-
_compressFailures: 0,
|
|
82
|
-
_emptyRetries: 0, // empty-response retry budget (per-run; reset on a fresh user turn)
|
|
83
|
-
_runStartHistoryLen: 0, // machine-line length at the start of the current run — end-of-run exploration distillation slices from here
|
|
84
|
-
_pendingDistill: null, // in-flight end-of-run exploration distillation (SEND-STALL-DISTILL §2.1) — awaited at next run start / TUI exit flush
|
|
85
|
-
_currentTurn: 0, _maxTurns: DEFAULT_MAX_TURNS, // turn counter for status bar display
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Run the agent loop: LLM ↔ tool-call cycle until task completion or turn limit. Returns final text content. */
|
|
90
|
-
export async function runAgent(agent, input, callbacks = {}, { depth = 0, signal, maxTurns: overrideTurns, resume = false, autoTurn = false, suspDriven = false, consumeInjected = null } = {}) {
|
|
91
|
-
// Previous run's async exploration distillation must settle before this run pushes
|
|
92
|
-
// input (SEND-STALL-DISTILL §2.2 N1) — await first, or its history replace wipes it.
|
|
93
|
-
if (agent._pendingDistill) {
|
|
94
|
-
const p = agent._pendingDistill
|
|
95
|
-
agent._pendingDistill = null
|
|
96
|
-
await p
|
|
97
|
-
}
|
|
98
|
-
// §17 D-S3: suspension-settled async results inject before EVERY run's prepareRun
|
|
99
|
-
// (user + auto-turn); spliced = consumed. collectSettledAsync owns a different
|
|
100
|
-
// container, so no double-inject across the two consumption points.
|
|
101
|
-
// ASYNC-RESULT-CONTAINER.md D2 (2026-09-08):pending 单容器 `_pendingAsyncResults`
|
|
102
|
-
// +role——四族(subagent/advisor/escalate/consult)统一停靠;注入器按 role 分发
|
|
103
|
-
// (consult → injectConsultResult;其余 → injectAsyncResult——族分支同 §25 D-R17a/b)
|
|
104
|
-
// ——单容器一处清,不再逐族三段。
|
|
105
|
-
const pendingAsync = agent._pendingAsyncResults
|
|
106
|
-
if (pendingAsync?.length) {
|
|
107
|
-
const { injectAsyncResult } = await import("./agent-tools/subagent.mjs")
|
|
108
|
-
const { injectConsultResult } = await import("./agent-tools/consult.mjs")
|
|
109
|
-
// TUI-OOM-ROOTCAUSE(AGENT-LOOP.md §23.3.1 消费点②——run 起始 pending 注入):
|
|
110
|
-
// 注入完成后释放条目对子代理对象的持有(childAgent/report 置空——幂等 helper)。
|
|
111
|
-
const { releaseSettledEntry } = await import("./agent-tools/async-settle.mjs")
|
|
112
|
-
for (const e of pendingAsync.splice(0)) {
|
|
113
|
-
if (e.role === "consult") await injectConsultResult(agent, e)
|
|
114
|
-
else await injectAsyncResult(agent, e)
|
|
115
|
-
releaseSettledEntry(e)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
agent._inAutoTurn = autoTurn // spawn gate for manual-tier digests (§17 D-S6/N3)
|
|
119
|
-
const { maxTurns, threshold, tools, toolSchemas, toolByName, systemPrompt } = await prepareRun(
|
|
120
|
-
// G1/G2(施工②):prompt 装配收口 prepareRun 内部(assemblePrompt——prompt-overlays.mjs
|
|
121
|
-
// 槽位常量,与子代理角色常量同源——单一权威锚 D1);本调用不再携带 prompt 常量。
|
|
122
|
-
agent, input, callbacks,
|
|
123
|
-
{ depth, signal, overrideTurns, resume: resume || autoTurn },
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
// Exploration-distillation boundary (CONTEXT-COMPACTION §5): prepareRun already
|
|
127
|
-
// pushed input + injections — appended from here counts as "this run's" work.
|
|
128
|
-
agent._runStartHistoryLen = agent.history.length
|
|
129
|
-
|
|
130
|
-
// Per-run bookkeeping reset — PRESERVED on `resume` (ContinueError continuation):
|
|
131
|
-
// mutation/guard continuity and the convergence budget must survive a continuation.
|
|
132
|
-
if (!resume) {
|
|
133
|
-
// 第 19 批(TURN-ACROSS-SEGMENTS——设计 TURN-CAP-CONTINUE.md §19.3):链内累计编号
|
|
134
|
-
// 只在链起点复位——续跑(resume:true)不重置、不回退(编号帧公式见 helpers.mjs
|
|
135
|
-
// turnFrame)。与下方 mutation/guard 复位同条件同点(全档唯一复位点)。
|
|
136
|
-
agent._turnSeq = 0
|
|
137
|
-
// §17 D-S6: an auto-turn's guard marks are inherited by the next USER run (not
|
|
138
|
-
// reset) so auto-turn changes never escape the guard silently.
|
|
139
|
-
const g = agent._inheritedGuard
|
|
140
|
-
if (g) {
|
|
141
|
-
for (const k of ["_mutatedThisRun", "_verifiedThisRun", "_verifyPassed", "_calledAdvisorThisRun", "_touchedFiles", "_verifyRetries", "_advisorRound"]) agent[k] = g[k]
|
|
142
|
-
agent._inheritedGuard = null
|
|
143
|
-
} else {
|
|
144
|
-
agent._mutatedThisRun = false
|
|
145
|
-
agent._verifiedThisRun = false
|
|
146
|
-
agent._verifyPassed = undefined
|
|
147
|
-
agent._calledAdvisorThisRun = false
|
|
148
|
-
agent._touchedFiles = []
|
|
149
|
-
agent._verifyRetries = 0
|
|
150
|
-
agent._advisorRound = 0
|
|
151
|
-
agent._advisorSession = null // advisor session is per-run: discard when the task ends, next task starts fresh
|
|
152
|
-
agent._emptyRetries = 0 // empty-response retry budget is per-run: a fresh user turn restarts from zero
|
|
153
|
-
agent._compressFailures = 0 // compaction summary-failure counter is per-run: a fresh user turn restarts from zero
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
// §17 D-S6 manual tier: digest action-domain reminder (system-driven turn — organize only).
|
|
157
|
-
if (autoTurn && !agent.autoApprove) {
|
|
158
|
-
agent.history.push({ role: "user", content: AUTO_TURN_DIGEST_DOMAIN, transient: true })
|
|
159
|
-
}
|
|
160
|
-
// eng-coder authorization (_engDesignReviewed) is eng-coder-only: set by subagent-spawn.mjs
|
|
161
|
-
// (spawn gate) / design-token.mjs (design review pass) BEFORE the child runAgent — the
|
|
162
|
-
// depth-0 parent never reads or writes it (the parent gate reads anyLiveDesignSlot; the
|
|
163
|
-
// depth-0 per-turn reset was removed 2026-09-08, ENG-SESSION-PROVIDER-CLEANUP D1.3).
|
|
164
|
-
// Design slots (_engDesignTokens Map) survive across turns (design review → approval →
|
|
165
|
-
// eng-coder spawn) — persisted to the session slot at settle time (DESIGN-TOKEN-
|
|
166
|
-
// SETTLEMENT D1); lifecycle: issued on a passing review, consumed by consume-design / TTL.
|
|
167
|
-
let guardPushbacks = 0
|
|
168
|
-
let advisorPushbacks = 0
|
|
169
|
-
let honestReminderInjected = false
|
|
170
|
-
const recentCallSigs = []
|
|
171
|
-
// "once" stream rules fire at most once per runAgent call; the set survives across
|
|
172
|
-
// chat() calls (rule abort-retry, tool loop) within the turn.
|
|
173
|
-
const streamRuleFired = new Set()
|
|
174
|
-
|
|
175
|
-
// Compaction overhead for the pure-estimation path: system prompt + tools schema are
|
|
176
|
-
// in every request but not in history — without them the first-turn/just-compacted
|
|
177
|
-
// estimate under-counts and may never trigger. Measured path already includes both.
|
|
178
|
-
const compactionOverhead = {
|
|
179
|
-
systemPrompt,
|
|
180
|
-
tools: toolSchemas,
|
|
181
|
-
// §18.6 D-TR4:compress 轨迹 depth 元数据(runAgent 的 depth 在此作用域——
|
|
182
|
-
// context.mjs compressIfNeeded 经 extras 透出到 logCtx)
|
|
183
|
-
traceDepth: depth,
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
let thrownError = null
|
|
187
|
-
try {
|
|
188
|
-
for (let turn = 0; turn < maxTurns; turn++) {
|
|
189
|
-
// 第 19 批(TURN-ACROSS-SEGMENTS——设计 TURN-CAP-CONTINUE.md §19.3):编号帧——
|
|
190
|
-
// `_turnSeq` 每轮 +1(跨段累计,仅 `!resume` 链起点复位);面向消费面的两字段
|
|
191
|
-
// (状态行 + ⟦ev⟧turn / ⟦ev⟧approval 事件共用)在此同点赋值(编号唯一权威;帧
|
|
192
|
-
// 公式 = helpers.mjs turnFrame)。段内帽判定不读帧(下行循环条件只读段内
|
|
193
|
-
// turn / maxTurns——N6)。
|
|
194
|
-
const frame = turnFrame(++agent._turnSeq, turn, maxTurns)
|
|
195
|
-
agent._currentTurn = frame.turn
|
|
196
|
-
agent._maxTurns = frame.maxTurns
|
|
197
|
-
// D2 (AGENT-LOOP.md §7.2): depth>0 children emit a ⟦ev⟧turn progress token each turn —
|
|
198
|
-
// single emit point covering all three spawn tools; phase=llm (tool/done progress rides
|
|
199
|
-
// the onToolCall/onToolResult relay — no token for those). 第 19 批:载荷取上方帧值
|
|
200
|
-
// (agent._currentTurn / _maxTurns——字段形态 / 字段数 / phase 零变化,N5)。
|
|
201
|
-
if (depth > 0 && callbacks.onToken) {
|
|
202
|
-
callbacks.onToken(`⟦ev⟧turn\x1e${agent._currentTurn}\x1e${agent._maxTurns}\x1ellm\x1e`)
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// SUBAGENT-OBSERVE-SEND D2: 子代理回合边界消费点——每轮开头把父侧经 subagent
|
|
206
|
-
// action:'send' 注入队列(entry._injected)的消息按普通 user 回合推入子历史
|
|
207
|
-
// (pushReal → 下一轮 chat 即含该指令)。由 executeAsyncSpawn 经 childRunOpts 贯通的
|
|
208
|
-
// consumeInjected 回调承载(异步子代理专属——缺省 null:主会话/阻塞子代理零开销)。
|
|
209
|
-
consumeInjected?.(agent)
|
|
210
|
-
|
|
211
|
-
const lastRole = agent.history.at(-1)?.role
|
|
212
|
-
if (lastRole === "user" || lastRole === "tool") {
|
|
213
|
-
// 2026-09-05 实践轮:压缩检查/降级计数提为 runCompactionCheck(agent/run-stages.mjs——
|
|
214
|
-
// CLI 对位 VS run-stages)——循环骨架此处只剩检查调用(recentCallSigs 对象引用回流)。
|
|
215
|
-
await runCompactionCheck(agent, { threshold, callbacks, compactionOverhead, signal, recentCallSigs })
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// 2026-09-05 实践轮:plan cadence + eng 状态注入提为 injectTurnReminders(run-stages)
|
|
219
|
-
await injectTurnReminders(agent, { depth })
|
|
220
|
-
|
|
221
|
-
const messages = [{ role: "system", content: systemPrompt }, ...agent.history]
|
|
222
|
-
let response
|
|
223
|
-
|
|
224
|
-
// Auto-think: classify difficulty and set reasoning effort on turn 0; silent on failure.
|
|
225
|
-
if (agent.config?.agent?.autoThink && turn === 0) {
|
|
226
|
-
const { classifyAndApply } = await import("./auto-think.mjs")
|
|
227
|
-
await classifyAndApply(agent, turn).catch(() => {})
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
if (process.env.ADVISOR_DEBUG) console.error("[chat-call]", JSON.stringify({ turn, histLen: agent.history.length, lastRole: agent.history.at(-1)?.role }))
|
|
231
|
-
try { response = await chat(agent.provider, {
|
|
232
|
-
messages, tools: toolSchemas,
|
|
233
|
-
onToken: callbacks.onToken,
|
|
234
|
-
onReasoning: callbacks.onReasoning,
|
|
235
|
-
onWait: callbacks.onWait,
|
|
236
|
-
signal,
|
|
237
|
-
streamRules: agent.config.agent?.streamRules ?? [],
|
|
238
|
-
firedPatterns: streamRuleFired,
|
|
239
|
-
// LOGGING(LOGGING.md):llm:* 事件的语义上下文(stage=turn 主循环回合——含
|
|
240
|
-
// digest 消化轮 auto=true;child=子代理 id(spawn 时 stamp 于 child._logId))
|
|
241
|
-
// §18.6 D-TR4:轨迹元数据增补(role/depth/kind/session/cwd——trace-store 只读
|
|
242
|
-
// logCtx,签名不变);kind:depth>0 = subagent(consult 孩子 = consult)——子代理
|
|
243
|
-
// 对回靠 role+depth+child id(children 无 _sessionStart——不经 depth-0 设置——
|
|
244
|
-
// session 字段对子代理轨迹为 null——见 trace-store/agent.mjs 注释)。
|
|
245
|
-
logCtx: {
|
|
246
|
-
stage: "turn", turn: turn + 1, auto: autoTurn, child: agent._logId,
|
|
247
|
-
role: agent._role ?? null,
|
|
248
|
-
depth,
|
|
249
|
-
kind: depth > 0 ? (agent._role === "consult" ? "consult" : "subagent") : "turn",
|
|
250
|
-
session: agent._sessionStart ?? null,
|
|
251
|
-
cwd: agent.cwd,
|
|
252
|
-
traces: agent.config?.traces?.enabled !== false,
|
|
253
|
-
},
|
|
254
|
-
})
|
|
255
|
-
} catch (e) {
|
|
256
|
-
// User interrupt (Ctrl+I): controller.abort({ interrupt: true, message }).
|
|
257
|
-
// Inject into history; the outer loop recreates the controller and resumes.
|
|
258
|
-
if (e.name === "AbortError" && signal?.reason?.interrupt) {
|
|
259
|
-
const msg = `[User interrupt: ${signal.reason.message}]`
|
|
260
|
-
// Dedup: if already handled during tool execution (interrupt branch below),
|
|
261
|
-
// don't push a duplicate — the outer loop still recreates the controller.
|
|
262
|
-
if (agent.history.at(-1)?.content !== msg) {
|
|
263
|
-
agent.history.push({ role: "user", content: msg })
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
throw e
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// 内置工具(Responses web_search)结果本地化:服务端已执行——入历史为 tool 消息;
|
|
270
|
-
// 服务端 item id 是 msg_xxx 非 web_search_call_ 前缀——必须合成前缀(toItems 识别锚点),
|
|
271
|
-
// 原始 id 存入 content(真机冒烟 2026-08-31 验证)。
|
|
272
|
-
for (const btr of response.builtinToolResults ?? []) {
|
|
273
|
-
if (!btr?.id) continue
|
|
274
|
-
pushReal(agent, {
|
|
275
|
-
role: "tool",
|
|
276
|
-
tool_call_id: `web_search_call_${btr.id}`,
|
|
277
|
-
content: JSON.stringify({ id: btr.id, query: btr.query ?? "", sources: btr.sources ?? [], status: btr.status ?? "completed" }),
|
|
278
|
-
})
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Stream rule triggered mid-generation (action: "abort"): halt, inject the rule's
|
|
282
|
-
// message as a reminder, retry from the same context.
|
|
283
|
-
if (response.ruleTriggered) {
|
|
284
|
-
if (response.content) {
|
|
285
|
-
pushReal(agent, { role: "assistant", content: response.content })
|
|
286
|
-
}
|
|
287
|
-
const label = response.ruleName ? ` — stream rule "${response.ruleName}"` : ""
|
|
288
|
-
agent.history.push({
|
|
289
|
-
role: "user",
|
|
290
|
-
content: `[System reminder${label}: ${response.ruleMessage}]`,
|
|
291
|
-
})
|
|
292
|
-
continue
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Stream rule warnings / finish-reason 警告(2026-09-05 实践轮——提为
|
|
296
|
-
// injectResponseReminders,agent/run-stages.mjs——verbatim,语义零变)
|
|
297
|
-
injectResponseReminders(agent, response)
|
|
298
|
-
|
|
299
|
-
// User interrupted mid-generation (Ctrl+I): commit partial output + inject the
|
|
300
|
-
// message, then signal the outer loop to recreate the controller and resume.
|
|
301
|
-
if (response.interrupted) {
|
|
302
|
-
if (response.content) {
|
|
303
|
-
pushReal(agent, { role: "assistant", content: response.content })
|
|
304
|
-
}
|
|
305
|
-
agent.history.push({
|
|
306
|
-
role: "user",
|
|
307
|
-
content: `[User interrupt: ${response.interruptMessage}]`,
|
|
308
|
-
})
|
|
309
|
-
// §20.3 站点 #8(第 24 批):错误对象已自带 name/message——只补来源标注(缺 abortInfo 才补)
|
|
310
|
-
throw annotateAbort(Object.assign(new Error("User interrupted"), { name: "AbortError" }), signal, "agent", "interrupted-response")
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
if (response.usage) {
|
|
314
|
-
callbacks.onUsage?.(response.usage)
|
|
315
|
-
if (response.usage.prompt_tokens != null) {
|
|
316
|
-
agent._lastPromptTokens = response.usage.prompt_tokens
|
|
317
|
-
agent._usageAtLen = agent.history.length
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// Warn on abnormal finish reasons — the response may be incomplete/truncated.
|
|
322
|
-
// 2026-09-05 实践轮:finish-reason 注入随流规则警告提为 injectResponseReminders。
|
|
323
|
-
|
|
324
|
-
if (response.toolCalls.length === 0) {
|
|
325
|
-
const cr = handleCompletion(agent, response, depth, turn, guardPushbacks, honestReminderInjected, advisorPushbacks, callbacks)
|
|
326
|
-
guardPushbacks = cr.guardPushbacks
|
|
327
|
-
honestReminderInjected = cr.honestReminderInjected
|
|
328
|
-
advisorPushbacks = cr.advisorPushbacks
|
|
329
|
-
if (cr.action === "continue") continue
|
|
330
|
-
if (depth === 0) {
|
|
331
|
-
// End-of-run exploration distillation (CONTEXT-COMPACTION §5 + SEND-STALL-DISTILL
|
|
332
|
-
// §2.1): async — the promise hangs on _pendingDistill, settling at the next run's
|
|
333
|
-
// start or the TUI exit flush. Silent (N3): failure never blocks return/history.
|
|
334
|
-
// §18.6 D-TR4:depth 透传(distill 轨迹元数据——与 compress 同通道)
|
|
335
|
-
const distill = summarizeRunExplorations(agent, callbacks, signal, depth).catch(() => {})
|
|
336
|
-
agent._pendingDistill = distill
|
|
337
|
-
}
|
|
338
|
-
return cr.content
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// abort after chat completes, before committing history: don't commit a half-finished turn
|
|
342
|
-
if (signal?.aborted) throw abortError(signal, "agent", "post-chat")
|
|
343
|
-
|
|
344
|
-
pushReal(agent, {
|
|
345
|
-
role: "assistant",
|
|
346
|
-
content: response.content || null,
|
|
347
|
-
tool_calls: response.toolCalls.map((tc) => ({
|
|
348
|
-
id: tc.id, type: "function",
|
|
349
|
-
function: { name: tc.name, arguments: tc.arguments },
|
|
350
|
-
})),
|
|
351
|
-
...(response.reasoning && specForModel(agent.provider.model).reasoningEcho === "required"
|
|
352
|
-
? { reasoning_content: response.reasoning }
|
|
353
|
-
: {}),
|
|
354
|
-
})
|
|
355
|
-
|
|
356
|
-
const results = await executeToolCalls(agent, toolByName, response.toolCalls, callbacks, depth, signal)
|
|
357
|
-
|
|
358
|
-
// Ctrl+I interrupt during tool execution: skip committing partial results — inject
|
|
359
|
-
// the interrupt and retry (placeholder results keep strict providers pairable).
|
|
360
|
-
if (signal?.reason?.interrupt) {
|
|
361
|
-
// 中断变更记账(2026-08-31 评审 #4):此分支的工具已全部执行完成(磁盘已变,execute 已完成),
|
|
362
|
-
// 真实结果按语义不进历史(placeholder 替代)——但变更必须记账:否则 guard 看到
|
|
363
|
-
// "本轮未改代码" 放行,评审/verify 门禁被绕过(文件改了却没评审)。
|
|
364
|
-
// §29 fix A(2026-09-07):mutation-seq 记账已收敛到 dispatch runOne 执行成功即刻
|
|
365
|
-
// (唯一记账点——本分支不再 noteMutations——不双计——中断+同批 launch seq 单计
|
|
366
|
-
// 回归断言见 §29 T-A1i);此处仅剩 guard 标志 + touchedFiles 记账。
|
|
367
|
-
for (const { toolCall, ok } of results) {
|
|
368
|
-
const tool = toolByName.get(toolCall.name)
|
|
369
|
-
if (!ok || !tool || !FILE_MUTATORS.has(toolCall.name)) continue
|
|
370
|
-
agent._mutatedThisRun = true
|
|
371
|
-
agent._calledAdvisorThisRun = false
|
|
372
|
-
agent._verifiedThisRun = false
|
|
373
|
-
agent._verifyPassed = undefined
|
|
374
|
-
try {
|
|
375
|
-
const args = JSON.parse(toolCall.arguments)
|
|
376
|
-
const paths = tool.touchedPaths ? tool.touchedPaths(args) : [args.path]
|
|
377
|
-
for (const p of paths) {
|
|
378
|
-
const abs = resolve(agent.cwd, p)
|
|
379
|
-
if (!agent._touchedFiles.includes(abs)) agent._touchedFiles.push(abs)
|
|
380
|
-
}
|
|
381
|
-
} catch { /* 畸形 args 不影响记账(touchedFiles 尽力而为) */ }
|
|
382
|
-
}
|
|
383
|
-
// The assistant tool_calls were committed above — synthesize placeholder tool
|
|
384
|
-
// results BEFORE the interrupt message (strict providers 400 on dangling
|
|
385
|
-
// tool_calls; consult P1, 2026-08-30).
|
|
386
|
-
for (const tc of response.toolCalls) {
|
|
387
|
-
agent.history.push({ role: "tool", tool_call_id: tc.id, content: "[Tool execution interrupted — results discarded]" })
|
|
388
|
-
}
|
|
389
|
-
agent.history.push({
|
|
390
|
-
role: "user",
|
|
391
|
-
content: `[User interrupt: ${signal.reason.message}]`,
|
|
392
|
-
})
|
|
393
|
-
callbacks.onTurnEnd?.(agent, turn)
|
|
394
|
-
continue
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// Model is executing tools → real work: reset guard pushback counters
|
|
398
|
-
guardPushbacks = 0
|
|
399
|
-
advisorPushbacks = 0
|
|
400
|
-
|
|
401
|
-
// Commit tool results (pairing, multimodal deferral, mutation accounting, reindex)
|
|
402
|
-
await recordToolResults(agent, toolByName, results)
|
|
403
|
-
|
|
404
|
-
injectPostTurn(agent, results, recentCallSigs, callbacks, turn)
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
throw new ContinueError(maxTurns)
|
|
408
|
-
} catch (e) {
|
|
409
|
-
thrownError = e
|
|
410
|
-
throw e
|
|
411
|
-
} finally {
|
|
412
|
-
// 2026-09-05 实践轮:回合收尾(consult 清理/async 池分流/guard 继承——原 425-462
|
|
413
|
-
// 段 + collectSettledAsync 466-494 整体迁 agent/run-stages.mjs finalizeAgentTurn——
|
|
414
|
-
// CLI 对位 VS run-stages——finally 只剩一行调用 + 骨架注释)。
|
|
415
|
-
await finalizeAgentTurn(agent, { signal, autoTurn, suspDriven, thrownError, depth })
|
|
416
|
-
}
|
|
417
|
-
}
|
package/src/auto-think.mjs
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* auto-think.mjs — automatic difficulty classification for reasoning effort.
|
|
3
|
-
*
|
|
4
|
-
* When enabled, before each user-facing turn a cheap classification call determines
|
|
5
|
-
* the task difficulty, then maps it to the model's reasoning effort. This replaces
|
|
6
|
-
* manual /think toggling with per-prompt automatic selection.
|
|
7
|
-
*
|
|
8
|
-
* Config:
|
|
9
|
-
* { agent: { autoThink: true } }
|
|
10
|
-
*
|
|
11
|
-
* Mechanism:
|
|
12
|
-
* 1. Take the last user message from history
|
|
13
|
-
* 2. Send a minimal classification prompt (expects one-word reply)
|
|
14
|
-
* 3. Map difficulty → reasoningEffort using the model's valid effort enum
|
|
15
|
-
* 4. Set agent.provider.reasoningEffort before the real chat() call
|
|
16
|
-
*/
|
|
17
|
-
import { chat } from "./provider/core.mjs"
|
|
18
|
-
import { specForModel } from "./config.mjs"
|
|
19
|
-
|
|
20
|
-
const CLASSIFY_PROMPT = `Classify this coding task's difficulty: low, medium, or high.
|
|
21
|
-
|
|
22
|
-
- low — trivial: rename, typo, formatting, one-liner, direct question
|
|
23
|
-
- medium — localized: small feature, straightforward bug fix, moderate change
|
|
24
|
-
- high — complex: multi-file, debugging, design decisions, large refactor
|
|
25
|
-
|
|
26
|
-
Reply with exactly one word.`
|
|
27
|
-
|
|
28
|
-
const EFFORT_MAP = {
|
|
29
|
-
low: ["low", "minimal", "none", "low"],
|
|
30
|
-
medium: ["high", "medium", "high"],
|
|
31
|
-
high: ["max", "max", "xhigh", "max"],
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Build classifier input from history: the latest real user message (reminders and
|
|
36
|
-
* interrupt injections excluded), plus the previous user message as context when the
|
|
37
|
-
* latest is too short to classify on its own (e.g. "继续" / "还有几个问题").
|
|
38
|
-
* Exported for tests.
|
|
39
|
-
*/
|
|
40
|
-
export function buildClassifierInput(history) {
|
|
41
|
-
const isRealUser = (m) =>
|
|
42
|
-
m.role === "user" && typeof m.content === "string"
|
|
43
|
-
&& !m.content.startsWith("[System reminder:") && !m.content.startsWith("[User interrupt:")
|
|
44
|
-
const users = history.filter(isRealUser)
|
|
45
|
-
const last = users.at(-1)
|
|
46
|
-
if (!last) return null
|
|
47
|
-
let prompt = last.content
|
|
48
|
-
if (prompt.length < 200 && users.length > 1) {
|
|
49
|
-
prompt = `Previous request (context):\n${users.at(-2).content.slice(0, 1200)}\n\nLatest message:\n${prompt}`
|
|
50
|
-
}
|
|
51
|
-
return prompt.slice(0, 2000)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Classify the difficulty of the user's prompt and adjust reasoning effort.
|
|
56
|
-
* Only runs on the first turn (turn === 0) of a user message.
|
|
57
|
-
* Returns the resolved level or null if auto-thinking is disabled or classification fails.
|
|
58
|
-
* @param {object} agent
|
|
59
|
-
* @param {number} turn
|
|
60
|
-
* @returns {Promise<string|null>}
|
|
61
|
-
*/
|
|
62
|
-
export async function classifyAndApply(agent, turn) {
|
|
63
|
-
if (!agent.config?.agent?.autoThink) return null
|
|
64
|
-
if (turn !== 0) return null // Only classify on the first turn of user input
|
|
65
|
-
|
|
66
|
-
const spec = specForModel(agent.provider.model)
|
|
67
|
-
const validEfforts = spec.reasoningEffortEnum
|
|
68
|
-
if (!validEfforts) return null // Model doesn't support reasoning effort
|
|
69
|
-
|
|
70
|
-
const prompt = buildClassifierInput(agent.history)
|
|
71
|
-
if (prompt == null) return null
|
|
72
|
-
|
|
73
|
-
// Classification call: use same provider, minimal tokens, no tools, no streaming
|
|
74
|
-
let level
|
|
75
|
-
try {
|
|
76
|
-
const classifierProvider = { ...agent.provider, maxTokens: 10 }
|
|
77
|
-
const response = await chat(classifierProvider, {
|
|
78
|
-
messages: [
|
|
79
|
-
{ role: "system", content: CLASSIFY_PROMPT },
|
|
80
|
-
{ role: "user", content: prompt },
|
|
81
|
-
],
|
|
82
|
-
tools: [],
|
|
83
|
-
signal: AbortSignal.timeout(5_000),
|
|
84
|
-
// D-TS12 (AGENT-LOOP.md §18.7): full logCtx field set at the chat call
|
|
85
|
-
// point — traces/session/cwd/role/depth/kind (this call point carried
|
|
86
|
-
// only {stage,turn,child}). The traces field closes the D-TR6 "off = no
|
|
87
|
-
// persist" switch: without it the tracer treated the auto-think call as
|
|
88
|
-
// enabled and persisted even when agent.config.traces.enabled was false.
|
|
89
|
-
logCtx: {
|
|
90
|
-
stage: "autothink", turn, child: agent._logId,
|
|
91
|
-
traces: agent.config?.traces?.enabled !== false,
|
|
92
|
-
session: agent._sessionStart ?? null,
|
|
93
|
-
cwd: agent.cwd,
|
|
94
|
-
role: agent._role ?? null,
|
|
95
|
-
depth: agent._depth ?? 0, // agent state carries no depth stamp (the call site passes none) — 0 for the top-level agent
|
|
96
|
-
kind: "autothink",
|
|
97
|
-
},
|
|
98
|
-
})
|
|
99
|
-
const word = (response.content ?? "").trim().toLowerCase()
|
|
100
|
-
if (word.startsWith("low")) level = "low"
|
|
101
|
-
else if (word.startsWith("medium") || word.startsWith("med")) level = "medium"
|
|
102
|
-
else if (word.startsWith("high")) level = "high"
|
|
103
|
-
else return null // Unparseable
|
|
104
|
-
} catch {
|
|
105
|
-
return null // Classification failure → fall back to current setting
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Map difficulty to the closest valid reasoning effort
|
|
109
|
-
const candidates = EFFORT_MAP[level] || EFFORT_MAP.medium
|
|
110
|
-
const matched = candidates.find(e => validEfforts.includes(e))
|
|
111
|
-
if (!matched) return null
|
|
112
|
-
|
|
113
|
-
agent.provider.reasoningEffort = matched
|
|
114
|
-
return matched
|
|
115
|
-
}
|
package/src/config-migrate.mjs
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* config-migrate.mjs — model-schema legacy-shape migration core (MODEL-SELECTION v2, §16.2 M7).
|
|
3
|
-
* Split out of config.mjs (500-line hard limit). Pure function, zero imports — the
|
|
4
|
-
* write-back orchestration (writeConfigAtomic / persistRaw, failure tolerance) stays in
|
|
5
|
-
* the caller (CLI: config.mjs loadConfig; VSC: config-io.mjs loadRaw — same rule, 双端各自
|
|
6
|
-
* 独立实现——不做同步依赖).
|
|
7
|
-
*
|
|
8
|
-
* 目标形态 C:每渠道恰好一个默认模型(`providers[].model` 单值)+ 顶层 `defaultModel` 复合。
|
|
9
|
-
* 老形态迁移(v2):
|
|
10
|
-
* - A:`providers[].model` + `activeProvider/activeModel` → `p.model` **保留**(不再搬入 models);
|
|
11
|
-
* `activeProvider/activeModel` → `defaultModel` 复合(`activeModel` 优先;AP 不存在/AM 空 → 首渠道回退)
|
|
12
|
-
* - B:`providers[].models[]` → `p.model = defaultModel 属本渠道的模型段 ?? 现有 p.model(非空字符串)
|
|
13
|
-
* ?? models[] 首个非空字符串`,然后 `delete p.models`
|
|
14
|
-
* - 混合/垃圾:非数组 `p.models` / 非字符串(空串)`p.model` → 删除(清理)
|
|
15
|
-
* - 顺序:先构造/读取有效 `defaultModel` 值(含老 active* 转换),再按它给各渠道播种 `p.model`
|
|
16
|
-
* - 幂等:无老字段(无 `models` / 无 active* / 无垃圾值)→ 返回 false 不动;slot 旧字段
|
|
17
|
-
* (会话文件内)不迁移——读侧容忍
|
|
18
|
-
* - 空结果合法:渠道无模型来源 → `p.model` 不设(模型选择经 `/models` 拉取候选——M8/M9)
|
|
19
|
-
*/
|
|
20
|
-
export function migrateLegacyModelFields(raw) {
|
|
21
|
-
if (!raw || typeof raw !== "object") return false
|
|
22
|
-
const providers = Array.isArray(raw.providers) ? raw.providers : []
|
|
23
|
-
let changed = false
|
|
24
|
-
|
|
25
|
-
// ── ① 有效 defaultModel 值(含老 active* 转换)──
|
|
26
|
-
if (raw.activeProvider !== undefined || raw.activeModel !== undefined) {
|
|
27
|
-
const rawAP = typeof raw.activeProvider === "string" && raw.activeProvider.trim() ? raw.activeProvider : ""
|
|
28
|
-
const rawAM = typeof raw.activeModel === "string" && raw.activeModel.trim() ? raw.activeModel : ""
|
|
29
|
-
const ap = rawAP ? providers.find((p) => p?.name === rawAP) : null
|
|
30
|
-
if (ap && rawAM) {
|
|
31
|
-
if (raw.defaultModel == null) { raw.defaultModel = `${rawAP}:${rawAM}`; changed = true }
|
|
32
|
-
} else {
|
|
33
|
-
// AP 已不存在 / AM 无法归属 → 首渠道回退(该渠道有模型来源才可用——迁移产物必可用)
|
|
34
|
-
const first = providers.find((p) => seedModel(p) != null)
|
|
35
|
-
if (first && raw.defaultModel == null) { raw.defaultModel = `${first.name}:${seedModel(first)}`; changed = true }
|
|
36
|
-
}
|
|
37
|
-
delete raw.activeProvider
|
|
38
|
-
delete raw.activeModel
|
|
39
|
-
changed = true
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// ── ② 渠道播种单值 `p.model` + 清 models ──
|
|
43
|
-
const dm = typeof raw.defaultModel === "string" && raw.defaultModel.trim() ? raw.defaultModel : null
|
|
44
|
-
const dmSep = dm ? dm.indexOf(":") : -1
|
|
45
|
-
const dmProvider = dmSep > 0 ? dm.slice(0, dmSep) : null
|
|
46
|
-
const dmModel = dmSep > 0 && dmSep < dm.length - 1 ? dm.slice(dmSep + 1) : null
|
|
47
|
-
for (const p of providers) {
|
|
48
|
-
if (!p || typeof p !== "object") continue
|
|
49
|
-
if (Array.isArray(p.models)) {
|
|
50
|
-
const next = (p.name === dmProvider && dmModel)
|
|
51
|
-
? dmModel
|
|
52
|
-
: (typeof p.model === "string" && p.model.trim() ? p.model : seedModel(p))
|
|
53
|
-
if (next == null) delete p.model
|
|
54
|
-
else p.model = next
|
|
55
|
-
delete p.models
|
|
56
|
-
changed = true
|
|
57
|
-
continue
|
|
58
|
-
}
|
|
59
|
-
if (p.models !== undefined) { delete p.models; changed = true } // 非数组 models —— 垃圾清理
|
|
60
|
-
if (p.model !== undefined && !(typeof p.model === "string" && p.model.trim())) { delete p.model; changed = true }
|
|
61
|
-
}
|
|
62
|
-
return changed
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** 渠道级模型来源读数(迁移用):现有 `p.model`(非空字符串优先)或 `models[]` 首个非空字符串。 */
|
|
66
|
-
function seedModel(p) {
|
|
67
|
-
if (typeof p?.model === "string" && p.model.trim()) return p.model
|
|
68
|
-
const models = Array.isArray(p?.models) ? p.models : []
|
|
69
|
-
return models.find((m) => typeof m === "string" && m.trim()) ?? null
|
|
70
|
-
}
|