@@ -15,8 +15,9 @@ It uses the [official Cursor SDK](https://cursor.com/docs/sdk/typescript) (`@cur
1515## Requirements
1616
1717- ** opencode 1.17+**
18- - ** Node.js 22+ on your ` PATH ` ** — opencode runs on [ Bun] ( https://bun.sh ) ; the plugin needs a
19- Node sidecar to host the Cursor SDK (see [ Runtime] ( #runtime-bun-and-the-node-sidecar ) ).
18+ - ** Node.js 22.13+ on your ` PATH ` ** (optional) — opencode runs on [ Bun] ( https://bun.sh ) and the
19+ plugin runs the Cursor SDK in-process by default; Node is only needed for the ` sidecar `
20+ transport fallback (see [ Transport] ( #transport ) ).
2021- A ** Cursor account and API key** (from the Cursor dashboard).
2122
2223## Install
@@ -28,7 +29,7 @@ curl -fsSL https://raw.githubusercontent.com/stablekernel/opencode-cursor/main/i
2829```
2930
3031Registers the plugin in your global ` opencode.json ` (` ~/.config/opencode/opencode.json ` ), checks
31- for Node.js 22+, and offers to set ` CURSOR_API_KEY ` . Flags:
32+ for Node.js 22.13 +, and offers to set ` CURSOR_API_KEY ` . Flags:
3233
3334- ` --project ` — write ` ./opencode.json ` in the current directory instead.
3435- ` --yes ` / ` -y ` — non-interactive.
@@ -137,20 +138,24 @@ See [SECURITY.md](./SECURITY.md) for the full threat model.
137138| ` mode ` | ` "agent" ` | Default conversation mode (` "agent" ` or ` "plan" ` ) |
138139| ` params ` | — | Default model params, e.g. ` { thinking: "high" } ` |
139140| ` settingSources ` | — | Cursor settings layers to load: ` ["project","user","all",...] ` — pulls in your Cursor skills, rules, and ` .cursor/mcp.json ` |
140- | ` sandbox ` | — | Run the agent's tools in Cursor's sandbox |
141+ | ` sandbox ` | — | Run the agent's tools in [ Cursor's sandbox] ( https://cursor.com/docs/agent/sandbox ) |
142+ | ` autoReview ` | ` false ` | Gate tool calls through Cursor's classifier-backed Auto review (best-effort, not a security boundary) |
141143| ` agents ` | — | Cursor subagent definitions |
142144| ` session ` | ` "auto" ` | Session reuse strategy — see [ Session reuse] ( #session-reuse-session ) |
143145| ` forwardMcp ` | ` true ` | Forward opencode's configured MCP servers to the Cursor agent |
144146| ` mcpServers ` | — | Extra MCP servers (Cursor ` McpServerConfig ` shape); merged with forwarded ones |
145147| ` toolDisplay ` | ` "blocks" ` | How Cursor's internal tool activity is shown — see [ Tool display] ( #tool-display ) |
146148| ` systemPrompt ` | ` "rules" ` | How opencode's system prompt reaches the agent — see [ System prompt] ( #system-prompt ) |
149+ | ` transport ` | — | Cursor agent transport (` "http1" ` \| ` "http2-direct" ` \| ` "sidecar" ` ) — see [ Transport] ( #transport ) |
147150
148151| Environment variable | Default | Meaning |
149152| --- | --- | --- |
150153| ` CURSOR_API_KEY ` | — | API key fallback |
151154| ` OPENCODE_CURSOR_MODEL_CACHE_TTL_MS ` | ` 86400000 ` | Model-list cache lifetime (ms) |
152155| ` OPENCODE_CURSOR_DEBUG ` | — | Set to ` 1 ` for trace logging on stderr |
153- | ` OPENCODE_CURSOR_SIDECAR ` | — | ` 1 ` = always use Node sidecar; ` 0 ` = never |
156+ | ` OPENCODE_CURSOR_TRANSPORT ` | — | Force a transport: ` http1 ` \| ` http2-direct ` \| ` sidecar ` — see [ Transport] ( #transport ) |
157+ | ` OPENCODE_CURSOR_STALL_MS ` | ` 60000 ` | Stream watchdog timeout (ms); ` 0 ` disables — see [ Reliability] ( #reliability ) |
158+ | ` OPENCODE_CURSOR_SIDECAR ` | — | Legacy: ` 1 ` maps to ` sidecar ` , ` 0 ` maps to ` http2-direct ` (superseded by ` OPENCODE_CURSOR_TRANSPORT ` ) |
154159
155160### Session reuse (` session ` )
156161
@@ -295,23 +300,56 @@ To force the fallback:
295300{ "provider" : { "cursor" : { "options" : { "toolDisplay" : " reasoning" } } } }
296301```
297302
298- ## Runtime: Bun and the Node sidecar
303+ ## Transport
299304
300- opencode runs on [ Bun] ( https://bun.sh ) , which has an ` node:http2 ` incompatibility with the Cursor
301- SDK's streaming RPC. The plugin transparently hosts the Cursor SDK in a short-lived ** Node child
302- process** when running under Bun. Under Node it runs in-process.
305+ opencode runs on [ Bun] ( https://bun.sh ) , whose ` node:http2 ` client is incompatible with the Cursor
306+ SDK's long-lived streaming RPC (` NGHTTP2_FRAME_SIZE_ERROR ` ; see
307+ [ oven-sh/bun #31499 ] ( https://github.com/oven-sh/bun/issues/31499 ) ). The plugin works around this by
308+ running the SDK over HTTP/1.1 in-process — no Node child process required. The historical Node
309+ sidecar remains as a rollback fallback.
303310
304- This is why ** Node.js 22+ on your ` PATH ` ** is required. If Node isn't found, the plugin warns once
305- and falls back to in-process (native Cursor tools will misbehave until Node is available).
311+ | Transport | Where it runs | When it's the default |
312+ | --- | --- | --- |
313+ | ` http1 ` | in-process, HTTP/1.1 + SSE (Bun-safe) | under Bun |
314+ | ` http2-direct ` | in-process, SDK's default HTTP/2 | under Node (tests, scripts, non-Bun hosts) |
315+ | ` sidecar ` | spawned Node child hosting the SDK | never (rollback only) |
316+
317+ Resolution order: the ` transport ` provider option → ` OPENCODE_CURSOR_TRANSPORT ` →
318+ legacy ` OPENCODE_CURSOR_SIDECAR ` (` 1 ` →` sidecar ` , ` 0 ` →` http2-direct ` ) → the per-runtime default
319+ above.
320+
321+ If you hit a regression on the in-process path, roll back to the sidecar:
322+
323+ ``` bash
324+ export OPENCODE_CURSOR_TRANSPORT=sidecar # requires Node.js 22.13+ on PATH
325+ ```
326+
327+ An explicit ` sidecar ` request with no Node on ` PATH ` falls back to ` http1 ` (Bun) or ` http2-direct `
328+ (Node) with a stderr notice.
329+
330+ ## Reliability
331+
332+ The provider classifies Cursor SDK errors into typed kinds (` agent-not-found ` , ` agent-busy ` ,
333+ ` rate-limit ` , ` network ` , ` auth ` , ` config ` , ` unknown ` ) and recovers per kind:
334+
335+ - ** agent-busy** — a previous crash left a run wedged; the send is retried once with the SDK's
336+ ` local.force ` escape hatch.
337+ - ** rate-limit / network** — bounded exponential backoff on the same agent.
338+ - ** auth / config** — fail fast (not retried).
339+
340+ Sends carry an idempotency key so a retry is a server-side dedupe, not a duplicate turn.
306341
307- Override with ` OPENCODE_CURSOR_SIDECAR=1 ` (always sidecar) or ` OPENCODE_CURSOR_SIDECAR=0 ` (never).
342+ A ** stream watchdog** guards against a wedged run that streams nothing: if no event arrives within
343+ ` OPENCODE_CURSOR_STALL_MS ` (default ` 60000 ` ), a pre-first-event stall cancels and force-resends
344+ once; a stall after partial output is surfaced as a terminal error rather than re-emitting the
345+ already-yielded prefix. Set ` OPENCODE_CURSOR_STALL_MS=0 ` to disable.
308346
309347## Troubleshooting
310348
311- - ** Native Cursor tools hang / "Tool execution aborted" (` NGHTTP2_FRAME_SIZE_ERROR ` ).** Node isn't
312- on your ` PATH ` . Install Node.js 22+, or force the sidecar with ` OPENCODE_CURSOR_SIDECAR=1 ` .
313- - ** "Running under Bun without a usable Node sidecar" warning. ** Install Node.js 22+, or set
314- ` OPENCODE_CURSOR_SIDECAR=0 ` to accept in-process behavior and silence the warning .
349+ - ** Native Cursor tools hang / "Tool execution aborted" (` NGHTTP2_FRAME_SIZE_ERROR ` ).** A
350+ ` http2-direct ` transport was forced under Bun. Unset ` OPENCODE_CURSOR_TRANSPORT ` (defaults to the
351+ Bun-safe ` http1 ` ), or roll back with ` OPENCODE_CURSOR_TRANSPORT= sidecar` (needs Node.js 22.13+ on
352+ ` PATH ` ) .
315353- ** Plugin enabled but no ` cursor ` provider/models appear, or you see a stale-version warning.**
316354 opencode caches the ` @latest ` plugin install on first use and never refreshes it.
317355 Exit opencode, delete ` ~/.cache/opencode/packages/@stablekernel/opencode-cursor@latest `
0 commit comments