Skip to content

feat: Transition project to OpenCode V2 - #13

Open
cshingle wants to merge 12 commits into
AshotN:masterfrom
cshingle:feat/opencode-v2
Open

cshingle wants to merge 12 commits into
AshotN:masterfrom
cshingle:feat/opencode-v2

Conversation

@cshingle

Copy link
Copy Markdown

I converted the plugin to OpenCode V2. This transition ended up being much larger than I expected. I tested it a fair amount in my own Intellij to find and fix the issues post migration. I didn't try the plugin with V1 so there are some things that I am not sure if they are bugs or just design decisions that are different that what I would have done. I wanted to get this PR out here before I spiraled into even more changes.

There are a few things that I ran into could be addressed with more effort.

  1. If the model dumps out a big chunk of data in one step it seems to lock the Intellij UI for a moment. I am pretty sure this is solvable. This seems like it should be solved in a separate PR.
  2. It feels a bit hacky to restart OpenCode when creating or resuming a session. I am not really happy with the current implementation but it is totally functional
  3. The tiny bit of input lag that you get from all Intellij terminals drives me nuts. I experience it on the standard Intellij terminal, so I highly doubt this is something new with the migration. There are some things I would like to try to address this. If I come up with a solution I will create a separate PR.

I Used OpenCode with Qwen 3.8 27B xhigh to assist with this PR.

