@remnic/core 10.0.85 → 10.0.87

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 (417) hide show
  1. package/dist/access-admin-ops-surface.js +72 -71
  2. package/dist/access-authorization-probe.js +73 -72
  3. package/dist/access-boundary.d.ts.map +1 -1
  4. package/dist/access-boundary.js +72 -71
  5. package/dist/access-cli.js +157 -155
  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 +72 -71
  11. package/dist/access-http-briefing.js +73 -72
  12. package/dist/access-http-query.js +74 -73
  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 +95 -94
  16. package/dist/access-identity-continuity-surface.js +72 -71
  17. package/dist/access-lcm-surface.js +72 -71
  18. package/dist/access-mcp.d.ts.map +1 -1
  19. package/dist/access-mcp.js +82 -81
  20. package/dist/access-memory-search-fanout.js +2 -2
  21. package/dist/access-namespace-preflight.js +72 -71
  22. package/dist/access-observe-write-category.js +10 -9
  23. package/dist/access-observe-write-surface.js +72 -71
  24. package/dist/access-offline-manifest.js +8 -8
  25. package/dist/access-operations-batch.js +73 -72
  26. package/dist/access-operations.js +80 -79
  27. package/dist/access-recall-concurrency.js +72 -71
  28. package/dist/access-recall-diagnostics.js +2 -2
  29. package/dist/access-recall-response.js +72 -71
  30. package/dist/access-recall-surface.js +72 -71
  31. package/dist/access-schema.d.ts.map +1 -1
  32. package/dist/access-schema.js +9 -8
  33. package/dist/access-service.d.ts +3 -3
  34. package/dist/access-service.d.ts.map +1 -1
  35. package/dist/access-service.js +72 -71
  36. package/dist/access-surface-catalog.js +74 -73
  37. package/dist/access-surface-catalog.js.map +1 -1
  38. package/dist/access-token-capabilities.js +2 -2
  39. package/dist/active-recall.js +32 -32
  40. package/dist/adapters/index.js +7 -7
  41. package/dist/adapters/registry.js +3 -3
  42. package/dist/briefing-deadline.d.ts +2 -2
  43. package/dist/briefing-deadline.d.ts.map +1 -1
  44. package/dist/briefing-deadline.js +1 -1
  45. package/dist/briefing.js +23 -23
  46. package/dist/buffer-admission.d.ts +22 -0
  47. package/dist/buffer-admission.d.ts.map +1 -0
  48. package/dist/buffer-admission.js +13 -0
  49. package/dist/buffer-eviction.d.ts +4 -5
  50. package/dist/buffer-eviction.d.ts.map +1 -1
  51. package/dist/buffer-eviction.js +1 -1
  52. package/dist/buffer-turn-helpers.d.ts +7 -0
  53. package/dist/buffer-turn-helpers.d.ts.map +1 -1
  54. package/dist/buffer-turn-helpers.js +9 -1
  55. package/dist/buffer-types.d.ts +24 -1
  56. package/dist/buffer-types.d.ts.map +1 -1
  57. package/dist/buffer.d.ts +17 -18
  58. package/dist/buffer.d.ts.map +1 -1
  59. package/dist/buffer.js +12 -8
  60. package/dist/calibration.js +2 -2
  61. package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
  62. package/dist/capsule-crypto-7FJQINUR.js.map +1 -0
  63. package/dist/capsule-import-gate.d.ts +11 -0
  64. package/dist/capsule-import-gate.d.ts.map +1 -0
  65. package/dist/capsule-import-gate.js +12 -0
  66. package/dist/capsule-import-gate.js.map +1 -0
  67. package/dist/causal-behavior.js +2 -2
  68. package/dist/causal-chain.js +2 -2
  69. package/dist/causal-consolidation.js +25 -25
  70. package/dist/causal-retrieval.js +2 -2
  71. package/dist/causal-trajectory.js +1 -1
  72. package/dist/{chunk-73YVMZ7D.js → chunk-2G4TV2ML.js} +11 -11
  73. package/dist/{chunk-BMIM6ESC.js → chunk-2JG2NI7C.js} +38 -38
  74. package/dist/{chunk-IR4C3DP2.js → chunk-2K3AUDH7.js} +2 -2
  75. package/dist/{chunk-6KFFRMPQ.js → chunk-2N4OUSIA.js} +2 -2
  76. package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
  77. package/dist/{chunk-44FQCC5D.js → chunk-2NUDHVYM.js} +2 -2
  78. package/dist/{chunk-FK4WC6VK.js → chunk-2U4LMSEX.js} +3 -3
  79. package/dist/{chunk-LT3CMO65.js → chunk-2XOFVTTT.js} +2 -2
  80. package/dist/{chunk-I6ORDT54.js → chunk-32CA7B6L.js} +2 -2
  81. package/dist/{chunk-TQVECGUQ.js → chunk-32YX7EJ3.js} +3 -3
  82. package/dist/{chunk-H4D5YURJ.js → chunk-3CTKEXXF.js} +120 -118
  83. package/dist/{chunk-H4D5YURJ.js.map → chunk-3CTKEXXF.js.map} +1 -1
  84. package/dist/{chunk-HLWXCDUQ.js → chunk-3DAWHP5K.js} +5 -5
  85. package/dist/{chunk-GBOYOL5L.js → chunk-3PJEDHLD.js} +2 -2
  86. package/dist/{chunk-ZWTHVUT5.js → chunk-3UZ2VMKD.js} +2 -2
  87. package/dist/{chunk-2XYDL2FG.js → chunk-3Y5X6YFH.js} +2 -2
  88. package/dist/{chunk-23B4HLWG.js → chunk-4GMI5HFH.js} +9 -9
  89. package/dist/{chunk-AP3TSV6S.js → chunk-4LCVA2LW.js} +3 -3
  90. package/dist/{chunk-MZCGHPIJ.js → chunk-4X7HQKAG.js} +2 -2
  91. package/dist/{chunk-YNBMBL4C.js → chunk-4XYAWK5H.js} +2 -2
  92. package/dist/{chunk-2JAT4S7N.js → chunk-5SOSQTVE.js} +2 -2
  93. package/dist/{chunk-G3T6YMBO.js → chunk-64S6X4IU.js} +7 -7
  94. package/dist/{chunk-NIV7RPWQ.js → chunk-6BCWFCDG.js} +7 -7
  95. package/dist/{chunk-V27FK6D6.js → chunk-6XTUZKCZ.js} +2 -2
  96. package/dist/{chunk-KASJJGRG.js → chunk-6ZELOK6O.js} +3 -8
  97. package/dist/chunk-6ZELOK6O.js.map +1 -0
  98. package/dist/{chunk-2ASEDFOA.js → chunk-73UZZFJC.js} +3 -3
  99. package/dist/{chunk-UHPHH74F.js → chunk-77PDJ4OE.js} +3 -3
  100. package/dist/{chunk-BATPQNRG.js → chunk-7GSL4LBG.js} +2 -2
  101. package/dist/{chunk-B6SPPYGJ.js → chunk-7IWRLYFJ.js} +2 -2
  102. package/dist/{chunk-BUKK5SWA.js → chunk-7LCGFUMB.js} +4 -4
  103. package/dist/{chunk-ZXF3O7P4.js → chunk-ARRTLPRC.js} +2 -2
  104. package/dist/{chunk-S5CQI2KF.js → chunk-B3GKQ6DU.js} +2 -2
  105. package/dist/{chunk-2URKYLMK.js → chunk-B56LORLY.js} +2 -2
  106. package/dist/{chunk-4IWEPQEV.js → chunk-BIVKPQ6Q.js} +2 -2
  107. package/dist/{chunk-DM4RRTN7.js → chunk-BLSHFIR6.js} +7 -6
  108. package/dist/chunk-BLSHFIR6.js.map +1 -0
  109. package/dist/{chunk-4DPLCYEK.js → chunk-C2HORX76.js} +3 -3
  110. package/dist/{chunk-ELWI2R6H.js → chunk-CC4V7HNP.js} +22 -22
  111. package/dist/{chunk-LGEH473S.js → chunk-CJCTKL6Y.js} +9 -1
  112. package/dist/chunk-CJCTKL6Y.js.map +1 -0
  113. package/dist/{chunk-WFDWLWDK.js → chunk-CN7Q3T2J.js} +4 -4
  114. package/dist/{chunk-GJVGFKOU.js → chunk-CYCEROPR.js} +3 -3
  115. package/dist/{chunk-CCU7URRP.js → chunk-D5EV6Y2L.js} +3 -3
  116. package/dist/{chunk-N6II66E5.js → chunk-DT7SNJH6.js} +3 -3
  117. package/dist/{chunk-FRM4UTDR.js → chunk-DZ7DPDVW.js} +8 -6
  118. package/dist/chunk-DZ7DPDVW.js.map +1 -0
  119. package/dist/{chunk-MVOJDLXS.js → chunk-EA2LA5RI.js} +2 -2
  120. package/dist/{chunk-WBN66HPI.js → chunk-ELHJK7LM.js} +6 -6
  121. package/dist/{chunk-GKYIRTNX.js → chunk-EQ4U73UH.js} +45 -14
  122. package/dist/chunk-EQ4U73UH.js.map +1 -0
  123. package/dist/{chunk-ONN2E5QD.js → chunk-EUBDYOLD.js} +6 -6
  124. package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
  125. package/dist/{chunk-3LHPTBWG.js → chunk-F74G4KIH.js} +2 -2
  126. package/dist/{chunk-X7REO4CH.js → chunk-FF3EFLUT.js} +7 -5
  127. package/dist/chunk-FF3EFLUT.js.map +1 -0
  128. package/dist/chunk-FKBGK3NC.js +62 -0
  129. package/dist/chunk-FKBGK3NC.js.map +1 -0
  130. package/dist/{chunk-X2KO4FK5.js → chunk-FNFURZBZ.js} +5 -5
  131. package/dist/chunk-FNFURZBZ.js.map +1 -0
  132. package/dist/{chunk-MYCCO6AA.js → chunk-G54BOKZ6.js} +2 -2
  133. package/dist/{chunk-JFH6JAGY.js → chunk-G67EUQAB.js} +5 -5
  134. package/dist/{chunk-TV65NR7L.js → chunk-GULUWOA5.js} +2 -2
  135. package/dist/{chunk-6DANGCMO.js → chunk-HFUKXOUW.js} +2 -2
  136. package/dist/{chunk-Y3IYKNBY.js → chunk-HLN2HOXX.js} +2 -2
  137. package/dist/{chunk-Y43X3E24.js → chunk-IBDWYRDR.js} +3 -3
  138. package/dist/{chunk-XSFQN7TP.js → chunk-IBGU6AN6.js} +15 -14
  139. package/dist/chunk-IBGU6AN6.js.map +1 -0
  140. package/dist/{chunk-AJG2TAMR.js → chunk-IEEV27BC.js} +2 -2
  141. package/dist/{chunk-FZPDRCOB.js → chunk-IJ3OEECW.js} +4 -4
  142. package/dist/{chunk-D6THW7PA.js → chunk-IPD5GC7C.js} +2 -2
  143. package/dist/{chunk-VCVEMQJK.js → chunk-JM2YZKCQ.js} +2 -2
  144. package/dist/{chunk-H34INI3P.js → chunk-KVSOEVAF.js} +34 -11
  145. package/dist/chunk-KVSOEVAF.js.map +1 -0
  146. package/dist/chunk-M3BEJJNV.js +31 -0
  147. package/dist/chunk-M3BEJJNV.js.map +1 -0
  148. package/dist/{chunk-OSTFDWZJ.js → chunk-NIVKG3DS.js} +4 -4
  149. package/dist/{chunk-VZELUMKK.js → chunk-NQP4ZYGG.js} +2 -2
  150. package/dist/{chunk-3ODZLCHC.js → chunk-NT7F7WJQ.js} +2 -2
  151. package/dist/{chunk-YO2JEX2Z.js → chunk-OGBQMY46.js} +2 -2
  152. package/dist/{chunk-O55AGDTI.js → chunk-OL553AWT.js} +10 -10
  153. package/dist/{chunk-CLVILSBR.js → chunk-OZNBZJ3S.js} +22 -22
  154. package/dist/{chunk-RNGDKFY2.js → chunk-P452SEKO.js} +2 -2
  155. package/dist/{chunk-JX3E4XCW.js → chunk-PDOOUXCS.js} +2 -2
  156. package/dist/{chunk-V45TEVDA.js → chunk-PKZ274PI.js} +3 -3
  157. package/dist/{chunk-MNYQVYJ4.js → chunk-QD5R6U5J.js} +2 -2
  158. package/dist/{chunk-DI5ET2IE.js → chunk-QEZ3DW5O.js} +9 -9
  159. package/dist/{chunk-WYPCJK6K.js → chunk-QKERFEJS.js} +2 -2
  160. package/dist/{chunk-BLMTOIBK.js → chunk-QKES7GU6.js} +2 -2
  161. package/dist/{chunk-PCQWKMOL.js → chunk-QNSZYTSG.js} +2 -2
  162. package/dist/{chunk-433EXTC4.js → chunk-ROGO7FRA.js} +5 -4
  163. package/dist/{chunk-433EXTC4.js.map → chunk-ROGO7FRA.js.map} +1 -1
  164. package/dist/{chunk-MHSBOEH7.js → chunk-ROKDHO74.js} +2 -2
  165. package/dist/{chunk-BLGQUZFB.js → chunk-RWGRSKN5.js} +7 -5
  166. package/dist/chunk-RWGRSKN5.js.map +1 -0
  167. package/dist/{chunk-3VU2FDTU.js → chunk-SYKJBS7S.js} +10 -10
  168. package/dist/{chunk-5BE3FLKL.js → chunk-TAUHAP5J.js} +4 -4
  169. package/dist/{chunk-QCP75JGO.js → chunk-TFA6HPOA.js} +5 -5
  170. package/dist/{chunk-SGHV6LZ7.js → chunk-TQ4SCPGQ.js} +67 -51
  171. package/dist/chunk-TQ4SCPGQ.js.map +1 -0
  172. package/dist/{chunk-E7V5AK7I.js → chunk-TRMZHGIX.js} +8 -8
  173. package/dist/{chunk-WBDIZWJQ.js → chunk-TSS3D7ZY.js} +2 -2
  174. package/dist/{chunk-HVFX2N2G.js → chunk-UAUV3BWS.js} +65 -83
  175. package/dist/chunk-UAUV3BWS.js.map +1 -0
  176. package/dist/{chunk-OMYRPMQC.js → chunk-US7KPQA5.js} +2 -2
  177. package/dist/{chunk-KHZVAISH.js → chunk-UWRHHRDK.js} +2 -2
  178. package/dist/{chunk-WLNWFHX3.js → chunk-UXXK523X.js} +40 -2
  179. package/dist/{chunk-WLNWFHX3.js.map → chunk-UXXK523X.js.map} +1 -1
  180. package/dist/{chunk-SDESKFKT.js → chunk-UYHDMCY2.js} +2 -2
  181. package/dist/{chunk-YAAKVTZA.js → chunk-VKILERSL.js} +2 -2
  182. package/dist/{chunk-2TGIKMMZ.js → chunk-WXDTOE6F.js} +2 -2
  183. package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
  184. package/dist/{chunk-P225W4RQ.js → chunk-XU37EJTW.js} +22 -22
  185. package/dist/chunk-XU37EJTW.js.map +1 -0
  186. package/dist/{chunk-QA3Y2O6H.js → chunk-YAVIBVCU.js} +2 -2
  187. package/dist/{chunk-CMRHXPLQ.js → chunk-YL4UKMD7.js} +117 -51
  188. package/dist/chunk-YL4UKMD7.js.map +1 -0
  189. package/dist/{chunk-GYNRNKMW.js → chunk-YMJRLT7S.js} +28 -8
  190. package/dist/chunk-YMJRLT7S.js.map +1 -0
  191. package/dist/{chunk-E5QEF3JO.js → chunk-YRZPBN46.js} +3 -3
  192. package/dist/{chunk-FMFCQRDM.js → chunk-ZANNIXXE.js} +3 -3
  193. package/dist/{chunk-3KGCHTTK.js → chunk-ZBFLV657.js} +3 -3
  194. package/dist/{chunk-VEFGHRKV.js → chunk-ZOJUWVAU.js} +1 -1
  195. package/dist/cli.js +117 -116
  196. package/dist/coding/export-okf-codegraph.js +21 -21
  197. package/dist/coding/pre-action-gate.js +2 -2
  198. package/dist/compounding/engine.js +23 -23
  199. package/dist/config.js +32 -32
  200. package/dist/connectors/codex-materialize-runner.js +22 -22
  201. package/dist/connectors/index.js +26 -26
  202. package/dist/convergence-refresh.js +24 -24
  203. package/dist/conversation-index/backend.js +2 -2
  204. package/dist/corpus-watermark.js +26 -26
  205. package/dist/dashboard-runtime.js +2 -2
  206. package/dist/deep-recall-config.js +2 -2
  207. package/dist/deep-recall-http-glue.js +11 -11
  208. package/dist/entity-retrieval.js +23 -23
  209. package/dist/explicit-capture.js +1 -1
  210. package/dist/external-wiki-access.js +73 -72
  211. package/dist/extraction-quote-grounding.d.ts +5 -1
  212. package/dist/extraction-quote-grounding.d.ts.map +1 -1
  213. package/dist/extraction-quote-grounding.js +1 -1
  214. package/dist/extraction-source-grounding-rules.d.ts +3 -1
  215. package/dist/extraction-source-grounding-rules.d.ts.map +1 -1
  216. package/dist/extraction-source-grounding-rules.js +2 -2
  217. package/dist/extraction-source-grounding.d.ts +1 -1
  218. package/dist/extraction-source-grounding.d.ts.map +1 -1
  219. package/dist/extraction-source-grounding.js +3 -3
  220. package/dist/extraction.d.ts.map +1 -1
  221. package/dist/extraction.js +40 -40
  222. package/dist/harmonic-retrieval.js +22 -22
  223. package/dist/index.js +196 -194
  224. package/dist/lcm/index.js +3 -3
  225. package/dist/local-llm.js +2 -2
  226. package/dist/location/index.js +25 -25
  227. package/dist/maintenance/memory-governance.js +22 -22
  228. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +22 -22
  229. package/dist/maintenance/rebuild-memory-projection-runner.js +24 -24
  230. package/dist/maintenance/rebuild-memory-projection.js +23 -23
  231. package/dist/memory-browse-http-glue.js +2 -2
  232. package/dist/memory-browse-operations.js +73 -72
  233. package/dist/memory-browse.js +5 -5
  234. package/dist/namespaces/migrate.js +23 -23
  235. package/dist/namespaces/search.d.ts +1 -0
  236. package/dist/namespaces/search.d.ts.map +1 -1
  237. package/dist/namespaces/search.js +10 -8
  238. package/dist/namespaces/storage.js +22 -22
  239. package/dist/offline-sync-changeset-generations.js +3 -3
  240. package/dist/offline-sync-embedding-generation.js +2 -2
  241. package/dist/offline-sync-impression-drain.js +2 -2
  242. package/dist/offline-sync-snapshot-stream.js +6 -6
  243. package/dist/offline-sync.js +5 -5
  244. package/dist/operator-doctor-corpus.js +29 -29
  245. package/dist/operator-doctor-replica.js +4 -4
  246. package/dist/operator-toolkit.js +44 -44
  247. package/dist/orchestration/backlog-scheduler.js +158 -156
  248. package/dist/orchestration/backlog-scheduler.js.map +1 -1
  249. package/dist/orchestration/buffer-drain-runner.js +158 -156
  250. package/dist/orchestration/maintenance.js +36 -36
  251. package/dist/orchestration/turn-ingestion.d.ts +2 -9
  252. package/dist/orchestration/turn-ingestion.d.ts.map +1 -1
  253. package/dist/orchestrator.js +159 -157
  254. package/dist/qmd.js +4 -4
  255. package/dist/recall-navigation-http-glue.js +2 -2
  256. package/dist/recall-navigation-operations.js +73 -72
  257. package/dist/recall-navigation.js +72 -71
  258. package/dist/recall-state-view-admission.js +5 -5
  259. package/dist/recall-why-service.js +5 -5
  260. package/dist/replica-divergence.js +3 -3
  261. package/dist/resume-bundles.js +33 -33
  262. package/dist/search/factory.js +7 -7
  263. package/dist/search/index.js +11 -11
  264. package/dist/semantic-consolidation.js +23 -23
  265. package/dist/semantic-rule-promotion.js +22 -22
  266. package/dist/semantic-rule-verifier.js +22 -22
  267. package/dist/source-agent-qualifier.d.ts +2 -0
  268. package/dist/source-agent-qualifier.d.ts.map +1 -1
  269. package/dist/source-agent-qualifier.js +33 -33
  270. package/dist/standup/build.js +23 -23
  271. package/dist/storage/buffer-drain-claims.js +2 -2
  272. package/dist/storage/buffer-session-files.js +2 -2
  273. package/dist/storage.js +23 -23
  274. package/dist/summarizer.js +3 -3
  275. package/dist/support-passport/index.js +75 -74
  276. package/dist/tokens.js +3 -3
  277. package/dist/transfer/backup.js +2 -2
  278. package/dist/transfer/capsule-export.js +2 -2
  279. package/dist/transfer/capsule-import.js +2 -2
  280. package/dist/transfer/export-okf.js +22 -22
  281. package/dist/verified-recall.js +22 -22
  282. package/dist/who-knows-cli.js +24 -24
  283. package/dist/who-knows.js +23 -23
  284. package/package.json +2 -2
  285. package/src/access-boundary.ts +15 -0
  286. package/src/access-errors.ts +22 -0
  287. package/src/access-fleet-wide-work-peer.test.ts +236 -0
  288. package/src/access-http-peer-fleet-wide.test.ts +192 -0
  289. package/src/access-http.test.ts +249 -4
  290. package/src/access-http.ts +114 -39
  291. package/src/access-mcp.test.ts +64 -0
  292. package/src/access-mcp.ts +17 -7
  293. package/src/access-observe-replay-durability.test.ts +40 -4
  294. package/src/access-operations-batch.ts +23 -12
  295. package/src/access-proto-injection.test.ts +340 -0
  296. package/src/access-schema.ts +31 -5
  297. package/src/access-service-capsule-import.test.ts +235 -0
  298. package/src/access-service.ts +6 -45
  299. package/src/briefing-deadline.ts +5 -4
  300. package/src/buffer-admission.ts +45 -0
  301. package/src/buffer-eviction-backpressure.test.ts +211 -0
  302. package/src/buffer-eviction.ts +8 -14
  303. package/src/buffer-incremental-persistence.test.ts +25 -37
  304. package/src/buffer-session.test.ts +32 -16
  305. package/src/buffer-turn-helpers.ts +41 -0
  306. package/src/buffer-types.ts +28 -1
  307. package/src/buffer.ts +77 -85
  308. package/src/capsule-import-gate.ts +109 -0
  309. package/src/extraction-quote-grounding.test.ts +76 -0
  310. package/src/extraction-quote-grounding.ts +49 -13
  311. package/src/extraction-source-grounding-rules.ts +6 -1
  312. package/src/extraction-source-grounding.ts +2 -0
  313. package/src/extraction.ts +3 -4
  314. package/src/namespaces/search.test.ts +141 -1
  315. package/src/namespaces/search.ts +39 -9
  316. package/src/orchestration/turn-ingestion.ts +38 -39
  317. package/src/recall-navigation-operations.ts +9 -2
  318. package/src/source-agent-qualifier.ts +3 -0
  319. package/dist/chunk-BLGQUZFB.js.map +0 -1
  320. package/dist/chunk-CMRHXPLQ.js.map +0 -1
  321. package/dist/chunk-DM4RRTN7.js.map +0 -1
  322. package/dist/chunk-FRM4UTDR.js.map +0 -1
  323. package/dist/chunk-GKYIRTNX.js.map +0 -1
  324. package/dist/chunk-GYNRNKMW.js.map +0 -1
  325. package/dist/chunk-H34INI3P.js.map +0 -1
  326. package/dist/chunk-HVFX2N2G.js.map +0 -1
  327. package/dist/chunk-KASJJGRG.js.map +0 -1
  328. package/dist/chunk-LGEH473S.js.map +0 -1
  329. package/dist/chunk-P225W4RQ.js.map +0 -1
  330. package/dist/chunk-SGHV6LZ7.js.map +0 -1
  331. package/dist/chunk-X2KO4FK5.js.map +0 -1
  332. package/dist/chunk-X7REO4CH.js.map +0 -1
  333. package/dist/chunk-XSFQN7TP.js.map +0 -1
  334. /package/dist/{capsule-crypto-GWVG7LGC.js.map → buffer-admission.js.map} +0 -0
  335. /package/dist/{chunk-73YVMZ7D.js.map → chunk-2G4TV2ML.js.map} +0 -0
  336. /package/dist/{chunk-BMIM6ESC.js.map → chunk-2JG2NI7C.js.map} +0 -0
  337. /package/dist/{chunk-IR4C3DP2.js.map → chunk-2K3AUDH7.js.map} +0 -0
  338. /package/dist/{chunk-6KFFRMPQ.js.map → chunk-2N4OUSIA.js.map} +0 -0
  339. /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
  340. /package/dist/{chunk-44FQCC5D.js.map → chunk-2NUDHVYM.js.map} +0 -0
  341. /package/dist/{chunk-FK4WC6VK.js.map → chunk-2U4LMSEX.js.map} +0 -0
  342. /package/dist/{chunk-LT3CMO65.js.map → chunk-2XOFVTTT.js.map} +0 -0
  343. /package/dist/{chunk-I6ORDT54.js.map → chunk-32CA7B6L.js.map} +0 -0
  344. /package/dist/{chunk-TQVECGUQ.js.map → chunk-32YX7EJ3.js.map} +0 -0
  345. /package/dist/{chunk-HLWXCDUQ.js.map → chunk-3DAWHP5K.js.map} +0 -0
  346. /package/dist/{chunk-GBOYOL5L.js.map → chunk-3PJEDHLD.js.map} +0 -0
  347. /package/dist/{chunk-ZWTHVUT5.js.map → chunk-3UZ2VMKD.js.map} +0 -0
  348. /package/dist/{chunk-2XYDL2FG.js.map → chunk-3Y5X6YFH.js.map} +0 -0
  349. /package/dist/{chunk-23B4HLWG.js.map → chunk-4GMI5HFH.js.map} +0 -0
  350. /package/dist/{chunk-AP3TSV6S.js.map → chunk-4LCVA2LW.js.map} +0 -0
  351. /package/dist/{chunk-MZCGHPIJ.js.map → chunk-4X7HQKAG.js.map} +0 -0
  352. /package/dist/{chunk-YNBMBL4C.js.map → chunk-4XYAWK5H.js.map} +0 -0
  353. /package/dist/{chunk-2JAT4S7N.js.map → chunk-5SOSQTVE.js.map} +0 -0
  354. /package/dist/{chunk-G3T6YMBO.js.map → chunk-64S6X4IU.js.map} +0 -0
  355. /package/dist/{chunk-NIV7RPWQ.js.map → chunk-6BCWFCDG.js.map} +0 -0
  356. /package/dist/{chunk-V27FK6D6.js.map → chunk-6XTUZKCZ.js.map} +0 -0
  357. /package/dist/{chunk-2ASEDFOA.js.map → chunk-73UZZFJC.js.map} +0 -0
  358. /package/dist/{chunk-UHPHH74F.js.map → chunk-77PDJ4OE.js.map} +0 -0
  359. /package/dist/{chunk-BATPQNRG.js.map → chunk-7GSL4LBG.js.map} +0 -0
  360. /package/dist/{chunk-B6SPPYGJ.js.map → chunk-7IWRLYFJ.js.map} +0 -0
  361. /package/dist/{chunk-BUKK5SWA.js.map → chunk-7LCGFUMB.js.map} +0 -0
  362. /package/dist/{chunk-ZXF3O7P4.js.map → chunk-ARRTLPRC.js.map} +0 -0
  363. /package/dist/{chunk-S5CQI2KF.js.map → chunk-B3GKQ6DU.js.map} +0 -0
  364. /package/dist/{chunk-2URKYLMK.js.map → chunk-B56LORLY.js.map} +0 -0
  365. /package/dist/{chunk-4IWEPQEV.js.map → chunk-BIVKPQ6Q.js.map} +0 -0
  366. /package/dist/{chunk-4DPLCYEK.js.map → chunk-C2HORX76.js.map} +0 -0
  367. /package/dist/{chunk-ELWI2R6H.js.map → chunk-CC4V7HNP.js.map} +0 -0
  368. /package/dist/{chunk-WFDWLWDK.js.map → chunk-CN7Q3T2J.js.map} +0 -0
  369. /package/dist/{chunk-GJVGFKOU.js.map → chunk-CYCEROPR.js.map} +0 -0
  370. /package/dist/{chunk-CCU7URRP.js.map → chunk-D5EV6Y2L.js.map} +0 -0
  371. /package/dist/{chunk-N6II66E5.js.map → chunk-DT7SNJH6.js.map} +0 -0
  372. /package/dist/{chunk-MVOJDLXS.js.map → chunk-EA2LA5RI.js.map} +0 -0
  373. /package/dist/{chunk-WBN66HPI.js.map → chunk-ELHJK7LM.js.map} +0 -0
  374. /package/dist/{chunk-ONN2E5QD.js.map → chunk-EUBDYOLD.js.map} +0 -0
  375. /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
  376. /package/dist/{chunk-3LHPTBWG.js.map → chunk-F74G4KIH.js.map} +0 -0
  377. /package/dist/{chunk-MYCCO6AA.js.map → chunk-G54BOKZ6.js.map} +0 -0
  378. /package/dist/{chunk-JFH6JAGY.js.map → chunk-G67EUQAB.js.map} +0 -0
  379. /package/dist/{chunk-TV65NR7L.js.map → chunk-GULUWOA5.js.map} +0 -0
  380. /package/dist/{chunk-6DANGCMO.js.map → chunk-HFUKXOUW.js.map} +0 -0
  381. /package/dist/{chunk-Y3IYKNBY.js.map → chunk-HLN2HOXX.js.map} +0 -0
  382. /package/dist/{chunk-Y43X3E24.js.map → chunk-IBDWYRDR.js.map} +0 -0
  383. /package/dist/{chunk-AJG2TAMR.js.map → chunk-IEEV27BC.js.map} +0 -0
  384. /package/dist/{chunk-FZPDRCOB.js.map → chunk-IJ3OEECW.js.map} +0 -0
  385. /package/dist/{chunk-D6THW7PA.js.map → chunk-IPD5GC7C.js.map} +0 -0
  386. /package/dist/{chunk-VCVEMQJK.js.map → chunk-JM2YZKCQ.js.map} +0 -0
  387. /package/dist/{chunk-OSTFDWZJ.js.map → chunk-NIVKG3DS.js.map} +0 -0
  388. /package/dist/{chunk-VZELUMKK.js.map → chunk-NQP4ZYGG.js.map} +0 -0
  389. /package/dist/{chunk-3ODZLCHC.js.map → chunk-NT7F7WJQ.js.map} +0 -0
  390. /package/dist/{chunk-YO2JEX2Z.js.map → chunk-OGBQMY46.js.map} +0 -0
  391. /package/dist/{chunk-O55AGDTI.js.map → chunk-OL553AWT.js.map} +0 -0
  392. /package/dist/{chunk-CLVILSBR.js.map → chunk-OZNBZJ3S.js.map} +0 -0
  393. /package/dist/{chunk-RNGDKFY2.js.map → chunk-P452SEKO.js.map} +0 -0
  394. /package/dist/{chunk-JX3E4XCW.js.map → chunk-PDOOUXCS.js.map} +0 -0
  395. /package/dist/{chunk-V45TEVDA.js.map → chunk-PKZ274PI.js.map} +0 -0
  396. /package/dist/{chunk-MNYQVYJ4.js.map → chunk-QD5R6U5J.js.map} +0 -0
  397. /package/dist/{chunk-DI5ET2IE.js.map → chunk-QEZ3DW5O.js.map} +0 -0
  398. /package/dist/{chunk-WYPCJK6K.js.map → chunk-QKERFEJS.js.map} +0 -0
  399. /package/dist/{chunk-BLMTOIBK.js.map → chunk-QKES7GU6.js.map} +0 -0
  400. /package/dist/{chunk-PCQWKMOL.js.map → chunk-QNSZYTSG.js.map} +0 -0
  401. /package/dist/{chunk-MHSBOEH7.js.map → chunk-ROKDHO74.js.map} +0 -0
  402. /package/dist/{chunk-3VU2FDTU.js.map → chunk-SYKJBS7S.js.map} +0 -0
  403. /package/dist/{chunk-5BE3FLKL.js.map → chunk-TAUHAP5J.js.map} +0 -0
  404. /package/dist/{chunk-QCP75JGO.js.map → chunk-TFA6HPOA.js.map} +0 -0
  405. /package/dist/{chunk-E7V5AK7I.js.map → chunk-TRMZHGIX.js.map} +0 -0
  406. /package/dist/{chunk-WBDIZWJQ.js.map → chunk-TSS3D7ZY.js.map} +0 -0
  407. /package/dist/{chunk-OMYRPMQC.js.map → chunk-US7KPQA5.js.map} +0 -0
  408. /package/dist/{chunk-KHZVAISH.js.map → chunk-UWRHHRDK.js.map} +0 -0
  409. /package/dist/{chunk-SDESKFKT.js.map → chunk-UYHDMCY2.js.map} +0 -0
  410. /package/dist/{chunk-YAAKVTZA.js.map → chunk-VKILERSL.js.map} +0 -0
  411. /package/dist/{chunk-2TGIKMMZ.js.map → chunk-WXDTOE6F.js.map} +0 -0
  412. /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
  413. /package/dist/{chunk-QA3Y2O6H.js.map → chunk-YAVIBVCU.js.map} +0 -0
  414. /package/dist/{chunk-E5QEF3JO.js.map → chunk-YRZPBN46.js.map} +0 -0
  415. /package/dist/{chunk-FMFCQRDM.js.map → chunk-ZANNIXXE.js.map} +0 -0
  416. /package/dist/{chunk-3KGCHTTK.js.map → chunk-ZBFLV657.js.map} +0 -0
  417. /package/dist/{chunk-VEFGHRKV.js.map → chunk-ZOJUWVAU.js.map} +0 -0
