pi-mega-compact 0.11.4 → 0.11.6
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/cache-stripes.js +8 -0
- package/dist/extensions/dashboard-server/api-contracts/endpoints.js +32 -0
- package/dist/extensions/dashboard-server/api-contracts/memory-map.js +1 -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 +135 -47
- package/dist/extensions/dashboard-server/routes-game.js +3 -0
- package/dist/extensions/dashboard-server/routes-memory-map.js +50 -0
- 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 +267 -0
- package/dist/extensions/dashboard-server/routes.js +4 -0
- package/dist/extensions/dashboard-server/server.js +13 -1
- package/dist/extensions/mega-commands.js +189 -1
- package/dist/extensions/mega-compact.js +5 -0
- package/dist/extensions/mega-config.js +7 -0
- package/dist/extensions/mega-events/agent-handlers.js +19 -4
- package/dist/extensions/mega-events/compact-handlers.js +1 -0
- package/dist/extensions/mega-events/context-handler.js +45 -92
- package/dist/extensions/mega-events/context-hwm.js +51 -0
- package/dist/extensions/mega-events/error-classifier.js +16 -5
- package/dist/extensions/mega-events/mirror-append.js +107 -0
- package/dist/extensions/mega-events/outage-advisor.js +7 -1
- package/dist/extensions/mega-events/perf-handler.js +134 -2
- package/dist/extensions/mega-events/recall-tail.js +59 -0
- package/dist/extensions/mega-events/separated-prompt.js +248 -0
- package/dist/extensions/mega-events/session-handlers.js +15 -8
- package/dist/extensions/mega-runtime/env-loader.js +56 -0
- package/dist/extensions/mega-runtime/reset-runtime.js +6 -0
- package/dist/extensions/mega-runtime/runtime-snapshot.js +1 -0
- package/dist/extensions/mega-runtime/runtime.js +7 -0
- package/dist/extensions/mega-runtime/snapshot.js +1 -0
- package/dist/extensions/mega-runtime/widget.js +5 -0
- package/dist/src/cache-stripe-impl.js +288 -0
- package/dist/src/cache-stripe.js +11 -0
- package/dist/src/config/stopwords.js +24 -0
- package/dist/src/config.js +17 -0
- package/dist/src/embedder.js +41 -2
- package/dist/src/memoryGraph.js +299 -0
- package/dist/src/memoryStats.js +75 -0
- package/dist/src/monitoring.js +13 -0
- package/dist/src/prefix-break.js +43 -0
- package/dist/src/pricing.js +1 -1
- package/dist/src/queryExpansion.js +163 -0
- package/dist/src/queryReformulation/cache.js +123 -0
- package/dist/src/queryReformulation/rrf.js +29 -0
- package/dist/src/queryReformulation/tfidf.js +61 -0
- package/dist/src/queryReformulation/vagueness.js +33 -0
- package/dist/src/queryReformulation.js +225 -0
- package/dist/src/recallMetrics.js +258 -0
- package/dist/src/store/sqlite/cache-stripes.js +133 -0
- package/dist/src/store/sqlite/fts5-search.js +68 -0
- package/dist/src/store/sqlite/perf-samples.js +145 -16
- package/dist/src/store/sqlite/schema.js +60 -1
- package/dist/src/store/sqlite.js +1 -0
- 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 +55 -10
- package/dist/src/tiered-router/types.js +18 -0
- package/dist/src/tieredRouter.js +421 -0
- package/dist/src/vectorStore.js +8 -6
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-vEo2MtE2.js → AchievementTiles-DR9cLGDL.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{AchievementTiles-vEo2MtE2.js.map → AchievementTiles-DR9cLGDL.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/AchievementsTab-svNUgJ1e.js +2 -0
- package/extensions/dashboard-client/dist/assets/{AchievementsTab-DjxfYrDc.js.map → AchievementsTab-svNUgJ1e.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/CacheTab-ASwhOMjR.js +2 -0
- package/extensions/dashboard-client/dist/assets/CacheTab-ASwhOMjR.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{ConfigTab-Cm6ymbmx.js → ConfigTab-CmmWheuW.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{ConfigTab-Cm6ymbmx.js.map → ConfigTab-CmmWheuW.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{EventsTab-DXWl_YzI.js → EventsTab-CFE7jLsa.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{EventsTab-DXWl_YzI.js.map → EventsTab-CFE7jLsa.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/GameTab-CJAX_th_.js +2 -0
- package/extensions/dashboard-client/dist/assets/{GameTab-BLDudcO5.js.map → GameTab-CJAX_th_.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DLkKCztn.js → MaintenanceTab-P4SUsvMf.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MaintenanceTab-DLkKCztn.js.map → MaintenanceTab-P4SUsvMf.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js +2 -0
- package/extensions/dashboard-client/dist/assets/MemoryMapTab-BBiH76eF.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Cq4u-rZF.js → MetricsTab-CHQscixy.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{MetricsTab-Cq4u-rZF.js.map → MetricsTab-CHQscixy.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/OverviewTab-DBs4DJdd.js +2 -0
- package/extensions/dashboard-client/dist/assets/{OverviewTab-D4kGvsiQ.js.map → OverviewTab-DBs4DJdd.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/ReposTab-DeFz76L5.js +2 -0
- package/extensions/dashboard-client/dist/assets/ReposTab-DeFz76L5.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DZC2-GCn.js → SessionsTab-BWNsj1OB.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{SessionsTab-DZC2-GCn.js.map → SessionsTab-BWNsj1OB.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/SetupTab-uS6A0xmw.js +2 -0
- package/extensions/dashboard-client/dist/assets/SetupTab-uS6A0xmw.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-CiqCOQuL.js +2 -0
- package/extensions/dashboard-client/dist/assets/TimeSavedCard-CiqCOQuL.js.map +1 -0
- package/extensions/dashboard-client/dist/assets/{TopicsTab-01G71hv1.js → TopicsTab-BWWQxltv.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TopicsTab-01G71hv1.js.map → TopicsTab-BWWQxltv.js.map} +1 -1
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DLRg0LZT.js → TurnsTab-CVz4f4-o.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{TurnsTab-DLRg0LZT.js.map → TurnsTab-CVz4f4-o.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-CUNn22iV.css +1 -0
- package/extensions/dashboard-client/dist/assets/index-DrktNOoT.js +42 -0
- package/extensions/dashboard-client/dist/assets/index-DrktNOoT.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-Bf5LOLSi.js} +2 -2
- package/extensions/dashboard-client/dist/assets/{useSSE-BBYt5UPB.js.map → useSSE-Bf5LOLSi.js.map} +1 -1
- package/extensions/dashboard-client/dist/index.html +2 -2
- package/extensions/dashboard-client/src/App.tsx +28 -8
- package/extensions/dashboard-client/src/api/client.ts +29 -0
- package/extensions/dashboard-client/src/components/ActiveReposTable.tsx +21 -12
- package/extensions/dashboard-client/src/components/CacheHitRateTrendCard.tsx +100 -0
- 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/components/StripeDistributionCard.tsx +110 -0
- package/extensions/dashboard-client/src/components/TabBar.tsx +49 -4
- package/extensions/dashboard-client/src/main.tsx +1 -0
- package/extensions/dashboard-client/src/styles/base.css +30 -0
- package/extensions/dashboard-client/src/styles/cache.css +167 -0
- package/extensions/dashboard-client/src/tabs/CacheTab.tsx +75 -12
- package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +440 -0
- package/extensions/dashboard-client/src/tabs/SetupTab.tsx +418 -0
- package/extensions/dashboard-client/tsconfig.json +3 -2
- package/extensions/dashboard-client/vite.config.ts +2 -0
- package/extensions/dashboard-server/api-contracts/cache-stripes.ts +49 -0
- package/extensions/dashboard-server/api-contracts/endpoints.ts +66 -3
- package/extensions/dashboard-server/api-contracts/index.ts +22 -0
- package/extensions/dashboard-server/api-contracts/memory-map.ts +50 -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 +80 -0
- package/extensions/dashboard-server/index-reader.ts +138 -121
- package/extensions/dashboard-server/routes-cache.ts +186 -75
- package/extensions/dashboard-server/routes-game.ts +3 -0
- package/extensions/dashboard-server/routes-memory-map.ts +105 -0
- 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 +293 -0
- package/extensions/dashboard-server/routes.ts +4 -0
- package/extensions/dashboard-server/server.ts +12 -0
- package/extensions/mega-commands.ts +211 -1
- package/extensions/mega-compact.ts +5 -0
- package/extensions/mega-config.ts +27 -0
- package/extensions/mega-events/agent-handlers.ts +20 -4
- package/extensions/mega-events/compact-handlers.ts +1 -0
- package/extensions/mega-events/context-handler.ts +53 -110
- package/extensions/mega-events/context-hwm.ts +85 -0
- package/extensions/mega-events/error-classifier.ts +15 -4
- package/extensions/mega-events/mirror-append.ts +155 -0
- package/extensions/mega-events/outage-advisor.ts +11 -5
- package/extensions/mega-events/perf-handler.ts +190 -6
- package/extensions/mega-events/recall-tail.ts +88 -0
- package/extensions/mega-events/separated-prompt.ts +329 -0
- package/extensions/mega-events/session-handlers.ts +14 -7
- package/extensions/mega-runtime/env-loader.ts +55 -0
- package/extensions/mega-runtime/helpers.ts +14 -0
- package/extensions/mega-runtime/reset-runtime.ts +6 -0
- package/extensions/mega-runtime/runtime-snapshot.ts +1 -0
- package/extensions/mega-runtime/runtime.ts +7 -0
- package/extensions/mega-runtime/snapshot.ts +6 -0
- package/extensions/mega-runtime/widget-types.ts +3 -0
- package/extensions/mega-runtime/widget.ts +5 -0
- package/package.json +5 -2
- package/src/cache-stripe-impl.ts +385 -0
- package/src/cache-stripe.ts +18 -0
- package/src/config/stopwords.ts +25 -0
- package/src/config.ts +26 -0
- package/src/embedder.ts +42 -2
- package/src/memoryGraph.ts +438 -0
- package/src/memoryStats.ts +107 -0
- package/src/monitoring.ts +35 -0
- package/src/prefix-break.ts +83 -0
- package/src/pricing.ts +2 -2
- package/src/queryExpansion.ts +206 -0
- package/src/queryReformulation/cache.ts +169 -0
- package/src/queryReformulation/rrf.ts +51 -0
- package/src/queryReformulation/tfidf.ts +79 -0
- package/src/queryReformulation/vagueness.ts +38 -0
- package/src/queryReformulation.ts +355 -0
- package/src/recallMetrics.ts +388 -0
- package/src/store/sqlite/cache-stripes.ts +196 -0
- package/src/store/sqlite/fts5-search.ts +90 -0
- package/src/store/sqlite/perf-samples.ts +218 -23
- package/src/store/sqlite/schema.ts +60 -1
- package/src/store/sqlite.ts +1 -0
- 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 +71 -11
- package/src/tiered-router/types.ts +64 -0
- package/src/tieredRouter.ts +489 -0
- package/src/vectorStore.ts +9 -5
- 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 -1383
- 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/GameTab-BLDudcO5.js +0 -2
- 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-BEdKlpAB.css +0 -1
- package/extensions/dashboard-client/dist/assets/index-BX0bcgLu.js +0 -42
- 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 -1433
- 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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { openStore,
|
|
1
|
+
import { openStore, writeCheckpointEpoch, } from "../../src/store/sqlite.js";
|
|
2
2
|
import { epochIdFor } from "../../src/mirror/epoch.js";
|
|
3
3
|
import { autoCompactCheck } from "../../src/compact.js";
|
|
4
4
|
import { estimateSessionTokens } from "../../src/tokens.js";
|
|
@@ -9,75 +9,9 @@ import { openTurnStore } from "../../src/store/turns/connection.js";
|
|
|
9
9
|
import { buildTopicModel, createTopicStore, bumpWikiCompactCounter, } from "../../src/topics/index.js";
|
|
10
10
|
import { computeLiveTrimCut, liveTrimSummaryMessage } from "../mega-trim.js";
|
|
11
11
|
import { pressureFromPct, pressureRatio, } from "../mega-config.js";
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* - Objects: keys sorted alphabetically, values recursively canonicalized
|
|
16
|
-
* - Arrays: elements recursively canonicalized (array ORDER is preserved)
|
|
17
|
-
* - Primitives: returned as-is
|
|
18
|
-
*
|
|
19
|
-
* F5 fix: shallow-sorted JSON.stringify(content, Object.keys(content).sort())
|
|
20
|
-
* omitted nested keys not in the top-level keys array and only sorted one level.
|
|
21
|
-
* This recursively sorts every object at every depth so semantically-equal
|
|
22
|
-
* differently-ordered content hashes identically.
|
|
23
|
-
*/
|
|
24
|
-
function canonicalize(value) {
|
|
25
|
-
if (value === null || value === undefined)
|
|
26
|
-
return value;
|
|
27
|
-
if (typeof value !== "object")
|
|
28
|
-
return value;
|
|
29
|
-
if (Array.isArray(value))
|
|
30
|
-
return value.map(canonicalize);
|
|
31
|
-
// Plain object: sort keys, recursively canonicalize values.
|
|
32
|
-
const sorted = Object.keys(value).sort();
|
|
33
|
-
const out = {};
|
|
34
|
-
for (const k of sorted) {
|
|
35
|
-
out[k] = canonicalize(value[k]);
|
|
36
|
-
}
|
|
37
|
-
return out;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Convert a pi AgentMessage to a RawTranscriptRow for the DB mirror.
|
|
41
|
-
* content_bytes is canonical JSON (sorted keys, recursive) for deterministic
|
|
42
|
-
* storage; contentHash is the canonical digest (normalize + hash) that matches
|
|
43
|
-
* what dedupTranscript computes so the two pipelines use the same linkage key.
|
|
44
|
-
*
|
|
45
|
-
* F4 fix: the hash key MUST match on both sides of the append/dedup split.
|
|
46
|
-
* Using computeContentDigest here (normalize → hash) ensures content_ref set
|
|
47
|
-
* by dedupTranscript always resolves to an existing dedup_mirror row even for
|
|
48
|
-
* whitespace/case-variant content. F5 fix: recursive canonicalize replaces the
|
|
49
|
-
* broken shallow-sorted JSON.stringify replacer.
|
|
50
|
-
*/
|
|
51
|
-
function toRawTranscriptRow(msg, sessionId, epochId, currentTurn) {
|
|
52
|
-
// Narrow to Message union (has content + timestamp).
|
|
53
|
-
const m = msg;
|
|
54
|
-
const content = m.content;
|
|
55
|
-
if (content == null || content === "")
|
|
56
|
-
return null;
|
|
57
|
-
// Canonical bytes: string content → normalize for consistent byte content;
|
|
58
|
-
// object content → recursive canonicalize then JSON (no replacer).
|
|
59
|
-
const contentBytes = typeof content === "string"
|
|
60
|
-
? content // F5: strings are primitives; their byte content is fixed
|
|
61
|
-
: JSON.stringify(canonicalize(content));
|
|
62
|
-
// F4 fix: use the same digest as dedupTranscript so contentHash is consistent
|
|
63
|
-
// on both sides of the append/dedup split. computeContentDigest normalizes
|
|
64
|
-
// (strip ANSI, NFC, case-fold, collapse whitespace) then hashes → the same
|
|
65
|
-
// key is stored in raw_transcript.content_hash AND used as the dedup link.
|
|
66
|
-
const { contentHash } = computeContentDigest(contentBytes);
|
|
67
|
-
return {
|
|
68
|
-
contentHash,
|
|
69
|
-
sessionId,
|
|
70
|
-
seq: 0, // assigned by appendRawTranscript (COALESCE(MAX(seq),0)+1)
|
|
71
|
-
role: m.role ?? "unknown",
|
|
72
|
-
contentBytes,
|
|
73
|
-
toolName: m.toolName ?? null,
|
|
74
|
-
messageTimestamp: m.timestamp ?? null,
|
|
75
|
-
checkpointEpoch: epochId,
|
|
76
|
-
// S50: label the row with the turn that produced it (per-turn dedup /
|
|
77
|
-
// compression-by-turn metrics). Null when the writer omits it (back-compat).
|
|
78
|
-
turnIndex: currentTurn ?? null,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
12
|
+
import { appendMirrorMessages } from "./mirror-append.js";
|
|
13
|
+
import { stagedForTail, withRecallTail } from "./recall-tail.js";
|
|
14
|
+
import { buildSeparatedPrompt } from "./separated-prompt.js";
|
|
81
15
|
/** Register the context event handler (live-trim auto-trigger). */
|
|
82
16
|
export function registerContextHandler(pi, runtime, config) {
|
|
83
17
|
// ---- Auto-trigger: live trim (compact and continue) + native durable ----
|
|
@@ -98,6 +32,24 @@ export function registerContextHandler(pi, runtime, config) {
|
|
|
98
32
|
const usage = ctx.getContextUsage();
|
|
99
33
|
const pct = usage?.percent;
|
|
100
34
|
const messages = event.messages;
|
|
35
|
+
// S53: helper to inject the staged recall/memory block as a user-role
|
|
36
|
+
// tail message at any view-return point. Returns undefined when nothing
|
|
37
|
+
// is staged (or the flag is OFF) so the caller falls through to its
|
|
38
|
+
// normal return. The F3 mirror append (above the gates) runs on the
|
|
39
|
+
// REAL transcript before any view is built, so the injected tail never
|
|
40
|
+
// reaches raw_transcript (PREVENT-PI: append-only, view-only).
|
|
41
|
+
const tailResult = (msgs) => {
|
|
42
|
+
const base = msgs ?? messages;
|
|
43
|
+
if (!stagedForTail(runtime, config) && !config.messageSeparation)
|
|
44
|
+
return undefined;
|
|
45
|
+
let result = stagedForTail(runtime, config)
|
|
46
|
+
? withRecallTail(base, runtime, config)
|
|
47
|
+
: base;
|
|
48
|
+
if (config.messageSeparation) {
|
|
49
|
+
result = buildSeparatedPrompt(result);
|
|
50
|
+
}
|
|
51
|
+
return { messages: result };
|
|
52
|
+
};
|
|
101
53
|
// Always track context for the dashboard/widget, even when auto is off.
|
|
102
54
|
// (v0.8 regression: !config.auto gate sat above this, leaving ctx stats
|
|
103
55
|
// null -> widget '?% / ?/?' when auto disabled. Track first, THEN gate.)
|
|
@@ -118,21 +70,24 @@ export function registerContextHandler(pi, runtime, config) {
|
|
|
118
70
|
runtime.lastCtxPercent = pct ?? null;
|
|
119
71
|
runtime.lastCtxWindow = usage?.contextWindow ?? 0;
|
|
120
72
|
runtime.snapshot(ctx);
|
|
121
|
-
if (!config.auto)
|
|
73
|
+
if (!config.auto) {
|
|
74
|
+
const tailed = tailResult();
|
|
75
|
+
if (tailed)
|
|
76
|
+
return tailed;
|
|
122
77
|
return;
|
|
78
|
+
}
|
|
123
79
|
const view = viewForFallback ?? runtime.engineView(messages);
|
|
124
|
-
// S27 DB-mirror: append
|
|
80
|
+
// S27 DB-mirror: append incoming messages to raw_transcript.
|
|
125
81
|
// Runs BEFORE fast-gate so every message is captured, even if we
|
|
126
82
|
// don't compact this turn. Append is idempotent (content_hash PK).
|
|
83
|
+
// F3: high-water mark (mirror-append.ts) skips already-processed
|
|
84
|
+
// messages on subsequent events. On fork/rewind (shorter list or
|
|
85
|
+
// boundary hash mismatch) the mark is dropped, falling back to a
|
|
86
|
+
// full reprocess.
|
|
127
87
|
if (config.dbMirror) {
|
|
128
88
|
try {
|
|
129
89
|
const db = openStore(runtime.currentStateDir);
|
|
130
|
-
|
|
131
|
-
for (const msg of messages) {
|
|
132
|
-
const raw = toRawTranscriptRow(msg, runtime.rt.sessionId, epochId, runtime.currentTurn);
|
|
133
|
-
if (raw)
|
|
134
|
-
appendRawTranscript(db, raw);
|
|
135
|
-
}
|
|
90
|
+
appendMirrorMessages(db, messages, runtime.rt.sessionId, epochIdFor(runtime.rt.sessionId), runtime.currentTurn);
|
|
136
91
|
}
|
|
137
92
|
catch (e) {
|
|
138
93
|
runtime.logger.warn("db-mirror-append-fail", { error: String(e) });
|
|
@@ -157,18 +112,18 @@ export function registerContextHandler(pi, runtime, config) {
|
|
|
157
112
|
// custom tier OR tiered-but-pct-unavailable → token gate (S27 fallback).
|
|
158
113
|
if (currentTokens < runtime.effectiveThreshold) {
|
|
159
114
|
runtime.diagCtxFastGate++;
|
|
160
|
-
return;
|
|
115
|
+
return tailResult() ?? undefined;
|
|
161
116
|
}
|
|
162
117
|
const check = autoCompactCheck(currentTokens, runtime.effectiveThreshold); // SERVER-STYLE CONFIRM (local)
|
|
163
118
|
if (!check.shouldCompact) {
|
|
164
119
|
runtime.diagCtxNoCompact++;
|
|
165
|
-
return;
|
|
120
|
+
return tailResult() ?? undefined;
|
|
166
121
|
}
|
|
167
122
|
gatePassed = true;
|
|
168
123
|
}
|
|
169
124
|
if (!gatePassed) {
|
|
170
125
|
runtime.diagCtxFastGate++;
|
|
171
|
-
return;
|
|
126
|
+
return tailResult() ?? undefined;
|
|
172
127
|
}
|
|
173
128
|
// D.2: Replay MUST be exempt from debounce — replay is free (no compute,
|
|
174
129
|
// no re-write) and prevents unnecessary KV-cache invalidation. Check
|
|
@@ -197,9 +152,8 @@ export function registerContextHandler(pi, runtime, config) {
|
|
|
197
152
|
runtime.snapshot(ctx);
|
|
198
153
|
// v0.8.7: shallow-copy the cached summary so pi's transformContext can't
|
|
199
154
|
// mutate the shared reference across replays (audit P3).
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
};
|
|
155
|
+
const replayView = [{ ...runtime.trimCache.summaryAgentMsg }, ...recent];
|
|
156
|
+
return tailResult(replayView) ?? { messages: replayView };
|
|
203
157
|
}
|
|
204
158
|
// else: context grew enough → fall through to re-compact (cache is stale)
|
|
205
159
|
}
|
|
@@ -208,7 +162,7 @@ export function registerContextHandler(pi, runtime, config) {
|
|
|
208
162
|
const now = Date.now();
|
|
209
163
|
if (now < runtime.debounceUntil) {
|
|
210
164
|
runtime.diagCtxDebounce++;
|
|
211
|
-
return;
|
|
165
|
+
return tailResult() ?? undefined;
|
|
212
166
|
}
|
|
213
167
|
runtime.debounceUntil = now + 2000;
|
|
214
168
|
// Adaptive compression (Fix E): scale compression strength + keepFrom depth
|
|
@@ -233,11 +187,10 @@ export function registerContextHandler(pi, runtime, config) {
|
|
|
233
187
|
runtime.diagLiveTrimFires++;
|
|
234
188
|
runtime.diagLiveTrimReplays++;
|
|
235
189
|
runtime.snapshot(ctx);
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
};
|
|
190
|
+
const skipView = [{ ...runtime.trimCache.summaryAgentMsg }, ...recent];
|
|
191
|
+
return tailResult(skipView) ?? { messages: skipView };
|
|
239
192
|
}
|
|
240
|
-
return;
|
|
193
|
+
return tailResult() ?? undefined;
|
|
241
194
|
}
|
|
242
195
|
// S27 DB-mirror: write checkpoint_epoch with deterministic nonce.
|
|
243
196
|
// This makes the cache key stable across identical compactions.
|
|
@@ -392,7 +345,7 @@ export function registerContextHandler(pi, runtime, config) {
|
|
|
392
345
|
viewLen: view.length,
|
|
393
346
|
anchorUserMessages,
|
|
394
347
|
});
|
|
395
|
-
return; // unsafe / below anchor floor — no trim this call
|
|
348
|
+
return tailResult() ?? undefined; // unsafe / below anchor floor — no trim this call
|
|
396
349
|
}
|
|
397
350
|
const summaryMsg = liveTrimSummaryMessage({
|
|
398
351
|
compactedFrom: ran.result.compactedFrom,
|
|
@@ -446,11 +399,11 @@ export function registerContextHandler(pi, runtime, config) {
|
|
|
446
399
|
ctxPct: pct,
|
|
447
400
|
ctxTokens: usage?.tokens ?? null,
|
|
448
401
|
});
|
|
449
|
-
return { messages: [summaryAgentMsg, ...recent] };
|
|
402
|
+
return tailResult([summaryAgentMsg, ...recent]) ?? { messages: [summaryAgentMsg, ...recent] };
|
|
450
403
|
}
|
|
451
404
|
catch {
|
|
452
405
|
runtime.diagCtxThrown++;
|
|
453
|
-
return; // non-fatal: no trim this call; the next context event retries
|
|
406
|
+
return tailResult() ?? undefined; // non-fatal: no trim this call; the next context event retries
|
|
454
407
|
}
|
|
455
408
|
});
|
|
456
409
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const TABLE_DDL = `CREATE TABLE IF NOT EXISTS context_hwm (
|
|
2
|
+
session_id TEXT PRIMARY KEY,
|
|
3
|
+
last_seq INTEGER NOT NULL,
|
|
4
|
+
last_content_hash TEXT NOT NULL
|
|
5
|
+
)`;
|
|
6
|
+
/** Ensure the context_hwm table exists. Idempotent. */
|
|
7
|
+
export function ensureHwmTable(db) {
|
|
8
|
+
try {
|
|
9
|
+
db.exec(TABLE_DDL);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
// non-fatal
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Read the high-water mark for a session. Returns undefined if none. */
|
|
16
|
+
export function readHwm(db, sessionId) {
|
|
17
|
+
try {
|
|
18
|
+
const row = db
|
|
19
|
+
.prepare("SELECT session_id, last_seq, last_content_hash FROM context_hwm WHERE session_id = ?")
|
|
20
|
+
.get(sessionId);
|
|
21
|
+
if (!row)
|
|
22
|
+
return undefined;
|
|
23
|
+
return {
|
|
24
|
+
sessionId: row.session_id,
|
|
25
|
+
lastSeq: Number(row.last_seq),
|
|
26
|
+
lastContentHash: row.last_content_hash,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Write (INSERT OR REPLACE) the high-water mark for a session. */
|
|
34
|
+
export function writeHwm(db, hwm) {
|
|
35
|
+
try {
|
|
36
|
+
db.prepare("INSERT OR REPLACE INTO context_hwm (session_id, last_seq, last_content_hash) VALUES (?, ?, ?)").run(hwm.sessionId, hwm.lastSeq, hwm.lastContentHash);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// non-fatal
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/** Drop the high-water mark for a session (invalidation). */
|
|
43
|
+
export function dropHwm(db, sessionId) {
|
|
44
|
+
try {
|
|
45
|
+
db.prepare("DELETE FROM context_hwm WHERE session_id = ?").run(sessionId);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// non-fatal
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export const TEST_ONLY = { TABLE_DDL };
|
|
@@ -63,7 +63,7 @@ export function classifyErrorDetailed(message) {
|
|
|
63
63
|
return { category: null, signal: 'length-guard' };
|
|
64
64
|
if (sr === 'aborted')
|
|
65
65
|
return { category: 'cancelled', signal: 'cancelled' };
|
|
66
|
-
if (
|
|
66
|
+
if (/^(stop|tool_?use|end_?turn|max_?tokens|complete|finished|done)$/i.test(sr))
|
|
67
67
|
return { category: null, signal: 'success' };
|
|
68
68
|
const usage = m.usage;
|
|
69
69
|
usagePresent = usage != null && typeof usage === 'object';
|
|
@@ -100,10 +100,21 @@ export function classifyErrorDetailed(message) {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
if (!sr) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
// A missing stopReason alone is not sufficient evidence of stream death.
|
|
104
|
+
// Require at least one corroborating signal: an error field, or 0 total tokens
|
|
105
|
+
// (indicating the stream died before producing anything). A normal completion
|
|
106
|
+
// from a runtime that omits stopReason has content and tokens — not an error.
|
|
107
|
+
const hasError = (m.error != null && m.error !== undefined && m.error !== '') ||
|
|
108
|
+
(typeof m.errorMessage === 'string' && m.errorMessage !== '');
|
|
109
|
+
if (hasError || (usagePresent && totalTokens === 0)) {
|
|
110
|
+
const r = { category: 'transient', signal: 'stream-death-no-stopreason' };
|
|
111
|
+
if (httpStatus !== undefined)
|
|
112
|
+
r.httpStatus = httpStatus;
|
|
113
|
+
return r;
|
|
114
|
+
}
|
|
115
|
+
// No stopReason, no error field, tokens present — likely a normal completion
|
|
116
|
+
// from a runtime that omits stopReason (pi-crew, etc.). Not an error.
|
|
117
|
+
return { category: null, signal: 'success' };
|
|
107
118
|
}
|
|
108
119
|
text = parts.join(' ');
|
|
109
120
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { appendRawTranscript } from "../../src/store/sqlite.js";
|
|
2
|
+
import { computeContentDigest } from "../../src/dedup/digest.js";
|
|
3
|
+
import { ensureHwmTable, readHwm, writeHwm, dropHwm, } from "./context-hwm.js";
|
|
4
|
+
/**
|
|
5
|
+
* Append incoming messages to raw_transcript with HWM incremental support.
|
|
6
|
+
*
|
|
7
|
+
* On fork/rewind (shorter message list or boundary hash mismatch), the HWM
|
|
8
|
+
* is dropped and all messages are reprocessed.
|
|
9
|
+
*/
|
|
10
|
+
export function appendMirrorMessages(db, messages, sessionId, epochId, currentTurn) {
|
|
11
|
+
ensureHwmTable(db);
|
|
12
|
+
const hwm = readHwm(db, sessionId);
|
|
13
|
+
let tailStart = 0;
|
|
14
|
+
let needDrop = false;
|
|
15
|
+
if (hwm) {
|
|
16
|
+
if (messages.length <= hwm.lastSeq) {
|
|
17
|
+
// Session was shortened (fork/rewind/clear) — full reprocess.
|
|
18
|
+
needDrop = true;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
// Verify boundary integrity: hash of message at hwm.lastSeq-1
|
|
22
|
+
// must match the stored hash.
|
|
23
|
+
const boundaryMsg = messages[hwm.lastSeq - 1];
|
|
24
|
+
const boundaryHash = contentHashOf(boundaryMsg.content);
|
|
25
|
+
if (boundaryHash === hwm.lastContentHash) {
|
|
26
|
+
tailStart = hwm.lastSeq;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
needDrop = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (needDrop) {
|
|
34
|
+
dropHwm(db, sessionId);
|
|
35
|
+
}
|
|
36
|
+
// Process tail (or all if tailStart === 0).
|
|
37
|
+
let rowsProcessed = 0;
|
|
38
|
+
for (let i = tailStart; i < messages.length; i++) {
|
|
39
|
+
const raw = toRawRow(messages[i], sessionId, epochId, currentTurn);
|
|
40
|
+
if (raw) {
|
|
41
|
+
appendRawTranscript(db, raw);
|
|
42
|
+
rowsProcessed++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Write new HWM.
|
|
46
|
+
if (messages.length > 0) {
|
|
47
|
+
const lastMsg = messages[messages.length - 1];
|
|
48
|
+
writeHwm(db, {
|
|
49
|
+
sessionId,
|
|
50
|
+
lastSeq: messages.length,
|
|
51
|
+
lastContentHash: contentHashOf(lastMsg.content),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return { rowsProcessed };
|
|
55
|
+
}
|
|
56
|
+
/** Single rule for hashing message content on BOTH sides of the HWM
|
|
57
|
+
* (write + boundary check) — a mismatch here silently defeats the
|
|
58
|
+
* incremental path (every event would full-reprocess). null/undefined → "". */
|
|
59
|
+
function contentHashOf(content) {
|
|
60
|
+
if (content == null)
|
|
61
|
+
return "";
|
|
62
|
+
const bytes = typeof content === "string"
|
|
63
|
+
? content
|
|
64
|
+
: JSON.stringify(canonicalize(content));
|
|
65
|
+
return computeContentDigest(bytes).contentHash;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Canonicalize a value for deterministic JSON serialization (keys sorted).
|
|
69
|
+
* This is the same logic as the inline function in context-handler.ts.
|
|
70
|
+
*/
|
|
71
|
+
function canonicalize(value) {
|
|
72
|
+
if (value === null || value === undefined)
|
|
73
|
+
return value;
|
|
74
|
+
if (typeof value !== "object")
|
|
75
|
+
return value;
|
|
76
|
+
if (Array.isArray(value))
|
|
77
|
+
return value.map(canonicalize);
|
|
78
|
+
const sorted = Object.keys(value).sort();
|
|
79
|
+
const out = {};
|
|
80
|
+
for (const k of sorted) {
|
|
81
|
+
out[k] = canonicalize(value[k]);
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
/** Convert an AgentMessage to a RawTranscriptRow (same logic as toRawTranscriptRow in context-handler.ts). */
|
|
86
|
+
function toRawRow(msg, sessionId, epochId, currentTurn) {
|
|
87
|
+
const m = msg;
|
|
88
|
+
const content = m.content;
|
|
89
|
+
if (content == null || content === "")
|
|
90
|
+
return null;
|
|
91
|
+
const contentBytes = typeof content === "string"
|
|
92
|
+
? content
|
|
93
|
+
: JSON.stringify(canonicalize(content));
|
|
94
|
+
return {
|
|
95
|
+
contentHash: computeContentDigest(contentBytes).contentHash,
|
|
96
|
+
sessionId,
|
|
97
|
+
seq: 0, // assigned by appendRawTranscript
|
|
98
|
+
role: m.role ?? "unknown",
|
|
99
|
+
contentBytes,
|
|
100
|
+
toolName: m.toolName ?? null,
|
|
101
|
+
messageTimestamp: m.timestamp ?? null,
|
|
102
|
+
checkpointEpoch: epochId,
|
|
103
|
+
turnIndex: currentTurn ?? null,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// Re-export for convenience when the handler only needs the table ensure.
|
|
107
|
+
export { ensureHwmTable, readHwm, writeHwm, dropHwm };
|
|
@@ -49,5 +49,11 @@ export async function maybeSendProviderOutageAdvisory(effectiveCategory, runtime
|
|
|
49
49
|
...(detail?.signal !== undefined ? { signal: detail.signal } : {}),
|
|
50
50
|
...(detail?.rawText !== undefined ? { rawText: detail.rawText } : {}),
|
|
51
51
|
});
|
|
52
|
-
|
|
52
|
+
// Advisory is dashboard-only (events tab + log). No user-visible message —
|
|
53
|
+
// injecting into the conversation scares users and triggers false-positive
|
|
54
|
+
// re-classification of the resulting turn (2026-07-31 incident).
|
|
55
|
+
if (!config.advisoryChannel) {
|
|
56
|
+
// Legacy path (flag OFF): user-role message injection.
|
|
57
|
+
await safeSendUserMessage(pi, `[mega-compact] the provider is having issues (${runtime.rt.consecutiveErrors} consecutive failures — timeouts/5xx/rate-limits). Retries are bounded and continue automatically; your context is fine — do NOT clear or reset it. Work resumes as soon as the provider recovers.`);
|
|
58
|
+
}
|
|
53
59
|
}
|
|
@@ -1,10 +1,126 @@
|
|
|
1
|
-
import { recordPerfSample } from "../../src/store/sqlite.js";
|
|
1
|
+
import { recordPerfSample, readPerfSamples, } from "../../src/store/sqlite.js";
|
|
2
|
+
import { classifyPrefixBreak } from "../../src/prefix-break.js";
|
|
2
3
|
/** Narrow a turn_end message to its usage block when it is an assistant msg. */
|
|
3
4
|
function usageOf(msg) {
|
|
4
5
|
if (msg.role !== "assistant" || !msg.usage)
|
|
5
6
|
return null;
|
|
6
7
|
return msg.usage;
|
|
7
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Attempt to record a prefix_break sample when a significant cache_hit_pct
|
|
11
|
+
* drop is detected between two consecutive turns.
|
|
12
|
+
*
|
|
13
|
+
* Drop threshold: 20 percentage points. When triggered, calls
|
|
14
|
+
* classifyPrefixBreak with the three candidate event timestamps from the
|
|
15
|
+
* runtime and records a `prefix_break` sample. Best-effort + non-fatal.
|
|
16
|
+
*
|
|
17
|
+
* Also evaluates rolling cache health and emits a degradation event when the
|
|
18
|
+
* composite score falls below 0.6 (A3, PLAN_V2 Phase 4).
|
|
19
|
+
*
|
|
20
|
+
* PREVENT-PI-004: guardrails-allow PREVENT-PI-004: local SQLite write only.
|
|
21
|
+
* PREVENT-002: SQL is fully parameterized via recordPerfSample.
|
|
22
|
+
*/
|
|
23
|
+
function tryRecordPrefixBreak(stateDir, prevHitPct, currHitPct, breakAt, runtime) {
|
|
24
|
+
try {
|
|
25
|
+
if (prevHitPct - currHitPct < 20)
|
|
26
|
+
return; // no significant drop
|
|
27
|
+
const events = {
|
|
28
|
+
lastRecallAt: runtime.rt.lastInjectAt, // S53: inject fires alongside recall
|
|
29
|
+
lastCompactAt: runtime.rt.lastCompactAt,
|
|
30
|
+
lastInjectAt: runtime.rt.lastInjectAt,
|
|
31
|
+
};
|
|
32
|
+
const { cause, confidence } = classifyPrefixBreak(breakAt, events);
|
|
33
|
+
recordPerfSample(stateDir, "prefix_break", 0, {
|
|
34
|
+
cause,
|
|
35
|
+
confidence,
|
|
36
|
+
prevHitPct,
|
|
37
|
+
currHitPct,
|
|
38
|
+
breakAt,
|
|
39
|
+
}); // guardrails-allow PREVENT-PI-004: local SQLite write (loopback perf instrumentation)
|
|
40
|
+
// A3: compute rolling cache health score and emit alert on degradation.
|
|
41
|
+
tryComputeCacheHealth(stateDir, currHitPct, runtime);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
/* non-fatal: prefix-break detection never blocks the agent loop */
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Compute a rolling cache health score and emit a dashboard event when it
|
|
49
|
+
* falls below the degradation threshold (0.6 / "yellow").
|
|
50
|
+
*
|
|
51
|
+
* The score is a weighted composite of:
|
|
52
|
+
* - hitRateScore (0-1): recent cache_hit_pct sample average / 100
|
|
53
|
+
* - stabilityScore (0-1): 1 - recent prefix_break count / maxExpectedBreaks
|
|
54
|
+
* - churnScore (0-1): 1 - stripe re-assignment rate (derived from prefix_break classifications)
|
|
55
|
+
*
|
|
56
|
+
* Weights: hit rate 50%, prefix stability 30%, churn 20%.
|
|
57
|
+
*
|
|
58
|
+
* PREVENT-PI-004: guardrails-allow PREVENT-PI-004: local SQLite read, no network.
|
|
59
|
+
* PREVENT-001: readPerfSamples returns empty array when no data; handled.
|
|
60
|
+
* PREVENT-011: no `any` — all arrays are typed PerfSampleRow[].
|
|
61
|
+
*/
|
|
62
|
+
function tryComputeCacheHealth(stateDir, currHitPct, runtime) {
|
|
63
|
+
try {
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
const windowMs = 300_000; // 5-minute lookback
|
|
66
|
+
const sinceTs = now - windowMs;
|
|
67
|
+
// Read recent cache_hit_pct and prefix_break samples.
|
|
68
|
+
const hitSamples = readPerfSamples(stateDir, sinceTs, "cache_hit_pct");
|
|
69
|
+
const breakSamples = readPerfSamples(stateDir, sinceTs, "prefix_break");
|
|
70
|
+
// hitRateScore: average of recent cache hit percentages / 100.
|
|
71
|
+
let hitRateAvg = currHitPct;
|
|
72
|
+
if (hitSamples.length > 0) {
|
|
73
|
+
const sum = hitSamples.reduce((a, r) => a + r.value, 0);
|
|
74
|
+
hitRateAvg = sum / hitSamples.length;
|
|
75
|
+
}
|
|
76
|
+
const hitRateScore = Math.max(0, Math.min(1, hitRateAvg / 100));
|
|
77
|
+
// stabilityScore: penalize for frequent prefix breaks.
|
|
78
|
+
// Assume at most 2 breaks in 5min is tolerable.
|
|
79
|
+
const maxExpectedBreaks = 2;
|
|
80
|
+
const stabilityScore = Math.max(0, 1 - breakSamples.length / maxExpectedBreaks);
|
|
81
|
+
// churnScore: if recent breaks have varied causes, penalize.
|
|
82
|
+
const causeVariety = new Set(breakSamples.map((r) => {
|
|
83
|
+
const m = typeof r.meta === "string" ? JSON.parse(r.meta) : null;
|
|
84
|
+
return typeof m?.cause === "string" ? m.cause : "other";
|
|
85
|
+
}));
|
|
86
|
+
const churnPenalty = Math.min(1, causeVariety.size / 4); // 4 possible causes
|
|
87
|
+
const churnScore = 1 - churnPenalty;
|
|
88
|
+
// Composite: hitRate dominates.
|
|
89
|
+
const composite = hitRateScore * 0.5 + stabilityScore * 0.3 + churnScore * 0.2;
|
|
90
|
+
// Emit degradation event when score < 0.6.
|
|
91
|
+
if (composite < 0.6) {
|
|
92
|
+
const prevScore = runtime.rt._lastCacheHealthScore ?? 1;
|
|
93
|
+
runtime.rt._lastCacheHealthScore = composite;
|
|
94
|
+
recordPerfSample(stateDir, "cache_health", composite, {
|
|
95
|
+
hitRateScore: Math.round(hitRateScore * 1000) / 1000,
|
|
96
|
+
stabilityScore: Math.round(stabilityScore * 1000) / 1000,
|
|
97
|
+
churnScore: Math.round(churnScore * 1000) / 1000,
|
|
98
|
+
breakCount: breakSamples.length,
|
|
99
|
+
hitSampleCount: hitSamples.length,
|
|
100
|
+
currHitPct,
|
|
101
|
+
trend: composite < prevScore ? "falling" : "stable",
|
|
102
|
+
}); // guardrails-allow PREVENT-PI-004: local SQLite write (loopback dashboard event)
|
|
103
|
+
// Structured log line for the host to pick up.
|
|
104
|
+
const level = composite < 0.4 ? "error" : "warn";
|
|
105
|
+
// eslint-disable-next-line no-console
|
|
106
|
+
console.log(JSON.stringify({
|
|
107
|
+
ts: now,
|
|
108
|
+
event: `cache_health_${level}`,
|
|
109
|
+
composite: Math.round(composite * 1000) / 1000,
|
|
110
|
+
hitRateAvg: Math.round(hitRateAvg * 10) / 10,
|
|
111
|
+
breakCount: breakSamples.length,
|
|
112
|
+
trend: composite < prevScore ? "falling" : "stable",
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Still healthy — just update the stored score.
|
|
117
|
+
runtime.rt._lastCacheHealthScore = composite;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
/* non-fatal: cache health computation never blocks */
|
|
122
|
+
}
|
|
123
|
+
}
|
|
8
124
|
/** Register perf instrumentation handlers + start the 5s cpu/mem interval. */
|
|
9
125
|
export function registerPerfHandler(pi, runtime) {
|
|
10
126
|
// turn_start: record the wall-clock start of the turn. Using Date.now() (not
|
|
@@ -23,6 +139,7 @@ export function registerPerfHandler(pi, runtime) {
|
|
|
23
139
|
});
|
|
24
140
|
// turn_end: compute turn latency + TPS + cache hit % from the assistant
|
|
25
141
|
// message's usage block. One perf_samples row per metric per turn.
|
|
142
|
+
// S53A: tracks prevHitPct across turns to detect prefix breaks.
|
|
26
143
|
pi.on("turn_end", async (event) => {
|
|
27
144
|
try {
|
|
28
145
|
if (runtime.perfTurnStart > 0) {
|
|
@@ -36,7 +153,22 @@ export function registerPerfHandler(pi, runtime) {
|
|
|
36
153
|
recordPerfSample(runtime.currentStateDir, "tps", u.output / durSec, { outputTokens: u.output });
|
|
37
154
|
const denom = u.cacheRead + u.input + u.cacheWrite;
|
|
38
155
|
const hitPct = denom > 0 ? (u.cacheRead / denom) * 100 : 0;
|
|
39
|
-
recordPerfSample(runtime.currentStateDir, "cache_hit_pct", hitPct, {
|
|
156
|
+
recordPerfSample(runtime.currentStateDir, "cache_hit_pct", hitPct, {
|
|
157
|
+
input: u.input,
|
|
158
|
+
cacheRead: u.cacheRead,
|
|
159
|
+
cacheWrite: u.cacheWrite,
|
|
160
|
+
modelName: runtime.currentModel?.modelName,
|
|
161
|
+
modelId: runtime.currentModel?.modelId,
|
|
162
|
+
});
|
|
163
|
+
// S53A: detect significant cache hit % drop → record prefix_break.
|
|
164
|
+
// Only trigger when we have a previous value AND the drop is >20pp.
|
|
165
|
+
if (runtime.rt._prevCacheHitPct != null &&
|
|
166
|
+
runtime.rt._prevCacheHitPct - hitPct >= 20) {
|
|
167
|
+
const breakAt = runtime.perfTurnStart;
|
|
168
|
+
tryRecordPrefixBreak(runtime.currentStateDir, runtime.rt._prevCacheHitPct, hitPct, breakAt, runtime);
|
|
169
|
+
}
|
|
170
|
+
// Stamp for the next turn's comparison (even when no break fires).
|
|
171
|
+
runtime.rt._prevCacheHitPct = hitPct;
|
|
40
172
|
}
|
|
41
173
|
}
|
|
42
174
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when recall blocks are staged AND the feature flag is ON. Called by the
|
|
3
|
+
* context handler to decide whether to call withRecallTail.
|
|
4
|
+
*/
|
|
5
|
+
export function stagedForTail(runtime, config) {
|
|
6
|
+
if (!config.recallTailInject)
|
|
7
|
+
return false;
|
|
8
|
+
return (runtime.pendingRecallBlock != null ||
|
|
9
|
+
runtime.pendingMemoryRecallBlock != null);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Append staged recall/memory blocks as a single user-role message at the very
|
|
13
|
+
* tail of the messages array. Returns a NEW array (does not mutate input).
|
|
14
|
+
*
|
|
15
|
+
* Sets `runtime.recallInjectedThisTurn = true` when it actually appends, so the
|
|
16
|
+
* turn_end handler (agent-handlers.ts) consumes the staged blocks only if they
|
|
17
|
+
* reached a view this turn. If a turn ends with no context event, the flag
|
|
18
|
+
* stays false and the blocks remain staged for the next turn.
|
|
19
|
+
*
|
|
20
|
+
* Stable timestamp (runtime.perfTurnStart ?? Date.now()) so replays within the
|
|
21
|
+
* turn are byte-identical (mirrors the v0.8.6 summaryAgentMsg rationale).
|
|
22
|
+
*
|
|
23
|
+
* Non-fatal: on any failure, returns messages unmodified and does NOT set the
|
|
24
|
+
* injected flag. PREVENT-PI-003: the injected message has role "user", never
|
|
25
|
+
* "system".
|
|
26
|
+
*/
|
|
27
|
+
export function withRecallTail(messages, runtime, config) {
|
|
28
|
+
if (!config.recallTailInject)
|
|
29
|
+
return messages;
|
|
30
|
+
try {
|
|
31
|
+
const blocks = [];
|
|
32
|
+
if (runtime.pendingRecallBlock != null) {
|
|
33
|
+
blocks.push(runtime.pendingRecallBlock);
|
|
34
|
+
}
|
|
35
|
+
if (runtime.pendingMemoryRecallBlock != null) {
|
|
36
|
+
blocks.push(runtime.pendingMemoryRecallBlock);
|
|
37
|
+
}
|
|
38
|
+
if (blocks.length === 0)
|
|
39
|
+
return messages;
|
|
40
|
+
const tailText = blocks.join("\n\n");
|
|
41
|
+
const timestamp = runtime.perfTurnStart ?? // stable within a turn
|
|
42
|
+
Date.now();
|
|
43
|
+
const tailMsg = {
|
|
44
|
+
role: "user",
|
|
45
|
+
content: tailText,
|
|
46
|
+
timestamp,
|
|
47
|
+
};
|
|
48
|
+
runtime.rt.recallInjectedThisTurn = true;
|
|
49
|
+
// Append at the very tail — never splits toolCall/toolResult.
|
|
50
|
+
// PREVENT-PI-001/002: a single message appended after a complete
|
|
51
|
+
// transcript prefix cannot split a toolCall/toolResult pair.
|
|
52
|
+
return [...messages, tailMsg];
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Non-fatal: leave blocks staged (flag stays unset) and return the
|
|
56
|
+
// unmodified view. The context event still proceeds normally.
|
|
57
|
+
return messages;
|
|
58
|
+
}
|
|
59
|
+
}
|