pantheon-opencode 1.5.2 → 1.6.0-beta.10

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 (146) hide show
  1. package/.pantheon/code-mode/README.md +4 -2
  2. package/.pantheon/code-mode/checkpoint_session.py +138 -102
  3. package/.pantheon/code-mode/compress-inline.py +102 -57
  4. package/.pantheon/code-mode/manifest.json +2 -8
  5. package/AGENTS.global.md +182 -0
  6. package/AGENTS.md +10 -891
  7. package/CHANGELOG.md +343 -0
  8. package/README.md +577 -79
  9. package/README.pt-BR.md +345 -60
  10. package/bin/pantheon-init.mjs +26 -3
  11. package/commands/pantheon-audit.md +7 -1
  12. package/commands/pantheon-consolidate.md +1 -1
  13. package/commands/pantheon-deepwork.md +14 -3
  14. package/commands/pantheon-focus.md +17 -0
  15. package/commands/pantheon-optimize.md +2 -2
  16. package/commands/pantheon-vision.md +61 -12
  17. package/package.json +30 -11
  18. package/plugin.json +2 -2
  19. package/pyproject.toml +11 -16
  20. package/scripts/build-agents-md.mjs +376 -58
  21. package/scripts/changelog-merge.mjs +161 -0
  22. package/scripts/check-plugin-v2-coverage.mjs +213 -0
  23. package/scripts/ci-validate-yaml.py +7 -5
  24. package/scripts/{code_mode_server.py → code_mode.py} +119 -27
  25. package/scripts/cost.mjs +244 -41
  26. package/scripts/doctor.mjs +236 -29
  27. package/scripts/install/agents-md.mjs +4 -4
  28. package/scripts/install/cli-ui.mjs +1 -1
  29. package/scripts/install/config-migration.mjs +319 -6
  30. package/scripts/install/health-check.mjs +26 -37
  31. package/scripts/install/interactive.mjs +7 -7
  32. package/scripts/install/migrate.mjs +1 -1
  33. package/scripts/install/opencode-version.mjs +246 -4
  34. package/scripts/install/opencode.mjs +627 -179
  35. package/scripts/install/plugin.mjs +43 -9
  36. package/scripts/install/shared.mjs +120 -12
  37. package/scripts/install/vision-prompt.mjs +161 -0
  38. package/scripts/install-mcp.mjs +28 -9
  39. package/scripts/{mcp_persistence_server.py → mcp_persistence.py} +392 -78
  40. package/{src/mcp/mcp_resources_server.py → scripts/mcp_resources.py} +2 -2
  41. package/scripts/{memory_mcp_server.py → memory_mcp.py} +372 -198
  42. package/scripts/postinstall.mjs +0 -7
  43. package/scripts/probe-context-rehydrate.mjs +36 -13
  44. package/scripts/probe-pantheon-cost.mjs +71 -15
  45. package/scripts/prune.mjs +2 -2
  46. package/scripts/run-node-tests.mjs +189 -0
  47. package/scripts/sync-artifacts.mjs +26 -13
  48. package/scripts/sync-tui.mjs +24 -3
  49. package/scripts/test-opencode-v2-sandbox.sh +1913 -0
  50. package/scripts/validate-routing.mjs +43 -3
  51. package/scripts/validate-sandbox-install.mjs +138 -0
  52. package/scripts/versioning.mjs +13 -1
  53. package/scripts/vision-status.mjs +682 -0
  54. package/skills-lock.json +15 -16
  55. package/src/agents/aphrodite.md +20 -37
  56. package/src/agents/apollo.md +7 -3
  57. package/src/agents/athena.md +63 -35
  58. package/src/agents/demeter.md +8 -28
  59. package/src/agents/gaia.md +1 -1
  60. package/src/agents/hephaestus.md +10 -21
  61. package/src/agents/hermes.md +15 -38
  62. package/src/agents/iris.md +1 -1
  63. package/src/agents/mnemosyne.md +23 -60
  64. package/src/agents/nyx.md +1 -1
  65. package/src/agents/prometheus.md +16 -20
  66. package/src/agents/talos.md +10 -2
  67. package/src/agents/themis.md +5 -5
  68. package/src/agents/zeus.md +99 -130
  69. package/src/instructions/agent-return-format.instructions.md +18 -3
  70. package/src/instructions/backend-standards.instructions.md +2 -0
  71. package/src/instructions/frontend-standards.instructions.md +3 -1
  72. package/src/instructions/memory-protocol.instructions.md +11 -110
  73. package/src/instructions/zeus-anti-stall.instructions.md +14 -61
  74. package/src/instructions/zeus-communication-rules.instructions.md +0 -1
  75. package/src/instructions/zeus-council-synthesis.instructions.md +32 -260
  76. package/src/instructions/zeus-memory-operations.instructions.md +26 -0
  77. package/src/instructions/zeus-timeout-retry.instructions.md +40 -87
  78. package/src/mcp/{code_mode_server.py → code_mode.py} +119 -27
  79. package/src/mcp/eval_store.py +2 -2
  80. package/src/mcp/mcp_codemap_module.py +559 -129
  81. package/src/mcp/{mcp_persistence_server.py → mcp_persistence.py} +392 -78
  82. package/{scripts/mcp_resources_server.py → src/mcp/mcp_resources.py} +2 -2
  83. package/src/mcp/{memory_mcp_server.py → memory_mcp.py} +373 -250
  84. package/src/mcp/{pantheon_vision_server.py → pantheon_vision.py} +396 -40
  85. package/src/mcp/requirements-mcp.txt +10 -6
  86. package/src/mcp/requirements-vision.txt +4 -2
  87. package/src/pantheon/cost-command.ts +399 -101
  88. package/src/pantheon/delegation-enforce.ts +10 -1
  89. package/src/pantheon/model-command.ts +18 -7
  90. package/src/pantheon/presets.d.mts +0 -8
  91. package/src/pantheon/presets.mjs +5 -32
  92. package/src/pantheon/todo-enforcer.ts +1 -1
  93. package/src/pantheon/v2-bounded-read.ts +640 -0
  94. package/src/pantheon/v2-delegation-enforce.ts +214 -0
  95. package/src/pantheon/v2-execute-after.ts +186 -0
  96. package/src/pantheon/v2-tools.ts +429 -0
  97. package/src/pantheon/v2-unsupported.d.mts +11 -0
  98. package/src/pantheon/v2-unsupported.mjs +90 -0
  99. package/src/pantheon/v2-vision.ts +622 -0
  100. package/src/pantheon/vision-local.ts +531 -0
  101. package/src/pantheon/vision.ts +1161 -116
  102. package/src/plugin-v2.ts +1244 -288
  103. package/src/plugin.ts +12 -9
  104. package/src/plugins/tui/dist/server.d.ts +1 -2
  105. package/src/plugins/tui/dist/server.d.ts.map +1 -1
  106. package/src/plugins/tui/dist/tui-v2.d.ts +154 -0
  107. package/src/plugins/tui/dist/tui-v2.d.ts.map +1 -0
  108. package/src/plugins/tui/dist/tui-v2.js +659 -0
  109. package/src/plugins/tui/dist/tui-v2.js.map +1 -0
  110. package/src/plugins/tui/dist/tui.d.ts +85 -82
  111. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  112. package/src/plugins/tui/dist/tui.js +72 -59
  113. package/src/plugins/tui/index-v2.ts +6 -0
  114. package/src/plugins/tui/package-lock.json +5504 -1341
  115. package/src/plugins/tui/package.json +25 -15
  116. package/src/plugins/tui/src/index-v2.tsx +734 -0
  117. package/src/plugins/tui/src/index.tsx +106 -85
  118. package/src/plugins/tui/src/opencode-v2.d.ts +13 -0
  119. package/src/plugins/tui/tsconfig.build.json +5 -0
  120. package/src/plugins/tui/tsconfig.typecheck.json +5 -0
  121. package/src/plugins/tui/tsdown.config.ts +22 -2
  122. package/src/plugins/tui/tui-v2.ts +2 -0
  123. package/src/plugins/tui/tui.ts +3 -0
  124. package/src/routing.yml +11 -22
  125. package/src/skills/README.md +34 -39
  126. package/src/skills/agent-coordination/SKILL.md +7 -2
  127. package/src/skills/artifact-management/SKILL.md +12 -5
  128. package/src/skills/auto-continue/SKILL.md +54 -95
  129. package/src/skills/code-review-checklist/SKILL.md +5 -5
  130. package/src/skills/context-compression/SKILL.md +26 -879
  131. package/src/skills/session-goal/SKILL.md +14 -5
  132. package/src/skills/spec-driven-development/SKILL.md +3 -1
  133. package/src/skills/tdd-with-agents/SKILL.md +18 -10
  134. package/.pantheon/code-mode/eval-llm-judge.py +0 -255
  135. package/.pantheon/code-mode/eval-monte-carlo.py +0 -368
  136. package/.pantheon/code-mode/eval-run.py +0 -197
  137. package/.pantheon/code-mode/eval-static.py +0 -321
  138. package/.pantheon/code-mode/session-end-save.py +0 -186
  139. package/.pantheon/code-mode/session-end-save.sh +0 -18
  140. package/opencode.json +0 -58
  141. package/scripts/recover-zenodo-deposition.mjs +0 -146
  142. package/scripts/test-opencode-v1-v2-sandbox.sh +0 -862
  143. package/scripts/validate-zenodo-release.mjs +0 -154
  144. package/scripts/zenodo-artifact.mjs +0 -94
  145. package/scripts/zenodo-release-state.mjs +0 -53
  146. package/src/skills/orchestration-workflow/SKILL.md +0 -311
