Per-request smart reasoning routing for OpenCode agents, decided by Jev (TypeSafe SystemOne via OpenCode Zen).
The plugin hooks the agent-loop model call (ctx.session.hook("context")),
asks Jev how much reasoning the current task needs, and applies the matching
model variant via
event.options. No prompt rewriting, no text parsing, no hardcoded
provider lists.
Question design follows the Context7 TypeSafe docs: atomic choice +
noul questions evaluated in parallel, composed in code.
Local dev:
{ "plugins": [{ "package": "./path/to/opencode-jev", "options": {} }] }Uses your existing Zen key. Set one of:
OPENCODE_API_KEY(preferred, same key as/connect)JEV_API_KEY(override)
Optional: JEV_MODEL (default jev-1.13-free, paid: jev-1.13).
Three levels, most specific wins:
endpointplugin option — e.g. a self-hosted SystemOne gateway or mock.JEV_ENDPOINTenvironment variable.- Default
https://opencode.ai/zen/v1/systemone.
{ "plugins": [{ "package": "opencode-smart-reasoning", "options": {
"endpoint": "https://my-gateway.internal/systemone",
} }] }Note: config/option changes require restarting the opencode2 process.
The decision happens before the request, at prompt admission
(ctx.session.hook("prompt")), and is applied to that request in the
model-dispatch hook (ctx.session.hook("context")):
prompthook: asks Jev once per new user prompt, stashes the effort per session (retry-safe: replayed admissions of the same prompt reuse the stash instead of paying for Jev twice).contexthook: applies the stashed effort to the outgoing model call, so tool-loop continuations of the same prompt reuse it without another call. Only when no stash exists (synthetic messages, sessions predating the plugin) does it fall back to a one-off decision from the messages.
reasoning_effort(choice: minimal/low/medium/high/xhigh) — base level.high_stakes(noul) — >= 0.7 bumps one level (irreversible / production / security / payments / migration work).- Choice confidence <
minConfidence(0.35) falls back todefaultEffort. - Result is clamped to
maxEffort.
Fail-open: missing key, timeout (8s), or Jev error leaves model defaults untouched.
A user-pinned variant (agent variant in config, variant_cycle keybind,
--model with variant, switchModel) always beats Jev when
respectExplicitVariant is true (default):
- the
prompthook skips the Jev call when the session already pinsmodel.variant(no wasted cost or latency); - the
contexthook never sets options on a request carryingmodel.variant.
Set respectExplicitVariant: false to let Jev override even pinned
variants.
Fully data-driven — no hardcoded provider or model lists:
- At startup the plugin reads
ctx.model.list()and learns every model's real variant vocabulary (variants[{id, settings}]). - Jev's abstract effort (
minimal…xhigh) is clamped to the request model's own variant ids (nearest on a weakest→strongest scale, ties break upward). Variants are placed by id, or — for custom ids likefast— by the effort strings in their own settings;maxsits at the top, soxhighdecisions land on it where supported. - The resolved variant's own
settingspayload is spread into the request — exactly what selecting that variant would do, whatever keys that provider uses. - No catalog data (unknown model, settings-less or custom-only variants)? Options are left untouched and model defaults apply.
optionTemplates covers the gaps: per-provider/model option payloads with
an {effort} placeholder, e.g. { "deepseek/*": { "reasoningEffort": "{effort}" } }. Keys: exact provider/model, provider/*, */model,
* (most specific wins).
Auxiliary agents title/summary/compaction are excluded by default;
override with includeAgents/excludeAgents.
The ./tui entrypoint (auto-loaded) shows the decided effort in the prompt
footer, e.g. effort high/max. The server pushes each applied decision to
it over plugin RPC (getDecision/decided in src/rpc.ts); the footer
also pulls the current value on render, so reconnects lose nothing.
Restart the TUI to pick up either side after updating.
See JevReasoningOptions in src/index.ts. Full example in
opencode.jsonc.example.