Skip to content

feat(opencode): support OpenCode 2 - #6

Merged
eshatalov merged 6 commits into
mainfrom
opencode-v2
Oct 3, 2026
Merged

eshatalov merged 6 commits into
mainfrom
opencode-v2

Conversation

@eshatalov

@eshatalov eshatalov commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

OpenCode 2 keeps credentials and sessions in one SQLite file, opencode.db, and has no auth.json, so the opencode plugin could neither check the login nor build a shadow home. It also lists its models before its plugins have added theirs, so a spawn on an OpenCode Go model (or a provider from the user's config) was refused with "model not found" every time.

The plugin now works with OpenCode 2 only.

  • Login check. Reads the credential table of the user's opencode.db, read-only.
  • Shadow home. A per-session snapshot of that file: VACUUM INTO from a read-only connection, then every table except the login and schema bookkeeping emptied. The user's sessions never reach the shadow and the shadow's sessions never reach the user's lists.
  • OPENCODE_DB. When the user's environment sets it, the login check and the snapshot read the file it names, and the child gets it set to the shadow database, so the subagent cannot write into the user's.
  • OAuth logins. Copied with an empty refresh token. The shadow can use the access token but cannot refresh it, so it cannot revoke the user's. When the token expires, turns in that shadow fail with an auth error and a new spawn snapshots the current login.
  • Late models. When the model is refused with -32602, the ACP turn sets it again after each config_option_update, for up to 5 s, and records each retry in the evidence. A model that never appears fails with the harness's own refusal, 5 s later than before. This needs OpenCode 2.0.21 or newer; upstream tracks the remaining gap in acp: set_config_option rejects a configured model selected right after session/new anomalyco/opencode#52472, and the retry can go once that lands.
  • subturn status. Models come from a throwaway ACP handshake in a temp shadow; opencode models would leave a background service running. It shows the list the server gives at session start, so late models such as opencode-go/* are not listed even though a turn can select them.
  • Usage. cachedWriteTokens and thoughtTokens are read from OpenCode 2's usage records.

Node 22.13 is now the minimum, for node:sqlite without a flag.

Two small riders, since they touch the same files: the docs and the live test name the current codex model ids (gpt-6-luna, gpt-6.1-sol), and plugin comments state each quirk without the date it was seen.

For a reviewer:

  • Offline: npm run typecheck, npm test (36 pass, 3 live tests skipped). New tests cover the login check, the snapshot, resume reusing the shadow, and both late-model paths on the fake agent.
  • Live on OpenCode 2.0.22: smoke + resume on zai-coding-plan/glm-5.3-flash with OPENCODE_DB pointing at a copy of the user's database, which gained no session. Before the rebase onto fix(core): the newest installed copy of a harness is launched #5: the same on opencode-go/muse-spark-1.3-contributor.

Claude Fable 5.1 in Claude Code.

🤖 Generated with Claude Code

eshatalov and others added 5 commits October 3, 2026 16:46
…ncode.db

OpenCode 2.x keeps credentials in the credential table of opencode.db, next
to the sessions; auth.json is gone and a fresh data dir never imports it, so
the 1.x plugin reported "not authorized" against a live login and its
shadow could not share the user's providers.

The plugin now targets 2.x only. auth() reads the provider ids from the
credential table through node:sqlite without launching the harness. home()
builds the shadow as a snapshot of the user's DB (VACUUM INTO from a
read-only connection, every project/session row deleted, no table dropped,
~240 KB) written through a .tmp file, so a resumed turn finds a finished
shadow. models() runs the same throwaway ACP handshake grok uses inside a
temporary shadow, because `opencode models` starts a persistent background
service when none runs and `--standalone` prints nothing in 2.0.20.
OPENCODE_PRINT_LOGS=1 keeps the private server's stderr as evidence.
extractUsage also reads thoughtTokens and cachedWriteTokens. Node 22.13+
for unflagged node:sqlite. Offline tests cover the snapshot, idempotent
resume, and the auth refusals.

Verified against opencode 2.0.20: smoke + resume and the cancel probe on
zai-coding-plan/glm-5.3-flash, the user's session list and background
service untouched.

Designed and finished by Claude Fable 5.1, implemented by Claude Opus,
reviewed by Claude Sonnet, in Claude Code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codex no longer serves gpt-5.3-codex-spark or the gpt-5.6 pair to ChatGPT
accounts; the routing template, the tool description example and the live
test's cheap bundle now name gpt-6.1-sol and gpt-6-luna.

Claude Fable 5.1 in Claude Code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Dates and anecdotes in the grok, codex and claude plugin comments are
replaced by the fact they were recording.

Claude Fable 5.1 in Claude Code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n be selected

OpenCode lists its models before its plugins have added theirs, so a
spawn on an OpenCode Go model (or a provider from the user's config) was
refused with "model not found" every time. The server announces the full
list with config_option_update about a second later.

When the model is refused with -32602, the ACP turn now sets it again
after each config_option_update, for up to 5 s, and records each retry in
the evidence. A model that never appears fails with the harness's own
refusal, as before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5c1cc72ab7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/plugins/opencode.ts Outdated
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-03T13:50:11.096972Z 5c1cc72 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

… database

OpenCode takes its database path from OPENCODE_DB when it is set. The
shadow only changed XDG_DATA_HOME, so with that variable in the user's
environment the subagent's session landed in the user's own database,
and the login check and the snapshot looked at the default file instead.

The login check and the snapshot now read the file OPENCODE_DB names, and
the child gets OPENCODE_DB set to the shadow database.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@eshatalov
eshatalov merged commit f008dab into main Oct 3, 2026
1 check passed
@eshatalov
eshatalov deleted the opencode-v2 branch October 3, 2026 13:56
@eshatalov eshatalov mentioned this pull request Oct 11, 2026
eshatalov added a commit that referenced this pull request Oct 11, 2026
Version bump for 0.2.0. Minor rather than patch because OpenCode support
moved to OpenCode 2 (#6).

Ships since 0.1.1:
- #5 the newest installed copy of a harness is launched
- #6 support OpenCode 2
- #7 OpenCode model is set once, without a retry
- #8 Grok's requested model is no longer overridden by launch campaigns
- #9 README: the caller's instructions are an inline example

Done by Claude Opus 5.5 in Claude Code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant