pi-mega-compact 0.13.0 → 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 (212) 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.js +12 -2
  21. package/dist/extensions/mega-compact.test/_helpers.js +225 -0
  22. package/dist/extensions/mega-events/agent-handlers/agentEndHandler.js +211 -0
  23. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/cacheStripe.js +49 -0
  24. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/contextHealth.js +33 -0
  25. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry.js +387 -0
  26. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/event.js +7 -0
  27. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/gameScoring.js +55 -0
  28. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/lengthStop.js +12 -0
  29. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/memoryReview.js +22 -0
  30. package/dist/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.js +38 -0
  31. package/dist/extensions/mega-events/agent-handlers/turnEndHandler.js +36 -0
  32. package/dist/extensions/mega-events/agent-handlers.js +4 -799
  33. package/dist/extensions/mega-events/context-handler/afterCompact.js +163 -0
  34. package/dist/extensions/mega-events/context-handler/messageText.js +12 -0
  35. package/dist/extensions/mega-events/context-handler/tailResult.js +46 -0
  36. package/dist/extensions/mega-events/context-handler.js +11 -198
  37. package/dist/extensions/mega-events/separated-prompt.test/_helpers.js +25 -0
  38. package/dist/src/config.js +2 -0
  39. package/dist/src/dedup-engine.test/_helpers.js +55 -0
  40. package/dist/src/e2e.test/_helpers.js +52 -0
  41. package/dist/src/embedder.js +1 -0
  42. package/dist/src/httpEmbedder.js +6 -0
  43. package/dist/src/hyde.js +146 -0
  44. package/dist/src/importance.test/_helpers.js +5 -0
  45. package/dist/src/memoryGraph/sources/checkpoints.js +78 -0
  46. package/dist/src/memoryGraph/sources/edges.js +17 -0
  47. package/dist/src/memoryGraph/sources/flags.js +22 -0
  48. package/dist/src/memoryGraph/sources/helpers.js +14 -0
  49. package/dist/src/memoryGraph/sources/memories.js +88 -0
  50. package/dist/src/memoryGraph/sources/raptor.js +26 -0
  51. package/dist/src/memoryGraph/sources/turns.js +176 -0
  52. package/dist/src/memoryGraph/sources.js +15 -441
  53. package/dist/src/memoryGraph-gates.test/_helpers.js +120 -0
  54. package/dist/src/ratio.bench.test/_helpers.js +198 -0
  55. package/dist/src/recall/async.js +112 -0
  56. package/dist/src/recall/format.js +149 -0
  57. package/dist/src/recall/memory.js +62 -0
  58. package/dist/src/recall/reformulate.js +82 -0
  59. package/dist/src/recall/sync.js +169 -0
  60. package/dist/src/recall/types.js +1 -0
  61. package/dist/src/recall.js +13 -530
  62. package/dist/src/store/sqlite/schema/core.js +232 -0
  63. package/dist/src/store/sqlite/schema/game.js +65 -0
  64. package/dist/src/store/sqlite/schema/plan-v2.js +107 -0
  65. package/dist/src/store/sqlite/schema/turns.js +59 -0
  66. package/dist/src/store/sqlite/schema.js +8 -441
  67. package/dist/src/store/turns/contract-compliance.test/_helpers.js +21 -0
  68. package/dist/src/store/turns/sqlite-store/admin.js +171 -0
  69. package/dist/src/store/turns/sqlite-store/ctx.js +1 -0
  70. package/dist/src/store/turns/sqlite-store/read.js +111 -0
  71. package/dist/src/store/turns/sqlite-store/rows.js +42 -0
  72. package/dist/src/store/turns/sqlite-store/write.js +102 -0
  73. package/dist/src/store/turns/sqlite-store.js +31 -367
  74. package/dist/src/vectorStore/class.js +356 -0
  75. package/dist/src/vectorStore/hash.js +10 -0
  76. package/dist/src/vectorStore/types.js +2 -0
  77. package/dist/src/vectorStore.js +8 -359
  78. package/dist/src/vectorStore.test/_helpers.js +17 -0
  79. package/extensions/dashboard-client/dist/assets/{CacheTab-BZNnKhh5.js → CacheTab-DPRHjYZ0.js} +2 -2
  80. package/extensions/dashboard-client/dist/assets/{CacheTab-BZNnKhh5.js.map → CacheTab-DPRHjYZ0.js.map} +1 -1
  81. package/extensions/dashboard-client/dist/assets/{EventsTab-CzF5L42U.js → EventsTab-CgwYq-Qf.js} +2 -2
  82. package/extensions/dashboard-client/dist/assets/{EventsTab-CzF5L42U.js.map → EventsTab-CgwYq-Qf.js.map} +1 -1
  83. package/extensions/dashboard-client/dist/assets/{HealthTab-BLpidMrX.js → HealthTab-BgOCjVzR.js} +2 -2
  84. package/extensions/dashboard-client/dist/assets/{HealthTab-BLpidMrX.js.map → HealthTab-BgOCjVzR.js.map} +1 -1
  85. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Yw1yFpsy.js +2 -0
  86. package/extensions/dashboard-client/dist/assets/MaintenanceTab-Yw1yFpsy.js.map +1 -0
  87. package/extensions/dashboard-client/dist/assets/MemoryMapTab-yHUX7Yzi.js +2 -0
  88. package/extensions/dashboard-client/dist/assets/MemoryMapTab-yHUX7Yzi.js.map +1 -0
  89. package/extensions/dashboard-client/dist/assets/{MetricsTab-B0PCysqs.js → MetricsTab-CcH9fP8P.js} +2 -2
  90. package/extensions/dashboard-client/dist/assets/{MetricsTab-B0PCysqs.js.map → MetricsTab-CcH9fP8P.js.map} +1 -1
  91. package/extensions/dashboard-client/dist/assets/{OverviewTab-DsOhqFu4.js → OverviewTab-BOs8ZNev.js} +2 -2
  92. package/extensions/dashboard-client/dist/assets/{OverviewTab-DsOhqFu4.js.map → OverviewTab-BOs8ZNev.js.map} +1 -1
  93. package/extensions/dashboard-client/dist/assets/{ReposTab-B3YKYuOf.js → ReposTab-B-MfSPvg.js} +2 -2
  94. package/extensions/dashboard-client/dist/assets/{ReposTab-B3YKYuOf.js.map → ReposTab-B-MfSPvg.js.map} +1 -1
  95. package/extensions/dashboard-client/dist/assets/{SessionsTab-B96FWcrC.js → SessionsTab-BmeN649Q.js} +2 -2
  96. package/extensions/dashboard-client/dist/assets/{SessionsTab-B96FWcrC.js.map → SessionsTab-BmeN649Q.js.map} +1 -1
  97. package/extensions/dashboard-client/dist/assets/SetupTab-nOknoph5.js +2 -0
  98. package/extensions/dashboard-client/dist/assets/SetupTab-nOknoph5.js.map +1 -0
  99. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BfkUVTvA.js → TimeSavedCard-DnPg0L0z.js} +2 -2
  100. package/extensions/dashboard-client/dist/assets/{TimeSavedCard-BfkUVTvA.js.map → TimeSavedCard-DnPg0L0z.js.map} +1 -1
  101. package/extensions/dashboard-client/dist/assets/{TopicsTab-DLRsZmXJ.js → TopicsTab-BODHOg_b.js} +2 -2
  102. package/extensions/dashboard-client/dist/assets/{TopicsTab-DLRsZmXJ.js.map → TopicsTab-BODHOg_b.js.map} +1 -1
  103. package/extensions/dashboard-client/dist/assets/{TurnsTab-DKGU2Bf6.js → TurnsTab-BK0I7hKr.js} +2 -2
  104. package/extensions/dashboard-client/dist/assets/{TurnsTab-DKGU2Bf6.js.map → TurnsTab-BK0I7hKr.js.map} +1 -1
  105. package/extensions/dashboard-client/dist/assets/{index-D-lPLRWB.js → index-mOlljTFI.js} +10 -10
  106. package/extensions/dashboard-client/dist/assets/index-mOlljTFI.js.map +1 -0
  107. package/extensions/dashboard-client/dist/assets/{useSSE-ClbbcgIp.js → useSSE-BYOtv1-3.js} +2 -2
  108. package/extensions/dashboard-client/dist/assets/{useSSE-ClbbcgIp.js.map → useSSE-BYOtv1-3.js.map} +1 -1
  109. package/extensions/dashboard-client/dist/index.html +1 -1
  110. package/extensions/dashboard-client/src/App.tsx +0 -4
  111. package/extensions/dashboard-client/src/api/client.ts +16 -0
  112. package/extensions/dashboard-client/src/tabs/MaintenanceTab/ActionsCard.tsx +178 -0
  113. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DbStatsCard.tsx +140 -0
  114. package/extensions/dashboard-client/src/tabs/MaintenanceTab/DebugBundleCard.tsx +109 -0
  115. package/extensions/dashboard-client/src/tabs/MaintenanceTab/HealthMitigationCard.tsx +98 -0
  116. package/extensions/dashboard-client/src/tabs/MaintenanceTab/SchemaHealthCard.tsx +108 -0
  117. package/extensions/dashboard-client/src/tabs/MaintenanceTab.tsx +12 -598
  118. package/extensions/dashboard-client/src/tabs/MemoryMapTab/MemoryMapView.tsx +476 -0
  119. package/extensions/dashboard-client/src/tabs/MemoryMapTab/RaptorTreeView.tsx +163 -0
  120. package/extensions/dashboard-client/src/tabs/MemoryMapTab.tsx +56 -466
  121. package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderHealthCard.tsx +109 -0
  122. package/extensions/dashboard-client/src/tabs/SetupTab/EmbedderSetup.tsx +468 -0
  123. package/extensions/dashboard-client/src/tabs/SetupTab.tsx +23 -484
  124. package/extensions/dashboard-server/api-contracts/embedder-health.ts +32 -0
  125. package/extensions/dashboard-server/api-contracts/endpoints/registry.ts +360 -0
  126. package/extensions/dashboard-server/api-contracts/endpoints/types.ts +350 -0
  127. package/extensions/dashboard-server/api-contracts/endpoints.ts +27 -648
  128. package/extensions/dashboard-server/api-contracts/index.ts +6 -0
  129. package/extensions/dashboard-server/api-contracts/raptor.ts +49 -0
  130. package/extensions/dashboard-server/api-contracts.test/_helpers.ts +47 -0
  131. package/extensions/dashboard-server/routes-cache.test/_helpers.ts +94 -0
  132. package/extensions/dashboard-server/routes-embedder-health.ts +77 -0
  133. package/extensions/dashboard-server/routes-raptor.ts +128 -0
  134. package/extensions/dashboard-server/routes-setup.ts +1 -1
  135. package/extensions/dashboard-server/routes.ts +2 -0
  136. package/extensions/dashboard-server/server.ts +4 -0
  137. package/extensions/mega-cache-replay.test/_helpers.ts +188 -0
  138. package/extensions/mega-commands/dataCommands.ts +169 -0
  139. package/extensions/mega-commands/helpers.ts +62 -0
  140. package/extensions/mega-commands/historyCommands.ts +117 -0
  141. package/extensions/mega-commands/setupCommand.ts +235 -0
  142. package/extensions/mega-commands.ts +25 -538
  143. package/extensions/mega-compact-s38.test/_helpers.ts +201 -0
  144. package/extensions/mega-compact.test/_helpers.ts +237 -0
  145. package/extensions/mega-compact.ts +12 -2
  146. package/extensions/mega-events/agent-handlers/agentEndHandler.ts +238 -0
  147. package/extensions/mega-events/agent-handlers/turnEndHandler/cacheStripe.ts +74 -0
  148. package/extensions/mega-events/agent-handlers/turnEndHandler/contextHealth.ts +50 -0
  149. package/extensions/mega-events/agent-handlers/turnEndHandler/errorRetry.ts +421 -0
  150. package/extensions/mega-events/agent-handlers/turnEndHandler/event.ts +16 -0
  151. package/extensions/mega-events/agent-handlers/turnEndHandler/gameScoring.ts +73 -0
  152. package/extensions/mega-events/agent-handlers/turnEndHandler/lengthStop.ts +29 -0
  153. package/extensions/mega-events/agent-handlers/turnEndHandler/memoryReview.ts +42 -0
  154. package/extensions/mega-events/agent-handlers/turnEndHandler/recordTurnRow.ts +61 -0
  155. package/extensions/mega-events/agent-handlers/turnEndHandler.ts +64 -0
  156. package/extensions/mega-events/agent-handlers.ts +15 -861
  157. package/extensions/mega-events/context-handler/afterCompact.ts +213 -0
  158. package/extensions/mega-events/context-handler/messageText.ts +20 -0
  159. package/extensions/mega-events/context-handler/tailResult.ts +63 -0
  160. package/extensions/mega-events/context-handler.ts +17 -235
  161. package/extensions/mega-events/separated-prompt.test/_helpers.ts +35 -0
  162. package/package.json +1 -1
  163. package/src/config.ts +4 -0
  164. package/src/dedup-engine.test/_helpers.ts +79 -0
  165. package/src/e2e.test/_helpers.ts +59 -0
  166. package/src/embedder.ts +4 -0
  167. package/src/httpEmbedder.ts +7 -0
  168. package/src/hyde.ts +162 -0
  169. package/src/importance.test/_helpers.ts +5 -0
  170. package/src/memoryGraph/sources/checkpoints.ts +99 -0
  171. package/src/memoryGraph/sources/edges.ts +23 -0
  172. package/src/memoryGraph/sources/flags.ts +26 -0
  173. package/src/memoryGraph/sources/helpers.ts +13 -0
  174. package/src/memoryGraph/sources/memories.ts +108 -0
  175. package/src/memoryGraph/sources/raptor.ts +37 -0
  176. package/src/memoryGraph/sources/turns.ts +226 -0
  177. package/src/memoryGraph/sources.ts +19 -544
  178. package/src/memoryGraph-gates.test/_helpers.ts +164 -0
  179. package/src/ratio.bench.test/_helpers.ts +268 -0
  180. package/src/recall/async.ts +142 -0
  181. package/src/recall/format.ts +173 -0
  182. package/src/recall/memory.ts +90 -0
  183. package/src/recall/reformulate.ts +111 -0
  184. package/src/recall/sync.ts +196 -0
  185. package/src/recall/types.ts +63 -0
  186. package/src/recall.ts +25 -712
  187. package/src/store/sqlite/schema/core.ts +233 -0
  188. package/src/store/sqlite/schema/game.ts +66 -0
  189. package/src/store/sqlite/schema/plan-v2.ts +108 -0
  190. package/src/store/sqlite/schema/turns.ts +60 -0
  191. package/src/store/sqlite/schema.ts +13 -441
  192. package/src/store/turns/contract-compliance.test/_helpers.ts +38 -0
  193. package/src/store/turns/sqlite-store/admin.ts +249 -0
  194. package/src/store/turns/sqlite-store/ctx.ts +14 -0
  195. package/src/store/turns/sqlite-store/read.ts +174 -0
  196. package/src/store/turns/sqlite-store/rows.ts +55 -0
  197. package/src/store/turns/sqlite-store/write.ts +193 -0
  198. package/src/store/turns/sqlite-store.ts +33 -514
  199. package/src/vectorStore/class.ts +433 -0
  200. package/src/vectorStore/hash.ts +11 -0
  201. package/src/vectorStore/types.ts +58 -0
  202. package/src/vectorStore.test/_helpers.ts +20 -0
  203. package/src/vectorStore.ts +13 -489
  204. package/extensions/dashboard-client/dist/assets/ConfigTab-DG0WkuuG.js +0 -2
  205. package/extensions/dashboard-client/dist/assets/ConfigTab-DG0WkuuG.js.map +0 -1
  206. package/extensions/dashboard-client/dist/assets/MaintenanceTab-BQF7W7xX.js +0 -2
  207. package/extensions/dashboard-client/dist/assets/MaintenanceTab-BQF7W7xX.js.map +0 -1
  208. package/extensions/dashboard-client/dist/assets/MemoryMapTab-CPtHLv0A.js +0 -2
  209. package/extensions/dashboard-client/dist/assets/MemoryMapTab-CPtHLv0A.js.map +0 -1
  210. package/extensions/dashboard-client/dist/assets/SetupTab-hbzQ_NZv.js +0 -2
  211. package/extensions/dashboard-client/dist/assets/SetupTab-hbzQ_NZv.js.map +0 -1
  212. package/extensions/dashboard-client/dist/assets/index-D-lPLRWB.js.map +0 -1
