Skip to content

Commit a0312ad

Browse files
committed
fix: add bash/shell to EXPLORATION_TOOLS for proper fingerprinting
Fixes #40 - tool loop guard was incorrectly triggering on different shell commands because they shared the same coarse fingerprint (schema shape). Now bash/shell use strict fingerprinting (argument values) and get the 5x exploration multiplier.
1 parent 0d071ce commit a0312ad

4 files changed

Lines changed: 28 additions & 8 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@rama_nigg/open-cursor",
3-
"version": "2.3.8",
3+
"version": "2.3.9",
44
"description": "No prompt limits. No broken streams. Full thinking + tool support. Your Cursor subscription, properly integrated.",
55
"type": "module",
66
"main": "dist/plugin-entry.js",

src/provider/tool-loop-guard.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ const EXPLORATION_TOOLS = new Set([
4141
"ls",
4242
"stat",
4343
"semsearch",
44+
"bash",
45+
"shell",
4446
]);
4547

4648
export interface ToolLoopGuardDecision {

tests/unit/provider-tool-loop-guard.test.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ describe("tool loop guard", () => {
206206
});
207207

208208
it("treats unknown bash output as success for loop tracking", () => {
209+
// bash is in EXPLORATION_TOOLS with 5x multiplier, so maxRepeat=1 => effective limit=5
209210
const guard = createToolLoopGuard(
210211
[
211212
{
@@ -225,18 +226,35 @@ describe("tool loop guard", () => {
225226
arguments: JSON.stringify({ command: "printf bash-ok" }),
226227
},
227228
});
228-
const second = guard.evaluate({
229+
230+
// bash is in UNKNOWN_AS_SUCCESS_TOOLS, so "bash-ok" (unknown) becomes "success"
231+
expect(first.errorClass).toBe("success");
232+
expect(first.triggered).toBe(false);
233+
234+
// With 5x exploration multiplier and maxRepeat=1, effective limit is 5
235+
// Calls 2-5 should NOT trigger
236+
for (let i = 2; i <= 5; i++) {
237+
const decision = guard.evaluate({
238+
id: "bash-1",
239+
type: "function",
240+
function: {
241+
name: "bash",
242+
arguments: JSON.stringify({ command: "printf bash-ok" }),
243+
},
244+
});
245+
expect(decision.triggered).toBe(false);
246+
}
247+
248+
// 6th call should trigger
249+
const sixth = guard.evaluate({
229250
id: "bash-1",
230251
type: "function",
231252
function: {
232253
name: "bash",
233254
arguments: JSON.stringify({ command: "printf bash-ok" }),
234255
},
235256
});
236-
237-
expect(first.errorClass).toBe("success");
238-
expect(first.triggered).toBe(false);
239-
expect(second.triggered).toBe(true);
257+
expect(sixth.triggered).toBe(true);
240258
});
241259

242260
it("seeds success-loop history across requests for identical successful calls", () => {

0 commit comments

Comments
 (0)