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/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 } 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)。钩子后移的可观察后果(拒 / 建档
|
|
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,44 @@ 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 跨模式防陈旧);工程模式四态:合法档 → 附着;缺档 + 根可解析 → `initManifest`
|
|
149
|
+
* 建档(拒进正常循环直到生成——E2 前置门槛);缺档 + 根不可解析 → 抛「工程模式启动拒绝」
|
|
150
|
+
* **不自动建档**;非法档 → fail-closed 抛出。
|
|
151
|
+
*
|
|
152
|
+
* `slotData` = 恢复槽记录,由调用方经**参数注入**(`bin` TUI 分支既有 `resumeSlot` 调用
|
|
153
|
+
* 前移——KD-M1-15:钩子内不调 `resumeSlot`(该函数非纯读:GC / 认领 / 端标写);`chat` /
|
|
154
|
+
* ACP 装配无会话 ⇒ 不传 ⇒ config 回退)。重估点不传(`applySession` 已按槽订正 config)。
|
|
155
|
+
*
|
|
156
|
+
* #41(薄包装——KD-M1-20 / AC-19):工程模式分支的决策树 = 核单源
|
|
157
|
+
* `resolveEngineeringManifest`(非抛错形态);本函数只把它翻回「抛错」——四条出口
|
|
158
|
+
* (附着 / 缺档建档 / 根不可解析抛 / 档非法抛)的**结果与文案逐字不变**(原句由决策树构造)。
|
|
159
|
+
* @param {object} agent — createAgent 产物(读 agent.config;写 agent.manifest)
|
|
160
|
+
* @param {{cwd?: string, slotData?: object|null}} [opts]
|
|
161
|
+
* @returns {object|null} 附着后的 manifest / 普通会话 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
|
+
const r = resolveEngineeringManifest(cwd, { writer: "main" })
|
|
172
|
+
if (!r.ok) throw new Error(r.message)
|
|
173
|
+
agent.manifest = r.manifest
|
|
174
|
+
return agent.manifest
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* SESSION.md §6.8 D-S1 — provider 有效性校验(assembleAgent 后唯一校验点,TUI/chat 两路径同源)。
|
|
137
179
|
* 判据(评审 #1/#2):仅 model/baseURL 缺失判 invalid——**不得用 MODEL_SPECS 成员资格判无效**
|
|
138
180
|
* (未知模型 = 受支持场景:自定义端点模型不在 spec 表是常态,误判会让自定义模型用户每次恢复都弹重选)。
|
|
139
181
|
* 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,17 @@
|
|
|
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"
|
|
13
15
|
|
|
14
16
|
/** Atomic slot write (same shape as session.mjs writeSessionFile — kept local to avoid a
|
|
15
17
|
* private-import; cmd-advisor's guard toggle shares this helper). */
|
|
@@ -29,8 +31,22 @@ export async function handleEngCommand(ctx) {
|
|
|
29
31
|
const { agent, pushLine, pushLabel } = ctx
|
|
30
32
|
agent.config.agent ??= {}
|
|
31
33
|
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
+
// No retired-concept prerequisite check (FR11 收正口径;退场原因见档头注)。ON 方向的门
|
|
35
|
+
// = 入口决策树(下方);OFF 方向恒放行(F2 表 OFF 行——零 manifest I/O)。
|
|
36
|
+
//
|
|
37
|
+
// #41 先判后翻(MANIFEST.md §2.8 F2/F3 · KD-M1-21):**ON 方向**先走入口决策树单源
|
|
38
|
+
// (resolveEngineeringManifest——KD-M1-20),判据通过才写态;OFF 方向零改(F2 表 OFF 行)。
|
|
39
|
+
// 拒 ⇒ warn 行 + **零标签**(标签 = 成功回显——拒时零标签防假成功)+ 零副作用
|
|
40
|
+
// (模式未翻 / token 未清 / `_advisorRuns` 未重置 / 提醒未入列 / 槽未写)。
|
|
41
|
+
// 准 ⇒ 缺档格就地建档(writer:'main')+ `agent.manifest` ← 结果(相位行当回合起活)。
|
|
42
|
+
if (!agent.config.agent.engineering) {
|
|
43
|
+
const r = resolveEngineeringManifest(agent.cwd ?? process.cwd(), { writer: "main" })
|
|
44
|
+
if (!r.ok) {
|
|
45
|
+
pushLine(`Engineering mode not enabled — ${r.message} (mode unchanged)`, C.warn)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
agent.manifest = r.manifest
|
|
49
|
+
}
|
|
34
50
|
agent.config.agent.engineering = !agent.config.agent.engineering
|
|
35
51
|
// §11.2 D-24b: per-review instances die with the mode (fresh convergence cycles
|
|
36
52
|
// on the next toggle).
|
package/src/tui/cmd-mcp.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { ansi, C } from "./ansi.mjs"
|
|
|
2
2
|
import { cloneEntry, fieldPicker, maskToken } from "./cmd-mcp-form.mjs"
|
|
3
3
|
|
|
4
4
|
/** /mcp command handler: view/add/edit/remove/test/reconnect MCP server.
|
|
5
|
-
* ctx: { agent, pushLine, pushLabel, showPicker, askQuestion, persistRaw, ansi, C }
|
|
5
|
+
* ctx: { agent, pushLine, pushLabel, showPicker, askQuestion, persistRaw, ansi, C, confirmDelete }
|
|
6
6
|
* MCP.md §5(2026-09-02,v2):列表即菜单(F7/D-2)、edit/add 统一字段 picker 表单
|
|
7
7
|
* (F3/F3b/D-1——表单机制在 cmd-mcp-form.mjs,本文件只调 fieldPicker)、保存前预览
|
|
8
8
|
* +探活(F2:探活失败回同一 fieldPicker——AC2)、AI 降 transport picker 末位(F4)、
|
|
@@ -37,7 +37,7 @@ async function addAndConnect(ctx, srv) {
|
|
|
37
37
|
agent.config.mcp.servers.push(srv)
|
|
38
38
|
try {
|
|
39
39
|
pushLine(`[mcp] Connecting ${srv.name}...`, C.dim)
|
|
40
|
-
const { connectMcpServer } = await import("
|
|
40
|
+
const { connectMcpServer } = await import("@thincoder/core/mcp.mjs")
|
|
41
41
|
const tools = await connectMcpServer(srv)
|
|
42
42
|
agent.tools.push(...tools)
|
|
43
43
|
pushLabel(`❯ MCP`, ansi.bold + C.tool)
|
|
@@ -50,7 +50,7 @@ async function addAndConnect(ctx, srv) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export async function handleMcpCommand(ctx, args = []) {
|
|
53
|
-
const { agent, pushLine, pushLabel, showPicker, askQuestion, persistRaw } = ctx
|
|
53
|
+
const { agent, pushLine, pushLabel, showPicker, askQuestion, persistRaw, confirmDelete = () => { throw new Error("ctx.confirmDelete missing — deletion refused (fail-closed)") } } = ctx
|
|
54
54
|
// 每轮重读:原本无 mcp 配置时 `?? []` 会拿到游离数组,Add server 后快照过期
|
|
55
55
|
const getServers = () => agent.config?.mcp?.servers ?? []
|
|
56
56
|
// D-3/T24:本菜单会话内最新一次磁盘重读是否失败(畸形 config.json → 内存态兜底)
|
|
@@ -61,7 +61,7 @@ export async function handleMcpCommand(ctx, args = []) {
|
|
|
61
61
|
* 测试注入 tmp config 路径用(生产 undefined → 默认 ~/.thincoder)。 */
|
|
62
62
|
let changedNames = [] // 最近一次成功重读的对账结果(T23 ⚠ 标记)
|
|
63
63
|
async function reloadFromDisk() {
|
|
64
|
-
const { reloadMcpFromDisk } = await import("
|
|
64
|
+
const { reloadMcpFromDisk } = await import("@thincoder/core/config.mjs")
|
|
65
65
|
const r = reloadMcpFromDisk(agent, ctx.configPath)
|
|
66
66
|
diskUnreadable = !r.ok
|
|
67
67
|
if (r.ok) changedNames = r.changedNames
|
|
@@ -85,6 +85,7 @@ export async function handleMcpCommand(ctx, args = []) {
|
|
|
85
85
|
// D-F5a(#7)先盘后存:磁盘 fresh raw.mcp.servers 上只删目标条目——不整节写回内存
|
|
86
86
|
// agent.config.mcp.servers(内存含 reloadMcpFromDisk 的 keptConnected 尾巴——
|
|
87
87
|
// 整节写回会把对端磁盘上其他 server 的新改动一起抹掉);冲突放弃不留内存 ghost
|
|
88
|
+
if (!(await confirmDelete(`Remove MCP server "${name}"?`))) return
|
|
88
89
|
await persistRaw((raw) => {
|
|
89
90
|
raw.mcp ??= { servers: [] }
|
|
90
91
|
if (!Array.isArray(raw.mcp.servers)) return
|
|
@@ -92,14 +93,14 @@ export async function handleMcpCommand(ctx, args = []) {
|
|
|
92
93
|
})
|
|
93
94
|
agent.config.mcp.servers = getServers().filter((s) => s.name !== name)
|
|
94
95
|
// Remove from tool list
|
|
95
|
-
const { removeMcpTools } = await import("
|
|
96
|
+
const { removeMcpTools } = await import("@thincoder/core/mcp.mjs")
|
|
96
97
|
removeMcpTools(agent, name)
|
|
97
98
|
pushLine(`[mcp] ${name} removed`, C.tool)
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
async function connectServer(name) {
|
|
101
102
|
const srv = getServers().find((s) => s.name === name)
|
|
102
|
-
const { removeMcpTools, connectMcpServer } = await import("
|
|
103
|
+
const { removeMcpTools, connectMcpServer } = await import("@thincoder/core/mcp.mjs")
|
|
103
104
|
removeMcpTools(agent, name)
|
|
104
105
|
try {
|
|
105
106
|
pushLine(`[mcp] Reconnecting ${name}...`, C.dim)
|
|
@@ -130,7 +131,7 @@ export async function handleMcpCommand(ctx, args = []) {
|
|
|
130
131
|
* agent.tools、探完即关)。未保存的临时 entry 直接传 probe(§5 D-1)。 */
|
|
131
132
|
async function probeLineFor(entry) {
|
|
132
133
|
pushLine(`[mcp] Probing ${entry.name}...`, C.dim)
|
|
133
|
-
const { probeMcpServer } = await import("
|
|
134
|
+
const { probeMcpServer } = await import("@thincoder/core/mcp.mjs")
|
|
134
135
|
const r = await probeMcpServer(entry)
|
|
135
136
|
return r.ok ? `✓ ${r.toolCount} tools, ${r.latencyMs}ms` : `✗ ${r.error}`
|
|
136
137
|
}
|
|
@@ -160,7 +161,7 @@ export async function handleMcpCommand(ctx, args = []) {
|
|
|
160
161
|
if (!description) return
|
|
161
162
|
pushLine("[mcp] Generating config from description...", C.dim)
|
|
162
163
|
try {
|
|
163
|
-
const { chat } = await import("
|
|
164
|
+
const { chat } = await import("@thincoder/core/provider/index.mjs")
|
|
164
165
|
const res = await chat(agent.provider, {
|
|
165
166
|
messages: [{
|
|
166
167
|
role: "user",
|
package/src/tui/cmd-model.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { C } from "./ansi.mjs"
|
|
2
|
-
import { parseModelRef } from "
|
|
2
|
+
import { parseModelRef } from "@thincoder/core/config.mjs"
|
|
3
3
|
|
|
4
4
|
/** /model command: open the session model picker, or switch the SESSION model directly via
|
|
5
5
|
* `/model <provider>:<model>`(MODEL-MERGE-SESSION——裁定③:裸 provider 拒——显式 p:m;
|