@rama_nigg/open-cursor 2.3.13 → 2.3.15
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/README.md +2 -0
- package/dist/cli/discover.js +27 -8
- package/dist/cli/opencode-cursor.js +38 -19
- package/dist/index.js +415 -162
- package/dist/plugin-entry.js +342 -166
- package/package.json +1 -1
- package/src/cli/model-discovery.ts +21 -1
- package/src/client/simple.ts +16 -3
- package/src/models/discovery.ts +13 -3
- package/src/models/sync.ts +153 -0
- package/src/plugin.ts +3 -2
- package/src/streaming/ai-sdk-parts.ts +10 -4
- package/src/streaming/openai-sse.ts +10 -4
package/dist/index.js
CHANGED
|
@@ -13364,9 +13364,12 @@ class StreamToSseConverter {
|
|
|
13364
13364
|
if (isAssistantText(event)) {
|
|
13365
13365
|
const isPartial = typeof event.timestamp_ms === "number";
|
|
13366
13366
|
if (isPartial) {
|
|
13367
|
-
this.sawAssistantPartials = true;
|
|
13368
13367
|
const text = extractText(event);
|
|
13369
|
-
|
|
13368
|
+
if (text) {
|
|
13369
|
+
this.sawAssistantPartials = true;
|
|
13370
|
+
return [this.chunkWith({ content: text })];
|
|
13371
|
+
}
|
|
13372
|
+
return [];
|
|
13370
13373
|
}
|
|
13371
13374
|
if (this.sawAssistantPartials) {
|
|
13372
13375
|
return [];
|
|
@@ -13377,9 +13380,12 @@ class StreamToSseConverter {
|
|
|
13377
13380
|
if (isThinking(event)) {
|
|
13378
13381
|
const isPartial = typeof event.timestamp_ms === "number";
|
|
13379
13382
|
if (isPartial) {
|
|
13380
|
-
this.sawThinkingPartials = true;
|
|
13381
13383
|
const text = extractThinking(event);
|
|
13382
|
-
|
|
13384
|
+
if (text) {
|
|
13385
|
+
this.sawThinkingPartials = true;
|
|
13386
|
+
return [this.chunkWith({ reasoning_content: text })];
|
|
13387
|
+
}
|
|
13388
|
+
return [];
|
|
13383
13389
|
}
|
|
13384
13390
|
if (this.sawThinkingPartials) {
|
|
13385
13391
|
return [];
|
|
@@ -14246,13 +14252,240 @@ class SkillResolver {
|
|
|
14246
14252
|
}
|
|
14247
14253
|
}
|
|
14248
14254
|
|
|
14255
|
+
// src/cli/model-discovery.ts
|
|
14256
|
+
import { execFileSync } from "child_process";
|
|
14257
|
+
function parseCursorModelsOutput(output) {
|
|
14258
|
+
const clean = stripAnsi(output);
|
|
14259
|
+
const models = [];
|
|
14260
|
+
const seen = new Set;
|
|
14261
|
+
for (const line of clean.split(`
|
|
14262
|
+
`)) {
|
|
14263
|
+
const trimmed = line.trim();
|
|
14264
|
+
if (!trimmed)
|
|
14265
|
+
continue;
|
|
14266
|
+
const match = trimmed.match(/^([a-zA-Z0-9._-]+)\s+-\s+(.+?)(?:\s+\((?:current|default)\))*\s*$/);
|
|
14267
|
+
if (!match)
|
|
14268
|
+
continue;
|
|
14269
|
+
const id = match[1];
|
|
14270
|
+
if (seen.has(id))
|
|
14271
|
+
continue;
|
|
14272
|
+
seen.add(id);
|
|
14273
|
+
models.push({ id, name: match[2].trim() });
|
|
14274
|
+
}
|
|
14275
|
+
return models;
|
|
14276
|
+
}
|
|
14277
|
+
function discoverModelsFromCursorAgent() {
|
|
14278
|
+
const raw = execFileSync("cursor-agent", ["models"], {
|
|
14279
|
+
encoding: "utf8",
|
|
14280
|
+
killSignal: "SIGTERM",
|
|
14281
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
14282
|
+
timeout: MODEL_DISCOVERY_TIMEOUT_MS
|
|
14283
|
+
});
|
|
14284
|
+
const models = parseCursorModelsOutput(raw);
|
|
14285
|
+
if (models.length === 0) {
|
|
14286
|
+
throw new Error("No models parsed from cursor-agent output");
|
|
14287
|
+
}
|
|
14288
|
+
return models;
|
|
14289
|
+
}
|
|
14290
|
+
function fallbackModels() {
|
|
14291
|
+
return [
|
|
14292
|
+
{ id: "auto", name: "Auto" },
|
|
14293
|
+
{ id: "composer-1.5", name: "Composer 1.5" },
|
|
14294
|
+
{ id: "composer-1", name: "Composer 1" },
|
|
14295
|
+
{ id: "opus-4.6-thinking", name: "Claude 4.6 Opus (Thinking)" },
|
|
14296
|
+
{ id: "opus-4.6", name: "Claude 4.6 Opus" },
|
|
14297
|
+
{ id: "sonnet-4.6", name: "Claude 4.6 Sonnet" },
|
|
14298
|
+
{ id: "sonnet-4.6-thinking", name: "Claude 4.6 Sonnet (Thinking)" },
|
|
14299
|
+
{ id: "opus-4.5", name: "Claude 4.5 Opus" },
|
|
14300
|
+
{ id: "opus-4.5-thinking", name: "Claude 4.5 Opus (Thinking)" },
|
|
14301
|
+
{ id: "sonnet-4.5", name: "Claude 4.5 Sonnet" },
|
|
14302
|
+
{ id: "sonnet-4.5-thinking", name: "Claude 4.5 Sonnet (Thinking)" },
|
|
14303
|
+
{ id: "gpt-5.4-high", name: "GPT-5.4 High" },
|
|
14304
|
+
{ id: "gpt-5.4-medium", name: "GPT-5.4" },
|
|
14305
|
+
{ id: "gpt-5.3-codex", name: "GPT-5.3 Codex" },
|
|
14306
|
+
{ id: "gpt-5.2", name: "GPT-5.2" },
|
|
14307
|
+
{ id: "gemini-3.1-pro", name: "Gemini 3.1 Pro" },
|
|
14308
|
+
{ id: "gemini-3-pro", name: "Gemini 3 Pro" },
|
|
14309
|
+
{ id: "gemini-3-flash", name: "Gemini 3 Flash" },
|
|
14310
|
+
{ id: "grok", name: "Grok" },
|
|
14311
|
+
{ id: "kimi-k2.5", name: "Kimi K2.5" }
|
|
14312
|
+
];
|
|
14313
|
+
}
|
|
14314
|
+
var MODEL_DISCOVERY_TIMEOUT_MS = 5000;
|
|
14315
|
+
var init_model_discovery = () => {};
|
|
14316
|
+
|
|
14317
|
+
// src/plugin-toggle.ts
|
|
14318
|
+
import { existsSync as existsSync4, readFileSync } from "fs";
|
|
14319
|
+
import { homedir as homedir4 } from "os";
|
|
14320
|
+
import { join as join4, resolve } from "path";
|
|
14321
|
+
function matchesPlugin(entry) {
|
|
14322
|
+
if (entry === CURSOR_PROVIDER_ID)
|
|
14323
|
+
return true;
|
|
14324
|
+
if (entry === NPM_PACKAGE_NAME)
|
|
14325
|
+
return true;
|
|
14326
|
+
if (entry.startsWith(`${NPM_PACKAGE_NAME}@`))
|
|
14327
|
+
return true;
|
|
14328
|
+
return false;
|
|
14329
|
+
}
|
|
14330
|
+
function resolveOpenCodeConfigPath(env = process.env) {
|
|
14331
|
+
if (env.OPENCODE_CONFIG && env.OPENCODE_CONFIG.length > 0) {
|
|
14332
|
+
return resolve(env.OPENCODE_CONFIG);
|
|
14333
|
+
}
|
|
14334
|
+
const configHome = env.XDG_CONFIG_HOME && env.XDG_CONFIG_HOME.length > 0 ? env.XDG_CONFIG_HOME : join4(homedir4(), ".config");
|
|
14335
|
+
return join4(configHome, "opencode", "opencode.json");
|
|
14336
|
+
}
|
|
14337
|
+
function isCursorPluginEnabledInConfig(config2) {
|
|
14338
|
+
if (!config2 || typeof config2 !== "object") {
|
|
14339
|
+
return true;
|
|
14340
|
+
}
|
|
14341
|
+
const configObject = config2;
|
|
14342
|
+
if (Array.isArray(configObject.plugin)) {
|
|
14343
|
+
return configObject.plugin.some((entry) => matchesPlugin(entry));
|
|
14344
|
+
}
|
|
14345
|
+
return true;
|
|
14346
|
+
}
|
|
14347
|
+
function shouldEnableCursorPlugin(env = process.env) {
|
|
14348
|
+
const configPath = resolveOpenCodeConfigPath(env);
|
|
14349
|
+
if (!existsSync4(configPath)) {
|
|
14350
|
+
return {
|
|
14351
|
+
enabled: true,
|
|
14352
|
+
configPath,
|
|
14353
|
+
reason: "config_missing"
|
|
14354
|
+
};
|
|
14355
|
+
}
|
|
14356
|
+
try {
|
|
14357
|
+
const raw = readFileSync(configPath, "utf8");
|
|
14358
|
+
const parsed = JSON.parse(raw);
|
|
14359
|
+
const enabled = isCursorPluginEnabledInConfig(parsed);
|
|
14360
|
+
return {
|
|
14361
|
+
enabled,
|
|
14362
|
+
configPath,
|
|
14363
|
+
reason: enabled ? "enabled_in_plugin_array_or_legacy" : "disabled_in_plugin_array"
|
|
14364
|
+
};
|
|
14365
|
+
} catch {
|
|
14366
|
+
return {
|
|
14367
|
+
enabled: true,
|
|
14368
|
+
configPath,
|
|
14369
|
+
reason: "config_unreadable_or_invalid"
|
|
14370
|
+
};
|
|
14371
|
+
}
|
|
14372
|
+
}
|
|
14373
|
+
var CURSOR_PROVIDER_ID = "cursor-acp", NPM_PACKAGE_NAME = "@rama_nigg/open-cursor";
|
|
14374
|
+
var init_plugin_toggle = () => {};
|
|
14375
|
+
|
|
14376
|
+
// src/models/sync.ts
|
|
14377
|
+
import {
|
|
14378
|
+
existsSync as nodeExistsSync,
|
|
14379
|
+
readFileSync as nodeReadFileSync,
|
|
14380
|
+
writeFileSync as nodeWriteFileSync
|
|
14381
|
+
} from "node:fs";
|
|
14382
|
+
function isRecord2(value) {
|
|
14383
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14384
|
+
}
|
|
14385
|
+
function parseConfig(raw) {
|
|
14386
|
+
try {
|
|
14387
|
+
const parsed = JSON.parse(raw);
|
|
14388
|
+
return isRecord2(parsed) ? parsed : null;
|
|
14389
|
+
} catch {
|
|
14390
|
+
return null;
|
|
14391
|
+
}
|
|
14392
|
+
}
|
|
14393
|
+
function getProviderConfig(config2) {
|
|
14394
|
+
if (!isRecord2(config2.provider)) {
|
|
14395
|
+
return null;
|
|
14396
|
+
}
|
|
14397
|
+
const provider = config2.provider[PROVIDER_ID];
|
|
14398
|
+
return isRecord2(provider) ? provider : null;
|
|
14399
|
+
}
|
|
14400
|
+
function getExistingModels(provider) {
|
|
14401
|
+
return isRecord2(provider.models) ? { ...provider.models } : {};
|
|
14402
|
+
}
|
|
14403
|
+
function yieldForFireAndForget() {
|
|
14404
|
+
return Promise.resolve();
|
|
14405
|
+
}
|
|
14406
|
+
async function autoRefreshModels(deps = {}) {
|
|
14407
|
+
const resolvedDeps = {
|
|
14408
|
+
...defaultDeps,
|
|
14409
|
+
defer: yieldForFireAndForget,
|
|
14410
|
+
...deps
|
|
14411
|
+
};
|
|
14412
|
+
await resolvedDeps.defer();
|
|
14413
|
+
try {
|
|
14414
|
+
const configPath = resolveOpenCodeConfigPath(resolvedDeps.env);
|
|
14415
|
+
if (!resolvedDeps.existsSync(configPath)) {
|
|
14416
|
+
resolvedDeps.log.debug("Config file not found, skipping model auto-refresh", { configPath });
|
|
14417
|
+
return;
|
|
14418
|
+
}
|
|
14419
|
+
const raw = resolvedDeps.readFileSync(configPath, "utf8");
|
|
14420
|
+
const config2 = parseConfig(raw);
|
|
14421
|
+
if (!config2) {
|
|
14422
|
+
resolvedDeps.log.debug("Config file is not valid JSON, skipping model auto-refresh");
|
|
14423
|
+
return;
|
|
14424
|
+
}
|
|
14425
|
+
const provider = getProviderConfig(config2);
|
|
14426
|
+
if (!provider) {
|
|
14427
|
+
resolvedDeps.log.debug("Provider section not found in config, skipping model auto-refresh");
|
|
14428
|
+
return;
|
|
14429
|
+
}
|
|
14430
|
+
const existingModels = getExistingModels(provider);
|
|
14431
|
+
let discovered;
|
|
14432
|
+
try {
|
|
14433
|
+
discovered = resolvedDeps.discoverModels();
|
|
14434
|
+
} catch (err) {
|
|
14435
|
+
resolvedDeps.log.debug("cursor-agent model discovery failed, skipping auto-refresh", {
|
|
14436
|
+
error: String(err)
|
|
14437
|
+
});
|
|
14438
|
+
return;
|
|
14439
|
+
}
|
|
14440
|
+
let addedCount = 0;
|
|
14441
|
+
for (const model of discovered) {
|
|
14442
|
+
if (Object.prototype.hasOwnProperty.call(existingModels, model.id))
|
|
14443
|
+
continue;
|
|
14444
|
+
existingModels[model.id] = { name: model.name };
|
|
14445
|
+
addedCount++;
|
|
14446
|
+
}
|
|
14447
|
+
if (addedCount === 0) {
|
|
14448
|
+
resolvedDeps.log.debug("Model auto-refresh: no new models found", {
|
|
14449
|
+
existing: Object.keys(existingModels).length,
|
|
14450
|
+
discovered: discovered.length
|
|
14451
|
+
});
|
|
14452
|
+
return;
|
|
14453
|
+
}
|
|
14454
|
+
provider.models = existingModels;
|
|
14455
|
+
resolvedDeps.writeFileSync(configPath, `${JSON.stringify(config2, null, 2)}
|
|
14456
|
+
`, "utf8");
|
|
14457
|
+
resolvedDeps.log.info("Model auto-refresh: added new models", {
|
|
14458
|
+
added: addedCount,
|
|
14459
|
+
total: Object.keys(existingModels).length
|
|
14460
|
+
});
|
|
14461
|
+
} catch (err) {
|
|
14462
|
+
resolvedDeps.log.debug("Model auto-refresh failed", { error: String(err) });
|
|
14463
|
+
}
|
|
14464
|
+
}
|
|
14465
|
+
var log9, PROVIDER_ID = "cursor-acp", defaultDeps;
|
|
14466
|
+
var init_sync = __esm(() => {
|
|
14467
|
+
init_model_discovery();
|
|
14468
|
+
init_plugin_toggle();
|
|
14469
|
+
init_logger();
|
|
14470
|
+
log9 = createLogger("model-sync");
|
|
14471
|
+
defaultDeps = {
|
|
14472
|
+
defer: () => Promise.resolve(),
|
|
14473
|
+
discoverModels: discoverModelsFromCursorAgent,
|
|
14474
|
+
env: process.env,
|
|
14475
|
+
existsSync: nodeExistsSync,
|
|
14476
|
+
log: log9,
|
|
14477
|
+
readFileSync: nodeReadFileSync,
|
|
14478
|
+
writeFileSync: nodeWriteFileSync
|
|
14479
|
+
};
|
|
14480
|
+
});
|
|
14481
|
+
|
|
14249
14482
|
// node_modules/@opencode-ai/sdk/dist/gen/types.gen.js
|
|
14250
14483
|
var init_types_gen = () => {};
|
|
14251
14484
|
|
|
14252
14485
|
// node_modules/@opencode-ai/sdk/dist/gen/core/serverSentEvents.gen.js
|
|
14253
14486
|
var createSseClient = ({ onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url: url2, ...options }) => {
|
|
14254
14487
|
let lastEventId;
|
|
14255
|
-
const sleep = sseSleepFn ?? ((ms) => new Promise((
|
|
14488
|
+
const sleep = sseSleepFn ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
|
|
14256
14489
|
const createStream = async function* () {
|
|
14257
14490
|
let retryDelay = sseDefaultRetryDelay ?? 3000;
|
|
14258
14491
|
let attempt = 0;
|
|
@@ -15654,15 +15887,15 @@ class LocalExecutor {
|
|
|
15654
15887
|
const out = await handler(args);
|
|
15655
15888
|
return { status: "success", output: out };
|
|
15656
15889
|
} catch (err) {
|
|
15657
|
-
|
|
15890
|
+
log10.warn("Local tool execution failed", { toolId, error: String(err?.message || err) });
|
|
15658
15891
|
return { status: "error", error: String(err?.message || err) };
|
|
15659
15892
|
}
|
|
15660
15893
|
}
|
|
15661
15894
|
}
|
|
15662
|
-
var
|
|
15895
|
+
var log10;
|
|
15663
15896
|
var init_local = __esm(() => {
|
|
15664
15897
|
init_logger();
|
|
15665
|
-
|
|
15898
|
+
log10 = createLogger("tools:executor:local");
|
|
15666
15899
|
});
|
|
15667
15900
|
|
|
15668
15901
|
// src/tools/executors/sdk.ts
|
|
@@ -15689,7 +15922,7 @@ class SdkExecutor {
|
|
|
15689
15922
|
const out = typeof res === "string" ? res : JSON.stringify(res);
|
|
15690
15923
|
return { status: "success", output: out };
|
|
15691
15924
|
} catch (err) {
|
|
15692
|
-
|
|
15925
|
+
log11.warn("SDK tool execution failed", { toolId, error: String(err?.message || err) });
|
|
15693
15926
|
return { status: "error", error: String(err?.message || err) };
|
|
15694
15927
|
}
|
|
15695
15928
|
}
|
|
@@ -15702,10 +15935,10 @@ class SdkExecutor {
|
|
|
15702
15935
|
]);
|
|
15703
15936
|
}
|
|
15704
15937
|
}
|
|
15705
|
-
var
|
|
15938
|
+
var log11;
|
|
15706
15939
|
var init_sdk = __esm(() => {
|
|
15707
15940
|
init_logger();
|
|
15708
|
-
|
|
15941
|
+
log11 = createLogger("tools:executor:sdk");
|
|
15709
15942
|
});
|
|
15710
15943
|
|
|
15711
15944
|
// src/tools/executors/mcp.ts
|
|
@@ -15732,7 +15965,7 @@ class McpExecutor {
|
|
|
15732
15965
|
const out = typeof res === "string" ? res : JSON.stringify(res);
|
|
15733
15966
|
return { status: "success", output: out };
|
|
15734
15967
|
} catch (err) {
|
|
15735
|
-
|
|
15968
|
+
log12.warn("MCP tool execution failed", { toolId, error: String(err?.message || err) });
|
|
15736
15969
|
return { status: "error", error: String(err?.message || err) };
|
|
15737
15970
|
}
|
|
15738
15971
|
}
|
|
@@ -15745,10 +15978,10 @@ class McpExecutor {
|
|
|
15745
15978
|
]);
|
|
15746
15979
|
}
|
|
15747
15980
|
}
|
|
15748
|
-
var
|
|
15981
|
+
var log12;
|
|
15749
15982
|
var init_mcp = __esm(() => {
|
|
15750
15983
|
init_logger();
|
|
15751
|
-
|
|
15984
|
+
log12 = createLogger("tools:executor:mcp");
|
|
15752
15985
|
});
|
|
15753
15986
|
|
|
15754
15987
|
// src/tools/core/executor.ts
|
|
@@ -15758,17 +15991,17 @@ async function executeWithChain(executors, toolId, args) {
|
|
|
15758
15991
|
try {
|
|
15759
15992
|
return await ex.execute(toolId, args);
|
|
15760
15993
|
} catch (err) {
|
|
15761
|
-
|
|
15994
|
+
log13.warn("Executor threw unexpected error", { toolId, error: String(err?.message || err) });
|
|
15762
15995
|
return { status: "error", error: String(err?.message || err) };
|
|
15763
15996
|
}
|
|
15764
15997
|
}
|
|
15765
15998
|
}
|
|
15766
15999
|
return { status: "error", error: `No executor available for ${toolId}` };
|
|
15767
16000
|
}
|
|
15768
|
-
var
|
|
16001
|
+
var log13;
|
|
15769
16002
|
var init_executor = __esm(() => {
|
|
15770
16003
|
init_logger();
|
|
15771
|
-
|
|
16004
|
+
log13 = createLogger("tools:executor:chain");
|
|
15772
16005
|
});
|
|
15773
16006
|
|
|
15774
16007
|
// src/tools/defaults.ts
|
|
@@ -16131,12 +16364,12 @@ function registerDefaultTools(registry2) {
|
|
|
16131
16364
|
source: "local"
|
|
16132
16365
|
}, async (args) => {
|
|
16133
16366
|
const { mkdir } = await import("fs/promises");
|
|
16134
|
-
const { resolve } = await import("path");
|
|
16367
|
+
const { resolve: resolve2 } = await import("path");
|
|
16135
16368
|
const rawPath = resolvePathArg(args, "mkdir");
|
|
16136
16369
|
if (!rawPath) {
|
|
16137
16370
|
throw new Error("mkdir: missing required argument 'path'");
|
|
16138
16371
|
}
|
|
16139
|
-
const target =
|
|
16372
|
+
const target = resolve2(rawPath);
|
|
16140
16373
|
await mkdir(target, { recursive: true });
|
|
16141
16374
|
return `Created directory: ${target}`;
|
|
16142
16375
|
});
|
|
@@ -16161,12 +16394,12 @@ function registerDefaultTools(registry2) {
|
|
|
16161
16394
|
source: "local"
|
|
16162
16395
|
}, async (args) => {
|
|
16163
16396
|
const { rm, stat } = await import("fs/promises");
|
|
16164
|
-
const { resolve } = await import("path");
|
|
16397
|
+
const { resolve: resolve2 } = await import("path");
|
|
16165
16398
|
const rawPath = resolvePathArg(args, "rm");
|
|
16166
16399
|
if (!rawPath) {
|
|
16167
16400
|
throw new Error("rm: missing required argument 'path'");
|
|
16168
16401
|
}
|
|
16169
|
-
const target =
|
|
16402
|
+
const target = resolve2(rawPath);
|
|
16170
16403
|
const force = resolveBoolean(args.force, false);
|
|
16171
16404
|
const info = await stat(target);
|
|
16172
16405
|
if (info.isDirectory() && !force) {
|
|
@@ -16192,12 +16425,12 @@ function registerDefaultTools(registry2) {
|
|
|
16192
16425
|
source: "local"
|
|
16193
16426
|
}, async (args) => {
|
|
16194
16427
|
const { stat } = await import("fs/promises");
|
|
16195
|
-
const { resolve } = await import("path");
|
|
16428
|
+
const { resolve: resolve2 } = await import("path");
|
|
16196
16429
|
const rawPath = resolvePathArg(args, "stat");
|
|
16197
16430
|
if (!rawPath) {
|
|
16198
16431
|
throw new Error("stat: missing required argument 'path'");
|
|
16199
16432
|
}
|
|
16200
|
-
const target =
|
|
16433
|
+
const target = resolve2(rawPath);
|
|
16201
16434
|
const info = await stat(target);
|
|
16202
16435
|
return JSON.stringify({
|
|
16203
16436
|
path: target,
|
|
@@ -16445,11 +16678,11 @@ var init_boundary = __esm(() => {
|
|
|
16445
16678
|
function buildToolSchemaMap(tools) {
|
|
16446
16679
|
const schemas3 = new Map;
|
|
16447
16680
|
for (const rawTool of tools) {
|
|
16448
|
-
const tool3 =
|
|
16681
|
+
const tool3 = isRecord3(rawTool) ? rawTool : null;
|
|
16449
16682
|
if (!tool3) {
|
|
16450
16683
|
continue;
|
|
16451
16684
|
}
|
|
16452
|
-
const fn =
|
|
16685
|
+
const fn = isRecord3(tool3.function) ? tool3.function : tool3;
|
|
16453
16686
|
const name = typeof fn.name === "string" ? fn.name.trim() : "";
|
|
16454
16687
|
if (!name) {
|
|
16455
16688
|
continue;
|
|
@@ -16487,7 +16720,7 @@ function applyToolSchemaCompat(toolCall, toolSchemaMap) {
|
|
|
16487
16720
|
function parseArguments(rawArguments) {
|
|
16488
16721
|
try {
|
|
16489
16722
|
const parsed = JSON.parse(rawArguments);
|
|
16490
|
-
if (
|
|
16723
|
+
if (isRecord3(parsed)) {
|
|
16491
16724
|
return parsed;
|
|
16492
16725
|
}
|
|
16493
16726
|
return { value: parsed };
|
|
@@ -16549,7 +16782,7 @@ function normalizeToolSpecificArgs(toolName, args) {
|
|
|
16549
16782
|
return args;
|
|
16550
16783
|
}
|
|
16551
16784
|
const todos = args.todos.map((entry) => {
|
|
16552
|
-
if (!
|
|
16785
|
+
if (!isRecord3(entry)) {
|
|
16553
16786
|
return entry;
|
|
16554
16787
|
}
|
|
16555
16788
|
const todo = { ...entry };
|
|
@@ -16612,7 +16845,7 @@ function normalizeBashCommand(value) {
|
|
|
16612
16845
|
const parts = value.map((entry) => typeof entry === "string" ? entry : coerceToString2(entry)).filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
16613
16846
|
return parts.length > 0 ? parts.join(" ") : null;
|
|
16614
16847
|
}
|
|
16615
|
-
if (
|
|
16848
|
+
if (isRecord3(value)) {
|
|
16616
16849
|
const command = typeof value.command === "string" ? value.command : null;
|
|
16617
16850
|
const args = Array.isArray(value.args) ? value.args.map((entry) => typeof entry === "string" ? entry : coerceToString2(entry)).filter((entry) => typeof entry === "string" && entry.length > 0) : [];
|
|
16618
16851
|
if (command && args.length > 0) {
|
|
@@ -16647,13 +16880,13 @@ function normalizeTodoStatus(status) {
|
|
|
16647
16880
|
return status;
|
|
16648
16881
|
}
|
|
16649
16882
|
function sanitizeArgumentsForSchema(args, schema) {
|
|
16650
|
-
if (!
|
|
16883
|
+
if (!isRecord3(schema)) {
|
|
16651
16884
|
return { args, unexpected: [] };
|
|
16652
16885
|
}
|
|
16653
16886
|
if (schema.additionalProperties !== false) {
|
|
16654
16887
|
return { args, unexpected: [] };
|
|
16655
16888
|
}
|
|
16656
|
-
const properties =
|
|
16889
|
+
const properties = isRecord3(schema.properties) ? schema.properties : {};
|
|
16657
16890
|
const propertyNames = new Set(Object.keys(properties));
|
|
16658
16891
|
const sanitized = {};
|
|
16659
16892
|
const unexpected = [];
|
|
@@ -16667,7 +16900,7 @@ function sanitizeArgumentsForSchema(args, schema) {
|
|
|
16667
16900
|
return { args: sanitized, unexpected };
|
|
16668
16901
|
}
|
|
16669
16902
|
function validateToolArguments(toolName, args, schema, unexpected) {
|
|
16670
|
-
if (!
|
|
16903
|
+
if (!isRecord3(schema)) {
|
|
16671
16904
|
return {
|
|
16672
16905
|
hasSchema: false,
|
|
16673
16906
|
ok: true,
|
|
@@ -16676,13 +16909,13 @@ function validateToolArguments(toolName, args, schema, unexpected) {
|
|
|
16676
16909
|
typeErrors: []
|
|
16677
16910
|
};
|
|
16678
16911
|
}
|
|
16679
|
-
const properties =
|
|
16912
|
+
const properties = isRecord3(schema.properties) ? schema.properties : {};
|
|
16680
16913
|
const required2 = Array.isArray(schema.required) ? schema.required.filter((value) => typeof value === "string") : [];
|
|
16681
16914
|
const missing = required2.filter((key) => !hasOwn(args, key));
|
|
16682
16915
|
const typeErrors = [];
|
|
16683
16916
|
for (const [key, value] of Object.entries(args)) {
|
|
16684
16917
|
const propertySchema = properties[key];
|
|
16685
|
-
if (!
|
|
16918
|
+
if (!isRecord3(propertySchema)) {
|
|
16686
16919
|
continue;
|
|
16687
16920
|
}
|
|
16688
16921
|
if (!matchesType(value, propertySchema.type)) {
|
|
@@ -16741,7 +16974,7 @@ function matchesType(value, schemaType) {
|
|
|
16741
16974
|
case "boolean":
|
|
16742
16975
|
return typeof value === "boolean";
|
|
16743
16976
|
case "object":
|
|
16744
|
-
return
|
|
16977
|
+
return isRecord3(value);
|
|
16745
16978
|
case "array":
|
|
16746
16979
|
return Array.isArray(value);
|
|
16747
16980
|
case "null":
|
|
@@ -16762,7 +16995,7 @@ function coerceToString2(value) {
|
|
|
16762
16995
|
for (const item of value) {
|
|
16763
16996
|
if (typeof item === "string") {
|
|
16764
16997
|
parts.push(item);
|
|
16765
|
-
} else if (
|
|
16998
|
+
} else if (isRecord3(item)) {
|
|
16766
16999
|
const text = typeof item.text === "string" ? item.text : typeof item.content === "string" ? item.content : typeof item.value === "string" ? item.value : null;
|
|
16767
17000
|
if (text !== null) {
|
|
16768
17001
|
parts.push(text);
|
|
@@ -16775,7 +17008,7 @@ function coerceToString2(value) {
|
|
|
16775
17008
|
}
|
|
16776
17009
|
return parts.length > 0 ? parts.join("") : null;
|
|
16777
17010
|
}
|
|
16778
|
-
if (
|
|
17011
|
+
if (isRecord3(value)) {
|
|
16779
17012
|
if (typeof value.text === "string") {
|
|
16780
17013
|
return value.text;
|
|
16781
17014
|
}
|
|
@@ -16795,7 +17028,7 @@ function coerceToString2(value) {
|
|
|
16795
17028
|
function hasOwn(record2, key) {
|
|
16796
17029
|
return Object.prototype.hasOwnProperty.call(record2, key);
|
|
16797
17030
|
}
|
|
16798
|
-
function
|
|
17031
|
+
function isRecord3(value) {
|
|
16799
17032
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16800
17033
|
}
|
|
16801
17034
|
var EDIT_COMPAT_REPAIR_ENABLED, ARG_KEY_ALIASES;
|
|
@@ -16858,7 +17091,7 @@ async function handleToolLoopEventLegacy(options) {
|
|
|
16858
17091
|
const extraction = toolLoopMode === "opencode" ? extractOpenAiToolCall(event, allowedToolNames) : { action: "skip", skipReason: "tool_loop_mode_not_opencode" };
|
|
16859
17092
|
if (extraction.action === "passthrough") {
|
|
16860
17093
|
passThroughTracker?.trackTool(extraction.passthroughName);
|
|
16861
|
-
|
|
17094
|
+
log14.debug("MCP tool passed through to cursor-agent (legacy)", {
|
|
16862
17095
|
tool: extraction.passthroughName
|
|
16863
17096
|
});
|
|
16864
17097
|
return { intercepted: false, skipConverter: false };
|
|
@@ -16882,7 +17115,7 @@ async function handleToolLoopEventLegacy(options) {
|
|
|
16882
17115
|
if (interceptedToolCall) {
|
|
16883
17116
|
const compat2 = applyToolSchemaCompat(interceptedToolCall, toolSchemaMap);
|
|
16884
17117
|
let normalizedToolCall = compat2.toolCall;
|
|
16885
|
-
|
|
17118
|
+
log14.debug("Applied tool schema compatibility (legacy)", {
|
|
16886
17119
|
tool: normalizedToolCall.function.name,
|
|
16887
17120
|
originalArgKeys: compat2.originalArgKeys,
|
|
16888
17121
|
normalizedArgKeys: compat2.normalizedArgKeys,
|
|
@@ -16896,7 +17129,7 @@ async function handleToolLoopEventLegacy(options) {
|
|
|
16896
17129
|
}
|
|
16897
17130
|
const reroutedWrite = tryRerouteEditToWrite(normalizedToolCall, compat2.normalizedArgs, allowedToolNames, toolSchemaMap);
|
|
16898
17131
|
if (reroutedWrite) {
|
|
16899
|
-
|
|
17132
|
+
log14.debug("Rerouting malformed edit call to write (legacy)", {
|
|
16900
17133
|
path: reroutedWrite.path,
|
|
16901
17134
|
missing: compat2.validation.missing,
|
|
16902
17135
|
typeErrors: compat2.validation.typeErrors
|
|
@@ -16904,7 +17137,7 @@ async function handleToolLoopEventLegacy(options) {
|
|
|
16904
17137
|
normalizedToolCall = reroutedWrite.toolCall;
|
|
16905
17138
|
} else if (shouldEmitNonFatalSchemaValidationHint(normalizedToolCall, compat2.validation)) {
|
|
16906
17139
|
const hintChunk = createNonFatalSchemaValidationHintChunk(responseMeta, normalizedToolCall, compat2.validation);
|
|
16907
|
-
|
|
17140
|
+
log14.debug("Emitting non-fatal schema validation hint in legacy and skipping malformed tool execution", {
|
|
16908
17141
|
tool: normalizedToolCall.function.name,
|
|
16909
17142
|
missing: compat2.validation.missing,
|
|
16910
17143
|
typeErrors: compat2.validation.typeErrors
|
|
@@ -16966,7 +17199,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
16966
17199
|
}
|
|
16967
17200
|
if (extraction.action === "passthrough") {
|
|
16968
17201
|
passThroughTracker?.trackTool(extraction.passthroughName);
|
|
16969
|
-
|
|
17202
|
+
log14.debug("MCP tool passed through to cursor-agent (v1)", {
|
|
16970
17203
|
tool: extraction.passthroughName
|
|
16971
17204
|
});
|
|
16972
17205
|
return { intercepted: false, skipConverter: false };
|
|
@@ -16993,7 +17226,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
16993
17226
|
rawArgs: safeArgTypeSummary(event),
|
|
16994
17227
|
normalizedArgs: compat2.normalizedArgs
|
|
16995
17228
|
} : undefined;
|
|
16996
|
-
|
|
17229
|
+
log14.debug("Applied tool schema compatibility", {
|
|
16997
17230
|
tool: normalizedToolCall.function.name,
|
|
16998
17231
|
originalArgKeys: compat2.originalArgKeys,
|
|
16999
17232
|
normalizedArgKeys: compat2.normalizedArgKeys,
|
|
@@ -17002,7 +17235,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
17002
17235
|
...editDiag ? { editDiag } : {}
|
|
17003
17236
|
});
|
|
17004
17237
|
if (compat2.validation.hasSchema && !compat2.validation.ok) {
|
|
17005
|
-
|
|
17238
|
+
log14.debug("Tool schema compatibility validation failed", {
|
|
17006
17239
|
tool: normalizedToolCall.function.name,
|
|
17007
17240
|
missing: compat2.validation.missing,
|
|
17008
17241
|
unexpected: compat2.validation.unexpected,
|
|
@@ -17019,7 +17252,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
17019
17252
|
}
|
|
17020
17253
|
const reroutedWrite = tryRerouteEditToWrite(normalizedToolCall, compat2.normalizedArgs, allowedToolNames, toolSchemaMap);
|
|
17021
17254
|
if (reroutedWrite) {
|
|
17022
|
-
|
|
17255
|
+
log14.debug("Rerouting malformed edit call to write", {
|
|
17023
17256
|
path: reroutedWrite.path,
|
|
17024
17257
|
missing: compat2.validation.missing,
|
|
17025
17258
|
typeErrors: compat2.validation.typeErrors
|
|
@@ -17039,7 +17272,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
17039
17272
|
}
|
|
17040
17273
|
if (schemaValidationFailureMode === "pass_through" && shouldEmitNonFatalSchemaValidationHint(normalizedToolCall, compat2.validation)) {
|
|
17041
17274
|
const hintChunk = createNonFatalSchemaValidationHintChunk(responseMeta, normalizedToolCall, compat2.validation);
|
|
17042
|
-
|
|
17275
|
+
log14.debug("Emitting non-fatal schema validation hint and skipping malformed tool execution", {
|
|
17043
17276
|
tool: normalizedToolCall.function.name,
|
|
17044
17277
|
missing: compat2.validation.missing,
|
|
17045
17278
|
typeErrors: compat2.validation.typeErrors
|
|
@@ -17057,7 +17290,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
17057
17290
|
terminate: createSchemaValidationTermination(normalizedToolCall, compat2.validation)
|
|
17058
17291
|
};
|
|
17059
17292
|
}
|
|
17060
|
-
|
|
17293
|
+
log14.debug("Forwarding schema-invalid tool call to OpenCode loop", {
|
|
17061
17294
|
tool: normalizedToolCall.function.name,
|
|
17062
17295
|
repairHint: compat2.validation.repairHint
|
|
17063
17296
|
});
|
|
@@ -17120,7 +17353,7 @@ function evaluateToolLoopGuard(toolLoopGuard, toolCall) {
|
|
|
17120
17353
|
if (!decision.triggered) {
|
|
17121
17354
|
return null;
|
|
17122
17355
|
}
|
|
17123
|
-
|
|
17356
|
+
log14.debug("Tool loop guard triggered", {
|
|
17124
17357
|
tool: toolCall.function.name,
|
|
17125
17358
|
fingerprint: decision.fingerprint,
|
|
17126
17359
|
repeatCount: decision.repeatCount,
|
|
@@ -17179,7 +17412,7 @@ function evaluateSchemaValidationLoopGuard(toolLoopGuard, toolCall, validation)
|
|
|
17179
17412
|
if (!decision.tracked || !decision.triggered) {
|
|
17180
17413
|
return null;
|
|
17181
17414
|
}
|
|
17182
|
-
|
|
17415
|
+
log14.warn("Tool loop guard triggered on schema validation", {
|
|
17183
17416
|
tool: toolCall.function.name,
|
|
17184
17417
|
fingerprint: decision.fingerprint,
|
|
17185
17418
|
repeatCount: decision.repeatCount,
|
|
@@ -17255,11 +17488,11 @@ function safeArgTypeSummary(event) {
|
|
|
17255
17488
|
try {
|
|
17256
17489
|
let raw;
|
|
17257
17490
|
const toolCallPayload = event?.tool_call;
|
|
17258
|
-
if (
|
|
17491
|
+
if (isRecord4(toolCallPayload)) {
|
|
17259
17492
|
const entries = Object.entries(toolCallPayload);
|
|
17260
17493
|
if (entries.length > 0) {
|
|
17261
17494
|
const [, payload] = entries[0];
|
|
17262
|
-
if (
|
|
17495
|
+
if (isRecord4(payload)) {
|
|
17263
17496
|
raw = payload.args;
|
|
17264
17497
|
if (raw === undefined) {
|
|
17265
17498
|
const { result: _result, ...rest } = payload;
|
|
@@ -17294,7 +17527,7 @@ function safeArgTypeSummary(event) {
|
|
|
17294
17527
|
}
|
|
17295
17528
|
function shouldUsePassThroughForEditSchema(event) {
|
|
17296
17529
|
const toolCallPayload = event?.tool_call;
|
|
17297
|
-
if (!
|
|
17530
|
+
if (!isRecord4(toolCallPayload)) {
|
|
17298
17531
|
return false;
|
|
17299
17532
|
}
|
|
17300
17533
|
const keys = Object.keys(toolCallPayload);
|
|
@@ -17335,15 +17568,15 @@ function tryRerouteEditToWrite(toolCall, normalizedArgs, allowedToolNames, toolS
|
|
|
17335
17568
|
}
|
|
17336
17569
|
};
|
|
17337
17570
|
}
|
|
17338
|
-
function
|
|
17571
|
+
function isRecord4(value) {
|
|
17339
17572
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17340
17573
|
}
|
|
17341
|
-
var
|
|
17574
|
+
var log14, ToolBoundaryExtractionError;
|
|
17342
17575
|
var init_runtime_interception = __esm(() => {
|
|
17343
17576
|
init_tool_loop();
|
|
17344
17577
|
init_logger();
|
|
17345
17578
|
init_tool_schema_compat();
|
|
17346
|
-
|
|
17579
|
+
log14 = createLogger("provider:runtime-interception");
|
|
17347
17580
|
ToolBoundaryExtractionError = class ToolBoundaryExtractionError extends Error {
|
|
17348
17581
|
cause;
|
|
17349
17582
|
constructor(message, cause) {
|
|
@@ -17385,7 +17618,7 @@ class ToastService {
|
|
|
17385
17618
|
}
|
|
17386
17619
|
async show(options) {
|
|
17387
17620
|
if (!this.client?.tui?.showToast) {
|
|
17388
|
-
|
|
17621
|
+
log15.debug("Toast not available; client.tui.showToast missing", { message: options.message });
|
|
17389
17622
|
return;
|
|
17390
17623
|
}
|
|
17391
17624
|
try {
|
|
@@ -17397,7 +17630,7 @@ class ToastService {
|
|
|
17397
17630
|
}
|
|
17398
17631
|
});
|
|
17399
17632
|
} catch (error45) {
|
|
17400
|
-
|
|
17633
|
+
log15.debug("Toast failed", { error: error45, message: options.message });
|
|
17401
17634
|
}
|
|
17402
17635
|
}
|
|
17403
17636
|
async showPassThroughSummary(tools) {
|
|
@@ -17421,10 +17654,10 @@ class ToastService {
|
|
|
17421
17654
|
});
|
|
17422
17655
|
}
|
|
17423
17656
|
}
|
|
17424
|
-
var
|
|
17657
|
+
var log15, toastService;
|
|
17425
17658
|
var init_toast_service = __esm(() => {
|
|
17426
17659
|
init_logger();
|
|
17427
|
-
|
|
17660
|
+
log15 = createLogger("services:toast");
|
|
17428
17661
|
toastService = new ToastService;
|
|
17429
17662
|
});
|
|
17430
17663
|
|
|
@@ -17522,7 +17755,7 @@ function indexToolResultErrorClasses(messages) {
|
|
|
17522
17755
|
const byCallId = new Map;
|
|
17523
17756
|
let latest = null;
|
|
17524
17757
|
for (const message of messages) {
|
|
17525
|
-
if (!
|
|
17758
|
+
if (!isRecord5(message) || message.role !== "tool") {
|
|
17526
17759
|
continue;
|
|
17527
17760
|
}
|
|
17528
17761
|
const errorClass = classifyToolResult(message.content);
|
|
@@ -17645,7 +17878,7 @@ function deriveSuccessCoarseFingerprint(toolName, rawArguments) {
|
|
|
17645
17878
|
}
|
|
17646
17879
|
try {
|
|
17647
17880
|
const parsed = JSON.parse(rawArguments);
|
|
17648
|
-
if (!
|
|
17881
|
+
if (!isRecord5(parsed)) {
|
|
17649
17882
|
return null;
|
|
17650
17883
|
}
|
|
17651
17884
|
const path2 = typeof parsed.path === "string" ? parsed.path : "";
|
|
@@ -17666,15 +17899,15 @@ function deriveSuccessCoarseFingerprint(toolName, rawArguments) {
|
|
|
17666
17899
|
function extractAssistantToolCalls(messages) {
|
|
17667
17900
|
const calls = [];
|
|
17668
17901
|
for (const message of messages) {
|
|
17669
|
-
if (!
|
|
17902
|
+
if (!isRecord5(message) || message.role !== "assistant" || !Array.isArray(message.tool_calls)) {
|
|
17670
17903
|
continue;
|
|
17671
17904
|
}
|
|
17672
17905
|
for (const call of message.tool_calls) {
|
|
17673
|
-
if (!
|
|
17906
|
+
if (!isRecord5(call)) {
|
|
17674
17907
|
continue;
|
|
17675
17908
|
}
|
|
17676
17909
|
const id = typeof call.id === "string" ? call.id : "";
|
|
17677
|
-
const fn =
|
|
17910
|
+
const fn = isRecord5(call.function) ? call.function : null;
|
|
17678
17911
|
const name = fn && typeof fn.name === "string" ? fn.name : "";
|
|
17679
17912
|
const rawArguments = fn && typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn?.arguments ?? {});
|
|
17680
17913
|
if (!id || !name) {
|
|
@@ -17695,7 +17928,7 @@ function extractAssistantToolCalls(messages) {
|
|
|
17695
17928
|
function extractArgumentKeys(rawArguments) {
|
|
17696
17929
|
try {
|
|
17697
17930
|
const parsed = JSON.parse(rawArguments);
|
|
17698
|
-
if (!
|
|
17931
|
+
if (!isRecord5(parsed)) {
|
|
17699
17932
|
return [];
|
|
17700
17933
|
}
|
|
17701
17934
|
return Object.keys(parsed);
|
|
@@ -17767,7 +18000,7 @@ function shapeOf(value) {
|
|
|
17767
18000
|
}
|
|
17768
18001
|
return [shapeOf(value[0])];
|
|
17769
18002
|
}
|
|
17770
|
-
if (
|
|
18003
|
+
if (isRecord5(value)) {
|
|
17771
18004
|
const shaped = {};
|
|
17772
18005
|
for (const key of Object.keys(value).sort()) {
|
|
17773
18006
|
shaped[key] = shapeOf(value[key]);
|
|
@@ -17783,7 +18016,7 @@ function canonicalizeValue(value) {
|
|
|
17783
18016
|
if (Array.isArray(value)) {
|
|
17784
18017
|
return value.map((entry) => canonicalizeValue(entry));
|
|
17785
18018
|
}
|
|
17786
|
-
if (
|
|
18019
|
+
if (isRecord5(value)) {
|
|
17787
18020
|
const canonical = {};
|
|
17788
18021
|
for (const key of Object.keys(value).sort()) {
|
|
17789
18022
|
canonical[key] = canonicalizeValue(value[key]);
|
|
@@ -17819,7 +18052,7 @@ function renderContent(content) {
|
|
|
17819
18052
|
if (typeof part === "string") {
|
|
17820
18053
|
return part;
|
|
17821
18054
|
}
|
|
17822
|
-
if (
|
|
18055
|
+
if (isRecord5(part) && typeof part.text === "string") {
|
|
17823
18056
|
return part.text;
|
|
17824
18057
|
}
|
|
17825
18058
|
return JSON.stringify(part);
|
|
@@ -17833,7 +18066,7 @@ function renderContent(content) {
|
|
|
17833
18066
|
function containsAny(text, patterns) {
|
|
17834
18067
|
return patterns.some((pattern) => text.includes(pattern));
|
|
17835
18068
|
}
|
|
17836
|
-
function
|
|
18069
|
+
function isRecord5(value) {
|
|
17837
18070
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17838
18071
|
}
|
|
17839
18072
|
var UNKNOWN_AS_SUCCESS_TOOLS, EXPLORATION_TOOLS, COARSE_LIMIT_MULTIPLIER = 3, EXPLORATION_LIMIT_MULTIPLIER = 5;
|
|
@@ -17878,13 +18111,13 @@ __export(exports_plugin, {
|
|
|
17878
18111
|
default: () => plugin_default,
|
|
17879
18112
|
CursorPlugin: () => CursorPlugin
|
|
17880
18113
|
});
|
|
17881
|
-
import { appendFileSync as appendFileSync3, existsSync as
|
|
18114
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync5, realpathSync } from "fs";
|
|
17882
18115
|
import { mkdir } from "fs/promises";
|
|
17883
|
-
import { homedir as
|
|
17884
|
-
import { isAbsolute, join as
|
|
18116
|
+
import { homedir as homedir5 } from "os";
|
|
18117
|
+
import { isAbsolute, join as join5, relative, resolve as resolve2 } from "path";
|
|
17885
18118
|
function ensureDebugLogDir() {
|
|
17886
18119
|
try {
|
|
17887
|
-
if (!
|
|
18120
|
+
if (!existsSync5(DEBUG_LOG_DIR2)) {
|
|
17888
18121
|
mkdir(DEBUG_LOG_DIR2, { recursive: true }).catch(() => {});
|
|
17889
18122
|
}
|
|
17890
18123
|
} catch {}
|
|
@@ -17899,13 +18132,13 @@ function debugLogToFile2(message, data) {
|
|
|
17899
18132
|
} catch {}
|
|
17900
18133
|
}
|
|
17901
18134
|
async function ensurePluginDirectory() {
|
|
17902
|
-
const configHome = process.env.XDG_CONFIG_HOME ?
|
|
17903
|
-
const pluginDir =
|
|
18135
|
+
const configHome = process.env.XDG_CONFIG_HOME ? resolve2(process.env.XDG_CONFIG_HOME) : join5(homedir5(), ".config");
|
|
18136
|
+
const pluginDir = join5(configHome, "opencode", "plugin");
|
|
17904
18137
|
try {
|
|
17905
18138
|
await mkdir(pluginDir, { recursive: true });
|
|
17906
|
-
|
|
18139
|
+
log16.debug("Plugin directory ensured", { path: pluginDir });
|
|
17907
18140
|
} catch (error45) {
|
|
17908
|
-
|
|
18141
|
+
log16.warn("Failed to create plugin directory", { error: String(error45) });
|
|
17909
18142
|
}
|
|
17910
18143
|
}
|
|
17911
18144
|
function shouldProcessModel(model) {
|
|
@@ -17917,11 +18150,11 @@ function getGlobalKey() {
|
|
|
17917
18150
|
return "__opencode_cursor_proxy_server__";
|
|
17918
18151
|
}
|
|
17919
18152
|
function getOpenCodeConfigPrefix() {
|
|
17920
|
-
const configHome = process.env.XDG_CONFIG_HOME ?
|
|
17921
|
-
return
|
|
18153
|
+
const configHome = process.env.XDG_CONFIG_HOME ? resolve2(process.env.XDG_CONFIG_HOME) : join5(homedir5(), ".config");
|
|
18154
|
+
return join5(configHome, "opencode");
|
|
17922
18155
|
}
|
|
17923
18156
|
function canonicalizePathForCompare(pathValue) {
|
|
17924
|
-
const resolvedPath =
|
|
18157
|
+
const resolvedPath = resolve2(pathValue);
|
|
17925
18158
|
let normalizedPath = resolvedPath;
|
|
17926
18159
|
try {
|
|
17927
18160
|
normalizedPath = typeof realpathSync.native === "function" ? realpathSync.native(resolvedPath) : realpathSync(resolvedPath);
|
|
@@ -17943,7 +18176,7 @@ function resolveCandidate(value) {
|
|
|
17943
18176
|
if (!value || value.trim().length === 0) {
|
|
17944
18177
|
return "";
|
|
17945
18178
|
}
|
|
17946
|
-
return
|
|
18179
|
+
return resolve2(value);
|
|
17947
18180
|
}
|
|
17948
18181
|
function isNonConfigPath(pathValue) {
|
|
17949
18182
|
if (!pathValue) {
|
|
@@ -17954,11 +18187,11 @@ function isNonConfigPath(pathValue) {
|
|
|
17954
18187
|
function resolveWorkspaceDirectory(worktree, directory) {
|
|
17955
18188
|
const envWorkspace = process.env.CURSOR_ACP_WORKSPACE?.trim();
|
|
17956
18189
|
if (envWorkspace) {
|
|
17957
|
-
return
|
|
18190
|
+
return resolve2(envWorkspace);
|
|
17958
18191
|
}
|
|
17959
18192
|
const envProjectDir = process.env.OPENCODE_CURSOR_PROJECT_DIR?.trim();
|
|
17960
18193
|
if (envProjectDir) {
|
|
17961
|
-
return
|
|
18194
|
+
return resolve2(envProjectDir);
|
|
17962
18195
|
}
|
|
17963
18196
|
const configPrefix = getOpenCodeConfigPrefix();
|
|
17964
18197
|
const worktreeCandidate = resolveCandidate(worktree);
|
|
@@ -17969,14 +18202,14 @@ function resolveWorkspaceDirectory(worktree, directory) {
|
|
|
17969
18202
|
if (dirCandidate && !isWithinPath(configPrefix, dirCandidate)) {
|
|
17970
18203
|
return dirCandidate;
|
|
17971
18204
|
}
|
|
17972
|
-
const cwd =
|
|
18205
|
+
const cwd = resolve2(process.cwd());
|
|
17973
18206
|
if (cwd && !isWithinPath(configPrefix, cwd)) {
|
|
17974
18207
|
return cwd;
|
|
17975
18208
|
}
|
|
17976
18209
|
return dirCandidate || cwd || configPrefix;
|
|
17977
18210
|
}
|
|
17978
18211
|
function normalizeWorkspaceForCompare(pathValue) {
|
|
17979
|
-
return
|
|
18212
|
+
return resolve2(pathValue);
|
|
17980
18213
|
}
|
|
17981
18214
|
function isReusableProxyHealthPayload(payload, workspaceDirectory) {
|
|
17982
18215
|
if (!payload || payload.ok !== true) {
|
|
@@ -18093,9 +18326,9 @@ function createBoundaryRuntimeContext(scope) {
|
|
|
18093
18326
|
error: toErrorMessage(error45)
|
|
18094
18327
|
};
|
|
18095
18328
|
if (!fallbackActive) {
|
|
18096
|
-
|
|
18329
|
+
log16.warn("Provider boundary v1 failed; switching to legacy for this request", details);
|
|
18097
18330
|
} else {
|
|
18098
|
-
|
|
18331
|
+
log16.debug("Provider boundary fallback already active", details);
|
|
18099
18332
|
}
|
|
18100
18333
|
fallbackActive = true;
|
|
18101
18334
|
return true;
|
|
@@ -18233,7 +18466,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18233
18466
|
headers: { "Content-Type": "application/json" }
|
|
18234
18467
|
});
|
|
18235
18468
|
} catch (err) {
|
|
18236
|
-
|
|
18469
|
+
log16.error("Failed to list models", { error: String(err) });
|
|
18237
18470
|
return new Response(JSON.stringify({ error: "Failed to fetch models from cursor-agent" }), {
|
|
18238
18471
|
status: 500,
|
|
18239
18472
|
headers: { "Content-Type": "application/json" }
|
|
@@ -18246,7 +18479,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18246
18479
|
headers: { "Content-Type": "application/json" }
|
|
18247
18480
|
});
|
|
18248
18481
|
}
|
|
18249
|
-
|
|
18482
|
+
log16.debug("Proxy request (bun)", { method: req.method, path: url2.pathname });
|
|
18250
18483
|
const body = await req.json().catch(() => ({}));
|
|
18251
18484
|
const messages = Array.isArray(body?.messages) ? body.messages : [];
|
|
18252
18485
|
const stream = body?.stream === true;
|
|
@@ -18273,7 +18506,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18273
18506
|
const clen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr${m.content.length}` : typeof m?.content;
|
|
18274
18507
|
return `${i}:${role}${hasTc ? `(tc:${hasTc})` : ""}(clen:${clen})`;
|
|
18275
18508
|
});
|
|
18276
|
-
|
|
18509
|
+
log16.debug("Proxy chat request (bun)", {
|
|
18277
18510
|
stream,
|
|
18278
18511
|
model,
|
|
18279
18512
|
messages: messages.length,
|
|
@@ -18319,7 +18552,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18319
18552
|
const stdout = (stdoutText || "").trim();
|
|
18320
18553
|
const stderr = (stderrText || "").trim();
|
|
18321
18554
|
const exitCode = await child.exited;
|
|
18322
|
-
|
|
18555
|
+
log16.debug("cursor-agent completed (bun non-stream)", {
|
|
18323
18556
|
exitCode,
|
|
18324
18557
|
stdoutChars: stdout.length,
|
|
18325
18558
|
stderrChars: stderr.length
|
|
@@ -18345,7 +18578,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18345
18578
|
});
|
|
18346
18579
|
}
|
|
18347
18580
|
if (intercepted.toolCall) {
|
|
18348
|
-
|
|
18581
|
+
log16.debug("Intercepted OpenCode tool call (non-stream)", {
|
|
18349
18582
|
name: intercepted.toolCall.function.name,
|
|
18350
18583
|
callId: intercepted.toolCall.id
|
|
18351
18584
|
});
|
|
@@ -18359,7 +18592,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18359
18592
|
const errSource = stderr || stdout || `cursor-agent exited with code ${String(exitCode ?? "unknown")} and no output`;
|
|
18360
18593
|
const parsed = parseAgentError(errSource);
|
|
18361
18594
|
const userError = formatErrorForUser(parsed);
|
|
18362
|
-
|
|
18595
|
+
log16.error("cursor-cli failed", {
|
|
18363
18596
|
type: parsed.type,
|
|
18364
18597
|
message: parsed.message,
|
|
18365
18598
|
code: exitCode
|
|
@@ -18394,7 +18627,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18394
18627
|
const converter = new StreamToSseConverter(model, { id, created });
|
|
18395
18628
|
const lineBuffer = new LineBuffer;
|
|
18396
18629
|
const emitToolCallAndTerminate = (toolCall) => {
|
|
18397
|
-
|
|
18630
|
+
log16.debug("Intercepted OpenCode tool call (stream)", {
|
|
18398
18631
|
name: toolCall.function.name,
|
|
18399
18632
|
callId: toolCall.id
|
|
18400
18633
|
});
|
|
@@ -18576,7 +18809,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18576
18809
|
const errSource = (stderrText || "").trim() || `cursor-agent exited with code ${String(exitCode ?? "unknown")} and no output`;
|
|
18577
18810
|
const parsed = parseAgentError(errSource);
|
|
18578
18811
|
const msg = formatErrorForUser(parsed);
|
|
18579
|
-
|
|
18812
|
+
log16.error("cursor-cli streaming failed", {
|
|
18580
18813
|
type: parsed.type,
|
|
18581
18814
|
code: exitCode
|
|
18582
18815
|
});
|
|
@@ -18587,7 +18820,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18587
18820
|
controller.enqueue(encoder.encode(formatSseDone()));
|
|
18588
18821
|
return;
|
|
18589
18822
|
}
|
|
18590
|
-
|
|
18823
|
+
log16.debug("cursor-agent completed (bun stream)", {
|
|
18591
18824
|
exitCode
|
|
18592
18825
|
});
|
|
18593
18826
|
const passThroughSummary = passThroughTracker.getSummary();
|
|
@@ -18669,7 +18902,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18669
18902
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
18670
18903
|
res.end(JSON.stringify({ object: "list", data: models }));
|
|
18671
18904
|
} catch (err) {
|
|
18672
|
-
|
|
18905
|
+
log16.error("Failed to list models", { error: String(err) });
|
|
18673
18906
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
18674
18907
|
res.end(JSON.stringify({ error: "Failed to fetch models" }));
|
|
18675
18908
|
}
|
|
@@ -18680,7 +18913,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18680
18913
|
res.end(JSON.stringify({ error: `Unsupported path: ${url2.pathname}` }));
|
|
18681
18914
|
return;
|
|
18682
18915
|
}
|
|
18683
|
-
|
|
18916
|
+
log16.debug("Proxy request (node)", { method: req.method, path: url2.pathname });
|
|
18684
18917
|
let body = "";
|
|
18685
18918
|
for await (const chunk of req) {
|
|
18686
18919
|
body += chunk;
|
|
@@ -18703,7 +18936,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18703
18936
|
const contentLen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr${m.content.length}` : typeof m?.content;
|
|
18704
18937
|
return `${i}:${role}${hasTc ? `(tc:${hasTc})` : ""}${role === "tool" ? `(tcid:${tcId},name:${tcName},clen:${contentLen})` : `(clen:${contentLen})`}`;
|
|
18705
18938
|
});
|
|
18706
|
-
|
|
18939
|
+
log16.debug("Proxy chat request (node)", {
|
|
18707
18940
|
stream,
|
|
18708
18941
|
model,
|
|
18709
18942
|
messages: messages.length,
|
|
@@ -18734,14 +18967,14 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18734
18967
|
let spawnErrorText = null;
|
|
18735
18968
|
child.on("error", (error45) => {
|
|
18736
18969
|
spawnErrorText = String(error45?.message || error45);
|
|
18737
|
-
|
|
18970
|
+
log16.error("Failed to spawn cursor-agent", { error: spawnErrorText, model });
|
|
18738
18971
|
});
|
|
18739
18972
|
child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
18740
18973
|
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
18741
18974
|
child.on("close", async (code) => {
|
|
18742
18975
|
const stdout = Buffer.concat(stdoutChunks).toString().trim();
|
|
18743
18976
|
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
18744
|
-
|
|
18977
|
+
log16.debug("cursor-agent completed (node non-stream)", {
|
|
18745
18978
|
code,
|
|
18746
18979
|
stdoutChars: stdout.length,
|
|
18747
18980
|
stderrChars: stderr.length,
|
|
@@ -18767,7 +19000,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18767
19000
|
return;
|
|
18768
19001
|
}
|
|
18769
19002
|
if (intercepted.toolCall) {
|
|
18770
|
-
|
|
19003
|
+
log16.debug("Intercepted OpenCode tool call (non-stream)", {
|
|
18771
19004
|
name: intercepted.toolCall.function.name,
|
|
18772
19005
|
callId: intercepted.toolCall.id
|
|
18773
19006
|
});
|
|
@@ -18781,7 +19014,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18781
19014
|
const errSource = stderr || stdout || spawnErrorText || `cursor-agent exited with code ${String(code ?? "unknown")} and no output`;
|
|
18782
19015
|
const parsed = parseAgentError(errSource);
|
|
18783
19016
|
const userError = formatErrorForUser(parsed);
|
|
18784
|
-
|
|
19017
|
+
log16.error("cursor-cli failed", {
|
|
18785
19018
|
type: parsed.type,
|
|
18786
19019
|
message: parsed.message,
|
|
18787
19020
|
code
|
|
@@ -18821,7 +19054,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18821
19054
|
return;
|
|
18822
19055
|
}
|
|
18823
19056
|
const errSource = String(error45?.message || error45);
|
|
18824
|
-
|
|
19057
|
+
log16.error("Failed to spawn cursor-agent (stream)", { error: errSource, model });
|
|
18825
19058
|
const parsed = parseAgentError(errSource);
|
|
18826
19059
|
const msg = formatErrorForUser(parsed);
|
|
18827
19060
|
const errChunk = createChatCompletionChunk(id, created, model, msg, true);
|
|
@@ -18836,7 +19069,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18836
19069
|
if (streamTerminated || res.writableEnded) {
|
|
18837
19070
|
return;
|
|
18838
19071
|
}
|
|
18839
|
-
|
|
19072
|
+
log16.debug("Intercepted OpenCode tool call (stream)", {
|
|
18840
19073
|
name: toolCall.function.name,
|
|
18841
19074
|
callId: toolCall.id
|
|
18842
19075
|
});
|
|
@@ -19020,7 +19253,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
19020
19253
|
perf.mark("request:done");
|
|
19021
19254
|
perf.summarize();
|
|
19022
19255
|
const stderrText = Buffer.concat(stderrChunks).toString().trim();
|
|
19023
|
-
|
|
19256
|
+
log16.debug("cursor-agent completed (node stream)", {
|
|
19024
19257
|
code,
|
|
19025
19258
|
stderrChars: stderrText.length
|
|
19026
19259
|
});
|
|
@@ -19072,8 +19305,8 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
19072
19305
|
};
|
|
19073
19306
|
let server2 = http.createServer(requestHandler);
|
|
19074
19307
|
try {
|
|
19075
|
-
await new Promise((
|
|
19076
|
-
server2.listen(CURSOR_PROXY_DEFAULT_PORT, CURSOR_PROXY_HOST, () =>
|
|
19308
|
+
await new Promise((resolve3, reject) => {
|
|
19309
|
+
server2.listen(CURSOR_PROXY_DEFAULT_PORT, CURSOR_PROXY_HOST, () => resolve3());
|
|
19077
19310
|
server2.once("error", reject);
|
|
19078
19311
|
});
|
|
19079
19312
|
const baseURL = `http://${CURSOR_PROXY_HOST}:${CURSOR_PROXY_DEFAULT_PORT}/v1`;
|
|
@@ -19098,8 +19331,8 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
19098
19331
|
} catch {}
|
|
19099
19332
|
}
|
|
19100
19333
|
server2 = http.createServer(requestHandler);
|
|
19101
|
-
await new Promise((
|
|
19102
|
-
server2.listen(0, CURSOR_PROXY_HOST, () =>
|
|
19334
|
+
await new Promise((resolve3, reject) => {
|
|
19335
|
+
server2.listen(0, CURSOR_PROXY_HOST, () => resolve3());
|
|
19103
19336
|
server2.once("error", reject);
|
|
19104
19337
|
});
|
|
19105
19338
|
const addr = server2.address();
|
|
@@ -19201,7 +19434,7 @@ function toAbsoluteWithBase(value, baseDir) {
|
|
|
19201
19434
|
if (trimmed.length === 0 || isAbsolute(trimmed)) {
|
|
19202
19435
|
return value;
|
|
19203
19436
|
}
|
|
19204
|
-
return
|
|
19437
|
+
return resolve2(baseDir, trimmed);
|
|
19205
19438
|
}
|
|
19206
19439
|
function applyToolContextDefaults(toolName, rawArgs, context, fallbackBaseDir, sessionWorkspaceBySession) {
|
|
19207
19440
|
const baseDir = resolveToolContextBaseDirWithSession(context, fallbackBaseDir, sessionWorkspaceBySession);
|
|
@@ -19248,7 +19481,7 @@ function buildToolHookEntries(registry2, fallbackBaseDir) {
|
|
|
19248
19481
|
const normalizedArgs = applyToolContextDefaults(toolName, args, context, fallbackBaseDir, sessionWorkspaceBySession);
|
|
19249
19482
|
return await handler(normalizedArgs);
|
|
19250
19483
|
} catch (error45) {
|
|
19251
|
-
|
|
19484
|
+
log16.debug("Tool hook execution failed", { tool: toolName, error: String(error45?.message || error45) });
|
|
19252
19485
|
throw error45;
|
|
19253
19486
|
}
|
|
19254
19487
|
}
|
|
@@ -19260,9 +19493,9 @@ function buildToolHookEntries(registry2, fallbackBaseDir) {
|
|
|
19260
19493
|
}
|
|
19261
19494
|
return entries;
|
|
19262
19495
|
}
|
|
19263
|
-
var
|
|
19496
|
+
var log16, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp", CURSOR_PROVIDER_PREFIX, CURSOR_PROXY_HOST = "127.0.0.1", CURSOR_PROXY_DEFAULT_PORT = 32124, CURSOR_PROXY_DEFAULT_BASE_URL, REUSE_EXISTING_PROXY, SESSION_WORKSPACE_CACHE_LIMIT = 200, FORCE_TOOL_MODE, EMIT_TOOL_UPDATES, FORWARD_TOOL_CALLS, TOOL_LOOP_MODE_RAW, TOOL_LOOP_MODE, TOOL_LOOP_MODE_VALID, PROVIDER_BOUNDARY_MODE_RAW, PROVIDER_BOUNDARY_MODE, PROVIDER_BOUNDARY_MODE_VALID, LEGACY_PROVIDER_BOUNDARY, PROVIDER_BOUNDARY, ENABLE_PROVIDER_BOUNDARY_AUTOFALLBACK, TOOL_LOOP_MAX_REPEAT_RAW, TOOL_LOOP_MAX_REPEAT, TOOL_LOOP_MAX_REPEAT_VALID, PROXY_EXECUTE_TOOL_CALLS, SUPPRESS_CONVERTER_TOOL_EVENTS, SHOULD_EMIT_TOOL_UPDATES, CursorPlugin = async ({ $, directory, worktree, client: client3, serverUrl }) => {
|
|
19264
19497
|
const workspaceDirectory = resolveWorkspaceDirectory(worktree, directory);
|
|
19265
|
-
|
|
19498
|
+
log16.debug("Plugin initializing", {
|
|
19266
19499
|
directory,
|
|
19267
19500
|
worktree,
|
|
19268
19501
|
workspaceDirectory,
|
|
@@ -19270,22 +19503,22 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19270
19503
|
serverUrl: serverUrl?.toString()
|
|
19271
19504
|
});
|
|
19272
19505
|
if (!TOOL_LOOP_MODE_VALID) {
|
|
19273
|
-
|
|
19506
|
+
log16.warn("Invalid CURSOR_ACP_TOOL_LOOP_MODE; defaulting to opencode", { value: TOOL_LOOP_MODE_RAW });
|
|
19274
19507
|
}
|
|
19275
19508
|
if (!PROVIDER_BOUNDARY_MODE_VALID) {
|
|
19276
|
-
|
|
19509
|
+
log16.warn("Invalid CURSOR_ACP_PROVIDER_BOUNDARY; defaulting to v1", {
|
|
19277
19510
|
value: PROVIDER_BOUNDARY_MODE_RAW
|
|
19278
19511
|
});
|
|
19279
19512
|
}
|
|
19280
19513
|
if (!TOOL_LOOP_MAX_REPEAT_VALID) {
|
|
19281
|
-
|
|
19514
|
+
log16.warn("Invalid CURSOR_ACP_TOOL_LOOP_MAX_REPEAT; defaulting to 3", {
|
|
19282
19515
|
value: TOOL_LOOP_MAX_REPEAT_RAW
|
|
19283
19516
|
});
|
|
19284
19517
|
}
|
|
19285
19518
|
if (ENABLE_PROVIDER_BOUNDARY_AUTOFALLBACK && PROVIDER_BOUNDARY.mode !== "v1") {
|
|
19286
|
-
|
|
19519
|
+
log16.debug("Provider boundary auto-fallback is enabled but inactive unless mode=v1");
|
|
19287
19520
|
}
|
|
19288
|
-
|
|
19521
|
+
log16.info("Tool loop mode configured", {
|
|
19289
19522
|
mode: TOOL_LOOP_MODE,
|
|
19290
19523
|
providerBoundary: PROVIDER_BOUNDARY.mode,
|
|
19291
19524
|
proxyExecToolCalls: PROXY_EXECUTE_TOOL_CALLS,
|
|
@@ -19293,14 +19526,14 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19293
19526
|
toolLoopMaxRepeat: TOOL_LOOP_MAX_REPEAT
|
|
19294
19527
|
});
|
|
19295
19528
|
await ensurePluginDirectory();
|
|
19296
|
-
|
|
19529
|
+
autoRefreshModels().catch(() => {});
|
|
19297
19530
|
toastService.setClient(client3);
|
|
19298
19531
|
const toolsEnabled = process.env.CURSOR_ACP_ENABLE_OPENCODE_TOOLS !== "false";
|
|
19299
19532
|
const legacyProxyToolPathsEnabled = toolsEnabled && TOOL_LOOP_MODE === "proxy-exec";
|
|
19300
19533
|
if (toolsEnabled && TOOL_LOOP_MODE === "opencode") {
|
|
19301
|
-
|
|
19534
|
+
log16.debug("OpenCode mode active; skipping legacy SDK/MCP discovery and proxy-side tool execution");
|
|
19302
19535
|
} else if (toolsEnabled && TOOL_LOOP_MODE === "off") {
|
|
19303
|
-
|
|
19536
|
+
log16.debug("Tool loop mode off; proxy-side tool execution disabled");
|
|
19304
19537
|
}
|
|
19305
19538
|
const serverClient = legacyProxyToolPathsEnabled ? createOpencodeClient({ baseUrl: serverUrl.toString(), directory: workspaceDirectory }) : null;
|
|
19306
19539
|
const discovery = legacyProxyToolPathsEnabled ? new OpenCodeToolDiscovery(serverClient ?? client3) : null;
|
|
@@ -19352,7 +19585,7 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19352
19585
|
discoveredList = await discovery.listTools();
|
|
19353
19586
|
discoveredList.forEach((t) => toolsByName.set(t.name, t));
|
|
19354
19587
|
} catch (err) {
|
|
19355
|
-
|
|
19588
|
+
log16.debug("Tool discovery failed, using local tools only", { error: String(err) });
|
|
19356
19589
|
}
|
|
19357
19590
|
}
|
|
19358
19591
|
const allTools = [...localTools, ...discoveredList];
|
|
@@ -19382,16 +19615,16 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19382
19615
|
}
|
|
19383
19616
|
lastToolNames = toolEntries.map((e) => e.function.name);
|
|
19384
19617
|
lastToolMap = allTools.map((t) => ({ id: t.id, name: t.name }));
|
|
19385
|
-
|
|
19618
|
+
log16.debug("Tools refreshed", { local: localTools.length, discovered: discoveredList.length, total: toolEntries.length });
|
|
19386
19619
|
return toolEntries;
|
|
19387
19620
|
}
|
|
19388
19621
|
const proxyBaseURL = await ensureCursorProxyServer(workspaceDirectory, router);
|
|
19389
|
-
|
|
19622
|
+
log16.debug("Proxy server started", { baseURL: proxyBaseURL });
|
|
19390
19623
|
const toolHookEntries = buildToolHookEntries(localRegistry, workspaceDirectory);
|
|
19391
19624
|
return {
|
|
19392
19625
|
tool: toolHookEntries,
|
|
19393
19626
|
auth: {
|
|
19394
|
-
provider:
|
|
19627
|
+
provider: CURSOR_PROVIDER_ID2,
|
|
19395
19628
|
async loader(_getAuth) {
|
|
19396
19629
|
return {};
|
|
19397
19630
|
},
|
|
@@ -19401,9 +19634,9 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19401
19634
|
type: "oauth",
|
|
19402
19635
|
async authorize() {
|
|
19403
19636
|
try {
|
|
19404
|
-
|
|
19637
|
+
log16.info("Starting OAuth flow");
|
|
19405
19638
|
const { url: url2, instructions, callback } = await startCursorOAuth();
|
|
19406
|
-
|
|
19639
|
+
log16.debug("Got OAuth URL", { url: url2.substring(0, 50) + "..." });
|
|
19407
19640
|
return {
|
|
19408
19641
|
url: url2,
|
|
19409
19642
|
instructions,
|
|
@@ -19411,7 +19644,7 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19411
19644
|
callback
|
|
19412
19645
|
};
|
|
19413
19646
|
} catch (error45) {
|
|
19414
|
-
|
|
19647
|
+
log16.error("OAuth error", { error: error45 });
|
|
19415
19648
|
throw error45;
|
|
19416
19649
|
}
|
|
19417
19650
|
}
|
|
@@ -19435,10 +19668,10 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19435
19668
|
output.options.tools = resolved.tools;
|
|
19436
19669
|
} else if (resolved.action === "preserve") {
|
|
19437
19670
|
const count = Array.isArray(existingTools) ? existingTools.length : 0;
|
|
19438
|
-
|
|
19671
|
+
log16.debug("Using OpenCode-provided tools from chat.params", { count });
|
|
19439
19672
|
}
|
|
19440
19673
|
} catch (err) {
|
|
19441
|
-
|
|
19674
|
+
log16.debug("Failed to refresh tools", { error: String(err) });
|
|
19442
19675
|
}
|
|
19443
19676
|
}
|
|
19444
19677
|
},
|
|
@@ -19464,6 +19697,7 @@ var init_plugin = __esm(() => {
|
|
|
19464
19697
|
init_discovery();
|
|
19465
19698
|
init_schema();
|
|
19466
19699
|
init_router();
|
|
19700
|
+
init_sync();
|
|
19467
19701
|
init_dist2();
|
|
19468
19702
|
init_local();
|
|
19469
19703
|
init_sdk();
|
|
@@ -19474,10 +19708,10 @@ var init_plugin = __esm(() => {
|
|
|
19474
19708
|
init_toast_service();
|
|
19475
19709
|
init_tool_schema_compat();
|
|
19476
19710
|
init_tool_loop_guard();
|
|
19477
|
-
|
|
19478
|
-
DEBUG_LOG_DIR2 =
|
|
19479
|
-
DEBUG_LOG_FILE2 =
|
|
19480
|
-
CURSOR_PROVIDER_PREFIX = `${
|
|
19711
|
+
log16 = createLogger("plugin");
|
|
19712
|
+
DEBUG_LOG_DIR2 = join5(homedir5(), ".config", "opencode", "logs");
|
|
19713
|
+
DEBUG_LOG_FILE2 = join5(DEBUG_LOG_DIR2, "tool-loop-debug.log");
|
|
19714
|
+
CURSOR_PROVIDER_PREFIX = `${CURSOR_PROVIDER_ID2}/`;
|
|
19481
19715
|
CURSOR_PROXY_DEFAULT_BASE_URL = `http://${CURSOR_PROXY_HOST}:${CURSOR_PROXY_DEFAULT_PORT}/v1`;
|
|
19482
19716
|
REUSE_EXISTING_PROXY = process.env.CURSOR_ACP_REUSE_EXISTING_PROXY !== "false";
|
|
19483
19717
|
FORCE_TOOL_MODE = process.env.CURSOR_ACP_FORCE !== "false";
|
|
@@ -19490,8 +19724,8 @@ var init_plugin = __esm(() => {
|
|
|
19490
19724
|
mode: PROVIDER_BOUNDARY_MODE,
|
|
19491
19725
|
valid: PROVIDER_BOUNDARY_MODE_VALID
|
|
19492
19726
|
} = parseProviderBoundaryMode(PROVIDER_BOUNDARY_MODE_RAW));
|
|
19493
|
-
LEGACY_PROVIDER_BOUNDARY = createProviderBoundary("legacy",
|
|
19494
|
-
PROVIDER_BOUNDARY = PROVIDER_BOUNDARY_MODE === "legacy" ? LEGACY_PROVIDER_BOUNDARY : createProviderBoundary(PROVIDER_BOUNDARY_MODE,
|
|
19727
|
+
LEGACY_PROVIDER_BOUNDARY = createProviderBoundary("legacy", CURSOR_PROVIDER_ID2);
|
|
19728
|
+
PROVIDER_BOUNDARY = PROVIDER_BOUNDARY_MODE === "legacy" ? LEGACY_PROVIDER_BOUNDARY : createProviderBoundary(PROVIDER_BOUNDARY_MODE, CURSOR_PROVIDER_ID2);
|
|
19495
19729
|
ENABLE_PROVIDER_BOUNDARY_AUTOFALLBACK = process.env.CURSOR_ACP_PROVIDER_BOUNDARY_AUTOFALLBACK !== "false";
|
|
19496
19730
|
TOOL_LOOP_MAX_REPEAT_RAW = process.env.CURSOR_ACP_TOOL_LOOP_MAX_REPEAT;
|
|
19497
19731
|
({
|
|
@@ -19573,20 +19807,20 @@ class SimpleCursorClient {
|
|
|
19573
19807
|
child.kill("SIGTERM");
|
|
19574
19808
|
processError = new Error(`Timeout after ${this.config.timeout}ms`);
|
|
19575
19809
|
}, this.config.timeout);
|
|
19576
|
-
const streamEnded = new Promise((
|
|
19810
|
+
const streamEnded = new Promise((resolve3) => {
|
|
19577
19811
|
child.on("close", (code) => {
|
|
19578
19812
|
clearTimeout(timeoutId);
|
|
19579
19813
|
if (code !== 0 && !processError) {
|
|
19580
19814
|
this.log.error("cursor-agent exited with non-zero code", { code });
|
|
19581
19815
|
processError = new Error(`cursor-agent exited with code ${code}`);
|
|
19582
19816
|
}
|
|
19583
|
-
|
|
19817
|
+
resolve3(code);
|
|
19584
19818
|
});
|
|
19585
19819
|
child.on("error", (error45) => {
|
|
19586
19820
|
clearTimeout(timeoutId);
|
|
19587
19821
|
this.log.error("cursor-agent process error", { error: error45.message });
|
|
19588
19822
|
processError = error45;
|
|
19589
|
-
|
|
19823
|
+
resolve3(null);
|
|
19590
19824
|
});
|
|
19591
19825
|
});
|
|
19592
19826
|
for await (const chunk of child.stdout) {
|
|
@@ -19639,7 +19873,7 @@ class SimpleCursorClient {
|
|
|
19639
19873
|
args.push("--resume", resumeId);
|
|
19640
19874
|
}
|
|
19641
19875
|
this.log.debug("Executing prompt", { promptLength: prompt.length, mode, model });
|
|
19642
|
-
return new Promise((
|
|
19876
|
+
return new Promise((resolve3, reject) => {
|
|
19643
19877
|
const child = spawn2(this.config.cursorAgentPath, args, {
|
|
19644
19878
|
cwd,
|
|
19645
19879
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -19678,7 +19912,7 @@ class SimpleCursorClient {
|
|
|
19678
19912
|
}
|
|
19679
19913
|
}
|
|
19680
19914
|
}
|
|
19681
|
-
|
|
19915
|
+
resolve3({
|
|
19682
19916
|
content,
|
|
19683
19917
|
done: true
|
|
19684
19918
|
});
|
|
@@ -19695,11 +19929,24 @@ class SimpleCursorClient {
|
|
|
19695
19929
|
async getAvailableModels() {
|
|
19696
19930
|
return [
|
|
19697
19931
|
{ id: "auto", name: "Cursor Agent Auto" },
|
|
19932
|
+
{ id: "composer-1.5", name: "Composer 1.5" },
|
|
19933
|
+
{ id: "opus-4.6-thinking", name: "Claude 4.6 Opus (Thinking)" },
|
|
19934
|
+
{ id: "opus-4.6", name: "Claude 4.6 Opus" },
|
|
19935
|
+
{ id: "sonnet-4.6", name: "Claude 4.6 Sonnet" },
|
|
19936
|
+
{ id: "sonnet-4.6-thinking", name: "Claude 4.6 Sonnet (Thinking)" },
|
|
19937
|
+
{ id: "opus-4.5", name: "Claude 4.5 Opus" },
|
|
19938
|
+
{ id: "opus-4.5-thinking", name: "Claude 4.5 Opus (Thinking)" },
|
|
19939
|
+
{ id: "sonnet-4.5", name: "Claude 4.5 Sonnet" },
|
|
19940
|
+
{ id: "sonnet-4.5-thinking", name: "Claude 4.5 Sonnet (Thinking)" },
|
|
19941
|
+
{ id: "gpt-5.4-high", name: "GPT-5.4 High" },
|
|
19942
|
+
{ id: "gpt-5.4-medium", name: "GPT-5.4" },
|
|
19943
|
+
{ id: "gpt-5.3-codex", name: "GPT-5.3 Codex" },
|
|
19698
19944
|
{ id: "gpt-5.2", name: "GPT-5.2" },
|
|
19945
|
+
{ id: "gemini-3.1-pro", name: "Gemini 3.1 Pro" },
|
|
19699
19946
|
{ id: "gemini-3-pro", name: "Gemini 3 Pro" },
|
|
19700
|
-
{ id: "
|
|
19701
|
-
{ id: "
|
|
19702
|
-
{ id: "
|
|
19947
|
+
{ id: "gemini-3-flash", name: "Gemini 3 Flash" },
|
|
19948
|
+
{ id: "grok", name: "Grok" },
|
|
19949
|
+
{ id: "kimi-k2.5", name: "Kimi K2.5" }
|
|
19703
19950
|
];
|
|
19704
19951
|
}
|
|
19705
19952
|
async validateInstallation() {
|
|
@@ -19718,19 +19965,19 @@ init_logger();
|
|
|
19718
19965
|
import { execSync } from "node:child_process";
|
|
19719
19966
|
import { createServer } from "node:net";
|
|
19720
19967
|
import { platform as platform2 } from "node:os";
|
|
19721
|
-
var
|
|
19968
|
+
var log17 = createLogger("proxy-server");
|
|
19722
19969
|
var DEFAULT_PORT = 32124;
|
|
19723
19970
|
var PORT_RANGE_SIZE = 256;
|
|
19724
19971
|
async function isPortAvailable(port, host) {
|
|
19725
|
-
return await new Promise((
|
|
19972
|
+
return await new Promise((resolve3) => {
|
|
19726
19973
|
const server2 = createServer();
|
|
19727
19974
|
server2.unref();
|
|
19728
19975
|
server2.once("error", () => {
|
|
19729
|
-
|
|
19976
|
+
resolve3(false);
|
|
19730
19977
|
});
|
|
19731
19978
|
server2.listen({ port, host }, () => {
|
|
19732
19979
|
server2.close(() => {
|
|
19733
|
-
|
|
19980
|
+
resolve3(true);
|
|
19734
19981
|
});
|
|
19735
19982
|
});
|
|
19736
19983
|
});
|
|
@@ -19765,11 +20012,11 @@ function getUsedPortsInRange(minPort, maxPort) {
|
|
|
19765
20012
|
}
|
|
19766
20013
|
}
|
|
19767
20014
|
} else {
|
|
19768
|
-
|
|
20015
|
+
log17.debug(`Port detection not supported on ${os2}. Using probe-based discovery.`);
|
|
19769
20016
|
}
|
|
19770
20017
|
} catch (error45) {
|
|
19771
20018
|
const msg = error45 instanceof Error ? error45.message : String(error45);
|
|
19772
|
-
|
|
20019
|
+
log17.debug(`Port detection failed: ${msg}. Using probe-based discovery.`);
|
|
19773
20020
|
}
|
|
19774
20021
|
return used;
|
|
19775
20022
|
}
|
|
@@ -19820,7 +20067,7 @@ function createProxyServer(config2) {
|
|
|
19820
20067
|
const err = error45 instanceof Error ? error45 : new Error(String(error45));
|
|
19821
20068
|
const isPortInUse = err.message.includes("EADDRINUSE") || err.message.includes("address already in use") || err.message.includes("port is already in use");
|
|
19822
20069
|
if (!isPortInUse) {
|
|
19823
|
-
|
|
20070
|
+
log17.debug(`Unexpected error starting on port ${port}: ${err.message}`);
|
|
19824
20071
|
}
|
|
19825
20072
|
return { success: false, error: err };
|
|
19826
20073
|
}
|
|
@@ -19836,13 +20083,13 @@ function createProxyServer(config2) {
|
|
|
19836
20083
|
if (result.success) {
|
|
19837
20084
|
port = requestedPort;
|
|
19838
20085
|
} else {
|
|
19839
|
-
|
|
20086
|
+
log17.debug(`Requested port ${requestedPort} unavailable: ${result.error?.message ?? "unknown"}. Falling back to automatic port selection.`);
|
|
19840
20087
|
port = await findAvailablePort(host);
|
|
19841
20088
|
const fallbackResult = tryStart(port);
|
|
19842
20089
|
if (!fallbackResult.success) {
|
|
19843
20090
|
throw new Error(`Failed to start server on port ${requestedPort} (${result.error?.message ?? "unknown"}) ` + `and fallback port ${port} (${fallbackResult.error?.message ?? "unknown"})`);
|
|
19844
20091
|
}
|
|
19845
|
-
|
|
20092
|
+
log17.debug(`Server started on fallback port ${port} instead of requested port ${requestedPort}`);
|
|
19846
20093
|
}
|
|
19847
20094
|
} else {
|
|
19848
20095
|
port = await findAvailablePort(host);
|
|
@@ -19884,9 +20131,12 @@ class StreamToAiSdkParts {
|
|
|
19884
20131
|
if (isAssistantText(event)) {
|
|
19885
20132
|
const isPartial = typeof event.timestamp_ms === "number";
|
|
19886
20133
|
if (isPartial) {
|
|
19887
|
-
this.sawAssistantPartials = true;
|
|
19888
20134
|
const text = extractText(event);
|
|
19889
|
-
|
|
20135
|
+
if (text) {
|
|
20136
|
+
this.sawAssistantPartials = true;
|
|
20137
|
+
return [{ type: "text-delta", textDelta: text }];
|
|
20138
|
+
}
|
|
20139
|
+
return [];
|
|
19890
20140
|
}
|
|
19891
20141
|
if (this.sawAssistantPartials) {
|
|
19892
20142
|
return [];
|
|
@@ -19897,9 +20147,12 @@ class StreamToAiSdkParts {
|
|
|
19897
20147
|
if (isThinking(event)) {
|
|
19898
20148
|
const isPartial = typeof event.timestamp_ms === "number";
|
|
19899
20149
|
if (isPartial) {
|
|
19900
|
-
this.sawThinkingPartials = true;
|
|
19901
20150
|
const text = extractThinking(event);
|
|
19902
|
-
|
|
20151
|
+
if (text) {
|
|
20152
|
+
this.sawThinkingPartials = true;
|
|
20153
|
+
return [{ type: "text-delta", textDelta: text }];
|
|
20154
|
+
}
|
|
20155
|
+
return [];
|
|
19903
20156
|
}
|
|
19904
20157
|
if (this.sawThinkingPartials) {
|
|
19905
20158
|
return [];
|
|
@@ -20207,12 +20460,12 @@ init_auth();
|
|
|
20207
20460
|
// src/commands/status.ts
|
|
20208
20461
|
init_auth();
|
|
20209
20462
|
init_logger();
|
|
20210
|
-
import { existsSync as
|
|
20211
|
-
var
|
|
20463
|
+
import { existsSync as existsSync6 } from "fs";
|
|
20464
|
+
var log18 = createLogger("status");
|
|
20212
20465
|
function checkAuthStatus() {
|
|
20213
20466
|
const authFilePath = getAuthFilePath();
|
|
20214
|
-
const exists =
|
|
20215
|
-
|
|
20467
|
+
const exists = existsSync6(authFilePath);
|
|
20468
|
+
log18.debug("Checking auth status", { path: authFilePath });
|
|
20216
20469
|
if (exists) {
|
|
20217
20470
|
return {
|
|
20218
20471
|
authenticated: true,
|