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
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { getStateDir } from "../../store.js";
|
|
17
17
|
import { openStore } from "./utils.js";
|
|
18
|
+
import type { PrefixBreak } from "../../prefix-break.js";
|
|
18
19
|
|
|
19
20
|
/** Sample kinds recorded into perf_samples. */
|
|
20
21
|
export type PerfKind =
|
|
@@ -22,6 +23,7 @@ export type PerfKind =
|
|
|
22
23
|
| "provider_latency_ms"
|
|
23
24
|
| "tps"
|
|
24
25
|
| "cache_hit_pct"
|
|
26
|
+
| "prefix_break"
|
|
25
27
|
| "rss_mb"
|
|
26
28
|
| "heap_mb"
|
|
27
29
|
| "cpu_user_ms"
|
|
@@ -35,6 +37,7 @@ export const PERF_KINDS: readonly PerfKind[] = [
|
|
|
35
37
|
"provider_latency_ms",
|
|
36
38
|
"tps",
|
|
37
39
|
"cache_hit_pct",
|
|
40
|
+
"prefix_break",
|
|
38
41
|
"rss_mb",
|
|
39
42
|
"heap_mb",
|
|
40
43
|
"cpu_user_ms",
|
|
@@ -137,30 +140,69 @@ export interface ProviderCacheLifetime {
|
|
|
137
140
|
firstSampleAt: string | null;
|
|
138
141
|
/** ISO timestamp of the most recent sample, or null if no samples. */
|
|
139
142
|
latestSampleAt: string | null;
|
|
143
|
+
/**
|
|
144
|
+
* Per-model breakdown of aggregates (F4). Samples without a model tag are
|
|
145
|
+
* omitted from this array (they remain in the flat totals above).
|
|
146
|
+
*/
|
|
147
|
+
byModel: ProviderCacheModelAgg[];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Per-model provider cache aggregate (F4). */
|
|
151
|
+
export interface ProviderCacheModelAgg {
|
|
152
|
+
/** Model label (modelName || modelId from meta, or null for untagged). */
|
|
153
|
+
model: string;
|
|
154
|
+
/** Average hit rate for this model (0-100). */
|
|
155
|
+
hitPct: number;
|
|
156
|
+
/** Sum of cache-read tokens for this model. */
|
|
157
|
+
totalCacheRead: number;
|
|
158
|
+
/** Sum of cache-write tokens for this model. */
|
|
159
|
+
totalCacheWrite: number;
|
|
160
|
+
/** Number of samples for this model. */
|
|
161
|
+
sampleCount: number;
|
|
140
162
|
}
|
|
141
163
|
|
|
142
164
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* Aggregates `cache_creation_input_tokens` / `cache_read_input_tokens` from
|
|
146
|
-
* the `meta` JSON column of `cache_hit_pct` samples (`json_extract`,
|
|
147
|
-
* available in node:sqlite ≥22.13 / SQLite ≥3.38). Returns a zeroed
|
|
148
|
-
* `ProviderCacheLifetime` when no samples exist (never undefined).
|
|
165
|
+
* Extract a model label from parsed meta (modelName or modelId), or null.
|
|
149
166
|
*/
|
|
150
|
-
|
|
151
|
-
|
|
167
|
+
function extractModelLabel(
|
|
168
|
+
meta: Record<string, unknown>,
|
|
169
|
+
): string | null {
|
|
170
|
+
if (typeof meta.modelName === "string" && meta.modelName.length > 0)
|
|
171
|
+
return meta.modelName;
|
|
172
|
+
if (typeof meta.modelId === "string" && meta.modelId.length > 0)
|
|
173
|
+
return meta.modelId;
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Build sorted byModel array from the model aggregation map.
|
|
179
|
+
*/
|
|
180
|
+
function byModelFromMap(
|
|
181
|
+
map: Map<string, { sumCr: number; sumCw: number; sumInp: number; sumHitPct: number; hitCount: number; samples: number }>,
|
|
182
|
+
): ProviderCacheModelAgg[] {
|
|
183
|
+
const out: ProviderCacheModelAgg[] = [];
|
|
184
|
+
for (const [model, m] of map) {
|
|
185
|
+
out.push({
|
|
186
|
+
model,
|
|
187
|
+
hitPct: m.hitCount > 0 ? m.sumHitPct / m.hitCount : 0,
|
|
188
|
+
totalCacheRead: m.sumCr,
|
|
189
|
+
totalCacheWrite: m.sumCw,
|
|
190
|
+
sampleCount: m.samples,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
out.sort((a, b) => b.sampleCount - a.sampleCount);
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Aggregate `cache_hit_pct` rows (ts + parsed meta) into a
|
|
199
|
+
* `ProviderCacheLifetime`. Shared by the lifetime and window readers so both
|
|
200
|
+
* use the identical aggregation formula. `meta` is parsed defensively
|
|
201
|
+
* (PREVENT-001 null-safe). Rows must be ascending by ts.
|
|
202
|
+
*/
|
|
203
|
+
function aggregateCacheRows(
|
|
204
|
+
rows: Array<{ ts: number; meta: string | null }>,
|
|
152
205
|
): ProviderCacheLifetime {
|
|
153
|
-
const db = openStore(stateDir);
|
|
154
|
-
const rows = db
|
|
155
|
-
.prepare(
|
|
156
|
-
`SELECT ts, meta FROM perf_samples
|
|
157
|
-
WHERE kind = ?
|
|
158
|
-
ORDER BY ts ASC`,
|
|
159
|
-
)
|
|
160
|
-
.all("cache_hit_pct") as Array<{
|
|
161
|
-
ts: number;
|
|
162
|
-
meta: string | null;
|
|
163
|
-
}>;
|
|
164
206
|
if (rows.length === 0) {
|
|
165
207
|
return {
|
|
166
208
|
sampleCount: 0,
|
|
@@ -170,6 +212,7 @@ export function readProviderCacheLifetime(
|
|
|
170
212
|
totalInput: 0,
|
|
171
213
|
firstSampleAt: null,
|
|
172
214
|
latestSampleAt: null,
|
|
215
|
+
byModel: [],
|
|
173
216
|
};
|
|
174
217
|
}
|
|
175
218
|
let totalCacheRead = 0;
|
|
@@ -177,6 +220,17 @@ export function readProviderCacheLifetime(
|
|
|
177
220
|
let totalInput = 0;
|
|
178
221
|
let sumHitPct = 0;
|
|
179
222
|
let hitPctCount = 0;
|
|
223
|
+
const modelMap = new Map<
|
|
224
|
+
string,
|
|
225
|
+
{
|
|
226
|
+
sumCr: number;
|
|
227
|
+
sumCw: number;
|
|
228
|
+
sumInp: number;
|
|
229
|
+
sumHitPct: number;
|
|
230
|
+
hitCount: number;
|
|
231
|
+
samples: number;
|
|
232
|
+
}
|
|
233
|
+
>();
|
|
180
234
|
for (const r of rows) {
|
|
181
235
|
if (r.meta != null) {
|
|
182
236
|
let meta: Record<string, unknown> | null = null;
|
|
@@ -208,6 +262,31 @@ export function readProviderCacheLifetime(
|
|
|
208
262
|
sumHitPct += (cr / denom) * 100;
|
|
209
263
|
hitPctCount++;
|
|
210
264
|
}
|
|
265
|
+
// Per-model grouping (F4)
|
|
266
|
+
const modelLabel = extractModelLabel(meta);
|
|
267
|
+
if (modelLabel != null) {
|
|
268
|
+
let m = modelMap.get(modelLabel);
|
|
269
|
+
if (!m) {
|
|
270
|
+
m = {
|
|
271
|
+
sumCr: 0,
|
|
272
|
+
sumCw: 0,
|
|
273
|
+
sumInp: 0,
|
|
274
|
+
sumHitPct: 0,
|
|
275
|
+
hitCount: 0,
|
|
276
|
+
samples: 0,
|
|
277
|
+
};
|
|
278
|
+
modelMap.set(modelLabel, m);
|
|
279
|
+
}
|
|
280
|
+
m.sumCr += cr;
|
|
281
|
+
m.sumCw += cw;
|
|
282
|
+
m.sumInp += inp;
|
|
283
|
+
m.samples++;
|
|
284
|
+
if (denom > 0) {
|
|
285
|
+
m.sumHitPct += (cr / denom) * 100;
|
|
286
|
+
m.hitCount++;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// Untagged samples are omitted from byModel
|
|
211
290
|
}
|
|
212
291
|
}
|
|
213
292
|
}
|
|
@@ -219,9 +298,62 @@ export function readProviderCacheLifetime(
|
|
|
219
298
|
totalInput,
|
|
220
299
|
firstSampleAt: new Date(rows[0].ts).toISOString(),
|
|
221
300
|
latestSampleAt: new Date(rows[rows.length - 1].ts).toISOString(),
|
|
301
|
+
byModel: byModelFromMap(modelMap),
|
|
222
302
|
};
|
|
223
303
|
}
|
|
224
304
|
|
|
305
|
+
/**
|
|
306
|
+
* Read lifetime provider prompt cache aggregates from `perf_samples`.
|
|
307
|
+
*
|
|
308
|
+
* Aggregates `cache_creation_input_tokens` / `cache_read_input_tokens` from
|
|
309
|
+
* the `meta` JSON column of `cache_hit_pct` samples (`json_extract`,
|
|
310
|
+
* available in node:sqlite >=22.13 / SQLite >=3.38). Returns a zeroed
|
|
311
|
+
* `ProviderCacheLifetime` when no samples exist (never undefined).
|
|
312
|
+
*/
|
|
313
|
+
export function readProviderCacheLifetime(
|
|
314
|
+
stateDir: string = getStateDir(),
|
|
315
|
+
): ProviderCacheLifetime {
|
|
316
|
+
const db = openStore(stateDir);
|
|
317
|
+
const rows = db
|
|
318
|
+
.prepare(
|
|
319
|
+
`SELECT ts, meta FROM perf_samples
|
|
320
|
+
WHERE kind = ?
|
|
321
|
+
ORDER BY ts ASC`,
|
|
322
|
+
)
|
|
323
|
+
.all("cache_hit_pct") as Array<{
|
|
324
|
+
ts: number;
|
|
325
|
+
meta: string | null;
|
|
326
|
+
}>;
|
|
327
|
+
return aggregateCacheRows(rows);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Read provider prompt cache aggregates over a trailing window of `minutes`
|
|
332
|
+
* (samples with ts >= now - minutes*60_000). Same aggregate shape as
|
|
333
|
+
* `readProviderCacheLifetime`; the existing lifetime reader is untouched.
|
|
334
|
+
* Returns a zeroed `ProviderCacheLifetime` when no samples fall in the window.
|
|
335
|
+
*/
|
|
336
|
+
export function readProviderCacheWindow(
|
|
337
|
+
stateDir: string = getStateDir(),
|
|
338
|
+
minutes: number,
|
|
339
|
+
): ProviderCacheLifetime {
|
|
340
|
+
const db = openStore(stateDir);
|
|
341
|
+
const sinceTs = Date.now() - minutes * 60_000;
|
|
342
|
+
const rows = db
|
|
343
|
+
.prepare(
|
|
344
|
+
`SELECT ts, meta FROM perf_samples
|
|
345
|
+
WHERE kind = ? AND ts >= ?
|
|
346
|
+
ORDER BY ts ASC`,
|
|
347
|
+
)
|
|
348
|
+
.all("cache_hit_pct", sinceTs) as Array<{
|
|
349
|
+
ts: number;
|
|
350
|
+
meta: string | null;
|
|
351
|
+
}>;
|
|
352
|
+
return aggregateCacheRows(rows);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ... keep every function below unchanged ...
|
|
356
|
+
|
|
225
357
|
/**
|
|
226
358
|
* Read the most recent `cache_hit_pct` value from `perf_samples`.
|
|
227
359
|
*
|
|
@@ -236,9 +368,9 @@ export function readLatestCacheHitPct(
|
|
|
236
368
|
const row = db
|
|
237
369
|
.prepare(
|
|
238
370
|
`SELECT value FROM perf_samples
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
371
|
+
WHERE kind = ?
|
|
372
|
+
ORDER BY ts DESC, id DESC
|
|
373
|
+
LIMIT 1`,
|
|
242
374
|
)
|
|
243
375
|
.get("cache_hit_pct") as { value: number } | undefined;
|
|
244
376
|
if (!row || typeof row.value !== "number" || !Number.isFinite(row.value))
|
|
@@ -259,3 +391,64 @@ export function readProviderCacheForRepo(
|
|
|
259
391
|
): ProviderCacheLifetime {
|
|
260
392
|
return readProviderCacheLifetime(stateDir);
|
|
261
393
|
}
|
|
394
|
+
|
|
395
|
+
// ─── Prefix-break reader (S53A) ───────────────────────────────────────────────
|
|
396
|
+
|
|
397
|
+
/** Re-exported for consumers that import via perf-samples. */
|
|
398
|
+
export type { PrefixBreak } from "../../prefix-break.js";
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Read `prefix_break` samples in a time window.
|
|
402
|
+
*
|
|
403
|
+
* @param stateDir State directory for this repo.
|
|
404
|
+
* @param sinceTs Lower bound (epoch ms). Pass 0 for no lower bound.
|
|
405
|
+
* @param untilTs Upper bound (epoch ms). Pass 0 for no upper bound.
|
|
406
|
+
* Rows with ts > untilTs are excluded.
|
|
407
|
+
* @returns Sorted ascending array of PrefixBreak rows.
|
|
408
|
+
*
|
|
409
|
+
* SQL is fully parameterized (PREVENT-002). `meta` is parsed defensively
|
|
410
|
+
* (PREVENT-001 null-safe). When no rows exist the return is an empty array.
|
|
411
|
+
*/
|
|
412
|
+
export function readPrefixBreaks(
|
|
413
|
+
stateDir: string = getStateDir(),
|
|
414
|
+
sinceTs: number = 0,
|
|
415
|
+
untilTs: number = 0,
|
|
416
|
+
): PrefixBreak[] {
|
|
417
|
+
const db = openStore(stateDir);
|
|
418
|
+
let rows: Array<{
|
|
419
|
+
id: number;
|
|
420
|
+
ts: number;
|
|
421
|
+
value: number;
|
|
422
|
+
meta: string | null;
|
|
423
|
+
}>;
|
|
424
|
+
|
|
425
|
+
if (untilTs > 0) {
|
|
426
|
+
const sql = `SELECT id, ts, value, meta FROM perf_samples
|
|
427
|
+
WHERE kind = ? AND ts >= ? AND ts <= ? ORDER BY ts ASC`;
|
|
428
|
+
rows = db.prepare(sql).all("prefix_break", sinceTs, untilTs) as typeof rows;
|
|
429
|
+
} else {
|
|
430
|
+
const sql = `SELECT id, ts, value, meta FROM perf_samples
|
|
431
|
+
WHERE kind = ? AND ts >= ? ORDER BY ts ASC`;
|
|
432
|
+
rows = db.prepare(sql).all("prefix_break", sinceTs) as typeof rows;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const out: PrefixBreak[] = [];
|
|
436
|
+
for (const r of rows) {
|
|
437
|
+
let meta: unknown = null;
|
|
438
|
+
if (r.meta != null) {
|
|
439
|
+
try {
|
|
440
|
+
meta = JSON.parse(r.meta);
|
|
441
|
+
} catch {
|
|
442
|
+
meta = null;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
out.push({
|
|
446
|
+
id: r.id,
|
|
447
|
+
ts: r.ts,
|
|
448
|
+
kind: "prefix_break",
|
|
449
|
+
value: r.value,
|
|
450
|
+
meta: meta as PrefixBreak["meta"],
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
return out;
|
|
454
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { randomBytes } from "node:crypto";
|
|
11
|
-
import type
|
|
11
|
+
import { DuplicateTurnError, type
|
|
12
12
|
TurnStore,
|
|
13
13
|
TurnReader,
|
|
14
14
|
TurnWriter,
|
|
@@ -198,6 +198,15 @@ export class InMemoryTurnStore implements TurnStore {
|
|
|
198
198
|
// ── TurnWriter ──────────────────────────────────────────────
|
|
199
199
|
|
|
200
200
|
appendTurn(entry: TurnEntry): TurnId {
|
|
201
|
+
// Check for duplicate (conversationId, turnIndex)
|
|
202
|
+
for (const [, row] of this.turns) {
|
|
203
|
+
if (
|
|
204
|
+
row.entry.conversationId === entry.conversationId &&
|
|
205
|
+
row.entry.turnIndex === entry.turnIndex
|
|
206
|
+
) {
|
|
207
|
+
throw new DuplicateTurnError(entry.conversationId, entry.turnIndex);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
201
210
|
const id = allocId();
|
|
202
211
|
const sid = normalizeSessionId(entry.sessionId);
|
|
203
212
|
const normalized = { ...entry, sessionId: sid };
|
|
@@ -33,6 +33,7 @@ import type {
|
|
|
33
33
|
ConversationStats,
|
|
34
34
|
TurnStoreOptions,
|
|
35
35
|
} from "./types.js";
|
|
36
|
+
import { DuplicateTurnError } from "./types.js";
|
|
36
37
|
|
|
37
38
|
// ─── Helpers ──────────────────────────────────────────────────────
|
|
38
39
|
|
|
@@ -237,23 +238,34 @@ export class SqliteTurnStore implements TurnStore {
|
|
|
237
238
|
appendTurn(entry: TurnEntry): TurnId {
|
|
238
239
|
const sid = normalizeSessionId(entry.sessionId);
|
|
239
240
|
this.persistSessionConv(sid, entry.conversationId);
|
|
240
|
-
|
|
241
|
-
.
|
|
242
|
-
|
|
241
|
+
try {
|
|
242
|
+
this.db
|
|
243
|
+
.prepare(
|
|
244
|
+
`INSERT INTO turns (conversation_id, session_id, turn_index, role, ended_at,
|
|
243
245
|
ctx_tokens, ctx_percent, pressure_band, model)
|
|
244
246
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
247
|
+
)
|
|
248
|
+
.run(
|
|
249
|
+
entry.conversationId,
|
|
250
|
+
sid,
|
|
251
|
+
entry.turnIndex,
|
|
252
|
+
entry.role,
|
|
253
|
+
entry.endedAt,
|
|
254
|
+
entry.ctxTokens ?? null,
|
|
255
|
+
entry.ctxPercent ?? null,
|
|
256
|
+
entry.pressureBand ?? null,
|
|
257
|
+
entry.model ?? null,
|
|
258
|
+
);
|
|
259
|
+
} catch (e) {
|
|
260
|
+
if (
|
|
261
|
+
e instanceof Error &&
|
|
262
|
+
e.message.includes("UNIQUE constraint") &&
|
|
263
|
+
(e as { code?: string }).code === "ERR_SQLITE_ERROR"
|
|
264
|
+
) {
|
|
265
|
+
throw new DuplicateTurnError(entry.conversationId, entry.turnIndex);
|
|
266
|
+
}
|
|
267
|
+
throw e;
|
|
268
|
+
}
|
|
257
269
|
const row = this.db
|
|
258
270
|
.prepare(
|
|
259
271
|
"SELECT id FROM turns WHERE conversation_id = ? AND turn_index = ?",
|
package/src/store/turns/types.ts
CHANGED
|
@@ -16,6 +16,27 @@
|
|
|
16
16
|
|
|
17
17
|
// ─── Domain types ───────────────────────────────────────────────────
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Thrown when appendTurn is called with a (conversationId, turnIndex) pair
|
|
21
|
+
* that already exists. Both backends must throw this in lieu of a raw
|
|
22
|
+
* SQLite error (SqliteTurnStore) or silent success (InMemoryTurnStore).
|
|
23
|
+
*/
|
|
24
|
+
export class DuplicateTurnError extends Error {
|
|
25
|
+
/** The conversation that already has this turn index. */
|
|
26
|
+
readonly conversationId: ConversationId;
|
|
27
|
+
/** The duplicate turn index. */
|
|
28
|
+
readonly turnIndex: number;
|
|
29
|
+
|
|
30
|
+
constructor(conversationId: ConversationId, turnIndex: number) {
|
|
31
|
+
super(
|
|
32
|
+
`Duplicate turn: conversation "${conversationId}" already has turnIndex ${turnIndex}`,
|
|
33
|
+
);
|
|
34
|
+
this.name = "DuplicateTurnError";
|
|
35
|
+
this.conversationId = conversationId;
|
|
36
|
+
this.turnIndex = turnIndex;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
19
40
|
/** Unique turn identifier (string to stay backend-agnostic). */
|
|
20
41
|
export type TurnId = string;
|
|
21
42
|
|
package/src/store/vectorIndex.ts
CHANGED
|
@@ -288,13 +288,42 @@ export async function searchAsync(
|
|
|
288
288
|
}
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
/** Default cap for the WASM close (PGlite can stall ~40 min on teardown). */
|
|
292
|
+
export const DEFAULT_CLOSE_TIMEOUT_MS = 5_000;
|
|
293
|
+
|
|
294
|
+
function closeTimeoutMs(): number {
|
|
295
|
+
const raw = process.env.MEGACOMPACT_PGLITE_CLOSE_TIMEOUT_MS;
|
|
296
|
+
if (raw) {
|
|
297
|
+
const n = Number(raw);
|
|
298
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
299
|
+
}
|
|
300
|
+
return DEFAULT_CLOSE_TIMEOUT_MS;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Race a promise against a timeout. If the inner promise never settles, the
|
|
305
|
+
* timer fires, resolves with `undefined`, and the caller proceeds. This mirrors
|
|
306
|
+
* `withOpenTimeout` (pgOpenGuard.ts) but is local to the close path.
|
|
307
|
+
*/
|
|
308
|
+
function withCloseTimeout<T>(
|
|
309
|
+
promise: Promise<T>,
|
|
310
|
+
timeoutMs: number,
|
|
311
|
+
): Promise<T | undefined> {
|
|
312
|
+
return Promise.race([
|
|
313
|
+
promise,
|
|
314
|
+
new Promise<undefined>((resolve) => setTimeout(() => resolve(undefined), timeoutMs)),
|
|
315
|
+
]);
|
|
316
|
+
}
|
|
317
|
+
|
|
291
318
|
/** Close the index (test teardown / shutdown). Safe to call when unopened. */
|
|
292
319
|
export async function closeVectorIndex(): Promise<void> {
|
|
293
320
|
if (db) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
321
|
+
const closePromise = db.close().catch(() => {});
|
|
322
|
+
const result = await withCloseTimeout(closePromise, closeTimeoutMs());
|
|
323
|
+
if (result === undefined) {
|
|
324
|
+
logWarn(
|
|
325
|
+
`close timed out after ${closeTimeoutMs()}ms (WASM teardown stalled; OS will reclaim)`,
|
|
326
|
+
);
|
|
298
327
|
}
|
|
299
328
|
}
|
|
300
329
|
db = undefined;
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* conflict-scan.test.ts — unit tests for the extension-conflict scanner.
|
|
3
|
-
*
|
|
4
|
-
* Fixture trees are written under a temp dir and scanned via
|
|
5
|
-
* MEGACOMPACT_EXT_SCAN_DIR (which makes collectScanRoots() return that
|
|
6
|
-
* single root). This covers the S24 follow-up fix:
|
|
7
|
-
*
|
|
8
|
-
* 1. node_modules-style code extensions (package.json + pi.extensions) are
|
|
9
|
-
* still detected by source-marker grep (regression).
|
|
10
|
-
* 2. USER-LEVEL extensions installed outside npm (e.g. pi-hermes-memory)
|
|
11
|
-
* now get scanned too — previously only `node_modules` was walked, so a
|
|
12
|
-
* data-only memory store (MEMORY.md + sessions.db, no package.json)
|
|
13
|
-
* was never flagged (the 5000-char file-buffer error slipped through).
|
|
14
|
-
* 3. The data-only memory-store signature is detected even with no source.
|
|
15
|
-
* 4. pi-mega-compact (selfName) is always skipped.
|
|
16
|
-
*/
|
|
17
|
-
import { test, after } from "node:test";
|
|
18
|
-
import assert from "node:assert/strict";
|
|
19
|
-
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
|
|
20
|
-
import { tmpdir } from "node:os";
|
|
21
|
-
import { join } from "node:path";
|
|
22
|
-
import { detectConflicts, collectScanRoots } from "./conflict-scan.js";
|
|
23
|
-
const base = mkdtempSync(join(tmpdir(), "mc-scan-"));
|
|
24
|
-
let n = 0;
|
|
25
|
-
/** Make a fixture root containing one or more fake extensions, return its path. */
|
|
26
|
-
function fixture(build) {
|
|
27
|
-
const root = join(base, `case-${n++}`);
|
|
28
|
-
mkdirSync(root, { recursive: true });
|
|
29
|
-
build(root);
|
|
30
|
-
return root;
|
|
31
|
-
}
|
|
32
|
-
after(() => {
|
|
33
|
-
rmSync(base, { recursive: true, force: true });
|
|
34
|
-
});
|
|
35
|
-
test("scans a user-level, data-only memory store (no package.json)", () => {
|
|
36
|
-
const root = fixture((r) => {
|
|
37
|
-
const ext = join(r, "pi-hermes-memory");
|
|
38
|
-
mkdirSync(ext, { recursive: true });
|
|
39
|
-
// No package.json, no source — just pi's memory-store signature.
|
|
40
|
-
writeFileSync(join(ext, "MEMORY.md"), "# memory\n");
|
|
41
|
-
writeFileSync(join(ext, "sessions.db"), "");
|
|
42
|
-
});
|
|
43
|
-
process.env.MEGACOMPACT_EXT_SCAN_DIR = root;
|
|
44
|
-
try {
|
|
45
|
-
const { conflicts } = detectConflicts();
|
|
46
|
-
assert.ok(conflicts.length >= 1, "expected a memory conflict");
|
|
47
|
-
const hit = conflicts.find((c) => c.kind === "memory");
|
|
48
|
-
assert.ok(hit, "expected a memory-kind conflict");
|
|
49
|
-
assert.equal(hit.severity, "high");
|
|
50
|
-
assert.ok(hit.evidence.includes("MEMORY.md") ||
|
|
51
|
-
hit.evidence.includes("sessions.db"), "evidence should name the on-disk memory signature");
|
|
52
|
-
}
|
|
53
|
-
finally {
|
|
54
|
-
delete process.env.MEGACOMPACT_EXT_SCAN_DIR;
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
test("still detects a code extension by source marker (regression)", () => {
|
|
58
|
-
const root = fixture((r) => {
|
|
59
|
-
// A code extension is a DIRECT child of the scan root (mirrors the
|
|
60
|
-
// node_modules layout: packages live one level under the root).
|
|
61
|
-
const ext = join(r, "some-memory-ext");
|
|
62
|
-
mkdirSync(ext, { recursive: true });
|
|
63
|
-
writeFileSync(join(ext, "package.json"), JSON.stringify({ name: "some-memory-ext", pi: { extensions: ["x.ts"] } }));
|
|
64
|
-
writeFileSync(join(ext, "index.ts"), "export const MEMORY_TOOL = true;");
|
|
65
|
-
});
|
|
66
|
-
process.env.MEGACOMPACT_EXT_SCAN_DIR = root;
|
|
67
|
-
try {
|
|
68
|
-
const { conflicts } = detectConflicts();
|
|
69
|
-
const hit = conflicts.find((c) => c.package === "some-memory-ext");
|
|
70
|
-
assert.ok(hit, "expected some-memory-ext to be flagged");
|
|
71
|
-
assert.equal(hit.kind, "memory");
|
|
72
|
-
}
|
|
73
|
-
finally {
|
|
74
|
-
delete process.env.MEGACOMPACT_EXT_SCAN_DIR;
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
test("skips pi-mega-compact (selfName) and non-extension dirs", () => {
|
|
78
|
-
const root = fixture((r) => {
|
|
79
|
-
// selfName dir with a memory signature — must be ignored.
|
|
80
|
-
const me = join(r, "node_modules", "pi-mega-compact");
|
|
81
|
-
mkdirSync(me, { recursive: true });
|
|
82
|
-
writeFileSync(join(me, "sessions.db"), "");
|
|
83
|
-
// unrelated dir with no pi.extensions and no memory signature.
|
|
84
|
-
mkdirSync(join(r, "node_modules", "totally-fine"), { recursive: true });
|
|
85
|
-
});
|
|
86
|
-
process.env.MEGACOMPACT_EXT_SCAN_DIR = root;
|
|
87
|
-
try {
|
|
88
|
-
const { scanned, conflicts } = detectConflicts();
|
|
89
|
-
assert.equal(conflicts.length, 0, "no conflicts expected");
|
|
90
|
-
assert.ok(!scanned.some((s) => s.includes("pi-mega-compact")), "selfName should not appear in scanned");
|
|
91
|
-
}
|
|
92
|
-
finally {
|
|
93
|
-
delete process.env.MEGACOMPACT_EXT_SCAN_DIR;
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
test("collectScanRoots honors MEGACOMPACT_EXT_SCAN_DIR override", () => {
|
|
97
|
-
const root = fixture(() => { });
|
|
98
|
-
process.env.MEGACOMPACT_EXT_SCAN_DIR = root;
|
|
99
|
-
try {
|
|
100
|
-
const roots = collectScanRoots();
|
|
101
|
-
assert.deepEqual(roots, [root], "override replaces the whole root list");
|
|
102
|
-
}
|
|
103
|
-
finally {
|
|
104
|
-
delete process.env.MEGACOMPACT_EXT_SCAN_DIR;
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
test("collectScanRoots falls back to node_modules + user dir when no override", () => {
|
|
108
|
-
delete process.env.MEGACOMPACT_EXT_SCAN_DIR;
|
|
109
|
-
delete process.env.MEGACOMPACT_EXT_USER_DIR;
|
|
110
|
-
// No override set and this test file lives under extensions/, so node_modules
|
|
111
|
-
// resolution walks up from here; the user dir (~/.pi/agent) may or may
|
|
112
|
-
// not exist in CI. We only assert the call returns a non-throwing array.
|
|
113
|
-
const roots = collectScanRoots();
|
|
114
|
-
assert.ok(Array.isArray(roots), "collectScanRoots must return an array");
|
|
115
|
-
});
|