pantheon-opencode 1.6.0-beta.1 → 1.6.0-beta.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/AGENTS.global.md +206 -0
  2. package/AGENTS.md +10 -891
  3. package/CHANGELOG.md +350 -0
  4. package/README.md +561 -52
  5. package/README.pt-BR.md +285 -20
  6. package/bin/pantheon-init.mjs +70 -4
  7. package/commands/pantheon-audit.md +7 -1
  8. package/commands/pantheon-consolidate.md +1 -1
  9. package/commands/pantheon-deepwork.md +14 -3
  10. package/commands/pantheon-focus.md +17 -0
  11. package/commands/pantheon-vision.md +61 -12
  12. package/package.json +24 -17
  13. package/plugin.json +2 -2
  14. package/pyproject.toml +8 -6
  15. package/scripts/build-agents-md.mjs +428 -56
  16. package/scripts/changelog-merge.mjs +161 -0
  17. package/scripts/{code_mode_server.py → code_mode.py} +3 -3
  18. package/scripts/cost.mjs +244 -41
  19. package/scripts/doctor.mjs +151 -1
  20. package/scripts/install/agents-md.mjs +4 -4
  21. package/scripts/install/backup.mjs +475 -0
  22. package/scripts/install/cli-ui.mjs +6 -1
  23. package/scripts/install/config-migration.mjs +317 -4
  24. package/scripts/install/exit-codes.mjs +26 -0
  25. package/scripts/install/health-check.mjs +21 -19
  26. package/scripts/install/interactive.mjs +16 -2
  27. package/scripts/install/migrate.mjs +41 -22
  28. package/scripts/install/opencode-version.mjs +246 -4
  29. package/scripts/install/opencode.mjs +1175 -215
  30. package/scripts/install/plan.mjs +287 -0
  31. package/scripts/install/plugin.mjs +62 -14
  32. package/scripts/install/shared.mjs +190 -48
  33. package/scripts/install/strings.mjs +20 -0
  34. package/scripts/install/venv.mjs +13 -3
  35. package/scripts/install/vision-prompt.mjs +171 -0
  36. package/scripts/install-mcp.mjs +28 -9
  37. package/scripts/{mcp_persistence_server.py → mcp_persistence.py} +52 -17
  38. package/{src/mcp/mcp_resources_server.py → scripts/mcp_resources.py} +2 -2
  39. package/scripts/{memory_mcp_server.py → memory_mcp.py} +2 -2
  40. package/scripts/probe-context-rehydrate.mjs +8 -2
  41. package/scripts/probe-pantheon-cost.mjs +71 -15
  42. package/scripts/prune.mjs +9 -3
  43. package/scripts/restore.mjs +222 -0
  44. package/scripts/run-node-tests.mjs +189 -0
  45. package/scripts/sync-artifacts.mjs +26 -13
  46. package/scripts/sync-tui.mjs +24 -3
  47. package/scripts/test-opencode-v2-sandbox.sh +180 -26
  48. package/scripts/validate-routing.mjs +3 -3
  49. package/scripts/validate-sandbox-install.mjs +138 -0
  50. package/scripts/versioning.mjs +13 -1
  51. package/scripts/vision-status.mjs +682 -0
  52. package/skills-lock.json +8 -8
  53. package/src/agents/aphrodite.md +20 -37
  54. package/src/agents/apollo.md +7 -3
  55. package/src/agents/athena.md +63 -35
  56. package/src/agents/demeter.md +8 -28
  57. package/src/agents/gaia.md +1 -1
  58. package/src/agents/hephaestus.md +10 -21
  59. package/src/agents/hermes.md +15 -38
  60. package/src/agents/iris.md +7 -1
  61. package/src/agents/mnemosyne.md +18 -54
  62. package/src/agents/nyx.md +1 -1
  63. package/src/agents/prometheus.md +16 -20
  64. package/src/agents/talos.md +10 -2
  65. package/src/agents/themis.md +5 -5
  66. package/src/agents/zeus.md +99 -172
  67. package/src/instructions/agent-return-format.instructions.md +34 -17
  68. package/src/instructions/backend-standards.instructions.md +2 -0
  69. package/src/instructions/frontend-standards.instructions.md +3 -1
  70. package/src/instructions/memory-protocol.instructions.md +12 -110
  71. package/src/instructions/test-economy.instructions.md +21 -0
  72. package/src/instructions/zeus-anti-stall.instructions.md +20 -67
  73. package/src/instructions/zeus-communication-rules.instructions.md +0 -1
  74. package/src/instructions/zeus-council-synthesis.instructions.md +31 -260
  75. package/src/instructions/zeus-memory-operations.instructions.md +30 -0
  76. package/src/instructions/zeus-timeout-retry.instructions.md +61 -89
  77. package/src/mcp/{code_mode_server.py → code_mode.py} +3 -3
  78. package/src/mcp/eval_store.py +2 -2
  79. package/src/mcp/{mcp_persistence_server.py → mcp_persistence.py} +52 -17
  80. package/{scripts/mcp_resources_server.py → src/mcp/mcp_resources.py} +2 -2
  81. package/src/mcp/{memory_mcp_server.py → memory_mcp.py} +2 -2
  82. package/src/mcp/{pantheon_vision_server.py → pantheon_vision.py} +286 -42
  83. package/src/mcp/requirements-mcp.txt +6 -4
  84. package/src/mcp/requirements-vision.txt +2 -2
  85. package/src/pantheon/cost-command.ts +403 -102
  86. package/src/pantheon/delegation-enforce.ts +10 -1
  87. package/src/pantheon/model-agents.ts +19 -0
  88. package/src/pantheon/model-command.ts +218 -56
  89. package/src/pantheon/presets.d.mts +0 -8
  90. package/src/pantheon/presets.mjs +5 -32
  91. package/src/pantheon/v2-bounded-read.ts +640 -0
  92. package/src/pantheon/v2-delegation-enforce.ts +214 -0
  93. package/src/pantheon/v2-execute-after.ts +186 -0
  94. package/src/pantheon/v2-model-overrides.ts +275 -0
  95. package/src/pantheon/v2-tools.ts +418 -0
  96. package/src/pantheon/v2-unsupported.d.mts +11 -0
  97. package/src/pantheon/v2-unsupported.mjs +90 -0
  98. package/src/pantheon/v2-vision.ts +622 -0
  99. package/src/pantheon/vision-local.ts +531 -0
  100. package/src/pantheon/vision.ts +1160 -115
  101. package/src/plugin-v2.ts +1158 -219
  102. package/src/plugin.ts +12 -9
  103. package/src/plugins/tui/dist/server.d.ts +1 -2
  104. package/src/plugins/tui/dist/server.d.ts.map +1 -1
  105. package/src/plugins/tui/dist/tui-v2.d.ts +154 -0
  106. package/src/plugins/tui/dist/tui-v2.d.ts.map +1 -0
  107. package/src/plugins/tui/dist/tui-v2.js +659 -0
  108. package/src/plugins/tui/dist/tui-v2.js.map +1 -0
  109. package/src/plugins/tui/dist/tui.d.ts +85 -82
  110. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  111. package/src/plugins/tui/dist/tui.js +72 -59
  112. package/src/plugins/tui/index-v2.ts +6 -0
  113. package/src/plugins/tui/package-lock.json +5516 -1353
  114. package/src/plugins/tui/package.json +22 -12
  115. package/src/plugins/tui/src/index-v2.tsx +734 -0
  116. package/src/plugins/tui/src/index.tsx +106 -85
  117. package/src/plugins/tui/src/opencode-v2.d.ts +13 -0
  118. package/src/plugins/tui/tsconfig.build.json +5 -0
  119. package/src/plugins/tui/tsconfig.typecheck.json +5 -0
  120. package/src/plugins/tui/tsdown.config.ts +22 -2
  121. package/src/plugins/tui/tui-v2.ts +2 -0
  122. package/src/plugins/tui/tui.ts +3 -0
  123. package/src/routing.yml +11 -21
  124. package/src/skills/README.md +3 -3
  125. package/src/skills/agent-coordination/SKILL.md +7 -2
  126. package/src/skills/artifact-management/SKILL.md +12 -5
  127. package/src/skills/auto-continue/SKILL.md +52 -35
  128. package/src/skills/code-review-checklist/SKILL.md +5 -5
  129. package/src/skills/context-compression/SKILL.md +26 -879
  130. package/src/skills/session-goal/SKILL.md +14 -5
  131. package/src/skills/spec-driven-development/SKILL.md +3 -1
  132. package/src/skills/tdd-with-agents/SKILL.md +18 -10
  133. package/opencode.json +0 -58
