thincoder 0.12.62 → 0.12.64
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 +27 -0
- package/README.md +46 -49
- package/bin/thincoder.mjs +68 -34
- package/package.json +7 -6
- 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 +257 -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 +60 -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 +35 -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-plan.mjs +9 -0
- 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 +18 -10
- package/src/tui/interaction.mjs +16 -7
- package/src/tui/key-modes.mjs +9 -4
- package/src/tui/ledger-surface.mjs +22 -59
- 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 +15 -6
- 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 +57 -23
- package/src/tui/tool-events.mjs +11 -8
- package/src/tui/tui-lifecycle.mjs +9 -0
- package/src/tui/wizard.mjs +3 -3
- package/src/tui/wrapped-spawn.mjs +6 -2
- 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/cli/make-agent.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { execSync } from "node:child_process"
|
|
2
2
|
import { isAbsolute, join } from "node:path"
|
|
3
|
-
import { createAgent } from "
|
|
4
|
-
import { loadConfig, configDir } from "
|
|
5
|
-
import { createMemory,
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
import {
|
|
3
|
+
import { createAgent } from "@thincoder/core/agent.mjs"
|
|
4
|
+
import { loadConfig, configDir } from "@thincoder/core/config.mjs"
|
|
5
|
+
import { createMemory, syncDir } from "@thincoder/core/memory.mjs"
|
|
6
|
+
import { discoverRules } from "@thincoder/core/rules.mjs"
|
|
7
|
+
// #70(CORE-UNIFICATION TOOLS):注册表与消费侧拼装面归位核内——单源 `assembleBuiltinTools`
|
|
8
|
+
// (静态表 ∪ memory / code_search / doc_search / repo_outline / settings / peer_instances 六面 ∪ 门控 read_image)。
|
|
9
|
+
// 传 `model` 必需:漏传 ⇒ `read_image` 对所有模型静默消失(门判据 = specForModel(model)?.multimodal)。
|
|
10
|
+
import { assembleBuiltinTools } from "@thincoder/core/tools/index.mjs"
|
|
11
|
+
import { resolveEngineeringManifest, projectView } from "@thincoder/core/manifest.mjs"
|
|
12
12
|
|
|
13
13
|
/** 第 27 批 §12.3⑤(R-A1.1):装配期工具剔除——按 `name` 过滤的纯函数(机验锚)。
|
|
14
14
|
* 恒等语义:空列表 / 零命中 → 原数组原样返回(零意外剔除——T15)。
|
|
@@ -21,13 +21,13 @@ export function applyToolExclusions(tools, excludeTools = []) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
/** Assemble an agent with memory, MCP tools, and code/doc indices attached (sync all layers, then return) */
|
|
24
|
-
export async function assembleAgent({ excludeTools = [] } = {}) {
|
|
24
|
+
export async function assembleAgent({ excludeTools = [], slotData } = {}) {
|
|
25
25
|
const config = loadConfig()
|
|
26
26
|
const provider = config.provider
|
|
27
27
|
const providers = config.providersList
|
|
28
28
|
|
|
29
29
|
// Inject proxy URI into providers (double opt-in: provider.proxy + config.proxy.model)
|
|
30
|
-
const { injectProxy } = await import("
|
|
30
|
+
const { injectProxy } = await import("@thincoder/core/proxy.mjs")
|
|
31
31
|
injectProxy(providers, config)
|
|
32
32
|
// config.provider 是 loadConfig 里的独立拷贝,同步注入结果
|
|
33
33
|
if (provider?.name) provider.proxyUri = providers.find((p) => p.name === provider.name)?.proxyUri
|
|
@@ -35,7 +35,7 @@ export async function assembleAgent({ excludeTools = [] } = {}) {
|
|
|
35
35
|
const memory = createMemory({ dbPath: config.memory.dbPath })
|
|
36
36
|
// Vector retrieval: enabled if embedding is configured (lazy vector generation, computed on first search)
|
|
37
37
|
if (config.embedding?.apiKey) {
|
|
38
|
-
const { createEmbedder } = await import("
|
|
38
|
+
const { createEmbedder } = await import("@thincoder/core/embedding.mjs")
|
|
39
39
|
memory.embedder = createEmbedder(config.embedding)
|
|
40
40
|
}
|
|
41
41
|
const cwd = process.cwd()
|
|
@@ -57,11 +57,11 @@ export async function assembleAgent({ excludeTools = [] } = {}) {
|
|
|
57
57
|
// Team layer (optional): auto-clone on first use; startup only indexes local dir, remote pull via explicit thincoder sync
|
|
58
58
|
const team = teamConfig(config)
|
|
59
59
|
if (team) {
|
|
60
|
-
const { ensureClone } = await import("
|
|
60
|
+
const { ensureClone } = await import("@thincoder/core/git/gitmem.mjs")
|
|
61
61
|
await ensureClone(team)
|
|
62
62
|
await syncDir(memory, { layer: "team", dir: team.dir })
|
|
63
63
|
}
|
|
64
|
-
const baseTools =
|
|
64
|
+
const baseTools = await assembleBuiltinTools({ memory, cwd, projectDir: config.memory.projectDir, author: gitAuthor(), team, model: provider?.model ?? null })
|
|
65
65
|
|
|
66
66
|
// MCP servers: connect in parallel (a dead server won't block startup), collect failures as warnings (stderr invisible in TUI, passed via agent object)
|
|
67
67
|
const mcpServers = config.mcp?.servers ?? []
|
|
@@ -94,7 +94,7 @@ export async function assembleAgent({ excludeTools = [] } = {}) {
|
|
|
94
94
|
let mcpTools = []
|
|
95
95
|
const mcpWarnings = []
|
|
96
96
|
if (mcpServers.length) {
|
|
97
|
-
const { connectMcpServer } = await import("
|
|
97
|
+
const { connectMcpServer } = await import("@thincoder/core/mcp.mjs")
|
|
98
98
|
const results = await Promise.allSettled(mcpServers.map((srv) => connectMcpServer(srv)))
|
|
99
99
|
for (let i = 0; i < results.length; i++) {
|
|
100
100
|
const r = results[i]
|
|
@@ -122,7 +122,12 @@ export async function assembleAgent({ excludeTools = [] } = {}) {
|
|
|
122
122
|
agent.activeProvider = provider.name ?? ""
|
|
123
123
|
agent.activeModel = provider.model ?? null
|
|
124
124
|
agent._mcpWarnings = mcpWarnings
|
|
125
|
-
//
|
|
125
|
+
// M1 装配钩子(会话起点①——装配期):判据 = 会话权威值(`slotData` 槽值优先 + config 回退);
|
|
126
|
+
// 附着 agent.manifest 供下游 M2–M9 读面(普通会话 = null);非 ok 态不抛(启动零拒绝)。
|
|
127
|
+
// 钩子后移的可观察后果(报明 / 建档晚于 memory sync / MCP 连接 / 工具装配——功能等价,仍在
|
|
128
|
+
// 进入正常循环之前)见 docs/core/design/MANIFEST.md §2.2。
|
|
129
|
+
attachManifest(agent, { cwd, slotData })
|
|
130
|
+
// SESSION.md §6.8 D-S1:assembleAgent 后唯一校验点(TUI/chat 两路径同源)——不抛错不退出,
|
|
126
131
|
// 标记由调用侧消费(TUI 弹重选 / headless 报错)。空 provider 由 TUI 路径在 startTUI 前清空。
|
|
127
132
|
validateProvider(agent)
|
|
128
133
|
// defaultModel 无效/未设的具体原因覆盖通用判据文案(providers 存在时更有指导性)
|
|
@@ -133,7 +138,45 @@ export async function assembleAgent({ excludeTools = [] } = {}) {
|
|
|
133
138
|
}
|
|
134
139
|
|
|
135
140
|
/**
|
|
136
|
-
*
|
|
141
|
+
* M1 装配钩子(docs/core/design/MANIFEST.md §2.2 两端装配钩子——CLI 端):
|
|
142
|
+
* 会话起点调用(装配期 ① + `bin` 启动恢复后重估 ②——幂等:重估即重读,无状态位)。
|
|
143
|
+
*
|
|
144
|
+
* 模式门(判据单源 = 会话权威值,槽优先 + config 回退——KD-M1-12 / KD-M1-13):
|
|
145
|
+
* 「恢复槽带 `engineering` 字段 ? 槽值 : `agent.config.agent.engineering`」(槽字段在场判据
|
|
146
|
+
* 与 `applySession` 同款——`thincoder-core/session.mjs:314-317`)。普通会话 → **装配钩子
|
|
147
|
+
* 零 manifest I/O**(不读 / 不拒 / 不建档)+ `agent.manifest = null`(清残留附着——复用
|
|
148
|
+
* agent 跨模式防陈旧)。
|
|
149
|
+
*
|
|
150
|
+
* 工程模式分支 = 核单源决策树 `resolveEngineeringManifest`(KD-M1-20);**非 fatal**
|
|
151
|
+
* (KD-M1-25 / M1-29——**启动零拒绝**):档合法 / 梯②④⑤ 缺档(轻动作就地建档,`writer:'main'`)
|
|
152
|
+
* ⇒ 附着;歧义(≥2 候选)/ 档非法 / 建档失败 ⇒ **不抛**——`agent.manifest = null` + 记
|
|
153
|
+
* `agent._projectView`(形状 = `projectView` 返回面 + `created` 建档标记——§2.2 钩子段)。
|
|
154
|
+
* 需要项目参数者各自报明(情境行 §2.6 / 动作侧各自报错——§2.9 C)。
|
|
155
|
+
*
|
|
156
|
+
* `slotData` = 恢复槽记录,由调用方经**参数注入**(`bin` TUI 分支既有 `resumeSlot` 调用
|
|
157
|
+
* 前移——KD-M1-15:钩子内不调 `resumeSlot`(该函数非纯读:GC / 认领 / 端标写);`chat` /
|
|
158
|
+
* ACP 装配无会话 ⇒ 不传 ⇒ config 回退)。重估点不传(`applySession` 已按槽订正 config)。
|
|
159
|
+
* @param {object} agent — createAgent 产物(读 agent.config;写 agent.manifest / agent._projectView)
|
|
160
|
+
* @param {{cwd?: string, slotData?: object|null}} [opts]
|
|
161
|
+
* @returns {object|null} 附着后的 manifest / 非 ok 态与普通会话 null
|
|
162
|
+
*/
|
|
163
|
+
export function attachManifest(agent, { cwd = process.cwd(), slotData } = {}) {
|
|
164
|
+
const slotEng = slotData?.engineering
|
|
165
|
+
const engineering = slotEng !== undefined ? slotEng === true : agent.config?.agent?.engineering === true
|
|
166
|
+
if (!engineering) {
|
|
167
|
+
agent.manifest = null // 清残留附着(KD-M1-12)——普通会话不读 / 不拒 / 不建档
|
|
168
|
+
return null
|
|
169
|
+
}
|
|
170
|
+
// 决策树(writer 显式 'main'——写门 fail-closed 缺省拒,KD-M1-3;漏传 ⇒ 缺档格退化
|
|
171
|
+
// `init-failed`)。任何非 ok 态都不抛(KD-M1-25)。
|
|
172
|
+
const r = resolveEngineeringManifest(cwd, { writer: "main" })
|
|
173
|
+
agent._projectView = { ...projectView(cwd), created: r.ok && r.created === true }
|
|
174
|
+
agent.manifest = r.ok ? r.manifest : null
|
|
175
|
+
return agent.manifest
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* SESSION.md §6.8 D-S1 — provider 有效性校验(assembleAgent 后唯一校验点,TUI/chat 两路径同源)。
|
|
137
180
|
* 判据(评审 #1/#2):仅 model/baseURL 缺失判 invalid——**不得用 MODEL_SPECS 成员资格判无效**
|
|
138
181
|
* (未知模型 = 受支持场景:自定义端点模型不在 spec 表是常态,误判会让自定义模型用户每次恢复都弹重选)。
|
|
139
182
|
* apiKey 缺失不判(既有 wizard /model 流程处理)。幂等:有效时清标记,无效时置标记 + 原因。
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isAbsolute, join } from "node:path"
|
|
2
|
-
import { loadConfig } from "
|
|
2
|
+
import { loadConfig } from "@thincoder/core/config.mjs"
|
|
3
3
|
import { teamConfig } from "./make-agent.mjs"
|
|
4
|
-
import { put, search, list } from "
|
|
5
|
-
import { deleteByUid } from "
|
|
4
|
+
import { put, search, list } from "@thincoder/core/memory/core.mjs"
|
|
5
|
+
import { deleteByUid } from "@thincoder/core/memory/delete.mjs"
|
|
6
6
|
|
|
7
7
|
/** thincoder memory <list|search|put|remove> subcommands.
|
|
8
8
|
* opts.dirs: { project, team } layer directories for project/team file deletion (tests inject their own);
|
package/src/cli/permission.mjs
CHANGED
|
@@ -1,48 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
const s = JSON.stringify(toolArgs)
|
|
6
|
-
return s.length > 120 ? s.slice(0, 120) + "..." : s
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/** Permission request key info (per-tool customized), aligned with TUI formatPermission. name may include sub-agent prefix ("coder/bash") — extract basename for matching */
|
|
10
|
-
export function formatPermission(name, args) {
|
|
11
|
-
const cap = (s, n = 1000) => (s.length > n ? `${s.slice(0, n)}…(共 ${s.length} 字符)` : s)
|
|
12
|
-
const base = name.includes("/") ? name.split("/").pop() : name
|
|
13
|
-
if (base === "bash") return cap(args.command ?? "")
|
|
14
|
-
if (base === "write") return `${args.path}(写入 ${(args.content ?? "").length} 字符)\n${cap(args.content ?? "", 1000)}`
|
|
15
|
-
if (base === "edit") {
|
|
16
|
-
const oldLines = cap(args.old_string ?? "", 500).split("\n").map((l) => `- ${l}`).join("\n")
|
|
17
|
-
const newLines = cap(args.new_string ?? "", 500).split("\n").map((l) => `+ ${l}`).join("\n")
|
|
18
|
-
return `${args.path}\n${oldLines}\n ↓\n${newLines}`
|
|
19
|
-
}
|
|
20
|
-
if (base === "delete") return `${args.path}${args.force ? "(force:跟踪文件也删)" : ""}`
|
|
21
|
-
if (base === "subagent") return cap(args.task ?? "", 500)
|
|
22
|
-
if (base === "memory") {
|
|
23
|
-
// §6 action-routed preview: put shows content, batch delete/clear show the gate args
|
|
24
|
-
const action = String(args.action ?? "")
|
|
25
|
-
if (action === "put") return `[${args.type ?? ""}] ${args.title ?? ""}\n${cap(args.content ?? "", 500)}`
|
|
26
|
-
if (action === "delete") return args.id ? `id=${args.id}${args.layer ? ` layer=${args.layer}` : ""}` : `batch delete layer=${args.layer ?? ""} type=${args.type ?? ""} keyword=${args.keyword ?? ""} confirm=${args.confirm}`
|
|
27
|
-
if (action === "clear") return `clear layer=${args.layer ?? ""} confirm=${args.confirm}`
|
|
28
|
-
return cap(summarize(args), 300)
|
|
29
|
-
}
|
|
30
|
-
return cap(summarize(args), 300)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Permission confirmation: interactive y/n on TTY; non-interactive defaults to deny (safety-first) */
|
|
34
|
-
export async function askPermission(name, toolArgs) {
|
|
35
|
-
if (!process.stdin.isTTY) {
|
|
36
|
-
console.error(`\n[deny] ${name} (non-interactive, side-effect tools require a TTY)`)
|
|
37
|
-
return false
|
|
38
|
-
}
|
|
39
|
-
const rl = createInterface({ input: process.stdin, output: process.stderr })
|
|
40
|
-
try {
|
|
41
|
-
const answer = await new Promise((resolve) => {
|
|
42
|
-
rl.question(`\n[allow?] ${name}\n${formatPermission(name, toolArgs)}\n(y/N) `, resolve)
|
|
43
|
-
})
|
|
44
|
-
return answer.trim().toLowerCase() === "y"
|
|
45
|
-
} finally {
|
|
46
|
-
rl.close()
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
/** permission.mjs — CLI 转口档(P2 机制层端差批 §2.20):闸语义 + 每工具请示文案单源 = 核
|
|
2
|
+
* `permission.mjs`(#165 落点——本端零第二实现);展示面由核的 `io.ask` 缝承载。
|
|
3
|
+
* 导出面与核逐字对齐,保既有 import 面(`bin/thincoder.mjs` · `distill-command.mjs`)。 */
|
|
4
|
+
export { summarize, formatPermission, askPermission } from "@thincoder/core/permission.mjs"
|
package/src/cli/setup-wizard.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createInterface } from "node:readline"
|
|
2
|
-
import { configPath, writeConfigAtomic, PROVIDER_PRESETS } from "
|
|
2
|
+
import { configPath, writeConfigAtomic, PROVIDER_PRESETS } from "@thincoder/core/config.mjs"
|
|
3
3
|
import { probeChannelModels } from "../tui/model-catalog.mjs"
|
|
4
4
|
|
|
5
5
|
/** First-time setup (TTY chat / distill): ask a few questions to configure a provider, save to disk, return runtime provider. Cancel returns null. */
|
package/src/completions.mjs
CHANGED
|
@@ -21,7 +21,7 @@ export function printCompletion(shell) {
|
|
|
21
21
|
distill) COMPREPLY=( \\$(compgen -W "--yes --layer=" -- "\\$cur") ) ;;
|
|
22
22
|
completion) COMPREPLY=( \\$(compgen -W "bash zsh fish" -- "\\$cur") ) ;;
|
|
23
23
|
*)
|
|
24
|
-
COMPREPLY=( \\$(compgen -W "chat acp memory sync reindex distill upgrade completion session -v --version -h --help" -- "\\$cur") ) ;;
|
|
24
|
+
COMPREPLY=( \\$(compgen -W "tui chat acp memory sync reindex distill upgrade completion session -v --version -h --help" -- "\\$cur") ) ;;
|
|
25
25
|
esac
|
|
26
26
|
}
|
|
27
27
|
complete -F _thincoder thincoder
|
|
@@ -39,6 +39,7 @@ _thincoder() {
|
|
|
39
39
|
case "\\$state" in
|
|
40
40
|
cmd)
|
|
41
41
|
_values 'command' \\
|
|
42
|
+
'tui[Launch the interactive TUI]' \\
|
|
42
43
|
'chat[One-shot agent run with tools]' \\
|
|
43
44
|
'acp[Agent Client Protocol server for IDEs]' \\
|
|
44
45
|
'memory[Manage long-term memory]' \\
|
|
@@ -69,6 +70,7 @@ _thincoder
|
|
|
69
70
|
complete -c thincoder -f
|
|
70
71
|
|
|
71
72
|
# Subcommands
|
|
73
|
+
complete -c thincoder -a tui -d 'Launch the interactive TUI'
|
|
72
74
|
complete -c thincoder -a chat -d 'One-shot agent run with tools'
|
|
73
75
|
complete -c thincoder -a memory -d 'Manage long-term memory'
|
|
74
76
|
complete -c thincoder -a sync -d 'Sync team memory repo'
|
package/src/crash-reports.mjs
CHANGED
|
@@ -27,7 +27,7 @@ import { chmodSync, existsSync, mkdirSync, readdirSync, statSync, unlinkSync, wr
|
|
|
27
27
|
import { join } from "node:path"
|
|
28
28
|
// F3① 命名空间 import:API 缺失(旧 Node)降级为调用期异常并被武装 try 吞掉——不做 import 期硬失败
|
|
29
29
|
import * as v8 from "node:v8"
|
|
30
|
-
import { configDir } from "
|
|
30
|
+
import { configDir } from "@thincoder/core/config.mjs"
|
|
31
31
|
|
|
32
32
|
/** F3② 关值集合(trim + 大小写不敏感);其余取值(未设 / 空串 / 未知串)默认开——fail-open 向取证。 */
|
|
33
33
|
const HEAP_SNAPSHOT_OFF_VALUES = new Set(["0", "false", "off", "no"])
|
package/src/distill.mjs
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* Absolutely no automatic storage at session end.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { chat } from "
|
|
8
|
-
import { loadConfig } from "
|
|
9
|
-
import { put, putMarkdown } from "
|
|
10
|
-
import { commitAndPush } from "
|
|
7
|
+
import { chat } from "@thincoder/core/provider/index.mjs"
|
|
8
|
+
import { loadConfig } from "@thincoder/core/config.mjs"
|
|
9
|
+
import { put, putMarkdown } from "@thincoder/core/memory.mjs"
|
|
10
|
+
import { commitAndPush } from "@thincoder/core/git/gitmem.mjs"
|
|
11
11
|
|
|
12
12
|
const DISTILL_PROMPT = `You are a knowledge extractor. Read the following agent work session log and extract knowledge worth remembering across sessions.
|
|
13
13
|
|
package/src/heap-watch.mjs
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* 直驱(零等待);失败面全吞(采样抛错不阻断——样本失败静默跳过本次)。
|
|
17
17
|
*/
|
|
18
18
|
import { getHeapStatistics } from "node:v8"
|
|
19
|
-
import { logEvent } from "
|
|
19
|
+
import { logEvent } from "@thincoder/core/log.mjs"
|
|
20
20
|
|
|
21
21
|
/** 关值集合(单点判定——§3.2 同约定复用)。 */
|
|
22
22
|
export const HEAP_WATCH_OFF = new Set(["0", "false", "off", "no"])
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* prompt-injections.mjs — the CLI end's prompt-anchor value table.
|
|
3
|
+
*
|
|
4
|
+
* The core's single resolution face (`@thincoder/core/prompt-files.mjs`) owns the anchors
|
|
5
|
+
* (`{{inject:<name>}}` in `thincoder-core/prompts/` + `thincoder-core/tool-docs/`); this end
|
|
6
|
+
* supplies the values. The table is registered ONCE at the process entry (`bin/thincoder.mjs`)
|
|
7
|
+
* — before any assembly — via `configurePromptInjections`.
|
|
8
|
+
*
|
|
9
|
+
* 2026-09-17 消端差:提示词面(persona / discipline / common)注入锚全消——导航注记删
|
|
10
|
+
* (正文自足)、端特有段通用化并入核内正文、文档地图路径统一写死 `docs/README.md`。
|
|
11
|
+
* 剩 = 工具面 2 锚(bash-terminal-face / question-ui-face——真实工具差异,待工具面 review 处置)。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** 工具面 2 锚(锚名 → 本端取值)。 */
|
|
15
|
+
export const CLI_PROMPT_INJECTIONS = {
|
|
16
|
+
// bash 宿主终端面——CLI 无 terminal 参数(工具面 review 再统一)
|
|
17
|
+
"bash-terminal-face": "",
|
|
18
|
+
// question 工具可用性行(核内正文该行已移出正文、锚原位替换——本端填回 CLI 措辞)
|
|
19
|
+
"question-ui-face": "- Availability: this tool needs an interactive UI — in contexts without one (headless runs, subagent children) it returns an error instead of asking; put the question in your reply text instead.",
|
|
20
|
+
}
|
package/src/tui/agent-turn.mjs
CHANGED
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
* 递归回本文件(函数级静态环——模块求值期无顶层调用,环安全——session-slots ↔
|
|
15
15
|
* session.mjs 同款先例)。
|
|
16
16
|
*/
|
|
17
|
-
import { runAgent, ContinueError } from "
|
|
18
|
-
import { saveSession } from "
|
|
17
|
+
import { runAgent, ContinueError } from "@thincoder/core/agent.mjs"
|
|
18
|
+
import { saveSession } from "@thincoder/core/session.mjs"
|
|
19
19
|
import { ansi, C } from "./ansi.mjs"
|
|
20
20
|
import { buildToolCallbacks, sweepToolBlocks } from "./tool-events.mjs"
|
|
21
21
|
import { freezeAllSubTasks } from "./subagent-blocks.mjs" // freezeReclaimDigestedBlocks 随 §17 段迁 suspension-drive.mjs
|
|
22
|
-
import { ensureSessionTitle } from "
|
|
23
|
-
import { logEvent, errText } from "
|
|
22
|
+
import { ensureSessionTitle } from "@thincoder/core/generate-title.mjs"
|
|
23
|
+
import { logEvent, errText } from "@thincoder/core/log.mjs"
|
|
24
24
|
import { suspensionSession, poolLive } from "./suspension-drive.mjs"
|
|
25
25
|
|
|
26
26
|
/** Exit-flush bound for the async end-of-run distillation (SEND-STALL-DISTILL §2.5):
|
|
@@ -28,6 +28,10 @@ import { suspensionSession, poolLive } from "./suspension-drive.mjs"
|
|
|
28
28
|
* never let shutdown hang on the background summary call. */
|
|
29
29
|
const DISTILL_FLUSH_TIMEOUT_MS = 5000
|
|
30
30
|
|
|
31
|
+
/** Provider 失败面 URL 脱敏(X8——判据与 VSC `panel-turn-loop.mjs:153` 同式;两端各自持有
|
|
32
|
+
* 字面 = 双写登记——单源化须动 VSC/核写域,出本批射程,见批档 §2.3)。 */
|
|
33
|
+
const PROVIDER_URL_RE = /https?:\/\/[^\s,)"]+/g
|
|
34
|
+
|
|
31
35
|
/**
|
|
32
36
|
* 第 33 批(TUI §14.3(d)——纯函数,供测试直驱):回合链尾「需要用户」谓词——agent 已停
|
|
33
37
|
* 且无自动续跑 ⇒ 置 attention 位。排除项语义(F13):`skipSession`(digest / 会话内回合
|
|
@@ -45,6 +49,8 @@ export function userNeededAtTurnEnd(state, agent, skipSession) {
|
|
|
45
49
|
* user/auto;result ok/stopped/error)。内层经 _logOutcome 载具回传终止原因(中止/
|
|
46
50
|
* turn-cap 拒绝/错误 vs 正常完成)——嵌套回合(队列递归/挂起会话内 digest 轮)各自
|
|
47
51
|
* 新开载具(每次包装调用独立),互不串扰。err:internal = 逃出内层的未分类异常。
|
|
52
|
+
* @returns {Promise<"ok"|"stopped"|"error">} 本回合终态(同一载具外化——X9 消化轮
|
|
53
|
+
* 收尾可见行的 ok/aborted 判据;VSC digest end ok 旗标同口径)。
|
|
48
54
|
*/
|
|
49
55
|
export async function runAgentTurn(ctx, text, opts = {}) {
|
|
50
56
|
const kind = opts?.autoTurn ? "auto" : "user"
|
|
@@ -55,8 +61,8 @@ export async function runAgentTurn(ctx, text, opts = {}) {
|
|
|
55
61
|
const t0 = Date.now()
|
|
56
62
|
logEvent("turn:start", { kind })
|
|
57
63
|
try {
|
|
58
|
-
|
|
59
|
-
return result
|
|
64
|
+
await runAgentTurnInner(ctx, text, runOpts)
|
|
65
|
+
return carrier.result ?? "ok"
|
|
60
66
|
} catch (e) {
|
|
61
67
|
carrier.result = "error"
|
|
62
68
|
logEvent("err:internal", { msg: errText(e, 200), where: "runAgentTurn" })
|
|
@@ -68,7 +74,7 @@ export async function runAgentTurn(ctx, text, opts = {}) {
|
|
|
68
74
|
|
|
69
75
|
/** runAgentTurn 本体(LOGGING 包装之外——见上方包装器)。 */
|
|
70
76
|
async function runAgentTurnInner(ctx, text, opts) {
|
|
71
|
-
const { autoTurn = false, skipSession = false } = opts ?? {}
|
|
77
|
+
const { autoTurn = false, skipSession = false, upstreamTurn = false } = opts ?? {} // upstreamTurn:上行 ask 唤醒轮旗标(§6.27.12.5 D/I——透传核 runAgent,仅供域文本选择)
|
|
72
78
|
const { agent, state, pushLine, pushLabel, render, scheduleRender, ensureAssistantLabel, askPermission, askBatchPermission, askQuestion, handleSlash } = ctx
|
|
73
79
|
// 可注入覆盖(测试用);默认走真实实现
|
|
74
80
|
const runAgentImpl = ctx.runAgent ?? runAgent
|
|
@@ -144,7 +150,7 @@ async function runAgentTurnInner(ctx, text, opts) {
|
|
|
144
150
|
try {
|
|
145
151
|
for (let resume = false; ; resume = true) {
|
|
146
152
|
try {
|
|
147
|
-
await runAgentImpl(agent, text, callbacks, { signal: state.controller.signal, resume, autoTurn, suspDriven: true })
|
|
153
|
+
await runAgentImpl(agent, text, callbacks, { signal: state.controller.signal, resume, autoTurn, upstreamTurn, suspDriven: true })
|
|
148
154
|
flushStream()
|
|
149
155
|
break // Normal completion, exit loop
|
|
150
156
|
} catch (error) {
|
|
@@ -216,7 +222,32 @@ async function runAgentTurnInner(ctx, text, opts) {
|
|
|
216
222
|
state.controller = makeController()
|
|
217
223
|
continue
|
|
218
224
|
}
|
|
219
|
-
|
|
225
|
+
// X8(2026-09-20 端差·显示面消差批):provider 失败面 = 友好首行(判据同 VSC
|
|
226
|
+
// panel-turn-loop.mjs:152-153:原文首行 + URL 脱敏)+ 诊断两行(Provider/Model)+ Retry
|
|
227
|
+
// 询问(复用 permission 机制——同意 ⇒ 重建 controller + resume 重入,与 ContinueError
|
|
228
|
+
// 同法;拒绝 ⇒ 现状 break,[error] 行保留)。
|
|
229
|
+
const rawMsg = error?.message || String(error)
|
|
230
|
+
const errLine = rawMsg.split("\n")[0].replace(PROVIDER_URL_RE, "[endpoint]")
|
|
231
|
+
// X8 跟进②(用户 2026-09-20 裁定):原文余行 = **log-only**(表面零膨胀——不增 UI 行)。
|
|
232
|
+
// `head` 与 `err` **同过脱敏管道**(PROVIDER_URL_RE ⇒ `[endpoint]`——LOGGING.md §6.3
|
|
233
|
+
// D-LG4「URL 不入事件」)**先脱敏后截断**(防截断留 URL 残段),自限 ≤200(`err` 200 +
|
|
234
|
+
// `head` 200 + 固定字段 < 512——log.mjs NF-L2);无余行 ⇒ 传 `null`(勿传空串)。
|
|
235
|
+
const restRaw = rawMsg.split("\n").slice(1).map((l) => l.trim()).filter(Boolean).join(" / ")
|
|
236
|
+
const restSan = restRaw.replace(PROVIDER_URL_RE, "[endpoint]")
|
|
237
|
+
logEvent("err:provider", { err: errLine, head: restSan ? (restSan.length > 200 ? restSan.slice(0, 199) + "…" : restSan) : null })
|
|
238
|
+
pushLine(`[error] ${errLine}`, C.error)
|
|
239
|
+
pushLine(`→ Provider: ${agent.provider?.baseURL}`, C.dim)
|
|
240
|
+
pushLine(`→ Model: ${agent.provider?.model}`, C.dim)
|
|
241
|
+
const willRetry = await new Promise((resolve) => {
|
|
242
|
+
state.permission = { name: "retry", args: { message: errLine }, resolve }
|
|
243
|
+
state.status = "Retry after provider error?"
|
|
244
|
+
render()
|
|
245
|
+
})
|
|
246
|
+
state.permission = null
|
|
247
|
+
if (willRetry) {
|
|
248
|
+
state.controller = makeController()
|
|
249
|
+
continue
|
|
250
|
+
}
|
|
220
251
|
if (opts?._logOutcome) opts._logOutcome.result = "error"
|
|
221
252
|
break
|
|
222
253
|
}
|
package/src/tui/cmd-advisor.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* ctx: { agent, showPicker, pushLine, pushLabel, persistRaw } */
|
|
4
4
|
import { readFileSync } from "node:fs"
|
|
5
5
|
import { ansi, C } from "./ansi.mjs"
|
|
6
|
-
import { activeSlot, slotPath } from "
|
|
6
|
+
import { activeSlot, slotPath } from "@thincoder/core/session.mjs"
|
|
7
7
|
import { writeSessionFile } from "./cmd-eng.mjs"
|
|
8
8
|
|
|
9
9
|
export async function handleAdvisorCommand(ctx) {
|
|
@@ -98,7 +98,7 @@ export async function handleAdvisorCommand(ctx) {
|
|
|
98
98
|
pushLabel("❯ Advisor", ansi.bold + C.tool)
|
|
99
99
|
pushLine("Thinking: using main model settings", C.tool)
|
|
100
100
|
} else if (c.action === "think_on") {
|
|
101
|
-
const { specForModel } = await import("
|
|
101
|
+
const { specForModel } = await import("@thincoder/core/config.mjs")
|
|
102
102
|
const spec = specForModel(getEffectiveModel(agent, cfg))
|
|
103
103
|
cfg.thinking = { type: spec.thinkEnabledValue ?? "enabled" }
|
|
104
104
|
if (spec.thinkApi === "effort") delete cfg.thinking
|
|
@@ -106,7 +106,7 @@ export async function handleAdvisorCommand(ctx) {
|
|
|
106
106
|
pushLabel("❯ Advisor", ansi.bold + C.tool)
|
|
107
107
|
pushLine(`Thinking: ON`, C.tool)
|
|
108
108
|
} else if (c.action === "think_off") {
|
|
109
|
-
const { specForModel } = await import("
|
|
109
|
+
const { specForModel } = await import("@thincoder/core/config.mjs")
|
|
110
110
|
const spec = specForModel(getEffectiveModel(agent, cfg))
|
|
111
111
|
const isCustomThink = (spec.thinkEnabledValue ?? "enabled") !== "enabled"
|
|
112
112
|
cfg.thinking = isCustomThink ? null : { type: "disabled" }
|
|
@@ -179,7 +179,7 @@ function getEffectiveModel(agent, cfg) {
|
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
async function fetchAdvisorModels(agent) {
|
|
182
|
-
const { listModels } = await import("
|
|
182
|
+
const { listModels } = await import("@thincoder/core/provider/index.mjs")
|
|
183
183
|
const result = new Map()
|
|
184
184
|
await Promise.all((agent.providers || []).map(async (p) => {
|
|
185
185
|
try {
|
|
@@ -223,7 +223,7 @@ function buildModelEntries(agent, cfg, cache) {
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
async function buildThinkingEntries(agent, cfg) {
|
|
226
|
-
const { specForModel } = await import("
|
|
226
|
+
const { specForModel } = await import("@thincoder/core/config.mjs")
|
|
227
227
|
const providerForDefaults = cfg.provider
|
|
228
228
|
? agent.providers?.find(p => p.name === cfg.provider) || agent.provider
|
|
229
229
|
: agent.provider
|
package/src/tui/cmd-config.mjs
CHANGED
|
@@ -23,7 +23,7 @@ export function poolCur(pl, defaults) {
|
|
|
23
23
|
/** /config command: view and set agent/embedding/proxy config. */
|
|
24
24
|
export async function handleConfigCommand(ctx, args = []) {
|
|
25
25
|
const { agent, pushLine, pushLabel, showPicker, askQuestion, persistRaw, maskKey, pickModelForSlot } = ctx
|
|
26
|
-
const { configPath, DEFAULTS } = await import("
|
|
26
|
+
const { configPath, DEFAULTS } = await import("@thincoder/core/config.mjs")
|
|
27
27
|
let ac = agent.config?.agent ?? {}
|
|
28
28
|
let ec = agent.config?.embedding ?? {}
|
|
29
29
|
let tc = agent.config?.traces ?? {}
|
|
@@ -43,7 +43,7 @@ export async function handleConfigCommand(ctx, args = []) {
|
|
|
43
43
|
agent.config.embedding ??= {}
|
|
44
44
|
agent.config.embedding.apiKey = embKey
|
|
45
45
|
if (agent.memory) {
|
|
46
|
-
const { createEmbedder } = await import("
|
|
46
|
+
const { createEmbedder } = await import("@thincoder/core/embedding.mjs")
|
|
47
47
|
agent.memory.embedder = createEmbedder(agent.config.embedding)
|
|
48
48
|
}
|
|
49
49
|
pushLabel("❯ Config", ansi.bold + C.tool)
|
|
@@ -55,8 +55,8 @@ export async function handleConfigCommand(ctx, args = []) {
|
|
|
55
55
|
* MODEL-MERGE-SESSION 三支改写(cfg 无 active*):运行时由「会话槽复合仍在 providers 中 →
|
|
56
56
|
* 保持槽值」或「config defaultModel(新会话起点)」决定——不再有渠道默认字段可回退。 */
|
|
57
57
|
async function reloadConfig() {
|
|
58
|
-
const { loadConfig } = await import("
|
|
59
|
-
const { injectProxy } = await import("
|
|
58
|
+
const { loadConfig } = await import("@thincoder/core/config.mjs")
|
|
59
|
+
const { injectProxy } = await import("@thincoder/core/proxy.mjs")
|
|
60
60
|
const cfg = loadConfig()
|
|
61
61
|
injectProxy(cfg.providersList, cfg)
|
|
62
62
|
const sessionName = agent.activeProvider
|
|
@@ -82,7 +82,7 @@ export async function handleConfigCommand(ctx, args = []) {
|
|
|
82
82
|
// 运行时重建后同步注入结果(cfg.provider 系 loadConfig 内建——injectProxy 后建)
|
|
83
83
|
agent.provider.proxyUri = cfg.providersList.find((p) => p.name === agent.activeProvider)?.proxyUri
|
|
84
84
|
if (agent.config?.agent?.compactThresholdAuto) {
|
|
85
|
-
const { resolveCompactThreshold } = await import("
|
|
85
|
+
const { resolveCompactThreshold } = await import("@thincoder/core/config.mjs")
|
|
86
86
|
agent.config.agent.compactThreshold = resolveCompactThreshold(null, agent.provider).value
|
|
87
87
|
}
|
|
88
88
|
}
|
|
@@ -136,8 +136,8 @@ export async function handleConfigCommand(ctx, args = []) {
|
|
|
136
136
|
pushLabel("❯ Config", ansi.bold + C.tool)
|
|
137
137
|
pushLine(`proxy.${key} = ${!pc[key] ? "on" : "off"}`, C.tool)
|
|
138
138
|
} else if (c.action === "test") {
|
|
139
|
-
const { proxyFetch, resolveWebProxy } = await import("
|
|
140
|
-
const { UA } = await import("
|
|
139
|
+
const { proxyFetch, resolveWebProxy } = await import("@thincoder/core/proxy.mjs")
|
|
140
|
+
const { UA } = await import("@thincoder/core/tools/web.mjs")
|
|
141
141
|
const uri = resolveWebProxy({ agent })
|
|
142
142
|
pushLabel("❯ Config", ansi.bold + C.tool)
|
|
143
143
|
pushLine(`Testing ${uri ? `via proxy ${uri}` : "direct (no proxy)"}...`, C.dim)
|
|
@@ -170,7 +170,7 @@ export async function handleConfigCommand(ctx, args = []) {
|
|
|
170
170
|
|
|
171
171
|
/** 会诊/飞刀候选池子菜单:列出 / 添加 / 编辑 effort / 删除 consultModels 条目。 */
|
|
172
172
|
async function pickEffort(current, model) {
|
|
173
|
-
const { specForModel } = await import("
|
|
173
|
+
const { specForModel } = await import("@thincoder/core/config.mjs")
|
|
174
174
|
const enumList = model ? specForModel(model).reasoningEffortEnum : null
|
|
175
175
|
// The model's reasoning-effort enum is HETEROGENEOUS across providers (deepseek:
|
|
176
176
|
// low/high/max; qwen3.8-max: xhigh/medium/low; kimi: 7 levels). A fixed
|
package/src/tui/cmd-eng.mjs
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/** /eng command: toggle engineering mode.
|
|
2
|
-
* No prerequisites (FR11
|
|
3
|
-
* no
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* No retired-concept prerequisites (FR11 收正口径 — `docs/core/requirements/PORTABILITY.md:62`):
|
|
3
|
+
* no methodology file, no docs tree. The **repo-root anchor(git)= E2 既有入口前提**:
|
|
4
|
+
* 非锚 cwd 上 ON = fail-closed 拒翻 + 明示原因、模式保持 OFF(#41 —
|
|
5
|
+
* `docs/core/design/MANIFEST.md` §2.8 F2/F3;判定面 = 入口决策树单源)。
|
|
6
|
+
* ctx: { agent, pushLine, pushLabel } */
|
|
6
7
|
import { readFileSync, writeFileSync, mkdirSync, renameSync, unlinkSync } from "node:fs"
|
|
7
8
|
import { dirname } from "node:path"
|
|
8
9
|
import { ansi, C } from "./ansi.mjs"
|
|
9
|
-
import { activeSlot, slotPath } from "
|
|
10
|
-
import { purgeExpiredDesignTokens } from "
|
|
10
|
+
import { activeSlot, slotPath } from "@thincoder/core/session.mjs"
|
|
11
|
+
import { purgeExpiredDesignTokens } from "@thincoder/core/token-ttl.mjs"
|
|
12
|
+
import { resolveEngineeringManifest } from "@thincoder/core/manifest.mjs"
|
|
11
13
|
|
|
12
|
-
import { ENG_OFF_REMINDER } from "
|
|
14
|
+
import { ENG_OFF_REMINDER } from "@thincoder/core/agent.mjs"
|
|
15
|
+
import { clearPlanMode } from "@thincoder/core/agent-tools/plan.mjs"
|
|
13
16
|
|
|
14
17
|
/** Atomic slot write (same shape as session.mjs writeSessionFile — kept local to avoid a
|
|
15
18
|
* private-import; cmd-advisor's guard toggle shares this helper). */
|
|
@@ -29,9 +32,26 @@ export async function handleEngCommand(ctx) {
|
|
|
29
32
|
const { agent, pushLine, pushLabel } = ctx
|
|
30
33
|
agent.config.agent ??= {}
|
|
31
34
|
|
|
32
|
-
//
|
|
33
|
-
//
|
|
35
|
+
// No retired-concept prerequisite check (FR11 收正口径;退场原因见档头注)。ON 方向的门
|
|
36
|
+
// = 入口决策树(下方);OFF 方向恒放行(F2 表 OFF 行——零 manifest I/O)。
|
|
37
|
+
//
|
|
38
|
+
// #41 先判后翻(MANIFEST.md §2.8 F2/F3 · KD-M1-21):**ON 方向**先走入口决策树单源
|
|
39
|
+
// (resolveEngineeringManifest——KD-M1-20),判据通过才写态;OFF 方向零改(F2 表 OFF 行)。
|
|
40
|
+
// 拒 ⇒ warn 行 + **零标签**(标签 = 成功回显——拒时零标签防假成功)+ 零副作用
|
|
41
|
+
// (模式未翻 / token 未清 / `_advisorRuns` 未重置 / 提醒未入列 / 槽未写)。
|
|
42
|
+
// 准 ⇒ 缺档格就地建档(writer:'main')+ `agent.manifest` ← 结果(相位行当回合起活)。
|
|
43
|
+
if (!agent.config.agent.engineering) {
|
|
44
|
+
const r = resolveEngineeringManifest(agent.cwd ?? process.cwd(), { writer: "main" })
|
|
45
|
+
if (!r.ok) {
|
|
46
|
+
pushLine(`Engineering mode not enabled — ${r.message} (mode unchanged)`, C.warn)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
agent.manifest = r.manifest
|
|
50
|
+
}
|
|
34
51
|
agent.config.agent.engineering = !agent.config.agent.engineering
|
|
52
|
+
// FR31 ③ / KD10(翻转点②·CLI `/eng`):ON ⇒ 清 plan 残留(内存位 + 未注入的 plan 提示语);
|
|
53
|
+
// OFF 方向不动(普通模式零改——FR31 边界)。提示行仅在真清到东西时出(不喧宾)。
|
|
54
|
+
const planReset = agent.config.agent.engineering ? clearPlanMode(agent) : false
|
|
35
55
|
// §11.2 D-24b: per-review instances die with the mode (fresh convergence cycles
|
|
36
56
|
// on the next toggle).
|
|
37
57
|
agent._advisorRuns = new Map()
|
|
@@ -52,6 +72,9 @@ export async function handleEngCommand(ctx) {
|
|
|
52
72
|
pushLine(`Engineering mode: ${agent.config.agent.engineering ? "ON" : "OFF"} (session)`, C.tool)
|
|
53
73
|
if (agent.config.agent.engineering) {
|
|
54
74
|
pushLine(` → design-before-code enforced (design review + user approval before code)`, C.dim)
|
|
75
|
+
if (planReset) {
|
|
76
|
+
pushLine(` → plan mode reset (engineering mode excludes plan mode)`, C.dim)
|
|
77
|
+
}
|
|
55
78
|
if (clearedExpired > 0) {
|
|
56
79
|
pushLine(` → cleared ${clearedExpired} expired design token${clearedExpired === 1 ? "" : "s"}; valid tokens from prior reviews stay usable`, C.dim)
|
|
57
80
|
}
|
|
@@ -64,6 +87,8 @@ export async function handleEngCommand(ctx) {
|
|
|
64
87
|
* VS Code, per-session; config.json is just the initial default, no mirror write).
|
|
65
88
|
* The in-memory agent.config.agent.engineering (already flipped) stays the live authority for
|
|
66
89
|
* this process; saveSession also round-trips it on every turn-end write.
|
|
90
|
+
* 槽写也担 FR31 ③ 的槽位收正(T12):ON 时 `data.planMode = false`——否则下次装载/恢复
|
|
91
|
+
* 把 plan-active 推回 UI(半状态复活面;验收判据 = 「内存 + 槽位」双清零)。OFF 方向零改。
|
|
67
92
|
*/
|
|
68
93
|
async function persistEngineering(agent) {
|
|
69
94
|
const slot = activeSlot(agent.cwd)
|
|
@@ -72,6 +97,7 @@ async function persistEngineering(agent) {
|
|
|
72
97
|
const data = JSON.parse(readFileSync(p, "utf8"))
|
|
73
98
|
if (data && typeof data === "object" && Array.isArray(data.history)) {
|
|
74
99
|
data.engineering = agent.config.agent.engineering
|
|
100
|
+
if (agent.config.agent.engineering) data.planMode = false
|
|
75
101
|
writeSessionFile(p, data)
|
|
76
102
|
}
|
|
77
103
|
} catch { /* slot missing/unreadable — in-memory flag already flipped; saveSession persists at turn end */ }
|