@stablekernel/opencode-cursor 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@ import {
6
6
  resolveControls,
7
7
  resolveCursorApiKey,
8
8
  streamAgentTurn
9
- } from "../chunk-D4YQ7ZEM.js";
9
+ } from "../chunk-BTI2NHEE.js";
10
10
 
11
11
  // src/model-cache.ts
12
12
  import { mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -195,11 +195,45 @@ function buildModelV2Map(items) {
195
195
  }
196
196
 
197
197
  // src/plugin/mcp-config.ts
198
- function translateMcpServers(mcp) {
198
+ var NEEDS_AUTH_STATUS = /* @__PURE__ */ new Set(["needs_auth", "needs_client_registration"]);
199
+ function oauthConfig(entry) {
200
+ if (entry.type !== "remote") return void 0;
201
+ return entry.oauth ? entry.oauth : void 0;
202
+ }
203
+ function toCursorAuth(oauth) {
204
+ if (!oauth?.clientId) return void 0;
205
+ const scopes = oauth.scope?.split(/\s+/).filter(Boolean);
206
+ return {
207
+ CLIENT_ID: oauth.clientId,
208
+ ...oauth.clientSecret ? { CLIENT_SECRET: oauth.clientSecret } : {},
209
+ ...scopes && scopes.length > 0 ? { scopes } : {}
210
+ };
211
+ }
212
+ function findUnshareableOAuthServers(mcp, status) {
213
+ const names = [];
214
+ if (!mcp) return names;
215
+ for (const [name, entry] of Object.entries(mcp)) {
216
+ if (!entry || entry.type !== "remote") continue;
217
+ if (!status && entry.enabled === false) continue;
218
+ const s2 = status?.[name]?.status;
219
+ if (status && s2 !== "connected" && !NEEDS_AUTH_STATUS.has(s2 ?? ""))
220
+ continue;
221
+ const oauth = oauthConfig(entry);
222
+ const needsOAuth = Boolean(oauth) || NEEDS_AUTH_STATUS.has(s2 ?? "");
223
+ if (needsOAuth && !toCursorAuth(oauth)) names.push(name);
224
+ }
225
+ return names;
226
+ }
227
+ function translateMcpServers(mcp, status) {
199
228
  const out = {};
200
229
  if (!mcp) return out;
201
230
  for (const [name, entry] of Object.entries(mcp)) {
202
- if (!entry || entry.enabled === false) continue;
231
+ if (!entry) continue;
232
+ if (status) {
233
+ if (status[name]?.status !== "connected") continue;
234
+ } else if (entry.enabled === false) {
235
+ continue;
236
+ }
203
237
  if (entry.type === "local") {
204
238
  const [command, ...args] = entry.command ?? [];
205
239
  if (!command) continue;
@@ -211,10 +245,14 @@ function translateMcpServers(mcp) {
211
245
  };
212
246
  } else if (entry.type === "remote") {
213
247
  if (!entry.url) continue;
248
+ const oauth = oauthConfig(entry);
249
+ const auth = toCursorAuth(oauth);
250
+ if (oauth && !auth) continue;
214
251
  out[name] = {
215
252
  type: "http",
216
253
  url: entry.url,
217
- ...entry.headers && Object.keys(entry.headers).length > 0 ? { headers: entry.headers } : {}
254
+ ...entry.headers && Object.keys(entry.headers).length > 0 ? { headers: entry.headers } : {},
255
+ ...auth ? { auth } : {}
218
256
  };
219
257
  }
220
258
  }
@@ -311,8 +349,7 @@ async function runDelegate(params) {
311
349
  mode,
312
350
  cwd: params.cwd,
313
351
  ...params.sandbox !== void 0 ? { sandbox: params.sandbox } : {},
314
- ...params.agentId ? { agentId: params.agentId } : {},
315
- session: false
352
+ ...params.agentId ? { resumeAgentId: params.agentId } : {}
316
353
  });
317
354
  const text = [];
318
355
  const reasoning = [];
@@ -322,7 +359,10 @@ async function runDelegate(params) {
322
359
  for await (const event of streamAgentTurn(
323
360
  acquired.agent,
324
361
  { text: params.prompt },
325
- { mode, ...params.abortSignal ? { abortSignal: params.abortSignal } : {} }
362
+ {
363
+ mode,
364
+ ...params.abortSignal ? { abortSignal: params.abortSignal } : {}
365
+ }
326
366
  )) {
327
367
  switch (event.type) {
328
368
  case "text-delta":
@@ -335,7 +375,8 @@ async function runDelegate(params) {
335
375
  toolActivity.push({ name: event.name, isError: false });
336
376
  break;
337
377
  case "tool-result":
338
- if (event.isError) toolActivity.push({ name: event.name, isError: true });
378
+ if (event.isError)
379
+ toolActivity.push({ name: event.name, isError: true });
339
380
  break;
340
381
  case "usage":
341
382
  usage = event.usage;
@@ -494,11 +535,18 @@ function apiKeyFromAuth(auth) {
494
535
  }
495
536
  var CursorPlugin = async (input) => {
496
537
  let capturedApiKey;
538
+ const client = input?.client;
539
+ const directory = input?.directory;
540
+ let forwardMcp = true;
541
+ let userMcp = {};
542
+ const warnedOAuth = /* @__PURE__ */ new Set();
497
543
  return {
498
544
  auth: {
499
545
  provider: PROVIDER_ID,
500
546
  loader: async (getAuth) => {
501
- const apiKey = resolveCursorApiKey(apiKeyFromAuth(await getAuth().catch(() => void 0)));
547
+ const apiKey = resolveCursorApiKey(
548
+ apiKeyFromAuth(await getAuth().catch(() => void 0))
549
+ );
502
550
  if (apiKey) {
503
551
  capturedApiKey = apiKey;
504
552
  void discoverModels({ apiKey });
@@ -519,8 +567,8 @@ var CursorPlugin = async (input) => {
519
567
  config.provider ??= {};
520
568
  const existing = config.provider[PROVIDER_ID] ?? {};
521
569
  const existingOptions = existing.options ?? {};
522
- const forwardMcp = existingOptions["forwardMcp"] !== false;
523
- const userMcp = existingOptions["mcpServers"] ?? {};
570
+ forwardMcp = existingOptions["forwardMcp"] !== false;
571
+ userMcp = existingOptions["mcpServers"] ?? {};
524
572
  const mcpServers = forwardMcp ? { ...userMcp, ...translateMcpServers(config.mcp) } : userMcp;
525
573
  config.provider[PROVIDER_ID] = {
526
574
  name: "Cursor",
@@ -551,10 +599,47 @@ var CursorPlugin = async (input) => {
551
599
  // agent-based default only applies when no mode was set.
552
600
  "chat.params": async (input2, output) => {
553
601
  if (input2.model?.providerID !== PROVIDER_ID) return;
554
- output.options = { ...output.options ?? {}, sessionID: input2.sessionID };
602
+ output.options = {
603
+ ...output.options ?? {},
604
+ sessionID: input2.sessionID
605
+ };
555
606
  if (input2.agent === "plan" && output.options["mode"] === void 0) {
556
607
  output.options["mode"] = "plan";
557
608
  }
609
+ if (forwardMcp && client) {
610
+ try {
611
+ const query = directory ? { query: { directory } } : void 0;
612
+ const [cfgRes, statusRes] = await Promise.all([
613
+ client.config.get(),
614
+ client.mcp.status(query)
615
+ ]);
616
+ const liveMcp = cfgRes?.data?.mcp;
617
+ const status = statusRes?.data;
618
+ if (status) {
619
+ output.options["mcpServers"] = {
620
+ ...userMcp,
621
+ ...translateMcpServers(liveMcp, status)
622
+ };
623
+ const unshareable = findUnshareableOAuthServers(
624
+ liveMcp,
625
+ status
626
+ ).filter((name) => !warnedOAuth.has(name));
627
+ if (unshareable.length > 0) {
628
+ for (const name of unshareable) warnedOAuth.add(name);
629
+ const plural = unshareable.length > 1;
630
+ void client.tui.showToast({
631
+ body: {
632
+ title: "Cursor MCP",
633
+ message: `Skipped OAuth MCP server${plural ? "s" : ""}: ${unshareable.join(", ")}. opencode's token can't be shared with the Cursor agent; configure an OAuth clientId to forward ${plural ? "them" : "it"}.`,
634
+ variant: "warning"
635
+ }
636
+ }).catch(() => {
637
+ });
638
+ }
639
+ }
640
+ } catch {
641
+ }
642
+ }
558
643
  },
559
644
  tool: {
560
645
  cursor_refresh_models: {
@@ -562,7 +647,9 @@ var CursorPlugin = async (input) => {
562
647
  args: {},
563
648
  execute: async () => {
564
649
  const result = await discoverModels({ forceRefresh: true });
565
- const lines = result.models.map((m) => `- ${m.id} \u2014 ${m.displayName}`);
650
+ const lines = result.models.map(
651
+ (m) => `- ${m.id} \u2014 ${m.displayName}`
652
+ );
566
653
  const header = result.source === "live" ? `Refreshed ${result.models.length} Cursor models (live):` : `Could not fetch live models (${result.source}). ${result.warning ?? ""}`.trim();
567
654
  return {
568
655
  title: `Cursor models (${result.source})`,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/model-cache.ts","../../src/fallback-models.ts","../../src/model-variants.ts","../../src/model-discovery.ts","../../src/plugin/model-v2.ts","../../src/plugin/mcp-config.ts","../../src/plugin/cursor-tools.ts","../../src/provider/cloud-agent.ts","../../src/provider/delegate.ts","../../src/plugin/index.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ModelListItem } from \"@cursor/sdk\";\n\n/** Default cache lifetime: 24 hours, overridable via env. */\nconst DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;\n\nfunction ttlMs(): number {\n const raw = process.env.OPENCODE_CURSOR_MODEL_CACHE_TTL_MS;\n const parsed = raw ? Number.parseInt(raw, 10) : NaN;\n return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TTL_MS;\n}\n\nfunction cacheDir(): string {\n const base =\n process.env.XDG_CACHE_HOME?.trim() ||\n (homedir() ? join(homedir(), \".cache\") : tmpdir());\n return join(base, \"opencode-cursor\");\n}\n\nfunction cacheFile(fingerprint: string): string {\n return join(cacheDir(), `models-${fingerprint}.json`);\n}\n\n/**\n * Key-independent \"latest known catalog\" file. The `config` plugin hook runs\n * without access to the stored API key, so it can't read the per-key cache.\n * This file lets a keyless caller (the config hook) seed opencode's model\n * picker with the real catalog that a previous *authed* load discovered.\n */\nfunction latestCacheFile(): string {\n return join(cacheDir(), \"models-latest.json\");\n}\n\n/** The latest-catalog seed is kept longer than the per-key cache: the catalog\n * is stable and this only feeds pre-auth UI seeding. */\nconst LATEST_TTL_MS = 30 * 24 * 60 * 60 * 1000;\n\ninterface CacheEnvelope {\n savedAt: number;\n models: ModelListItem[];\n}\n\nfunction readCacheFile(file: string, maxAgeMs: number): ModelListItem[] | undefined {\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\")) as CacheEnvelope;\n if (!parsed?.savedAt || !Array.isArray(parsed.models)) return undefined;\n if (Date.now() - parsed.savedAt > maxAgeMs) return undefined;\n return parsed.models;\n } catch {\n return undefined;\n }\n}\n\nfunction writeCacheFile(file: string, models: ModelListItem[]): void {\n try {\n mkdirSync(cacheDir(), { recursive: true });\n const envelope: CacheEnvelope = { savedAt: Date.now(), models };\n writeFileSync(file, JSON.stringify(envelope), \"utf8\");\n } catch {\n // Caching is an optimization; ignore write failures.\n }\n}\n\n/**\n * Return cached models for the given API-key fingerprint when present and still\n * fresh, otherwise `undefined`. Never throws on a missing/corrupt cache.\n */\nexport function readModelCache(fingerprint: string): ModelListItem[] | undefined {\n return readCacheFile(cacheFile(fingerprint), ttlMs());\n}\n\n/** Persist the discovered model list (per-key cache + key-independent latest\n * catalog). Best-effort; never throws. */\nexport function writeModelCache(fingerprint: string, models: ModelListItem[]): void {\n writeCacheFile(cacheFile(fingerprint), models);\n writeCacheFile(latestCacheFile(), models);\n}\n\n/**\n * Return the most recently discovered catalog regardless of API key, when\n * present and within {@link LATEST_TTL_MS}. Used by the keyless `config` hook to\n * seed the picker with the real catalog after a prior authed load.\n */\nexport function readLatestModelCache(): ModelListItem[] | undefined {\n return readCacheFile(latestCacheFile(), LATEST_TTL_MS);\n}\n","import type { ModelListItem } from \"@cursor/sdk\";\n\n/**\n * A small static snapshot of well-known Cursor models, used only when live\n * discovery is unavailable (no API key, offline, or an SDK error). The live\n * `Cursor.models.list()` result always takes precedence; this just lets the\n * provider appear in opencode with sensible defaults so the user can reach the\n * login flow. Refresh the real catalog with the `cursor_refresh_models` tool.\n */\nexport const FALLBACK_MODELS: ModelListItem[] = [\n {\n id: \"composer-2.5\",\n displayName: \"Composer 2.5\",\n description: \"Cursor's default agent model (fallback entry).\",\n parameters: [\n { id: \"thinking\", displayName: \"Thinking\", values: [{ value: \"off\" }, { value: \"on\" }] },\n ],\n },\n { id: \"claude-opus-4-8\", displayName: \"Claude Opus 4.8 (via Cursor)\" },\n { id: \"claude-sonnet-4-6\", displayName: \"Claude Sonnet 4.6 (via Cursor)\" },\n { id: \"gpt-5.5\", displayName: \"GPT-5.5 (via Cursor)\" },\n];\n","import type { ModelListItem } from \"@cursor/sdk\";\n\n/**\n * A Cursor model \"variant\" as opencode stores it: an options object that, when\n * the variant is selected, is merged into `providerOptions.cursor` and read back\n * by {@link resolveControls}.\n */\nexport interface CursorVariant {\n params?: Record<string, string>;\n mode?: \"agent\" | \"plan\";\n}\n\nconst REASONING_PARAM = /think|reason|effort/i;\nconst BOOLEAN_VALUES = new Set([\"true\", \"false\"]);\n\n/**\n * Derive opencode model variants from a Cursor model's parameters so the\n * variant picker can expose thinking/reasoning levels. Each variant's object is\n * exactly what {@link resolveControls} consumes. Plan mode is NOT a variant:\n * opencode's plan agent (Tab) is mapped to Cursor's plan mode by the plugin's\n * `chat.params` hook.\n */\nexport function buildModelVariants(item: ModelListItem): Record<string, CursorVariant> {\n const out: Record<string, CursorVariant> = {};\n\n for (const param of item.parameters ?? []) {\n if (!REASONING_PARAM.test(param.id)) continue;\n const values = (param.values ?? []).map((v) => v.value);\n if (values.length === 0) continue;\n\n if (values.every((v) => BOOLEAN_VALUES.has(v))) {\n // Boolean toggle (e.g. thinking=[\"false\",\"true\"]). Literal true/false\n // variant names are meaningless in the picker — surface a single\n // variant named after the param that switches it on. \"Off\" is the\n // model's default (no variant selected).\n if (values.includes(\"true\")) {\n out[param.id.toLowerCase()] = { params: { [param.id]: \"true\" } };\n }\n continue;\n }\n\n for (const value of values) {\n // Key by the bare value (e.g. \"high\"); prefix with the param id only\n // when two params share a value (e.g. reasoning-low vs effort-low).\n const key = out[value] === undefined ? value : `${param.id}-${value}`;\n out[key] = { params: { [param.id]: value } };\n }\n }\n\n return out;\n}\n","import type { ModelListItem } from \"@cursor/sdk\";\nimport { fingerprintApiKey, resolveCursorApiKey } from \"./api-key.js\";\nimport { readLatestModelCache, readModelCache, writeModelCache } from \"./model-cache.js\";\nimport { FALLBACK_MODELS } from \"./fallback-models.js\";\nimport { loadCursorSdk } from \"./cursor-runtime.js\";\nimport { buildModelVariants, type CursorVariant } from \"./model-variants.js\";\n\nexport type ModelSource = \"live\" | \"cache\" | \"fallback\";\n\nexport interface DiscoveryResult {\n models: ModelListItem[];\n source: ModelSource;\n /** Human-readable note when discovery degraded (e.g. missing key, error). */\n warning?: string;\n}\n\nexport interface DiscoverOptions {\n /** Explicit key; falls back to CURSOR_API_KEY. */\n apiKey?: string;\n /** Bypass the on-disk cache and force a live `Cursor.models.list()`. */\n forceRefresh?: boolean;\n}\n\n/**\n * Discover the Cursor model catalog. Tries (in order): on-disk cache (unless\n * forced), live `Cursor.models.list()`, then the static fallback snapshot.\n * Always resolves — failures degrade to the fallback with a `warning`.\n */\nexport async function discoverModels(options: DiscoverOptions = {}): Promise<DiscoveryResult> {\n const apiKey = resolveCursorApiKey(options.apiKey);\n if (!apiKey) {\n // No key here (e.g. the keyless `config` hook). Prefer the real catalog a\n // prior authed load cached, so opencode's picker shows the full list rather\n // than only the static snapshot.\n const latest = readLatestModelCache();\n if (latest && latest.length > 0) return { models: latest, source: \"cache\" };\n return {\n models: FALLBACK_MODELS,\n source: \"fallback\",\n warning:\n \"No Cursor API key found. Run `opencode auth login` and choose Cursor, or set CURSOR_API_KEY. Showing fallback models.\",\n };\n }\n\n const fingerprint = fingerprintApiKey(apiKey);\n\n if (!options.forceRefresh) {\n const cached = readModelCache(fingerprint);\n if (cached && cached.length > 0) {\n return { models: cached, source: \"cache\" };\n }\n }\n\n try {\n const { Cursor } = await loadCursorSdk();\n const models = await Cursor.models.list({ apiKey });\n if (models.length > 0) {\n writeModelCache(fingerprint, models);\n return { models, source: \"live\" };\n }\n return {\n models: FALLBACK_MODELS,\n source: \"fallback\",\n warning: \"Cursor.models.list() returned no models; showing fallback models.\",\n };\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n // A stale cache is better than nothing on a transient failure.\n const stale = readModelCache(fingerprint);\n if (stale && stale.length > 0) {\n return { models: stale, source: \"cache\", warning: `Live discovery failed (${detail}); using cached models.` };\n }\n return {\n models: FALLBACK_MODELS,\n source: \"fallback\",\n warning: `Live discovery failed (${detail}); showing fallback models.`,\n };\n }\n}\n\n/** True when a model exposes a thinking/reasoning parameter. */\nexport function modelSupportsReasoning(item: ModelListItem): boolean {\n return (item.parameters ?? []).some((p) => /think|reason/i.test(p.id));\n}\n\n/** Shape of a single entry in opencode's `provider.<id>.models` config map. */\nexport interface OpencodeModelConfigEntry {\n id: string;\n name: string;\n attachment: boolean;\n reasoning: boolean;\n temperature: boolean;\n tool_call: boolean;\n /**\n * opencode model variants (thinking levels + plan mode). They MUST be seeded\n * here: opencode discards the plugin `provider.models()` hook for providers\n * absent from its models.dev catalog, so this config map is the only channel\n * through which cursor model variants reach the picker.\n */\n variants: Record<string, CursorVariant>;\n}\n\n/**\n * Map discovered Cursor models to opencode's provider config `models` map. The\n * Cursor SDK runs an agent (it calls tools itself), so every model is marked\n * `tool_call: true` and `temperature: false`.\n */\nexport function toOpencodeModels(items: ModelListItem[]): Record<string, OpencodeModelConfigEntry> {\n const out: Record<string, OpencodeModelConfigEntry> = {};\n for (const item of items) {\n out[item.id] = {\n id: item.id,\n name: item.displayName || item.id,\n attachment: true,\n reasoning: modelSupportsReasoning(item),\n temperature: false,\n tool_call: true,\n variants: buildModelVariants(item),\n };\n }\n return out;\n}\n","import type { Model as ModelV2 } from \"@opencode-ai/sdk/v2\";\nimport type { ModelListItem } from \"@cursor/sdk\";\nimport { modelSupportsReasoning } from \"../model-discovery.js\";\nimport { buildModelVariants } from \"../model-variants.js\";\n\nexport const PROVIDER_ID = \"cursor\";\nexport const NPM_PACKAGE = \"@stablekernel/opencode-cursor\";\n\n/**\n * The npm specifier opencode uses to load the provider SDK. Defaults to the\n * published package name; can be overridden with a `file://...` URL (which\n * opencode imports directly, skipping a registry install) via\n * `OPENCODE_CURSOR_PROVIDER_NPM` — useful for local development and CI before\n * the package is published.\n */\nexport function providerNpm(): string {\n return process.env.OPENCODE_CURSOR_PROVIDER_NPM?.trim() || NPM_PACKAGE;\n}\n\n/**\n * Build opencode's rich runtime `Model` objects from discovered Cursor models.\n * Used by the auth-aware `provider.models()` hook. Fields opencode does not get\n * from the Cursor catalog are filled with safe defaults (zero cost — Cursor\n * bills separately; generous context limits).\n */\nexport function buildModelV2Map(items: ModelListItem[]): Record<string, ModelV2> {\n const out: Record<string, ModelV2> = {};\n for (const item of items) {\n out[item.id] = {\n id: item.id,\n providerID: PROVIDER_ID,\n api: { id: item.id, url: \"\", npm: providerNpm() },\n name: item.displayName || item.id,\n capabilities: {\n temperature: false,\n reasoning: modelSupportsReasoning(item),\n attachment: true,\n toolcall: true,\n input: { text: true, audio: false, image: true, video: false, pdf: false },\n output: { text: true, audio: false, image: false, video: false, pdf: false },\n interleaved: false,\n },\n cost: { input: 0, output: 0, cache: { read: 0, write: 0 } },\n limit: { context: 200_000, output: 32_000 },\n status: \"active\",\n options: {},\n headers: {},\n release_date: \"\",\n variants: buildModelVariants(item) as ModelV2[\"variants\"],\n };\n }\n return out;\n}\n","import type { Config } from \"@opencode-ai/plugin\";\nimport type { McpServerConfig } from \"@cursor/sdk\";\n\n/** The value type of opencode's `config.mcp` map. */\ntype OpencodeMcp = NonNullable<Config[\"mcp\"]>;\ntype OpencodeMcpEntry = OpencodeMcp[string];\n\n/**\n * Translate opencode's configured MCP servers (`config.mcp`) into the Cursor\n * SDK's `McpServerConfig` shape so the same servers (e.g. Serena) can be handed\n * to the Cursor agent via `Agent.create({ mcpServers })`.\n *\n * MCP servers are independent processes addressed by a launch spec, so opencode\n * and the Cursor agent can each connect to the same server. Disabled entries\n * (`enabled: false`) are skipped. opencode-only fields with no Cursor\n * equivalent (timeout, oauth) are dropped.\n */\nexport function translateMcpServers(mcp: Config[\"mcp\"]): Record<string, McpServerConfig> {\n const out: Record<string, McpServerConfig> = {};\n if (!mcp) return out;\n\n for (const [name, entry] of Object.entries(mcp) as Array<[string, OpencodeMcpEntry]>) {\n if (!entry || entry.enabled === false) continue;\n\n if (entry.type === \"local\") {\n const [command, ...args] = entry.command ?? [];\n if (!command) continue;\n out[name] = {\n type: \"stdio\",\n command,\n ...(args.length > 0 ? { args } : {}),\n ...(entry.environment && Object.keys(entry.environment).length > 0\n ? { env: entry.environment }\n : {}),\n };\n } else if (entry.type === \"remote\") {\n if (!entry.url) continue;\n out[name] = {\n type: \"http\",\n url: entry.url,\n ...(entry.headers && Object.keys(entry.headers).length > 0\n ? { headers: entry.headers }\n : {}),\n };\n }\n }\n\n return out;\n}\n","import { tool, type ToolContext, type ToolDefinition } from \"@opencode-ai/plugin\";\nimport { runCloudAgent } from \"../provider/cloud-agent.js\";\nimport { runDelegate } from \"../provider/delegate.js\";\n\nconst s = tool.schema;\n\nexport interface CursorToolDeps {\n /**\n * Resolve the Cursor API key (from opencode auth, captured by the plugin's\n * auth loader, or the CURSOR_API_KEY env var). Returns undefined when no key\n * is available so the tool can return a clear \"needs auth\" message.\n */\n resolveApiKey: () => string | undefined;\n /** Default working directory for local delegation (the session worktree/cwd). */\n defaultCwd: () => string;\n}\n\nconst NEEDS_AUTH =\n \"No Cursor API key available. Run `opencode auth login` and choose Cursor, or set CURSOR_API_KEY.\";\n\n/**\n * Request approval for a sensitive Cursor invocation. `context.ask` is the\n * opencode mechanism a custom tool uses to gate itself; it honors the user's\n * `permission` config (allow resolves silently, ask prompts, deny rejects).\n *\n * Returns `{ ok: true }` when approved, or `{ ok: false, reason }` when the\n * request was rejected. We deliberately do not claim the rejection was a policy\n * \"deny\" — `context.ask` rejects on both an explicit deny and an internal\n * failure, and conflating them produces misleading messages. The gate is\n * fail-closed: any rejection (including a host that doesn't provide `ask`)\n * blocks the call rather than silently allowing it.\n */\nasync function requestApproval(\n context: ToolContext,\n permission: string,\n patterns: string[],\n metadata: Record<string, unknown>,\n): Promise<{ ok: boolean; reason?: string }> {\n try {\n await context.ask({ permission, patterns, always: patterns, metadata });\n return { ok: true };\n } catch (err) {\n return { ok: false, reason: err instanceof Error ? err.message : String(err) };\n }\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Build the Cursor delegation tools that complement the native provider:\n * - `cursor_cloud_agent`: run a background agent on a remote repo (optionally\n * opening a PR) — work that maps poorly onto the synchronous provider path.\n * - `cursor_delegate`: run a single local Cursor turn as a permission-gated,\n * auditable tool call (for users who want Cursor as a delegate rather than\n * as their primary model).\n *\n * Both are gated via `context.ask`, so a user `permission` policy controls them.\n */\nexport function buildCursorTools(deps: CursorToolDeps): Record<string, ToolDefinition> {\n return {\n cursor_cloud_agent: tool({\n description:\n \"Launch a Cursor background ('cloud') agent on a remote repository. Runs autonomously \" +\n \"(may take minutes) and can open a pull request. Returns the cloud agent id, final \" +\n \"status, result, and PR url when available.\",\n args: {\n prompt: s.string().describe(\"The task/instruction for the background agent.\"),\n repoUrl: s\n .string()\n .describe(\"Target repository URL, e.g. https://github.com/owner/repo.\"),\n startingRef: s\n .string()\n .optional()\n .describe(\"Branch or ref to start from (defaults to the repo default branch).\"),\n model: s.string().optional().describe(\"Cursor model id (optional for cloud).\"),\n mode: s.enum([\"agent\", \"plan\"]).optional().describe(\"Conversation mode.\"),\n thinking: s.string().optional().describe(\"Thinking level, e.g. 'high'.\"),\n autoCreatePR: s\n .boolean()\n .optional()\n .describe(\"Open a pull request automatically when finished.\"),\n workOnCurrentBranch: s\n .boolean()\n .optional()\n .describe(\"Operate on the current branch instead of creating a new one.\"),\n },\n execute: async (args, context) => {\n const apiKey = deps.resolveApiKey();\n if (!apiKey) return NEEDS_AUTH;\n\n const approval = await requestApproval(\n context,\n \"cursor_cloud_agent\",\n [args.repoUrl],\n { repoUrl: args.repoUrl, autoCreatePR: args.autoCreatePR ?? false },\n );\n if (!approval.ok) {\n return `Cloud agent not approved for ${args.repoUrl}${approval.reason ? `: ${approval.reason}` : \".\"}`;\n }\n\n let result;\n try {\n result = await runCloudAgent({\n apiKey,\n prompt: args.prompt,\n repoUrl: args.repoUrl,\n ...(args.startingRef ? { startingRef: args.startingRef } : {}),\n ...(args.model ? { model: args.model } : {}),\n ...(args.mode ? { mode: args.mode } : {}),\n ...(args.thinking ? { thinking: args.thinking } : {}),\n ...(args.autoCreatePR !== undefined ? { autoCreatePR: args.autoCreatePR } : {}),\n ...(args.workOnCurrentBranch !== undefined\n ? { workOnCurrentBranch: args.workOnCurrentBranch }\n : {}),\n abortSignal: context.abort,\n });\n } catch (err) {\n return `Cloud agent failed: ${errorMessage(err)}`;\n }\n\n const lines = [\n `Cloud agent ${result.agentId} — ${result.status}`,\n ...(result.prUrl ? [`PR: ${result.prUrl}`] : []),\n ...(result.branches.length > 0\n ? [`Branches: ${result.branches.map((b) => b.branch ?? b.repoUrl).join(\", \")}`]\n : []),\n ...(result.result ? [\"\", result.result] : []),\n ...(result.progress.length > 0 ? [\"\", \"Progress:\", ...result.progress] : []),\n ];\n\n return {\n title: `Cursor cloud agent (${result.status})`,\n output: lines.join(\"\\n\"),\n metadata: {\n agentId: result.agentId,\n status: result.status,\n prUrl: result.prUrl ?? null,\n durationMs: result.durationMs ?? null,\n },\n };\n },\n }),\n\n cursor_delegate: tool({\n description:\n \"Delegate a single subtask to a local Cursor agent and return its result. Use to hand \" +\n \"off discrete work to Cursor while keeping your primary model in control. Permission-gated.\",\n args: {\n prompt: s.string().describe(\"The subtask to delegate to Cursor.\"),\n model: s.string().describe(\"Cursor model id to run the delegation on.\"),\n mode: s.enum([\"agent\", \"plan\"]).optional().describe(\"Conversation mode.\"),\n thinking: s.string().optional().describe(\"Thinking level, e.g. 'high'.\"),\n cwd: s\n .string()\n .optional()\n .describe(\"Working directory (defaults to the session directory).\"),\n sandbox: s.boolean().optional().describe(\"Run the agent's tools in Cursor's sandbox.\"),\n agentId: s\n .string()\n .optional()\n .describe(\"Resume a specific Cursor agent id instead of starting fresh.\"),\n },\n execute: async (args, context) => {\n const apiKey = deps.resolveApiKey();\n if (!apiKey) return NEEDS_AUTH;\n\n const approval = await requestApproval(context, \"cursor_delegate\", [args.model], {\n model: args.model,\n prompt: args.prompt,\n });\n if (!approval.ok) {\n return `Delegation to ${args.model} not approved${approval.reason ? `: ${approval.reason}` : \".\"}`;\n }\n\n let result;\n try {\n result = await runDelegate({\n apiKey,\n prompt: args.prompt,\n model: args.model,\n cwd: args.cwd ?? context.directory ?? deps.defaultCwd(),\n ...(args.mode ? { mode: args.mode } : {}),\n ...(args.thinking ? { thinking: args.thinking } : {}),\n ...(args.sandbox !== undefined ? { sandbox: args.sandbox } : {}),\n ...(args.agentId ? { agentId: args.agentId } : {}),\n abortSignal: context.abort,\n });\n } catch (err) {\n return `Delegation failed: ${errorMessage(err)}`;\n }\n\n const toolNote =\n result.toolActivity.length > 0\n ? `\\n\\n(${result.toolActivity.length} tool call(s)` +\n `${result.toolActivity.some((t) => t.isError) ? \", some failed\" : \"\"})`\n : \"\";\n\n return {\n title: `Cursor delegate (${args.model})`,\n output: (result.text || \"(no text output)\") + toolNote,\n metadata: {\n agentId: result.agentId,\n model: args.model,\n toolCalls: result.toolActivity.length,\n usage: result.usage ?? null,\n },\n };\n },\n }),\n };\n}\n","import type { AgentModeOption, ConversationStep, InteractionUpdate } from \"@cursor/sdk\";\nimport { loadCursorSdk } from \"../cursor-runtime.js\";\nimport { buildModelSelection } from \"./controls.js\";\n\n/**\n * A target repository for a cloud agent. Cursor's cloud runtime accepts an\n * array of repos; the tool surface exposes the common single-repo case.\n */\nexport interface CloudRepoTarget {\n url: string;\n startingRef?: string;\n}\n\nexport interface CloudAgentParams {\n apiKey: string;\n /** The instruction/task for the background agent. */\n prompt: string;\n /** Target repository URL (e.g. https://github.com/owner/repo). */\n repoUrl: string;\n /** Branch/ref to start from. Defaults to the repo's default branch. */\n startingRef?: string;\n /** Cursor model id. Optional for cloud (server picks a default otherwise). */\n model?: string;\n /** Conversation mode; defaults to \"agent\". */\n mode?: AgentModeOption;\n /** Convenience for the Cursor `thinking` model param (e.g. \"high\"). */\n thinking?: string;\n /** When true, open a PR automatically once the agent finishes. */\n autoCreatePR?: boolean;\n /** Operate on the current branch instead of creating a new one. */\n workOnCurrentBranch?: boolean;\n /** Cancels the run when aborted (wired to the tool's abort signal). */\n abortSignal?: AbortSignal;\n}\n\nexport interface CloudAgentBranch {\n repoUrl: string;\n branch?: string;\n prUrl?: string;\n}\n\nexport interface CloudAgentResult {\n agentId: string;\n /** Terminal run status: \"finished\" | \"error\" | \"cancelled\". */\n status: string;\n /** The agent's final textual result, when present. */\n result?: string;\n /** First PR url found across result branches (when `autoCreatePR`). */\n prUrl?: string;\n /** Per-repo branch/PR info reported by the run. */\n branches: CloudAgentBranch[];\n durationMs?: number;\n /** Human-readable progress lines captured from status/step/summary updates. */\n progress: string[];\n}\n\n/**\n * Run a Cursor background (\"cloud\") agent against a remote repository and wait\n * for it to finish, returning the final status, result text, and any PR url.\n *\n * A cloud agent can run for minutes and produce a PR rather than a chat reply,\n * which maps poorly onto the synchronous provider `doStream` path — so this is\n * exposed as an opencode tool instead (see plugin/index.ts). Progress is\n * collected into `progress[]` (opencode custom tools return a single result\n * rather than a live stream) and the lifecycle is bridged through the same\n * `loadCursorSdk` plumbing the provider uses.\n */\nexport async function runCloudAgent(params: CloudAgentParams): Promise<CloudAgentResult> {\n const { Agent } = await loadCursorSdk();\n const modelSelection = params.model\n ? buildModelSelection(params.model, params.thinking ? { thinking: params.thinking } : undefined)\n : undefined;\n const mode: AgentModeOption = params.mode ?? \"agent\";\n\n const createOptions = {\n apiKey: params.apiKey,\n ...(modelSelection ? { model: modelSelection } : {}),\n mode,\n cloud: {\n repos: [\n {\n url: params.repoUrl,\n ...(params.startingRef ? { startingRef: params.startingRef } : {}),\n },\n ],\n ...(params.autoCreatePR !== undefined ? { autoCreatePR: params.autoCreatePR } : {}),\n ...(params.workOnCurrentBranch !== undefined\n ? { workOnCurrentBranch: params.workOnCurrentBranch }\n : {}),\n },\n };\n\n const progress: string[] = [];\n const agent = await Agent.create(createOptions);\n\n // `onDelta` carries fine-grained updates; for a cloud (background) run the\n // higher-signal progress arrives via `onStep` (whole conversation steps) and\n // `run.onDidChangeStatus`. We capture all three — whichever the runtime emits.\n const onDelta = ({ update }: { update: InteractionUpdate }) => {\n if (update.type === \"summary\") progress.push(`summary: ${update.summary}`);\n };\n\n const onStep = ({ step }: { step: ConversationStep }) => {\n progress.push(`step: ${describeStep(step)}`);\n };\n\n try {\n const run = await agent.send(params.prompt, { mode, onDelta, onStep });\n\n const off = run.onDidChangeStatus?.((status: string) => {\n progress.push(`status: ${status}`);\n });\n const onAbort = () => {\n run.cancel().catch(() => {});\n };\n params.abortSignal?.addEventListener(\"abort\", onAbort);\n\n try {\n const result = await run.wait();\n const branches: CloudAgentBranch[] = (result.git?.branches ?? []).map((b) => ({\n repoUrl: b.repoUrl,\n ...(b.branch ? { branch: b.branch } : {}),\n ...(b.prUrl ? { prUrl: b.prUrl } : {}),\n }));\n const prUrl = branches.find((b) => b.prUrl)?.prUrl;\n return {\n agentId: agent.agentId,\n status: result.status,\n ...(result.result !== undefined ? { result: result.result } : {}),\n ...(prUrl ? { prUrl } : {}),\n branches,\n ...(result.durationMs !== undefined ? { durationMs: result.durationMs } : {}),\n progress,\n };\n } finally {\n off?.();\n params.abortSignal?.removeEventListener(\"abort\", onAbort);\n }\n } finally {\n try {\n agent.close();\n } catch {\n // best effort; cloud agents persist server-side regardless.\n }\n }\n}\n\n/** A short, log-friendly description of a conversation step for progress output. */\nfunction describeStep(step: ConversationStep): string {\n if (step.type === \"toolCall\") return `toolCall:${step.message.type}`;\n return step.type;\n}\n","import type { AgentModeOption } from \"@cursor/sdk\";\nimport type { CursorUsage } from \"./agent-events.js\";\nimport { streamAgentTurn } from \"./agent-events.js\";\nimport { resolveControls } from \"./controls.js\";\nimport { acquireAgent } from \"./session-pool.js\";\n\nexport interface DelegateParams {\n apiKey: string;\n /** The subtask to delegate to the Cursor agent. */\n prompt: string;\n /** Cursor model id to run the delegation on. */\n model: string;\n /** Conversation mode; defaults to \"agent\". */\n mode?: AgentModeOption;\n /** Convenience for the Cursor `thinking` model param (e.g. \"high\"). */\n thinking?: string;\n /** Working directory the local agent operates in. */\n cwd: string;\n /** Run the agent's tools inside Cursor's sandbox. */\n sandbox?: boolean;\n /** Resume a specific Cursor agent by id instead of creating a fresh one. */\n agentId?: string;\n /** Cancels the run when aborted (wired to the tool's abort signal). */\n abortSignal?: AbortSignal;\n}\n\nexport interface DelegateToolActivity {\n name: string;\n isError: boolean;\n}\n\nexport interface DelegateResult {\n agentId: string;\n text: string;\n reasoning: string;\n toolActivity: DelegateToolActivity[];\n usage?: CursorUsage;\n}\n\n/**\n * Run a single delegated turn on a fresh (or explicitly resumed) local Cursor\n * agent and aggregate the outcome into a plain result. This backs the opt-in\n * `cursor_delegate` tool, which gives users a permission-gated boundary around\n * Cursor (the provider path runs Cursor's own loop without per-call gating).\n *\n * Reuses the provider's `acquireAgent` + `streamAgentTurn` plumbing; the turn\n * is consumed eagerly here because a tool returns a single result rather than a\n * live stream.\n */\nexport async function runDelegate(params: DelegateParams): Promise<DelegateResult> {\n const { mode, modelSelection } = resolveControls(\n params.model,\n {\n mode: params.mode ?? \"agent\",\n ...(params.thinking ? { params: { thinking: params.thinking } } : {}),\n },\n undefined,\n );\n\n const acquired = await acquireAgent({\n apiKey: params.apiKey,\n modelSelection,\n mode,\n cwd: params.cwd,\n ...(params.sandbox !== undefined ? { sandbox: params.sandbox } : {}),\n ...(params.agentId ? { agentId: params.agentId } : {}),\n session: false,\n });\n\n const text: string[] = [];\n const reasoning: string[] = [];\n const toolActivity: DelegateToolActivity[] = [];\n let usage: CursorUsage | undefined;\n\n try {\n for await (const event of streamAgentTurn(\n acquired.agent,\n { text: params.prompt },\n { mode, ...(params.abortSignal ? { abortSignal: params.abortSignal } : {}) },\n )) {\n switch (event.type) {\n case \"text-delta\":\n text.push(event.text);\n break;\n case \"reasoning-delta\":\n reasoning.push(event.text);\n break;\n case \"tool-call\":\n toolActivity.push({ name: event.name, isError: false });\n break;\n case \"tool-result\":\n if (event.isError) toolActivity.push({ name: event.name, isError: true });\n break;\n case \"usage\":\n usage = event.usage;\n break;\n case \"finish\":\n // The aggregated result text; prefer it when deltas were absent.\n if (event.text && text.length === 0) text.push(event.text);\n break;\n }\n }\n } finally {\n acquired.release();\n }\n\n return {\n agentId: acquired.agent.agentId,\n text: text.join(\"\"),\n reasoning: reasoning.join(\"\"),\n toolActivity,\n ...(usage ? { usage } : {}),\n };\n}\n","import type { Plugin } from \"@opencode-ai/plugin\";\nimport type { Auth } from \"@opencode-ai/sdk/v2\";\nimport { resolveCursorApiKey } from \"../api-key.js\";\nimport { discoverModels, toOpencodeModels } from \"../model-discovery.js\";\nimport { buildModelV2Map, PROVIDER_ID, providerNpm } from \"./model-v2.js\";\nimport { translateMcpServers } from \"./mcp-config.js\";\nimport { buildCursorTools } from \"./cursor-tools.js\";\n\nfunction apiKeyFromAuth(auth: Auth | undefined): string | undefined {\n return auth?.type === \"api\" ? auth.key : undefined;\n}\n\n/**\n * opencode plugin that adds a \"Cursor\" provider backed by the official Cursor\n * SDK (`@cursor/sdk`).\n *\n * - `auth`: registers an API-key login for Cursor and a `loader` that feeds the\n * key into the AI-SDK provider factory. The key is validated on first use\n * (model discovery / first call), not at login — see the note on `methods`.\n * - `config`: registers the provider (npm package + discovered/fallback models)\n * so it shows up in opencode immediately.\n * - `provider.models`: auth-aware live model discovery via `Cursor.models.list`.\n * - `tool.cursor_refresh_models`: force-refresh the model catalog.\n */\nexport const CursorPlugin: Plugin = async (input) => {\n // The Cursor API key resolved by opencode's auth loader, captured so the\n // delegation tools (which don't receive auth directly) can reuse it. Falls\n // back to the CURSOR_API_KEY env var when the loader hasn't run.\n let capturedApiKey: string | undefined;\n\n return {\n auth: {\n provider: PROVIDER_ID,\n loader: async (getAuth) => {\n const apiKey = resolveCursorApiKey(apiKeyFromAuth(await getAuth().catch(() => undefined)));\n if (apiKey) {\n capturedApiKey = apiKey;\n // The `config` hook (which seeds opencode's model picker) runs without\n // a key. Warm the catalog cache here — the loader is the hook that\n // reliably has the key — so the next launch seeds the full live\n // catalog instead of the static fallback. Fire-and-forget: discovery\n // never throws and must not block auth/provider load.\n void discoverModels({ apiKey });\n }\n return apiKey ? { apiKey } : {};\n },\n // A single API-key method. opencode always shows its built-in \"Enter your\n // API key\" prompt for `type: \"api\"`, so we intentionally do NOT declare\n // custom `prompts` (that asks for the key a second time) or an `authorize`\n // callback. opencode only passes `authorize` the *custom-prompt* inputs —\n // never the built-in key — so validating the key in `authorize` would\n // force that redundant extra prompt. Instead the key is validated on first\n // use (model discovery / the first call both surface a bad key clearly).\n methods: [{ type: \"api\", label: \"Cursor API Key\" }],\n },\n\n config: async (config) => {\n const { models } = await discoverModels({});\n config.provider ??= {};\n const existing = config.provider[PROVIDER_ID] ?? {};\n const existingOptions = (existing.options ?? {}) as Record<string, unknown>;\n\n // Forward opencode's configured MCP servers (e.g. Serena) to the Cursor\n // agent so it can use the same servers. Opt out via\n // `provider.cursor.options.forwardMcp: false`.\n const forwardMcp = existingOptions[\"forwardMcp\"] !== false;\n const userMcp = (existingOptions[\"mcpServers\"] ?? {}) as Record<string, unknown>;\n const mcpServers = forwardMcp\n ? { ...userMcp, ...translateMcpServers(config.mcp) }\n : userMcp;\n\n config.provider[PROVIDER_ID] = {\n name: \"Cursor\",\n npm: providerNpm(),\n ...existing,\n options: {\n ...existingOptions,\n ...(Object.keys(mcpServers).length > 0 ? { mcpServers } : {}),\n },\n models: { ...toOpencodeModels(models), ...(existing.models ?? {}) },\n };\n },\n\n provider: {\n id: PROVIDER_ID,\n models: async (_provider, ctx) => {\n const apiKey = apiKeyFromAuth(ctx.auth);\n const { models } = await discoverModels({ apiKey });\n return buildModelV2Map(models);\n },\n },\n\n // Bridge opencode's session id to the provider: it lands in\n // providerOptions.cursor.sessionID, which the provider reads to pool/resume a\n // Cursor agent per session (when the `session` option is enabled).\n //\n // Also map opencode's plan AGENT to Cursor's plan mode. This hook fires\n // after opencode merges the selected variant into `output.options`, so an\n // explicit mode from the `plan` variant (or model options) wins — the\n // agent-based default only applies when no mode was set.\n \"chat.params\": async (input, output) => {\n if (input.model?.providerID !== PROVIDER_ID) return;\n output.options = { ...(output.options ?? {}), sessionID: input.sessionID };\n if (input.agent === \"plan\" && output.options[\"mode\"] === undefined) {\n output.options[\"mode\"] = \"plan\";\n }\n },\n\n tool: {\n cursor_refresh_models: {\n description:\n \"Refresh the live Cursor model catalog (bypasses the 24h cache) and report the available models.\",\n args: {},\n execute: async () => {\n const result = await discoverModels({ forceRefresh: true });\n const lines = result.models.map((m) => `- ${m.id} — ${m.displayName}`);\n const header =\n result.source === \"live\"\n ? `Refreshed ${result.models.length} Cursor models (live):`\n : `Could not fetch live models (${result.source}). ${result.warning ?? \"\"}`.trim();\n return {\n title: `Cursor models (${result.source})`,\n output: [header, ...lines].join(\"\\n\"),\n metadata: { source: result.source, count: result.models.length },\n };\n },\n },\n // Delegation tools that complement the provider: a cloud/background agent\n // and a permission-gated local delegate. They resolve the Cursor key from\n // the auth loader (captured above) or CURSOR_API_KEY.\n ...buildCursorTools({\n resolveApiKey: () => resolveCursorApiKey(capturedApiKey),\n defaultCwd: () => input?.directory ?? process.cwd(),\n }),\n },\n };\n};\n\nexport default CursorPlugin;\n"],"mappings":"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","names":["input"]}
1
+ {"version":3,"sources":["../../src/model-cache.ts","../../src/fallback-models.ts","../../src/model-variants.ts","../../src/model-discovery.ts","../../src/plugin/model-v2.ts","../../src/plugin/mcp-config.ts","../../src/plugin/cursor-tools.ts","../../src/provider/cloud-agent.ts","../../src/provider/delegate.ts","../../src/plugin/index.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { ModelListItem } from \"@cursor/sdk\";\n\n/** Default cache lifetime: 24 hours, overridable via env. */\nconst DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;\n\nfunction ttlMs(): number {\n const raw = process.env.OPENCODE_CURSOR_MODEL_CACHE_TTL_MS;\n const parsed = raw ? Number.parseInt(raw, 10) : NaN;\n return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TTL_MS;\n}\n\nfunction cacheDir(): string {\n const base =\n process.env.XDG_CACHE_HOME?.trim() ||\n (homedir() ? join(homedir(), \".cache\") : tmpdir());\n return join(base, \"opencode-cursor\");\n}\n\nfunction cacheFile(fingerprint: string): string {\n return join(cacheDir(), `models-${fingerprint}.json`);\n}\n\n/**\n * Key-independent \"latest known catalog\" file. The `config` plugin hook runs\n * without access to the stored API key, so it can't read the per-key cache.\n * This file lets a keyless caller (the config hook) seed opencode's model\n * picker with the real catalog that a previous *authed* load discovered.\n */\nfunction latestCacheFile(): string {\n return join(cacheDir(), \"models-latest.json\");\n}\n\n/** The latest-catalog seed is kept longer than the per-key cache: the catalog\n * is stable and this only feeds pre-auth UI seeding. */\nconst LATEST_TTL_MS = 30 * 24 * 60 * 60 * 1000;\n\ninterface CacheEnvelope {\n savedAt: number;\n models: ModelListItem[];\n}\n\nfunction readCacheFile(file: string, maxAgeMs: number): ModelListItem[] | undefined {\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\")) as CacheEnvelope;\n if (!parsed?.savedAt || !Array.isArray(parsed.models)) return undefined;\n if (Date.now() - parsed.savedAt > maxAgeMs) return undefined;\n return parsed.models;\n } catch {\n return undefined;\n }\n}\n\nfunction writeCacheFile(file: string, models: ModelListItem[]): void {\n try {\n mkdirSync(cacheDir(), { recursive: true });\n const envelope: CacheEnvelope = { savedAt: Date.now(), models };\n writeFileSync(file, JSON.stringify(envelope), \"utf8\");\n } catch {\n // Caching is an optimization; ignore write failures.\n }\n}\n\n/**\n * Return cached models for the given API-key fingerprint when present and still\n * fresh, otherwise `undefined`. Never throws on a missing/corrupt cache.\n */\nexport function readModelCache(fingerprint: string): ModelListItem[] | undefined {\n return readCacheFile(cacheFile(fingerprint), ttlMs());\n}\n\n/** Persist the discovered model list (per-key cache + key-independent latest\n * catalog). Best-effort; never throws. */\nexport function writeModelCache(fingerprint: string, models: ModelListItem[]): void {\n writeCacheFile(cacheFile(fingerprint), models);\n writeCacheFile(latestCacheFile(), models);\n}\n\n/**\n * Return the most recently discovered catalog regardless of API key, when\n * present and within {@link LATEST_TTL_MS}. Used by the keyless `config` hook to\n * seed the picker with the real catalog after a prior authed load.\n */\nexport function readLatestModelCache(): ModelListItem[] | undefined {\n return readCacheFile(latestCacheFile(), LATEST_TTL_MS);\n}\n","import type { ModelListItem } from \"@cursor/sdk\";\n\n/**\n * A small static snapshot of well-known Cursor models, used only when live\n * discovery is unavailable (no API key, offline, or an SDK error). The live\n * `Cursor.models.list()` result always takes precedence; this just lets the\n * provider appear in opencode with sensible defaults so the user can reach the\n * login flow. Refresh the real catalog with the `cursor_refresh_models` tool.\n */\nexport const FALLBACK_MODELS: ModelListItem[] = [\n {\n id: \"composer-2.5\",\n displayName: \"Composer 2.5\",\n description: \"Cursor's default agent model (fallback entry).\",\n parameters: [\n { id: \"thinking\", displayName: \"Thinking\", values: [{ value: \"off\" }, { value: \"on\" }] },\n ],\n },\n { id: \"claude-opus-4-8\", displayName: \"Claude Opus 4.8 (via Cursor)\" },\n { id: \"claude-sonnet-4-6\", displayName: \"Claude Sonnet 4.6 (via Cursor)\" },\n { id: \"gpt-5.5\", displayName: \"GPT-5.5 (via Cursor)\" },\n];\n","import type { ModelListItem } from \"@cursor/sdk\";\n\n/**\n * A Cursor model \"variant\" as opencode stores it: an options object that, when\n * the variant is selected, is merged into `providerOptions.cursor` and read back\n * by {@link resolveControls}.\n */\nexport interface CursorVariant {\n params?: Record<string, string>;\n mode?: \"agent\" | \"plan\";\n}\n\nconst REASONING_PARAM = /think|reason|effort/i;\nconst BOOLEAN_VALUES = new Set([\"true\", \"false\"]);\n\n/**\n * Derive opencode model variants from a Cursor model's parameters so the\n * variant picker can expose thinking/reasoning levels. Each variant's object is\n * exactly what {@link resolveControls} consumes. Plan mode is NOT a variant:\n * opencode's plan agent (Tab) is mapped to Cursor's plan mode by the plugin's\n * `chat.params` hook.\n */\nexport function buildModelVariants(item: ModelListItem): Record<string, CursorVariant> {\n const out: Record<string, CursorVariant> = {};\n\n for (const param of item.parameters ?? []) {\n if (!REASONING_PARAM.test(param.id)) continue;\n const values = (param.values ?? []).map((v) => v.value);\n if (values.length === 0) continue;\n\n if (values.every((v) => BOOLEAN_VALUES.has(v))) {\n // Boolean toggle (e.g. thinking=[\"false\",\"true\"]). Literal true/false\n // variant names are meaningless in the picker — surface a single\n // variant named after the param that switches it on. \"Off\" is the\n // model's default (no variant selected).\n if (values.includes(\"true\")) {\n out[param.id.toLowerCase()] = { params: { [param.id]: \"true\" } };\n }\n continue;\n }\n\n for (const value of values) {\n // Key by the bare value (e.g. \"high\"); prefix with the param id only\n // when two params share a value (e.g. reasoning-low vs effort-low).\n const key = out[value] === undefined ? value : `${param.id}-${value}`;\n out[key] = { params: { [param.id]: value } };\n }\n }\n\n return out;\n}\n","import type { ModelListItem } from \"@cursor/sdk\";\nimport { fingerprintApiKey, resolveCursorApiKey } from \"./api-key.js\";\nimport { readLatestModelCache, readModelCache, writeModelCache } from \"./model-cache.js\";\nimport { FALLBACK_MODELS } from \"./fallback-models.js\";\nimport { loadCursorSdk } from \"./cursor-runtime.js\";\nimport { buildModelVariants, type CursorVariant } from \"./model-variants.js\";\n\nexport type ModelSource = \"live\" | \"cache\" | \"fallback\";\n\nexport interface DiscoveryResult {\n models: ModelListItem[];\n source: ModelSource;\n /** Human-readable note when discovery degraded (e.g. missing key, error). */\n warning?: string;\n}\n\nexport interface DiscoverOptions {\n /** Explicit key; falls back to CURSOR_API_KEY. */\n apiKey?: string;\n /** Bypass the on-disk cache and force a live `Cursor.models.list()`. */\n forceRefresh?: boolean;\n}\n\n/**\n * Discover the Cursor model catalog. Tries (in order): on-disk cache (unless\n * forced), live `Cursor.models.list()`, then the static fallback snapshot.\n * Always resolves — failures degrade to the fallback with a `warning`.\n */\nexport async function discoverModels(options: DiscoverOptions = {}): Promise<DiscoveryResult> {\n const apiKey = resolveCursorApiKey(options.apiKey);\n if (!apiKey) {\n // No key here (e.g. the keyless `config` hook). Prefer the real catalog a\n // prior authed load cached, so opencode's picker shows the full list rather\n // than only the static snapshot.\n const latest = readLatestModelCache();\n if (latest && latest.length > 0) return { models: latest, source: \"cache\" };\n return {\n models: FALLBACK_MODELS,\n source: \"fallback\",\n warning:\n \"No Cursor API key found. Run `opencode auth login` and choose Cursor, or set CURSOR_API_KEY. Showing fallback models.\",\n };\n }\n\n const fingerprint = fingerprintApiKey(apiKey);\n\n if (!options.forceRefresh) {\n const cached = readModelCache(fingerprint);\n if (cached && cached.length > 0) {\n return { models: cached, source: \"cache\" };\n }\n }\n\n try {\n const { Cursor } = await loadCursorSdk();\n const models = await Cursor.models.list({ apiKey });\n if (models.length > 0) {\n writeModelCache(fingerprint, models);\n return { models, source: \"live\" };\n }\n return {\n models: FALLBACK_MODELS,\n source: \"fallback\",\n warning: \"Cursor.models.list() returned no models; showing fallback models.\",\n };\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n // A stale cache is better than nothing on a transient failure.\n const stale = readModelCache(fingerprint);\n if (stale && stale.length > 0) {\n return { models: stale, source: \"cache\", warning: `Live discovery failed (${detail}); using cached models.` };\n }\n return {\n models: FALLBACK_MODELS,\n source: \"fallback\",\n warning: `Live discovery failed (${detail}); showing fallback models.`,\n };\n }\n}\n\n/** True when a model exposes a thinking/reasoning parameter. */\nexport function modelSupportsReasoning(item: ModelListItem): boolean {\n return (item.parameters ?? []).some((p) => /think|reason/i.test(p.id));\n}\n\n/** Shape of a single entry in opencode's `provider.<id>.models` config map. */\nexport interface OpencodeModelConfigEntry {\n id: string;\n name: string;\n attachment: boolean;\n reasoning: boolean;\n temperature: boolean;\n tool_call: boolean;\n /**\n * opencode model variants (thinking levels + plan mode). They MUST be seeded\n * here: opencode discards the plugin `provider.models()` hook for providers\n * absent from its models.dev catalog, so this config map is the only channel\n * through which cursor model variants reach the picker.\n */\n variants: Record<string, CursorVariant>;\n}\n\n/**\n * Map discovered Cursor models to opencode's provider config `models` map. The\n * Cursor SDK runs an agent (it calls tools itself), so every model is marked\n * `tool_call: true` and `temperature: false`.\n */\nexport function toOpencodeModels(items: ModelListItem[]): Record<string, OpencodeModelConfigEntry> {\n const out: Record<string, OpencodeModelConfigEntry> = {};\n for (const item of items) {\n out[item.id] = {\n id: item.id,\n name: item.displayName || item.id,\n attachment: true,\n reasoning: modelSupportsReasoning(item),\n temperature: false,\n tool_call: true,\n variants: buildModelVariants(item),\n };\n }\n return out;\n}\n","import type { Model as ModelV2 } from \"@opencode-ai/sdk/v2\";\nimport type { ModelListItem } from \"@cursor/sdk\";\nimport { modelSupportsReasoning } from \"../model-discovery.js\";\nimport { buildModelVariants } from \"../model-variants.js\";\n\nexport const PROVIDER_ID = \"cursor\";\nexport const NPM_PACKAGE = \"@stablekernel/opencode-cursor\";\n\n/**\n * The npm specifier opencode uses to load the provider SDK. Defaults to the\n * published package name; can be overridden with a `file://...` URL (which\n * opencode imports directly, skipping a registry install) via\n * `OPENCODE_CURSOR_PROVIDER_NPM` — useful for local development and CI before\n * the package is published.\n */\nexport function providerNpm(): string {\n return process.env.OPENCODE_CURSOR_PROVIDER_NPM?.trim() || NPM_PACKAGE;\n}\n\n/**\n * Build opencode's rich runtime `Model` objects from discovered Cursor models.\n * Used by the auth-aware `provider.models()` hook. Fields opencode does not get\n * from the Cursor catalog are filled with safe defaults (zero cost — Cursor\n * bills separately; generous context limits).\n */\nexport function buildModelV2Map(items: ModelListItem[]): Record<string, ModelV2> {\n const out: Record<string, ModelV2> = {};\n for (const item of items) {\n out[item.id] = {\n id: item.id,\n providerID: PROVIDER_ID,\n api: { id: item.id, url: \"\", npm: providerNpm() },\n name: item.displayName || item.id,\n capabilities: {\n temperature: false,\n reasoning: modelSupportsReasoning(item),\n attachment: true,\n toolcall: true,\n input: { text: true, audio: false, image: true, video: false, pdf: false },\n output: { text: true, audio: false, image: false, video: false, pdf: false },\n interleaved: false,\n },\n cost: { input: 0, output: 0, cache: { read: 0, write: 0 } },\n limit: { context: 200_000, output: 32_000 },\n status: \"active\",\n options: {},\n headers: {},\n release_date: \"\",\n variants: buildModelVariants(item) as ModelV2[\"variants\"],\n };\n }\n return out;\n}\n","import type { Config } from \"@opencode-ai/plugin\";\nimport type { McpServerConfig } from \"@cursor/sdk\";\n\n/** The value type of opencode's `config.mcp` map. */\ntype OpencodeMcp = NonNullable<Config[\"mcp\"]>;\ntype OpencodeMcpEntry = OpencodeMcp[string];\n\n/**\n * Live MCP server status, keyed by server name, as reported by opencode's\n * `client.mcp.status()`. Only the `status` field is consumed; `\"connected\"`\n * means the server is currently usable. Mirrors the SDK's `McpStatus` union\n * without importing it (keeps this module dependency-light).\n */\nexport type McpStatusMap = Record<string, { status?: string } | undefined>;\n\n/** opencode runtime statuses that mean a server still needs OAuth to connect. */\nconst NEEDS_AUTH_STATUS = new Set([\"needs_auth\", \"needs_client_registration\"]);\n\n/** The OAuth client registration on a remote entry, or undefined when none. */\nfunction oauthConfig(\n\tentry: OpencodeMcpEntry,\n): { clientId?: string; clientSecret?: string; scope?: string } | undefined {\n\tif (entry.type !== \"remote\") return undefined;\n\t// `oauth` is `McpOAuthConfig | false | undefined`; both false and undefined\n\t// are falsy, so a truthy value is the client-registration object.\n\treturn entry.oauth ? entry.oauth : undefined;\n}\n\n/**\n * Map opencode's OAuth client registration to the Cursor SDK's `auth` block so\n * the Cursor agent can run its own OAuth flow. Returns undefined when there is\n * no `clientId` to share (e.g. RFC 7591 dynamic registration) — opencode's\n * access token itself never reaches `config.mcp`, so a bare URL would fail.\n */\nfunction toCursorAuth(\n\toauth:\n\t\t| { clientId?: string; clientSecret?: string; scope?: string }\n\t\t| undefined,\n):\n\t| { CLIENT_ID: string; CLIENT_SECRET?: string; scopes?: string[] }\n\t| undefined {\n\tif (!oauth?.clientId) return undefined;\n\tconst scopes = oauth.scope?.split(/\\s+/).filter(Boolean);\n\treturn {\n\t\tCLIENT_ID: oauth.clientId,\n\t\t...(oauth.clientSecret ? { CLIENT_SECRET: oauth.clientSecret } : {}),\n\t\t...(scopes && scopes.length > 0 ? { scopes } : {}),\n\t};\n}\n\n/**\n * Names of remote servers that require OAuth but cannot be forwarded to the\n * Cursor agent because no shareable client registration exists (dynamic\n * registration, or a `needs_auth` runtime status with no configured\n * `clientId`). The plugin surfaces these to the user instead of silently\n * forwarding a spec that would 401.\n */\nexport function findUnshareableOAuthServers(\n\tmcp: Config[\"mcp\"],\n\tstatus?: McpStatusMap,\n): string[] {\n\tconst names: string[] = [];\n\tif (!mcp) return names;\n\tfor (const [name, entry] of Object.entries(mcp) as Array<\n\t\t[string, OpencodeMcpEntry]\n\t>) {\n\t\tif (!entry || entry.type !== \"remote\") continue;\n\t\tif (!status && entry.enabled === false) continue;\n\t\tconst s = status?.[name]?.status;\n\t\tif (status && s !== \"connected\" && !NEEDS_AUTH_STATUS.has(s ?? \"\"))\n\t\t\tcontinue;\n\t\tconst oauth = oauthConfig(entry);\n\t\tconst needsOAuth = Boolean(oauth) || NEEDS_AUTH_STATUS.has(s ?? \"\");\n\t\tif (needsOAuth && !toCursorAuth(oauth)) names.push(name);\n\t}\n\treturn names;\n}\n\n/**\n * Translate opencode's configured MCP servers (`config.mcp`) into the Cursor\n * SDK's `McpServerConfig` shape so the same servers can be handed\n * to the Cursor agent via `Agent.create({ mcpServers })`.\n *\n * MCP servers are independent processes addressed by a launch spec, so opencode\n * and the Cursor agent can each connect to the same server. Disabled entries\n * (`enabled: false`) are skipped. The `timeout` field is dropped (no Cursor\n * equivalent). OAuth is mapped where possible: a remote server's `oauth` client\n * registration becomes Cursor's `auth` block so the agent runs its own OAuth\n * flow; servers needing OAuth with no shareable `clientId` are skipped (the\n * plugin reports them via {@link findUnshareableOAuthServers}).\n */\nexport function translateMcpServers(\n\tmcp: Config[\"mcp\"],\n\tstatus?: McpStatusMap,\n): Record<string, McpServerConfig> {\n\tconst out: Record<string, McpServerConfig> = {};\n\tif (!mcp) return out;\n\n\tfor (const [name, entry] of Object.entries(mcp) as Array<\n\t\t[string, OpencodeMcpEntry]\n\t>) {\n\t\tif (!entry) continue;\n\n\t\t// When a live status map is supplied (per-turn dynamic forwarding), it is\n\t\t// the source of truth: forward only servers opencode has currently\n\t\t// connected, so mid-session enable/disable propagates to the Cursor agent.\n\t\t// Without it (the startup config snapshot), fall back to the static\n\t\t// `enabled` flag.\n\t\tif (status) {\n\t\t\tif (status[name]?.status !== \"connected\") continue;\n\t\t} else if (entry.enabled === false) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (entry.type === \"local\") {\n\t\t\tconst [command, ...args] = entry.command ?? [];\n\t\t\tif (!command) continue;\n\t\t\tout[name] = {\n\t\t\t\ttype: \"stdio\",\n\t\t\t\tcommand,\n\t\t\t\t...(args.length > 0 ? { args } : {}),\n\t\t\t\t...(entry.environment && Object.keys(entry.environment).length > 0\n\t\t\t\t\t? { env: entry.environment }\n\t\t\t\t\t: {}),\n\t\t\t};\n\t\t} else if (entry.type === \"remote\") {\n\t\t\tif (!entry.url) continue;\n\t\t\tconst oauth = oauthConfig(entry);\n\t\t\tconst auth = toCursorAuth(oauth);\n\t\t\t// OAuth server with no shareable client registration: opencode holds the\n\t\t\t// token and it never lands in config.mcp, so skip rather than forward a\n\t\t\t// bare URL that would 401. The plugin notifies the user (see\n\t\t\t// findUnshareableOAuthServers).\n\t\t\tif (oauth && !auth) continue;\n\t\t\tout[name] = {\n\t\t\t\ttype: \"http\",\n\t\t\t\turl: entry.url,\n\t\t\t\t...(entry.headers && Object.keys(entry.headers).length > 0\n\t\t\t\t\t? { headers: entry.headers }\n\t\t\t\t\t: {}),\n\t\t\t\t...(auth ? { auth } : {}),\n\t\t\t};\n\t\t}\n\t}\n\n\treturn out;\n}\n","import { tool, type ToolContext, type ToolDefinition } from \"@opencode-ai/plugin\";\nimport { runCloudAgent } from \"../provider/cloud-agent.js\";\nimport { runDelegate } from \"../provider/delegate.js\";\n\nconst s = tool.schema;\n\nexport interface CursorToolDeps {\n /**\n * Resolve the Cursor API key (from opencode auth, captured by the plugin's\n * auth loader, or the CURSOR_API_KEY env var). Returns undefined when no key\n * is available so the tool can return a clear \"needs auth\" message.\n */\n resolveApiKey: () => string | undefined;\n /** Default working directory for local delegation (the session worktree/cwd). */\n defaultCwd: () => string;\n}\n\nconst NEEDS_AUTH =\n \"No Cursor API key available. Run `opencode auth login` and choose Cursor, or set CURSOR_API_KEY.\";\n\n/**\n * Request approval for a sensitive Cursor invocation. `context.ask` is the\n * opencode mechanism a custom tool uses to gate itself; it honors the user's\n * `permission` config (allow resolves silently, ask prompts, deny rejects).\n *\n * Returns `{ ok: true }` when approved, or `{ ok: false, reason }` when the\n * request was rejected. We deliberately do not claim the rejection was a policy\n * \"deny\" — `context.ask` rejects on both an explicit deny and an internal\n * failure, and conflating them produces misleading messages. The gate is\n * fail-closed: any rejection (including a host that doesn't provide `ask`)\n * blocks the call rather than silently allowing it.\n */\nasync function requestApproval(\n context: ToolContext,\n permission: string,\n patterns: string[],\n metadata: Record<string, unknown>,\n): Promise<{ ok: boolean; reason?: string }> {\n try {\n await context.ask({ permission, patterns, always: patterns, metadata });\n return { ok: true };\n } catch (err) {\n return { ok: false, reason: err instanceof Error ? err.message : String(err) };\n }\n}\n\nfunction errorMessage(err: unknown): string {\n return err instanceof Error ? err.message : String(err);\n}\n\n/**\n * Build the Cursor delegation tools that complement the native provider:\n * - `cursor_cloud_agent`: run a background agent on a remote repo (optionally\n * opening a PR) — work that maps poorly onto the synchronous provider path.\n * - `cursor_delegate`: run a single local Cursor turn as a permission-gated,\n * auditable tool call (for users who want Cursor as a delegate rather than\n * as their primary model).\n *\n * Both are gated via `context.ask`, so a user `permission` policy controls them.\n */\nexport function buildCursorTools(deps: CursorToolDeps): Record<string, ToolDefinition> {\n return {\n cursor_cloud_agent: tool({\n description:\n \"Launch a Cursor background ('cloud') agent on a remote repository. Runs autonomously \" +\n \"(may take minutes) and can open a pull request. Returns the cloud agent id, final \" +\n \"status, result, and PR url when available.\",\n args: {\n prompt: s.string().describe(\"The task/instruction for the background agent.\"),\n repoUrl: s\n .string()\n .describe(\"Target repository URL, e.g. https://github.com/owner/repo.\"),\n startingRef: s\n .string()\n .optional()\n .describe(\"Branch or ref to start from (defaults to the repo default branch).\"),\n model: s.string().optional().describe(\"Cursor model id (optional for cloud).\"),\n mode: s.enum([\"agent\", \"plan\"]).optional().describe(\"Conversation mode.\"),\n thinking: s.string().optional().describe(\"Thinking level, e.g. 'high'.\"),\n autoCreatePR: s\n .boolean()\n .optional()\n .describe(\"Open a pull request automatically when finished.\"),\n workOnCurrentBranch: s\n .boolean()\n .optional()\n .describe(\"Operate on the current branch instead of creating a new one.\"),\n },\n execute: async (args, context) => {\n const apiKey = deps.resolveApiKey();\n if (!apiKey) return NEEDS_AUTH;\n\n const approval = await requestApproval(\n context,\n \"cursor_cloud_agent\",\n [args.repoUrl],\n { repoUrl: args.repoUrl, autoCreatePR: args.autoCreatePR ?? false },\n );\n if (!approval.ok) {\n return `Cloud agent not approved for ${args.repoUrl}${approval.reason ? `: ${approval.reason}` : \".\"}`;\n }\n\n let result;\n try {\n result = await runCloudAgent({\n apiKey,\n prompt: args.prompt,\n repoUrl: args.repoUrl,\n ...(args.startingRef ? { startingRef: args.startingRef } : {}),\n ...(args.model ? { model: args.model } : {}),\n ...(args.mode ? { mode: args.mode } : {}),\n ...(args.thinking ? { thinking: args.thinking } : {}),\n ...(args.autoCreatePR !== undefined ? { autoCreatePR: args.autoCreatePR } : {}),\n ...(args.workOnCurrentBranch !== undefined\n ? { workOnCurrentBranch: args.workOnCurrentBranch }\n : {}),\n abortSignal: context.abort,\n });\n } catch (err) {\n return `Cloud agent failed: ${errorMessage(err)}`;\n }\n\n const lines = [\n `Cloud agent ${result.agentId} — ${result.status}`,\n ...(result.prUrl ? [`PR: ${result.prUrl}`] : []),\n ...(result.branches.length > 0\n ? [`Branches: ${result.branches.map((b) => b.branch ?? b.repoUrl).join(\", \")}`]\n : []),\n ...(result.result ? [\"\", result.result] : []),\n ...(result.progress.length > 0 ? [\"\", \"Progress:\", ...result.progress] : []),\n ];\n\n return {\n title: `Cursor cloud agent (${result.status})`,\n output: lines.join(\"\\n\"),\n metadata: {\n agentId: result.agentId,\n status: result.status,\n prUrl: result.prUrl ?? null,\n durationMs: result.durationMs ?? null,\n },\n };\n },\n }),\n\n cursor_delegate: tool({\n description:\n \"Delegate a single subtask to a local Cursor agent and return its result. Use to hand \" +\n \"off discrete work to Cursor while keeping your primary model in control. Permission-gated.\",\n args: {\n prompt: s.string().describe(\"The subtask to delegate to Cursor.\"),\n model: s.string().describe(\"Cursor model id to run the delegation on.\"),\n mode: s.enum([\"agent\", \"plan\"]).optional().describe(\"Conversation mode.\"),\n thinking: s.string().optional().describe(\"Thinking level, e.g. 'high'.\"),\n cwd: s\n .string()\n .optional()\n .describe(\"Working directory (defaults to the session directory).\"),\n sandbox: s.boolean().optional().describe(\"Run the agent's tools in Cursor's sandbox.\"),\n agentId: s\n .string()\n .optional()\n .describe(\"Resume a specific Cursor agent id instead of starting fresh.\"),\n },\n execute: async (args, context) => {\n const apiKey = deps.resolveApiKey();\n if (!apiKey) return NEEDS_AUTH;\n\n const approval = await requestApproval(context, \"cursor_delegate\", [args.model], {\n model: args.model,\n prompt: args.prompt,\n });\n if (!approval.ok) {\n return `Delegation to ${args.model} not approved${approval.reason ? `: ${approval.reason}` : \".\"}`;\n }\n\n let result;\n try {\n result = await runDelegate({\n apiKey,\n prompt: args.prompt,\n model: args.model,\n cwd: args.cwd ?? context.directory ?? deps.defaultCwd(),\n ...(args.mode ? { mode: args.mode } : {}),\n ...(args.thinking ? { thinking: args.thinking } : {}),\n ...(args.sandbox !== undefined ? { sandbox: args.sandbox } : {}),\n ...(args.agentId ? { agentId: args.agentId } : {}),\n abortSignal: context.abort,\n });\n } catch (err) {\n return `Delegation failed: ${errorMessage(err)}`;\n }\n\n const toolNote =\n result.toolActivity.length > 0\n ? `\\n\\n(${result.toolActivity.length} tool call(s)` +\n `${result.toolActivity.some((t) => t.isError) ? \", some failed\" : \"\"})`\n : \"\";\n\n return {\n title: `Cursor delegate (${args.model})`,\n output: (result.text || \"(no text output)\") + toolNote,\n metadata: {\n agentId: result.agentId,\n model: args.model,\n toolCalls: result.toolActivity.length,\n usage: result.usage ?? null,\n },\n };\n },\n }),\n };\n}\n","import type { AgentModeOption, ConversationStep, InteractionUpdate } from \"@cursor/sdk\";\nimport { loadCursorSdk } from \"../cursor-runtime.js\";\nimport { buildModelSelection } from \"./controls.js\";\n\n/**\n * A target repository for a cloud agent. Cursor's cloud runtime accepts an\n * array of repos; the tool surface exposes the common single-repo case.\n */\nexport interface CloudRepoTarget {\n url: string;\n startingRef?: string;\n}\n\nexport interface CloudAgentParams {\n apiKey: string;\n /** The instruction/task for the background agent. */\n prompt: string;\n /** Target repository URL (e.g. https://github.com/owner/repo). */\n repoUrl: string;\n /** Branch/ref to start from. Defaults to the repo's default branch. */\n startingRef?: string;\n /** Cursor model id. Optional for cloud (server picks a default otherwise). */\n model?: string;\n /** Conversation mode; defaults to \"agent\". */\n mode?: AgentModeOption;\n /** Convenience for the Cursor `thinking` model param (e.g. \"high\"). */\n thinking?: string;\n /** When true, open a PR automatically once the agent finishes. */\n autoCreatePR?: boolean;\n /** Operate on the current branch instead of creating a new one. */\n workOnCurrentBranch?: boolean;\n /** Cancels the run when aborted (wired to the tool's abort signal). */\n abortSignal?: AbortSignal;\n}\n\nexport interface CloudAgentBranch {\n repoUrl: string;\n branch?: string;\n prUrl?: string;\n}\n\nexport interface CloudAgentResult {\n agentId: string;\n /** Terminal run status: \"finished\" | \"error\" | \"cancelled\". */\n status: string;\n /** The agent's final textual result, when present. */\n result?: string;\n /** First PR url found across result branches (when `autoCreatePR`). */\n prUrl?: string;\n /** Per-repo branch/PR info reported by the run. */\n branches: CloudAgentBranch[];\n durationMs?: number;\n /** Human-readable progress lines captured from status/step/summary updates. */\n progress: string[];\n}\n\n/**\n * Run a Cursor background (\"cloud\") agent against a remote repository and wait\n * for it to finish, returning the final status, result text, and any PR url.\n *\n * A cloud agent can run for minutes and produce a PR rather than a chat reply,\n * which maps poorly onto the synchronous provider `doStream` path — so this is\n * exposed as an opencode tool instead (see plugin/index.ts). Progress is\n * collected into `progress[]` (opencode custom tools return a single result\n * rather than a live stream) and the lifecycle is bridged through the same\n * `loadCursorSdk` plumbing the provider uses.\n */\nexport async function runCloudAgent(params: CloudAgentParams): Promise<CloudAgentResult> {\n const { Agent } = await loadCursorSdk();\n const modelSelection = params.model\n ? buildModelSelection(params.model, params.thinking ? { thinking: params.thinking } : undefined)\n : undefined;\n const mode: AgentModeOption = params.mode ?? \"agent\";\n\n const createOptions = {\n apiKey: params.apiKey,\n ...(modelSelection ? { model: modelSelection } : {}),\n mode,\n cloud: {\n repos: [\n {\n url: params.repoUrl,\n ...(params.startingRef ? { startingRef: params.startingRef } : {}),\n },\n ],\n ...(params.autoCreatePR !== undefined ? { autoCreatePR: params.autoCreatePR } : {}),\n ...(params.workOnCurrentBranch !== undefined\n ? { workOnCurrentBranch: params.workOnCurrentBranch }\n : {}),\n },\n };\n\n const progress: string[] = [];\n const agent = await Agent.create(createOptions);\n\n // `onDelta` carries fine-grained updates; for a cloud (background) run the\n // higher-signal progress arrives via `onStep` (whole conversation steps) and\n // `run.onDidChangeStatus`. We capture all three — whichever the runtime emits.\n const onDelta = ({ update }: { update: InteractionUpdate }) => {\n if (update.type === \"summary\") progress.push(`summary: ${update.summary}`);\n };\n\n const onStep = ({ step }: { step: ConversationStep }) => {\n progress.push(`step: ${describeStep(step)}`);\n };\n\n try {\n const run = await agent.send(params.prompt, { mode, onDelta, onStep });\n\n const off = run.onDidChangeStatus?.((status: string) => {\n progress.push(`status: ${status}`);\n });\n const onAbort = () => {\n run.cancel().catch(() => {});\n };\n params.abortSignal?.addEventListener(\"abort\", onAbort);\n\n try {\n const result = await run.wait();\n const branches: CloudAgentBranch[] = (result.git?.branches ?? []).map((b) => ({\n repoUrl: b.repoUrl,\n ...(b.branch ? { branch: b.branch } : {}),\n ...(b.prUrl ? { prUrl: b.prUrl } : {}),\n }));\n const prUrl = branches.find((b) => b.prUrl)?.prUrl;\n return {\n agentId: agent.agentId,\n status: result.status,\n ...(result.result !== undefined ? { result: result.result } : {}),\n ...(prUrl ? { prUrl } : {}),\n branches,\n ...(result.durationMs !== undefined ? { durationMs: result.durationMs } : {}),\n progress,\n };\n } finally {\n off?.();\n params.abortSignal?.removeEventListener(\"abort\", onAbort);\n }\n } finally {\n try {\n agent.close();\n } catch {\n // best effort; cloud agents persist server-side regardless.\n }\n }\n}\n\n/** A short, log-friendly description of a conversation step for progress output. */\nfunction describeStep(step: ConversationStep): string {\n if (step.type === \"toolCall\") return `toolCall:${step.message.type}`;\n return step.type;\n}\n","import type { AgentModeOption } from \"@cursor/sdk\";\nimport type { CursorUsage } from \"./agent-events.js\";\nimport { streamAgentTurn } from \"./agent-events.js\";\nimport { resolveControls } from \"./controls.js\";\nimport { acquireAgent } from \"./session-pool.js\";\n\nexport interface DelegateParams {\n\tapiKey: string;\n\t/** The subtask to delegate to the Cursor agent. */\n\tprompt: string;\n\t/** Cursor model id to run the delegation on. */\n\tmodel: string;\n\t/** Conversation mode; defaults to \"agent\". */\n\tmode?: AgentModeOption;\n\t/** Convenience for the Cursor `thinking` model param (e.g. \"high\"). */\n\tthinking?: string;\n\t/** Working directory the local agent operates in. */\n\tcwd: string;\n\t/** Run the agent's tools inside Cursor's sandbox. */\n\tsandbox?: boolean;\n\t/** Resume a specific Cursor agent by id instead of creating a fresh one. */\n\tagentId?: string;\n\t/** Cancels the run when aborted (wired to the tool's abort signal). */\n\tabortSignal?: AbortSignal;\n}\n\nexport interface DelegateToolActivity {\n\tname: string;\n\tisError: boolean;\n}\n\nexport interface DelegateResult {\n\tagentId: string;\n\ttext: string;\n\treasoning: string;\n\ttoolActivity: DelegateToolActivity[];\n\tusage?: CursorUsage;\n}\n\n/**\n * Run a single delegated turn on a fresh (or explicitly resumed) local Cursor\n * agent and aggregate the outcome into a plain result. This backs the opt-in\n * `cursor_delegate` tool, which gives users a permission-gated boundary around\n * Cursor (the provider path runs Cursor's own loop without per-call gating).\n *\n * Reuses the provider's `acquireAgent` + `streamAgentTurn` plumbing; the turn\n * is consumed eagerly here because a tool returns a single result rather than a\n * live stream.\n */\nexport async function runDelegate(\n\tparams: DelegateParams,\n): Promise<DelegateResult> {\n\tconst { mode, modelSelection } = resolveControls(\n\t\tparams.model,\n\t\t{\n\t\t\tmode: params.mode ?? \"agent\",\n\t\t\t...(params.thinking ? { params: { thinking: params.thinking } } : {}),\n\t\t},\n\t\tundefined,\n\t);\n\n\tconst acquired = await acquireAgent({\n\t\tapiKey: params.apiKey,\n\t\tmodelSelection,\n\t\tmode,\n\t\tcwd: params.cwd,\n\t\t...(params.sandbox !== undefined ? { sandbox: params.sandbox } : {}),\n\t\t...(params.agentId ? { resumeAgentId: params.agentId } : {}),\n\t});\n\n\tconst text: string[] = [];\n\tconst reasoning: string[] = [];\n\tconst toolActivity: DelegateToolActivity[] = [];\n\tlet usage: CursorUsage | undefined;\n\n\ttry {\n\t\tfor await (const event of streamAgentTurn(\n\t\t\tacquired.agent,\n\t\t\t{ text: params.prompt },\n\t\t\t{\n\t\t\t\tmode,\n\t\t\t\t...(params.abortSignal ? { abortSignal: params.abortSignal } : {}),\n\t\t\t},\n\t\t)) {\n\t\t\tswitch (event.type) {\n\t\t\t\tcase \"text-delta\":\n\t\t\t\t\ttext.push(event.text);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"reasoning-delta\":\n\t\t\t\t\treasoning.push(event.text);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tool-call\":\n\t\t\t\t\ttoolActivity.push({ name: event.name, isError: false });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tool-result\":\n\t\t\t\t\tif (event.isError)\n\t\t\t\t\t\ttoolActivity.push({ name: event.name, isError: true });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"usage\":\n\t\t\t\t\tusage = event.usage;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"finish\":\n\t\t\t\t\t// The aggregated result text; prefer it when deltas were absent.\n\t\t\t\t\tif (event.text && text.length === 0) text.push(event.text);\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} finally {\n\t\tacquired.release();\n\t}\n\n\treturn {\n\t\tagentId: acquired.agent.agentId,\n\t\ttext: text.join(\"\"),\n\t\treasoning: reasoning.join(\"\"),\n\t\ttoolActivity,\n\t\t...(usage ? { usage } : {}),\n\t};\n}\n","import type { Config, Plugin } from \"@opencode-ai/plugin\";\nimport type { Auth } from \"@opencode-ai/sdk/v2\";\nimport type { McpServerConfig } from \"@cursor/sdk\";\nimport { resolveCursorApiKey } from \"../api-key.js\";\nimport { discoverModels, toOpencodeModels } from \"../model-discovery.js\";\nimport { buildModelV2Map, PROVIDER_ID, providerNpm } from \"./model-v2.js\";\nimport {\n\tfindUnshareableOAuthServers,\n\ttype McpStatusMap,\n\ttranslateMcpServers,\n} from \"./mcp-config.js\";\nimport { buildCursorTools } from \"./cursor-tools.js\";\n\nfunction apiKeyFromAuth(auth: Auth | undefined): string | undefined {\n\treturn auth?.type === \"api\" ? auth.key : undefined;\n}\n\n/**\n * opencode plugin that adds a \"Cursor\" provider backed by the official Cursor\n * SDK (`@cursor/sdk`).\n *\n * - `auth`: registers an API-key login for Cursor and a `loader` that feeds the\n * key into the AI-SDK provider factory. The key is validated on first use\n * (model discovery / first call), not at login — see the note on `methods`.\n * - `config`: registers the provider (npm package + discovered/fallback models)\n * so it shows up in opencode immediately.\n * - `provider.models`: auth-aware live model discovery via `Cursor.models.list`.\n * - `tool.cursor_refresh_models`: force-refresh the model catalog.\n */\nexport const CursorPlugin: Plugin = async (input) => {\n\t// The Cursor API key resolved by opencode's auth loader, captured so the\n\t// delegation tools (which don't receive auth directly) can reuse it. Falls\n\t// back to the CURSOR_API_KEY env var when the loader hasn't run.\n\tlet capturedApiKey: string | undefined;\n\n\t// opencode client + MCP-forwarding settings captured at config time so the\n\t// per-turn chat.params hook can re-forward the *live* MCP server set\n\t// (reflecting mid-session enable/disable) rather than the startup snapshot.\n\tconst client = input?.client;\n\tconst directory = input?.directory;\n\tlet forwardMcp = true;\n\tlet userMcp: Record<string, McpServerConfig> = {};\n\t// OAuth servers we've already warned about, so the toast fires once per\n\t// server rather than on every turn.\n\tconst warnedOAuth = new Set<string>();\n\n\treturn {\n\t\tauth: {\n\t\t\tprovider: PROVIDER_ID,\n\t\t\tloader: async (getAuth) => {\n\t\t\t\tconst apiKey = resolveCursorApiKey(\n\t\t\t\t\tapiKeyFromAuth(await getAuth().catch(() => undefined)),\n\t\t\t\t);\n\t\t\t\tif (apiKey) {\n\t\t\t\t\tcapturedApiKey = apiKey;\n\t\t\t\t\t// The `config` hook (which seeds opencode's model picker) runs without\n\t\t\t\t\t// a key. Warm the catalog cache here — the loader is the hook that\n\t\t\t\t\t// reliably has the key — so the next launch seeds the full live\n\t\t\t\t\t// catalog instead of the static fallback. Fire-and-forget: discovery\n\t\t\t\t\t// never throws and must not block auth/provider load.\n\t\t\t\t\tvoid discoverModels({ apiKey });\n\t\t\t\t}\n\t\t\t\treturn apiKey ? { apiKey } : {};\n\t\t\t},\n\t\t\t// A single API-key method. opencode always shows its built-in \"Enter your\n\t\t\t// API key\" prompt for `type: \"api\"`, so we intentionally do NOT declare\n\t\t\t// custom `prompts` (that asks for the key a second time) or an `authorize`\n\t\t\t// callback. opencode only passes `authorize` the *custom-prompt* inputs —\n\t\t\t// never the built-in key — so validating the key in `authorize` would\n\t\t\t// force that redundant extra prompt. Instead the key is validated on first\n\t\t\t// use (model discovery / the first call both surface a bad key clearly).\n\t\t\tmethods: [{ type: \"api\", label: \"Cursor API Key\" }],\n\t\t},\n\n\t\tconfig: async (config) => {\n\t\t\tconst { models } = await discoverModels({});\n\t\t\tconfig.provider ??= {};\n\t\t\tconst existing = config.provider[PROVIDER_ID] ?? {};\n\t\t\tconst existingOptions = (existing.options ?? {}) as Record<\n\t\t\t\tstring,\n\t\t\t\tunknown\n\t\t\t>;\n\n\t\t\t// Forward opencode's configured MCP servers to the Cursor\n\t\t\t// agent so it can use the same servers. Opt out via\n\t\t\t// `provider.cursor.options.forwardMcp: false`.\n\t\t\tforwardMcp = existingOptions[\"forwardMcp\"] !== false;\n\t\t\tuserMcp = (existingOptions[\"mcpServers\"] ?? {}) as Record<\n\t\t\t\tstring,\n\t\t\t\tMcpServerConfig\n\t\t\t>;\n\t\t\tconst mcpServers = forwardMcp\n\t\t\t\t? { ...userMcp, ...translateMcpServers(config.mcp) }\n\t\t\t\t: userMcp;\n\n\t\t\tconfig.provider[PROVIDER_ID] = {\n\t\t\t\tname: \"Cursor\",\n\t\t\t\tnpm: providerNpm(),\n\t\t\t\t...existing,\n\t\t\t\toptions: {\n\t\t\t\t\t...existingOptions,\n\t\t\t\t\t...(Object.keys(mcpServers).length > 0 ? { mcpServers } : {}),\n\t\t\t\t},\n\t\t\t\tmodels: { ...toOpencodeModels(models), ...(existing.models ?? {}) },\n\t\t\t};\n\t\t},\n\n\t\tprovider: {\n\t\t\tid: PROVIDER_ID,\n\t\t\tmodels: async (_provider, ctx) => {\n\t\t\t\tconst apiKey = apiKeyFromAuth(ctx.auth);\n\t\t\t\tconst { models } = await discoverModels({ apiKey });\n\t\t\t\treturn buildModelV2Map(models);\n\t\t\t},\n\t\t},\n\n\t\t// Bridge opencode's session id to the provider: it lands in\n\t\t// providerOptions.cursor.sessionID, which the provider reads to pool/resume a\n\t\t// Cursor agent per session (when the `session` option is enabled).\n\t\t//\n\t\t// Also map opencode's plan AGENT to Cursor's plan mode. This hook fires\n\t\t// after opencode merges the selected variant into `output.options`, so an\n\t\t// explicit mode from the `plan` variant (or model options) wins — the\n\t\t// agent-based default only applies when no mode was set.\n\t\t\"chat.params\": async (input, output) => {\n\t\t\tif (input.model?.providerID !== PROVIDER_ID) return;\n\t\t\toutput.options = {\n\t\t\t\t...(output.options ?? {}),\n\t\t\t\tsessionID: input.sessionID,\n\t\t\t};\n\t\t\tif (input.agent === \"plan\" && output.options[\"mode\"] === undefined) {\n\t\t\t\toutput.options[\"mode\"] = \"plan\";\n\t\t\t}\n\n\t\t\t// Dynamically re-forward MCP servers from opencode's *live* state so\n\t\t\t// mid-session enable/disable reaches the Cursor agent (the config hook\n\t\t\t// only snapshots the set once, at startup). `client.mcp.status()` is the\n\t\t\t// runtime truth (connected/disabled/...) and `client.config.get()`\n\t\t\t// supplies the launch specs. On any failure we leave the static snapshot\n\t\t\t// (already baked into the provider options) in place.\n\t\t\tif (forwardMcp && client) {\n\t\t\t\ttry {\n\t\t\t\t\tconst query = directory ? { query: { directory } } : undefined;\n\t\t\t\t\tconst [cfgRes, statusRes] = await Promise.all([\n\t\t\t\t\t\tclient.config.get(),\n\t\t\t\t\t\tclient.mcp.status(query),\n\t\t\t\t\t]);\n\t\t\t\t\tconst liveMcp = (cfgRes?.data as Config | undefined)?.mcp;\n\t\t\t\t\tconst status = statusRes?.data as McpStatusMap | undefined;\n\t\t\t\t\tif (status) {\n\t\t\t\t\t\toutput.options[\"mcpServers\"] = {\n\t\t\t\t\t\t\t...userMcp,\n\t\t\t\t\t\t\t...translateMcpServers(liveMcp, status),\n\t\t\t\t\t\t};\n\t\t\t\t\t\t// Notify (once) about OAuth servers we can't forward: opencode\n\t\t\t\t\t\t// holds their token and it never reaches config.mcp, so the\n\t\t\t\t\t\t// Cursor agent can't connect. Only those without a shareable\n\t\t\t\t\t\t// client registration are skipped; ones with a clientId are\n\t\t\t\t\t\t// forwarded with an `auth` block for the agent's own OAuth flow.\n\t\t\t\t\t\tconst unshareable = findUnshareableOAuthServers(\n\t\t\t\t\t\t\tliveMcp,\n\t\t\t\t\t\t\tstatus,\n\t\t\t\t\t\t).filter((name) => !warnedOAuth.has(name));\n\t\t\t\t\t\tif (unshareable.length > 0) {\n\t\t\t\t\t\t\tfor (const name of unshareable) warnedOAuth.add(name);\n\t\t\t\t\t\t\tconst plural = unshareable.length > 1;\n\t\t\t\t\t\t\tvoid client.tui\n\t\t\t\t\t\t\t\t.showToast({\n\t\t\t\t\t\t\t\t\tbody: {\n\t\t\t\t\t\t\t\t\t\ttitle: \"Cursor MCP\",\n\t\t\t\t\t\t\t\t\t\tmessage: `Skipped OAuth MCP server${plural ? \"s\" : \"\"}: ${unshareable.join(\", \")}. opencode's token can't be shared with the Cursor agent; configure an OAuth clientId to forward ${plural ? \"them\" : \"it\"}.`,\n\t\t\t\t\t\t\t\t\t\tvariant: \"warning\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.catch(() => {});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Keep the static snapshot; live forwarding is best-effort.\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\ttool: {\n\t\t\tcursor_refresh_models: {\n\t\t\t\tdescription:\n\t\t\t\t\t\"Refresh the live Cursor model catalog (bypasses the 24h cache) and report the available models.\",\n\t\t\t\targs: {},\n\t\t\t\texecute: async () => {\n\t\t\t\t\tconst result = await discoverModels({ forceRefresh: true });\n\t\t\t\t\tconst lines = result.models.map(\n\t\t\t\t\t\t(m) => `- ${m.id} — ${m.displayName}`,\n\t\t\t\t\t);\n\t\t\t\t\tconst header =\n\t\t\t\t\t\tresult.source === \"live\"\n\t\t\t\t\t\t\t? `Refreshed ${result.models.length} Cursor models (live):`\n\t\t\t\t\t\t\t: `Could not fetch live models (${result.source}). ${result.warning ?? \"\"}`.trim();\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttitle: `Cursor models (${result.source})`,\n\t\t\t\t\t\toutput: [header, ...lines].join(\"\\n\"),\n\t\t\t\t\t\tmetadata: { source: result.source, count: result.models.length },\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t},\n\t\t\t// Delegation tools that complement the provider: a cloud/background agent\n\t\t\t// and a permission-gated local delegate. They resolve the Cursor key from\n\t\t\t// the auth loader (captured above) or CURSOR_API_KEY.\n\t\t\t...buildCursorTools({\n\t\t\t\tresolveApiKey: () => resolveCursorApiKey(capturedApiKey),\n\t\t\t\tdefaultCwd: () => input?.directory ?? process.cwd(),\n\t\t\t}),\n\t\t},\n\t};\n};\n\nexport default CursorPlugin;\n"],"mappings":"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","names":["s","input"]}
@@ -36,8 +36,8 @@ interface CursorProviderOptions {
36
36
  /**
37
37
  * MCP servers to make available to the Cursor agent, keyed by name. The
38
38
  * plugin's `config` hook populates this by translating opencode's configured
39
- * `config.mcp` servers, so the agent can use the same MCP servers (e.g.
40
- * Serena) that opencode does.
39
+ * `config.mcp` servers, so the agent can use the same MCP servers that
40
+ * opencode does.
41
41
  */
42
42
  mcpServers?: Record<string, McpServerConfig>;
43
43
  /**
@@ -51,10 +51,12 @@ interface CursorProviderOptions {
51
51
  /** Cursor subagent definitions (`{ description, prompt, model?, mcpServers? }`). */
52
52
  agents?: Record<string, AgentDefinition>;
53
53
  /**
54
- * Reuse one Cursor agent per opencode session (resume across turns instead of
55
- * creating a fresh agent each turn). Off by default.
54
+ * Session reuse strategy: `"auto"` (default) resumes the pooled Cursor agent
55
+ * and sends only the new message on a clean continuation, falling back to a
56
+ * fresh agent + full transcript on edits/reverts/compaction/side calls; `true`
57
+ * is an alias for `"auto"`; `false` always creates a fresh agent per turn.
56
58
  */
57
- session?: boolean;
59
+ session?: boolean | "auto";
58
60
  /**
59
61
  * How Cursor's internal tool activity (shell/read/edit/mcp/…) is surfaced:
60
62
  * - `"blocks"` (default): structured provider-executed `tool-call`/