OpenCode 2 gets the Pantheon plugin: host-native tools, hooks, and a native TUI surface. This page covers what a user sees. The plugin contract, tool-by-tool registration, and the delegation matrix are in Architecture; supported hosts and evidence are in Compatibility.
The installer must confirm the host is OpenCode 2.x before writing. There is
no generation to select and no fallback: OPENCODE_VERSION=v2 pins the
answer, an OPENCODE_BIN basename of opencode2 is an explicit operator
hint, and otherwise a --version probe decides. A confirmed 1.x host, a
failed probe, or an unreadable version is a hard refusal before any write.
The resolution order and the exact refusal behavior live in
Compatibility. One plugin is registered; a
host never loads two Pantheon plugins.
The plugin registers tools through ctx.tool.transform(). The exact list,
with source locators, is in
Architecture. In user terms:
hashline_editedits files by line-tag anchors instead of raw line numbers.pantheon_costreports tokens and USD by agent, model, and session.pantheon_modelshows, sets, or resets per-agent project-scoped model overrides.pantheon_local_decisionis the advisory local decision selector; it runs only for Zeus.vision_describeappears when the host exposes its model catalog.pantheon_bounded_readappears when the host exposes its permission and session APIs, and only in a root Zeus session.
The three legacy goal tools are not registered. Use the Persistence MCP
goal_* tools for durable manual goals; see Memory.
The installer reads each agent's mcp_tools allowlist and generates native
per-agent permissions. On OpenCode 2.x the host evaluates a
permissions rule whose action is <server>_* (or <server>_<tool>) with
effect deny/allow. The legacy permission.mcp.<server> key is not an
action the host evaluates; the installer translates allow/deny into the
former. ask is not expressible for MCP on that host — it hides the tool
instead of prompting, so the installer emits no rule and warns.
An existing native permissions array remains authoritative. The installer
does not append its legacy default grants to that array; it seeds those defaults
only when the config has no native permissions array yet.
Deny-by-default, the entrypoints, and custom-server setup are in MCP.
The native sidebar shows the Pantheon version, active preset/source, git
state, a Context block with thousands-separated tokens and <percent>% used of <window> (blue ≤25%, green ≤50%, yellow ≤90%, red above), $<spent> spent, and
a muted cache <n>% · <n.n> tok/s line.
One aggregated Dispatch block covers the whole child set:
- header
◈ Dispatch · <n> rodando · $<cost>, summing every child, live and finished; - one row per live child (
⠸ <agent>·<kind> $<cost> <duration>, newest first, three then… +N rodando); - finished children rolled up per agent:
✓ <total> · $<sum> · <M> agentes(plus· <F> falhaswhen any child failed), up to three<agent> ×<count>·<kind> $<sum>rows and… +K $<sum>beyond — never one row per session.
The ·<kind> suffix says why the child exists: coun/plan/revw/hot/
disc from an explicit [council]/[plan]/[review]/[hotfix]/[discovery]
prefix on the subagent description, otherwise from the agent
(athena→plan, themis→revw, talos→hot, apollo→disc, anything else→oth).
council is never inferred from an agent. A ~ after the kind marks a
background child.
The block stays within eight lines and is omitted entirely when the session has
no child. It is added with append claims: the native prompt footer keeps its
own line, and Pantheon only appends ▸<preset> · ✦<n> ativos while delegations
are running.
Pantheon v1.6.0
▸ teampro · anthropic
Sessão: MCP permission audit
▤ Context
142.6K tokens
▰▰▰▰▰▰▰▰▱▱▱▱ 71% of 200K
$4.12 spent
cache 92% · 42.3 tok/s
◈ Dispatch · 2 rodando · $3.49
⠸ prometheus·plan $0.09 14m34s
⠸ apollo·disc $0.03 1m12s
✓ 70 · $3.40 · 9 agentes
prometheus × 12·plan $1.20
hermes × 31 $1.44
athena × 9·plan $0.76
Illustrative layout — not a screenshot. Live children lead the block; finished children collapse into one roll-up per agent, never one row per session.
OpenCode 2.x · pantheon-opencode 1.6.0
/dispatch (also in the palette as Pantheon: Dispatch) opens
pantheon.dispatch in the host's session.panel slot: a full-width view of the
same delegation tree, rendered from the same collector as the sidebar block, so
a child is never listed twice.
The header is ◈ Dispatch — <session> (+ [fullscreen]) with counters for the
whole tree (2 rodando · 70 concluídos · 1 falha · $3.59; a zero counter is
omitted) over one row per child — ⠋ prometheus plan $0.09 14m34s — running
first, then most recent activity, with ✓/✗/○/? for
completed/failed/interrupted/unknown. The detail pane shows the selected child's
title or spawning description, short id, model, in/out/reasoning/cache tokens,
kind evidence, and parent id; — means the host did not publish it.
Panels at least 72 columns put the detail beside the list; narrower ones stack
it under the list and truncate per column. Keys are namespaced and bound
(k/up, j/down, home, end, pageup, pagedown, enter, f,
escape/q) and live inside the panel claim, so they stay inert while the
panel is not focused. The list is paged at ten rows and selection is held by
session id.
The panel reads only host-cached state — no session sync, no child
message.list, no HTTP — and its 80 ms spinner tick exists only while the panel
is open with something running. Opening it requires an open session; otherwise
Pantheon toasts Open a session to view Dispatch instead of inventing a
route. Jumping from a row to the child session is a documented TODO: no
session router destination is verified for a child id.
◈ Dispatch — MCP permission audit
2 rodando · 70 concluídos · 1 falha · $3.59
──────────────────────────────────────────────────────────────────────
▸ ⠋ prometheus plan $0.09 14m34s MCP permission audit
✓ hermes oth $0.44 2m10s ses_ab12cd · anthropic/claude-sonnet-4.5
✓ apollo disc $0.12 1m02s in 12.4K · out 430 · r 120 · cache 8.0K/1.0K
✗ athena plan $0.01 48s kind plan (prefixo) · parent ses_9f31aa
✓ zeus plan $0.07 3m21s
✓ demeter oth $0.05 1m45s
1-10 de 71 · k/up j/down mover · enter detalhe · f fullscreen · escape/q fechar
Illustrative layout — not a screenshot. Above 72 columns the detail sits beside the list; narrower panels stack it underneath and truncate per column.
OpenCode 2.x · pantheon-opencode 1.6.0
When you ask for a plan or start /pantheon-deepwork, Athena returns the full
plan to Zeus, who displays it in the main conversation before asking for
approval. The approval prompt never points to a plan hidden in a child agent's
conversation.
Zeus · teampro
## Plan: MCP permission audit
1. Read every agent allowlist in src/agents/*.md
2. Compare it with the `permissions` rules pantheon-init emits
3. Report drift and update docs/MCP.md where a rule is undocumented
4. Verify with npm run test:node and npm run docs:lint
Files: docs/MCP.md, src/agents/*.md, scripts/install/*.mjs
Risks: an `ask` rule cannot be expressed for MCP on this host
Approve this plan? (yes / no)
Illustrative layout — not a screenshot. The plan is printed in the main conversation before the question, so approval never refers to a child's view.
OpenCode 2.x · pantheon-opencode 1.6.0
The compaction hook performs dynamic context pruning on the
provider-bound message array immediately before summarization, so old tool
output is not re-summarized into permanent context.
- It rewrites tool parts only.
- An invocation repeated with the same tool name and normalized arguments keeps
its most recent result verbatim; every earlier successful body becomes a
[pantheon-prune:v1 …]placeholder. - Error results are never rewritten. A group is pruned only when its newest invocation carries a successful result.
- Arguments of failed calls are blanked after two turns while the error text stays visible.
- The last four turns and a protected tool list (
subagent,task,skill) are never touched. - The marker makes a second pass a no-op, and a pass estimated below 200 tokens does nothing.
The hook log records savings for changed passes and the first occurrence of
each fixed no-op reason (no-candidates or below-threshold) per session and
hook site. These entries contain counts and estimated tokens only; tool content
and arguments are not logged.
The array is mutated in place: no message is deleted or reordered, system is
untouched, and an unexpected shape fails open without throwing. The same engine
is available per request only when the plugin entry sets
options.context_pruner.enabled to true (default false), because
per-request pruning invalidates the provider prompt cache. keepTurns,
purgeAfterTurns, protectedTools, minSavingsTokens, and charsPerToken
tune it.
init writes the entry in its object form ({ "package": …, "options": { … } }),
so there is a place to put that key without guessing the syntax — a bare-string
entry leaves the host with no options at all. The context-pruner option and
default are listed in the README's Plugin options table. Memory retrieval
uses the pantheon-memory MCP only when an agent explicitly calls
memory_search; see Memory.
Durable manual goals are Persistence MCP tools, not plugin tools:
goal_create, goal_get, goal_update, and goal_done. They are granted only
to Zeus, partitioned by OpenCode session id, and never continue a session
automatically. See Memory.
The execute.after chain runs task-result-guard → context-sandbox →
read-enhancer. The guard intercepts native child-session dispatch where the
child returns an empty or missing result. Instead of surfacing a silent
completed with no payload, it raises an explicit error. This catches the
common free-tier failure mode where a child exceeds the uncached-prefill token
budget and returns nothing.
Background dispatch and status/fan-in facilities are runtime-dependent. When no
status tool is visible, use synchronous subagent dispatch or wait for a
host-provided completion notification; do not claim deterministic fan-in or job
recovery without an exposed, verified mechanism.
The plugin reports these gaps through getUnsupportedFeatures() (the seed in
src/pantheon/unsupported.mjs):
- Adapter scope choices:
legacy-hooks(the hook surface of the removed V1 plugin line),catalog-transform,integration-transform,skill-transform,goal-tools(the goal loop needs infrastructure the host's plugin context does not expose). - Host-domain gaps:
idle-todo-enforcer(the enforcer is constructed only when the host'ssessiondomain exposestodo/messages/children/promptAsync; the measured 2.x domain does not),tool-ceiling(nomessage.updatedproducer for the host-reported budget, so the<tool_ceiling>block is not injected),todo-restore(no todo API to snapshot across compaction), andboard-transitions(the shared board is reachable through the injected board, but the writers — terminal audit, idle reports, dispatch mirror — lived in the removed generation's event hook andpantheon-hooks.ts).
Everything else the removed generation wired is here: the Zeus read guard, the
compaction context, vision history sanitization, the post-write formatter, and
the delegation audit trail (start/stop records joined on delegation_id).
Idle dispatch is wired but conditional — it reaches an enforcer only where
idle-todo-enforcer is not in the seed, which is why that marker is listed
above. pantheon_bounded_read applies the same code-root restriction as the
read guard: src/, tests/ and scripts/ are refused; docs, config, the
memory bank and .pantheon/ remain readable.