pi-mega-compact 0.8.12 → 0.8.15

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.
Files changed (127) hide show
  1. package/README.md +44 -21
  2. package/dist/extensions/mega-cache-replay.test.js +4 -2
  3. package/dist/extensions/mega-compact-s38.test.js +317 -0
  4. package/dist/extensions/mega-compact.js +14 -0
  5. package/dist/extensions/mega-compact.test.js +37 -8
  6. package/dist/extensions/mega-config.js +5 -0
  7. package/dist/extensions/mega-events/agent-handlers.js +184 -4
  8. package/dist/extensions/mega-events/context-handler.js +35 -2
  9. package/dist/extensions/mega-events/error-classifier.js +118 -0
  10. package/dist/extensions/mega-events.js +1 -0
  11. package/dist/extensions/mega-runtime/state.js +16 -0
  12. package/dist/extensions/mega-teamrun.test.js +14 -1
  13. package/extensions/dashboard-client/dist/assets/AchievementTiles-BH26e95w.js +2 -0
  14. package/extensions/dashboard-client/dist/assets/AchievementTiles-BH26e95w.js.map +1 -0
  15. package/extensions/dashboard-client/dist/assets/AchievementsTab-B94wvRV_.js +2 -0
  16. package/extensions/dashboard-client/dist/assets/AchievementsTab-B94wvRV_.js.map +1 -0
  17. package/extensions/dashboard-client/dist/assets/CacheTab-DPmw3NHR.js +2 -0
  18. package/extensions/dashboard-client/dist/assets/CacheTab-DPmw3NHR.js.map +1 -0
  19. package/extensions/dashboard-client/dist/assets/ConfigTab-C3g3smRL.js +2 -0
  20. package/extensions/dashboard-client/dist/assets/ConfigTab-C3g3smRL.js.map +1 -0
  21. package/extensions/dashboard-client/dist/assets/EventsTab-B5PicfFw.js +2 -0
  22. package/extensions/dashboard-client/dist/assets/EventsTab-B5PicfFw.js.map +1 -0
  23. package/extensions/dashboard-client/dist/assets/GameTab-CsVwp78a.js +2 -0
  24. package/extensions/dashboard-client/dist/assets/GameTab-CsVwp78a.js.map +1 -0
  25. package/extensions/dashboard-client/dist/assets/MetricsTab-Cf72UewX.js +2 -0
  26. package/extensions/dashboard-client/dist/assets/MetricsTab-Cf72UewX.js.map +1 -0
  27. package/extensions/dashboard-client/dist/assets/OverviewTab-Dh6aG3gy.js +2 -0
  28. package/extensions/dashboard-client/dist/assets/OverviewTab-Dh6aG3gy.js.map +1 -0
  29. package/extensions/dashboard-client/dist/assets/ReposTab-HXf2dorl.js +2 -0
  30. package/extensions/dashboard-client/dist/assets/ReposTab-HXf2dorl.js.map +1 -0
  31. package/extensions/dashboard-client/dist/assets/TimeSavedCard-kUnoospE.js +2 -0
  32. package/extensions/dashboard-client/dist/assets/TimeSavedCard-kUnoospE.js.map +1 -0
  33. package/extensions/dashboard-client/dist/assets/index-Cfxniu2R.css +1 -0
  34. package/extensions/dashboard-client/dist/assets/{index-XAIN8km6.js → index-DMntLu-8.js} +11 -10
  35. package/extensions/dashboard-client/dist/assets/index-DMntLu-8.js.map +1 -0
  36. package/extensions/dashboard-client/dist/index.html +2 -2
  37. package/extensions/dashboard-client/src/App.tsx +18 -4
  38. package/extensions/dashboard-client/src/components/AchievementTiles.tsx +105 -0
  39. package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +116 -0
  40. package/extensions/dashboard-client/src/components/CacheHitsCard.tsx +73 -0
  41. package/extensions/dashboard-client/src/components/CacheStatusPerModel.tsx +78 -35
  42. package/extensions/dashboard-client/src/components/ConfigSummaryCard.tsx +101 -0
  43. package/extensions/dashboard-client/src/components/ContextGauge.tsx +11 -15
  44. package/extensions/dashboard-client/src/components/DataSafetyCard.tsx +42 -33
  45. package/extensions/dashboard-client/src/components/EventCategoryFilter.tsx +46 -9
  46. package/extensions/dashboard-client/src/components/LegendCard.tsx +49 -0
  47. package/extensions/dashboard-client/src/components/ModelBadge.tsx +42 -11
  48. package/extensions/dashboard-client/src/components/PerfCards.tsx +91 -0
  49. package/extensions/dashboard-client/src/components/RepoAllSessionsCard.tsx +102 -0
  50. package/extensions/dashboard-client/src/components/RepoDetailModal.tsx +9 -2
  51. package/extensions/dashboard-client/src/components/RepoTable.tsx +30 -22
  52. package/extensions/dashboard-client/src/components/SavingsByModelTable.tsx +226 -0
  53. package/extensions/dashboard-client/src/components/SessionInfo.tsx +25 -50
  54. package/extensions/dashboard-client/src/components/SummaryTiles.tsx +20 -11
  55. package/extensions/dashboard-client/src/components/TimeSavedCard.tsx +61 -0
  56. package/extensions/dashboard-client/src/components/TriggerStatus.tsx +31 -19
  57. package/extensions/dashboard-client/src/components/VectorStoreCard.tsx +134 -0
  58. package/extensions/dashboard-client/src/main.tsx +5 -0
  59. package/extensions/dashboard-client/src/styles/base.css +77 -9
  60. package/extensions/dashboard-client/src/styles/config.css +149 -0
  61. package/extensions/dashboard-client/src/styles/game-achievements.css +265 -0
  62. package/extensions/dashboard-client/src/styles/metrics-extra.css +69 -0
  63. package/extensions/dashboard-client/src/styles/overview-extra.css +170 -0
  64. package/extensions/dashboard-client/src/styles/repos-extra.css +99 -0
  65. package/extensions/dashboard-client/src/styles/repos-metrics.css +14 -23
  66. package/extensions/dashboard-client/src/tabs/AchievementsTab.tsx +55 -0
  67. package/extensions/dashboard-client/src/tabs/CacheTab.tsx +50 -0
  68. package/extensions/dashboard-client/src/tabs/ConfigTab.tsx +240 -5
  69. package/extensions/dashboard-client/src/tabs/EventsTab.tsx +55 -3
  70. package/extensions/dashboard-client/src/tabs/GameTab.tsx +287 -0
  71. package/extensions/dashboard-client/src/tabs/MetricsTab.tsx +7 -1
  72. package/extensions/dashboard-client/src/tabs/OverviewTab.tsx +103 -17
  73. package/extensions/dashboard-client/src/tabs/ReposTab.tsx +57 -22
  74. package/extensions/dashboard-client/src/utils/format.ts +85 -0
  75. package/extensions/dashboard-client/src/utils/types.ts +27 -0
  76. package/extensions/mega-cache-replay.test.ts +3 -3
  77. package/extensions/mega-compact-s38.test.ts +330 -0
  78. package/extensions/mega-compact.test.ts +41 -13
  79. package/extensions/mega-compact.ts +14 -0
  80. package/extensions/mega-config.ts +24 -0
  81. package/extensions/mega-dashboard.ts +7 -0
  82. package/extensions/mega-events/agent-handlers.ts +179 -5
  83. package/extensions/mega-events/context-handler.ts +31 -2
  84. package/extensions/mega-events/error-classifier.ts +109 -0
  85. package/extensions/mega-events.ts +1 -0
  86. package/extensions/mega-runtime/helpers.ts +5 -0
  87. package/extensions/mega-runtime/state.ts +17 -1
  88. package/extensions/mega-teamrun.test.ts +13 -1
  89. package/package.json +4 -1
  90. package/dist/extensions/dashboard-server/helpers.js +0 -37
  91. package/dist/extensions/dashboard-server/html/all-repos-tab.js +0 -26
  92. package/dist/extensions/dashboard-server/html/body-open.js +0 -23
  93. package/dist/extensions/dashboard-server/html/current-repo-tab.js +0 -130
  94. package/dist/extensions/dashboard-server/html/head-open.js +0 -16
  95. package/dist/extensions/dashboard-server/html/high-score-tab.js +0 -25
  96. package/dist/extensions/dashboard-server/html/repo-detail-modal.js +0 -26
  97. package/dist/extensions/dashboard-server/html/script.js +0 -259
  98. package/dist/extensions/dashboard-server/html/styles.js +0 -103
  99. package/dist/extensions/dashboard-server/html/summary-tab.js +0 -19
  100. package/dist/extensions/dashboard-server/html-template.js +0 -41
  101. package/dist/src/store/sqlite/connection.js +0 -35
  102. package/dist/src/store/sqlite/index-store.js +0 -167
  103. package/dist/src/store/sqlite/memory.js +0 -54
  104. package/dist/src/store/sqlite/minhash-lsh.js +0 -47
  105. package/dist/src/store/sqlite/sessions.js +0 -39
  106. package/dist/src/store/sqlite/transaction.js +0 -19
  107. package/dist/src/vectorStore/add.js +0 -260
  108. package/dist/src/vectorStore/dedup.js +0 -52
  109. package/dist/src/vectorStore/index.js +0 -10
  110. package/dist/src/vectorStore/queries.js +0 -83
  111. package/dist/src/vectorStore/search.js +0 -95
  112. package/dist/src/vectorStore/session.js +0 -19
  113. package/dist/src/vectorStore/store.js +0 -105
  114. package/dist/src/vectorStore/types.js +0 -6
  115. package/dist/src/vectorStore/utils.js +0 -23
  116. package/extensions/dashboard-client/dist/assets/ConfigTab-RrjXWfvr.js +0 -2
  117. package/extensions/dashboard-client/dist/assets/ConfigTab-RrjXWfvr.js.map +0 -1
  118. package/extensions/dashboard-client/dist/assets/EventsTab-BH_FxJ08.js +0 -2
  119. package/extensions/dashboard-client/dist/assets/EventsTab-BH_FxJ08.js.map +0 -1
  120. package/extensions/dashboard-client/dist/assets/MetricsTab-C-PoFV4p.js +0 -2
  121. package/extensions/dashboard-client/dist/assets/MetricsTab-C-PoFV4p.js.map +0 -1
  122. package/extensions/dashboard-client/dist/assets/OverviewTab-Db-qBqFN.js +0 -2
  123. package/extensions/dashboard-client/dist/assets/OverviewTab-Db-qBqFN.js.map +0 -1
  124. package/extensions/dashboard-client/dist/assets/ReposTab-DhCjJvuj.js +0 -2
  125. package/extensions/dashboard-client/dist/assets/ReposTab-DhCjJvuj.js.map +0 -1
  126. package/extensions/dashboard-client/dist/assets/index-Buer5yxB.css +0 -1
  127. package/extensions/dashboard-client/dist/assets/index-XAIN8km6.js.map +0 -1