package/README.md CHANGED
@@ -10,7 +10,7 @@ want useful structure without giving up control of their code.
10
10
 
11
11
  [![Version](https://img.shields.io/github/v/release/ils15/pantheon-opencode?label=version)](https://github.com/ils15/pantheon-opencode/releases/latest)
12
12
  [![CI](https://img.shields.io/github/actions/workflow/status/ils15/pantheon-opencode/ci.yml?branch=main&label=CI)](https://github.com/ils15/pantheon-opencode/actions)
13
- [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22306637.svg)](https://doi.org/10.5281/zenodo.22306637)
13
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.22650136.svg)](https://doi.org/10.5281/zenodo.22650136)
14
14
 
15
15
  ## What is it?
16
16
 
@@ -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
@@ -50,6 +52,11 @@ opencode
50
52
  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).
55
+ The bundled Python MCP servers use the pinned MCP SDK 2.2.0 and standalone
56
+ FastMCP 4.0.10 runtime; see the [MCP guide](docs/MCP.md).
57
+ The installer also turns each agent's `mcp_tools` allowlist into native
58
+ per-agent permissions for OpenCode V1 and V2; see the
59
+ [agent MCP reference](docs/AGENT-MCP.md).
53
60
 
54
61
  ## A simple example
55
62
 
@@ -60,6 +67,10 @@ Once OpenCode is running, describe the outcome you want:
60
67
  ```
61
68
 
62
69
  Pantheon helps turn that request into a plan and a sequence of reviewed steps.
70
+ When you ask for a plan or start `/pantheon-deepwork`, Athena returns the full
71
+ plan to Zeus, who displays it in the main conversation before asking for
72
+ approval. The approval prompt will not point to a plan hidden in a child-agent
73
+ conversation.
63
74
 
64
75
  ## Who is it for?
65
76
 
@@ -76,13 +87,100 @@ clear handoff between stages of work.
76
87
  - Project memory that helps preserve relevant context between sessions.
77
88
  - Optional integrations for common development tasks.
78
89
 
90
+ Memory is used selectively: the coordinator searches once only when project
91
+ history could affect a task, then passes relevant results to specialists. Store
92
+ one concise, reusable outcome at task completion when it will matter later;
93
+ routine child summaries and phase updates stay in the current handoff. The
94
+ session-checkpoint tools are separate, optional persistence APIs and are not
95
+ called automatically by compaction hooks. See [Memory usage](docs/MEMORY.md).
96
+
79
97
  ## Status
80
98
 
81
- Operational checkout version: **v1.5.0-beta.2** (candidate; publication is not
82
- 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.
99
+ Candidate version: the manifests in this checkout express the version being
100
+ prepared, not a confirmed publication. A beta is published only after the
101
+ Release workflow completes successfully. To check which version is actually
102
+ published, consult the npm `beta` dist-tag. Pantheon is designed for OpenCode
103
+ and depends on the availability and configuration of OpenCode and any optional
104
+ services you choose to use. Check the
105
+ [releases](https://github.com/ils15/pantheon-opencode/releases) and
106
+ [changelog](CHANGELOG.md) for the latest published changes.
107
+
108
+ ## Compatibility and support policy
109
+
110
+ > ### ⚠️ 1.6.x is the LAST line that supports OpenCode 1.X
111
+ >
112
+ > **OpenCode 1.X support ends after the 1.6 line.** From **1.7 onward, every
113
+ > release is a breaking change targeting OpenCode 2.** A 1.6.x install is the
114
+ > last one that runs on an OpenCode 1.X host; do not plan an upgrade across the
115
+ > 1.6 → 1.7 boundary while your host is still on OpenCode 1.X.
116
+
117
+ | Pantheon line | OpenCode 1.X host | OpenCode 2 host |
118
+ |---|---|---|
119
+ | **1.6.x** (current line) | **Supported.** V1 plugin contract | Supported, reduced surface — V2 contract |
120
+ | **1.7 and later** | **Not supported.** Breaking change | Target of the 1.7+ work |
121
+
122
+ Both columns of the 1.6.x row are real, and they are not the same claim. Read
123
+ [the plugin contract](#opencode-v1v2--dual-version-160-beta1) for what each
124
+ generation actually registers.
125
+
126
+ ### What "supported on OpenCode 1.X" concretely means
127
+
128
+ On the 1.6 line, an OpenCode 1.X host gets the **V1 plugin**:
129
+ `src/plugin.ts` plus `src/plugins/pantheon-hooks.ts`, registered under the
130
+ singular `plugin` config key. That surface registers 6 tools
131
+ (`hashline_edit`, the 3 `pantheon_goal_*` tools, `pantheon_cost`,
132
+ `pantheon_model`), the BackgroundJobBoard lifecycle, the V1 event/tool hooks and
133
+ the V1 compaction path. It is selected automatically: the generation gate
134
+ (`--opencode-version`, else `OPENCODE_VERSION`, else a `OPENCODE_BIN` basename of
135
+ `opencode2`, else a `--version` probe where major ≥ 2 selects V2) resolves a 1.X
136
+ host to V1.
137
+
138
+ The two generations are exclusive. The installer strips Pantheon references from
139
+ both config shapes and registers only the selected one, so a 1.X host never
140
+ loads the V2 plugin and a V2 host never loads the V1 plugin. Forcing V2 on a 1.X
141
+ host is an unsupported mixed/wrong-generation configuration, not a supported mode.
142
+
143
+ ### What the OpenCode 2 path is — and is not
144
+
145
+ **Not inert.** The V2 path (`src/plugin-v2`, registered as a directory because
146
+ the V2 loader rejects bare file paths) registers real tools with real
147
+ `input`/`output` schemas via `ctx.tool.transform()`, 5 event subscriptions,
148
+ session hooks and tool/permission hooks that enforce read-only sessions and the
149
+ caller→target delegation matrix. The matrix applies only to Pantheon-managed
150
+ targets: Zeus may delegate to any registered agent; Athena and Hermes may target
151
+ Apollo only; other Pantheon callers and unknown callers are denied. Child
152
+ sessions cannot delegate to Pantheon agents. Calls between native/custom agents
153
+ remain under host permissions. The V2 path never adds an allow grant or
154
+ overrides an explicit host deny; missing or mismatched identity fails closed
155
+ for the attempted Pantheon delegation. The 3 goal tools remain unsupported and
156
+ are reported as `goal-tools`.
157
+
158
+ When the host successfully registers V2's permission hook, Pantheon does not
159
+ trust `permission.evaluate.agent` by itself for a Pantheon target. It matches
160
+ the permission source's session, message and tool-call IDs to a short-lived
161
+ record from the host's `tool.execute.before` event, then requires the host
162
+ session to have no `parentID` before applying the matrix. This admits valid
163
+ root dispatches and denies child-session dispatches; missing, stale, replayed
164
+ or mismatched origins are denied. OpenCode v2.0.29 runtime evidence is in
165
+ [`bench/results/f4a-v229-admission-identity-2026-10-14.md`](bench/results/f4a-v229-admission-identity-2026-10-14.md).
166
+
167
+ **Partial, with the exercised host version made explicit.** The V2 tool canary
168
+ started against `opencode 2.0.22`, a version that no npm channel publishes.
169
+ The current F4A rerun now uses the installable **OpenCode v2.0.29** binary and
170
+ passes 18/18 checks with permission probes: the real plugin registers and runs
171
+ its tools, admits the valid Zeus→Apollo root dispatch, rejects an invalid edge
172
+ before child creation, and rejects a matrix-valid dispatch from a child. The
173
+ remaining V2 surface still has unsupported goal tools and registration-only
174
+ session/compaction hooks, so the 1.6 line is not full V2 feature parity.
175
+
176
+ ### Verified against which OpenCode versions
177
+
178
+ | What | Verified against | How |
179
+ |---|---|---|
180
+ | 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 |
181
+ | 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 |
182
+ | 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) |
183
+ | 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 | — |
86
184
 
87
185
  ## Delegation (native `task()`)
88
186
 
@@ -93,6 +191,28 @@ there is no Pantheon-specific delegation tool surface to configure. See
93
191
  [ADR-0011](.pantheon/memory-bank/adr/0011-delegation-engine-contract.md) for the
94
192
  historical engine contract.
95
193
 
194
+ This beta.8 candidate combines native per-agent MCP permissions for OpenCode V1
195
+ and V2, V2 delegation-permission alignment, simpler memory/compression
196
+ instructions, and a sandbox check that proves the installed package matches
197
+ this checkout instead of silently accepting stale npm-cached code. The isolated
198
+ sandbox launcher also pins the OpenCode background service to its dedicated
199
+ loopback port, rather than colliding with another OpenCode installation. The Zeus
200
+ prompt reuses one task-start memory-search result for context and routing;
201
+ runtime call counts and latency have not been measured. A beta.7 V2 dispatch
202
+ failed because the permission hook passed a bare string to `ctx.session.get`
203
+ instead of the V2 input object `{ sessionID }`; beta.8 corrects the call and
204
+ adds contract tests for the success and failure cases. The user subsequently
205
+ reported a successful Hermes liveness delegation in the active session, but
206
+ freshly restarting and testing the beta.8 sandbox remains the end-to-end gate.
207
+ The original report also included a separate `GET /api/session/{id}/permission`
208
+ 404; the session ID exists in the beta7-candidate database but not the default
209
+ global database. Both OpenCode services were running, so the 404 in the global
210
+ log may come from the other instance; this remains an end-to-end verification
211
+ item.
212
+
213
+ Delegated tasks get at most one retry after an initial timeout or failure. If
214
+ that retry fails, the configured fallback or escalation chain applies.
215
+
96
216
  ## Cost tool backend
97
217
 
98
218
  `pantheon_cost` prefers a read-only `node:sqlite` backend against the selected
@@ -132,6 +252,10 @@ only when they are explicitly approved. Approval is recorded in
132
252
  - **Hash mismatch → `CORRUPT_DATA`.** The SHA-256 of the file on disk must
133
253
  match the manifest entry, so edits after approval are detected.
134
254
 
255
+ Session persistence is provided by the persistence MCP server. The bundled
256
+ code-mode payload contains execution helpers only and does not export or back
257
+ up a database.
258
+
135
259
  Approve or re-approve a script with the `approve_code_script` MCP tool:
136
260
 
137
261
  ```
@@ -153,28 +277,25 @@ once a project directory is selected, a missing or corrupt manifest fails
153
277
  closed instead of falling back. `doctor` validates the manifest and every
154
278
  script's SHA-256 without regenerating it.
155
279
 
280
+ The local `.pantheon/code-mode/eval-*.py` helpers and any Promptfoo/evaluation
281
+ assets are development-only inputs and are excluded from the npm tarball and
282
+ from the runtime manifest. `src/mcp/eval_store.py` is different: it is a
283
+ shipped runtime dependency of the MCP resources server, not an evaluation
284
+ asset, so it remains packaged.
285
+
156
286
 
157
- ## What's new in 1.5.0-beta.2
287
+ ## What's new in 1.6.0-beta.1
158
288
 
159
- - OpenCode-only installer: platform guides consolidated into a single
160
- [OpenCode guide](docs/platforms/opencode.md).
161
- - New `uninstall` CLI with project and global scopes and ownership checks:
162
- `node scripts/uninstall.mjs --project|--global [--dry-run] [--force]`.
163
- - Hardened MCP resources: fixed `pantheon://agents` listing and added
164
- symlink/traversal protection for resource paths.
165
- - OpenCode V2 compatibility: `plugins` / `mcp.servers.enabled` config merge
166
- and PWD-correct stdio MCP launch.
167
- - Expanded `doctor` and install health checks.
168
- - Sandbox validator for global installs (`scripts/test-opencode-v1-v2-sandbox.sh`)
169
- covering OpenCode V1/V2 side by side — see
170
- [Sandbox validation](#sandbox-validation-v1v2).
171
- - Beta2 agent-economy policy: direct native delegation, bounded compaction
172
- carry-forward, compact context encoding, and quality floors.
173
- - A `--prompts` installer flag is planned for a future release.
289
+ - First beta compatible with the OpenCode 2 plugin contract.
290
+ - Removed the legacy vector-memory pipeline while preserving SQLite FTS5/BM25
291
+ keyword search and the `code_*` codemap tools.
292
+ - CI and release validation are fail-closed; `doctor` checks and the V2-only
293
+ sandbox validator cover the OpenCode 2 installation path.
174
294
 
175
- ## OpenCode V1/V2 — Dual Version (1.5.0-beta.2)
295
+ ## OpenCode V1/V2 — Dual Version (1.6.0-beta.1)
176
296
 
177
- Pantheon has two **exclusive** OpenCode plugin contracts. Ordinary OpenCode
297
+ This is the first beta compatible with OpenCode 2. Pantheon has two **exclusive** OpenCode
298
+ plugin contracts. Ordinary OpenCode
178
299
  configuration may be shared, but the Pantheon plugin registration is selected
179
300
  per installation; V1 and V2 Pantheon plugins must never be registered together.
180
301
 
@@ -182,15 +303,128 @@ per installation; V1 and V2 Pantheon plugins must never be registered together.
182
303
  |---|---|---|
183
304
  | OpenCode config key | singular `plugin` | plural `plugins` |
184
305
  | Pantheon registration | `src/plugin.ts` plus `src/plugins/pantheon-hooks.ts` | `<installed>/src/plugin-v2` directory (`index.ts` re-exports `src/plugin-v2.ts`) |
185
- | 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 |
306
+ | 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 |
186
307
  | V1 APIs | Registered | Own tool definitions via `ctx.tool.transform()` — not the V1 plugin path |
187
308
 
188
- The V2 plugin provides 6 orchestration tools (`hashline_edit`,
189
- `pantheon_goal_create`, `pantheon_goal_get`, `pantheon_goal_update`,
190
- `pantheon_cost`, `pantheon_model`), 4 event subscriptions (`session.created`,
191
- `session.idle`, `session.error`, `session.compacted`), session hooks (`prompt`,
192
- `context`), and tool hooks (`execute.before`, `execute.after`). The only
193
- unsupported V2 feature is `legacy-hooks` (the V1-specific hook surface).
309
+ The V2 plugin provides 3 orchestration tools (`hashline_edit`, `pantheon_cost`,
310
+ `pantheon_model`), 5 event subscriptions (`session.created`, `session.idle`,
311
+ `session.deleted`, `session.error`, `session.compacted`), session hooks
312
+ (`prompt`, `context`),
313
+ and a tool `execute.before` hook that enforces read-only sessions. The 3 goal
314
+ tools are **not**
315
+ registered on V2: the goal loop needs a `GoalStore`, a `GoalLoopClient` and a
316
+ `BackgroundJobBoard`, none of which the V2 `PluginContext` exposes, so the gap
317
+ is reported as the `goal-tools` unsupported-feature marker. Use the V1
318
+ contract if you need the goal loop.
319
+
320
+ V2 also registers `vision_describe` when `ctx.model.list()` is available. It
321
+ checks the live catalog for image input, prefers the paid OpenCode Go
322
+ `opencode-go/mimo-v2.6-flash` model (free-tier models are excluded from automatic
323
+ selection — the direct gateway rejects them with HTTP 403, they only work from
324
+ inside OpenCode; an explicit `PANTHEON_VISION_MODEL`/config/preset pin is still
325
+ honored, so its 403 surfaces as an actionable error rather than a silent
326
+ substitution), and
327
+ sends the image directly to the authenticated OpenCode Zen endpoint. The request
328
+ carries the live session id as the Zen-only `x-opencode-session` header the
329
+ gateway requires (never sent to a non-Zen provider, and never logged). OpenCode
330
+ credentials come from
331
+ `opencode auth login` or `PANTHEON_OPENCODE_API_KEY`; if no supported model or
332
+ credentials are available, the tool returns an explicit error instead of a
333
+ fabricated description. This path works without the optional V1 bridge.
334
+ The V2 prompt hook also preserves image attachment references and tells Zeus
335
+ to inspect relevant visual context and pass the reference plus concise findings
336
+ to Aphrodite. Inline image bytes are not copied into persisted prompt text, and
337
+ an image does not authorize deleting or reverting unrelated work.
338
+
339
+ The chat-turn native path picks its vision model in a fixed precedence: the
340
+ `PANTHEON_VISION_MODEL` env var, then `visionModel` in `opencode-vision.json`,
341
+ then the active preset's `vision.model`, then automatic discovery in the host
342
+ provider catalog (`GET /config/providers`), then the built-in default. Discovery
343
+ is conservative and paid-only: it keeps only image-capable entries
344
+ (`capabilities.input.image`) whose provider is a trusted Zen provider or whose
345
+ `npm` package is OpenAI-compatible (`@ai-sdk/openai-compatible` /
346
+ `@ai-sdk/openai`), EXCLUDES free-tier entries (`-free` id or `cost.input <= 0`
347
+ — the direct gateway rejects them with HTTP 403), picks the cheapest remaining
348
+ entry, and caches the catalog for 60
349
+ seconds per process; an incompatible model simply falls through to the vision
350
+ MCP tool. The base URL comes from the catalog entry's `api.url`, and the legacy
351
+ Zen endpoints remain the no-catalog fallback. Credentials follow the endpoint:
352
+ for a Zen endpoint the shared `PANTHEON_OPENCODE_API_KEY` / `OPENCODE_API_KEY`
353
+ env keys are tried first, then the OpenCode auth store (the resolved provider
354
+ first, then the legacy `opencode-go` / `opencode` entries); for a non-Zen
355
+ endpoint only that provider's own auth-store entry is used — the shared env keys
356
+ and the legacy Zen entries are never attached to a non-Zen base URL. An
357
+ explicitly configured, qualifying vision model always wins over discovery — a
358
+ pin that does not qualify (non-Zen, text-only, or absent from the catalog) is
359
+ discarded and the automatic selection takes over. `pantheon-opencode init`
360
+ offers an optional vision step in interactive TTY sessions — automatic discovery
361
+ is the default and writes nothing; pinning merges `visionModel` into the global
362
+ `opencode-vision.json`; non-interactive runs never prompt and never write.
363
+ The `/pantheon-vision` command adds a model picker: `vision-status.mjs --list`
364
+ enumerates image-capable candidates — labelling whether the source is the live
365
+ host catalog or an offline `opencode models` heuristic, plus `free`/`paid` and
366
+ credential presence per provider (never the token) — and `--set` pins the chosen
367
+ `provider/model` with the same atomic merge.
368
+
369
+ The user's question is injected into the vision prompt on both the native and V2
370
+ paths, trimmed and capped at 2000 characters; the same image asked a different
371
+ question gets a fresh description, while an identical re-ask inside the cache
372
+ TTL reuses the stored text. Native describes are robust by design: the timeout
373
+ scales from a 20-second base at roughly 10 seconds per MB of payload up to a
374
+ 300-second ceiling, and a zero-dependency EXIF-orientation check (JPEG) adds a
375
+ textual hint so the vision model describes a rotated or mirrored image upright —
376
+ detection only, pixels are never rewritten, and the orientation is folded into
377
+ the cache key.
378
+
379
+ Vision degrades through an explicit chain — native → vision MCP tool → local.
380
+ When native is unavailable and the vision MCP tool is absent, the image is
381
+ replaced by deterministic local extraction: dimensions, format and size from the
382
+ PNG/JPEG/GIF/WebP header, dominant colors for RGB/RGBA PNG, and OCR only when a
383
+ `tesseract` binary is on `PATH`. It never fabricates content — the degradation is
384
+ stated explicitly. The `vision_describe` and `vision_ocr` MCP tools degrade to
385
+ that local mode when no gateway credential is configured, with no new
386
+ dependencies. (`vision_analyze` requires a gateway credential and returns an
387
+ explicit error instead.)
388
+ Deliberate trade-offs, recorded so they are not reversed by accident: Pantheon
389
+ describes each image once and keeps the text in history (council 2026-08-03)
390
+ rather than preserving the original image for per-turn re-observation;
391
+ describe-always favors zero added latency over an on-demand `describe_image`
392
+ tool. Multi-turn sanitization (`experimental.chat.messages.transform`) is a
393
+ mandatory invariant — it guarantees `image_url` / `FilePart` payloads never
394
+ reach a text-only provider and is not configurable. Bifrost stays opt-in and
395
+ never the default, per the credential incident in [SECURITY.md](SECURITY.md).
396
+
397
+ The V2 `execute.before` hook is the read-only enforcement point for that
398
+ surface: the host includes the active agent on the event, a read-only agent
399
+ (`apollo`, `gaia`) registers its session, and the shared
400
+ `createEnforcementGuard` throws to deny `edit`, `write`, `bash`, `task`,
401
+ `hashline_edit` and `pantheon_model`. It does **not** rely on the V1
402
+ `tool.execute.before` hook, which lives in `src/plugin.ts` and is not loaded
403
+ when only `plugin-v2` is configured. When the host registers the
404
+ `permission.evaluate` hook, it enforces the V2 caller/target matrix for
405
+ Pantheon-managed native `subagent` requests. If hook registration is
406
+ unavailable, the matrix is unavailable and the host remains in control.
407
+ Native-only resources pass through unchanged to OpenCode's permission policy;
408
+ mixed native/Pantheon and multi-target requests are denied. Pantheon matches
409
+ the permission source's session, message and tool-call IDs to a short-lived
410
+ record from `tool.execute.before`, cross-checks the captured host caller against
411
+ `permission.evaluate.agent`, and confirms the session has no `parentID`.
412
+ `permission.evaluate.agent` alone is not trusted because
413
+ `ctx.permission.assert` accepts a caller-supplied agent. Missing, stale,
414
+ replayed or mismatched origins fail closed, and explicit host denies remain
415
+ final. On OpenCode v2.0.29, a runtime canary admits Zeus→Apollo from a root,
416
+ denies a matrix-valid Athena→Apollo call from a child and rejects an invalid
417
+ edge before child creation; see the
418
+ [`F4A evidence`](bench/results/f4a-v229-admission-identity-2026-10-14.md).
419
+ V2 `execute.after` runs the completed-result chain:
420
+ `task-result-guard` → `context-sandbox` → `read-enhancer`.
421
+
422
+ Every V2 tool declares an `output` schema. OpenCode 2.0.x requires the
423
+ declaration and the resolved result to agree in both directions, so an
424
+ undeclared tool — or one that returns `output` without declaring it — fails on
425
+ every call. Unsupported V2 features are `legacy-hooks` (the V1-specific hook
426
+ surface), `catalog-transform`, `integration-transform`, `skill-transform` and
427
+ `goal-tools`.
194
428
 
195
429
  The package exposes both contracts as importable exports: `pantheon-opencode/plugin`
196
430
  (V1), `pantheon-opencode/plugin-v2` (V2) and `pantheon-opencode/v2-bridge`
@@ -209,28 +443,75 @@ npx pantheon-opencode init --opencode-version v2
209
443
  npx pantheon-opencode init --opencode-version auto
210
444
  ```
211
445
 
212
- `--version v1|v2|auto` is accepted as the older selector spelling when used
213
- after `init`. `auto` is conservative, not general platform autodetection:
214
- `OPENCODE_VERSION=v1|v2` wins; otherwise an `OPENCODE_BIN` ending in
215
- `opencode2` selects V2; every other case selects V1. The installer removes
216
- Pantheon references from both config shapes before writing only the selected
217
- Pantheon registration. Third-party entries are not converted or claimed by
218
- this rule.
219
-
220
- The V1-only `pantheon_cost` report can select its database with
221
- `PANTHEON_OPENCODE_VERSION=v1` or `v2` (`opencode.db` or `opencode-v2.db`).
222
- `PANTHEON_COST_DB=/absolute/path/to/opencode.db` takes precedence over the
223
- version selector, and an explicit `dbPath` supplied by the tool caller takes
224
- precedence over both. The resolver never probes the other version's database
225
- and reports an actionable error when the selected DB is missing or has an
226
- incompatible schema.
446
+ `auto` is the default. `--version v1|v2|auto` is accepted as the older
447
+ selector spelling when used after `init`. `auto` resolves the generation from
448
+ the host in this order: an explicit `OPENCODE_VERSION=v1|v2` wins; otherwise an
449
+ `OPENCODE_BIN` path ending in `opencode2` selects V2; otherwise the host
450
+ binary is asked for its `--version` and a major of 2 or more selects V2. Every
451
+ other case — an unreadable probe, an unparseable banner, or a banner whose
452
+ version-like tokens contradict each other with no tool name to break the tie —
453
+ warns once and falls back to V1, because a plural `plugins` directory entry on
454
+ an unknown 1.x host loses the plugin entirely.
455
+
456
+ The probe prefers the token that follows the tool name, so a runtime token
457
+ ahead of it (`node v22.1.0 (opencode 1.18.33)`) or a trailing build date
458
+ (`opencode v1.18.33 built 2026.10.04`) cannot flip the generation. The
459
+ installer removes Pantheon references from both config shapes before writing
460
+ only the selected Pantheon registration. Third-party entries are not converted
461
+ or claimed by this rule.
462
+
463
+ The `pantheon_cost` report resolves its database by PATH, in this order: an
464
+ explicit `dbPath` supplied by the tool caller, then
465
+ `PANTHEON_COST_DB=/absolute/path/to/opencode.db`, then `OPENCODE_DB`, then the
466
+ XDG default `opencode.db`. There is no per-version filename in that chain:
467
+ `opencode-v2.db` is not a host 2.x database — it is the name a sandbox gives
468
+ its own state database through `OPENCODE_DB`.
469
+
470
+ The v1/v2 distinction is the DETECTED SCHEMA, not the file name. The report
471
+ looks for the `message` and `session_message` tables and reads whichever the
472
+ open database actually holds, because a migrated database carries both
473
+ families at once. `PANTHEON_OPENCODE_VERSION=v1|v2` selects no file: it only
474
+ narrows an already-detected set to one table family, and fails fast when the
475
+ value is neither `v1` nor `v2` or when the requested family is not there. Left
476
+ unset, every detected family is read. A database carrying neither table comes
477
+ back as an actionable error (`CORRUPT_DATA`), and a ledger that exists but
478
+ yields no readable tokens as `UNSUPPORTED` — never as a successful empty
479
+ report.
227
480
 
228
481
  The installer still writes the compatibility settings required by the selected
229
482
  OpenCode host, such as `experimental.subagent_depth`; this does not convert a
230
483
  V1 plugin into V2 or provide V2 with V1 hooks.
231
484
 
485
+ Pantheon does not set a step ceiling on agents. On OpenCode 2 the `steps`
486
+ field is optional and has no default: absent means the host applies no native
487
+ limit. A config written before this change still carries the old value, and the
488
+ V2 install path strips it from every agent Pantheon manages — an agent you
489
+ define yourself is never touched, and the retiring V1 singular `agent` block
490
+ is left alone. Run the installer once to clear a stale value from an existing
491
+ config:
492
+
493
+ ```bash
494
+ npx pantheon-opencode init --opencode-version=v2
495
+ ```
496
+
497
+ Context control is not a Pantheon setting. Whatever budget an agent runs
498
+ against comes from the compaction settings of the OpenCode host you are
499
+ running, and Pantheon does not define them.
500
+
501
+ When a V1 `agent` block and a V2 `agents` block are both present — which is
502
+ what an installed `opencode.json` carries — the V1→V2 conversion merges them
503
+ instead of letting one replace the other. The V2 block is the base, the
504
+ framework-managed fields from the V1 block overwrite, and your own fields are
505
+ preserved, matching the precedence the installer already applies elsewhere.
506
+ Key order does not matter.
507
+
232
508
  ## Updating between releases (beta.5+)
233
509
 
510
+ > **Read [Compatibility and support policy](#compatibility-and-support-policy)
511
+ > before upgrading across a line boundary.** 1.6.x is the last line that
512
+ > supports an OpenCode 1.X host; 1.7+ is breaking-change territory targeting
513
+ > OpenCode 2. Within the 1.6 line, `update` is the supported path.
514
+
234
515
  One command keeps an existing installation current:
235
516
 
236
517
  ```bash
@@ -251,7 +532,14 @@ Two freshness guarantees back it up:
251
532
  - **Postinstall artifact sync** — after any `npm install`, copy-only artifacts
252
533
  (agents, skills, AGENTS.md, commands, MCP scripts, code-mode payload) are
253
534
  refreshed into the existing config dir automatically; you never have to
254
- re-run `init` just for file copies.
535
+ re-run `init` just for file copies. The global config dir receives the
536
+ repo-agnostic **global artifact** (`AGENTS.global.md`: agent table + shared
537
+ rules), while each project's own `AGENTS.md` carries only project-specific
538
+ setup — the two are carved to be disjoint (beta-10 latency work), so no rule
539
+ is paid for twice per session. Installs are conservative: content outside
540
+ the generated managed region (user house rules) is preserved verbatim, a
541
+ legacy fully-generated `AGENTS.md` upgrades in place, and any merge is
542
+ reported with a warning.
255
543
  - **Drift detection** — the installer stamps the installed version in
256
544
  `.pantheon/install-state.json` and `doctor` warns when the package is newer
257
545
  than the last sync, pointing at `update`. `doctor` also detects **plugin
@@ -313,6 +601,10 @@ pushes, merges, and tags never publish anything, and every validation gate is
313
601
  fail-closed: only an explicit PASS authorizes release evidence. See
314
602
  [docs/RELEASING.md](docs/RELEASING.md) for validation and recovery details.
315
603
 
604
+ For a candidate whose release section already exists, maintain its notes with
605
+ `node scripts/versioning.mjs changelog <version>`; this merges `[Unreleased]`
606
+ into that version without advancing the package version.
607
+
316
608
  Release validation keeps each manifest with its lockfile: the root
317
609
  `package.json` + `package-lock.json` and the TUI
318
610
  `src/plugins/tui/package.json` + `src/plugins/tui/package-lock.json`. Both use
@@ -321,39 +613,215 @@ Release validation keeps each manifest with its lockfile: the root
321
613
  SHA-256 of that same artifact, and binds the tarball and GitHub release to the
322
614
  full `TARGET_SHA`; a second pack is not interchangeable.
323
615
 
324
- ## Sandbox validation (V1/V2)
616
+ CI also rebuilds the TUI with `npm run build --prefix src/plugins/tui` and
617
+ checks every packaged `dist` artifact byte-for-byte against its checked-in
618
+ version, including final newlines. The build includes separate V1 and native
619
+ OpenCode V2 TUI entrypoints; `init` selects the entrypoint and sidebar slot API
620
+ for the host generation in use.
325
621
 
326
- `scripts/test-opencode-v1-v2-sandbox.sh` validates the globally installed
622
+ ## Sandbox validation (V2)
623
+
624
+ `scripts/test-opencode-v2-sandbox.sh` validates the globally installed
327
625
  package as a real user inside an isolated sandbox (own `HOME`, npm prefix and
328
- venv) — never the dev environment. It checks OpenCode V1 (`opencode`) and V2
329
- (`opencode2`) side by side: binaries, MCP connectivity, `doctor`, and — with
330
- `--prompts` — a prompt battery covering the `pantheon://agents` resource,
331
- memory store/recall, filesystem writes and agent delegation. The gate is
332
- fail-closed: every required check must return an explicit PASS; timeouts,
333
- auth/network/provider failures and missing prerequisites block the run.
626
+ venv) — never the dev environment. It checks the OpenCode V2 leg: the binary,
627
+ MCP connectivity, `doctor`, and — with `--prompts` — a prompt battery covering
628
+ the `pantheon://agents` resource, memory store/recall, filesystem writes and
629
+ agent delegation. The gate is fail-closed: every required check must return an
630
+ explicit PASS; timeouts, auth/network/provider failures and missing
631
+ prerequisites block the run.
632
+
633
+ The runner defaults to the current stable `@opencode/cli` package and its
634
+ `opencode2` alias; set `OPENCODE_V2_SPEC` only when a test needs a different
635
+ host build. This keeps Pantheon beta testing separate from the OpenCode CLI
636
+ release channel.
637
+
638
+ "V2" here refers only to the hook canary observed against an OpenCode v2.0.18
639
+ host, where at least one tested hook callback fired; it does not establish
640
+ compatibility with the stable `@opencode/plugin@2.0.18` SDK or full 2.x
641
+ contract. This branch still pins transitional `@opencode-ai/plugin@1.18.30`.
642
+ On hosts where both `opencode` and `opencode2` exist, the latter is typically
643
+ a shim that execs the same binary, so an older side-by-side comparison proved
644
+ nothing about the binary itself. The project is V2-exclusive, so there is a
645
+ single leg.
334
646
 
335
647
  ```bash
336
- scripts/test-opencode-v1-v2-sandbox.sh --prepare # tarball + install + init in the sandbox
337
- scripts/test-opencode-v1-v2-sandbox.sh --run v1 --prompts # base validation + prompt battery (V1)
338
- scripts/test-opencode-v1-v2-sandbox.sh --run v2 # base validation only (V2)
339
- scripts/test-opencode-v1-v2-sandbox.sh --prompts # prompt battery for both versions
340
- scripts/test-opencode-v1-v2-sandbox.sh --reset # wipe the sandbox root
648
+ scripts/test-opencode-v2-sandbox.sh --prepare # tarball + install + init in the sandbox
649
+ ~/pantheon-sandbox/start-pantheon.sh # interactive TUI in that isolated beta sandbox
650
+ scripts/test-opencode-v2-sandbox.sh --run v2 # base validation only
651
+ scripts/test-opencode-v2-sandbox.sh --prompts # base validation + prompt battery
652
+ scripts/test-opencode-v2-sandbox.sh --rehydrate # offline context rehydration/session-summary probe
653
+ scripts/test-opencode-v2-sandbox.sh --hooks # V2 hook callback canary
654
+ scripts/test-opencode-v2-sandbox.sh --rehydrate --hooks # run both canaries
655
+ scripts/test-opencode-v2-sandbox.sh --reset # wipe the sandbox root
341
656
  ```
342
657
 
343
- Modes are combinable (e.g. `--prepare --run v1 --prompts`). Binaries are
344
- resolved strictly inside the sandbox npm prefix — a non-prepared sandbox fails
345
- fast instead of silently testing the host installation.
658
+ Modes are combinable (e.g. `--prepare --run v2 --prompts`). `--rehydrate` runs
659
+ offline `context_rehydrate` and `context_session_summary` probes. `--hooks` runs
660
+ a V2 hook canary against the sandbox binary to check that hook callbacks fire;
661
+ it does not test transform callback effects or prove Pantheon's
662
+ `execute.before` security enforcement. With the probe-only `--rehydrate --hooks`
663
+ pair (without `--run`, `--prompts`, or `--cost`), the hooks canary still runs if
664
+ rehydration fails, and the command returns a failing status afterward. These
665
+ are test/sandbox canaries, not proof of Pantheon security enforcement. Binaries
666
+ are resolved strictly inside the sandbox npm prefix — a non-prepared sandbox
667
+ fails fast instead of silently testing the host installation. The sandbox is
668
+ always built from the checkout this script lives in; it never infers a
669
+ repository from a sibling directory.
346
670
 
347
671
  This validates the prepared isolated sandbox only. A PASS is not proof of
348
672
  support for every real host or for host configurations that were not exercised.
349
673
 
674
+ ## Latency benchmark (bench/)
675
+
676
+ `bench/` measures end-to-end session latency of the installed `opencode` CLI
677
+ with the repo's V2 plugin — the baseline every performance change in a release
678
+ must be compared against. It runs two scenarios N times each, interleaved
679
+ (non-interactive, one fresh sandbox directory per run), collects per-run
680
+ metrics from `opencode.db` in READ-ONLY mode (tokens root + child sessions
681
+ without double counting, tool calls per name, child-session count) and hook
682
+ timings from the `[bench]` lines the plugin appends to `hooks.log`:
683
+
684
+ ```bash
685
+ node bench/bin/bench.mjs # 5 runs per scenario (A and B), ~1h
686
+ BENCH_MODEL=opencode-go/glm-5.3-flash node bench/bin/bench.mjs # pin a model
687
+ node bench/bin/bench.mjs --runs 1 # smoke: one run per scenario
688
+ ```
689
+
690
+ Results land in `bench/results/<label>-<git-sha>.json` (median + p80 +
691
+ failures per scenario); the label defaults to `baseline` and the harness
692
+ refuses to overwrite an existing result, so the run identity lives in the file
693
+ name. Scenario A performs real web research, so its variance is
694
+ network-dependent and recorded per run.
695
+
696
+ ### Measurement profiles (explicit, never inferred)
697
+
698
+ Instructions are injected from two surfaces: the global config dir and the
699
+ project directory. The harness states which one it exercises, because a
700
+ result only means anything in the profile where the change acts:
701
+
702
+ | `--profile` | Global `AGENTS.md` | Project `AGENTS.md` | Use |
703
+ |---|---|---|---|
704
+ | `host` (default) | host config dir (or `--global-agents`) | none — run dirs stay empty | the host's real, unmodified injection |
705
+ | `double-injection` | host config dir (or `--global-agents`) | `--project-agents`, written into every run dir | the in-repo profile where global+project overlap |
706
+
707
+ The default measures scenarios A and B. Use `--scenarios B` to isolate the F1
708
+ one-shot claim; the selected scenario still runs N times (`--runs 5` by
709
+ default).
710
+
711
+ ```bash
712
+ # candidate arm: the carved global artifact + the carved project artifact
713
+ node bench/bin/bench.mjs --runs 5 --scenarios B --profile double-injection \
714
+ --global-agents AGENTS.global.md --project-agents AGENTS.md --label f1-candidate
715
+ ```
716
+
717
+ Each arm records, per run and read from the db, the identity (path, bytes,
718
+ sha256) of every instruction file the session actually loaded
719
+ (`instruction_state` + `instruction_blob`) — the fidelity proof that what was
720
+ measured is what was generated. A claim may only rest on a run whose
721
+ provenance shows the intended global **and** project artifacts were loaded.
722
+
723
+ ### Signal vs noise
724
+
725
+ Wall time is a lead, not a criterion: same-day spread reaches ~8× in scenario
726
+ A and ~13× in B. An effect is **confirmed** only when it exceeds the same-day
727
+ spread (or demands a larger N); otherwise it is recorded as **inconclusive**.
728
+ Prefer token medians, which are far more stable. Scenario A's wall is not a
729
+ claim metric — it performs real web research, so network variance dominates.
730
+
731
+ ### First-request proxy and F1 evidence
732
+
733
+ The injected instruction payload only lands on the first request, so the
734
+ tree-total `tokens.input` (summed over every turn) is dominated by
735
+ session-length variance. Each run also records `firstRequest` — the first
736
+ assistant row's `tokens.input` + `tokens.cache.read` — the proxy that isolates
737
+ the payload.
738
+
739
+ An interleaved OpenCode v2.0.29 run (C1/K1…C5/K5, N=5/arm) measured
740
+ first-request context at 17555 tokens for control and 15489 for candidate
741
+ (**−2066, −11.8%**; within-arm spread 14 tokens in each arm). D4 confirmed
742
+ 22350 B loaded for control and 13400 B for candidate (−8950 B, −40.0%); the
743
+ pre-registered correctness oracle passed 5/5 in both arms. Themis accepted F1
744
+ with **PASS_WITH_NOTES**. The first-request effect is confirmed against
745
+ within-arm spread. Tree-total input fell from a median 47594 to 38253
746
+ tokens, but ranges overlap and the ≤30k hypothesis was not met. Wall medians
747
+ were 30.078 s and 24.130 s, with high spread; tree-total input and wall remain
748
+ **inconclusive**. No wall-time or monetary-cost gain is claimed. Details and
749
+ per-run records: `bench/results/f1-di-v229-interleaved-2026-10-14.md`.
750
+ The earlier sequential pair remains supporting-only in
751
+ `bench/results/f1-di-v229-clean-2026-10-14.md`. Earlier exploratory runs are
752
+ retained separately in `bench/results/f1-double-injection.md` and
753
+ `bench/results/f1-di-v229-preliminary-2026-10-14.md`.
754
+
755
+ ### Explicit root specialist result (F3a)
756
+
757
+ For the bounded one-shot scenario B, explicitly starting Talos as the root
758
+ produced the oracle-passing artifact in 5/5 runs with no child session or
759
+ `subagent` call. In the same-day paired sample, Talos used 16,171 median input
760
+ tokens per run versus 33,301 for Zeus (−51.4%); this is a descriptive result
761
+ for this task, model, and configuration, not a general routing or monetary
762
+ savings claim. Wall time is inconclusive: medians were 30.347 s and 31.893 s,
763
+ the ranges overlap widely, and Talos had the higher p80. No automatic
764
+ LLM-selected root routing is introduced by this result.
765
+
766
+ A final integration check after F4A/F3b initially completed 4/5 Talos artifacts.
767
+ Talos's prompt now says to finish named files with native `write`/`edit` tools
768
+ and not confuse MCP tool discovery with the host's native tool catalog. The
769
+ failure remains recorded in
770
+ [`beta10-final-integration-2026-10-15.md`](bench/results/beta10-final-integration-2026-10-15.md).
771
+ A new pre-registered cohort on that prompt passed the artifact oracle in 5/5
772
+ Talos runs with zero children or `subagent` calls. Its first-request context
773
+ proxy was 9,675 tokens versus 13,009 for Zeus (−25.6%, confirmed against the
774
+ same-arm spread). Tree-total input is descriptive, and wall time remains
775
+ inconclusive. See the [follow-up report](bench/results/beta10-final-integration-followup-2026-10-15.md)
776
+ and [raw run record](bench/results/beta10-final-integration-followup-20261015.json).
777
+
778
+ The v2.0.29 harness requires explicit agent and fixture selection, and proves
779
+ global plus project instruction provenance for each run:
780
+
781
+ ```bash
782
+ BENCH_MODEL=opencode-go/glm-5.3-flash node bench/bin/bench.mjs \
783
+ --scenario B --runs 5 --profile double-injection \
784
+ --global-agents AGENTS.global.md --project-agents AGENTS.md \
785
+ --agents-dir src/agents --skills-dir src/skills \
786
+ --root-agent talos --control-root-agent zeus --paired \
787
+ --out bench/results/phase3-root-paired-20261014.json
788
+ ```
789
+
790
+ Themis reviewed the harness and evidence (**PASS_WITH_NOTES**). Full method,
791
+ oracle audit, limitations, and per-run data are in
792
+ `bench/results/f3a-root-paired-2026-10-14.md` and
793
+ `bench/results/phase3-root-paired-20261014.json`.
794
+
795
+ ### Bounded workspace reads (F3b)
796
+
797
+ On OpenCode v2.0.29, the V2 plugin can expose `pantheon_bounded_read` when the
798
+ host provides its native permission and session APIs. Only a root Zeus session
799
+ can use it. Each call asks the host for `read` permission to one exact
800
+ workspace-relative file, with an empty save scope; content is read only after
801
+ approval and is scanned by the existing fail-closed secret scanner. Limits are
802
+ two calls and two distinct files, 400 lines and 32 KiB per assistant message.
803
+ Traversal, symlinks, directories, sensitive scopes, recognized binary/media
804
+ formats, and scanner failures are rejected. This is scoped to a local workspace
805
+ without concurrent writers; it does not claim adversarial filesystem isolation
806
+ or whole-file binary classification. The host-backed evidence is in
807
+ [`bench/results/f3b-v229-bounded-read-2026-10-15.md`](bench/results/f3b-v229-bounded-read-2026-10-15.md).
808
+
809
+
810
+ ## Plugin V2 TypeScript coverage
811
+
812
+ `npm run coverage:plugin-v2` runs the `tests/pantheon/*.test.ts` suite with
813
+ Node's source-mapped native coverage and enforces an 80% line-coverage minimum
814
+ for `src/plugin-v2.ts` only. It requires Node `v24.15.0`; branch and function
815
+ coverage are reported but are not gates. This is not a repository-wide coverage
816
+ claim.
817
+
350
818
  Env overrides:
351
819
 
352
820
  | Variable | Default | Purpose |
353
821
  |----------|---------|---------|
354
822
  | `PANTHEON_SANDBOX_ROOT` | `~/pantheon-sandbox` | Sandbox root (refused if unsafe for `--reset`) |
355
- | `OPENCODE_V1_SPEC` | `opencode-ai@1.18.18` | npm spec providing the `opencode` binary |
356
- | `OPENCODE_V2_SPEC` | `@opencode-ai/cli@beta` | npm spec providing the `opencode2` binary |
823
+ | `OPENCODE_V1_SPEC` | `opencode-ai@1.18.18` | Configures the V1 plugin only; the V2-only sandbox runner does not consume or support it |
824
+ | `OPENCODE_V2_SPEC` | `@opencode/cli@latest` | npm spec providing the `opencode2` binary |
357
825
  | `PANTHEON_SANDBOX_MODEL` | `opencode-go/mimo-v2.5` | Model used by init and prompts |
358
826
  | `PANTHEON_PROMPT_TIMEOUT` | `300` | Per-prompt timeout in seconds |
359
827
 
@@ -362,7 +830,7 @@ in the sandbox root) · `2` usage error · `3` sandbox not prepared.
362
830
 
363
831
  ### Intentional memory MCP divergence
364
832
 
365
- `scripts/memory_mcp_server.py` and `src/mcp/memory_mcp_server.py` are
833
+ `scripts/memory_mcp.py` and `src/mcp/memory_mcp.py` are
366
834
  intentionally different. The standalone `scripts/` copy keeps the lightweight
367
835
  `memory_*` contract; the installed `src/mcp/` copy additionally exposes the
368
836
  optional codemap schema and `code_index`, `code_query`, and `code_neighbors`.
@@ -382,18 +850,22 @@ and returns nothing. Prefer `background=true` dispatches with an explicit
382
850
 
383
851
  ## Configuration (environment variables)
384
852
 
385
- | Variable | Default | Description |
386
- |---|---|---|
387
- | `PANTHEON_MEMORY_EMBED` | `on` | `off` (also `0`, `false`, `no`) disables the `pantheon-memory` embedding/vector pipeline — search runs in FTS5-only mode with no model download and no `sqlite-vec` writes. Also forced `off` automatically when `fastembed` fails to import, so a broken embedding install never takes the server down (issue #159). |
388
-
389
- The memory MCP degrades gracefully: when `fastembed` or `sqlite-vec` are
390
- unavailable (network failure, incompatible wheel), the server still starts,
391
- answers the MCP `initialize` handshake, and serves keyword search — only the
392
- semantic vector ranking is unavailable.
853
+ The `pantheon-memory` MCP server needs no configuration: search is SQLite FTS5
854
+ (BM25) only, all stdlib, with no embedding model, no vector index, and no
855
+ `sqlite-vec`/`fastembed` dependency. Other environment variables are documented
856
+ in the sections above.
393
857
 
394
858
 
395
859
  ## Documentation
396
860
 
861
+ ### Local evaluations
862
+
863
+ Promptfoo/evaluation experiments are local-only: place them under
864
+ `evals/promptfoo/`, which is gitignored and excluded from npm packaging,
865
+ package evidence, CI tests, coverage, and release gates. Publish reviewed
866
+ findings as documentation, not the local harness, datasets, outputs, or
867
+ credentials.
868
+
397
869
  - [Installation](docs/INSTALLATION.md) · [Quick start](docs/QUICKSTART.md)
398
870
  - [Architecture](docs/ARCHITECTURE.md) · [MCP tools](docs/mcp-tools.md)
399
871
  - [Platforms](docs/PLATFORMS.md) · [Upgrading](docs/UPGRADING.md)
@@ -407,12 +879,38 @@ Ideas, bug reports, documentation improvements, and code contributions are
407
879
  welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening an issue
408
880
  or pull request.
409
881
 
882
+ ### Reproducible benchmark evidence
883
+
884
+ The beta2 benchmark records reported token usage separately from its
885
+ character-based fallback estimate; source bytes and logical line counts remain
886
+ provenance data and are never converted into tokens. Cross-report comparisons
887
+ require matching model/config provenance and reported OpenCode usage. Offline
888
+ fixture responses are mocked: their reported values are labeled separately,
889
+ excluded from `actual` usage, and blocked from comparative metrics and GEPA.
890
+ Fixture quality outcomes test the offline contract; they are not evaluation
891
+ evidence.
892
+ Reproduce the
893
+ checked-in AGENTS.md history manifest with
894
+ `python3 -m benchmarks.beta2.provenance --output benchmarks/beta2/fixtures/agent-baseline-provenance.json`.
895
+ For OpenCode benchmark runs, provide `--model` and `--config-sha256` so reports
896
+ can be compared safely.
897
+
898
+ ### Project-level `opencode.json`
899
+
900
+ The published package carries no `opencode.json` template. `init` still writes
901
+ your project's `opencode.json` itself, and the few product defaults an install
902
+ seeds — `default_agent: zeus` and `permission.skill` — come from the installer
903
+ code, not from a packaged file. Contributors who keep a project-level
904
+ `opencode.json` for their own OpenCode sessions should leave it untracked —
905
+ `.git/info/exclude` per clone, or a shared `.gitignore` decision — rather than
906
+ versioning it.
907
+
410
908
  ## Citation and DOI
411
909
 
412
- Pantheon is released under the [MIT License](LICENSE). For the historical
413
- published v1.4.3 record only, use the [Zenodo DOI](https://doi.org/10.5281/zenodo.22306637);
414
- it is not the current operational version. Citation metadata is also available
415
- in [CITATION.cff](CITATION.cff).
910
+ Pantheon is released under the [MIT License](LICENSE). Cite the
911
+ [Zenodo concept DOI](https://doi.org/10.5281/zenodo.22650136), which always
912
+ resolves to the latest archived release; each release also has its own version
913
+ DOI. Citation metadata is also available in [CITATION.cff](CITATION.cff).
416
914
 
417
915
  Canonical repository: <https://github.com/ils15/pantheon-opencode>
418
916