Repository navigation
feat(opencode): support the OpenCode V2 server API alongside V1 - #3
Merged
Merged
Conversation
OpenCode 2.x replaced the whole server contract: every route moved under
/api, responses are wrapped in {data}, messages became a typed union, the
event stream was rewritten into lifecycle events and questions became
forms. A V1 client pointed at a V2 server only ever sees the SPA HTML.
Connections now go through ProtocolDetectingOpenCodeApi, which probes
GET /api/info and falls back to the unchanged V1 client when the server
is not V2. The V2 client maps the new shapes onto the existing app DTOs
and OpenCodeEvent stream, so the chat, activity and schedule layers keep
working unmodified:
- sessions/messages walk cursor pages newest-first; archive is emulated
with session metadata because V2 has no archive field
- prompts apply the chosen agent/model per prompt under a per-session lock
- forms map to question cards (labels answered as option values)
- the event translator keeps part ids aligned with the REST transcript,
maps execution lifecycle to busy/idle/abort, and keeps long compactions
alive for stall detection; reconnects reconcile pending permissions,
forms and running sessions since V2 does not replay events
- message delete and config editing are gated off on V2, where the server
offers no equivalent
OpenCodeV2LiveTest exercises the backend against a real server and only
runs when OPENCODE_LIVE_URL is set.
The protocol probe authenticated both api/info and the V1 global/health confirmation as V2's fixed 'opencode' user, so a V1 server running with a custom OPENCODE_SERVER_USERNAME could no longer be reached. Only api/info keeps the fixed user now. LocalOpenCodeBackend.protocol also stops building a delegate: it is read from Compose on the main thread and building one reads from disk.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
OpenCode 2.x replaced the whole server contract (routes under
/api,{data}envelopes, a typed message union, a rewritten event stream, questions → forms). A V1 client pointed at a V2 server only sees the SPA HTML. Upstream (yuga-hashimoto/and-code) has no V2 support yet.ProtocolDetectingOpenCodeApiprobesGET /api/infoand falls back to the unchanged V1OpenCodeApiClient(interface extraction only) when the server is not V2. Transient 5xx never decide the protocol; V1 is confirmed only throughglobal/health; a re-detected protocol restarts the event stream.OpenCodeV2ApiClientmaps V2 onto the existing DTOs, so chat / activity / schedule layers are untouched:orderonly on the first page; the server rejects it alongside a cursor)metadata(V2 has no archive field)OpenCodeV2EventTranslatorkeeps part ids aligned with the REST transcript, maps execution lifecycle to busy / idle / abort, and keeps long compactions alive for stall detection. Reconnects reconcile pending permissions, forms and running sessions because V2 does not replay events.PATCH /api/experimental/configonly acceptsshell):editMessages/ newconfigEditingcapabilities are gated off on V2.Verification
./gradlew spotlessCheck :app:testGithubDebugUnitTest(JDK 17 + SDK 35 in Docker): 1312 tests, 0 failures.OpenCodeV2LiveTestagainst a real OpenCode v2.0.28 server (keyless Zen free model): 8/8 passed, none skipped — health/401, session CRUD + archive, catalog/files/vcs, a real streamed turn with a shell tool reloaded without duplicate parts, permission ask → once, question form answer, abort, summarize. It is skipped unlessOPENCODE_LIVE_URLis set, so CI is unaffected.detektreports NO-SOURCE at the root (pre-existing configuration, same as CI).Pre-PR review
判定: APPROVE
ブロッカー
なし
提案(非ブロッキング)
RemoteRuntimeTarget.capabilitiesとLocalRuntimeTarget.capabilitiesが、V2 判定後にeditMessages/configEditingを false にすることを確認するテストがありません。ServerInfoViewModel.kt:48の既定値は?: false、SettingsViewModel.kt:248は?: trueで食い違っています。また capability は getter なので、プローブ後に値が変わっても画面状態は自動で再計算されません。api/infoが 5xx を返し続けると、current()が呼び出しのたびに再プローブします。LocalOpenCodeBackend.protocol(LocalOpenCodeBackend.kt:93-94)をcached?.backend?.protocolに変えたので、invalidate()直後や最初の delegate 生成前は null を返し、各 Target は V1 として capability を出します。ローカルランタイムは V1 固定(LocalRuntimeTarget.kt:54-58 のコメントどおり)なので実害はありません。将来ローカルに V2 を同梱する場合は、この一時的な窓に注意してください。チェック済み項目
前提:
.git/worktrees/opencode-v2/logs/HEADで、HEAD が7d0b10fa(fix(opencode): confirm V1 servers with the profile's username)であることを確認しました。シェルが使えないためgit show/git diffは実行できず、変更後のファイルを直接読んで確認しています。ブロッカーの修正:
ProtocolDetectingOpenCodeApi.kt:318-353を確認しました。fetchStatusにusername引数が追加されています。既定値は V2 の固定ユーザーOpenCodeV2ApiClient.USERNAMEです。confirmLegacy()のglobal/healthはprofile.username.ifBlank { "opencode" }で認証します。V1 クライアントのOpenCodeApiClient.kt:743と同じ扱いです。カスタムユーザー名の V1 サーバーの判定:
api/infoが 401 を返し、本文に_tagがなければ、:309 の分岐でconfirmLegacy()に進み、aliceで認証して V1 と判定されます。前回指摘したリグレッションは解消しています。回帰テスト:
OpenCodeV2ApiClientTest.kt:110-128のprobe confirms a V1 server that uses a custom usernameが、次の 3 点を明示的に検証しています。api/infoがopencode:secretで認証されることglobal/healthがalice:secretで認証されることOpenCodeProtocol.V1になることprofile()ヘルパーに追加されたusername引数の既定値は"opencode"なので、既存のテストには影響しません。LocalOpenCodeBackend:
protocolgetter がdelegate()を呼ばなくなり、readMetadata()/LocalServerAuth.password()のディスク I/O が Compose のメインスレッドで走らなくなったことを確認しました(LocalOpenCodeBackend.kt:88-94)。KDoc もこの変更に合わせて更新されています。その他: 修正範囲に新しいユーザー向け文字列、リソースキー、シークレットはありません。i18n にも影響しません。
CI 結果: spotlessCheck と
:app:testGithubDebugUnitTest1312 件の成功(live テスト 8 件、スキップ 0)は依頼者の報告です。こちらでは再実行していません。