@@ -417,6 +417,11 @@ export class EngramAccessHttpServer extends ReviewDeckAccessHttpBase {
417
417
  private readonly resourceMetadataUrl?: string;
418
418
  private readonly externalRequestHandler?: SupportPassportExternalRequestHandler;
419
419
  private readonly writeLimiter: WriteRateLimiter;
420
+ // Reservations taken by ensureWriteRateLimitAvailable and not yet committed
421
+ // by recordWriteRateLimitHit, keyed by request. The per-request release in
422
+ // the connection callback covers every early return and error path, so a
423
+ // failed or exempt write never leaks its slot (issue #3225 C4).
424
+ private readonly pendingWriteQuotas = new WeakMap<IncomingMessage, WriteRateLimitReservation>();
420
425
  private readonly mcpServer: EngramMcpServer;
421
426
  private server: Server | null = null;
422
427
  private boundPort = 0;
@@ -544,6 +549,13 @@ export class EngramAccessHttpServer extends ReviewDeckAccessHttpBase {
544
549
  });
545
550
  return;
546
551
  }
552
+ if (err.name === "ReplayAdmissionRefusedError") {
553
+ // Durable-capture backpressure (#3251/#1908): the request was
554
+ // never accepted — the buffer is at entry capacity. Retryable,
555
+ // not a server fault (503, not 500).
556
+ this.respondJson(res, 503, { error: err.message, code: "buffer_admission_refused" });
557
+ return;
558
+ }
547
559
  if (res.headersSent) {
548
560
  res.destroy(err as Error);
549
561
  return;
@@ -556,6 +568,9 @@ export class EngramAccessHttpServer extends ReviewDeckAccessHttpBase {
556
568
  }).finally(() => {
557
569
  req.off("aborted", abortDisconnectedRequest);
558
570
  res.off("close", abortDisconnectedRequest);
571
+ // A counted write that failed or was exempt (dry run, replay) must
572
+ // not hold its reserved slot past the request (issue #3225 C4).
573
+ this.releasePendingWriteQuota(req);
559
574
  });
