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.
- package/.pantheon/code-mode/README.md +4 -2
- package/.pantheon/code-mode/checkpoint_session.py +138 -102
- package/.pantheon/code-mode/compress-inline.py +102 -57
- package/.pantheon/code-mode/manifest.json +2 -8
- package/AGENTS.global.md +182 -0
- package/AGENTS.md +10 -891
- package/CHANGELOG.md +343 -0
- package/README.md +577 -79
- package/README.pt-BR.md +345 -60
- package/bin/pantheon-init.mjs +26 -3
- package/commands/pantheon-audit.md +7 -1
- package/commands/pantheon-consolidate.md +1 -1
- package/commands/pantheon-deepwork.md +14 -3
- package/commands/pantheon-focus.md +17 -0
- package/commands/pantheon-optimize.md +2 -2
- package/commands/pantheon-vision.md +61 -12
- package/package.json +30 -11
- package/plugin.json +2 -2
- package/pyproject.toml +11 -16
- package/scripts/build-agents-md.mjs +376 -58
- package/scripts/changelog-merge.mjs +161 -0
- package/scripts/check-plugin-v2-coverage.mjs +213 -0
- package/scripts/ci-validate-yaml.py +7 -5
- package/scripts/{code_mode_server.py → code_mode.py} +119 -27
- package/scripts/cost.mjs +244 -41
- package/scripts/doctor.mjs +236 -29
- package/scripts/install/agents-md.mjs +4 -4
- package/scripts/install/cli-ui.mjs +1 -1
- package/scripts/install/config-migration.mjs +319 -6
- package/scripts/install/health-check.mjs +26 -37
- package/scripts/install/interactive.mjs +7 -7
- package/scripts/install/migrate.mjs +1 -1
- package/scripts/install/opencode-version.mjs +246 -4
- package/scripts/install/opencode.mjs +627 -179
- package/scripts/install/plugin.mjs +43 -9
- package/scripts/install/shared.mjs +120 -12
- package/scripts/install/vision-prompt.mjs +161 -0
- package/scripts/install-mcp.mjs +28 -9
- package/scripts/{mcp_persistence_server.py → mcp_persistence.py} +392 -78
- package/{src/mcp/mcp_resources_server.py → scripts/mcp_resources.py} +2 -2
- package/scripts/{memory_mcp_server.py → memory_mcp.py} +372 -198
- package/scripts/postinstall.mjs +0 -7
- package/scripts/probe-context-rehydrate.mjs +36 -13
- package/scripts/probe-pantheon-cost.mjs +71 -15
- package/scripts/prune.mjs +2 -2
- package/scripts/run-node-tests.mjs +189 -0
- package/scripts/sync-artifacts.mjs +26 -13
- package/scripts/sync-tui.mjs +24 -3
- package/scripts/test-opencode-v2-sandbox.sh +1913 -0
- package/scripts/validate-routing.mjs +43 -3
- package/scripts/validate-sandbox-install.mjs +138 -0
- package/scripts/versioning.mjs +13 -1
- package/scripts/vision-status.mjs +682 -0
- package/skills-lock.json +15 -16
- package/src/agents/aphrodite.md +20 -37
- package/src/agents/apollo.md +7 -3
- package/src/agents/athena.md +63 -35
- package/src/agents/demeter.md +8 -28
- package/src/agents/gaia.md +1 -1
- package/src/agents/hephaestus.md +10 -21
- package/src/agents/hermes.md +15 -38
- package/src/agents/iris.md +1 -1
- package/src/agents/mnemosyne.md +23 -60
- package/src/agents/nyx.md +1 -1
- package/src/agents/prometheus.md +16 -20
- package/src/agents/talos.md +10 -2
- package/src/agents/themis.md +5 -5
- package/src/agents/zeus.md +99 -130
- package/src/instructions/agent-return-format.instructions.md +18 -3
- package/src/instructions/backend-standards.instructions.md +2 -0
- package/src/instructions/frontend-standards.instructions.md +3 -1
- package/src/instructions/memory-protocol.instructions.md +11 -110
- package/src/instructions/zeus-anti-stall.instructions.md +14 -61
- package/src/instructions/zeus-communication-rules.instructions.md +0 -1
- package/src/instructions/zeus-council-synthesis.instructions.md +32 -260
- package/src/instructions/zeus-memory-operations.instructions.md +26 -0
- package/src/instructions/zeus-timeout-retry.instructions.md +40 -87
- package/src/mcp/{code_mode_server.py → code_mode.py} +119 -27
- package/src/mcp/eval_store.py +2 -2
- package/src/mcp/mcp_codemap_module.py +559 -129
- package/src/mcp/{mcp_persistence_server.py → mcp_persistence.py} +392 -78
- package/{scripts/mcp_resources_server.py → src/mcp/mcp_resources.py} +2 -2
- package/src/mcp/{memory_mcp_server.py → memory_mcp.py} +373 -250
- package/src/mcp/{pantheon_vision_server.py → pantheon_vision.py} +396 -40
- package/src/mcp/requirements-mcp.txt +10 -6
- package/src/mcp/requirements-vision.txt +4 -2
- package/src/pantheon/cost-command.ts +399 -101
- package/src/pantheon/delegation-enforce.ts +10 -1
- package/src/pantheon/model-command.ts +18 -7
- package/src/pantheon/presets.d.mts +0 -8
- package/src/pantheon/presets.mjs +5 -32
- package/src/pantheon/todo-enforcer.ts +1 -1
- package/src/pantheon/v2-bounded-read.ts +640 -0
- package/src/pantheon/v2-delegation-enforce.ts +214 -0
- package/src/pantheon/v2-execute-after.ts +186 -0
- package/src/pantheon/v2-tools.ts +429 -0
- package/src/pantheon/v2-unsupported.d.mts +11 -0
- package/src/pantheon/v2-unsupported.mjs +90 -0
- package/src/pantheon/v2-vision.ts +622 -0
- package/src/pantheon/vision-local.ts +531 -0
- package/src/pantheon/vision.ts +1161 -116
- package/src/plugin-v2.ts +1244 -288
- package/src/plugin.ts +12 -9
- package/src/plugins/tui/dist/server.d.ts +1 -2
- package/src/plugins/tui/dist/server.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui-v2.d.ts +154 -0
- package/src/plugins/tui/dist/tui-v2.d.ts.map +1 -0
- package/src/plugins/tui/dist/tui-v2.js +659 -0
- package/src/plugins/tui/dist/tui-v2.js.map +1 -0
- package/src/plugins/tui/dist/tui.d.ts +85 -82
- package/src/plugins/tui/dist/tui.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui.js +72 -59
- package/src/plugins/tui/index-v2.ts +6 -0
- package/src/plugins/tui/package-lock.json +5504 -1341
- package/src/plugins/tui/package.json +25 -15
- package/src/plugins/tui/src/index-v2.tsx +734 -0
- package/src/plugins/tui/src/index.tsx +106 -85
- package/src/plugins/tui/src/opencode-v2.d.ts +13 -0
- package/src/plugins/tui/tsconfig.build.json +5 -0
- package/src/plugins/tui/tsconfig.typecheck.json +5 -0
- package/src/plugins/tui/tsdown.config.ts +22 -2
- package/src/plugins/tui/tui-v2.ts +2 -0
- package/src/plugins/tui/tui.ts +3 -0
- package/src/routing.yml +11 -22
- package/src/skills/README.md +34 -39
- package/src/skills/agent-coordination/SKILL.md +7 -2
- package/src/skills/artifact-management/SKILL.md +12 -5
- package/src/skills/auto-continue/SKILL.md +54 -95
- package/src/skills/code-review-checklist/SKILL.md +5 -5
- package/src/skills/context-compression/SKILL.md +26 -879
- package/src/skills/session-goal/SKILL.md +14 -5
- package/src/skills/spec-driven-development/SKILL.md +3 -1
- package/src/skills/tdd-with-agents/SKILL.md +18 -10
- package/.pantheon/code-mode/eval-llm-judge.py +0 -255
- package/.pantheon/code-mode/eval-monte-carlo.py +0 -368
- package/.pantheon/code-mode/eval-run.py +0 -197
- package/.pantheon/code-mode/eval-static.py +0 -321
- package/.pantheon/code-mode/session-end-save.py +0 -186
- package/.pantheon/code-mode/session-end-save.sh +0 -18
- package/opencode.json +0 -58
- package/scripts/recover-zenodo-deposition.mjs +0 -146
- package/scripts/test-opencode-v1-v2-sandbox.sh +0 -862
- package/scripts/validate-zenodo-release.mjs +0 -154
- package/scripts/zenodo-artifact.mjs +0 -94
- package/scripts/zenodo-release-state.mjs +0 -53
- 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
|
[](https://github.com/ils15/pantheon-opencode/releases/latest)
|
|
12
12
|
[](https://github.com/ils15/pantheon-opencode/actions)
|
|
13
|
-
[](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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
and
|
|
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.
|
|
287
|
+
## What's new in 1.6.0-beta.1
|
|
158
288
|
|
|
159
|
-
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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.
|
|
295
|
+
## OpenCode V1/V2 — Dual Version (1.6.0-beta.1)
|
|
176
296
|
|
|
177
|
-
Pantheon has two **exclusive** 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:
|
|
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
|
|
189
|
-
`
|
|
190
|
-
`
|
|
191
|
-
`
|
|
192
|
-
|
|
193
|
-
|
|
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
|
|
213
|
-
after `init`. `auto`
|
|
214
|
-
`OPENCODE_VERSION=v1|v2` wins; otherwise an
|
|
215
|
-
`
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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-
|
|
337
|
-
|
|
338
|
-
scripts/test-opencode-
|
|
339
|
-
scripts/test-opencode-
|
|
340
|
-
scripts/test-opencode-
|
|
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
|
|
344
|
-
|
|
345
|
-
|
|
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` |
|
|
356
|
-
| `OPENCODE_V2_SPEC` | `@opencode
|
|
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/
|
|
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
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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).
|
|
413
|
-
|
|
414
|
-
|
|
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
|
|