thincoder 0.12.69 → 0.12.70
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 +23 -0
- package/README.md +11 -3
- package/bin/node-version-gate.cjs +28 -0
- package/bin/thincoder.cjs +2 -0
- package/bin/thincoder.mjs +2 -0
- package/package.json +2 -2
- package/src/acp/bridge.mjs +50 -12
- package/src/acp/handlers-session.mjs +95 -44
- package/src/acp/handlers-slots.mjs +41 -14
- package/src/acp/read-data.mjs +127 -0
- package/src/acp/resource-link.mjs +124 -0
- package/src/acp/session.mjs +1 -1
- package/src/acp.mjs +15 -6
- package/src/command-interactive.mjs +1 -1
- package/src/command-table.mjs +8 -1
- package/src/completions.mjs +7 -4
- package/src/tui/cmd-model.mjs +3 -2
- package/src/tui/cmd-reindex.mjs +10 -0
- package/src/tui/cmd-undo.mjs +16 -38
- package/src/tui/config-helpers.mjs +34 -1
- package/src/tui/index.mjs +5 -5
- package/src/tui/ledger-surface.mjs +5 -7
- package/src/tui/model-picker.mjs +15 -6
- package/src/tui/startup.mjs +12 -2
- package/src/tui/wizard.mjs +1 -1
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ledger-surface.mjs — TUI 台账可见面胶水(K-LX3 归核:实现单源 = `@thincoder/core/ledger-surface.mjs`,
|
|
3
|
-
* 本档只剩**动态 import
|
|
3
|
+
* 本档只剩**动态 import 转口**一事——端壳静态链不得达 `node:sqlite`(W8 契约②);
|
|
4
4
|
* 载入失败 = 降级不崩(N1),扫描拍重试)。
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* 本档零实现、零复刻(KD-M2-3)。
|
|
6
|
+
* 状态位语义见核档头注;判活展示(F-LX1)亦随归核自动带上——本档零实现、零复刻(KD-M2-3)。
|
|
8
7
|
*
|
|
9
8
|
* 两端契约(#233——端壳与核形一致):`startLedgerSurface` **恒同步返回 `{ dispose }`**——
|
|
10
9
|
* 核句柄异步就绪(动态 import)后桥接;载入失败 ⇒ 空句柄(`dispose` 恒可调);`dispose()`
|
|
11
10
|
* 早于核就绪 ⇒ 置位且**跳过启动**(核侧零调用——零周期;N1 降级不崩)。调用点因此恒可按
|
|
12
11
|
* 同步形态挂进程退钩(`process.on("exit", () => handle.dispose())`)。
|
|
13
12
|
*/
|
|
14
|
-
import { C } from "./ansi.mjs"
|
|
15
13
|
|
|
16
14
|
/** 核台账面惰性载入(动态 import——失败不缓存,下次扫描拍重试;N1 降级不崩)。 */
|
|
17
15
|
let _core = null
|
|
@@ -29,10 +27,10 @@ let _loadCoreForTest = null
|
|
|
29
27
|
export function _setLoadCoreForTest(fn) { _loadCoreForTest = fn }
|
|
30
28
|
const loadCoreNow = () => (_loadCoreForTest ?? loadCore)()
|
|
31
29
|
|
|
32
|
-
/**
|
|
30
|
+
/** 单次扫描(直驱面——直通核实现)。 */
|
|
33
31
|
export async function runLedgerScan(opts = {}) {
|
|
34
32
|
const core = await loadCoreNow()
|
|
35
|
-
return core.runLedgerScan(
|
|
33
|
+
return core.runLedgerScan(opts)
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
/** TUI 挂载(首扫 + 周期——直通核实现):**恒同步返回 `{ dispose }`**——核句柄异步就绪后
|
|
@@ -42,7 +40,7 @@ export function startLedgerSurface(ctx = {}) {
|
|
|
42
40
|
let disposed = false
|
|
43
41
|
let handle = null
|
|
44
42
|
loadCoreNow()
|
|
45
|
-
.then((core) => { if (!disposed) handle = core.startLedgerSurface(
|
|
43
|
+
.then((core) => { if (!disposed) handle = core.startLedgerSurface(ctx) })
|
|
46
44
|
.catch(() => { /* 载入失败 ⇒ 空句柄(N1) */ })
|
|
47
45
|
return {
|
|
48
46
|
dispose() {
|
package/src/tui/model-picker.mjs
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* model-picker.mjs — /model two-level picker + provider management + session-slot model
|
|
3
3
|
* selection(MODEL-MERGE-SESSION 拆分产物——/model 两级面自 pickers.mjs 迁出)。
|
|
4
4
|
*
|
|
5
|
-
* 语义(2026-09-10 MODEL-SELECTION v2):selectModel 写**会话槽**(agent 内存态 + saveSession
|
|
6
|
-
*
|
|
5
|
+
* 语义(2026-09-10 MODEL-SELECTION v2):selectModel 写**会话槽**(agent 内存态 + saveSession);
|
|
6
|
+
* 会话级模型面**实变** ⇒ 同拍写回 config.defaultModel(判据句 6——写面单点 = `config-helpers.mjs`
|
|
7
|
+
* `carryoverDefaultModel`;等值 ∕ 回声零写)。候选 = **运行期拉取**(`GET /models`——M2;
|
|
7
8
|
* 拉到的行直接可选);显式 `provider:model` 一律放行(M4——仅[空值/裸值/未知 provider]无效)。
|
|
8
9
|
* 切换成功回显规格来源(M6);渠道默认模型 = `providers[].model` 单值(M3——显示回退读取)。
|
|
9
10
|
* 配置写入面(加渠道 / 设 key)探一次 `/models`(M9——探不通标「不可用」+ 明示原因,不阻断保存)。
|
|
@@ -15,7 +16,8 @@
|
|
|
15
16
|
*/
|
|
16
17
|
import { sliceByWidth } from "./render.mjs"
|
|
17
18
|
import { providerSpec, specMatch } from "@thincoder/core/config.mjs"
|
|
18
|
-
import { saveSession } from "@thincoder/core/session.mjs"
|
|
19
|
+
import { saveSession, loadSlotFile } from "@thincoder/core/session.mjs"
|
|
20
|
+
import { carryoverDefaultModel } from "./config-helpers.mjs"
|
|
19
21
|
import { getProviderModels, modelListFailureText, dedupeModels } from "./model-catalog.mjs"
|
|
20
22
|
import { createProviderAdmin } from "./provider-admin.mjs"
|
|
21
23
|
|
|
@@ -218,7 +220,8 @@ export function createModelPicker(ctx) {
|
|
|
218
220
|
return entries
|
|
219
221
|
}
|
|
220
222
|
|
|
221
|
-
/** F-3 /model
|
|
223
|
+
/** F-3 /model 会话级:写槽(agent 内存态 + saveSession);选定实变 ⇒ 同拍写回
|
|
224
|
+
* config.defaultModel(判据句 6——新会话起点随动;等值 ∕ 回声零写;写回失败不反扑会话写、记错零静默)。
|
|
222
225
|
* 放行语义(M4):仅未知 provider 拒——显式 p:m 一律放行(候选外/多冒号不再拒)。 */
|
|
223
226
|
async function selectModel(item) {
|
|
224
227
|
closePicker()
|
|
@@ -229,7 +232,9 @@ export function createModelPicker(ctx) {
|
|
|
229
232
|
if (!item.model) {
|
|
230
233
|
throw new Error(`Missing model name for provider: ${item.provider}`)
|
|
231
234
|
}
|
|
232
|
-
//
|
|
235
|
+
// 写前读:槽复合基线(判据句 6 实变单元——档缺 ⇒ null;`_slot` 未钉 ⇒ 无槽面)
|
|
236
|
+
const slotBefore = agent._slot == null ? null : loadSlotFile(agent.cwd, agent._slot)
|
|
237
|
+
// 内存态(agent 会话运行时)——config 写仅经写回单点(下方实变节)
|
|
233
238
|
agent.activeProvider = target.name
|
|
234
239
|
agent.activeModel = item.model
|
|
235
240
|
agent.provider = { ...target }
|
|
@@ -238,8 +243,12 @@ export function createModelPicker(ctx) {
|
|
|
238
243
|
const { resolveCompactThreshold } = await import("@thincoder/core/config.mjs")
|
|
239
244
|
agent.config.agent.compactThreshold = resolveCompactThreshold(null, agent.provider).value
|
|
240
245
|
}
|
|
241
|
-
// 写会话槽(saveSession
|
|
246
|
+
// 写会话槽(saveSession——槽双字段恒非空)——写回单点在其后(槽先配置后)
|
|
242
247
|
saveSession(agent)
|
|
248
|
+
// 选定写回(判据句 6——定序槽先配置后;失败不反扑会话写、记错零静默)
|
|
249
|
+
const carried = await carryoverDefaultModel({ provider: target.name, model: item.model, slotBefore })
|
|
250
|
+
if (carried.ok !== true) console.error(`[model] default model carryover failed: ${carried.reason}`)
|
|
251
|
+
else if (carried.written === true && agent.config) agent.config.defaultModel = `${target.name}:${item.model}` // 内存镜像(/config 显示面新鲜——wizard.mjs:210-211 先例)
|
|
243
252
|
// M6 切换回显:规格来源一行(DEFAULT 兜底 → 警示色 + /config 提示)
|
|
244
253
|
const { matched } = specMatch(agent.provider.model)
|
|
245
254
|
const spec = providerSpec(agent.provider)
|
package/src/tui/startup.mjs
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { homedir } from "node:os"
|
|
2
|
+
import { resolve } from "node:path"
|
|
1
3
|
import { listSlots, ledgerHealth } from "@thincoder/core/session.mjs"
|
|
2
4
|
import { stripAtRefs } from "@thincoder/core/file-refs.mjs"
|
|
3
5
|
import { ansi, C } from "./ansi.mjs"
|
|
@@ -263,12 +265,20 @@ export function showStartup(ctx) {
|
|
|
263
265
|
render()
|
|
264
266
|
}
|
|
265
267
|
|
|
268
|
+
/** home 根防护(#867 · `MEMORY.md` §6.14 L-④):`cwd` = 用户主目录(resolve 后;win32 大小写不敏感)。 */
|
|
269
|
+
const isHomeDir = (dir) => typeof dir === "string" && dir !== "" &&
|
|
270
|
+
(process.platform === "win32" ? resolve(dir).toLowerCase() === resolve(homedir()).toLowerCase() : resolve(dir) === resolve(homedir()))
|
|
271
|
+
|
|
266
272
|
/** Background indexing (runs after startup screen, non-blocking); progress shown in status bar, not conversation.
|
|
267
273
|
* Prefers git diff incremental (fast); falls back to full scan when git is unavailable or on first run. */
|
|
268
274
|
export async function backgroundIndex(ctx) {
|
|
269
|
-
const { agent, state, render } = ctx
|
|
270
|
-
const { codeSync, docSync, gitSync } = await import("@thincoder/core/memory.mjs")
|
|
275
|
+
const { agent, state, render, pushLine } = ctx
|
|
271
276
|
const cwd = agent.cwd
|
|
277
|
+
if (isHomeDir(cwd)) { // #867:home 根 ⇒ 跳过索引(三 sync 零调用;不阻断启动)
|
|
278
|
+
pushLine("[index] Skipped: working directory is the home directory — start in a project dir", C.warn)
|
|
279
|
+
return
|
|
280
|
+
}
|
|
281
|
+
const { codeSync, docSync, gitSync } = await import("@thincoder/core/memory.mjs")
|
|
272
282
|
let codeFiles = 0, docFiles = 0
|
|
273
283
|
|
|
274
284
|
state.status = "Indexing..."
|
package/src/tui/wizard.mjs
CHANGED
|
@@ -162,7 +162,7 @@ export function createWizard(ctx) {
|
|
|
162
162
|
state.wizard = null
|
|
163
163
|
// MODEL-MERGE-SESSION 引导 A(F-6):有 provider 但 defaultModel 未设时指引 /config 入口
|
|
164
164
|
const hint = (agent.providers?.length ?? 0) > 0 && !agent.config?.defaultModel
|
|
165
|
-
? "Skipped initial setup. 已配置渠道但 config.defaultModel 未设——新会话无起点:/config → 默认模型 设置一次(或 /model
|
|
165
|
+
? "Skipped initial setup. 已配置渠道但 config.defaultModel 未设——新会话无起点:/config → 默认模型 设置一次(或 /model 选定即成为默认模型)。"
|
|
166
166
|
: "Skipped initial setup. Use /model to add providers and configure API keys anytime."
|
|
167
167
|
pushLine(hint, C.dim)
|
|
168
168
|
render()
|