@rama_nigg/open-cursor 2.3.3 → 2.3.5
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 +32 -10
- package/dist/index.js +314 -216
- package/dist/plugin-entry.js +308 -210
- package/package.json +1 -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 +11 -65
- package/src/proxy/tool-loop.ts +42 -28
- package/src/services/toast-service.ts +81 -0
package/dist/plugin-entry.js
CHANGED
|
@@ -13614,41 +13614,46 @@ function extractAllowedToolNames(tools) {
|
|
|
13614
13614
|
}
|
|
13615
13615
|
function extractOpenAiToolCall(event, allowedToolNames) {
|
|
13616
13616
|
if (allowedToolNames.size === 0) {
|
|
13617
|
-
return
|
|
13617
|
+
return { action: "skip", skipReason: "no_allowed_tools" };
|
|
13618
13618
|
}
|
|
13619
13619
|
const { name, args, skipped } = extractToolNameAndArgs(event);
|
|
13620
13620
|
if (skipped) {
|
|
13621
|
-
return
|
|
13621
|
+
return { action: "skip", skipReason: "event_skipped" };
|
|
13622
13622
|
}
|
|
13623
13623
|
if (!name) {
|
|
13624
|
-
return
|
|
13624
|
+
return { action: "skip", skipReason: "no_name" };
|
|
13625
13625
|
}
|
|
13626
13626
|
const resolvedName = resolveAllowedToolName(name, allowedToolNames);
|
|
13627
|
-
if (
|
|
13628
|
-
|
|
13629
|
-
|
|
13630
|
-
|
|
13631
|
-
|
|
13632
|
-
|
|
13633
|
-
|
|
13634
|
-
|
|
13635
|
-
|
|
13636
|
-
|
|
13637
|
-
|
|
13638
|
-
|
|
13639
|
-
|
|
13640
|
-
|
|
13641
|
-
|
|
13642
|
-
|
|
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
|
+
};
|
|
13643
13648
|
}
|
|
13644
|
-
|
|
13649
|
+
log5.debug("Tool call not in allowlist; passing through to cursor-agent", {
|
|
13650
|
+
name,
|
|
13651
|
+
normalized: normalizeAliasKey(name),
|
|
13652
|
+
allowedToolCount: allowedToolNames.size
|
|
13653
|
+
});
|
|
13645
13654
|
return {
|
|
13646
|
-
|
|
13647
|
-
|
|
13648
|
-
function: {
|
|
13649
|
-
name: resolvedName,
|
|
13650
|
-
arguments: toOpenAiArguments(args)
|
|
13651
|
-
}
|
|
13655
|
+
action: "passthrough",
|
|
13656
|
+
passthroughName: name
|
|
13652
13657
|
};
|
|
13653
13658
|
}
|
|
13654
13659
|
function createToolCallCompletionResponse(meta, toolCall) {
|
|
@@ -16388,7 +16393,7 @@ function createSharedBoundary(providerId) {
|
|
|
16388
16393
|
},
|
|
16389
16394
|
maybeExtractToolCall(event, allowedToolNames, toolLoopMode) {
|
|
16390
16395
|
if (toolLoopMode !== "opencode") {
|
|
16391
|
-
return
|
|
16396
|
+
return { action: "skip", skipReason: "tool_loop_mode_not_opencode" };
|
|
16392
16397
|
}
|
|
16393
16398
|
return extractOpenAiToolCall(event, allowedToolNames);
|
|
16394
16399
|
},
|
|
@@ -16815,9 +16820,33 @@ async function handleToolLoopEventLegacy(options) {
|
|
|
16815
16820
|
responseMeta,
|
|
16816
16821
|
onToolUpdate,
|
|
16817
16822
|
onToolResult,
|
|
16818
|
-
onInterceptedToolCall
|
|
16823
|
+
onInterceptedToolCall,
|
|
16824
|
+
passThroughTracker
|
|
16819
16825
|
} = options;
|
|
16820
|
-
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;
|
|
16821
16850
|
if (interceptedToolCall) {
|
|
16822
16851
|
const compat2 = applyToolSchemaCompat(interceptedToolCall, toolSchemaMap);
|
|
16823
16852
|
let normalizedToolCall = compat2.toolCall;
|
|
@@ -16894,118 +16923,125 @@ async function handleToolLoopEventV1(options) {
|
|
|
16894
16923
|
responseMeta,
|
|
16895
16924
|
onToolUpdate,
|
|
16896
16925
|
onToolResult,
|
|
16897
|
-
onInterceptedToolCall
|
|
16926
|
+
onInterceptedToolCall,
|
|
16927
|
+
passThroughTracker
|
|
16898
16928
|
} = options;
|
|
16899
|
-
let
|
|
16929
|
+
let extraction;
|
|
16900
16930
|
try {
|
|
16901
|
-
|
|
16931
|
+
extraction = boundary.maybeExtractToolCall(event, allowedToolNames, toolLoopMode);
|
|
16902
16932
|
} catch (error45) {
|
|
16903
16933
|
throw new ToolBoundaryExtractionError("Boundary tool extraction failed", error45);
|
|
16904
16934
|
}
|
|
16905
|
-
if (
|
|
16906
|
-
|
|
16907
|
-
|
|
16908
|
-
|
|
16909
|
-
rawArgs: safeArgTypeSummary(event),
|
|
16910
|
-
normalizedArgs: compat2.normalizedArgs
|
|
16911
|
-
} : undefined;
|
|
16912
|
-
log13.debug("Applied tool schema compatibility", {
|
|
16913
|
-
tool: interceptedToolCall.function.name,
|
|
16914
|
-
originalArgKeys: compat2.originalArgKeys,
|
|
16915
|
-
normalizedArgKeys: compat2.normalizedArgKeys,
|
|
16916
|
-
collisionKeys: compat2.collisionKeys,
|
|
16917
|
-
validationOk: compat2.validation.ok,
|
|
16918
|
-
...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
|
|
16919
16939
|
});
|
|
16920
|
-
|
|
16921
|
-
|
|
16922
|
-
|
|
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,
|
|
16923
16992
|
missing: compat2.validation.missing,
|
|
16924
|
-
|
|
16925
|
-
typeErrors: compat2.validation.typeErrors,
|
|
16926
|
-
repairHint: compat2.validation.repairHint
|
|
16993
|
+
typeErrors: compat2.validation.typeErrors
|
|
16927
16994
|
});
|
|
16928
|
-
|
|
16929
|
-
if (validationTermination) {
|
|
16930
|
-
return { intercepted: false, skipConverter: true, terminate: validationTermination };
|
|
16931
|
-
}
|
|
16932
|
-
const termination2 = evaluateToolLoopGuard(toolLoopGuard, interceptedToolCall);
|
|
16933
|
-
if (termination2) {
|
|
16934
|
-
return { intercepted: false, skipConverter: true, terminate: termination2 };
|
|
16935
|
-
}
|
|
16936
|
-
const reroutedWrite = tryRerouteEditToWrite(interceptedToolCall, compat2.normalizedArgs, allowedToolNames, toolSchemaMap);
|
|
16937
|
-
if (reroutedWrite) {
|
|
16938
|
-
log13.debug("Rerouting malformed edit call to write", {
|
|
16939
|
-
path: reroutedWrite.path,
|
|
16940
|
-
missing: compat2.validation.missing,
|
|
16941
|
-
typeErrors: compat2.validation.typeErrors
|
|
16942
|
-
});
|
|
16943
|
-
await onInterceptedToolCall(reroutedWrite.toolCall);
|
|
16944
|
-
return {
|
|
16945
|
-
intercepted: true,
|
|
16946
|
-
skipConverter: true
|
|
16947
|
-
};
|
|
16948
|
-
}
|
|
16949
|
-
if (schemaValidationFailureMode === "pass_through" && shouldTerminateOnSchemaValidation(interceptedToolCall, compat2.validation)) {
|
|
16950
|
-
return {
|
|
16951
|
-
intercepted: false,
|
|
16952
|
-
skipConverter: true,
|
|
16953
|
-
terminate: createSchemaValidationTermination(interceptedToolCall, compat2.validation)
|
|
16954
|
-
};
|
|
16955
|
-
}
|
|
16956
|
-
if (schemaValidationFailureMode === "pass_through" && shouldEmitNonFatalSchemaValidationHint(interceptedToolCall, compat2.validation)) {
|
|
16957
|
-
const hintChunk = createNonFatalSchemaValidationHintChunk(responseMeta, interceptedToolCall, compat2.validation);
|
|
16958
|
-
log13.debug("Emitting non-fatal schema validation hint and skipping malformed tool execution", {
|
|
16959
|
-
tool: interceptedToolCall.function.name,
|
|
16960
|
-
missing: compat2.validation.missing,
|
|
16961
|
-
typeErrors: compat2.validation.typeErrors
|
|
16962
|
-
});
|
|
16963
|
-
await onToolResult(hintChunk);
|
|
16964
|
-
return {
|
|
16965
|
-
intercepted: false,
|
|
16966
|
-
skipConverter: true
|
|
16967
|
-
};
|
|
16968
|
-
}
|
|
16969
|
-
if (schemaValidationFailureMode === "terminate") {
|
|
16970
|
-
return {
|
|
16971
|
-
intercepted: false,
|
|
16972
|
-
skipConverter: true,
|
|
16973
|
-
terminate: createSchemaValidationTermination(interceptedToolCall, compat2.validation)
|
|
16974
|
-
};
|
|
16975
|
-
}
|
|
16976
|
-
log13.debug("Forwarding schema-invalid tool call to OpenCode loop", {
|
|
16977
|
-
tool: interceptedToolCall.function.name,
|
|
16978
|
-
repairHint: compat2.validation.repairHint
|
|
16979
|
-
});
|
|
16980
|
-
await onInterceptedToolCall(interceptedToolCall);
|
|
16995
|
+
await onInterceptedToolCall(reroutedWrite.toolCall);
|
|
16981
16996
|
return {
|
|
16982
16997
|
intercepted: true,
|
|
16983
16998
|
skipConverter: true
|
|
16984
16999
|
};
|
|
16985
17000
|
}
|
|
16986
|
-
|
|
16987
|
-
|
|
16988
|
-
|
|
17001
|
+
if (schemaValidationFailureMode === "pass_through" && shouldTerminateOnSchemaValidation(normalizedToolCall, compat2.validation)) {
|
|
17002
|
+
return {
|
|
17003
|
+
intercepted: false,
|
|
17004
|
+
skipConverter: true,
|
|
17005
|
+
terminate: createSchemaValidationTermination(normalizedToolCall, compat2.validation)
|
|
17006
|
+
};
|
|
16989
17007
|
}
|
|
16990
|
-
|
|
16991
|
-
|
|
16992
|
-
|
|
16993
|
-
|
|
16994
|
-
|
|
16995
|
-
|
|
16996
|
-
|
|
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
|
+
};
|
|
16997
17020
|
}
|
|
16998
|
-
|
|
16999
|
-
|
|
17000
|
-
|
|
17001
|
-
|
|
17002
|
-
|
|
17021
|
+
if (schemaValidationFailureMode === "terminate") {
|
|
17022
|
+
return {
|
|
17023
|
+
intercepted: false,
|
|
17024
|
+
skipConverter: true,
|
|
17025
|
+
terminate: createSchemaValidationTermination(normalizedToolCall, compat2.validation)
|
|
17026
|
+
};
|
|
17003
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
|
+
};
|
|
17004
17037
|
}
|
|
17005
|
-
|
|
17006
|
-
|
|
17007
|
-
skipConverter:
|
|
17008
|
-
}
|
|
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 };
|
|
17009
17045
|
}
|
|
17010
17046
|
async function handleToolLoopEventWithFallback(options) {
|
|
17011
17047
|
const {
|
|
@@ -17286,6 +17322,80 @@ var init_runtime_interception = __esm(() => {
|
|
|
17286
17322
|
};
|
|
17287
17323
|
});
|
|
17288
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
|
+
|
|
17289
17399
|
// src/provider/tool-loop-guard.ts
|
|
17290
17400
|
function parseToolLoopMaxRepeat(value) {
|
|
17291
17401
|
if (value === undefined) {
|
|
@@ -17298,6 +17408,7 @@ function parseToolLoopMaxRepeat(value) {
|
|
|
17298
17408
|
return { value: Math.floor(parsed), valid: true };
|
|
17299
17409
|
}
|
|
17300
17410
|
function createToolLoopGuard(messages, maxRepeat) {
|
|
17411
|
+
const coarseMaxRepeat = maxRepeat * COARSE_LIMIT_MULTIPLIER;
|
|
17301
17412
|
const {
|
|
17302
17413
|
byCallId,
|
|
17303
17414
|
latest,
|
|
@@ -17337,13 +17448,13 @@ function createToolLoopGuard(messages, maxRepeat) {
|
|
|
17337
17448
|
}
|
|
17338
17449
|
const strictFingerprint = `${toolCall.function.name}|${argShape}|${errorClass}`;
|
|
17339
17450
|
const coarseFingerprint = `${toolCall.function.name}|${errorClass}`;
|
|
17340
|
-
return evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, counts, coarseCounts, maxRepeat);
|
|
17451
|
+
return evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, counts, coarseCounts, maxRepeat, coarseMaxRepeat);
|
|
17341
17452
|
},
|
|
17342
17453
|
evaluateValidation(toolCall, validationSignature) {
|
|
17343
17454
|
const normalizedSignature = normalizeValidationSignature(validationSignature);
|
|
17344
17455
|
const strictFingerprint = `${toolCall.function.name}|schema:${normalizedSignature}|validation`;
|
|
17345
17456
|
const coarseFingerprint = `${toolCall.function.name}|validation`;
|
|
17346
|
-
return evaluateWithFingerprints("validation", strictFingerprint, coarseFingerprint, validationCounts, validationCoarseCounts, maxRepeat);
|
|
17457
|
+
return evaluateWithFingerprints("validation", strictFingerprint, coarseFingerprint, validationCounts, validationCoarseCounts, maxRepeat, coarseMaxRepeat);
|
|
17347
17458
|
},
|
|
17348
17459
|
resetFingerprint(fingerprint) {
|
|
17349
17460
|
counts.delete(fingerprint);
|
|
@@ -17428,25 +17539,6 @@ function indexToolLoopHistory(messages) {
|
|
|
17428
17539
|
incrementCount(initialValidationCounts, `${call.name}|schema:${schemaSignature}|validation`);
|
|
17429
17540
|
incrementCount(initialValidationCoarseCounts, `${call.name}|validation`);
|
|
17430
17541
|
}
|
|
17431
|
-
const strippedRounds = countStrippedAssistantRounds(messages);
|
|
17432
|
-
if (strippedRounds > 0 && assistantCalls.length > 0) {
|
|
17433
|
-
for (const call of assistantCalls) {
|
|
17434
|
-
const errorClass = normalizeErrorClassForTool(call.name, byCallId.get(call.id) ?? latestByToolName.get(call.name) ?? latest ?? "unknown");
|
|
17435
|
-
if (errorClass !== "success") {
|
|
17436
|
-
continue;
|
|
17437
|
-
}
|
|
17438
|
-
const coarseSuccessFP = deriveSuccessCoarseFingerprint(call.name, call.rawArguments);
|
|
17439
|
-
if (coarseSuccessFP) {
|
|
17440
|
-
for (let i = 0;i < strippedRounds; i++) {
|
|
17441
|
-
incrementCount(initialCoarseCounts, coarseSuccessFP);
|
|
17442
|
-
}
|
|
17443
|
-
}
|
|
17444
|
-
const successFP = `${call.name}|values:${call.argValueSignature}|success`;
|
|
17445
|
-
for (let i = 0;i < strippedRounds; i++) {
|
|
17446
|
-
incrementCount(initialCounts, successFP);
|
|
17447
|
-
}
|
|
17448
|
-
}
|
|
17449
|
-
}
|
|
17450
17542
|
return {
|
|
17451
17543
|
byCallId,
|
|
17452
17544
|
latest,
|
|
@@ -17593,7 +17685,7 @@ function normalizeValidationSignature(signature) {
|
|
|
17593
17685
|
const normalized = signature.trim().toLowerCase();
|
|
17594
17686
|
return normalized.length > 0 ? normalized : "invalid";
|
|
17595
17687
|
}
|
|
17596
|
-
function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, strictCounts, coarseCounts, maxRepeat) {
|
|
17688
|
+
function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, strictCounts, coarseCounts, maxRepeat, coarseMaxRepeat) {
|
|
17597
17689
|
if (errorClass === "success") {
|
|
17598
17690
|
return {
|
|
17599
17691
|
fingerprint: strictFingerprint,
|
|
@@ -17609,12 +17701,12 @@ function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerpri
|
|
|
17609
17701
|
const coarseRepeatCount = (coarseCounts.get(coarseFingerprint) ?? 0) + 1;
|
|
17610
17702
|
coarseCounts.set(coarseFingerprint, coarseRepeatCount);
|
|
17611
17703
|
const strictTriggered = strictRepeatCount > maxRepeat;
|
|
17612
|
-
const coarseTriggered = coarseRepeatCount >
|
|
17704
|
+
const coarseTriggered = coarseRepeatCount > coarseMaxRepeat;
|
|
17613
17705
|
const preferCoarseFingerprint = coarseTriggered && !strictTriggered;
|
|
17614
17706
|
return {
|
|
17615
17707
|
fingerprint: preferCoarseFingerprint ? coarseFingerprint : strictFingerprint,
|
|
17616
17708
|
repeatCount: preferCoarseFingerprint ? coarseRepeatCount : strictRepeatCount,
|
|
17617
|
-
maxRepeat,
|
|
17709
|
+
maxRepeat: preferCoarseFingerprint ? coarseMaxRepeat : maxRepeat,
|
|
17618
17710
|
errorClass,
|
|
17619
17711
|
triggered: strictTriggered || coarseTriggered,
|
|
17620
17712
|
tracked: true
|
|
@@ -17669,23 +17761,6 @@ function normalizeErrorClassForTool(toolName, errorClass) {
|
|
|
17669
17761
|
}
|
|
17670
17762
|
return errorClass;
|
|
17671
17763
|
}
|
|
17672
|
-
function countStrippedAssistantRounds(messages) {
|
|
17673
|
-
let count = 0;
|
|
17674
|
-
for (const message of messages) {
|
|
17675
|
-
if (!isRecord4(message) || message.role !== "assistant") {
|
|
17676
|
-
continue;
|
|
17677
|
-
}
|
|
17678
|
-
if (Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
|
|
17679
|
-
continue;
|
|
17680
|
-
}
|
|
17681
|
-
const content = message.content;
|
|
17682
|
-
const hasContent = typeof content === "string" && content.trim().length > 0 || Array.isArray(content) && content.length > 0;
|
|
17683
|
-
if (!hasContent) {
|
|
17684
|
-
count++;
|
|
17685
|
-
}
|
|
17686
|
-
}
|
|
17687
|
-
return count;
|
|
17688
|
-
}
|
|
17689
17764
|
function toLowerText(content) {
|
|
17690
17765
|
const rendered = renderContent(content);
|
|
17691
17766
|
return rendered.trim().toLowerCase();
|
|
@@ -17716,7 +17791,7 @@ function containsAny(text, patterns) {
|
|
|
17716
17791
|
function isRecord4(value) {
|
|
17717
17792
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17718
17793
|
}
|
|
17719
|
-
var UNKNOWN_AS_SUCCESS_TOOLS;
|
|
17794
|
+
var UNKNOWN_AS_SUCCESS_TOOLS, COARSE_LIMIT_MULTIPLIER = 3;
|
|
17720
17795
|
var init_tool_loop_guard = __esm(() => {
|
|
17721
17796
|
UNKNOWN_AS_SUCCESS_TOOLS = new Set([
|
|
17722
17797
|
"bash",
|
|
@@ -17770,9 +17845,9 @@ async function ensurePluginDirectory() {
|
|
|
17770
17845
|
const pluginDir = join5(configHome, "opencode", "plugin");
|
|
17771
17846
|
try {
|
|
17772
17847
|
await mkdir(pluginDir, { recursive: true });
|
|
17773
|
-
|
|
17848
|
+
log15.debug("Plugin directory ensured", { path: pluginDir });
|
|
17774
17849
|
} catch (error45) {
|
|
17775
|
-
|
|
17850
|
+
log15.warn("Failed to create plugin directory", { error: String(error45) });
|
|
17776
17851
|
}
|
|
17777
17852
|
}
|
|
17778
17853
|
function shouldProcessModel(model) {
|
|
@@ -17960,9 +18035,9 @@ function createBoundaryRuntimeContext(scope) {
|
|
|
17960
18035
|
error: toErrorMessage(error45)
|
|
17961
18036
|
};
|
|
17962
18037
|
if (!fallbackActive) {
|
|
17963
|
-
|
|
18038
|
+
log15.warn("Provider boundary v1 failed; switching to legacy for this request", details);
|
|
17964
18039
|
} else {
|
|
17965
|
-
|
|
18040
|
+
log15.debug("Provider boundary fallback already active", details);
|
|
17966
18041
|
}
|
|
17967
18042
|
fallbackActive = true;
|
|
17968
18043
|
return true;
|
|
@@ -18100,7 +18175,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18100
18175
|
headers: { "Content-Type": "application/json" }
|
|
18101
18176
|
});
|
|
18102
18177
|
} catch (err) {
|
|
18103
|
-
|
|
18178
|
+
log15.error("Failed to list models", { error: String(err) });
|
|
18104
18179
|
return new Response(JSON.stringify({ error: "Failed to fetch models from cursor-agent" }), {
|
|
18105
18180
|
status: 500,
|
|
18106
18181
|
headers: { "Content-Type": "application/json" }
|
|
@@ -18113,7 +18188,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18113
18188
|
headers: { "Content-Type": "application/json" }
|
|
18114
18189
|
});
|
|
18115
18190
|
}
|
|
18116
|
-
|
|
18191
|
+
log15.debug("Proxy request (bun)", { method: req.method, path: url2.pathname });
|
|
18117
18192
|
const body = await req.json().catch(() => ({}));
|
|
18118
18193
|
const messages = Array.isArray(body?.messages) ? body.messages : [];
|
|
18119
18194
|
const stream = body?.stream === true;
|
|
@@ -18140,7 +18215,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18140
18215
|
const clen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr${m.content.length}` : typeof m?.content;
|
|
18141
18216
|
return `${i}:${role}${hasTc ? `(tc:${hasTc})` : ""}(clen:${clen})`;
|
|
18142
18217
|
});
|
|
18143
|
-
|
|
18218
|
+
log15.debug("Proxy chat request (bun)", {
|
|
18144
18219
|
stream,
|
|
18145
18220
|
model,
|
|
18146
18221
|
messages: messages.length,
|
|
@@ -18186,7 +18261,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18186
18261
|
const stdout = (stdoutText || "").trim();
|
|
18187
18262
|
const stderr = (stderrText || "").trim();
|
|
18188
18263
|
const exitCode = await child.exited;
|
|
18189
|
-
|
|
18264
|
+
log15.debug("cursor-agent completed (bun non-stream)", {
|
|
18190
18265
|
exitCode,
|
|
18191
18266
|
stdoutChars: stdout.length,
|
|
18192
18267
|
stderrChars: stderr.length
|
|
@@ -18212,7 +18287,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18212
18287
|
});
|
|
18213
18288
|
}
|
|
18214
18289
|
if (intercepted.toolCall) {
|
|
18215
|
-
|
|
18290
|
+
log15.debug("Intercepted OpenCode tool call (non-stream)", {
|
|
18216
18291
|
name: intercepted.toolCall.function.name,
|
|
18217
18292
|
callId: intercepted.toolCall.id
|
|
18218
18293
|
});
|
|
@@ -18226,7 +18301,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18226
18301
|
const errSource = stderr || stdout || `cursor-agent exited with code ${String(exitCode ?? "unknown")} and no output`;
|
|
18227
18302
|
const parsed = parseAgentError(errSource);
|
|
18228
18303
|
const userError = formatErrorForUser(parsed);
|
|
18229
|
-
|
|
18304
|
+
log15.error("cursor-cli failed", {
|
|
18230
18305
|
type: parsed.type,
|
|
18231
18306
|
message: parsed.message,
|
|
18232
18307
|
code: exitCode
|
|
@@ -18250,6 +18325,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18250
18325
|
const perf = new RequestPerf(id);
|
|
18251
18326
|
const toolMapper = new ToolMapper;
|
|
18252
18327
|
const toolSessionId = id;
|
|
18328
|
+
const passThroughTracker = new PassThroughTracker;
|
|
18253
18329
|
perf.mark("spawn");
|
|
18254
18330
|
const sse = new ReadableStream({
|
|
18255
18331
|
async start(controller) {
|
|
@@ -18260,7 +18336,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18260
18336
|
const converter = new StreamToSseConverter(model, { id, created });
|
|
18261
18337
|
const lineBuffer = new LineBuffer;
|
|
18262
18338
|
const emitToolCallAndTerminate = (toolCall) => {
|
|
18263
|
-
|
|
18339
|
+
log15.debug("Intercepted OpenCode tool call (stream)", {
|
|
18264
18340
|
name: toolCall.function.name,
|
|
18265
18341
|
callId: toolCall.id
|
|
18266
18342
|
});
|
|
@@ -18327,6 +18403,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18327
18403
|
suppressConverterToolEvents: SUPPRESS_CONVERTER_TOOL_EVENTS,
|
|
18328
18404
|
toolRouter,
|
|
18329
18405
|
responseMeta: { id, created, model },
|
|
18406
|
+
passThroughTracker,
|
|
18330
18407
|
onToolUpdate: (update) => {
|
|
18331
18408
|
controller.enqueue(encoder.encode(formatToolUpdateEvent(update)));
|
|
18332
18409
|
},
|
|
@@ -18393,6 +18470,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18393
18470
|
suppressConverterToolEvents: SUPPRESS_CONVERTER_TOOL_EVENTS,
|
|
18394
18471
|
toolRouter,
|
|
18395
18472
|
responseMeta: { id, created, model },
|
|
18473
|
+
passThroughTracker,
|
|
18396
18474
|
onToolUpdate: (update) => {
|
|
18397
18475
|
controller.enqueue(encoder.encode(formatToolUpdateEvent(update)));
|
|
18398
18476
|
},
|
|
@@ -18440,7 +18518,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18440
18518
|
const errSource = (stderrText || "").trim() || `cursor-agent exited with code ${String(exitCode ?? "unknown")} and no output`;
|
|
18441
18519
|
const parsed = parseAgentError(errSource);
|
|
18442
18520
|
const msg = formatErrorForUser(parsed);
|
|
18443
|
-
|
|
18521
|
+
log15.error("cursor-cli streaming failed", {
|
|
18444
18522
|
type: parsed.type,
|
|
18445
18523
|
code: exitCode
|
|
18446
18524
|
});
|
|
@@ -18451,9 +18529,16 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18451
18529
|
controller.enqueue(encoder.encode(formatSseDone()));
|
|
18452
18530
|
return;
|
|
18453
18531
|
}
|
|
18454
|
-
|
|
18532
|
+
log15.debug("cursor-agent completed (bun stream)", {
|
|
18455
18533
|
exitCode
|
|
18456
18534
|
});
|
|
18535
|
+
const passThroughSummary = passThroughTracker.getSummary();
|
|
18536
|
+
if (passThroughSummary.hasActivity) {
|
|
18537
|
+
await toastService.showPassThroughSummary(passThroughSummary.tools);
|
|
18538
|
+
}
|
|
18539
|
+
if (passThroughSummary.errors.length > 0) {
|
|
18540
|
+
await toastService.showErrorSummary(passThroughSummary.errors);
|
|
18541
|
+
}
|
|
18457
18542
|
const doneChunk = createChatCompletionChunk(id, created, model, "", true);
|
|
18458
18543
|
controller.enqueue(encoder.encode(`data: ${JSON.stringify(doneChunk)}
|
|
18459
18544
|
|
|
@@ -18526,7 +18611,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18526
18611
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
18527
18612
|
res.end(JSON.stringify({ object: "list", data: models }));
|
|
18528
18613
|
} catch (err) {
|
|
18529
|
-
|
|
18614
|
+
log15.error("Failed to list models", { error: String(err) });
|
|
18530
18615
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
18531
18616
|
res.end(JSON.stringify({ error: "Failed to fetch models" }));
|
|
18532
18617
|
}
|
|
@@ -18537,7 +18622,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18537
18622
|
res.end(JSON.stringify({ error: `Unsupported path: ${url2.pathname}` }));
|
|
18538
18623
|
return;
|
|
18539
18624
|
}
|
|
18540
|
-
|
|
18625
|
+
log15.debug("Proxy request (node)", { method: req.method, path: url2.pathname });
|
|
18541
18626
|
let body = "";
|
|
18542
18627
|
for await (const chunk of req) {
|
|
18543
18628
|
body += chunk;
|
|
@@ -18560,7 +18645,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18560
18645
|
const contentLen = typeof m?.content === "string" ? m.content.length : Array.isArray(m?.content) ? `arr${m.content.length}` : typeof m?.content;
|
|
18561
18646
|
return `${i}:${role}${hasTc ? `(tc:${hasTc})` : ""}${role === "tool" ? `(tcid:${tcId},name:${tcName},clen:${contentLen})` : `(clen:${contentLen})`}`;
|
|
18562
18647
|
});
|
|
18563
|
-
|
|
18648
|
+
log15.debug("Proxy chat request (node)", {
|
|
18564
18649
|
stream,
|
|
18565
18650
|
model,
|
|
18566
18651
|
messages: messages.length,
|
|
@@ -18591,14 +18676,14 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18591
18676
|
let spawnErrorText = null;
|
|
18592
18677
|
child.on("error", (error45) => {
|
|
18593
18678
|
spawnErrorText = String(error45?.message || error45);
|
|
18594
|
-
|
|
18679
|
+
log15.error("Failed to spawn cursor-agent", { error: spawnErrorText, model });
|
|
18595
18680
|
});
|
|
18596
18681
|
child.stdout.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
18597
18682
|
child.stderr.on("data", (chunk) => stderrChunks.push(chunk));
|
|
18598
18683
|
child.on("close", async (code) => {
|
|
18599
18684
|
const stdout = Buffer.concat(stdoutChunks).toString().trim();
|
|
18600
18685
|
const stderr = Buffer.concat(stderrChunks).toString().trim();
|
|
18601
|
-
|
|
18686
|
+
log15.debug("cursor-agent completed (node non-stream)", {
|
|
18602
18687
|
code,
|
|
18603
18688
|
stdoutChars: stdout.length,
|
|
18604
18689
|
stderrChars: stderr.length,
|
|
@@ -18624,7 +18709,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18624
18709
|
return;
|
|
18625
18710
|
}
|
|
18626
18711
|
if (intercepted.toolCall) {
|
|
18627
|
-
|
|
18712
|
+
log15.debug("Intercepted OpenCode tool call (non-stream)", {
|
|
18628
18713
|
name: intercepted.toolCall.function.name,
|
|
18629
18714
|
callId: intercepted.toolCall.id
|
|
18630
18715
|
});
|
|
@@ -18638,7 +18723,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18638
18723
|
const errSource = stderr || stdout || spawnErrorText || `cursor-agent exited with code ${String(code ?? "unknown")} and no output`;
|
|
18639
18724
|
const parsed = parseAgentError(errSource);
|
|
18640
18725
|
const userError = formatErrorForUser(parsed);
|
|
18641
|
-
|
|
18726
|
+
log15.error("cursor-cli failed", {
|
|
18642
18727
|
type: parsed.type,
|
|
18643
18728
|
message: parsed.message,
|
|
18644
18729
|
code
|
|
@@ -18666,6 +18751,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18666
18751
|
const lineBuffer = new LineBuffer;
|
|
18667
18752
|
const toolMapper = new ToolMapper;
|
|
18668
18753
|
const toolSessionId = id;
|
|
18754
|
+
const passThroughTracker = new PassThroughTracker;
|
|
18669
18755
|
const stderrChunks = [];
|
|
18670
18756
|
let streamTerminated = false;
|
|
18671
18757
|
let firstTokenReceived = false;
|
|
@@ -18677,7 +18763,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18677
18763
|
return;
|
|
18678
18764
|
}
|
|
18679
18765
|
const errSource = String(error45?.message || error45);
|
|
18680
|
-
|
|
18766
|
+
log15.error("Failed to spawn cursor-agent (stream)", { error: errSource, model });
|
|
18681
18767
|
const parsed = parseAgentError(errSource);
|
|
18682
18768
|
const msg = formatErrorForUser(parsed);
|
|
18683
18769
|
const errChunk = createChatCompletionChunk(id, created, model, msg, true);
|
|
@@ -18692,7 +18778,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18692
18778
|
if (streamTerminated || res.writableEnded) {
|
|
18693
18779
|
return;
|
|
18694
18780
|
}
|
|
18695
|
-
|
|
18781
|
+
log15.debug("Intercepted OpenCode tool call (stream)", {
|
|
18696
18782
|
name: toolCall.function.name,
|
|
18697
18783
|
callId: toolCall.id
|
|
18698
18784
|
});
|
|
@@ -18758,6 +18844,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18758
18844
|
suppressConverterToolEvents: SUPPRESS_CONVERTER_TOOL_EVENTS,
|
|
18759
18845
|
toolRouter,
|
|
18760
18846
|
responseMeta: { id, created, model },
|
|
18847
|
+
passThroughTracker,
|
|
18761
18848
|
onToolUpdate: (update) => {
|
|
18762
18849
|
res.write(formatToolUpdateEvent(update));
|
|
18763
18850
|
},
|
|
@@ -18828,6 +18915,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18828
18915
|
suppressConverterToolEvents: SUPPRESS_CONVERTER_TOOL_EVENTS,
|
|
18829
18916
|
toolRouter,
|
|
18830
18917
|
responseMeta: { id, created, model },
|
|
18918
|
+
passThroughTracker,
|
|
18831
18919
|
onToolUpdate: (update) => {
|
|
18832
18920
|
res.write(formatToolUpdateEvent(update));
|
|
18833
18921
|
},
|
|
@@ -18874,7 +18962,7 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18874
18962
|
perf.mark("request:done");
|
|
18875
18963
|
perf.summarize();
|
|
18876
18964
|
const stderrText = Buffer.concat(stderrChunks).toString().trim();
|
|
18877
|
-
|
|
18965
|
+
log15.debug("cursor-agent completed (node stream)", {
|
|
18878
18966
|
code,
|
|
18879
18967
|
stderrChars: stderrText.length
|
|
18880
18968
|
});
|
|
@@ -18891,6 +18979,13 @@ async function ensureCursorProxyServer(workspaceDirectory, toolRouter) {
|
|
|
18891
18979
|
res.end();
|
|
18892
18980
|
return;
|
|
18893
18981
|
}
|
|
18982
|
+
const passThroughSummary = passThroughTracker.getSummary();
|
|
18983
|
+
if (passThroughSummary.hasActivity) {
|
|
18984
|
+
await toastService.showPassThroughSummary(passThroughSummary.tools);
|
|
18985
|
+
}
|
|
18986
|
+
if (passThroughSummary.errors.length > 0) {
|
|
18987
|
+
await toastService.showErrorSummary(passThroughSummary.errors);
|
|
18988
|
+
}
|
|
18894
18989
|
const doneChunk = {
|
|
18895
18990
|
id,
|
|
18896
18991
|
object: "chat.completion.chunk",
|
|
@@ -19095,7 +19190,7 @@ function buildToolHookEntries(registry2, fallbackBaseDir) {
|
|
|
19095
19190
|
const normalizedArgs = applyToolContextDefaults(toolName, args, context, fallbackBaseDir, sessionWorkspaceBySession);
|
|
19096
19191
|
return await handler(normalizedArgs);
|
|
19097
19192
|
} catch (error45) {
|
|
19098
|
-
|
|
19193
|
+
log15.debug("Tool hook execution failed", { tool: toolName, error: String(error45?.message || error45) });
|
|
19099
19194
|
throw error45;
|
|
19100
19195
|
}
|
|
19101
19196
|
}
|
|
@@ -19107,9 +19202,9 @@ function buildToolHookEntries(registry2, fallbackBaseDir) {
|
|
|
19107
19202
|
}
|
|
19108
19203
|
return entries;
|
|
19109
19204
|
}
|
|
19110
|
-
var
|
|
19205
|
+
var log15, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp", CURSOR_PROVIDER_PREFIX, CURSOR_PROXY_HOST = "127.0.0.1", CURSOR_PROXY_DEFAULT_PORT = 32124, CURSOR_PROXY_DEFAULT_BASE_URL, REUSE_EXISTING_PROXY, SESSION_WORKSPACE_CACHE_LIMIT = 200, FORCE_TOOL_MODE, EMIT_TOOL_UPDATES, FORWARD_TOOL_CALLS, TOOL_LOOP_MODE_RAW, TOOL_LOOP_MODE, TOOL_LOOP_MODE_VALID, PROVIDER_BOUNDARY_MODE_RAW, PROVIDER_BOUNDARY_MODE, PROVIDER_BOUNDARY_MODE_VALID, LEGACY_PROVIDER_BOUNDARY, PROVIDER_BOUNDARY, ENABLE_PROVIDER_BOUNDARY_AUTOFALLBACK, TOOL_LOOP_MAX_REPEAT_RAW, TOOL_LOOP_MAX_REPEAT, TOOL_LOOP_MAX_REPEAT_VALID, PROXY_EXECUTE_TOOL_CALLS, SUPPRESS_CONVERTER_TOOL_EVENTS, SHOULD_EMIT_TOOL_UPDATES, CursorPlugin = async ({ $, directory, worktree, client: client3, serverUrl }) => {
|
|
19111
19206
|
const workspaceDirectory = resolveWorkspaceDirectory(worktree, directory);
|
|
19112
|
-
|
|
19207
|
+
log15.debug("Plugin initializing", {
|
|
19113
19208
|
directory,
|
|
19114
19209
|
worktree,
|
|
19115
19210
|
workspaceDirectory,
|
|
@@ -19117,22 +19212,22 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
|
|
|
19117
19212
|
serverUrl: serverUrl?.toString()
|
|
19118
19213
|
});
|
|
19119
19214
|
if (!TOOL_LOOP_MODE_VALID) {
|
|
19120
|
-
|
|
19215
|
+
log15.warn("Invalid CURSOR_ACP_TOOL_LOOP_MODE; defaulting to opencode", { value: TOOL_LOOP_MODE_RAW });
|
|
19121
19216
|
}
|
|
19122
19217
|
if (!PROVIDER_BOUNDARY_MODE_VALID) {
|
|
19123
|
-
|
|
19218
|
+
log15.warn("Invalid CURSOR_ACP_PROVIDER_BOUNDARY; defaulting to v1", {
|
|
19124
19219
|
value: PROVIDER_BOUNDARY_MODE_RAW
|
|
19125
19220
|
});
|
|
19126
19221
|
}
|
|
19127
19222
|
if (!TOOL_LOOP_MAX_REPEAT_VALID) {
|
|
19128
|
-
|
|
19223
|
+
log15.warn("Invalid CURSOR_ACP_TOOL_LOOP_MAX_REPEAT; defaulting to 3", {
|
|
19129
19224
|
value: TOOL_LOOP_MAX_REPEAT_RAW
|
|
19130
19225
|
});
|
|
19131
19226
|
}
|
|
19132
19227
|
if (ENABLE_PROVIDER_BOUNDARY_AUTOFALLBACK && PROVIDER_BOUNDARY.mode !== "v1") {
|
|
19133
|
-
|
|
19228
|
+
log15.debug("Provider boundary auto-fallback is enabled but inactive unless mode=v1");
|
|
19134
19229
|
}
|
|
19135
|
-
|
|
19230
|
+
log15.info("Tool loop mode configured", {
|
|
19136
19231
|
mode: TOOL_LOOP_MODE,
|
|
19137
19232
|
providerBoundary: PROVIDER_BOUNDARY.mode,
|
|
19138
19233
|
proxyExecToolCalls: PROXY_EXECUTE_TOOL_CALLS,
|
|
@@ -19140,12 +19235,14 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
|
|
|
19140
19235
|
toolLoopMaxRepeat: TOOL_LOOP_MAX_REPEAT
|
|
19141
19236
|
});
|
|
19142
19237
|
await ensurePluginDirectory();
|
|
19238
|
+
toastService.setClient(client3);
|
|
19239
|
+
toastService.setClient(client3);
|
|
19143
19240
|
const toolsEnabled = process.env.CURSOR_ACP_ENABLE_OPENCODE_TOOLS !== "false";
|
|
19144
19241
|
const legacyProxyToolPathsEnabled = toolsEnabled && TOOL_LOOP_MODE === "proxy-exec";
|
|
19145
19242
|
if (toolsEnabled && TOOL_LOOP_MODE === "opencode") {
|
|
19146
|
-
|
|
19243
|
+
log15.debug("OpenCode mode active; skipping legacy SDK/MCP discovery and proxy-side tool execution");
|
|
19147
19244
|
} else if (toolsEnabled && TOOL_LOOP_MODE === "off") {
|
|
19148
|
-
|
|
19245
|
+
log15.debug("Tool loop mode off; proxy-side tool execution disabled");
|
|
19149
19246
|
}
|
|
19150
19247
|
const serverClient = legacyProxyToolPathsEnabled ? createOpencodeClient({ baseUrl: serverUrl.toString(), directory: workspaceDirectory }) : null;
|
|
19151
19248
|
const discovery = legacyProxyToolPathsEnabled ? new OpenCodeToolDiscovery(serverClient ?? client3) : null;
|
|
@@ -19197,7 +19294,7 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
|
|
|
19197
19294
|
discoveredList = await discovery.listTools();
|
|
19198
19295
|
discoveredList.forEach((t) => toolsByName.set(t.name, t));
|
|
19199
19296
|
} catch (err) {
|
|
19200
|
-
|
|
19297
|
+
log15.debug("Tool discovery failed, using local tools only", { error: String(err) });
|
|
19201
19298
|
}
|
|
19202
19299
|
}
|
|
19203
19300
|
const allTools = [...localTools, ...discoveredList];
|
|
@@ -19227,11 +19324,11 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
|
|
|
19227
19324
|
}
|
|
19228
19325
|
lastToolNames = toolEntries.map((e) => e.function.name);
|
|
19229
19326
|
lastToolMap = allTools.map((t) => ({ id: t.id, name: t.name }));
|
|
19230
|
-
|
|
19327
|
+
log15.debug("Tools refreshed", { local: localTools.length, discovered: discoveredList.length, total: toolEntries.length });
|
|
19231
19328
|
return toolEntries;
|
|
19232
19329
|
}
|
|
19233
19330
|
const proxyBaseURL = await ensureCursorProxyServer(workspaceDirectory, router);
|
|
19234
|
-
|
|
19331
|
+
log15.debug("Proxy server started", { baseURL: proxyBaseURL });
|
|
19235
19332
|
const toolHookEntries = buildToolHookEntries(localRegistry, workspaceDirectory);
|
|
19236
19333
|
return {
|
|
19237
19334
|
tool: toolHookEntries,
|
|
@@ -19246,9 +19343,9 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
|
|
|
19246
19343
|
type: "oauth",
|
|
19247
19344
|
async authorize() {
|
|
19248
19345
|
try {
|
|
19249
|
-
|
|
19346
|
+
log15.info("Starting OAuth flow");
|
|
19250
19347
|
const { url: url2, instructions, callback } = await startCursorOAuth();
|
|
19251
|
-
|
|
19348
|
+
log15.debug("Got OAuth URL", { url: url2.substring(0, 50) + "..." });
|
|
19252
19349
|
return {
|
|
19253
19350
|
url: url2,
|
|
19254
19351
|
instructions,
|
|
@@ -19256,7 +19353,7 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
|
|
|
19256
19353
|
callback
|
|
19257
19354
|
};
|
|
19258
19355
|
} catch (error45) {
|
|
19259
|
-
|
|
19356
|
+
log15.error("OAuth error", { error: error45 });
|
|
19260
19357
|
throw error45;
|
|
19261
19358
|
}
|
|
19262
19359
|
}
|
|
@@ -19280,10 +19377,10 @@ var log14, DEBUG_LOG_DIR2, DEBUG_LOG_FILE2, CURSOR_PROVIDER_ID2 = "cursor-acp",
|
|
|
19280
19377
|
output.options.tools = resolved.tools;
|
|
19281
19378
|
} else if (resolved.action === "preserve") {
|
|
19282
19379
|
const count = Array.isArray(existingTools) ? existingTools.length : 0;
|
|
19283
|
-
|
|
19380
|
+
log15.debug("Using OpenCode-provided tools from chat.params", { count });
|
|
19284
19381
|
}
|
|
19285
19382
|
} catch (err) {
|
|
19286
|
-
|
|
19383
|
+
log15.debug("Failed to refresh tools", { error: String(err) });
|
|
19287
19384
|
}
|
|
19288
19385
|
}
|
|
19289
19386
|
},
|
|
@@ -19316,9 +19413,10 @@ var init_plugin = __esm(() => {
|
|
|
19316
19413
|
init_executor();
|
|
19317
19414
|
init_boundary();
|
|
19318
19415
|
init_runtime_interception();
|
|
19416
|
+
init_toast_service();
|
|
19319
19417
|
init_tool_schema_compat();
|
|
19320
19418
|
init_tool_loop_guard();
|
|
19321
|
-
|
|
19419
|
+
log15 = createLogger("plugin");
|
|
19322
19420
|
DEBUG_LOG_DIR2 = join5(homedir5(), ".config", "opencode", "logs");
|
|
19323
19421
|
DEBUG_LOG_FILE2 = join5(DEBUG_LOG_DIR2, "tool-loop-debug.log");
|
|
19324
19422
|
CURSOR_PROVIDER_PREFIX = `${CURSOR_PROVIDER_ID2}/`;
|
|
@@ -19411,11 +19509,11 @@ function shouldEnableCursorPlugin(env = process.env) {
|
|
|
19411
19509
|
|
|
19412
19510
|
// src/plugin-entry.ts
|
|
19413
19511
|
init_logger();
|
|
19414
|
-
var
|
|
19512
|
+
var log16 = createLogger("plugin-entry");
|
|
19415
19513
|
var CursorPluginEntry = async (input) => {
|
|
19416
19514
|
const state = shouldEnableCursorPlugin();
|
|
19417
19515
|
if (!state.enabled) {
|
|
19418
|
-
|
|
19516
|
+
log16.info("Plugin disabled in OpenCode config; skipping initialization", {
|
|
19419
19517
|
configPath: state.configPath,
|
|
19420
19518
|
reason: state.reason
|
|
19421
19519
|
});
|