@node9/proxy 2.6.2 → 2.7.0
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/dist/cli.js +66 -26
- package/dist/cli.mjs +66 -26
- package/dist/dashboard.mjs +13 -5
- package/dist/scan-ink.mjs +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2561,8 +2561,7 @@ function extractSessionLevelFindings(calls, ctx) {
|
|
|
2561
2561
|
occurrenceCount: 1,
|
|
2562
2562
|
loopCount: verdict.count,
|
|
2563
2563
|
loopKind: "loop",
|
|
2564
|
-
commandPreview: previewArgs(call.args, DEDUPE_PREVIEW_LEN)
|
|
2565
|
-
costUsd: verdict.count * COST_PER_LOOP_ITER_USD
|
|
2564
|
+
commandPreview: previewArgs(call.args, DEDUPE_PREVIEW_LEN)
|
|
2566
2565
|
});
|
|
2567
2566
|
}
|
|
2568
2567
|
return out;
|
|
@@ -2632,7 +2631,7 @@ function* stringValues(obj, depth = 0) {
|
|
|
2632
2631
|
}
|
|
2633
2632
|
for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
|
|
2634
2633
|
}
|
|
2635
|
-
var import_safe_regex2, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto3, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE,
|
|
2634
|
+
var import_safe_regex2, import_mvdan_sh, import_picomatch, import_safe_regex22, import_safe_regex23, import_crypto3, MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
|
|
2636
2635
|
var init_dist = __esm({
|
|
2637
2636
|
"packages/policy-engine/dist/index.mjs"() {
|
|
2638
2637
|
"use strict";
|
|
@@ -4439,7 +4438,6 @@ var init_dist = __esm({
|
|
|
4439
4438
|
longOutputRedactions: 1
|
|
4440
4439
|
};
|
|
4441
4440
|
LOOP_THRESHOLD_FOR_WASTE = 3;
|
|
4442
|
-
COST_PER_LOOP_ITER_USD = 6e-3;
|
|
4443
4441
|
DESTRUCTIVE_OP_RE = /\brm\s+-[rRf]+\b|\bDROP\s+(TABLE|DATABASE|COLLECTION|SCHEMA)\b|\bTRUNCATE\s+TABLE\b|\bgit\s+push\s+(--force|-f)\b|\bFLUSHALL\b|\bFLUSHDB\b|\bkubectl\s+delete\b|\bhelm\s+uninstall\b/i;
|
|
4444
4442
|
SENSITIVE_PATH_RE = /\.aws\/(credentials|config)\b|\.ssh\/(id_rsa|id_ed25519|id_ecdsa|id_dsa)\b|\.env(\.|$|\b)|\.config\/gcloud\/credentials\.db\b|\.docker\/config\.json\b|\.netrc\b|\.npmrc\b|\.node9\/credentials\.json\b/i;
|
|
4445
4443
|
FILE_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -11962,6 +11960,31 @@ function agentColorName(agent) {
|
|
|
11962
11960
|
return "cyan";
|
|
11963
11961
|
}
|
|
11964
11962
|
}
|
|
11963
|
+
function computeLoopWaste(loops, perSession) {
|
|
11964
|
+
const rate = /* @__PURE__ */ new Map();
|
|
11965
|
+
for (const s of perSession) {
|
|
11966
|
+
if (!(s.toolCalls > 0) || !(s.costUSD > 0)) continue;
|
|
11967
|
+
const r = s.costUSD / s.toolCalls;
|
|
11968
|
+
if (!Number.isFinite(r) || r <= 0 || r > MAX_PLAUSIBLE_RATE_USD) continue;
|
|
11969
|
+
rate.set(s.sessionId, r);
|
|
11970
|
+
}
|
|
11971
|
+
let usd = 0;
|
|
11972
|
+
let pricedIterations = 0;
|
|
11973
|
+
let unpricedIterations = 0;
|
|
11974
|
+
for (const l of loops) {
|
|
11975
|
+
if (l.kind === "long-iteration") continue;
|
|
11976
|
+
const wasted = Math.max(0, l.count - LOOP_THRESHOLD_FOR_WASTE);
|
|
11977
|
+
if (!Number.isFinite(wasted) || wasted === 0) continue;
|
|
11978
|
+
const r = rate.get(l.sessionId);
|
|
11979
|
+
if (r === void 0) {
|
|
11980
|
+
unpricedIterations += wasted;
|
|
11981
|
+
continue;
|
|
11982
|
+
}
|
|
11983
|
+
usd += wasted * r;
|
|
11984
|
+
pricedIterations += wasted;
|
|
11985
|
+
}
|
|
11986
|
+
return { usd, pricedIterations, unpricedIterations };
|
|
11987
|
+
}
|
|
11965
11988
|
function buildScanSummary(agents) {
|
|
11966
11989
|
const stats = {
|
|
11967
11990
|
sessions: 0,
|
|
@@ -12022,8 +12045,11 @@ function buildScanSummary(agents) {
|
|
|
12022
12045
|
costUSD: a.scan.totalCostUSD
|
|
12023
12046
|
})).filter((s) => s.sessions > 0 || s.findings > 0);
|
|
12024
12047
|
const sections = buildSections(allFindings);
|
|
12025
|
-
const
|
|
12026
|
-
|
|
12048
|
+
const loopWaste = computeLoopWaste(
|
|
12049
|
+
allLoops,
|
|
12050
|
+
agents.flatMap((a) => a.scan.perSession)
|
|
12051
|
+
);
|
|
12052
|
+
const loopWastedUSD = loopWaste.usd;
|
|
12027
12053
|
return {
|
|
12028
12054
|
stats,
|
|
12029
12055
|
byVerdict,
|
|
@@ -12031,7 +12057,8 @@ function buildScanSummary(agents) {
|
|
|
12031
12057
|
sections,
|
|
12032
12058
|
leaks: allLeaks,
|
|
12033
12059
|
loops: allLoops,
|
|
12034
|
-
loopWastedUSD
|
|
12060
|
+
loopWastedUSD,
|
|
12061
|
+
loopWaste
|
|
12035
12062
|
};
|
|
12036
12063
|
}
|
|
12037
12064
|
function buildSections(findings) {
|
|
@@ -12138,7 +12165,7 @@ function fullCommandOf(input) {
|
|
|
12138
12165
|
const raw = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
|
|
12139
12166
|
return String(raw).replace(/\s+/g, " ").trim();
|
|
12140
12167
|
}
|
|
12141
|
-
var AGENT_SHORT, AGENT_LONG;
|
|
12168
|
+
var AGENT_SHORT, AGENT_LONG, MAX_PLAUSIBLE_RATE_USD;
|
|
12142
12169
|
var init_scan_summary = __esm({
|
|
12143
12170
|
"src/scan-summary.ts"() {
|
|
12144
12171
|
"use strict";
|
|
@@ -12161,6 +12188,7 @@ var init_scan_summary = __esm({
|
|
|
12161
12188
|
copilot: "GitHub Copilot",
|
|
12162
12189
|
shell: "Shell"
|
|
12163
12190
|
};
|
|
12191
|
+
MAX_PLAUSIBLE_RATE_USD = 50;
|
|
12164
12192
|
}
|
|
12165
12193
|
});
|
|
12166
12194
|
|
|
@@ -12368,7 +12396,7 @@ function topRulesByVerdict(sections, verdict, n) {
|
|
|
12368
12396
|
}
|
|
12369
12397
|
return matched.sort((a, b) => b.count - a.count).slice(0, n);
|
|
12370
12398
|
}
|
|
12371
|
-
function
|
|
12399
|
+
function computeLoopWaste2(loops, totalToolCalls) {
|
|
12372
12400
|
const wastedCalls = loops.reduce((s, l) => s + Math.max(0, l.count - 1), 0);
|
|
12373
12401
|
const wastePct = totalToolCalls > 0 ? Math.round(wastedCalls / totalToolCalls * 100) : 0;
|
|
12374
12402
|
return { wastedCalls, wastePct };
|
|
@@ -13954,6 +13982,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
13954
13982
|
result.sessions++;
|
|
13955
13983
|
onProgress?.(result.filesScanned);
|
|
13956
13984
|
const sessionId = file.replace(/\.jsonl$/, "");
|
|
13985
|
+
const session = { sessionId, costUSD: 0, toolCalls: 0 };
|
|
13957
13986
|
let raw;
|
|
13958
13987
|
try {
|
|
13959
13988
|
raw = import_fs26.default.readFileSync(import_path28.default.join(projPath, file), "utf-8");
|
|
@@ -14041,7 +14070,9 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
14041
14070
|
if (usage && model) {
|
|
14042
14071
|
const p = claudeModelPrice(model);
|
|
14043
14072
|
if (p) {
|
|
14044
|
-
|
|
14073
|
+
const rowCost = (usage.input_tokens ?? 0) * p.i + (usage.output_tokens ?? 0) * p.o + (usage.cache_creation_input_tokens ?? 0) * p.cw + (usage.cache_read_input_tokens ?? 0) * p.cr;
|
|
14074
|
+
result.totalCostUSD += rowCost;
|
|
14075
|
+
session.costUSD += rowCost;
|
|
14045
14076
|
}
|
|
14046
14077
|
}
|
|
14047
14078
|
const content = entry.message?.content;
|
|
@@ -14049,6 +14080,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
14049
14080
|
for (const block of content) {
|
|
14050
14081
|
if (block.type !== "tool_use") continue;
|
|
14051
14082
|
result.totalToolCalls++;
|
|
14083
|
+
session.toolCalls++;
|
|
14052
14084
|
const toolName = block.name ?? "";
|
|
14053
14085
|
const toolNameLower = toolName.toLowerCase();
|
|
14054
14086
|
const input = block.input ?? {};
|
|
@@ -14160,6 +14192,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
14160
14192
|
if (firstDlpTs !== null && (firstEditTs === null || firstDlpTs < firstEditTs)) {
|
|
14161
14193
|
result.sessionsWithEarlySecrets++;
|
|
14162
14194
|
}
|
|
14195
|
+
result.perSession.push(session);
|
|
14163
14196
|
}
|
|
14164
14197
|
function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
|
|
14165
14198
|
const projPath = import_path28.default.join(projectsDir, proj);
|
|
@@ -14204,7 +14237,8 @@ function emptyClaudeScan() {
|
|
|
14204
14237
|
totalCostUSD: 0,
|
|
14205
14238
|
firstDate: null,
|
|
14206
14239
|
lastDate: null,
|
|
14207
|
-
sessionsWithEarlySecrets: 0
|
|
14240
|
+
sessionsWithEarlySecrets: 0,
|
|
14241
|
+
perSession: []
|
|
14208
14242
|
};
|
|
14209
14243
|
}
|
|
14210
14244
|
function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
@@ -14246,7 +14280,8 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
14246
14280
|
totalCostUSD: 0,
|
|
14247
14281
|
firstDate: null,
|
|
14248
14282
|
lastDate: null,
|
|
14249
|
-
sessionsWithEarlySecrets: 0
|
|
14283
|
+
sessionsWithEarlySecrets: 0,
|
|
14284
|
+
perSession: []
|
|
14250
14285
|
};
|
|
14251
14286
|
const dedup = emptyScanDedup();
|
|
14252
14287
|
if (!import_fs26.default.existsSync(tmpDir)) return result;
|
|
@@ -14484,7 +14519,8 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
14484
14519
|
// transcripts carry no token/model data
|
|
14485
14520
|
firstDate: null,
|
|
14486
14521
|
lastDate: null,
|
|
14487
|
-
sessionsWithEarlySecrets: 0
|
|
14522
|
+
sessionsWithEarlySecrets: 0,
|
|
14523
|
+
perSession: []
|
|
14488
14524
|
};
|
|
14489
14525
|
const dedup = emptyScanDedup();
|
|
14490
14526
|
const brainDirs = antigravityBrainDirs();
|
|
@@ -14680,7 +14716,8 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
14680
14716
|
// event logs carry no token/cost rollup
|
|
14681
14717
|
firstDate: null,
|
|
14682
14718
|
lastDate: null,
|
|
14683
|
-
sessionsWithEarlySecrets: 0
|
|
14719
|
+
sessionsWithEarlySecrets: 0,
|
|
14720
|
+
perSession: []
|
|
14684
14721
|
};
|
|
14685
14722
|
const dedup = emptyScanDedup();
|
|
14686
14723
|
if (!import_fs26.default.existsSync(sessionDir)) return result;
|
|
@@ -14862,7 +14899,8 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
14862
14899
|
totalCostUSD: 0,
|
|
14863
14900
|
firstDate: null,
|
|
14864
14901
|
lastDate: null,
|
|
14865
|
-
sessionsWithEarlySecrets: 0
|
|
14902
|
+
sessionsWithEarlySecrets: 0,
|
|
14903
|
+
perSession: []
|
|
14866
14904
|
};
|
|
14867
14905
|
const dedup = emptyScanDedup();
|
|
14868
14906
|
if (!import_fs26.default.existsSync(sessionsBase)) return result;
|
|
@@ -15141,7 +15179,8 @@ function mergeScans(a, b) {
|
|
|
15141
15179
|
totalCostUSD: a.totalCostUSD + b.totalCostUSD,
|
|
15142
15180
|
firstDate: dates.length ? dates.sort()[0] : null,
|
|
15143
15181
|
lastDate: lastDates.length ? lastDates.sort().at(-1) : null,
|
|
15144
|
-
sessionsWithEarlySecrets: a.sessionsWithEarlySecrets + b.sessionsWithEarlySecrets
|
|
15182
|
+
sessionsWithEarlySecrets: a.sessionsWithEarlySecrets + b.sessionsWithEarlySecrets,
|
|
15183
|
+
perSession: [...a.perSession, ...b.perSession]
|
|
15145
15184
|
};
|
|
15146
15185
|
}
|
|
15147
15186
|
function verdictIcon(verdict) {
|
|
@@ -15225,9 +15264,9 @@ function renderCompactScorecard(input) {
|
|
|
15225
15264
|
const realLoops = scan.loopFindings.filter((l) => l.kind !== "long-iteration");
|
|
15226
15265
|
const longIterations = scan.loopFindings.filter((l) => l.kind === "long-iteration");
|
|
15227
15266
|
if (realLoops.length > 0) {
|
|
15228
|
-
const { wastePct, wastedCalls } =
|
|
15267
|
+
const { wastePct, wastedCalls } = computeLoopWaste2(realLoops, scan.totalToolCalls);
|
|
15229
15268
|
const wasteParts = [];
|
|
15230
|
-
if (wastedCalls > 0) wasteParts.push(`${wastedCalls}
|
|
15269
|
+
if (wastedCalls > 0) wasteParts.push(`${wastedCalls} repeat calls (${wastePct}% of all calls)`);
|
|
15231
15270
|
if (summary.loopWastedUSD > 0) wasteParts.push("~" + fmtCost(summary.loopWastedUSD));
|
|
15232
15271
|
const wasteSummary = wasteParts.length ? `(${wasteParts.join(" \xB7 ")})` : "";
|
|
15233
15272
|
console.log(
|
|
@@ -15305,10 +15344,10 @@ function renderNarrativeScorecard(input) {
|
|
|
15305
15344
|
});
|
|
15306
15345
|
}
|
|
15307
15346
|
if (scan.loopFindings.length > 0) {
|
|
15308
|
-
const { wastePct, wastedCalls } =
|
|
15347
|
+
const { wastePct, wastedCalls } = computeLoopWaste2(scan.loopFindings, scan.totalToolCalls);
|
|
15309
15348
|
const cost = summary.loopWastedUSD > 0 ? `, ~${fmtCost(summary.loopWastedUSD)} wasted` : "";
|
|
15310
15349
|
medium.push({
|
|
15311
|
-
label: `${scan.loopFindings.length} agent loops (${wastedCalls}
|
|
15350
|
+
label: `${scan.loopFindings.length} agent loops (${wastedCalls} repeat calls, ${wastePct}% of calls${cost})`,
|
|
15312
15351
|
count: scan.loopFindings.length
|
|
15313
15352
|
});
|
|
15314
15353
|
}
|
|
@@ -15420,13 +15459,13 @@ function renderPanelScorecard(input, now = /* @__PURE__ */ new Date()) {
|
|
|
15420
15459
|
);
|
|
15421
15460
|
}
|
|
15422
15461
|
if (scan.loopFindings.length > 0) {
|
|
15423
|
-
const { wastePct, wastedCalls } =
|
|
15462
|
+
const { wastePct, wastedCalls } = computeLoopWaste2(scan.loopFindings, scan.totalToolCalls);
|
|
15424
15463
|
const byTool = /* @__PURE__ */ new Map();
|
|
15425
15464
|
for (const f of scan.loopFindings) {
|
|
15426
15465
|
byTool.set(f.toolName, (byTool.get(f.toolName) ?? 0) + Math.max(0, f.count - 1));
|
|
15427
15466
|
}
|
|
15428
15467
|
const top = [...byTool.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
15429
|
-
const wasteSuffix = wastedCalls > 0 ? `, ${wastedCalls}
|
|
15468
|
+
const wasteSuffix = wastedCalls > 0 ? `, ${wastedCalls} repeat calls \xB7 ${wastePct}% of calls` : "";
|
|
15430
15469
|
const detail = top ? `(${top[0]} dominates${wasteSuffix})` : "";
|
|
15431
15470
|
topLines.push(
|
|
15432
15471
|
mkLine(
|
|
@@ -15518,7 +15557,7 @@ function renderPanelScorecard(input, now = /* @__PURE__ */ new Date()) {
|
|
|
15518
15557
|
console.log("");
|
|
15519
15558
|
}
|
|
15520
15559
|
if (scan.loopFindings.length > 0) {
|
|
15521
|
-
const { wastePct, wastedCalls } =
|
|
15560
|
+
const { wastePct, wastedCalls } = computeLoopWaste2(scan.loopFindings, scan.totalToolCalls);
|
|
15522
15561
|
const byTool = /* @__PURE__ */ new Map();
|
|
15523
15562
|
let totalRepeats = 0;
|
|
15524
15563
|
for (const f of scan.loopFindings) {
|
|
@@ -15548,7 +15587,7 @@ function renderPanelScorecard(input, now = /* @__PURE__ */ new Date()) {
|
|
|
15548
15587
|
loopLines.push(mkLine([`\xD7${num(f.count).padEnd(4)} `, import_chalk6.default.bold], [target, import_chalk6.default.dim]));
|
|
15549
15588
|
}
|
|
15550
15589
|
}
|
|
15551
|
-
const wasteSuffix = wastedCalls > 0 ? ` \xB7 ${wastedCalls}
|
|
15590
|
+
const wasteSuffix = wastedCalls > 0 ? ` \xB7 ${wastedCalls} repeat calls (${wastePct}% of all calls)` : "";
|
|
15552
15591
|
const title = `AGENT LOOPS \xB7 ${scan.loopFindings.length} pattern${scan.loopFindings.length !== 1 ? "s" : ""}${wasteSuffix}`;
|
|
15553
15592
|
for (const ln of boxPanel(title, loopLines)) console.log(" " + ln);
|
|
15554
15593
|
console.log("");
|
|
@@ -15883,7 +15922,7 @@ function registerScanCommand(program2) {
|
|
|
15883
15922
|
);
|
|
15884
15923
|
}
|
|
15885
15924
|
if (scan.loopFindings.length > 0) {
|
|
15886
|
-
const { wastePct } =
|
|
15925
|
+
const { wastePct } = computeLoopWaste2(scan.loopFindings, scan.totalToolCalls);
|
|
15887
15926
|
const wasteSuffix = wastePct > 0 ? import_chalk6.default.dim(` (${wastePct}% of calls)`) : "";
|
|
15888
15927
|
cardParts.push(
|
|
15889
15928
|
import_chalk6.default.yellow("\u{1F501} ") + import_chalk6.default.yellow.bold(String(scan.loopFindings.length)) + import_chalk6.default.dim(" loops") + wasteSuffix
|
|
@@ -21056,7 +21095,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
21056
21095
|
totalCostUSD: 0,
|
|
21057
21096
|
firstDate: null,
|
|
21058
21097
|
lastDate: null,
|
|
21059
|
-
sessionsWithEarlySecrets: 0
|
|
21098
|
+
sessionsWithEarlySecrets: 0,
|
|
21099
|
+
perSession: []
|
|
21060
21100
|
};
|
|
21061
21101
|
let claude = EMPTY_SCAN;
|
|
21062
21102
|
let gemini = EMPTY_SCAN;
|
package/dist/cli.mjs
CHANGED
|
@@ -2571,8 +2571,7 @@ function extractSessionLevelFindings(calls, ctx) {
|
|
|
2571
2571
|
occurrenceCount: 1,
|
|
2572
2572
|
loopCount: verdict.count,
|
|
2573
2573
|
loopKind: "loop",
|
|
2574
|
-
commandPreview: previewArgs(call.args, DEDUPE_PREVIEW_LEN)
|
|
2575
|
-
costUsd: verdict.count * COST_PER_LOOP_ITER_USD
|
|
2574
|
+
commandPreview: previewArgs(call.args, DEDUPE_PREVIEW_LEN)
|
|
2576
2575
|
});
|
|
2577
2576
|
}
|
|
2578
2577
|
return out;
|
|
@@ -2642,7 +2641,7 @@ function* stringValues(obj, depth = 0) {
|
|
|
2642
2641
|
}
|
|
2643
2642
|
for (const v of Object.values(obj)) yield* stringValues(v, depth + 1);
|
|
2644
2643
|
}
|
|
2645
|
-
var MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE,
|
|
2644
|
+
var MAX, UNTRUSTED_TOOLS, SIGNALS, ASSIGNMENT_CONTEXT_RE, DLP_STOPWORDS, DLP_PATTERNS, DLP_PATTERNS_GLOBAL, SENSITIVE_PATH_PATTERNS, MAX_DEPTH, MAX_STRING_BYTES, MAX_JSON_PARSE_BYTES, DLP_SCAN_LIMITS, MAX_REGEX_LENGTH, REGEX_CACHE_MAX, regexCache, FORBIDDEN_PATH_SEGMENTS, syntax, sharedParser, MESSAGE_FLAGS, SHELL_INTERPRETERS, DOWNLOAD_CMDS, NORMALIZE_CACHE_MAX, normalizeCache, AST_CACHE_MAX, astCache, PARSE_FAIL, FS_READ_TOOLS, FS_OP_PRESCREEN_RE, HOME_CACHE_ALLOWLIST, SENSITIVE_PATH_RULES, BASH_TOOL_NAMES, AST_FS_REGEX_RULES, SQL_DB_CLIS, SQL_DDL_RE, CHMOD_OPEN_PERM_TOKENS, COMMAND_WRAPPERS, INLINE_INTERPRETER, RUNNER_WRAPPERS, _redirStdinOps, _listOps, WRAPPER_TAKES_TARGET, INTERP_LEADING_TARGET, NET_BINARIES, VALUE_FLAGS, FS_OP_CACHE_MAX, fsOpCache, stripDotSlash, REDIR_TRUNCATE_OPS, REDIR_HEREDOC_OPS, DEFAULT_EGRESS_ALLOWLIST, SOURCE_COMMANDS, SINK_COMMANDS, OBFUSCATORS, SENSITIVE_PATTERNS, FLAGS_WITH_VALUES, VERDICT_RANK, SQL_DML_KEYWORDS, aws_default, bash_safe_default, docker_default, filesystem_default, github_default, k8s_default, mongodb_default, postgres_default, project_jail_default, redis_default, BUILTIN_SHIELDS, LOOP_MAX_RECORDS, FINDING_TO_SIGNAL, SCAN_SIGNAL_WEIGHTS, LOOP_THRESHOLD_FOR_WASTE, DESTRUCTIVE_OP_RE, SENSITIVE_PATH_RE, FILE_TOOLS, PII_EMAIL_RE, PII_SSN_RE, PII_PHONE_RE, PII_CC_RE, REALTIME_PII_PATTERNS, MAX_PII_SCAN_BYTES, LONG_OUTPUT_THRESHOLD_BYTES, CANONICAL_EXTRACTOR_VERSION, DEDUPE_PREVIEW_LEN, TERMINAL_ESCAPE_RE, ENGINE_VERSION;
|
|
2646
2645
|
var init_dist = __esm({
|
|
2647
2646
|
"packages/policy-engine/dist/index.mjs"() {
|
|
2648
2647
|
"use strict";
|
|
@@ -4443,7 +4442,6 @@ var init_dist = __esm({
|
|
|
4443
4442
|
longOutputRedactions: 1
|
|
4444
4443
|
};
|
|
4445
4444
|
LOOP_THRESHOLD_FOR_WASTE = 3;
|
|
4446
|
-
COST_PER_LOOP_ITER_USD = 6e-3;
|
|
4447
4445
|
DESTRUCTIVE_OP_RE = /\brm\s+-[rRf]+\b|\bDROP\s+(TABLE|DATABASE|COLLECTION|SCHEMA)\b|\bTRUNCATE\s+TABLE\b|\bgit\s+push\s+(--force|-f)\b|\bFLUSHALL\b|\bFLUSHDB\b|\bkubectl\s+delete\b|\bhelm\s+uninstall\b/i;
|
|
4448
4446
|
SENSITIVE_PATH_RE = /\.aws\/(credentials|config)\b|\.ssh\/(id_rsa|id_ed25519|id_ecdsa|id_dsa)\b|\.env(\.|$|\b)|\.config\/gcloud\/credentials\.db\b|\.docker\/config\.json\b|\.netrc\b|\.npmrc\b|\.node9\/credentials\.json\b/i;
|
|
4449
4447
|
FILE_TOOLS = /* @__PURE__ */ new Set([
|
|
@@ -11963,6 +11961,31 @@ function agentColorName(agent) {
|
|
|
11963
11961
|
return "cyan";
|
|
11964
11962
|
}
|
|
11965
11963
|
}
|
|
11964
|
+
function computeLoopWaste(loops, perSession) {
|
|
11965
|
+
const rate = /* @__PURE__ */ new Map();
|
|
11966
|
+
for (const s of perSession) {
|
|
11967
|
+
if (!(s.toolCalls > 0) || !(s.costUSD > 0)) continue;
|
|
11968
|
+
const r = s.costUSD / s.toolCalls;
|
|
11969
|
+
if (!Number.isFinite(r) || r <= 0 || r > MAX_PLAUSIBLE_RATE_USD) continue;
|
|
11970
|
+
rate.set(s.sessionId, r);
|
|
11971
|
+
}
|
|
11972
|
+
let usd = 0;
|
|
11973
|
+
let pricedIterations = 0;
|
|
11974
|
+
let unpricedIterations = 0;
|
|
11975
|
+
for (const l of loops) {
|
|
11976
|
+
if (l.kind === "long-iteration") continue;
|
|
11977
|
+
const wasted = Math.max(0, l.count - LOOP_THRESHOLD_FOR_WASTE);
|
|
11978
|
+
if (!Number.isFinite(wasted) || wasted === 0) continue;
|
|
11979
|
+
const r = rate.get(l.sessionId);
|
|
11980
|
+
if (r === void 0) {
|
|
11981
|
+
unpricedIterations += wasted;
|
|
11982
|
+
continue;
|
|
11983
|
+
}
|
|
11984
|
+
usd += wasted * r;
|
|
11985
|
+
pricedIterations += wasted;
|
|
11986
|
+
}
|
|
11987
|
+
return { usd, pricedIterations, unpricedIterations };
|
|
11988
|
+
}
|
|
11966
11989
|
function buildScanSummary(agents) {
|
|
11967
11990
|
const stats = {
|
|
11968
11991
|
sessions: 0,
|
|
@@ -12023,8 +12046,11 @@ function buildScanSummary(agents) {
|
|
|
12023
12046
|
costUSD: a.scan.totalCostUSD
|
|
12024
12047
|
})).filter((s) => s.sessions > 0 || s.findings > 0);
|
|
12025
12048
|
const sections = buildSections(allFindings);
|
|
12026
|
-
const
|
|
12027
|
-
|
|
12049
|
+
const loopWaste = computeLoopWaste(
|
|
12050
|
+
allLoops,
|
|
12051
|
+
agents.flatMap((a) => a.scan.perSession)
|
|
12052
|
+
);
|
|
12053
|
+
const loopWastedUSD = loopWaste.usd;
|
|
12028
12054
|
return {
|
|
12029
12055
|
stats,
|
|
12030
12056
|
byVerdict,
|
|
@@ -12032,7 +12058,8 @@ function buildScanSummary(agents) {
|
|
|
12032
12058
|
sections,
|
|
12033
12059
|
leaks: allLeaks,
|
|
12034
12060
|
loops: allLoops,
|
|
12035
|
-
loopWastedUSD
|
|
12061
|
+
loopWastedUSD,
|
|
12062
|
+
loopWaste
|
|
12036
12063
|
};
|
|
12037
12064
|
}
|
|
12038
12065
|
function buildSections(findings) {
|
|
@@ -12139,7 +12166,7 @@ function fullCommandOf(input) {
|
|
|
12139
12166
|
const raw = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
|
|
12140
12167
|
return String(raw).replace(/\s+/g, " ").trim();
|
|
12141
12168
|
}
|
|
12142
|
-
var AGENT_SHORT, AGENT_LONG;
|
|
12169
|
+
var AGENT_SHORT, AGENT_LONG, MAX_PLAUSIBLE_RATE_USD;
|
|
12143
12170
|
var init_scan_summary = __esm({
|
|
12144
12171
|
"src/scan-summary.ts"() {
|
|
12145
12172
|
"use strict";
|
|
@@ -12162,6 +12189,7 @@ var init_scan_summary = __esm({
|
|
|
12162
12189
|
copilot: "GitHub Copilot",
|
|
12163
12190
|
shell: "Shell"
|
|
12164
12191
|
};
|
|
12192
|
+
MAX_PLAUSIBLE_RATE_USD = 50;
|
|
12165
12193
|
}
|
|
12166
12194
|
});
|
|
12167
12195
|
|
|
@@ -12370,7 +12398,7 @@ function topRulesByVerdict(sections, verdict, n) {
|
|
|
12370
12398
|
}
|
|
12371
12399
|
return matched.sort((a, b) => b.count - a.count).slice(0, n);
|
|
12372
12400
|
}
|
|
12373
|
-
function
|
|
12401
|
+
function computeLoopWaste2(loops, totalToolCalls) {
|
|
12374
12402
|
const wastedCalls = loops.reduce((s, l) => s + Math.max(0, l.count - 1), 0);
|
|
12375
12403
|
const wastePct = totalToolCalls > 0 ? Math.round(wastedCalls / totalToolCalls * 100) : 0;
|
|
12376
12404
|
return { wastedCalls, wastePct };
|
|
@@ -13959,6 +13987,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
13959
13987
|
result.sessions++;
|
|
13960
13988
|
onProgress?.(result.filesScanned);
|
|
13961
13989
|
const sessionId = file.replace(/\.jsonl$/, "");
|
|
13990
|
+
const session = { sessionId, costUSD: 0, toolCalls: 0 };
|
|
13962
13991
|
let raw;
|
|
13963
13992
|
try {
|
|
13964
13993
|
raw = fs27.readFileSync(path29.join(projPath, file), "utf-8");
|
|
@@ -14046,7 +14075,9 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
14046
14075
|
if (usage && model) {
|
|
14047
14076
|
const p = claudeModelPrice(model);
|
|
14048
14077
|
if (p) {
|
|
14049
|
-
|
|
14078
|
+
const rowCost = (usage.input_tokens ?? 0) * p.i + (usage.output_tokens ?? 0) * p.o + (usage.cache_creation_input_tokens ?? 0) * p.cw + (usage.cache_read_input_tokens ?? 0) * p.cr;
|
|
14079
|
+
result.totalCostUSD += rowCost;
|
|
14080
|
+
session.costUSD += rowCost;
|
|
14050
14081
|
}
|
|
14051
14082
|
}
|
|
14052
14083
|
const content = entry.message?.content;
|
|
@@ -14054,6 +14085,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
14054
14085
|
for (const block of content) {
|
|
14055
14086
|
if (block.type !== "tool_use") continue;
|
|
14056
14087
|
result.totalToolCalls++;
|
|
14088
|
+
session.toolCalls++;
|
|
14057
14089
|
const toolName = block.name ?? "";
|
|
14058
14090
|
const toolNameLower = toolName.toLowerCase();
|
|
14059
14091
|
const input = block.input ?? {};
|
|
@@ -14165,6 +14197,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
14165
14197
|
if (firstDlpTs !== null && (firstEditTs === null || firstDlpTs < firstEditTs)) {
|
|
14166
14198
|
result.sessionsWithEarlySecrets++;
|
|
14167
14199
|
}
|
|
14200
|
+
result.perSession.push(session);
|
|
14168
14201
|
}
|
|
14169
14202
|
function processClaudeProject(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
|
|
14170
14203
|
const projPath = path29.join(projectsDir, proj);
|
|
@@ -14209,7 +14242,8 @@ function emptyClaudeScan() {
|
|
|
14209
14242
|
totalCostUSD: 0,
|
|
14210
14243
|
firstDate: null,
|
|
14211
14244
|
lastDate: null,
|
|
14212
|
-
sessionsWithEarlySecrets: 0
|
|
14245
|
+
sessionsWithEarlySecrets: 0,
|
|
14246
|
+
perSession: []
|
|
14213
14247
|
};
|
|
14214
14248
|
}
|
|
14215
14249
|
function scanClaudeHistory(startDate, onProgress, onLine) {
|
|
@@ -14251,7 +14285,8 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
14251
14285
|
totalCostUSD: 0,
|
|
14252
14286
|
firstDate: null,
|
|
14253
14287
|
lastDate: null,
|
|
14254
|
-
sessionsWithEarlySecrets: 0
|
|
14288
|
+
sessionsWithEarlySecrets: 0,
|
|
14289
|
+
perSession: []
|
|
14255
14290
|
};
|
|
14256
14291
|
const dedup = emptyScanDedup();
|
|
14257
14292
|
if (!fs27.existsSync(tmpDir)) return result;
|
|
@@ -14489,7 +14524,8 @@ function scanAntigravityHistory(startDate, onProgress, onLine) {
|
|
|
14489
14524
|
// transcripts carry no token/model data
|
|
14490
14525
|
firstDate: null,
|
|
14491
14526
|
lastDate: null,
|
|
14492
|
-
sessionsWithEarlySecrets: 0
|
|
14527
|
+
sessionsWithEarlySecrets: 0,
|
|
14528
|
+
perSession: []
|
|
14493
14529
|
};
|
|
14494
14530
|
const dedup = emptyScanDedup();
|
|
14495
14531
|
const brainDirs = antigravityBrainDirs();
|
|
@@ -14685,7 +14721,8 @@ function scanCopilotHistory(startDate, onProgress, onLine) {
|
|
|
14685
14721
|
// event logs carry no token/cost rollup
|
|
14686
14722
|
firstDate: null,
|
|
14687
14723
|
lastDate: null,
|
|
14688
|
-
sessionsWithEarlySecrets: 0
|
|
14724
|
+
sessionsWithEarlySecrets: 0,
|
|
14725
|
+
perSession: []
|
|
14689
14726
|
};
|
|
14690
14727
|
const dedup = emptyScanDedup();
|
|
14691
14728
|
if (!fs27.existsSync(sessionDir)) return result;
|
|
@@ -14867,7 +14904,8 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
14867
14904
|
totalCostUSD: 0,
|
|
14868
14905
|
firstDate: null,
|
|
14869
14906
|
lastDate: null,
|
|
14870
|
-
sessionsWithEarlySecrets: 0
|
|
14907
|
+
sessionsWithEarlySecrets: 0,
|
|
14908
|
+
perSession: []
|
|
14871
14909
|
};
|
|
14872
14910
|
const dedup = emptyScanDedup();
|
|
14873
14911
|
if (!fs27.existsSync(sessionsBase)) return result;
|
|
@@ -15146,7 +15184,8 @@ function mergeScans(a, b) {
|
|
|
15146
15184
|
totalCostUSD: a.totalCostUSD + b.totalCostUSD,
|
|
15147
15185
|
firstDate: dates.length ? dates.sort()[0] : null,
|
|
15148
15186
|
lastDate: lastDates.length ? lastDates.sort().at(-1) : null,
|
|
15149
|
-
sessionsWithEarlySecrets: a.sessionsWithEarlySecrets + b.sessionsWithEarlySecrets
|
|
15187
|
+
sessionsWithEarlySecrets: a.sessionsWithEarlySecrets + b.sessionsWithEarlySecrets,
|
|
15188
|
+
perSession: [...a.perSession, ...b.perSession]
|
|
15150
15189
|
};
|
|
15151
15190
|
}
|
|
15152
15191
|
function verdictIcon(verdict) {
|
|
@@ -15230,9 +15269,9 @@ function renderCompactScorecard(input) {
|
|
|
15230
15269
|
const realLoops = scan.loopFindings.filter((l) => l.kind !== "long-iteration");
|
|
15231
15270
|
const longIterations = scan.loopFindings.filter((l) => l.kind === "long-iteration");
|
|
15232
15271
|
if (realLoops.length > 0) {
|
|
15233
|
-
const { wastePct, wastedCalls } =
|
|
15272
|
+
const { wastePct, wastedCalls } = computeLoopWaste2(realLoops, scan.totalToolCalls);
|
|
15234
15273
|
const wasteParts = [];
|
|
15235
|
-
if (wastedCalls > 0) wasteParts.push(`${wastedCalls}
|
|
15274
|
+
if (wastedCalls > 0) wasteParts.push(`${wastedCalls} repeat calls (${wastePct}% of all calls)`);
|
|
15236
15275
|
if (summary.loopWastedUSD > 0) wasteParts.push("~" + fmtCost(summary.loopWastedUSD));
|
|
15237
15276
|
const wasteSummary = wasteParts.length ? `(${wasteParts.join(" \xB7 ")})` : "";
|
|
15238
15277
|
console.log(
|
|
@@ -15310,10 +15349,10 @@ function renderNarrativeScorecard(input) {
|
|
|
15310
15349
|
});
|
|
15311
15350
|
}
|
|
15312
15351
|
if (scan.loopFindings.length > 0) {
|
|
15313
|
-
const { wastePct, wastedCalls } =
|
|
15352
|
+
const { wastePct, wastedCalls } = computeLoopWaste2(scan.loopFindings, scan.totalToolCalls);
|
|
15314
15353
|
const cost = summary.loopWastedUSD > 0 ? `, ~${fmtCost(summary.loopWastedUSD)} wasted` : "";
|
|
15315
15354
|
medium.push({
|
|
15316
|
-
label: `${scan.loopFindings.length} agent loops (${wastedCalls}
|
|
15355
|
+
label: `${scan.loopFindings.length} agent loops (${wastedCalls} repeat calls, ${wastePct}% of calls${cost})`,
|
|
15317
15356
|
count: scan.loopFindings.length
|
|
15318
15357
|
});
|
|
15319
15358
|
}
|
|
@@ -15425,13 +15464,13 @@ function renderPanelScorecard(input, now = /* @__PURE__ */ new Date()) {
|
|
|
15425
15464
|
);
|
|
15426
15465
|
}
|
|
15427
15466
|
if (scan.loopFindings.length > 0) {
|
|
15428
|
-
const { wastePct, wastedCalls } =
|
|
15467
|
+
const { wastePct, wastedCalls } = computeLoopWaste2(scan.loopFindings, scan.totalToolCalls);
|
|
15429
15468
|
const byTool = /* @__PURE__ */ new Map();
|
|
15430
15469
|
for (const f of scan.loopFindings) {
|
|
15431
15470
|
byTool.set(f.toolName, (byTool.get(f.toolName) ?? 0) + Math.max(0, f.count - 1));
|
|
15432
15471
|
}
|
|
15433
15472
|
const top = [...byTool.entries()].sort((a, b) => b[1] - a[1])[0];
|
|
15434
|
-
const wasteSuffix = wastedCalls > 0 ? `, ${wastedCalls}
|
|
15473
|
+
const wasteSuffix = wastedCalls > 0 ? `, ${wastedCalls} repeat calls \xB7 ${wastePct}% of calls` : "";
|
|
15435
15474
|
const detail = top ? `(${top[0]} dominates${wasteSuffix})` : "";
|
|
15436
15475
|
topLines.push(
|
|
15437
15476
|
mkLine(
|
|
@@ -15523,7 +15562,7 @@ function renderPanelScorecard(input, now = /* @__PURE__ */ new Date()) {
|
|
|
15523
15562
|
console.log("");
|
|
15524
15563
|
}
|
|
15525
15564
|
if (scan.loopFindings.length > 0) {
|
|
15526
|
-
const { wastePct, wastedCalls } =
|
|
15565
|
+
const { wastePct, wastedCalls } = computeLoopWaste2(scan.loopFindings, scan.totalToolCalls);
|
|
15527
15566
|
const byTool = /* @__PURE__ */ new Map();
|
|
15528
15567
|
let totalRepeats = 0;
|
|
15529
15568
|
for (const f of scan.loopFindings) {
|
|
@@ -15553,7 +15592,7 @@ function renderPanelScorecard(input, now = /* @__PURE__ */ new Date()) {
|
|
|
15553
15592
|
loopLines.push(mkLine([`\xD7${num(f.count).padEnd(4)} `, chalk6.bold], [target, chalk6.dim]));
|
|
15554
15593
|
}
|
|
15555
15594
|
}
|
|
15556
|
-
const wasteSuffix = wastedCalls > 0 ? ` \xB7 ${wastedCalls}
|
|
15595
|
+
const wasteSuffix = wastedCalls > 0 ? ` \xB7 ${wastedCalls} repeat calls (${wastePct}% of all calls)` : "";
|
|
15557
15596
|
const title = `AGENT LOOPS \xB7 ${scan.loopFindings.length} pattern${scan.loopFindings.length !== 1 ? "s" : ""}${wasteSuffix}`;
|
|
15558
15597
|
for (const ln of boxPanel(title, loopLines)) console.log(" " + ln);
|
|
15559
15598
|
console.log("");
|
|
@@ -15888,7 +15927,7 @@ function registerScanCommand(program2) {
|
|
|
15888
15927
|
);
|
|
15889
15928
|
}
|
|
15890
15929
|
if (scan.loopFindings.length > 0) {
|
|
15891
|
-
const { wastePct } =
|
|
15930
|
+
const { wastePct } = computeLoopWaste2(scan.loopFindings, scan.totalToolCalls);
|
|
15892
15931
|
const wasteSuffix = wastePct > 0 ? chalk6.dim(` (${wastePct}% of calls)`) : "";
|
|
15893
15932
|
cardParts.push(
|
|
15894
15933
|
chalk6.yellow("\u{1F501} ") + chalk6.yellow.bold(String(scan.loopFindings.length)) + chalk6.dim(" loops") + wasteSuffix
|
|
@@ -21057,7 +21096,8 @@ data: ${JSON.stringify(item.data)}
|
|
|
21057
21096
|
totalCostUSD: 0,
|
|
21058
21097
|
firstDate: null,
|
|
21059
21098
|
lastDate: null,
|
|
21060
|
-
sessionsWithEarlySecrets: 0
|
|
21099
|
+
sessionsWithEarlySecrets: 0,
|
|
21100
|
+
perSession: []
|
|
21061
21101
|
};
|
|
21062
21102
|
let claude = EMPTY_SCAN;
|
|
21063
21103
|
let gemini = EMPTY_SCAN;
|
package/dist/dashboard.mjs
CHANGED
|
@@ -5047,6 +5047,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
5047
5047
|
result.sessions++;
|
|
5048
5048
|
onProgress?.(result.filesScanned);
|
|
5049
5049
|
const sessionId = file.replace(/\.jsonl$/, "");
|
|
5050
|
+
const session = { sessionId, costUSD: 0, toolCalls: 0 };
|
|
5050
5051
|
let raw;
|
|
5051
5052
|
try {
|
|
5052
5053
|
raw = fs7.readFileSync(path10.join(projPath, file), "utf-8");
|
|
@@ -5134,7 +5135,9 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
5134
5135
|
if (usage && model) {
|
|
5135
5136
|
const p = claudeModelPrice2(model);
|
|
5136
5137
|
if (p) {
|
|
5137
|
-
|
|
5138
|
+
const rowCost = (usage.input_tokens ?? 0) * p.i + (usage.output_tokens ?? 0) * p.o + (usage.cache_creation_input_tokens ?? 0) * p.cw + (usage.cache_read_input_tokens ?? 0) * p.cr;
|
|
5139
|
+
result.totalCostUSD += rowCost;
|
|
5140
|
+
session.costUSD += rowCost;
|
|
5138
5141
|
}
|
|
5139
5142
|
}
|
|
5140
5143
|
const content = entry.message?.content;
|
|
@@ -5142,6 +5145,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
5142
5145
|
for (const block of content) {
|
|
5143
5146
|
if (block.type !== "tool_use") continue;
|
|
5144
5147
|
result.totalToolCalls++;
|
|
5148
|
+
session.toolCalls++;
|
|
5145
5149
|
const toolName = block.name ?? "";
|
|
5146
5150
|
const toolNameLower = toolName.toLowerCase();
|
|
5147
5151
|
const input = block.input ?? {};
|
|
@@ -5253,6 +5257,7 @@ function processClaudeFile(file, projPath, projLabel, ruleSources, startDate, re
|
|
|
5253
5257
|
if (firstDlpTs !== null && (firstEditTs === null || firstDlpTs < firstEditTs)) {
|
|
5254
5258
|
result.sessionsWithEarlySecrets++;
|
|
5255
5259
|
}
|
|
5260
|
+
result.perSession.push(session);
|
|
5256
5261
|
}
|
|
5257
5262
|
async function processClaudeProjectAsync(proj, projectsDir, ruleSources, startDate, result, dedup, onProgress, onLine) {
|
|
5258
5263
|
const projPath = path10.join(projectsDir, proj);
|
|
@@ -5301,7 +5306,8 @@ function emptyClaudeScan() {
|
|
|
5301
5306
|
totalCostUSD: 0,
|
|
5302
5307
|
firstDate: null,
|
|
5303
5308
|
lastDate: null,
|
|
5304
|
-
sessionsWithEarlySecrets: 0
|
|
5309
|
+
sessionsWithEarlySecrets: 0,
|
|
5310
|
+
perSession: []
|
|
5305
5311
|
};
|
|
5306
5312
|
}
|
|
5307
5313
|
async function scanClaudeHistoryAsync(startDate, onProgress, onLine) {
|
|
@@ -5343,7 +5349,8 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
|
|
|
5343
5349
|
totalCostUSD: 0,
|
|
5344
5350
|
firstDate: null,
|
|
5345
5351
|
lastDate: null,
|
|
5346
|
-
sessionsWithEarlySecrets: 0
|
|
5352
|
+
sessionsWithEarlySecrets: 0,
|
|
5353
|
+
perSession: []
|
|
5347
5354
|
};
|
|
5348
5355
|
const dedup = emptyScanDedup();
|
|
5349
5356
|
if (!fs7.existsSync(tmpDir)) return result;
|
|
@@ -5570,7 +5577,8 @@ function scanCodexHistory(startDate, onProgress, onLine) {
|
|
|
5570
5577
|
totalCostUSD: 0,
|
|
5571
5578
|
firstDate: null,
|
|
5572
5579
|
lastDate: null,
|
|
5573
|
-
sessionsWithEarlySecrets: 0
|
|
5580
|
+
sessionsWithEarlySecrets: 0,
|
|
5581
|
+
perSession: []
|
|
5574
5582
|
};
|
|
5575
5583
|
const dedup = emptyScanDedup();
|
|
5576
5584
|
if (!fs7.existsSync(sessionsBase)) return result;
|
|
@@ -7070,7 +7078,7 @@ function Cost({ audit }) {
|
|
|
7070
7078
|
flexGrow: 1,
|
|
7071
7079
|
flexBasis: 0,
|
|
7072
7080
|
children: [
|
|
7073
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "COST" }),
|
|
7081
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "COST \xB7 API value" }),
|
|
7074
7082
|
audit === null ? /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "loading\u2026" }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
7075
7083
|
/* @__PURE__ */ jsxs3(Box3, { height: 1, children: [
|
|
7076
7084
|
/* @__PURE__ */ jsx3(Box3, { width: LABEL_W2, children: /* @__PURE__ */ jsx3(Text3, { children: "Total" }) }),
|
package/dist/scan-ink.mjs
CHANGED
|
@@ -45,7 +45,7 @@ var LABEL_W = 16;
|
|
|
45
45
|
function CostPanel({ summary, width }) {
|
|
46
46
|
const total = summary.stats.totalCostUSD;
|
|
47
47
|
return /* @__PURE__ */ jsxs3(Box3, { borderStyle: "round", borderColor: "gray", paddingX: 1, flexDirection: "column", width, children: [
|
|
48
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "COST" }),
|
|
48
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "COST \xB7 API value" }),
|
|
49
49
|
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
50
50
|
/* @__PURE__ */ jsx3(Box3, { width: LABEL_W, children: /* @__PURE__ */ jsx3(Text3, { children: "Total" }) }),
|
|
51
51
|
/* @__PURE__ */ jsx3(Text3, { bold: true, children: formatCost(total) })
|
|
@@ -1791,7 +1791,7 @@ function StaticScorecard({ input, rangeLabel, now }) {
|
|
|
1791
1791
|
if (reviewCount > 0) parts.push(`${reviewCount} op${reviewCount !== 1 ? "s" : ""} flagged`);
|
|
1792
1792
|
if (loopCount > 0)
|
|
1793
1793
|
parts.push(
|
|
1794
|
-
`${loopCount} loop${loopCount !== 1 ? "s" : ""}${wastedCalls > 0 ? ` \xB7 ${wastedCalls}
|
|
1794
|
+
`${loopCount} loop${loopCount !== 1 ? "s" : ""}${wastedCalls > 0 ? ` \xB7 ${wastedCalls} repeat calls (${wastePct}% of all calls)` : ""}`
|
|
1795
1795
|
);
|
|
1796
1796
|
return /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
1797
1797
|
/* @__PURE__ */ jsx11(SeverityBand, { label: `Medium (${parts.join(" \xB7 ")})`, width }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node9/proxy",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|