@remnic/core 10.0.84 → 10.0.86

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 (545) hide show
  1. package/dist/access-admin-ops-surface.js +90 -88
  2. package/dist/access-authorization-probe.js +91 -89
  3. package/dist/access-boundary.d.ts.map +1 -1
  4. package/dist/access-boundary.js +90 -88
  5. package/dist/access-cli.js +198 -194
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-errors.d.ts +14 -0
  8. package/dist/access-errors.d.ts.map +1 -1
  9. package/dist/access-errors.js +5 -1
  10. package/dist/access-extraction-force-flush.js +90 -88
  11. package/dist/access-http-briefing.js +91 -89
  12. package/dist/access-http-query.js +92 -90
  13. package/dist/access-http.d.ts +12 -0
  14. package/dist/access-http.d.ts.map +1 -1
  15. package/dist/access-http.js +119 -116
  16. package/dist/access-identity-continuity-surface.js +90 -88
  17. package/dist/access-lcm-surface.js +90 -88
  18. package/dist/access-mcp-frames.d.ts +29 -0
  19. package/dist/access-mcp-frames.d.ts.map +1 -0
  20. package/dist/access-mcp-frames.js +10 -0
  21. package/dist/access-mcp.d.ts +1 -1
  22. package/dist/access-mcp.d.ts.map +1 -1
  23. package/dist/access-mcp.js +105 -102
  24. package/dist/access-memory-search-fanout.js +2 -2
  25. package/dist/access-namespace-preflight.js +90 -88
  26. package/dist/access-observe-write-category.js +15 -14
  27. package/dist/access-observe-write-surface.d.ts.map +1 -1
  28. package/dist/access-observe-write-surface.js +90 -88
  29. package/dist/access-offline-manifest.js +10 -10
  30. package/dist/access-operations-batch.js +94 -92
  31. package/dist/access-operations.js +101 -99
  32. package/dist/access-recall-concurrency.js +90 -88
  33. package/dist/access-recall-diagnostics.js +2 -2
  34. package/dist/access-recall-response.js +90 -88
  35. package/dist/access-recall-surface.js +90 -88
  36. package/dist/access-schema.d.ts.map +1 -1
  37. package/dist/access-schema.js +10 -9
  38. package/dist/access-service.d.ts +3 -3
  39. package/dist/access-service.d.ts.map +1 -1
  40. package/dist/access-service.js +90 -88
  41. package/dist/access-surface-catalog.js +92 -90
  42. package/dist/access-surface-catalog.js.map +1 -1
  43. package/dist/access-token-capabilities.js +2 -2
  44. package/dist/active-recall.js +38 -38
  45. package/dist/adapters/index.js +7 -7
  46. package/dist/adapters/registry.js +3 -3
  47. package/dist/{auto-sync-C7E6G7Y2.js → auto-sync-3AZW7MXI.js} +6 -6
  48. package/dist/briefing-deadline.d.ts +2 -2
  49. package/dist/briefing-deadline.d.ts.map +1 -1
  50. package/dist/briefing-deadline.js +1 -1
  51. package/dist/briefing.js +25 -25
  52. package/dist/buffer-admission.d.ts +22 -0
  53. package/dist/buffer-admission.d.ts.map +1 -0
  54. package/dist/buffer-admission.js +13 -0
  55. package/dist/buffer-eviction.d.ts +4 -5
  56. package/dist/buffer-eviction.d.ts.map +1 -1
  57. package/dist/buffer-eviction.js +1 -1
  58. package/dist/buffer-persistence.d.ts +8 -4
  59. package/dist/buffer-persistence.d.ts.map +1 -1
  60. package/dist/buffer-persistence.js +1 -1
  61. package/dist/buffer-turn-helpers.d.ts +12 -0
  62. package/dist/buffer-turn-helpers.d.ts.map +1 -1
  63. package/dist/buffer-turn-helpers.js +9 -1
  64. package/dist/buffer-types.d.ts +24 -1
  65. package/dist/buffer-types.d.ts.map +1 -1
  66. package/dist/buffer.d.ts +21 -19
  67. package/dist/buffer.d.ts.map +1 -1
  68. package/dist/buffer.js +17 -13
  69. package/dist/calibration.js +3 -3
  70. package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-7FJQINUR.js} +2 -2
  71. package/dist/capsule-gunzip-KGBDGF7P.js +11 -0
  72. package/dist/capsule-gunzip-KGBDGF7P.js.map +1 -0
  73. package/dist/capsule-import-gate.d.ts +11 -0
  74. package/dist/capsule-import-gate.d.ts.map +1 -0
  75. package/dist/capsule-import-gate.js +12 -0
  76. package/dist/capsule-import-gate.js.map +1 -0
  77. package/dist/{capsule-merge-XVTQJJ6R.js → capsule-merge-2IIXFXN5.js} +5 -11
  78. package/dist/capsule-merge-2IIXFXN5.js.map +1 -0
  79. package/dist/causal-behavior.js +2 -2
  80. package/dist/causal-chain.js +2 -2
  81. package/dist/causal-consolidation.js +27 -27
  82. package/dist/causal-retrieval.js +2 -2
  83. package/dist/causal-trajectory-graph.js +2 -2
  84. package/dist/causal-trajectory.js +1 -1
  85. package/dist/{chunk-MSIRVXQ3.js → chunk-2G4TV2ML.js} +12 -12
  86. package/dist/{chunk-B74NPUHT.js → chunk-2JG2NI7C.js} +54 -54
  87. package/dist/chunk-2JG2NI7C.js.map +1 -0
  88. package/dist/{chunk-IR4C3DP2.js → chunk-2K3AUDH7.js} +2 -2
  89. package/dist/{chunk-2IW5PT7U.js → chunk-2N4OUSIA.js} +2 -2
  90. package/dist/{chunk-BXLOS5AJ.js → chunk-2NLLXCJG.js} +2 -2
  91. package/dist/{chunk-QZSIDSB2.js → chunk-2NUDHVYM.js} +2 -2
  92. package/dist/{chunk-HPFVZPRH.js → chunk-2U4LMSEX.js} +3 -3
  93. package/dist/{chunk-WHQXWFH7.js → chunk-2XOFVTTT.js} +2 -2
  94. package/dist/{chunk-I6ORDT54.js → chunk-32CA7B6L.js} +2 -2
  95. package/dist/{chunk-T77AX6DG.js → chunk-32YX7EJ3.js} +4 -4
  96. package/dist/{chunk-HLKUZW42.js → chunk-3CTKEXXF.js} +144 -156
  97. package/dist/chunk-3CTKEXXF.js.map +1 -0
  98. package/dist/{chunk-HLWXCDUQ.js → chunk-3DAWHP5K.js} +5 -5
  99. package/dist/{chunk-ZJJFUEPO.js → chunk-3PJEDHLD.js} +2 -2
  100. package/dist/{chunk-PJYK7HH2.js → chunk-3UZ2VMKD.js} +2 -2
  101. package/dist/{chunk-IN4OFAHX.js → chunk-3Y5X6YFH.js} +2 -2
  102. package/dist/{chunk-YUTT4ZTI.js → chunk-45VUHDV2.js} +2 -2
  103. package/dist/{chunk-T6T2RMWW.js → chunk-4GMI5HFH.js} +12 -12
  104. package/dist/{chunk-YOEYL6CH.js → chunk-4LCVA2LW.js} +3 -3
  105. package/dist/{chunk-66JOLMWS.js → chunk-4X7HQKAG.js} +2 -2
  106. package/dist/{chunk-65SKM4BF.js → chunk-4XYAWK5H.js} +2 -2
  107. package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
  108. package/dist/{chunk-TPX7XXY4.js → chunk-5SOSQTVE.js} +2 -2
  109. package/dist/{chunk-G3T6YMBO.js → chunk-64S6X4IU.js} +7 -7
  110. package/dist/{chunk-BK7AJUWB.js → chunk-6BCWFCDG.js} +7 -7
  111. package/dist/{chunk-V27FK6D6.js → chunk-6XTUZKCZ.js} +2 -2
  112. package/dist/{chunk-KASJJGRG.js → chunk-6ZELOK6O.js} +3 -8
  113. package/dist/chunk-6ZELOK6O.js.map +1 -0
  114. package/dist/{chunk-LECSS7EE.js → chunk-73UZZFJC.js} +3 -3
  115. package/dist/{chunk-4EACCIPO.js → chunk-77PDJ4OE.js} +3 -3
  116. package/dist/{chunk-GQZSGXR6.js → chunk-7GSL4LBG.js} +2 -2
  117. package/dist/{chunk-B6SPPYGJ.js → chunk-7IWRLYFJ.js} +2 -2
  118. package/dist/{chunk-R45L4TV6.js → chunk-7LCGFUMB.js} +6 -6
  119. package/dist/{chunk-A7BM2JTH.js → chunk-AJHNXIQU.js} +2 -2
  120. package/dist/{chunk-2D7BTSDW.js → chunk-ANZZLRUX.js} +2 -2
  121. package/dist/{chunk-ZXF3O7P4.js → chunk-ARRTLPRC.js} +2 -2
  122. package/dist/{chunk-S5CQI2KF.js → chunk-B3GKQ6DU.js} +2 -2
  123. package/dist/{chunk-XRBHIR7Q.js → chunk-B56LORLY.js} +2 -2
  124. package/dist/{chunk-KO3YBHOV.js → chunk-BIVKPQ6Q.js} +3 -3
  125. package/dist/{chunk-QZBTIYKD.js → chunk-BLSHFIR6.js} +7 -6
  126. package/dist/chunk-BLSHFIR6.js.map +1 -0
  127. package/dist/{chunk-54FDG7K7.js → chunk-C2HORX76.js} +3 -3
  128. package/dist/{chunk-WAD32CZG.js → chunk-CC4V7HNP.js} +43 -30
  129. package/dist/chunk-CC4V7HNP.js.map +1 -0
  130. package/dist/{chunk-LGEH473S.js → chunk-CJCTKL6Y.js} +9 -1
  131. package/dist/chunk-CJCTKL6Y.js.map +1 -0
  132. package/dist/{chunk-E33UJHY3.js → chunk-CN7Q3T2J.js} +4 -4
  133. package/dist/{chunk-YUTG35M4.js → chunk-CYCEROPR.js} +3 -3
  134. package/dist/{chunk-TFLPDNQV.js → chunk-D5EV6Y2L.js} +3 -3
  135. package/dist/{chunk-BFBS7OFQ.js → chunk-DC55RIEZ.js} +4 -4
  136. package/dist/{chunk-B4ZU34JQ.js → chunk-DT7SNJH6.js} +3 -3
  137. package/dist/{chunk-VLMBSGQF.js → chunk-DZ7DPDVW.js} +10 -8
  138. package/dist/chunk-DZ7DPDVW.js.map +1 -0
  139. package/dist/{chunk-FVRTCYZ3.js → chunk-EA2LA5RI.js} +2 -2
  140. package/dist/{chunk-WBN66HPI.js → chunk-ELHJK7LM.js} +6 -6
  141. package/dist/{chunk-77R7BQZ2.js → chunk-EQ4U73UH.js} +77 -24
  142. package/dist/chunk-EQ4U73UH.js.map +1 -0
  143. package/dist/{chunk-3JPTUCD7.js → chunk-EUBDYOLD.js} +32 -23
  144. package/dist/chunk-EUBDYOLD.js.map +1 -0
  145. package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
  146. package/dist/{chunk-YAUHSNPB.js → chunk-F74G4KIH.js} +2 -2
  147. package/dist/{chunk-MPGLM2VU.js → chunk-FF3EFLUT.js} +7 -5
  148. package/dist/chunk-FF3EFLUT.js.map +1 -0
  149. package/dist/{chunk-BV2BRHD4.js → chunk-FHGWKODN.js} +2 -2
  150. package/dist/chunk-FKBGK3NC.js +62 -0
  151. package/dist/chunk-FKBGK3NC.js.map +1 -0
  152. package/dist/{chunk-X2KO4FK5.js → chunk-FNFURZBZ.js} +5 -5
  153. package/dist/chunk-FNFURZBZ.js.map +1 -0
  154. package/dist/{chunk-KESJ5XX3.js → chunk-G54BOKZ6.js} +2 -2
  155. package/dist/{chunk-YK7GOPIE.js → chunk-G67EUQAB.js} +9 -7
  156. package/dist/chunk-G67EUQAB.js.map +1 -0
  157. package/dist/{chunk-K36RMINQ.js → chunk-GOZXQNIM.js} +3 -3
  158. package/dist/{chunk-OQSSQOKQ.js → chunk-GULUWOA5.js} +2 -2
  159. package/dist/{chunk-K6RTQZA4.js → chunk-HFUKXOUW.js} +2 -2
  160. package/dist/{chunk-WV254W4V.js → chunk-HLN2HOXX.js} +2 -2
  161. package/dist/{chunk-FUOAIJKQ.js → chunk-IBDWYRDR.js} +3 -3
  162. package/dist/{chunk-Z3DDQXM4.js → chunk-IBGU6AN6.js} +45 -45
  163. package/dist/chunk-IBGU6AN6.js.map +1 -0
  164. package/dist/{chunk-AJG2TAMR.js → chunk-IEEV27BC.js} +2 -2
  165. package/dist/chunk-IGLJNHOX.js +62 -0
  166. package/dist/chunk-IGLJNHOX.js.map +1 -0
  167. package/dist/{chunk-LTDLJCMQ.js → chunk-IJ3OEECW.js} +4 -4
  168. package/dist/{chunk-BK4FBHZ3.js → chunk-IPD5GC7C.js} +3 -3
  169. package/dist/{chunk-KFLD7ZZG.js → chunk-IUKYFTGW.js} +3 -3
  170. package/dist/{chunk-GCAQ3VUJ.js → chunk-JM2YZKCQ.js} +2 -2
  171. package/dist/{chunk-BNQJKWWZ.js → chunk-K3FH3LAQ.js} +4 -4
  172. package/dist/{chunk-LU66I56Q.js → chunk-KVSOEVAF.js} +61 -12
  173. package/dist/chunk-KVSOEVAF.js.map +1 -0
  174. package/dist/chunk-M3BEJJNV.js +31 -0
  175. package/dist/chunk-M3BEJJNV.js.map +1 -0
  176. package/dist/{chunk-RZH5RVJC.js → chunk-MTTYIXSU.js} +2 -2
  177. package/dist/{chunk-XHGYZRVW.js → chunk-NIVKG3DS.js} +5 -5
  178. package/dist/{chunk-VZELUMKK.js → chunk-NQP4ZYGG.js} +2 -2
  179. package/dist/{chunk-7PMOCQOL.js → chunk-NT7F7WJQ.js} +192 -63
  180. package/dist/chunk-NT7F7WJQ.js.map +1 -0
  181. package/dist/{chunk-YO2JEX2Z.js → chunk-OGBQMY46.js} +2 -2
  182. package/dist/{chunk-TLHF75OR.js → chunk-OL553AWT.js} +12 -12
  183. package/dist/{chunk-CLVILSBR.js → chunk-OZNBZJ3S.js} +22 -22
  184. package/dist/{chunk-VUROWXFL.js → chunk-P452SEKO.js} +2 -2
  185. package/dist/{chunk-JX3E4XCW.js → chunk-PDOOUXCS.js} +2 -2
  186. package/dist/{chunk-6QYPL6NI.js → chunk-PKZ274PI.js} +3 -3
  187. package/dist/{chunk-KJCL2FZO.js → chunk-QD5R6U5J.js} +2 -2
  188. package/dist/{chunk-JRVMCZE3.js → chunk-QEYTRFR3.js} +2 -2
  189. package/dist/{chunk-VKDAJJQP.js → chunk-QEZ3DW5O.js} +11 -11
  190. package/dist/{chunk-WYPCJK6K.js → chunk-QKERFEJS.js} +2 -2
  191. package/dist/{chunk-BDN3TDGV.js → chunk-QKES7GU6.js} +2 -2
  192. package/dist/{chunk-PCQWKMOL.js → chunk-QNSZYTSG.js} +2 -2
  193. package/dist/chunk-QV2VZKD5.js +31 -0
  194. package/dist/chunk-QV2VZKD5.js.map +1 -0
  195. package/dist/{chunk-IZJQNDGI.js → chunk-R6HMAIKM.js} +2 -2
  196. package/dist/{chunk-CIIHIAJ2.js → chunk-ROGO7FRA.js} +6 -5
  197. package/dist/{chunk-CIIHIAJ2.js.map → chunk-ROGO7FRA.js.map} +1 -1
  198. package/dist/{chunk-NUSAAN75.js → chunk-ROKDHO74.js} +2 -2
  199. package/dist/{chunk-D5SSBCYY.js → chunk-RWGRSKN5.js} +7 -5
  200. package/dist/chunk-RWGRSKN5.js.map +1 -0
  201. package/dist/{chunk-FF6N7BXC.js → chunk-S45INSV3.js} +4 -4
  202. package/dist/{chunk-2RUTN7PR.js → chunk-S7GJNA3K.js} +2 -2
  203. package/dist/{chunk-XT45WCKX.js → chunk-SYKJBS7S.js} +10 -10
  204. package/dist/{chunk-5BE3FLKL.js → chunk-TAUHAP5J.js} +4 -4
  205. package/dist/{chunk-RJWL3RHB.js → chunk-TFEHSPGQ.js} +2 -2
  206. package/dist/{chunk-XYP5WBDO.js → chunk-TQ4SCPGQ.js} +76 -60
  207. package/dist/chunk-TQ4SCPGQ.js.map +1 -0
  208. package/dist/{chunk-K3B72DKJ.js → chunk-TRMZHGIX.js} +8 -8
  209. package/dist/{chunk-WBDIZWJQ.js → chunk-TSS3D7ZY.js} +2 -2
  210. package/dist/{chunk-6LCWKS62.js → chunk-UAUV3BWS.js} +104 -121
  211. package/dist/chunk-UAUV3BWS.js.map +1 -0
  212. package/dist/{chunk-KQUSP6LB.js → chunk-UDKWFDV2.js} +1 -1
  213. package/dist/chunk-UDKWFDV2.js.map +1 -0
  214. package/dist/{chunk-HQPFACXO.js → chunk-UH3FPX3L.js} +2 -2
  215. package/dist/{chunk-GGJFRVRD.js → chunk-US7KPQA5.js} +2 -2
  216. package/dist/{chunk-FJ2VJTGA.js → chunk-UWRHHRDK.js} +3 -3
  217. package/dist/{chunk-4KLQI3L2.js → chunk-UXXK523X.js} +40 -2
  218. package/dist/chunk-UXXK523X.js.map +1 -0
  219. package/dist/{chunk-SDESKFKT.js → chunk-UYHDMCY2.js} +2 -2
  220. package/dist/{chunk-3T3VHXUH.js → chunk-VKILERSL.js} +2 -2
  221. package/dist/{chunk-2TGIKMMZ.js → chunk-WXDTOE6F.js} +2 -2
  222. package/dist/{chunk-KQLBXUUQ.js → chunk-WYG5UNDY.js} +2 -2
  223. package/dist/{chunk-DQEMWVMT.js → chunk-X7Y7WX73.js} +1 -1
  224. package/dist/{chunk-MPDLLLOG.js → chunk-XO23OKMK.js} +9 -5
  225. package/dist/chunk-XO23OKMK.js.map +1 -0
  226. package/dist/{chunk-WC475JOG.js → chunk-XU37EJTW.js} +24 -24
  227. package/dist/chunk-XU37EJTW.js.map +1 -0
  228. package/dist/{chunk-FZVROZKC.js → chunk-XVZA6VXC.js} +4 -4
  229. package/dist/{chunk-QA3Y2O6H.js → chunk-YAVIBVCU.js} +2 -2
  230. package/dist/{chunk-2XJZLSXE.js → chunk-YL4UKMD7.js} +124 -58
  231. package/dist/chunk-YL4UKMD7.js.map +1 -0
  232. package/dist/{chunk-VPEIWMUT.js → chunk-YLTU2DC4.js} +2 -2
  233. package/dist/{chunk-HWHNQVRV.js → chunk-YMJRLT7S.js} +28 -8
  234. package/dist/chunk-YMJRLT7S.js.map +1 -0
  235. package/dist/{chunk-EBOHFZI4.js → chunk-YRZPBN46.js} +3 -3
  236. package/dist/{chunk-FMFCQRDM.js → chunk-ZANNIXXE.js} +3 -3
  237. package/dist/{chunk-HQGATKG2.js → chunk-ZBFLV657.js} +3 -3
  238. package/dist/{chunk-6L63IN3M.js → chunk-ZOJUWVAU.js} +1 -1
  239. package/dist/cli.js +144 -141
  240. package/dist/coding/export-okf-codegraph.js +24 -24
  241. package/dist/coding/pre-action-gate.js +2 -2
  242. package/dist/compounding/engine.js +26 -26
  243. package/dist/config.js +35 -35
  244. package/dist/connectors/codex-materialize-runner.js +24 -24
  245. package/dist/connectors/index.js +28 -28
  246. package/dist/contradiction/index.js +4 -4
  247. package/dist/convergence-refresh.js +26 -26
  248. package/dist/corpus-watermark.js +28 -28
  249. package/dist/dashboard-runtime.js +2 -2
  250. package/dist/deep-recall-config.js +2 -2
  251. package/dist/deep-recall-http-glue.js +12 -12
  252. package/dist/entity-retrieval.js +25 -25
  253. package/dist/event-order-recall.js +2 -2
  254. package/dist/explicit-capture.js +6 -6
  255. package/dist/external-wiki-access.js +91 -89
  256. package/dist/extraction-evidence-class.js +2 -2
  257. package/dist/extraction-quote-grounding.d.ts +5 -1
  258. package/dist/extraction-quote-grounding.d.ts.map +1 -1
  259. package/dist/extraction-quote-grounding.js +1 -1
  260. package/dist/extraction-source-grounding-rules.d.ts +3 -1
  261. package/dist/extraction-source-grounding-rules.d.ts.map +1 -1
  262. package/dist/extraction-source-grounding-rules.js +2 -2
  263. package/dist/extraction-source-grounding.d.ts +1 -1
  264. package/dist/extraction-source-grounding.d.ts.map +1 -1
  265. package/dist/extraction-source-grounding.js +5 -5
  266. package/dist/extraction-span-materialize.js +2 -2
  267. package/dist/extraction.d.ts.map +1 -1
  268. package/dist/extraction.js +43 -43
  269. package/dist/{first-start-migration-QVD22WWS.js → first-start-migration-A6PHIBUQ.js} +4 -4
  270. package/dist/{graph-edge-decay-CXTNJ7IQ.js → graph-edge-decay-EKWXRBWF.js} +5 -5
  271. package/dist/graph-edge-decay-EKWXRBWF.js.map +1 -0
  272. package/dist/graph-jsonl.js +3 -3
  273. package/dist/graph-snapshot.js +3 -3
  274. package/dist/graph.js +2 -2
  275. package/dist/harmonic-retrieval.js +24 -24
  276. package/dist/index.js +255 -251
  277. package/dist/lcm/engine.js +2 -2
  278. package/dist/lcm/index.js +5 -5
  279. package/dist/local-llm.js +3 -3
  280. package/dist/location/index.js +28 -28
  281. package/dist/maintenance/memory-governance.js +24 -24
  282. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +24 -24
  283. package/dist/maintenance/rebuild-memory-projection-runner.js +26 -26
  284. package/dist/maintenance/rebuild-memory-projection.js +25 -25
  285. package/dist/memory-browse-http-glue.js +2 -2
  286. package/dist/memory-browse-operations.js +91 -89
  287. package/dist/memory-browse.js +5 -5
  288. package/dist/memory-subject.js +2 -2
  289. package/dist/namespaces/migrate.js +25 -25
  290. package/dist/namespaces/search.d.ts +9 -0
  291. package/dist/namespaces/search.d.ts.map +1 -1
  292. package/dist/namespaces/search.js +13 -7
  293. package/dist/namespaces/storage.js +24 -24
  294. package/dist/offline-sync-changeset-generations.js +3 -3
  295. package/dist/offline-sync-embedding-generation.js +2 -2
  296. package/dist/offline-sync-impression-drain.js +2 -2
  297. package/dist/offline-sync-snapshot-stream.js +6 -6
  298. package/dist/offline-sync.js +5 -5
  299. package/dist/operator-doctor-corpus.js +31 -31
  300. package/dist/operator-doctor-replica.js +4 -4
  301. package/dist/operator-toolkit.js +49 -49
  302. package/dist/orchestration/backlog-scheduler.d.ts +3 -2
  303. package/dist/orchestration/backlog-scheduler.d.ts.map +1 -1
  304. package/dist/orchestration/backlog-scheduler.js +209 -205
  305. package/dist/orchestration/backlog-scheduler.js.map +1 -1
  306. package/dist/orchestration/buffer-drain-runner.d.ts +6 -1
  307. package/dist/orchestration/buffer-drain-runner.d.ts.map +1 -1
  308. package/dist/orchestration/buffer-drain-runner.js +199 -195
  309. package/dist/orchestration/extraction-persist.d.ts.map +1 -1
  310. package/dist/orchestration/fact-dedup-window.d.ts +1 -4
  311. package/dist/orchestration/fact-dedup-window.d.ts.map +1 -1
  312. package/dist/orchestration/maintenance.js +41 -41
  313. package/dist/orchestration/turn-ingestion.d.ts +2 -9
  314. package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
  315. package/dist/orchestrator.js +198 -194
  316. package/dist/provenance.js +2 -2
  317. package/dist/qmd.js +4 -4
  318. package/dist/recall-navigation-http-glue.js +2 -2
  319. package/dist/recall-navigation-operations.js +91 -89
  320. package/dist/recall-navigation.js +90 -88
  321. package/dist/recall-state-view-admission.js +5 -5
  322. package/dist/recall-why-service.js +5 -5
  323. package/dist/reconcile/manifest.js +2 -2
  324. package/dist/replica-divergence.js +3 -3
  325. package/dist/{resolution-BNWE5Y25.js → resolution-23ZBDM6A.js} +5 -5
  326. package/dist/resolution-23ZBDM6A.js.map +1 -0
  327. package/dist/resume-bundles.js +36 -36
  328. package/dist/salvage-envelope.js +4 -4
  329. package/dist/search/factory.js +6 -6
  330. package/dist/search/index.js +12 -12
  331. package/dist/semantic-consolidation.js +25 -25
  332. package/dist/semantic-rule-promotion.js +24 -24
  333. package/dist/semantic-rule-verifier.js +24 -24
  334. package/dist/source-agent-qualifier.d.ts +2 -0
  335. package/dist/source-agent-qualifier.d.ts.map +1 -1
  336. package/dist/source-agent-qualifier.js +36 -36
  337. package/dist/standup/build.js +25 -25
  338. package/dist/storage/buffer-drain-claims.d.ts +18 -6
  339. package/dist/storage/buffer-drain-claims.d.ts.map +1 -1
  340. package/dist/storage/buffer-drain-claims.js +2 -2
  341. package/dist/storage/buffer-session-files.js +2 -2
  342. package/dist/storage/content-hash-index.d.ts +8 -0
  343. package/dist/storage/content-hash-index.d.ts.map +1 -1
  344. package/dist/storage.d.ts +10 -1
  345. package/dist/storage.d.ts.map +1 -1
  346. package/dist/storage.js +23 -23
  347. package/dist/subject-config.js +3 -3
  348. package/dist/summarizer.js +4 -4
  349. package/dist/support-passport/index.js +93 -91
  350. package/dist/tokens.js +3 -3
  351. package/dist/transfer/autodetect.js +1 -1
  352. package/dist/transfer/backup.js +2 -2
  353. package/dist/transfer/capsule-export.js +3 -3
  354. package/dist/transfer/capsule-gunzip.d.ts +14 -0
  355. package/dist/transfer/capsule-gunzip.d.ts.map +1 -0
  356. package/dist/transfer/capsule-import.d.ts.map +1 -1
  357. package/dist/transfer/capsule-import.js +3 -2
  358. package/dist/transfer/capsule-merge.d.ts.map +1 -1
  359. package/dist/transfer/export-okf.js +24 -24
  360. package/dist/verified-recall.js +24 -24
  361. package/dist/who-knows-cli.js +26 -26
  362. package/dist/who-knows.js +25 -25
  363. package/dist/write-envelope.js +3 -3
  364. package/package.json +2 -2
  365. package/src/access-boundary.ts +15 -0
  366. package/src/access-errors.ts +22 -0
  367. package/src/access-fleet-wide-work-peer.test.ts +236 -0
  368. package/src/access-http-peer-fleet-wide.test.ts +192 -0
  369. package/src/access-http.test.ts +249 -4
  370. package/src/access-http.ts +114 -39
  371. package/src/access-mcp-frames.test.ts +47 -0
  372. package/src/access-mcp-frames.ts +74 -0
  373. package/src/access-mcp.test.ts +210 -0
  374. package/src/access-mcp.ts +47 -37
  375. package/src/access-observe-replay-durability.test.ts +40 -4
  376. package/src/access-observe-write-surface.ts +4 -4
  377. package/src/access-operations-batch.ts +23 -12
  378. package/src/access-proto-injection.test.ts +340 -0
  379. package/src/access-schema.ts +31 -5
  380. package/src/access-service-capsule-import.test.ts +235 -0
  381. package/src/access-service.ts +6 -45
  382. package/src/backlog-scheduler.test.ts +443 -3
  383. package/src/briefing-deadline.ts +5 -4
  384. package/src/buffer-admission.ts +45 -0
  385. package/src/buffer-eviction-backpressure.test.ts +211 -0
  386. package/src/buffer-eviction.ts +8 -14
  387. package/src/buffer-incremental-persistence.test.ts +25 -37
  388. package/src/buffer-persistence.ts +15 -6
  389. package/src/buffer-session.test.ts +32 -16
  390. package/src/buffer-turn-helpers.ts +47 -0
  391. package/src/buffer-types.ts +28 -1
  392. package/src/buffer.ts +83 -91
  393. package/src/capsule-import-gate.ts +109 -0
  394. package/src/cli.ts +4 -4
  395. package/src/extraction-quote-grounding.test.ts +125 -0
  396. package/src/extraction-quote-grounding.ts +99 -25
  397. package/src/extraction-source-grounding-rules.ts +6 -1
  398. package/src/extraction-source-grounding.ts +2 -0
  399. package/src/extraction.ts +3 -4
  400. package/src/fact-dedup-window.test.ts +103 -20
  401. package/src/namespaces/search.test.ts +480 -1
  402. package/src/namespaces/search.ts +93 -10
  403. package/src/orchestration/backlog-scheduler.ts +13 -12
  404. package/src/orchestration/buffer-drain-runner.ts +40 -16
  405. package/src/orchestration/extraction-persist.ts +2 -10
  406. package/src/orchestration/fact-dedup-window.ts +5 -12
  407. package/src/orchestration/turn-ingestion.ts +38 -39
  408. package/src/recall-navigation-operations.ts +9 -2
  409. package/src/source-agent-qualifier.ts +3 -0
  410. package/src/storage/buffer-drain-claims.test.ts +330 -6
  411. package/src/storage/buffer-drain-claims.ts +226 -80
  412. package/src/storage/buffer-session-files.ts +2 -2
  413. package/src/storage/content-hash-index.ts +9 -0
  414. package/src/storage.ts +15 -0
  415. package/src/transfer/capsule-gunzip.ts +39 -0
  416. package/src/transfer/capsule-import.test.ts +73 -0
  417. package/src/transfer/capsule-import.ts +2 -2
  418. package/src/transfer/capsule-merge.test.ts +34 -0
  419. package/src/transfer/capsule-merge.ts +2 -10
  420. package/dist/capsule-merge-XVTQJJ6R.js.map +0 -1
  421. package/dist/chunk-2XJZLSXE.js.map +0 -1
  422. package/dist/chunk-3JPTUCD7.js.map +0 -1
  423. package/dist/chunk-4KLQI3L2.js.map +0 -1
  424. package/dist/chunk-6LCWKS62.js.map +0 -1
  425. package/dist/chunk-77R7BQZ2.js.map +0 -1
  426. package/dist/chunk-7PMOCQOL.js.map +0 -1
  427. package/dist/chunk-B74NPUHT.js.map +0 -1
  428. package/dist/chunk-D5SSBCYY.js.map +0 -1
  429. package/dist/chunk-HLKUZW42.js.map +0 -1
  430. package/dist/chunk-HWHNQVRV.js.map +0 -1
  431. package/dist/chunk-KASJJGRG.js.map +0 -1
  432. package/dist/chunk-KQUSP6LB.js.map +0 -1
  433. package/dist/chunk-LGEH473S.js.map +0 -1
  434. package/dist/chunk-LU66I56Q.js.map +0 -1
  435. package/dist/chunk-MPDLLLOG.js.map +0 -1
  436. package/dist/chunk-MPGLM2VU.js.map +0 -1
  437. package/dist/chunk-QZBTIYKD.js.map +0 -1
  438. package/dist/chunk-VLMBSGQF.js.map +0 -1
  439. package/dist/chunk-WAD32CZG.js.map +0 -1
  440. package/dist/chunk-WC475JOG.js.map +0 -1
  441. package/dist/chunk-X2KO4FK5.js.map +0 -1
  442. package/dist/chunk-XYP5WBDO.js.map +0 -1
  443. package/dist/chunk-YK7GOPIE.js.map +0 -1
  444. package/dist/chunk-Z3DDQXM4.js.map +0 -1
  445. /package/dist/{capsule-crypto-YO5QJ6L3.js.map → access-mcp-frames.js.map} +0 -0
  446. /package/dist/{auto-sync-C7E6G7Y2.js.map → auto-sync-3AZW7MXI.js.map} +0 -0
  447. /package/dist/{graph-edge-decay-CXTNJ7IQ.js.map → buffer-admission.js.map} +0 -0
  448. /package/dist/{resolution-BNWE5Y25.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
  449. /package/dist/{chunk-MSIRVXQ3.js.map → chunk-2G4TV2ML.js.map} +0 -0
  450. /package/dist/{chunk-IR4C3DP2.js.map → chunk-2K3AUDH7.js.map} +0 -0
  451. /package/dist/{chunk-2IW5PT7U.js.map → chunk-2N4OUSIA.js.map} +0 -0
  452. /package/dist/{chunk-BXLOS5AJ.js.map → chunk-2NLLXCJG.js.map} +0 -0
  453. /package/dist/{chunk-QZSIDSB2.js.map → chunk-2NUDHVYM.js.map} +0 -0
  454. /package/dist/{chunk-HPFVZPRH.js.map → chunk-2U4LMSEX.js.map} +0 -0
  455. /package/dist/{chunk-WHQXWFH7.js.map → chunk-2XOFVTTT.js.map} +0 -0
  456. /package/dist/{chunk-I6ORDT54.js.map → chunk-32CA7B6L.js.map} +0 -0
  457. /package/dist/{chunk-T77AX6DG.js.map → chunk-32YX7EJ3.js.map} +0 -0
  458. /package/dist/{chunk-HLWXCDUQ.js.map → chunk-3DAWHP5K.js.map} +0 -0
  459. /package/dist/{chunk-ZJJFUEPO.js.map → chunk-3PJEDHLD.js.map} +0 -0
  460. /package/dist/{chunk-PJYK7HH2.js.map → chunk-3UZ2VMKD.js.map} +0 -0
  461. /package/dist/{chunk-IN4OFAHX.js.map → chunk-3Y5X6YFH.js.map} +0 -0
  462. /package/dist/{chunk-YUTT4ZTI.js.map → chunk-45VUHDV2.js.map} +0 -0
  463. /package/dist/{chunk-T6T2RMWW.js.map → chunk-4GMI5HFH.js.map} +0 -0
  464. /package/dist/{chunk-YOEYL6CH.js.map → chunk-4LCVA2LW.js.map} +0 -0
  465. /package/dist/{chunk-66JOLMWS.js.map → chunk-4X7HQKAG.js.map} +0 -0
  466. /package/dist/{chunk-65SKM4BF.js.map → chunk-4XYAWK5H.js.map} +0 -0
  467. /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
  468. /package/dist/{chunk-TPX7XXY4.js.map → chunk-5SOSQTVE.js.map} +0 -0
  469. /package/dist/{chunk-G3T6YMBO.js.map → chunk-64S6X4IU.js.map} +0 -0
  470. /package/dist/{chunk-BK7AJUWB.js.map → chunk-6BCWFCDG.js.map} +0 -0
  471. /package/dist/{chunk-V27FK6D6.js.map → chunk-6XTUZKCZ.js.map} +0 -0
  472. /package/dist/{chunk-LECSS7EE.js.map → chunk-73UZZFJC.js.map} +0 -0
  473. /package/dist/{chunk-4EACCIPO.js.map → chunk-77PDJ4OE.js.map} +0 -0
  474. /package/dist/{chunk-GQZSGXR6.js.map → chunk-7GSL4LBG.js.map} +0 -0
  475. /package/dist/{chunk-B6SPPYGJ.js.map → chunk-7IWRLYFJ.js.map} +0 -0
  476. /package/dist/{chunk-R45L4TV6.js.map → chunk-7LCGFUMB.js.map} +0 -0
  477. /package/dist/{chunk-A7BM2JTH.js.map → chunk-AJHNXIQU.js.map} +0 -0
  478. /package/dist/{chunk-2D7BTSDW.js.map → chunk-ANZZLRUX.js.map} +0 -0
  479. /package/dist/{chunk-ZXF3O7P4.js.map → chunk-ARRTLPRC.js.map} +0 -0
  480. /package/dist/{chunk-S5CQI2KF.js.map → chunk-B3GKQ6DU.js.map} +0 -0
  481. /package/dist/{chunk-XRBHIR7Q.js.map → chunk-B56LORLY.js.map} +0 -0
  482. /package/dist/{chunk-KO3YBHOV.js.map → chunk-BIVKPQ6Q.js.map} +0 -0
  483. /package/dist/{chunk-54FDG7K7.js.map → chunk-C2HORX76.js.map} +0 -0
  484. /package/dist/{chunk-E33UJHY3.js.map → chunk-CN7Q3T2J.js.map} +0 -0
  485. /package/dist/{chunk-YUTG35M4.js.map → chunk-CYCEROPR.js.map} +0 -0
  486. /package/dist/{chunk-TFLPDNQV.js.map → chunk-D5EV6Y2L.js.map} +0 -0
  487. /package/dist/{chunk-BFBS7OFQ.js.map → chunk-DC55RIEZ.js.map} +0 -0
  488. /package/dist/{chunk-B4ZU34JQ.js.map → chunk-DT7SNJH6.js.map} +0 -0
  489. /package/dist/{chunk-FVRTCYZ3.js.map → chunk-EA2LA5RI.js.map} +0 -0
  490. /package/dist/{chunk-WBN66HPI.js.map → chunk-ELHJK7LM.js.map} +0 -0
  491. /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
  492. /package/dist/{chunk-YAUHSNPB.js.map → chunk-F74G4KIH.js.map} +0 -0
  493. /package/dist/{chunk-BV2BRHD4.js.map → chunk-FHGWKODN.js.map} +0 -0
  494. /package/dist/{chunk-KESJ5XX3.js.map → chunk-G54BOKZ6.js.map} +0 -0
  495. /package/dist/{chunk-K36RMINQ.js.map → chunk-GOZXQNIM.js.map} +0 -0
  496. /package/dist/{chunk-OQSSQOKQ.js.map → chunk-GULUWOA5.js.map} +0 -0
  497. /package/dist/{chunk-K6RTQZA4.js.map → chunk-HFUKXOUW.js.map} +0 -0
  498. /package/dist/{chunk-WV254W4V.js.map → chunk-HLN2HOXX.js.map} +0 -0
  499. /package/dist/{chunk-FUOAIJKQ.js.map → chunk-IBDWYRDR.js.map} +0 -0
  500. /package/dist/{chunk-AJG2TAMR.js.map → chunk-IEEV27BC.js.map} +0 -0
  501. /package/dist/{chunk-LTDLJCMQ.js.map → chunk-IJ3OEECW.js.map} +0 -0
  502. /package/dist/{chunk-BK4FBHZ3.js.map → chunk-IPD5GC7C.js.map} +0 -0
  503. /package/dist/{chunk-KFLD7ZZG.js.map → chunk-IUKYFTGW.js.map} +0 -0
  504. /package/dist/{chunk-GCAQ3VUJ.js.map → chunk-JM2YZKCQ.js.map} +0 -0
  505. /package/dist/{chunk-BNQJKWWZ.js.map → chunk-K3FH3LAQ.js.map} +0 -0
  506. /package/dist/{chunk-RZH5RVJC.js.map → chunk-MTTYIXSU.js.map} +0 -0
  507. /package/dist/{chunk-XHGYZRVW.js.map → chunk-NIVKG3DS.js.map} +0 -0
  508. /package/dist/{chunk-VZELUMKK.js.map → chunk-NQP4ZYGG.js.map} +0 -0
  509. /package/dist/{chunk-YO2JEX2Z.js.map → chunk-OGBQMY46.js.map} +0 -0
  510. /package/dist/{chunk-TLHF75OR.js.map → chunk-OL553AWT.js.map} +0 -0
  511. /package/dist/{chunk-CLVILSBR.js.map → chunk-OZNBZJ3S.js.map} +0 -0
  512. /package/dist/{chunk-VUROWXFL.js.map → chunk-P452SEKO.js.map} +0 -0
  513. /package/dist/{chunk-JX3E4XCW.js.map → chunk-PDOOUXCS.js.map} +0 -0
  514. /package/dist/{chunk-6QYPL6NI.js.map → chunk-PKZ274PI.js.map} +0 -0
  515. /package/dist/{chunk-KJCL2FZO.js.map → chunk-QD5R6U5J.js.map} +0 -0
  516. /package/dist/{chunk-JRVMCZE3.js.map → chunk-QEYTRFR3.js.map} +0 -0
  517. /package/dist/{chunk-VKDAJJQP.js.map → chunk-QEZ3DW5O.js.map} +0 -0
  518. /package/dist/{chunk-WYPCJK6K.js.map → chunk-QKERFEJS.js.map} +0 -0
  519. /package/dist/{chunk-BDN3TDGV.js.map → chunk-QKES7GU6.js.map} +0 -0
  520. /package/dist/{chunk-PCQWKMOL.js.map → chunk-QNSZYTSG.js.map} +0 -0
  521. /package/dist/{chunk-IZJQNDGI.js.map → chunk-R6HMAIKM.js.map} +0 -0
  522. /package/dist/{chunk-NUSAAN75.js.map → chunk-ROKDHO74.js.map} +0 -0
  523. /package/dist/{chunk-FF6N7BXC.js.map → chunk-S45INSV3.js.map} +0 -0
  524. /package/dist/{chunk-2RUTN7PR.js.map → chunk-S7GJNA3K.js.map} +0 -0
  525. /package/dist/{chunk-XT45WCKX.js.map → chunk-SYKJBS7S.js.map} +0 -0
  526. /package/dist/{chunk-5BE3FLKL.js.map → chunk-TAUHAP5J.js.map} +0 -0
  527. /package/dist/{chunk-RJWL3RHB.js.map → chunk-TFEHSPGQ.js.map} +0 -0
  528. /package/dist/{chunk-K3B72DKJ.js.map → chunk-TRMZHGIX.js.map} +0 -0
  529. /package/dist/{chunk-WBDIZWJQ.js.map → chunk-TSS3D7ZY.js.map} +0 -0
  530. /package/dist/{chunk-HQPFACXO.js.map → chunk-UH3FPX3L.js.map} +0 -0
  531. /package/dist/{chunk-GGJFRVRD.js.map → chunk-US7KPQA5.js.map} +0 -0
  532. /package/dist/{chunk-FJ2VJTGA.js.map → chunk-UWRHHRDK.js.map} +0 -0
  533. /package/dist/{chunk-SDESKFKT.js.map → chunk-UYHDMCY2.js.map} +0 -0
  534. /package/dist/{chunk-3T3VHXUH.js.map → chunk-VKILERSL.js.map} +0 -0
  535. /package/dist/{chunk-2TGIKMMZ.js.map → chunk-WXDTOE6F.js.map} +0 -0
  536. /package/dist/{chunk-KQLBXUUQ.js.map → chunk-WYG5UNDY.js.map} +0 -0
  537. /package/dist/{chunk-DQEMWVMT.js.map → chunk-X7Y7WX73.js.map} +0 -0
  538. /package/dist/{chunk-FZVROZKC.js.map → chunk-XVZA6VXC.js.map} +0 -0
  539. /package/dist/{chunk-QA3Y2O6H.js.map → chunk-YAVIBVCU.js.map} +0 -0
  540. /package/dist/{chunk-VPEIWMUT.js.map → chunk-YLTU2DC4.js.map} +0 -0
  541. /package/dist/{chunk-EBOHFZI4.js.map → chunk-YRZPBN46.js.map} +0 -0
  542. /package/dist/{chunk-FMFCQRDM.js.map → chunk-ZANNIXXE.js.map} +0 -0
  543. /package/dist/{chunk-HQGATKG2.js.map → chunk-ZBFLV657.js.map} +0 -0
  544. /package/dist/{chunk-6L63IN3M.js.map → chunk-ZOJUWVAU.js.map} +0 -0
  545. /package/dist/{first-start-migration-QVD22WWS.js.map → first-start-migration-A6PHIBUQ.js.map} +0 -0
@@ -10,12 +10,16 @@ import assert from "node:assert/strict";
10
10
  import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
11
11
  import os from "node:os";
12
12
  import path from "node:path";
13
+ import { PassThrough } from "node:stream";
13
14
  import test from "node:test";
14
15
 
15
16
  import { EngramMcpServer } from "./access-mcp.js";
17
+ import { MCP_STDIO_MAX_FRAME_BYTES } from "./access-mcp-frames.js";
18
+ import { EngramAccessForbiddenError } from "./access-errors.js";
16
19
  import { tokenCapabilityStore } from "./access-token-capabilities.js";
17
20
  import { EngramAccessInputError, type EngramAccessService } from "./access-service.js";
18
21
  import { parseConfig } from "./config.js";
22
+ import { initLogger, resetLogger } from "./logger.js";
19
23
  import { readPair, writePair } from "./contradiction/contradiction-review.js";
20
24
  import type { StorageManager } from "./storage.js";
21
25
 
@@ -105,6 +109,46 @@ function makeToolRequest(name: string, args: Record<string, unknown> = {}): Reco
105
109
  },
