@kidsinai/kids-client 0.0.23 → 0.0.25
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/package.json +2 -2
- package/src/core/models.ts +25 -0
- package/src/index.tsx +24 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@kidsinai/kids-client",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.25",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Own-client TUI for Kids OpenCode — talks to local `opencode serve` via @opencode-ai/sdk v2 with kid-warm rendering, mission progress, permission dialog, and stderr-tail audit pipeline.",
|
|
7
7
|
"license": "MIT",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"ink-spinner": "^5.0.0",
|
|
36
36
|
"ink-text-input": "^6.0.0",
|
|
37
37
|
"react": "^18.3.1",
|
|
38
|
-
"@kidsinai/kids-opencode-plugin": "^0.0.
|
|
38
|
+
"@kidsinai/kids-opencode-plugin": "^0.0.24"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@opencode-ai/sdk": "^1.14.51",
|
package/src/core/models.ts
CHANGED
|
@@ -69,3 +69,28 @@ function pickModels(models: unknown): unknown[] {
|
|
|
69
69
|
if (models && typeof models === "object") return Object.values(models)
|
|
70
70
|
return []
|
|
71
71
|
}
|
|
72
|
+
|
|
73
|
+
/** True when an LLM error is about the *model itself* (not auth/network) — e.g.
|
|
74
|
+
* a model the current ChatGPT-account auth isn't allowed to use. The fix is to
|
|
75
|
+
* switch models, so callers should re-pick rather than retry or re-auth. */
|
|
76
|
+
export function isModelUnavailable(msg: string): boolean {
|
|
77
|
+
const m = msg.toLowerCase()
|
|
78
|
+
return m.includes("not supported")
|
|
79
|
+
|| m.includes("model_not_found")
|
|
80
|
+
|| m.includes("does not have access")
|
|
81
|
+
|| m.includes("not available")
|
|
82
|
+
|| m.includes("unsupported model")
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Pick a kid-safe default model: prefer the small/standard tiers that work
|
|
86
|
+
* with ChatGPT-account auth; skip the `-pro` tiers (API-key only, rejected by
|
|
87
|
+
* the Codex/OAuth path). Returns null only if the server reports no models. */
|
|
88
|
+
export function pickDefaultModel(models: ModelChoice[]): ModelChoice | null {
|
|
89
|
+
const usable = models.filter((m) => !/-pro\b/i.test(m.id))
|
|
90
|
+
const prefer = ["gpt-5.4-mini", "gpt-5.4", "claude-3-5-sonnet", "sonnet", "gpt-5.5"]
|
|
91
|
+
for (const p of prefer) {
|
|
92
|
+
const hit = usable.find((m) => m.id.toLowerCase().includes(p))
|
|
93
|
+
if (hit) return hit
|
|
94
|
+
}
|
|
95
|
+
return usable[0] ?? models[0] ?? null
|
|
96
|
+
}
|
package/src/index.tsx
CHANGED
|
@@ -31,7 +31,7 @@ import { listInstalledPacks, resolveContext } from "./core/course-pack.ts"
|
|
|
31
31
|
import { readLastSession, writeLastSession } from "./core/last-session.ts"
|
|
32
32
|
import { isCompletionTrigger, runCheck } from "./core/check-runner.ts"
|
|
33
33
|
import { parseSlash, matchCommand } from "./core/commands.ts"
|
|
34
|
-
import { listModels } from "./core/models.ts"
|
|
34
|
+
import { listModels, isModelUnavailable, pickDefaultModel } from "./core/models.ts"
|
|
35
35
|
import { findFiles } from "./core/files.ts"
|
|
36
36
|
import { App } from "./render/ink/App.tsx"
|
|
37
37
|
import { FREE_PLAY_PACK_ID } from "./render/ink/screens/CoursePackPicker.tsx"
|
|
@@ -652,10 +652,32 @@ function makeFullHandlers(
|
|
|
652
652
|
|
|
653
653
|
store.update({ thinking: true })
|
|
654
654
|
updateLastSession()
|
|
655
|
+
// Resolve a usable model up front. With no explicit pick, the server
|
|
656
|
+
// falls back to whatever model it last used — which may be one the
|
|
657
|
+
// current auth can't use (a ChatGPT-account login can't use gpt-5.5-pro).
|
|
658
|
+
// Pinning a known-good default makes the kid's first message just work.
|
|
659
|
+
let model = snap.selectedModel
|
|
660
|
+
if (!model) {
|
|
661
|
+
const def = pickDefaultModel(await listModels(client))
|
|
662
|
+
if (def) {
|
|
663
|
+
model = def.id
|
|
664
|
+
store.update({ selectedModel: def.id, selectedModelLabel: def.label })
|
|
665
|
+
}
|
|
666
|
+
}
|
|
655
667
|
try {
|
|
656
|
-
await session.prompt(text, { model:
|
|
668
|
+
await session.prompt(text, { model: model ?? undefined })
|
|
657
669
|
} catch (err) {
|
|
658
670
|
const detail = errMessage(err)
|
|
671
|
+
if (isModelUnavailable(detail)) {
|
|
672
|
+
// The model isn't usable on this account (e.g. a -pro model under a
|
|
673
|
+
// ChatGPT login). Clear it so the next message auto-picks a good
|
|
674
|
+
// default, and guide the kid to /model instead of a scary error.
|
|
675
|
+
store.update({ thinking: false, selectedModel: null, selectedModelLabel: null })
|
|
676
|
+
sysMessage(env.locale === "zh-Hans"
|
|
677
|
+
? "这个 AI 模型在你的账号下用不了。直接再发一条消息会自动换成可用模型,或打 /model 自己选(推荐 gpt-5.4-mini)。"
|
|
678
|
+
: "That AI model isn't available on your account. Just send again to auto-switch to a usable one, or type /model to choose (try gpt-5.4-mini).")
|
|
679
|
+
return
|
|
680
|
+
}
|
|
659
681
|
store.update({ thinking: false, screen: { kind: "error", variant: classifyLlmError(detail), detail } })
|
|
660
682
|
}
|
|
661
683
|
},
|