@rama_nigg/open-cursor 2.3.5 → 2.3.8

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 CHANGED
@@ -27,6 +27,9 @@ Add to `~/.config/opencode/opencode.json`:
27
27
  "cursor-acp": {
28
28
  "name": "Cursor ACP",
29
29
  "npm": "@ai-sdk/openai-compatible",
30
+ "options": {
31
+ "baseURL": "http://127.0.0.1:32124/v1"
32
+ },
30
33
  "models": {
31
34
  "cursor-acp/auto": { "name": "Auto" },
32
35
  "cursor-acp/composer-1.5": { "name": "Composer 1.5" },
package/dist/index.js CHANGED
@@ -17431,30 +17431,32 @@ function createToolLoopGuard(messages, maxRepeat) {
17431
17431
  const successFingerprint = `${toolCall.function.name}|values:${valueSignature}|success`;
17432
17432
  const repeatCount = (counts.get(successFingerprint) ?? 0) + 1;
17433
17433
  counts.set(successFingerprint, repeatCount);
17434
+ const isExplorationTool = EXPLORATION_TOOLS.has(toolCall.function.name.toLowerCase());
17435
+ const effectiveMaxRepeat = isExplorationTool ? maxRepeat * EXPLORATION_LIMIT_MULTIPLIER : maxRepeat;
17434
17436
  const coarseSuccessFingerprint = deriveSuccessCoarseFingerprint(toolCall.function.name, toolCall.function.arguments);
17435
17437
  const coarseRepeatCount = coarseSuccessFingerprint ? (coarseCounts.get(coarseSuccessFingerprint) ?? 0) + 1 : 0;
17436
17438
  if (coarseSuccessFingerprint) {
17437
17439
  coarseCounts.set(coarseSuccessFingerprint, coarseRepeatCount);
17438
17440
  }
17439
- const coarseTriggered = coarseSuccessFingerprint ? coarseRepeatCount > maxRepeat : false;
17441
+ const coarseTriggered = coarseSuccessFingerprint ? coarseRepeatCount > effectiveMaxRepeat : false;
17440
17442
  return {
17441
17443
  fingerprint: coarseTriggered ? coarseSuccessFingerprint : successFingerprint,
17442
17444
  repeatCount: coarseTriggered ? coarseRepeatCount : repeatCount,
17443
- maxRepeat,
17445
+ maxRepeat: effectiveMaxRepeat,
17444
17446
  errorClass,
17445
- triggered: repeatCount > maxRepeat || coarseTriggered,
17447
+ triggered: repeatCount > effectiveMaxRepeat || coarseTriggered,
17446
17448
  tracked: true
17447
17449
  };
17448
17450
  }
17449
17451
  const strictFingerprint = `${toolCall.function.name}|${argShape}|${errorClass}`;
17450
17452
  const coarseFingerprint = `${toolCall.function.name}|${errorClass}`;
17451
- return evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, counts, coarseCounts, maxRepeat, coarseMaxRepeat);
17453
+ return evaluateWithFingerprints(toolCall.function.name, errorClass, strictFingerprint, coarseFingerprint, counts, coarseCounts, maxRepeat, coarseMaxRepeat);
17452
17454
  },
17453
17455
  evaluateValidation(toolCall, validationSignature) {
17454
17456
  const normalizedSignature = normalizeValidationSignature(validationSignature);
17455
17457
  const strictFingerprint = `${toolCall.function.name}|schema:${normalizedSignature}|validation`;
17456
17458
  const coarseFingerprint = `${toolCall.function.name}|validation`;
17457
- return evaluateWithFingerprints("validation", strictFingerprint, coarseFingerprint, validationCounts, validationCoarseCounts, maxRepeat, coarseMaxRepeat);
17459
+ return evaluateWithFingerprints(toolCall.function.name, "validation", strictFingerprint, coarseFingerprint, validationCounts, validationCoarseCounts, maxRepeat, coarseMaxRepeat);
17458
17460
  },
