@remnic/core 9.3.752 → 9.3.754

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 (331) hide show
  1. package/dist/access-admin-ops-surface.d.ts +194 -0
  2. package/dist/access-admin-ops-surface.js +128 -0
  3. package/dist/access-boundary.d.ts +5 -5
  4. package/dist/access-boundary.js +37 -37
  5. package/dist/access-cli.js +109 -109
  6. package/dist/access-http.d.ts +5 -5
  7. package/dist/access-http.js +46 -46
  8. package/dist/access-lcm-surface.d.ts +188 -0
  9. package/dist/access-lcm-surface.js +128 -0
  10. package/dist/access-lcm-surface.js.map +1 -0
  11. package/dist/access-mcp.d.ts +5 -5
  12. package/dist/access-mcp.js +42 -42
  13. package/dist/access-operations-batch.js +39 -39
  14. package/dist/access-operations.d.ts +8 -8
  15. package/dist/access-operations.js +41 -41
  16. package/dist/access-recall-surface.d.ts +5 -5
  17. package/dist/access-recall-surface.js +36 -36
  18. package/dist/access-schema.d.ts +54 -54
  19. package/dist/access-schema.js +5 -5
  20. package/dist/{access-service-C2cOHQvg.d.ts → access-service-DKRV1zxh.d.ts} +75 -159
  21. package/dist/access-service.d.ts +5 -5
  22. package/dist/access-service.js +40 -36
  23. package/dist/access-surface-catalog.d.ts +5 -5
  24. package/dist/action-confidence.d.ts +1 -1
  25. package/dist/active-memory-bridge.d.ts +1 -1
  26. package/dist/active-recall.d.ts +1 -1
  27. package/dist/active-recall.js +5 -5
  28. package/dist/adapters/index.js +7 -7
  29. package/dist/adapters/registry.js +3 -3
  30. package/dist/{auto-sync-IEBKPGAM.js → auto-sync-G4YKBPOU.js} +5 -5
  31. package/dist/behavior-learner.d.ts +1 -1
  32. package/dist/behavior-signals.d.ts +1 -1
  33. package/dist/bootstrap.d.ts +4 -4
  34. package/dist/briefing.d.ts +3 -3
  35. package/dist/briefing.js +13 -13
  36. package/dist/buffer-surprise-report.d.ts +1 -1
  37. package/dist/buffer.d.ts +3 -3
  38. package/dist/buffer.js +2 -2
  39. package/dist/calibration.d.ts +1 -1
  40. package/dist/calibration.js +2 -2
  41. package/dist/capabilities.d.ts +1 -1
  42. package/dist/{catalog-D_FvzTM0.d.ts → catalog-BEmNVRN0.d.ts} +1 -1
  43. package/dist/causal-behavior.d.ts +1 -1
  44. package/dist/causal-consolidation.d.ts +1 -1
  45. package/dist/causal-consolidation.js +16 -16
  46. package/dist/causal-trajectory-graph.d.ts +1 -1
  47. package/dist/{chunk-5DXUE7GB.js → chunk-24463PIV.js} +2 -2
  48. package/dist/{chunk-2DPCAW2T.js → chunk-2MHCWXX6.js} +8 -8
  49. package/dist/{chunk-KFNG72W7.js → chunk-2MR3MFQB.js} +6 -6
  50. package/dist/{chunk-YTU7WKYN.js → chunk-2OZ2JEUP.js} +2 -2
  51. package/dist/{chunk-JBPKEARU.js → chunk-2QSZNTDO.js} +7 -7
  52. package/dist/{chunk-EHYB325U.js → chunk-3DOD6ICJ.js} +2 -2
  53. package/dist/{chunk-5JAT4ZJD.js → chunk-4DLFJJOQ.js} +4 -4
  54. package/dist/{chunk-JNKSFIAY.js → chunk-4DVYRPLL.js} +76 -76
  55. package/dist/{chunk-VUA4S7P6.js → chunk-5J4OL7TJ.js} +4 -4
  56. package/dist/{chunk-BIAMJUUJ.js → chunk-5QANTTY2.js} +4 -4
  57. package/dist/{chunk-K5CWCJKJ.js → chunk-7AL5WNPW.js} +10 -10
  58. package/dist/{chunk-V4ZPLQLC.js → chunk-7CAJLUNW.js} +2 -2
  59. package/dist/{chunk-MEPWWSTD.js → chunk-BERWPOXU.js} +4 -4
  60. package/dist/{chunk-UFNQKZV7.js → chunk-BQ6J6P3K.js} +23 -23
  61. package/dist/{chunk-KT2VQLP7.js → chunk-CJ276MDI.js} +8 -8
  62. package/dist/{chunk-DC5OBZS3.js → chunk-COZEVVXZ.js} +2 -2
  63. package/dist/{chunk-67JRXV6Z.js → chunk-CXCZ4LAI.js} +5 -5
  64. package/dist/{chunk-MW7UUXTA.js → chunk-DBSMLRTO.js} +5 -5
  65. package/dist/{chunk-RBYV3WSB.js → chunk-DS4QM6I7.js} +4 -4
  66. package/dist/{chunk-MLDAD27I.js → chunk-G7LCG56O.js} +8 -8
  67. package/dist/{chunk-IBPEVGP5.js → chunk-GAH5755F.js} +2 -2
  68. package/dist/{chunk-SC5AEZLU.js → chunk-GPFXPC2V.js} +10 -10
  69. package/dist/{chunk-R7TGDLV3.js → chunk-HM6UJPPO.js} +4 -4
  70. package/dist/{chunk-2U4E34Y3.js → chunk-JBO6UBIE.js} +4 -4
  71. package/dist/{chunk-33NX5BLR.js → chunk-JRZV3767.js} +4 -4
  72. package/dist/{chunk-IQWFWSCP.js → chunk-JZ7P26GF.js} +4 -4
  73. package/dist/{chunk-XKAOT4ZB.js → chunk-K5LLP4CY.js} +4 -4
  74. package/dist/{chunk-JHZ3LEM7.js → chunk-KURUULFF.js} +2 -2
  75. package/dist/{chunk-VX2RYM2K.js → chunk-L4AS7TIG.js} +5644 -5529
  76. package/dist/chunk-L4AS7TIG.js.map +1 -0
  77. package/dist/{chunk-LQ6JI4VH.js → chunk-LD53WPMU.js} +4 -4
  78. package/dist/{chunk-RRRZ3CLN.js → chunk-LOPZ2DWR.js} +4 -4
  79. package/dist/{chunk-GFGYHLON.js → chunk-OH66EBCS.js} +2 -2
  80. package/dist/{chunk-OKOF7ITI.js → chunk-OSMBC3JU.js} +6 -6
  81. package/dist/{chunk-QMMCMV2L.js → chunk-PJQMISPD.js} +7 -7
  82. package/dist/{chunk-VQL22SHO.js → chunk-PYVZKEZZ.js} +130 -130
  83. package/dist/{chunk-DCWIQFNA.js → chunk-T422SYM6.js} +5 -5
  84. package/dist/{chunk-5CEJH5ZN.js → chunk-TAPSNIMA.js} +4 -4
  85. package/dist/{chunk-3NN73CM7.js → chunk-TH7WMHVK.js} +2 -2
  86. package/dist/{chunk-YGNQG4Q6.js → chunk-THZ4WR5L.js} +19 -19
  87. package/dist/{chunk-MTN2G4DT.js → chunk-TIFF32QK.js} +2 -2
  88. package/dist/{chunk-QRM3K3XT.js → chunk-TJATYB3F.js} +14 -14
  89. package/dist/{chunk-6ROCH5JV.js → chunk-TXALJRYO.js} +8 -8
  90. package/dist/{chunk-OVMLS7TN.js → chunk-U2SMR57B.js} +10 -10
  91. package/dist/{chunk-S5UYWK3O.js → chunk-V4C4SFZB.js} +6 -6
  92. package/dist/{chunk-L3VSTJ2V.js → chunk-VSU2NZGC.js} +2 -2
  93. package/dist/{chunk-GECEETQ3.js → chunk-W3M34XT4.js} +4 -4
  94. package/dist/{chunk-FXRFFYWD.js → chunk-WIWPSQYU.js} +4 -4
  95. package/dist/{chunk-FEELYTRT.js → chunk-WS6GLJ7P.js} +5 -5
  96. package/dist/{chunk-C4LNKQ3M.js → chunk-WUVUTOUV.js} +5 -5
  97. package/dist/{chunk-WRFKZEO6.js → chunk-YQGHW5ML.js} +4 -4
  98. package/dist/{chunk-J2JB6BHT.js → chunk-YRBXBHU7.js} +8 -8
  99. package/dist/{chunk-ZGGI3COG.js → chunk-ZJMSTFOR.js} +9 -9
  100. package/dist/{cli-DFUU9WYu.d.ts → cli-D-xw8Sp1.d.ts} +3 -3
  101. package/dist/cli.d.ts +6 -6
  102. package/dist/cli.js +73 -73
  103. package/dist/compounding/engine.d.ts +3 -3
  104. package/dist/compounding/engine.js +15 -15
  105. package/dist/compounding/preference-consolidator.d.ts +1 -1
  106. package/dist/compression-optimizer.d.ts +1 -1
  107. package/dist/config.d.ts +1 -1
  108. package/dist/config.js +2 -2
  109. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  110. package/dist/connectors/codex-materialize-runner.js +14 -14
  111. package/dist/connectors/codex-materialize.d.ts +1 -1
  112. package/dist/connectors/index.d.ts +1 -1
  113. package/dist/connectors/index.js +16 -16
  114. package/dist/consolidation-provenance-check.d.ts +1 -1
  115. package/dist/consolidation-undo.d.ts +1 -1
  116. package/dist/contradiction/index.d.ts +1 -1
  117. package/dist/contradiction/index.js +2 -2
  118. package/dist/{contradiction-scan-KO4WWCS4.js → contradiction-scan-2SODRNUF.js} +3 -3
  119. package/dist/contradiction-scan-2SODRNUF.js.map +1 -0
  120. package/dist/conversation-index/backend.d.ts +1 -1
  121. package/dist/conversation-index/backend.js +2 -2
  122. package/dist/conversation-index/chunker.d.ts +1 -1
  123. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  124. package/dist/conversation-index/indexer.d.ts +1 -1
  125. package/dist/conversation-index/search.d.ts +1 -1
  126. package/dist/day-summary.d.ts +1 -1
  127. package/dist/day-summary.js +2 -2
  128. package/dist/delinearize.d.ts +1 -1
  129. package/dist/direct-answer-wiring.d.ts +1 -1
  130. package/dist/direct-answer.d.ts +1 -1
  131. package/dist/embedding-fallback.d.ts +1 -1
  132. package/dist/embedding-fallback.js +2 -2
  133. package/dist/enrichment/index.d.ts +1 -1
  134. package/dist/entity-retrieval.d.ts +1 -1
  135. package/dist/entity-retrieval.js +14 -14
  136. package/dist/entity-schema.d.ts +1 -1
  137. package/dist/explicit-capture.d.ts +6 -6
  138. package/dist/explicit-capture.js +2 -2
  139. package/dist/extraction-faithfulness.d.ts +1 -1
  140. package/dist/extraction-faithfulness.js +3 -3
  141. package/dist/extraction-judge-telemetry.d.ts +1 -1
  142. package/dist/extraction-judge-training.d.ts +1 -1
  143. package/dist/extraction-judge.d.ts +1 -1
  144. package/dist/extraction-judge.js +3 -3
  145. package/dist/extraction.d.ts +1 -1
  146. package/dist/extraction.js +7 -7
  147. package/dist/fallback-llm.d.ts +1 -1
  148. package/dist/fallback-llm.js +2 -2
  149. package/dist/{first-start-migration-4I3VDSYS.js → first-start-migration-DY6YCRC2.js} +2 -2
  150. package/dist/{forget-WYLR4ZFV.js → forget-OEE5OELK.js} +2 -2
  151. package/dist/graph-dashboard-diff.d.ts +1 -1
  152. package/dist/graph-dashboard-key.d.ts +1 -1
  153. package/dist/graph-dashboard-parser.d.ts +1 -1
  154. package/dist/graph-edge-reinforcement.d.ts +1 -1
  155. package/dist/graph-snapshot.d.ts +1 -1
  156. package/dist/graph.d.ts +1 -1
  157. package/dist/identity-continuity.d.ts +1 -1
  158. package/dist/importance.d.ts +1 -1
  159. package/dist/index.d.ts +1284 -1284
  160. package/dist/index.js +204 -204
  161. package/dist/intent.d.ts +1 -1
  162. package/dist/lcm/engine.d.ts +1 -1
  163. package/dist/lcm/engine.js +3 -3
  164. package/dist/lcm/index.d.ts +1 -1
  165. package/dist/lcm/index.js +8 -8
  166. package/dist/lcm/tools.d.ts +1 -1
  167. package/dist/lifecycle.d.ts +1 -1
  168. package/dist/live-connectors-runner.d.ts +1 -1
  169. package/dist/local-llm.d.ts +1 -1
  170. package/dist/local-llm.js +2 -2
  171. package/dist/local-model-endpoint.d.ts +1 -1
  172. package/dist/maintenance/memory-governance.d.ts +1 -1
  173. package/dist/maintenance/memory-governance.js +13 -13
  174. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +13 -13
  175. package/dist/maintenance/rebuild-memory-projection.js +14 -14
  176. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  177. package/dist/memory-action-policy.d.ts +1 -1
  178. package/dist/memory-cache.d.ts +1 -1
  179. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  180. package/dist/memory-projection-store.d.ts +1 -1
  181. package/dist/memory-provenance.d.ts +1 -1
  182. package/dist/memory-worth-outcomes.d.ts +1 -1
  183. package/dist/models-json.d.ts +1 -1
  184. package/dist/namespaces/migrate.d.ts +2 -2
  185. package/dist/namespaces/migrate.js +26 -26
  186. package/dist/namespaces/principal.d.ts +1 -1
  187. package/dist/namespaces/search.d.ts +1 -1
  188. package/dist/namespaces/search.js +11 -11
  189. package/dist/namespaces/storage.d.ts +2 -2
  190. package/dist/namespaces/storage.js +15 -15
  191. package/dist/native-knowledge.d.ts +1 -1
  192. package/dist/operator-toolkit.d.ts +1 -1
  193. package/dist/operator-toolkit.js +33 -33
  194. package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
  195. package/dist/orchestration/compression-guideline-coordinator.js +2 -2
  196. package/dist/orchestration/maintenance.d.ts +2 -2
  197. package/dist/orchestration/maintenance.js +19 -19
  198. package/dist/{orchestrator-BhxMxtQJ.d.ts → orchestrator-D7ZGOQLX.d.ts} +77 -77
  199. package/dist/orchestrator.d.ts +4 -4
  200. package/dist/orchestrator.js +109 -109
  201. package/dist/patterns-cli.d.ts +1 -1
  202. package/dist/policy-runtime.d.ts +1 -1
  203. package/dist/provenance.d.ts +1 -1
  204. package/dist/qmd-recall-cache.d.ts +1 -1
  205. package/dist/qmd.d.ts +1 -1
  206. package/dist/recall-disclosure-escalation.d.ts +1 -1
  207. package/dist/recall-explain-renderer.d.ts +1 -1
  208. package/dist/recall-planner-llm.d.ts +1 -1
  209. package/dist/recall-planner-llm.js +2 -2
  210. package/dist/recall-state.d.ts +1 -1
  211. package/dist/recall-tag-filter.d.ts +1 -1
  212. package/dist/recall-xray-cli.d.ts +1 -1
  213. package/dist/recall-xray-renderer.d.ts +1 -1
  214. package/dist/recall-xray.d.ts +1 -1
  215. package/dist/resolve-auth-token.d.ts +1 -1
  216. package/dist/resume-bundles.js +2 -2
  217. package/dist/retrieval-agents.d.ts +1 -1
  218. package/dist/retrieval-tiers.d.ts +1 -1
  219. package/dist/routing/engine.d.ts +1 -1
  220. package/dist/routing/store.d.ts +1 -1
  221. package/dist/routing/store.js +2 -2
  222. package/dist/schemas.d.ts +42 -42
  223. package/dist/search/embed-helper.d.ts +1 -1
  224. package/dist/search/embed-helper.js +2 -2
  225. package/dist/search/factory.d.ts +1 -1
  226. package/dist/search/factory.js +10 -10
  227. package/dist/search/index.d.ts +1 -1
  228. package/dist/search/index.js +14 -14
  229. package/dist/search/lancedb-backend.d.ts +1 -1
  230. package/dist/search/lancedb-backend.js +2 -2
  231. package/dist/search/meilisearch-backend.d.ts +1 -1
  232. package/dist/search/meilisearch-backend.js +2 -2
  233. package/dist/search/noop-backend.d.ts +1 -1
  234. package/dist/search/orama-backend.d.ts +1 -1
  235. package/dist/search/orama-backend.js +2 -2
  236. package/dist/search/port.d.ts +1 -1
  237. package/dist/search/remote-backend.d.ts +1 -1
  238. package/dist/{semantic-consolidation-BrsoFeMP.d.ts → semantic-consolidation-7Sndms2M.d.ts} +1 -1
  239. package/dist/semantic-consolidation.d.ts +2 -2
  240. package/dist/semantic-consolidation.js +16 -16
  241. package/dist/semantic-rule-promotion.js +13 -13
  242. package/dist/semantic-rule-verifier.d.ts +1 -1
  243. package/dist/semantic-rule-verifier.js +13 -13
  244. package/dist/session-observer-bands.d.ts +1 -1
  245. package/dist/session-observer-state.d.ts +1 -1
  246. package/dist/shared-context/manager.d.ts +1 -1
  247. package/dist/shared-context/manager.js +2 -2
  248. package/dist/signal.d.ts +1 -1
  249. package/dist/storage.d.ts +1 -1
  250. package/dist/storage.js +12 -12
  251. package/dist/summarizer.d.ts +1 -1
  252. package/dist/summarizer.js +5 -5
  253. package/dist/summary-snapshot.d.ts +1 -1
  254. package/dist/temporal-supersession.d.ts +1 -1
  255. package/dist/temporal-validity.d.ts +1 -1
  256. package/dist/threading.d.ts +1 -1
  257. package/dist/tier-migration.d.ts +1 -1
  258. package/dist/tier-routing.d.ts +1 -1
  259. package/dist/{tier-stats-5XF4UBRQ.js → tier-stats-TG6GUZ6N.js} +4 -4
  260. package/dist/topics.d.ts +1 -1
  261. package/dist/transcript.d.ts +1 -1
  262. package/dist/transfer/backup.js +1 -1
  263. package/dist/transfer/capsule-export.js +1 -1
  264. package/dist/transfer/types.d.ts +44 -44
  265. package/dist/trust-score-stage.d.ts +1 -1
  266. package/dist/trust-score.d.ts +1 -1
  267. package/dist/{types-BfI4uJeb.d.ts → types-DTEFxFuL.d.ts} +1 -1
  268. package/dist/types.d.ts +1 -1
  269. package/dist/utility-runtime.d.ts +1 -1
  270. package/dist/verified-recall.js +13 -13
  271. package/package.json +2 -2
  272. package/src/access-admin-ops-surface.ts +828 -0
  273. package/src/access-lcm-surface.ts +517 -0
  274. package/src/access-service.ts +62 -1115
  275. package/dist/chunk-VX2RYM2K.js.map +0 -1
  276. /package/dist/{contradiction-scan-KO4WWCS4.js.map → access-admin-ops-surface.js.map} +0 -0
  277. /package/dist/{auto-sync-IEBKPGAM.js.map → auto-sync-G4YKBPOU.js.map} +0 -0
  278. /package/dist/{chunk-5DXUE7GB.js.map → chunk-24463PIV.js.map} +0 -0
  279. /package/dist/{chunk-2DPCAW2T.js.map → chunk-2MHCWXX6.js.map} +0 -0
  280. /package/dist/{chunk-KFNG72W7.js.map → chunk-2MR3MFQB.js.map} +0 -0
  281. /package/dist/{chunk-YTU7WKYN.js.map → chunk-2OZ2JEUP.js.map} +0 -0
  282. /package/dist/{chunk-JBPKEARU.js.map → chunk-2QSZNTDO.js.map} +0 -0
  283. /package/dist/{chunk-EHYB325U.js.map → chunk-3DOD6ICJ.js.map} +0 -0
  284. /package/dist/{chunk-5JAT4ZJD.js.map → chunk-4DLFJJOQ.js.map} +0 -0
  285. /package/dist/{chunk-JNKSFIAY.js.map → chunk-4DVYRPLL.js.map} +0 -0
  286. /package/dist/{chunk-VUA4S7P6.js.map → chunk-5J4OL7TJ.js.map} +0 -0
  287. /package/dist/{chunk-BIAMJUUJ.js.map → chunk-5QANTTY2.js.map} +0 -0
  288. /package/dist/{chunk-K5CWCJKJ.js.map → chunk-7AL5WNPW.js.map} +0 -0
  289. /package/dist/{chunk-V4ZPLQLC.js.map → chunk-7CAJLUNW.js.map} +0 -0
  290. /package/dist/{chunk-MEPWWSTD.js.map → chunk-BERWPOXU.js.map} +0 -0
  291. /package/dist/{chunk-UFNQKZV7.js.map → chunk-BQ6J6P3K.js.map} +0 -0
  292. /package/dist/{chunk-KT2VQLP7.js.map → chunk-CJ276MDI.js.map} +0 -0
  293. /package/dist/{chunk-DC5OBZS3.js.map → chunk-COZEVVXZ.js.map} +0 -0
  294. /package/dist/{chunk-67JRXV6Z.js.map → chunk-CXCZ4LAI.js.map} +0 -0
  295. /package/dist/{chunk-MW7UUXTA.js.map → chunk-DBSMLRTO.js.map} +0 -0
  296. /package/dist/{chunk-RBYV3WSB.js.map → chunk-DS4QM6I7.js.map} +0 -0
  297. /package/dist/{chunk-MLDAD27I.js.map → chunk-G7LCG56O.js.map} +0 -0
  298. /package/dist/{chunk-IBPEVGP5.js.map → chunk-GAH5755F.js.map} +0 -0
  299. /package/dist/{chunk-SC5AEZLU.js.map → chunk-GPFXPC2V.js.map} +0 -0
  300. /package/dist/{chunk-R7TGDLV3.js.map → chunk-HM6UJPPO.js.map} +0 -0
  301. /package/dist/{chunk-2U4E34Y3.js.map → chunk-JBO6UBIE.js.map} +0 -0
  302. /package/dist/{chunk-33NX5BLR.js.map → chunk-JRZV3767.js.map} +0 -0
  303. /package/dist/{chunk-IQWFWSCP.js.map → chunk-JZ7P26GF.js.map} +0 -0
  304. /package/dist/{chunk-XKAOT4ZB.js.map → chunk-K5LLP4CY.js.map} +0 -0
  305. /package/dist/{chunk-JHZ3LEM7.js.map → chunk-KURUULFF.js.map} +0 -0
  306. /package/dist/{chunk-LQ6JI4VH.js.map → chunk-LD53WPMU.js.map} +0 -0
  307. /package/dist/{chunk-RRRZ3CLN.js.map → chunk-LOPZ2DWR.js.map} +0 -0
  308. /package/dist/{chunk-GFGYHLON.js.map → chunk-OH66EBCS.js.map} +0 -0
  309. /package/dist/{chunk-OKOF7ITI.js.map → chunk-OSMBC3JU.js.map} +0 -0
  310. /package/dist/{chunk-QMMCMV2L.js.map → chunk-PJQMISPD.js.map} +0 -0
  311. /package/dist/{chunk-VQL22SHO.js.map → chunk-PYVZKEZZ.js.map} +0 -0
  312. /package/dist/{chunk-DCWIQFNA.js.map → chunk-T422SYM6.js.map} +0 -0
  313. /package/dist/{chunk-5CEJH5ZN.js.map → chunk-TAPSNIMA.js.map} +0 -0
  314. /package/dist/{chunk-3NN73CM7.js.map → chunk-TH7WMHVK.js.map} +0 -0
  315. /package/dist/{chunk-YGNQG4Q6.js.map → chunk-THZ4WR5L.js.map} +0 -0
  316. /package/dist/{chunk-MTN2G4DT.js.map → chunk-TIFF32QK.js.map} +0 -0
  317. /package/dist/{chunk-QRM3K3XT.js.map → chunk-TJATYB3F.js.map} +0 -0
  318. /package/dist/{chunk-6ROCH5JV.js.map → chunk-TXALJRYO.js.map} +0 -0
  319. /package/dist/{chunk-OVMLS7TN.js.map → chunk-U2SMR57B.js.map} +0 -0
  320. /package/dist/{chunk-S5UYWK3O.js.map → chunk-V4C4SFZB.js.map} +0 -0
  321. /package/dist/{chunk-L3VSTJ2V.js.map → chunk-VSU2NZGC.js.map} +0 -0
  322. /package/dist/{chunk-GECEETQ3.js.map → chunk-W3M34XT4.js.map} +0 -0
  323. /package/dist/{chunk-FXRFFYWD.js.map → chunk-WIWPSQYU.js.map} +0 -0
  324. /package/dist/{chunk-FEELYTRT.js.map → chunk-WS6GLJ7P.js.map} +0 -0
  325. /package/dist/{chunk-C4LNKQ3M.js.map → chunk-WUVUTOUV.js.map} +0 -0
  326. /package/dist/{chunk-WRFKZEO6.js.map → chunk-YQGHW5ML.js.map} +0 -0
  327. /package/dist/{chunk-J2JB6BHT.js.map → chunk-YRBXBHU7.js.map} +0 -0
  328. /package/dist/{chunk-ZGGI3COG.js.map → chunk-ZJMSTFOR.js.map} +0 -0
  329. /package/dist/{first-start-migration-4I3VDSYS.js.map → first-start-migration-DY6YCRC2.js.map} +0 -0
  330. /package/dist/{forget-WYLR4ZFV.js.map → forget-OEE5OELK.js.map} +0 -0
  331. /package/dist/{tier-stats-5XF4UBRQ.js.map → tier-stats-TG6GUZ6N.js.map} +0 -0
