pi-mega-compact 0.11.3 → 0.11.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extensions/dashboard-server/api-contracts/endpoints.js +19 -0
- package/dist/extensions/dashboard-server/api-contracts/memory.js +8 -0
- package/dist/extensions/dashboard-server/api-contracts/provider-cache.js +1 -0
- package/dist/extensions/dashboard-server/api-contracts/setup.js +10 -0
- package/dist/extensions/dashboard-server/index-reader.js +6 -2
- package/dist/extensions/dashboard-server/routes-cache.js +115 -55
- package/dist/extensions/dashboard-server/routes-memory.js +51 -0
- package/dist/extensions/dashboard-server/routes-repo.js +2 -2
- package/dist/extensions/dashboard-server/routes-setup.js +162 -0
- package/dist/extensions/dashboard-server/routes.js +2 -0
- package/dist/extensions/dashboard-server/server.js +7 -1
- package/dist/extensions/mega-commands.js +189 -1
- package/dist/extensions/mega-config.js +3 -0
- package/dist/extensions/mega-events/agent-handlers.js +41 -6
- package/dist/extensions/mega-events/compact-handlers.js +1 -0
- package/dist/extensions/mega-events/context-handler.js +37 -92
- package/dist/extensions/mega-events/context-hwm.js +51 -0
- package/dist/extensions/mega-events/error-classifier.js +118 -128
- package/dist/extensions/mega-events/mirror-append.js +107 -0
- package/dist/extensions/mega-events/outage-advisor.js +59 -0
- package/dist/extensions/mega-events/perf-handler.js +51 -1
- package/dist/extensions/mega-events/recall-tail.js +59 -0
- package/dist/extensions/mega-events/session-handlers.js +15 -8
- package/dist/extensions/mega-runtime/reset-runtime.js +6 -0
- package/dist/extensions/mega-runtime/runtime.js +7 -0
- package/dist/src/embedder.js +41 -2
- package/dist/src/memoryStats.js +75 -0
- package/dist/src/prefix-break.js +43 -0
- package/dist/src/pricing.js +1 -1
- package/dist/src/store/sqlite/perf-samples.js +144 -16
- package/dist/src/store/turns/memory-store.js +8 -0
- package/dist/src/store/turns/sqlite-store.js +14 -3
- package/dist/src/store/turns/types.js +18 -1
- package/dist/src/store/vectorIndex.js +26 -5
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-vEo2MtE2.js → AchievementTiles-BoL7qBOJ.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-vEo2MtE2.js.map → AchievementTiles-BoL7qBOJ.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/AchievementsTab-BFDE3HYc.js +2 -0
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-DjxfYrDc.js.map → AchievementsTab-BFDE3HYc.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/CacheTab-eKVc-NV1.js +2 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-eKVc-NV1.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{ConfigTab-Cm6ymbmx.js → ConfigTab-DDFE-ckA.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ConfigTab-Cm6ymbmx.js.map → ConfigTab-DDFE-ckA.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-DXWl_YzI.js → EventsTab-B6UxZ7PM.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-DXWl_YzI.js.map → EventsTab-B6UxZ7PM.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{GameTab-BLDudcO5.js → GameTab-DehUX-KE.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{GameTab-BLDudcO5.js.map → GameTab-DehUX-KE.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DLkKCztn.js → MaintenanceTab-0GWRt4wn.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DLkKCztn.js.map → MaintenanceTab-0GWRt4wn.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Cq4u-rZF.js → MetricsTab-C3rwMVsN.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Cq4u-rZF.js.map → MetricsTab-C3rwMVsN.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/OverviewTab-39O9_x5j.js +2 -0
- package/extensions/dashboard-client/dist/assets/{OverviewTab-D4kGvsiQ.js.map → OverviewTab-39O9_x5j.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/ReposTab-Cfj6Q5sH.js +2 -0
- package/extensions/dashboard-client/dist/assets/ReposTab-Cfj6Q5sH.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DZC2-GCn.js → SessionsTab-DXDTCi3r.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DZC2-GCn.js.map → SessionsTab-DXDTCi3r.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-mMlfunJa.js +2 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-mMlfunJa.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-Cw1dJ4e9.js +2 -0
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-Cw1dJ4e9.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{TopicsTab-01G71hv1.js → TopicsTab-BAw972BV.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TopicsTab-01G71hv1.js.map → TopicsTab-BAw972BV.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DLRg0LZT.js → TurnsTab-Cf5mGIdl.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DLRg0LZT.js.map → TurnsTab-Cf5mGIdl.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/format-COjDIVGj.js +2 -0
- package/extensions/dashboard-client/dist/assets/format-COjDIVGj.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{index-BX0bcgLu.js → index-4gQ7auad.js} +10 -10
- package/extensions/dashboard-client/dist/assets/index-4gQ7auad.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/pricing-CvE4NllQ.js +2 -0
- package/extensions/dashboard-client/dist/assets/pricing-CvE4NllQ.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{useSSE-BBYt5UPB.js → useSSE-DNgMuyP6.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-BBYt5UPB.js.map → useSSE-DNgMuyP6.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +1 -1
- package/extensions/dashboard-client/src/App.tsx +4 -0
- package/extensions/dashboard-client/src/api/client.ts +14 -0
- package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +21 -12
- package/extensions/dashboard-client/src/components/ProviderCacheCard.tsx +56 -0
- package/extensions/dashboard-client/src/components/SavingsByModelTable.tsx +38 -57
- package/extensions/dashboard-client/src/tabs/CacheTab.tsx +1 -0
- package/extensions/dashboard-client/src/tabs/SetupTab.tsx +355 -0
- package/extensions/dashboard-client/tsconfig.json +3 -2
- package/extensions/dashboard-client/vite.config.ts +2 -0
- package/extensions/dashboard-server/api-contracts/endpoints.ts +36 -3
- package/extensions/dashboard-server/api-contracts/index.ts +7 -0
- package/extensions/dashboard-server/api-contracts/memory.ts +27 -0
- package/extensions/dashboard-server/api-contracts/multi-repo.ts +231 -231
- package/extensions/dashboard-server/api-contracts/provider-cache.ts +37 -0
- package/extensions/dashboard-server/api-contracts/setup.ts +58 -0
- package/extensions/dashboard-server/index-reader.ts +138 -121
- package/extensions/dashboard-server/routes-cache.ts +161 -85
- package/extensions/dashboard-server/routes-memory.ts +63 -0
- package/extensions/dashboard-server/routes-repo.ts +36 -36
- package/extensions/dashboard-server/routes-setup.ts +181 -0
- package/extensions/dashboard-server/routes.ts +2 -0
- package/extensions/dashboard-server/server.ts +6 -0
- package/extensions/mega-commands.ts +211 -1
- package/extensions/mega-config.ts +18 -0
- package/extensions/mega-events/agent-handlers.ts +44 -5
- package/extensions/mega-events/compact-handlers.ts +1 -0
- package/extensions/mega-events/context-handler.ts +45 -110
- package/extensions/mega-events/context-hwm.ts +85 -0
- package/extensions/mega-events/error-classifier.ts +156 -124
- package/extensions/mega-events/mirror-append.ts +155 -0
- package/extensions/mega-events/outage-advisor.ts +79 -0
- package/extensions/mega-events/perf-handler.ts +79 -5
- package/extensions/mega-events/recall-tail.ts +88 -0
- package/extensions/mega-events/session-handlers.ts +14 -7
- package/extensions/mega-runtime/helpers.ts +14 -0
- package/extensions/mega-runtime/reset-runtime.ts +6 -0
- package/extensions/mega-runtime/runtime.ts +7 -0
- package/package.json +5 -2
- package/src/embedder.ts +42 -2
- package/src/memoryStats.ts +107 -0
- package/src/prefix-break.ts +83 -0
- package/src/pricing.ts +2 -2
- package/src/store/sqlite/perf-samples.ts +215 -22
- package/src/store/turns/memory-store.ts +10 -1
- package/src/store/turns/sqlite-store.ts +27 -15
- package/src/store/turns/types.ts +21 -0
- package/src/store/vectorIndex.ts +33 -4
- package/dist/extensions/conflict-scan.test.js +0 -115
- package/dist/extensions/dashboard-server/api-contracts.test.js +0 -884
- package/dist/extensions/dashboard-server/html.test.js +0 -52
- package/dist/extensions/dashboard-server/perf-server.test.js +0 -80
- package/dist/extensions/dashboard-server/routes-cache.test.js +0 -248
- package/dist/extensions/dashboard-server/server.test.js +0 -120
- package/dist/extensions/dashboard-server-s32.test.js +0 -181
- package/dist/extensions/dashboard-server.test.js +0 -339
- package/dist/extensions/mega-cache-replay.test.js +0 -400
- package/dist/extensions/mega-compact-s38.test.js +0 -820
- package/dist/extensions/mega-compact.test.js +0 -1112
- package/dist/extensions/mega-events.test.js +0 -47
- package/dist/extensions/mega-game-cmds.test.js +0 -163
- package/dist/extensions/mega-runtime/snapshot.test.js +0 -127
- package/dist/extensions/mega-runtime/state.test.js +0 -266
- package/dist/extensions/mega-runtime/widget.test.js +0 -343
- package/dist/extensions/mega-shutdown-widget.test.js +0 -121
- package/dist/extensions/mega-teamrun.test.js +0 -156
- package/dist/extensions/mega-trim.test.js +0 -58
- package/dist/extensions/mega-turn-store.test.js +0 -68
- package/dist/src/boundary.test.js +0 -170
- package/dist/src/compact.test.js +0 -78
- package/dist/src/config/themes.test.js +0 -94
- package/dist/src/dedup/dedup.test.js +0 -41
- package/dist/src/dedup/l1.test.js +0 -50
- package/dist/src/dedup/raptor/buildHistory.test.js +0 -292
- package/dist/src/dedup/raptor/multilevel-serve.test.js +0 -229
- package/dist/src/dedup/raptor/multilevel.test.js +0 -238
- package/dist/src/dedup/raptor/promote.test.js +0 -69
- package/dist/src/dedup/raptor/raptor.test.js +0 -248
- package/dist/src/dedup/raptor/retrieval.test.js +0 -95
- package/dist/src/dedup/raptor/serve-gate.test.js +0 -298
- package/dist/src/dedup/sprint12.test.js +0 -219
- package/dist/src/dedup-engine.test.js +0 -472
- package/dist/src/driftDetection.test.js +0 -87
- package/dist/src/e2e.test.js +0 -698
- package/dist/src/engine.test.js +0 -111
- package/dist/src/extractive.test.js +0 -147
- package/dist/src/fork.test.js +0 -81
- package/dist/src/game/scoring.test.js +0 -98
- package/dist/src/httpEmbedder.test.js +0 -277
- package/dist/src/importance.test.js +0 -372
- package/dist/src/intent.test.js +0 -78
- package/dist/src/log.test.js +0 -42
- package/dist/src/mechanical-fix.test.js +0 -65
- package/dist/src/memory.test.js +0 -70
- package/dist/src/memoryConsolidate.test.js +0 -38
- package/dist/src/memoryOps.test.js +0 -129
- package/dist/src/memoryRecall.test.js +0 -171
- package/dist/src/memoryRoundtrip.test.js +0 -137
- package/dist/src/metrics/turns.test.js +0 -152
- package/dist/src/mirror/mirror.test.js +0 -185
- package/dist/src/raptor-inject-summaries.test.js +0 -162
- package/dist/src/ratio.bench.test.js +0 -897
- package/dist/src/recall.integration.test.js +0 -77
- package/dist/src/recall.test.js +0 -379
- package/dist/src/sprint14.test.js +0 -219
- package/dist/src/sprint4x-rag-verification.test.js +0 -93
- package/dist/src/store/compression.test.js +0 -139
- package/dist/src/store/memoryIndex.test.js +0 -51
- package/dist/src/store/migrate.test.js +0 -139
- package/dist/src/store/pgOpenGuard.test.js +0 -74
- package/dist/src/store/phase01.test.js +0 -81
- package/dist/src/store/phase04.test.js +0 -54
- package/dist/src/store/sprint10.test.js +0 -186
- package/dist/src/store/sqlite/game-achievements.test.js +0 -67
- package/dist/src/store/sqlite/game-scores.test.js +0 -106
- package/dist/src/store/sqlite/game-state.test.js +0 -76
- package/dist/src/store/sqlite/global-index.test.js +0 -136
- package/dist/src/store/sqlite/mechanical-fix.test.js +0 -146
- package/dist/src/store/sqlite/perf-samples.test.js +0 -146
- package/dist/src/store/sqlite/raptor.test.js +0 -106
- package/dist/src/store/sqlite/turns.test.js +0 -183
- package/dist/src/store/sqlite.cachehit.test.js +0 -55
- package/dist/src/store/sqlite.dbmaint.test.js +0 -158
- package/dist/src/store/sqlite.dbmirror.test.js +0 -184
- package/dist/src/store/turns/contract-compliance.test.js +0 -428
- package/dist/src/store/turns/memory-store.test.js +0 -9
- package/dist/src/store/turns/migrations.test.js +0 -138
- package/dist/src/store/turns/sqlite-store.test.js +0 -144
- package/dist/src/store/vectorIndex.test.js +0 -123
- package/dist/src/store.test.js +0 -161
- package/dist/src/supersede.test.js +0 -36
- package/dist/src/topics/cluster.test.js +0 -150
- package/dist/src/topics/store.test.js +0 -117
- package/dist/src/types.test.js +0 -9
- package/dist/src/vector-search-cache.test.js +0 -157
- package/dist/src/vectorStore.test.js +0 -479
- package/dist/src/wiki.test.js +0 -171
- package/extensions/conflict-scan.test.ts +0 -129
- package/extensions/dashboard-client/dist/assets/AchievementsTab-DjxfYrDc.js +0 -2
- package/extensions/dashboard-client/dist/assets/CacheTab-Dnpv94wq.js +0 -2
- package/extensions/dashboard-client/dist/assets/CacheTab-Dnpv94wq.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/OverviewTab-D4kGvsiQ.js +0 -2
- package/extensions/dashboard-client/dist/assets/ReposTab-C0Wnrf2u.js +0 -2
- package/extensions/dashboard-client/dist/assets/ReposTab-C0Wnrf2u.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-UqljSyP-.js +0 -2
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-UqljSyP-.js.map +0 -1
- package/extensions/dashboard-client/dist/assets/index-BX0bcgLu.js.map +0 -1
- package/extensions/dashboard-server/api-contracts.test.ts +0 -1065
- package/extensions/dashboard-server/html.test.ts +0 -62
- package/extensions/dashboard-server/perf-server.test.ts +0 -101
- package/extensions/dashboard-server/routes-cache.test.ts +0 -302
- package/extensions/dashboard-server/server.test.ts +0 -131
- package/extensions/dashboard-server-s32.test.ts +0 -195
- package/extensions/dashboard-server.test.ts +0 -382
- package/extensions/mega-cache-replay.test.ts +0 -526
- package/extensions/mega-compact-s38.test.ts +0 -872
- package/extensions/mega-compact.test.ts +0 -1509
- package/extensions/mega-events.test.ts +0 -55
- package/extensions/mega-game-cmds.test.ts +0 -197
- package/extensions/mega-runtime/snapshot.test.ts +0 -139
- package/extensions/mega-runtime/state.test.ts +0 -262
- package/extensions/mega-runtime/widget.test.ts +0 -468
- package/extensions/mega-shutdown-widget.test.ts +0 -141
- package/extensions/mega-teamrun.test.ts +0 -176
- package/extensions/mega-trim.test.ts +0 -64
- package/extensions/mega-turn-store.test.ts +0 -108
- package/src/boundary.test.ts +0 -187
- package/src/compact.test.ts +0 -99
- package/src/config/themes.test.ts +0 -116
- package/src/dedup/dedup.test.ts +0 -46
- package/src/dedup/l1.test.ts +0 -57
- package/src/dedup/raptor/buildHistory.test.ts +0 -353
- package/src/dedup/raptor/multilevel-serve.test.ts +0 -273
- package/src/dedup/raptor/multilevel.test.ts +0 -325
- package/src/dedup/raptor/promote.test.ts +0 -82
- package/src/dedup/raptor/raptor.test.ts +0 -297
- package/src/dedup/raptor/retrieval.test.ts +0 -118
- package/src/dedup/raptor/serve-gate.test.ts +0 -348
- package/src/dedup/sprint12.test.ts +0 -242
- package/src/dedup-engine.test.ts +0 -670
- package/src/driftDetection.test.ts +0 -100
- package/src/e2e.test.ts +0 -843
- package/src/engine.test.ts +0 -123
- package/src/extractive.test.ts +0 -174
- package/src/fork.test.ts +0 -94
- package/src/game/scoring.test.ts +0 -103
- package/src/httpEmbedder.test.ts +0 -286
- package/src/importance.test.ts +0 -538
- package/src/intent.test.ts +0 -87
- package/src/log.test.ts +0 -47
- package/src/mechanical-fix.test.ts +0 -70
- package/src/memory.test.ts +0 -92
- package/src/memoryConsolidate.test.ts +0 -47
- package/src/memoryOps.test.ts +0 -148
- package/src/memoryRecall.test.ts +0 -194
- package/src/memoryRoundtrip.test.ts +0 -155
- package/src/metrics/turns.test.ts +0 -165
- package/src/mirror/mirror.test.ts +0 -240
- package/src/raptor-inject-summaries.test.ts +0 -228
- package/src/ratio.bench.test.ts +0 -1316
- package/src/recall.integration.test.ts +0 -96
- package/src/recall.test.ts +0 -467
- package/src/sprint14.test.ts +0 -245
- package/src/sprint4x-rag-verification.test.ts +0 -119
- package/src/store/compression.test.ts +0 -168
- package/src/store/memoryIndex.test.ts +0 -61
- package/src/store/migrate.test.ts +0 -158
- package/src/store/pgOpenGuard.test.ts +0 -89
- package/src/store/phase01.test.ts +0 -88
- package/src/store/phase04.test.ts +0 -58
- package/src/store/sprint10.test.ts +0 -214
- package/src/store/sqlite/game-achievements.test.ts +0 -80
- package/src/store/sqlite/game-scores.test.ts +0 -132
- package/src/store/sqlite/game-state.test.ts +0 -89
- package/src/store/sqlite/global-index.test.ts +0 -222
- package/src/store/sqlite/mechanical-fix.test.ts +0 -162
- package/src/store/sqlite/perf-samples.test.ts +0 -169
- package/src/store/sqlite/raptor.test.ts +0 -139
- package/src/store/sqlite/turns.test.ts +0 -218
- package/src/store/sqlite.cachehit.test.ts +0 -68
- package/src/store/sqlite.dbmaint.test.ts +0 -193
- package/src/store/sqlite.dbmirror.test.ts +0 -229
- package/src/store/turns/contract-compliance.test.ts +0 -562
- package/src/store/turns/memory-store.test.ts +0 -15
- package/src/store/turns/migrations.test.ts +0 -161
- package/src/store/turns/sqlite-store.test.ts +0 -161
- package/src/store/vectorIndex.test.ts +0 -140
- package/src/store.test.ts +0 -191
- package/src/supersede.test.ts +0 -42
- package/src/topics/cluster.test.ts +0 -176
- package/src/topics/store.test.ts +0 -132
- package/src/types.test.ts +0 -10
- package/src/vector-search-cache.test.ts +0 -190
- package/src/vectorStore.test.ts +0 -533
- package/src/wiki.test.ts +0 -217
|
@@ -24,6 +24,9 @@ export function resetRuntimeImpl(self, sessionId) {
|
|
|
24
24
|
dedupAttempts: 0,
|
|
25
25
|
tokensSaved: 0,
|
|
26
26
|
lastCompactAt: null,
|
|
27
|
+
lastRecallAt: null,
|
|
28
|
+
lastInjectAt: null,
|
|
29
|
+
_prevCacheHitPct: null,
|
|
27
30
|
lastNativeCompactAt: null,
|
|
28
31
|
compactCount: 0,
|
|
29
32
|
recallInjections: 0,
|
|
@@ -38,8 +41,11 @@ export function resetRuntimeImpl(self, sessionId) {
|
|
|
38
41
|
lastErrorText: undefined,
|
|
39
42
|
errorTextRepeatCount: 0,
|
|
40
43
|
poisonedAdviseSent: false,
|
|
44
|
+
providerOutageAdvised: false,
|
|
41
45
|
poisonedCompactSignatures: new Set(),
|
|
46
|
+
recallInjectedThisTurn: false,
|
|
42
47
|
poisonedCount: 0,
|
|
48
|
+
extensionInitiatedTurn: false,
|
|
43
49
|
};
|
|
44
50
|
self.trimCache = null; // v0.8.6: never replay a stale trim into a new session
|
|
45
51
|
self.statusKey = undefined;
|
|
@@ -49,6 +49,9 @@ export class MegaRuntime {
|
|
|
49
49
|
dedupAttempts: 0,
|
|
50
50
|
tokensSaved: 0,
|
|
51
51
|
lastCompactAt: null,
|
|
52
|
+
lastRecallAt: null,
|
|
53
|
+
lastInjectAt: null,
|
|
54
|
+
_prevCacheHitPct: null,
|
|
52
55
|
lastNativeCompactAt: null,
|
|
53
56
|
compactCount: 0,
|
|
54
57
|
recallInjections: 0,
|
|
@@ -63,8 +66,12 @@ export class MegaRuntime {
|
|
|
63
66
|
lastErrorText: undefined,
|
|
64
67
|
errorTextRepeatCount: 0,
|
|
65
68
|
poisonedAdviseSent: false,
|
|
69
|
+
providerOutageAdvised: false,
|
|
66
70
|
poisonedCompactSignatures: new Set(),
|
|
71
|
+
// S53: tracks whether withRecallTail injected this turn (consumed at turn_end).
|
|
72
|
+
recallInjectedThisTurn: false,
|
|
67
73
|
poisonedCount: 0,
|
|
74
|
+
extensionInitiatedTurn: false,
|
|
68
75
|
};
|
|
69
76
|
// v0.8.6 cache-stability: the cached live-trim view for the current
|
|
70
77
|
// compaction epoch. Set after a fresh runCompact + computeLiveTrimCut, and
|
package/dist/src/embedder.js
CHANGED
|
@@ -49,20 +49,60 @@ function fnv1a(str) {
|
|
|
49
49
|
* Default embedder: character 3-gram bag-of-counts, hashed into a fixed-dim
|
|
50
50
|
* vector, L2-normalized. Captures local lexical/structure overlap well enough
|
|
51
51
|
* for checkpoint relevance ranking.
|
|
52
|
+
*
|
|
53
|
+
* S53B: includes a 256-entry (configurable) FIFO cache keyed by FNV-1a(text).
|
|
54
|
+
* Each cache entry holds a DEFENSIVE COPY of the embedding to prevent mutation
|
|
55
|
+
* from corrupting stored vectors. Set MEGACOMPACT_EMBED_CACHE=0 to disable.
|
|
52
56
|
*/
|
|
53
57
|
export class TrigramEmbedder {
|
|
54
58
|
dim;
|
|
55
59
|
seed;
|
|
60
|
+
/** @type {Map<string, Vector>} */
|
|
61
|
+
_cache;
|
|
62
|
+
_cacheSize;
|
|
63
|
+
_hits = 0;
|
|
64
|
+
_misses = 0;
|
|
56
65
|
constructor(dim = 512, seed = 0x9e3779b9) {
|
|
57
66
|
this.dim = dim;
|
|
58
67
|
this.seed = seed >>> 0;
|
|
68
|
+
const cap = Number(process.env.MEGACOMPACT_EMBED_CACHE ?? "256");
|
|
69
|
+
this._cacheSize = cap > 0 ? Math.floor(cap) : 0;
|
|
70
|
+
this._cache = new Map();
|
|
71
|
+
}
|
|
72
|
+
/** Cache statistics — always { hits: 0, misses: 0 } when cache is disabled. */
|
|
73
|
+
getEmbedCacheStats() {
|
|
74
|
+
return { hits: this._hits, misses: this._misses };
|
|
59
75
|
}
|
|
60
76
|
embed(text) {
|
|
77
|
+
if (this._cacheSize === 0) {
|
|
78
|
+
return this._embedRaw(text);
|
|
79
|
+
}
|
|
80
|
+
const key = fnv1a(text).toString(36);
|
|
81
|
+
const cached = this._cache.get(key);
|
|
82
|
+
if (cached !== undefined) {
|
|
83
|
+
this._hits++;
|
|
84
|
+
// Defensive copy: caller mutating the returned vector must not corrupt cache.
|
|
85
|
+
return cached.slice();
|
|
86
|
+
}
|
|
87
|
+
this._misses++;
|
|
88
|
+
const vec = this._embedRaw(text);
|
|
89
|
+
// Store a defensive copy (slice) so caller mutation cannot corrupt stored value.
|
|
90
|
+
this._cache.set(key, vec.slice());
|
|
91
|
+
// FIFO eviction: remove the oldest entry when at capacity.
|
|
92
|
+
if (this._cache.size > this._cacheSize) {
|
|
93
|
+
// Map insertion order is guaranteed — first key is the oldest.
|
|
94
|
+
const firstKey = this._cache.keys().next().value;
|
|
95
|
+
if (firstKey !== undefined)
|
|
96
|
+
this._cache.delete(firstKey);
|
|
97
|
+
}
|
|
98
|
+
return vec;
|
|
99
|
+
}
|
|
100
|
+
/** Raw embed computation (no cache logic). Public so tests can bypass cache. */
|
|
101
|
+
_embedRaw(text) {
|
|
61
102
|
const vec = new Array(this.dim).fill(0);
|
|
62
103
|
const norm = text.toLowerCase().replace(/\s+/g, " ");
|
|
63
104
|
if (norm.length === 0)
|
|
64
105
|
return l2Normalize(vec);
|
|
65
|
-
// Whole-string + word + char-trigram signals.
|
|
66
106
|
vec[fnv1a(norm) % this.dim] += 1;
|
|
67
107
|
for (const word of norm.split(" ")) {
|
|
68
108
|
if (word.length === 0)
|
|
@@ -74,7 +114,6 @@ export class TrigramEmbedder {
|
|
|
74
114
|
vec[idx] += 1;
|
|
75
115
|
}
|
|
76
116
|
}
|
|
77
|
-
// Edge: very short tokens still get a slot.
|
|
78
117
|
if (norm.length < 3)
|
|
79
118
|
vec[fnv1a(norm) % this.dim] += 1;
|
|
80
119
|
return l2Normalize(vec);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* memoryStats.ts — aggregate memory-store statistics (S53B).
|
|
3
|
+
*
|
|
4
|
+
* Reads from the durable SQLite memories table to produce:
|
|
5
|
+
* - totalMemories: all memories in the store
|
|
6
|
+
* - memoriesInLast30Days: memories created within the last 30 days
|
|
7
|
+
* - topStableMemories: top-N memories by recall count (desc)
|
|
8
|
+
* - avgRecallScore: mean recallCount across all memories
|
|
9
|
+
*
|
|
10
|
+
* recallCount = 1 if last_recalled_at IS NOT NULL, else 0.
|
|
11
|
+
* This is the durable signal from recallMemory() — a memory that was
|
|
12
|
+
* recalled at least once is "stable" (still relevant). The exact recall
|
|
13
|
+
* frequency is tracked via turn_recall rows (source='memory') for
|
|
14
|
+
* cross-session analysis, but the memories table provides a reliable
|
|
15
|
+
* on-store signal that is always present without a schema migration.
|
|
16
|
+
*
|
|
17
|
+
* Real-store only (PREVENT-PI-004). No mocks. No network.
|
|
18
|
+
*/
|
|
19
|
+
import { openStore } from "./store/sqlite/utils.js";
|
|
20
|
+
/**
|
|
21
|
+
* Aggregate memory statistics from the SQLite store.
|
|
22
|
+
* Always returns a valid MemoryStatsResult; never throws.
|
|
23
|
+
*/
|
|
24
|
+
export async function memoryStats(stateDir, opts = {}) {
|
|
25
|
+
const topN = opts.topN ?? 5;
|
|
26
|
+
try {
|
|
27
|
+
const db = openStore(stateDir);
|
|
28
|
+
// ── totalMemories ──────────────────────────────────────────────────────
|
|
29
|
+
const totalRow = db
|
|
30
|
+
.prepare("SELECT COUNT(*) AS n FROM memories")
|
|
31
|
+
.get();
|
|
32
|
+
const totalMemories = totalRow.n;
|
|
33
|
+
// ── memoriesInLast30Days ───────────────────────────────────────────────
|
|
34
|
+
const thirtyDaysAgo = Math.floor(Date.now() / 1000) - 30 * 86_400;
|
|
35
|
+
const recentRow = db
|
|
36
|
+
.prepare("SELECT COUNT(*) AS n FROM memories WHERE created_at >= ?")
|
|
37
|
+
.get(thirtyDaysAgo);
|
|
38
|
+
const memoriesInLast30Days = recentRow.n;
|
|
39
|
+
// ── topStableMemories + avgRecallScore ─────────────────────────────────
|
|
40
|
+
// recallCount: 1 if last_recalled_at IS NOT NULL (recalled at least once),
|
|
41
|
+
// 0 otherwise. Stable memories score high.
|
|
42
|
+
const topRows = db
|
|
43
|
+
.prepare(`SELECT
|
|
44
|
+
m.id,
|
|
45
|
+
m.content AS text,
|
|
46
|
+
m.last_recalled_at AS lastRecalledAt,
|
|
47
|
+
CASE WHEN m.last_recalled_at IS NOT NULL THEN 1 ELSE 0 END AS recallCount
|
|
48
|
+
FROM memories m
|
|
49
|
+
ORDER BY recallCount DESC, m.id ASC
|
|
50
|
+
LIMIT ?`)
|
|
51
|
+
.all(topN);
|
|
52
|
+
// avgRecallScore = (count of recalled memories) / totalMemories.
|
|
53
|
+
// This represents the fraction of memories that are "stable" (recalled at least once).
|
|
54
|
+
const recalledRow = db
|
|
55
|
+
.prepare("SELECT COUNT(*) AS n FROM memories WHERE last_recalled_at IS NOT NULL")
|
|
56
|
+
.get();
|
|
57
|
+
const avgRecallScore = totalMemories > 0 ? recalledRow.n / totalMemories : 0;
|
|
58
|
+
const topStableMemories = topRows.map((r) => ({
|
|
59
|
+
id: r.id,
|
|
60
|
+
text: r.text ?? "",
|
|
61
|
+
recallCount: r.recallCount ?? 0,
|
|
62
|
+
lastRecalledAt: r.lastRecalledAt ?? null,
|
|
63
|
+
}));
|
|
64
|
+
return { totalMemories, memoriesInLast30Days, topStableMemories, avgRecallScore };
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// Non-fatal: return zeros rather than propagating an error.
|
|
68
|
+
return {
|
|
69
|
+
totalMemories: 0,
|
|
70
|
+
memoriesInLast30Days: 0,
|
|
71
|
+
topStableMemories: [],
|
|
72
|
+
avgRecallScore: 0,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* prefix-break.ts — S53A prefix-break classification.
|
|
3
|
+
*
|
|
4
|
+
* Classifies WHY the provider cache prefix broke on a given turn by comparing
|
|
5
|
+
* the break timestamp against the three candidate event timestamps with a
|
|
6
|
+
* configurable tolerance window.
|
|
7
|
+
*
|
|
8
|
+
* The classification is best-effort (non-fatal): null timestamps are absent,
|
|
9
|
+
* and a break outside all tolerance windows is classified as 'other'.
|
|
10
|
+
*
|
|
11
|
+
* Pi-agnostic: no pi runtime types, no network.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Classify why the provider cache prefix broke at `breakTimestamp`.
|
|
15
|
+
*
|
|
16
|
+
* @param breakTimestamp Wall-clock ms of the cache hit % drop sample.
|
|
17
|
+
* @param events Recent event timestamps from the runtime.
|
|
18
|
+
* @param toleranceMs Max ms gap to consider a match (default 2000).
|
|
19
|
+
* @returns Classification result with cause + confidence.
|
|
20
|
+
*
|
|
21
|
+
* Priority: recall > compaction > inject. Confidence = 1 − (gap / (2·tolerance)),
|
|
22
|
+
* capped at [0.5, 1.0]. Returns 'other' when no event is within tolerance.
|
|
23
|
+
*/
|
|
24
|
+
export function classifyPrefixBreak(breakTimestamp, events, toleranceMs = 2000) {
|
|
25
|
+
// Ordered by priority (recall wins if multiple match).
|
|
26
|
+
const candidates = [
|
|
27
|
+
{ cause: "recall", ts: events.lastRecallAt },
|
|
28
|
+
{ cause: "compaction", ts: events.lastCompactAt },
|
|
29
|
+
{ cause: "inject", ts: events.lastInjectAt },
|
|
30
|
+
];
|
|
31
|
+
for (const { cause, ts } of candidates) {
|
|
32
|
+
if (ts == null)
|
|
33
|
+
continue;
|
|
34
|
+
const gap = Math.abs(breakTimestamp - ts);
|
|
35
|
+
if (gap <= toleranceMs) {
|
|
36
|
+
// confidence = 1 − (gap / (2·tolerance)) ∈ [0.5, 1.0]
|
|
37
|
+
// exact match (gap=0) → 1.0; at boundary (gap=tolerance) → 0.5
|
|
38
|
+
const confidence = Math.max(0.5, 1 - gap / (2 * toleranceMs));
|
|
39
|
+
return { cause, confidence };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { cause: "other", confidence: 0 };
|
|
43
|
+
}
|
package/dist/src/pricing.js
CHANGED
|
@@ -21,6 +21,7 @@ export const PERF_KINDS = [
|
|
|
21
21
|
"provider_latency_ms",
|
|
22
22
|
"tps",
|
|
23
23
|
"cache_hit_pct",
|
|
24
|
+
"prefix_break",
|
|
24
25
|
"rss_mb",
|
|
25
26
|
"heap_mb",
|
|
26
27
|
"cpu_user_ms",
|
|
@@ -80,20 +81,39 @@ export function readPerfSamples(stateDir = getStateDir(), sinceTs = 0, kind) {
|
|
|
80
81
|
return out;
|
|
81
82
|
}
|
|
82
83
|
/**
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* Aggregates `cache_creation_input_tokens` / `cache_read_input_tokens` from
|
|
86
|
-
* the `meta` JSON column of `cache_hit_pct` samples (`json_extract`,
|
|
87
|
-
* available in node:sqlite ≥22.13 / SQLite ≥3.38). Returns a zeroed
|
|
88
|
-
* `ProviderCacheLifetime` when no samples exist (never undefined).
|
|
84
|
+
* Extract a model label from parsed meta (modelName or modelId), or null.
|
|
89
85
|
*/
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
86
|
+
function extractModelLabel(meta) {
|
|
87
|
+
if (typeof meta.modelName === "string" && meta.modelName.length > 0)
|
|
88
|
+
return meta.modelName;
|
|
89
|
+
if (typeof meta.modelId === "string" && meta.modelId.length > 0)
|
|
90
|
+
return meta.modelId;
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Build sorted byModel array from the model aggregation map.
|
|
95
|
+
*/
|
|
96
|
+
function byModelFromMap(map) {
|
|
97
|
+
const out = [];
|
|
98
|
+
for (const [model, m] of map) {
|
|
99
|
+
out.push({
|
|
100
|
+
model,
|
|
101
|
+
hitPct: m.hitCount > 0 ? m.sumHitPct / m.hitCount : 0,
|
|
102
|
+
totalCacheRead: m.sumCr,
|
|
103
|
+
totalCacheWrite: m.sumCw,
|
|
104
|
+
sampleCount: m.samples,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
out.sort((a, b) => b.sampleCount - a.sampleCount);
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Aggregate `cache_hit_pct` rows (ts + parsed meta) into a
|
|
112
|
+
* `ProviderCacheLifetime`. Shared by the lifetime and window readers so both
|
|
113
|
+
* use the identical aggregation formula. `meta` is parsed defensively
|
|
114
|
+
* (PREVENT-001 null-safe). Rows must be ascending by ts.
|
|
115
|
+
*/
|
|
116
|
+
function aggregateCacheRows(rows) {
|
|
97
117
|
if (rows.length === 0) {
|
|
98
118
|
return {
|
|
99
119
|
sampleCount: 0,
|
|
@@ -103,6 +123,7 @@ export function readProviderCacheLifetime(stateDir = getStateDir()) {
|
|
|
103
123
|
totalInput: 0,
|
|
104
124
|
firstSampleAt: null,
|
|
105
125
|
latestSampleAt: null,
|
|
126
|
+
byModel: [],
|
|
106
127
|
};
|
|
107
128
|
}
|
|
108
129
|
let totalCacheRead = 0;
|
|
@@ -110,6 +131,7 @@ export function readProviderCacheLifetime(stateDir = getStateDir()) {
|
|
|
110
131
|
let totalInput = 0;
|
|
111
132
|
let sumHitPct = 0;
|
|
112
133
|
let hitPctCount = 0;
|
|
134
|
+
const modelMap = new Map();
|
|
113
135
|
for (const r of rows) {
|
|
114
136
|
if (r.meta != null) {
|
|
115
137
|
let meta = null;
|
|
@@ -140,6 +162,31 @@ export function readProviderCacheLifetime(stateDir = getStateDir()) {
|
|
|
140
162
|
sumHitPct += (cr / denom) * 100;
|
|
141
163
|
hitPctCount++;
|
|
142
164
|
}
|
|
165
|
+
// Per-model grouping (F4)
|
|
166
|
+
const modelLabel = extractModelLabel(meta);
|
|
167
|
+
if (modelLabel != null) {
|
|
168
|
+
let m = modelMap.get(modelLabel);
|
|
169
|
+
if (!m) {
|
|
170
|
+
m = {
|
|
171
|
+
sumCr: 0,
|
|
172
|
+
sumCw: 0,
|
|
173
|
+
sumInp: 0,
|
|
174
|
+
sumHitPct: 0,
|
|
175
|
+
hitCount: 0,
|
|
176
|
+
samples: 0,
|
|
177
|
+
};
|
|
178
|
+
modelMap.set(modelLabel, m);
|
|
179
|
+
}
|
|
180
|
+
m.sumCr += cr;
|
|
181
|
+
m.sumCw += cw;
|
|
182
|
+
m.sumInp += inp;
|
|
183
|
+
m.samples++;
|
|
184
|
+
if (denom > 0) {
|
|
185
|
+
m.sumHitPct += (cr / denom) * 100;
|
|
186
|
+
m.hitCount++;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Untagged samples are omitted from byModel
|
|
143
190
|
}
|
|
144
191
|
}
|
|
145
192
|
}
|
|
@@ -151,8 +198,43 @@ export function readProviderCacheLifetime(stateDir = getStateDir()) {
|
|
|
151
198
|
totalInput,
|
|
152
199
|
firstSampleAt: new Date(rows[0].ts).toISOString(),
|
|
153
200
|
latestSampleAt: new Date(rows[rows.length - 1].ts).toISOString(),
|
|
201
|
+
byModel: byModelFromMap(modelMap),
|
|
154
202
|
};
|
|
155
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Read lifetime provider prompt cache aggregates from `perf_samples`.
|
|
206
|
+
*
|
|
207
|
+
* Aggregates `cache_creation_input_tokens` / `cache_read_input_tokens` from
|
|
208
|
+
* the `meta` JSON column of `cache_hit_pct` samples (`json_extract`,
|
|
209
|
+
* available in node:sqlite >=22.13 / SQLite >=3.38). Returns a zeroed
|
|
210
|
+
* `ProviderCacheLifetime` when no samples exist (never undefined).
|
|
211
|
+
*/
|
|
212
|
+
export function readProviderCacheLifetime(stateDir = getStateDir()) {
|
|
213
|
+
const db = openStore(stateDir);
|
|
214
|
+
const rows = db
|
|
215
|
+
.prepare(`SELECT ts, meta FROM perf_samples
|
|
216
|
+
WHERE kind = ?
|
|
217
|
+
ORDER BY ts ASC`)
|
|
218
|
+
.all("cache_hit_pct");
|
|
219
|
+
return aggregateCacheRows(rows);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Read provider prompt cache aggregates over a trailing window of `minutes`
|
|
223
|
+
* (samples with ts >= now - minutes*60_000). Same aggregate shape as
|
|
224
|
+
* `readProviderCacheLifetime`; the existing lifetime reader is untouched.
|
|
225
|
+
* Returns a zeroed `ProviderCacheLifetime` when no samples fall in the window.
|
|
226
|
+
*/
|
|
227
|
+
export function readProviderCacheWindow(stateDir = getStateDir(), minutes) {
|
|
228
|
+
const db = openStore(stateDir);
|
|
229
|
+
const sinceTs = Date.now() - minutes * 60_000;
|
|
230
|
+
const rows = db
|
|
231
|
+
.prepare(`SELECT ts, meta FROM perf_samples
|
|
232
|
+
WHERE kind = ? AND ts >= ?
|
|
233
|
+
ORDER BY ts ASC`)
|
|
234
|
+
.all("cache_hit_pct", sinceTs);
|
|
235
|
+
return aggregateCacheRows(rows);
|
|
236
|
+
}
|
|
237
|
+
// ... keep every function below unchanged ...
|
|
156
238
|
/**
|
|
157
239
|
* Read the most recent `cache_hit_pct` value from `perf_samples`.
|
|
158
240
|
*
|
|
@@ -164,9 +246,9 @@ export function readLatestCacheHitPct(stateDir = getStateDir()) {
|
|
|
164
246
|
const db = openStore(stateDir);
|
|
165
247
|
const row = db
|
|
166
248
|
.prepare(`SELECT value FROM perf_samples
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
249
|
+
WHERE kind = ?
|
|
250
|
+
ORDER BY ts DESC, id DESC
|
|
251
|
+
LIMIT 1`)
|
|
170
252
|
.get("cache_hit_pct");
|
|
171
253
|
if (!row || typeof row.value !== "number" || !Number.isFinite(row.value))
|
|
172
254
|
return 0;
|
|
@@ -183,3 +265,49 @@ export function readLatestCacheHitPct(stateDir = getStateDir()) {
|
|
|
183
265
|
export function readProviderCacheForRepo(stateDir) {
|
|
184
266
|
return readProviderCacheLifetime(stateDir);
|
|
185
267
|
}
|
|
268
|
+
/**
|
|
269
|
+
* Read `prefix_break` samples in a time window.
|
|
270
|
+
*
|
|
271
|
+
* @param stateDir State directory for this repo.
|
|
272
|
+
* @param sinceTs Lower bound (epoch ms). Pass 0 for no lower bound.
|
|
273
|
+
* @param untilTs Upper bound (epoch ms). Pass 0 for no upper bound.
|
|
274
|
+
* Rows with ts > untilTs are excluded.
|
|
275
|
+
* @returns Sorted ascending array of PrefixBreak rows.
|
|
276
|
+
*
|
|
277
|
+
* SQL is fully parameterized (PREVENT-002). `meta` is parsed defensively
|
|
278
|
+
* (PREVENT-001 null-safe). When no rows exist the return is an empty array.
|
|
279
|
+
*/
|
|
280
|
+
export function readPrefixBreaks(stateDir = getStateDir(), sinceTs = 0, untilTs = 0) {
|
|
281
|
+
const db = openStore(stateDir);
|
|
282
|
+
let rows;
|
|
283
|
+
if (untilTs > 0) {
|
|
284
|
+
const sql = `SELECT id, ts, value, meta FROM perf_samples
|
|
285
|
+
WHERE kind = ? AND ts >= ? AND ts <= ? ORDER BY ts ASC`;
|
|
286
|
+
rows = db.prepare(sql).all("prefix_break", sinceTs, untilTs);
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
const sql = `SELECT id, ts, value, meta FROM perf_samples
|
|
290
|
+
WHERE kind = ? AND ts >= ? ORDER BY ts ASC`;
|
|
291
|
+
rows = db.prepare(sql).all("prefix_break", sinceTs);
|
|
292
|
+
}
|
|
293
|
+
const out = [];
|
|
294
|
+
for (const r of rows) {
|
|
295
|
+
let meta = null;
|
|
296
|
+
if (r.meta != null) {
|
|
297
|
+
try {
|
|
298
|
+
meta = JSON.parse(r.meta);
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
meta = null;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
out.push({
|
|
305
|
+
id: r.id,
|
|
306
|
+
ts: r.ts,
|
|
307
|
+
kind: "prefix_break",
|
|
308
|
+
value: r.value,
|
|
309
|
+
meta: meta,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
return out;
|
|
313
|
+
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* PREVENT-PI-004: no network. PREVENT-002: no SQL here.
|
|
8
8
|
*/
|
|
9
9
|
import { randomBytes } from "node:crypto";
|
|
10
|
+
import { DuplicateTurnError, } from "./types.js";
|
|
10
11
|
function newConversationId() {
|
|
11
12
|
return `conv_${randomBytes(8).toString("hex")}`;
|
|
12
13
|
}
|
|
@@ -134,6 +135,13 @@ export class InMemoryTurnStore {
|
|
|
134
135
|
}
|
|
135
136
|
// ── TurnWriter ──────────────────────────────────────────────
|
|
136
137
|
appendTurn(entry) {
|
|
138
|
+
// Check for duplicate (conversationId, turnIndex)
|
|
139
|
+
for (const [, row] of this.turns) {
|
|
140
|
+
if (row.entry.conversationId === entry.conversationId &&
|
|
141
|
+
row.entry.turnIndex === entry.turnIndex) {
|
|
142
|
+
throw new DuplicateTurnError(entry.conversationId, entry.turnIndex);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
137
145
|
const id = allocId();
|
|
138
146
|
const sid = normalizeSessionId(entry.sessionId);
|
|
139
147
|
const normalized = { ...entry, sessionId: sid };
|
|
@@ -13,6 +13,7 @@ import { randomBytes } from "node:crypto";
|
|
|
13
13
|
import { statSync } from "node:fs";
|
|
14
14
|
import { join } from "node:path";
|
|
15
15
|
import { openTurnDb, closeTurnDb } from "./connection.js";
|
|
16
|
+
import { DuplicateTurnError } from "./types.js";
|
|
16
17
|
// ─── Helpers ──────────────────────────────────────────────────────
|
|
17
18
|
function newConversationId() {
|
|
18
19
|
return `conv_${randomBytes(8).toString("hex")}`;
|
|
@@ -172,11 +173,21 @@ export class SqliteTurnStore {
|
|
|
172
173
|
appendTurn(entry) {
|
|
173
174
|
const sid = normalizeSessionId(entry.sessionId);
|
|
174
175
|
this.persistSessionConv(sid, entry.conversationId);
|
|
175
|
-
|
|
176
|
-
.
|
|
176
|
+
try {
|
|
177
|
+
this.db
|
|
178
|
+
.prepare(`INSERT INTO turns (conversation_id, session_id, turn_index, role, ended_at,
|
|
177
179
|
ctx_tokens, ctx_percent, pressure_band, model)
|
|
178
180
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
179
|
-
|
|
181
|
+
.run(entry.conversationId, sid, entry.turnIndex, entry.role, entry.endedAt, entry.ctxTokens ?? null, entry.ctxPercent ?? null, entry.pressureBand ?? null, entry.model ?? null);
|
|
182
|
+
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
if (e instanceof Error &&
|
|
185
|
+
e.message.includes("UNIQUE constraint") &&
|
|
186
|
+
e.code === "ERR_SQLITE_ERROR") {
|
|
187
|
+
throw new DuplicateTurnError(entry.conversationId, entry.turnIndex);
|
|
188
|
+
}
|
|
189
|
+
throw e;
|
|
190
|
+
}
|
|
180
191
|
const row = this.db
|
|
181
192
|
.prepare("SELECT id FROM turns WHERE conversation_id = ? AND turn_index = ?")
|
|
182
193
|
.get(entry.conversationId, entry.turnIndex);
|
|
@@ -13,4 +13,21 @@
|
|
|
13
13
|
*
|
|
14
14
|
* PREVENT-PI-004: zero network. PREVENT-002: no SQL here (private to impls).
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
// ─── Domain types ───────────────────────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* Thrown when appendTurn is called with a (conversationId, turnIndex) pair
|
|
19
|
+
* that already exists. Both backends must throw this in lieu of a raw
|
|
20
|
+
* SQLite error (SqliteTurnStore) or silent success (InMemoryTurnStore).
|
|
21
|
+
*/
|
|
22
|
+
export class DuplicateTurnError extends Error {
|
|
23
|
+
/** The conversation that already has this turn index. */
|
|
24
|
+
conversationId;
|
|
25
|
+
/** The duplicate turn index. */
|
|
26
|
+
turnIndex;
|
|
27
|
+
constructor(conversationId, turnIndex) {
|
|
28
|
+
super(`Duplicate turn: conversation "${conversationId}" already has turnIndex ${turnIndex}`);
|
|
29
|
+
this.name = "DuplicateTurnError";
|
|
30
|
+
this.conversationId = conversationId;
|
|
31
|
+
this.turnIndex = turnIndex;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -241,14 +241,35 @@ export async function searchAsync(query, opts = {}) {
|
|
|
241
241
|
return [];
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
|
+
/** Default cap for the WASM close (PGlite can stall ~40 min on teardown). */
|
|
245
|
+
export const DEFAULT_CLOSE_TIMEOUT_MS = 5_000;
|
|
246
|
+
function closeTimeoutMs() {
|
|
247
|
+
const raw = process.env.MEGACOMPACT_PGLITE_CLOSE_TIMEOUT_MS;
|
|
248
|
+
if (raw) {
|
|
249
|
+
const n = Number(raw);
|
|
250
|
+
if (Number.isFinite(n) && n > 0)
|
|
251
|
+
return n;
|
|
252
|
+
}
|
|
253
|
+
return DEFAULT_CLOSE_TIMEOUT_MS;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Race a promise against a timeout. If the inner promise never settles, the
|
|
257
|
+
* timer fires, resolves with `undefined`, and the caller proceeds. This mirrors
|
|
258
|
+
* `withOpenTimeout` (pgOpenGuard.ts) but is local to the close path.
|
|
259
|
+
*/
|
|
260
|
+
function withCloseTimeout(promise, timeoutMs) {
|
|
261
|
+
return Promise.race([
|
|
262
|
+
promise,
|
|
263
|
+
new Promise((resolve) => setTimeout(() => resolve(undefined), timeoutMs)),
|
|
264
|
+
]);
|
|
265
|
+
}
|
|
244
266
|
/** Close the index (test teardown / shutdown). Safe to call when unopened. */
|
|
245
267
|
export async function closeVectorIndex() {
|
|
246
268
|
if (db) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
/* ignore */
|
|
269
|
+
const closePromise = db.close().catch(() => { });
|
|
270
|
+
const result = await withCloseTimeout(closePromise, closeTimeoutMs());
|
|
271
|
+
if (result === undefined) {
|
|
272
|
+
logWarn(`close timed out after ${closeTimeoutMs()}ms (WASM teardown stalled; OS will reclaim)`);
|
|
252
273
|
}
|
|
253
274
|
}
|
|
254
275
|
db = undefined;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{r as o,j as n}from"./index-
|
|
2
|
-
//# sourceMappingURL=AchievementTiles-
|
|
1
|
+
import{r as o,j as n}from"./index-4gQ7auad.js";function x(s){try{return new Date(s*1e3).toLocaleString()}catch{return"—"}}function f({achievements:s}){const i=o.useRef(0),[r,a]=o.useState(null),l=s.reduce((e,t)=>Math.max(e,t.unlocked_at??0),0),c=i.current;o.useEffect(()=>{l>i.current&&(i.current=l)},[l]),o.useEffect(()=>{if(c>0&&l>c){const e=s.filter(t=>t.unlocked_at!=null&&(t.unlocked_at??0)>c);if(e.length>0){const t=e.map(d=>`${d.icon??""} ${d.title}`.trim()).join(", ")+" unlocked!";a(t);const h=setTimeout(()=>a(null),4e3);return()=>clearTimeout(h)}}},[s,l,c]);const u=s.filter(e=>!(e.hidden===1&&e.unlocked_at==null));return n.jsxs("div",{className:"ach-section",children:[r!=null&&n.jsx("div",{className:"ach-toast show",children:r}),n.jsxs("div",{className:"ach-tiles",children:[u.length===0&&n.jsx("span",{className:"repo-none",children:"no achievements yet"}),u.map(e=>{if(e.unlocked_at!=null){const t=c>0&&(e.unlocked_at??0)>c;return n.jsxs("div",{className:`ach-tile unlocked${t?" just-unlocked":""}`,children:[e.icon??""," ",e.title,n.jsxs("span",{className:"ach-detail",children:["unlocked ",x(e.unlocked_at)]})]},e.id)}return n.jsxs("div",{className:"ach-tile locked",children:["??? ",e.title]},e.id)})]})]})}export{f as A};
|
|
2
|
+
//# sourceMappingURL=AchievementTiles-BoL7qBOJ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AchievementTiles-
|
|
1
|
+
{"version":3,"file":"AchievementTiles-BoL7qBOJ.js","sources":["../../src/components/AchievementTiles.tsx"],"sourcesContent":["/**\n * dashboard-client/src/components/AchievementTiles.tsx — shared achievement grid.\n *\n * Renders a grid of achievement tiles from fetchAchievements() data.\n * Tile states: unlocked (icon+title+date), visible-locked (??? teaser),\n * hidden+locked (not rendered). Just-unlocked tiles get a pulse animation.\n * Toast notification fires for newly-unlocked achievements.\n *\n * Used by both GameTab (sub-section) and AchievementsTab (standalone).\n */\n\nimport type React from \"react\";\nimport { useRef, useState, useEffect } from \"react\";\nimport type { AchievementRow } from \"@contracts\";\n\nexport interface AchievementTilesProps {\n\t/** Achievement rows from /api/achievements. */\n\tachievements: AchievementRow[];\n}\n\n/** Format unlock timestamp (seconds → locale string). */\nfunction fmtUnlockDate(ts: number): string {\n\ttry {\n\t\treturn new Date(ts * 1000).toLocaleString();\n\t} catch {\n\t\treturn \"\\u2014\";\n\t}\n}\n\nexport function AchievementTiles({\n\tachievements,\n}: AchievementTilesProps): React.ReactElement {\n\tconst lastMaxTsRef = useRef(0);\n\tconst [toastText, setToastText] = useState<string | null>(null);\n\n\tconst maxTs = achievements.reduce(\n\t\t(mx, a) => Math.max(mx, a.unlocked_at ?? 0),\n\t\t0,\n\t);\n\tconst prevMaxTs = lastMaxTsRef.current;\n\n\t/* Update ref after render so next poll sees previous baseline. */\n\tuseEffect(() => {\n\t\tif (maxTs > lastMaxTsRef.current) {\n\t\t\tlastMaxTsRef.current = maxTs;\n\t\t}\n\t}, [maxTs]);\n\n\t/* Toast for newly-unlocked achievements. */\n\tuseEffect(() => {\n\t\tif (prevMaxTs > 0 && maxTs > prevMaxTs) {\n\t\t\tconst newly = achievements.filter(\n\t\t\t\t(a) => a.unlocked_at != null && (a.unlocked_at ?? 0) > prevMaxTs,\n\t\t\t);\n\t\t\tif (newly.length > 0) {\n\t\t\t\tconst text =\n\t\t\t\t\tnewly\n\t\t\t\t\t\t.map((a) => `${a.icon ?? \"\"} ${a.title}`.trim())\n\t\t\t\t\t\t.join(\", \") + \" unlocked!\";\n\t\t\t\tsetToastText(text);\n\t\t\t\tconst t = setTimeout(() => setToastText(null), 4000);\n\t\t\t\treturn (): void => clearTimeout(t);\n\t\t\t}\n\t\t}\n\t}, [achievements, maxTs, prevMaxTs]);\n\n\t/* Visible tiles: skip hidden+locked (hidden === 1 && unlocked_at == null). */\n\tconst tiles = achievements.filter(\n\t\t(a) => !(a.hidden === 1 && a.unlocked_at == null),\n\t);\n\n\treturn (\n\t\t<div className=\"ach-section\">\n\t\t\t{toastText != null && (\n\t\t\t\t<div className=\"ach-toast show\">{toastText}</div>\n\t\t\t)}\n\t\t\t<div className=\"ach-tiles\">\n\t\t\t\t{tiles.length === 0 && (\n\t\t\t\t\t<span className=\"repo-none\">no achievements yet</span>\n\t\t\t\t)}\n\t\t\t\t{tiles.map((a) => {\n\t\t\t\t\tif (a.unlocked_at != null) {\n\t\t\t\t\t\tconst isNew = prevMaxTs > 0 && (a.unlocked_at ?? 0) > prevMaxTs;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={a.id}\n\t\t\t\t\t\t\t\tclassName={`ach-tile unlocked${isNew ? \" just-unlocked\" : \"\"}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{a.icon ?? \"\"} {a.title}\n\t\t\t\t\t\t\t\t<span className=\"ach-detail\">\n\t\t\t\t\t\t\t\t\tunlocked {fmtUnlockDate(a.unlocked_at)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<div key={a.id} className=\"ach-tile locked\">\n\t\t\t\t\t\t\t??? {a.title}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t);\n\t\t\t\t})}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n"],"names":["fmtUnlockDate","ts","AchievementTiles","achievements","lastMaxTsRef","useRef","toastText","setToastText","useState","maxTs","mx","a","prevMaxTs","useEffect","newly","text","t","tiles","jsxs","jsx","isNew"],"mappings":"+CAqBA,SAASA,EAAcC,EAAoB,CAC1C,GAAI,CACH,OAAO,IAAI,KAAKA,EAAK,GAAI,EAAE,eAAA,CAC5B,MAAQ,CACP,MAAO,GACR,CACD,CAEO,SAASC,EAAiB,CAChC,aAAAC,CACD,EAA8C,CAC7C,MAAMC,EAAeC,EAAAA,OAAO,CAAC,EACvB,CAACC,EAAWC,CAAY,EAAIC,EAAAA,SAAwB,IAAI,EAExDC,EAAQN,EAAa,OAC1B,CAACO,EAAIC,IAAM,KAAK,IAAID,EAAIC,EAAE,aAAe,CAAC,EAC1C,CAAA,EAEKC,EAAYR,EAAa,QAG/BS,EAAAA,UAAU,IAAM,CACXJ,EAAQL,EAAa,UACxBA,EAAa,QAAUK,EAEzB,EAAG,CAACA,CAAK,CAAC,EAGVI,EAAAA,UAAU,IAAM,CACf,GAAID,EAAY,GAAKH,EAAQG,EAAW,CACvC,MAAME,EAAQX,EAAa,OACzBQ,GAAMA,EAAE,aAAe,OAASA,EAAE,aAAe,GAAKC,CAAA,EAExD,GAAIE,EAAM,OAAS,EAAG,CACrB,MAAMC,EACLD,EACE,IAAKH,GAAM,GAAGA,EAAE,MAAQ,EAAE,IAAIA,EAAE,KAAK,GAAG,KAAA,CAAM,EAC9C,KAAK,IAAI,EAAI,aAChBJ,EAAaQ,CAAI,EACjB,MAAMC,EAAI,WAAW,IAAMT,EAAa,IAAI,EAAG,GAAI,EACnD,MAAO,IAAY,aAAaS,CAAC,CAClC,CACD,CACD,EAAG,CAACb,EAAcM,EAAOG,CAAS,CAAC,EAGnC,MAAMK,EAAQd,EAAa,OACzBQ,GAAM,EAAEA,EAAE,SAAW,GAAKA,EAAE,aAAe,KAAA,EAG7C,OACCO,EAAAA,KAAC,MAAA,CAAI,UAAU,cACb,SAAA,CAAAZ,GAAa,MACba,EAAAA,IAAC,MAAA,CAAI,UAAU,iBAAkB,SAAAb,EAAU,EAE5CY,EAAAA,KAAC,MAAA,CAAI,UAAU,YACb,SAAA,CAAAD,EAAM,SAAW,GACjBE,MAAC,OAAA,CAAK,UAAU,YAAY,SAAA,sBAAmB,EAE/CF,EAAM,IAAKN,GAAM,CACjB,GAAIA,EAAE,aAAe,KAAM,CAC1B,MAAMS,EAAQR,EAAY,IAAMD,EAAE,aAAe,GAAKC,EACtD,OACCM,EAAAA,KAAC,MAAA,CAEA,UAAW,oBAAoBE,EAAQ,iBAAmB,EAAE,GAE3D,SAAA,CAAAT,EAAE,MAAQ,GAAG,IAAEA,EAAE,MAClBO,EAAAA,KAAC,OAAA,CAAK,UAAU,aAAa,SAAA,CAAA,YAClBlB,EAAcW,EAAE,WAAW,CAAA,CAAA,CACtC,CAAA,CAAA,EANKA,EAAE,EAAA,CASV,CACA,OACCO,EAAAA,KAAC,MAAA,CAAe,UAAU,kBAAkB,SAAA,CAAA,OACtCP,EAAE,KAAA,CAAA,EADEA,EAAE,EAEZ,CAEF,CAAC,CAAA,CAAA,CACF,CAAA,EACD,CAEF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{r as t,l as m,j as s}from"./index-4gQ7auad.js";import{A as v}from"./AchievementTiles-BoL7qBOJ.js";function x(){const[r,o]=t.useState([]),[l,u]=t.useState(!0),[a,n]=t.useState(null),c=t.useCallback(async()=>{try{const e=await m();o(e),n(null)}catch(e){n(e instanceof Error?e:new Error(String(e)))}finally{u(!1)}},[]);return t.useEffect(()=>{let e=!0;const i=async()=>{e&&await c()};i();const h=setInterval(()=>void i(),15e3);return()=>{e=!1,clearInterval(h)}},[c]),l&&r.length===0?s.jsx("div",{className:"tab-stub",children:"Loading achievements…"}):a&&r.length===0?s.jsxs("div",{className:"tab-stub",children:["Error: ",a.message]}):s.jsxs("div",{className:"achievements-tab",children:[s.jsx("h2",{children:"Achievements"}),s.jsx(v,{achievements:r})]})}export{x as default};
|
|
2
|
+
//# sourceMappingURL=AchievementsTab-BFDE3HYc.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AchievementsTab-
|
|
1
|
+
{"version":3,"file":"AchievementsTab-BFDE3HYc.js","sources":["../../src/tabs/AchievementsTab.tsx"],"sourcesContent":["/**\n * dashboard-client/src/tabs/AchievementsTab.tsx — Achievements tab (NEW).\n *\n * Renders the shared AchievementTiles component standalone, plus a toast area.\n * Polls /api/achievements every 15s.\n */\n\nimport type React from \"react\";\nimport { useState, useEffect, useCallback } from \"react\";\nimport type { AchievementRow } from \"@contracts\";\nimport { fetchAchievements } from \"../api/client\";\nimport { AchievementTiles } from \"../components/AchievementTiles\";\n\nexport default function AchievementsTab(): React.ReactElement {\n\tconst [achievements, setAchievements] = useState<AchievementRow[]>([]);\n\tconst [loading, setLoading] = useState(true);\n\tconst [error, setError] = useState<Error | null>(null);\n\n\tconst doFetch = useCallback(async (): Promise<void> => {\n\t\ttry {\n\t\t\tconst rows = await fetchAchievements();\n\t\t\tsetAchievements(rows);\n\t\t\tsetError(null);\n\t\t} catch (e) {\n\t\t\tsetError(e instanceof Error ? e : new Error(String(e)));\n\t\t} finally {\n\t\t\tsetLoading(false);\n\t\t}\n\t}, []);\n\n\tuseEffect(() => {\n\t\tlet active = true;\n\t\tconst run = async (): Promise<void> => {\n\t\t\tif (active) await doFetch();\n\t\t};\n\t\tvoid run();\n\t\tconst timer = setInterval(() => void run(), 15000);\n\t\treturn () => {\n\t\t\tactive = false;\n\t\t\tclearInterval(timer);\n\t\t};\n\t}, [doFetch]);\n\n\tif (loading && achievements.length === 0)\n\t\treturn <div className=\"tab-stub\">Loading achievements…</div>;\n\tif (error && achievements.length === 0)\n\t\treturn <div className=\"tab-stub\">Error: {error.message}</div>;\n\n\treturn (\n\t\t<div className=\"achievements-tab\">\n\t\t\t<h2>Achievements</h2>\n\t\t\t<AchievementTiles achievements={achievements} />\n\t\t</div>\n\t);\n}\n"],"names":["AchievementsTab","achievements","setAchievements","useState","loading","setLoading","error","setError","doFetch","useCallback","rows","fetchAchievements","useEffect","active","run","timer","jsx","jsxs","AchievementTiles"],"mappings":"yGAaA,SAAwBA,GAAsC,CAC7D,KAAM,CAACC,EAAcC,CAAe,EAAIC,EAAAA,SAA2B,CAAA,CAAE,EAC/D,CAACC,EAASC,CAAU,EAAIF,EAAAA,SAAS,EAAI,EACrC,CAACG,EAAOC,CAAQ,EAAIJ,EAAAA,SAAuB,IAAI,EAE/CK,EAAUC,EAAAA,YAAY,SAA2B,CACtD,GAAI,CACH,MAAMC,EAAO,MAAMC,EAAA,EACnBT,EAAgBQ,CAAI,EACpBH,EAAS,IAAI,CACd,OAAS,EAAG,CACXA,EAAS,aAAa,MAAQ,EAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,CACvD,QAAA,CACCF,EAAW,EAAK,CACjB,CACD,EAAG,CAAA,CAAE,EAeL,OAbAO,EAAAA,UAAU,IAAM,CACf,IAAIC,EAAS,GACb,MAAMC,EAAM,SAA2B,CAClCD,SAAcL,EAAA,CACnB,EACKM,EAAA,EACL,MAAMC,EAAQ,YAAY,IAAM,KAAKD,EAAA,EAAO,IAAK,EACjD,MAAO,IAAM,CACZD,EAAS,GACT,cAAcE,CAAK,CACpB,CACD,EAAG,CAACP,CAAO,CAAC,EAERJ,GAAWH,EAAa,SAAW,EAC/Be,EAAAA,IAAC,MAAA,CAAI,UAAU,WAAW,SAAA,wBAAqB,EACnDV,GAASL,EAAa,SAAW,EAC7BgB,EAAAA,KAAC,MAAA,CAAI,UAAU,WAAW,SAAA,CAAA,UAAQX,EAAM,OAAA,EAAQ,EAGvDW,EAAAA,KAAC,MAAA,CAAI,UAAU,mBACd,SAAA,CAAAD,EAAAA,IAAC,MAAG,SAAA,cAAA,CAAY,EAChBA,MAACE,GAAiB,aAAAjB,CAAA,CAA4B,CAAA,EAC/C,CAEF"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{j as a,u as v,r as m,i as b,c as j}from"./index-4gQ7auad.js";import{C,T as S}from"./TimeSavedCard-Cw1dJ4e9.js";import{b as n,c as o,d as g,e as T}from"./format-COjDIVGj.js";import{l as f}from"./pricing-CvE4NllQ.js";function x(e){return e>=80?"ov-stat-value-green":e>=50?"ov-stat-value-yellow":"ov-stat-value-red"}function p(e){return e==null?"":e>=0?"ov-stat-value-green":"ov-stat-value-red"}function t({label:e,value:l,className:s,title:c}){return a.jsxs("div",{className:"ov-stat-row",children:[a.jsx("span",{className:"ov-stat-label",title:c,children:e}),a.jsx("span",{className:`ov-stat-value${s?` ${s}`:""}`,children:l})]})}function R(e){return a.jsxs("div",{className:"card provider-cache-card",children:[a.jsx(t,{label:"Cache Hit Rate",value:`${e.hitPct.toFixed(1)}%`,className:x(e.hitPct)}),a.jsx(t,{label:"Turns Recorded",value:String(e.turnCount)}),a.jsx(t,{label:"Cache Read Tokens",value:n(e.totalCacheRead)}),a.jsx(t,{label:"Cache Write Tokens",value:n(e.totalCacheWrite)}),a.jsx(t,{label:"Input Tokens",value:n(e.totalInput)}),a.jsx(t,{label:"$ Saved (reads)",value:o(e.cacheReadSaved)}),a.jsx(t,{label:"Write Investment",value:o(e.cacheWriteCost)}),a.jsx(t,{label:"Net Saved",value:o(e.netSaved),className:p(e.netSaved)}),a.jsx(t,{label:"Model",value:e.modelLabel??"—"}),a.jsx(t,{label:"Tracked Since",value:g(e.firstTurnAt)}),a.jsx(t,{label:"Last Updated",value:T(e.latestTurnAt)}),e.byModel.length>0&&a.jsxs(a.Fragment,{children:[a.jsx("div",{className:"ov-stat-section-label",children:"Per Model"}),e.byModel.map(l=>{const s=f(l.model),c=s!=null?l.totalCacheRead/1e6*s:null,r=s!=null?l.totalCacheWrite/1e6*s:null;return a.jsxs("div",{className:"ov-stat-group",children:[a.jsx(t,{label:"Model",value:l.model}),a.jsx(t,{label:"Hit Rate",value:`${l.hitPct.toFixed(1)}%`,className:x(l.hitPct)}),a.jsx(t,{label:"Cache Read",value:n(l.totalCacheRead)}),a.jsx(t,{label:"Cache Write",value:n(l.totalCacheWrite)}),a.jsx(t,{label:"Samples",value:String(l.sampleCount)}),a.jsx(t,{label:"$ Saved (reads)",value:c!=null?o(c):"—"}),a.jsx(t,{label:"Write Investment",value:r!=null?o(r):"—"})]},l.model)})]})]})}function H(){const{data:e,loading:l,error:s}=v(m.useCallback(()=>b(),[]),{pollInterval:5e3}),{data:c,loading:r,error:u}=v(m.useCallback(()=>j(),[]),{pollInterval:5e3});return a.jsxs("div",{className:"cache-tab",children:[a.jsx("h2",{children:"Provider Prompt Cache"}),l?a.jsx("p",{className:"tab-stub",children:"Loading…"}):s?a.jsxs("p",{className:"tab-stub",children:["Provider cache unavailable: ",s.message]}):e?a.jsx(R,{hitPct:e.cache.avgHitPct,turnCount:e.cache.turnCount,totalCacheRead:e.cache.totalCacheRead,totalCacheWrite:e.cache.totalCacheWrite,totalInput:e.cache.totalInput,cacheReadSaved:e.savings?.cacheReadSaved??null,cacheWriteCost:e.savings?.cacheWriteCost??null,netSaved:e.savings?.netSaved??null,modelLabel:e.savings?.model??null,firstTurnAt:e.cache.firstTurnAt,latestTurnAt:e.cache.latestTurnAt,byModel:e.cache.byModel??[]}):null,a.jsx("h2",{children:"Mega-Compact Dedup Cache"}),r?a.jsx("p",{className:"tab-stub",children:"Loading…"}):u?a.jsxs("p",{className:"tab-stub",children:["Snapshot unavailable: ",u.message]}):c&&(()=>{const{cacheHits:i,compacts:h,timeSaved:d}=c;return a.jsxs("div",{className:"card-grid overview-card-grid",children:[a.jsx(C,{cacheHitsSession:i.session,cacheHitsTotal:i.total,tokensSavedSession:i.sessionTokensSaved,tokensSavedTotal:i.totalTokensSaved,compactionsSession:h.session,compactionsTotal:h.total}),a.jsx(S,{compactSessionSec:d.compact.sessionSec,compactTotalSec:d.compact.totalSec,cacheHitSessionSec:d.cacheHit.sessionSec,cacheHitTotalSec:d.cacheHit.totalSec})]})})()]})}export{H as default};
|
|
2
|
+
//# sourceMappingURL=CacheTab-eKVc-NV1.js.map
|