Every agent's model assignment now lives in a single-line text file at
.opencode/models/<name>.txt; default.txt is the 27-agent catch-all.
opencode.json's agent.<name>.model uses
{file:./.opencode/models/<name>.txt} interpolation, and
tools/_opencode_worker.py reads the same files at session-create to
stamp the resolved model on the session record (observability + drift
sentinel; OpenCode does NOT propagate session-level model to
prompt_async — schema for that is undocumented and deferred to Stage
2). 39 .md frontmatter `model:` lines stripped; the two intentional
inheritors (task-implementor, agent-evolution-pool-supervisor) keep
their model-less frontmatter.
Operator workflow for swapping a model is now: edit
.opencode/models/<role>.txt, restart OpenCode so opencode.json's
{file:...} re-resolves, run the dispatcher. Live-swap without restart
was attempted (override on prompt_async); OpenCode 0.x silently
dropped those requests (200 OK, no assistant message) and the
prompt_async override was reverted. The session-create override
remains for observability + drift detection.
End-to-end validation (2026-05-10): dispatch_review.py on PR #25 with
default.txt=openai/gpt-5-mini produced a clean REQUEST_CHANGES review
in 26 s for ~$0.016; dispatch_implementer.py on PR #30 with
tier-qwen-* files remapped to openai/{gpt-5-nano, gpt-5-mini,
gpt-5-codex} ran the full implementation-worker → tier-dispatcher →
estimator-implementation → tier-qwen-med → task-implementor →
git-isolator-util chain in 16 min with model=gpt-5-mini end-to-end.
Also documents the printenv VAR form as the only allowed env-read in
implementation-worker.md and task-implementor.md (live testing
showed the worker burning 2–4 turns on permission-denied
trial-and-error trying printf and echo variants).
Tests: 21 in tests/auto_agents/test_opencode_worker_models.py
(resolver semantics with caplog assertions on every malformed-input
path; session-create body shape; prompt_async body never carries
model; three repo-level invariants — every {file:...} reference
resolves, every .md is wired or in the inheritor allowlist, no .md
has a model: frontmatter). 1027 auto_agents tests pass / 3 skipped.
Note: .opencode/models/default.txt and the three tier-qwen-*.txt
files are committed with their OpenAI swaps in place (gpt-5-mini,
gpt-5-nano, gpt-5-mini, gpt-5-codex respectively) because the
CleverThis HuggingFace endpoints are paused. Revert with `git diff
HEAD~1 -- .opencode/models/*.txt | git apply -R` if/when they come
back.
Co-authored-by: Cursor <cursoragent@cursor.com>
.opencode/models/ — Model registry
Each <name>.txt file in this directory contains exactly one model id
(e.g. anthropic/claude-haiku-4-5), with no surrounding whitespace except a
single trailing newline. These files are the single source of truth for which
model each agent uses.
How agents pick their model
Two paths read from this directory; both honour the same convention.
Runtime (dispatcher path)
tools/_opencode_worker.py resolves a model for an agent by:
- Looking for
.opencode/models/<agent-name>.txt. - If absent, falling back to
.opencode/models/default.txt. - Parsing the single line as
providerID/modelIDand passing it as themodelfield onPOST /sessionandPOST /session/{id}/prompt_async.
This is the path used by dispatch_review.py, dispatch_implementer.py,
conflict_drive.py, and any other code that calls
_opencode_worker.run_session_blocking. Changes to a .txt file take effect
on the next dispatched session — no OpenCode restart needed.
Static (OpenCode config path)
opencode.json declares an agent.<name>.model for every agent that has an
explicit assignment, using {file:./.opencode/models/<name>.txt} so OpenCode
reads the same file at config-load time. This covers interactive sessions and
any Task-tool subagent invocations that bypass the dispatcher API override.
OpenCode caches its agent registry at process start, so static-path changes
require a server restart (pkill -f 'opencode serve' then re-launch).
How to swap a model
# Switch the worker fleet from Qwen to Sonnet for the next dispatch
echo "anthropic/claude-sonnet-4-6" > .opencode/models/default.txt
python3 tools/dispatch_implementer.py --once
The dispatcher reads .opencode/models/default.txt fresh on every
POST /session, so the next cycle picks up the change immediately. No
restart, no /config/reload, no env-var juggling. Interactive sessions
spawned through OpenCode directly will keep the previous model until the
server is restarted.
Inventory
| Role file | Agents that resolve to it |
|---|---|
default.txt |
All agents that don't have a name-matched file (27 agents today: pr-review-worker, implementation-worker, auto-agents, supervisor, every git-*-util, every session-health-*-util, work-group-util, async-agent-util, tier-dispatcher, estimator-implementation, conflict-resolver-worker, pr-merge-worker, pr-merge-supervisor) |
tier-qwen-small.txt |
tier-qwen-small |
tier-qwen-med.txt |
tier-qwen-med |
tier-qwen-large.txt |
tier-qwen-large |
tier-kimi.txt |
tier-kimi |
tier-haiku.txt |
tier-haiku |
tier-sonnet.txt |
tier-sonnet |
tier-opus.txt |
tier-opus |
tier-codex.txt |
tier-codex |
tier-gpt5-mini.txt |
tier-gpt5-mini |
tier-gpt5-nano.txt |
tier-gpt5-nano |
tier-o4-mini.txt |
tier-o4-mini |
ca-test-infra-improver.txt |
ca-test-infra-improver |
Agents that deliberately inherit from their caller (no model: line, no
override file): task-implementor, agent-evolution-pool-supervisor.
File format
- Exactly one line per file (the model id).
- Trailing newline is stripped by the resolver and by OpenCode's
{file:...}interpolation. Do not put anything else in these files (no comments, no blank lines, no extra whitespace). - The model id must be valid
providerID/modelIDsyntax (e.g.CleverThis-15/Qwen3-6-35B-A3B-GGUF-UD-Q3-K-XL,anthropic/claude-sonnet-4-6). The provider id must already be declared inopencode.json'sproviderblock.
See also
docs/development/models.md— full design rationale and the planned Stage 2 fallback layer.opencode.json—agent.<name>.modelblock that references these files.tools/_opencode_worker.py— runtime resolver:_resolve_role_model(agent_name).