pi-mega-compact 0.8.16 → 0.8.18

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 (98) hide show
  1. package/README.md +1 -1
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints.js +12 -0
  3. package/dist/extensions/dashboard-server/api-contracts.test.js +19 -6
  4. package/dist/extensions/dashboard-server/helpers.js +37 -0
  5. package/dist/extensions/dashboard-server/html/all-repos-tab.js +26 -0
  6. package/dist/extensions/dashboard-server/html/body-open.js +23 -0
  7. package/dist/extensions/dashboard-server/html/current-repo-tab.js +130 -0
  8. package/dist/extensions/dashboard-server/html/head-open.js +16 -0
  9. package/dist/extensions/dashboard-server/html/high-score-tab.js +25 -0
  10. package/dist/extensions/dashboard-server/html/repo-detail-modal.js +26 -0
  11. package/dist/extensions/dashboard-server/html/script.js +259 -0
  12. package/dist/extensions/dashboard-server/html/styles.js +103 -0
  13. package/dist/extensions/dashboard-server/html/summary-tab.js +19 -0
  14. package/dist/extensions/dashboard-server/html-template.js +41 -0
  15. package/dist/extensions/dashboard-server/server.js +83 -0
  16. package/dist/extensions/mega-compact-s38.test.js +33 -0
  17. package/dist/extensions/mega-events/agent-handlers.js +51 -3
  18. package/dist/extensions/mega-events/compact-handlers.js +10 -4
  19. package/dist/extensions/mega-events/error-classifier.js +24 -1
  20. package/dist/extensions/mega-events/send-safe.js +12 -0
  21. package/dist/extensions/mega-events.js +1 -0
  22. package/dist/extensions/mega-runtime/state.js +17 -1
  23. package/dist/src/store/sqlite/connection.js +35 -0
  24. package/dist/src/store/sqlite/global-index.js +199 -1
  25. package/dist/src/store/sqlite/global-index.test.js +136 -0
  26. package/dist/src/store/sqlite/index-store.js +167 -0
  27. package/dist/src/store/sqlite/memory.js +54 -0
  28. package/dist/src/store/sqlite/minhash-lsh.js +47 -0
  29. package/dist/src/store/sqlite/sessions.js +39 -0
  30. package/dist/src/store/sqlite/transaction.js +19 -0
  31. package/dist/src/vectorStore/add.js +260 -0
  32. package/dist/src/vectorStore/dedup.js +52 -0
  33. package/dist/src/vectorStore/index.js +10 -0
  34. package/dist/src/vectorStore/queries.js +83 -0
  35. package/dist/src/vectorStore/search.js +95 -0
  36. package/dist/src/vectorStore/session.js +19 -0
  37. package/dist/src/vectorStore/store.js +105 -0
  38. package/dist/src/vectorStore/types.js +6 -0
  39. package/dist/src/vectorStore/utils.js +23 -0
  40. package/extensions/dashboard-client/dist/assets/{AchievementTiles-BozP_h3c.js → AchievementTiles-CNWZ5NjP.js} +2 -2
  41. package/extensions/dashboard-client/dist/assets/{AchievementTiles-BozP_h3c.js.map → AchievementTiles-CNWZ5NjP.js.map} +1 -1
  42. package/extensions/dashboard-client/dist/assets/{AchievementsTab-DKgqx1RQ.js → AchievementsTab-BKPifNKj.js} +2 -2
  43. package/extensions/dashboard-client/dist/assets/{AchievementsTab-DKgqx1RQ.js.map → AchievementsTab-BKPifNKj.js.map} +1 -1
  44. package/extensions/dashboard-client/dist/assets/{CacheTab-CPvso4-0.js → CacheTab--epzrQHD.js} +2 -2
  45. package/extensions/dashboard-client/dist/assets/{CacheTab-CPvso4-0.js.map → CacheTab--epzrQHD.js.map} +1 -1
  46. package/extensions/dashboard-client/dist/assets/{ConfigTab-DXD7zbqy.js → ConfigTab-D3ralwSj.js} +2 -2
  47. package/extensions/dashboard-client/dist/assets/ConfigTab-D3ralwSj.js.map +1 -0
  48. package/extensions/dashboard-client/dist/assets/EventsTab-BKkuVQsM.js +2 -0
  49. package/extensions/dashboard-client/dist/assets/EventsTab-BKkuVQsM.js.map +1 -0
  50. package/extensions/dashboard-client/dist/assets/{GameTab-Qj19W1Y-.js → GameTab-CgUvITYy.js} +2 -2
  51. package/extensions/dashboard-client/dist/assets/{GameTab-Qj19W1Y-.js.map → GameTab-CgUvITYy.js.map} +1 -1
  52. package/extensions/dashboard-client/dist/assets/{MetricsTab-DA27vA95.js → MetricsTab-DrlJqXGL.js} +2 -2
  53. package/extensions/dashboard-client/dist/assets/{MetricsTab-DA27vA95.js.map → MetricsTab-DrlJqXGL.js.map} +1 -1
  54. package/extensions/dashboard-client/dist/assets/{OverviewTab-lVGR1Jl_.js → OverviewTab-_gD756wJ.js} +2 -2
  55. package/extensions/dashboard-client/dist/assets/{OverviewTab-lVGR1Jl_.js.map → OverviewTab-_gD756wJ.js.map} +1 -1
  56. package/extensions/dashboard-client/dist/assets/{ReposTab-_HNR3qYj.js → ReposTab-DjEZmqUb.js} +2 -2
  57. package/extensions/dashboard-client/dist/assets/{ReposTab-_HNR3qYj.js.map → ReposTab-DjEZmqUb.js.map} +1 -1
  58. package/extensions/dashboard-client/dist/assets/SessionsTab-DsYMUzoh.js +70 -0
  59. package/extensions/dashboard-client/dist/assets/SessionsTab-DsYMUzoh.js.map +1 -0
  60. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-B7aPcBMs.js → TimeSavedCard-BbAXT1wO.js} +2 -2
  61. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-B7aPcBMs.js.map → TimeSavedCard-BbAXT1wO.js.map} +1 -1
  62. package/extensions/dashboard-client/dist/assets/{index-Cfxniu2R.css → index-BTft6DM_.css} +1 -1
  63. package/extensions/dashboard-client/dist/assets/{index-B2JJExE9.js → index-CnRzusfi.js} +10 -10
  64. package/extensions/dashboard-client/dist/assets/{index-B2JJExE9.js.map → index-CnRzusfi.js.map} +1 -1
  65. package/extensions/dashboard-client/dist/assets/useSSE-Bf-1rZ2N.js +2 -0
  66. package/extensions/dashboard-client/dist/assets/useSSE-Bf-1rZ2N.js.map +1 -0
  67. package/extensions/dashboard-client/dist/index.html +2 -2
  68. package/extensions/dashboard-client/package-lock.json +374 -2
  69. package/extensions/dashboard-client/package.json +2 -1
  70. package/extensions/dashboard-client/src/App.tsx +5 -1
  71. package/extensions/dashboard-client/src/api/client.ts +12 -0
  72. package/extensions/dashboard-client/src/components/ActiveSessionsTable.tsx +185 -0
  73. package/extensions/dashboard-client/src/components/EventStream.tsx +3 -0
  74. package/extensions/dashboard-client/src/components/SessionsMemoryChart.tsx +199 -0
  75. package/extensions/dashboard-client/src/main.tsx +1 -0
  76. package/extensions/dashboard-client/src/styles/sessions.css +193 -0
  77. package/extensions/dashboard-client/src/tabs/ConfigTab.tsx +6 -3
  78. package/extensions/dashboard-client/src/tabs/SessionsTab.tsx +193 -0
  79. package/extensions/dashboard-server/api-contracts/core.ts +25 -0
  80. package/extensions/dashboard-server/api-contracts/endpoints.ts +102 -0
  81. package/extensions/dashboard-server/api-contracts/index.ts +10 -1
  82. package/extensions/dashboard-server/api-contracts.test.ts +20 -7
  83. package/extensions/dashboard-server/server.ts +100 -0
  84. package/extensions/mega-compact-s38.test.ts +63 -0
  85. package/extensions/mega-events/agent-handlers.ts +47 -3
  86. package/extensions/mega-events/compact-handlers.ts +11 -4
  87. package/extensions/mega-events/error-classifier.ts +25 -1
  88. package/extensions/mega-events/send-safe.ts +37 -0
  89. package/extensions/mega-events.ts +1 -0
  90. package/extensions/mega-runtime/state.ts +31 -0
  91. package/package.json +1 -1
  92. package/src/store/sqlite/global-index.test.ts +222 -0
  93. package/src/store/sqlite/global-index.ts +316 -1
  94. package/dist/extensions/dashboard-client/src/hooks/useApi.js +0 -51
  95. package/dist/extensions/dashboard-client/src/hooks/useSSE.js +0 -63
  96. package/extensions/dashboard-client/dist/assets/ConfigTab-DXD7zbqy.js.map +0 -1
  97. package/extensions/dashboard-client/dist/assets/EventsTab-BGeuYvM3.js +0 -2
  98. package/extensions/dashboard-client/dist/assets/EventsTab-BGeuYvM3.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ReposTab-_HNR3qYj.js","sources":["../../src/components/SummaryTiles.tsx","../../src/components/RepoTable.tsx","../../src/components/RepoDetailModal.tsx","../../src/components/ActiveReposTable.tsx","../../src/components/SavingsByModelTable.tsx","../../src/tabs/ReposTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/SummaryTiles.tsx — aggregate header tiles.\n *\n * 4 tiles: Repositories, Total Checkpoints, Total Tokens Saved,\n * Compressed-Original. Matches old dashboard #panel-summary tiles.\n */\n\nimport type React from \"react\";\n\nexport interface SummaryTilesProps {\n\t/** Total repos in the registry. */\n\ttotalRepos: number;\n\t/** Total checkpoints across all repos. */\n\ttotalCheckpoints: number;\n\t/** Total tokens saved across all repos. */\n\ttotalTokensSaved: number;\n\t/** Total compressed-original bytes across all repos. */\n\tcompressedOriginalBytes: number;\n}\n\n/** Format bytes → MiB/KiB/B (matches html.ts fmtBytesTop). */\nfunction fmtBytesTop(b: number): string {\n\tif (b >= 1_048_576) return `${(b / 1_048_576).toFixed(1)} MiB`;\n\tif (b >= 1024) return `${(b / 1024).toFixed(1)} KiB`;\n\treturn `${b} B`;\n}\n\n/** Format large numbers with K/M suffix. */\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 SummaryTiles({\n\ttotalRepos,\n\ttotalCheckpoints,\n\ttotalTokensSaved,\n\tcompressedOriginalBytes,\n}: SummaryTilesProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"summary-tiles\">\n\t\t\t<div className=\"tile\">\n\t\t\t\t<span className=\"tile-label\">Repositories</span>\n\t\t\t\t<span className=\"tile-value\">{totalRepos.toLocaleString()}</span>\n\t\t\t</div>\n\t\t\t<div className=\"tile\">\n\t\t\t\t<span className=\"tile-label\">Total Checkpoints</span>\n\t\t\t\t<span className=\"tile-value\">{totalCheckpoints.toLocaleString()}</span>\n\t\t\t</div>\n\t\t\t<div className=\"tile tile-highlight\">\n\t\t\t\t<span className=\"tile-label\">Total Tokens Saved</span>\n\t\t\t\t<span className=\"tile-value\">{fmt(totalTokensSaved)}</span>\n\t\t\t</div>\n\t\t\t<div className=\"tile\">\n\t\t\t\t<span className=\"tile-label\">Compressed-Original</span>\n\t\t\t\t<span className=\"tile-value\">{fmtBytesTop(compressedOriginalBytes)}</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/RepoTable.tsx — sortable, searchable repo table.\n *\n * 6 columns: Repo, Model, Checkpoints, Tokens Saved, Retained, Last Compacted.\n * Client-side sort (click header to toggle asc/desc). Search by displayName\n * (case-insensitive substring). Default sort: Last Compacted DESC.\n * Row click opens RepoDetailModal.\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { IndexesIndexRow } from \"@contracts\";\n\nexport interface RepoTableProps {\n\trepos: IndexesIndexRow[];\n\t/** Called when a row is clicked — opens the detail modal. */\n\tonSelect: (repo: IndexesIndexRow) => void;\n}\n\ntype SortKey =\n\t| \"displayName\"\n\t| \"modelName\"\n\t| \"checkpointCount\"\n\t| \"tokensSaved\"\n\t| \"compressedOriginalBytes\"\n\t| \"lastCompactedAt\";\n\ntype SortDir = \"asc\" | \"desc\";\n\nconst COLUMNS: Array<{ key: SortKey; label: string }> = [\n\t{ key: \"displayName\", label: \"Repo\" },\n\t{ key: \"modelName\", label: \"Model\" },\n\t{ key: \"checkpointCount\", label: \"Checkpoints\" },\n\t{ key: \"tokensSaved\", label: \"Tokens Saved\" },\n\t{ key: \"compressedOriginalBytes\", label: \"Retained\" },\n\t{ key: \"lastCompactedAt\", label: \"Last Compacted\" },\n];\n\n/** Format bytes → MiB/KiB/B (matches html.ts fmtBytesTop). */\nfunction fmtBytesTop(b: number): string {\n\tif (b >= 1_048_576) return `${(b / 1_048_576).toFixed(1)} MiB`;\n\tif (b >= 1024) return `${(b / 1024).toFixed(1)} KiB`;\n\treturn `${b} B`;\n}\n\nfunction formatTs(ts: number | null): string {\n\tif (ts === null || ts === undefined) return \"\\u2014\";\n\ttry {\n\t\treturn new Date(ts).toLocaleString();\n\t} catch {\n\t\treturn String(ts);\n\t}\n}\n\nexport function RepoTable({\n\trepos,\n\tonSelect,\n}: RepoTableProps): React.ReactElement {\n\tconst [search, setSearch] = useState(\"\");\n\tconst [sortKey, setSortKey] = useState<SortKey>(\"lastCompactedAt\");\n\tconst [sortDir, setSortDir] = useState<SortDir>(\"desc\");\n\n\tconst filtered = useMemo(() => {\n\t\tconst q = search.trim().toLowerCase();\n\t\tconst list = q\n\t\t\t? repos.filter((r) => r.displayName.toLowerCase().includes(q))\n\t\t\t: repos.slice();\n\t\tlist.sort((a, b) => {\n\t\t\tconst av = a[sortKey];\n\t\t\tconst bv = b[sortKey];\n\t\t\t// nulls last on desc, first on asc (consistent UX)\n\t\t\tconst aNull = av === null || av === undefined;\n\t\t\tconst bNull = bv === null || bv === undefined;\n\t\t\tif (aNull && bNull) return 0;\n\t\t\tif (aNull) return sortDir === \"desc\" ? 1 : -1;\n\t\t\tif (bNull) return sortDir === \"desc\" ? -1 : 1;\n\t\t\tconst cmp =\n\t\t\t\ttypeof av === \"number\" && typeof bv === \"number\"\n\t\t\t\t\t? av - bv\n\t\t\t\t\t: String(av).localeCompare(String(bv));\n\t\t\treturn sortDir === \"desc\" ? -cmp : cmp;\n\t\t});\n\t\treturn list;\n\t}, [repos, search, sortKey, sortDir]);\n\n\tconst handleSort = (key: SortKey): void => {\n\t\tif (sortKey === key) {\n\t\t\tsetSortDir((d) => (d === \"asc\" ? \"desc\" : \"asc\"));\n\t\t} else {\n\t\t\tsetSortKey(key);\n\t\t\tsetSortDir(\n\t\t\t\tkey === \"lastCompactedAt\" ||\n\t\t\t\tkey === \"tokensSaved\" ||\n\t\t\t\tkey === \"compressedOriginalBytes\"\n\t\t\t\t\t? \"desc\"\n\t\t\t\t\t: \"asc\",\n\t\t\t);\n\t\t}\n\t};\n\n\treturn (\n\t\t<div className=\"repo-table-wrap\">\n\t\t\t<input\n\t\t\t\ttype=\"search\"\n\t\t\t\tclassName=\"repo-search\"\n\t\t\t\tplaceholder=\"Search repos…\"\n\t\t\t\tvalue={search}\n\t\t\t\tonChange={(e) => setSearch(e.target.value)}\n\t\t\t\taria-label=\"Search repos\"\n\t\t\t/>\n\t\t\t<table className=\"repo-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t{COLUMNS.map((col) => (\n\t\t\t\t\t\t\t<th key={col.key}>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tclassName={`sort-header ${sortKey === col.key ? `active-${sortDir}` : \"\"}`}\n\t\t\t\t\t\t\t\t\tonClick={() => handleSort(col.key)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{col.label}\n\t\t\t\t\t\t\t\t\t{sortKey === col.key && (\n\t\t\t\t\t\t\t\t\t\t<span className=\"sort-arrow\">\n\t\t\t\t\t\t\t\t\t\t\t{sortDir === \"asc\" ? \" ▲\" : \" ▼\"}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td colSpan={COLUMNS.length} className=\"repo-empty\">\n\t\t\t\t\t\t\t\t{repos.length === 0\n\t\t\t\t\t\t\t\t\t? \"No repositories registered yet.\"\n\t\t\t\t\t\t\t\t\t: \"No repos match your search.\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t{filtered.map((repo) => (\n\t\t\t\t\t\t<tr key={repo.stateDir} onClick={() => onSelect(repo)}>\n\t\t\t\t\t\t\t<td className=\"repo-name\" title={repo.repoRoot}>\n\t\t\t\t\t\t\t\t{repo.displayName}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td className=\"repo-model\">{repo.modelName ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t<td className=\"num\">{repo.checkpointCount.toLocaleString()}</td>\n\t\t\t\t\t\t\t<td className=\"num\">{repo.tokensSaved.toLocaleString()}</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytesTop(repo.compressedOriginalBytes)}</td>\n\t\t\t\t\t\t\t<td className=\"num\">{formatTs(repo.lastCompactedAt)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/RepoDetailModal.tsx — repo drill-down.\n *\n * Full detail from an /api/index row: token breakdown (kept/dropped), model\n * info, context window, dedup rate, reasoning flag. Click backdrop to close.\n */\n\nimport type React from \"react\";\nimport { useEffect } from \"react\";\nimport type { IndexesIndexRow } from \"@contracts\";\n\nexport interface RepoDetailModalProps {\n\trepo: IndexesIndexRow;\n\tonClose: () => void;\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\n/** Format bytes → MiB/KiB/B (matches html.ts fmtBytesTop). */\nfunction fmtBytesTop(b: number): string {\n\tif (b >= 1_048_576) return `${(b / 1_048_576).toFixed(1)} MiB`;\n\tif (b >= 1024) return `${(b / 1024).toFixed(1)} KiB`;\n\treturn `${b} B`;\n}\n\nfunction formatTs(ts: number | null): string {\n\tif (ts === null || ts === undefined) return \"never\";\n\ttry {\n\t\treturn new Date(ts).toLocaleString();\n\t} catch {\n\t\treturn String(ts);\n\t}\n}\n\nexport function RepoDetailModal({\n\trepo,\n\tonClose,\n}: RepoDetailModalProps): React.ReactElement {\n\t// Close on Escape key.\n\tuseEffect(() => {\n\t\tconst handler = (e: KeyboardEvent): void => {\n\t\t\tif (e.key === \"Escape\") onClose();\n\t\t};\n\t\twindow.addEventListener(\"keydown\", handler);\n\t\treturn () => window.removeEventListener(\"keydown\", handler);\n\t}, [onClose]);\n\n\treturn (\n\t\t<div\n\t\t\tclassName=\"modal-backdrop\"\n\t\t\tonClick={onClose}\n\t\t\trole=\"dialog\"\n\t\t\taria-modal=\"true\"\n\t\t>\n\t\t\t<div className=\"modal-card\" onClick={(e) => e.stopPropagation()}>\n\t\t\t\t<header className=\"modal-header\">\n\t\t\t\t\t<h2>{repo.displayName}</h2>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"modal-close\"\n\t\t\t\t\t\tonClick={onClose}\n\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t>\n\t\t\t\t\t\t×\n\t\t\t\t\t</button>\n\t\t\t\t</header>\n\t\t\t\t<p className=\"modal-path\">{repo.repoRoot}</p>\n\t\t\t\t<div className=\"modal-grid\">\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Checkpoints</span>\n\t\t\t\t\t\t<span className=\"modal-value\">\n\t\t\t\t\t\t\t{repo.checkpointCount.toLocaleString()}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Sessions</span>\n\t\t\t\t\t\t<span className=\"modal-value\">\n\t\t\t\t\t\t\t{repo.sessions.toLocaleString()}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat modal-highlight\">\n\t\t\t\t\t\t<span className=\"modal-label\">Tokens saved</span>\n\t\t\t\t\t\t<span className=\"modal-value\">{fmt(repo.tokensSaved)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Tokens kept</span>\n\t\t\t\t\t\t<span className=\"modal-value\">{fmt(repo.tokensKept)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Tokens dropped</span>\n\t\t\t\t\t\t<span className=\"modal-value\">{fmt(repo.tokensDropped)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Dedup rate</span>\n\t\t\t\t\t\t<span className=\"modal-value\">—</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<h3>Model</h3>\n\t\t\t\t<dl className=\"modal-dl\">\n\t\t\t\t\t<dt>Provider</dt>\n\t\t\t\t\t<dd>{repo.providerName ?? \"—\"}</dd>\n\t\t\t\t\t<dt>Model</dt>\n\t\t\t\t\t<dd>{repo.modelName ?? \"—\"}</dd>\n\t\t\t\t\t<dt>Input rate</dt>\n\t\t\t\t\t<dd>{repo.inputRate !== null ? `${repo.inputRate} tok/s` : \"—\"}</dd>\n\t\t\t\t\t<dt>Output rate</dt>\n\t\t\t\t\t<dd>{repo.outputRate !== null ? `${repo.outputRate} tok/s` : \"—\"}</dd>\n\t\t\t\t\t<dt>Context window</dt>\n\t\t\t\t\t<dd>{repo.contextWindow !== null ? fmt(repo.contextWindow) : \"—\"}</dd>\n\t\t\t\t\t<dt>Max tokens</dt>\n\t\t\t\t\t<dd>{repo.maxTokens !== null ? fmt(repo.maxTokens) : \"—\"}</dd>\n\t\t\t\t\t<dt>Reasoning</dt>\n\t\t\t\t\t<dd>\n\t\t\t\t\t\t{repo.reasoning === null ? \"—\" : repo.reasoning ? \"on\" : \"off\"}\n\t\t\t\t\t</dd>\n\t\t\t\t</dl>\n\t\t\t\t<h3>Activity</h3>\n\t\t\t\t<dl className=\"modal-dl\">\n\t\t\t\t\t<dt>Last compacted</dt>\n\t\t\t\t\t<dd>{formatTs(repo.lastCompactedAt)}</dd>\n\t\t\t\t\t<dt>Last seen</dt>\n\t\t\t\t\t<dd>{formatTs(repo.lastSeen)}</dd>\n\t\t\t\t\t<dt>Compressed-Original</dt>\n\t\t\t\t\t<dd>{fmtBytesTop(repo.compressedOriginalBytes)}</dd>\n\t\t\t\t</dl>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/ActiveReposTable.tsx — Active repos table.\n *\n * 9 columns: Repo, Model, Tier, Context %, State, Compactions s/t,\n * Cache Hits s/t, Compact s/t, CacheHit s/t.\n * Data from fetchServers() via useApi with 10s polling.\n */\n\nimport type React from \"react\";\nimport type { ServerEntry } from \"@contracts\";\n\nexport interface ActiveReposTableProps {\n\tservers: ServerEntry[];\n}\n\n/** Format seconds → human-readable duration (matches html.ts fmtSec). */\nfunction fmtSec(s: number | undefined): string {\n\tconst v = s ?? 0;\n\tif (v >= 3600) return `${(v / 3600).toFixed(1)}h`;\n\tif (v >= 60) return `${Math.round(v / 60)}m`;\n\tif (v >= 1) return `${v.toFixed(1)}s`;\n\treturn `${Math.round(v * 1000)}ms`;\n}\n\nconst HEADERS = [\n\t\"Repo\",\n\t\"Model\",\n\t\"Tier\",\n\t\"Context %\",\n\t\"State\",\n\t\"Compactions (s/t)\",\n\t\"Cache Hits (s/t)\",\n\t\"Compact s/t (s)\",\n\t\"CacheHit s/t (s)\",\n] as const;\n\nexport function ActiveReposTable({\n\tservers,\n}: ActiveReposTableProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"active-repos-section\">\n\t\t\t<p className=\"legend-note\">\n\t\t\t\tRepos seen within the last 30 minutes, with their per-repo\n\t\t\t\tcache-hit, compaction, and time-saved (est.) totals pulled live\n\t\t\t\tfrom each repo&apos;s dashboard.json.\n\t\t\t</p>\n\t\t\t<div className=\"table-scroll\">\n\t\t\t\t<table className=\"active-repos-table\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t{HEADERS.map((h, i) => (\n\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\tkey={h}\n\t\t\t\t\t\t\t\t\tclassName={i >= 3 && i !== 4 ? \"num\" : undefined}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{h}\n\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{servers.length === 0 && (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={9} className=\"repo-empty\">\n\t\t\t\t\t\t\t\t\tNo active repositories.\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{servers.map((r) => {\n\t\t\t\t\t\t\tconst ch = r.cacheHits ?? {\n\t\t\t\t\t\t\t\tsession: 0,\n\t\t\t\t\t\t\t\ttotal: 0,\n\t\t\t\t\t\t\t\tsessionTokensSaved: 0,\n\t\t\t\t\t\t\t\ttotalTokensSaved: 0,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tconst cp = r.compacts ?? { session: 0, total: 0 };\n\t\t\t\t\t\t\tconst ts = r.timeSaved ?? {\n\t\t\t\t\t\t\t\tcompact: { sessionSec: 0, totalSec: 0 },\n\t\t\t\t\t\t\t\tcacheHit: { sessionSec: 0, totalSec: 0 },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<tr key={r.repoRoot}>\n\t\t\t\t\t\t\t\t\t<td title={r.repoRoot}>\n\t\t\t\t\t\t\t\t\t\t{r.displayName || r.repoRoot}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td>{r.model ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t\t\t<td>{r.tier ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{r.contextPct != null\n\t\t\t\t\t\t\t\t\t\t\t? `${Math.round(r.contextPct * 100)}%`\n\t\t\t\t\t\t\t\t\t\t\t: \"\\u2014\"}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td>{r.state ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{cp.session} / {cp.total}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{ch.session} / {ch.total}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{fmtSec(ts.compact.sessionSec)} /{\" \"}\n\t\t\t\t\t\t\t\t\t\t{fmtSec(ts.compact.totalSec)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{fmtSec(ts.cacheHit.sessionSec)} /{\" \"}\n\t\t\t\t\t\t\t\t\t\t{fmtSec(ts.cacheHit.totalSec)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/SavingsByModelTable.tsx — Savings by Model.\n *\n * 14 columns aggregated client-side from /api/index repos by model.\n * Replicates html.ts renderByModel logic: group by modelName, accumulate\n * tokens/cost/sessions, collapse numeric ranges when repos disagree.\n */\n\nimport type React from \"react\";\nimport { useMemo } from \"react\";\nimport type { IndexesIndexRow } from \"@contracts\";\n\nexport interface SavingsByModelTableProps {\n\trepos: IndexesIndexRow[];\n}\n\ninterface ModelGroup {\n\tmodel: string;\n\tprovider: string;\n\tcheckpoints: number;\n\ttokensSaved: number;\n\ttokensIn: number;\n\ttokensOut: number;\n\tsessions: number;\n\tusd: number;\n\tinRates: number[];\n\toutRates: number[];\n\tctxWindows: number[];\n\tmaxTokens: number[];\n\treasoning: boolean | null;\n\tlastAt: number;\n}\n\n/** Collapse numeric samples: \"—\" | single | \"lo–hi\" (matches html.ts). */\nfunction collapseNum(samples: number[]): string {\n\tif (!samples.length) return \"\\u2014\";\n\tconst lo = Math.min(...samples);\n\tconst hi = Math.max(...samples);\n\treturn lo === hi\n\t\t? lo.toLocaleString()\n\t\t: `${lo.toLocaleString()}\\u2013${hi.toLocaleString()}`;\n}\n\n/** Collapse rate samples: \"—\" | \"$rate\" | \"$lo–$hi\" (matches html.ts). */\nfunction collapseRate(samples: number[]): string {\n\tif (!samples.length) return \"\\u2014\";\n\tconst lo = Math.min(...samples);\n\tconst hi = Math.max(...samples);\n\tconst fmt = (v: number): string => `$${v.toFixed(6)}`;\n\treturn lo === hi ? fmt(lo) : `${fmt(lo)}\\u2013${fmt(hi)}`;\n}\n\n/** Group repos by model, accumulate totals, sort by tokensSaved desc. */\nfunction aggregate(repos: IndexesIndexRow[]): ModelGroup[] {\n\tconst map: Record<string, ModelGroup> = {};\n\tfor (const r of repos) {\n\t\tconst key = (r.modelName && r.modelName.trim()) || \"(unknown)\";\n\t\tif (!map[key]) {\n\t\t\tmap[key] = {\n\t\t\t\tmodel: key,\n\t\t\t\tprovider: r.providerName ?? r.provider ?? \"\\u2014\",\n\t\t\t\tcheckpoints: 0,\n\t\t\t\ttokensSaved: 0,\n\t\t\t\ttokensIn: 0,\n\t\t\t\ttokensOut: 0,\n\t\t\t\tsessions: 0,\n\t\t\t\tusd: 0,\n\t\t\t\tinRates: [],\n\t\t\t\toutRates: [],\n\t\t\t\tctxWindows: [],\n\t\t\t\tmaxTokens: [],\n\t\t\t\treasoning: null,\n\t\t\t\tlastAt: 0,\n\t\t\t};\n\t\t}\n\t\tconst g = map[key];\n\t\tg.checkpoints += r.checkpointCount || 0;\n\t\tg.tokensSaved += r.tokensSaved || 0;\n\t\tg.tokensIn += r.tokensDropped || 0;\n\t\tg.tokensOut += r.tokensKept || 0;\n\t\tg.sessions += r.sessions || 0;\n\t\tif (r.inputRate) {\n\t\t\tg.usd += (r.tokensSaved || 0) * r.inputRate;\n\t\t\tg.inRates.push(r.inputRate);\n\t\t}\n\t\tif (r.outputRate) g.outRates.push(r.outputRate);\n\t\tif (r.contextWindow) g.ctxWindows.push(r.contextWindow);\n\t\tif (r.maxTokens) g.maxTokens.push(r.maxTokens);\n\t\tif (r.reasoning != null) g.reasoning = r.reasoning;\n\t\tif (r.lastCompactedAt && r.lastCompactedAt > g.lastAt)\n\t\t\tg.lastAt = r.lastCompactedAt;\n\t}\n\treturn Object.values(map).sort((a, b) => b.tokensSaved - a.tokensSaved);\n}\n\n/** Tooltip text copied verbatim from html.ts title attributes. */\nconst TOOLTIPS = {\n\ttokensIn: \"Tokens dropped from context by compaction (the input reclaimed)\",\n\ttokensOut:\n\t\t\"Tokens kept as compacted summaries still in context (the output retained)\",\n\tctxWindow: \"Model context window (max input tokens the model accepts)\",\n\tmaxOut: \"Model max output tokens per turn\",\n\treas: \"Reasoning-capable model\",\n\tsessions: \"Distinct sessions with at least one checkpoint\",\n\tinRate: \"USD per input token\",\n\toutRate: \"USD per output token\",\n} as const;\n\nexport function SavingsByModelTable({\n\trepos,\n}: SavingsByModelTableProps): React.ReactElement {\n\tconst groups = useMemo(() => aggregate(repos), [repos]);\n\n\treturn (\n\t\t<div className=\"savings-by-model\">\n\t\t\t<p className=\"legend-note\">\n\t\t\t\tHow much context &amp; cost mega-compact has reclaimed, grouped by\n\t\t\t\tthe model you were running. Compression ratio reflects\n\t\t\t\tworkload/content, not model quality.\n\t\t\t</p>\n\t\t\t<div className=\"table-scroll\">\n\t\t\t\t<table className=\"savings-table\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<th>Model</th>\n\t\t\t\t\t\t\t<th>Provider</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.tokensIn}>\n\t\t\t\t\t\t\t\tTokens In\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.tokensOut}>\n\t\t\t\t\t\t\t\tTokens Out\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\">Freed</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.ctxWindow}>\n\t\t\t\t\t\t\t\tCtx Window\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.maxOut}>\n\t\t\t\t\t\t\t\tMax Out\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.reas}>\n\t\t\t\t\t\t\t\tReas.\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.sessions}>\n\t\t\t\t\t\t\t\tSessions\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\">Checkpoints</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.inRate}>\n\t\t\t\t\t\t\t\tIn $/tok\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.outRate}>\n\t\t\t\t\t\t\t\tOut $/tok\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\">$ Saved</th>\n\t\t\t\t\t\t\t<th className=\"num\">Last Used</th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{groups.length === 0 && (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={14} className=\"repo-empty\">\n\t\t\t\t\t\t\t\t\tNo repositories registered yet.\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{groups.map((g) => {\n\t\t\t\t\t\t\tconst freed = (g.tokensIn || 0) - (g.tokensOut || 0);\n\t\t\t\t\t\t\tconst usd =\n\t\t\t\t\t\t\t\tg.usd > 0 ? `$${g.usd.toFixed(4)}` : \"\\u2014\";\n\t\t\t\t\t\t\tconst when = g.lastAt\n\t\t\t\t\t\t\t\t? new Date(g.lastAt).toLocaleString()\n\t\t\t\t\t\t\t\t: \"\\u2014\";\n\t\t\t\t\t\t\tconst reas =\n\t\t\t\t\t\t\t\tg.reasoning == null\n\t\t\t\t\t\t\t\t\t? \"\\u2014\"\n\t\t\t\t\t\t\t\t\t: g.reasoning\n\t\t\t\t\t\t\t\t\t\t? \"yes\"\n\t\t\t\t\t\t\t\t\t\t: \"no\";\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<tr key={g.model}>\n\t\t\t\t\t\t\t\t\t<td className=\"repo-model\">{g.model}</td>\n\t\t\t\t\t\t\t\t\t<td>{g.provider}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{g.tokensIn.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{g.tokensOut.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{freed.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{collapseNum(g.ctxWindows)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{collapseNum(g.maxTokens)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">{reas}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{g.sessions.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{g.checkpoints.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{collapseRate(g.inRates)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{collapseRate(g.outRates)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">{usd}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">{when}</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\t\t\t<p className=\"legend-note\">\n\t\t\t\tTokens In = Σ original region tokens dropped by compaction. Tokens\n\t\t\t\tOut = Σ compacted summary tokens still retained in context. Freed =\n\t\t\t\tTokens In − Tokens Out (net context reclaimed). Ctx Window / Max Out\n\t\t\t\t/ Reas. come from the latest captured model snapshot for each repo.\n\t\t\t</p>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/ReposTab.tsx — Repos tab (C2 fleshed out).\n *\n * Renders: SummaryTiles (4 tiles), All Repositories table (RepoTable),\n * Active Repos table (ActiveReposTable), Savings by Model table\n * (SavingsByModelTable), and per-repo detail modal (RepoDetailModal).\n *\n * Data sources: fetchIndex() for summary + all repos + savings-by-model;\n * fetchServers() for active repos. Both polled every 10s.\n */\n\nimport type React from \"react\";\nimport { useCallback, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchIndex, fetchServers } from \"../api/client\";\nimport type { IndexesIndexRow, ServersResponse } from \"@contracts\";\nimport { SummaryTiles } from \"../components/SummaryTiles\";\nimport { RepoTable } from \"../components/RepoTable\";\nimport { RepoDetailModal } from \"../components/RepoDetailModal\";\nimport { ActiveReposTable } from \"../components/ActiveReposTable\";\nimport { SavingsByModelTable } from \"../components/SavingsByModelTable\";\n\n/**\n * Actual runtime shape of /api/index. The contract's IndexesSummaryResponse\n * puts summary fields at the top level, but the real server nests them under\n * `summary` (matching IndexFallbackResponse shape with non-null summary).\n */\ninterface DashboardIndexSummary {\n\ttotalRepos: number;\n\ttotalCheckpoints: number;\n\ttotalTokensSaved: number;\n\ttotalCompressedOriginalBytes: number;\n}\n\ninterface DashboardIndexResponse {\n\tupdatedAt: string | null;\n\tsummary: DashboardIndexSummary | null;\n\trepos: IndexesIndexRow[];\n}\n\n/** Fetch /api/index and cast to the actual runtime shape. */\nasync function fetchIndexTyped(): Promise<DashboardIndexResponse> {\n\treturn (await fetchIndex()) as unknown as DashboardIndexResponse;\n}\n\nexport default function ReposTab(): React.ReactElement {\n\tconst [selected, setSelected] = useState<IndexesIndexRow | null>(null);\n\n\tconst { data: indexData, error: indexErr } =\n\t\tuseApi<DashboardIndexResponse>(\n\t\t\tuseCallback(() => fetchIndexTyped(), []),\n\t\t\t{ pollInterval: 10_000 },\n\t\t);\n\n\tconst { data: serversData } = useApi<ServersResponse>(\n\t\tuseCallback(() => fetchServers(), []),\n\t\t{ pollInterval: 10_000 },\n\t);\n\n\tif (indexErr && !indexData) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">Error loading repos: {indexErr.message}</div>\n\t\t);\n\t}\n\tif (!indexData) {\n\t\treturn <div className=\"tab-stub\">Loading repos…</div>;\n\t}\n\n\tconst summary = indexData.summary;\n\tconst repos = indexData.repos;\n\tconst servers = serversData?.servers ?? [];\n\n\treturn (\n\t\t<div className=\"repos-tab\">\n\t\t\t<SummaryTiles\n\t\t\t\ttotalRepos={summary?.totalRepos ?? 0}\n\t\t\t\ttotalCheckpoints={summary?.totalCheckpoints ?? 0}\n\t\t\t\ttotalTokensSaved={summary?.totalTokensSaved ?? 0}\n\t\t\t\tcompressedOriginalBytes={summary?.totalCompressedOriginalBytes ?? 0}\n\t\t\t/>\n\t\t\t<h2 className=\"section-header\">All Repositories</h2>\n\t\t\t<RepoTable repos={repos} onSelect={setSelected} />\n\t\t\t<h2 className=\"section-header\">\n\t\t\t\tActive Repos — Live Cache Hits &amp; Compactions\n\t\t\t</h2>\n\t\t\t<ActiveReposTable servers={servers} />\n\t\t\t<h2 className=\"section-header\">Savings by Model</h2>\n\t\t\t<SavingsByModelTable repos={repos} />\n\t\t\t{selected && (\n\t\t\t\t<RepoDetailModal repo={selected} onClose={() => setSelected(null)} />\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["fmtBytesTop","b","fmt","n","SummaryTiles","totalRepos","totalCheckpoints","totalTokensSaved","compressedOriginalBytes","jsxs","jsx","COLUMNS","formatTs","ts","RepoTable","repos","onSelect","search","setSearch","useState","sortKey","setSortKey","sortDir","setSortDir","filtered","useMemo","q","list","r","a","av","bv","aNull","bNull","cmp","handleSort","key","d","e","col","repo","RepoDetailModal","onClose","useEffect","handler","fmtSec","v","HEADERS","ActiveReposTable","servers","h","i","ch","cp","collapseNum","samples","lo","hi","collapseRate","aggregate","map","g","TOOLTIPS","SavingsByModelTable","groups","freed","usd","when","reas","fetchIndexTyped","fetchIndex","ReposTab","selected","setSelected","indexData","indexErr","useApi","useCallback","serversData","fetchServers","summary"],"mappings":"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"}
1
+ {"version":3,"file":"ReposTab-DjEZmqUb.js","sources":["../../src/components/SummaryTiles.tsx","../../src/components/RepoTable.tsx","../../src/components/RepoDetailModal.tsx","../../src/components/ActiveReposTable.tsx","../../src/components/SavingsByModelTable.tsx","../../src/tabs/ReposTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/SummaryTiles.tsx — aggregate header tiles.\n *\n * 4 tiles: Repositories, Total Checkpoints, Total Tokens Saved,\n * Compressed-Original. Matches old dashboard #panel-summary tiles.\n */\n\nimport type React from \"react\";\n\nexport interface SummaryTilesProps {\n\t/** Total repos in the registry. */\n\ttotalRepos: number;\n\t/** Total checkpoints across all repos. */\n\ttotalCheckpoints: number;\n\t/** Total tokens saved across all repos. */\n\ttotalTokensSaved: number;\n\t/** Total compressed-original bytes across all repos. */\n\tcompressedOriginalBytes: number;\n}\n\n/** Format bytes → MiB/KiB/B (matches html.ts fmtBytesTop). */\nfunction fmtBytesTop(b: number): string {\n\tif (b >= 1_048_576) return `${(b / 1_048_576).toFixed(1)} MiB`;\n\tif (b >= 1024) return `${(b / 1024).toFixed(1)} KiB`;\n\treturn `${b} B`;\n}\n\n/** Format large numbers with K/M suffix. */\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 SummaryTiles({\n\ttotalRepos,\n\ttotalCheckpoints,\n\ttotalTokensSaved,\n\tcompressedOriginalBytes,\n}: SummaryTilesProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"summary-tiles\">\n\t\t\t<div className=\"tile\">\n\t\t\t\t<span className=\"tile-label\">Repositories</span>\n\t\t\t\t<span className=\"tile-value\">{totalRepos.toLocaleString()}</span>\n\t\t\t</div>\n\t\t\t<div className=\"tile\">\n\t\t\t\t<span className=\"tile-label\">Total Checkpoints</span>\n\t\t\t\t<span className=\"tile-value\">{totalCheckpoints.toLocaleString()}</span>\n\t\t\t</div>\n\t\t\t<div className=\"tile tile-highlight\">\n\t\t\t\t<span className=\"tile-label\">Total Tokens Saved</span>\n\t\t\t\t<span className=\"tile-value\">{fmt(totalTokensSaved)}</span>\n\t\t\t</div>\n\t\t\t<div className=\"tile\">\n\t\t\t\t<span className=\"tile-label\">Compressed-Original</span>\n\t\t\t\t<span className=\"tile-value\">{fmtBytesTop(compressedOriginalBytes)}</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/RepoTable.tsx — sortable, searchable repo table.\n *\n * 6 columns: Repo, Model, Checkpoints, Tokens Saved, Retained, Last Compacted.\n * Client-side sort (click header to toggle asc/desc). Search by displayName\n * (case-insensitive substring). Default sort: Last Compacted DESC.\n * Row click opens RepoDetailModal.\n */\n\nimport type React from \"react\";\nimport { useState, useMemo } from \"react\";\nimport type { IndexesIndexRow } from \"@contracts\";\n\nexport interface RepoTableProps {\n\trepos: IndexesIndexRow[];\n\t/** Called when a row is clicked — opens the detail modal. */\n\tonSelect: (repo: IndexesIndexRow) => void;\n}\n\ntype SortKey =\n\t| \"displayName\"\n\t| \"modelName\"\n\t| \"checkpointCount\"\n\t| \"tokensSaved\"\n\t| \"compressedOriginalBytes\"\n\t| \"lastCompactedAt\";\n\ntype SortDir = \"asc\" | \"desc\";\n\nconst COLUMNS: Array<{ key: SortKey; label: string }> = [\n\t{ key: \"displayName\", label: \"Repo\" },\n\t{ key: \"modelName\", label: \"Model\" },\n\t{ key: \"checkpointCount\", label: \"Checkpoints\" },\n\t{ key: \"tokensSaved\", label: \"Tokens Saved\" },\n\t{ key: \"compressedOriginalBytes\", label: \"Retained\" },\n\t{ key: \"lastCompactedAt\", label: \"Last Compacted\" },\n];\n\n/** Format bytes → MiB/KiB/B (matches html.ts fmtBytesTop). */\nfunction fmtBytesTop(b: number): string {\n\tif (b >= 1_048_576) return `${(b / 1_048_576).toFixed(1)} MiB`;\n\tif (b >= 1024) return `${(b / 1024).toFixed(1)} KiB`;\n\treturn `${b} B`;\n}\n\nfunction formatTs(ts: number | null): string {\n\tif (ts === null || ts === undefined) return \"\\u2014\";\n\ttry {\n\t\treturn new Date(ts).toLocaleString();\n\t} catch {\n\t\treturn String(ts);\n\t}\n}\n\nexport function RepoTable({\n\trepos,\n\tonSelect,\n}: RepoTableProps): React.ReactElement {\n\tconst [search, setSearch] = useState(\"\");\n\tconst [sortKey, setSortKey] = useState<SortKey>(\"lastCompactedAt\");\n\tconst [sortDir, setSortDir] = useState<SortDir>(\"desc\");\n\n\tconst filtered = useMemo(() => {\n\t\tconst q = search.trim().toLowerCase();\n\t\tconst list = q\n\t\t\t? repos.filter((r) => r.displayName.toLowerCase().includes(q))\n\t\t\t: repos.slice();\n\t\tlist.sort((a, b) => {\n\t\t\tconst av = a[sortKey];\n\t\t\tconst bv = b[sortKey];\n\t\t\t// nulls last on desc, first on asc (consistent UX)\n\t\t\tconst aNull = av === null || av === undefined;\n\t\t\tconst bNull = bv === null || bv === undefined;\n\t\t\tif (aNull && bNull) return 0;\n\t\t\tif (aNull) return sortDir === \"desc\" ? 1 : -1;\n\t\t\tif (bNull) return sortDir === \"desc\" ? -1 : 1;\n\t\t\tconst cmp =\n\t\t\t\ttypeof av === \"number\" && typeof bv === \"number\"\n\t\t\t\t\t? av - bv\n\t\t\t\t\t: String(av).localeCompare(String(bv));\n\t\t\treturn sortDir === \"desc\" ? -cmp : cmp;\n\t\t});\n\t\treturn list;\n\t}, [repos, search, sortKey, sortDir]);\n\n\tconst handleSort = (key: SortKey): void => {\n\t\tif (sortKey === key) {\n\t\t\tsetSortDir((d) => (d === \"asc\" ? \"desc\" : \"asc\"));\n\t\t} else {\n\t\t\tsetSortKey(key);\n\t\t\tsetSortDir(\n\t\t\t\tkey === \"lastCompactedAt\" ||\n\t\t\t\tkey === \"tokensSaved\" ||\n\t\t\t\tkey === \"compressedOriginalBytes\"\n\t\t\t\t\t? \"desc\"\n\t\t\t\t\t: \"asc\",\n\t\t\t);\n\t\t}\n\t};\n\n\treturn (\n\t\t<div className=\"repo-table-wrap\">\n\t\t\t<input\n\t\t\t\ttype=\"search\"\n\t\t\t\tclassName=\"repo-search\"\n\t\t\t\tplaceholder=\"Search repos…\"\n\t\t\t\tvalue={search}\n\t\t\t\tonChange={(e) => setSearch(e.target.value)}\n\t\t\t\taria-label=\"Search repos\"\n\t\t\t/>\n\t\t\t<table className=\"repo-table\">\n\t\t\t\t<thead>\n\t\t\t\t\t<tr>\n\t\t\t\t\t\t{COLUMNS.map((col) => (\n\t\t\t\t\t\t\t<th key={col.key}>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tclassName={`sort-header ${sortKey === col.key ? `active-${sortDir}` : \"\"}`}\n\t\t\t\t\t\t\t\t\tonClick={() => handleSort(col.key)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{col.label}\n\t\t\t\t\t\t\t\t\t{sortKey === col.key && (\n\t\t\t\t\t\t\t\t\t\t<span className=\"sort-arrow\">\n\t\t\t\t\t\t\t\t\t\t\t{sortDir === \"asc\" ? \" ▲\" : \" ▼\"}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</tr>\n\t\t\t\t</thead>\n\t\t\t\t<tbody>\n\t\t\t\t\t{filtered.length === 0 && (\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td colSpan={COLUMNS.length} className=\"repo-empty\">\n\t\t\t\t\t\t\t\t{repos.length === 0\n\t\t\t\t\t\t\t\t\t? \"No repositories registered yet.\"\n\t\t\t\t\t\t\t\t\t: \"No repos match your search.\"}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t)}\n\t\t\t\t\t{filtered.map((repo) => (\n\t\t\t\t\t\t<tr key={repo.stateDir} onClick={() => onSelect(repo)}>\n\t\t\t\t\t\t\t<td className=\"repo-name\" title={repo.repoRoot}>\n\t\t\t\t\t\t\t\t{repo.displayName}\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t<td className=\"repo-model\">{repo.modelName ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t<td className=\"num\">{repo.checkpointCount.toLocaleString()}</td>\n\t\t\t\t\t\t\t<td className=\"num\">{repo.tokensSaved.toLocaleString()}</td>\n\t\t\t\t\t\t\t<td className=\"num\">{fmtBytesTop(repo.compressedOriginalBytes)}</td>\n\t\t\t\t\t\t\t<td className=\"num\">{formatTs(repo.lastCompactedAt)}</td>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t))}\n\t\t\t\t</tbody>\n\t\t\t</table>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/RepoDetailModal.tsx — repo drill-down.\n *\n * Full detail from an /api/index row: token breakdown (kept/dropped), model\n * info, context window, dedup rate, reasoning flag. Click backdrop to close.\n */\n\nimport type React from \"react\";\nimport { useEffect } from \"react\";\nimport type { IndexesIndexRow } from \"@contracts\";\n\nexport interface RepoDetailModalProps {\n\trepo: IndexesIndexRow;\n\tonClose: () => void;\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\n/** Format bytes → MiB/KiB/B (matches html.ts fmtBytesTop). */\nfunction fmtBytesTop(b: number): string {\n\tif (b >= 1_048_576) return `${(b / 1_048_576).toFixed(1)} MiB`;\n\tif (b >= 1024) return `${(b / 1024).toFixed(1)} KiB`;\n\treturn `${b} B`;\n}\n\nfunction formatTs(ts: number | null): string {\n\tif (ts === null || ts === undefined) return \"never\";\n\ttry {\n\t\treturn new Date(ts).toLocaleString();\n\t} catch {\n\t\treturn String(ts);\n\t}\n}\n\nexport function RepoDetailModal({\n\trepo,\n\tonClose,\n}: RepoDetailModalProps): React.ReactElement {\n\t// Close on Escape key.\n\tuseEffect(() => {\n\t\tconst handler = (e: KeyboardEvent): void => {\n\t\t\tif (e.key === \"Escape\") onClose();\n\t\t};\n\t\twindow.addEventListener(\"keydown\", handler);\n\t\treturn () => window.removeEventListener(\"keydown\", handler);\n\t}, [onClose]);\n\n\treturn (\n\t\t<div\n\t\t\tclassName=\"modal-backdrop\"\n\t\t\tonClick={onClose}\n\t\t\trole=\"dialog\"\n\t\t\taria-modal=\"true\"\n\t\t>\n\t\t\t<div className=\"modal-card\" onClick={(e) => e.stopPropagation()}>\n\t\t\t\t<header className=\"modal-header\">\n\t\t\t\t\t<h2>{repo.displayName}</h2>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"modal-close\"\n\t\t\t\t\t\tonClick={onClose}\n\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t>\n\t\t\t\t\t\t×\n\t\t\t\t\t</button>\n\t\t\t\t</header>\n\t\t\t\t<p className=\"modal-path\">{repo.repoRoot}</p>\n\t\t\t\t<div className=\"modal-grid\">\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Checkpoints</span>\n\t\t\t\t\t\t<span className=\"modal-value\">\n\t\t\t\t\t\t\t{repo.checkpointCount.toLocaleString()}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Sessions</span>\n\t\t\t\t\t\t<span className=\"modal-value\">\n\t\t\t\t\t\t\t{repo.sessions.toLocaleString()}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat modal-highlight\">\n\t\t\t\t\t\t<span className=\"modal-label\">Tokens saved</span>\n\t\t\t\t\t\t<span className=\"modal-value\">{fmt(repo.tokensSaved)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Tokens kept</span>\n\t\t\t\t\t\t<span className=\"modal-value\">{fmt(repo.tokensKept)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Tokens dropped</span>\n\t\t\t\t\t\t<span className=\"modal-value\">{fmt(repo.tokensDropped)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"modal-stat\">\n\t\t\t\t\t\t<span className=\"modal-label\">Dedup rate</span>\n\t\t\t\t\t\t<span className=\"modal-value\">—</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<h3>Model</h3>\n\t\t\t\t<dl className=\"modal-dl\">\n\t\t\t\t\t<dt>Provider</dt>\n\t\t\t\t\t<dd>{repo.providerName ?? \"—\"}</dd>\n\t\t\t\t\t<dt>Model</dt>\n\t\t\t\t\t<dd>{repo.modelName ?? \"—\"}</dd>\n\t\t\t\t\t<dt>Input rate</dt>\n\t\t\t\t\t<dd>{repo.inputRate !== null ? `${repo.inputRate} tok/s` : \"—\"}</dd>\n\t\t\t\t\t<dt>Output rate</dt>\n\t\t\t\t\t<dd>{repo.outputRate !== null ? `${repo.outputRate} tok/s` : \"—\"}</dd>\n\t\t\t\t\t<dt>Context window</dt>\n\t\t\t\t\t<dd>{repo.contextWindow !== null ? fmt(repo.contextWindow) : \"—\"}</dd>\n\t\t\t\t\t<dt>Max tokens</dt>\n\t\t\t\t\t<dd>{repo.maxTokens !== null ? fmt(repo.maxTokens) : \"—\"}</dd>\n\t\t\t\t\t<dt>Reasoning</dt>\n\t\t\t\t\t<dd>\n\t\t\t\t\t\t{repo.reasoning === null ? \"—\" : repo.reasoning ? \"on\" : \"off\"}\n\t\t\t\t\t</dd>\n\t\t\t\t</dl>\n\t\t\t\t<h3>Activity</h3>\n\t\t\t\t<dl className=\"modal-dl\">\n\t\t\t\t\t<dt>Last compacted</dt>\n\t\t\t\t\t<dd>{formatTs(repo.lastCompactedAt)}</dd>\n\t\t\t\t\t<dt>Last seen</dt>\n\t\t\t\t\t<dd>{formatTs(repo.lastSeen)}</dd>\n\t\t\t\t\t<dt>Compressed-Original</dt>\n\t\t\t\t\t<dd>{fmtBytesTop(repo.compressedOriginalBytes)}</dd>\n\t\t\t\t</dl>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/ActiveReposTable.tsx — Active repos table.\n *\n * 9 columns: Repo, Model, Tier, Context %, State, Compactions s/t,\n * Cache Hits s/t, Compact s/t, CacheHit s/t.\n * Data from fetchServers() via useApi with 10s polling.\n */\n\nimport type React from \"react\";\nimport type { ServerEntry } from \"@contracts\";\n\nexport interface ActiveReposTableProps {\n\tservers: ServerEntry[];\n}\n\n/** Format seconds → human-readable duration (matches html.ts fmtSec). */\nfunction fmtSec(s: number | undefined): string {\n\tconst v = s ?? 0;\n\tif (v >= 3600) return `${(v / 3600).toFixed(1)}h`;\n\tif (v >= 60) return `${Math.round(v / 60)}m`;\n\tif (v >= 1) return `${v.toFixed(1)}s`;\n\treturn `${Math.round(v * 1000)}ms`;\n}\n\nconst HEADERS = [\n\t\"Repo\",\n\t\"Model\",\n\t\"Tier\",\n\t\"Context %\",\n\t\"State\",\n\t\"Compactions (s/t)\",\n\t\"Cache Hits (s/t)\",\n\t\"Compact s/t (s)\",\n\t\"CacheHit s/t (s)\",\n] as const;\n\nexport function ActiveReposTable({\n\tservers,\n}: ActiveReposTableProps): React.ReactElement {\n\treturn (\n\t\t<div className=\"active-repos-section\">\n\t\t\t<p className=\"legend-note\">\n\t\t\t\tRepos seen within the last 30 minutes, with their per-repo\n\t\t\t\tcache-hit, compaction, and time-saved (est.) totals pulled live\n\t\t\t\tfrom each repo&apos;s dashboard.json.\n\t\t\t</p>\n\t\t\t<div className=\"table-scroll\">\n\t\t\t\t<table className=\"active-repos-table\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t{HEADERS.map((h, i) => (\n\t\t\t\t\t\t\t\t<th\n\t\t\t\t\t\t\t\t\tkey={h}\n\t\t\t\t\t\t\t\t\tclassName={i >= 3 && i !== 4 ? \"num\" : undefined}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{h}\n\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{servers.length === 0 && (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={9} className=\"repo-empty\">\n\t\t\t\t\t\t\t\t\tNo active repositories.\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{servers.map((r) => {\n\t\t\t\t\t\t\tconst ch = r.cacheHits ?? {\n\t\t\t\t\t\t\t\tsession: 0,\n\t\t\t\t\t\t\t\ttotal: 0,\n\t\t\t\t\t\t\t\tsessionTokensSaved: 0,\n\t\t\t\t\t\t\t\ttotalTokensSaved: 0,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tconst cp = r.compacts ?? { session: 0, total: 0 };\n\t\t\t\t\t\t\tconst ts = r.timeSaved ?? {\n\t\t\t\t\t\t\t\tcompact: { sessionSec: 0, totalSec: 0 },\n\t\t\t\t\t\t\t\tcacheHit: { sessionSec: 0, totalSec: 0 },\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<tr key={r.repoRoot}>\n\t\t\t\t\t\t\t\t\t<td title={r.repoRoot}>\n\t\t\t\t\t\t\t\t\t\t{r.displayName || r.repoRoot}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td>{r.model ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t\t\t<td>{r.tier ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{r.contextPct != null\n\t\t\t\t\t\t\t\t\t\t\t? `${Math.round(r.contextPct * 100)}%`\n\t\t\t\t\t\t\t\t\t\t\t: \"\\u2014\"}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td>{r.state ?? \"\\u2014\"}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{cp.session} / {cp.total}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{ch.session} / {ch.total}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{fmtSec(ts.compact.sessionSec)} /{\" \"}\n\t\t\t\t\t\t\t\t\t\t{fmtSec(ts.compact.totalSec)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{fmtSec(ts.cacheHit.sessionSec)} /{\" \"}\n\t\t\t\t\t\t\t\t\t\t{fmtSec(ts.cacheHit.totalSec)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/components/SavingsByModelTable.tsx — Savings by Model.\n *\n * 14 columns aggregated client-side from /api/index repos by model.\n * Replicates html.ts renderByModel logic: group by modelName, accumulate\n * tokens/cost/sessions, collapse numeric ranges when repos disagree.\n */\n\nimport type React from \"react\";\nimport { useMemo } from \"react\";\nimport type { IndexesIndexRow } from \"@contracts\";\n\nexport interface SavingsByModelTableProps {\n\trepos: IndexesIndexRow[];\n}\n\ninterface ModelGroup {\n\tmodel: string;\n\tprovider: string;\n\tcheckpoints: number;\n\ttokensSaved: number;\n\ttokensIn: number;\n\ttokensOut: number;\n\tsessions: number;\n\tusd: number;\n\tinRates: number[];\n\toutRates: number[];\n\tctxWindows: number[];\n\tmaxTokens: number[];\n\treasoning: boolean | null;\n\tlastAt: number;\n}\n\n/** Collapse numeric samples: \"—\" | single | \"lo–hi\" (matches html.ts). */\nfunction collapseNum(samples: number[]): string {\n\tif (!samples.length) return \"\\u2014\";\n\tconst lo = Math.min(...samples);\n\tconst hi = Math.max(...samples);\n\treturn lo === hi\n\t\t? lo.toLocaleString()\n\t\t: `${lo.toLocaleString()}\\u2013${hi.toLocaleString()}`;\n}\n\n/** Collapse rate samples: \"—\" | \"$rate\" | \"$lo–$hi\" (matches html.ts). */\nfunction collapseRate(samples: number[]): string {\n\tif (!samples.length) return \"\\u2014\";\n\tconst lo = Math.min(...samples);\n\tconst hi = Math.max(...samples);\n\tconst fmt = (v: number): string => `$${v.toFixed(6)}`;\n\treturn lo === hi ? fmt(lo) : `${fmt(lo)}\\u2013${fmt(hi)}`;\n}\n\n/** Group repos by model, accumulate totals, sort by tokensSaved desc. */\nfunction aggregate(repos: IndexesIndexRow[]): ModelGroup[] {\n\tconst map: Record<string, ModelGroup> = {};\n\tfor (const r of repos) {\n\t\tconst key = (r.modelName && r.modelName.trim()) || \"(unknown)\";\n\t\tif (!map[key]) {\n\t\t\tmap[key] = {\n\t\t\t\tmodel: key,\n\t\t\t\tprovider: r.providerName ?? r.provider ?? \"\\u2014\",\n\t\t\t\tcheckpoints: 0,\n\t\t\t\ttokensSaved: 0,\n\t\t\t\ttokensIn: 0,\n\t\t\t\ttokensOut: 0,\n\t\t\t\tsessions: 0,\n\t\t\t\tusd: 0,\n\t\t\t\tinRates: [],\n\t\t\t\toutRates: [],\n\t\t\t\tctxWindows: [],\n\t\t\t\tmaxTokens: [],\n\t\t\t\treasoning: null,\n\t\t\t\tlastAt: 0,\n\t\t\t};\n\t\t}\n\t\tconst g = map[key];\n\t\tg.checkpoints += r.checkpointCount || 0;\n\t\tg.tokensSaved += r.tokensSaved || 0;\n\t\tg.tokensIn += r.tokensDropped || 0;\n\t\tg.tokensOut += r.tokensKept || 0;\n\t\tg.sessions += r.sessions || 0;\n\t\tif (r.inputRate) {\n\t\t\tg.usd += (r.tokensSaved || 0) * r.inputRate;\n\t\t\tg.inRates.push(r.inputRate);\n\t\t}\n\t\tif (r.outputRate) g.outRates.push(r.outputRate);\n\t\tif (r.contextWindow) g.ctxWindows.push(r.contextWindow);\n\t\tif (r.maxTokens) g.maxTokens.push(r.maxTokens);\n\t\tif (r.reasoning != null) g.reasoning = r.reasoning;\n\t\tif (r.lastCompactedAt && r.lastCompactedAt > g.lastAt)\n\t\t\tg.lastAt = r.lastCompactedAt;\n\t}\n\treturn Object.values(map).sort((a, b) => b.tokensSaved - a.tokensSaved);\n}\n\n/** Tooltip text copied verbatim from html.ts title attributes. */\nconst TOOLTIPS = {\n\ttokensIn: \"Tokens dropped from context by compaction (the input reclaimed)\",\n\ttokensOut:\n\t\t\"Tokens kept as compacted summaries still in context (the output retained)\",\n\tctxWindow: \"Model context window (max input tokens the model accepts)\",\n\tmaxOut: \"Model max output tokens per turn\",\n\treas: \"Reasoning-capable model\",\n\tsessions: \"Distinct sessions with at least one checkpoint\",\n\tinRate: \"USD per input token\",\n\toutRate: \"USD per output token\",\n} as const;\n\nexport function SavingsByModelTable({\n\trepos,\n}: SavingsByModelTableProps): React.ReactElement {\n\tconst groups = useMemo(() => aggregate(repos), [repos]);\n\n\treturn (\n\t\t<div className=\"savings-by-model\">\n\t\t\t<p className=\"legend-note\">\n\t\t\t\tHow much context &amp; cost mega-compact has reclaimed, grouped by\n\t\t\t\tthe model you were running. Compression ratio reflects\n\t\t\t\tworkload/content, not model quality.\n\t\t\t</p>\n\t\t\t<div className=\"table-scroll\">\n\t\t\t\t<table className=\"savings-table\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<th>Model</th>\n\t\t\t\t\t\t\t<th>Provider</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.tokensIn}>\n\t\t\t\t\t\t\t\tTokens In\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.tokensOut}>\n\t\t\t\t\t\t\t\tTokens Out\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\">Freed</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.ctxWindow}>\n\t\t\t\t\t\t\t\tCtx Window\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.maxOut}>\n\t\t\t\t\t\t\t\tMax Out\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.reas}>\n\t\t\t\t\t\t\t\tReas.\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.sessions}>\n\t\t\t\t\t\t\t\tSessions\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\">Checkpoints</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.inRate}>\n\t\t\t\t\t\t\t\tIn $/tok\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\" title={TOOLTIPS.outRate}>\n\t\t\t\t\t\t\t\tOut $/tok\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th className=\"num\">$ Saved</th>\n\t\t\t\t\t\t\t<th className=\"num\">Last Used</th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{groups.length === 0 && (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={14} className=\"repo-empty\">\n\t\t\t\t\t\t\t\t\tNo repositories registered yet.\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{groups.map((g) => {\n\t\t\t\t\t\t\tconst freed = (g.tokensIn || 0) - (g.tokensOut || 0);\n\t\t\t\t\t\t\tconst usd =\n\t\t\t\t\t\t\t\tg.usd > 0 ? `$${g.usd.toFixed(4)}` : \"\\u2014\";\n\t\t\t\t\t\t\tconst when = g.lastAt\n\t\t\t\t\t\t\t\t? new Date(g.lastAt).toLocaleString()\n\t\t\t\t\t\t\t\t: \"\\u2014\";\n\t\t\t\t\t\t\tconst reas =\n\t\t\t\t\t\t\t\tg.reasoning == null\n\t\t\t\t\t\t\t\t\t? \"\\u2014\"\n\t\t\t\t\t\t\t\t\t: g.reasoning\n\t\t\t\t\t\t\t\t\t\t? \"yes\"\n\t\t\t\t\t\t\t\t\t\t: \"no\";\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<tr key={g.model}>\n\t\t\t\t\t\t\t\t\t<td className=\"repo-model\">{g.model}</td>\n\t\t\t\t\t\t\t\t\t<td>{g.provider}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{g.tokensIn.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{g.tokensOut.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{freed.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{collapseNum(g.ctxWindows)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{collapseNum(g.maxTokens)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">{reas}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{g.sessions.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{g.checkpoints.toLocaleString()}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{collapseRate(g.inRates)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">\n\t\t\t\t\t\t\t\t\t\t{collapseRate(g.outRates)}\n\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">{usd}</td>\n\t\t\t\t\t\t\t\t\t<td className=\"num\">{when}</td>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\t\t\t<p className=\"legend-note\">\n\t\t\t\tTokens In = Σ original region tokens dropped by compaction. Tokens\n\t\t\t\tOut = Σ compacted summary tokens still retained in context. Freed =\n\t\t\t\tTokens In − Tokens Out (net context reclaimed). Ctx Window / Max Out\n\t\t\t\t/ Reas. come from the latest captured model snapshot for each repo.\n\t\t\t</p>\n\t\t</div>\n\t);\n}\n","/**\n * dashboard-client/src/tabs/ReposTab.tsx — Repos tab (C2 fleshed out).\n *\n * Renders: SummaryTiles (4 tiles), All Repositories table (RepoTable),\n * Active Repos table (ActiveReposTable), Savings by Model table\n * (SavingsByModelTable), and per-repo detail modal (RepoDetailModal).\n *\n * Data sources: fetchIndex() for summary + all repos + savings-by-model;\n * fetchServers() for active repos. Both polled every 10s.\n */\n\nimport type React from \"react\";\nimport { useCallback, useState } from \"react\";\nimport { useApi } from \"../hooks/useApi\";\nimport { fetchIndex, fetchServers } from \"../api/client\";\nimport type { IndexesIndexRow, ServersResponse } from \"@contracts\";\nimport { SummaryTiles } from \"../components/SummaryTiles\";\nimport { RepoTable } from \"../components/RepoTable\";\nimport { RepoDetailModal } from \"../components/RepoDetailModal\";\nimport { ActiveReposTable } from \"../components/ActiveReposTable\";\nimport { SavingsByModelTable } from \"../components/SavingsByModelTable\";\n\n/**\n * Actual runtime shape of /api/index. The contract's IndexesSummaryResponse\n * puts summary fields at the top level, but the real server nests them under\n * `summary` (matching IndexFallbackResponse shape with non-null summary).\n */\ninterface DashboardIndexSummary {\n\ttotalRepos: number;\n\ttotalCheckpoints: number;\n\ttotalTokensSaved: number;\n\ttotalCompressedOriginalBytes: number;\n}\n\ninterface DashboardIndexResponse {\n\tupdatedAt: string | null;\n\tsummary: DashboardIndexSummary | null;\n\trepos: IndexesIndexRow[];\n}\n\n/** Fetch /api/index and cast to the actual runtime shape. */\nasync function fetchIndexTyped(): Promise<DashboardIndexResponse> {\n\treturn (await fetchIndex()) as unknown as DashboardIndexResponse;\n}\n\nexport default function ReposTab(): React.ReactElement {\n\tconst [selected, setSelected] = useState<IndexesIndexRow | null>(null);\n\n\tconst { data: indexData, error: indexErr } =\n\t\tuseApi<DashboardIndexResponse>(\n\t\t\tuseCallback(() => fetchIndexTyped(), []),\n\t\t\t{ pollInterval: 10_000 },\n\t\t);\n\n\tconst { data: serversData } = useApi<ServersResponse>(\n\t\tuseCallback(() => fetchServers(), []),\n\t\t{ pollInterval: 10_000 },\n\t);\n\n\tif (indexErr && !indexData) {\n\t\treturn (\n\t\t\t<div className=\"tab-stub\">Error loading repos: {indexErr.message}</div>\n\t\t);\n\t}\n\tif (!indexData) {\n\t\treturn <div className=\"tab-stub\">Loading repos…</div>;\n\t}\n\n\tconst summary = indexData.summary;\n\tconst repos = indexData.repos;\n\tconst servers = serversData?.servers ?? [];\n\n\treturn (\n\t\t<div className=\"repos-tab\">\n\t\t\t<SummaryTiles\n\t\t\t\ttotalRepos={summary?.totalRepos ?? 0}\n\t\t\t\ttotalCheckpoints={summary?.totalCheckpoints ?? 0}\n\t\t\t\ttotalTokensSaved={summary?.totalTokensSaved ?? 0}\n\t\t\t\tcompressedOriginalBytes={summary?.totalCompressedOriginalBytes ?? 0}\n\t\t\t/>\n\t\t\t<h2 className=\"section-header\">All Repositories</h2>\n\t\t\t<RepoTable repos={repos} onSelect={setSelected} />\n\t\t\t<h2 className=\"section-header\">\n\t\t\t\tActive Repos — Live Cache Hits &amp; Compactions\n\t\t\t</h2>\n\t\t\t<ActiveReposTable servers={servers} />\n\t\t\t<h2 className=\"section-header\">Savings by Model</h2>\n\t\t\t<SavingsByModelTable repos={repos} />\n\t\t\t{selected && (\n\t\t\t\t<RepoDetailModal repo={selected} onClose={() => setSelected(null)} />\n\t\t\t)}\n\t\t</div>\n\t);\n}\n"],"names":["fmtBytesTop","b","fmt","n","SummaryTiles","totalRepos","totalCheckpoints","totalTokensSaved","compressedOriginalBytes","jsxs","jsx","COLUMNS","formatTs","ts","RepoTable","repos","onSelect","search","setSearch","useState","sortKey","setSortKey","sortDir","setSortDir","filtered","useMemo","q","list","r","a","av","bv","aNull","bNull","cmp","handleSort","key","d","e","col","repo","RepoDetailModal","onClose","useEffect","handler","fmtSec","v","HEADERS","ActiveReposTable","servers","h","i","ch","cp","collapseNum","samples","lo","hi","collapseRate","aggregate","map","g","TOOLTIPS","SavingsByModelTable","groups","freed","usd","when","reas","fetchIndexTyped","fetchIndex","ReposTab","selected","setSelected","indexData","indexErr","useApi","useCallback","serversData","fetchServers","summary"],"mappings":"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"}