@rama_nigg/open-cursor 2.3.20 → 2.4.1

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/dist/index.js CHANGED
@@ -421,11 +421,52 @@ function formatErrorForUser(error) {
421
421
  return output;
422
422
  }
423
423
 
424
+ // src/utils/binary.ts
425
+ import { existsSync as fsExistsSync } from "fs";
426
+ import * as pathModule from "path";
427
+ import { homedir as osHomedir } from "os";
428
+ function resolveCursorAgentBinary(deps = {}) {
429
+ const platform = deps.platform ?? process.platform;
430
+ const env = deps.env ?? process.env;
431
+ const checkExists = deps.existsSync ?? fsExistsSync;
432
+ const home = (deps.homedir ?? osHomedir)();
433
+ const envOverride = env.CURSOR_AGENT_EXECUTABLE;
434
+ if (envOverride && envOverride.length > 0) {
435
+ return envOverride;
436
+ }
437
+ if (platform === "win32") {
438
+ const pathJoin = pathModule.win32.join;
439
+ const localAppData = env.LOCALAPPDATA ?? pathJoin(home, "AppData", "Local");
440
+ const knownPath = pathJoin(localAppData, "cursor-agent", "cursor-agent.cmd");
441
+ if (checkExists(knownPath)) {
442
+ return knownPath;
443
+ }
444
+ log.warn("cursor-agent not found at known Windows path, falling back to PATH", { checkedPath: knownPath });
445
+ return "cursor-agent.cmd";
446
+ }
447
+ const knownPaths = [
448
+ pathModule.join(home, ".cursor-agent", "cursor-agent"),
449
+ "/usr/local/bin/cursor-agent"
450
+ ];
451
+ for (const p of knownPaths) {
452
+ if (checkExists(p)) {
453
+ return p;
454
+ }
455
+ }
456
+ log.warn("cursor-agent not found at known paths, falling back to PATH", { checkedPaths: knownPaths });
457
+ return "cursor-agent";
458
+ }
459
+ var log;
460
+ var init_binary = __esm(() => {
461
+ init_logger();
462
+ log = createLogger("binary");
463
+ });
464
+
424
465
  // src/auth.ts
425
466
  import { spawn } from "child_process";
426
467
  import { existsSync as existsSync2 } from "fs";
427
468
  import { homedir as homedir2, platform } from "os";
