@remnic/core 9.51.1 → 9.52.0

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 (234) hide show
  1. package/dist/access-admin-ops-surface.d.ts +6 -5
  2. package/dist/access-admin-ops-surface.js +16 -16
  3. package/dist/access-authorization-probe.d.ts +6 -5
  4. package/dist/access-authorization-probe.js +17 -17
  5. package/dist/access-boundary.d.ts +6 -5
  6. package/dist/access-boundary.js +16 -16
  7. package/dist/access-cli.js +39 -39
  8. package/dist/access-extraction-force-flush.d.ts +6 -5
  9. package/dist/access-extraction-force-flush.js +16 -16
  10. package/dist/access-http-lcm-compaction.d.ts +6 -5
  11. package/dist/access-http-lifecycle-flush.d.ts +6 -5
  12. package/dist/access-http-offline-stream.d.ts +6 -5
  13. package/dist/access-http-query.js +16 -16
  14. package/dist/access-http.d.ts +6 -5
  15. package/dist/access-http.js +22 -22
  16. package/dist/access-identity-continuity-surface.d.ts +5 -4
  17. package/dist/access-identity-continuity-surface.js +16 -16
  18. package/dist/access-lcm-surface.d.ts +6 -5
  19. package/dist/access-lcm-surface.js +16 -16
  20. package/dist/access-mcp.d.ts +6 -5
  21. package/dist/access-mcp.js +20 -20
  22. package/dist/access-namespace-preflight.js +16 -16
  23. package/dist/access-observe-write-surface.d.ts +6 -5
  24. package/dist/access-observe-write-surface.js +16 -16
  25. package/dist/access-offline-manifest.d.ts +5 -4
  26. package/dist/access-operations-batch.js +17 -17
  27. package/dist/access-operations.d.ts +9 -8
  28. package/dist/access-operations.js +19 -19
  29. package/dist/access-recall-concurrency.d.ts +6 -5
  30. package/dist/access-recall-concurrency.js +16 -16
  31. package/dist/access-recall-response.d.ts +6 -5
  32. package/dist/access-recall-response.js +16 -16
  33. package/dist/access-recall-surface.d.ts +6 -5
  34. package/dist/access-recall-surface.js +16 -16
  35. package/dist/access-schema.d.ts +88 -88
  36. package/dist/{access-service-BY0KJ44c.d.ts → access-service-Cbk3bNbD.d.ts} +3 -3
  37. package/dist/access-service-helpers.d.ts +6 -5
  38. package/dist/access-service.d.ts +6 -5
  39. package/dist/access-service.js +16 -16
  40. package/dist/access-surface-catalog.d.ts +6 -5
  41. package/dist/access-wearables-meetings-surface.d.ts +2 -2
  42. package/dist/bootstrap.d.ts +5 -4
  43. package/dist/briefing.d.ts +1 -1
  44. package/dist/briefing.js +7 -7
  45. package/dist/buffer.d.ts +1 -1
  46. package/dist/{capsule-merge-XMFBXVDP.js → capsule-merge-HCJJMLPS.js} +4 -4
  47. package/dist/causal-consolidation.js +8 -8
  48. package/dist/{chunk-PWN32TAG.js → chunk-2ASAS3CQ.js} +2 -2
  49. package/dist/{chunk-WQM326GS.js → chunk-2LY6GIIE.js} +2 -2
  50. package/dist/{chunk-JL43BVDE.js → chunk-2OIQDTDC.js} +2 -2
  51. package/dist/{chunk-33SV3E43.js → chunk-3OY7KXHM.js} +3 -3
  52. package/dist/{chunk-AIUBBHSF.js → chunk-5UZ6UCOZ.js} +3 -3
  53. package/dist/chunk-5WITETWU.js +591 -0
  54. package/dist/chunk-5WITETWU.js.map +1 -0
  55. package/dist/{chunk-UT5BV5OG.js → chunk-7HXZJAID.js} +2 -2
  56. package/dist/{chunk-M7BXDKPR.js → chunk-7M6NYQWB.js} +3 -3
  57. package/dist/{chunk-TPTECFWR.js → chunk-B6LZ4IBH.js} +3 -3
  58. package/dist/{chunk-J5LLX6IL.js → chunk-BF6SXNQJ.js} +10 -10
  59. package/dist/{chunk-4PC4PRJE.js → chunk-BKIFTXHR.js} +2 -2
  60. package/dist/{chunk-VVG2BOKA.js → chunk-BMEYMKWX.js} +2 -2
  61. package/dist/{chunk-DCZBSE4F.js → chunk-D5YOZ66N.js} +18 -18
  62. package/dist/{chunk-BHXYAVUM.js → chunk-DLG5V6ND.js} +2 -2
  63. package/dist/{chunk-52UKFTUK.js → chunk-F4EU6VUC.js} +2 -2
  64. package/dist/{chunk-M4A44LUA.js → chunk-GSTIBXBW.js} +2 -2
  65. package/dist/{chunk-W5XVQFLM.js → chunk-GXFSQ6SD.js} +2 -2
  66. package/dist/{chunk-XADV5IZ3.js → chunk-HDIUYMSB.js} +5 -5
  67. package/dist/{chunk-FXTYIA46.js → chunk-J272WKX2.js} +2 -2
  68. package/dist/{chunk-ENTCVTQR.js → chunk-JJRVPWFY.js} +11 -1
  69. package/dist/chunk-JJRVPWFY.js.map +1 -0
  70. package/dist/{chunk-JYL3BVVZ.js → chunk-JSEJH2YQ.js} +2 -2
  71. package/dist/{chunk-5UDMFE3Q.js → chunk-MLXZD4WM.js} +2 -2
  72. package/dist/{chunk-PUSLMZBQ.js → chunk-NOEMGE56.js} +2 -2
  73. package/dist/{chunk-C5QLI33R.js → chunk-QINJITDS.js} +2 -2
  74. package/dist/{chunk-45BRYON6.js → chunk-QPOHMO6M.js} +2 -2
  75. package/dist/{chunk-ZQOCRIGA.js → chunk-QYN5PXDZ.js} +3 -3
  76. package/dist/{chunk-PA45XCUD.js → chunk-RANKJI5T.js} +2 -2
  77. package/dist/{chunk-ZHY5TMJG.js → chunk-RMHMUUBW.js} +2392 -628
  78. package/dist/chunk-RMHMUUBW.js.map +1 -0
  79. package/dist/{chunk-37ZO6LLJ.js → chunk-RYLEDKQ2.js} +2 -2
  80. package/dist/{chunk-CV6BBVYV.js → chunk-TEMSWHQD.js} +2 -2
  81. package/dist/{chunk-QGR6OVGT.js → chunk-VFSAWRMZ.js} +2 -2
  82. package/dist/{chunk-NLK2VZYZ.js → chunk-VXCNQYTC.js} +2 -2
  83. package/dist/{chunk-AZ2QBGYB.js → chunk-W7Y5JDVB.js} +1 -1
  84. package/dist/{chunk-VBHPOWBJ.js → chunk-WD5B5AIE.js} +1051 -591
  85. package/dist/chunk-WD5B5AIE.js.map +1 -0
  86. package/dist/{chunk-OYESWJ43.js → chunk-WLRD2TXF.js} +15 -10
  87. package/dist/chunk-WLRD2TXF.js.map +1 -0
  88. package/dist/{chunk-4F6X3RXW.js → chunk-X57M2Q4I.js} +2 -2
  89. package/dist/{chunk-6MTSFX7A.js → chunk-X7XEDOSL.js} +8 -8
  90. package/dist/{chunk-UEVLYVNW.js → chunk-YXKVLYXU.js} +2 -2
  91. package/dist/{chunk-OQAL6SIR.js → chunk-ZMSZVISA.js} +2 -2
  92. package/dist/{cli-CnOkH9hA.d.ts → cli-DMAFI0qt.d.ts} +3 -3
  93. package/dist/cli.d.ts +7 -6
  94. package/dist/cli.js +33 -33
  95. package/dist/compounding/engine.d.ts +1 -1
  96. package/dist/compounding/engine.js +7 -7
  97. package/dist/connectors/codex-materialize-runner.js +7 -7
  98. package/dist/connectors/index.js +7 -7
  99. package/dist/consolidation-provenance-check.d.ts +1 -1
  100. package/dist/consolidation-provenance-check.js +4 -4
  101. package/dist/consolidation-undo.d.ts +1 -1
  102. package/dist/consolidation-undo.js +3 -3
  103. package/dist/contradiction/index.d.ts +1 -1
  104. package/dist/convergence-refresh.d.ts +1 -1
  105. package/dist/convergence-refresh.js +9 -9
  106. package/dist/corpus-watermark.js +9 -9
  107. package/dist/dependency-propagation-delivery-D63r24pH.d.ts +41 -0
  108. package/dist/entity-retrieval.d.ts +1 -1
  109. package/dist/entity-retrieval.js +7 -7
  110. package/dist/explicit-capture.d.ts +5 -4
  111. package/dist/explicit-capture.js +4 -4
  112. package/dist/external-wiki-access.d.ts +6 -5
  113. package/dist/external-wiki-access.js +17 -17
  114. package/dist/external-wiki-mcp-tools.d.ts +6 -5
  115. package/dist/extraction.js +7 -7
  116. package/dist/{forget-UJDJHG6R.js → forget-HDS4N6CG.js} +3 -3
  117. package/dist/harmonic-retrieval.js +1 -1
  118. package/dist/index.d.ts +687 -686
  119. package/dist/index.js +39 -39
  120. package/dist/maintenance/memory-governance.js +7 -7
  121. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +1 -1
  122. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
  123. package/dist/maintenance/rebuild-memory-projection.d.ts +1 -1
  124. package/dist/maintenance/rebuild-memory-projection.js +8 -8
  125. package/dist/{maintenance-CKwxpdRx.d.ts → maintenance-kBII2qUI.d.ts} +3 -2
  126. package/dist/mcp-memory-inspector-app.d.ts +6 -5
  127. package/dist/memory-worth-outcomes.d.ts +1 -1
  128. package/dist/namespaces/migrate.d.ts +1 -1
  129. package/dist/namespaces/migrate.js +8 -8
  130. package/dist/namespaces/storage.d.ts +1 -1
  131. package/dist/namespaces/storage.js +7 -7
  132. package/dist/operator-doctor-corpus.js +10 -10
  133. package/dist/operator-toolkit.d.ts +1 -1
  134. package/dist/operator-toolkit.js +15 -15
  135. package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
  136. package/dist/orchestration/maintenance.d.ts +2 -2
  137. package/dist/orchestration/maintenance.js +12 -12
  138. package/dist/{orchestrator-BshGam-o.d.ts → orchestrator-DHy5LtTy.d.ts} +20 -35
  139. package/dist/orchestrator.d.ts +5 -4
  140. package/dist/orchestrator.js +39 -39
  141. package/dist/page-versioning.js +3 -3
  142. package/dist/schemas.d.ts +138 -138
  143. package/dist/semantic-consolidation.js +8 -8
  144. package/dist/semantic-rule-promotion.js +7 -7
  145. package/dist/semantic-rule-verifier.js +7 -7
  146. package/dist/{service-BIgVM7fL.d.ts → service-PT2_z7xm.d.ts} +1 -1
  147. package/dist/shared-context/manager.d.ts +8 -8
  148. package/dist/source-agent-qualifier.js +7 -7
  149. package/dist/{storage-CVfGWqCI.d.ts → storage-BmVudhk4.d.ts} +46 -27
  150. package/dist/storage.d.ts +1 -1
  151. package/dist/storage.js +6 -6
  152. package/dist/temporal-supersession.d.ts +51 -26
  153. package/dist/temporal-supersession.js +3 -1
  154. package/dist/tier-migration.d.ts +1 -1
  155. package/dist/transfer/capsule-import.js +4 -4
  156. package/dist/transfer/types.d.ts +66 -66
  157. package/dist/verified-recall.js +7 -7
  158. package/package.json +2 -2
  159. package/src/lifecycle/tombstones.ts +30 -0
  160. package/src/orchestration/consolidation-run.ts +324 -106
  161. package/src/orchestration/contradiction-linking-coordinator.ts +237 -64
  162. package/src/orchestration/dependency-propagation-delivery.test.ts +2353 -0
  163. package/src/orchestration/dependency-propagation-delivery.ts +1143 -0
  164. package/src/orchestration/dependency-propagation-queue-state.ts +220 -0
  165. package/src/orchestration/dependency-propagation-recovery-replay.ts +237 -0
  166. package/src/orchestration/dependency-propagation-replay-reconciliation.ts +73 -0
  167. package/src/orchestration/dependency-propagation-triggers.test.ts +1069 -11
  168. package/src/orchestration/dependency-propagation.test.ts +82 -31
  169. package/src/orchestration/dependency-propagation.ts +55 -17
  170. package/src/orchestration/extraction-persist-deps.ts +2 -2
  171. package/src/orchestration/extraction-persist.ts +14 -15
  172. package/src/orchestration/extraction-queue-coordinator.ts +23 -1
  173. package/src/orchestration/extraction-shutdown.ts +29 -0
  174. package/src/orchestration/maintenance.ts +13 -7
  175. package/src/orchestration/turn-ingestion.ts +5 -1
  176. package/src/orchestrator-background-writes.test.ts +265 -1
  177. package/src/orchestrator-extraction-queue.test.ts +39 -0
  178. package/src/orchestrator.ts +59 -58
  179. package/src/storage/committed-invalidation.ts +34 -0
  180. package/src/storage/deletion-revision-store.ts +529 -0
  181. package/src/storage/supersession-audit.ts +41 -0
  182. package/src/storage/supersession-side-effects.ts +52 -25
  183. package/src/storage-contract/failure-semantics.test.ts +12 -0
  184. package/src/storage-contract/round-trip.test.ts +184 -2
  185. package/src/storage-deletion-revisions.test.ts +213 -1
  186. package/src/storage-frontmatter-cas.test.ts +141 -0
  187. package/src/storage-memory-lifecycle-ledger.test.ts +372 -0
  188. package/src/storage-replay.test.ts +149 -0
  189. package/src/storage.ts +345 -358
  190. package/src/temporal-supersession.test.ts +431 -2
  191. package/src/temporal-supersession.ts +567 -107
  192. package/src/testing/subjects/dependency-propagation.test.ts +179 -38
  193. package/dist/chunk-ENTCVTQR.js.map +0 -1
  194. package/dist/chunk-EWMTOVMY.js +0 -484
  195. package/dist/chunk-EWMTOVMY.js.map +0 -1
  196. package/dist/chunk-OYESWJ43.js.map +0 -1
  197. package/dist/chunk-VBHPOWBJ.js.map +0 -1
  198. package/dist/chunk-ZHY5TMJG.js.map +0 -1
  199. /package/dist/{capsule-merge-XMFBXVDP.js.map → capsule-merge-HCJJMLPS.js.map} +0 -0
  200. /package/dist/{chunk-PWN32TAG.js.map → chunk-2ASAS3CQ.js.map} +0 -0
  201. /package/dist/{chunk-WQM326GS.js.map → chunk-2LY6GIIE.js.map} +0 -0
  202. /package/dist/{chunk-JL43BVDE.js.map → chunk-2OIQDTDC.js.map} +0 -0
  203. /package/dist/{chunk-33SV3E43.js.map → chunk-3OY7KXHM.js.map} +0 -0
  204. /package/dist/{chunk-AIUBBHSF.js.map → chunk-5UZ6UCOZ.js.map} +0 -0
  205. /package/dist/{chunk-UT5BV5OG.js.map → chunk-7HXZJAID.js.map} +0 -0
  206. /package/dist/{chunk-M7BXDKPR.js.map → chunk-7M6NYQWB.js.map} +0 -0
  207. /package/dist/{chunk-TPTECFWR.js.map → chunk-B6LZ4IBH.js.map} +0 -0
  208. /package/dist/{chunk-J5LLX6IL.js.map → chunk-BF6SXNQJ.js.map} +0 -0
  209. /package/dist/{chunk-4PC4PRJE.js.map → chunk-BKIFTXHR.js.map} +0 -0
  210. /package/dist/{chunk-VVG2BOKA.js.map → chunk-BMEYMKWX.js.map} +0 -0
  211. /package/dist/{chunk-DCZBSE4F.js.map → chunk-D5YOZ66N.js.map} +0 -0
  212. /package/dist/{chunk-BHXYAVUM.js.map → chunk-DLG5V6ND.js.map} +0 -0
  213. /package/dist/{chunk-52UKFTUK.js.map → chunk-F4EU6VUC.js.map} +0 -0
  214. /package/dist/{chunk-M4A44LUA.js.map → chunk-GSTIBXBW.js.map} +0 -0
  215. /package/dist/{chunk-W5XVQFLM.js.map → chunk-GXFSQ6SD.js.map} +0 -0
  216. /package/dist/{chunk-XADV5IZ3.js.map → chunk-HDIUYMSB.js.map} +0 -0
  217. /package/dist/{chunk-FXTYIA46.js.map → chunk-J272WKX2.js.map} +0 -0
  218. /package/dist/{chunk-JYL3BVVZ.js.map → chunk-JSEJH2YQ.js.map} +0 -0
  219. /package/dist/{chunk-5UDMFE3Q.js.map → chunk-MLXZD4WM.js.map} +0 -0
  220. /package/dist/{chunk-PUSLMZBQ.js.map → chunk-NOEMGE56.js.map} +0 -0
  221. /package/dist/{chunk-C5QLI33R.js.map → chunk-QINJITDS.js.map} +0 -0
  222. /package/dist/{chunk-45BRYON6.js.map → chunk-QPOHMO6M.js.map} +0 -0
  223. /package/dist/{chunk-ZQOCRIGA.js.map → chunk-QYN5PXDZ.js.map} +0 -0
  224. /package/dist/{chunk-PA45XCUD.js.map → chunk-RANKJI5T.js.map} +0 -0
  225. /package/dist/{chunk-37ZO6LLJ.js.map → chunk-RYLEDKQ2.js.map} +0 -0
  226. /package/dist/{chunk-CV6BBVYV.js.map → chunk-TEMSWHQD.js.map} +0 -0
  227. /package/dist/{chunk-QGR6OVGT.js.map → chunk-VFSAWRMZ.js.map} +0 -0
  228. /package/dist/{chunk-NLK2VZYZ.js.map → chunk-VXCNQYTC.js.map} +0 -0
  229. /package/dist/{chunk-AZ2QBGYB.js.map → chunk-W7Y5JDVB.js.map} +0 -0
  230. /package/dist/{chunk-4F6X3RXW.js.map → chunk-X57M2Q4I.js.map} +0 -0
  231. /package/dist/{chunk-6MTSFX7A.js.map → chunk-X7XEDOSL.js.map} +0 -0
  232. /package/dist/{chunk-UEVLYVNW.js.map → chunk-YXKVLYXU.js.map} +0 -0
  233. /package/dist/{chunk-OQAL6SIR.js.map → chunk-ZMSZVISA.js.map} +0 -0
  234. /package/dist/{forget-UJDJHG6R.js.map → forget-HDS4N6CG.js.map} +0 -0
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  ActivityStore,
15
15
  MaintenanceScheduler
16
- } from "./chunk-OYESWJ43.js";
16
+ } from "./chunk-WLRD2TXF.js";
17
17
  import {
18
18
  runPeerProfileReasoner
19
19
  } from "./chunk-I74SUMNI.js";
@@ -32,7 +32,7 @@ import {
32
32
  import {
33
33
  CompoundingEngine,
34
34
  defaultTierMigrationCycleBudget
35
- } from "./chunk-UT5BV5OG.js";
35
+ } from "./chunk-7HXZJAID.js";
36
36
  import {
37
37
  SharedContextManager
38
38
  } from "./chunk-M23RF3LJ.js";
@@ -190,7 +190,7 @@ import {
190
190
  buildEntityRecallSection,
191
191
  entityRecentTranscriptLookbackHours,
192
192
  readRecentEntityTranscriptEntries
193
- } from "./chunk-CV6BBVYV.js";
193
+ } from "./chunk-TEMSWHQD.js";
194
194
  import {
195
195
  buildEventOrderRecallSection,
196
196
  shouldRecallEventOrderEvidence
@@ -207,7 +207,7 @@ import {
207
207
  } from "./chunk-YK6BDDHP.js";
208
208
  import {
209
209
  refreshConvergedNamespaces
210
- } from "./chunk-W5XVQFLM.js";
210
+ } from "./chunk-GXFSQ6SD.js";
211
211
  import {
212
212
  formatDaySummaryMemories
213
213
  } from "./chunk-RPSYDM6C.js";
@@ -216,7 +216,7 @@ import {
216
216
  ensureBuiltInWearableConnectors,
217
217
  getWearableConnector,
218
218
  listWearableConnectors
219
- } from "./chunk-DCZBSE4F.js";
219
+ } from "./chunk-D5YOZ66N.js";
220
220
  import {
221
221
  normalizeReplaySessionKey
222
222
  } from "./chunk-AJVLDJU6.js";
@@ -226,14 +226,14 @@ import {
226
226
  import {
227
227
  compareVerifiedEpisodeResults,
228
228
  searchVerifiedEpisodes
229
- } from "./chunk-FXTYIA46.js";
229
+ } from "./chunk-J272WKX2.js";
230
230
  import {
231
231
  ThreadingManager
232
232
  } from "./chunk-OJP3N7UK.js";
233
233
  import {
234
234
  compareVerifiedSemanticRuleResults,
235
235
  searchVerifiedSemanticRules
236
- } from "./chunk-QGR6OVGT.js";
236
+ } from "./chunk-VFSAWRMZ.js";
237
237
  import {
238
238
  searchWorkProductLedgerEntries
239
239
  } from "./chunk-WT6UL3UA.js";
@@ -246,14 +246,14 @@ import {
246
246
  import {
247
247
  runNamespaceMaintenanceFanout,
248
248
  summarizeNamespaceMaintenanceHealth
249
- } from "./chunk-6MTSFX7A.js";
249
+ } from "./chunk-X7XEDOSL.js";
250
250
  import {
251
251
  NamespaceCatalog,
252
252
  planNamespaceMaintenance
253
- } from "./chunk-4PC4PRJE.js";
253
+ } from "./chunk-BKIFTXHR.js";
254
254
  import {
255
255
  searchHarmonicRetrieval
256
- } from "./chunk-AZ2QBGYB.js";
256
+ } from "./chunk-W7Y5JDVB.js";
257
257
  import {
258
258
  isAboveImportanceThreshold,
259
259
  scoreImportance
@@ -285,7 +285,7 @@ import {
285
285
  materializeAfterSemanticConsolidation,
286
286
  parseConsolidationResponse,
287
287
  parseOperatorAwareConsolidationResponse
288
- } from "./chunk-VVG2BOKA.js";
288
+ } from "./chunk-BMEYMKWX.js";
289
289
  import {
290
290
  BootstrapEngine
291
291
  } from "./chunk-MYVPLJOO.js";
@@ -331,7 +331,7 @@ import {
331
331
  } from "./chunk-KD3QD3A5.js";
332
332
  import {
333
333
  MeetingsInputError
334
- } from "./chunk-XADV5IZ3.js";
334
+ } from "./chunk-HDIUYMSB.js";
335
335
  import {
336
336
  WearablesInputError,
337
337
  describeErrorForOperator
@@ -352,10 +352,10 @@ import {
352
352
  filterRecallCandidates,
353
353
  isGenericRecallExcludedPath,
354
354
  isTopLevelArchivePath
355
- } from "./chunk-J5LLX6IL.js";
355
+ } from "./chunk-BF6SXNQJ.js";
356
356
  import {
357
357
  importCapsule
358
- } from "./chunk-ZQOCRIGA.js";
358
+ } from "./chunk-QYN5PXDZ.js";
359
359
  import {
360
360
  isCodingGraphInstalled
361
361
  } from "./chunk-VQ7B2Q6M.js";
@@ -396,7 +396,7 @@ import {
396
396
  } from "./chunk-QDW3E4RD.js";
397
397
  import {
398
398
  shouldSkipImplicitExtraction
399
- } from "./chunk-37ZO6LLJ.js";
399
+ } from "./chunk-RYLEDKQ2.js";
400
400
  import {
401
401
  NamespaceSearchRouter,
402
402
  namespaceCollectionName
@@ -411,7 +411,7 @@ import {
411
411
  } from "./chunk-CYEPCZN5.js";
412
412
  import {
413
413
  NamespaceStorageRouter
414
- } from "./chunk-WQM326GS.js";
414
+ } from "./chunk-2LY6GIIE.js";
415
415
  import {
416
416
  writeConversationChunks
417
417
  } from "./chunk-72A4L5JJ.js";
@@ -423,7 +423,7 @@ import {
423
423
  } from "./chunk-BIJAWZYW.js";
