@remnic/core 9.4.1 → 9.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. package/dist/access-admin-ops-surface.d.ts +5 -5
  2. package/dist/access-admin-ops-surface.js +26 -26
  3. package/dist/access-boundary.d.ts +26 -7
  4. package/dist/access-boundary.js +31 -27
  5. package/dist/access-cli.js +79 -77
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-http.d.ts +51 -5
  8. package/dist/access-http.js +38 -36
  9. package/dist/access-identity-continuity-surface.d.ts +4 -4
  10. package/dist/access-identity-continuity-surface.js +26 -26
  11. package/dist/access-lcm-surface.d.ts +5 -5
  12. package/dist/access-lcm-surface.js +26 -26
  13. package/dist/access-mcp.d.ts +15 -5
  14. package/dist/access-mcp.js +35 -33
  15. package/dist/access-observe-write-surface.d.ts +5 -5
  16. package/dist/access-observe-write-surface.js +26 -26
  17. package/dist/access-operations-batch.js +31 -30
  18. package/dist/access-operations.d.ts +7 -7
  19. package/dist/access-operations.js +33 -32
  20. package/dist/access-recall-surface.d.ts +5 -5
  21. package/dist/access-recall-surface.js +26 -26
  22. package/dist/access-schema.js +4 -4
  23. package/dist/{access-service-m-2VO_Ig.d.ts → access-service-DZX0sYAv.d.ts} +3 -3
  24. package/dist/access-service.d.ts +5 -5
  25. package/dist/access-service.js +26 -26
  26. package/dist/access-surface-catalog.d.ts +5 -5
  27. package/dist/access-surface-catalog.js +1 -0
  28. package/dist/access-surface-catalog.js.map +1 -1
  29. package/dist/access-token-capabilities.d.ts +174 -0
  30. package/dist/access-token-capabilities.js +33 -0
  31. package/dist/action-confidence.d.ts +1 -1
  32. package/dist/active-memory-bridge.d.ts +1 -1
  33. package/dist/active-recall.d.ts +1 -1
  34. package/dist/active-recall.js +1 -1
  35. package/dist/adapters/index.js +4 -4
  36. package/dist/adapters/registry.js +2 -2
  37. package/dist/{auto-sync-HMHQHUAG.js → auto-sync-G6IU7L6C.js} +3 -3
  38. package/dist/behavior-learner.d.ts +1 -1
  39. package/dist/behavior-signals.d.ts +1 -1
  40. package/dist/bootstrap.d.ts +4 -4
  41. package/dist/briefing.d.ts +1 -1
  42. package/dist/briefing.js +7 -7
  43. package/dist/buffer-surprise-report.d.ts +1 -1
  44. package/dist/buffer.d.ts +1 -1
  45. package/dist/calibration.d.ts +1 -1
  46. package/dist/capabilities.d.ts +1 -1
  47. package/dist/{capsule-crypto-CZJSLEFG.js → capsule-crypto-RAARDY3M.js} +2 -2
  48. package/dist/capsule-crypto-RAARDY3M.js.map +1 -0
  49. package/dist/{catalog-BxWokdC2.d.ts → catalog-DoH2b0OR.d.ts} +1 -1
  50. package/dist/causal-behavior.d.ts +1 -1
  51. package/dist/causal-behavior.js +5 -5
  52. package/dist/causal-chain.js +3 -3
  53. package/dist/causal-consolidation.d.ts +1 -1
  54. package/dist/causal-consolidation.js +11 -11
  55. package/dist/causal-retrieval.js +3 -3
  56. package/dist/causal-trajectory-graph.d.ts +1 -1
  57. package/dist/causal-trajectory.js +1 -1
  58. package/dist/{chunk-7QWMTOIF.js → chunk-2B7DP7AP.js} +31 -31
  59. package/dist/{chunk-4BFRBP45.js → chunk-2JFDHI2K.js} +2 -2
  60. package/dist/{chunk-OHDJ34CC.js → chunk-2SFEFR5I.js} +2 -2
  61. package/dist/{chunk-E2SPGGUI.js → chunk-4OKLXWDZ.js} +13 -6
  62. package/dist/chunk-4OKLXWDZ.js.map +1 -0
  63. package/dist/{chunk-ILFJ7AVS.js → chunk-4WNLSSHL.js} +35 -21
  64. package/dist/chunk-4WNLSSHL.js.map +1 -0
  65. package/dist/{chunk-YDSWRD33.js → chunk-5K5VEDFY.js} +4 -4
  66. package/dist/{chunk-CZAZX7JP.js → chunk-7BAJAXGB.js} +4 -4
  67. package/dist/{chunk-CXLNZXG5.js → chunk-7XGSIUP5.js} +1 -1
  68. package/dist/{chunk-ZPOROUTX.js → chunk-AOYJVCFN.js} +5 -5
  69. package/dist/{chunk-IC6QX5TC.js → chunk-BGMSRN6I.js} +5 -5
  70. package/dist/{chunk-5QZDMSSN.js → chunk-CXEL3Y22.js} +70 -70
  71. package/dist/{chunk-M5QKGHCR.js → chunk-CYWCKFCL.js} +5 -5
  72. package/dist/{chunk-2U2A5HF4.js → chunk-D2XYYU5Q.js} +5 -5
  73. package/dist/chunk-D7FOKIY5.js +173 -0
  74. package/dist/chunk-D7FOKIY5.js.map +1 -0
  75. package/dist/{chunk-DGYCWCQ7.js → chunk-DZSAXE3T.js} +2 -2
  76. package/dist/{chunk-2BFLDH5F.js → chunk-EEIROWEG.js} +2 -2
  77. package/dist/{chunk-6ZIETXZ2.js → chunk-H5PFR5ZT.js} +2 -2
  78. package/dist/{chunk-2FOD2YY3.js → chunk-I74ZEBMA.js} +236 -103
  79. package/dist/chunk-I74ZEBMA.js.map +1 -0
  80. package/dist/chunk-IC2TCZJ2.js +54 -0
  81. package/dist/chunk-IC2TCZJ2.js.map +1 -0
  82. package/dist/{chunk-66FLU5BT.js → chunk-II6WHCCW.js} +6 -6
  83. package/dist/{chunk-YPEMNNNY.js → chunk-ITWTKT2R.js} +6 -6
  84. package/dist/{chunk-AKUCU7CI.js → chunk-JI5AVXYO.js} +5 -5
  85. package/dist/{chunk-27FLVMDC.js → chunk-KMF67QDQ.js} +7 -7
  86. package/dist/{chunk-FLVL4L3L.js → chunk-KTULX4YO.js} +4 -4
  87. package/dist/{chunk-FZGGZAAU.js → chunk-L2QJQSHL.js} +146 -2
  88. package/dist/chunk-L2QJQSHL.js.map +1 -0
  89. package/dist/{chunk-4P4626RY.js → chunk-L4UZBJNM.js} +4 -4
  90. package/dist/{chunk-JLR3JSTL.js → chunk-L6V3WJ55.js} +3 -3
  91. package/dist/{chunk-6BISDEN6.js → chunk-LNHJ32NK.js} +14 -14
  92. package/dist/{chunk-437S3G37.js → chunk-LNQUAJNC.js} +1 -1
  93. package/dist/{chunk-LOR6ZM5R.js → chunk-MFDJ63U5.js} +2 -2
  94. package/dist/{chunk-HRCACVHL.js → chunk-NFTABV56.js} +2 -2
  95. package/dist/{chunk-OUMTBFSQ.js → chunk-NKCPUHCI.js} +5 -5
  96. package/dist/{chunk-KCJ5H6XQ.js → chunk-ODSYJXDU.js} +2 -2
  97. package/dist/{chunk-2QSZNTDO.js → chunk-RKNJBZ55.js} +4 -4
  98. package/dist/{chunk-NG7YBCJO.js → chunk-SNBEVJBX.js} +4 -4
  99. package/dist/{chunk-CZAA5N5S.js → chunk-SNNIUDRS.js} +2 -2
  100. package/dist/{chunk-P6SYWE4G.js → chunk-STCCFQU5.js} +9 -9
  101. package/dist/{chunk-X4A62ET7.js → chunk-TI7IHDBQ.js} +17 -17
  102. package/dist/{chunk-BGTAL46Y.js → chunk-TV7C3HCB.js} +2 -2
  103. package/dist/{chunk-EPGWWCZF.js → chunk-UBGAOYBP.js} +6 -6
  104. package/dist/{chunk-M7GGMGMZ.js → chunk-UDCDGP6A.js} +15 -15
  105. package/dist/{chunk-2MR3MFQB.js → chunk-UPSAT4PY.js} +2 -2
  106. package/dist/{chunk-CGS7UKWM.js → chunk-VCW74PNH.js} +2 -2
  107. package/dist/{chunk-DR6PEJU3.js → chunk-VWEWK2BM.js} +44 -59
  108. package/dist/chunk-VWEWK2BM.js.map +1 -0
  109. package/dist/{chunk-CLX5VDNY.js → chunk-Y4RKAQZ7.js} +4 -4
  110. package/dist/{chunk-EZ25VE3G.js → chunk-YNDLCWXS.js} +4 -4
  111. package/dist/{cli-EiBBOt3r.d.ts → cli-1dghPCO2.d.ts} +3 -3
  112. package/dist/cli.d.ts +6 -6
  113. package/dist/cli.js +57 -55
  114. package/dist/compounding/engine.d.ts +1 -1
  115. package/dist/compounding/engine.js +7 -7
  116. package/dist/compounding/preference-consolidator.d.ts +1 -1
  117. package/dist/compression-optimizer.d.ts +1 -1
  118. package/dist/config.d.ts +1 -1
  119. package/dist/config.js +1 -1
  120. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  121. package/dist/connectors/codex-materialize-runner.js +7 -7
  122. package/dist/connectors/codex-materialize.d.ts +1 -1
  123. package/dist/connectors/index.d.ts +1 -1
  124. package/dist/connectors/index.js +11 -9
  125. package/dist/consolidation-provenance-check.d.ts +1 -1
  126. package/dist/consolidation-undo.d.ts +1 -1
  127. package/dist/contradiction/index.d.ts +1 -1
  128. package/dist/contradiction/index.js +4 -4
  129. package/dist/conversation-index/backend.d.ts +1 -1
  130. package/dist/conversation-index/backend.js +2 -2
  131. package/dist/conversation-index/chunker.d.ts +1 -1
  132. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  133. package/dist/conversation-index/indexer.d.ts +1 -1
  134. package/dist/conversation-index/search.d.ts +1 -1
  135. package/dist/dashboard-runtime.js +2 -2
  136. package/dist/day-summary.d.ts +1 -1
  137. package/dist/delinearize.d.ts +1 -1
  138. package/dist/direct-answer-wiring.d.ts +1 -1
  139. package/dist/direct-answer.d.ts +1 -1
  140. package/dist/embedding-fallback.d.ts +1 -1
  141. package/dist/enrichment/index.d.ts +1 -1
  142. package/dist/entity-retrieval.d.ts +1 -1
  143. package/dist/entity-retrieval.js +7 -7
  144. package/dist/entity-schema.d.ts +1 -1
  145. package/dist/explicit-capture.d.ts +4 -4
  146. package/dist/extraction-faithfulness.d.ts +1 -1
  147. package/dist/extraction-judge-telemetry.d.ts +1 -1
  148. package/dist/extraction-judge-training.d.ts +1 -1
  149. package/dist/extraction-judge.d.ts +1 -1
  150. package/dist/extraction.d.ts +1 -1
  151. package/dist/fallback-llm.d.ts +1 -1
  152. package/dist/{first-start-migration-DY6YCRC2.js → first-start-migration-VBJQL6OI.js} +5 -5
  153. package/dist/graph-dashboard-diff.d.ts +1 -1
  154. package/dist/graph-dashboard-key.d.ts +1 -1
  155. package/dist/graph-dashboard-parser.d.ts +1 -1
  156. package/dist/graph-edge-reinforcement.d.ts +1 -1
  157. package/dist/graph-snapshot.d.ts +1 -1
  158. package/dist/graph.d.ts +1 -1
  159. package/dist/identity-continuity.d.ts +1 -1
  160. package/dist/importance.d.ts +1 -1
  161. package/dist/index.d.ts +13 -10
  162. package/dist/index.js +115 -93
  163. package/dist/intent.d.ts +1 -1
  164. package/dist/lcm/engine.d.ts +1 -1
  165. package/dist/lcm/engine.js +2 -2
  166. package/dist/lcm/index.d.ts +1 -1
  167. package/dist/lcm/index.js +2 -2
  168. package/dist/lcm/tools.d.ts +1 -1
  169. package/dist/lifecycle.d.ts +1 -1
  170. package/dist/live-connectors-runner.d.ts +1 -1
  171. package/dist/local-llm.d.ts +1 -1
  172. package/dist/local-model-endpoint.d.ts +1 -1
  173. package/dist/maintenance/memory-governance.d.ts +1 -1
  174. package/dist/maintenance/memory-governance.js +8 -8
  175. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
  176. package/dist/maintenance/rebuild-memory-projection.js +9 -9
  177. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  178. package/dist/mcp-read-only-tools.d.ts +16 -0
  179. package/dist/mcp-read-only-tools.js +8 -0
  180. package/dist/mcp-read-only-tools.js.map +1 -0
  181. package/dist/memory-action-policy.d.ts +1 -1
  182. package/dist/memory-cache.d.ts +1 -1
  183. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  184. package/dist/memory-projection-store.d.ts +1 -1
  185. package/dist/memory-provenance.d.ts +1 -1
  186. package/dist/memory-worth-outcomes.d.ts +1 -1
  187. package/dist/models-json.d.ts +1 -1
  188. package/dist/namespaces/migrate.d.ts +2 -2
  189. package/dist/namespaces/migrate.js +17 -17
  190. package/dist/namespaces/principal.d.ts +1 -1
  191. package/dist/namespaces/search.d.ts +1 -1
  192. package/dist/namespaces/search.js +9 -9
  193. package/dist/namespaces/storage.d.ts +2 -2
  194. package/dist/namespaces/storage.js +7 -7
  195. package/dist/native-knowledge.d.ts +1 -1
  196. package/dist/operator-toolkit.d.ts +1 -1
  197. package/dist/operator-toolkit.js +22 -22
  198. package/dist/orchestration/compression-guideline-coordinator.d.ts +1 -1
  199. package/dist/orchestration/maintenance.d.ts +2 -2
  200. package/dist/orchestration/maintenance.js +9 -9
  201. package/dist/{orchestrator-B2Y28Z9t.d.ts → orchestrator-Dh3WSW_O.d.ts} +3 -3
  202. package/dist/orchestrator.d.ts +4 -4
  203. package/dist/orchestrator.js +81 -79
  204. package/dist/patterns-cli.d.ts +1 -1
  205. package/dist/policy-runtime.d.ts +1 -1
  206. package/dist/provenance.d.ts +1 -1
  207. package/dist/{qmd-Db9mOkzB.d.ts → qmd-DoUVxqND.d.ts} +1 -1
  208. package/dist/qmd-preflight.d.ts +2 -2
  209. package/dist/qmd-recall-cache.d.ts +1 -1
  210. package/dist/qmd.d.ts +2 -2
  211. package/dist/recall-disclosure-escalation.d.ts +1 -1
  212. package/dist/recall-explain-renderer.d.ts +1 -1
  213. package/dist/recall-planner-llm.d.ts +1 -1
  214. package/dist/recall-state.d.ts +1 -1
  215. package/dist/recall-tag-filter.d.ts +1 -1
  216. package/dist/recall-timings.d.ts +1 -1
  217. package/dist/recall-xray-cli.d.ts +1 -1
  218. package/dist/recall-xray-renderer.d.ts +1 -1
  219. package/dist/recall-xray.d.ts +1 -1
  220. package/dist/resolve-auth-token.d.ts +1 -1
  221. package/dist/resume-bundles.js +2 -2
  222. package/dist/retrieval-agents.d.ts +2 -2
  223. package/dist/retrieval-tiers.d.ts +1 -1
  224. package/dist/routing/engine.d.ts +1 -1
  225. package/dist/routing/store.d.ts +1 -1
  226. package/dist/schemas.d.ts +22 -22
  227. package/dist/search/embed-helper.d.ts +1 -1
  228. package/dist/search/factory.d.ts +1 -1
  229. package/dist/search/factory.js +8 -8
  230. package/dist/search/index.d.ts +1 -1
  231. package/dist/search/index.js +12 -12
  232. package/dist/search/lancedb-backend.d.ts +1 -1
  233. package/dist/search/lancedb-backend.js +2 -2
  234. package/dist/search/meilisearch-backend.d.ts +1 -1
  235. package/dist/search/meilisearch-backend.js +2 -2
  236. package/dist/search/noop-backend.d.ts +1 -1
  237. package/dist/search/orama-backend.d.ts +1 -1
  238. package/dist/search/orama-backend.js +2 -2
  239. package/dist/search/port.d.ts +1 -1
  240. package/dist/search/remote-backend.d.ts +1 -1
  241. package/dist/{semantic-consolidation-BDVIbLRw.d.ts → semantic-consolidation-Dof1865o.d.ts} +1 -1
  242. package/dist/semantic-consolidation.d.ts +2 -2
  243. package/dist/semantic-consolidation.js +9 -9
  244. package/dist/semantic-rule-promotion.js +7 -7
  245. package/dist/semantic-rule-verifier.d.ts +1 -1
  246. package/dist/semantic-rule-verifier.js +8 -8
  247. package/dist/session-observer-bands.d.ts +1 -1
  248. package/dist/session-observer-state.d.ts +1 -1
  249. package/dist/shared-context/manager.d.ts +1 -1
  250. package/dist/signal.d.ts +1 -1
  251. package/dist/storage.d.ts +1 -1
  252. package/dist/storage.js +6 -6
  253. package/dist/summarizer.d.ts +1 -1
  254. package/dist/summary-snapshot.d.ts +1 -1
  255. package/dist/temporal-supersession.d.ts +1 -1
  256. package/dist/temporal-validity.d.ts +1 -1
  257. package/dist/threading.d.ts +1 -1
  258. package/dist/tier-migration.d.ts +1 -1
  259. package/dist/tier-routing.d.ts +1 -1
  260. package/dist/{tier-stats-TG6GUZ6N.js → tier-stats-5XF4UBRQ.js} +4 -4
  261. package/dist/tokens.d.ts +17 -2
  262. package/dist/tokens.js +3 -1
  263. package/dist/topics.d.ts +1 -1
  264. package/dist/transcript.d.ts +1 -1
  265. package/dist/transfer/backup.js +2 -2
  266. package/dist/transfer/capsule-export.js +3 -3
  267. package/dist/transfer/capsule-import.js +3 -3
  268. package/dist/transfer/types.d.ts +12 -12
  269. package/dist/trust-score-stage.d.ts +1 -1
  270. package/dist/trust-score.d.ts +1 -1
  271. package/dist/{types-ljkd82rn.d.ts → types-C7HUgOuA.d.ts} +1 -1
  272. package/dist/types.d.ts +1 -1
  273. package/dist/utility-runtime.d.ts +1 -1
  274. package/dist/verified-recall.js +8 -8
  275. package/package.json +2 -2
  276. package/src/access-boundary.test.ts +62 -0
  277. package/src/access-boundary.ts +165 -126
  278. package/src/access-http.test.ts +781 -0
  279. package/src/access-http.ts +334 -108
  280. package/src/access-mcp.test.ts +264 -0
  281. package/src/access-mcp.ts +45 -70
  282. package/src/access-operations-batch.ts +35 -12
  283. package/src/access-surface-catalog.test.ts +22 -14
  284. package/src/access-surface-catalog.ts +6 -3
  285. package/src/access-token-capabilities.test.ts +844 -0
  286. package/src/access-token-capabilities.ts +391 -0
  287. package/src/chat/chat-mcp.test.ts +232 -0
  288. package/src/chat/chat-session.ts +34 -0
  289. package/src/index.ts +14 -0
  290. package/src/mcp-read-only-tools.ts +61 -0
  291. package/src/tokens.ts +32 -3
  292. package/dist/chunk-2FOD2YY3.js.map +0 -1
  293. package/dist/chunk-DR6PEJU3.js.map +0 -1
  294. package/dist/chunk-E2SPGGUI.js.map +0 -1
  295. package/dist/chunk-FZGGZAAU.js.map +0 -1
  296. package/dist/chunk-ILFJ7AVS.js.map +0 -1
  297. /package/dist/{capsule-crypto-CZJSLEFG.js.map → access-token-capabilities.js.map} +0 -0
  298. /package/dist/{auto-sync-HMHQHUAG.js.map → auto-sync-G6IU7L6C.js.map} +0 -0
  299. /package/dist/{chunk-7QWMTOIF.js.map → chunk-2B7DP7AP.js.map} +0 -0
  300. /package/dist/{chunk-4BFRBP45.js.map → chunk-2JFDHI2K.js.map} +0 -0
  301. /package/dist/{chunk-OHDJ34CC.js.map → chunk-2SFEFR5I.js.map} +0 -0
  302. /package/dist/{chunk-YDSWRD33.js.map → chunk-5K5VEDFY.js.map} +0 -0
  303. /package/dist/{chunk-CZAZX7JP.js.map → chunk-7BAJAXGB.js.map} +0 -0
  304. /package/dist/{chunk-CXLNZXG5.js.map → chunk-7XGSIUP5.js.map} +0 -0
  305. /package/dist/{chunk-ZPOROUTX.js.map → chunk-AOYJVCFN.js.map} +0 -0
  306. /package/dist/{chunk-IC6QX5TC.js.map → chunk-BGMSRN6I.js.map} +0 -0
  307. /package/dist/{chunk-5QZDMSSN.js.map → chunk-CXEL3Y22.js.map} +0 -0
  308. /package/dist/{chunk-M5QKGHCR.js.map → chunk-CYWCKFCL.js.map} +0 -0
  309. /package/dist/{chunk-2U2A5HF4.js.map → chunk-D2XYYU5Q.js.map} +0 -0
  310. /package/dist/{chunk-DGYCWCQ7.js.map → chunk-DZSAXE3T.js.map} +0 -0
  311. /package/dist/{chunk-2BFLDH5F.js.map → chunk-EEIROWEG.js.map} +0 -0
  312. /package/dist/{chunk-6ZIETXZ2.js.map → chunk-H5PFR5ZT.js.map} +0 -0
  313. /package/dist/{chunk-66FLU5BT.js.map → chunk-II6WHCCW.js.map} +0 -0
  314. /package/dist/{chunk-YPEMNNNY.js.map → chunk-ITWTKT2R.js.map} +0 -0
  315. /package/dist/{chunk-AKUCU7CI.js.map → chunk-JI5AVXYO.js.map} +0 -0
  316. /package/dist/{chunk-27FLVMDC.js.map → chunk-KMF67QDQ.js.map} +0 -0
  317. /package/dist/{chunk-FLVL4L3L.js.map → chunk-KTULX4YO.js.map} +0 -0
  318. /package/dist/{chunk-4P4626RY.js.map → chunk-L4UZBJNM.js.map} +0 -0
  319. /package/dist/{chunk-JLR3JSTL.js.map → chunk-L6V3WJ55.js.map} +0 -0
  320. /package/dist/{chunk-6BISDEN6.js.map → chunk-LNHJ32NK.js.map} +0 -0
  321. /package/dist/{chunk-437S3G37.js.map → chunk-LNQUAJNC.js.map} +0 -0
  322. /package/dist/{chunk-LOR6ZM5R.js.map → chunk-MFDJ63U5.js.map} +0 -0
  323. /package/dist/{chunk-HRCACVHL.js.map → chunk-NFTABV56.js.map} +0 -0
  324. /package/dist/{chunk-OUMTBFSQ.js.map → chunk-NKCPUHCI.js.map} +0 -0
  325. /package/dist/{chunk-KCJ5H6XQ.js.map → chunk-ODSYJXDU.js.map} +0 -0
  326. /package/dist/{chunk-2QSZNTDO.js.map → chunk-RKNJBZ55.js.map} +0 -0
  327. /package/dist/{chunk-NG7YBCJO.js.map → chunk-SNBEVJBX.js.map} +0 -0
  328. /package/dist/{chunk-CZAA5N5S.js.map → chunk-SNNIUDRS.js.map} +0 -0
  329. /package/dist/{chunk-P6SYWE4G.js.map → chunk-STCCFQU5.js.map} +0 -0
  330. /package/dist/{chunk-X4A62ET7.js.map → chunk-TI7IHDBQ.js.map} +0 -0
  331. /package/dist/{chunk-BGTAL46Y.js.map → chunk-TV7C3HCB.js.map} +0 -0
  332. /package/dist/{chunk-EPGWWCZF.js.map → chunk-UBGAOYBP.js.map} +0 -0
  333. /package/dist/{chunk-M7GGMGMZ.js.map → chunk-UDCDGP6A.js.map} +0 -0
  334. /package/dist/{chunk-2MR3MFQB.js.map → chunk-UPSAT4PY.js.map} +0 -0
  335. /package/dist/{chunk-CGS7UKWM.js.map → chunk-VCW74PNH.js.map} +0 -0
  336. /package/dist/{chunk-CLX5VDNY.js.map → chunk-Y4RKAQZ7.js.map} +0 -0
  337. /package/dist/{chunk-EZ25VE3G.js.map → chunk-YNDLCWXS.js.map} +0 -0
  338. /package/dist/{first-start-migration-DY6YCRC2.js.map → first-start-migration-VBJQL6OI.js.map} +0 -0
  339. /package/dist/{tier-stats-TG6GUZ6N.js.map → tier-stats-5XF4UBRQ.js.map} +0 -0