106
110
  };
107
111
  }
112
+ test("MCP reports buffer admission refusal as retryable and does not return success", async () => {
113
+ let attempts = 0;
114
+ const refusal = new Error("buffer at session-entry cap");
115
+ refusal.name = "ReplayAdmissionRefusedError";
116
+ // SAFETY: the mock overrides only observe; every other MCP dispatch path is unreachable in this test.
117
+ const service = Object.assign({} as EngramAccessService, {
118
+ observe: async () => {
119
+ attempts += 1;
120
+ if (attempts === 1) throw refusal;
121
+ return { ok: true };
122
+ },
123
+ });
124
+ const server = new EngramMcpServer(service);
125
+ const request = makeToolRequest("engram.observe", {
126
+ sessionKey: "capacity-test-session",
127
+ messages: [{ role: "user", content: "retry me" }],
128
+ });
129
+
130
+ const refused = await server.handleRequest(request);
131
+ // SAFETY: a tools/call request always resolves to a JSON-RPC result envelope; only isError and structuredContent are asserted.
132
+ const refusedResult = refused as {
133
+ result?: {
134
+ isError?: boolean;
135
+ structuredContent?: { code?: string; retryable?: boolean };
136
+ };
137
+ };
138
+ assert.equal(refusedResult.result?.isError, true, "refused admission must not be acknowledged as success");
139
+ // SAFETY: same tools/call result envelope as the assertion above.
140
+ assert.deepEqual(refusedResult.result?.structuredContent, {
141
+ code: "buffer_admission_refused",
142
+ retryable: true,
143
+ }, JSON.stringify(refused));
144
+
145
+ const retried = await server.handleRequest(request);
146
+ // SAFETY: same tools/call result envelope as refusedResult above.
147
+ const retriedResult = retried as typeof refusedResult;
148
+ assert.equal(retriedResult.result?.isError, false);
149
+ assert.equal(attempts, 2, "the same request can be retried after capacity is available");
150
+ });
151
+
108
152
 
