@rama_nigg/open-cursor 2.3.2 → 2.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -247
- package/dist/cli/discover.js +0 -15
- package/dist/cli/opencode-cursor.js +0 -15
- package/dist/index.js +307 -225
- package/dist/plugin-entry.js +312 -220
- package/package.json +7 -2
- package/src/plugin-toggle.ts +9 -1
- package/src/plugin.ts +33 -0
- package/src/provider/boundary.ts +3 -3
- package/src/provider/passthrough-tracker.ts +38 -0
- package/src/provider/runtime-interception.ts +175 -123
- package/src/provider/tool-loop-guard.ts +0 -63
- package/src/proxy/tool-loop.ts +42 -28
- package/src/services/toast-service.ts +81 -0
package/dist/index.js
CHANGED
|
@@ -1,20 +1,5 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
4
2
|
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
|
-
};
|
|
18
3
|
var __export = (target, all) => {
|
|
19
4
|
for (var name in all)
|
|
20
5
|
__defProp(target, name, {
|
|
@@ -13629,41 +13614,46 @@ function extractAllowedToolNames(tools) {
|
|
|
13629
13614
|
}
|
|
13630
13615
|
function extractOpenAiToolCall(event, allowedToolNames) {
|
|
13631
13616
|
if (allowedToolNames.size === 0) {
|
|
13632
|
-
return
|
|
13617
|
+
return { action: "skip", skipReason: "no_allowed_tools" };
|
|
13633
13618
|
}
|
|
13634
13619
|
const { name, args, skipped } = extractToolNameAndArgs(event);
|
|
13635
13620
|
if (skipped) {
|
|
13636
|
-
return
|
|
13621
|
+
return { action: "skip", skipReason: "event_skipped" };
|
|
13637
13622
|
}
|
|
13638
13623
|
if (!name) {
|
|
13639
|
-
return
|
|
13624
|
+
return { action: "skip", skipReason: "no_name" };
|
|
13640
13625
|
}
|
|
13641
13626
|
const resolvedName = resolveAllowedToolName(name, allowedToolNames);
|
|
13642
|
-
if (
|
|
13643
|
-
|
|
13644
|
-
|
|
13645
|
-
|
|
13646
|
-
|
|
13647
|
-
|
|
13648
|
-
|
|
13649
|
-
|
|
13650
|
-
|
|
13651
|
-
|
|
13652
|
-
|
|
13653
|
-
|
|
13654
|
-
|
|
13655
|
-
|
|
13656
|
-
|
|
13657
|
-
|
|
13627
|
+
if (resolvedName) {
|
|
13628
|
+
if (args === undefined && event.subtype === "started") {
|
|
13629
|
+
log5.debug("Tool call args extraction returned undefined", {
|
|
13630
|
+
toolName: name,
|
|
13631
|
+
subtype: event.subtype ?? "none",
|
|
13632
|
+
payloadKeys: Object.entries(event.tool_call || {}).map(([k, v]) => `${k}:[${isRecord(v) ? Object.keys(v).join(",") : typeof v}]`),
|
|
13633
|
+
hasCallId: Boolean(event.call_id)
|
|
13634
|
+
});
|
|
13635
|
+
}
|
|
13636
|
+
const callId = event.call_id || event.tool_call_id || "call_unknown";
|
|
13637
|
+
return {
|
|
13638
|
+
action: "intercept",
|
|
13639
|
+
toolCall: {
|
|
13640
|
+
id: callId,
|
|
13641
|
+
type: "function",
|
|
13642
|
+
function: {
|
|
13643
|
+
name: resolvedName,
|
|
13644
|
+
arguments: toOpenAiArguments(args)
|
|
13645
|
+
}
|
|
13646
|
+
}
|
|
13647
|
+
};
|
|
13658
13648
|
}
|
|
13659
|
-
|
|
13649
|
+
log5.debug("Tool call not in allowlist; passing through to cursor-agent", {
|
|
13650
|
+
name,
|
|
13651
|
+
normalized: normalizeAliasKey(name),
|
|
13652
|
+
allowedToolCount: allowedToolNames.size
|
|
13653
|
+
});
|
|
13660
13654
|
return {
|
|
13661
|
-
|
|
13662
|
-
|
|
13663
|
-
function: {
|
|
13664
|
-
name: resolvedName,
|
|
13665
|
-
arguments: toOpenAiArguments(args)
|
|
13666
|
-
}
|
|
13655
|
+
action: "passthrough",
|
|
13656
|
+
passthroughName: name
|
|
13667
13657
|
};
|
|
13668
13658
|
}
|
|
13669
13659
|
function createToolCallCompletionResponse(meta, toolCall) {
|
|
@@ -16403,7 +16393,7 @@ function createSharedBoundary(providerId) {
|
|
|
16403
16393
|
},
|
|
16404
16394
|
maybeExtractToolCall(event, allowedToolNames, toolLoopMode) {
|
|
16405
16395
|
if (toolLoopMode !== "opencode") {
|
|
16406
|
-
return
|
|
16396
|
+
return { action: "skip", skipReason: "tool_loop_mode_not_opencode" };
|
|
16407
16397
|
}
|
|
16408
16398
|
return extractOpenAiToolCall(event, allowedToolNames);
|
|
16409
16399
|
},
|
|
@@ -16830,9 +16820,33 @@ async function handleToolLoopEventLegacy(options) {
|
|
|
16830
16820
|
responseMeta,
|
|
16831
16821
|
onToolUpdate,
|
|
16832
16822
|
onToolResult,
|
|
16833
|
-
onInterceptedToolCall
|
|
16823
|
+
onInterceptedToolCall,
|
|
16824
|
+
passThroughTracker
|
|
16834
16825
|
} = options;
|
|
16835
|
-
const
|
|
16826
|
+
const extraction = toolLoopMode === "opencode" ? extractOpenAiToolCall(event, allowedToolNames) : { action: "skip", skipReason: "tool_loop_mode_not_opencode" };
|
|
16827
|
+
if (extraction.action === "passthrough") {
|
|
16828
|
+
passThroughTracker?.trackTool(extraction.passthroughName);
|
|
16829
|
+
log13.debug("MCP tool passed through to cursor-agent (legacy)", {
|
|
16830
|
+
tool: extraction.passthroughName
|
|
16831
|
+
});
|
|
16832
|
+
return { intercepted: false, skipConverter: false };
|
|
16833
|
+
}
|
|
16834
|
+
if (extraction.action === "skip" || !extraction.toolCall) {
|
|
16835
|
+
const updates2 = await toolMapper.mapCursorEventToAcp(event, event.session_id ?? toolSessionId);
|
|
16836
|
+
if (shouldEmitToolUpdates) {
|
|
16837
|
+
for (const update of updates2) {
|
|
16838
|
+
await onToolUpdate(update);
|
|
16839
|
+
}
|
|
16840
|
+
}
|
|
16841
|
+
if (toolRouter && proxyExecuteToolCalls) {
|
|
16842
|
+
const toolResult = await toolRouter.handleToolCall(event, responseMeta);
|
|
16843
|
+
if (toolResult) {
|
|
16844
|
+
await onToolResult(toolResult);
|
|
16845
|
+
}
|
|
16846
|
+
}
|
|
16847
|
+
return { intercepted: false, skipConverter: suppressConverterToolEvents };
|
|
16848
|
+
}
|
|
16849
|
+
const interceptedToolCall = extraction.toolCall;
|
|
16836
16850
|
if (interceptedToolCall) {
|
|
16837
16851
|
const compat2 = applyToolSchemaCompat(interceptedToolCall, toolSchemaMap);
|
|
16838
16852
|
let normalizedToolCall = compat2.toolCall;
|
|
@@ -16909,118 +16923,125 @@ async function handleToolLoopEventV1(options) {
|
|
|
16909
16923
|
responseMeta,
|
|
16910
16924
|
onToolUpdate,
|
|
16911
16925
|
onToolResult,
|
|
16912
|
-
onInterceptedToolCall
|
|
16926
|
+
onInterceptedToolCall,
|
|
16927
|
+
passThroughTracker
|
|
16913
16928
|
} = options;
|
|
16914
|
-
let
|
|
16929
|
+
let extraction;
|
|
16915
16930
|
try {
|
|
16916
|
-
|
|
16931
|
+
extraction = boundary.maybeExtractToolCall(event, allowedToolNames, toolLoopMode);
|
|
16917
16932
|
} catch (error45) {
|
|
16918
16933
|
throw new ToolBoundaryExtractionError("Boundary tool extraction failed", error45);
|
|
16919
16934
|
}
|
|
16920
|
-
if (
|
|
16921
|
-
|
|
16922
|
-
|
|
16923
|
-
|
|
16924
|
-
rawArgs: safeArgTypeSummary(event),
|
|
16925
|
-
normalizedArgs: compat2.normalizedArgs
|
|
16926
|
-
} : undefined;
|
|
16927
|
-
log13.debug("Applied tool schema compatibility", {
|
|
16928
|
-
tool: interceptedToolCall.function.name,
|
|
16929
|
-
originalArgKeys: compat2.originalArgKeys,
|
|
16930
|
-
normalizedArgKeys: compat2.normalizedArgKeys,
|
|
16931
|
-
collisionKeys: compat2.collisionKeys,
|
|
16932
|
-
validationOk: compat2.validation.ok,
|
|
16933
|
-
...editDiag ? { editDiag } : {}
|
|
16935
|
+
if (extraction.action === "passthrough") {
|
|
16936
|
+
passThroughTracker?.trackTool(extraction.passthroughName);
|
|
16937
|
+
log13.debug("MCP tool passed through to cursor-agent (v1)", {
|
|
16938
|
+
tool: extraction.passthroughName
|
|
16934
16939
|
});
|
|
16935
|
-
|
|
16936
|
-
|
|
16937
|
-
|
|
16940
|
+
return { intercepted: false, skipConverter: false };
|
|
16941
|
+
}
|
|
16942
|
+
if (extraction.action === "skip" || !extraction.toolCall) {
|
|
16943
|
+
const updates = await toolMapper.mapCursorEventToAcp(event, event.session_id ?? toolSessionId);
|
|
16944
|
+
if (shouldEmitToolUpdates) {
|
|
16945
|
+
for (const update of updates) {
|
|
16946
|
+
await onToolUpdate(update);
|
|
16947
|
+
}
|
|
16948
|
+
}
|
|
16949
|
+
if (toolRouter && proxyExecuteToolCalls) {
|
|
16950
|
+
const toolResult = await toolRouter.handleToolCall(event, responseMeta);
|
|
16951
|
+
if (toolResult) {
|
|
16952
|
+
await onToolResult(toolResult);
|
|
16953
|
+
}
|
|
16954
|
+
}
|
|
16955
|
+
return { intercepted: false, skipConverter: suppressConverterToolEvents };
|
|
16956
|
+
}
|
|
16957
|
+
const interceptedToolCall = extraction.toolCall;
|
|
16958
|
+
const compat2 = applyToolSchemaCompat(interceptedToolCall, toolSchemaMap);
|
|
16959
|
+
let normalizedToolCall = compat2.toolCall;
|
|
16960
|
+
const editDiag = normalizedToolCall.function.name.toLowerCase() === "edit" ? {
|
|
16961
|
+
rawArgs: safeArgTypeSummary(event),
|
|
16962
|
+
normalizedArgs: compat2.normalizedArgs
|
|
16963
|
+
} : undefined;
|
|
16964
|
+
log13.debug("Applied tool schema compatibility", {
|
|
16965
|
+
tool: normalizedToolCall.function.name,
|
|
16966
|
+
originalArgKeys: compat2.originalArgKeys,
|
|
16967
|
+
normalizedArgKeys: compat2.normalizedArgKeys,
|
|
16968
|
+
collisionKeys: compat2.collisionKeys,
|
|
16969
|
+
validationOk: compat2.validation.ok,
|
|
16970
|
+
...editDiag ? { editDiag } : {}
|
|
16971
|
+
});
|
|
16972
|
+
if (compat2.validation.hasSchema && !compat2.validation.ok) {
|
|
16973
|
+
log13.debug("Tool schema compatibility validation failed", {
|
|
16974
|
+
tool: normalizedToolCall.function.name,
|
|
16975
|
+
missing: compat2.validation.missing,
|
|
16976
|
+
unexpected: compat2.validation.unexpected,
|
|
16977
|
+
typeErrors: compat2.validation.typeErrors,
|
|
16978
|
+
repairHint: compat2.validation.repairHint
|
|
16979
|
+
});
|
|
16980
|
+
const validationTermination = evaluateSchemaValidationLoopGuard(toolLoopGuard, normalizedToolCall, compat2.validation);
|
|
16981
|
+
if (validationTermination) {
|
|
16982
|
+
return { intercepted: false, skipConverter: true, terminate: validationTermination };
|
|
16983
|
+
}
|
|
16984
|
+
const termination2 = evaluateToolLoopGuard(toolLoopGuard, normalizedToolCall);
|
|
16985
|
+
if (termination2) {
|
|
16986
|
+
return { intercepted: false, skipConverter: true, terminate: termination2 };
|
|
16987
|
+
}
|
|
16988
|
+
const reroutedWrite = tryRerouteEditToWrite(normalizedToolCall, compat2.normalizedArgs, allowedToolNames, toolSchemaMap);
|
|
16989
|
+
if (reroutedWrite) {
|
|
16990
|
+
log13.debug("Rerouting malformed edit call to write", {
|
|
16991
|
+
path: reroutedWrite.path,
|
|
16938
16992
|
missing: compat2.validation.missing,
|
|
16939
|
-
|
|
16940
|
-
typeErrors: compat2.validation.typeErrors,
|
|
16941
|
-
repairHint: compat2.validation.repairHint
|
|
16993
|
+
typeErrors: compat2.validation.typeErrors
|
|
16942
16994
|
});
|
|
16943
|
-
|
|
16944
|
-
if (validationTermination) {
|
|
16945
|
-
return { intercepted: false, skipConverter: true, terminate: validationTermination };
|
|
16946
|
-
}
|
|
16947
|
-
const termination2 = evaluateToolLoopGuard(toolLoopGuard, interceptedToolCall);
|
|
16948
|
-
if (termination2) {
|
|
16949
|
-
return { intercepted: false, skipConverter: true, terminate: termination2 };
|
|
16950
|
-
}
|
|
16951
|
-
const reroutedWrite = tryRerouteEditToWrite(interceptedToolCall, compat2.normalizedArgs, allowedToolNames, toolSchemaMap);
|
|
16952
|
-
if (reroutedWrite) {
|
|
16953
|
-
log13.debug("Rerouting malformed edit call to write", {
|
|
16954
|
-
path: reroutedWrite.path,
|
|
16955
|
-
missing: compat2.validation.missing,
|
|
16956
|
-
typeErrors: compat2.validation.typeErrors
|
|
16957
|
-
});
|
|
16958
|
-
await onInterceptedToolCall(reroutedWrite.toolCall);
|
|
16959
|
-
return {
|
|
16960
|
-
intercepted: true,
|
|
16961
|
-
skipConverter: true
|
|
16962
|
-
};
|
|
16963
|
-
}
|
|
16964
|
-
if (schemaValidationFailureMode === "pass_through" && shouldTerminateOnSchemaValidation(interceptedToolCall, compat2.validation)) {
|
|
16965
|
-
return {
|
|
16966
|
-
intercepted: false,
|
|
16967
|
-
skipConverter: true,
|
|
16968
|
-
terminate: createSchemaValidationTermination(interceptedToolCall, compat2.validation)
|
|
16969
|
-
};
|
|
16970
|
-
}
|
|
16971
|
-
if (schemaValidationFailureMode === "pass_through" && shouldEmitNonFatalSchemaValidationHint(interceptedToolCall, compat2.validation)) {
|
|
16972
|
-
const hintChunk = createNonFatalSchemaValidationHintChunk(responseMeta, interceptedToolCall, compat2.validation);
|
|
16973
|
-
log13.debug("Emitting non-fatal schema validation hint and skipping malformed tool execution", {
|
|
16974
|
-
tool: interceptedToolCall.function.name,
|
|
16975
|
-
missing: compat2.validation.missing,
|
|
16976
|
-
typeErrors: compat2.validation.typeErrors
|
|
16977
|
-
});
|
|
16978
|
-
await onToolResult(hintChunk);
|
|
16979
|
-
return {
|
|
16980
|
-
intercepted: false,
|
|
16981
|
-
skipConverter: true
|
|
16982
|
-
};
|
|
16983
|
-
}
|
|
16984
|
-
if (schemaValidationFailureMode === "terminate") {
|
|
16985
|
-
return {
|
|
16986
|
-
intercepted: false,
|
|
16987
|
-
skipConverter: true,
|
|
16988
|
-
terminate: createSchemaValidationTermination(interceptedToolCall, compat2.validation)
|
|
16989
|
-
};
|
|
16990
|
-
}
|
|
16991
|
-
log13.debug("Forwarding schema-invalid tool call to OpenCode loop", {
|
|
16992
|
-
tool: interceptedToolCall.function.name,
|
|
16993
|
-
repairHint: compat2.validation.repairHint
|
|
16994
|
-
});
|
|
16995
|
-
await onInterceptedToolCall(interceptedToolCall);
|
|
16995
|
+
await onInterceptedToolCall(reroutedWrite.toolCall);
|
|
16996
16996
|
return {
|
|
16997
16997
|
intercepted: true,
|
|
16998
16998
|
skipConverter: true
|
|
16999
16999
|
};
|
|
17000
17000
|
}
|
|
17001
|
-
|
|
17002
|
-
|
|
17003
|
-
|
|
17001
|
+
if (schemaValidationFailureMode === "pass_through" && shouldTerminateOnSchemaValidation(normalizedToolCall, compat2.validation)) {
|
|
17002
|
+
return {
|
|
17003
|
+
intercepted: false,
|
|
17004
|
+
skipConverter: true,
|
|
17005
|
+
terminate: createSchemaValidationTermination(normalizedToolCall, compat2.validation)
|
|
17006
|
+
};
|
|
17004
17007
|
}
|
|
17005
|
-
|
|
17006
|
-
|
|
17007
|
-
|
|
17008
|
-
|
|
17009
|
-
|
|
17010
|
-
|
|
17011
|
-
|
|
17008
|
+
if (schemaValidationFailureMode === "pass_through" && shouldEmitNonFatalSchemaValidationHint(normalizedToolCall, compat2.validation)) {
|
|
17009
|
+
const hintChunk = createNonFatalSchemaValidationHintChunk(responseMeta, normalizedToolCall, compat2.validation);
|
|
17010
|
+
log13.debug("Emitting non-fatal schema validation hint and skipping malformed tool execution", {
|
|
17011
|
+
tool: normalizedToolCall.function.name,
|
|
17012
|
+
missing: compat2.validation.missing,
|
|
17013
|
+
typeErrors: compat2.validation.typeErrors
|
|
17014
|
+
});
|
|
17015
|
+
await onToolResult(hintChunk);
|
|
17016
|
+
return {
|
|
17017
|
+
intercepted: false,
|
|
17018
|
+
skipConverter: true
|
|
17019
|
+
};
|
|
17012
17020
|
}
|
|
17013
|
-
|
|
17014
|
-
|
|
17015
|
-
|
|
17016
|
-
|
|
17017
|
-
|
|
17021
|
+
if (schemaValidationFailureMode === "terminate") {
|
|
17022
|
+
return {
|
|
17023
|
+
intercepted: false,
|
|
17024
|
+
skipConverter: true,
|
|
17025
|
+
terminate: createSchemaValidationTermination(normalizedToolCall, compat2.validation)
|
|
17026
|
+
};
|
|
17018
17027
|
}
|
|
17028
|
+
log13.debug("Forwarding schema-invalid tool call to OpenCode loop", {
|
|
17029
|
+
tool: normalizedToolCall.function.name,
|
|
17030
|
+
repairHint: compat2.validation.repairHint
|
|
17031
|
+
});
|
|
17032
|
+
await onInterceptedToolCall(normalizedToolCall);
|
|
17033
|
+
return {
|
|
17034
|
+
intercepted: true,
|
|
17035
|
+
skipConverter: true
|
|
17036
|
+
};
|
|
17019
17037
|
}
|
|
17020
|
-
|
|
17021
|
-
|
|
17022
|
-
skipConverter:
|
|
17023
|
-
}
|
|
17038
|
+
const termination = evaluateToolLoopGuard(toolLoopGuard, normalizedToolCall);
|
|
17039
|
+
if (termination) {
|
|
17040
|
+
return { intercepted: false, skipConverter: true, terminate: termination };
|
|
17041
|
+
}
|
|
17042
|
+
await onInterceptedToolCall(normalizedToolCall);
|
|
17043
|
+
return { intercepted: true, skipConverter: true };
|
|
17044
|
+
return { intercepted: false, skipConverter: suppressConverterToolEvents };
|
|
17024
17045
|
}
|
|
17025
17046
|
async function handleToolLoopEventWithFallback(options) {
|
|
17026
17047
|
const {
|
|
@@ -17301,6 +17322,80 @@ var init_runtime_interception = __esm(() => {
|
|
|
17301
17322
|
};
|
|
17302
17323
|
});
|
|
17303
17324
|
|
|
17325
|
+
// src/provider/passthrough-tracker.ts
|
|
17326
|
+
class PassThroughTracker {
|
|
17327
|
+
tools = new Set;
|
|
17328
|
+
errors = [];
|
|
17329
|
+
trackTool(name) {
|
|
17330
|
+
this.tools.add(name);
|
|
17331
|
+
}
|
|
17332
|
+
trackError(toolName, message) {
|
|
17333
|
+
this.errors.push(`${toolName}: ${message}`);
|
|
17334
|
+
}
|
|
17335
|
+
getSummary() {
|
|
17336
|
+
return {
|
|
17337
|
+
tools: Array.from(this.tools),
|
|
17338
|
+
errors: [...this.errors],
|
|
17339
|
+
hasActivity: this.tools.size > 0
|
|
17340
|
+
};
|
|
17341
|
+
}
|
|
17342
|
+
reset() {
|
|
17343
|
+
this.tools.clear();
|
|
17344
|
+
this.errors.length = 0;
|
|
17345
|
+
}
|
|
17346
|
+
}
|
|
17347
|
+
|
|
17348
|
+
// src/services/toast-service.ts
|
|
17349
|
+
class ToastService {
|
|
17350
|
+
client = null;
|
|
17351
|
+
setClient(client3) {
|
|
17352
|
+
this.client = client3;
|
|
17353
|
+
}
|
|
17354
|
+
async show(options) {
|
|
17355
|
+
if (!this.client?.tui?.showToast) {
|
|
17356
|
+
log14.debug("Toast not available; client.tui.showToast missing", { message: options.message });
|
|
17357
|
+
return;
|
|
17358
|
+
}
|
|
17359
|
+
try {
|
|
17360
|
+
await this.client.tui.showToast({
|
|
17361
|
+
body: {
|
|
17362
|
+
title: options.title,
|
|
17363
|
+
message: options.message,
|
|
17364
|
+
variant: options.variant
|
|
17365
|
+
}
|
|
17366
|
+
});
|
|
17367
|
+
} catch (error45) {
|
|
17368
|
+
log14.debug("Toast failed", { error: error45, message: options.message });
|
|
17369
|
+
}
|
|
17370
|
+
}
|
|
17371
|
+
async showPassThroughSummary(tools) {
|
|
17372
|
+
if (tools.length === 0)
|
|
17373
|
+
return;
|
|
17374
|
+
const toolList = tools.length <= 3 ? tools.join(", ") : `${tools.slice(0, 3).join(", ")} +${tools.length - 3} more`;
|
|
17375
|
+
await this.show({
|
|
17376
|
+
title: "MCP Tools",
|
|
17377
|
+
message: `\uD83C\uDFAD ${tools.length} tool${tools.length > 1 ? "s" : ""} handled by cursor-agent: ${toolList}`,
|
|
17378
|
+
variant: "info"
|
|
17379
|
+
});
|
|
17380
|
+
}
|
|
17381
|
+
async showErrorSummary(errors3) {
|
|
17382
|
+
if (errors3.length === 0)
|
|
17383
|
+
return;
|
|
17384
|
+
const errorList = errors3.length <= 2 ? errors3.join("; ") : `${errors3.slice(0, 2).join("; ")} +${errors3.length - 2} more`;
|
|
17385
|
+
await this.show({
|
|
17386
|
+
title: "MCP Errors",
|
|
17387
|
+
message: `⚠️ ${errors3.length} MCP tool${errors3.length > 1 ? "s" : ""} failed: ${errorList}`,
|
|
17388
|
+
variant: "warning"
|
|
17389
|
+
});
|
|
17390
|
+
}
|
|
17391
|
+
}
|
|
17392
|
+
var log14, toastService;
|
|
17393
|
+
var init_toast_service = __esm(() => {
|
|
17394
|
+
init_logger();
|
|
17395
|
+
log14 = createLogger("services:toast");
|
|
17396
|
+
toastService = new ToastService;
|
|
17397
|
+
});
|
|
17398
|
+
|
|
17304
17399
|
// src/provider/tool-loop-guard.ts
|
|
17305
17400
|
function parseToolLoopMaxRepeat(value) {
|
|
17306
17401
|
if (value === undefined) {
|
|
@@ -17443,25 +17538,6 @@ function indexToolLoopHistory(messages) {
|
|
|
17443
17538
|
incrementCount(initialValidationCounts, `${call.name}|schema:${schemaSignature}|validation`);
|
|
17444
17539
|
incrementCount(initialValidationCoarseCounts, `${call.name}|validation`);
|
|
17445
17540
|
}
|
|
17446
|
-
const strippedRounds = countStrippedAssistantRounds(messages);
|
|
17447
|
-
if (strippedRounds > 0 && assistantCalls.length > 0) {
|
|
17448
|
-
for (const call of assistantCalls) {
|
|
17449
|
-
const errorClass = normalizeErrorClassForTool(call.name, byCallId.get(call.id) ?? latestByToolName.get(call.name) ?? latest ?? "unknown");
|
|
17450
|
-
if (errorClass !== "success") {
|
|
17451
|
-
continue;
|
|
17452
|
-
}
|
|
17453
|
-
const coarseSuccessFP = deriveSuccessCoarseFingerprint(call.name, call.rawArguments);
|
|
17454
|
-
if (coarseSuccessFP) {
|
|
17455
|
-
for (let i = 0;i < strippedRounds; i++) {
|
|
17456
|
-
incrementCount(initialCoarseCounts, coarseSuccessFP);
|
|
17457
|
-
}
|
|
17458
|
-
}
|
|
17459
|
-
const successFP = `${call.name}|values:${call.argValueSignature}|success`;
|
|
17460
|
-
for (let i = 0;i < strippedRounds; i++) {
|
|
17461
|
-
incrementCount(initialCounts, successFP);
|
|
17462
|
-
}
|
|
17463
|
-
}
|
|
17464
|
-
}
|
|
17465
17541
|
return {
|
|
17466
17542
|
byCallId,
|
|
17467
17543
|
latest,
|
|
@@ -17684,23 +17760,6 @@ function normalizeErrorClassForTool(toolName, errorClass) {
|
|
|
17684
17760
|
}
|
|
17685
17761
|
return errorClass;
|
|
17686
17762
|
}
|
|
17687
|
-
function countStrippedAssistantRounds(messages) {
|
|
17688
|
-
let count = 0;
|
|
17689
|
-
for (const message of messages) {
|
|
17690
|
-
if (!isRecord4(message) || message.role !== "assistant") {
|
|
17691
|
-
continue;
|
|
17692
|
-
}
|
|
17693
|
-
if (Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
|
|
17694
|
-
continue;
|
|
17695
|
-
}
|
|
17696
|
-
const content = message.content;
|
|
17697
|
-
const hasContent = typeof content === "string" && content.trim().length > 0 || Array.isArray(content) && content.length > 0;
|
|
17698
|
-
if (!hasContent) {
|
|
17699
|
-
count++;
|
|
17700
|
-
}
|
|
17701
|
-
}
|
|
17702
|
-
return count;
|
|
17703
|
-
}
|
|
17704
17763
|
function toLowerText(content) {
|
|
17705
17764
|
const rendered = renderContent(content);
|
|
17706
17765
|
return rendered.trim().toLowerCase();
|
|
@@ -17785,9 +17844,9 @@ async function ensurePluginDirectory() {
|
|
|
17785
17844
|
const pluginDir = join4(configHome, "opencode", "plugin");
|
|
17786
17845
|
try {
|
|
17787
17846
|
await mkdir(pluginDir, { recursive: true });
|
|
17788
|
-
|
|
17847
|
+
log15.debug("Plugin directory ensured", { path: pluginDir });
|
|
17789
17848
|
} catch (error45) {
|
|
17790
|
-
|
|
17849
|
+
log15.warn("Failed to create plugin directory", { error: String(error45) });
|
|
17791
17850
|
}
|
|
17792
17851
|
}
|
|
17793
17852
|
function shouldProcessModel(model) {
|
|
@@ -17975,9 +18034,9 @@ function createBoundaryRuntimeContext(scope) {
|
|
|
17975
18034
|
error: toErrorMessage(error45)
|
|
17976
18035
|
};
|
|
17977
18036
|
if (!fallbackActive) {
|
|
17978
|
-
|
|
18037
|
+
log15.warn("Provider boundary v1 failed; switching to legacy for this request", details);
|
|
17979
18038
|
} else {
|
|
17980
|
-
|
|
18039
|
+
log15.debug("Provider boundary fallback already active", details);
|
|
17981
18040
|
}
|
|
17982
18041
|
fallbackActive = true;
|
|
17983
18042
|
return true;
|
|
@@ -18115,7 +18174,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18115
18174
|
headers: { "Content-Type": "application/json" }
|
|
18116
18175
|
});
|
|
18117
18176
|
} catch (err) {
|
|
18118
|
-
|
|
18177
|
+
log15.error("Failed to list models", { error: String(err) });
|
|
18119
18178
|
return new Response(JSON.stringify({ error: "Failed to fetch models from cursor-agent" }), {
|
|
18120
18179
|
status: 500,
|
|
18121
18180
|
headers: { "Content-Type": "application/json" }
|
|
@@ -18128,7 +18187,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18128
18187
|
headers: { "Content-Type": "application/json" }
|
|
18129
18188
|
});
|
|
18130
18189
|
}
|
|
18131
|
-
|
|
18190
|
+
log15.debug("Proxy request (bun)", { method: req.method, path: url2.pathname });
|
|
18132
18191
|
const body = await req.json().catch(() => ({}));
|
|
18133
18192
|
const messages = Array.isArray(body?.messages) ? body.messages : [];
|
|
18134
18193
|
const stream = body?.stream === true;
|
|
@@ -18155,7 +18214,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18155
18214
|
const clen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr${m.content.length}` : typeof m?.content;
|
|
18156
18215
|
return `${i}:${role}${hasTc ? `(tc:${hasTc})` : ""}(clen:${clen})`;
|
|
18157
18216
|
});
|
|
18158
|
-
|
|
18217
|
+
log15.debug("Proxy chat request (bun)", {
|
|
18159
18218
|
stream,
|
|
18160
18219
|
model,
|
|
18161
18220
|
messages: messages.length,
|
|
@@ -18201,7 +18260,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18201
18260
|
const stdout = (stdoutText || "").trim();
|
|
18202
18261
|
const stderr = (stderrText || "").trim();
|
|
18203
18262
|
const exitCode = await child.exited;
|
|
18204
|
-
|
|
18263
|
+
log15.debug("cursor-agent completed (bun non-stream)", {
|
|
18205
18264
|
exitCode,
|
|
18206
18265
|
stdoutChars: stdout.length,
|
|
18207
18266
|
stderrChars: stderr.length
|
|
@@ -18227,7 +18286,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18227
18286
|
});
|
|
18228
18287
|
}
|
|
18229
18288
|
if (intercepted.toolCall) {
|
|
18230
|
-
|
|
18289
|
+
log15.debug("Intercepted OpenCode tool call (non-stream)", {
|
|
18231
18290
|
name: intercepted.toolCall.function.name,
|
|
18232
18291
|
callId: intercepted.toolCall.id
|
|
18233
18292
|
});
|
|
@@ -18241,7 +18300,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18241
18300
|
const errSource = stderr || stdout || `cursor-agent exited with code ${String(exitCode ?? "unknown")} and no output`;
|
|
18242
18301
|
const parsed = parseAgentError(errSource);
|
|
18243
18302
|
const userError = formatErrorForUser(parsed);
|
|
18244
|
-
|
|
18303
|
+
log15.error("cursor-cli failed", {
|
|
18245
18304
|
type: parsed.type,
|
|
18246
18305
|
message: parsed.message,
|
|
18247
18306
|
code: exitCode
|
|
@@ -18265,6 +18324,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18265
18324
|
const perf = new RequestPerf(id);
|
|
18266
18325
|
const toolMapper = new ToolMapper;
|
|
18267
18326
|
const toolSessionId = id;
|
|
18327
|
+
const passThroughTracker = new PassThroughTracker;
|
|
18268
18328
|
perf.mark("spawn");
|
|
18269
18329
|
const sse = new ReadableStream({
|
|
18270
18330
|
async start(controller) {
|
|
@@ -18275,7 +18335,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18275
18335
|
const converter = new StreamToSseConverter(model, { id, created });
|
|
18276
18336
|
const lineBuffer = new LineBuffer;
|
|
18277
18337
|
const emitToolCallAndTerminate = (toolCall) => {
|
|
18278
|
-
|
|
18338
|
+
log15.debug("Intercepted OpenCode tool call (stream)", {
|
|
18279
18339
|
name: toolCall.function.name,
|
|
18280
18340
|
callId: toolCall.id
|
|
18281
18341
|
});
|
|
@@ -18342,6 +18402,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18342
18402
|
suppressConverterToolEvents: SUPPRESS_CONVERTER_TOOL_EVENTS,
|
|
18343
18403
|
toolRouter,
|
|
18344
18404
|
responseMeta: { id, created, model },
|
|
18405
|
+
passThroughTracker,
|
|
18345
18406
|
onToolUpdate: (update) => {
|
|
18346
18407
|
controller.enqueue(encoder.encode(formatToolUpdateEvent(update)));
|
|
18347
18408
|
},
|
|
@@ -18408,6 +18469,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18408
18469
|
suppressConverterToolEvents: SUPPRESS_CONVERTER_TOOL_EVENTS,
|
|
18409
18470
|
toolRouter,
|
|
18410
18471
|
responseMeta: { id, created, model },
|
|
18472
|
+
passThroughTracker,
|
|
18411
18473
|
onToolUpdate: (update) => {
|
|
18412
18474
|
controller.enqueue(encoder.encode(formatToolUpdateEvent(update)));
|
|
18413
18475
|
},
|
|
@@ -18455,7 +18517,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18455
18517
|
const errSource = (stderrText || "").trim() || `cursor-agent exited with code ${String(exitCode ?? "unknown")} and no output`;
|
|
18456
18518
|
const parsed = parseAgentError(errSource);
|
|
18457
18519
|
const msg = formatErrorForUser(parsed);
|
|
18458
|
-
|
|
18520
|
+
log15.error("cursor-cli streaming failed", {
|
|
18459
18521
|
type: parsed.type,
|
|
18460
18522
|
code: exitCode
|
|
18461
18523
|
});
|
|
@@ -18466,9 +18528,16 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18466
18528
|
controller.enqueue(encoder.encode(formatSseDone()));
|
|
18467
18529
|
return;
|
|
18468
18530
|
}
|
|
18469
|
-
|
|
18531
|
+
log15.debug("cursor-agent completed (bun stream)", {
|
|
18470
18532
|
exitCode
|
|
18471
18533
|
});
|
|
18534
|
+
const passThroughSummary = passThroughTracker.getSummary();
|
|
18535
|
+
if (passThroughSummary.hasActivity) {
|
|
18536
|
+
await toastService.showPassThroughSummary(passThroughSummary.tools);
|
|
18537
|
+
}
|
|
18538
|
+
if (passThroughSummary.errors.length > 0) {
|
|
18539
|
+
await toastService.showErrorSummary(passThroughSummary.errors);
|
|
18540
|
+
}
|
|
18472
18541
|
const doneChunk = createChatCompletionChunk(id, created, model, "", true);
|
|
18473
18542
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify(doneChunk)}
|
|
18474
18543
|
|
|
@@ -18541,7 +18610,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18541
18610
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
18542
18611
|
res.end(JSON.stringify({ object: "list", data: models }));
|
|
18543
18612
|
} catch (err) {
|
|
18544
|
-
|
|
18613
|
+
log15.error("Failed to list models", { error: String(err) });
|
|
18545
18614
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
18546
18615
|
res.end(JSON.stringify({ error: "Failed to fetch models" }));
|
|
18547
18616
|
}
|
|
@@ -18552,7 +18621,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18552
18621
|
res.end(JSON.stringify({ error: `Unsupported path: ${url2.pathname}` }));
|
|
18553
18622
|
return;
|
|
18554
18623
|
}
|
|
18555
|
-
|
|
18624
|
+
log15.debug("Proxy request (node)", { method: req.method, path: url2.pathname });
|
|
18556
18625
|
let body = "";
|
|
18557
18626
|
for await (const chunk of req) {
|
|
18558
18627
|
body += chunk;
|
|
@@ -18575,7 +18644,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18575
18644
|
const contentLen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr${m.content.length}` : typeof m?.content;
|
|
18576
18645
|
return `${i}:${role}${hasTc ? `(tc:${hasTc})` : ""}${role === "tool" ? `(tcid:${tcId},name:${tcName},clen:${contentLen})` : `(clen:${contentLen})`}`;
|
|
18577
18646
|
});
|
|
18578
|
-
|
|
18647
|
+
log15.debug("Proxy chat request (node)", {
|
|
18579
18648
|
stream,
|
|
18580
18649
|
model,
|
|
18581
18650
|
messages: messages.length,
|
|
@@ -18606,14 +18675,14 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18606
18675
|
let spawnErrorText = null;
|
|
18607
18676
|
child.on("error", (error45) => {
|
|
18608
18677
|
spawnErrorText = String(error45?.message || error45);
|
|
18609
|
-
|
|
18678
|
+
log15.error("Failed to spawn cursor-agent", { error: spawnErrorText, model });
|
|
18610
18679
|
});
|
|
18611
18680
|
child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
18612
18681
|
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
18613
18682
|
child.on("close", async (code) => {
|
|
18614
18683
|
const stdout = Buffer.concat(stdoutChunks).toString().trim();
|
|
18615
18684
|
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
18616
|
-
|
|
18685
|
+
log15.debug("cursor-agent completed (node non-stream)", {
|
|
18617
18686
|
code,
|
|
18618
18687
|
stdoutChars: stdout.length,
|
|
18619
18688
|
stderrChars: stderr.length,
|
|
@@ -18639,7 +18708,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18639
18708
|
return;
|
|
18640
18709
|
}
|
|
18641
18710
|
if (intercepted.toolCall) {
|
|
18642
|
-
|
|
18711
|
+
log15.debug("Intercepted OpenCode tool call (non-stream)", {
|
|
18643
18712
|
name: intercepted.toolCall.function.name,
|
|
18644
18713
|
callId: intercepted.toolCall.id
|
|
18645
18714
|
});
|
|
@@ -18653,7 +18722,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18653
18722
|
const errSource = stderr || stdout || spawnErrorText || `cursor-agent exited with code ${String(code ?? "unknown")} and no output`;
|
|
18654
18723
|
const parsed = parseAgentError(errSource);
|
|
18655
18724
|
const userError = formatErrorForUser(parsed);
|
|
18656
|
-
|
|
18725
|
+
log15.error("cursor-cli failed", {
|
|
18657
18726
|
type: parsed.type,
|
|
18658
18727
|
message: parsed.message,
|
|
18659
18728
|
code
|
|
@@ -18681,6 +18750,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18681
18750
|
const lineBuffer = new LineBuffer;
|
|
18682
18751
|
const toolMapper = new ToolMapper;
|
|
18683
18752
|
const toolSessionId = id;
|
|
18753
|
+
const passThroughTracker = new PassThroughTracker;
|
|
18684
18754
|
const stderrChunks = [];
|
|
18685
18755
|
let streamTerminated = false;
|
|
18686
18756
|
let firstTokenReceived = false;
|
|
@@ -18692,7 +18762,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18692
18762
|
return;
|
|
18693
18763
|
}
|
|
18694
18764
|
const errSource = String(error45?.message || error45);
|
|
18695
|
-
|
|
18765
|
+
log15.error("Failed to spawn cursor-agent (stream)", { error: errSource, model });
|
|
18696
18766
|
const parsed = parseAgentError(errSource);
|
|
18697
18767
|
const msg = formatErrorForUser(parsed);
|
|
18698
18768
|
const errChunk = createChatCompletionChunk(id, created, model, msg, true);
|
|
@@ -18707,7 +18777,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18707
18777
|
if (streamTerminated || res.writableEnded) {
|
|
18708
18778
|
return;
|
|
18709
18779
|
}
|
|
18710
|
-
|
|
18780
|
+
log15.debug("Intercepted OpenCode tool call (stream)", {
|
|
18711
18781
|
name: toolCall.function.name,
|
|
18712
18782
|
callId: toolCall.id
|
|
18713
18783
|
});
|
|
@@ -18773,6 +18843,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18773
18843
|
suppressConverterToolEvents: SUPPRESS_CONVERTER_TOOL_EVENTS,
|
|
18774
18844
|
toolRouter,
|
|
18775
18845
|
responseMeta: { id, created, model },
|
|
18846
|
+
passThroughTracker,
|
|
18776
18847
|
onToolUpdate: (update) => {
|
|
18777
18848
|
res.write(formatToolUpdateEvent(update));
|
|
18778
18849
|
},
|
|
@@ -18843,6 +18914,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18843
18914
|
suppressConverterToolEvents: SUPPRESS_CONVERTER_TOOL_EVENTS,
|
|
18844
18915
|
toolRouter,
|
|
18845
18916
|
responseMeta: { id, created, model },
|
|
18917
|
+
passThroughTracker,
|
|
18846
18918
|
onToolUpdate: (update) => {
|
|
18847
18919
|
res.write(formatToolUpdateEvent(update));
|
|
18848
18920
|
},
|
|
@@ -18889,7 +18961,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18889
18961
|
perf.mark("request:done");
|
|
18890
18962
|
perf.summarize();
|
|
18891
18963
|
const stderrText = Buffer.concat(stderrChunks).toString().trim();
|
|
18892
|
-
|
|
18964
|
+
log15.debug("cursor-agent completed (node stream)", {
|
|
18893
18965
|
code,
|
|
18894
18966
|
stderrChars: stderrText.length
|
|
18895
18967
|
});
|
|
@@ -18906,6 +18978,13 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18906
18978
|
res.end();
|
|
18907
18979
|
return;
|
|
18908
18980
|
}
|
|
18981
|
+
const passThroughSummary = passThroughTracker.getSummary();
|
|
18982
|
+
if (passThroughSummary.hasActivity) {
|
|
18983
|
+
await toastService.showPassThroughSummary(passThroughSummary.tools);
|
|
18984
|
+
}
|
|
18985
|
+
if (passThroughSummary.errors.length > 0) {
|
|
18986
|
+
await toastService.showErrorSummary(passThroughSummary.errors);
|
|
18987
|
+
}
|
|
18909
18988
|
const doneChunk = {
|
|
18910
18989
|
id,
|
|
18911
18990
|
object: "chat.completion.chunk",
|
|
@@ -19110,7 +19189,7 @@ function buildToolHookEntries(registry2, fallbackBaseDir) {
|
|
|
19110
19189
|
const normalizedArgs = applyToolContextDefaults(toolName, args, context, fallbackBaseDir, sessionWorkspaceBySession);
|
|
19111
19190
|
return await handler(normalizedArgs);
|
|
19112
19191
|
} catch (error45) {
|
|
19113
|
-
|
|
19192
|
+
log15.debug("Tool hook execution failed", { tool: toolName, error: String(error45?.message || error45) });
|
|
19114
19193
|
throw error45;
|
|
19115
19194
|
}
|
|
19116
19195
|
}
|
|
@@ -19122,9 +19201,9 @@ function buildToolHookEntries(registry2, fallbackBaseDir) {
|
|
|
19122
19201
|
}
|
|
19123
19202
|
return entries;
|
|
19124
19203
|
}
|
|
19125
|
-
var
|
|
19204
|
+
var log15, 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 }) => {
|
|
19126
19205
|
const workspaceDirectory = resolveWorkspaceDirectory(worktree, directory);
|
|
19127
|
-
|
|
19206
|
+
log15.debug("Plugin initializing", {
|
|
19128
19207
|
directory,
|
|
19129
19208
|
worktree,
|
|
19130
19209
|
workspaceDirectory,
|
|
@@ -19132,22 +19211,22 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19132
19211
|
serverUrl: serverUrl?.toString()
|
|
19133
19212
|
});
|
|
19134
19213
|
if (!TOOL_LOOP_MODE_VALID) {
|
|
19135
|
-
|
|
19214
|
+
log15.warn("Invalid CURSOR_ACP_TOOL_LOOP_MODE; defaulting to opencode", { value: TOOL_LOOP_MODE_RAW });
|
|
19136
19215
|
}
|
|
19137
19216
|
if (!PROVIDER_BOUNDARY_MODE_VALID) {
|
|
19138
|
-
|
|
19217
|
+
log15.warn("Invalid CURSOR_ACP_PROVIDER_BOUNDARY; defaulting to v1", {
|
|
19139
19218
|
value: PROVIDER_BOUNDARY_MODE_RAW
|
|
19140
19219
|
});
|
|
19141
19220
|
}
|
|
19142
19221
|
if (!TOOL_LOOP_MAX_REPEAT_VALID) {
|
|
19143
|
-
|
|
19222
|
+
log15.warn("Invalid CURSOR_ACP_TOOL_LOOP_MAX_REPEAT; defaulting to 3", {
|
|
19144
19223
|
value: TOOL_LOOP_MAX_REPEAT_RAW
|
|
19145
19224
|
});
|
|
19146
19225
|
}
|
|
19147
19226
|
if (ENABLE_PROVIDER_BOUNDARY_AUTOFALLBACK && PROVIDER_BOUNDARY.mode !== "v1") {
|
|
19148
|
-
|
|
19227
|
+
log15.debug("Provider boundary auto-fallback is enabled but inactive unless mode=v1");
|
|
19149
19228
|
}
|
|
19150
|
-
|
|
19229
|
+
log15.info("Tool loop mode configured", {
|
|
19151
19230
|
mode: TOOL_LOOP_MODE,
|
|
19152
19231
|
providerBoundary: PROVIDER_BOUNDARY.mode,
|
|
19153
19232
|
proxyExecToolCalls: PROXY_EXECUTE_TOOL_CALLS,
|
|
@@ -19155,12 +19234,14 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19155
19234
|
toolLoopMaxRepeat: TOOL_LOOP_MAX_REPEAT
|
|
19156
19235
|
});
|
|
19157
19236
|
await ensurePluginDirectory();
|
|
19237
|
+
toastService.setClient(client3);
|
|
19238
|
+
toastService.setClient(client3);
|
|
19158
19239
|
const toolsEnabled = process.env.CURSOR_ACP_ENABLE_OPENCODE_TOOLS !== "false";
|
|
19159
19240
|
const legacyProxyToolPathsEnabled = toolsEnabled && TOOL_LOOP_MODE === "proxy-exec";
|
|
19160
19241
|
if (toolsEnabled && TOOL_LOOP_MODE === "opencode") {
|
|
19161
|
-
|
|
19242
|
+
log15.debug("OpenCode mode active; skipping legacy SDK/MCP discovery and proxy-side tool execution");
|
|
19162
19243
|
} else if (toolsEnabled && TOOL_LOOP_MODE === "off") {
|
|
19163
|
-
|
|
19244
|
+
log15.debug("Tool loop mode off; proxy-side tool execution disabled");
|
|
19164
19245
|
}
|
|
19165
19246
|
const serverClient = legacyProxyToolPathsEnabled ? createOpencodeClient({ baseUrl: serverUrl.toString(), directory: workspaceDirectory }) : null;
|
|
19166
19247
|
const discovery = legacyProxyToolPathsEnabled ? new OpenCodeToolDiscovery(serverClient ?? client3) : null;
|
|
@@ -19212,7 +19293,7 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19212
19293
|
discoveredList = await discovery.listTools();
|
|
19213
19294
|
discoveredList.forEach((t) => toolsByName.set(t.name, t));
|
|
19214
19295
|
} catch (err) {
|
|
19215
|
-
|
|
19296
|
+
log15.debug("Tool discovery failed, using local tools only", { error: String(err) });
|
|
19216
19297
|
}
|
|
19217
19298
|
}
|
|
19218
19299
|
const allTools = [...localTools, ...discoveredList];
|
|
@@ -19242,11 +19323,11 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19242
19323
|
}
|
|
19243
19324
|
lastToolNames = toolEntries.map((e) => e.function.name);
|
|
19244
19325
|
lastToolMap = allTools.map((t) => ({ id: t.id, name: t.name }));
|
|
19245
|
-
|
|
19326
|
+
log15.debug("Tools refreshed", { local: localTools.length, discovered: discoveredList.length, total: toolEntries.length });
|
|
19246
19327
|
return toolEntries;
|
|
19247
19328
|
}
|
|
19248
19329
|
const proxyBaseURL = await ensureCursorProxyServer(workspaceDirectory, router);
|
|
19249
|
-
|
|
19330
|
+
log15.debug("Proxy server started", { baseURL: proxyBaseURL });
|
|
19250
19331
|
const toolHookEntries = buildToolHookEntries(localRegistry, workspaceDirectory);
|
|
19251
19332
|
return {
|
|
19252
19333
|
tool: toolHookEntries,
|
|
@@ -19261,9 +19342,9 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19261
19342
|
type: "oauth",
|
|
19262
19343
|
async authorize() {
|
|
19263
19344
|
try {
|
|
19264
|
-
|
|
19345
|
+
log15.info("Starting OAuth flow");
|
|
19265
19346
|
const { url: url2, instructions, callback } = await startCursorOAuth();
|
|
19266
|
-
|
|
19347
|
+
log15.debug("Got OAuth URL", { url: url2.substring(0, 50) + "..." });
|
|
19267
19348
|
return {
|
|
19268
19349
|
url: url2,
|
|
19269
19350
|
instructions,
|
|
@@ -19271,7 +19352,7 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19271
19352
|
callback
|
|
19272
19353
|
};
|
|
19273
19354
|
} catch (error45) {
|
|
19274
|
-
|
|
19355
|
+
log15.error("OAuth error", { error: error45 });
|
|
19275
19356
|
throw error45;
|
|
19276
19357
|
}
|
|
19277
19358
|
}
|
|
@@ -19295,10 +19376,10 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID = "cursor-acp", C
|
|
|
19295
19376
|
output.options.tools = resolved.tools;
|
|
19296
19377
|
} else if (resolved.action === "preserve") {
|
|
19297
19378
|
const count = Array.isArray(existingTools) ? existingTools.length : 0;
|
|
19298
|
-
|
|
19379
|
+
log15.debug("Using OpenCode-provided tools from chat.params", { count });
|
|
19299
19380
|
}
|
|
19300
19381
|
} catch (err) {
|
|
19301
|
-
|
|
19382
|
+
log15.debug("Failed to refresh tools", { error: String(err) });
|
|
19302
19383
|
}
|
|
19303
19384
|
}
|
|
19304
19385
|
},
|
|
@@ -19331,9 +19412,10 @@ var init_plugin = __esm(() => {
|
|
|
19331
19412
|
init_executor();
|
|
19332
19413
|
init_boundary();
|
|
19333
19414
|
init_runtime_interception();
|
|
19415
|
+
init_toast_service();
|
|
19334
19416
|
init_tool_schema_compat();
|
|
19335
19417
|
init_tool_loop_guard();
|
|
19336
|
-
|
|
19418
|
+
log15 = createLogger("plugin");
|
|
19337
19419
|
DEBUG_LOG_DIR2 = join4(homedir4(), ".config", "opencode", "logs");
|
|
19338
19420
|
DEBUG_LOG_FILE2 = join4(DEBUG_LOG_DIR2, "tool-loop-debug.log");
|
|
19339
19421
|
CURSOR_PROVIDER_PREFIX = `${CURSOR_PROVIDER_ID}/`;
|
|
@@ -19577,7 +19659,7 @@ init_logger();
|
|
|
19577
19659
|
import { execSync } from "node:child_process";
|
|
19578
19660
|
import { createServer } from "node:net";
|
|
19579
19661
|
import { platform as platform2 } from "node:os";
|
|
19580
|
-
var
|
|
19662
|
+
var log16 = createLogger("proxy-server");
|
|
19581
19663
|
var DEFAULT_PORT = 32124;
|
|
19582
19664
|
var PORT_RANGE_SIZE = 256;
|
|
19583
19665
|
async function isPortAvailable(port, host) {
|
|
@@ -19624,11 +19706,11 @@ function getUsedPortsInRange(minPort, maxPort) {
|
|
|
19624
19706
|
}
|
|
19625
19707
|
}
|
|
19626
19708
|
} else {
|
|
19627
|
-
|
|
19709
|
+
log16.debug(`Port detection not supported on ${os2}. Using probe-based discovery.`);
|
|
19628
19710
|
}
|
|
19629
19711
|
} catch (error45) {
|
|
19630
19712
|
const msg = error45 instanceof Error ? error45.message : String(error45);
|
|
19631
|
-
|
|
19713
|
+
log16.debug(`Port detection failed: ${msg}. Using probe-based discovery.`);
|
|
19632
19714
|
}
|
|
19633
19715
|
return used;
|
|
19634
19716
|
}
|
|
@@ -19679,7 +19761,7 @@ function createProxyServer(config2) {
|
|
|
19679
19761
|
const err = error45 instanceof Error ? error45 : new Error(String(error45));
|
|
19680
19762
|
const isPortInUse = err.message.includes("EADDRINUSE") || err.message.includes("address already in use") || err.message.includes("port is already in use");
|
|
19681
19763
|
if (!isPortInUse) {
|
|
19682
|
-
|
|
19764
|
+
log16.debug(`Unexpected error starting on port ${port}: ${err.message}`);
|
|
19683
19765
|
}
|
|
19684
19766
|
return { success: false, error: err };
|
|
19685
19767
|
}
|
|
@@ -19695,13 +19777,13 @@ function createProxyServer(config2) {
|
|
|
19695
19777
|
if (result.success) {
|
|
19696
19778
|
port = requestedPort;
|
|
19697
19779
|
} else {
|
|
19698
|
-
|
|
19780
|
+
log16.debug(`Requested port ${requestedPort} unavailable: ${result.error?.message ?? "unknown"}. Falling back to automatic port selection.`);
|
|
19699
19781
|
port = await findAvailablePort(host);
|
|
19700
19782
|
const fallbackResult = tryStart(port);
|
|
19701
19783
|
if (!fallbackResult.success) {
|
|
19702
19784
|
throw new Error(`Failed to start server on port ${requestedPort} (${result.error?.message ?? "unknown"}) ` + `and fallback port ${port} (${fallbackResult.error?.message ?? "unknown"})`);
|
|
19703
19785
|
}
|
|
19704
|
-
|
|
19786
|
+
log16.debug(`Server started on fallback port ${port} instead of requested port ${requestedPort}`);
|
|
19705
19787
|
}
|
|
19706
19788
|
} else {
|
|
19707
19789
|
port = await findAvailablePort(host);
|
|
@@ -20047,11 +20129,11 @@ init_auth();
|
|
|
20047
20129
|
init_auth();
|
|
20048
20130
|
init_logger();
|
|
20049
20131
|
import { existsSync as existsSync5 } from "fs";
|
|
20050
|
-
var
|
|
20132
|
+
var log17 = createLogger("status");
|
|
20051
20133
|
function checkAuthStatus() {
|
|
20052
20134
|
const authFilePath = getAuthFilePath();
|
|
20053
20135
|
const exists = existsSync5(authFilePath);
|
|
20054
|
-
|
|
20136
|
+
log17.debug("Checking auth status", { path: authFilePath });
|
|
20055
20137
|
if (exists) {
|
|
20056
20138
|
return {
|
|
20057
20139
|
authenticated: true,
|