@@ -1,52 +0,0 @@
1
- /**
2
- * vectorStore/dedup.ts — SemDeDup cleanup and dedup sentinel check.
3
- */
4
- import { cosineSimilarity } from "../embedder.js";
5
- import { normalizeSessionId } from "../store.js";
6
- import { listCheckpoints, setDedupStatus, loadSessionState } from "../store/sqlite.js";
7
- import { computeRegionHash } from "./utils.js";
8
- /**
9
- * SemDeDup offline cleanup (Sprint 12, QA #17): within a session, mark the
10
- * lower-quality row of any pair scoring cosine > `threshold` as
11
- * `dedup_status='removed'` (kept, not deleted — retrieval excludes it). Keeps
12
- * the row with the higher `tokenEstimate` (more context preserved). Runs as a
13
- * single scan; idempotent (re-running skips already-removed rows).
14
- *
15
- * Returns the number of rows marked removed.
16
- */
17
- export function semDedupStore(ctx, sessionId, threshold) {
18
- const sid = normalizeSessionId(sessionId);
19
- const cps = listCheckpoints(sid, ctx.stateDir).filter((c) => c.dedupStatus !== "removed");
20
- let removed = 0;
21
- for (let i = 0; i < cps.length; i++) {
22
- for (let j = i + 1; j < cps.length; j++) {
23
- const a = cps[i];
24
- const b = cps[j];
25
- if (a.dedupStatus === "removed" || b.dedupStatus === "removed")
26
- continue;
27
- if (cosineSimilarity(a.embedding, b.embedding) > threshold) {
28
- // Keep the higher-tokenEstimate row; remove the other.
29
- const keep = a.tokenEstimate >= b.tokenEstimate ? a : b;
30
- const drop = keep === a ? b : a;
31
- setDedupStatus(drop.checkpointId, sid, "removed", ctx.stateDir);
32
- drop.dedupStatus = "removed";
33
- removed++;
34
- }
35
- }
36
- }
37
- return removed;
38
- }
39
- /**
40
- * Dedup sentinel check: has this region already been stored/represented?
41
- * Consulted by both the persist path and the recall/inline path.
42
- */
43
- export function dedupeCheck(ctx, sessionId, regionHashOrText, isText = false) {
44
- const sid = normalizeSessionId(sessionId);
45
- const hash = isText
46
- ? computeRegionHash(regionHashOrText)
47
- : regionHashOrText;
48
- const state = loadSessionState(sid, ctx.stateDir);
49
- if (state.storedRegionHashes.includes(hash))
50
- return true;
51
- return listCheckpoints(sid, ctx.stateDir).some((c) => c.regionHash === hash);
52
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * vectorStore/index.ts — Barrel re-export for the vector store sub-modules.
3
- *
4
- * All public symbols previously exported from the monolithic `vectorStore.ts`
5
- * are re-exported here so existing imports (`from "./vectorStore.js"`) continue
6
- * to work unchanged.
7
- */
8
- export { L2_ENABLED } from "./types.js";
9
- export { computeRegionHash } from "./utils.js";
10
- export { VectorStore } from "./store.js";
@@ -1,83 +0,0 @@
1
- /**
2
- * vectorStore/queries.ts — Read-only queries: list, topSimilar, stats, repoStats,
3
- * dataInvariant, similarity.
4
- */
5
- import { cosineSimilarity } from "../embedder.js";
6
- import { normalizeSessionId } from "../store.js";
7
- import { listCheckpoints, loadSessionState, getDedupStats, repoStats as repoStatsFromStore, dataInvariantStats, } from "../store/sqlite.js";
8
- /** All checkpoints for a session (sorted by checkpointId). */
9
- export function listSession(ctx, sessionId) {
10
- return listCheckpoints(normalizeSessionId(sessionId), ctx.stateDir);
11
- }
12
- /** Convenience for a raw vector cosine (exposed for tests). */
13
- export function similarityScore(a, b) {
14
- return cosineSimilarity(a, b);
15
- }
16
- /**
17
- * Return the n most similar checkpoints to the current (most recent) checkpoint
18
- * by cosine similarity. Returns fewer than n if the session has fewer checkpoints.
19
- * The current checkpoint itself is excluded from results.
20
- */
21
- export function topSimilarSession(ctx, sessionId, n) {
22
- const sid = normalizeSessionId(sessionId);
23
- const checkpoints = listCheckpoints(sid, ctx.stateDir);
24
- if (checkpoints.length <= 1)
25
- return [];
26
- // Find the most recent checkpoint (by checkpointId, which is sequential)
27
- const ordered = [...checkpoints].sort((a, b) => a.checkpointId.localeCompare(b.checkpointId));
28
- const current = ordered[ordered.length - 1];
29
- // Score all other checkpoints by similarity to current
30
- const scored = ordered
31
- .filter((cp) => cp.checkpointId !== current.checkpointId)
32
- .map((cp) => ({
33
- checkpoint: cp,
34
- score: cosineSimilarity(current.embedding, cp.embedding),
35
- }))
36
- .sort((a, b) => b.score - a.score);
37
- return scored.slice(0, n);
38
- }
39
- /**
40
- * Store statistics for status reporting / logging. Returns counts + the last
41
- * (highest-numbered) checkpoint, or nulls when the session is empty.
42
- */
43
- export function statsSession(ctx, sessionId) {
44
- const sid = normalizeSessionId(sessionId);
45
- const cps = listCheckpoints(sid, ctx.stateDir);
46
- const state = loadSessionState(sid, ctx.stateDir);
47
- const ordered = [...cps].sort((a, b) => a.checkpointId.localeCompare(b.checkpointId));
48
- const last = ordered[ordered.length - 1];
49
- const injected = state.injectedCheckpointIds.length;
50
- const ds = getDedupStats(ctx.stateDir);
51
- const sessionTok = cps.reduce((s, c) => s + (c.tokenEstimate ?? 0), 0);
52
- const sessionOrig = cps.reduce((s, c) => s + (c.originalTokenEstimate ?? 0), 0);
53
- // Per-session "tokens saved" = Σ(original − stored) over this session's
54
- // stored checkpoints. Deduped adds (whole region discarded, nothing stored)
55
- // are counted in the repo-wide meta counter via repoStats(); the per-session
56
- // DB sum here covers the rows that exist.
57
- const sessionSaved = cps.reduce((s, c) => s + Math.max(0, (c.originalTokenEstimate ?? 0) - (c.tokenEstimate ?? 0)), 0);
58
- return {
59
- checkpointCount: cps.length,
60
- totalTokenEstimate: sessionTok,
61
- lastCheckpointId: last?.checkpointId,
62
- lastSummary: last?.summary,
63
- injectedCount: injected,
64
- dedupHitRate: cps.length === 0 ? 0 : injected / cps.length,
65
- storageDedupRate: ds.attempts === 0 ? 0 : ds.deduped / ds.attempts,
66
- tokensSaved: sessionSaved,
67
- originalTokens: sessionOrig,
68
- dedupAttempts: ds.attempts,
69
- dedupCollapsed: ds.deduped,
70
- };
71
- }
72
- /**
73
- * Repo-wide stats — aggregates every session in this store (one per repo).
74
- * Cumulative, resumable, cross-device. Surfaces the dashboard's "Repo …"
75
- * figures; distinct from {@link statsSession} (per-session).
76
- */
77
- export function repoStatsStore(ctx) {
78
- return repoStatsFromStore(ctx.stateDir);
79
- }
80
- /** Data-safety invariant (Phase 0): regions retained vs bytes permanently deleted. */
81
- export function dataInvariantStore(ctx) {
82
- return dataInvariantStats(ctx.stateDir);
83
- }
@@ -1,95 +0,0 @@
1
- /**
2
- * vectorStore/search.ts — Semantic search and RAPTOR integration.
3
- */
4
- import { cosineSimilarity } from "../embedder.js";
5
- import { normalizeSessionId } from "../store.js";
6
- import { mmrRerank } from "../dedup/mmr.js";
7
- import { topK } from "../dedup/topk.js";
8
- import { listCheckpoints } from "../store/sqlite.js";
9
- import { rehydrateRaptorTree } from "../dedup/raptor/index.js";
10
- import { stagedExpansion } from "../dedup/raptor/retrieval.js";
11
- /**
12
- * Serve the RAPTOR tree for a query (Fix D): rehydrate the persisted tree and
13
- * return its staged-expansion leaf hits as SearchHits. Returns [] when no tree
14
- * exists (small sessions — flat search remains the path). Best-effort/non-fatal.
15
- */
16
- export function raptorSearchHits(ctx, sid, query, k) {
17
- try {
18
- const tree = rehydrateRaptorTree(sid, ctx.stateDir);
19
- if (!tree || !tree.rootId)
20
- return [];
21
- const leafIds = stagedExpansion(query, tree, {
22
- embedder: ctx.embedder,
23
- k,
24
- topM: ctx.cfg.RAPTOR_CLUSTERS_PER_LEVEL,
25
- mmrLambda: ctx.cfg.MMR_LAMBDA,
26
- });
27
- if (leafIds.length === 0)
28
- return [];
29
- const all = listCheckpoints(sid, ctx.stateDir).filter((cp) => cp.dedupStatus !== "removed");
30
- const qv = ctx.embedder.embed(query);
31
- const hits = [];
32
- for (const id of leafIds) {
33
- const cp = all.find((c) => c.checkpointId === id);
34
- if (cp)
35
- hits.push({ checkpoint: cp, score: cosineSimilarity(qv, cp.embedding) });
36
- }
37
- return hits;
38
- }
39
- catch {
40
- return [];
41
- }
42
- }
43
- /**
44
- * Semantic search within a session's checkpoints. Returns top-K by cosine
45
- * similarity, diversified via MMR (QA #10) so a cluster of near-identical
46
- * hits yields at most a few distinct-relevance results.
47
- *
48
- * Heap-based top-K (QA #4, O(N log k)) replaces the old full sort; MMR then
49
- * reranks the candidate window for diversity.
50
- */
51
- export function searchStore(ctx, sessionId, query, k = 3) {
52
- const sid = normalizeSessionId(sessionId);
53
- const checkpoints = listCheckpoints(sid, ctx.stateDir).filter((cp) => cp.dedupStatus !== "removed");
54
- if (checkpoints.length === 0)
55
- return [];
56
- const qv = ctx.embedder.embed(query);
57
- const scored = checkpoints.map((cp) => ({
58
- checkpoint: cp,
59
- score: cosineSimilarity(qv, cp.embedding),
60
- }));
61
- // Heap top-K over a widened window (2k) so MMR has diverse candidates.
62
- const window = topK(scored.map((h) => ({ item: h, score: h.score })), Math.max(k * 2, k)).map((s) => s.item);
63
- // MMR (QA #10) is part of the L2 semantic tier: skip it when L2 is disabled
64
- // (Sprint 14 flag), returning the plain relevance-ranked window instead.
65
- if (!ctx.cfg.L2_ENABLED)
66
- return window.slice(0, k);
67
- // Fix D: when RAPTOR is promoted, ALSO recall high-level tree summaries and
68
- // merge them with the flat hits via MMR so RAPTOR + flat don't double-cover.
69
- // RAPTOR returns fewer, broader hits (O(log n) high-level nodes) than the
70
- // O(n) flat leaves, tightening the block at read time.
71
- if (ctx.cfg.RAPTOR_ENABLED) {
72
- const raptorHits = raptorSearchHits(ctx, sid, query, k);
73
- if (raptorHits.length > 0) {
74
- const merged = [...window];
75
- for (const rh of raptorHits) {
76
- if (!merged.some((m) => m.checkpoint.checkpointId === rh.checkpoint.checkpointId)) {
77
- merged.push(rh);
78
- }
79
- }
80
- const mmrItems = merged.map((h) => ({
81
- item: h,
82
- vector: h.checkpoint.embedding,
83
- relevance: h.score,
84
- }));
85
- return mmrRerank(mmrItems, k, ctx.cfg.MMR_LAMBDA);
86
- }
87
- }
88
- const mmrItems = window.map((h) => ({
89
- item: h,
90
- vector: h.checkpoint.embedding,
91
- relevance: h.score,
92
- }));
93
- const ranked = mmrRerank(mmrItems, k, ctx.cfg.MMR_LAMBDA);
94
- return ranked;
95
- }
@@ -1,19 +0,0 @@
1
- /**
2
- * vectorStore/session.ts — Session-state mutations (injection tracking).
3
- */
4
- import { normalizeSessionId } from "../store.js";
5
- import { loadSessionState, saveSessionState } from "../store/sqlite.js";
6
- /** Mark a checkpoint as injected into the window (recall dedup). */
7
- export function markInjectedStore(ctx, sessionId, checkpointId) {
8
- const sid = normalizeSessionId(sessionId);
9
- const state = loadSessionState(sid, ctx.stateDir);
10
- if (!state.injectedCheckpointIds.includes(checkpointId)) {
11
- state.injectedCheckpointIds.push(checkpointId);
12
- saveSessionState(sid, state, ctx.stateDir);
13
- }
14
- }
15
- /** True if this checkpoint was already injected this session. */
16
- export function wasInjectedStore(ctx, sessionId, checkpointId) {
17
- const state = loadSessionState(normalizeSessionId(sessionId), ctx.stateDir);
18
- return state.injectedCheckpointIds.includes(checkpointId);
19
- }
@@ -1,105 +0,0 @@
1
- /**
2
- * vectorStore/store.ts — VectorStore class (thin wrapper delegating to
3
- * sub-modules). Public API is identical to the pre-split monolith.
4
- */
5
- import { defaultEmbedder } from "../embedder.js";
6
- import { loadDedupConfig } from "../config/dedup.js";
7
- import { getStateDir } from "../store.js";
8
- import { openBloom } from "../store/bloom.js";
9
- import { migrateJsonToSqlite } from "../store/migrate.js";
10
- import { addToStore } from "./add.js";
11
- import { searchStore } from "./search.js";
12
- import { semDedupStore, dedupeCheck } from "./dedup.js";
13
- import { markInjectedStore, wasInjectedStore } from "./session.js";
14
- import { listSession, topSimilarSession, statsSession, repoStatsStore, dataInvariantStore, similarityScore, } from "./queries.js";
15
- export class VectorStore {
16
- ctx;
17
- constructor(opts = {}) {
18
- const embedder = opts.embedder ?? defaultEmbedder();
19
- const stateDir = opts.stateDir ?? getStateDir();
20
- // Sprint 14: all tier flags/thresholds flow from the single config source
21
- // (DedupConfig). The legacy opts.dedupSim / opts.l2Enabled remain accepted
22
- // for backward-compat callers but flags are authoritative via `cfg`.
23
- void opts.dedupSim;
24
- void opts.l2Enabled;
25
- // Sprint 12 L2 semantic tier. Threshold 0.85 is the default trigram
26
- // embedder's honest firing point; a direct override is allowed for tests.
27
- const cfg = opts.config ?? loadDedupConfig();
28
- const l2Threshold = opts.l2Threshold ?? cfg.L2_COSINE;
29
- this.ctx = {
30
- embedder,
31
- stateDir,
32
- l2Threshold,
33
- cfg,
34
- eventsPath: opts.eventsPath,
35
- };
36
- // Sprint 8: bring any v0.1.0 JSON checkpoint files into SQLite (idempotent).
37
- migrateJsonToSqlite(stateDir);
38
- // Sprint 10: warm the bloom accelerator (accelerator only — SQLite stays
39
- // source of truth; a bloom hit is always confirmed by a query below).
40
- openBloom(stateDir);
41
- }
42
- /** @internal Shared context for sub-module operations. */
43
- get context() {
44
- return this.ctx;
45
- }
46
- /** @internal Kept for test compatibility — delegates to ctx.stateDir. */
47
- get stateDir() {
48
- return this.ctx.stateDir;
49
- }
50
- add(input) {
51
- return addToStore(this.ctx, input);
52
- }
53
- search(sessionId, query, k = 3) {
54
- return searchStore(this.ctx, sessionId, query, k);
55
- }
56
- semDedup(sessionId, threshold = this.ctx.cfg.SEMDEDUP_COSINE) {
57
- return semDedupStore(this.ctx, sessionId, threshold);
58
- }
59
- dedupe(sessionId, regionHashOrText, isText = false) {
60
- return dedupeCheck(this.ctx, sessionId, regionHashOrText, isText);
61
- }
62
- /** Mark a checkpoint as injected into the window (recall dedup). */
63
- markInjected(sessionId, checkpointId) {
64
- markInjectedStore(this.ctx, sessionId, checkpointId);
65
- }
66
- /** True if this checkpoint was already injected this session. */
67
- wasInjected(sessionId, checkpointId) {
68
- return wasInjectedStore(this.ctx, sessionId, checkpointId);
69
- }
70
- /** Convenience for a raw vector cosine (exposed for tests). */
71
- similarity(a, b) {
72
- return similarityScore(a, b);
73
- }
74
- /** All checkpoints for a session (sorted by checkpointId). */
75
- list(sessionId) {
76
- return listSession(this.ctx, sessionId);
77
- }
78
- /**
79
- * Return the n most similar checkpoints to the current (most recent) checkpoint
80
- * by cosine similarity. Returns fewer than n if the session has fewer checkpoints.
81
- * The current checkpoint itself is excluded from results.
82
- */
83
- topSimilar(sessionId, n) {
84
- return topSimilarSession(this.ctx, sessionId, n);
85
- }
86
- /**
87
- * Store statistics for status reporting / logging. Returns counts + the last
88
- * (highest-numbered) checkpoint, or nulls when the session is empty.
89
- */
90
- stats(sessionId) {
91
- return statsSession(this.ctx, sessionId);
92
- }
93
- /**
94
- * Repo-wide stats — aggregates every session in this store (one per repo).
95
- * Cumulative, resumable, cross-device. Surfaces the dashboard's "Repo …"
96
- * figures; distinct from {@link stats} (per-session).
97
- */
98
- repoStats() {
99
- return repoStatsStore(this.ctx);
100
- }
101
- /** Data-safety invariant (Phase 0): regions retained vs bytes permanently deleted. */
102
- dataInvariant() {
103
- return dataInvariantStore(this.ctx);
104
- }
105
- }
@@ -1,6 +0,0 @@
1
- /**
2
- * vectorStore/types.ts — Shared types, constants, and context interface for the
3
- * vector store sub-modules.
4
- */
5
- /** Default L2 semantic-dedup enable flag (trigram embedder is local, zero-network). */
6
- export const L2_ENABLED = true;
@@ -1,23 +0,0 @@
1
- /**
2
- * vectorStore/utils.ts — Hashing and monitoring utilities.
3
- */
4
- import { createHash } from "node:crypto";
5
- import { logDecision } from "../monitoring.js";
6
- /** Stable hash of a compacted region, the dedup sentinel key. */
7
- export function computeRegionHash(regionText) {
8
- // Normalize whitespace before hashing so "foo bar" and "foo bar" dedup.
9
- const normalized = regionText.replace(/\s+/g, " ").trim();
10
- return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
11
- }
12
- /** Emit a structured dedup-decision event (best-effort, never throws). */
13
- export function recordDecision(ctx, tier, result, reason, latencyMs) {
14
- if (!ctx.eventsPath)
15
- return;
16
- logDecision(ctx.eventsPath, {
17
- ts: Date.now(),
18
- tier,
19
- result,
20
- reason,
21
- latencyMs: Math.round(latencyMs * 100) / 100,
22
- });
23
- }
@@ -1,2 +0,0 @@
1
- import{j as s}from"./index-XAIN8km6.js";function n(){return s.jsxs("div",{className:"tab-stub",children:[s.jsx("strong",{children:"Config"})," (B1 stub)"]})}export{n as default};
2
- //# sourceMappingURL=ConfigTab-RrjXWfvr.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ConfigTab-RrjXWfvr.js","sources":["../../src/tabs/ConfigTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/ConfigTab.tsx — Config tab (B1 stub).\n *\n * SPRINT-C3: config form, game-mode settings, theme picker.\n */\n\nimport type React from \"react\";\n\nexport default function ConfigTab(): React.ReactElement {\n\treturn (\n\t\t<div className=\"tab-stub\">\n\t\t\t<strong>Config</strong> (B1 stub)\n\t\t\t{/* SPRINT-C3-REMAINING: /mega-compact-settings form, theme picker */}\n\t\t</div>\n\t);\n}\n"],"names":["ConfigTab","jsxs","jsx"],"mappings":"wCAQA,SAAwBA,GAAgC,CACvD,OACCC,EAAAA,KAAC,MAAA,CAAI,UAAU,WACd,SAAA,CAAAC,EAAAA,IAAC,UAAO,SAAA,QAAA,CAAM,EAAS,YAAA,EAExB,CAEF"}
@@ -1,2 +0,0 @@
1
- import{r as c,j as s}from"./index-XAIN8km6.js";function k(e={}){const{maxEvents:n=500,maxReconnects:a=5}=e,[o,i]=c.useState([]),[u,t]=c.useState("connecting"),[r,m]=c.useState(0),[$,f]=c.useState(null),p=c.useRef(0),d=c.useRef(null),g=c.useCallback(()=>{d.current&&d.current.close(),t("connecting");const l=new EventSource("/api/events");d.current=l,l.onopen=()=>{t("connected"),p.current=0},l.onmessage=x=>{try{const j=JSON.parse(x.data);i(_=>{const h=[..._,j];return h.length>n?h.slice(-n):h}),m(_=>_+1),f(Date.now())}catch{}},l.onerror=()=>{l.close(),p.current<a?(p.current+=1,t("connecting"),setTimeout(g,1e3)):t("disconnected")}},[n,a]);return c.useEffect(()=>(g(),()=>{d.current?.close()}),[g]),{events:o,status:u,eventCount:r,lastEventAt:$}}const N={compact_start:"ev-compact",compact_end:"ev-compact",compact_trigger:"ev-trigger",compact_skip:"ev-trigger",tier_changed:"ev-tier",model_changed:"ev-tier",pressure_lifted:"ev-tier",checkpoint_persisted:"ev-checkpoint",recall_inject:"ev-recall",anchors_updated:"ev-recall",config_updated:"ev-config",config_preset:"ev-config",crew_presence_changed:"ev-crew",crew_turn_changed:"ev-crew",crew_bandit_chosen:"ev-crew",game_ritual_start:"ev-game",game_ritual_stage:"ev-game",game_ritual_end:"ev-game",game_mode_changed:"ev-game",game_render:"ev-game"},v=["all","compact_start","compact_end","recall_inject","checkpoint_persisted"];function y(e){switch(e.type){case"compact_start":return`trigger=${e.trigger} session=${e.sessionId.slice(0,8)}`;case"compact_end":return`freed=${e.tokensFreed} ok=${e.success} cp=${e.checkpointId.slice(0,8)}`;case"compact_trigger":return`pressure=${e.pressure}% threshold=${e.threshold}% armed=${e.armed}`;case"compact_skip":return`reason=${e.reason}`;case"tier_changed":return`${e.from} → ${e.to} ctx=${e.contextPct}%`;case"model_changed":return`${e.providerName}/${e.model}`;case"pressure_lifted":return`${e.beforePct}% → ${e.afterPct}%`;case"checkpoint_persisted":return`cp=${e.checkpointId.slice(0,8)} sessionTok=${e.sessionTokens}`;case"recall_inject":return`q="${e.query.slice(0,40)}" chunks=${e.chunks} tok=${e.tokens}`;case"anchors_updated":return`count=${e.count} pinned=${e.pinned}`;case"config_updated":return`key=${e.key}`;case"config_preset":return`preset=${e.preset}`;case"crew_presence_changed":return`agents=${e.activeAgents} turn=${e.currentTurn}`;case"crew_turn_changed":return`turn=${e.turnIndex} agent=${e.agentName}`;case"crew_bandit_chosen":return`agent=${e.chosenAgent} score=${e.score} regret=${e.regret}`;case"game_ritual_start":case"game_ritual_stage":case"game_ritual_end":case"game_mode_changed":case"game_render":return`stage=${e.type}`}}function E(e){try{return new Date(e).toLocaleTimeString()}catch{return e}}function S({events:e}){const[n,a]=c.useState("all"),[o,i]=c.useState(null),u=c.useMemo(()=>(n==="all"?e:e.filter(r=>r.type===n)).slice(-200).reverse(),[e,n]);return s.jsxs("div",{className:"event-stream",children:[s.jsxs("div",{className:"event-filters",role:"toolbar",children:[v.map(t=>s.jsx("button",{type:"button",className:`filter-chip ${n===t?"active":""}`,onClick:()=>a(t),children:t==="all"?"all":t.replace(/_/g," ")},t)),s.jsxs("span",{className:"event-count",children:[e.length," buffered"]})]}),s.jsxs("ul",{className:"event-list",children:[u.length===0&&s.jsx("li",{className:"event-empty",children:"No events yet."}),u.map((t,r)=>{const m=N[t.type]??"ev-default";return s.jsxs("li",{className:`event-row ${m}`,onClick:()=>i(o===r?null:r),children:[s.jsx("span",{className:"ev-time",children:E(t.ts)}),s.jsx("span",{className:"ev-type",children:t.type}),s.jsx("span",{className:"ev-summary",children:y(t)}),o===r&&s.jsx("pre",{className:"ev-detail",children:JSON.stringify(t,null,2)})]},`${t.ts}-${r}`)})]})]})}function b(){const{events:e,status:n,eventCount:a}=k(),o=`sse-status sse-${n}`,i=n==="connected"?"connected":n==="connecting"?"connecting…":n==="error"?"error":"disconnected";return s.jsxs("div",{className:"events-tab",children:[s.jsxs("div",{className:"events-header",children:[s.jsxs("span",{className:o,role:"status",children:[s.jsx("span",{className:"sse-dot","aria-hidden":"true"}),i]}),s.jsxs("span",{className:"event-count",children:[a," events received"]})]}),s.jsx(S,{events:e})]})}export{b as default};
2
- //# sourceMappingURL=EventsTab-BH_FxJ08.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"EventsTab-BH_FxJ08.js","sources":["../../src/hooks/useSSE.ts","../../src/components/EventStream.tsx","../../src/tabs/EventsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/hooks/useSSE.ts — Server-Sent Events hook.\n *\n * Connects to /api/events, provides real-time event stream.\n * SPRINT-B1: basic connection. SPRINT-D1: reconnect with backoff.\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport type { SseEvent } from '../../../dashboard-server/api-contracts';\n\nexport type SseStatus = 'connecting' | 'connected' | 'disconnected' | 'error';\n\nexport interface UseSSEResult {\n events: SseEvent[];\n status: SseStatus;\n eventCount: number;\n lastEventAt: number | null;\n}\n\nexport interface UseSSEOptions {\n /** Max events to keep in memory (ring buffer). */\n maxEvents?: number;\n /** Max reconnect attempts before giving up. */\n maxReconnects?: number;\n}\n\n// SPRINT-D1-REMAINING: exponential backoff (1s → 2s → 4s → max 30s).\n// SPRINT-D1-REMAINING: after 5 failures, set status='disconnected'.\n\nexport function useSSE(options: UseSSEOptions = {}): UseSSEResult {\n const { maxEvents = 500, maxReconnects = 5 } = options;\n const [events, setEvents] = useState<SseEvent[]>([]);\n const [status, setStatus] = useState<SseStatus>('connecting');\n const [eventCount, setEventCount] = useState(0);\n const [lastEventAt, setLastEventAt] = useState<number | null>(null);\n const reconnectCount = useRef(0);\n const sourceRef = useRef<EventSource | null>(null);\n\n const connect = useCallback(() => {\n if (sourceRef.current) {\n sourceRef.current.close();\n }\n setStatus('connecting');\n const source = new EventSource('/api/events');\n sourceRef.current = source;\n\n source.onopen = () => {\n setStatus('connected');\n reconnectCount.current = 0;\n };\n\n source.onmessage = (event) => {\n try {\n const parsed = JSON.parse(event.data) as SseEvent;\n setEvents(prev => {\n const next = [...prev, parsed];\n return next.length > maxEvents ? next.slice(-maxEvents) : next;\n });\n setEventCount(c => c + 1);\n setLastEventAt(Date.now());\n } catch {\n // non-fatal: skip malformed events\n }\n };\n\n source.onerror = () => {\n source.close();\n if (reconnectCount.current < maxReconnects) {\n reconnectCount.current += 1;\n setStatus('connecting');\n // SPRINT-D1: use exponential backoff instead of fixed delay\n setTimeout(connect, 1000);\n } else {\n setStatus('disconnected');\n }\n };\n }, [maxEvents, maxReconnects]);\n\n useEffect(() => {\n connect();\n return () => {\n sourceRef.current?.close();\n };\n }, [connect]);\n\n return { events, status, eventCount, lastEventAt };\n}\n","/**\n * dashboard-client/src/components/EventStream.tsx — SSE event list with filters.\n *\n * Type badge colored by event type. Timestamp + expandable detail row.\n * Type filter chips (all + per-type). Virtualization: manual windowing for\n * 500+ events (render last N visible).\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { SseEvent } from \"@contracts\";\n\nexport interface EventStreamProps {\n\tevents: SseEvent[];\n}\n\nconst TYPE_COLORS: Record<string, string> = {\n\tcompact_start: \"ev-compact\",\n\tcompact_end: \"ev-compact\",\n\tcompact_trigger: \"ev-trigger\",\n\tcompact_skip: \"ev-trigger\",\n\ttier_changed: \"ev-tier\",\n\tmodel_changed: \"ev-tier\",\n\tpressure_lifted: \"ev-tier\",\n\tcheckpoint_persisted: \"ev-checkpoint\",\n\trecall_inject: \"ev-recall\",\n\tanchors_updated: \"ev-recall\",\n\tconfig_updated: \"ev-config\",\n\tconfig_preset: \"ev-config\",\n\tcrew_presence_changed: \"ev-crew\",\n\tcrew_turn_changed: \"ev-crew\",\n\tcrew_bandit_chosen: \"ev-crew\",\n\tgame_ritual_start: \"ev-game\",\n\tgame_ritual_stage: \"ev-game\",\n\tgame_ritual_end: \"ev-game\",\n\tgame_mode_changed: \"ev-game\",\n\tgame_render: \"ev-game\",\n};\n\n// Filterable event types (spec: compact_start/end, recall_inject, checkpoint_persisted + all).\nconst FILTER_TYPES = [\n\t\"all\",\n\t\"compact_start\",\n\t\"compact_end\",\n\t\"recall_inject\",\n\t\"checkpoint_persisted\",\n] as const;\ntype FilterType = (typeof FILTER_TYPES)[number];\n\nconst RENDER_WINDOW = 200; // render last 200 to keep DOM light under 500+ events\n\nfunction summarize(ev: SseEvent): string {\n\tswitch (ev.type) {\n\t\tcase \"compact_start\":\n\t\t\treturn `trigger=${ev.trigger} session=${ev.sessionId.slice(0, 8)}`;\n\t\tcase \"compact_end\":\n\t\t\treturn `freed=${ev.tokensFreed} ok=${ev.success} cp=${ev.checkpointId.slice(0, 8)}`;\n\t\tcase \"compact_trigger\":\n\t\t\treturn `pressure=${ev.pressure}% threshold=${ev.threshold}% armed=${ev.armed}`;\n\t\tcase \"compact_skip\":\n\t\t\treturn `reason=${ev.reason}`;\n\t\tcase \"tier_changed\":\n\t\t\treturn `${ev.from} → ${ev.to} ctx=${ev.contextPct}%`;\n\t\tcase \"model_changed\":\n\t\t\treturn `${ev.providerName}/${ev.model}`;\n\t\tcase \"pressure_lifted\":\n\t\t\treturn `${ev.beforePct}% → ${ev.afterPct}%`;\n\t\tcase \"checkpoint_persisted\":\n\t\t\treturn `cp=${ev.checkpointId.slice(0, 8)} sessionTok=${ev.sessionTokens}`;\n\t\tcase \"recall_inject\":\n\t\t\treturn `q=\"${ev.query.slice(0, 40)}\" chunks=${ev.chunks} tok=${ev.tokens}`;\n\t\tcase \"anchors_updated\":\n\t\t\treturn `count=${ev.count} pinned=${ev.pinned}`;\n\t\tcase \"config_updated\":\n\t\t\treturn `key=${ev.key}`;\n\t\tcase \"config_preset\":\n\t\t\treturn `preset=${ev.preset}`;\n\t\tcase \"crew_presence_changed\":\n\t\t\treturn `agents=${ev.activeAgents} turn=${ev.currentTurn}`;\n\t\tcase \"crew_turn_changed\":\n\t\t\treturn `turn=${ev.turnIndex} agent=${ev.agentName}`;\n\t\tcase \"crew_bandit_chosen\":\n\t\t\treturn `agent=${ev.chosenAgent} score=${ev.score} regret=${ev.regret}`;\n\t\tcase \"game_ritual_start\":\n\t\tcase \"game_ritual_stage\":\n\t\tcase \"game_ritual_end\":\n\t\tcase \"game_mode_changed\":\n\t\tcase \"game_render\":\n\t\t\treturn `stage=${ev.type}`;\n\t}\n}\n\nfunction formatTs(ts: string): string {\n\ttry {\n\t\tconst d = new Date(ts);\n\t\treturn d.toLocaleTimeString();\n\t} catch {\n\t\treturn ts;\n\t}\n}\n\nexport function EventStream({ events }: EventStreamProps): React.ReactElement {\n\tconst [filter, setFilter] = useState<FilterType>(\"all\");\n\tconst [expanded, setExpanded] = useState<number | null>(null);\n\n\tconst filtered = useMemo(() => {\n\t\tconst list =\n\t\t\tfilter === \"all\" ? events : events.filter((e) => e.type === filter);\n\t\t// Newest first; render only the last RENDER_WINDOW to keep DOM light.\n\t\treturn list.slice(-RENDER_WINDOW).reverse();\n\t}, [events, filter]);\n\n\treturn (\n\t\t<div className=\"event-stream\">\n\t\t\t<div className=\"event-filters\" role=\"toolbar\">\n\t\t\t\t{FILTER_TYPES.map((t) => (\n\t\t\t\t\t<button\n\t\t\t\t\t\tkey={t}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName={`filter-chip ${filter === t ? \"active\" : \"\"}`}\n\t\t\t\t\t\tonClick={() => setFilter(t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{t === \"all\" ? \"all\" : t.replace(/_/g, \" \")}\n\t\t\t\t\t</button>\n\t\t\t\t))}\n\t\t\t\t<span className=\"event-count\">{events.length} buffered</span>\n\t\t\t</div>\n\t\t\t<ul className=\"event-list\">\n\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t<li className=\"event-empty\">No events yet.</li>\n\t\t\t\t)}\n\t\t\t\t{filtered.map((ev, idx) => {\n\t\t\t\t\tconst cls = TYPE_COLORS[ev.type] ?? \"ev-default\";\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<li\n\t\t\t\t\t\t\tkey={`${ev.ts}-${idx}`}\n\t\t\t\t\t\t\tclassName={`event-row ${cls}`}\n\t\t\t\t\t\t\tonClick={() => setExpanded(expanded === idx ? null : idx)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span className=\"ev-time\">{formatTs(ev.ts)}</span>\n\t\t\t\t\t\t\t<span className=\"ev-type\">{ev.type}</span>\n\t\t\t\t\t\t\t<span className=\"ev-summary\">{summarize(ev)}</span>\n\t\t\t\t\t\t\t{expanded === idx && (\n\t\t\t\t\t\t\t\t<pre className=\"ev-detail\">{JSON.stringify(ev, null, 2)}</pre>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</li>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</ul>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/EventsTab.tsx — Events tab (C1).\n *\n * Live SSE event stream via useSSE + EventStream with type filters.\n * Shows connection status + event count.\n */\n\nimport type React from \"react\";\nimport { useSSE } from \"../hooks/useSSE\";\nimport { EventStream } from \"../components/EventStream\";\n\nexport default function EventsTab(): React.ReactElement {\n\tconst { events, status, eventCount } = useSSE();\n\n\tconst statusCls = `sse-status sse-${status}`;\n\tconst statusLabel =\n\t\tstatus === \"connected\"\n\t\t\t? \"connected\"\n\t\t\t: status === \"connecting\"\n\t\t\t\t? \"connecting…\"\n\t\t\t\t: status === \"error\"\n\t\t\t\t\t? \"error\"\n\t\t\t\t\t: \"disconnected\";\n\n\treturn (\n\t\t<div className=\"events-tab\">\n\t\t\t<div className=\"events-header\">\n\t\t\t\t<span className={statusCls} role=\"status\">\n\t\t\t\t\t<span className=\"sse-dot\" aria-hidden=\"true\" />\n\t\t\t\t\t{statusLabel}\n\t\t\t\t</span>\n\t\t\t\t<span className=\"event-count\">{eventCount} events received</span>\n\t\t\t</div>\n\t\t\t<EventStream events={events} />\n\t\t</div>\n\t);\n}\n"],"names":["useSSE","options","maxEvents","maxReconnects","events","setEvents","useState","status","setStatus","eventCount","setEventCount","lastEventAt","setLastEventAt","reconnectCount","useRef","sourceRef","connect","useCallback","source","event","parsed","prev","next","c","useEffect","TYPE_COLORS","FILTER_TYPES","summarize","ev","formatTs","ts","EventStream","filter","setFilter","expanded","setExpanded","filtered","useMemo","e","jsxs","jsx","idx","cls","EventsTab","statusCls","statusLabel"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{j as s,u as n,r as t,b as m,c as p}from"./index-XAIN8km6.js";function r(e){return e>=1e3?`${(e/1e3).toFixed(1)}s`:`${e.toFixed(0)}ms`}function c({value:e,max:a,colorClass:l}){const i=a>0?Math.max(0,Math.min(100,e/a*100)):0;return s.jsx("div",{className:"perf-bar-track",children:s.jsx("div",{className:`perf-bar-fill ${l}`,style:{width:`${i}%`}})})}function o({perf:e}){const a=Math.max(e.turn_latency_ms.p95,e.provider_latency_ms.p95,1),l=Math.max(e.tps.avg,1);return s.jsxs("div",{className:"perf-chart",children:[s.jsxs("div",{className:"perf-header",children:[s.jsx("h3",{children:"Performance"}),s.jsxs("span",{className:"perf-window",children:[e.windowMinutes,"min window · ",e.sampleCount," samples"]})]}),s.jsxs("div",{className:"perf-grid",children:[s.jsxs("div",{className:"perf-metric",children:[s.jsx("span",{className:"perf-label",children:"Turn latency p50 / p95"}),s.jsxs("span",{className:"perf-value",children:[r(e.turn_latency_ms.p50)," /"," ",r(e.turn_latency_ms.p95)]}),s.jsx(c,{value:e.turn_latency_ms.p95,max:a,colorClass:"perf-blue"})]}),s.jsxs("div",{className:"perf-metric",children:[s.jsx("span",{className:"perf-label",children:"Provider latency p50 / p95"}),s.jsxs("span",{className:"perf-value",children:[r(e.provider_latency_ms.p50)," /"," ",r(e.provider_latency_ms.p95)]}),s.jsx(c,{value:e.provider_latency_ms.p95,max:a,colorClass:"perf-purple"})]}),s.jsxs("div",{className:"perf-metric",children:[s.jsx("span",{className:"perf-label",children:"Tokens/sec (avg)"}),s.jsx("span",{className:"perf-value",children:e.tps.avg.toFixed(1)}),s.jsx(c,{value:e.tps.avg,max:l,colorClass:"perf-green"})]}),s.jsxs("div",{className:"perf-metric",children:[s.jsx("span",{className:"perf-label",children:"Cache hit (avg / latest)"}),s.jsxs("span",{className:"perf-value",children:[e.cache_hit_pct.avg.toFixed(1),"% /"," ",e.cache_hit_pct.latest.toFixed(1),"%"]}),s.jsx(c,{value:e.cache_hit_pct.avg,max:100,colorClass:"perf-green"})]}),s.jsxs("div",{className:"perf-metric",children:[s.jsx("span",{className:"perf-label",children:"DB recompute p95"}),s.jsx("span",{className:"perf-value",children:r(e.db_recompute_ms.p95)}),s.jsx(c,{value:e.db_recompute_ms.p95,max:a,colorClass:"perf-yellow"})]}),s.jsxs("div",{className:"perf-metric",children:[s.jsx("span",{className:"perf-label",children:"Disk write p95"}),s.jsx("span",{className:"perf-value",children:r(e.disk_write_ms.p95)}),s.jsx(c,{value:e.disk_write_ms.p95,max:a,colorClass:"perf-yellow"})]})]}),s.jsxs("div",{className:"perf-resources",children:[s.jsxs("div",{className:"perf-metric",children:[s.jsx("span",{className:"perf-label",children:"RSS"}),s.jsxs("span",{className:"perf-value",children:[e.rss_mb.latest.toFixed(0)," MB"]})]}),s.jsxs("div",{className:"perf-metric",children:[s.jsx("span",{className:"perf-label",children:"Heap"}),s.jsxs("span",{className:"perf-value",children:[e.heap_mb.latest.toFixed(0)," MB"]})]}),s.jsxs("div",{className:"perf-metric",children:[s.jsx("span",{className:"perf-label",children:"CPU user"}),s.jsxs("span",{className:"perf-value",children:[(e.cpu_user_ms.latest/1e3).toFixed(1),"s"]})]}),s.jsxs("div",{className:"perf-metric",children:[s.jsx("span",{className:"perf-label",children:"CPU sys"}),s.jsxs("span",{className:"perf-value",children:[(e.cpu_sys_ms.latest/1e3).toFixed(1),"s"]})]})]}),e.diag&&s.jsxs("div",{className:"perf-diag",children:[s.jsxs("span",{className:"perf-label",children:["Fast-gate fires: ",e.diag.ctxFastGate]}),s.jsxs("span",{className:"perf-label",children:["Live trim fires: ",e.diag.liveTrimFires]}),s.jsxs("span",{className:"perf-label",children:["Live trim replays: ",e.diag.liveTrimReplays]})]})]})}function x({name:e,providerName:a,provider:l,inputRate:i,outputRate:d}){return s.jsxs("div",{className:"model-badge",children:[s.jsx("span",{className:"model-name",children:e}),s.jsx("span",{className:"model-provider",children:a}),s.jsx("span",{className:"model-provider-id",children:l}),s.jsxs("div",{className:"model-rates",children:[s.jsxs("span",{className:"rate",children:["in ",i," tok/s"]}),s.jsxs("span",{className:"rate",children:["out ",d," tok/s"]})]})]})}function j(){const{data:e,error:a}=n(t.useCallback(()=>m({minutes:30}),[]),{pollInterval:1e4}),{data:l}=n(t.useCallback(()=>p(),[]),{pollInterval:1e4});return a&&!e?s.jsxs("div",{className:"tab-stub",children:["Error loading perf: ",a.message]}):e?s.jsxs("div",{className:"metrics-tab",children:[l?.model&&s.jsx(x,{name:l.model.name,providerName:l.model.providerName,provider:l.model.provider,inputRate:l.model.inputRate,outputRate:l.model.outputRate}),s.jsx(o,{perf:e})]}):s.jsx("div",{className:"tab-stub",children:"Loading perf…"})}export{j as default};
2
- //# sourceMappingURL=MetricsTab-C-PoFV4p.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MetricsTab-C-PoFV4p.js","sources":["../../src/components/PerfChart.tsx","../../src/components/ModelBadge.tsx","../../src/tabs/MetricsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/PerfChart.tsx — perf stat chart.\n *\n * The /api/perf endpoint returns rolling-window aggregates (not time-series),\n * so this renders a stat-card \"chart\": p50/p95 latency bars, TPS, cache hit %,\n * db recompute + disk write. Each metric shows a normalized bar + numeric.\n */\n\nimport type React from \"react\";\nimport type { PerfResponse } from \"@contracts\";\n\nexport interface PerfChartProps {\n\tperf: PerfResponse;\n}\n\nfunction fmtMs(n: number): string {\n\treturn n >= 1000 ? `${(n / 1000).toFixed(1)}s` : `${n.toFixed(0)}ms`;\n}\n\ninterface BarProps {\n\t/** Current value. */\n\tvalue: number;\n\t/** Value that represents 100% bar fill (the \"max\" reference). */\n\tmax: number;\n\t/** Color class. */\n\tcolorClass: string;\n}\n\nfunction Bar({ value, max, colorClass }: BarProps): React.ReactElement {\n\tconst pct = max > 0 ? Math.max(0, Math.min(100, (value / max) * 100)) : 0;\n\treturn (\n\t\t<div className=\"perf-bar-track\">\n\t\t\t<div\n\t\t\t\tclassName={`perf-bar-fill ${colorClass}`}\n\t\t\t\tstyle={{ width: `${pct}%` }}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\nexport function PerfChart({ perf }: PerfChartProps): React.ReactElement {\n\t// Reference maxes: p95 is the upper bound for latency bars; rates scale to a\n\t// reasonable reference (tok/s) so bars are visually meaningful even with\n\t// small samples. cache_hit_pct is already 0-100.\n\tconst latencyMax = Math.max(\n\t\tperf.turn_latency_ms.p95,\n\t\tperf.provider_latency_ms.p95,\n\t\t1,\n\t);\n\tconst tpsRef = Math.max(perf.tps.avg, 1);\n\n\treturn (\n\t\t<div className=\"perf-chart\">\n\t\t\t<div className=\"perf-header\">\n\t\t\t\t<h3>Performance</h3>\n\t\t\t\t<span className=\"perf-window\">\n\t\t\t\t\t{perf.windowMinutes}min window · {perf.sampleCount} samples\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t\t<div className=\"perf-grid\">\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Turn latency p50 / p95</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{fmtMs(perf.turn_latency_ms.p50)} /{\" \"}\n\t\t\t\t\t\t{fmtMs(perf.turn_latency_ms.p95)}\n\t\t\t\t\t</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.turn_latency_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-blue\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Provider latency p50 / p95</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{fmtMs(perf.provider_latency_ms.p50)} /{\" \"}\n\t\t\t\t\t\t{fmtMs(perf.provider_latency_ms.p95)}\n\t\t\t\t\t</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.provider_latency_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-purple\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Tokens/sec (avg)</span>\n\t\t\t\t\t<span className=\"perf-value\">{perf.tps.avg.toFixed(1)}</span>\n\t\t\t\t\t<Bar value={perf.tps.avg} max={tpsRef} colorClass=\"perf-green\" />\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Cache hit (avg / latest)</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{perf.cache_hit_pct.avg.toFixed(1)}% /{\" \"}\n\t\t\t\t\t\t{perf.cache_hit_pct.latest.toFixed(1)}%\n\t\t\t\t\t</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.cache_hit_pct.avg}\n\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\tcolorClass=\"perf-green\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">DB recompute p95</span>\n\t\t\t\t\t<span className=\"perf-value\">{fmtMs(perf.db_recompute_ms.p95)}</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.db_recompute_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-yellow\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Disk write p95</span>\n\t\t\t\t\t<span className=\"perf-value\">{fmtMs(perf.disk_write_ms.p95)}</span>\n\t\t\t\t\t<Bar\n\t\t\t\t\t\tvalue={perf.disk_write_ms.p95}\n\t\t\t\t\t\tmax={latencyMax}\n\t\t\t\t\t\tcolorClass=\"perf-yellow\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<div className=\"perf-resources\">\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">RSS</span>\n\t\t\t\t\t<span className=\"perf-value\">{perf.rss_mb.latest.toFixed(0)} MB</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">Heap</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{perf.heap_mb.latest.toFixed(0)} MB\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">CPU user</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{(perf.cpu_user_ms.latest / 1000).toFixed(1)}s\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"perf-metric\">\n\t\t\t\t\t<span className=\"perf-label\">CPU sys</span>\n\t\t\t\t\t<span className=\"perf-value\">\n\t\t\t\t\t\t{(perf.cpu_sys_ms.latest / 1000).toFixed(1)}s\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{perf.diag && (\n\t\t\t\t<div className=\"perf-diag\">\n\t\t\t\t\t<span className=\"perf-label\">\n\t\t\t\t\t\tFast-gate fires: {perf.diag.ctxFastGate}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className=\"perf-label\">\n\t\t\t\t\t\tLive trim fires: {perf.diag.liveTrimFires}\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className=\"perf-label\">\n\t\t\t\t\t\tLive trim replays: {perf.diag.liveTrimReplays}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/ModelBadge.tsx — model + provider + rates.\n *\n * Shows the active model name, human-readable provider, and input/output\n * token rates. Used in the Metrics tab header.\n */\n\nimport type React from \"react\";\n\nexport interface ModelBadgeProps {\n\t/** Model name/identifier. */\n\tname: string;\n\t/** Human-readable provider name. */\n\tproviderName: string;\n\t/** Machine-readable provider identifier. */\n\tprovider: string;\n\t/** Model input processing rate (tokens per second). */\n\tinputRate: number;\n\t/** Model output processing rate (tokens per second). */\n\toutputRate: number;\n}\n\nexport function ModelBadge({\n\tname,\n\tproviderName,\n\tprovider,\n\tinputRate,\n\toutputRate,\n}: ModelBadgeProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"model-badge\">\n\t\t\t<span className=\"model-name\">{name}</span>\n\t\t\t<span className=\"model-provider\">{providerName}</span>\n\t\t\t<span className=\"model-provider-id\">{provider}</span>\n\t\t\t<div className=\"model-rates\">\n\t\t\t\t<span className=\"rate\">in {inputRate} tok/s</span>\n\t\t\t\t<span className=\"rate\">out {outputRate} tok/s</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/MetricsTab.tsx — Metrics tab (C2).\n *\n * Fetches /api/perf (rolling-window aggregates) + /api/snapshot (for model).\n * Renders PerfChart + ModelBadge + resource gauges. Polls every 10s.\n */\n\nimport type React from \"react\";\nimport { useCallback } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchPerf, fetchSnapshot } from \"../api/client\";\nimport type { PerfResponse, SnapshotResponse } from \"@contracts\";\nimport { PerfChart } from \"../components/PerfChart\";\nimport { ModelBadge } from \"../components/ModelBadge\";\n\nexport default function MetricsTab(): React.ReactElement {\n\tconst { data: perf, error: perfErr } = useApi<PerfResponse>(\n\t\tuseCallback(() => fetchPerf({ minutes: 30 }), []),\n\t\t{ pollInterval: 10_000 },\n\t);\n\tconst { data: snapshot } = useApi<SnapshotResponse>(\n\t\tuseCallback(() => fetchSnapshot(), []),\n\t\t{ pollInterval: 10_000 },\n\t);\n\n\tif (perfErr && !perf) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">Error loading perf: {perfErr.message}</div>\n\t\t);\n\t}\n\tif (!perf) {\n\t\treturn <div className=\"tab-stub\">Loading perf…</div>;\n\t}\n\n\treturn (\n\t\t<div className=\"metrics-tab\">\n\t\t\t{snapshot?.model && (\n\t\t\t\t<ModelBadge\n\t\t\t\t\tname={snapshot.model.name}\n\t\t\t\t\tproviderName={snapshot.model.providerName}\n\t\t\t\t\tprovider={snapshot.model.provider}\n\t\t\t\t\tinputRate={snapshot.model.inputRate}\n\t\t\t\t\toutputRate={snapshot.model.outputRate}\n\t\t\t\t/>\n\t\t\t)}\n\t\t\t<PerfChart perf={perf} />\n\t\t</div>\n\t);\n}\n"],"names":["fmtMs","n","Bar","value","max","colorClass","pct","jsx","PerfChart","perf","latencyMax","tpsRef","jsxs","ModelBadge","name","providerName","provider","inputRate","outputRate","MetricsTab","perfErr","useApi","useCallback","fetchPerf","snapshot","fetchSnapshot"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{j as e}from"./index-XAIN8km6.js";function m(s){return s>=80?"gauge-red":s>=60?"gauge-yellow":"gauge-green"}function d(s){return s>=1e6?`${(s/1e6).toFixed(1)}M`:s>=1e3?`${(s/1e3).toFixed(1)}K`:String(s)}function j({tokens:s,percent:r,contextWindow:t}){const a=r??0,n=Math.max(0,Math.min(100,a)),i=m(a),l=s!=null?`${d(s)} / ${d(t)} tokens (${a.toFixed(1)}%)`:`${d(t)} window (usage unknown)`;return e.jsxs("div",{className:"card context-gauge",children:[e.jsx("h3",{children:"Context"}),e.jsx("div",{className:`gauge-bar ${i}`,role:"meter","aria-valuenow":a,"aria-valuemin":0,"aria-valuemax":100,children:e.jsx("div",{className:"gauge-fill",style:{width:`${n}%`}})}),e.jsx("p",{className:"gauge-label",children:l})]})}function o(s){return s>=1e6?`${(s/1e6).toFixed(1)}M`:s>=1e3?`${(s/1e3).toFixed(1)}K`:String(s)}function p({tokensIn:s,tokensOut:r,tokensFreed:t,compressionPct:a,dedupPct:n}){return e.jsxs("div",{className:"card compression-card",children:[e.jsx("h3",{children:"Compression (repo)"}),e.jsxs("div",{className:"stat-grid",children:[e.jsxs("div",{className:"stat",children:[e.jsx("span",{className:"stat-label",children:"In"}),e.jsx("span",{className:"stat-value",children:o(s)})]}),e.jsxs("div",{className:"stat",children:[e.jsx("span",{className:"stat-label",children:"Out"}),e.jsx("span",{className:"stat-value",children:o(r)})]}),e.jsxs("div",{className:"stat stat-highlight",children:[e.jsx("span",{className:"stat-label",children:"Freed"}),e.jsx("span",{className:"stat-value",children:o(t)})]}),e.jsxs("div",{className:"stat",children:[e.jsx("span",{className:"stat-label",children:"Ratio"}),e.jsxs("span",{className:"stat-value",children:[a.toFixed(1),"%"]})]}),e.jsxs("div",{className:"stat",children:[e.jsx("span",{className:"stat-label",children:"Dedup"}),e.jsxs("span",{className:"stat-value",children:[n.toFixed(1),"%"]})]})]})]})}function u({on:s,label:r}){return e.jsxs("div",{className:`bullet ${s?"bullet-on":"bullet-off"}`,children:[e.jsx("span",{className:"bullet-dot","aria-hidden":"true"}),e.jsx("span",{className:"bullet-label",children:r})]})}function g({armed:s,ready:r,currentTokens:t,thresholdTokens:a,fastGatePct:n}){const i=t!=null?t.toLocaleString():"unknown";return e.jsxs("div",{className:"card trigger-status",children:[e.jsx("h3",{children:"Trigger"}),e.jsxs("div",{className:"bullet-row",children:[e.jsx(u,{on:s,label:"Armed"}),e.jsx(u,{on:r,label:"Ready"})]}),e.jsxs("p",{className:"trigger-detail",children:["Threshold: ",a.toLocaleString()," tokens"]}),e.jsxs("p",{className:"trigger-detail",children:["Current: ",i," tokens"]}),e.jsxs("p",{className:"trigger-detail",children:["Fast gate: ",n,"%"]})]})}function N(s){return s>=1e6?`${(s/1e6).toFixed(1)}M`:s>=1e3?`${(s/1e3).toFixed(1)}K`:String(s)}function v({state:s,persistedThisSession:r,checkpointCount:t,tokensSaved:a,dedupHitRate:n}){const i=s??"no session";return e.jsxs("div",{className:"card session-info",children:[e.jsx("h3",{children:"Session"}),e.jsxs("p",{children:[e.jsx("span",{className:`state-badge state-${(s??"none").toLowerCase()}`,children:i}),r&&e.jsx("span",{className:"persisted-tag",children:"persisted"})]}),e.jsxs("div",{className:"stat-grid",children:[e.jsxs("div",{className:"stat",children:[e.jsx("span",{className:"stat-label",children:"Checkpoints"}),e.jsx("span",{className:"stat-value",children:t.toLocaleString()})]}),e.jsxs("div",{className:"stat stat-highlight",children:[e.jsx("span",{className:"stat-label",children:"Saved"}),e.jsx("span",{className:"stat-value",children:N(a)})]}),e.jsxs("div",{className:"stat",children:[e.jsx("span",{className:"stat-label",children:"Dedup hit"}),e.jsxs("span",{className:"stat-value",children:[n.toFixed(1),"%"]})]})]})]})}function f(s){if(!s)return"never";try{return new Date(s).toLocaleString()}catch{return s}}function k({snapshot:s,loading:r,error:t}){if(r&&!s)return e.jsx("div",{className:"tab-stub",children:"Loading snapshot…"});if(t&&!s)return e.jsxs("div",{className:"tab-stub",children:["Error: ",t.message]});if(!s)return e.jsx("div",{className:"tab-stub",children:"No snapshot data."});const{context:a,compression:n,trigger:i,session:l,store:c,tier:h,updatedAt:x}=s;return e.jsxs("div",{className:"overview-tab",children:[e.jsxs("div",{className:"overview-header",children:[e.jsx("span",{className:"tier-pill",children:h}),e.jsxs("span",{className:"updated",children:["updated ",f(x)]})]}),e.jsxs("div",{className:"card-grid",children:[e.jsx(j,{tokens:a.tokens,percent:a.percent,contextWindow:a.contextWindow}),e.jsx(p,{tokensIn:n.repo.tokensIn,tokensOut:n.repo.tokensOut,tokensFreed:n.repo.tokensFreed,compressionPct:n.repo.compressionPct,dedupPct:n.repo.dedupPct}),e.jsx(g,{armed:i.armed,ready:i.ready,currentTokens:i.currentTokens,thresholdTokens:i.thresholdTokens,fastGatePct:i.fastGatePct}),e.jsx(v,{state:l.state,persistedThisSession:l.persistedThisSession,checkpointCount:c.checkpointCount,tokensSaved:c.tokensSaved,dedupHitRate:c.dedupHitRate})]})]})}export{k as default};
2
- //# sourceMappingURL=OverviewTab-Db-qBqFN.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"OverviewTab-Db-qBqFN.js","sources":["../../src/components/ContextGauge.tsx","../../src/components/CompressionCard.tsx","../../src/components/TriggerStatus.tsx","../../src/components/SessionInfo.tsx","../../src/tabs/OverviewTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/ContextGauge.tsx — token usage meter.\n *\n * Color-coded percent fill bar: green <60%, yellow 60–80%, red >80%.\n * Sublabel: \"{tokens} / {contextWindow} tokens ({percent}%)\".\n */\n\nimport type React from \"react\";\n\nexport interface ContextGaugeProps {\n\t/** Current token count in the context window, or null if unknown. */\n\ttokens: number | null;\n\t/** Context window usage percent (0–100), or null if unknown. */\n\tpercent: number | null;\n\t/** Maximum context window size for the active model. */\n\tcontextWindow: number;\n}\n\nfunction severityClass(percent: number): string {\n\tif (percent >= 80) return \"gauge-red\";\n\tif (percent >= 60) return \"gauge-yellow\";\n\treturn \"gauge-green\";\n}\n\nfunction formatTokens(n: number): string {\n\tif (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n\tif (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n\treturn String(n);\n}\n\nexport function ContextGauge({\n\ttokens,\n\tpercent,\n\tcontextWindow,\n}: ContextGaugeProps): React.ReactElement {\n\tconst pct = percent ?? 0;\n\tconst fillWidth = Math.max(0, Math.min(100, pct));\n\tconst cls = severityClass(pct);\n\tconst label =\n\t\ttokens !== null && tokens !== undefined\n\t\t\t? `${formatTokens(tokens)} / ${formatTokens(contextWindow)} tokens (${pct.toFixed(1)}%)`\n\t\t\t: `${formatTokens(contextWindow)} window (usage unknown)`;\n\n\treturn (\n\t\t<div className=\"card context-gauge\">\n\t\t\t<h3>Context</h3>\n\t\t\t<div\n\t\t\t\tclassName={`gauge-bar ${cls}`}\n\t\t\t\trole=\"meter\"\n\t\t\t\taria-valuenow={pct}\n\t\t\t\taria-valuemin={0}\n\t\t\t\taria-valuemax={100}\n\t\t\t>\n\t\t\t\t<div className=\"gauge-fill\" style={{ width: `${fillWidth}%` }} />\n\t\t\t</div>\n\t\t\t<p className=\"gauge-label\">{label}</p>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/CompressionCard.tsx — compression stats.\n *\n * Stat grid: tokens in → out, freed amount highlighted, compression %,\n * dedup contribution %. Consumes snapshot.compression.repo.\n */\n\nimport type React from \"react\";\n\nexport interface CompressionCardProps {\n\t/** Repo-level compression stats (tokens in/out/freed + percentages). */\n\ttokensIn: number;\n\ttokensOut: number;\n\ttokensFreed: number;\n\t/** Compression ratio (percent, 0–100). */\n\tcompressionPct: number;\n\t/** Dedup contribution (percent, 0–100). */\n\tdedupPct: number;\n}\n\nfunction fmt(n: number): string {\n\tif (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n\tif (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n\treturn String(n);\n}\n\nexport function CompressionCard({\n\ttokensIn,\n\ttokensOut,\n\ttokensFreed,\n\tcompressionPct,\n\tdedupPct,\n}: CompressionCardProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"card compression-card\">\n\t\t\t<h3>Compression (repo)</h3>\n\t\t\t<div className=\"stat-grid\">\n\t\t\t\t<div className=\"stat\">\n\t\t\t\t\t<span className=\"stat-label\">In</span>\n\t\t\t\t\t<span className=\"stat-value\">{fmt(tokensIn)}</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"stat\">\n\t\t\t\t\t<span className=\"stat-label\">Out</span>\n\t\t\t\t\t<span className=\"stat-value\">{fmt(tokensOut)}</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"stat stat-highlight\">\n\t\t\t\t\t<span className=\"stat-label\">Freed</span>\n\t\t\t\t\t<span className=\"stat-value\">{fmt(tokensFreed)}</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"stat\">\n\t\t\t\t\t<span className=\"stat-label\">Ratio</span>\n\t\t\t\t\t<span className=\"stat-value\">{compressionPct.toFixed(1)}%</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"stat\">\n\t\t\t\t\t<span className=\"stat-label\">Dedup</span>\n\t\t\t\t\t<span className=\"stat-value\">{dedupPct.toFixed(1)}%</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/TriggerStatus.tsx — trigger armed/ready status.\n *\n * Two bullets (armed, ready) + threshold display + fast-gate %.\n */\n\nimport type React from \"react\";\n\nexport interface TriggerStatusProps {\n\t/** Whether the trigger is armed (waiting for threshold). */\n\tarmed: boolean;\n\t/** Whether the trigger is ready to fire (pressure at/above threshold). */\n\tready: boolean;\n\t/** Current context token count, or null if unknown. */\n\tcurrentTokens: number | null;\n\t/** Token threshold for firing. */\n\tthresholdTokens: number;\n\t/** Fast-gate pressure threshold (percent, 0–100). */\n\tfastGatePct: number;\n}\n\nfunction Bullet({\n\ton,\n\tlabel,\n}: {\n\ton: boolean;\n\tlabel: string;\n}): React.ReactElement {\n\treturn (\n\t\t<div className={`bullet ${on ? \"bullet-on\" : \"bullet-off\"}`}>\n\t\t\t<span className=\"bullet-dot\" aria-hidden=\"true\" />\n\t\t\t<span className=\"bullet-label\">{label}</span>\n\t\t</div>\n\t);\n}\n\nexport function TriggerStatus({\n\tarmed,\n\tready,\n\tcurrentTokens,\n\tthresholdTokens,\n\tfastGatePct,\n}: TriggerStatusProps): React.ReactElement {\n\tconst cur =\n\t\tcurrentTokens !== null && currentTokens !== undefined\n\t\t\t? currentTokens.toLocaleString()\n\t\t\t: \"unknown\";\n\treturn (\n\t\t<div className=\"card trigger-status\">\n\t\t\t<h3>Trigger</h3>\n\t\t\t<div className=\"bullet-row\">\n\t\t\t\t<Bullet on={armed} label=\"Armed\" />\n\t\t\t\t<Bullet on={ready} label=\"Ready\" />\n\t\t\t</div>\n\t\t\t<p className=\"trigger-detail\">\n\t\t\t\tThreshold: {thresholdTokens.toLocaleString()} tokens\n\t\t\t</p>\n\t\t\t<p className=\"trigger-detail\">Current: {cur} tokens</p>\n\t\t\t<p className=\"trigger-detail\">Fast gate: {fastGatePct}%</p>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/SessionInfo.tsx — session state + store metrics.\n *\n * State badge, checkpoint count, tokens saved, dedup hit rate.\n */\n\nimport type React from \"react\";\n\nexport interface SessionInfoProps {\n\t/** Session lifecycle state, or null when no session is active. */\n\tstate: string | null;\n\t/** Whether a checkpoint has been persisted during this session. */\n\tpersistedThisSession: boolean;\n\t/** Total checkpoints across all sessions. */\n\tcheckpointCount: number;\n\t/** Total tokens saved through compaction. */\n\ttokensSaved: number;\n\t/** Dedup hit rate (percent, 0–100). */\n\tdedupHitRate: number;\n}\n\nfunction fmt(n: number): string {\n\tif (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;\n\tif (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;\n\treturn String(n);\n}\n\nexport function SessionInfo({\n\tstate,\n\tpersistedThisSession,\n\tcheckpointCount,\n\ttokensSaved,\n\tdedupHitRate,\n}: SessionInfoProps): React.ReactElement {\n\tconst badge = state ?? \"no session\";\n\treturn (\n\t\t<div className=\"card session-info\">\n\t\t\t<h3>Session</h3>\n\t\t\t<p>\n\t\t\t\t<span\n\t\t\t\t\tclassName={`state-badge state-${(state ?? \"none\").toLowerCase()}`}\n\t\t\t\t>\n\t\t\t\t\t{badge}\n\t\t\t\t</span>\n\t\t\t\t{persistedThisSession && (\n\t\t\t\t\t<span className=\"persisted-tag\">persisted</span>\n\t\t\t\t)}\n\t\t\t</p>\n\t\t\t<div className=\"stat-grid\">\n\t\t\t\t<div className=\"stat\">\n\t\t\t\t\t<span className=\"stat-label\">Checkpoints</span>\n\t\t\t\t\t<span className=\"stat-value\">{checkpointCount.toLocaleString()}</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"stat stat-highlight\">\n\t\t\t\t\t<span className=\"stat-label\">Saved</span>\n\t\t\t\t\t<span className=\"stat-value\">{fmt(tokensSaved)}</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"stat\">\n\t\t\t\t\t<span className=\"stat-label\">Dedup hit</span>\n\t\t\t\t\t<span className=\"stat-value\">{dedupHitRate.toFixed(1)}%</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/OverviewTab.tsx — Overview tab (C1).\n *\n * 4-card grid: ContextGauge, CompressionCard, TriggerStatus, SessionInfo.\n * Header: tier pill + last updated timestamp. Polls /api/snapshot.\n */\n\nimport type React from \"react\";\nimport type { SnapshotResponse } from \"@contracts\";\nimport { ContextGauge } from \"../components/ContextGauge\";\nimport { CompressionCard } from \"../components/CompressionCard\";\nimport { TriggerStatus } from \"../components/TriggerStatus\";\nimport { SessionInfo } from \"../components/SessionInfo\";\n\nexport interface OverviewTabProps {\n\tsnapshot: SnapshotResponse | null;\n\tloading: boolean;\n\terror: Error | null;\n}\n\nfunction formatUpdatedAt(ts: string | null): string {\n\tif (!ts) return \"never\";\n\ttry {\n\t\treturn new Date(ts).toLocaleString();\n\t} catch {\n\t\treturn ts;\n\t}\n}\n\nexport default function OverviewTab({\n\tsnapshot,\n\tloading,\n\terror,\n}: OverviewTabProps): React.ReactElement {\n\tif (loading && !snapshot)\n\t\treturn <div className=\"tab-stub\">Loading snapshot…</div>;\n\tif (error && !snapshot)\n\t\treturn <div className=\"tab-stub\">Error: {error.message}</div>;\n\tif (!snapshot) return <div className=\"tab-stub\">No snapshot data.</div>;\n\n\tconst { context, compression, trigger, session, store, tier, updatedAt } =\n\t\tsnapshot;\n\n\treturn (\n\t\t<div className=\"overview-tab\">\n\t\t\t<div className=\"overview-header\">\n\t\t\t\t<span className=\"tier-pill\">{tier}</span>\n\t\t\t\t<span className=\"updated\">updated {formatUpdatedAt(updatedAt)}</span>\n\t\t\t</div>\n\t\t\t<div className=\"card-grid\">\n\t\t\t\t<ContextGauge\n\t\t\t\t\ttokens={context.tokens}\n\t\t\t\t\tpercent={context.percent}\n\t\t\t\t\tcontextWindow={context.contextWindow}\n\t\t\t\t/>\n\t\t\t\t<CompressionCard\n\t\t\t\t\ttokensIn={compression.repo.tokensIn}\n\t\t\t\t\ttokensOut={compression.repo.tokensOut}\n\t\t\t\t\ttokensFreed={compression.repo.tokensFreed}\n\t\t\t\t\tcompressionPct={compression.repo.compressionPct}\n\t\t\t\t\tdedupPct={compression.repo.dedupPct}\n\t\t\t\t/>\n\t\t\t\t<TriggerStatus\n\t\t\t\t\tarmed={trigger.armed}\n\t\t\t\t\tready={trigger.ready}\n\t\t\t\t\tcurrentTokens={trigger.currentTokens}\n\t\t\t\t\tthresholdTokens={trigger.thresholdTokens}\n\t\t\t\t\tfastGatePct={trigger.fastGatePct}\n\t\t\t\t/>\n\t\t\t\t<SessionInfo\n\t\t\t\t\tstate={session.state}\n\t\t\t\t\tpersistedThisSession={session.persistedThisSession}\n\t\t\t\t\tcheckpointCount={store.checkpointCount}\n\t\t\t\t\ttokensSaved={store.tokensSaved}\n\t\t\t\t\tdedupHitRate={store.dedupHitRate}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"names":["severityClass","percent","formatTokens","n","ContextGauge","tokens","contextWindow","pct","fillWidth","cls","label","jsxs","jsx","fmt","CompressionCard","tokensIn","tokensOut","tokensFreed","compressionPct","dedupPct","Bullet","on","TriggerStatus","armed","ready","currentTokens","thresholdTokens","fastGatePct","cur","SessionInfo","state","persistedThisSession","checkpointCount","tokensSaved","dedupHitRate","badge","formatUpdatedAt","ts","OverviewTab","snapshot","loading","error","context","compression","trigger","session","store","tier","updatedAt"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- import{j as e,r as i,u as b,f as C,a as L}from"./index-XAIN8km6.js";function R(s){return s>=1e6?`${(s/1e6).toFixed(1)}M`:s>=1e3?`${(s/1e3).toFixed(1)}K`:String(s)}function w({totalRepos:s,activeRepos:t,totalCheckpoints:l,totalTokensSaved:c}){return e.jsxs("div",{className:"summary-tiles",children:[e.jsxs("div",{className:"tile",children:[e.jsx("span",{className:"tile-label",children:"Total repos"}),e.jsx("span",{className:"tile-value",children:s.toLocaleString()})]}),e.jsxs("div",{className:"tile",children:[e.jsx("span",{className:"tile-label",children:"Active (24h)"}),e.jsx("span",{className:"tile-value",children:t.toLocaleString()})]}),e.jsxs("div",{className:"tile",children:[e.jsx("span",{className:"tile-label",children:"Checkpoints"}),e.jsx("span",{className:"tile-value",children:l.toLocaleString()})]}),e.jsxs("div",{className:"tile tile-highlight",children:[e.jsx("span",{className:"tile-label",children:"Tokens saved"}),e.jsx("span",{className:"tile-value",children:R(c)})]})]})}const f=[{key:"displayName",label:"Name"},{key:"modelName",label:"Model"},{key:"checkpointCount",label:"Checkpoints"},{key:"tokensSaved",label:"Saved"},{key:"lastCompactedAt",label:"Last compacted"},{key:"sessions",label:"Sessions"}];function T(s){return s>=1e6?`${(s/1e6).toFixed(1)}M`:s>=1e3?`${(s/1e3).toFixed(1)}K`:String(s)}function $(s){if(s==null)return"never";try{return new Date(s).toLocaleDateString()}catch{return String(s)}}function M({repos:s,onSelect:t}){const[l,c]=i.useState(""),[n,u]=i.useState("lastCompactedAt"),[d,j]=i.useState("desc"),p=i.useMemo(()=>{const a=l.trim().toLowerCase(),h=a?s.filter(x=>x.displayName.toLowerCase().includes(a)):s.slice();return h.sort((x,g)=>{const o=x[n],m=g[n],N=o==null,v=m==null;if(N&&v)return 0;if(N)return d==="desc"?1:-1;if(v)return d==="desc"?-1:1;const k=typeof o=="number"&&typeof m=="number"?o-m:String(o).localeCompare(String(m));return d==="desc"?-k:k}),h},[s,l,n,d]),y=a=>{n===a?j(h=>h==="asc"?"desc":"asc"):(u(a),j(a==="lastCompactedAt"||a==="tokensSaved"?"desc":"asc"))};return e.jsxs("div",{className:"repo-table-wrap",children:[e.jsx("input",{type:"search",className:"repo-search",placeholder:"Search repos…",value:l,onChange:a=>c(a.target.value),"aria-label":"Search repos"}),e.jsxs("table",{className:"repo-table",children:[e.jsx("thead",{children:e.jsx("tr",{children:f.map(a=>e.jsx("th",{children:e.jsxs("button",{type:"button",className:`sort-header ${n===a.key?`active-${d}`:""}`,onClick:()=>y(a.key),children:[a.label,n===a.key&&e.jsx("span",{className:"sort-arrow",children:d==="asc"?" ▲":" ▼"})]})},a.key))})}),e.jsxs("tbody",{children:[p.length===0&&e.jsx("tr",{children:e.jsx("td",{colSpan:f.length,className:"repo-empty",children:s.length===0?"No repos yet.":"No repos match your search."})}),p.map(a=>e.jsxs("tr",{onClick:()=>t(a),children:[e.jsx("td",{className:"repo-name",children:a.displayName}),e.jsx("td",{className:"repo-model",children:a.modelName??"—"}),e.jsx("td",{children:a.checkpointCount.toLocaleString()}),e.jsx("td",{children:T(a.tokensSaved)}),e.jsx("td",{children:$(a.lastCompactedAt)}),e.jsx("td",{children:a.sessions.toLocaleString()})]},a.stateDir))]})]})]})}function r(s){return s>=1e6?`${(s/1e6).toFixed(1)}M`:s>=1e3?`${(s/1e3).toFixed(1)}K`:String(s)}function S(s){if(s==null)return"never";try{return new Date(s).toLocaleString()}catch{return String(s)}}function D({repo:s,onClose:t}){return i.useEffect(()=>{const l=c=>{c.key==="Escape"&&t()};return window.addEventListener("keydown",l),()=>window.removeEventListener("keydown",l)},[t]),e.jsx("div",{className:"modal-backdrop",onClick:t,role:"dialog","aria-modal":"true",children:e.jsxs("div",{className:"modal-card",onClick:l=>l.stopPropagation(),children:[e.jsxs("header",{className:"modal-header",children:[e.jsx("h2",{children:s.displayName}),e.jsx("button",{type:"button",className:"modal-close",onClick:t,"aria-label":"Close",children:"×"})]}),e.jsx("p",{className:"modal-path",children:s.repoRoot}),e.jsxs("div",{className:"modal-grid",children:[e.jsxs("div",{className:"modal-stat",children:[e.jsx("span",{className:"modal-label",children:"Checkpoints"}),e.jsx("span",{className:"modal-value",children:s.checkpointCount.toLocaleString()})]}),e.jsxs("div",{className:"modal-stat",children:[e.jsx("span",{className:"modal-label",children:"Sessions"}),e.jsx("span",{className:"modal-value",children:s.sessions.toLocaleString()})]}),e.jsxs("div",{className:"modal-stat modal-highlight",children:[e.jsx("span",{className:"modal-label",children:"Tokens saved"}),e.jsx("span",{className:"modal-value",children:r(s.tokensSaved)})]}),e.jsxs("div",{className:"modal-stat",children:[e.jsx("span",{className:"modal-label",children:"Tokens kept"}),e.jsx("span",{className:"modal-value",children:r(s.tokensKept)})]}),e.jsxs("div",{className:"modal-stat",children:[e.jsx("span",{className:"modal-label",children:"Tokens dropped"}),e.jsx("span",{className:"modal-value",children:r(s.tokensDropped)})]}),e.jsxs("div",{className:"modal-stat",children:[e.jsx("span",{className:"modal-label",children:"Dedup rate"}),e.jsx("span",{className:"modal-value",children:"—"})]})]}),e.jsx("h3",{children:"Model"}),e.jsxs("dl",{className:"modal-dl",children:[e.jsx("dt",{children:"Provider"}),e.jsx("dd",{children:s.providerName??"—"}),e.jsx("dt",{children:"Model"}),e.jsx("dd",{children:s.modelName??"—"}),e.jsx("dt",{children:"Input rate"}),e.jsx("dd",{children:s.inputRate!==null?`${s.inputRate} tok/s`:"—"}),e.jsx("dt",{children:"Output rate"}),e.jsx("dd",{children:s.outputRate!==null?`${s.outputRate} tok/s`:"—"}),e.jsx("dt",{children:"Context window"}),e.jsx("dd",{children:s.contextWindow!==null?r(s.contextWindow):"—"}),e.jsx("dt",{children:"Max tokens"}),e.jsx("dd",{children:s.maxTokens!==null?r(s.maxTokens):"—"}),e.jsx("dt",{children:"Reasoning"}),e.jsx("dd",{children:s.reasoning===null?"—":s.reasoning?"on":"off"})]}),e.jsx("h3",{children:"Activity"}),e.jsxs("dl",{className:"modal-dl",children:[e.jsx("dt",{children:"Last compacted"}),e.jsx("dd",{children:S(s.lastCompactedAt)}),e.jsx("dt",{children:"Last seen"}),e.jsx("dd",{children:S(s.lastSeen)}),e.jsx("dt",{children:"Compressed bytes"}),e.jsx("dd",{children:r(s.compressedOriginalBytes)})]})]})})}function E(){const[s,t]=i.useState(null),{data:l,error:c}=b(i.useCallback(()=>C(),[]),{pollInterval:1e4}),{data:n}=b(i.useCallback(()=>L(),[]),{pollInterval:1e4});if(c&&!l)return e.jsxs("div",{className:"tab-stub",children:["Error loading repos: ",c.message]});if(!l)return e.jsx("div",{className:"tab-stub",children:"Loading repos…"});const u=l.repos,d=n?.summary;return e.jsxs("div",{className:"repos-tab",children:[e.jsx(w,{totalRepos:n?.totalRepos??0,activeRepos:n?.activeRepos??0,totalCheckpoints:d?.totalCheckpoints??0,totalTokensSaved:d?.totalTokensSaved??0}),e.jsx(M,{repos:u,onSelect:t}),s&&e.jsx(D,{repo:s,onClose:()=>t(null)})]})}export{E as default};
2
- //# sourceMappingURL=ReposTab-DhCjJvuj.js.map