Repository navigation
Description
- I have verified this feature I'm about to request hasn't been suggested before.
Feature: Dynamic Model Routing via Plugin Hook - Update Active Model State
What it is
A plugin hook that allows plugins to dynamically change the active model for the entire OpenCode session. When a plugin overrides the model, it should update OpenCode's active model state, so that all subsequent agents and tasks using inherited model (no explicit model pin) will use the new model.
Current behavior
- Subagents launched via Task tool use the global config model, not the parent's active model ([Bug] Subagent spawned via Task tool uses global config model instead of inheriting parent session's active model #17870, Subtask commands do not inherit model #6928)
- Model selection is static and determined at session start
- The model picker shows one model, but subagents use a different one
- Plugins cannot affect the active model shown in the UI
Proposed behavior
- Plugins can intercept and override the model selection via the
chat.messagehook - When a plugin changes the model, it updates OpenCode's active model state
- The UI/model picker reflects this change
- All subsequent agents/tasks that use inherited model will use this new active model
- The routed model persists through the user message → assistant → subagent chain
- Subagent model inheritance ([Bug] Subagent spawned via Task tool uses global config model instead of inheriting parent session's active model #17870) works correctly
Key distinction from current chat.message behavior
- Current: Plugin can mutate
output.message.model, but it only affects the user message and subsequent assistant/subagent for that turn - Proposed: Plugin can change the active model, affecting all future turns and the UI state
Benefits
- True active model inheritance - Fixes [Bug] Subagent spawned via Task tool uses global config model instead of inheriting parent session's active model #17870 where subagents ignore parent's active model
- UI consistency - Model picker always reflects the actual model being used
- Dynamic routing - Route tasks to appropriate models based on content/complexity
- Cost optimization - Use cheaper models for simple tasks
- Failover - Automatically switch providers on rate limits or errors
- A/B testing - Blind model evaluation with proper UI feedback ([FEATURE]: chat.model hook for blind LLM benchmarking feat(plugin) #16932)
Current Status
Partially implemented. The existing chat.message hook allows plugins to mutate output.message.model, but this only affects the current turn's chain (user → assistant → subagent). It does NOT update OpenCode's active model state or UI, and future turns revert to the old model.
Test coverage exists at packages/opencode/test/session/chat-message-model-routing.test.ts.
This is insufficient because:
- Turn N+1 reverts to the old model
- Subagents in future turns still ignore the parent's active model ([Bug] Subagent spawned via Task tool uses global config model instead of inheriting parent session's active model #17870)
- Model picker shows a model that doesn't match actual behavior
This request is to:
- When a plugin changes the model, update OpenCode's active model state
- Update the UI/model picker to reflect the new active model
- Ensure all future turns use the new active model
- Subagents inherit correctly across all turns
Problem
Describe the problem or limitation that this feature would solve.
Related issues:
- [Bug] Subagent spawned via Task tool uses global config model instead of inheriting parent session's active model #17870 - Subagent spawned via Task tool uses global config model instead of inheriting parent session's active model
- Subtask commands do not inherit model #6928 - Subtask commands do not inherit model
- [FEATURE]: chat.model hook for blind LLM benchmarking feat(plugin) #16932 - chat.model hook for blind LLM benchmarking (proposes a hook but for a different use case)
- Plugins using noReply seem to cause model to switch to agent default #4475 - Plugins using noReply cause model to switch to agent default
Proposed Solution
Extend the chat.message hook (or add a dedicated chat.model hook) to update OpenCode's active model state when a plugin changes the model.
"chat.model"?: (
input: {
sessionID: string
agent: string
proposedModel?: { providerID: string; modelID: string }
},
output: {
model?: { providerID: string; modelID: string }
}
) => Promise<void>Hook semantics:
- Fires when a plugin wants to change the active model
- When
output.modelis set, OpenCode updates its active model state - The UI/model picker reflects the new active model
- All subsequent turns use the new active model by default
- Subagents launched via Task tool inherit the new active model (unless pinned)
Resolution order:
- Plugin
chat.model/chat.messagemodel mutation (updates active model) - Explicit
agent.modelpin in agent configuration - Parent assistant model (subagent inheritance)
- Default model resolution
Effect on active model:
- When a plugin returns a different model, OpenCode should update the active model
- This affects future turns until another model change occurs
- Subagents without explicit
modelinherit the active model - The model picker UI should display the active model
Use Cases
-
Dynamic model routing based on prompt content
- Route coding tasks to specialized code models
- Route debugging tasks to models with better reasoning
- Route simple queries to faster/cheaper models
-
Model fallback/failover
- Detect rate limits or errors and switch to backup provider
- Handle provider-specific outages gracefully
-
A/B testing and benchmarking
- Randomly assign models for blind evaluation (original [FEATURE]: chat.model hook for blind LLM benchmarking feat(plugin) #16932 use case)
- Track performance metrics across different models
-
Enterprise model governance
- Enforce policy-based model selection (e.g., cost limits, compliance)
- Audit trail of model selection decisions
-
Context-aware routing
- Analyze file types, project size, or task complexity
- Select appropriate model tier dynamically
Implementation Notes
What exists:
chat.messagehook allows plugins to mutateoutput.message.model- Model inheritance chain works (user → assistant → subagent)
- Test coverage at
packages/opencode/test/session/chat-message-model-routing.test.ts
What needs to be added:
- Update OpenCode's active model state when plugin changes model
- Update the UI/model picker to reflect the new active model
- Ensure future turns use the new active model by default
Key change needed:
In prompt.ts where chat.message is triggered, when output.message.model is mutated, also update the session's active model. This should propagate to the UI so the model picker reflects the change.
Files involved:
packages/plugin/src/index.ts- Hook type definitionpackages/opencode/src/session/prompt.ts- Update active model when hook mutates modelpackages/opencode/src/tool/task.ts- Already respects parent assistant model (lines 108-111)- UI components - Display active model from session state
Complementary Approaches
- Add model parameter to Task tool
- PR feat: add model parameter to Task tool for dynamic subagent model selection #14961 already implements explicit model passing
- Requires caller to know which model to use
- Doesn't update UI or active model state
- Useful for intentional model selection, not dynamic routing
Questions for Discussion
- Hook timing: Should the hook fire before or after the user message is persisted?
- UI update: How should the model picker UI respond when a plugin changes the active model? (animate? flash? silent update?)
- Validation: How should model validation failures be handled when updating active model?
- Override mechanism: Should users be able to opt out of plugin model changes? (e.g., config flag)
- Dedicated hook vs extend existing: Should we add a dedicated
chat.modelhook or extendchat.message? - Persistence: Should the active model change persist across session restarts?
Activity
github-actions commentedon Mar 22, 2026
This issue doesn't fully meet our contributing guidelines.
What needs to be fixed:
Please edit this issue to address the above within 2 hours, or it will be automatically closed.
This issue might be a duplicate of existing issues. Please check:
chat.modelplugin hook for model override — the current issue explicitly references it as a related issue but frames this as a distinct extensionIf you believe this was flagged incorrectly, please let a maintainer know.
github-actions commentedon Mar 22, 2026
This issue has been automatically closed because it was not updated to meet our contributing guidelines within the 2-hour window.
Feel free to open a new issue that follows our issue templates.
feat(plugin): add chat.model hook for dynamic model routing for token…
feat: add chat.model hook with reference token-optimizer plugin