opencode is a first-class docket harness. An install generates two opencode artifacts:
.opencode/agents/docket-*.md— the agent definitions: markdown with YAML frontmatter, one per docket agent. The filename is the agent identifier, so noname:field is written. These are machine-local: gitignored, regenerated per machine, never committed (they bake resolved model IDs — ADR-0020).agents/harness-defaults.ymlships a complete sixteen-agentopencode:block, so all sixteen definitions are generated pinned with no configuration at all. Any field is overridable per agent from any config layer — your value wins over the shipped one.- A
docketdispatch block inAGENTS.md— the same marker-bounded repo-root block, shared with Codex: opencode reads the same committed project-rootAGENTS.md, so one managed block serves both. A repo targeting either harness gets it; a repo targeting both gets it exactly once. It is committed and machine-neutral (ADR-0036).
docket's skills themselves need no extra step: opencode resolves skills out of ~/.agents/skills/,
which the install already populates.
An install writes in two passes, and opencode lives in both:
| Pass | Writes | Governed by |
|---|---|---|
| User-level | ~/.opencode/agents/docket-*.md |
global agent_harnesses: in ~/.config/docket/config.yml |
| Per-repo | <repo>/.opencode/agents/docket-*.md and the AGENTS.md dispatch block |
the repo's own agent_harnesses: in .docket.yml or .docket.local.yml |
The gotcha: a global agent_harnesses does NOT generate per-repo opencode artifacts. Setting
agent_harnesses: [claude, opencode] in ~/.config/docket/config.yml writes ~/.opencode/agents/…
but produces nothing inside a repo. To get the per-repo artifacts, the repo must opt in, in
either its committed .docket.yml or its machine-local .docket.local.yml:
agent_harnesses: [claude, opencode]The first of local-then-committed that declares the key wins the list outright. Re-run install.sh
after editing any config layer — it reconciles opencode's definitions and the committed AGENTS.md
dispatch block for you in one journaled transaction (change 0351). The three opt-in states (absent /
non-empty / explicit empty) and the shared-block removal rule are exactly as described for
Codex; because the block is shared, it is removed only when the last
AGENTS.md-dispatch harness is de-listed, and your own AGENTS.md content outside the docket
markers is preserved untouched.
The generated definitions carry the model/effort resolved from the layered agents: config over
docket's shipped opencode: block, which overrides the shipped pin. Models are reached through
OpenRouter, so authenticate that provider first (opencode auth login). OpenRouter model IDs are
double-prefixed — openrouter/<vendor>/<model>, e.g.
openrouter/deepseek/deepseek-v4-flash-0731. opencode splits that into a provider id (openrouter)
and a model id itself; docket passes the whole string through untouched and validates nothing
(ADR-0015). Use exactly the IDs opencode reports:
opencode models openrouterEffort is a provider model option, not a first-class opencode field. opencode has no
reasoning-effort field of its own; it forwards unrecognized agent-frontmatter keys to the provider as
model options. Docket therefore emits effort as reasoningEffort:, and it arrives as a real
per-agent reasoning effort. Two consequences: it only applies when a model resolves (docket drops the
effort with a warning when the model is unset or is the model: inherit sentinel, which has no
opencode equivalent), and the vocabulary is model-specific — high is the ceiling docket ships; if
your model rejects docket's token, pin effort explicitly alongside your model.
Everything above configures opencode as the harness hosting your session. Runner delegation is
the other direction — your session stays in Claude Code and individual docket agents are handed to
opencode, with its models and its bill, for their whole run. The config recipe and its full rules
live under Delegation; this is a different mechanism from the
hosting path here: hosting writes reasoningEffort: into .opencode/agents/docket-*.md, read by
opencode's own agent loader, while delegation bakes --variant into the shim's command line. Same
effort: key in your config, two different destinations.
The one grant delegation demands is runners.opencode.permissions: auto-approve. opencode has no
sandbox levels; it has a permission system that prompts before editing a file or running a command,
and --auto auto-approves everything not explicitly denied in opencode's own config (its own
help text marks it (dangerous!)). A delegated run cannot answer a prompt, so the knob has two
values and no useful third:
| Value | Effect |
|---|---|
ask (default) |
The adapter refuses to delegate, with a diagnostic naming this knob — turning a silent hang into a message. |
auto-approve |
Bakes --auto. A delegated build worker can then run any command in the repository unwatched, except what your opencode deny rules forbid. |
Nobody receives blanket auto-approval as a side effect of typing runner: opencode — the risk is
accepted at a visible line in config. Pair auto-approve with opencode's own deny rules: --auto
approves what is not explicitly denied, so the deny list — not the flag — is intended to be the
real boundary. Treat that as unverified: docket read it from a single line of opencode run --help
and has not tested the interaction, so confirm the deny-rule spelling and behavior against your own
opencode version before relying on it.
After opting a repo in and running the install:
.opencode/agents/docket-*.mdexist — sixteen of them.AGENTS.mdcontains the marker-boundeddocketdispatch block.- Ask opencode to resolve one definition and read back what it actually applied:
opencode debug agent docket-build-economyIt prints the fully resolved config; model split into providerID + modelID confirms the
double-prefixed ID parsed, and the effort appearing under options (not as a top-level field) is
exactly the passthrough described above. Compare the values against the opencode: block in
agents/harness-defaults.yml, the single source of truth — a field you set in a config layer wins
over the shipped value.
opencode loads its config once at process start and does not hot-reload. After an install writes new definitions, quit and restart opencode before invoking a docket skill — an already-open session keeps the old definitions.