@@ -1,11 +1,13 @@
1
1
  /**
2
- * recall.ts — Layer 5 (RECALL / INLINE): the unified injection path.
2
+ * recall.ts — Layer 5 (RECALL / INLINE): the unified injection path (public
3
+ * pointer).
3
4
  *
4
- * ONE vector store, THREE entry points, ONE dedup engine. Every way context
5
- * gets re-injected into the window (auto-inline on resume, on-demand
6
- * /recall-context, and the dedup sentinel) goes through `recallAndInline`.
7
- * It always does: search -> dedupe -> inject. The only thing that differs per
8
- * entry point is *what triggers it* and *what query it uses*.
5
+ * Thin shell that re-exports the recall pipeline from ./recall/*. The heavy
6
+ * logic lives in sibling impl files (sync.ts, async.ts, memory.ts,
7
+ * reformulate.ts, format.ts) so no single file crosses the 500-line hard
8
+ * limit (delegate-shell pattern). All public symbols are re-exported here so
9
+ * existing callers `import { recallAndInline } from "./recall.js"` (or
10
+ * "../recall.js") continue to work unchanged.
9
11
  *
10
12
  * Injection respects PREVENT-PI-003: pi has no `system` message role, so we
11
13
  * prepend our recall block to the system prompt via the `before_agent_start`
@@ -13,527 +15,8 @@
13
15
  * pi-agnostic: it returns an injectable text block and records injections; the
14
16
  * extension decides where it lands.
15
17
  */
16
- import { recall as searchRecall } from "./engine.js";
17
- import { vectorWasInjected, vectorMarkInjected, vectorSearchAsync, } from "./vectorStore.js";
18
- import { estimateBlockTokens } from "./tokens.js";
19
- import { defaultEmbedder, cosineSimilarity } from "./embedder.js";
20
- import { rehydrateRaptorTree, isShadowMode } from "./dedup/raptor/index.js";
21
- import { maxCheckpointTimestamp } from "./store/sqlite.js";
22
- import { normalizeSessionId } from "./store.js";
23
- // ---------------------------------------------------------------------------
24
- // S57 RAG suite feature flags + module imports. Each flag defaults OFF — the
25
- // flag-OFF path below is byte-identical to the pre-S57 recall path (recallQuery
26
- // === opts.query, the single searchRecall call), so the helpers are only reached
27
- // when their flag is ON. Wiring is additive; the proven v0.11.5 behavior is the
28
- // default.
29
- // ---------------------------------------------------------------------------
30
- import { RAG_QUERY_REFORMULATION, RAG_TIERED_ROUTER, RAG_RECALL_METRICS } from "./config.js";
31
- import { reformulateQuery, isVagueQuery } from "./queryReformulation.js";
32
- import { VAGUE_MIN_WORDS, VAGUE_VERY_SHORT_WORDS } from "./queryReformulation/cache.js";
33
- import { getTieredRouter } from "./tieredRouter.js";
34
- import { computeRecallMetrics } from "./recallMetrics.js";
35
- import { Logger } from "./log.js";
36
- /** Wrap a recall block so the model reads it as restored compacted context. */
37
- export function formatRecallBlock(hits) {
38
- if (hits.length === 0)
39
- return "";
40
- const parts = hits.map((h, i) => {
41
- const score = (h.score * 100).toFixed(0);
42
- // S17: label a cross-repo hit with its source repo (the repoId doubles as
43
- // that repo's stateDir, so the last path segment is the repo's display
44
- // name). Same-repo hits (no repoId) stay unlabeled.
45
- const repoName = h.repoId
46
- ? ` (from repo ${h.repoId.split("/").filter(Boolean).pop() ?? h.repoId})`
47
- : "";
48
- // S42B: a RAPTOR cluster node hit (not a stored checkpoint) is labeled as a
49
- // hierarchical summary and uses raptorSummary as its body. No Key files line
50
- // (cluster nodes carry no file list).
51
- if (h.raptorLevel !== undefined) {
52
- return (`### Recalled cluster summary [${i + 1}] (level ${h.raptorLevel}, relevance ${score}%)${repoName}\n` +
53
- `${(h.raptorSummary ?? h.checkpoint.summary).trim()}\n`);
54
- }
55
- return (`### Recalled context [${i + 1}] (relevance ${score}%)${repoName}\n` +
56
- `${h.checkpoint.summary.trim()}\n` +
57
- (h.checkpoint.filesModified.length
58
- ? `Key files: ${h.checkpoint.filesModified.join(", ")}.\n`
59
- : ""));
60
- });
61
- return ("The following compacted context was recalled from earlier in this session " +
62
- "and is relevant to the current request. Treat it as background you already know:\n\n" +
63
- parts.join("\n"));
64
- }
65
- /**
66
- * S25 Phase-2: format the RAPTOR tree's top-level summary nodes (root + level-1
67
- * clusters) as a hierarchical overview HEADER. Surfacing the high-level topical
68
- * structure before the detailed checkpoint hits gives the model a map of what
69
- * the session has covered. `nodes` are the RAPTOR summary nodes to surface,
70
- * highest level first (root → level-1 clusters). Returns "" when empty.
71
- */
72
- export function formatRaptorBlock(nodes) {
73
- if (nodes.length === 0)
74
- return "";
75
- const parts = nodes.map((n, i) => {
76
- const score = n.score !== undefined
77
- ? ` (relevance ${(n.score * 100).toFixed(0)}%)`
78
- : "";
79
- const label = n.level === 0
80
- ? `Session overview [${i + 1}]${score}`
81
- : `Cluster summary [${i + 1}] (level ${n.level})${score}`;
82
- return `### ${label}\n${n.summary.trim()}\n`;
83
- });
84
- return ("The following hierarchical overview summarizes the structure of this " +
85
- "session so far. Use it as a map of what has been covered:\n\n" +
86
- parts.join("\n"));
87
- }
88
- // ---------------------------------------------------------------------------
89
- // S57 RAG suite helpers (B1/B2/B3). Each is only called when its feature flag
90
- // is ON; flag-OFF never reaches them. All degrade non-fatally to the standard
91
- // path on any error.
92
- // ---------------------------------------------------------------------------
93
- /**
94
- * B1: If the query is vague, reformulate it via embedding-neighbor TF-IDF
95
- * expansion. Returns the original query when reformulation is not supported
96
- * or the query is already specific. Non-fatal: any error → original query.
97
- */
98
- function reformulateRecallQuery(query, store, sessionId) {
99
- try {
100
- if (!isVagueQuery(query, {
101
- vagueMinWords: VAGUE_MIN_WORDS,
102
- vagueVeryShortWords: VAGUE_VERY_SHORT_WORDS,
103
- }))
104
- return query;
105
- // Pre-compute neighbor candidates once. reformulateQuery embeds the query
106
- // and calls `scan` internally; we return these pre-computed neighbors (same
107
- // query) so the store needs no by-embedding lookup.
108
- const { hits } = searchRecall({ sessionId, query, limit: 10, skipInjected: false }, store);
109
- if (hits.length < 2)
110
- return query; // too few neighbors for TF-IDF
111
- const neighbors = hits.map((h) => ({
112
- id: h.checkpoint.checkpointId,
113
- score: h.score,
114
- }));
115
- const textById = new Map();
116
- for (const h of hits)
117
- textById.set(h.checkpoint.checkpointId, h.checkpoint.summary);
118
- const scan = () => neighbors;
119
- const corpus = {
120
- totalDocs: neighbors.length,
121
- docFreq: (term) => {
122
- let df = 0;
123
- for (const text of textById.values()) {
124
- if (text.toLowerCase().includes(term.toLowerCase()))
125
- df++;
126
- }
127
- return Math.max(df, 1);
128
- },
129
- };
130
- const neighborTexts = (ids) => ids.map((id) => ({ id, text: textById.get(id) ?? "" }));
131
- const { result } = reformulateQuery(query, defaultEmbedder(), scan, corpus, neighborTexts);
132
- return result.expanded.length > query.length ? result.expanded : query;
133
- }
134
- catch {
135
- return query; // non-fatal: fall back to original query
136
- }
137
- }
138
- /**
139
- * B2: Synchronous tiered recall. The TieredRouter.route() is async, so the
140
- * sync path can only peek the L0 in-memory cache; on miss it falls through to
141
- * the standard sync vector scan. The router's L1/L2 value is realized on the
142
- * async path (recallAndInlineAsync). Non-fatal.
143
- */
144
- function runTieredRecall(query, sessionId, limit, skip, store) {
145
- try {
146
- const router = getTieredRouter();
147
- if (!router) {
148
- const result = searchRecall({ sessionId, query, limit, skipInjected: skip }, store);
149
- return { newHits: result.newHits, tier: "off" };
150
- }
151
- // Peek the L0 in-memory cache synchronously; fall through on miss.
152
- const cached = router.peekCache(sessionId, query, limit);
153
- if (cached && cached.length > 0)
154
- return { newHits: cached, tier: "L0-cache" };
155
- const result = searchRecall({ sessionId, query, limit, skipInjected: skip }, store);
156
- return { newHits: result.newHits, tier: "sync-fallback" };
157
- }
158
- catch {
159
- const result = searchRecall({ sessionId, query, limit, skipInjected: skip }, store);
160
- return { newHits: result.newHits, tier: "fallback-error" };
161
- }
162
- }
163
- /**
164
- * B3: Compute recall-quality metrics on the injected hits and log them.
165
- * Only called when RAG_RECALL_METRICS() is ON. Non-fatal.
166
- */
167
- function scoreAndLogRecallMetrics(query, toInject) {
168
- try {
169
- const logger = new Logger();
170
- const metrics = computeRecallMetrics(query, toInject);
171
- logger.info("recall_metrics", {
172
- hitCount: toInject.length,
173
- score: metrics.score,
174
- pass: metrics.pass,
175
- });
176
- if (!metrics.pass && toInject.length > 0) {
177
- logger.info("recall_metrics_low_quality", {
178
- score: metrics.score,
179
- relevance: metrics.breakdown.relevance,
180
- coverage: metrics.breakdown.coverage,
181
- diversity: metrics.breakdown.diversity,
182
- });
183
- }
184
- }
185
- catch {
186
- /* non-fatal: metrics never break recall */
187
- }
188
- }
189
- /**
190
- * Run the unified recall+dudupe+prepare-inject pipeline. Does NOT touch pi;
191
- * it records injections via `markInjected` so the next call dedupes. The
192
- * `store` is passed by the extension (defaults to the engine's default store).
193
- */
194
- /**
195
- * Recall and inline context from the checkpoint store.
196
- *
197
- * S27 contract — DB-Mirror demotion:
198
- *
199
- * When `MEGACOMPACT_DB_MIRROR` is ON, the `raw_transcript` table is the
200
- * canonical, byte-stable source of truth for message reconstruction. The
201
- * `dedup_mirror` provides space-efficient storage with ref_count tracking
202
- * (see src/mirror/dedup.ts). The legacy JSON checkpoint is retained as a
203
- * DR snapshot only (see src/store.ts checkpoint helpers).
204
- *
205
- * The recall function continues to work from the VectorStore (checkpoint
206
- * summaries + embeddings) for fast semantic search — this path is unaffected
207
- * by the mirror flag. If full transcript reconstruction is ever needed
208
- * (replay, export, debug), prefer reading from `raw_transcript + dedup_mirror`
209
- * via `listRawTranscriptRange()` + `dedupTranscript()` instead of the legacy
210
- * JSON checkpoint. Falls back to legacy checkpoint if mirror is empty
211
- * (pre-migration sessions).
212
- *
213
- * Pi-agnostic: no pi runtime imports (src/ invariant).
214
- */
215
- export function recallAndInline(opts, store) {
216
- // ── S27 Recall Demotion ─────────────────────────────────────────────
217
- //
218
- // When MEGACOMPACT_DB_MIRROR is ON, the raw_transcript + dedup_mirror
219
- // tables are preferred for byte-stable reconstruction. The current
220
- // recall path (VectorStore search → format → inject) is unaffected —
221
- // it provides fast semantic search over checkpoint summaries.
222
- //
223
- // If full transcript reconstruction is ever needed (replay, export,
224
- // debug), call reconstructFromMirror(db, sessionId, fromSeq, toSeq)
225
- // from src/mirror/dedup.ts instead of reading from the legacy JSON
226
- // checkpoint. Falls back to legacy checkpoint if mirror is empty
227
- // (pre-migration sessions).
228
- //
229
- // Invariant: raw_transcript + dedup_mirror are additive and never
230
- // lose data. The legacy JSON checkpoint remains as a DR snapshot.
231
- // ─────────────────────────────────────────────────────────────────────
232
- const limit = opts.limit ?? 3;
233
- const skip = opts.skipInjected ?? true;
234
- const maxTokens = opts.recallMaxTokens ?? 0; // 0 = unbounded (legacy behavior)
235
- const doWindowDedupe = opts.windowDedupe ?? false;
236
- const dedupSim = opts.dedupSim ?? 0.9;
237
- // F4: thread skipInjected through to searchRecall instead of hardcoding false
238
- // and re-implementing the filter here. newHits is already deduped when skip
239
- // is true (default); equals hits when skip is false (openclaw command path).
240
- //
241
- // S57 B1: optionally expand a vague query via TF-IDF neighbor terms.
242
- // S57 B2: optionally route via the TieredRouter's sync L0 cache peek.
243
- // Both flags default OFF — recallQuery === opts.query and the single
244
- // searchRecall call reproduce the pre-S57 byte-identical path.
245
- const recallQuery = RAG_QUERY_REFORMULATION()
246
- ? reformulateRecallQuery(opts.query, store, opts.sessionId)
247
- : opts.query;
248
- let newHits;
249
- if (RAG_TIERED_ROUTER()) {
250
- newHits = runTieredRecall(recallQuery, opts.sessionId, limit, skip, store).newHits;
251
- }
252
- else {
253
- const result = searchRecall({ sessionId: opts.sessionId, query: recallQuery, limit, skipInjected: skip }, store);
254
- newHits = result.newHits;
255
- }
256
- // F1: hoist one embedder instance for inline dedupe (matches the async path).
257
- // defaultEmbedder() is deterministic but creating it per hit wastes allocations.
258
- const embedder = defaultEmbedder();
259
- // Precompute live-window embeddings once for inline dedupe (Fix C). Trigram
260
- // embedder is local + cheap; never a network call (PREVENT-PI-004).
261
- let liveEmbeddings = [];
262
- if (doWindowDedupe && opts.liveWindow && opts.liveWindow.length > 0) {
263
- liveEmbeddings = opts.liveWindow.map((m) => embedder.embed(m));
264
- }
265
- // F3: build the hit list first; format ONCE at the end so the block carries
266
- // exactly one preamble and [1..n] numbering, and the token cap counts body
267
- // tokens (one preamble at format time, not N). We accumulate summaries and
268
- // break mid-stream when the cap would be exceeded.
269
- const toInject = [];
270
- let blockTokens = 0;
271
- for (const h of newHits) {
272
- // Inline dedupe: skip a hit already resident in the live window (Fix C).
273
- if (doWindowDedupe && liveEmbeddings.length > 0) {
274
- const hitVec = embedder.embed(h.checkpoint.summary);
275
- if (liveEmbeddings.some((v) => cosineSimilarity(v, hitVec) >= dedupSim))
276
- continue;
277
- }
278
- const partTokens = estimateBlockTokens(h.checkpoint.summary);
279
- // Token cap: never push a chunk that would overrun the ceiling.
280
- if (maxTokens > 0 && blockTokens + partTokens > maxTokens)
281
- break;
282
- toInject.push(h);
283
- blockTokens += partTokens;
284
- vectorMarkInjected(store, opts.sessionId, h.checkpoint.checkpointId);
285
- }
286
- // S57 B3: optionally score + log recall quality metrics (flag-OFF: skipped,
287
- // byte-identical). Scores the injected hits against the ORIGINAL query so the
288
- // metric measures whether the (possibly expanded) search results stay
289
- // relevant to what the user actually asked.
290
- if (RAG_RECALL_METRICS())
291
- scoreAndLogRecallMetrics(opts.query, toInject);
292
- // F3: format once — one preamble, correct [1..n] numbering.
293
- const recallBlock = toInject.length > 0 ? formatRecallBlock(toInject) : "";
294
- const report = toInject.map((h) => ` • ${h.checkpoint.checkpointId} (${h.checkpoint.summary.slice(0, 60).replace(/\n/g, " ")}…)`);
295
- // S25 Phase-2 (RAPTOR_INJECT_SUMMARIES): prepend a hierarchical overview
296
- // header built from the tree's top-level summary nodes (root + the
297
- // highest-scoring level-1 cluster summaries). This gives the model a topical
298
- // map of the session before the detailed checkpoint hits. Default ON via
299
- // the store's config; `opts.raptorSummaries` (when explicitly set) overrides.
300
- // Skipped when no tree exists, the tree is stale/timedOut, or shadow mode is on.
301
- let overview = "";
302
- const injectSummaries = opts.raptorSummaries ?? store.cfg.RAPTOR_INJECT_SUMMARIES;
303
- if (injectSummaries && recallBlock) {
304
- overview = raptorOverviewBlock(store, opts.sessionId, opts.query);
305
- }
306
- const block = overview && recallBlock
307
- ? overview + "\n" + recallBlock
308
- : overview || recallBlock;
309
- return {
310
- toInject,
311
- report,
312
- block,
313
- empty: block.length === 0,
314
- };
315
- }
316
- /**
317
- * S25 Phase-2: build the hierarchical overview header for a session. Rehydrates
318
- * the persisted RAPTOR tree, picks the root + top level-1 cluster nodes by
319
- * cosine similarity to the query, and formats them via formatRaptorBlock.
320
- * Returns "" when no tree, stale tree, timedOut tree, or shadow mode. Non-fatal
321
- * (wrapped in try/catch) — the overview is a bonus; a failure must never block
322
- * the detailed recall block.
323
- */
324
- function raptorOverviewBlock(store, sessionId, query) {
325
- try {
326
- const sid = normalizeSessionId(sessionId);
327
- // S25 gate: the overview header is part of the RAPTOR serve surface, so it
328
- // must honor the same contract as raptorSearchHits — shadow mode is
329
- // logging-only building, not injection.
330
- if (isShadowMode())
331
- return "";
332
- const tree = rehydrateRaptorTree(sid, store.stateDir);
333
- if (!tree || !tree.rootId || tree.timedOut)
334
- return "";
335
- // Freshness: a tree built before the session's latest checkpoint is stale.
336
- const maxTs = maxCheckpointTimestamp(sid, store.stateDir);
337
- if (tree.builtAt && tree.builtAt < maxTs)
338
- return "";
339
- const root = tree.nodes.get(tree.rootId);
340
- if (!root)
341
- return "";
342
- const qv = store.embedder.embed(query);
343
- // Root (level 0) first, then the top level-1 clusters by cosine to the query.
344
- const nodes = [
345
- {
346
- summary: root.summary,
347
- level: root.level,
348
- score: cosineSimilarity(qv, root.embedding),
349
- },
350
- ];
351
- const level1 = [...tree.nodes.values()]
352
- .filter((n) => n.level === 1 && n.summary)
353
- .map((n) => ({
354
- summary: n.summary,
355
- level: n.level,
356
- score: cosineSimilarity(qv, n.embedding),
357
- }))
358
- .sort((a, b) => b.score - a.score)
359
- .slice(0, 3);
360
- nodes.push(...level1);
361
- return formatRaptorBlock(nodes);
362
- }
363
- catch {
364
- return ""; // non-fatal: overview is a bonus
365
- }
366
- }
367
- /** Format one memory hit for the recall block. Category + score for traceability. */
368
- export function formatMemoryRecallBlock(hits) {
369
- if (hits.length === 0)
370
- return "";
371
- const parts = hits.map((h, i) => {
372
- const pct = (h.score * 100).toFixed(0);
373
- const cat = h.category ? `[${h.category}] ` : "";
374
- const src = h.label ? ` ${h.label}` : "";
375
- return `### Recalled memory [${i + 1}] (relevance ${pct}%${src})\n${cat}${h.content.trim()}`;
376
- });
377
- return ("The following facts about this project were saved from earlier turns " +
378
- "and are relevant to the current request. Treat them as established:\n\n" +
379
- parts.join("\n"));
380
- }
381
- /** Recall top-k durable memories, format into a token-capped block. */
382
- export async function recallMemoriesAndInline(opts) {
383
- const limit = opts.limit ?? 5;
384
- const maxTokens = opts.recallMaxTokens ?? 0;
385
- const { recallMemories, recallMemoriesCrossRepo } = await import("./memoryRecall.js");
386
- const hits = await recallMemories(opts.query, opts.stateDir, {
387
- topK: limit,
388
- minSimilarity: opts.minSimilarity ?? 0.2,
389
- });
390
- // S24 cross-repo augmentation: if same-repo recall is thin, pull additional
391
- // memories from OTHER repos via the PGlite HNSW index. Non-fatal: a failure
392
- // degrades to the same-repo hits only.
393
- const crossHits = [];
394
- if (opts.crossRepo && hits.length < limit) {
395
- try {
396
- const x = await recallMemoriesCrossRepo(opts.query, opts.stateDir, {
397
- repo: null,
398
- limit: limit - hits.length,
399
- crossRepoCosine: opts.crossRepoCosine ?? 0.3,
400
- });
401
- for (const h of x)
402
- crossHits.push(h);
403
- }
404
- catch {
405
- /* non-fatal — cross-repo failure → same-repo only */
406
- }
407
- }
408
- if (hits.length === 0 && crossHits.length === 0)
409
- return { empty: true, block: "", report: [] };
410
- // Same incremental token cap pattern as checkpoint recall.
411
- const parts = [];
412
- const report = [];
413
- let blockTokens = 0;
414
- const pushHit = (content, category, score, label, blockSuffix) => {
415
- const part = formatMemoryRecallBlock([{ content, category, score, label: blockSuffix }]);
416
- const partTokens = estimateBlockTokens(part);
417
- if (maxTokens > 0 && blockTokens + partTokens > maxTokens)
418
- return false;
419
- parts.push(part);
420
- report.push(` • ${label} (${(score * 100).toFixed(0)}%): ${content.slice(0, 60).replace(/\n/g, " ")}…`);
421
- blockTokens += partTokens;
422
- return true;
423
- };
424
- for (const h of hits) {
425
- if (!pushHit(h.memory.content, h.memory.category, h.score, `memory#${h.memory.id}`))
426
- break;
427
- }
428
- for (const h of crossHits) {
429
- const repoLabel = h.repoId.split(/[\\/]/).filter(Boolean).pop() ?? h.repoId;
430
- if (!pushHit(h.memory.content, h.memory.category, h.score, `memory#${h.memory.id} (from ${repoLabel})`, `from ${repoLabel}`))
431
- break;
432
- }
433
- return { empty: parts.length === 0, block: parts.join("\n"), report };
434
- }
435
- /**
436
- * Slice 2 async cross-repo recall. Same dedup/bound/inline contract as
437
- * `recallAndInline`, but backed by `VectorStore.searchAsync` so it can recall
438
- * across repos (HNSW NN over the global PGlite index) when `opts.crossRepo` is
439
- * set. The synchronous `recallAndInline` is unchanged and remains the default
440
- * per-session path. Inline-window dedupe + token cap (Fix C) apply here too.
441
- *
442
- * `store` must provide `searchAsync` (the live VectorStore does). Errors fall
443
- * back to an empty result — recall is a bonus, never a hard dependency.
444
- */
445
- export async function recallAndInlineAsync(opts, store) {
446
- const limit = opts.limit ?? 3;
447
- const skip = opts.skipInjected ?? true;
448
- const maxTokens = opts.recallMaxTokens ?? 0;
449
- const doWindowDedupe = opts.windowDedupe ?? false;
450
- const dedupSim = opts.dedupSim ?? 0.9;
451
- let hits = [];
452
- try {
453
- hits = await vectorSearchAsync(store, opts.sessionId, opts.query, limit, {
454
- crossRepo: opts.crossRepo,
455
- repoId: opts.repoId,
456
- });
457
- }
458
- catch {
459
- hits = [];
460
- }
461
- // F1: hoist one embedder instance for inline dedupe. defaultEmbedder() is
462
- // deterministic but creating it per call wastes allocations on large hit sets.
463
- // (recallAndInline already hoisted this; applying the same fix here.)
464
- const embedder = defaultEmbedder();
465
- let liveEmbeddings = [];
466
- if (doWindowDedupe && opts.liveWindow && opts.liveWindow.length > 0) {
467
- liveEmbeddings = opts.liveWindow.map((m) => embedder.embed(m));
468
- }
469
- const toInject = [];
470
- let blockTokens = 0;
471
- // F2: when cross-repo is on but no global index dir could be resolved, skip
472
- // foreign hits rather than injecting them undeduped — otherwise a foreign
473
- // checkpoint with no machine-wide injected-set to consult would re-inject in
474
- // every new session. Same-repo hits (no repoId) are unaffected. Warn once so
475
- // the silent degradation is observable. (The extension resolver normally
476
- // supplies a default globalIndexDir, so this is belt-and-braces.)
477
- const skipCrossRepoHits = !!opts.crossRepo && !opts.globalIndexDir;
478
- if (skipCrossRepoHits) {
479
- try {
480
- console.warn("[mega-compact:recall] cross-repo recall enabled but globalIndexDir is unset — " +
481
- "skipping cross-repo injection to avoid re-injecting undeduped foreign checkpoints");
482
- }
483
- catch {
484
- /* ignore */
485
- }
486
- }
487
- for (const h of hits) {
488
- // F2: skip foreign hits when we can't dedup them machine-wide.
489
- if (skipCrossRepoHits && h.repoId)
490
- continue;
491
- if (skip &&
492
- vectorWasInjected(store, opts.sessionId, h.checkpoint.checkpointId))
493
- continue;
494
- // S18: machine-wide injected-set — a foreign checkpoint already injected
495
- // (in any session) is never re-injected. Only applies to cross-repo hits
496
- // (same-repo hits have no repoId and are handled by the per-session set).
497
- if (opts.globalIndexDir && h.repoId) {
498
- try {
499
- const { wasInjectedGlobal } = await import("./store/sqlite.js");
500
- if (wasInjectedGlobal(h.checkpoint.checkpointId, opts.sessionId, opts.globalIndexDir))
501
- continue;
502
- }
503
- catch {
504
- /* non-fatal: degrade to per-session injected-set only */
505
- }
506
- }
507
- // Inline dedupe: skip a hit already resident in the live window (F1: hoisted embedder).
508
- if (doWindowDedupe && liveEmbeddings.length > 0) {
509
- const hitVec = embedder.embed(h.checkpoint.summary);
510
- if (liveEmbeddings.some((v) => cosineSimilarity(v, hitVec) >= dedupSim))
511
- continue;
512
- }
513
- // F3: build the hit list first; format ONCE at the end so the block carries
514
- // exactly one preamble and numbering [1..n] rather than one per hit.
515
- const partTokens = estimateBlockTokens(h.checkpoint.summary);
516
- if (maxTokens > 0 && blockTokens + partTokens > maxTokens)
517
- break;
518
- toInject.push(h);
519
- blockTokens += partTokens;
520
- vectorMarkInjected(store, opts.sessionId, h.checkpoint.checkpointId);
521
- // S18: record the cross-repo injection machine-wide so it's not re-injected
522
- // by a later recall (same or different session).
523
- if (opts.globalIndexDir && h.repoId) {
524
- try {
525
- const { markInjectedGlobal } = await import("./store/sqlite.js");
526
- markInjectedGlobal(h.checkpoint.checkpointId, h.repoId, opts.sessionId, opts.globalIndexDir);
527
- }
528
- catch {
529
- /* non-fatal */
530
- }
531
- }
532
- }
533
- // F3: format once — one preamble, correct [1..n] numbering, token cap counted
534
- // against one preamble (not N). Pass the full toInject array so formatRecallBlock
535
- // has repoId + score for proper labeling.
536
- const block = toInject.length > 0 ? formatRecallBlock(toInject) : "";
537
- const report = toInject.map((h) => ` • ${h.checkpoint.checkpointId} (${h.checkpoint.summary.slice(0, 60).replace(/\n/g, " ")}…)`);
538
- return { toInject, report, block, empty: toInject.length === 0 };
539
- }
18
+ export { recallAndInline } from "./recall/sync.js";
19
+ export { recallAndInlineAsync } from "./recall/async.js";
20
+ export { recallMemoriesAndInline } from "./recall/memory.js";
21
+ export { formatRecallBlock, formatRaptorBlock, formatMemoryRecallBlock, raptorOverviewBlock, scoreAndLogRecallMetrics, } from "./recall/format.js";
22
+ export { reformulateRecallQuery, runTieredRecall } from "./recall/reformulate.js";