feat(models): fetch live model lists from CLIs, allow custom ids

Each agent definition now declares an optional `listModels` spec; the
daemon runs the CLI's own list-models command (e.g. `opencode models`,
`cursor-agent models`) during agent detection and uses the result as
the dropdown options. Hardcoded entries shrink to a `fallbackModels`
hint that only kicks in when the CLI has no listing command (Claude,
Codex, Gemini, Qwen) or when the listing fails (e.g. unauth'd
cursor-agent).

UI groups `provider/model` ids by provider via <optgroup> so opencode's
~175 live models stay navigable, and the Settings dialog gains a
"Custom…" entry that opens a free-text input for any model id the
listing didn't surface yet. Daemon validates picks against the live
cache + fallback, with a permissive sanitizer for custom ids.
This commit is contained in:
pftom
2026-04-29 00:32:03 +08:00
parent f2d28a1cca
commit bbeb868040
8 changed files with 331 additions and 58 deletions
+5 -1
View File
@@ -86,7 +86,10 @@ export const en: Dict = {
'settings.modelPicker': 'Model',
'settings.reasoningPicker': 'Reasoning effort',
'settings.modelPickerHint':
'Picked per CLI. "Default" leaves the choice to the CLIs own config.',
'Fetched from the CLI when it exposes a `models` command. "Default" leaves the choice to the CLIs own config; "Custom…" lets you type any model id the CLI accepts.',
'settings.modelCustom': 'Custom (type below)…',
'settings.modelCustomLabel': 'Custom model id',
'settings.modelCustomPlaceholder': 'e.g. anthropic/claude-sonnet-4-6',
'entry.tabDesigns': 'Designs',
'entry.tabExamples': 'Examples',
@@ -218,6 +221,7 @@ export const en: Dict = {
'avatar.modelSection': 'Model',
'avatar.modelLabel': 'Model',
'avatar.reasoningLabel': 'Reasoning',
'avatar.customSuffix': '(custom)',
'project.backToProjects': 'Back to projects',
'project.metaFreeform': 'freeform',