@rama_nigg/open-cursor 2.3.14 → 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 +395 -154
- package/dist/plugin-entry.js +332 -162
- 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/dist/index.js
CHANGED
|
@@ -14252,13 +14252,240 @@ class SkillResolver {
|
|
|
14252
14252
|
}
|
|
14253
14253
|
}
|
|
14254
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
|
+
|
|
14255
14482
|
// node_modules/@opencode-ai/sdk/dist/gen/types.gen.js
|
|
14256
14483
|
var init_types_gen = () => {};
|
|
14257
14484
|
|
|
14258
14485
|
// node_modules/@opencode-ai/sdk/dist/gen/core/serverSentEvents.gen.js
|
|
14259
14486
|
var createSseClient = ({ onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url: url2, ...options }) => {
|
|
14260
14487
|
let lastEventId;
|
|
14261
|
-
const sleep = sseSleepFn ?? ((ms) => new Promise((
|
|
14488
|
+
const sleep = sseSleepFn ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
|
|
14262
14489
|
const createStream = async function* () {
|
|
14263
14490
|
let retryDelay = sseDefaultRetryDelay ?? 3000;
|
|
14264
14491
|
let attempt = 0;
|
|
@@ -15660,15 +15887,15 @@ class LocalExecutor {
|
|
|
15660
15887
|
const out = await handler(args);
|
|
15661
15888
|
return { status: "success", output: out };
|
|
15662
15889
|
} catch (err) {
|
|
15663
|
-
|
|
15890
|
+
log10.warn("Local tool execution failed", { toolId, error: String(err?.message || err) });
|
|
15664
15891
|
return { status: "error", error: String(err?.message || err) };
|
|
15665
15892
|
}
|
|
15666
15893
|
}
|
|
15667
15894
|
}
|
|
15668
|
-
var
|
|
15895
|
+
var log10;
|
|
15669
15896
|
var init_local = __esm(() => {
|
|
15670
15897
|
init_logger();
|
|
15671
|
-
|
|
15898
|
+
log10 = createLogger("tools:executor:local");
|
|
15672
15899
|
});
|
|
15673
15900
|
|
|
15674
15901
|
// src/tools/executors/sdk.ts
|
|
@@ -15695,7 +15922,7 @@ class SdkExecutor {
|
|
|
15695
15922
|
const out = typeof res === "string" ? res : JSON.stringify(res);
|
|
15696
15923
|
return { status: "success", output: out };
|
|
15697
15924
|
} catch (err) {
|
|
15698
|
-
|
|
15925
|
+
log11.warn("SDK tool execution failed", { toolId, error: String(err?.message || err) });
|
|
15699
15926
|
return { status: "error", error: String(err?.message || err) };
|
|
15700
15927
|
}
|
|
15701
15928
|
}
|
|
@@ -15708,10 +15935,10 @@ class SdkExecutor {
|
|
|
15708
15935
|
]);
|
|
15709
15936
|
}
|
|
15710
15937
|
}
|
|
15711
|
-
var
|
|
15938
|
+
var log11;
|
|
15712
15939
|
var init_sdk = __esm(() => {
|
|
15713
15940
|
init_logger();
|
|
15714
|
-
|
|
15941
|
+
log11 = createLogger("tools:executor:sdk");
|
|
15715
15942
|
});
|
|
15716
15943
|
|
|
15717
15944
|
// src/tools/executors/mcp.ts
|
|
@@ -15738,7 +15965,7 @@ class McpExecutor {
|
|
|
15738
15965
|
const out = typeof res === "string" ? res : JSON.stringify(res);
|
|
15739
15966
|
return { status: "success", output: out };
|
|
15740
15967
|
} catch (err) {
|
|
15741
|
-
|
|
15968
|
+
log12.warn("MCP tool execution failed", { toolId, error: String(err?.message || err) });
|
|
15742
15969
|
return { status: "error", error: String(err?.message || err) };
|
|
15743
15970
|
}
|
|
15744
15971
|
}
|
|
@@ -15751,10 +15978,10 @@ class McpExecutor {
|
|
|
15751
15978
|
]);
|
|
15752
15979
|
}
|
|
15753
15980
|
}
|
|
15754
|
-
var
|
|
15981
|
+
var log12;
|
|
15755
15982
|
var init_mcp = __esm(() => {
|
|
15756
15983
|
init_logger();
|
|
15757
|
-
|
|
15984
|
+
log12 = createLogger("tools:executor:mcp");
|
|
15758
15985
|
});
|
|
15759
15986
|
|
|
15760
15987
|
// src/tools/core/executor.ts
|
|
@@ -15764,17 +15991,17 @@ async function executeWithChain(executors, toolId, args) {
|
|
|
15764
15991
|
try {
|
|
15765
15992
|
return await ex.execute(toolId, args);
|
|
15766
15993
|
} catch (err) {
|
|
15767
|
-
|
|
15994
|
+
log13.warn("Executor threw unexpected error", { toolId, error: String(err?.message || err) });
|
|
15768
15995
|
return { status: "error", error: String(err?.message || err) };
|
|
15769
15996
|
}
|
|
15770
15997
|
}
|
|
15771
15998
|
}
|
|
15772
15999
|
return { status: "error", error: `No executor available for ${toolId}` };
|
|
15773
16000
|
}
|
|
15774
|
-
var
|
|
16001
|
+
var log13;
|
|
15775
16002
|
var init_executor = __esm(() => {
|
|
15776
16003
|
init_logger();
|
|
15777
|
-
|
|
16004
|
+
log13 = createLogger("tools:executor:chain");
|
|
15778
16005
|
});
|
|
15779
16006
|
|
|
15780
16007
|
// src/tools/defaults.ts
|
|
@@ -16137,12 +16364,12 @@ function registerDefaultTools(registry2) {
|
|
|
16137
16364
|
source: "local"
|
|
16138
16365
|
}, async (args) => {
|
|
16139
16366
|
const { mkdir } = await import("fs/promises");
|
|
16140
|
-
const { resolve } = await import("path");
|
|
16367
|
+
const { resolve: resolve2 } = await import("path");
|
|
16141
16368
|
const rawPath = resolvePathArg(args, "mkdir");
|
|
16142
16369
|
if (!rawPath) {
|
|
16143
16370
|
throw new Error("mkdir: missing required argument 'path'");
|
|
16144
16371
|
}
|
|
16145
|
-
const target =
|
|
16372
|
+
const target = resolve2(rawPath);
|
|
16146
16373
|
await mkdir(target, { recursive: true });
|
|
16147
16374
|
return `Created directory: ${target}`;
|
|
16148
16375
|
});
|
|
@@ -16167,12 +16394,12 @@ function registerDefaultTools(registry2) {
|
|
|
16167
16394
|
source: "local"
|
|
16168
16395
|
}, async (args) => {
|
|
16169
16396
|
const { rm, stat } = await import("fs/promises");
|
|
16170
|
-
const { resolve } = await import("path");
|
|
16397
|
+
const { resolve: resolve2 } = await import("path");
|
|
16171
16398
|
const rawPath = resolvePathArg(args, "rm");
|
|
16172
16399
|
if (!rawPath) {
|
|
16173
16400
|
throw new Error("rm: missing required argument 'path'");
|
|
16174
16401
|
}
|
|
16175
|
-
const target =
|
|
16402
|
+
const target = resolve2(rawPath);
|
|
16176
16403
|
const force = resolveBoolean(args.force, false);
|
|
16177
16404
|
const info = await stat(target);
|
|
16178
16405
|
if (info.isDirectory() && !force) {
|
|
@@ -16198,12 +16425,12 @@ function registerDefaultTools(registry2) {
|
|
|
16198
16425
|
source: "local"
|
|
16199
16426
|
}, async (args) => {
|
|
16200
16427
|
const { stat } = await import("fs/promises");
|
|
16201
|
-
const { resolve } = await import("path");
|
|
16428
|
+
const { resolve: resolve2 } = await import("path");
|
|
16202
16429
|
const rawPath = resolvePathArg(args, "stat");
|
|
16203
16430
|
if (!rawPath) {
|
|
16204
16431
|
throw new Error("stat: missing required argument 'path'");
|
|
16205
16432
|
}
|
|
16206
|
-
const target =
|
|
16433
|
+
const target = resolve2(rawPath);
|
|
16207
16434
|
const info = await stat(target);
|
|
16208
16435
|
return JSON.stringify({
|
|
16209
16436
|
path: target,
|
|
@@ -16451,11 +16678,11 @@ var init_boundary = __esm(() => {
|
|
|
16451
16678
|
function buildToolSchemaMap(tools) {
|
|
16452
16679
|
const schemas3 = new Map;
|
|
16453
16680
|
for (const rawTool of tools) {
|
|
16454
|
-
const tool3 =
|
|
16681
|
+
const tool3 = isRecord3(rawTool) ? rawTool : null;
|
|
16455
16682
|
if (!tool3) {
|
|
16456
16683
|
continue;
|
|
16457
16684
|
}
|
|
16458
|
-
const fn =
|
|
16685
|
+
const fn = isRecord3(tool3.function) ? tool3.function : tool3;
|
|
16459
16686
|
const name = typeof fn.name === "string" ? fn.name.trim() : "";
|
|
16460
16687
|
if (!name) {
|
|
16461
16688
|
continue;
|
|
@@ -16493,7 +16720,7 @@ function applyToolSchemaCompat(toolCall, toolSchemaMap) {
|
|
|
16493
16720
|
function parseArguments(rawArguments) {
|
|
16494
16721
|
try {
|
|
16495
16722
|
const parsed = JSON.parse(rawArguments);
|
|
16496
|
-
if (
|
|
16723
|
+
if (isRecord3(parsed)) {
|
|
16497
16724
|
return parsed;
|
|
16498
16725
|
}
|
|
16499
16726
|
return { value: parsed };
|
|
@@ -16555,7 +16782,7 @@ function normalizeToolSpecificArgs(toolName, args) {
|
|
|
16555
16782
|
return args;
|
|
16556
16783
|
}
|
|
16557
16784
|
const todos = args.todos.map((entry) => {
|
|
16558
|
-
if (!
|
|
16785
|
+
if (!isRecord3(entry)) {
|
|
16559
16786
|
return entry;
|
|
16560
16787
|
}
|
|
16561
16788
|
const todo = { ...entry };
|
|
@@ -16618,7 +16845,7 @@ function normalizeBashCommand(value) {
|
|
|
16618
16845
|
const parts = value.map((entry) => typeof entry === "string" ? entry : coerceToString2(entry)).filter((entry) => typeof entry === "string" && entry.length > 0);
|
|
16619
16846
|
return parts.length > 0 ? parts.join(" ") : null;
|
|
16620
16847
|
}
|
|
16621
|
-
if (
|
|
16848
|
+
if (isRecord3(value)) {
|
|
16622
16849
|
const command = typeof value.command === "string" ? value.command : null;
|
|
16623
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) : [];
|
|
16624
16851
|
if (command && args.length > 0) {
|
|
@@ -16653,13 +16880,13 @@ function normalizeTodoStatus(status) {
|
|
|
16653
16880
|
return status;
|
|
16654
16881
|
}
|
|
16655
16882
|
function sanitizeArgumentsForSchema(args, schema) {
|
|
16656
|
-
if (!
|
|
16883
|
+
if (!isRecord3(schema)) {
|
|
16657
16884
|
return { args, unexpected: [] };
|
|
16658
16885
|
}
|
|
16659
16886
|
if (schema.additionalProperties !== false) {
|
|
16660
16887
|
return { args, unexpected: [] };
|
|
16661
16888
|
}
|
|
16662
|
-
const properties =
|
|
16889
|
+
const properties = isRecord3(schema.properties) ? schema.properties : {};
|
|
16663
16890
|
const propertyNames = new Set(Object.keys(properties));
|
|
16664
16891
|
const sanitized = {};
|
|
16665
16892
|
const unexpected = [];
|
|
@@ -16673,7 +16900,7 @@ function sanitizeArgumentsForSchema(args, schema) {
|
|
|
16673
16900
|
return { args: sanitized, unexpected };
|
|
16674
16901
|
}
|
|
16675
16902
|
function validateToolArguments(toolName, args, schema, unexpected) {
|
|
16676
|
-
if (!
|
|
16903
|
+
if (!isRecord3(schema)) {
|
|
16677
16904
|
return {
|
|
16678
16905
|
hasSchema: false,
|
|
16679
16906
|
ok: true,
|
|
@@ -16682,13 +16909,13 @@ function validateToolArguments(toolName, args, schema, unexpected) {
|
|
|
16682
16909
|
typeErrors: []
|
|
16683
16910
|
};
|
|
16684
16911
|
}
|
|
16685
|
-
const properties =
|
|
16912
|
+
const properties = isRecord3(schema.properties) ? schema.properties : {};
|
|
16686
16913
|
const required2 = Array.isArray(schema.required) ? schema.required.filter((value) => typeof value === "string") : [];
|
|
16687
16914
|
const missing = required2.filter((key) => !hasOwn(args, key));
|
|
16688
16915
|
const typeErrors = [];
|
|
16689
16916
|
for (const [key, value] of Object.entries(args)) {
|
|
16690
16917
|
const propertySchema = properties[key];
|
|
16691
|
-
if (!
|
|
16918
|
+
if (!isRecord3(propertySchema)) {
|
|
16692
16919
|
continue;
|
|
16693
16920
|
}
|
|
16694
16921
|
if (!matchesType(value, propertySchema.type)) {
|
|
@@ -16747,7 +16974,7 @@ function matchesType(value, schemaType) {
|
|
|
16747
16974
|
case "boolean":
|
|
16748
16975
|
return typeof value === "boolean";
|
|
16749
16976
|
case "object":
|
|
16750
|
-
return
|
|
16977
|
+
return isRecord3(value);
|
|
16751
16978
|
case "array":
|
|
16752
16979
|
return Array.isArray(value);
|
|
16753
16980
|
case "null":
|
|
@@ -16768,7 +16995,7 @@ function coerceToString2(value) {
|
|
|
16768
16995
|
for (const item of value) {
|
|
16769
16996
|
if (typeof item === "string") {
|
|
16770
16997
|
parts.push(item);
|
|
16771
|
-
} else if (
|
|
16998
|
+
} else if (isRecord3(item)) {
|
|
16772
16999
|
const text = typeof item.text === "string" ? item.text : typeof item.content === "string" ? item.content : typeof item.value === "string" ? item.value : null;
|
|
16773
17000
|
if (text !== null) {
|
|
16774
17001
|
parts.push(text);
|
|
@@ -16781,7 +17008,7 @@ function coerceToString2(value) {
|
|
|
16781
17008
|
}
|
|
16782
17009
|
return parts.length > 0 ? parts.join("") : null;
|
|
16783
17010
|
}
|
|
16784
|
-
if (
|
|
17011
|
+
if (isRecord3(value)) {
|
|
16785
17012
|
if (typeof value.text === "string") {
|
|
16786
17013
|
return value.text;
|
|
16787
17014
|
}
|
|
@@ -16801,7 +17028,7 @@ function coerceToString2(value) {
|
|
|
16801
17028
|
function hasOwn(record2, key) {
|
|
16802
17029
|
return Object.prototype.hasOwnProperty.call(record2, key);
|
|
16803
17030
|
}
|
|
16804
|
-
function
|
|
17031
|
+
function isRecord3(value) {
|
|
16805
17032
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16806
17033
|
}
|
|
16807
17034
|
var EDIT_COMPAT_REPAIR_ENABLED, ARG_KEY_ALIASES;
|
|
@@ -16864,7 +17091,7 @@ async function handleToolLoopEventLegacy(options) {
|
|
|
16864
17091
|
const extraction = toolLoopMode === "opencode" ? extractOpenAiToolCall(event, allowedToolNames) : { action: "skip", skipReason: "tool_loop_mode_not_opencode" };
|
|
16865
17092
|
if (extraction.action === "passthrough") {
|
|
16866
17093
|
passThroughTracker?.trackTool(extraction.passthroughName);
|
|
16867
|
-
|
|
17094
|
+
log14.debug("MCP tool passed through to cursor-agent (legacy)", {
|
|
16868
17095
|
tool: extraction.passthroughName
|
|
16869
17096
|
});
|
|
16870
17097
|
return { intercepted: false, skipConverter: false };
|
|
@@ -16888,7 +17115,7 @@ async function handleToolLoopEventLegacy(options) {
|
|
|
16888
17115
|
if (interceptedToolCall) {
|
|
16889
17116
|
const compat2 = applyToolSchemaCompat(interceptedToolCall, toolSchemaMap);
|
|
16890
17117
|
let normalizedToolCall = compat2.toolCall;
|
|
16891
|
-
|
|
17118
|
+
log14.debug("Applied tool schema compatibility (legacy)", {
|
|
16892
17119
|
tool: normalizedToolCall.function.name,
|
|
16893
17120
|
originalArgKeys: compat2.originalArgKeys,
|
|
16894
17121
|
normalizedArgKeys: compat2.normalizedArgKeys,
|
|
@@ -16902,7 +17129,7 @@ async function handleToolLoopEventLegacy(options) {
|
|
|
16902
17129
|
}
|
|
16903
17130
|
const reroutedWrite = tryRerouteEditToWrite(normalizedToolCall, compat2.normalizedArgs, allowedToolNames, toolSchemaMap);
|
|
16904
17131
|
if (reroutedWrite) {
|
|
16905
|
-
|
|
17132
|
+
log14.debug("Rerouting malformed edit call to write (legacy)", {
|
|
16906
17133
|
path: reroutedWrite.path,
|
|
16907
17134
|
missing: compat2.validation.missing,
|
|
16908
17135
|
typeErrors: compat2.validation.typeErrors
|
|
@@ -16910,7 +17137,7 @@ async function handleToolLoopEventLegacy(options) {
|
|
|
16910
17137
|
normalizedToolCall = reroutedWrite.toolCall;
|
|
16911
17138
|
} else if (shouldEmitNonFatalSchemaValidationHint(normalizedToolCall, compat2.validation)) {
|
|
16912
17139
|
const hintChunk = createNonFatalSchemaValidationHintChunk(responseMeta, normalizedToolCall, compat2.validation);
|
|
16913
|
-
|
|
17140
|
+
log14.debug("Emitting non-fatal schema validation hint in legacy and skipping malformed tool execution", {
|
|
16914
17141
|
tool: normalizedToolCall.function.name,
|
|
16915
17142
|
missing: compat2.validation.missing,
|
|
16916
17143
|
typeErrors: compat2.validation.typeErrors
|
|
@@ -16972,7 +17199,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
16972
17199
|
}
|
|
16973
17200
|
if (extraction.action === "passthrough") {
|
|
16974
17201
|
passThroughTracker?.trackTool(extraction.passthroughName);
|
|
16975
|
-
|
|
17202
|
+
log14.debug("MCP tool passed through to cursor-agent (v1)", {
|
|
16976
17203
|
tool: extraction.passthroughName
|
|
16977
17204
|
});
|
|
16978
17205
|
return { intercepted: false, skipConverter: false };
|
|
@@ -16999,7 +17226,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
16999
17226
|
rawArgs: safeArgTypeSummary(event),
|
|
17000
17227
|
normalizedArgs: compat2.normalizedArgs
|
|
17001
17228
|
} : undefined;
|
|
17002
|
-
|
|
17229
|
+
log14.debug("Applied tool schema compatibility", {
|
|
17003
17230
|
tool: normalizedToolCall.function.name,
|
|
17004
17231
|
originalArgKeys: compat2.originalArgKeys,
|
|
17005
17232
|
normalizedArgKeys: compat2.normalizedArgKeys,
|
|
@@ -17008,7 +17235,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
17008
17235
|
...editDiag ? { editDiag } : {}
|
|
17009
17236
|
});
|
|
17010
17237
|
if (compat2.validation.hasSchema && !compat2.validation.ok) {
|
|
17011
|
-
|
|
17238
|
+
log14.debug("Tool schema compatibility validation failed", {
|
|
17012
17239
|
tool: normalizedToolCall.function.name,
|
|
17013
17240
|
missing: compat2.validation.missing,
|
|
17014
17241
|
unexpected: compat2.validation.unexpected,
|
|
@@ -17025,7 +17252,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
17025
17252
|
}
|
|
17026
17253
|
const reroutedWrite = tryRerouteEditToWrite(normalizedToolCall, compat2.normalizedArgs, allowedToolNames, toolSchemaMap);
|
|
17027
17254
|
if (reroutedWrite) {
|
|
17028
|
-
|
|
17255
|
+
log14.debug("Rerouting malformed edit call to write", {
|
|
17029
17256
|
path: reroutedWrite.path,
|
|
17030
17257
|
missing: compat2.validation.missing,
|
|
17031
17258
|
typeErrors: compat2.validation.typeErrors
|
|
@@ -17045,7 +17272,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
17045
17272
|
}
|
|
17046
17273
|
if (schemaValidationFailureMode === "pass_through" && shouldEmitNonFatalSchemaValidationHint(normalizedToolCall, compat2.validation)) {
|
|
17047
17274
|
const hintChunk = createNonFatalSchemaValidationHintChunk(responseMeta, normalizedToolCall, compat2.validation);
|
|
17048
|
-
|
|
17275
|
+
log14.debug("Emitting non-fatal schema validation hint and skipping malformed tool execution", {
|
|
17049
17276
|
tool: normalizedToolCall.function.name,
|
|
17050
17277
|
missing: compat2.validation.missing,
|
|
17051
17278
|
typeErrors: compat2.validation.typeErrors
|
|
@@ -17063,7 +17290,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
17063
17290
|
terminate: createSchemaValidationTermination(normalizedToolCall, compat2.validation)
|
|
17064
17291
|
};
|
|
17065
17292
|
}
|
|
17066
|
-
|
|
17293
|
+
log14.debug("Forwarding schema-invalid tool call to OpenCode loop", {
|
|
17067
17294
|
tool: normalizedToolCall.function.name,
|
|
17068
17295
|
repairHint: compat2.validation.repairHint
|
|
17069
17296
|
});
|
|
@@ -17126,7 +17353,7 @@ function evaluateToolLoopGuard(toolLoopGuard, toolCall) {
|
|
|
17126
17353
|
if (!decision.triggered) {
|
|
17127
17354
|
return null;
|
|
17128
17355
|
}
|
|
17129
|
-
|
|
17356
|
+
log14.debug("Tool loop guard triggered", {
|
|
17130
17357
|
tool: toolCall.function.name,
|
|
17131
17358
|
fingerprint: decision.fingerprint,
|
|
17132
17359
|
repeatCount: decision.repeatCount,
|
|
@@ -17185,7 +17412,7 @@ function evaluateSchemaValidationLoopGuard(toolLoopGuard, toolCall, validation)
|
|
|
17185
17412
|
if (!decision.tracked || !decision.triggered) {
|
|
17186
17413
|
return null;
|
|
17187
17414
|
}
|
|
17188
|
-
|
|
17415
|
+
log14.warn("Tool loop guard triggered on schema validation", {
|
|
17189
17416
|
tool: toolCall.function.name,
|
|
17190
17417
|
fingerprint: decision.fingerprint,
|
|
17191
17418
|
repeatCount: decision.repeatCount,
|
|
@@ -17261,11 +17488,11 @@ function safeArgTypeSummary(event) {
|
|
|
17261
17488
|
try {
|
|
17262
17489
|
let raw;
|
|
17263
17490
|
const toolCallPayload = event?.tool_call;
|
|
17264
|
-
if (
|
|
17491
|
+
if (isRecord4(toolCallPayload)) {
|
|
17265
17492
|
const entries = Object.entries(toolCallPayload);
|
|
17266
17493
|
if (entries.length > 0) {
|
|
17267
17494
|
const [, payload] = entries[0];
|
|
17268
|
-
if (
|
|
17495
|
+
if (isRecord4(payload)) {
|
|
17269
17496
|
raw = payload.args;
|
|
17270
17497
|
if (raw === undefined) {
|
|
17271
17498
|
const { result: _result, ...rest } = payload;
|
|
@@ -17300,7 +17527,7 @@ function safeArgTypeSummary(event) {
|
|
|
17300
17527
|
}
|
|
17301
17528
|
function shouldUsePassThroughForEditSchema(event) {
|
|
17302
17529
|
const toolCallPayload = event?.tool_call;
|
|
17303
|
-
if (!
|
|
17530
|
+
if (!isRecord4(toolCallPayload)) {
|
|
17304
17531
|
return false;
|
|
17305
17532
|
}
|
|
17306
17533
|
const keys = Object.keys(toolCallPayload);
|
|
@@ -17341,15 +17568,15 @@ function tryRerouteEditToWrite(toolCall, normalizedArgs, allowedToolNames, toolS
|
|
|
17341
17568
|
}
|
|
17342
17569
|
};
|
|
17343
17570
|
}
|
|
17344
|
-
function
|
|
17571
|
+
function isRecord4(value) {
|
|
17345
17572
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17346
17573
|
}
|
|
17347
|
-
var
|
|
17574
|
+
var log14, ToolBoundaryExtractionError;
|
|
17348
17575
|
var init_runtime_interception = __esm(() => {
|
|
17349
17576
|
init_tool_loop();
|
|
17350
17577
|
init_logger();
|
|
17351
17578
|
init_tool_schema_compat();
|
|
17352
|
-
|
|
17579
|
+
log14 = createLogger("provider:runtime-interception");
|
|
17353
17580
|
ToolBoundaryExtractionError = class ToolBoundaryExtractionError extends Error {
|
|
17354
17581
|
cause;
|
|
17355
17582
|
constructor(message, cause) {
|
|
@@ -17391,7 +17618,7 @@ class ToastService {
|
|
|
17391
17618
|
}
|
|
17392
17619
|
async show(options) {
|
|
17393
17620
|
if (!this.client?.tui?.showToast) {
|
|
17394
|
-
|
|
17621
|
+
log15.debug("Toast not available; client.tui.showToast missing", { message: options.message });
|
|
17395
17622
|
return;
|
|
17396
17623
|
}
|
|
17397
17624
|
try {
|
|
@@ -17403,7 +17630,7 @@ class ToastService {
|
|
|
17403
17630
|
}
|
|
17404
17631
|
});
|
|
17405
17632
|
} catch (error45) {
|
|
17406
|
-
|
|
17633
|
+
log15.debug("Toast failed", { error: error45, message: options.message });
|
|
17407
17634
|
}
|
|
17408
17635
|
}
|
|
17409
17636
|
async showPassThroughSummary(tools) {
|
|
@@ -17427,10 +17654,10 @@ class ToastService {
|
|
|
17427
17654
|
});
|
|
17428
17655
|
}
|
|
17429
17656
|
}
|
|
17430
|
-
var
|
|
17657
|
+
var log15, toastService;
|
|
17431
17658
|
var init_toast_service = __esm(() => {
|
|
17432
17659
|
init_logger();
|
|
17433
|
-
|
|
17660
|
+
log15 = createLogger("services:toast");
|
|
17434
17661
|
toastService = new ToastService;
|
|
17435
17662
|
});
|
|
17436
17663
|
|
|
@@ -17528,7 +17755,7 @@ function indexToolResultErrorClasses(messages) {
|
|
|
17528
17755
|
const byCallId = new Map;
|
|
17529
17756
|
let latest = null;
|
|
17530
17757
|
for (const message of messages) {
|
|
17531
|
-
if (!
|
|
17758
|
+
if (!isRecord5(message) || message.role !== "tool") {
|
|
17532
17759
|
continue;
|
|
17533
17760
|
}
|
|
17534
17761
|
const errorClass = classifyToolResult(message.content);
|
|
@@ -17651,7 +17878,7 @@ function deriveSuccessCoarseFingerprint(toolName, rawArguments) {
|
|
|
17651
17878
|
}
|
|
17652
17879
|
try {
|
|
17653
17880
|
const parsed = JSON.parse(rawArguments);
|
|
17654
|
-
if (!
|
|
17881
|
+
if (!isRecord5(parsed)) {
|
|
17655
17882
|
return null;
|
|
17656
17883
|
}
|
|
17657
17884
|
const path2 = typeof parsed.path === "string" ? parsed.path : "";
|
|
@@ -17672,15 +17899,15 @@ function deriveSuccessCoarseFingerprint(toolName, rawArguments) {
|
|
|
17672
17899
|
function extractAssistantToolCalls(messages) {
|
|
17673
17900
|
const calls = [];
|
|
17674
17901
|
for (const message of messages) {
|
|
17675
|
-
if (!
|
|
17902
|
+
if (!isRecord5(message) || message.role !== "assistant" || !Array.isArray(message.tool_calls)) {
|
|
17676
17903
|
continue;
|
|
17677
17904
|
}
|
|
17678
17905
|
for (const call of message.tool_calls) {
|
|
17679
|
-
if (!
|
|
17906
|
+
if (!isRecord5(call)) {
|
|
17680
17907
|
continue;
|
|
17681
17908
|
}
|
|
17682
17909
|
const id = typeof call.id === "string" ? call.id : "";
|
|
17683
|
-
const fn =
|
|
17910
|
+
const fn = isRecord5(call.function) ? call.function : null;
|
|
17684
17911
|
const name = fn && typeof fn.name === "string" ? fn.name : "";
|
|
17685
17912
|
const rawArguments = fn && typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn?.arguments ?? {});
|
|
17686
17913
|
if (!id || !name) {
|
|
@@ -17701,7 +17928,7 @@ function extractAssistantToolCalls(messages) {
|
|
|
17701
17928
|
function extractArgumentKeys(rawArguments) {
|
|
17702
17929
|
try {
|
|
17703
17930
|
const parsed = JSON.parse(rawArguments);
|
|
17704
|
-
if (!
|
|
17931
|
+
if (!isRecord5(parsed)) {
|
|
17705
17932
|
return [];
|
|
17706
17933
|
}
|
|
17707
17934
|
return Object.keys(parsed);
|
|
@@ -17773,7 +18000,7 @@ function shapeOf(value) {
|
|
|
17773
18000
|
}
|
|
17774
18001
|
return [shapeOf(value[0])];
|
|
17775
18002
|
}
|
|
17776
|
-
if (
|
|
18003
|
+
if (isRecord5(value)) {
|
|
17777
18004
|
const shaped = {};
|
|
17778
18005
|
for (const key of Object.keys(value).sort()) {
|
|
17779
18006
|
shaped[key] = shapeOf(value[key]);
|
|
@@ -17789,7 +18016,7 @@ function canonicalizeValue(value) {
|
|
|
17789
18016
|
if (Array.isArray(value)) {
|
|
17790
18017
|
return value.map((entry) => canonicalizeValue(entry));
|
|
17791
18018
|
}
|
|
17792
|
-
if (
|
|
18019
|
+
if (isRecord5(value)) {
|
|
17793
18020
|
const canonical = {};
|
|
17794
18021
|
for (const key of Object.keys(value).sort()) {
|
|
17795
18022
|
canonical[key] = canonicalizeValue(value[key]);
|
|
@@ -17825,7 +18052,7 @@ function renderContent(content) {
|
|
|
17825
18052
|
if (typeof part === "string") {
|
|
17826
18053
|
return part;
|
|
17827
18054
|
}
|
|
17828
|
-
if (
|
|
18055
|
+
if (isRecord5(part) && typeof part.text === "string") {
|
|
17829
18056
|
return part.text;
|
|
17830
18057
|
}
|
|
17831
18058
|
return JSON.stringify(part);
|
|
@@ -17839,7 +18066,7 @@ function renderContent(content) {
|
|
|
17839
18066
|
function containsAny(text, patterns) {
|
|
17840
18067
|
return patterns.some((pattern) => text.includes(pattern));
|
|
17841
18068
|
}
|
|
17842
|
-
function
|
|
18069
|
+
function isRecord5(value) {
|
|
17843
18070
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17844
18071
|
}
|
|
17845
18072
|
var UNKNOWN_AS_SUCCESS_TOOLS, EXPLORATION_TOOLS, COARSE_LIMIT_MULTIPLIER = 3, EXPLORATION_LIMIT_MULTIPLIER = 5;
|
|
@@ -17884,13 +18111,13 @@ __export(exports_plugin, {
|
|
|
17884
18111
|
default: () => plugin_default,
|
|
17885
18112
|
CursorPlugin: () => CursorPlugin
|
|
17886
18113
|
});
|
|
17887
|
-
import { appendFileSync as appendFileSync3, existsSync as
|
|
18114
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync5, realpathSync } from "fs";
|
|
17888
18115
|
import { mkdir } from "fs/promises";
|
|
17889
|
-
import { homedir as
|
|
17890
|
-
import { isAbsolute, join as
|
|
18116
|
+
import { homedir as homedir5 } from "os";
|
|
18117
|
+
import { isAbsolute, join as join5, relative, resolve as resolve2 } from "path";
|
|
17891
18118
|
function ensureDebugLogDir() {
|
|
17892
18119
|
try {
|
|
17893
|
-
if (!
|
|
18120
|
+
if (!existsSync5(DEBUG_LOG_DIR2)) {
|
|
17894
18121
|
mkdir(DEBUG_LOG_DIR2, { recursive: true }).catch(() => {});
|
|
17895
18122
|
}
|
|
17896
18123
|
} catch {}
|
|
@@ -17905,13 +18132,13 @@ function debugLogToFile2(message, data) {
|
|
|
17905
18132
|
} catch {}
|
|
17906
18133
|
}
|
|
17907
18134
|
async function ensurePluginDirectory() {
|
|
17908
|
-
const configHome = process.env.XDG_CONFIG_HOME ?
|
|
17909
|
-
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");
|
|
17910
18137
|
try {
|
|
17911
18138
|
await mkdir(pluginDir, { recursive: true });
|
|
17912
|
-
|
|
18139
|
+
log16.debug("Plugin directory ensured", { path: pluginDir });
|
|
17913
18140
|
} catch (error45) {
|
|
17914
|
-
|
|
18141
|
+
log16.warn("Failed to create plugin directory", { error: String(error45) });
|
|
17915
18142
|
}
|
|
17916
18143
|
}
|
|
17917
18144
|
function shouldProcessModel(model) {
|
|
@@ -17923,11 +18150,11 @@ function getGlobalKey() {
|
|
|
17923
18150
|
return "__opencode_cursor_proxy_server__";
|
|
17924
18151
|
}
|
|
17925
18152
|
function getOpenCodeConfigPrefix() {
|
|
17926
|
-
const configHome = process.env.XDG_CONFIG_HOME ?
|
|
17927
|
-
return
|
|
18153
|
+
const configHome = process.env.XDG_CONFIG_HOME ? resolve2(process.env.XDG_CONFIG_HOME) : join5(homedir5(), ".config");
|
|
18154
|
+
return join5(configHome, "opencode");
|
|
17928
18155
|
}
|
|
17929
18156
|
function canonicalizePathForCompare(pathValue) {
|
|
17930
|
-
const resolvedPath =
|
|
18157
|
+
const resolvedPath = resolve2(pathValue);
|
|
17931
18158
|
let normalizedPath = resolvedPath;
|
|
17932
18159
|
try {
|
|
17933
18160
|
normalizedPath = typeof realpathSync.native === "function" ? realpathSync.native(resolvedPath) : realpathSync(resolvedPath);
|
|
@@ -17949,7 +18176,7 @@ function resolveCandidate(value) {
|
|
|
17949
18176
|
if (!value || value.trim().length === 0) {
|
|
17950
18177
|
return "";
|
|
17951
18178
|
}
|
|
17952
|
-
return
|
|
18179
|
+
return resolve2(value);
|
|
17953
18180
|
}
|
|
17954
18181
|
function isNonConfigPath(pathValue) {
|
|
17955
18182
|
if (!pathValue) {
|
|
@@ -17960,11 +18187,11 @@ function isNonConfigPath(pathValue) {
|
|
|
17960
18187
|
function resolveWorkspaceDirectory(worktree, directory) {
|
|
17961
18188
|
const envWorkspace = process.env.CURSOR_ACP_WORKSPACE?.trim();
|
|
17962
18189
|
if (envWorkspace) {
|
|
17963
|
-
return
|
|
18190
|
+
return resolve2(envWorkspace);
|
|
17964
18191
|
}
|
|
17965
18192
|
const envProjectDir = process.env.OPENCODE_CURSOR_PROJECT_DIR?.trim();
|
|
17966
18193
|
if (envProjectDir) {
|
|
17967
|
-
return
|
|
18194
|
+
return resolve2(envProjectDir);
|
|
17968
18195
|
}
|
|
17969
18196
|
const configPrefix = getOpenCodeConfigPrefix();
|
|
17970
18197
|
const worktreeCandidate = resolveCandidate(worktree);
|
|
@@ -17975,14 +18202,14 @@ function resolveWorkspaceDirectory(worktree, directory) {
|
|
|
17975
18202
|
if (dirCandidate && !isWithinPath(configPrefix, dirCandidate)) {
|
|
17976
18203
|
return dirCandidate;
|
|
17977
18204
|
}
|
|
17978
|
-
const cwd =
|
|
18205
|
+
const cwd = resolve2(process.cwd());
|
|
17979
18206
|
if (cwd && !isWithinPath(configPrefix, cwd)) {
|
|
17980
18207
|
return cwd;
|
|
17981
18208
|
}
|
|
17982
18209
|
return dirCandidate || cwd || configPrefix;
|
|
17983
18210
|
}
|
|
17984
18211
|
function normalizeWorkspaceForCompare(pathValue) {
|
|
17985
|
-
return
|
|
18212
|
+
return resolve2(pathValue);
|
|
17986
18213
|
}
|
|
17987
18214
|
function isReusableProxyHealthPayload(payload, workspaceDirectory) {
|
|
17988
18215
|
if (!payload || payload.ok !== true) {
|
|
@@ -18099,9 +18326,9 @@ function createBoundaryRuntimeContext(scope) {
|
|
|
18099
18326
|
error: toErrorMessage(error45)
|
|
18100
18327
|
};
|
|
18101
18328
|
if (!fallbackActive) {
|
|
18102
|
-
|
|
18329
|
+
log16.warn("Provider boundary v1 failed; switching to legacy for this request", details);
|
|
18103
18330
|
} else {
|
|
18104
|
-
|
|
18331
|
+
log16.debug("Provider boundary fallback already active", details);
|
|
18105
18332
|
}
|
|
18106
18333
|
fallbackActive = true;
|
|
18107
18334
|
return true;
|
|
@@ -18239,7 +18466,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18239
18466
|
headers: { "Content-Type": "application/json" }
|
|
18240
18467
|
});
|
|
18241
18468
|
} catch (err) {
|
|
18242
|
-
|
|
18469
|
+
log16.error("Failed to list models", { error: String(err) });
|
|
18243
18470
|
return new Response(JSON.stringify({ error: "Failed to fetch models from cursor-agent" }), {
|
|
18244
18471
|
status: 500,
|
|
18245
18472
|
headers: { "Content-Type": "application/json" }
|
|
@@ -18252,7 +18479,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18252
18479
|
headers: { "Content-Type": "application/json" }
|
|
18253
18480
|
});
|
|
18254
18481
|
}
|
|
18255
|
-
|
|
18482
|
+
log16.debug("Proxy request (bun)", { method: req.method, path: url2.pathname });
|
|
18256
18483
|
const body = await req.json().catch(() => ({}));
|
|
18257
18484
|
const messages = Array.isArray(body?.messages) ? body.messages : [];
|
|
18258
18485
|
const stream = body?.stream === true;
|
|
@@ -18279,7 +18506,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18279
18506
|
const clen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr${m.content.length}` : typeof m?.content;
|
|
18280
18507
|
return `${i}:${role}${hasTc ? `(tc:${hasTc})` : ""}(clen:${clen})`;
|
|
18281
18508
|
});
|
|
18282
|
-
|
|
18509
|
+
log16.debug("Proxy chat request (bun)", {
|
|
18283
18510
|
stream,
|
|
18284
18511
|
model,
|
|
18285
18512
|
messages: messages.length,
|
|
@@ -18325,7 +18552,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18325
18552
|
const stdout = (stdoutText || "").trim();
|
|
18326
18553
|
const stderr = (stderrText || "").trim();
|
|
18327
18554
|
const exitCode = await child.exited;
|
|
18328
|
-
|
|
18555
|
+
log16.debug("cursor-agent completed (bun non-stream)", {
|
|
18329
18556
|
exitCode,
|
|
18330
18557
|
stdoutChars: stdout.length,
|
|
18331
18558
|
stderrChars: stderr.length
|
|
@@ -18351,7 +18578,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18351
18578
|
});
|
|
18352
18579
|
}
|
|
18353
18580
|
if (intercepted.toolCall) {
|
|
18354
|
-
|
|
18581
|
+
log16.debug("Intercepted OpenCode tool call (non-stream)", {
|
|
18355
18582
|
name: intercepted.toolCall.function.name,
|
|
18356
18583
|
callId: intercepted.toolCall.id
|
|
18357
18584
|
});
|
|
@@ -18365,7 +18592,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18365
18592
|
const errSource = stderr || stdout || `cursor-agent exited with code ${String(exitCode ?? "unknown")} and no output`;
|
|
18366
18593
|
const parsed = parseAgentError(errSource);
|
|
18367
18594
|
const userError = formatErrorForUser(parsed);
|
|
18368
|
-
|
|
18595
|
+
log16.error("cursor-cli failed", {
|
|
18369
18596
|
type: parsed.type,
|
|
18370
18597
|
message: parsed.message,
|
|
18371
18598
|
code: exitCode
|
|
@@ -18400,7 +18627,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18400
18627
|
const converter = new StreamToSseConverter(model, { id, created });
|
|
18401
18628
|
const lineBuffer = new LineBuffer;
|
|
18402
18629
|
const emitToolCallAndTerminate = (toolCall) => {
|
|
18403
|
-
|
|
18630
|
+
log16.debug("Intercepted OpenCode tool call (stream)", {
|
|
18404
18631
|
name: toolCall.function.name,
|
|
18405
18632
|
callId: toolCall.id
|
|
18406
18633
|
});
|
|
@@ -18582,7 +18809,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18582
18809
|
const errSource = (stderrText || "").trim() || `cursor-agent exited with code ${String(exitCode ?? "unknown")} and no output`;
|
|
18583
18810
|
const parsed = parseAgentError(errSource);
|
|
18584
18811
|
const msg = formatErrorForUser(parsed);
|
|
18585
|
-
|
|
18812
|
+
log16.error("cursor-cli streaming failed", {
|
|
18586
18813
|
type: parsed.type,
|
|
18587
18814
|
code: exitCode
|
|
18588
18815
|
});
|
|
@@ -18593,7 +18820,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18593
18820
|
controller.enqueue(encoder.encode(formatSseDone()));
|
|
18594
18821
|
return;
|
|
18595
18822
|
}
|
|
18596
|
-
|
|
18823
|
+
log16.debug("cursor-agent completed (bun stream)", {
|
|
18597
18824
|
exitCode
|
|
18598
18825
|
});
|
|
18599
18826
|
const passThroughSummary = passThroughTracker.getSummary();
|
|
@@ -18675,7 +18902,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18675
18902
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
18676
18903
|
res.end(JSON.stringify({ object: "list", data: models }));
|
|
18677
18904
|
} catch (err) {
|
|
18678
|
-
|
|
18905
|
+
log16.error("Failed to list models", { error: String(err) });
|
|
18679
18906
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
18680
18907
|
res.end(JSON.stringify({ error: "Failed to fetch models" }));
|
|
18681
18908
|
}
|
|
@@ -18686,7 +18913,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18686
18913
|
res.end(JSON.stringify({ error: `Unsupported path: ${url2.pathname}` }));
|
|
18687
18914
|
return;
|
|
18688
18915
|
}
|
|
18689
|
-
|
|
18916
|
+
log16.debug("Proxy request (node)", { method: req.method, path: url2.pathname });
|
|
18690
18917
|
let body = "";
|
|
18691
18918
|
for await (const chunk of req) {
|
|
18692
18919
|
body += chunk;
|
|
@@ -18709,7 +18936,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18709
18936
|
const contentLen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr${m.content.length}` : typeof m?.content;
|
|
18710
18937
|
return `${i}:${role}${hasTc ? `(tc:${hasTc})` : ""}${role === "tool" ? `(tcid:${tcId},name:${tcName},clen:${contentLen})` : `(clen:${contentLen})`}`;
|
|
18711
18938
|
});
|
|
18712
|
-
|
|
18939
|
+
log16.debug("Proxy chat request (node)", {
|
|
18713
18940
|
stream,
|
|
18714
18941
|
model,
|
|
18715
18942
|
messages: messages.length,
|
|
@@ -18740,14 +18967,14 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18740
18967
|
let spawnErrorText = null;
|
|
18741
18968
|
child.on("error", (error45) => {
|
|
18742
18969
|
spawnErrorText = String(error45?.message || error45);
|
|
18743
|
-
|
|
18970
|
+
log16.error("Failed to spawn cursor-agent", { error: spawnErrorText, model });
|
|
18744
18971
|
});
|
|
18745
18972
|
child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
18746
18973
|
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
18747
18974
|
child.on("close", async (code) => {
|
|
18748
18975
|
const stdout = Buffer.concat(stdoutChunks).toString().trim();
|
|
18749
18976
|
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
18750
|
-
|
|
18977
|
+
log16.debug("cursor-agent completed (node non-stream)", {
|
|
18751
18978
|
code,
|
|
18752
18979
|
stdoutChars: stdout.length,
|
|
18753
18980
|
stderrChars: stderr.length,
|
|
@@ -18773,7 +19000,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18773
19000
|
return;
|
|
18774
19001
|
}
|
|
18775
19002
|
if (intercepted.toolCall) {
|
|
18776
|
-
|
|
19003
|
+
log16.debug("Intercepted OpenCode tool call (non-stream)", {
|
|
18777
19004
|
name: intercepted.toolCall.function.name,
|
|
18778
19005
|
callId: intercepted.toolCall.id
|
|
18779
19006
|
});
|
|
@@ -18787,7 +19014,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18787
19014
|
const errSource = stderr || stdout || spawnErrorText || `cursor-agent exited with code ${String(code ?? "unknown")} and no output`;
|
|
18788
19015
|
const parsed = parseAgentError(errSource);
|
|
18789
19016
|
const userError = formatErrorForUser(parsed);
|
|
18790
|
-
|
|
19017
|
+
log16.error("cursor-cli failed", {
|
|
18791
19018
|
type: parsed.type,
|
|
18792
19019
|
message: parsed.message,
|
|
18793
19020
|
code
|
|
@@ -18827,7 +19054,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18827
19054
|
return;
|
|
18828
19055
|
}
|
|
18829
19056
|
const errSource = String(error45?.message || error45);
|
|
18830
|
-
|
|
19057
|
+
log16.error("Failed to spawn cursor-agent (stream)", { error: errSource, model });
|
|
18831
19058
|
const parsed = parseAgentError(errSource);
|
|
18832
19059
|
const msg = formatErrorForUser(parsed);
|
|
18833
19060
|
const errChunk = createChatCompletionChunk(id, created, model, msg, true);
|
|
@@ -18842,7 +19069,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18842
19069
|
if (streamTerminated || res.writableEnded) {
|
|
18843
19070
|
return;
|
|
18844
19071
|
}
|
|
18845
|
-
|
|
19072
|
+
log16.debug("Intercepted OpenCode tool call (stream)", {
|
|
18846
19073
|
name: toolCall.function.name,
|
|
18847
19074
|
callId: toolCall.id
|
|
18848
19075
|
});
|
|
@@ -19026,7 +19253,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
19026
19253
|
perf.mark("request:done");
|
|
19027
19254
|
perf.summarize();
|
|
19028
19255
|
const stderrText = Buffer.concat(stderrChunks).toString().trim();
|
|
19029
|
-
|
|
19256
|
+
log16.debug("cursor-agent completed (node stream)", {
|
|
19030
19257
|
code,
|
|
19031
19258
|
stderrChars: stderrText.length
|
|
19032
19259
|
});
|
|
@@ -19078,8 +19305,8 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
19078
19305
|
};
|
|
19079
19306
|
let server2 = http.createServer(requestHandler);
|
|
19080
19307
|
try {
|
|
19081
|
-
await new Promise((
|
|
19082
|
-
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());
|
|
19083
19310
|
server2.once("error", reject);
|
|
19084
19311
|
});
|
|
19085
19312
|
const baseURL = `http://${CURSOR_PROXY_HOST}:${CURSOR_PROXY_DEFAULT_PORT}/v1`;
|
|
@@ -19104,8 +19331,8 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
19104
19331
|
} catch {}
|
|
19105
19332
|
}
|
|
19106
19333
|
server2 = http.createServer(requestHandler);
|
|
19107
|
-
await new Promise((
|
|
19108
|
-
server2.listen(0, CURSOR_PROXY_HOST, () =>
|
|
19334
|
+
await new Promise((resolve3, reject) => {
|
|
19335
|
+
server2.listen(0, CURSOR_PROXY_HOST, () => resolve3());
|
|
19109
19336
|
server2.once("error", reject);
|
|
19110
19337
|
});
|
|
19111
19338
|
const addr = server2.address();
|
|
@@ -19207,7 +19434,7 @@ function toAbsoluteWithBase(value, baseDir) {
|
|
|
19207
19434
|
if (trimmed.length === 0 || isAbsolute(trimmed)) {
|
|
19208
19435
|
return value;
|
|
19209
19436
|
}
|
|
19210
|
-
return
|
|
19437
|
+
return resolve2(baseDir, trimmed);
|
|
19211
19438
|
}
|
|
19212
19439
|
function applyToolContextDefaults(toolName, rawArgs, context, fallbackBaseDir, sessionWorkspaceBySession) {
|
|
19213
19440
|
const baseDir = resolveToolContextBaseDirWithSession(context, fallbackBaseDir, sessionWorkspaceBySession);
|
|
@@ -19254,7 +19481,7 @@ function buildToolHookEntries(registry2, fallbackBaseDir) {
|
|
|
19254
19481
|
const normalizedArgs = applyToolContextDefaults(toolName, args, context, fallbackBaseDir, sessionWorkspaceBySession);
|
|
19255
19482
|
return await handler(normalizedArgs);
|
|
19256
19483
|
} catch (error45) {
|
|
19257
|
-
|
|
19484
|
+
log16.debug("Tool hook execution failed", { tool: toolName, error: String(error45?.message || error45) });
|
|
19258
19485
|
throw error45;
|
|
19259
19486
|
}
|
|
19260
19487
|
}
|
|
@@ -19266,9 +19493,9 @@ function buildToolHookEntries(registry2, fallbackBaseDir) {
|
|
|
19266
19493
|
}
|
|
19267
19494
|
return entries;
|
|
19268
19495
|
}
|
|
19269
|
-
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 }) => {
|
|
19270
19497
|
const workspaceDirectory = resolveWorkspaceDirectory(worktree, directory);
|
|
19271
|
-
|
|
19498
|
+
log16.debug("Plugin initializing", {
|
|
19272
19499
|
directory,
|
|
19273
19500
|
worktree,
|
|
19274
19501
|
workspaceDirectory,
|
|
@@ -19276,22 +19503,22 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19276
19503
|
serverUrl: serverUrl?.toString()
|
|
19277
19504
|
});
|
|
19278
19505
|
if (!TOOL_LOOP_MODE_VALID) {
|
|
19279
|
-
|
|
19506
|
+
log16.warn("Invalid CURSOR_ACP_TOOL_LOOP_MODE; defaulting to opencode", { value: TOOL_LOOP_MODE_RAW });
|
|
19280
19507
|
}
|
|
19281
19508
|
if (!PROVIDER_BOUNDARY_MODE_VALID) {
|
|
19282
|
-
|
|
19509
|
+
log16.warn("Invalid CURSOR_ACP_PROVIDER_BOUNDARY; defaulting to v1", {
|
|
19283
19510
|
value: PROVIDER_BOUNDARY_MODE_RAW
|
|
19284
19511
|
});
|
|
19285
19512
|
}
|
|
19286
19513
|
if (!TOOL_LOOP_MAX_REPEAT_VALID) {
|
|
19287
|
-
|
|
19514
|
+
log16.warn("Invalid CURSOR_ACP_TOOL_LOOP_MAX_REPEAT; defaulting to 3", {
|
|
19288
19515
|
value: TOOL_LOOP_MAX_REPEAT_RAW
|
|
19289
19516
|
});
|
|
19290
19517
|
}
|
|
19291
19518
|
if (ENABLE_PROVIDER_BOUNDARY_AUTOFALLBACK && PROVIDER_BOUNDARY.mode !== "v1") {
|
|
19292
|
-
|
|
19519
|
+
log16.debug("Provider boundary auto-fallback is enabled but inactive unless mode=v1");
|
|
19293
19520
|
}
|
|
19294
|
-
|
|
19521
|
+
log16.info("Tool loop mode configured", {
|
|
19295
19522
|
mode: TOOL_LOOP_MODE,
|
|
19296
19523
|
providerBoundary: PROVIDER_BOUNDARY.mode,
|
|
19297
19524
|
proxyExecToolCalls: PROXY_EXECUTE_TOOL_CALLS,
|
|
@@ -19299,14 +19526,14 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19299
19526
|
toolLoopMaxRepeat: TOOL_LOOP_MAX_REPEAT
|
|
19300
19527
|
});
|
|
19301
19528
|
await ensurePluginDirectory();
|
|
19302
|
-
|
|
19529
|
+
autoRefreshModels().catch(() => {});
|
|
19303
19530
|
toastService.setClient(client3);
|
|
19304
19531
|
const toolsEnabled = process.env.CURSOR_ACP_ENABLE_OPENCODE_TOOLS !== "false";
|
|
19305
19532
|
const legacyProxyToolPathsEnabled = toolsEnabled && TOOL_LOOP_MODE === "proxy-exec";
|
|
19306
19533
|
if (toolsEnabled && TOOL_LOOP_MODE === "opencode") {
|
|
19307
|
-
|
|
19534
|
+
log16.debug("OpenCode mode active; skipping legacy SDK/MCP discovery and proxy-side tool execution");
|
|
19308
19535
|
} else if (toolsEnabled && TOOL_LOOP_MODE === "off") {
|
|
19309
|
-
|
|
19536
|
+
log16.debug("Tool loop mode off; proxy-side tool execution disabled");
|
|
19310
19537
|
}
|
|
19311
19538
|
const serverClient = legacyProxyToolPathsEnabled ? createOpencodeClient({ baseUrl: serverUrl.toString(), directory: workspaceDirectory }) : null;
|
|
19312
19539
|
const discovery = legacyProxyToolPathsEnabled ? new OpenCodeToolDiscovery(serverClient ?? client3) : null;
|
|
@@ -19358,7 +19585,7 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19358
19585
|
discoveredList = await discovery.listTools();
|
|
19359
19586
|
discoveredList.forEach((t) => toolsByName.set(t.name, t));
|
|
19360
19587
|
} catch (err) {
|
|
19361
|
-
|
|
19588
|
+
log16.debug("Tool discovery failed, using local tools only", { error: String(err) });
|
|
19362
19589
|
}
|
|
19363
19590
|
}
|
|
19364
19591
|
const allTools = [...localTools, ...discoveredList];
|
|
@@ -19388,16 +19615,16 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19388
19615
|
}
|
|
19389
19616
|
lastToolNames = toolEntries.map((e) => e.function.name);
|
|
19390
19617
|
lastToolMap = allTools.map((t) => ({ id: t.id, name: t.name }));
|
|
19391
|
-
|
|
19618
|
+
log16.debug("Tools refreshed", { local: localTools.length, discovered: discoveredList.length, total: toolEntries.length });
|
|
19392
19619
|
return toolEntries;
|
|
19393
19620
|
}
|
|
19394
19621
|
const proxyBaseURL = await ensureCursorProxyServer(workspaceDirectory, router);
|
|
19395
|
-
|
|
19622
|
+
log16.debug("Proxy server started", { baseURL: proxyBaseURL });
|
|
19396
19623
|
const toolHookEntries = buildToolHookEntries(localRegistry, workspaceDirectory);
|
|
19397
19624
|
return {
|
|
19398
19625
|
tool: toolHookEntries,
|
|
19399
19626
|
auth: {
|
|
19400
|
-
provider:
|
|
19627
|
+
provider: CURSOR_PROVIDER_ID2,
|
|
19401
19628
|
async loader(_getAuth) {
|
|
19402
19629
|
return {};
|
|
19403
19630
|
},
|
|
@@ -19407,9 +19634,9 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19407
19634
|
type: "oauth",
|
|
19408
19635
|
async authorize() {
|
|
19409
19636
|
try {
|
|
19410
|
-
|
|
19637
|
+
log16.info("Starting OAuth flow");
|
|
19411
19638
|
const { url: url2, instructions, callback } = await startCursorOAuth();
|
|
19412
|
-
|
|
19639
|
+
log16.debug("Got OAuth URL", { url: url2.substring(0, 50) + "..." });
|
|
19413
19640
|
return {
|
|
19414
19641
|
url: url2,
|
|
19415
19642
|
instructions,
|
|
@@ -19417,7 +19644,7 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19417
19644
|
callback
|
|
19418
19645
|
};
|
|
19419
19646
|
} catch (error45) {
|
|
19420
|
-
|
|
19647
|
+
log16.error("OAuth error", { error: error45 });
|
|
19421
19648
|
throw error45;
|
|
19422
19649
|
}
|
|
19423
19650
|
}
|
|
@@ -19441,10 +19668,10 @@ var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19441
19668
|
output.options.tools = resolved.tools;
|
|
19442
19669
|
} else if (resolved.action === "preserve") {
|
|
19443
19670
|
const count = Array.isArray(existingTools) ? existingTools.length : 0;
|
|
19444
|
-
|
|
19671
|
+
log16.debug("Using OpenCode-provided tools from chat.params", { count });
|
|
19445
19672
|
}
|
|
19446
19673
|
} catch (err) {
|
|
19447
|
-
|
|
19674
|
+
log16.debug("Failed to refresh tools", { error: String(err) });
|
|
19448
19675
|
}
|
|
19449
19676
|
}
|
|
19450
19677
|
},
|
|
@@ -19470,6 +19697,7 @@ var init_plugin = __esm(() => {
|
|
|
19470
19697
|
init_discovery();
|
|
19471
19698
|
init_schema();
|
|
19472
19699
|
init_router();
|
|
19700
|
+
init_sync();
|
|
19473
19701
|
init_dist2();
|
|
19474
19702
|
init_local();
|
|
19475
19703
|
init_sdk();
|
|
@@ -19480,10 +19708,10 @@ var init_plugin = __esm(() => {
|
|
|
19480
19708
|
init_toast_service();
|
|
19481
19709
|
init_tool_schema_compat();
|
|
19482
19710
|
init_tool_loop_guard();
|
|
19483
|
-
|
|
19484
|
-
DEBUG_LOG_DIR2 =
|
|
19485
|
-
DEBUG_LOG_FILE2 =
|
|
19486
|
-
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}/`;
|
|
19487
19715
|
CURSOR_PROXY_DEFAULT_BASE_URL = `http://${CURSOR_PROXY_HOST}:${CURSOR_PROXY_DEFAULT_PORT}/v1`;
|
|
19488
19716
|
REUSE_EXISTING_PROXY = process.env.CURSOR_ACP_REUSE_EXISTING_PROXY !== "false";
|
|
19489
19717
|
FORCE_TOOL_MODE = process.env.CURSOR_ACP_FORCE !== "false";
|
|
@@ -19496,8 +19724,8 @@ var init_plugin = __esm(() => {
|
|
|
19496
19724
|
mode: PROVIDER_BOUNDARY_MODE,
|
|
19497
19725
|
valid: PROVIDER_BOUNDARY_MODE_VALID
|
|
19498
19726
|
} = parseProviderBoundaryMode(PROVIDER_BOUNDARY_MODE_RAW));
|
|
19499
|
-
LEGACY_PROVIDER_BOUNDARY = createProviderBoundary("legacy",
|
|
19500
|
-
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);
|
|
19501
19729
|
ENABLE_PROVIDER_BOUNDARY_AUTOFALLBACK = process.env.CURSOR_ACP_PROVIDER_BOUNDARY_AUTOFALLBACK !== "false";
|
|
19502
19730
|
TOOL_LOOP_MAX_REPEAT_RAW = process.env.CURSOR_ACP_TOOL_LOOP_MAX_REPEAT;
|
|
19503
19731
|
({
|
|
@@ -19579,20 +19807,20 @@ class SimpleCursorClient {
|
|
|
19579
19807
|
child.kill("SIGTERM");
|
|
19580
19808
|
processError = new Error(`Timeout after ${this.config.timeout}ms`);
|
|
19581
19809
|
}, this.config.timeout);
|
|
19582
|
-
const streamEnded = new Promise((
|
|
19810
|
+
const streamEnded = new Promise((resolve3) => {
|
|
19583
19811
|
child.on("close", (code) => {
|
|
19584
19812
|
clearTimeout(timeoutId);
|
|
19585
19813
|
if (code !== 0 && !processError) {
|
|
19586
19814
|
this.log.error("cursor-agent exited with non-zero code", { code });
|
|
19587
19815
|
processError = new Error(`cursor-agent exited with code ${code}`);
|
|
19588
19816
|
}
|
|
19589
|
-
|
|
19817
|
+
resolve3(code);
|
|
19590
19818
|
});
|
|
19591
19819
|
child.on("error", (error45) => {
|
|
19592
19820
|
clearTimeout(timeoutId);
|
|
19593
19821
|
this.log.error("cursor-agent process error", { error: error45.message });
|
|
19594
19822
|
processError = error45;
|
|
19595
|
-
|
|
19823
|
+
resolve3(null);
|
|
19596
19824
|
});
|
|
19597
19825
|
});
|
|
19598
19826
|
for await (const chunk of child.stdout) {
|
|
@@ -19645,7 +19873,7 @@ class SimpleCursorClient {
|
|
|
19645
19873
|
args.push("--resume", resumeId);
|
|
19646
19874
|
}
|
|
19647
19875
|
this.log.debug("Executing prompt", { promptLength: prompt.length, mode, model });
|
|
19648
|
-
return new Promise((
|
|
19876
|
+
return new Promise((resolve3, reject) => {
|
|
19649
19877
|
const child = spawn2(this.config.cursorAgentPath, args, {
|
|
19650
19878
|
cwd,
|
|
19651
19879
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -19684,7 +19912,7 @@ class SimpleCursorClient {
|
|
|
19684
19912
|
}
|
|
19685
19913
|
}
|
|
19686
19914
|
}
|
|
19687
|
-
|
|
19915
|
+
resolve3({
|
|
19688
19916
|
content,
|
|
19689
19917
|
done: true
|
|
19690
19918
|
});
|
|
@@ -19701,11 +19929,24 @@ class SimpleCursorClient {
|
|
|
19701
19929
|
async getAvailableModels() {
|
|
19702
19930
|
return [
|
|
19703
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" },
|
|
19704
19944
|
{ id: "gpt-5.2", name: "GPT-5.2" },
|
|
19945
|
+
{ id: "gemini-3.1-pro", name: "Gemini 3.1 Pro" },
|
|
19705
19946
|
{ id: "gemini-3-pro", name: "Gemini 3 Pro" },
|
|
19706
|
-
{ id: "
|
|
19707
|
-
{ id: "
|
|
19708
|
-
{ id: "
|
|
19947
|
+
{ id: "gemini-3-flash", name: "Gemini 3 Flash" },
|
|
19948
|
+
{ id: "grok", name: "Grok" },
|
|
19949
|
+
{ id: "kimi-k2.5", name: "Kimi K2.5" }
|
|
19709
19950
|
];
|
|
19710
19951
|
}
|
|
19711
19952
|
async validateInstallation() {
|
|
@@ -19724,19 +19965,19 @@ init_logger();
|
|
|
19724
19965
|
import { execSync } from "node:child_process";
|
|
19725
19966
|
import { createServer } from "node:net";
|
|
19726
19967
|
import { platform as platform2 } from "node:os";
|
|
19727
|
-
var
|
|
19968
|
+
var log17 = createLogger("proxy-server");
|
|
19728
19969
|
var DEFAULT_PORT = 32124;
|
|
19729
19970
|
var PORT_RANGE_SIZE = 256;
|
|
19730
19971
|
async function isPortAvailable(port, host) {
|
|
19731
|
-
return await new Promise((
|
|
19972
|
+
return await new Promise((resolve3) => {
|
|
19732
19973
|
const server2 = createServer();
|
|
19733
19974
|
server2.unref();
|
|
19734
19975
|
server2.once("error", () => {
|
|
19735
|
-
|
|
19976
|
+
resolve3(false);
|
|
19736
19977
|
});
|
|
19737
19978
|
server2.listen({ port, host }, () => {
|
|
19738
19979
|
server2.close(() => {
|
|
19739
|
-
|
|
19980
|
+
resolve3(true);
|
|
19740
19981
|
});
|
|
19741
19982
|
});
|
|
19742
19983
|
});
|
|
@@ -19771,11 +20012,11 @@ function getUsedPortsInRange(minPort, maxPort) {
|
|
|
19771
20012
|
}
|
|
19772
20013
|
}
|
|
19773
20014
|
} else {
|
|
19774
|
-
|
|
20015
|
+
log17.debug(`Port detection not supported on ${os2}. Using probe-based discovery.`);
|
|
19775
20016
|
}
|
|
19776
20017
|
} catch (error45) {
|
|
19777
20018
|
const msg = error45 instanceof Error ? error45.message : String(error45);
|
|
19778
|
-
|
|
20019
|
+
log17.debug(`Port detection failed: ${msg}. Using probe-based discovery.`);
|
|
19779
20020
|
}
|
|
19780
20021
|
return used;
|
|
19781
20022
|
}
|
|
@@ -19826,7 +20067,7 @@ function createProxyServer(config2) {
|
|
|
19826
20067
|
const err = error45 instanceof Error ? error45 : new Error(String(error45));
|
|
19827
20068
|
const isPortInUse = err.message.includes("EADDRINUSE") || err.message.includes("address already in use") || err.message.includes("port is already in use");
|
|
19828
20069
|
if (!isPortInUse) {
|
|
19829
|
-
|
|
20070
|
+
log17.debug(`Unexpected error starting on port ${port}: ${err.message}`);
|
|
19830
20071
|
}
|
|
19831
20072
|
return { success: false, error: err };
|
|
19832
20073
|
}
|
|
@@ -19842,13 +20083,13 @@ function createProxyServer(config2) {
|
|
|
19842
20083
|
if (result.success) {
|
|
19843
20084
|
port = requestedPort;
|
|
19844
20085
|
} else {
|
|
19845
|
-
|
|
20086
|
+
log17.debug(`Requested port ${requestedPort} unavailable: ${result.error?.message ?? "unknown"}. Falling back to automatic port selection.`);
|
|
19846
20087
|
port = await findAvailablePort(host);
|
|
19847
20088
|
const fallbackResult = tryStart(port);
|
|
19848
20089
|
if (!fallbackResult.success) {
|
|
19849
20090
|
throw new Error(`Failed to start server on port ${requestedPort} (${result.error?.message ?? "unknown"}) ` + `and fallback port ${port} (${fallbackResult.error?.message ?? "unknown"})`);
|
|
19850
20091
|
}
|
|
19851
|
-
|
|
20092
|
+
log17.debug(`Server started on fallback port ${port} instead of requested port ${requestedPort}`);
|
|
19852
20093
|
}
|
|
19853
20094
|
} else {
|
|
19854
20095
|
port = await findAvailablePort(host);
|
|
@@ -20219,12 +20460,12 @@ init_auth();
|
|
|
20219
20460
|
// src/commands/status.ts
|
|
20220
20461
|
init_auth();
|
|
20221
20462
|
init_logger();
|
|
20222
|
-
import { existsSync as
|
|
20223
|
-
var
|
|
20463
|
+
import { existsSync as existsSync6 } from "fs";
|
|
20464
|
+
var log18 = createLogger("status");
|
|
20224
20465
|
function checkAuthStatus() {
|
|
20225
20466
|
const authFilePath = getAuthFilePath();
|
|
20226
|
-
const exists =
|
|
20227
|
-
|
|
20467
|
+
const exists = existsSync6(authFilePath);
|
|
20468
|
+
log18.debug("Checking auth status", { path: authFilePath });
|
|
20228
20469
|
if (exists) {
|
|
20229
20470
|
return {
|
|
20230
20471
|
authenticated: true,
|