diff --git a/vibn-agent-runner/dist/agent-session-runner.js b/vibn-agent-runner/dist/agent-session-runner.js index 8b91ae15..b291e186 100644 --- a/vibn-agent-runner/dist/agent-session-runner.js +++ b/vibn-agent-runner/dist/agent-session-runner.js @@ -300,21 +300,30 @@ File: "${path.relative(opts.repoRoot ?? ctx.workspaceRoot, task.filePath)}" (lin const toolFingerprints = []; let ralphIteration = 0; function fingerprintToolCall(tc) { - if (tc.name === "shell_exec") { - const cmd = String(tc.args?.command ?? "").trim(); - const verb = cmd - .split("&&") - .map((s) => s.trim()) - .find((s) => !s.startsWith("cd ")) - ?.split(/\s+/)[0] ?? "shell"; - return `shell_exec:${verb}`; + const name = tc.name; + const args = tc.args ?? {}; + if (name === "shell_exec") { + const cmd = String(args.command ?? "").trim(); + const firstWord = cmd.split(/\s+/)[0] ?? "shell"; + return `shell_exec:${firstWord}`; } - if (tc.name === "fs_write" || - tc.name === "fs_edit" || - tc.name === "fs_read") { - return `${tc.name}:${tc.args?.path}`; + // Determine target based on most common descriptive parameter keys + const target = args.path ?? + args.pattern ?? + args.command ?? + args.commandId ?? + args.appUuid ?? + args.uuid ?? + ""; + if (target) { + return `${name}:${target}`; } - return `${tc.name}:${Object.values(tc.args ?? {})[0]}`; + // Filter out common metadata like projectId, and use first real argument + const keys = Object.keys(args).filter((k) => k !== "projectId"); + if (keys.length > 0) { + return `${name}:${args[keys[0]]}`; + } + return `${name}:default`; } while (subTurn < SUB_MAX_TURNS) { if (opts.isStopped()) { @@ -421,20 +430,23 @@ ${verification.error} for (const tc of resp.toolCalls) { toolFingerprints.push(fingerprintToolCall(tc)); } - const window = toolFingerprints.slice(-6); + const window = toolFingerprints.slice(-12); const counts = new Map(); for (const fp of window) counts.set(fp, (counts.get(fp) ?? 0) + 1); let maxRepeats = 0; + let repeatedCmd = ""; for (const [fp, n] of counts.entries()) { - if (n > maxRepeats) + if (n > maxRepeats) { maxRepeats = n; + repeatedCmd = fp; + } } - if (maxRepeats >= 4) { + if (maxRepeats >= 6) { await emit({ ts: now(), type: "error", - text: `Loop detected in subtask execution, breaking loop.`, + text: `Loop detected in subtask execution (repeated "${repeatedCmd}" ${maxRepeats}x in last 12 calls), breaking loop.`, }); return false; } diff --git a/vibn-agent-runner/src/agent-session-runner.ts b/vibn-agent-runner/src/agent-session-runner.ts index 4f8e145f..8030ecea 100644 --- a/vibn-agent-runner/src/agent-session-runner.ts +++ b/vibn-agent-runner/src/agent-session-runner.ts @@ -403,24 +403,35 @@ File: "${path.relative(opts.repoRoot ?? ctx.workspaceRoot, task.filePath)}" (lin let ralphIteration = 0; function fingerprintToolCall(tc: any) { - if (tc.name === "shell_exec") { - const cmd = String(tc.args?.command ?? "").trim(); - const verb = - cmd - .split("&&") - .map((s) => s.trim()) - .find((s) => !s.startsWith("cd ")) - ?.split(/\s+/)[0] ?? "shell"; - return `shell_exec:${verb}`; + const name = tc.name; + const args = tc.args ?? {}; + + if (name === "shell_exec") { + const cmd = String(args.command ?? "").trim(); + const firstWord = cmd.split(/\s+/)[0] ?? "shell"; + return `shell_exec:${firstWord}`; } - if ( - tc.name === "fs_write" || - tc.name === "fs_edit" || - tc.name === "fs_read" - ) { - return `${tc.name}:${tc.args?.path}`; + + // Determine target based on most common descriptive parameter keys + const target = + args.path ?? + args.pattern ?? + args.command ?? + args.commandId ?? + args.appUuid ?? + args.uuid ?? + ""; + if (target) { + return `${name}:${target}`; } - return `${tc.name}:${Object.values(tc.args ?? {})[0]}`; + + // Filter out common metadata like projectId, and use first real argument + const keys = Object.keys(args).filter((k) => k !== "projectId"); + if (keys.length > 0) { + return `${name}:${args[keys[0]]}`; + } + + return `${name}:default`; } while (subTurn < SUB_MAX_TURNS) { @@ -536,20 +547,24 @@ ${verification.error} for (const tc of resp.toolCalls) { toolFingerprints.push(fingerprintToolCall(tc)); } - const window = toolFingerprints.slice(-6); + const window = toolFingerprints.slice(-12); const counts = new Map(); for (const fp of window) counts.set(fp, (counts.get(fp) ?? 0) + 1); let maxRepeats = 0; + let repeatedCmd = ""; for (const [fp, n] of counts.entries()) { - if (n > maxRepeats) maxRepeats = n; + if (n > maxRepeats) { + maxRepeats = n; + repeatedCmd = fp; + } } - if (maxRepeats >= 4) { + if (maxRepeats >= 6) { await emit({ ts: now(), type: "error", - text: `Loop detected in subtask execution, breaking loop.`, + text: `Loop detected in subtask execution (repeated "${repeatedCmd}" ${maxRepeats}x in last 12 calls), breaking loop.`, }); return false; }