109
153
  // ──────────────────────────────────────────────────────────────────────────
110
154
  // Finding 2 (#396 new): invalid MCP briefing format values must be rejected
@@ -1291,6 +1335,17 @@ const FLEET_WIDE_MAINTENANCE_TOOLS = [
1291
1335
  "compounding_promote_candidate",
1292
1336
  "compression_guidelines_optimize",
1293
1337
  "compression_guidelines_activate",
1338
+ // Issue #3225 B3: work and peer ops run against GLOBAL stores, so a
1339
+ // namespace argument cannot isolate them — same fleet-wide rule.
1340
+ "work_task",
1341
+ "work_project",
1342
+ "work_board",
1343
+ "peer_list",
1344
+ "peer_get",
1345
+ "peer_set",
1346
+ "peer_delete",
1347
+ "peer_profile_get",
1348
+ "peer_forget",
1294
1349
  ] as const;
1295
1350
 
1296
1351
  test("MCP fleet-wide maintenance ops: namespace-scoped token denied for every op, no side effect (issue #1850 round 10)", async () => {
@@ -1315,6 +1370,19 @@ test("MCP fleet-wide maintenance ops: namespace-scoped token denied for every op
1315
1370
  compressionGuidelinesOptimize: track,
1316
1371
  compressionGuidelinesActivate: track,
1317
1372
  } as unknown as EngramAccessService;
1373
+ // Issue #3225 B3: track the global-store methods the fleet-wide guard must
1374
+ // block; the dispatch reads them off this same object at call time.
1375
+ Object.assign(service, {
1376
+ workTask: track,
1377
+ workProject: track,
1378
+ workBoard: track,
1379
+ peerList: track,
1380
+ peerGet: track,
1381
+ peerSet: track,
1382
+ peerDelete: track,
1383
+ peerProfileGet: track,
1384
+ peerForget: track,
1385
+ });
1318
1386
  const server = new EngramMcpServer(service, { principal: "ops" });
1319
1387
  try {
1320
1388
  for (const tool of FLEET_WIDE_MAINTENANCE_TOOLS) {
@@ -1451,3 +1519,145 @@ test("MCP external_wiki_search rejects an empty query before service dispatch",
1451
1519
  assert.equal(readMcpResult(response).isError, true);
1452
1520
  assert.equal(configRead, false);
1453
1521
  });
1522
+
1523
+ // ===========================================================================
1524
+ // Issue #3225 C5 — tools/call catch-all must not leak unexpected Error.message
1525
+ // (paths from fs/backend). Intended typed access errors stay readable.
1526
+ // ===========================================================================
1527
+
1528
+ function serviceWithRecallThrow(error: Error): EngramAccessService {
1529
+ const service = makeMockService();
1530
+ service.recall = async () => {
1531
+ throw error;
1532
+ };
1533
+ return service;
1534
+ }
1535
+
1536
+ test("MCP tools/call hides unexpected Error.message paths behind internal error", async () => {
1537
+ const secretPath = "/home/secret/vault/token.pem";
1538
+ const service = serviceWithRecallThrow(
1539
+ new Error(`ENOENT: no such file or directory, open '${secretPath}'`),
1540
+ );
1541
+
1542
+ const logLines: string[] = [];
1543
+ initLogger(
1544
+ {
1545
+ info() {},
1546
+ warn() {},
1547
+ error(msg: string) {
1548
+ logLines.push(msg);
1549
+ },
1550
+ },
1551
+ false,
1552
+ { timestamps: false },
1553
+ );
1554
+ try {
1555
+ const server = new EngramMcpServer(service);
1556
+ const outcome = readCallToolOutcome(
1557
+ await server.handleRequest(makeToolRequest("engram.recall", { query: "anything" })),
1558
+ );
1559
+ assert.equal(outcome.isError, true);
1560
+ assert.equal(outcome.text, "internal error");
1561
+ assert.equal(outcome.text.includes(secretPath), false);
1562
+ assert.ok(
1563
+ logLines.some((line) => line.includes(secretPath)),
1564
+ "server log must retain the unexpected detail",
1565
+ );
1566
+ } finally {
1567
+ resetLogger();
1568
+ }
1569
+ });
1570
+
1571
+ test("MCP tools/call keeps intended EngramAccessInputError and ForbiddenError messages", async () => {
1572
+ const inputOutcome = readCallToolOutcome(
1573
+ await new EngramMcpServer(serviceWithRecallThrow(new EngramAccessInputError("memory not found: mem-42"))).handleRequest(
1574
+ makeToolRequest("engram.recall", { query: "anything" }),
1575
+ ),
1576
+ );
1577
+ assert.equal(inputOutcome.isError, true);
1578
+ assert.equal(inputOutcome.text, "memory not found: mem-42");
1579
+
1580
+ const forbiddenOutcome = readCallToolOutcome(
1581
+ await new EngramMcpServer(
1582
+ serviceWithRecallThrow(new EngramAccessForbiddenError("token is not permitted to call operation: recall")),
1583
+ ).handleRequest(makeToolRequest("engram.recall", { query: "anything" })),
1584
+ );
1585
+ assert.equal(forbiddenOutcome.isError, true);
1586
+ assert.equal(forbiddenOutcome.text, "token is not permitted to call operation: recall");
1587
+ });
1588
+
1589
+ // ===========================================================================
1590
+ // Issue #3225 N1 — stdio framing is bounded, refuses an oversized frame,
1591
+ // and keeps answering the frames that follow it.
1592
+ // ===========================================================================
1593
+
1594
+ type StdioMessage = { id?: number | null; result?: object; error?: { message: string } };
1595
+ type StdioRequest = { jsonrpc: "2.0"; id: number; method: string };
1596
+
1597
+ function stdioFrame(payload: StdioRequest): string {
1598
+ const body = JSON.stringify(payload);
1599
+ return `Content-Length: ${Buffer.byteLength(body, "utf-8")}\r\n\r\n${body}`;
1600
+ }
1601
+
1602
+ function readStdioMessages(raw: string): StdioMessage[] {
1603
+ const messages: StdioMessage[] = [];
1604
+ let rest = raw;
1605
+ while (rest.length > 0) {
1606
+ const headerEnd = rest.indexOf("\r\n\r\n");
1607
+ assert.ok(headerEnd >= 0, "server output must be Content-Length framed");
1608
+ const length = Number.parseInt(rest.slice(0, headerEnd).split(":")[1] ?? "", 10);
1609
+ const message: StdioMessage = JSON.parse(rest.slice(headerEnd + 4, headerEnd + 4 + length));
1610
+ messages.push(message);
1611
+ rest = rest.slice(headerEnd + 4 + length);
1612
+ }
1613
+ return messages;
1614
+ }
1615
+
1616
+ /** Feeds each chunk in its own tick so the server sees separate data events. */
1617
+ async function runStdioChunks(chunks: string[]): Promise<StdioMessage[]> {
1618
+ const input = new PassThrough();
1619
+ const output = new PassThrough();
1620
+ output.setEncoding("utf8");
1621
+ let raw = "";
1622
+ output.on("data", (chunk: string) => {
1623
+ raw += chunk;
1624
+ });
1625
+ const running = new EngramMcpServer(makeMockService()).runStdio(input, output);
1626
+ for (const chunk of chunks) {
1627
+ input.write(chunk);
1628
+ await new Promise<void>((resolve) => setImmediate(resolve));
1629
+ }
1630
+ input.end();
1631
+ await running;
1632
+ return readStdioMessages(raw);
1633
+ }
1634
+
1635
+ const stdioPing = (id: number) => stdioFrame({ jsonrpc: "2.0", id, method: "ping" });
1636
+
1637
+ test("MCP stdio refuses an oversized Content-Length, discards its body, and answers the next frame", async () => {
1638
+ const declared = MCP_STDIO_MAX_FRAME_BYTES + 1;
1639
+ const messages = await runStdioChunks([
1640
+ `Content-Length: ${declared}\r\n\r\n`,
1641
+ "x".repeat(1000),
1642
+ "x".repeat(declared - 1000),
1643
+ stdioPing(7),
1644
+ ]);
1645
+ assert.equal(messages.length, 2);
1646
+ assert.match(messages[0]?.error?.message ?? "", /frame too large/);
1647
+ assert.equal(messages[1]?.id, 7);
1648
+ assert.ok(messages[1]?.result, "the frame after the refused one is answered normally");
1649
+ });
1650
+
1651
+ test("MCP stdio refuses a header that never terminates and answers the next frame", async () => {
1652
+ const messages = await runStdioChunks(["A".repeat(MCP_STDIO_MAX_FRAME_BYTES + 1), stdioPing(8)]);
1653
+ assert.equal(messages.length, 2);
1654
+ assert.match(messages[0]?.error?.message ?? "", /frame too large/);
1655
+ assert.equal(messages[1]?.id, 8);
1656
+ });
1657
+
1658
+ test("MCP stdio reassembles a frame split inside its header and inside its body", async () => {
1659
+ const frame = stdioPing(9);
1660
+ const messages = await runStdioChunks([frame.slice(0, 6), frame.slice(6, frame.length - 4), frame.slice(frame.length - 4)]);
1661
+ assert.equal(messages.length, 1);
1662
+ assert.equal(messages[0]?.id, 9);
1663
+ });
package/src/access-mcp.ts CHANGED
@@ -30,7 +30,9 @@ import {
30
30
  validateRequest,
31
31
  } from "./access-schema.js";
32
32
  import { EngramAccessInputError, type EngramAccessRecallResponse, type EngramAccessService } from "./access-service.js";
33
- import { unknownToolError } from "./access-errors.js";
33
+ import { EngramAccessForbiddenError, unknownToolError } from "./access-errors.js";
34
+ import { log } from "./logger.js";
35
+ import { McpFrameReader } from "./access-mcp-frames.js";
34
36
  import "./access-operations.js";
35
37
  import { validateBriefingFormat } from "./briefing.js";
36
38
  import { processChatMessage } from "./chat/chat-factory.js";
@@ -82,12 +84,32 @@ type JsonRpcRequest = {
82
84
  params?: Record<string, unknown>;
83
85
  };
84
86
 
87
+ /** tools/call failure envelope; admission backpressure adds a retryable code. */
88
+ type McpToolErrorResult = {
89
+ content: Array<{ type: string; text: string }>;
90
+ isError: boolean;
91
+ structuredContent?: { code: string; retryable: boolean };
92
+ };
93
+
85
94
  function throwMcpAbort(signal: AbortSignal | undefined, message: string): void {
86
95
  if (!signal?.aborted) return;
87
96
  if (isAbortError(signal.reason)) throw signal.reason;
88
97
  throw abortError(message);
89
98
  }
90
99
 
100
+ /**
101
+ * Map tools/call failures to a client-safe message. Unexpected backend/fs
102
+ * errors become a stable generic string; detail stays in the server log.
103
+ * Intended EngramAccessInputError / EngramAccessForbiddenError stay readable.
104
+ */
105
+ function mcpToolErrorMessage(err: Error): string {
106
+ if (err instanceof EngramAccessInputError || err instanceof EngramAccessForbiddenError) {
107
+ return err.message;
108
+ }
109
+ log.error("engram MCP tools/call internal error", err);
110
+ return "internal error";
111
+ }
112
+
91
113
  type McpRequestOptions = {
92
114
  principalOverride?: string;
93
115
  namespaceOverride?: string;
@@ -406,7 +428,7 @@ function parseMcpRequest<N extends SchemaName>(schemaName: N, args: Record<strin
406
428
  function optionalNonEmptyString(value: unknown, label: string): string | undefined {
407
429
  if (value === undefined || value === null || value === "") return undefined;
408
430
  if (typeof value !== "string") {
409
- throw new Error(`${label} expects a string; got ${JSON.stringify(value)}`);
431
+ throw new EngramAccessInputError(`${label} expects a string; got ${JSON.stringify(value)}`);
410
432
  }
411
433
  return value;
412
434
  }
@@ -420,11 +442,11 @@ function optionalNonEmptyString(value: unknown, label: string): string | undefin
420
442
  function optionalPositiveInteger(value: unknown, label: string): number | undefined {
421
443
  if (value === undefined || value === null || value === "") return undefined;
422
444
  if (typeof value !== "number" && typeof value !== "string") {
423
- throw new Error(`${label} expects a positive integer; got ${JSON.stringify(value)}`);
445
+ throw new EngramAccessInputError(`${label} expects a positive integer; got ${JSON.stringify(value)}`);
424
446
  }
425
447
  const parsed = typeof value === "number" ? value : Number(value);
426
448
  if (!Number.isFinite(parsed) || parsed <= 0 || !Number.isInteger(parsed)) {
427
- throw new Error(`${label} expects a positive integer; got ${JSON.stringify(value)}`);
449
+ throw new EngramAccessInputError(`${label} expects a positive integer; got ${JSON.stringify(value)}`);
428
450
  }
429
451
  return parsed;
430
452
  }
@@ -447,7 +469,7 @@ async function getMcpServerVersion(): Promise<string> {
447
469
  }
448
470
 
449
471
  export class EngramMcpServer {
450
- private buffer = Buffer.alloc(0);
472
+ private readonly frames = new McpFrameReader();
451
473
  private flushTask: Promise<void> | null = null;
452
474
  private readonly tools: McpTool[];
453
475
  private readonly resources: McpResource[];
@@ -2402,15 +2424,18 @@ export class EngramMcpServer {
2402
2424
  // Cancellation is transport control flow, not a JSON-RPC tool error.
2403
2425
  // Preserve the original AbortError so HTTP can silently end a dead socket.
2404
2426
  if (isAbortError(err)) throw err;
2405
- const message = err instanceof Error ? err.message : String(err);
2406
- return {
2407
- jsonrpc: "2.0",
2408
- id,
2409
- result: {
2410
- content: [{ type: "text", text: message }],
2411
- isError: true,
2412
- },
2427
+ const message = mcpToolErrorMessage(err instanceof Error ? err : new Error(String(err)));
2428
+ const admissionBackpressure =
2429
+ err instanceof Error &&
2430
+ (err.name === "ReplayAdmissionRefusedError" || err.name === "BufferAdmissionRefusedError");
2431
+ const result: McpToolErrorResult = {
2432
+ content: [{ type: "text", text: message }],
2433
+ isError: true,
2413
2434
  };
2435
+ if (admissionBackpressure) {
2436
+ result.structuredContent = { code: "buffer_admission_refused", retryable: true };
2437
+ }
2438
+ return { jsonrpc: "2.0", id, result };
2414
2439
  }
2415
2440
  }
2416
2441
 
@@ -2426,7 +2451,7 @@ export class EngramMcpServer {
2426
2451
 
2427
2452
  async runStdio(input: Readable, output: Writable): Promise<void> {
2428
2453
  input.on("data", (chunk) => {
2429
- this.buffer = Buffer.concat([this.buffer, Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)]);
2454
+ this.frames.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
2430
2455
  this.scheduleFlush(output);
2431
2456
  });
2432
2457
  await new Promise<void>((resolve, reject) => {
@@ -2455,7 +2480,7 @@ export class EngramMcpServer {
2455
2480
  if (this.flushTask === task) {
2456
2481
  this.flushTask = null;
2457
2482
  }
2458
- if (this.buffer.length > 0) {
2483
+ if (this.frames.hasUnread()) {
2459
2484
  this.scheduleFlush(output);
2460
2485
  }
2461
2486
  });
@@ -2463,30 +2488,15 @@ export class EngramMcpServer {
2463
2488
  }
2464
2489
 
2465
2490
  private async flushBuffer(output: Writable): Promise<void> {
2466
- while (true) {
2467
- const headerEnd = this.buffer.indexOf("\r\n\r\n");
2468
- if (headerEnd < 0) return;
2469
- const headerText = this.buffer.slice(0, headerEnd).toString("utf-8");
2470
- const headers = headerText.split("\r\n");
2471
- const contentLengthHeader = headers.find((line) => line.toLowerCase().startsWith("content-length:"));
2472
- if (!contentLengthHeader) {
2473
- this.buffer = Buffer.alloc(0);
2474
- return;
2475
- }
2476
- const contentLength = Number.parseInt(contentLengthHeader.split(":")[1]?.trim() ?? "0", 10);
2477
- if (!Number.isFinite(contentLength) || contentLength < 0) {
2478
- this.buffer = Buffer.alloc(0);
2479
- return;
2491
+ for (let frame = this.frames.next(); frame; frame = this.frames.next()) {
2492
+ if (frame.kind === "too-large") {
2493
+ this.writeMessage(output, { jsonrpc: "2.0", id: null, error: { code: -32600, message: "frame too large" } });
2494
+ continue;
2480
2495
  }
2481
- const messageStart = headerEnd + 4;
2482
- const messageEnd = messageStart + contentLength;
2483
- if (this.buffer.length < messageEnd) return;
2484
- const body = this.buffer.slice(messageStart, messageEnd).toString("utf-8");
2485
- this.buffer = this.buffer.slice(messageEnd);
2486
-
2487
2496
  let parsed: JsonRpcRequest;
2488
2497
  try {
2489
- parsed = JSON.parse(body) as JsonRpcRequest;
2498
+ // SAFETY: handleRequest defaults missing fields; a non-object body throws and scheduleFlush answers -32700.
2499
+ parsed = JSON.parse(frame.text) as JsonRpcRequest;
2490
2500
  } catch {
2491
2501
  this.writeMessage(output, {
2492
2502
  jsonrpc: "2.0",
@@ -2620,7 +2630,7 @@ export class EngramMcpServer {
2620
2630
  caps?.ops === undefined || capabilityAllowsOp(caps, MCP_MIGRATED_OPERATIONS[toLegacyToolName(t.name)] ?? "")
2621
2631
  )
2622
2632
  .map((t) => t.name);
2623
- throw new Error(unknownToolError(name, visible));
2633
+ throw new EngramAccessInputError(unknownToolError(name, visible));
2624
2634
  }
2625
2635
  const op = getOperation(migrated);
2626
2636
  if (!op) {
@@ -32,7 +32,7 @@ import { EngramAccessService } from "./access-service.js";
32
32
  import { SmartBuffer } from "./buffer.js";
33
33
  import { ExtractionDeadlineError } from "./orchestration/extraction-run.js";
34
34
  import { runExtractionDeferRetention } from "./orchestration/extraction-run-helpers.js";
35
- import { TurnIngestionCoordinator, type TurnIngestionDeps } from "./orchestration/turn-ingestion.js";
35
+ import { ReplayAdmissionRefusedError, TurnIngestionCoordinator, type TurnIngestionDeps } from "./orchestration/turn-ingestion.js";
36
36
  import { resolveScopeProfilePlan } from "./namespaces/scope-profiles.js";
37
37
  import { resolveCodingNamespaceOverlay } from "./coding/coding-namespace.js";
38
38
  import type { ResolvedScopeProfilePlan } from "./namespaces/scope-profiles.js";
@@ -378,6 +378,40 @@ test("#3251 buffered observe append is durable before accept: a failed save reje
378
378
  await cleanupDir(memoryDir);
379
379
  }
380
380
  });
381
+ test("#3251 multi-session refusal is atomic: earlier sessions are not partially accepted", async () => {
382
+ const config = parseConfig({
383
+ bufferMaxSessions: 3,
384
+ bufferMaxTurns: 100,
385
+ triggerMode: "every_n",
386
+ extractionMinChars: 1_000_000,
387
+ extractionMinUserTurns: 1000,
388
+ });
389
+ // SAFETY: SeedStorage implements the loadBuffer/saveBuffer surface this fixture drives.
390
+ const buffer = new SmartBuffer(config, new SeedStorage() as StorageManager);
391
+ const occupied = makeReplayTurn("occupied", "existing backlog");
392
+ await buffer.addTurn("occupied", {
393
+ role: occupied.role,
394
+ content: occupied.content,
395
+ timestamp: occupied.timestamp,
396
+ sessionKey: occupied.sessionKey,
397
+ });
398
+ const coordinator = makeTurnIngestionDouble(config, buffer, []);
399
+
400
+ await assert.rejects(
401
+ coordinator.ingestReplayBatch(
402
+ [
403
+ makeReplayTurn("fresh-a", "first in batch"),
404
+ makeReplayTurn("fresh-b", "second in batch"),
405
+ ],
406
+ bufferedObserveOptions({ writeNamespaceOverride: "default", sessionOwnerPrincipal: "alice" }),
407
+ ),
408
+ ReplayAdmissionRefusedError,
409
+ );
410
+
411
+ assert.equal(buffer.getTurns("fresh-a").length, 0, "refused batch must not durably accept its first session");
412
+ assert.equal(buffer.getTurns("fresh-b").length, 0, "refused batch must not durably accept its second session");
413
+ assert.equal(buffer.getTurns("occupied").length, 1, "pre-existing backlog remains intact");
414
+ });
381
415
 
382
416
  test("#3251 durable observe admission is bounded: at the entry cap a new session is refused, not evicted", async () => {
383
417
  // Real SmartBuffer + real coordinator, NO Orchestrator background actors —
@@ -413,8 +447,10 @@ test("#3251 durable observe admission is bounded: at the entry cap a new session
413
447
  // (buffer.ts), each holding an accepted, durably pending capture. The
414
448
  // seed turns are PINNED accepted captures — cap pressure must refuse new
415
449
  // captures, never evict these (#3251 eviction guard; the pre-existing
416
- // `default` mirror entry also occupies the entries map).
417
- for (let index = 0; index < 200; index += 1) {
450
+ // `default` mirror entry also occupies the entries map). 199 seeds +
451
+ // `default` = 200 entries: the reachable cap boundary (durable ingress
452
+ // refuses a 200th seed once pinned entries fill the cap).
453
+ for (let index = 0; index < 199; index += 1) {
418
454
  const key = `seed-${index}`;
419
455
  await buffer.addTurn(key, {
420
456
  role: "user",
@@ -452,7 +488,7 @@ test("#3251 durable observe admission is bounded: at the entry cap a new session
452
488
  assert.equal(queued.length, 0, "refused capture must not queue extraction");
453
489
 
454
490
  const snapshot = await buffer.getBufferSnapshot();
455
- assert.equal(snapshot.bufferedSessionCount, 200, "refusal must not evict accepted captures");
491
+ assert.equal(snapshot.bufferedSessionCount, 199, "refusal must not evict accepted captures");
456
492
  assert.ok(
457
493
  buffer.getTurns("seed-0").some((turn) => turn.content.includes("accepted seed capture 0")),
458
494
  );
@@ -51,6 +51,7 @@ import {
51
51
  queueExplicitCaptureForReview,
52
52
  } from "./explicit-capture.js";
53
53
  import { log } from "./logger.js";
54
+ import { embeddingBacklogExceeds, isSearchHealthDegraded } from "./namespaces/search.js";
54
55
  import { recordObjectiveStateSnapshotsFromObservedMessages } from "./objective-state-writers.js";
55
56
  import type { Orchestrator } from "./orchestrator.js";
56
57
  import { displayErrorDetail } from "./runtime/better-sqlite.js";
@@ -289,7 +290,7 @@ export class AccessObserveWriteSurface {
289
290
  /* status probe failed — non-fatal */
290
291
  }
291
292
  }
292
- if (threshold > 0 && pendingEmbeddings !== null && pendingEmbeddings > threshold) {
293
+ if (embeddingBacklogExceeds(pendingEmbeddings, threshold)) {
293
294
  degraded = true;
294
295
  degradedReason = `embedding-backlog: ${pendingEmbeddings} pending > threshold ${threshold}`;
295
296
  }
@@ -355,14 +356,13 @@ export class AccessObserveWriteSurface {
355
356
  const pending = health.pendingEmbeddings ?? null;
356
357
  const oldest = health.oldestPendingAgeMs ?? null;
357
358
  const threshold = this.deps.orchestrator.config.qmdEmbeddingBacklogThreshold;
358
- const backlogDegraded = threshold > 0 && pending !== null && pending > threshold;
359
- const degraded = !active || health.collectionState === "unknown" || backlogDegraded;
359
+ const backlogDegraded = embeddingBacklogExceeds(pending, threshold);
360
360
  const mode = !active ? "fallback" : health.daemonMode === true ? "daemon" : "cli";
361
361
 
362
362
  return {
363
363
  enabled: true,
364
364
  active,
365
- degraded,
365
+ degraded: isSearchHealthDegraded({ ...health, pendingEmbeddings: pending }, threshold),
366
366
  mode,
367
367
  collection: health.collection || fallbackCollection,
368
368
  collectionState: health.collectionState,
@@ -14,7 +14,7 @@ import { z } from "zod";
14
14
  import type { DreamsPhase, RecallDisclosure, RecallPlanMode } from "./types.js";
15
15
  import type { RemnicChatGptMemoryInspectorInput } from "./mcp-memory-inspector-app.js";
16
16
  import { defineOperation } from "./access-boundary.js";
17
- import { EngramAccessForbiddenError } from "./access-errors.js";
17
+ import { EngramAccessForbiddenError, rejectReservedArgumentKey } from "./access-errors.js";
18
18
  import {
19
19
  type ObserveRequest,
20
20
  observeRequestSchema,
@@ -55,11 +55,15 @@ import { normalizeDreamsStatusWindowHours } from "./maintenance/dreams-ledger.js
55
55
  // Shared helpers
56
56
  // ---------------------------------------------------------------------------
57
57
 
58
+ // Object.create(null): a plain {} lets a copied own `__proto__` member invoke
59
+ // the prototype setter, so an inherited field could reach the zod parse
60
+ // (issue #3225 B1).
58
61
  function stripNulls(data: unknown): unknown {
59
62
  if (data !== null && typeof data === "object" && !Array.isArray(data)) {
60
63
  const obj = data as Record<string, unknown>;
61
- const cleaned: Record<string, unknown> = {};
64
+ const cleaned = Object.create(null);
62
65
  for (const [key, value] of Object.entries(obj)) {
66
+ rejectReservedArgumentKey(key);
63
67
  if (value !== null) cleaned[key] = value;
64
68
  }
65
69
  return cleaned;
@@ -79,8 +83,9 @@ function strictSchema<T extends z.ZodRawShape>(shape: T): z.ZodType<Record<strin
79
83
  */
80
84
  function stripNullsExcept(data: unknown, preserve: readonly string[]): unknown {
81
85
  if (data !== null && typeof data === "object" && !Array.isArray(data)) {
82
- const cleaned: Record<string, unknown> = {};
86
+ const cleaned = Object.create(null);
83
87
  for (const [key, value] of Object.entries(data as Record<string, unknown>)) {
88
+ rejectReservedArgumentKey(key);
84
89
  if (value !== null || preserve.includes(key)) cleaned[key] = value;
85
90
  }
86
91
  return cleaned;
@@ -328,9 +333,15 @@ defineOperation({ name: "identity_anchor_update", description: "Update identity
328
333
  defineOperation({ name: "memory_identity", description: "Memory identity.", schema: strictSchema({ namespace: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.memoryIdentity({ namespace: optStr(input.namespace), principal: ctx.authenticatedPrincipal }) }) });
329
334
 
330
335
  // === WORK/SHARED ===
331
- defineOperation({ name: "work_task", description: "Manage work task.", schema: strictSchema({ action: S.str, id: S.str, title: S.str, description: S.str, status: S.str, priority: S.str, owner: S.str, assignee: S.str, projectId: S.str, tags: S.strArr, dueAt: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.workTask({ action: (optStr(input.action) as "update" | "list" | "get" | "create" | "delete" | "transition") ?? "list", id: optStr(input.id), title: optStr(input.title), description: optStr(input.description), status: optStr(input.status), priority: optStr(input.priority), owner: optStr(input.owner), assignee: optStr(input.assignee), projectId: optStr(input.projectId), tags: optStrArr(input.tags), dueAt: optStr(input.dueAt) }) }) });
332
- defineOperation({ name: "work_project", description: "Manage work project.", schema: strictSchema({ action: S.str, id: S.str, name: S.str, description: S.str, status: S.str, owner: S.str, tags: S.strArr, taskId: S.str, projectId: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.workProject({ action: (optStr(input.action) as "update" | "list" | "get" | "create" | "delete" | "link_task") ?? "list", id: optStr(input.id), name: optStr(input.name), description: optStr(input.description), status: optStr(input.status), owner: optStr(input.owner), tags: optStrArr(input.tags), taskId: optStr(input.taskId), projectId: optStr(input.projectId) }) }) });
333
- defineOperation({ name: "work_board", description: "Manage work board.", schema: strictSchema({ action: S.str, projectId: S.str, snapshotJson: S.str, linkToMemory: S.bool }), handler: async (input, ctx) => ({ result: await ctx.service.workBoard({ action: (optStr(input.action) as "export_markdown" | "export_snapshot" | "import_snapshot") ?? "export_markdown", projectId: optStr(input.projectId) ?? "", snapshotJson: optStr(input.snapshotJson) ?? "", linkToMemory: input.linkToMemory === true }) }) });
336
+ // SAFETY: optStr yields undefined or a string; workTask rejects any action
337
+ // string outside its union with an input error before touching a store.
338
+ defineOperation({ name: "work_task", description: "Manage work task.", fleetWide: true, schema: strictSchema({ action: S.str, id: S.str, title: S.str, description: S.str, status: S.str, priority: S.str, owner: S.str, assignee: S.str, projectId: S.str, tags: S.strArr, dueAt: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.workTask({ action: (optStr(input.action) as "update" | "list" | "get" | "create" | "delete" | "transition") ?? "list", id: optStr(input.id), title: optStr(input.title), description: optStr(input.description), status: optStr(input.status), priority: optStr(input.priority), owner: optStr(input.owner), assignee: optStr(input.assignee), projectId: optStr(input.projectId), tags: optStrArr(input.tags), dueAt: optStr(input.dueAt) }) }) });
339
+ // SAFETY: optStr yields undefined or a string; workProject rejects any action
340
+ // string outside its union with an input error before touching a store.
341
+ defineOperation({ name: "work_project", description: "Manage work project.", fleetWide: true, schema: strictSchema({ action: S.str, id: S.str, name: S.str, description: S.str, status: S.str, owner: S.str, tags: S.strArr, taskId: S.str, projectId: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.workProject({ action: (optStr(input.action) as "update" | "list" | "get" | "create" | "delete" | "link_task") ?? "list", id: optStr(input.id), name: optStr(input.name), description: optStr(input.description), status: optStr(input.status), owner: optStr(input.owner), tags: optStrArr(input.tags), taskId: optStr(input.taskId), projectId: optStr(input.projectId) }) }) });
342
+ // SAFETY: optStr yields undefined or a string; workBoard rejects any action
343
+ // string outside its union with an input error before touching a store.
344
+ defineOperation({ name: "work_board", description: "Manage work board.", fleetWide: true, schema: strictSchema({ action: S.str, projectId: S.str, snapshotJson: S.str, linkToMemory: S.bool }), handler: async (input, ctx) => ({ result: await ctx.service.workBoard({ action: (optStr(input.action) as "export_markdown" | "export_snapshot" | "import_snapshot") ?? "export_markdown", projectId: optStr(input.projectId) ?? "", snapshotJson: optStr(input.snapshotJson) ?? "", linkToMemory: input.linkToMemory === true }) }) });
334
345
  // Issue #2920: the envelope controls (authority/expiresAt/supersedes) are
335
346
  // parsed by the ONE canonical surface module shared with the OpenClaw tool
336
347
  // (write-output-controls.ts); semantics stay in composeWriteEnvelope. A
@@ -451,12 +462,12 @@ defineOperation({ name: "memory_summarize_hourly", description: "Hourly summariz
451
462
  defineOperation({ name: "conversation_index_update", description: "Update conv index.", fleetWide: true, schema: strictSchema({ sessionKey: S.str, hours: S.num, embed: S.bool }), handler: async (input, ctx) => { if (input.sessionKey !== undefined && typeof input.sessionKey !== "string") throw new EngramAccessInputError("sessionKey must be a string when provided"); return { result: await ctx.service.conversationIndexUpdate({ sessionKey: optStr(input.sessionKey), hours: optNum(input.hours), embed: optBool(input.embed) }) }; } });
452
463
  defineOperation({ name: "profiling_report", description: "Profiling report.", schema: strictSchema({ format: S.str, limit: S.num }), handler: async (input, ctx) => { if (input.format !== undefined && typeof input.format !== "string") throw new EngramAccessInputError("format must be a string when provided"); if (input.limit !== undefined && typeof input.limit !== "number") throw new EngramAccessInputError("limit must be a number when provided"); return { result: await ctx.service.profilingReport({ format: optStr(input.format), limit: optNum(input.limit) }) }; } });
453
464
  defineOperation({ name: "live_connectors_run", description: "Run connectors.", fleetWide: true, schema: strictSchema({ force: S.bool }), handler: async (input, ctx) => ({ result: await ctx.service.liveConnectorsRun({ authenticatedPrincipal: ctx.authenticatedPrincipal, force: input.force === true }, ctx.authenticatedPrincipal) }) });
454
- defineOperation({ name: "peer_list", description: "List peers.", schema: strictSchema({}), handler: async (_i, ctx) => ({ result: await ctx.service.peerList() }) });
455
- defineOperation({ name: "peer_get", description: "Get peer.", schema: strictSchema({ id: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_get: id is required"); return { result: await ctx.service.peerGet(id) }; } });
456
- defineOperation({ name: "peer_set", description: "Set peer.", schema: strictSchema({ id: S.str, kind: S.str, displayName: S.str, notes: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_set: id is required"); if (input.kind !== undefined && typeof input.kind !== "string") throw new EngramAccessInputError("peer_set: kind must be a string when provided"); if (input.displayName !== undefined && typeof input.displayName !== "string") throw new EngramAccessInputError("peer_set: displayName must be a string when provided"); if (input.notes !== undefined && typeof input.notes !== "string") throw new EngramAccessInputError("peer_set: notes must be a string when provided"); return { result: await ctx.service.peerSet({ id, kind: optStr(input.kind), displayName: optStr(input.displayName), notes: optStr(input.notes) }) }; } });
457
- defineOperation({ name: "peer_delete", description: "Delete peer.", schema: strictSchema({ id: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_delete: id is required"); return { result: await ctx.service.peerDelete(id) }; } });
458
- defineOperation({ name: "peer_profile_get", description: "Get peer profile.", schema: strictSchema({ id: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_profile_get: id is required"); return { result: await ctx.service.peerProfileGet(id) }; } });
459
- defineOperation({ name: "peer_forget", description: "Forget peer.", schema: strictSchema({ id: S.str, confirm: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_forget: id is required"); const c = optStr(input.confirm) ?? ""; if (c !== "yes") throw new EngramAccessInputError("peer_forget: confirm must be 'yes' to prevent accidental data loss"); return { result: await ctx.service.peerForget(id, { confirm: "yes" }) }; } });
465
+ defineOperation({ name: "peer_list", description: "List peers.", fleetWide: true, schema: strictSchema({}), handler: async (_i, ctx) => ({ result: await ctx.service.peerList() }) });
466
+ defineOperation({ name: "peer_get", description: "Get peer.", fleetWide: true, schema: strictSchema({ id: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_get: id is required"); return { result: await ctx.service.peerGet(id) }; } });
467
+ defineOperation({ name: "peer_set", description: "Set peer.", fleetWide: true, schema: strictSchema({ id: S.str, kind: S.str, displayName: S.str, notes: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_set: id is required"); return { result: await ctx.service.peerSet({ id, kind: optStr(input.kind), displayName: optStr(input.displayName), notes: optStr(input.notes) }) }; } });
468
+ defineOperation({ name: "peer_delete", description: "Delete peer.", fleetWide: true, schema: strictSchema({ id: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_delete: id is required"); return { result: await ctx.service.peerDelete(id) }; } });
469
+ defineOperation({ name: "peer_profile_get", description: "Get peer profile.", fleetWide: true, schema: strictSchema({ id: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_profile_get: id is required"); return { result: await ctx.service.peerProfileGet(id) }; } });
470
+ defineOperation({ name: "peer_forget", description: "Forget peer.", fleetWide: true, schema: strictSchema({ id: S.str, confirm: S.str }), handler: async (input, ctx) => { const id = defStr(input.id, ""); if (!id) throw new EngramAccessInputError("peer_forget: id is required"); const c = optStr(input.confirm) ?? ""; if (c !== "yes") throw new EngramAccessInputError("peer_forget: confirm must be 'yes' to prevent accidental data loss"); return { result: await ctx.service.peerForget(id, { confirm: "yes" }) }; } });
460
471
  defineOperation({ name: "console_state", description: "Console state.", schema: strictSchema({ namespace: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.consoleState(optStr(input.namespace), ctx.authenticatedPrincipal) }) });
461
472
  defineOperation({ name: "dreams_status", description: "Dreams status.", schema: strictSchema({ windowHours: S.num, namespace: S.str }), handler: async (input, ctx) => { let wh = 24; try { wh = normalizeDreamsStatusWindowHours(input.windowHours); } catch { throw new EngramAccessInputError("dreams_status: windowHours must be a positive integer. Got: " + String(input.windowHours)); } return { result: await ctx.service.dreamsStatus({ windowHours: wh, namespace: optStr(input.namespace), principal: ctx.authenticatedPrincipal }) }; } });
462
473
  defineOperation({ name: "dreams_run", description: "Run dreams.", schema: strictSchema({ phase: S.str, dryRun: S.bool, namespace: S.str }), handler: async (input, ctx) => { const VP = ["lightSleep", "rem", "deepSleep"]; const ph = optStr(input.phase) ?? ""; if (!ph || !VP.includes(ph)) throw new EngramAccessInputError("dreams_run: phase must be one of: " + VP.join(", ")); if (input.dryRun !== undefined && typeof input.dryRun !== "boolean") throw new EngramAccessInputError("dreams_run: dryRun must be boolean"); return { result: await ctx.service.dreamsRun({ phase: ph as DreamsPhase, dryRun: input.dryRun === true, namespace: optStr(input.namespace), authenticatedPrincipal: ctx.authenticatedPrincipal }) }; } });