pi-mega-compact 0.13.1 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/dist/extensions/dashboard-server/api-contracts/embedder-health.js +11 -0
  2. package/dist/extensions/dashboard-server/api-contracts/endpoints/registry.js +225 -0
  3. package/dist/extensions/dashboard-server/api-contracts/endpoints/types.js +1 -0
  4. package/dist/extensions/dashboard-server/api-contracts/endpoints.js +5 -224
  5. package/dist/extensions/dashboard-server/api-contracts/raptor.js +8 -0
  6. package/dist/extensions/dashboard-server/api-contracts.test/_helpers.js +32 -0
  7. package/dist/extensions/dashboard-server/routes-cache.test/_helpers.js +48 -0
  8. package/dist/extensions/dashboard-server/routes-embedder-health.js +67 -0
  9. package/dist/extensions/dashboard-server/routes-raptor.js +88 -0
  10. package/dist/extensions/dashboard-server/routes-setup.js +1 -1
  11. package/dist/extensions/dashboard-server/routes.js +2 -0
  12. package/dist/extensions/dashboard-server/server.js +5 -1
  13. package/dist/extensions/mega-cache-replay.test/_helpers.js +175 -0
  14. package/dist/extensions/mega-commands/dataCommands.js +151 -0
  15. package/dist/extensions/mega-commands/helpers.js +61 -0
  16. package/dist/extensions/mega-commands/historyCommands.js +97 -0
  17. package/dist/extensions/mega-commands/setupCommand.js +200 -0
  18. package/dist/extensions/mega-commands.js +14 -499
  19. package/dist/extensions/mega-compact-s38.test/_helpers.js +194 -0
  20. package/dist/extensions/mega-compact.test/_helpers.js +225 -0
  21. package/dist/extensions/mega-events/agent-handlers/agentEndHandler.js +211 -0
  22. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/cacheStripe.js +49 -0
  23. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/contextHealth.js +33 -0
  24. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry.js +387 -0
  25. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/event.js +7 -0
  26. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/gameScoring.js +55 -0
  27. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/lengthStop.js +12 -0
  28. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/memoryReview.js +22 -0
  29. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.js +38 -0
  30. package/dist/extensions/mega-events/agent-handlers/turnEndHandler.js +36 -0
  31. package/dist/extensions/mega-events/agent-handlers.js +4 -799
  32. package/dist/extensions/mega-events/context-handler/afterCompact.js +163 -0
  33. package/dist/extensions/mega-events/context-handler/messageText.js +12 -0
  34. package/dist/extensions/mega-events/context-handler/tailResult.js +46 -0
  35. package/dist/extensions/mega-events/context-handler.js +11 -198
  36. package/dist/extensions/mega-events/separated-prompt.test/_helpers.js +25 -0
  37. package/dist/src/config.js +2 -0
  38. package/dist/src/dedup-engine.test/_helpers.js +55 -0
  39. package/dist/src/e2e.test/_helpers.js +52 -0
  40. package/dist/src/embedder.js +1 -0
  41. package/dist/src/httpEmbedder.js +6 -0
  42. package/dist/src/hyde.js +146 -0
  43. package/dist/src/importance.test/_helpers.js +5 -0
  44. package/dist/src/memoryGraph/sources/checkpoints.js +78 -0
  45. package/dist/src/memoryGraph/sources/edges.js +17 -0
  46. package/dist/src/memoryGraph/sources/flags.js +22 -0
  47. package/dist/src/memoryGraph/sources/helpers.js +14 -0
  48. package/dist/src/memoryGraph/sources/memories.js +88 -0
  49. package/dist/src/memoryGraph/sources/raptor.js +26 -0
  50. package/dist/src/memoryGraph/sources/turns.js +176 -0
  51. package/dist/src/memoryGraph/sources.js +15 -441
  52. package/dist/src/memoryGraph-gates.test/_helpers.js +120 -0
  53. package/dist/src/ratio.bench.test/_helpers.js +198 -0
  54. package/dist/src/recall/async.js +112 -0
  55. package/dist/src/recall/format.js +149 -0
  56. package/dist/src/recall/memory.js +62 -0
  57. package/dist/src/recall/reformulate.js +82 -0
  58. package/dist/src/recall/sync.js +169 -0
  59. package/dist/src/recall/types.js +1 -0
  60. package/dist/src/recall.js +13 -530
  61. package/dist/src/store/sqlite/schema/core.js +232 -0
  62. package/dist/src/store/sqlite/schema/game.js +65 -0
  63. package/dist/src/store/sqlite/schema/plan-v2.js +107 -0
  64. package/dist/src/store/sqlite/schema/turns.js +59 -0
  65. package/dist/src/store/sqlite/schema.js +8 -441
  66. package/dist/src/store/turns/contract-compliance.test/_helpers.js +21 -0
  67. package/dist/src/store/turns/sqlite-store/admin.js +171 -0
  68. package/dist/src/store/turns/sqlite-store/ctx.js +1 -0
  69. package/dist/src/store/turns/sqlite-store/read.js +111 -0
  70. package/dist/src/store/turns/sqlite-store/rows.js +42 -0
  71. package/dist/src/store/turns/sqlite-store/write.js +102 -0
  72. package/dist/src/store/turns/sqlite-store.js +31 -367
  73. package/dist/src/vectorStore/class.js +356 -0
  74. package/dist/src/vectorStore/hash.js +10 -0
  75. package/dist/src/vectorStore/types.js +2 -0
  76. package/dist/src/vectorStore.js +8 -359
  77. package/dist/src/vectorStore.test/_helpers.js +17 -0
  78. package/extensions/dashboard-client/dist/assets/{CacheTab-8HRroK9K.js → CacheTab-DPRHjYZ0.js} +2 -2
  79. package/extensions/dashboard-client/dist/assets/{CacheTab-8HRroK9K.js.map → CacheTab-DPRHjYZ0.js.map} +1 -1
  80. package/extensions/dashboard-client/dist/assets/{EventsTab-ncobZw-I.js → EventsTab-CgwYq-Qf.js} +2 -2
  81. package/extensions/dashboard-client/dist/assets/{EventsTab-ncobZw-I.js.map → EventsTab-CgwYq-Qf.js.map} +1 -1
  82. package/extensions/dashboard-client/dist/assets/{HealthTab-C6BI1EZG.js → HealthTab-BgOCjVzR.js} +2 -2
  83. package/extensions/dashboard-client/dist/assets/{HealthTab-C6BI1EZG.js.map → HealthTab-BgOCjVzR.js.map} +1 -1
  84. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Yw1yFpsy.js +2 -0
  85. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Yw1yFpsy.js.map +1 -0
  86. package/extensions/dashboard-client/dist/assets/MemoryMapTab-yHUX7Yzi.js +2 -0
  87. package/extensions/dashboard-client/dist/assets/MemoryMapTab-yHUX7Yzi.js.map +1 -0
  88. package/extensions/dashboard-client/dist/assets/{MetricsTab-B88XTCMk.js → MetricsTab-CcH9fP8P.js} +2 -2
  89. package/extensions/dashboard-client/dist/assets/{MetricsTab-B88XTCMk.js.map → MetricsTab-CcH9fP8P.js.map} +1 -1
  90. package/extensions/dashboard-client/dist/assets/{OverviewTab-CGZrSia-.js → OverviewTab-BOs8ZNev.js} +2 -2
  91. package/extensions/dashboard-client/dist/assets/{OverviewTab-CGZrSia-.js.map → OverviewTab-BOs8ZNev.js.map} +1 -1
  92. package/extensions/dashboard-client/dist/assets/{ReposTab-BZo5cNS1.js → ReposTab-B-MfSPvg.js} +2 -2
  93. package/extensions/dashboard-client/dist/assets/{ReposTab-BZo5cNS1.js.map → ReposTab-B-MfSPvg.js.map} +1 -1
  94. package/extensions/dashboard-client/dist/assets/{SessionsTab-C2RokJcw.js → SessionsTab-BmeN649Q.js} +2 -2
  95. package/extensions/dashboard-client/dist/assets/{SessionsTab-C2RokJcw.js.map → SessionsTab-BmeN649Q.js.map} +1 -1
  96. package/extensions/dashboard-client/dist/assets/SetupTab-nOknoph5.js +2 -0
  97. package/extensions/dashboard-client/dist/assets/SetupTab-nOknoph5.js.map +1 -0
  98. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CewYB2aA.js → TimeSavedCard-DnPg0L0z.js} +2 -2
  99. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-CewYB2aA.js.map → TimeSavedCard-DnPg0L0z.js.map} +1 -1
  100. package/extensions/dashboard-client/dist/assets/{TopicsTab-DpVNIuaL.js → TopicsTab-BODHOg_b.js} +2 -2
  101. package/extensions/dashboard-client/dist/assets/{TopicsTab-DpVNIuaL.js.map → TopicsTab-BODHOg_b.js.map} +1 -1
  102. package/extensions/dashboard-client/dist/assets/{TurnsTab-KeuKKVC9.js → TurnsTab-BK0I7hKr.js} +2 -2
  103. package/extensions/dashboard-client/dist/assets/{TurnsTab-KeuKKVC9.js.map → TurnsTab-BK0I7hKr.js.map} +1 -1
  104. package/extensions/dashboard-client/dist/assets/{index-Cucgohga.js → index-mOlljTFI.js} +9 -9
  105. package/extensions/dashboard-client/dist/assets/index-mOlljTFI.js.map +1 -0
  106. package/extensions/dashboard-client/dist/assets/{useSSE-DJsqdPXC.js → useSSE-BYOtv1-3.js} +2 -2
  107. package/extensions/dashboard-client/dist/assets/{useSSE-DJsqdPXC.js.map → useSSE-BYOtv1-3.js.map} +1 -1
  108. package/extensions/dashboard-client/dist/index.html +1 -1
  109. package/extensions/dashboard-client/src/api/client.ts +16 -0
  110. package/extensions/dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx +178 -0
  111. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx +140 -0
  112. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx +109 -0
  113. package/extensions/dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx +98 -0
  114. package/extensions/dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx +108 -0
  115. package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +12 -598
  116. package/extensions/dashboard-client/src/tabs/MemoryMapTab/MemoryMapView.tsx +476 -0
  117. package/extensions/dashboard-client/src/tabs/MemoryMapTab/RaptorTreeView.tsx +163 -0
  118. package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +56 -466
  119. package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderHealthCard.tsx +109 -0
  120. package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderSetup.tsx +468 -0
  121. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +13 -483
  122. package/extensions/dashboard-server/api-contracts/embedder-health.ts +32 -0
  123. package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +360 -0
  124. package/extensions/dashboard-server/api-contracts/endpoints/types.ts +350 -0
  125. package/extensions/dashboard-server/api-contracts/endpoints.ts +27 -648
  126. package/extensions/dashboard-server/api-contracts/index.ts +6 -0
  127. package/extensions/dashboard-server/api-contracts/raptor.ts +49 -0
  128. package/extensions/dashboard-server/api-contracts.test/_helpers.ts +47 -0
  129. package/extensions/dashboard-server/routes-cache.test/_helpers.ts +94 -0
  130. package/extensions/dashboard-server/routes-embedder-health.ts +77 -0
  131. package/extensions/dashboard-server/routes-raptor.ts +128 -0
  132. package/extensions/dashboard-server/routes-setup.ts +1 -1
  133. package/extensions/dashboard-server/routes.ts +2 -0
  134. package/extensions/dashboard-server/server.ts +4 -0
  135. package/extensions/mega-cache-replay.test/_helpers.ts +188 -0
  136. package/extensions/mega-commands/dataCommands.ts +169 -0
  137. package/extensions/mega-commands/helpers.ts +62 -0
  138. package/extensions/mega-commands/historyCommands.ts +117 -0
  139. package/extensions/mega-commands/setupCommand.ts +235 -0
  140. package/extensions/mega-commands.ts +25 -538
  141. package/extensions/mega-compact-s38.test/_helpers.ts +201 -0
  142. package/extensions/mega-compact.test/_helpers.ts +237 -0
  143. package/extensions/mega-events/agent-handlers/agentEndHandler.ts +238 -0
  144. package/extensions/mega-events/agent-handlers/turnEndHandler/cacheStripe.ts +74 -0
  145. package/extensions/mega-events/agent-handlers/turnEndHandler/contextHealth.ts +50 -0
  146. package/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry.ts +421 -0
  147. package/extensions/mega-events/agent-handlers/turnEndHandler/event.ts +16 -0
  148. package/extensions/mega-events/agent-handlers/turnEndHandler/gameScoring.ts +73 -0
  149. package/extensions/mega-events/agent-handlers/turnEndHandler/lengthStop.ts +29 -0
  150. package/extensions/mega-events/agent-handlers/turnEndHandler/memoryReview.ts +42 -0
  151. package/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.ts +61 -0
  152. package/extensions/mega-events/agent-handlers/turnEndHandler.ts +64 -0
  153. package/extensions/mega-events/agent-handlers.ts +15 -861
  154. package/extensions/mega-events/context-handler/afterCompact.ts +213 -0
  155. package/extensions/mega-events/context-handler/messageText.ts +20 -0
  156. package/extensions/mega-events/context-handler/tailResult.ts +63 -0
  157. package/extensions/mega-events/context-handler.ts +17 -235
  158. package/extensions/mega-events/separated-prompt.test/_helpers.ts +35 -0
  159. package/package.json +1 -1
  160. package/src/config.ts +4 -0
  161. package/src/dedup-engine.test/_helpers.ts +79 -0
  162. package/src/e2e.test/_helpers.ts +59 -0
  163. package/src/embedder.ts +4 -0
  164. package/src/httpEmbedder.ts +7 -0
  165. package/src/hyde.ts +162 -0
  166. package/src/importance.test/_helpers.ts +5 -0
  167. package/src/memoryGraph/sources/checkpoints.ts +99 -0
  168. package/src/memoryGraph/sources/edges.ts +23 -0
  169. package/src/memoryGraph/sources/flags.ts +26 -0
  170. package/src/memoryGraph/sources/helpers.ts +13 -0
  171. package/src/memoryGraph/sources/memories.ts +108 -0
  172. package/src/memoryGraph/sources/raptor.ts +37 -0
  173. package/src/memoryGraph/sources/turns.ts +226 -0
  174. package/src/memoryGraph/sources.ts +19 -544
  175. package/src/memoryGraph-gates.test/_helpers.ts +164 -0
  176. package/src/ratio.bench.test/_helpers.ts +268 -0
  177. package/src/recall/async.ts +142 -0
  178. package/src/recall/format.ts +173 -0
  179. package/src/recall/memory.ts +90 -0
  180. package/src/recall/reformulate.ts +111 -0
  181. package/src/recall/sync.ts +196 -0
  182. package/src/recall/types.ts +63 -0
  183. package/src/recall.ts +25 -712
  184. package/src/store/sqlite/schema/core.ts +233 -0
  185. package/src/store/sqlite/schema/game.ts +66 -0
  186. package/src/store/sqlite/schema/plan-v2.ts +108 -0
  187. package/src/store/sqlite/schema/turns.ts +60 -0
  188. package/src/store/sqlite/schema.ts +13 -441
  189. package/src/store/turns/contract-compliance.test/_helpers.ts +38 -0
  190. package/src/store/turns/sqlite-store/admin.ts +249 -0
  191. package/src/store/turns/sqlite-store/ctx.ts +14 -0
  192. package/src/store/turns/sqlite-store/read.ts +174 -0
  193. package/src/store/turns/sqlite-store/rows.ts +55 -0
  194. package/src/store/turns/sqlite-store/write.ts +193 -0
  195. package/src/store/turns/sqlite-store.ts +33 -514
  196. package/src/vectorStore/class.ts +433 -0
  197. package/src/vectorStore/hash.ts +11 -0
  198. package/src/vectorStore/types.ts +58 -0
  199. package/src/vectorStore.test/_helpers.ts +20 -0
  200. package/src/vectorStore.ts +13 -489
  201. package/extensions/dashboard-client/dist/assets/MaintenanceTab-DZi1MnZI.js +0 -2
  202. package/extensions/dashboard-client/dist/assets/MaintenanceTab-DZi1MnZI.js.map +0 -1
  203. package/extensions/dashboard-client/dist/assets/MemoryMapTab-CjUa9zzw.js +0 -2
  204. package/extensions/dashboard-client/dist/assets/MemoryMapTab-CjUa9zzw.js.map +0 -1
  205. package/extensions/dashboard-client/dist/assets/SetupTab-C_BwmebC.js +0 -2
  206. package/extensions/dashboard-client/dist/assets/SetupTab-C_BwmebC.js.map +0 -1
  207. package/extensions/dashboard-client/dist/assets/index-Cucgohga.js.map +0 -1
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Shared helpers for the memoryGraph-gates split files.
3
+ * Extracted from src/memoryGraph-gates.test.ts: temp dirs, node/edge/ws
4
+ * builders, withEnv, and populateStore (real SQLite seeding).
5
+ */
6
+ import { mkdtempSync } from "node:fs";
7
+ import { tmpdir } from "node:os";
8
+ import { join } from "node:path";
9
+ import type { MemoryGraphNode, MemoryGraphEdge } from "../memoryGraph.js";
10
+ import type { GraphWorkingSet } from "../memoryGraph/gates.js";
11
+ import { openStore } from "../store/sqlite/utils.js";
12
+ import { upsertCheckpoint } from "../store/sqlite/checkpoints.js";
13
+ import { recordTurn, newConversationId } from "../store/sqlite/turns.js";
14
+ import { appendRawTranscript } from "../store/sqlite/raw-transcript.js";
15
+ import type { RawTranscriptRow } from "../store/sqlite/raw-transcript.js";
16
+
17
+ export const baseTmp = mkdtempSync(join(tmpdir(), "mc-mgraph-gates-"));
18
+ let counter = 0;
19
+
20
+ export function tmpDir(): string {
21
+ return join(baseTmp, `run-${counter++}`);
22
+ }
23
+
24
+ export function n(
25
+ id: string,
26
+ overrides: Partial<MemoryGraphNode> = {},
27
+ ): MemoryGraphNode {
28
+ return {
29
+ id,
30
+ sessionId: "sess_test",
31
+ label: id,
32
+ summaryTruncated: "",
33
+ tokenEstimate: 0,
34
+ timestamp: 1000,
35
+ dedupStatus: undefined,
36
+ raptorLevel: 0,
37
+ topicSummary: undefined,
38
+ decisionCount: 0,
39
+ textSnippet: "",
40
+ nodeType: "checkpoint",
41
+ epochId: undefined,
42
+ ...overrides,
43
+ };
44
+ }
45
+
46
+ export function e(
47
+ source: string,
48
+ target: string,
49
+ overrides: Partial<MemoryGraphEdge> = {},
50
+ ): MemoryGraphEdge {
51
+ return { source, target, weight: 1.0, type: "temporal", ...overrides };
52
+ }
53
+
54
+ export function ws(
55
+ nodes: MemoryGraphNode[],
56
+ edges: MemoryGraphEdge[] = [],
57
+ ): GraphWorkingSet {
58
+ return { nodes, edges };
59
+ }
60
+
61
+ /** Save and restore env vars around a test. */
62
+ export function withEnv(
63
+ env: Record<string, string | undefined>,
64
+ fn: () => void,
65
+ ): void {
66
+ const saved: Record<string, string | undefined> = {};
67
+ for (const k of Object.keys(env)) {
68
+ saved[k] = process.env[k];
69
+ if (env[k] === undefined) {
70
+ delete process.env[k];
71
+ } else {
72
+ process.env[k] = env[k]!;
73
+ }
74
+ }
75
+ try {
76
+ fn();
77
+ } finally {
78
+ for (const k of Object.keys(env)) {
79
+ if (saved[k] === undefined) {
80
+ delete process.env[k];
81
+ } else {
82
+ process.env[k] = saved[k];
83
+ }
84
+ }
85
+ }
86
+ }
87
+
88
+ export function populateStore(
89
+ dir: string,
90
+ sessionId: string,
91
+ sources: { checkpoint?: boolean; turn?: boolean; turnContent?: boolean; memory?: boolean },
92
+ ): void {
93
+ const db = openStore(dir);
94
+
95
+ if (sources.checkpoint) {
96
+ upsertCheckpoint(
97
+ {
98
+ checkpointId: "chkpt_001",
99
+ sessionId,
100
+ regionHash: "abc",
101
+ contentHash: "def",
102
+ normalizedText: "checkpoint one",
103
+ summary: "Checkpoint One",
104
+ tokenEstimate: 50,
105
+ timestamp: 1000,
106
+ embedding: [],
107
+ keyDecisions: [],
108
+ nextSteps: [],
109
+ filesModified: [],
110
+ },
111
+ dir,
112
+ );
113
+ upsertCheckpoint(
114
+ {
115
+ checkpointId: "chkpt_002",
116
+ sessionId,
117
+ regionHash: "ghi",
118
+ contentHash: "jkl",
119
+ normalizedText: "checkpoint two",
120
+ summary: "Checkpoint Two",
121
+ tokenEstimate: 60,
122
+ timestamp: 2000,
123
+ embedding: [],
124
+ keyDecisions: [],
125
+ nextSteps: [],
126
+ filesModified: [],
127
+ },
128
+ dir,
129
+ );
130
+ }
131
+
132
+ if (sources.turn) {
133
+ const convId = newConversationId();
134
+ recordTurn(
135
+ { conversationId: convId, sessionId, turnIndex: 0, role: "user", endedAt: 1500, ctxTokens: 100 },
136
+ dir,
137
+ );
138
+ recordTurn(
139
+ { conversationId: convId, sessionId, turnIndex: 1, role: "assistant", endedAt: 2500, ctxTokens: 200 },
140
+ dir,
141
+ );
142
+ }
143
+
144
+ if (sources.turnContent) {
145
+ const rows: RawTranscriptRow[] = [
146
+ { sessionId, seq: 0, role: "user", contentBytes: "hello from the user", contentHash: "hash_0", toolName: null, messageTimestamp: 1500, checkpointEpoch: "", turnIndex: 0 },
147
+ { sessionId, seq: 1, role: "assistant", contentBytes: "response from assistant", contentHash: "hash_1", toolName: null, messageTimestamp: 2500, checkpointEpoch: "", turnIndex: 1 },
148
+ ];
149
+ for (const row of rows) {
150
+ appendRawTranscript(db, row);
151
+ }
152
+ }
153
+
154
+ if (sources.memory) {
155
+ db.prepare(
156
+ `INSERT OR IGNORE INTO memories (content, kind, created_at, source_turn)
157
+ VALUES (?, ?, ?, ?)`,
158
+ ).run("remembered fact", "fact", 2000, 0);
159
+ db.prepare(
160
+ `INSERT OR IGNORE INTO memories (content, kind, created_at, source_turn)
161
+ VALUES (?, ?, ?, ?)`,
162
+ ).run("user preference", "preference", 3000, 1);
163
+ }
164
+ }
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Shared helpers for the ratio benchmark split test files.
3
+ *
4
+ * Extracted from ratio.bench.test.ts: message builders, tmp-dir lifecycle,
5
+ * and the generateMessages / generateRealisticConversation / generateNearDuplicates
6
+ * fixtures used across all benchmark describe blocks.
7
+ */
8
+ import * as fs from "node:fs";
9
+ import * as path from "node:path";
10
+ import * as os from "node:os";
11
+ import type { EngineMessage } from "../types.js";
12
+
13
+ export function makeMsg(role: EngineMessage["role"], text: string): EngineMessage {
14
+ return { role, text };
15
+ }
16
+
17
+ export function makeDir(): string {
18
+ return fs.mkdtempSync(path.join(os.tmpdir(), "ratio-bench-"));
19
+ }
20
+
21
+ /** Generate N messages with controlled repetition. */
22
+ export function generateMessages(
23
+ n: number,
24
+ opts: {
25
+ pattern: "unique" | "repetitive" | "mixed" | "code-review" | "debug-session";
26
+ },
27
+ ): EngineMessage[] {
28
+ const msgs: EngineMessage[] = [];
29
+ const templates = [
30
+ "I'm looking at the {file} module. The {component} needs refactoring because {reason}.",
31
+ "Found a bug in {file}: the {component} doesn't handle {case} correctly. Here's the fix: {fix}",
32
+ "Let me check the {file} implementation. The {component} uses {pattern} pattern which is fine for now.",
33
+ "Updated {file} to fix the {component} issue. The {reason} was causing {case}.",
34
+ "Running tests on {file}. The {component} test covers {case} and {fix}. Looks good.",
35
+ "The {component} in {file} needs to handle {case}. Currently it just {fix}.",
36
+ "Reviewed the {file} changes. The {component} refactor looks solid. {reason}.",
37
+ "Deployed the {component} fix to staging. {file} is now handling {case} correctly.",
38
+ ];
39
+
40
+ const files = [
41
+ "src/engine.ts",
42
+ "src/store.ts",
43
+ "src/vectorStore.ts",
44
+ "src/compact.ts",
45
+ "src/recall.ts",
46
+ ];
47
+ const components = [
48
+ "compression",
49
+ "dedup",
50
+ "embedding",
51
+ "search",
52
+ "checkpoint",
53
+ "supersede",
54
+ "recall",
55
+ ];
56
+ const reasons = [
57
+ "performance",
58
+ "correctness",
59
+ "maintainability",
60
+ "edge case handling",
61
+ "type safety",
62
+ ];
63
+ const cases = [
64
+ "empty input",
65
+ "large payloads",
66
+ "concurrent access",
67
+ "unicode content",
68
+ "timeout",
69
+ ];
70
+ const fixes = [
71
+ "added bounds checking",
72
+ "refactored the loop",
73
+ "added error handling",
74
+ "simplified the logic",
75
+ "added unit tests",
76
+ ];
77
+ const patterns = ["singleton", "observer", "strategy", "factory", "builder"];
78
+
79
+ for (let i = 0; i < n; i++) {
80
+ const template = templates[i % templates.length];
81
+ const fill = (s: string): string =>
82
+ s
83
+ .replace("{file}", files[i % files.length])
84
+ .replace("{component}", components[i % components.length])
85
+ .replace("{reason}", reasons[i % reasons.length])
86
+ .replace("{case}", cases[i % cases.length])
87
+ .replace("{fix}", fixes[i % fixes.length])
88
+ .replace("{pattern}", patterns[i % patterns.length]);
89
+
90
+ let text: string;
91
+
92
+ switch (opts.pattern) {
93
+ case "unique":
94
+ text = fill(template) + ` [turn ${i}]`;
95
+ break;
96
+
97
+ case "repetitive":
98
+ text = fill(templates[i % 5]);
99
+ break;
100
+
101
+ case "mixed":
102
+ text =
103
+ i % 5 < 3
104
+ ? fill(template) + ` [turn ${i}]`
105
+ : fill(templates[i % 3]);
106
+ break;
107
+
108
+ case "code-review":
109
+ if (i % 4 === 0) {
110
+ text = `Reviewing PR #${i}: ${fill(template)}`;
111
+ } else if (i % 4 === 1) {
112
+ text = "```typescript\nfunction handle" + components[i % components.length] + "() {\n // " + fill(template) + "\n return result;\n}\n```";
113
+ } else if (i % 4 === 2) {
114
+ text = `LGTM. ${fill(template)} The test coverage looks good.`;
115
+ } else {
116
+ text = `@user requested changes: ${fill(template)}`;
117
+ }
118
+ break;
119
+
120
+ case "debug-session":
121
+ if (i % 6 === 0) {
122
+ text = `Error in ${files[i % files.length]}: TypeError: Cannot read property '${components[i % components.length]}' of undefined`;
123
+ } else if (i % 6 === 1) {
124
+ text = `Stack trace:\n at ${components[i % components.length]} (${files[i % files.length]}:${100 + i}:15)\n at process (${files[(i + 1) % files.length]}:${50 + i}:3)`;
125
+ } else if (i % 6 === 2) {
126
+ text = `The issue is that ${cases[i % cases.length]} isn't being handled. ${fixes[i % fixes.length]}.`;
127
+ } else if (i % 6 === 3) {
128
+ text = `Applied fix: ${fill(template)}`;
129
+ } else if (i % 6 === 4) {
130
+ text = `Tests passing now. ${reasons[i % reasons.length]}.`;
131
+ } else {
132
+ text = `Committed fix for ${components[i % components.length]}. ${fill(template)}`;
133
+ }
134
+ break;
135
+
136
+ default:
137
+ text = fill(template);
138
+ }
139
+
140
+ const role: EngineMessage["role"] = i % 2 === 0 ? "user" : "assistant";
141
+ msgs.push(makeMsg(role, text));
142
+ }
143
+
144
+ return msgs;
145
+ }
146
+
147
+ /** Generate a large realistic conversation with tool reads, code blocks, discussion. */
148
+ export function generateRealisticConversation(turns: number): EngineMessage[] {
149
+ const msgs: EngineMessage[] = [];
150
+ for (let i = 0; i < turns; i++) {
151
+ const phase = i % 8;
152
+ const mods = ["compression", "dedup", "embedding", "search"];
153
+ const fns = ["vectorStore.ts", "engine.ts", "compact.ts", "recall.ts"];
154
+ const issues = ["error handling", "type safety", "performance", "logging"];
155
+ const bugs = ["null pointer", "type mismatch", "race condition", "memory leak"];
156
+
157
+ switch (phase) {
158
+ case 0:
159
+ msgs.push(
160
+ makeMsg(
161
+ "user",
162
+ `I need help with the ${mods[i % 4]} module. It's not working correctly.`,
163
+ ),
164
+ );
165
+ break;
166
+ case 1:
167
+ msgs.push(
168
+ makeMsg(
169
+ "assistant",
170
+ `Let me look at the code. I'll check the ${fns[i % 4]} file.`,
171
+ ),
172
+ );
173
+ break;
174
+ case 2:
175
+ msgs.push(
176
+ makeMsg(
177
+ "tool",
178
+ `File content of src/${["vectorStore", "engine", "compact", "recall"][i % 4]}.ts:\n${"x".repeat(800 + (i % 5) * 200)}\n// Line ${i * 10}: function ${["search", "compact", "embed", "dedup"][i % 4]}() { ... }`,
179
+ ),
180
+ );
181
+ break;
182
+ case 3:
183
+ msgs.push(
184
+ makeMsg(
185
+ "assistant",
186
+ `I found the issue. The ${mods[i % 4]} function doesn't handle edge cases properly. Here's what I suggest:\n\n\`\`\`typescript\nfunction fixed${["Search", "Compact", "Embed", "Dedup"][i % 4]}() {\n if (!input) return null;\n return process(input);\n}\n\`\`\``,
187
+ ),
188
+ );
189
+ break;
190
+ case 4:
191
+ msgs.push(
192
+ makeMsg(
193
+ "user",
194
+ `That looks good. Can you also fix the ${issues[i % 4]} while you're at it?`,
195
+ ),
196
+ );
197
+ break;
198
+ case 5:
199
+ msgs.push(
200
+ makeMsg(
201
+ "assistant",
202
+ `Sure. I've updated the ${issues[i % 4]} as well. The changes affect:\n- src/engine.ts\n- src/store.ts`,
203
+ ),
204
+ );
205
+ break;
206
+ case 6:
207
+ msgs.push(makeMsg("user", "Run the tests to make sure nothing is broken."));
208
+ break;
209
+ case 7:
210
+ msgs.push(
211
+ makeMsg(
212
+ "assistant",
213
+ `All ${150 + i * 3} tests pass. The fix resolved the ${bugs[i % 4]} issue.`,
214
+ ),
215
+ );
216
+ break;
217
+ }
218
+ }
219
+ return msgs;
220
+ }
221
+
222
+ /** Generate near-duplicate messages with controlled edit distance. */
223
+ export function generateNearDuplicates(
224
+ base: string,
225
+ count: number,
226
+ editLevel: "none" | "one-word" | "minor-rephrase" | "major-change",
227
+ ): string[] {
228
+ const results: string[] = [];
229
+ for (let i = 0; i < count; i++) {
230
+ switch (editLevel) {
231
+ case "none":
232
+ results.push(base);
233
+ break;
234
+ case "one-word":
235
+ results.push(
236
+ base.replace(
237
+ "bug",
238
+ ["issue", "defect", "error", "problem"][i % 4],
239
+ ),
240
+ );
241
+ break;
242
+ case "minor-rephrase":
243
+ results.push(
244
+ base
245
+ .replace(
246
+ "Found a bug",
247
+ [
248
+ "Discovered an issue",
249
+ "Spotted a defect",
250
+ "Located an error",
251
+ "Identified a problem",
252
+ ][i % 4],
253
+ )
254
+ .replace(
255
+ "in the",
256
+ ["in the", "within the", "inside the", "in our"][i % 4],
257
+ ),
258
+ );
259
+ break;
260
+ case "major-change":
261
+ results.push(
262
+ `Turn ${i}: ${base.split(" ").reverse().join(" ")}. Additional context: ${"y".repeat(200)}`,
263
+ );
264
+ break;
265
+ }
266
+ }
267
+ return results;
268
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * recall/async.ts — Slice 2 async cross-repo recall.
3
+ *
4
+ * Same dedup/bound/inline contract as `recallAndInline`, but backed by
5
+ * `VectorStore.searchAsync` so it can recall across repos (HNSW NN over the
6
+ * global PGlite index) when `opts.crossRepo` is set. The synchronous
7
+ * `recallAndInline` is unchanged and remains the default per-session path.
8
+ * Inline-window dedupe + token cap (Fix C) apply here too.
9
+ */
10
+ import {
11
+ vectorSearchAsync,
12
+ vectorWasInjected,
13
+ vectorMarkInjected,
14
+ type SearchHit,
15
+ type VectorStore,
16
+ } from "../vectorStore.js";
17
+ import { estimateBlockTokens } from "../tokens.js";
18
+ import { defaultEmbedder, cosineSimilarity } from "../embedder.js";
19
+ import { formatRecallBlock } from "./format.js";
20
+ import type { RecallInjectOptions, RecallInjectResult } from "./types.js";
21
+
22
+ /**
23
+ * `store` must provide `searchAsync` (the live VectorStore does). Errors fall
24
+ * back to an empty result — recall is a bonus, never a hard dependency.
25
+ */
26
+ export async function recallAndInlineAsync(
27
+ opts: RecallInjectOptions & { crossRepo?: boolean; repoId?: string },
28
+ store: VectorStore,
29
+ ): Promise<RecallInjectResult> {
30
+ const limit = opts.limit ?? 3;
31
+ const skip = opts.skipInjected ?? true;
32
+ const maxTokens = opts.recallMaxTokens ?? 0;
33
+ const doWindowDedupe = opts.windowDedupe ?? false;
34
+ const dedupSim = opts.dedupSim ?? 0.9;
35
+
36
+ let hits: SearchHit[] = [];
37
+ try {
38
+ hits = await vectorSearchAsync(store, opts.sessionId, opts.query, limit, {
39
+ crossRepo: opts.crossRepo,
40
+ repoId: opts.repoId,
41
+ });
42
+ } catch {
43
+ hits = [];
44
+ }
45
+
46
+ // F1: hoist one embedder instance for inline dedupe. defaultEmbedder() is
47
+ // deterministic but creating it per call wastes allocations on large hit sets.
48
+ // (recallAndInline already hoisted this; applying the same fix here.)
49
+ const embedder = defaultEmbedder();
50
+ let liveEmbeddings: number[][] = [];
51
+ if (doWindowDedupe && opts.liveWindow && opts.liveWindow.length > 0) {
52
+ liveEmbeddings = opts.liveWindow.map((m) => embedder.embed(m));
53
+ }
54
+
55
+ const toInject: SearchHit[] = [];
56
+ let blockTokens = 0;
57
+
58
+ // F2: when cross-repo is on but no global index dir could be resolved, skip
59
+ // foreign hits rather than injecting them undeduped — otherwise a foreign
60
+ // checkpoint with no machine-wide injected-set to consult would re-inject in
61
+ // every new session. Same-repo hits (no repoId) are unaffected. Warn once so
62
+ // the silent degradation is observable. (The extension resolver normally
63
+ // supplies a default globalIndexDir, so this is belt-and-braces.)
64
+ const skipCrossRepoHits = !!opts.crossRepo && !opts.globalIndexDir;
65
+ if (skipCrossRepoHits) {
66
+ try {
67
+ console.warn(
68
+ "[mega-compact:recall] cross-repo recall enabled but globalIndexDir is unset — " +
69
+ "skipping cross-repo injection to avoid re-injecting undeduped foreign checkpoints",
70
+ );
71
+ } catch {
72
+ /* ignore */
73
+ }
74
+ }
75
+
76
+ for (const h of hits) {
77
+ // F2: skip foreign hits when we can't dedup them machine-wide.
78
+ if (skipCrossRepoHits && h.repoId) continue;
79
+ if (
80
+ skip &&
81
+ vectorWasInjected(store, opts.sessionId, h.checkpoint.checkpointId)
82
+ )
83
+ continue;
84
+ // S18: machine-wide injected-set — a foreign checkpoint already injected
85
+ // (in any session) is never re-injected. Only applies to cross-repo hits
86
+ // (same-repo hits have no repoId and are handled by the per-session set).
87
+ if (opts.globalIndexDir && h.repoId) {
88
+ try {
89
+ const { wasInjectedGlobal } = await import("./../store/sqlite.js");
90
+ if (
91
+ wasInjectedGlobal(
92
+ h.checkpoint.checkpointId,
93
+ opts.sessionId,
94
+ opts.globalIndexDir,
95
+ )
96
+ )
97
+ continue;
98
+ } catch {
99
+ /* non-fatal: degrade to per-session injected-set only */
100
+ }
101
+ }
102
+ // Inline dedupe: skip a hit already resident in the live window (F1: hoisted embedder).
103
+ if (doWindowDedupe && liveEmbeddings.length > 0) {
104
+ const hitVec = embedder.embed(h.checkpoint.summary);
105
+ if (liveEmbeddings.some((v) => cosineSimilarity(v, hitVec) >= dedupSim))
106
+ continue;
107
+ }
108
+ // F3: build the hit list first; format ONCE at the end so the block carries
109
+ // exactly one preamble and numbering [1..n] rather than one per hit.
110
+ const partTokens = estimateBlockTokens(h.checkpoint.summary);
111
+ if (maxTokens > 0 && blockTokens + partTokens > maxTokens) break;
112
+ toInject.push(h);
113
+ blockTokens += partTokens;
114
+ vectorMarkInjected(store, opts.sessionId, h.checkpoint.checkpointId);
115
+ // S18: record the cross-repo injection machine-wide so it's not re-injected
116
+ // by a later recall (same or different session).
117
+ if (opts.globalIndexDir && h.repoId) {
118
+ try {
119
+ const { markInjectedGlobal } = await import("./../store/sqlite.js");
120
+ markInjectedGlobal(
121
+ h.checkpoint.checkpointId,
122
+ h.repoId,
123
+ opts.sessionId,
124
+ opts.globalIndexDir,
125
+ );
126
+ } catch {
127
+ /* non-fatal */
128
+ }
129
+ }
130
+ }
131
+
132
+ // F3: format once — one preamble, correct [1..n] numbering, token cap counted
133
+ // against one preamble (not N). Pass the full toInject array so formatRecallBlock
134
+ // has repoId + score for proper labeling.
135
+ const block = toInject.length > 0 ? formatRecallBlock(toInject) : "";
136
+ const report = toInject.map(
137
+ (h) =>
138
+ ` • ${h.checkpoint.checkpointId} (${h.checkpoint.summary.slice(0, 60).replace(/\n/g, " ")}…)`,
139
+ );
140
+
141
+ return { toInject, report, block, empty: toInject.length === 0 };
142
+ }