package/README.md CHANGED
@@ -31,7 +31,9 @@ way to plan work, make progress, check results, and keep useful project context.
31
31
  ## Start in 2 minutes
32
32
 
33
33
  Requirements: [OpenCode 1.18.4+](https://opencode.ai/docs/) and Node.js
34
- 22.22.2+ (or 24.15.0+ / 26+).
34
+ 22.22.2+ (or 24.15.0+ / 26+). For which OpenCode generation each Pantheon
35
+ release line supports, see
36
+ [Compatibility and support policy](#compatibility-and-support-policy).
35
37
 
36
38
  Pantheon declares `engines.node` as `^22.22.2 || ^24.15.0 || >=26.0.0`. The
37
39
  floor reflects what the dependency tree actually needs — the transitive
@@ -51,6 +53,28 @@ The installer guides you through the available setup. For optional MCP servers,
51
53
  project-local installation, or non-interactive setup, see the
52
54
  [installation guide](docs/INSTALLATION.md).
53
55
 
56
+ A non-interactive apply requires `--yes` (alias `-y`): without a TTY and
57
+ without `--yes`, `init` prints the change plan and applies nothing. Exit codes:
58
+ `0` success or plan/no-apply, `1` failure, `2` apply requested without
59
+ authorization, `3` drift (a planned target changed between plan and apply),
60
+ `130` canceled. Drift is checked within a single run (plan → apply); it cannot
61
+ be detected across two separate CLI invocations (no plan is persisted).
62
+
63
+ Preview without writing anything with `init --dry-run`. Before the first write,
64
+ `init` saves an exact backup set under `.pantheon/backups/<timestamp>/`
65
+ (directory `0700`, files `0600`, the 5 most recent kept); it is a byte-for-byte
66
+ copy and therefore also contains any API keys or tokens already present in your
67
+ files — it is never printed. Recover with
68
+ `pantheon-opencode restore [--project] [--yes]` (there is no automatic
69
+ rollback; an interrupted apply is recoverable the same way). The
70
+ `postinstall`/`sync-tui` step only refreshes TUI artifacts and is deliberately
71
+ outside this approval flow — no prompt is possible there.
72
+ The bundled Python MCP servers use the pinned MCP SDK 2.2.0 and standalone
73
+ FastMCP 4.0.10 runtime; see the [MCP guide](docs/MCP.md).
74
+ The installer also turns each agent's `mcp_tools` allowlist into native
75
+ per-agent permissions for OpenCode V1 and V2; see the
76
+ [agent MCP reference](docs/AGENT-MCP.md).
77
+
54
78
  ## A simple example
55
79
 
56
80
  Once OpenCode is running, describe the outcome you want:
@@ -60,6 +84,10 @@ Once OpenCode is running, describe the outcome you want:
60
84
  ```
61
85
 
62
86
  Pantheon helps turn that request into a plan and a sequence of reviewed steps.
87
+ When you ask for a plan or start `/pantheon-deepwork`, Athena returns the full
88
+ plan to Zeus, who displays it in the main conversation before asking for
89
+ approval. The approval prompt will not point to a plan hidden in a child-agent
90
+ conversation.
63
91
 
64
92
  ## Who is it for?
65
93
 
@@ -76,23 +104,141 @@ clear handoff between stages of work.
76
104
  - Project memory that helps preserve relevant context between sessions.
77
105
  - Optional integrations for common development tasks.
78
106
 
79
- ## Status
80
-
81
- Operational checkout version: **v1.6.0-beta.1** (first beta compatible with
82
- OpenCode 2; publication is not asserted here). Pantheon is designed for OpenCode and depends on the
83
- availability and configuration of OpenCode and any optional services you choose
84
- to use. Check the [releases](https://github.com/ils15/pantheon-opencode/releases)
85
- and [changelog](CHANGELOG.md) for the latest published changes.
107
+ Memory is used selectively: the coordinator searches once only when project
108
+ history could affect a task, then passes relevant results to specialists. Store
109
+ one concise, reusable outcome at task completion when it will matter later;
110
+ routine child summaries and phase updates stay in the current handoff. The
111
+ session-checkpoint tools are separate, optional persistence APIs and are not
112
+ called automatically by compaction hooks. See [Memory usage](docs/MEMORY.md).
86
113
 
87
- ## Delegation (native `task()`)
114
+ ## Status
88
115
 
89
- Pantheon delegates exclusively through OpenCode's native `task()` child-session
90
- engine. The former custom `pantheon_delegate` tool and the V1 delegation engine
91
- (`delegation.ts`, `delegate-manager.ts` and supporting modules) were removed;
92
- there is no Pantheon-specific delegation tool surface to configure. See
116
+ Candidate version: the manifests in this checkout express the version being
117
+ prepared, not a confirmed publication. A beta is published only after the
118
+ Release workflow completes successfully. To check which version is actually
119
+ published, consult the npm `beta` dist-tag. Pantheon is designed for OpenCode
120
+ and depends on the availability and configuration of OpenCode and any optional
121
+ services you choose to use. Check the
122
+ [releases](https://github.com/ils15/pantheon-opencode/releases) and
123
+ [changelog](CHANGELOG.md) for the latest published changes.
124
+
125
+ ## Compatibility and support policy
126
+
127
+ > ### ⚠️ 1.6.x is the LAST line that supports OpenCode 1.X
128
+ >
129
+ > **OpenCode 1.X support ends after the 1.6 line.** From **1.7 onward, every
130
+ > release is a breaking change targeting OpenCode 2.** A 1.6.x install is the
131
+ > last one that runs on an OpenCode 1.X host; do not plan an upgrade across the
132
+ > 1.6 → 1.7 boundary while your host is still on OpenCode 1.X.
133
+
134
+ | Pantheon line | OpenCode 1.X host | OpenCode 2 host |
135
+ |---|---|---|
136
+ | **1.6.x** (current line) | **Supported.** V1 plugin contract | Supported, reduced surface — V2 contract |
137
+ | **1.7 and later** | **Not supported.** Breaking change | Target of the 1.7+ work |
138
+
139
+ Both columns of the 1.6.x row are real, and they are not the same claim. Read
140
+ [the plugin contract](#opencode-v1v2--dual-version-160-beta1) for what each
141
+ generation actually registers.
142
+
143
+ ### What "supported on OpenCode 1.X" concretely means
144
+
145
+ On the 1.6 line, an OpenCode 1.X host gets the **V1 plugin**:
146
+ `src/plugin.ts` plus `src/plugins/pantheon-hooks.ts`, registered under the
147
+ singular `plugin` config key. That surface registers 6 tools
148
+ (`hashline_edit`, the 3 `pantheon_goal_*` tools, `pantheon_cost`,
149
+ `pantheon_model`), the BackgroundJobBoard lifecycle, the V1 event/tool hooks and
150
+ the V1 compaction path. It is selected automatically: the generation gate
151
+ (`--opencode-version`, else `OPENCODE_VERSION`, else a `OPENCODE_BIN` basename of
152
+ `opencode2`, else a `--version` probe where major ≥ 2 selects V2) resolves a 1.X
153
+ host to V1.
154
+
155
+ The two generations are exclusive. The installer strips Pantheon references from
156
+ both config shapes and registers only the selected one, so a 1.X host never
157
+ loads the V2 plugin and a V2 host never loads the V1 plugin. Forcing V2 on a 1.X
158
+ host is an unsupported mixed/wrong-generation configuration, not a supported mode.
159
+
160
+ ### What the OpenCode 2 path is — and is not
161
+
162
+ **Not inert.** The V2 path (`src/plugin-v2`, registered as a directory because
163
+ the V2 loader rejects bare file paths) registers real tools with real
164
+ `input`/`output` schemas via `ctx.tool.transform()`, 5 event subscriptions,
165
+ session hooks and tool/permission hooks that enforce read-only sessions and the
166
+ caller→target delegation matrix. The matrix applies only to Pantheon-managed
167
+ targets: Zeus may delegate to any registered agent; Athena and Hermes may target
168
+ Apollo only; other Pantheon callers and unknown callers are denied. Child
169
+ sessions cannot delegate to Pantheon agents. Calls between native/custom agents
170
+ remain under host permissions. The V2 path never adds an allow grant or
171
+ overrides an explicit host deny; missing or mismatched identity fails closed
172
+ for the attempted Pantheon delegation. The 3 goal tools remain unsupported and
173
+ are reported as `goal-tools`.
174
+
175
+ When the host successfully registers V2's permission hook, Pantheon does not
176
+ trust `permission.evaluate.agent` by itself for a Pantheon target. It matches
177
+ the permission source's session, message and tool-call IDs to a short-lived
178
+ record from the host's `tool.execute.before` event, then requires the host
179
+ session to have no `parentID` before applying the matrix. This admits valid
180
+ root dispatches and denies child-session dispatches; missing, stale, replayed
181
+ or mismatched origins are denied. OpenCode v2.0.29 runtime evidence is in
182
+ [`bench/results/f4a-v229-admission-identity-2026-10-14.md`](bench/results/f4a-v229-admission-identity-2026-10-14.md).
183
+
184
+ **Partial, with the exercised host version made explicit.** The V2 tool canary
185
+ started against `opencode 2.0.22`, a version that no npm channel publishes.
186
+ The current F4A rerun now uses the installable **OpenCode v2.0.29** binary and
187
+ passes 18/18 checks with permission probes: the real plugin registers and runs
188
+ its tools, admits the valid Zeus→Apollo root dispatch, rejects an invalid edge
189
+ before child creation, and rejects a matrix-valid dispatch from a child. The
190
+ remaining V2 surface still has unsupported goal tools and registration-only
191
+ session/compaction hooks, so the 1.6 line is not full V2 feature parity.
192
+
193
+ ### Verified against which OpenCode versions
194
+
195
+ | What | Verified against | How |
196
+ |---|---|---|
197
+ | V1 plugin + generation gate | The OpenCode 1.18.x line. This package pins `@opencode-ai/plugin` and `@opencode-ai/sdk` to `1.18.33`; the highest published `opencode-ai` host package (the one shipping the `opencode` binary) is `1.18.34` | Dependency pin (`package.json`); generation-gate unit tests over injected host banners; installer end-to-end tests asserting the emitted `opencode.json` shape |
198
+ | V2 plugin tool surface | OpenCode v2.0.29 | `tests/canary/plugin-v2-tool-canary.test.mjs`, driving the real plugin through `opencode serve`; F4A permission canary passes 18/18 |
199
+ | V2 native subagent admission | OpenCode v2.0.29 | `tests/canary/run-v2-permission-proof.sh`; identity, root/child checks and permission-before-child ordering are recorded in [`bench/results/f4a-v229-admission-identity-2026-10-14.md`](bench/results/f4a-v229-admission-identity-2026-10-14.md) |
200
+ | Exact 1.X host version the 1.6 line is exercised against end-to-end | **Unverified.** No V1 host leg exists: the sandbox runner is V2-only and `--run v1` is rejected | — |
201
+
202
+ ## Delegation (root `subagent`; host/API `task()`)
203
+
204
+ The host's child-session engine is referred to as native `task()` in API and
205
+ implementation documentation. In the user-reported Beta 10 Zeus-root runtime,
206
+ the callable dispatch tool was `subagent`; `task()` was not exposed as a root
207
+ tool. Treat the API name and examples below as host/API terminology, not a
208
+ guaranteed root-tool name or signature. The active runtime determines the
209
+ `subagent` schema, background options, and completion observability. The former
210
+ custom `pantheon_delegate` tool and the V1 delegation engine (`delegation.ts`,
211
+ `delegate-manager.ts` and supporting modules) were removed; there is no
212
+ Pantheon-specific delegation tool surface to configure. See
93
213
  [ADR-0011](.pantheon/memory-bank/adr/0011-delegation-engine-contract.md) for the
94
214
  historical engine contract.
95
215
 
216
+ This beta.8 candidate combines native per-agent MCP permissions for OpenCode V1
217
+ and V2, V2 delegation-permission alignment, simpler memory/compression
218
+ instructions, and a sandbox check that proves the installed package matches
219
+ this checkout instead of silently accepting stale npm-cached code. The isolated
220
+ sandbox launcher also pins the OpenCode background service to its dedicated
221
+ loopback port, rather than colliding with another OpenCode installation. The Zeus
222
+ prompt reuses one task-start memory-search result for context and routing;
223
+ runtime call counts and latency have not been measured. A beta.7 V2 dispatch
224
+ failed because the permission hook passed a bare string to `ctx.session.get`
225
+ instead of the V2 input object `{ sessionID }`; beta.8 corrects the call and
226
+ adds contract tests for the success and failure cases. The user subsequently
227
+ reported a successful Hermes liveness delegation in the active session, but
228
+ freshly restarting and testing the beta.8 sandbox remains the end-to-end gate.
229
+ The original report also included a separate `GET /api/session/{id}/permission`
230
+ 404; the session ID exists in the beta7-candidate database but not the default
231
+ global database. Both OpenCode services were running, so the 404 in the global
232
+ log may come from the other instance; this remains an end-to-end verification
233
+ item.
234
+
235
+ Delegated work gets at most one retry after an initial transient failure. If no
236
+ completion/status tool is exposed, prefer synchronous `subagent` dispatch or
237
+ wait for a host-provided completion notification; do not assume polling,
238
+ cancellation, job tracking, or board-based recovery. If the runtime does not
239
+ make completion observable, report that limitation. The configured fallback or
240
+ escalation chain still applies.
241
+
96
242
  ## Cost tool backend
97
243
 
98
244
  `pantheon_cost` prefers a read-only `node:sqlite` backend against the selected
@@ -110,6 +256,8 @@ available.
110
256
  |----------|---------|---------|
111
257
  | `PANTHEON_NODE` | `node` from `PATH` | Path to a Node.js >= 22.5 binary used by the CLI fallback; an explicit path must exist and be executable, otherwise the tool fails fast instead of ignoring the override |
112
258
 
259
+ Tool interpretation matters: `pantheon_model status` reports configuration, not the model serving a completion. V2 manual model overrides are project-only; global override files are neither read nor applied, and global mutation is unsupported until a future surface can obtain separate explicit human authorization. V2 ignores static presets and applies only explicit per-agent project model/variant overrides, leaving the host default unchanged otherwise; V1 preset behavior remains legacy pending compatibility decisions. `pantheon_cost` is a rolling token aggregate, not a session-level or monetary cost report. See the [OpenCode plugin tool notes](docs/mcp-tools.md#opencode-plugin-tools) for details and measurement limits.
260
+
113
261
 
114
262
  ## Code-mode execution (explicit opt-in)
115
263
 
@@ -183,16 +331,128 @@ per installation; V1 and V2 Pantheon plugins must never be registered together.
183
331
  |---|---|---|
184
332
  | OpenCode config key | singular `plugin` | plural `plugins` |
185
333
  | Pantheon registration | `src/plugin.ts` plus `src/plugins/pantheon-hooks.ts` | `<installed>/src/plugin-v2` directory (`index.ts` re-exports `src/plugin-v2.ts`) |
186
- | Runtime contract | Pantheon V1 plugin: 6 tools (`hashline_edit`, the 3 goal tools, `pantheon_cost`, `pantheon_model`), event/tool hooks and V1 compaction handling | Full V2 plugin: 6 orchestration tools, 4 event subscriptions, session hooks (`prompt`, `context`), tool hooks (`execute.before`/`after`), plus configuration transforms |
334
+ | Runtime contract | Pantheon V1 plugin: 6 tools (`hashline_edit`, the 3 goal tools, `pantheon_cost`, `pantheon_model`), event/tool hooks and V1 compaction handling | Full V2 plugin: 3 orchestration tools plus `vision_describe` when the model catalog is available, 5 event subscriptions, session hooks (`prompt`, `context`), a read-only-enforcing tool `execute.before` hook, plus configuration transforms |
187
335
  | V1 APIs | Registered | Own tool definitions via `ctx.tool.transform()` — not the V1 plugin path |
188
336
 
189
- The V2 plugin provides 6 orchestration tools (`hashline_edit`,
190
- `pantheon_goal_create`, `pantheon_goal_get`, `pantheon_goal_update`,
191
- `pantheon_cost`, `pantheon_model`), 4 event subscriptions (`session.created`,
192
- `session.idle`, `session.error`, `session.compacted`), session hooks (`prompt`,
193
- `context`), and tool hooks (`execute.before`, `execute.after`). Unsupported V2
194
- features are `legacy-hooks` (the V1-specific hook surface), `catalog-transform`,
195
- `integration-transform`, and `skill-transform`.
337
+ The V2 plugin provides 3 orchestration tools (`hashline_edit`, `pantheon_cost`,
338
+ `pantheon_model`), 5 event subscriptions (`session.created`, `session.idle`,
339
+ `session.deleted`, `session.error`, `session.compacted`), session hooks
340
+ (`prompt`, `context`),
341
+ and a tool `execute.before` hook that enforces read-only sessions. The 3 goal
342
+ tools are **not**
343
+ registered on V2: the goal loop needs a `GoalStore`, a `GoalLoopClient` and a
344
+ `BackgroundJobBoard`, none of which the V2 `PluginContext` exposes, so the gap
345
+ is reported as the `goal-tools` unsupported-feature marker. Use the V1
346
+ contract if you need the goal loop.
347
+
348
+ V2 also registers `vision_describe` when `ctx.model.list()` is available. It
349
+ checks the live catalog for image input, prefers the paid OpenCode Go
350
+ `opencode-go/mimo-v2.6-flash` model (free-tier models are excluded from automatic
351
+ selection — the direct gateway rejects them with HTTP 403, they only work from
352
+ inside OpenCode; an explicit `PANTHEON_VISION_MODEL`/config/preset pin is still
353
+ honored, so its 403 surfaces as an actionable error rather than a silent
354
+ substitution), and
355
+ sends the image directly to the authenticated OpenCode Zen endpoint. The request
356
+ carries the live session id as the Zen-only `x-opencode-session` header the
357
+ gateway requires (never sent to a non-Zen provider, and never logged). OpenCode
358
+ credentials come from
359
+ `opencode auth login` or `PANTHEON_OPENCODE_API_KEY`; if no supported model or
360
+ credentials are available, the tool returns an explicit error instead of a
361
+ fabricated description. This path works without the optional V1 bridge.
362
+ The V2 prompt hook also preserves image attachment references and tells Zeus
363
+ to inspect relevant visual context and pass the reference plus concise findings
364
+ to Aphrodite. Inline image bytes are not copied into persisted prompt text, and
365
+ an image does not authorize deleting or reverting unrelated work.
366
+
367
+ The chat-turn native path picks its vision model in a fixed precedence: the
368
+ `PANTHEON_VISION_MODEL` env var, then `visionModel` in `opencode-vision.json`,
369
+ then the active preset's `vision.model`, then automatic discovery in the host
370
+ provider catalog (`GET /config/providers`), then the built-in default. Discovery
371
+ is conservative and paid-only: it keeps only image-capable entries
372
+ (`capabilities.input.image`) whose provider is a trusted Zen provider or whose
373
+ `npm` package is OpenAI-compatible (`@ai-sdk/openai-compatible` /
374
+ `@ai-sdk/openai`), EXCLUDES free-tier entries (`-free` id or `cost.input <= 0`
375
+ — the direct gateway rejects them with HTTP 403), picks the cheapest remaining
376
+ entry, and caches the catalog for 60
377
+ seconds per process; an incompatible model simply falls through to the vision
378
+ MCP tool. The base URL comes from the catalog entry's `api.url`, and the legacy
379
+ Zen endpoints remain the no-catalog fallback. Credentials follow the endpoint:
380
+ for a Zen endpoint the shared `PANTHEON_OPENCODE_API_KEY` / `OPENCODE_API_KEY`
381
+ env keys are tried first, then the OpenCode auth store (the resolved provider
382
+ first, then the legacy `opencode-go` / `opencode` entries); for a non-Zen
383
+ endpoint only that provider's own auth-store entry is used — the shared env keys
384
+ and the legacy Zen entries are never attached to a non-Zen base URL. An
385
+ explicitly configured, qualifying vision model always wins over discovery — a
386
+ pin that does not qualify (non-Zen, text-only, or absent from the catalog) is
387
+ discarded and the automatic selection takes over. `pantheon-opencode init`
388
+ offers an optional vision step in interactive TTY sessions — automatic discovery
389
+ is the default and writes nothing; pinning merges `visionModel` into the global
390
+ `opencode-vision.json`; non-interactive runs never prompt and never write.
391
+ The `/pantheon-vision` command adds a model picker: `vision-status.mjs --list`
392
+ enumerates image-capable candidates — labelling whether the source is the live
393
+ host catalog or an offline `opencode models` heuristic, plus `free`/`paid` and
394
+ credential presence per provider (never the token) — and `--set` pins the chosen
395
+ `provider/model` with the same atomic merge.
396
+
397
+ The user's question is injected into the vision prompt on both the native and V2
398
+ paths, trimmed and capped at 2000 characters; the same image asked a different
399
+ question gets a fresh description, while an identical re-ask inside the cache
400
+ TTL reuses the stored text. Native describes are robust by design: the timeout
401
+ scales from a 20-second base at roughly 10 seconds per MB of payload up to a
402
+ 300-second ceiling, and a zero-dependency EXIF-orientation check (JPEG) adds a
403
+ textual hint so the vision model describes a rotated or mirrored image upright —
404
+ detection only, pixels are never rewritten, and the orientation is folded into
405
+ the cache key.
406
+
407
+ Vision degrades through an explicit chain — native → vision MCP tool → local.
408
+ When native is unavailable and the vision MCP tool is absent, the image is
409
+ replaced by deterministic local extraction: dimensions, format and size from the
410
+ PNG/JPEG/GIF/WebP header, dominant colors for RGB/RGBA PNG, and OCR only when a
411
+ `tesseract` binary is on `PATH`. It never fabricates content — the degradation is
412
+ stated explicitly. The `vision_describe` and `vision_ocr` MCP tools degrade to
413
+ that local mode when no gateway credential is configured, with no new
414
+ dependencies. (`vision_analyze` requires a gateway credential and returns an
415
+ explicit error instead.)
416
+ Deliberate trade-offs, recorded so they are not reversed by accident: Pantheon
417
+ describes each image once and keeps the text in history (council 2026-08-03)
418
+ rather than preserving the original image for per-turn re-observation;
419
+ describe-always favors zero added latency over an on-demand `describe_image`
420
+ tool. Multi-turn sanitization (`experimental.chat.messages.transform`) is a
421
+ mandatory invariant — it guarantees `image_url` / `FilePart` payloads never
422
+ reach a text-only provider and is not configurable. Bifrost stays opt-in and
423
+ never the default, per the credential incident in [SECURITY.md](SECURITY.md).
424
+
425
+ The V2 `execute.before` hook is the read-only enforcement point for that
426
+ surface: the host includes the active agent on the event, a read-only agent
427
+ (`apollo`, `gaia`) registers its session, and the shared
428
+ `createEnforcementGuard` throws to deny `edit`, `write`, `bash`, `task`,
429
+ `hashline_edit` and `pantheon_model`. It does **not** rely on the V1
430
+ `tool.execute.before` hook, which lives in `src/plugin.ts` and is not loaded
431
+ when only `plugin-v2` is configured. When the host registers the
432
+ `permission.evaluate` hook, it enforces the V2 caller/target matrix for
433
+ Pantheon-managed native `subagent` requests. If hook registration is
434
+ unavailable, the matrix is unavailable and the host remains in control.
435
+ Native-only resources pass through unchanged to OpenCode's permission policy;
436
+ mixed native/Pantheon and multi-target requests are denied. Pantheon matches
437
+ the permission source's session, message and tool-call IDs to a short-lived
438
+ record from `tool.execute.before`, cross-checks the captured host caller against
439
+ `permission.evaluate.agent`, and confirms the session has no `parentID`.
440
+ `permission.evaluate.agent` alone is not trusted because
441
+ `ctx.permission.assert` accepts a caller-supplied agent. Missing, stale,
442
+ replayed or mismatched origins fail closed, and explicit host denies remain
443
+ final. On OpenCode v2.0.29, a runtime canary admits Zeus→Apollo from a root,
444
+ denies a matrix-valid Athena→Apollo call from a child and rejects an invalid
445
+ edge before child creation; see the
446
+ [`F4A evidence`](bench/results/f4a-v229-admission-identity-2026-10-14.md).
447
+ V2 `execute.after` runs the completed-result chain:
448
+ `task-result-guard` → `context-sandbox` → `read-enhancer`.
449
+
450
+ Every V2 tool declares an `output` schema. OpenCode 2.0.x requires the
451
+ declaration and the resolved result to agree in both directions, so an
452
+ undeclared tool — or one that returns `output` without declaring it — fails on
453
+ every call. Unsupported V2 features are `legacy-hooks` (the V1-specific hook
454
+ surface), `catalog-transform`, `integration-transform`, `skill-transform` and
455
+ `goal-tools`.
196
456
 
197
457
  The package exposes both contracts as importable exports: `pantheon-opencode/plugin`
198
458
  (V1), `pantheon-opencode/plugin-v2` (V2) and `pantheon-opencode/v2-bridge`
@@ -211,28 +471,75 @@ npx pantheon-opencode init --opencode-version v2
211
471
  npx pantheon-opencode init --opencode-version auto
212
472
  ```
213
473
 
214
- `--version v1|v2|auto` is accepted as the older selector spelling when used
215
- after `init`. `auto` is conservative, not general platform autodetection:
216
- `OPENCODE_VERSION=v1|v2` wins; otherwise an `OPENCODE_BIN` ending in
217
- `opencode2` selects V2; every other case selects V1. The installer removes
218
- Pantheon references from both config shapes before writing only the selected
219
- Pantheon registration. Third-party entries are not converted or claimed by
220
- this rule.
221
-
222
- The V1-only `pantheon_cost` report can select its database with
223
- `PANTHEON_OPENCODE_VERSION=v1` or `v2` (`opencode.db` or `opencode-v2.db`).
224
- `PANTHEON_COST_DB=/absolute/path/to/opencode.db` takes precedence over the
225
- version selector, and an explicit `dbPath` supplied by the tool caller takes
226
- precedence over both. The resolver never probes the other version's database
227
- and reports an actionable error when the selected DB is missing or has an
228
- incompatible schema.
474
+ `auto` is the default. `--version v1|v2|auto` is accepted as the older
475
+ selector spelling when used after `init`. `auto` resolves the generation from
476
+ the host in this order: an explicit `OPENCODE_VERSION=v1|v2` wins; otherwise an
477
+ `OPENCODE_BIN` path ending in `opencode2` selects V2; otherwise the host
478
+ binary is asked for its `--version` and a major of 2 or more selects V2. Every
479
+ other case — an unreadable probe, an unparseable banner, or a banner whose
480
+ version-like tokens contradict each other with no tool name to break the tie —
481
+ warns once and falls back to V1, because a plural `plugins` directory entry on
482
+ an unknown 1.x host loses the plugin entirely.
483
+
484
+ The probe prefers the token that follows the tool name, so a runtime token
485
+ ahead of it (`node v22.1.0 (opencode 1.18.33)`) or a trailing build date
486
+ (`opencode v1.18.33 built 2026.10.04`) cannot flip the generation. The
487
+ installer removes Pantheon references from both config shapes before writing
488
+ only the selected Pantheon registration. Third-party entries are not converted
489
+ or claimed by this rule.
490
+
491
+ The `pantheon_cost` report resolves its database by PATH, in this order: an
492
+ explicit `dbPath` supplied by the tool caller, then
493
+ `PANTHEON_COST_DB=/absolute/path/to/opencode.db`, then `OPENCODE_DB`, then the
494
+ XDG default `opencode.db`. There is no per-version filename in that chain:
495
+ `opencode-v2.db` is not a host 2.x database — it is the name a sandbox gives
496
+ its own state database through `OPENCODE_DB`.
497
+
498
+ The v1/v2 distinction is the DETECTED SCHEMA, not the file name. The report
499
+ looks for the `message` and `session_message` tables and reads whichever the
500
+ open database actually holds, because a migrated database carries both
501
+ families at once. `PANTHEON_OPENCODE_VERSION=v1|v2` selects no file: it only
502
+ narrows an already-detected set to one table family, and fails fast when the
503
+ value is neither `v1` nor `v2` or when the requested family is not there. Left
504
+ unset, every detected family is read. A database carrying neither table comes
505
+ back as an actionable error (`CORRUPT_DATA`), and a ledger that exists but
506
+ yields no readable tokens as `UNSUPPORTED` — never as a successful empty
507
+ report.
229
508
 
230
509
  The installer still writes the compatibility settings required by the selected
231
510
  OpenCode host, such as `experimental.subagent_depth`; this does not convert a
232
511
  V1 plugin into V2 or provide V2 with V1 hooks.
233
512
 
513
+ Pantheon does not set a step ceiling on agents. On OpenCode 2 the `steps`
514
+ field is optional and has no default: absent means the host applies no native
515
+ limit. A config written before this change still carries the old value, and the
516
+ V2 install path strips it from every agent Pantheon manages — an agent you
517
+ define yourself is never touched, and the retiring V1 singular `agent` block
518
+ is left alone. Run the installer once to clear a stale value from an existing
519
+ config:
520
+
521
+ ```bash
522
+ npx pantheon-opencode init --opencode-version=v2
523
+ ```
524
+
525
+ Context control is not a Pantheon setting. Whatever budget an agent runs
526
+ against comes from the compaction settings of the OpenCode host you are
527
+ running, and Pantheon does not define them.
528
+
529
+ When a V1 `agent` block and a V2 `agents` block are both present — which is
530
+ what an installed `opencode.json` carries — the V1→V2 conversion merges them
531
+ instead of letting one replace the other. The V2 block is the base, the
532
+ framework-managed fields from the V1 block overwrite, and your own fields are
533
+ preserved, matching the precedence the installer already applies elsewhere.
534
+ Key order does not matter.
535
+
234
536
  ## Updating between releases (beta.5+)
235
537
 
538
+ > **Read [Compatibility and support policy](#compatibility-and-support-policy)
539
+ > before upgrading across a line boundary.** 1.6.x is the last line that
540
+ > supports an OpenCode 1.X host; 1.7+ is breaking-change territory targeting
541
+ > OpenCode 2. Within the 1.6 line, `update` is the supported path.
542
+
236
543
  One command keeps an existing installation current:
237
544
 
238
545
  ```bash
@@ -253,7 +560,14 @@ Two freshness guarantees back it up:
253
560
  - **Postinstall artifact sync** — after any `npm install`, copy-only artifacts
254
561
  (agents, skills, AGENTS.md, commands, MCP scripts, code-mode payload) are
255
562
  refreshed into the existing config dir automatically; you never have to
256
- re-run `init` just for file copies.
563
+ re-run `init` just for file copies. The global config dir receives the
564
+ repo-agnostic **global artifact** (`AGENTS.global.md`: agent table + shared
565
+ rules), while each project's own `AGENTS.md` carries only project-specific
566
+ setup — the two are carved to be disjoint (beta-10 latency work), so no rule
567
+ is paid for twice per session. Installs are conservative: content outside
568
+ the generated managed region (user house rules) is preserved verbatim, a
569
+ legacy fully-generated `AGENTS.md` upgrades in place, and any merge is
570
+ reported with a warning.
257
571
  - **Drift detection** — the installer stamps the installed version in
258
572
  `.pantheon/install-state.json` and `doctor` warns when the package is newer
259
573
  than the last sync, pointing at `update`. `doctor` also detects **plugin
@@ -264,23 +578,35 @@ Two freshness guarantees back it up:
264
578
  current package.
265
579
 
266
580
  `init` also gained `--components agents,skills,...` (narrow install),
267
- `--clean` (alias of `--force`), `--opencode-version auto`, atomic config
268
- writes with an `opencode.json.bak` backup, preflight checks for python3/npm
269
- before any file is written, and a non-fatal Python runtime: if the venv fails,
270
- the install completes but MCP entries are omitted (with a warning) instead of
271
- pointing at a broken interpreter. Installer messages auto-detect pt-BR via
272
- `LANG`/`LC_ALL`.
581
+ `--clean` (alias of `--force`), `--opencode-version auto`, preflight checks for
582
+ python3/npm before any file is written, and a non-fatal Python runtime: if the
583
+ venv fails, the install completes but MCP entries are omitted (with a warning)
584
+ instead of pointing at a broken interpreter. Before the first modifying write,
585
+ `init` preserves, byte-for-byte, every regular file it can modify or remove
586
+ inside the config dir (including files a `--clean` run deletes) under
587
+ `.pantheon/backups/<timestamp>/` (directory `0700`, files `0600`, kept 5 deep);
588
+ bulk directories
589
+ (`.venv`, the plugin's `node_modules`) and out-of-target global files
590
+ (`tui.json` copies elsewhere, the global `opencode-vision.json`) are listed in
591
+ the manifest as `notRevertible` rather than silently omitted. The exact-set
592
+ warning is printed once and never echoes file contents. Restore with
593
+ `pantheon-opencode restore [--project] [--yes]` (the old `opencode.json.bak`
594
+ sidecar is gone; the global vision config keeps its own `.bak`). Installer
595
+ messages auto-detect pt-BR via `LANG`/`LC_ALL`.
273
596
 
274
597
  ## Migrating to 1.5.x (from 1.4.x)
275
598
 
276
599
  1.5.0 removed the custom `pantheon_delegate` tool (and the V1 delegation
277
- engine) in favor of OpenCode's native `task()`; see
278
- [Delegation (native `task()`)](#delegation-native-task). Two things change on
600
+ engine) in favor of the host child-session engine; see
601
+ [Delegation (`subagent` / host API `task()`)](#delegation-root-subagent-hostapi-task). In the
602
+ reported Beta 10 runtime Zeus dispatches through `subagent`; `task()` remains
603
+ host/API terminology, not a guaranteed root tool. Two things change on
279
604
  an existing install:
280
605
 
281
606
  1. **The tool disappears from the plugin surface.** `pantheon_delegate` is no
282
607
  longer registered by `src/plugin.ts` or `src/plugin-v2.ts`. Agents now
283
- delegate through `task()` only — no configuration is needed.
608
+ delegate through the dispatch tool actually exposed by the active runtime —
609
+ no Pantheon-specific delegation tool configuration is needed.
284
610
  2. **A lockfile-pinned copy can keep the old tool alive.** `npm install` is
285
611
  lockfile-authoritative: a `package-lock.json` pinned to `1.4.1` (which
286
612
  satisfies `^1.4.1`) is never re-resolved, so a project's
@@ -315,6 +641,10 @@ pushes, merges, and tags never publish anything, and every validation gate is
315
641
  fail-closed: only an explicit PASS authorizes release evidence. See
316
642
  [docs/RELEASING.md](docs/RELEASING.md) for validation and recovery details.
317
643
 
644
+ For a candidate whose release section already exists, maintain its notes with
645
+ `node scripts/versioning.mjs changelog <version>`; this merges `[Unreleased]`
646
+ into that version without advancing the package version.
647
+
318
648
  Release validation keeps each manifest with its lockfile: the root
319
649
  `package.json` + `package-lock.json` and the TUI
320
650
  `src/plugins/tui/package.json` + `src/plugins/tui/package-lock.json`. Both use
@@ -323,6 +653,12 @@ Release validation keeps each manifest with its lockfile: the root
323
653
  SHA-256 of that same artifact, and binds the tarball and GitHub release to the
324
654
  full `TARGET_SHA`; a second pack is not interchangeable.
325
655
 
656
+ CI also rebuilds the TUI with `npm run build --prefix src/plugins/tui` and
657
+ checks every packaged `dist` artifact byte-for-byte against its checked-in
658
+ version, including final newlines. The build includes separate V1 and native
659
+ OpenCode V2 TUI entrypoints; `init` selects the entrypoint and sidebar slot API
660
+ for the host generation in use.
661
+
326
662
  ## Sandbox validation (V2)
327
663
 
328
664
  `scripts/test-opencode-v2-sandbox.sh` validates the globally installed
@@ -334,6 +670,11 @@ agent delegation. The gate is fail-closed: every required check must return an
334
670
  explicit PASS; timeouts, auth/network/provider failures and missing
335
671
  prerequisites block the run.
336
672
 
673
+ The runner defaults to the current stable `@opencode/cli` package and its
674
+ `opencode2` alias; set `OPENCODE_V2_SPEC` only when a test needs a different
675
+ host build. This keeps Pantheon beta testing separate from the OpenCode CLI
676
+ release channel.
677
+
337
678
  "V2" here refers only to the hook canary observed against an OpenCode v2.0.18
338
679
  host, where at least one tested hook callback fired; it does not establish
339
680
  compatibility with the stable `@opencode/plugin@2.0.18` SDK or full 2.x
@@ -345,6 +686,7 @@ single leg.
345
686
 
346
687
  ```bash
347
688
  scripts/test-opencode-v2-sandbox.sh --prepare # tarball + install + init in the sandbox
689
+ ~/pantheon-sandbox/start-pantheon.sh # interactive TUI in that isolated beta sandbox
348
690
  scripts/test-opencode-v2-sandbox.sh --run v2 # base validation only
349
691
  scripts/test-opencode-v2-sandbox.sh --prompts # base validation + prompt battery
350
692
  scripts/test-opencode-v2-sandbox.sh --rehydrate # offline context rehydration/session-summary probe
@@ -369,6 +711,142 @@ repository from a sibling directory.
369
711
  This validates the prepared isolated sandbox only. A PASS is not proof of
370
712
  support for every real host or for host configurations that were not exercised.
371
713
 
714
+ ## Latency benchmark (bench/)
715
+
716
+ `bench/` measures end-to-end session latency of the installed `opencode` CLI
717
+ with the repo's V2 plugin — the baseline every performance change in a release
718
+ must be compared against. It runs two scenarios N times each, interleaved
719
+ (non-interactive, one fresh sandbox directory per run), collects per-run
720
+ metrics from `opencode.db` in READ-ONLY mode (tokens root + child sessions
721
+ without double counting, tool calls per name, child-session count) and hook
722
+ timings from the `[bench]` lines the plugin appends to `hooks.log`:
723
+
724
+ ```bash
725
+ node bench/bin/bench.mjs # 5 runs per scenario (A and B), ~1h
726
+ BENCH_MODEL=opencode-go/glm-5.3-flash node bench/bin/bench.mjs # pin a model
727
+ node bench/bin/bench.mjs --runs 1 # smoke: one run per scenario
728
+ ```
729
+
730
+ Results land in `bench/results/<label>-<git-sha>.json` (median + p80 +
731
+ failures per scenario); the label defaults to `baseline` and the harness
732
+ refuses to overwrite an existing result, so the run identity lives in the file
733
+ name. Scenario A performs real web research, so its variance is
734
+ network-dependent and recorded per run.
735
+
736
+ ### Measurement profiles (explicit, never inferred)
737
+
738
+ Instructions are injected from two surfaces: the global config dir and the
739
+ project directory. The harness states which one it exercises, because a
740
+ result only means anything in the profile where the change acts:
741
+
742
+ | `--profile` | Global `AGENTS.md` | Project `AGENTS.md` | Use |
743
+ |---|---|---|---|
744
+ | `host` (default) | host config dir (or `--global-agents`) | none — run dirs stay empty | the host's real, unmodified injection |
745
+ | `double-injection` | host config dir (or `--global-agents`) | `--project-agents`, written into every run dir | the in-repo profile where global+project overlap |
746
+
747
+ The default measures scenarios A and B. Use `--scenarios B` to isolate the F1
748
+ one-shot claim; the selected scenario still runs N times (`--runs 5` by
749
+ default).
750
+
751
+ ```bash
752
+ # candidate arm: the carved global artifact + the carved project artifact
753
+ node bench/bin/bench.mjs --runs 5 --scenarios B --profile double-injection \
754
+ --global-agents AGENTS.global.md --project-agents AGENTS.md --label f1-candidate
755
+ ```
756
+
757
+ Each arm records, per run and read from the db, the identity (path, bytes,
758
+ sha256) of every instruction file the session actually loaded
759
+ (`instruction_state` + `instruction_blob`) — the fidelity proof that what was
760
+ measured is what was generated. A claim may only rest on a run whose
761
+ provenance shows the intended global **and** project artifacts were loaded.
762
+
763
+ ### Signal vs noise
764
+
765
+ Wall time is a lead, not a criterion: same-day spread reaches ~8× in scenario
766
+ A and ~13× in B. An effect is **confirmed** only when it exceeds the same-day
767
+ spread (or demands a larger N); otherwise it is recorded as **inconclusive**.
768
+ Prefer token medians, which are far more stable. Scenario A's wall is not a
769
+ claim metric — it performs real web research, so network variance dominates.
770
+
771
+ ### First-request proxy and F1 evidence
772
+
773
+ The injected instruction payload only lands on the first request, so the
774
+ tree-total `tokens.input` (summed over every turn) is dominated by
775
+ session-length variance. Each run also records `firstRequest` — the first
776
+ assistant row's `tokens.input` + `tokens.cache.read` — the proxy that isolates
777
+ the payload.
778
+
779
+ An interleaved OpenCode v2.0.29 run (C1/K1…C5/K5, N=5/arm) measured
780
+ first-request context at 17555 tokens for control and 15489 for candidate
781
+ (**−2066, −11.8%**; within-arm spread 14 tokens in each arm). D4 confirmed
782
+ 22350 B loaded for control and 13400 B for candidate (−8950 B, −40.0%); the
783
+ pre-registered correctness oracle passed 5/5 in both arms. Themis accepted F1
784
+ with **PASS_WITH_NOTES**. The first-request effect is confirmed against
785
+ within-arm spread. Tree-total input fell from a median 47594 to 38253
786
+ tokens, but ranges overlap and the ≤30k hypothesis was not met. Wall medians
787
+ were 30.078 s and 24.130 s, with high spread; tree-total input and wall remain
788
+ **inconclusive**. No wall-time or monetary-cost gain is claimed. Details and
789
+ per-run records: `bench/results/f1-di-v229-interleaved-2026-10-14.md`.
790
+ The earlier sequential pair remains supporting-only in
791
+ `bench/results/f1-di-v229-clean-2026-10-14.md`. Earlier exploratory runs are
792
+ retained separately in `bench/results/f1-double-injection.md` and
793
+ `bench/results/f1-di-v229-preliminary-2026-10-14.md`.
794
+
795
+ ### Explicit root specialist result (F3a)
796
+
797
+ For the bounded one-shot scenario B, explicitly starting Talos as the root
798
+ produced the oracle-passing artifact in 5/5 runs with no child session or
799
+ `subagent` call. In the same-day paired sample, Talos used 16,171 median input
800
+ tokens per run versus 33,301 for Zeus (−51.4%); this is a descriptive result
801
+ for this task, model, and configuration, not a general routing or monetary
802
+ savings claim. Wall time is inconclusive: medians were 30.347 s and 31.893 s,
803
+ the ranges overlap widely, and Talos had the higher p80. No automatic
804
+ LLM-selected root routing is introduced by this result.
805
+
806
+ A final integration check after F4A/F3b initially completed 4/5 Talos artifacts.
807
+ Talos's prompt now says to finish named files with native `write`/`edit` tools
808
+ and not confuse MCP tool discovery with the host's native tool catalog. The
809
+ failure remains recorded in
810
+ [`beta10-final-integration-2026-10-15.md`](bench/results/beta10-final-integration-2026-10-15.md).
811
+ A new pre-registered cohort on that prompt passed the artifact oracle in 5/5
812
+ Talos runs with zero children or `subagent` calls. Its first-request context
813
+ proxy was 9,675 tokens versus 13,009 for Zeus (−25.6%, confirmed against the
814
+ same-arm spread). Tree-total input is descriptive, and wall time remains
815
+ inconclusive. See the [follow-up report](bench/results/beta10-final-integration-followup-2026-10-15.md)
816
+ and [raw run record](bench/results/beta10-final-integration-followup-20261015.json).
817
+
818
+ The v2.0.29 harness requires explicit agent and fixture selection, and proves
819
+ global plus project instruction provenance for each run:
820
+
821
+ ```bash
822
+ BENCH_MODEL=opencode-go/glm-5.3-flash node bench/bin/bench.mjs \
823
+ --scenario B --runs 5 --profile double-injection \
824
+ --global-agents AGENTS.global.md --project-agents AGENTS.md \
825
+ --agents-dir src/agents --skills-dir src/skills \
826
+ --root-agent talos --control-root-agent zeus --paired \
827
+ --out bench/results/phase3-root-paired-20261014.json
828
+ ```
829
+
830
+ Themis reviewed the harness and evidence (**PASS_WITH_NOTES**). Full method,
831
+ oracle audit, limitations, and per-run data are in
832
+ `bench/results/f3a-root-paired-2026-10-14.md` and
833
+ `bench/results/phase3-root-paired-20261014.json`.
834
+
835
+ ### Bounded workspace reads (F3b)
836
+
837
+ On OpenCode v2.0.29, the V2 plugin can expose `pantheon_bounded_read` when the
838
+ host provides its native permission and session APIs. Only a root Zeus session
839
+ can use it. Each call asks the host for `read` permission to one exact
840
+ workspace-relative file, with an empty save scope; content is read only after
841
+ approval and is scanned by the existing fail-closed secret scanner. Limits are
842
+ two calls and two distinct files, 400 lines and 32 KiB per assistant message.
843
+ Traversal, symlinks, directories, sensitive scopes, recognized binary/media
844
+ formats, and scanner failures are rejected. This is scoped to a local workspace
845
+ without concurrent writers; it does not claim adversarial filesystem isolation
846
+ or whole-file binary classification. The host-backed evidence is in
847
+ [`bench/results/f3b-v229-bounded-read-2026-10-15.md`](bench/results/f3b-v229-bounded-read-2026-10-15.md).
848
+
849
+
372
850
  ## Plugin V2 TypeScript coverage
373
851
 
374
852
  `npm run coverage:plugin-v2` runs the `tests/pantheon/*.test.ts` suite with
@@ -383,7 +861,7 @@ Env overrides:
383
861
  |----------|---------|---------|
384
862
  | `PANTHEON_SANDBOX_ROOT` | `~/pantheon-sandbox` | Sandbox root (refused if unsafe for `--reset`) |
385
863
  | `OPENCODE_V1_SPEC` | `opencode-ai@1.18.18` | Configures the V1 plugin only; the V2-only sandbox runner does not consume or support it |
386
- | `OPENCODE_V2_SPEC` | `@opencode-ai/cli@beta` | npm spec providing the `opencode2` binary |
864
+ | `OPENCODE_V2_SPEC` | `@opencode/cli@latest` | npm spec providing the `opencode2` binary |
387
865
  | `PANTHEON_SANDBOX_MODEL` | `opencode-go/mimo-v2.5` | Model used by init and prompts |
388
866
  | `PANTHEON_PROMPT_TIMEOUT` | `300` | Per-prompt timeout in seconds |
389
867
 
@@ -392,7 +870,7 @@ in the sandbox root) · `2` usage error · `3` sandbox not prepared.
392
870
 
393
871
  ### Intentional memory MCP divergence
394
872
 
395
- `scripts/memory_mcp_server.py` and `src/mcp/memory_mcp_server.py` are
873
+ `scripts/memory_mcp.py` and `src/mcp/memory_mcp.py` are
396
874
  intentionally different. The standalone `scripts/` copy keeps the lightweight
397
875
  `memory_*` contract; the installed `src/mcp/` copy additionally exposes the
398
876
  optional codemap schema and `code_index`, `code_query`, and `code_neighbors`.
@@ -401,13 +879,18 @@ with the other.
401
879
 
402
880
  ### Task-result guard
403
881
 
404
- A `task-result-guard` intercepts calls to the native `task()` tool where the
405
- child session returns an empty or missing result. Instead of surfacing a silent
882
+ A `task-result-guard` intercepts host/API child-session dispatch calls where the
883
+ child session returns an empty or missing result. In the reported Beta 10 Zeus
884
+ root, dispatch is exposed as `subagent`; host/API `task()` terminology does not
885
+ guarantee a root tool by that name. Instead of surfacing a silent
406
886
  `completed` with no payload (which confuses the orchestrator), the guard now
407
887
  raises an explicit error. This catches the common free-tier failure mode where a
408
888
  child session exceeds the uncached-prefill token budget (`BackendAdmissionRejected`)
409
- and returns nothing. Prefer `background=true` dispatches with an explicit
410
- `task_status(wait=true)` fan-in so large payloads are collected deterministically.
889
+ and returns nothing. Background dispatch and status/fan-in facilities are
890
+ runtime-dependent. The reported Beta 10 Zeus root did not expose `task_status`;
891
+ use synchronous `subagent` dispatch or wait for a host-provided completion
892
+ notification when no status tool is visible. Do not claim deterministic fan-in
893
+ or job recovery without an exposed, verified mechanism.
411
894
 
412
895
 
413
896
  ## Configuration (environment variables)
@@ -441,6 +924,32 @@ Ideas, bug reports, documentation improvements, and code contributions are
441
924
  welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening an issue
442
925
  or pull request.
443
926
 
927
+ ### Reproducible benchmark evidence
928
+
929
+ The beta2 benchmark records reported token usage separately from its
930
+ character-based fallback estimate; source bytes and logical line counts remain
931
+ provenance data and are never converted into tokens. Cross-report comparisons
932
+ require matching model/config provenance and reported OpenCode usage. Offline
933
+ fixture responses are mocked: their reported values are labeled separately,
934
+ excluded from `actual` usage, and blocked from comparative metrics and GEPA.
935
+ Fixture quality outcomes test the offline contract; they are not evaluation
936
+ evidence.
937
+ Reproduce the
938
+ checked-in AGENTS.md history manifest with
939
+ `python3 -m benchmarks.beta2.provenance --output benchmarks/beta2/fixtures/agent-baseline-provenance.json`.
940
+ For OpenCode benchmark runs, provide `--model` and `--config-sha256` so reports
941
+ can be compared safely.
942
+
943
+ ### Project-level `opencode.json`
944
+
945
+ The published package carries no `opencode.json` template. `init` still writes
946
+ your project's `opencode.json` itself, and the few product defaults an install
947
+ seeds — `default_agent: zeus` and `permission.skill` — come from the installer
948
+ code, not from a packaged file. Contributors who keep a project-level
949
+ `opencode.json` for their own OpenCode sessions should leave it untracked —
950
+ `.git/info/exclude` per clone, or a shared `.gitignore` decision — rather than
951
+ versioning it.
952
+
444
953
  ## Citation and DOI
445
954
 
446
955
  Pantheon is released under the [MIT License](LICENSE). Cite the