560
575
  });
561
576
  });
@@ -2739,26 +2754,38 @@ export class EngramAccessHttpServer extends ReviewDeckAccessHttpBase {
2739
2754
  // DELETE /engram/v1/peers/:id?forget=true — destructive full purge (issue #679 completion)
2740
2755
  // GET /engram/v1/peers/:id/profile — get peer profile
2741
2756
  if (req.method === "GET" && pathname === "/engram/v1/peers") {
2742
- this.enforceTokenOp("peer_list"); // boundary dispatch (issue #1525)
2743
- const result = await this.service.peerList();
2744
- this.respondJson(res, 200, result);
2757
+ const op = getOperation("peer_list"); // boundary dispatch (issue #1525)
2758
+ if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: peer_list");
2759
+ // SAFETY: the registered peer_list handler returns { result } from the service.
2760
+ const output = (await op.run({}, {
2761
+ service: this.service,
2762
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
2763
+ sourceConnector: this.resolveConnector(req),
2764
+ })) as { result: unknown };
2765
+ this.respondJson(res, 200, output.result);
2745
2766
  return;
2746
2767
  }
2747
2768
 
2748
2769
  const peerProfileMatch = /^\/engram\/v1\/peers\/([^/]+)\/profile$/.exec(pathname);
2749
2770
  if (peerProfileMatch) {
2750
- this.enforceTokenOp("peer_profile_get"); // boundary dispatch (issue #1525)
2751
2771
  if (req.method !== "GET") {
2752
2772
  this.respondJson(res, 405, { error: "method_not_allowed", code: "method_not_allowed" });
2753
2773
  return;
2754
2774
  }
2775
+ const op = getOperation("peer_profile_get"); // boundary dispatch (issue #1525)
2776
+ if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: peer_profile_get");
2755
2777
  const peerId = decodePeerIdSegment(peerProfileMatch[1] ?? "");
2756
- const result = await this.service.peerProfileGet(peerId);
2757
- if (!result.found) {
2778
+ // SAFETY: the registered peer_profile_get handler returns { result } from the service.
2779
+ const output = (await op.run({ id: peerId }, {
2780
+ service: this.service,
2781
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
2782
+ sourceConnector: this.resolveConnector(req),
2783
+ })) as { result: { found: boolean } };
2784
+ if (!output.result.found) {
2758
2785
  this.respondJson(res, 404, { error: "peer_profile_not_found", code: "peer_profile_not_found" });
2759
2786
  return;
2760
2787
  }
2761
- this.respondJson(res, 200, result);
2788
+ this.respondJson(res, 200, output.result);
2762
2789
  return;
2763
2790
  }
2764
2791
 
@@ -2767,47 +2794,49 @@ export class EngramAccessHttpServer extends ReviewDeckAccessHttpBase {
2767
2794
  const peerId = decodePeerIdSegment(peerIdMatch[1] ?? "");
2768
2795
 
2769
2796
  if (req.method === "GET") {
2770
- this.enforceTokenOp("peer_get"); // boundary dispatch (issue #1525)
2771
- const result = await this.service.peerGet(peerId);
2772
- if (!result.found) {
2797
+ const op = getOperation("peer_get"); // boundary dispatch (issue #1525)
2798
+ if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: peer_get");
2799
+ // SAFETY: the registered peer_get handler returns { result } with a found flag.
2800
+ const output = (await op.run({ id: peerId }, {
2801
+ service: this.service,
2802
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
2803
+ sourceConnector: this.resolveConnector(req),
2804
+ })) as { result: { found: boolean } };
2805
+ if (!output.result.found) {
2773
2806
  this.respondJson(res, 404, { error: "peer_not_found", code: "peer_not_found" });
2774
2807
  return;
2775
2808
  }
2776
- this.respondJson(res, 200, result);
2809
+ this.respondJson(res, 200, output.result);
2777
2810
  return;
2778
2811
  }
2779
2812
 
2780
2813
  if (req.method === "PUT") {
2781
- this.enforceTokenOp("peer_set"); // boundary dispatch (issue #1525)
2782
2814
  const body = await this.readJsonBody(req) as Record<string, unknown>;
2783
- // Reject malformed types up front rather than silently dropping them
2784
- // to undefined and letting peerSet fall back to defaults
2785
- // (CLAUDE.md rule 51: no silent defaults on bad input).
2786
- if ("kind" in body && body.kind !== undefined && typeof body.kind !== "string") {
2787
- throw new EngramAccessInputError("kind must be a string when provided");
2788
- }
2789
- if (
2790
- "displayName" in body &&
2791
- body.displayName !== undefined &&
2792
- typeof body.displayName !== "string"
2793
- ) {
2794
- throw new EngramAccessInputError("displayName must be a string when provided");
2795
- }
2796
- if ("notes" in body && body.notes !== undefined && typeof body.notes !== "string") {
2797
- throw new EngramAccessInputError("notes must be a string when provided");
2815
+ const op = getOperation("peer_set"); // boundary dispatch (issue #1525)
2816
+ if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: peer_set");
2817
+ // The operation schema strips nulls as "absent", so an explicit null
2818
+ // must fail here or the peer would register with default fields.
2819
+ for (const field of ["kind", "displayName", "notes"]) {
2820
+ if (body[field] === null) throw new EngramAccessInputError(`${field} must be a string when provided`);
2798
2821
  }
2799
- const result = await this.service.peerSet({
2822
+ // Raw body fields: the operation schema rejects malformed types with an
2823
+ // input error instead of silently defaulting them (rule 51).
2824
+ // SAFETY: the registered peer_set handler returns { result } with the upsert outcome.
2825
+ const output = (await op.run({
2800
2826
  id: peerId,
2801
- kind: typeof body.kind === "string" ? body.kind : undefined,
2802
- displayName: typeof body.displayName === "string" ? body.displayName : undefined,
2803
- notes: typeof body.notes === "string" ? body.notes : undefined,
2804
- });
2805
- this.respondJson(res, result.created ? 201 : 200, result);
2827
+ kind: body.kind,
2828
+ displayName: body.displayName,
2829
+ notes: body.notes,
2830
+ }, {
2831
+ service: this.service,
2832
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
2833
+ sourceConnector: this.resolveConnector(req),
2834
+ })) as { result: { created: boolean } };
2835
+ this.respondJson(res, output.result.created ? 201 : 200, output.result);
2806
2836
  return;
2807
2837
  }
2808
2838
 
2809
2839
  if (req.method === "DELETE") {
2810
- this.enforceTokenOp("peer_delete"); // boundary dispatch (issue #1525)
2811
2840
  // `?forget=true` triggers the destructive full-purge path (issue #679
2812
2841
  // completion). The caller must also pass `confirm=yes` in the request
2813
2842
  // body; absent confirmation yields 400. Plain DELETE (no ?forget) keeps
@@ -2824,12 +2853,26 @@ export class EngramAccessHttpServer extends ReviewDeckAccessHttpBase {
2824
2853
  });
2825
2854
  return;
2826
2855
  }
2827
- const result = await this.service.peerForget(peerId, { confirm: "yes" });
2828
- this.respondJson(res, 200, result);
2856
+ const op = getOperation("peer_forget"); // boundary dispatch (issue #1525)
2857
+ if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: peer_forget");
2858
+ // SAFETY: the registered peer_forget handler returns { result } with the purge outcome.
2859
+ const output = (await op.run({ id: peerId, confirm: "yes" }, {
2860
+ service: this.service,
2861
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
2862
+ sourceConnector: this.resolveConnector(req),
2863
+ })) as { result: unknown };
2864
+ this.respondJson(res, 200, output.result);
2829
2865
  return;
2830
2866
  }
2831
- const result = await this.service.peerDelete(peerId);
2832
- this.respondJson(res, 200, result);
2867
+ const op = getOperation("peer_delete"); // boundary dispatch (issue #1525)
2868
+ if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: peer_delete");
2869
+ // SAFETY: the registered peer_delete handler returns { result } with the delete outcome.
2870
+ const output = (await op.run({ id: peerId }, {
2871
+ service: this.service,
2872
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
2873
+ sourceConnector: this.resolveConnector(req),
2874
+ })) as { result: unknown };
2875
+ this.respondJson(res, 200, output.result);
2833
2876
  return;
2834
2877
  }
2835
2878
 
@@ -3931,16 +3974,48 @@ export class EngramAccessHttpServer extends ReviewDeckAccessHttpBase {
3931
3974
  return 200;
3932
3975
  }
3933
3976
 
3977
+ /**
3978
+ * Admit a quota-counted write by RESERVING a limiter slot for the request
3979
+ * (issue #3225 C4). A bare hasCapacity check admits N concurrent requests
3980
+ * against the same committed count; the reservation holds in-flight
3981
+ * capacity until the write commits at recordWriteRateLimitHit or the
3982
+ * request ends. Idempotent per request, so a quota hook that fires after
3983
+ * an earlier reservation is a no-op.
3984
+ */
3934
3985
  protected ensureWriteRateLimitAvailable(req?: IncomingMessage): void {
3935
- if (!this.writeLimiter.hasCapacity(this.principalForRateLimit(req))) {
3986
+ if (!req) {
3987
+ if (!this.writeLimiter.hasCapacity()) {
3988
+ throw new HttpError(429, "write_rate_limited", "write_rate_limited");
3989
+ }
3990
+ return;
3991
+ }
3992
+ if (this.pendingWriteQuotas.has(req)) return;
3993
+ const reservation = this.writeLimiter.reserve(this.principalForRateLimit(req));
3994
+ if (!reservation) {
3936
3995
  throw new HttpError(429, "write_rate_limited", "write_rate_limited");
3937
3996
  }
3997
+ this.pendingWriteQuotas.set(req, reservation);
3938
3998
  }
3939
3999
 
4000
+ /** Commit a counted write at the durable boundary; consumes the request's reservation. */
3940
4001
  protected recordWriteRateLimitHit(req?: IncomingMessage): void {
4002
+ const pending = req ? this.pendingWriteQuotas.get(req) : undefined;
4003
+ if (pending && req) {
4004
+ this.pendingWriteQuotas.delete(req);
4005
+ pending.commit();
4006
+ return;
4007
+ }
3941
4008
  this.writeLimiter.record(this.principalForRateLimit(req));
3942
4009
  }
3943
4010
 
4011
+ /** Release an uncommitted reservation after a rejection, failure, or exempt outcome. */
4012
+ private releasePendingWriteQuota(req: IncomingMessage): void {
4013
+ const pending = this.pendingWriteQuotas.get(req);
4014
+ if (!pending) return;
4015
+ this.pendingWriteQuotas.delete(req);
4016
+ pending.release();
4017
+ }
4018
+
3944
4019
  protected reserveWriteRateLimitSlot(req?: IncomingMessage): WriteRateLimitReservation {
3945
4020
  const reservation = this.writeLimiter.reserve(this.principalForRateLimit(req));
3946
4021
  if (!reservation) {
@@ -109,6 +109,46 @@ function makeToolRequest(name: string, args: Record<string, unknown> = {}): Reco
109
109
  },
110
110
  };
111
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
+
112
152
 
113
153
  // ──────────────────────────────────────────────────────────────────────────
114
154
  // Finding 2 (#396 new): invalid MCP briefing format values must be rejected
@@ -1295,6 +1335,17 @@ const FLEET_WIDE_MAINTENANCE_TOOLS = [
1295
1335
  "compounding_promote_candidate",
1296
1336
  "compression_guidelines_optimize",
1297
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",
1298
1349
  ] as const;
1299
1350
 
1300
1351
  test("MCP fleet-wide maintenance ops: namespace-scoped token denied for every op, no side effect (issue #1850 round 10)", async () => {
@@ -1319,6 +1370,19 @@ test("MCP fleet-wide maintenance ops: namespace-scoped token denied for every op
1319
1370
  compressionGuidelinesOptimize: track,
1320
1371
  compressionGuidelinesActivate: track,
1321
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
+ });
1322
1386
  const server = new EngramMcpServer(service, { principal: "ops" });
1323
1387
  try {
1324
1388
  for (const tool of FLEET_WIDE_MAINTENANCE_TOOLS) {
package/src/access-mcp.ts CHANGED
@@ -84,6 +84,13 @@ type JsonRpcRequest = {
84
84
  params?: Record<string, unknown>;
85
85
  };
86
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
+
87
94
  function throwMcpAbort(signal: AbortSignal | undefined, message: string): void {
88
95
  if (!signal?.aborted) return;
89
96
  if (isAbortError(signal.reason)) throw signal.reason;
@@ -2418,14 +2425,17 @@ export class EngramMcpServer {
2418
2425
  // Preserve the original AbortError so HTTP can silently end a dead socket.
2419
2426
  if (isAbortError(err)) throw err;
2420
2427
  const message = mcpToolErrorMessage(err instanceof Error ? err : new Error(String(err)));
2421
- return {
2422
- jsonrpc: "2.0",
2423
- id,
2424
- result: {
2425
- content: [{ type: "text", text: message }],
2426
- isError: true,
2427
- },
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,
2428
2434
  };
2435
+ if (admissionBackpressure) {
2436
+ result.structuredContent = { code: "buffer_admission_refused", retryable: true };
2437
+ }
2438
+ return { jsonrpc: "2.0", id, result };
2429
2439
  }
2430
2440
  }
2431
2441
 
@@ -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
  );
@@ -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 }) }; } });