@rama_nigg/open-cursor 2.1.7 → 2.2.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/cli/discover.js +15 -0
- package/dist/cli/opencode-cursor.js +42 -8
- package/dist/index.js +521 -200
- package/dist/plugin-entry.js +521 -200
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
2
4
|
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
3
18
|
var __export = (target, all) => {
|
|
4
19
|
for (var name in all)
|
|
5
20
|
__defProp(target, name, {
|
|
@@ -12833,6 +12848,9 @@ ${result.output || "(no output)"}`
|
|
|
12833
12848
|
}
|
|
12834
12849
|
|
|
12835
12850
|
// src/utils/logger.ts
|
|
12851
|
+
import * as fs from "node:fs";
|
|
12852
|
+
import * as path from "node:path";
|
|
12853
|
+
import * as os from "node:os";
|
|
12836
12854
|
function getConfiguredLevel() {
|
|
12837
12855
|
const env = process.env.CURSOR_ACP_LOG_LEVEL?.toLowerCase();
|
|
12838
12856
|
if (env && env in LEVEL_PRIORITY) {
|
|
@@ -12863,32 +12881,90 @@ function formatMessage(level, component, message, data) {
|
|
|
12863
12881
|
}
|
|
12864
12882
|
return formatted;
|
|
12865
12883
|
}
|
|
12884
|
+
function isConsoleEnabled() {
|
|
12885
|
+
const consoleEnv = process.env.CURSOR_ACP_LOG_CONSOLE;
|
|
12886
|
+
return consoleEnv === "1" || consoleEnv === "true";
|
|
12887
|
+
}
|
|
12888
|
+
function ensureLogDir() {
|
|
12889
|
+
if (logDirEnsured)
|
|
12890
|
+
return;
|
|
12891
|
+
try {
|
|
12892
|
+
if (!fs.existsSync(LOG_DIR)) {
|
|
12893
|
+
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
12894
|
+
}
|
|
12895
|
+
logDirEnsured = true;
|
|
12896
|
+
} catch {
|
|
12897
|
+
logFileError = true;
|
|
12898
|
+
}
|
|
12899
|
+
}
|
|
12900
|
+
function rotateIfNeeded() {
|
|
12901
|
+
try {
|
|
12902
|
+
const stats = fs.statSync(LOG_FILE);
|
|
12903
|
+
if (stats.size >= MAX_LOG_SIZE) {
|
|
12904
|
+
const backupFile = LOG_FILE + ".1";
|
|
12905
|
+
fs.renameSync(LOG_FILE, backupFile);
|
|
12906
|
+
}
|
|
12907
|
+
} catch {}
|
|
12908
|
+
}
|
|
12909
|
+
function writeToFile(message) {
|
|
12910
|
+
if (logFileError)
|
|
12911
|
+
return;
|
|
12912
|
+
ensureLogDir();
|
|
12913
|
+
if (logFileError)
|
|
12914
|
+
return;
|
|
12915
|
+
try {
|
|
12916
|
+
rotateIfNeeded();
|
|
12917
|
+
const timestamp = new Date().toISOString();
|
|
12918
|
+
fs.appendFileSync(LOG_FILE, `${timestamp} ${message}
|
|
12919
|
+
`);
|
|
12920
|
+
} catch {
|
|
12921
|
+
if (!logFileError) {
|
|
12922
|
+
logFileError = true;
|
|
12923
|
+
console.error(`[cursor-acp] Failed to write logs. Using: ${LOG_FILE}`);
|
|
12924
|
+
}
|
|
12925
|
+
}
|
|
12926
|
+
}
|
|
12866
12927
|
function createLogger(component) {
|
|
12867
12928
|
return {
|
|
12868
12929
|
debug: (message, data) => {
|
|
12869
|
-
if (shouldLog("debug"))
|
|
12870
|
-
|
|
12871
|
-
|
|
12930
|
+
if (!shouldLog("debug"))
|
|
12931
|
+
return;
|
|
12932
|
+
const formatted = formatMessage("debug", component, message, data);
|
|
12933
|
+
writeToFile(formatted);
|
|
12934
|
+
if (isConsoleEnabled())
|
|
12935
|
+
console.error(formatted);
|
|
12872
12936
|
},
|
|
12873
12937
|
info: (message, data) => {
|
|
12874
|
-
if (shouldLog("info"))
|
|
12875
|
-
|
|
12876
|
-
|
|
12938
|
+
if (!shouldLog("info"))
|
|
12939
|
+
return;
|
|
12940
|
+
const formatted = formatMessage("info", component, message, data);
|
|
12941
|
+
writeToFile(formatted);
|
|
12942
|
+
if (isConsoleEnabled())
|
|
12943
|
+
console.error(formatted);
|
|
12877
12944
|
},
|
|
12878
12945
|
warn: (message, data) => {
|
|
12879
|
-
if (shouldLog("warn"))
|
|
12880
|
-
|
|
12881
|
-
|
|
12946
|
+
if (!shouldLog("warn"))
|
|
12947
|
+
return;
|
|
12948
|
+
const formatted = formatMessage("warn", component, message, data);
|
|
12949
|
+
writeToFile(formatted);
|
|
12950
|
+
if (isConsoleEnabled())
|
|
12951
|
+
console.error(formatted);
|
|
12882
12952
|
},
|
|
12883
12953
|
error: (message, data) => {
|
|
12884
|
-
if (shouldLog("error"))
|
|
12885
|
-
|
|
12886
|
-
|
|
12954
|
+
if (!shouldLog("error"))
|
|
12955
|
+
return;
|
|
12956
|
+
const formatted = formatMessage("error", component, message, data);
|
|
12957
|
+
writeToFile(formatted);
|
|
12958
|
+
if (isConsoleEnabled())
|
|
12959
|
+
console.error(formatted);
|
|
12887
12960
|
}
|
|
12888
12961
|
};
|
|
12889
12962
|
}
|
|
12890
|
-
var LEVEL_PRIORITY;
|
|
12963
|
+
var LOG_DIR, LOG_FILE, MAX_LOG_SIZE, LEVEL_PRIORITY, logDirEnsured = false, logFileError = false;
|
|
12891
12964
|
var init_logger = __esm(() => {
|
|
12965
|
+
LOG_DIR = path.join(os.homedir(), ".opencode-cursor");
|
|
12966
|
+
LOG_FILE = path.join(LOG_DIR, "plugin.log");
|
|
12967
|
+
MAX_LOG_SIZE = 5 * 1024 * 1024;
|
|
12892
12968
|
LEVEL_PRIORITY = {
|
|
12893
12969
|
debug: 0,
|
|
12894
12970
|
info: 1,
|
|
@@ -12989,15 +13065,15 @@ function formatErrorForUser(error45) {
|
|
|
12989
13065
|
|
|
12990
13066
|
// src/auth.ts
|
|
12991
13067
|
import { spawn } from "child_process";
|
|
12992
|
-
import { existsSync } from "fs";
|
|
12993
|
-
import { homedir, platform } from "os";
|
|
12994
|
-
import { join } from "path";
|
|
13068
|
+
import { existsSync as existsSync2 } from "fs";
|
|
13069
|
+
import { homedir as homedir2, platform } from "os";
|
|
13070
|
+
import { join as join2 } from "path";
|
|
12995
13071
|
function getHomeDir() {
|
|
12996
13072
|
const override = process.env.CURSOR_ACP_HOME_DIR;
|
|
12997
13073
|
if (override && override.length > 0) {
|
|
12998
13074
|
return override;
|
|
12999
13075
|
}
|
|
13000
|
-
return
|
|
13076
|
+
return homedir2();
|
|
13001
13077
|
}
|
|
13002
13078
|
async function pollForAuthFile(timeoutMs = AUTH_POLL_TIMEOUT, intervalMs = AUTH_POLL_INTERVAL) {
|
|
13003
13079
|
const startTime = Date.now();
|
|
@@ -13006,7 +13082,7 @@ async function pollForAuthFile(timeoutMs = AUTH_POLL_TIMEOUT, intervalMs = AUTH_
|
|
|
13006
13082
|
const check2 = () => {
|
|
13007
13083
|
const elapsed = Date.now() - startTime;
|
|
13008
13084
|
for (const authPath of possiblePaths) {
|
|
13009
|
-
if (
|
|
13085
|
+
if (existsSync2(authPath)) {
|
|
13010
13086
|
log.debug("Auth file detected", { path: authPath });
|
|
13011
13087
|
resolve(true);
|
|
13012
13088
|
return;
|
|
@@ -13133,7 +13209,7 @@ async function startCursorOAuth() {
|
|
|
13133
13209
|
function verifyCursorAuth() {
|
|
13134
13210
|
const possiblePaths = getPossibleAuthPaths();
|
|
13135
13211
|
for (const authPath of possiblePaths) {
|
|
13136
|
-
if (
|
|
13212
|
+
if (existsSync2(authPath)) {
|
|
13137
13213
|
log.debug("Auth file found", { path: authPath });
|
|
13138
13214
|
return true;
|
|
13139
13215
|
}
|
|
@@ -13148,23 +13224,23 @@ function getPossibleAuthPaths() {
|
|
|
13148
13224
|
const authFiles = ["cli-config.json", "auth.json"];
|
|
13149
13225
|
if (isDarwin) {
|
|
13150
13226
|
for (const file2 of authFiles) {
|
|
13151
|
-
paths.push(
|
|
13227
|
+
paths.push(join2(home, ".cursor", file2));
|
|
13152
13228
|
}
|
|
13153
13229
|
for (const file2 of authFiles) {
|
|
13154
|
-
paths.push(
|
|
13230
|
+
paths.push(join2(home, ".config", "cursor", file2));
|
|
13155
13231
|
}
|
|
13156
13232
|
} else {
|
|
13157
13233
|
for (const file2 of authFiles) {
|
|
13158
|
-
paths.push(
|
|
13234
|
+
paths.push(join2(home, ".config", "cursor", file2));
|
|
13159
13235
|
}
|
|
13160
13236
|
const xdgConfig = process.env.XDG_CONFIG_HOME;
|
|
13161
|
-
if (xdgConfig && xdgConfig !==
|
|
13237
|
+
if (xdgConfig && xdgConfig !== join2(home, ".config")) {
|
|
13162
13238
|
for (const file2 of authFiles) {
|
|
13163
|
-
paths.push(
|
|
13239
|
+
paths.push(join2(xdgConfig, "cursor", file2));
|
|
13164
13240
|
}
|
|
13165
13241
|
}
|
|
13166
13242
|
for (const file2 of authFiles) {
|
|
13167
|
-
paths.push(
|
|
13243
|
+
paths.push(join2(home, ".cursor", file2));
|
|
13168
13244
|
}
|
|
13169
13245
|
}
|
|
13170
13246
|
return paths;
|
|
@@ -13172,7 +13248,7 @@ function getPossibleAuthPaths() {
|
|
|
13172
13248
|
function getAuthFilePath() {
|
|
13173
13249
|
const possiblePaths = getPossibleAuthPaths();
|
|
13174
13250
|
for (const authPath of possiblePaths) {
|
|
13175
|
-
if (
|
|
13251
|
+
if (existsSync2(authPath)) {
|
|
13176
13252
|
return authPath;
|
|
13177
13253
|
}
|
|
13178
13254
|
}
|
|
@@ -13400,7 +13476,68 @@ var init_perf = __esm(() => {
|
|
|
13400
13476
|
});
|
|
13401
13477
|
|
|
13402
13478
|
// src/proxy/prompt-builder.ts
|
|
13479
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "node:fs";
|
|
13480
|
+
import { homedir as homedir3 } from "node:os";
|
|
13481
|
+
import { join as join3 } from "node:path";
|
|
13482
|
+
function ensureLogDir2() {
|
|
13483
|
+
try {
|
|
13484
|
+
if (!existsSync3(DEBUG_LOG_DIR)) {
|
|
13485
|
+
mkdirSync2(DEBUG_LOG_DIR, { recursive: true });
|
|
13486
|
+
}
|
|
13487
|
+
} catch {}
|
|
13488
|
+
}
|
|
13489
|
+
function debugLogToFile(message, data) {
|
|
13490
|
+
try {
|
|
13491
|
+
ensureLogDir2();
|
|
13492
|
+
const timestamp = new Date().toISOString();
|
|
13493
|
+
const logLine = `[${timestamp}] ${message}: ${JSON.stringify(data, null, 2)}
|
|
13494
|
+
`;
|
|
13495
|
+
appendFileSync2(DEBUG_LOG_FILE, logLine);
|
|
13496
|
+
} catch (err) {
|
|
13497
|
+
log4.debug(message, data);
|
|
13498
|
+
}
|
|
13499
|
+
}
|
|
13403
13500
|
function buildPromptFromMessages(messages, tools) {
|
|
13501
|
+
const messageSummary = messages.map((m, i) => {
|
|
13502
|
+
const role = m?.role ?? "?";
|
|
13503
|
+
const hasToolCalls = Array.isArray(m?.tool_calls) ? m.tool_calls.length : 0;
|
|
13504
|
+
const tcNames = hasToolCalls > 0 ? m.tool_calls.map((tc) => tc?.function?.name).join(",") : "";
|
|
13505
|
+
const contentType = typeof m?.content;
|
|
13506
|
+
const contentLen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr:${m.content.length}` : "null";
|
|
13507
|
+
const toolCallId = m?.tool_call_id ?? null;
|
|
13508
|
+
return { i, role, hasToolCalls, tcNames, contentType, contentLen, toolCallId };
|
|
13509
|
+
});
|
|
13510
|
+
const assistantWithToolCalls = messages.filter((m) => m?.role === "assistant" && Array.isArray(m?.tool_calls) && m.tool_calls.length > 0);
|
|
13511
|
+
const assistantEmpty = messages.filter((m) => m?.role === "assistant" && (!m?.tool_calls || m.tool_calls.length === 0) && (!m?.content || m.content === "" || m.content === null));
|
|
13512
|
+
const toolResults = messages.filter((m) => m?.role === "tool");
|
|
13513
|
+
debugLogToFile("buildPromptFromMessages", {
|
|
13514
|
+
totalMessages: messages.length,
|
|
13515
|
+
totalTools: tools.length,
|
|
13516
|
+
messageSummary,
|
|
13517
|
+
stats: {
|
|
13518
|
+
assistantWithToolCalls: assistantWithToolCalls.length,
|
|
13519
|
+
assistantEmpty: assistantEmpty.length,
|
|
13520
|
+
toolResults: toolResults.length
|
|
13521
|
+
},
|
|
13522
|
+
assistantDetails: assistantWithToolCalls.length > 0 ? assistantWithToolCalls.map((m, i) => ({
|
|
13523
|
+
index: i,
|
|
13524
|
+
toolCallCount: Array.isArray(m?.tool_calls) ? m.tool_calls.length : 0,
|
|
13525
|
+
toolCallIds: Array.isArray(m?.tool_calls) ? m.tool_calls.map((tc) => tc?.id).join(",") : "",
|
|
13526
|
+
toolCallNames: Array.isArray(m?.tool_calls) ? m.tool_calls.map((tc) => tc?.function?.name).join(",") : "",
|
|
13527
|
+
contentType: typeof m?.content,
|
|
13528
|
+
contentPreview: typeof m?.content === "string" ? m.content.slice(0, 50) : typeof m?.content
|
|
13529
|
+
})) : [],
|
|
13530
|
+
emptyAssistantDetails: assistantEmpty.length > 0 ? assistantEmpty.map((m, i) => ({
|
|
13531
|
+
index: i,
|
|
13532
|
+
contentType: typeof m?.content,
|
|
13533
|
+
contentPreview: typeof m?.content === "string" ? m.content.slice(0, 50) : typeof m?.content
|
|
13534
|
+
})) : [],
|
|
13535
|
+
toolResultDetails: toolResults.length > 0 ? toolResults.map((m, i) => ({
|
|
13536
|
+
index: i,
|
|
13537
|
+
toolCallId: m?.tool_call_id,
|
|
13538
|
+
contentPreview: typeof m?.content === "string" ? m.content.slice(0, 100) : typeof m?.content
|
|
13539
|
+
})) : []
|
|
13540
|
+
});
|
|
13404
13541
|
const lines = [];
|
|
13405
13542
|
if (tools.length > 0) {
|
|
13406
13543
|
const toolDescs = tools.map((t) => {
|
|
@@ -13414,6 +13551,7 @@ function buildPromptFromMessages(messages, tools) {
|
|
|
13414
13551
|
}).join(`
|
|
13415
13552
|
`);
|
|
13416
13553
|
lines.push(`SYSTEM: You have access to the following tools. When you need to use one, respond with a tool_call in the standard OpenAI format.
|
|
13554
|
+
` + `Tool guidance: prefer write/edit for file changes; use bash mainly to run commands/tests.
|
|
13417
13555
|
|
|
13418
13556
|
Available tools:
|
|
13419
13557
|
${toolDescs}`);
|
|
@@ -13453,10 +13591,30 @@ ${toolDescs}`);
|
|
|
13453
13591
|
}
|
|
13454
13592
|
}
|
|
13455
13593
|
}
|
|
13456
|
-
|
|
13594
|
+
const hasToolResults = messages.some((m) => m?.role === "tool");
|
|
13595
|
+
if (hasToolResults) {
|
|
13596
|
+
lines.push("The above tool calls have been executed. Continue your response based on these results.");
|
|
13597
|
+
}
|
|
13598
|
+
const finalPrompt = lines.join(`
|
|
13457
13599
|
|
|
13458
13600
|
`);
|
|
13459
|
-
|
|
13601
|
+
debugLogToFile("buildPromptFromMessages: final prompt", {
|
|
13602
|
+
lineCount: lines.length,
|
|
13603
|
+
promptLength: finalPrompt.length,
|
|
13604
|
+
promptPreview: finalPrompt.slice(0, 500),
|
|
13605
|
+
hasToolResultFormat: finalPrompt.includes("TOOL_RESULT"),
|
|
13606
|
+
hasAssistantToolCallFormat: finalPrompt.includes("tool_call(id:"),
|
|
13607
|
+
hasCompletionSignal: finalPrompt.includes("Based on the tool results")
|
|
13608
|
+
});
|
|
13609
|
+
return finalPrompt;
|
|
13610
|
+
}
|
|
13611
|
+
var log4, DEBUG_LOG_DIR, DEBUG_LOG_FILE;
|
|
13612
|
+
var init_prompt_builder = __esm(() => {
|
|
13613
|
+
init_logger();
|
|
13614
|
+
log4 = createLogger("proxy:prompt-builder");
|
|
13615
|
+
DEBUG_LOG_DIR = join3(homedir3(), ".config", "opencode", "logs");
|
|
13616
|
+
DEBUG_LOG_FILE = join3(DEBUG_LOG_DIR, "tool-loop-debug.log");
|
|
13617
|
+
});
|
|
13460
13618
|
|
|
13461
13619
|
// src/proxy/tool-loop.ts
|
|
13462
13620
|
function extractAllowedToolNames(tools) {
|
|
@@ -13482,7 +13640,7 @@ function extractOpenAiToolCall(event, allowedToolNames) {
|
|
|
13482
13640
|
}
|
|
13483
13641
|
const resolvedName = resolveAllowedToolName(name, allowedToolNames);
|
|
13484
13642
|
if (!resolvedName) {
|
|
13485
|
-
|
|
13643
|
+
log5.debug("Tool call name not allowed; skipping interception", {
|
|
13486
13644
|
name,
|
|
13487
13645
|
normalized: normalizeAliasKey(name),
|
|
13488
13646
|
allowedToolCount: allowedToolNames.size,
|
|
@@ -13491,7 +13649,7 @@ function extractOpenAiToolCall(event, allowedToolNames) {
|
|
|
13491
13649
|
return null;
|
|
13492
13650
|
}
|
|
13493
13651
|
if (args === undefined && event.subtype === "started") {
|
|
13494
|
-
|
|
13652
|
+
log5.debug("Tool call args extraction returned undefined", {
|
|
13495
13653
|
toolName: name,
|
|
13496
13654
|
subtype: event.subtype ?? "none",
|
|
13497
13655
|
payloadKeys: Object.entries(event.tool_call || {}).map(([k, v]) => `${k}:[${isRecord(v) ? Object.keys(v).join(",") : typeof v}]`),
|
|
@@ -13647,10 +13805,10 @@ function toOpenAiArguments(args) {
|
|
|
13647
13805
|
function isRecord(value) {
|
|
13648
13806
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13649
13807
|
}
|
|
13650
|
-
var
|
|
13808
|
+
var log5, TOOL_NAME_ALIASES;
|
|
13651
13809
|
var init_tool_loop = __esm(() => {
|
|
13652
13810
|
init_logger();
|
|
13653
|
-
|
|
13811
|
+
log5 = createLogger("proxy:tool-loop");
|
|
13654
13812
|
TOOL_NAME_ALIASES = new Map([
|
|
13655
13813
|
["runcommand", "bash"],
|
|
13656
13814
|
["executecommand", "bash"],
|
|
@@ -13764,7 +13922,7 @@ class OpenCodeToolDiscovery {
|
|
|
13764
13922
|
const mcpTools = await this.tryListMcpTools();
|
|
13765
13923
|
tools = tools.concat(mcpTools);
|
|
13766
13924
|
} catch (err) {
|
|
13767
|
-
|
|
13925
|
+
log6.debug("SDK tool.list failed, will try CLI", { error: String(err) });
|
|
13768
13926
|
}
|
|
13769
13927
|
}
|
|
13770
13928
|
if (tools.length === 0 && this.executorPref !== "sdk") {
|
|
@@ -13776,10 +13934,10 @@ class OpenCodeToolDiscovery {
|
|
|
13776
13934
|
if (parsed?.data?.tools?.length) {
|
|
13777
13935
|
tools = parsed.data.tools.map((t) => this.normalize(t, "cli"));
|
|
13778
13936
|
} else {
|
|
13779
|
-
|
|
13937
|
+
log6.debug("CLI tool list failed", { status: res.status, stderr: res.stderr });
|
|
13780
13938
|
}
|
|
13781
13939
|
} catch (err) {
|
|
13782
|
-
|
|
13940
|
+
log6.debug("CLI tool list error", { error: String(err) });
|
|
13783
13941
|
}
|
|
13784
13942
|
}
|
|
13785
13943
|
const map2 = new Map;
|
|
@@ -13815,7 +13973,7 @@ class OpenCodeToolDiscovery {
|
|
|
13815
13973
|
return [];
|
|
13816
13974
|
return mcpList.data.tools.map((t) => this.normalize(t, "mcp"));
|
|
13817
13975
|
} catch (err) {
|
|
13818
|
-
|
|
13976
|
+
log6.debug("MCP tool discovery skipped", { error: String(err) });
|
|
13819
13977
|
return [];
|
|
13820
13978
|
}
|
|
13821
13979
|
}
|
|
@@ -13836,11 +13994,11 @@ class OpenCodeToolDiscovery {
|
|
|
13836
13994
|
return null;
|
|
13837
13995
|
}
|
|
13838
13996
|
}
|
|
13839
|
-
var
|
|
13997
|
+
var log6;
|
|
13840
13998
|
var init_discovery = __esm(() => {
|
|
13841
13999
|
init_logger();
|
|
13842
14000
|
init_strip_ansi();
|
|
13843
|
-
|
|
14001
|
+
log6 = createLogger("tools:discovery");
|
|
13844
14002
|
});
|
|
13845
14003
|
|
|
13846
14004
|
// src/tools/schema.ts
|
|
@@ -13897,10 +14055,10 @@ function describeTool(t) {
|
|
|
13897
14055
|
const base = t.description || "OpenCode tool";
|
|
13898
14056
|
return base.length > 400 ? base.slice(0, 400) : base;
|
|
13899
14057
|
}
|
|
13900
|
-
var
|
|
14058
|
+
var log7;
|
|
13901
14059
|
var init_schema = __esm(() => {
|
|
13902
14060
|
init_logger();
|
|
13903
|
-
|
|
14061
|
+
log7 = createLogger("tools:schema");
|
|
13904
14062
|
});
|
|
13905
14063
|
|
|
13906
14064
|
// src/tools/router.ts
|
|
@@ -13925,18 +14083,18 @@ class ToolRouter {
|
|
|
13925
14083
|
}
|
|
13926
14084
|
const tool3 = this.ctx.toolsByName.get(name);
|
|
13927
14085
|
if (!tool3) {
|
|
13928
|
-
|
|
14086
|
+
log8.warn("Unknown tool call", { name });
|
|
13929
14087
|
return this.buildResult(meta, callId, name, { status: "error", error: `Unknown tool ${name}` });
|
|
13930
14088
|
}
|
|
13931
14089
|
const args = this.extractArgs(event);
|
|
13932
|
-
|
|
14090
|
+
log8.debug("Executing tool", { name, toolId: tool3.id });
|
|
13933
14091
|
const t0 = Date.now();
|
|
13934
14092
|
const result = await this.ctx.execute(tool3.id, args);
|
|
13935
14093
|
const elapsed = Date.now() - t0;
|
|
13936
14094
|
if (result.status === "error") {
|
|
13937
|
-
|
|
14095
|
+
log8.warn("Tool execution returned error", { name, error: result.error, elapsed });
|
|
13938
14096
|
} else {
|
|
13939
|
-
|
|
14097
|
+
log8.debug("Tool execution completed", { name, toolId: tool3.id, elapsed });
|
|
13940
14098
|
}
|
|
13941
14099
|
return this.buildResult(meta, callId, name, result);
|
|
13942
14100
|
}
|
|
@@ -13985,10 +14143,10 @@ class ToolRouter {
|
|
|
13985
14143
|
};
|
|
13986
14144
|
}
|
|
13987
14145
|
}
|
|
13988
|
-
var
|
|
14146
|
+
var log8;
|
|
13989
14147
|
var init_router = __esm(() => {
|
|
13990
14148
|
init_logger();
|
|
13991
|
-
|
|
14149
|
+
log8 = createLogger("tools:router");
|
|
13992
14150
|
});
|
|
13993
14151
|
|
|
13994
14152
|
// src/tools/skills/loader.ts
|
|
@@ -14300,7 +14458,7 @@ var separatorArrayExplode = (style) => {
|
|
|
14300
14458
|
};
|
|
14301
14459
|
|
|
14302
14460
|
// node_modules/@opencode-ai/sdk/dist/gen/core/utils.gen.js
|
|
14303
|
-
var PATH_PARAM_RE, defaultPathSerializer = ({ path, url: _url2 }) => {
|
|
14461
|
+
var PATH_PARAM_RE, defaultPathSerializer = ({ path: path2, url: _url2 }) => {
|
|
14304
14462
|
let url2 = _url2;
|
|
14305
14463
|
const matches = _url2.match(PATH_PARAM_RE);
|
|
14306
14464
|
if (matches) {
|
|
@@ -14319,7 +14477,7 @@ var PATH_PARAM_RE, defaultPathSerializer = ({ path, url: _url2 }) => {
|
|
|
14319
14477
|
name = name.substring(1);
|
|
14320
14478
|
style = "matrix";
|
|
14321
14479
|
}
|
|
14322
|
-
const value =
|
|
14480
|
+
const value = path2[name];
|
|
14323
14481
|
if (value === undefined || value === null) {
|
|
14324
14482
|
continue;
|
|
14325
14483
|
}
|
|
@@ -14349,11 +14507,11 @@ var PATH_PARAM_RE, defaultPathSerializer = ({ path, url: _url2 }) => {
|
|
|
14349
14507
|
}
|
|
14350
14508
|
}
|
|
14351
14509
|
return url2;
|
|
14352
|
-
}, getUrl = ({ baseUrl, path, query, querySerializer, url: _url2 }) => {
|
|
14510
|
+
}, getUrl = ({ baseUrl, path: path2, query, querySerializer, url: _url2 }) => {
|
|
14353
14511
|
const pathUrl = _url2.startsWith("/") ? _url2 : `/${_url2}`;
|
|
14354
14512
|
let url2 = (baseUrl ?? "") + pathUrl;
|
|
14355
|
-
if (
|
|
14356
|
-
url2 = defaultPathSerializer({ path, url: url2 });
|
|
14513
|
+
if (path2) {
|
|
14514
|
+
url2 = defaultPathSerializer({ path: path2, url: url2 });
|
|
14357
14515
|
}
|
|
14358
14516
|
let search = query ? querySerializer(query) : "";
|
|
14359
14517
|
if (search.startsWith("?")) {
|
|
@@ -15474,15 +15632,15 @@ class LocalExecutor {
|
|
|
15474
15632
|
const out = await handler(args);
|
|
15475
15633
|
return { status: "success", output: out };
|
|
15476
15634
|
} catch (err) {
|
|
15477
|
-
|
|
15635
|
+
log9.warn("Local tool execution failed", { toolId, error: String(err?.message || err) });
|
|
15478
15636
|
return { status: "error", error: String(err?.message || err) };
|
|
15479
15637
|
}
|
|
15480
15638
|
}
|
|
15481
15639
|
}
|
|
15482
|
-
var
|
|
15640
|
+
var log9;
|
|
15483
15641
|
var init_local = __esm(() => {
|
|
15484
15642
|
init_logger();
|
|
15485
|
-
|
|
15643
|
+
log9 = createLogger("tools:executor:local");
|
|
15486
15644
|
});
|
|
15487
15645
|
|
|
15488
15646
|
// src/tools/executors/sdk.ts
|
|
@@ -15509,7 +15667,7 @@ class SdkExecutor {
|
|
|
15509
15667
|
const out = typeof res === "string" ? res : JSON.stringify(res);
|
|
15510
15668
|
return { status: "success", output: out };
|
|
15511
15669
|
} catch (err) {
|
|
15512
|
-
|
|
15670
|
+
log10.warn("SDK tool execution failed", { toolId, error: String(err?.message || err) });
|
|
15513
15671
|
return { status: "error", error: String(err?.message || err) };
|
|
15514
15672
|
}
|
|
15515
15673
|
}
|
|
@@ -15522,10 +15680,10 @@ class SdkExecutor {
|
|
|
15522
15680
|
]);
|
|
15523
15681
|
}
|
|
15524
15682
|
}
|
|
15525
|
-
var
|
|
15683
|
+
var log10;
|
|
15526
15684
|
var init_sdk = __esm(() => {
|
|
15527
15685
|
init_logger();
|
|
15528
|
-
|
|
15686
|
+
log10 = createLogger("tools:executor:sdk");
|
|
15529
15687
|
});
|
|
15530
15688
|
|
|
15531
15689
|
// src/tools/executors/mcp.ts
|
|
@@ -15552,7 +15710,7 @@ class McpExecutor {
|
|
|
15552
15710
|
const out = typeof res === "string" ? res : JSON.stringify(res);
|
|
15553
15711
|
return { status: "success", output: out };
|
|
15554
15712
|
} catch (err) {
|
|
15555
|
-
|
|
15713
|
+
log11.warn("MCP tool execution failed", { toolId, error: String(err?.message || err) });
|
|
15556
15714
|
return { status: "error", error: String(err?.message || err) };
|
|
15557
15715
|
}
|
|
15558
15716
|
}
|
|
@@ -15565,10 +15723,10 @@ class McpExecutor {
|
|
|
15565
15723
|
]);
|
|
15566
15724
|
}
|
|
15567
15725
|
}
|
|
15568
|
-
var
|
|
15726
|
+
var log11;
|
|
15569
15727
|
var init_mcp = __esm(() => {
|
|
15570
15728
|
init_logger();
|
|
15571
|
-
|
|
15729
|
+
log11 = createLogger("tools:executor:mcp");
|
|
15572
15730
|
});
|
|
15573
15731
|
|
|
15574
15732
|
// src/tools/core/executor.ts
|
|
@@ -15578,17 +15736,17 @@ async function executeWithChain(executors, toolId, args) {
|
|
|
15578
15736
|
try {
|
|
15579
15737
|
return await ex.execute(toolId, args);
|
|
15580
15738
|
} catch (err) {
|
|
15581
|
-
|
|
15739
|
+
log12.warn("Executor threw unexpected error", { toolId, error: String(err?.message || err) });
|
|
15582
15740
|
return { status: "error", error: String(err?.message || err) };
|
|
15583
15741
|
}
|
|
15584
15742
|
}
|
|
15585
15743
|
}
|
|
15586
15744
|
return { status: "error", error: `No executor available for ${toolId}` };
|
|
15587
15745
|
}
|
|
15588
|
-
var
|
|
15746
|
+
var log12;
|
|
15589
15747
|
var init_executor = __esm(() => {
|
|
15590
15748
|
init_logger();
|
|
15591
|
-
|
|
15749
|
+
log12 = createLogger("tools:executor:chain");
|
|
15592
15750
|
});
|
|
15593
15751
|
|
|
15594
15752
|
// src/tools/defaults.ts
|
|
@@ -15596,7 +15754,7 @@ function registerDefaultTools(registry2) {
|
|
|
15596
15754
|
registry2.register({
|
|
15597
15755
|
id: "bash",
|
|
15598
15756
|
name: "bash",
|
|
15599
|
-
description: "Execute a shell command
|
|
15757
|
+
description: "Execute a shell command. Use this to run programs/tests; prefer write/edit for creating or modifying files.",
|
|
15600
15758
|
parameters: {
|
|
15601
15759
|
type: "object",
|
|
15602
15760
|
properties: {
|
|
@@ -15660,12 +15818,12 @@ function registerDefaultTools(registry2) {
|
|
|
15660
15818
|
},
|
|
15661
15819
|
source: "local"
|
|
15662
15820
|
}, async (args) => {
|
|
15663
|
-
const
|
|
15821
|
+
const fs2 = await import("fs");
|
|
15664
15822
|
try {
|
|
15665
|
-
const
|
|
15823
|
+
const path2 = args.path;
|
|
15666
15824
|
const offset = args.offset;
|
|
15667
15825
|
const limit = args.limit;
|
|
15668
|
-
let content =
|
|
15826
|
+
let content = fs2.readFileSync(path2, "utf-8");
|
|
15669
15827
|
if (offset !== undefined || limit !== undefined) {
|
|
15670
15828
|
const lines = content.split(`
|
|
15671
15829
|
`);
|
|
@@ -15682,7 +15840,7 @@ function registerDefaultTools(registry2) {
|
|
|
15682
15840
|
registry2.register({
|
|
15683
15841
|
id: "write",
|
|
15684
15842
|
name: "write",
|
|
15685
|
-
description: "Write content to a file (creates or overwrites)",
|
|
15843
|
+
description: "Write content to a file (creates or overwrites). Prefer this over using bash redirection/heredocs for file creation.",
|
|
15686
15844
|
parameters: {
|
|
15687
15845
|
type: "object",
|
|
15688
15846
|
properties: {
|
|
@@ -15699,16 +15857,16 @@ function registerDefaultTools(registry2) {
|
|
|
15699
15857
|
},
|
|
15700
15858
|
source: "local"
|
|
15701
15859
|
}, async (args) => {
|
|
15702
|
-
const
|
|
15703
|
-
const
|
|
15860
|
+
const fs2 = await import("fs");
|
|
15861
|
+
const path2 = await import("path");
|
|
15704
15862
|
try {
|
|
15705
15863
|
const filePath = args.path;
|
|
15706
15864
|
const content = args.content;
|
|
15707
|
-
const dir =
|
|
15708
|
-
if (!
|
|
15709
|
-
|
|
15865
|
+
const dir = path2.dirname(filePath);
|
|
15866
|
+
if (!fs2.existsSync(dir)) {
|
|
15867
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
15710
15868
|
}
|
|
15711
|
-
|
|
15869
|
+
fs2.writeFileSync(filePath, content, "utf-8");
|
|
15712
15870
|
return `File written successfully: ${filePath}`;
|
|
15713
15871
|
} catch (error45) {
|
|
15714
15872
|
throw error45;
|
|
@@ -15717,7 +15875,7 @@ function registerDefaultTools(registry2) {
|
|
|
15717
15875
|
registry2.register({
|
|
15718
15876
|
id: "edit",
|
|
15719
15877
|
name: "edit",
|
|
15720
|
-
description: "Edit a file by replacing old text with new text",
|
|
15878
|
+
description: "Edit a file by replacing old text with new text. Use for targeted replacements; use write to overwrite an entire file.",
|
|
15721
15879
|
parameters: {
|
|
15722
15880
|
type: "object",
|
|
15723
15881
|
properties: {
|
|
@@ -15738,8 +15896,8 @@ function registerDefaultTools(registry2) {
|
|
|
15738
15896
|
},
|
|
15739
15897
|
source: "local"
|
|
15740
15898
|
}, async (args) => {
|
|
15741
|
-
const
|
|
15742
|
-
const
|
|
15899
|
+
const fs2 = await import("fs");
|
|
15900
|
+
const path2 = await import("path");
|
|
15743
15901
|
try {
|
|
15744
15902
|
const resolvedArgs = resolveEditArguments(args);
|
|
15745
15903
|
const filePath = resolvedArgs.path;
|
|
@@ -15756,27 +15914,27 @@ function registerDefaultTools(registry2) {
|
|
|
15756
15914
|
}
|
|
15757
15915
|
let content = "";
|
|
15758
15916
|
try {
|
|
15759
|
-
content =
|
|
15917
|
+
content = fs2.readFileSync(filePath, "utf-8");
|
|
15760
15918
|
} catch (error45) {
|
|
15761
15919
|
if (error45?.code === "ENOENT") {
|
|
15762
|
-
const dir =
|
|
15763
|
-
if (!
|
|
15764
|
-
|
|
15920
|
+
const dir = path2.dirname(filePath);
|
|
15921
|
+
if (!fs2.existsSync(dir)) {
|
|
15922
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
15765
15923
|
}
|
|
15766
|
-
|
|
15924
|
+
fs2.writeFileSync(filePath, newString, "utf-8");
|
|
15767
15925
|
return `File did not exist. Created and wrote content: ${filePath}`;
|
|
15768
15926
|
}
|
|
15769
15927
|
throw error45;
|
|
15770
15928
|
}
|
|
15771
15929
|
if (!oldString) {
|
|
15772
|
-
|
|
15930
|
+
fs2.writeFileSync(filePath, newString, "utf-8");
|
|
15773
15931
|
return `File edited successfully: ${filePath}`;
|
|
15774
15932
|
}
|
|
15775
15933
|
if (!content.includes(oldString)) {
|
|
15776
15934
|
return `Error: Could not find the text to replace in ${filePath}`;
|
|
15777
15935
|
}
|
|
15778
15936
|
content = content.replaceAll(oldString, newString);
|
|
15779
|
-
|
|
15937
|
+
fs2.writeFileSync(filePath, content, "utf-8");
|
|
15780
15938
|
return `File edited successfully: ${filePath}`;
|
|
15781
15939
|
} catch (error45) {
|
|
15782
15940
|
throw error45;
|
|
@@ -15810,13 +15968,13 @@ function registerDefaultTools(registry2) {
|
|
|
15810
15968
|
const { promisify } = await import("util");
|
|
15811
15969
|
const execFileAsync = promisify(execFile);
|
|
15812
15970
|
const pattern = args.pattern;
|
|
15813
|
-
const
|
|
15971
|
+
const path2 = args.path;
|
|
15814
15972
|
const include = args.include;
|
|
15815
15973
|
const grepArgs = ["-r", "-n"];
|
|
15816
15974
|
if (include) {
|
|
15817
15975
|
grepArgs.push(`--include=${include}`);
|
|
15818
15976
|
}
|
|
15819
|
-
grepArgs.push(pattern,
|
|
15977
|
+
grepArgs.push(pattern, path2);
|
|
15820
15978
|
const runGrep = async (extraArgs = []) => {
|
|
15821
15979
|
return execFileAsync("grep", [...extraArgs, ...grepArgs], { timeout: 30000 });
|
|
15822
15980
|
};
|
|
@@ -15859,11 +16017,11 @@ function registerDefaultTools(registry2) {
|
|
|
15859
16017
|
},
|
|
15860
16018
|
source: "local"
|
|
15861
16019
|
}, async (args) => {
|
|
15862
|
-
const
|
|
15863
|
-
const
|
|
16020
|
+
const fs2 = await import("fs");
|
|
16021
|
+
const path2 = await import("path");
|
|
15864
16022
|
try {
|
|
15865
16023
|
const dirPath = args.path;
|
|
15866
|
-
const entries =
|
|
16024
|
+
const entries = fs2.readdirSync(dirPath, { withFileTypes: true });
|
|
15867
16025
|
const result = entries.map((entry) => {
|
|
15868
16026
|
const type = entry.isDirectory() ? "d" : entry.isSymbolicLink() ? "l" : entry.isFile() ? "f" : "?";
|
|
15869
16027
|
return `[${type}] ${entry.name}`;
|
|
@@ -15901,8 +16059,8 @@ function registerDefaultTools(registry2) {
|
|
|
15901
16059
|
if (!pattern) {
|
|
15902
16060
|
throw new Error("glob: missing required argument 'pattern'");
|
|
15903
16061
|
}
|
|
15904
|
-
const
|
|
15905
|
-
const cwd =
|
|
16062
|
+
const path2 = resolvePathArg(args, "glob");
|
|
16063
|
+
const cwd = path2 || ".";
|
|
15906
16064
|
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
15907
16065
|
const isPathPattern = normalizedPattern.includes("/");
|
|
15908
16066
|
const findArgs = [cwd, "-type", "f"];
|
|
@@ -16030,7 +16188,7 @@ function registerDefaultTools(registry2) {
|
|
|
16030
16188
|
});
|
|
16031
16189
|
}
|
|
16032
16190
|
function resolveEditArguments(args) {
|
|
16033
|
-
const
|
|
16191
|
+
const path2 = typeof args.path === "string" ? args.path : "";
|
|
16034
16192
|
let oldString = typeof args.old_string === "string" ? args.old_string : undefined;
|
|
16035
16193
|
let newString = typeof args.new_string === "string" ? args.new_string : undefined;
|
|
16036
16194
|
if (newString === undefined) {
|
|
@@ -16043,7 +16201,7 @@ function resolveEditArguments(args) {
|
|
|
16043
16201
|
oldString = "";
|
|
16044
16202
|
}
|
|
16045
16203
|
return {
|
|
16046
|
-
path,
|
|
16204
|
+
path: path2,
|
|
16047
16205
|
old_string: oldString,
|
|
16048
16206
|
new_string: newString
|
|
16049
16207
|
};
|
|
@@ -16386,6 +16544,23 @@ function normalizeToolSpecificArgs(toolName, args) {
|
|
|
16386
16544
|
todos
|
|
16387
16545
|
};
|
|
16388
16546
|
}
|
|
16547
|
+
if (normalizedToolName === "write") {
|
|
16548
|
+
const normalized = { ...args };
|
|
16549
|
+
if (normalized.content === undefined && normalized.new_string !== undefined) {
|
|
16550
|
+
const coerced = coerceToString2(normalized.new_string);
|
|
16551
|
+
if (coerced !== null) {
|
|
16552
|
+
normalized.content = coerced;
|
|
16553
|
+
}
|
|
16554
|
+
delete normalized.new_string;
|
|
16555
|
+
}
|
|
16556
|
+
if (normalized.content !== undefined && typeof normalized.content !== "string") {
|
|
16557
|
+
const coerced = coerceToString2(normalized.content);
|
|
16558
|
+
if (coerced !== null) {
|
|
16559
|
+
normalized.content = coerced;
|
|
16560
|
+
}
|
|
16561
|
+
}
|
|
16562
|
+
return normalized;
|
|
16563
|
+
}
|
|
16389
16564
|
if (normalizedToolName !== "edit" || !EDIT_COMPAT_REPAIR_ENABLED) {
|
|
16390
16565
|
return args;
|
|
16391
16566
|
}
|
|
@@ -16628,6 +16803,9 @@ var init_tool_schema_compat = __esm(() => {
|
|
|
16628
16803
|
["terminalcommand", "command"],
|
|
16629
16804
|
["contents", "content"],
|
|
16630
16805
|
["text", "content"],
|
|
16806
|
+
["body", "content"],
|
|
16807
|
+
["data", "content"],
|
|
16808
|
+
["payload", "content"],
|
|
16631
16809
|
["streamcontent", "content"],
|
|
16632
16810
|
["recursive", "force"],
|
|
16633
16811
|
["oldstring", "old_string"],
|
|
@@ -16641,7 +16819,7 @@ async function handleToolLoopEventLegacy(options) {
|
|
|
16641
16819
|
event,
|
|
16642
16820
|
toolLoopMode,
|
|
16643
16821
|
allowedToolNames,
|
|
16644
|
-
toolSchemaMap
|
|
16822
|
+
toolSchemaMap,
|
|
16645
16823
|
toolLoopGuard,
|
|
16646
16824
|
toolMapper,
|
|
16647
16825
|
toolSessionId,
|
|
@@ -16656,11 +16834,44 @@ async function handleToolLoopEventLegacy(options) {
|
|
|
16656
16834
|
} = options;
|
|
16657
16835
|
const interceptedToolCall = toolLoopMode === "opencode" ? extractOpenAiToolCall(event, allowedToolNames) : null;
|
|
16658
16836
|
if (interceptedToolCall) {
|
|
16659
|
-
const
|
|
16837
|
+
const compat2 = applyToolSchemaCompat(interceptedToolCall, toolSchemaMap);
|
|
16838
|
+
let normalizedToolCall = compat2.toolCall;
|
|
16839
|
+
log13.debug("Applied tool schema compatibility (legacy)", {
|
|
16840
|
+
tool: normalizedToolCall.function.name,
|
|
16841
|
+
originalArgKeys: compat2.originalArgKeys,
|
|
16842
|
+
normalizedArgKeys: compat2.normalizedArgKeys,
|
|
16843
|
+
collisionKeys: compat2.collisionKeys,
|
|
16844
|
+
validationOk: compat2.validation.ok
|
|
16845
|
+
});
|
|
16846
|
+
if (compat2.validation.hasSchema && !compat2.validation.ok) {
|
|
16847
|
+
const validationTermination = evaluateSchemaValidationLoopGuard(toolLoopGuard, normalizedToolCall, compat2.validation);
|
|
16848
|
+
if (validationTermination) {
|
|
16849
|
+
return { intercepted: false, skipConverter: true, terminate: validationTermination };
|
|
16850
|
+
}
|
|
16851
|
+
const reroutedWrite = tryRerouteEditToWrite(normalizedToolCall, compat2.normalizedArgs, allowedToolNames, toolSchemaMap);
|
|
16852
|
+
if (reroutedWrite) {
|
|
16853
|
+
log13.debug("Rerouting malformed edit call to write (legacy)", {
|
|
16854
|
+
path: reroutedWrite.path,
|
|
16855
|
+
missing: compat2.validation.missing,
|
|
16856
|
+
typeErrors: compat2.validation.typeErrors
|
|
16857
|
+
});
|
|
16858
|
+
normalizedToolCall = reroutedWrite.toolCall;
|
|
16859
|
+
} else if (shouldEmitNonFatalSchemaValidationHint(normalizedToolCall, compat2.validation)) {
|
|
16860
|
+
const hintChunk = createNonFatalSchemaValidationHintChunk(responseMeta, normalizedToolCall, compat2.validation);
|
|
16861
|
+
log13.debug("Emitting non-fatal schema validation hint in legacy and skipping malformed tool execution", {
|
|
16862
|
+
tool: normalizedToolCall.function.name,
|
|
16863
|
+
missing: compat2.validation.missing,
|
|
16864
|
+
typeErrors: compat2.validation.typeErrors
|
|
16865
|
+
});
|
|
16866
|
+
await onToolResult(hintChunk);
|
|
16867
|
+
return { intercepted: false, skipConverter: true };
|
|
16868
|
+
}
|
|
16869
|
+
}
|
|
16870
|
+
const termination = evaluateToolLoopGuard(toolLoopGuard, normalizedToolCall);
|
|
16660
16871
|
if (termination) {
|
|
16661
16872
|
return { intercepted: false, skipConverter: true, terminate: termination };
|
|
16662
16873
|
}
|
|
16663
|
-
await onInterceptedToolCall(
|
|
16874
|
+
await onInterceptedToolCall(normalizedToolCall);
|
|
16664
16875
|
return { intercepted: true, skipConverter: true };
|
|
16665
16876
|
}
|
|
16666
16877
|
const updates = await toolMapper.mapCursorEventToAcp(event, event.session_id ?? toolSessionId);
|
|
@@ -16713,7 +16924,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
16713
16924
|
rawArgs: safeArgTypeSummary(event),
|
|
16714
16925
|
normalizedArgs: compat2.normalizedArgs
|
|
16715
16926
|
} : undefined;
|
|
16716
|
-
|
|
16927
|
+
log13.debug("Applied tool schema compatibility", {
|
|
16717
16928
|
tool: interceptedToolCall.function.name,
|
|
16718
16929
|
originalArgKeys: compat2.originalArgKeys,
|
|
16719
16930
|
normalizedArgKeys: compat2.normalizedArgKeys,
|
|
@@ -16722,7 +16933,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
16722
16933
|
...editDiag ? { editDiag } : {}
|
|
16723
16934
|
});
|
|
16724
16935
|
if (compat2.validation.hasSchema && !compat2.validation.ok) {
|
|
16725
|
-
|
|
16936
|
+
log13.debug("Tool schema compatibility validation failed", {
|
|
16726
16937
|
tool: interceptedToolCall.function.name,
|
|
16727
16938
|
missing: compat2.validation.missing,
|
|
16728
16939
|
unexpected: compat2.validation.unexpected,
|
|
@@ -16739,7 +16950,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
16739
16950
|
}
|
|
16740
16951
|
const reroutedWrite = tryRerouteEditToWrite(interceptedToolCall, compat2.normalizedArgs, allowedToolNames, toolSchemaMap);
|
|
16741
16952
|
if (reroutedWrite) {
|
|
16742
|
-
|
|
16953
|
+
log13.debug("Rerouting malformed edit call to write", {
|
|
16743
16954
|
path: reroutedWrite.path,
|
|
16744
16955
|
missing: compat2.validation.missing,
|
|
16745
16956
|
typeErrors: compat2.validation.typeErrors
|
|
@@ -16759,7 +16970,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
16759
16970
|
}
|
|
16760
16971
|
if (schemaValidationFailureMode === "pass_through" && shouldEmitNonFatalSchemaValidationHint(interceptedToolCall, compat2.validation)) {
|
|
16761
16972
|
const hintChunk = createNonFatalSchemaValidationHintChunk(responseMeta, interceptedToolCall, compat2.validation);
|
|
16762
|
-
|
|
16973
|
+
log13.debug("Emitting non-fatal schema validation hint and skipping malformed tool execution", {
|
|
16763
16974
|
tool: interceptedToolCall.function.name,
|
|
16764
16975
|
missing: compat2.validation.missing,
|
|
16765
16976
|
typeErrors: compat2.validation.typeErrors
|
|
@@ -16777,7 +16988,7 @@ async function handleToolLoopEventV1(options) {
|
|
|
16777
16988
|
terminate: createSchemaValidationTermination(interceptedToolCall, compat2.validation)
|
|
16778
16989
|
};
|
|
16779
16990
|
}
|
|
16780
|
-
|
|
16991
|
+
log13.debug("Forwarding schema-invalid tool call to OpenCode loop", {
|
|
16781
16992
|
tool: interceptedToolCall.function.name,
|
|
16782
16993
|
repairHint: compat2.validation.repairHint
|
|
16783
16994
|
});
|
|
@@ -16856,16 +17067,28 @@ function evaluateToolLoopGuard(toolLoopGuard, toolCall) {
|
|
|
16856
17067
|
if (!decision.triggered) {
|
|
16857
17068
|
return null;
|
|
16858
17069
|
}
|
|
16859
|
-
|
|
17070
|
+
log13.debug("Tool loop guard triggered", {
|
|
16860
17071
|
tool: toolCall.function.name,
|
|
16861
17072
|
fingerprint: decision.fingerprint,
|
|
16862
17073
|
repeatCount: decision.repeatCount,
|
|
16863
17074
|
maxRepeat: decision.maxRepeat,
|
|
16864
17075
|
errorClass: decision.errorClass
|
|
16865
17076
|
});
|
|
17077
|
+
if (decision.errorClass === "success") {
|
|
17078
|
+
return {
|
|
17079
|
+
reason: "loop_guard",
|
|
17080
|
+
message: "",
|
|
17081
|
+
tool: toolCall.function.name,
|
|
17082
|
+
fingerprint: decision.fingerprint,
|
|
17083
|
+
repeatCount: decision.repeatCount,
|
|
17084
|
+
maxRepeat: decision.maxRepeat,
|
|
17085
|
+
errorClass: decision.errorClass,
|
|
17086
|
+
silent: true
|
|
17087
|
+
};
|
|
17088
|
+
}
|
|
16866
17089
|
return {
|
|
16867
17090
|
reason: "loop_guard",
|
|
16868
|
-
message:
|
|
17091
|
+
message: `Tool loop guard stopped repeated failing calls to "${toolCall.function.name}" ` + `after ${decision.repeatCount} attempts (limit ${decision.maxRepeat}). ` + "Adjust tool arguments and retry.",
|
|
16869
17092
|
tool: toolCall.function.name,
|
|
16870
17093
|
fingerprint: decision.fingerprint,
|
|
16871
17094
|
repeatCount: decision.repeatCount,
|
|
@@ -16903,7 +17126,7 @@ function evaluateSchemaValidationLoopGuard(toolLoopGuard, toolCall, validation)
|
|
|
16903
17126
|
if (!decision.tracked || !decision.triggered) {
|
|
16904
17127
|
return null;
|
|
16905
17128
|
}
|
|
16906
|
-
|
|
17129
|
+
log13.warn("Tool loop guard triggered on schema validation", {
|
|
16907
17130
|
tool: toolCall.function.name,
|
|
16908
17131
|
fingerprint: decision.fingerprint,
|
|
16909
17132
|
repeatCount: decision.repeatCount,
|
|
@@ -17036,8 +17259,8 @@ function tryRerouteEditToWrite(toolCall, normalizedArgs, allowedToolNames, toolS
|
|
|
17036
17259
|
if (!allowedToolNames.has("write") || !toolSchemaMap.has("write")) {
|
|
17037
17260
|
return null;
|
|
17038
17261
|
}
|
|
17039
|
-
const
|
|
17040
|
-
if (!
|
|
17262
|
+
const path2 = typeof normalizedArgs.path === "string" && normalizedArgs.path.length > 0 ? normalizedArgs.path : null;
|
|
17263
|
+
if (!path2) {
|
|
17041
17264
|
return null;
|
|
17042
17265
|
}
|
|
17043
17266
|
const content = typeof normalizedArgs.new_string === "string" ? normalizedArgs.new_string : typeof normalizedArgs.content === "string" ? normalizedArgs.content : null;
|
|
@@ -17049,12 +17272,12 @@ function tryRerouteEditToWrite(toolCall, normalizedArgs, allowedToolNames, toolS
|
|
|
17049
17272
|
return null;
|
|
17050
17273
|
}
|
|
17051
17274
|
return {
|
|
17052
|
-
path,
|
|
17275
|
+
path: path2,
|
|
17053
17276
|
toolCall: {
|
|
17054
17277
|
...toolCall,
|
|
17055
17278
|
function: {
|
|
17056
17279
|
name: "write",
|
|
17057
|
-
arguments: JSON.stringify({ path, content })
|
|
17280
|
+
arguments: JSON.stringify({ path: path2, content })
|
|
17058
17281
|
}
|
|
17059
17282
|
}
|
|
17060
17283
|
};
|
|
@@ -17062,12 +17285,12 @@ function tryRerouteEditToWrite(toolCall, normalizedArgs, allowedToolNames, toolS
|
|
|
17062
17285
|
function isRecord3(value) {
|
|
17063
17286
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17064
17287
|
}
|
|
17065
|
-
var
|
|
17288
|
+
var log13, ToolBoundaryExtractionError;
|
|
17066
17289
|
var init_runtime_interception = __esm(() => {
|
|
17067
17290
|
init_tool_loop();
|
|
17068
17291
|
init_logger();
|
|
17069
17292
|
init_tool_schema_compat();
|
|
17070
|
-
|
|
17293
|
+
log13 = createLogger("provider:runtime-interception");
|
|
17071
17294
|
ToolBoundaryExtractionError = class ToolBoundaryExtractionError extends Error {
|
|
17072
17295
|
cause;
|
|
17073
17296
|
constructor(message, cause) {
|
|
@@ -17081,11 +17304,11 @@ var init_runtime_interception = __esm(() => {
|
|
|
17081
17304
|
// src/provider/tool-loop-guard.ts
|
|
17082
17305
|
function parseToolLoopMaxRepeat(value) {
|
|
17083
17306
|
if (value === undefined) {
|
|
17084
|
-
return { value:
|
|
17307
|
+
return { value: 2, valid: true };
|
|
17085
17308
|
}
|
|
17086
17309
|
const parsed = Number(value);
|
|
17087
17310
|
if (!Number.isFinite(parsed) || parsed < 1) {
|
|
17088
|
-
return { value:
|
|
17311
|
+
return { value: 2, valid: false };
|
|
17089
17312
|
}
|
|
17090
17313
|
return { value: Math.floor(parsed), valid: true };
|
|
17091
17314
|
}
|
|
@@ -17314,8 +17537,8 @@ function deriveSuccessCoarseFingerprint(toolName, rawArguments) {
|
|
|
17314
17537
|
if (!isRecord4(parsed)) {
|
|
17315
17538
|
return null;
|
|
17316
17539
|
}
|
|
17317
|
-
const
|
|
17318
|
-
if (!
|
|
17540
|
+
const path2 = typeof parsed.path === "string" ? parsed.path : "";
|
|
17541
|
+
if (!path2) {
|
|
17319
17542
|
return null;
|
|
17320
17543
|
}
|
|
17321
17544
|
if (lowered === "edit") {
|
|
@@ -17324,7 +17547,7 @@ function deriveSuccessCoarseFingerprint(toolName, rawArguments) {
|
|
|
17324
17547
|
return null;
|
|
17325
17548
|
}
|
|
17326
17549
|
}
|
|
17327
|
-
return `${toolName}|path:${hashString(
|
|
17550
|
+
return `${toolName}|path:${hashString(path2)}|success`;
|
|
17328
17551
|
} catch {
|
|
17329
17552
|
return null;
|
|
17330
17553
|
}
|
|
@@ -17512,6 +17735,7 @@ var UNKNOWN_AS_SUCCESS_TOOLS;
|
|
|
17512
17735
|
var init_tool_loop_guard = __esm(() => {
|
|
17513
17736
|
UNKNOWN_AS_SUCCESS_TOOLS = new Set([
|
|
17514
17737
|
"bash",
|
|
17738
|
+
"shell",
|
|
17515
17739
|
"read",
|
|
17516
17740
|
"write",
|
|
17517
17741
|
"edit",
|
|
@@ -17519,38 +17743,64 @@ var init_tool_loop_guard = __esm(() => {
|
|
|
17519
17743
|
"ls",
|
|
17520
17744
|
"glob",
|
|
17521
17745
|
"stat",
|
|
17522
|
-
"webfetch"
|
|
17746
|
+
"webfetch",
|
|
17747
|
+
"mkdir",
|
|
17748
|
+
"rm"
|
|
17523
17749
|
]);
|
|
17524
17750
|
});
|
|
17525
17751
|
|
|
17526
17752
|
// src/plugin.ts
|
|
17527
17753
|
var exports_plugin = {};
|
|
17528
17754
|
__export(exports_plugin, {
|
|
17755
|
+
shouldProcessModel: () => shouldProcessModel,
|
|
17529
17756
|
resolveChatParamTools: () => resolveChatParamTools,
|
|
17757
|
+
normalizeWorkspaceForCompare: () => normalizeWorkspaceForCompare,
|
|
17758
|
+
isReusableProxyHealthPayload: () => isReusableProxyHealthPayload,
|
|
17530
17759
|
ensurePluginDirectory: () => ensurePluginDirectory,
|
|
17531
17760
|
default: () => plugin_default,
|
|
17532
17761
|
CursorPlugin: () => CursorPlugin
|
|
17533
17762
|
});
|
|
17534
|
-
import { realpathSync } from "fs";
|
|
17763
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync4, realpathSync } from "fs";
|
|
17535
17764
|
import { mkdir } from "fs/promises";
|
|
17536
|
-
import { homedir as
|
|
17537
|
-
import { isAbsolute, join as
|
|
17765
|
+
import { homedir as homedir4 } from "os";
|
|
17766
|
+
import { isAbsolute, join as join4, relative, resolve } from "path";
|
|
17767
|
+
function ensureDebugLogDir() {
|
|
17768
|
+
try {
|
|
17769
|
+
if (!existsSync4(DEBUG_LOG_DIR2)) {
|
|
17770
|
+
mkdir(DEBUG_LOG_DIR2, { recursive: true }).catch(() => {});
|
|
17771
|
+
}
|
|
17772
|
+
} catch {}
|
|
17773
|
+
}
|
|
17774
|
+
function debugLogToFile2(message, data) {
|
|
17775
|
+
try {
|
|
17776
|
+
ensureDebugLogDir();
|
|
17777
|
+
const timestamp = new Date().toISOString();
|
|
17778
|
+
const logLine = `[${timestamp}] ${message}: ${JSON.stringify(data, null, 2)}
|
|
17779
|
+
`;
|
|
17780
|
+
appendFileSync3(DEBUG_LOG_FILE2, logLine);
|
|
17781
|
+
} catch {}
|
|
17782
|
+
}
|
|
17538
17783
|
async function ensurePluginDirectory() {
|
|
17539
|
-
const configHome = process.env.XDG_CONFIG_HOME ? resolve(process.env.XDG_CONFIG_HOME) :
|
|
17540
|
-
const pluginDir =
|
|
17784
|
+
const configHome = process.env.XDG_CONFIG_HOME ? resolve(process.env.XDG_CONFIG_HOME) : join4(homedir4(), ".config");
|
|
17785
|
+
const pluginDir = join4(configHome, "opencode", "plugin");
|
|
17541
17786
|
try {
|
|
17542
17787
|
await mkdir(pluginDir, { recursive: true });
|
|
17543
|
-
|
|
17788
|
+
log14.debug("Plugin directory ensured", { path: pluginDir });
|
|
17544
17789
|
} catch (error45) {
|
|
17545
|
-
|
|
17790
|
+
log14.warn("Failed to create plugin directory", { error: String(error45) });
|
|
17546
17791
|
}
|
|
17547
17792
|
}
|
|
17793
|
+
function shouldProcessModel(model) {
|
|
17794
|
+
if (!model)
|
|
17795
|
+
return false;
|
|
17796
|
+
return model.startsWith(CURSOR_PROVIDER_PREFIX);
|
|
17797
|
+
}
|
|
17548
17798
|
function getGlobalKey() {
|
|
17549
17799
|
return "__opencode_cursor_proxy_server__";
|
|
17550
17800
|
}
|
|
17551
17801
|
function getOpenCodeConfigPrefix() {
|
|
17552
|
-
const configHome = process.env.XDG_CONFIG_HOME ? resolve(process.env.XDG_CONFIG_HOME) :
|
|
17553
|
-
return
|
|
17802
|
+
const configHome = process.env.XDG_CONFIG_HOME ? resolve(process.env.XDG_CONFIG_HOME) : join4(homedir4(), ".config");
|
|
17803
|
+
return join4(configHome, "opencode");
|
|
17554
17804
|
}
|
|
17555
17805
|
function canonicalizePathForCompare(pathValue) {
|
|
17556
17806
|
const resolvedPath = resolve(pathValue);
|
|
@@ -17607,6 +17857,18 @@ function resolveWorkspaceDirectory(worktree, directory) {
|
|
|
17607
17857
|
}
|
|
17608
17858
|
return dirCandidate || cwd || configPrefix;
|
|
17609
17859
|
}
|
|
17860
|
+
function normalizeWorkspaceForCompare(pathValue) {
|
|
17861
|
+
return resolve(pathValue);
|
|
17862
|
+
}
|
|
17863
|
+
function isReusableProxyHealthPayload(payload, workspaceDirectory) {
|
|
17864
|
+
if (!payload || payload.ok !== true) {
|
|
17865
|
+
return false;
|
|
17866
|
+
}
|
|
17867
|
+
if (typeof payload.workspaceDirectory !== "string" || payload.workspaceDirectory.length === 0) {
|
|
17868
|
+
return false;
|
|
17869
|
+
}
|
|
17870
|
+
return normalizeWorkspaceForCompare(payload.workspaceDirectory) === normalizeWorkspaceForCompare(workspaceDirectory);
|
|
17871
|
+
}
|
|
17610
17872
|
function parseToolLoopMode(value) {
|
|
17611
17873
|
const normalized = (value ?? "opencode").trim().toLowerCase();
|
|
17612
17874
|
if (normalized === "opencode" || normalized === "proxy-exec" || normalized === "off") {
|
|
@@ -17713,9 +17975,9 @@ function createBoundaryRuntimeContext(scope) {
|
|
|
17713
17975
|
error: toErrorMessage(error45)
|
|
17714
17976
|
};
|
|
17715
17977
|
if (!fallbackActive) {
|
|
17716
|
-
|
|
17978
|
+
log14.warn("Provider boundary v1 failed; switching to legacy for this request", details);
|
|
17717
17979
|
} else {
|
|
17718
|
-
|
|
17980
|
+
log14.debug("Provider boundary fallback already active", details);
|
|
17719
17981
|
}
|
|
17720
17982
|
fallbackActive = true;
|
|
17721
17983
|
return true;
|
|
@@ -17792,7 +18054,7 @@ async function findFirstAllowedToolCallInOutput(output, options) {
|
|
|
17792
18054
|
if (result.terminate) {
|
|
17793
18055
|
return {
|
|
17794
18056
|
toolCall: null,
|
|
17795
|
-
terminationMessage: result.terminate.message
|
|
18057
|
+
terminationMessage: result.terminate.silent ? null : result.terminate.message
|
|
17796
18058
|
};
|
|
17797
18059
|
}
|
|
17798
18060
|
if (result.intercepted && interceptedToolCall) {
|
|
@@ -17807,16 +18069,20 @@ async function findFirstAllowedToolCallInOutput(output, options) {
|
|
|
17807
18069
|
async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
17808
18070
|
const key = getGlobalKey();
|
|
17809
18071
|
const g = globalThis;
|
|
17810
|
-
const
|
|
18072
|
+
const normalizedWorkspace = normalizeWorkspaceForCompare(workspaceDirectory);
|
|
18073
|
+
const state = g[key] ?? { baseURL: "", baseURLByWorkspace: {} };
|
|
18074
|
+
state.baseURLByWorkspace = state.baseURLByWorkspace ?? {};
|
|
18075
|
+
g[key] = state;
|
|
18076
|
+
const existingBaseURL = state.baseURLByWorkspace[normalizedWorkspace] ?? state.baseURL;
|
|
17811
18077
|
if (typeof existingBaseURL === "string" && existingBaseURL.length > 0) {
|
|
17812
18078
|
return existingBaseURL;
|
|
17813
18079
|
}
|
|
17814
|
-
|
|
18080
|
+
state.baseURL = "";
|
|
17815
18081
|
const handler = async (req) => {
|
|
17816
18082
|
try {
|
|
17817
18083
|
const url2 = new URL(req.url);
|
|
17818
18084
|
if (url2.pathname === "/health") {
|
|
17819
|
-
return new Response(JSON.stringify({ ok: true }), {
|
|
18085
|
+
return new Response(JSON.stringify({ ok: true, workspaceDirectory }), {
|
|
17820
18086
|
status: 200,
|
|
17821
18087
|
headers: { "Content-Type": "application/json" }
|
|
17822
18088
|
});
|
|
@@ -17849,7 +18115,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
17849
18115
|
headers: { "Content-Type": "application/json" }
|
|
17850
18116
|
});
|
|
17851
18117
|
} catch (err) {
|
|
17852
|
-
|
|
18118
|
+
log14.error("Failed to list models", { error: String(err) });
|
|
17853
18119
|
return new Response(JSON.stringify({ error: "Failed to fetch models from cursor-agent" }), {
|
|
17854
18120
|
status: 500,
|
|
17855
18121
|
headers: { "Content-Type": "application/json" }
|
|
@@ -17862,11 +18128,21 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
17862
18128
|
headers: { "Content-Type": "application/json" }
|
|
17863
18129
|
});
|
|
17864
18130
|
}
|
|
17865
|
-
|
|
18131
|
+
log14.debug("Proxy request (bun)", { method: req.method, path: url2.pathname });
|
|
17866
18132
|
const body = await req.json().catch(() => ({}));
|
|
17867
18133
|
const messages = Array.isArray(body?.messages) ? body.messages : [];
|
|
17868
18134
|
const stream = body?.stream === true;
|
|
17869
18135
|
const tools = Array.isArray(body?.tools) ? body.tools : [];
|
|
18136
|
+
debugLogToFile2("raw_request_body", {
|
|
18137
|
+
model: body?.model,
|
|
18138
|
+
stream,
|
|
18139
|
+
toolCount: tools.length,
|
|
18140
|
+
toolNames: tools.map((t) => t?.function?.name ?? t?.name ?? "unknown"),
|
|
18141
|
+
messageCount: messages.length,
|
|
18142
|
+
messageRoles: messages.map((m) => m?.role),
|
|
18143
|
+
hasMessagesWithToolCalls: messages.some((m) => Array.isArray(m?.tool_calls) && m.tool_calls.length > 0),
|
|
18144
|
+
hasToolResultMessages: messages.some((m) => m?.role === "tool")
|
|
18145
|
+
});
|
|
17870
18146
|
const allowedToolNames = extractAllowedToolNames(tools);
|
|
17871
18147
|
const toolSchemaMap = buildToolSchemaMap(tools);
|
|
17872
18148
|
const toolLoopGuard = createToolLoopGuard(messages, TOOL_LOOP_MAX_REPEAT);
|
|
@@ -17879,7 +18155,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
17879
18155
|
const clen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr${m.content.length}` : typeof m?.content;
|
|
17880
18156
|
return `${i}:${role}${hasTc ? `(tc:${hasTc})` : ""}(clen:${clen})`;
|
|
17881
18157
|
});
|
|
17882
|
-
|
|
18158
|
+
log14.debug("Proxy chat request (bun)", {
|
|
17883
18159
|
stream,
|
|
17884
18160
|
model,
|
|
17885
18161
|
messages: messages.length,
|
|
@@ -17924,8 +18200,8 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
17924
18200
|
]);
|
|
17925
18201
|
const stdout = (stdoutText || "").trim();
|
|
17926
18202
|
const stderr = (stderrText || "").trim();
|
|
17927
|
-
const exitCode = child.
|
|
17928
|
-
|
|
18203
|
+
const exitCode = await child.exited;
|
|
18204
|
+
log14.debug("cursor-agent completed (bun non-stream)", {
|
|
17929
18205
|
exitCode,
|
|
17930
18206
|
stdoutChars: stdout.length,
|
|
17931
18207
|
stderrChars: stderr.length
|
|
@@ -17951,7 +18227,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
17951
18227
|
});
|
|
17952
18228
|
}
|
|
17953
18229
|
if (intercepted.toolCall) {
|
|
17954
|
-
|
|
18230
|
+
log14.debug("Intercepted OpenCode tool call (non-stream)", {
|
|
17955
18231
|
name: intercepted.toolCall.function.name,
|
|
17956
18232
|
callId: intercepted.toolCall.id
|
|
17957
18233
|
});
|
|
@@ -17965,7 +18241,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
17965
18241
|
const errSource = stderr || stdout || `cursor-agent exited with code ${String(exitCode ?? "unknown")} and no output`;
|
|
17966
18242
|
const parsed = parseAgentError(errSource);
|
|
17967
18243
|
const userError = formatErrorForUser(parsed);
|
|
17968
|
-
|
|
18244
|
+
log14.error("cursor-cli failed", {
|
|
17969
18245
|
type: parsed.type,
|
|
17970
18246
|
message: parsed.message,
|
|
17971
18247
|
code: exitCode
|
|
@@ -17999,7 +18275,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
17999
18275
|
const converter = new StreamToSseConverter(model, { id, created });
|
|
18000
18276
|
const lineBuffer = new LineBuffer;
|
|
18001
18277
|
const emitToolCallAndTerminate = (toolCall) => {
|
|
18002
|
-
|
|
18278
|
+
log14.debug("Intercepted OpenCode tool call (stream)", {
|
|
18003
18279
|
name: toolCall.function.name,
|
|
18004
18280
|
callId: toolCall.id
|
|
18005
18281
|
});
|
|
@@ -18082,7 +18358,15 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18082
18358
|
}
|
|
18083
18359
|
});
|
|
18084
18360
|
if (result.terminate) {
|
|
18085
|
-
|
|
18361
|
+
if (!result.terminate.silent) {
|
|
18362
|
+
emitTerminalAssistantErrorAndTerminate(result.terminate.message);
|
|
18363
|
+
} else {
|
|
18364
|
+
controller.enqueue(encoder.encode(formatSseDone()));
|
|
18365
|
+
streamTerminated = true;
|
|
18366
|
+
try {
|
|
18367
|
+
child.kill();
|
|
18368
|
+
} catch {}
|
|
18369
|
+
}
|
|
18086
18370
|
break;
|
|
18087
18371
|
}
|
|
18088
18372
|
if (result.intercepted) {
|
|
@@ -18140,7 +18424,15 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18140
18424
|
}
|
|
18141
18425
|
});
|
|
18142
18426
|
if (result.terminate) {
|
|
18143
|
-
|
|
18427
|
+
if (!result.terminate.silent) {
|
|
18428
|
+
emitTerminalAssistantErrorAndTerminate(result.terminate.message);
|
|
18429
|
+
} else {
|
|
18430
|
+
controller.enqueue(encoder.encode(formatSseDone()));
|
|
18431
|
+
streamTerminated = true;
|
|
18432
|
+
try {
|
|
18433
|
+
child.kill();
|
|
18434
|
+
} catch {}
|
|
18435
|
+
}
|
|
18144
18436
|
break;
|
|
18145
18437
|
}
|
|
18146
18438
|
if (result.intercepted) {
|
|
@@ -18157,14 +18449,15 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18157
18449
|
if (streamTerminated) {
|
|
18158
18450
|
return;
|
|
18159
18451
|
}
|
|
18160
|
-
|
|
18452
|
+
const exitCode = await child.exited;
|
|
18453
|
+
if (exitCode !== 0) {
|
|
18161
18454
|
const stderrText = await new Response(child.stderr).text();
|
|
18162
|
-
const errSource = (stderrText || "").trim() || `cursor-agent exited with code ${String(
|
|
18455
|
+
const errSource = (stderrText || "").trim() || `cursor-agent exited with code ${String(exitCode ?? "unknown")} and no output`;
|
|
18163
18456
|
const parsed = parseAgentError(errSource);
|
|
18164
18457
|
const msg = formatErrorForUser(parsed);
|
|
18165
|
-
|
|
18458
|
+
log14.error("cursor-cli streaming failed", {
|
|
18166
18459
|
type: parsed.type,
|
|
18167
|
-
code:
|
|
18460
|
+
code: exitCode
|
|
18168
18461
|
});
|
|
18169
18462
|
const errChunk = createChatCompletionChunk(id, created, model, msg, true);
|
|
18170
18463
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify(errChunk)}
|
|
@@ -18173,8 +18466,8 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18173
18466
|
controller.enqueue(encoder.encode(formatSseDone()));
|
|
18174
18467
|
return;
|
|
18175
18468
|
}
|
|
18176
|
-
|
|
18177
|
-
exitCode
|
|
18469
|
+
log14.debug("cursor-agent completed (bun stream)", {
|
|
18470
|
+
exitCode
|
|
18178
18471
|
});
|
|
18179
18472
|
const doneChunk = createChatCompletionChunk(id, created, model, "", true);
|
|
18180
18473
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify(doneChunk)}
|
|
@@ -18208,8 +18501,12 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18208
18501
|
try {
|
|
18209
18502
|
const res = await fetch(`http://${CURSOR_PROXY_HOST}:${CURSOR_PROXY_DEFAULT_PORT}/health`).catch(() => null);
|
|
18210
18503
|
if (res && res.ok) {
|
|
18211
|
-
|
|
18212
|
-
|
|
18504
|
+
const payload = await res.json().catch(() => null);
|
|
18505
|
+
if (isReusableProxyHealthPayload(payload, workspaceDirectory)) {
|
|
18506
|
+
state.baseURL = CURSOR_PROXY_DEFAULT_BASE_URL;
|
|
18507
|
+
state.baseURLByWorkspace[normalizedWorkspace] = CURSOR_PROXY_DEFAULT_BASE_URL;
|
|
18508
|
+
return CURSOR_PROXY_DEFAULT_BASE_URL;
|
|
18509
|
+
}
|
|
18213
18510
|
}
|
|
18214
18511
|
} catch {}
|
|
18215
18512
|
}
|
|
@@ -18220,7 +18517,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18220
18517
|
const url2 = new URL(req.url || "/", `http://${req.headers.host}`);
|
|
18221
18518
|
if (url2.pathname === "/health") {
|
|
18222
18519
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
18223
|
-
res.end(JSON.stringify({ ok: true }));
|
|
18520
|
+
res.end(JSON.stringify({ ok: true, workspaceDirectory }));
|
|
18224
18521
|
return;
|
|
18225
18522
|
}
|
|
18226
18523
|
if (url2.pathname === "/v1/models" || url2.pathname === "/models") {
|
|
@@ -18244,7 +18541,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18244
18541
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
18245
18542
|
res.end(JSON.stringify({ object: "list", data: models }));
|
|
18246
18543
|
} catch (err) {
|
|
18247
|
-
|
|
18544
|
+
log14.error("Failed to list models", { error: String(err) });
|
|
18248
18545
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
18249
18546
|
res.end(JSON.stringify({ error: "Failed to fetch models" }));
|
|
18250
18547
|
}
|
|
@@ -18255,7 +18552,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18255
18552
|
res.end(JSON.stringify({ error: `Unsupported path: ${url2.pathname}` }));
|
|
18256
18553
|
return;
|
|
18257
18554
|
}
|
|
18258
|
-
|
|
18555
|
+
log14.debug("Proxy request (node)", { method: req.method, path: url2.pathname });
|
|
18259
18556
|
let body = "";
|
|
18260
18557
|
for await (const chunk of req) {
|
|
18261
18558
|
body += chunk;
|
|
@@ -18278,7 +18575,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18278
18575
|
const contentLen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr${m.content.length}` : typeof m?.content;
|
|
18279
18576
|
return `${i}:${role}${hasTc ? `(tc:${hasTc})` : ""}${role === "tool" ? `(tcid:${tcId},name:${tcName},clen:${contentLen})` : `(clen:${contentLen})`}`;
|
|
18280
18577
|
});
|
|
18281
|
-
|
|
18578
|
+
log14.debug("Proxy chat request (node)", {
|
|
18282
18579
|
stream,
|
|
18283
18580
|
model,
|
|
18284
18581
|
messages: messages.length,
|
|
@@ -18309,14 +18606,14 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18309
18606
|
let spawnErrorText = null;
|
|
18310
18607
|
child.on("error", (error45) => {
|
|
18311
18608
|
spawnErrorText = String(error45?.message || error45);
|
|
18312
|
-
|
|
18609
|
+
log14.error("Failed to spawn cursor-agent", { error: spawnErrorText, model });
|
|
18313
18610
|
});
|
|
18314
18611
|
child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
18315
18612
|
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
18316
18613
|
child.on("close", async (code) => {
|
|
18317
18614
|
const stdout = Buffer.concat(stdoutChunks).toString().trim();
|
|
18318
18615
|
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
18319
|
-
|
|
18616
|
+
log14.debug("cursor-agent completed (node non-stream)", {
|
|
18320
18617
|
code,
|
|
18321
18618
|
stdoutChars: stdout.length,
|
|
18322
18619
|
stderrChars: stderr.length,
|
|
@@ -18342,7 +18639,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18342
18639
|
return;
|
|
18343
18640
|
}
|
|
18344
18641
|
if (intercepted.toolCall) {
|
|
18345
|
-
|
|
18642
|
+
log14.debug("Intercepted OpenCode tool call (non-stream)", {
|
|
18346
18643
|
name: intercepted.toolCall.function.name,
|
|
18347
18644
|
callId: intercepted.toolCall.id
|
|
18348
18645
|
});
|
|
@@ -18356,7 +18653,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18356
18653
|
const errSource = stderr || stdout || spawnErrorText || `cursor-agent exited with code ${String(code ?? "unknown")} and no output`;
|
|
18357
18654
|
const parsed = parseAgentError(errSource);
|
|
18358
18655
|
const userError = formatErrorForUser(parsed);
|
|
18359
|
-
|
|
18656
|
+
log14.error("cursor-cli failed", {
|
|
18360
18657
|
type: parsed.type,
|
|
18361
18658
|
message: parsed.message,
|
|
18362
18659
|
code
|
|
@@ -18395,7 +18692,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18395
18692
|
return;
|
|
18396
18693
|
}
|
|
18397
18694
|
const errSource = String(error45?.message || error45);
|
|
18398
|
-
|
|
18695
|
+
log14.error("Failed to spawn cursor-agent (stream)", { error: errSource, model });
|
|
18399
18696
|
const parsed = parseAgentError(errSource);
|
|
18400
18697
|
const msg = formatErrorForUser(parsed);
|
|
18401
18698
|
const errChunk = createChatCompletionChunk(id, created, model, msg, true);
|
|
@@ -18410,7 +18707,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18410
18707
|
if (streamTerminated || res.writableEnded) {
|
|
18411
18708
|
return;
|
|
18412
18709
|
}
|
|
18413
|
-
|
|
18710
|
+
log14.debug("Intercepted OpenCode tool call (stream)", {
|
|
18414
18711
|
name: toolCall.function.name,
|
|
18415
18712
|
callId: toolCall.id
|
|
18416
18713
|
});
|
|
@@ -18492,7 +18789,14 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18492
18789
|
}
|
|
18493
18790
|
});
|
|
18494
18791
|
if (result.terminate) {
|
|
18495
|
-
|
|
18792
|
+
if (!result.terminate.silent) {
|
|
18793
|
+
emitTerminalAssistantErrorAndTerminate(result.terminate.message);
|
|
18794
|
+
} else {
|
|
18795
|
+
streamTerminated = true;
|
|
18796
|
+
try {
|
|
18797
|
+
child.kill();
|
|
18798
|
+
} catch {}
|
|
18799
|
+
}
|
|
18496
18800
|
break;
|
|
18497
18801
|
}
|
|
18498
18802
|
if (result.intercepted) {
|
|
@@ -18555,7 +18859,14 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18555
18859
|
}
|
|
18556
18860
|
});
|
|
18557
18861
|
if (result.terminate) {
|
|
18558
|
-
|
|
18862
|
+
if (!result.terminate.silent) {
|
|
18863
|
+
emitTerminalAssistantErrorAndTerminate(result.terminate.message);
|
|
18864
|
+
} else {
|
|
18865
|
+
streamTerminated = true;
|
|
18866
|
+
try {
|
|
18867
|
+
child.kill();
|
|
18868
|
+
} catch {}
|
|
18869
|
+
}
|
|
18559
18870
|
break;
|
|
18560
18871
|
}
|
|
18561
18872
|
if (result.intercepted) {
|
|
@@ -18578,7 +18889,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18578
18889
|
perf.mark("request:done");
|
|
18579
18890
|
perf.summarize();
|
|
18580
18891
|
const stderrText = Buffer.concat(stderrChunks).toString().trim();
|
|
18581
|
-
|
|
18892
|
+
log14.debug("cursor-agent completed (node stream)", {
|
|
18582
18893
|
code,
|
|
18583
18894
|
stderrChars: stderrText.length
|
|
18584
18895
|
});
|
|
@@ -18628,7 +18939,8 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18628
18939
|
server2.once("error", reject);
|
|
18629
18940
|
});
|
|
18630
18941
|
const baseURL = `http://${CURSOR_PROXY_HOST}:${CURSOR_PROXY_DEFAULT_PORT}/v1`;
|
|
18631
|
-
|
|
18942
|
+
state.baseURL = baseURL;
|
|
18943
|
+
state.baseURLByWorkspace[normalizedWorkspace] = baseURL;
|
|
18632
18944
|
return baseURL;
|
|
18633
18945
|
} catch (error45) {
|
|
18634
18946
|
if (error45?.code !== "EADDRINUSE") {
|
|
@@ -18638,8 +18950,12 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18638
18950
|
try {
|
|
18639
18951
|
const res = await fetch(`http://${CURSOR_PROXY_HOST}:${CURSOR_PROXY_DEFAULT_PORT}/health`).catch(() => null);
|
|
18640
18952
|
if (res && res.ok) {
|
|
18641
|
-
|
|
18642
|
-
|
|
18953
|
+
const payload = await res.json().catch(() => null);
|
|
18954
|
+
if (isReusableProxyHealthPayload(payload, workspaceDirectory)) {
|
|
18955
|
+
state.baseURL = CURSOR_PROXY_DEFAULT_BASE_URL;
|
|
18956
|
+
state.baseURLByWorkspace[normalizedWorkspace] = CURSOR_PROXY_DEFAULT_BASE_URL;
|
|
18957
|
+
return CURSOR_PROXY_DEFAULT_BASE_URL;
|
|
18958
|
+
}
|
|
18643
18959
|
}
|
|
18644
18960
|
} catch {}
|
|
18645
18961
|
}
|
|
@@ -18650,7 +18966,8 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18650
18966
|
});
|
|
18651
18967
|
const addr = server2.address();
|
|
18652
18968
|
const baseURL = `http://${CURSOR_PROXY_HOST}:${addr.port}/v1`;
|
|
18653
|
-
|
|
18969
|
+
state.baseURL = baseURL;
|
|
18970
|
+
state.baseURLByWorkspace[normalizedWorkspace] = baseURL;
|
|
18654
18971
|
return baseURL;
|
|
18655
18972
|
}
|
|
18656
18973
|
}
|
|
@@ -18793,7 +19110,7 @@ function buildToolHookEntries(registry2, fallbackBaseDir) {
|
|
|
18793
19110
|
const normalizedArgs = applyToolContextDefaults(toolName, args, context, fallbackBaseDir, sessionWorkspaceBySession);
|
|
18794
19111
|
return await handler(normalizedArgs);
|
|
18795
19112
|
} catch (error45) {
|
|
18796
|
-
|
|
19113
|
+
log14.debug("Tool hook execution failed", { tool: toolName, error: String(error45?.message || error45) });
|
|
18797
19114
|
throw error45;
|
|
18798
19115
|
}
|
|
18799
19116
|
}
|
|
@@ -18805,9 +19122,9 @@ function buildToolHookEntries(registry2, fallbackBaseDir) {
|
|
|
18805
19122
|
}
|
|
18806
19123
|
return entries;
|
|
18807
19124
|
}
|
|
18808
|
-
var
|
|
19125
|
+
var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "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 }) => {
|
|
18809
19126
|
const workspaceDirectory = resolveWorkspaceDirectory(worktree, directory);
|
|
18810
|
-
|
|
19127
|
+
log14.debug("Plugin initializing", {
|
|
18811
19128
|
directory,
|
|
18812
19129
|
worktree,
|
|
18813
19130
|
workspaceDirectory,
|
|
@@ -18815,22 +19132,22 @@ var log13, CURSOR_PROVIDER_ID = "cursor-acp", CURSOR_PROXY_HOST = "127.0.0.1", C
|
|
|
18815
19132
|
serverUrl: serverUrl?.toString()
|
|
18816
19133
|
});
|
|
18817
19134
|
if (!TOOL_LOOP_MODE_VALID) {
|
|
18818
|
-
|
|
19135
|
+
log14.warn("Invalid CURSOR_ACP_TOOL_LOOP_MODE; defaulting to opencode", { value: TOOL_LOOP_MODE_RAW });
|
|
18819
19136
|
}
|
|
18820
19137
|
if (!PROVIDER_BOUNDARY_MODE_VALID) {
|
|
18821
|
-
|
|
19138
|
+
log14.warn("Invalid CURSOR_ACP_PROVIDER_BOUNDARY; defaulting to v1", {
|
|
18822
19139
|
value: PROVIDER_BOUNDARY_MODE_RAW
|
|
18823
19140
|
});
|
|
18824
19141
|
}
|
|
18825
19142
|
if (!TOOL_LOOP_MAX_REPEAT_VALID) {
|
|
18826
|
-
|
|
19143
|
+
log14.warn("Invalid CURSOR_ACP_TOOL_LOOP_MAX_REPEAT; defaulting to 3", {
|
|
18827
19144
|
value: TOOL_LOOP_MAX_REPEAT_RAW
|
|
18828
19145
|
});
|
|
18829
19146
|
}
|
|
18830
19147
|
if (ENABLE_PROVIDER_BOUNDARY_AUTOFALLBACK && PROVIDER_BOUNDARY.mode !== "v1") {
|
|
18831
|
-
|
|
19148
|
+
log14.debug("Provider boundary auto-fallback is enabled but inactive unless mode=v1");
|
|
18832
19149
|
}
|
|
18833
|
-
|
|
19150
|
+
log14.info("Tool loop mode configured", {
|
|
18834
19151
|
mode: TOOL_LOOP_MODE,
|
|
18835
19152
|
providerBoundary: PROVIDER_BOUNDARY.mode,
|
|
18836
19153
|
proxyExecToolCalls: PROXY_EXECUTE_TOOL_CALLS,
|
|
@@ -18841,9 +19158,9 @@ var log13, CURSOR_PROVIDER_ID = "cursor-acp", CURSOR_PROXY_HOST = "127.0.0.1", C
|
|
|
18841
19158
|
const toolsEnabled = process.env.CURSOR_ACP_ENABLE_OPENCODE_TOOLS !== "false";
|
|
18842
19159
|
const legacyProxyToolPathsEnabled = toolsEnabled && TOOL_LOOP_MODE === "proxy-exec";
|
|
18843
19160
|
if (toolsEnabled && TOOL_LOOP_MODE === "opencode") {
|
|
18844
|
-
|
|
19161
|
+
log14.debug("OpenCode mode active; skipping legacy SDK/MCP discovery and proxy-side tool execution");
|
|
18845
19162
|
} else if (toolsEnabled && TOOL_LOOP_MODE === "off") {
|
|
18846
|
-
|
|
19163
|
+
log14.debug("Tool loop mode off; proxy-side tool execution disabled");
|
|
18847
19164
|
}
|
|
18848
19165
|
const serverClient = legacyProxyToolPathsEnabled ? createOpencodeClient({ baseUrl: serverUrl.toString(), directory: workspaceDirectory }) : null;
|
|
18849
19166
|
const discovery = legacyProxyToolPathsEnabled ? new OpenCodeToolDiscovery(serverClient ?? client3) : null;
|
|
@@ -18895,7 +19212,7 @@ var log13, CURSOR_PROVIDER_ID = "cursor-acp", CURSOR_PROXY_HOST = "127.0.0.1", C
|
|
|
18895
19212
|
discoveredList = await discovery.listTools();
|
|
18896
19213
|
discoveredList.forEach((t) => toolsByName.set(t.name, t));
|
|
18897
19214
|
} catch (err) {
|
|
18898
|
-
|
|
19215
|
+
log14.debug("Tool discovery failed, using local tools only", { error: String(err) });
|
|
18899
19216
|
}
|
|
18900
19217
|
}
|
|
18901
19218
|
const allTools = [...localTools, ...discoveredList];
|
|
@@ -18925,11 +19242,11 @@ var log13, CURSOR_PROVIDER_ID = "cursor-acp", CURSOR_PROXY_HOST = "127.0.0.1", C
|
|
|
18925
19242
|
}
|
|
18926
19243
|
lastToolNames = toolEntries.map((e) => e.function.name);
|
|
18927
19244
|
lastToolMap = allTools.map((t) => ({ id: t.id, name: t.name }));
|
|
18928
|
-
|
|
19245
|
+
log14.debug("Tools refreshed", { local: localTools.length, discovered: discoveredList.length, total: toolEntries.length });
|
|
18929
19246
|
return toolEntries;
|
|
18930
19247
|
}
|
|
18931
19248
|
const proxyBaseURL = await ensureCursorProxyServer(workspaceDirectory, router);
|
|
18932
|
-
|
|
19249
|
+
log14.debug("Proxy server started", { baseURL: proxyBaseURL });
|
|
18933
19250
|
const toolHookEntries = buildToolHookEntries(localRegistry, workspaceDirectory);
|
|
18934
19251
|
return {
|
|
18935
19252
|
tool: toolHookEntries,
|
|
@@ -18944,9 +19261,9 @@ var log13, CURSOR_PROVIDER_ID = "cursor-acp", CURSOR_PROXY_HOST = "127.0.0.1", C
|
|
|
18944
19261
|
type: "oauth",
|
|
18945
19262
|
async authorize() {
|
|
18946
19263
|
try {
|
|
18947
|
-
|
|
19264
|
+
log14.info("Starting OAuth flow");
|
|
18948
19265
|
const { url: url2, instructions, callback } = await startCursorOAuth();
|
|
18949
|
-
|
|
19266
|
+
log14.debug("Got OAuth URL", { url: url2.substring(0, 50) + "..." });
|
|
18950
19267
|
return {
|
|
18951
19268
|
url: url2,
|
|
18952
19269
|
instructions,
|
|
@@ -18954,7 +19271,7 @@ var log13, CURSOR_PROVIDER_ID = "cursor-acp", CURSOR_PROXY_HOST = "127.0.0.1", C
|
|
|
18954
19271
|
callback
|
|
18955
19272
|
};
|
|
18956
19273
|
} catch (error45) {
|
|
18957
|
-
|
|
19274
|
+
log14.error("OAuth error", { error: error45 });
|
|
18958
19275
|
throw error45;
|
|
18959
19276
|
}
|
|
18960
19277
|
}
|
|
@@ -18978,10 +19295,10 @@ var log13, CURSOR_PROVIDER_ID = "cursor-acp", CURSOR_PROXY_HOST = "127.0.0.1", C
|
|
|
18978
19295
|
output.options.tools = resolved.tools;
|
|
18979
19296
|
} else if (resolved.action === "preserve") {
|
|
18980
19297
|
const count = Array.isArray(existingTools) ? existingTools.length : 0;
|
|
18981
|
-
|
|
19298
|
+
log14.debug("Using OpenCode-provided tools from chat.params", { count });
|
|
18982
19299
|
}
|
|
18983
19300
|
} catch (err) {
|
|
18984
|
-
|
|
19301
|
+
log14.debug("Failed to refresh tools", { error: String(err) });
|
|
18985
19302
|
}
|
|
18986
19303
|
}
|
|
18987
19304
|
},
|
|
@@ -19002,6 +19319,7 @@ var init_plugin = __esm(() => {
|
|
|
19002
19319
|
init_parser();
|
|
19003
19320
|
init_logger();
|
|
19004
19321
|
init_perf();
|
|
19322
|
+
init_prompt_builder();
|
|
19005
19323
|
init_tool_loop();
|
|
19006
19324
|
init_discovery();
|
|
19007
19325
|
init_schema();
|
|
@@ -19015,7 +19333,10 @@ var init_plugin = __esm(() => {
|
|
|
19015
19333
|
init_runtime_interception();
|
|
19016
19334
|
init_tool_schema_compat();
|
|
19017
19335
|
init_tool_loop_guard();
|
|
19018
|
-
|
|
19336
|
+
log14 = createLogger("plugin");
|
|
19337
|
+
DEBUG_LOG_DIR2 = join4(homedir4(), ".config", "opencode", "logs");
|
|
19338
|
+
DEBUG_LOG_FILE2 = join4(DEBUG_LOG_DIR2, "tool-loop-debug.log");
|
|
19339
|
+
CURSOR_PROVIDER_PREFIX = `${CURSOR_PROVIDER_ID}/`;
|
|
19019
19340
|
CURSOR_PROXY_DEFAULT_BASE_URL = `http://${CURSOR_PROXY_HOST}:${CURSOR_PROXY_DEFAULT_PORT}/v1`;
|
|
19020
19341
|
REUSE_EXISTING_PROXY = process.env.CURSOR_ACP_REUSE_EXISTING_PROXY !== "false";
|
|
19021
19342
|
FORCE_TOOL_MODE = process.env.CURSOR_ACP_FORCE !== "false";
|
|
@@ -19256,7 +19577,7 @@ init_logger();
|
|
|
19256
19577
|
import { execSync } from "node:child_process";
|
|
19257
19578
|
import { createServer } from "node:net";
|
|
19258
19579
|
import { platform as platform2 } from "node:os";
|
|
19259
|
-
var
|
|
19580
|
+
var log15 = createLogger("proxy-server");
|
|
19260
19581
|
var DEFAULT_PORT = 32124;
|
|
19261
19582
|
var PORT_RANGE_SIZE = 256;
|
|
19262
19583
|
async function isPortAvailable(port, host) {
|
|
@@ -19275,10 +19596,10 @@ async function isPortAvailable(port, host) {
|
|
|
19275
19596
|
}
|
|
19276
19597
|
function getUsedPortsInRange(minPort, maxPort) {
|
|
19277
19598
|
const used = new Set;
|
|
19278
|
-
const
|
|
19599
|
+
const os2 = platform2();
|
|
19279
19600
|
try {
|
|
19280
19601
|
let out;
|
|
19281
|
-
if (
|
|
19602
|
+
if (os2 === "linux") {
|
|
19282
19603
|
out = execSync("ss -tlnH", { encoding: "utf8", timeout: 5000, stdio: ["pipe", "pipe", "ignore"] });
|
|
19283
19604
|
for (const line of out.split(`
|
|
19284
19605
|
`)) {
|
|
@@ -19291,7 +19612,7 @@ function getUsedPortsInRange(minPort, maxPort) {
|
|
|
19291
19612
|
if (!Number.isNaN(port) && port >= minPort && port < maxPort)
|
|
19292
19613
|
used.add(port);
|
|
19293
19614
|
}
|
|
19294
|
-
} else if (
|
|
19615
|
+
} else if (os2 === "darwin") {
|
|
19295
19616
|
out = execSync("lsof -iTCP -sTCP:LISTEN -nP", { encoding: "utf8", timeout: 5000, stdio: ["pipe", "pipe", "ignore"] });
|
|
19296
19617
|
for (const line of out.split(`
|
|
19297
19618
|
`)) {
|
|
@@ -19303,11 +19624,11 @@ function getUsedPortsInRange(minPort, maxPort) {
|
|
|
19303
19624
|
}
|
|
19304
19625
|
}
|
|
19305
19626
|
} else {
|
|
19306
|
-
|
|
19627
|
+
log15.debug(`Port detection not supported on ${os2}. Using probe-based discovery.`);
|
|
19307
19628
|
}
|
|
19308
19629
|
} catch (error45) {
|
|
19309
19630
|
const msg = error45 instanceof Error ? error45.message : String(error45);
|
|
19310
|
-
|
|
19631
|
+
log15.debug(`Port detection failed: ${msg}. Using probe-based discovery.`);
|
|
19311
19632
|
}
|
|
19312
19633
|
return used;
|
|
19313
19634
|
}
|
|
@@ -19346,8 +19667,8 @@ function createProxyServer(config2) {
|
|
|
19346
19667
|
hostname: host,
|
|
19347
19668
|
fetch(request) {
|
|
19348
19669
|
const url2 = new URL(request.url);
|
|
19349
|
-
const
|
|
19350
|
-
if (
|
|
19670
|
+
const path2 = url2.pathname;
|
|
19671
|
+
if (path2 === healthCheckPath && request.method === "GET") {
|
|
19351
19672
|
return Response.json({ ok: true });
|
|
19352
19673
|
}
|
|
19353
19674
|
return new Response("Not Found", { status: 404 });
|
|
@@ -19358,7 +19679,7 @@ function createProxyServer(config2) {
|
|
|
19358
19679
|
const err = error45 instanceof Error ? error45 : new Error(String(error45));
|
|
19359
19680
|
const isPortInUse = err.message.includes("EADDRINUSE") || err.message.includes("address already in use") || err.message.includes("port is already in use");
|
|
19360
19681
|
if (!isPortInUse) {
|
|
19361
|
-
|
|
19682
|
+
log15.debug(`Unexpected error starting on port ${port}: ${err.message}`);
|
|
19362
19683
|
}
|
|
19363
19684
|
return { success: false, error: err };
|
|
19364
19685
|
}
|
|
@@ -19374,13 +19695,13 @@ function createProxyServer(config2) {
|
|
|
19374
19695
|
if (result.success) {
|
|
19375
19696
|
port = requestedPort;
|
|
19376
19697
|
} else {
|
|
19377
|
-
|
|
19698
|
+
log15.debug(`Requested port ${requestedPort} unavailable: ${result.error?.message ?? "unknown"}. Falling back to automatic port selection.`);
|
|
19378
19699
|
port = await findAvailablePort(host);
|
|
19379
19700
|
const fallbackResult = tryStart(port);
|
|
19380
19701
|
if (!fallbackResult.success) {
|
|
19381
19702
|
throw new Error(`Failed to start server on port ${requestedPort} (${result.error?.message ?? "unknown"}) ` + `and fallback port ${port} (${fallbackResult.error?.message ?? "unknown"})`);
|
|
19382
19703
|
}
|
|
19383
|
-
|
|
19704
|
+
log15.debug(`Server started on fallback port ${port} instead of requested port ${requestedPort}`);
|
|
19384
19705
|
}
|
|
19385
19706
|
} else {
|
|
19386
19707
|
port = await findAvailablePort(host);
|
|
@@ -19725,12 +20046,12 @@ init_auth();
|
|
|
19725
20046
|
// src/commands/status.ts
|
|
19726
20047
|
init_auth();
|
|
19727
20048
|
init_logger();
|
|
19728
|
-
import { existsSync as
|
|
19729
|
-
var
|
|
20049
|
+
import { existsSync as existsSync5 } from "fs";
|
|
20050
|
+
var log16 = createLogger("status");
|
|
19730
20051
|
function checkAuthStatus() {
|
|
19731
20052
|
const authFilePath = getAuthFilePath();
|
|
19732
|
-
const exists =
|
|
19733
|
-
|
|
20053
|
+
const exists = existsSync5(authFilePath);
|
|
20054
|
+
log16.debug("Checking auth status", { path: authFilePath });
|
|
19734
20055
|
if (exists) {
|
|
19735
20056
|
return {
|
|
19736
20057
|
authenticated: true,
|