An opencode plugin that implements the advisor pattern: a cheaper/faster executor model consults a stronger planner model mid-generation for strategic guidance, then continues the task informed by the advice.
This is a client-side reimplementation of Anthropic's advisor tool, which is server-side and Claude-only. This plugin works with any executor model opencode supports and lets you pick the advisor model from several providers.
- The plugin registers an
advisortool with the executor. - When the executor calls
advisor, optionally with a shortfocus, the plugin fetches the full conversation transcript via the opencode SDK, serializes it, and sends it to a configured advisor model (Claude, GPT, etc.). - The advisor's guidance returns to the executor as the tool result, and the executor continues.
The executor decides when to call the advisor (like any tool). Models not tuned for this pattern (e.g. GLM, Haiku) may need a nudge in your agent prompt or a mid-conversation reminder to call it.
This isn't the only opencode adaptation of the advisor pattern. A quick search will turn up several others, and some are better engineered than this one in real ways (per-session call budgets, secret redaction, actual test suites). Several of those gaps are acknowledged in Future work.
What this plugin optimizes for, in order:
- Fidelity to the documented pattern's behavioral core. The advisor is a single tool-less inference call that returns advice text only, capped at the docs' recommended 2048 output tokens. It is not a research subagent with its own tool loop. The full transcript is attached automatically; the executor's only job is a short
focus. - Anthropic's call-timing guidance, taken seriously. The docs' eval data suggests most of the pattern's gains come from when the executor calls the advisor: early, before the approach crystallizes, plus once before declaring done. It also suggests that "use sparingly / only when stuck" framing gives most of the value back.
AGENTS.mdships the docs' suggested timing prompt nearly word-for-word instead of a rewrite. - Freedom in choosing the advisor. Any Anthropic- or OpenAI-protocol model can advise, with reasoning effort passed through. Picking a stronger planner than your executor is the whole point of doing this client-side.
When I did a brief search online (July 2026), those were the strengths that set this one apart: the alternatives I found variously gave the advisor its own tool loop, told the executor to hold back on calling it, or fixed the advisor to a single provider. By the time you read this, someone may well have built one that's better even by these criteria. If you find it, use it (and tell me).
In an opencode session, ask your agent to integrate the plugin for you:
Integrate the opencode-advisor plugin from https://github.com/jcfrancisco/opencode-advisor into this project.
The agent will read the repo, create .opencode/plugins/advisor.ts, .opencode/package.json, and .opencode/advisor.config.json in your project, and tell you how to configure it. It should also append the executor guidance from AGENTS.md to your project's AGENTS.md (creating one if it doesn't exist) so the executor calls advisor at the right moments.
Copy .opencode/plugins/advisor.ts, .opencode/package.json, and .opencode/advisor.config.json from the repo into your project's .opencode/ directory. opencode auto-loads plugins from .opencode/plugins/ and runs bun install for package.json deps at startup.
Not on npm yet. Holding off for now — this file structure is easier to test locally, and installing via agent is seamless enough anyway.
Configuration lives in JSON, not environment variables. The plugin reads two files and merges them, with the second taking priority:
- Project default —
.opencode/advisor.config.json, shipped with the plugin and committed to your repo. - Personal override —
$XDG_CONFIG_HOME/opencode-advisor/config.json(defaults to~/.config/opencode-advisor/config.json), for a per-machine choice you don't want to commit (e.g. a provider only you have a key for).
Either file may set any subset of keys; unset keys fall through to the other file, then to built-in defaults. Example personal override:
{
"provider": "zen-anthropic",
"model": "claude-opus-4-8",
"effort": "xhigh"
}You still need an API key for your chosen provider — set it via environment variable, or sign in via opencode auth login (see API keys below).
Add the suggested text from AGENTS.md to your executor agent's prompt (the body of .opencode/agent/<name>.md, or the prompt field under agent in opencode.json) so the executor calls advisor at the right moments. If your project doesn't already have an AGENTS.md, the agent-assisted install below will create one.
Restart opencode.
.opencode/advisor.config.json (and the personal override) accept:
| Key | Default | Description |
|---|---|---|
provider |
anthropic |
Which provider/protocol to use for the advisor. See table below. |
model |
provider-specific | The advisor model ID. See table below. |
effort |
null (unset = provider default) |
Effort/reasoning level, passed through verbatim. See table below. |
maxTokens |
2048 |
Cap on the advisor's response length. |
provider |
Required env var |
|---|---|
anthropic |
ANTHROPIC_API_KEY |
openai |
OPENAI_API_KEY |
zen-anthropic, zen-openai |
ZEN_API_KEY |
If the env var isn't set, the plugin falls back to opencode's own ~/.local/share/opencode/auth.json (written when you sign in via opencode). API keys are never read from the config JSON.
provider |
API used | Key env var | Wire param for effort |
|---|---|---|---|
anthropic (default) |
Anthropic Messages | ANTHROPIC_API_KEY |
output_config.effort |
openai |
OpenAI Chat Completions | OPENAI_API_KEY |
reasoning_effort |
zen-anthropic |
Zen → Anthropic /v1/messages |
ZEN_API_KEY |
output_config.effort |
zen-openai |
Zen → OpenAI /v1/responses |
ZEN_API_KEY |
reasoning.effort |
Pick model from the column for your provider. Defaults are marked.
| Provider | Default | Example model IDs |
|---|---|---|
anthropic |
claude-sonnet-4-6 |
claude-opus-4-8, claude-opus-4-7, claude-sonnet-5, claude-sonnet-4-6, claude-haiku-4-5 |
openai |
gpt-5.5 |
gpt-5.5, gpt-5.5-pro, gpt-5.4, gpt-5.4-mini, gpt-5.3-codex |
zen-anthropic |
claude-sonnet-4-6 |
claude-fable-5, claude-opus-4-8, claude-opus-4-7, claude-opus-4-6, claude-opus-4-5, claude-sonnet-5, claude-sonnet-4-6, claude-sonnet-4-5, claude-haiku-4-5, qwen3.7-max, qwen3.7-plus, qwen3.6-plus, qwen3.5-plus |
zen-openai |
gpt-5.5 |
gpt-5.5, gpt-5.5-pro, gpt-5.4, gpt-5.4-mini, gpt-5.4-nano, gpt-5.3-codex, gpt-5.3-codex-spark, gpt-5.2, gpt-5.1, gpt-5, gpt-5-nano |
Zen's Gemini and OpenAI-compatible models (GLM, Kimi, DeepSeek, MiniMax, Grok) are not supported as advisors yet — only the Anthropic-protocol and OpenAI-protocol models above.
effort is passed through verbatim per provider — no mapping. Unset = omit the param (provider default). Invalid values for your model will be rejected by the API with a 400, which surfaces to the executor as a tool error.
| Provider | Param location | Valid levels (model-dependent) |
|---|---|---|
anthropic, zen-anthropic |
output_config.effort |
low, medium, high (default), xhigh, max |
openai |
reasoning_effort (top-level) |
none, minimal, low, medium, high, xhigh |
zen-openai |
reasoning.effort |
none, minimal, low, medium, high, xhigh |
Notes:
- Anthropic effort is supported on Fable 5, Mythos 5, Opus 4.8/4.7/4.6, Sonnet 5/4.6, and Opus 4.5 — not Haiku 4.5 or older.
highis equivalent to omitting it. - OpenAI effort levels are model-dependent ("some models support only a subset").
gpt-5.5defaults tomedium. - The vocabularies differ between providers (Anthropic has
maxwith no OpenAI equivalent; OpenAI hasnone/minimalwith no Anthropic equivalent), so values are not normalized — pick one valid for your provider/model.
This is a client-side approximation. Differences from Anthropic's server-side advisor tool:
- Transcript timing: the advisor sees messages up to the last completed one, not the in-progress partial of the current turn.
- No single-request illusion: each
advisorcall is a genuine extra round trip from your machine. - No advisor-side prompt caching across calls within a conversation.
- No
max_usescap, nousage.iterationscost breakdown, no redacted/encrypted result variants. - Timing is not tuned: the executor decides when to call the advisor. Models not trained for this (GLM, Haiku) may not call it reliably without a nudge — add language to your agent's prompt ("consult the advisor for non-obvious design decisions") or inject a mid-conversation reminder.
.opencode/
package.json # declares @opencode-ai/plugin, @anthropic-ai/sdk, openai deps
advisor.config.json # shipped default config (provider, model, effort, maxTokens)
plugins/
advisor.ts # the plugin: registers the `advisor` tool
Features from Anthropic's advisor tool docs that are reasonable to reproduce client-side but aren't implemented yet. Each notes the doc section it comes from and a starting point in this plugin's architecture.
-
Automate the mid-conversation nudge. Inject a reminder to consult the advisor if an under-calling executor (Haiku, GLM) hasn't called it by turn N. Today this is manual prose in
AGENTS.md. Docs: Mid-conversation nudge for under-calling executors (NUDGE_TURN/NUDGE_TEXT). Approach: count assistant turns and whetheradvisorfired via thechat.message/tool.execute.afterhooks, then append the nudge as a user message throughexperimental.chat.messages.transform. Gate on a config key (e.g.nudgeTurn). -
Call caps (
max_uses+ conversation-level). Cap how many times the advisor runs so a chatty executor can't rack up cost. Docs: Tool parameters (max_uses) and Cost control. Approach: a module-levelMap<sessionID, count>incremented inexecute; pastmaxUsesreturn a short "advisor limit reached" string instead of calling the model. -
Graceful error results. An advisor failure (network, bad
effort,prompt_too_long) currently throws raw. Anthropic's design is that the executor sees an error result and continues; the turn doesn't fail. Docs: Error results. Approach: wrap the provider call intry/catchand return"(advisor unavailable: …)"so the executor proceeds. -
Advisor usage/cost reporting. Surface advisor token counts so the extra spend is visible. Docs: Usage and billing (
usage.iterations). Approach: readresp.usagefrom each provider response and report it via the tool context'smetadata({ metadata: { … } }). -
Advisor-side prompt caching (stretch, Anthropic only). Cache the advisor's transcript prefix across calls in a conversation. Lower ROI here since each call is stateless and the payoff only appears at ~3+ calls per conversation. Docs: Advisor prompt caching. Approach: add a
cache_controlbreakpoint on the transcript block in the Anthropic/zen-anthropicbranch; OpenAI providers cache automatically with no knob.