Repository navigation
Description
Summary
Beta-19271 hardening pass surfaced four independent defects plus a docs gap:
(1) all V1-style plugins fail to load under the V2 schema with no migration
path; (2) custom slash commands registered via command.transform cannot be
invoked through headless run (treated as plain text); (3) recall-scoring
footgun for context-hook authors (behavior note); (4) no V2 plugin API
migration guide or agent-oriented API docs exist, which is why (1)–(3) cost a
full hardening pass to diagnose instead of ten minutes with docs.
Environment
- opencode version: 0.0.0-beta-19271
- OS: Linux 6.17.0-1009-oracle (linux arm64)
- Terminal: Unavailable: headless service (systemd)
- Shell: /bin/bash
- Install/channel: beta
- Active plugins: oh-my-opencode-slim, conclave-v2 (local path),
magic-context-v2 (local path). Notes: a temporary redacted wire-logging
plugin was attached during diagnosis and removed;opencode-supabaseand
listfailures below reproduce from project-local config.
Reproduction
1. V1 plugins unloadable, no migration path
- Configure any of
@op1/goals,opencode2-goal-plugin@1.0.5,
@cortexkit/opencode-magic-context@0.41.2,opencode-supabase, or a
barelistentry inopencode.jsoncplugins. - Start the server.
- Every boot logs, e.g.:
Plugin must export a default definition with an id and an effect or setup function (cause: SchemaError … ["default"]["effect"] … ["default"]["setup"])
ormust default export an object with server(), or
Plugin export is not a function.
2. Custom commands unreachable headless
- Register a command via
ctx.command.transform(editor => editor.add({name: "ctx-status", …}))(loads fine, no errors). - Run
opencode2 run --format json "/ctx-status". - The model receives
/ctx-statusas plain user text ("Checking session
context…") — the handler never fires. No CLI/API path exists to invoke a
registered custom command outside the TUI palette.
3. Recall-scoring footgun (behavior note, local port)
recall(db, "<fixed generic query>", …) plus score-gated fallback
(hits.length>0 ? hits : pool) systematically excludes fresh, distinctive
observations whenever generic session-event rows match the fixed terms. A
planted unique marker (DB-verified, 3 rows from head) was never injected
while noise rows matched. Recency-first retrieval fixed it locally. Suggest
documenting retrieval guidance for context-hook authors.
4. Missing docs: V2 plugin migration guide + agent-oriented API reference
There is no published guide for migrating a V1 plugin (export default async (ctx) => … / {id, server} shapes) to the V2 Plugin.define +
effect/setup API, and no agent-oriented reference for the plugin
surface that coding agents actually need: full SessionHooks inventory
(prompt/context/model.request/http.request/http.response/retry) with
mutability and firing semantics per entry point (TUI vs run vs
exec vs subagent), the command.transform palette-vs-headless matrix,
and the session/tool/event domain method list. The only reason defects
(1)–(3) above required source-diving the bundled @opencode/plugin
.d.ts files is that this documentation does not exist. Request:
(a) a V1→V2 migration guide with before/after for each export shape,
(b) a hook/API reference written for agent implementers, not just humans
clicking the TUI.
Expected Behavior
- Either V1 export shapes keep working or the error names the exact migration
(Plugin.define+effect/setup) with a link. - A supported headless invocation path for registered custom commands
(e.g.runrecognizing leading-/or an explicit flag). - Guidance on recall/injection patterns that survive real session noise.
- Migration + API docs sufficient for an agent to port a plugin without
reverse-engineering bundled type declarations.
Actual Behavior
- SchemaError spam every boot; entire plugin categories (goals, memory,
supabase) dead on beta with no path forward. - Commands exist only in the TUI palette; automation cannot reach them.
- Naive fixed-query recall silently drops fresh context.
- No migration guide; API surface discoverable only via bundled
.d.ts.
Additional Context
- Frequency: 1 and 2 reproduce consistently (100+
listfailures, goals on
every boot until removed; command trial sessionses_f76382ce). - Workarounds: removed dead entries from production config (backups kept);
commands verified by code + DB-level handler tests only; recall fixed
locally via recency-first retrieval. - Related: provider: stale composite reasoning item id (rs_A:rs_B) replayed after restart → invalid_request_error #48319 (stale composite reasoning ids after restart) — same
hardening pass, filed separately.
Activity
github-actions commentedon Sep 10, 2026
This issue might be a duplicate of existing issues. Please check:
If your issue has meaningful differences from the above (e.g. headless custom command invocation in item (2) or the recall-scoring note in item (3) are not covered there), it may be worth splitting those into separate focused issues and closing this one as a duplicate of the relevant existing reports.
famewolf commentedon Sep 24, 2026
Impact on a real installation (2026-09-23, v2.0.15 sidecar):
5 of 8 plugins in a v1 config failed to load under v2:
opencode-auto-resume,smartsnip-fork,lowfat.ts,openslimedit,opencode-handoff. All 8 load correctly on v1.18.31.The 2 that DID load (
opencode-token-optimizer,@tarquinen/opencode-dcp) happened to already use the v2 object shape.Workaround we used: compiled TS→JS via
bun buildand restructured exports to match the v2 runtime schema. This is a manual, per-plugin fix with no tooling or migration path.The practical consequence: users who have built a plugin stack on v1 (including official plugins like DCP) are effectively stranded on v1. Upgrading to v2 silently breaks their setup with no warning beyond a WARN log line.
mphanthj-bot commentedon Oct 21, 2026
Adding a real-world data point for item (1) — a V1 plugin shipped by a first-party vendor — plus the exact error the loader produces for it.
Reproduction
Microsoft Intelligent Terminal 0.2.2572.0 deploys its OpenCode plugin (
wt-agent-hooks) to~/.config/opencode/plugins/wt-agent-hooks.js. It is a V1 plugin:On opencode 2.1.2 (Windows 11 x64) the V2 loader rejects it on every boot:
GET /api/pluginconfirmsstate.status: "failed". The vendor's app therefore never receives a single OpenCode lifecycle event, and the only user-visible symptom is that the terminal never shows OpenCode agent status.Why this variant is worth a targeted message
This is the
Missing key at ["default"]variant, not theExpected object at ["default"]variant from #50590: the module has no default export at all, so the error never mentions that the module looks like a V1 plugin, never namesPlugin.define/setup(ctx), and never links a migration guide. A user who does not read the server log gets no signal whatsoever.Something along these lines would have turned a silent integration failure into a one-line fix:
Gap in the migration guide's lookup table (item 4)
Item (4) of this issue is now partly addressed: a V1→V2 plugin migration guide exists at https://opencode.ai/v2/docs/build/plugins/migrate-v1.
Two things the port still needed are not covered by it, and neither is obvious from the type declarations:
The event payload field moved. The guide maps V1
event→ctx.event.subscribe(), but V2 events carry their payload onevent.data, while V1 usedevent.properties. The guide's example only readsevent.type.Several V1 event types were renamed. The port needed these mappings, none of which appear in the guide:
session.updated→session.renamed/session.metadata.updatedsession.error→session.execution.failedquestion.asked/question.replied→form.created/form.replied/form.cancelledThe session id also moved: V1 read
properties.info.id, V2 readsdata.sessionID.For reference, the working V2 shape that satisfied the loader was a plain object export with no SDK import at all:
Note the missing SDK import is deliberate — see #50434 for local plugins being unable to resolve
@opencode/plugin.opencodejr commentedon Oct 28, 2026
Thanks for the detailed write-up. Here is how each part maps to existing work:
run: this is tracked in run: --command removed in v2 and slash commands are not expanded — no CLI path for headless command invocation #49903 (run --commandwas removed in v2 and a leading/isn't expanded inrun).contexthook chooses what to inject, so it's mostly guidance for plugin authors. Thanks for sharing what worked for you (retrieving the most recent entries first).ctx.command.transform,listandreload. A reference written for agents, listing each hook with when it fires in TUI,run,execand subagents, is still a fair request.Maintainers will look at the requests above. Related issues: