Repository navigation
feat: added OpenCode v2 support - #11
ilyhalight wants to merge 2 commits into
Conversation
LegolasUndomiel
left a comment
There was a problem hiding this comment.
Thanks for tackling v2 support. The overall direction is right: reading session_v2 / session_message, taking the role from the type column, and the model.id alias are exactly what is needed. A few suggestions after testing this against a real v2 database (OpenCode 2.0.32, with a DB upgraded from v1).
1. Merge v1 + v2 tables instead of switching exclusively to v2
A database upgraded from v1 keeps both schemas. The migration copies messages into session_message keeping the same ids (on my DB: 2808 ids shared between message and session_message, of which 2563 are assistant messages), but it is not a complete superset: I found 48 assistant messages and 2 sessions that exist only in the legacy session / message tables. Since this PR uses session_v2 / session_message and never touches the legacy tables whenever the v2 tables exist, that data is silently dropped.
Suggestion: read both and deduplicate by message id — e.g. seed a HashSet from the v1 message ids first, then skip ids already seen while streaming session_message; merge the session lists the same way (v2 rows win on shared ids, since they also carry directory). This also keeps a single code path when only v1 exists.
2. Keep the table-existence probe read-only
database_has_tables now calls open_database (read-write), whereas the previous database_has_expected_tables used Connection::open_with_flags(..., SQLITE_OPEN_READ_ONLY). Opening a live database read-write just to list tables can take write locks; consider preserving the read-only flag.
3. Tests
Good that you flagged this yourself. A synthetic-v2 unit test (create project / session / message / session_v2 / session_message, insert a v2-only session plus an assistant payload with the nested model.id, assert exactly one usage event and that idle records are skipped) and a "same id present in both tables is counted once" test would protect the merge logic. I have both passing locally.
Minor / optional:
session_v2.directoryis a better per-session project path thanproject.worktree(COALESCE(s.directory, p.worktree)).type IN ('user', 'assistant')dropscompaction/synthetic, which the legacy schema stored withrole = "user"; include them if you want message counts to stay consistent with v1.
I have the above implemented locally (merge + dedup + 3 tests, full suite green). Happy to share the diff or open a follow-up PR if that makes it easier.
Follow-up: an implementation that merges the v1 and v2 tablesFollowing the suggestion in the review above, I implemented the "merge v1 + v2 and de-duplicate by message id" approach as a standalone branch you can merge or cherry-pick:
How it differs from #11
Why an either/or choice loses data (measured on a real opencode 2.0.32 database)
Approach
End-to-end on a real database: Happy to adjust anything on that branch. |
synthetic don't contain tokens |
Added OpenCode v2 support (support for v1 also saved).
I don't make:
before:

after:
