agentwrangler 0.1.0 → 0.1.1-next.2e8c141
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/README.md +152 -84
- package/dist/daemon/index.js +9 -3
- package/dist/daemon/readiness.js +26 -1
- package/dist/daemon/router.js +3 -1
- package/dist/db/migrations/016_ingest_offset_file_version.sql +8 -0
- package/dist/db/migrations/017_ingest_metric_events.sql +22 -0
- package/dist/detector/measurement.js +3 -2
- package/dist/hook/precompact-checkpoint-hook.mjs +51 -15
- package/dist/ingest/ingestor.js +151 -62
- package/dist/ingest/pricing.js +15 -0
- package/dist/ingest/tail.js +55 -11
- package/dist/query/api/outcomes.js +2 -1
- package/dist/query/api/overview.js +13 -3
- package/dist/query/settings-store.js +2 -0
- package/dist/ui/assets/BarChart-BwFZrcLn.js +3 -0
- package/dist/ui/assets/BriefsPage-CtJwHLOh.js +2 -0
- package/dist/ui/assets/CacheWriteSpikesChart-zJIJWO41.js +1 -0
- package/dist/ui/assets/CartesianChart-qApXl3Ak.js +84 -0
- package/dist/ui/assets/Chip-Cpt_fEc9.js +1 -0
- package/dist/ui/assets/ComposedChart-DYf1wymW.js +1 -0
- package/dist/ui/assets/EmptyState-DL8gAVnJ.js +1 -0
- package/dist/ui/assets/FlavorDecomposition-CvJW78M9.js +1 -0
- package/dist/ui/assets/FrictionCell-DciiSd3L.js +2 -0
- package/dist/ui/assets/GlossaryPage-BMkZGt2D.js +1 -0
- package/dist/ui/assets/HotSessionsPage-Ejn385bb.js +1 -0
- package/dist/ui/assets/InfoTip-Ckc9_LpJ.js +1 -0
- package/dist/ui/assets/Legend-C_YMp0Rv.js +5 -0
- package/dist/ui/assets/Line-DU4UvEBO.js +1 -0
- package/dist/ui/assets/OverviewPage-BcEKdWZj.js +2 -0
- package/dist/ui/assets/RecommendationsPage-CHfuNP_W.js +2 -0
- package/dist/ui/assets/Scatter-CNetVLuX.js +1 -0
- package/dist/ui/assets/SessionDetailPage-CYm5Gxo0.js +1 -0
- package/dist/ui/assets/SettingsPage-DjgGZj8N.js +25 -0
- package/dist/ui/assets/Skeleton-BO9mXDXU.js +1 -0
- package/dist/ui/assets/SpendPercentileChip-BvaZ8Lzi.js +1 -0
- package/dist/ui/assets/TrendChart-DKy9aH5G.js +1 -0
- package/dist/ui/assets/WorkspaceDetailPage-dutc4EpN.js +1 -0
- package/dist/ui/assets/WorkspacesPage-isSML2fh.js +1 -0
- package/dist/ui/assets/chart-theme-BPPMjbVX.js +1 -0
- package/dist/ui/assets/graphicalItemSelectors-DgKF1Dg2.js +1 -0
- package/dist/ui/assets/index-CUyiomzU.js +41 -0
- package/dist/ui/assets/index-SrfNUeBZ.css +1 -0
- package/dist/ui/assets/prompt-templates-DhR4Qoy9.js +13 -0
- package/dist/ui/assets/useExperimentalActions-BeDSmwBu.js +1 -0
- package/dist/ui/assets/workspace-label-CysPJNc0.js +1 -0
- package/dist/ui/index.html +2 -2
- package/package.json +21 -5
- package/dist/ui/assets/index-DnRKgc21.css +0 -1
- package/dist/ui/assets/index-h1Q1wWq5.js +0 -168
package/dist/ingest/ingestor.js
CHANGED
|
@@ -18,7 +18,7 @@ import { Health } from "./health.js";
|
|
|
18
18
|
import { PARSER_VERSION, projectLine } from "./parser.js";
|
|
19
19
|
import { PricingSnapshotStore, seedListPrices } from "./pricing.js";
|
|
20
20
|
import { DEFAULT_RECONCILE_OPTIONS, reconcileSessions, } from "./reconcile.js";
|
|
21
|
-
import { loadOffset, saveOffset, tailFile } from "./tail.js";
|
|
21
|
+
import { fileVersion, loadOffset, sameFileVersion, saveOffset, tailFile, } from "./tail.js";
|
|
22
22
|
import { LONG_GAP_THRESHOLD_S } from "./types.js";
|
|
23
23
|
import { backfillDiscoveredCwd, defaultReadCwd, defaultReadRemote, resolveWorkspaceMappings, } from "./workspace-mapping.js";
|
|
24
24
|
export const DEFAULT_INGESTOR_OPTIONS = {
|
|
@@ -41,10 +41,9 @@ export class Ingestor {
|
|
|
41
41
|
countedResults = new Set(); // toolUseIds already summed
|
|
42
42
|
resultBytesByMsg = new Map(); // messageId → running byte sum
|
|
43
43
|
lineCursor = new Map(); // filePath → complete lines consumed
|
|
44
|
-
|
|
44
|
+
lastVersion = new Map(); // filePath → last-seen version
|
|
45
45
|
discoveryCache = createDiscoveryCache();
|
|
46
46
|
unresolvedRemotes = new Set();
|
|
47
|
-
userTurnTsBySession = new Map(); // sessionId → epoch-ms of user turns
|
|
48
47
|
// Prepared statements.
|
|
49
48
|
stInsertTurn;
|
|
50
49
|
stInsertSession;
|
|
@@ -52,7 +51,11 @@ export class Ingestor {
|
|
|
52
51
|
stBumpUserTurnCount;
|
|
53
52
|
stBumpFrictionCounts;
|
|
54
53
|
stSetGapAggregates;
|
|
54
|
+
stInsertMetricEvent;
|
|
55
|
+
stGetMetricTimestamps;
|
|
55
56
|
stGetGapN;
|
|
57
|
+
stGetMetricBaseline;
|
|
58
|
+
stSetMetricBaseline;
|
|
56
59
|
stInsertToolEvent;
|
|
57
60
|
stUpsertToolEventMetadata;
|
|
58
61
|
stSetToolResult;
|
|
@@ -93,7 +96,16 @@ export class Ingestor {
|
|
|
93
96
|
interrupt_count = interrupt_count + ?
|
|
94
97
|
WHERE session_id = ?`);
|
|
95
98
|
this.stSetGapAggregates = db.prepare("UPDATE sessions SET gap_median_s=?, gap_p90_s=?, long_gap_count=?, gap_n=? WHERE session_id=?");
|
|
96
|
-
this.
|
|
99
|
+
this.stInsertMetricEvent = db.prepare(`INSERT OR IGNORE INTO ingest_metric_events
|
|
100
|
+
(event_id, session_id, user_turn_ts, is_user_turn,
|
|
101
|
+
is_compact_summary, is_api_error, is_interrupt)
|
|
102
|
+
VALUES (?,?,?,?,?,?,?)`);
|
|
103
|
+
this.stGetMetricTimestamps = db.prepare(`SELECT user_turn_ts FROM ingest_metric_events
|
|
104
|
+
WHERE session_id = ? AND is_user_turn = 1 AND user_turn_ts IS NOT NULL`);
|
|
105
|
+
this.stGetGapN = db.prepare("SELECT gap_n FROM sessions WHERE session_id = ?");
|
|
106
|
+
this.stGetMetricBaseline = db.prepare("SELECT seeded_offset FROM ingest_metric_baselines WHERE file_path = ?");
|
|
107
|
+
this.stSetMetricBaseline = db.prepare(`INSERT INTO ingest_metric_baselines (file_path, seeded_offset) VALUES (?, ?)
|
|
108
|
+
ON CONFLICT(file_path) DO UPDATE SET seeded_offset = excluded.seeded_offset`);
|
|
97
109
|
this.stInsertToolEvent = db.prepare(`INSERT INTO tool_events
|
|
98
110
|
(event_id, session_id, ts, tool_name, input_bytes, result_bytes, input_hash, exit_class, commit_sha)
|
|
99
111
|
VALUES (?,?,?,?,?,?,?,?,?)
|
|
@@ -236,9 +248,8 @@ export class Ingestor {
|
|
|
236
248
|
this.countedResults.clear();
|
|
237
249
|
this.resultBytesByMsg.clear();
|
|
238
250
|
this.lineCursor.clear();
|
|
239
|
-
this.
|
|
251
|
+
this.lastVersion.clear();
|
|
240
252
|
this.unresolvedRemotes.clear();
|
|
241
|
-
this.userTurnTsBySession.clear();
|
|
242
253
|
}
|
|
243
254
|
// ── internals ──────────────────────────────────────────────────────────────
|
|
244
255
|
ingestAllKnown() {
|
|
@@ -278,31 +289,39 @@ export class Ingestor {
|
|
|
278
289
|
}
|
|
279
290
|
/** Tail one file from its stored offset and ingest the complete new lines. */
|
|
280
291
|
ingestFile(filePath, projectSlug) {
|
|
281
|
-
//
|
|
282
|
-
//
|
|
283
|
-
//
|
|
292
|
+
// Skip only when size, identity, and timestamps all match. This keeps the
|
|
293
|
+
// 2s tail tick from touching the DB for idle files while still noticing
|
|
294
|
+
// same-size rewrites and path replacement.
|
|
284
295
|
// every tick, which was starving the daemon's event loop (outcomes pass).
|
|
285
296
|
// Keep the per-file stat: directory mtimes only decide when to refresh paths;
|
|
286
297
|
// they cannot safely replace append/rotation change detection for each file.
|
|
287
|
-
let
|
|
298
|
+
let currentVersion;
|
|
288
299
|
try {
|
|
289
|
-
|
|
300
|
+
currentVersion = fileVersion(fs.statSync(filePath));
|
|
290
301
|
}
|
|
291
302
|
catch {
|
|
292
303
|
return; // file vanished between discovery and tail; nothing to ingest
|
|
293
304
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
305
|
+
const previousVersion = this.lastVersion.get(filePath);
|
|
306
|
+
if (previousVersion !== undefined && sameFileVersion(previousVersion, currentVersion))
|
|
307
|
+
return;
|
|
308
|
+
this.lastVersion.set(filePath, currentVersion);
|
|
297
309
|
this.health.fileSeen();
|
|
298
310
|
registerWorkspace(this.db, projectSlug);
|
|
299
311
|
const stored = loadOffset(this.db, filePath);
|
|
300
|
-
|
|
312
|
+
this.seedLegacyMetricPrefix(filePath, stored?.offset ?? null, currentVersion.size);
|
|
313
|
+
const result = tailFile(filePath, stored, currentVersion);
|
|
301
314
|
if (result.wasReset)
|
|
302
315
|
this.lineCursor.set(filePath, 0);
|
|
303
316
|
if (result.lines.length === 0) {
|
|
304
|
-
|
|
305
|
-
|
|
317
|
+
if (stored === null ||
|
|
318
|
+
stored.offset !== result.newOffset ||
|
|
319
|
+
stored.headHash !== result.newHeadHash ||
|
|
320
|
+
stored.fileVersion === null ||
|
|
321
|
+
!sameFileVersion(stored.fileVersion, currentVersion)) {
|
|
322
|
+
saveOffset(this.db, filePath, result.newOffset, result.newHeadHash, currentVersion);
|
|
323
|
+
}
|
|
324
|
+
this.stSetMetricBaseline.run(filePath, result.newOffset);
|
|
306
325
|
return;
|
|
307
326
|
}
|
|
308
327
|
const defaultSessionId = sessionStemFor(filePath);
|
|
@@ -317,9 +336,50 @@ export class Ingestor {
|
|
|
317
336
|
});
|
|
318
337
|
tx();
|
|
319
338
|
this.lineCursor.set(filePath, base + result.lines.length);
|
|
320
|
-
saveOffset(this.db, filePath, result.newOffset, result.newHeadHash);
|
|
339
|
+
saveOffset(this.db, filePath, result.newOffset, result.newHeadHash, currentVersion);
|
|
340
|
+
this.stSetMetricBaseline.run(filePath, result.newOffset);
|
|
321
341
|
this.health.fileParsed();
|
|
322
342
|
}
|
|
343
|
+
/**
|
|
344
|
+
* Upgrade bridge for offsets created before the metric-event ledger existed.
|
|
345
|
+
* Already-consumed complete lines are registered without advancing counters.
|
|
346
|
+
*/
|
|
347
|
+
seedLegacyMetricPrefix(filePath, storedOffset, currentSize) {
|
|
348
|
+
const baseline = this.stGetMetricBaseline.get(filePath);
|
|
349
|
+
if (baseline !== undefined)
|
|
350
|
+
return;
|
|
351
|
+
if (storedOffset === null || storedOffset <= 0 || storedOffset > currentSize) {
|
|
352
|
+
this.stSetMetricBaseline.run(filePath, 0);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
const fd = fs.openSync(filePath, "r");
|
|
356
|
+
try {
|
|
357
|
+
const buf = Buffer.alloc(storedOffset);
|
|
358
|
+
const bytesRead = fs.readSync(fd, buf, 0, storedOffset, 0);
|
|
359
|
+
let lastNl = -1;
|
|
360
|
+
for (let i = bytesRead - 1; i >= 0; i--) {
|
|
361
|
+
if (buf[i] === 0x0a) {
|
|
362
|
+
lastNl = i;
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const lines = lastNl < 0 ? [] : buf.subarray(0, lastNl).toString("utf8").split("\n");
|
|
367
|
+
const defaultSessionId = sessionStemFor(filePath);
|
|
368
|
+
this.db.transaction(() => {
|
|
369
|
+
for (const raw of lines) {
|
|
370
|
+
if (raw.length === 0)
|
|
371
|
+
continue;
|
|
372
|
+
const proj = projectLine(raw, { defaultSessionId });
|
|
373
|
+
if (proj.kind === "record")
|
|
374
|
+
this.recordMetricEvent(raw, proj, false);
|
|
375
|
+
}
|
|
376
|
+
this.stSetMetricBaseline.run(filePath, storedOffset);
|
|
377
|
+
})();
|
|
378
|
+
}
|
|
379
|
+
finally {
|
|
380
|
+
fs.closeSync(fd);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
323
383
|
applyLine(raw, ctx, workspaceId, filePath, lineNo) {
|
|
324
384
|
const proj = projectLine(raw, ctx);
|
|
325
385
|
if (proj.kind === "quarantine") {
|
|
@@ -339,31 +399,13 @@ export class Ingestor {
|
|
|
339
399
|
if (lineSessionId !== null) {
|
|
340
400
|
this.ensureSession(lineSessionId, workspaceId, filePath, lineTs === "" ? null : lineTs);
|
|
341
401
|
}
|
|
342
|
-
if (
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
const epochMs = Date.parse(proj.ts);
|
|
346
|
-
if (Number.isFinite(epochMs)) {
|
|
347
|
-
const list = this.userTurnTsBySession.get(proj.sessionId);
|
|
348
|
-
if (list !== undefined) {
|
|
349
|
-
list.push(epochMs);
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
this.userTurnTsBySession.set(proj.sessionId, [epochMs]);
|
|
353
|
-
}
|
|
354
|
-
this.applyGapAggregates(proj.sessionId);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
// Friction counters (RV2a): compact summary, API error, interrupt.
|
|
359
|
-
if (proj.isCompactSummary || proj.isApiErrorMessage || proj.isInterrupt) {
|
|
360
|
-
// Ensure session row exists for lines that carry only top-level flags
|
|
361
|
-
// (no turn, command, or tool events — so lineSessionId would be null).
|
|
362
|
-
if (lineSessionId === null) {
|
|
363
|
-
this.ensureSession(proj.sessionId, workspaceId, filePath, null);
|
|
364
|
-
}
|
|
365
|
-
this.stBumpFrictionCounts.run(proj.isCompactSummary ? 1 : 0, proj.isApiErrorMessage ? 1 : 0, proj.isInterrupt ? 1 : 0, proj.sessionId);
|
|
402
|
+
if (lineSessionId === null &&
|
|
403
|
+
(proj.isCompactSummary || proj.isApiErrorMessage || proj.isInterrupt)) {
|
|
404
|
+
this.ensureSession(proj.sessionId, workspaceId, filePath, null);
|
|
366
405
|
}
|
|
406
|
+
// User/friction aggregates advance only for a structurally new source record.
|
|
407
|
+
// The persisted digest makes replay idempotent across restarts and files.
|
|
408
|
+
this.recordMetricEvent(raw, proj, true);
|
|
367
409
|
// Command markers → tool_events(local_command) for hygiene evaluation.
|
|
368
410
|
if (proj.command !== null) {
|
|
369
411
|
this.recordCommand(proj.command.sessionId, proj.command.ts, proj.command.command);
|
|
@@ -457,25 +499,83 @@ export class Ingestor {
|
|
|
457
499
|
.slice(0, 20)}`;
|
|
458
500
|
this.stInsertToolEvent.run(eventId, sessionId, ts, "local_command", null, null, command, null, null);
|
|
459
501
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
502
|
+
recordMetricEvent(raw, proj, applyAggregates) {
|
|
503
|
+
if (!proj.isUserTurn &&
|
|
504
|
+
!proj.isCompactSummary &&
|
|
505
|
+
!proj.isApiErrorMessage &&
|
|
506
|
+
!proj.isInterrupt) {
|
|
464
507
|
return;
|
|
465
508
|
}
|
|
466
|
-
const
|
|
509
|
+
const flags = [
|
|
510
|
+
proj.isUserTurn ? "u" : "",
|
|
511
|
+
proj.isCompactSummary ? "c" : "",
|
|
512
|
+
proj.isApiErrorMessage ? "a" : "",
|
|
513
|
+
proj.isInterrupt ? "i" : "",
|
|
514
|
+
].join("");
|
|
515
|
+
let sourceIdentity;
|
|
516
|
+
try {
|
|
517
|
+
const parsed = JSON.parse(raw);
|
|
518
|
+
if (typeof parsed.uuid === "string" && parsed.uuid.trim() !== "") {
|
|
519
|
+
sourceIdentity = `uuid:${parsed.uuid}`;
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
const message = parsed.message;
|
|
523
|
+
const messageId = typeof message === "object" && message !== null
|
|
524
|
+
? message.id
|
|
525
|
+
: null;
|
|
526
|
+
if (typeof messageId === "string" && messageId.trim() !== "") {
|
|
527
|
+
sourceIdentity = `message:${String(parsed.type ?? "")}:${messageId}:${flags}`;
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
sourceIdentity = `raw:${crypto.createHash("sha256").update(raw).digest("hex")}`;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
// projectLine already parsed record lines; retain a safe fallback if that contract changes.
|
|
536
|
+
sourceIdentity = `raw:${crypto.createHash("sha256").update(raw).digest("hex")}`;
|
|
537
|
+
}
|
|
538
|
+
const eventId = crypto
|
|
539
|
+
.createHash("sha256")
|
|
540
|
+
.update(`${proj.sessionId}\0${sourceIdentity}`)
|
|
541
|
+
.digest("hex");
|
|
542
|
+
const validUserTs = proj.isUserTurn && typeof proj.ts === "string" && Number.isFinite(Date.parse(proj.ts))
|
|
543
|
+
? proj.ts
|
|
544
|
+
: null;
|
|
545
|
+
const inserted = this.stInsertMetricEvent.run(eventId, proj.sessionId, validUserTs, proj.isUserTurn ? 1 : 0, proj.isCompactSummary ? 1 : 0, proj.isApiErrorMessage ? 1 : 0, proj.isInterrupt ? 1 : 0);
|
|
546
|
+
if (!applyAggregates || inserted.changes === 0)
|
|
547
|
+
return;
|
|
548
|
+
if (proj.isUserTurn)
|
|
549
|
+
this.stBumpUserTurnCount.run(proj.sessionId);
|
|
550
|
+
if (proj.isCompactSummary || proj.isApiErrorMessage || proj.isInterrupt) {
|
|
551
|
+
this.stBumpFrictionCounts.run(proj.isCompactSummary ? 1 : 0, proj.isApiErrorMessage ? 1 : 0, proj.isInterrupt ? 1 : 0, proj.sessionId);
|
|
552
|
+
}
|
|
553
|
+
if (validUserTs !== null)
|
|
554
|
+
this.applyGapAggregates(proj.sessionId);
|
|
555
|
+
}
|
|
556
|
+
applyGapAggregates(sessionId) {
|
|
557
|
+
const rows = this.stGetMetricTimestamps.all(sessionId);
|
|
558
|
+
const sorted = rows
|
|
559
|
+
.map((row) => Date.parse(row.user_turn_ts))
|
|
560
|
+
.filter((epochMs) => Number.isFinite(epochMs))
|
|
561
|
+
.sort((a, b) => a - b);
|
|
467
562
|
const gaps = [];
|
|
468
563
|
for (let i = 1; i < sorted.length; i++) {
|
|
469
564
|
gaps.push((sorted[i] - sorted[i - 1]) / 1000);
|
|
470
565
|
}
|
|
471
566
|
const gapN = gaps.length;
|
|
567
|
+
const stored = this.stGetGapN.get(sessionId);
|
|
568
|
+
// During the 017 upgrade, a session's legacy timestamps can be spread across
|
|
569
|
+
// files that have not all established their metric-ledger baselines yet.
|
|
570
|
+
// Preserve the richer legacy aggregate until the ledger has caught up.
|
|
571
|
+
if (stored !== undefined && gapN < stored.gap_n)
|
|
572
|
+
return;
|
|
472
573
|
if (gapN === 0) {
|
|
473
|
-
this.
|
|
574
|
+
this.stSetGapAggregates.run(null, null, 0, 0, sessionId);
|
|
474
575
|
return;
|
|
475
576
|
}
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
// must not be suppressed just because it's smaller than a pre-restart gap_n.
|
|
577
|
+
// The persisted event ledger supplies the complete known history across
|
|
578
|
+
// restarts and source files, so this aggregate can be recomputed deterministically.
|
|
479
579
|
const g = [...gaps].sort((a, b) => a - b);
|
|
480
580
|
const mid = g.length;
|
|
481
581
|
const gapMedianS = mid % 2 === 1
|
|
@@ -486,17 +586,6 @@ export class Ingestor {
|
|
|
486
586
|
const longGapCount = g.filter((x) => x > LONG_GAP_THRESHOLD_S).length;
|
|
487
587
|
this.stSetGapAggregates.run(gapMedianS, gapP90S, longGapCount, gapN, sessionId);
|
|
488
588
|
}
|
|
489
|
-
// Write-guard for the "no computable gap" collapse (0 or 1 in-process user turns):
|
|
490
|
-
// the in-memory turn-timestamp map resets on every daemon restart, so this state is
|
|
491
|
-
// reached on the first post-restart user turn even when a prior cold runBackscan()
|
|
492
|
-
// already persisted a richer gap_n. Skip the null/zero write in that case so it
|
|
493
|
-
// doesn't clobber the richer aggregate.
|
|
494
|
-
clearGapAggregatesUnlessRicher(sessionId) {
|
|
495
|
-
const storedRow = this.stGetGapN.get(sessionId);
|
|
496
|
-
if ((storedRow?.gap_n ?? 0) > 0)
|
|
497
|
-
return;
|
|
498
|
-
this.stSetGapAggregates.run(null, null, 0, 0, sessionId);
|
|
499
|
-
}
|
|
500
589
|
quarantine(filePath, lineNo, errorClass) {
|
|
501
590
|
const qId = crypto
|
|
502
591
|
.createHash("sha1")
|
package/dist/ingest/pricing.js
CHANGED
|
@@ -62,6 +62,21 @@ export function modelTier(model) {
|
|
|
62
62
|
}
|
|
63
63
|
return null;
|
|
64
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* SQL predicate matching premium-priced models on a `model` column: Opus plus
|
|
67
|
+
* the Fable/Mythos families (Fable's $10/$50 base is 2× Opus). Shared by every
|
|
68
|
+
* premium-share aggregate so the family list can't drift between queries.
|
|
69
|
+
*/
|
|
70
|
+
export const PREMIUM_MODEL_SQL = "(model LIKE '%opus%' OR model LIKE '%fable%' OR model LIKE '%mythos%')";
|
|
71
|
+
/**
|
|
72
|
+
* Input list price ($/MTok) for a model, for ordering model lists
|
|
73
|
+
* most-expensive first. Derived from LIST_PRICES via modelTier so a new tier
|
|
74
|
+
* automatically sorts by its price; unpriceable models return 0 (sort last).
|
|
75
|
+
*/
|
|
76
|
+
export function modelInputListPrice(model) {
|
|
77
|
+
const tier = modelTier(model);
|
|
78
|
+
return tier === null ? 0 : (LIST_PRICES[tier]?.[0] ?? 0);
|
|
79
|
+
}
|
|
65
80
|
function addDaysIso(iso, days) {
|
|
66
81
|
const d = new Date(iso);
|
|
67
82
|
d.setUTCDate(d.getUTCDate() + days);
|
package/dist/ingest/tail.js
CHANGED
|
@@ -14,6 +14,22 @@
|
|
|
14
14
|
import * as crypto from "node:crypto";
|
|
15
15
|
import * as fs from "node:fs";
|
|
16
16
|
const HEAD_BYTES = 256; // bytes hashed for rotation detection
|
|
17
|
+
export function fileVersion(stat) {
|
|
18
|
+
return {
|
|
19
|
+
size: stat.size,
|
|
20
|
+
dev: String(stat.dev),
|
|
21
|
+
ino: String(stat.ino),
|
|
22
|
+
mtimeMs: stat.mtimeMs,
|
|
23
|
+
ctimeMs: stat.ctimeMs,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function sameFileVersion(a, b) {
|
|
27
|
+
return (a.size === b.size &&
|
|
28
|
+
a.dev === b.dev &&
|
|
29
|
+
a.ino === b.ino &&
|
|
30
|
+
a.mtimeMs === b.mtimeMs &&
|
|
31
|
+
a.ctimeMs === b.ctimeMs);
|
|
32
|
+
}
|
|
17
33
|
/** SHA-256 of the first n bytes of a file (or fewer if the file is shorter). */
|
|
18
34
|
function headHashN(filePath, n) {
|
|
19
35
|
let fd;
|
|
@@ -51,12 +67,12 @@ export function headHash(filePath) {
|
|
|
51
67
|
* Tail one file from its stored offset. Returns complete lines only.
|
|
52
68
|
* See module header for the rotation/truncation contract.
|
|
53
69
|
*/
|
|
54
|
-
export function tailFile(filePath, stored) {
|
|
70
|
+
export function tailFile(filePath, stored, currentVersion) {
|
|
55
71
|
let storedOffset = stored?.offset ?? 0;
|
|
56
72
|
let storedHead = stored?.headHash ?? null;
|
|
57
|
-
let
|
|
73
|
+
let version = currentVersion;
|
|
58
74
|
try {
|
|
59
|
-
|
|
75
|
+
version ??= fileVersion(fs.statSync(filePath));
|
|
60
76
|
}
|
|
61
77
|
catch {
|
|
62
78
|
return {
|
|
@@ -67,7 +83,7 @@ export function tailFile(filePath, stored) {
|
|
|
67
83
|
wasReset: false,
|
|
68
84
|
};
|
|
69
85
|
}
|
|
70
|
-
const fileSize =
|
|
86
|
+
const fileSize = version.size;
|
|
71
87
|
let event = null;
|
|
72
88
|
let wasReset = false;
|
|
73
89
|
const curHead = fileSize > 0 ? headHash(filePath) : "";
|
|
@@ -84,6 +100,12 @@ export function tailFile(filePath, stored) {
|
|
|
84
100
|
}
|
|
85
101
|
// fileSize < storedN: size regression — headChanged stays false; handled below.
|
|
86
102
|
}
|
|
103
|
+
const storedVersion = stored?.fileVersion ?? null;
|
|
104
|
+
const identityChanged = storedVersion !== null &&
|
|
105
|
+
(storedVersion.dev !== version.dev || storedVersion.ino !== version.ino);
|
|
106
|
+
const equalSizeTimestampChanged = storedVersion !== null &&
|
|
107
|
+
storedVersion.size === version.size &&
|
|
108
|
+
(storedVersion.mtimeMs !== version.mtimeMs || storedVersion.ctimeMs !== version.ctimeMs);
|
|
87
109
|
if (storedOffset > fileSize) {
|
|
88
110
|
// Size regression always means truncation, regardless of head change.
|
|
89
111
|
event = "TRUNCATION";
|
|
@@ -91,7 +113,7 @@ export function tailFile(filePath, stored) {
|
|
|
91
113
|
wasReset = true;
|
|
92
114
|
storedHead = curHead;
|
|
93
115
|
}
|
|
94
|
-
else if (headChanged) {
|
|
116
|
+
else if (identityChanged || equalSizeTimestampChanged || headChanged) {
|
|
95
117
|
event = "ROTATION";
|
|
96
118
|
storedOffset = 0;
|
|
97
119
|
wasReset = true;
|
|
@@ -135,18 +157,40 @@ export function tailFile(filePath, stored) {
|
|
|
135
157
|
/** Read the persisted offset for a file, or null if never tailed. */
|
|
136
158
|
export function loadOffset(db, filePath) {
|
|
137
159
|
const row = db
|
|
138
|
-
.prepare("SELECT byte_offset, file_hash_head FROM ingest_offsets WHERE file_path = ?")
|
|
160
|
+
.prepare("SELECT byte_offset, file_hash_head, file_size, file_dev, file_ino, file_mtime_ms, file_ctime_ms FROM ingest_offsets WHERE file_path = ?")
|
|
139
161
|
.get(filePath);
|
|
140
162
|
if (row === undefined)
|
|
141
163
|
return null;
|
|
142
|
-
|
|
164
|
+
const hasVersion = row.file_size !== null &&
|
|
165
|
+
row.file_dev !== null &&
|
|
166
|
+
row.file_ino !== null &&
|
|
167
|
+
row.file_mtime_ms !== null &&
|
|
168
|
+
row.file_ctime_ms !== null;
|
|
169
|
+
return {
|
|
170
|
+
offset: row.byte_offset,
|
|
171
|
+
headHash: row.file_hash_head,
|
|
172
|
+
fileVersion: hasVersion
|
|
173
|
+
? {
|
|
174
|
+
size: row.file_size,
|
|
175
|
+
dev: row.file_dev,
|
|
176
|
+
ino: row.file_ino,
|
|
177
|
+
mtimeMs: row.file_mtime_ms,
|
|
178
|
+
ctimeMs: row.file_ctime_ms,
|
|
179
|
+
}
|
|
180
|
+
: null,
|
|
181
|
+
};
|
|
143
182
|
}
|
|
144
183
|
/** Upsert the persisted offset for a file. */
|
|
145
|
-
export function saveOffset(db, filePath, offset, headHash) {
|
|
146
|
-
db.prepare(`INSERT INTO ingest_offsets (file_path, byte_offset, file_hash_head, updated_at)
|
|
147
|
-
VALUES (
|
|
184
|
+
export function saveOffset(db, filePath, offset, headHash, version) {
|
|
185
|
+
db.prepare(`INSERT INTO ingest_offsets (file_path, byte_offset, file_hash_head, file_size, file_dev, file_ino, file_mtime_ms, file_ctime_ms, updated_at)
|
|
186
|
+
VALUES (?,?,?,?,?,?,?,?,?)
|
|
148
187
|
ON CONFLICT(file_path) DO UPDATE SET
|
|
149
188
|
byte_offset = excluded.byte_offset,
|
|
150
189
|
file_hash_head = excluded.file_hash_head,
|
|
151
|
-
|
|
190
|
+
file_size = excluded.file_size,
|
|
191
|
+
file_dev = excluded.file_dev,
|
|
192
|
+
file_ino = excluded.file_ino,
|
|
193
|
+
file_mtime_ms = excluded.file_mtime_ms,
|
|
194
|
+
file_ctime_ms = excluded.file_ctime_ms,
|
|
195
|
+
updated_at = excluded.updated_at`).run(filePath, offset, headHash, version.size, version.dev, version.ino, version.mtimeMs, version.ctimeMs, new Date().toISOString());
|
|
152
196
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* SQL references: Data Model v2 §2 lines 261-275 / 303-314 / 329-334.
|
|
13
13
|
*/
|
|
14
|
+
import { PREMIUM_MODEL_SQL } from "../../ingest/pricing.js";
|
|
14
15
|
import { getQueryDb } from "../db-context.js";
|
|
15
16
|
import { buildResponse } from "../envelope.js";
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
@@ -136,7 +137,7 @@ export function listWorkspaceOutcomes() {
|
|
|
136
137
|
const lrByWs = new Map(lrRows.map((r) => [r.workspace_id, r.linkage_rate]));
|
|
137
138
|
const adherenceRows = db
|
|
138
139
|
.prepare(`SELECT workspace_id, COUNT(*) AS n,
|
|
139
|
-
AVG(CASE WHEN
|
|
140
|
+
AVG(CASE WHEN ${PREMIUM_MODEL_SQL} THEN 1.0 ELSE 0.0 END) AS premium_share
|
|
140
141
|
FROM turns
|
|
141
142
|
WHERE is_sidechain = 0 AND provisional = 0
|
|
142
143
|
GROUP BY workspace_id`)
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* windows/cutoffs, maps rows to the frozen DTOs, and attaches the envelope.
|
|
11
11
|
*/
|
|
12
12
|
import { loadConfig } from "../../daemon/config.js";
|
|
13
|
+
import { PREMIUM_MODEL_SQL, modelInputListPrice } from "../../ingest/pricing.js";
|
|
13
14
|
import { getQueryDb } from "../db-context.js";
|
|
14
15
|
import { forecastFromDb } from "../forecast.js";
|
|
15
16
|
import { contextPerTurnByModel, globalSpend, hasStaleClaim, liveSessionCount, liveSessions, spendByWorkspace, } from "../spend.js";
|
|
@@ -167,14 +168,20 @@ export function getGlobalOverview(filters) {
|
|
|
167
168
|
// counts sum to `turns_total`, NOT the reconciled `turns`. This is the
|
|
168
169
|
// deliberate spend-vs-context difference (Data Model §2/§3) — a consumer must
|
|
169
170
|
// not expect model_mix turns to reconcile with the reconciled `turns` figure.
|
|
170
|
-
|
|
171
|
+
// Display order: most-expensive tier first (then turns, then name) — the
|
|
172
|
+
// premium models matter most per turn, and a price-stable order keeps the
|
|
173
|
+
// index-keyed card colors from shuffling as usage volume shifts.
|
|
174
|
+
const ctxSorted = [...ctx].sort((a, b) => modelInputListPrice(b.model) - modelInputListPrice(a.model) ||
|
|
175
|
+
b.n - a.n ||
|
|
176
|
+
a.model.localeCompare(b.model));
|
|
177
|
+
const context_per_turn = ctxSorted.map((r) => ({
|
|
171
178
|
model: r.model,
|
|
172
179
|
n: r.n,
|
|
173
180
|
avg_context_per_turn: r.avg_context_per_turn,
|
|
174
181
|
avg_output_per_turn: r.avg_output_per_turn,
|
|
175
182
|
usd_per_turn: r.usd_per_turn,
|
|
176
183
|
}));
|
|
177
|
-
const model_mix =
|
|
184
|
+
const model_mix = ctxSorted.map((r) => ({ model: r.model, turns: r.n }));
|
|
178
185
|
const data = {
|
|
179
186
|
cost_equiv_u: g.cost_equiv_u,
|
|
180
187
|
turns: g.turns,
|
|
@@ -224,7 +231,9 @@ export function listWorkspaces(filters) {
|
|
|
224
231
|
+ cache_write_5m + cache_write_1h + cache_write_other)
|
|
225
232
|
ELSE NULL END AS cache_write_pct,
|
|
226
233
|
CAST(SUM(CASE WHEN model LIKE '%opus%' THEN 1 ELSE 0 END) AS REAL)
|
|
227
|
-
/ NULLIF(COUNT(*), 0) AS opus_pct
|
|
234
|
+
/ NULLIF(COUNT(*), 0) AS opus_pct,
|
|
235
|
+
CAST(SUM(CASE WHEN ${PREMIUM_MODEL_SQL} THEN 1 ELSE 0 END) AS REAL)
|
|
236
|
+
/ NULLIF(COUNT(*), 0) AS premium_pct
|
|
228
237
|
FROM turns WHERE ts >= ? AND ts < ?
|
|
229
238
|
GROUP BY workspace_id`)
|
|
230
239
|
.all(from, to));
|
|
@@ -245,6 +254,7 @@ export function listWorkspaces(filters) {
|
|
|
245
254
|
avg_context_per_turn: agg?.avg_context_per_turn ?? null,
|
|
246
255
|
cache_write_pct: agg?.cache_write_pct ?? null,
|
|
247
256
|
opus_pct: agg?.opus_pct ?? null,
|
|
257
|
+
premium_pct: agg?.premium_pct ?? null,
|
|
248
258
|
};
|
|
249
259
|
});
|
|
250
260
|
return makeResponse({ items, next_cursor: null }, {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{r as u}from"./index-CUyiomzU.js";import{aX as Le,a2 as V,aY as q,n as J,g as Te,aZ as Re,o as se,a1 as Fe,c as j,a_ as Ne,a$ as $e,s as F,a as We,d as T,e as R,f as ge,h as be,b0 as Ae,b1 as _e,b2 as Ge,k as ue,b3 as Ue,b4 as ce,x as Ve,L as _,p as Ze,r as Xe,D as ee,aN as Ye,t as Ie,v as He,S as qe,w as Je,Z as ae,y as O,z as Pe,A as Qe,B as ea,G as aa,E as ta,aO as ra,H as G,a9 as na,aP as ia,N as ke,J as oa,M as la,b5 as sa,b6 as ua,b7 as de,ay as Be,aR as Ee,aS as Se,aT as Oe,aA as ca,aa as we,U as da,W as va,_ as fa,$ as ma,l as pa,b8 as ha,a3 as ya,b9 as ve,a4 as xa,a5 as ga}from"./CartesianChart-qApXl3Ak.js";import{s as K,a as z}from"./graphicalItemSelectors-DgKF1Dg2.js";function ba(e,a){return ka(e)||Pa(e,a)||Ia(e,a)||Aa()}function Aa(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
|
|
2
|
+
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function Ia(e,a){if(e){if(typeof e=="string")return fe(e,a);var t={}.toString.call(e).slice(8,-1);return t==="Object"&&e.constructor&&(t=e.constructor.name),t==="Map"||t==="Set"?Array.from(e):t==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?fe(e,a):void 0}}function fe(e,a){(a==null||a>e.length)&&(a=e.length);for(var t=0,r=Array(a);t<a;t++)r[t]=e[t];return r}function Pa(e,a){var t=e==null?null:typeof Symbol<"u"&&e[Symbol.iterator]||e["@@iterator"];if(t!=null){var r,n,i,o,s=[],c=!0,l=!1;try{if(i=(t=t.call(e)).next,a!==0)for(;!(c=(r=i.call(t)).done)&&(s.push(r.value),s.length!==a);c=!0);}catch(d){l=!0,n=d}finally{try{if(!c&&t.return!=null&&(o=t.return(),Object(o)!==o))return}finally{if(l)throw n}}return s}}function ka(e){if(Array.isArray(e))return e}var me=(e,a,t)=>{var r=t??e;if(!V(r))return q(r,a,0)},Ba=(e,a,t)=>{var r={},n=e.filter(Le),i=e.filter(l=>l.stackId==null),o=n.reduce((l,d)=>{var v=l[d.stackId];return v==null&&(v=[]),v.push(d),l[d.stackId]=v,l},r),s=Object.entries(o).map(l=>{var d,v=ba(l,2),f=v[0],y=v[1],m=y.map(h=>h.dataKey),x=me(a,t,(d=y[0])===null||d===void 0?void 0:d.barSize);return{stackId:f,dataKeys:m,barSize:x}}),c=i.map(l=>{var d=[l.dataKey].filter(f=>f!=null),v=me(a,t,l.barSize);return{stackId:void 0,dataKeys:d,barSize:v}});return[...s,...c]};function pe(e,a){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);a&&(r=r.filter(function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable})),t.push.apply(t,r)}return t}function W(e){for(var a=1;a<arguments.length;a++){var t=arguments[a]!=null?arguments[a]:{};a%2?pe(Object(t),!0).forEach(function(r){Ea(e,r,t[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):pe(Object(t)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r))})}return e}function Ea(e,a,t){return(a=Sa(a))in e?Object.defineProperty(e,a,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[a]=t,e}function Sa(e){var a=Oa(e,"string");return typeof a=="symbol"?a:a+""}function Oa(e,a){if(typeof e!="object"||!e)return e;var t=e[Symbol.toPrimitive];if(t!==void 0){var r=t.call(e,a);if(typeof r!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(a==="string"?String:Number)(e)}function wa(e,a,t,r,n){var i,o=r.length;if(!(o<1)){var s=q(e,t,0,!0),c,l=[];if(J((i=r[0])===null||i===void 0?void 0:i.barSize)){var d=!1,v=t/o,f=r.reduce((g,I)=>g+(I.barSize||0),0);f+=(o-1)*s,f>=t&&(f-=(o-1)*s,s=0),f>=t&&v>0&&(d=!0,v*=.9,f=o*v);var y=Math.round((t-f)/2),m={offset:y-s,size:0};c=r.reduce((g,I)=>{var p,k={stackId:I.stackId,dataKeys:I.dataKeys,position:{offset:m.offset+m.size+s,size:d?v:(p=I.barSize)!==null&&p!==void 0?p:0}},B=[...g,k];return m=k.position,B},l)}else{var x=q(a,t,0,!0);t-2*x-(o-1)*s<=0&&(s=0);var h=(t-2*x-(o-1)*s)/o;h>1&&(h=Math.round(h));var b=J(n)?Math.min(h,n):h;c=r.reduce((g,I,p)=>[...g,{stackId:I.stackId,dataKeys:I.dataKeys,position:{offset:x+o*(h-b)/2+(b+s)*p,size:b}}],l)}return c}}var Ca=(e,a,t,r,n,i,o)=>{var s=V(o)?a:o,c=wa(t,r,n!==i?n:i,e,s);return n!==i&&c!=null&&(c=c.map(l=>W(W({},l),{},{position:W(W({},l.position),{},{offset:l.position.offset-n/2})}))),c},Da=(e,a)=>{var t=Te(a);if(!(!e||t==null||a==null)){var r=a.stackId;if(r!=null){var n=e[r];if(n){var i=n.stackedData;if(i)return i.find(o=>o.key===t)}}}},ja=(e,a)=>{if(!(e==null||a==null)){var t=e.find(r=>r.stackId===a.stackId&&a.dataKey!=null&&r.dataKeys.includes(a.dataKey));if(t!=null)return t.position}};function Ma(e,a){return e&&typeof e=="object"&&"zIndex"in e&&typeof e.zIndex=="number"&&J(e.zIndex)?e.zIndex:a}var Ka="Invariant failed";function za(e,a){throw new Error(Ka)}var La=["option"];function Ta(e,a){if(e==null)return{};var t,r,n=Ra(e,a);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)t=i[r],a.indexOf(t)===-1&&{}.propertyIsEnumerable.call(e,t)&&(n[t]=e[t])}return n}function Ra(e,a){if(e==null)return{};var t={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(a.indexOf(r)!==-1)continue;t[r]=e[r]}return t}var te=Re;function re(e){var a=e.option,t=Ta(e,La);return u.createElement(Fe,{option:a,DefaultShape:te,shapeProps:t,activeClassName:"recharts-active-bar",inActiveClassName:"recharts-inactive-bar"})}var Fa=function(a){var t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:0;return(r,n)=>{if(se(a))return a;var i=se(r)||V(r);return i?a(r,n):(i||za(),t)}},Na=(e,a,t)=>t,$a=(e,a)=>a,$=j([be,$a],(e,a)=>e.filter(t=>t.type==="bar").find(t=>t.id===a)),Wa=j([$],e=>e==null?void 0:e.maxBarSize),_a=(e,a,t,r)=>r,Ga=j([F,be,K,z,Na],(e,a,t,r,n)=>a.filter(i=>e==="horizontal"?i.xAxisId===t:i.yAxisId===r).filter(i=>i.isPanorama===n).filter(i=>i.hide===!1).filter(i=>i.type==="bar")),Ua=(e,a,t)=>{var r=F(e),n=K(e,a),i=z(e,a);if(!(n==null||i==null))return r==="horizontal"?ue(e,"yAxis",i,t):ue(e,"xAxis",n,t)},Va=(e,a)=>{var t=F(e),r=K(e,a),n=z(e,a);if(!(r==null||n==null))return t==="horizontal"?ce(e,"xAxis",r):ce(e,"yAxis",n)},Za=j([Ga,Ue,Va],Ba),Xa=(e,a,t)=>{var r,n,i=$(e,a);if(i==null)return 0;var o=K(e,a),s=z(e,a);if(o==null||s==null)return 0;var c=F(e),l=Ae(e),d=i.maxBarSize,v=V(d)?l:d,f,y;return c==="horizontal"?(f=T(e,"xAxis",o,t),y=R(e,"xAxis",o,t)):(f=T(e,"yAxis",s,t),y=R(e,"yAxis",s,t)),(r=(n=ge(f,y,!0))!==null&&n!==void 0?n:v)!==null&&r!==void 0?r:0},Ce=(e,a,t)=>{var r=F(e),n=K(e,a),i=z(e,a);if(!(n==null||i==null)){var o,s;return r==="horizontal"?(o=T(e,"xAxis",n,t),s=R(e,"xAxis",n,t)):(o=T(e,"yAxis",i,t),s=R(e,"yAxis",i,t)),ge(o,s)}},Ya=j([Za,Ae,_e,Ge,Xa,Ce,Wa],Ca),Ha=(e,a,t)=>{var r=K(e,a);if(r!=null)return T(e,"xAxis",r,t)},qa=(e,a,t)=>{var r=z(e,a);if(r!=null)return T(e,"yAxis",r,t)},Ja=(e,a,t)=>{var r=K(e,a);if(r!=null)return R(e,"xAxis",r,t)},Qa=(e,a,t)=>{var r=z(e,a);if(r!=null)return R(e,"yAxis",r,t)},et=j([Ya,$],ja),at=j([Ua,$],Da),tt=j([Ne,$e,Ha,qa,Ja,Qa,et,F,We,Ce,at,$,_a],(e,a,t,r,n,i,o,s,c,l,d,v,f)=>{var y=c.chartData,m=c.dataStartIndex,x=c.dataEndIndex;if(!(v==null||o==null||a==null||s!=="horizontal"&&s!=="vertical"||t==null||r==null||n==null||i==null||l==null)){var h=v.data,b;if(h!=null&&h.length>0?b=h:b=y==null?void 0:y.slice(m,x+1),b!=null)return zt({layout:s,barSettings:v,pos:o,parentViewBox:a,bandSize:l,xAxis:t,yAxis:r,xAxisTicks:n,yAxisTicks:i,stackedData:d,displayedData:b,offset:e,cells:f,dataStartIndex:m})}}),rt=["index"];function Q(){return Q=Object.assign?Object.assign.bind():function(e){for(var a=1;a<arguments.length;a++){var t=arguments[a];for(var r in t)({}).hasOwnProperty.call(t,r)&&(e[r]=t[r])}return e},Q.apply(null,arguments)}function nt(e,a){if(e==null)return{};var t,r,n=it(e,a);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)t=i[r],a.indexOf(t)===-1&&{}.propertyIsEnumerable.call(e,t)&&(n[t]=e[t])}return n}function it(e,a){if(e==null)return{};var t={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(a.indexOf(r)!==-1)continue;t[r]=e[r]}return t}var De=u.createContext(void 0),ot=e=>{var a=u.useContext(De);if(a!=null)return a.stackId;if(e!=null)return Ve(e)},lt=(e,a)=>"recharts-bar-stack-clip-path-".concat(e,"-").concat(a),st=e=>{var a=u.useContext(De);if(a!=null){var t=a.stackId;return"url(#".concat(lt(t,e),")")}},je=e=>{var a=e.index,t=nt(e,rt),r=st(a);return u.createElement(_,Q({className:"recharts-bar-stack-layer",clipPath:r},t))},ut=["onMouseEnter","onMouseLeave","onClick"],ct=["value","background","tooltipPosition"],dt=["id"],vt=["onMouseEnter","onClick","onMouseLeave"];function he(e,a){return ht(e)||pt(e,a)||mt(e,a)||ft()}function ft(){throw new TypeError(`Invalid attempt to destructure non-iterable instance.
|
|
3
|
+
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function mt(e,a){if(e){if(typeof e=="string")return ye(e,a);var t={}.toString.call(e).slice(8,-1);return t==="Object"&&e.constructor&&(t=e.constructor.name),t==="Map"||t==="Set"?Array.from(e):t==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?ye(e,a):void 0}}function ye(e,a){(a==null||a>e.length)&&(a=e.length);for(var t=0,r=Array(a);t<a;t++)r[t]=e[t];return r}function pt(e,a){var t=e==null?null:typeof Symbol<"u"&&e[Symbol.iterator]||e["@@iterator"];if(t!=null){var r,n,i,o,s=[],c=!0,l=!1;try{if(i=(t=t.call(e)).next,a!==0)for(;!(c=(r=i.call(t)).done)&&(s.push(r.value),s.length!==a);c=!0);}catch(d){l=!0,n=d}finally{try{if(!c&&t.return!=null&&(o=t.return(),Object(o)!==o))return}finally{if(l)throw n}}return s}}function ht(e){if(Array.isArray(e))return e}function D(){return D=Object.assign?Object.assign.bind():function(e){for(var a=1;a<arguments.length;a++){var t=arguments[a];for(var r in t)({}).hasOwnProperty.call(t,r)&&(e[r]=t[r])}return e},D.apply(null,arguments)}function xe(e,a){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);a&&(r=r.filter(function(n){return Object.getOwnPropertyDescriptor(e,n).enumerable})),t.push.apply(t,r)}return t}function A(e){for(var a=1;a<arguments.length;a++){var t=arguments[a]!=null?arguments[a]:{};a%2?xe(Object(t),!0).forEach(function(r){yt(e,r,t[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(t)):xe(Object(t)).forEach(function(r){Object.defineProperty(e,r,Object.getOwnPropertyDescriptor(t,r))})}return e}function yt(e,a,t){return(a=xt(a))in e?Object.defineProperty(e,a,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[a]=t,e}function xt(e){var a=gt(e,"string");return typeof a=="symbol"?a:a+""}function gt(e,a){if(typeof e!="object"||!e)return e;var t=e[Symbol.toPrimitive];if(t!==void 0){var r=t.call(e,a);if(typeof r!="object")return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return(a==="string"?String:Number)(e)}function U(e,a){if(e==null)return{};var t,r,n=bt(e,a);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r<i.length;r++)t=i[r],a.indexOf(t)===-1&&{}.propertyIsEnumerable.call(e,t)&&(n[t]=e[t])}return n}function bt(e,a){if(e==null)return{};var t={};for(var r in e)if({}.hasOwnProperty.call(e,r)){if(a.indexOf(r)!==-1)continue;t[r]=e[r]}return t}var At=e=>{var a=e.dataKey,t=e.name,r=e.fill,n=e.legendType,i=e.hide;return[{inactive:i,dataKey:a,type:n,color:r,value:Pe(t,a),payload:e}]},It=u.memo(e=>{var a=e.dataKey,t=e.stroke,r=e.strokeWidth,n=e.fill,i=e.name,o=e.hide,s=e.unit,c=e.formatter,l=e.tooltipType,d=e.id,v={dataDefinedOnItem:void 0,getPosition:ea,settings:{stroke:t,strokeWidth:r,fill:n,dataKey:a,nameKey:void 0,name:Pe(i,a),hide:o,type:l,color:n,unit:s,formatter:c,graphicalItemId:d}};return u.createElement(Qe,{tooltipEntrySettings:v})});function Pt(e){var a=G(Be),t=e.data,r=e.dataKey,n=e.background,i=e.allOtherBarProps,o=i.onMouseEnter,s=i.onMouseLeave,c=i.onClick,l=U(i,ut),d=Ee(o,r,i.id),v=Se(s),f=Oe(c,r,i.id);if(!n||t==null)return null;var y=ca(n);return u.createElement(ae,{zIndex:Ma(n,ee.barBackground)},t.map((m,x)=>{m.value;var h=m.background;m.tooltipPosition;var b=U(m,ct);if(!h)return null;var g=d(m,m.originalDataIndex),I=v(m,m.originalDataIndex),p=f(m,m.originalDataIndex),k=A(A(A(A(A({option:n,isActive:String(m.originalDataIndex)===a},b),{},{fill:"#eee"},h),y),we(l,m,x)),{},{onMouseEnter:g,onMouseLeave:I,onClick:p,dataKey:r,index:x,className:"recharts-bar-background-rectangle"});return u.createElement(re,D({key:"background-bar-".concat(x)},k))}))}function kt(e){var a=e.showLabels,t=e.children,r=e.rects,n=r==null?void 0:r.map(i=>{var o={x:i.x,y:i.y,width:i.width,lowerWidth:i.width,upperWidth:i.width,height:i.height};return A(A({},o),{},{value:i.value,payload:i.payload,parentViewBox:i.parentViewBox,viewBox:o,fill:i.fill})});return u.createElement(ma,{value:a?n:void 0},t)}function Bt(e){var a=e.shape,t=e.activeBar,r=e.baseProps,n=e.entry,i=e.index,o=e.dataKey,s=G(Be),c=G(ha),l=t&&String(n.originalDataIndex)===s&&(c==null||o===c),d=s!=null&&(String(n.originalDataIndex)!==s||c!=null&&o!==c),v=u.useState(!1),f=he(v,2),y=f[0],m=f[1],x=u.useState(!1),h=he(x,2),b=h[0],g=h[1];u.useEffect(()=>{var P;return l?(m(!0),P=requestAnimationFrame(()=>{g(!0)})):(g(!1),d&&m(!1)),()=>{cancelAnimationFrame(P)}},[l,d]);var I=u.useCallback(()=>{l||m(!1)},[l]),p=l&&b,k=l||y,B;l?t===!0?B=a:B=t:B=a;var w=u.createElement(re,D({},r,{name:String(r.name)},n,{isActive:p,option:B,index:i,dataKey:o,animationElapsedTime:e.animationElapsedTime,isAnimating:e.isAnimating,isEntrance:e.isEntrance,onTransitionEnd:I}));return k?u.createElement(ae,{zIndex:ee.activeBar},u.createElement(je,{index:n.originalDataIndex},w)):w}function Et(e){var a=e.shape,t=e.baseProps,r=e.entry,n=e.index,i=e.dataKey;return u.createElement(re,D({},t,{name:String(t.name)},r,{isActive:!1,option:a,index:n,dataKey:i,animationElapsedTime:e.animationElapsedTime,isAnimating:e.isAnimating,isEntrance:e.isEntrance}))}function St(e){var a,t=e.data,r=e.props,n=e.animationElapsedTime,i=e.isAnimating,o=e.isEntrance,s=(a=pa(r))!==null&&a!==void 0?a:{},c=s.id,l=U(s,dt),d=r.shape,v=r.dataKey,f=r.activeBar,y=r.onMouseEnter,m=r.onClick,x=r.onMouseLeave,h=U(r,vt),b=Ee(y,v,c),g=Se(x),I=Oe(m,v,c);return t?u.createElement(u.Fragment,null,t.map((p,k)=>u.createElement(je,D({index:p.originalDataIndex,key:"rectangle-".concat(p==null?void 0:p.x,"-").concat(p==null?void 0:p.y,"-").concat(p==null?void 0:p.value,"-").concat(k),className:"recharts-bar-rectangle"},we(h,p,k),{onMouseEnter:b(p,p.originalDataIndex),onMouseLeave:g(p,p.originalDataIndex),onClick:I(p,p.originalDataIndex)}),f?u.createElement(Bt,{shape:d,activeBar:f,baseProps:l,entry:p,index:k,dataKey:v,animationElapsedTime:n,isAnimating:i,isEntrance:o}):u.createElement(Et,{shape:d,baseProps:l,entry:p,index:k,dataKey:v,animationElapsedTime:n,isAnimating:i,isEntrance:o})))):null}var Ot=(e,a,t)=>e==null?[]:a===1?e.flatMap(r=>r.status==="removed"?[]:[r.next]):e.flatMap(r=>{if(r.status==="removed")return t==="horizontal"?[A(A({},r.prev),{},{height:O(r.prev.height,0,a),y:O(r.prev.y,r.prev.y+r.prev.height,a)})]:[A(A({},r.prev),{},{width:O(r.prev.width,0,a)})];if(r.status==="matched")return[A(A({},r.next),{},{x:O(r.prev.x,r.next.x,a),y:O(r.prev.y,r.next.y,a),width:O(r.prev.width,r.next.width,a),height:O(r.prev.height,r.next.height,a)})];var n=r.next;return t==="horizontal"?[A(A({},n),{},{height:O(0,n.height,a),y:O(n.stackedBarStart,n.y,a)})]:[A(A({},n),{},{width:O(0,n.width,a),x:O(n.stackedBarStart,n.x,a)})]});function wt(e){var a=e.props,t=e.previousRectanglesRef,r=a.data,n=a.isAnimationActive,i=a.animationBegin,o=a.animationDuration,s=a.animationEasing,c=a.animationInterpolateFn,l=a.layout,d=da(a.onAnimationStart,a.onAnimationEnd),v=d.isAnimating,f=d.handleAnimationStart,y=d.handleAnimationEnd;return u.createElement(kt,{showLabels:!v,rects:r},u.createElement(va,{animationInput:r,animationIdPrefix:"recharts-bar-",items:r,previousItemsRef:t,isAnimationActive:n,animationBegin:i,animationDuration:o,animationEasing:s,onAnimationStart:f,onAnimationEnd:y,animationInterpolateFn:c,animationMatchBy:a.animationMatchBy,layout:l},(m,x,h)=>u.createElement(_,null,u.createElement(St,{props:a,data:m,animationElapsedTime:x,isAnimating:v||x<1,isEntrance:h}))),u.createElement(fa,{label:a.label}),a.children)}function Ct(e){var a=u.useRef(null);return u.createElement(wt,{previousRectanglesRef:a,props:e})}var Me=0,Dt=(e,a)=>{var t=Array.isArray(e.value)?e.value[1]:e.value;return{x:e.x,y:e.y,value:t,errorVal:ke(e,a)}};class jt extends u.PureComponent{render(){var a=this.props,t=a.hide,r=a.data,n=a.dataKey,i=a.className,o=a.xAxisId,s=a.yAxisId,c=a.needClip,l=a.background,d=a.id;if(t||r==null)return null;var v=oa("recharts-bar",i),f=d;return u.createElement(_,{className:v,id:d},c&&u.createElement("defs",null,u.createElement(la,{clipPathId:f,xAxisId:o,yAxisId:s})),u.createElement(_,{className:"recharts-bar-rectangles",clipPath:c?"url(#clipPath-".concat(f,")"):void 0},u.createElement(Pt,{data:r,dataKey:n,background:l,allOtherBarProps:this.props}),u.createElement(Ct,this.props)))}}var Mt={activeBar:!1,animationBegin:0,animationDuration:400,animationEasing:"ease",animationInterpolateFn:Ot,animationMatchBy:Ye,background:!1,hide:!1,isAnimationActive:"auto",label:!1,legendType:"rect",minPointSize:Me,shape:te,xAxisId:0,yAxisId:0,zIndex:ee.bar};function Kt(e){var a=e.xAxisId,t=e.yAxisId,r=e.hide,n=e.legendType,i=e.minPointSize,o=e.activeBar,s=e.animationBegin,c=e.animationDuration,l=e.animationEasing,d=e.isAnimationActive,v=aa(a,t),f=v.needClip,y=ta(),m=Ie(),x=ra(e.children,ia),h=G(I=>tt(I,e.id,m,x));if(y!=="vertical"&&y!=="horizontal")return null;var b,g=h==null?void 0:h[0];return g==null||g.height==null||g.width==null?b=0:b=y==="vertical"?g.height/2:g.width/2,u.createElement(na,{xAxisId:a,yAxisId:t,data:h,dataPointFormatter:Dt,errorBarOffset:b},u.createElement(jt,D({},e,{layout:y,needClip:f,data:h,xAxisId:a,yAxisId:t,hide:r,legendType:n,minPointSize:i,activeBar:o,animationBegin:s,animationDuration:c,animationEasing:l,isAnimationActive:d})))}function zt(e){var a=e.layout,t=e.barSettings,r=t.dataKey,n=t.minPointSize,i=t.hasCustomShape,o=e.pos,s=e.bandSize,c=e.xAxis,l=e.yAxis,d=e.xAxisTicks,v=e.yAxisTicks,f=e.stackedData,y=e.displayedData,m=e.offset,x=e.cells,h=e.parentViewBox,b=e.dataStartIndex,g=a==="horizontal"?l:c,I=f?g.scale.domain():null,p=sa({numericAxis:g}),k=g.scale.map(p);return y.map((B,w)=>{var P,L,M,E,S,Z;if(f){var ne=f[w+b];if(ne==null)return null;P=ua(ne,I)}else P=ke(B,r),Array.isArray(P)||(P=[p,P]);var C=Fa(n,Me)(P[1],w);if(a==="horizontal"){var X,Y=l.scale.map(P[0]),N=l.scale.map(P[1]);if(Y==null||N==null)return null;L=de({axis:c,ticks:d,bandSize:s,offset:o.offset,entry:B,index:w}),M=(X=N??Y)!==null&&X!==void 0?X:void 0,E=o.size;var ie=Y-N;if(S=ya(ie)?0:ie,Z={x:L,y:m.top,width:E,height:m.height},Math.abs(C)>0&&Math.abs(S)<Math.abs(C)){var oe=ve(S||C)*(Math.abs(C)-Math.abs(S));M-=oe,S+=oe}}else{var H=c.scale.map(P[0]),le=c.scale.map(P[1]);if(H==null||le==null)return null;if(L=H,M=de({axis:l,ticks:v,bandSize:s,offset:o.offset,entry:B,index:w}),E=le-H,S=o.size,Z={x:m.left,y:M,width:m.width,height:S},Math.abs(C)>0&&Math.abs(E)<Math.abs(C)){var Ke=ve(E||C)*(Math.abs(C)-Math.abs(E));E+=Ke}}if(L==null||M==null||E==null||S==null||!i&&(E===0||S===0))return null;var ze=A(A({},B),{},{stackedBarStart:k,x:L,y:M,width:E,height:S,value:f?P:P[1],payload:B,background:Z,tooltipPosition:{x:L+E/2,y:M+S/2},parentViewBox:h,originalDataIndex:w},x&&x[w]&&x[w].props);return ze}).filter(Boolean)}function Lt(e){var a=Xe(e,Mt),t=ot(a.stackId),r=Ie();return u.createElement(He,{id:a.id,type:"bar"},n=>u.createElement(u.Fragment,null,u.createElement(qe,{legendPayload:At(a)}),u.createElement(It,{dataKey:a.dataKey,stroke:a.stroke,strokeWidth:a.strokeWidth,fill:a.fill,name:a.name,hide:a.hide,unit:a.unit,formatter:a.formatter,tooltipType:a.tooltipType,id:n}),u.createElement(Je,{type:"bar",id:n,data:void 0,xAxisId:a.xAxisId,yAxisId:a.yAxisId,zAxisId:0,dataKey:a.dataKey,stackId:t,hide:a.hide,barSize:a.barSize,minPointSize:a.minPointSize,maxBarSize:a.maxBarSize,isPanorama:r,hasCustomShape:a.shape!=null&&a.shape!==te}),u.createElement(ae,{zIndex:a.zIndex},u.createElement(Kt,D({},a,{id:n})))))}var Tt=u.memo(Lt,Ze);Tt.displayName="Bar";var Rt=["axis","item"],Wt=u.forwardRef((e,a)=>u.createElement(xa,{chartName:"BarChart",defaultTooltipEventType:"axis",validateTooltipEventTypes:Rt,tooltipPayloadSearcher:ga,categoricalChartProps:e,ref:a}));export{Wt as B,Tt as a};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as p,f as D,a as P,b as y,c as B,d as I,j as t}from"./index-CUyiomzU.js";import{w as q}from"./workspace-label-CysPJNc0.js";import{I as g}from"./InfoTip-Ckc9_LpJ.js";import{b as Y}from"./prompt-templates-DhR4Qoy9.js";import{f as G}from"./FrictionCell-DciiSd3L.js";import"./Chip-Cpt_fEc9.js";const V=1e6,R=5,K=3;function c(e){return Number.isFinite(e)?e:0}function m(e){return c(e)/V}function W(e){let r=0,s=0;for(const d of e.buckets)r+=c(d.cache_creation_tokens),s+=c(d.cache_read_tokens);const o=r+s;return o===0?0:r/o}function E(e,r){return e.filter(s=>r===null||s.workspace_id===r)}const A={LOW:0,ELEVATED:1,HIGH:2};function X(e){if(e.length===0)return null;let r="LOW";for(const s of e){const o=G({api_error_count:s.api_error_count,tool_error_count:s.tool_error_count,test_fail_count:s.test_fail_count,compaction_count:s.compaction_count,interrupt_count:s.interrupt_count,user_turn_count:s.user_turn_count,turn_count:s.turns});A[o]>A[r]&&(r=o)}return r}function j(e,r){return{current:e,prior:r,delta:r===null?null:e-r}}function z(e,r){return c(r.cost_equiv_u)-c(e.cost_equiv_u)||e.session_id.localeCompare(r.session_id)}function J(e,r){const s=+(e.flavor!=="TURNKEY")-+(r.flavor!=="TURNKEY"),o=e.modeled_savings_usd_per_wk??Number.NEGATIVE_INFINITY,d=r.modeled_savings_usd_per_wk??Number.NEGATIVE_INFINITY;return s||d-o||e.detector_id.localeCompare(r.detector_id)||e.id.localeCompare(r.id)}function Q(e){const r=e.hotSessions.filter(n=>e.scopeWorkspaceId===null||n.workspace_id===e.scopeWorkspaceId).slice().sort(z).slice(0,R).map(n=>({session_id:n.session_id,usd:m(n.cost_equiv_u),turns:c(n.turns),avg_context_tokens:c(n.avg_context_tokens),model:n.model}));let s=0,o=0,d=0;for(const n of e.cacheTrend.buckets)s+=c(n.cache_creation_tokens),o+=c(n.cache_read_tokens),d+=c(n.turns);const x=s+o,b=e.recs.map(n=>{const f=Y(n);return f===null?null:{id:n.rec_id,detector_id:n.detector_id,lever:n.lever,flavor:f.flavor,modeled_savings_usd_per_wk:n.modeled_savings_u_per_wk===null?null:m(n.modeled_savings_u_per_wk),prompt:f.text}}).filter(n=>n!==null).sort(J),h=E(e.hotSessions,e.scopeWorkspaceId),l=e.prior!==void 0&&c(e.prior.overview.turns_total)>0?e.prior:null;return{scope:{label:e.scopeLabel,workspace_id:e.scopeWorkspaceId},verdict:{cost_usd:m(e.overview.cost_equiv_u),hot_session_count:h.length,peak_friction:X(h)},deltas:{spend_usd:j(m(e.overview.cost_equiv_u),l===null?null:m(l.overview.cost_equiv_u)),cache_write_share:j(W(e.cacheTrend),l===null?null:W(l.cacheTrend)),hot_session_count:j(h.length,l===null?null:E(l.hotSessions,e.scopeWorkspaceId).length)},actions:b.slice(0,K),overview:{cost_usd:m(e.overview.cost_equiv_u),turns:c(e.overview.turns),turns_total:c(e.overview.turns_total)},attribution:{hot_sessions:r,context_per_turn:e.overview.context_per_turn.map(n=>({model:n.model,turns:c(n.n),avg_context_per_turn:c(n.avg_context_per_turn),avg_output_per_turn:c(n.avg_output_per_turn),usd_per_turn:n.usd_per_turn===null?null:c(n.usd_per_turn)})),model_mix:e.overview.model_mix.map(n=>({model:n.model,turns:c(n.turns)})),cache_mix:{bucket_count:e.cacheTrend.buckets.length,spike_bucket_count:e.cacheTrend.spike_buckets.length,cache_write_tokens:s,cache_read_tokens:o,cache_read_share:x===0?0:o/x,turns:d}}}}function a(e){return c(e).toLocaleString(void 0,{maximumFractionDigits:6})}function k(e){const r=e.prior===null?"—":a(e.prior),s=e.delta===null?"—":a(e.delta);return`${a(e.current)} (prior ${r}, delta ${s})`}function Z(e){const r=[`# Brief: ${e.scope.label}`,"","## Verdict",`- Cost equivalent USD: ${a(e.verdict.cost_usd)}`,`- Hot sessions: ${a(e.verdict.hot_session_count)}`,`- Peak friction: ${e.verdict.peak_friction??"none"}`,"","## Change vs prior 7 days",`- Spend equivalent USD: ${k(e.deltas.spend_usd)}`,`- Cache-write share: ${k(e.deltas.cache_write_share)}`,`- Hot sessions: ${k(e.deltas.hot_session_count)}`,"","## Do these three things",`- Actions: ${a(e.actions.length)}`];return e.actions.forEach((s,o)=>{r.push(`### ${o+1}. ${s.detector_id}: ${s.id}`),r.push(`- Lever: ${s.lever}`),r.push(`- Mode: ${s.flavor}`),r.push(`- Modeled savings USD/week: ${s.modeled_savings_usd_per_wk===null?"0":a(s.modeled_savings_usd_per_wk)}`),r.push("- Prompt artifact:"),r.push(s.prompt)}),r.push("","<details>","","## Attribution",`- Hot sessions: ${a(e.attribution.hot_sessions.length)}`,...e.attribution.hot_sessions.map(s=>`- Session ${s.session_id}: USD ${a(s.usd)}, turns ${a(s.turns)}, context ${a(s.avg_context_tokens)}, model ${s.model}`),`- Context models: ${a(e.attribution.context_per_turn.length)}`,...e.attribution.context_per_turn.map(s=>{const o=s.usd_per_turn===null?"unpriced 0":`USD/turn ${a(s.usd_per_turn)}`;return`- Model ${s.model}: turns ${a(s.turns)}, context/turn ${a(s.avg_context_per_turn)}, output/turn ${a(s.avg_output_per_turn)}, ${o}`}),`- Model mix entries: ${a(e.attribution.model_mix.length)}`,...e.attribution.model_mix.map(s=>`- Model mix ${s.model}: turns ${a(s.turns)}`),`- Cache mix: ${a(e.attribution.cache_mix.bucket_count)} buckets, ${a(e.attribution.cache_mix.spike_bucket_count)} spikes, ${a(e.attribution.cache_mix.cache_write_tokens)} write tokens, ${a(e.attribution.cache_mix.cache_read_tokens)} read tokens, ${a(e.attribution.cache_mix.cache_read_share)} read share, ${a(e.attribution.cache_mix.turns)} turns`,"","</details>"),r.join(`
|
|
2
|
+
`)}const M=7,L=1440*60*1e3;function ee(e=new Date){const r=e.getTime(),s=r-M*L,o=s-M*L;return{current:{from:new Date(s).toISOString(),to:new Date(r).toISOString()},prior:{from:new Date(o).toISOString(),to:new Date(s).toISOString()},label:`${new Date(s).toLocaleDateString()} – ${new Date(r).toLocaleDateString()}`}}function u(e){return e.toLocaleString(void 0,{maximumFractionDigits:2})}function v(e){return`$${u(e)}`}function O(e){return`${(e*100).toLocaleString(void 0,{maximumFractionDigits:1})}%`}function te(e,r){if(r===null)return"Global";const s=e.find(o=>o.workspace_id===r);return s===void 0?r:q(s)}function se({delta:e,format:r}){if(e.delta===null)return t.jsx("span",{className:"brief-tile-delta brief-tile-delta-none",title:"No prior-week data to compare",children:"— vs prior week"});const s=e.delta>0?"up":e.delta<0?"down":"flat",o=s==="up"?"▲":s==="down"?"▼":"▬",d=e.delta>0?"+":"";return t.jsxs("span",{className:`brief-tile-delta brief-tile-delta-${s}`,children:[o," ",d,r(e.delta)," vs prior week"]})}function N({label:e,value:r,delta:s,format:o}){return t.jsxs("div",{className:"brief-tile",children:[t.jsx("div",{className:"brief-tile-label",children:e}),t.jsx("div",{className:"brief-tile-value",children:r}),t.jsx(se,{delta:s,format:o})]})}function re({brief:e}){const{cache_mix:r,hot_sessions:s}=e.attribution;return t.jsxs("details",{className:"briefs-details",children:[t.jsx("summary",{children:"Attribution & hot sessions"}),t.jsx("div",{className:"table-wrap",children:t.jsxs("table",{children:[t.jsx("thead",{children:t.jsxs("tr",{children:[t.jsx("th",{scope:"col",children:"Measure"}),t.jsx("th",{scope:"col",children:"Value"})]})}),t.jsxs("tbody",{children:[t.jsxs("tr",{children:[t.jsx("td",{children:"Cost equivalent"}),t.jsx("td",{children:v(e.overview.cost_usd)})]}),t.jsxs("tr",{children:[t.jsx("td",{children:"Reconciled turns"}),t.jsx("td",{children:u(e.overview.turns)})]}),t.jsxs("tr",{children:[t.jsx("td",{children:"Total turns"}),t.jsx("td",{children:u(e.overview.turns_total)})]}),t.jsxs("tr",{children:[t.jsx("td",{children:"Cache write tokens"}),t.jsx("td",{children:u(r.cache_write_tokens)})]}),t.jsxs("tr",{children:[t.jsx("td",{children:"Cache read tokens"}),t.jsx("td",{children:u(r.cache_read_tokens)})]}),t.jsxs("tr",{children:[t.jsx("td",{children:"Hot sessions"}),t.jsx("td",{children:u(s.length)})]})]})]})}),s.length>0&&t.jsx("div",{className:"table-wrap",children:t.jsxs("table",{children:[t.jsx("thead",{children:t.jsxs("tr",{children:[t.jsx("th",{scope:"col",children:"Session"}),t.jsx("th",{scope:"col",children:"Cost"}),t.jsx("th",{scope:"col",children:"Turns"}),t.jsx("th",{scope:"col",children:"Context / turn"}),t.jsx("th",{scope:"col",children:"Model"})]})}),t.jsx("tbody",{children:s.map(o=>t.jsxs("tr",{children:[t.jsx("td",{children:o.session_id}),t.jsx("td",{children:v(o.usd)}),t.jsx("td",{children:u(o.turns)}),t.jsx("td",{children:u(o.avg_context_tokens)}),t.jsx("td",{children:o.model})]},o.session_id))})]})})]})}function de(){const[e,r]=p.useState({status:"loading"}),[s,o]=p.useState(null),[d,x]=p.useState(!1),[b,h]=p.useState(null),l=p.useMemo(()=>ee(),[]);p.useEffect(()=>{let i=!1;return Promise.all([D(l.current),P(l.current),y(l.current),B(),I(l.current),D(l.prior),y(l.prior),I(l.prior)]).then(([_,w,S,$,U,T,C,F])=>{if(!i){if(_.data===null||w.data===null||S.data===null||$.data===null||T.data===null||C.data===null)throw new Error("Brief data was unavailable");r({status:"ok",overview:_.data,workspaces:w.data.items,cacheTrend:S.data,recs:$.data,hotSessions:U,priorOverview:T.data,priorCacheTrend:C.data,priorHotSessions:F})}}).catch(_=>{i||r({status:"error",message:String(_)})}),()=>{i=!0}},[l]);const n=e.status==="ok"?Q({scopeLabel:te(e.workspaces,s),scopeWorkspaceId:s,overview:e.overview,hotSessions:e.hotSessions,cacheTrend:e.cacheTrend,recs:e.recs.active.filter(i=>s===null||i.scope_workspace_id===null||i.scope_workspace_id===s),prior:{overview:e.priorOverview,hotSessions:e.priorHotSessions,cacheTrend:e.priorCacheTrend}}):null;async function f(){n!==null&&(await navigator.clipboard.writeText(Z(n)),x(!0),window.setTimeout(()=>x(!1),2e3))}async function H(i,_){await navigator.clipboard.writeText(_),h(i),window.setTimeout(()=>h(w=>w===i?null:w),2e3)}return t.jsxs("div",{className:"briefs-page",children:[t.jsx("div",{className:"page-top",children:t.jsxs("div",{className:"page-title",children:[t.jsx("h1",{children:"Briefs"}),t.jsx("p",{className:"page-sub",children:"This week in one page: verdict, what changed, what to do"})]})}),e.status==="loading"&&t.jsx("div",{className:"card","aria-busy":"true",children:"Loading brief data..."}),e.status==="error"&&t.jsxs("div",{className:"banner banner-error",role:"alert",children:["Could not load brief: ",e.message]}),e.status==="ok"&&n!==null&&t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"briefs-controls",children:[t.jsxs("label",{className:"briefs-scope",htmlFor:"briefs-scope",children:["Scope",t.jsxs("select",{id:"briefs-scope",value:s??"",onChange:i=>o(i.target.value||null),children:[t.jsx("option",{value:"",children:"Global"}),e.workspaces.map(i=>t.jsx("option",{value:i.workspace_id,children:q(i)},i.workspace_id))]})]}),t.jsx("span",{className:"briefs-window",title:"Rolling seven-day window",children:l.label}),t.jsx("button",{type:"button",className:"btn-primary",onClick:()=>void f(),children:d?"Copied":"Copy as markdown"})]}),t.jsx("section",{className:"card briefs-verdict","aria-labelledby":"briefs-verdict-heading",children:t.jsxs("h2",{id:"briefs-verdict-heading",className:"briefs-verdict-line",children:[t.jsx("strong",{children:v(n.verdict.cost_usd)})," ",t.jsx(g,{label:"What cap-weighted equivalent means",content:"Cost weighted the way your usage cap counts it — cache reads count roughly a tenth of fresh tokens. It's the number that actually moves you toward a limit, not raw token cost.",children:"cap-weighted equivalent"}),t.jsx("span",{className:"briefs-verdict-sep",children:" · "}),u(n.verdict.hot_session_count)," hot sessions",t.jsx("span",{className:"briefs-verdict-sep",children:" · "}),n.verdict.peak_friction===null?t.jsx("span",{className:"briefs-friction",children:"no friction signal"}):t.jsxs("span",{className:`briefs-friction briefs-friction-${n.verdict.peak_friction}`,children:[t.jsx(g,{label:"What peak friction means",content:"The single worst per-session friction band across this scope's hot sessions, not an average. It flags whether any one session went badly, which an average would hide.",children:"peak friction"})," ",n.verdict.peak_friction]})]})}),t.jsxs("div",{className:"brief-tiles-head",children:[t.jsx("span",{className:"brief-tiles-heading",children:"Week-over-week changes"}),t.jsx(g,{label:"What the delta tiles show",content:"Change versus the prior 7 days for spend, cache-write share, and hot-session count. A green delta is improvement; a red one is where this week got worse."})]}),t.jsxs("div",{className:"brief-tiles",children:[t.jsx(N,{label:"Spend equivalent",value:v(n.deltas.spend_usd.current),delta:n.deltas.spend_usd,format:v}),t.jsx(N,{label:"Cache-write share",value:O(n.deltas.cache_write_share.current),delta:n.deltas.cache_write_share,format:O}),t.jsx(N,{label:"Hot sessions",value:u(n.deltas.hot_session_count.current),delta:n.deltas.hot_session_count,format:u})]}),t.jsxs("section",{className:"card briefs-actions","aria-labelledby":"briefs-actions-heading",children:[t.jsx("div",{className:"section-head",children:t.jsx("h2",{id:"briefs-actions-heading",children:"Do these three things"})}),n.actions.length===0?t.jsx("p",{className:"kpi-off-hint",children:"No active recommendations were returned for this scope."}):t.jsx("ol",{className:"briefs-action-list",children:n.actions.map(i=>t.jsxs("li",{className:"briefs-action",children:[t.jsxs("div",{className:"briefs-action-main",children:[t.jsx("strong",{children:i.lever}),t.jsxs("span",{className:"kpi-off-hint",children:[i.detector_id," · ",i.flavor," · modeled savings"," ",i.modeled_savings_usd_per_wk===null?"unavailable":`${v(i.modeled_savings_usd_per_wk)} / week`]})]}),t.jsx("button",{type:"button",className:"btn-secondary briefs-action-copy",onClick:()=>void H(i.id,i.prompt),children:b===i.id?"Copied":"Copy prompt"})]},i.id))})]}),t.jsx(re,{brief:n})]})]})}export{de as default};
|