@@ -13,6 +13,7 @@ import path from "node:path";
13
13
  import test from "node:test";
14
14
 
15
15
  import { EngramMcpServer } from "./access-mcp.js";
16
+ import { tokenCapabilityStore } from "./access-token-capabilities.js";
16
17
  import { EngramAccessInputError, type EngramAccessService } from "./access-service.js";
17
18
  import { parseConfig } from "./config.js";
18
19
  import { readPair, writePair } from "./contradiction/contradiction-review.js";
@@ -979,3 +980,266 @@ test("emitLegacyTools=false still allows calling tools under BOTH names (adverti
979
980
  "legacy engram.recall call still works (callability preserved)",
980
981
  );
981
982
  });
983
+
984
+ // ──────────────────────────────────────────────────────────────────────────
985
+ // Issue #1850 round 5 (finding 3): tools/list must reflect the token's ops scope
986
+ // ──────────────────────────────────────────────────────────────────────────
987
+
988
+ test("tools/list: deny-all (ops:[]) token sees NO tools (issue #1850 finding 3)", async () => {
989
+ const server = new EngramMcpServer(makeMockService(), { emitLegacyTools: false });
990
+ const names = await tokenCapabilityStore.run({ version: 1, ops: [] }, async () =>
991
+ listToolNames(await server.handleRequest(TOOLS_LIST_REQUEST)),
992
+ );
993
+ assert.deepEqual(names, [], "a deny-all token must enumerate zero tools");
994
+ });
995
+
996
+ test("tools/list: ops-scoped token sees ONLY its permitted tools (issue #1850 finding 3)", async () => {
997
+ const server = new EngramMcpServer(makeMockService(), { emitLegacyTools: false });
998
+ const names = await tokenCapabilityStore.run({ version: 1, ops: ["recall", "memory_get"] }, async () =>
999
+ listToolNames(await server.handleRequest(TOOLS_LIST_REQUEST)),
1000
+ );
1001
+ assert.ok(names.includes("remnic.recall"), "permitted recall tool advertised");
1002
+ assert.ok(names.includes("remnic.memory_get"), "permitted memory_get tool advertised");
1003
+ assert.ok(!names.includes("remnic.memory_store"), "non-permitted tool must NOT be advertised");
1004
+ assert.ok(!names.includes("remnic.observe"), "non-permitted tool must NOT be advertised");
1005
+ assert.equal(names.length, 2, "exactly the two permitted tools advertised");
1006
+ });
1007
+
1008
+ test("tools/list: unrestricted token (ops axis absent) sees the FULL surface (unchanged)", async () => {
1009
+ const server = new EngramMcpServer(makeMockService(), { emitLegacyTools: false });
1010
+ // No capability context at all (stdio / direct call) ⇒ full surface.
1011
+ const noContext = listToolNames(await server.handleRequest(TOOLS_LIST_REQUEST));
1012
+ // Explicit-unrestricted record (version present, no ops axis) ⇒ same surface.
1013
+ const explicitUnrestricted = await tokenCapabilityStore.run({ version: 1 }, async () =>
1014
+ listToolNames(await server.handleRequest(TOOLS_LIST_REQUEST)),
1015
+ );
1016
+ assert.deepEqual(explicitUnrestricted, noContext, "unrestricted record sees the same surface as legacy/no-context");
1017
+ assert.ok(noContext.length > 10, "sanity: the full tool surface is non-trivial");
1018
+ });
1019
+
1020
+ // ===========================================================================
1021
+ // Issue #1850 round 9 — MCP review_resolve namespace allow-list gate.
1022
+ // Mirror of the HTTP review/resolve namespace gate (access-http.test.ts).
1023
+ // review_resolve selects its target BY pairId, so the pair's namespace comes
1024
+ // from the record — NOT a request param the MCP-over-HTTP tools/call gate
1025
+ // (toolAcceptsNamespace) already enforces, because this tool's schema carries
1026
+ // no `namespace` property. A namespace-scoped bearer must NOT mutate a pair in
1027
+ // a namespace outside its allow-list. Both canonical (remnic.*) and legacy
1028
+ // (engram.*) tool-name aliases route through the SAME handler/gate.
1029
+ // ===========================================================================
1030
+
1031
+ test("MCP review_resolve: namespace-scoped token cannot mutate a pair in a disallowed namespace (issue #1850 round 9)", async () => {
1032
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-mcp-review-resolve-scoped-"));
1033
+ const allowedPair = writePair(dir, {
1034
+ namespace: "ns_a",
1035
+ memoryIds: ["a-1", "a-2"],
1036
+ verdict: "contradicts",
1037
+ rationale: "pair in the allowed namespace",
1038
+ confidence: 0.9,
1039
+ detectedAt: new Date().toISOString(),
1040
+ });
1041
+ const deniedPair = writePair(dir, {
1042
+ namespace: "ns_b",
1043
+ memoryIds: ["b-1", "b-2"],
1044
+ verdict: "contradicts",
1045
+ rationale: "pair in a denied namespace",
1046
+ confidence: 0.9,
1047
+ detectedAt: new Date().toISOString(),
1048
+ });
1049
+ const storage = { dir } as unknown as StorageManager;
1050
+ const service = {
1051
+ ...makeMockService(),
1052
+ configRef: parseConfig({
1053
+ memoryDir: dir,
1054
+ namespacesEnabled: true,
1055
+ defaultNamespace: "default",
1056
+ }),
1057
+ memoryDir: dir,
1058
+ storageRef: storage,
1059
+ getWritableStorageForNamespace: async (namespace: string | undefined) => ({
1060
+ namespace: namespace ?? "default",
1061
+ storage,
1062
+ }),
1063
+ } as unknown as EngramAccessService;
1064
+ const server = new EngramMcpServer(service, { principal: "writer" });
1065
+ const readOutcome = (response: unknown): { isError: boolean; text: string } => {
1066
+ if (typeof response !== "object" || response === null || !("result" in response)) {
1067
+ return { isError: false, text: "" };
1068
+ }
1069
+ const result = response.result;
1070
+ if (typeof result !== "object" || result === null) return { isError: false, text: "" };
1071
+ const isError = "isError" in result && result.isError === true;
1072
+ let text = "";
1073
+ if ("content" in result && Array.isArray(result.content)) {
1074
+ const entry = result.content[0];
1075
+ if (entry != null && typeof entry === "object" && "text" in entry && typeof entry.text === "string") {
1076
+ text = entry.text;
1077
+ }
1078
+ }
1079
+ return { isError, text };
1080
+ };
1081
+ try {
1082
+ // ── scoped to ns_a: denied-namespace pair → isError (fail closed), NOT mutated ──
1083
+ const denied = await tokenCapabilityStore.run({ version: 1, namespaces: ["ns_a"] }, async () =>
1084
+ readOutcome(await server.handleRequest(makeToolRequest("engram.review_resolve", { pairId: deniedPair.pairId, verb: "both-valid" }))),
1085
+ );
1086
+ assert.equal(denied.isError, true, "scoped resolve: a pair in an unlisted namespace must be denied");
1087
+ assert.match(denied.text, /ns_b/, "denial message names the forbidden namespace");
1088
+ assert.notEqual(
1089
+ readPair(dir, deniedPair.pairId)?.resolution,
1090
+ "both-valid",
1091
+ "scoped resolve: the denied pair must remain unresolved (no mutation leak)",
1092
+ );
1093
+
1094
+ // ── scoped to ns_a: allowed-namespace pair → resolves, mutated ──
1095
+ const allowed = await tokenCapabilityStore.run({ version: 1, namespaces: ["ns_a"] }, async () =>
1096
+ readOutcome(await server.handleRequest(makeToolRequest("engram.review_resolve", { pairId: allowedPair.pairId, verb: "both-valid" }))),
1097
+ );
1098
+ assert.equal(allowed.isError, false, "scoped resolve: a pair in the allowed namespace must succeed");
1099
+ assert.equal(
1100
+ readPair(dir, allowedPair.pairId)?.resolution,
1101
+ "both-valid",
1102
+ "scoped resolve: the allowed pair is marked resolved",
1103
+ );
1104
+
1105
+ // ── canonical alias (remnic.review_resolve) routes through the SAME gate ──
1106
+ const deniedCanonical = writePair(dir, {
1107
+ namespace: "ns_b",
1108
+ memoryIds: ["b-3", "b-4"],
1109
+ verdict: "contradicts",
1110
+ rationale: "pair for canonical-alias denial",
1111
+ confidence: 0.9,
1112
+ detectedAt: new Date().toISOString(),
1113
+ });
1114
+ const deniedViaCanonical = await tokenCapabilityStore.run({ version: 1, namespaces: ["ns_a"] }, async () =>
1115
+ readOutcome(await server.handleRequest(makeToolRequest("remnic.review_resolve", { pairId: deniedCanonical.pairId, verb: "both-valid" }))),
1116
+ );
1117
+ assert.equal(deniedViaCanonical.isError, true, "canonical alias remnic.review_resolve must also be gated");
1118
+ assert.match(deniedViaCanonical.text, /ns_b/, "canonical-alias denial names the forbidden namespace");
1119
+ assert.notEqual(
1120
+ readPair(dir, deniedCanonical.pairId)?.resolution,
1121
+ "both-valid",
1122
+ "canonical-alias denied pair must remain unresolved",
1123
+ );
1124
+
1125
+ // ── unrestricted token (no namespaces axis): the denied-namespace pair is reachable ──
1126
+ const unrestrictedDenied = await tokenCapabilityStore.run({ version: 1 }, async () =>
1127
+ readOutcome(await server.handleRequest(makeToolRequest("engram.review_resolve", { pairId: deniedPair.pairId, verb: "both-valid" }))),
1128
+ );
1129
+ assert.equal(unrestrictedDenied.isError, false, "unrestricted token: the denied-namespace pair is reachable");
1130
+ assert.equal(
1131
+ readPair(dir, deniedPair.pairId)?.resolution,
1132
+ "both-valid",
1133
+ "unrestricted token resolves the previously-denied pair",
1134
+ );
1135
+ } finally {
1136
+ await rm(dir, { recursive: true, force: true });
1137
+ }
1138
+ });
1139
+
1140
+ // ===========================================================================
1141
+ // Issue #1850 round 10 — fleet-wide maintenance ops namespace gate.
1142
+ // These ops run ACROSS ALL namespaces (or a global non-namespaced layer) and
1143
+ // carry NO `namespace` arg, so the MCP tools/call effective-namespace
1144
+ // chokepoint (toolAcceptsNamespace) never fires. The `fleetWide` flag on each
1145
+ // op makes defineOperation's run wrapper reject a namespace-scoped token
1146
+ // BEFORE the handler — no side effect on denial; unrestricted/legacy allowed.
1147
+ // Both canonical (remnic.*) and legacy (engram.*) aliases route through the
1148
+ // SAME gate.
1149
+ // ===========================================================================
1150
+
1151
+ function readCallToolOutcome(response: unknown): { isError: boolean; text: string } {
1152
+ if (typeof response !== "object" || response === null || !("result" in response)) {
1153
+ return { isError: false, text: "" };
1154
+ }
1155
+ const result = response.result;
1156
+ if (typeof result !== "object" || result === null) return { isError: false, text: "" };
1157
+ const isError = "isError" in result && result.isError === true;
1158
+ let text = "";
1159
+ if ("content" in result && Array.isArray(result.content)) {
1160
+ const entry = result.content[0];
1161
+ if (entry != null && typeof entry === "object" && "text" in entry && typeof entry.text === "string") {
1162
+ text = entry.text;
1163
+ }
1164
+ }
1165
+ return { isError, text };
1166
+ }
1167
+
1168
+ const FLEET_WIDE_MAINTENANCE_TOOLS = [
1169
+ "graph_edge_decay_run",
1170
+ "memory_summarize_hourly",
1171
+ "conversation_index_update",
1172
+ "live_connectors_run",
1173
+ "continuity_audit_generate",
1174
+ "shared_context_cross_signals_run",
1175
+ "shared_context_curate_daily",
1176
+ "compounding_weekly_synthesize",
1177
+ "compounding_promote_candidate",
1178
+ "compression_guidelines_optimize",
1179
+ "compression_guidelines_activate",
1180
+ ] as const;
1181
+
1182
+ test("MCP fleet-wide maintenance ops: namespace-scoped token denied for every op, no side effect (issue #1850 round 10)", async () => {
1183
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-mcp-fleetwide-scoped-"));
1184
+ let sideEffects = 0;
1185
+ // Every fleet-wide op that dispatches to a service method routes through
1186
+ // this tracker; graph_edge_decay_run short-circuits via graphEdgeDecayEnabled
1187
+ // false. Under a scoped token NONE may run — the guard throws first.
1188
+ const track = (): Promise<{ ok: true }> => { sideEffects += 1; return Promise.resolve({ ok: true }); };
1189
+ const service = {
1190
+ ...makeMockService(),
1191
+ configRef: parseConfig({ memoryDir: dir, graphEdgeDecayEnabled: false }),
1192
+ memoryDir: dir,
1193
+ memorySummarizeHourly: track,
1194
+ conversationIndexUpdate: track,
1195
+ liveConnectorsRun: track,
1196
+ continuityAuditGenerate: track,
1197
+ sharedContextCrossSignalsRun: track,
1198
+ sharedContextCurateDaily: track,
1199
+ compoundingWeeklySynthesize: track,
1200
+ compoundingPromoteCandidate: track,
1201
+ compressionGuidelinesOptimize: track,
1202
+ compressionGuidelinesActivate: track,
1203
+ } as unknown as EngramAccessService;
1204
+ const server = new EngramMcpServer(service, { principal: "ops" });
1205
+ try {
1206
+ for (const tool of FLEET_WIDE_MAINTENANCE_TOOLS) {
1207
+ // Legacy alias (engra.*).
1208
+ const legacy = await tokenCapabilityStore.run({ version: 1, namespaces: ["ns_a"] }, async () =>
1209
+ readCallToolOutcome(await server.handleRequest(makeToolRequest("engram." + tool))),
1210
+ );
1211
+ assert.equal(legacy.isError, true, `engram.${tool}: a namespace-scoped token must be denied`);
1212
+ assert.match(legacy.text, /across all namespaces/, `engram.${tool}: denial names the fleet-wide restriction`);
1213
+ // Canonical alias (remnic.*) routes through the SAME gate.
1214
+ const canonical = await tokenCapabilityStore.run({ version: 1, namespaces: ["ns_a"] }, async () =>
1215
+ readCallToolOutcome(await server.handleRequest(makeToolRequest("remnic." + tool))),
1216
+ );
1217
+ assert.equal(canonical.isError, true, `remnic.${tool}: canonical alias must also be gated`);
1218
+ }
1219
+ assert.equal(sideEffects, 0, "no fleet-wide maintenance service method may run for a namespace-scoped token");
1220
+ } finally {
1221
+ await rm(dir, { recursive: true, force: true });
1222
+ }
1223
+ });
1224
+
1225
+ test("MCP graph_edge_decay_run: unrestricted and legacy tokens reach the handler (issue #1850 round 10)", async () => {
1226
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-mcp-decay-unrestricted-"));
1227
+ try {
1228
+ const service = {
1229
+ ...makeMockService(),
1230
+ configRef: parseConfig({ memoryDir: dir, graphEdgeDecayEnabled: false }),
1231
+ memoryDir: dir,
1232
+ } as unknown as EngramAccessService;
1233
+ const server = new EngramMcpServer(service, { principal: "ops" });
1234
+ // Explicit-unrestricted record (version present, no namespaces axis).
1235
+ const unrestricted = await tokenCapabilityStore.run({ version: 1 }, async () =>
1236
+ readCallToolOutcome(await server.handleRequest(makeToolRequest("engram.graph_edge_decay_run"))),
1237
+ );
1238
+ assert.equal(unrestricted.isError, false, "unrestricted token reaches the handler");
1239
+ // Legacy / no ALS at all (cron, internal caller) — same path.
1240
+ const legacy = readCallToolOutcome(await server.handleRequest(makeToolRequest("remnic.graph_edge_decay_run")));
1241
+ assert.equal(legacy.isError, false, "legacy token (no ALS) reaches the handler");
1242
+ } finally {
1243
+ await rm(dir, { recursive: true, force: true });
1244
+ }
1245
+ });
package/src/access-mcp.ts CHANGED
@@ -5,6 +5,7 @@ import type { Readable, Writable } from "node:stream";
5
5
  // (memory_get / memory_search / memory_store) as a side effect; callTool
