@stablekernel/opencode-cursor 0.4.4 → 0.4.5-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.
package/CHANGELOG.md CHANGED
@@ -4,7 +4,36 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
- _No unreleased changes._
7
+ ## [0.4.5-next.0] — 2026-07-06
8
+
9
+ - **Fixed: Cursor agent rejecting turns as "prompt injection" / "gaslighting."**
10
+ The provider flattened opencode's system prompt into the user-message transcript;
11
+ Cursor's agent (which has its own system prompt) treated that as an injection
12
+ attempt. opencode's system prompt is now delivered through Cursor's authoritative
13
+ rules channel — written to a git-ignored `.cursor/rules/opencode.mdc` and loaded
14
+ via `settingSources` — so opencode keeps control without being flagged. New
15
+ `systemPrompt` option: `"rules"` (default), `"message"` (legacy inline), `"omit"`.
16
+ An explicit `settingSources` opt-out of the `"project"` layer is respected
17
+ (degrades to `"message"` delivery), and a failed rule write degrades gracefully
18
+ instead of failing the turn. The generated rule carries a `generated: opencode-cursor`
19
+ sentinel so a user-owned `opencode.mdc` is never overwritten or deleted (#56).
20
+ - **Added: warm resume for multi-message prompt interjections.** When a resumed
21
+ Cursor agent has several queued user messages, the earlier messages are now
22
+ delivered as silent turns and the final one streams, instead of forcing a cold
23
+ full-transcript replay. A tail mismatch safely falls back to cold replay so no
24
+ messages are dropped (#57).
25
+ - **Fixed: resumed turns against an expired Cursor agent are retried.** A pooled
26
+ agent whose server-side state expired would pass `resume()` locally but error in
27
+ `run.wait()`; the turn now retries once with a fresh agent when nothing was
28
+ emitted yet, re-pointing the pool (#52).
29
+ - **Fixed: file/dir `@`-mentions no longer fail the turn.** The local Cursor SDK
30
+ agent rejects attachment forms, so file/dir mentions are now noted as text
31
+ (with a filesystem path for `file://` sources) instead of attached; raw base64
32
+ data without a filename is guarded against inlining a blob into the note (#58).
33
+ - **Added: warn when the installed plugin lags the npm registry latest.** A
34
+ throttled (24h) startup check compares the installed version against `latest`
35
+ and prints a one-line upgrade hint; skipped under `CI` / `NO_UPDATE_NOTIFIER`,
36
+ never blocks init (#33).
8
37
 
9
38
  ## [0.4.4] — 2026-06-24
10
39
 
package/README.md CHANGED
@@ -51,8 +51,24 @@ Add to your `opencode.json` (or `opencode.jsonc` — both are supported):
51
51
  ```
52
52
 
53
53
  The `@latest` suffix makes opencode re-resolve to the newest release on each
54
- startup. Drop it (`"@stablekernel/opencode-cursor"`) or pin a version
55
- (`"@stablekernel/opencode-cursor@1.2.3"`) if you prefer.
54
+ startup. In practice opencode caches the `@latest` plugin install and does **not**
55
+ auto-update it. If you see a stale-version warning from the plugin, or a version
56
+ mismatch, exit opencode and clear the cached package:
57
+
58
+ ```bash
59
+ # macOS / Linux
60
+ rm -rf ~/.cache/opencode/packages/@stablekernel/opencode-cursor@latest
61
+ # Windows
62
+ rmdir /s /q "%LocalAppData%\opencode\cache\packages\@stablekernel\opencode-cursor@latest"
63
+ ```
64
+
65
+ Then restart opencode.
66
+
67
+ Drop `@latest` (`"@stablekernel/opencode-cursor"`) or pin a version
68
+ (`"@stablekernel/opencode-cursor@1.2.3"`) if you prefer deterministic installs.
69
+
70
+ The stale-version check is skipped when the `CI` or `NO_UPDATE_NOTIFIER`
71
+ environment variable is set.
56
72
 
57
73
  The plugin injects the `provider` block automatically. If you need explicit control:
58
74
 
@@ -106,6 +122,9 @@ The plugin also registers two **delegation tools**:
106
122
  > - Set `sandbox: true` in `provider.cursor.options` to run Cursor's tools in Cursor's sandbox.
107
123
  > - Use `cursor_delegate` instead of the provider path — it is gated by opencode's `permission`
108
124
  > config.
125
+ > - By default opencode's system prompt is delivered via a git-ignored Cursor
126
+ > rule (`systemPrompt: "rules"`), not inlined into the message stream. See
127
+ > [System prompt](#system-prompt).
109
128
 
110
129
  See [SECURITY.md](./SECURITY.md) for the full threat model.
111
130
 
@@ -124,6 +143,7 @@ See [SECURITY.md](./SECURITY.md) for the full threat model.
124
143
  | `forwardMcp` | `true` | Forward opencode's configured MCP servers to the Cursor agent |
125
144
  | `mcpServers` | — | Extra MCP servers (Cursor `McpServerConfig` shape); merged with forwarded ones |
126
145
  | `toolDisplay` | `"blocks"` | How Cursor's internal tool activity is shown — see [Tool display](#tool-display) |
146
+ | `systemPrompt` | `"rules"` | How opencode's system prompt reaches the agent — see [System prompt](#system-prompt) |
127
147
 
128
148
  | Environment variable | Default | Meaning |
129
149
  | --- | --- | --- |
@@ -174,6 +194,31 @@ To set controls statically per model:
174
194
  } } } }
175
195
  ```
176
196
 
197
+ ## System prompt
198
+
199
+ opencode drives the Cursor agent the way it drives any provider — through its
200
+ **system prompt**. But the Cursor SDK has no system-prompt input (an agent, not a
201
+ raw model), and flattening opencode's system prompt into the message stream makes
202
+ injection-hardened Cursor models reject it as a prompt-injection attempt.
203
+
204
+ So by default (`systemPrompt: "rules"`) the plugin writes opencode's system prompt
205
+ to `<cwd>/.cursor/rules/opencode.mdc` (`alwaysApply: true`, git-ignored) and loads
206
+ the `project` settings layer, delivering it through Cursor's **authoritative rules
207
+ channel**. Cursor treats rules as system-level instructions, so opencode stays in
208
+ control and nothing is flagged.
209
+
210
+ Tradeoffs to know:
211
+
212
+ - A project rule also applies to **your own Cursor IDE** open on this repo. The
213
+ plugin removes the file when the session disposes (best-effort).
214
+ - Enabling the `project` layer also loads other `.cursor/` config (`.cursor/mcp.json`,
215
+ `.cursor/agents`, hooks).
216
+
217
+ Alternatives:
218
+
219
+ - `systemPrompt: "message"` — legacy inline delivery (may be rejected as injection).
220
+ - `systemPrompt: "omit"` — don't forward the system prompt at all.
221
+
177
222
  ## MCP servers
178
223
 
179
224
  With `forwardMcp: true` (default), the Cursor agent uses the same MCP servers configured in
@@ -267,9 +312,10 @@ Override with `OPENCODE_CURSOR_SIDECAR=1` (always sidecar) or `OPENCODE_CURSOR_S
267
312
  on your `PATH`. Install Node.js 22+, or force the sidecar with `OPENCODE_CURSOR_SIDECAR=1`.
268
313
  - **"Running under Bun without a usable Node sidecar" warning.** Install Node.js 22+, or set
269
314
  `OPENCODE_CURSOR_SIDECAR=0` to accept in-process behavior and silence the warning.
270
- - **Plugin enabled but no `cursor` provider/models appear.** Stale opencode plugin cache. Pin an
271
- exact version (`@stablekernel/opencode-cursor@<version>`) or delete
272
- `~/.cache/opencode/packages/` and restart.
315
+ - **Plugin enabled but no `cursor` provider/models appear, or you see a stale-version warning.**
316
+ opencode caches the `@latest` plugin install on first use and never refreshes it.
317
+ Exit opencode, delete `~/.cache/opencode/packages/@stablekernel/opencode-cursor@latest`
318
+ (or the pinned version directory), and restart.
273
319
  - **Only the four fallback models appear.** The live catalog loads after the first authenticated
274
320
  use. Restart opencode once after login, or run `cursor_refresh_models`.
275
321
  - **Invalid or expired key.** Validated on first use — that's where the error surfaces.
@@ -16,6 +16,101 @@ function fingerprintApiKey(apiKey) {
16
16
  return createHash("sha256").update(apiKey).digest("hex").slice(0, 16);
17
17
  }
18
18
 
19
+ // src/provider/system-rule.ts
20
+ import {
21
+ mkdirSync,
22
+ writeFileSync,
23
+ readFileSync,
24
+ existsSync,
25
+ rmSync
26
+ } from "fs";
27
+ import { join } from "path";
28
+ var RULES_DIR = join(".cursor", "rules");
29
+ var RULE_FILE = "opencode.mdc";
30
+ var IGNORE_FILE = ".gitignore";
31
+ var SENTINEL = "generated: opencode-cursor";
32
+ function extractSystemText(prompt) {
33
+ const parts = [];
34
+ for (const message of prompt) {
35
+ if (message.role === "system") parts.push(message.content);
36
+ }
37
+ return parts.join("\n\n").trim();
38
+ }
39
+ function isGenerated(content) {
40
+ if (!content.startsWith("---")) return false;
41
+ const end = content.indexOf("\n---", 3);
42
+ const frontmatter = end === -1 ? content : content.slice(0, end);
43
+ return frontmatter.split(/\r?\n/).includes(SENTINEL);
44
+ }
45
+ function writeSystemRule(cwd, systemText) {
46
+ if (!systemText) return "empty";
47
+ const dir = join(cwd, RULES_DIR);
48
+ const path = join(dir, RULE_FILE);
49
+ const body = `---
50
+ alwaysApply: true
51
+ ${SENTINEL}
52
+ ---
53
+
54
+ ${systemText}
55
+ `;
56
+ const existing = existsSync(path) ? readFileSync(path, "utf8") : void 0;
57
+ if (existing !== void 0) {
58
+ if (!isGenerated(existing)) return "blocked";
59
+ if (existing === body) return "unchanged";
60
+ }
61
+ mkdirSync(dir, { recursive: true });
62
+ writeFileSync(path, body, "utf8");
63
+ ensureGitIgnored(dir);
64
+ return "written";
65
+ }
66
+ function ensureGitIgnored(dir) {
67
+ const path = join(dir, IGNORE_FILE);
68
+ const existing = existsSync(path) ? readFileSync(path, "utf8") : "";
69
+ const lines = existing.split(/\r?\n/);
70
+ const missing = [RULE_FILE, IGNORE_FILE].filter(
71
+ (entry) => !lines.includes(entry)
72
+ );
73
+ if (missing.length === 0) return;
74
+ const prefix = existing && !existing.endsWith("\n") ? `${existing}
75
+ ` : existing;
76
+ writeFileSync(path, `${prefix}${missing.join("\n")}
77
+ `, "utf8");
78
+ }
79
+ function removeSystemRule(cwd) {
80
+ try {
81
+ const path = join(cwd, RULES_DIR, RULE_FILE);
82
+ if (isGenerated(readFileSync(path, "utf8"))) rmSync(path);
83
+ } catch {
84
+ }
85
+ }
86
+ function resolveSystemDelivery(options) {
87
+ const { mode, settingSources, cwd, systemText, warn } = options;
88
+ if (mode !== "rules") return { mode, settingSources };
89
+ if (settingSources && !settingSources.includes("project")) {
90
+ warn(
91
+ '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.'
92
+ );
93
+ return { mode: "message", settingSources };
94
+ }
95
+ let result;
96
+ try {
97
+ result = writeSystemRule(cwd, systemText);
98
+ } catch (error) {
99
+ warn(
100
+ `failed to write .cursor/rules/${RULE_FILE} (${error instanceof Error ? error.message : String(error)}); delivering the system prompt inline ("message" mode) for this turn.`
101
+ );
102
+ return { mode: "message", settingSources };
103
+ }
104
+ if (result === "blocked") {
105
+ warn(
106
+ `.cursor/rules/${RULE_FILE} exists but was not generated by opencode-cursor; leaving it untouched and delivering the system prompt inline ("message" mode).`
107
+ );
108
+ return { mode: "message", settingSources };
109
+ }
110
+ if (result === "empty") return { mode, settingSources };
111
+ return { mode, settingSources: settingSources ?? ["project"] };
112
+ }
113
+
19
114
  // src/provider/agent-events.ts
20
115
  function toolDisplayName(toolCall) {
21
116
  if (!toolCall) return "tool";
@@ -80,17 +175,7 @@ async function* streamAgentTurn(agent, message, options) {
80
175
  });
81
176
  };
82
177
  options.abortSignal?.addEventListener("abort", onAbort);
83
- const sendTurn = async () => {
84
- try {
85
- return await agent.send(message, { mode: options.mode, onDelta });
86
- } catch (err) {
87
- if (err instanceof Error && err.name === "AgentBusyError") {
88
- if (debug) console.error("[cursor:debug] agent busy; retrying send with local.force");
89
- return agent.send(message, { mode: options.mode, onDelta, local: { force: true } });
90
- }
91
- throw err;
92
- }
93
- };
178
+ const sendTurn = () => sendWithBusyRetry(agent, message, { mode: options.mode, onDelta }, debug);
94
179
  void sendTurn().then(async (run) => {
95
180
  runHolder.run = run;
96
181
  const result = await run.wait();
@@ -130,6 +215,42 @@ async function* streamAgentTurn(agent, message, options) {
130
215
  options.abortSignal?.removeEventListener("abort", onAbort);
131
216
  }
132
217
  }
218
+ async function sendWithBusyRetry(agent, message, sendOptions, debug) {
219
+ try {
220
+ return await agent.send(message, sendOptions);
221
+ } catch (err) {
222
+ if (err instanceof Error && err.name === "AgentBusyError") {
223
+ if (debug) console.error("[cursor:debug] agent busy; retrying send with local.force");
224
+ return agent.send(message, { ...sendOptions, local: { force: true } });
225
+ }
226
+ throw err;
227
+ }
228
+ }
229
+ async function sendAgentTurnSilently(agent, message, options) {
230
+ if (options.abortSignal?.aborted) return;
231
+ const debug = process.env.OPENCODE_CURSOR_DEBUG === "1";
232
+ const runHolder = {};
233
+ const onAbort = () => {
234
+ void Promise.resolve(runHolder.run?.cancel()).catch(() => {
235
+ });
236
+ };
237
+ options.abortSignal?.addEventListener("abort", onAbort);
238
+ try {
239
+ const run = await sendWithBusyRetry(agent, message, { mode: options.mode }, debug);
240
+ runHolder.run = run;
241
+ if (options.abortSignal?.aborted) void Promise.resolve(run.cancel()).catch(() => {
242
+ });
243
+ const result = await run.wait();
244
+ if (result.status !== "finished") {
245
+ if (options.abortSignal?.aborted) return;
246
+ throw new Error(
247
+ `Cursor run ended with status "${result.status}"${result.result ? `: ${result.result}` : ""}`
248
+ );
249
+ }
250
+ } finally {
251
+ options.abortSignal?.removeEventListener("abort", onAbort);
252
+ }
253
+ }
133
254
 
134
255
  // src/provider/controls.ts
135
256
  function buildModelSelection(modelId, params) {
@@ -174,7 +295,7 @@ async function loadCursorSdk() {
174
295
 
175
296
  // src/provider/agent-backend.ts
176
297
  import { execSync } from "child_process";
177
- import { existsSync } from "fs";
298
+ import { existsSync as existsSync2 } from "fs";
178
299
  import { fileURLToPath } from "url";
179
300
 
180
301
  // src/provider/sidecar-client.ts
@@ -415,7 +536,7 @@ function resolveSidecarScript() {
415
536
  ];
416
537
  for (const candidate of candidates) {
417
538
  const path = fileURLToPath(new URL(candidate, import.meta.url));
418
- if (existsSync(path)) return path;
539
+ if (existsSync2(path)) return path;
419
540
  }
420
541
  return void 0;
421
542
  }
@@ -446,17 +567,17 @@ function loadAgentBackend() {
446
567
  }
447
568
 
448
569
  // src/provider/session-store.ts
449
- import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
570
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
450
571
  import { homedir, tmpdir } from "os";
451
- import { join } from "path";
572
+ import { join as join2 } from "path";
452
573
  var ENTRY_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
453
574
  var MAX_ENTRIES = 200;
454
575
  function storeDir() {
455
- const base = process.env.XDG_CACHE_HOME?.trim() || (homedir() ? join(homedir(), ".cache") : tmpdir());
456
- return join(base, "opencode-cursor");
576
+ const base = process.env.XDG_CACHE_HOME?.trim() || (homedir() ? join2(homedir(), ".cache") : tmpdir());
577
+ return join2(base, "opencode-cursor");
457
578
  }
458
579
  function storeFile() {
459
- return join(storeDir(), "session-pool.json");
580
+ return join2(storeDir(), "session-pool.json");
460
581
  }
461
582
  function isStoredRecord(value) {
462
583
  if (typeof value !== "object" || value === null) return false;
@@ -467,7 +588,7 @@ function loadSessionRecords(now = Date.now()) {
467
588
  const out = /* @__PURE__ */ new Map();
468
589
  try {
469
590
  const parsed = JSON.parse(
470
- readFileSync(storeFile(), "utf8")
591
+ readFileSync2(storeFile(), "utf8")
471
592
  );
472
593
  if (typeof parsed?.sessions !== "object" || parsed.sessions === null)
473
594
  return out;
@@ -483,9 +604,9 @@ function loadSessionRecords(now = Date.now()) {
483
604
  function saveSessionRecords(records, now = Date.now()) {
484
605
  try {
485
606
  const live = [...records.entries()].filter(([, r]) => now - r.updatedAt <= ENTRY_TTL_MS).sort(([, a], [, b]) => b.updatedAt - a.updatedAt).slice(0, MAX_ENTRIES);
486
- mkdirSync(storeDir(), { recursive: true });
607
+ mkdirSync2(storeDir(), { recursive: true });
487
608
  const envelope = { sessions: Object.fromEntries(live) };
488
- writeFileSync(storeFile(), JSON.stringify(envelope), "utf8");
609
+ writeFileSync2(storeFile(), JSON.stringify(envelope), "utf8");
489
610
  } catch {
490
611
  }
491
612
  }
@@ -504,6 +625,10 @@ function getSessionRecord(sessionID) {
504
625
  hydrate();
505
626
  return pool.get(sessionID);
506
627
  }
628
+ function dropSessionRecord(sessionID) {
629
+ hydrate();
630
+ if (pool.delete(sessionID)) saveSessionRecords(pool);
631
+ }
507
632
  async function acquireAgent(params) {
508
633
  const backend = loadAgentBackend();
509
634
  const createOptions = {
@@ -557,11 +682,16 @@ async function acquireAgent(params) {
557
682
  export {
558
683
  resolveCursorApiKey,
559
684
  fingerprintApiKey,
685
+ extractSystemText,
686
+ removeSystemRule,
687
+ resolveSystemDelivery,
560
688
  streamAgentTurn,
689
+ sendAgentTurnSilently,
561
690
  buildModelSelection,
562
691
  resolveControls,
563
692
  loadCursorSdk,
564
693
  getSessionRecord,
694
+ dropSessionRecord,
565
695
  acquireAgent
566
696
  };
567
- //# sourceMappingURL=chunk-KGDYXGZL.js.map
697
+ //# sourceMappingURL=chunk-734L3SKU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/api-key.ts","../src/provider/system-rule.ts","../src/provider/agent-events.ts","../src/provider/controls.ts","../src/cursor-runtime.ts","../src/provider/agent-backend.ts","../src/provider/sidecar-client.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 {\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 { AgentModeOption, SDKUserMessage } from \"@cursor/sdk\";\nimport type { AgentLike, AgentRunLike } from \"./agent-backend.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-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: \"finish\"; text?: string };\n\nexport interface StreamAgentTurnOptions {\n mode: AgentModeOption;\n abortSignal?: AbortSignal;\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 * 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 const push = (event: CursorEvent) => {\n queue.push(event);\n wake?.();\n wake = undefined;\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 \"tool-call-started\":\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 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 if (update.usage) push({ type: \"usage\", usage: update.usage as CursorUsage });\n break;\n }\n };\n\n const runHolder: { run?: AgentRunLike } = {};\n const onAbort = () => {\n void Promise.resolve(runHolder.run?.cancel()).catch(() => {});\n };\n options.abortSignal?.addEventListener(\"abort\", onAbort);\n\n const sendTurn = (): Promise<AgentRunLike> =>\n sendWithBusyRetry(agent, message, { mode: options.mode, onDelta }, debug);\n\n // Kick off the turn. Resolve text from run.wait() for models that don't emit\n // incremental text deltas.\n void sendTurn()\n .then(async (run) => {\n runHolder.run = run;\n const result = await run.wait();\n if (debug) {\n console.error(\n `[cursor:debug] updates=${JSON.stringify(counts)} status=${result.status} resultLen=${(result.result ?? \"\").length}`,\n );\n }\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 failure = err;\n if (debug) console.error(`[cursor:debug] send failed: ${err instanceof Error ? err.message : String(err)}`);\n })\n .finally(() => {\n finished = true;\n wake?.();\n wake = undefined;\n });\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 options.abortSignal?.removeEventListener(\"abort\", onAbort);\n }\n}\n\n/**\n * Send a message on an agent, retrying once with the SDK's documented recovery\n * path on `AgentBusyError`. A previous opencode/CLI crash (or a second instance\n * racing on the same agent store) can leave a persisted run wedged; the SDK then\n * rejects new sends with `AgentBusyError`. `local.force` expires the wedged run\n * instead of failing the turn. Shared by streaming and silent sends.\n */\nasync function sendWithBusyRetry(\n agent: AgentLike,\n message: SDKUserMessage,\n sendOptions: {\n mode: AgentModeOption;\n onDelta?: (args: { update: { type: string } & Record<string, any> }) => void;\n },\n debug: boolean,\n): Promise<AgentRunLike> {\n try {\n return await agent.send(message, sendOptions);\n } catch (err) {\n if (err instanceof Error && err.name === \"AgentBusyError\") {\n if (debug) console.error(\"[cursor:debug] agent busy; retrying send with local.force\");\n return agent.send(message, { ...sendOptions, local: { force: true } });\n }\n throw err;\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 * - Usage undercount: no onDelta means the `turn-ended` usage update is never\n * observed, so opencode slightly undercounts tokens on multi-message turns.\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<void> {\n // Already aborted: don't start a turn just to cancel it.\n if (options.abortSignal?.aborted) return;\n const debug = process.env.OPENCODE_CURSOR_DEBUG === \"1\";\n const runHolder: { run?: AgentRunLike } = {};\n const onAbort = () => {\n void Promise.resolve(runHolder.run?.cancel()).catch(() => {});\n };\n options.abortSignal?.addEventListener(\"abort\", onAbort);\n try {\n const run = await sendWithBusyRetry(agent, message, { mode: options.mode }, debug);\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;\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 } 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\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> = { ...(staticControls.params ?? {}) };\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","/**\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","/**\n * Selects where Cursor agents run:\n *\n * - \"in-process\": straight through `@cursor/sdk` in this process (Node — the\n * normal path for tests, scripts, and any non-Bun host).\n * - \"sidecar\": a spawned Node child hosting the SDK (Bun — opencode's runtime —\n * has a `node:http2` bug that kills Cursor's streaming RPC with\n * NGHTTP2_FRAME_SIZE_ERROR, losing tool-completion updates; see\n * src/sidecar/agent-host.mjs).\n *\n * Override with OPENCODE_CURSOR_SIDECAR=1/0 (force on/off).\n */\nimport { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { loadCursorSdk } from \"../cursor-runtime.js\";\nimport { SidecarClient, type AgentLike } from \"./sidecar-client.js\";\n\nexport type { AgentLike, AgentRunLike, AgentSendOptions } from \"./sidecar-client.js\";\n\nexport type BackendKind = \"in-process\" | \"sidecar\";\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/** Pure selection logic (unit-testable without spawning anything). */\nexport function resolveBackendKind(env: BackendEnvironment): BackendKind {\n const override = process.env[\"OPENCODE_CURSOR_SIDECAR\"];\n if (override === \"0\" || override === \"false\") return \"in-process\";\n if (override === \"1\" || override === \"true\") return env.nodePath ? \"sidecar\" : \"in-process\";\n return env.isBun && env.nodePath ? \"sidecar\" : \"in-process\";\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\nfunction inProcessBackend(): AgentBackend {\n return {\n kind: \"in-process\",\n createAgent: async (options) => {\n const { Agent } = await loadCursorSdk();\n return (await Agent.create(options as never)) as unknown as AgentLike;\n },\n resumeAgent: async (agentId, options) => {\n const { Agent } = await loadCursorSdk();\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({ scriptPath, nodePath });\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 kind = resolveBackendKind(env);\n const scriptPath = kind === \"sidecar\" ? resolveSidecarScript() : undefined;\n // A user who explicitly opted out (OPENCODE_CURSOR_SIDECAR=0/false) has\n // accepted the in-process behavior and should not be warned.\n const override = process.env[\"OPENCODE_CURSOR_SIDECAR\"];\n const optedOut = override === \"0\" || override === \"false\";\n if (env.isBun && !optedOut && (kind === \"in-process\" || !scriptPath)) {\n console.error(\n \"[opencode-cursor] Running under Bun without a usable Node sidecar \" +\n `(node: ${env.nodePath ?? \"not found\"}, script: ${scriptPath ?? \"not found\"}): ` +\n \"Cursor native tool calls may fail (Bun node:http2 incompatibility). \" +\n \"Install Node.js to enable the sidecar, or set OPENCODE_CURSOR_SIDECAR=0 \" +\n \"to silence this warning.\",\n );\n }\n cached =\n kind === \"sidecar\" && env.nodePath && scriptPath\n ? sidecarBackend(env.nodePath, scriptPath)\n : inProcessBackend();\n }\n return cached;\n}\n\n/** Test hook. */\nexport function resetAgentBackend(): void {\n cached = undefined;\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}\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\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 { name?: string; message?: string };\n const err = new Error(e.message ?? \"sidecar error\");\n if (e.name) err.name = e.name;\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 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 },\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 { 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\nexport interface AcquireAgentParams {\n\tapiKey: string;\n\tmodelSelection: ModelSelection;\n\tmode: AgentModeOption;\n\tcwd: string;\n\tsettingSources?: SettingSource[];\n\tsandbox?: 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},\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":["existsSync","existsSync","cached","mkdirSync","readFileSync","rmSync","writeFileSync","join"]}