- OpenCodeChecker: parse v2 --version output ('opencode v2.0.32'); gate on
  major >= 2 and the 'serve' subcommand; report v1 installs as
  IncompatibleVersion so the UI can show 'OpenCode 2.x required' instead of
  the misleading 'OpenCode not found' (AshotN#12)
- Transport: unwrap v2 {data: ...} envelopes in the response parsers;
  health/readiness via GET /api/info (ServerInfo); SSE stream at /api/event
- Server lifecycle: v2 serve flags only (--port/--hostname/--cors, no mdns);
  always launch with OPENCODE_PASSWORD (generated or reused); background
  service auto-detection from XDG_STATE_HOME/opencode/service.json with
  pid-verified /api/info health check; plugin connects to the detected
  service port via OpenCodePlugin.connectionPort
- Auth: Basic opencode:<password> on all routes; fixed 'opencode' username;
  v1 OPENCODE_SERVER_* env vars replaced by OPENCODE_PASSWORD
- New tool window screen for detected-but-unsupported 1.x installs

Verified against a live OpenCode 2.0.32 background service (pid-matched
/api/info handshake) and the v2.0.32 OpenAPI spec.
…ts (P2)

Rewrite the session/permission/MCP/config API clients and the SSE event
pipeline for the OpenCode 2.x server API:

- Sessions: /api/session routes with cursor pagination (server caps pages
  at 10), server-side directory filter, flat v2 session shape
  (location.directory, no version/summary/share), create/prompt/delete
  via POST /api/session/{id}/prompt {text} replacing prompt_async.
- Diffs: per-turn diff routes; latest-turn snapshot (GET /api/session/{id}/diff)
  as the live snapshot, cumulative snapshot via a single from/to user-turn
  range request; assistant-message snapshot.files replace the v1
  user-message summary.diffs for file counts.
- Permissions: GET/POST /api/session/{id}/permission[/{requestID}/reply]
  with {decision} body (once/always/reject unchanged on the wire).
- MCP: GET /api/mcp composite envelope with nested status objects
  (adds PENDING, drops NEEDS_CLIENT_REGISTRATION), connect/disconnect via
  /api/experimental/mcp/{name}/...
- Config: /api/config now returns layered config documents; MCP servers
  live under info.mcp.servers with a disabled flag.
- Events: v2 SSE envelope {id, created, type, location?, data} with
  location.directory scoping; session.execution.started -> busy,
  execution succeeded/failed/interrupted -> SessionExecutionFinished
  (drives the latest-turn diff fetch), session.idle -> idle,
  mcp.status.changed replaces mcp.tools.changed.
- Core service: live turn diff fetch replaces per-message diff flow
  (coalescer generalized to key-based), hasMessages/trackedFileCount
  derived from local diff state and loaded file counts.
- Removed v1 DTO fields (Session.version/summary/share, MessageDiffAvailable).
- Tests: session/permission/SSE/coalescer suites rewritten for v2
  shapes; liveTest event collector and prompt calls updated.
AGENTS.md:
- Replace stale jetbrains_*/webstorm_* tool names with the current
  idea.* MCP namespace (idea.read_file, idea.search_file, idea.build_project,
  idea.get_run_configurations, ...) and note idea.execute_tool as the
  dynamic fallback.
- Record the OpenCode 2.x-only policy (v1 rejection, Basic auth on /api/*,
  OPENCODE_PASSWORD for plugin-launched servers).
- Record build rules: JDK 21 requirement, the Kotlin 2.1.0 daemon crash on
  JDK 27 with the JAVA_HOME workaround, unit vs liveTest usage.
- Record the project's adapted Kotlin style (best-practice-kotlin skill:
  no !!, sealed result/event models, read-only collections at boundaries).

README.md:
- Requirements now require OpenCode 2.x; 1.x installs are detected and
  reported with an upgrade message.
- Server lifecycle: document background-service auto-detection
  (service.json state file, pid-verified) and v2 authentication behavior.
- Session list is scoped to the project's sessions.
- Mark the TUI features (Send Selection, New Session) as in progress for
  the 2.x rework — they still target v1 /tui/* endpoints.
- JetBrains MCP example uses the 2.x config format (mcp.servers wrapper,
  stream URL) and mentions the MCP Servers status action.
v2 does not emit session.idle or session.status when a turn completes;
only session.execution.{succeeded,failed,interrupted} marks the end of a
turn (verified live against v2.0.32). SseClient now derives the IDLE
status from the finished execution, keeping explicit idle/status parsing
for forward compatibility.

The v2 message list arrives newest-first, but the diff range route
requires from to precede to (otherwise 400 InvalidRequestError).
fetchUserMessageIds now sorts by time.created ascending.

Session creation also gains v2 session-scoped permission rules and an
optional model reference (the v1 OPENCODE_PERMISSION env var is gone).
- v2 servers always require Basic auth: the factory generates a
  client-side OPENCODE_PASSWORD (same design as the plugin launch flow)
  and the health check, session client, and SSE collector carry it.
- v2 removed the OPENCODE_PERMISSION env var; sessions are created with
  explicit permission rules and an optional pinned model
  (OPENCODE_TEST_MODEL).
- OPENCODE_TEST_CONFIG_JSON writes the provider definition as the global
  config in the isolated scenario: providers defined only in the user's
  own opencode.json are otherwise invisible to the test server and turns
  fail with provider.no-route / deprecated-catalog errors.
- OPENCODE_TEST_BIN selects a pre-installed opencode binary on machines
  without node/npm (its --version must still match the test list).
- Diff tests are resilient to small local models completing a turn
  without doing the work: bounded prompt resubmission with an explicit
  retry note, generous turn timeouts, and tolerant trailing-newline
  assertions.
- build.gradle.kts forwards the new variables; AGENTS.md documents the
  liveTest environment, including the Gradle daemon env fix.
…one)

v2 removed the v1 'attach' subcommand; the TUI is the default command and
connects to an explicit server via --server=<url>. The old launch command
made the TUI misparse the URL as the positional directory argument
(chdir ENOENT) and render nothing, leaving the tool window panel blank.

The equals form is required: with a space-separated value the URL is
still misparsed as the directory argument on v2.0.32. Verified:
'opencode --server=<url>' renders the full TUI against a running
background service.

Applies to the embedded classic and reworked terminal panels and the
external 'Open in Terminal' action; action descriptions no longer
reference 'attach'.
OpenCode 2.x always authenticates as the fixed "opencode" user with a
password, and its serve command no longer has mDNS options, so the
username field, the "Protect server launched by plugin" toggle, and the
mDNS settings were dead UI that silently did nothing:

- drop serverAuthUsername, protectPluginLaunchedServerWithAuth,
  serverMdnsEnabled, and serverMdnsDomain from the settings state,
  snapshot, UI, and tests
- the Server Authentication group now holds only the password field
  (blank means the plugin generates and stores one on first launch)
- the reserved OPENCODE_PASSWORD env-var error points at the password
  field
- README: the session list panel is labeled "Sessions" in the UI, not
  "Session changes"
- AGENTS.md: record the v2 TUI launch form (opencode --server=<url>;
  the space-separated value is misparsed as a directory) so the fix
  does not regress
- CHANGELOG: fill in the [Unreleased] section describing the v2
  migration
…igration edge cases

- fetchUserMessageIds: reverse arrival order instead of sorting by time.created
  (stable sort kept newest-first order on timestamp ties, inverting from/to
  and hitting the 400 InvalidRequestError from the diff range route)
- OpenCodePlugin: correct detectBackgroundService caching KDoc (negatives
  deliberately re-probed so mid-session service start is picked up)
- OpenCodeServerAuth: document transient-password shadowing semantics
- AGENTS.md: record the /tui/* v1-route exception with the v2.0.32 OpenAPI
  evidence (no TUI-control routes exist in v2)
…for v2

OpenCode 2.x removed the v1 /tui/ control routes, so the TUI is now
steered through its embedded terminal instead:

- "Send Selection" / "Send File" paste an @path#L... reference into the
  prompt input via bracketed paste (TuiPanel.appendToPrompt)
- New Session / session-list clicks create or reuse a session via
  POST /api/session (reusing the newest empty root session) and relaunch
  the embedded TUI against --session=<id> (TuiPanel.relaunchForSession)
- TuiPanel: startIfNeeded takes an initialSessionId; add appendToPrompt
  and relaunchForSession to both panel implementations
- OpenCodeTuiClient: drop TuiApiClient and drive the TuiPanel registered
  by the tool window (setTuiPanel); guard actions on the inline terminal
  setting; reuse-check now requires a root session
- Remove TuiApiClient, TuiEndpoints, and TuiApiClientTest
- README/CHANGELOG/AGENTS.md/API_ARCHITECTURE: document the
  terminal-based TUI control
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