Conversation
…8957) - Harden parse in util/model.ts against non-string, undefined, and empty inputs to avoid crashes (U.split is not a function). - Parse #variant syntax from model identifiers so modelID remains clean and catalog lookups succeed. - Validate configured model documents in context/local.tsx so malformed or incomplete entries cleanly return undefined rather than leaking { modelID: undefined }. - Thread variant through fallbackModel and preferredSelection while keeping in-session user variant switches authoritative. - Guard switchLabel against incomplete model descriptors. - Add unit and regression tests for model parsing and local selection. Closes anomalyco#48957
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
holny
left a comment
There was a problem hiding this comment.
Ran the two test files against head locally (18 pass), and the malformed-config hardening is a real fix — configured.model as a number or null previously threw on property access in configuredModel. Three things worth a look:
-
CLI
#variantloses to the stored per-model variant preference. In preferredSelection the chain ispreferences.variant[key] ?? model.variant ?? ..., so with a saved variant for that model,--model provider/second#highis silently ignored. On top of that, the newlocal.model.variant.set(variant)in app.tsx onMount writes the CLI variant into the preference store, so one launch with#highbecomes the default for later launches that pass no flag. Mirroring recents might be intended, but taken together thepreferred ?? model.variantordering is mostly unexercised and there's no test covering stored-preference + CLI-variant together — worth deciding explicitly which one wins. -
That
variant.setcall is a silent no-op unlesscurrentSelection()resolves at mount (it early-returns without a selection, which needs the models list loaded). The variant still reaches the selection later via fallbackModel → preferredSelection, but the preference/draft side effects get skipped depending on timing. Deriving persistence from the loaded selection pipeline instead of an imperative set at mount might be more predictable. -
util/model.ts parse now reimplements the
provider/model#variantsplit, but packages/schema already ships the canonical parser (Model.Ref.parse, used by the CLI via parseSessionTargetModel) with stricter rules — it rejects an empty variant and#inside provider/variant, while the TUI parse accepts both (provider/mo#de#lparses to variantde#l). Benign today since preferredSelection normalizes unknown variants away, but two parsers for one syntax will drift; tui already depends on @opencode/schema, so reusing Ref.parse with a soft fallback may end up less code.
Minor: preferredSelection's new parameter type is the same shape as the existing ModelSelection alias.
|
All four addressed.
Minor: Verified: target files 20 pass, |
Issue for this PR
Closes #48957
Type of change
What does this PR do?
parseinutil/model.tsagainst non-string, undefined, and empty inputs to prevent startup crashes (U.split is not a function).#variantsyntax inutil/model.tsso model identifiers in the formatprovider/model#variantcleanly separatemodelIDfromvariant, ensuring downstream catalog lookups succeed.context/local.tsxso malformed or incomplete config entries returnundefinedinstead of leaking{ modelID: undefined }.fallbackModelintopreferredSelectionwhile keeping in-session user variant switches authoritative.switchLabelagainst incomplete model descriptors.parseedge cases and regression tests inlocal-selection.test.tsx.How did you verify your code works?
bun test test/util/model.test.ts test/context/local-selection.test.tsxfrompackages/tui: 18 passed (51 assertions).bun testfrompackages/tui: 1335 passed (0 failed).bun run typecheckfrom repository root: passed across all 41 packages.oxlint: 0 errors, 0 warnings.Checklist