thincoder 0.12.68 → 0.12.69
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 +4 -0
- package/package.json +2 -2
- package/src/command-interactive.mjs +18 -8
- package/src/tui/index.mjs +34 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## [0.12.69] — 2026-10-03
|
|
2
|
+
|
|
3
|
+
> 0.12.68 → 0.12.69(连续号——发布时定号)
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **核依赖升级**:`@thincoder/core` `^0.10.1` → `^0.10.2`(统一 provider 解析随核生效)。
|
|
8
|
+
- **无效 provider 态分流(#841)**:TUI——`fallback`(可运行但未设默认模型)⇒ 不弹选择器 + 一行明示(`模型` 缺 ⇒ 词形仅渠道名);真无效(无 provider ∕ 无 key)⇒ 选择器 + 引导提示行。headless——真无效 ⇒ 可读错误 + 明确退出码;`fallback` ⇒ stderr 一行 + **继续运行**(不退出)。
|
|
9
|
+
|
|
1
10
|
## [0.12.68] — 2026-10-03
|
|
2
11
|
|
|
3
12
|
> 0.12.67 → 0.12.68(连续号——发布时定号)
|
package/README.md
CHANGED
|
@@ -231,6 +231,10 @@ Code conventions: pure `.mjs`, no semicolons, no third-party npm dependencies al
|
|
|
231
231
|
|
|
232
232
|
## Changelog
|
|
233
233
|
|
|
234
|
+
### 0.12.69 (2026-10)
|
|
235
|
+
- **Provider-state unify (#841)** — one core resolution for the three ends (slot → `defaultModel` → first keyed channel) with three states: `ok`; `fallback` (runnable but no default model — one clear notice line, never blocked); `invalid` (no provider/key — guided picker; headless exits with a readable message and a clear code, while `fallback` prints one stderr line and keeps running). No more false "API key not configured" on a working channel.
|
|
236
|
+
- **Core `^0.10.2`** — unified provider resolution (`resolveProviderPlan` / `resolveChannelModel`), `providerState` / `providerStateReason` keys, slot key gate.
|
|
237
|
+
|
|
234
238
|
### 0.12.68 (2026-10)
|
|
235
239
|
- **Idle wake** — suspended sessions wake to timers: they resume on schedule (visibility surface + abort accommodation).
|
|
236
240
|
- **Ledger health signals** — an unhealthy ledger database now shows a warning line in the TUI (empty values render as `—`).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thincoder",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.69",
|
|
4
4
|
"description": "Thin coding agent - zero third-party dependencies, no build step, Node.js native. Sharp code, zero bloat.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"node": ">=24"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@thincoder/core": "^0.10.
|
|
23
|
+
"@thincoder/core": "^0.10.2"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"bin/",
|
|
@@ -30,14 +30,23 @@ export async function chatCommand(args, ctx) {
|
|
|
30
30
|
if (crashNotice) console.error(crashNotice)
|
|
31
31
|
|
|
32
32
|
const agent = await assembleAgent()
|
|
33
|
-
// SESSION.md §6.8 D-S4
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
// SESSION.md §6.8 D-S4(#841 收正):headless 无 TUI——按**核统一态分流**(判据单源 = PROVIDER.md §6.22,
|
|
34
|
+
// 与 TUI 面同式):**invalid 类**(`state === "invalid"` ∨ `providerInvalidReason` 非空——无渠/key
|
|
35
|
+
// ⇒ 不可运行)⇒ 可读错误 + 退出码 1(不弹 UI、不崩溃);**fallback**(无有效 defaultModel 但可运行)
|
|
36
|
+
// ⇒ stderr 一行明示 + 继续运行(不退出——D-S2 同态同解)。条目结构不全不在本门(负向锁 =
|
|
37
|
+
// PROVIDER.md:437)——归运行期失败径(stderr + 非 0 退出)。
|
|
38
|
+
const headlessCfg = agent.config ?? {}
|
|
39
|
+
const invalidClass = headlessCfg.providerState === "invalid"
|
|
40
|
+
|| (typeof headlessCfg.providerInvalidReason === "string" && headlessCfg.providerInvalidReason !== "")
|
|
41
|
+
if (invalidClass) {
|
|
42
|
+
console.error(`[error] 未配置有效渠道(${agent._providerInvalidReason ?? headlessCfg.providerStateReason ?? "provider unavailable"})。请运行 thincoder 进入 TUI 设置(/config 设置渠道与密钥;或 /model 选择后以会话槽生效),或编辑 ${configPath}`)
|
|
38
43
|
exitSoon(1)
|
|
39
44
|
return
|
|
40
45
|
}
|
|
46
|
+
if (headlessCfg.providerState === "fallback" && agent.provider?.name) {
|
|
47
|
+
const m = typeof agent.provider.model === "string" && agent.provider.model ? `:${agent.provider.model}` : ""
|
|
48
|
+
console.error(`尚未设置默认模型:本次使用 \`${agent.provider.name}${m}\`——/config → 默认模型 设置一次;/model 仅改本会话`)
|
|
49
|
+
}
|
|
41
50
|
if (!agent.provider.apiKey) {
|
|
42
51
|
if (!process.stdin.isTTY) {
|
|
43
52
|
console.error(noKeyMessage())
|
|
@@ -121,9 +130,10 @@ export async function tuiCommand(ctx) {
|
|
|
121
130
|
// F-MI7:resumeSlot = async(探测束不阻塞事件循环)——此处必须 await
|
|
122
131
|
const { slot, data } = await resumeSlot(process.cwd())
|
|
123
132
|
const agent = await assembleAgent({ slotData: data })
|
|
124
|
-
// SESSION.md §6.8 D-S1
|
|
125
|
-
// (崩溃源:chat() 缺 model → 网关 400
|
|
126
|
-
|
|
133
|
+
// SESSION.md §6.8 D-S1(#841 收正):TUI 路径**仅在 `state === "invalid"`**(无渠/key)于 startTUI
|
|
134
|
+
// 前置 `agent.provider = null`——空 provider 不流入 runAgent(崩溃源:chat() 缺 model → 网关 400
|
|
135
|
+
// 或 fetch("undefined/...") TypeError);`fallback` 态 provider 有效——不清(可运行 + 明示)。
|
|
136
|
+
if (agent.config?.providerState === "invalid") agent.provider = null
|
|
127
137
|
const config = loadConfig()
|
|
128
138
|
if (data) {
|
|
129
139
|
// applySession 内部已按槽复合重算 compactThreshold(auto 时)——不再需要 switched 分支
|
package/src/tui/index.mjs
CHANGED
|
@@ -47,24 +47,40 @@ import { createTimerWatch, fireTimerWake } from "./timer-watch.mjs"
|
|
|
47
47
|
export { upgradeFailureText, pendingNoticeReady } from "./update-notice.mjs"
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* SESSION.md §6.8 D-S2 — TUI 启动首帧前的 provider
|
|
51
|
-
*
|
|
52
|
-
* (复用 openModelPicker,展示当前可用 providers
|
|
53
|
-
*
|
|
54
|
-
*
|
|
50
|
+
* SESSION.md §6.8 D-S2 — TUI 启动首帧前的 provider 分流(判据单源 = PROVIDER.md §6.22):
|
|
51
|
+
* · **invalid 类**(`state === "invalid"` ∨ `providerInvalidReason` 非空——核「provider 不完整面」
|
|
52
|
+
* 快照的合成式)→ 先弹模型选择 picker(复用 openModelPicker,展示当前可用 providers);
|
|
53
|
+
* 用户选定后继续正常启动。选择取消(Esc)→ 仍进入 TUI,推送提示行(措辞指渠道 ∕ 密钥——
|
|
54
|
+
* 绝不因无 provider 拒绝进入)。
|
|
55
|
+
* · **`fallback`**(无有效 defaultModel 但可运行)→ **不弹 picker**(不打断)+ 提示行明示
|
|
56
|
+
* (「尚未设置默认模型:本次使用 `<渠道>[:<模型>]`——…」;`model` 缺省 ⇒ 仅渠道名)。
|
|
57
|
+
* 返回 true 表示弹过选择流程。
|
|
55
58
|
*/
|
|
56
59
|
export async function promptProviderIfInvalid(agent, openModelPicker, pushLine) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
const cfg = agent.config ?? {}
|
|
61
|
+
// invalid 类(合成式——单源 = PROVIDER.md §6.22):`state === "invalid"` ∨ `providerInvalidReason`
|
|
62
|
+
// 非空(核 provider 不完整面快照);`!agent.provider`(`{}` 已被上游清成 null)同判。
|
|
63
|
+
// 结构不全(缺 baseURL 等)不在本门——归发送失败面(#840 `provider` 类词);负向锁 = PROVIDER.md:437。
|
|
64
|
+
const invalidClass = cfg.providerState === "invalid"
|
|
65
|
+
|| (typeof cfg.providerInvalidReason === "string" && cfg.providerInvalidReason !== "")
|
|
66
|
+
|| !agent.provider
|
|
67
|
+
if (invalidClass) {
|
|
68
|
+
await openModelPicker()
|
|
69
|
+
if (!agent.provider) {
|
|
70
|
+
// D-S2 取消提示(#841 措辞收正 = 渠道 ∕ 密钥——配置入口 /config)
|
|
71
|
+
pushLine("未配置有效渠道(渠道 ∥ 密钥缺位):/config 设置渠道与密钥;/model 仅改本会话", C.warn)
|
|
72
|
+
}
|
|
73
|
+
return true
|
|
66
74
|
}
|
|
67
|
-
|
|
75
|
+
if (cfg.providerState === "fallback") {
|
|
76
|
+
const name = agent.provider?.name
|
|
77
|
+
if (name) {
|
|
78
|
+
const model = typeof agent.provider.model === "string" && agent.provider.model ? agent.provider.model : null
|
|
79
|
+
pushLine(`尚未设置默认模型:本次使用 \`${name}${model ? `:${model}` : ""}\`——/config → 默认模型 设置一次;/model 仅改本会话`, C.warn)
|
|
80
|
+
}
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
return false
|
|
68
84
|
}
|
|
69
85
|
|
|
70
86
|
/**
|
|
@@ -220,8 +236,9 @@ export async function startTUI(agent, opts = {}) {
|
|
|
220
236
|
|
|
221
237
|
// ---------------------------------------------------------- Startup screen + background indexing
|
|
222
238
|
|
|
223
|
-
// SESSION.md §6.8 D-S2:startTUI
|
|
224
|
-
// → 先弹模型选择 picker(keyStream 已挂 keypress,Esc/Enter 可用);Esc → 提示行,仍进 TUI
|
|
239
|
+
// SESSION.md §6.8 D-S2:startTUI 首帧前——按核统一态分流(单源 = PROVIDER.md §6.22):
|
|
240
|
+
// invalid 类 → 先弹模型选择 picker(keyStream 已挂 keypress,Esc/Enter 可用);Esc → 提示行,仍进 TUI;
|
|
241
|
+
// fallback → 不弹 picker + 提示行明示(本次使用 `<渠道>[:<模型>]`)
|
|
225
242
|
await promptProviderIfInvalid(agent, () => openModelPicker(), pushLine)
|
|
226
243
|
|
|
227
244
|
showStartup({ agent, state, opts, pushLine, pushLabel, render, startWizard })
|