@stablekernel/opencode-cursor 0.4.6 → 0.5.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +61 -0
- package/README.md +55 -16
- package/dist/{chunk-734L3SKU.js → chunk-LAOFD3JB.js} +491 -293
- package/dist/chunk-LAOFD3JB.js.map +1 -0
- package/dist/plugin/index.js +35 -5
- package/dist/plugin/index.js.map +1 -1
- package/dist/provider/index.d.ts +18 -0
- package/dist/provider/index.js +216 -24
- 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-734L3SKU.js.map +0 -1
|
@@ -16,267 +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 = { ...staticControls.params ?? {} };
|
|
270
|
-
if (isRecord(po["params"])) {
|
|
271
|
-
for (const [key, value] of Object.entries(po["params"])) {
|
|
272
|
-
if (value != null) params[key] = String(value);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
if (typeof po["thinking"] === "string" && params["thinking"] === void 0) {
|
|
276
|
-
params["thinking"] = po["thinking"];
|
|
277
|
-
}
|
|
278
|
-
return { mode, modelSelection: buildModelSelection(modelId, params) };
|
|
279
|
-
}
|
|
19
|
+
// src/provider/agent-backend.ts
|
|
20
|
+
import { execSync } from "child_process";
|
|
21
|
+
import { existsSync } from "fs";
|
|
22
|
+
import { fileURLToPath } from "url";
|
|
280
23
|
|
|
281
24
|
// src/cursor-runtime.ts
|
|
282
25
|
var cached;
|
|
@@ -293,11 +36,6 @@ async function loadCursorSdk() {
|
|
|
293
36
|
return cached;
|
|
294
37
|
}
|
|
295
38
|
|
|
296
|
-
// src/provider/agent-backend.ts
|
|
297
|
-
import { execSync } from "child_process";
|
|
298
|
-
import { existsSync as existsSync2 } from "fs";
|
|
299
|
-
import { fileURLToPath } from "url";
|
|
300
|
-
|
|
301
39
|
// src/provider/sidecar-client.ts
|
|
302
40
|
import { spawn } from "child_process";
|
|
303
41
|
import { createInterface } from "readline";
|
|
@@ -305,6 +43,10 @@ function reviveError(error) {
|
|
|
305
43
|
const e = error ?? {};
|
|
306
44
|
const err = new Error(e.message ?? "sidecar error");
|
|
307
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;
|
|
308
50
|
return err;
|
|
309
51
|
}
|
|
310
52
|
var SidecarClient = class {
|
|
@@ -459,7 +201,8 @@ var SidecarClient = class {
|
|
|
459
201
|
agentId,
|
|
460
202
|
message,
|
|
461
203
|
...options?.mode ? { mode: options.mode } : {},
|
|
462
|
-
...options?.local?.force ? { force: true } : {}
|
|
204
|
+
...options?.local?.force ? { force: true } : {},
|
|
205
|
+
...options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {}
|
|
463
206
|
},
|
|
464
207
|
{
|
|
465
208
|
onUpdate: (update) => options?.onDelta?.({ update }),
|
|
@@ -490,11 +233,28 @@ var SidecarClient = class {
|
|
|
490
233
|
};
|
|
491
234
|
|
|
492
235
|
// src/provider/agent-backend.ts
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
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";
|
|
498
258
|
}
|
|
499
259
|
function detectNode() {
|
|
500
260
|
try {
|
|
@@ -512,15 +272,24 @@ function detectEnvironment() {
|
|
|
512
272
|
const needsNode = isBun || process.env["OPENCODE_CURSOR_SIDECAR"] === "1";
|
|
513
273
|
return { isBun, nodePath: needsNode ? detectNode() : process.execPath };
|
|
514
274
|
}
|
|
515
|
-
|
|
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) {
|
|
516
283
|
return {
|
|
517
284
|
kind: "in-process",
|
|
518
285
|
createAgent: async (options) => {
|
|
519
286
|
const { Agent } = await loadCursorSdk();
|
|
287
|
+
if (useHttp1) await ensureHttp1Configured();
|
|
520
288
|
return await Agent.create(options);
|
|
521
289
|
},
|
|
522
290
|
resumeAgent: async (agentId, options) => {
|
|
523
291
|
const { Agent } = await loadCursorSdk();
|
|
292
|
+
if (useHttp1) await ensureHttp1Configured();
|
|
524
293
|
return await Agent.resume(agentId, options);
|
|
525
294
|
}
|
|
526
295
|
};
|
|
@@ -536,7 +305,7 @@ function resolveSidecarScript() {
|
|
|
536
305
|
];
|
|
537
306
|
for (const candidate of candidates) {
|
|
538
307
|
const path = fileURLToPath(new URL(candidate, import.meta.url));
|
|
539
|
-
if (
|
|
308
|
+
if (existsSync(path)) return path;
|
|
540
309
|
}
|
|
541
310
|
return void 0;
|
|
542
311
|
}
|
|
@@ -552,29 +321,454 @@ var cached2;
|
|
|
552
321
|
function loadAgentBackend() {
|
|
553
322
|
if (!cached2) {
|
|
554
323
|
const env = detectEnvironment();
|
|
555
|
-
const
|
|
556
|
-
const scriptPath =
|
|
557
|
-
|
|
558
|
-
const optedOut = override === "0" || override === "false";
|
|
559
|
-
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)) {
|
|
560
327
|
console.error(
|
|
561
|
-
`[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.`
|
|
562
329
|
);
|
|
330
|
+
cached2 = inProcessBackend(true);
|
|
331
|
+
return cached2;
|
|
563
332
|
}
|
|
564
|
-
|
|
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");
|
|
565
339
|
}
|
|
566
340
|
return cached2;
|
|
567
341
|
}
|
|
568
342
|
|
|
569
|
-
// src/provider/
|
|
570
|
-
import {
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
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;
|
|
768
|
+
var MAX_ENTRIES = 200;
|
|
769
|
+
function storeDir() {
|
|
770
|
+
const base = process.env.XDG_CACHE_HOME?.trim() || (homedir() ? join2(homedir(), ".cache") : tmpdir());
|
|
771
|
+
return join2(base, "opencode-cursor");
|
|
578
772
|
}
|
|
579
773
|
function storeFile() {
|
|
580
774
|
return join2(storeDir(), "session-pool.json");
|
|
@@ -638,7 +832,8 @@ async function acquireAgent(params) {
|
|
|
638
832
|
local: {
|
|
639
833
|
cwd: params.cwd,
|
|
640
834
|
...params.settingSources ? { settingSources: params.settingSources } : {},
|
|
641
|
-
...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 } : {}
|
|
642
837
|
},
|
|
643
838
|
...params.mcpServers ? { mcpServers: params.mcpServers } : {},
|
|
644
839
|
...params.agents ? { agents: params.agents } : {},
|
|
@@ -682,16 +877,19 @@ async function acquireAgent(params) {
|
|
|
682
877
|
export {
|
|
683
878
|
resolveCursorApiKey,
|
|
684
879
|
fingerprintApiKey,
|
|
880
|
+
loadCursorSdk,
|
|
881
|
+
setPreferredTransport,
|
|
685
882
|
extractSystemText,
|
|
686
883
|
removeSystemRule,
|
|
687
884
|
resolveSystemDelivery,
|
|
885
|
+
classifyError,
|
|
886
|
+
addUsage,
|
|
688
887
|
streamAgentTurn,
|
|
689
888
|
sendAgentTurnSilently,
|
|
690
889
|
buildModelSelection,
|
|
691
890
|
resolveControls,
|
|
692
|
-
loadCursorSdk,
|
|
693
891
|
getSessionRecord,
|
|
694
892
|
dropSessionRecord,
|
|
695
893
|
acquireAgent
|
|
696
894
|
};
|
|
697
|
-
//# sourceMappingURL=chunk-
|
|
895
|
+
//# sourceMappingURL=chunk-LAOFD3JB.js.map
|