6
6
  // dispatches migrated tools through the registry (issue #1525).
7
7
  import { type OperationName, getOperation } from "./access-boundary.js";
8
+ import { assertOperationAllowed, capabilityAllowsOp, enforceNamespaceAllowList, tokenCapabilityStore } from "./access-token-capabilities.js";
8
9
  import {
9
10
  type ActionConfidenceRequest,
10
11
  type CapsuleExportRequest,
@@ -20,6 +21,7 @@ import { EngramAccessInputError, type EngramAccessRecallResponse, type EngramAcc
20
21
  import "./access-operations.js";
21
22
  import { validateBriefingFormat } from "./briefing.js";
22
23
  import { processChatMessage } from "./chat/chat-factory.js";
24
+ import { enforceChatSessionNamespace } from "./chat/chat-session.js";
23
25
  import { type CitationMetadata, buildCitationGuidance } from "./citations.js";
24
26
  import { projectTagProjectId } from "./coding/coding-namespace.js";
25
27
  import {
@@ -37,6 +39,7 @@ import type { RecallDisclosure, RecallPlanMode } from "./types.js";
37
39
  import { expandTildePath } from "./utils/path.js";
38
40
 
39
41
  import { applyToolOutputSchemas } from "./access-mcp-output-schemas.js";
42
+ import { MCP_READ_ONLY_TOOL_SUFFIXES } from "./mcp-read-only-tools.js";
40
43
  type JsonRpcId = string | number | null;
41
44
 
42
45
  type JsonRpcRequest = {
@@ -79,68 +82,6 @@ type McpResource = {
79
82
  _meta?: Record<string, unknown>;
80
83
  };
81
84
 
82
- /**
83
- * Conservative allowlist of canonical MCP tool suffixes that are
84
- * unambiguously read-only. Tools in this set are tagged with
85
- * `annotations: { readOnlyHint: true }` so ChatGPT (and other MCP
86
- * clients that honor the hint) can skip per-call confirmation.
87
- *
88
- * The list is suffix-based so it covers both the `remnic.*` and
89
- * `engram.*` naming forms. Anything not on it stays unannotated:
90
- * uncertainty is resolved as "might mutate".
91
- *
92
- * Excluded by construction: anything that writes, runs a pipeline,
93
- * flushes, applies, records, imports, or destructively deletes.
94
- */
95
- const MCP_READ_ONLY_TOOL_SUFFIXES: Readonly<Record<string, true>> = {
96
- recall: true,
97
- recall_explain: true,
98
- recall_tier_explain: true,
99
- recall_xray: true,
100
- briefing: true,
101
- wearables_status: true,
102
- transcript_day: true,
103
- transcript_search: true,
104
- transcript_memories: true,
105
- action_confidence: true,
106
- capsule_list: true,
107
- procedural_stats: true,
108
- memory_get: true,
109
- memory_timeline: true,
110
- entity_get: true,
111
- review_queue_list: true,
112
- lcm_search: true,
113
- continuity_incident_list: true,
114
- identity_anchor_get: true,
115
- memory_identity: true,
116
- memory_search: true,
117
- memory_profile: true,
118
- memory_entities_list: true,
119
- memory_questions: true,
120
- memory_last_recall: true,
121
- memory_intent_debug: true,
122
- memory_qmd_debug: true,
123
- memory_graph_explain: true,
124
- graph_snapshot: true,
125
- review_list: true,
126
- profiling_report: true,
127
- peer_list: true,
128
- peer_get: true,
129
- peer_profile_get: true,
130
- console_state: true,
131
- dreams_status: true,
132
- codegraph_list_projects: true,
133
- codegraph_index_status: true,
134
- codegraph_search_graph: true,
135
- codegraph_trace_path: true,
136
- codegraph_detect_changes: true,
137
- codegraph_query_graph: true,
138
- codegraph_get_schema: true,
139
- codegraph_get_snippet: true,
140
- codegraph_get_architecture: true,
141
- codegraph_search_code: true,
142
- };
143
-
144
85
  /**
145
86
  * MCP protocol versions this server understands, ordered newest → oldest.
146
87
  * Exported so the HTTP transport can validate the `MCP-Protocol-Version`
@@ -2592,13 +2533,15 @@ export class EngramMcpServer {
2592
2533
  };
2593
2534
  }
2594
2535
  if (method === "tools/list") {
2595
- return {
2596
- jsonrpc: "2.0",
2597
- id,
2598
- result: {
2599
- tools: this.tools,
2600
- },
2601
- };
2536
+ // Issue #1850 round 5 (finding 3): a scoped/deny-all token must not
2537
+ // enumerate the full tool surface — filter advertised tools by the
2538
+ // token's ops allow-list via the same map callTool uses. Unrestricted
2539
+ // tokens (ops axis absent) see everything; unmapped tool ⇒ "" ⇒ hidden.
2540
+ const caps = tokenCapabilityStore.getStore();
2541
+ const tools = caps?.ops === undefined
2542
+ ? this.tools
2543
+ : this.tools.filter((t) => capabilityAllowsOp(caps, MCP_MIGRATED_OPERATIONS[toLegacyToolName(t.name)] ?? ""));
2544
+ return { jsonrpc: "2.0", id, result: { tools } };
2602
2545
  }
2603
2546
  if (method === "resources/list") {
2604
2547
  return {
@@ -2838,6 +2781,19 @@ export class EngramMcpServer {
2838
2781
  return inputSchema?.additionalProperties === true;
2839
2782
  }
2840
2783
 
2784
+ /**
2785
+ * Whether a tool accepts a `namespace` argument — i.e. it is a
2786
+ * namespace-scoped operation whose effective namespace MUST be gated by the
2787
+ * per-token allow-list. Exposed (public) so the HTTP MCP transport can run
2788
+ * the SAME effective-namespace enforcement the REST surface runs, without
2789
+ * duplicating the tool-schema introspection (issue #1850). Namespace-
2790
+ * agnostic tools (peer/wearables/etc.) return false and stay ungated,
2791
+ * matching the REST surface where those routes never call resolveNamespace.
2792
+ */
2793
+ toolAcceptsNamespace(name: string): boolean {
2794
+ return this.toolAcceptsArgument(name, "namespace");
2795
+ }
2796
+
2841
2797
  /**
2842
2798
  * Determine whether oai-mem-citation guidance should be appended to recall.
2843
2799
  * Returns true when explicitly enabled via config OR when auto-detect is
@@ -2931,10 +2887,29 @@ export class EngramMcpServer {
2931
2887
  } else if (migrated.startsWith("codegraph_")) {
2932
2888
  envelope = { ...args, tool: migrated.slice("codegraph_".length) };
2933
2889
  } else if (migrated === "chat_message") {
2934
- // memory_chat dispatches to processChatMessage, not a pure service call.
2890
+ // memory_chat bypasses op.run(); gate op + resumed-session namespace like the HTTP route (#1850 r6).
2891
+ assertOperationAllowed(tokenCapabilityStore.getStore(), migrated);
2935
2892
  const message = typeof args.message === "string" ? args.message : "";
2936
2893
  if (!message) throw new EngramAccessInputError("message is required");
2937
2894
  const chatSessionId = typeof args.chatSessionId === "string" ? args.chatSessionId : undefined;
2895
+ if (chatSessionId) {
2896
+ await enforceChatSessionNamespace(this.service, chatSessionId);
2897
+ } else {
2898
+ // NEW chat session (no chatSessionId): processChatMessage mints a fresh
2899
+ // session under the EFFECTIVE namespace (scope.namespace OR server
2900
+ // default). Unlike the HTTP chat handler (which forwards no namespace),
2901
+ // the MCP scope CAN carry one (Thread 17), so route it through the SAME
2902
+ // effective-namespace chokepoint as the HTTP new-chat path
2903
+ // (enforceNamespaceAllowList maps undefined → default) — a namespace-
2904
+ // scoped token CANNOT start a chat in a namespace outside its allow-
2905
+ // list, including an unconfigured/forbidden server default. Fail closed;
2906
+ // no-op for unrestricted/legacy tokens (issue #1850 round 8).
2907
+ enforceNamespaceAllowList(
2908
+ tokenCapabilityStore.getStore(),
2909
+ scope?.namespace,
2910
+ this.service.configRef?.defaultNamespace,
2911
+ );
2912
+ }
2938
2913
  const chatResult = await processChatMessage({
2939
2914
  service: this.service,
2940
2915
  config: this.service.configRef?.chat,
@@ -16,6 +16,7 @@ import type { RemnicChatGptMemoryInspectorInput } from "./mcp-memory-inspector-a
16
16
  import { defineOperation } from "./access-boundary.js";
17
17
  import { EngramAccessForbiddenError } from "./access-errors.js";
18
18
  import { EngramAccessInputError, type EngramAccessService } from "./access-service.js";
19
+ import { enforceNamespaceAllowList, tokenCapabilityStore } from "./access-token-capabilities.js";
19
20
  import { projectTagProjectId } from "./coding/coding-namespace.js";
20
21
  import { expandTildePath } from "./utils/path.js";
21
22
  import { resolvePrincipal } from "./namespaces/principal.js";
@@ -25,7 +26,7 @@ import {
25
26
  buildChatGptMemoryInspectorActionRequest,
26
27
  buildChatGptMemoryInspectorResult,
27
28
  } from "./mcp-memory-inspector-app.js";
28
- import { listPairs, isDefaultReviewNamespace } from "./contradiction/contradiction-review.js";
29
+ import { listPairs, isDefaultReviewNamespace, readPair } from "./contradiction/contradiction-review.js";
29
30
  import { executeResolution, isValidResolutionVerb } from "./contradiction/resolution.js";
30
31
  import { runContradictionScan } from "./contradiction/contradiction-scan.js";
31
32
  import { runGraphEdgeDecayMaintenanceAcrossNamespaces } from "./maintenance/graph-edge-decay.js";
@@ -166,7 +167,7 @@ defineOperation({ name: "lcm_compaction_flush", description: "Flush LCM compacti
166
167
  defineOperation({ name: "lcm_compaction_record", description: "Record LCM compaction.", schema: strictSchema({ sessionKey: S.str, namespace: S.str, tokensBefore: S.num, tokensAfter: S.num }), handler: async (input, ctx) => ({ result: await ctx.service.lcmCompactionRecord({ sessionKey: optStr(input.sessionKey) ?? "", namespace: optStr(input.namespace), tokensBefore: optNum(input.tokensBefore) ?? 0, tokensAfter: optNum(input.tokensAfter) ?? 0, authenticatedPrincipal: ctx.authenticatedPrincipal }) }) });
167
168
 
168
169
  // === CONTINUITY/IDENTITY ===
169
- defineOperation({ name: "continuity_audit_generate", description: "Continuity audit.", schema: strictSchema({ period: S.str, key: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.continuityAuditGenerate({ period: optStr(input.period) === "monthly" ? "monthly" : optStr(input.period) === "weekly" ? "weekly" : undefined, key: optStr(input.key) }) }) });
170
+ defineOperation({ name: "continuity_audit_generate", description: "Continuity audit.", fleetWide: true, schema: strictSchema({ period: S.str, key: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.continuityAuditGenerate({ period: optStr(input.period) === "monthly" ? "monthly" : optStr(input.period) === "weekly" ? "weekly" : undefined, key: optStr(input.key) }) }) });
170
171
  defineOperation({ name: "continuity_incident_open", description: "Open incident.", schema: strictSchema({ symptom: S.str, namespace: S.str, triggerWindow: S.str, suspectedCause: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.continuityIncidentOpen({ symptom: optStr(input.symptom) ?? "", namespace: optStr(input.namespace), triggerWindow: optStr(input.triggerWindow), suspectedCause: optStr(input.suspectedCause), principal: ctx.authenticatedPrincipal }) }) });
171
172
  defineOperation({ name: "continuity_incident_close", description: "Close incident.", schema: strictSchema({ id: S.str, namespace: S.str, fixApplied: S.str, verificationResult: S.str, preventiveRule: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.continuityIncidentClose({ id: optStr(input.id) ?? "", namespace: optStr(input.namespace), fixApplied: optStr(input.fixApplied) ?? "", verificationResult: optStr(input.verificationResult) ?? "", preventiveRule: optStr(input.preventiveRule), principal: ctx.authenticatedPrincipal }) }) });
172
173
  defineOperation({ name: "continuity_incident_list", description: "List incidents.", schema: strictSchema({ state: S.str, namespace: S.str, limit: S.num }), handler: async (input, ctx) => { const st = optStr(input.state); return { result: await ctx.service.continuityIncidentList({ state: st === "closed" ? "closed" : st === "all" ? "all" : st === "open" ? "open" : undefined, namespace: optStr(input.namespace), limit: optNum(input.limit), principal: ctx.authenticatedPrincipal }) }; } });
@@ -183,12 +184,12 @@ defineOperation({ name: "work_board", description: "Manage work board.", schema:
183
184
  defineOperation({ name: "shared_context_write_output", description: "Write shared output.", schema: strictSchema({ agentId: S.str, title: S.str, content: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.sharedContextWriteOutput({ agentId: optStr(input.agentId) ?? "", title: optStr(input.title) ?? "", content: optStr(input.content) ?? "" }) }) });
184
185
  defineOperation({ name: "shared_feedback_record", description: "Record shared feedback.", schema: strictSchema({ agent: S.str, decision: S.str, reason: S.str, date: S.str, learning: S.str, outcome: S.str, severity: S.str, confidence: S.num, workflow: S.str, tags: S.strArr, evidenceWindowStart: S.str, evidenceWindowEnd: S.str, refs: S.strArr }), handler: async (input, ctx) => { const sv = optStr(input.severity); return { result: await ctx.service.sharedFeedbackRecord({ agent: optStr(input.agent) ?? "", decision: (optStr(input.decision) as "rejected" | "approved" | "approved_with_feedback") ?? "approved", reason: optStr(input.reason) ?? "", date: optStr(input.date), learning: optStr(input.learning), outcome: optStr(input.outcome), severity: sv === "low" || sv === "medium" || sv === "high" ? sv as "low" | "medium" | "high" : undefined, confidence: optNum(input.confidence), workflow: optStr(input.workflow), tags: optStrArr(input.tags), evidenceWindowStart: optStr(input.evidenceWindowStart), evidenceWindowEnd: optStr(input.evidenceWindowEnd), refs: optStrArr(input.refs) }) }; } });
185
186
  defineOperation({ name: "shared_priorities_append", description: "Append priorities.", schema: strictSchema({ agentId: S.str, text: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.sharedPrioritiesAppend({ agentId: optStr(input.agentId) ?? "", text: optStr(input.text) ?? "" }) }) });
186
- defineOperation({ name: "shared_context_cross_signals_run", description: "Run cross signals.", schema: strictSchema({ date: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.sharedContextCrossSignalsRun({ date: optStr(input.date) }) }) });
187
- defineOperation({ name: "shared_context_curate_daily", description: "Curate daily.", schema: strictSchema({ date: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.sharedContextCurateDaily({ date: optStr(input.date) }) }) });
188
- defineOperation({ name: "compounding_weekly_synthesize", description: "Weekly synthesize.", schema: strictSchema({ weekId: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.compoundingWeeklySynthesize({ weekId: optStr(input.weekId) }) }) });
189
- defineOperation({ name: "compounding_promote_candidate", description: "Promote candidate.", schema: strictSchema({ weekId: S.str, candidateId: S.str, dryRun: S.bool }), handler: async (input, ctx) => ({ result: await ctx.service.compoundingPromoteCandidate({ weekId: optStr(input.weekId) ?? "", candidateId: optStr(input.candidateId) ?? "", dryRun: input.dryRun === true }) }) });
190
- defineOperation({ name: "compression_guidelines_optimize", description: "Optimize guidelines.", schema: strictSchema({ dryRun: S.bool, eventLimit: S.num }), handler: async (input, ctx) => ({ result: await ctx.service.compressionGuidelinesOptimize({ dryRun: input.dryRun === true, eventLimit: optNum(input.eventLimit) }) }) });
191
- defineOperation({ name: "compression_guidelines_activate", description: "Activate guidelines.", schema: strictSchema({ expectedContentHash: S.str, expectedGuidelineVersion: S.num }), handler: async (input, ctx) => ({ result: await ctx.service.compressionGuidelinesActivate({ expectedContentHash: optStr(input.expectedContentHash), expectedGuidelineVersion: optNum(input.expectedGuidelineVersion) }) }) });
187
+ defineOperation({ name: "shared_context_cross_signals_run", description: "Run cross signals.", fleetWide: true, schema: strictSchema({ date: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.sharedContextCrossSignalsRun({ date: optStr(input.date) }) }) });
188
+ defineOperation({ name: "shared_context_curate_daily", description: "Curate daily.", fleetWide: true, schema: strictSchema({ date: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.sharedContextCurateDaily({ date: optStr(input.date) }) }) });
189
+ defineOperation({ name: "compounding_weekly_synthesize", description: "Weekly synthesize.", fleetWide: true, schema: strictSchema({ weekId: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.compoundingWeeklySynthesize({ weekId: optStr(input.weekId) }) }) });
190
+ defineOperation({ name: "compounding_promote_candidate", description: "Promote candidate.", fleetWide: true, schema: strictSchema({ weekId: S.str, candidateId: S.str, dryRun: S.bool }), handler: async (input, ctx) => ({ result: await ctx.service.compoundingPromoteCandidate({ weekId: optStr(input.weekId) ?? "", candidateId: optStr(input.candidateId) ?? "", dryRun: input.dryRun === true }) }) });
191
+ defineOperation({ name: "compression_guidelines_optimize", description: "Optimize guidelines.", fleetWide: true, schema: strictSchema({ dryRun: S.bool, eventLimit: S.num }), handler: async (input, ctx) => ({ result: await ctx.service.compressionGuidelinesOptimize({ dryRun: input.dryRun === true, eventLimit: optNum(input.eventLimit) }) }) });
192
+ defineOperation({ name: "compression_guidelines_activate", description: "Activate guidelines.", fleetWide: true, schema: strictSchema({ expectedContentHash: S.str, expectedGuidelineVersion: S.num }), handler: async (input, ctx) => ({ result: await ctx.service.compressionGuidelinesActivate({ expectedContentHash: optStr(input.expectedContentHash), expectedGuidelineVersion: optNum(input.expectedGuidelineVersion) }) }) });
192
193
 
193
194
  // === MEMORY DEBUG/GRAPH/FEEDBACK ===
194
195
  defineOperation({ name: "memory_profile", description: "Memory profile.", schema: strictSchema({ namespace: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.memoryProfile(optStr(input.namespace), ctx.authenticatedPrincipal) }) });
@@ -225,17 +226,38 @@ defineOperation({ name: "review_resolve", description: "Resolve pair.", schema:
225
226
  if (!pid) throw new EngramAccessInputError("pairId is required");
226
227
  if (!vb) throw new EngramAccessInputError("verb is required");
227
228
  if (!isValidResolutionVerb(vb)) throw new EngramAccessInputError("Invalid verb: " + vb);
229
+ // Per-token namespace enforcement (issue #1850 round 9): review_resolve
230
+ // selects its target BY pairId, so the affected namespace comes from the
231
+ // record — NOT a request param that the MCP-over-HTTP tools/call gate
232
+ // (toolAcceptsNamespace) already enforces (this tool's schema carries no
233
+ // `namespace` property). A namespace-scoped bearer must not mutate a
234
+ // contradiction pair in a namespace outside its allow-list. Load the pair
235
+ // to learn its namespace, enforce the token's scope via the SAME
236
+ // effective-namespace chokepoint the HTTP review/resolve route uses
237
+ // (enforceNamespaceAllowList maps undefined → default, so a scoped token
238
+ // whose allow-list INCLUDES the default can resolve a legacy pair), and
239
+ // fail closed BEFORE dispatching the (mutating) resolution. A missing pair
240
+ // falls through to executeResolution's existing not-found result. No-op for
241
+ // unrestricted/legacy tokens. Mirrors access-http.ts review/resolve exactly.
242
+ const targetPair = readPair(ctx.service.memoryDir, pid);
243
+ if (targetPair) {
244
+ enforceNamespaceAllowList(
245
+ tokenCapabilityStore.getStore(),
246
+ targetPair.namespace,
247
+ ctx.service.configRef?.defaultNamespace,
248
+ );
249
+ }
228
250
  return { result: await executeResolution(ctx.service.memoryDir, ctx.service.storageRef, pid, vb, { mergedMemoryId: optStr(input.mergedMemoryId), mergedContent: optStr(input.mergedContent), storageForNamespace: async (namespace) => { const r = await ctx.service.getWritableStorageForNamespace(namespace, ctx.authenticatedPrincipal); return r.storage; }, onMergedMemoryWritten: () => {} }) };
229
251
  },
230
252
  });
231
253
  defineOperation({ name: "contradiction_scan_run", description: "Run scan.", schema: strictSchema({ namespace: S.str }), handler: async (input, ctx) => ({ result: await runContradictionScan({ storage: ctx.service.storageRef, config: ctx.service.configRef, memoryDir: ctx.service.memoryDir, embeddingLookupFactory: ctx.service.embeddingLookupFactoryRef, storageForNamespace: (ns) => ctx.service.getWritableStorageForNamespace(ns ?? undefined, ctx.authenticatedPrincipal), localLlm: ctx.service.localLlmRef, fallbackLlm: ctx.service.fallbackLlmRef, namespace: optStr(input.namespace) }) }) });
232
- defineOperation({ name: "graph_edge_decay_run", description: "Run edge decay.", schema: strictSchema({ dryRun: S.bool }), handler: async (input, ctx) => { const cfg = ctx.service.configRef; if (!cfg.graphEdgeDecayEnabled) return { result: { ranAt: new Date().toISOString(), disabled: true, reason: "graphEdgeDecayEnabled is false" } }; return { result: { results: await runGraphEdgeDecayMaintenanceAcrossNamespaces(ctx.service.memoryDir, { windowMs: cfg.graphEdgeDecayWindowMs, perWindow: cfg.graphEdgeDecayPerWindow, floor: cfg.graphEdgeDecayFloor, visibilityThreshold: cfg.graphEdgeDecayVisibilityThreshold, dryRun: input.dryRun === true, namespacesEnabled: cfg.namespacesEnabled === true, defaultNamespace: cfg.defaultNamespace }) } }; } });
254
+ defineOperation({ name: "graph_edge_decay_run", description: "Run edge decay.", fleetWide: true, schema: strictSchema({ dryRun: S.bool }), handler: async (input, ctx) => { const cfg = ctx.service.configRef; if (!cfg.graphEdgeDecayEnabled) return { result: { ranAt: new Date().toISOString(), disabled: true, reason: "graphEdgeDecayEnabled is false" } }; return { result: { results: await runGraphEdgeDecayMaintenanceAcrossNamespaces(ctx.service.memoryDir, { windowMs: cfg.graphEdgeDecayWindowMs, perWindow: cfg.graphEdgeDecayPerWindow, floor: cfg.graphEdgeDecayFloor, visibilityThreshold: cfg.graphEdgeDecayVisibilityThreshold, dryRun: input.dryRun === true, namespacesEnabled: cfg.namespacesEnabled === true, defaultNamespace: cfg.defaultNamespace }) } }; } });
233
255
 
234
256
  // === SUMMARIZE/PROFILING/PEERS/CONSOLE/DREAMS ===
235
- defineOperation({ name: "memory_summarize_hourly", description: "Hourly summarize.", schema: strictSchema({}), handler: async (_i, ctx) => ({ result: await ctx.service.memorySummarizeHourly() }) });
236
- defineOperation({ name: "conversation_index_update", description: "Update conv index.", 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) }) }; } });
257
+ defineOperation({ name: "memory_summarize_hourly", description: "Hourly summarize.", fleetWide: true, schema: strictSchema({}), handler: async (_i, ctx) => ({ result: await ctx.service.memorySummarizeHourly() }) });
258
+ 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) }) }; } });
237
259
  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) }) }; } });
238
- defineOperation({ name: "live_connectors_run", description: "Run connectors.", schema: strictSchema({ force: S.bool }), handler: async (input, ctx) => ({ result: await ctx.service.liveConnectorsRun({ authenticatedPrincipal: ctx.authenticatedPrincipal, force: input.force === true }, ctx.authenticatedPrincipal) }) });
260
+ 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) }) });
239
261
  defineOperation({ name: "peer_list", description: "List peers.", schema: strictSchema({}), handler: async (_i, ctx) => ({ result: await ctx.service.peerList() }) });
240
262
  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) }; } });
241
263
  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) }) }; } });
@@ -287,3 +309,4 @@ defineOperation({ name: "offline_sync_snapshot_stream", description: "SSE stream
287
309
  defineOperation({ name: "graph_events", description: "SSE graph events.", schema: strictSchema({}), handler: async (_i, _ctx) => ({ result: { stream: "sse" } }) });
288
310
  defineOperation({ name: "chat_message", description: "Chat message.", schema: strictSchema({ message: S.str, chatSessionId: S.str }), handler: async (_i, _ctx) => ({ result: { ok: true } }) });
289
311
  defineOperation({ name: "chat_events", description: "SSE chat events.", schema: strictSchema({}), handler: async (_i, _ctx) => ({ result: { stream: "sse" } }) });
312
+ defineOperation({ name: "adapters_status", description: "Adapter registry metadata (issue #1850 round 5). Marker op: the HTTP transport builds the response from its own adapterRegistry, but the op still must be registered so it appears in listRegisteredOperations and enforceTokenOp can gate it for scoped tokens.", schema: strictSchema({}), handler: async (_i, _ctx) => ({ result: { ok: true } }) });