-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
fix: respect OpenCode model resolution priority in bootstrap injection #294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: respect OpenCode model resolution priority in bootstrap injection #294
Conversation
Previously getPreferredModel() only read from ~/.local/state/opencode/model.json (last used model), ignoring user-configured agent and global models. Now getBootstrapTarget() follows OpenCode's documented priority: 1. default_agent's configured model (config.agent[name].model) 2. Agent model from markdown-defined agents (via client.app.agents()) 3. Global model (config.model) 4. Last used model (fallback) Also passes agent parameter to session.prompt() for proper context.
📝 WalkthroughWalkthroughThe pull request adds model and session state resolution capabilities to the bootstrap flow. It introduces helper functions to parse model references, read persisted model state, and resolve bootstrap targets from configuration or fallback sources, enabling the system to remember and reuse selected models across sessions. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @.opencode/plugin/superpowers.js:
- Around line 60-97: The top comment above getBootstrapTarget no longer matches
the actual resolution order—update it to reflect the current logic: first check
default_agent.model from config (config.agent[agentName].model parsed via
parseModelRef), then fallback to agents returned by client.app.agents
(markdown-defined agents) and parseModelRef on the matched agent, then fallback
to global config.model (parseModelRef), and finally use getLastUsedModel(); also
reflect the outer try/catch behavior that returns { agentName: 'build', model:
lastUsed } on errors.
| // Resolve the model the session is expected to use: | ||
| // default_agent.model (if set) -> global model -> last used. | ||
| const getBootstrapTarget = async () => { | ||
| const lastUsed = getLastUsedModel(); | ||
|
|
||
| try { | ||
| const configRes = await client.config.get(); | ||
| const config = configRes?.data ?? configRes; | ||
|
|
||
| const agentName = | ||
| typeof config?.default_agent === 'string' && config.default_agent.trim() | ||
| ? config.default_agent.trim() | ||
| : 'build'; | ||
|
|
||
| const agentModelFromConfig = parseModelRef(config?.agent?.[agentName]?.model); | ||
| if (agentModelFromConfig) return { agentName, model: agentModelFromConfig }; | ||
|
|
||
| // Agents defined via markdown files might not appear under config.agent. | ||
| try { | ||
| const agentsRes = await client.app.agents(); | ||
| const agents = agentsRes?.data ?? agentsRes; | ||
| if (Array.isArray(agents)) { | ||
| const agent = agents.find((a) => a?.id === agentName || a?.name === agentName); | ||
| const agentModelFromList = parseModelRef(agent?.model); | ||
| if (agentModelFromList) return { agentName, model: agentModelFromList }; | ||
| } | ||
| } catch { | ||
| // ignore; fall back to other sources | ||
| } | ||
|
|
||
| const globalModel = parseModelRef(config?.model); | ||
| if (globalModel) return { agentName, model: globalModel }; | ||
|
|
||
| return { agentName, model: lastUsed }; | ||
| } catch { | ||
| return { agentName: 'build', model: lastUsed }; | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update the resolution-order comment to match the code.
The inline comment omits the markdown-defined agent model check now present in the logic.
📝 Suggested comment fix
- // Resolve the model the session is expected to use:
- // default_agent.model (if set) -> global model -> last used.
+ // Resolve the model the session is expected to use:
+ // default_agent.model -> markdown-defined agent model -> global model -> last used.🤖 Prompt for AI Agents
In @.opencode/plugin/superpowers.js around lines 60 - 97, The top comment above
getBootstrapTarget no longer matches the actual resolution order—update it to
reflect the current logic: first check default_agent.model from config
(config.agent[agentName].model parsed via parseModelRef), then fallback to
agents returned by client.app.agents (markdown-defined agents) and parseModelRef
on the matched agent, then fallback to global config.model (parseModelRef), and
finally use getLastUsedModel(); also reflect the outer try/catch behavior that
returns { agentName: 'build', model: lastUsed } on errors.
Reproduction AttemptI tried to reproduce this issue on OpenCode v1.1.31 (macOS) with the superpowers plugin installed via the documented symlink method. Test Performedcd /tmp
opencode run -m opencode/gpt-5-nano "What skills do you have? Use find_skills tool."ResultThe model specified ( Logs showed: QuestionsTo help reproduce this:
The 81 lines of model resolution code is substantial - I'd like to confirm the issue exists before merging a fix that might not be necessary. |
|
Thank you for this contribution! However, this PR has been superseded by PR #330. Context: PR #330 completely rewrote the bootstrap injection mechanism. Instead of using 'experimental.chat.system.transform': async (_input, output) => {
const bootstrap = getBootstrapContent();
if (bootstrap) {
(output.system ||= []).push(bootstrap);
}
}This approach:
Since Closing as superseded by #330. |
|
Superseded by PR #330 |
Summary
Problem
The Superpowers plugin injects a bootstrap prompt via
session.prompt()without specifying amodeloragent. This means OpenCode falls back to its internal default, which may differ from the model the user actually selected or configured.Example of the issue
User selects GPT-5.2 Codex in the TUI:
But the bootstrap is processed by a different model (gemini-3-pro-preview):
The bootstrap injection used OpenCode's internal default instead of the user's selected model, causing the skill context to be processed by a completely different model than expected.
Solution
Added
getBootstrapTarget()that resolves the expected model following OpenCode's documented priority:config.agent[default_agent].model— Agent-specific model (highest priority)client.app.agents()for agents defined in.mdfilesconfig.model— Global model setting~/.local/state/opencode/model.jsonNow passes both
agentandmodelparameters tosession.prompt()for proper alignment.References