Skip to content

fix(opencode): write the session export into the OpenCode 2.x storage schema - #1199

Open
BambinoSK wants to merge 1 commit into
catatafishen:masterfrom
BambinoSK:feat/opencode-v2-session-export
Open

BambinoSK wants to merge 1 commit into
catatafishen:masterfrom
BambinoSK:feat/opencode-v2-session-export

Conversation

@BambinoSK

Copy link
Copy Markdown
Contributor

Fixes #1198.

Problem

OpenCodeClientExporter exported the agent-switch session into opencode.db using the OpenCode 1.2.x schema (legacy session, message, part tables via raw JDBC). Against OpenCode 2.x the exported sessions were invisible to session/resume (-32602 session not found, falling back to conversation-history injection), and the plugin-created legacy tables broke v2's boot-time v1 migration on every start (SQLiteError: no such column: path).

What this PR does

  • Schema-aware routing. The exporter detects the database generation before writing: a session_v2 table means v2 storage, a legacy session table keeps the old 1.x path unchanged, and an uninitialized database skips the raw export entirely instead of fabricating tables OpenCode does not expect.
  • v2 storage writes. Against v2 the export writes one session_v2 row, session_message rows with parts embedded in a content array, and the event_sequence watermark, mirroring the row shapes OpenCode's own v1-to-v2 migration produces. The whole export runs in a single transaction, directory is normalized to forward slashes to match the form v2 compares against on lookup, path is stored as the session directory relative to the git root, and the version string is copied from the newest existing session so exported rows are indistinguishable from native ones.
  • No fabricated project rows. v2 derives project ids from the git remote, which the plugin cannot replicate safely, so the export resolves the existing project row for the git root worktree, falling back to the directory row and then v2's literal global id, instead of inserting project rows. v2's session lookup is by id plus a directory comparison, so this is sufficient for session/resume.

Notes for reviewers

The v2 storage schema is a private internal of OpenCode (no supported import API exists on the ACP or HTTP surface), so this PR keeps the existing coupling but tracks the current shapes; the issue this fixes is exactly the cost of that coupling drifting. The legacy 1.x path is intentionally untouched for older OpenCode installs. Schema details were validated against a live OpenCode 2.0.32 database: row shapes, event_sequence watermarks, and project id derivation. Two files changed, +751/-24.

Verification

  • OpenCodeClientExporterTest: 61/61 pass, including new SQLite round-trip tests for the v2 path (session row, message payloads, watermark, no legacy tables created), a legacy-database regression guard, an uninitialized-database skip test, a git-root subdir path test, and the global project fallback.
  • :plugin-core:buildPlugin succeeds.
  • End-to-end on a real machine with OpenCode 2.0.32: an agent switch exports the session, session/resume loads it (previously -32602 session not found), and the new session_v2 row matches native rows in project, directory, version, and message shapes.

Closes #1198

OpenCode 2.x reads sessions from session_v2 and session_message, not the
legacy session, message, and part tables. The legacy-shaped writes were
invisible to session/resume and broke the v1 to v2 migration on every
start, because that migration selects a path column the exporter's
hardcoded 1.2.x schema never created.

The exporter now detects the schema generation before writing. Against
session_v2 it writes one session row, session_message rows with parts
embedded in a content array, and the event_sequence watermark, mirroring
the shapes OpenCode's own migration produces. The whole export runs in a
single transaction so a failure cannot leave a partial session row, the
directory column is normalized to forward slashes to match the form v2
compares against on lookup, and path is stored as the session directory
relative to the git root. The project is resolved
from the git root worktree with the global fallback instead of
fabricating project rows, and an uninitialized database skips the raw
export entirely rather than creating tables OpenCode does not expect.
@github-actions

Copy link
Copy Markdown

⚠️ ClusterFuzzLite — Maintainer Security Review Required

A maintainer needs to approve the ClusterFuzzLite PR fuzzing workflow run for this fork PR.

Before approving, please review the code changes from BambinoSK/agentbridge@d0dc92de to confirm there is no code that could:

  • Exfiltrate the CFLITE_STORAGE_REPO_TOKEN CI secret
  • Abuse CI infrastructure or the fuzz corpus storage repository

The fuzz targets from the fork branch will be compiled and executed with access to repository secrets. Rubber-stamping this approval without reviewing the code is a security risk.

This comment is posted automatically whenever ClusterFuzzLite is triggered on a fork PR.

@codecov

codecov Bot commented Oct 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

This branch is waiting to be deployed

1 waiting deployment
fuzzing-gate — d0dc92de Waiting Oct 21, 2026 by BambinoSK via Maintainer approval gate (fork PR) #1645
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OpenCode session export writes the 1.2.x schema: exported sessions cannot be resumed and the v1 migration fails on every start on OpenCode 2.x

1 participant