@agentistics/mcp 2.100.0 → 2.101.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/agentistics-mcp.js +490 -194
- package/package.json +1 -1
package/dist/agentistics-mcp.js
CHANGED
|
@@ -43,6 +43,12 @@ var HARNESS_SORT = {
|
|
|
43
43
|
opencode: 6
|
|
44
44
|
};
|
|
45
45
|
var HARNESS_ORDER = Object.keys(HARNESS_SORT).sort((a, b) => HARNESS_SORT[a] - HARNESS_SORT[b]);
|
|
46
|
+
function canonicalProjectPath(path) {
|
|
47
|
+
if (!path)
|
|
48
|
+
return path;
|
|
49
|
+
const m = /^(.*?)\/{1,2}(?:\.?claude\/worktrees|\.worktrees)\//.exec(path);
|
|
50
|
+
return m && m[1] ? m[1] : path;
|
|
51
|
+
}
|
|
46
52
|
var BASE_MODEL_PRICING = {
|
|
47
53
|
"claude-fable-5": { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
|
|
48
54
|
"claude-mythos-5": { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
|
|
@@ -216,6 +222,114 @@ function redactSecrets(text) {
|
|
|
216
222
|
out = out.replace(BEARER, (whole, prefix, value) => guarded(whole, prefix, value));
|
|
217
223
|
return out;
|
|
218
224
|
}
|
|
225
|
+
// ../core/src/projection-surfaces.ts
|
|
226
|
+
var PROJECTION_SURFACES_ENV = "AGENTISTICS_PROJECTIONS_SURFACES";
|
|
227
|
+
var PROJECTION_SURFACE_IDS = ["mcp", "vscode", "tui", "web"];
|
|
228
|
+
var LEGACY_SURFACES_VALUES = ["legacy", "none"];
|
|
229
|
+
function projectionSurfaces(env = process.env) {
|
|
230
|
+
const names = (env[PROJECTION_SURFACES_ENV] ?? "").split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
231
|
+
if (names.length === 0 || names.includes("all"))
|
|
232
|
+
return [...PROJECTION_SURFACE_IDS];
|
|
233
|
+
if (names.some((n) => LEGACY_SURFACES_VALUES.includes(n)))
|
|
234
|
+
return [];
|
|
235
|
+
return PROJECTION_SURFACE_IDS.filter((id) => names.includes(id));
|
|
236
|
+
}
|
|
237
|
+
function projectionSurfaceOn(surface, env = process.env) {
|
|
238
|
+
return projectionSurfaces(env).includes(surface);
|
|
239
|
+
}
|
|
240
|
+
// ../core/src/projection-client.ts
|
|
241
|
+
class ProjectionUnavailable extends Error {
|
|
242
|
+
status;
|
|
243
|
+
code;
|
|
244
|
+
constructor(status, code) {
|
|
245
|
+
super(`projections unavailable (${status} ${code})`);
|
|
246
|
+
this.status = status;
|
|
247
|
+
this.code = code;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
var MAX_PAGES = 200;
|
|
251
|
+
function metricsSearchParams(params) {
|
|
252
|
+
const p = new URLSearchParams;
|
|
253
|
+
for (const [k, v] of Object.entries(params)) {
|
|
254
|
+
if (typeof v === "string")
|
|
255
|
+
p.append(k, v);
|
|
256
|
+
else
|
|
257
|
+
for (const one of v)
|
|
258
|
+
p.append(k, one);
|
|
259
|
+
}
|
|
260
|
+
return p;
|
|
261
|
+
}
|
|
262
|
+
async function allMetricGroups(q, params) {
|
|
263
|
+
const groups = [];
|
|
264
|
+
let cursor = null;
|
|
265
|
+
let basis;
|
|
266
|
+
for (let i = 0;i < MAX_PAGES; i++) {
|
|
267
|
+
const p = metricsSearchParams({ ...params, limit: "1000" });
|
|
268
|
+
if (cursor)
|
|
269
|
+
p.set("cursor", cursor);
|
|
270
|
+
const a = await q(p);
|
|
271
|
+
if (!a.ok)
|
|
272
|
+
throw new ProjectionUnavailable(a.status, a.error);
|
|
273
|
+
groups.push(...a.body.groups);
|
|
274
|
+
basis = a.body.basis;
|
|
275
|
+
cursor = a.body.page.nextCursor;
|
|
276
|
+
if (!cursor)
|
|
277
|
+
return { groups, basis };
|
|
278
|
+
}
|
|
279
|
+
throw new Error(`metrics query did not finish in ${MAX_PAGES} pages`);
|
|
280
|
+
}
|
|
281
|
+
var tok = (t) => ({
|
|
282
|
+
input: t?.input ?? 0,
|
|
283
|
+
output: t?.output ?? 0,
|
|
284
|
+
cacheRead: t?.cacheRead ?? 0,
|
|
285
|
+
cacheWrite: t?.cacheWrite ?? 0,
|
|
286
|
+
total: t?.total ?? 0
|
|
287
|
+
});
|
|
288
|
+
function harnessScope(harness) {
|
|
289
|
+
return harness && harness !== "all" ? { harness } : {};
|
|
290
|
+
}
|
|
291
|
+
async function projectedRollup(q, dim, harness) {
|
|
292
|
+
const by = (extra) => [...dim ? [dim] : [], ...extra].join(",");
|
|
293
|
+
const base = harnessScope(harness);
|
|
294
|
+
const [all, main, days] = await Promise.all([
|
|
295
|
+
allMetricGroups(q, { ...base, ...dim ? { groupBy: by([]) } : {}, metrics: "sessions,messages,tokens,cost" }),
|
|
296
|
+
allMetricGroups(q, { ...base, ...dim ? { groupBy: by([]) } : {}, subagent: "false", metrics: "tokens,cost" }),
|
|
297
|
+
allMetricGroups(q, { ...base, groupBy: by(["day"]), metrics: "sessions" })
|
|
298
|
+
]);
|
|
299
|
+
const keyOf = (g) => dim ? g.key[dim] ?? null : null;
|
|
300
|
+
const out = new Map;
|
|
301
|
+
const row = (k) => {
|
|
302
|
+
let r = out.get(k);
|
|
303
|
+
if (!r) {
|
|
304
|
+
r = { key: k, sessions: 0, personTurns: null, main: tok(undefined), mainCost: 0, allTokens: 0, allCost: 0, costPartial: false, lastActive: null };
|
|
305
|
+
out.set(k, r);
|
|
306
|
+
}
|
|
307
|
+
return r;
|
|
308
|
+
};
|
|
309
|
+
for (const g of all.groups) {
|
|
310
|
+
const r = row(keyOf(g));
|
|
311
|
+
r.sessions = g.metrics.sessions?.count ?? 0;
|
|
312
|
+
r.personTurns = g.metrics.messages ? g.metrics.messages.value : null;
|
|
313
|
+
r.allTokens = g.metrics.tokens?.total ?? 0;
|
|
314
|
+
r.allCost = g.metrics.cost?.usd ?? 0;
|
|
315
|
+
r.costPartial ||= g.metrics.cost?.partial ?? false;
|
|
316
|
+
}
|
|
317
|
+
for (const g of main.groups) {
|
|
318
|
+
const r = row(keyOf(g));
|
|
319
|
+
r.main = tok(g.metrics.tokens);
|
|
320
|
+
r.mainCost = g.metrics.cost?.usd ?? 0;
|
|
321
|
+
r.costPartial ||= g.metrics.cost?.partial ?? false;
|
|
322
|
+
}
|
|
323
|
+
for (const g of days.groups) {
|
|
324
|
+
const d = g.key.day;
|
|
325
|
+
if (!d)
|
|
326
|
+
continue;
|
|
327
|
+
const r = row(keyOf(g));
|
|
328
|
+
if (!r.lastActive || d > r.lastActive)
|
|
329
|
+
r.lastActive = d;
|
|
330
|
+
}
|
|
331
|
+
return out;
|
|
332
|
+
}
|
|
219
333
|
// ../core/src/plan-catalog.ts
|
|
220
334
|
var CLAUDE_PRICING = "https://claude.com/pricing";
|
|
221
335
|
var COPILOT_PRICING = "https://github.com/features/copilot/plans";
|
|
@@ -490,6 +604,7 @@ var EVENT_TYPES = [
|
|
|
490
604
|
"tool.approved",
|
|
491
605
|
"tool.denied",
|
|
492
606
|
"tool.progress",
|
|
607
|
+
"tool.executed",
|
|
493
608
|
"mcp.requested",
|
|
494
609
|
"mcp.completed",
|
|
495
610
|
"browser.session.started",
|
|
@@ -504,6 +619,7 @@ var EVENT_TYPES = [
|
|
|
504
619
|
"browser.tab.closed",
|
|
505
620
|
"context.compacted",
|
|
506
621
|
"context.window.observed",
|
|
622
|
+
"context.recalled",
|
|
507
623
|
"policy.requested",
|
|
508
624
|
"policy.approved",
|
|
509
625
|
"policy.denied",
|
|
@@ -800,12 +916,12 @@ var AGENT_TOOL_POLICY = {
|
|
|
800
916
|
routes: ["POST /api/session-notify"],
|
|
801
917
|
reason: "a per-session on/off switch for delivery; reversible, and the session state is untouched"
|
|
802
918
|
},
|
|
803
|
-
agentistics_summary: { ...DATA, risk: "R", routes: ["GET /api/data"], reason: "reads computed metrics" },
|
|
804
|
-
agentistics_harnesses: { ...DATA, risk: "R", routes: ["GET /api/data"], reason: "reads computed metrics per harness" },
|
|
805
|
-
agentistics_projects: { ...DATA, risk: "R", routes: ["GET /api/data"], reason: "reads computed metrics per project" },
|
|
919
|
+
agentistics_summary: { ...DATA, capability: "localTranscripts", host: true, risk: "R", routes: ["GET /api/data", "GET /api/runtime/metrics"], reason: "reads computed metrics" },
|
|
920
|
+
agentistics_harnesses: { ...DATA, capability: "localTranscripts", host: true, risk: "R", routes: ["GET /api/data", "GET /api/runtime/metrics"], reason: "reads computed metrics per harness" },
|
|
921
|
+
agentistics_projects: { ...DATA, capability: "localTranscripts", host: true, risk: "R", routes: ["GET /api/data", "GET /api/runtime/metrics"], reason: "reads computed metrics per project" },
|
|
806
922
|
agentistics_sessions: { ...DATA, risk: "R", routes: ["GET /api/data"], reason: "reads session metadata (first_prompt is untrusted text, §4.8)" },
|
|
807
|
-
agentistics_costs: { ...DATA, risk: "R", routes: ["GET /api/data"], reason: "reads computed costs" },
|
|
808
|
-
agentistics_repos: { ...DATA, risk: "R", routes: ["GET /api/data"], reason: "reads computed metrics per repository" },
|
|
923
|
+
agentistics_costs: { ...DATA, capability: "localTranscripts", host: true, risk: "R", routes: ["GET /api/data", "GET /api/runtime/metrics"], reason: "reads computed costs" },
|
|
924
|
+
agentistics_repos: { ...DATA, capability: "localTranscripts", host: true, risk: "R", routes: ["GET /api/data", "GET /api/runtime/metrics"], reason: "reads computed metrics per repository" },
|
|
809
925
|
agentistics_component_catalog: { ...DATA, risk: "R", routes: [], reason: "returns a static list; calls nothing" },
|
|
810
926
|
agentistics_get_layouts: { ...PREFS, risk: "R", routes: ["GET /api/preferences"], reason: "reads the saved layouts" },
|
|
811
927
|
agentistics_create_layout: { ...PREFS, risk: "W", routes: ["GET /api/preferences", "PUT /api/preferences"], reason: "adds a layout; removed by delete_layout" },
|
|
@@ -845,7 +961,9 @@ function sessionTokens2(s) {
|
|
|
845
961
|
input_tokens: input,
|
|
846
962
|
output_tokens: output,
|
|
847
963
|
cache_read_input_tokens: cacheRead,
|
|
848
|
-
cache_creation_input_tokens: cacheWrite
|
|
964
|
+
cache_creation_input_tokens: cacheWrite,
|
|
965
|
+
cache_creation_1h_input_tokens: s.cache_creation_1h_input_tokens,
|
|
966
|
+
cache_creation_5m_input_tokens: s.cache_creation_5m_input_tokens
|
|
849
967
|
}) ?? calcCost({
|
|
850
968
|
inputTokens: input,
|
|
851
969
|
outputTokens: output,
|
|
@@ -1120,6 +1238,325 @@ function taskCommentRequest(args) {
|
|
|
1120
1238
|
};
|
|
1121
1239
|
}
|
|
1122
1240
|
|
|
1241
|
+
// legacy-analytics.ts
|
|
1242
|
+
function legacySummary(data, harness) {
|
|
1243
|
+
const unified = !harness || harness === "all";
|
|
1244
|
+
const sc = data.statsCache ?? {};
|
|
1245
|
+
const totals = statsCacheTotals(sc);
|
|
1246
|
+
const allSessions = filterSessions(data.sessions ?? [], harness);
|
|
1247
|
+
let totalCostUSD = 0;
|
|
1248
|
+
let totalInput = 0, totalOutput = 0, totalCacheRead = 0, totalCacheWrite = 0;
|
|
1249
|
+
const modelTokens = {};
|
|
1250
|
+
const projectSessions = {};
|
|
1251
|
+
const activeDates = new Set;
|
|
1252
|
+
for (const s of allSessions) {
|
|
1253
|
+
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
1254
|
+
totalInput += input;
|
|
1255
|
+
totalOutput += output;
|
|
1256
|
+
totalCacheRead += cacheRead;
|
|
1257
|
+
totalCacheWrite += cacheWrite;
|
|
1258
|
+
totalCostUSD += cost;
|
|
1259
|
+
if (s.model)
|
|
1260
|
+
modelTokens[s.model] = (modelTokens[s.model] ?? 0) + input + output;
|
|
1261
|
+
if (s.project_path) {
|
|
1262
|
+
const root = canonicalProjectPath(s.project_path);
|
|
1263
|
+
projectSessions[root] = (projectSessions[root] ?? 0) + 1;
|
|
1264
|
+
}
|
|
1265
|
+
if (s.start_time)
|
|
1266
|
+
activeDates.add(String(s.start_time).slice(0, 10));
|
|
1267
|
+
}
|
|
1268
|
+
const claudeFallback = unified || harness === "claude";
|
|
1269
|
+
const topModel = Object.entries(modelTokens).sort(([, a], [, b]) => b - a)[0]?.[0] ?? (claudeFallback ? totals.topModel ?? "—" : "—");
|
|
1270
|
+
const topProject = Object.entries(projectSessions).sort(([, a], [, b]) => b - a)[0]?.[0] ?? "—";
|
|
1271
|
+
return {
|
|
1272
|
+
harness: unified ? "all" : harness,
|
|
1273
|
+
totalInputTokens: totalInput || (claudeFallback ? totals.input : 0),
|
|
1274
|
+
totalOutputTokens: totalOutput || (claudeFallback ? totals.output : 0),
|
|
1275
|
+
totalCacheReadTokens: totalCacheRead || (claudeFallback ? totals.cacheRead : 0),
|
|
1276
|
+
totalCacheWriteTokens: totalCacheWrite || (claudeFallback ? totals.cacheWrite : 0),
|
|
1277
|
+
estimatedCostUSD: Math.round((totalCostUSD || (claudeFallback ? totals.cost : 0)) * 100) / 100,
|
|
1278
|
+
totalSessions: allSessions.length,
|
|
1279
|
+
topModel,
|
|
1280
|
+
topProject,
|
|
1281
|
+
activeDays: activeDates.size || (claudeFallback ? sc.activeDays ?? 0 : 0),
|
|
1282
|
+
currentStreak: claudeFallback ? sc.currentStreak ?? 0 : null
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
function legacyHarnesses(data) {
|
|
1286
|
+
const present = data.harnesses ?? ["claude"];
|
|
1287
|
+
const sessions = data.sessions ?? [];
|
|
1288
|
+
const rows = present.map((h) => {
|
|
1289
|
+
const hs = sessions.filter((s) => sessionHarness(s) === h);
|
|
1290
|
+
let input = 0, output = 0, cacheRead = 0, cacheWrite = 0, cost = 0, messages = 0, lastActive = "";
|
|
1291
|
+
for (const s of hs) {
|
|
1292
|
+
const t = sessionTokens2(s);
|
|
1293
|
+
input += t.input;
|
|
1294
|
+
output += t.output;
|
|
1295
|
+
cacheRead += t.cacheRead;
|
|
1296
|
+
cacheWrite += t.cacheWrite;
|
|
1297
|
+
cost += t.cost;
|
|
1298
|
+
messages += sessionMessages(s);
|
|
1299
|
+
if (s.start_time && String(s.start_time) > lastActive)
|
|
1300
|
+
lastActive = String(s.start_time);
|
|
1301
|
+
}
|
|
1302
|
+
return {
|
|
1303
|
+
harness: h,
|
|
1304
|
+
sessions: hs.length,
|
|
1305
|
+
messages,
|
|
1306
|
+
inputTokens: input,
|
|
1307
|
+
outputTokens: output,
|
|
1308
|
+
cacheReadTokens: cacheRead,
|
|
1309
|
+
cacheWriteTokens: cacheWrite,
|
|
1310
|
+
totalTokens: input + output + cacheRead + cacheWrite,
|
|
1311
|
+
estimatedCostUSD: Math.round(cost * 100) / 100,
|
|
1312
|
+
lastActive: lastActive || null
|
|
1313
|
+
};
|
|
1314
|
+
}).sort((a, b) => b.totalTokens - a.totalTokens);
|
|
1315
|
+
return rows;
|
|
1316
|
+
}
|
|
1317
|
+
function legacyProjects(data, harness) {
|
|
1318
|
+
const allSessions = filterSessions(data.sessions ?? [], harness);
|
|
1319
|
+
const byPath = {};
|
|
1320
|
+
for (const s of allSessions) {
|
|
1321
|
+
const key = canonicalProjectPath(s.project_path);
|
|
1322
|
+
if (!key)
|
|
1323
|
+
continue;
|
|
1324
|
+
if (!byPath[key])
|
|
1325
|
+
byPath[key] = { sessions: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0, costUSD: 0, messages: 0, lastActive: "", languages: [] };
|
|
1326
|
+
const agg = byPath[key];
|
|
1327
|
+
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
1328
|
+
agg.sessions += 1;
|
|
1329
|
+
agg.inputTokens += input;
|
|
1330
|
+
agg.outputTokens += output;
|
|
1331
|
+
agg.cacheRead += cacheRead;
|
|
1332
|
+
agg.cacheWrite += cacheWrite;
|
|
1333
|
+
agg.costUSD += cost;
|
|
1334
|
+
agg.messages += sessionMessages(s);
|
|
1335
|
+
if (!agg.lastActive || (s.start_time ?? "") > agg.lastActive)
|
|
1336
|
+
agg.lastActive = s.start_time ?? "";
|
|
1337
|
+
for (const lang of s.languages ?? [])
|
|
1338
|
+
if (!agg.languages.includes(lang))
|
|
1339
|
+
agg.languages.push(lang);
|
|
1340
|
+
}
|
|
1341
|
+
const roots = new Map;
|
|
1342
|
+
for (const p of data.projects ?? []) {
|
|
1343
|
+
const path = canonicalProjectPath(p.path ?? "");
|
|
1344
|
+
const r = roots.get(path) ?? { name: p.name, path, sessions: [] };
|
|
1345
|
+
if (p.path === path)
|
|
1346
|
+
r.name = p.name;
|
|
1347
|
+
r.sessions.push(...p.sessions ?? []);
|
|
1348
|
+
roots.set(path, r);
|
|
1349
|
+
}
|
|
1350
|
+
const projects = [...roots.values()];
|
|
1351
|
+
const summary = projects.filter((p) => !harness || harness === "all" || byPath[p.path]).map((p) => {
|
|
1352
|
+
const agg = byPath[p.path] ?? { sessions: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0, costUSD: 0, messages: 0, lastActive: "", languages: [] };
|
|
1353
|
+
return {
|
|
1354
|
+
name: p.name,
|
|
1355
|
+
path: p.path,
|
|
1356
|
+
sessions: !harness || harness === "all" ? (p.sessions ?? []).length : agg.sessions,
|
|
1357
|
+
messages: agg.messages,
|
|
1358
|
+
inputTokens: agg.inputTokens,
|
|
1359
|
+
outputTokens: agg.outputTokens,
|
|
1360
|
+
totalTokens: agg.inputTokens + agg.outputTokens,
|
|
1361
|
+
estimatedCostUSD: Math.round(agg.costUSD * 1e4) / 1e4,
|
|
1362
|
+
lastActive: agg.lastActive || null,
|
|
1363
|
+
languages: agg.languages
|
|
1364
|
+
};
|
|
1365
|
+
}).sort((a, b) => b.totalTokens - a.totalTokens);
|
|
1366
|
+
return summary;
|
|
1367
|
+
}
|
|
1368
|
+
function legacyCosts(data, harness) {
|
|
1369
|
+
if (!harness || harness === "all" || harness === "claude") {
|
|
1370
|
+
const usage = data.statsCache?.modelUsage ?? {};
|
|
1371
|
+
const breakdown = Object.entries(usage).map(([model, u]) => ({
|
|
1372
|
+
model,
|
|
1373
|
+
inputTokens: u.inputTokens ?? 0,
|
|
1374
|
+
outputTokens: u.outputTokens ?? 0,
|
|
1375
|
+
cacheReadTokens: u.cacheReadTokens ?? 0,
|
|
1376
|
+
cacheWriteTokens: u.cacheWriteTokens ?? 0,
|
|
1377
|
+
totalTokens: u.totalTokens ?? 0,
|
|
1378
|
+
estimatedCostUSD: u.costUSD ?? 0
|
|
1379
|
+
})).sort((a, b) => b.totalTokens - a.totalTokens);
|
|
1380
|
+
return breakdown;
|
|
1381
|
+
}
|
|
1382
|
+
const byModel = {};
|
|
1383
|
+
for (const s of filterSessions(data.sessions ?? [], harness)) {
|
|
1384
|
+
const model = s.model || "unknown";
|
|
1385
|
+
if (!byModel[model])
|
|
1386
|
+
byModel[model] = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, estimatedCostUSD: 0 };
|
|
1387
|
+
const agg = byModel[model];
|
|
1388
|
+
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
1389
|
+
agg.inputTokens += input;
|
|
1390
|
+
agg.outputTokens += output;
|
|
1391
|
+
agg.cacheReadTokens += cacheRead;
|
|
1392
|
+
agg.cacheWriteTokens += cacheWrite;
|
|
1393
|
+
agg.estimatedCostUSD += cost;
|
|
1394
|
+
}
|
|
1395
|
+
const breakdown = Object.entries(byModel).map(([model, u]) => ({
|
|
1396
|
+
model,
|
|
1397
|
+
inputTokens: u.inputTokens,
|
|
1398
|
+
outputTokens: u.outputTokens,
|
|
1399
|
+
cacheReadTokens: u.cacheReadTokens,
|
|
1400
|
+
cacheWriteTokens: u.cacheWriteTokens,
|
|
1401
|
+
totalTokens: u.inputTokens + u.outputTokens + u.cacheReadTokens + u.cacheWriteTokens,
|
|
1402
|
+
estimatedCostUSD: Math.round(u.estimatedCostUSD * 1e4) / 1e4
|
|
1403
|
+
})).sort((a, b) => b.totalTokens - a.totalTokens);
|
|
1404
|
+
return breakdown;
|
|
1405
|
+
}
|
|
1406
|
+
function legacyRepos(data, harness) {
|
|
1407
|
+
const allSessions = filterSessions(data.sessions ?? [], harness);
|
|
1408
|
+
const byRemote = {};
|
|
1409
|
+
for (const s of allSessions) {
|
|
1410
|
+
const key = s.git_remote || "";
|
|
1411
|
+
if (!byRemote[key])
|
|
1412
|
+
byRemote[key] = { sessions: 0, messages: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0, costUSD: 0, lastActive: "" };
|
|
1413
|
+
const agg = byRemote[key];
|
|
1414
|
+
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
1415
|
+
agg.sessions += 1;
|
|
1416
|
+
agg.messages += sessionMessages(s);
|
|
1417
|
+
agg.inputTokens += input;
|
|
1418
|
+
agg.outputTokens += output;
|
|
1419
|
+
agg.cacheRead += cacheRead;
|
|
1420
|
+
agg.cacheWrite += cacheWrite;
|
|
1421
|
+
agg.costUSD += cost;
|
|
1422
|
+
if (s.start_time && String(s.start_time) > agg.lastActive)
|
|
1423
|
+
agg.lastActive = String(s.start_time);
|
|
1424
|
+
}
|
|
1425
|
+
const rows = Object.entries(byRemote).map(([remote, agg]) => ({
|
|
1426
|
+
repo: remote || "unlinked",
|
|
1427
|
+
remote: remote || null,
|
|
1428
|
+
sessions: agg.sessions,
|
|
1429
|
+
messages: agg.messages,
|
|
1430
|
+
inputTokens: agg.inputTokens,
|
|
1431
|
+
outputTokens: agg.outputTokens,
|
|
1432
|
+
totalTokens: agg.inputTokens + agg.outputTokens + agg.cacheRead + agg.cacheWrite,
|
|
1433
|
+
estimatedCostUSD: Math.round(agg.costUSD * 1e4) / 1e4,
|
|
1434
|
+
lastActive: agg.lastActive || null
|
|
1435
|
+
})).sort((a, b) => b.totalTokens - a.totalTokens);
|
|
1436
|
+
return rows;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// projected-analytics.ts
|
|
1440
|
+
var round = (n, places) => Math.round(n * 10 ** places) / 10 ** places;
|
|
1441
|
+
function spend(r, places) {
|
|
1442
|
+
return {
|
|
1443
|
+
inputTokens: r.main.input,
|
|
1444
|
+
outputTokens: r.main.output,
|
|
1445
|
+
cacheReadTokens: r.main.cacheRead,
|
|
1446
|
+
cacheWriteTokens: r.main.cacheWrite,
|
|
1447
|
+
totalTokens: r.main.input + r.main.output + r.main.cacheRead + r.main.cacheWrite,
|
|
1448
|
+
estimatedCostUSD: round(r.mainCost, places),
|
|
1449
|
+
costPartial: r.costPartial,
|
|
1450
|
+
subagentTokens: Math.max(0, r.allTokens - r.main.total),
|
|
1451
|
+
subagentCostUSD: round(Math.max(0, r.allCost - r.mainCost), places)
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
async function projectedHarnesses(q) {
|
|
1455
|
+
const rows = [...(await projectedRollup(q, "harness")).values()].map((r) => ({
|
|
1456
|
+
harness: r.key,
|
|
1457
|
+
sessions: r.sessions,
|
|
1458
|
+
personTurns: r.personTurns,
|
|
1459
|
+
messages: r.personTurns,
|
|
1460
|
+
...spend(r, 2),
|
|
1461
|
+
lastActive: r.lastActive,
|
|
1462
|
+
source: "projections"
|
|
1463
|
+
}));
|
|
1464
|
+
return rows.sort((a, b) => b.totalTokens - a.totalTokens);
|
|
1465
|
+
}
|
|
1466
|
+
async function projectedProjects(q, harness) {
|
|
1467
|
+
const rows = [...(await projectedRollup(q, "project", harness)).values()].filter((r) => r.key).map((r) => {
|
|
1468
|
+
const s = spend(r, 4);
|
|
1469
|
+
return {
|
|
1470
|
+
name: r.key.split(/[\\/]/).filter(Boolean).pop() ?? r.key,
|
|
1471
|
+
path: r.key,
|
|
1472
|
+
sessions: r.sessions,
|
|
1473
|
+
personTurns: r.personTurns,
|
|
1474
|
+
messages: r.personTurns,
|
|
1475
|
+
inputTokens: s.inputTokens,
|
|
1476
|
+
outputTokens: s.outputTokens,
|
|
1477
|
+
totalTokens: s.inputTokens + s.outputTokens,
|
|
1478
|
+
estimatedCostUSD: s.estimatedCostUSD,
|
|
1479
|
+
costPartial: s.costPartial,
|
|
1480
|
+
subagentTokens: s.subagentTokens,
|
|
1481
|
+
subagentCostUSD: s.subagentCostUSD,
|
|
1482
|
+
lastActive: r.lastActive,
|
|
1483
|
+
languages: null,
|
|
1484
|
+
source: "projections"
|
|
1485
|
+
};
|
|
1486
|
+
});
|
|
1487
|
+
return rows.sort((a, b) => b.totalTokens - a.totalTokens);
|
|
1488
|
+
}
|
|
1489
|
+
async function projectedRepos(q, harness) {
|
|
1490
|
+
const rows = [...(await projectedRollup(q, "repo", harness)).values()].map((r) => ({
|
|
1491
|
+
repo: r.key || "unlinked",
|
|
1492
|
+
remote: r.key || null,
|
|
1493
|
+
sessions: r.sessions,
|
|
1494
|
+
personTurns: r.personTurns,
|
|
1495
|
+
messages: r.personTurns,
|
|
1496
|
+
...spend(r, 4),
|
|
1497
|
+
lastActive: r.lastActive,
|
|
1498
|
+
source: "projections"
|
|
1499
|
+
}));
|
|
1500
|
+
return rows.sort((a, b) => b.totalTokens - a.totalTokens);
|
|
1501
|
+
}
|
|
1502
|
+
async function projectedCosts(q, harness) {
|
|
1503
|
+
const rows = [...(await projectedRollup(q, "model", harness)).values()].map((r) => {
|
|
1504
|
+
const { costPartial, subagentTokens, subagentCostUSD, ...s } = spend(r, 4);
|
|
1505
|
+
return { model: r.key ?? "unknown", ...s, costPartial, subagentTokens, subagentCostUSD, source: "projections" };
|
|
1506
|
+
});
|
|
1507
|
+
return rows.sort((a, b) => b.totalTokens - a.totalTokens);
|
|
1508
|
+
}
|
|
1509
|
+
function streakOf(days, today) {
|
|
1510
|
+
const set = new Set(days);
|
|
1511
|
+
const prev = (d) => new Date(Date.parse(`${d}T00:00:00Z`) - 86400000).toISOString().slice(0, 10);
|
|
1512
|
+
let d = set.has(today) ? today : prev(today);
|
|
1513
|
+
let n = 0;
|
|
1514
|
+
while (set.has(d)) {
|
|
1515
|
+
n++;
|
|
1516
|
+
d = prev(d);
|
|
1517
|
+
}
|
|
1518
|
+
return n;
|
|
1519
|
+
}
|
|
1520
|
+
async function projectedSummary(q, harness, now = () => new Date) {
|
|
1521
|
+
const unified = !harness || harness === "all";
|
|
1522
|
+
const base = harnessScope(harness);
|
|
1523
|
+
const [totals, models, projects, days] = await Promise.all([
|
|
1524
|
+
projectedRollup(q, null, harness),
|
|
1525
|
+
allMetricGroups(q, { ...base, groupBy: "model", subagent: "false", metrics: "tokens" }),
|
|
1526
|
+
allMetricGroups(q, { ...base, groupBy: "project", metrics: "sessions" }),
|
|
1527
|
+
allMetricGroups(q, { ...base, groupBy: "day", metrics: "sessions" })
|
|
1528
|
+
]);
|
|
1529
|
+
const t = totals.get(null) ?? null;
|
|
1530
|
+
const top = (gs, dim, n) => gs.filter((g) => g.key[dim]).sort((a, b) => n(b) - n(a))[0]?.key[dim] ?? "—";
|
|
1531
|
+
const activeDays = days.groups.map((g) => g.key.day).filter((d) => !!d);
|
|
1532
|
+
const s = t ? spend(t, 2) : null;
|
|
1533
|
+
return {
|
|
1534
|
+
harness: unified ? "all" : harness,
|
|
1535
|
+
totalInputTokens: s?.inputTokens ?? 0,
|
|
1536
|
+
totalOutputTokens: s?.outputTokens ?? 0,
|
|
1537
|
+
totalCacheReadTokens: s?.cacheReadTokens ?? 0,
|
|
1538
|
+
totalCacheWriteTokens: s?.cacheWriteTokens ?? 0,
|
|
1539
|
+
estimatedCostUSD: s?.estimatedCostUSD ?? 0,
|
|
1540
|
+
costPartial: s?.costPartial ?? false,
|
|
1541
|
+
subagentTokens: s?.subagentTokens ?? 0,
|
|
1542
|
+
subagentCostUSD: s?.subagentCostUSD ?? 0,
|
|
1543
|
+
totalSessions: t?.sessions ?? 0,
|
|
1544
|
+
topModel: top(models.groups, "model", (g) => (g.metrics.tokens?.input ?? 0) + (g.metrics.tokens?.output ?? 0)),
|
|
1545
|
+
topProject: top(projects.groups, "project", (g) => g.metrics.sessions?.count ?? 0),
|
|
1546
|
+
activeDays: activeDays.length,
|
|
1547
|
+
currentStreak: streakOf(activeDays, now().toISOString().slice(0, 10)),
|
|
1548
|
+
freshness: days.basis?.freshness ?? null,
|
|
1549
|
+
source: "projections"
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
var PROJECTED_TOOLS = {
|
|
1553
|
+
agentistics_summary: projectedSummary,
|
|
1554
|
+
agentistics_harnesses: (q) => projectedHarnesses(q),
|
|
1555
|
+
agentistics_projects: projectedProjects,
|
|
1556
|
+
agentistics_costs: projectedCosts,
|
|
1557
|
+
agentistics_repos: projectedRepos
|
|
1558
|
+
};
|
|
1559
|
+
|
|
1123
1560
|
// agentistics-mcp.ts
|
|
1124
1561
|
var API = process.env.AGENTISTICS_API ?? "http://localhost:47291";
|
|
1125
1562
|
var HARNESS_PARAM = harnessParam();
|
|
@@ -1159,6 +1596,30 @@ async function apiGet(path) {
|
|
|
1159
1596
|
throw new Error(`agentistics API ${path} → HTTP ${res.status}`);
|
|
1160
1597
|
return res.json();
|
|
1161
1598
|
}
|
|
1599
|
+
var LEGACY_TOOLS = {
|
|
1600
|
+
agentistics_summary: legacySummary,
|
|
1601
|
+
agentistics_harnesses: (data) => legacyHarnesses(data),
|
|
1602
|
+
agentistics_projects: legacyProjects,
|
|
1603
|
+
agentistics_costs: legacyCosts,
|
|
1604
|
+
agentistics_repos: legacyRepos
|
|
1605
|
+
};
|
|
1606
|
+
async function analytics(tool, harness, from) {
|
|
1607
|
+
if (projectionSurfaceOn("mcp")) {
|
|
1608
|
+
const query = async (params) => {
|
|
1609
|
+
const res = await from.metrics(params);
|
|
1610
|
+
const body = await res.json().catch(() => ({}));
|
|
1611
|
+
return res.ok ? { ok: true, body } : { ok: false, status: res.status, error: String(body?.error ?? res.status) };
|
|
1612
|
+
};
|
|
1613
|
+
try {
|
|
1614
|
+
return await PROJECTED_TOOLS[tool](query, harness);
|
|
1615
|
+
} catch (err) {
|
|
1616
|
+
if (!(err instanceof ProjectionUnavailable))
|
|
1617
|
+
throw err;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
return LEGACY_TOOLS[tool](await from.legacy(), harness);
|
|
1621
|
+
}
|
|
1622
|
+
var text = (value) => ({ content: [{ type: "text", text: JSON.stringify(value, null, 2) }] });
|
|
1162
1623
|
async function getPrefs() {
|
|
1163
1624
|
return apiGet("/api/preferences");
|
|
1164
1625
|
}
|
|
@@ -1538,12 +1999,12 @@ var TOOLS = [
|
|
|
1538
1999
|
},
|
|
1539
2000
|
{
|
|
1540
2001
|
name: "agentistics_harnesses",
|
|
1541
|
-
description: "Compare the tracked AI coding harnesses side by side. Lists every harness present in the data with its sessions, messages, token usage, estimated cost, and last-active date. Use this to answer 'which harness do I use most / costs most' questions.",
|
|
2002
|
+
description: "Compare the tracked AI coding harnesses side by side. Lists every harness present in the data with its sessions, messages, token usage, estimated cost, and last-active date. Use this to answer 'which harness do I use most / costs most' questions. Rows tagged source \"projections\" (read from the journal) carry personTurns (the person's turns); there `messages` is a deprecated alias of personTurns, and subagent spend is reported apart (subagentTokens, subagentCostUSD).",
|
|
1542
2003
|
inputSchema: { type: "object", properties: {}, required: [] }
|
|
1543
2004
|
},
|
|
1544
2005
|
{
|
|
1545
2006
|
name: "agentistics_projects",
|
|
1546
|
-
description:
|
|
2007
|
+
description: 'List projects with session counts, message counts, token usage, estimated cost, and last active date. Optionally scope to a single harness. Rows tagged source "projections" (read from the journal) carry personTurns (the person\'s turns); there `messages` is a deprecated alias of personTurns, and subagent spend is reported apart (subagentTokens, subagentCostUSD).',
|
|
1547
2008
|
inputSchema: { type: "object", properties: { harness: HARNESS_PARAM }, required: [] }
|
|
1548
2009
|
},
|
|
1549
2010
|
{
|
|
@@ -1720,7 +2181,7 @@ var TOOLS = [
|
|
|
1720
2181
|
},
|
|
1721
2182
|
{
|
|
1722
2183
|
name: "agentistics_repos",
|
|
1723
|
-
description: "List repositories, grouped by normalized git remote (independent of local path or which machine produced the session), with session/message/token/cost totals and last-active date. Sessions with no linked repository are grouped under 'unlinked'. Optionally scope to a single harness.",
|
|
2184
|
+
description: "List repositories, grouped by normalized git remote (independent of local path or which machine produced the session), with session/message/token/cost totals and last-active date. Sessions with no linked repository are grouped under 'unlinked'. Optionally scope to a single harness. Rows tagged source \"projections\" (read from the journal) carry personTurns (the person's turns); there `messages` is a deprecated alias of personTurns, and subagent spend is reported apart (subagentTokens, subagentCostUSD).",
|
|
1724
2185
|
inputSchema: { type: "object", properties: { harness: HARNESS_PARAM }, required: [] }
|
|
1725
2186
|
},
|
|
1726
2187
|
{
|
|
@@ -1961,128 +2422,23 @@ async function callTool(req) {
|
|
|
1961
2422
|
}
|
|
1962
2423
|
case "agentistics_summary": {
|
|
1963
2424
|
const harness = args?.harness;
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
const allSessions = filterSessions(data.sessions ?? [], harness);
|
|
1969
|
-
let totalCostUSD = 0;
|
|
1970
|
-
let totalInput = 0, totalOutput = 0, totalCacheRead = 0, totalCacheWrite = 0;
|
|
1971
|
-
const modelTokens = {};
|
|
1972
|
-
const projectSessions = {};
|
|
1973
|
-
const activeDates = new Set;
|
|
1974
|
-
for (const s of allSessions) {
|
|
1975
|
-
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
1976
|
-
totalInput += input;
|
|
1977
|
-
totalOutput += output;
|
|
1978
|
-
totalCacheRead += cacheRead;
|
|
1979
|
-
totalCacheWrite += cacheWrite;
|
|
1980
|
-
totalCostUSD += cost;
|
|
1981
|
-
if (s.model)
|
|
1982
|
-
modelTokens[s.model] = (modelTokens[s.model] ?? 0) + input + output;
|
|
1983
|
-
if (s.project_path)
|
|
1984
|
-
projectSessions[s.project_path] = (projectSessions[s.project_path] ?? 0) + 1;
|
|
1985
|
-
if (s.start_time)
|
|
1986
|
-
activeDates.add(String(s.start_time).slice(0, 10));
|
|
1987
|
-
}
|
|
1988
|
-
const claudeFallback = unified || harness === "claude";
|
|
1989
|
-
const topModel = Object.entries(modelTokens).sort(([, a], [, b]) => b - a)[0]?.[0] ?? (claudeFallback ? totals.topModel ?? "—" : "—");
|
|
1990
|
-
const topProject = Object.entries(projectSessions).sort(([, a], [, b]) => b - a)[0]?.[0] ?? "—";
|
|
1991
|
-
return {
|
|
1992
|
-
content: [{
|
|
1993
|
-
type: "text",
|
|
1994
|
-
text: JSON.stringify({
|
|
1995
|
-
harness: unified ? "all" : harness,
|
|
1996
|
-
totalInputTokens: totalInput || (claudeFallback ? totals.input : 0),
|
|
1997
|
-
totalOutputTokens: totalOutput || (claudeFallback ? totals.output : 0),
|
|
1998
|
-
totalCacheReadTokens: totalCacheRead || (claudeFallback ? totals.cacheRead : 0),
|
|
1999
|
-
totalCacheWriteTokens: totalCacheWrite || (claudeFallback ? totals.cacheWrite : 0),
|
|
2000
|
-
estimatedCostUSD: Math.round((totalCostUSD || (claudeFallback ? totals.cost : 0)) * 100) / 100,
|
|
2001
|
-
totalSessions: allSessions.length,
|
|
2002
|
-
topModel,
|
|
2003
|
-
topProject,
|
|
2004
|
-
activeDays: activeDates.size || (claudeFallback ? sc.activeDays ?? 0 : 0),
|
|
2005
|
-
currentStreak: claudeFallback ? sc.currentStreak ?? 0 : null
|
|
2006
|
-
}, null, 2)
|
|
2007
|
-
}]
|
|
2008
|
-
};
|
|
2425
|
+
return text(await analytics("agentistics_summary", harness, {
|
|
2426
|
+
legacy: () => apiGet("/api/data"),
|
|
2427
|
+
metrics: (q) => fetch(`${API}/api/runtime/metrics?${q}`)
|
|
2428
|
+
}));
|
|
2009
2429
|
}
|
|
2010
2430
|
case "agentistics_harnesses": {
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
const hs = sessions.filter((s) => sessionHarness(s) === h);
|
|
2016
|
-
let input = 0, output = 0, cacheRead = 0, cacheWrite = 0, cost = 0, messages = 0, lastActive = "";
|
|
2017
|
-
for (const s of hs) {
|
|
2018
|
-
const t = sessionTokens2(s);
|
|
2019
|
-
input += t.input;
|
|
2020
|
-
output += t.output;
|
|
2021
|
-
cacheRead += t.cacheRead;
|
|
2022
|
-
cacheWrite += t.cacheWrite;
|
|
2023
|
-
cost += t.cost;
|
|
2024
|
-
messages += sessionMessages(s);
|
|
2025
|
-
if (s.start_time && String(s.start_time) > lastActive)
|
|
2026
|
-
lastActive = String(s.start_time);
|
|
2027
|
-
}
|
|
2028
|
-
return {
|
|
2029
|
-
harness: h,
|
|
2030
|
-
sessions: hs.length,
|
|
2031
|
-
messages,
|
|
2032
|
-
inputTokens: input,
|
|
2033
|
-
outputTokens: output,
|
|
2034
|
-
cacheReadTokens: cacheRead,
|
|
2035
|
-
cacheWriteTokens: cacheWrite,
|
|
2036
|
-
totalTokens: input + output + cacheRead + cacheWrite,
|
|
2037
|
-
estimatedCostUSD: Math.round(cost * 100) / 100,
|
|
2038
|
-
lastActive: lastActive || null
|
|
2039
|
-
};
|
|
2040
|
-
}).sort((a, b) => b.totalTokens - a.totalTokens);
|
|
2041
|
-
return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] };
|
|
2431
|
+
return text(await analytics("agentistics_harnesses", undefined, {
|
|
2432
|
+
legacy: () => apiGet("/api/data"),
|
|
2433
|
+
metrics: (q) => fetch(`${API}/api/runtime/metrics?${q}`)
|
|
2434
|
+
}));
|
|
2042
2435
|
}
|
|
2043
2436
|
case "agentistics_projects": {
|
|
2044
2437
|
const harness = args?.harness;
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
const key = s.project_path;
|
|
2050
|
-
if (!key)
|
|
2051
|
-
continue;
|
|
2052
|
-
if (!byPath[key])
|
|
2053
|
-
byPath[key] = { sessions: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0, costUSD: 0, messages: 0, lastActive: "", languages: [] };
|
|
2054
|
-
const agg = byPath[key];
|
|
2055
|
-
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
2056
|
-
agg.sessions += 1;
|
|
2057
|
-
agg.inputTokens += input;
|
|
2058
|
-
agg.outputTokens += output;
|
|
2059
|
-
agg.cacheRead += cacheRead;
|
|
2060
|
-
agg.cacheWrite += cacheWrite;
|
|
2061
|
-
agg.costUSD += cost;
|
|
2062
|
-
agg.messages += sessionMessages(s);
|
|
2063
|
-
if (!agg.lastActive || (s.start_time ?? "") > agg.lastActive)
|
|
2064
|
-
agg.lastActive = s.start_time ?? "";
|
|
2065
|
-
for (const lang of s.languages ?? [])
|
|
2066
|
-
if (!agg.languages.includes(lang))
|
|
2067
|
-
agg.languages.push(lang);
|
|
2068
|
-
}
|
|
2069
|
-
const projects = data.projects ?? [];
|
|
2070
|
-
const summary = projects.filter((p) => !harness || harness === "all" || byPath[p.path]).map((p) => {
|
|
2071
|
-
const agg = byPath[p.path] ?? { sessions: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0, costUSD: 0, messages: 0, lastActive: "", languages: [] };
|
|
2072
|
-
return {
|
|
2073
|
-
name: p.name,
|
|
2074
|
-
path: p.path,
|
|
2075
|
-
sessions: !harness || harness === "all" ? (p.sessions ?? []).length : agg.sessions,
|
|
2076
|
-
messages: agg.messages,
|
|
2077
|
-
inputTokens: agg.inputTokens,
|
|
2078
|
-
outputTokens: agg.outputTokens,
|
|
2079
|
-
totalTokens: agg.inputTokens + agg.outputTokens,
|
|
2080
|
-
estimatedCostUSD: Math.round(agg.costUSD * 1e4) / 1e4,
|
|
2081
|
-
lastActive: agg.lastActive || null,
|
|
2082
|
-
languages: agg.languages
|
|
2083
|
-
};
|
|
2084
|
-
}).sort((a, b) => b.totalTokens - a.totalTokens);
|
|
2085
|
-
return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
|
|
2438
|
+
return text(await analytics("agentistics_projects", harness, {
|
|
2439
|
+
legacy: () => apiGet("/api/data"),
|
|
2440
|
+
metrics: (q) => fetch(`${API}/api/runtime/metrics?${q}`)
|
|
2441
|
+
}));
|
|
2086
2442
|
}
|
|
2087
2443
|
case "agentistics_sessions": {
|
|
2088
2444
|
const limit = Math.min(args?.limit ?? 20, 50);
|
|
@@ -2110,43 +2466,10 @@ async function callTool(req) {
|
|
|
2110
2466
|
}
|
|
2111
2467
|
case "agentistics_costs": {
|
|
2112
2468
|
const harness = args?.harness;
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
model,
|
|
2118
|
-
inputTokens: u.inputTokens ?? 0,
|
|
2119
|
-
outputTokens: u.outputTokens ?? 0,
|
|
2120
|
-
cacheReadTokens: u.cacheReadTokens ?? 0,
|
|
2121
|
-
cacheWriteTokens: u.cacheWriteTokens ?? 0,
|
|
2122
|
-
totalTokens: u.totalTokens ?? 0,
|
|
2123
|
-
estimatedCostUSD: u.costUSD ?? 0
|
|
2124
|
-
})).sort((a, b) => b.totalTokens - a.totalTokens);
|
|
2125
|
-
return { content: [{ type: "text", text: JSON.stringify(breakdown, null, 2) }] };
|
|
2126
|
-
}
|
|
2127
|
-
const byModel = {};
|
|
2128
|
-
for (const s of filterSessions(data.sessions ?? [], harness)) {
|
|
2129
|
-
const model = s.model || "unknown";
|
|
2130
|
-
if (!byModel[model])
|
|
2131
|
-
byModel[model] = { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0, estimatedCostUSD: 0 };
|
|
2132
|
-
const agg = byModel[model];
|
|
2133
|
-
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
2134
|
-
agg.inputTokens += input;
|
|
2135
|
-
agg.outputTokens += output;
|
|
2136
|
-
agg.cacheReadTokens += cacheRead;
|
|
2137
|
-
agg.cacheWriteTokens += cacheWrite;
|
|
2138
|
-
agg.estimatedCostUSD += cost;
|
|
2139
|
-
}
|
|
2140
|
-
const breakdown = Object.entries(byModel).map(([model, u]) => ({
|
|
2141
|
-
model,
|
|
2142
|
-
inputTokens: u.inputTokens,
|
|
2143
|
-
outputTokens: u.outputTokens,
|
|
2144
|
-
cacheReadTokens: u.cacheReadTokens,
|
|
2145
|
-
cacheWriteTokens: u.cacheWriteTokens,
|
|
2146
|
-
totalTokens: u.inputTokens + u.outputTokens + u.cacheReadTokens + u.cacheWriteTokens,
|
|
2147
|
-
estimatedCostUSD: Math.round(u.estimatedCostUSD * 1e4) / 1e4
|
|
2148
|
-
})).sort((a, b) => b.totalTokens - a.totalTokens);
|
|
2149
|
-
return { content: [{ type: "text", text: JSON.stringify(breakdown, null, 2) }] };
|
|
2469
|
+
return text(await analytics("agentistics_costs", harness, {
|
|
2470
|
+
legacy: () => apiGet("/api/data"),
|
|
2471
|
+
metrics: (q) => fetch(`${API}/api/runtime/metrics?${q}`)
|
|
2472
|
+
}));
|
|
2150
2473
|
}
|
|
2151
2474
|
case "agentistics_component_catalog": {
|
|
2152
2475
|
const category = args?.category;
|
|
@@ -2382,37 +2705,10 @@ async function callTool(req) {
|
|
|
2382
2705
|
}
|
|
2383
2706
|
case "agentistics_repos": {
|
|
2384
2707
|
const harness = args?.harness;
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
const key = s.git_remote || "";
|
|
2390
|
-
if (!byRemote[key])
|
|
2391
|
-
byRemote[key] = { sessions: 0, messages: 0, inputTokens: 0, outputTokens: 0, cacheRead: 0, cacheWrite: 0, costUSD: 0, lastActive: "" };
|
|
2392
|
-
const agg = byRemote[key];
|
|
2393
|
-
const { input, output, cacheRead, cacheWrite, cost } = sessionTokens2(s);
|
|
2394
|
-
agg.sessions += 1;
|
|
2395
|
-
agg.messages += sessionMessages(s);
|
|
2396
|
-
agg.inputTokens += input;
|
|
2397
|
-
agg.outputTokens += output;
|
|
2398
|
-
agg.cacheRead += cacheRead;
|
|
2399
|
-
agg.cacheWrite += cacheWrite;
|
|
2400
|
-
agg.costUSD += cost;
|
|
2401
|
-
if (s.start_time && String(s.start_time) > agg.lastActive)
|
|
2402
|
-
agg.lastActive = String(s.start_time);
|
|
2403
|
-
}
|
|
2404
|
-
const rows = Object.entries(byRemote).map(([remote, agg]) => ({
|
|
2405
|
-
repo: remote || "unlinked",
|
|
2406
|
-
remote: remote || null,
|
|
2407
|
-
sessions: agg.sessions,
|
|
2408
|
-
messages: agg.messages,
|
|
2409
|
-
inputTokens: agg.inputTokens,
|
|
2410
|
-
outputTokens: agg.outputTokens,
|
|
2411
|
-
totalTokens: agg.inputTokens + agg.outputTokens + agg.cacheRead + agg.cacheWrite,
|
|
2412
|
-
estimatedCostUSD: Math.round(agg.costUSD * 1e4) / 1e4,
|
|
2413
|
-
lastActive: agg.lastActive || null
|
|
2414
|
-
})).sort((a, b) => b.totalTokens - a.totalTokens);
|
|
2415
|
-
return { content: [{ type: "text", text: JSON.stringify(rows, null, 2) }] };
|
|
2708
|
+
return text(await analytics("agentistics_repos", harness, {
|
|
2709
|
+
legacy: () => apiGet("/api/data"),
|
|
2710
|
+
metrics: (q) => fetch(`${API}/api/runtime/metrics?${q}`)
|
|
2711
|
+
}));
|
|
2416
2712
|
}
|
|
2417
2713
|
case "agentistics_team_status": {
|
|
2418
2714
|
const prefs = await getPrefs();
|
package/package.json
CHANGED