Skip to content

feat: opencode v2 server support with v1 compatibility - #209

Open
bluetoe-99 wants to merge 1 commit into
dzianisv:mainfrom
bluetoe-99:feat/opencode-v2
Open

bluetoe-99 wants to merge 1 commit into
dzianisv:mainfrom
bluetoe-99:feat/opencode-v2

Conversation

@bluetoe-99

@bluetoe-99 bluetoe-99 commented Sep 25, 2026 •

Copy link
Copy Markdown

What

Fixes #194 (Android: chat input hidden behind keyboard).
Fixes #205 (opencode2 support?).
Fixes #156 (Unable to see the input box — same composer-behind-keyboard report as #194).

Adds opencode v2 server support while keeping every opencode 1.x (≤ 1.18.x) server working. npm install -g opencode-ai still installs the 1.x line (latest = 1.18.32 as of 2026-09-25), so both APIs are common in the wild and the client now speaks whichever the server speaks.

v2 (src/lib/sdk.ts, src/lib/v2-shape.ts)

  • /api/... endpoints, the v2 SSE envelope ({id, type, data}), the renamed message/part shapes, interrupt, staged revert, session diff.
  • The question tool arrives as a server-side form nested under data.form on form.created. Reading it flat silently dropped every prompt, so questions never appeared in the app.
  • Session-scoped permission and form replies.

v1 (src/lib/sdk-v1.ts, src/lib/api-detect.ts)

The transport main already ships, restored unchanged (sessions, prompts, abort, revert, diff, questions, permissions, agents, commands, providers, file browsing). A connect-time probe classifies the server by response content type — never status code, because unknown routes on both server generations fall through to the web SPA with 200 text/html:

probe v2.0.16 1.18.32
GET /api/info 200 application/json, or 401 unauthenticated 200 text/html (SPA)
GET /global/health 200 text/html (SPA) 200 application/json {healthy, version}

The detected api and serverVersion are stored on the connection, reused across screens, and invalidated when the URL changes; createClient() dispatches on them.

Selection and copying

  • MessageTextSheet + src/lib/message-text.ts: Select text and Edit are header buttons, and the sheet renders the whole message (text, reasoning, tool input/result/error, files) in a plain ScrollView where native selection actually works, with Copy all as the whole-message fallback. The bubble itself has no press at all: a press whose movement stays inside its bounds is kept by the touchable, which is what opened the message menu mid-scroll.
  • Inline selection is now iOS-only for user message text, message error text, code lines and diff lines. selectable <Text> inside FlatList rows is react-native#46999 (still open, no linked PR) and Markdown.tsx already works around it. ToolCallCard, ReasoningBlock, ErrorBoundary and Markdown.tsx are unchanged.
  • Wide code and diff lines wrap instead of scrolling sideways. CodeBlock's Copy still hands the clipboard the original string, so pasted code is byte-identical to what the server sent.
  • Keyboard avoidance via react-native-keyboard-controller, including system-bar insets, so the composer is never hidden behind the keyboard (Android: chat input hidden behind keyboard #194); the SSE stream reconnects on app foreground.

Screenshots

Wrapped code block with the header buttons and the reasoning block:

wrapped code block

Composer visible above the keyboard (#194):

composer above keyboard

Select text sheet with Copy all:

select text sheet

Verification

  • v2: live opencode 2.0.16 server; the question path was verified by replaying a captured form.created stream.
  • v1: live opencode serve on 1.18.32 — health, project, path, session create/list/update/delete/abort/messages, agents, commands, questions, permissions, providers, and the wrapped {"payload": ...} event stream through the existing unwrap in stores/events.ts.
  • tsc --noEmit clean; npm test 363/363, including new suites for the probe classifier, the v2 shape translator, message-text assembly, and two component regression files.

docs/v2-api-map.md records the verified API maps, event schemas and probe behaviour for both server generations.

Notes

DCO signed off.

opencode 2.x replaced the HTTP API: routes moved under /api/, the SSE envelope
changed from {"payload":{id,type,properties}} to {id,type,data}, message/part
payloads were renamed (Session.Info, Message.Info, UserMessage/AssistantMessage),
and question prompts became server-side forms. `npm install -g opencode-ai` still
installs the 1.x line (1.18.32 as of 2026-09-24), so a client has to speak both
APIs.

v2 client (src/lib/sdk.ts, src/lib/v2-shape.ts):
- /api/... endpoints, the v2 message shapes, interrupt, staged revert, session diff
- SSE via /api/event; the question tool arrives as a form nested under data.form on
  form.created - reading it flat silently dropped every prompt
- session-scoped permission and form replies

v1 client (src/lib/sdk-v1.ts, src/lib/api-detect.ts):
- the transport main already ships, restored unchanged (sessions, prompts, abort,
  revert, diff, questions, permissions, agents, commands, providers, file browsing)
- a connect-time probe classifies the server by response content type: unknown
  routes on both server generations answer 200 text/html through the web SPA, so
  status codes cannot discriminate. /api/info JSON means v2, /global/health JSON
  means v1; api + serverVersion are recorded on the connection and createClient()
  dispatches on them

Message UI:
- selection and copying rework around MessageTextSheet: Select text / Edit are
  header buttons and the sheet renders the whole message (text, reasoning, tool
  input/result/error, files) in a plain ScrollView where native selection works,
  with Copy all as the whole-message fallback. The bubble has no press at all: a
  press whose movement stays inside its bounds is kept by the touchable, which
  opened the message menu mid-scroll.
- inline selection is iOS-only for user message text, message error text, code
  lines and diff lines: selectable <Text> inside FlatList rows is the Android bug
  (react-native#46999, still open) that Markdown.tsx already works around.
  ToolCallCard, ReasoningBlock, ErrorBoundary and Markdown.tsx are unchanged.
- wide code and diff lines wrap instead of scrolling sideways; CodeBlock's Copy
  still hands the clipboard the original string, so pasted code is byte-identical
- keyboard avoidance via react-native-keyboard-controller, including system-bar
  insets, so the composer is never hidden behind the keyboard; SSE reconnects on
  app foreground

Verified against live servers: opencode 2.0.16 (v2 endpoints, and the question path
replayed from a captured form.created stream) and opencode 1.18.32 (`opencode
serve`: health, project, path, session create/list/update/delete/abort/messages,
agents, commands, questions, permissions, providers, and the wrapped {"payload":...}
event stream through the existing unwrap in stores/events.ts).

docs/v2-api-map.md records the verified API maps, event schemas and probe behaviour
for both server generations.

Signed-off-by: bluetoe-99 <102478736+bluetoe-99@users.noreply.github.com>
@bluetoe-99 bluetoe-99 mentioned this pull request Sep 25, 2026

This branch has not been deployed

No deployments
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.

opencode2 support? Android: chat input hidden behind keyboard Unable to see the input box

1 participant