424
424
  import {
425
425
  buildChainFollowupGenerator
426
- } from "./chunk-52UKFTUK.js";
426
+ } from "./chunk-F4EU6VUC.js";
427
427
  import {
428
428
  QmdResultResolver,
429
429
  StorageManager,
@@ -442,7 +442,7 @@ import {
442
442
  reconstructFusionInputs,
443
443
  selfDeps,
444
444
  serializeFusionDay
445
- } from "./chunk-VBHPOWBJ.js";
445
+ } from "./chunk-WD5B5AIE.js";
446
446
  import {
447
447
  bodyIsEscaped,
448
448
  decodeTranscriptBody,
@@ -455,7 +455,7 @@ import {
455
455
  } from "./chunk-UHVXE24Y.js";
456
456
  import {
457
457
  buildRetiredFactTombstoneInputs
458
- } from "./chunk-ENTCVTQR.js";
458
+ } from "./chunk-JJRVPWFY.js";
459
459
  import {
460
460
  canRecallToolScopedMemory,
461
461
  shouldPromoteGlobalFactToShared,
@@ -506,15 +506,15 @@ import {
506
506
  requestEntityCanonicalIdReconcile,
507
507
  requestEntityCanonicalIdReconcileSync,
508
508
  withRawEntityPageMutation
509
- } from "./chunk-M4A44LUA.js";
509
+ } from "./chunk-GSTIBXBW.js";
510
510
  import {
511
511
  applyTemporalSupersession,
512
+ applyTemporalSupersessionPrimaryMutation,
512
513
  lookupAttributeByNormalizedKey,
513
514
  normalizeSupersessionKey,
514
- propagateInvalidation,
515
515
  shouldFilterSupersededFromRecall,
516
516
  supersessionKeysForFact
517
- } from "./chunk-EWMTOVMY.js";
517
+ } from "./chunk-5WITETWU.js";
518
518
  import {
519
519
  isValidAsOf,
520
520
  isValidityExpiredNow
@@ -681,7 +681,16 @@ import {
681
681
  throwIfAborted
682
682
  } from "./chunk-BTTPKDIL.js";
683
683
  import {
684
- isPathInsideStorageRoot
684
+ isRecord
685
+ } from "./chunk-A2IYSXDQ.js";
686
+ import {
687
+ listJsonFilesStrict,
688
+ withJsonStoreMutationLock,
689
+ writeJsonFileAtomic
690
+ } from "./chunk-P77QX3ID.js";
691
+ import {
692
+ isPathInsideStorageRoot,
693
+ resolveSafeStoragePath
685
694
  } from "./chunk-FF4KLI5W.js";
686
695
  import {
687
696
  SecureStoreLockedError
@@ -700,8 +709,8 @@ import {
700
709
  } from "./chunk-3WCASNQZ.js";
701
710
 
702
711
  // src/orchestrator.ts
703
- import path19 from "path";
704
- import { createHash as createHash6 } from "crypto";
712
+ import path20 from "path";
713
+ import { createHash as createHash8 } from "crypto";
705
714
 
706
715
  // src/orchestration/tier-migration-coordinator.ts
707
716
  import path from "path";
@@ -898,6 +907,7 @@ var ExtractionQueueCoordinator = class {
898
907
  queue = [];
899
908
  /** Whether the serial drain loop is currently running. */
900
909
  processing = false;
910
+ accepting = true;
901
911
  /** Current queue depth (idle-wait + test seam). */
902
912
  get length() {
903
913
  return this.queue.length;
@@ -912,6 +922,7 @@ var ExtractionQueueCoordinator = class {
912
922
  * builds each task closure and hands it here.
913
923
  */
914
924
  enqueue(task) {
925
+ if (!this.accepting) return false;
915
926
  this.queue.push(task);
916
927
  if (!this.processing) {
917
928
  this.processing = true;
@@ -920,6 +931,23 @@ var ExtractionQueueCoordinator = class {
920
931
  this.processing = false;
921
932
  });
922
933
  }
934
+ return true;
935
+ }
936
+ /** Stop new producers from entering the queue. */
937
+ stopAccepting() {
938
+ this.accepting = false;
939
+ }
940
+ /** Resume producer acceptance after an orchestrator re-initializes. */
941
+ resumeAccepting() {
942
+ this.accepting = true;
943
+ }
944
+ /** Stop new producers and wait for all accepted work to finish. */
945
+ async pauseAndDrain(timeoutMs = 6e4) {
946
+ this.stopAccepting();
947
+ if (await this.waitForIdle(timeoutMs)) return true;
948
+ log.warn("extraction queue drain exceeded timeout; waiting for accepted work to settle");
949
+ await this.waitForIdle(Number.POSITIVE_INFINITY);
950
+ return true;
923
951
  }
924
952
  /**
925
953
  * Background serial queue processor.
@@ -2195,7 +2223,7 @@ var RecallRerankCoordinator = class _RecallRerankCoordinator {
2195
2223
  namespaces,
2196
2224
  {
2197
2225
  readNamespaceMemories: async (ns) => (await this.getStorage(ns)).readAllMemories(),
2198
- readMemoryFrontmatter: async (path34, preferredNamespace) => {
2226
+ readMemoryFrontmatter: async (path35, preferredNamespace) => {
2199
2227
  if (!fallbackReader) {
2200
2228
  for (const ns of namespaces) {
2201
2229
  try {
@@ -2207,7 +2235,7 @@ var RecallRerankCoordinator = class _RecallRerankCoordinator {
2207
2235
  }
2208
2236
  if (!fallbackReader) return null;
2209
2237
  const memory = await this.readQmdResultMemory(
2210
- path34,
2238
+ path35,
2211
2239
  fallbackReader,
2212
2240
  namespaces,
2213
2241
  preferredNamespace
@@ -2819,6 +2847,214 @@ async function localizeUpdateCandidates(deps, anchor, newContent, options) {
2819
2847
  return merged;
2820
2848
  }
2821
2849
 
2850
+ // src/orchestration/dependency-propagation.ts
2851
+ function isDependencyPropagationEnabled(config) {
2852
+ const parsed = propagationConfig(config);
2853
+ return isEnabled(parsed.enabled) && parsed.maxDependents > 0;
2854
+ }
2855
+ function propagationConfig(config) {
2856
+ return config.dependencyPropagation;
2857
+ }
2858
+ function isEnabled(value) {
2859
+ if (value === true) return true;
2860
+ if (typeof value !== "string") return false;
2861
+ return ["true", "1", "yes", "on"].includes(value.trim().toLowerCase());
2862
+ }
2863
+ var ACTIVE_STATUS = "active";
2864
+ function findDependents(memories, oldMemory, linkTypes = ["supports", "follows"]) {
2865
+ const allowed = new Set(linkTypes);
2866
+ const ids = /* @__PURE__ */ new Set();
2867
+ const byId = new Map(memories.map((memory) => [memory.frontmatter.id, memory]));
2868
+ for (const link of oldMemory.frontmatter.links ?? []) {
2869
+ if (link.linkType !== "supports" && link.linkType !== "references") continue;
2870
+ if (!allowed.has(link.linkType)) continue;
2871
+ const dependent = byId.get(link.targetId);
2872
+ if (dependent && (dependent.frontmatter.status ?? ACTIVE_STATUS) === ACTIVE_STATUS) {
2873
+ ids.add(dependent.frontmatter.id);
2874
+ }
2875
+ }
2876
+ const oldId = oldMemory.frontmatter.id;
2877
+ for (const memory of memories) {
2878
+ if ((memory.frontmatter.status ?? ACTIVE_STATUS) !== ACTIVE_STATUS) continue;
2879
+ if (memory.frontmatter.id === oldId) continue;
2880
+ for (const link of memory.frontmatter.links ?? []) {
2881
+ if (link.targetId !== oldId) continue;
2882
+ if (link.linkType !== "follows" && link.linkType !== "references") continue;
2883
+ if (!allowed.has(link.linkType)) continue;
2884
+ ids.add(memory.frontmatter.id);
2885
+ }
2886
+ }
2887
+ return Array.from(ids).map((id) => byId.get(id)).filter((memory) => memory !== void 0).sort((a, b) => {
2888
+ const left = a.frontmatter.id;
2889
+ const right = b.frontmatter.id;
2890
+ return left < right ? -1 : left > right ? 1 : 0;
2891
+ });
2892
+ }
2893
+ function emptyResult(skipped, durationMs, route = null, dependentsFound = 0) {
2894
+ return { dependentsFound, invalidated: 0, stillValid: 0, uncertain: 0, skipped, route, durationMs };
2895
+ }
2896
+ function logResult(event, result) {
2897
+ const oldId = event.oldMemory.frontmatter.id;
2898
+ log.info(
2899
+ `dependency-propagation ${JSON.stringify({
2900
+ cause: event.cause,
2901
+ oldId,
2902
+ dependentsFound: result.dependentsFound,
2903
+ invalidated: result.invalidated,
2904
+ stillValid: result.stillValid,
2905
+ uncertain: result.uncertain,
2906
+ skipped: result.skipped,
2907
+ route: result.route,
2908
+ durationMs: result.durationMs
2909
+ })}`
2910
+ );
2911
+ }
2912
+ var PropagationTimeoutError = class extends Error {
2913
+ constructor() {
2914
+ super("dependency propagation timed out");
2915
+ this.name = "PropagationTimeoutError";
2916
+ }
2917
+ };
2918
+ function isTimeoutError(error, signal) {
2919
+ return signal.aborted || error instanceof PropagationTimeoutError || error instanceof Error && (error.name === "AbortError" || /timeout|timed out/i.test(error.message));
2920
+ }
2921
+ async function propagateInvalidation(deps, event) {
2922
+ const startedAt = Date.now();
2923
+ const config = propagationConfig(deps.config);
2924
+ if (!isEnabled(config.enabled)) {
2925
+ const result2 = emptyResult("disabled", Date.now() - startedAt);
2926
+ logResult(event, result2);
2927
+ return result2;
2928
+ }
2929
+ const maxDependents = config.maxDependents;
2930
+ if (maxDependents <= 0) {
2931
+ const result2 = emptyResult("no_dependents", Date.now() - startedAt);
2932
+ logResult(event, result2);
2933
+ return result2;
2934
+ }
2935
+ let memories;
2936
+ try {
2937
+ memories = await deps.storage.readAllMemories();
2938
+ } catch (error) {
2939
+ log.warn(`dependency propagation discovery failed for ${event.oldMemory.frontmatter.id}: ${error}`);
2940
+ const result2 = emptyResult("llm_error", Date.now() - startedAt);
2941
+ logResult(event, result2);
2942
+ return result2;
2943
+ }
2944
+ const discovered = findDependents(
2945
+ memories,
2946
+ event.oldMemory,
2947
+ config.linkTypes
2948
+ ).filter((memory) => memory.frontmatter.id !== event.replacementId);
2949
+ const dependents = discovered.slice(0, Math.max(0, Math.floor(maxDependents)));
2950
+ if (dependents.length === 0) {
2951
+ const result2 = emptyResult("no_dependents", Date.now() - startedAt);
2952
+ logResult(event, result2);
2953
+ return result2;
2954
+ }
2955
+ const controller = new AbortController();
2956
+ const timeoutMs = config.timeoutMs;
2957
+ let timedOut = false;
2958
+ let timeout;
2959
+ const deadline = Number.isFinite(timeoutMs) && timeoutMs > 0 ? new Promise((_, reject) => {
2960
+ timeout = setTimeout(() => {
2961
+ timedOut = true;
2962
+ const error = new PropagationTimeoutError();
2963
+ controller.abort(error);
2964
+ reject(error);
2965
+ }, timeoutMs);
2966
+ }) : void 0;
2967
+ const revalidation = Promise.resolve().then(() => deps.extraction.revalidateDependents(
2968
+ { id: event.oldMemory.frontmatter.id, content: event.oldMemory.content },
2969
+ event.replacementId && event.replacementContent !== null ? { id: event.replacementId, content: event.replacementContent } : null,
2970
+ dependents.map((memory) => ({
2971
+ id: memory.frontmatter.id,
2972
+ category: memory.frontmatter.category,
2973
+ content: memory.content
2974
+ })),
2975
+ controller.signal
2976
+ ));
2977
+ let verdicts;
2978
+ try {
2979
+ const response = deadline ? await Promise.race([revalidation, deadline]) : await revalidation;
2980
+ if (timedOut || controller.signal.aborted) {
2981
+ const result2 = emptyResult(
2982
+ "timeout",
2983
+ Date.now() - startedAt,
2984
+ "fast-completion",
2985
+ dependents.length
2986
+ );
2987
+ logResult(event, result2);
2988
+ return result2;
2989
+ }
2990
+ verdicts = response.verdicts;
2991
+ } catch (error) {
2992
+ const result2 = emptyResult(
2993
+ timedOut || isTimeoutError(error, controller.signal) ? "timeout" : "llm_error",
2994
+ Date.now() - startedAt,
2995
+ "fast-completion",
2996
+ dependents.length
2997
+ );
2998
+ log.warn(`dependency propagation revalidation failed for ${event.oldMemory.frontmatter.id}: ${error}`);
2999
+ logResult(event, result2);
3000
+ return result2;
3001
+ } finally {
3002
+ clearTimeout(timeout);
3003
+ }
3004
+ let invalidated = 0;
3005
+ let stillValid = 0;
3006
+ let uncertain = 0;
3007
+ for (const dependent of dependents) {
3008
+ const verdict = verdicts.find((candidate) => candidate.memoryId === dependent.frontmatter.id);
3009
+ if (verdict?.verdict === "invalidated") {
3010
+ if (config.dryRun) {
3011
+ invalidated += 1;
3012
+ continue;
3013
+ }
3014
+ try {
3015
+ const replacementId = event.replacementId ?? event.oldMemory.frontmatter.id;
3016
+ const write = () => deps.storage.supersedeMemory(
3017
+ dependent.frontmatter.id,
3018
+ replacementId,
3019
+ `dependency_propagation:${event.cause}`,
3020
+ {
3021
+ supersessionCause: "dependency",
3022
+ invalidatedBy: event.oldMemory.frontmatter.id
3023
+ },
3024
+ {
3025
+ requireActive: true,
3026
+ acceptExactReplay: true,
3027
+ expectedSnapshot: dependent
3028
+ }
3029
+ );
3030
+ const superseded = deps.writeFence ? await deps.writeFence(write) : await write();
3031
+ if (superseded !== true) continue;
3032
+ invalidated += 1;
3033
+ } catch (error) {
3034
+ log.warn(`dependency propagation write failed for ${dependent.frontmatter.id}: ${error}`);
3035
+ }
3036
+ continue;
3037
+ }
3038
+ if (verdict?.verdict === "still_valid") {
3039
+ stillValid += 1;
3040
+ } else {
3041
+ uncertain += 1;
3042
+ log.info(`dependency propagation uncertain for ${dependent.frontmatter.id}`);
3043
+ }
3044
+ }
3045
+ const result = {
3046
+ dependentsFound: dependents.length,
3047
+ invalidated,
3048
+ stillValid,
3049
+ uncertain,
3050
+ skipped: null,
3051
+ route: "fast-completion",
3052
+ durationMs: Date.now() - startedAt
3053
+ };
3054
+ logResult(event, result);
3055
+ return result;
3056
+ }
3057
+
2822
3058
  // src/orchestration/contradiction-linking-coordinator.ts
2823
3059
  var ContradictionLinkingCoordinator = class {
2824
3060
  getConfig;
@@ -2828,6 +3064,8 @@ var ContradictionLinkingCoordinator = class {
2828
3064
  namespaceFromPath;
2829
3065
  storageForNamespace;
2830
3066
  getExtraction;
3067
+ storageDirNamespace;
3068
+ getDependencyPropagationDelivery;
2831
3069
  constructor(options) {
2832
3070
  this.getConfig = options.getConfig;
2833
3071
  this.isSearchAvailable = options.isSearchAvailable;
@@ -2836,6 +3074,10 @@ var ContradictionLinkingCoordinator = class {
2836
3074
  this.namespaceFromPath = options.namespaceFromPath;
2837
3075
  this.storageForNamespace = options.storageForNamespace;
2838
3076
  this.getExtraction = options.getExtraction;
3077
+ this.storageDirNamespace = options.storageDirNamespace ?? (() => "default");
3078
+ this.getDependencyPropagationDelivery = options.getDependencyPropagationDelivery ?? (() => {
3079
+ throw new Error("dependency propagation delivery is unavailable");
3080
+ });
2839
3081
  }
2840
3082
  /**
2841
3083
  * Check if a new memory contradicts an existing one.
@@ -3017,28 +3259,165 @@ var ContradictionLinkingCoordinator = class {
3017
3259
  return postWriteGuard ? "blocked" : "not_attempted";
3018
3260
  }
3019
3261
  let oldMemory = null;
3020
- if (coerceBool(config.dependencyPropagation?.enabled) === true) {
3262
+ let replacementContent = null;
3263
+ let propagationEvent = null;
3264
+ try {
3265
+ const [hot, cold] = await Promise.all([
3266
+ typeof storage.readAllMemories === "function" ? storage.readAllMemories() : Promise.resolve([]),
3267
+ typeof storage.readAllColdMemories === "function" ? storage.readAllColdMemories() : Promise.resolve([])
3268
+ ]);
3269
+ const memories = mergeMemorySnapshots(hot, cold, storage.dir);
3270
+ const captured = memories.find(
3271
+ (memory) => memory.frontmatter.id === contradiction.supersededId && inferLocalizationMemoryStatus(memory, storage.dir) === "active"
3272
+ );
3273
+ if (captured) {
3274
+ oldMemory = {
3275
+ content: captured.content,
3276
+ frontmatter: {
3277
+ ...captured.frontmatter,
3278
+ ...captured.frontmatter.tags ? { tags: [...captured.frontmatter.tags] } : {},
3279
+ ...captured.frontmatter.links ? { links: captured.frontmatter.links.map((link) => ({ ...link })) } : {}
3280
+ },
3281
+ path: captured.path
3282
+ };
3283
+ }
3284
+ replacementContent = memories.find((memory) => memory.frontmatter.id === newMemoryId)?.content ?? (await storage.getMemoryById(newMemoryId))?.content ?? null;
3285
+ } catch (err) {
3286
+ log.warn(
3287
+ `contradiction propagation snapshot failed for ${contradiction.supersededId}: ${err}`
3288
+ );
3289
+ }
3290
+ if (oldMemory && replacementContent !== null) {
3021
3291
  try {
3022
- const [hot, cold] = await Promise.all([
3023
- typeof storage.readAllMemories === "function" ? storage.readAllMemories() : storage.getMemoryById(contradiction.supersededId).then(
3024
- (memory) => memory ? [memory] : []
3025
- ),
3026
- typeof storage.readAllColdMemories === "function" ? storage.readAllColdMemories() : Promise.resolve([])
3027
- ]);
3028
- oldMemory = mergeMemorySnapshots(hot, cold, storage.dir).find(
3029
- (memory) => memory.frontmatter.id === contradiction.supersededId
3030
- ) ?? null;
3292
+ propagationEvent = {
3293
+ oldMemory,
3294
+ replacementId: newMemoryId,
3295
+ replacementContent,
3296
+ cause: "contradiction",
3297
+ namespaceScope: this.storageDirNamespace(storage.dir)
3298
+ };
3299
+ } catch (err) {
3300
+ log.warn(
3301
+ `contradiction dependency propagation event failed for ${contradiction.supersededId}: ${err}`
3302
+ );
3303
+ }
3304
+ } else if (oldMemory) {
3305
+ log.warn(
3306
+ `contradiction dependency propagation skipped for ${contradiction.supersededId}: replacement content was not captured`
3307
+ );
3308
+ }
3309
+ const propagationEnabled = isDependencyPropagationEnabled(config);
3310
+ let delivery = null;
3311
+ if (propagationEvent && propagationEnabled) {
3312
+ try {
3313
+ delivery = this.getDependencyPropagationDelivery();
3314
+ } catch (err) {
3315
+ log.warn(
3316
+ `contradiction dependency propagation delivery unavailable for ${contradiction.supersededId}: ${err}`
3317
+ );
3318
+ }
3319
+ }
3320
+ let preparation = null;
3321
+ if (delivery && propagationEvent) {
3322
+ try {
3323
+ preparation = await delivery.prepare(propagationEvent);
3031
3324
  } catch (err) {
3032
- log.warn(`contradiction propagation snapshot failed for ${contradiction.supersededId}: ${err}`);
3325
+ log.warn(
3326
+ `contradiction dependency propagation prepare failed for ${contradiction.supersededId}: ${err}`
3327
+ );
3033
3328
  }
3034
3329
  }
3330
+ const supersessionCommitted = async () => {
3331
+ try {
3332
+ const current = await storage.getMemoryById(contradiction.supersededId);
3333
+ if (!current) return null;
3334
+ return current.frontmatter.status === "superseded" && current.frontmatter.supersededBy === newMemoryId;
3335
+ } catch (err) {
3336
+ log.warn(
3337
+ `contradiction supersession state read failed for ${contradiction.supersededId}: ${err}`
3338
+ );
3339
+ return null;
3340
+ }
3341
+ };
3342
+ const settleFailedPropagation = async (commitState2) => {
3343
+ let token = preparation;
3344
+ if (commitState2 === false) {
3345
+ if (token !== null) {
3346
+ try {
3347
+ await delivery?.cancel(token);
3348
+ } catch (err) {
3349
+ log.warn(
3350
+ `contradiction dependency propagation cancel failed for ${contradiction.supersededId}: ${err}`
3351
+ );
3352
+ }
3353
+ }
3354
+ return;
3355
+ }
3356
+ if (commitState2 === null && token === null && delivery && propagationEvent) {
3357
+ try {
3358
+ token = await delivery.prepare(propagationEvent);
3359
+ } catch (err) {
3360
+ log.warn(
3361
+ `contradiction dependency propagation recovery prepare failed for ${contradiction.supersededId}: ${err}`
3362
+ );
3363
+ }
3364
+ }
3365
+ if (token !== null) {
3366
+ try {
3367
+ await delivery?.deferPrepared(token);
3368
+ } catch (err) {
3369
+ log.warn(
3370
+ `contradiction dependency propagation defer failed for ${contradiction.supersededId}: ${err}`
3371
+ );
3372
+ }
3373
+ return;
3374
+ }
3375
+ if (commitState2 === true && delivery && propagationEvent) {
3376
+ try {
3377
+ await delivery.afterMutation(null, propagationEvent);
3378
+ } catch (err) {
3379
+ log.warn(
3380
+ `contradiction dependency propagation fallback failed for ${contradiction.supersededId}: ${err}`
3381
+ );
3382
+ }
3383
+ }
3384
+ };
3385
+ const readyPreparedPropagation = async () => {
3386
+ if (!propagationEvent || !propagationEnabled) return;
3387
+ if (!delivery) {
3388
+ await propagateInvalidation(
3389
+ { storage, extraction: this.getExtraction(), config },
3390
+ propagationEvent
3391
+ );
3392
+ return;
3393
+ }
3394
+ let token = preparation;
3395
+ if (token === null) {
3396
+ try {
3397
+ token = await delivery.prepare(propagationEvent);
3398
+ } catch (err) {
3399
+ log.warn(
3400
+ `contradiction dependency propagation recovery prepare failed for ${contradiction.supersededId}: ${err}`
3401
+ );
3402
+ }
3403
+ }
3404
+ try {
3405
+ await delivery.afterMutation(token, propagationEvent);
3406
+ } catch (err) {
3407
+ log.warn(
3408
+ `contradiction dependency propagation failed for ${contradiction.supersededId}: ${err}`
3409
+ );
3410
+ }
3411
+ };
3412
+ let commitState = false;
3035
3413
  try {
3036
- const superseded = await storage.supersedeMemory(
3414
+ const committed = await storage.supersedeMemory(
3037
3415
  contradiction.supersededId,
3038
3416
  newMemoryId,
3039
3417
  contradiction.reason
3040
3418
  );
3041
- if (!superseded) {
3419
+ commitState = committed ? true : await supersessionCommitted();
3420
+ if (!committed || commitState !== true) {
3042
3421
  let mergedSnapshot;
3043
3422
  try {
3044
3423
  const [hot, cold] = await Promise.all([
@@ -3055,72 +3434,74 @@ var ContradictionLinkingCoordinator = class {
3055
3434
  log.warn(
3056
3435
  `contradiction auto-resolve race check failed for ${contradiction.supersededId}: ${err}`
3057
3436
  );
3437
+ await settleFailedPropagation(commitState);
3058
3438
  return "supersede_failed";
3059
3439
  }
3060
3440
  const target = mergedSnapshot.find(
3061
3441
  (memory) => memory.frontmatter.id === contradiction.supersededId
3062
3442
  );
3063
- if (!target) return "supersede_failed";
3443
+ if (!target) {
3444
+ await settleFailedPropagation(commitState);
3445
+ return "supersede_failed";
3446
+ }
3064
3447
  const losingMemory = mergedSnapshot.find(
3065
3448
  (memory) => memory.frontmatter.id === newMemoryId && inferLocalizationMemoryStatus(memory, storage.dir) === "active"
3066
3449
  );
3067
- if (!losingMemory) return "supersede_failed";
3450
+ if (!losingMemory) {
3451
+ await settleFailedPropagation(commitState);
3452
+ return "supersede_failed";
3453
+ }
3068
3454
  try {
3069
3455
  if (losingMemory.frontmatter.supersedes === contradiction.supersededId) {
3070
3456
  const cleared = await storage.writeMemoryFrontmatter(losingMemory, {
3071
3457
  supersedes: void 0
3072
3458
  });
3073
- if (cleared === false) return "supersedes_clear_failed";
3459
+ if (cleared === false) {
3460
+ await settleFailedPropagation(commitState);
3461
+ return "supersedes_clear_failed";
3462
+ }
3074
3463
  }
3075
3464
  } catch (err) {
3076
3465
  log.warn(
3077
3466
  `contradiction auto-resolve losing supersedes clear failed for ${newMemoryId}: ${err}`
3078
3467
  );
3468
+ await settleFailedPropagation(commitState);
3079
3469
  return "supersedes_clear_failed";
3080
3470
  }
3081
- return inferLocalizationMemoryStatus(target, storage.dir) === "active" ? "supersede_failed" : "lost_race";
3082
- }
3083
- if (oldMemory) {
3084
- try {
3085
- await propagateInvalidation(
3086
- {
3087
- storage,
3088
- extraction: this.getExtraction(),
3089
- config
3090
- },
3091
- {
3092
- oldMemory,
3093
- replacementId: newMemoryId,
3094
- replacementContent: (await storage.getMemoryById(newMemoryId))?.content ?? null,
3095
- cause: "contradiction",
3096
- namespaceScope: this.namespaceFromPath(storage.dir)
3097
- }
3098
- );
3099
- } catch (err) {
3100
- log.warn(`contradiction dependency propagation failed for ${contradiction.supersededId}: ${err}`);
3101
- }
3102
- }
3103
- if (resolveIndexingCapabilities(config).queryAwareIndexing && contradiction.supersededPath) {
3104
- try {
3105
- await deindexMemoryAsync(
3106
- config.memoryDir,
3107
- contradiction.supersededPath,
3108
- contradiction.supersededCreated,
3109
- contradiction.supersededTags
3110
- );
3111
- } catch (err) {
3112
- log.warn(
3113
- `contradiction auto-resolve deindex failed for ${contradiction.supersededId}: ${err}`
3114
- );
3471
+ const outcome = inferLocalizationMemoryStatus(target, storage.dir) === "active" ? "supersede_failed" : "lost_race";
3472
+ if (commitState === true) {
3473
+ await readyPreparedPropagation();
3474
+ } else {
3475
+ await settleFailedPropagation(commitState);
3115
3476
  }
3477
+ return outcome;
3116
3478
  }
3117
- return "resolved";
3118
3479
  } catch (err) {
3480
+ commitState = await supersessionCommitted();
3119
3481
  log.warn(
3120
3482
  `contradiction auto-resolve supersede failed for ${contradiction.supersededId}: ${err}`
3121
3483
  );
3122
- return "supersede_failed";
3484
+ if (commitState !== true) {
3485
+ await settleFailedPropagation(commitState);
3486
+ return "supersede_failed";
3487
+ }
3488
+ }
3489
+ await readyPreparedPropagation();
3490
+ if (resolveIndexingCapabilities(config).queryAwareIndexing && contradiction.supersededPath) {
3491
+ try {
3492
+ await deindexMemoryAsync(
3493
+ config.memoryDir,
3494
+ contradiction.supersededPath,
3495
+ contradiction.supersededCreated,
3496
+ contradiction.supersededTags
3497
+ );
3498
+ } catch (err) {
3499
+ log.warn(
3500
+ `contradiction auto-resolve deindex failed for ${contradiction.supersededId}: ${err}`
3501
+ );
3502
+ }
3123
3503
  }
3504
+ return "resolved";
3124
3505
  }
3125
3506
  // ---------------------------------------------------------------------------
3126
3507
  // Memory Linking (Phase 3A)
@@ -3172,46 +3553,326 @@ var ContradictionLinkingCoordinator = class {
3172
3553
  // src/orchestration/consolidation-run.ts
3173
3554
  import path4 from "path";
3174
3555
  import { mkdir as mkdir2, readFile, writeFile as writeFile2 } from "fs/promises";
3556
+
3557
+ // src/orchestration/dependency-propagation-queue-state.ts
3558
+ import { createHash as createHash2 } from "crypto";
3559
+ var JOB_STATES = [
3560
+ "prepared",
3561
+ "ready",
3562
+ "leased",
3563
+ "retryable",
3564
+ "completed",
3565
+ "dead_letter",
3566
+ "canceled"
3567
+ ];
3568
+ var CAUSES = {
3569
+ contradiction: true,
3570
+ temporal_supersession: true,
3571
+ consolidation_invalidate: true,
3572
+ consolidation_merge: true
3573
+ };
3574
+ function isFiniteNumber(value) {
3575
+ return typeof value === "number" && Number.isFinite(value);
3576
+ }
3577
+ function isPropagationEvent(value) {
3578
+ if (!isRecord(value) || !isRecord(value.oldMemory)) return false;
3579
+ const oldMemory = value.oldMemory;
3580
+ if (!isRecord(oldMemory.frontmatter)) return false;
3581
+ const frontmatter = oldMemory.frontmatter;
3582
+ if (typeof oldMemory.content !== "string" || typeof frontmatter.id !== "string") return false;
3583
+ if (!(value.replacementId === null || typeof value.replacementId === "string") || !(value.replacementContent === null || typeof value.replacementContent === "string") || typeof value.namespaceScope !== "string" || value.namespaceScope.length === 0 || typeof value.cause !== "string" || !Object.hasOwn(CAUSES, value.cause)) {
3584
+ return false;
3585
+ }
3586
+ const temporalMutation = value.temporalMutation;
3587
+ if (temporalMutation !== void 0 && (!isRecord(temporalMutation) || typeof temporalMutation.supersededAt !== "string" || temporalMutation.invalidAt !== void 0 && typeof temporalMutation.invalidAt !== "string" || !Array.isArray(temporalMutation.matchedKeys) || temporalMutation.matchedKeys.some((key) => typeof key !== "string"))) {
3588
+ return false;
3589
+ }
3590
+ if (frontmatter.links !== void 0) {
3591
+ if (!Array.isArray(frontmatter.links)) return false;
3592
+ for (const link of frontmatter.links) {
3593
+ if (!isRecord(link) || typeof link.targetId !== "string" || typeof link.linkType !== "string") return false;
3594
+ }
3595
+ }
3596
+ return true;
3597
+ }
3598
+ function validateJob(value, expectedState) {
3599
+ if (!isRecord(value)) return null;
3600
+ const status = value.status;
3601
+ const event = value.event;
3602
+ const attempts = value.attempts;
3603
+ const createdAt = value.createdAt;
3604
+ const updatedAt = value.updatedAt;
3605
+ if (typeof value.jobId !== "string" || value.jobId.length === 0 || typeof value.namespace !== "string" || value.namespace.length === 0 || typeof value.sourceId !== "string" || value.sourceId.length === 0 || !isPropagationEvent(event) || value.jobId !== eventJobId(event) || value.namespace !== event.namespaceScope || value.sourceId !== event.oldMemory.frontmatter.id || typeof status !== "string" || !JOB_STATES.includes(status) || expectedState !== void 0 && status !== expectedState || typeof attempts !== "number" || !Number.isInteger(attempts) || attempts < 0 || !isFiniteNumber(createdAt) || !isFiniteNumber(updatedAt)) {
3606
+ return null;
3607
+ }
3608
+ const revision = value.revision === void 0 ? 0 : value.revision;
3609
+ if (typeof revision !== "number" || !Number.isInteger(revision) || revision < 0) return null;
3610
+ const reservationIdsValue = value.reservationIds;
3611
+ if (!Array.isArray(reservationIdsValue)) return null;
3612
+ const reservationIds = reservationIdsValue;
3613
+ if (reservationIds.some(
3614
+ (reservationId) => typeof reservationId !== "string" || reservationId.trim().length === 0
3615
+ ) || new Set(reservationIds).size !== reservationIds.length) {
3616
+ return null;
3617
+ }
3618
+ const reservations = value.reservations;
3619
+ if (typeof reservations !== "number" || !Number.isInteger(reservations) || reservations < 0 || reservations !== reservationIds.length) {
3620
+ return null;
3621
+ }
3622
+ const nextAttemptAt = value.nextAttemptAt;
3623
+ const leaseOwner = value.leaseOwner;
3624
+ const leaseExpiresAt = value.leaseExpiresAt;
3625
+ const lastError = value.lastError;
3626
+ const preparedReplacementFingerprint = value.preparedReplacementFingerprint;
3627
+ if (nextAttemptAt !== void 0 && !isFiniteNumber(nextAttemptAt)) return null;
3628
+ if (leaseOwner !== void 0 && typeof leaseOwner !== "string") return null;
3629
+ if (leaseExpiresAt !== void 0 && !isFiniteNumber(leaseExpiresAt)) return null;
3630
+ if (lastError !== void 0 && typeof lastError !== "string") return null;
3631
+ if (preparedReplacementFingerprint !== void 0 && (typeof preparedReplacementFingerprint !== "string" || preparedReplacementFingerprint.length === 0)) {
3632
+ return null;
3633
+ }
3634
+ if (status === "retryable" && nextAttemptAt === void 0) return null;
3635
+ if (status === "leased" && (typeof leaseOwner !== "string" || leaseExpiresAt === void 0)) return null;
3636
+ return {
3637
+ jobId: value.jobId,
3638
+ namespace: value.namespace,
3639
+ sourceId: value.sourceId,
3640
+ event,
3641
+ status,
3642
+ attempts,
3643
+ createdAt,
3644
+ updatedAt,
3645
+ reservations,
3646
+ reservationIds: [...reservationIds],
3647
+ revision,
3648
+ ...nextAttemptAt === void 0 ? {} : { nextAttemptAt },
3649
+ ...leaseOwner === void 0 ? {} : { leaseOwner },
3650
+ ...leaseExpiresAt === void 0 ? {} : { leaseExpiresAt },
3651
+ ...lastError === void 0 ? {} : { lastError },
3652
+ ...preparedReplacementFingerprint === void 0 ? {} : { preparedReplacementFingerprint }
3653
+ };
3654
+ }
3655
+ function canonicalize(value) {
3656
+ if (Array.isArray(value)) return value.map((item) => canonicalize(item));
3657
+ if (isRecord(value)) {
3658
+ const result = {};
3659
+ for (const key of Object.keys(value).sort()) {
3660
+ if (value[key] === void 0) continue;
3661
+ result[key] = canonicalize(value[key]);
3662
+ }
3663
+ return result;
3664
+ }
3665
+ return value === void 0 ? null : value;
3666
+ }
3667
+ function compareByteStable(left, right) {
3668
+ return Buffer.from(left, "utf8").compare(Buffer.from(right, "utf8"));
3669
+ }
3670
+ function canonicalEvent(event) {
3671
+ return canonicalize(event);
3672
+ }
3673
+ function stableLinks(links) {
3674
+ if (!Array.isArray(links)) return links;
3675
+ return [...links].sort((left, right) => {
3676
+ const leftKey = JSON.stringify(canonicalize(left));
3677
+ const rightKey = JSON.stringify(canonicalize(right));
3678
+ return compareByteStable(leftKey, rightKey);
3679
+ });
3680
+ }
3681
+ function matchesPreparedSource(current, prepared) {
3682
+ const { accessCount: _currentAccessCount, lastAccessed: _currentLastAccessed, ...currentSemantic } = current.frontmatter;
3683
+ const { accessCount: _preparedAccessCount, lastAccessed: _preparedLastAccessed, ...preparedSemantic } = prepared.frontmatter;
3684
+ const normalizedCurrent = { ...currentSemantic, links: stableLinks(currentSemantic.links) };
3685
+ const normalizedPrepared = { ...preparedSemantic, links: stableLinks(preparedSemantic.links) };
3686
+ return current.content === prepared.content && JSON.stringify(canonicalize(normalizedCurrent)) === JSON.stringify(canonicalize(normalizedPrepared));
3687
+ }
3688
+ function eventJobId(event) {
3689
+ const { accessCount: _accessCount, lastAccessed: _lastAccessed, links, ...semanticFrontmatter } = event.oldMemory.frontmatter;
3690
+ const semanticEvent = {
3691
+ ...event,
3692
+ oldMemory: {
3693
+ ...event.oldMemory,
3694
+ frontmatter: { ...semanticFrontmatter, links: stableLinks(links) }
3695
+ }
3696
+ };
3697
+ return createHash2("sha256").update(JSON.stringify(canonicalize(semanticEvent))).digest("hex");
3698
+ }
3699
+ function isNotFound(error) {
3700
+ return isRecord(error) && error.code === "ENOENT";
3701
+ }
3702
+
3703
+ // src/orchestration/consolidation-run.ts
3175
3704
  var MAX_HARMONIC_NAMESPACE_STORES = 50;
3176
3705
  var HARMONIC_CATALOG_CURSOR_FILE = "harmonic-catalog-cursor.json";
3177
- var ConsolidationRunCoordinator = class {
3178
- constructor(deps) {
3179
- this.deps = deps;
3706
+ async function prepareDependencyPropagation(getDelivery, event, enabled) {
3707
+ if (!enabled) return null;
3708
+ let delivery;
3709
+ try {
3710
+ delivery = getDelivery();
3711
+ } catch (error) {
3712
+ log.warn(`consolidation dependency propagation getter failed: ${error}`);
3713
+ return null;
3180
3714
  }
3181
- deps;
3182
- async readHarmonicCatalogCursor(defaultDir) {
3183
- const cursorPath = path4.join(defaultDir, "state", HARMONIC_CATALOG_CURSOR_FILE);
3715
+ let preparation = null;
3716
+ try {
3717
+ preparation = await delivery.prepare(event);
3718
+ } catch (error) {
3719
+ log.warn(`consolidation dependency propagation preparation failed: ${error}`);
3720
+ }
3721
+ return { delivery, event, preparation };
3722
+ }
3723
+ function clonePropagationMemory(memory) {
3724
+ return structuredClone(memory);
3725
+ }
3726
+ function sameMemorySnapshot(a, b) {
3727
+ return a.content === b.content && JSON.stringify(canonicalize(a.frontmatter)) === JSON.stringify(canonicalize(b.frontmatter));
3728
+ }
3729
+ async function prepareCurrentDependencyPropagation(storage, getDelivery, sourceId, buildEvent, enabled) {
3730
+ const loaded = await storage.getMemoryById(sourceId);
3731
+ if (!loaded) return null;
3732
+ let source = clonePropagationMemory(loaded);
3733
+ let pending = await prepareDependencyPropagation(getDelivery, buildEvent(source), enabled);
3734
+ if (!pending) return { source, pending: null };
3735
+ let latest;
3736
+ try {
3737
+ latest = await storage.getMemoryById(sourceId);
3738
+ } catch (error) {
3739
+ await cancelDependencyPropagation(pending);
3740
+ throw error;
3741
+ }
3742
+ if (!latest) {
3743
+ await cancelDependencyPropagation(pending);
3744
+ return null;
3745
+ }
3746
+ if (!sameMemorySnapshot(source, latest)) {
3747
+ await cancelDependencyPropagation(pending);
3748
+ source = clonePropagationMemory(latest);
3749
+ pending = await prepareDependencyPropagation(getDelivery, buildEvent(source), enabled);
3750
+ }
3751
+ return { source, pending };
3752
+ }
3753
+ async function cancelDependencyPropagation(pending) {
3754
+ if (!pending.preparation?.ownsPreparedJob) return;
3755
+ try {
3756
+ await pending.delivery.cancel(pending.preparation);
3757
+ } catch (error) {
3758
+ log.warn(`consolidation dependency propagation cancellation failed: ${error}`);
3759
+ }
3760
+ }
3761
+ async function deferDependencyPropagation(pending) {
3762
+ if (!pending) return;
3763
+ let token = pending.preparation;
3764
+ if (token === null) {
3184
3765
  try {
3185
- const raw = await readFile(cursorPath, "utf-8");
3186
- const parsed = JSON.parse(raw);
3187
- return typeof parsed.nextStorageDir === "string" && parsed.nextStorageDir.length > 0 ? path4.resolve(parsed.nextStorageDir) : void 0;
3766
+ token = await pending.delivery.prepare(pending.event);
3188
3767
  } catch (error) {
3189
- if (error.code !== "ENOENT") {
3190
- log.warn(
3191
- `harmonic namespace catalog cursor read failed open: ${error instanceof Error ? error.message : String(error)}`
3192
- );
3193
- }
3194
- return void 0;
3768
+ log.warn(`consolidation dependency propagation recovery preparation failed: ${error}`);
3195
3769
  }
3196
3770
  }
3197
- async writeHarmonicCatalogCursor(defaultDir, nextStorageDir) {
3198
- const stateDir = path4.join(defaultDir, "state");
3199
- await mkdir2(stateDir, { recursive: true });
3200
- await writeFile2(
3201
- path4.join(stateDir, HARMONIC_CATALOG_CURSOR_FILE),
3202
- JSON.stringify({ nextStorageDir }, null, 2),
3203
- "utf-8"
3204
- );
3771
+ if (token === null) return;
3772
+ try {
3773
+ await pending.delivery.deferPrepared(token);
3774
+ } catch (error) {
3775
+ log.warn(`consolidation dependency propagation defer failed: ${error}`);
3205
3776
  }
3206
- async pruneHarmonicStores(storage) {
3207
- const config = this.deps.config;
3208
- if (!resolveCapabilities(config).harmonicRetrieval || !resolveConsolidationCapabilities(config).abstractionAnchors) {
3209
- return;
3210
- }
3211
- const stores = /* @__PURE__ */ new Map();
3212
- const defaultDir = path4.resolve(storage.dir);
3213
- stores.set(defaultDir, {
3214
- memoryDir: storage.dir,
3777
+ }
3778
+ async function afterDependencyPropagationMutation(pending) {
3779
+ try {
3780
+ await pending.delivery.afterMutation(pending.preparation, pending.event);
3781
+ } catch (error) {
3782
+ log.warn(`consolidation dependency propagation delivery failed: ${error}`);
3783
+ }
3784
+ }
3785
+ async function classifyInvalidation(storage, memoryId, expectedSnapshot, options = {}) {
3786
+ if (options.checkInvalidationProof !== false && expectedSnapshot) {
3787
+ if (!storage.hasCommittedInvalidation) {
3788
+ log.warn(`consolidation invalidation proof capability missing for ${memoryId}`);
3789
+ } else {
3790
+ try {
3791
+ if (await storage.hasCommittedInvalidation(expectedSnapshot)) return "committed";
3792
+ } catch (error) {
3793
+ log.warn(`consolidation invalidation proof read failed for ${memoryId}: ${error}`);
3794
+ }
3795
+ }
3796
+ }
3797
+ try {
3798
+ const current = await storage.getMemoryById(memoryId);
3799
+ if (!current) {
3800
+ if (options.missingMeansCommitted === false) return "not-committed";
3801
+ if (expectedSnapshot && !storage.hasCommittedInvalidation) return "unknown";
3802
+ return "committed";
3803
+ }
3804
+ if (options.matchesCommitted?.(current)) return "committed";
3805
+ if (expectedSnapshot && sameMemorySnapshot(current, expectedSnapshot)) {
3806
+ return "not-committed";
3807
+ }
3808
+ return "unknown";
3809
+ } catch (error) {
3810
+ log.warn(`consolidation invalidation state read failed for ${memoryId}: ${error}`);
3811
+ return "unknown";
3812
+ }
3813
+ }
3814
+ async function settleFailedConsolidationPropagation(pending, commitState, memoryId) {
3815
+ if (!pending) return;
3816
+ let token = pending.preparation;
3817
+ if (commitState === "not-committed") {
3818
+ if (token !== null) await cancelDependencyPropagation({ ...pending, preparation: token });
3819
+ return;
3820
+ }
3821
+ if (token === null && (commitState === "committed" || commitState === "unknown")) {
3822
+ try {
3823
+ token = await pending.delivery.prepare(pending.event);
3824
+ } catch (error) {
3825
+ log.warn(
3826
+ `consolidation dependency propagation recovery preparation failed for ${memoryId}: ${error}`
3827
+ );
3828
+ }
3829
+ }
3830
+ if (token !== null) {
3831
+ try {
3832
+ await pending.delivery.deferPrepared(token);
3833
+ } catch (error) {
3834
+ log.warn(`consolidation dependency propagation defer failed for ${memoryId}: ${error}`);
3835
+ }
3836
+ }
3837
+ }
3838
+ var ConsolidationRunCoordinator = class {
3839
+ constructor(deps) {
3840
+ this.deps = deps;
3841
+ }
3842
+ deps;
3843
+ async readHarmonicCatalogCursor(defaultDir) {
3844
+ const cursorPath = path4.join(defaultDir, "state", HARMONIC_CATALOG_CURSOR_FILE);
3845
+ try {
3846
+ const raw = await readFile(cursorPath, "utf-8");
3847
+ const parsed = JSON.parse(raw);
3848
+ return typeof parsed.nextStorageDir === "string" && parsed.nextStorageDir.length > 0 ? path4.resolve(parsed.nextStorageDir) : void 0;
3849
+ } catch (error) {
3850
+ if (error.code !== "ENOENT") {
3851
+ log.warn(
3852
+ `harmonic namespace catalog cursor read failed open: ${error instanceof Error ? error.message : String(error)}`
3853
+ );
3854
+ }
3855
+ return void 0;
3856
+ }
3857
+ }
3858
+ async writeHarmonicCatalogCursor(defaultDir, nextStorageDir) {
3859
+ const stateDir = path4.join(defaultDir, "state");
3860
+ await mkdir2(stateDir, { recursive: true });
3861
+ await writeFile2(
3862
+ path4.join(stateDir, HARMONIC_CATALOG_CURSOR_FILE),
3863
+ JSON.stringify({ nextStorageDir }, null, 2),
3864
+ "utf-8"
3865
+ );
3866
+ }
3867
+ async pruneHarmonicStores(storage) {
3868
+ const config = this.deps.config;
3869
+ if (!resolveCapabilities(config).harmonicRetrieval || !resolveConsolidationCapabilities(config).abstractionAnchors) {
3870
+ return;
3871
+ }
3872
+ const stores = /* @__PURE__ */ new Map();
3873
+ const defaultDir = path4.resolve(storage.dir);
3874
+ stores.set(defaultDir, {
3875
+ memoryDir: storage.dir,
3215
3876
  abstractionNodeStoreDir: config.abstractionNodeStoreDir
3216
3877
  });
3217
3878
  try {
@@ -3297,49 +3958,65 @@ var ConsolidationRunCoordinator = class {
3297
3958
  );
3298
3959
  const profile = await storage.readProfile();
3299
3960
  const result = await this.deps.getExtraction().consolidate(recent, older, profile);
3300
- const memoryLookup = resolveIndexingCapabilities(config).queryAwareIndexing ? new Map(allMemories.map((m) => [m.frontmatter.id, m])) : null;
3301
3961
  const itemsDeindexBatch = [];
3302
3962
  try {
3303
3963
  for (const item of result.items) {
3304
3964
  switch (item.action) {
3305
3965
  case "INVALIDATE": {
3306
- const toInvalidate = resolveIndexingCapabilities(config).queryAwareIndexing ? memoryLookup?.get(item.existingId) ?? null : null;
3307
- const propagationOld = allMemories.find(
3308
- (memory) => memory.frontmatter.id === item.existingId
3966
+ const propagationEnabled = config.dependencyPropagation.enabled && config.dependencyPropagation.maxDependents > 0;
3967
+ const prepared = await prepareCurrentDependencyPropagation(
3968
+ storage,
3969
+ this.deps.getDependencyPropagationDelivery,
3970
+ item.existingId,
3971
+ (propagationOld2) => ({
3972
+ oldMemory: propagationOld2,
3973
+ replacementId: null,
3974
+ replacementContent: null,
3975
+ cause: "consolidation_invalidate",
3976
+ namespaceScope: this.deps.storageDirNamespace(storage.dir)
3977
+ }),
3978
+ propagationEnabled
3309
3979
  );
3310
- if (await storage.invalidateMemory(item.existingId)) {
3311
- invalidated += 1;
3312
- memoryItemMutated = true;
3313
- await this.deps.embeddingFallback.removeFromIndex(item.existingId);
3314
- if (propagationOld) {
3315
- try {
3316
- await propagateInvalidation(
3317
- {
3318
- storage,
3319
- extraction: this.deps.getExtraction(),
3320
- config
3321
- },
3322
- {
3323
- oldMemory: propagationOld,
3324
- replacementId: null,
3325
- replacementContent: null,
3326
- cause: "consolidation_invalidate",
3327
- namespaceScope: config.defaultNamespace
3328
- }
3329
- );
3330
- } catch (propagationErr) {
3331
- log.warn(
3332
- `consolidation dependency propagation failed for ${item.existingId}: ${propagationErr}`
3333
- );
3334
- }
3335
- }
3336
- if (toInvalidate?.path && toInvalidate.frontmatter?.created) {
3337
- itemsDeindexBatch.push({
3338
- path: toInvalidate.path,
3339
- createdAt: toInvalidate.frontmatter.created,
3340
- tags: toInvalidate.frontmatter.tags ?? []
3341
- });
3342
- }
3980
+ const propagationOld = prepared?.source ?? null;
3981
+ const pending = prepared?.pending ?? null;
3982
+ const expectedSnapshot = pending ? propagationOld : null;
3983
+ const toInvalidate = resolveIndexingCapabilities(config).queryAwareIndexing ? propagationOld : null;
3984
+ const recordCommitProof = pending?.preparation?.ownsPreparedJob === true;
3985
+ let didInvalidate;
3986
+ try {
3987
+ didInvalidate = await storage.invalidateMemory(
3988
+ item.existingId,
3989
+ expectedSnapshot ?? void 0,
3990
+ { recordCommitProof }
3991
+ );
3992
+ } catch (error) {
3993
+ const commitState = await classifyInvalidation(storage, item.existingId, expectedSnapshot);
3994
+ await settleFailedConsolidationPropagation(
3995
+ pending,
3996
+ commitState,
3997
+ item.existingId
3998
+ );
3999
+ throw error;
4000
+ }
4001
+ if (!didInvalidate) {
4002
+ const commitState = await classifyInvalidation(storage, item.existingId, expectedSnapshot);
4003
+ await settleFailedConsolidationPropagation(
4004
+ pending,
4005
+ commitState,
4006
+ item.existingId
4007
+ );
4008
+ break;
4009
+ }
4010
+ invalidated += 1;
4011
+ memoryItemMutated = true;
4012
+ if (pending) await afterDependencyPropagationMutation(pending);
4013
+ await this.deps.embeddingFallback.removeFromIndex(item.existingId);
4014
+ if (toInvalidate?.path && toInvalidate.frontmatter?.created) {
4015
+ itemsDeindexBatch.push({
4016
+ path: toInvalidate.path,
4017
+ createdAt: toInvalidate.frontmatter.created,
4018
+ tags: toInvalidate.frontmatter.tags ?? []
4019
+ });
3343
4020
  }
3344
4021
  break;
3345
4022
  }
@@ -3358,53 +4035,81 @@ var ConsolidationRunCoordinator = class {
3358
4035
  break;
3359
4036
  case "MERGE":
3360
4037
  if (item.updatedContent && item.mergeWith) {
3361
- await storage.updateMemory(
3362
- item.existingId,
3363
- item.updatedContent,
3364
- {
3365
- supersedes: item.mergeWith,
3366
- lineage: [item.existingId, item.mergeWith]
3367
- }
4038
+ const updatedContent = item.updatedContent;
4039
+ const mergeWith = item.mergeWith;
4040
+ const propagationEnabled = config.dependencyPropagation.enabled && config.dependencyPropagation.maxDependents > 0;
4041
+ const prepared = await prepareCurrentDependencyPropagation(
4042
+ storage,
4043
+ this.deps.getDependencyPropagationDelivery,
4044
+ item.mergeWith,
4045
+ (propagationOld2) => ({
4046
+ oldMemory: propagationOld2,
4047
+ replacementId: item.existingId,
4048
+ replacementContent: updatedContent,
4049
+ cause: "consolidation_merge",
4050
+ namespaceScope: this.deps.storageDirNamespace(storage.dir)
4051
+ }),
4052
+ propagationEnabled
3368
4053
  );
4054
+ const propagationOld = prepared?.source ?? null;
4055
+ const pending = prepared?.pending ?? null;
4056
+ const expectedSnapshot = pending ? propagationOld : null;
4057
+ const toMergeInvalidate = resolveIndexingCapabilities(config).queryAwareIndexing ? propagationOld : null;
4058
+ let didUpdate;
4059
+ try {
4060
+ didUpdate = await storage.updateMemory(
4061
+ item.existingId,
4062
+ updatedContent,
4063
+ {
4064
+ supersedes: item.mergeWith,
4065
+ lineage: [item.existingId, item.mergeWith]
4066
+ }
4067
+ );
4068
+ } catch (error) {
4069
+ await deferDependencyPropagation(pending);
4070
+ throw error;
4071
+ }
4072
+ if (!didUpdate) {
4073
+ if (pending) await cancelDependencyPropagation(pending);
4074
+ break;
4075
+ }
3369
4076
  memoryItemMutated = true;
3370
4077
  await this.deps.indexPersistedMemory(storage, item.existingId);
3371
- const toMergeInvalidate = resolveIndexingCapabilities(config).queryAwareIndexing ? memoryLookup?.get(item.mergeWith) ?? null : null;
3372
- const propagationOld = allMemories.find(
3373
- (memory) => memory.frontmatter.id === item.mergeWith
3374
- );
3375
- if (await storage.invalidateMemory(item.mergeWith)) {
3376
- invalidated += 1;
3377
- merged += 1;
3378
- await this.deps.embeddingFallback.removeFromIndex(item.mergeWith);
3379
- if (propagationOld) {
3380
- try {
3381
- await propagateInvalidation(
3382
- {
3383
- storage,
3384
- extraction: this.deps.getExtraction(),
3385
- config
3386
- },
3387
- {
3388
- oldMemory: propagationOld,
3389
- replacementId: item.existingId,
3390
- replacementContent: item.updatedContent,
3391
- cause: "consolidation_merge",
3392
- namespaceScope: config.defaultNamespace
3393
- }
3394
- );
3395
- } catch (propagationErr) {
3396
- log.warn(
3397
- `consolidation dependency propagation failed for ${item.mergeWith}: ${propagationErr}`
3398
- );
3399
- }
3400
- }
3401
- if (toMergeInvalidate?.path && toMergeInvalidate.frontmatter?.created) {
3402
- itemsDeindexBatch.push({
3403
- path: toMergeInvalidate.path,
3404
- createdAt: toMergeInvalidate.frontmatter.created,
3405
- tags: toMergeInvalidate.frontmatter.tags ?? []
3406
- });
3407
- }
4078
+ let didInvalidate;
4079
+ try {
4080
+ didInvalidate = await storage.invalidateMemory(
4081
+ item.mergeWith,
4082
+ expectedSnapshot ?? void 0,
4083
+ { recordCommitProof: pending?.preparation?.ownsPreparedJob === true }
4084
+ );
4085
+ } catch (error) {
4086
+ const commitState = await classifyInvalidation(storage, item.mergeWith, expectedSnapshot);
4087
+ await settleFailedConsolidationPropagation(
4088
+ pending,
4089
+ commitState,
4090
+ item.mergeWith
4091
+ );
4092
+ throw error;
4093
+ }
4094
+ if (!didInvalidate) {
4095
+ const commitState = await classifyInvalidation(storage, item.mergeWith, expectedSnapshot);
4096
+ await settleFailedConsolidationPropagation(
4097
+ pending,
4098
+ commitState,
4099
+ item.mergeWith
4100
+ );
4101
+ break;
4102
+ }
4103
+ invalidated += 1;
4104
+ merged += 1;
4105
+ if (pending) await afterDependencyPropagationMutation(pending);
4106
+ await this.deps.embeddingFallback.removeFromIndex(item.mergeWith);
4107
+ if (toMergeInvalidate?.path && toMergeInvalidate.frontmatter?.created) {
4108
+ itemsDeindexBatch.push({
4109
+ path: toMergeInvalidate.path,
4110
+ createdAt: toMergeInvalidate.frontmatter.created,
4111
+ tags: toMergeInvalidate.frontmatter.tags ?? []
4112
+ });
3408
4113
  }
3409
4114
  }
3410
4115
  break;
@@ -4028,6 +4733,10 @@ var ExtractionPersistCoordinator = class {
4028
4733
  get config() {
4029
4734
  return this.deps.config;
4030
4735
  }
4736
+ get dependencyPropagationDelivery() {
4737
+ if (!isDependencyPropagationEnabled(this.config)) return void 0;
4738
+ return this.deps.getDependencyPropagationDelivery();
4739
+ }
4031
4740
  async persistExtraction(result, storage, threadIdForExtraction, sourceContext, baseNamespace, scopeProfileWritePlan, sourceText, graphCaps = resolveGraphConstructionCapabilities(this.deps.config), lifecycleCaps = resolveMemoryLifecycleCapabilities(this.deps.config)) {
4032
4741
  const citationEnabled = resolvePipelineProcessingCapabilities(this.deps.config).inlineSourceAttribution === true;
4033
4742
  const citationTemplate = this.deps.config.inlineSourceAttributionFormat;
@@ -4238,8 +4947,7 @@ var ExtractionPersistCoordinator = class {
4238
4947
  structuredAttributes: { ...targetSurvivingAttrs },
4239
4948
  createdAt: supersessionOrderingAt(options.validAt),
4240
4949
  enabled: !(options.eventTimeSource === "extracted" && !options.validAt),
4241
- extraction: this.deps.getExtraction(),
4242
- config: this.config,
4950
+ dependencyPropagationDelivery: this.dependencyPropagationDelivery,
4243
4951
  namespaceScope: this.deps.storageDirNamespace(targetStorage.dir)
4244
4952
  });
4245
4953
  } catch (profileSupersessionErr) {
@@ -4380,10 +5088,9 @@ var ExtractionPersistCoordinator = class {
4380
5088
  entityRef: sharedPromotionEnvelope.entityRef,
4381
5089
  structuredAttributes: { ...sharedSurvivingAttrs },
4382
5090
  createdAt: supersessionOrderingAt(options.validAt),
4383
- enabled: !(options.eventTimeSource === "extracted" && !options.validAt),
4384
5091
  useCallerTimestamp: true,
4385
- extraction: this.deps.getExtraction(),
4386
- config: this.config,
5092
+ enabled: !(options.eventTimeSource === "extracted" && !options.validAt),
5093
+ dependencyPropagationDelivery: this.dependencyPropagationDelivery,
4387
5094
  namespaceScope: this.deps.storageDirNamespace(sharedStorage.dir)
4388
5095
  });
4389
5096
  if (hashDedupSupersession.supersededIds.length > 0) {
@@ -4455,8 +5162,7 @@ var ExtractionPersistCoordinator = class {
4455
5162
  structuredAttributes: { ...sharedSurvivingAttrs },
4456
5163
  createdAt: supersessionOrderingAt(options.validAt),
4457
5164
  enabled: !(options.eventTimeSource === "extracted" && !options.validAt),
4458
- extraction: this.deps.getExtraction(),
4459
- config: this.config,
5165
+ dependencyPropagationDelivery: this.dependencyPropagationDelivery,
4460
5166
  namespaceScope: this.deps.storageDirNamespace(sharedStorage.dir)
4461
5167
  });
4462
5168
  } catch (sharedSupersessionErr) {
@@ -5280,13 +5986,12 @@ var ExtractionPersistCoordinator = class {
5280
5986
  newMemoryId: parentId,
5281
5987
  entityRef: parentWriteEnvelope.entityRef,
5282
5988
  structuredAttributes: parentWriteEnvelope.rawStructuredAttributes ? { ...parentWriteEnvelope.rawStructuredAttributes } : void 0,
5283
- createdAt: supersessionOrderingAt(biTemporal?.validFrom ?? sourceContext?.validAt),
5284
5989
  // #1578 r3: an extracted end-only bound (validFrom absent) is
5285
5990
  // historical, not a new authoritative state — never let it
5286
5991
  // supersede a later active fact (codex P1 on :15534).
5992
+ createdAt: supersessionOrderingAt(biTemporal?.validFrom ?? sourceContext?.validAt),
5287
5993
  enabled: lifecycleCaps.temporalSupersession && !(biTemporal && !biTemporal.validFrom),
5288
- extraction: this.deps.getExtraction(),
5289
- config: this.config,
5994
+ dependencyPropagationDelivery: this.dependencyPropagationDelivery,
5290
5995
  namespaceScope: this.deps.storageDirNamespace(targetStorage.dir)
5291
5996
  });
5292
5997
  for (const supersededId of temporalSupersession.supersededIds) {
@@ -5486,8 +6191,7 @@ var ExtractionPersistCoordinator = class {
5486
6191
  structuredAttributes: factWriteEnvelope.rawStructuredAttributes ? { ...factWriteEnvelope.rawStructuredAttributes } : void 0,
5487
6192
  createdAt: supersessionOrderingAt(biTemporal?.validFrom ?? sourceContext?.validAt),
5488
6193
  enabled: lifecycleCaps.temporalSupersession && !(biTemporal && !biTemporal.validFrom),
5489
- extraction: this.deps.getExtraction(),
5490
- config: this.config,
6194
+ dependencyPropagationDelivery: this.dependencyPropagationDelivery,
5491
6195
  namespaceScope: this.deps.storageDirNamespace(targetStorage.dir)
5492
6196
  });
5493
6197
  for (const supersededId of temporalSupersession.supersededIds) {
@@ -5685,84 +6389,1127 @@ var ExtractionPersistCoordinator = class {
5685
6389
  }
5686
6390
  }
5687
6391
  }
5688
- if (profileUpdates.length > 0) {
5689
- await storage.appendToProfile(profileUpdates);
5690
- recordDurableNonFactWrite();
6392
+ if (profileUpdates.length > 0) {
6393
+ await storage.appendToProfile(profileUpdates);
6394
+ recordDurableNonFactWrite();
6395
+ }
6396
+ for (const q of questions) {
6397
+ const id = await storage.writeQuestion(q.question, q.context, q.priority);
6398
+ if (id) {
6399
+ trackPersistedId(storage, id);
6400
+ recordDurableNonFactWrite();
6401
+ }
6402
+ }
6403
+ if (resolveRecallEnhancementCapabilities(this.deps.config).identity && result.identityReflection) {
6404
+ try {
6405
+ await storage.appendIdentityReflection(result.identityReflection);
6406
+ recordDurableNonFactWrite();
6407
+ } catch (err) {
6408
+ log.debug(`identity reflection write failed: ${err}`);
6409
+ }
6410
+ }
6411
+ if (durableNonFactWritten) {
6412
+ touchBaseNonFactNamespace();
6413
+ }
6414
+ await this.deps.saveContentHashIndexes().catch((err) => {
6415
+ log.warn(`content-hash index save failed: ${err}`);
6416
+ });
6417
+ for (const {
6418
+ storage: targetStorage,
6419
+ events
6420
+ } of behaviorSignalsByStorage.values()) {
6421
+ const dedupedSignals = dedupeBehaviorSignalsByMemoryAndHash(events);
6422
+ if (dedupedSignals.length === 0) continue;
6423
+ await targetStorage.appendBehaviorSignals(dedupedSignals).catch(
6424
+ (err) => log.warn(`appendBehaviorSignals failed (non-fatal): ${err}`)
6425
+ );
6426
+ }
6427
+ const dedupSuffix = dedupedCount > 0 ? ` (${dedupedCount} deduped)` : "";
6428
+ const gatedSuffix = importanceGatedCount > 0 ? ` (${importanceGatedCount} gated)` : "";
6429
+ const judgeSuffix = judgeGatedCount > 0 ? ` (${judgeGatedCount} judge-rejected)` : "";
6430
+ const redactionSuffix = redactionGatedCount > 0 ? ` (${redactionGatedCount} redacted)` : "";
6431
+ log.info(
6432
+ `persisted: ${facts.length - dedupedCount - importanceGatedCount - judgeGatedCount - redactionGatedCount} facts${dedupSuffix}${gatedSuffix}${judgeSuffix}${redactionSuffix}, ${entities.length} entities, ${questions.length} questions, ${profileUpdates.length} profile updates`
6433
+ );
6434
+ if (harmonicConstructionEnabled && harmonicFactsByStorage.size > 0) {
6435
+ await persistConstructedHarmonicRecords({
6436
+ entries: harmonicFactsByStorage.values(),
6437
+ baseStorageDir: storage.dir,
6438
+ abstractionNodeStoreDir: !scopeProfileWritePlan && harmonicBaseNamespace === this.deps.config.defaultNamespace ? this.deps.config.abstractionNodeStoreDir : void 0,
6439
+ sessionKey: sourceContext?.sessionKey,
6440
+ validAt: sourceContext?.validAt,
6441
+ episodeTitle: result.episodeTitle,
6442
+ anchorsEnabled: harmonicAnchorsEnabled,
6443
+ entityMentions: entities
6444
+ });
6445
+ }
6446
+ void (async () => {
6447
+ if (persistedIdsByStorage.size === 0) {
6448
+ await this.deps.updateTemporalTagIndexes(storage, []);
6449
+ return;
6450
+ }
6451
+ for (const entry of persistedIdsByStorage.values()) {
6452
+ await this.deps.updateTemporalTagIndexes(entry.storage, entry.ids);
6453
+ }
6454
+ })().catch(
6455
+ (err) => log.debug(`temporal-index update error (non-fatal): ${err}`)
6456
+ );
6457
+ this.deps.setLastPersistExtractionPendingReviewIds(pendingReviewPersistedIds);
6458
+ return { persistedIds, memoryPathById };
6459
+ }
6460
+ };
6461
+
6462
+ // src/orchestration/extraction-shutdown.ts
6463
+ async function drainExtractionAndShutdownDependencyPropagation(extractionQueue, dependencyPropagationDelivery) {
6464
+ let extractionDrainError;
6465
+ try {
6466
+ if (!await extractionQueue.pauseAndDrain()) {
6467
+ extractionDrainError = new Error("orchestrator.destroy: extraction queue did not drain before teardown");
6468
+ }
6469
+ } catch (error) {
6470
+ extractionDrainError = error;
6471
+ }
6472
+ try {
6473
+ await dependencyPropagationDelivery?.shutdown();
6474
+ } catch (error) {
6475
+ log.warn(`orchestrator.destroy: dependency propagation shutdown failed: ${error}`);
6476
+ }
6477
+ return extractionDrainError;
6478
+ }
6479
+
6480
+ // src/orchestration/dependency-propagation-delivery.ts
6481
+ import { randomUUID } from "crypto";
6482
+ import { readFile as readFile2, unlink } from "fs/promises";
6483
+ import path6 from "path";
6484
+
6485
+ // src/orchestration/dependency-propagation-recovery-replay.ts
6486
+ import { createHash as createHash3 } from "crypto";
6487
+ function memoryFingerprint(memory) {
6488
+ const { accessCount: _accessCount, lastAccessed: _lastAccessed, ...frontmatter } = memory.frontmatter;
6489
+ return createHash3("sha256").update(JSON.stringify(canonicalize({ content: memory.content, frontmatter }))).digest("hex");
6490
+ }
6491
+ async function findRecoverySource(storage, sourceId) {
6492
+ const hot = await storage.getMemoryById(sourceId);
6493
+ if (hot?.frontmatter.id === sourceId) return hot;
6494
+ if (storage.readAllColdMemories) {
6495
+ const cold = await storage.readAllColdMemories();
6496
+ const coldMatch = cold.find((memory) => memory.frontmatter.id === sourceId);
6497
+ if (coldMatch) return coldMatch;
6498
+ }
6499
+ if (storage.readArchivedMemories) {
6500
+ const archived = await storage.readArchivedMemories();
6501
+ const archivedMatch = archived.find((memory) => memory.frontmatter.id === sourceId);
6502
+ if (archivedMatch) return archivedMatch;
6503
+ }
6504
+ return null;
6505
+ }
6506
+ async function readCurrentReplacementEvent(storage, event) {
6507
+ if (event.replacementId === null) return event;
6508
+ const replacement = await findRecoverySource(
6509
+ storage,
6510
+ event.replacementId
6511
+ );
6512
+ if (!replacement) return null;
6513
+ return {
6514
+ ...event,
6515
+ replacementContent: replacement.content
6516
+ };
6517
+ }
6518
+ async function replayPrimaryMutation(sourceStorage, event, preparedReplacementFingerprint) {
6519
+ const storage = sourceStorage;
6520
+ const source = await findRecoverySource(storage, event.oldMemory.frontmatter.id);
6521
+ const primaryApplied = source?.frontmatter.status === "superseded" && source.frontmatter.supersededBy === event.replacementId;
6522
+ if (event.cause === "temporal_supersession") {
6523
+ if (!event.replacementId || !event.temporalMutation || !source) return false;
6524
+ const temporalPrimaryApplied = primaryApplied && source.frontmatter.supersededAt === event.temporalMutation.supersededAt && (event.temporalMutation.invalidAt === void 0 || source.frontmatter.invalid_at === event.temporalMutation.invalidAt);
6525
+ if (!temporalPrimaryApplied) {
6526
+ const replacement2 = await findRecoverySource(storage, event.replacementId);
6527
+ if (event.replacementContent === null || !replacement2 || replacement2.frontmatter.id !== event.replacementId || replacement2.content !== event.replacementContent)
6528
+ return false;
6529
+ if (!matchesPreparedSource(source, event.oldMemory)) return false;
6530
+ }
6531
+ return await applyTemporalSupersessionPrimaryMutation({
6532
+ storage: sourceStorage,
6533
+ oldMemory: source,
6534
+ replacementId: event.replacementId,
6535
+ mutation: event.temporalMutation
6536
+ });
6537
+ }
6538
+ if (event.cause === "contradiction") {
6539
+ if (!event.replacementId || !source) return false;
6540
+ if (primaryApplied) return true;
6541
+ const replacement2 = await findRecoverySource(storage, event.replacementId);
6542
+ if (event.replacementContent === null || !replacement2 || replacement2.frontmatter.id !== event.replacementId || replacement2.content !== event.replacementContent)
6543
+ return false;
6544
+ if (!matchesPreparedSource(source, event.oldMemory)) return false;
6545
+ return await storage.supersedeMemory(
6546
+ event.oldMemory.frontmatter.id,
6547
+ event.replacementId,
6548
+ `dependency_propagation:${event.cause}`,
6549
+ void 0,
6550
+ { requireActive: true, acceptExactReplay: true, expectedSnapshot: source }
6551
+ );
6552
+ }
6553
+ if (event.cause === "consolidation_invalidate") {
6554
+ if (!source) {
6555
+ return storage.hasCommittedInvalidation ? await storage.hasCommittedInvalidation(event.oldMemory) : false;
6556
+ }
6557
+ if (!storage.invalidateMemory || !matchesPreparedSource(source, event.oldMemory)) return false;
6558
+ return await storage.invalidateMemory(source.frontmatter.id, source, {
6559
+ recordCommitProof: true
6560
+ });
6561
+ }
6562
+ if (!event.replacementId || event.replacementContent === null || !storage.updateMemoryIfUnchanged) {
6563
+ return false;
6564
+ }
6565
+ const replacement = await findRecoverySource(storage, event.replacementId);
6566
+ const persistedReplacementContent = sanitizeMemoryContent(event.replacementContent).text;
6567
+ if (replacement && replacement.frontmatter.supersedes === event.oldMemory.frontmatter.id && replacement.content !== persistedReplacementContent) {
6568
+ return false;
6569
+ }
6570
+ const mergeAlreadyApplied = replacement !== null && replacement.frontmatter.supersedes === event.oldMemory.frontmatter.id && replacement.content === persistedReplacementContent;
6571
+ if (!source) {
6572
+ if (!storage.hasCommittedInvalidation) return false;
6573
+ return await storage.hasCommittedInvalidation(event.oldMemory);
6574
+ }
6575
+ if (preparedReplacementFingerprint && source && !mergeAlreadyApplied && (!replacement || memoryFingerprint(replacement) !== preparedReplacementFingerprint)) {
6576
+ return false;
6577
+ }
6578
+ if (!replacement || !matchesPreparedSource(source, event.oldMemory)) return false;
6579
+ if (!mergeAlreadyApplied) {
6580
+ const updated = await storage.updateMemoryIfUnchanged(
6581
+ replacement,
6582
+ event.replacementContent,
6583
+ {
6584
+ supersedes: event.oldMemory.frontmatter.id,
6585
+ lineage: [event.replacementId, event.oldMemory.frontmatter.id]
6586
+ }
6587
+ );
6588
+ if (!updated) return false;
6589
+ }
6590
+ if (!storage.invalidateMemory) return false;
6591
+ return await storage.invalidateMemory(source.frontmatter.id, source, {
6592
+ recordCommitProof: true
6593
+ });
6594
+ }
6595
+
6596
+ // src/orchestration/dependency-propagation-delivery.ts
6597
+ var DEFAULT_RETRY_DELAY_MS = 1e3;
6598
+ var DEFAULT_LEASE_MS = 6e4;
6599
+ var DEFAULT_MAX_ATTEMPTS = 3;
6600
+ var TERMINAL_RETENTION_COUNT = 32;
6601
+ var MAX_SERIALIZED_QUEUE_JOB_BYTES = 64 * 1024;
6602
+ var MAX_STORED_ERROR_LENGTH = 1024;
6603
+ var QueueJobTooLargeError = class extends Error {
6604
+ constructor(bytes) {
6605
+ super(`dependency propagation queue job exceeds ${MAX_SERIALIZED_QUEUE_JOB_BYTES} bytes (${bytes})`);
6606
+ this.name = "QueueJobTooLargeError";
6607
+ }
6608
+ };
6609
+ function formatStoredError(error) {
6610
+ const text = String(error);
6611
+ return text.length > MAX_STORED_ERROR_LENGTH ? text.slice(0, MAX_STORED_ERROR_LENGTH) : text;
6612
+ }
6613
+ function chooseEntry(entries) {
6614
+ return [...entries].sort((left, right) => {
6615
+ if (left.job.revision !== right.job.revision) return right.job.revision - left.job.revision;
6616
+ if (left.job.updatedAt !== right.job.updatedAt) return right.job.updatedAt - left.job.updatedAt;
6617
+ return left.filePath < right.filePath ? -1 : left.filePath > right.filePath ? 1 : 0;
6618
+ })[0];
6619
+ }
6620
+ var DependencyPropagationDelivery = class {
6621
+ queueRoot;
6622
+ getStorage;
6623
+ config;
6624
+ extraction;
6625
+ workerId;
6626
+ clock;
6627
+ retryDelayMs;
6628
+ leaseMs;
6629
+ maxAttempts;
6630
+ autoStart;
6631
+ schedule;
6632
+ readQueueFile;
6633
+ writeQueueFile;
6634
+ replayReconciliation;
6635
+ backgroundScheduled = false;
6636
+ recoveryScheduled = false;
6637
+ backgroundRun = null;
6638
+ activeRuns = /* @__PURE__ */ new Set();
6639
+ stopped = false;
6640
+ constructor(options) {
6641
+ this.queueRoot = options.queueRoot;
6642
+ this.config = options.config;
6643
+ this.extraction = options.extraction;
6644
+ this.getStorage = options.getStorage;
6645
+ this.workerId = options.workerId ?? `worker-${process.pid}-${randomUUID()}`;
6646
+ this.clock = options.clock ?? Date.now;
6647
+ this.retryDelayMs = Math.max(0, options.retryDelayMs ?? DEFAULT_RETRY_DELAY_MS);
6648
+ this.leaseMs = Math.max(1, options.leaseMs ?? DEFAULT_LEASE_MS);
6649
+ this.maxAttempts = Math.max(1, Math.floor(options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS));
6650
+ this.autoStart = options.autoStart ?? false;
6651
+ this.schedule = options.schedule ?? ((run, delayMs) => {
6652
+ const timer = setTimeout(() => {
6653
+ void run().catch((error) => {
6654
+ log.warn(`dependency propagation scheduled run failed: ${error}`);
6655
+ });
6656
+ }, delayMs);
6657
+ timer.unref();
6658
+ });
6659
+ this.readQueueFile = options.readQueueFile ?? ((filePath) => readFile2(filePath, "utf8"));
6660
+ this.writeQueueFile = options.writeQueueFile ?? ((filePath, content) => writeJsonFileAtomic(filePath, JSON.parse(content)));
6661
+ this.replayReconciliation = options.replayReconciliation;
6662
+ }
6663
+ async prepare(event) {
6664
+ if (this.stopped) return null;
6665
+ if (!isDependencyPropagationEnabled(this.config)) return null;
6666
+ const reservationId = randomUUID();
6667
+ try {
6668
+ if (!isPropagationEvent(event)) throw new Error("invalid dependency propagation event");
6669
+ const snapshot = canonicalEvent(event);
6670
+ const jobId = eventJobId(snapshot);
6671
+ let preparedReplacementFingerprint;
6672
+ if (snapshot.cause === "consolidation_merge" && snapshot.replacementId !== null) {
6673
+ try {
6674
+ const storage = await this.getStorage(snapshot.namespaceScope);
6675
+ const replacement = await storage.getMemoryById(snapshot.replacementId);
6676
+ if (!replacement) {
6677
+ log.warn(
6678
+ `dependency propagation survivor lookup returned no record for ${snapshot.replacementId}`
6679
+ );
6680
+ return null;
6681
+ }
6682
+ preparedReplacementFingerprint = memoryFingerprint(replacement);
6683
+ } catch (error) {
6684
+ log.warn(`dependency propagation survivor fingerprint capture failed: ${error}`);
6685
+ return null;
6686
+ }
6687
+ }
6688
+ let token = null;
6689
+ await withJsonStoreMutationLock(this.queueRoot, async () => {
6690
+ const entries = await this.readEntries();
6691
+ const existing = chooseEntry(entries.filter((entry) => entry.job.jobId === jobId));
6692
+ if (existing && existing.job.status === "canceled") {
6693
+ const reopened = {
6694
+ ...existing.job,
6695
+ status: "prepared",
6696
+ attempts: 0,
6697
+ reservations: 1,
6698
+ reservationIds: [reservationId],
6699
+ nextAttemptAt: void 0,
6700
+ leaseOwner: void 0,
6701
+ leaseExpiresAt: void 0,
6702
+ lastError: void 0,
6703
+ updatedAt: this.clock(),
6704
+ revision: existing.job.revision + 1
6705
+ };
6706
+ await this.writeQueueJob(await this.jobPath("prepared", jobId), reopened);
6707
+ await unlink(existing.filePath).catch((error) => {
6708
+ if (!isNotFound(error)) throw error;
6709
+ });
6710
+ token = {
6711
+ jobId,
6712
+ revision: reopened.revision,
6713
+ ownsPreparedJob: true,
6714
+ reservationId
6715
+ };
6716
+ return;
6717
+ }
6718
+ if (existing && existing.job.status === "prepared") {
6719
+ const reservationIds = existing.job.reservationIds ?? [];
6720
+ const next = {
6721
+ ...existing.job,
6722
+ reservations: reservationIds.length + 1,
6723
+ reservationIds: [...reservationIds, reservationId],
6724
+ updatedAt: this.clock()
6725
+ };
6726
+ await this.writeQueueJob(existing.filePath, next);
6727
+ token = {
6728
+ jobId,
6729
+ revision: existing.job.revision,
6730
+ ownsPreparedJob: true,
6731
+ reservationId
6732
+ };
6733
+ return;
6734
+ }
6735
+ if (existing) {
6736
+ token = {
6737
+ jobId,
6738
+ revision: existing.job.revision,
6739
+ ownsPreparedJob: false,
6740
+ reservationId
6741
+ };
6742
+ return;
6743
+ }
6744
+ const now = this.clock();
6745
+ const job = {
6746
+ jobId,
6747
+ namespace: snapshot.namespaceScope,
6748
+ sourceId: snapshot.oldMemory.frontmatter.id,
6749
+ event: snapshot,
6750
+ status: "prepared",
6751
+ attempts: 0,
6752
+ createdAt: now,
6753
+ updatedAt: now,
6754
+ reservations: 1,
6755
+ reservationIds: [reservationId],
6756
+ revision: 0,
6757
+ ...preparedReplacementFingerprint ? { preparedReplacementFingerprint } : {}
6758
+ };
6759
+ await this.writeQueueJob(await this.jobPath(job.status, job.jobId), job);
6760
+ token = {
6761
+ jobId,
6762
+ revision: job.revision,
6763
+ ownsPreparedJob: true,
6764
+ reservationId
6765
+ };
6766
+ });
6767
+ return token;
6768
+ } catch (error) {
6769
+ log.warn(`dependency propagation queue preparation failed: ${error}`);
6770
+ if (error instanceof QueueJobTooLargeError) return null;
6771
+ try {
6772
+ if (isPropagationEvent(event)) {
6773
+ const snapshot = canonicalEvent(event);
6774
+ const jobId = eventJobId(snapshot);
6775
+ return await withJsonStoreMutationLock(this.queueRoot, async () => {
6776
+ const existing = chooseEntry(
6777
+ (await this.readEntries()).filter((entry) => entry.job.jobId === jobId)
6778
+ );
6779
+ if (!existing || existing.job.status !== "prepared") return null;
6780
+ const reservationIds = existing.job.reservationIds ?? [];
6781
+ if (!reservationIds.includes(reservationId)) {
6782
+ reservationIds.push(reservationId);
6783
+ await this.writeQueueJob(existing.filePath, {
6784
+ ...existing.job,
6785
+ reservationIds,
6786
+ reservations: reservationIds.length,
6787
+ updatedAt: this.clock()
6788
+ });
6789
+ }
6790
+ return {
6791
+ jobId,
6792
+ revision: existing.job.revision,
6793
+ ownsPreparedJob: true,
6794
+ reservationId
6795
+ };
6796
+ });
6797
+ }
6798
+ } catch (readbackError) {
6799
+ log.warn(`dependency propagation queue preparation readback failed: ${readbackError}`);
6800
+ }
6801
+ return null;
6802
+ }
6803
+ }
6804
+ async afterMutation(token, event) {
6805
+ if (this.stopped) return;
6806
+ if (token !== null) {
6807
+ try {
6808
+ await withJsonStoreMutationLock(this.queueRoot, async () => {
6809
+ const entries = await this.readEntries();
6810
+ const matching = entries.filter((entry) => entry.job.jobId === token.jobId);
6811
+ const current = chooseEntry(matching);
6812
+ if (!current) throw new Error(`dependency propagation job ${token.jobId} is missing`);
6813
+ if (current.job.status === "prepared" && current.job.revision === token.revision) {
6814
+ const reservationIds = current.job.reservationIds;
6815
+ if (reservationIds && !reservationIds.includes(token.reservationId)) return;
6816
+ await this.transitionUnlocked(matching, current.job, "ready", {
6817
+ ...reservationIds ? {
6818
+ reservationIds: reservationIds.filter((id) => id !== token.reservationId),
6819
+ reservations: Math.max(0, reservationIds.length - 1)
6820
+ } : {}
6821
+ });
6822
+ }
6823
+ });
6824
+ this.scheduleBackgroundRun();
6825
+ return;
6826
+ } catch (error) {
6827
+ log.warn(`dependency propagation ready transition failed: ${error}`);
6828
+ this.scheduleBackgroundRecovery();
6829
+ return;
6830
+ }
6831
+ }
6832
+ await this.propagateDirect(event);
6833
+ }
6834
+ async cancel(token) {
6835
+ if (token === null) return;
6836
+ try {
6837
+ let terminalJob = null;
6838
+ await withJsonStoreMutationLock(this.queueRoot, async () => {
6839
+ const entries = await this.readEntries();
6840
+ const matching = entries.filter((entry) => entry.job.jobId === token.jobId);
6841
+ const current = chooseEntry(matching);
6842
+ if (!current || current.job.status !== "prepared" || current.job.revision !== token.revision) {
6843
+ return;
6844
+ }
6845
+ const reservationIds = current.job.reservationIds;
6846
+ if (reservationIds && !reservationIds.includes(token.reservationId)) return;
6847
+ if (reservationIds && reservationIds.length > 1) {
6848
+ const remaining = reservationIds.filter((id) => id !== token.reservationId);
6849
+ await this.writeQueueJob(current.filePath, {
6850
+ ...current.job,
6851
+ reservationIds: remaining,
6852
+ reservations: remaining.length,
6853
+ updatedAt: this.clock()
6854
+ });
6855
+ return;
6856
+ }
6857
+ terminalJob = await this.transitionUnlocked(matching, current.job, "canceled", {
6858
+ reservationIds: [],
6859
+ reservations: 0,
6860
+ lastError: void 0
6861
+ });
6862
+ });
6863
+ if (terminalJob !== null && !await this.finalizeTerminalJob(terminalJob)) {
6864
+ this.scheduleBackgroundRecovery();
6865
+ }
6866
+ } catch (error) {
6867
+ log.warn(`dependency propagation cancellation failed for ${token.jobId}: ${error}`);
6868
+ }
6869
+ }
6870
+ async deferPrepared(token) {
6871
+ if (this.stopped || token === null) return;
6872
+ try {
6873
+ const shouldSchedule = await withJsonStoreMutationLock(this.queueRoot, async () => {
6874
+ const entries = await this.readEntries();
6875
+ const current = chooseEntry(
6876
+ entries.filter((entry) => entry.job.jobId === token.jobId)
6877
+ );
6878
+ if (!current || current.job.status !== "prepared" || current.job.revision !== token.revision) {
6879
+ return false;
6880
+ }
6881
+ const reservationIds = current.job.reservationIds;
6882
+ return !reservationIds || reservationIds.includes(token.reservationId);
6883
+ });
6884
+ if (shouldSchedule) this.scheduleBackgroundRecovery();
6885
+ } catch (error) {
6886
+ log.warn(`dependency propagation prepared-job deferral failed for ${token.jobId}: ${error}`);
6887
+ }
6888
+ }
6889
+ trackRun(run) {
6890
+ this.activeRuns.add(run);
6891
+ void run.finally(() => this.activeRuns.delete(run)).catch(() => void 0);
6892
+ return run;
6893
+ }
6894
+ recover() {
6895
+ return this.trackRun(this.recoverInternal());
6896
+ }
6897
+ async recoverInternal() {
6898
+ let jobs;
6899
+ try {
6900
+ jobs = await this.listJobs();
6901
+ } catch (error) {
6902
+ log.warn(`dependency propagation recovery scan failed: ${error}`);
6903
+ this.scheduleBackgroundRecovery();
6904
+ return;
6905
+ }
6906
+ let retryPreparedRecovery = false;
6907
+ for (const job of jobs) {
6908
+ if (job.status === "completed" || job.status === "dead_letter" || job.status === "canceled") {
6909
+ retryPreparedRecovery ||= !await this.finalizeTerminalJob(job);
6910
+ }
6911
+ }
6912
+ const now = this.clock();
6913
+ for (const job of jobs) {
6914
+ if (job.status !== "leased" || job.leaseOwner === void 0 || !job.leaseOwner.includes(":recovery:") || (job.leaseExpiresAt ?? Number.POSITIVE_INFINITY) > now)
6915
+ continue;
6916
+ let claimed;
6917
+ try {
6918
+ claimed = await this.claimExpiredRecoveryLease(job);
6919
+ } catch (error) {
6920
+ retryPreparedRecovery = true;
6921
+ log.warn(`dependency propagation expired recovery lease claim failed: ${error}`);
6922
+ continue;
6923
+ }
6924
+ if (!claimed) continue;
6925
+ const outcome = await this.recoverPrimaryMutation(claimed);
6926
+ retryPreparedRecovery ||= outcome.retry;
6927
+ }
6928
+ for (const job of jobs) {
6929
+ if (job.status !== "prepared") continue;
6930
+ let claimed;
6931
+ try {
6932
+ claimed = await this.claimPrepared(job);
6933
+ } catch (error) {
6934
+ retryPreparedRecovery = true;
6935
+ log.warn(`dependency propagation prepared-job claim failed: ${error}`);
6936
+ continue;
6937
+ }
6938
+ if (!claimed) continue;
6939
+ const outcome = await this.recoverPrimaryMutation(claimed);
6940
+ retryPreparedRecovery ||= outcome.retry;
6941
+ }
6942
+ if (retryPreparedRecovery) this.scheduleBackgroundRecovery();
6943
+ await this.scheduleNextPendingRun();
6944
+ }
6945
+ async claimPrepared(job) {
6946
+ return withJsonStoreMutationLock(this.queueRoot, async () => {
6947
+ const entries = await this.readEntries();
6948
+ const matching = entries.filter((entry) => entry.job.jobId === job.jobId);
6949
+ const current = chooseEntry(matching);
6950
+ if (!current || current.job.status !== "prepared" || current.job.revision !== job.revision) {
6951
+ return null;
6952
+ }
6953
+ return this.transitionUnlocked(matching, current.job, "leased", {
6954
+ leaseOwner: `${this.workerId}:recovery:${randomUUID()}`,
6955
+ leaseExpiresAt: this.clock() + this.leaseMs,
6956
+ nextAttemptAt: void 0,
6957
+ lastError: void 0
6958
+ });
6959
+ });
6960
+ }
6961
+ async claimExpiredRecoveryLease(job) {
6962
+ return withJsonStoreMutationLock(this.queueRoot, async () => {
6963
+ const entries = await this.readEntries();
6964
+ const matching = entries.filter((entry) => entry.job.jobId === job.jobId);
6965
+ const current = chooseEntry(matching);
6966
+ if (!current || current.job.status !== "leased" || current.job.leaseOwner !== job.leaseOwner) {
6967
+ return null;
6968
+ }
6969
+ if ((current.job.leaseExpiresAt ?? Number.POSITIVE_INFINITY) > this.clock()) {
6970
+ return null;
6971
+ }
6972
+ return this.transitionUnlocked(matching, current.job, "leased", {
6973
+ leaseOwner: `${this.workerId}:recovery:${randomUUID()}`,
6974
+ leaseExpiresAt: this.clock() + this.leaseMs,
6975
+ nextAttemptAt: void 0,
6976
+ lastError: void 0
6977
+ });
6978
+ });
6979
+ }
6980
+ async recoverPrimaryMutation(claimed) {
6981
+ try {
6982
+ const storage = await this.getStorage(claimed.event.namespaceScope);
6983
+ const replayed = await this.withCurrentLease(
6984
+ claimed,
6985
+ () => replayPrimaryMutation(
6986
+ storage,
6987
+ claimed.event,
6988
+ claimed.preparedReplacementFingerprint
6989
+ )
6990
+ );
6991
+ if (replayed !== true) {
6992
+ return {
6993
+ retry: await this.retainPreparedJob(
6994
+ claimed,
6995
+ new Error("primary mutation replay incomplete")
6996
+ )
6997
+ };
6998
+ }
6999
+ if (this.replayReconciliation) {
7000
+ if (claimed.event.cause === "consolidation_merge" && claimed.event.replacementId) {
7001
+ await this.replayReconciliation.reindex(
7002
+ claimed.event,
7003
+ claimed.event.replacementId
7004
+ );
7005
+ }
7006
+ await this.replayReconciliation.deindex(
7007
+ claimed.event,
7008
+ [claimed.event.oldMemory.frontmatter.id]
7009
+ );
7010
+ }
7011
+ await withJsonStoreMutationLock(this.queueRoot, async () => {
7012
+ const entries = await this.readEntries();
7013
+ const matching = entries.filter((entry) => entry.job.jobId === claimed.jobId);
7014
+ const current = chooseEntry(matching);
7015
+ if (current && current.job.status === "leased" && current.job.leaseOwner === claimed.leaseOwner && (current.job.leaseExpiresAt ?? Number.NEGATIVE_INFINITY) > this.clock()) {
7016
+ await this.transitionUnlocked(matching, current.job, "ready", {
7017
+ leaseOwner: void 0,
7018
+ leaseExpiresAt: void 0
7019
+ });
7020
+ }
7021
+ });
7022
+ return { retry: false };
7023
+ } catch (error) {
7024
+ log.warn(`dependency propagation prepared-job recovery failed: ${error}`);
7025
+ return { retry: await this.retainPreparedJob(claimed, error) };
7026
+ }
7027
+ }
7028
+ runUntilIdle() {
7029
+ return this.trackRun(this.runUntilIdleInternal());
7030
+ }
7031
+ async runUntilIdleInternal() {
7032
+ let processed = 0;
7033
+ while (true) {
7034
+ let claimed;
7035
+ try {
7036
+ claimed = await this.claimNext();
7037
+ } catch (error) {
7038
+ log.warn(`dependency propagation claim failed: ${error}`);
7039
+ this.scheduleBackgroundRecovery();
7040
+ break;
7041
+ }
7042
+ if (!claimed) break;
7043
+ processed += 1;
7044
+ await this.processClaimed(claimed);
7045
+ }
7046
+ return processed;
7047
+ }
7048
+ scheduleBackgroundRun(delayMs = 0) {
7049
+ if (!this.autoStart || this.stopped || this.backgroundScheduled) return;
7050
+ this.backgroundScheduled = true;
7051
+ this.schedule(async () => {
7052
+ this.backgroundScheduled = false;
7053
+ if (this.stopped) return;
7054
+ if (this.backgroundRun !== null) {
7055
+ await this.backgroundRun.catch(() => void 0);
7056
+ this.scheduleBackgroundRun();
7057
+ return;
7058
+ }
7059
+ const run = this.runUntilIdle();
7060
+ this.backgroundRun = run;
7061
+ try {
7062
+ await run;
7063
+ } finally {
7064
+ this.backgroundRun = null;
7065
+ }
7066
+ await this.scheduleNextPendingRun();
7067
+ }, delayMs);
7068
+ }
7069
+ scheduleBackgroundRecovery(delayMs = this.retryDelayMs) {
7070
+ if (!this.autoStart || this.stopped || this.recoveryScheduled) return;
7071
+ this.recoveryScheduled = true;
7072
+ this.schedule(async () => {
7073
+ this.recoveryScheduled = false;
7074
+ if (this.stopped) return;
7075
+ await this.recover();
7076
+ }, delayMs);
7077
+ }
7078
+ async scheduleNextPendingRun() {
7079
+ if (!this.autoStart || this.stopped || this.backgroundScheduled) return;
7080
+ let jobs;
7081
+ try {
7082
+ jobs = await this.listJobs();
7083
+ } catch (error) {
7084
+ log.warn(`dependency propagation scheduling scan failed: ${error}`);
7085
+ this.scheduleBackgroundRecovery();
7086
+ return;
7087
+ }
7088
+ const now = this.clock();
7089
+ let nextDelayMs = Number.POSITIVE_INFINITY;
7090
+ for (const job of jobs) {
7091
+ if (job.status === "ready") {
7092
+ nextDelayMs = 0;
7093
+ break;
7094
+ }
7095
+ if (job.status === "retryable" && job.nextAttemptAt !== void 0) {
7096
+ nextDelayMs = Math.min(nextDelayMs, Math.max(0, job.nextAttemptAt - now));
7097
+ }
7098
+ if (job.status === "leased" && job.leaseExpiresAt !== void 0) {
7099
+ nextDelayMs = Math.min(nextDelayMs, Math.max(0, job.leaseExpiresAt - now));
7100
+ }
7101
+ }
7102
+ if (Number.isFinite(nextDelayMs)) this.scheduleBackgroundRun(nextDelayMs);
7103
+ }
7104
+ async listJobs() {
7105
+ const entries = await this.readEntries();
7106
+ const grouped = /* @__PURE__ */ new Map();
7107
+ for (const entry of entries) {
7108
+ const group = grouped.get(entry.job.jobId) ?? [];
7109
+ group.push(entry);
7110
+ grouped.set(entry.job.jobId, group);
7111
+ }
7112
+ return [...grouped.values()].map((group) => chooseEntry(group)?.job).filter((job) => job !== void 0).sort((left, right) => compareByteStable(left.jobId, right.jobId));
7113
+ }
7114
+ async shutdown() {
7115
+ this.stopped = true;
7116
+ while (true) {
7117
+ const activeRuns = [...this.activeRuns];
7118
+ if (activeRuns.length > 0) {
7119
+ await Promise.all(activeRuns.map((run) => run.catch(() => 0)));
7120
+ continue;
7121
+ }
7122
+ if (this.backgroundScheduled) {
7123
+ this.backgroundScheduled = false;
7124
+ await this.runUntilIdle();
7125
+ continue;
7126
+ }
7127
+ break;
7128
+ }
7129
+ for (let pass = 0; pass <= this.maxAttempts; pass += 1) {
7130
+ await this.recover();
7131
+ await this.runUntilIdle();
7132
+ const jobs = await this.listJobs();
7133
+ const now = this.clock();
7134
+ const actionable = jobs.some(
7135
+ (job) => job.status === "prepared" || job.status === "ready" || job.status === "retryable" && (job.nextAttemptAt === void 0 || job.nextAttemptAt <= now) || job.status === "leased" && job.leaseExpiresAt !== void 0 && job.leaseExpiresAt <= now
7136
+ );
7137
+ if (!actionable) return;
7138
+ }
7139
+ }
7140
+ async stateDir(status) {
7141
+ return resolveSafeStoragePath(this.queueRoot, status);
7142
+ }
7143
+ async jobPath(status, jobId) {
7144
+ return resolveSafeStoragePath(this.queueRoot, status, `${jobId}.json`);
7145
+ }
7146
+ async writeQueueJob(filePath, job) {
7147
+ const serialized = JSON.stringify(job, null, 2);
7148
+ const bytes = Buffer.byteLength(serialized, "utf8");
7149
+ if (bytes > MAX_SERIALIZED_QUEUE_JOB_BYTES) throw new QueueJobTooLargeError(bytes);
7150
+ await this.writeQueueFile(filePath, serialized);
7151
+ }
7152
+ async readEntries() {
7153
+ const entries = [];
7154
+ for (const status of JOB_STATES) {
7155
+ let files;
7156
+ try {
7157
+ files = await listJsonFilesStrict(await this.stateDir(status), { allowMissingDirectory: true });
7158
+ } catch (error) {
7159
+ log.warn(`dependency propagation state scan failed for ${status}: ${error}`);
7160
+ throw error;
7161
+ }
7162
+ for (const filePath of files) {
7163
+ try {
7164
+ const job = validateJob(JSON.parse(await this.readQueueFile(filePath)), status);
7165
+ if (!job) {
7166
+ log.warn(`dependency propagation ignored invalid job file ${path6.basename(filePath)}`);
7167
+ continue;
7168
+ }
7169
+ entries.push({ job, filePath });
7170
+ } catch (error) {
7171
+ if (error instanceof SyntaxError || isNotFound(error)) {
7172
+ log.warn(`dependency propagation ignored unreadable job file ${path6.basename(filePath)}: ${error}`);
7173
+ continue;
7174
+ }
7175
+ throw error;
7176
+ }
7177
+ }
7178
+ }
7179
+ return entries;
7180
+ }
7181
+ async clearCommittedInvalidationProof(job) {
7182
+ if (job.event.cause !== "consolidation_invalidate" && job.event.cause !== "consolidation_merge") {
7183
+ return true;
5691
7184
  }
5692
- for (const q of questions) {
5693
- const id = await storage.writeQuestion(q.question, q.context, q.priority);
5694
- if (id) {
5695
- trackPersistedId(storage, id);
5696
- recordDurableNonFactWrite();
7185
+ try {
7186
+ const storage = await this.getStorage(job.event.namespaceScope);
7187
+ if (!storage.clearCommittedInvalidation) {
7188
+ log.warn("dependency propagation invalidation proof cleanup capability missing");
7189
+ return false;
5697
7190
  }
7191
+ await storage.clearCommittedInvalidation(job.event.oldMemory);
7192
+ return true;
7193
+ } catch (error) {
7194
+ log.warn(`dependency propagation invalidation proof cleanup failed: ${error}`);
7195
+ return false;
5698
7196
  }
5699
- if (resolveRecallEnhancementCapabilities(this.deps.config).identity && result.identityReflection) {
5700
- try {
5701
- await storage.appendIdentityReflection(result.identityReflection);
5702
- recordDurableNonFactWrite();
5703
- } catch (err) {
5704
- log.debug(`identity reflection write failed: ${err}`);
7197
+ }
7198
+ async retainPreparedJob(job, error) {
7199
+ try {
7200
+ let terminalJob = null;
7201
+ const retained = await withJsonStoreMutationLock(this.queueRoot, async () => {
7202
+ const entries = await this.readEntries();
7203
+ const matching = entries.filter((entry) => entry.job.jobId === job.jobId);
7204
+ const current = chooseEntry(matching);
7205
+ if (!current || current.job.status !== "leased" || current.job.leaseOwner !== job.leaseOwner) {
7206
+ return false;
7207
+ }
7208
+ const attempts = current.job.attempts + 1;
7209
+ if (attempts >= this.maxAttempts) {
7210
+ terminalJob = await this.transitionUnlocked(matching, current.job, "dead_letter", {
7211
+ attempts,
7212
+ lastError: formatStoredError(error)
7213
+ });
7214
+ return false;
7215
+ }
7216
+ await this.transitionUnlocked(matching, current.job, "prepared", {
7217
+ attempts,
7218
+ lastError: formatStoredError(error)
7219
+ });
7220
+ return true;
7221
+ });
7222
+ if (terminalJob !== null && !await this.finalizeTerminalJob(terminalJob)) {
7223
+ this.scheduleBackgroundRecovery();
5705
7224
  }
7225
+ return retained;
7226
+ } catch (retainError) {
7227
+ log.warn(`dependency propagation prepared-job retention failed: ${retainError}`);
7228
+ return true;
5706
7229
  }
5707
- if (durableNonFactWritten) {
5708
- touchBaseNonFactNamespace();
7230
+ }
7231
+ async transitionUnlocked(entries, job, status, patch = {}) {
7232
+ const next = {
7233
+ ...job,
7234
+ ...patch,
7235
+ status,
7236
+ updatedAt: this.clock(),
7237
+ revision: job.revision + 1
7238
+ };
7239
+ const targetPath = await this.jobPath(status, next.jobId);
7240
+ await this.writeQueueJob(targetPath, next);
7241
+ const terminal = status === "completed" || status === "dead_letter" || status === "canceled";
7242
+ for (const entry of entries) {
7243
+ if (path6.resolve(entry.filePath) === path6.resolve(targetPath)) continue;
7244
+ await unlink(entry.filePath).catch((error) => {
7245
+ if (isNotFound(error)) return;
7246
+ if (terminal) {
7247
+ log.warn(`dependency propagation old terminal state unlink failed: ${error}`);
7248
+ return;
7249
+ }
7250
+ throw error;
7251
+ });
5709
7252
  }
5710
- await this.deps.saveContentHashIndexes().catch((err) => {
5711
- log.warn(`content-hash index save failed: ${err}`);
5712
- });
5713
- for (const {
5714
- storage: targetStorage,
5715
- events
5716
- } of behaviorSignalsByStorage.values()) {
5717
- const dedupedSignals = dedupeBehaviorSignalsByMemoryAndHash(events);
5718
- if (dedupedSignals.length === 0) continue;
5719
- await targetStorage.appendBehaviorSignals(dedupedSignals).catch(
5720
- (err) => log.warn(`appendBehaviorSignals failed (non-fatal): ${err}`)
7253
+ return next;
7254
+ }
7255
+ async finalizeTerminalJob(job) {
7256
+ if (!await this.clearCommittedInvalidationProof(job)) return false;
7257
+ try {
7258
+ return await withJsonStoreMutationLock(
7259
+ this.queueRoot,
7260
+ () => this.pruneTerminalStatusUnlocked(job.status)
5721
7261
  );
7262
+ } catch (error) {
7263
+ log.warn(`dependency propagation terminal finalization failed: ${error}`);
7264
+ return false;
5722
7265
  }
5723
- const dedupSuffix = dedupedCount > 0 ? ` (${dedupedCount} deduped)` : "";
5724
- const gatedSuffix = importanceGatedCount > 0 ? ` (${importanceGatedCount} gated)` : "";
5725
- const judgeSuffix = judgeGatedCount > 0 ? ` (${judgeGatedCount} judge-rejected)` : "";
5726
- const redactionSuffix = redactionGatedCount > 0 ? ` (${redactionGatedCount} redacted)` : "";
5727
- log.info(
5728
- `persisted: ${facts.length - dedupedCount - importanceGatedCount - judgeGatedCount - redactionGatedCount} facts${dedupSuffix}${gatedSuffix}${judgeSuffix}${redactionSuffix}, ${entities.length} entities, ${questions.length} questions, ${profileUpdates.length} profile updates`
5729
- );
5730
- if (harmonicConstructionEnabled && harmonicFactsByStorage.size > 0) {
5731
- await persistConstructedHarmonicRecords({
5732
- entries: harmonicFactsByStorage.values(),
5733
- baseStorageDir: storage.dir,
5734
- abstractionNodeStoreDir: !scopeProfileWritePlan && harmonicBaseNamespace === this.deps.config.defaultNamespace ? this.deps.config.abstractionNodeStoreDir : void 0,
5735
- sessionKey: sourceContext?.sessionKey,
5736
- validAt: sourceContext?.validAt,
5737
- episodeTitle: result.episodeTitle,
5738
- anchorsEnabled: harmonicAnchorsEnabled,
5739
- entityMentions: entities
7266
+ }
7267
+ async pruneTerminalStatusUnlocked(status) {
7268
+ try {
7269
+ const latestByJobId = /* @__PURE__ */ new Map();
7270
+ for (const entry of (await this.readEntries()).filter((item) => item.job.status === status)) {
7271
+ const current = latestByJobId.get(entry.job.jobId);
7272
+ if (!current || chooseEntry([current, entry]) === entry) {
7273
+ latestByJobId.set(entry.job.jobId, entry);
7274
+ }
7275
+ }
7276
+ const entries = [...latestByJobId.values()].sort((left, right) => {
7277
+ if (left.job.updatedAt !== right.job.updatedAt) {
7278
+ return right.job.updatedAt > left.job.updatedAt ? 1 : -1;
7279
+ }
7280
+ if (left.job.revision !== right.job.revision) {
7281
+ return right.job.revision > left.job.revision ? 1 : -1;
7282
+ }
7283
+ return compareByteStable(left.filePath, right.filePath);
5740
7284
  });
7285
+ const oldJobs = entries.slice(TERMINAL_RETENTION_COUNT);
7286
+ for (const entry of oldJobs) {
7287
+ if (!await this.clearCommittedInvalidationProof(entry.job)) return false;
7288
+ }
7289
+ let removed = true;
7290
+ for (const entry of oldJobs) {
7291
+ const jobRemoved = await this.removeAllQueueFilesForJobUnlocked(entry.job.jobId);
7292
+ removed = jobRemoved && removed;
7293
+ }
7294
+ return removed;
7295
+ } catch (error) {
7296
+ log.warn(`dependency propagation terminal retention failed: ${error}`);
7297
+ return false;
5741
7298
  }
5742
- void (async () => {
5743
- if (persistedIdsByStorage.size === 0) {
5744
- await this.deps.updateTemporalTagIndexes(storage, []);
7299
+ }
7300
+ async removeAllQueueFilesForJobUnlocked(jobId) {
7301
+ let removed = true;
7302
+ for (const status of JOB_STATES) {
7303
+ const files = await listJsonFilesStrict(await this.stateDir(status), {
7304
+ allowMissingDirectory: true
7305
+ });
7306
+ for (const filePath of files) {
7307
+ if (path6.basename(filePath, ".json") !== jobId) continue;
7308
+ try {
7309
+ await unlink(filePath);
7310
+ } catch (error) {
7311
+ if (!isNotFound(error)) {
7312
+ removed = false;
7313
+ log.warn(`dependency propagation queue cleanup unlink failed: ${error}`);
7314
+ }
7315
+ }
7316
+ }
7317
+ }
7318
+ return removed;
7319
+ }
7320
+ async claimNext() {
7321
+ return withJsonStoreMutationLock(this.queueRoot, async () => {
7322
+ const now = this.clock();
7323
+ const entries = await this.readEntries();
7324
+ const candidates = /* @__PURE__ */ new Map();
7325
+ for (const entry of entries) {
7326
+ const group = candidates.get(entry.job.jobId) ?? [];
7327
+ group.push(entry);
7328
+ candidates.set(entry.job.jobId, group);
7329
+ }
7330
+ const jobs = [...candidates.values()].map((group) => ({ group, current: chooseEntry(group) })).filter((item) => item.current !== void 0).sort((left, right) => compareByteStable(left.current.job.jobId, right.current.job.jobId));
7331
+ for (const item of jobs) {
7332
+ const current = item.current.job;
7333
+ const expiredLease = current.status === "leased" && (current.leaseExpiresAt ?? Number.POSITIVE_INFINITY) <= now;
7334
+ const ready = current.status === "ready";
7335
+ const retryable = current.status === "retryable" && (current.nextAttemptAt ?? Number.POSITIVE_INFINITY) <= now;
7336
+ if (!ready && !retryable && !expiredLease) continue;
7337
+ return this.transitionUnlocked(item.group, current, "leased", {
7338
+ attempts: current.attempts + 1,
7339
+ leaseOwner: `${this.workerId}:${randomUUID()}`,
7340
+ leaseExpiresAt: now + this.leaseMs,
7341
+ nextAttemptAt: void 0,
7342
+ lastError: void 0
7343
+ });
7344
+ }
7345
+ return null;
7346
+ });
7347
+ }
7348
+ async processClaimed(job) {
7349
+ let renewal = Promise.resolve();
7350
+ const heartbeatMs = Math.max(1, Math.floor(this.leaseMs / 3));
7351
+ const heartbeat = setInterval(() => {
7352
+ renewal = renewal.then(() => this.renewLease(job)).catch((error) => {
7353
+ log.warn(`dependency propagation lease renewal failed: ${error}`);
7354
+ });
7355
+ }, heartbeatMs);
7356
+ heartbeat.unref();
7357
+ try {
7358
+ const storage = await this.getStorage(job.event.namespaceScope);
7359
+ const currentEvent = await readCurrentReplacementEvent(storage, job.event);
7360
+ if (currentEvent === null) {
7361
+ await this.finishAttempt(job, new Error("persisted replacement is missing"));
5745
7362
  return;
5746
7363
  }
5747
- for (const entry of persistedIdsByStorage.values()) {
5748
- await this.deps.updateTemporalTagIndexes(entry.storage, entry.ids);
7364
+ const result = await propagateInvalidation(
7365
+ {
7366
+ storage,
7367
+ extraction: this.extraction,
7368
+ config: this.config,
7369
+ writeFence: (write) => this.withCurrentLease(job, write)
7370
+ },
7371
+ currentEvent
7372
+ );
7373
+ const accountedDependents = result.invalidated + result.stillValid + result.uncertain;
7374
+ if (result.skipped === "timeout" || result.skipped === "llm_error") {
7375
+ await this.finishAttempt(job, new Error(`propagation skipped: ${result.skipped}`));
7376
+ } else if (accountedDependents < result.dependentsFound) {
7377
+ await this.finishAttempt(job, new Error("one or more dependent writes did not commit"));
7378
+ } else {
7379
+ await this.finishAttempt(job, null);
5749
7380
  }
5750
- })().catch(
5751
- (err) => log.debug(`temporal-index update error (non-fatal): ${err}`)
5752
- );
5753
- this.deps.setLastPersistExtractionPendingReviewIds(pendingReviewPersistedIds);
5754
- return { persistedIds, memoryPathById };
7381
+ } catch (error) {
7382
+ await this.finishAttempt(job, error);
7383
+ } finally {
7384
+ clearInterval(heartbeat);
7385
+ await renewal;
7386
+ }
7387
+ }
7388
+ async withCurrentLease(job, write) {
7389
+ return withJsonStoreMutationLock(this.queueRoot, async () => {
7390
+ const entries = await this.readEntries();
7391
+ const current = chooseEntry(entries.filter((entry) => entry.job.jobId === job.jobId));
7392
+ if (current?.job.status !== "leased" || current.job.leaseOwner !== job.leaseOwner || (current.job.leaseExpiresAt ?? Number.NEGATIVE_INFINITY) <= this.clock()) {
7393
+ return void 0;
7394
+ }
7395
+ const result = await write();
7396
+ const renewedEntries = await this.readEntries();
7397
+ const renewedMatching = renewedEntries.filter((entry) => entry.job.jobId === job.jobId);
7398
+ const renewedCurrent = chooseEntry(renewedMatching);
7399
+ if (renewedCurrent && renewedCurrent.job.status === "leased" && renewedCurrent.job.leaseOwner === job.leaseOwner) {
7400
+ await this.transitionUnlocked(renewedMatching, renewedCurrent.job, "leased", {
7401
+ leaseExpiresAt: this.clock() + this.leaseMs
7402
+ });
7403
+ }
7404
+ return result;
7405
+ });
7406
+ }
7407
+ async renewLease(job) {
7408
+ await withJsonStoreMutationLock(this.queueRoot, async () => {
7409
+ const entries = await this.readEntries();
7410
+ const matching = entries.filter((entry) => entry.job.jobId === job.jobId);
7411
+ const current = chooseEntry(matching);
7412
+ if (!current || current.job.status !== "leased" || current.job.leaseOwner !== job.leaseOwner || (current.job.leaseExpiresAt ?? Number.NEGATIVE_INFINITY) <= this.clock()) {
7413
+ return;
7414
+ }
7415
+ await this.transitionUnlocked(matching, current.job, "leased", {
7416
+ leaseExpiresAt: this.clock() + this.leaseMs
7417
+ });
7418
+ });
7419
+ }
7420
+ async finishAttempt(job, error) {
7421
+ let retryDelayMs = null;
7422
+ let terminalJob = null;
7423
+ try {
7424
+ await withJsonStoreMutationLock(this.queueRoot, async () => {
7425
+ const entries = await this.readEntries();
7426
+ const matching = entries.filter((entry) => entry.job.jobId === job.jobId);
7427
+ const current = chooseEntry(matching);
7428
+ if (!current || current.job.status !== "leased" || current.job.leaseOwner !== job.leaseOwner || (current.job.leaseExpiresAt ?? Number.NEGATIVE_INFINITY) <= this.clock()) {
7429
+ return;
7430
+ }
7431
+ if (error === null) {
7432
+ terminalJob = await this.transitionUnlocked(matching, current.job, "completed", {
7433
+ leaseOwner: void 0,
7434
+ leaseExpiresAt: void 0,
7435
+ nextAttemptAt: void 0,
7436
+ lastError: void 0
7437
+ });
7438
+ return;
7439
+ }
7440
+ if (current.job.attempts >= this.maxAttempts) {
7441
+ terminalJob = await this.transitionUnlocked(matching, current.job, "dead_letter", {
7442
+ leaseOwner: void 0,
7443
+ leaseExpiresAt: void 0,
7444
+ nextAttemptAt: void 0,
7445
+ lastError: formatStoredError(error)
7446
+ });
7447
+ return;
7448
+ }
7449
+ retryDelayMs = this.retryDelayMs * 2 ** Math.max(0, current.job.attempts - 1);
7450
+ await this.transitionUnlocked(matching, current.job, "retryable", {
7451
+ leaseOwner: void 0,
7452
+ leaseExpiresAt: void 0,
7453
+ nextAttemptAt: this.clock() + retryDelayMs,
7454
+ lastError: formatStoredError(error)
7455
+ });
7456
+ });
7457
+ if (terminalJob !== null && !await this.finalizeTerminalJob(terminalJob)) {
7458
+ this.scheduleBackgroundRecovery();
7459
+ }
7460
+ if (retryDelayMs !== null) this.scheduleBackgroundRun(retryDelayMs);
7461
+ } catch (finishError) {
7462
+ log.warn(`dependency propagation attempt finalization failed: ${finishError}`);
7463
+ }
7464
+ }
7465
+ async propagateDirect(event) {
7466
+ try {
7467
+ const storage = await this.getStorage(event.namespaceScope);
7468
+ await propagateInvalidation(
7469
+ { storage, extraction: this.extraction, config: this.config },
7470
+ event
7471
+ );
7472
+ } catch (error) {
7473
+ log.warn(`dependency propagation direct fallback failed: ${error}`);
7474
+ }
5755
7475
  }
5756
7476
  };
5757
7477
 
7478
+ // src/orchestration/dependency-propagation-replay-reconciliation.ts
7479
+ function createDependencyPropagationReplayReconciliation(host) {
7480
+ return {
7481
+ reindex: async (event, survivorId) => {
7482
+ if (event.cause !== "consolidation_merge") return;
7483
+ const storage = await host.getStorage(event.namespaceScope);
7484
+ await host.indexPersistedMemory(storage, survivorId);
7485
+ },
7486
+ deindex: async (event, memoryIds) => {
7487
+ if (event.cause !== "consolidation_merge" && event.cause !== "consolidation_invalidate") {
7488
+ return;
7489
+ }
7490
+ await Promise.all(
7491
+ memoryIds.map((memoryId) => host.removeFromIndex(memoryId))
7492
+ );
7493
+ if (!resolveIndexingCapabilities(host.config).queryAwareIndexing) return;
7494
+ if (!event.oldMemory.path || !event.oldMemory.frontmatter.created) return;
7495
+ const storage = await host.getStorage(event.namespaceScope);
7496
+ await deindexMemoriesBatchAsync(storage.dir, [{
7497
+ path: event.oldMemory.path,
7498
+ createdAt: event.oldMemory.frontmatter.created,
7499
+ tags: event.oldMemory.frontmatter.tags ?? []
7500
+ }]);
7501
+ }
7502
+ };
7503
+ }
7504
+
5758
7505
  // src/orchestration/recall-internal.ts
5759
- import { createHash as createHash3 } from "crypto";
5760
- import { readFile as readFile2, stat, unlink } from "fs/promises";
5761
- import path9 from "path";
7506
+ import { createHash as createHash5 } from "crypto";
7507
+ import { readFile as readFile3, stat, unlink as unlink2 } from "fs/promises";
7508
+ import path10 from "path";
5762
7509
 
5763
7510
  // src/orchestration/graph-path-state-loader.ts
5764
7511
  import { lstat, opendir, realpath } from "fs/promises";
5765
- import path6 from "path";
7512
+ import path7 from "path";
5766
7513
  function isArchivePathIndexUnavailable(index) {
5767
7514
  return "unavailable" in index;
5768
7515
  }
@@ -5781,11 +7528,11 @@ var GraphPathStateLoader = class {
5781
7528
  openArchiveDirectory = opendir;
5782
7529
  async readNode(storage, nodeId, deadlineAtMs, allowArchiveLookup) {
5783
7530
  if (typeof deadlineAtMs === "number" && Date.now() >= deadlineAtMs) return null;
5784
- const configuredStorageRoot = path6.resolve(storage.dir);
7531
+ const configuredStorageRoot = path7.resolve(storage.dir);
5785
7532
  const storageRoot = await realpath(configuredStorageRoot).catch(() => null);
5786
7533
  if (!storageRoot || storageRoot !== configuredStorageRoot) return null;
5787
- const logicalId = path6.basename(nodeId, path6.extname(nodeId));
5788
- const directRelativePaths = [nodeId, path6.join("cold", nodeId)];
7534
+ const logicalId = path7.basename(nodeId, path7.extname(nodeId));
7535
+ const directRelativePaths = [nodeId, path7.join("cold", nodeId)];
5789
7536
  let inactiveDirectMemory = null;
5790
7537
  for (const relativePath of directRelativePaths) {
5791
7538
  const safePath = await this.resolveContainedPath(storageRoot, relativePath);
@@ -5808,7 +7555,7 @@ var GraphPathStateLoader = class {
5808
7555
  if (!archiveIndex || isArchivePathIndexUnavailable(archiveIndex) || typeof deadlineAtMs === "number" && Date.now() >= deadlineAtMs) {
5809
7556
  return null;
5810
7557
  }
5811
- const archivePaths = archiveIndex.pathsByBasename.get(path6.basename(nodeId)) ?? [];
7558
+ const archivePaths = archiveIndex.pathsByBasename.get(path7.basename(nodeId)) ?? [];
5812
7559
  for (const archivePath of archivePaths) {
5813
7560
  if (typeof deadlineAtMs === "number" && Date.now() >= deadlineAtMs) return null;
5814
7561
  const memory = await storage.readMemoryByPath(archivePath);
@@ -5818,17 +7565,17 @@ var GraphPathStateLoader = class {
5818
7565
  return null;
5819
7566
  }
5820
7567
  async resolveContainedPath(storageRoot, relativePath) {
5821
- if (path6.isAbsolute(relativePath)) return null;
7568
+ if (path7.isAbsolute(relativePath)) return null;
5822
7569
  const rawSegments = relativePath.replace(/\\/g, "/").split("/");
5823
7570
  if (rawSegments.includes("..")) return null;
5824
- const candidate = path6.resolve(storageRoot, relativePath);
5825
- const lexicalRelative = path6.relative(storageRoot, candidate);
5826
- if (lexicalRelative.length > 0 && (lexicalRelative.startsWith("..") || path6.isAbsolute(lexicalRelative))) {
7571
+ const candidate = path7.resolve(storageRoot, relativePath);
7572
+ const lexicalRelative = path7.relative(storageRoot, candidate);
7573
+ if (lexicalRelative.length > 0 && (lexicalRelative.startsWith("..") || path7.isAbsolute(lexicalRelative))) {
5827
7574
  return null;
5828
7575
  }
5829
7576
  const canonical = await realpath(candidate).catch(() => candidate);
5830
- const canonicalRelative = path6.relative(storageRoot, canonical);
5831
- if (canonicalRelative.length > 0 && (canonicalRelative.startsWith("..") || path6.isAbsolute(canonicalRelative))) {
7577
+ const canonicalRelative = path7.relative(storageRoot, canonical);
7578
+ if (canonicalRelative.length > 0 && (canonicalRelative.startsWith("..") || path7.isAbsolute(canonicalRelative))) {
5832
7579
  return null;
5833
7580
  }
5834
7581
  return canonical;
@@ -5901,7 +7648,7 @@ var GraphPathStateLoader = class {
5901
7648
  }
5902
7649
  async buildArchivePathIndex(storageRoot, version) {
5903
7650
  const pathsByBasename = /* @__PURE__ */ new Map();
5904
- const archiveRoot = path6.join(storageRoot, "archive");
7651
+ const archiveRoot = path7.join(storageRoot, "archive");
5905
7652
  let archiveRootStatus;
5906
7653
  try {
5907
7654
  archiveRootStatus = await this.archiveLstat(archiveRoot);
@@ -5919,8 +7666,8 @@ var GraphPathStateLoader = class {
5919
7666
  if (isArchiveScanAbsenceError(error)) return { version, pathsByBasename };
5920
7667
  return null;
5921
7668
  }
5922
- const archiveRelative = path6.relative(storageRoot, canonicalArchiveRoot);
5923
- if (archiveRelative.length > 0 && (archiveRelative.startsWith("..") || path6.isAbsolute(archiveRelative))) {
7669
+ const archiveRelative = path7.relative(storageRoot, canonicalArchiveRoot);
7670
+ if (archiveRelative.length > 0 && (archiveRelative.startsWith("..") || path7.isAbsolute(archiveRelative))) {
5924
7671
  return { version, pathsByBasename };
5925
7672
  }
5926
7673
  const pending = [canonicalArchiveRoot];
@@ -5946,8 +7693,8 @@ var GraphPathStateLoader = class {
5946
7693
  if (isArchiveScanAbsenceError(error)) continue;
5947
7694
  return null;
5948
7695
  }
5949
- const currentRelative = path6.relative(storageRoot, canonicalCurrent);
5950
- if (currentRelative.length > 0 && (currentRelative.startsWith("..") || path6.isAbsolute(currentRelative))) {
7696
+ const currentRelative = path7.relative(storageRoot, canonicalCurrent);
7697
+ if (currentRelative.length > 0 && (currentRelative.startsWith("..") || path7.isAbsolute(currentRelative))) {
5951
7698
  continue;
5952
7699
  }
5953
7700
  let directory;
@@ -5964,7 +7711,7 @@ var GraphPathStateLoader = class {
5964
7711
  return { version, unavailable: "oversized" };
5965
7712
  }
5966
7713
  if (entry.isSymbolicLink()) continue;
5967
- const entryPath = path6.join(canonicalCurrent, entry.name);
7714
+ const entryPath = path7.join(canonicalCurrent, entry.name);
5968
7715
  if (entry.isDirectory()) {
5969
7716
  pending.push(entryPath);
5970
7717
  continue;
@@ -5980,8 +7727,8 @@ var GraphPathStateLoader = class {
5980
7727
  if (isArchiveScanAbsenceError(error)) continue;
5981
7728
  return null;
5982
7729
  }
5983
- const relative = path6.relative(storageRoot, canonical);
5984
- if (relative.length > 0 && (relative.startsWith("..") || path6.isAbsolute(relative))) {
7730
+ const relative = path7.relative(storageRoot, canonical);
7731
+ if (relative.length > 0 && (relative.startsWith("..") || path7.isAbsolute(relative))) {
5985
7732
  continue;
5986
7733
  }
5987
7734
  const paths = pathsByBasename.get(entry.name);
@@ -6005,9 +7752,9 @@ var GraphPathStateLoader = class {
6005
7752
  };
6006
7753
 
6007
7754
  // src/orchestration/graph-recall-coordinator.ts
6008
- import { createHash as createHash2 } from "crypto";
7755
+ import { createHash as createHash4 } from "crypto";
6009
7756
  import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
6010
- import path7 from "path";
7757
+ import path8 from "path";
6011
7758
  function mergeGraphExpandedResults(primary, expanded) {
6012
7759
  const mergedByNamespaceAndPath = /* @__PURE__ */ new Map();
6013
7760
  for (const item of [...primary, ...expanded]) {
@@ -6024,11 +7771,11 @@ function mergeGraphExpandedResults(primary, expanded) {
6024
7771
  return Array.from(mergedByNamespaceAndPath.values());
6025
7772
  }
6026
7773
  function graphPathRelativeToStorage(storageDir, candidatePath) {
6027
- const absolutePath = path7.isAbsolute(candidatePath) ? candidatePath : path7.resolve(storageDir, candidatePath);
6028
- const rel = path7.relative(storageDir, absolutePath);
7774
+ const absolutePath = path8.isAbsolute(candidatePath) ? candidatePath : path8.resolve(storageDir, candidatePath);
7775
+ const rel = path8.relative(storageDir, absolutePath);
6029
7776
  if (!rel || rel === ".") return null;
6030
7777
  if (rel.startsWith("..")) return null;
6031
- return rel.split(path7.sep).join("/");
7778
+ return rel.split(path8.sep).join("/");
6032
7779
  }
6033
7780
  function normalizeGraphActivationScore(score) {
6034
7781
  const bounded = Number.isFinite(score) && score > 0 ? score : 0;
@@ -6128,7 +7875,7 @@ var GraphRecallCoordinator = class {
6128
7875
  resolvedPath = resolvedCold.result.path;
6129
7876
  resolvedResult = resolvedCold.result;
6130
7877
  }
6131
- if (!path7.isAbsolute(resolvedPath)) {
7878
+ if (!path8.isAbsolute(resolvedPath)) {
6132
7879
  resolvedAmbiguousSeeds.set(result.path, null);
6133
7880
  return null;
6134
7881
  }
@@ -6153,7 +7900,7 @@ var GraphRecallCoordinator = class {
6153
7900
  }
6154
7901
  continue;
6155
7902
  }
6156
- if (path7.isAbsolute(result.path)) {
7903
+ if (path8.isAbsolute(result.path)) {
6157
7904
  const resolved = await resolveAmbiguousSeedOwner(result, null);
6158
7905
  if (resolved) {
6159
7906
  addResultForNamespace(resolved.namespace, resolved.result);
@@ -6199,7 +7946,7 @@ var GraphRecallCoordinator = class {
6199
7946
  0
6200
7947
  );
6201
7948
  seedPaths.push(
6202
- ...seedRelativePaths.map((rel) => path7.join(storage.dir, rel))
7949
+ ...seedRelativePaths.map((rel) => path8.join(storage.dir, rel))
6203
7950
  );
6204
7951
  const seedSet = new Set(seedRelativePaths);
6205
7952
  const nodeMemoryCache = /* @__PURE__ */ new Map();
@@ -6253,7 +8000,7 @@ var GraphRecallCoordinator = class {
6253
8000
  path: memory.path,
6254
8001
  score,
6255
8002
  namespace,
6256
- seed: path7.resolve(storage.dir, candidate.seed),
8003
+ seed: path8.resolve(storage.dir, candidate.seed),
6257
8004
  hopDepth: candidate.hopDepth,
6258
8005
  decayedWeight: candidate.decayedWeight,
6259
8006
  graphType: candidate.graphType,
@@ -6285,7 +8032,7 @@ var GraphRecallCoordinator = class {
6285
8032
  add(memory.frontmatter.id);
6286
8033
  if (relativePath) {
6287
8034
  add(relativePath);
6288
- if (relativePath.endsWith(".md")) add(path7.basename(relativePath, ".md"));
8035
+ if (relativePath.endsWith(".md")) add(path8.basename(relativePath, ".md"));
6289
8036
  }
6290
8037
  return state;
6291
8038
  };
@@ -6332,7 +8079,7 @@ var GraphRecallCoordinator = class {
6332
8079
  path: memory.path,
6333
8080
  score,
6334
8081
  namespace,
6335
- seed: path7.resolve(storage.dir, candidate.seed),
8082
+ seed: path8.resolve(storage.dir, candidate.seed),
6336
8083
  hopDepth: candidate.hopDepth,
6337
8084
  decayedWeight: candidate.decayedWeight,
6338
8085
  graphType: candidate.graphType,
@@ -6374,12 +8121,12 @@ var GraphRecallCoordinator = class {
6374
8121
  }
6375
8122
  async recordLastGraphRecallSnapshot(options) {
6376
8123
  try {
6377
- const snapshotPath = path7.join(
8124
+ const snapshotPath = path8.join(
6378
8125
  options.storage.dir,
6379
8126
  "state",
6380
8127
  "last_graph_recall.json"
6381
8128
  );
6382
- await mkdir3(path7.dirname(snapshotPath), { recursive: true });
8129
+ await mkdir3(path8.dirname(snapshotPath), { recursive: true });
6383
8130
  const now = (/* @__PURE__ */ new Date()).toISOString();
6384
8131
  const totalSeedCount = options.seedPaths.length;
6385
8132
  const totalExpandedCount = options.expandedPaths.length;
@@ -6391,7 +8138,7 @@ var GraphRecallCoordinator = class {
6391
8138
  const payload = {
6392
8139
  recordedAt: now,
6393
8140
  mode: options.recallMode,
6394
- queryHash: createHash2("sha256").update(options.prompt).digest("hex"),
8141
+ queryHash: createHash4("sha256").update(options.prompt).digest("hex"),
6395
8142
  queryLength: options.prompt.length,
6396
8143
  namespaces: options.recallNamespaces,
6397
8144
  seedCount: totalSeedCount,
@@ -6494,11 +8241,11 @@ ${blocks.join("\n\n")}`;
6494
8241
  }
6495
8242
 
6496
8243
  // src/orchestration/recall-profile-storage.ts
6497
- import path8 from "path";
8244
+ import path9 from "path";
6498
8245
  function resolveCompositeProfileStorage(options) {
6499
8246
  const { profileStorages, memoryDir } = options;
6500
8247
  const emptyProfileStorage = new Proxy(
6501
- { dir: path8.join(memoryDir, ".empty-scope-profile") },
8248
+ { dir: path9.join(memoryDir, ".empty-scope-profile") },
6502
8249
  {
6503
8250
  get(target, prop) {
6504
8251
  if (prop in target) return Reflect.get(target, prop);
@@ -6724,8 +8471,8 @@ var RecallInternalCoordinator = class {
6724
8471
  record: recordRecallSectionMetric,
6725
8472
  logger: log
6726
8473
  });
6727
- const promptHash = createHash3("sha256").update(prompt).digest("hex");
6728
- const traceId = createHash3("sha256").update(`${sessionKey ?? "default"}:${recallStart}:${promptHash}`).digest("hex").slice(0, 16);
8474
+ const promptHash = createHash5("sha256").update(prompt).digest("hex");
8475
+ const traceId = createHash5("sha256").update(`${sessionKey ?? "default"}:${recallStart}:${promptHash}`).digest("hex").slice(0, 16);
6729
8476
  const sectionBuckets = /* @__PURE__ */ new Map();
6730
8477
  const queryPolicy = buildRecallQueryPolicy(prompt, sessionKey, {
6731
8478
  cronRecallPolicyEnabled: resolveRecallAuxiliaryCapabilities(this.deps.config).cronRecallPolicy,
@@ -6734,7 +8481,7 @@ var RecallInternalCoordinator = class {
6734
8481
  cronConversationRecallMode: this.deps.config.cronConversationRecallMode
6735
8482
  });
6736
8483
  const retrievalQuery = queryPolicy.retrievalQuery || prompt;
6737
- const retrievalQueryHash = createHash3("sha256").update(retrievalQuery).digest("hex");
8484
+ const retrievalQueryHash = createHash5("sha256").update(retrievalQuery).digest("hex");
6738
8485
  const policyVersion = this.deps.currentPolicyVersion();
6739
8486
  let recallSource = "none";
6740
8487
  let recalledMemoryCount = 0;
@@ -6875,7 +8622,7 @@ var RecallInternalCoordinator = class {
6875
8622
  const graphExpandedResultPaths = /* @__PURE__ */ new Set();
6876
8623
  const graphSourceLabelsForPath = (resultPath) => {
6877
8624
  const labels = [];
6878
- const normalizedPath = resultPath.split(path9.sep).join("/");
8625
+ const normalizedPath = resultPath.split(path10.sep).join("/");
6879
8626
  const isEntityPath = normalizedPath.startsWith("entities/") || normalizedPath.includes("/entities/");
6880
8627
  if (graphBaselinePaths.has(resultPath)) labels.push("baseline");
6881
8628
  if (graphExpandedResultPaths.has(resultPath))
@@ -8392,16 +10139,16 @@ ${formatted}`;
8392
10139
  if (!resolveRecallAuxiliaryCapabilities(this.deps.config).compactionReset) return null;
8393
10140
  const workspaceDir = compactionWorkspaceDir || this.deps.config.workspaceDir || defaultWorkspaceDir();
8394
10141
  const safeSessionKey = sanitizeSessionKeyForFilename(effectiveSessionKey);
8395
- const signalPath = path9.join(
10142
+ const signalPath = path10.join(
8396
10143
  workspaceDir,
8397
10144
  `.compaction-reset-signal-${safeSessionKey}`
8398
10145
  );
8399
- const bootPath = path9.join(workspaceDir, "BOOT.md");
10146
+ const bootPath = path10.join(workspaceDir, "BOOT.md");
8400
10147
  try {
8401
10148
  const signalStat = await stat(signalPath).catch(() => null);
8402
10149
  if (!signalStat) return null;
8403
10150
  const signalAge = Date.now() - signalStat.mtimeMs;
8404
- const signalData = JSON.parse(await readFile2(signalPath, "utf-8"));
10151
+ const signalData = JSON.parse(await readFile3(signalPath, "utf-8"));
8405
10152
  if (signalData.sessionKey !== effectiveSessionKey) {
8406
10153
  log.debug(
8407
10154
  `recall: compaction signal is for ${signalData.sessionKey}, not ${effectiveSessionKey} \u2014 skipping`
@@ -8412,7 +10159,7 @@ ${formatted}`;
8412
10159
  log.debug(
8413
10160
  `recall: stale compaction signal (${Math.round(signalAge / 1e3)}s old), skipping`
8414
10161
  );
8415
- await unlink(signalPath).catch(() => {
10162
+ await unlink2(signalPath).catch(() => {
8416
10163
  });
8417
10164
  return null;
8418
10165
  }
@@ -8421,7 +10168,7 @@ ${formatted}`;
8421
10168
 
8422
10169
  `;
8423
10170
  try {
8424
- const bootContent = await readFile2(bootPath, "utf-8");
10171
+ const bootContent = await readFile3(bootPath, "utf-8");
8425
10172
  section += "### BOOT.md (working state before compaction)\n\n";
8426
10173
  section += bootContent + "\n";
8427
10174
  } catch {
@@ -8432,12 +10179,12 @@ ${formatted}`;
8432
10179
  log.info(
8433
10180
  `recall: injected compaction reset context for ${effectiveSessionKey}`
8434
10181
  );
8435
- await unlink(signalPath).catch(() => {
10182
+ await unlink2(signalPath).catch(() => {
8436
10183
  });
8437
10184
  return section;
8438
10185
  } catch (err) {
8439
10186
  log.debug("recall: compaction signal check failed:", err);
8440
- await unlink(signalPath).catch(() => {
10187
+ await unlink2(signalPath).catch(() => {
8441
10188
  });
8442
10189
  return null;
8443
10190
  }
@@ -10630,11 +12377,11 @@ ${tmtNode.summary}`
10630
12377
  };
10631
12378
 
10632
12379
  // src/orchestration/recall-search-pipeline.ts
10633
- import { createHash as createHash4 } from "crypto";
10634
- import path11 from "path";
12380
+ import { createHash as createHash6 } from "crypto";
12381
+ import path12 from "path";
10635
12382
 
10636
12383
  // src/orchestration/recall-search-prefilter.ts
10637
- import path10 from "path";
12384
+ import path11 from "path";
10638
12385
  async function fetchActiveArtifactsForNamespace(deps, namespace, prompt, targetCount, options = {}) {
10639
12386
  const storage = await deps.storageRouter.storageFor(namespace);
10640
12387
  let fetchLimit = computeArtifactCandidateFetchLimit(targetCount);
@@ -10763,7 +12510,7 @@ async function searchEmbeddingFallback(deps, query, limit) {
10763
12510
  if (hits.length === 0) return [];
10764
12511
  const results = [];
10765
12512
  for (const hit of hits) {
10766
- const fullPath = path10.isAbsolute(hit.path) ? hit.path : path10.join(deps.config.memoryDir, hit.path);
12513
+ const fullPath = path11.isAbsolute(hit.path) ? hit.path : path11.join(deps.config.memoryDir, hit.path);
10767
12514
  const memory = await deps.storage.readMemoryByPath(fullPath);
10768
12515
  if (!memory) continue;
10769
12516
  results.push({
@@ -10851,7 +12598,7 @@ var RecallSearchPipelineCoordinator = class {
10851
12598
  if (!options.onDebugSnapshot) return;
10852
12599
  await options.onDebugSnapshot({
10853
12600
  recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
10854
- queryHash: createHash4("sha256").update(prompt).digest("hex"),
12601
+ queryHash: createHash6("sha256").update(prompt).digest("hex"),
10855
12602
  queryLength: prompt.length,
10856
12603
  collection: options.collection,
10857
12604
  namespaces: options.recallNamespaces,
@@ -11131,7 +12878,7 @@ var RecallSearchPipelineCoordinator = class {
11131
12878
  const storage = await this.deps.storageRouter.storageFor(namespace);
11132
12879
  const storageDir = typeof storage.dir === "string" && storage.dir ? storage.dir : null;
11133
12880
  if (!storageDir) continue;
11134
- const recallRoot = path11.resolve(storageDir);
12881
+ const recallRoot = path12.resolve(storageDir);
11135
12882
  if (seenRecallRoots.has(recallRoot)) continue;
11136
12883
  seenRecallRoots.add(recallRoot);
11137
12884
  recallRoots.push(recallRoot);
@@ -11155,8 +12902,8 @@ var RecallSearchPipelineCoordinator = class {
11155
12902
  if (resolvedCold) scopedResults.push(resolvedCold.result);
11156
12903
  continue;
11157
12904
  }
11158
- if (path11.isAbsolute(result.path)) {
11159
- if (recallRoots.some((recallRoot) => isPathInsideStorageRoot(recallRoot, path11.resolve(result.path)))) {
12905
+ if (path12.isAbsolute(result.path)) {
12906
+ if (recallRoots.some((recallRoot) => isPathInsideStorageRoot(recallRoot, path12.resolve(result.path)))) {
11160
12907
  scopedResults.push(result);
11161
12908
  }
11162
12909
  continue;
@@ -11719,7 +13466,7 @@ var RecallSearchPipelineCoordinator = class {
11719
13466
  };
11720
13467
 
11721
13468
  // src/orchestration/turn-ingestion.ts
11722
- import { createHash as createHash5, randomBytes } from "crypto";
13469
+ import { createHash as createHash7, randomBytes } from "crypto";
11723
13470
  var TurnIngestionCoordinator = class {
11724
13471
  constructor(deps) {
11725
13472
  this.deps = deps;
@@ -11904,7 +13651,7 @@ var TurnIngestionCoordinator = class {
11904
13651
  const shouldUseStableBatchKey = turns.some(
11905
13652
  (turn) => turn.persistProcessedFingerprint === true || typeof turn.turnFingerprint === "string" && turn.turnFingerprint.length > 0
11906
13653
  );
11907
- const stableBatchFingerprint = shouldUseStableBatchKey ? createHash5("sha256").update(
13654
+ const stableBatchFingerprint = shouldUseStableBatchKey ? createHash7("sha256").update(
11908
13655
  turns.map(
11909
13656
  (turn) => [
11910
13657
  turn.role,
@@ -12142,7 +13889,7 @@ var TurnIngestionCoordinator = class {
12142
13889
  };
12143
13890
  scheduleQueueWaitTimeout();
12144
13891
  }
12145
- this.deps.extractionQueueCoordinator.enqueue(async () => {
13892
+ const accepted = this.deps.extractionQueueCoordinator.enqueue(async () => {
12146
13893
  if (settled) return;
12147
13894
  if (typeof extractionDeadlineMs === "number" && extractionDeadlineMs <= Date.now()) {
12148
13895
  settleTask(new ExtractionDeadlineError("queue_wait"));
@@ -12172,6 +13919,10 @@ var TurnIngestionCoordinator = class {
12172
13919
  }
12173
13920
  }
12174
13921
  });
13922
+ if (!accepted) {
13923
+ settleTask(new Error("extraction queue is stopped"));
13924
+ return;
13925
+ }
12175
13926
  log.debug(`queued extraction from ${reason}`);
12176
13927
  }
12177
13928
  /**
@@ -12245,8 +13996,8 @@ var TurnIngestionCoordinator = class {
12245
13996
  };
12246
13997
 
12247
13998
  // src/orchestration/recall-introspection.ts
12248
- import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
12249
- import path13 from "path";
13999
+ import { mkdir as mkdir5, readFile as readFile5, writeFile as writeFile5 } from "fs/promises";
14000
+ import path14 from "path";
12250
14001
 
12251
14002
  // src/taxonomy/default-taxonomy.ts
12252
14003
  var DEFAULT_TAXONOMY = {
@@ -12368,8 +14119,8 @@ function generateResolverDocument(taxonomy) {
12368
14119
  }
12369
14120
 
12370
14121
  // src/taxonomy/taxonomy-loader.ts
12371
- import { readFile as readFile3, mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
12372
- import path12 from "path";
14122
+ import { readFile as readFile4, mkdir as mkdir4, writeFile as writeFile4 } from "fs/promises";
14123
+ import path13 from "path";
12373
14124
  var TAXONOMY_DIR = ".taxonomy";
12374
14125
  var TAXONOMY_FILE = "taxonomy.json";
12375
14126
  var MAX_SLUG_LENGTH = 32;
@@ -12467,10 +14218,10 @@ function validateTaxonomy(taxonomy) {
12467
14218
  }
12468
14219
  }
12469
14220
  async function loadTaxonomy(memoryDir) {
12470
- const taxonomyPath = path12.join(memoryDir, TAXONOMY_DIR, TAXONOMY_FILE);
14221
+ const taxonomyPath = path13.join(memoryDir, TAXONOMY_DIR, TAXONOMY_FILE);
12471
14222
  let raw;
12472
14223
  try {
12473
- raw = await readFile3(taxonomyPath, "utf-8");
14224
+ raw = await readFile4(taxonomyPath, "utf-8");
12474
14225
  } catch (err) {
12475
14226
  if (err instanceof Error && err.code === "ENOENT") {
12476
14227
  return structuredClone(DEFAULT_TAXONOMY);
@@ -12517,16 +14268,16 @@ async function loadTaxonomy(memoryDir) {
12517
14268
  }
12518
14269
  async function saveTaxonomy(memoryDir, taxonomy) {
12519
14270
  validateTaxonomy(taxonomy);
12520
- const dir = path12.join(memoryDir, TAXONOMY_DIR);
14271
+ const dir = path13.join(memoryDir, TAXONOMY_DIR);
12521
14272
  await mkdir4(dir, { recursive: true });
12522
- const filePath = path12.join(dir, TAXONOMY_FILE);
14273
+ const filePath = path13.join(dir, TAXONOMY_FILE);
12523
14274
  await writeFile4(filePath, JSON.stringify(taxonomy, null, 2) + "\n", "utf-8");
12524
14275
  }
12525
14276
  function getTaxonomyDir(memoryDir) {
12526
- return path12.join(memoryDir, TAXONOMY_DIR);
14277
+ return path13.join(memoryDir, TAXONOMY_DIR);
12527
14278
  }
12528
14279
  function getTaxonomyFilePath(memoryDir) {
12529
- return path12.join(memoryDir, TAXONOMY_DIR, TAXONOMY_FILE);
14280
+ return path13.join(memoryDir, TAXONOMY_DIR, TAXONOMY_FILE);
12530
14281
  }
12531
14282
 
12532
14283
  // src/orchestration/recall-introspection.ts
@@ -12550,13 +14301,13 @@ var RecallIntrospectionCoordinator = class {
12550
14301
  }
12551
14302
  async getLastGraphRecallSnapshot(namespace) {
12552
14303
  const storage = await this.deps.getStorage(namespace);
12553
- const snapshotPath = path13.join(
14304
+ const snapshotPath = path14.join(
12554
14305
  storage.dir,
12555
14306
  "state",
12556
14307
  "last_graph_recall.json"
12557
14308
  );
12558
14309
  try {
12559
- const raw = await readFile4(snapshotPath, "utf-8");
14310
+ const raw = await readFile5(snapshotPath, "utf-8");
12560
14311
  const parsed = JSON.parse(raw);
12561
14312
  if (!parsed || typeof parsed !== "object") return null;
12562
14313
  return {
@@ -12589,9 +14340,9 @@ var RecallIntrospectionCoordinator = class {
12589
14340
  }
12590
14341
  async getLastIntentSnapshot(namespace) {
12591
14342
  const storage = await this.deps.getStorage(namespace);
12592
- const snapshotPath = path13.join(storage.dir, "state", "last_intent.json");
14343
+ const snapshotPath = path14.join(storage.dir, "state", "last_intent.json");
12593
14344
  try {
12594
- const raw = await readFile4(snapshotPath, "utf-8");
14345
+ const raw = await readFile5(snapshotPath, "utf-8");
12595
14346
  const parsed = JSON.parse(raw);
12596
14347
  if (!parsed || typeof parsed !== "object") return null;
12597
14348
  const graphDecision = parsed.graphDecision && typeof parsed.graphDecision === "object" ? parsed.graphDecision : void 0;
@@ -12622,13 +14373,13 @@ var RecallIntrospectionCoordinator = class {
12622
14373
  }
12623
14374
  async getLastQmdRecallSnapshot(namespace) {
12624
14375
  const storage = await this.deps.getStorage(namespace);
12625
- const snapshotPath = path13.join(
14376
+ const snapshotPath = path14.join(
12626
14377
  storage.dir,
12627
14378
  "state",
12628
14379
  "last_qmd_recall.json"
12629
14380
  );
12630
14381
  try {
12631
- const raw = await readFile4(snapshotPath, "utf-8");
14382
+ const raw = await readFile5(snapshotPath, "utf-8");
12632
14383
  const parsed = JSON.parse(raw);
12633
14384
  if (!parsed || typeof parsed !== "object") return null;
12634
14385
  return {
@@ -12914,12 +14665,12 @@ var RecallIntrospectionCoordinator = class {
12914
14665
  }
12915
14666
  async recordLastIntentSnapshot(options) {
12916
14667
  try {
12917
- const snapshotPath = path13.join(
14668
+ const snapshotPath = path14.join(
12918
14669
  options.storage.dir,
12919
14670
  "state",
12920
14671
  "last_intent.json"
12921
14672
  );
12922
- await mkdir5(path13.dirname(snapshotPath), { recursive: true });
14673
+ await mkdir5(path14.dirname(snapshotPath), { recursive: true });
12923
14674
  await writeFile5(
12924
14675
  snapshotPath,
12925
14676
  JSON.stringify(options.snapshot, null, 2),
@@ -12931,12 +14682,12 @@ var RecallIntrospectionCoordinator = class {
12931
14682
  }
12932
14683
  async recordLastQmdRecallSnapshot(options) {
12933
14684
  try {
12934
- const snapshotPath = path13.join(
14685
+ const snapshotPath = path14.join(
12935
14686
  options.storage.dir,
12936
14687
  "state",
12937
14688
  "last_qmd_recall.json"
12938
14689
  );
12939
- await mkdir5(path13.dirname(snapshotPath), { recursive: true });
14690
+ await mkdir5(path14.dirname(snapshotPath), { recursive: true });
12940
14691
  await writeFile5(
12941
14692
  snapshotPath,
12942
14693
  JSON.stringify(options.snapshot, null, 2),
@@ -12951,8 +14702,8 @@ var RecallIntrospectionCoordinator = class {
12951
14702
  const stateDir = await this.deps.resolveStateDirForNamespace(
12952
14703
  options.namespace
12953
14704
  );
12954
- const snapshotPath = path13.join(stateDir, "last_intent.json");
12955
- await mkdir5(path13.dirname(snapshotPath), { recursive: true });
14705
+ const snapshotPath = path14.join(stateDir, "last_intent.json");
14706
+ await mkdir5(path14.dirname(snapshotPath), { recursive: true });
12956
14707
  await writeFile5(
12957
14708
  snapshotPath,
12958
14709
  JSON.stringify(options.snapshot, null, 2),
@@ -12965,8 +14716,8 @@ var RecallIntrospectionCoordinator = class {
12965
14716
  };
12966
14717
 
12967
14718
  // src/orchestration/orchestrator-init.ts
12968
- import { readdir as readdir2, stat as stat2, unlink as unlink2 } from "fs/promises";
12969
- import path14 from "path";
14719
+ import { readdir as readdir2, stat as stat2, unlink as unlink3 } from "fs/promises";
14720
+ import path15 from "path";
12970
14721
  var OrchestratorInitCoordinator = class {
12971
14722
  constructor(deps) {
12972
14723
  this.deps = deps;
@@ -13070,10 +14821,10 @@ var OrchestratorInitCoordinator = class {
13070
14821
  const files = await readdir2(wsDir).catch(() => []);
13071
14822
  for (const f of files) {
13072
14823
  if (!f.startsWith(".compaction-reset-signal-")) continue;
13073
- const fp = path14.join(wsDir, f);
14824
+ const fp = path15.join(wsDir, f);
13074
14825
  const s = await stat2(fp).catch(() => null);
13075
14826
  if (s && Date.now() - s.mtimeMs >= COMPACTION_SIGNAL_MAX_AGE_MS) {
13076
- await unlink2(fp).catch(() => {
14827
+ await unlink3(fp).catch(() => {
13077
14828
  });
13078
14829
  log.debug(`initialize: removed stale compaction signal ${f}`);
13079
14830
  }
@@ -13422,7 +15173,7 @@ var OrchestratorInitCoordinator = class {
13422
15173
  };
13423
15174
 
13424
15175
  // src/orchestration/persistence-index.ts
13425
- import path15 from "path";
15176
+ import path16 from "path";
13426
15177
  var PersistenceIndexCoordinator = class {
13427
15178
  constructor(deps) {
13428
15179
  this.deps = deps;
@@ -13608,7 +15359,7 @@ var PersistenceIndexCoordinator = class {
13608
15359
  const allMems = allMemsForGraph ?? [];
13609
15360
  for (const m of allMems) {
13610
15361
  if (m.frontmatter.entityRef === entityRef) {
13611
- const rel = path15.relative(storage.dir, m.path);
15362
+ const rel = path16.relative(storage.dir, m.path);
13612
15363
  if (rel !== memoryRelPath) entitySiblings.push(rel);
13613
15364
  }
13614
15365
  }
@@ -13756,8 +15507,8 @@ var PersistenceIndexCoordinator = class {
13756
15507
  };
13757
15508
 
13758
15509
  // src/orchestration/workspace-ops.ts
13759
- import { lstat as lstat2, mkdir as mkdir6, readFile as readFile5, readdir as readdir3, realpath as realpath2, writeFile as writeFile6 } from "fs/promises";
13760
- import path16 from "path";
15510
+ import { lstat as lstat2, mkdir as mkdir6, readFile as readFile6, readdir as readdir3, realpath as realpath2, writeFile as writeFile6 } from "fs/promises";
15511
+ import path17 from "path";
13761
15512
 
13762
15513
  // src/maintenance/pattern-reinforcement.ts
13763
15514
  function patternReinforcementKey(content) {
@@ -13797,22 +15548,22 @@ function memoryInstant(memory) {
13797
15548
  const parsed = Date.parse(stamp);
13798
15549
  return Number.isFinite(parsed) ? parsed : null;
13799
15550
  }
13800
- var ACTIVE_STATUS = "active";
15551
+ var ACTIVE_STATUS2 = "active";
13801
15552
  async function runPatternReinforcement(storage, options) {
13802
15553
  const minCount = Math.max(2, Math.floor(options.minCount));
13803
15554
  const targetCategories = new Set(options.categories);
13804
15555
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
13805
15556
  const nowIso = now().toISOString();
13806
15557
  if (targetCategories.size === 0) {
13807
- return emptyResult();
15558
+ return emptyResult2();
13808
15559
  }
13809
15560
  const memories = await storage.readAllMemories();
13810
15561
  const eligible = memories.filter((m) => {
13811
15562
  if (!targetCategories.has(m.frontmatter.category)) return false;
13812
- const status = m.frontmatter.status ?? ACTIVE_STATUS;
13813
- return status === ACTIVE_STATUS || status === "superseded";
15563
+ const status = m.frontmatter.status ?? ACTIVE_STATUS2;
15564
+ return status === ACTIVE_STATUS2 || status === "superseded";
13814
15565
  });
13815
- if (eligible.length === 0) return emptyResult();
15566
+ if (eligible.length === 0) return emptyResult2();
13816
15567
  const clusters = clusterByKey(
13817
15568
  eligible,
13818
15569
  (m) => patternReinforcementClusterKey(m.frontmatter.category, m.content)
@@ -13826,8 +15577,8 @@ async function runPatternReinforcement(storage, options) {
13826
15577
  for (const cluster of clusters.values()) {
13827
15578
  if (cluster.length < minCount) continue;
13828
15579
  const activeMembers = cluster.filter((m) => {
13829
- const status = m.frontmatter.status ?? ACTIVE_STATUS;
13830
- return status === ACTIVE_STATUS;
15580
+ const status = m.frontmatter.status ?? ACTIVE_STATUS2;
15581
+ return status === ACTIVE_STATUS2;
13831
15582
  });
13832
15583
  if (activeMembers.length === 0) continue;
13833
15584
  result.clustersFound += 1;
@@ -13898,7 +15649,7 @@ async function runPatternReinforcement(storage, options) {
13898
15649
  }
13899
15650
  return result;
13900
15651
  }
13901
- function emptyResult() {
15652
+ function emptyResult2() {
13902
15653
  return {
13903
15654
  clustersFound: 0,
13904
15655
  canonicalsUpdated: 0,
@@ -15664,8 +17415,8 @@ var MeetingsService = class {
15664
17415
  // src/orchestration/workspace-ops.ts
15665
17416
  var MEETINGS_BUILD_DEBOUNCE_MS = 5e3;
15666
17417
  function matchesMemoryPath(candidatePath, requestedPath, memoryDir) {
15667
- const normalizedCandidate = path16.resolve(candidatePath);
15668
- return normalizedCandidate === path16.resolve(requestedPath) || normalizedCandidate === path16.resolve(memoryDir, requestedPath);
17418
+ const normalizedCandidate = path17.resolve(candidatePath);
17419
+ return normalizedCandidate === path17.resolve(requestedPath) || normalizedCandidate === path17.resolve(memoryDir, requestedPath);
15669
17420
  }
15670
17421
  function accessTrackingPathCandidates(storageDir, memoryPath) {
15671
17422
  const candidates = qmdResultPathCandidates(storageDir, memoryPath);
@@ -15673,12 +17424,12 @@ function accessTrackingPathCandidates(storageDir, memoryPath) {
15673
17424
  if (!parts) return candidates;
15674
17425
  candidates.push(
15675
17426
  ...qmdResultPathCandidates(storageDir, parts.relativePath),
15676
- ...qmdResultPathCandidates(path16.join(storageDir, "cold"), parts.relativePath)
17427
+ ...qmdResultPathCandidates(path17.join(storageDir, "cold"), parts.relativePath)
15677
17428
  );
15678
17429
  return [...new Set(candidates)];
15679
17430
  }
15680
17431
  function canonicalMemoryPath(memoryPath, memoryDir) {
15681
- return path16.resolve(path16.isAbsolute(memoryPath) ? memoryPath : path16.resolve(memoryDir, memoryPath));
17432
+ return path17.resolve(path17.isAbsolute(memoryPath) ? memoryPath : path17.resolve(memoryDir, memoryPath));
15682
17433
  }
15683
17434
  var WorkspaceOpsCoordinator = class {
15684
17435
  constructor(deps) {
@@ -15760,15 +17511,15 @@ var WorkspaceOpsCoordinator = class {
15760
17511
  this.deps.lastFileHygieneRunAtMs = now;
15761
17512
  if (hygiene.rotateEnabled) {
15762
17513
  for (const rel of hygiene.rotatePaths) {
15763
- const abs = path16.isAbsolute(rel) ? rel : path16.join(this.deps.config.workspaceDir, rel);
17514
+ const abs = path17.isAbsolute(rel) ? rel : path17.join(this.deps.config.workspaceDir, rel);
15764
17515
  try {
15765
- const raw = await readFile5(abs, "utf-8");
17516
+ const raw = await readFile6(abs, "utf-8");
15766
17517
  if (raw.length > hygiene.rotateMaxBytes) {
15767
- const archiveDir = path16.join(
17518
+ const archiveDir = path17.join(
15768
17519
  this.deps.config.workspaceDir,
15769
17520
  hygiene.archiveDir
15770
17521
  );
15771
- const base = path16.basename(abs);
17522
+ const base = path17.basename(abs);
15772
17523
  const prefix = base.toUpperCase().replace(/\.MD$/i, "").replace(/[^A-Z0-9]+/g, "-") || "FILE";
15773
17524
  const { newContent } = await rotateMarkdownFileToArchive({
15774
17525
  filePath: abs,
@@ -15793,8 +17544,8 @@ var WorkspaceOpsCoordinator = class {
15793
17544
  log.warn(w.message);
15794
17545
  }
15795
17546
  if (hygiene.warningsLogEnabled && warnings.length > 0) {
15796
- const fp = path16.join(this.deps.config.memoryDir, hygiene.warningsLogPath);
15797
- await mkdir6(path16.dirname(fp), { recursive: true });
17547
+ const fp = path17.join(this.deps.config.memoryDir, hygiene.warningsLogPath);
17548
+ await mkdir6(path17.dirname(fp), { recursive: true });
15798
17549
  const stamp = (/* @__PURE__ */ new Date()).toISOString();
15799
17550
  const block = `
15800
17551
 
@@ -15803,7 +17554,7 @@ var WorkspaceOpsCoordinator = class {
15803
17554
  ` + warnings.map((w) => `- ${w.message}`).join("\n") + "\n";
15804
17555
  let existing = "";
15805
17556
  try {
15806
- existing = await readFile5(fp, "utf-8");
17557
+ existing = await readFile6(fp, "utf-8");
15807
17558
  } catch {
15808
17559
  existing = "# Engram File Hygiene Warnings\n";
15809
17560
  }
@@ -15834,7 +17585,7 @@ var WorkspaceOpsCoordinator = class {
15834
17585
  for (const categoryDir of RECALL_FALLBACK_DIRS) {
15835
17586
  if (memoryRootReal === null) break;
15836
17587
  for (const date of datesToScan) {
15837
- const dateDir = path16.join(storage.dir, categoryDir, date);
17588
+ const dateDir = path17.join(storage.dir, categoryDir, date);
15838
17589
  try {
15839
17590
  const dirStat = await lstat2(dateDir);
15840
17591
  if (dirStat.isSymbolicLink() || !dirStat.isDirectory()) continue;
@@ -15843,10 +17594,10 @@ var WorkspaceOpsCoordinator = class {
15843
17594
  for (const entry of entries) {
15844
17595
  if (entry.isSymbolicLink()) continue;
15845
17596
  if (!entry.name.endsWith(".md")) continue;
15846
- const fullPath = path16.join(dateDir, entry.name);
17597
+ const fullPath = path17.join(dateDir, entry.name);
15847
17598
  try {
15848
17599
  assertPathInsideRoot(memoryRootReal, await realpath2(fullPath), fullPath);
15849
- const raw = await readFile5(fullPath, "utf-8");
17600
+ const raw = await readFile6(fullPath, "utf-8");
15850
17601
  const fmMatch = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
15851
17602
  if (!fmMatch) continue;
15852
17603
  const fmBlock = fmMatch[1];
@@ -15865,7 +17616,7 @@ var WorkspaceOpsCoordinator = class {
15865
17616
  facts.push({
15866
17617
  path: fullPath,
15867
17618
  frontmatter: {
15868
- id: fm.id || path16.basename(entry.name, ".md"),
17619
+ id: fm.id || path17.basename(entry.name, ".md"),
15869
17620
  category: fm.category || "fact",
15870
17621
  created,
15871
17622
  updated: fm.updated || created,
@@ -15888,15 +17639,15 @@ var WorkspaceOpsCoordinator = class {
15888
17639
  return a.frontmatter.created < b.frontmatter.created ? -1 : 1;
15889
17640
  });
15890
17641
  const hourlySummaries = [];
15891
- const hourlyBaseDir = path16.join(storage.dir, "summaries", "hourly");
17642
+ const hourlyBaseDir = path17.join(storage.dir, "summaries", "hourly");
15892
17643
  try {
15893
17644
  const sessionKeys = await readdir3(hourlyBaseDir, { withFileTypes: true });
15894
17645
  for (const sk of sessionKeys) {
15895
17646
  if (!sk.isDirectory()) continue;
15896
17647
  for (const date of datesToScan) {
15897
- const summaryFile = path16.join(hourlyBaseDir, sk.name, `${date}.md`);
17648
+ const summaryFile = path17.join(hourlyBaseDir, sk.name, `${date}.md`);
15898
17649
  try {
15899
- const raw = await readFile5(summaryFile, "utf-8");
17650
+ const raw = await readFile6(summaryFile, "utf-8");
15900
17651
  const filtered = filterHourlySummaryMarkdownForLocalDay(
15901
17652
  raw,
15902
17653
  date,
@@ -16358,18 +18109,18 @@ ${reflectionsContent.trim()}
16358
18109
  // src/orchestration/namespace-read-fanout.ts
16359
18110
  import { existsSync, readFileSync } from "fs";
16360
18111
  import { stat as stat3 } from "fs/promises";
16361
- import path18 from "path";
18112
+ import path19 from "path";
16362
18113
 
16363
18114
  // src/orchestration/orchestrator-namespace-scope.ts
16364
- import path17 from "path";
18115
+ import path18 from "path";
16365
18116
  function computeSemanticDedupScope(targetStorage, config) {
16366
18117
  if (!resolveNamespaceCapabilities(config).namespaces) return {};
16367
- const memoryDir = path17.resolve(config.memoryDir);
16368
- const storageDir = path17.resolve(targetStorage.dir);
18118
+ const memoryDir = path18.resolve(config.memoryDir);
18119
+ const storageDir = path18.resolve(targetStorage.dir);
16369
18120
  if (storageDir === memoryDir) {
16370
18121
  return { pathExcludePrefixes: ["namespaces/"] };
16371
18122
  }
16372
- let rel = path17.relative(memoryDir, storageDir);
18123
+ let rel = path18.relative(memoryDir, storageDir);
16373
18124
  if (!rel || rel.startsWith("..")) {
16374
18125
  log.debug(
16375
18126
  `semantic dedup: target storage dir ${storageDir} is outside memoryDir ${memoryDir}; scoping lookup to absolute path prefix`
@@ -16422,7 +18173,7 @@ var NamespaceReadFanoutCoordinator = class {
16422
18173
  loadNamespaceStorageDirHintsFromCatalog() {
16423
18174
  if (this.deps.namespaceStorageDirHintsLoaded || !this.deps.namespaceCatalog.enabled) return;
16424
18175
  this.deps.namespaceStorageDirHintsLoaded = true;
16425
- const catalogPath = path18.join(this.deps.config.memoryDir, "state", "namespaces.jsonl");
18176
+ const catalogPath = path19.join(this.deps.config.memoryDir, "state", "namespaces.jsonl");
16426
18177
  if (!existsSync(catalogPath)) return;
16427
18178
  let body;
16428
18179
  try {
@@ -16459,7 +18210,7 @@ var NamespaceReadFanoutCoordinator = class {
16459
18210
  if (!this.deps.storageDirMatchesNamespaceHint(record.namespace, record.storageDir)) {
16460
18211
  continue;
16461
18212
  }
16462
- const resolvedStorageDir = path18.resolve(record.storageDir);
18213
+ const resolvedStorageDir = path19.resolve(record.storageDir);
16463
18214
  const current = preferredByStorageDir.get(resolvedStorageDir);
16464
18215
  preferredByStorageDir.set(
16465
18216
  resolvedStorageDir,
@@ -16643,12 +18394,12 @@ var NamespaceReadFanoutCoordinator = class {
16643
18394
  }
16644
18395
  async resolveStateDirForNamespace(namespace) {
16645
18396
  if (!resolveNamespaceCapabilities(this.deps.config).namespaces) {
16646
- return path18.join(this.deps.config.memoryDir, "state");
18397
+ return path19.join(this.deps.config.memoryDir, "state");
16647
18398
  }
16648
18399
  if (namespace !== this.deps.config.defaultNamespace) {
16649
- return path18.join(this.deps.config.memoryDir, "namespaces", namespace, "state");
18400
+ return path19.join(this.deps.config.memoryDir, "namespaces", namespace, "state");
16650
18401
  }
16651
- const candidate = path18.join(
18402
+ const candidate = path19.join(
16652
18403
  this.deps.config.memoryDir,
16653
18404
  "namespaces",
16654
18405
  this.deps.config.defaultNamespace
@@ -16656,11 +18407,11 @@ var NamespaceReadFanoutCoordinator = class {
16656
18407
  try {
16657
18408
  const candidateStat = await stat3(candidate);
16658
18409
  if (candidateStat.isDirectory()) {
16659
- return path18.join(candidate, "state");
18410
+ return path19.join(candidate, "state");
16660
18411
  }
16661
18412
  } catch {
16662
18413
  }
16663
- return path18.join(this.deps.config.memoryDir, "state");
18414
+ return path19.join(this.deps.config.memoryDir, "state");
16664
18415
  }
16665
18416
  namespaceFromPath(p) {
16666
18417
  if (!resolveNamespaceCapabilities(this.deps.config).namespaces) return this.deps.config.defaultNamespace;
@@ -17097,25 +18848,27 @@ var Orchestrator = class {
17097
18848
  passiveCorrectionDedup = /* @__PURE__ */ new Set();
17098
18849
  _passiveCorrectionService = null;
17099
18850
  passiveCorrectionTelemetry = { detected: 0, queued: 0, autoApplied: 0, suppressedReasonCounts: {} };
17100
- /**
17101
- * Background serial extraction queue coordinator (issue #1526 — moved
17102
- * from inline `extractionQueue`/`queueProcessing` fields). Owns queue
17103
- * state + scheduling + the serial drain + failure classification; the
17104
- * orchestrator builds each task closure in `queueBufferedExtraction` and
17105
- * hands it to `enqueue`.
17106
- */
18851
+ _dependencyPropagationDelivery;
18852
+ _dependencyPropagationRecovery = null;
18853
+ destroyed = false;
18854
+ get dependencyPropagationDelivery() {
18855
+ if (this.destroyed) throw new Error("orchestrator has been destroyed");
18856
+ if (!this._dependencyPropagationDelivery) {
18857
+ this._dependencyPropagationDelivery = new DependencyPropagationDelivery({
18858
+ queueRoot: path20.join(this.config.memoryDir, "state", "dependency-propagation"),
18859
+ config: this.config,
18860
+ extraction: this.extraction,
18861
+ getStorage: (namespace) => this.getStorage(namespace),
18862
+ readQueueFile: (filePath) => this.storage.readDependencyPropagationQueueFile(filePath),
18863
+ writeQueueFile: (filePath, content) => this.storage.writeDependencyPropagationQueueFile(filePath, content),
18864
+ autoStart: true,
18865
+ replayReconciliation: createDependencyPropagationReplayReconciliation({ getStorage: (namespace) => this.getStorage(namespace), indexPersistedMemory: (storage, memoryId) => this.indexPersistedMemory(storage, memoryId), removeFromIndex: (memoryId) => this.embeddingFallback.removeFromIndex(memoryId), config: this.config })
18866
+ });
18867
+ }
18868
+ return this._dependencyPropagationDelivery;
18869
+ }
17107
18870
  extractionQueueCoordinator;
17108
- /**
17109
- * Extraction-run pipeline coordinator (issue #1526 seam 15). Owns
17110
- * `runExtraction` + dedupe fingerprint helpers + processed-fingerprint
17111
- * recording. The orchestrator delegates and injects its own methods.
17112
- */
17113
18871
  _extractionRunCoordinator;
17114
- /**
17115
- * Lazy getter: creates the ExtractionRunCoordinator on first access using
17116
- * the orchestrator's live field references. Supports Object.create(prototype)
17117
- * tests that set fields post-construction without invoking the constructor.
17118
- */
17119
18872
  get extractionRunCoordinator() {
17120
18873
  if (!this._extractionRunCoordinator) {
17121
18874
  this._extractionRunCoordinator = new ExtractionRunCoordinator({
@@ -17140,15 +18893,6 @@ var Orchestrator = class {
17140
18893
  }
17141
18894
  return this._extractionRunCoordinator;
17142
18895
  }
17143
- /**
17144
- * Consolidation-run coordinator (issue #1526 seam 17). Owns
17145
- * the full consolidation maintenance pass (LLM merge/invalidate/update,
17146
- * entity merge, commitment/TTL cleanup, lifecycle policy, compression
17147
- * guideline learning, tier migration, fact archival, semantic consolidation,
17148
- * identity consolidation, profile consolidation, summarization, topic
17149
- * extraction, TMT rebuild). The orchestrator delegates and injects
17150
- * its own methods + coordinators.
17151
- */
17152
18896
  _consolidationRunCoordinator;
17153
18897
  get consolidationRunCoordinator() {
17154
18898
  if (!this._consolidationRunCoordinator) {
@@ -17158,6 +18902,8 @@ var Orchestrator = class {
17158
18902
  getStorageRouter: () => this.storageRouter,
17159
18903
  getNamespaceCatalog: () => this.namespaceCatalog,
17160
18904
  getExtraction: () => this.extraction,
18905
+ storageDirNamespace: (storageDir) => this.storageDirNamespace(storageDir),
18906
+ getDependencyPropagationDelivery: () => this.dependencyPropagationDelivery,
17161
18907
  embeddingFallback: this.embeddingFallback,
17162
18908
  tmtBuilder: this.tmtBuilder,
17163
18909
  consolidationObservers: this.consolidationObservers,
@@ -17176,11 +18922,6 @@ var Orchestrator = class {
17176
18922
  }
17177
18923
  return this._consolidationRunCoordinator;
17178
18924
  }
17179
- /**
17180
- * Extraction-persist coordinator (issue #1526 seam 16). Owns the
17181
- * `persistExtraction` pipeline. Lazy: created on first access so
17182
- * Object.create(prototype) tests that set fields post-construction work.
17183
- */
17184
18925
  _extractionPersistCoordinator;
17185
18926
  get extractionPersistCoordinator() {
17186
18927
  if (!this._extractionPersistCoordinator) {
@@ -17188,8 +18929,8 @@ var Orchestrator = class {
17188
18929
  config: this.config,
17189
18930
  getStorageRouter: () => this.storageRouter,
17190
18931
  getThreading: () => this.threading,
18932
+ getDependencyPropagationDelivery: () => this.dependencyPropagationDelivery,
17191
18933
  getLocalLlm: () => this.localLlm,
17192
- getExtraction: () => this.extraction,
17193
18934
  getQmd: () => this.qmd,
17194
18935
  getJudgeVerdictCache: () => this.judgeVerdictCache,
17195
18936
  getJudgeDeferCounts: () => this.judgeDeferCounts,
@@ -17309,12 +19050,21 @@ var Orchestrator = class {
17309
19050
  */
17310
19051
  async destroy() {
17311
19052
  this.abortDeferredInit();
19053
+ this.extractionQueueCoordinator.stopAccepting();
17312
19054
  if (this.wearablesAutoSyncHandle) {
17313
19055
  await this.wearablesAutoSyncHandle.stop();
17314
19056
  this.wearablesAutoSyncHandle = null;
17315
19057
  }
17316
19058
  for (const svc of this.meetingsServiceByNamespace.values()) svc.dispose();
17317
19059
  await this.maintenanceScheduler.dispose();
19060
+ await this._dependencyPropagationRecovery;
19061
+ this._dependencyPropagationRecovery = null;
19062
+ const extractionDrainError = await drainExtractionAndShutdownDependencyPropagation(
19063
+ this.extractionQueueCoordinator,
19064
+ this._dependencyPropagationDelivery
19065
+ );
19066
+ this.destroyed = true;
19067
+ this._dependencyPropagationDelivery = void 0;
17318
19068
  await drainRecallWrites(this);
17319
19069
  await this.persistenceIndexCoordinator.drainContentHashReconcileRetries().catch((err) => log.warn(`content-hash reconcile drain failed during destroy: ${err}`));
17320
19070
  let bufferFlushError;
@@ -17338,6 +19088,12 @@ var Orchestrator = class {
17338
19088
  );
17339
19089
  throw bufferFlushError;
17340
19090
  }
19091
+ if (extractionDrainError !== void 0) {
19092
+ log.warn(
19093
+ `orchestrator.destroy: extraction queue did not drain before teardown: ${String(extractionDrainError)}`
19094
+ );
19095
+ throw extractionDrainError;
19096
+ }
17341
19097
  }
17342
19098
  }
17343
19099
  /** Set per-session workspace for the next recall() call (compaction reset). @internal */
@@ -17441,7 +19197,7 @@ var Orchestrator = class {
17441
19197
  const defaultNs = normalizeNamespaceIdentity(this.config.defaultNamespace);
17442
19198
  if (ns !== defaultNs && !isSafeRouteNamespace(ns)) return;
17443
19199
  if (!this.storageDirMatchesNamespaceHint(ns, storageDir)) return;
17444
- const resolvedStorageDir = path19.resolve(storageDir);
19200
+ const resolvedStorageDir = path20.resolve(storageDir);
17445
19201
  let hints = this.namespaceStorageDirHints.get(resolvedStorageDir);
17446
19202
  if (!hints) {
17447
19203
  hints = /* @__PURE__ */ new Set();
@@ -17452,21 +19208,21 @@ var Orchestrator = class {
17452
19208
  storageDirMatchesNamespaceHint(namespace, storageDir) {
17453
19209
  const ns = normalizeNamespaceIdentity(namespace);
17454
19210
  if (!ns) return false;
17455
- const resolvedStorageDir = path19.resolve(storageDir);
17456
- const resolvedMemoryDir = path19.resolve(this.config.memoryDir);
19211
+ const resolvedStorageDir = path20.resolve(storageDir);
19212
+ const resolvedMemoryDir = path20.resolve(this.config.memoryDir);
17457
19213
  const defaultNs = normalizeNamespaceIdentity(this.config.defaultNamespace);
17458
19214
  if (resolvedStorageDir === resolvedMemoryDir) return ns === defaultNs;
17459
- const resolvedNamespacesDir = path19.join(resolvedMemoryDir, "namespaces");
19215
+ const resolvedNamespacesDir = path20.join(resolvedMemoryDir, "namespaces");
17460
19216
  if (!isPathInsideStorageRoot(resolvedNamespacesDir, resolvedStorageDir)) return false;
17461
- const rawRoot = path19.resolve(resolvedNamespacesDir, ns);
17462
- const tokenRoot = path19.resolve(resolvedNamespacesDir, namespaceIdentityToken(ns));
19217
+ const rawRoot = path20.resolve(resolvedNamespacesDir, ns);
19218
+ const tokenRoot = path20.resolve(resolvedNamespacesDir, namespaceIdentityToken(ns));
17463
19219
  return resolvedStorageDir === rawRoot || resolvedStorageDir === tokenRoot;
17464
19220
  }
17465
19221
  namespaceStorageDirHintOwnershipRank(record, resolvedStorageDir, configured) {
17466
- if (resolvedStorageDir === path19.resolve(this.config.memoryDir)) {
19222
+ if (resolvedStorageDir === path20.resolve(this.config.memoryDir)) {
17467
19223
  return record.namespace === normalizeNamespaceIdentity(this.config.defaultNamespace) ? 0 : 3;
17468
19224
  }
17469
- const leaf = path19.basename(resolvedStorageDir);
19225
+ const leaf = path20.basename(resolvedStorageDir);
17470
19226
  const tokenOwnsRoot = namespaceIdentityToken(record.namespace) === leaf;
17471
19227
  if (tokenOwnsRoot && configured.has(record.namespace)) return 0;
17472
19228
  if (record.namespace === leaf) return 1;
@@ -17606,7 +19362,7 @@ var Orchestrator = class {
17606
19362
  StorageManager.setScopedCacheInvalidationDefault(config.memoryDir, scopedCacheInvalidationEnabled);
17607
19363
  this.profiler = new ProfilingCollector({
17608
19364
  enabled: resolvePipelineProcessingCapabilities(this.config).profiling,
17609
- storageDir: config.profilingStorageDir || path19.join(config.memoryDir, "profiling"),
19365
+ storageDir: config.profilingStorageDir || path20.join(config.memoryDir, "profiling"),
17610
19366
  maxTraces: config.profilingMaxTraces
17611
19367
  });
17612
19368
  this.namespaceCatalog = new NamespaceCatalog(config);
@@ -17693,7 +19449,7 @@ var Orchestrator = class {
17693
19449
  this.compounding = resolveConsolidationCapabilities(config).compounding ? new CompoundingEngine(config, this.storage) : void 0;
17694
19450
  this.buffer = new SmartBuffer(config, this.storage);
17695
19451
  this.transcript = new TranscriptManager(config);
17696
- this.conversationIndexDir = path19.join(
19452
+ this.conversationIndexDir = path20.join(
17697
19453
  config.memoryDir,
17698
19454
  "conversation-index",
17699
19455
  "chunks"
@@ -17720,7 +19476,9 @@ var Orchestrator = class {
17720
19476
  extractMemoryIdsFromResults: (results) => this.extractMemoryIdsFromResults(results),
17721
19477
  namespaceFromPath: (p) => this.namespaceFromPath(p),
17722
19478
  storageForNamespace: (namespace) => this.storageRouter.storageFor(namespace),
17723
- getExtraction: () => this.extraction
19479
+ getExtraction: () => this.extraction,
19480
+ storageDirNamespace: (storageDir) => this.storageDirNamespace(storageDir),
19481
+ getDependencyPropagationDelivery: () => this.dependencyPropagationDelivery
17724
19482
  });
17725
19483
  this.modelRegistry = new ModelRegistry(config.memoryDir);
17726
19484
  this.graphRecallCoordinator = new GraphRecallCoordinator({
@@ -17841,7 +19599,7 @@ var Orchestrator = class {
17841
19599
  saveContentHashIndexes: () => this.saveContentHashIndexes()
17842
19600
  });
17843
19601
  this.threading = new ThreadingManager(
17844
- path19.join(config.memoryDir, "threads"),
19602
+ path20.join(config.memoryDir, "threads"),
17845
19603
  config.threadingGapMinutes
17846
19604
  );
17847
19605
  const lifecycleCaps = resolveMemoryLifecycleCapabilities(config);
@@ -17921,7 +19679,7 @@ var Orchestrator = class {
17921
19679
  utilityPromoteThresholdDelta: this.utilityRuntimeValues?.promoteThresholdDelta ?? 0,
17922
19680
  utilityDemoteThresholdDelta: this.utilityRuntimeValues?.demoteThresholdDelta ?? 0
17923
19681
  };
17924
- return createHash6("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 12);
19682
+ return createHash8("sha256").update(JSON.stringify(payload)).digest("hex").slice(0, 12);
17925
19683
  }
17926
19684
  effectiveLifecycleThresholds() {
17927
19685
  const archiveDecayThreshold = this.config.lifecycleArchiveDecayThreshold;
@@ -18022,7 +19780,13 @@ var Orchestrator = class {
18022
19780
  return buildChainFollowupGenerator(this.fastLlmForRerank);
18023
19781
  }
18024
19782
  async initialize() {
18025
- return this.orchestratorInitCoordinator.initialize();
19783
+ this.extractionQueueCoordinator.resumeAccepting();
19784
+ await this.orchestratorInitCoordinator.initialize();
19785
+ if (isDependencyPropagationEnabled(this.config)) {
19786
+ this._dependencyPropagationRecovery = this.dependencyPropagationDelivery.recover().catch((err) => {
19787
+ log.warn(`dependency propagation startup recovery failed: ${err}`);
19788
+ });
19789
+ }
18026
19790
  }
18027
19791
  async deferredInitialize(signal) {
18028
19792
  return this.orchestratorInitCoordinator.deferredInitialize(
@@ -19945,9 +21709,9 @@ function readStructuralSymbol(entry) {
19945
21709
  const rec = entry;
19946
21710
  const symbol = typeof rec.symbol === "string" ? rec.symbol.trim() : "";
19947
21711
  if (!symbol) return null;
19948
- const path34 = typeof rec.path === "string" && rec.path.length > 0 ? rec.path : void 0;
21712
+ const path35 = typeof rec.path === "string" && rec.path.length > 0 ? rec.path : void 0;
19949
21713
  const kind = typeof rec.kind === "string" && rec.kind.length > 0 ? rec.kind : void 0;
19950
- const item = path34 !== void 0 && kind !== void 0 ? { symbol, path: path34, kind } : path34 !== void 0 ? { symbol, path: path34 } : kind !== void 0 ? { symbol, kind } : { symbol };
21714
+ const item = path35 !== void 0 && kind !== void 0 ? { symbol, path: path35, kind } : path35 !== void 0 ? { symbol, path: path35 } : kind !== void 0 ? { symbol, kind } : { symbol };
19951
21715
  return item;
19952
21716
  }
19953
21717
  function classifySpawnError(err) {
@@ -20021,7 +21785,7 @@ var DEFAULT_GRACE_PERIOD_DAYS = 7;
20021
21785
 
20022
21786
  // src/binary-lifecycle/backend.ts
20023
21787
  import fsp from "fs/promises";
20024
- import path20 from "path";
21788
+ import path21 from "path";
20025
21789
  var FilesystemBackend = class {
20026
21790
  type = "filesystem";
20027
21791
  basePath;
@@ -20029,19 +21793,19 @@ var FilesystemBackend = class {
20029
21793
  if (!basePath || basePath.trim().length === 0) {
20030
21794
  throw new Error("FilesystemBackend requires a non-empty basePath");
20031
21795
  }
20032
- this.basePath = path20.resolve(basePath);
21796
+ this.basePath = path21.resolve(basePath);
20033
21797
  }
20034
21798
  resolveRemotePath(remotePath) {
20035
- const resolved = path20.isAbsolute(remotePath) ? path20.resolve(remotePath) : path20.resolve(this.basePath, remotePath);
20036
- const relative = path20.relative(this.basePath, resolved);
20037
- if (relative === ".." || relative.startsWith(`..${path20.sep}`) || path20.isAbsolute(relative)) {
21799
+ const resolved = path21.isAbsolute(remotePath) ? path21.resolve(remotePath) : path21.resolve(this.basePath, remotePath);
21800
+ const relative = path21.relative(this.basePath, resolved);
21801
+ if (relative === ".." || relative.startsWith(`..${path21.sep}`) || path21.isAbsolute(relative)) {
20038
21802
  throw new Error(`FilesystemBackend remotePath escapes basePath: ${JSON.stringify(remotePath)}`);
20039
21803
  }
20040
21804
  return resolved;
20041
21805
  }
20042
21806
  isInsideBase(candidate, realBase) {
20043
- const relative = path20.relative(realBase, candidate);
20044
- return relative === "" || relative !== ".." && !relative.startsWith(`..${path20.sep}`) && !path20.isAbsolute(relative);
21807
+ const relative = path21.relative(realBase, candidate);
21808
+ return relative === "" || relative !== ".." && !relative.startsWith(`..${path21.sep}`) && !path21.isAbsolute(relative);
20045
21809
  }
20046
21810
  async realBasePathIfExists() {
20047
21811
  try {
@@ -20070,13 +21834,13 @@ var FilesystemBackend = class {
20070
21834
  }
20071
21835
  async ensureSafeParentDirectory(dest) {
20072
21836
  const realBase = await this.ensureBaseDirectory();
20073
- const destDir = path20.dirname(dest);
20074
- const relativeDir = path20.relative(this.basePath, destDir);
20075
- const segments = relativeDir === "" ? [] : relativeDir.split(path20.sep);
21837
+ const destDir = path21.dirname(dest);
21838
+ const relativeDir = path21.relative(this.basePath, destDir);
21839
+ const segments = relativeDir === "" ? [] : relativeDir.split(path21.sep);
20076
21840
  let current = this.basePath;
20077
21841
  for (const segment of segments) {
20078
21842
  if (segment === "." || segment === "") continue;
20079
- current = path20.join(current, segment);
21843
+ current = path21.join(current, segment);
20080
21844
  try {
20081
21845
  const stat5 = await fsp.lstat(current);
20082
21846
  if (stat5.isSymbolicLink()) {
@@ -20104,13 +21868,13 @@ var FilesystemBackend = class {
20104
21868
  if (realBase === null) {
20105
21869
  return null;
20106
21870
  }
20107
- const destDir = path20.dirname(dest);
20108
- const relativeDir = path20.relative(this.basePath, destDir);
20109
- const segments = relativeDir === "" ? [] : relativeDir.split(path20.sep);
21871
+ const destDir = path21.dirname(dest);
21872
+ const relativeDir = path21.relative(this.basePath, destDir);
21873
+ const segments = relativeDir === "" ? [] : relativeDir.split(path21.sep);
20110
21874
  let current = this.basePath;
20111
21875
  for (const segment of segments) {
20112
21876
  if (segment === "." || segment === "") continue;
20113
- current = path20.join(current, segment);
21877
+ current = path21.join(current, segment);
20114
21878
  let stat5;
20115
21879
  try {
20116
21880
  stat5 = await fsp.lstat(current);
@@ -20158,7 +21922,7 @@ var FilesystemBackend = class {
20158
21922
  return dest;
20159
21923
  }
20160
21924
  async upload(localPath, remotePath) {
20161
- if (path20.isAbsolute(remotePath)) {
21925
+ if (path21.isAbsolute(remotePath)) {
20162
21926
  throw new Error(`FilesystemBackend upload remotePath must be relative: ${JSON.stringify(remotePath)}`);
20163
21927
  }
20164
21928
  const dest = this.resolveRemotePath(remotePath);
@@ -20231,7 +21995,7 @@ function createBackend(cfg) {
20231
21995
 
20232
21996
  // src/binary-lifecycle/scanner.ts
20233
21997
  import fsp2 from "fs/promises";
20234
- import path21 from "path";
21998
+ import path22 from "path";
20235
21999
  import crypto from "crypto";
20236
22000
  function matchesPatterns(filename, patterns) {
20237
22001
  const lower = filename.toLowerCase();
@@ -20256,8 +22020,8 @@ async function scanForBinaries(memoryDir, config, manifest) {
20256
22020
  return;
20257
22021
  }
20258
22022
  for (const entry of entries) {
20259
- const fullPath = path21.join(dir, entry.name);
20260
- const relativePath = path21.relative(memoryDir, fullPath).split(path21.sep).join("/");
22023
+ const fullPath = path22.join(dir, entry.name);
22024
+ const relativePath = path22.relative(memoryDir, fullPath).split(path22.sep).join("/");
20261
22025
  if (entry.isDirectory()) {
20262
22026
  if (entry.name === ".binary-lifecycle") continue;
20263
22027
  await walk(fullPath);
@@ -20297,15 +22061,15 @@ async function hashFile(filePath) {
20297
22061
 
20298
22062
  // src/binary-lifecycle/manifest.ts
20299
22063
  import fsp3 from "fs/promises";
20300
- import path22 from "path";
22064
+ import path23 from "path";
20301
22065
  import crypto2 from "crypto";
20302
22066
  var MANIFEST_DIR = ".binary-lifecycle";
20303
22067
  var MANIFEST_FILE = "manifest.json";
20304
22068
  function manifestDir(memoryDir) {
20305
- return path22.join(memoryDir, MANIFEST_DIR);
22069
+ return path23.join(memoryDir, MANIFEST_DIR);
20306
22070
  }
20307
22071
  function manifestPath(memoryDir) {
20308
- return path22.join(memoryDir, MANIFEST_DIR, MANIFEST_FILE);
22072
+ return path23.join(memoryDir, MANIFEST_DIR, MANIFEST_FILE);
20309
22073
  }
20310
22074
  async function readManifest(memoryDir) {
20311
22075
  const filePath = manifestPath(memoryDir);
@@ -20357,13 +22121,13 @@ function emptyManifest() {
20357
22121
 
20358
22122
  // src/binary-lifecycle/pipeline.ts
20359
22123
  import fsp4 from "fs/promises";
20360
- import path23 from "path";
22124
+ import path24 from "path";
20361
22125
  import crypto3 from "crypto";
20362
22126
  var historicalIdCache = new HistoricalEntityCanonicalIdCache();
20363
22127
  function isRecallCorpusMarkdown(mdPath, memoryDir) {
20364
- const rel = path23.relative(memoryDir, mdPath);
20365
- if (rel.startsWith("..") || path23.isAbsolute(rel)) return false;
20366
- const top = rel.split(path23.sep)[0];
22128
+ const rel = path24.relative(memoryDir, mdPath);
22129
+ if (rel.startsWith("..") || path24.isAbsolute(rel)) return false;
22130
+ const top = rel.split(path24.sep)[0];
20367
22131
  return RECALL_FALLBACK_DIRS.includes(top);
20368
22132
  }
20369
22133
  async function hashFile2(filePath) {
@@ -20387,13 +22151,13 @@ function escapeRegex(s) {
20387
22151
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
20388
22152
  }
20389
22153
  function resolveManifestAssetPath(memoryDir, originalPath) {
20390
- if (originalPath.length === 0 || originalPath.includes("\0") || originalPath.includes("\\") || path23.isAbsolute(originalPath) || path23.win32.isAbsolute(originalPath)) {
22154
+ if (originalPath.length === 0 || originalPath.includes("\0") || originalPath.includes("\\") || path24.isAbsolute(originalPath) || path24.win32.isAbsolute(originalPath)) {
20391
22155
  return null;
20392
22156
  }
20393
- const memoryRoot = path23.resolve(memoryDir);
20394
- const fullPath = path23.resolve(memoryRoot, originalPath);
20395
- const relative = path23.relative(memoryRoot, fullPath);
20396
- if (relative === "" || relative === ".." || relative.startsWith(`..${path23.sep}`) || path23.isAbsolute(relative)) {
22157
+ const memoryRoot = path24.resolve(memoryDir);
22158
+ const fullPath = path24.resolve(memoryRoot, originalPath);
22159
+ const relative = path24.relative(memoryRoot, fullPath);
22160
+ if (relative === "" || relative === ".." || relative.startsWith(`..${path24.sep}`) || path24.isAbsolute(relative)) {
20397
22161
  return null;
20398
22162
  }
20399
22163
  return fullPath;
@@ -20404,7 +22168,7 @@ function validateBinaryLifecycleConfig(config) {
20404
22168
  }
20405
22169
  }
20406
22170
  function remotePathForAsset(backend, relPath) {
20407
- const normalized = relPath.split(path23.sep).join("/");
22171
+ const normalized = relPath.split(path24.sep).join("/");
20408
22172
  if (backend.type === "filesystem") {
20409
22173
  return `.binary-lifecycle/mirrors/${normalized}`;
20410
22174
  }
@@ -20417,11 +22181,11 @@ async function stageMirror(memoryDir, newPaths, backend, assets, log2, dryRun) {
20417
22181
  let mirrored = 0;
20418
22182
  const errors = [];
20419
22183
  for (const relPath of newPaths) {
20420
- const fullPath = path23.join(memoryDir, relPath);
22184
+ const fullPath = path24.join(memoryDir, relPath);
20421
22185
  try {
20422
22186
  const stat5 = await fsp4.stat(fullPath);
20423
22187
  const contentHash = await hashFile2(fullPath);
20424
- const ext = path23.extname(relPath);
22188
+ const ext = path24.extname(relPath);
20425
22189
  const mimeType = guessMimeType(ext);
20426
22190
  const remotePath = remotePathForAsset(backend, relPath);
20427
22191
  let backendLocation = remotePath;
@@ -20559,7 +22323,7 @@ async function stageRedirect(memoryDir, assets, log2, dryRun, readMarkdownFile,
20559
22323
  for (const update of updates) {
20560
22324
  try {
20561
22325
  await withRawEntityPageMutation(memoryDir, update.mdPath, async () => {
20562
- const stateDir = path23.join(memoryDir, "state");
22326
+ const stateDir = path24.join(memoryDir, "state");
20563
22327
  const kind = classifyEntityRefWritePath(memoryDir, update.mdPath);
20564
22328
  if (kind === "memory") {
20565
22329
  const idsAtWrite = historicalIdCache.get(stateDir);
@@ -20653,10 +22417,10 @@ async function countRemainingLocalReferences(memoryDir, asset, assetAbsolute, md
20653
22417
  return { remaining, errors };
20654
22418
  }
20655
22419
  function markdownReferencePattern(asset, assetAbsolute, mdPath) {
20656
- const mdDir = path23.dirname(mdPath);
22420
+ const mdDir = path24.dirname(mdPath);
20657
22421
  const candidates = /* @__PURE__ */ new Set();
20658
22422
  const addCandidate = (candidate) => {
20659
- const normalized = candidate.split(path23.sep).join("/");
22423
+ const normalized = candidate.split(path24.sep).join("/");
20660
22424
  if (normalized.length === 0) return;
20661
22425
  candidates.add(normalized);
20662
22426
  const isParentTraversal = normalized === ".." || normalized.startsWith("../");
@@ -20664,10 +22428,10 @@ function markdownReferencePattern(asset, assetAbsolute, mdPath) {
20664
22428
  candidates.add(`./${normalized}`);
20665
22429
  }
20666
22430
  };
20667
- addCandidate(path23.relative(mdDir, assetAbsolute));
20668
- const originalPath = asset.originalPath.split(path23.sep).join("/");
20669
- const originalAsFileRelative = path23.resolve(mdDir, ...originalPath.split("/"));
20670
- if (path23.resolve(originalAsFileRelative) === path23.resolve(assetAbsolute)) {
22431
+ addCandidate(path24.relative(mdDir, assetAbsolute));
22432
+ const originalPath = asset.originalPath.split(path24.sep).join("/");
22433
+ const originalAsFileRelative = path24.resolve(mdDir, ...originalPath.split("/"));
22434
+ if (path24.resolve(originalAsFileRelative) === path24.resolve(assetAbsolute)) {
20671
22435
  addCandidate(originalPath);
20672
22436
  }
20673
22437
  addCandidate(`/${originalPath}`);
@@ -20763,7 +22527,7 @@ async function findMarkdownFiles(dir) {
20763
22527
  return;
20764
22528
  }
20765
22529
  for (const entry of entries) {
20766
- const full = path23.join(current, entry.name);
22530
+ const full = path24.join(current, entry.name);
20767
22531
  if (entry.isDirectory()) {
20768
22532
  if (entry.name === ".binary-lifecycle") continue;
20769
22533
  await walk(full);
@@ -20838,7 +22602,7 @@ async function runBinaryLifecyclePipeline(memoryDir, config, backend, log2, opts
20838
22602
 
20839
22603
  // src/projection/index.ts
20840
22604
  import fs from "fs";
20841
- import path24 from "path";
22605
+ import path25 from "path";
20842
22606
  var VALID_PROJECTION_CATEGORIES = new Set(ALL_CATEGORY_KEYS);
20843
22607
  async function generateContextTree(options) {
20844
22608
  const startTime = Date.now();
@@ -20853,8 +22617,8 @@ async function generateContextTree(options) {
20853
22617
  let nodesGenerated = 0;
20854
22618
  let nodesSkipped = 0;
20855
22619
  const categoryCounts = {};
20856
- const resolvedMemoryDir = path24.resolve(memoryDir);
20857
- const resolvedOutputDir = path24.resolve(outputDir);
22620
+ const resolvedMemoryDir = path25.resolve(memoryDir);
22621
+ const resolvedOutputDir = path25.resolve(outputDir);
20858
22622
  const requestedCategories = validateProjectionCategories(filterCategories);
20859
22623
  const realMemoryDir = assertSafeMemoryRoot(resolvedMemoryDir);
20860
22624
  assertNotSymlink(resolvedOutputDir, "context tree outputDir");
@@ -20892,7 +22656,7 @@ async function generateContextTree(options) {
20892
22656
  }
20893
22657
  }
20894
22658
  if (includeEntities) {
20895
- const entitiesDir = path24.join(memoryDir, "entities");
22659
+ const entitiesDir = path25.join(memoryDir, "entities");
20896
22660
  if (fs.existsSync(entitiesDir)) {
20897
22661
  assertSafeInputRoot(realMemoryDir, entitiesDir, "entities root");
20898
22662
  categoryCounts["entity"] = 0;
@@ -20904,7 +22668,7 @@ async function generateContextTree(options) {
20904
22668
  continue;
20905
22669
  }
20906
22670
  const content = fs.readFileSync(filePath, "utf8");
20907
- const fileName = path24.basename(filePath, ".md");
22671
+ const fileName = path25.basename(filePath, ".md");
20908
22672
  const node = projectEntityNode(fileName, content);
20909
22673
  const outputPath = resolveContainedOutputPath(realOutputDir, "entities", `${fileName}.md`);
20910
22674
  writeProjectedContent(realOutputDir, outputPath, node.content);
@@ -20916,7 +22680,7 @@ async function generateContextTree(options) {
20916
22680
  }
20917
22681
  const shouldIncludeQuestions = includeQuestions && (requestedCategories === void 0 || requestedCategories.includes("question"));
20918
22682
  if (shouldIncludeQuestions) {
20919
- const questionsDir = path24.join(memoryDir, "questions");
22683
+ const questionsDir = path25.join(memoryDir, "questions");
20920
22684
  if (fs.existsSync(questionsDir)) {
20921
22685
  assertSafeInputRoot(realMemoryDir, questionsDir, "questions root");
20922
22686
  categoryCounts["question"] = 0;
@@ -20957,8 +22721,8 @@ async function generateContextTree(options) {
20957
22721
  };
20958
22722
  }
20959
22723
  function isPathInside(root, candidate) {
20960
- const relative = path24.relative(root, candidate);
20961
- return relative === "" || !relative.startsWith("..") && !path24.isAbsolute(relative);
22724
+ const relative = path25.relative(root, candidate);
22725
+ return relative === "" || !relative.startsWith("..") && !path25.isAbsolute(relative);
20962
22726
  }
20963
22727
  function assertNotSymlink(targetPath, label) {
20964
22728
  try {
@@ -20995,12 +22759,12 @@ function assertSafeInputRoot(realMemoryDir, targetPath, label) {
20995
22759
  }
20996
22760
  function assertSafeOutputTarget(realOutputDir, outputPath) {
20997
22761
  let current = realOutputDir;
20998
- const relative = path24.relative(realOutputDir, outputPath);
20999
- if (relative.startsWith("..") || path24.isAbsolute(relative)) {
22762
+ const relative = path25.relative(realOutputDir, outputPath);
22763
+ if (relative.startsWith("..") || path25.isAbsolute(relative)) {
21000
22764
  throw new Error(`context tree output path escapes outputDir: ${outputPath}`);
21001
22765
  }
21002
- for (const segment of relative.split(path24.sep).filter(Boolean)) {
21003
- current = path24.join(current, segment);
22766
+ for (const segment of relative.split(path25.sep).filter(Boolean)) {
22767
+ current = path25.join(current, segment);
21004
22768
  try {
21005
22769
  const stat5 = fs.lstatSync(current);
21006
22770
  if (stat5.isSymbolicLink()) {
@@ -21030,17 +22794,17 @@ function validateProjectionCategories(categories) {
21030
22794
  return validated;
21031
22795
  }
21032
22796
  function resolveContainedOutputPath(outputRoot, ...segments) {
21033
- const resolved = path24.resolve(outputRoot, ...segments);
21034
- const relative = path24.relative(outputRoot, resolved);
21035
- if (relative === "" || !relative.startsWith("..") && !path24.isAbsolute(relative)) {
22797
+ const resolved = path25.resolve(outputRoot, ...segments);
22798
+ const relative = path25.relative(outputRoot, resolved);
22799
+ if (relative === "" || !relative.startsWith("..") && !path25.isAbsolute(relative)) {
21036
22800
  return resolved;
21037
22801
  }
21038
22802
  throw new Error(`context tree output path escapes outputDir: ${segments.join("/")}`);
21039
22803
  }
21040
22804
  function writeProjectedContent(realOutputDir, outputPath, generatedContent) {
21041
22805
  assertSafeOutputTarget(realOutputDir, outputPath);
21042
- fs.mkdirSync(path24.dirname(outputPath), { recursive: true });
21043
- const realParent = fs.realpathSync(path24.dirname(outputPath));
22806
+ fs.mkdirSync(path25.dirname(outputPath), { recursive: true });
22807
+ const realParent = fs.realpathSync(path25.dirname(outputPath));
21044
22808
  if (!isPathInside(realOutputDir, realParent)) {
21045
22809
  throw new Error(`context tree output path escapes outputDir: ${outputPath}`);
21046
22810
  }
@@ -21079,7 +22843,7 @@ function walkR(dir, realMemoryDir) {
21079
22843
  const results = [];
21080
22844
  function walk(directory) {
21081
22845
  for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
21082
- const fullPath = path24.join(directory, entry.name);
22846
+ const fullPath = path25.join(directory, entry.name);
21083
22847
  if (entry.isSymbolicLink()) {
21084
22848
  throw new Error(`context tree input path contains symlink: ${fullPath}`);
21085
22849
  }
@@ -21132,13 +22896,13 @@ function extractBody(content) {
21132
22896
  }
21133
22897
  function projectNode(filePath, category, fm, rawContent) {
21134
22898
  const body = extractBody(rawContent);
21135
- const fileName = path24.basename(filePath, ".md");
21136
- const dateDir = path24.basename(path24.dirname(filePath));
22899
+ const fileName = path25.basename(filePath, ".md");
22900
+ const dateDir = path25.basename(path25.dirname(filePath));
21137
22901
  let relPath;
21138
22902
  if (/^\d{4}-\d{2}-\d{2}$/.test(dateDir)) {
21139
- relPath = path24.join(category, dateDir, `${fileName}.md`);
22903
+ relPath = path25.join(category, dateDir, `${fileName}.md`);
21140
22904
  } else {
21141
- relPath = path24.join(category, `${fileName}.md`);
22905
+ relPath = path25.join(category, `${fileName}.md`);
21142
22906
  }
21143
22907
  const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
21144
22908
  const md = `# ${fm.id}
@@ -21184,7 +22948,7 @@ function projectEntityNode(fileName, content) {
21184
22948
  ${content}
21185
22949
  `;
21186
22950
  return {
21187
- path: path24.join("entities", `${fileName}.md`),
22951
+ path: path25.join("entities", `${fileName}.md`),
21188
22952
  category: "entity",
21189
22953
  title: fileName,
21190
22954
  content: md,
@@ -21237,7 +23001,7 @@ function generateIndex(categoryCounts, outputDir) {
21237
23001
 
21238
23002
  // src/onboarding/index.ts
21239
23003
  import fs2 from "fs";
21240
- import path25 from "path";
23004
+ import path26 from "path";
21241
23005
  var LANGUAGE_RULES = [
21242
23006
  {
21243
23007
  language: "TypeScript",
@@ -21339,7 +23103,7 @@ function onboard(options) {
21339
23103
  maxDepth = 6,
21340
23104
  excludeDirs = []
21341
23105
  } = options;
21342
- const directory = path25.resolve(options.directory ?? process.cwd());
23106
+ const directory = path26.resolve(options.directory ?? process.cwd());
21343
23107
  let rootStat;
21344
23108
  try {
21345
23109
  rootStat = fs2.statSync(directory);
@@ -21381,7 +23145,7 @@ function walkDir(root, exclude, maxDepth) {
21381
23145
  }
21382
23146
  for (const entry of entries) {
21383
23147
  if (exclude.has(entry.name)) continue;
21384
- const fullPath = path25.join(dir, entry.name);
23148
+ const fullPath = path26.join(dir, entry.name);
21385
23149
  if (entry.isDirectory()) {
21386
23150
  walk(fullPath, depth + 1);
21387
23151
  } else if (entry.isFile()) {
@@ -21396,11 +23160,11 @@ function detectLanguages(files, root) {
21396
23160
  const results = [];
21397
23161
  const extCounts = /* @__PURE__ */ new Map();
21398
23162
  for (const f of files) {
21399
- const ext = path25.extname(f).toLowerCase();
23163
+ const ext = path26.extname(f).toLowerCase();
21400
23164
  if (ext) extCounts.set(ext, (extCounts.get(ext) ?? 0) + 1);
21401
23165
  }
21402
23166
  const rootFiles = new Set(
21403
- files.filter((f) => path25.dirname(f) === root).map((f) => path25.basename(f))
23167
+ files.filter((f) => path26.dirname(f) === root).map((f) => path26.basename(f))
21404
23168
  );
21405
23169
  for (const rule of LANGUAGE_RULES) {
21406
23170
  const evidence = [];
@@ -21444,7 +23208,7 @@ function detectLanguages(files, root) {
21444
23208
  }
21445
23209
  function detectShape(files, root) {
21446
23210
  const rootFiles = new Set(
21447
- files.filter((f) => path25.dirname(f) === root).map((f) => path25.basename(f))
23211
+ files.filter((f) => path26.dirname(f) === root).map((f) => path26.basename(f))
21448
23212
  );
21449
23213
  const rootDirs = /* @__PURE__ */ new Set();
21450
23214
  try {
@@ -21455,7 +23219,7 @@ function detectShape(files, root) {
21455
23219
  }
21456
23220
  const evidence = [];
21457
23221
  if (rootFiles.has("package.json")) {
21458
- const pkg = readJsonSafe(path25.join(root, "package.json"));
23222
+ const pkg = readJsonSafe(path26.join(root, "package.json"));
21459
23223
  if (pkg?.workspaces) {
21460
23224
  evidence.push("package.json has workspaces");
21461
23225
  return { shape: "monorepo", evidence };
@@ -21469,13 +23233,13 @@ function detectShape(files, root) {
21469
23233
  evidence.push("workspace manifest found");
21470
23234
  return { shape: "workspace", evidence };
21471
23235
  }
21472
- const cargoToml = readTomlWorkspace(path25.join(root, "Cargo.toml"));
23236
+ const cargoToml = readTomlWorkspace(path26.join(root, "Cargo.toml"));
21473
23237
  if (cargoToml) {
21474
23238
  evidence.push("Cargo.toml has workspace");
21475
23239
  return { shape: "workspace", evidence };
21476
23240
  }
21477
23241
  if (rootFiles.has("package.json")) {
21478
- const pkg = readJsonSafe(path25.join(root, "package.json"));
23242
+ const pkg = readJsonSafe(path26.join(root, "package.json"));
21479
23243
  if (pkg?.exports || pkg?.main) {
21480
23244
  if (pkg?.bin) {
21481
23245
  evidence.push("package.json has bin");
@@ -21509,8 +23273,8 @@ function discoverDocs(files, root) {
21509
23273
  { pattern: /^\.editorconfig$/i, kind: "config" }
21510
23274
  ];
21511
23275
  for (const filePath of files) {
21512
- const basename = path25.basename(filePath).toLowerCase();
21513
- const relPath = path25.relative(root, filePath);
23276
+ const basename = path26.basename(filePath).toLowerCase();
23277
+ const relPath = path26.relative(root, filePath);
21514
23278
  let kind;
21515
23279
  for (const { pattern, kind: k } of docPatterns) {
21516
23280
  if (pattern.test(basename)) {
@@ -21522,7 +23286,7 @@ function discoverDocs(files, root) {
21522
23286
  kind = "docs";
21523
23287
  }
21524
23288
  if (!kind && (basename.endsWith(".md") || basename.endsWith(".mdx"))) {
21525
- if (path25.dirname(relPath) === "." || isUnderDocsDir(relPath)) {
23289
+ if (path26.dirname(relPath) === "." || isUnderDocsDir(relPath)) {
21526
23290
  kind = "docs";
21527
23291
  }
21528
23292
  }
@@ -21543,7 +23307,7 @@ function discoverDocs(files, root) {
21543
23307
  return docs;
21544
23308
  }
21545
23309
  function isUnderDocsDir(relPath) {
21546
- const parts = relPath.split(path25.sep);
23310
+ const parts = relPath.split(path26.sep);
21547
23311
  return parts[0] === "docs" || parts[0] === "doc" || parts[0] === "documentation";
21548
23312
  }
21549
23313
  function buildPlan(languages, shape, docs, _root) {
@@ -21587,7 +23351,7 @@ function readTomlWorkspace(filePath) {
21587
23351
 
21588
23352
  // src/curation/index.ts
21589
23353
  import fs3 from "fs";
21590
- import path26 from "path";
23354
+ import path27 from "path";
21591
23355
  import crypto4 from "crypto";
21592
23356
  async function curate(options) {
21593
23357
  const startTime = Date.now();
@@ -21678,12 +23442,12 @@ function resolveTargets(targetPath) {
21678
23442
  const extensions = /* @__PURE__ */ new Set([".md", ".txt", ".mdx", ".rst"]);
21679
23443
  function walk(dir) {
21680
23444
  for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
21681
- const fullPath = path26.join(dir, entry.name);
23445
+ const fullPath = path27.join(dir, entry.name);
21682
23446
  if (entry.isDirectory()) {
21683
23447
  if (entry.name !== "node_modules" && entry.name !== ".git") {
21684
23448
  walk(fullPath);
21685
23449
  }
21686
- } else if (extensions.has(path26.extname(entry.name).toLowerCase())) {
23450
+ } else if (extensions.has(path27.extname(entry.name).toLowerCase())) {
21687
23451
  results.push(fullPath);
21688
23452
  }
21689
23453
  }
@@ -21692,12 +23456,12 @@ function resolveTargets(targetPath) {
21692
23456
  return results;
21693
23457
  }
21694
23458
  function resolveProvenanceRoot(targetPath) {
21695
- const resolvedTarget = path26.resolve(targetPath);
23459
+ const resolvedTarget = path27.resolve(targetPath);
21696
23460
  const stat5 = fs3.statSync(resolvedTarget);
21697
- return stat5.isFile() ? path26.dirname(resolvedTarget) : resolvedTarget;
23461
+ return stat5.isFile() ? path27.dirname(resolvedTarget) : resolvedTarget;
21698
23462
  }
21699
23463
  function extractStatements(content, filePath, projectRoot, source, sourceFileHash, categoryOverride, confidence, entityRef, tags) {
21700
- const relativePath = path26.relative(projectRoot, path26.resolve(filePath)) || path26.basename(filePath);
23464
+ const relativePath = path27.relative(projectRoot, path27.resolve(filePath)) || path27.basename(filePath);
21701
23465
  const statements = [];
21702
23466
  const now = (/* @__PURE__ */ new Date()).toISOString();
21703
23467
  const paragraphs = content.split(/\n{2,}/).map((p) => p.trim()).filter((p) => p.length > 20 && p.length < 2e3);
@@ -21791,7 +23555,7 @@ function loadExistingMemories(memoryDir) {
21791
23555
  if (!fs3.existsSync(memoryDir)) return result;
21792
23556
  const dirs = ALL_CATEGORY_DIRS;
21793
23557
  for (const dir of dirs) {
21794
- const fullDir = path26.join(memoryDir, dir);
23558
+ const fullDir = path27.join(memoryDir, dir);
21795
23559
  if (!fs3.existsSync(fullDir)) continue;
21796
23560
  walkFiles(fullDir, (filePath) => {
21797
23561
  const content = readFileSafe(filePath);
@@ -21814,11 +23578,11 @@ function writeStatement(stmt, memoryDir) {
21814
23578
  const now = /* @__PURE__ */ new Date();
21815
23579
  const dateDir = now.toISOString().split("T")[0];
21816
23580
  const categoryDir = getCategoryDir(memoryDir, stmt.category);
21817
- const dir = path26.join(categoryDir, dateDir);
23581
+ const dir = path27.join(categoryDir, dateDir);
21818
23582
  fs3.mkdirSync(dir, { recursive: true });
21819
23583
  const fileName = `${stmt.category}-${Date.now()}-${stmt.id.slice(0, 8)}.md`;
21820
- const filePath = path26.join(dir, fileName);
21821
- const historicalIds = historicalIdCache2.get(path26.join(memoryDir, "state"));
23584
+ const filePath = path27.join(dir, fileName);
23585
+ const historicalIds = historicalIdCache2.get(path27.join(memoryDir, "state"));
21822
23586
  const rawBody = `${[
21823
23587
  "---",
21824
23588
  `id: ${stmt.id}`,
@@ -21841,7 +23605,7 @@ ${stmt.content}
21841
23605
  fs3.writeFileSync(filePath, body);
21842
23606
  try {
21843
23607
  repairContentAfterJournalMoveSync({
21844
- stateDir: path26.join(memoryDir, "state"),
23608
+ stateDir: path27.join(memoryDir, "state"),
21845
23609
  cache: historicalIdCache2,
21846
23610
  idsAtWrite: historicalIds,
21847
23611
  rawContent: rawBody,
@@ -21902,7 +23666,7 @@ function extractBody2(content) {
21902
23666
  }
21903
23667
  function walkFiles(dir, callback) {
21904
23668
  for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
21905
- const fullPath = path26.join(dir, entry.name);
23669
+ const fullPath = path27.join(dir, entry.name);
21906
23670
  if (entry.isDirectory()) {
21907
23671
  walkFiles(fullPath, callback);
21908
23672
  } else if (entry.name.endsWith(".md")) {
@@ -21913,7 +23677,7 @@ function walkFiles(dir, callback) {
21913
23677
 
21914
23678
  // src/dedup/index.ts
21915
23679
  import fs4 from "fs";
21916
- import path27 from "path";
23680
+ import path28 from "path";
21917
23681
  import crypto5 from "crypto";
21918
23682
  var DEFAULT_DEDUP_THRESHOLD = 0.85;
21919
23683
  var DEFAULT_MAX_LOAD = 1e4;
@@ -22055,7 +23819,7 @@ function loadMemories(memoryDir, categories, maxLoad = 1e4) {
22055
23819
  const memoryRootReal = fs4.realpathSync(memoryDir);
22056
23820
  for (const category of allCategories) {
22057
23821
  if (result.length >= maxLoad) break;
22058
- const dir = path27.join(memoryDir, category);
23822
+ const dir = path28.join(memoryDir, category);
22059
23823
  if (!fs4.existsSync(dir)) continue;
22060
23824
  const categoryStat = fs4.lstatSync(dir);
22061
23825
  if (categoryStat.isSymbolicLink()) {
@@ -22116,15 +23880,15 @@ function extractBody3(content) {
22116
23880
  return match ? match[1].trim() : content.trim();
22117
23881
  }
22118
23882
  function assertPathInsideRoot2(rootReal, targetReal, sourcePath) {
22119
- const rel = path27.relative(rootReal, targetReal);
22120
- if (rel === "" || !rel.startsWith("..") && !path27.isAbsolute(rel)) {
23883
+ const rel = path28.relative(rootReal, targetReal);
23884
+ if (rel === "" || !rel.startsWith("..") && !path28.isAbsolute(rel)) {
22121
23885
  return;
22122
23886
  }
22123
23887
  throw new Error(`Refusing to scan memory path outside root: ${sourcePath}`);
22124
23888
  }
22125
23889
  function walkMdFiles(dir, memoryRootReal, categoryRootReal, callback) {
22126
23890
  for (const entry of fs4.readdirSync(dir, { withFileTypes: true })) {
22127
- const fullPath = path27.join(dir, entry.name);
23891
+ const fullPath = path28.join(dir, entry.name);
22128
23892
  const entryStat = fs4.lstatSync(fullPath);
22129
23893
  if (entryStat.isSymbolicLink()) {
22130
23894
  throw new Error(`Refusing to scan symlinked memory path: ${fullPath}`);
@@ -22142,10 +23906,10 @@ function walkMdFiles(dir, memoryRootReal, categoryRootReal, callback) {
22142
23906
 
22143
23907
  // src/review/index.ts
22144
23908
  import fs5 from "fs";
22145
- import path28 from "path";
23909
+ import path29 from "path";
22146
23910
  var historicalIdCache3 = new HistoricalEntityCanonicalIdCache();
22147
23911
  function writeReviewFileCanonicalized(memoryDir, filePath, content) {
22148
- const stateDir = path28.join(memoryDir, "state");
23912
+ const stateDir = path29.join(memoryDir, "state");
22149
23913
  const idsAtWrite = historicalIdCache3.get(stateDir);
22150
23914
  let originalBytes = null;
22151
23915
  try {
@@ -22187,8 +23951,8 @@ function realMemoryRoot(memoryDir) {
22187
23951
  }
22188
23952
  }
22189
23953
  function isPathInside2(rootReal, candidateReal) {
22190
- const relative = path28.relative(rootReal, candidateReal);
22191
- return relative === "" || !!relative && !relative.startsWith("..") && !path28.isAbsolute(relative);
23954
+ const relative = path29.relative(rootReal, candidateReal);
23955
+ return relative === "" || !!relative && !relative.startsWith("..") && !path29.isAbsolute(relative);
22192
23956
  }
22193
23957
  function isSafeDirectory(rootReal, dir) {
22194
23958
  try {
@@ -22231,7 +23995,7 @@ function listReviewItems(options) {
22231
23995
  if (filterReason && item.reviewReason !== filterReason) return;
22232
23996
  items.push(item);
22233
23997
  };
22234
- const suggestionsDir = path28.join(memoryDir, "suggestions");
23998
+ const suggestionsDir = path29.join(memoryDir, "suggestions");
22235
23999
  if (!isLimitReached() && fs5.existsSync(suggestionsDir) && isSafeDirectory(rootReal, suggestionsDir)) {
22236
24000
  walkMd(rootReal, suggestionsDir, (filePath, content) => {
22237
24001
  if (isLimitReached()) return true;
@@ -22252,7 +24016,7 @@ function listReviewItems(options) {
22252
24016
  return isLimitReached();
22253
24017
  });
22254
24018
  }
22255
- const reviewDir = path28.join(memoryDir, "review");
24019
+ const reviewDir = path29.join(memoryDir, "review");
22256
24020
  if (!isLimitReached() && fs5.existsSync(reviewDir) && isSafeDirectory(rootReal, reviewDir)) {
22257
24021
  walkMd(rootReal, reviewDir, (filePath, content) => {
22258
24022
  if (isLimitReached()) return true;
@@ -22277,7 +24041,7 @@ function listReviewItems(options) {
22277
24041
  const categories = ALL_CATEGORY_DIRS;
22278
24042
  for (const category of categories) {
22279
24043
  if (isLimitReached()) break;
22280
- const dir = path28.join(memoryDir, category);
24044
+ const dir = path29.join(memoryDir, category);
22281
24045
  if (!fs5.existsSync(dir) || !isSafeDirectory(rootReal, dir)) continue;
22282
24046
  walkMd(rootReal, dir, (filePath, content) => {
22283
24047
  if (isLimitReached()) return true;
@@ -22359,9 +24123,9 @@ function approveItem(memoryDir, itemId, options) {
22359
24123
  const targetDir = getCategoryDir(memoryDir, category);
22360
24124
  const dateDir = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
22361
24125
  ensureSafeDirectory(rootReal, targetDir);
22362
- const outputPath = path28.join(targetDir, dateDir, path28.basename(found.filePath));
22363
- ensureSafeDirectory(rootReal, path28.dirname(outputPath));
22364
- const reviewStateDir = path28.join(memoryDir, "state");
24126
+ const outputPath = path29.join(targetDir, dateDir, path29.basename(found.filePath));
24127
+ ensureSafeDirectory(rootReal, path29.dirname(outputPath));
24128
+ const reviewStateDir = path29.join(memoryDir, "state");
22365
24129
  const idsAtWrite = historicalIdCache3.get(reviewStateDir);
22366
24130
  const promotedWritten = canonicalizeEntityRefFrontmatter(updatedContent, idsAtWrite);
22367
24131
  const promotedPath = writeFileWithoutClobber(outputPath, promotedWritten, itemId);
@@ -22438,13 +24202,13 @@ function findReviewFileById(memoryDir, id, options = {}) {
22438
24202
  const rootReal = realMemoryRoot(memoryDir);
22439
24203
  if (!rootReal) return null;
22440
24204
  for (const loc of ["suggestions", "review"]) {
22441
- const dir = path28.join(memoryDir, loc);
24205
+ const dir = path29.join(memoryDir, loc);
22442
24206
  if (!fs5.existsSync(dir) || !isSafeDirectory(rootReal, dir)) continue;
22443
24207
  const found = findFileById(rootReal, dir, id);
22444
24208
  if (found) return { filePath: found, location: "queue" };
22445
24209
  }
22446
24210
  for (const category of ALL_CATEGORY_DIRS) {
22447
- const dir = path28.join(memoryDir, category);
24211
+ const dir = path29.join(memoryDir, category);
22448
24212
  if (!fs5.existsSync(dir) || !isSafeDirectory(rootReal, dir)) continue;
22449
24213
  const found = findFileById(rootReal, dir, id, (fm) => isLowConfidenceReviewCandidate(fm, options));
22450
24214
  if (found) return { filePath: found, location: "category" };
@@ -22520,10 +24284,10 @@ function readFileSafe3(filePath) {
22520
24284
  }
22521
24285
  }
22522
24286
  function writeFileWithoutClobber(basePath, content, discriminator) {
22523
- const parsed = path28.parse(basePath);
24287
+ const parsed = path29.parse(basePath);
22524
24288
  const safeDiscriminator = sanitizeFilePart(discriminator);
22525
24289
  for (let attempt = 0; attempt < 1e3; attempt++) {
22526
- const candidate = attempt === 0 ? basePath : path28.join(
24290
+ const candidate = attempt === 0 ? basePath : path29.join(
22527
24291
  parsed.dir,
22528
24292
  `${parsed.name}-${safeDiscriminator}${attempt === 1 ? "" : `-${attempt}`}${parsed.ext || ".md"}`
22529
24293
  );
@@ -22579,7 +24343,7 @@ function extractBody4(content) {
22579
24343
  function walkMd(rootReal, dir, callback) {
22580
24344
  if (!isSafeDirectory(rootReal, dir)) return false;
22581
24345
  for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
22582
- const fullPath = path28.join(dir, entry.name);
24346
+ const fullPath = path29.join(dir, entry.name);
22583
24347
  if (entry.isSymbolicLink()) continue;
22584
24348
  if (entry.isDirectory()) {
22585
24349
  if (walkMd(rootReal, fullPath, callback)) return true;
@@ -22594,7 +24358,7 @@ function walkMdPaths(rootReal, dir) {
22594
24358
  const results = [];
22595
24359
  if (!isSafeDirectory(rootReal, dir)) return results;
22596
24360
  for (const entry of fs5.readdirSync(dir, { withFileTypes: true })) {
22597
- const fullPath = path28.join(dir, entry.name);
24361
+ const fullPath = path29.join(dir, entry.name);
22598
24362
  if (entry.isSymbolicLink()) continue;
22599
24363
  if (entry.isDirectory()) {
22600
24364
  results.push(...walkMdPaths(rootReal, fullPath));
@@ -22607,7 +24371,7 @@ function walkMdPaths(rootReal, dir) {
22607
24371
 
22608
24372
  // src/sync/index.ts
22609
24373
  import fs6 from "fs";
22610
- import path29 from "path";
24374
+ import path30 from "path";
22611
24375
  import crypto6 from "crypto";
22612
24376
  var DEFAULT_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".txt", ".mdx", ".rst"]);
22613
24377
  var DEFAULT_EXCLUDE2 = /* @__PURE__ */ new Set([
@@ -22629,7 +24393,7 @@ function syncChanges(options) {
22629
24393
  } = options;
22630
24394
  const extSet = new Set(extensions);
22631
24395
  const excludeSet = /* @__PURE__ */ new Set([...DEFAULT_EXCLUDE2, ...excludeDirs]);
22632
- const stateFilePath = options.stateFile ?? path29.join(memoryDir, ".sync-state.json");
24396
+ const stateFilePath = options.stateFile ?? path30.join(memoryDir, ".sync-state.json");
22633
24397
  const prevState = loadState(stateFilePath);
22634
24398
  const currentFiles = scanFiles(sourceDir, extSet, excludeSet);
22635
24399
  const changes = computeDiff(currentFiles, prevState.fileHashes, sourceDir);
@@ -22645,7 +24409,7 @@ function syncChanges(options) {
22645
24409
  for (const [relPath, hash] of Object.entries(currentFiles)) {
22646
24410
  newState.fileHashes[relPath] = hash;
22647
24411
  }
22648
- fs6.mkdirSync(path29.dirname(stateFilePath), { recursive: true });
24412
+ fs6.mkdirSync(path30.dirname(stateFilePath), { recursive: true });
22649
24413
  fs6.writeFileSync(stateFilePath, JSON.stringify(newState, null, 2));
22650
24414
  }
22651
24415
  return {
@@ -22711,13 +24475,13 @@ function scanFiles(root, extensions, exclude) {
22711
24475
  }
22712
24476
  for (const entry of entries) {
22713
24477
  if (exclude.has(entry.name)) continue;
22714
- const fullPath = path29.join(dir, entry.name);
24478
+ const fullPath = path30.join(dir, entry.name);
22715
24479
  if (entry.isDirectory()) {
22716
24480
  walk(fullPath);
22717
24481
  } else if (entry.isFile()) {
22718
- const ext = path29.extname(entry.name).toLowerCase();
24482
+ const ext = path30.extname(entry.name).toLowerCase();
22719
24483
  if (!extensions.has(ext)) continue;
22720
- const relPath = path29.relative(root, fullPath);
24484
+ const relPath = path30.relative(root, fullPath);
22721
24485
  try {
22722
24486
  const content = fs6.readFileSync(fullPath, "utf8");
22723
24487
  result[relPath] = hashContent3(content);
@@ -22732,7 +24496,7 @@ function scanFiles(root, extensions, exclude) {
22732
24496
  function computeDiff(current, previous, sourceDir) {
22733
24497
  const changes = [];
22734
24498
  for (const [relPath, hash] of Object.entries(current)) {
22735
- const fullPath = path29.join(sourceDir, relPath);
24499
+ const fullPath = path30.join(sourceDir, relPath);
22736
24500
  if (!(relPath in previous)) {
22737
24501
  let size = 0;
22738
24502
  try {
@@ -22765,7 +24529,7 @@ function computeDiff(current, previous, sourceDir) {
22765
24529
  for (const relPath of Object.keys(previous)) {
22766
24530
  if (!(relPath in current)) {
22767
24531
  changes.push({
22768
- filePath: path29.join(sourceDir, relPath),
24532
+ filePath: path30.join(sourceDir, relPath),
22769
24533
  relativePath: relPath,
22770
24534
  type: "deleted",
22771
24535
  currentHash: "",
@@ -22795,21 +24559,21 @@ function hashContent3(content) {
22795
24559
  import { spawnSync } from "child_process";
22796
24560
  import crypto7 from "crypto";
22797
24561
  import fs7 from "fs";
22798
- import path30 from "path";
24562
+ import path31 from "path";
22799
24563
  var historicalIdCache4 = new HistoricalEntityCanonicalIdCache();
22800
24564
  var MANIFEST_VERSION = 1;
22801
24565
  var MANIFEST_LOCK_STALE_MS = 3e4;
22802
24566
  var MANIFEST_LOCK_TIMEOUT_MS = MANIFEST_LOCK_STALE_MS + 1e4;
22803
24567
  var MANIFEST_LOCK_SLEEP_MS = 20;
22804
24568
  function normalizeSpaceMemoryDir(memoryDir) {
22805
- return path30.resolve(memoryDir);
24569
+ return path31.resolve(memoryDir);
22806
24570
  }
22807
24571
  function getSpacesDir(baseDir) {
22808
24572
  const homeDir = baseDir ?? resolveHomeDir();
22809
- return path30.join(homeDir, ".config", "engram", "spaces");
24573
+ return path31.join(homeDir, ".config", "engram", "spaces");
22810
24574
  }
22811
24575
  function getManifestPath(baseDir) {
22812
- return path30.join(getSpacesDir(baseDir), "manifest.json");
24576
+ return path31.join(getSpacesDir(baseDir), "manifest.json");
22813
24577
  }
22814
24578
  function loadManifest(baseDir, memoryDirOverride) {
22815
24579
  if (fs7.existsSync(getManifestPath(baseDir))) {
@@ -22856,9 +24620,9 @@ function readManifestUnlocked(baseDir, memoryDirOverride, options = {}) {
22856
24620
  }
22857
24621
  function saveManifestUnlocked(manifest, baseDir) {
22858
24622
  const manifestPath2 = getManifestPath(baseDir);
22859
- const manifestDir2 = path30.dirname(manifestPath2);
24623
+ const manifestDir2 = path31.dirname(manifestPath2);
22860
24624
  fs7.mkdirSync(manifestDir2, { recursive: true });
22861
- const tempPath = path30.join(manifestDir2, `.manifest.${process.pid}.${Date.now()}.${crypto7.randomUUID()}.tmp`);
24625
+ const tempPath = path31.join(manifestDir2, `.manifest.${process.pid}.${Date.now()}.${crypto7.randomUUID()}.tmp`);
22862
24626
  try {
22863
24627
  fs7.writeFileSync(tempPath, `${JSON.stringify(manifest, null, 2)}
22864
24628
  `, { flag: "wx" });
@@ -22873,7 +24637,7 @@ function saveManifestUnlocked(manifest, baseDir) {
22873
24637
  }
22874
24638
  function withManifestLock(baseDir, operation) {
22875
24639
  const lockDir = `${getManifestPath(baseDir)}.lock`;
22876
- fs7.mkdirSync(path30.dirname(lockDir), { recursive: true });
24640
+ fs7.mkdirSync(path31.dirname(lockDir), { recursive: true });
22877
24641
  const lockOwner = acquireManifestLock(lockDir);
22878
24642
  try {
22879
24643
  return operation();
@@ -22899,7 +24663,7 @@ function acquireManifestLock(lockDir) {
22899
24663
  try {
22900
24664
  fs7.mkdirSync(lockDir, { recursive: false });
22901
24665
  try {
22902
- fs7.writeFileSync(path30.join(lockDir, "owner"), `${owner}
24666
+ fs7.writeFileSync(path31.join(lockDir, "owner"), `${owner}
22903
24667
  `, { flag: "wx" });
22904
24668
  } catch (error) {
22905
24669
  fs7.rmSync(lockDir, { recursive: true, force: true });
@@ -22929,7 +24693,7 @@ function acquireManifestLock(lockDir) {
22929
24693
  }
22930
24694
  function releaseManifestLock(lockDir, owner) {
22931
24695
  try {
22932
- const ownerPath = path30.join(lockDir, "owner");
24696
+ const ownerPath = path31.join(lockDir, "owner");
22933
24697
  if (fs7.readFileSync(ownerPath, "utf8").trim() === owner) {
22934
24698
  fs7.rmSync(lockDir, { recursive: true, force: true });
22935
24699
  }
@@ -22945,7 +24709,7 @@ function removeStaleManifestLock(lockDir) {
22945
24709
  try {
22946
24710
  fs7.mkdirSync(reclaimDir, { recursive: false });
22947
24711
  try {
22948
- fs7.writeFileSync(path30.join(reclaimDir, "owner"), `${reclaimOwner}
24712
+ fs7.writeFileSync(path31.join(reclaimDir, "owner"), `${reclaimOwner}
22949
24713
  `, { flag: "wx" });
22950
24714
  } catch (error) {
22951
24715
  fs7.rmSync(reclaimDir, { recursive: true, force: true });
@@ -23020,7 +24784,7 @@ function readManifestLockSnapshot(lockDir) {
23020
24784
  throw error;
23021
24785
  }
23022
24786
  try {
23023
- const owner = fs7.readFileSync(path30.join(lockDir, "owner"), "utf8").trim();
24787
+ const owner = fs7.readFileSync(path31.join(lockDir, "owner"), "utf8").trim();
23024
24788
  return { mtimeMs: stat5.mtimeMs, owner };
23025
24789
  } catch (error) {
23026
24790
  if (error.code === "ENOENT") {
@@ -23092,8 +24856,8 @@ function sleepSync(ms) {
23092
24856
  }
23093
24857
  function createPersonalSpace(baseDir, memoryDirOverride) {
23094
24858
  const homeDir = baseDir ?? resolveHomeDir();
23095
- const standalonePath = path30.join(homeDir, ".engram", "memory");
23096
- const openclawPath = path30.join(homeDir, ".openclaw", "workspace", "memory", "local");
24859
+ const standalonePath = path31.join(homeDir, ".engram", "memory");
24860
+ const openclawPath = path31.join(homeDir, ".openclaw", "workspace", "memory", "local");
23097
24861
  const memoryDir = memoryDirOverride ?? readEnvVar("REMNIC_MEMORY_DIR") ?? readEnvVar("ENGRAM_MEMORY_DIR") ?? (fs7.existsSync(standalonePath) ? standalonePath : fs7.existsSync(openclawPath) ? openclawPath : standalonePath);
23098
24862
  const normalizedMemoryDir = normalizeSpaceMemoryDir(memoryDir);
23099
24863
  const now = (/* @__PURE__ */ new Date()).toISOString();
@@ -23122,7 +24886,7 @@ function createSpace(options) {
23122
24886
  const id = options.name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-");
23123
24887
  const now = (/* @__PURE__ */ new Date()).toISOString();
23124
24888
  const memoryDir = normalizeSpaceMemoryDir(
23125
- options.memoryDir ?? path30.join(getSpacesDir(options.baseDir), id, "memory")
24889
+ options.memoryDir ?? path31.join(getSpacesDir(options.baseDir), id, "memory")
23126
24890
  );
23127
24891
  const space = updateManifest(options.baseDir, (manifest) => {
23128
24892
  if (manifest.spaces.some((s) => s.id === id)) {
@@ -23343,14 +25107,14 @@ async function mergeSpaces(sourceSpaceId, targetSpaceId, options) {
23343
25107
  };
23344
25108
  }
23345
25109
  function getAuditLog(baseDir) {
23346
- const auditPath = path30.join(getSpacesDir(baseDir), "audit.jsonl");
25110
+ const auditPath = path31.join(getSpacesDir(baseDir), "audit.jsonl");
23347
25111
  if (!fs7.existsSync(auditPath)) return [];
23348
25112
  const lines = fs7.readFileSync(auditPath, "utf8").trim().split("\n");
23349
25113
  return lines.filter((l) => l.trim()).map((l) => JSON.parse(l));
23350
25114
  }
23351
25115
  function appendAudit(entry, baseDir) {
23352
- const auditPath = path30.join(getSpacesDir(baseDir), "audit.jsonl");
23353
- fs7.mkdirSync(path30.dirname(auditPath), { recursive: true });
25116
+ const auditPath = path31.join(getSpacesDir(baseDir), "audit.jsonl");
25117
+ fs7.mkdirSync(path31.dirname(auditPath), { recursive: true });
23354
25118
  const full = {
23355
25119
  id: crypto7.randomUUID(),
23356
25120
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -23371,11 +25135,11 @@ async function copyMemories(sourceDir, targetDir, options) {
23371
25135
  sourceFiles.filter((p) => {
23372
25136
  const rp = safeRealpath(p);
23373
25137
  if (!rp || !isPathInsideRoot(rp, sourceRoot)) return false;
23374
- const rel = path30.relative(sourceRoot, rp);
23375
- const mapped = path30.resolve(targetRoot, rel);
25138
+ const rel = path31.relative(sourceRoot, rp);
25139
+ const mapped = path31.resolve(targetRoot, rel);
23376
25140
  if (!isPathInsideRoot(mapped, targetRoot)) return false;
23377
25141
  return classifyEntityRefWritePath(targetRoot, mapped) === "entity";
23378
- }).map((p) => path30.basename(p, ".md"))
25142
+ }).map((p) => path31.basename(p, ".md"))
23379
25143
  );
23380
25144
  const firstEntityId = sourceEntityIds.values().next().value;
23381
25145
  if (firstEntityId === void 0) {
@@ -23383,7 +25147,7 @@ async function copyMemories(sourceDir, targetDir, options) {
23383
25147
  }
23384
25148
  return withRawEntityPageMutation(
23385
25149
  targetRoot,
23386
- path30.join(targetRoot, "entities", `${firstEntityId}.md`),
25150
+ path31.join(targetRoot, "entities", `${firstEntityId}.md`),
23387
25151
  async () => copyPreparedMemories(
23388
25152
  sourceFiles,
23389
25153
  sourceRoot,
@@ -23410,14 +25174,14 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
23410
25174
  continue;
23411
25175
  }
23412
25176
  const content = fs7.readFileSync(sourcePath, "utf8");
23413
- const relativePath = path30.relative(sourceRoot, sourceRealPath);
23414
- const targetPath = path30.resolve(targetRoot, relativePath);
25177
+ const relativePath = path31.relative(sourceRoot, sourceRealPath);
25178
+ const targetPath = path31.resolve(targetRoot, relativePath);
23415
25179
  if (!isPathInsideRoot(targetPath, targetRoot)) {
23416
25180
  skipped++;
23417
25181
  continue;
23418
25182
  }
23419
25183
  const targetKind = classifyEntityRefWritePath(targetRoot, targetPath);
23420
- const idsAtCompare = historicalIdCache4.get(path30.join(targetRoot, "state"));
25184
+ const idsAtCompare = historicalIdCache4.get(path31.join(targetRoot, "state"));
23421
25185
  const sourceRef = targetKind === "memory" ? readEntityRefFromFrontmatter(content) : null;
23422
25186
  const refHasSourceClaimant = sourceRef !== null && sourceEntityIds.has(sourceRef);
23423
25187
  const canonicalContent = targetKind === "memory" && !refHasSourceClaimant ? canonicalizeEntityRefFrontmatter(content, idsAtCompare) : content;
@@ -23444,7 +25208,7 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
23444
25208
  if (fs7.existsSync(targetPath) && !options?.force) {
23445
25209
  const targetContent = fs7.readFileSync(targetPath, "utf8");
23446
25210
  const targetHash = hashContent4(targetContent);
23447
- if (hashContent4(canonicalContent) === targetHash && historicalIdCache4.get(path30.join(targetRoot, "state")) === idsAtCompare) {
25211
+ if (hashContent4(canonicalContent) === targetHash && historicalIdCache4.get(path31.join(targetRoot, "state")) === idsAtCompare) {
23448
25212
  skipped++;
23449
25213
  continue;
23450
25214
  }
@@ -23460,13 +25224,13 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
23460
25224
  continue;
23461
25225
  }
23462
25226
  }
23463
- fs7.mkdirSync(path30.dirname(targetPath), { recursive: true });
23464
- const targetParentRealPath = safeRealpath(path30.dirname(targetPath));
25227
+ fs7.mkdirSync(path31.dirname(targetPath), { recursive: true });
25228
+ const targetParentRealPath = safeRealpath(path31.dirname(targetPath));
23465
25229
  if (!targetParentRealPath || !isPathInsideRoot(targetParentRealPath, targetRoot)) {
23466
25230
  skipped++;
23467
25231
  continue;
23468
25232
  }
23469
- const stateDir = path30.join(targetRoot, "state");
25233
+ const stateDir = path31.join(targetRoot, "state");
23470
25234
  if (targetKind === "memory" && !refHasSourceClaimant) {
23471
25235
  const idsAtWrite = historicalIdCache4.get(stateDir);
23472
25236
  const written = canonicalizeEntityRefFrontmatter(content, idsAtWrite);
@@ -23510,7 +25274,7 @@ function copyPreparedMemories(sourceFiles, sourceRoot, targetRoot, sourceEntityI
23510
25274
  bumpMemoryCorpusVersionForDir(targetRoot);
23511
25275
  }
23512
25276
  if (reconcileNeeded) {
23513
- requestEntityCanonicalIdReconcileSync(path30.join(targetRoot, "state"));
25277
+ requestEntityCanonicalIdReconcileSync(path31.join(targetRoot, "state"));
23514
25278
  }
23515
25279
  return { merged, conflicts, skipped };
23516
25280
  }
@@ -23521,7 +25285,7 @@ function walkMd2(dir) {
23521
25285
  const results = [];
23522
25286
  function walk(d) {
23523
25287
  for (const entry of fs7.readdirSync(d, { withFileTypes: true })) {
23524
- const fullPath = path30.join(d, entry.name);
25288
+ const fullPath = path31.join(d, entry.name);
23525
25289
  if (entry.isSymbolicLink()) {
23526
25290
  continue;
23527
25291
  }
@@ -23550,8 +25314,8 @@ function safeRealpath(filePath) {
23550
25314
  }
23551
25315
  }
23552
25316
  function isPathInsideRoot(candidatePath, rootPath) {
23553
- const relative = path30.relative(rootPath, candidatePath);
23554
- return relative === "" || !relative.startsWith("..") && !path30.isAbsolute(relative);
25317
+ const relative = path31.relative(rootPath, candidatePath);
25318
+ return relative === "" || !relative.startsWith("..") && !path31.isAbsolute(relative);
23555
25319
  }
23556
25320
  function parseSimpleFrontmatter(content) {
23557
25321
  const match = content.match(/^---\n([\s\S]*?)\n---/);
@@ -23672,7 +25436,7 @@ var REMNIC_RECALL_DECISION_RULES = `## When to Use Recall vs Direct Read
23672
25436
  // src/memory-extension/codex-publisher.ts
23673
25437
  import fs8 from "fs";
23674
25438
  import os from "os";
23675
- import path31 from "path";
25439
+ import path32 from "path";
23676
25440
  var REMNIC_EXTENSION_DIR_NAME = "remnic";
23677
25441
  function resolveEnvHome(env) {
23678
25442
  if (env === void 0) return resolveHomeDir();
@@ -23681,12 +25445,12 @@ function resolveEnvHome(env) {
23681
25445
  function expandTildeWithHome(input, homeDir) {
23682
25446
  if (input === "~") return homeDir;
23683
25447
  if (input.startsWith("~/") || input.startsWith("~\\")) {
23684
- return path31.join(homeDir, input.slice(2));
25448
+ return path32.join(homeDir, input.slice(2));
23685
25449
  }
23686
25450
  return input;
23687
25451
  }
23688
25452
  function normalizeHostRoot(input, homeDir) {
23689
- return path31.resolve(expandTildeWithHome(input.trim(), homeDir));
25453
+ return path32.resolve(expandTildeWithHome(input.trim(), homeDir));
23690
25454
  }
23691
25455
  var CodexMemoryExtensionPublisher = class {
23692
25456
  hostId = "codex";
@@ -23700,12 +25464,12 @@ var CodexMemoryExtensionPublisher = class {
23700
25464
  async resolveExtensionRoot(env) {
23701
25465
  const homeDir = resolveEnvHome(env);
23702
25466
  const codexHomeInput = env === void 0 ? readEnvVar("CODEX_HOME")?.trim() : env.CODEX_HOME?.trim();
23703
- const codexHome = codexHomeInput ? normalizeHostRoot(codexHomeInput, homeDir) : path31.resolve(homeDir, ".codex");
23704
- return path31.join(codexHome, "memories_extensions", REMNIC_EXTENSION_DIR_NAME);
25467
+ const codexHome = codexHomeInput ? normalizeHostRoot(codexHomeInput, homeDir) : path32.resolve(homeDir, ".codex");
25468
+ return path32.join(codexHome, "memories_extensions", REMNIC_EXTENSION_DIR_NAME);
23705
25469
  }
23706
25470
  async isHostAvailable() {
23707
25471
  try {
23708
- const home = readEnvVar("CODEX_HOME")?.trim() || path31.join(resolveHomeDir(), ".codex");
25472
+ const home = readEnvVar("CODEX_HOME")?.trim() || path32.join(resolveHomeDir(), ".codex");
23709
25473
  return fs8.existsSync(home);
23710
25474
  } catch {
23711
25475
  return false;
@@ -23756,7 +25520,7 @@ When running inside the Codex phase-2 consolidation sandbox:
23756
25520
  }
23757
25521
  async publish(ctx) {
23758
25522
  const extensionRoot = await this.resolveExtensionRoot();
23759
- const instructionsPath = path31.join(extensionRoot, "instructions.md");
25523
+ const instructionsPath = path32.join(extensionRoot, "instructions.md");
23760
25524
  const filesWritten = [];
23761
25525
  const skipped = [];
23762
25526
  ctx.log.info(`Publishing Codex memory extension to ${extensionRoot}`);
@@ -24130,9 +25894,9 @@ async function generateActivityMemories(date, digestBody, config, deps) {
24130
25894
  }
24131
25895
 
24132
25896
  // src/session-summaries/index.ts
24133
- import { createHash as createHash7 } from "crypto";
24134
- import { lstat as lstat3, mkdir as mkdir7, readFile as readFile6, readdir as readdir4, rename, rm, stat as stat4, writeFile as writeFile7 } from "fs/promises";
24135
- import path32 from "path";
25897
+ import { createHash as createHash9 } from "crypto";
25898
+ import { lstat as lstat3, mkdir as mkdir7, readFile as readFile7, readdir as readdir4, rename, rm, stat as stat4, writeFile as writeFile7 } from "fs/promises";
25899
+ import path33 from "path";
24136
25900
 
24137
25901
  // src/session-summaries/adapters.ts
24138
25902
  var VALID_ROLES = /* @__PURE__ */ new Set(["user", "assistant", "tool", "system", "other"]);
@@ -24632,12 +26396,12 @@ var DEFAULT_MAX_FILES = 5e3;
24632
26396
  var DEFAULT_MAX_SESSIONS = 500;
24633
26397
  var SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".json", ".jsonl"]);
24634
26398
  function shortHash(value, length = 16) {
24635
- return createHash7("sha256").update(value).digest("hex").slice(0, length);
26399
+ return createHash9("sha256").update(value).digest("hex").slice(0, length);
24636
26400
  }
24637
26401
  async function listTranscriptFiles(root, maxFiles) {
24638
26402
  const out = [];
24639
26403
  let truncated = false;
24640
- const entrySortKey = (entryName, isDirectory) => isDirectory ? `${entryName}${path32.sep}` : entryName;
26404
+ const entrySortKey = (entryName, isDirectory) => isDirectory ? `${entryName}${path33.sep}` : entryName;
24641
26405
  async function visit(dir) {
24642
26406
  if (truncated) return;
24643
26407
  const entries = (await readdir4(dir, { withFileTypes: true })).sort(
@@ -24647,13 +26411,13 @@ async function listTranscriptFiles(root, maxFiles) {
24647
26411
  if (truncated) return;
24648
26412
  if (entry.name.startsWith(".")) continue;
24649
26413
  if (entry.isSymbolicLink()) continue;
24650
- const fullPath = path32.join(dir, entry.name);
26414
+ const fullPath = path33.join(dir, entry.name);
24651
26415
  if (entry.isDirectory()) {
24652
26416
  await visit(fullPath);
24653
26417
  continue;
24654
26418
  }
24655
26419
  if (!entry.isFile()) continue;
24656
- const ext = path32.extname(entry.name).toLowerCase();
26420
+ const ext = path33.extname(entry.name).toLowerCase();
24657
26421
  if (!SUPPORTED_EXTENSIONS.has(ext)) continue;
24658
26422
  out.push(fullPath);
24659
26423
  if (out.length > maxFiles) {
@@ -24680,7 +26444,7 @@ function normalizeInputDir(inputDir) {
24680
26444
  if (typeof inputDir !== "string" || inputDir.trim().length === 0) {
24681
26445
  throw new Error("inputDir must be a non-empty string");
24682
26446
  }
24683
- return path32.resolve(expandTildePath(inputDir.trim()));
26447
+ return path33.resolve(expandTildePath(inputDir.trim()));
24684
26448
  }
24685
26449
  function initRoleCounts() {
24686
26450
  return {
@@ -24826,7 +26590,7 @@ async function collectLocalSessionSummaries(options) {
24826
26590
  });
24827
26591
  }
24828
26592
  for (const filePath of files) {
24829
- const content = await readFile6(filePath, "utf-8");
26593
+ const content = await readFile7(filePath, "utf-8");
24830
26594
  const fileHash = shortHash(content, 24);
24831
26595
  if (seenFileHashes.has(fileHash)) {
24832
26596
  warnings.push({
@@ -24837,11 +26601,11 @@ async function collectLocalSessionSummaries(options) {
24837
26601
  continue;
24838
26602
  }
24839
26603
  seenFileHashes.add(fileHash);
24840
- const fileExtension = path32.extname(filePath).toLowerCase();
26604
+ const fileExtension = path33.extname(filePath).toLowerCase();
24841
26605
  const parsed = await adapter.parseFile(
24842
26606
  {
24843
26607
  content,
24844
- fileName: path32.basename(filePath),
26608
+ fileName: path33.basename(filePath),
24845
26609
  fileExtension,
24846
26610
  fileRef: fileHash
24847
26611
  },
@@ -24897,7 +26661,7 @@ async function collectLocalSessionSummaries(options) {
24897
26661
  }
24898
26662
  async function readRedactionConfig(pathLike) {
24899
26663
  if (!pathLike) return void 0;
24900
- const raw = await readFile6(path32.resolve(expandTildePath(pathLike)), "utf-8");
26664
+ const raw = await readFile7(path33.resolve(expandTildePath(pathLike)), "utf-8");
24901
26665
  const parsed = JSON.parse(raw);
24902
26666
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
24903
26667
  throw new Error("redaction config must be a JSON object");
@@ -24910,15 +26674,15 @@ function toJsonl(drafts) {
24910
26674
  }
24911
26675
  function defaultDraftOutputPath(memoryDir, generatedAt) {
24912
26676
  const stamp = generatedAt.replace(/[:.]/g, "-");
24913
- return path32.join(
24914
- path32.resolve(expandTildePath(memoryDir)),
26677
+ return path33.join(
26678
+ path33.resolve(expandTildePath(memoryDir)),
24915
26679
  "state",
24916
26680
  "session-summary-drafts",
24917
26681
  `session-summaries-${stamp}.jsonl`
24918
26682
  );
24919
26683
  }
24920
26684
  async function writeDrafts(filePath, drafts) {
24921
- await mkdir7(path32.dirname(filePath), { recursive: true });
26685
+ await mkdir7(path33.dirname(filePath), { recursive: true });
24922
26686
  const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
24923
26687
  try {
24924
26688
  await writeFile7(tempPath, toJsonl(drafts), "utf-8");
@@ -24936,7 +26700,7 @@ async function runLocalSessionSummaryCliCommand(options) {
24936
26700
  });
24937
26701
  const wroteFiles = [];
24938
26702
  if (options.output) {
24939
- const outputPath = path32.resolve(expandTildePath(options.output));
26703
+ const outputPath = path33.resolve(expandTildePath(options.output));
24940
26704
  await writeDrafts(outputPath, report.drafts);
24941
26705
  wroteFiles.push(outputPath);
24942
26706
  }
@@ -24970,20 +26734,20 @@ async function runLocalSessionSummaryCliCommand(options) {
24970
26734
  }
24971
26735
 
24972
26736
  // src/transfer/capsule-fork.ts
24973
- import { lstat as lstat4, mkdir as mkdir8, readFile as readFile7, realpath as realpath3, writeFile as writeFile8 } from "fs/promises";
24974
- import path33 from "path";
26737
+ import { lstat as lstat4, mkdir as mkdir8, readFile as readFile8, realpath as realpath3, writeFile as writeFile8 } from "fs/promises";
26738
+ import path34 from "path";
24975
26739
  async function forkCapsule(opts) {
24976
26740
  validateForkId(opts.forkId);
24977
- const rootAbs = path33.resolve(opts.targetRoot);
26741
+ const rootAbs = path34.resolve(opts.targetRoot);
24978
26742
  await assertIsDirectoryNotSymlink(rootAbs, "forkCapsule", "targetRoot");
24979
- const forkDirAbs = path33.join(rootAbs, "forks", opts.forkId);
26743
+ const forkDirAbs = path34.join(rootAbs, "forks", opts.forkId);
24980
26744
  const forkEntryExists = await pathEntryExists(forkDirAbs);
24981
26745
  if (forkEntryExists) {
24982
26746
  throw new Error(
24983
26747
  `forkCapsule: fork path already exists \u2014 forkId "${opts.forkId}" is already in use at: ${forkDirAbs}`
24984
26748
  );
24985
26749
  }
24986
- const archiveAbs = path33.resolve(opts.sourceArchive);
26750
+ const archiveAbs = path34.resolve(opts.sourceArchive);
24987
26751
  const importResult = await importCapsule({
24988
26752
  archivePath: archiveAbs,
24989
26753
  root: rootAbs,
@@ -25005,10 +26769,10 @@ async function forkCapsule(opts) {
25005
26769
  importedRecords: importResult.imported.length,
25006
26770
  skippedRecords: importResult.skipped.length
25007
26771
  };
25008
- const lineagePath = path33.join(forkDirAbs, "lineage.json");
26772
+ const lineagePath = path34.join(forkDirAbs, "lineage.json");
25009
26773
  const rootReal = await realpath3(rootAbs);
25010
26774
  await assertRealpathInsideRoot(rootReal, lineagePath, `forks/${opts.forkId}/lineage.json`, "forkCapsule");
25011
- await mkdir8(path33.dirname(lineagePath), { recursive: true });
26775
+ await mkdir8(path34.dirname(lineagePath), { recursive: true });
25012
26776
  await writeFile8(lineagePath, JSON.stringify(lineage, null, 2) + "\n", "utf-8");
25013
26777
  return {
25014
26778
  archivePath: archiveAbs,
@@ -25022,17 +26786,17 @@ async function readForkLineage(targetRoot, forkId) {
25022
26786
  if (typeof forkId !== "string" || forkId.length === 0 || forkId.length > 64 || !CAPSULE_ID_PATTERN.test(forkId)) {
25023
26787
  return null;
25024
26788
  }
25025
- const rootAbs = path33.resolve(targetRoot);
26789
+ const rootAbs = path34.resolve(targetRoot);
25026
26790
  const rootReal = await realpath3(rootAbs).catch(() => rootAbs);
25027
- const lineagePath = path33.join(rootReal, "forks", forkId, "lineage.json");
25028
- const rel = path33.relative(rootReal, lineagePath);
25029
- if (rel.startsWith("..") || path33.isAbsolute(rel)) {
26791
+ const lineagePath = path34.join(rootReal, "forks", forkId, "lineage.json");
26792
+ const rel = path34.relative(rootReal, lineagePath);
26793
+ if (rel.startsWith("..") || path34.isAbsolute(rel)) {
25030
26794
  return null;
25031
26795
  }
25032
26796
  if (!await isLineagePathContained(rootReal, lineagePath)) {
25033
26797
  return null;
25034
26798
  }
25035
- const raw = await readFile7(lineagePath, "utf-8").catch(() => null);
26799
+ const raw = await readFile8(lineagePath, "utf-8").catch(() => null);
25036
26800
  if (raw === null) return null;
25037
26801
  try {
25038
26802
  const parsed = JSON.parse(raw);
@@ -25067,19 +26831,19 @@ async function pathEntryExists(absPath) {
25067
26831
  async function isLineagePathContained(rootReal, lineagePath) {
25068
26832
  let existing = lineagePath;
25069
26833
  const suffix = [];
25070
- while (existing !== path33.dirname(existing)) {
26834
+ while (existing !== path34.dirname(existing)) {
25071
26835
  const st = await lstat4(existing).catch(() => null);
25072
26836
  if (st !== null) break;
25073
- suffix.unshift(path33.basename(existing));
25074
- existing = path33.dirname(existing);
26837
+ suffix.unshift(path34.basename(existing));
26838
+ existing = path34.dirname(existing);
25075
26839
  }
25076
26840
  const existingReal = await realpath3(existing).catch(() => existing);
25077
- const targetReal = suffix.length > 0 ? path33.join(existingReal, ...suffix) : existingReal;
25078
- const rel = path33.relative(rootReal, targetReal);
26841
+ const targetReal = suffix.length > 0 ? path34.join(existingReal, ...suffix) : existingReal;
26842
+ const rel = path34.relative(rootReal, targetReal);
25079
26843
  if (rel === "") return true;
25080
26844
  if (rel === "..") return false;
25081
- if (rel.startsWith(`..${path33.sep}`)) return false;
25082
- if (path33.isAbsolute(rel)) return false;
26845
+ if (rel.startsWith(`..${path34.sep}`)) return false;
26846
+ if (path34.isAbsolute(rel)) return false;
25083
26847
  return true;
25084
26848
  }
25085
26849
 
@@ -25198,4 +26962,4 @@ export {
25198
26962
  blendGraphExpandedRecallScore,
25199
26963
  Orchestrator
25200
26964
  };
25201
- //# sourceMappingURL=chunk-ZHY5TMJG.js.map
26965
+ //# sourceMappingURL=chunk-RMHMUUBW.js.map