Skip to content

Latest commit

 

History

History
117 lines (101 loc) · 6.09 KB

File metadata and controls

117 lines (101 loc) · 6.09 KB

opencode2 support

This plugin runs on both hosts from a single package entry:

  • opencode 1.x reads server() (V1 hooks, opencode.jsonagent map).
  • opencode2 reads id + setup() (V2 context API, src/v2/* adapter).

Install for opencode2:

// opencode.jsonc
{
  "$schema": "https://opencode.ai/config.json",
  "plugins": [
    { "package": "[email protected]", "options": { "min_rollout_idle_hours": 1 } },
  ],
}

All V1 plugin options (generate_memories, use_memories, dedicated_tools, disable_on_external_context, extract_model, consolidation_model, numeric clamps, codex_interop, claude_import) apply unchanged — option parsing is shared (applyPluginOptions).

How it works

The entire memory pipeline (extraction → consolidation → injection → citation feedback against the global ~/.local/share/opencode workspace) runs byte-identical on both hosts. src/v2/shim.ts presents a V1-shaped client façade over the V2 plugin context, so phase1/phase2/capture/llm execute the same code paths. Only genuinely missing V2 surfaces are adapted; everything else is hook translation (src/v2/plugin.ts):

V1 V2
config hook agent injection agent.transform ensure (update creates)
returned tool map tool.transform (same tools/* logic via adapter)
chat.message pump prompt hook
system.transform injection context hook (system.push({type:"text",…}))
text.complete + messages.transform citations session.text.ended durable accounting + context reconciliation/strip
tool.execute.before pollution tool.execute.before (same hook name)
session.status idle / session.idle pump session.execution.succeeded event
session.deleted cleanup public session.remove with liveness fallback
experimental/session global discovery authenticated public session.list with cursor pagination
V1 session.messages authenticated public message.list (full persisted history)

Deliberate V2 differences

  • Registered service is required for global reads. V2 discovers the local service with Service.discover(), preserves its auth headers, and accepts it only when /health reports the plugin host's own PID. It never starts a service with Service.ensure(). If no matching service is registered, global discovery reports a clear unavailable error.
  • Global discovery is complete. The adapter follows public session.list cursors and uses public message.list for full persisted history. Helper sessions are excluded by durable metadata and the cleanup sweep reclaims them after a restart; session.context is not used for transcript capture.
  • Agents are location-scoped. V2 provisions the agents in the active plugin location and creates helper sessions in that same location. The consolidation agent's read/edit/search/glob permissions are allowlisted only under the memory workspace; all other actions remain denied. A global OpenCode plugin install therefore provisions the agents as each active location loads the plugin.
  • Citations are accounted durably. session.text.ended is the primary hook because it contains the completed text after durable commit. A SQLite reconciliation table deduplicates (assistant message, cited session) pairs across duplicate events, context calls, and process restarts. The context hook strips citation markup before the next model call; retained markup in persisted history is harmless and can be rendered by the TUI.
  • Config and models are explicit. V2 adapts public config documents for the shared resolver. V2 configs do not provide V1's small_model field; unset extract_model uses the session default, while consolidation_model uses the configured model when present. Set both plugin options explicitly for deterministic routing. Cancellation is verified through request signals plus interrupt-and-wait cleanup.
  • Both agents ship; only memorize works. Extraction runs sessionless through generate.text, so memorize-extract is provisioned hidden and unused (V1 likewise skips injecting unused agents).

Sidebar status

The package's ./tui entry adds a Memory section to the session sidebar. OpenCode2 loads it automatically alongside the server plugin. /memory-status (also Show memory status in the command palette) opens effective models, read/write settings, import status, retry eligibility, and warnings.

  • Status is global, using the same job snapshots as memory_inspect.
  • The UI refreshes on pipeline events and reconciles every five seconds while loaded, including changes made by another worker; viewing it never starts jobs.
  • A disconnected or unavailable server shows Unavailable, not stale Idle.
  • Last success is shown only when the latest recorded consolidation attempt succeeded; means no clean success timestamp is available for that attempt.
  • TUI dependencies are optional peers supplied by OpenCode2; V1 loads only the ./server entry and does not import V2 runtime dependencies. The build compiles Solid JSX and ships the result under dist/.
  • TUI rules learned the hard way: setup() must only claim slots — keymap.layer throws outside a Solid component scope, so it lives in an app-slot component; never render <Show> (or any conditional) with element children directly under <box> — its empty placeholder is a bare text node and the renderer rejects it. Use unconditional lines with placeholders.
  • The TUI bundle must import only @opencode/plugin/tui, solid-js, and @opentui/solid: the CLI sandbox does not resolve zod or @opencode/plugin/rpc, so the status contract (src/v2/status-rpc.ts) is plain JSON Schema with a hand-written guard.

For a local wrapper, add tui.ts beside its index.ts, re-exporting the built dist/src/v2/tui.js default export, then run bun run build in this repository.

Verify

bun run typecheck && bun test && bun run build
bun run smoke        # V1 entry
bun run contract     # V1 host surface
bun run contract:v2  # V2 host surface (needs the opencode2 service)