@stablekernel/opencode-cursor 0.4.2 → 0.4.4-next.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.
@@ -93,13 +93,16 @@ function defaultModelParams(item) {
93
93
  function buildModelVariants(item) {
94
94
  const out = {};
95
95
  const defaults = defaultModelParams(item);
96
+ const hasEffortEnum = (item.parameters ?? []).some(
97
+ (p) => REASONING_PARAM.test(p.id) && !isBooleanParam(paramValues(p)) && paramValues(p).length > 0
98
+ );
96
99
  for (const param of item.parameters ?? []) {
97
100
  const values = paramValues(param);
98
101
  if (values.length === 0) continue;
99
102
  const boolean = isBooleanParam(values);
100
103
  if (REASONING_PARAM.test(param.id)) {
101
104
  if (boolean) {
102
- if (values.includes("true")) {
105
+ if (!hasEffortEnum && values.includes("true")) {
103
106
  out[param.id.toLowerCase()] = { params: { ...defaults, [param.id]: "true" } };
104
107
  }
105
108
  continue;
@@ -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\nfunction paramValues(param: NonNullable<ModelListItem[\"parameters\"]>[number]): string[] {\n return (param.values ?? []).map((v) => v.value);\n}\n\nfunction isBooleanParam(values: string[]): boolean {\n return values.length > 0 && values.every((v) => BOOLEAN_VALUES.has(v));\n}\n\n/**\n * Params opencode must send by DEFAULT for this model — i.e. when the user has\n * NOT picked a variant. Non-reasoning boolean toggles (notably Cursor's `fast`)\n * are pinned OFF here so the provider never silently inherits Cursor's\n * server-side default, which is `fast: true` for several models (composer-*,\n * gpt-*-codex). The user opts back IN via the matching picker variant.\n *\n * Seeded into each model's opencode `options.params` (see `toOpencodeModels` /\n * `buildModelV2Map`); {@link resolveControls} merges it into the request.\n */\nexport function defaultModelParams(item: ModelListItem): Record<string, string> {\n const out: Record<string, string> = {};\n for (const param of item.parameters ?? []) {\n if (REASONING_PARAM.test(param.id)) continue;\n if (isBooleanParam(paramValues(param))) out[param.id] = \"false\";\n }\n return out;\n}\n\n/**\n * Derive opencode model variants from a Cursor model's parameters so the\n * variant picker can expose thinking/reasoning levels plus the `fast` toggle.\n * Each variant's object is exactly what {@link resolveControls} consumes. Plan\n * mode is NOT a variant: opencode's plan agent (Tab) is mapped to Cursor's plan\n * mode by the plugin's `chat.params` hook.\n *\n * Every variant for a fast-capable model carries an explicit `fast` value\n * (reasoning variants pin it OFF via {@link defaultModelParams}; the `fast`\n * variant turns it ON) so a selection never depends on Cursor's server-side\n * default for an omitted param.\n */\nexport function buildModelVariants(item: ModelListItem): Record<string, CursorVariant> {\n const out: Record<string, CursorVariant> = {};\n // Non-reasoning boolean defaults (e.g. { fast: \"false\" }), pinned into every\n // reasoning variant so picking a reasoning level never re-enables fast.\n const defaults = defaultModelParams(item);\n\n for (const param of item.parameters ?? []) {\n const values = paramValues(param);\n if (values.length === 0) continue;\n const boolean = isBooleanParam(values);\n\n if (REASONING_PARAM.test(param.id)) {\n if (boolean) {\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: { ...defaults, [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: { ...defaults, [param.id]: value } };\n }\n continue;\n }\n\n // Non-reasoning boolean toggle (e.g. Cursor's `fast`). Default is OFF (see\n // defaultModelParams); expose a single opt-in variant that turns it ON.\n if (boolean && values.includes(\"true\")) {\n out[param.id.toLowerCase()] = { params: { ...defaults, [param.id]: \"true\" } };\n }\n // Non-reasoning enum params (e.g. `context`) remain unsupported in the picker.\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, defaultModelParams, 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 * Default `providerOptions.cursor` for the model, merged into every request\n * unless a variant overrides it. Carries the non-reasoning boolean defaults\n * (e.g. `{ params: { fast: \"false\" } }`) so the provider never silently runs\n * Cursor's server-side `fast` default. See {@link defaultModelParams}.\n */\n options: { params?: Record<string, string> };\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 const params = defaultModelParams(item);\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 options: Object.keys(params).length > 0 ? { params } : {},\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, defaultModelParams } 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 const params = defaultModelParams(item);\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: Object.keys(params).length > 0 ? { params } : {},\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"]}
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\nfunction paramValues(param: NonNullable<ModelListItem[\"parameters\"]>[number]): string[] {\n return (param.values ?? []).map((v) => v.value);\n}\n\nfunction isBooleanParam(values: string[]): boolean {\n return values.length > 0 && values.every((v) => BOOLEAN_VALUES.has(v));\n}\n\n/**\n * Params opencode must send by DEFAULT for this model — i.e. when the user has\n * NOT picked a variant. Non-reasoning boolean toggles (notably Cursor's `fast`)\n * are pinned OFF here so the provider never silently inherits Cursor's\n * server-side default, which is `fast: true` for several models (composer-*,\n * gpt-*-codex). The user opts back IN via the matching picker variant.\n *\n * Seeded into each model's opencode `options.params` (see `toOpencodeModels` /\n * `buildModelV2Map`); {@link resolveControls} merges it into the request.\n */\nexport function defaultModelParams(item: ModelListItem): Record<string, string> {\n const out: Record<string, string> = {};\n for (const param of item.parameters ?? []) {\n if (REASONING_PARAM.test(param.id)) continue;\n if (isBooleanParam(paramValues(param))) out[param.id] = \"false\";\n }\n return out;\n}\n\n/**\n * Derive opencode model variants from a Cursor model's parameters so the\n * variant picker can expose thinking/reasoning levels plus the `fast` toggle.\n * Each variant's object is exactly what {@link resolveControls} consumes. Plan\n * mode is NOT a variant: opencode's plan agent (Tab) is mapped to Cursor's plan\n * mode by the plugin's `chat.params` hook.\n *\n * Every variant for a fast-capable model carries an explicit `fast` value\n * (reasoning variants pin it OFF via {@link defaultModelParams}; the `fast`\n * variant turns it ON) so a selection never depends on Cursor's server-side\n * default for an omitted param.\n */\nexport function buildModelVariants(item: ModelListItem): Record<string, CursorVariant> {\n const out: Record<string, CursorVariant> = {};\n // Non-reasoning boolean defaults (e.g. { fast: \"false\" }), pinned into every\n // reasoning variant so picking a reasoning level never re-enables fast.\n const defaults = defaultModelParams(item);\n\n // Pre-pass: does any reasoning param expose a non-boolean effort enum (e.g.\n // [\"low\",\"medium\",\"high\",\"xhigh\",\"max\"])? When it does, a coexisting boolean\n // reasoning toggle (Cursor's `thinking=[\"false\",\"true\"]` on claude-* models)\n // is redundant — selecting any effort level already enables reasoning — and\n // surfacing it would add a stray `thinking` variant the standard opencode\n // providers don't show. Suppress the boolean variant for parity. Order-\n // independent: the enum may be declared before or after the boolean.\n const hasEffortEnum = (item.parameters ?? []).some(\n (p) => REASONING_PARAM.test(p.id) && !isBooleanParam(paramValues(p)) && paramValues(p).length > 0,\n );\n\n for (const param of item.parameters ?? []) {\n const values = paramValues(param);\n if (values.length === 0) continue;\n const boolean = isBooleanParam(values);\n\n if (REASONING_PARAM.test(param.id)) {\n if (boolean) {\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). Skipped entirely when an\n // effort enum coexists (see hasEffortEnum above).\n if (!hasEffortEnum && values.includes(\"true\")) {\n out[param.id.toLowerCase()] = { params: { ...defaults, [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: { ...defaults, [param.id]: value } };\n }\n continue;\n }\n\n // Non-reasoning boolean toggle (e.g. Cursor's `fast`). Default is OFF (see\n // defaultModelParams); expose a single opt-in variant that turns it ON.\n if (boolean && values.includes(\"true\")) {\n out[param.id.toLowerCase()] = { params: { ...defaults, [param.id]: \"true\" } };\n }\n // Non-reasoning enum params (e.g. `context`) remain unsupported in the picker.\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, defaultModelParams, 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 * Default `providerOptions.cursor` for the model, merged into every request\n * unless a variant overrides it. Carries the non-reasoning boolean defaults\n * (e.g. `{ params: { fast: \"false\" } }`) so the provider never silently runs\n * Cursor's server-side `fast` default. See {@link defaultModelParams}.\n */\n options: { params?: Record<string, string> };\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 const params = defaultModelParams(item);\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 options: Object.keys(params).length > 0 ? { params } : {},\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, defaultModelParams } 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 const params = defaultModelParams(item);\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: Object.keys(params).length > 0 ? { params } : {},\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"]}
@@ -162,6 +162,13 @@ function toolResultObj(id, name, result, isError) {
162
162
  return nativeToolResult(id, blockToolName(name), result, isError);
163
163
  }
164
164
  var EDIT_TOOL_NAME = "edit";
165
+ var CREATE_PLAN_TOOL_NAME = "createPlan";
166
+ function isCreatePlanTool(name) {
167
+ return name === CREATE_PLAN_TOOL_NAME;
168
+ }
169
+ function createPlanContent(input) {
170
+ return strField(input, "plan");
171
+ }
165
172
  function isRecord(v) {
166
173
  return typeof v === "object" && v !== null;
167
174
  }
@@ -569,7 +576,7 @@ function editResultFields(id, filePath, diff, result) {
569
576
  };
570
577
  }
571
578
  function newBlockToolState() {
572
- return { open: /* @__PURE__ */ new Map(), pendingEdits: /* @__PURE__ */ new Map() };
579
+ return { open: /* @__PURE__ */ new Map(), pendingEdits: /* @__PURE__ */ new Map(), dropped: /* @__PURE__ */ new Set() };
573
580
  }
574
581
  function blockToolCallParts(id, name, input, state) {
575
582
  if (name === EDIT_TOOL_NAME) {
@@ -735,6 +742,20 @@ function cursorEventsToStream(events, toolDisplay = "blocks") {
735
742
  reasoningLine(event.text);
736
743
  break;
737
744
  case "tool-call":
745
+ if (isCreatePlanTool(event.name)) {
746
+ const plan = createPlanContent(event.input);
747
+ if (plan) {
748
+ closeReasoning();
749
+ streamedText = true;
750
+ controller.enqueue({
751
+ type: "text-delta",
752
+ id: ensureText(),
753
+ delta: plan
754
+ });
755
+ }
756
+ toolState.dropped.add(event.id);
757
+ break;
758
+ }
738
759
  if (toolDisplay === "blocks") {
739
760
  const parts = blockToolCallParts(
740
761
  event.id,
@@ -756,6 +777,7 @@ ${formatToolCall(event.name, event.input)}
756
777
  }
757
778
  break;
758
779
  case "tool-result":
780
+ if (toolState.dropped.delete(event.id)) break;
759
781
  if (toolDisplay === "blocks") {
760
782
  const parts = blockToolResultParts(
761
783
  event.id,
@@ -832,6 +854,12 @@ async function cursorEventsToContent(events, toolDisplay = "blocks") {
832
854
  reasoning += event.text;
833
855
  break;
834
856
  case "tool-call":
857
+ if (isCreatePlanTool(event.name)) {
858
+ const plan = createPlanContent(event.input);
859
+ if (plan) text += plan;
860
+ toolState.dropped.add(event.id);
861
+ break;
862
+ }
835
863
  if (toolDisplay === "blocks") {
836
864
  for (const part of blockToolCallParts(
837
865
  event.id,
@@ -848,6 +876,7 @@ ${formatToolCall(event.name, event.input)}
848
876
  }
849
877
  break;
850
878
  case "tool-result":
879
+ if (toolState.dropped.delete(event.id)) break;
851
880
  if (toolDisplay === "blocks") {
852
881
  for (const part of blockToolResultParts(
853
882
  event.id,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/provider/index.ts","../../src/provider/language-model.ts","../../src/provider/message-map.ts","../../src/provider/stream-map.ts","../../src/provider/transcript-fingerprint.ts"],"sourcesContent":["import type {\n\tEmbeddingModelV3,\n\tImageModelV3,\n\tProviderV3,\n} from \"@ai-sdk/provider\";\nimport { NoSuchModelError } from \"@ai-sdk/provider\";\nimport type {\n\tAgentDefinition,\n\tAgentModeOption,\n\tMcpServerConfig,\n\tSettingSource,\n} from \"@cursor/sdk\";\nimport { resolveCursorApiKey } from \"../api-key.js\";\nimport {\n\tCursorLanguageModel,\n\ttype CursorModelConfig,\n} from \"./language-model.js\";\nimport type { ToolDisplay } from \"./stream-map.js\";\n\nexport interface CursorProviderOptions {\n\t/**\n\t * Cursor API key. opencode passes this from the provider's resolved auth /\n\t * options. When omitted, falls back to the CURSOR_API_KEY environment\n\t * variable at call time.\n\t */\n\tapiKey?: string;\n\t/** Provider id, supplied by opencode as `name`. Defaults to \"cursor\". */\n\tname?: string;\n\t/** Working directory for the local Cursor agent. Defaults to process.cwd(). */\n\tcwd?: string;\n\t/** Default conversation mode: \"agent\" (default) or \"plan\". Overridable per-request. */\n\tmode?: AgentModeOption;\n\t/** Default Cursor model params (id -> value), e.g. { thinking: \"high\" }. */\n\tparams?: Record<string, string>;\n\t/**\n\t * MCP servers to make available to the Cursor agent, keyed by name. The\n\t * plugin's `config` hook populates this by translating opencode's configured\n\t * `config.mcp` servers, so the agent can use the same MCP servers that\n\t * opencode does.\n\t */\n\tmcpServers?: Record<string, McpServerConfig>;\n\t/**\n\t * Cursor settings layers to load from the local filesystem (\"project\",\n\t * \"user\", \"all\", ...). Enables the agent to pick up your Cursor skills,\n\t * rules, and `.cursor/mcp.json` servers.\n\t */\n\tsettingSources?: SettingSource[];\n\t/** Run the agent's tools inside Cursor's sandbox. */\n\tsandbox?: boolean;\n\t/** Cursor subagent definitions (`{ description, prompt, model?, mcpServers? }`). */\n\tagents?: Record<string, AgentDefinition>;\n\t/**\n\t * Session reuse strategy: `\"auto\"` (default) resumes the pooled Cursor agent\n\t * and sends only the new message on a clean continuation, falling back to a\n\t * fresh agent + full transcript on edits/reverts/compaction/side calls; `true`\n\t * is an alias for `\"auto\"`; `false` always creates a fresh agent per turn.\n\t */\n\tsession?: boolean | \"auto\";\n\t/**\n\t * How Cursor's internal tool activity (shell/read/edit/mcp/…) is surfaced:\n\t * - `\"blocks\"` (default): structured provider-executed `tool-call`/\n\t * `tool-result` parts so opencode renders proper tool blocks. Requires a\n\t * V3-native opencode host (1.16+).\n\t * - `\"reasoning\"`: compact reasoning lines; the fallback for older/non-V3\n\t * hosts (works everywhere).\n\t */\n\ttoolDisplay?: ToolDisplay;\n}\n\n/**\n * Cursor provider for the Vercel AI SDK (V3), backed by the official\n * `@cursor/sdk` local agent runtime.\n *\n * opencode loads this package by its `npm` provider config, finds the export\n * whose name starts with `create`, calls it with `{ name, apiKey, ...options }`,\n * and then calls `.languageModel(modelId)`.\n */\nexport function createCursor(options: CursorProviderOptions = {}): ProviderV3 {\n\tconst mcpServers =\n\t\toptions.mcpServers && Object.keys(options.mcpServers).length > 0\n\t\t\t? options.mcpServers\n\t\t\t: undefined;\n\tconst config: CursorModelConfig = {\n\t\tproviderName: options.name ?? \"cursor\",\n\t\tapiKey: resolveCursorApiKey(options.apiKey),\n\t\tcwd: options.cwd ?? process.cwd(),\n\t\tmode: options.mode ?? \"agent\",\n\t\t...(options.params ? { params: options.params } : {}),\n\t\t...(mcpServers ? { mcpServers } : {}),\n\t\t...(options.settingSources\n\t\t\t? { settingSources: options.settingSources }\n\t\t\t: {}),\n\t\t...(options.sandbox !== undefined ? { sandbox: options.sandbox } : {}),\n\t\t...(options.agents ? { agents: options.agents } : {}),\n\t\tsession: options.session ?? \"auto\",\n\t\ttoolDisplay: options.toolDisplay ?? \"blocks\",\n\t};\n\n\tconst notImplemented = (kind: string, modelId: string): never => {\n\t\tthrow new NoSuchModelError({\n\t\t\tmodelId,\n\t\t\tmodelType: kind as \"languageModel\",\n\t\t\tmessage: `The Cursor provider does not support ${kind} models.`,\n\t\t});\n\t};\n\n\treturn {\n\t\tspecificationVersion: \"v3\",\n\t\tlanguageModel: (modelId: string) =>\n\t\t\tnew CursorLanguageModel(modelId, config),\n\t\tembeddingModel: (modelId: string): EmbeddingModelV3 =>\n\t\t\tnotImplemented(\"embeddingModel\", modelId),\n\t\timageModel: (modelId: string): ImageModelV3 =>\n\t\t\tnotImplemented(\"imageModel\", modelId),\n\t};\n}\n","import type {\n\tLanguageModelV3,\n\tLanguageModelV3CallOptions,\n\tLanguageModelV3Content,\n\tLanguageModelV3FinishReason,\n\tLanguageModelV3StreamPart,\n\tLanguageModelV3Usage,\n} from \"@ai-sdk/provider\";\nimport { LoadAPIKeyError } from \"@ai-sdk/provider\";\nimport type {\n\tAgentDefinition,\n\tMcpServerConfig,\n\tSettingSource,\n\tAgentModeOption,\n} from \"@cursor/sdk\";\nimport { resolveCursorApiKey } from \"../api-key.js\";\nimport { latestUserMessage, promptToCursorMessage } from \"./message-map.js\";\nimport { streamAgentTurn, type CursorEvent } from \"./agent-events.js\";\nimport {\n\tcursorEventsToContent,\n\tcursorEventsToStream,\n\ttype ToolDisplay,\n} from \"./stream-map.js\";\nimport { resolveControls } from \"./controls.js\";\nimport { acquireAgent, getSessionRecord } from \"./session-pool.js\";\nimport {\n\tclassifyTurn,\n\tfingerprint,\n\tmcpServersFingerprint,\n} from \"./transcript-fingerprint.js\";\n\nexport interface CursorModelConfig {\n\t/** Provider id used for logging and the providerOptions key (e.g. \"cursor\"). */\n\tproviderName: string;\n\t/** Explicit API key; re-resolved against the env at call time when absent. */\n\tapiKey?: string;\n\t/** Working directory the local Cursor agent operates in. */\n\tcwd: string;\n\t/** Default conversation mode; overridable per-request via providerOptions. */\n\tmode: AgentModeOption;\n\t/** Default Cursor model params (id -> value); overridable per-request. */\n\tparams?: Record<string, string>;\n\t/** MCP servers forwarded to the Cursor agent from opencode's config. */\n\tmcpServers?: Record<string, McpServerConfig>;\n\t/** Cursor settings layers to load from disk (skills, rules, .cursor/mcp.json). */\n\tsettingSources?: SettingSource[];\n\t/** Run the agent's tools inside Cursor's sandbox. */\n\tsandbox?: boolean;\n\t/** Cursor subagent definitions made available to the agent. */\n\tagents?: Record<string, AgentDefinition>;\n\t/**\n\t * Session reuse strategy:\n\t * - `\"auto\"` (default): fingerprint-guarded reuse — resume the pooled Cursor\n\t * agent and send only the new message on a clean continuation, otherwise\n\t * fall back to a fresh agent + full transcript. Robust to opencode's\n\t * non-chat side calls, message edits, reverts, and compaction.\n\t * - `true`: alias for `\"auto\"`.\n\t * - `false`: always create a fresh agent per turn and re-send the full\n\t * transcript (the original behavior; the escape hatch).\n\t */\n\tsession?: boolean | \"auto\";\n\t/**\n\t * How Cursor's internal tool activity is surfaced (see {@link ToolDisplay}).\n\t * Defaults to `\"blocks\"`.\n\t */\n\ttoolDisplay?: ToolDisplay;\n}\n\n/**\n * A Vercel AI SDK `LanguageModelV3` backed by a local Cursor agent. opencode\n * loads this via the provider factory and calls `doStream` / `doGenerate`.\n * The event→stream translation lives in stream-map.ts so it can be unit tested\n * without a live agent.\n */\nexport class CursorLanguageModel implements LanguageModelV3 {\n\treadonly specificationVersion = \"v3\" as const;\n\treadonly modelId: string;\n\treadonly provider: string;\n\t// Images are passed inline as base64 data, so no URLs are fetched natively.\n\treadonly supportedUrls: Record<string, RegExp[]> = {};\n\n\tconstructor(\n\t\tmodelId: string,\n\t\tprivate readonly config: CursorModelConfig,\n\t) {\n\t\tthis.modelId = modelId;\n\t\tthis.provider = config.providerName;\n\t}\n\n\tprivate requireApiKey(): string {\n\t\tconst apiKey = resolveCursorApiKey(this.config.apiKey);\n\t\tif (!apiKey) {\n\t\t\tthrow new LoadAPIKeyError({\n\t\t\t\tmessage:\n\t\t\t\t\t\"Cursor API key missing. Run `opencode auth login` and choose Cursor, or set CURSOR_API_KEY.\",\n\t\t\t});\n\t\t}\n\t\treturn apiKey;\n\t}\n\n\tprivate async *agentRun(\n\t\toptions: LanguageModelV3CallOptions,\n\t): AsyncGenerator<CursorEvent> {\n\t\t// opencode delivers per-request controls (merged model options + selected\n\t\t// variant) under providerOptions keyed by our provider id. The session id is\n\t\t// injected there by the plugin's chat.params hook.\n\t\tconst providerOptions = options.providerOptions?.[this.provider] as\n\t\t\t| Record<string, unknown>\n\t\t\t| undefined;\n\t\tconst { mode, modelSelection } = resolveControls(\n\t\t\tthis.modelId,\n\t\t\t{ mode: this.config.mode, params: this.config.params },\n\t\t\tproviderOptions,\n\t\t);\n\t\tconst sessionID =\n\t\t\ttypeof providerOptions?.[\"sessionID\"] === \"string\"\n\t\t\t\t? (providerOptions[\"sessionID\"] as string)\n\t\t\t\t: undefined;\n\t\t// MCP servers may be re-forwarded per turn by the plugin's chat.params hook\n\t\t// (reflecting live opencode enable/disable). When present, the dynamic set\n\t\t// wins over the static startup snapshot baked into config.mcpServers.\n\t\tconst dynamicMcp = providerOptions?.[\"mcpServers\"] as\n\t\t\t| Record<string, McpServerConfig>\n\t\t\t| undefined;\n\t\tconst mcpServers = dynamicMcp ?? this.config.mcpServers;\n\t\tconst mcpHash = mcpServersFingerprint(mcpServers);\n\t\t// `session` defaults to \"auto\" (fingerprint-guarded reuse); `true` is an\n\t\t// alias for \"auto\"; `false` keeps the per-turn-fresh full-transcript path.\n\t\tconst sessionEnabled = (this.config.session ?? \"auto\") !== false;\n\t\t// Power users can resume a specific Cursor agent via\n\t\t// `providerOptions.cursor.agentId`; it takes precedence over session pooling.\n\t\tconst explicitAgentId =\n\t\t\ttypeof providerOptions?.[\"agentId\"] === \"string\"\n\t\t\t\t? (providerOptions[\"agentId\"] as string)\n\t\t\t\t: undefined;\n\t\t// The plugin may mark opencode's non-chat side calls (e.g. title\n\t\t// generation) so they never resume or disturb the pooled agent.\n\t\tconst ephemeral = providerOptions?.[\"ephemeral\"] === true;\n\n\t\t// Decide create-vs-resume and whether to pool, from the turn classification.\n\t\tconst usePool = sessionEnabled && Boolean(sessionID) && !explicitAgentId;\n\t\tlet resumeAgentId: string | undefined = explicitAgentId;\n\t\tlet poolKey: string | undefined;\n\t\tlet record:\n\t\t\t| { systemHash: string; userHashes: string[]; mcpHash?: string }\n\t\t\t| undefined;\n\t\tif (usePool) {\n\t\t\tconst classification = ephemeral\n\t\t\t\t? {\n\t\t\t\t\t\tkind: \"side-call\" as const,\n\t\t\t\t\t\tfingerprint: fingerprint(options.prompt),\n\t\t\t\t\t}\n\t\t\t\t: classifyTurn(getSessionRecord(sessionID!), options.prompt);\n\t\t\tswitch (classification.kind) {\n\t\t\t\tcase \"continuation\": {\n\t\t\t\t\tconst prev = getSessionRecord(sessionID!);\n\t\t\t\t\t// A resumed agent keeps its original MCP servers, so only resume\n\t\t\t\t\t// when the live MCP set is unchanged; otherwise create fresh so the\n\t\t\t\t\t// new server set takes effect (re-pooled under the same session).\n\t\t\t\t\tif (prev?.mcpHash === mcpHash) {\n\t\t\t\t\t\tresumeAgentId = prev?.agentId;\n\t\t\t\t\t}\n\t\t\t\t\tpoolKey = sessionID;\n\t\t\t\t\trecord = { ...classification.fingerprint, mcpHash };\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"new\":\n\t\t\t\tcase \"divergence\":\n\t\t\t\t\tpoolKey = sessionID;\n\t\t\t\t\trecord = { ...classification.fingerprint, mcpHash };\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"side-call\":\n\t\t\t\t\t// fresh ephemeral agent; pool left untouched.\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (process.env[\"OPENCODE_CURSOR_DEBUG\"] === \"1\") {\n\t\t\t\tconst label =\n\t\t\t\t\tclassification.kind === \"continuation\"\n\t\t\t\t\t\t? \"resume\"\n\t\t\t\t\t\t: `fresh:${classification.kind}`;\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[cursor:debug] turn classification=${label} session=${sessionID}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst acquired = await acquireAgent({\n\t\t\tapiKey: this.requireApiKey(),\n\t\t\tmodelSelection,\n\t\t\tmode,\n\t\t\tcwd: this.config.cwd,\n\t\t\t...(this.config.settingSources\n\t\t\t\t? { settingSources: this.config.settingSources }\n\t\t\t\t: {}),\n\t\t\t...(this.config.sandbox !== undefined\n\t\t\t\t? { sandbox: this.config.sandbox }\n\t\t\t\t: {}),\n\t\t\t...(mcpServers ? { mcpServers } : {}),\n\t\t\t...(this.config.agents ? { agents: this.config.agents } : {}),\n\t\t\t...(poolKey ? { name: `opencode/${sessionID!.slice(-8)}` } : {}),\n\t\t\t...(resumeAgentId ? { resumeAgentId } : {}),\n\t\t\t...(poolKey ? { poolKey } : {}),\n\t\t\t...(record ? { record } : {}),\n\t\t});\n\n\t\t// A resumed agent already remembers the prior conversation, so send only the\n\t\t// new turn; otherwise send the full transcript.\n\t\tconst message = acquired.resumed\n\t\t\t? (latestUserMessage(options.prompt) ??\n\t\t\t\tpromptToCursorMessage(options.prompt))\n\t\t\t: promptToCursorMessage(options.prompt);\n\n\t\ttry {\n\t\t\tyield* streamAgentTurn(acquired.agent, message, {\n\t\t\t\tmode,\n\t\t\t\tabortSignal: options.abortSignal,\n\t\t\t});\n\t\t} finally {\n\t\t\tacquired.release();\n\t\t}\n\t}\n\n\tasync doStream(options: LanguageModelV3CallOptions): Promise<{\n\t\tstream: ReadableStream<LanguageModelV3StreamPart>;\n\t}> {\n\t\treturn {\n\t\t\tstream: cursorEventsToStream(\n\t\t\t\tthis.agentRun(options),\n\t\t\t\tthis.config.toolDisplay,\n\t\t\t),\n\t\t};\n\t}\n\n\tasync doGenerate(options: LanguageModelV3CallOptions): Promise<{\n\t\tcontent: Array<LanguageModelV3Content>;\n\t\tfinishReason: LanguageModelV3FinishReason;\n\t\tusage: LanguageModelV3Usage;\n\t\twarnings: Array<never>;\n\t}> {\n\t\tconst result = await cursorEventsToContent(\n\t\t\tthis.agentRun(options),\n\t\t\tthis.config.toolDisplay,\n\t\t);\n\t\treturn { ...result, warnings: [] };\n\t}\n}\n","import type { LanguageModelV3Prompt } from \"@ai-sdk/provider\";\nimport type { SDKImage, SDKUserMessage } from \"@cursor/sdk\";\n\n/**\n * Caps on inlined tool payloads in the flattened transcript. Tool outputs are\n * INCLUDED (truncated) rather than dropped so a fresh/diverged agent still sees\n * what prior tools produced, while a huge file read or search dump can't bloat\n * the replayed message unbounded.\n */\nconst TOOL_RESULT_CAP = 2000;\nconst TOOL_ARGS_CAP = 500;\n\nfunction stringify(value: unknown): string {\n\tif (typeof value === \"string\") return value;\n\ttry {\n\t\treturn JSON.stringify(value ?? null);\n\t} catch {\n\t\treturn String(value);\n\t}\n}\n\nfunction truncate(text: string, cap: number): string {\n\treturn text.length > cap\n\t\t? `${text.slice(0, cap)}…[+${text.length - cap} chars]`\n\t\t: text;\n}\n\n/**\n * Convert an AI-SDK prompt (the full conversation opencode sends on every call)\n * into a single Cursor `SDKUserMessage`.\n *\n * The Cursor agent keeps its own per-agent conversation memory, but opencode\n * re-sends the whole history each turn. To stay correct without double-counting\n * context, we create a fresh agent per turn (see language-model.ts) and flatten\n * the entire prompt into one transcript message. Images from the final user\n * turn are attached natively so multimodal models can see them.\n */\nexport function promptToCursorMessage(\n\tprompt: LanguageModelV3Prompt,\n): SDKUserMessage {\n\tconst lines: string[] = [];\n\tconst images: SDKImage[] = [];\n\n\tprompt.forEach((message, index) => {\n\t\tconst isLast = index === prompt.length - 1;\n\t\tswitch (message.role) {\n\t\t\tcase \"system\":\n\t\t\t\tlines.push(`# System\\n${message.content}`);\n\t\t\t\tbreak;\n\t\t\tcase \"user\": {\n\t\t\t\tconst text: string[] = [];\n\t\t\t\tfor (const part of message.content) {\n\t\t\t\t\tif (part.type === \"text\") text.push(part.text);\n\t\t\t\t\telse if (\n\t\t\t\t\t\tpart.type === \"file\" &&\n\t\t\t\t\t\tpart.mediaType.startsWith(\"image/\")\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst image = fileToImage(part.data, part.mediaType);\n\t\t\t\t\t\t// Only attach images natively for the final user turn; earlier ones\n\t\t\t\t\t\t// are referenced by transcript order.\n\t\t\t\t\t\tif (isLast && image) images.push(image);\n\t\t\t\t\t\ttext.push(\"[image attached]\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlines.push(`# User\\n${text.join(\"\\n\")}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"assistant\": {\n\t\t\t\tconst text: string[] = [];\n\t\t\t\tfor (const part of message.content) {\n\t\t\t\t\tif (part.type === \"text\") text.push(part.text);\n\t\t\t\t\telse if (part.type === \"reasoning\")\n\t\t\t\t\t\ttext.push(`(thinking) ${part.text}`);\n\t\t\t\t\telse if (part.type === \"tool-call\")\n\t\t\t\t\t\ttext.push(\n\t\t\t\t\t\t\t`[called ${part.toolName}(${truncate(stringify(part.input), TOOL_ARGS_CAP)})]`,\n\t\t\t\t\t\t);\n\t\t\t\t\telse if (part.type === \"tool-result\")\n\t\t\t\t\t\ttext.push(\n\t\t\t\t\t\t\t`[result of ${part.toolName}: ${truncate(stringify(part.output), TOOL_RESULT_CAP)}]`,\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlines.push(`# Assistant\\n${text.join(\"\\n\")}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"tool\": {\n\t\t\t\tfor (const part of message.content) {\n\t\t\t\t\tif (part.type === \"tool-result\") {\n\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t`# Tool result (${part.toolName})\\n${truncate(stringify(part.output), TOOL_RESULT_CAP)}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\tconst out: SDKUserMessage = { text: lines.join(\"\\n\\n\") };\n\tif (images.length > 0) out.images = images;\n\treturn out;\n}\n\nfunction fileToImage(\n\tdata: string | Uint8Array | URL,\n\tmediaType: string,\n): SDKImage | undefined {\n\tif (data instanceof URL) return { url: data.toString() };\n\tif (typeof data === \"string\") {\n\t\t// Either a URL or already-base64 encoded data.\n\t\tif (/^https?:\\/\\//i.test(data)) return { url: data };\n\t\treturn { data, mimeType: mediaType };\n\t}\n\tif (data instanceof Uint8Array) {\n\t\treturn { data: Buffer.from(data).toString(\"base64\"), mimeType: mediaType };\n\t}\n\treturn undefined;\n}\n\n/**\n * Extract only the final user turn as a Cursor message. Used when resuming a\n * pooled agent that already remembers the prior conversation, so we send just\n * the new message instead of the whole transcript. Returns `undefined` if the\n * last message isn't a user turn (caller should fall back to the full transcript).\n */\nexport function latestUserMessage(\n\tprompt: LanguageModelV3Prompt,\n): SDKUserMessage | undefined {\n\tconst last = prompt[prompt.length - 1];\n\tif (!last || last.role !== \"user\") return undefined;\n\n\tconst text: string[] = [];\n\tconst images: SDKImage[] = [];\n\tfor (const part of last.content) {\n\t\tif (part.type === \"text\") text.push(part.text);\n\t\telse if (part.type === \"file\" && part.mediaType.startsWith(\"image/\")) {\n\t\t\tconst image = fileToImage(part.data, part.mediaType);\n\t\t\tif (image) images.push(image);\n\t\t\ttext.push(\"[image attached]\");\n\t\t}\n\t}\n\n\tconst out: SDKUserMessage = { text: text.join(\"\\n\") };\n\tif (images.length > 0) out.images = images;\n\treturn out;\n}\n","import type {\n\tLanguageModelV3Content,\n\tLanguageModelV3FinishReason,\n\tLanguageModelV3StreamPart,\n\tLanguageModelV3Usage,\n} from \"@ai-sdk/provider\";\nimport type { CursorEvent, CursorUsage } from \"./agent-events.js\";\n\n/**\n * How Cursor's internal tool activity (shell/read/edit/mcp/…) is surfaced to\n * opencode:\n * - `\"blocks\"` (default): emitted as provider-executed AI-SDK\n * `tool-call`/`tool-result` parts so opencode renders structured tool\n * blocks. Requires a V3-native opencode host (1.16+). The parts must carry\n * BOTH `providerExecuted: true` AND `dynamic: true` — ai's `parseToolCall`\n * (v6, `doParseToolCall`) only exempts that combination from registered-tool\n * validation; without `dynamic` an unknown name raises `NoSuchToolError`,\n * which opencode's `experimental_repairToolCall` rewrites into its \"invalid\"\n * tool. Names are also prefixed (`cursor_…`) so they can never collide with\n * a tool opencode has registered (`read`, `grep`, `task`, …) — a colliding\n * name is validated against that tool's input schema instead of being\n * treated as dynamic.\n */\nexport type ToolDisplay = \"reasoning\" | \"blocks\";\n\nconst FINISH_STOP: LanguageModelV3FinishReason = {\n\tunified: \"stop\",\n\traw: undefined,\n};\nconst FINISH_ERROR: LanguageModelV3FinishReason = {\n\tunified: \"error\",\n\traw: undefined,\n};\n\nfunction safeJsonString(input: unknown): string {\n\ttry {\n\t\treturn typeof input === \"string\" ? input : JSON.stringify(input ?? {});\n\t} catch {\n\t\treturn \"{}\";\n\t}\n}\n\n/**\n * Tool name as it crosses into opencode in \"blocks\" mode. Prefixed so it can\n * never collide with a tool opencode has registered, and sanitized because MCP\n * names contain `/` (e.g. `myserver/find_symbol` → `cursor_myserver_find_symbol`).\n */\nfunction blockToolName(name: string): string {\n\treturn `cursor_${name.replace(/[^A-Za-z0-9_-]/g, \"_\")}`;\n}\n\n/**\n * A blocks-mode tool part. `tool-call` / `tool-result` are structurally\n * identical in `LanguageModelV3StreamPart` (streaming) and\n * `LanguageModelV3Content` (`doGenerate`), so the builders below produce one\n * shape that both consumers cast to their respective union.\n */\ntype BlockToolPart = LanguageModelV3StreamPart;\n\n/**\n * Build a provider-executed dynamic `tool-call` under an EXACT (already-final)\n * tool name. `input` is a stringified JSON object per the V3 spec. Both\n * `providerExecuted` and `dynamic` are set so ai's `parseToolCall` accepts the\n * part without registered-tool validation, and a host that hasn't registered\n * the named tool degrades to a generic dynamic block instead of erroring.\n */\nfunction nativeToolCall(\n\tid: string,\n\ttoolName: string,\n\tinput: unknown,\n): BlockToolPart {\n\treturn {\n\t\ttype: \"tool-call\",\n\t\ttoolCallId: id,\n\t\ttoolName,\n\t\tinput: safeJsonString(input),\n\t\tproviderExecuted: true,\n\t\tdynamic: true,\n\t} as BlockToolPart;\n}\n\n/**\n * Build a provider-executed dynamic `tool-result` under an EXACT tool name. Per\n * the V3 spec (and ai v6's `runToolsTransformation`, which reads\n * `chunk.result` / `chunk.isError`) the payload goes in `result`; `result` is\n * typed `NonNullable<JSONValue>` so a missing payload is coalesced to `null`.\n */\nfunction nativeToolResult(\n\tid: string,\n\ttoolName: string,\n\tresult: unknown,\n\tisError: boolean,\n): BlockToolPart {\n\treturn {\n\t\ttype: \"tool-result\",\n\t\ttoolCallId: id,\n\t\ttoolName,\n\t\tresult: (result ?? null) as never,\n\t\tisError,\n\t\tproviderExecuted: true,\n\t\tdynamic: true,\n\t} as BlockToolPart;\n}\n\n/**\n * Build a generic `tool-call` for a Cursor tool with no native opencode\n * counterpart. The name is `cursor_`-prefixed so it can't collide with a tool\n * opencode has registered.\n */\nfunction toolCallObj(id: string, name: string, input: unknown): BlockToolPart {\n\treturn nativeToolCall(id, blockToolName(name), input);\n}\n\n/** Build a generic (`cursor_`-prefixed) `tool-result`. */\nfunction toolResultObj(\n\tid: string,\n\tname: string,\n\tresult: unknown,\n\tisError: boolean,\n): BlockToolPart {\n\treturn nativeToolResult(id, blockToolName(name), result, isError);\n}\n\n/** Cursor's file-edit tool surfaces with this name (its `toolCall.type`). */\nconst EDIT_TOOL_NAME = \"edit\";\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n\treturn typeof v === \"object\" && v !== null;\n}\n\nfunction strField(v: unknown, key: string): string | undefined {\n\treturn isRecord(v) && typeof v[key] === \"string\"\n\t\t? (v[key] as string)\n\t\t: undefined;\n}\n\nfunction numField(v: unknown, key: string): number | undefined {\n\treturn isRecord(v) && typeof v[key] === \"number\"\n\t\t? (v[key] as number)\n\t\t: undefined;\n}\n\n/** Unwrap a Cursor `{ status:\"success\", value }` result to its `value`. */\nfunction successValue(result: unknown): unknown {\n\treturn isRecord(result) && result[\"status\"] === \"success\"\n\t\t? result[\"value\"]\n\t\t: undefined;\n}\n\n/** The `{title, metadata, output}` shape opencode folds into a tool part's state. */\ninterface FoldedResult {\n\ttitle: string;\n\tmetadata: Record<string, unknown>;\n\toutput: string;\n}\n\n/**\n * Maps a Cursor tool's call/result onto an opencode tool so opencode renders\n * something nicer than a raw-JSON block. Mirrors the `edit` mapping: parts are\n * emitted `providerExecuted` + `dynamic`, so the call is never executed on disk\n * and a host that hasn't registered the tool degrades to a generic dynamic\n * block.\n *\n * - `tool` is opencode's REGISTERED tool name when there's a native renderer to\n * reuse (`bash`, `read`, `websearch`, …). Omit it for \"format-only\" adapters\n * (`readLints`, `delete`): the part stays a `cursor_*` block, but `result`\n * still folds the payload into a clean `output` string so opencode's generic\n * renderer shows formatted text instead of dumped JSON.\n * - `input` translates Cursor's arg keys to the names opencode's renderer reads\n * (e.g. Cursor `path` → opencode `filePath`).\n * - `result` folds a SUCCESSFUL Cursor result `value` into `{title, metadata,\n * output}`; returning `null` falls back to a generic block (unexpected shape).\n */\ninterface NativeToolAdapter {\n\ttool?: string;\n\tinput(args: unknown): Record<string, unknown>;\n\tresult(value: unknown, args: unknown): FoldedResult | null;\n}\n\n/**\n * Flatten a Cursor MCP result `value` (`{ content: [{ text: { text } }, …] }`)\n * into plain text. Returns `null` when `value` isn't MCP-shaped, so callers can\n * fall back to the raw payload. An MCP `content` array that holds no text\n * (e.g. only images) yields a `\"[image]\"`-style placeholder rather than `null`.\n */\nfunction flattenMcpContent(value: unknown): string | null {\n\tif (!isRecord(value) || !Array.isArray(value[\"content\"])) return null;\n\tconst parts = (value[\"content\"] as unknown[]).flatMap((item) => {\n\t\tconst text = isRecord(item) ? strField(item[\"text\"], \"text\") : undefined;\n\t\tif (text !== undefined) return [text];\n\t\tif (isRecord(item) && isRecord(item[\"image\"])) return [\"[image]\"];\n\t\treturn [];\n\t});\n\treturn parts.join(\"\\n\");\n}\n\n/**\n * Fold a generic (non-adapter) Cursor result into a clean `output` string IF\n * it's an MCP result; otherwise `null` so the raw payload is passed through.\n * This stops every MCP tool (web search, custom servers, …) from dumping the\n * nested `{content:[{text:{text}}]}` JSON into the block.\n */\nfunction mcpFold(result: unknown): FoldedResult | null {\n\tconst text = flattenMcpContent(successValue(result));\n\tif (text === null) return null;\n\treturn { title: \"\", metadata: {}, output: text };\n}\n\n/** Cursor MCP call args nest the real tool input under `args.args`. */\nfunction mcpInputArgs(args: unknown): unknown {\n\treturn isRecord(args) ? args[\"args\"] : undefined;\n}\n\n/** Map a Cursor MCP `providerIdentifier` to a websearch provider label key. */\nfunction webSearchProvider(args: unknown): string | undefined {\n\tconst id = (strField(args, \"providerIdentifier\") ?? \"\").toLowerCase();\n\tif (id.includes(\"exa\")) return \"exa\";\n\tif (id.includes(\"parallel\")) return \"parallel\";\n\treturn undefined;\n}\n\n/** A Cursor MCP tool whose name looks like a web search (`web_search`, …). */\nfunction isWebSearchName(name: string): boolean {\n\treturn /web[_-]?search/i.test(name);\n}\n\n/**\n * Cursor web search arrives as an MCP tool; map it onto opencode's native\n * `websearch` renderer (query subtitle + result body). The query lives in the\n * nested MCP input (`args.args.query`); the result is MCP `content`.\n */\nconst WEBSEARCH_ADAPTER: NativeToolAdapter = {\n\ttool: \"websearch\",\n\tinput: (args) => {\n\t\tconst query = strField(mcpInputArgs(args), \"query\");\n\t\treturn query !== undefined ? { query } : {};\n\t},\n\tresult: (value, args) => {\n\t\tconst provider = webSearchProvider(args);\n\t\treturn {\n\t\t\ttitle: \"\",\n\t\t\tmetadata: provider ? { provider } : {},\n\t\t\toutput: flattenMcpContent(value) ?? \"\",\n\t\t};\n\t},\n};\n\n/**\n * Resolve a Cursor tool name (+ call args) to an adapter, or `undefined` for a\n * plain `cursor_*` block. `edit` is handled separately (its native call input\n * depends on the diff in the result).\n */\nfunction resolveAdapter(\n\tname: string,\n\t_input: unknown,\n): NativeToolAdapter | undefined {\n\tconst exact = NATIVE_ADAPTERS[name];\n\tif (exact) return exact;\n\tif (isWebSearchName(name)) return WEBSEARCH_ADAPTER;\n\treturn undefined;\n}\n\n/** Cursor todo status (`inProgress`) → opencode todo status (`in_progress`). */\nfunction mapTodoStatus(status: unknown): string {\n\tif (status === \"inProgress\") return \"in_progress\";\n\treturn typeof status === \"string\" ? status : \"pending\";\n}\n\n/** Normalize Cursor `updateTodos` args into opencode `todowrite` todos. */\nfunction mapTodos(args: unknown): Array<{ content: string; status: string }> {\n\tconst todos =\n\t\tisRecord(args) && Array.isArray(args[\"todos\"]) ? args[\"todos\"] : [];\n\treturn (todos as unknown[]).flatMap((t) =>\n\t\tisRecord(t) && typeof t[\"content\"] === \"string\"\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tcontent: t[\"content\"] as string,\n\t\t\t\t\t\tstatus: mapTodoStatus(t[\"status\"]),\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: [],\n\t);\n}\n\n/**\n * Cursor tool name → opencode native tool adapter. Cursor tools without a\n * natural opencode counterpart (`delete`, `mcp`, `semSearch`, `readLints`,\n * `generateImage`, `createPlan`, `recordScreen`, `task`) are intentionally\n * absent and fall through to generic `cursor_*` blocks. `edit` is handled\n * separately (its native call input depends on the diff in the result).\n */\nconst NATIVE_ADAPTERS: Record<string, NativeToolAdapter> = {\n\t// Cursor `shell` → opencode `bash` (console renderer).\n\tshell: {\n\t\ttool: \"bash\",\n\t\tinput: (args) => ({ command: strField(args, \"command\") ?? \"\" }),\n\t\tresult: (value, args) => {\n\t\t\tif (!isRecord(value)) return null;\n\t\t\tconst command = strField(args, \"command\") ?? \"\";\n\t\t\tconst stdout = strField(value, \"stdout\") ?? \"\";\n\t\t\tconst stderr = strField(value, \"stderr\") ?? \"\";\n\t\t\tconst exit = numField(value, \"exitCode\");\n\t\t\tconst body = [stdout, stderr].filter((s) => s.length > 0).join(\"\\n\");\n\t\t\tconst output =\n\t\t\t\texit !== undefined && exit !== 0\n\t\t\t\t\t? `${body}${body ? \"\\n\" : \"\"}(exit ${exit})`\n\t\t\t\t\t: body;\n\t\t\treturn {\n\t\t\t\ttitle: command,\n\t\t\t\tmetadata: { command, output, exit: exit ?? 0 },\n\t\t\t\toutput,\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `read` → opencode `read`.\n\t//\n\t// Cursor's read tool takes ONLY `{ path }` — there is no `offset`/`limit`/\n\t// line-range arg (see `@cursor/sdk` `ReadArgsSchema`, a `{ path: string }`\n\t// \"strip\" object). The model therefore cannot request a sub-range, so two\n\t// same-path reads in a transcript are RE-READS of the same content, never\n\t// \"different sections\". The only per-call detail available is in the result\n\t// `value` (`content`, `totalLines`, `fileSize`); we derive the lines-read\n\t// count from `content` and surface `linesReturned/totalLines` in the title\n\t// so a reader can see how much was read and spot redundant re-reads.\n\tread: {\n\t\ttool: \"read\",\n\t\tinput: (args) => ({ filePath: strField(args, \"path\") ?? \"\" }),\n\t\tresult: (value, args) => {\n\t\t\tconst content = strField(value, \"content\");\n\t\t\tif (content === undefined) return null;\n\t\t\tconst filePath = strField(args, \"path\") ?? \"\";\n\t\t\tconst totalLines = numField(value, \"totalLines\");\n\t\t\tconst fileSize = numField(value, \"fileSize\");\n\t\t\tconst linesReturned = content.split(\"\\n\").length;\n\t\t\tconst lineLabel =\n\t\t\t\ttotalLines !== undefined\n\t\t\t\t\t? `${linesReturned}/${totalLines} lines`\n\t\t\t\t\t: `${linesReturned} lines`;\n\t\t\treturn {\n\t\t\t\ttitle: `${filePath} (${lineLabel})`,\n\t\t\t\tmetadata: {\n\t\t\t\t\tpreview: content.split(\"\\n\").slice(0, 20).join(\"\\n\"),\n\t\t\t\t\tloaded: [] as string[],\n\t\t\t\t\tlinesReturned,\n\t\t\t\t\t...(totalLines !== undefined ? { totalLines } : {}),\n\t\t\t\t\t...(fileSize !== undefined ? { fileSize } : {}),\n\t\t\t\t},\n\t\t\t\toutput: content,\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `write` → opencode `write` (renders input.content as the new file).\n\twrite: {\n\t\ttool: \"write\",\n\t\tinput: (args) => ({\n\t\t\tfilePath: strField(args, \"path\") ?? \"\",\n\t\t\tcontent: strField(args, \"fileText\") ?? \"\",\n\t\t}),\n\t\tresult: (value, args) => {\n\t\t\tconst filePath = strField(args, \"path\") ?? \"\";\n\t\t\tconst lines = numField(value, \"linesCreated\");\n\t\t\tconst output =\n\t\t\t\tlines !== undefined\n\t\t\t\t\t? `Wrote ${lines} line${lines === 1 ? \"\" : \"s\"}.`\n\t\t\t\t\t: \"Wrote file successfully.\";\n\t\t\treturn {\n\t\t\t\ttitle: filePath,\n\t\t\t\tmetadata: { diagnostics: {}, filepath: filePath, exists: false },\n\t\t\t\toutput,\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `glob` → opencode `glob`.\n\tglob: {\n\t\ttool: \"glob\",\n\t\tinput: (args) => {\n\t\t\tconst pattern = strField(args, \"globPattern\") ?? \"\";\n\t\t\tconst dir = strField(args, \"targetDirectory\");\n\t\t\treturn dir ? { pattern, path: dir } : { pattern };\n\t\t},\n\t\tresult: (value, args) => {\n\t\t\tif (!isRecord(value) || !Array.isArray(value[\"files\"])) return null;\n\t\t\tconst files = (value[\"files\"] as unknown[]).filter(\n\t\t\t\t(f): f is string => typeof f === \"string\",\n\t\t\t);\n\t\t\tconst truncated =\n\t\t\t\tvalue[\"clientTruncated\"] === true || value[\"ripgrepTruncated\"] === true;\n\t\t\tconst pattern = strField(args, \"globPattern\") ?? \"\";\n\t\t\tconst dir = strField(args, \"targetDirectory\");\n\t\t\treturn {\n\t\t\t\ttitle: dir ? `${pattern} in ${dir}` : pattern,\n\t\t\t\tmetadata: { count: files.length, truncated },\n\t\t\t\toutput: files.length > 0 ? files.join(\"\\n\") : \"No files found\",\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `grep` → opencode `grep` (flatten matches into ripgrep-style text).\n\tgrep: {\n\t\ttool: \"grep\",\n\t\tinput: (args) => {\n\t\t\tconst out: Record<string, unknown> = {\n\t\t\t\tpattern: strField(args, \"pattern\") ?? \"\",\n\t\t\t};\n\t\t\tconst p = strField(args, \"path\");\n\t\t\tif (p) out[\"path\"] = p;\n\t\t\tconst g = strField(args, \"glob\");\n\t\t\tif (g) out[\"include\"] = g;\n\t\t\treturn out;\n\t\t},\n\t\tresult: (value, args) => {\n\t\t\tif (!isRecord(value)) return null;\n\t\t\tconst unions: unknown[] = [];\n\t\t\tconst ws = value[\"workspaceResults\"];\n\t\t\tif (isRecord(ws)) unions.push(...Object.values(ws));\n\t\t\tif (value[\"activeEditorResult\"] !== undefined)\n\t\t\t\tunions.push(value[\"activeEditorResult\"]);\n\t\t\tconst lines: string[] = [];\n\t\t\tlet total = 0;\n\t\t\tlet current = \"\";\n\t\t\tfor (const u of unions) {\n\t\t\t\tif (!isRecord(u)) continue;\n\t\t\t\tconst output = u[\"output\"];\n\t\t\t\tif (\n\t\t\t\t\tu[\"type\"] === \"content\" &&\n\t\t\t\t\tisRecord(output) &&\n\t\t\t\t\tArray.isArray(output[\"matches\"])\n\t\t\t\t) {\n\t\t\t\t\tfor (const m of output[\"matches\"] as unknown[]) {\n\t\t\t\t\t\tif (!isRecord(m)) continue;\n\t\t\t\t\t\tconst file = strField(m, \"file\") ?? \"\";\n\t\t\t\t\t\tconst line = numField(m, \"lineNumber\");\n\t\t\t\t\t\tconst text = strField(m, \"line\") ?? \"\";\n\t\t\t\t\t\tif (current !== file) {\n\t\t\t\t\t\t\tif (current) lines.push(\"\");\n\t\t\t\t\t\t\tcurrent = file;\n\t\t\t\t\t\t\tlines.push(`${file}:`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\tline !== undefined ? ` Line ${line}: ${text}` : ` ${text}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\ttotal++;\n\t\t\t\t\t}\n\t\t\t\t} else if (\n\t\t\t\t\tu[\"type\"] === \"files\" &&\n\t\t\t\t\tisRecord(output) &&\n\t\t\t\t\tArray.isArray(output[\"files\"])\n\t\t\t\t) {\n\t\t\t\t\tfor (const f of output[\"files\"] as unknown[]) {\n\t\t\t\t\t\tif (typeof f === \"string\") {\n\t\t\t\t\t\t\tlines.push(f);\n\t\t\t\t\t\t\ttotal++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst pattern = strField(args, \"pattern\") ?? \"\";\n\t\t\tconst glob = strField(args, \"glob\");\n\t\t\tconst path = strField(args, \"path\");\n\t\t\tlet title = pattern;\n\t\t\tif (glob) title += ` (${glob})`;\n\t\t\tif (path) title += ` in ${path}`;\n\t\t\treturn {\n\t\t\t\ttitle,\n\t\t\t\tmetadata: { matches: total, truncated: false },\n\t\t\t\toutput:\n\t\t\t\t\ttotal > 0\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t`Found ${total} match${total === 1 ? \"\" : \"es\"}`,\n\t\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\t\t...lines,\n\t\t\t\t\t\t\t].join(\"\\n\")\n\t\t\t\t\t\t: \"No matches found\",\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `ls` → opencode `list` (flatten the directory tree into paths).\n\tls: {\n\t\ttool: \"list\",\n\t\tinput: (args) => ({ path: strField(args, \"path\") ?? \"\" }),\n\t\tresult: (value) => {\n\t\t\tif (!isRecord(value)) return null;\n\t\t\tconst root = value[\"directoryTreeRoot\"];\n\t\t\tif (!isRecord(root)) return null;\n\t\t\tconst out: string[] = [];\n\t\t\tconst walk = (node: Record<string, unknown>) => {\n\t\t\t\tconst base = strField(node, \"absPath\") ?? \"\";\n\t\t\t\tconst files = Array.isArray(node[\"childrenFiles\"])\n\t\t\t\t\t? node[\"childrenFiles\"]\n\t\t\t\t\t: [];\n\t\t\t\tfor (const f of files) {\n\t\t\t\t\tconst name = strField(f, \"name\");\n\t\t\t\t\tif (name) out.push(`${base}/${name}`);\n\t\t\t\t}\n\t\t\t\tconst dirs = Array.isArray(node[\"childrenDirs\"])\n\t\t\t\t\t? node[\"childrenDirs\"]\n\t\t\t\t\t: [];\n\t\t\t\tfor (const d of dirs) {\n\t\t\t\t\tif (!isRecord(d)) continue;\n\t\t\t\t\tout.push(`${strField(d, \"absPath\") ?? \"\"}/`);\n\t\t\t\t\twalk(d);\n\t\t\t\t}\n\t\t\t};\n\t\t\twalk(root);\n\t\t\treturn {\n\t\t\t\ttitle: strField(root, \"absPath\") ?? \"\",\n\t\t\t\tmetadata: {},\n\t\t\t\toutput: out.length > 0 ? out.join(\"\\n\") : \"(empty)\",\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `updateTodos` → opencode `todowrite` (todo checklist renderer).\n\tupdateTodos: {\n\t\ttool: \"todowrite\",\n\t\tinput: (args) => ({ todos: mapTodos(args) }),\n\t\tresult: (_value, args) => {\n\t\t\tconst todos = mapTodos(args);\n\t\t\tconst done = todos.filter((t) => t.status === \"completed\").length;\n\t\t\treturn {\n\t\t\t\ttitle: `${done}/${todos.length}`,\n\t\t\t\tmetadata: { todos },\n\t\t\t\toutput: `Updated ${todos.length} todo${todos.length === 1 ? \"\" : \"s\"}.`,\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `task` (subagent) → opencode `task` (agent card: name + description).\n\t// Non-clickable here — the subagent ran inside Cursor, not as an opencode\n\t// child session — but the native card reads far better than raw JSON.\n\ttask: {\n\t\ttool: \"task\",\n\t\tinput: (args) => {\n\t\t\tconst description = strField(args, \"description\") ?? \"\";\n\t\t\tconst sub = isRecord(args) ? args[\"subagentType\"] : undefined;\n\t\t\tconst subagent =\n\t\t\t\tstrField(sub, \"name\") ?? strField(sub, \"kind\") ?? undefined;\n\t\t\treturn subagent\n\t\t\t\t? { description, subagent_type: subagent }\n\t\t\t\t: { description };\n\t\t},\n\t\tresult: (value, args) => {\n\t\t\tconst description = strField(args, \"description\") ?? \"\";\n\t\t\tconst suffix = strField(value, \"resultSuffix\");\n\t\t\tconst background = isRecord(value) && value[\"isBackground\"] === true;\n\t\t\treturn {\n\t\t\t\ttitle: description,\n\t\t\t\tmetadata: background ? { background: true } : {},\n\t\t\t\toutput: suffix ?? \"Subagent task completed.\",\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `readLints` has no opencode counterpart — format-only: render the\n\t// diagnostics as a readable list instead of dumping the nested JSON.\n\treadLints: {\n\t\tinput: (args) => {\n\t\t\tconst paths =\n\t\t\t\tisRecord(args) && Array.isArray(args[\"paths\"]) ? args[\"paths\"] : [];\n\t\t\treturn { paths };\n\t\t},\n\t\tresult: (value) => {\n\t\t\tconst files =\n\t\t\t\tisRecord(value) && Array.isArray(value[\"fileDiagnostics\"])\n\t\t\t\t\t? (value[\"fileDiagnostics\"] as unknown[])\n\t\t\t\t\t: [];\n\t\t\tconst lines: string[] = [];\n\t\t\tlet total = 0;\n\t\t\tfor (const file of files) {\n\t\t\t\tif (!isRecord(file)) continue;\n\t\t\t\tconst diags = Array.isArray(file[\"diagnostics\"])\n\t\t\t\t\t? (file[\"diagnostics\"] as unknown[])\n\t\t\t\t\t: [];\n\t\t\t\tif (diags.length === 0) continue;\n\t\t\t\tlines.push(`${strField(file, \"path\") ?? \"\"}`);\n\t\t\t\tfor (const d of diags) {\n\t\t\t\t\tif (!isRecord(d)) continue;\n\t\t\t\t\tconst severity = strField(d, \"severity\") ?? \"info\";\n\t\t\t\t\tconst start = isRecord(d[\"range\"]) ? d[\"range\"][\"start\"] : undefined;\n\t\t\t\t\tconst line = numField(start, \"line\");\n\t\t\t\t\tconst char = numField(start, \"character\");\n\t\t\t\t\tconst loc =\n\t\t\t\t\t\tline !== undefined\n\t\t\t\t\t\t\t? ` L${line + 1}${char !== undefined ? `:${char + 1}` : \"\"}`\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\tlines.push(` ${severity}${loc}: ${strField(d, \"message\") ?? \"\"}`);\n\t\t\t\t\ttotal++;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\ttitle:\n\t\t\t\t\ttotal > 0\n\t\t\t\t\t\t? `${total} problem${total === 1 ? \"\" : \"s\"}`\n\t\t\t\t\t\t: \"No problems\",\n\t\t\t\tmetadata: { count: total },\n\t\t\t\toutput: total > 0 ? lines.join(\"\\n\") : \"No problems found.\",\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `delete` has no opencode counterpart — format-only: a one-line\n\t// confirmation instead of `{\"fileSize\":N}`.\n\tdelete: {\n\t\tinput: (args) => ({ path: strField(args, \"path\") ?? \"\" }),\n\t\tresult: (value, args) => {\n\t\t\tconst path = strField(args, \"path\") ?? \"\";\n\t\t\tconst size = numField(value, \"fileSize\");\n\t\t\treturn {\n\t\t\t\ttitle: path,\n\t\t\t\tmetadata: {},\n\t\t\t\toutput:\n\t\t\t\t\tsize !== undefined\n\t\t\t\t\t\t? `Deleted ${path} (${size} bytes).`\n\t\t\t\t\t\t: `Deleted ${path}.`,\n\t\t\t};\n\t\t},\n\t},\n};\n\n/** Extract the edit target path from Cursor's edit tool-call args (`{ path }`). */\nfunction editFilePath(input: unknown): string {\n\treturn isRecord(input) && typeof input[\"path\"] === \"string\"\n\t\t? input[\"path\"]\n\t\t: \"\";\n}\n\n/**\n * If `result` is a successful Cursor edit result carrying a unified diff, return\n * its `diffString`; otherwise null (caller falls back to a safe generic block).\n * Cursor shape: `{ status:\"success\", value:{ diffString?, linesAdded?, linesRemoved? } }`.\n */\nfunction editDiffString(result: unknown): string | null {\n\tif (!isRecord(result) || result[\"status\"] !== \"success\") return null;\n\tconst value = result[\"value\"];\n\tif (!isRecord(value)) return null;\n\tconst diff = value[\"diffString\"];\n\treturn typeof diff === \"string\" && diff.length > 0 ? diff : null;\n}\n\n/**\n * Reconstruct opencode `edit` `{oldString,newString}` from a unified diff:\n * removed (`-`) lines → oldString, added (`+`) lines → newString (file/hunk\n * headers skipped). Faithful for a single hunk; approximate (concatenated)\n * across multiple hunks. Used only to satisfy opencode's edit input schema — the\n * call is provider-executed, so these strings are never applied to disk; the\n * rendered diff comes from `metadata.diff`.\n */\nfunction reconstructEditStrings(diff: string): {\n\toldString: string;\n\tnewString: string;\n} {\n\tconst oldLines: string[] = [];\n\tconst newLines: string[] = [];\n\tfor (const line of diff.split(\"\\n\")) {\n\t\tif (\n\t\t\tline.startsWith(\"---\") ||\n\t\t\tline.startsWith(\"+++\") ||\n\t\t\tline.startsWith(\"@@\") ||\n\t\t\tline.startsWith(\"Index:\") ||\n\t\t\tline.startsWith(\"===\")\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (line.startsWith(\"-\")) oldLines.push(line.slice(1));\n\t\telse if (line.startsWith(\"+\")) newLines.push(line.slice(1));\n\t}\n\treturn { oldString: oldLines.join(\"\\n\"), newString: newLines.join(\"\\n\") };\n}\n\n/**\n * Build the opencode-native `edit` `tool-call` payload for a completed Cursor\n * edit. Emitted under the registered name `edit` (so opencode's diff viewer\n * renders) with a schema-valid `{filePath, oldString, newString}` input. Still\n * carries `providerExecuted` + `dynamic` so a host without a registered `edit`\n * tool degrades to a dynamic generic block instead of erroring.\n */\nfunction editCallFields(\n\tid: string,\n\tfilePath: string,\n\tdiff: string,\n): BlockToolPart {\n\tconst { oldString, newString } = reconstructEditStrings(diff);\n\treturn {\n\t\ttype: \"tool-call\",\n\t\ttoolCallId: id,\n\t\ttoolName: EDIT_TOOL_NAME,\n\t\tinput: safeJsonString({ filePath, oldString, newString }),\n\t\tproviderExecuted: true,\n\t\tdynamic: true,\n\t} as BlockToolPart;\n}\n\n/**\n * Build the opencode-native `edit` `tool-result` payload. opencode's processor\n * folds a tool-result's payload into `state.{title,metadata,output}`; the Edit\n * renderer's diff viewer keys on `metadata.diff`.\n */\nfunction editResultFields(\n\tid: string,\n\tfilePath: string,\n\tdiff: string,\n\tresult: unknown,\n): BlockToolPart {\n\tconst value = isRecord(result) ? result[\"value\"] : undefined;\n\tconst added =\n\t\tisRecord(value) && typeof value[\"linesAdded\"] === \"number\"\n\t\t\t? value[\"linesAdded\"]\n\t\t\t: undefined;\n\tconst removed =\n\t\tisRecord(value) && typeof value[\"linesRemoved\"] === \"number\"\n\t\t\t? value[\"linesRemoved\"]\n\t\t\t: undefined;\n\tconst counts =\n\t\tadded !== undefined || removed !== undefined\n\t\t\t? ` (+${added ?? 0}/-${removed ?? 0})`\n\t\t\t: \"\";\n\treturn {\n\t\ttype: \"tool-result\" as const,\n\t\ttoolCallId: id,\n\t\ttoolName: EDIT_TOOL_NAME,\n\t\tresult: {\n\t\t\ttitle: filePath,\n\t\t\tmetadata: { diff, diagnostics: {} },\n\t\t\toutput: `Edit applied${counts}.`,\n\t\t} as never,\n\t\tisError: false,\n\t\tproviderExecuted: true,\n\t\tdynamic: true,\n\t} as BlockToolPart;\n}\n\n/**\n * Per-turn blocks-mode tool bookkeeping, shared by the streaming and\n * `doGenerate` paths:\n * - `open`: non-edit calls awaiting their result. Stores the FINAL emitted tool\n * name (native, e.g. `bash`, or generic `cursor_*`), the native adapter (if\n * any) used to fold the result, and the original Cursor args (some adapters\n * build their result from the call args, e.g. `write`/`updateTodos`).\n * - `pendingEdits`: edit calls held until their result, which carries the diff\n * needed to emit a schema-valid native `edit` call (id → filePath).\n */\ninterface OpenToolCall {\n\ttoolName: string;\n\tadapter?: NativeToolAdapter;\n\targs: unknown;\n}\n\ninterface BlockToolState {\n\topen: Map<string, OpenToolCall>;\n\tpendingEdits: Map<string, string>;\n}\n\nfunction newBlockToolState(): BlockToolState {\n\treturn { open: new Map(), pendingEdits: new Map() };\n}\n\n/** Parts to emit for a blocks-mode `tool-call` event (edits are buffered). */\nfunction blockToolCallParts(\n\tid: string,\n\tname: string,\n\tinput: unknown,\n\tstate: BlockToolState,\n): BlockToolPart[] {\n\tif (name === EDIT_TOOL_NAME) {\n\t\t// Hold the edit call until its result (which carries the diff).\n\t\tstate.pendingEdits.set(id, editFilePath(input));\n\t\treturn [];\n\t}\n\tconst adapter = resolveAdapter(name, input);\n\tif (adapter) {\n\t\t// Adapter mapping: native registered tool (`adapter.tool`) when there's a\n\t\t// renderer to reuse, else a `cursor_*` block whose result is still folded\n\t\t// into clean output (format-only adapters).\n\t\tconst toolName = adapter.tool ?? blockToolName(name);\n\t\tstate.open.set(id, { toolName, adapter, args: input });\n\t\treturn [nativeToolCall(id, toolName, adapter.input(input))];\n\t}\n\t// No adapter: generic prefixed block.\n\tconst toolName = blockToolName(name);\n\tstate.open.set(id, { toolName, args: input });\n\treturn [nativeToolCall(id, toolName, input)];\n}\n\n/** Parts to emit for a blocks-mode `tool-result` event. */\nfunction blockToolResultParts(\n\tid: string,\n\tname: string,\n\tresult: unknown,\n\tisError: boolean,\n\tstate: BlockToolState,\n): BlockToolPart[] {\n\tif (state.pendingEdits.has(id)) {\n\t\tconst filePath = state.pendingEdits.get(id)!;\n\t\tstate.pendingEdits.delete(id);\n\t\tconst diff = isError ? null : editDiffString(result);\n\t\tif (diff && filePath) {\n\t\t\t// Native edit: opencode renders its built-in diff viewer.\n\t\t\treturn [\n\t\t\t\teditCallFields(id, filePath, diff),\n\t\t\t\teditResultFields(id, filePath, diff, result),\n\t\t\t];\n\t\t}\n\t\t// No usable diff (error / unexpected shape): safe generic fallback.\n\t\treturn [\n\t\t\ttoolCallObj(id, EDIT_TOOL_NAME, { path: filePath }),\n\t\t\ttoolResultObj(id, EDIT_TOOL_NAME, result, isError),\n\t\t];\n\t}\n\tconst open = state.open.get(id);\n\tstate.open.delete(id);\n\tconst toolName = open?.toolName ?? blockToolName(name);\n\tif (open?.adapter && !isError) {\n\t\tconst value = successValue(result);\n\t\tif (value !== undefined) {\n\t\t\tconst folded = open.adapter.result(value, open.args);\n\t\t\tif (folded) return [nativeToolResult(id, toolName, folded, false)];\n\t\t}\n\t}\n\tif (!isError) {\n\t\t// No adapter (or it declined): if this is an MCP result, fold its `content`\n\t\t// into readable text so the block isn't a raw JSON dump.\n\t\tconst folded = mcpFold(result);\n\t\tif (folded) return [nativeToolResult(id, toolName, folded, false)];\n\t}\n\t// Generic block / error / unexpected shape: emit the raw Cursor result under\n\t// the (already-resolved) tool name.\n\treturn [nativeToolResult(id, toolName, result, isError)];\n}\n\n/**\n * Parts that close out any tool call whose completion never arrived (run\n * errored/cancelled mid-tool) so blocks never dangle as \"Tool execution\n * aborted\". Clears the state.\n */\nfunction blockDanglingParts(state: BlockToolState): BlockToolPart[] {\n\tconst parts: BlockToolPart[] = [];\n\tfor (const [id, open] of state.open) {\n\t\tparts.push(nativeToolResult(id, open.toolName, DANGLING_TOOL_RESULT, true));\n\t}\n\tstate.open.clear();\n\t// Edits whose result never arrived: safe generic block + synthetic error\n\t// (no diff available to build a native edit).\n\tfor (const [id, filePath] of state.pendingEdits) {\n\t\tparts.push(toolCallObj(id, EDIT_TOOL_NAME, { path: filePath }));\n\t\tparts.push(toolResultObj(id, EDIT_TOOL_NAME, DANGLING_TOOL_RESULT, true));\n\t}\n\tstate.pendingEdits.clear();\n\treturn parts;\n}\n\nexport const EMPTY_USAGE: LanguageModelV3Usage = {\n\tinputTokens: {\n\t\ttotal: undefined,\n\t\tnoCache: undefined,\n\t\tcacheRead: undefined,\n\t\tcacheWrite: undefined,\n\t},\n\toutputTokens: { total: undefined, text: undefined, reasoning: undefined },\n};\n\nexport function mapUsage(usage: CursorUsage): LanguageModelV3Usage {\n\treturn {\n\t\tinputTokens: {\n\t\t\ttotal: usage.inputTokens,\n\t\t\tnoCache: undefined,\n\t\t\tcacheRead: usage.cacheReadTokens,\n\t\t\tcacheWrite: usage.cacheWriteTokens,\n\t\t},\n\t\toutputTokens: {\n\t\t\ttotal: usage.outputTokens,\n\t\t\ttext: undefined,\n\t\t\treasoning: undefined,\n\t\t},\n\t};\n}\n\n/**\n * Render Cursor's internal tool activity as a short, human-readable line.\n *\n * Cursor runs its own agent loop and executes its own tools (shell/read/edit/\n * mcp/…). We surface that activity as reasoning text — NOT as AI-SDK\n * `tool-call`/`tool-result` parts. opencode (a V3-native host) only treats\n * registered tools as callable; a provider-executed call naming a tool it\n * doesn't know (e.g. `mcp`, `shell`) is rejected as an \"unavailable tool\".\n * Rendering as reasoning keeps the activity visible without crossing the\n * tool-execution boundary. Tool outputs can be huge (file contents, search\n * dumps), so only the call (name + short arg summary) and error status are\n * shown — never the raw result.\n */\nfunction formatToolCall(name: string, input: unknown): string {\n\tlet arg = \"\";\n\ttry {\n\t\tconst s = typeof input === \"string\" ? input : JSON.stringify(input);\n\t\tif (s && s !== \"{}\" && s !== '\"\"')\n\t\t\targ = ` ${s.length > 120 ? `${s.slice(0, 120)}…` : s}`;\n\t} catch {\n\t\t// Non-serializable input; show the name only.\n\t}\n\treturn `[tool] ${name}${arg}`;\n}\n\n/**\n * Synthetic error payload for a tool call whose completion never arrived\n * (run errored/cancelled/wedged mid-tool). Mirrors Cursor's own\n * `{status:\"error\"}` result union so consumers see a consistent shape.\n * Without a matching result, opencode renders the part as\n * \"Tool execution aborted\" and the block dangles forever.\n */\nconst DANGLING_TOOL_RESULT = {\n\tstatus: \"error\",\n\terror: \"Cursor run ended before this tool call completed.\",\n};\n\n/**\n * Translate the normalized Cursor agent events into an AI-SDK V3 stream.\n *\n * Pure with respect to the event source, so it can be tested by feeding a\n * fixed event sequence (no live agent required). Reasoning blocks are closed\n * before text begins so reasoning/text parts nest cleanly. Tool activity is\n * surfaced per {@link ToolDisplay} (default `\"blocks\"`): structured tool parts,\n * or reasoning lines when `\"reasoning\"` (see {@link formatToolCall}).\n */\nexport function cursorEventsToStream(\n\tevents: AsyncIterable<CursorEvent>,\n\ttoolDisplay: ToolDisplay = \"blocks\",\n): ReadableStream<LanguageModelV3StreamPart> {\n\treturn new ReadableStream<LanguageModelV3StreamPart>({\n\t\tasync start(controller) {\n\t\t\tcontroller.enqueue({ type: \"stream-start\", warnings: [] });\n\n\t\t\tlet textId: string | undefined;\n\t\t\tlet textCount = 0;\n\t\t\tlet reasoningId: string | undefined;\n\t\t\tlet reasoningCount = 0;\n\t\t\tlet usage: LanguageModelV3Usage | undefined;\n\t\t\tlet streamedText = false;\n\t\t\t// Blocks-mode tool bookkeeping (open non-edit calls + buffered edits).\n\t\t\tconst toolState = newBlockToolState();\n\t\t\tconst closeDanglingToolCalls = () => {\n\t\t\t\tfor (const part of blockDanglingParts(toolState)) {\n\t\t\t\t\tcontroller.enqueue(part);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst closeReasoning = () => {\n\t\t\t\tif (reasoningId) {\n\t\t\t\t\tcontroller.enqueue({ type: \"reasoning-end\", id: reasoningId });\n\t\t\t\t\treasoningId = undefined;\n\t\t\t\t}\n\t\t\t};\n\t\t\t// Close the open text part when reasoning resumes: hosts position a part\n\t\t\t// where it STARTED, so appending later text to an earlier part would\n\t\t\t// render the final answer above the reasoning that preceded it.\n\t\t\tconst closeText = () => {\n\t\t\t\tif (textId) {\n\t\t\t\t\tcontroller.enqueue({ type: \"text-end\", id: textId });\n\t\t\t\t\ttextId = undefined;\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst ensureText = () => {\n\t\t\t\tcloseReasoning();\n\t\t\t\tif (!textId) {\n\t\t\t\t\ttextId = `text-${textCount++}`;\n\t\t\t\t\tcontroller.enqueue({ type: \"text-start\", id: textId });\n\t\t\t\t}\n\t\t\t\treturn textId;\n\t\t\t};\n\t\t\tconst ensureReasoning = () => {\n\t\t\t\tcloseText();\n\t\t\t\tif (!reasoningId) {\n\t\t\t\t\treasoningId = `reasoning-${reasoningCount++}`;\n\t\t\t\t\tcontroller.enqueue({ type: \"reasoning-start\", id: reasoningId });\n\t\t\t\t}\n\t\t\t\treturn reasoningId;\n\t\t\t};\n\t\t\tconst reasoningLine = (text: string) => {\n\t\t\t\tcontroller.enqueue({\n\t\t\t\t\ttype: \"reasoning-delta\",\n\t\t\t\t\tid: ensureReasoning(),\n\t\t\t\t\tdelta: text,\n\t\t\t\t});\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tfor await (const event of events) {\n\t\t\t\t\tswitch (event.type) {\n\t\t\t\t\t\tcase \"text-delta\":\n\t\t\t\t\t\t\tstreamedText = true;\n\t\t\t\t\t\t\tcontroller.enqueue({\n\t\t\t\t\t\t\t\ttype: \"text-delta\",\n\t\t\t\t\t\t\t\tid: ensureText(),\n\t\t\t\t\t\t\t\tdelta: event.text,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"reasoning-delta\":\n\t\t\t\t\t\t\treasoningLine(event.text);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"tool-call\":\n\t\t\t\t\t\t\tif (toolDisplay === \"blocks\") {\n\t\t\t\t\t\t\t\tconst parts = blockToolCallParts(\n\t\t\t\t\t\t\t\t\tevent.id,\n\t\t\t\t\t\t\t\t\tevent.name,\n\t\t\t\t\t\t\t\t\tevent.input,\n\t\t\t\t\t\t\t\t\ttoolState,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t// Edit calls buffer until their result (no parts yet) — keep\n\t\t\t\t\t\t\t\t// the open narration part alive across the gap. Every other\n\t\t\t\t\t\t\t\t// tool emits immediately, so close open text/reasoning first\n\t\t\t\t\t\t\t\t// so post-tool narration lands in a later part (hosts position\n\t\t\t\t\t\t\t\t// parts where they START).\n\t\t\t\t\t\t\t\tif (parts.length > 0) {\n\t\t\t\t\t\t\t\t\tcloseText();\n\t\t\t\t\t\t\t\t\tcloseReasoning();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfor (const part of parts) {\n\t\t\t\t\t\t\t\t\tcontroller.enqueue(part);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\treasoningLine(`\\n${formatToolCall(event.name, event.input)}\\n`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"tool-result\":\n\t\t\t\t\t\t\tif (toolDisplay === \"blocks\") {\n\t\t\t\t\t\t\t\tconst parts = blockToolResultParts(\n\t\t\t\t\t\t\t\t\tevent.id,\n\t\t\t\t\t\t\t\t\tevent.name,\n\t\t\t\t\t\t\t\t\tevent.result,\n\t\t\t\t\t\t\t\t\tevent.isError,\n\t\t\t\t\t\t\t\t\ttoolState,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tif (parts.length > 0) {\n\t\t\t\t\t\t\t\t\tcloseText();\n\t\t\t\t\t\t\t\t\tcloseReasoning();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfor (const part of parts) {\n\t\t\t\t\t\t\t\t\tcontroller.enqueue(part);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (event.isError) {\n\t\t\t\t\t\t\t\treasoningLine(`[tool] ${event.name} failed\\n`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"usage\":\n\t\t\t\t\t\t\tusage = mapUsage(event.usage);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"finish\":\n\t\t\t\t\t\t\tif (!streamedText && event.text) {\n\t\t\t\t\t\t\t\tcontroller.enqueue({\n\t\t\t\t\t\t\t\t\ttype: \"text-delta\",\n\t\t\t\t\t\t\t\t\tid: ensureText(),\n\t\t\t\t\t\t\t\t\tdelta: event.text,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcloseDanglingToolCalls();\n\t\t\t\tcloseReasoning();\n\t\t\t\tcloseText();\n\t\t\t\tcontroller.enqueue({\n\t\t\t\t\ttype: \"finish\",\n\t\t\t\t\tusage: usage ?? EMPTY_USAGE,\n\t\t\t\t\tfinishReason: FINISH_STOP,\n\t\t\t\t});\n\t\t\t\tcontroller.close();\n\t\t\t} catch (err) {\n\t\t\t\tcontroller.enqueue({ type: \"error\", error: err });\n\t\t\t\tcloseDanglingToolCalls();\n\t\t\t\tcloseReasoning();\n\t\t\t\tcloseText();\n\t\t\t\tcontroller.enqueue({\n\t\t\t\t\ttype: \"finish\",\n\t\t\t\t\tusage: usage ?? EMPTY_USAGE,\n\t\t\t\t\tfinishReason: FINISH_ERROR,\n\t\t\t\t});\n\t\t\t\tcontroller.close();\n\t\t\t}\n\t\t},\n\t});\n}\n\n/**\n * Aggregate the normalized Cursor agent events into a non-streaming result for\n * `doGenerate`. Same event source contract as {@link cursorEventsToStream}\n * (consumed via `for await`). Tool activity is surfaced per {@link ToolDisplay}\n * (default `\"blocks\"`): structured tool parts (see {@link blockToolCallParts} /\n * {@link blockToolResultParts}), or folded into the reasoning text when\n * `\"reasoning\"`.\n */\nexport async function cursorEventsToContent(\n\tevents: AsyncIterable<CursorEvent>,\n\ttoolDisplay: ToolDisplay = \"blocks\",\n): Promise<{\n\tcontent: Array<LanguageModelV3Content>;\n\tfinishReason: LanguageModelV3FinishReason;\n\tusage: LanguageModelV3Usage;\n}> {\n\tconst content: Array<LanguageModelV3Content> = [];\n\tconst toolParts: Array<LanguageModelV3Content> = [];\n\t// Blocks-mode tool bookkeeping (open non-edit calls + buffered edits).\n\tconst toolState = newBlockToolState();\n\tlet text = \"\";\n\tlet reasoning = \"\";\n\tlet usage: LanguageModelV3Usage = EMPTY_USAGE;\n\tlet finishReason: LanguageModelV3FinishReason = FINISH_STOP;\n\n\ttry {\n\t\tfor await (const event of events) {\n\t\t\tswitch (event.type) {\n\t\t\t\tcase \"text-delta\":\n\t\t\t\t\ttext += event.text;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"reasoning-delta\":\n\t\t\t\t\treasoning += event.text;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tool-call\":\n\t\t\t\t\tif (toolDisplay === \"blocks\") {\n\t\t\t\t\t\tfor (const part of blockToolCallParts(\n\t\t\t\t\t\t\tevent.id,\n\t\t\t\t\t\t\tevent.name,\n\t\t\t\t\t\t\tevent.input,\n\t\t\t\t\t\t\ttoolState,\n\t\t\t\t\t\t)) {\n\t\t\t\t\t\t\ttoolParts.push(part as LanguageModelV3Content);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treasoning += `\\n${formatToolCall(event.name, event.input)}\\n`;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tool-result\":\n\t\t\t\t\tif (toolDisplay === \"blocks\") {\n\t\t\t\t\t\tfor (const part of blockToolResultParts(\n\t\t\t\t\t\t\tevent.id,\n\t\t\t\t\t\t\tevent.name,\n\t\t\t\t\t\t\tevent.result,\n\t\t\t\t\t\t\tevent.isError,\n\t\t\t\t\t\t\ttoolState,\n\t\t\t\t\t\t)) {\n\t\t\t\t\t\t\ttoolParts.push(part as LanguageModelV3Content);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (event.isError) {\n\t\t\t\t\t\treasoning += `[tool] ${event.name} failed\\n`;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"usage\":\n\t\t\t\t\tusage = mapUsage(event.usage);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"finish\":\n\t\t\t\t\tif (!text && event.text) text = event.text;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\tfinishReason = FINISH_ERROR;\n\t}\n\n\t// Close out any tool call whose completion never arrived (see DANGLING_TOOL_RESULT).\n\tfor (const part of blockDanglingParts(toolState)) {\n\t\ttoolParts.push(part as LanguageModelV3Content);\n\t}\n\n\tif (reasoning) content.push({ type: \"reasoning\", text: reasoning });\n\tcontent.push(...toolParts);\n\tif (text) content.push({ type: \"text\", text });\n\n\treturn { content, finishReason, usage };\n}\n","import { createHash } from \"node:crypto\";\nimport type { LanguageModelV3Prompt } from \"@ai-sdk/provider\";\nimport type { McpServerConfig } from \"@cursor/sdk\";\n\n/**\n * Per-session bookkeeping that lets the provider decide, on each turn, whether\n * it can safely resume the pooled Cursor agent (and send only the new message)\n * or must start fresh and re-send the whole transcript.\n *\n * We hash ONLY the parts opencode replays verbatim — the system prompt and the\n * user messages. We deliberately do NOT hash assistant output: opencode\n * re-serializes our streamed reply (reasoning, tool blocks) back into the next\n * prompt in a shape we can't predict byte-for-byte, so hashing it would\n * spuriously mismatch every turn and silently collapse to always-full-replay.\n * The system prompt + user-message sequence is the stable identity of a\n * conversation.\n */\nexport interface TranscriptRecord {\n\t/** Cursor agentId currently pooled for the session. */\n\tagentId: string;\n\t/** Hash of the concatenated system message content. */\n\tsystemHash: string;\n\t/** Ordered hash per user message (text + a stable image token). */\n\tuserHashes: string[];\n\t/**\n\t * Hash of the MCP server set the pooled agent was created with. A resumed\n\t * Cursor agent keeps its original MCP servers, so when this changes between\n\t * turns the pool must create a fresh agent rather than resume.\n\t */\n\tmcpHash?: string;\n}\n\n/** What kind of turn this is relative to the session's last recorded state. */\nexport type TurnKind =\n\t/** No prior record: first turn of the session. */\n\t| \"new\"\n\t/** System prompt differs (opencode's non-chat side call, e.g. title gen). */\n\t| \"side-call\"\n\t/** Prior user sequence is a strict prefix + exactly one new trailing user msg. */\n\t| \"continuation\"\n\t/** Edit / revert / compaction / multiple queued msgs — prior prefix no longer holds. */\n\t| \"divergence\";\n\nexport interface TurnClassification {\n\tkind: TurnKind;\n\t/** Fingerprint of the CURRENT prompt, to store when (re)pooling. */\n\tfingerprint: { systemHash: string; userHashes: string[] };\n}\n\nfunction sha(input: string): string {\n\treturn createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\n/**\n * Stable hash of the MCP server set handed to `Agent.create`. Keys are sorted\n * so map ordering never changes the result; empty/undefined sets hash to \"\".\n */\nexport function mcpServersFingerprint(\n\tservers: Record<string, McpServerConfig> | undefined,\n): string {\n\tif (!servers) return \"\";\n\tconst keys = Object.keys(servers).sort();\n\tif (keys.length === 0) return \"\";\n\treturn sha(JSON.stringify(keys.map((k) => [k, servers[k]])));\n}\n\n/** Stable key for one user message: its text plus a token per attached image. */\nfunction userMessageKey(\n\tmessage: Extract<LanguageModelV3Prompt[number], { role: \"user\" }>,\n): string {\n\tconst parts: string[] = [];\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\") parts.push(`t:${part.text}`);\n\t\telse if (part.type === \"file\") parts.push(`f:${part.mediaType}`);\n\t}\n\treturn parts.join(\"\\n\");\n}\n\n/** Compute the system + user-message fingerprint of a prompt. */\nexport function fingerprint(prompt: LanguageModelV3Prompt): {\n\tsystemHash: string;\n\tuserHashes: string[];\n} {\n\tconst systemParts: string[] = [];\n\tconst userHashes: string[] = [];\n\tfor (const message of prompt) {\n\t\tif (message.role === \"system\") systemParts.push(message.content);\n\t\telse if (message.role === \"user\")\n\t\t\tuserHashes.push(sha(userMessageKey(message)));\n\t}\n\treturn { systemHash: sha(systemParts.join(\"\\n\")), userHashes };\n}\n\n/** True when `prefix` is a strict element-wise prefix of `full`. */\nfunction isStrictPrefix(prefix: string[], full: string[]): boolean {\n\tif (prefix.length >= full.length) return false;\n\tfor (let i = 0; i < prefix.length; i++) {\n\t\tif (prefix[i] !== full[i]) return false;\n\t}\n\treturn true;\n}\n\n/**\n * Classify the current turn against the session's last recorded fingerprint.\n *\n * Order matters:\n * 1. no record -> \"new\"\n * 2. system prompt changed -> \"side-call\" (don't touch the pool)\n * 3. prior user hashes are a strict prefix AND exactly one new trailing user\n * message AND the last prompt message is a user turn -> \"continuation\"\n * 4. otherwise -> \"divergence\" (edit/revert/compaction/queued)\n *\n * Worst case on any misclassification is a single wasted full replay that\n * self-heals on the next turn — never worse than the `session: false` default.\n */\nexport function classifyTurn(\n\tprev: TranscriptRecord | undefined,\n\tprompt: LanguageModelV3Prompt,\n): TurnClassification {\n\tconst fp = fingerprint(prompt);\n\tif (!prev) return { kind: \"new\", fingerprint: fp };\n\tif (prev.systemHash !== fp.systemHash)\n\t\treturn { kind: \"side-call\", fingerprint: fp };\n\n\tconst lastIsUser = prompt[prompt.length - 1]?.role === \"user\";\n\tconst exactlyOneNew = fp.userHashes.length === prev.userHashes.length + 1;\n\tif (\n\t\tlastIsUser &&\n\t\texactlyOneNew &&\n\t\tisStrictPrefix(prev.userHashes, fp.userHashes)\n\t) {\n\t\treturn { kind: \"continuation\", fingerprint: fp };\n\t}\n\treturn { kind: \"divergence\", fingerprint: fp };\n}\n"],"mappings":"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","names":["toolName"]}
1
+ {"version":3,"sources":["../../src/provider/index.ts","../../src/provider/language-model.ts","../../src/provider/message-map.ts","../../src/provider/stream-map.ts","../../src/provider/transcript-fingerprint.ts"],"sourcesContent":["import type {\n\tEmbeddingModelV3,\n\tImageModelV3,\n\tProviderV3,\n} from \"@ai-sdk/provider\";\nimport { NoSuchModelError } from \"@ai-sdk/provider\";\nimport type {\n\tAgentDefinition,\n\tAgentModeOption,\n\tMcpServerConfig,\n\tSettingSource,\n} from \"@cursor/sdk\";\nimport { resolveCursorApiKey } from \"../api-key.js\";\nimport {\n\tCursorLanguageModel,\n\ttype CursorModelConfig,\n} from \"./language-model.js\";\nimport type { ToolDisplay } from \"./stream-map.js\";\n\nexport interface CursorProviderOptions {\n\t/**\n\t * Cursor API key. opencode passes this from the provider's resolved auth /\n\t * options. When omitted, falls back to the CURSOR_API_KEY environment\n\t * variable at call time.\n\t */\n\tapiKey?: string;\n\t/** Provider id, supplied by opencode as `name`. Defaults to \"cursor\". */\n\tname?: string;\n\t/** Working directory for the local Cursor agent. Defaults to process.cwd(). */\n\tcwd?: string;\n\t/** Default conversation mode: \"agent\" (default) or \"plan\". Overridable per-request. */\n\tmode?: AgentModeOption;\n\t/** Default Cursor model params (id -> value), e.g. { thinking: \"high\" }. */\n\tparams?: Record<string, string>;\n\t/**\n\t * MCP servers to make available to the Cursor agent, keyed by name. The\n\t * plugin's `config` hook populates this by translating opencode's configured\n\t * `config.mcp` servers, so the agent can use the same MCP servers that\n\t * opencode does.\n\t */\n\tmcpServers?: Record<string, McpServerConfig>;\n\t/**\n\t * Cursor settings layers to load from the local filesystem (\"project\",\n\t * \"user\", \"all\", ...). Enables the agent to pick up your Cursor skills,\n\t * rules, and `.cursor/mcp.json` servers.\n\t */\n\tsettingSources?: SettingSource[];\n\t/** Run the agent's tools inside Cursor's sandbox. */\n\tsandbox?: boolean;\n\t/** Cursor subagent definitions (`{ description, prompt, model?, mcpServers? }`). */\n\tagents?: Record<string, AgentDefinition>;\n\t/**\n\t * Session reuse strategy: `\"auto\"` (default) resumes the pooled Cursor agent\n\t * and sends only the new message on a clean continuation, falling back to a\n\t * fresh agent + full transcript on edits/reverts/compaction/side calls; `true`\n\t * is an alias for `\"auto\"`; `false` always creates a fresh agent per turn.\n\t */\n\tsession?: boolean | \"auto\";\n\t/**\n\t * How Cursor's internal tool activity (shell/read/edit/mcp/…) is surfaced:\n\t * - `\"blocks\"` (default): structured provider-executed `tool-call`/\n\t * `tool-result` parts so opencode renders proper tool blocks. Requires a\n\t * V3-native opencode host (1.16+).\n\t * - `\"reasoning\"`: compact reasoning lines; the fallback for older/non-V3\n\t * hosts (works everywhere).\n\t */\n\ttoolDisplay?: ToolDisplay;\n}\n\n/**\n * Cursor provider for the Vercel AI SDK (V3), backed by the official\n * `@cursor/sdk` local agent runtime.\n *\n * opencode loads this package by its `npm` provider config, finds the export\n * whose name starts with `create`, calls it with `{ name, apiKey, ...options }`,\n * and then calls `.languageModel(modelId)`.\n */\nexport function createCursor(options: CursorProviderOptions = {}): ProviderV3 {\n\tconst mcpServers =\n\t\toptions.mcpServers && Object.keys(options.mcpServers).length > 0\n\t\t\t? options.mcpServers\n\t\t\t: undefined;\n\tconst config: CursorModelConfig = {\n\t\tproviderName: options.name ?? \"cursor\",\n\t\tapiKey: resolveCursorApiKey(options.apiKey),\n\t\tcwd: options.cwd ?? process.cwd(),\n\t\tmode: options.mode ?? \"agent\",\n\t\t...(options.params ? { params: options.params } : {}),\n\t\t...(mcpServers ? { mcpServers } : {}),\n\t\t...(options.settingSources\n\t\t\t? { settingSources: options.settingSources }\n\t\t\t: {}),\n\t\t...(options.sandbox !== undefined ? { sandbox: options.sandbox } : {}),\n\t\t...(options.agents ? { agents: options.agents } : {}),\n\t\tsession: options.session ?? \"auto\",\n\t\ttoolDisplay: options.toolDisplay ?? \"blocks\",\n\t};\n\n\tconst notImplemented = (kind: string, modelId: string): never => {\n\t\tthrow new NoSuchModelError({\n\t\t\tmodelId,\n\t\t\tmodelType: kind as \"languageModel\",\n\t\t\tmessage: `The Cursor provider does not support ${kind} models.`,\n\t\t});\n\t};\n\n\treturn {\n\t\tspecificationVersion: \"v3\",\n\t\tlanguageModel: (modelId: string) =>\n\t\t\tnew CursorLanguageModel(modelId, config),\n\t\tembeddingModel: (modelId: string): EmbeddingModelV3 =>\n\t\t\tnotImplemented(\"embeddingModel\", modelId),\n\t\timageModel: (modelId: string): ImageModelV3 =>\n\t\t\tnotImplemented(\"imageModel\", modelId),\n\t};\n}\n","import type {\n\tLanguageModelV3,\n\tLanguageModelV3CallOptions,\n\tLanguageModelV3Content,\n\tLanguageModelV3FinishReason,\n\tLanguageModelV3StreamPart,\n\tLanguageModelV3Usage,\n} from \"@ai-sdk/provider\";\nimport { LoadAPIKeyError } from \"@ai-sdk/provider\";\nimport type {\n\tAgentDefinition,\n\tMcpServerConfig,\n\tSettingSource,\n\tAgentModeOption,\n} from \"@cursor/sdk\";\nimport { resolveCursorApiKey } from \"../api-key.js\";\nimport { latestUserMessage, promptToCursorMessage } from \"./message-map.js\";\nimport { streamAgentTurn, type CursorEvent } from \"./agent-events.js\";\nimport {\n\tcursorEventsToContent,\n\tcursorEventsToStream,\n\ttype ToolDisplay,\n} from \"./stream-map.js\";\nimport { resolveControls } from \"./controls.js\";\nimport { acquireAgent, getSessionRecord } from \"./session-pool.js\";\nimport {\n\tclassifyTurn,\n\tfingerprint,\n\tmcpServersFingerprint,\n} from \"./transcript-fingerprint.js\";\n\nexport interface CursorModelConfig {\n\t/** Provider id used for logging and the providerOptions key (e.g. \"cursor\"). */\n\tproviderName: string;\n\t/** Explicit API key; re-resolved against the env at call time when absent. */\n\tapiKey?: string;\n\t/** Working directory the local Cursor agent operates in. */\n\tcwd: string;\n\t/** Default conversation mode; overridable per-request via providerOptions. */\n\tmode: AgentModeOption;\n\t/** Default Cursor model params (id -> value); overridable per-request. */\n\tparams?: Record<string, string>;\n\t/** MCP servers forwarded to the Cursor agent from opencode's config. */\n\tmcpServers?: Record<string, McpServerConfig>;\n\t/** Cursor settings layers to load from disk (skills, rules, .cursor/mcp.json). */\n\tsettingSources?: SettingSource[];\n\t/** Run the agent's tools inside Cursor's sandbox. */\n\tsandbox?: boolean;\n\t/** Cursor subagent definitions made available to the agent. */\n\tagents?: Record<string, AgentDefinition>;\n\t/**\n\t * Session reuse strategy:\n\t * - `\"auto\"` (default): fingerprint-guarded reuse — resume the pooled Cursor\n\t * agent and send only the new message on a clean continuation, otherwise\n\t * fall back to a fresh agent + full transcript. Robust to opencode's\n\t * non-chat side calls, message edits, reverts, and compaction.\n\t * - `true`: alias for `\"auto\"`.\n\t * - `false`: always create a fresh agent per turn and re-send the full\n\t * transcript (the original behavior; the escape hatch).\n\t */\n\tsession?: boolean | \"auto\";\n\t/**\n\t * How Cursor's internal tool activity is surfaced (see {@link ToolDisplay}).\n\t * Defaults to `\"blocks\"`.\n\t */\n\ttoolDisplay?: ToolDisplay;\n}\n\n/**\n * A Vercel AI SDK `LanguageModelV3` backed by a local Cursor agent. opencode\n * loads this via the provider factory and calls `doStream` / `doGenerate`.\n * The event→stream translation lives in stream-map.ts so it can be unit tested\n * without a live agent.\n */\nexport class CursorLanguageModel implements LanguageModelV3 {\n\treadonly specificationVersion = \"v3\" as const;\n\treadonly modelId: string;\n\treadonly provider: string;\n\t// Images are passed inline as base64 data, so no URLs are fetched natively.\n\treadonly supportedUrls: Record<string, RegExp[]> = {};\n\n\tconstructor(\n\t\tmodelId: string,\n\t\tprivate readonly config: CursorModelConfig,\n\t) {\n\t\tthis.modelId = modelId;\n\t\tthis.provider = config.providerName;\n\t}\n\n\tprivate requireApiKey(): string {\n\t\tconst apiKey = resolveCursorApiKey(this.config.apiKey);\n\t\tif (!apiKey) {\n\t\t\tthrow new LoadAPIKeyError({\n\t\t\t\tmessage:\n\t\t\t\t\t\"Cursor API key missing. Run `opencode auth login` and choose Cursor, or set CURSOR_API_KEY.\",\n\t\t\t});\n\t\t}\n\t\treturn apiKey;\n\t}\n\n\tprivate async *agentRun(\n\t\toptions: LanguageModelV3CallOptions,\n\t): AsyncGenerator<CursorEvent> {\n\t\t// opencode delivers per-request controls (merged model options + selected\n\t\t// variant) under providerOptions keyed by our provider id. The session id is\n\t\t// injected there by the plugin's chat.params hook.\n\t\tconst providerOptions = options.providerOptions?.[this.provider] as\n\t\t\t| Record<string, unknown>\n\t\t\t| undefined;\n\t\tconst { mode, modelSelection } = resolveControls(\n\t\t\tthis.modelId,\n\t\t\t{ mode: this.config.mode, params: this.config.params },\n\t\t\tproviderOptions,\n\t\t);\n\t\tconst sessionID =\n\t\t\ttypeof providerOptions?.[\"sessionID\"] === \"string\"\n\t\t\t\t? (providerOptions[\"sessionID\"] as string)\n\t\t\t\t: undefined;\n\t\t// MCP servers may be re-forwarded per turn by the plugin's chat.params hook\n\t\t// (reflecting live opencode enable/disable). When present, the dynamic set\n\t\t// wins over the static startup snapshot baked into config.mcpServers.\n\t\tconst dynamicMcp = providerOptions?.[\"mcpServers\"] as\n\t\t\t| Record<string, McpServerConfig>\n\t\t\t| undefined;\n\t\tconst mcpServers = dynamicMcp ?? this.config.mcpServers;\n\t\tconst mcpHash = mcpServersFingerprint(mcpServers);\n\t\t// `session` defaults to \"auto\" (fingerprint-guarded reuse); `true` is an\n\t\t// alias for \"auto\"; `false` keeps the per-turn-fresh full-transcript path.\n\t\tconst sessionEnabled = (this.config.session ?? \"auto\") !== false;\n\t\t// Power users can resume a specific Cursor agent via\n\t\t// `providerOptions.cursor.agentId`; it takes precedence over session pooling.\n\t\tconst explicitAgentId =\n\t\t\ttypeof providerOptions?.[\"agentId\"] === \"string\"\n\t\t\t\t? (providerOptions[\"agentId\"] as string)\n\t\t\t\t: undefined;\n\t\t// The plugin may mark opencode's non-chat side calls (e.g. title\n\t\t// generation) so they never resume or disturb the pooled agent.\n\t\tconst ephemeral = providerOptions?.[\"ephemeral\"] === true;\n\n\t\t// Decide create-vs-resume and whether to pool, from the turn classification.\n\t\tconst usePool = sessionEnabled && Boolean(sessionID) && !explicitAgentId;\n\t\tlet resumeAgentId: string | undefined = explicitAgentId;\n\t\tlet poolKey: string | undefined;\n\t\tlet record:\n\t\t\t| { systemHash: string; userHashes: string[]; mcpHash?: string }\n\t\t\t| undefined;\n\t\tif (usePool) {\n\t\t\tconst classification = ephemeral\n\t\t\t\t? {\n\t\t\t\t\t\tkind: \"side-call\" as const,\n\t\t\t\t\t\tfingerprint: fingerprint(options.prompt),\n\t\t\t\t\t}\n\t\t\t\t: classifyTurn(getSessionRecord(sessionID!), options.prompt);\n\t\t\tswitch (classification.kind) {\n\t\t\t\tcase \"continuation\": {\n\t\t\t\t\tconst prev = getSessionRecord(sessionID!);\n\t\t\t\t\t// A resumed agent keeps its original MCP servers, so only resume\n\t\t\t\t\t// when the live MCP set is unchanged; otherwise create fresh so the\n\t\t\t\t\t// new server set takes effect (re-pooled under the same session).\n\t\t\t\t\tif (prev?.mcpHash === mcpHash) {\n\t\t\t\t\t\tresumeAgentId = prev?.agentId;\n\t\t\t\t\t}\n\t\t\t\t\tpoolKey = sessionID;\n\t\t\t\t\trecord = { ...classification.fingerprint, mcpHash };\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase \"new\":\n\t\t\t\tcase \"divergence\":\n\t\t\t\t\tpoolKey = sessionID;\n\t\t\t\t\trecord = { ...classification.fingerprint, mcpHash };\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"side-call\":\n\t\t\t\t\t// fresh ephemeral agent; pool left untouched.\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (process.env[\"OPENCODE_CURSOR_DEBUG\"] === \"1\") {\n\t\t\t\tconst label =\n\t\t\t\t\tclassification.kind === \"continuation\"\n\t\t\t\t\t\t? \"resume\"\n\t\t\t\t\t\t: `fresh:${classification.kind}`;\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[cursor:debug] turn classification=${label} session=${sessionID}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst acquired = await acquireAgent({\n\t\t\tapiKey: this.requireApiKey(),\n\t\t\tmodelSelection,\n\t\t\tmode,\n\t\t\tcwd: this.config.cwd,\n\t\t\t...(this.config.settingSources\n\t\t\t\t? { settingSources: this.config.settingSources }\n\t\t\t\t: {}),\n\t\t\t...(this.config.sandbox !== undefined\n\t\t\t\t? { sandbox: this.config.sandbox }\n\t\t\t\t: {}),\n\t\t\t...(mcpServers ? { mcpServers } : {}),\n\t\t\t...(this.config.agents ? { agents: this.config.agents } : {}),\n\t\t\t...(poolKey ? { name: `opencode/${sessionID!.slice(-8)}` } : {}),\n\t\t\t...(resumeAgentId ? { resumeAgentId } : {}),\n\t\t\t...(poolKey ? { poolKey } : {}),\n\t\t\t...(record ? { record } : {}),\n\t\t});\n\n\t\t// A resumed agent already remembers the prior conversation, so send only the\n\t\t// new turn; otherwise send the full transcript.\n\t\tconst message = acquired.resumed\n\t\t\t? (latestUserMessage(options.prompt) ??\n\t\t\t\tpromptToCursorMessage(options.prompt))\n\t\t\t: promptToCursorMessage(options.prompt);\n\n\t\ttry {\n\t\t\tyield* streamAgentTurn(acquired.agent, message, {\n\t\t\t\tmode,\n\t\t\t\tabortSignal: options.abortSignal,\n\t\t\t});\n\t\t} finally {\n\t\t\tacquired.release();\n\t\t}\n\t}\n\n\tasync doStream(options: LanguageModelV3CallOptions): Promise<{\n\t\tstream: ReadableStream<LanguageModelV3StreamPart>;\n\t}> {\n\t\treturn {\n\t\t\tstream: cursorEventsToStream(\n\t\t\t\tthis.agentRun(options),\n\t\t\t\tthis.config.toolDisplay,\n\t\t\t),\n\t\t};\n\t}\n\n\tasync doGenerate(options: LanguageModelV3CallOptions): Promise<{\n\t\tcontent: Array<LanguageModelV3Content>;\n\t\tfinishReason: LanguageModelV3FinishReason;\n\t\tusage: LanguageModelV3Usage;\n\t\twarnings: Array<never>;\n\t}> {\n\t\tconst result = await cursorEventsToContent(\n\t\t\tthis.agentRun(options),\n\t\t\tthis.config.toolDisplay,\n\t\t);\n\t\treturn { ...result, warnings: [] };\n\t}\n}\n","import type { LanguageModelV3Prompt } from \"@ai-sdk/provider\";\nimport type { SDKImage, SDKUserMessage } from \"@cursor/sdk\";\n\n/**\n * Caps on inlined tool payloads in the flattened transcript. Tool outputs are\n * INCLUDED (truncated) rather than dropped so a fresh/diverged agent still sees\n * what prior tools produced, while a huge file read or search dump can't bloat\n * the replayed message unbounded.\n */\nconst TOOL_RESULT_CAP = 2000;\nconst TOOL_ARGS_CAP = 500;\n\nfunction stringify(value: unknown): string {\n\tif (typeof value === \"string\") return value;\n\ttry {\n\t\treturn JSON.stringify(value ?? null);\n\t} catch {\n\t\treturn String(value);\n\t}\n}\n\nfunction truncate(text: string, cap: number): string {\n\treturn text.length > cap\n\t\t? `${text.slice(0, cap)}…[+${text.length - cap} chars]`\n\t\t: text;\n}\n\n/**\n * Convert an AI-SDK prompt (the full conversation opencode sends on every call)\n * into a single Cursor `SDKUserMessage`.\n *\n * The Cursor agent keeps its own per-agent conversation memory, but opencode\n * re-sends the whole history each turn. To stay correct without double-counting\n * context, we create a fresh agent per turn (see language-model.ts) and flatten\n * the entire prompt into one transcript message. Images from the final user\n * turn are attached natively so multimodal models can see them.\n */\nexport function promptToCursorMessage(\n\tprompt: LanguageModelV3Prompt,\n): SDKUserMessage {\n\tconst lines: string[] = [];\n\tconst images: SDKImage[] = [];\n\n\tprompt.forEach((message, index) => {\n\t\tconst isLast = index === prompt.length - 1;\n\t\tswitch (message.role) {\n\t\t\tcase \"system\":\n\t\t\t\tlines.push(`# System\\n${message.content}`);\n\t\t\t\tbreak;\n\t\t\tcase \"user\": {\n\t\t\t\tconst text: string[] = [];\n\t\t\t\tfor (const part of message.content) {\n\t\t\t\t\tif (part.type === \"text\") text.push(part.text);\n\t\t\t\t\telse if (\n\t\t\t\t\t\tpart.type === \"file\" &&\n\t\t\t\t\t\tpart.mediaType.startsWith(\"image/\")\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst image = fileToImage(part.data, part.mediaType);\n\t\t\t\t\t\t// Only attach images natively for the final user turn; earlier ones\n\t\t\t\t\t\t// are referenced by transcript order.\n\t\t\t\t\t\tif (isLast && image) images.push(image);\n\t\t\t\t\t\ttext.push(\"[image attached]\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tlines.push(`# User\\n${text.join(\"\\n\")}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"assistant\": {\n\t\t\t\tconst text: string[] = [];\n\t\t\t\tfor (const part of message.content) {\n\t\t\t\t\tif (part.type === \"text\") text.push(part.text);\n\t\t\t\t\telse if (part.type === \"reasoning\")\n\t\t\t\t\t\ttext.push(`(thinking) ${part.text}`);\n\t\t\t\t\telse if (part.type === \"tool-call\")\n\t\t\t\t\t\ttext.push(\n\t\t\t\t\t\t\t`[called ${part.toolName}(${truncate(stringify(part.input), TOOL_ARGS_CAP)})]`,\n\t\t\t\t\t\t);\n\t\t\t\t\telse if (part.type === \"tool-result\")\n\t\t\t\t\t\ttext.push(\n\t\t\t\t\t\t\t`[result of ${part.toolName}: ${truncate(stringify(part.output), TOOL_RESULT_CAP)}]`,\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tlines.push(`# Assistant\\n${text.join(\"\\n\")}`);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase \"tool\": {\n\t\t\t\tfor (const part of message.content) {\n\t\t\t\t\tif (part.type === \"tool-result\") {\n\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\t`# Tool result (${part.toolName})\\n${truncate(stringify(part.output), TOOL_RESULT_CAP)}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t});\n\n\tconst out: SDKUserMessage = { text: lines.join(\"\\n\\n\") };\n\tif (images.length > 0) out.images = images;\n\treturn out;\n}\n\nfunction fileToImage(\n\tdata: string | Uint8Array | URL,\n\tmediaType: string,\n): SDKImage | undefined {\n\tif (data instanceof URL) return { url: data.toString() };\n\tif (typeof data === \"string\") {\n\t\t// Either a URL or already-base64 encoded data.\n\t\tif (/^https?:\\/\\//i.test(data)) return { url: data };\n\t\treturn { data, mimeType: mediaType };\n\t}\n\tif (data instanceof Uint8Array) {\n\t\treturn { data: Buffer.from(data).toString(\"base64\"), mimeType: mediaType };\n\t}\n\treturn undefined;\n}\n\n/**\n * Extract only the final user turn as a Cursor message. Used when resuming a\n * pooled agent that already remembers the prior conversation, so we send just\n * the new message instead of the whole transcript. Returns `undefined` if the\n * last message isn't a user turn (caller should fall back to the full transcript).\n */\nexport function latestUserMessage(\n\tprompt: LanguageModelV3Prompt,\n): SDKUserMessage | undefined {\n\tconst last = prompt[prompt.length - 1];\n\tif (!last || last.role !== \"user\") return undefined;\n\n\tconst text: string[] = [];\n\tconst images: SDKImage[] = [];\n\tfor (const part of last.content) {\n\t\tif (part.type === \"text\") text.push(part.text);\n\t\telse if (part.type === \"file\" && part.mediaType.startsWith(\"image/\")) {\n\t\t\tconst image = fileToImage(part.data, part.mediaType);\n\t\t\tif (image) images.push(image);\n\t\t\ttext.push(\"[image attached]\");\n\t\t}\n\t}\n\n\tconst out: SDKUserMessage = { text: text.join(\"\\n\") };\n\tif (images.length > 0) out.images = images;\n\treturn out;\n}\n","import type {\n\tLanguageModelV3Content,\n\tLanguageModelV3FinishReason,\n\tLanguageModelV3StreamPart,\n\tLanguageModelV3Usage,\n} from \"@ai-sdk/provider\";\nimport type { CursorEvent, CursorUsage } from \"./agent-events.js\";\n\n/**\n * How Cursor's internal tool activity (shell/read/edit/mcp/…) is surfaced to\n * opencode:\n * - `\"blocks\"` (default): emitted as provider-executed AI-SDK\n * `tool-call`/`tool-result` parts so opencode renders structured tool\n * blocks. Requires a V3-native opencode host (1.16+). The parts must carry\n * BOTH `providerExecuted: true` AND `dynamic: true` — ai's `parseToolCall`\n * (v6, `doParseToolCall`) only exempts that combination from registered-tool\n * validation; without `dynamic` an unknown name raises `NoSuchToolError`,\n * which opencode's `experimental_repairToolCall` rewrites into its \"invalid\"\n * tool. Names are also prefixed (`cursor_…`) so they can never collide with\n * a tool opencode has registered (`read`, `grep`, `task`, …) — a colliding\n * name is validated against that tool's input schema instead of being\n * treated as dynamic.\n */\nexport type ToolDisplay = \"reasoning\" | \"blocks\";\n\nconst FINISH_STOP: LanguageModelV3FinishReason = {\n\tunified: \"stop\",\n\traw: undefined,\n};\nconst FINISH_ERROR: LanguageModelV3FinishReason = {\n\tunified: \"error\",\n\traw: undefined,\n};\n\nfunction safeJsonString(input: unknown): string {\n\ttry {\n\t\treturn typeof input === \"string\" ? input : JSON.stringify(input ?? {});\n\t} catch {\n\t\treturn \"{}\";\n\t}\n}\n\n/**\n * Tool name as it crosses into opencode in \"blocks\" mode. Prefixed so it can\n * never collide with a tool opencode has registered, and sanitized because MCP\n * names contain `/` (e.g. `myserver/find_symbol` → `cursor_myserver_find_symbol`).\n */\nfunction blockToolName(name: string): string {\n\treturn `cursor_${name.replace(/[^A-Za-z0-9_-]/g, \"_\")}`;\n}\n\n/**\n * A blocks-mode tool part. `tool-call` / `tool-result` are structurally\n * identical in `LanguageModelV3StreamPart` (streaming) and\n * `LanguageModelV3Content` (`doGenerate`), so the builders below produce one\n * shape that both consumers cast to their respective union.\n */\ntype BlockToolPart = LanguageModelV3StreamPart;\n\n/**\n * Build a provider-executed dynamic `tool-call` under an EXACT (already-final)\n * tool name. `input` is a stringified JSON object per the V3 spec. Both\n * `providerExecuted` and `dynamic` are set so ai's `parseToolCall` accepts the\n * part without registered-tool validation, and a host that hasn't registered\n * the named tool degrades to a generic dynamic block instead of erroring.\n */\nfunction nativeToolCall(\n\tid: string,\n\ttoolName: string,\n\tinput: unknown,\n): BlockToolPart {\n\treturn {\n\t\ttype: \"tool-call\",\n\t\ttoolCallId: id,\n\t\ttoolName,\n\t\tinput: safeJsonString(input),\n\t\tproviderExecuted: true,\n\t\tdynamic: true,\n\t} as BlockToolPart;\n}\n\n/**\n * Build a provider-executed dynamic `tool-result` under an EXACT tool name. Per\n * the V3 spec (and ai v6's `runToolsTransformation`, which reads\n * `chunk.result` / `chunk.isError`) the payload goes in `result`; `result` is\n * typed `NonNullable<JSONValue>` so a missing payload is coalesced to `null`.\n */\nfunction nativeToolResult(\n\tid: string,\n\ttoolName: string,\n\tresult: unknown,\n\tisError: boolean,\n): BlockToolPart {\n\treturn {\n\t\ttype: \"tool-result\",\n\t\ttoolCallId: id,\n\t\ttoolName,\n\t\tresult: (result ?? null) as never,\n\t\tisError,\n\t\tproviderExecuted: true,\n\t\tdynamic: true,\n\t} as BlockToolPart;\n}\n\n/**\n * Build a generic `tool-call` for a Cursor tool with no native opencode\n * counterpart. The name is `cursor_`-prefixed so it can't collide with a tool\n * opencode has registered.\n */\nfunction toolCallObj(id: string, name: string, input: unknown): BlockToolPart {\n\treturn nativeToolCall(id, blockToolName(name), input);\n}\n\n/** Build a generic (`cursor_`-prefixed) `tool-result`. */\nfunction toolResultObj(\n\tid: string,\n\tname: string,\n\tresult: unknown,\n\tisError: boolean,\n): BlockToolPart {\n\treturn nativeToolResult(id, blockToolName(name), result, isError);\n}\n\n/** Cursor's file-edit tool surfaces with this name (its `toolCall.type`). */\nconst EDIT_TOOL_NAME = \"edit\";\n\n/** Cursor plan-mode tool; markdown lives in call args, result is empty. */\nconst CREATE_PLAN_TOOL_NAME = \"createPlan\";\n\nfunction isCreatePlanTool(name: string): boolean {\n\treturn name === CREATE_PLAN_TOOL_NAME;\n}\n\nfunction createPlanContent(input: unknown): string | undefined {\n\treturn strField(input, \"plan\");\n}\n\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n\treturn typeof v === \"object\" && v !== null;\n}\n\nfunction strField(v: unknown, key: string): string | undefined {\n\treturn isRecord(v) && typeof v[key] === \"string\"\n\t\t? (v[key] as string)\n\t\t: undefined;\n}\n\nfunction numField(v: unknown, key: string): number | undefined {\n\treturn isRecord(v) && typeof v[key] === \"number\"\n\t\t? (v[key] as number)\n\t\t: undefined;\n}\n\n/** Unwrap a Cursor `{ status:\"success\", value }` result to its `value`. */\nfunction successValue(result: unknown): unknown {\n\treturn isRecord(result) && result[\"status\"] === \"success\"\n\t\t? result[\"value\"]\n\t\t: undefined;\n}\n\n/** The `{title, metadata, output}` shape opencode folds into a tool part's state. */\ninterface FoldedResult {\n\ttitle: string;\n\tmetadata: Record<string, unknown>;\n\toutput: string;\n}\n\n/**\n * Maps a Cursor tool's call/result onto an opencode tool so opencode renders\n * something nicer than a raw-JSON block. Mirrors the `edit` mapping: parts are\n * emitted `providerExecuted` + `dynamic`, so the call is never executed on disk\n * and a host that hasn't registered the tool degrades to a generic dynamic\n * block.\n *\n * - `tool` is opencode's REGISTERED tool name when there's a native renderer to\n * reuse (`bash`, `read`, `websearch`, …). Omit it for \"format-only\" adapters\n * (`readLints`, `delete`): the part stays a `cursor_*` block, but `result`\n * still folds the payload into a clean `output` string so opencode's generic\n * renderer shows formatted text instead of dumped JSON.\n * - `input` translates Cursor's arg keys to the names opencode's renderer reads\n * (e.g. Cursor `path` → opencode `filePath`).\n * - `result` folds a SUCCESSFUL Cursor result `value` into `{title, metadata,\n * output}`; returning `null` falls back to a generic block (unexpected shape).\n */\ninterface NativeToolAdapter {\n\ttool?: string;\n\tinput(args: unknown): Record<string, unknown>;\n\tresult(value: unknown, args: unknown): FoldedResult | null;\n}\n\n/**\n * Flatten a Cursor MCP result `value` (`{ content: [{ text: { text } }, …] }`)\n * into plain text. Returns `null` when `value` isn't MCP-shaped, so callers can\n * fall back to the raw payload. An MCP `content` array that holds no text\n * (e.g. only images) yields a `\"[image]\"`-style placeholder rather than `null`.\n */\nfunction flattenMcpContent(value: unknown): string | null {\n\tif (!isRecord(value) || !Array.isArray(value[\"content\"])) return null;\n\tconst parts = (value[\"content\"] as unknown[]).flatMap((item) => {\n\t\tconst text = isRecord(item) ? strField(item[\"text\"], \"text\") : undefined;\n\t\tif (text !== undefined) return [text];\n\t\tif (isRecord(item) && isRecord(item[\"image\"])) return [\"[image]\"];\n\t\treturn [];\n\t});\n\treturn parts.join(\"\\n\");\n}\n\n/**\n * Fold a generic (non-adapter) Cursor result into a clean `output` string IF\n * it's an MCP result; otherwise `null` so the raw payload is passed through.\n * This stops every MCP tool (web search, custom servers, …) from dumping the\n * nested `{content:[{text:{text}}]}` JSON into the block.\n */\nfunction mcpFold(result: unknown): FoldedResult | null {\n\tconst text = flattenMcpContent(successValue(result));\n\tif (text === null) return null;\n\treturn { title: \"\", metadata: {}, output: text };\n}\n\n/** Cursor MCP call args nest the real tool input under `args.args`. */\nfunction mcpInputArgs(args: unknown): unknown {\n\treturn isRecord(args) ? args[\"args\"] : undefined;\n}\n\n/** Map a Cursor MCP `providerIdentifier` to a websearch provider label key. */\nfunction webSearchProvider(args: unknown): string | undefined {\n\tconst id = (strField(args, \"providerIdentifier\") ?? \"\").toLowerCase();\n\tif (id.includes(\"exa\")) return \"exa\";\n\tif (id.includes(\"parallel\")) return \"parallel\";\n\treturn undefined;\n}\n\n/** A Cursor MCP tool whose name looks like a web search (`web_search`, …). */\nfunction isWebSearchName(name: string): boolean {\n\treturn /web[_-]?search/i.test(name);\n}\n\n/**\n * Cursor web search arrives as an MCP tool; map it onto opencode's native\n * `websearch` renderer (query subtitle + result body). The query lives in the\n * nested MCP input (`args.args.query`); the result is MCP `content`.\n */\nconst WEBSEARCH_ADAPTER: NativeToolAdapter = {\n\ttool: \"websearch\",\n\tinput: (args) => {\n\t\tconst query = strField(mcpInputArgs(args), \"query\");\n\t\treturn query !== undefined ? { query } : {};\n\t},\n\tresult: (value, args) => {\n\t\tconst provider = webSearchProvider(args);\n\t\treturn {\n\t\t\ttitle: \"\",\n\t\t\tmetadata: provider ? { provider } : {},\n\t\t\toutput: flattenMcpContent(value) ?? \"\",\n\t\t};\n\t},\n};\n\n/**\n * Resolve a Cursor tool name (+ call args) to an adapter, or `undefined` for a\n * plain `cursor_*` block. `edit` is handled separately (its native call input\n * depends on the diff in the result).\n */\nfunction resolveAdapter(\n\tname: string,\n\t_input: unknown,\n): NativeToolAdapter | undefined {\n\tconst exact = NATIVE_ADAPTERS[name];\n\tif (exact) return exact;\n\tif (isWebSearchName(name)) return WEBSEARCH_ADAPTER;\n\treturn undefined;\n}\n\n/** Cursor todo status (`inProgress`) → opencode todo status (`in_progress`). */\nfunction mapTodoStatus(status: unknown): string {\n\tif (status === \"inProgress\") return \"in_progress\";\n\treturn typeof status === \"string\" ? status : \"pending\";\n}\n\n/** Normalize Cursor `updateTodos` args into opencode `todowrite` todos. */\nfunction mapTodos(args: unknown): Array<{ content: string; status: string }> {\n\tconst todos =\n\t\tisRecord(args) && Array.isArray(args[\"todos\"]) ? args[\"todos\"] : [];\n\treturn (todos as unknown[]).flatMap((t) =>\n\t\tisRecord(t) && typeof t[\"content\"] === \"string\"\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\tcontent: t[\"content\"] as string,\n\t\t\t\t\t\tstatus: mapTodoStatus(t[\"status\"]),\n\t\t\t\t\t},\n\t\t\t\t]\n\t\t\t: [],\n\t);\n}\n\n/**\n * Cursor tool name → opencode native tool adapter. Cursor tools without a\n * natural opencode counterpart (`delete`, `mcp`, `semSearch`, `readLints`,\n * `generateImage`, `recordScreen`) are intentionally absent and fall through\n * to generic `cursor_*` blocks. `edit` and `createPlan` are handled separately\n * (edit: native call input depends on the diff in the result; createPlan:\n * emitted as assistant markdown text so opencode renders it like a normal plan).\n */\nconst NATIVE_ADAPTERS: Record<string, NativeToolAdapter> = {\n\t// Cursor `shell` → opencode `bash` (console renderer).\n\tshell: {\n\t\ttool: \"bash\",\n\t\tinput: (args) => ({ command: strField(args, \"command\") ?? \"\" }),\n\t\tresult: (value, args) => {\n\t\t\tif (!isRecord(value)) return null;\n\t\t\tconst command = strField(args, \"command\") ?? \"\";\n\t\t\tconst stdout = strField(value, \"stdout\") ?? \"\";\n\t\t\tconst stderr = strField(value, \"stderr\") ?? \"\";\n\t\t\tconst exit = numField(value, \"exitCode\");\n\t\t\tconst body = [stdout, stderr].filter((s) => s.length > 0).join(\"\\n\");\n\t\t\tconst output =\n\t\t\t\texit !== undefined && exit !== 0\n\t\t\t\t\t? `${body}${body ? \"\\n\" : \"\"}(exit ${exit})`\n\t\t\t\t\t: body;\n\t\t\treturn {\n\t\t\t\ttitle: command,\n\t\t\t\tmetadata: { command, output, exit: exit ?? 0 },\n\t\t\t\toutput,\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `read` → opencode `read`.\n\t//\n\t// Cursor's read tool takes ONLY `{ path }` — there is no `offset`/`limit`/\n\t// line-range arg (see `@cursor/sdk` `ReadArgsSchema`, a `{ path: string }`\n\t// \"strip\" object). The model therefore cannot request a sub-range, so two\n\t// same-path reads in a transcript are RE-READS of the same content, never\n\t// \"different sections\". The only per-call detail available is in the result\n\t// `value` (`content`, `totalLines`, `fileSize`); we derive the lines-read\n\t// count from `content` and surface `linesReturned/totalLines` in the title\n\t// so a reader can see how much was read and spot redundant re-reads.\n\tread: {\n\t\ttool: \"read\",\n\t\tinput: (args) => ({ filePath: strField(args, \"path\") ?? \"\" }),\n\t\tresult: (value, args) => {\n\t\t\tconst content = strField(value, \"content\");\n\t\t\tif (content === undefined) return null;\n\t\t\tconst filePath = strField(args, \"path\") ?? \"\";\n\t\t\tconst totalLines = numField(value, \"totalLines\");\n\t\t\tconst fileSize = numField(value, \"fileSize\");\n\t\t\tconst linesReturned = content.split(\"\\n\").length;\n\t\t\tconst lineLabel =\n\t\t\t\ttotalLines !== undefined\n\t\t\t\t\t? `${linesReturned}/${totalLines} lines`\n\t\t\t\t\t: `${linesReturned} lines`;\n\t\t\treturn {\n\t\t\t\ttitle: `${filePath} (${lineLabel})`,\n\t\t\t\tmetadata: {\n\t\t\t\t\tpreview: content.split(\"\\n\").slice(0, 20).join(\"\\n\"),\n\t\t\t\t\tloaded: [] as string[],\n\t\t\t\t\tlinesReturned,\n\t\t\t\t\t...(totalLines !== undefined ? { totalLines } : {}),\n\t\t\t\t\t...(fileSize !== undefined ? { fileSize } : {}),\n\t\t\t\t},\n\t\t\t\toutput: content,\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `write` → opencode `write` (renders input.content as the new file).\n\twrite: {\n\t\ttool: \"write\",\n\t\tinput: (args) => ({\n\t\t\tfilePath: strField(args, \"path\") ?? \"\",\n\t\t\tcontent: strField(args, \"fileText\") ?? \"\",\n\t\t}),\n\t\tresult: (value, args) => {\n\t\t\tconst filePath = strField(args, \"path\") ?? \"\";\n\t\t\tconst lines = numField(value, \"linesCreated\");\n\t\t\tconst output =\n\t\t\t\tlines !== undefined\n\t\t\t\t\t? `Wrote ${lines} line${lines === 1 ? \"\" : \"s\"}.`\n\t\t\t\t\t: \"Wrote file successfully.\";\n\t\t\treturn {\n\t\t\t\ttitle: filePath,\n\t\t\t\tmetadata: { diagnostics: {}, filepath: filePath, exists: false },\n\t\t\t\toutput,\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `glob` → opencode `glob`.\n\tglob: {\n\t\ttool: \"glob\",\n\t\tinput: (args) => {\n\t\t\tconst pattern = strField(args, \"globPattern\") ?? \"\";\n\t\t\tconst dir = strField(args, \"targetDirectory\");\n\t\t\treturn dir ? { pattern, path: dir } : { pattern };\n\t\t},\n\t\tresult: (value, args) => {\n\t\t\tif (!isRecord(value) || !Array.isArray(value[\"files\"])) return null;\n\t\t\tconst files = (value[\"files\"] as unknown[]).filter(\n\t\t\t\t(f): f is string => typeof f === \"string\",\n\t\t\t);\n\t\t\tconst truncated =\n\t\t\t\tvalue[\"clientTruncated\"] === true || value[\"ripgrepTruncated\"] === true;\n\t\t\tconst pattern = strField(args, \"globPattern\") ?? \"\";\n\t\t\tconst dir = strField(args, \"targetDirectory\");\n\t\t\treturn {\n\t\t\t\ttitle: dir ? `${pattern} in ${dir}` : pattern,\n\t\t\t\tmetadata: { count: files.length, truncated },\n\t\t\t\toutput: files.length > 0 ? files.join(\"\\n\") : \"No files found\",\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `grep` → opencode `grep` (flatten matches into ripgrep-style text).\n\tgrep: {\n\t\ttool: \"grep\",\n\t\tinput: (args) => {\n\t\t\tconst out: Record<string, unknown> = {\n\t\t\t\tpattern: strField(args, \"pattern\") ?? \"\",\n\t\t\t};\n\t\t\tconst p = strField(args, \"path\");\n\t\t\tif (p) out[\"path\"] = p;\n\t\t\tconst g = strField(args, \"glob\");\n\t\t\tif (g) out[\"include\"] = g;\n\t\t\treturn out;\n\t\t},\n\t\tresult: (value, args) => {\n\t\t\tif (!isRecord(value)) return null;\n\t\t\tconst unions: unknown[] = [];\n\t\t\tconst ws = value[\"workspaceResults\"];\n\t\t\tif (isRecord(ws)) unions.push(...Object.values(ws));\n\t\t\tif (value[\"activeEditorResult\"] !== undefined)\n\t\t\t\tunions.push(value[\"activeEditorResult\"]);\n\t\t\tconst lines: string[] = [];\n\t\t\tlet total = 0;\n\t\t\tlet current = \"\";\n\t\t\tfor (const u of unions) {\n\t\t\t\tif (!isRecord(u)) continue;\n\t\t\t\tconst output = u[\"output\"];\n\t\t\t\tif (\n\t\t\t\t\tu[\"type\"] === \"content\" &&\n\t\t\t\t\tisRecord(output) &&\n\t\t\t\t\tArray.isArray(output[\"matches\"])\n\t\t\t\t) {\n\t\t\t\t\tfor (const m of output[\"matches\"] as unknown[]) {\n\t\t\t\t\t\tif (!isRecord(m)) continue;\n\t\t\t\t\t\tconst file = strField(m, \"file\") ?? \"\";\n\t\t\t\t\t\tconst line = numField(m, \"lineNumber\");\n\t\t\t\t\t\tconst text = strField(m, \"line\") ?? \"\";\n\t\t\t\t\t\tif (current !== file) {\n\t\t\t\t\t\t\tif (current) lines.push(\"\");\n\t\t\t\t\t\t\tcurrent = file;\n\t\t\t\t\t\t\tlines.push(`${file}:`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlines.push(\n\t\t\t\t\t\t\tline !== undefined ? ` Line ${line}: ${text}` : ` ${text}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t\ttotal++;\n\t\t\t\t\t}\n\t\t\t\t} else if (\n\t\t\t\t\tu[\"type\"] === \"files\" &&\n\t\t\t\t\tisRecord(output) &&\n\t\t\t\t\tArray.isArray(output[\"files\"])\n\t\t\t\t) {\n\t\t\t\t\tfor (const f of output[\"files\"] as unknown[]) {\n\t\t\t\t\t\tif (typeof f === \"string\") {\n\t\t\t\t\t\t\tlines.push(f);\n\t\t\t\t\t\t\ttotal++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst pattern = strField(args, \"pattern\") ?? \"\";\n\t\t\tconst glob = strField(args, \"glob\");\n\t\t\tconst path = strField(args, \"path\");\n\t\t\tlet title = pattern;\n\t\t\tif (glob) title += ` (${glob})`;\n\t\t\tif (path) title += ` in ${path}`;\n\t\t\treturn {\n\t\t\t\ttitle,\n\t\t\t\tmetadata: { matches: total, truncated: false },\n\t\t\t\toutput:\n\t\t\t\t\ttotal > 0\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t`Found ${total} match${total === 1 ? \"\" : \"es\"}`,\n\t\t\t\t\t\t\t\t\"\",\n\t\t\t\t\t\t\t\t...lines,\n\t\t\t\t\t\t\t].join(\"\\n\")\n\t\t\t\t\t\t: \"No matches found\",\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `ls` → opencode `list` (flatten the directory tree into paths).\n\tls: {\n\t\ttool: \"list\",\n\t\tinput: (args) => ({ path: strField(args, \"path\") ?? \"\" }),\n\t\tresult: (value) => {\n\t\t\tif (!isRecord(value)) return null;\n\t\t\tconst root = value[\"directoryTreeRoot\"];\n\t\t\tif (!isRecord(root)) return null;\n\t\t\tconst out: string[] = [];\n\t\t\tconst walk = (node: Record<string, unknown>) => {\n\t\t\t\tconst base = strField(node, \"absPath\") ?? \"\";\n\t\t\t\tconst files = Array.isArray(node[\"childrenFiles\"])\n\t\t\t\t\t? node[\"childrenFiles\"]\n\t\t\t\t\t: [];\n\t\t\t\tfor (const f of files) {\n\t\t\t\t\tconst name = strField(f, \"name\");\n\t\t\t\t\tif (name) out.push(`${base}/${name}`);\n\t\t\t\t}\n\t\t\t\tconst dirs = Array.isArray(node[\"childrenDirs\"])\n\t\t\t\t\t? node[\"childrenDirs\"]\n\t\t\t\t\t: [];\n\t\t\t\tfor (const d of dirs) {\n\t\t\t\t\tif (!isRecord(d)) continue;\n\t\t\t\t\tout.push(`${strField(d, \"absPath\") ?? \"\"}/`);\n\t\t\t\t\twalk(d);\n\t\t\t\t}\n\t\t\t};\n\t\t\twalk(root);\n\t\t\treturn {\n\t\t\t\ttitle: strField(root, \"absPath\") ?? \"\",\n\t\t\t\tmetadata: {},\n\t\t\t\toutput: out.length > 0 ? out.join(\"\\n\") : \"(empty)\",\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `updateTodos` → opencode `todowrite` (todo checklist renderer).\n\tupdateTodos: {\n\t\ttool: \"todowrite\",\n\t\tinput: (args) => ({ todos: mapTodos(args) }),\n\t\tresult: (_value, args) => {\n\t\t\tconst todos = mapTodos(args);\n\t\t\tconst done = todos.filter((t) => t.status === \"completed\").length;\n\t\t\treturn {\n\t\t\t\ttitle: `${done}/${todos.length}`,\n\t\t\t\tmetadata: { todos },\n\t\t\t\toutput: `Updated ${todos.length} todo${todos.length === 1 ? \"\" : \"s\"}.`,\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `task` (subagent) → opencode `task` (agent card: name + description).\n\t// Non-clickable here — the subagent ran inside Cursor, not as an opencode\n\t// child session — but the native card reads far better than raw JSON.\n\ttask: {\n\t\ttool: \"task\",\n\t\tinput: (args) => {\n\t\t\tconst description = strField(args, \"description\") ?? \"\";\n\t\t\tconst sub = isRecord(args) ? args[\"subagentType\"] : undefined;\n\t\t\tconst subagent =\n\t\t\t\tstrField(sub, \"name\") ?? strField(sub, \"kind\") ?? undefined;\n\t\t\treturn subagent\n\t\t\t\t? { description, subagent_type: subagent }\n\t\t\t\t: { description };\n\t\t},\n\t\tresult: (value, args) => {\n\t\t\tconst description = strField(args, \"description\") ?? \"\";\n\t\t\tconst suffix = strField(value, \"resultSuffix\");\n\t\t\tconst background = isRecord(value) && value[\"isBackground\"] === true;\n\t\t\treturn {\n\t\t\t\ttitle: description,\n\t\t\t\tmetadata: background ? { background: true } : {},\n\t\t\t\toutput: suffix ?? \"Subagent task completed.\",\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `readLints` has no opencode counterpart — format-only: render the\n\t// diagnostics as a readable list instead of dumping the nested JSON.\n\treadLints: {\n\t\tinput: (args) => {\n\t\t\tconst paths =\n\t\t\t\tisRecord(args) && Array.isArray(args[\"paths\"]) ? args[\"paths\"] : [];\n\t\t\treturn { paths };\n\t\t},\n\t\tresult: (value) => {\n\t\t\tconst files =\n\t\t\t\tisRecord(value) && Array.isArray(value[\"fileDiagnostics\"])\n\t\t\t\t\t? (value[\"fileDiagnostics\"] as unknown[])\n\t\t\t\t\t: [];\n\t\t\tconst lines: string[] = [];\n\t\t\tlet total = 0;\n\t\t\tfor (const file of files) {\n\t\t\t\tif (!isRecord(file)) continue;\n\t\t\t\tconst diags = Array.isArray(file[\"diagnostics\"])\n\t\t\t\t\t? (file[\"diagnostics\"] as unknown[])\n\t\t\t\t\t: [];\n\t\t\t\tif (diags.length === 0) continue;\n\t\t\t\tlines.push(`${strField(file, \"path\") ?? \"\"}`);\n\t\t\t\tfor (const d of diags) {\n\t\t\t\t\tif (!isRecord(d)) continue;\n\t\t\t\t\tconst severity = strField(d, \"severity\") ?? \"info\";\n\t\t\t\t\tconst start = isRecord(d[\"range\"]) ? d[\"range\"][\"start\"] : undefined;\n\t\t\t\t\tconst line = numField(start, \"line\");\n\t\t\t\t\tconst char = numField(start, \"character\");\n\t\t\t\t\tconst loc =\n\t\t\t\t\t\tline !== undefined\n\t\t\t\t\t\t\t? ` L${line + 1}${char !== undefined ? `:${char + 1}` : \"\"}`\n\t\t\t\t\t\t\t: \"\";\n\t\t\t\t\tlines.push(` ${severity}${loc}: ${strField(d, \"message\") ?? \"\"}`);\n\t\t\t\t\ttotal++;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\ttitle:\n\t\t\t\t\ttotal > 0\n\t\t\t\t\t\t? `${total} problem${total === 1 ? \"\" : \"s\"}`\n\t\t\t\t\t\t: \"No problems\",\n\t\t\t\tmetadata: { count: total },\n\t\t\t\toutput: total > 0 ? lines.join(\"\\n\") : \"No problems found.\",\n\t\t\t};\n\t\t},\n\t},\n\t// Cursor `delete` has no opencode counterpart — format-only: a one-line\n\t// confirmation instead of `{\"fileSize\":N}`.\n\tdelete: {\n\t\tinput: (args) => ({ path: strField(args, \"path\") ?? \"\" }),\n\t\tresult: (value, args) => {\n\t\t\tconst path = strField(args, \"path\") ?? \"\";\n\t\t\tconst size = numField(value, \"fileSize\");\n\t\t\treturn {\n\t\t\t\ttitle: path,\n\t\t\t\tmetadata: {},\n\t\t\t\toutput:\n\t\t\t\t\tsize !== undefined\n\t\t\t\t\t\t? `Deleted ${path} (${size} bytes).`\n\t\t\t\t\t\t: `Deleted ${path}.`,\n\t\t\t};\n\t\t},\n\t},\n};\n\n/** Extract the edit target path from Cursor's edit tool-call args (`{ path }`). */\nfunction editFilePath(input: unknown): string {\n\treturn isRecord(input) && typeof input[\"path\"] === \"string\"\n\t\t? input[\"path\"]\n\t\t: \"\";\n}\n\n/**\n * If `result` is a successful Cursor edit result carrying a unified diff, return\n * its `diffString`; otherwise null (caller falls back to a safe generic block).\n * Cursor shape: `{ status:\"success\", value:{ diffString?, linesAdded?, linesRemoved? } }`.\n */\nfunction editDiffString(result: unknown): string | null {\n\tif (!isRecord(result) || result[\"status\"] !== \"success\") return null;\n\tconst value = result[\"value\"];\n\tif (!isRecord(value)) return null;\n\tconst diff = value[\"diffString\"];\n\treturn typeof diff === \"string\" && diff.length > 0 ? diff : null;\n}\n\n/**\n * Reconstruct opencode `edit` `{oldString,newString}` from a unified diff:\n * removed (`-`) lines → oldString, added (`+`) lines → newString (file/hunk\n * headers skipped). Faithful for a single hunk; approximate (concatenated)\n * across multiple hunks. Used only to satisfy opencode's edit input schema — the\n * call is provider-executed, so these strings are never applied to disk; the\n * rendered diff comes from `metadata.diff`.\n */\nfunction reconstructEditStrings(diff: string): {\n\toldString: string;\n\tnewString: string;\n} {\n\tconst oldLines: string[] = [];\n\tconst newLines: string[] = [];\n\tfor (const line of diff.split(\"\\n\")) {\n\t\tif (\n\t\t\tline.startsWith(\"---\") ||\n\t\t\tline.startsWith(\"+++\") ||\n\t\t\tline.startsWith(\"@@\") ||\n\t\t\tline.startsWith(\"Index:\") ||\n\t\t\tline.startsWith(\"===\")\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (line.startsWith(\"-\")) oldLines.push(line.slice(1));\n\t\telse if (line.startsWith(\"+\")) newLines.push(line.slice(1));\n\t}\n\treturn { oldString: oldLines.join(\"\\n\"), newString: newLines.join(\"\\n\") };\n}\n\n/**\n * Build the opencode-native `edit` `tool-call` payload for a completed Cursor\n * edit. Emitted under the registered name `edit` (so opencode's diff viewer\n * renders) with a schema-valid `{filePath, oldString, newString}` input. Still\n * carries `providerExecuted` + `dynamic` so a host without a registered `edit`\n * tool degrades to a dynamic generic block instead of erroring.\n */\nfunction editCallFields(\n\tid: string,\n\tfilePath: string,\n\tdiff: string,\n): BlockToolPart {\n\tconst { oldString, newString } = reconstructEditStrings(diff);\n\treturn {\n\t\ttype: \"tool-call\",\n\t\ttoolCallId: id,\n\t\ttoolName: EDIT_TOOL_NAME,\n\t\tinput: safeJsonString({ filePath, oldString, newString }),\n\t\tproviderExecuted: true,\n\t\tdynamic: true,\n\t} as BlockToolPart;\n}\n\n/**\n * Build the opencode-native `edit` `tool-result` payload. opencode's processor\n * folds a tool-result's payload into `state.{title,metadata,output}`; the Edit\n * renderer's diff viewer keys on `metadata.diff`.\n */\nfunction editResultFields(\n\tid: string,\n\tfilePath: string,\n\tdiff: string,\n\tresult: unknown,\n): BlockToolPart {\n\tconst value = isRecord(result) ? result[\"value\"] : undefined;\n\tconst added =\n\t\tisRecord(value) && typeof value[\"linesAdded\"] === \"number\"\n\t\t\t? value[\"linesAdded\"]\n\t\t\t: undefined;\n\tconst removed =\n\t\tisRecord(value) && typeof value[\"linesRemoved\"] === \"number\"\n\t\t\t? value[\"linesRemoved\"]\n\t\t\t: undefined;\n\tconst counts =\n\t\tadded !== undefined || removed !== undefined\n\t\t\t? ` (+${added ?? 0}/-${removed ?? 0})`\n\t\t\t: \"\";\n\treturn {\n\t\ttype: \"tool-result\" as const,\n\t\ttoolCallId: id,\n\t\ttoolName: EDIT_TOOL_NAME,\n\t\tresult: {\n\t\t\ttitle: filePath,\n\t\t\tmetadata: { diff, diagnostics: {} },\n\t\t\toutput: `Edit applied${counts}.`,\n\t\t} as never,\n\t\tisError: false,\n\t\tproviderExecuted: true,\n\t\tdynamic: true,\n\t} as BlockToolPart;\n}\n\n/**\n * Per-turn blocks-mode tool bookkeeping, shared by the streaming and\n * `doGenerate` paths:\n * - `open`: non-edit calls awaiting their result. Stores the FINAL emitted tool\n * name (native, e.g. `bash`, or generic `cursor_*`), the native adapter (if\n * any) used to fold the result, and the original Cursor args (some adapters\n * build their result from the call args, e.g. `write`/`updateTodos`).\n * - `pendingEdits`: edit calls held until their result, which carries the diff\n * needed to emit a schema-valid native `edit` call (id → filePath).\n */\ninterface OpenToolCall {\n\ttoolName: string;\n\tadapter?: NativeToolAdapter;\n\targs: unknown;\n}\n\ninterface BlockToolState {\n\topen: Map<string, OpenToolCall>;\n\tpendingEdits: Map<string, string>;\n\tdropped: Set<string>;\n}\n\nfunction newBlockToolState(): BlockToolState {\n\treturn { open: new Map(), pendingEdits: new Map(), dropped: new Set() };\n}\n\n/** Parts to emit for a blocks-mode `tool-call` event (edits are buffered). */\nfunction blockToolCallParts(\n\tid: string,\n\tname: string,\n\tinput: unknown,\n\tstate: BlockToolState,\n): BlockToolPart[] {\n\tif (name === EDIT_TOOL_NAME) {\n\t\t// Hold the edit call until its result (which carries the diff).\n\t\tstate.pendingEdits.set(id, editFilePath(input));\n\t\treturn [];\n\t}\n\tconst adapter = resolveAdapter(name, input);\n\tif (adapter) {\n\t\t// Adapter mapping: native registered tool (`adapter.tool`) when there's a\n\t\t// renderer to reuse, else a `cursor_*` block whose result is still folded\n\t\t// into clean output (format-only adapters).\n\t\tconst toolName = adapter.tool ?? blockToolName(name);\n\t\tstate.open.set(id, { toolName, adapter, args: input });\n\t\treturn [nativeToolCall(id, toolName, adapter.input(input))];\n\t}\n\t// No adapter: generic prefixed block.\n\tconst toolName = blockToolName(name);\n\tstate.open.set(id, { toolName, args: input });\n\treturn [nativeToolCall(id, toolName, input)];\n}\n\n/** Parts to emit for a blocks-mode `tool-result` event. */\nfunction blockToolResultParts(\n\tid: string,\n\tname: string,\n\tresult: unknown,\n\tisError: boolean,\n\tstate: BlockToolState,\n): BlockToolPart[] {\n\tif (state.pendingEdits.has(id)) {\n\t\tconst filePath = state.pendingEdits.get(id)!;\n\t\tstate.pendingEdits.delete(id);\n\t\tconst diff = isError ? null : editDiffString(result);\n\t\tif (diff && filePath) {\n\t\t\t// Native edit: opencode renders its built-in diff viewer.\n\t\t\treturn [\n\t\t\t\teditCallFields(id, filePath, diff),\n\t\t\t\teditResultFields(id, filePath, diff, result),\n\t\t\t];\n\t\t}\n\t\t// No usable diff (error / unexpected shape): safe generic fallback.\n\t\treturn [\n\t\t\ttoolCallObj(id, EDIT_TOOL_NAME, { path: filePath }),\n\t\t\ttoolResultObj(id, EDIT_TOOL_NAME, result, isError),\n\t\t];\n\t}\n\tconst open = state.open.get(id);\n\tstate.open.delete(id);\n\tconst toolName = open?.toolName ?? blockToolName(name);\n\tif (open?.adapter && !isError) {\n\t\tconst value = successValue(result);\n\t\tif (value !== undefined) {\n\t\t\tconst folded = open.adapter.result(value, open.args);\n\t\t\tif (folded) return [nativeToolResult(id, toolName, folded, false)];\n\t\t}\n\t}\n\tif (!isError) {\n\t\t// No adapter (or it declined): if this is an MCP result, fold its `content`\n\t\t// into readable text so the block isn't a raw JSON dump.\n\t\tconst folded = mcpFold(result);\n\t\tif (folded) return [nativeToolResult(id, toolName, folded, false)];\n\t}\n\t// Generic block / error / unexpected shape: emit the raw Cursor result under\n\t// the (already-resolved) tool name.\n\treturn [nativeToolResult(id, toolName, result, isError)];\n}\n\n/**\n * Parts that close out any tool call whose completion never arrived (run\n * errored/cancelled mid-tool) so blocks never dangle as \"Tool execution\n * aborted\". Clears the state.\n */\nfunction blockDanglingParts(state: BlockToolState): BlockToolPart[] {\n\tconst parts: BlockToolPart[] = [];\n\tfor (const [id, open] of state.open) {\n\t\tparts.push(nativeToolResult(id, open.toolName, DANGLING_TOOL_RESULT, true));\n\t}\n\tstate.open.clear();\n\t// Edits whose result never arrived: safe generic block + synthetic error\n\t// (no diff available to build a native edit).\n\tfor (const [id, filePath] of state.pendingEdits) {\n\t\tparts.push(toolCallObj(id, EDIT_TOOL_NAME, { path: filePath }));\n\t\tparts.push(toolResultObj(id, EDIT_TOOL_NAME, DANGLING_TOOL_RESULT, true));\n\t}\n\tstate.pendingEdits.clear();\n\treturn parts;\n}\n\nexport const EMPTY_USAGE: LanguageModelV3Usage = {\n\tinputTokens: {\n\t\ttotal: undefined,\n\t\tnoCache: undefined,\n\t\tcacheRead: undefined,\n\t\tcacheWrite: undefined,\n\t},\n\toutputTokens: { total: undefined, text: undefined, reasoning: undefined },\n};\n\nexport function mapUsage(usage: CursorUsage): LanguageModelV3Usage {\n\treturn {\n\t\tinputTokens: {\n\t\t\ttotal: usage.inputTokens,\n\t\t\tnoCache: undefined,\n\t\t\tcacheRead: usage.cacheReadTokens,\n\t\t\tcacheWrite: usage.cacheWriteTokens,\n\t\t},\n\t\toutputTokens: {\n\t\t\ttotal: usage.outputTokens,\n\t\t\ttext: undefined,\n\t\t\treasoning: undefined,\n\t\t},\n\t};\n}\n\n/**\n * Render Cursor's internal tool activity as a short, human-readable line.\n *\n * Cursor runs its own agent loop and executes its own tools (shell/read/edit/\n * mcp/…). We surface that activity as reasoning text — NOT as AI-SDK\n * `tool-call`/`tool-result` parts. opencode (a V3-native host) only treats\n * registered tools as callable; a provider-executed call naming a tool it\n * doesn't know (e.g. `mcp`, `shell`) is rejected as an \"unavailable tool\".\n * Rendering as reasoning keeps the activity visible without crossing the\n * tool-execution boundary. Tool outputs can be huge (file contents, search\n * dumps), so only the call (name + short arg summary) and error status are\n * shown — never the raw result.\n */\nfunction formatToolCall(name: string, input: unknown): string {\n\tlet arg = \"\";\n\ttry {\n\t\tconst s = typeof input === \"string\" ? input : JSON.stringify(input);\n\t\tif (s && s !== \"{}\" && s !== '\"\"')\n\t\t\targ = ` ${s.length > 120 ? `${s.slice(0, 120)}…` : s}`;\n\t} catch {\n\t\t// Non-serializable input; show the name only.\n\t}\n\treturn `[tool] ${name}${arg}`;\n}\n\n/**\n * Synthetic error payload for a tool call whose completion never arrived\n * (run errored/cancelled/wedged mid-tool). Mirrors Cursor's own\n * `{status:\"error\"}` result union so consumers see a consistent shape.\n * Without a matching result, opencode renders the part as\n * \"Tool execution aborted\" and the block dangles forever.\n */\nconst DANGLING_TOOL_RESULT = {\n\tstatus: \"error\",\n\terror: \"Cursor run ended before this tool call completed.\",\n};\n\n/**\n * Translate the normalized Cursor agent events into an AI-SDK V3 stream.\n *\n * Pure with respect to the event source, so it can be tested by feeding a\n * fixed event sequence (no live agent required). Reasoning blocks are closed\n * before text begins so reasoning/text parts nest cleanly. Tool activity is\n * surfaced per {@link ToolDisplay} (default `\"blocks\"`): structured tool parts,\n * or reasoning lines when `\"reasoning\"` (see {@link formatToolCall}).\n */\nexport function cursorEventsToStream(\n\tevents: AsyncIterable<CursorEvent>,\n\ttoolDisplay: ToolDisplay = \"blocks\",\n): ReadableStream<LanguageModelV3StreamPart> {\n\treturn new ReadableStream<LanguageModelV3StreamPart>({\n\t\tasync start(controller) {\n\t\t\tcontroller.enqueue({ type: \"stream-start\", warnings: [] });\n\n\t\t\tlet textId: string | undefined;\n\t\t\tlet textCount = 0;\n\t\t\tlet reasoningId: string | undefined;\n\t\t\tlet reasoningCount = 0;\n\t\t\tlet usage: LanguageModelV3Usage | undefined;\n\t\t\tlet streamedText = false;\n\t\t\t// Blocks-mode tool bookkeeping (open non-edit calls + buffered edits).\n\t\t\tconst toolState = newBlockToolState();\n\t\t\tconst closeDanglingToolCalls = () => {\n\t\t\t\tfor (const part of blockDanglingParts(toolState)) {\n\t\t\t\t\tcontroller.enqueue(part);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst closeReasoning = () => {\n\t\t\t\tif (reasoningId) {\n\t\t\t\t\tcontroller.enqueue({ type: \"reasoning-end\", id: reasoningId });\n\t\t\t\t\treasoningId = undefined;\n\t\t\t\t}\n\t\t\t};\n\t\t\t// Close the open text part when reasoning resumes: hosts position a part\n\t\t\t// where it STARTED, so appending later text to an earlier part would\n\t\t\t// render the final answer above the reasoning that preceded it.\n\t\t\tconst closeText = () => {\n\t\t\t\tif (textId) {\n\t\t\t\t\tcontroller.enqueue({ type: \"text-end\", id: textId });\n\t\t\t\t\ttextId = undefined;\n\t\t\t\t}\n\t\t\t};\n\t\t\tconst ensureText = () => {\n\t\t\t\tcloseReasoning();\n\t\t\t\tif (!textId) {\n\t\t\t\t\ttextId = `text-${textCount++}`;\n\t\t\t\t\tcontroller.enqueue({ type: \"text-start\", id: textId });\n\t\t\t\t}\n\t\t\t\treturn textId;\n\t\t\t};\n\t\t\tconst ensureReasoning = () => {\n\t\t\t\tcloseText();\n\t\t\t\tif (!reasoningId) {\n\t\t\t\t\treasoningId = `reasoning-${reasoningCount++}`;\n\t\t\t\t\tcontroller.enqueue({ type: \"reasoning-start\", id: reasoningId });\n\t\t\t\t}\n\t\t\t\treturn reasoningId;\n\t\t\t};\n\t\t\tconst reasoningLine = (text: string) => {\n\t\t\t\tcontroller.enqueue({\n\t\t\t\t\ttype: \"reasoning-delta\",\n\t\t\t\t\tid: ensureReasoning(),\n\t\t\t\t\tdelta: text,\n\t\t\t\t});\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\tfor await (const event of events) {\n\t\t\t\t\tswitch (event.type) {\n\t\t\t\t\t\tcase \"text-delta\":\n\t\t\t\t\t\t\tstreamedText = true;\n\t\t\t\t\t\t\tcontroller.enqueue({\n\t\t\t\t\t\t\t\ttype: \"text-delta\",\n\t\t\t\t\t\t\t\tid: ensureText(),\n\t\t\t\t\t\t\t\tdelta: event.text,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"reasoning-delta\":\n\t\t\t\t\t\t\treasoningLine(event.text);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"tool-call\":\n\t\t\t\t\t\t\tif (isCreatePlanTool(event.name)) {\n\t\t\t\t\t\t\t\tconst plan = createPlanContent(event.input);\n\t\t\t\t\t\t\t\tif (plan) {\n\t\t\t\t\t\t\t\t\tcloseReasoning();\n\t\t\t\t\t\t\t\t\tstreamedText = true;\n\t\t\t\t\t\t\t\t\tcontroller.enqueue({\n\t\t\t\t\t\t\t\t\t\ttype: \"text-delta\",\n\t\t\t\t\t\t\t\t\t\tid: ensureText(),\n\t\t\t\t\t\t\t\t\t\tdelta: plan,\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\ttoolState.dropped.add(event.id);\n\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (toolDisplay === \"blocks\") {\n\t\t\t\t\t\t\t\tconst parts = blockToolCallParts(\n\t\t\t\t\t\t\t\t\tevent.id,\n\t\t\t\t\t\t\t\t\tevent.name,\n\t\t\t\t\t\t\t\t\tevent.input,\n\t\t\t\t\t\t\t\t\ttoolState,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t// Edit calls buffer until their result (no parts yet) — keep\n\t\t\t\t\t\t\t\t// the open narration part alive across the gap. Every other\n\t\t\t\t\t\t\t\t// tool emits immediately, so close open text/reasoning first\n\t\t\t\t\t\t\t\t// so post-tool narration lands in a later part (hosts position\n\t\t\t\t\t\t\t\t// parts where they START).\n\t\t\t\t\t\t\t\tif (parts.length > 0) {\n\t\t\t\t\t\t\t\t\tcloseText();\n\t\t\t\t\t\t\t\t\tcloseReasoning();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfor (const part of parts) {\n\t\t\t\t\t\t\t\t\tcontroller.enqueue(part);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\treasoningLine(`\\n${formatToolCall(event.name, event.input)}\\n`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"tool-result\":\n\t\t\t\t\t\t\tif (toolState.dropped.delete(event.id)) break;\n\t\t\t\t\t\t\tif (toolDisplay === \"blocks\") {\n\t\t\t\t\t\t\t\tconst parts = blockToolResultParts(\n\t\t\t\t\t\t\t\t\tevent.id,\n\t\t\t\t\t\t\t\t\tevent.name,\n\t\t\t\t\t\t\t\t\tevent.result,\n\t\t\t\t\t\t\t\t\tevent.isError,\n\t\t\t\t\t\t\t\t\ttoolState,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tif (parts.length > 0) {\n\t\t\t\t\t\t\t\t\tcloseText();\n\t\t\t\t\t\t\t\t\tcloseReasoning();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tfor (const part of parts) {\n\t\t\t\t\t\t\t\t\tcontroller.enqueue(part);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else if (event.isError) {\n\t\t\t\t\t\t\t\treasoningLine(`[tool] ${event.name} failed\\n`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"usage\":\n\t\t\t\t\t\t\tusage = mapUsage(event.usage);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"finish\":\n\t\t\t\t\t\t\tif (!streamedText && event.text) {\n\t\t\t\t\t\t\t\tcontroller.enqueue({\n\t\t\t\t\t\t\t\t\ttype: \"text-delta\",\n\t\t\t\t\t\t\t\t\tid: ensureText(),\n\t\t\t\t\t\t\t\t\tdelta: event.text,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tcloseDanglingToolCalls();\n\t\t\t\tcloseReasoning();\n\t\t\t\tcloseText();\n\t\t\t\tcontroller.enqueue({\n\t\t\t\t\ttype: \"finish\",\n\t\t\t\t\tusage: usage ?? EMPTY_USAGE,\n\t\t\t\t\tfinishReason: FINISH_STOP,\n\t\t\t\t});\n\t\t\t\tcontroller.close();\n\t\t\t} catch (err) {\n\t\t\t\tcontroller.enqueue({ type: \"error\", error: err });\n\t\t\t\tcloseDanglingToolCalls();\n\t\t\t\tcloseReasoning();\n\t\t\t\tcloseText();\n\t\t\t\tcontroller.enqueue({\n\t\t\t\t\ttype: \"finish\",\n\t\t\t\t\tusage: usage ?? EMPTY_USAGE,\n\t\t\t\t\tfinishReason: FINISH_ERROR,\n\t\t\t\t});\n\t\t\t\tcontroller.close();\n\t\t\t}\n\t\t},\n\t});\n}\n\n/**\n * Aggregate the normalized Cursor agent events into a non-streaming result for\n * `doGenerate`. Same event source contract as {@link cursorEventsToStream}\n * (consumed via `for await`). Tool activity is surfaced per {@link ToolDisplay}\n * (default `\"blocks\"`): structured tool parts (see {@link blockToolCallParts} /\n * {@link blockToolResultParts}), or folded into the reasoning text when\n * `\"reasoning\"`.\n */\nexport async function cursorEventsToContent(\n\tevents: AsyncIterable<CursorEvent>,\n\ttoolDisplay: ToolDisplay = \"blocks\",\n): Promise<{\n\tcontent: Array<LanguageModelV3Content>;\n\tfinishReason: LanguageModelV3FinishReason;\n\tusage: LanguageModelV3Usage;\n}> {\n\tconst content: Array<LanguageModelV3Content> = [];\n\tconst toolParts: Array<LanguageModelV3Content> = [];\n\t// Blocks-mode tool bookkeeping (open non-edit calls + buffered edits).\n\tconst toolState = newBlockToolState();\n\tlet text = \"\";\n\tlet reasoning = \"\";\n\tlet usage: LanguageModelV3Usage = EMPTY_USAGE;\n\tlet finishReason: LanguageModelV3FinishReason = FINISH_STOP;\n\n\ttry {\n\t\tfor await (const event of events) {\n\t\t\tswitch (event.type) {\n\t\t\t\tcase \"text-delta\":\n\t\t\t\t\ttext += event.text;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"reasoning-delta\":\n\t\t\t\t\treasoning += event.text;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tool-call\":\n\t\t\t\t\tif (isCreatePlanTool(event.name)) {\n\t\t\t\t\t\tconst plan = createPlanContent(event.input);\n\t\t\t\t\t\tif (plan) text += plan;\n\t\t\t\t\t\ttoolState.dropped.add(event.id);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tif (toolDisplay === \"blocks\") {\n\t\t\t\t\t\tfor (const part of blockToolCallParts(\n\t\t\t\t\t\t\tevent.id,\n\t\t\t\t\t\t\tevent.name,\n\t\t\t\t\t\t\tevent.input,\n\t\t\t\t\t\t\ttoolState,\n\t\t\t\t\t\t)) {\n\t\t\t\t\t\t\ttoolParts.push(part as LanguageModelV3Content);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\treasoning += `\\n${formatToolCall(event.name, event.input)}\\n`;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"tool-result\":\n\t\t\t\t\tif (toolState.dropped.delete(event.id)) break;\n\t\t\t\t\tif (toolDisplay === \"blocks\") {\n\t\t\t\t\t\tfor (const part of blockToolResultParts(\n\t\t\t\t\t\t\tevent.id,\n\t\t\t\t\t\t\tevent.name,\n\t\t\t\t\t\t\tevent.result,\n\t\t\t\t\t\t\tevent.isError,\n\t\t\t\t\t\t\ttoolState,\n\t\t\t\t\t\t)) {\n\t\t\t\t\t\t\ttoolParts.push(part as LanguageModelV3Content);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (event.isError) {\n\t\t\t\t\t\treasoning += `[tool] ${event.name} failed\\n`;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"usage\":\n\t\t\t\t\tusage = mapUsage(event.usage);\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"finish\":\n\t\t\t\t\tif (!text && event.text) text = event.text;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t} catch {\n\t\tfinishReason = FINISH_ERROR;\n\t}\n\n\t// Close out any tool call whose completion never arrived (see DANGLING_TOOL_RESULT).\n\tfor (const part of blockDanglingParts(toolState)) {\n\t\ttoolParts.push(part as LanguageModelV3Content);\n\t}\n\n\tif (reasoning) content.push({ type: \"reasoning\", text: reasoning });\n\tcontent.push(...toolParts);\n\tif (text) content.push({ type: \"text\", text });\n\n\treturn { content, finishReason, usage };\n}\n","import { createHash } from \"node:crypto\";\nimport type { LanguageModelV3Prompt } from \"@ai-sdk/provider\";\nimport type { McpServerConfig } from \"@cursor/sdk\";\n\n/**\n * Per-session bookkeeping that lets the provider decide, on each turn, whether\n * it can safely resume the pooled Cursor agent (and send only the new message)\n * or must start fresh and re-send the whole transcript.\n *\n * We hash ONLY the parts opencode replays verbatim — the system prompt and the\n * user messages. We deliberately do NOT hash assistant output: opencode\n * re-serializes our streamed reply (reasoning, tool blocks) back into the next\n * prompt in a shape we can't predict byte-for-byte, so hashing it would\n * spuriously mismatch every turn and silently collapse to always-full-replay.\n * The system prompt + user-message sequence is the stable identity of a\n * conversation.\n */\nexport interface TranscriptRecord {\n\t/** Cursor agentId currently pooled for the session. */\n\tagentId: string;\n\t/** Hash of the concatenated system message content. */\n\tsystemHash: string;\n\t/** Ordered hash per user message (text + a stable image token). */\n\tuserHashes: string[];\n\t/**\n\t * Hash of the MCP server set the pooled agent was created with. A resumed\n\t * Cursor agent keeps its original MCP servers, so when this changes between\n\t * turns the pool must create a fresh agent rather than resume.\n\t */\n\tmcpHash?: string;\n}\n\n/** What kind of turn this is relative to the session's last recorded state. */\nexport type TurnKind =\n\t/** No prior record: first turn of the session. */\n\t| \"new\"\n\t/** System prompt differs (opencode's non-chat side call, e.g. title gen). */\n\t| \"side-call\"\n\t/** Prior user sequence is a strict prefix + exactly one new trailing user msg. */\n\t| \"continuation\"\n\t/** Edit / revert / compaction / multiple queued msgs — prior prefix no longer holds. */\n\t| \"divergence\";\n\nexport interface TurnClassification {\n\tkind: TurnKind;\n\t/** Fingerprint of the CURRENT prompt, to store when (re)pooling. */\n\tfingerprint: { systemHash: string; userHashes: string[] };\n}\n\nfunction sha(input: string): string {\n\treturn createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\n/**\n * Stable hash of the MCP server set handed to `Agent.create`. Keys are sorted\n * so map ordering never changes the result; empty/undefined sets hash to \"\".\n */\nexport function mcpServersFingerprint(\n\tservers: Record<string, McpServerConfig> | undefined,\n): string {\n\tif (!servers) return \"\";\n\tconst keys = Object.keys(servers).sort();\n\tif (keys.length === 0) return \"\";\n\treturn sha(JSON.stringify(keys.map((k) => [k, servers[k]])));\n}\n\n/** Stable key for one user message: its text plus a token per attached image. */\nfunction userMessageKey(\n\tmessage: Extract<LanguageModelV3Prompt[number], { role: \"user\" }>,\n): string {\n\tconst parts: string[] = [];\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\") parts.push(`t:${part.text}`);\n\t\telse if (part.type === \"file\") parts.push(`f:${part.mediaType}`);\n\t}\n\treturn parts.join(\"\\n\");\n}\n\n/** Compute the system + user-message fingerprint of a prompt. */\nexport function fingerprint(prompt: LanguageModelV3Prompt): {\n\tsystemHash: string;\n\tuserHashes: string[];\n} {\n\tconst systemParts: string[] = [];\n\tconst userHashes: string[] = [];\n\tfor (const message of prompt) {\n\t\tif (message.role === \"system\") systemParts.push(message.content);\n\t\telse if (message.role === \"user\")\n\t\t\tuserHashes.push(sha(userMessageKey(message)));\n\t}\n\treturn { systemHash: sha(systemParts.join(\"\\n\")), userHashes };\n}\n\n/** True when `prefix` is a strict element-wise prefix of `full`. */\nfunction isStrictPrefix(prefix: string[], full: string[]): boolean {\n\tif (prefix.length >= full.length) return false;\n\tfor (let i = 0; i < prefix.length; i++) {\n\t\tif (prefix[i] !== full[i]) return false;\n\t}\n\treturn true;\n}\n\n/**\n * Classify the current turn against the session's last recorded fingerprint.\n *\n * Order matters:\n * 1. no record -> \"new\"\n * 2. system prompt changed -> \"side-call\" (don't touch the pool)\n * 3. prior user hashes are a strict prefix AND exactly one new trailing user\n * message AND the last prompt message is a user turn -> \"continuation\"\n * 4. otherwise -> \"divergence\" (edit/revert/compaction/queued)\n *\n * Worst case on any misclassification is a single wasted full replay that\n * self-heals on the next turn — never worse than the `session: false` default.\n */\nexport function classifyTurn(\n\tprev: TranscriptRecord | undefined,\n\tprompt: LanguageModelV3Prompt,\n): TurnClassification {\n\tconst fp = fingerprint(prompt);\n\tif (!prev) return { kind: \"new\", fingerprint: fp };\n\tif (prev.systemHash !== fp.systemHash)\n\t\treturn { kind: \"side-call\", fingerprint: fp };\n\n\tconst lastIsUser = prompt[prompt.length - 1]?.role === \"user\";\n\tconst exactlyOneNew = fp.userHashes.length === prev.userHashes.length + 1;\n\tif (\n\t\tlastIsUser &&\n\t\texactlyOneNew &&\n\t\tisStrictPrefix(prev.userHashes, fp.userHashes)\n\t) {\n\t\treturn { kind: \"continuation\", fingerprint: fp };\n\t}\n\treturn { kind: \"divergence\", fingerprint: fp };\n}\n"],"mappings":"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","names":["toolName"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stablekernel/opencode-cursor",
3
- "version": "0.4.2",
3
+ "version": "0.4.4-next.0",
4
4
  "description": "opencode provider plugin backed by the official Cursor SDK (@cursor/sdk) — adds a Cursor provider and lists its models",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -55,9 +55,9 @@
55
55
  "prepublishOnly": "npm run typecheck && npm test && npm run build"
56
56
  },
57
57
  "dependencies": {
58
- "@connectrpc/connect-node": "^1.6.1",
59
- "@cursor/sdk": "^1.0.18",
60
- "@opencode-ai/plugin": "^1.17.7"
58
+ "@connectrpc/connect-node": "^2.1.2",
59
+ "@cursor/sdk": "^1.0.20",
60
+ "@opencode-ai/plugin": "^1.17.9"
61
61
  },
62
62
  "overrides": {
63
63
  "undici": "^6.24.0",
@@ -69,8 +69,8 @@
69
69
  },
70
70
  "devDependencies": {
71
71
  "@ai-sdk/provider": "^3.0.10",
72
- "@opencode-ai/sdk": "^1.17.1",
73
- "@types/node": "^25.9.2",
72
+ "@opencode-ai/sdk": "^1.17.9",
73
+ "@types/node": "^26.0.0",
74
74
  "tsup": "^8.5.1",
75
75
  "typescript": "^6.0.3",
76
76
  "vitest": "^4.1.8"