428
- import { join as join2 } from "path";
469
+ import { join as join3 } from "path";
429
470
  function getHomeDir() {
430
471
  const override = process.env.CURSOR_ACP_HOME_DIR;
431
472
  if (override && override.length > 0) {
@@ -441,18 +482,18 @@ async function pollForAuthFile(timeoutMs = AUTH_POLL_TIMEOUT, intervalMs = AUTH_
441
482
  const elapsed = Date.now() - startTime;
442
483
  for (const authPath of possiblePaths) {
443
484
  if (existsSync2(authPath)) {
444
- log.debug("Auth file detected", { path: authPath });
485
+ log2.debug("Auth file detected", { path: authPath });
445
486
  resolve(true);
446
487
  return;
447
488
  }
448
489
  }
449
- log.debug("Polling for auth file", {
490
+ log2.debug("Polling for auth file", {
450
491
  checkedPaths: possiblePaths,
451
492
  elapsed: `${elapsed}ms`,
452
493
  timeout: `${timeoutMs}ms`
453
494
  });
454
495
  if (elapsed >= timeoutMs) {
455
- log.debug("Auth file polling timed out");
496
+ log2.debug("Auth file polling timed out");
456
497
  resolve(false);
457
498
  return;
458
499
  }
@@ -463,9 +504,10 @@ async function pollForAuthFile(timeoutMs = AUTH_POLL_TIMEOUT, intervalMs = AUTH_
463
504
  }
464
505
  async function startCursorOAuth() {
465
506
  return new Promise((resolve, reject) => {
466
- log.info("Starting cursor-cli login process");
467
- const proc = spawn("cursor-agent", ["login"], {
468
- stdio: ["pipe", "pipe", "pipe"]
507
+ log2.info("Starting cursor-cli login process");
508
+ const proc = spawn(resolveCursorAgentBinary(), ["login"], {
509
+ stdio: ["pipe", "pipe", "pipe"],
510
+ shell: process.platform === "win32"
469
511
  });
470
512
  let stdout = "";
471
513
  let stderr = "";
@@ -489,9 +531,9 @@ async function startCursorOAuth() {
489
531
  const url = extractUrl();
490
532
  if (url && !urlExtracted) {
491
533
  urlExtracted = true;
492
- log.debug("Captured stdout", { length: stdout.length });
493
- log.debug("Extracted URL", { url: url.substring(0, 50) + "..." });
494
- log.info("Got login URL, waiting for browser auth");
534
+ log2.debug("Captured stdout", { length: stdout.length });
535
+ log2.debug("Extracted URL", { url: url.substring(0, 50) + "..." });
536
+ log2.info("Got login URL, waiting for browser auth");
495
537
  resolve({
496
538
  url,
497
539
  instructions: "Click 'Continue with Cursor' in your browser to authenticate",
@@ -505,26 +547,26 @@ async function startCursorOAuth() {
505
547
  }
506
548
  };
507
549
  proc.on("close", async (code) => {
508
- log.debug("Login process closed", { code });
550
+ log2.debug("Login process closed", { code });
509
551
  if (code === 0) {
510
- log.info("Process exited successfully, polling for auth file...");
552
+ log2.info("Process exited successfully, polling for auth file...");
511
553
  const isAuthenticated = await pollForAuthFile();
512
554
  if (isAuthenticated) {
513
- log.info("Authentication successful");
555
+ log2.info("Authentication successful");
514
556
  resolveOnce({
515
557
  type: "success",
516
558
  provider: "cursor-acp",
517
559
  key: "cursor-auth"
518
560
  });
519
561
  } else {
520
- log.warn("Auth file not found after polling");
562
+ log2.warn("Auth file not found after polling");
521
563
  resolveOnce({
522
564
  type: "failed",
523
565
  error: "Authentication was not completed. Please try again."
524
566
  });
525
567
  }
526
568
  } else {
527
- log.warn("Login process failed", { code });
569
+ log2.warn("Login process failed", { code });
528
570
  resolveOnce({
529
571
  type: "failed",
530
572
  error: stderr ? stripAnsi(stderr) : `Authentication failed with code ${code}`
@@ -532,7 +574,7 @@ async function startCursorOAuth() {
532
574
  }
533
575
  });
534
576
  setTimeout(() => {
535
- log.warn("Authentication timed out after 5 minutes");
577
+ log2.warn("Authentication timed out after 5 minutes");
536
578
  proc.kill();
537
579
  resolveOnce({
538
580
  type: "failed",
@@ -552,7 +594,7 @@ async function startCursorOAuth() {
552
594
  if (elapsed >= URL_EXTRACTION_TIMEOUT) {
553
595
  proc.kill();
554
596
  const errorMsg = stderr ? stripAnsi(stderr) : "No login URL received within timeout";
555
- log.error("Failed to extract login URL", { error: errorMsg, elapsed: `${elapsed}ms` });
597
+ log2.error("Failed to extract login URL", { error: errorMsg, elapsed: `${elapsed}ms` });
556
598
  reject(new Error(`Failed to get login URL: ${errorMsg}`));
557
599
  return;
558
600
  }
@@ -568,11 +610,11 @@ function verifyCursorAuth() {
568
610
  const possiblePaths = getPossibleAuthPaths();
569
611
  for (const authPath of possiblePaths) {
570
612
  if (existsSync2(authPath)) {
571
- log.debug("Auth file found", { path: authPath });
613
+ log2.debug("Auth file found", { path: authPath });
572
614
  return true;
573
615
  }
574
616
  }
575
- log.debug("No auth file found", { checkedPaths: possiblePaths });
617
+ log2.debug("No auth file found", { checkedPaths: possiblePaths });
576
618
  return false;
577
619
  }
578
620
  function getPossibleAuthPaths() {
@@ -582,23 +624,23 @@ function getPossibleAuthPaths() {
582
624
  const authFiles = ["cli-config.json", "auth.json"];
583
625
  if (isDarwin) {
584
626
  for (const file of authFiles) {
585
- paths.push(join2(home, ".cursor", file));
627
+ paths.push(join3(home, ".cursor", file));
586
628
  }
587
629
  for (const file of authFiles) {
588
- paths.push(join2(home, ".config", "cursor", file));
630
+ paths.push(join3(home, ".config", "cursor", file));
589
631
  }
590
632
  } else {
591
633
  for (const file of authFiles) {
592
- paths.push(join2(home, ".config", "cursor", file));
634
+ paths.push(join3(home, ".config", "cursor", file));
593
635
  }
594
636
  const xdgConfig = process.env.XDG_CONFIG_HOME;
595
- if (xdgConfig && xdgConfig !== join2(home, ".config")) {
637
+ if (xdgConfig && xdgConfig !== join3(home, ".config")) {
596
638
  for (const file of authFiles) {
597
- paths.push(join2(xdgConfig, "cursor", file));
639
+ paths.push(join3(xdgConfig, "cursor", file));
598
640
  }
599
641
  }
600
642
  for (const file of authFiles) {
601
- paths.push(join2(home, ".cursor", file));
643
+ paths.push(join3(home, ".cursor", file));
602
644
  }
603
645
  }
604
646
  return paths;
@@ -612,10 +654,11 @@ function getAuthFilePath() {
612
654
  }
613
655
  return possiblePaths[0];
614
656
  }
615
- var log, AUTH_POLL_INTERVAL = 2000, AUTH_POLL_TIMEOUT, URL_EXTRACTION_TIMEOUT = 1e4;
657
+ var log2, AUTH_POLL_INTERVAL = 2000, AUTH_POLL_TIMEOUT, URL_EXTRACTION_TIMEOUT = 1e4;
616
658
  var init_auth = __esm(() => {
617
659
  init_logger();
618
- log = createLogger("auth");
660
+ init_binary();
661
+ log2 = createLogger("auth");
619
662
  AUTH_POLL_TIMEOUT = 5 * 60 * 1000;
620
663
  });
621
664
 
@@ -662,7 +705,7 @@ var hasTextContent = (event) => event.message.content.some((content) => content.
662
705
  return true;
663
706
  }
664
707
  return event.type === "assistant" && hasThinkingContent(event);
665
- }, isToolCall = (event) => event.type === "tool_call", extractText = (event) => event.message.content.filter((content) => content.type === "text").map((content) => content.text).join(""), extractThinking = (event) => {
708
+ }, isToolCall = (event) => event.type === "tool_call", isResult = (event) => event.type === "result", extractText = (event) => event.message.content.filter((content) => content.type === "text").map((content) => content.text).join(""), extractThinking = (event) => {
666
709
  if (event.type === "thinking") {
667
710
  return event.text ?? "";
668
711
  }
@@ -811,7 +854,7 @@ var createChunk = (id, created, model, delta) => ({
811
854
  var init_openai_sse = () => {};
812
855
 
813
856
  // src/streaming/parser.ts
814
- var log2, parseStreamJsonLine = (line) => {
857
+ var log3, parseStreamJsonLine = (line) => {
815
858
  const trimmed = line.trim();
816
859
  if (!trimmed) {
817
860
  return null;
@@ -823,15 +866,82 @@ var log2, parseStreamJsonLine = (line) => {
823
866
  }
824
867
  return parsed;
825
868
  } catch {
826
- log2.debug("Failed to parse NDJSON line", { line: trimmed.substring(0, 100) });
869
+ log3.debug("Failed to parse NDJSON line", { line: trimmed.substring(0, 100) });
827
870
  return null;
828
871
  }
829
872
  };
830
873
  var init_parser = __esm(() => {
831
874
  init_logger();
832
- log2 = createLogger("streaming:parser");
875
+ log3 = createLogger("streaming:parser");
833
876
  });
834
877
 
878
+ // src/usage.ts
879
+ function readTokenCount(value) {
880
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
881
+ return 0;
882
+ }
883
+ return Math.floor(value);
884
+ }
885
+ function readOptionalCost(value) {
886
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
887
+ return;
888
+ }
889
+ return value;
890
+ }
891
+ function normalizeCursorUsage(value) {
892
+ if (!value || typeof value !== "object") {
893
+ return;
894
+ }
895
+ const usage = value;
896
+ const metrics = {
897
+ inputTokens: readTokenCount(usage.inputTokens ?? usage.input_tokens ?? usage.prompt_tokens),
898
+ outputTokens: readTokenCount(usage.outputTokens ?? usage.output_tokens ?? usage.completion_tokens),
899
+ reasoningTokens: readTokenCount(usage.reasoningTokens ?? usage.reasoning_tokens),
900
+ cacheReadTokens: readTokenCount(usage.cacheReadTokens ?? usage.cache_read_tokens),
901
+ cacheWriteTokens: readTokenCount(usage.cacheWriteTokens ?? usage.cache_write_tokens)
902
+ };
903
+ const cost = readOptionalCost(usage.cost ?? usage.totalCost ?? usage.total_cost);
904
+ if (cost !== undefined) {
905
+ metrics.cost = cost;
906
+ }
907
+ const hasUsage = metrics.inputTokens > 0 || metrics.outputTokens > 0 || metrics.reasoningTokens > 0 || metrics.cacheReadTokens > 0 || metrics.cacheWriteTokens > 0 || cost !== undefined;
908
+ return hasUsage ? metrics : undefined;
909
+ }
910
+ function createOpenAiUsage(metrics) {
911
+ const promptTokens = metrics.inputTokens + metrics.cacheReadTokens + metrics.cacheWriteTokens;
912
+ const totalTokens = promptTokens + metrics.outputTokens + metrics.reasoningTokens;
913
+ const usage = {
914
+ prompt_tokens: promptTokens,
915
+ completion_tokens: metrics.outputTokens,
916
+ total_tokens: totalTokens,
917
+ prompt_tokens_details: {
918
+ cached_tokens: metrics.cacheReadTokens,
919
+ cache_write_tokens: metrics.cacheWriteTokens
920
+ },
921
+ completion_tokens_details: {
922
+ reasoning_tokens: metrics.reasoningTokens
923
+ }
924
+ };
925
+ if (metrics.cost !== undefined) {
926
+ usage.cost = metrics.cost;
927
+ }
928
+ return usage;
929
+ }
930
+ function extractOpenAiUsageFromResult(event) {
931
+ const metrics = normalizeCursorUsage(event.usage);
932
+ return metrics ? createOpenAiUsage(metrics) : undefined;
933
+ }
934
+ function createChatCompletionUsageChunk(id, created, model, usage) {
935
+ return {
936
+ id,
937
+ object: "chat.completion.chunk",
938
+ created,
939
+ model,
940
+ choices: [],
941
+ usage
942
+ };
943
+ }
944
+
835
945
  // src/utils/perf.ts
836
946
  class RequestPerf {
837
947
  markers = [];
@@ -852,7 +962,7 @@ class RequestPerf {
852
962
  phases[this.markers[i].name] = this.markers[i].ts - this.markers[i - 1].ts;
853
963
  }
854
964
  const total = this.markers[this.markers.length - 1].ts - start;
855
- log3.debug("Request timing", { requestId: this.requestId, total, phases });
965
+ log4.debug("Request timing", { requestId: this.requestId, total, phases });
856
966
  }
857
967
  elapsed() {
858
968
  return this.markers.length > 0 ? Date.now() - this.markers[0].ts : 0;
@@ -861,16 +971,16 @@ class RequestPerf {
861
971
  return this.markers;
862
972
  }
863
973
  }
864
- var log3;
974
+ var log4;
865
975
  var init_perf = __esm(() => {
866
976
  init_logger();
867
- log3 = createLogger("perf");
977
+ log4 = createLogger("perf");
868
978
  });
869
979
 
870
980
  // src/proxy/prompt-builder.ts
871
981
  import { appendFileSync as appendFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "node:fs";
872
982
  import { homedir as homedir3 } from "node:os";
873
- import { join as join3 } from "node:path";
983
+ import { join as join4 } from "node:path";
874
984
  function ensureLogDir2() {
875
985
  try {
876
986
  if (!existsSync3(DEBUG_LOG_DIR)) {
@@ -886,7 +996,7 @@ function debugLogToFile(message, data) {
886
996
  `;
887
997
  appendFileSync2(DEBUG_LOG_FILE, logLine);
888
998
  } catch (err) {
889
- log4.debug(message, data);
999
+ log5.debug(message, data);
890
1000
  }
891
1001
  }
892
1002
  function buildPromptFromMessages(messages, tools, subagentNames = []) {
@@ -1007,12 +1117,12 @@ ${toolDescs}`);
1007
1117
  });
1008
1118
  return finalPrompt;
1009
1119
  }
1010
- var log4, DEBUG_LOG_DIR, DEBUG_LOG_FILE;
1120
+ var log5, DEBUG_LOG_DIR, DEBUG_LOG_FILE;
1011
1121
  var init_prompt_builder = __esm(() => {
1012
1122
  init_logger();
1013
- log4 = createLogger("proxy:prompt-builder");
1014
- DEBUG_LOG_DIR = join3(homedir3(), ".config", "opencode", "logs");
1015
- DEBUG_LOG_FILE = join3(DEBUG_LOG_DIR, "tool-loop-debug.log");
1123
+ log5 = createLogger("proxy:prompt-builder");
1124
+ DEBUG_LOG_DIR = join4(homedir3(), ".config", "opencode", "logs");
1125
+ DEBUG_LOG_FILE = join4(DEBUG_LOG_DIR, "tool-loop-debug.log");
1016
1126
  });
1017
1127
 
1018
1128
  // src/proxy/tool-loop.ts
@@ -1040,7 +1150,7 @@ function extractOpenAiToolCall(event, allowedToolNames) {
1040
1150
  const resolvedName = resolveAllowedToolName(name, allowedToolNames);
1041
1151
  if (resolvedName) {
1042
1152
  if (args === undefined && event.subtype === "started") {
1043
- log5.debug("Tool call args extraction returned undefined", {
1153
+ log6.debug("Tool call args extraction returned undefined", {
1044
1154
  toolName: name,
1045
1155
  subtype: event.subtype ?? "none",
1046
1156
  payloadKeys: Object.entries(event.tool_call || {}).map(([k, v]) => `${k}:[${isRecord(v) ? Object.keys(v).join(",") : typeof v}]`),
@@ -1060,7 +1170,7 @@ function extractOpenAiToolCall(event, allowedToolNames) {
1060
1170
  }
1061
1171
  };
1062
1172
  }
1063
- log5.debug("Tool call not in allowlist; passing through to cursor-agent", {
1173
+ log6.debug("Tool call not in allowlist; passing through to cursor-agent", {
1064
1174
  name,
1065
1175
  normalized: normalizeAliasKey(name),
1066
1176
  allowedToolCount: allowedToolNames.size
@@ -1209,10 +1319,10 @@ function toOpenAiArguments(args) {
1209
1319
  function isRecord(value) {
1210
1320
  return typeof value === "object" && value !== null && !Array.isArray(value);
1211
1321
  }
1212
- var log5, TOOL_NAME_ALIASES;
1322
+ var log6, TOOL_NAME_ALIASES;
1213
1323
  var init_tool_loop = __esm(() => {
1214
1324
  init_logger();
1215
- log5 = createLogger("proxy:tool-loop");
1325
+ log6 = createLogger("proxy:tool-loop");
1216
1326
  TOOL_NAME_ALIASES = new Map([
1217
1327
  ["runcommand", "bash"],
1218
1328
  ["executecommand", "bash"],
@@ -1326,7 +1436,7 @@ class OpenCodeToolDiscovery {
1326
1436
  const mcpTools = await this.tryListMcpTools();
1327
1437
  tools = tools.concat(mcpTools);
1328
1438
  } catch (err) {
1329
- log6.debug("SDK tool.list failed, will try CLI", { error: String(err) });
1439
+ log7.debug("SDK tool.list failed, will try CLI", { error: String(err) });
1330
1440
  }
1331
1441
  }
1332
1442
  if (tools.length === 0 && this.executorPref !== "sdk") {
@@ -1338,10 +1448,10 @@ class OpenCodeToolDiscovery {
1338
1448
  if (parsed?.data?.tools?.length) {
1339
1449
  tools = parsed.data.tools.map((t) => this.normalize(t, "cli"));
1340
1450
  } else {
1341
- log6.debug("CLI tool list failed", { status: res.status, stderr: res.stderr });
1451
+ log7.debug("CLI tool list failed", { status: res.status, stderr: res.stderr });
1342
1452
  }
1343
1453
  } catch (err) {
1344
- log6.debug("CLI tool list error", { error: String(err) });
1454
+ log7.debug("CLI tool list error", { error: String(err) });
1345
1455
  }
1346
1456
  }
1347
1457
  const map = new Map;
@@ -1377,7 +1487,7 @@ class OpenCodeToolDiscovery {
1377
1487
  return [];
1378
1488
  return mcpList.data.tools.map((t) => this.normalize(t, "mcp"));
1379
1489
  } catch (err) {
1380
- log6.debug("MCP tool discovery skipped", { error: String(err) });
1490
+ log7.debug("MCP tool discovery skipped", { error: String(err) });
1381
1491
  return [];
1382
1492
  }
1383
1493
  }
@@ -1398,11 +1508,11 @@ class OpenCodeToolDiscovery {
1398
1508
  return null;
1399
1509
  }
1400
1510
  }
1401
- var log6;
1511
+ var log7;
1402
1512
  var init_discovery = __esm(() => {
1403
1513
  init_logger();
1404
1514
  init_strip_ansi();
1405
- log6 = createLogger("tools:discovery");
1515
+ log7 = createLogger("tools:discovery");
1406
1516
  });
1407
1517
 
1408
1518
  // src/tools/schema.ts
@@ -1459,10 +1569,10 @@ function describeTool(t) {
1459
1569
  const base = t.description || "OpenCode tool";
1460
1570
  return base.length > 400 ? base.slice(0, 400) : base;
1461
1571
  }
1462
- var log7;
1572
+ var log8;
1463
1573
  var init_schema = __esm(() => {
1464
1574
  init_logger();
1465
- log7 = createLogger("tools:schema");
1575
+ log8 = createLogger("tools:schema");
1466
1576
  });
1467
1577
 
1468
1578
  // src/tools/router.ts
@@ -1487,18 +1597,18 @@ class ToolRouter {
1487
1597
  }
1488
1598
  const tool = this.ctx.toolsByName.get(name);
1489
1599
  if (!tool) {
1490
- log8.warn("Unknown tool call", { name });
1600
+ log9.warn("Unknown tool call", { name });
1491
1601
  return this.buildResult(meta, callId, name, { status: "error", error: `Unknown tool ${name}` });
1492
1602
  }
1493
1603
  const args = this.extractArgs(event);
1494
- log8.debug("Executing tool", { name, toolId: tool.id });
1604
+ log9.debug("Executing tool", { name, toolId: tool.id });
1495
1605
  const t0 = Date.now();
1496
1606
  const result = await this.ctx.execute(tool.id, args);
1497
1607
  const elapsed = Date.now() - t0;
1498
1608
  if (result.status === "error") {
1499
- log8.warn("Tool execution returned error", { name, error: result.error, elapsed });
1609
+ log9.warn("Tool execution returned error", { name, error: result.error, elapsed });
1500
1610
  } else {
1501
- log8.debug("Tool execution completed", { name, toolId: tool.id, elapsed });
1611
+ log9.debug("Tool execution completed", { name, toolId: tool.id, elapsed });
1502
1612
  }
1503
1613
  return this.buildResult(meta, callId, name, result);
1504
1614
  }
@@ -1547,10 +1657,10 @@ class ToolRouter {
1547
1657
  };
1548
1658
  }
1549
1659
  }
1550
- var log8;
1660
+ var log9;
1551
1661
  var init_router = __esm(() => {
1552
1662
  init_logger();
1553
- log8 = createLogger("tools:router");
1663
+ log9 = createLogger("tools:router");
1554
1664
  });
1555
1665
 
1556
1666
  // src/tools/skills/loader.ts
@@ -1651,9 +1761,9 @@ function parseCursorModelsOutput(output) {
1651
1761
  return models;
1652
1762
  }
1653
1763
  function discoverModelsFromCursorAgent() {
1654
- const raw = execFileSync("cursor-agent", ["models"], {
1764
+ const raw = execFileSync(resolveCursorAgentBinary(), ["models"], {
1655
1765
  encoding: "utf8",
1656
- killSignal: "SIGTERM",
1766
+ ...process.platform !== "win32" && { killSignal: "SIGTERM" },
1657
1767
  stdio: ["ignore", "pipe", "pipe"],
1658
1768
  timeout: MODEL_DISCOVERY_TIMEOUT_MS
1659
1769
  });
@@ -1688,12 +1798,14 @@ function fallbackModels() {
1688
1798
  ];
1689
1799
  }
1690
1800
  var MODEL_DISCOVERY_TIMEOUT_MS = 5000;
1691
- var init_model_discovery = () => {};
1801
+ var init_model_discovery = __esm(() => {
1802
+ init_binary();
1803
+ });
1692
1804
 
1693
1805
  // src/plugin-toggle.ts
1694
1806
  import { existsSync as existsSync4, readFileSync } from "fs";
1695
1807
  import { homedir as homedir4 } from "os";
1696
- import { join as join4, resolve } from "path";
1808
+ import { join as join5, resolve } from "path";
1697
1809
  function matchesPlugin(entry) {
1698
1810
  if (entry === CURSOR_PROVIDER_ID)
1699
1811
  return true;
@@ -1707,14 +1819,19 @@ function resolveOpenCodeConfigPath(env = process.env) {
1707
1819
  if (env.OPENCODE_CONFIG && env.OPENCODE_CONFIG.length > 0) {
1708
1820
  return resolve(env.OPENCODE_CONFIG);
1709
1821
  }
1710
- const configHome = env.XDG_CONFIG_HOME && env.XDG_CONFIG_HOME.length > 0 ? env.XDG_CONFIG_HOME : join4(homedir4(), ".config");
1711
- return join4(configHome, "opencode", "opencode.json");
1822
+ const configHome = env.XDG_CONFIG_HOME && env.XDG_CONFIG_HOME.length > 0 ? env.XDG_CONFIG_HOME : join5(homedir4(), ".config");
1823
+ return join5(configHome, "opencode", "opencode.json");
1712
1824
  }
1713
1825
  function isCursorPluginEnabledInConfig(config) {
1714
1826
  if (!config || typeof config !== "object") {
1715
1827
  return true;
1716
1828
  }
1717
1829
  const configObject = config;
1830
+ if (configObject.provider && typeof configObject.provider === "object") {
1831
+ if (CURSOR_PROVIDER_ID in configObject.provider) {
1832
+ return true;
1833
+ }
1834
+ }
1718
1835
  if (Array.isArray(configObject.plugin)) {
1719
1836
  return configObject.plugin.some((entry) => matchesPlugin(entry));
1720
1837
  }
@@ -1736,7 +1853,7 @@ function shouldEnableCursorPlugin(env = process.env) {
1736
1853
  return {
1737
1854
  enabled,
1738
1855
  configPath,
1739
- reason: enabled ? "enabled_in_plugin_array_or_legacy" : "disabled_in_plugin_array"
1856
+ reason: enabled ? "enabled" : "disabled_in_plugin_array"
1740
1857
  };
1741
1858
  } catch {
1742
1859
  return {
@@ -1838,18 +1955,18 @@ async function autoRefreshModels(deps = {}) {
1838
1955
  resolvedDeps.log.debug("Model auto-refresh failed", { error: String(err) });
1839
1956
  }
1840
1957
  }
1841
- var log9, PROVIDER_ID = "cursor-acp", defaultDeps;
1958
+ var log10, PROVIDER_ID = "cursor-acp", defaultDeps;
1842
1959
  var init_sync = __esm(() => {
1843
1960
  init_model_discovery();
1844
1961
  init_plugin_toggle();
1845
1962
  init_logger();
1846
- log9 = createLogger("model-sync");
1963
+ log10 = createLogger("model-sync");
1847
1964
  defaultDeps = {
1848
1965
  defer: () => Promise.resolve(),
1849
1966
  discoverModels: discoverModelsFromCursorAgent,
1850
1967
  env: process.env,
1851
1968
  existsSync: nodeExistsSync,
1852
- log: log9,
1969
+ log: log10,
1853
1970
  readFileSync: nodeReadFileSync,
1854
1971
  writeFileSync: nodeWriteFileSync
1855
1972
  };
@@ -1910,7 +2027,7 @@ function readMcpConfigs(deps = {}) {
1910
2027
  timeout: typeof e.timeout === "number" ? e.timeout : undefined
1911
2028
  });
1912
2029
  } else {
1913
- log10.debug("Skipping unrecognised MCP config entry", { name, type: e.type });
2030
+ log11.debug("Skipping unrecognised MCP config entry", { name, type: e.type });
1914
2031
  }
1915
2032
  }
1916
2033
  return configs;
@@ -1954,11 +2071,11 @@ function readSubagentNames(deps = {}) {
1954
2071
  function isStringRecord(v) {
1955
2072
  return typeof v === "object" && v !== null && !Array.isArray(v);
1956
2073
  }
1957
- var log10;
2074
+ var log11;
1958
2075
  var init_config = __esm(() => {
1959
2076
  init_plugin_toggle();
1960
2077
  init_logger();
1961
- log10 = createLogger("mcp:config");
2078
+ log11 = createLogger("mcp:config");
1962
2079
  });
1963
2080
 
1964
2081
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
@@ -8095,11 +8212,11 @@ var require_core = __commonJS((exports) => {
8095
8212
  Ajv.ValidationError = validation_error_1.default;
8096
8213
  Ajv.MissingRefError = ref_error_1.default;
8097
8214
  exports.default = Ajv;
8098
- function checkOptions(checkOpts, options, msg, log11 = "error") {
8215
+ function checkOptions(checkOpts, options, msg, log12 = "error") {
8099
8216
  for (const key in checkOpts) {
8100
8217
  const opt = key;
8101
8218
  if (opt in options)
8102
- this.logger[log11](`${msg}: option ${key}. ${checkOpts[opt]}`);
8219
+ this.logger[log12](`${msg}: option ${key}. ${checkOpts[opt]}`);
8103
8220
  }
8104
8221
  }
8105
8222
  function getSchEnv(keyRef) {
@@ -11716,14 +11833,14 @@ class McpClientManager {
11716
11833
  }
11717
11834
  async connectServer(config) {
11718
11835
  if (this.connections.has(config.name)) {
11719
- log11.debug("Server already connected, skipping", { server: config.name });
11836
+ log12.debug("Server already connected, skipping", { server: config.name });
11720
11837
  return;
11721
11838
  }
11722
11839
  if (!this.deps) {
11723
11840
  try {
11724
11841
  this.deps = await loadDefaultDeps();
11725
11842
  } catch (err) {
11726
- log11.warn("Failed to load MCP SDK", { error: String(err) });
11843
+ log12.warn("Failed to load MCP SDK", { error: String(err) });
11727
11844
  return;
11728
11845
  }
11729
11846
  }
@@ -11734,7 +11851,7 @@ class McpClientManager {
11734
11851
  const transport = deps.createTransport(config);
11735
11852
  await client.connect(transport);
11736
11853
  } catch (err) {
11737
- log11.warn("MCP server connection failed", {
11854
+ log12.warn("MCP server connection failed", {
11738
11855
  server: config.name,
11739
11856
  error: String(err)
11740
11857
  });
@@ -11744,12 +11861,12 @@ class McpClientManager {
11744
11861
  try {
11745
11862
  const result = await client.listTools();
11746
11863
  tools = result?.tools ?? [];
11747
- log11.info("MCP server connected", {
11864
+ log12.info("MCP server connected", {
11748
11865
  server: config.name,
11749
11866
  tools: tools.length
11750
11867
  });
11751
11868
  } catch (err) {
11752
- log11.warn("MCP tool discovery failed", {
11869
+ log12.warn("MCP tool discovery failed", {
11753
11870
  server: config.name,
11754
11871
  error: String(err)
11755
11872
  });
@@ -11781,7 +11898,7 @@ class McpClientManager {
11781
11898
  }
11782
11899
  return typeof result === "string" ? result : JSON.stringify(result);
11783
11900
  } catch (err) {
11784
- log11.warn("MCP tool call failed", {
11901
+ log12.warn("MCP tool call failed", {
11785
11902
  server: serverName,
11786
11903
  tool: toolName,
11787
11904
  error: String(err?.message || err)
@@ -11793,9 +11910,9 @@ class McpClientManager {
11793
11910
  for (const [name, conn] of this.connections) {
11794
11911
  try {
11795
11912
  await conn.client.close();
11796
- log11.debug("MCP server disconnected", { server: name });
11913
+ log12.debug("MCP server disconnected", { server: name });
11797
11914
  } catch (err) {
11798
- log11.debug("MCP server disconnect failed", { server: name, error: String(err) });
11915
+ log12.debug("MCP server disconnect failed", { server: name, error: String(err) });
11799
11916
  }
11800
11917
  }
11801
11918
  this.connections.clear();
@@ -11804,10 +11921,10 @@ class McpClientManager {
11804
11921
  return Array.from(this.connections.keys());
11805
11922
  }
11806
11923
  }
11807
- var log11, defaultDeps2 = null;
11924
+ var log12, defaultDeps2 = null;
11808
11925
  var init_client_manager = __esm(() => {
11809
11926
  init_logger();
11810
- log11 = createLogger("mcp:client-manager");
11927
+ log12 = createLogger("mcp:client-manager");
11811
11928
  });
11812
11929
 
11813
11930
  // src/mcp/tool-bridge.ts
@@ -11818,7 +11935,7 @@ function buildMcpToolHookEntries(tools, manager) {
11818
11935
  for (const t of tools) {
11819
11936
  const hookName = namespaceMcpTool(t.serverName, t.name);
11820
11937
  if (entries[hookName]) {
11821
- log12.debug("Duplicate MCP tool name, skipping", { hookName });
11938
+ log13.debug("Duplicate MCP tool name, skipping", { hookName });
11822
11939
  continue;
11823
11940
  }
11824
11941
  const zodArgs = mcpSchemaToZod(t.inputSchema, z2);
@@ -11828,7 +11945,7 @@ function buildMcpToolHookEntries(tools, manager) {
11828
11945
  description: t.description || `MCP tool: ${t.name} (server: ${t.serverName})`,
11829
11946
  args: zodArgs,
11830
11947
  async execute(args) {
11831
- log12.debug("Executing MCP tool", { server: serverName, tool: toolName });
11948
+ log13.debug("Executing MCP tool", { server: serverName, tool: toolName });
11832
11949
  const result = await manager.callTool(serverName, toolName, args ?? {});
11833
11950
  if (result.startsWith("Error:")) {
11834
11951
  throw new Error(result);
@@ -11837,7 +11954,7 @@ function buildMcpToolHookEntries(tools, manager) {
11837
11954
  }
11838
11955
  });
11839
11956
  }
11840
- log12.debug("Built MCP tool hook entries", { count: Object.keys(entries).length });
11957
+ log13.debug("Built MCP tool hook entries", { count: Object.keys(entries).length });
11841
11958
  return entries;
11842
11959
  }
11843
11960
  function buildMcpToolDefinitions(tools) {
@@ -11884,7 +12001,7 @@ function mcpSchemaToZod(inputSchema, z2) {
11884
12001
  zodType = z2.array(z2.any());
11885
12002
  break;
11886
12003
  case "object":
11887
- zodType = z2.record(z2.any());
12004
+ zodType = z2.record(z2.string(), z2.any());
11888
12005
  break;
11889
12006
  default:
11890
12007
  zodType = z2.any();
@@ -11900,10 +12017,10 @@ function mcpSchemaToZod(inputSchema, z2) {
11900
12017
  }
11901
12018
  return shape;
11902
12019
  }
11903
- var log12;
12020
+ var log13;
11904
12021
  var init_tool_bridge = __esm(() => {
11905
12022
  init_logger();
11906
- log12 = createLogger("mcp:tool-bridge");
12023
+ log13 = createLogger("mcp:tool-bridge");
11907
12024
  });
11908
12025
 
11909
12026
  // node_modules/@opencode-ai/sdk/dist/gen/types.gen.js
@@ -13314,15 +13431,15 @@ class LocalExecutor {
13314
13431
  const out = await handler(args);
13315
13432
  return { status: "success", output: out };
13316
13433
  } catch (err) {
13317
- log13.warn("Local tool execution failed", { toolId, error: String(err?.message || err) });
13434
+ log14.warn("Local tool execution failed", { toolId, error: String(err?.message || err) });
13318
13435
  return { status: "error", error: String(err?.message || err) };
13319
13436
  }
13320
13437
  }
13321
13438
  }
13322
- var log13;
13439
+ var log14;
13323
13440
  var init_local = __esm(() => {
13324
13441
  init_logger();
13325
- log13 = createLogger("tools:executor:local");
13442
+ log14 = createLogger("tools:executor:local");
13326
13443
  });
13327
13444
 
13328
13445
  // src/tools/executors/sdk.ts
@@ -13349,7 +13466,7 @@ class SdkExecutor {
13349
13466
  const out = typeof res === "string" ? res : JSON.stringify(res);
13350
13467
  return { status: "success", output: out };
13351
13468
  } catch (err) {
13352
- log14.warn("SDK tool execution failed", { toolId, error: String(err?.message || err) });
13469
+ log15.warn("SDK tool execution failed", { toolId, error: String(err?.message || err) });
13353
13470
  return { status: "error", error: String(err?.message || err) };
13354
13471
  }
13355
13472
  }
@@ -13362,10 +13479,10 @@ class SdkExecutor {
13362
13479
  ]);
13363
13480
  }
13364
13481
  }
13365
- var log14;
13482
+ var log15;
13366
13483
  var init_sdk = __esm(() => {
13367
13484
  init_logger();
13368
- log14 = createLogger("tools:executor:sdk");
13485
+ log15 = createLogger("tools:executor:sdk");
13369
13486
  });
13370
13487
 
13371
13488
  // src/tools/executors/mcp.ts
@@ -13392,7 +13509,7 @@ class McpExecutor {
13392
13509
  const out = typeof res === "string" ? res : JSON.stringify(res);
13393
13510
  return { status: "success", output: out };
13394
13511
  } catch (err) {
13395
- log15.warn("MCP tool execution failed", { toolId, error: String(err?.message || err) });
13512
+ log16.warn("MCP tool execution failed", { toolId, error: String(err?.message || err) });
13396
13513
  return { status: "error", error: String(err?.message || err) };
13397
13514
  }
13398
13515
  }
@@ -13405,10 +13522,10 @@ class McpExecutor {
13405
13522
  ]);
13406
13523
  }
13407
13524
  }
13408
- var log15;
13525
+ var log16;
13409
13526
  var init_mcp = __esm(() => {
13410
13527
  init_logger();
13411
- log15 = createLogger("tools:executor:mcp");
13528
+ log16 = createLogger("tools:executor:mcp");
13412
13529
  });
13413
13530
 
13414
13531
  // src/tools/core/executor.ts
@@ -13418,17 +13535,17 @@ async function executeWithChain(executors, toolId, args) {
13418
13535
  try {
13419
13536
  return await ex.execute(toolId, args);
13420
13537
  } catch (err) {
13421
- log16.warn("Executor threw unexpected error", { toolId, error: String(err?.message || err) });
13538
+ log17.warn("Executor threw unexpected error", { toolId, error: String(err?.message || err) });
13422
13539
  return { status: "error", error: String(err?.message || err) };
13423
13540
  }
13424
13541
  }
13425
13542
  }
13426
13543
  return { status: "error", error: `No executor available for ${toolId}` };
13427
13544
  }
13428
- var log16;
13545
+ var log17;
13429
13546
  var init_executor = __esm(() => {
13430
13547
  init_logger();
13431
- log16 = createLogger("tools:executor:chain");
13548
+ log17 = createLogger("tools:executor:chain");
13432
13549
  });
13433
13550
 
13434
13551
  // src/tools/defaults.ts
@@ -13466,7 +13583,7 @@ function registerDefaultTools(registry) {
13466
13583
  const cwd = resolveWorkingDirectory(args);
13467
13584
  return new Promise((resolve2, reject) => {
13468
13585
  const proc = spawn3(command, {
13469
- shell: process.env.SHELL || "/bin/bash",
13586
+ shell: resolveShellOption(),
13470
13587
  cwd
13471
13588
  });
13472
13589
  const stdoutChunks = [];
@@ -13672,6 +13789,9 @@ ${output}`);
13672
13789
  const pattern = args.pattern;
13673
13790
  const path2 = args.path;
13674
13791
  const include = args.include;
13792
+ if (process.platform === "win32") {
13793
+ return nodeFallbackGrep(pattern, path2, include);
13794
+ }
13675
13795
  const grepArgs = ["-r", "-n"];
13676
13796
  if (include) {
13677
13797
  grepArgs.push(`--include=${include}`);
@@ -13764,6 +13884,9 @@ ${output}`);
13764
13884
  const path2 = resolvePathArg(args, "glob");
13765
13885
  const cwd = path2 || ".";
13766
13886
  const normalizedPattern = pattern.replace(/\\/g, "/");
13887
+ if (process.platform === "win32") {
13888
+ return nodeFallbackGlob(normalizedPattern, cwd);
13889
+ }
13767
13890
  const isPathPattern = normalizedPattern.includes("/");
13768
13891
  const findArgs = [cwd, "-type", "f"];
13769
13892
  if (isPathPattern) {
@@ -13975,6 +14098,14 @@ function resolveTimeoutMs(value) {
13975
14098
  return 30000;
13976
14099
  return raw <= 600 ? raw * 1000 : raw;
13977
14100
  }
14101
+ function resolveShellOption(deps = {}) {
14102
+ const platform2 = deps.platform ?? process.platform;
14103
+ const env = deps.env ?? process.env;
14104
+ if (platform2 === "win32") {
14105
+ return env.ComSpec || env.COMSPEC || true;
14106
+ }
14107
+ return env.SHELL || "/bin/bash";
14108
+ }
13978
14109
  function resolveBoolean(value, defaultValue) {
13979
14110
  if (typeof value === "boolean") {
13980
14111
  return value;
@@ -14040,6 +14171,146 @@ function coerceToString(value) {
14040
14171
  }
14041
14172
  return null;
14042
14173
  }
14174
+ async function nodeFallbackGrep(pattern, searchPath, include) {
14175
+ const fs2 = await import("fs/promises");
14176
+ const path2 = await import("path");
14177
+ let regex2;
14178
+ try {
14179
+ regex2 = new RegExp(pattern);
14180
+ } catch {
14181
+ return "Invalid regex pattern";
14182
+ }
14183
+ let includeRegex;
14184
+ if (include) {
14185
+ const incPattern = include.replace(/\./g, "\\.").replace(/\?/g, ".").replace(/\*/g, ".*");
14186
+ includeRegex = new RegExp(`^${incPattern}$`);
14187
+ }
14188
+ const results = [];
14189
+ async function walk(dir) {
14190
+ if (results.length >= 100)
14191
+ return;
14192
+ let entries;
14193
+ try {
14194
+ entries = await fs2.readdir(dir, { withFileTypes: true });
14195
+ } catch (err) {
14196
+ if (err?.code !== "ENOENT" && err?.code !== "EACCES") {
14197
+ fallbackLog.error("Unexpected error reading directory", { dir, code: err?.code, message: err?.message });
14198
+ }
14199
+ return;
14200
+ }
14201
+ for (const entry of entries) {
14202
+ if (results.length >= 100)
14203
+ return;
14204
+ const fullPath = path2.join(dir, entry.name);
14205
+ if (entry.isDirectory()) {
14206
+ if (!FALLBACK_SKIP_DIRS.has(entry.name)) {
14207
+ await walk(fullPath);
14208
+ }
14209
+ } else if (entry.isFile()) {
14210
+ if (includeRegex && !includeRegex.test(entry.name))
14211
+ continue;
14212
+ let content;
14213
+ try {
14214
+ content = await fs2.readFile(fullPath, "utf-8");
14215
+ } catch (err) {
14216
+ if (err?.code !== "ENOENT" && err?.code !== "EACCES") {
14217
+ fallbackLog.error("Unexpected error reading file", { path: fullPath, code: err?.code, message: err?.message });
14218
+ }
14219
+ continue;
14220
+ }
14221
+ const lines = content.split(`
14222
+ `);
14223
+ for (let i = 0;i < lines.length; i++) {
14224
+ if (regex2.test(lines[i])) {
14225
+ results.push(`${fullPath}:${i + 1}:${lines[i]}`);
14226
+ if (results.length >= 100)
14227
+ break;
14228
+ }
14229
+ }
14230
+ }
14231
+ }
14232
+ }
14233
+ let stat;
14234
+ try {
14235
+ stat = await fs2.stat(searchPath);
14236
+ } catch {
14237
+ return "Path not found";
14238
+ }
14239
+ if (stat.isFile()) {
14240
+ let content;
14241
+ try {
14242
+ content = await fs2.readFile(searchPath, "utf-8");
14243
+ } catch (err) {
14244
+ if (err?.code !== "ENOENT" && err?.code !== "EACCES") {
14245
+ fallbackLog.error("Unexpected error reading file", { path: searchPath, code: err?.code, message: err?.message });
14246
+ }
14247
+ return "Path not found";
14248
+ }
14249
+ const lines = content.split(`
14250
+ `);
14251
+ for (let i = 0;i < lines.length; i++) {
14252
+ if (regex2.test(lines[i])) {
14253
+ results.push(`${searchPath}:${i + 1}:${lines[i]}`);
14254
+ if (results.length >= 100)
14255
+ break;
14256
+ }
14257
+ }
14258
+ } else {
14259
+ await walk(searchPath);
14260
+ }
14261
+ return results.join(`
14262
+ `) || "No matches found";
14263
+ }
14264
+ async function nodeFallbackGlob(pattern, searchPath) {
14265
+ const fs2 = await import("fs/promises");
14266
+ const path2 = await import("path");
14267
+ const results = [];
14268
+ const isPathPattern = pattern.includes("/");
14269
+ let regexPattern = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "\x00").replace(/\*/g, "[^/]*").replace(/\x00/g, ".*");
14270
+ let regex2;
14271
+ try {
14272
+ regex2 = isPathPattern ? new RegExp(`${regexPattern}$`) : new RegExp(`^${regexPattern}$`);
14273
+ } catch {
14274
+ return "No files found";
14275
+ }
14276
+ async function walk(dir) {
14277
+ if (results.length >= 50)
14278
+ return;
14279
+ let entries;
14280
+ try {
14281
+ entries = await fs2.readdir(dir, { withFileTypes: true });
14282
+ } catch (err) {
14283
+ if (err?.code !== "ENOENT" && err?.code !== "EACCES") {
14284
+ fallbackLog.error("Unexpected error reading directory", { dir, code: err?.code, message: err?.message });
14285
+ }
14286
+ return;
14287
+ }
14288
+ for (const entry of entries) {
14289
+ if (results.length >= 50)
14290
+ return;
14291
+ const fullPath = path2.join(dir, entry.name);
14292
+ if (entry.isDirectory()) {
14293
+ if (!FALLBACK_SKIP_DIRS.has(entry.name)) {
14294
+ await walk(fullPath);
14295
+ }
14296
+ } else if (entry.isFile()) {
14297
+ const matchTarget = isPathPattern ? fullPath.replace(/\\/g, "/") : entry.name;
14298
+ if (regex2.test(matchTarget)) {
14299
+ results.push(fullPath);
14300
+ }
14301
+ }
14302
+ }
14303
+ }
14304
+ await walk(searchPath);
14305
+ return results.join(`
14306
+ `) || "No files found";
14307
+ }
14308
+ var FALLBACK_SKIP_DIRS, fallbackLog;
14309
+ var init_defaults = __esm(() => {
14310
+ init_logger();
14311
+ FALLBACK_SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build"]);
14312
+ fallbackLog = createLogger("tools:fallback");
14313
+ });
14043
14314
 
14044
14315
  // src/provider/boundary.ts
14045
14316
  function parseProviderBoundaryMode(value) {
@@ -14104,6 +14375,13 @@ function createSharedBoundary(providerId) {
14104
14375
  }
14105
14376
  return raw;
14106
14377
  },
14378
+ resolveRuntimeModel(model, cursorModel) {
14379
+ const rawCursorModel = typeof cursorModel === "string" ? cursorModel.trim() : "";
14380
+ if (rawCursorModel.length > 0) {
14381
+ return this.normalizeRuntimeModel(rawCursorModel);
14382
+ }
14383
+ return this.normalizeRuntimeModel(model);
14384
+ },
14107
14385
  applyChatParamDefaults(output, proxyBaseURL, defaultBaseURL, defaultApiKey) {
14108
14386
  output.options = output.options || {};
14109
14387
  output.options.baseURL = proxyBaseURL || defaultBaseURL;
@@ -14544,7 +14822,7 @@ async function handleToolLoopEventLegacy(options) {
14544
14822
  const extraction = toolLoopMode === "opencode" ? extractOpenAiToolCall(event, allowedToolNames) : { action: "skip", skipReason: "tool_loop_mode_not_opencode" };
14545
14823
  if (extraction.action === "passthrough") {
14546
14824
  passThroughTracker?.trackTool(extraction.passthroughName);
14547
- log17.debug("MCP tool passed through to cursor-agent (legacy)", {
14825
+ log18.debug("MCP tool passed through to cursor-agent (legacy)", {
14548
14826
  tool: extraction.passthroughName
14549
14827
  });
14550
14828
  return { intercepted: false, skipConverter: false };
@@ -14568,7 +14846,7 @@ async function handleToolLoopEventLegacy(options) {
14568
14846
  if (interceptedToolCall) {
14569
14847
  const compat = applyToolSchemaCompat(interceptedToolCall, toolSchemaMap);
14570
14848
  let normalizedToolCall = compat.toolCall;
14571
- log17.debug("Applied tool schema compatibility (legacy)", {
14849
+ log18.debug("Applied tool schema compatibility (legacy)", {
14572
14850
  tool: normalizedToolCall.function.name,
14573
14851
  originalArgKeys: compat.originalArgKeys,
14574
14852
  normalizedArgKeys: compat.normalizedArgKeys,
@@ -14580,7 +14858,7 @@ async function handleToolLoopEventLegacy(options) {
14580
14858
  if (validationTermination) {
14581
14859
  if (validationTermination.soft) {
14582
14860
  const hintChunk = createLoopGuardHintChunk(responseMeta, normalizedToolCall, validationTermination);
14583
- log17.debug("Soft-blocking schema validation loop guard in legacy (emitting hint)", {
14861
+ log18.debug("Soft-blocking schema validation loop guard in legacy (emitting hint)", {
14584
14862
  tool: normalizedToolCall.function.name,
14585
14863
  fingerprint: validationTermination.fingerprint
14586
14864
  });
@@ -14591,7 +14869,7 @@ async function handleToolLoopEventLegacy(options) {
14591
14869
  }
14592
14870
  const reroutedWrite = tryRerouteEditToWrite(normalizedToolCall, compat.normalizedArgs, allowedToolNames, toolSchemaMap);
14593
14871
  if (reroutedWrite) {
14594
- log17.debug("Rerouting malformed edit call to write (legacy)", {
14872
+ log18.debug("Rerouting malformed edit call to write (legacy)", {
14595
14873
  path: reroutedWrite.path,
14596
14874
  missing: compat.validation.missing,
14597
14875
  typeErrors: compat.validation.typeErrors
@@ -14599,7 +14877,7 @@ async function handleToolLoopEventLegacy(options) {
14599
14877
  normalizedToolCall = reroutedWrite.toolCall;
14600
14878
  } else if (shouldEmitNonFatalSchemaValidationHint(normalizedToolCall, compat.validation)) {
14601
14879
  const hintChunk = createNonFatalSchemaValidationHintChunk(responseMeta, normalizedToolCall, compat.validation);
14602
- log17.debug("Emitting non-fatal schema validation hint in legacy and skipping malformed tool execution", {
14880
+ log18.debug("Emitting non-fatal schema validation hint in legacy and skipping malformed tool execution", {
14603
14881
  tool: normalizedToolCall.function.name,
14604
14882
  missing: compat.validation.missing,
14605
14883
  typeErrors: compat.validation.typeErrors
@@ -14612,7 +14890,7 @@ async function handleToolLoopEventLegacy(options) {
14612
14890
  if (termination) {
14613
14891
  if (termination.soft) {
14614
14892
  const hintChunk = createLoopGuardHintChunk(responseMeta, normalizedToolCall, termination);
14615
- log17.debug("Soft-blocking tool loop guard in legacy (emitting hint)", {
14893
+ log18.debug("Soft-blocking tool loop guard in legacy (emitting hint)", {
14616
14894
  tool: normalizedToolCall.function.name,
14617
14895
  fingerprint: termination.fingerprint
14618
14896
  });
@@ -14670,7 +14948,7 @@ async function handleToolLoopEventV1(options) {
14670
14948
  }
14671
14949
  if (extraction.action === "passthrough") {
14672
14950
  passThroughTracker?.trackTool(extraction.passthroughName);
14673
- log17.debug("MCP tool passed through to cursor-agent (v1)", {
14951
+ log18.debug("MCP tool passed through to cursor-agent (v1)", {
14674
14952
  tool: extraction.passthroughName
14675
14953
  });
14676
14954
  return { intercepted: false, skipConverter: false };
@@ -14697,7 +14975,7 @@ async function handleToolLoopEventV1(options) {
14697
14975
  rawArgs: safeArgTypeSummary(event),
14698
14976
  normalizedArgs: compat.normalizedArgs
14699
14977
  } : undefined;
14700
- log17.debug("Applied tool schema compatibility", {
14978
+ log18.debug("Applied tool schema compatibility", {
14701
14979
  tool: normalizedToolCall.function.name,
14702
14980
  originalArgKeys: compat.originalArgKeys,
14703
14981
  normalizedArgKeys: compat.normalizedArgKeys,
@@ -14706,7 +14984,7 @@ async function handleToolLoopEventV1(options) {
14706
14984
  ...editDiag ? { editDiag } : {}
14707
14985
  });
14708
14986
  if (compat.validation.hasSchema && !compat.validation.ok) {
14709
- log17.debug("Tool schema compatibility validation failed", {
14987
+ log18.debug("Tool schema compatibility validation failed", {
14710
14988
  tool: normalizedToolCall.function.name,
14711
14989
  missing: compat.validation.missing,
14712
14990
  unexpected: compat.validation.unexpected,
@@ -14717,7 +14995,7 @@ async function handleToolLoopEventV1(options) {
14717
14995
  if (validationTermination) {
14718
14996
  if (validationTermination.soft) {
14719
14997
  const hintChunk = createLoopGuardHintChunk(responseMeta, normalizedToolCall, validationTermination);
14720
- log17.debug("Soft-blocking schema validation loop guard (emitting hint)", {
14998
+ log18.debug("Soft-blocking schema validation loop guard (emitting hint)", {
14721
14999
  tool: normalizedToolCall.function.name,
14722
15000
  fingerprint: validationTermination.fingerprint,
14723
15001
  repeatCount: validationTermination.repeatCount
@@ -14731,7 +15009,7 @@ async function handleToolLoopEventV1(options) {
14731
15009
  if (termination2) {
14732
15010
  if (termination2.soft) {
14733
15011
  const hintChunk = createLoopGuardHintChunk(responseMeta, normalizedToolCall, termination2);
14734
- log17.debug("Soft-blocking tool loop guard in validation path (emitting hint)", {
15012
+ log18.debug("Soft-blocking tool loop guard in validation path (emitting hint)", {
14735
15013
  tool: normalizedToolCall.function.name,
14736
15014
  fingerprint: termination2.fingerprint,
14737
15015
  repeatCount: termination2.repeatCount
@@ -14743,7 +15021,7 @@ async function handleToolLoopEventV1(options) {
14743
15021
  }
14744
15022
  const reroutedWrite = tryRerouteEditToWrite(normalizedToolCall, compat.normalizedArgs, allowedToolNames, toolSchemaMap);
14745
15023
  if (reroutedWrite) {
14746
- log17.debug("Rerouting malformed edit call to write", {
15024
+ log18.debug("Rerouting malformed edit call to write", {
14747
15025
  path: reroutedWrite.path,
14748
15026
  missing: compat.validation.missing,
14749
15027
  typeErrors: compat.validation.typeErrors
@@ -14763,7 +15041,7 @@ async function handleToolLoopEventV1(options) {
14763
15041
  }
14764
15042
  if (schemaValidationFailureMode === "pass_through" && shouldEmitNonFatalSchemaValidationHint(normalizedToolCall, compat.validation)) {
14765
15043
  const hintChunk = createNonFatalSchemaValidationHintChunk(responseMeta, normalizedToolCall, compat.validation);
14766
- log17.debug("Emitting non-fatal schema validation hint and skipping malformed tool execution", {
15044
+ log18.debug("Emitting non-fatal schema validation hint and skipping malformed tool execution", {
14767
15045
  tool: normalizedToolCall.function.name,
14768
15046
  missing: compat.validation.missing,
14769
15047
  typeErrors: compat.validation.typeErrors
@@ -14781,7 +15059,7 @@ async function handleToolLoopEventV1(options) {
14781
15059
  terminate: createSchemaValidationTermination(normalizedToolCall, compat.validation)
14782
15060
  };
14783
15061
  }
14784
- log17.debug("Forwarding schema-invalid tool call to OpenCode loop", {
15062
+ log18.debug("Forwarding schema-invalid tool call to OpenCode loop", {
14785
15063
  tool: normalizedToolCall.function.name,
14786
15064
  repairHint: compat.validation.repairHint
14787
15065
  });
@@ -14795,7 +15073,7 @@ async function handleToolLoopEventV1(options) {
14795
15073
  if (termination) {
14796
15074
  if (termination.soft) {
14797
15075
  const hintChunk = createLoopGuardHintChunk(responseMeta, normalizedToolCall, termination);
14798
- log17.debug("Soft-blocking tool loop guard (emitting hint)", {
15076
+ log18.debug("Soft-blocking tool loop guard (emitting hint)", {
14799
15077
  tool: normalizedToolCall.function.name,
14800
15078
  fingerprint: termination.fingerprint,
14801
15079
  repeatCount: termination.repeatCount
@@ -14854,7 +15132,7 @@ function evaluateToolLoopGuard(toolLoopGuard, toolCall) {
14854
15132
  if (!decision.triggered) {
14855
15133
  return null;
14856
15134
  }
14857
- log17.debug("Tool loop guard triggered", {
15135
+ log18.debug("Tool loop guard triggered", {
14858
15136
  tool: toolCall.function.name,
14859
15137
  fingerprint: decision.fingerprint,
14860
15138
  repeatCount: decision.repeatCount,
@@ -14916,7 +15194,7 @@ function evaluateSchemaValidationLoopGuard(toolLoopGuard, toolCall, validation)
14916
15194
  return null;
14917
15195
  }
14918
15196
  const isFirstTrigger = decision.repeatCount === decision.maxRepeat + 1;
14919
- log17.debug("Tool loop guard triggered on schema validation", {
15197
+ log18.debug("Tool loop guard triggered on schema validation", {
14920
15198
  tool: toolCall.function.name,
14921
15199
  fingerprint: decision.fingerprint,
14922
15200
  repeatCount: decision.repeatCount,
@@ -15096,12 +15374,12 @@ function tryRerouteEditToWrite(toolCall, normalizedArgs, allowedToolNames, toolS
15096
15374
  function isRecord4(value) {
15097
15375
  return typeof value === "object" && value !== null && !Array.isArray(value);
15098
15376
  }
15099
- var log17, ToolBoundaryExtractionError;
15377
+ var log18, ToolBoundaryExtractionError;
15100
15378
  var init_runtime_interception = __esm(() => {
15101
15379
  init_tool_loop();
15102
15380
  init_logger();
15103
15381
  init_tool_schema_compat();
15104
- log17 = createLogger("provider:runtime-interception");
15382
+ log18 = createLogger("provider:runtime-interception");
15105
15383
  ToolBoundaryExtractionError = class ToolBoundaryExtractionError extends Error {
15106
15384
  cause;
15107
15385
  constructor(message, cause) {
@@ -15143,7 +15421,7 @@ class ToastService {
15143
15421
  }
15144
15422
  async show(options) {
15145
15423
  if (!this.client?.tui?.showToast) {
15146
- log18.debug("Toast not available; client.tui.showToast missing", { message: options.message });
15424
+ log19.debug("Toast not available; client.tui.showToast missing", { message: options.message });
15147
15425
  return;
15148
15426
  }
15149
15427
  try {
@@ -15155,7 +15433,7 @@ class ToastService {
15155
15433
  }
15156
15434
  });
15157
15435
  } catch (error) {
15158
- log18.debug("Toast failed", { error, message: options.message });
15436
+ log19.debug("Toast failed", { error, message: options.message });
15159
15437
  }
15160
15438
  }
15161
15439
  async showPassThroughSummary(tools) {
@@ -15179,10 +15457,10 @@ class ToastService {
15179
15457
  });
15180
15458
  }
15181
15459
  }
15182
- var log18, toastService;
15460
+ var log19, toastService;
15183
15461
  var init_toast_service = __esm(() => {
15184
15462
  init_logger();
15185
- log18 = createLogger("services:toast");
15463
+ log19 = createLogger("services:toast");
15186
15464
  toastService = new ToastService;
15187
15465
  });
15188
15466
 
@@ -15631,9 +15909,12 @@ var init_tool_loop_guard = __esm(() => {
15631
15909
  var exports_plugin = {};
15632
15910
  __export(exports_plugin, {
15633
15911
  shouldProcessModel: () => shouldProcessModel,
15912
+ resolveWorkspaceDirectory: () => resolveWorkspaceDirectory,
15634
15913
  resolveChatParamTools: () => resolveChatParamTools,
15635
15914
  normalizeWorkspaceForCompare: () => normalizeWorkspaceForCompare,
15915
+ isRootPath: () => isRootPath,
15636
15916
  isReusableProxyHealthPayload: () => isReusableProxyHealthPayload,
15917
+ extractCompletionFromStream: () => extractCompletionFromStream,
15637
15918
  ensurePluginDirectory: () => ensurePluginDirectory,
15638
15919
  default: () => plugin_default,
15639
15920
  buildAvailableToolsSystemMessage: () => buildAvailableToolsSystemMessage,
@@ -15643,7 +15924,7 @@ import { tool as tool2 } from "@opencode-ai/plugin";
15643
15924
  import { appendFileSync as appendFileSync3, existsSync as existsSync5, realpathSync } from "fs";
15644
15925
  import { mkdir } from "fs/promises";
15645
15926
  import { homedir as homedir5 } from "os";
15646
- import { isAbsolute, join as join5, relative, resolve as resolve2 } from "path";
15927
+ import { isAbsolute, join as join6, relative, resolve as resolve2 } from "path";
15647
15928
  function ensureDebugLogDir() {
15648
15929
  try {
15649
15930
  if (!existsSync5(DEBUG_LOG_DIR2)) {
@@ -15703,13 +15984,13 @@ function buildAvailableToolsSystemMessage(lastToolNames, lastToolMap, mcpToolDef
15703
15984
  `) : null;
15704
15985
  }
15705
15986
  async function ensurePluginDirectory() {
15706
- const configHome = process.env.XDG_CONFIG_HOME ? resolve2(process.env.XDG_CONFIG_HOME) : join5(homedir5(), ".config");
15707
- const pluginDir = join5(configHome, "opencode", "plugin");
15987
+ const configHome = process.env.XDG_CONFIG_HOME ? resolve2(process.env.XDG_CONFIG_HOME) : join6(homedir5(), ".config");
15988
+ const pluginDir = join6(configHome, "opencode", "plugin");
15708
15989
  try {
15709
15990
  await mkdir(pluginDir, { recursive: true });
15710
- log19.debug("Plugin directory ensured", { path: pluginDir });
15991
+ log20.debug("Plugin directory ensured", { path: pluginDir });
15711
15992
  } catch (error) {
15712
- log19.warn("Failed to create plugin directory", { error: String(error) });
15993
+ log20.warn("Failed to create plugin directory", { error: String(error) });
15713
15994
  }
15714
15995
  }
15715
15996
  function shouldProcessModel(model) {
@@ -15721,8 +16002,8 @@ function getGlobalKey() {
15721
16002
  return "__opencode_cursor_proxy_server__";
15722
16003
  }
15723
16004
  function getOpenCodeConfigPrefix() {
15724
- const configHome = process.env.XDG_CONFIG_HOME ? resolve2(process.env.XDG_CONFIG_HOME) : join5(homedir5(), ".config");
15725
- return join5(configHome, "opencode");
16005
+ const configHome = process.env.XDG_CONFIG_HOME ? resolve2(process.env.XDG_CONFIG_HOME) : join6(homedir5(), ".config");
16006
+ return join6(configHome, "opencode");
15726
16007
  }
15727
16008
  function canonicalizePathForCompare(pathValue) {
15728
16009
  const resolvedPath = resolve2(pathValue);
@@ -15732,7 +16013,7 @@ function canonicalizePathForCompare(pathValue) {
15732
16013
  } catch {
15733
16014
  normalizedPath = resolvedPath;
15734
16015
  }
15735
- if (process.platform === "darwin") {
16016
+ if (process.platform === "darwin" || process.platform === "win32") {
15736
16017
  return normalizedPath.toLowerCase();
15737
16018
  }
15738
16019
  return normalizedPath;
@@ -15755,32 +16036,62 @@ function isNonConfigPath(pathValue) {
15755
16036
  }
15756
16037
  return !isWithinPath(getOpenCodeConfigPrefix(), pathValue);
15757
16038
  }
15758
- function resolveWorkspaceDirectory(worktree, directory) {
15759
- const envWorkspace = process.env.CURSOR_ACP_WORKSPACE?.trim();
15760
- if (envWorkspace) {
15761
- return resolve2(envWorkspace);
16039
+ function isRootPath(pathValue) {
16040
+ if (!pathValue) {
16041
+ return false;
16042
+ }
16043
+ const resolved = resolve2(pathValue);
16044
+ if (resolved === "/") {
16045
+ return true;
15762
16046
  }
15763
- const envProjectDir = process.env.OPENCODE_CURSOR_PROJECT_DIR?.trim();
15764
- if (envProjectDir) {
15765
- return resolve2(envProjectDir);
16047
+ return /^[A-Za-z]:[\\/]?$/.test(resolved);
16048
+ }
16049
+ function isAcceptableWorkspace(pathValue, configPrefix) {
16050
+ if (!pathValue) {
16051
+ return false;
16052
+ }
16053
+ if (isRootPath(pathValue)) {
16054
+ return false;
15766
16055
  }
16056
+ if (isWithinPath(configPrefix, pathValue)) {
16057
+ return false;
16058
+ }
16059
+ return true;
16060
+ }
16061
+ function resolveWorkspaceDirectory(worktree, directory) {
15767
16062
  const configPrefix = getOpenCodeConfigPrefix();
16063
+ const envWorkspace = resolveCandidate(process.env.CURSOR_ACP_WORKSPACE);
16064
+ if (envWorkspace && !isRootPath(envWorkspace)) {
16065
+ return envWorkspace;
16066
+ }
16067
+ const envProjectDir = resolveCandidate(process.env.OPENCODE_CURSOR_PROJECT_DIR);
16068
+ if (envProjectDir && !isRootPath(envProjectDir)) {
16069
+ return envProjectDir;
16070
+ }
15768
16071
  const worktreeCandidate = resolveCandidate(worktree);
15769
- if (worktreeCandidate && !isWithinPath(configPrefix, worktreeCandidate)) {
16072
+ if (isAcceptableWorkspace(worktreeCandidate, configPrefix)) {
15770
16073
  return worktreeCandidate;
15771
16074
  }
15772
16075
  const dirCandidate = resolveCandidate(directory);
15773
- if (dirCandidate && !isWithinPath(configPrefix, dirCandidate)) {
16076
+ if (isAcceptableWorkspace(dirCandidate, configPrefix)) {
15774
16077
  return dirCandidate;
15775
16078
  }
15776
16079
  const cwd = resolve2(process.cwd());
15777
- if (cwd && !isWithinPath(configPrefix, cwd)) {
16080
+ if (isAcceptableWorkspace(cwd, configPrefix)) {
15778
16081
  return cwd;
15779
16082
  }
15780
- return dirCandidate || cwd || configPrefix;
16083
+ const home = resolveCandidate(homedir5());
16084
+ if (home && !isRootPath(home)) {
16085
+ return home;
16086
+ }
16087
+ return configPrefix;
15781
16088
  }
15782
16089
  function normalizeWorkspaceForCompare(pathValue) {
15783
- return resolve2(pathValue);
16090
+ const resolved = resolve2(pathValue);
16091
+ if (process.platform === "darwin" || process.platform === "win32") {
16092
+ return resolved.toLowerCase();
16093
+ }
16094
+ return resolved;
15784
16095
  }
15785
16096
  function isReusableProxyHealthPayload(payload, workspaceDirectory) {
15786
16097
  if (!payload || payload.ok !== true) {
@@ -15801,7 +16112,7 @@ function parseToolLoopMode(value) {
15801
16112
  function resolveChatParamTools(mode, existingTools, refreshedTools) {
15802
16113
  return PROVIDER_BOUNDARY.resolveChatParamTools(mode, existingTools, refreshedTools);
15803
16114
  }
15804
- function createChatCompletionResponse(model, content, reasoningContent) {
16115
+ function createChatCompletionResponse(model, content, reasoningContent, usage) {
15805
16116
  const message = {
15806
16117
  role: "assistant",
15807
16118
  content
@@ -15809,7 +16120,7 @@ function createChatCompletionResponse(model, content, reasoningContent) {
15809
16120
  if (reasoningContent && reasoningContent.length > 0) {
15810
16121
  message.reasoning_content = reasoningContent;
15811
16122
  }
15812
- return {
16123
+ const response = {
15813
16124
  id: `cursor-acp-${Date.now()}`,
15814
16125
  object: "chat.completion",
15815
16126
  created: Math.floor(Date.now() / 1000),
@@ -15822,6 +16133,10 @@ function createChatCompletionResponse(model, content, reasoningContent) {
15822
16133
  }
15823
16134
  ]
15824
16135
  };
16136
+ if (usage) {
16137
+ response.usage = usage;
16138
+ }
16139
+ return response;
15825
16140
  }
15826
16141
  function createChatCompletionChunk(id, created, model, deltaContent, done = false) {
15827
16142
  return {
@@ -15843,7 +16158,9 @@ function extractCompletionFromStream(output) {
15843
16158
  `);
15844
16159
  let assistantText = "";
15845
16160
  let reasoningText = "";
16161
+ let usage;
15846
16162
  let sawAssistantPartials = false;
16163
+ let sawThinkingPartials = false;
15847
16164
  for (const line of lines) {
15848
16165
  const event = parseStreamJsonLine(line);
15849
16166
  if (!event) {
@@ -15864,11 +16181,20 @@ function extractCompletionFromStream(output) {
15864
16181
  if (isThinking(event)) {
15865
16182
  const thinking = extractThinking(event);
15866
16183
  if (thinking) {
15867
- reasoningText += thinking;
16184
+ const isPartial = typeof event.timestamp_ms === "number";
16185
+ if (isPartial) {
16186
+ reasoningText += thinking;
16187
+ sawThinkingPartials = true;
16188
+ } else if (!sawThinkingPartials) {
16189
+ reasoningText = thinking;
16190
+ }
15868
16191
  }
15869
16192
  }
16193
+ if (isResult(event)) {
16194
+ usage = extractOpenAiUsageFromResult(event) ?? usage;
16195
+ }
15870
16196
  }
15871
- return { assistantText, reasoningText };
16197
+ return { assistantText, reasoningText, usage };
15872
16198
  }
15873
16199
  function formatToolUpdateEvent(update) {
15874
16200
  return `event: tool_update
@@ -15897,9 +16223,9 @@ function createBoundaryRuntimeContext(scope) {
15897
16223
  error: toErrorMessage(error)
15898
16224
  };
15899
16225
  if (!fallbackActive) {
15900
- log19.warn("Provider boundary v1 failed; switching to legacy for this request", details);
16226
+ log20.warn("Provider boundary v1 failed; switching to legacy for this request", details);
15901
16227
  } else {
15902
- log19.debug("Provider boundary fallback already active", details);
16228
+ log20.debug("Provider boundary fallback already active", details);
15903
16229
  }
15904
16230
  fallbackActive = true;
15905
16231
  return true;
@@ -16012,7 +16338,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16012
16338
  if (url.pathname === "/v1/models" || url.pathname === "/models") {
16013
16339
  try {
16014
16340
  const bunAny2 = globalThis;
16015
- const proc = bunAny2.Bun.spawn(["cursor-agent", "models"], {
16341
+ const proc = bunAny2.Bun.spawn([resolveCursorAgentBinary(), "models"], {
16016
16342
  stdout: "pipe",
16017
16343
  stderr: "pipe"
16018
16344
  });
@@ -16037,7 +16363,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16037
16363
  headers: { "Content-Type": "application/json" }
16038
16364
  });
16039
16365
  } catch (err) {
16040
- log19.error("Failed to list models", { error: String(err) });
16366
+ log20.error("Failed to list models", { error: String(err) });
16041
16367
  return new Response(JSON.stringify({ error: "Failed to fetch models from cursor-agent" }), {
16042
16368
  status: 500,
16043
16369
  headers: { "Content-Type": "application/json" }
@@ -16050,13 +16376,14 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16050
16376
  headers: { "Content-Type": "application/json" }
16051
16377
  });
16052
16378
  }
16053
- log19.debug("Proxy request (bun)", { method: req.method, path: url.pathname });
16379
+ log20.debug("Proxy request (bun)", { method: req.method, path: url.pathname });
16054
16380
  const body = await req.json().catch(() => ({}));
16055
16381
  const messages = Array.isArray(body?.messages) ? body.messages : [];
16056
16382
  const stream = body?.stream === true;
16057
16383
  const tools = Array.isArray(body?.tools) ? body.tools : [];
16058
16384
  debugLogToFile2("raw_request_body", {
16059
16385
  model: body?.model,
16386
+ cursorModel: body?.cursorModel,
16060
16387
  stream,
16061
16388
  toolCount: tools.length,
16062
16389
  toolNames: tools.map((t) => t?.function?.name ?? t?.name ?? "unknown"),
@@ -16071,14 +16398,14 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16071
16398
  const boundaryContext = createBoundaryRuntimeContext("bun-handler");
16072
16399
  const subagentNames = readSubagentNames();
16073
16400
  const prompt = buildPromptFromMessages(messages, tools, subagentNames);
16074
- const model = boundaryContext.run("normalizeRuntimeModel", (boundary) => boundary.normalizeRuntimeModel(body?.model));
16401
+ const model = boundaryContext.run("resolveRuntimeModel", (boundary) => boundary.resolveRuntimeModel(body?.model, body?.cursorModel));
16075
16402
  const msgSummaryBun = messages.map((m, i) => {
16076
16403
  const role = m?.role ?? "?";
16077
16404
  const hasTc = Array.isArray(m?.tool_calls) ? m.tool_calls.length : 0;
16078
16405
  const clen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr${m.content.length}` : typeof m?.content;
16079
16406
  return `${i}:${role}${hasTc ? `(tc:${hasTc})` : ""}(clen:${clen})`;
16080
16407
  });
16081
- log19.debug("Proxy chat request (bun)", {
16408
+ log20.debug("Proxy chat request (bun)", {
16082
16409
  stream,
16083
16410
  model,
16084
16411
  messages: messages.length,
@@ -16094,7 +16421,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16094
16421
  });
16095
16422
  }
16096
16423
  const cmd = [
16097
- "cursor-agent",
16424
+ resolveCursorAgentBinary(),
16098
16425
  "--print",
16099
16426
  "--output-format",
16100
16427
  "stream-json",
@@ -16124,7 +16451,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16124
16451
  const stdout = (stdoutText || "").trim();
16125
16452
  const stderr = (stderrText || "").trim();
16126
16453
  const exitCode = await child.exited;
16127
- log19.debug("cursor-agent completed (bun non-stream)", {
16454
+ log20.debug("cursor-agent completed (bun non-stream)", {
16128
16455
  exitCode,
16129
16456
  stdoutChars: stdout.length,
16130
16457
  stderrChars: stderr.length
@@ -16150,7 +16477,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16150
16477
  });
16151
16478
  }
16152
16479
  if (intercepted.toolCall) {
16153
- log19.debug("Intercepted OpenCode tool call (non-stream)", {
16480
+ log20.debug("Intercepted OpenCode tool call (non-stream)", {
16154
16481
  name: intercepted.toolCall.function.name,
16155
16482
  callId: intercepted.toolCall.id
16156
16483
  });
@@ -16164,7 +16491,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16164
16491
  const errSource = stderr || stdout || `cursor-agent exited with code ${String(exitCode ?? "unknown")} and no output`;
16165
16492
  const parsed = parseAgentError(errSource);
16166
16493
  const userError = formatErrorForUser(parsed);
16167
- log19.error("cursor-cli failed", {
16494
+ log20.error("cursor-cli failed", {
16168
16495
  type: parsed.type,
16169
16496
  message: parsed.message,
16170
16497
  code: exitCode
@@ -16176,7 +16503,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16176
16503
  });
16177
16504
  }
16178
16505
  const completion = extractCompletionFromStream(stdout);
16179
- const payload = createChatCompletionResponse(model, completion.assistantText || stdout || stderr, completion.reasoningText || undefined);
16506
+ const payload = createChatCompletionResponse(model, completion.assistantText || stdout || stderr, completion.reasoningText || undefined, completion.usage);
16180
16507
  return new Response(JSON.stringify(payload), {
16181
16508
  status: 200,
16182
16509
  headers: { "Content-Type": "application/json" }
@@ -16194,12 +16521,13 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16194
16521
  async start(controller) {
16195
16522
  let streamTerminated = false;
16196
16523
  let firstTokenReceived = false;
16524
+ let usage;
16197
16525
  try {
16198
16526
  const reader = child.stdout.getReader();
16199
16527
  const converter = new StreamToSseConverter(model, { id, created });
16200
16528
  const lineBuffer = new LineBuffer;
16201
16529
  const emitToolCallAndTerminate = (toolCall) => {
16202
- log19.debug("Intercepted OpenCode tool call (stream)", {
16530
+ log20.debug("Intercepted OpenCode tool call (stream)", {
16203
16531
  name: toolCall.function.name,
16204
16532
  callId: toolCall.id
16205
16533
  });
@@ -16248,6 +16576,9 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16248
16576
  if (!event) {
16249
16577
  continue;
16250
16578
  }
16579
+ if (isResult(event)) {
16580
+ usage = extractOpenAiUsageFromResult(event) ?? usage;
16581
+ }
16251
16582
  if (event.type === "tool_call") {
16252
16583
  perf.mark("tool-call");
16253
16584
  const result = await handleToolLoopEventWithFallback({
@@ -16316,6 +16647,9 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16316
16647
  if (!event) {
16317
16648
  continue;
16318
16649
  }
16650
+ if (isResult(event)) {
16651
+ usage = extractOpenAiUsageFromResult(event) ?? usage;
16652
+ }
16319
16653
  if (event.type === "tool_call") {
16320
16654
  const result = await handleToolLoopEventWithFallback({
16321
16655
  event,
@@ -16381,7 +16715,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16381
16715
  const errSource = (stderrText || "").trim() || `cursor-agent exited with code ${String(exitCode ?? "unknown")} and no output`;
16382
16716
  const parsed = parseAgentError(errSource);
16383
16717
  const msg = formatErrorForUser(parsed);
16384
- log19.error("cursor-cli streaming failed", {
16718
+ log20.error("cursor-cli streaming failed", {
16385
16719
  type: parsed.type,
16386
16720
  code: exitCode
16387
16721
  });
@@ -16392,7 +16726,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16392
16726
  controller.enqueue(encoder.encode(formatSseDone()));
16393
16727
  return;
16394
16728
  }
16395
- log19.debug("cursor-agent completed (bun stream)", {
16729
+ log20.debug("cursor-agent completed (bun stream)", {
16396
16730
  exitCode
16397
16731
  });
16398
16732
  const passThroughSummary = passThroughTracker.getSummary();
@@ -16406,6 +16740,12 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16406
16740
  controller.enqueue(encoder.encode(`data: ${JSON.stringify(doneChunk)}
16407
16741
 
16408
16742
  `));
16743
+ if (usage) {
16744
+ const usageChunk = createChatCompletionUsageChunk(id, created, model, usage);
16745
+ controller.enqueue(encoder.encode(`data: ${JSON.stringify(usageChunk)}
16746
+
16747
+ `));
16748
+ }
16409
16749
  controller.enqueue(encoder.encode(formatSseDone()));
16410
16750
  } finally {
16411
16751
  perf.mark("request:done");
@@ -16455,8 +16795,8 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16455
16795
  }
16456
16796
  if (url.pathname === "/v1/models" || url.pathname === "/models") {
16457
16797
  try {
16458
- const { execSync } = await import("child_process");
16459
- const output = execSync("cursor-agent models", { encoding: "utf-8", timeout: 30000 });
16798
+ const { execFileSync: execFileSync2 } = await import("child_process");
16799
+ const output = execFileSync2(resolveCursorAgentBinary(), ["models"], { encoding: "utf-8", timeout: 30000 });
16460
16800
  const clean = stripAnsi(output);
16461
16801
  const models = [];
16462
16802
  for (const line of clean.split(`
@@ -16474,7 +16814,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16474
16814
  res.writeHead(200, { "Content-Type": "application/json" });
16475
16815
  res.end(JSON.stringify({ object: "list", data: models }));
16476
16816
  } catch (err) {
16477
- log19.error("Failed to list models", { error: String(err) });
16817
+ log20.error("Failed to list models", { error: String(err) });
16478
16818
  res.writeHead(500, { "Content-Type": "application/json" });
16479
16819
  res.end(JSON.stringify({ error: "Failed to fetch models" }));
16480
16820
  }
@@ -16485,7 +16825,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16485
16825
  res.end(JSON.stringify({ error: `Unsupported path: ${url.pathname}` }));
16486
16826
  return;
16487
16827
  }
16488
- log19.debug("Proxy request (node)", { method: req.method, path: url.pathname });
16828
+ log20.debug("Proxy request (node)", { method: req.method, path: url.pathname });
16489
16829
  let body = "";
16490
16830
  for await (const chunk of req) {
16491
16831
  body += chunk;
@@ -16500,7 +16840,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16500
16840
  const boundaryContext = createBoundaryRuntimeContext("node-handler");
16501
16841
  const subagentNames = readSubagentNames();
16502
16842
  const prompt = buildPromptFromMessages(messages, tools, subagentNames);
16503
- const model = boundaryContext.run("normalizeRuntimeModel", (boundary) => boundary.normalizeRuntimeModel(bodyData?.model));
16843
+ const model = boundaryContext.run("resolveRuntimeModel", (boundary) => boundary.resolveRuntimeModel(bodyData?.model, bodyData?.cursorModel));
16504
16844
  const msgSummary = messages.map((m, i) => {
16505
16845
  const role = m?.role ?? "?";
16506
16846
  const hasTc = Array.isArray(m?.tool_calls) ? m.tool_calls.length : 0;
@@ -16509,7 +16849,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16509
16849
  const contentLen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr${m.content.length}` : typeof m?.content;
16510
16850
  return `${i}:${role}${hasTc ? `(tc:${hasTc})` : ""}${role === "tool" ? `(tcid:${tcId},name:${tcName},clen:${contentLen})` : `(clen:${contentLen})`}`;
16511
16851
  });
16512
- log19.debug("Proxy chat request (node)", {
16852
+ log20.debug("Proxy chat request (node)", {
16513
16853
  stream,
16514
16854
  model,
16515
16855
  messages: messages.length,
@@ -16518,7 +16858,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16518
16858
  msgRoles: msgSummary.join(",")
16519
16859
  });
16520
16860
  const cmd = [
16521
- "cursor-agent",
16861
+ resolveCursorAgentBinary(),
16522
16862
  "--print",
16523
16863
  "--output-format",
16524
16864
  "stream-json",
@@ -16531,7 +16871,10 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16531
16871
  if (FORCE_TOOL_MODE) {
16532
16872
  cmd.push("--force");
16533
16873
  }
16534
- const child = spawn3(cmd[0], cmd.slice(1), { stdio: ["pipe", "pipe", "pipe"] });
16874
+ const child = spawn3(cmd[0], cmd.slice(1), {
16875
+ stdio: ["pipe", "pipe", "pipe"],
16876
+ shell: process.platform === "win32"
16877
+ });
16535
16878
  child.stdin.write(prompt);
16536
16879
  child.stdin.end();
16537
16880
  if (!stream) {
@@ -16540,14 +16883,14 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16540
16883
  let spawnErrorText = null;
16541
16884
  child.on("error", (error) => {
16542
16885
  spawnErrorText = String(error?.message || error);
16543
- log19.error("Failed to spawn cursor-agent", { error: spawnErrorText, model });
16886
+ log20.error("Failed to spawn cursor-agent", { error: spawnErrorText, model });
16544
16887
  });
16545
16888
  child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
16546
16889
  child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
16547
16890
  child.on("close", async (code) => {
16548
16891
  const stdout = Buffer.concat(stdoutChunks).toString().trim();
16549
16892
  const stderr = Buffer.concat(stderrChunks).toString().trim();
16550
- log19.debug("cursor-agent completed (node non-stream)", {
16893
+ log20.debug("cursor-agent completed (node non-stream)", {
16551
16894
  code,
16552
16895
  stdoutChars: stdout.length,
16553
16896
  stderrChars: stderr.length,
@@ -16573,7 +16916,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16573
16916
  return;
16574
16917
  }
16575
16918
  if (intercepted.toolCall) {
16576
- log19.debug("Intercepted OpenCode tool call (non-stream)", {
16919
+ log20.debug("Intercepted OpenCode tool call (non-stream)", {
16577
16920
  name: intercepted.toolCall.function.name,
16578
16921
  callId: intercepted.toolCall.id
16579
16922
  });
@@ -16587,7 +16930,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16587
16930
  const errSource = stderr || stdout || spawnErrorText || `cursor-agent exited with code ${String(code ?? "unknown")} and no output`;
16588
16931
  const parsed = parseAgentError(errSource);
16589
16932
  const userError = formatErrorForUser(parsed);
16590
- log19.error("cursor-cli failed", {
16933
+ log20.error("cursor-cli failed", {
16591
16934
  type: parsed.type,
16592
16935
  message: parsed.message,
16593
16936
  code
@@ -16597,7 +16940,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16597
16940
  res.end(JSON.stringify(errorResponse));
16598
16941
  return;
16599
16942
  }
16600
- const response = createChatCompletionResponse(model, completion.assistantText || stdout || stderr, completion.reasoningText || undefined);
16943
+ const response = createChatCompletionResponse(model, completion.assistantText || stdout || stderr, completion.reasoningText || undefined, completion.usage);
16601
16944
  res.writeHead(200, { "Content-Type": "application/json" });
16602
16945
  res.end(JSON.stringify(response));
16603
16946
  });
@@ -16619,6 +16962,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16619
16962
  const stderrChunks = [];
16620
16963
  let streamTerminated = false;
16621
16964
  let firstTokenReceived = false;
16965
+ let usage;
16622
16966
  child.stderr.on("data", (chunk) => {
16623
16967
  stderrChunks.push(Buffer.from(chunk));
16624
16968
  });
@@ -16627,7 +16971,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16627
16971
  return;
16628
16972
  }
16629
16973
  const errSource = String(error?.message || error);
16630
- log19.error("Failed to spawn cursor-agent (stream)", { error: errSource, model });
16974
+ log20.error("Failed to spawn cursor-agent (stream)", { error: errSource, model });
16631
16975
  const parsed = parseAgentError(errSource);
16632
16976
  const msg = formatErrorForUser(parsed);
16633
16977
  const errChunk = createChatCompletionChunk(id, created, model, msg, true);
@@ -16642,7 +16986,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16642
16986
  if (streamTerminated || res.writableEnded) {
16643
16987
  return;
16644
16988
  }
16645
- log19.debug("Intercepted OpenCode tool call (stream)", {
16989
+ log20.debug("Intercepted OpenCode tool call (stream)", {
16646
16990
  name: toolCall.function.name,
16647
16991
  callId: toolCall.id
16648
16992
  });
@@ -16690,6 +17034,9 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16690
17034
  if (!event) {
16691
17035
  continue;
16692
17036
  }
17037
+ if (isResult(event)) {
17038
+ usage = extractOpenAiUsageFromResult(event) ?? usage;
17039
+ }
16693
17040
  if (event.type === "tool_call") {
16694
17041
  perf.mark("tool-call");
16695
17042
  const result = await handleToolLoopEventWithFallback({
@@ -16762,6 +17109,9 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16762
17109
  if (!event) {
16763
17110
  continue;
16764
17111
  }
17112
+ if (isResult(event)) {
17113
+ usage = extractOpenAiUsageFromResult(event) ?? usage;
17114
+ }
16765
17115
  if (event.type === "tool_call") {
16766
17116
  const result = await handleToolLoopEventWithFallback({
16767
17117
  event,
@@ -16826,7 +17176,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16826
17176
  perf.mark("request:done");
16827
17177
  perf.summarize();
16828
17178
  const stderrText = Buffer.concat(stderrChunks).toString().trim();
16829
- log19.debug("cursor-agent completed (node stream)", {
17179
+ log20.debug("cursor-agent completed (node stream)", {
16830
17180
  code,
16831
17181
  stderrChars: stderrText.length
16832
17182
  });
@@ -16866,6 +17216,12 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
16866
17216
  res.write(`data: ${JSON.stringify(doneChunk)}
16867
17217
 
16868
17218
  `);
17219
+ if (usage) {
17220
+ const usageChunk = createChatCompletionUsageChunk(id, created, model, usage);
17221
+ res.write(`data: ${JSON.stringify(usageChunk)}
17222
+
17223
+ `);
17224
+ }
16869
17225
  res.write(formatSseDone());
16870
17226
  res.end();
16871
17227
  });
@@ -16943,7 +17299,7 @@ function jsonSchemaToZod(jsonSchema) {
16943
17299
  }
16944
17300
  break;
16945
17301
  case "object":
16946
- zodType = z2.record(z2.any());
17302
+ zodType = z2.record(z2.string(), z2.any());
16947
17303
  if (p.description) {
16948
17304
  zodType = zodType.describe(p.description);
16949
17305
  }
@@ -17054,7 +17410,7 @@ function buildToolHookEntries(registry, fallbackBaseDir) {
17054
17410
  const normalizedArgs = applyToolContextDefaults(toolName, args, context, fallbackBaseDir, sessionWorkspaceBySession);
17055
17411
  return await handler(normalizedArgs);
17056
17412
  } catch (error) {
17057
- log19.debug("Tool hook execution failed", { tool: toolName, error: String(error?.message || error) });
17413
+ log20.debug("Tool hook execution failed", { tool: toolName, error: String(error?.message || error) });
17058
17414
  throw error;
17059
17415
  }
17060
17416
  }
@@ -17066,9 +17422,9 @@ function buildToolHookEntries(registry, fallbackBaseDir) {
17066
17422
  }
17067
17423
  return entries;
17068
17424
  }
17069
- var log19, 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 }) => {
17425
+ var log20, 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 }) => {
17070
17426
  const workspaceDirectory = resolveWorkspaceDirectory(worktree, directory);
17071
- log19.debug("Plugin initializing", {
17427
+ log20.debug("Plugin initializing", {
17072
17428
  directory,
17073
17429
  worktree,
17074
17430
  workspaceDirectory,
@@ -17076,22 +17432,22 @@ var log19, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
17076
17432
  serverUrl: serverUrl?.toString()
17077
17433
  });
17078
17434
  if (!TOOL_LOOP_MODE_VALID) {
17079
- log19.warn("Invalid CURSOR_ACP_TOOL_LOOP_MODE; defaulting to opencode", { value: TOOL_LOOP_MODE_RAW });
17435
+ log20.warn("Invalid CURSOR_ACP_TOOL_LOOP_MODE; defaulting to opencode", { value: TOOL_LOOP_MODE_RAW });
17080
17436
  }
17081
17437
  if (!PROVIDER_BOUNDARY_MODE_VALID) {
17082
- log19.warn("Invalid CURSOR_ACP_PROVIDER_BOUNDARY; defaulting to v1", {
17438
+ log20.warn("Invalid CURSOR_ACP_PROVIDER_BOUNDARY; defaulting to v1", {
17083
17439
  value: PROVIDER_BOUNDARY_MODE_RAW
17084
17440
  });
17085
17441
  }
17086
17442
  if (!TOOL_LOOP_MAX_REPEAT_VALID) {
17087
- log19.warn("Invalid CURSOR_ACP_TOOL_LOOP_MAX_REPEAT; defaulting to 3", {
17443
+ log20.warn("Invalid CURSOR_ACP_TOOL_LOOP_MAX_REPEAT; defaulting to 3", {
17088
17444
  value: TOOL_LOOP_MAX_REPEAT_RAW
17089
17445
  });
17090
17446
  }
17091
17447
  if (ENABLE_PROVIDER_BOUNDARY_AUTOFALLBACK && PROVIDER_BOUNDARY.mode !== "v1") {
17092
- log19.debug("Provider boundary auto-fallback is enabled but inactive unless mode=v1");
17448
+ log20.debug("Provider boundary auto-fallback is enabled but inactive unless mode=v1");
17093
17449
  }
17094
- log19.info("Tool loop mode configured", {
17450
+ log20.info("Tool loop mode configured", {
17095
17451
  mode: TOOL_LOOP_MODE,
17096
17452
  providerBoundary: PROVIDER_BOUNDARY.mode,
17097
17453
  proxyExecToolCalls: PROXY_EXECUTE_TOOL_CALLS,
@@ -17109,13 +17465,13 @@ var log19, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
17109
17465
  try {
17110
17466
  const configs = readMcpConfigs();
17111
17467
  if (configs.length === 0) {
17112
- log19.debug("No MCP servers configured, skipping MCP bridge");
17468
+ log20.debug("No MCP servers configured, skipping MCP bridge");
17113
17469
  } else {
17114
- log19.debug("MCP bridge: connecting to servers", { count: configs.length });
17470
+ log20.debug("MCP bridge: connecting to servers", { count: configs.length });
17115
17471
  await Promise.allSettled(configs.map((c) => mcpManager.connectServer(c)));
17116
17472
  const tools = mcpManager.listTools();
17117
17473
  if (tools.length === 0) {
17118
- log19.debug("MCP bridge: no tools discovered");
17474
+ log20.debug("MCP bridge: no tools discovered");
17119
17475
  } else {
17120
17476
  mcpToolEntries = buildMcpToolHookEntries(tools, mcpManager);
17121
17477
  mcpToolDefs = buildMcpToolDefinitions(tools);
@@ -17125,23 +17481,23 @@ var log19, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
17125
17481
  description: t.description,
17126
17482
  params: t.inputSchema ? Object.keys(t.inputSchema.properties ?? {}) : undefined
17127
17483
  }));
17128
- log19.info("MCP bridge: registered tools", {
17484
+ log20.info("MCP bridge: registered tools", {
17129
17485
  servers: mcpManager.connectedServers.length,
17130
17486
  tools: Object.keys(mcpToolEntries).length
17131
17487
  });
17132
17488
  }
17133
17489
  }
17134
17490
  } catch (err) {
17135
- log19.debug("MCP bridge init failed", { error: String(err) });
17491
+ log20.debug("MCP bridge init failed", { error: String(err) });
17136
17492
  }
17137
17493
  }
17138
17494
  toastService.setClient(client3);
17139
17495
  const toolsEnabled = process.env.CURSOR_ACP_ENABLE_OPENCODE_TOOLS !== "false";
17140
17496
  const legacyProxyToolPathsEnabled = toolsEnabled && TOOL_LOOP_MODE === "proxy-exec";
17141
17497
  if (toolsEnabled && TOOL_LOOP_MODE === "opencode") {
17142
- log19.debug("OpenCode mode active; skipping legacy SDK/MCP discovery and proxy-side tool execution");
17498
+ log20.debug("OpenCode mode active; skipping legacy SDK/MCP discovery and proxy-side tool execution");
17143
17499
  } else if (toolsEnabled && TOOL_LOOP_MODE === "off") {
17144
- log19.debug("Tool loop mode off; proxy-side tool execution disabled");
17500
+ log20.debug("Tool loop mode off; proxy-side tool execution disabled");
17145
17501
  }
17146
17502
  const serverClient = legacyProxyToolPathsEnabled ? createOpencodeClient({ baseUrl: serverUrl.toString(), directory: workspaceDirectory }) : null;
17147
17503
  const discovery = legacyProxyToolPathsEnabled ? new OpenCodeToolDiscovery(serverClient ?? client3) : null;
@@ -17193,7 +17549,7 @@ var log19, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
17193
17549
  discoveredList = await discovery.listTools();
17194
17550
  discoveredList.forEach((t) => toolsByName.set(t.name, t));
17195
17551
  } catch (err) {
17196
- log19.debug("Tool discovery failed, using local tools only", { error: String(err) });
17552
+ log20.debug("Tool discovery failed, using local tools only", { error: String(err) });
17197
17553
  }
17198
17554
  }
17199
17555
  const allTools = [...localTools, ...discoveredList];
@@ -17223,11 +17579,11 @@ var log19, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
17223
17579
  }
17224
17580
  lastToolNames = toolEntries.map((e) => e.function.name);
17225
17581
  lastToolMap = allTools.map((t) => ({ id: t.id, name: t.name }));
17226
- log19.debug("Tools refreshed", { local: localTools.length, discovered: discoveredList.length, total: toolEntries.length });
17582
+ log20.debug("Tools refreshed", { local: localTools.length, discovered: discoveredList.length, total: toolEntries.length });
17227
17583
  return toolEntries;
17228
17584
  }
17229
17585
  const proxyBaseURL = await ensureCursorProxyServer(workspaceDirectory, router);
17230
- log19.debug("Proxy server started", { baseURL: proxyBaseURL });
17586
+ log20.debug("Proxy server started", { baseURL: proxyBaseURL });
17231
17587
  const toolHookEntries = buildToolHookEntries(localRegistry, workspaceDirectory);
17232
17588
  return {
17233
17589
  tool: { ...toolHookEntries, ...mcpToolEntries },
@@ -17242,9 +17598,9 @@ var log19, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
17242
17598
  type: "oauth",
17243
17599
  async authorize() {
17244
17600
  try {
17245
- log19.info("Starting OAuth flow");
17601
+ log20.info("Starting OAuth flow");
17246
17602
  const { url, instructions, callback } = await startCursorOAuth();
17247
- log19.debug("Got OAuth URL", { url: url.substring(0, 50) + "..." });
17603
+ log20.debug("Got OAuth URL", { url: url.substring(0, 50) + "..." });
17248
17604
  return {
17249
17605
  url,
17250
17606
  instructions,
@@ -17252,7 +17608,7 @@ var log19, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
17252
17608
  callback
17253
17609
  };
17254
17610
  } catch (error) {
17255
- log19.error("OAuth error", { error });
17611
+ log20.error("OAuth error", { error });
17256
17612
  throw error;
17257
17613
  }
17258
17614
  }
@@ -17276,10 +17632,10 @@ var log19, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
17276
17632
  output.options.tools = resolved.tools;
17277
17633
  } else if (resolved.action === "preserve") {
17278
17634
  const count = Array.isArray(existingTools) ? existingTools.length : 0;
17279
- log19.debug("Using OpenCode-provided tools from chat.params", { count });
17635
+ log20.debug("Using OpenCode-provided tools from chat.params", { count });
17280
17636
  }
17281
17637
  } catch (err) {
17282
- log19.debug("Failed to refresh tools", { error: String(err) });
17638
+ log20.debug("Failed to refresh tools", { error: String(err) });
17283
17639
  }
17284
17640
  }
17285
17641
  if (mcpToolDefs.length > 0) {
@@ -17290,7 +17646,7 @@ var log19, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
17290
17646
  output.options.tools = mcpToolDefs;
17291
17647
  }
17292
17648
  const afterTools = Array.isArray(output.options.tools) ? output.options.tools : [];
17293
- log19.debug("Injected MCP tool definitions into chat.params", {
17649
+ log20.debug("Injected MCP tool definitions into chat.params", {
17294
17650
  injectedCount: mcpToolDefs.length,
17295
17651
  beforeCount: beforeTools.length,
17296
17652
  afterCount: afterTools.length,
@@ -17331,14 +17687,16 @@ var init_plugin = __esm(() => {
17331
17687
  init_sdk();
17332
17688
  init_mcp();
17333
17689
  init_executor();
17690
+ init_defaults();
17334
17691
  init_boundary();
17335
17692
  init_runtime_interception();
17336
17693
  init_toast_service();
17337
17694
  init_tool_schema_compat();
17338
17695
  init_tool_loop_guard();
17339
- log19 = createLogger("plugin");
17340
- DEBUG_LOG_DIR2 = join5(homedir5(), ".config", "opencode", "logs");
17341
- DEBUG_LOG_FILE2 = join5(DEBUG_LOG_DIR2, "tool-loop-debug.log");
17696
+ init_binary();
17697
+ log20 = createLogger("plugin");
17698
+ DEBUG_LOG_DIR2 = join6(homedir5(), ".config", "opencode", "logs");
17699
+ DEBUG_LOG_FILE2 = join6(DEBUG_LOG_DIR2, "tool-loop-debug.log");
17342
17700
  CURSOR_PROVIDER_PREFIX = `${CURSOR_PROVIDER_ID2}/`;
17343
17701
  CURSOR_PROXY_DEFAULT_BASE_URL = `http://${CURSOR_PROXY_HOST}:${CURSOR_PROXY_DEFAULT_PORT}/v1`;
17344
17702
  REUSE_EXISTING_PROXY = process.env.CURSOR_ACP_REUSE_EXISTING_PROXY !== "false";
@@ -17374,6 +17732,7 @@ init_plugin();
17374
17732
  // src/client/simple.ts
17375
17733
  init_parser();
17376
17734
  init_logger();
17735
+ init_binary();
17377
17736
  import { spawn as spawn3 } from "child_process";
17378
17737
 
17379
17738
  class SimpleCursorClient {
@@ -17384,7 +17743,7 @@ class SimpleCursorClient {
17384
17743
  timeout: 30000,
17385
17744
  maxRetries: 3,
17386
17745
  streamOutput: true,
17387
- cursorAgentPath: process.env.CURSOR_AGENT_EXECUTABLE || "cursor-agent",
17746
+ cursorAgentPath: resolveCursorAgentBinary(),
17388
17747
  ...config
17389
17748
  };
17390
17749
  this.log = createLogger("cursor-client");
@@ -17418,7 +17777,8 @@ class SimpleCursorClient {
17418
17777
  this.log.debug("Executing prompt stream", { promptLength: prompt.length, mode, model });
17419
17778
  const child = spawn3(this.config.cursorAgentPath, args, {
17420
17779
  cwd,
17421
- stdio: ["pipe", "pipe", "pipe"]
17780
+ stdio: ["pipe", "pipe", "pipe"],
17781
+ shell: process.platform === "win32"
17422
17782
  });
17423
17783
  if (prompt) {
17424
17784
  child.stdin.write(prompt);
@@ -17504,7 +17864,8 @@ class SimpleCursorClient {
17504
17864
  return new Promise((resolve3, reject) => {
17505
17865
  const child = spawn3(this.config.cursorAgentPath, args, {
17506
17866
  cwd,
17507
- stdio: ["pipe", "pipe", "pipe"]
17867
+ stdio: ["pipe", "pipe", "pipe"],
17868
+ shell: process.platform === "win32"
17508
17869
  });
17509
17870
  let stdoutBuffer = "";
17510
17871
  let stderrBuffer = "";
@@ -17593,7 +17954,7 @@ init_logger();
17593
17954
  import { execSync } from "node:child_process";
17594
17955
  import { createServer } from "node:net";
17595
17956
  import { platform as platform2 } from "node:os";
17596
- var log20 = createLogger("proxy-server");
17957
+ var log21 = createLogger("proxy-server");
17597
17958
  var DEFAULT_PORT = 32124;
17598
17959
  var PORT_RANGE_SIZE = 256;
17599
17960
  async function isPortAvailable(port, host) {
@@ -17640,11 +18001,11 @@ function getUsedPortsInRange(minPort, maxPort) {
17640
18001
  }
17641
18002
  }
17642
18003
  } else {
17643
- log20.debug(`Port detection not supported on ${os2}. Using probe-based discovery.`);
18004
+ log21.debug(`Port detection not supported on ${os2}. Using probe-based discovery.`);
17644
18005
  }
17645
18006
  } catch (error) {
17646
18007
  const msg = error instanceof Error ? error.message : String(error);
17647
- log20.debug(`Port detection failed: ${msg}. Using probe-based discovery.`);
18008
+ log21.debug(`Port detection failed: ${msg}. Using probe-based discovery.`);
17648
18009
  }
17649
18010
  return used;
17650
18011
  }
@@ -17695,7 +18056,7 @@ function createProxyServer(config) {
17695
18056
  const err = error instanceof Error ? error : new Error(String(error));
17696
18057
  const isPortInUse = err.message.includes("EADDRINUSE") || err.message.includes("address already in use") || err.message.includes("port is already in use");
17697
18058
  if (!isPortInUse) {
17698
- log20.debug(`Unexpected error starting on port ${port}: ${err.message}`);
18059
+ log21.debug(`Unexpected error starting on port ${port}: ${err.message}`);
17699
18060
  }
17700
18061
  return { success: false, error: err };
17701
18062
  }
@@ -17711,13 +18072,13 @@ function createProxyServer(config) {
17711
18072
  if (result.success) {
17712
18073
  port = requestedPort;
17713
18074
  } else {
17714
- log20.debug(`Requested port ${requestedPort} unavailable: ${result.error?.message ?? "unknown"}. Falling back to automatic port selection.`);
18075
+ log21.debug(`Requested port ${requestedPort} unavailable: ${result.error?.message ?? "unknown"}. Falling back to automatic port selection.`);
17715
18076
  port = await findAvailablePort(host);
17716
18077
  const fallbackResult = tryStart(port);
17717
18078
  if (!fallbackResult.success) {
17718
18079
  throw new Error(`Failed to start server on port ${requestedPort} (${result.error?.message ?? "unknown"}) ` + `and fallback port ${port} (${fallbackResult.error?.message ?? "unknown"})`);
17719
18080
  }
17720
- log20.debug(`Server started on fallback port ${port} instead of requested port ${requestedPort}`);
18081
+ log21.debug(`Server started on fallback port ${port} instead of requested port ${requestedPort}`);
17721
18082
  }
17722
18083
  } else {
17723
18084
  port = await findAvailablePort(host);
@@ -18046,8 +18407,8 @@ function parseOpenAIRequest(body) {
18046
18407
  };
18047
18408
  }
18048
18409
  // src/proxy/formatter.ts
18049
- function createChatCompletionResponse2(model, content) {
18050
- return {
18410
+ function createChatCompletionResponse2(model, content, usage) {
18411
+ const response = {
18051
18412
  id: `cursor-acp-${Date.now()}`,
18052
18413
  object: "chat.completion",
18053
18414
  created: Math.floor(Date.now() / 1000),
@@ -18058,13 +18419,12 @@ function createChatCompletionResponse2(model, content) {
18058
18419
  message: { role: "assistant", content },
18059
18420
  finish_reason: "stop"
18060
18421
  }
18061
- ],
18062
- usage: {
18063
- prompt_tokens: 0,
18064
- completion_tokens: 0,
18065
- total_tokens: 0
18066
- }
18422
+ ]
18067
18423
  };
18424
+ if (usage) {
18425
+ response.usage = usage;
18426
+ }
18427
+ return response;
18068
18428
  }
18069
18429
  function createChatCompletionChunk2(id, created, model, deltaContent, done = false) {
18070
18430
  return {
@@ -18089,11 +18449,11 @@ init_auth();
18089
18449
  init_auth();
18090
18450
  init_logger();
18091
18451
  import { existsSync as existsSync6 } from "fs";
18092
- var log21 = createLogger("status");
18452
+ var log22 = createLogger("status");
18093
18453
  function checkAuthStatus() {
18094
18454
  const authFilePath = getAuthFilePath();
18095
18455
  const exists = existsSync6(authFilePath);
18096
- log21.debug("Checking auth status", { path: authFilePath });
18456
+ log22.debug("Checking auth status", { path: authFilePath });
18097
18457
  if (exists) {
18098
18458
  return {
18099
18459
  authenticated: true,