fix(model-resolution): ensure subagents use free models when no paid providers configured (fixes #1883)#3529
fix(model-resolution): ensure subagents use free models when no paid providers configured (fixes #1883)#3529MoerAI wants to merge 2 commits into
Conversation
…providers configured (fixes code-yeongyu#1883)
|
I have read the CLA Document and I hereby sign the CLA |
There was a problem hiding this comment.
2 issues found across 4 files
Confidence score: 3/5
- There is a concrete regression risk in
src/tools/delegate-task/model-selection.ts: deriving free-only mode from a potentially staleconnected-providers.jsoncan rewrite a valid paid resolution, which may cause incorrect model selection behavior for users. src/tools/delegate-task/free-model-fallback.tsappears to keep the filtered single-model chain (for examplegpt-5-nano) instead of the new stable free-only chain, so free-only resolution can still fail even when another free opencode model is available.- Given both issues are medium severity (6/10) with fairly high confidence (8/10) and affect runtime resolution paths, this looks like moderate merge risk rather than a safe-to-merge state.
- Pay close attention to
src/tools/delegate-task/model-selection.tsandsrc/tools/delegate-task/free-model-fallback.ts- free-only/paid resolution logic may select the wrong chain or fail fallback selection.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/tools/delegate-task/model-selection.ts">
<violation number="1" location="src/tools/delegate-task/model-selection.ts:37">
P2: Free-only mode is derived from the wrong cache source here. If `connected-providers.json` is stale but `availableModels` came from a newer provider-models cache, this can incorrectly rewrite a valid paid resolution to the free opencode fallback chain.</violation>
</file>
<file name="src/tools/delegate-task/free-model-fallback.ts">
<violation number="1" location="src/tools/delegate-task/free-model-fallback.ts:60">
P2: Returning the filtered chain here keeps single-model free fallbacks like `gpt-5-nano` and skips the new stable free-only chain, so free-only resolution can still fail when another free opencode model is available.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
|
|
||
| const connectedProviders = input.availableModels.size === 0 ? readConnectedProvidersCache() : null | ||
| const connectedProviders = readConnectedProvidersCache() | ||
| const freeOnlyProviderConfiguration = isFreeOnlyProviderConfiguration(connectedProviders) |
There was a problem hiding this comment.
P2: Free-only mode is derived from the wrong cache source here. If connected-providers.json is stale but availableModels came from a newer provider-models cache, this can incorrectly rewrite a valid paid resolution to the free opencode fallback chain.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tools/delegate-task/model-selection.ts, line 37:
<comment>Free-only mode is derived from the wrong cache source here. If `connected-providers.json` is stale but `availableModels` came from a newer provider-models cache, this can incorrectly rewrite a valid paid resolution to the free opencode fallback chain.</comment>
<file context>
@@ -63,15 +33,18 @@ export function resolveModelForDelegateTask(input: {
- const connectedProviders = input.availableModels.size === 0 ? readConnectedProvidersCache() : null
+ const connectedProviders = readConnectedProvidersCache()
+ const freeOnlyProviderConfiguration = isFreeOnlyProviderConfiguration(connectedProviders)
- // Before provider cache is created (first run), skip model resolution entirely.
</file context>
| const freeOnlyProviderConfiguration = isFreeOnlyProviderConfiguration(connectedProviders) | |
| const providersForFreeOnlyDetection = input.availableModels.size > 0 | |
| ? [...new Set([...input.availableModels].map((model) => model.split("/")[0]))] | |
| : connectedProviders | |
| const freeOnlyProviderConfiguration = isFreeOnlyProviderConfiguration(providersForFreeOnlyDetection) |
| entry.providers.includes("opencode") && isKnownFreeModel(entry.model), | ||
| ) | ||
|
|
||
| return freeEntries.length > 0 ? freeEntries : FREE_ONLY_FALLBACK_CHAIN |
There was a problem hiding this comment.
P2: Returning the filtered chain here keeps single-model free fallbacks like gpt-5-nano and skips the new stable free-only chain, so free-only resolution can still fail when another free opencode model is available.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/tools/delegate-task/free-model-fallback.ts, line 60:
<comment>Returning the filtered chain here keeps single-model free fallbacks like `gpt-5-nano` and skips the new stable free-only chain, so free-only resolution can still fail when another free opencode model is available.</comment>
<file context>
@@ -0,0 +1,61 @@
+ entry.providers.includes("opencode") && isKnownFreeModel(entry.model),
+ )
+
+ return freeEntries.length > 0 ? freeEntries : FREE_ONLY_FALLBACK_CHAIN
+}
</file context>
|
hi - thanks for taking this on! immediate thing: the new so the new hardcoded list is shipping the same shape of artifact that broke the original issue. separate thing: +1 on both of cubic's findings. bigger picture - hardcoded model names anywhere in source go stale on opencode's rotation cadence. wommy's jq from the original thread gives the current canonical set: two of those ( |
…fallback chain Per code-yeongyu#3529 (comment) review and the live opencode catalog (https://opencode.ai/zen/v1/models, https://models.dev/api.json), kimi-k2.5-free is now marked deprecated and no longer resolves. Shipping it in FREE_ONLY_FALLBACK_CHAIN reproduces the same shape of artifact that triggered code-yeongyu#1883. Replaces it with the currently live free opencode models that are non-deprecated and have cost.input == 0: hy3-preview-free, nemotron-3-super-free (plus the existing big-pickle, minimax-m2.5-free, gpt-5-nano). Keeps the chain ordered by capability (multimodal -> general). Adds a regression test that asserts FREE_ONLY_FALLBACK_CHAIN never contains models in a known-deprecated list and that every entry passes isKnownFreeModel, so future drift fails CI instead of silently shipping a stale model.
|
@mrosnerr thanks for the careful read. Concern 1 — Confirmed against both
After fix the chain is: Concern 2 — You're right that paid Zen subscribers also see
Happy to take that follow-up if @code-yeongyu agrees with the direction. Want me to open a tracking issue? Also +1 on cubic's findings — the current heuristic is doing two jobs (gating + chain construction) that should be separated. |
|
Opened a PR against this branch with some test additions and a simplified approach to the free-only detection: MoerAI#1 The main concern we found: Our approach replaces the category-default gating and chain replacement with an append strategy — free models get added as a tail to the existing chain, and on warm cache, free-only is derived from Feel free to merge it in, cherry-pick what's useful, or just use it as context for the edge cases — whatever works best for getting the right behavior out. |
|
Closing — @mrosnerr identified a real bug in my approach: Their approach is the right one — I'd rather they take this forward as their own PR against upstream/dev so the credit goes to them. Their PR description is also a much better explanation of the edge cases than mine. @mrosnerr if you want to open a PR against Thanks for the deep review @mrosnerr. |
Summary
opencode/*models when only free providers are configuredProblem
Subagent model resolution could still select paid
opencode/*entries from hardcoded category and agent fallback chains when the user had no paid providers configured. This let child agents resolve to models likegpt-5.4instead of staying on free models, especially when the connected-provider cache only exposedopencode.Fix
Free-only provider setups now strip paid defaults out of delegate-task category and fallback resolution and replace them with a stable free-model chain. The parsing helpers were extracted into focused modules, and targeted regression tests now cover both cached and cold-cache free-only resolution paths.
Changes
src/tools/delegate-task/model-selection.tssrc/tools/delegate-task/free-model-fallback.tssrc/tools/delegate-task/model-selection-input.tssrc/tools/delegate-task/model-selection.free-model-fallback.test.tsFixes #1883
Summary by cubic
Keeps delegate-task subagents on free
opencode/*models when only free providers are connected by stripping paid defaults/fallbacks and using an updated, non-deprecated free chain.Bug Fixes
big-pickle→minimax-m2.5-free→hy3-preview-free→nemotron-3-super-free→gpt-5-nano.Refactors
src/tools/delegate-task/free-model-fallback.ts.src/tools/delegate-task/model-selection-input.ts.Written for commit 49a1540. Summary will update on new commits. Review in cubic