@stablekernel/opencode-cursor 0.7.0 → 0.7.1-next.1
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.
- package/CHANGELOG.md +56 -0
- package/README.md +87 -4
- package/dist/{chunk-COE6ZXMP.js → chunk-RDY3H2LE.js} +7 -6
- package/dist/chunk-RDY3H2LE.js.map +1 -0
- package/dist/plugin/index.js +795 -8
- package/dist/plugin/index.js.map +1 -1
- package/dist/provider/index.d.ts +7 -0
- package/dist/provider/index.js +16 -6
- package/dist/provider/index.js.map +1 -1
- package/package.json +2 -1
- package/dist/chunk-COE6ZXMP.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stablekernel/opencode-cursor",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1-next.1",
|
|
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",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"test": "vitest run",
|
|
54
54
|
"test:watch": "vitest",
|
|
55
55
|
"test:e2e": "vitest run --config vitest.e2e.config.ts --passWithNoTests",
|
|
56
|
+
"sync:model-limits": "node scripts/sync-model-limits-cli.mjs",
|
|
56
57
|
"prepublishOnly": "npm run typecheck && npm test && npm run build"
|
|
57
58
|
},
|
|
58
59
|
"dependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/api-key.ts","../src/provider/log-bridge.ts","../src/provider/agent-backend.ts","../src/cursor-runtime.ts","../src/provider/cursor-log-intercept.ts","../src/provider/sidecar-client.ts","../src/provider/system-rule.ts","../src/provider/subagent-bridge.ts","../src/provider/error-classify.ts","../src/provider/agent-events.ts","../src/provider/controls.ts","../src/provider/session-store.ts","../src/provider/session-pool.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\n/** Environment variable the Cursor SDK itself reads as a fallback. */\nexport const CURSOR_API_KEY_ENV_VAR = \"CURSOR_API_KEY\";\n\n/**\n * Values that are *not* real keys but rather instructions to read the key from\n * the environment. opencode config commonly stores literal `{env:...}` style\n * placeholders, and users sometimes paste the variable name itself.\n */\nconst PLACEHOLDERS = new Set<string>([\n CURSOR_API_KEY_ENV_VAR,\n `$${CURSOR_API_KEY_ENV_VAR}`,\n `\\${${CURSOR_API_KEY_ENV_VAR}}`,\n]);\n\n/**\n * Resolve a usable Cursor API key.\n *\n * Resolution order: an explicit, non-placeholder candidate (e.g. from opencode\n * auth storage or provider options) wins; otherwise fall back to the\n * `CURSOR_API_KEY` environment variable. Returns `undefined` when no key is\n * available so callers can present a clear \"needs auth\" path.\n *\n * The key is never logged or persisted by this module.\n */\nexport function resolveCursorApiKey(candidate?: string | null): string | undefined {\n const trimmed = candidate?.trim();\n if (trimmed && !PLACEHOLDERS.has(trimmed)) return trimmed;\n const fromEnv = process.env[CURSOR_API_KEY_ENV_VAR]?.trim();\n return fromEnv ? fromEnv : undefined;\n}\n\n/**\n * Produce a short, non-reversible fingerprint of an API key. Used purely to key\n * the on-disk model cache so the cache invalidates when the key changes. The\n * raw key is never written to disk.\n */\nexport function fingerprintApiKey(apiKey: string): string {\n return createHash(\"sha256\").update(apiKey).digest(\"hex\").slice(0, 16);\n}\n","import type { OpencodeClient } from \"@opencode-ai/sdk\";\n\nexport type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nexport interface LogBridge {\n\tclient: OpencodeClient;\n\t/** Workspace directory forwarded on each log call. */\n\tdirectory?: string;\n}\n\nconst BRIDGE_KEY = Symbol.for(\"@stablekernel/opencode-cursor:log-bridge\");\n\ntype BridgeHolder = { [BRIDGE_KEY]?: LogBridge };\n\n/** Publish the opencode client + directory for the provider to log through. */\nexport function setLogBridge(bridge: LogBridge): void {\n\t(globalThis as BridgeHolder)[BRIDGE_KEY] = bridge;\n}\n\n/** Drop the bridge (plugin dispose). */\nexport function clearLogBridge(): void {\n\tdelete (globalThis as BridgeHolder)[BRIDGE_KEY];\n}\n\n/** Read the current bridge, or `undefined` when the plugin hasn't published one. */\nexport function getLogBridge(): LogBridge | undefined {\n\treturn (globalThis as BridgeHolder)[BRIDGE_KEY];\n}\n\nconst SERVICE = \"opencode-cursor\";\n\n/**\n * Structured log emission for the provider layer, which has no direct access\n * to the opencode client. Routes through `client.app.log()` (see\n * `plugins.mdx`) when the plugin has published a bridge via\n * {@link setLogBridge}; otherwise falls back to `console.*` so the provider\n * still surfaces diagnostics when used standalone (tests, scripts, or the\n * provider package without the plugin).\n *\n * Best-effort: a failed `app.log` call (e.g. server unavailable) is swallowed\n * rather than thrown, matching every other fire-and-forget client call in\n * this plugin.\n */\nexport function pluginLog(\n\tlevel: LogLevel,\n\tmessage: string,\n\textra?: Record<string, unknown>,\n): void {\n\tconst bridge = getLogBridge();\n\tif (bridge) {\n\t\tvoid bridge.client.app\n\t\t\t.log({\n\t\t\t\tbody: {\n\t\t\t\t\tservice: SERVICE,\n\t\t\t\t\tlevel,\n\t\t\t\t\tmessage,\n\t\t\t\t\t...(extra ? { extra } : {}),\n\t\t\t\t},\n\t\t\t\t...(bridge.directory ? { query: { directory: bridge.directory } } : {}),\n\t\t\t})\n\t\t\t.catch(() => {});\n\t\treturn;\n\t}\n\tconst line = extra\n\t\t? `[${SERVICE}] ${message} ${JSON.stringify(extra)}`\n\t\t: `[${SERVICE}] ${message}`;\n\tswitch (level) {\n\t\tcase \"debug\":\n\t\t\tconsole.debug(line);\n\t\t\tbreak;\n\t\tcase \"info\":\n\t\t\tconsole.info(line);\n\t\t\tbreak;\n\t\tcase \"warn\":\n\t\t\tconsole.warn(line);\n\t\t\tbreak;\n\t\tcase \"error\":\n\t\t\tconsole.error(line);\n\t\t\tbreak;\n\t}\n}\n","/**\n * Selects where Cursor agents run — three transports:\n *\n * - \"http1\": in-process via `@cursor/sdk` with\n * `Cursor.configure({ local: { useHttp1ForAgent: true } })` — HTTP/1.1 + SSE,\n * the Bun-safe path (Bun's `node:http2` bug kills Cursor's streaming RPC with\n * NGHTTP2_FRAME_SIZE_ERROR; see oven-sh/bun#31499).\n * - \"http2-direct\": in-process via `@cursor/sdk` default HTTP/2 transport\n * (Node — the normal path for tests, scripts, and any non-Bun host).\n * - \"sidecar\": a spawned Node child hosting the SDK (the historical Bun\n * workaround; see src/sidecar/agent-host.mjs).\n *\n * Resolution order: provider option (`transport`) -> OPENCODE_CURSOR_TRANSPORT\n * -> legacy OPENCODE_CURSOR_SIDECAR (1=sidecar, 0=http2-direct) -> default\n * (Bun: DEFAULT_BUN_TRANSPORT, post-gate \"http1\"; Node: http2-direct).\n */\nimport { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { loadCursorSdk } from \"../cursor-runtime.js\";\nimport { installCursorLogInterceptor } from \"./cursor-log-intercept.js\";\nimport { pluginLog } from \"./log-bridge.js\";\nimport { SidecarClient, type AgentLike } from \"./sidecar-client.js\";\n\nexport type { AgentLike, AgentRunLike, AgentSendOptions } from \"./sidecar-client.js\";\n\nexport type TransportKind = \"http1\" | \"http2-direct\" | \"sidecar\";\nexport type BackendKind = \"in-process\" | \"sidecar\";\n\n/**\n * Bun default. Post-evidence-gate this is \"http1\" (Task 5/6 matrix green;\n * TTFT ≤ 1.5× sidecar). Sidecar remains via OPENCODE_CURSOR_TRANSPORT=sidecar.\n */\nexport const DEFAULT_BUN_TRANSPORT: TransportKind = \"http1\";\n\nlet preferredTransport: TransportKind | undefined;\n\n/** Provider-option override (createCursor({transport})); beats env. Process-global. */\nexport function setPreferredTransport(t: TransportKind | undefined): void {\n preferredTransport = t;\n}\n\nfunction isTransportKind(v: string | undefined): v is TransportKind {\n return v === \"http1\" || v === \"http2-direct\" || v === \"sidecar\";\n}\n\nexport interface AgentBackend {\n kind: BackendKind;\n createAgent(options: unknown): Promise<AgentLike>;\n resumeAgent(agentId: string, options: unknown): Promise<AgentLike>;\n}\n\nexport interface BackendEnvironment {\n isBun: boolean;\n /** Resolved node executable, or undefined when not on PATH. */\n nodePath: string | undefined;\n}\n\n/**\n * Where Cursor agents run. Resolution: provider option -> OPENCODE_CURSOR_TRANSPORT\n * -> legacy OPENCODE_CURSOR_SIDECAR (1=sidecar, 0=http2-direct) -> default\n * (Bun: DEFAULT_BUN_TRANSPORT; Node: http2-direct, today's in-process path).\n */\nexport function resolveTransport(env: BackendEnvironment): TransportKind {\n const requested =\n preferredTransport ??\n (isTransportKind(process.env[\"OPENCODE_CURSOR_TRANSPORT\"])\n ? (process.env[\"OPENCODE_CURSOR_TRANSPORT\"] as TransportKind)\n : undefined);\n if (requested) {\n if (requested === \"sidecar\" && !env.nodePath) {\n return env.isBun ? \"http1\" : \"http2-direct\";\n }\n return requested;\n }\n const legacy = process.env[\"OPENCODE_CURSOR_SIDECAR\"];\n if (legacy === \"1\" || legacy === \"true\") {\n return env.nodePath ? \"sidecar\" : env.isBun ? \"http1\" : \"http2-direct\";\n }\n if (legacy === \"0\" || legacy === \"false\") return env.isBun ? \"http1\" : \"http2-direct\";\n return env.isBun ? DEFAULT_BUN_TRANSPORT : \"http2-direct\";\n}\n\nfunction detectNode(): string | undefined {\n try {\n const out = execSync(process.platform === \"win32\" ? \"where node\" : \"command -v node\", {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n return out.split(\"\\n\")[0] || undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction detectEnvironment(): BackendEnvironment {\n const isBun = typeof (globalThis as { Bun?: unknown }).Bun !== \"undefined\";\n // Only pay the PATH lookup when the answer can matter.\n const needsNode = isBun || process.env[\"OPENCODE_CURSOR_SIDECAR\"] === \"1\";\n return { isBun, nodePath: needsNode ? detectNode() : process.execPath };\n}\n\nlet http1Configured = false;\n\n/** Idempotent: enable the SDK's HTTP/1.1 agent transport (Bun-sanctioned path). */\nasync function ensureHttp1Configured(): Promise<void> {\n if (http1Configured) return;\n const { Cursor } = await loadCursorSdk();\n Cursor.configure({ local: { useHttp1ForAgent: true } });\n http1Configured = true;\n}\n\nfunction inProcessBackend(useHttp1: boolean): AgentBackend {\n // The SDK runs in this process and writes its own diagnostics straight to\n // the shared global `console` (see cursor-log-intercept.ts); install once\n // so its rules/skills load-completion logs route through opencode logging\n // instead of appearing as raw stdout noise.\n installCursorLogInterceptor();\n return {\n kind: \"in-process\",\n createAgent: async (options) => {\n const { Agent } = await loadCursorSdk();\n if (useHttp1) await ensureHttp1Configured();\n return (await Agent.create(options as never)) as unknown as AgentLike;\n },\n resumeAgent: async (agentId, options) => {\n const { Agent } = await loadCursorSdk();\n if (useHttp1) await ensureHttp1Configured();\n return (await Agent.resume(agentId, options as never)) as unknown as AgentLike;\n },\n };\n}\n\n/**\n * Locate the sidecar script across layouts: tsup may place this module in\n * dist/provider/index.js or hoist it into a root-level dist/chunk-*.js, and in\n * dev/tests it runs straight from src/. Try each known relative position.\n */\nexport function resolveSidecarScript(): string | undefined {\n const candidates = [\n \"./sidecar/agent-host.js\", // importer is a chunk at dist root\n \"../sidecar/agent-host.js\", // importer is dist/provider/index.js\n \"../sidecar/agent-host.mjs\", // importer is src/provider/*.ts (dev/tests)\n ];\n for (const candidate of candidates) {\n const path = fileURLToPath(new URL(candidate, import.meta.url));\n if (existsSync(path)) return path;\n }\n return undefined;\n}\n\nfunction sidecarBackend(nodePath: string, scriptPath: string): AgentBackend {\n const client = new SidecarClient({\n scriptPath,\n nodePath,\n onLog: (level, message, meta) => pluginLog(level, message, meta),\n });\n return {\n kind: \"sidecar\",\n createAgent: (options) => client.createAgent(options),\n resumeAgent: (agentId, options) => client.resumeAgent(agentId, options),\n };\n}\n\nlet cached: AgentBackend | undefined;\n\n/** Resolve (and cache) the agent backend for this process. */\nexport function loadAgentBackend(): AgentBackend {\n if (!cached) {\n const env = detectEnvironment();\n const transport = resolveTransport(env);\n const scriptPath = transport === \"sidecar\" ? resolveSidecarScript() : undefined;\n if (transport === \"sidecar\" && (!env.nodePath || !scriptPath)) {\n // Explicit sidecar request we can't satisfy: fall back loudly.\n pluginLog(\n \"warn\",\n \"Node sidecar requested but unavailable; falling back to in-process HTTP/1.1 transport.\",\n { node: env.nodePath ?? null, script: scriptPath ?? null },\n );\n cached = inProcessBackend(true);\n return cached;\n }\n if (transport === \"http2-direct\" && env.isBun) {\n pluginLog(\n \"warn\",\n \"http2-direct under Bun: Cursor streams may fail (Bun node:http2 incompatibility, oven-sh/bun#31499). \" +\n \"Set OPENCODE_CURSOR_TRANSPORT=http1 (recommended) or sidecar.\",\n );\n }\n cached =\n transport === \"sidecar\" && env.nodePath && scriptPath\n ? sidecarBackend(env.nodePath, scriptPath)\n : inProcessBackend(transport === \"http1\");\n }\n return cached;\n}\n\n/** Test hook. */\nexport function resetAgentBackend(): void {\n cached = undefined;\n}\n","/**\n * Lazy loader for the official Cursor SDK (`@cursor/sdk`).\n *\n * The SDK is heavy and only needed once a Cursor model is actually used or\n * models are discovered, so it is imported on demand. A failed import (e.g. the\n * dependency is missing) degrades gracefully into a clear error instead of\n * crashing opencode at startup.\n */\nexport type CursorSdkModule = typeof import(\"@cursor/sdk\");\n\nlet cached: Promise<CursorSdkModule> | undefined;\n\nexport async function loadCursorSdk(): Promise<CursorSdkModule> {\n if (!cached) {\n cached = import(\"@cursor/sdk\").catch((err: unknown) => {\n // Allow a later retry if the failure was transient.\n cached = undefined;\n const detail = err instanceof Error ? err.message : String(err);\n throw new Error(\n `[opencode-cursor] Failed to load \"@cursor/sdk\". Make sure it is installed ` +\n `(\\`npm install @cursor/sdk\\`). Original error: ${detail}`,\n );\n });\n }\n return cached;\n}\n","import { pluginLog } from \"./log-bridge.js\";\n\n// eslint-disable-next-line no-control-regex\nconst ANSI_PATTERN = /\\x1b\\[[0-9;]*m/g;\n\nfunction stripAnsi(input: string): string {\n\treturn input.replace(ANSI_PATTERN, \"\");\n}\n\n/**\n * `@cursor/sdk`'s bundled local-exec runtime formats its \"rules\"/\"skills\"\n * loading diagnostics (context logger `local-exec:cursor-rules`) into one\n * preformatted string and writes it straight to `console.log` — there is no\n * public logger hook to redirect it instead. Observed shapes (colors\n * stripped):\n *\n * 16:05:53.036 INFO LocalCursorRulesService load completed meta={durationMs: 89, ruleCount: 1}\n * 16:05:53.036 INFO AgentSkillsCursorRulesService load completed meta={durationMs: 86, ruleCount: 18, skillCount: 18}\n * 16:05:53.036 INFO CursorPluginsAgentSkillsService load completed meta={durationMs: 12, ruleCount: 2, skillCount: 0}\n *\n * The context path (`ctx=...`) is only present in some builds/configs.\n */\nconst RULE_LOAD_PATTERN =\n\t/^\\d{2}:\\d{2}:\\d{2}\\.\\d{3}\\s+INFO\\s+(LocalCursorRulesService|AgentSkillsCursorRulesService|CursorPluginsAgentSkillsService) load completed(?:\\s+ctx=\\S+)?\\s+meta=\\{([^}]*)\\}\\s*$/;\n\n/** Parses the `meta={key: value, ...}` tail into a plain numeric object. */\nexport function parseCursorLogMeta(raw: string): Record<string, number> {\n\tconst out: Record<string, number> = {};\n\tfor (const part of raw.split(\",\")) {\n\t\tconst [key, value] = part.split(\":\").map((s) => s.trim());\n\t\tif (!key || value === undefined) continue;\n\t\tconst num = Number(value);\n\t\tif (Number.isFinite(num)) out[key] = num;\n\t}\n\treturn out;\n}\n\nexport interface ParsedCursorRuleLog {\n\tservice: string;\n\tmeta: Record<string, number>;\n}\n\n/** Matches one line against the known Cursor rules/skills load-completion shape. */\nexport function parseCursorRuleLoadLine(line: string): ParsedCursorRuleLog | undefined {\n\tconst match = RULE_LOAD_PATTERN.exec(stripAnsi(line));\n\tif (!match) return undefined;\n\tconst [, service, meta] = match;\n\tif (!service) return undefined;\n\treturn { service, meta: parseCursorLogMeta(meta ?? \"\") };\n}\n\nlet installed = false;\nlet original: typeof console.log | undefined;\n\n/**\n * Installs a narrowly-scoped `console.log` interceptor that recognizes only\n * the known Cursor rules/skills \"load completed\" messages (see\n * {@link parseCursorRuleLoadLine}) and re-emits them as structured opencode\n * logs via {@link pluginLog}. Every other `console.log` call — including\n * anything else the SDK or the host process writes — passes through\n * unchanged.\n *\n * Only relevant to the in-process transport, where the SDK runs inside this\n * process and writes directly to the shared global `console`. The sidecar\n * transport intercepts the same messages in the child process instead (see\n * `src/sidecar/agent-host.mjs`) and forwards them over the JSONL protocol.\n *\n * Idempotent: safe to call on every agent creation.\n */\nexport function installCursorLogInterceptor(): void {\n\tif (installed) return;\n\toriginal = console.log.bind(console);\n\tconst passthrough = original;\n\tconsole.log = (...args: unknown[]) => {\n\t\tif (args.length === 1 && typeof args[0] === \"string\") {\n\t\t\tconst parsed = parseCursorRuleLoadLine(args[0]);\n\t\t\tif (parsed) {\n\t\t\t\tpluginLog(\"info\", `${parsed.service} load completed`, parsed.meta);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tpassthrough(...(args as Parameters<typeof console.log>));\n\t};\n\tinstalled = true;\n}\n\n/** Test hook. */\nexport function resetCursorLogInterceptor(): void {\n\tif (original) console.log = original;\n\toriginal = undefined;\n\tinstalled = false;\n}\n","/**\n * Client half of the Node sidecar (see src/sidecar/agent-host.mjs for the\n * protocol and the why). Spawns one Node child per client and multiplexes\n * agent create/resume/send/cancel/close requests over JSON-lines stdio,\n * exposing agents through the same minimal surface the provider already\n * consumes ({@link AgentLike}), so session-pool/agent-events need no\n * sidecar-specific logic.\n */\nimport { spawn, type ChildProcessByStdio } from \"node:child_process\";\nimport { createInterface, type Interface } from \"node:readline\";\nimport type { Readable, Writable } from \"node:stream\";\n\n/** Minimal run surface the provider consumes (subset of the SDK's Run). */\nexport interface AgentRunLike {\n wait(): Promise<{ status: string; result?: string }>;\n cancel(): void | Promise<void>;\n}\n\nexport interface AgentSendOptions {\n mode?: string;\n onDelta?: (input: { update: Record<string, unknown> & { type: string } }) => void;\n local?: { force?: boolean };\n idempotencyKey?: string;\n}\n\n/** Minimal agent surface the provider consumes (subset of the SDK's SDKAgent). */\nexport interface AgentLike {\n agentId: string;\n send(message: unknown, options?: AgentSendOptions): Promise<AgentRunLike>;\n close(): void;\n}\n\nexport interface SidecarClientOptions {\n /** Path to the agent-host script. */\n scriptPath: string;\n /** Node executable; default \"node\" from PATH. */\n nodePath?: string;\n /** Extra environment for the child (merged over process.env). */\n env?: Record<string, string>;\n /** Mirror child stderr to this process (debug aid). */\n debug?: boolean;\n /**\n * Structured log lines the child recognized on the SDK's stdout (currently\n * Cursor's rules/skills load-completion diagnostics; see\n * src/sidecar/agent-host.mjs) and forwarded over the protocol instead of\n * discarding as non-JSON noise.\n */\n onLog?: (level: \"debug\" | \"info\" | \"warn\" | \"error\", message: string, meta?: Record<string, unknown>) => void;\n}\n\ninterface Pending {\n resolve: (msg: Record<string, unknown>) => void;\n reject: (err: Error) => void;\n /** Streaming hooks for \"send\" requests. */\n onUpdate?: (update: Record<string, unknown> & { type: string }) => void;\n onResult?: (result: { status: string; result?: string }) => void;\n onStreamError?: (err: Error) => void;\n}\n\nfunction reviveError(error: unknown): Error {\n const e = (error ?? {}) as {\n name?: string;\n message?: string;\n status?: number;\n code?: string;\n isRetryable?: boolean;\n helpUrl?: string;\n };\n const err = new Error(e.message ?? \"sidecar error\");\n if (e.name) err.name = e.name;\n if (e.status !== undefined) (err as { status?: number }).status = e.status;\n if (e.code !== undefined) (err as { code?: string }).code = e.code;\n if (e.isRetryable !== undefined) (err as { isRetryable?: boolean }).isRetryable = e.isRetryable;\n if (e.helpUrl !== undefined) (err as { helpUrl?: string }).helpUrl = e.helpUrl;\n return err;\n}\n\nexport class SidecarClient {\n private readonly options: SidecarClientOptions;\n private child: ChildProcessByStdio<Writable, Readable, Readable> | undefined;\n private reader: Interface | undefined;\n private readonly pending = new Map<number, Pending>();\n private nextId = 1;\n private disposed = false;\n\n constructor(options: SidecarClientOptions) {\n this.options = options;\n }\n\n /** Spawn (or reuse) the child process. */\n private ensureChild(): ChildProcessByStdio<Writable, Readable, Readable> {\n if (this.disposed) throw new Error(\"cursor sidecar client disposed\");\n if (this.child) return this.child;\n\n const child = spawn(this.options.nodePath ?? \"node\", [this.options.scriptPath], {\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n env: { ...process.env, ...this.options.env },\n });\n this.child = child;\n\n this.reader = createInterface({ input: child.stdout });\n this.reader.on(\"line\", (line) => this.handleLine(line));\n child.stderr.on(\"data\", (chunk: Buffer) => {\n if (this.options.debug || process.env[\"OPENCODE_CURSOR_DEBUG\"]) {\n process.stderr.write(`[cursor:sidecar] ${chunk}`);\n }\n });\n child.on(\"exit\", (code) => {\n this.failAll(new Error(`cursor sidecar exited (code ${code ?? \"unknown\"})`));\n this.child = undefined;\n this.reader?.close();\n this.reader = undefined;\n });\n child.on(\"error\", (err) => {\n this.failAll(new Error(`cursor sidecar failed to start: ${err.message}`));\n this.child = undefined;\n });\n this.updateRefs();\n return child;\n }\n\n /**\n * Keep the child (and its pipes) from holding the parent's event loop open\n * while idle, but ref it whenever a reply is outstanding so the loop can't\n * exit mid-request. Without this, any process that uses the provider and\n * never dispose()s — scripts, tests, opencode itself on shutdown — hangs.\n */\n private updateRefs(): void {\n const child = this.child;\n if (!child) return;\n const refable = [child, child.stdin, child.stdout, child.stderr] as Array<{\n ref?: () => void;\n unref?: () => void;\n }>;\n if (this.pending.size > 0) {\n for (const target of refable) target.ref?.();\n } else {\n for (const target of refable) target.unref?.();\n }\n }\n\n private failAll(err: Error): void {\n for (const pending of this.pending.values()) {\n pending.onStreamError?.(err);\n pending.reject(err);\n }\n this.pending.clear();\n this.updateRefs();\n }\n\n private handleLine(line: string): void {\n if (!line.trim()) return;\n let msg: Record<string, unknown>;\n try {\n msg = JSON.parse(line) as Record<string, unknown>;\n } catch {\n return; // ignore non-protocol noise on stdout\n }\n if (msg[\"ev\"] === \"log\") {\n const level = msg[\"level\"];\n const message = msg[\"message\"];\n if (typeof level === \"string\" && typeof message === \"string\") {\n this.options.onLog?.(\n level as \"debug\" | \"info\" | \"warn\" | \"error\",\n message,\n msg[\"meta\"] as Record<string, unknown> | undefined,\n );\n }\n return;\n }\n\n const id = msg[\"id\"];\n if (typeof id !== \"number\") return;\n const pending = this.pending.get(id);\n if (!pending) return;\n\n const ev = msg[\"ev\"];\n if (ev === \"update\") {\n pending.onUpdate?.(msg[\"update\"] as Record<string, unknown> & { type: string });\n return;\n }\n if (ev === \"result\") {\n this.pending.delete(id);\n this.updateRefs();\n pending.onResult?.(msg[\"result\"] as { status: string; result?: string });\n return;\n }\n if (ev === \"error\") {\n this.pending.delete(id);\n this.updateRefs();\n pending.onStreamError?.(reviveError(msg[\"error\"]));\n return;\n }\n\n if (msg[\"ok\"] === true) {\n // \"send\" acks stay pending for their streaming terminal event.\n if (!pending.onResult) {\n this.pending.delete(id);\n this.updateRefs();\n }\n pending.resolve(msg);\n } else {\n this.pending.delete(id);\n this.updateRefs();\n pending.reject(reviveError(msg[\"error\"]));\n }\n }\n\n private request(\n payload: Record<string, unknown>,\n hooks?: Pick<Pending, \"onUpdate\" | \"onResult\" | \"onStreamError\">,\n ): Promise<Record<string, unknown>> {\n const child = this.ensureChild();\n const id = this.nextId++;\n return new Promise<Record<string, unknown>>((resolve, reject) => {\n this.pending.set(id, { resolve, reject, ...hooks });\n this.updateRefs();\n child.stdin.write(`${JSON.stringify({ id, ...payload })}\\n`, (err) => {\n if (err) {\n this.pending.delete(id);\n this.updateRefs();\n reject(err);\n }\n });\n });\n }\n\n async createAgent(options: unknown): Promise<AgentLike> {\n const res = await this.request({ op: \"create\", options });\n return this.wrapAgent(String(res[\"agentId\"]));\n }\n\n async resumeAgent(agentId: string, options: unknown): Promise<AgentLike> {\n const res = await this.request({ op: \"resume\", agentId, options });\n return this.wrapAgent(String(res[\"agentId\"]));\n }\n\n private wrapAgent(agentId: string): AgentLike {\n return {\n agentId,\n send: (message, options) => this.sendTurn(agentId, message, options),\n close: () => {\n void this.request({ op: \"close\", agentId }).catch(() => {\n // best effort, mirrors SDKAgent.close()\n });\n },\n };\n }\n\n private async sendTurn(\n agentId: string,\n message: unknown,\n options?: AgentSendOptions,\n ): Promise<AgentRunLike> {\n let settle!: {\n resolve: (r: { status: string; result?: string }) => void;\n reject: (e: Error) => void;\n };\n const waited = new Promise<{ status: string; result?: string }>((resolve, reject) => {\n settle = { resolve, reject };\n });\n // Avoid unhandled-rejection noise when the consumer never calls wait().\n waited.catch(() => {});\n\n let sendId: number | undefined;\n const ack = this.request(\n {\n op: \"send\",\n agentId,\n message,\n ...(options?.mode ? { mode: options.mode } : {}),\n ...(options?.local?.force ? { force: true } : {}),\n ...(options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),\n },\n {\n onUpdate: (update) => options?.onDelta?.({ update }),\n onResult: (result) => settle.resolve(result),\n onStreamError: (err) => settle.reject(err),\n },\n );\n // The request id is allocated synchronously inside request(); capture it\n // for cancel by reading the id we just used.\n sendId = this.nextId - 1;\n\n await ack;\n return {\n wait: () => waited,\n cancel: async () => {\n if (sendId === undefined) return;\n await this.request({ op: \"cancel\", sendId }).catch(() => {});\n },\n };\n }\n\n /** Kill the child and reject anything in flight. */\n dispose(): void {\n this.disposed = true;\n this.failAll(new Error(\"cursor sidecar client disposed\"));\n this.reader?.close();\n this.reader = undefined;\n this.child?.kill();\n this.child = undefined;\n }\n}\n","import {\n\tmkdirSync,\n\twriteFileSync,\n\treadFileSync,\n\texistsSync,\n\trmSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { LanguageModelV3Prompt } from \"@ai-sdk/provider\";\nimport type { SettingSource } from \"@cursor/sdk\";\nimport type { SystemPromptMode } from \"./message-map.js\";\n\n/** Location of the generated rule, relative to the agent's cwd. */\nconst RULES_DIR = join(\".cursor\", \"rules\");\nconst RULE_FILE = \"opencode.mdc\";\nconst IGNORE_FILE = \".gitignore\";\n\n/**\n * Frontmatter sentinel marking the rule as generated by this plugin. Only\n * files carrying it are ever overwritten or deleted, so a user-owned\n * `.cursor/rules/opencode.mdc` is never clobbered.\n */\nconst SENTINEL = \"generated: opencode-cursor\";\n\n/** Concatenate every system-message body from an AI-SDK prompt (trimmed). */\nexport function extractSystemText(prompt: LanguageModelV3Prompt): string {\n\tconst parts: string[] = [];\n\tfor (const message of prompt) {\n\t\tif (message.role === \"system\") parts.push(message.content);\n\t}\n\treturn parts.join(\"\\n\\n\").trim();\n}\n\n/** Outcome of a {@link writeSystemRule} attempt. */\nexport type SystemRuleWrite =\n\t/** Rule file created or updated. */\n\t| \"written\"\n\t/** Existing generated rule already has this content; write skipped. */\n\t| \"unchanged\"\n\t/** No system text to deliver; nothing written. */\n\t| \"empty\"\n\t/** A user-owned (sentinel-less) opencode.mdc exists; left untouched. */\n\t| \"blocked\";\n\n/** True when the file carries the generated-by sentinel in its frontmatter. */\nfunction isGenerated(content: string): boolean {\n\tif (!content.startsWith(\"---\")) return false;\n\tconst end = content.indexOf(\"\\n---\", 3);\n\tconst frontmatter = end === -1 ? content : content.slice(0, end);\n\treturn frontmatter.split(/\\r?\\n/).includes(SENTINEL);\n}\n\n/**\n * Write opencode's system prompt to `<cwd>/.cursor/rules/opencode.mdc` as an\n * always-applied Cursor project rule. Cursor loads this through its authoritative\n * rules channel (`settingSources` including \"project\"), so opencode's controlling\n * instructions reach the agent without being flattened into the untrusted\n * user-message transcript (which injection-hardened models reject).\n *\n * The file carries a generated-by sentinel; a pre-existing sentinel-less file\n * is treated as user-owned and never overwritten (\"blocked\"). An existing\n * generated rule with identical content is left as-is (\"unchanged\") to keep\n * sync fs writes off the stream hot path. May throw on fs errors (read-only\n * checkout etc.) — callers should degrade gracefully.\n */\nexport function writeSystemRule(\n\tcwd: string,\n\tsystemText: string,\n): SystemRuleWrite {\n\tif (!systemText) return \"empty\";\n\tconst dir = join(cwd, RULES_DIR);\n\tconst path = join(dir, RULE_FILE);\n\tconst body = `---\\nalwaysApply: true\\n${SENTINEL}\\n---\\n\\n${systemText}\\n`;\n\tconst existing = existsSync(path) ? readFileSync(path, \"utf8\") : undefined;\n\tif (existing !== undefined) {\n\t\tif (!isGenerated(existing)) return \"blocked\";\n\t\tif (existing === body) return \"unchanged\";\n\t}\n\tmkdirSync(dir, { recursive: true });\n\twriteFileSync(path, body, \"utf8\");\n\tensureGitIgnored(dir);\n\treturn \"written\";\n}\n\n/**\n * Keep the generated rule out of git via `.cursor/rules/.gitignore` (which\n * also ignores itself so it doesn't pollute `git status`).\n */\nfunction ensureGitIgnored(dir: string): void {\n\tconst path = join(dir, IGNORE_FILE);\n\tconst existing = existsSync(path) ? readFileSync(path, \"utf8\") : \"\";\n\tconst lines = existing.split(/\\r?\\n/);\n\tconst missing = [RULE_FILE, IGNORE_FILE].filter(\n\t\t(entry) => !lines.includes(entry),\n\t);\n\tif (missing.length === 0) return;\n\tconst prefix =\n\t\texisting && !existing.endsWith(\"\\n\") ? `${existing}\\n` : existing;\n\twriteFileSync(path, `${prefix}${missing.join(\"\\n\")}\\n`, \"utf8\");\n}\n\n/**\n * Remove the generated rule (best-effort); used on plugin dispose. Only\n * deletes files carrying the generated-by sentinel — a user-owned\n * opencode.mdc is left in place.\n */\nexport function removeSystemRule(cwd: string): void {\n\ttry {\n\t\tconst path = join(cwd, RULES_DIR, RULE_FILE);\n\t\tif (isGenerated(readFileSync(path, \"utf8\"))) rmSync(path);\n\t} catch {\n\t\t// best effort — already gone or never written\n\t}\n}\n\n/** How the system prompt will be delivered for this turn. */\nexport interface SystemDelivery {\n\tmode: SystemPromptMode;\n\tsettingSources: SettingSource[] | undefined;\n}\n\n/**\n * Decide how opencode's system prompt reaches the Cursor agent for one turn.\n *\n * In \"rules\" mode this writes the rule file and enables the `project`\n * settings layer — but ONLY when the user did not explicitly configure\n * `settingSources` without \"project\" (a deliberate hardening opt-out: the\n * project layer also loads the repo's `.cursor/mcp.json`, hooks, and other\n * rules). On an opt-out, a failed write (read-only checkout etc.), or a\n * user-owned rule file, it degrades to inline \"message\" delivery for the\n * turn and reports the reason via `warn`. Never throws.\n */\nexport function resolveSystemDelivery(options: {\n\tmode: SystemPromptMode;\n\tsettingSources: SettingSource[] | undefined;\n\tcwd: string;\n\tsystemText: string;\n\twarn: (message: string) => void;\n}): SystemDelivery {\n\tconst { mode, settingSources, cwd, systemText, warn } = options;\n\tif (mode !== \"rules\") return { mode, settingSources };\n\tif (settingSources && !settingSources.includes(\"project\")) {\n\t\twarn(\n\t\t\t'systemPrompt \"rules\" needs the \"project\" settings layer, but settingSources was explicitly configured without it; delivering the system prompt inline (\"message\" mode) instead. Add \"project\" to settingSources or set systemPrompt: \"message\" to silence this.',\n\t\t);\n\t\treturn { mode: \"message\", settingSources };\n\t}\n\tlet result: SystemRuleWrite;\n\ttry {\n\t\tresult = writeSystemRule(cwd, systemText);\n\t} catch (error) {\n\t\twarn(\n\t\t\t`failed to write .cursor/rules/${RULE_FILE} (${error instanceof Error ? error.message : String(error)}); delivering the system prompt inline (\"message\" mode) for this turn.`,\n\t\t);\n\t\treturn { mode: \"message\", settingSources };\n\t}\n\tif (result === \"blocked\") {\n\t\twarn(\n\t\t\t`.cursor/rules/${RULE_FILE} exists but was not generated by opencode-cursor; leaving it untouched and delivering the system prompt inline (\"message\" mode).`,\n\t\t);\n\t\treturn { mode: \"message\", settingSources };\n\t}\n\tif (result === \"empty\") return { mode, settingSources };\n\treturn { mode, settingSources: settingSources ?? [\"project\"] };\n}\n","import type { OpencodeClient } from \"@opencode-ai/sdk\";\n\n/**\n * Bridge from the opencode plugin to the provider stream layer.\n *\n * A Cursor subagent runs entirely inside Cursor's process — no opencode child\n * session exists, so its `task` card is dead (not clickable, `ctrl+x down`\n * navigates nowhere). To make it native, the provider must create a REAL\n * opencode child session (`Session.parentID`) and point the task part's\n * `state.metadata.sessionId` at it.\n *\n * The provider stream code ({@link cursorEventsToStream}) has no opencode\n * client. The plugin does (`PluginInput.client` + `directory`). They run in the\n * same process, so the plugin publishes them here on a `globalThis` registry\n * (immune to bundler entry-point splitting) and the provider reads them lazily.\n * When the bridge is absent (provider used without the plugin, or client\n * unavailable), subagent linking is skipped and the card degrades to exactly\n * its previous non-navigable behavior.\n */\nexport interface SubagentBridge {\n\tclient: OpencodeClient;\n\t/** Workspace directory threaded into session create/prompt calls. */\n\tdirectory?: string;\n}\n\nconst BRIDGE_KEY = Symbol.for(\"@stablekernel/opencode-cursor:subagent-bridge\");\n\ntype BridgeHolder = { [BRIDGE_KEY]?: SubagentBridge };\n\n/** Publish the opencode client + directory for the provider to use. */\nexport function setSubagentBridge(bridge: SubagentBridge): void {\n\t(globalThis as BridgeHolder)[BRIDGE_KEY] = bridge;\n}\n\n/** Drop the bridge (plugin dispose). */\nexport function clearSubagentBridge(): void {\n\tdelete (globalThis as BridgeHolder)[BRIDGE_KEY];\n}\n\n/** Read the current bridge, or `undefined` when the plugin hasn't published one. */\nexport function getSubagentBridge(): SubagentBridge | undefined {\n\treturn (globalThis as BridgeHolder)[BRIDGE_KEY];\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/** Format a millisecond duration the way opencode's TUI does (ms / s / m). */\nfunction formatDuration(ms: number): string {\n\tif (ms < 1000) return `${ms}ms`;\n\tif (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n\tconst minutes = Math.floor(ms / 60000);\n\tconst seconds = Math.floor((ms % 60000) / 1000);\n\treturn `${minutes}m ${seconds}s`;\n}\n\n/**\n * A compact activity line (\"ran 5 steps in 12.3s\") built from Cursor's task\n * result. `conversationSteps` is opaquely typed, so it's reported as a step\n * count (an honest proxy) rather than claiming an exact tool-call count.\n * Returns `undefined` when neither timing nor steps are available.\n */\nfunction activityLine(value: unknown): string | undefined {\n\tconst durationMs = numField(value, \"durationMs\");\n\tconst steps =\n\t\tisRecord(value) && Array.isArray(value[\"conversationSteps\"])\n\t\t\t? (value[\"conversationSteps\"] as unknown[]).length\n\t\t\t: undefined;\n\tconst bits: string[] = [];\n\tif (steps && steps > 0) bits.push(`${steps} step${steps === 1 ? \"\" : \"s\"}`);\n\tif (typeof durationMs === \"number\") bits.push(`in ${formatDuration(durationMs)}`);\n\treturn bits.length > 0 ? `_Subagent ran ${bits.join(\" \")}._` : undefined;\n}\n\n/**\n * Cursor's proto zero-value subagent kind. Never forward it: the TUI would\n * titlecase it to \"Unspecified\", and it's meaningless as an agent label.\n */\nconst UNSPECIFIED_KIND = \"unspecified\";\n\n/**\n * Resolve a human agent label from Cursor's `subagentType` ({ kind, name? }).\n * Prefers a real `name`; falls back to a meaningful `kind`; otherwise\n * `\"general\"` (which the TUI renders as \"General Task\").\n */\nexport function subagentLabel(args: unknown): string {\n\tconst sub = isRecord(args) ? args[\"subagentType\"] : undefined;\n\tconst name = strField(sub, \"name\");\n\tif (name) return name;\n\tconst kind = strField(sub, \"kind\");\n\tif (kind && kind !== UNSPECIFIED_KIND) return kind;\n\treturn \"general\";\n}\n\n/**\n * Build the child-session transcript body from Cursor's task result `value`.\n * Prefers the model-authored `resultSuffix`; appends a compact render of\n * `conversationSteps` when present. Returns `undefined` when there's nothing\n * useful to post (the prompt message alone still makes the session readable).\n */\nfunction buildTranscript(value: unknown): string | undefined {\n\tconst parts: string[] = [];\n\tconst suffix = strField(value, \"resultSuffix\");\n\tif (suffix) parts.push(suffix);\n\tif (isRecord(value) && Array.isArray(value[\"conversationSteps\"])) {\n\t\tconst steps = value[\"conversationSteps\"] as unknown[];\n\t\tconst rendered = steps\n\t\t\t.flatMap((s) => {\n\t\t\t\tconst text = strField(s, \"text\") ?? strField(s, \"content\");\n\t\t\t\treturn text ? [text] : [];\n\t\t\t})\n\t\t\t.join(\"\\n\\n\");\n\t\tif (rendered) parts.push(rendered);\n\t}\n\t// Cursor's real timing/activity, surfaced where it's guaranteed visible: the\n\t// child session you navigate into (the collapsed one-liner is rendered by\n\t// opencode from child-session messages we can't synthesize).\n\tconst activity = activityLine(value);\n\tif (activity) parts.push(activity);\n\tconst body = parts.join(\"\\n\\n\").trim();\n\treturn body.length > 0 ? body : undefined;\n}\n\n/**\n * Create a REAL opencode child session for a completed Cursor subagent and seed\n * it with the originating prompt + the returned transcript (both as user-role\n * messages via `noReply` — the public API can't synthesize assistant messages).\n * Returns the child session id so the caller can set the task part's\n * `state.metadata.sessionId`, or `undefined` when the bridge is unavailable or\n * any step fails (the card then degrades to its previous, non-navigable form).\n */\nexport async function linkSubagentSession(opts: {\n\tparentSessionID: string;\n\targs: unknown;\n\tresult: unknown;\n}): Promise<string | undefined> {\n\tconst bridge = getSubagentBridge();\n\tif (!bridge) return undefined;\n\tconst { client, directory } = bridge;\n\tconst query = directory ? { directory } : undefined;\n\ttry {\n\t\tconst description = strField(opts.args, \"description\") ?? \"Subagent task\";\n\t\tconst agent = subagentLabel(opts.args);\n\t\tconst created = await client.session.create({\n\t\t\tbody: {\n\t\t\t\tparentID: opts.parentSessionID,\n\t\t\t\ttitle: `${description} (@${agent} subagent)`,\n\t\t\t},\n\t\t\t...(query ? { query } : {}),\n\t\t});\n\t\tconst childId = created?.data?.id;\n\t\tif (!childId) return undefined;\n\n\t\tconst prompt = strField(opts.args, \"prompt\");\n\t\tif (prompt) {\n\t\t\tawait client.session.prompt({\n\t\t\t\tpath: { id: childId },\n\t\t\t\t...(query ? { query } : {}),\n\t\t\t\tbody: { noReply: true, parts: [{ type: \"text\", text: prompt }] },\n\t\t\t});\n\t\t}\n\t\tconst value =\n\t\t\tisRecord(opts.result) && opts.result[\"status\"] === \"success\"\n\t\t\t\t? opts.result[\"value\"]\n\t\t\t\t: undefined;\n\t\tconst transcript = buildTranscript(value);\n\t\tif (transcript) {\n\t\t\tawait client.session.prompt({\n\t\t\t\tpath: { id: childId },\n\t\t\t\t...(query ? { query } : {}),\n\t\t\t\tbody: { noReply: true, parts: [{ type: \"text\", text: transcript }] },\n\t\t\t});\n\t\t}\n\t\treturn childId;\n\t} catch {\n\t\t// Best-effort: a failed link must never break the turn.\n\t\treturn undefined;\n\t}\n}\n","/**\n * Classify errors from the Cursor SDK into recovery actions. Works off plain\n * {name, message, status?, code?, isRetryable?, helpUrl?} shape ONLY — errors\n * that cross the Node sidecar arrive as re-hydrated plain Errors (never SDK\n * instances), so `instanceof` discrimination is impossible on the Bun side and\n * forbidden here.\n */\nexport type CursorErrorKind =\n\t| \"agent-not-found\"\n\t| \"agent-busy\"\n\t| \"rate-limit\"\n\t| \"network\"\n\t| \"auth\"\n\t| \"config\"\n\t| \"unknown\";\n\nexport interface ClassifiedError {\n\tkind: CursorErrorKind;\n\t/** Safe to retry the same operation (bounded by the caller). */\n\tretryable: boolean;\n\tstatus?: number;\n\thelpUrl?: string;\n\tmessage: string;\n}\n\nexport function classifyError(err: unknown): ClassifiedError {\n\tconst e = (err ?? {}) as {\n\t\tname?: string;\n\t\tmessage?: string;\n\t\tstatus?: unknown;\n\t\tcode?: unknown;\n\t\tisRetryable?: unknown;\n\t\thelpUrl?: unknown;\n\t};\n\tconst name = typeof e.name === \"string\" ? e.name : \"Error\";\n\tconst message = typeof e.message === \"string\" ? e.message : String(err);\n\tconst status = typeof e.status === \"number\" ? e.status : undefined;\n\tconst code = typeof e.code === \"string\" ? e.code : undefined;\n\tconst helpUrl = typeof e.helpUrl === \"string\" ? e.helpUrl : undefined;\n\tconst base = { ...(status !== undefined ? { status } : {}), ...(helpUrl ? { helpUrl } : {}), message };\n\n\tswitch (name) {\n\t\tcase \"AgentNotFoundError\":\n\t\t\treturn { kind: \"agent-not-found\", retryable: false, ...base };\n\t\tcase \"AgentBusyError\":\n\t\t\treturn { kind: \"agent-busy\", retryable: false, ...base };\n\t\tcase \"RateLimitError\":\n\t\t\treturn { kind: \"rate-limit\", retryable: true, ...base };\n\t\tcase \"NetworkError\":\n\t\t\treturn { kind: \"network\", retryable: true, ...base };\n\t\tcase \"AuthenticationError\":\n\t\t\treturn { kind: \"auth\", retryable: false, ...base };\n\t\tcase \"ConfigurationError\":\n\t\tcase \"IntegrationNotConnectedError\":\n\t\tcase \"UnsupportedRunOperationError\":\n\t\t\treturn { kind: \"config\", retryable: false, ...base };\n\t}\n\n\t// Transports/serializers that lose the class name but keep status/code.\n\tif (status === 401) return { kind: \"auth\", retryable: false, ...base };\n\tif (status === 429) return { kind: \"rate-limit\", retryable: true, ...base };\n\tif (status === 409) return { kind: \"agent-busy\", retryable: false, ...base };\n\tif (status === 503 || status === 504) return { kind: \"network\", retryable: true, ...base };\n\tif (code === \"agent_not_found\") return { kind: \"agent-not-found\", retryable: false, ...base };\n\n\treturn { kind: \"unknown\", retryable: e.isRetryable === true, ...base };\n}\n","import type { AgentModeOption, SDKUserMessage } from \"@cursor/sdk\";\nimport type { AgentLike, AgentRunLike, AgentSendOptions } from \"./agent-backend.js\";\nimport { classifyError } from \"./error-classify.js\";\nimport { pluginLog } from \"./log-bridge.js\";\n\n/** Token usage as reported by Cursor's `turn-ended` update. */\nexport interface CursorUsage {\n inputTokens: number;\n outputTokens: number;\n cacheReadTokens: number;\n cacheWriteTokens: number;\n}\n\n/** Normalized events bridged from the Cursor SDK's push callbacks. */\nexport type CursorEvent =\n | { type: \"text-delta\"; text: string }\n | { type: \"reasoning-delta\"; text: string }\n | { type: \"tool-input-partial\"; id: string; name: string; input: unknown }\n | { type: \"tool-call\"; id: string; name: string; input: unknown }\n | { type: \"tool-result\"; id: string; name: string; result: unknown; isError: boolean }\n | { type: \"usage\"; usage: CursorUsage }\n | { type: \"reasoning-complete\"; durationMs?: number }\n | { type: \"compaction\" }\n | { type: \"finish\"; text?: string };\n\nexport interface StreamAgentTurnOptions {\n mode: AgentModeOption;\n abortSignal?: AbortSignal;\n /** Dedupe key forwarded to every (re)send of this turn. */\n idempotencyKey?: string;\n /**\n * Usage accumulated by preceding silent replay turns. When set, yielded\n * `usage` events carry `usageBase + turn-ended` sums so the visible turn's\n * reported usage includes everything spent replaying earlier messages.\n */\n usageBase?: CursorUsage;\n}\n\n/** Sum two usage reports (either may be absent). */\nexport function addUsage(a?: CursorUsage, b?: CursorUsage): CursorUsage | undefined {\n if (!a) return b;\n if (!b) return a;\n return {\n inputTokens: a.inputTokens + b.inputTokens,\n outputTokens: a.outputTokens + b.outputTokens,\n cacheReadTokens: a.cacheReadTokens + b.cacheReadTokens,\n cacheWriteTokens: a.cacheWriteTokens + b.cacheWriteTokens,\n };\n}\n\n/**\n * Human-readable name for a Cursor tool call. Most Cursor tools carry their\n * name in `toolCall.type` (shell/read/edit/…), but an MCP tool call has\n * `type: \"mcp\"` with the real tool in `args.toolName` (and server in\n * `args.providerIdentifier`) — surface that instead of the literal \"mcp\".\n */\nfunction toolDisplayName(toolCall: ({ type?: string } & Record<string, any>) | undefined): string {\n if (!toolCall) return \"tool\";\n if (toolCall.type === \"mcp\") {\n const name = toolCall.args?.toolName;\n const server = toolCall.args?.providerIdentifier;\n if (name) return server ? `${server}/${name}` : String(name);\n return \"mcp\";\n }\n return toolCall.type ?? \"tool\";\n}\n\n/**\n * Node stores a timer delay in a signed 32-bit int; anything larger overflows\n * and is silently clamped to `1`. An operator following the tool-phase stall\n * message's own advice to \"raise OPENCODE_CURSOR_TOOL_STALL_MS\" could therefore\n * pick a number so large that every tool-bearing turn stalls within a\n * millisecond — the exact failure the budget is meant to prevent. Cap instead.\n */\nconst MAX_TIMEOUT_MS = 2_147_483_647;\n\n/**\n * Parse a millisecond env var, falling back when unset. An empty string is\n * treated as `0` (preserving the historical \"set to empty to disable\" behavior\n * of `OPENCODE_CURSOR_STALL_MS`); any other non-finite value falls back to the\n * default so a typo can't arm `setTimeout(fn, NaN)` (which fires immediately).\n * Finite values are capped at {@link MAX_TIMEOUT_MS} so an over-large budget\n * degrades to \"as long as a timer can express\" rather than to ~instant.\n */\nfunction envMs(name: string, fallback: number): number {\n const raw = process.env[name];\n if (raw === undefined) return fallback;\n if (raw === \"\") return 0;\n const n = Number(raw);\n if (!Number.isFinite(n)) return fallback;\n return Math.min(n, MAX_TIMEOUT_MS);\n}\n\n/**\n * Stream a single turn on an already-acquired Cursor agent and yield normalized\n * events. The agent's lifecycle (create/resume/close) is owned by the caller\n * (see session-pool.ts) so it can be reused across turns. The SDK streams via\n * `onDelta` callbacks; we bridge those into a pull-based async generator so both\n * `doStream` and `doGenerate` can consume them.\n */\nexport async function* streamAgentTurn(\n agent: AgentLike,\n message: SDKUserMessage,\n options: StreamAgentTurnOptions,\n): AsyncGenerator<CursorEvent> {\n const queue: CursorEvent[] = [];\n let wake: (() => void) | undefined;\n let finished = false;\n let failure: unknown;\n\n // Opt-in stderr tracing of what the live agent emits (set OPENCODE_CURSOR_DEBUG=1).\n const debug = process.env.OPENCODE_CURSOR_DEBUG === \"1\";\n const counts: Record<string, number> = {};\n\n // Stall watchdog. Two budgets:\n // - stallMs: idle budget (no tool call open). `0` disables the whole\n // watchdog, matching the historical single-knob behavior.\n // - toolStallMs: budget while at least one tool call is in flight. A long\n // shell command or test suite legitimately streams nothing for minutes;\n // killing it at the idle budget was a real-work-destroying false stall.\n // `0` disables the bound during tool execution only. Default 10 min.\n const stallMs = envMs(\"OPENCODE_CURSOR_STALL_MS\", 120_000);\n const toolStallMs = envMs(\"OPENCODE_CURSOR_TOOL_STALL_MS\", 600_000);\n let stallTimer: ReturnType<typeof setTimeout> | undefined;\n let forced = false;\n let anyEvent = false;\n\n // Open tool calls: callId -> display name. Lets the stall message name the\n // culprit and lets armWatchdog pick the larger budget while a tool runs.\n const openTools = new Map<string, string>();\n\n const push = (event: CursorEvent) => {\n anyEvent = true;\n queue.push(event);\n wake?.();\n wake = undefined;\n armWatchdog();\n };\n\n const armWatchdog = () => {\n if (stallMs <= 0 || finished) return;\n const budget = openTools.size > 0 ? toolStallMs : stallMs;\n if (stallTimer) clearTimeout(stallTimer);\n if (budget <= 0) {\n stallTimer = undefined;\n return;\n }\n stallTimer = setTimeout(() => {\n void onStall();\n }, budget);\n stallTimer.unref?.();\n };\n\n const onDelta = ({ update }: { update: { type: string } & Record<string, any> }) => {\n if (debug) counts[update.type] = (counts[update.type] ?? 0) + 1;\n switch (update.type) {\n case \"text-delta\":\n push({ type: \"text-delta\", text: update.text });\n break;\n case \"thinking-delta\":\n push({ type: \"reasoning-delta\", text: update.text });\n break;\n case \"thinking-completed\":\n push({ type: \"reasoning-complete\", durationMs: update.thinkingDurationMs as number | undefined });\n break;\n case \"summary-started\":\n case \"summary\":\n case \"summary-completed\":\n push({ type: \"compaction\" });\n break;\n case \"partial-tool-call\":\n push({\n type: \"tool-input-partial\",\n id: String(update.callId),\n name: toolDisplayName(update.toolCall),\n input: update.toolCall?.args ?? {},\n });\n break;\n case \"tool-call-started\":\n // Track the open call BEFORE push() re-arms the watchdog with the\n // larger tool budget.\n openTools.set(String(update.callId), toolDisplayName(update.toolCall));\n push({\n type: \"tool-call\",\n id: String(update.callId),\n name: toolDisplayName(update.toolCall),\n input: update.toolCall?.args ?? {},\n });\n break;\n case \"tool-call-completed\": {\n openTools.delete(String(update.callId));\n const tool = update.toolCall ?? {};\n const result = tool.result;\n // MCP failures often arrive as {status:\"success\", value:{isError:true}}\n // (the MCP-protocol error flag), not as a top-level status error.\n const mcpError = tool.type === \"mcp\" && result?.value?.isError === true;\n push({\n type: \"tool-result\",\n id: String(update.callId),\n name: toolDisplayName(tool),\n result: result ?? null,\n isError: result?.status === \"error\" || mcpError,\n });\n break;\n }\n case \"turn-ended\":\n // Reconcile: a dropped or differently-keyed `tool-call-completed`\n // would otherwise leave an entry pinned here, holding the turn on the\n // 10-minute tool budget and naming a tool that already finished.\n openTools.clear();\n if (update.usage) {\n const summed = addUsage(options.usageBase, update.usage as CursorUsage);\n if (summed) push({ type: \"usage\", usage: summed });\n }\n break;\n }\n // Any SDK update proves the stream is alive — including types we don't map\n // (progress, heartbeats, future types), which never reach `push()`. Armed\n // AFTER the switch so it observes the post-mutation `openTools` state and\n // therefore always selects the correct budget (a `turn-ended` that cleared\n // the map must fall back to the idle budget immediately).\n armWatchdog();\n };\n\n const runHolder: { run?: AgentRunLike } = {};\n const onAbort = () => {\n // Clear the stall timer so an armed watchdog can't fire a force-resend of\n // an aborted turn (abort during the pre-first-event wait would otherwise\n // still trip onStall).\n if (stallTimer) clearTimeout(stallTimer);\n stallTimer = undefined;\n void Promise.resolve(runHolder.run?.cancel()).catch(() => {});\n };\n options.abortSignal?.addEventListener(\"abort\", onAbort);\n\n // Kick off the turn. Resolve text from run.wait() for models that don't emit\n // incremental text deltas. `runGen` disambiguates run invocations: when the\n // watchdog cancels a wedged run and starts a fresh one, the stale run's\n // completion handlers must NOT finish the stream (a new run is in flight).\n let runGen = 0;\n const startRun = (force: boolean): void => {\n const gen = ++runGen;\n void sendWithRecovery(\n agent,\n message,\n {\n mode: options.mode,\n onDelta,\n ...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}),\n ...(force ? { local: { force: true } } : {}),\n },\n debug,\n )\n .then(async (run) => {\n runHolder.run = run;\n // The signal may have fired while send() was in flight (before runHolder\n // was populated, so onAbort had nothing to cancel); cancel now.\n if (options.abortSignal?.aborted) void Promise.resolve(run.cancel()).catch(() => {});\n const result = await run.wait();\n if (debug) {\n pluginLog(\"debug\", \"turn finished\", {\n updates: counts,\n status: result.status,\n resultLen: (result.result ?? \"\").length,\n });\n }\n // Superseded by a watchdog force-resend: this run is abandoned.\n if (gen !== runGen || finished) return;\n if (result.status === \"error\") {\n // Surface the failure instead of finishing silently — a silent stop\n // leaves opencode showing dangling tool calls with no explanation.\n throw new Error(\n `Cursor run ended with status \"error\"${result.result ? `: ${result.result}` : \"\"}`,\n );\n }\n // A cancelled run finishes without fabricating final text.\n push({ type: \"finish\", ...(result.status === \"cancelled\" ? {} : { text: result.result }) });\n })\n .catch((err) => {\n if (gen !== runGen) return;\n failure = err;\n if (debug) {\n pluginLog(\"debug\", \"send failed\", {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n })\n .finally(() => {\n // Only the live run finishes the stream; a superseded (cancelled-for-\n // resend) run leaves `finished` untouched so the resend can complete.\n if (gen !== runGen) return;\n finished = true;\n if (stallTimer) clearTimeout(stallTimer);\n wake?.();\n wake = undefined;\n });\n };\n\n const onStall = async (): Promise<void> => {\n if (finished) return;\n // The turn was aborted: don't resend or surface a spurious stall error.\n if (options.abortSignal?.aborted) return;\n const failTerminal = async (message: string): Promise<void> => {\n // Cancel the wedged server run (best-effort) so it isn't orphaned in a\n // RUNNING state, then surface the stall as a terminal failure.\n try {\n await runHolder.run?.cancel();\n } catch {\n /* best effort */\n }\n failure = new Error(message);\n finished = true;\n if (stallTimer) clearTimeout(stallTimer);\n stallTimer = undefined;\n wake?.();\n wake = undefined;\n };\n if (anyEvent) {\n // A stall AFTER partial output is terminal: force-resending would\n // re-emit the already-yielded prefix. Cancel the wedged run and surface\n // the stall instead.\n const budget = openTools.size > 0 ? toolStallMs : stallMs;\n const inFlight = [...openTools.values()];\n const toolHint =\n inFlight.length > 0\n ? `; tool${inFlight.length > 1 ? \"s\" : \"\"} ${inFlight.map((n) => `\"${n}\"`).join(\", \")} still in flight`\n : \"\";\n const knob = openTools.size > 0 ? \"OPENCODE_CURSOR_TOOL_STALL_MS\" : \"OPENCODE_CURSOR_STALL_MS\";\n await failTerminal(\n `Cursor run stalled (no events for ${budget}ms${toolHint}). Raise ${knob} (or set 0 to disable) if this legitimately runs longer.`,\n );\n return;\n }\n if (forced) {\n await failTerminal(`Cursor run stalled twice (no events for ${stallMs}ms)`);\n return;\n }\n forced = true;\n if (debug) pluginLog(\"debug\", \"stream stalled; cancelling and resending with local.force\");\n try {\n await runHolder.run?.cancel();\n } catch {\n /* best effort */\n }\n // The abandoned run's tool calls will never complete; don't let them hold\n // the resend on the tool budget.\n openTools.clear();\n armWatchdog();\n startRun(true);\n };\n\n armWatchdog();\n startRun(false);\n\n try {\n while (true) {\n if (queue.length > 0) {\n yield queue.shift()!;\n continue;\n }\n if (finished) break;\n await new Promise<void>((resolve) => {\n wake = resolve;\n });\n }\n // Drain anything queued right before completion.\n while (queue.length > 0) yield queue.shift()!;\n if (failure) throw failure;\n } finally {\n if (stallTimer) clearTimeout(stallTimer);\n options.abortSignal?.removeEventListener(\"abort\", onAbort);\n }\n}\n\nconst sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));\n\n/** Bounded backoff for retryable send failures (ms per attempt). */\nconst RETRY_BACKOFF_MS = [500, 1500] as const;\n\n/**\n * Send a message on an agent with typed recovery (see error-classify.ts):\n * - agent-busy: a previous crash left a persisted run wedged — resend once\n * with `local.force` to expire it (SDK-documented recovery).\n * - rate-limit / network: bounded exponential backoff on the SAME agent; the\n * shared idempotencyKey makes the resend a server-side dedupe, not a dup.\n * - everything else: rethrow (auth/config fail fast upstream; unknown is\n * handled by the caller's fresh-replay path).\n */\nexport async function sendWithRecovery(\n agent: AgentLike,\n message: SDKUserMessage,\n sendOptions: AgentSendOptions,\n debug: boolean,\n): Promise<AgentRunLike> {\n for (let attempt = 0; ; attempt++) {\n try {\n return await agent.send(message, sendOptions);\n } catch (err) {\n const classified = classifyError(err);\n if (classified.kind === \"agent-busy\") {\n if (debug) pluginLog(\"debug\", \"agent busy; retrying send with local.force\");\n return agent.send(message, { ...sendOptions, local: { force: true } });\n }\n if (\n (classified.kind === \"rate-limit\" || classified.kind === \"network\") &&\n attempt < RETRY_BACKOFF_MS.length\n ) {\n if (debug)\n pluginLog(\"debug\", `${classified.kind}; retrying send`, {\n delayMs: RETRY_BACKOFF_MS[attempt],\n });\n await sleep(RETRY_BACKOFF_MS[attempt]!);\n continue;\n }\n throw err;\n }\n }\n}\n\n/**\n * Send a single turn on an already-acquired agent WITHOUT streaming anything\n * back. Used to replay the leading messages of a multi-message interjection\n * (two-or-more user messages queued while the agent was busy): messages\n * `1..N-1` are sent silently and awaited, and only the final message streams\n * via {@link streamAgentTurn}. This mirrors opencode's own model, where\n * interjected messages fold into a single visible turn.\n *\n * Honors `options.abortSignal`: an abort cancels the in-flight run so the\n * caller can stop before sending the next queued message.\n *\n * Known trade-offs of silent turns being FULL agent runs:\n * - Tool invisibility: the agent may execute tools (shell, edits, MCP) during\n * a silent turn with zero streamed output or tool display — the user sees\n * nothing until the final message streams. Accepted because interjections\n * are typically short course-corrections, and opencode itself folds\n * interjected messages into one visible turn.\n * - Serial latency: each silent turn is awaited to completion before the next\n * send, so an N-message interjection costs N sequential agent runs.\n *\n * Concatenating the queued messages into one Cursor message was rejected for\n * message fidelity: each interjection must land as a distinct user turn in the\n * agent's conversation memory (mirroring opencode's transcript), so the model\n * sees the same message boundaries the user created and later fingerprint\n * classification stays aligned turn-for-turn.\n */\nexport async function sendAgentTurnSilently(\n agent: AgentLike,\n message: SDKUserMessage,\n options: StreamAgentTurnOptions,\n): Promise<CursorUsage | undefined> {\n // Already aborted: don't start a turn just to cancel it.\n if (options.abortSignal?.aborted) return undefined;\n const debug = process.env.OPENCODE_CURSOR_DEBUG === \"1\";\n const runHolder: { run?: AgentRunLike } = {};\n // Capture only the turn-ended usage; a silent turn streams nothing else.\n let usage: CursorUsage | undefined;\n const onDelta = ({ update }: { update: { type: string } & Record<string, any> }) => {\n if (update.type === \"turn-ended\" && update.usage) usage = update.usage as CursorUsage;\n };\n const onAbort = () => {\n void Promise.resolve(runHolder.run?.cancel()).catch(() => {});\n };\n options.abortSignal?.addEventListener(\"abort\", onAbort);\n try {\n const run = await sendWithRecovery(\n agent,\n message,\n { mode: options.mode, onDelta, ...(options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}) },\n debug,\n );\n runHolder.run = run;\n // The signal may have fired while send() was in flight (before runHolder\n // was populated, so onAbort had nothing to cancel); cancel now.\n if (options.abortSignal?.aborted) void Promise.resolve(run.cancel()).catch(() => {});\n const result = await run.wait();\n if (result.status !== \"finished\") {\n // Our own abort cancelled the run mid-flight: expected, not a failure.\n // The caller's abort check stops the multi-send sequence and drops the\n // session record, so this partial turn is never counted as delivered.\n if (options.abortSignal?.aborted) return undefined;\n // Anything else (\"error\", an external \"cancelled\", unknown states) means\n // the message was NOT delivered; treating it as success would leave the\n // session record claiming the agent saw a message it never received.\n throw new Error(\n `Cursor run ended with status \"${result.status}\"${result.result ? `: ${result.result}` : \"\"}`,\n );\n }\n return usage;\n } finally {\n options.abortSignal?.removeEventListener(\"abort\", onAbort);\n }\n}\n","import type { AgentModeOption, ModelSelection } from \"@cursor/sdk\";\n\n/** Per-model static control defaults (from provider/model config options). */\nexport interface StaticControls {\n mode: AgentModeOption;\n /** Default Cursor model params (id -> value), e.g. { thinking: \"high\" }. */\n params?: Record<string, string>;\n /**\n * Per-model floor params, applied UNDER {@link params} and per-request options\n * (an explicit param always wins). Pins Cursor's boolean toggles, e.g.\n * `{ fast: \"false\" }`, when a turn arrives with no params of its own.\n */\n defaults?: Record<string, string>;\n}\n\nexport interface ResolvedControls {\n mode: AgentModeOption;\n modelSelection: ModelSelection;\n}\n\n/**\n * Build a Cursor `ModelSelection` from a model id and an optional map of model\n * params (e.g. `{ thinking: \"high\" }`). Shared by the provider control\n * resolution and the cloud/delegate tools so param handling stays consistent.\n */\nexport function buildModelSelection(\n modelId: string,\n params?: Record<string, string>,\n): ModelSelection {\n const paramList = Object.entries(params ?? {}).map(([id, value]) => ({ id, value }));\n return paramList.length > 0 ? { id: modelId, params: paramList } : { id: modelId };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isMode(value: unknown): value is AgentModeOption {\n return value === \"agent\" || value === \"plan\";\n}\n\n/**\n * Resolve the per-turn Cursor controls from static config plus opencode's\n * per-request `providerOptions.cursor` (which carries merged model `options` and\n * the selected model `variant`). Per-request values win over static defaults.\n *\n * Recognized keys in `providerOptions.cursor`:\n * - `mode`: \"agent\" | \"plan\"\n * - `params`: Record<string,string> of Cursor model params (e.g. { thinking: \"high\" })\n * - `thinking`: string convenience, mapped to the `thinking` param if not already set\n */\nexport function resolveControls(\n modelId: string,\n staticControls: StaticControls,\n providerOptions: Record<string, unknown> | undefined,\n): ResolvedControls {\n const po = providerOptions ?? {};\n\n const mode: AgentModeOption = isMode(po[\"mode\"]) ? po[\"mode\"] : staticControls.mode;\n\n const params: Record<string, string> = {\n ...(staticControls.defaults ?? {}),\n ...(staticControls.params ?? {}),\n };\n if (isRecord(po[\"params\"])) {\n for (const [key, value] of Object.entries(po[\"params\"])) {\n if (value != null) params[key] = String(value);\n }\n }\n if (typeof po[\"thinking\"] === \"string\" && params[\"thinking\"] === undefined) {\n params[\"thinking\"] = po[\"thinking\"];\n }\n\n return { mode, modelSelection: buildModelSelection(modelId, params) };\n}\n","import { mkdirSync, readFileSync, rmSync, writeFileSync } from \"node:fs\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { TranscriptRecord } from \"./transcript-fingerprint.js\";\n\n/**\n * Best-effort disk persistence for the session pool's fingerprint records, so\n * `session: \"auto\"` survives opencode restarts: the pool can re-resume a\n * session's Cursor agent (whose conversation lives in Cursor's own checkpoint\n * store) instead of paying a cache-cold full-transcript replay.\n *\n * Follows the model-cache pattern: JSON under `~/.cache/opencode-cursor/`,\n * never throws, treats the file as an optimization only. Multiple opencode\n * processes write last-wins on the whole file — a lost record costs exactly\n * one self-healing full replay, which is the same as not having the store.\n */\n\n/** A record persists this long after its last turn before being pruned. */\nconst ENTRY_TTL_MS = 7 * 24 * 60 * 60 * 1000;\n/** Cap stored sessions (most recently used win) to bound file growth. */\nconst MAX_ENTRIES = 200;\n\nexport interface StoredSessionRecord extends TranscriptRecord {\n\tupdatedAt: number;\n}\n\ninterface StoreEnvelope {\n\tsessions: Record<string, StoredSessionRecord>;\n}\n\nfunction storeDir(): string {\n\tconst base =\n\t\tprocess.env.XDG_CACHE_HOME?.trim() ||\n\t\t(homedir() ? join(homedir(), \".cache\") : tmpdir());\n\treturn join(base, \"opencode-cursor\");\n}\n\nfunction storeFile(): string {\n\treturn join(storeDir(), \"session-pool.json\");\n}\n\nfunction isStoredRecord(value: unknown): value is StoredSessionRecord {\n\tif (typeof value !== \"object\" || value === null) return false;\n\tconst v = value as Record<string, unknown>;\n\treturn (\n\t\ttypeof v[\"agentId\"] === \"string\" &&\n\t\ttypeof v[\"systemHash\"] === \"string\" &&\n\t\tArray.isArray(v[\"userHashes\"]) &&\n\t\t(v[\"userHashes\"] as unknown[]).every((h) => typeof h === \"string\") &&\n\t\ttypeof v[\"updatedAt\"] === \"number\"\n\t);\n}\n\n/** Load persisted records, dropping expired/corrupt entries. Never throws. */\nexport function loadSessionRecords(\n\tnow = Date.now(),\n): Map<string, StoredSessionRecord> {\n\tconst out = new Map<string, StoredSessionRecord>();\n\ttry {\n\t\tconst parsed = JSON.parse(\n\t\t\treadFileSync(storeFile(), \"utf8\"),\n\t\t) as StoreEnvelope;\n\t\tif (typeof parsed?.sessions !== \"object\" || parsed.sessions === null)\n\t\t\treturn out;\n\t\tfor (const [key, value] of Object.entries(parsed.sessions)) {\n\t\t\tif (!isStoredRecord(value)) continue;\n\t\t\tif (now - value.updatedAt > ENTRY_TTL_MS) continue;\n\t\t\tout.set(key, value);\n\t\t}\n\t} catch {\n\t\t// Missing/corrupt store: start empty.\n\t}\n\treturn out;\n}\n\n/** Persist records (pruned to TTL + entry cap). Best-effort; never throws. */\nexport function saveSessionRecords(\n\trecords: ReadonlyMap<string, StoredSessionRecord>,\n\tnow = Date.now(),\n): void {\n\ttry {\n\t\tconst live = [...records.entries()]\n\t\t\t.filter(([, r]) => now - r.updatedAt <= ENTRY_TTL_MS)\n\t\t\t.sort(([, a], [, b]) => b.updatedAt - a.updatedAt)\n\t\t\t.slice(0, MAX_ENTRIES);\n\t\tmkdirSync(storeDir(), { recursive: true });\n\t\tconst envelope: StoreEnvelope = { sessions: Object.fromEntries(live) };\n\t\twriteFileSync(storeFile(), JSON.stringify(envelope), \"utf8\");\n\t} catch {\n\t\t// Persistence is an optimization; ignore write failures.\n\t}\n}\n\n/** Delete the store file (test/diagnostic helper). Never throws. */\nexport function deleteSessionStore(): void {\n\ttry {\n\t\trmSync(storeFile(), { force: true });\n\t} catch {\n\t\t// best effort\n\t}\n}\n","import type {\n\tAgentDefinition,\n\tAgentModeOption,\n\tMcpServerConfig,\n\tModelSelection,\n\tSettingSource,\n} from \"@cursor/sdk\";\nimport { loadAgentBackend, type AgentLike } from \"./agent-backend.js\";\nimport {\n\tdeleteSessionStore,\n\tloadSessionRecords,\n\tsaveSessionRecords,\n\ttype StoredSessionRecord,\n} from \"./session-store.js\";\nimport type { TranscriptRecord } from \"./transcript-fingerprint.js\";\n\n/** sessionID -> fingerprint record, so a session reuses one Cursor agent across turns. */\nconst pool = new Map<string, StoredSessionRecord>();\n\n/**\n * Lazily merge disk-persisted records into the in-memory pool (memory wins),\n * so `session: \"auto\"` resumes a session's Cursor agent even after an opencode\n * restart. The agent's conversation itself lives in Cursor's checkpoint store;\n * this only restores our agentId + fingerprint bookkeeping.\n */\nlet hydrated = false;\nfunction hydrate(): void {\n\tif (hydrated) return;\n\thydrated = true;\n\tfor (const [key, record] of loadSessionRecords()) {\n\t\tif (!pool.has(key)) pool.set(key, record);\n\t}\n}\n\n/** Read the fingerprint record pooled for a session (undefined if none). */\nexport function getSessionRecord(\n\tsessionID: string,\n): TranscriptRecord | undefined {\n\thydrate();\n\treturn pool.get(sessionID);\n}\n\n/**\n * Drop a session's pooled record so the NEXT turn classifies as \"new\" (fresh\n * agent + full transcript replay). Called when a multi-message replay fails or\n * aborts mid-sequence: the record was written optimistically with the full new\n * fingerprint before delivery, so leaving it in place would let a later\n * \"continuation\" resume on top of messages the agent never received.\n */\nexport function dropSessionRecord(sessionID: string): void {\n\thydrate();\n\tif (pool.delete(sessionID)) saveSessionRecords(pool);\n}\n\n/** Test/diagnostic helpers. */\nexport function getPooledAgentId(sessionID: string): string | undefined {\n\thydrate();\n\treturn pool.get(sessionID)?.agentId;\n}\nexport function clearAgentPool(): void {\n\tpool.clear();\n\thydrated = true; // don't re-hydrate stale disk state into a cleared pool\n\tdeleteSessionStore();\n}\n/** Test hook: drop in-memory state only, as if the process restarted. */\nexport function resetSessionPoolMemory(): void {\n\tpool.clear();\n\thydrated = false;\n}\n\n/**\n * Per-session chain of pending lock holders, so concurrent turns for the same\n * opencode session serialize across the classify-then-acquire-then-pool-write\n * span instead of racing on the shared `pool` map. Two calls for the SAME\n * sessionID (e.g. opencode's forked title-generation call racing the real\n * first turn) can otherwise both read \"no prior record\", both classify as\n * \"new\", and both write to the pool — whichever's agent-creation round-trip\n * resolves last silently overwrites the other's entry, permanently. Calls for\n * different sessionIDs are unaffected and run fully concurrently.\n */\nconst sessionLocks = new Map<string, Promise<unknown>>();\n\nexport function withSessionLock<T>(\n\tsessionID: string | undefined,\n\tfn: () => Promise<T>,\n): Promise<T> {\n\tif (!sessionID) return fn();\n\tconst prior = sessionLocks.get(sessionID) ?? Promise.resolve();\n\tconst run = prior.then(fn, fn);\n\t// Chained promise for ordering only; errors are handled by the caller via\n\t// the returned `run`, not here.\n\tconst guarded = run.catch(() => {});\n\tsessionLocks.set(sessionID, guarded);\n\tvoid guarded.finally(() => {\n\t\tif (sessionLocks.get(sessionID) === guarded) sessionLocks.delete(sessionID);\n\t});\n\treturn run;\n}\n\nexport interface AcquireAgentParams {\n\tapiKey: string;\n\tmodelSelection: ModelSelection;\n\tmode: AgentModeOption;\n\t/** SDK `local.cwd` accepts a single root or several (multi-root workspace). */\n\tcwd: string | string[];\n\tsettingSources?: SettingSource[];\n\tsandbox?: boolean;\n\tautoReview?: boolean;\n\tmcpServers?: Record<string, McpServerConfig>;\n\tagents?: Record<string, AgentDefinition>;\n\tname?: string;\n\t/**\n\t * Resume this Cursor agent before falling back to a fresh create. Set for a\n\t * fingerprinted \"continuation\" (the pooled agentId) or an explicit\n\t * `providerOptions.cursor.agentId`. A failed resume degrades to create.\n\t */\n\tresumeAgentId?: string;\n\t/**\n\t * Pool the resulting agent under this opencode session id. When set, the\n\t * agent persists across turns (release() does not close it) and `record` is\n\t * stored for the next turn's classification. When undefined, no pooling and\n\t * the agent is closed on release.\n\t */\n\tpoolKey?: string;\n\t/** Fingerprint of the current prompt, stored when `poolKey` is set. */\n\trecord?: { systemHash: string; userHashes: string[]; mcpHash?: string };\n}\n\nexport interface AcquiredAgent {\n\tagent: AgentLike;\n\t/** True when an existing agent was resumed (send only the new turn). */\n\tresumed: boolean;\n\t/** Close the agent unless it's pooled (pooled agents persist for the next turn). */\n\trelease: () => void;\n}\n\n/**\n * Get an agent to run a turn. Attempts a resume of `resumeAgentId` when given,\n * otherwise creates a fresh agent; a failed resume degrades to a fresh create\n * (so a stale/expired pool entry becomes a correct full-transcript turn rather\n * than an error). When `poolKey` is set, the resulting agent + `record` are\n * pooled for the session and survive `release()`.\n */\nexport async function acquireAgent(\n\tparams: AcquireAgentParams,\n): Promise<AcquiredAgent> {\n\tconst backend = loadAgentBackend();\n\n\tconst createOptions = {\n\t\tapiKey: params.apiKey,\n\t\tmodel: params.modelSelection,\n\t\tmode: params.mode,\n\t\tlocal: {\n\t\t\tcwd: params.cwd,\n\t\t\t...(params.settingSources\n\t\t\t\t? { settingSources: params.settingSources }\n\t\t\t\t: {}),\n\t\t\t...(params.sandbox !== undefined\n\t\t\t\t? { sandboxOptions: { enabled: params.sandbox } }\n\t\t\t\t: {}),\n\t\t\t...(params.autoReview !== undefined\n\t\t\t\t? { autoReview: params.autoReview }\n\t\t\t\t: {}),\n\t\t},\n\t\t...(params.mcpServers ? { mcpServers: params.mcpServers } : {}),\n\t\t...(params.agents ? { agents: params.agents } : {}),\n\t\t...(params.name ? { name: params.name } : {}),\n\t};\n\n\tlet agent: AgentLike | undefined;\n\tlet resumed = false;\n\tif (params.resumeAgentId) {\n\t\ttry {\n\t\t\tagent = await backend.resumeAgent(params.resumeAgentId, createOptions);\n\t\t\tresumed = true;\n\t\t} catch {\n\t\t\t// Stale/expired id: fall through to a fresh create (full replay).\n\t\t}\n\t}\n\tif (!agent) {\n\t\tagent = await backend.createAgent(createOptions);\n\t}\n\n\tconst pooling = params.poolKey !== undefined;\n\tif (pooling && params.record) {\n\t\thydrate();\n\t\tpool.set(params.poolKey!, {\n\t\t\tagentId: agent.agentId,\n\t\t\tsystemHash: params.record.systemHash,\n\t\t\tuserHashes: params.record.userHashes,\n\t\t\t...(params.record.mcpHash !== undefined\n\t\t\t\t? { mcpHash: params.record.mcpHash }\n\t\t\t\t: {}),\n\t\t\tupdatedAt: Date.now(),\n\t\t});\n\t\t// Persist so session reuse survives opencode restarts (best-effort).\n\t\tsaveSessionRecords(pool);\n\t}\n\n\tconst release = () => {\n\t\tif (!pooling) {\n\t\t\ttry {\n\t\t\t\tagent!.close();\n\t\t\t} catch {\n\t\t\t\t// best effort\n\t\t\t}\n\t\t}\n\t};\n\n\treturn { agent, resumed, release };\n}\n"],"mappings":"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","names":["cached","existsSync","BRIDGE_KEY","message","isRecord","mkdirSync","readFileSync","rmSync","writeFileSync","join"]}
|