17459
17461
  resetFingerprint(fingerprint) {
17460
17462
  counts.delete(fingerprint);
@@ -17685,7 +17687,7 @@ function normalizeValidationSignature(signature) {
17685
17687
  const normalized = signature.trim().toLowerCase();
17686
17688
  return normalized.length > 0 ? normalized : "invalid";
17687
17689
  }
17688
- function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, strictCounts, coarseCounts, maxRepeat, coarseMaxRepeat) {
17690
+ function evaluateWithFingerprints(toolName, errorClass, strictFingerprint, coarseFingerprint, strictCounts, coarseCounts, maxRepeat, coarseMaxRepeat) {
17689
17691
  if (errorClass === "success") {
17690
17692
  return {
17691
17693
  fingerprint: strictFingerprint,
@@ -17698,9 +17700,20 @@ function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerpri
17698
17700
  }
17699
17701
  const strictRepeatCount = (strictCounts.get(strictFingerprint) ?? 0) + 1;
17700
17702
  strictCounts.set(strictFingerprint, strictRepeatCount);
17703
+ const strictTriggered = strictRepeatCount > maxRepeat;
17704
+ const isExplorationTool = EXPLORATION_TOOLS.has(toolName.toLowerCase());
17705
+ if (isExplorationTool) {
17706
+ return {
17707
+ fingerprint: strictFingerprint,
17708
+ repeatCount: strictRepeatCount,
17709
+ maxRepeat,
17710
+ errorClass,
17711
+ triggered: strictTriggered,
17712
+ tracked: true
17713
+ };
17714
+ }
17701
17715
  const coarseRepeatCount = (coarseCounts.get(coarseFingerprint) ?? 0) + 1;
17702
17716
  coarseCounts.set(coarseFingerprint, coarseRepeatCount);
17703
- const strictTriggered = strictRepeatCount > maxRepeat;
17704
17717
  const coarseTriggered = coarseRepeatCount > coarseMaxRepeat;
17705
17718
  const preferCoarseFingerprint = coarseTriggered && !strictTriggered;
17706
17719
  return {
@@ -17791,7 +17804,7 @@ function containsAny(text, patterns) {
17791
17804
  function isRecord4(value) {
17792
17805
  return typeof value === "object" && value !== null && !Array.isArray(value);
17793
17806
  }
17794
- var UNKNOWN_AS_SUCCESS_TOOLS, COARSE_LIMIT_MULTIPLIER = 3;
17807
+ var UNKNOWN_AS_SUCCESS_TOOLS, EXPLORATION_TOOLS, COARSE_LIMIT_MULTIPLIER = 3, EXPLORATION_LIMIT_MULTIPLIER = 5;
17795
17808
  var init_tool_loop_guard = __esm(() => {
17796
17809
  UNKNOWN_AS_SUCCESS_TOOLS = new Set([
17797
17810
  "bash",
@@ -17803,9 +17816,19 @@ var init_tool_loop_guard = __esm(() => {
17803
17816
  "ls",
17804
17817
  "glob",
17805
17818
  "stat",
17806
- "webfetch",
17807
17819
  "mkdir",
17808
- "rm"
17820
+ "rm",
17821
+ "webfetch",
17822
+ "semsearch",
17823
+ "readlints"
17824
+ ]);
17825
+ EXPLORATION_TOOLS = new Set([
17826
+ "read",
17827
+ "grep",
17828
+ "glob",
17829
+ "ls",
17830
+ "stat",
17831
+ "semsearch"
17809
17832
  ]);
17810
17833
  });
17811
17834
 
@@ -17431,30 +17431,32 @@ function createToolLoopGuard(messages, maxRepeat) {
17431
17431
  const successFingerprint = `${toolCall.function.name}|values:${valueSignature}|success`;
17432
17432
  const repeatCount = (counts.get(successFingerprint) ?? 0) + 1;
17433
17433
  counts.set(successFingerprint, repeatCount);
17434
+ const isExplorationTool = EXPLORATION_TOOLS.has(toolCall.function.name.toLowerCase());
17435
+ const effectiveMaxRepeat = isExplorationTool ? maxRepeat * EXPLORATION_LIMIT_MULTIPLIER : maxRepeat;
17434
17436
  const coarseSuccessFingerprint = deriveSuccessCoarseFingerprint(toolCall.function.name, toolCall.function.arguments);
17435
17437
  const coarseRepeatCount = coarseSuccessFingerprint ? (coarseCounts.get(coarseSuccessFingerprint) ?? 0) + 1 : 0;
17436
17438
  if (coarseSuccessFingerprint) {
17437
17439
  coarseCounts.set(coarseSuccessFingerprint, coarseRepeatCount);
17438
17440
  }
17439
- const coarseTriggered = coarseSuccessFingerprint ? coarseRepeatCount > maxRepeat : false;
17441
+ const coarseTriggered = coarseSuccessFingerprint ? coarseRepeatCount > effectiveMaxRepeat : false;
17440
17442
  return {
17441
17443
  fingerprint: coarseTriggered ? coarseSuccessFingerprint : successFingerprint,
17442
17444
  repeatCount: coarseTriggered ? coarseRepeatCount : repeatCount,
17443
- maxRepeat,
17445
+ maxRepeat: effectiveMaxRepeat,
17444
17446
  errorClass,
17445
- triggered: repeatCount > maxRepeat || coarseTriggered,
17447
+ triggered: repeatCount > effectiveMaxRepeat || coarseTriggered,
17446
17448
  tracked: true
17447
17449
  };
17448
17450
  }
17449
17451
  const strictFingerprint = `${toolCall.function.name}|${argShape}|${errorClass}`;
17450
17452
  const coarseFingerprint = `${toolCall.function.name}|${errorClass}`;
17451
- return evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, counts, coarseCounts, maxRepeat, coarseMaxRepeat);
17453
+ return evaluateWithFingerprints(toolCall.function.name, errorClass, strictFingerprint, coarseFingerprint, counts, coarseCounts, maxRepeat, coarseMaxRepeat);
17452
17454
  },
17453
17455
  evaluateValidation(toolCall, validationSignature) {
17454
17456
  const normalizedSignature = normalizeValidationSignature(validationSignature);
17455
17457
  const strictFingerprint = `${toolCall.function.name}|schema:${normalizedSignature}|validation`;
17456
17458
  const coarseFingerprint = `${toolCall.function.name}|validation`;
17457
- return evaluateWithFingerprints("validation", strictFingerprint, coarseFingerprint, validationCounts, validationCoarseCounts, maxRepeat, coarseMaxRepeat);
17459
+ return evaluateWithFingerprints(toolCall.function.name, "validation", strictFingerprint, coarseFingerprint, validationCounts, validationCoarseCounts, maxRepeat, coarseMaxRepeat);
17458
17460
  },
17459
17461
  resetFingerprint(fingerprint) {
17460
17462
  counts.delete(fingerprint);
@@ -17685,7 +17687,7 @@ function normalizeValidationSignature(signature) {
17685
17687
  const normalized = signature.trim().toLowerCase();
17686
17688
  return normalized.length > 0 ? normalized : "invalid";
17687
17689
  }
17688
- function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerprint, strictCounts, coarseCounts, maxRepeat, coarseMaxRepeat) {
17690
+ function evaluateWithFingerprints(toolName, errorClass, strictFingerprint, coarseFingerprint, strictCounts, coarseCounts, maxRepeat, coarseMaxRepeat) {
17689
17691
  if (errorClass === "success") {
17690
17692
  return {
17691
17693
  fingerprint: strictFingerprint,
@@ -17698,9 +17700,20 @@ function evaluateWithFingerprints(errorClass, strictFingerprint, coarseFingerpri
17698
17700
  }
17699
17701
  const strictRepeatCount = (strictCounts.get(strictFingerprint) ?? 0) + 1;
17700
17702
  strictCounts.set(strictFingerprint, strictRepeatCount);
17703
+ const strictTriggered = strictRepeatCount > maxRepeat;
17704
+ const isExplorationTool = EXPLORATION_TOOLS.has(toolName.toLowerCase());
17705
+ if (isExplorationTool) {
17706
+ return {
17707
+ fingerprint: strictFingerprint,
17708
+ repeatCount: strictRepeatCount,
17709
+ maxRepeat,
17710
+ errorClass,
17711
+ triggered: strictTriggered,
17712
+ tracked: true
17713
+ };
17714
+ }
17701
17715
  const coarseRepeatCount = (coarseCounts.get(coarseFingerprint) ?? 0) + 1;
17702
17716
  coarseCounts.set(coarseFingerprint, coarseRepeatCount);
17703
- const strictTriggered = strictRepeatCount > maxRepeat;
17704
17717
  const coarseTriggered = coarseRepeatCount > coarseMaxRepeat;
17705
17718
  const preferCoarseFingerprint = coarseTriggered && !strictTriggered;
17706
17719
  return {
@@ -17791,7 +17804,7 @@ function containsAny(text, patterns) {
17791
17804
  function isRecord4(value) {
17792
17805
  return typeof value === "object" && value !== null && !Array.isArray(value);
17793
17806
  }
17794
- var UNKNOWN_AS_SUCCESS_TOOLS, COARSE_LIMIT_MULTIPLIER = 3;
17807
+ var UNKNOWN_AS_SUCCESS_TOOLS, EXPLORATION_TOOLS, COARSE_LIMIT_MULTIPLIER = 3, EXPLORATION_LIMIT_MULTIPLIER = 5;
17795
17808
  var init_tool_loop_guard = __esm(() => {
17796
17809
  UNKNOWN_AS_SUCCESS_TOOLS = new Set([
17797
17810
  "bash",
@@ -17803,9 +17816,19 @@ var init_tool_loop_guard = __esm(() => {
17803
17816
  "ls",
17804
17817
  "glob",
17805
17818
  "stat",
17806
- "webfetch",
17807
17819
  "mkdir",
17808
- "rm"
17820
+ "rm",
17821
+ "webfetch",
17822
+ "semsearch",
17823
+ "readlints"
17824
+ ]);
17825
+ EXPLORATION_TOOLS = new Set([
17826
+ "read",
17827
+ "grep",
17828
+ "glob",
17829
+ "ls",
17830
+ "stat",
17831
+ "semsearch"
17809
17832
  ]);
17810
17833
  });
17811
17834
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rama_nigg/open-cursor",
3
- "version": "2.3.5",
3
+ "version": "2.3.8",
4
4
  "description": "No prompt limits. No broken streams. Full thinking + tool support. Your Cursor subscription, properly integrated.",
5
5
  "type": "module",
6
6
  "main": "dist/plugin-entry.js",
@@ -10,6 +10,7 @@ type ToolLoopErrorClass =
10
10
  | "unknown";
11
11
 
12
12
  const UNKNOWN_AS_SUCCESS_TOOLS = new Set([
13
+ // Core filesystem tools
13
14
  "bash",
14
15
  "shell",
15
16
  "read",
@@ -19,9 +20,27 @@ const UNKNOWN_AS_SUCCESS_TOOLS = new Set([
19
20
  "ls",
20
21
  "glob",
21
22
  "stat",
22
- "webfetch",
23
23
  "mkdir",
24
24
  "rm",
25
+ // Web/network tools
26
+ "webfetch",
27
+ // cursor-agent specific tools (passthrough, but should not trigger loop guard)
28
+ // Discovered via tests/experiments/ harness - see docs/cursor-agent-tools.md
29
+ "semsearch", // semantic code search
30
+ "readlints", // lint/diagnostic reader
31
+ ]);
32
+
33
+ // Exploratory tools that commonly iterate over many files/patterns.
34
+ // These are exempt from COARSE fingerprint tracking (tool|errorClass) to allow
35
+ // legitimate multi-file exploration. Strict fingerprints (tool|args|errorClass)
36
+ // still apply to catch identical repeated failures.
37
+ const EXPLORATION_TOOLS = new Set([
38
+ "read",
39
+ "grep",
40
+ "glob",
41
+ "ls",
42
+ "stat",
43
+ "semsearch",
25
44
  ]);
26
45
 
27
46
  export interface ToolLoopGuardDecision {
@@ -56,6 +75,7 @@ export function parseToolLoopMaxRepeat(
56
75
  // to allow legitimate exploration across different files/targets while still
57
76
  // catching spray-and-pray patterns.
58
77
  const COARSE_LIMIT_MULTIPLIER = 3;
78
+ const EXPLORATION_LIMIT_MULTIPLIER = 5;
59
79
 
60
80
  export function createToolLoopGuard(
61
81
  messages: Array<unknown>,
@@ -94,6 +114,16 @@ export function createToolLoopGuard(
94
114
  const repeatCount = (counts.get(successFingerprint) ?? 0) + 1;
95
115
  counts.set(successFingerprint, repeatCount);
96
116
 
117
+ // Exploration tools (read, grep, glob, etc.) get a higher limit because
118
+ // re-reading the same file across turns is legitimate behavior (verifying
119
+ // edits, checking state, etc.). Use 5x multiplier for these tools.
120
+ const isExplorationTool = EXPLORATION_TOOLS.has(
121
+ toolCall.function.name.toLowerCase(),
122
+ );
123
+ const effectiveMaxRepeat = isExplorationTool
124
+ ? maxRepeat * EXPLORATION_LIMIT_MULTIPLIER
125
+ : maxRepeat;
126
+
97
127
  // Some tools (notably edit/write) can get stuck in "successful" loops where
98
128
  // the model keeps re-issuing the same operation with slightly different
99
129
  // content (e.g. trailing newline differences). Track a coarse signature for
@@ -110,14 +140,14 @@ export function createToolLoopGuard(
110
140
  coarseCounts.set(coarseSuccessFingerprint, coarseRepeatCount);
111
141
  }
112
142
  const coarseTriggered = coarseSuccessFingerprint
113
- ? coarseRepeatCount > maxRepeat
143
+ ? coarseRepeatCount > effectiveMaxRepeat
114
144
  : false;
115
145
  return {
116
146
  fingerprint: coarseTriggered ? coarseSuccessFingerprint! : successFingerprint,
117
147
  repeatCount: coarseTriggered ? coarseRepeatCount : repeatCount,
118
- maxRepeat,
148
+ maxRepeat: effectiveMaxRepeat,
119
149
  errorClass,
120
- triggered: repeatCount > maxRepeat || coarseTriggered,
150
+ triggered: repeatCount > effectiveMaxRepeat || coarseTriggered,
121
151
  tracked: true,
122
152
  };
123
153
  }
@@ -125,6 +155,7 @@ export function createToolLoopGuard(
125
155
  const coarseFingerprint = `${toolCall.function.name}|${errorClass}`;
126
156
 
127
157
  return evaluateWithFingerprints(
158
+ toolCall.function.name,
128
159
  errorClass,
129
160
  strictFingerprint,
130
161
  coarseFingerprint,
@@ -140,6 +171,7 @@ export function createToolLoopGuard(
140
171
  const strictFingerprint = `${toolCall.function.name}|schema:${normalizedSignature}|validation`;
141
172
  const coarseFingerprint = `${toolCall.function.name}|validation`;
142
173
  return evaluateWithFingerprints(
174
+ toolCall.function.name,
143
175
  "validation",
144
176
  strictFingerprint,
145
177
  coarseFingerprint,
@@ -456,6 +488,7 @@ function normalizeValidationSignature(signature: string): string {
456
488
  }
457
489
 
458
490
  function evaluateWithFingerprints(
491
+ toolName: string,
459
492
  errorClass: ToolLoopErrorClass,
460
493
  strictFingerprint: string,
461
494
  coarseFingerprint: string,
@@ -477,9 +510,22 @@ function evaluateWithFingerprints(
477
510
 
478
511
  const strictRepeatCount = (strictCounts.get(strictFingerprint) ?? 0) + 1;
479
512
  strictCounts.set(strictFingerprint, strictRepeatCount);
513
+ const strictTriggered = strictRepeatCount > maxRepeat;
514
+
515
+ const isExplorationTool = EXPLORATION_TOOLS.has(toolName.toLowerCase());
516
+ if (isExplorationTool) {
517
+ return {
518
+ fingerprint: strictFingerprint,
519
+ repeatCount: strictRepeatCount,
520
+ maxRepeat,
521
+ errorClass,
522
+ triggered: strictTriggered,
523
+ tracked: true,
524
+ };
525
+ }
526
+
480
527
  const coarseRepeatCount = (coarseCounts.get(coarseFingerprint) ?? 0) + 1;
481
528
  coarseCounts.set(coarseFingerprint, coarseRepeatCount);
482
- const strictTriggered = strictRepeatCount > maxRepeat;
483
529
  const coarseTriggered = coarseRepeatCount > coarseMaxRepeat;
484
530
  const preferCoarseFingerprint = coarseTriggered && !strictTriggered;
485
531
  return {