@stablekernel/opencode-cursor 0.4.7-next.0 → 0.5.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 -0
- package/README.md +55 -16
- package/dist/{chunk-SVIXYMHP.js → chunk-LAOFD3JB.js} +487 -292
- package/dist/chunk-LAOFD3JB.js.map +1 -0
- package/dist/plugin/index.js +28 -4
- package/dist/plugin/index.js.map +1 -1
- package/dist/provider/index.d.ts +12 -0
- package/dist/provider/index.js +205 -23
- package/dist/provider/index.js.map +1 -1
- package/dist/sidecar/agent-host.d.ts +9 -2
- package/dist/sidecar/agent-host.js +7 -1
- package/dist/sidecar/agent-host.js.map +1 -1
- package/package.json +6 -5
- package/dist/chunk-SVIXYMHP.js.map +0 -1
|
@@ -16,270 +16,10 @@ function fingerprintApiKey(apiKey) {
|
|
|
16
16
|
return createHash("sha256").update(apiKey).digest("hex").slice(0, 16);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
// src/provider/
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
114
|
-
// src/provider/agent-events.ts
|
|
115
|
-
function toolDisplayName(toolCall) {
|
|
116
|
-
if (!toolCall) return "tool";
|
|
117
|
-
if (toolCall.type === "mcp") {
|
|
118
|
-
const name = toolCall.args?.toolName;
|
|
119
|
-
const server = toolCall.args?.providerIdentifier;
|
|
120
|
-
if (name) return server ? `${server}/${name}` : String(name);
|
|
121
|
-
return "mcp";
|
|
122
|
-
}
|
|
123
|
-
return toolCall.type ?? "tool";
|
|
124
|
-
}
|
|
125
|
-
async function* streamAgentTurn(agent, message, options) {
|
|
126
|
-
const queue = [];
|
|
127
|
-
let wake;
|
|
128
|
-
let finished = false;
|
|
129
|
-
let failure;
|
|
130
|
-
const debug = process.env.OPENCODE_CURSOR_DEBUG === "1";
|
|
131
|
-
const counts = {};
|
|
132
|
-
const push = (event) => {
|
|
133
|
-
queue.push(event);
|
|
134
|
-
wake?.();
|
|
135
|
-
wake = void 0;
|
|
136
|
-
};
|
|
137
|
-
const onDelta = ({ update }) => {
|
|
138
|
-
if (debug) counts[update.type] = (counts[update.type] ?? 0) + 1;
|
|
139
|
-
switch (update.type) {
|
|
140
|
-
case "text-delta":
|
|
141
|
-
push({ type: "text-delta", text: update.text });
|
|
142
|
-
break;
|
|
143
|
-
case "thinking-delta":
|
|
144
|
-
push({ type: "reasoning-delta", text: update.text });
|
|
145
|
-
break;
|
|
146
|
-
case "tool-call-started":
|
|
147
|
-
push({
|
|
148
|
-
type: "tool-call",
|
|
149
|
-
id: String(update.callId),
|
|
150
|
-
name: toolDisplayName(update.toolCall),
|
|
151
|
-
input: update.toolCall?.args ?? {}
|
|
152
|
-
});
|
|
153
|
-
break;
|
|
154
|
-
case "tool-call-completed": {
|
|
155
|
-
const tool = update.toolCall ?? {};
|
|
156
|
-
const result = tool.result;
|
|
157
|
-
const mcpError = tool.type === "mcp" && result?.value?.isError === true;
|
|
158
|
-
push({
|
|
159
|
-
type: "tool-result",
|
|
160
|
-
id: String(update.callId),
|
|
161
|
-
name: toolDisplayName(tool),
|
|
162
|
-
result: result ?? null,
|
|
163
|
-
isError: result?.status === "error" || mcpError
|
|
164
|
-
});
|
|
165
|
-
break;
|
|
166
|
-
}
|
|
167
|
-
case "turn-ended":
|
|
168
|
-
if (update.usage) push({ type: "usage", usage: update.usage });
|
|
169
|
-
break;
|
|
170
|
-
}
|
|
171
|
-
};
|
|
172
|
-
const runHolder = {};
|
|
173
|
-
const onAbort = () => {
|
|
174
|
-
void Promise.resolve(runHolder.run?.cancel()).catch(() => {
|
|
175
|
-
});
|
|
176
|
-
};
|
|
177
|
-
options.abortSignal?.addEventListener("abort", onAbort);
|
|
178
|
-
const sendTurn = () => sendWithBusyRetry(agent, message, { mode: options.mode, onDelta }, debug);
|
|
179
|
-
void sendTurn().then(async (run) => {
|
|
180
|
-
runHolder.run = run;
|
|
181
|
-
const result = await run.wait();
|
|
182
|
-
if (debug) {
|
|
183
|
-
console.error(
|
|
184
|
-
`[cursor:debug] updates=${JSON.stringify(counts)} status=${result.status} resultLen=${(result.result ?? "").length}`
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
if (result.status === "error") {
|
|
188
|
-
throw new Error(
|
|
189
|
-
`Cursor run ended with status "error"${result.result ? `: ${result.result}` : ""}`
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
push({ type: "finish", ...result.status === "cancelled" ? {} : { text: result.result } });
|
|
193
|
-
}).catch((err) => {
|
|
194
|
-
failure = err;
|
|
195
|
-
if (debug) console.error(`[cursor:debug] send failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
196
|
-
}).finally(() => {
|
|
197
|
-
finished = true;
|
|
198
|
-
wake?.();
|
|
199
|
-
wake = void 0;
|
|
200
|
-
});
|
|
201
|
-
try {
|
|
202
|
-
while (true) {
|
|
203
|
-
if (queue.length > 0) {
|
|
204
|
-
yield queue.shift();
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
if (finished) break;
|
|
208
|
-
await new Promise((resolve) => {
|
|
209
|
-
wake = resolve;
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
while (queue.length > 0) yield queue.shift();
|
|
213
|
-
if (failure) throw failure;
|
|
214
|
-
} finally {
|
|
215
|
-
options.abortSignal?.removeEventListener("abort", onAbort);
|
|
216
|
-
}
|
|
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
|
-
}
|
|
254
|
-
|
|
255
|
-
// src/provider/controls.ts
|
|
256
|
-
function buildModelSelection(modelId, params) {
|
|
257
|
-
const paramList = Object.entries(params ?? {}).map(([id, value]) => ({ id, value }));
|
|
258
|
-
return paramList.length > 0 ? { id: modelId, params: paramList } : { id: modelId };
|
|
259
|
-
}
|
|
260
|
-
function isRecord(value) {
|
|
261
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
262
|
-
}
|
|
263
|
-
function isMode(value) {
|
|
264
|
-
return value === "agent" || value === "plan";
|
|
265
|
-
}
|
|
266
|
-
function resolveControls(modelId, staticControls, providerOptions) {
|
|
267
|
-
const po = providerOptions ?? {};
|
|
268
|
-
const mode = isMode(po["mode"]) ? po["mode"] : staticControls.mode;
|
|
269
|
-
const params = {
|
|
270
|
-
...staticControls.defaults ?? {},
|
|
271
|
-
...staticControls.params ?? {}
|
|
272
|
-
};
|
|
273
|
-
if (isRecord(po["params"])) {
|
|
274
|
-
for (const [key, value] of Object.entries(po["params"])) {
|
|
275
|
-
if (value != null) params[key] = String(value);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
if (typeof po["thinking"] === "string" && params["thinking"] === void 0) {
|
|
279
|
-
params["thinking"] = po["thinking"];
|
|
280
|
-
}
|
|
281
|
-
return { mode, modelSelection: buildModelSelection(modelId, params) };
|
|
282
|
-
}
|
|
19
|
+
// src/provider/agent-backend.ts
|
|
20
|
+
import { execSync } from "child_process";
|
|
21
|
+
import { existsSync } from "fs";
|
|
22
|
+
import { fileURLToPath } from "url";
|
|
283
23
|
|
|
284
24
|
// src/cursor-runtime.ts
|
|
285
25
|
var cached;
|
|
@@ -296,11 +36,6 @@ async function loadCursorSdk() {
|
|
|
296
36
|
return cached;
|
|
297
37
|
}
|
|
298
38
|
|
|
299
|
-
// src/provider/agent-backend.ts
|
|
300
|
-
import { execSync } from "child_process";
|
|
301
|
-
import { existsSync as existsSync2 } from "fs";
|
|
302
|
-
import { fileURLToPath } from "url";
|
|
303
|
-
|
|
304
39
|
// src/provider/sidecar-client.ts
|
|
305
40
|
import { spawn } from "child_process";
|
|
306
41
|
import { createInterface } from "readline";
|
|
@@ -308,6 +43,10 @@ function reviveError(error) {
|
|
|
308
43
|
const e = error ?? {};
|
|
309
44
|
const err = new Error(e.message ?? "sidecar error");
|
|
310
45
|
if (e.name) err.name = e.name;
|
|
46
|
+
if (e.status !== void 0) err.status = e.status;
|
|
47
|
+
if (e.code !== void 0) err.code = e.code;
|
|
48
|
+
if (e.isRetryable !== void 0) err.isRetryable = e.isRetryable;
|
|
49
|
+
if (e.helpUrl !== void 0) err.helpUrl = e.helpUrl;
|
|
311
50
|
return err;
|
|
312
51
|
}
|
|
313
52
|
var SidecarClient = class {
|
|
@@ -462,7 +201,8 @@ var SidecarClient = class {
|
|
|
462
201
|
agentId,
|
|
463
202
|
message,
|
|
464
203
|
...options?.mode ? { mode: options.mode } : {},
|
|
465
|
-
...options?.local?.force ? { force: true } : {}
|
|
204
|
+
...options?.local?.force ? { force: true } : {},
|
|
205
|
+
...options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}
|
|
466
206
|
},
|
|
467
207
|
{
|
|
468
208
|
onUpdate: (update) => options?.onDelta?.({ update }),
|
|
@@ -493,11 +233,28 @@ var SidecarClient = class {
|
|
|
493
233
|
};
|
|
494
234
|
|
|
495
235
|
// src/provider/agent-backend.ts
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
236
|
+
var DEFAULT_BUN_TRANSPORT = "http1";
|
|
237
|
+
var preferredTransport;
|
|
238
|
+
function setPreferredTransport(t) {
|
|
239
|
+
preferredTransport = t;
|
|
240
|
+
}
|
|
241
|
+
function isTransportKind(v) {
|
|
242
|
+
return v === "http1" || v === "http2-direct" || v === "sidecar";
|
|
243
|
+
}
|
|
244
|
+
function resolveTransport(env) {
|
|
245
|
+
const requested = preferredTransport ?? (isTransportKind(process.env["OPENCODE_CURSOR_TRANSPORT"]) ? process.env["OPENCODE_CURSOR_TRANSPORT"] : void 0);
|
|
246
|
+
if (requested) {
|
|
247
|
+
if (requested === "sidecar" && !env.nodePath) {
|
|
248
|
+
return env.isBun ? "http1" : "http2-direct";
|
|
249
|
+
}
|
|
250
|
+
return requested;
|
|
251
|
+
}
|
|
252
|
+
const legacy = process.env["OPENCODE_CURSOR_SIDECAR"];
|
|
253
|
+
if (legacy === "1" || legacy === "true") {
|
|
254
|
+
return env.nodePath ? "sidecar" : env.isBun ? "http1" : "http2-direct";
|
|
255
|
+
}
|
|
256
|
+
if (legacy === "0" || legacy === "false") return env.isBun ? "http1" : "http2-direct";
|
|
257
|
+
return env.isBun ? DEFAULT_BUN_TRANSPORT : "http2-direct";
|
|
501
258
|
}
|
|
502
259
|
function detectNode() {
|
|
503
260
|
try {
|
|
@@ -515,15 +272,24 @@ function detectEnvironment() {
|
|
|
515
272
|
const needsNode = isBun || process.env["OPENCODE_CURSOR_SIDECAR"] === "1";
|
|
516
273
|
return { isBun, nodePath: needsNode ? detectNode() : process.execPath };
|
|
517
274
|
}
|
|
518
|
-
|
|
275
|
+
var http1Configured = false;
|
|
276
|
+
async function ensureHttp1Configured() {
|
|
277
|
+
if (http1Configured) return;
|
|
278
|
+
const { Cursor } = await loadCursorSdk();
|
|
279
|
+
Cursor.configure({ local: { useHttp1ForAgent: true } });
|
|
280
|
+
http1Configured = true;
|
|
281
|
+
}
|
|
282
|
+
function inProcessBackend(useHttp1) {
|
|
519
283
|
return {
|
|
520
284
|
kind: "in-process",
|
|
521
285
|
createAgent: async (options) => {
|
|
522
286
|
const { Agent } = await loadCursorSdk();
|
|
287
|
+
if (useHttp1) await ensureHttp1Configured();
|
|
523
288
|
return await Agent.create(options);
|
|
524
289
|
},
|
|
525
290
|
resumeAgent: async (agentId, options) => {
|
|
526
291
|
const { Agent } = await loadCursorSdk();
|
|
292
|
+
if (useHttp1) await ensureHttp1Configured();
|
|
527
293
|
return await Agent.resume(agentId, options);
|
|
528
294
|
}
|
|
529
295
|
};
|
|
@@ -539,7 +305,7 @@ function resolveSidecarScript() {
|
|
|
539
305
|
];
|
|
540
306
|
for (const candidate of candidates) {
|
|
541
307
|
const path = fileURLToPath(new URL(candidate, import.meta.url));
|
|
542
|
-
if (
|
|
308
|
+
if (existsSync(path)) return path;
|
|
543
309
|
}
|
|
544
310
|
return void 0;
|
|
545
311
|
}
|
|
@@ -555,25 +321,450 @@ var cached2;
|
|
|
555
321
|
function loadAgentBackend() {
|
|
556
322
|
if (!cached2) {
|
|
557
323
|
const env = detectEnvironment();
|
|
558
|
-
const
|
|
559
|
-
const scriptPath =
|
|
560
|
-
|
|
561
|
-
const optedOut = override === "0" || override === "false";
|
|
562
|
-
if (env.isBun && !optedOut && (kind === "in-process" || !scriptPath)) {
|
|
324
|
+
const transport = resolveTransport(env);
|
|
325
|
+
const scriptPath = transport === "sidecar" ? resolveSidecarScript() : void 0;
|
|
326
|
+
if (transport === "sidecar" && (!env.nodePath || !scriptPath)) {
|
|
563
327
|
console.error(
|
|
564
|
-
`[opencode-cursor]
|
|
328
|
+
`[opencode-cursor] Node sidecar requested but unavailable (node: ${env.nodePath ?? "not found"}, script: ${scriptPath ?? "not found"}); falling back to in-process HTTP/1.1 transport.`
|
|
565
329
|
);
|
|
330
|
+
cached2 = inProcessBackend(true);
|
|
331
|
+
return cached2;
|
|
566
332
|
}
|
|
567
|
-
|
|
333
|
+
if (transport === "http2-direct" && env.isBun) {
|
|
334
|
+
console.error(
|
|
335
|
+
"[opencode-cursor] http2-direct under Bun: Cursor streams may fail (Bun node:http2 incompatibility, oven-sh/bun#31499). Set OPENCODE_CURSOR_TRANSPORT=http1 (recommended) or sidecar."
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
cached2 = transport === "sidecar" && env.nodePath && scriptPath ? sidecarBackend(env.nodePath, scriptPath) : inProcessBackend(transport === "http1");
|
|
568
339
|
}
|
|
569
340
|
return cached2;
|
|
570
341
|
}
|
|
571
342
|
|
|
572
|
-
// src/provider/
|
|
573
|
-
import {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
343
|
+
// src/provider/system-rule.ts
|
|
344
|
+
import {
|
|
345
|
+
mkdirSync,
|
|
346
|
+
writeFileSync,
|
|
347
|
+
readFileSync,
|
|
348
|
+
existsSync as existsSync2,
|
|
349
|
+
rmSync
|
|
350
|
+
} from "fs";
|
|
351
|
+
import { join } from "path";
|
|
352
|
+
var RULES_DIR = join(".cursor", "rules");
|
|
353
|
+
var RULE_FILE = "opencode.mdc";
|
|
354
|
+
var IGNORE_FILE = ".gitignore";
|
|
355
|
+
var SENTINEL = "generated: opencode-cursor";
|
|
356
|
+
function extractSystemText(prompt) {
|
|
357
|
+
const parts = [];
|
|
358
|
+
for (const message of prompt) {
|
|
359
|
+
if (message.role === "system") parts.push(message.content);
|
|
360
|
+
}
|
|
361
|
+
return parts.join("\n\n").trim();
|
|
362
|
+
}
|
|
363
|
+
function isGenerated(content) {
|
|
364
|
+
if (!content.startsWith("---")) return false;
|
|
365
|
+
const end = content.indexOf("\n---", 3);
|
|
366
|
+
const frontmatter = end === -1 ? content : content.slice(0, end);
|
|
367
|
+
return frontmatter.split(/\r?\n/).includes(SENTINEL);
|
|
368
|
+
}
|
|
369
|
+
function writeSystemRule(cwd, systemText) {
|
|
370
|
+
if (!systemText) return "empty";
|
|
371
|
+
const dir = join(cwd, RULES_DIR);
|
|
372
|
+
const path = join(dir, RULE_FILE);
|
|
373
|
+
const body = `---
|
|
374
|
+
alwaysApply: true
|
|
375
|
+
${SENTINEL}
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
${systemText}
|
|
379
|
+
`;
|
|
380
|
+
const existing = existsSync2(path) ? readFileSync(path, "utf8") : void 0;
|
|
381
|
+
if (existing !== void 0) {
|
|
382
|
+
if (!isGenerated(existing)) return "blocked";
|
|
383
|
+
if (existing === body) return "unchanged";
|
|
384
|
+
}
|
|
385
|
+
mkdirSync(dir, { recursive: true });
|
|
386
|
+
writeFileSync(path, body, "utf8");
|
|
387
|
+
ensureGitIgnored(dir);
|
|
388
|
+
return "written";
|
|
389
|
+
}
|
|
390
|
+
function ensureGitIgnored(dir) {
|
|
391
|
+
const path = join(dir, IGNORE_FILE);
|
|
392
|
+
const existing = existsSync2(path) ? readFileSync(path, "utf8") : "";
|
|
393
|
+
const lines = existing.split(/\r?\n/);
|
|
394
|
+
const missing = [RULE_FILE, IGNORE_FILE].filter(
|
|
395
|
+
(entry) => !lines.includes(entry)
|
|
396
|
+
);
|
|
397
|
+
if (missing.length === 0) return;
|
|
398
|
+
const prefix = existing && !existing.endsWith("\n") ? `${existing}
|
|
399
|
+
` : existing;
|
|
400
|
+
writeFileSync(path, `${prefix}${missing.join("\n")}
|
|
401
|
+
`, "utf8");
|
|
402
|
+
}
|
|
403
|
+
function removeSystemRule(cwd) {
|
|
404
|
+
try {
|
|
405
|
+
const path = join(cwd, RULES_DIR, RULE_FILE);
|
|
406
|
+
if (isGenerated(readFileSync(path, "utf8"))) rmSync(path);
|
|
407
|
+
} catch {
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
function resolveSystemDelivery(options) {
|
|
411
|
+
const { mode, settingSources, cwd, systemText, warn } = options;
|
|
412
|
+
if (mode !== "rules") return { mode, settingSources };
|
|
413
|
+
if (settingSources && !settingSources.includes("project")) {
|
|
414
|
+
warn(
|
|
415
|
+
'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.'
|
|
416
|
+
);
|
|
417
|
+
return { mode: "message", settingSources };
|
|
418
|
+
}
|
|
419
|
+
let result;
|
|
420
|
+
try {
|
|
421
|
+
result = writeSystemRule(cwd, systemText);
|
|
422
|
+
} catch (error) {
|
|
423
|
+
warn(
|
|
424
|
+
`failed to write .cursor/rules/${RULE_FILE} (${error instanceof Error ? error.message : String(error)}); delivering the system prompt inline ("message" mode) for this turn.`
|
|
425
|
+
);
|
|
426
|
+
return { mode: "message", settingSources };
|
|
427
|
+
}
|
|
428
|
+
if (result === "blocked") {
|
|
429
|
+
warn(
|
|
430
|
+
`.cursor/rules/${RULE_FILE} exists but was not generated by opencode-cursor; leaving it untouched and delivering the system prompt inline ("message" mode).`
|
|
431
|
+
);
|
|
432
|
+
return { mode: "message", settingSources };
|
|
433
|
+
}
|
|
434
|
+
if (result === "empty") return { mode, settingSources };
|
|
435
|
+
return { mode, settingSources: settingSources ?? ["project"] };
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// src/provider/error-classify.ts
|
|
439
|
+
function classifyError(err) {
|
|
440
|
+
const e = err ?? {};
|
|
441
|
+
const name = typeof e.name === "string" ? e.name : "Error";
|
|
442
|
+
const message = typeof e.message === "string" ? e.message : String(err);
|
|
443
|
+
const status = typeof e.status === "number" ? e.status : void 0;
|
|
444
|
+
const code = typeof e.code === "string" ? e.code : void 0;
|
|
445
|
+
const helpUrl = typeof e.helpUrl === "string" ? e.helpUrl : void 0;
|
|
446
|
+
const base = { ...status !== void 0 ? { status } : {}, ...helpUrl ? { helpUrl } : {}, message };
|
|
447
|
+
switch (name) {
|
|
448
|
+
case "AgentNotFoundError":
|
|
449
|
+
return { kind: "agent-not-found", retryable: false, ...base };
|
|
450
|
+
case "AgentBusyError":
|
|
451
|
+
return { kind: "agent-busy", retryable: false, ...base };
|
|
452
|
+
case "RateLimitError":
|
|
453
|
+
return { kind: "rate-limit", retryable: true, ...base };
|
|
454
|
+
case "NetworkError":
|
|
455
|
+
return { kind: "network", retryable: true, ...base };
|
|
456
|
+
case "AuthenticationError":
|
|
457
|
+
return { kind: "auth", retryable: false, ...base };
|
|
458
|
+
case "ConfigurationError":
|
|
459
|
+
case "IntegrationNotConnectedError":
|
|
460
|
+
case "UnsupportedRunOperationError":
|
|
461
|
+
return { kind: "config", retryable: false, ...base };
|
|
462
|
+
}
|
|
463
|
+
if (status === 401) return { kind: "auth", retryable: false, ...base };
|
|
464
|
+
if (status === 429) return { kind: "rate-limit", retryable: true, ...base };
|
|
465
|
+
if (status === 409) return { kind: "agent-busy", retryable: false, ...base };
|
|
466
|
+
if (status === 503 || status === 504) return { kind: "network", retryable: true, ...base };
|
|
467
|
+
if (code === "agent_not_found") return { kind: "agent-not-found", retryable: false, ...base };
|
|
468
|
+
return { kind: "unknown", retryable: e.isRetryable === true, ...base };
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// src/provider/agent-events.ts
|
|
472
|
+
function addUsage(a, b) {
|
|
473
|
+
if (!a) return b;
|
|
474
|
+
if (!b) return a;
|
|
475
|
+
return {
|
|
476
|
+
inputTokens: a.inputTokens + b.inputTokens,
|
|
477
|
+
outputTokens: a.outputTokens + b.outputTokens,
|
|
478
|
+
cacheReadTokens: a.cacheReadTokens + b.cacheReadTokens,
|
|
479
|
+
cacheWriteTokens: a.cacheWriteTokens + b.cacheWriteTokens
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
function toolDisplayName(toolCall) {
|
|
483
|
+
if (!toolCall) return "tool";
|
|
484
|
+
if (toolCall.type === "mcp") {
|
|
485
|
+
const name = toolCall.args?.toolName;
|
|
486
|
+
const server = toolCall.args?.providerIdentifier;
|
|
487
|
+
if (name) return server ? `${server}/${name}` : String(name);
|
|
488
|
+
return "mcp";
|
|
489
|
+
}
|
|
490
|
+
return toolCall.type ?? "tool";
|
|
491
|
+
}
|
|
492
|
+
async function* streamAgentTurn(agent, message, options) {
|
|
493
|
+
const queue = [];
|
|
494
|
+
let wake;
|
|
495
|
+
let finished = false;
|
|
496
|
+
let failure;
|
|
497
|
+
const debug = process.env.OPENCODE_CURSOR_DEBUG === "1";
|
|
498
|
+
const counts = {};
|
|
499
|
+
const stallMs = Number(process.env.OPENCODE_CURSOR_STALL_MS ?? 6e4);
|
|
500
|
+
let stallTimer;
|
|
501
|
+
let forced = false;
|
|
502
|
+
let anyEvent = false;
|
|
503
|
+
const push = (event) => {
|
|
504
|
+
anyEvent = true;
|
|
505
|
+
queue.push(event);
|
|
506
|
+
wake?.();
|
|
507
|
+
wake = void 0;
|
|
508
|
+
armWatchdog();
|
|
509
|
+
};
|
|
510
|
+
const armWatchdog = () => {
|
|
511
|
+
if (stallMs <= 0 || finished) return;
|
|
512
|
+
if (stallTimer) clearTimeout(stallTimer);
|
|
513
|
+
stallTimer = setTimeout(() => {
|
|
514
|
+
void onStall();
|
|
515
|
+
}, stallMs);
|
|
516
|
+
stallTimer.unref?.();
|
|
517
|
+
};
|
|
518
|
+
const onDelta = ({ update }) => {
|
|
519
|
+
if (debug) counts[update.type] = (counts[update.type] ?? 0) + 1;
|
|
520
|
+
switch (update.type) {
|
|
521
|
+
case "text-delta":
|
|
522
|
+
push({ type: "text-delta", text: update.text });
|
|
523
|
+
break;
|
|
524
|
+
case "thinking-delta":
|
|
525
|
+
push({ type: "reasoning-delta", text: update.text });
|
|
526
|
+
break;
|
|
527
|
+
case "thinking-completed":
|
|
528
|
+
push({ type: "reasoning-complete", durationMs: update.thinkingDurationMs });
|
|
529
|
+
break;
|
|
530
|
+
case "summary-started":
|
|
531
|
+
case "summary":
|
|
532
|
+
case "summary-completed":
|
|
533
|
+
push({ type: "compaction" });
|
|
534
|
+
break;
|
|
535
|
+
case "partial-tool-call":
|
|
536
|
+
push({
|
|
537
|
+
type: "tool-input-partial",
|
|
538
|
+
id: String(update.callId),
|
|
539
|
+
name: toolDisplayName(update.toolCall),
|
|
540
|
+
input: update.toolCall?.args ?? {}
|
|
541
|
+
});
|
|
542
|
+
break;
|
|
543
|
+
case "tool-call-started":
|
|
544
|
+
push({
|
|
545
|
+
type: "tool-call",
|
|
546
|
+
id: String(update.callId),
|
|
547
|
+
name: toolDisplayName(update.toolCall),
|
|
548
|
+
input: update.toolCall?.args ?? {}
|
|
549
|
+
});
|
|
550
|
+
break;
|
|
551
|
+
case "tool-call-completed": {
|
|
552
|
+
const tool = update.toolCall ?? {};
|
|
553
|
+
const result = tool.result;
|
|
554
|
+
const mcpError = tool.type === "mcp" && result?.value?.isError === true;
|
|
555
|
+
push({
|
|
556
|
+
type: "tool-result",
|
|
557
|
+
id: String(update.callId),
|
|
558
|
+
name: toolDisplayName(tool),
|
|
559
|
+
result: result ?? null,
|
|
560
|
+
isError: result?.status === "error" || mcpError
|
|
561
|
+
});
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
case "turn-ended":
|
|
565
|
+
if (update.usage) {
|
|
566
|
+
const summed = addUsage(options.usageBase, update.usage);
|
|
567
|
+
if (summed) push({ type: "usage", usage: summed });
|
|
568
|
+
}
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
const runHolder = {};
|
|
573
|
+
const onAbort = () => {
|
|
574
|
+
if (stallTimer) clearTimeout(stallTimer);
|
|
575
|
+
stallTimer = void 0;
|
|
576
|
+
void Promise.resolve(runHolder.run?.cancel()).catch(() => {
|
|
577
|
+
});
|
|
578
|
+
};
|
|
579
|
+
options.abortSignal?.addEventListener("abort", onAbort);
|
|
580
|
+
let runGen = 0;
|
|
581
|
+
const startRun = (force) => {
|
|
582
|
+
const gen = ++runGen;
|
|
583
|
+
void sendWithRecovery(
|
|
584
|
+
agent,
|
|
585
|
+
message,
|
|
586
|
+
{
|
|
587
|
+
mode: options.mode,
|
|
588
|
+
onDelta,
|
|
589
|
+
...options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {},
|
|
590
|
+
...force ? { local: { force: true } } : {}
|
|
591
|
+
},
|
|
592
|
+
debug
|
|
593
|
+
).then(async (run) => {
|
|
594
|
+
runHolder.run = run;
|
|
595
|
+
if (options.abortSignal?.aborted) void Promise.resolve(run.cancel()).catch(() => {
|
|
596
|
+
});
|
|
597
|
+
const result = await run.wait();
|
|
598
|
+
if (debug) {
|
|
599
|
+
console.error(
|
|
600
|
+
`[cursor:debug] updates=${JSON.stringify(counts)} status=${result.status} resultLen=${(result.result ?? "").length}`
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
if (gen !== runGen || finished) return;
|
|
604
|
+
if (result.status === "error") {
|
|
605
|
+
throw new Error(
|
|
606
|
+
`Cursor run ended with status "error"${result.result ? `: ${result.result}` : ""}`
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
push({ type: "finish", ...result.status === "cancelled" ? {} : { text: result.result } });
|
|
610
|
+
}).catch((err) => {
|
|
611
|
+
if (gen !== runGen) return;
|
|
612
|
+
failure = err;
|
|
613
|
+
if (debug) console.error(`[cursor:debug] send failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
614
|
+
}).finally(() => {
|
|
615
|
+
if (gen !== runGen) return;
|
|
616
|
+
finished = true;
|
|
617
|
+
if (stallTimer) clearTimeout(stallTimer);
|
|
618
|
+
wake?.();
|
|
619
|
+
wake = void 0;
|
|
620
|
+
});
|
|
621
|
+
};
|
|
622
|
+
const onStall = async () => {
|
|
623
|
+
if (finished) return;
|
|
624
|
+
if (options.abortSignal?.aborted) return;
|
|
625
|
+
const failTerminal = async (message2) => {
|
|
626
|
+
try {
|
|
627
|
+
await runHolder.run?.cancel();
|
|
628
|
+
} catch {
|
|
629
|
+
}
|
|
630
|
+
failure = new Error(message2);
|
|
631
|
+
finished = true;
|
|
632
|
+
if (stallTimer) clearTimeout(stallTimer);
|
|
633
|
+
stallTimer = void 0;
|
|
634
|
+
wake?.();
|
|
635
|
+
wake = void 0;
|
|
636
|
+
};
|
|
637
|
+
if (anyEvent) {
|
|
638
|
+
await failTerminal(`Cursor run stalled (no events for ${stallMs}ms)`);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (forced) {
|
|
642
|
+
await failTerminal(`Cursor run stalled twice (no events for ${stallMs}ms)`);
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
forced = true;
|
|
646
|
+
if (debug) console.error("[cursor:debug] stream stalled; cancelling and resending with local.force");
|
|
647
|
+
try {
|
|
648
|
+
await runHolder.run?.cancel();
|
|
649
|
+
} catch {
|
|
650
|
+
}
|
|
651
|
+
armWatchdog();
|
|
652
|
+
startRun(true);
|
|
653
|
+
};
|
|
654
|
+
armWatchdog();
|
|
655
|
+
startRun(false);
|
|
656
|
+
try {
|
|
657
|
+
while (true) {
|
|
658
|
+
if (queue.length > 0) {
|
|
659
|
+
yield queue.shift();
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
if (finished) break;
|
|
663
|
+
await new Promise((resolve) => {
|
|
664
|
+
wake = resolve;
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
while (queue.length > 0) yield queue.shift();
|
|
668
|
+
if (failure) throw failure;
|
|
669
|
+
} finally {
|
|
670
|
+
if (stallTimer) clearTimeout(stallTimer);
|
|
671
|
+
options.abortSignal?.removeEventListener("abort", onAbort);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
675
|
+
var RETRY_BACKOFF_MS = [500, 1500];
|
|
676
|
+
async function sendWithRecovery(agent, message, sendOptions, debug) {
|
|
677
|
+
for (let attempt = 0; ; attempt++) {
|
|
678
|
+
try {
|
|
679
|
+
return await agent.send(message, sendOptions);
|
|
680
|
+
} catch (err) {
|
|
681
|
+
const classified = classifyError(err);
|
|
682
|
+
if (classified.kind === "agent-busy") {
|
|
683
|
+
if (debug) console.error("[cursor:debug] agent busy; retrying send with local.force");
|
|
684
|
+
return agent.send(message, { ...sendOptions, local: { force: true } });
|
|
685
|
+
}
|
|
686
|
+
if ((classified.kind === "rate-limit" || classified.kind === "network") && attempt < RETRY_BACKOFF_MS.length) {
|
|
687
|
+
if (debug)
|
|
688
|
+
console.error(
|
|
689
|
+
`[cursor:debug] ${classified.kind}; retrying send in ${RETRY_BACKOFF_MS[attempt]}ms`
|
|
690
|
+
);
|
|
691
|
+
await sleep(RETRY_BACKOFF_MS[attempt]);
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
throw err;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
async function sendAgentTurnSilently(agent, message, options) {
|
|
699
|
+
if (options.abortSignal?.aborted) return void 0;
|
|
700
|
+
const debug = process.env.OPENCODE_CURSOR_DEBUG === "1";
|
|
701
|
+
const runHolder = {};
|
|
702
|
+
let usage;
|
|
703
|
+
const onDelta = ({ update }) => {
|
|
704
|
+
if (update.type === "turn-ended" && update.usage) usage = update.usage;
|
|
705
|
+
};
|
|
706
|
+
const onAbort = () => {
|
|
707
|
+
void Promise.resolve(runHolder.run?.cancel()).catch(() => {
|
|
708
|
+
});
|
|
709
|
+
};
|
|
710
|
+
options.abortSignal?.addEventListener("abort", onAbort);
|
|
711
|
+
try {
|
|
712
|
+
const run = await sendWithRecovery(
|
|
713
|
+
agent,
|
|
714
|
+
message,
|
|
715
|
+
{ mode: options.mode, onDelta, ...options.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {} },
|
|
716
|
+
debug
|
|
717
|
+
);
|
|
718
|
+
runHolder.run = run;
|
|
719
|
+
if (options.abortSignal?.aborted) void Promise.resolve(run.cancel()).catch(() => {
|
|
720
|
+
});
|
|
721
|
+
const result = await run.wait();
|
|
722
|
+
if (result.status !== "finished") {
|
|
723
|
+
if (options.abortSignal?.aborted) return void 0;
|
|
724
|
+
throw new Error(
|
|
725
|
+
`Cursor run ended with status "${result.status}"${result.result ? `: ${result.result}` : ""}`
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
return usage;
|
|
729
|
+
} finally {
|
|
730
|
+
options.abortSignal?.removeEventListener("abort", onAbort);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// src/provider/controls.ts
|
|
735
|
+
function buildModelSelection(modelId, params) {
|
|
736
|
+
const paramList = Object.entries(params ?? {}).map(([id, value]) => ({ id, value }));
|
|
737
|
+
return paramList.length > 0 ? { id: modelId, params: paramList } : { id: modelId };
|
|
738
|
+
}
|
|
739
|
+
function isRecord(value) {
|
|
740
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
741
|
+
}
|
|
742
|
+
function isMode(value) {
|
|
743
|
+
return value === "agent" || value === "plan";
|
|
744
|
+
}
|
|
745
|
+
function resolveControls(modelId, staticControls, providerOptions) {
|
|
746
|
+
const po = providerOptions ?? {};
|
|
747
|
+
const mode = isMode(po["mode"]) ? po["mode"] : staticControls.mode;
|
|
748
|
+
const params = {
|
|
749
|
+
...staticControls.defaults ?? {},
|
|
750
|
+
...staticControls.params ?? {}
|
|
751
|
+
};
|
|
752
|
+
if (isRecord(po["params"])) {
|
|
753
|
+
for (const [key, value] of Object.entries(po["params"])) {
|
|
754
|
+
if (value != null) params[key] = String(value);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
if (typeof po["thinking"] === "string" && params["thinking"] === void 0) {
|
|
758
|
+
params["thinking"] = po["thinking"];
|
|
759
|
+
}
|
|
760
|
+
return { mode, modelSelection: buildModelSelection(modelId, params) };
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// src/provider/session-store.ts
|
|
764
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
765
|
+
import { homedir, tmpdir } from "os";
|
|
766
|
+
import { join as join2 } from "path";
|
|
767
|
+
var ENTRY_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
577
768
|
var MAX_ENTRIES = 200;
|
|
578
769
|
function storeDir() {
|
|
579
770
|
const base = process.env.XDG_CACHE_HOME?.trim() || (homedir() ? join2(homedir(), ".cache") : tmpdir());
|
|
@@ -641,7 +832,8 @@ async function acquireAgent(params) {
|
|
|
641
832
|
local: {
|
|
642
833
|
cwd: params.cwd,
|
|
643
834
|
...params.settingSources ? { settingSources: params.settingSources } : {},
|
|
644
|
-
...params.sandbox !== void 0 ? { sandboxOptions: { enabled: params.sandbox } } : {}
|
|
835
|
+
...params.sandbox !== void 0 ? { sandboxOptions: { enabled: params.sandbox } } : {},
|
|
836
|
+
...params.autoReview !== void 0 ? { autoReview: params.autoReview } : {}
|
|
645
837
|
},
|
|
646
838
|
...params.mcpServers ? { mcpServers: params.mcpServers } : {},
|
|
647
839
|
...params.agents ? { agents: params.agents } : {},
|
|
@@ -685,16 +877,19 @@ async function acquireAgent(params) {
|
|
|
685
877
|
export {
|
|
686
878
|
resolveCursorApiKey,
|
|
687
879
|
fingerprintApiKey,
|
|
880
|
+
loadCursorSdk,
|
|
881
|
+
setPreferredTransport,
|
|
688
882
|
extractSystemText,
|
|
689
883
|
removeSystemRule,
|
|
690
884
|
resolveSystemDelivery,
|
|
885
|
+
classifyError,
|
|
886
|
+
addUsage,
|
|
691
887
|
streamAgentTurn,
|
|
692
888
|
sendAgentTurnSilently,
|
|
693
889
|
buildModelSelection,
|
|
694
890
|
resolveControls,
|
|
695
|
-
loadCursorSdk,
|
|
696
891
|
getSessionRecord,
|
|
697
892
|
dropSessionRecord,
|
|
698
893
|
acquireAgent
|
|
699
894
|
};
|
|
700
|
-
//# sourceMappingURL=chunk-
|
|
895
|
+
//# sourceMappingURL=chunk-LAOFD3JB.js.map
|