Skip to content

Latest commit

 

History

History
626 lines (562 loc) · 39.8 KB

File metadata and controls

626 lines (562 loc) · 39.8 KB

Changelog

All notable changes to this project will be documented in this file.

[Unreleased]

[0.9.1-next.0] — 2026-08-26 (pre-release)

Fixes the subagent child-session pane fragmenting one flowing answer into many small messages. Not on latest; install with npm install @stablekernel/opencode-cursor@next to test.

  • Fix: subagent pane shows one growing transcript instead of fragment messages. Live activity snapshots were posted as a NEW message on every flush (the 1.5s timer, every tool result, plus up to four more on finalize), so a single subagent turn rendered as 5–20 fragments — a paragraph split mid-sentence across messages. The seeded prompt message's text part now grows in place: each flush PATCHes it via part.update with the FULL cumulative transcript (the endpoint the child session's tool parts already use; opencode publishes part.updated, so live views re-render). Falls back to the old per-flush message only when the seed response carries no parts or the PATCH fails. Tool activity no longer duplicates into the transcript markdown — the child session's tool parts already render it live on the subagent card — and resultSuffix + conversationSteps + the activity line merge into the single final transcript instead of three extra messages.

[0.9.0] — 2026-08-26

The Cursor agent can now use installed opencode plugins (#104), their skills mirror into .cursor/skills/, and the Cursor SDK's shell-parser diagnostic no longer leaks into the opencode TUI prompt (#111). Consolidates pre-releases 0.9.0-next.0 and 0.9.0-next.1.

  • Fix: shell-parser: tree-sitter natives are unavailable… no longer appears in the TUI prompt. @cursor/sdk's bundled shell-parser emits a one-shot console.warn when its vendored tree-sitter natives fail to load (e.g. when opencode runs the plugin under Bun); opencode renders plugin stderr into the prompt, so the line surfaced visually even though it is benign (shell command analysis degrades to parsingFailed, which the SDK handles). The existing console.log interceptor for the SDK's rules/skills load diagnostics now covers console.warn on both transports: in-process, known SDK warnings route through pluginLog("warn") to opencode's app.log instead of stderr; in the Node sidecar, matched lines forward as structured {ev:"log", level:"warn"} events over the JSONL protocol. Unrelated console.warn output passes through unchanged, and the message remains visible in opencode logs (service opencode-cursor).

  • Plugin tools bridge: other plugins' custom tools are now exposed to the Cursor agent. Custom tools from installed opencode plugins (e.g. opencode-pty's pty_spawn, context-mode's ctx_*) are bridged to Cursor via an opencode-plugin-tools MCP server (hand-rolled stdio JSON-RPC, no MCP SDK dependency, running under Bun). Permission handling mirrors opencode's Permission.evaluate — last matching rule wins per ask pattern; a rule resolving to ask (which can't prompt from Cursor) or no match fails closed. Controlled by forwardPluginTools and pluginTools.include/exclude.

  • Plugin-bundled skills are mirrored into .cursor/skills/ too. New lowest-priority scan roots in discoverSkills: the opencode plugin cache (~/.cache/opencode/packages/) and skills//skill/ dirs alongside file-based plugins, handling npm and git plugin specs. The per-turn re-sync also merges opencode's live skills inventory at the same priority. No new config surface — folds into forwardSkills; skills.include/exclude and permission filtering apply unchanged. Project/global skills always win on duplicate ids; user-owned skills are never overwritten.

  • Dependency bumps: @opencode-ai/plugin ^1.18.4 → ^1.18.21 (deps), @opencode-ai/sdk ^1.18.18 → ^1.18.21 (dev). Consolidates dependabot PRs #105 and #106.

[0.8.0] — 2026-08-21

Live Cursor subagent activity: the task card behaves like a native opencode subagent card — navigable while running, with a live activity subtitle — and the child session carries the subagent's full transcript (#99).

  • Cursor subagent transcripts in the TUI subagent view. The child session created for a Cursor subagent (task tool) is now seeded with the subagent's own activity — its assistant text, thinking, and tool calls with args and results — rendered from Cursor's conversationSteps, plus the final answer and duration. Previously only a post-completion activity summary appeared. Steps arrive as raw protobuf JSON, where agent.v1.ConversationStep's message oneof serialises to a single camelCase key ({ assistantMessage: … }, { toolCall: { shellToolCall: … } }) rather than the { type, message } shape of the SDK's public type; both are accepted. Transcript content is never truncated — the child session carries the subagent's full output.
  • Live activity on the Cursor subagent card. The SDK streams a local subagent's nested activity via taskUpdate payloads on the parent task's tool-call-delta updates (text, thinking, tool-start/tool-result with id + name + input). Those events now write real tool parts into the child session via part.update (an upsert — session/processor.ts creates parts the same way), so the task card shows a live ↳ <Tool> <title> subtitle while the subagent runs (the TUI builds that line purely from tool parts in the child session — tui/routes/session/index.tsx:2227-2279). The child session is created up-front when the task call starts and the task card's state.metadata.sessionId is stamped while the subagent is still running (via opencode's part.update endpoint, mirroring the native task tool's execute-time metadata publication), so the card is clickable / ctrl+x-navigable live. Tool calls complete when their tool-result event arrives; any call left open is completed at finalize. cursor_delegate also creates a child session seeded with its transcript, discoverable via the TUI's subagent panel.

[0.7.1] — 2026-08-05

The skills bridge (#90), per-model context limits and pricing (#89), and the compaction fixes (#91, #92).

  • Skills bridge: opencode skills are now mirrored into .cursor/skills/ for the Cursor agent. Both project-scoped and global skills are discovered (matching opencode's resolution order: .opencode/skills/, .claude/skills/, .agents/skills/, walked up to the git worktree root, plus global ~/.config/opencode/skills/ etc.), filtered through opencode's permission config, and materialised as a git-ignored mirror with a generated: opencode-cursor sentinel. An <available_skills> catalogue is appended to the generated system rule so the Cursor agent can discover and load skills on demand. Works for the primary agent, Cursor sub-agents, and cursor_delegate (which passes settingSources: ["project"]). ask-permissioned skills are withheld (the ask prompt can't cross the Cursor boundary). Opt out with forwardSkills: false; manual override with skills: { include, exclude }. User-owned .cursor/skills/<id>/ directories are never overwritten. config.skills.paths directories are also scanned (lowest priority, first-wins on duplicate ids). Symlinked skill directories and symlinked supporting files are followed (broken links and symlink loops are skipped). Mirror diagnostics (withheld skills, oversized files, write failures) route through opencode's structured plugin logging rather than the terminal, matching 0.7.0's logging change. config.skills.urls (HTTP catalogs) and skills bundled inside opencode plugin packages are not yet supported. Skills bridge contributed by Wayne Simpson (@WayneSimpson).

  • Per-model context limits and pricing generated from Cursor's docs. Each resolved model now carries a context-window limit and per-token pricing, emitted on the config channel opencode reads so the TUI can show cost and token counts. High-output frontier models carry a separate output-token limit. The data ships as a generated src/model-limits.ts, produced from Cursor's published tables by npm run sync:model-limits; a weekly CI job re-checks the committed data against those tables and fails if it has fallen behind (it verifies only — regenerating is a manual step).

  • Dependency bumps: @ai-sdk/provider 3.0.13 → 3.0.14, @types/node 26.0.0 → 26.1.2, vitest 4.1.9 → 4.1.10 (dev-dependencies group, #88).

  • opencode's threshold-triggered auto-compaction is now suppressed for Cursor models by default. The Cursor agent runtime already self-compacts on its own context threshold (preCompact hook with trigger: "auto"), so opencode-driven compaction was redundant — and it caused two real failures. First, the compaction turn runs with zero tools declared while the Cursor agent uses its own tools anyway, which opencode rejects (Tool call not allowed while generating summary) — mitigated in 0.7.1-next.1 (#91), and now avoided entirely for the automatic trigger. Second, compaction rewrites the transcript, which classifies as a divergence and mints a fresh Cursor agentId — and every distinct agentId permanently holds a guarded SQLite store.db/-wal/-shm triple that agent.close() cannot release (it only flushes analytics and releases the executor lease). That descriptor growth fed an uncatchable EXC_GUARD process kill.

    Suppression uses a large limit.input — the value opencode uses as its compaction threshold — leaving the real limit.context intact so the TUI context gauge and cost reporting still work. Manual /compact is unaffected and still relies on #91's fix.

    Tradeoff: this suppresses the proactive threshold trigger only, and opencode has no reactive context-overflow recovery wired up for this provider, so its transcript is no longer trimmed automatically. Ordinary turns send only the new message, but a cold replay (new session, expired agent, changed MCP set) resends everything; if that overflows the model the turn fails and /compact is the manual recovery. Opt back out with provider.cursor.options.autoCompaction: true.

  • Fixed: auto-compaction (and manual /compact) failed with Tool call not allowed while generating summary whenever the Cursor agent used a tool while summarizing. opencode declares zero tools on a compaction/summary turn, but the Cursor agent runs its own tools regardless; the provider forwarded that activity as provider-executed tool-call parts, which opencode's summary guard rejects. The provider now routes no-tools turns through the existing "reasoning" tool-display path, so Cursor's tool activity surfaces as reasoning text instead of crossing the tool-execution boundary. Manual /compact was affected all along; auto-compaction became reachable only in 0.7.1-next.0, because #89 published real per-model context windows — pre-0.7.1 opencode saw limit.context: 0 for every Cursor model, and a zero context limit structurally disables the auto-compaction trigger.

[0.7.0] — 2026-07-30

Structured logging (#85), the stream-watchdog tool-phase budget (#86), and the session-pool title-generation race fix (#84).

  • Structured logging via client.app.log() instead of raw console.*. The plugin's own diagnostics (transport fallback warnings, per-turn debug traces gated on OPENCODE_CURSOR_DEBUG=1) now route through opencode's plugin logging API (service: "opencode-cursor") rather than console.warn/ console.error. Falls back to console.* when no client is available (e.g. running the provider standalone).
  • Cursor SDK's own "rules"/"skills" load diagnostics captured and forwarded. @cursor/sdk's bundled local-exec runtime writes internal messages like LocalCursorRulesService load completed meta={durationMs, ruleCount} and AgentSkillsCursorRulesService load completed meta={durationMs, ruleCount, skillCount} straight to console.log, with no public logger hook to redirect it. These are now recognized (in-process transport via a narrowly scoped console.log interceptor; sidecar transport via the child process's own interceptor forwarding over the existing JSONL protocol) and re-emitted as structured opencode logs instead of raw terminal noise. Every other console.log call passes through unchanged.
  • Fixed: the stream watchdog killed healthy runs during long tool execution. The watchdog re-armed only on mapped event types, so a long shell command, build, or test suite that streamed nothing for 60s was cancelled and the turn lost. It now uses two budgets — an idle budget (OPENCODE_CURSOR_STALL_MS, default raised to 120000) and a larger tool-phase budget (OPENCODE_CURSOR_TOOL_STALL_MS, default 600000) applied while a tool call is in flight — and re-arms on any SDK update, including types the plugin doesn't model (progress/heartbeats). A tool-phase stall is terminal and names the in-flight tool. OPENCODE_CURSOR_STALL_MS=0 still disables the whole watchdog; the tool-phase bound is independently disabled with OPENCODE_CURSOR_TOOL_STALL_MS=0. Open tool calls are reconciled on turn-ended and on a forced resend, so a dropped completion can't pin a turn to the 10-minute budget.
  • Fixed: a non-numeric OPENCODE_CURSOR_STALL_MS stalled every turn immediately. Number("abc") is NaN; NaN <= 0 is false, so the guard passed and setTimeout(fn, NaN) fired at once. Env parsing now falls back to the default for non-finite values (an empty string still disables, preserving the historical escape hatch).
  • Fixed: an over-large stall budget overflowed to a ~1 ms deadline. A setTimeout delay is stored as a signed 32-bit int, so anything above 2147483647 is silently clamped to 1 — and the tool-phase stall message tells operators to raise OPENCODE_CURSOR_TOOL_STALL_MS, making the trap reachable by following the plugin's own advice. Setting it to e.g. 999999999999 stalled every tool-bearing turn within milliseconds while reporting no events for 999999999999ms. Both budgets are now capped at 2147483647.
  • Fixed: opencode's title-generation call could poison a session's pool entry. opencode forks a title-generation call on the same sessionID as the session's real first turn, concurrently and with an empty system prompt. classifyTurn's side-call detection only fires once a prior pool record exists, so on turn 1 both calls classified as "new" and both wrote to the pool — whichever agent-creation round-trip resolved last silently overwrote the other, leaving the session fingerprinted against the title prompt. Two fixes: the plugin's chat.params hook now marks opencode's title agent call as providerOptions.cursor.ephemeral = true (the provider already honored this flag but nothing set it), and withSessionLock (a per-sessionID async lock) now wraps agentRun's classify-then-acquire span so concurrent turns for one session serialize and the second call always observes the first's completed pool write.
  • Dependency bumps: @cursor/sdk 1.0.24 → 1.0.26, @opencode-ai/plugin (opencode-ai group).

[0.6.2] — 2026-07-28

Version-check UX cleanup from #79.

  • Fixed: startup toast no longer suspends into the user's first prompt on slow networks. The version-check toast previously ran setTimeout(callback, 2000) and then await _versionCheckPromise inside the callback, so a slow npm registry fetch could block the callback until after the user's first message was sent. The delay now runs after the promise resolves: _versionCheckPromise.then(async (result) => { await sleep(2000); showToast() }). The 2 s TUI-init pause is preserved; only the ordering changes.
  • Removed: terminal console.warn for update notifications. The warnIfStale function previously printed a multi-line warning to stderr on every startup when the plugin was outdated. This message is removed — the UI toast (introduced in 0.4.5) is the sole notification channel, avoiding duplicate noise in the terminal.
  • New: scripts/opencode-plugins-refresh. Helper script that compares cached @latest plugin versions against npm and optionally clears outdated caches so opencode re-fetches the latest on next launch. Supports --check (exit 1 if outdated, CI/cron-friendly) and --force (clear without prompting).
  • install.sh now offers to install opencode-plugins-refresh to ~/.local/bin (step 4).
  • PLUGIN_CACHE_PATH exported from src/version-check.ts. Single source of truth for the opencode plugin cache path (cross-platform). Used by both the startup warning and the cursor_update_plugin tool to build the removal command / actually clear the cache — removes the duplication that could cause them to diverge.
  • warnIfStale accepts an optional pre-fetched version string. warnIfStale(prefetchedLatest?) now skips the registry call when the caller has already resolved it. Paired with a single _latestVersionPromise in the plugin that is shared by the console warning, the UI toast, and the system-prompt notice — so only one npm registry fetch happens per startup regardless of how many paths consume it.

[0.6.1] — 2026-07-24

  • Fixed: reasoning/thinking variants showed as meaningless numbered entries for most models. Cursor returns every variant of a model with the same displayName (the model's own name), so the SDK-authoritative variant path (0.5.0) keyed off it and emitted collision-numbered junk — e.g. grok-4.5cursor-grok-4-5, cursor-grok-4-5-2-5; claude-opus-4-8opus-4-8-2-39 — which the global model cache surfaced in the picker of every project. ~20 of 32 models were affected. Same-named presets now fall back to param-derived keys (low/medium/high/xhigh/max/fast); presets with genuinely distinct labels are still honored.

[0.6.0] — 2026-07-24

Native Cursor subagents: the Cursor agent's task tool now renders as a navigable opencode child session instead of a dead "Unspecified Task" card.

  • Cursor subagents are now navigable opencode child sessions. When the Cursor agent runs its task tool, the plugin creates a real opencode child session (parentID = the current session) and links it to the task card, so it's clickable and reachable via ctrl+x down — like a native subagent. The child session is seeded with the subagent's prompt and Cursor's returned transcript plus a real duration line (posted as user-role messages via noReply; the public API can't synthesize assistant messages). Best-effort: if the opencode client is unavailable the card degrades to its previous, non-navigable form. The plugin hands its opencode client to the provider stream layer through an in-process bridge (src/provider/subagent-bridge.ts).
  • Fixed: generic Cursor subagents rendered as "Unspecified Task". Cursor's proto zero-value subagentType.kind ("unspecified") is no longer forwarded as the agent label; the card now falls back to opencode's "General Task" (or the real subagent name when Cursor provides one).

[0.5.0] — 2026-07-24

Native-experience overhaul: in-process HTTP/1.1 transport under Bun, typed-error reliability, and full streaming fidelity.

  • HTTP/1.1 in-process transport is now the default under Bun; the Node sidecar is a fallback. opencode runs on Bun, whose node:http2 client breaks the Cursor SDK's streaming RPC (NGHTTP2_FRAME_SIZE_ERROR; oven-sh/bun#31499). The SDK now runs in-process over HTTP/1.1 + SSE (Cursor.configure({ local: { useHttp1ForAgent: true } })) — no Node child process required. Three transports are selectable via the transport provider option or OPENCODE_CURSOR_TRANSPORT: http1 (Bun default), http2-direct (Node default), and sidecar (rollback). Resolution order is option → OPENCODE_CURSOR_TRANSPORT → legacy OPENCODE_CURSOR_SIDECAR (1sidecar, 0http2-direct) → per-runtime default. Roll back with OPENCODE_CURSOR_TRANSPORT=sidecar.
  • Typed error classification with per-kind recovery. SDK errors are classified into agent-not-found, agent-busy, rate-limit, network, auth, config, and unknown (by error name/status/code, never instanceof — sidecar-forwarded errors arrive as plain objects). agent-busy resends once with local.force; rate-limit/network retry with bounded backoff on the same agent; auth/config fail fast.
  • Idempotent resends. Every (re)send of a turn carries an idempotency key so a retry is a server-side dedupe, not a duplicate turn.
  • Stream watchdog. A wedged run that streams nothing is bounded by OPENCODE_CURSOR_STALL_MS (default 60000): a pre-first-event stall cancels and force-resends once; a stall after partial output surfaces a terminal error rather than re-emitting the already-yielded prefix. Set to 0 to disable.
  • Fixed: silent-replay turns dropped their token usage. A multi-message interjection replays leading messages silently and streams only the last; the usage from the silent turns is now summed into the visible turn's reported usage instead of being lost.
  • Live tool-input streaming. Cursor's partial-tool-call updates are bridged to incremental tool-input parts, so tool arguments stream as they arrive instead of appearing all at once when the call completes.
  • Thinking duration and compaction metadata. thinking-completed carries the reasoning duration, and Cursor's summary/compaction updates are surfaced as compaction events in the stream.
  • SDK-authoritative model variants. Variant construction prefers the SDK's own displayName/isDefault metadata rather than deriving it locally.
  • autoReview option and multi-root delegation. New autoReview provider option gates tool calls through Cursor's classifier-backed Auto review (best-effort, not a security boundary). cursor_delegate gains additionalCwds to combine extra workspace roots into a multi-root agent workspace.
  • Node floor raised to >=22.13 (engines.node, from >=22.0), and only needed for the sidecar fallback transport.
  • Dependency bumps. @cursor/sdk 1.0.23→1.0.24, @opencode-ai/plugin 1.17.14→1.18.4, @opencode-ai/sdk 1.17.14→1.18.4.

[0.4.7-next.0] — 2026-07-17

  • Fixed: subagents silently ran Cursor's server-side fast default (e.g. composer-2.5 in "fast" mode). A subagent inherits its parent agent's model but reached the provider with the model's options.params dropped, so the fast: "false" opencode default was lost and Cursor's server-side fast: true applied. Each model's default params are now threaded through the provider options and re-applied as a lowest-precedence floor, so fast stays off unless a variant or per-request param explicitly opts in. Set OPENCODE_CURSOR_DEBUG=1 to log the resolved model selection per turn (#71).

[0.4.6] — 2026-07-08

  • Fixed: newly released Cursor models didn't appear locally without a manual refresh. The auth loader warmed the model cache with a call that respected the 24h on-disk TTL and no-opped while the cache was still fresh. It now passes forceRefresh: true, so the catalog is force-refreshed via a live Cursor.models.list() on every opencode startup (fire-and-forget, no added latency); the config and provider.models hooks keep serving the existing cache instantly (#65).

[0.4.5] — 2026-07-07

  • Fixed: Cursor agent rejecting turns as "prompt injection" / "gaslighting." The provider flattened opencode's system prompt into the user-message transcript; Cursor's agent (which has its own system prompt) treated that as an injection attempt. opencode's system prompt is now delivered through Cursor's authoritative rules channel — written to a git-ignored .cursor/rules/opencode.mdc and loaded via settingSources — so opencode keeps control without being flagged. New systemPrompt option: "rules" (default), "message" (legacy inline), "omit". An explicit settingSources opt-out of the "project" layer is respected (degrades to "message" delivery), and a failed rule write degrades gracefully instead of failing the turn. The generated rule carries a generated: opencode-cursor sentinel so a user-owned opencode.mdc is never overwritten or deleted (#56).
  • Added: warm resume for multi-message prompt interjections. When a resumed Cursor agent has several queued user messages, the earlier messages are now delivered as silent turns and the final one streams, instead of forcing a cold full-transcript replay. A tail mismatch safely falls back to cold replay so no messages are dropped (#57).
  • Fixed: resumed turns against an expired Cursor agent are retried. A pooled agent whose server-side state expired would pass resume() locally but error in run.wait(); the turn now retries once with a fresh agent when nothing was emitted yet, re-pointing the pool (#52).
  • Fixed: file/dir @-mentions no longer fail the turn. The local Cursor SDK agent rejects attachment forms, so file/dir mentions are now noted as text (with a filesystem path for file:// sources) instead of attached; raw base64 data without a filename is guarded against inlining a blob into the note (#58).
  • Added: warn when the installed plugin lags the npm registry latest. A throttled (24h) startup check compares the installed version against latest and prints a one-line upgrade hint; skipped under CI / NO_UPDATE_NOTIFIER, never blocks init (#33).

[0.4.4] — 2026-06-24

  • Fixed: installer fails on opencode.jsonc files with trailing commas. The installer's naive JSON parse broke on JSONC's trailing-comma syntax (common in hand-edited configs). A dedicated JSONC parser (src/jsonc.ts) now strips trailing commas before parsing, so existing JSONC configs are detected and reused instead of clobbering or skipping them (#49).
  • Fixed: variant enum keys normalized and 'none' dropped for provider parity. Cursor's model params can advertise enum values like 'none' for reasoning/effort levels that don't make sense as a model variant. Enum keys are now normalized (lowercased, trimmed) and 'none' is excluded so the variant picker doesn't show a no-op variant. This aligns the Cursor provider's variant surface with other opencode providers (#48).
  • Fixed: redundant thinking variant dropped when an effort enum is present. When a model advertises both a boolean thinking param and an effort enum (e.g. low/medium/high), the variant builder previously emitted a standalone thinking variant that duplicated one of the effort levels. The standalone thinking variant is now suppressed in favor of the effort enum so the picker isn't cluttered with duplicates (#43).
  • Dependency consolidation. npm deps bumped in one pass: @connectrpc/connect-node 1.7.0→2.1.2, @cursor/sdk 1.0.19→1.0.20, @opencode-ai/plugin 1.17.7→1.17.9, @opencode-ai/sdk 1.17.7→1.17.9, @types/node 25.9.3→26.0.0 (#44). GitHub Actions updates consolidated into a single dependabot group (#45), and @opencode-ai/* packages are now grouped together (#46).

[0.4.3] — 2026-06-18

  • createPlan mapping emits markdown as plain text. Cursor's plan-mode tool returned markdown that opencode rendered as a raw code block. The createPlan tool output is now mapped to a plain-text part so the plan reads as formatted prose in the opencode transcript.

[0.4.2] — 2026-06-17

  • Fixed: missing @connectrpc/connect-node dependency. @cursor/sdk requires @connectrpc/connect-node at runtime but didn't declare it as a direct dependency, so installs that hoisted differently could fail with a module-not-found error. It's now an explicit dependency (#31).

[0.4.1] — 2026-06-17

  • read transcript label surfaces lines-read / total. The Read tool's transcript label now shows how many lines were read out of the total (e.g. read src/foo.ts (50/200)), so partial reads are visible in the conversation.
  • Removed obsolete sqlite3 native-binding self-heal. The workaround for a Bun/sqlite3 native-binding crash is no longer needed and has been removed.
  • Dependency bumps. @cursor/sdk 1.0.18→1.0.19, @opencode-ai/plugin 1.17.3→1.17.7, dev-dependencies group bumped.

[0.4.0] — 2026-06-16

  • Fixed: Cursor's fast tier is no longer silently forced on. The variant builder only mapped reasoning/effort params and dropped Cursor's fast toggle entirely, so it never reached providerOptions.cursor. Because Cursor marks the default variant of several models as fast: 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. Now fast defaults OFF — fast-capable models seed options.params.fast = "false" (sent every turn, and pinned into each reasoning variant so picking a reasoning level can't re-enable it) — and a fast picker variant lets you opt back in. Override per model via provider.cursor.models.<id>.options.params.fast.
  • Installer detects and reuses opencode.jsonc. The installer now recognizes both opencode.json and opencode.jsonc and reuses whichever exists instead of always writing opencode.json. The plugin is also pinned to @latest so opencode re-resolves to the newest release on each startup (#24).
  • Grep and glob tool blocks get a distinguishing title. Cursor's grep and glob tools both render as search-result blocks; they now carry distinct titles so you can tell them apart in the opencode transcript (#22).

[0.3.0] — 2026-06-11

  • 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-in session: true resumed 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: true is now an alias for "auto"; session: false keeps the always-fresh behavior. Set OPENCODE_CURSOR_DEBUG=1 to 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 config hook's startup snapshot meant mid-session MCP enable/disable never reached the Cursor agent. The chat.params hook 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 an mcpHash for this. Remote servers with a registered OAuth client are forwarded with a Cursor auth block so the agent runs its own OAuth flow; servers needing OAuth without a shareable clientId (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: false replay 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.
  • Patched transitive dependabot vulnerabilities via overrides. undici, tar, and node-gyp pinned via npm overrides to clear advisories in transitive dependencies (#16).

[0.2.0] — 2026-06-11

  • More Cursor tools map onto opencode's native tool renderers (blocks mode). Following the edit → diff-viewer mapping, Cursor's shell, read, write, glob, grep, ls, updateTodos, and task tool activity is now surfaced under opencode's registered bash, read, write, glob, grep, list, todowrite, and task tools, and Cursor's web search (which runs as an MCP tool) maps onto opencode's websearch renderer — so opencode renders its native UI (shell console, file viewer, todo checklist, subagent card, search results, …) instead of generic cursor_* blocks. Cursor's arg shape is translated to opencode's (e.g. pathfilePath, globPatternpattern, fileTextcontent); calls stay provider-executed (display-only, never re-run on disk).
  • Cleaner fallback blocks for tools without an opencode counterpart. readLints and delete now render as formatted cursor_* blocks (a diagnostics list / a one-line confirmation) instead of raw JSON, and every MCP tool's content array is flattened to readable text. Anything else — or a result with an unexpected shape — still falls back to a safe cursor_* block with the raw payload.

[0.1.0] — 2026-06-10

Pre-releases: 0.1.0-rc.1 and 0.1.0-rc.2 were published to the npm next dist-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.

Provider

  • Cursor provider backed by the official @cursor/sdk — drives a local Cursor agent (Agent.create / agent.send) and translates its onDelta callbacks into AI SDK LanguageModelV3 stream parts (text, reasoning, tool activity, usage). Implements both doStream() and doGenerate().
  • Per-request controls via providerOptions.cursormode (agent/plan), params, and thinking level; works with opencode's model variant picker.
  • Model variants auto-generated from Cursor.models.list parameters: 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 via Agent.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's local.force escape hatch. (Superseded by the fingerprint-guarded session: "auto" default; see Unreleased.)
  • Native diff viewer for Cursor edits (blocks mode). A Cursor edit tool call is now surfaced under opencode's registered edit tool with its real unified diff in metadata.diff, so opencode renders its built-in diff viewer instead of a generic block. The required oldString/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 registered edit tool) falls back to a safe cursor_edit block. Other Cursor tools (shell/read/mcp/…) remain prefixed cursor_* blocks.
  • toolDisplay provider option ("blocks" default | "reasoning"):
    • "blocks" (default) emits structured, provider-executed dynamic tool-call / tool-result parts so opencode renders native tool blocks. Names are cursor_-prefixed and sanitized (shellcursor_shell, myserver/find_symbolcursor_myserver_find_symbol) so they can't collide with opencode-registered tools, and carry providerExecuted: true + dynamic: true so ai v6's parseToolCall accepts them without registered-tool validation. Tool-results use the V3-spec result + isError fields. 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 status error surfaces 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").

Node sidecar (Bun compatibility)

  • Automatic Node sidecar — opencode runs on Bun, whose node:http2 client 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 and node is on PATH, 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 with OPENCODE_CURSOR_SIDECAR=1 (force on) or OPENCODE_CURSOR_SIDECAR=0 (force in-process / silence the Bun warning).

Plugin

  • opencode plugin (@stablekernel/opencode-cursor, resolved via the package's ./server export): auth hook (API-key login; the key is validated on first use rather than at login), config hook (auto-injects provider.cursor), provider.models() (live catalog via Cursor.models.list), and the cursor_refresh_models tool. 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.mcp entries are translated to Cursor McpServerConfig and passed to the local agent so it can use the same servers. Opt out with provider.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.

Delegation tools

  • cursor_cloud_agent — launch a Cursor cloud (background) agent on a remote repo via Agent.create({ cloud: { repos, autoCreatePR } }); returns the agent id, terminal status, result, and PR url. Progress is collected from run.onDidChangeStatus, onStep, and onDelta.
  • cursor_delegate — run a single local Cursor turn as a permission-gated, auditable opencode tool call (reuses the provider's acquireAgent + streamAgentTurn plumbing). Both tools honor opencode's permission config via ToolContext.ask and are fail-closed when no permission gate is present.

Tooling

  • 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).