All notable changes to this project will be documented in this file.
- Fixed: Cursor's
fasttier is no longer silently forced on. The variant builder only mapped reasoning/effort params and dropped Cursor'sfasttoggle entirely, so it never reachedproviderOptions.cursor. Because Cursor marks the default variant of several models asfast: true(composer-2.5, composer-2, and the gpt-*-codex line), omitting the param meant opencode silently ran the fast tier with no way to opt out. Nowfastdefaults OFF — fast-capable models seedoptions.params.fast = "false"(sent every turn, and pinned into each reasoning variant so picking a reasoning level can't re-enable it) — and afastpicker variant lets you opt back in. Override per model viaprovider.cursor.models.<id>.options.params.fast. - Fingerprint-guarded session reuse, now the default (
session: "auto"). Previously the provider created a fresh Cursor agent every turn and re-sent the whole transcript (robust but cache-hostile and increasingly costly as a conversation grows), while opt-insession: trueresumed one agent per session but could drift from opencode's history (edits/reverts/compaction) and was disturbed by non-chat side calls.session: "auto"(the new default) hashes only the parts opencode replays verbatim — the system prompt and the user-message sequence — and classifies each turn: a clean continuation resumes the pooled agent and sends only the new message (maximizing prefix cache hits); a side-call (system prompt differs, e.g. title generation) runs a fresh ephemeral agent without touching the pool; a divergence (edit/revert/compaction/queued messages) or a failed resume falls back to a fresh agent + full transcript and re-pools. Worst case is one self-healing full replay — never worse than the old default.session: trueis now an alias for"auto";session: falsekeeps the always-fresh behavior. SetOPENCODE_CURSOR_DEBUG=1to log per-turn classification and cache usage. - Session reuse survives opencode restarts. The pool's fingerprint records
persist (best-effort) to
~/.cache/opencode-cursor/session-pool.json(7-day TTL, 200-entry LRU cap), so the first turn after a restart resumes the session's Cursor agent — whose conversation lives in Cursor's own checkpoint store — instead of paying a cache-cold full-transcript replay. - MCP servers are re-forwarded live, per turn, with OAuth mapping. The
confighook's startup snapshot meant mid-session MCP enable/disable never reached the Cursor agent. Thechat.paramshook now forwards the live set each turn (client.mcp.status()for runtime truth,client.config.get()for launch specs). Because a resumed agent keeps its original servers, a changed set forces a fresh agent (full-transcript replay, re-pooled) so the new servers take effect — the session fingerprint carries anmcpHashfor this. Remote servers with a registered OAuth client are forwarded with a Cursorauthblock so the agent runs its own OAuth flow; servers needing OAuth without a shareableclientId(dynamic registration) are skipped with a one-time toast instead of forwarding a spec that would 401. - Fixed: text/reasoning streamed after a tool call rendered above the tool block. The earlier ordering fix closed parts on text↔reasoning transitions, but blocks-mode tool parts were emitted while the narration part stayed open — and hosts position a part where it started. Open text/reasoning parts are now closed before tool parts are emitted (except for buffered edit calls, which emit nothing until their result arrives, so narration isn't split needlessly).
- Tool outputs are included (truncated) in flattened transcripts. The
fresh/divergence/
session: falsereplay paths previously dropped Cursor tool results to bare[result of X]placeholders, so a fresh agent re-read a transcript with prior tool outputs missing. Outputs are now inlined and capped (2,000 chars per result, 500 per tool-call args) so context stays faithful without unbounded bloat.
- More Cursor tools map onto opencode's native tool renderers (blocks mode).
Following the
edit→ diff-viewer mapping, Cursor'sshell,read,write,glob,grep,ls,updateTodos, andtasktool activity is now surfaced under opencode's registeredbash,read,write,glob,grep,list,todowrite, andtasktools, and Cursor's web search (which runs as an MCP tool) maps onto opencode'swebsearchrenderer — so opencode renders its native UI (shell console, file viewer, todo checklist, subagent card, search results, …) instead of genericcursor_*blocks. Cursor's arg shape is translated to opencode's (e.g.path→filePath,globPattern→pattern,fileText→content); calls stay provider-executed (display-only, never re-run on disk). - Cleaner fallback blocks for tools without an opencode counterpart.
readLintsanddeletenow render as formattedcursor_*blocks (a diagnostics list / a one-line confirmation) instead of raw JSON, and every MCP tool'scontentarray is flattened to readable text. Anything else — or a result with an unexpected shape — still falls back to a safecursor_*block with the raw payload.
Pre-releases:
0.1.0-rc.1and0.1.0-rc.2were published to the npmnextdist-tag for validation ahead of this stable release.
Initial public release. A complete opencode integration for Cursor built on the
official @cursor/sdk: a streaming chat provider, an auth/config/model plugin,
and a permission-gated delegation tool surface.
- Cursor provider backed by the official
@cursor/sdk— drives a local Cursor agent (Agent.create/agent.send) and translates itsonDeltacallbacks into AI SDKLanguageModelV3stream parts (text, reasoning, tool activity, usage). Implements bothdoStream()anddoGenerate(). - Per-request controls via
providerOptions.cursor—mode(agent/plan),params, andthinkinglevel; works with opencode's model variant picker. - Model variants auto-generated from
Cursor.models.listparameters: one per reasoning/effort level a model advertises (boolean params collapse to a single on-variant). opencode's plan agent maps to Cursor plan mode. - Session reuse (
session: true) — keeps one Cursor agent per opencode session viaAgent.resume()across turns, with automatic fallback to a fresh agent. A run wedged by a crashed/duplicate process is recovered by retrying the send once with the SDK'slocal.forceescape hatch. (Superseded by the fingerprint-guardedsession: "auto"default; see Unreleased.) - Native diff viewer for Cursor edits (blocks mode). A Cursor
edittool call is now surfaced under opencode's registerededittool with its real unified diff inmetadata.diff, so opencode renders its built-in diff viewer instead of a generic block. The requiredoldString/newString(which Cursor does not expose) are reconstructed from the diff; the call is provider-executed so they are never applied to disk. Any edit without a usable diff (errors, unexpected shapes, or a host without a registerededittool) falls back to a safecursor_editblock. Other Cursor tools (shell/read/mcp/…) remain prefixedcursor_*blocks. toolDisplayprovider option ("blocks"default |"reasoning"):"blocks"(default) emits structured, provider-executed dynamictool-call/tool-resultparts so opencode renders native tool blocks. Names arecursor_-prefixed and sanitized (shell→cursor_shell,myserver/find_symbol→cursor_myserver_find_symbol) so they can't collide with opencode-registered tools, and carryproviderExecuted: true+dynamic: trueso ai v6'sparseToolCallaccepts them without registered-tool validation. Tool-results use the V3-specresult+isErrorfields. A tool call whose completion never arrives (run errored/cancelled mid-tool) is closed with a synthetic error result so the block never dangles as "Tool execution aborted", and a run that ends with statuserrorsurfaces the failure instead of finishing silently."reasoning"renders Cursor's internal tool activity (including the real MCP tool name) as concise[tool] …reasoning lines. Always safe — tool calls never cross opencode's tool-execution boundary; the fallback for older/non-V3 hosts (provider.cursor.options.toolDisplay: "reasoning").
- Automatic Node sidecar — opencode runs on Bun, whose
node:http2client is incompatible with the Cursor SDK's long-lived streaming RPC (NGHTTP2_FRAME_SIZE_ERROR), causing native tool calls to execute but never report completion. When Bun is detected andnodeis onPATH, the SDK agent is hosted in a Node child process and driven over a JSON-lines stdio protocol; the provider is otherwise unchanged. Under Node the SDK runs in-process. Override withOPENCODE_CURSOR_SIDECAR=1(force on) orOPENCODE_CURSOR_SIDECAR=0(force in-process / silence the Bun warning).
- opencode plugin (
@stablekernel/opencode-cursor, resolved via the package's./serverexport): auth hook (API-key login; the key is validated on first use rather than at login), config hook (auto-injectsprovider.cursor),provider.models()(live catalog viaCursor.models.list), and thecursor_refresh_modelstool. The auth loader warms a key-independent catalog cache so the model picker is populated on first authed load (and restart) rather than showing only the fallback snapshot. - MCP server forwarding — opencode's configured
config.mcpentries are translated to CursorMcpServerConfigand passed to the local agent so it can use the same servers. Opt out withprovider.cursor.options.forwardMcp. - Model discovery with a 24-hour cache (keyed by key fingerprint) and a built-in fallback snapshot (composer-2.5, claude-opus-4-8, claude-sonnet-4-6, gpt-5.5) for use without an API key.
cursor_cloud_agent— launch a Cursor cloud (background) agent on a remote repo viaAgent.create({ cloud: { repos, autoCreatePR } }); returns the agent id, terminal status, result, and PR url. Progress is collected fromrun.onDidChangeStatus,onStep, andonDelta.cursor_delegate— run a single local Cursor turn as a permission-gated, auditable opencode tool call (reuses the provider'sacquireAgent+streamAgentTurnplumbing). Both tools honor opencode'spermissionconfig viaToolContext.askand are fail-closed when no permission gate is present.
- Provider debug tracing — opt-in via
OPENCODE_CURSOR_DEBUG=1. - End-to-end CI: unit tests on two Node versions plus a full integration test (opencode loads the plugin, lists models, optionally runs a live chat turn).