@stablekernel/opencode-cursor 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -3
- package/README.md +67 -16
- package/dist/{chunk-D4YQ7ZEM.js → chunk-BTI2NHEE.js} +73 -8
- package/dist/chunk-BTI2NHEE.js.map +1 -0
- package/dist/plugin/index.js +100 -13
- package/dist/plugin/index.js.map +1 -1
- package/dist/provider/index.d.ts +7 -5
- package/dist/provider/index.js +137 -17
- package/dist/provider/index.js.map +1 -1
- package/package.json +6 -1
- package/dist/chunk-D4YQ7ZEM.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,52 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
- **Fingerprint-guarded session reuse, now the default (`session: "auto"`).**
|
|
8
|
+
Previously the provider created a fresh Cursor agent every turn and re-sent
|
|
9
|
+
the whole transcript (robust but cache-hostile and increasingly costly as a
|
|
10
|
+
conversation grows), while opt-in `session: true` resumed one agent per
|
|
11
|
+
session but could drift from opencode's history (edits/reverts/compaction) and
|
|
12
|
+
was disturbed by non-chat side calls. `session: "auto"` (the new default)
|
|
13
|
+
hashes only the parts opencode replays verbatim — the system prompt and the
|
|
14
|
+
user-message sequence — and classifies each turn: a clean **continuation**
|
|
15
|
+
resumes the pooled agent and sends only the new message (maximizing prefix
|
|
16
|
+
cache hits); a **side-call** (system prompt differs, e.g. title generation)
|
|
17
|
+
runs a fresh ephemeral agent without touching the pool; a **divergence**
|
|
18
|
+
(edit/revert/compaction/queued messages) or a failed resume falls back to a
|
|
19
|
+
fresh agent + full transcript and re-pools. Worst case is one self-healing
|
|
20
|
+
full replay — never worse than the old default. `session: true` is now an
|
|
21
|
+
alias for `"auto"`; `session: false` keeps the always-fresh behavior.
|
|
22
|
+
Set `OPENCODE_CURSOR_DEBUG=1` to log per-turn classification and cache usage.
|
|
23
|
+
- **Session reuse survives opencode restarts.** The pool's fingerprint records
|
|
24
|
+
persist (best-effort) to `~/.cache/opencode-cursor/session-pool.json` (7-day
|
|
25
|
+
TTL, 200-entry LRU cap), so the first turn after a restart resumes the
|
|
26
|
+
session's Cursor agent — whose conversation lives in Cursor's own checkpoint
|
|
27
|
+
store — instead of paying a cache-cold full-transcript replay.
|
|
28
|
+
- **MCP servers are re-forwarded live, per turn, with OAuth mapping.** The
|
|
29
|
+
`config` hook's startup snapshot meant mid-session MCP enable/disable never
|
|
30
|
+
reached the Cursor agent. The `chat.params` hook now forwards the live set
|
|
31
|
+
each turn (`client.mcp.status()` for runtime truth, `client.config.get()` for
|
|
32
|
+
launch specs). Because a resumed agent keeps its original servers, a changed
|
|
33
|
+
set forces a fresh agent (full-transcript replay, re-pooled) so the new
|
|
34
|
+
servers take effect — the session fingerprint carries an `mcpHash` for this.
|
|
35
|
+
Remote servers with a registered OAuth client are forwarded with a Cursor
|
|
36
|
+
`auth` block so the agent runs its own OAuth flow; servers needing OAuth
|
|
37
|
+
without a shareable `clientId` (dynamic registration) are skipped with a
|
|
38
|
+
one-time toast instead of forwarding a spec that would 401.
|
|
39
|
+
- **Fixed: text/reasoning streamed after a tool call rendered above the tool
|
|
40
|
+
block.** The earlier ordering fix closed parts on text↔reasoning transitions,
|
|
41
|
+
but blocks-mode tool parts were emitted while the narration part stayed open
|
|
42
|
+
— and hosts position a part where it started. Open text/reasoning parts are
|
|
43
|
+
now closed before tool parts are emitted (except for buffered edit calls,
|
|
44
|
+
which emit nothing until their result arrives, so narration isn't split
|
|
45
|
+
needlessly).
|
|
46
|
+
- **Tool outputs are included (truncated) in flattened transcripts.** The
|
|
47
|
+
fresh/divergence/`session: false` replay paths previously dropped Cursor tool
|
|
48
|
+
results to bare `[result of X]` placeholders, so a fresh agent re-read a
|
|
49
|
+
transcript with prior tool outputs missing. Outputs are now inlined and capped
|
|
50
|
+
(2,000 chars per result, 500 per tool-call args) so context stays faithful
|
|
51
|
+
without unbounded bloat.
|
|
52
|
+
|
|
7
53
|
## [0.2.0] — 2026-06-11
|
|
8
54
|
|
|
9
55
|
- **More Cursor tools map onto opencode's native tool renderers (blocks mode).**
|
|
@@ -47,7 +93,8 @@ and a permission-gated delegation tool surface.
|
|
|
47
93
|
- **Session reuse** (`session: true`) — keeps one Cursor agent per opencode
|
|
48
94
|
session via `Agent.resume()` across turns, with automatic fallback to a fresh
|
|
49
95
|
agent. A run wedged by a crashed/duplicate process is recovered by retrying
|
|
50
|
-
the send once with the SDK's `local.force` escape hatch.
|
|
96
|
+
the send once with the SDK's `local.force` escape hatch. (Superseded by the
|
|
97
|
+
fingerprint-guarded `session: "auto"` default; see Unreleased.)
|
|
51
98
|
- **Native diff viewer for Cursor edits (blocks mode).** A Cursor `edit` tool
|
|
52
99
|
call is now surfaced under opencode's registered `edit` tool with its real
|
|
53
100
|
unified diff in `metadata.diff`, so opencode renders its built-in diff viewer
|
|
@@ -61,7 +108,7 @@ and a permission-gated delegation tool surface.
|
|
|
61
108
|
- `"blocks"` (default) emits structured, provider-executed **dynamic** `tool-call` /
|
|
62
109
|
`tool-result` parts so opencode renders native tool blocks. Names are
|
|
63
110
|
`cursor_`-prefixed and sanitized (`shell` → `cursor_shell`,
|
|
64
|
-
`
|
|
111
|
+
`myserver/find_symbol` → `cursor_myserver_find_symbol`) so they can't collide
|
|
65
112
|
with opencode-registered tools, and carry `providerExecuted: true` +
|
|
66
113
|
`dynamic: true` so ai v6's `parseToolCall` accepts them without
|
|
67
114
|
registered-tool validation. Tool-results use the V3-spec `result` +
|
|
@@ -97,7 +144,7 @@ and a permission-gated delegation tool surface.
|
|
|
97
144
|
rather than showing only the fallback snapshot.
|
|
98
145
|
- **MCP server forwarding** — opencode's configured `config.mcp` entries are
|
|
99
146
|
translated to Cursor `McpServerConfig` and passed to the local agent so it can
|
|
100
|
-
use the same servers
|
|
147
|
+
use the same servers. Opt out with `provider.cursor.options.forwardMcp`.
|
|
101
148
|
- **Model discovery** with a 24-hour cache (keyed by key fingerprint) and a
|
|
102
149
|
built-in fallback snapshot (composer-2.5, claude-opus-4-8, claude-sonnet-4-6,
|
|
103
150
|
gpt-5.5) for use without an API key.
|
package/README.md
CHANGED
|
@@ -152,20 +152,53 @@ This plugin also registers two **delegation tools** that complement the provider
|
|
|
152
152
|
| `settingSources` | — | Cursor settings layers to load from disk: `["project","user","all",...]` — pulls in your Cursor **skills**, rules, and `.cursor/mcp.json` |
|
|
153
153
|
| `sandbox` | — | Run the agent's tools inside Cursor's sandbox (`true`/`false`) |
|
|
154
154
|
| `agents` | — | Cursor subagent definitions (`{ <name>: { description, prompt, model?, mcpServers? } }`) |
|
|
155
|
-
| `session` | `
|
|
155
|
+
| `session` | `"auto"` | Session reuse strategy: `"auto"` (fingerprint-guarded resume), `true` (alias for `"auto"`), or `false` (always fresh). See below |
|
|
156
156
|
| `forwardMcp` | `true` | Forward opencode's configured MCP servers to the Cursor agent |
|
|
157
157
|
| `mcpServers` | — | Extra MCP servers (Cursor `McpServerConfig` shape); merged with forwarded ones |
|
|
158
158
|
| `toolDisplay` | `"blocks"` | How Cursor's internal tool activity is shown: `"blocks"` (structured provider-executed tool blocks; default, requires opencode 1.16+) or `"reasoning"` (compact lines, the fallback for older/non-V3 hosts). See [Tool display](#tool-display) |
|
|
159
159
|
|
|
160
160
|
### Session reuse (`session`)
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
162
|
+
opencode re-sends the **entire** conversation transcript on every turn. Replaying that into a fresh
|
|
163
|
+
Cursor agent each turn is robust but costs more input tokens as the conversation grows (and pays
|
|
164
|
+
opencode's system prompt on top of Cursor's own). Reusing one Cursor agent and sending only the new
|
|
165
|
+
message is the cache-friendly, native-CLI-like path — but a blindly resumed agent can drift from
|
|
166
|
+
opencode's view of history (message edits, reverts, opencode-side compaction) and must not be
|
|
167
|
+
disturbed by opencode's non-chat side calls (e.g. title generation).
|
|
168
|
+
|
|
169
|
+
**`session: "auto"` (the default) resolves this with a per-turn fingerprint.** The provider hashes
|
|
170
|
+
only the parts opencode replays verbatim — the system prompt and the user-message sequence — and
|
|
171
|
+
classifies each turn:
|
|
172
|
+
|
|
173
|
+
| Situation | Classification | What the provider does |
|
|
174
|
+
| --- | --- | --- |
|
|
175
|
+
| First turn of the session | **new** | fresh agent, full transcript, pool it |
|
|
176
|
+
| System prompt differs (title gen and other side calls) | **side-call** | fresh ephemeral agent; the pooled agent is left untouched |
|
|
177
|
+
| Prior user sequence is an exact prefix + exactly one new user message | **continuation** | `Agent.resume` the pooled agent, send **only** the new message |
|
|
178
|
+
| Continuation, but the forwarded MCP server set changed | **continuation** (fresh agent) | fresh agent + full transcript, re-pool — a resumed agent keeps its original MCP servers, so a fresh one is needed for the new set |
|
|
179
|
+
| Earlier message edited/reverted, conversation compacted, or several messages queued | **divergence** | fresh agent, full transcript, re-pool |
|
|
180
|
+
|
|
181
|
+
The worst case on any misclassification is a single full-transcript replay that self-heals on the
|
|
182
|
+
next turn — never worse than `session: false`. A failed resume also degrades to a fresh replay. The
|
|
183
|
+
resumed agent is named after the session and visible in Cursor's dashboard; the opencode session id
|
|
184
|
+
reaches the provider via the plugin's `chat.params` hook (`providerOptions.cursor.sessionID`).
|
|
185
|
+
Fingerprint records persist (best-effort) to `~/.cache/opencode-cursor/session-pool.json`, so
|
|
186
|
+
session reuse survives opencode restarts — the conversation itself lives in Cursor's own local
|
|
187
|
+
checkpoint store, and the next turn resumes it instead of replaying the transcript.
|
|
188
|
+
|
|
189
|
+
- `session: true` is an alias for `"auto"`.
|
|
190
|
+
- `session: false` restores the original behavior: always a fresh agent + full transcript, every
|
|
191
|
+
turn. Use it if you want each turn fully independent.
|
|
192
|
+
|
|
193
|
+
**Cache implications.** Cursor builds prompts cache-friendly and the model provider's own prefix
|
|
194
|
+
cache (Anthropic uses a ~5-minute sliding TTL) decides hits. `"auto"` keeps the prompt prefix stable
|
|
195
|
+
across turns, which is what lands cache reads instead of expensive re-seeds. Things that re-seed the
|
|
196
|
+
cache even mid-window: switching model/variant, changing the thinking level, toggling agent/plan
|
|
197
|
+
mode, editing an earlier message, or changing the forwarded MCP server set (tool definitions sit at
|
|
198
|
+
the top of the provider's cache-prefix hierarchy, so they invalidate everything after them). Tool outputs from earlier
|
|
199
|
+
turns are included (truncated) in the replay paths so a fresh/diverged agent still sees what prior
|
|
200
|
+
tools produced. Set `OPENCODE_CURSOR_DEBUG=1` to log the per-turn classification and the
|
|
201
|
+
`cacheReadTokens`/`cacheWriteTokens` reported by Cursor.
|
|
169
202
|
|
|
170
203
|
### Per-request controls (`mode`, thinking level)
|
|
171
204
|
|
|
@@ -205,19 +238,36 @@ To disable MCP forwarding, set `provider.cursor.options.forwardMcp: false` in yo
|
|
|
205
238
|
|
|
206
239
|
## MCP servers
|
|
207
240
|
|
|
208
|
-
The Cursor agent can use the **same MCP servers you've configured in opencode**.
|
|
209
|
-
|
|
210
|
-
`
|
|
241
|
+
The Cursor agent can use the **same MCP servers you've configured in opencode**. Forwarding is
|
|
242
|
+
**live, per turn**: the plugin's `chat.params` hook reads opencode's current MCP state
|
|
243
|
+
(`client.mcp.status()` for what's actually enabled right now, `client.config.get()` for the launch
|
|
244
|
+
specs), translates each entry into the Cursor SDK's `McpServerConfig` shape, and hands the set to
|
|
245
|
+
the agent — so enabling or disabling an MCP server mid-session takes effect on the next turn, not
|
|
246
|
+
the next restart. A startup snapshot from the `config` hook remains as the fallback when the live
|
|
247
|
+
read is unavailable.
|
|
211
248
|
|
|
212
249
|
| opencode `config.mcp` | → Cursor |
|
|
213
250
|
| --- | --- |
|
|
214
251
|
| `{ type: "local", command: [cmd, ...args], environment }` | `{ type: "stdio", command: cmd, args, env }` |
|
|
215
252
|
| `{ type: "remote", url, headers }` | `{ type: "http", url, headers }` |
|
|
253
|
+
| remote with registered OAuth client (`clientId`, optional secret/scopes) | `{ type: "http", url, auth: { CLIENT_ID, … } }` — the agent runs its own OAuth flow |
|
|
216
254
|
|
|
217
|
-
So
|
|
218
|
-
servers are independent processes, so opencode and the agent each connect to them
|
|
255
|
+
So whatever MCP servers your `opencode.json` defines, your Cursor agent connects to those same
|
|
256
|
+
servers — MCP servers are independent processes, so opencode and the agent each connect to them
|
|
257
|
+
directly.
|
|
219
258
|
Disabled entries (`enabled: false`) are skipped. Turn this off with `forwardMcp: false`.
|
|
220
259
|
|
|
260
|
+
> **OAuth caveat.** opencode's own access tokens never land in `config.mcp`, so a remote server
|
|
261
|
+
> that needs OAuth **without** a shareable `clientId` (dynamic client registration / `needs_auth`)
|
|
262
|
+
> can't be forwarded — forwarding its spec would just 401. Such servers are skipped and a one-time
|
|
263
|
+
> toast tells you which ones; they keep working inside opencode itself.
|
|
264
|
+
>
|
|
265
|
+
> **Session-reuse interaction.** A resumed Cursor agent keeps the MCP servers it was created with,
|
|
266
|
+
> so when the forwarded set changes between turns the provider creates a fresh agent (full
|
|
267
|
+
> transcript replay, re-pooled) instead of resuming — see
|
|
268
|
+
> [Session reuse](#session-reuse-session). Tool definitions sit at the top of the provider's
|
|
269
|
+
> cache-prefix hierarchy, so an MCP change also re-seeds the prompt cache.
|
|
270
|
+
|
|
221
271
|
> Scope note: this forwards **MCP servers**. opencode's *loop-internal* features — its own skills
|
|
222
272
|
> and subagents — are not exposed to the Cursor agent (they run inside opencode's agent loop, which
|
|
223
273
|
> this provider bypasses). The Cursor agent's *own* skills/rules can be loaded with the
|
|
@@ -296,9 +346,10 @@ This plugin runs Cursor as a **local agent** (`Agent.create({ local: { cwd } })`
|
|
|
296
346
|
directory. How that activity is shown is controlled by the [`toolDisplay`](#tool-display) option.
|
|
297
347
|
Either way it is **not** routed through opencode's tool/permission system — Cursor runs the tools
|
|
298
348
|
itself.
|
|
299
|
-
- By default
|
|
300
|
-
|
|
301
|
-
|
|
349
|
+
- By default (`session: "auto"`) the provider resumes one Cursor agent per session and sends only
|
|
350
|
+
the new message on a clean continuation, falling back to a fresh agent + full transcript on
|
|
351
|
+
edits/reverts/compaction/side calls (see [Session reuse](#session-reuse-session)). Set
|
|
352
|
+
`session: false` to always create a fresh agent and re-send the full transcript every turn.
|
|
302
353
|
- Token usage is reported from Cursor's `turn-ended` event; cost is shown as `0` because Cursor
|
|
303
354
|
bills your account separately.
|
|
304
355
|
- **Provider path is local.** The `cursor/*` models you chat with run as a **local** agent. Cursor's
|
|
@@ -562,8 +562,65 @@ function loadAgentBackend() {
|
|
|
562
562
|
return cached3;
|
|
563
563
|
}
|
|
564
564
|
|
|
565
|
+
// src/provider/session-store.ts
|
|
566
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
567
|
+
import { homedir, tmpdir } from "os";
|
|
568
|
+
import { join as join2 } from "path";
|
|
569
|
+
var ENTRY_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
570
|
+
var MAX_ENTRIES = 200;
|
|
571
|
+
function storeDir() {
|
|
572
|
+
const base = process.env.XDG_CACHE_HOME?.trim() || (homedir() ? join2(homedir(), ".cache") : tmpdir());
|
|
573
|
+
return join2(base, "opencode-cursor");
|
|
574
|
+
}
|
|
575
|
+
function storeFile() {
|
|
576
|
+
return join2(storeDir(), "session-pool.json");
|
|
577
|
+
}
|
|
578
|
+
function isStoredRecord(value) {
|
|
579
|
+
if (typeof value !== "object" || value === null) return false;
|
|
580
|
+
const v = value;
|
|
581
|
+
return typeof v["agentId"] === "string" && typeof v["systemHash"] === "string" && Array.isArray(v["userHashes"]) && v["userHashes"].every((h) => typeof h === "string") && typeof v["updatedAt"] === "number";
|
|
582
|
+
}
|
|
583
|
+
function loadSessionRecords(now = Date.now()) {
|
|
584
|
+
const out = /* @__PURE__ */ new Map();
|
|
585
|
+
try {
|
|
586
|
+
const parsed = JSON.parse(
|
|
587
|
+
readFileSync(storeFile(), "utf8")
|
|
588
|
+
);
|
|
589
|
+
if (typeof parsed?.sessions !== "object" || parsed.sessions === null)
|
|
590
|
+
return out;
|
|
591
|
+
for (const [key, value] of Object.entries(parsed.sessions)) {
|
|
592
|
+
if (!isStoredRecord(value)) continue;
|
|
593
|
+
if (now - value.updatedAt > ENTRY_TTL_MS) continue;
|
|
594
|
+
out.set(key, value);
|
|
595
|
+
}
|
|
596
|
+
} catch {
|
|
597
|
+
}
|
|
598
|
+
return out;
|
|
599
|
+
}
|
|
600
|
+
function saveSessionRecords(records, now = Date.now()) {
|
|
601
|
+
try {
|
|
602
|
+
const live = [...records.entries()].filter(([, r]) => now - r.updatedAt <= ENTRY_TTL_MS).sort(([, a], [, b]) => b.updatedAt - a.updatedAt).slice(0, MAX_ENTRIES);
|
|
603
|
+
mkdirSync(storeDir(), { recursive: true });
|
|
604
|
+
const envelope = { sessions: Object.fromEntries(live) };
|
|
605
|
+
writeFileSync(storeFile(), JSON.stringify(envelope), "utf8");
|
|
606
|
+
} catch {
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
565
610
|
// src/provider/session-pool.ts
|
|
566
611
|
var pool = /* @__PURE__ */ new Map();
|
|
612
|
+
var hydrated = false;
|
|
613
|
+
function hydrate() {
|
|
614
|
+
if (hydrated) return;
|
|
615
|
+
hydrated = true;
|
|
616
|
+
for (const [key, record] of loadSessionRecords()) {
|
|
617
|
+
if (!pool.has(key)) pool.set(key, record);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
function getSessionRecord(sessionID) {
|
|
621
|
+
hydrate();
|
|
622
|
+
return pool.get(sessionID);
|
|
623
|
+
}
|
|
567
624
|
async function acquireAgent(params) {
|
|
568
625
|
const backend = loadAgentBackend();
|
|
569
626
|
const createOptions = {
|
|
@@ -579,23 +636,30 @@ async function acquireAgent(params) {
|
|
|
579
636
|
...params.agents ? { agents: params.agents } : {},
|
|
580
637
|
...params.name ? { name: params.name } : {}
|
|
581
638
|
};
|
|
582
|
-
const pooling = params.session && Boolean(params.sessionID);
|
|
583
|
-
const pooledId = pooling ? pool.get(params.sessionID) : void 0;
|
|
584
|
-
const resumeId = params.agentId ?? pooledId;
|
|
585
639
|
let agent;
|
|
586
640
|
let resumed = false;
|
|
587
|
-
if (
|
|
641
|
+
if (params.resumeAgentId) {
|
|
588
642
|
try {
|
|
589
|
-
agent = await backend.resumeAgent(
|
|
643
|
+
agent = await backend.resumeAgent(params.resumeAgentId, createOptions);
|
|
590
644
|
resumed = true;
|
|
591
645
|
} catch {
|
|
592
|
-
if (pooledId && resumeId === pooledId) pool.delete(params.sessionID);
|
|
593
646
|
}
|
|
594
647
|
}
|
|
595
648
|
if (!agent) {
|
|
596
649
|
agent = await backend.createAgent(createOptions);
|
|
597
650
|
}
|
|
598
|
-
|
|
651
|
+
const pooling = params.poolKey !== void 0;
|
|
652
|
+
if (pooling && params.record) {
|
|
653
|
+
hydrate();
|
|
654
|
+
pool.set(params.poolKey, {
|
|
655
|
+
agentId: agent.agentId,
|
|
656
|
+
systemHash: params.record.systemHash,
|
|
657
|
+
userHashes: params.record.userHashes,
|
|
658
|
+
...params.record.mcpHash !== void 0 ? { mcpHash: params.record.mcpHash } : {},
|
|
659
|
+
updatedAt: Date.now()
|
|
660
|
+
});
|
|
661
|
+
saveSessionRecords(pool);
|
|
662
|
+
}
|
|
599
663
|
const release = () => {
|
|
600
664
|
if (!pooling) {
|
|
601
665
|
try {
|
|
@@ -614,6 +678,7 @@ export {
|
|
|
614
678
|
buildModelSelection,
|
|
615
679
|
resolveControls,
|
|
616
680
|
loadCursorSdk,
|
|
681
|
+
getSessionRecord,
|
|
617
682
|
acquireAgent
|
|
618
683
|
};
|
|
619
|
-
//# sourceMappingURL=chunk-
|
|
684
|
+
//# sourceMappingURL=chunk-BTI2NHEE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api-key.ts","../src/provider/agent-events.ts","../src/provider/controls.ts","../src/native-binding.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 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 // A previous opencode/CLI crash (or a second instance racing on the same\n // agent store) can leave a persisted run wedged; the SDK then rejects new\n // sends with AgentBusyError. Retry once with the SDK's documented recovery\n // path (local.force expires the wedged run) instead of failing the turn.\n const sendTurn = async (): Promise<AgentRunLike> => {\n try {\n return await agent.send(message, { mode: options.mode, onDelta });\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, { mode: options.mode, onDelta, local: { force: true } });\n }\n throw err;\n }\n };\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","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 * Self-heal for sqlite3's native binding.\n *\n * `@cursor/sdk` depends on `sqlite3` (a native addon). opencode installs\n * plugin packages with Bun, which does not run sqlite3's `install` lifecycle\n * script (`prebuild-install -r napi || node-gyp rebuild`), so the installed\n * tree has **no** `node_sqlite3.node` binary and the SDK crashes at import\n * with \"Could not locate the bindings file\".\n *\n * Before loading the SDK (in-process or via the Node sidecar) we check for a\n * binding and, when it is missing, run sqlite3's own `prebuild-install -r napi`\n * to fetch the prebuilt NAPI binary (ABI-portable across Node versions, also\n * loadable by Bun). Failures degrade to a clear warning; the SDK import then\n * surfaces its own error.\n */\nimport { execSync, spawn } from \"node:child_process\";\nimport { existsSync, readdirSync, statSync } from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { dirname, join } from \"node:path\";\n\nexport type EnsureResult = \"present\" | \"repaired\" | \"failed\" | \"not-found\";\n\nexport interface EnsureOptions {\n /** Override the sqlite3 package directory (tests). */\n sqliteDir?: string;\n /** Override the repair runner (tests). Returns true when the command succeeded. */\n run?: (sqliteDir: string) => Promise<boolean>;\n /** Override the warning sink (tests). */\n log?: (message: string) => void;\n}\n\n/** Directories (relative to the sqlite3 package root) that may hold the binding. */\nconst BINDING_ROOTS = [\"build\", \"lib/binding\", \"compiled\"];\n\nfunction hasNodeFile(dir: string, depth: number): boolean {\n if (depth < 0) return false;\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return false;\n }\n for (const entry of entries) {\n const path = join(dir, entry);\n if (entry.endsWith(\".node\")) {\n try {\n if (statSync(path).isFile()) return true;\n } catch {\n // ignore unreadable entries\n }\n continue;\n }\n try {\n if (statSync(path).isDirectory() && hasNodeFile(path, depth - 1)) return true;\n } catch {\n // ignore unreadable entries\n }\n }\n return false;\n}\n\n/** True when the sqlite3 package dir contains a compiled `.node` binding. */\nexport function hasSqliteBinding(sqliteDir: string): boolean {\n return BINDING_ROOTS.some((root) => hasNodeFile(join(sqliteDir, root), 3));\n}\n\n/**\n * Locate the sqlite3 package directory that `@cursor/sdk` will load, walking\n * the same resolution chain (our module -> @cursor/sdk -> sqlite3).\n */\nexport function resolveSqliteDir(): string | undefined {\n const req = createRequire(import.meta.url);\n try {\n const sdkPkg = req.resolve(\"@cursor/sdk/package.json\");\n return dirname(createRequire(sdkPkg).resolve(\"sqlite3/package.json\"));\n } catch {\n // fall through: try resolving sqlite3 directly (hoisted installs)\n }\n try {\n return dirname(req.resolve(\"sqlite3/package.json\"));\n } catch {\n return undefined;\n }\n}\n\nfunction detectNodeExecutable(): string {\n const isBun = typeof (globalThis as { Bun?: unknown }).Bun !== \"undefined\";\n if (!isBun) return process.execPath;\n // Under Bun prefer a real Node (matches the sidecar runtime); prebuild-install\n // itself is plain JS, so Bun works as a last resort.\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] || process.execPath;\n } catch {\n return process.execPath;\n }\n}\n\n/** Default repair: run sqlite3's own `prebuild-install -r napi` in its package dir. */\nasync function runPrebuildInstall(sqliteDir: string): Promise<boolean> {\n let bin: string;\n try {\n const req = createRequire(join(sqliteDir, \"package.json\"));\n const pkgPath = req.resolve(\"prebuild-install/package.json\");\n const pkg = (await import(pkgPath, { with: { type: \"json\" } })) as {\n default: { bin?: string | Record<string, string> };\n };\n const binField = pkg.default.bin;\n const rel = typeof binField === \"string\" ? binField : binField?.[\"prebuild-install\"];\n if (!rel) return false;\n bin = join(dirname(pkgPath), rel);\n } catch {\n return false;\n }\n if (!existsSync(bin)) return false;\n\n return new Promise<boolean>((resolve) => {\n const child = spawn(detectNodeExecutable(), [bin, \"-r\", \"napi\"], {\n cwd: sqliteDir,\n stdio: [\"ignore\", \"ignore\", \"pipe\"],\n });\n let stderr = \"\";\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n child.on(\"error\", () => resolve(false));\n child.on(\"exit\", (code) => {\n if (code !== 0 && stderr && process.env[\"OPENCODE_CURSOR_DEBUG\"]) {\n console.error(`[opencode-cursor] prebuild-install stderr: ${stderr.trim()}`);\n }\n resolve(code === 0);\n });\n });\n}\n\nlet cached: Promise<EnsureResult> | undefined;\n\n/**\n * Ensure the sqlite3 native binding exists, repairing it once per process if\n * needed. Never throws; \"failed\"/\"not-found\" outcomes warn and let the SDK\n * import surface its own error.\n */\nexport function ensureSqliteBinding(options: EnsureOptions = {}): Promise<EnsureResult> {\n cached ??= (async () => {\n const log = options.log ?? ((message: string) => console.error(message));\n const sqliteDir = options.sqliteDir ?? resolveSqliteDir();\n if (!sqliteDir || !existsSync(join(sqliteDir, \"package.json\"))) {\n return \"not-found\";\n }\n if (hasSqliteBinding(sqliteDir)) return \"present\";\n\n const run = options.run ?? runPrebuildInstall;\n const ok = await run(sqliteDir).catch(() => false);\n if (ok && hasSqliteBinding(sqliteDir)) return \"repaired\";\n\n log(\n `[opencode-cursor] sqlite3 native binding is missing in ${sqliteDir} and automatic ` +\n `repair failed. @cursor/sdk will not load. Fix manually with: ` +\n `cd ${sqliteDir} && npx prebuild-install -r napi (or: npm rebuild sqlite3)`,\n );\n return \"failed\";\n })();\n return cached;\n}\n\n/** Test hook. */\nexport function resetNativeBinding(): 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 */\nimport { ensureSqliteBinding } from \"./native-binding.js\";\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 // @cursor/sdk eagerly requires sqlite3 (native addon); opencode's Bun\n // install skips its build script, so repair the binding first if missing.\n cached = ensureSqliteBinding()\n .then(() => import(\"@cursor/sdk\"))\n .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 { ensureSqliteBinding } from \"../native-binding.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 // The sidecar imports @cursor/sdk (which eagerly requires sqlite3's native\n // binding) in the child process; repair the binding before first use.\n return {\n kind: \"sidecar\",\n createAgent: async (options) => {\n await ensureSqliteBinding();\n return client.createAgent(options);\n },\n resumeAgent: async (agentId, options) => {\n await ensureSqliteBinding();\n return client.resumeAgent(agentId, options);\n },\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/** 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":["cached","execSync","existsSync","spawn","execSync","existsSync","cached","join"]}
|