@@ -251,6 +251,8 @@ import {
251
251
  import { formatProfileTraceAscii } from "./profiling.js";
252
252
  import { resolveAccessSetupCapabilities, resolveGraphConstructionCapabilities, resolveIndexingCapabilities } from "./capabilities.js";
253
253
  import { resolveRecallEnhancementCapabilities } from "./capabilities.js";
254
+ import { AccessLcmSurface } from "./access-lcm-surface.js";
255
+ import { AccessAdminOpsSurface } from "./access-admin-ops-surface.js";
254
256
  import { AccessRecallSurface } from "./access-recall-surface.js";
255
257
  import { selfDeps } from "./orchestration/self-deps.js";
256
258
 
@@ -302,12 +304,12 @@ function qmdResultPathCandidates(
302
304
  return [...candidates];
303
305
  }
304
306
 
305
- type AccessProfilingReportRequest = {
307
+ export type AccessProfilingReportRequest = {
306
308
  format?: string;
307
309
  limit?: number;
308
310
  };
309
311
 
310
- type AccessProfilingReportResponse = {
312
+ export type AccessProfilingReportResponse = {
311
313
  enabled: boolean;
312
314
  format?: "ascii" | "json";
313
315
  report?: string;
@@ -860,7 +862,7 @@ export interface EngramAccessOfflineSyncApplyResponse extends OfflineSyncApplyCh
860
862
  export type EngramAccessActionConfidenceRequest = ActionConfidenceInput;
861
863
  export type EngramAccessActionConfidenceResponse = ActionConfidenceResult;
862
864
 
863
- async function buildProjectedGovernanceProposedActions(
865
+ export async function buildProjectedGovernanceProposedActions(
864
866
  storage: Awaited<ReturnType<Orchestrator["getStorage"]>>,
865
867
  projected: NonNullable<Awaited<ReturnType<Awaited<ReturnType<Orchestrator["getStorage"]>>["getProjectedGovernanceRecord"]>>>,
866
868
  ): Promise<Awaited<ReturnType<typeof readMemoryGovernanceRunArtifact>>["appliedActions"]> {
@@ -879,7 +881,7 @@ async function buildProjectedGovernanceProposedActions(
879
881
  return buildProposedActions(reviewQueue, memories);
880
882
  }
881
883
 
882
- function hasGroupedGovernanceActions(
884
+ export function hasGroupedGovernanceActions(
883
885
  grouped?: Awaited<ReturnType<typeof readMemoryGovernanceRunArtifact>>["transitionReport"]["proposed"],
884
886
  ): boolean {
885
887
  if (!grouped) return false;
@@ -1307,6 +1309,30 @@ export class EngramAccessService {
1307
1309
  private readonly budget: CrossNamespaceBudget;
1308
1310
  private readonly auditAdapter: AccessAuditAdapter | null;
1309
1311
 
1312
+ /** AccessLcmSurface (access-service decomposition). Lazy; selfDeps live wiring. */
1313
+ private _accessLcmSurface: AccessLcmSurface | undefined;
1314
+
1315
+ private get accessLcmSurface(): AccessLcmSurface {
1316
+ if (!this._accessLcmSurface) {
1317
+ this._accessLcmSurface = new AccessLcmSurface(
1318
+ selfDeps<ConstructorParameters<typeof AccessLcmSurface>[0]>(this),
1319
+ );
1320
+ }
1321
+ return this._accessLcmSurface;
1322
+ }
1323
+
1324
+ /** AccessAdminOpsSurface (access-service decomposition). Lazy; selfDeps live wiring. */
1325
+ private _accessAdminOpsSurface: AccessAdminOpsSurface | undefined;
1326
+
1327
+ private get accessAdminOpsSurface(): AccessAdminOpsSurface {
1328
+ if (!this._accessAdminOpsSurface) {
1329
+ this._accessAdminOpsSurface = new AccessAdminOpsSurface(
1330
+ selfDeps<ConstructorParameters<typeof AccessAdminOpsSurface>[0]>(this),
1331
+ );
1332
+ }
1333
+ return this._accessAdminOpsSurface;
1334
+ }
1335
+
1310
1336
  /** AccessRecallSurface (access-service decomposition). Lazy; selfDeps live wiring. */
1311
1337
  private _accessRecallSurface: AccessRecallSurface | undefined;
1312
1338
 
@@ -3835,97 +3861,11 @@ export class EngramAccessService {
3835
3861
  }
3836
3862
 
3837
3863
  async reviewQueue(runId?: string, namespace?: string, principal?: string): Promise<EngramAccessReviewQueueResponse> {
3838
- const resolvedNamespace = this.resolveReadableNamespace(namespace, principal);
3839
- const storage = await this.orchestrator.getStorage(resolvedNamespace);
3840
- const projected = await storage.getProjectedGovernanceRecord();
3841
- if (projected && (!runId || projected.runId === runId.trim())) {
3842
- const projectedAppliedActions = projected.appliedActionRows.map((row) => ({
3843
- action: row.action,
3844
- memoryId: row.memoryId,
3845
- reasonCode: row.reasonCode,
3846
- beforeStatus: row.beforeStatus,
3847
- afterStatus: row.afterStatus,
3848
- originalPath: row.originalPath,
3849
- currentPath: row.currentPath,
3850
- })) as Awaited<
3851
- ReturnType<typeof readMemoryGovernanceRunArtifact>
3852
- >["appliedActions"];
3853
- const projectedProposedActions = await buildProjectedGovernanceProposedActions(storage, projected);
3854
- const projectedArtifact = await (async () => {
3855
- try {
3856
- return await readMemoryGovernanceRunArtifact(storage.dir, projected.runId);
3857
- } catch {
3858
- return null;
3859
- }
3860
- })();
3861
- const metrics = projected.metrics as Awaited<ReturnType<typeof readMemoryGovernanceRunArtifact>>["metrics"];
3862
- const fallbackTransitionReport = {
3863
- proposed: groupActionsByStatus(projectedProposedActions),
3864
- applied: groupActionsByStatus(projectedAppliedActions),
3865
- };
3866
- const transitionReport = projectedArtifact?.transitionReport
3867
- ? {
3868
- proposed:
3869
- hasGroupedGovernanceActions(projectedArtifact.transitionReport.proposed) || projectedProposedActions.length === 0
3870
- ? projectedArtifact.transitionReport.proposed
3871
- : fallbackTransitionReport.proposed,
3872
- applied:
3873
- hasGroupedGovernanceActions(projectedArtifact.transitionReport.applied) || projectedAppliedActions.length === 0
3874
- ? projectedArtifact.transitionReport.applied
3875
- : fallbackTransitionReport.applied,
3876
- }
3877
- : fallbackTransitionReport;
3878
- const qualityScore = projectedArtifact?.qualityScore ?? metrics?.qualityScore ?? buildQualityScore(metrics?.reviewReasons ?? {
3879
- exact_duplicate: 0,
3880
- semantic_duplicate_candidate: 0,
3881
- disputed_memory: 0,
3882
- speculative_low_confidence: 0,
3883
- archive_candidate: 0,
3884
- explicit_capture_review: 0,
3885
- malformed_import: 0,
3886
- });
3887
- const effectiveMetrics = metrics ? { ...metrics, qualityScore: metrics.qualityScore ?? qualityScore } : metrics;
3888
-
3889
- return {
3890
- found: true,
3891
- namespace: resolvedNamespace,
3892
- runId: projected.runId,
3893
- summary: projected.summary as Awaited<ReturnType<typeof readMemoryGovernanceRunArtifact>>["summary"],
3894
- metrics: effectiveMetrics,
3895
- qualityScore,
3896
- reviewQueue: projected.reviewQueueRows.map((row) => ({
3897
- entryId: row.entryId,
3898
- memoryId: row.memoryId,
3899
- path: row.path,
3900
- reasonCode: row.reasonCode,
3901
- severity: row.severity,
3902
- suggestedAction: row.suggestedAction,
3903
- suggestedStatus: row.suggestedStatus,
3904
- relatedMemoryIds: row.relatedMemoryIds,
3905
- })) as Awaited<
3906
- ReturnType<typeof readMemoryGovernanceRunArtifact>
3907
- >["reviewQueue"],
3908
- appliedActions: projectedAppliedActions,
3909
- transitionReport,
3910
- report: projected.report,
3911
- };
3912
- }
3913
-
3914
- const resolvedRunId = runId?.trim() || (await listMemoryGovernanceRuns(storage.dir))[0];
3915
- if (!resolvedRunId) return { found: false, namespace: resolvedNamespace };
3916
- const artifact = await readMemoryGovernanceRunArtifact(storage.dir, resolvedRunId);
3917
- return {
3918
- found: true,
3919
- namespace: resolvedNamespace,
3920
- runId: resolvedRunId,
3921
- summary: artifact.summary,
3922
- metrics: artifact.metrics,
3923
- qualityScore: artifact.qualityScore,
3924
- reviewQueue: artifact.reviewQueue,
3925
- appliedActions: artifact.appliedActions,
3926
- transitionReport: artifact.transitionReport,
3927
- report: artifact.report,
3928
- };
3864
+ return this.accessAdminOpsSurface.reviewQueue(
3865
+ runId,
3866
+ namespace,
3867
+ principal,
3868
+ );
3929
3869
  }
3930
3870
 
3931
3871
  async maintenance(namespace?: string, principal?: string): Promise<EngramAccessMaintenanceResponse> {
@@ -4010,56 +3950,10 @@ export class EngramAccessService {
4010
3950
  summaryPath: string;
4011
3951
  reportPath: string;
4012
3952
  }> {
4013
- const deepSleep = this.orchestrator.config.dreamsPhases.deepSleep;
4014
- if (deepSleep.enabled === false && deepSleep.enabledExplicitlySet === true) {
4015
- throw new Error("memory governance is disabled by dreams.phases.deepSleep.enabled=false");
4016
- }
4017
- const resolvedNamespace = this.writableNamespaceFor(
4018
- request.namespace,
4019
- undefined,
4020
- request.authenticatedPrincipal ?? principal,
3953
+ return this.accessAdminOpsSurface.governanceRun(
3954
+ request,
3955
+ principal,
4021
3956
  );
4022
- const storage = await this.orchestrator.getStorage(resolvedNamespace);
4023
- const mode = request.mode === "apply" ? "apply" : "shadow";
4024
- const boundedBatchSize =
4025
- typeof request.batchSize === "number" && Number.isFinite(request.batchSize)
4026
- ? Math.max(1, Math.floor(request.batchSize))
4027
- : undefined;
4028
- const result = await runMemoryGovernance({
4029
- memoryDir: storage.dir,
4030
- mode,
4031
- recentDays:
4032
- typeof request.recentDays === "number" && Number.isFinite(request.recentDays)
4033
- ? Math.max(1, Math.floor(request.recentDays))
4034
- : undefined,
4035
- maxMemories:
4036
- typeof request.maxMemories === "number" && Number.isFinite(request.maxMemories)
4037
- ? Math.max(1, Math.floor(request.maxMemories))
4038
- : undefined,
4039
- batchSize: boundedBatchSize,
4040
- });
4041
- if (mode === "apply") {
4042
- try {
4043
- await this.orchestrator.processEntitySynthesisQueue(
4044
- resolvedNamespace,
4045
- Math.min(boundedBatchSize ?? 5, 5),
4046
- );
4047
- } catch (error) {
4048
- log.debug(`governanceRun: entity synthesis refresh failed after governance apply: ${error}`);
4049
- }
4050
- }
4051
-
4052
- return {
4053
- namespace: resolvedNamespace,
4054
- runId: result.runId,
4055
- traceId: result.traceId,
4056
- mode: result.mode,
4057
- reviewQueueCount: result.reviewQueue.length,
4058
- proposedActionCount: result.proposedActions.length,
4059
- appliedActionCount: result.appliedActions.length,
4060
- summaryPath: result.summaryPath,
4061
- reportPath: result.reportPath,
4062
- };
4063
3957
  }
4064
3958
 
4065
3959
  async procedureMiningRun(
@@ -4225,160 +4119,17 @@ export class EngramAccessService {
4225
4119
  reason?: string;
4226
4120
  retryAfterMs?: number;
4227
4121
  }> {
4228
- if (!resolveIndexingCapabilities(this.orchestrator.config).conversationIndex) {
4229
- return {
4230
- enabled: false,
4231
- sessions: 0,
4232
- chunks: 0,
4233
- skipped: 0,
4234
- skippedSessionKeys: [],
4235
- embeddedRuns: 0,
4236
- reason: "disabled",
4237
- };
4238
- }
4239
-
4240
- const hours =
4241
- typeof request.hours === "number" && Number.isFinite(request.hours)
4242
- ? Math.max(1, Math.floor(request.hours))
4243
- : 24;
4244
-
4245
- let sessionKey: string | undefined;
4246
- if (request.sessionKey !== undefined) {
4247
- if (typeof request.sessionKey !== "string" || request.sessionKey.trim().length === 0) {
4248
- throw new EngramAccessInputError("sessionKey must be a non-empty string when provided");
4249
- }
4250
- sessionKey = request.sessionKey.trim();
4251
- }
4252
-
4253
- if (sessionKey) {
4254
- const result = await this.orchestrator.updateConversationIndex(
4255
- sessionKey,
4256
- hours,
4257
- { embed: request.embed },
4258
- );
4259
- return {
4260
- enabled: true,
4261
- sessionKey,
4262
- sessions: 1,
4263
- chunks: result.chunks,
4264
- skipped: result.skipped ? 1 : 0,
4265
- skippedSessionKeys: result.skipped ? [sessionKey] : [],
4266
- embeddedRuns: result.embedded ? 1 : 0,
4267
- reason: result.reason,
4268
- retryAfterMs: result.retryAfterMs,
4269
- };
4270
- }
4271
-
4272
- const sessionKeys = await this.orchestrator.transcript.listSessionKeys();
4273
- let chunks = 0;
4274
- let skipped = 0;
4275
- const skippedSessionKeys: string[] = [];
4276
- let embeddedRuns = 0;
4277
-
4278
- for (const sessionKey of sessionKeys) {
4279
- const result = await this.orchestrator.updateConversationIndex(
4280
- sessionKey,
4281
- hours,
4282
- { embed: request.embed },
4283
- );
4284
- chunks += result.chunks;
4285
- if (result.skipped) {
4286
- skipped += 1;
4287
- skippedSessionKeys.push(sessionKey);
4288
- }
4289
- if (result.embedded) {
4290
- embeddedRuns += 1;
4291
- }
4292
- }
4293
-
4294
- return {
4295
- enabled: true,
4296
- sessions: sessionKeys.length,
4297
- chunks,
4298
- skipped,
4299
- skippedSessionKeys,
4300
- embeddedRuns,
4301
- };
4122
+ return this.accessAdminOpsSurface.conversationIndexUpdate(
4123
+ request,
4124
+ );
4302
4125
  }
4303
4126
 
4304
4127
  async profilingReport(
4305
4128
  request: AccessProfilingReportRequest = {},
4306
4129
  ): Promise<AccessProfilingReportResponse> {
4307
- const profiler = this.orchestrator.profiler;
4308
- if (!profiler.isEnabled) {
4309
- return {
4310
- enabled: false,
4311
- reason: "disabled",
4312
- message: "Profiling is disabled. Set profilingEnabled: true in your plugin config to enable.",
4313
- };
4314
- }
4315
-
4316
- const format = request.format ?? "ascii";
4317
- if (format !== "ascii" && format !== "json") {
4318
- throw new EngramAccessInputError("format must be one of: ascii, json");
4319
- }
4320
-
4321
- const limit = request.limit ?? 5;
4322
- if (!Number.isInteger(limit) || limit < 1 || limit > 20) {
4323
- throw new EngramAccessInputError("limit must be an integer between 1 and 20");
4324
- }
4325
-
4326
- const traces = profiler.getRecentTraces(limit);
4327
- const stats = profiler.getStats();
4328
- const bottleneck = profiler.identifyBottleneck();
4329
-
4330
- if (format === "json") {
4331
- return {
4332
- enabled: true,
4333
- format,
4334
- traces,
4335
- stats,
4336
- bottleneck,
4337
- };
4338
- }
4339
-
4340
- const lines: string[] = [];
4341
- lines.push("Engram Profiling Report");
4342
- lines.push("=".repeat(60));
4343
- lines.push("");
4344
-
4345
- type BucketEntry = { count: number; avgMs: number; p50Ms: number; p95Ms: number; maxMs: number };
4346
- const allBuckets: Array<[string, Record<string, BucketEntry>]> = [
4347
- ["byKind", stats.byKind],
4348
- ["bySpan", stats.bySpan],
4349
- ];
4350
- const hasStats = allBuckets.some(([, entries]) => Object.keys(entries).length > 0);
4351
- if (hasStats) {
4352
- lines.push("Aggregate Stats (all retained traces):");
4353
- for (const [bucket, entries] of allBuckets) {
4354
- for (const [key, summary] of Object.entries(entries)) {
4355
- lines.push(
4356
- ` ${bucket}/${key}: avg=${summary.avgMs}ms p50=${summary.p50Ms}ms p95=${summary.p95Ms}ms max=${summary.maxMs}ms (n=${summary.count})`,
4357
- );
4358
- }
4359
- }
4360
- lines.push("");
4361
- }
4362
-
4363
- if (bottleneck) {
4364
- lines.push(`Bottleneck: ${bottleneck}`);
4365
- lines.push("");
4366
- }
4367
-
4368
- if (traces.length === 0) {
4369
- lines.push("No traces recorded yet. Trigger a recall or extraction to see timing data.");
4370
- } else {
4371
- for (const trace of traces) {
4372
- lines.push(formatProfileTraceAscii(trace));
4373
- lines.push("");
4374
- }
4375
- }
4376
-
4377
- return {
4378
- enabled: true,
4379
- format,
4380
- report: lines.join("\n"),
4381
- };
4130
+ return this.accessAdminOpsSurface.profilingReport(
4131
+ request,
4132
+ );
4382
4133
  }
4383
4134
 
4384
4135
  async trustZoneStatus(namespace?: string, principal?: string): Promise<EngramAccessTrustZoneStatusResponse> {
@@ -4865,212 +4616,9 @@ export class EngramAccessService {
4865
4616
  }
4866
4617
 
4867
4618
  async lcmSearch(request: EngramAccessLcmSearchRequest): Promise<EngramAccessLcmSearchResponse> {
4868
- if (!request.query || typeof request.query !== "string" || request.query.trim().length === 0) {
4869
- throw new EngramAccessInputError("query is required and must be a non-empty string");
4870
- }
4871
-
4872
- const principal = this.resolveRequestPrincipal(request.sessionKey, request.authenticatedPrincipal);
4873
- const hasExplicitNamespace =
4874
- typeof request.namespace === "string" &&
4875
- request.namespace.trim().length > 0;
4876
- // Resolve the readable base namespace WITHOUT pre-authorizing `default`
4877
- // (#1505 thread NBHWz). An EXPLICIT namespace is still authorized strictly
4878
- // via `resolveReadableNamespace` (explicit reads must pass the ACL — throws
4879
- // on an unreadable explicit namespace). For an IMPLICIT read, derive the
4880
- // fallback from the ALREADY read-authorized recall namespace set instead of
4881
- // read-authorizing `config.defaultNamespace`: under a restrictive `default`
4882
- // READ policy where the principal's self namespace is readable, normal recall
4883
- // still succeeds via `recallNamespacesForPrincipal`, so `lcmSearch` must too
4884
- // (the same defect class the raw-excerpt path fixes). `undefined` ⇒ no
4885
- // readable LCM namespace exists, so return NO rows rather than throwing.
4886
- const profileLcmReadNamespaces = hasExplicitNamespace
4887
- ? null
4888
- : this.resolveScopeProfileLcmReadNamespaces(
4889
- request.sessionKey,
4890
- request.authenticatedPrincipal,
4891
- );
4892
- const namespace = hasExplicitNamespace
4893
- ? this.resolveReadableNamespace(request.namespace, principal)
4894
- : profileLcmReadNamespaces !== null
4895
- ? profileLcmReadNamespaces[0]
4896
- : this.resolveImplicitLcmReadFallbackNamespace(principal);
4897
-
4898
- if (!this.orchestrator.lcmEngine || !this.orchestrator.lcmEngine.enabled) {
4899
- return {
4900
- query: request.query,
4901
- namespace: namespace ?? this.orchestrator.config.defaultNamespace,
4902
- results: [],
4903
- count: 0,
4904
- lcmEnabled: false,
4905
- };
4906
- }
4907
-
4908
- // An active scope profile with no readable layers is authoritative: search
4909
- // no legacy/default LCM keys instead of falling back around the profile.
4910
- if (profileLcmReadNamespaces !== null && profileLcmReadNamespaces.length === 0) {
4911
- return {
4912
- query: request.query,
4913
- namespace: this.orchestrator.config.defaultNamespace,
4914
- results: [],
4915
- count: 0,
4916
- lcmEnabled: true,
4917
- };
4918
- }
4919
-
4920
- // No readable LCM namespace for an IMPLICIT read (restrictive `default` READ
4921
- // policy, no readable overlay/self) ⇒ return NO rows instead of pre-
4922
- // authorizing the denied default (#1505 thread NBHWz). Normal recall still
4923
- // succeeds through the readable self namespace; LCM search degrades to empty.
4924
- if (namespace === undefined) {
4925
- return {
4926
- query: request.query,
4927
- namespace: this.orchestrator.config.defaultNamespace,
4928
- results: [],
4929
- count: 0,
4930
- lcmEnabled: true,
4931
- };
4932
- }
4933
-
4934
- const limit = Math.max(1, Math.min(request.limit ?? 10, 100));
4935
- // Route the LCM read session_id AND prefix through the SAME overlay-aware
4936
- // namespace `observe`'s write key and compaction use (#1505 round 3). A
4937
- // project-scoped `observe` with no explicit namespace archived under the
4938
- // coding-overlay namespace; deriving the prefix only from the readable
4939
- // `namespace` (as before) would search the raw key and miss those turns.
4940
- // The effective namespace is resolved ONCE from the real session
4941
- // (`request.sessionKey`) — the prefix is a search fragment with no bound
4942
- // coding context, so it inherits the same namespace. Collapses to the raw key
4943
- // for single-store / no-overlay / explicit-default flows (existing behavior).
4944
- const lcmReadNamespace = profileLcmReadNamespaces !== null
4945
- ? profileLcmReadNamespaces[0] ?? this.orchestrator.config.defaultNamespace
4946
- : this.resolveLcmReadNamespace(
4947
- request.namespace,
4948
- namespace,
4949
- request.sessionKey,
4950
- request.authenticatedPrincipal,
4951
- );
4952
- // Ordered, read-authorized LCM read key SET for a concrete `sessionKey`
4953
- // (#1505 fallback unification + #1501 scope profiles). A branch-scoped
4954
- // session whose rows were archived at project/root scope is found by querying
4955
- // the primary overlay key first, then each fallback. When a scope profile is
4956
- // active, the profile's expanded read namespace set is authoritative,
4957
- // including the empty set.
4958
- const lcmSessionKeyIds = request.sessionKey
4959
- ? profileLcmReadNamespaces !== null
4960
- ? this.lcmSessionIdsForNamespaces(
4961
- profileLcmReadNamespaces,
4962
- request.sessionKey,
4963
- )
4964
- : this.resolveLcmReadSessionIds(
4965
- request.namespace,
4966
- namespace,
4967
- request.sessionKey,
4968
- request.authenticatedPrincipal,
4969
- )
4970
- : [undefined];
4971
- const lcmSessionPrefixes = request.sessionPrefix
4972
- ? profileLcmReadNamespaces !== null && !request.sessionKey
4973
- ? this.lcmSessionIdsForNamespaces(
4974
- profileLcmReadNamespaces,
4975
- request.sessionPrefix,
4976
- )
4977
- : [
4978
- lcmSessionKeyForNamespace(
4979
- lcmReadNamespace,
4980
- request.sessionPrefix,
4981
- this.orchestrator.config.defaultNamespace,
4982
- ) ?? request.sessionPrefix,
4983
- ]
4984
- : [undefined];
4985
- // SECURITY (#1495 P1 + codex P1 r2 "Require a scoped LCM filter before
4986
- // archive searches"): a sessionless, prefixless `lcmSearch` issues
4987
- // `searchContextFull(query, limit, undefined, undefined)`, an archive-wide
4988
- // FTS scan over EVERY `session_id`, including the sentinel-framed
4989
- // `<ns>`-scoped overlay/tenant rows. The LCM archive is keyed by the
4990
- // `session_id` STRING and is NOT partitioned by namespace, so an unscoped
4991
- // scan CANNOT be constrained to the caller's authorized namespace — neither
4992
- // an explicit `namespace` nor a readable `default` confines its results to
4993
- // rows the caller may read. The scan is therefore safe ONLY in single-store
4994
- // mode (namespaces disabled, one shared archive owned by the caller). When
4995
- // namespaces are ENABLED, an unscoped `lcmSearch` (no `sessionKey` AND no
4996
- // `sessionPrefix`) must be SUPPRESSED — return EMPTY — regardless of an
4997
- // explicit namespace or default-readability, so a caller authorized for
4998
- // `default` (or for one explicit namespace) cannot read other namespaces'
4999
- // transcript rows via the archive-wide scan (cross-tenant read leak). A
5000
- // SCOPED call (sessionKey or sessionPrefix present) is unaffected: it carries
5001
- // a namespace-framed `session_id` / prefix filter that already constrains the
5002
- // search to the caller's authorized, read-gated namespace.
5003
- const hasScopedSession =
5004
- (typeof request.sessionKey === "string" &&
5005
- request.sessionKey.length > 0) ||
5006
- lcmSessionPrefixes.some((prefix) => typeof prefix === "string" && prefix.length > 0);
5007
- if (!hasScopedSession && resolveNamespaceCapabilities(this.orchestrator.config).namespaces === true) {
5008
- return {
5009
- query: request.query,
5010
- namespace,
5011
- results: [],
5012
- count: 0,
5013
- lcmEnabled: true,
5014
- };
5015
- }
5016
- // Query each LCM read key in order, merging + deduping rows (by
5017
- // sessionId+turnIndex) and preserving first-seen order, capped at `limit`.
5018
- // Use allSettled so one corrupt/failed namespace key cannot discard sibling
5019
- // results from other authorized profile namespaces.
5020
- const seenRows = new Set<string>();
5021
- const results: Array<{ sessionId: string; content: string; turnIndex: number }> = [];
5022
- const lcmSearches: Array<{
5023
- key: string | undefined;
5024
- prefix: string | undefined;
5025
- promise: Promise<Array<{ session_id: string; content: string; turn_index: number }>>;
5026
- }> = [];
5027
- for (const lcmSessionKey of lcmSessionKeyIds) {
5028
- for (const lcmSessionPrefix of lcmSessionPrefixes) {
5029
- lcmSearches.push({
5030
- key: lcmSessionKey,
5031
- prefix: lcmSessionPrefix,
5032
- promise: this.orchestrator.lcmEngine.searchContextFull(
5033
- request.query,
5034
- limit,
5035
- lcmSessionKey,
5036
- lcmSessionPrefix,
5037
- ) as Promise<Array<{ session_id: string; content: string; turn_index: number }>>,
5038
- });
5039
- }
5040
- }
5041
- const settledSearches = await Promise.allSettled(
5042
- lcmSearches.map((search) => search.promise),
4619
+ return this.accessLcmSurface.lcmSearch(
4620
+ request,
5043
4621
  );
5044
- for (let i = 0; i < settledSearches.length; i += 1) {
5045
- if (results.length >= limit) break;
5046
- const settled = settledSearches[i];
5047
- if (!settled || settled.status === "rejected") {
5048
- const failed = lcmSearches[i];
5049
- log.warn(
5050
- `lcmSearch: failed for key=${failed?.key ?? "<none>"} prefix=${failed?.prefix ?? "<none>"}: ${settled?.status === "rejected" ? settled.reason : "missing result"}`,
5051
- );
5052
- continue;
5053
- }
5054
- for (const r of settled.value) {
5055
- const dedupeKey = `${r.session_id}\0${r.turn_index}`;
5056
- if (seenRows.has(dedupeKey)) continue;
5057
- seenRows.add(dedupeKey);
5058
- results.push({
5059
- sessionId: r.session_id,
5060
- content: r.content,
5061
- turnIndex: r.turn_index,
5062
- });
5063
- if (results.length >= limit) break;
5064
- }
5065
- }
5066
-
5067
- return {
5068
- query: request.query,
5069
- namespace,
5070
- results,
5071
- count: results.length,
5072
- lcmEnabled: true,
5073
- };
5074
4622
  }
5075
4623
 
5076
4624
  /**
@@ -5095,38 +4643,6 @@ export class EngramAccessService {
5095
4643
  * so the read key is byte-for-byte what the LCM write and the recall readers
5096
4644
  * use.
5097
4645
  */
5098
- /**
5099
- * Resolve the effective LCM NAMESPACE a same-session operation must prefix
5100
- * with (the namespace half of {@link resolveLcmReadSessionKey}). Split out so
5101
- * `lcmSearch` can apply ONE namespace to BOTH its `sessionKey` and its
5102
- * `sessionPrefix` — the prefix is a search fragment, not a real session, so its
5103
- * own coding context can't be looked up; it must inherit the namespace resolved
5104
- * from the real session (`sessionKeyForOverlay`).
5105
- *
5106
- * `purpose` selects the AUTHORIZATION gate applied before honouring the
5107
- * coding overlay (#1505 round 3 + round 4, codex P2):
5108
- *
5109
- * - `"read"` (`lcmSearch` / raw-excerpt recall): the overlay rows are only
5110
- * visible when the principal SELF base is in the READABLE RECALL SET — the
5111
- * same gate the orchestrator's `lcmReadNamespaceForSession` and the recall
5112
- * namespace set use (`recallNamespacesForPrincipal`, gated by both
5113
- * `defaultRecallNamespaces.includes("self")` AND `canReadNamespace`). A
5114
- * caller that passed the default read check must NOT receive
5115
- * `<principal>-project-*` rows the policy never granted (cross-tenant read
5116
- * leak). When the self base is not readable, keep the just-authorized
5117
- * namespace (collapses to the raw key on the default store).
5118
- *
5119
- * - `"write"` (`lcmCompactionFlush` / `lcmCompactionRecord`): these are
5120
- * write/maintenance operations on the SAME queue `observe` just wrote, so
5121
- * the gate must mirror observe's WRITE authorization (`canWriteNamespace`
5122
- * on the self base), NOT readability. A principal that can WRITE but not
5123
- * READ its self namespace (or whose `defaultRecallNamespaces` omits `self`)
5124
- * archived under the overlay key via `observe`; gating compaction by
5125
- * readability would fall back to the default/raw key and leave that queue
5126
- * never flushed/recorded (round-4 codex P2). Write-authorized ⇒ overlay
5127
- * key, matching the observe write key (rule 42 read/write parity; rule 39
5128
- * identical gates across paths).
5129
- */
5130
4646
  private resolveLcmReadNamespace(
5131
4647
  explicitNamespace: string | undefined,
5132
4648
  resolvedNamespace: string,
@@ -5134,134 +4650,24 @@ export class EngramAccessService {
5134
4650
  authenticatedPrincipal: string | undefined,
5135
4651
  purpose: "read" | "write" = "read",
5136
4652
  ): string {
5137
- const hasExplicitNamespace =
5138
- typeof explicitNamespace === "string" && explicitNamespace.trim().length > 0;
5139
- if (hasExplicitNamespace) return resolvedNamespace;
5140
- // Mirror observe's write resolution: use the coding-overlay namespace when
5141
- // one applies, else the default store. NOT the principal self base — an
5142
- // unqualified observe archives under the default store, so a self-base prefix
5143
- // here would target a queue observe never wrote to (#1495).
5144
- const principal = this.resolveRequestPrincipal(
4653
+ return this.accessLcmSurface.resolveLcmReadNamespace(
4654
+ explicitNamespace,
4655
+ resolvedNamespace,
5145
4656
  sessionKeyForOverlay,
5146
4657
  authenticatedPrincipal,
4658
+ purpose,
5147
4659
  );
5148
- const base = defaultNamespaceForPrincipal(principal, this.orchestrator.config);
5149
- const overlaid = this.orchestrator.applyCodingNamespaceOverlay(
5150
- sessionKeyForOverlay,
5151
- base,
5152
- );
5153
- // No overlay → the default store (raw sessionKey), as before.
5154
- if (overlaid === base) return this.orchestrator.config.defaultNamespace;
5155
- // Overlay applied. Authorize access to the principal's `<principal>-project-*`
5156
- // overlay base before switching the LCM key to it. The gate differs by
5157
- // operation purpose (see the doc comment): reads use the readable-recall-set
5158
- // gate (no cross-tenant read leak), writes use observe's write authorization
5159
- // (so compaction targets the same overlay queue observe wrote to).
5160
- const authorized =
5161
- purpose === "write"
5162
- ? canWriteNamespace(principal, base, this.orchestrator.config)
5163
- : recallNamespacesForPrincipal(
5164
- principal,
5165
- this.orchestrator.config,
5166
- ).includes(base);
5167
- if (authorized) return overlaid;
5168
- // Unauthorized overlay base. For READS, collapse to the DEFAULT STORE (the
5169
- // raw sessionKey) EXACTLY like the orchestrator's `lcmReadNamespaceForSession`
5170
- // (rule 39 / 42) — NOT the caller's `resolvedNamespace`, which for an implicit
5171
- // read can be a readable recall namespace (e.g. `shared`). Returning that
5172
- // would prefix LCM reads with `shared:sessionKey` while in-prompt recall uses
5173
- // the raw `sessionKey`, diverging `lcmSearch`/raw disclosure from orchestrator
5174
- // LCM reads (cursor "LCM read gate wrong fallback"). For an explicit read the
5175
- // method already returned at the top, so this only affects implicit reads.
5176
- // The (currently unused) write purpose preserves its prior `resolvedNamespace`
5177
- // fallback for backward compatibility.
5178
- if (purpose === "read") return this.orchestrator.config.defaultNamespace;
5179
- return resolvedNamespace;
5180
4660
  }
5181
4661
 
5182
- /**
5183
- * Resolve the namespace the raw-disclosure excerpt lookup
5184
- * ({@link fetchRawExcerpts}) must prefix its LCM `session_id` with (#1505
5185
- * thread 2f7). Raw disclosure reads the SAME LCM archive `lcmSearch` and the
5186
- * in-prompt LCM sections read, so it MUST pass through the identical
5187
- * read-authorization gate — NOT `snapshot.namespace`, which records the
5188
- * effective WRITE/overlay namespace (`<principal>-project-*`) even when the
5189
- * principal can WRITE but not READ its self base (or `defaultRecallNamespaces`
5190
- * omits `self`). Routing through `resolveLcmReadNamespace(..., "read")` makes
5191
- * raw disclosure fall back to the default store exactly like normal recall +
5192
- * `lcmSearch`, so it never attaches overlay transcript rows the read gate
5193
- * excludes (cross-tenant read leak). Collapses to the default store / raw
5194
- * sessionKey for single-store / no-overlay / explicit-default flows, so
5195
- * single-user recall is byte-for-byte unchanged.
5196
- *
5197
- * Returns `undefined` when NO readable LCM namespace exists for an IMPLICIT
5198
- * (no explicit `namespace`) raw recall — i.e. a restrictive `default` READ
5199
- * policy denies the principal `default` AND no overlay/self namespace is
5200
- * readable. In that case the caller emits NO excerpts rather than throwing
5201
- * `namespace is not readable: default` (#1505 thread NBHWz): normal recall
5202
- * still succeeds via `recallNamespacesForPrincipal`, so `disclosure: "raw"`
5203
- * must degrade gracefully (empty excerpts), never pre-authorize `default`.
5204
- *
5205
- * IMPLICIT-namespace fallback selection derives from the ALREADY
5206
- * read-authorized recall namespace set (`recallNamespacesForPrincipal` +
5207
- * `canReadNamespace`) — the principal's self base when it is in the readable
5208
- * recall set, else `config.defaultNamespace` ONLY when the principal may read
5209
- * it. It NEVER pre-authorizes `default`. An EXPLICIT `namespace` is still
5210
- * authorized strictly via `resolveReadableNamespace` (explicit reads must pass
5211
- * the ACL — no behavior change).
5212
- */
5213
4662
  private resolveRawExcerptReadNamespace(
5214
4663
  explicitNamespace: string | undefined,
5215
4664
  sessionKey: string | undefined,
5216
4665
  authenticatedPrincipal: string | undefined,
5217
4666
  ): string | undefined {
5218
- const principal = this.resolveRequestPrincipal(
5219
- sessionKey,
5220
- authenticatedPrincipal,
5221
- );
5222
- const hasExplicitNamespace =
5223
- typeof explicitNamespace === "string" &&
5224
- explicitNamespace.trim().length > 0;
5225
- if (hasExplicitNamespace) {
5226
- // Explicit reads must pass the ACL — authorize strictly, exactly as
5227
- // `lcmSearch` does (throws on an unreadable explicit namespace).
5228
- const resolvedNamespace = this.resolveReadableNamespace(
5229
- explicitNamespace,
5230
- principal,
5231
- );
5232
- return this.resolveLcmReadNamespace(
5233
- explicitNamespace,
5234
- resolvedNamespace,
5235
- sessionKey,
5236
- authenticatedPrincipal,
5237
- "read",
5238
- );
5239
- }
5240
- // IMPLICIT raw recall: an active scope profile owns the same LCM read
5241
- // namespace set used by recall and lcmSearch. Return the first profile
5242
- // namespace as the legacy raw-excerpt namespace hint; the concrete ordered
5243
- // key set is still produced by resolveLcmReadSessionIds(), which also treats
5244
- // an empty profile read set as authoritative.
5245
- const profileReadNamespaces = this.resolveScopeProfileLcmReadNamespaces(
5246
- sessionKey,
5247
- authenticatedPrincipal,
5248
- );
5249
- if (profileReadNamespaces !== null) return profileReadNamespaces[0];
5250
-
5251
- // Otherwise derive the read fallback from the ALREADY read-authorized recall
5252
- // namespace set — NEVER pre-authorize `default` (#1505 thread NBHWz). When
5253
- // namespaces are disabled the default store is the only namespace and is
5254
- // always readable (byte-for-byte single-user path).
5255
- const fallbackNamespace =
5256
- this.resolveImplicitLcmReadFallbackNamespace(principal);
5257
- // No readable LCM namespace at all ⇒ no excerpts (caller short-circuits).
5258
- if (fallbackNamespace === undefined) return undefined;
5259
- return this.resolveLcmReadNamespace(
4667
+ return this.accessLcmSurface.resolveRawExcerptReadNamespace(
5260
4668
  explicitNamespace,
5261
- fallbackNamespace,
5262
4669
  sessionKey,
5263
4670
  authenticatedPrincipal,
5264
- "read",
5265
4671
  );
5266
4672
  }
5267
4673
 
@@ -5437,76 +4843,12 @@ export class EngramAccessService {
5437
4843
  sessionKey: string,
5438
4844
  authenticatedPrincipal: string | undefined,
5439
4845
  ): string[] {
5440
- const primary = this.resolveLcmReadSessionKey(
4846
+ return this.accessLcmSurface.resolveLcmReadSessionIds(
5441
4847
  explicitNamespace,
5442
4848
  resolvedNamespace,
5443
4849
  sessionKey,
5444
4850
  authenticatedPrincipal,
5445
- "read",
5446
4851
  );
5447
- const hasExplicitNamespace =
5448
- typeof explicitNamespace === "string" &&
5449
- explicitNamespace.trim().length > 0;
5450
- // Explicit namespace → no overlay fallbacks (the overlay never applies to an
5451
- // explicit read). Single key, unchanged.
5452
- if (hasExplicitNamespace) return [primary];
5453
-
5454
- const profileReadNamespaces = this.resolveScopeProfileLcmReadNamespaces(
5455
- sessionKey,
5456
- authenticatedPrincipal,
5457
- );
5458
- if (profileReadNamespaces !== null) {
5459
- return this.lcmSessionIdsForNamespaces(profileReadNamespaces, sessionKey);
5460
- }
5461
-
5462
- const principal = this.resolveRequestPrincipal(
5463
- sessionKey,
5464
- authenticatedPrincipal,
5465
- );
5466
- const base = defaultNamespaceForPrincipal(
5467
- principal,
5468
- this.orchestrator.config,
5469
- );
5470
- const overlaid = this.orchestrator.applyCodingNamespaceOverlay(
5471
- sessionKey,
5472
- base,
5473
- );
5474
- // No overlay → single default-store key, unchanged.
5475
- if (overlaid === base) return [primary];
5476
- // Overlay present but self base unreadable → the "read" gate already
5477
- // collapsed `primary` to the default store; do NOT add overlay fallbacks
5478
- // (they would be unauthorized `<principal>-project-*` keys). Single key.
5479
- const selfReadableInRecall = recallNamespacesForPrincipal(
5480
- principal,
5481
- this.orchestrator.config,
5482
- ).includes(base);
5483
- if (!selfReadableInRecall) return [primary];
5484
- // Self base readable → overlay rows authorized. Append one LCM key per coding
5485
- // read fallback (project → root), combined with the principal base for
5486
- // isolation — the SAME ordered set the orchestrator recall path searches.
5487
- const overlay = resolveCodingNamespaceOverlay(
5488
- this.orchestrator.getCodingContextForSession(sessionKey),
5489
- this.orchestrator.config.codingMode,
5490
- this.orchestrator.config.defaultNamespace,
5491
- );
5492
- const fallbackNamespaces = (overlay?.readFallbacks ?? []).map((fallback) =>
5493
- combineNamespaces(base, fallback),
5494
- );
5495
- const out = [primary];
5496
- const seen = new Set<string>([primary]);
5497
- for (const ns of fallbackNamespaces) {
5498
- const key =
5499
- lcmSessionKeyForNamespace(
5500
- ns,
5501
- sessionKey,
5502
- this.orchestrator.config.defaultNamespace,
5503
- ) ?? sessionKey;
5504
- if (!seen.has(key)) {
5505
- seen.add(key);
5506
- out.push(key);
5507
- }
5508
- }
5509
- return out;
5510
4852
  }
5511
4853
 
5512
4854
  async lcmCompactionFlush(
@@ -6316,134 +5658,14 @@ export class EngramAccessService {
6316
5658
  return { explanation };
6317
5659
  }
6318
5660
 
6319
- /**
6320
- * Read-only graph snapshot for the admin pane (issue #691 PR 2/5).
6321
- *
6322
- * Reads adjacency from the JSONL edge store written by `GraphIndex` and
6323
- * resolves node metadata via the namespaced storage manager. Namespace
6324
- * resolution mirrors the read-side path used by `recall` /
6325
- * `procedureStats`, so multi-principal deployments can't leak edges from
6326
- * a peer namespace (CLAUDE.md rule 42).
6327
- */
6328
5661
  async graphSnapshot(
6329
5662
  request: GraphSnapshotRequest & { namespace?: string },
6330
5663
  authenticatedPrincipal?: string,
6331
5664
  ): Promise<GraphSnapshotResponse> {
6332
- const namespace = this.resolveReadableNamespace(
6333
- request.namespace,
5665
+ return this.accessAdminOpsSurface.graphSnapshot(
5666
+ request,
6334
5667
  authenticatedPrincipal,
6335
5668
  );
6336
- const storage = await this.orchestrator.getStorage(namespace);
6337
- const cfg = this.orchestrator.config;
6338
- const graphCaps = resolveGraphConstructionCapabilities(cfg);
6339
- // Canonicalize the storage root once — through `realpath` so that any
6340
- // symlink in the namespace root path itself is resolved before we
6341
- // compare children against it. This is required because
6342
- // `GraphEdge.from` / `to` are JSONL-parsed strings — a malformed edge
6343
- // with an absolute path, a `..` segment, OR a symlink that resolves
6344
- // to a file outside the namespace would otherwise read a memory file
6345
- // from a peer namespace, leaking metadata across tenants
6346
- // (codex P1 + follow-up on PR #734; CLAUDE.md rule 42).
6347
- let namespaceRootReal: string;
6348
- try {
6349
- namespaceRootReal = await nodeFs.realpath(storage.dir);
6350
- } catch {
6351
- // If the namespace root itself doesn't exist on disk yet (fresh
6352
- // install with no memories), fall back to the lexical resolve so
6353
- // the snapshot can still return an empty result rather than
6354
- // throwing. No symlink can resolve through a missing path, so
6355
- // this fallback is safe — every candidate we see will fail the
6356
- // realpath step below and surface as `null`.
6357
- namespaceRootReal = nodePath.resolve(storage.dir);
6358
- }
6359
- const namespaceRootWithSep = namespaceRootReal.endsWith(nodePath.sep)
6360
- ? namespaceRootReal
6361
- : namespaceRootReal + nodePath.sep;
6362
- const loadNode = async (relPath: string): Promise<GraphSnapshotNodeMetadata | null> => {
6363
- // `GraphEdge.from` / `to` are storage-relative paths; resolve against
6364
- // the namespaced storage root so the metadata read honors namespace
6365
- // boundaries even when the same memory id exists in multiple
6366
- // namespaces.
6367
- //
6368
- // Three-stage guard:
6369
- // 1. Reject absolute paths up front — only relative endpoints are
6370
- // ever produced by the writer, so anything else is malformed.
6371
- // 2. Lexical containment check on the resolved path. This catches
6372
- // `..` traversals before we touch the filesystem.
6373
- // 3. `fs.realpath` containment check — resolves symlinks so an
6374
- // in-namespace path that *points* at an out-of-namespace file
6375
- // is still rejected. Without this step a symlinked endpoint
6376
- // could leak a peer namespace's frontmatter.
6377
- // Bad paths surface a length-only warning (never echo the offending
6378
- // segments — those would themselves cross namespace boundaries
6379
- // through the log surface) and fall through to a `null` metadata
6380
- // result.
6381
- if (nodePath.isAbsolute(relPath)) {
6382
- log.warn(
6383
- `graphSnapshot: rejected absolute edge endpoint (len=${relPath.length}) `
6384
- + `outside namespace root`,
6385
- );
6386
- return null;
6387
- }
6388
- const candidate = nodePath.resolve(namespaceRootReal, relPath);
6389
- if (candidate !== namespaceRootReal && !candidate.startsWith(namespaceRootWithSep)) {
6390
- log.warn(
6391
- `graphSnapshot: rejected traversing edge endpoint (len=${relPath.length}) `
6392
- + `outside namespace root`,
6393
- );
6394
- return null;
6395
- }
6396
- let canonical: string;
6397
- try {
6398
- canonical = await nodeFs.realpath(candidate);
6399
- } catch {
6400
- // Missing file — `readMemoryByPath` will return null too. We
6401
- // intentionally still call it so callers see a consistent
6402
- // "unknown" result rather than special-casing missing edges.
6403
- canonical = candidate;
6404
- }
6405
- if (canonical !== namespaceRootReal && !canonical.startsWith(namespaceRootWithSep)) {
6406
- log.warn(
6407
- `graphSnapshot: rejected symlinked edge endpoint (len=${relPath.length}) `
6408
- + `that resolved outside namespace root`,
6409
- );
6410
- return null;
6411
- }
6412
- // Both `canonical` (realpath of candidate) and `namespaceRootReal`
6413
- // (realpath of storage.dir) are fully resolved here, so the
6414
- // containment check above is comparing apples-to-apples even when
6415
- // storage.dir (= storage.baseDir) is itself a symlink to the real
6416
- // directory. Pass `canonical` — not the pre-realpath `candidate` —
6417
- // so the storage read also uses the stable real path.
6418
- const memory = await storage.readMemoryByPath(canonical);
6419
- if (!memory) return null;
6420
- const fm = memory.frontmatter;
6421
- return {
6422
- category: fm.category ?? "unknown",
6423
- label: fm.id ?? nodePath.basename(canonical, nodePath.extname(canonical)),
6424
- updated: fm.updated,
6425
- };
6426
- };
6427
- // Use the realpath-resolved namespace root for the edge-file read so
6428
- // the JSONL location is stable whether storage.dir is a direct path
6429
- // or a symlink. namespaceRootReal was computed via `fs.realpath`
6430
- // above; using it here keeps both the graph-file I/O and the loadNode
6431
- // containment check on the same resolved base path.
6432
- return buildGraphSnapshot({
6433
- memoryDir: namespaceRootReal,
6434
- graphConfig: {
6435
- entityGraph: graphCaps.entityGraph,
6436
- timeGraph: graphCaps.timeGraph,
6437
- causalGraph: graphCaps.causalGraph,
6438
- },
6439
- request: {
6440
- limit: request.limit,
6441
- since: request.since,
6442
- focusNodeId: request.focusNodeId,
6443
- categories: request.categories,
6444
- },
6445
- loadNode,
6446
- });
6447
5669
  }
6448
5670
 
6449
5671
  async memoryFeedback(request: {
@@ -7140,108 +6362,13 @@ export class EngramAccessService {
7140
6362
  });
7141
6363
  }
7142
6364
 
7143
- /**
7144
- * List capsule archives in the namespace-scoped capsule store.
7145
- *
7146
- * MCP uses this access-layer method instead of reading arbitrary paths so
7147
- * capsule discovery remains bound to the same namespace ACLs as export and
7148
- * import.
7149
- */
7150
6365
  async capsuleList(options?: {
7151
6366
  namespace?: string;
7152
6367
  principal?: string;
7153
6368
  }): Promise<EngramAccessCapsuleListResponse> {
7154
- const resolvedNamespace = this.resolveReadableNamespace(options?.namespace, options?.principal);
7155
- const storage = await this.orchestrator.getStorage(resolvedNamespace);
7156
- const capsulesDir = defaultCapsulesDir(storage.dir);
7157
- let dirEntries: import("node:fs").Dirent[];
7158
- try {
7159
- const capsulesDirStat = await nodeFs.lstat(capsulesDir);
7160
- if (capsulesDirStat.isSymbolicLink()) {
7161
- throw new EngramAccessInputError("capsule list failed: capsule store directory must not be a symlink");
7162
- }
7163
- if (!capsulesDirStat.isDirectory()) {
7164
- throw new EngramAccessInputError("capsule list failed: capsule store path must be a directory");
7165
- }
7166
- dirEntries = await nodeFs.readdir(capsulesDir, { withFileTypes: true });
7167
- } catch (err) {
7168
- const code = typeof err === "object" && err !== null && "code" in err
7169
- ? (err as { code?: unknown }).code
7170
- : undefined;
7171
- if (code === "ENOENT") {
7172
- return { namespace: resolvedNamespace, capsulesDir, capsules: [] };
7173
- }
7174
- throw err;
7175
- }
7176
-
7177
- const archiveNames = dirEntries
7178
- .filter(
7179
- (entry) =>
7180
- entry.isFile() &&
7181
- (entry.name.endsWith(".capsule.json.gz") ||
7182
- entry.name.endsWith(".capsule.json.gz.enc")),
7183
- )
7184
- .map((entry) => entry.name)
7185
- .sort();
7186
-
7187
- const capsules: CapsuleListEntry[] = [];
7188
- for (const archiveName of archiveNames) {
7189
- const archivePath = nodePath.join(capsulesDir, archiveName);
7190
- const id = archiveName
7191
- .replace(/\.capsule\.json\.gz\.enc$/, "")
7192
- .replace(/\.capsule\.json\.gz$/, "");
7193
- const manifestPath = nodePath.join(capsulesDir, `${id}.manifest.json`);
7194
-
7195
- let createdAt: string | null = null;
7196
- let pluginVersion: string | null = null;
7197
- let fileCount: number | null = null;
7198
- let description: string | null = null;
7199
- let manifestPathOrNull: string | null = manifestPath;
7200
-
7201
- try {
7202
- const manifestStat = await nodeFs.lstat(manifestPath);
7203
- if (manifestStat.isSymbolicLink() || !manifestStat.isFile()) {
7204
- capsules.push({
7205
- id,
7206
- archivePath,
7207
- manifestPath: manifestPathOrNull,
7208
- createdAt,
7209
- pluginVersion,
7210
- fileCount,
7211
- description,
7212
- });
7213
- continue;
7214
- }
7215
- const raw = await nodeFs.readFile(manifestPath, "utf-8");
7216
- const sidecar = JSON.parse(raw) as Record<string, unknown>;
7217
- createdAt = typeof sidecar.createdAt === "string" ? sidecar.createdAt : null;
7218
- pluginVersion = typeof sidecar.pluginVersion === "string" ? sidecar.pluginVersion : null;
7219
- fileCount = Array.isArray(sidecar.files) ? sidecar.files.length : null;
7220
- const capsule = sidecar.capsule as Record<string, unknown> | undefined;
7221
- description = capsule && typeof capsule.description === "string"
7222
- ? capsule.description
7223
- : null;
7224
- } catch (err) {
7225
- const code = typeof err === "object" && err !== null && "code" in err
7226
- ? (err as { code?: unknown }).code
7227
- : undefined;
7228
- if (code === "ENOENT") {
7229
- manifestPathOrNull = null;
7230
- }
7231
- }
7232
-
7233
- capsules.push({
7234
- id,
7235
- archivePath,
7236
- manifestPath: manifestPathOrNull,
7237
- createdAt,
7238
- pluginVersion,
7239
- fileCount,
7240
- description,
7241
- });
7242
- }
7243
-
7244
- return { namespace: resolvedNamespace, capsulesDir, capsules };
6369
+ return this.accessAdminOpsSurface.capsuleList(
6370
+ options,
6371
+ );
7245
6372
  }
7246
6373
 
7247
6374
  /**
@@ -7484,87 +6611,15 @@ export class EngramAccessService {
7484
6611
  return getDreamsStatus(storage.dir, windowHours);
7485
6612
  }
7486
6613
 
7487
- /**
7488
- * Manually invoke a single Dreams phase pass (PR 4/4).
7489
- *
7490
- * Deep-sleep delegates to memory governance (shadow → dry-run, apply → live).
7491
- * Light-sleep and REM scan the observation ledger and memory corpus
7492
- * respectively, returning the same telemetry shape as a scheduled run.
7493
- */
7494
6614
  async dreamsRun(options: {
7495
6615
  phase: import("./types.js").DreamsPhase;
7496
6616
  dryRun?: boolean;
7497
6617
  namespace?: string;
7498
6618
  authenticatedPrincipal?: string;
7499
6619
  }): Promise<import("./types.js").DreamsRunResult> {
7500
- const { runDreamsPhase } = await import("./maintenance/dreams-ledger.js");
7501
- const validPhases = ["lightSleep", "rem", "deepSleep"];
7502
- if (!validPhases.includes(options.phase)) {
7503
- throw new EngramAccessInputError(
7504
- `Invalid phase: ${String(options.phase)}. Must be one of: ${validPhases.join(", ")}`,
7505
- );
7506
- }
7507
- const deepSleep = this.orchestrator.config.dreamsPhases.deepSleep;
7508
- if (
7509
- options.phase === "deepSleep" &&
7510
- deepSleep.enabled === false &&
7511
- deepSleep.enabledExplicitlySet === true
7512
- ) {
7513
- throw new EngramAccessInputError(
7514
- "memory governance is disabled by dreams.phases.deepSleep.enabled=false",
7515
- );
7516
- }
7517
- const dryRun = options.dryRun === true;
7518
- const resolvedNamespace = this.writableNamespaceFor(
7519
- options.namespace,
7520
- undefined,
7521
- options.authenticatedPrincipal,
7522
- );
7523
- const storage = await this.orchestrator.getStorage(resolvedNamespace);
7524
- const memoryDir = storage.dir;
7525
- const phaseRunner = dryRun || options.phase === "deepSleep"
7526
- ? undefined
7527
- : async (_opts: { memoryDir: string; phase: "lightSleep" | "rem" }) => {
7528
- if (_opts.phase === "lightSleep") {
7529
- const result = await this.orchestrator.runLifecyclePolicyNow(storage);
7530
- return {
7531
- itemsProcessed: result.memoriesAssessed,
7532
- notes: `scored ${result.memoriesAssessed} memories`,
7533
- };
7534
- }
7535
- const result = await this.orchestrator.runSemanticConsolidationNow({
7536
- dryRun: false,
7537
- storage,
7538
- });
7539
- const itemsProcessed = result.clusters.reduce(
7540
- (sum, cluster) => sum + cluster.memories.length,
7541
- 0,
7542
- );
7543
- return {
7544
- itemsProcessed,
7545
- notes: `REM consolidation found ${result.clustersFound} clusters`,
7546
- };
7547
- };
7548
- const governanceRunner = options.phase === "deepSleep"
7549
- ? async (_opts: { memoryDir: string; dryRun: boolean }) => {
7550
- return this.orchestrator.runDeepSleepGovernanceNow({
7551
- storage,
7552
- dryRun: _opts.dryRun,
7553
- });
7554
- }
7555
- : undefined;
7556
- const result = await runDreamsPhase(
7557
- { memoryDir, phase: options.phase, dryRun },
7558
- governanceRunner,
7559
- phaseRunner,
6620
+ return this.accessAdminOpsSurface.dreamsRun(
6621
+ options,
7560
6622
  );
7561
- return {
7562
- phase: result.phase,
7563
- dryRun: result.dryRun,
7564
- durationMs: result.durationMs,
7565
- itemsProcessed: result.itemsProcessed,
7566
- notes: result.notes,
7567
- };
7568
6623
  }
7569
6624
 
7570
6625
  // ---------------------------------------------------------------------------
@@ -7723,12 +6778,6 @@ export class EngramAccessService {
7723
6778
  return redactSensitive(result);
7724
6779
  }
7725
6780
 
7726
- /**
7727
- * Manually promote a memory into one or more authorized targets. Requires
7728
- * a non-empty reason (audit-logged). Reuses the scope-profile promotion
7729
- * resolution and `canWriteNamespace` gate — there is no dashboard-only
7730
- * write path.
7731
- */
7732
6781
  async adminPromoteMemory(request: {
7733
6782
  sourceMemoryId: string;
7734
6783
  namespace?: string;
@@ -7738,111 +6787,9 @@ export class EngramAccessService {
7738
6787
  reason: string;
7739
6788
  actor?: never; // ignored — actor is derived from the authenticated principal
7740
6789
  }) {
7741
- const config = this.orchestrator.config;
7742
- const namespacesEnabled = resolveNamespaceCapabilities(config).namespaces === true;
7743
- // Fix OdCl3: when no namespace is supplied, resolve the source via the
7744
- // scope plan (principal self / scope-profile write layer / coding overlay)
7745
- // instead of falling through to config.defaultNamespace. This matches the
7746
- // runtime observe/write path.
7747
- let sourceNamespace: string;
7748
- if (request.namespace) {
7749
- sourceNamespace = this.resolveReadableNamespace(
7750
- request.namespace,
7751
- request.principal,
7752
- );
7753
- } else {
7754
- const codingContext = request.sessionKey
7755
- ? this.orchestrator.getCodingContextForSession(request.sessionKey) ?? null
7756
- : null;
7757
- const plan = resolveScopePlan({
7758
- config,
7759
- sessionKey: request.sessionKey,
7760
- principalOverride: request.principal,
7761
- codingContext,
7762
- namespacesEnabled,
7763
- });
7764
- sourceNamespace = this.resolveReadableNamespace(
7765
- plan.baseNamespace,
7766
- request.principal,
7767
- );
7768
- }
7769
- // Fix OdB0c: thread coding context into the scope-profile plan so
7770
- // userProject/teamProject promotion targets resolve for project-scoped
7771
- // sessions (previously hard-coded to null).
7772
- const codingContext = request.sessionKey
7773
- ? this.orchestrator.getCodingContextForSession(request.sessionKey) ?? null
7774
- : null;
7775
- const codingOverlay = codingContext
7776
- ? resolveCodingNamespaceOverlay(
7777
- codingContext,
7778
- config.codingMode,
7779
- config.defaultNamespace,
7780
- )
7781
- : null;
7782
- const scopeProfilePlan = resolveScopeProfilePlan({
7783
- config,
7784
- principal: request.principal,
7785
- codingContext,
7786
- codingOverlay,
7787
- });
7788
- const storage: PromotionStorageProvider = {
7789
- readMemory: async (namespace, memoryId) => {
7790
- const resolved = await this.orchestrator.getStorage(namespace);
7791
- const memory = await resolved.getMemoryById(memoryId);
7792
- if (!memory) return null;
7793
- return {
7794
- category: memory.frontmatter.category,
7795
- content: memory.content,
7796
- frontmatter: memory.frontmatter,
7797
- };
7798
- },
7799
- writePromotedMemory: async (namespace, memory) => {
7800
- const resolved = await this.orchestrator.getStorage(namespace);
7801
- const { id, tombstoneBlocked } = await resolved.writeMemory(
7802
- memory.category,
7803
- memory.content,
7804
- {
7805
- confidence: memory.confidence,
7806
- tags: memory.tags,
7807
- entityRef: memory.entityRef,
7808
- source: `admin-promotion:${memory.sourceNamespace}:${memory.reason.slice(0, 120)}`,
7809
- lineage: memory.lineage,
7810
- sourceMemoryId: memory.sourceMemoryId,
7811
- actor: memory.actor,
7812
- validAt: memory.validAt,
7813
- },
7814
- );
7815
- // #1645: a tombstone-blocked promotion lands pending_review (no active
7816
- // copy in the target). Report it as a failed promotion so the admin
7817
- // sees an honest result — the content is queued for review, not
7818
- // actively promoted. promoteMemory's catch block sanitizes this into
7819
- // a generic "promotion write failed" audit entry.
7820
- if (tombstoneBlocked) {
7821
- throw new Error(
7822
- "target namespace tombstone-blocked the promoted content (pending_review)",
7823
- );
7824
- }
7825
- return id;
7826
- },
7827
- };
7828
- try {
7829
- return await promoteMemory({
7830
- config,
7831
- sourceMemoryId: request.sourceMemoryId,
7832
- sourceNamespace,
7833
- principal: request.principal,
7834
- targets: request.targets,
7835
- reason: request.reason,
7836
- actor: request.principal ?? "admin-console",
7837
- storage,
7838
- scopeProfilePlan,
7839
- });
7840
- } catch (err) {
7841
- if (err instanceof AdminPromotionError) {
7842
- throw new EngramAccessInputError(err.message);
7843
- }
7844
- throw err;
7845
- }
6790
+ return this.accessAdminOpsSurface.adminPromoteMemory(
6791
+ request,
6792
+ );
7846
6793
  }
7847
6794
  }
7848
6795