resolveModelForDelegateTask returned input.userModel immediately without
availability checking when it was set, silently ignoring the user's
configured fallback_models. Effect: a team-mode category member
(e.g. hyperplan's 'artistry' category) configured with
{ model: "opencode/gemini-3.1-pro", fallback_models: [...] }
spawned with the unreachable primary even though a listed fallback was
reachable, leading to a dropped/broken team member instead of graceful
degradation.
Now, when the provider-models cache is warm AND userFallbackModels is
non-empty, the function verifies userModel is reachable via
fuzzyMatchModel. If not, it iterates userFallbackModels and promotes the
first reachable entry. Cold-cache (first-run) behavior is preserved -
the userModel is returned as-is when availability data is unavailable,
matching the existing 'trust the user' contract covered by the
pre-cache test fixtures.
Adds three regression tests covering: (1) unreachable primary + reachable
fallback -> fallback promoted, (2) reachable primary + reachable fallback
-> primary kept (fast path), (3) unreachable primary + unreachable
fallback -> legacy trust-user behavior preserved.
Move parseModelString into src/shared so callers can depend on a neutral module instead of reaching into delegate-task internals.
Cross-tool coupling violates module boundaries, and this keeps call-omo-agent plus runtime-fallback from importing through a sibling tool.
model-selection.ts now separates model selection from variant/reasoning
tier, so agent model overrides don't lock the variant.
34 tests pass, 5009 total, tsc clean.
Closes#3163
Background subagents (explore/librarian) failed with auth errors because
resolveModelForDelegateTask() always picked the first fallback entry when
availableModels was empty — often an unauthenticated provider like xai
or opencode-go.
Fix: when connectedProvidersCache is populated, iterate fallback chain
and pick the first entry whose provider is in the connected set.
Legacy behavior preserved when cache is null (not yet populated).
- model-selection.ts: use readConnectedProvidersCache to filter fallback chain
- test: 4 new tests for connected-provider-aware resolution
Add support for object-style entries in fallback_models arrays, enabling
per-model configuration of variant, reasoningEffort, temperature, top_p,
maxTokens, and thinking settings.
- Zod schema for FallbackModelObject with full validation
- normalizeFallbackModels() and flattenToFallbackModelStrings() utilities
- Provider-agnostic model resolution pipeline with fallback chain
- Session prompt params state management
- Fallback chain construction with prefix-match lookup
- Integration across delegate-task, background-agent, and plugin layers
Before provider cache exists (first run), resolveModelForDelegateTask now
returns undefined instead of guessing a model. This lets OpenCode use its
system default model when no model is specified in the prompt body.
User-specified model overrides still take priority regardless of cache state.
- Update DEFAULT_CATEGORIES to use 'openai/gpt-5.4-high' directly instead of separate model + variant
- Add helper functions (isExplicitHighModel, getExplicitHighBaseModel) to preserve explicit high models during fuzzy matching
- Update category resolver to avoid collapsing explicit high models to base model + variant pair
- Update tests to verify explicit high model handling in both background and sync modes
- Update documentation examples to reflect new configuration
🤖 Generated with OhMyOpenCode assistance
transformModelForProvider only handled github-copilot provider, leaving
google provider models untransformed. This caused ProviderModelNotFoundError
when google/gemini-3-flash was sent to the API (correct ID is
gemini-3-flash-preview).
Changes:
- Add google provider to transformModelForProvider with idempotent regex
negative lookahead to prevent double -preview suffix
- Fix category-default path in model-resolution-pipeline when
availableModels is empty but connected provider exists
- Fix getFirstFallbackModel first-run path that constructed raw model IDs
without transformation
- Fix github-copilot provider gemini transforms to also use idempotent
regex (was vulnerable to double-transform)
- Extract transformModelForProvider to shared module (single source of
truth, imported by cli and shared layers)
- Add 20 new test cases: unit tests for both providers, runtime
integration tests for category-default and fallback-chain paths,
double-transform prevention for both providers
- Create available-models.ts for model availability checking
- Create model-selection.ts for category-to-model resolution logic
- Update category-resolver, subagent-resolver, and sync modules to import
from new focused modules instead of monolithic sources