@swestash/agent-lens 0.10.0 → 0.11.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/agent-lens.js +114 -7
- package/dist/agent-lens.js.map +1 -1
- package/package.json +1 -1
- package/web/404.html +1 -1
- package/web/assets/AboutView-B6r8WJKS.js +1 -0
- package/web/assets/{Dashboard-B9-O6UM7.js → Dashboard-C0axgFBI.js} +1 -1
- package/web/assets/{FileView-D4iZxHta.js → FileView-D3OB1c3e.js} +1 -1
- package/web/assets/{FilesView-bjBm-f1c.js → FilesView-DYSJHOfO.js} +1 -1
- package/web/assets/{SecurityView-DNgbLbZ2.js → SecurityView-Dl5rRtT4.js} +1 -1
- package/web/assets/{SessionView-BCz9AnOz.js → SessionView-77dXiwzp.js} +8 -8
- package/web/assets/SkillView-Bkwf24pB.js +1 -0
- package/web/assets/{SkillsView-Dvke2ty1.js → SkillsView-CAGiD59V.js} +1 -1
- package/web/assets/{WorkflowView-BadGoeGI.js → WorkflowView-CR2GzUdU.js} +1 -1
- package/web/assets/{index-DxkiGVr3.js → index-BnVRCFL6.js} +1 -1
- package/web/assets/{index-CqWARNqS.js → index-BxbXFMQG.js} +4 -4
- package/web/assets/{jsonish-SimOVe9L.js → jsonish-B3QCD4cU.js} +1 -1
- package/web/assets/{severity-DJTsjBgl.js → severity-DTzxtVJn.js} +1 -1
- package/web/index.html +1 -1
- package/web/assets/AboutView-Bngkx739.js +0 -1
- package/web/assets/SkillView-D6egSywk.js +0 -1
package/dist/agent-lens.js
CHANGED
|
@@ -1242,6 +1242,15 @@ function classifyToolError(resultSummary) {
|
|
|
1242
1242
|
}
|
|
1243
1243
|
|
|
1244
1244
|
// ../core/dist/pricing.js
|
|
1245
|
+
var SYNTHETIC_MODEL = "<synthetic>";
|
|
1246
|
+
function unpricedModels(models) {
|
|
1247
|
+
const out = /* @__PURE__ */ new Set();
|
|
1248
|
+
for (const m of models) {
|
|
1249
|
+
if (m && m !== SYNTHETIC_MODEL && !rateForModel(m))
|
|
1250
|
+
out.add(m);
|
|
1251
|
+
}
|
|
1252
|
+
return [...out].sort();
|
|
1253
|
+
}
|
|
1245
1254
|
var PRICE_TABLE = {
|
|
1246
1255
|
// Opus 4.0 / 4.1 launched at $15/$75; Opus 4.5 cut the Opus tier to $5/$25 and 4.6/4.7/4.8 kept it.
|
|
1247
1256
|
// The bare 'claude-opus-4' fallback stays at the legacy rate for 4.0/4.1; 4.5+ override below.
|
|
@@ -1250,17 +1259,65 @@ var PRICE_TABLE = {
|
|
|
1250
1259
|
"claude-opus-4-6": { input: 5, output: 25, cacheWrite: 6.25, cacheRead: 0.5 },
|
|
1251
1260
|
"claude-opus-4-7": { input: 5, output: 25, cacheWrite: 6.25, cacheRead: 0.5 },
|
|
1252
1261
|
"claude-opus-4-8": { input: 5, output: 25, cacheWrite: 6.25, cacheRead: 0.5 },
|
|
1262
|
+
"claude-opus-5": { input: 5, output: 25, cacheWrite: 6.25, cacheRead: 0.5 },
|
|
1253
1263
|
"claude-fable-5": { input: 10, output: 50, cacheWrite: 12.5, cacheRead: 1 },
|
|
1254
1264
|
"claude-sonnet-4": { input: 3, output: 15, cacheWrite: 3.75, cacheRead: 0.3 },
|
|
1265
|
+
// Sonnet 5 list price. An introductory $2/$10 rate runs through 2026-08-31; we bill the list
|
|
1266
|
+
// price so the estimate doesn't silently change under a date, matching the "list price" label.
|
|
1267
|
+
"claude-sonnet-5": { input: 3, output: 15, cacheWrite: 3.75, cacheRead: 0.3 },
|
|
1255
1268
|
"claude-haiku-4": { input: 1, output: 5, cacheWrite: 1.25, cacheRead: 0.1 },
|
|
1256
1269
|
"claude-3-5-haiku": { input: 0.8, output: 4, cacheWrite: 1, cacheRead: 0.08 },
|
|
1257
1270
|
"claude-3-5-sonnet": { input: 3, output: 15, cacheWrite: 3.75, cacheRead: 0.3 }
|
|
1258
1271
|
};
|
|
1272
|
+
var CACHE_WRITE_MULTIPLIER = 1.25;
|
|
1273
|
+
var CACHE_READ_MULTIPLIER = 0.1;
|
|
1274
|
+
function toRate(v) {
|
|
1275
|
+
if (v === null || typeof v !== "object" || Array.isArray(v))
|
|
1276
|
+
return null;
|
|
1277
|
+
const o = v;
|
|
1278
|
+
const num = (x) => typeof x === "number" && Number.isFinite(x) && x >= 0 ? x : null;
|
|
1279
|
+
const input = num(o.input);
|
|
1280
|
+
const output = num(o.output);
|
|
1281
|
+
if (input === null || output === null)
|
|
1282
|
+
return null;
|
|
1283
|
+
if (o.cacheWrite !== void 0 && num(o.cacheWrite) === null)
|
|
1284
|
+
return null;
|
|
1285
|
+
if (o.cacheRead !== void 0 && num(o.cacheRead) === null)
|
|
1286
|
+
return null;
|
|
1287
|
+
return {
|
|
1288
|
+
input,
|
|
1289
|
+
output,
|
|
1290
|
+
cacheWrite: num(o.cacheWrite) ?? input * CACHE_WRITE_MULTIPLIER,
|
|
1291
|
+
cacheRead: num(o.cacheRead) ?? input * CACHE_READ_MULTIPLIER
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
function mergePricing(base, overrides) {
|
|
1295
|
+
const table = { ...base };
|
|
1296
|
+
const invalid = [];
|
|
1297
|
+
const applied = [];
|
|
1298
|
+
if (overrides === null || typeof overrides !== "object" || Array.isArray(overrides)) {
|
|
1299
|
+
return { table, invalid, applied };
|
|
1300
|
+
}
|
|
1301
|
+
for (const [prefix, value] of Object.entries(overrides)) {
|
|
1302
|
+
const rate = toRate(value);
|
|
1303
|
+
if (!prefix.trim() || !rate)
|
|
1304
|
+
invalid.push(prefix);
|
|
1305
|
+
else {
|
|
1306
|
+
table[prefix] = rate;
|
|
1307
|
+
applied.push(prefix);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return { table, invalid: invalid.sort(), applied: applied.sort() };
|
|
1311
|
+
}
|
|
1312
|
+
var activeTable = PRICE_TABLE;
|
|
1313
|
+
function usePricing(table) {
|
|
1314
|
+
activeTable = table;
|
|
1315
|
+
}
|
|
1259
1316
|
function rateForModel(model) {
|
|
1260
1317
|
if (!model)
|
|
1261
1318
|
return null;
|
|
1262
1319
|
let best = null;
|
|
1263
|
-
for (const [prefix, rate] of Object.entries(
|
|
1320
|
+
for (const [prefix, rate] of Object.entries(activeTable)) {
|
|
1264
1321
|
if (model.startsWith(prefix) && (!best || prefix.length > best.len)) {
|
|
1265
1322
|
best = { len: prefix.length, rate };
|
|
1266
1323
|
}
|
|
@@ -1383,6 +1440,18 @@ function resolveRetention() {
|
|
|
1383
1440
|
const parsed = raw ? Number(raw) : NaN;
|
|
1384
1441
|
return Number.isFinite(parsed) && parsed >= 0 ? { keepDays: parsed, origin: "env" } : { keepDays: DEFAULT_VERSIONS_KEEP_DAYS, origin: "default" };
|
|
1385
1442
|
}
|
|
1443
|
+
function resolvePricing(cfg = readConfigFile()) {
|
|
1444
|
+
const { table, invalid, applied } = mergePricing(PRICE_TABLE, cfg?.pricing);
|
|
1445
|
+
return { table, origin: applied.length ? "file" : "default", applied, invalid };
|
|
1446
|
+
}
|
|
1447
|
+
function installPricing(cfg) {
|
|
1448
|
+
const resolved = cfg === void 0 ? resolvePricing() : resolvePricing(cfg);
|
|
1449
|
+
usePricing(resolved.table);
|
|
1450
|
+
if (resolved.invalid.length) {
|
|
1451
|
+
console.error(`agent-lens: ignoring ${resolved.invalid.length} malformed pricing override(s): ${resolved.invalid.join(", ")} (each entry needs numeric input + output; built-in rates kept)`);
|
|
1452
|
+
}
|
|
1453
|
+
return resolved;
|
|
1454
|
+
}
|
|
1386
1455
|
function readConfigFile() {
|
|
1387
1456
|
const repoRoot = findRepoRoot();
|
|
1388
1457
|
const dataDir = resolveDataDir(repoRoot);
|
|
@@ -4235,10 +4304,16 @@ function printIngestReport(db, dbPath, r) {
|
|
|
4235
4304
|
const usageRows = db.prepare("SELECT model, input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens FROM token_usage").all();
|
|
4236
4305
|
let cost = 0;
|
|
4237
4306
|
let totalTokens = 0;
|
|
4307
|
+
const unpriced2 = /* @__PURE__ */ new Map();
|
|
4238
4308
|
for (const u of usageRows) {
|
|
4239
4309
|
cost += costForUsage(u.model, u);
|
|
4240
4310
|
totalTokens += u.input_tokens + u.output_tokens + u.cache_creation_input_tokens + u.cache_read_input_tokens;
|
|
4311
|
+
if (u.model && u.model !== SYNTHETIC_MODEL && !rateForModel(u.model)) {
|
|
4312
|
+
unpriced2.set(u.model, (unpriced2.get(u.model) ?? 0) + 1);
|
|
4313
|
+
}
|
|
4241
4314
|
}
|
|
4315
|
+
const unpricedLine = unpriced2.size ? `
|
|
4316
|
+
unpriced=${unpriced2.size} model(s): ` + [...unpriced2].map(([m, n]) => `${m} (${n} records)`).join(", ") + ` \u2014 cost above excludes them; add rates under "pricing" in agent-lens.config.json` : "";
|
|
4242
4317
|
const wfRuns = count("SELECT COUNT(*) n FROM workflow_results");
|
|
4243
4318
|
const metaRows = count("SELECT COUNT(*) n FROM session_meta");
|
|
4244
4319
|
const trRows = count("SELECT COUNT(*) n FROM tool_results");
|
|
@@ -4253,12 +4328,13 @@ function printIngestReport(db, dbPath, r) {
|
|
|
4253
4328
|
tool_results=${trRows} (upserted=${r.trStats.upserted} skipped=${r.trStats.skipped} malformed=${r.trStats.malformed})
|
|
4254
4329
|
findings=${r.detected.count} (${sevLine})
|
|
4255
4330
|
file_changes=${r.fileChanges.count}
|
|
4256
|
-
tokens=${totalTokens.toLocaleString()} est_cost=$${cost.toFixed(2)} db=${dbPath}`);
|
|
4331
|
+
tokens=${totalTokens.toLocaleString()} est_cost=$${cost.toFixed(2)} db=${dbPath}` + unpricedLine);
|
|
4257
4332
|
}
|
|
4258
4333
|
function runIngest(argv = process.argv.slice(2)) {
|
|
4259
4334
|
const args = parseIngestArgs(argv);
|
|
4260
4335
|
const archiveRoot = args.archive || resolveArchiveDir();
|
|
4261
4336
|
const { path: dbPath } = resolveDbPath(args.db);
|
|
4337
|
+
installPricing();
|
|
4262
4338
|
if (!existsSync5(archiveRoot)) {
|
|
4263
4339
|
console.error(`agent-lens-ingest: archive not found: ${archiveRoot} (run 'agent-lens collect' first)`);
|
|
4264
4340
|
process.exit(1);
|
|
@@ -4432,6 +4508,9 @@ function splitOf(rows) {
|
|
|
4432
4508
|
function tokensOf(split) {
|
|
4433
4509
|
return split.input + split.output + split.cache_creation + split.cache_read;
|
|
4434
4510
|
}
|
|
4511
|
+
function unpricedOf(rows) {
|
|
4512
|
+
return unpricedModels(rows.filter((u) => u.i || u.o || u.cw || u.cr).map((u) => u.model));
|
|
4513
|
+
}
|
|
4435
4514
|
function costOf(rows) {
|
|
4436
4515
|
let cost = 0;
|
|
4437
4516
|
for (const u of rows) {
|
|
@@ -4594,7 +4673,14 @@ function attachSessionCost(db, rows) {
|
|
|
4594
4673
|
return rows.map(({ ai_title, slug, ...r }) => {
|
|
4595
4674
|
const u = usageBySession.get(r.id) ?? [];
|
|
4596
4675
|
const split = splitOf(u);
|
|
4597
|
-
return {
|
|
4676
|
+
return {
|
|
4677
|
+
...r,
|
|
4678
|
+
tokens: tokensOf(split),
|
|
4679
|
+
token_split: split,
|
|
4680
|
+
cost: costOf(u),
|
|
4681
|
+
unpriced_models: unpricedOf(u),
|
|
4682
|
+
title: ai_title || slug || null
|
|
4683
|
+
};
|
|
4598
4684
|
});
|
|
4599
4685
|
}
|
|
4600
4686
|
function loadToolCalls(db, id) {
|
|
@@ -4739,6 +4825,7 @@ function withSessionTotals(db, session, id, toolRows) {
|
|
|
4739
4825
|
tokens: tokensOf(split),
|
|
4740
4826
|
token_split: split,
|
|
4741
4827
|
cost: costOf(usage),
|
|
4828
|
+
unpriced_models: unpricedOf(usage),
|
|
4742
4829
|
title: session.ai_title || session.slug || null,
|
|
4743
4830
|
tool_call_count: toolRows.length,
|
|
4744
4831
|
tool_error_count,
|
|
@@ -5250,12 +5337,12 @@ function dashboardOverview(db, f) {
|
|
|
5250
5337
|
GROUP BY t.model`, ...w.params);
|
|
5251
5338
|
const tokens = zeroSplit();
|
|
5252
5339
|
let cost = 0;
|
|
5253
|
-
const
|
|
5340
|
+
const unpriced2 = [];
|
|
5254
5341
|
for (const u of usage) {
|
|
5255
5342
|
addUsage(tokens, u);
|
|
5256
5343
|
cost += costForUsage(u.model, usageForCost(u));
|
|
5257
5344
|
if (u.model && !rateForModel(u.model) && (u.i || u.o || u.cw || u.cr))
|
|
5258
|
-
|
|
5345
|
+
unpriced2.push(u.model);
|
|
5259
5346
|
}
|
|
5260
5347
|
const totalTokens = tokens.input + tokens.output + tokens.cache_creation + tokens.cache_read;
|
|
5261
5348
|
const durs = queryAll(db, `SELECT tn.duration_ms d FROM turns tn JOIN sessions s ON s.id = tn.session_id
|
|
@@ -5275,7 +5362,7 @@ function dashboardOverview(db, f) {
|
|
|
5275
5362
|
total_tokens: totalTokens,
|
|
5276
5363
|
cache_read_ratio: totalTokens ? tokens.cache_read / totalTokens : 0,
|
|
5277
5364
|
cost: Number(cost.toFixed(4)),
|
|
5278
|
-
unpriced_models: [...new Set(
|
|
5365
|
+
unpriced_models: [...new Set(unpriced2)].sort(),
|
|
5279
5366
|
turn_duration_ms: { p50: pct(durs, 50), p95: pct(durs, 95), count: durs.length },
|
|
5280
5367
|
session_duration_ms: { p50: pct(sessDurs, 50), p95: pct(sessDurs, 95), count: sessDurs.length },
|
|
5281
5368
|
workflows: workflowAgg(db, f)
|
|
@@ -5564,6 +5651,13 @@ function storage(db, dbPath) {
|
|
|
5564
5651
|
last_ingested: row?.last ?? null
|
|
5565
5652
|
};
|
|
5566
5653
|
}
|
|
5654
|
+
function unpriced(db) {
|
|
5655
|
+
if (!tableExists(db, "token_usage"))
|
|
5656
|
+
return [];
|
|
5657
|
+
const rows = queryAll(db, `SELECT DISTINCT model FROM token_usage
|
|
5658
|
+
WHERE input_tokens + output_tokens + cache_creation_input_tokens + cache_read_input_tokens > 0`);
|
|
5659
|
+
return unpricedModels(rows.map((r) => r.model));
|
|
5660
|
+
}
|
|
5567
5661
|
function engineVersion(db, table, sql, expected) {
|
|
5568
5662
|
const in_data = tableExists(db, table) ? queryAll(db, sql).map((r) => r.v).filter((v) => Number.isFinite(v)) : [];
|
|
5569
5663
|
return { expected, in_data, stale: in_data.some((v) => v < expected) };
|
|
@@ -5594,6 +5688,13 @@ function about(db, ctx) {
|
|
|
5594
5688
|
},
|
|
5595
5689
|
server: { host: ctx.host, port: ctx.port, loopback_only: ctx.loopbackOnly },
|
|
5596
5690
|
retention: { versions_keep_days: retention.keepDays, origin: retention.origin },
|
|
5691
|
+
pricing: {
|
|
5692
|
+
origin: ctx.pricing.origin,
|
|
5693
|
+
models: Object.keys(ctx.pricing.table).length,
|
|
5694
|
+
applied: ctx.pricing.applied,
|
|
5695
|
+
invalid: ctx.pricing.invalid,
|
|
5696
|
+
unpriced: unpriced(db)
|
|
5697
|
+
},
|
|
5597
5698
|
sources: loadSources().map((s) => ({ label: s.label, agent: s.agent, config_dir: s.configDir })),
|
|
5598
5699
|
// storage() already carries last_ingested from the same MAX(ingested_at) it aggregates over —
|
|
5599
5700
|
// reusing it avoids a second query AND keeps the tableExists guard that lastIngested() lacks.
|
|
@@ -5886,6 +5987,7 @@ async function startServer(overrides = {}) {
|
|
|
5886
5987
|
const repoRoot = findRepoRoot(here);
|
|
5887
5988
|
const { path: dbPath, origin: dbOrigin } = resolveDbPath(overrides.db);
|
|
5888
5989
|
const { host, port } = resolveServerConfig({ port: overrides.port, host: overrides.host });
|
|
5990
|
+
const pricing = installPricing();
|
|
5889
5991
|
const webDist = resolveWebDist(here, repoRoot);
|
|
5890
5992
|
if (host !== "127.0.0.1" && host !== "localhost" && !process.env.AGENT_LENS_ALLOW_NONLOCAL) {
|
|
5891
5993
|
console.error(`agent-lens-server: refusing to bind non-loopback host '${host}' (privacy). Set AGENT_LENS_ALLOW_NONLOCAL=1 to override.`);
|
|
@@ -5902,7 +6004,7 @@ async function startServer(overrides = {}) {
|
|
|
5902
6004
|
webDist,
|
|
5903
6005
|
triageDbPath,
|
|
5904
6006
|
enforceLoopbackHost,
|
|
5905
|
-
about: { db: { path: dbPath, origin: dbOrigin }, host, port, loopbackOnly: enforceLoopbackHost, repoRoot }
|
|
6007
|
+
about: { db: { path: dbPath, origin: dbOrigin }, host, port, loopbackOnly: enforceLoopbackHost, repoRoot, pricing }
|
|
5906
6008
|
});
|
|
5907
6009
|
try {
|
|
5908
6010
|
await app.listen({ host, port });
|
|
@@ -6070,6 +6172,7 @@ cli.command("config", "Show the effective configuration (sources, paths, server)
|
|
|
6070
6172
|
const archive = resolveArchiveDir(repoRoot);
|
|
6071
6173
|
const triageDb = triageDbFor(dbPath);
|
|
6072
6174
|
const { keepDays, origin: keepOrigin } = resolveRetention();
|
|
6175
|
+
const pricing = resolvePricing();
|
|
6073
6176
|
console.log("agent-lens config (precedence: flag > env > config file > default)\n");
|
|
6074
6177
|
console.log("Paths");
|
|
6075
6178
|
console.log(` config file ${configFile ?? "(none \u2014 using built-in default source)"}`);
|
|
@@ -6083,6 +6186,10 @@ cli.command("config", "Show the effective configuration (sources, paths, server)
|
|
|
6083
6186
|
console.log(` non-local bind ${process.env.AGENT_LENS_ALLOW_NONLOCAL ? "allowed (AGENT_LENS_ALLOW_NONLOCAL)" : "blocked (loopback only)"}`);
|
|
6084
6187
|
console.log("\nRetention");
|
|
6085
6188
|
console.log(` .versions keep ${keepDays} day(s) [${keepOrigin}]`);
|
|
6189
|
+
console.log("\nPricing");
|
|
6190
|
+
console.log(` models priced ${Object.keys(pricing.table).length} [${pricing.origin}]`);
|
|
6191
|
+
if (pricing.applied.length) console.log(` overrides ${pricing.applied.join(", ")}`);
|
|
6192
|
+
if (pricing.invalid.length) console.log(` ignored (bad) ${pricing.invalid.join(", ")}`);
|
|
6086
6193
|
console.log("\nSources");
|
|
6087
6194
|
for (const s of loadSources()) console.log(` ${s.label.padEnd(14)} ${s.configDir} (${s.agent})`);
|
|
6088
6195
|
});
|