@remnic/core 10.0.92 → 10.0.94

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 (501) hide show
  1. package/dist/access-admin-ops-surface.js +332 -59
  2. package/dist/access-authorization-probe.js +2 -283
  3. package/dist/access-boundary.d.ts +1 -1
  4. package/dist/access-boundary.d.ts.map +1 -1
  5. package/dist/access-boundary.js +1 -282
  6. package/dist/access-cli.js +287 -303
  7. package/dist/access-cli.js.map +1 -1
  8. package/dist/access-coding-context-resolution.js +3 -3
  9. package/dist/access-extraction-force-flush.js +332 -59
  10. package/dist/access-http-briefing.js +332 -60
  11. package/dist/access-http-query.js +332 -61
  12. package/dist/access-http.js +330 -112
  13. package/dist/access-identity-continuity-surface.js +332 -59
  14. package/dist/access-lcm-surface.js +332 -59
  15. package/dist/access-mcp-frames.d.ts +20 -6
  16. package/dist/access-mcp-frames.d.ts.map +1 -1
  17. package/dist/access-mcp-frames.js +1 -1
  18. package/dist/access-mcp.d.ts +5 -0
  19. package/dist/access-mcp.d.ts.map +1 -1
  20. package/dist/access-mcp.js +330 -87
  21. package/dist/access-namespace-preflight.js +2 -282
  22. package/dist/access-observe-transcript.js +3 -3
  23. package/dist/access-observe-write-category.js +6 -6
  24. package/dist/access-observe-write-surface.js +332 -59
  25. package/dist/access-offline-manifest.js +12 -11
  26. package/dist/access-operations-batch.js +330 -64
  27. package/dist/access-operations.js +330 -73
  28. package/dist/access-recall-concurrency.js +332 -59
  29. package/dist/access-recall-diagnostics.js +5 -4
  30. package/dist/access-recall-response.js +332 -59
  31. package/dist/access-recall-surface.js +332 -59
  32. package/dist/access-schema.js +2 -2
  33. package/dist/access-service.js +335 -62
  34. package/dist/access-surface-catalog.js +6 -283
  35. package/dist/access-surface-catalog.js.map +1 -1
  36. package/dist/active-context-transform.js +1 -1
  37. package/dist/active-memory-bridge.js +3 -2
  38. package/dist/active-recall.js +36 -37
  39. package/dist/active-recall.js.map +1 -1
  40. package/dist/activity/journal-extract.d.ts +13 -1
  41. package/dist/activity/journal-extract.d.ts.map +1 -1
  42. package/dist/{auto-sync-3AZW7MXI.js → auto-sync-BO5YZKM6.js} +13 -15
  43. package/dist/{auto-sync-3AZW7MXI.js.map → auto-sync-BO5YZKM6.js.map} +1 -1
  44. package/dist/briefing.js +20 -20
  45. package/dist/buffer.js +16 -16
  46. package/dist/calibration.js +10 -10
  47. package/dist/{capsule-merge-2IIXFXN5.js → capsule-merge-6IDAYTKG.js} +6 -6
  48. package/dist/causal-consolidation.js +23 -23
  49. package/dist/{chunk-RGUUBAT5.js → chunk-2WCLTWV2.js} +3 -3
  50. package/dist/{chunk-BQZEGOZ4.js → chunk-2YODOG2O.js} +2 -2
  51. package/dist/{chunk-WYG5UNDY.js → chunk-3AUDELZL.js} +2 -2
  52. package/dist/{chunk-MB6ZJDN6.js → chunk-3FBAEPCU.js} +2 -2
  53. package/dist/{chunk-PIITB2YZ.js → chunk-3ZS3NUEI.js} +4 -4
  54. package/dist/{chunk-ATPWZ4F7.js → chunk-4EZWSDFL.js} +5 -5
  55. package/dist/{chunk-ANZZLRUX.js → chunk-4IVRQMZW.js} +2 -2
  56. package/dist/{chunk-63E37JCN.js → chunk-4RAJT5L2.js} +4 -4
  57. package/dist/{chunk-45VUHDV2.js → chunk-5CO5G4MN.js} +2 -2
  58. package/dist/{chunk-LUJCQQ3L.js → chunk-65HWAQ4F.js} +2 -2
  59. package/dist/{chunk-T67LUYCR.js → chunk-77NTFICT.js} +5 -5
  60. package/dist/chunk-A2WFUMUN.js +86 -0
  61. package/dist/chunk-A2WFUMUN.js.map +1 -0
  62. package/dist/{chunk-UE6RRXMF.js → chunk-A4F7OIC5.js} +2 -2
  63. package/dist/chunk-AHA5VQGP.js +117 -0
  64. package/dist/chunk-AHA5VQGP.js.map +1 -0
  65. package/dist/{chunk-RRJBDXAQ.js → chunk-B377OKUP.js} +2 -2
  66. package/dist/{chunk-IUKYFTGW.js → chunk-B3DR2JBZ.js} +140 -125
  67. package/dist/chunk-B3DR2JBZ.js.map +1 -0
  68. package/dist/{chunk-Z7JIZ42P.js → chunk-BN2EBCLB.js} +3 -3
  69. package/dist/{chunk-YYOMGOHB.js → chunk-BOK6BN5M.js} +2 -2
  70. package/dist/{chunk-U6UNF5ZS.js → chunk-BPK32FRM.js} +3 -3
  71. package/dist/{chunk-OH7QLI6W.js → chunk-BPPUH5JM.js} +5 -5
  72. package/dist/{chunk-O6ZSWHVR.js → chunk-C2KE5DMD.js} +4 -4
  73. package/dist/{chunk-47F6WWLQ.js → chunk-C73N66PR.js} +2 -2
  74. package/dist/{chunk-WRT7GJ2D.js → chunk-CIWMTNHW.js} +4 -4
  75. package/dist/{chunk-HF3BKUVV.js → chunk-CL4JU3DF.js} +7 -7
  76. package/dist/{chunk-O55EKIEC.js → chunk-D3ZKHLN2.js} +2 -2
  77. package/dist/chunk-D5S6RL32.js +67137 -0
  78. package/dist/chunk-D5S6RL32.js.map +1 -0
  79. package/dist/{chunk-ETZLQOUR.js → chunk-D5XG2KPS.js} +2 -2
  80. package/dist/{chunk-CD4BCCNP.js → chunk-DJ7WVEWX.js} +2 -2
  81. package/dist/{chunk-PWXPRVLK.js → chunk-DNY63QWJ.js} +2 -2
  82. package/dist/{chunk-4CJTQHEG.js → chunk-ESLOLCD4.js} +3 -3
  83. package/dist/{chunk-VILPVT4S.js → chunk-EZ47HOJ6.js} +4 -4
  84. package/dist/{chunk-EFYN5I7Z.js → chunk-F4QFU5IZ.js} +2 -2
  85. package/dist/{chunk-T6OZW2WR.js → chunk-FA7SLQ5X.js} +7 -1
  86. package/dist/chunk-FA7SLQ5X.js.map +1 -0
  87. package/dist/{chunk-BRLXOZLM.js → chunk-FQAQ63QA.js} +729 -17
  88. package/dist/chunk-FQAQ63QA.js.map +1 -0
  89. package/dist/{chunk-FSO3G4JJ.js → chunk-FWRXW7CZ.js} +5000 -2748
  90. package/dist/chunk-FWRXW7CZ.js.map +1 -0
  91. package/dist/{chunk-XYKBWTWI.js → chunk-FZZMLGWQ.js} +2 -2
  92. package/dist/{chunk-FNXQZGUS.js → chunk-GBJUTX44.js} +2 -2
  93. package/dist/{chunk-YLXETSAH.js → chunk-GCZJ7XRI.js} +2 -2
  94. package/dist/{chunk-UDKWFDV2.js → chunk-HLUHEMN4.js} +27 -3
  95. package/dist/chunk-HLUHEMN4.js.map +1 -0
  96. package/dist/{chunk-SUSW74WU.js → chunk-IL3RHBWM.js} +56 -5
  97. package/dist/chunk-IL3RHBWM.js.map +1 -0
  98. package/dist/{chunk-JV4S6VDH.js → chunk-ILY5SSOM.js} +2 -2
  99. package/dist/{chunk-EXHXJETE.js → chunk-IQ55QGY5.js} +2 -2
  100. package/dist/{chunk-4J363OAA.js → chunk-J7BIFOYP.js} +2 -2
  101. package/dist/{chunk-QFEDITXM.js → chunk-JNDMIAJ4.js} +2 -2
  102. package/dist/chunk-JTSSSR4L.js +359 -0
  103. package/dist/chunk-JTSSSR4L.js.map +1 -0
  104. package/dist/{chunk-5KBIOI4G.js → chunk-JVBAPAWZ.js} +217 -13
  105. package/dist/chunk-JVBAPAWZ.js.map +1 -0
  106. package/dist/{chunk-E5PPE4VA.js → chunk-K2VRKCPA.js} +2 -2
  107. package/dist/{chunk-MKRYQROF.js → chunk-LGEHAQHK.js} +4 -4
  108. package/dist/{chunk-7HTUYFR3.js → chunk-LLPMMMWG.js} +27 -10
  109. package/dist/chunk-LLPMMMWG.js.map +1 -0
  110. package/dist/{chunk-XYTQJD7F.js → chunk-LMJVP5CM.js} +2 -2
  111. package/dist/{chunk-E7YZXWIS.js → chunk-LNGELD2M.js} +2 -2
  112. package/dist/{chunk-FQKBJJYB.js → chunk-LPV72KRQ.js} +2 -2
  113. package/dist/{chunk-2LCRG2YV.js → chunk-M2XR6B6V.js} +2 -2
  114. package/dist/{chunk-EQ4U73UH.js → chunk-MLAMXQAW.js} +14 -2
  115. package/dist/chunk-MLAMXQAW.js.map +1 -0
  116. package/dist/{chunk-ZU5JDLOH.js → chunk-MMRTPH3J.js} +2 -2
  117. package/dist/{chunk-TVIVY7KU.js → chunk-NED7IR7B.js} +4 -4
  118. package/dist/{chunk-6F5MJFXY.js → chunk-NHVAX7MH.js} +2 -2
  119. package/dist/{chunk-TG3R2SID.js → chunk-NMGHGTVR.js} +5 -5
  120. package/dist/{chunk-YCNIGRRF.js → chunk-NS7U4ZYU.js} +13 -117
  121. package/dist/chunk-NS7U4ZYU.js.map +1 -0
  122. package/dist/{chunk-NGHDW6UU.js → chunk-NUQEREIP.js} +2 -2
  123. package/dist/{chunk-5IIQU4JZ.js → chunk-OB72KNMF.js} +4 -4
  124. package/dist/{chunk-RIMK327Y.js → chunk-OUVYYKYU.js} +7 -7
  125. package/dist/{chunk-QJPJXCS4.js → chunk-P3WRNWWM.js} +157 -4
  126. package/dist/chunk-P3WRNWWM.js.map +1 -0
  127. package/dist/{chunk-E6M225BJ.js → chunk-PC5DRBGD.js} +18 -8
  128. package/dist/chunk-PC5DRBGD.js.map +1 -0
  129. package/dist/{chunk-RWGRSKN5.js → chunk-PT4DZDJA.js} +2 -2
  130. package/dist/{chunk-7EPY3IQL.js → chunk-PZJQ46DH.js} +2 -2
  131. package/dist/{chunk-MXHMNKWG.js → chunk-QDESY76F.js} +3 -3
  132. package/dist/{chunk-E3A7ELSG.js → chunk-QHARXWTC.js} +2 -2
  133. package/dist/{chunk-BLJT6JP2.js → chunk-QMFAMOT2.js} +2 -2
  134. package/dist/{chunk-I6QGI65T.js → chunk-QRMG4HK4.js} +3 -3
  135. package/dist/{chunk-ZU3NJNO7.js → chunk-QSF5SJAM.js} +4 -4
  136. package/dist/{chunk-5IYOKDM4.js → chunk-R23JWJD3.js} +5 -5
  137. package/dist/{chunk-RHQLIHMB.js → chunk-R4BBGPI2.js} +218 -218
  138. package/dist/chunk-R4BBGPI2.js.map +1 -0
  139. package/dist/{chunk-NT7F7WJQ.js → chunk-RFUL2ZIQ.js} +291 -93
  140. package/dist/chunk-RFUL2ZIQ.js.map +1 -0
  141. package/dist/{chunk-E4IRW2FG.js → chunk-RKABLU5M.js} +233 -40
  142. package/dist/chunk-RKABLU5M.js.map +1 -0
  143. package/dist/{chunk-P4WNBSTW.js → chunk-RNG7S3FY.js} +2 -2
  144. package/dist/{chunk-IPT2OY4B.js → chunk-RQFXZYTF.js} +35 -33
  145. package/dist/chunk-RQFXZYTF.js.map +1 -0
  146. package/dist/{chunk-5HFXGXE5.js → chunk-RUTN5MMC.js} +7 -9
  147. package/dist/{chunk-5HFXGXE5.js.map → chunk-RUTN5MMC.js.map} +1 -1
  148. package/dist/{chunk-AP6NCP3V.js → chunk-S3S72HRM.js} +8 -6
  149. package/dist/chunk-S3S72HRM.js.map +1 -0
  150. package/dist/{chunk-ZGFLIK5H.js → chunk-SPAKBUJY.js} +2 -2
  151. package/dist/{chunk-ER7XANP3.js → chunk-SQ3GEIEL.js} +4 -4
  152. package/dist/{chunk-PMJFRDJI.js → chunk-SUTEBYGI.js} +2 -2
  153. package/dist/{chunk-HVX4LPNY.js → chunk-T4KY6UYF.js} +5 -3
  154. package/dist/{chunk-HVX4LPNY.js.map → chunk-T4KY6UYF.js.map} +1 -1
  155. package/dist/{chunk-R6HMAIKM.js → chunk-TOSPNM4T.js} +2 -2
  156. package/dist/{chunk-RMXS4I66.js → chunk-TVDJEUV5.js} +2 -2
  157. package/dist/{chunk-PLCYKAWH.js → chunk-TYRWSE4B.js} +2 -2
  158. package/dist/{chunk-B4QYQFGY.js → chunk-UBEKET2V.js} +383 -36
  159. package/dist/chunk-UBEKET2V.js.map +1 -0
  160. package/dist/{chunk-IQWUACVG.js → chunk-UHMHJX5G.js} +3 -3
  161. package/dist/{chunk-YODUDA6R.js → chunk-UN4RVUG2.js} +22 -22
  162. package/dist/{chunk-E6Z4FBX6.js → chunk-V2QSNFCT.js} +2 -2
  163. package/dist/{chunk-NBH6K6G5.js → chunk-V65H7MI6.js} +345 -16
  164. package/dist/chunk-V65H7MI6.js.map +1 -0
  165. package/dist/chunk-W54Z2MOP.js +4500 -0
  166. package/dist/chunk-W54Z2MOP.js.map +1 -0
  167. package/dist/{chunk-7W3RXOIG.js → chunk-WGGRZYSQ.js} +14 -14
  168. package/dist/{chunk-RIZ5NUNN.js → chunk-WQ4XKIOX.js} +4 -4
  169. package/dist/{chunk-QEYTRFR3.js → chunk-X26YOFGS.js} +2 -2
  170. package/dist/{chunk-ITHZFHST.js → chunk-X2BVB74H.js} +2 -2
  171. package/dist/{chunk-6F42QONS.js → chunk-X5HGU2EM.js} +4 -4
  172. package/dist/{chunk-GVGAO3WV.js → chunk-XJ2S5UBF.js} +3 -3
  173. package/dist/{chunk-IBJ6U753.js → chunk-XKZBZOKV.js} +4 -4
  174. package/dist/{chunk-65AUYLU3.js → chunk-XPXHQI6N.js} +2 -2
  175. package/dist/chunk-XRWWCW4H.js +102 -0
  176. package/dist/chunk-XRWWCW4H.js.map +1 -0
  177. package/dist/{chunk-HH5LY6RR.js → chunk-YIXJSW5M.js} +5 -5
  178. package/dist/{chunk-2JSDPUKD.js → chunk-YMFPFXEX.js} +2 -2
  179. package/dist/{chunk-N4KAE2NB.js → chunk-Z3FTSHT5.js} +3 -3
  180. package/dist/{chunk-7GLJMDEQ.js → chunk-ZDV5YWID.js} +6 -6
  181. package/dist/{chunk-645WOU5I.js → chunk-ZDZJNXL6.js} +4 -4
  182. package/dist/{chunk-6TCXIRP5.js → chunk-ZKE4CO6B.js} +4 -4
  183. package/dist/cli.js +303 -185
  184. package/dist/coding/export-okf-codegraph.js +29 -29
  185. package/dist/compounding/engine.js +23 -24
  186. package/dist/config.js +36 -37
  187. package/dist/connectors/codex-materialize-runner.js +20 -20
  188. package/dist/connectors/index.js +21 -21
  189. package/dist/connectors/live/google-drive.d.ts +2 -0
  190. package/dist/connectors/live/google-drive.d.ts.map +1 -1
  191. package/dist/consolidation-provenance-check.js +4 -4
  192. package/dist/consolidation-undo.js +5 -5
  193. package/dist/contradiction/index.js +7 -6
  194. package/dist/{contradiction-scan-P2MHYFVG.js → contradiction-scan-IBPYGQZF.js} +4 -3
  195. package/dist/convergence-refresh.js +22 -22
  196. package/dist/corpus-watermark.js +23 -23
  197. package/dist/deep-recall-http-glue.js +2 -2
  198. package/dist/deep-recall-policy-llm.js +4 -4
  199. package/dist/direct-answer-wiring.js +2 -2
  200. package/dist/embedding-fallback.js +1 -1
  201. package/dist/entity-retrieval.js +21 -21
  202. package/dist/eval-trajectory-store.js +2 -2
  203. package/dist/eval-trajectory.js +2 -2
  204. package/dist/event-order-recall.js +2 -2
  205. package/dist/explicit-capture.d.ts +3 -2
  206. package/dist/explicit-capture.d.ts.map +1 -1
  207. package/dist/explicit-capture.js +530 -6
  208. package/dist/explicit-cue-recall.js +2 -2
  209. package/dist/external-wiki-access.js +2 -283
  210. package/dist/extraction-faithfulness.js +4 -4
  211. package/dist/extraction-judge.js +5 -5
  212. package/dist/extraction-normalization.js +1 -1
  213. package/dist/extraction-quote-grounding.d.ts.map +1 -1
  214. package/dist/extraction-quote-grounding.js +1 -1
  215. package/dist/extraction-source-grounding-rules.js +2 -2
  216. package/dist/extraction-source-grounding.js +3 -3
  217. package/dist/extraction.js +43 -44
  218. package/dist/fallback-llm-codex-subscription.js +2 -2
  219. package/dist/fallback-llm.js +3 -3
  220. package/dist/{first-start-migration-QFI7B5SH.js → first-start-migration-FILJH3RO.js} +6 -5
  221. package/dist/{first-start-migration-QFI7B5SH.js.map → first-start-migration-FILJH3RO.js.map} +1 -1
  222. package/dist/focused-list-recall.js +2 -2
  223. package/dist/harmonic-retrieval.js +20 -20
  224. package/dist/index.js +773 -794
  225. package/dist/lcm/index.js +2 -2
  226. package/dist/live-connectors-runner.js +2 -3
  227. package/dist/local-llm-config.js +2 -2
  228. package/dist/local-llm-helpers.d.ts +4 -0
  229. package/dist/local-llm-helpers.d.ts.map +1 -1
  230. package/dist/local-llm-helpers.js +3 -1
  231. package/dist/local-llm.d.ts +4 -1
  232. package/dist/local-llm.d.ts.map +1 -1
  233. package/dist/local-llm.js +4 -4
  234. package/dist/location/index.js +27 -29
  235. package/dist/maintenance/memory-governance.js +20 -20
  236. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +20 -20
  237. package/dist/maintenance/rebuild-memory-projection-runner.js +22 -22
  238. package/dist/maintenance/rebuild-memory-projection.js +21 -21
  239. package/dist/memory-browse-operations.js +2 -283
  240. package/dist/memory-browse.js +8 -7
  241. package/dist/memory-controller.js +2 -2
  242. package/dist/memory-subject.js +2 -2
  243. package/dist/namespaces/migrate.js +21 -21
  244. package/dist/namespaces/search.js +5 -5
  245. package/dist/namespaces/storage.js +20 -20
  246. package/dist/offline-sync-changeset-generations.js +1 -1
  247. package/dist/offline-sync-embedding-generation.js +1 -1
  248. package/dist/offline-sync-impression-drain.js +6 -6
  249. package/dist/offline-sync-snapshot-stream.js +1 -1
  250. package/dist/offline-sync.js +1 -1
  251. package/dist/operator-doctor-corpus.js +24 -24
  252. package/dist/operator-toolkit.js +58 -61
  253. package/dist/orchestration/backlog-scheduler.js +278 -294
  254. package/dist/orchestration/backlog-scheduler.js.map +1 -1
  255. package/dist/orchestration/buffer-drain-runner.js +278 -294
  256. package/dist/orchestration/extraction-run.d.ts +4 -0
  257. package/dist/orchestration/extraction-run.d.ts.map +1 -1
  258. package/dist/orchestration/fact-dedup-window.d.ts +1 -1
  259. package/dist/orchestration/fact-dedup-window.d.ts.map +1 -1
  260. package/dist/orchestration/maintenance.js +39 -42
  261. package/dist/orchestrator.d.ts +9 -11
  262. package/dist/orchestrator.d.ts.map +1 -1
  263. package/dist/orchestrator.js +290 -307
  264. package/dist/page-versioning.js +3 -3
  265. package/dist/qmd-daemon-session.js +2 -2
  266. package/dist/qmd.js +3 -3
  267. package/dist/recall-explain-renderer.js +1 -1
  268. package/dist/recall-navigation-operations.js +2 -283
  269. package/dist/recall-navigation.js +332 -59
  270. package/dist/recall-planner-llm.js +3 -3
  271. package/dist/recall-state-view-admission.js +6 -6
  272. package/dist/recall-why-service.js +10 -9
  273. package/dist/recall-why.js +4 -3
  274. package/dist/recall-xray-cli.js +2 -2
  275. package/dist/recall-xray-renderer.js +1 -1
  276. package/dist/reconcile/manifest.js +3 -3
  277. package/dist/{resolution-N7H3F2E4.js → resolution-OVVIF7ZY.js} +8 -7
  278. package/dist/response-guidance-recall.js +2 -2
  279. package/dist/resume-bundles.js +40 -41
  280. package/dist/salvage-envelope.js +4 -4
  281. package/dist/search/factory.js +4 -4
  282. package/dist/search/index.js +4 -4
  283. package/dist/semantic-consolidation.js +23 -23
  284. package/dist/semantic-rule-promotion.js +20 -20
  285. package/dist/semantic-rule-verifier.js +20 -20
  286. package/dist/shared-context/manager.js +2 -3
  287. package/dist/source-agent-qualifier.js +32 -33
  288. package/dist/standup/build.js +20 -20
  289. package/dist/storage/buffer-drain-claims.d.ts +97 -12
  290. package/dist/storage/buffer-drain-claims.d.ts.map +1 -1
  291. package/dist/storage/buffer-drain-claims.js +1 -1
  292. package/dist/storage/buffer-session-files.d.ts +4 -0
  293. package/dist/storage/buffer-session-files.d.ts.map +1 -1
  294. package/dist/storage/buffer-session-files.js +1 -1
  295. package/dist/storage/content-hash-index.d.ts +10 -0
  296. package/dist/storage/content-hash-index.d.ts.map +1 -1
  297. package/dist/storage.d.ts.map +1 -1
  298. package/dist/storage.js +19 -19
  299. package/dist/subject-config.js +3 -3
  300. package/dist/summarizer.d.ts +4 -1
  301. package/dist/summarizer.d.ts.map +1 -1
  302. package/dist/summarizer.js +6 -6
  303. package/dist/support-passport/index.js +53 -297
  304. package/dist/targeted-fact-recall.js +2 -2
  305. package/dist/tier-routing.js +3 -2
  306. package/dist/{tier-stats-FQGQSFET.js → tier-stats-FXEUJN73.js} +6 -5
  307. package/dist/{tier-stats-FQGQSFET.js.map → tier-stats-FXEUJN73.js.map} +1 -1
  308. package/dist/transcript.js +2 -2
  309. package/dist/transfer/capsule-import.js +4 -4
  310. package/dist/transfer/export-okf.js +20 -20
  311. package/dist/verified-recall.js +20 -20
  312. package/dist/wearables/memory-gen.d.ts +5 -1
  313. package/dist/wearables/memory-gen.d.ts.map +1 -1
  314. package/dist/wearables/service.d.ts.map +1 -1
  315. package/dist/who-knows-cli.js +21 -21
  316. package/dist/who-knows.js +20 -20
  317. package/dist/write-envelope.js +3 -3
  318. package/package.json +2 -2
  319. package/src/access-boundary.ts +5 -1
  320. package/src/access-category-alias.test.ts +5 -0
  321. package/src/access-mcp-frames.test.ts +60 -5
  322. package/src/access-mcp-frames.ts +72 -18
  323. package/src/access-mcp.test.ts +66 -2
  324. package/src/access-mcp.ts +19 -13
  325. package/src/access-service-coding-write.test.ts +7 -0
  326. package/src/activity/journal-extract-storage.test.ts +31 -0
  327. package/src/activity/journal-extract.test.ts +1 -0
  328. package/src/activity/journal-extract.ts +24 -3
  329. package/src/buffer-drain-handover.test.ts +5 -1
  330. package/src/connectors/live/google-drive.test.ts +39 -0
  331. package/src/connectors/live/google-drive.ts +41 -12
  332. package/src/explicit-capture.test.ts +109 -17
  333. package/src/explicit-capture.ts +90 -37
  334. package/src/extraction-quote-grounding.test.ts +20 -0
  335. package/src/extraction-quote-grounding.ts +21 -11
  336. package/src/fact-dedup-window.test.ts +37 -0
  337. package/src/local-llm-helpers.ts +12 -0
  338. package/src/local-llm-shutdown.test.ts +194 -0
  339. package/src/local-llm.ts +32 -30
  340. package/src/orchestration/extraction-run.ts +12 -3
  341. package/src/orchestration/fact-dedup-window.ts +5 -1
  342. package/src/orchestrator-connector-dedup.test.ts +9 -5
  343. package/src/orchestrator.ts +47 -50
  344. package/src/storage/buffer-drain-claims.test.ts +385 -1
  345. package/src/storage/buffer-drain-claims.ts +380 -97
  346. package/src/storage/buffer-session-files.ts +4 -0
  347. package/src/storage/content-hash-index.ts +27 -2
  348. package/src/storage-fact-hash-publish-failure.test.ts +87 -0
  349. package/src/storage.ts +5 -1
  350. package/src/summarizer-shutdown.test.ts +195 -0
  351. package/src/summarizer.ts +15 -2
  352. package/src/wearables/memory-gen.test.ts +20 -0
  353. package/src/wearables/memory-gen.ts +14 -7
  354. package/src/wearables/service.ts +14 -1
  355. package/src/wearables/storage-io.test.ts +25 -0
  356. package/dist/chunk-2FGLEF7C.js +0 -61
  357. package/dist/chunk-2FGLEF7C.js.map +0 -1
  358. package/dist/chunk-3RLK65ZK.js +0 -203
  359. package/dist/chunk-3RLK65ZK.js.map +0 -1
  360. package/dist/chunk-4WR2OD7K.js +0 -43
  361. package/dist/chunk-4WR2OD7K.js.map +0 -1
  362. package/dist/chunk-5KBIOI4G.js.map +0 -1
  363. package/dist/chunk-5M5M22GW.js +0 -346
  364. package/dist/chunk-5M5M22GW.js.map +0 -1
  365. package/dist/chunk-7HTUYFR3.js.map +0 -1
  366. package/dist/chunk-AP6NCP3V.js.map +0 -1
  367. package/dist/chunk-B4QYQFGY.js.map +0 -1
  368. package/dist/chunk-BJRTMNCB.js +0 -1919
  369. package/dist/chunk-BJRTMNCB.js.map +0 -1
  370. package/dist/chunk-BRLXOZLM.js.map +0 -1
  371. package/dist/chunk-BSACVS6R.js +0 -621
  372. package/dist/chunk-BSACVS6R.js.map +0 -1
  373. package/dist/chunk-DKAA2IHQ.js +0 -3644
  374. package/dist/chunk-DKAA2IHQ.js.map +0 -1
  375. package/dist/chunk-E4IRW2FG.js.map +0 -1
  376. package/dist/chunk-E6M225BJ.js.map +0 -1
  377. package/dist/chunk-EQ4U73UH.js.map +0 -1
  378. package/dist/chunk-ETSJD5IV.js +0 -361
  379. package/dist/chunk-ETSJD5IV.js.map +0 -1
  380. package/dist/chunk-F55YQP7N.js +0 -336
  381. package/dist/chunk-F55YQP7N.js.map +0 -1
  382. package/dist/chunk-FHGWKODN.js +0 -2300
  383. package/dist/chunk-FHGWKODN.js.map +0 -1
  384. package/dist/chunk-FLRP63GU.js +0 -32219
  385. package/dist/chunk-FLRP63GU.js.map +0 -1
  386. package/dist/chunk-FSO3G4JJ.js.map +0 -1
  387. package/dist/chunk-ID6JGIMD.js +0 -25
  388. package/dist/chunk-ID6JGIMD.js.map +0 -1
  389. package/dist/chunk-IGLJNHOX.js +0 -62
  390. package/dist/chunk-IGLJNHOX.js.map +0 -1
  391. package/dist/chunk-IPT2OY4B.js.map +0 -1
  392. package/dist/chunk-IUKYFTGW.js.map +0 -1
  393. package/dist/chunk-NBH6K6G5.js.map +0 -1
  394. package/dist/chunk-NT7F7WJQ.js.map +0 -1
  395. package/dist/chunk-NTRUKAAE.js +0 -223
  396. package/dist/chunk-NTRUKAAE.js.map +0 -1
  397. package/dist/chunk-OGXXGHW6.js +0 -223
  398. package/dist/chunk-OGXXGHW6.js.map +0 -1
  399. package/dist/chunk-PPCLZ4JU.js +0 -28
  400. package/dist/chunk-PPCLZ4JU.js.map +0 -1
  401. package/dist/chunk-QJPJXCS4.js.map +0 -1
  402. package/dist/chunk-R2LBF4HH.js +0 -729
  403. package/dist/chunk-R2LBF4HH.js.map +0 -1
  404. package/dist/chunk-RHQLIHMB.js.map +0 -1
  405. package/dist/chunk-RRTVGD3J.js +0 -792
  406. package/dist/chunk-RRTVGD3J.js.map +0 -1
  407. package/dist/chunk-SUSW74WU.js.map +0 -1
  408. package/dist/chunk-T6OZW2WR.js.map +0 -1
  409. package/dist/chunk-UDKWFDV2.js.map +0 -1
  410. package/dist/chunk-V7IZKJI3.js +0 -22
  411. package/dist/chunk-V7IZKJI3.js.map +0 -1
  412. package/dist/chunk-X562NQFA.js +0 -10144
  413. package/dist/chunk-X562NQFA.js.map +0 -1
  414. package/dist/chunk-XDSCJOH7.js +0 -3991
  415. package/dist/chunk-XDSCJOH7.js.map +0 -1
  416. package/dist/chunk-YCNIGRRF.js.map +0 -1
  417. package/dist/chunk-ZTTCDQ4I.js +0 -18796
  418. package/dist/chunk-ZTTCDQ4I.js.map +0 -1
  419. /package/dist/{capsule-merge-2IIXFXN5.js.map → capsule-merge-6IDAYTKG.js.map} +0 -0
  420. /package/dist/{chunk-RGUUBAT5.js.map → chunk-2WCLTWV2.js.map} +0 -0
  421. /package/dist/{chunk-BQZEGOZ4.js.map → chunk-2YODOG2O.js.map} +0 -0
  422. /package/dist/{chunk-WYG5UNDY.js.map → chunk-3AUDELZL.js.map} +0 -0
  423. /package/dist/{chunk-MB6ZJDN6.js.map → chunk-3FBAEPCU.js.map} +0 -0
  424. /package/dist/{chunk-PIITB2YZ.js.map → chunk-3ZS3NUEI.js.map} +0 -0
  425. /package/dist/{chunk-ATPWZ4F7.js.map → chunk-4EZWSDFL.js.map} +0 -0
  426. /package/dist/{chunk-ANZZLRUX.js.map → chunk-4IVRQMZW.js.map} +0 -0
  427. /package/dist/{chunk-63E37JCN.js.map → chunk-4RAJT5L2.js.map} +0 -0
  428. /package/dist/{chunk-45VUHDV2.js.map → chunk-5CO5G4MN.js.map} +0 -0
  429. /package/dist/{chunk-LUJCQQ3L.js.map → chunk-65HWAQ4F.js.map} +0 -0
  430. /package/dist/{chunk-T67LUYCR.js.map → chunk-77NTFICT.js.map} +0 -0
  431. /package/dist/{chunk-UE6RRXMF.js.map → chunk-A4F7OIC5.js.map} +0 -0
  432. /package/dist/{chunk-RRJBDXAQ.js.map → chunk-B377OKUP.js.map} +0 -0
  433. /package/dist/{chunk-Z7JIZ42P.js.map → chunk-BN2EBCLB.js.map} +0 -0
  434. /package/dist/{chunk-YYOMGOHB.js.map → chunk-BOK6BN5M.js.map} +0 -0
  435. /package/dist/{chunk-U6UNF5ZS.js.map → chunk-BPK32FRM.js.map} +0 -0
  436. /package/dist/{chunk-OH7QLI6W.js.map → chunk-BPPUH5JM.js.map} +0 -0
  437. /package/dist/{chunk-O6ZSWHVR.js.map → chunk-C2KE5DMD.js.map} +0 -0
  438. /package/dist/{chunk-47F6WWLQ.js.map → chunk-C73N66PR.js.map} +0 -0
  439. /package/dist/{chunk-WRT7GJ2D.js.map → chunk-CIWMTNHW.js.map} +0 -0
  440. /package/dist/{chunk-HF3BKUVV.js.map → chunk-CL4JU3DF.js.map} +0 -0
  441. /package/dist/{chunk-O55EKIEC.js.map → chunk-D3ZKHLN2.js.map} +0 -0
  442. /package/dist/{chunk-ETZLQOUR.js.map → chunk-D5XG2KPS.js.map} +0 -0
  443. /package/dist/{chunk-CD4BCCNP.js.map → chunk-DJ7WVEWX.js.map} +0 -0
  444. /package/dist/{chunk-PWXPRVLK.js.map → chunk-DNY63QWJ.js.map} +0 -0
  445. /package/dist/{chunk-4CJTQHEG.js.map → chunk-ESLOLCD4.js.map} +0 -0
  446. /package/dist/{chunk-VILPVT4S.js.map → chunk-EZ47HOJ6.js.map} +0 -0
  447. /package/dist/{chunk-EFYN5I7Z.js.map → chunk-F4QFU5IZ.js.map} +0 -0
  448. /package/dist/{chunk-XYKBWTWI.js.map → chunk-FZZMLGWQ.js.map} +0 -0
  449. /package/dist/{chunk-FNXQZGUS.js.map → chunk-GBJUTX44.js.map} +0 -0
  450. /package/dist/{chunk-YLXETSAH.js.map → chunk-GCZJ7XRI.js.map} +0 -0
  451. /package/dist/{chunk-JV4S6VDH.js.map → chunk-ILY5SSOM.js.map} +0 -0
  452. /package/dist/{chunk-EXHXJETE.js.map → chunk-IQ55QGY5.js.map} +0 -0
  453. /package/dist/{chunk-4J363OAA.js.map → chunk-J7BIFOYP.js.map} +0 -0
  454. /package/dist/{chunk-QFEDITXM.js.map → chunk-JNDMIAJ4.js.map} +0 -0
  455. /package/dist/{chunk-E5PPE4VA.js.map → chunk-K2VRKCPA.js.map} +0 -0
  456. /package/dist/{chunk-MKRYQROF.js.map → chunk-LGEHAQHK.js.map} +0 -0
  457. /package/dist/{chunk-XYTQJD7F.js.map → chunk-LMJVP5CM.js.map} +0 -0
  458. /package/dist/{chunk-E7YZXWIS.js.map → chunk-LNGELD2M.js.map} +0 -0
  459. /package/dist/{chunk-FQKBJJYB.js.map → chunk-LPV72KRQ.js.map} +0 -0
  460. /package/dist/{chunk-2LCRG2YV.js.map → chunk-M2XR6B6V.js.map} +0 -0
  461. /package/dist/{chunk-ZU5JDLOH.js.map → chunk-MMRTPH3J.js.map} +0 -0
  462. /package/dist/{chunk-TVIVY7KU.js.map → chunk-NED7IR7B.js.map} +0 -0
  463. /package/dist/{chunk-6F5MJFXY.js.map → chunk-NHVAX7MH.js.map} +0 -0
  464. /package/dist/{chunk-TG3R2SID.js.map → chunk-NMGHGTVR.js.map} +0 -0
  465. /package/dist/{chunk-NGHDW6UU.js.map → chunk-NUQEREIP.js.map} +0 -0
  466. /package/dist/{chunk-5IIQU4JZ.js.map → chunk-OB72KNMF.js.map} +0 -0
  467. /package/dist/{chunk-RIMK327Y.js.map → chunk-OUVYYKYU.js.map} +0 -0
  468. /package/dist/{chunk-RWGRSKN5.js.map → chunk-PT4DZDJA.js.map} +0 -0
  469. /package/dist/{chunk-7EPY3IQL.js.map → chunk-PZJQ46DH.js.map} +0 -0
  470. /package/dist/{chunk-MXHMNKWG.js.map → chunk-QDESY76F.js.map} +0 -0
  471. /package/dist/{chunk-E3A7ELSG.js.map → chunk-QHARXWTC.js.map} +0 -0
  472. /package/dist/{chunk-BLJT6JP2.js.map → chunk-QMFAMOT2.js.map} +0 -0
  473. /package/dist/{chunk-I6QGI65T.js.map → chunk-QRMG4HK4.js.map} +0 -0
  474. /package/dist/{chunk-ZU3NJNO7.js.map → chunk-QSF5SJAM.js.map} +0 -0
  475. /package/dist/{chunk-5IYOKDM4.js.map → chunk-R23JWJD3.js.map} +0 -0
  476. /package/dist/{chunk-P4WNBSTW.js.map → chunk-RNG7S3FY.js.map} +0 -0
  477. /package/dist/{chunk-ZGFLIK5H.js.map → chunk-SPAKBUJY.js.map} +0 -0
  478. /package/dist/{chunk-ER7XANP3.js.map → chunk-SQ3GEIEL.js.map} +0 -0
  479. /package/dist/{chunk-PMJFRDJI.js.map → chunk-SUTEBYGI.js.map} +0 -0
  480. /package/dist/{chunk-R6HMAIKM.js.map → chunk-TOSPNM4T.js.map} +0 -0
  481. /package/dist/{chunk-RMXS4I66.js.map → chunk-TVDJEUV5.js.map} +0 -0
  482. /package/dist/{chunk-PLCYKAWH.js.map → chunk-TYRWSE4B.js.map} +0 -0
  483. /package/dist/{chunk-IQWUACVG.js.map → chunk-UHMHJX5G.js.map} +0 -0
  484. /package/dist/{chunk-YODUDA6R.js.map → chunk-UN4RVUG2.js.map} +0 -0
  485. /package/dist/{chunk-E6Z4FBX6.js.map → chunk-V2QSNFCT.js.map} +0 -0
  486. /package/dist/{chunk-7W3RXOIG.js.map → chunk-WGGRZYSQ.js.map} +0 -0
  487. /package/dist/{chunk-RIZ5NUNN.js.map → chunk-WQ4XKIOX.js.map} +0 -0
  488. /package/dist/{chunk-QEYTRFR3.js.map → chunk-X26YOFGS.js.map} +0 -0
  489. /package/dist/{chunk-ITHZFHST.js.map → chunk-X2BVB74H.js.map} +0 -0
  490. /package/dist/{chunk-6F42QONS.js.map → chunk-X5HGU2EM.js.map} +0 -0
  491. /package/dist/{chunk-GVGAO3WV.js.map → chunk-XJ2S5UBF.js.map} +0 -0
  492. /package/dist/{chunk-IBJ6U753.js.map → chunk-XKZBZOKV.js.map} +0 -0
  493. /package/dist/{chunk-65AUYLU3.js.map → chunk-XPXHQI6N.js.map} +0 -0
  494. /package/dist/{chunk-HH5LY6RR.js.map → chunk-YIXJSW5M.js.map} +0 -0
  495. /package/dist/{chunk-2JSDPUKD.js.map → chunk-YMFPFXEX.js.map} +0 -0
  496. /package/dist/{chunk-N4KAE2NB.js.map → chunk-Z3FTSHT5.js.map} +0 -0
  497. /package/dist/{chunk-7GLJMDEQ.js.map → chunk-ZDV5YWID.js.map} +0 -0
  498. /package/dist/{chunk-645WOU5I.js.map → chunk-ZDZJNXL6.js.map} +0 -0
  499. /package/dist/{chunk-6TCXIRP5.js.map → chunk-ZKE4CO6B.js.map} +0 -0
  500. /package/dist/{contradiction-scan-P2MHYFVG.js.map → contradiction-scan-IBPYGQZF.js.map} +0 -0
  501. /package/dist/{resolution-N7H3F2E4.js.map → resolution-OVVIF7ZY.js.map} +0 -0
@@ -1,3644 +0,0 @@
1
- import {
2
- WearablesInputError
3
- } from "./chunk-V7IZKJI3.js";
4
- import {
5
- AdapterRegistry
6
- } from "./chunk-XTN54NML.js";
7
- import {
8
- WriteRateLimiter
9
- } from "./chunk-TB6CBZ5V.js";
10
- import {
11
- maybeRespondRecallNavigation
12
- } from "./chunk-WXDTOE6F.js";
13
- import {
14
- maybeRespondMemoryBrowse
15
- } from "./chunk-UYHDMCY2.js";
16
- import {
17
- serializeInlineScriptValue
18
- } from "./chunk-3HSRWJTC.js";
19
- import {
20
- respondDeepRecall
21
- } from "./chunk-XNI3URDR.js";
22
- import {
23
- handleSupportPassportOwnerHttp
24
- } from "./chunk-HVX4LPNY.js";
25
- import {
26
- buildSupportPassportPublicRequestHandler
27
- } from "./chunk-QJPJXCS4.js";
28
- import {
29
- handleMcpGetSse
30
- } from "./chunk-SRT6SHVZ.js";
31
- import {
32
- EngramMcpServer,
33
- MCP_SUPPORTED_PROTOCOL_VERSIONS,
34
- cleanupExpiredChatSessions,
35
- enforceChatSessionNamespace,
36
- loadChatSession,
37
- processChatMessage,
38
- sessionBelongsToPrincipal,
39
- subscribeChatTranscript
40
- } from "./chunk-XDSCJOH7.js";
41
- import {
42
- RelayMissionStoreError
43
- } from "./chunk-BJRTMNCB.js";
44
- import {
45
- executeResolution,
46
- isValidResolutionVerb
47
- } from "./chunk-IQWUACVG.js";
48
- import {
49
- handleRecallWhyHttpQuery,
50
- handleRecallXrayHttpQuery
51
- } from "./chunk-LUJCQQ3L.js";
52
- import {
53
- toDottedRemnicName
54
- } from "./chunk-ZQUVBZI4.js";
55
- import {
56
- maybeHandleLifecycleFlush
57
- } from "./chunk-2BPPGNAQ.js";
58
- import {
59
- respondOfflineManifestStream,
60
- respondOfflineSnapshotStream
61
- } from "./chunk-5R627MYY.js";
62
- import {
63
- nonEmptyQueryParam,
64
- optionalNamespaceKindQueryParam,
65
- optionalQueryString,
66
- positiveIntQueryParam
67
- } from "./chunk-4WR2OD7K.js";
68
- import {
69
- respondBufferAdmissionError
70
- } from "./chunk-I3V3JJS7.js";
71
- import {
72
- respondBriefingHttp
73
- } from "./chunk-ID6JGIMD.js";
74
- import {
75
- respondAccessCapabilitiesHttp
76
- } from "./chunk-DJSJ4ULL.js";
77
- import {
78
- authorizationProbeNamespaces,
79
- authorizationProbeRequiresPrincipalNamespace,
80
- probeOperationAuthorization
81
- } from "./chunk-RRJBDXAQ.js";
82
- import {
83
- getOperation,
84
- resolveQueryNamespaceWritablePreflight
85
- } from "./chunk-ZTTCDQ4I.js";
86
- import {
87
- handleWhoKnowsHttpQuery
88
- } from "./chunk-EXHXJETE.js";
89
- import {
90
- isTrustZoneName
91
- } from "./chunk-JWX2Q6HS.js";
92
- import {
93
- subscribeGraphEvents
94
- } from "./chunk-DEUNUKTD.js";
95
- import {
96
- BriefingTimeoutError
97
- } from "./chunk-FNFURZBZ.js";
98
- import {
99
- CorrectionContractError
100
- } from "./chunk-ETSJD5IV.js";
101
- import {
102
- peerManifestRevision
103
- } from "./chunk-2LCRG2YV.js";
104
- import {
105
- assertOperationAllowed,
106
- capabilityAllowsOp,
107
- enforceNamespaceAllowList,
108
- isCapabilityRestricted,
109
- tokenCapabilityStore
110
- } from "./chunk-OGBQMY46.js";
111
- import {
112
- isDefaultReviewNamespace,
113
- listPairs,
114
- readPair
115
- } from "./chunk-CTVWELNK.js";
116
- import {
117
- isRecallDisclosure
118
- } from "./chunk-43AA5FVD.js";
119
- import {
120
- resolveNamespaceCapabilities
121
- } from "./chunk-C5LWPYS3.js";
122
- import {
123
- validateRequest
124
- } from "./chunk-R2DOFBG4.js";
125
- import {
126
- OFFLINE_SYNC_APPLY_MAX_BODY_BYTES,
127
- OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES,
128
- OFFLINE_SYNC_SNAPSHOT_BASE_MAX_BODY_BYTES
129
- } from "./chunk-SLDRXK37.js";
130
- import {
131
- projectTagProjectId
132
- } from "./chunk-63E37JCN.js";
133
- import {
134
- expandTildePath
135
- } from "./chunk-6KH5NLQS.js";
136
- import {
137
- EngramAccessForbiddenError,
138
- EngramAccessInputError
139
- } from "./chunk-CJCTKL6Y.js";
140
- import {
141
- abortError,
142
- isAbortError
143
- } from "./chunk-R2MBW2S4.js";
144
- import {
145
- log
146
- } from "./chunk-3WCASNQZ.js";
147
-
148
- // src/access-http.ts
149
- import { createServer } from "http";
150
- import { randomUUID, timingSafeEqual } from "crypto";
151
- import { AsyncLocalStorage } from "async_hooks";
152
- import { existsSync } from "fs";
153
- import { readFile } from "fs/promises";
154
- import path2 from "path";
155
- import { fileURLToPath, URL as URL2 } from "url";
156
- import { gunzipSync } from "zlib";
157
-
158
- // src/wearables/http-glue.ts
159
- function respondWearablesErrorGlue(respondJson2, res, err) {
160
- if (err instanceof WearablesInputError) {
161
- respondJson2(res, 400, {
162
- error: "invalid_request",
163
- code: "invalid_request",
164
- message: err.message
165
- });
166
- return true;
167
- }
168
- return false;
169
- }
170
-
171
- // src/meetings/errors.ts
172
- var MeetingsInputError = class extends Error {
173
- constructor(message) {
174
- super(message);
175
- this.name = "MeetingsInputError";
176
- }
177
- };
178
-
179
- // src/meetings/http-glue.ts
180
- function respondMeetingsError(respondJson2, res, err) {
181
- if (err instanceof MeetingsInputError) {
182
- respondJson2(res, 400, { error: "invalid_request", code: "invalid_request", message: err.message });
183
- return true;
184
- }
185
- return false;
186
- }
187
- function decodeMeetingIdSegment(raw) {
188
- try {
189
- return decodeURIComponent(raw);
190
- } catch {
191
- throw new MeetingsInputError("meeting id path segment is not valid percent-encoded input");
192
- }
193
- }
194
- async function respondMeetingsList(res, respondJson2, service, date, scope) {
195
- try {
196
- respondJson2(res, 200, await service.meetingsList(date, scope));
197
- } catch (err) {
198
- if (respondMeetingsError(respondJson2, res, err)) return;
199
- throw err;
200
- }
201
- }
202
- async function respondMeetingsGet(res, respondJson2, service, rawId, scope) {
203
- try {
204
- respondJson2(res, 200, await service.meetingsGet(decodeMeetingIdSegment(rawId), scope));
205
- } catch (err) {
206
- if (respondMeetingsError(respondJson2, res, err)) return;
207
- throw err;
208
- }
209
- }
210
- async function respondMeetingsBuild(req, res, respondJson2, readJsonBody, service, quota, scopeFor) {
211
- const body = await readJsonBody(req);
212
- const raw = body.date;
213
- if (raw !== void 0 && raw !== null && raw !== "" && typeof raw !== "string") {
214
- throw new EngramAccessInputError(`date must be a string (got ${JSON.stringify(raw)})`);
215
- }
216
- const date = raw === void 0 || raw === null || raw === "" ? void 0 : raw;
217
- if (date === void 0) {
218
- throw new EngramAccessInputError("date is required (YYYY-MM-DD)");
219
- }
220
- quota.enforceQuota();
221
- const scope = scopeFor?.(
222
- typeof body.namespace === "string" ? body.namespace : void 0,
223
- typeof body.sessionKey === "string" ? body.sessionKey : void 0
224
- );
225
- try {
226
- const result = await service.meetingsBuild(date, scope);
227
- quota.recordHit();
228
- respondJson2(res, 200, result);
229
- } catch (err) {
230
- if (respondMeetingsError(respondJson2, res, err)) return;
231
- throw err;
232
- }
233
- }
234
-
235
- // src/location/http-glue.ts
236
- function requireLocationOperation(name) {
237
- const op = getOperation(name);
238
- if (!op) {
239
- throw new Error(`access-boundary: operation not registered: ${name}`);
240
- }
241
- return op;
242
- }
243
- async function respondLocationStatus(res, respondJson2, service) {
244
- const output = await requireLocationOperation("location_status").run({}, { service });
245
- respondJson2(res, 200, output.result);
246
- }
247
- async function respondLocationCheck(res, respondJson2, service) {
248
- const output = await requireLocationOperation("location_check").run({}, { service });
249
- respondJson2(res, 200, { results: output.result });
250
- }
251
- async function respondLocationSync(body, res, respondJson2, service) {
252
- const output = await requireLocationOperation("location_sync").run(
253
- { date: body.date ?? null, days: body.days ?? null },
254
- { service }
255
- );
256
- respondJson2(res, 200, output.result);
257
- }
258
- async function respondLocationBackfill(body, res, respondJson2, service) {
259
- const output = await requireLocationOperation("location_backfill").run(
260
- { from: body.from ?? null, to: body.to ?? null, dryRun: body.dryRun === true },
261
- { service }
262
- );
263
- respondJson2(res, 200, output.result);
264
- }
265
- async function respondLocationDay(date, res, respondJson2, service) {
266
- const output = await requireLocationOperation("location_day").run(
267
- { date: date ?? null },
268
- { service }
269
- );
270
- respondJson2(res, output.result.found ? 200 : 404, output.result);
271
- }
272
-
273
- // src/standup/http-glue.ts
274
- async function respondStandup(date, res, respondJson2, service) {
275
- const op = getOperation("standup");
276
- if (!op) throw new Error("access-boundary: operation not registered: standup");
277
- const output = await op.run({ ...date === null ? {} : { date } }, { service });
278
- respondJson2(res, 200, output.result);
279
- }
280
-
281
- // src/chat/chat-http.ts
282
- import { createHash } from "crypto";
283
- var chatInFlight = /* @__PURE__ */ new Map();
284
- function principalFingerprintComponent(principal) {
285
- return principal === void 0 ? "\0absent" : `${principal}`;
286
- }
287
- function chatDedupFingerprint(memoryDir, chatSessionId, principal, message) {
288
- const messageHash = createHash("sha256").update(message, "utf8").digest("hex").slice(0, 32);
289
- return `${memoryDir}:${chatSessionId}:${principalFingerprintComponent(principal)}:${messageHash}`;
290
- }
291
- function processChatMessageDedup(opts) {
292
- if (!opts.chatSessionId) {
293
- return processChatMessage(opts);
294
- }
295
- const fp = chatDedupFingerprint(opts.memoryDir, opts.chatSessionId, opts.principal, opts.message);
296
- const existing = chatInFlight.get(fp);
297
- if (existing) return existing;
298
- const pending = processChatMessage(opts).finally(() => {
299
- chatInFlight.delete(fp);
300
- });
301
- chatInFlight.set(fp, pending);
302
- return pending;
303
- }
304
- async function handleChatMessage(req, res, body, opts, principal) {
305
- if (!opts.config?.enabled) {
306
- respondJson(res, 404, { error: "chat_disabled", code: "chat_disabled" });
307
- return;
308
- }
309
- const message = typeof body.message === "string" ? body.message : "";
310
- if (!message) {
311
- respondJson(res, 400, { error: "message is required", code: "input_error" });
312
- return;
313
- }
314
- if (!opts.service.fallbackLlmRef && !opts.service.localLlmRef) {
315
- respondJson(res, 503, {
316
- error: "No LLM model is available. Configure a local or cloud model.",
317
- code: "no_llm"
318
- });
319
- return;
320
- }
321
- const chatSessionId = typeof body.chatSessionId === "string" ? body.chatSessionId : void 0;
322
- let result;
323
- try {
324
- result = await processChatMessageDedup({
325
- service: opts.service,
326
- config: opts.config,
327
- memoryDir: opts.memoryDir,
328
- message,
329
- ...chatSessionId ? { chatSessionId } : {},
330
- ...principal ? { principal } : {}
331
- });
332
- } catch (err) {
333
- const code = err instanceof Error ? err.message : String(err);
334
- if (code === "chat_session_not_found") {
335
- respondJson(res, 404, { error: "chat_session_not_found", code: "chat_session_not_found" });
336
- } else if (code === "access_denied") {
337
- respondJson(res, 403, { error: "access_denied", code: "access_denied" });
338
- } else if (code === "chat_session_expired") {
339
- respondJson(res, 410, { error: "chat_session_expired", code: "chat_session_expired" });
340
- } else if (code === "chat_disabled") {
341
- respondJson(res, 404, { error: "chat_disabled", code: "chat_disabled" });
342
- } else if (code === "no_llm_available" || code === "engine_unavailable") {
343
- respondJson(res, 503, {
344
- error: "No LLM model is available. Configure a local or cloud model.",
345
- code: "no_llm"
346
- });
347
- } else {
348
- respondJson(res, 500, { error: "internal_error", code: "internal_error" });
349
- }
350
- return;
351
- }
352
- const wireResult = {
353
- reply: result.reply,
354
- chatSessionId: result.chatSessionId,
355
- ...result.pendingPlan ? {
356
- pendingPlan: {
357
- planId: result.pendingPlan.planId,
358
- preview: result.pendingPlan.preview
359
- }
360
- } : {},
361
- ...result.skippedTools ? { skippedTools: result.skippedTools.filter((name) => typeof name === "string") } : {}
362
- };
363
- respondJson(res, 200, wireResult);
364
- }
365
- async function handleChatEventsSSE(req, res, chatSessionId, opts, principal) {
366
- if (!opts.config?.enabled) {
367
- respondJson(res, 404, { error: "chat_disabled", code: "chat_disabled" });
368
- return;
369
- }
370
- let heartbeat;
371
- let closed = false;
372
- const writeSse = (chunk) => {
373
- if (closed) return false;
374
- try {
375
- res.write(chunk);
376
- return true;
377
- } catch {
378
- return false;
379
- }
380
- };
381
- const pending = [];
382
- let bursting = true;
383
- const unsubscribe = subscribeChatTranscript(opts.memoryDir, chatSessionId, (entry) => {
384
- if (closed) return;
385
- if (bursting) {
386
- pending.push(entry);
387
- return;
388
- }
389
- writeSse(`data: ${JSON.stringify(entry)}
390
-
391
- `);
392
- });
393
- const doCleanup = () => {
394
- closed = true;
395
- clearInterval(heartbeat);
396
- unsubscribe();
397
- try {
398
- res.end();
399
- } catch {
400
- }
401
- };
402
- const unregisterServerCleanup = opts.registerSseCleanup?.(doCleanup) ?? (() => {
403
- });
404
- req.on("close", () => {
405
- doCleanup();
406
- unregisterServerCleanup();
407
- });
408
- const session = await loadChatSession(opts.memoryDir, chatSessionId);
409
- if (closed) return;
410
- if (!session) {
411
- unsubscribe();
412
- unregisterServerCleanup();
413
- respondJson(res, 404, { error: "chat_session_not_found", code: "chat_session_not_found" });
414
- return;
415
- }
416
- if (!sessionBelongsToPrincipal(session, principal)) {
417
- unsubscribe();
418
- unregisterServerCleanup();
419
- respondJson(res, 403, { error: "access_denied", code: "access_denied" });
420
- return;
421
- }
422
- res.writeHead(200, {
423
- "content-type": "text/event-stream",
424
- "cache-control": "no-cache",
425
- connection: "keep-alive"
426
- });
427
- writeSse("retry: 5000\n\n");
428
- const deliveredSeqs = /* @__PURE__ */ new Set();
429
- for (const entry of session.transcript) {
430
- writeSse(`data: ${JSON.stringify(entry)}
431
-
432
- `);
433
- deliveredSeqs.add(entry.seq);
434
- }
435
- bursting = false;
436
- for (const entry of pending) {
437
- if (!deliveredSeqs.has(entry.seq)) {
438
- writeSse(`data: ${JSON.stringify(entry)}
439
-
440
- `);
441
- }
442
- }
443
- if (closed) return;
444
- heartbeat = setInterval(() => {
445
- writeSse(`data: ${JSON.stringify({ type: "heartbeat" })}
446
-
447
- `);
448
- }, 25e3);
449
- heartbeat.unref?.();
450
- }
451
- function respondJson(res, status, body) {
452
- const json = JSON.stringify(body);
453
- res.writeHead(status, {
454
- "content-type": "application/json; charset=utf-8",
455
- "content-length": Buffer.byteLength(json, "utf8")
456
- });
457
- res.end(json);
458
- }
459
-
460
- // src/support-passport/access-http-base.ts
461
- import path from "path";
462
- var SUPPORT_PASSPORT_QUOTA_LIMITED_WRITE_TOOLS = /* @__PURE__ */ new Set([
463
- "engram.support_passport_draft_create",
464
- "remnic.support_passport_draft_create",
465
- "engram.support_passport_drafts_generate",
466
- "remnic.support_passport_drafts_generate",
467
- "engram.support_passport_card_replace",
468
- "remnic.support_passport_card_replace",
469
- "engram.support_passport_card_approve",
470
- "remnic.support_passport_card_approve",
471
- "engram.support_passport_card_reject",
472
- "remnic.support_passport_card_reject",
473
- "engram.support_passport_card_withdraw",
474
- "remnic.support_passport_card_withdraw",
475
- "engram.support_passport_grant_create",
476
- "remnic.support_passport_grant_create",
477
- "engram.support_passport_grant_revoke",
478
- "remnic.support_passport_grant_revoke"
479
- ]);
480
- var SUPPORT_PASSPORT_ADMIN_CONSOLE_ASSETS = /* @__PURE__ */ new Map([
481
- ["what-helps-me.css", "text/css; charset=utf-8"],
482
- ["model.js", "application/javascript; charset=utf-8"],
483
- ["app.js", "application/javascript; charset=utf-8"]
484
- ]);
485
- var SupportPassportAccessHttpBase = class {
486
- supportPassportPublicHandler;
487
- handleSupportPassportOwnerRequest(req, res, pathname, hasQuery, abortSignal) {
488
- return handleSupportPassportOwnerHttp({
489
- req,
490
- res,
491
- pathname,
492
- hasQuery,
493
- service: this.service,
494
- principal: this.resolveRequestPrincipal(req),
495
- abortSignal,
496
- readJsonBody: () => this.readJsonBody(req),
497
- respondJson: (status, payload) => {
498
- res.setHeader("cache-control", "private, no-store");
499
- res.setHeader("vary", "Authorization");
500
- this.respondJson(res, status, payload);
501
- },
502
- reserveWriteRateLimitSlot: () => this.reserveWriteRateLimitSlot(req)
503
- });
504
- }
505
- handleSupportPassportPublicRequest(req, res) {
506
- this.supportPassportPublicHandler ??= buildSupportPassportPublicRequestHandler(this.service, {
507
- trustedProxyAddresses: this.service.configRef?.supportPassport?.trustedProxyAddresses ?? []
508
- });
509
- return this.supportPassportPublicHandler(req, res, {
510
- authorized: false,
511
- tokenAuthorized: false
512
- });
513
- }
514
- async enforceSupportPassportAuthorizationProbe(req) {
515
- const principal = this.resolveRequestPrincipal(req)?.trim();
516
- if (!principal) {
517
- throw new EngramAccessForbiddenError("A trusted principal is required for support passport access.");
518
- }
519
- const owner = await this.service.getWritableStorageForNamespace(void 0, principal);
520
- enforceNamespaceAllowList(
521
- tokenCapabilityStore.getStore(),
522
- owner.namespace,
523
- this.service.configRef.defaultNamespace
524
- );
525
- }
526
- isSupportPassportQuotaLimitedWriteTool(toolName) {
527
- return SUPPORT_PASSPORT_QUOTA_LIMITED_WRITE_TOOLS.has(toolName);
528
- }
529
- async handleSupportPassportUi(req, res, pathname) {
530
- if (req.method !== "GET") return false;
531
- const isSupportPassportPath = pathname === "/remnic/ui/what-helps-me" || pathname === "/engram/ui/what-helps-me" || pathname.startsWith("/remnic/ui/what-helps-me/") || pathname.startsWith("/engram/ui/what-helps-me/");
532
- if (!isSupportPassportPath) return false;
533
- if (!this.service.supportPassportEnabled) {
534
- this.respondJson(res, 404, { error: "not_found" });
535
- return true;
536
- }
537
- if (pathname === "/remnic/ui/what-helps-me" || pathname === "/engram/ui/what-helps-me") {
538
- const search = new URL(req.url ?? pathname, "http://placeholder").search;
539
- res.statusCode = 301;
540
- res.setHeader("location", `${pathname}/${search}`);
541
- res.end();
542
- return true;
543
- }
544
- if (pathname === "/remnic/ui/what-helps-me/" || pathname === "/engram/ui/what-helps-me/") {
545
- const helperRequest = new URL(req.url ?? pathname, "http://placeholder").searchParams.has("grant");
546
- res.setHeader("content-security-policy", "frame-ancestors 'none'");
547
- res.setHeader("x-frame-options", "DENY");
548
- await this.respondAdminConsoleShell(req, res, pathname, "what-helps-me/index.html", !helperRequest);
549
- return true;
550
- }
551
- const fileName = pathname.split("/").at(-1) ?? "";
552
- const assetType = SUPPORT_PASSPORT_ADMIN_CONSOLE_ASSETS.get(fileName);
553
- if (assetType && pathname.split("/").length === 5) {
554
- await this.respondStatic(res, path.join(this.adminConsolePublicDir, "what-helps-me", fileName), assetType);
555
- return true;
556
- }
557
- this.respondJson(res, 404, { error: "not_found" });
558
- return true;
559
- }
560
- };
561
-
562
- // src/review/review-deck-http-base.ts
563
- var LIST_PATHS = ["/remnic/v1/review/deck", "/engram/v1/review/deck"];
564
- var ACTION_PATHS = ["/remnic/v1/review/deck/action", "/engram/v1/review/deck/action"];
565
- var UNDO_PATHS = ["/remnic/v1/review/deck/undo", "/engram/v1/review/deck/undo"];
566
- var LIMIT_MIN = 1;
567
- var LIMIT_MAX = 100;
568
- var CURSOR_RE = /^[^\s%]{1,2048}$/;
569
- function requiredString(body, field) {
570
- const value = body[field];
571
- if (typeof value !== "string" || value.trim().length === 0) {
572
- throw new EngramAccessInputError(`${field} is required`);
573
- }
574
- return value.trim();
575
- }
576
- function parseLimit(raw) {
577
- if (raw === null || raw.length === 0) {
578
- throw new EngramAccessInputError("limit is required and must be an integer");
579
- }
580
- if (!/^(?:0|[1-9]\d*)$/.test(raw)) {
581
- throw new EngramAccessInputError("limit must be an integer");
582
- }
583
- const value = Number(raw);
584
- if (!Number.isInteger(value) || value < LIMIT_MIN || value > LIMIT_MAX) {
585
- throw new EngramAccessInputError(`limit must be an integer from ${LIMIT_MIN} to ${LIMIT_MAX}`);
586
- }
587
- return value;
588
- }
589
- function parseCursor(raw) {
590
- if (raw === null) return void 0;
591
- if (!CURSOR_RE.test(raw)) {
592
- throw new EngramAccessInputError("cursor is malformed");
593
- }
594
- return raw;
595
- }
596
- function parseActionBody(body) {
597
- const action = body.action;
598
- if (action !== "keep" && action !== "not_true" && action !== "prepare_fix") {
599
- throw new EngramAccessInputError("action must be one of: keep, not_true, prepare_fix");
600
- }
601
- const itemId = requiredString(body, "itemId");
602
- const revision = requiredString(body, "revision");
603
- const idempotencyKey = requiredString(body, "idempotencyKey");
604
- if (action === "prepare_fix") {
605
- const correctionText = requiredString(body, "correctionText");
606
- return { schemaVersion: 1, itemId, revision, action, correctionText, idempotencyKey };
607
- }
608
- return { schemaVersion: 1, itemId, revision, action, idempotencyKey };
609
- }
610
- function parseUndoBody(body) {
611
- return {
612
- schemaVersion: 1,
613
- receiptId: requiredString(body, "receiptId"),
614
- expectedRevision: requiredString(body, "expectedRevision"),
615
- idempotencyKey: requiredString(body, "idempotencyKey")
616
- };
617
- }
618
- var ReviewDeckAccessHttpBase = class extends SupportPassportAccessHttpBase {
619
- async handleReviewDeckRequest(req, res, pathname, abortSignal) {
620
- const method = req.method;
621
- const isList = method === "GET" && (pathname === LIST_PATHS[0] || pathname === LIST_PATHS[1]);
622
- const isAction = method === "POST" && (pathname === ACTION_PATHS[0] || pathname === ACTION_PATHS[1]);
623
- const isUndo = method === "POST" && (pathname === UNDO_PATHS[0] || pathname === UNDO_PATHS[1]);
624
- if (!isList && !isAction && !isUndo) return false;
625
- if (!this.service.reviewDeckEnabled) {
626
- this.respondJson(res, 404, { error: "not_found", code: "not_found" });
627
- return true;
628
- }
629
- const parsed = new URL(req.url ?? "/", "http://127.0.0.1");
630
- const namespace = this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0);
631
- const principal = this.resolveRequestPrincipal(req);
632
- if (isList) {
633
- this.enforceTokenOp("review_deck_list");
634
- const page = await this.service.reviewDeckList({
635
- namespace,
636
- principal,
637
- cursor: parseCursor(parsed.searchParams.get("cursor")),
638
- limit: parseLimit(parsed.searchParams.get("limit"))
639
- });
640
- this.respondJson(res, 200, page);
641
- return true;
642
- }
643
- if (isAction) this.enforceTokenOp("review_deck_action");
644
- else this.enforceTokenOp("review_deck_undo");
645
- const writeQuota = this.reserveWriteRateLimitSlot(req);
646
- try {
647
- const body = await this.readJsonBody(req, this.maxBodyBytes);
648
- const result = isAction ? await this.service.reviewDeckAction(parseActionBody(body), {
649
- namespace,
650
- principal,
651
- signal: abortSignal
652
- }) : await this.service.reviewDeckUndo(parseUndoBody(body), {
653
- namespace,
654
- principal,
655
- signal: abortSignal
656
- });
657
- writeQuota.commit();
658
- this.respondJson(res, 200, result);
659
- } catch (error) {
660
- writeQuota.release();
661
- throw error;
662
- }
663
- return true;
664
- }
665
- };
666
-
667
- // src/access-http.ts
668
- function resolveDefaultAdminConsolePublicDir(sourceUrl = import.meta.url) {
669
- const thisDir = path2.dirname(fileURLToPath(sourceUrl));
670
- const candidates = [
671
- path2.resolve(thisDir, "../../../admin-console/public"),
672
- path2.resolve(thisDir, "../dist/admin-console/public"),
673
- path2.resolve(thisDir, "./admin-console/public"),
674
- path2.resolve(thisDir, "../../admin-console/public")
675
- ];
676
- return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
677
- }
678
- var defaultAdminConsolePublicDir = resolveDefaultAdminConsolePublicDir();
679
- var correlationIdStore = new AsyncLocalStorage();
680
- var RELAY_ADMIN_CONSOLE_ASSETS = /* @__PURE__ */ new Map([
681
- ["relay.css", "text/css; charset=utf-8"],
682
- ["relay-model.js", "application/javascript; charset=utf-8"],
683
- ["relay.js", "application/javascript; charset=utf-8"],
684
- ["replay.json", "application/json; charset=utf-8"]
685
- ]);
686
- var TRUST_ZONE_RECORD_KINDS = ["memory", "artifact", "state", "trajectory", "external"];
687
- var TRUST_ZONE_SOURCE_CLASSES = ["tool_output", "web_content", "subagent_trace", "system_memory", "user_input", "manual"];
688
- var HttpError = class extends Error {
689
- constructor(status, message, code, details) {
690
- super(message);
691
- this.status = status;
692
- this.code = code ?? `http_${status}`;
693
- this.details = details;
694
- }
695
- status;
696
- code;
697
- details;
698
- };
699
- function hostToUrlAuthority(host) {
700
- if (host.includes(":") && !host.startsWith("[") && !host.endsWith("]")) {
701
- return `[${host}]`;
702
- }
703
- return host;
704
- }
705
- function parseHttpServerPort(port) {
706
- if (port === void 0) return 0;
707
- if (!Number.isInteger(port) || port < 0 || port > 65535) {
708
- throw new Error("access HTTP port must be an integer from 0 to 65535");
709
- }
710
- return port;
711
- }
712
- function assertResourceMetadataUrl(value) {
713
- if (value === void 0) return void 0;
714
- let parsed;
715
- try {
716
- parsed = new URL2(value);
717
- } catch {
718
- throw new Error(
719
- `access HTTP resourceMetadataUrl must be an absolute http(s) URL, got: ${value}`
720
- );
721
- }
722
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
723
- throw new Error(
724
- `access HTTP resourceMetadataUrl must use http or https, got: ${parsed.protocol}`
725
- );
726
- }
727
- return value;
728
- }
729
- function parseTrustZoneKindFilter(raw) {
730
- if (raw === null) return void 0;
731
- if (TRUST_ZONE_RECORD_KINDS.includes(raw)) {
732
- return raw;
733
- }
734
- throw new HttpError(400, `kind must be one of ${TRUST_ZONE_RECORD_KINDS.join("|")}`, "invalid_kind_filter");
735
- }
736
- function parseTrustZoneSourceClassFilter(raw) {
737
- if (raw === null) return void 0;
738
- if (TRUST_ZONE_SOURCE_CLASSES.includes(raw)) {
739
- return raw;
740
- }
741
- throw new HttpError(400, `sourceClass must be one of ${TRUST_ZONE_SOURCE_CLASSES.join("|")}`, "invalid_source_class_filter");
742
- }
743
- function parseTrustZoneFilter(raw) {
744
- if (raw === null) return void 0;
745
- if (isTrustZoneName(raw)) {
746
- return raw;
747
- }
748
- throw new HttpError(400, "zone must be one of quarantine|working|trusted", "invalid_zone_filter");
749
- }
750
- function summarizeHttpRequest(req) {
751
- const method = req.method ?? "UNKNOWN";
752
- try {
753
- const parsed = new URL2(req.url ?? "/", "http://localhost");
754
- return `${method} ${parsed.pathname}`;
755
- } catch {
756
- return `${method} ${(req.url ?? "/").split("?")[0]}`;
757
- }
758
- }
759
- function parseStrictIntegerQuery(raw, field, defaultValue, minValue) {
760
- if (raw === null) return defaultValue;
761
- if (!/^(?:0|[1-9]\d*)$/.test(raw)) {
762
- throw new HttpError(400, `${field} must be an integer`, `invalid_${field}`);
763
- }
764
- const value = Number(raw);
765
- if (!Number.isInteger(value) || value < minValue) {
766
- throw new HttpError(400, `${field} must be an integer >= ${minValue}`, `invalid_${field}`);
767
- }
768
- return value;
769
- }
770
- function parseMemorySort(raw) {
771
- if (raw === null) return void 0;
772
- if (raw === "updated_desc" || raw === "updated_asc" || raw === "created_desc" || raw === "created_asc") {
773
- return raw;
774
- }
775
- throw new HttpError(400, "sort must be one of updated_desc|updated_asc|created_desc|created_asc", "invalid_sort");
776
- }
777
- function decodePeerIdSegment(raw) {
778
- try {
779
- return decodeURIComponent(raw);
780
- } catch {
781
- throw new EngramAccessInputError("peerId path segment is not valid percent-encoded input");
782
- }
783
- }
784
- function decodeRelayMissionIdSegment(raw) {
785
- try {
786
- return decodeURIComponent(raw);
787
- } catch {
788
- throw new EngramAccessInputError("missionId path segment is not valid percent-encoded input");
789
- }
790
- }
791
- function rejectBlankRelayNamespace(value) {
792
- if (value !== void 0 && value !== null && value.trim().length === 0) {
793
- throw new EngramAccessInputError("Relay namespace must not be blank when provided");
794
- }
795
- }
796
- function codingContextFromProjectTag(projectTag) {
797
- const projectId = projectTagProjectId(projectTag);
798
- return {
799
- projectId,
800
- branch: null,
801
- rootPath: projectId,
802
- defaultBranch: null
803
- };
804
- }
805
- var EngramAccessHttpServer = class extends ReviewDeckAccessHttpBase {
806
- service;
807
- host;
808
- requestedPort;
809
- authToken;
810
- authTokens;
811
- authTokensGetter;
812
- authTokenEntriesGetter;
813
- tokenPathPolicy;
814
- authenticatedPrincipal;
815
- maxBodyBytes;
816
- adminConsoleEnabled;
817
- adminConsolePublicDir;
818
- adminConsolePrefillToken;
819
- adminControls;
820
- trustPrincipalHeader;
821
- adapterRegistry;
822
- readiness;
823
- resourceMetadataUrl;
824
- externalRequestHandler;
825
- writeLimiter;
826
- // Reservations taken by ensureWriteRateLimitAvailable and not yet committed
827
- // by recordWriteRateLimitHit, keyed by request. The per-request release in
828
- // the connection callback covers every early return and error path, so a
829
- // failed or exempt write never leaks its slot (issue #3225 C4).
830
- pendingWriteQuotas = /* @__PURE__ */ new WeakMap();
831
- mcpServer;
832
- server = null;
833
- boundPort = 0;
834
- /** Active SSE response objects for /engram/v1/graph/events. */
835
- sseClients = /* @__PURE__ */ new Set();
836
- /** Throttle batch: pending SSE event batches per client. */
837
- sseBatchTimers = /* @__PURE__ */ new Map();
838
- ssePendingBatches = /* @__PURE__ */ new Map();
839
- /**
840
- * Per-client cleanup callbacks: clear heartbeat interval, flush timer,
841
- * unsubscribe from bus, and end the response. Stored here so `stop()`
842
- * can invoke them even when the client hasn't disconnected yet
843
- * (Cursor review thread `access-http.ts:232`).
844
- */
845
- sseCleanupFns = /* @__PURE__ */ new Set();
846
- /**
847
- * Periodic chat-session TTL sweep handle (issue #1685 item 1 /
848
- * #1687 Thread 21). unref'd so it never blocks process exit
849
- * (rule 47); cleared in `stop()`.
850
- */
851
- chatTtlTimer = null;
852
- constructor(options) {
853
- super();
854
- this.service = options.service;
855
- this.host = options.host?.trim() || "127.0.0.1";
856
- this.requestedPort = parseHttpServerPort(options.port);
857
- this.authToken = options.authToken?.trim() || void 0;
858
- this.authTokens = (options.authTokens ?? []).map((t) => t.trim()).filter(Boolean);
859
- this.authTokensGetter = options.authTokensGetter;
860
- this.authTokenEntriesGetter = options.authTokenEntriesGetter;
861
- this.tokenPathPolicy = options.tokenPathPolicy;
862
- this.authenticatedPrincipal = options.principal?.trim() || void 0;
863
- this.maxBodyBytes = Number.isFinite(options.maxBodyBytes) ? Math.max(1, Math.floor(options.maxBodyBytes ?? 131072)) : 131072;
864
- this.writeLimiter = new WriteRateLimiter(
865
- options.writeRateLimitMaxRequests,
866
- options.writeRateLimitWindowMs
867
- );
868
- this.adminConsoleEnabled = options.adminConsoleEnabled !== false;
869
- this.adminConsolePublicDir = options.adminConsolePublicDir ?? defaultAdminConsolePublicDir;
870
- this.adminConsolePrefillToken = options.adminConsolePrefillToken === true ? this.authToken : void 0;
871
- this.adminControls = options.adminControls;
872
- this.trustPrincipalHeader = options.trustPrincipalHeader === true;
873
- this.readiness = options.readiness ?? (() => ({ ready: true, warmupAttempts: 0 }));
874
- this.resourceMetadataUrl = assertResourceMetadataUrl(options.resourceMetadataUrl);
875
- this.externalRequestHandler = options.externalRequestHandler;
876
- this.adapterRegistry = options.enableAdapters !== false ? options.adapterRegistry ?? new AdapterRegistry() : null;
877
- this.mcpServer = new EngramMcpServer(this.service, {
878
- principal: options.principal,
879
- citationsEnabled: options.citationsEnabled,
880
- citationsAutoDetect: options.citationsAutoDetect,
881
- emitLegacyTools: options.emitLegacyTools,
882
- codingDecisionVisible: this.service.decisionRecordSurfaceVisible,
883
- architectureCardVisible: this.service.architectureCardSurfaceVisible,
884
- codegraphVisible: this.service.codegraphSurfaceVisible,
885
- sessionDeltaVisible: this.service.sessionDeltaSurfaceVisible,
886
- chatVisible: this.service.configRef?.chat?.enabled === true,
887
- correctionVisible: this.service.correctionSurfaceVisible
888
- });
889
- }
890
- async start() {
891
- if (!this.authToken && this.authTokens.length === 0 && !this.authTokensGetter && !this.authTokenEntriesGetter) {
892
- throw new Error("engram access HTTP requires authToken or authTokens");
893
- }
894
- if (this.server) return this.status();
895
- const server = createServer((req, res) => {
896
- const correlationId = randomUUID();
897
- const abortController = new AbortController();
898
- const abortDisconnectedRequest = () => {
899
- if (!res.writableFinished && !abortController.signal.aborted) {
900
- abortController.abort(abortError("HTTP client disconnected"));
901
- }
902
- };
903
- req.once("aborted", abortDisconnectedRequest);
904
- res.once("close", abortDisconnectedRequest);
905
- correlationIdStore.run(correlationId, () => {
906
- void this.handle(req, res, correlationId, abortController.signal).catch((err) => {
907
- if (err instanceof BriefingTimeoutError) {
908
- this.respondJson(res, 504, { error: err.message, code: "briefing_timeout" });
909
- return;
910
- }
911
- if (isAbortError(err)) {
912
- if (!res.destroyed && !res.writableEnded) {
913
- res.end();
914
- }
915
- return;
916
- }
917
- log.debug(`engram access HTTP request failed [${correlationId}]: ${err}`);
918
- if (err instanceof HttpError) {
919
- const payload = { error: err.message, code: err.code };
920
- if (err.details) payload.details = err.details;
921
- this.respondJson(res, err.status, payload);
922
- return;
923
- }
924
- if (err instanceof EngramAccessInputError) {
925
- this.respondJson(res, 400, { error: err.message, code: "input_error" });
926
- return;
927
- }
928
- if (err instanceof EngramAccessForbiddenError) {
929
- this.respondJson(res, 403, { error: err.message, code: "forbidden" });
930
- return;
931
- }
932
- if (err instanceof CorrectionContractError) {
933
- this.respondJson(res, 400, { error: err.message, code: "correction_contract_error" });
934
- return;
935
- }
936
- if (err instanceof RelayMissionStoreError) {
937
- const status = err.code === "idempotency_conflict" ? 409 : err.code === "limit_exceeded" ? 413 : err.code === "lock_unavailable" ? 503 : 500;
938
- this.respondJson(res, status, {
939
- error: status >= 500 ? "relay_backend_unavailable" : err.message,
940
- code: `relay_${err.code}`
941
- });
942
- return;
943
- }
944
- if (respondBufferAdmissionError(err, res, correlationId, (status, body) => this.respondJson(res, status, body))) {
945
- return;
946
- }
947
- if (res.headersSent) {
948
- res.destroy(err);
949
- return;
950
- }
951
- log.error(
952
- `engram access HTTP internal error [${correlationId}] ${summarizeHttpRequest(req)}`,
953
- err
954
- );
955
- this.respondJson(res, 500, { error: "internal_error", code: "internal_error" });
956
- }).finally(() => {
957
- req.off("aborted", abortDisconnectedRequest);
958
- res.off("close", abortDisconnectedRequest);
959
- this.releasePendingWriteQuota(req);
960
- });
961
- });
962
- });
963
- try {
964
- await new Promise((resolve, reject) => {
965
- const onError = (err) => {
966
- server.off("listening", onListening);
967
- reject(err);
968
- };
969
- const onListening = () => {
970
- server.off("error", onError);
971
- resolve();
972
- };
973
- server.once("error", onError);
974
- server.once("listening", onListening);
975
- server.listen(this.requestedPort, this.host);
976
- });
977
- } catch (err) {
978
- server.close();
979
- throw err;
980
- }
981
- this.server = server;
982
- const address = server.address();
983
- this.boundPort = typeof address === "object" && address ? address.port : this.requestedPort;
984
- this.scheduleChatSessionTtlSweep();
985
- return this.status();
986
- }
987
- async stop() {
988
- if (!this.server) return;
989
- if (this.chatTtlTimer) {
990
- clearInterval(this.chatTtlTimer);
991
- this.chatTtlTimer = null;
992
- }
993
- const server = this.server;
994
- this.server = null;
995
- this.boundPort = 0;
996
- for (const cleanup of this.sseCleanupFns) {
997
- try {
998
- cleanup();
999
- } catch {
1000
- }
1001
- }
1002
- this.sseCleanupFns.clear();
1003
- for (const [res, timer] of this.sseBatchTimers.entries()) {
1004
- clearTimeout(timer);
1005
- this.sseBatchTimers.delete(res);
1006
- }
1007
- this.ssePendingBatches.clear();
1008
- for (const res of this.sseClients) {
1009
- try {
1010
- res.end();
1011
- } catch {
1012
- }
1013
- }
1014
- this.sseClients.clear();
1015
- await new Promise((resolve, reject) => {
1016
- server.close((err) => err ? reject(err) : resolve());
1017
- });
1018
- }
1019
- /**
1020
- * Schedule the chat-session TTL sweep: one immediate pass plus a
1021
- * recurring hourly pass (issue #1685 item 1 / #1687 Thread 21). Both
1022
- * are skipped when chat is disabled. The recurring timer is unref'd
1023
- * so it never keeps the event loop alive on its own (rule 47). The
1024
- * sweep itself is best-effort — cleanup failures are swallowed inside
1025
- * `cleanupExpiredChatSessions`.
1026
- */
1027
- scheduleChatSessionTtlSweep() {
1028
- if (this.chatTtlTimer) {
1029
- clearInterval(this.chatTtlTimer);
1030
- this.chatTtlTimer = null;
1031
- }
1032
- const chat = this.service.configRef?.chat;
1033
- if (!chat?.enabled) return;
1034
- const ttlHours = typeof chat.sessionTtlHours === "number" && chat.sessionTtlHours > 0 ? chat.sessionTtlHours : 72;
1035
- void cleanupExpiredChatSessions(this.service.memoryDir, ttlHours).catch(() => {
1036
- });
1037
- const SWEEP_INTERVAL_MS = 60 * 60 * 1e3;
1038
- this.chatTtlTimer = setInterval(() => {
1039
- void cleanupExpiredChatSessions(this.service.memoryDir, ttlHours).catch(() => {
1040
- });
1041
- }, SWEEP_INTERVAL_MS);
1042
- this.chatTtlTimer.unref?.();
1043
- }
1044
- status() {
1045
- return {
1046
- running: this.server !== null,
1047
- host: this.host,
1048
- port: this.boundPort,
1049
- maxBodyBytes: this.maxBodyBytes
1050
- };
1051
- }
1052
- /**
1053
- * Resolve the adapter identity for the incoming request.
1054
- * Includes MCP clientInfo from the last initialize handshake if available.
1055
- * Returns null if no adapter matches or adapters are disabled.
1056
- */
1057
- resolveAdapterIdentity(req) {
1058
- if (!this.adapterRegistry) return null;
1059
- const sessionId = (() => {
1060
- const raw = req.headers["mcp-session-id"];
1061
- return typeof raw === "string" ? raw.trim() : void 0;
1062
- })();
1063
- return this.adapterRegistry.resolve({
1064
- headers: req.headers,
1065
- clientInfo: this.mcpServer.getClientInfo(sessionId)
1066
- });
1067
- }
1068
- /** Cache for per-request identity resolution (avoids double adapter resolution) */
1069
- identityCache = /* @__PURE__ */ new WeakMap();
1070
- /** Resolve principal, namespace, and session key from request headers and adapter identity */
1071
- resolveRequestIdentity(req) {
1072
- const cached = this.identityCache.get(req);
1073
- if (cached) return cached;
1074
- let principal;
1075
- let namespace;
1076
- let sessionKey;
1077
- if (this.trustPrincipalHeader) {
1078
- const headerVal = req.headers["x-engram-principal"];
1079
- const raw = Array.isArray(headerVal) ? headerVal[0] : headerVal;
1080
- if (typeof raw === "string") {
1081
- const trimmed = raw.trim();
1082
- if (trimmed.length > 0) {
1083
- principal = trimmed;
1084
- }
1085
- }
1086
- }
1087
- if (!principal) {
1088
- principal = this.authenticatedPrincipal;
1089
- }
1090
- const adapterIdentity = this.resolveAdapterIdentity(req);
1091
- if (adapterIdentity) {
1092
- if (!principal) {
1093
- principal = adapterIdentity.principal;
1094
- }
1095
- namespace = adapterIdentity.namespace;
1096
- const config = this.service.configRef;
1097
- if (adapterIdentity.namespaceExplicit === false && !(config && resolveNamespaceCapabilities(config).namespaces)) {
1098
- namespace = void 0;
1099
- }
1100
- sessionKey = adapterIdentity.sessionKey;
1101
- }
1102
- const result = { principal, namespace, sessionKey };
1103
- this.identityCache.set(req, result);
1104
- return result;
1105
- }
1106
- resolveRequestPrincipal(req) {
1107
- return this.resolveRequestIdentity(req).principal;
1108
- }
1109
- /** Caller scope for wearables/meetings ops (issue #2123): request principal +
1110
- * gated namespace + optional sessionKey, so reads/writes resolve to the caller
1111
- * namespace instead of the machine default. */
1112
- wearablesScope(req, namespace, sessionKey) {
1113
- return { authenticatedPrincipal: this.resolveRequestPrincipal(req), namespace: this.resolveNamespace(req, namespace), ...sessionKey ? { sessionKey } : {} };
1114
- }
1115
- /** Resolve namespace: only use the explicit body value. Adapter-inferred namespace
1116
- * is intentionally NOT used as a fallback for REST requests — omitting namespace
1117
- * should default to the server's global namespace, not silently scope to an adapter. */
1118
- resolveNamespace(_req, bodyNamespace) {
1119
- const namespace = bodyNamespace || void 0;
1120
- enforceNamespaceAllowList(tokenCapabilityStore.getStore(), namespace, this.service.configRef?.defaultNamespace);
1121
- return namespace;
1122
- }
1123
- /**
1124
- * Resolve + enforce the `namespace` field carried in a POST request body,
1125
- * returning a shallow-copied envelope with the gated namespace stamped on.
1126
- * Used by the coding/correction POST routes that pass `body` straight to
1127
- * `op.run`: the body `namespace` is a user-controlled field that MUST flow
1128
- * through the same effective-namespace allow-list gate as the query-param
1129
- * routes, otherwise a scoped bearer can scope its call to another tenant
1130
- * by setting `body.namespace` (issue #1850 finding 2). Throws 403 for a
1131
- * scoped token whose allow-list does not cover the effective namespace.
1132
- *
1133
- * A `namespace` that is neither a string nor `null` is REJECTED here rather
1134
- * than coerced to `undefined`: silently reinterpreting it would default the
1135
- * request to the principal's namespace set and answer 200, hiding the
1136
- * caller's mistake behind a plausible result (AGENTS.md pattern 39). `null`
1137
- * and an absent field keep their documented "no explicit namespace" meaning.
1138
- */
1139
- gatedBodyNamespace(req, body) {
1140
- const requested = body.namespace;
1141
- if (requested !== void 0 && requested !== null && typeof requested !== "string") {
1142
- throw new EngramAccessInputError(
1143
- `namespace must be a string or null (got: ${typeof requested})`
1144
- );
1145
- }
1146
- const trimmed = typeof requested === "string" ? requested.trim() : void 0;
1147
- const namespace = this.resolveNamespace(req, trimmed || void 0);
1148
- return { ...body, namespace };
1149
- }
1150
- /**
1151
- * Resolve the recall disclosure depth from the request (issue #677 PR
1152
- * 2/4). Explicit body value wins; otherwise we accept a
1153
- * `?disclosure=...` query parameter so curl/browser tooling can use the
1154
- * three-tier surface without rewriting JSON. Invalid query values
1155
- * throw `EngramAccessInputError` (CLAUDE.md rule 51 — no silent
1156
- * fallback). An absent body field AND an absent query param yields
1157
- * `undefined`, which the service maps to `DEFAULT_RECALL_DISCLOSURE`.
1158
- */
1159
- resolveRecallDisclosure(bodyDisclosure, parsed) {
1160
- if (bodyDisclosure !== void 0) {
1161
- return bodyDisclosure;
1162
- }
1163
- const queryDisclosure = parsed.searchParams.get("disclosure");
1164
- if (queryDisclosure === null) {
1165
- return void 0;
1166
- }
1167
- if (!isRecallDisclosure(queryDisclosure)) {
1168
- throw new EngramAccessInputError(
1169
- `disclosure must be one of: chunk, section, raw (got: ${queryDisclosure})`
1170
- );
1171
- }
1172
- return queryDisclosure;
1173
- }
1174
- async handle(req, res, correlationId, abortSignal) {
1175
- const parsed = new URL2(req.url ?? "/", `http://${hostToUrlAuthority(this.host)}`);
1176
- const pathname = parsed.pathname;
1177
- if (await this.handleSupportPassportUi(req, res, pathname)) return;
1178
- if (this.adminConsoleEnabled && await this.handleAdminConsole(req, res, pathname)) return;
1179
- if (req.method === "GET" && (pathname === "/engram/v1/live" || pathname === "/engram/v1/health")) {
1180
- const { ready, warmupAttempts, lastError } = this.readiness();
1181
- if (!ready) {
1182
- this.respondJson(res, 503, {
1183
- ok: false,
1184
- ready: false,
1185
- warmupAttempts,
1186
- lastError: lastError ?? null,
1187
- code: "not_ready"
1188
- });
1189
- return;
1190
- }
1191
- }
1192
- if (await this.handleSupportPassportPublicRequest(req, res)) return;
1193
- if (this.externalRequestHandler) {
1194
- const tokenAuthorized = this.isAuthorized(req, pathname);
1195
- const capabilities = this.resolveTokenCapabilities(req, pathname);
1196
- const authorized = tokenAuthorized && !isCapabilityRestricted(capabilities);
1197
- if (await this.externalRequestHandler(req, res, {
1198
- authorized,
1199
- tokenAuthorized
1200
- })) return;
1201
- }
1202
- if (!this.isAuthorized(req, pathname)) {
1203
- const body = JSON.stringify({ error: "unauthorized", code: "unauthorized" });
1204
- res.writeHead(401, {
1205
- "content-type": "application/json; charset=utf-8",
1206
- "www-authenticate": this.bearerChallenge(),
1207
- "x-request-id": correlationId
1208
- });
1209
- res.end(body);
1210
- return;
1211
- }
1212
- const caps = this.resolveTokenCapabilities(req, pathname);
1213
- await tokenCapabilityStore.run(caps, async () => {
1214
- await this.dispatchAuthorizedRequest(req, res, parsed, pathname, correlationId, abortSignal);
1215
- });
1216
- }
1217
- async dispatchAuthorizedRequest(req, res, parsed, pathname, correlationId, abortSignal) {
1218
- if (pathname === "/mcp" && req.method === "DELETE") {
1219
- res.writeHead(204);
1220
- res.end();
1221
- return;
1222
- }
1223
- if (pathname === "/mcp" && req.method === "GET") {
1224
- if ((req.headers.accept ?? "").includes("text/event-stream"))
1225
- return handleMcpGetSse(req, res, this.sseCleanupFns);
1226
- const body = JSON.stringify({ error: "method_not_allowed", code: "method_not_allowed" });
1227
- res.writeHead(405, { "content-type": "application/json; charset=utf-8", allow: "GET, POST, DELETE", "x-request-id": correlationId });
1228
- res.end(body);
1229
- return;
1230
- }
1231
- if (req.method === "POST" && pathname === "/mcp") return this.handleMcpRequest(req, res, abortSignal);
1232
- if (await this.handleSupportPassportOwnerRequest(req, res, pathname, parsed.search.length > 0, abortSignal)) return;
1233
- if (await this.handleReviewDeckRequest(req, res, pathname, abortSignal)) return;
1234
- if (req.method === "GET" && pathname === "/engram/v1/live") return this.respondJson(res, 200, { ok: true, ready: true });
1235
- if (req.method === "GET" && pathname === "/engram/v1/health") {
1236
- const { degraded, warmupAttempts, lastError } = this.readiness();
1237
- const health = await this.service.health();
1238
- this.respondJson(res, 200, degraded !== true ? health : {
1239
- ...health,
1240
- degraded: true,
1241
- warmupAttempts,
1242
- lastError: lastError ?? null
1243
- });
1244
- return;
1245
- }
1246
- if (req.method === "GET" && pathname === "/engram/v1/capabilities") return respondAccessCapabilitiesHttp(res);
1247
- if (req.method === "GET" && pathname === "/engram/v1/authorization") {
1248
- res.setHeader("cache-control", "no-store");
1249
- const probe = probeOperationAuthorization(tokenCapabilityStore.getStore(), parsed.searchParams.getAll("op"));
1250
- for (const namespace of authorizationProbeNamespaces(probe.operations, parsed.searchParams.get("namespace") ?? void 0))
1251
- this.resolveNamespace(req, namespace);
1252
- if (authorizationProbeRequiresPrincipalNamespace(probe.operations))
1253
- await this.enforceSupportPassportAuthorizationProbe(req);
1254
- this.respondJson(res, 200, probe);
1255
- return;
1256
- }
1257
- if (req.method === "GET" && pathname === "/engram/v1/adapters") {
1258
- this.enforceTokenOp("adapters_status");
1259
- const identity = this.resolveAdapterIdentity(req);
1260
- this.respondJson(res, 200, {
1261
- adaptersEnabled: this.adapterRegistry !== null,
1262
- registered: this.adapterRegistry?.list() ?? [],
1263
- resolved: identity
1264
- });
1265
- return;
1266
- }
1267
- if (req.method === "GET" && (pathname === "/engram/v1/admin/dashboard" || pathname === "/remnic/v1/admin/dashboard")) {
1268
- this.requireOperatorToken();
1269
- if (!this.adminControls) {
1270
- this.respondJson(res, 404, { error: "admin_controls_unavailable", code: "admin_controls_unavailable" });
1271
- return;
1272
- }
1273
- this.respondJson(res, 200, await this.adminControls.status());
1274
- return;
1275
- }
1276
- if (req.method === "PATCH" && (pathname === "/engram/v1/admin/config" || pathname === "/remnic/v1/admin/config")) {
1277
- this.requireOperatorToken();
1278
- if (!this.adminControls?.update) {
1279
- this.respondJson(res, 404, { error: "admin_controls_unavailable", code: "admin_controls_unavailable" });
1280
- return;
1281
- }
1282
- try {
1283
- this.respondJson(res, 200, await this.adminControls.update(await this.readJsonBody(req)));
1284
- } catch (error) {
1285
- throw new HttpError(
1286
- 400,
1287
- error instanceof Error ? error.message : "invalid_admin_config_patch",
1288
- "invalid_admin_config_patch"
1289
- );
1290
- }
1291
- return;
1292
- }
1293
- if (req.method === "POST" && (pathname === "/engram/v1/external-wikis/search" || pathname === "/remnic/v1/external-wikis/search")) {
1294
- this.enforceTokenOp("external_wiki_search");
1295
- const operation = getOperation("external_wiki_search");
1296
- if (!operation) throw new EngramAccessInputError("external_wiki_search operation is not registered");
1297
- const output = await operation.run(await this.readJsonBody(req), { service: this.service });
1298
- if (!output || typeof output !== "object" || !("result" in output)) {
1299
- throw new Error("external_wiki_search returned an invalid operation result");
1300
- }
1301
- this.respondJson(res, 200, output.result);
1302
- return;
1303
- }
1304
- if (req.method === "POST" && (pathname === "/engram/v1/memories/search" || pathname === "/remnic/v1/memories/search")) {
1305
- this.enforceTokenOp("memory_search");
1306
- const operation = getOperation("memory_search");
1307
- if (!operation) throw new EngramAccessInputError("access-boundary: operation not registered: memory_search");
1308
- const body = this.gatedBodyNamespace(req, await this.readJsonBody(req));
1309
- if (body.namespace === void 0 && tokenCapabilityStore.getStore()?.namespaces !== void 0) {
1310
- body.namespace = this.service.configRef?.defaultNamespace ?? "";
1311
- }
1312
- const output = await operation.run(body, {
1313
- service: this.service,
1314
- authenticatedPrincipal: this.resolveRequestPrincipal(req)
1315
- });
1316
- this.respondJson(res, 200, output.result);
1317
- return;
1318
- }
1319
- if (req.method === "POST" && pathname === "/engram/v1/recall") {
1320
- this.enforceTokenOp("recall");
1321
- const body = await this.readValidatedBody(req, "recall");
1322
- const disclosure = this.resolveRecallDisclosure(body.disclosure, parsed);
1323
- const asOfQueryRaw = parsed.searchParams.get("as_of");
1324
- const bodyHasAsOf = typeof body.asOf === "string" && body.asOf.length > 0;
1325
- if (!bodyHasAsOf && asOfQueryRaw !== null && asOfQueryRaw.length === 0) {
1326
- throw new EngramAccessInputError(
1327
- "as_of must be a non-empty timestamp (got empty value)"
1328
- );
1329
- }
1330
- const asOf = body.asOf ?? (asOfQueryRaw !== null && asOfQueryRaw.length > 0 ? asOfQueryRaw : void 0);
1331
- const bodyHasTagsField = body !== null && typeof body === "object" && "tags" in body;
1332
- const bodyTagsValue = bodyHasTagsField ? body.tags : void 0;
1333
- const bodyTags = Array.isArray(bodyTagsValue) ? bodyTagsValue : void 0;
1334
- const queryTags = parsed.searchParams.getAll("tag");
1335
- const tags = bodyHasTagsField ? bodyTags : queryTags.length > 0 ? queryTags : void 0;
1336
- const bodyTagMatch = body.tagMatch;
1337
- let tagMatch;
1338
- if (bodyTagMatch !== void 0) {
1339
- if (bodyTagMatch === "any" || bodyTagMatch === "all") {
1340
- tagMatch = bodyTagMatch;
1341
- }
1342
- } else {
1343
- const queryTagMatch = parsed.searchParams.get("tag_match");
1344
- if (queryTagMatch !== null) {
1345
- if (queryTagMatch !== "any" && queryTagMatch !== "all") {
1346
- throw new EngramAccessInputError(
1347
- `tag_match must be one of: any, all (got: ${queryTagMatch})`
1348
- );
1349
- }
1350
- tagMatch = queryTagMatch;
1351
- }
1352
- }
1353
- const bodyIncludeLowConfidence = body.includeLowConfidence;
1354
- const queryIncludeLowConfidence = parsed.searchParams.get("include_low_confidence");
1355
- if (bodyIncludeLowConfidence === void 0 && queryIncludeLowConfidence !== null && queryIncludeLowConfidence !== "true" && queryIncludeLowConfidence !== "false") {
1356
- throw new EngramAccessInputError(
1357
- `include_low_confidence must be one of: true, false (got: ${queryIncludeLowConfidence})`
1358
- );
1359
- }
1360
- const includeLowConfidence = bodyIncludeLowConfidence === true || bodyIncludeLowConfidence === void 0 && queryIncludeLowConfidence === "true";
1361
- const op = getOperation("recall");
1362
- if (!op) {
1363
- throw new EngramAccessInputError("access-boundary: operation not registered: recall");
1364
- }
1365
- const output = await op.run(
1366
- {
1367
- ...body,
1368
- namespace: this.resolveNamespace(req, body.namespace),
1369
- ...disclosure !== void 0 ? { disclosure } : {},
1370
- ...asOf !== void 0 ? { asOf } : {},
1371
- ...tags !== void 0 ? { tags } : {},
1372
- ...tagMatch !== void 0 ? { tagMatch } : {},
1373
- ...includeLowConfidence ? { includeLowConfidence: true } : {}
1374
- },
1375
- {
1376
- service: this.service,
1377
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
1378
- sourceConnector: this.resolveConnector(req),
1379
- ...abortSignal ? { abortSignal } : {}
1380
- }
1381
- );
1382
- this.respondJson(res, 200, output.result);
1383
- return;
1384
- }
1385
- if (req.method === "POST" && pathname === "/engram/v1/coding-context") {
1386
- this.enforceTokenOp("set_coding_context");
1387
- const body = await this.readValidatedBody(req, "setCodingContext");
1388
- const codingContext = body.codingContext !== void 0 ? body.codingContext : typeof body.projectTag === "string" ? codingContextFromProjectTag(body.projectTag) : (() => {
1389
- throw new EngramAccessInputError("codingContext or projectTag is required");
1390
- })();
1391
- this.service.setCodingContext({
1392
- sessionKey: body.sessionKey,
1393
- codingContext
1394
- });
1395
- this.respondJson(res, 200, { ok: true });
1396
- return;
1397
- }
1398
- if (req.method === "POST" && (pathname === "/engram/v1/capsules/export" || pathname === "/remnic/v1/capsules/export")) {
1399
- this.enforceTokenOp("capsule_export");
1400
- const body = await this.readValidatedBody(req, "capsuleExport");
1401
- this.ensureWriteRateLimitAvailable(req);
1402
- const result = await this.service.capsuleExport({
1403
- name: body.name,
1404
- namespace: this.resolveNamespace(req, body.namespace),
1405
- principal: this.resolveRequestPrincipal(req),
1406
- since: body.since,
1407
- includeKinds: body.includeKinds,
1408
- peerIds: body.peerIds,
1409
- includeTranscripts: body.includeTranscripts,
1410
- encrypt: body.encrypt
1411
- });
1412
- this.recordWriteRateLimitHit(req);
1413
- this.respondJson(res, 200, result);
1414
- return;
1415
- }
1416
- if (req.method === "POST" && (pathname === "/engram/v1/capsules/import" || pathname === "/remnic/v1/capsules/import")) {
1417
- this.enforceTokenOp("capsule_import");
1418
- const body = await this.readValidatedBody(req, "capsuleImport");
1419
- this.ensureWriteRateLimitAvailable(req);
1420
- const result = await this.service.capsuleImport({
1421
- archivePath: expandTildePath(body.archivePath),
1422
- namespace: this.resolveNamespace(req, body.namespace),
1423
- principal: this.resolveRequestPrincipal(req),
1424
- mode: body.mode,
1425
- passphrase: body.passphrase
1426
- });
1427
- this.recordWriteRateLimitHit(req);
1428
- this.respondJson(res, 200, result);
1429
- return;
1430
- }
1431
- if (req.method === "GET" && (pathname === "/engram/v1/offline-sync/capabilities" || pathname === "/remnic/v1/offline-sync/capabilities")) {
1432
- this.enforceTokenOp("offline_sync_snapshot");
1433
- this.respondJson(res, 200, {
1434
- version: 1,
1435
- convergenceFinalization: true,
1436
- manifestStream: true,
1437
- manifestRevision: peerManifestRevision(this.service.configRef?.inlineSourceAttributionFormat)
1438
- });
1439
- return;
1440
- }
1441
- if (req.method === "GET" && (pathname === "/engram/v1/offline-sync/snapshot" || pathname === "/remnic/v1/offline-sync/snapshot")) {
1442
- this.enforceTokenOp("offline_sync_snapshot");
1443
- const includeTranscriptsRaw = parsed.searchParams.get("include_transcripts");
1444
- const includeContentRaw = parsed.searchParams.get("content");
1445
- if (includeTranscriptsRaw !== null && includeTranscriptsRaw !== "true" && includeTranscriptsRaw !== "false") {
1446
- throw new EngramAccessInputError(
1447
- `include_transcripts must be one of: true, false (got: ${includeTranscriptsRaw})`
1448
- );
1449
- }
1450
- if (includeContentRaw !== null && includeContentRaw !== "true" && includeContentRaw !== "false") {
1451
- throw new EngramAccessInputError(
1452
- `content must be one of: true, false (got: ${includeContentRaw})`
1453
- );
1454
- }
1455
- const namespaceParam = parsed.searchParams.get("namespace");
1456
- const result = await this.service.offlineSyncSnapshot({
1457
- namespace: this.resolveNamespace(
1458
- req,
1459
- namespaceParam && namespaceParam.length > 0 ? namespaceParam : void 0
1460
- ),
1461
- principal: this.resolveRequestPrincipal(req),
1462
- includeTranscripts: includeTranscriptsRaw !== "false",
1463
- includeContent: includeContentRaw !== "false"
1464
- });
1465
- this.respondJson(res, 200, result);
1466
- return;
1467
- }
1468
- if (req.method === "GET" && (pathname === "/engram/v1/offline-sync/snapshot-stream" || pathname === "/remnic/v1/offline-sync/snapshot-stream")) {
1469
- this.enforceTokenOp("offline_sync_snapshot_stream");
1470
- const includeTranscriptsRaw = parsed.searchParams.get("include_transcripts");
1471
- const includeContentRaw = parsed.searchParams.get("content");
1472
- if (includeTranscriptsRaw !== null && includeTranscriptsRaw !== "true" && includeTranscriptsRaw !== "false") {
1473
- throw new EngramAccessInputError(
1474
- `include_transcripts must be one of: true, false (got: ${includeTranscriptsRaw})`
1475
- );
1476
- }
1477
- if (includeContentRaw !== null && includeContentRaw !== "false") {
1478
- throw new EngramAccessInputError("snapshot-stream content must be false");
1479
- }
1480
- const namespaceParam = parsed.searchParams.get("namespace");
1481
- const result = await this.service.offlineSyncSnapshotStream({
1482
- namespace: this.resolveNamespace(
1483
- req,
1484
- namespaceParam && namespaceParam.length > 0 ? namespaceParam : void 0
1485
- ),
1486
- principal: this.resolveRequestPrincipal(req),
1487
- includeTranscripts: includeTranscriptsRaw !== "false",
1488
- includeContent: false,
1489
- signal: this.createRequestAbortSignal(req, res)
1490
- });
1491
- await respondOfflineSnapshotStream(res, result, correlationIdStore.getStore());
1492
- return;
1493
- }
1494
- if (req.method === "GET" && (pathname === "/engram/v1/offline-sync/manifest-stream" || pathname === "/remnic/v1/offline-sync/manifest-stream")) {
1495
- this.enforceTokenOp("offline_sync_snapshot_stream");
1496
- const includeTranscriptsRaw = parsed.searchParams.get("include_transcripts");
1497
- const includeContentRaw = parsed.searchParams.get("content");
1498
- if (includeTranscriptsRaw !== null && includeTranscriptsRaw !== "true" && includeTranscriptsRaw !== "false") {
1499
- throw new EngramAccessInputError(
1500
- `include_transcripts must be one of: true, false (got: ${includeTranscriptsRaw})`
1501
- );
1502
- }
1503
- if (includeContentRaw !== null && includeContentRaw !== "false") {
1504
- throw new EngramAccessInputError("manifest-stream content must be false");
1505
- }
1506
- const namespaceParam = parsed.searchParams.get("namespace");
1507
- const result = await this.service.offlineSyncManifestStream({
1508
- namespace: this.resolveNamespace(
1509
- req,
1510
- namespaceParam && namespaceParam.length > 0 ? namespaceParam : void 0
1511
- ),
1512
- principal: this.resolveRequestPrincipal(req),
1513
- includeTranscripts: includeTranscriptsRaw !== "false",
1514
- signal: this.createRequestAbortSignal(req, res)
1515
- });
1516
- await respondOfflineManifestStream(res, result, correlationIdStore.getStore());
1517
- return;
1518
- }
1519
- if (req.method === "POST" && (pathname === "/engram/v1/offline-sync/snapshot" || pathname === "/remnic/v1/offline-sync/snapshot")) {
1520
- this.enforceTokenOp("offline_sync_snapshot");
1521
- const body = await this.readValidatedBody(
1522
- req,
1523
- "offlineSyncSnapshot",
1524
- OFFLINE_SYNC_SNAPSHOT_BASE_MAX_BODY_BYTES
1525
- );
1526
- const result = await this.service.offlineSyncSnapshot({
1527
- namespace: this.resolveNamespace(req, body.namespace),
1528
- principal: this.resolveRequestPrincipal(req),
1529
- includeTranscripts: body.includeTranscripts,
1530
- includeContent: body.includeContent,
1531
- baseFiles: body.baseFiles,
1532
- ...body.baseCapturedAt ? { baseCapturedAt: new Date(body.baseCapturedAt) } : {},
1533
- signal: this.createRequestAbortSignal(req, res)
1534
- });
1535
- this.respondJson(res, 200, result);
1536
- return;
1537
- }
1538
- if (req.method === "POST" && (pathname === "/engram/v1/offline-sync/files" || pathname === "/remnic/v1/offline-sync/files")) {
1539
- this.enforceTokenOp("offline_sync_files");
1540
- const body = await this.readValidatedBody(req, "offlineSyncFiles");
1541
- const result = await this.service.offlineSyncFiles({
1542
- namespace: this.resolveNamespace(req, body.namespace),
1543
- principal: this.resolveRequestPrincipal(req),
1544
- includeTranscripts: body.includeTranscripts,
1545
- paths: body.paths
1546
- });
1547
- this.respondJson(res, 200, result);
1548
- return;
1549
- }
1550
- if (req.method === "POST" && (pathname === "/engram/v1/offline-sync/file-content" || pathname === "/remnic/v1/offline-sync/file-content")) {
1551
- this.enforceTokenOp("offline_sync_file_content");
1552
- const body = await this.readValidatedBody(req, "offlineSyncFileContent");
1553
- const result = await this.service.offlineSyncFileContent({
1554
- namespace: this.resolveNamespace(req, body.namespace),
1555
- principal: this.resolveRequestPrincipal(req),
1556
- includeTranscripts: body.includeTranscripts,
1557
- path: body.path,
1558
- offset: body.offset,
1559
- length: body.length
1560
- });
1561
- this.respondBinary(res, 200, result.content, {
1562
- "x-remnic-namespace": encodeURIComponent(result.namespace),
1563
- "x-remnic-file-path": encodeURIComponent(result.path),
1564
- "x-remnic-file-bytes": String(result.bytes),
1565
- "x-remnic-file-mtime-ms": String(result.mtimeMs),
1566
- "x-remnic-chunk-offset": String(result.offset),
1567
- "x-remnic-chunk-bytes": String(result.chunkBytes),
1568
- ...result.sha256 ? { "x-remnic-file-sha256": result.sha256 } : {}
1569
- });
1570
- return;
1571
- }
1572
- if (req.method === "POST" && (pathname === "/engram/v1/offline-sync/apply-file-content" || pathname === "/remnic/v1/offline-sync/apply-file-content")) {
1573
- this.enforceTokenOp("offline_sync_apply_file_content");
1574
- const namespaceParam = parsed.searchParams.get("namespace");
1575
- const bytes = this.readRequiredIntegerHeader(req, "x-remnic-file-bytes");
1576
- const offset = this.readOptionalIntegerHeader(req, "x-remnic-chunk-offset") ?? 0;
1577
- const content = await this.readBinaryBody(req, OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES);
1578
- const result = await this.service.offlineSyncApplyFileContent({
1579
- namespace: this.resolveNamespace(
1580
- req,
1581
- namespaceParam && namespaceParam.length > 0 ? namespaceParam : void 0
1582
- ),
1583
- principal: this.resolveRequestPrincipal(req),
1584
- includeTranscripts: this.parseOptionalBooleanHeader(
1585
- req,
1586
- "x-remnic-include-transcripts",
1587
- true
1588
- ),
1589
- sourceId: this.readRequiredDecodedHeader(req, "x-remnic-source-id"),
1590
- path: this.readRequiredDecodedHeader(req, "x-remnic-file-path"),
1591
- sha256: this.readRequiredHeader(req, "x-remnic-file-sha256"),
1592
- bytes,
1593
- mtimeMs: this.readRequiredNumberHeader(req, "x-remnic-file-mtime-ms"),
1594
- offset,
1595
- baseSha256: this.readOptionalHeader(req, "x-remnic-base-sha256"),
1596
- content
1597
- });
1598
- this.respondJson(res, 200, result);
1599
- return;
1600
- }
1601
- if (req.method === "POST" && (pathname === "/engram/v1/offline-sync/convergence-complete" || pathname === "/remnic/v1/offline-sync/convergence-complete")) {
1602
- void getOperation("offline_sync_apply_file_content");
1603
- this.enforceTokenAnyOf(["offline_sync_apply_file_content", "offline_sync_apply"]);
1604
- const namespaceParams = parsed.searchParams.getAll("namespace").filter(Boolean);
1605
- const requestedNamespaces = namespaceParams.length > 0 ? namespaceParams : [void 0];
1606
- const namespaces = requestedNamespaces.map((namespace) => this.resolveNamespace(req, namespace)).filter((namespace) => namespace !== void 0);
1607
- this.ensureWriteRateLimitAvailable(req);
1608
- const result = await this.service.offlineSyncFinalizeConvergence({
1609
- ...namespaces.length > 0 ? { namespaces } : {},
1610
- principal: this.resolveRequestPrincipal(req),
1611
- sourceId: this.readRequiredDecodedHeader(req, "x-remnic-source-id")
1612
- });
1613
- this.recordWriteRateLimitHit(req);
1614
- this.respondJson(res, 200, result);
1615
- return;
1616
- }
1617
- if (req.method === "POST" && (pathname === "/engram/v1/offline-sync/apply" || pathname === "/remnic/v1/offline-sync/apply")) {
1618
- this.enforceTokenOp("offline_sync_apply");
1619
- const body = await this.readValidatedBody(req, "offlineSyncApply", OFFLINE_SYNC_APPLY_MAX_BODY_BYTES);
1620
- const result = await this.service.offlineSyncApply({
1621
- namespace: this.resolveNamespace(req, body.namespace),
1622
- principal: this.resolveRequestPrincipal(req),
1623
- changeset: body.changeset,
1624
- returnCurrentFiles: body.returnCurrentFiles
1625
- });
1626
- this.respondJson(res, 200, result);
1627
- return;
1628
- }
1629
- if (req.method === "POST" && pathname === "/engram/v1/recall/explain") {
1630
- this.enforceTokenOp("recall_explain");
1631
- const body = await this.readValidatedBody(req, "recallExplain");
1632
- const response = await this.service.recallExplain({
1633
- sessionKey: body.sessionKey,
1634
- namespace: this.resolveNamespace(req, body.namespace),
1635
- authenticatedPrincipal: this.resolveRequestPrincipal(req)
1636
- });
1637
- this.respondJson(res, 200, response);
1638
- return;
1639
- }
1640
- if (req.method === "POST" && (pathname === "/engram/v1/action-confidence" || pathname === "/remnic/v1/action-confidence")) {
1641
- this.enforceTokenOp("action_confidence");
1642
- const body = await this.readValidatedBody(req, "actionConfidence");
1643
- this.respondJson(res, 200, await this.service.actionConfidence(body));
1644
- return;
1645
- }
1646
- if (req.method === "GET" && pathname === "/engram/v1/recall/tier-explain") {
1647
- this.enforceTokenOp("recall_tier_explain");
1648
- const sessionParam = parsed.searchParams.get("session");
1649
- const sessionKey = sessionParam && sessionParam.length > 0 ? sessionParam : void 0;
1650
- const namespaceParam = parsed.searchParams.get("namespace");
1651
- const namespace = this.resolveNamespace(
1652
- req,
1653
- namespaceParam && namespaceParam.length > 0 ? namespaceParam : void 0
1654
- );
1655
- const payload = await this.service.recallTierExplain(sessionKey, namespace, this.resolveRequestPrincipal(req));
1656
- this.respondJson(res, 200, payload);
1657
- return;
1658
- }
1659
- if (req.method === "GET" && pathname === "/engram/v1/recall/xray") {
1660
- this.enforceTokenOp("recall_xray");
1661
- const outcome = await handleRecallXrayHttpQuery({
1662
- getParam: (name) => parsed.searchParams.get(name),
1663
- resolveNamespace: (namespace) => this.resolveNamespace(req, namespace),
1664
- principal: this.resolveRequestPrincipal(req),
1665
- connector: this.resolveConnector(req),
1666
- run: (request) => this.service.recallXray(request)
1667
- });
1668
- this.respondJson(res, outcome.status, outcome.body);
1669
- return;
1670
- }
1671
- if (req.method === "GET" && (pathname === "/engram/v1/recall/why" || pathname === "/remnic/v1/recall/why")) {
1672
- this.enforceTokenOp("recall_why");
1673
- const outcome = await handleRecallWhyHttpQuery({
1674
- getParam: (name) => parsed.searchParams.get(name),
1675
- resolveNamespace: (namespace) => this.resolveNamespace(req, namespace),
1676
- principal: this.resolveRequestPrincipal(req),
1677
- connector: this.resolveConnector(req),
1678
- run: (request) => this.service.recallWhy(request)
1679
- });
1680
- this.respondJson(res, outcome.status, outcome.body);
1681
- return;
1682
- }
1683
- if (req.method === "GET" && (pathname === "/engram/v1/who-knows" || pathname === "/remnic/v1/who-knows")) {
1684
- this.enforceTokenOp("who_knows");
1685
- const outcome = await handleWhoKnowsHttpQuery({
1686
- getParam: (name) => parsed.searchParams.get(name),
1687
- resolveNamespace: (namespace) => this.resolveNamespace(req, namespace),
1688
- principal: this.resolveRequestPrincipal(req),
1689
- run: (request) => this.service.whoKnows(request)
1690
- });
1691
- this.respondJson(res, outcome.status, outcome.body);
1692
- return;
1693
- }
1694
- if (req.method === "GET" && pathname === "/engram/v1/namespace/writable") {
1695
- if (!getOperation("namespace_writable")) throw new Error("namespace_writable operation is not registered");
1696
- this.respondJson(
1697
- res,
1698
- 200,
1699
- await resolveQueryNamespaceWritablePreflight(
1700
- tokenCapabilityStore.getStore(),
1701
- parsed.searchParams,
1702
- this.resolveRequestPrincipal(req),
1703
- this.service.configRef.defaultNamespace,
1704
- (request) => this.service.namespaceWritablePreflight(request)
1705
- )
1706
- );
1707
- return;
1708
- }
1709
- if (req.method === "GET" && (pathname === "/engram/v1/wearables/status" || pathname === "/remnic/v1/wearables/status")) {
1710
- this.enforceTokenOp("wearables_status");
1711
- this.respondJson(res, 200, await this.service.wearablesStatus(this.wearablesScope(req, parsed.searchParams.get("namespace") ?? void 0, parsed.searchParams.get("sessionKey") ?? void 0)));
1712
- return;
1713
- }
1714
- if (req.method === "POST" && (pathname === "/engram/v1/wearables/sync" || pathname === "/remnic/v1/wearables/sync")) {
1715
- this.enforceTokenOp("wearables_sync");
1716
- const body = await this.readJsonBody(req);
1717
- const source = optionalQueryString(body.source, "source");
1718
- const date = optionalQueryString(body.date, "date");
1719
- let days;
1720
- if (body.days !== void 0 && body.days !== null) {
1721
- if (typeof body.days !== "number" || !Number.isInteger(body.days) || body.days < 1) {
1722
- throw new EngramAccessInputError(
1723
- `days must be a positive integer (got ${JSON.stringify(body.days)})`
1724
- );
1725
- }
1726
- days = body.days;
1727
- }
1728
- if (body.forceMemories !== void 0 && typeof body.forceMemories !== "boolean") {
1729
- throw new EngramAccessInputError(
1730
- `forceMemories must be a boolean (got ${JSON.stringify(body.forceMemories)})`
1731
- );
1732
- }
1733
- try {
1734
- const summaries = await this.service.wearablesSync({ source, date, days, forceMemories: body.forceMemories === true, ...this.wearablesScope(req, optionalQueryString(body.namespace, "namespace"), optionalQueryString(body.sessionKey, "sessionKey")) });
1735
- this.respondJson(res, 200, { summaries });
1736
- } catch (err) {
1737
- if (this.respondWearablesError(res, err)) return;
1738
- throw err;
1739
- }
1740
- return;
1741
- }
1742
- if (req.method === "GET" && (pathname === "/engram/v1/wearables/transcript" || pathname === "/remnic/v1/wearables/transcript")) {
1743
- this.enforceTokenOp("transcript_day");
1744
- const date = parsed.searchParams.get("date");
1745
- if (!date || date.trim().length === 0) {
1746
- throw new EngramAccessInputError(
1747
- "date query parameter is required (YYYY-MM-DD)"
1748
- );
1749
- }
1750
- const sourceParam = parsed.searchParams.get("source");
1751
- try {
1752
- const transcripts = await this.service.wearablesTranscriptDay({ date, source: sourceParam && sourceParam.length > 0 ? sourceParam : void 0, ...this.wearablesScope(req, parsed.searchParams.get("namespace") ?? void 0, parsed.searchParams.get("sessionKey") ?? void 0) });
1753
- this.respondJson(res, 200, { transcripts });
1754
- } catch (err) {
1755
- if (this.respondWearablesError(res, err)) return;
1756
- throw err;
1757
- }
1758
- return;
1759
- }
1760
- if (req.method === "GET" && (pathname === "/engram/v1/wearables/transcripts/search" || pathname === "/remnic/v1/wearables/transcripts/search")) {
1761
- this.enforceTokenOp("transcript_search");
1762
- const queryParam = parsed.searchParams.get("q");
1763
- if (!queryParam || queryParam.trim().length === 0) {
1764
- throw new EngramAccessInputError(
1765
- "q query parameter is required and must be non-empty"
1766
- );
1767
- }
1768
- try {
1769
- const results = await this.service.wearablesTranscriptSearch({ query: queryParam, source: nonEmptyQueryParam(parsed.searchParams.get("source")), from: nonEmptyQueryParam(parsed.searchParams.get("from")), to: nonEmptyQueryParam(parsed.searchParams.get("to")), limit: positiveIntQueryParam(parsed.searchParams.get("limit"), "limit"), ...this.wearablesScope(req, parsed.searchParams.get("namespace") ?? void 0, parsed.searchParams.get("sessionKey") ?? void 0) });
1770
- this.respondJson(res, 200, { results });
1771
- } catch (err) {
1772
- if (this.respondWearablesError(res, err)) return;
1773
- throw err;
1774
- }
1775
- return;
1776
- }
1777
- if (req.method === "GET" && (pathname === "/engram/v1/wearables/memories" || pathname === "/remnic/v1/wearables/memories")) {
1778
- this.enforceTokenOp("transcript_memories");
1779
- try {
1780
- const memories = await this.service.wearablesTranscriptMemories({ source: nonEmptyQueryParam(parsed.searchParams.get("source")), date: nonEmptyQueryParam(parsed.searchParams.get("date")), limit: positiveIntQueryParam(parsed.searchParams.get("limit"), "limit"), ...this.wearablesScope(req, parsed.searchParams.get("namespace") ?? void 0, parsed.searchParams.get("sessionKey") ?? void 0) });
1781
- this.respondJson(res, 200, { memories });
1782
- } catch (err) {
1783
- if (this.respondWearablesError(res, err)) return;
1784
- throw err;
1785
- }
1786
- return;
1787
- }
1788
- if (req.method === "GET" && (pathname === "/engram/v1/meetings" || pathname === "/remnic/v1/meetings")) {
1789
- this.enforceTokenOp("meetings_list");
1790
- await respondMeetingsList(res, this.respondJson.bind(this), this.service, nonEmptyQueryParam(parsed.searchParams.get("date")), this.wearablesScope(req, parsed.searchParams.get("namespace") ?? void 0, parsed.searchParams.get("sessionKey") ?? void 0));
1791
- return;
1792
- }
1793
- if (req.method === "POST" && (pathname === "/engram/v1/meetings/build" || pathname === "/remnic/v1/meetings/build")) {
1794
- this.enforceTokenOp("meetings_build");
1795
- await respondMeetingsBuild(req, res, this.respondJson.bind(this), this.readJsonBody.bind(this), this.service, { enforceQuota: () => this.ensureWriteRateLimitAvailable(req), recordHit: () => this.recordWriteRateLimitHit(req) }, (ns, sk) => this.wearablesScope(req, ns, sk));
1796
- return;
1797
- }
1798
- const meetingGetEngram = /^\/engram\/v1\/meetings\/([^/]+)$/.exec(pathname);
1799
- if (req.method === "GET" && meetingGetEngram) {
1800
- this.enforceTokenOp("meetings_get");
1801
- await respondMeetingsGet(res, this.respondJson.bind(this), this.service, meetingGetEngram[1] ?? "", this.wearablesScope(req, parsed.searchParams.get("namespace") ?? void 0, parsed.searchParams.get("sessionKey") ?? void 0));
1802
- return;
1803
- }
1804
- const meetingGetRemnic = /^\/remnic\/v1\/meetings\/([^/]+)$/.exec(pathname);
1805
- if (req.method === "GET" && meetingGetRemnic) {
1806
- this.enforceTokenOp("meetings_get");
1807
- await respondMeetingsGet(res, this.respondJson.bind(this), this.service, meetingGetRemnic[1] ?? "", this.wearablesScope(req, parsed.searchParams.get("namespace") ?? void 0, parsed.searchParams.get("sessionKey") ?? void 0));
1808
- return;
1809
- }
1810
- if (req.method === "POST" && (pathname === "/engram/v1/recall/deep" || pathname === "/remnic/v1/recall/deep")) {
1811
- this.enforceTokenOp("deep_recall");
1812
- await respondDeepRecall(req, res, this.respondJson.bind(this), this.readJsonBody.bind(this), this.service, (ns, sk) => this.wearablesScope(req, ns ?? parsed.searchParams.get("namespace") ?? void 0, sk ?? parsed.searchParams.get("sessionKey") ?? void 0), abortSignal);
1813
- return;
1814
- }
1815
- if (await maybeRespondRecallNavigation(req, res, pathname, req.method, (op) => this.enforceTokenOp(op), this.respondJson.bind(this), this.readJsonBody.bind(this), this.service, (ns, sk) => this.wearablesScope(req, ns ?? parsed.searchParams.get("namespace") ?? void 0, sk ?? parsed.searchParams.get("sessionKey") ?? void 0))) return;
1816
- if (await maybeRespondMemoryBrowse(req, res, pathname, req.method, (op) => this.enforceTokenOp(op), this.respondJson.bind(this), this.readJsonBody.bind(this), this.service, (ns) => this.wearablesScope(req, ns ?? parsed.searchParams.get("namespace") ?? void 0))) return;
1817
- if (req.method === "GET" && (pathname === "/engram/v1/location/status" || pathname === "/remnic/v1/location/status")) {
1818
- this.enforceTokenOp("location_status");
1819
- await respondLocationStatus(res, this.respondJson.bind(this), this.service);
1820
- return;
1821
- }
1822
- if (req.method === "GET" && (pathname === "/engram/v1/location/check" || pathname === "/remnic/v1/location/check")) {
1823
- this.enforceTokenOp("location_check");
1824
- await respondLocationCheck(res, this.respondJson.bind(this), this.service);
1825
- return;
1826
- }
1827
- if (req.method === "POST" && (pathname === "/engram/v1/location/sync" || pathname === "/remnic/v1/location/sync")) {
1828
- this.enforceTokenOp("location_sync");
1829
- await respondLocationSync(await this.readJsonBody(req), res, this.respondJson.bind(this), this.service);
1830
- return;
1831
- }
1832
- if (req.method === "POST" && (pathname === "/engram/v1/location/backfill" || pathname === "/remnic/v1/location/backfill")) {
1833
- this.enforceTokenOp("location_backfill");
1834
- await respondLocationBackfill(await this.readJsonBody(req), res, this.respondJson.bind(this), this.service);
1835
- return;
1836
- }
1837
- if (req.method === "GET" && (pathname === "/engram/v1/location/day" || pathname === "/remnic/v1/location/day")) {
1838
- this.enforceTokenOp("location_day");
1839
- await respondLocationDay(parsed.searchParams.get("date"), res, this.respondJson.bind(this), this.service);
1840
- return;
1841
- }
1842
- if (req.method === "GET" && (pathname === "/engram/v1/standup" || pathname === "/remnic/v1/standup")) {
1843
- this.enforceTokenOp("standup");
1844
- await respondStandup(parsed.searchParams.get("date"), res, this.respondJson.bind(this), this.service);
1845
- return;
1846
- }
1847
- if (req.method === "POST" && pathname === "/engram/v1/observe") {
1848
- this.enforceTokenOp("observe");
1849
- const body = await this.readValidatedBody(req, "observe");
1850
- const op = getOperation("observe");
1851
- if (!op) {
1852
- throw new EngramAccessInputError("access-boundary: operation not registered: observe");
1853
- }
1854
- const output = await op.run(
1855
- { ...body, namespace: this.resolveNamespace(req, body.namespace) },
1856
- {
1857
- service: this.service,
1858
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
1859
- hooks: { enforceWriteQuota: () => this.ensureWriteRateLimitAvailable(req) },
1860
- // Phase 1 provenance: server-resolved connector identity from the
1861
- // bearer token (REST path, mirroring the MCP tools/call dispatch).
1862
- sourceConnector: this.resolveConnector(req)
1863
- }
1864
- );
1865
- if (this.shouldCountWriteRateLimit(output.result)) {
1866
- this.recordWriteRateLimitHit(req);
1867
- }
1868
- this.respondJson(res, 202, output.result);
1869
- return;
1870
- }
1871
- if (req.method === "POST" && pathname === "/engram/v1/lcm/search") {
1872
- this.enforceTokenOp("lcm_search");
1873
- const body = await this.readValidatedBody(req, "lcmSearch");
1874
- const response = await this.service.lcmSearch({
1875
- query: body.query,
1876
- sessionKey: body.sessionKey,
1877
- sessionPrefix: body.sessionPrefix,
1878
- namespace: this.resolveNamespace(req, body.namespace),
1879
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
1880
- limit: body.limit
1881
- });
1882
- this.respondJson(res, 200, response);
1883
- return;
1884
- }
1885
- if (await maybeHandleLifecycleFlush(
1886
- this.lifecycleFlushDeps(req, res),
1887
- req.method,
1888
- pathname,
1889
- abortSignal
1890
- )) return;
1891
- if (req.method === "GET" && pathname === "/engram/v1/lcm/status") {
1892
- this.enforceTokenOp("lcm_status");
1893
- this.respondJson(res, 200, await this.service.lcmStatus());
1894
- return;
1895
- }
1896
- const relayEventMatch = /^\/engram\/v1\/relay\/missions\/([^/]+)\/events$/.exec(pathname);
1897
- if (req.method === "POST" && relayEventMatch) {
1898
- const missionId = decodeRelayMissionIdSegment(relayEventMatch[1] ?? "");
1899
- const body = await this.readJsonBody(req);
1900
- const unexpectedFields = Object.keys(body).filter(
1901
- (key) => key !== "namespace" && key !== "event"
1902
- );
1903
- if (unexpectedFields.length > 0) {
1904
- throw new EngramAccessInputError(
1905
- `Relay append body contains unexpected field(s): ${unexpectedFields.sort().join(", ")}`
1906
- );
1907
- }
1908
- if (!Object.prototype.hasOwnProperty.call(body, "event")) {
1909
- throw new EngramAccessInputError("Relay append body must contain an event object");
1910
- }
1911
- if (Object.prototype.hasOwnProperty.call(body, "namespace") && body.namespace !== void 0 && body.namespace !== null && typeof body.namespace !== "string") {
1912
- throw new EngramAccessInputError("namespace must be a string when provided");
1913
- }
1914
- rejectBlankRelayNamespace(
1915
- typeof body.namespace === "string" ? body.namespace : void 0
1916
- );
1917
- const namespace = this.resolveNamespace(
1918
- req,
1919
- typeof body.namespace === "string" ? body.namespace : void 0
1920
- );
1921
- const op = getOperation("relay_mission_append");
1922
- if (!op) {
1923
- throw new Error("access-boundary: operation not registered: relay_mission_append");
1924
- }
1925
- let writeQuota;
1926
- try {
1927
- const output = await op.run(
1928
- { missionId, namespace, event: body.event },
1929
- {
1930
- service: this.service,
1931
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
1932
- hooks: {
1933
- enforceWriteQuota: () => {
1934
- writeQuota ??= this.reserveWriteRateLimitSlot(req);
1935
- }
1936
- }
1937
- }
1938
- );
1939
- if (output.result.appended) {
1940
- writeQuota?.commit();
1941
- writeQuota = void 0;
1942
- }
1943
- this.respondJson(res, output.result.appended ? 201 : 200, output.result);
1944
- } finally {
1945
- writeQuota?.release();
1946
- }
1947
- return;
1948
- }
1949
- const relayMissionMatch = /^\/engram\/v1\/relay\/missions\/([^/]+)$/.exec(pathname);
1950
- if (req.method === "GET" && relayMissionMatch) {
1951
- const missionId = decodeRelayMissionIdSegment(relayMissionMatch[1] ?? "");
1952
- rejectBlankRelayNamespace(parsed.searchParams.get("namespace"));
1953
- const namespace = this.resolveNamespace(
1954
- req,
1955
- parsed.searchParams.get("namespace") ?? void 0
1956
- );
1957
- const op = getOperation("relay_mission_read");
1958
- if (!op) {
1959
- throw new Error("access-boundary: operation not registered: relay_mission_read");
1960
- }
1961
- const authenticatedPrincipal = this.resolveRequestPrincipal(req)?.trim() || void 0;
1962
- const output = await op.run(
1963
- {
1964
- missionId,
1965
- namespace,
1966
- since: parsed.searchParams.get("since") ?? void 0,
1967
- until: parsed.searchParams.get("until") ?? void 0,
1968
- limit: parsed.searchParams.get("limit") ?? void 0
1969
- },
1970
- {
1971
- service: this.service,
1972
- authenticatedPrincipal
1973
- }
1974
- );
1975
- res.setHeader("cache-control", "no-store");
1976
- if (authenticatedPrincipal !== void 0) {
1977
- try {
1978
- res.setHeader("x-remnic-authenticated-principal", encodeURIComponent(authenticatedPrincipal));
1979
- } catch {
1980
- }
1981
- }
1982
- this.respondJson(res, 200, output.result);
1983
- return;
1984
- }
1985
- if (req.method === "POST" && pathname === "/engram/v1/memories") {
1986
- const body = await this.readValidatedBody(req, "memoryStore");
1987
- const envelope = {
1988
- ...body,
1989
- namespace: this.resolveNamespace(req, body.namespace)
1990
- };
1991
- const op = getOperation("memory_store");
1992
- if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: memory_store");
1993
- const output = await op.run(envelope, {
1994
- service: this.service,
1995
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
1996
- hooks: { enforceWriteQuota: () => this.ensureWriteRateLimitAvailable(req) },
1997
- // Phase 1 provenance: server-resolved connector identity from the
1998
- // bearer token (REST path, mirroring the MCP tools/call dispatch).
1999
- sourceConnector: this.resolveConnector(req)
2000
- });
2001
- const response = output.result;
2002
- if (this.shouldCountWriteRateLimit(response)) {
2003
- this.recordWriteRateLimitHit(req);
2004
- }
2005
- this.respondJson(res, this.writeResponseStatus(response), response);
2006
- return;
2007
- }
2008
- if (req.method === "POST" && pathname === "/engram/v1/coding/decisions") {
2009
- const body = await this.readJsonBody(req);
2010
- const isWriteSubcommand = body.subcommand === "record" || body.subcommand === "supersede";
2011
- if (isWriteSubcommand) {
2012
- this.ensureWriteRateLimitAvailable(req);
2013
- }
2014
- const op = getOperation("coding_decision");
2015
- if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: coding_decision");
2016
- const output = await op.run(this.gatedBodyNamespace(req, body), {
2017
- service: this.service,
2018
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2019
- sourceConnector: this.resolveConnector(req)
2020
- });
2021
- if (isWriteSubcommand) {
2022
- this.recordWriteRateLimitHit(req);
2023
- }
2024
- this.respondJson(res, 200, output.result);
2025
- return;
2026
- }
2027
- if (req.method === "POST" && pathname === "/engram/v1/coding/architecture") {
2028
- const body = await this.readJsonBody(req);
2029
- const isWriteSubcommand = body.subcommand === "refresh";
2030
- if (isWriteSubcommand) {
2031
- this.ensureWriteRateLimitAvailable(req);
2032
- }
2033
- const op = getOperation("coding_architecture");
2034
- if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: coding_architecture");
2035
- const output = await op.run(this.gatedBodyNamespace(req, body), {
2036
- service: this.service,
2037
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2038
- sourceConnector: this.resolveConnector(req)
2039
- });
2040
- if (isWriteSubcommand) {
2041
- this.recordWriteRateLimitHit(req);
2042
- }
2043
- this.respondJson(res, 200, output.result);
2044
- return;
2045
- }
2046
- if (req.method === "POST" && pathname === "/engram/v1/coding/delta") {
2047
- const body = await this.readJsonBody(req);
2048
- const op = getOperation("coding_delta");
2049
- if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: coding_delta");
2050
- const output = await op.run(this.gatedBodyNamespace(req, body), {
2051
- service: this.service,
2052
- authenticatedPrincipal: this.resolveRequestPrincipal(req)
2053
- });
2054
- this.respondJson(res, 200, output.result);
2055
- return;
2056
- }
2057
- if (req.method === "POST" && pathname === "/engram/v1/briefing") {
2058
- await respondBriefingHttp({
2059
- gatedBody: this.gatedBodyNamespace(req, await this.readJsonBody(req)),
2060
- service: this.service,
2061
- principal: this.resolveRequestPrincipal(req),
2062
- respondJson: (status, payload) => this.respondJson(res, status, payload),
2063
- abortSignal
2064
- });
2065
- return;
2066
- }
2067
- if (req.method === "POST" && pathname === "/engram/v1/correction/plan") {
2068
- this.ensureWriteRateLimitAvailable(req);
2069
- const body = await this.readJsonBody(req);
2070
- const op = getOperation("memory_correct_plan");
2071
- if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: memory_correct_plan");
2072
- const output = await op.run(this.gatedBodyNamespace(req, body), {
2073
- service: this.service,
2074
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2075
- ...abortSignal ? { abortSignal } : {}
2076
- });
2077
- this.recordWriteRateLimitHit(req);
2078
- this.respondJson(res, 200, output.result);
2079
- return;
2080
- }
2081
- if (req.method === "POST" && pathname === "/engram/v1/correction/apply") {
2082
- this.ensureWriteRateLimitAvailable(req);
2083
- const body = await this.readJsonBody(req);
2084
- const op = getOperation("memory_correct_apply");
2085
- if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: memory_correct_apply");
2086
- const output = await op.run(this.gatedBodyNamespace(req, body), {
2087
- service: this.service,
2088
- authenticatedPrincipal: this.resolveRequestPrincipal(req)
2089
- });
2090
- this.recordWriteRateLimitHit(req);
2091
- this.respondJson(res, 200, output.result);
2092
- return;
2093
- }
2094
- if (req.method === "GET" && pathname === "/engram/v1/correction/pending") {
2095
- const op = getOperation("correction_pending");
2096
- if (op) {
2097
- const output = await op.run(
2098
- { namespace: this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0), sessionKey: parsed.searchParams.get("sessionKey") ?? void 0 },
2099
- { service: this.service, authenticatedPrincipal: this.resolveRequestPrincipal(req) }
2100
- );
2101
- this.respondJson(res, 200, output.result);
2102
- } else {
2103
- const namespace = this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0);
2104
- const sessionKey = parsed.searchParams.get("sessionKey") ?? void 0;
2105
- const plans = await this.service.correctionListPending({
2106
- ...namespace ? { namespace } : {},
2107
- ...sessionKey ? { sessionKey } : {},
2108
- principal: this.resolveRequestPrincipal(req)
2109
- });
2110
- this.respondJson(res, 200, plans);
2111
- }
2112
- return;
2113
- }
2114
- if (req.method === "POST" && pathname === "/engram/v1/suggestions") {
2115
- this.enforceTokenOp("suggestion_submit");
2116
- const body = await this.readValidatedBody(req, "suggestionSubmit");
2117
- const request = {
2118
- schemaVersion: body.schemaVersion,
2119
- idempotencyKey: body.idempotencyKey,
2120
- dryRun: body.dryRun === true,
2121
- sessionKey: body.sessionKey,
2122
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2123
- content: body.content,
2124
- category: body.category,
2125
- confidence: body.confidence,
2126
- namespace: this.resolveNamespace(req, body.namespace),
2127
- tags: body.tags,
2128
- entityRef: body.entityRef,
2129
- ttl: body.ttl,
2130
- sourceReason: body.sourceReason,
2131
- cwd: body.cwd,
2132
- projectTag: body.projectTag,
2133
- // #2829: raw alias spelling retained by the schema transform.
2134
- ...body.rawCategory !== void 0 ? { rawCategory: body.rawCategory } : {},
2135
- // Phase 1 provenance: server-resolved connector identity from the
2136
- // bearer token (REST path, mirroring the MCP tools/call dispatch).
2137
- sourceConnector: this.resolveConnector(req)
2138
- };
2139
- const response = await this.service.suggestionSubmit(request, {
2140
- enforceWriteQuota: () => this.ensureWriteRateLimitAvailable(req)
2141
- });
2142
- if (this.shouldCountWriteRateLimit(response)) {
2143
- this.recordWriteRateLimitHit(req);
2144
- }
2145
- this.respondJson(res, this.writeResponseStatus(response), response);
2146
- return;
2147
- }
2148
- if (req.method === "GET" && pathname === "/engram/v1/memories") {
2149
- this.enforceTokenOp("memory_list");
2150
- const limit = parseStrictIntegerQuery(parsed.searchParams.get("limit"), "limit", 50, 1);
2151
- const offset = parseStrictIntegerQuery(parsed.searchParams.get("offset"), "offset", 0, 0);
2152
- const sort = parseMemorySort(parsed.searchParams.get("sort"));
2153
- const response = await this.service.memoryBrowse({
2154
- query: parsed.searchParams.get("q") ?? void 0,
2155
- status: parsed.searchParams.get("status") ?? void 0,
2156
- category: parsed.searchParams.get("category") ?? void 0,
2157
- namespace: this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0),
2158
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2159
- sort,
2160
- limit,
2161
- offset
2162
- });
2163
- this.respondJson(res, 200, response);
2164
- return;
2165
- }
2166
- if (req.method === "GET" && pathname === "/engram/v1/recall/timings") {
2167
- const op = getOperation("recall_timings");
2168
- if (!op) {
2169
- throw new Error("access-boundary: operation not registered: recall_timings");
2170
- }
2171
- res.setHeader("cache-control", "no-store");
2172
- const output = await op.run({}, {
2173
- service: this.service,
2174
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2175
- operatorPrincipal: this.authenticatedPrincipal
2176
- });
2177
- this.respondJson(res, 200, output.result);
2178
- return;
2179
- }
2180
- const memoryMatch = pathname.match(/^\/engram\/v1\/memories\/([^/]+)$/);
2181
- if (req.method === "GET" && memoryMatch) {
2182
- const memoryId = decodeURIComponent(memoryMatch[1] ?? "");
2183
- const namespace = this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0);
2184
- const sessionKey = this.resolveRequestIdentity(req).sessionKey ?? parsed.searchParams.get("session") ?? parsed.searchParams.get("sessionKey") ?? void 0;
2185
- const op = getOperation("memory_get");
2186
- if (!op) {
2187
- throw new EngramAccessInputError("access-boundary: operation not registered: memory_get");
2188
- }
2189
- const output = await op.run(
2190
- { memoryId, namespace: namespace ?? null, sessionKey: sessionKey ?? null },
2191
- { service: this.service, authenticatedPrincipal: this.resolveRequestPrincipal(req) }
2192
- );
2193
- const response = output.result;
2194
- this.respondJson(res, response.found ? 200 : 404, response);
2195
- return;
2196
- }
2197
- const timelineMatch = pathname.match(/^\/engram\/v1\/memories\/([^/]+)\/timeline$/);
2198
- if (req.method === "GET" && timelineMatch) {
2199
- this.enforceTokenOp("memory_timeline");
2200
- const memoryId = decodeURIComponent(timelineMatch[1] ?? "");
2201
- const namespace = this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0);
2202
- const limit = parseStrictIntegerQuery(parsed.searchParams.get("limit"), "limit", 200, 1);
2203
- const response = await this.service.memoryTimeline(memoryId, namespace, limit, this.resolveRequestPrincipal(req));
2204
- this.respondJson(res, response.found ? 200 : 404, response);
2205
- return;
2206
- }
2207
- if (req.method === "GET" && pathname === "/engram/v1/entities") {
2208
- this.enforceTokenOp("entity_list");
2209
- const limit = parseStrictIntegerQuery(parsed.searchParams.get("limit"), "limit", 50, 1);
2210
- const offset = parseStrictIntegerQuery(parsed.searchParams.get("offset"), "offset", 0, 0);
2211
- const response = await this.service.entityList({
2212
- namespace: this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0),
2213
- query: parsed.searchParams.get("q") ?? void 0,
2214
- limit,
2215
- offset
2216
- }, this.resolveRequestPrincipal(req));
2217
- this.respondJson(res, 200, response);
2218
- return;
2219
- }
2220
- const entityMatch = pathname.match(/^\/engram\/v1\/entities\/([^/]+)$/);
2221
- if (req.method === "GET" && entityMatch) {
2222
- this.enforceTokenOp("entity_get");
2223
- const entityName = decodeURIComponent(entityMatch[1] ?? "");
2224
- const namespace = this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0);
2225
- const response = await this.service.entityGet(entityName, namespace, this.resolveRequestPrincipal(req));
2226
- this.respondJson(res, response.found ? 200 : 404, response);
2227
- return;
2228
- }
2229
- if (req.method === "GET" && pathname === "/engram/v1/review-queue") {
2230
- this.enforceTokenOp("review_queue_list");
2231
- const response = await this.service.reviewQueue(
2232
- parsed.searchParams.get("runId") ?? void 0,
2233
- this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0),
2234
- this.resolveRequestPrincipal(req)
2235
- );
2236
- this.respondJson(res, 200, response);
2237
- return;
2238
- }
2239
- if (req.method === "GET" && pathname === "/engram/v1/maintenance") {
2240
- this.enforceTokenOp("maintenance_status");
2241
- const namespace = this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0);
2242
- this.respondJson(res, 200, await this.service.maintenance(namespace, this.resolveRequestPrincipal(req)));
2243
- return;
2244
- }
2245
- if (req.method === "GET" && pathname === "/engram/v1/quality") {
2246
- this.enforceTokenOp("quality_status");
2247
- const namespace = this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0);
2248
- this.respondJson(res, 200, await this.service.quality(namespace, this.resolveRequestPrincipal(req)));
2249
- return;
2250
- }
2251
- if (req.method === "GET" && pathname === "/engram/v1/trust-zones/status") {
2252
- this.enforceTokenOp("trust_zones_status");
2253
- this.respondJson(
2254
- res,
2255
- 200,
2256
- await this.service.trustZoneStatus(this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0), this.resolveRequestPrincipal(req))
2257
- );
2258
- return;
2259
- }
2260
- if (req.method === "GET" && pathname === "/engram/v1/procedural/stats") {
2261
- this.enforceTokenOp("procedural_stats");
2262
- const namespaceParam = parsed.searchParams.get("namespace");
2263
- this.respondJson(
2264
- res,
2265
- 200,
2266
- await this.service.procedureStats(
2267
- {
2268
- namespace: this.resolveNamespace(
2269
- req,
2270
- namespaceParam && namespaceParam.length > 0 ? namespaceParam : void 0
2271
- )
2272
- },
2273
- this.resolveRequestPrincipal(req)
2274
- )
2275
- );
2276
- return;
2277
- }
2278
- if (req.method === "GET" && pathname === "/engram/v1/trust-zones/records") {
2279
- this.enforceTokenOp("trust_zones_records");
2280
- const limit = parseStrictIntegerQuery(parsed.searchParams.get("limit"), "limit", 25, 1);
2281
- const offset = parseStrictIntegerQuery(parsed.searchParams.get("offset"), "offset", 0, 0);
2282
- const response = await this.service.trustZoneBrowse({
2283
- query: parsed.searchParams.get("q") ?? void 0,
2284
- zone: parseTrustZoneFilter(parsed.searchParams.get("zone")),
2285
- kind: parseTrustZoneKindFilter(parsed.searchParams.get("kind")),
2286
- sourceClass: parseTrustZoneSourceClassFilter(parsed.searchParams.get("sourceClass")),
2287
- namespace: this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0),
2288
- limit,
2289
- offset
2290
- }, this.resolveRequestPrincipal(req));
2291
- this.respondJson(res, 200, response);
2292
- return;
2293
- }
2294
- if (req.method === "POST" && pathname === "/engram/v1/review-disposition") {
2295
- this.enforceTokenOp("review_disposition");
2296
- const body = await this.readValidatedBody(req, "reviewDisposition");
2297
- this.ensureWriteRateLimitAvailable(req);
2298
- const response = await this.service.reviewDisposition({
2299
- memoryId: body.memoryId,
2300
- status: body.status,
2301
- reasonCode: body.reasonCode,
2302
- namespace: this.resolveNamespace(req, body.namespace),
2303
- authenticatedPrincipal: this.resolveRequestPrincipal(req)
2304
- });
2305
- if (this.shouldCountWriteRateLimit(response)) {
2306
- this.recordWriteRateLimitHit(req);
2307
- }
2308
- this.respondJson(res, 200, response);
2309
- return;
2310
- }
2311
- if (req.method === "POST" && pathname === "/engram/v1/trust-zones/promote") {
2312
- this.enforceTokenOp("trust_zones_promote");
2313
- const body = await this.readValidatedBody(req, "trustZonePromote");
2314
- const dryRun = body.dryRun === true;
2315
- if (!dryRun) {
2316
- this.ensureWriteRateLimitAvailable(req);
2317
- }
2318
- const response = await this.service.trustZonePromote({
2319
- recordId: body.recordId,
2320
- targetZone: body.targetZone,
2321
- promotionReason: body.promotionReason,
2322
- recordedAt: body.recordedAt,
2323
- summary: body.summary,
2324
- dryRun,
2325
- namespace: this.resolveNamespace(req, body.namespace),
2326
- authenticatedPrincipal: this.resolveRequestPrincipal(req)
2327
- });
2328
- if (this.shouldCountWriteRateLimit(response)) {
2329
- this.recordWriteRateLimitHit(req);
2330
- }
2331
- this.respondJson(res, response.dryRun ? 200 : 201, response);
2332
- return;
2333
- }
2334
- if (req.method === "POST" && pathname === "/engram/v1/trust-zones/demo-seed") {
2335
- this.enforceTokenOp("trust_zones_demo_seed");
2336
- const body = await this.readValidatedBody(req, "trustZoneDemoSeed");
2337
- const dryRun = body.dryRun === true;
2338
- if (!dryRun) {
2339
- this.ensureWriteRateLimitAvailable(req);
2340
- }
2341
- const response = await this.service.trustZoneDemoSeed({
2342
- scenario: body.scenario,
2343
- recordedAt: body.recordedAt,
2344
- dryRun,
2345
- namespace: this.resolveNamespace(req, body.namespace),
2346
- authenticatedPrincipal: this.resolveRequestPrincipal(req)
2347
- });
2348
- if (this.shouldCountWriteRateLimit(response)) {
2349
- this.recordWriteRateLimitHit(req);
2350
- }
2351
- this.respondJson(res, response.dryRun ? 200 : 201, response);
2352
- return;
2353
- }
2354
- if (req.method === "POST" && pathname === "/v1/citations/observed") {
2355
- this.enforceTokenOp("citations_observed");
2356
- const body = await this.readJsonBody(req);
2357
- if (!body || typeof body !== "object" || Array.isArray(body)) {
2358
- throw new HttpError(400, "request body must be a JSON object", "invalid_body");
2359
- }
2360
- const payload = body;
2361
- const sessionId = typeof payload.sessionId === "string" ? payload.sessionId : void 0;
2362
- const namespace = typeof payload.namespace === "string" ? payload.namespace : void 0;
2363
- const citationsRaw = payload.citations;
2364
- if (!citationsRaw || typeof citationsRaw !== "object" || Array.isArray(citationsRaw)) {
2365
- throw new HttpError(400, "citations must be a JSON object with entries and rolloutIds", "invalid_citations");
2366
- }
2367
- const citObj = citationsRaw;
2368
- const entries = [];
2369
- if (Array.isArray(citObj.entries)) {
2370
- for (const raw of citObj.entries) {
2371
- if (raw && typeof raw === "object" && !Array.isArray(raw)) {
2372
- const e = raw;
2373
- if (typeof e.path === "string" && typeof e.lineStart === "number" && typeof e.lineEnd === "number") {
2374
- entries.push({
2375
- path: e.path,
2376
- lineStart: e.lineStart,
2377
- lineEnd: e.lineEnd,
2378
- note: typeof e.note === "string" ? e.note : ""
2379
- });
2380
- }
2381
- }
2382
- }
2383
- }
2384
- const rolloutIds = [];
2385
- if (Array.isArray(citObj.rolloutIds)) {
2386
- for (const id of citObj.rolloutIds) {
2387
- if (typeof id === "string" && id.length > 0) {
2388
- rolloutIds.push(id);
2389
- }
2390
- }
2391
- }
2392
- let matched = 0;
2393
- let submitted = 0;
2394
- if (typeof this.service.recordCitationUsage === "function") {
2395
- const result = await this.service.recordCitationUsage({
2396
- sessionId,
2397
- namespace: this.resolveNamespace(req, namespace),
2398
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2399
- entries,
2400
- rolloutIds
2401
- });
2402
- submitted = result.submitted;
2403
- matched = result.matched;
2404
- }
2405
- this.respondJson(res, 200, {
2406
- ok: true,
2407
- submitted,
2408
- matched,
2409
- entriesReceived: entries.length,
2410
- rolloutIdsReceived: rolloutIds.length
2411
- });
2412
- return;
2413
- }
2414
- if (req.method === "GET" && pathname === "/engram/v1/review/contradictions") {
2415
- this.enforceTokenOp("review_list");
2416
- const VALID_FILTERS = /* @__PURE__ */ new Set(["all", "unresolved", "contradicts", "independent", "duplicates", "needs-user"]);
2417
- const rawFilter = parsed.searchParams.get("filter") ?? "unresolved";
2418
- if (!VALID_FILTERS.has(rawFilter)) {
2419
- this.respondJson(res, 400, { error: `Invalid filter '${rawFilter}'. Valid: ${[...VALID_FILTERS].join(", ")}` });
2420
- return;
2421
- }
2422
- const namespace = this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0);
2423
- const limit = parseStrictIntegerQuery(parsed.searchParams.get("limit"), "limit", 50, 1);
2424
- const principal = this.resolveRequestPrincipal(req);
2425
- const resolved = await this.service.getReadableStorageForNamespace(namespace, principal);
2426
- const reviewNamespace = this.service.configRef.namespacesEnabled ? resolved.namespace : void 0;
2427
- const includeUnscopedForNamespace = Boolean(
2428
- reviewNamespace && isDefaultReviewNamespace(this.service.configRef.defaultNamespace, namespace, reviewNamespace)
2429
- );
2430
- const result = listPairs(this.service.memoryDir, {
2431
- filter: rawFilter,
2432
- namespace: reviewNamespace,
2433
- includeUnscopedForNamespace,
2434
- limit
2435
- });
2436
- this.respondJson(res, 200, result);
2437
- return;
2438
- }
2439
- if (req.method === "GET" && pathname.startsWith("/engram/v1/review/contradictions/")) {
2440
- this.enforceTokenOp("contradiction_detail");
2441
- const pairId = pathname.split("/").pop() ?? "";
2442
- const pair = readPair(this.service.memoryDir, pairId);
2443
- if (!pair) {
2444
- this.respondJson(res, 404, { error: "pair_not_found" });
2445
- return;
2446
- }
2447
- enforceNamespaceAllowList(
2448
- tokenCapabilityStore.getStore(),
2449
- pair.namespace,
2450
- this.service.configRef?.defaultNamespace
2451
- );
2452
- try {
2453
- await this.service.getReadableStorageForNamespace(pair.namespace, this.resolveRequestPrincipal(req));
2454
- } catch {
2455
- this.respondJson(res, 404, { error: "pair_not_found" });
2456
- return;
2457
- }
2458
- this.respondJson(res, 200, pair);
2459
- return;
2460
- }
2461
- if (req.method === "POST" && pathname === "/engram/v1/review/resolve") {
2462
- this.enforceTokenOp("review_resolve");
2463
- const body = await this.readJsonBody(req);
2464
- const pairId = typeof body.pairId === "string" ? body.pairId : "";
2465
- const verb = typeof body.verb === "string" ? body.verb : "";
2466
- if (!pairId || !verb) {
2467
- this.respondJson(res, 400, { error: "pairId and verb are required" });
2468
- return;
2469
- }
2470
- if (!isValidResolutionVerb(verb)) {
2471
- this.respondJson(res, 400, { error: `Invalid verb: ${verb}. Must be one of: keep-a, keep-b, merge, both-valid, needs-more-context` });
2472
- return;
2473
- }
2474
- const targetPair = readPair(this.service.memoryDir, pairId);
2475
- if (targetPair) {
2476
- enforceNamespaceAllowList(
2477
- tokenCapabilityStore.getStore(),
2478
- targetPair.namespace,
2479
- this.service.configRef?.defaultNamespace
2480
- );
2481
- }
2482
- const principal = this.resolveRequestPrincipal(req);
2483
- const result = await executeResolution(this.service.memoryDir, this.service.storageRef, pairId, verb, {
2484
- mergedMemoryId: typeof body.mergedMemoryId === "string" ? body.mergedMemoryId : void 0,
2485
- mergedContent: typeof body.mergedContent === "string" ? body.mergedContent : void 0,
2486
- storageForNamespace: async (namespace) => {
2487
- const resolved = await this.service.getWritableStorageForNamespace(namespace, principal);
2488
- return resolved.storage;
2489
- },
2490
- // Catalog write touch (issue #1499 sweep): a contradiction merge writes a
2491
- // new memory directly to the resolved namespace storage, bypassing the
2492
- // extraction write path. Record it so QMD maintenance / writtenSince
2493
- // don't miss the write. Best-effort and failure-tolerant.
2494
- onMergedMemoryWritten: (namespace, storageDir) => {
2495
- }
2496
- });
2497
- this.respondJson(res, 200, result);
2498
- return;
2499
- }
2500
- if (req.method === "GET" && pathname === "/engram/v1/graph/snapshot") {
2501
- this.enforceTokenOp("graph_snapshot");
2502
- const limitRaw = parsed.searchParams.get("limit");
2503
- let limit;
2504
- if (limitRaw !== null && limitRaw.length > 0) {
2505
- const parsedLimit = Number(limitRaw);
2506
- if (!Number.isFinite(parsedLimit) || !Number.isInteger(parsedLimit) || parsedLimit <= 0) {
2507
- this.respondJson(res, 400, {
2508
- error: "invalid_limit",
2509
- code: "invalid_limit",
2510
- message: "limit must be a positive integer"
2511
- });
2512
- return;
2513
- }
2514
- limit = parsedLimit;
2515
- }
2516
- const sinceRaw = parsed.searchParams.get("since");
2517
- let since;
2518
- if (sinceRaw !== null && sinceRaw.length > 0) {
2519
- if (!Number.isFinite(Date.parse(sinceRaw))) {
2520
- this.respondJson(res, 400, {
2521
- error: "invalid_since",
2522
- code: "invalid_since",
2523
- message: "since must be a parseable ISO timestamp"
2524
- });
2525
- return;
2526
- }
2527
- since = sinceRaw;
2528
- }
2529
- const focusNodeIdRaw = parsed.searchParams.get("focusNodeId");
2530
- const focusNodeId = focusNodeIdRaw && focusNodeIdRaw.length > 0 ? focusNodeIdRaw : void 0;
2531
- const categoriesRaw = parsed.searchParams.get("categories");
2532
- let categories;
2533
- if (categoriesRaw !== null && categoriesRaw.length > 0) {
2534
- categories = categoriesRaw.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
2535
- if (categories.length === 0) {
2536
- this.respondJson(res, 400, {
2537
- error: "invalid_categories",
2538
- code: "invalid_categories",
2539
- message: "categories must be a comma-separated list with at least one non-empty value"
2540
- });
2541
- return;
2542
- }
2543
- }
2544
- const namespaceParam = parsed.searchParams.get("namespace");
2545
- const namespace = this.resolveNamespace(
2546
- req,
2547
- namespaceParam && namespaceParam.length > 0 ? namespaceParam : void 0
2548
- );
2549
- try {
2550
- const snapshot = await this.service.graphSnapshot(
2551
- {
2552
- namespace,
2553
- ...limit !== void 0 ? { limit } : {},
2554
- ...since !== void 0 ? { since } : {},
2555
- ...focusNodeId !== void 0 ? { focusNodeId } : {},
2556
- ...categories !== void 0 ? { categories } : {}
2557
- },
2558
- this.resolveRequestPrincipal(req)
2559
- );
2560
- this.respondJson(res, 200, snapshot);
2561
- } catch (err) {
2562
- if (err instanceof Error && err.message.startsWith("graphSnapshot:")) {
2563
- this.respondJson(res, 400, {
2564
- error: "invalid_request",
2565
- code: "invalid_request",
2566
- message: err.message
2567
- });
2568
- return;
2569
- }
2570
- throw err;
2571
- }
2572
- return;
2573
- }
2574
- if (req.method === "POST" && pathname === "/engram/v1/contradiction-scan") {
2575
- this.enforceTokenOp("contradiction_scan_run");
2576
- const body = await this.readJsonBody(req);
2577
- const { runContradictionScan } = await import("./contradiction-scan-P2MHYFVG.js");
2578
- const principal = this.resolveRequestPrincipal(req);
2579
- const result = await runContradictionScan({
2580
- storage: this.service.storageRef,
2581
- config: this.service.configRef,
2582
- memoryDir: this.service.memoryDir,
2583
- embeddingLookupFactory: this.service.embeddingLookupFactoryRef,
2584
- storageForNamespace: (namespace) => this.service.getWritableStorageForNamespace(namespace, principal),
2585
- localLlm: this.service.localLlmRef,
2586
- fallbackLlm: this.service.fallbackLlmRef,
2587
- namespace: this.resolveNamespace(req, typeof body.namespace === "string" ? body.namespace : void 0)
2588
- });
2589
- this.respondJson(res, 200, result);
2590
- return;
2591
- }
2592
- if (req.method === "GET" && pathname === "/engram/v1/graph/events") {
2593
- this.enforceTokenOp("graph_events");
2594
- await this.handleGraphEventsSSE(req, res);
2595
- return;
2596
- }
2597
- if (req.method === "POST" && pathname === "/engram/v1/chat/message") {
2598
- this.enforceTokenOp("chat_message");
2599
- const body = await this.readJsonBody(req);
2600
- const resumeChatSessionId = typeof body.chatSessionId === "string" ? body.chatSessionId : void 0;
2601
- if (resumeChatSessionId) {
2602
- await enforceChatSessionNamespace(this.service, resumeChatSessionId);
2603
- } else {
2604
- enforceNamespaceAllowList(
2605
- tokenCapabilityStore.getStore(),
2606
- void 0,
2607
- this.service.configRef?.defaultNamespace
2608
- );
2609
- }
2610
- await handleChatMessage(
2611
- req,
2612
- res,
2613
- body,
2614
- { service: this.service, config: this.service.configRef?.chat, memoryDir: this.service.memoryDir },
2615
- this.resolveRequestPrincipal(req)
2616
- );
2617
- return;
2618
- }
2619
- const chatEventsMatch = /^\/engram\/v1\/chat\/events\/([^/]+)$/.exec(pathname);
2620
- if (req.method === "GET" && chatEventsMatch) {
2621
- this.enforceTokenOp("chat_events");
2622
- await enforceChatSessionNamespace(this.service, chatEventsMatch[1] ?? "");
2623
- await handleChatEventsSSE(
2624
- req,
2625
- res,
2626
- chatEventsMatch[1] ?? "",
2627
- {
2628
- service: this.service,
2629
- config: this.service.configRef?.chat,
2630
- memoryDir: this.service.memoryDir,
2631
- // Register the chat-SSE disconnect cleanup with this server's
2632
- // stop() set so an HTTP shutdown forcibly releases the heartbeat
2633
- // and transcript subscription even when a lingering client never
2634
- // emits 'close' (cursor Medium review; mirrors handleGraphEventsSSE
2635
- // which registers in sseCleanupFns at access-http.ts:2604).
2636
- registerSseCleanup: (cleanup) => {
2637
- this.sseCleanupFns.add(cleanup);
2638
- return () => {
2639
- this.sseCleanupFns.delete(cleanup);
2640
- };
2641
- }
2642
- },
2643
- this.resolveRequestPrincipal(req)
2644
- );
2645
- return;
2646
- }
2647
- if (req.method === "GET" && pathname === "/engram/v1/console/state") {
2648
- this.enforceTokenOp("console_state");
2649
- const namespace = this.resolveNamespace(req, parsed.searchParams.get("namespace") ?? void 0);
2650
- const snapshot = await this.service.consoleState(namespace, this.resolveRequestPrincipal(req));
2651
- this.respondJson(res, 200, snapshot);
2652
- return;
2653
- }
2654
- if (req.method === "GET" && pathname === "/engram/v1/peers") {
2655
- const op = getOperation("peer_list");
2656
- if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: peer_list");
2657
- const output = await op.run({}, {
2658
- service: this.service,
2659
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2660
- sourceConnector: this.resolveConnector(req)
2661
- });
2662
- this.respondJson(res, 200, output.result);
2663
- return;
2664
- }
2665
- const peerProfileMatch = /^\/engram\/v1\/peers\/([^/]+)\/profile$/.exec(pathname);
2666
- if (peerProfileMatch) {
2667
- if (req.method !== "GET") {
2668
- this.respondJson(res, 405, { error: "method_not_allowed", code: "method_not_allowed" });
2669
- return;
2670
- }
2671
- const op = getOperation("peer_profile_get");
2672
- if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: peer_profile_get");
2673
- const peerId = decodePeerIdSegment(peerProfileMatch[1] ?? "");
2674
- const output = await op.run({ id: peerId }, {
2675
- service: this.service,
2676
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2677
- sourceConnector: this.resolveConnector(req)
2678
- });
2679
- if (!output.result.found) {
2680
- this.respondJson(res, 404, { error: "peer_profile_not_found", code: "peer_profile_not_found" });
2681
- return;
2682
- }
2683
- this.respondJson(res, 200, output.result);
2684
- return;
2685
- }
2686
- const peerIdMatch = /^\/engram\/v1\/peers\/([^/]+)$/.exec(pathname);
2687
- if (peerIdMatch) {
2688
- const peerId = decodePeerIdSegment(peerIdMatch[1] ?? "");
2689
- if (req.method === "GET") {
2690
- const op = getOperation("peer_get");
2691
- if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: peer_get");
2692
- const output = await op.run({ id: peerId }, {
2693
- service: this.service,
2694
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2695
- sourceConnector: this.resolveConnector(req)
2696
- });
2697
- if (!output.result.found) {
2698
- this.respondJson(res, 404, { error: "peer_not_found", code: "peer_not_found" });
2699
- return;
2700
- }
2701
- this.respondJson(res, 200, output.result);
2702
- return;
2703
- }
2704
- if (req.method === "PUT") {
2705
- const body = await this.readJsonBody(req);
2706
- const op = getOperation("peer_set");
2707
- if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: peer_set");
2708
- for (const field of ["kind", "displayName", "notes"]) {
2709
- if (body[field] === null) throw new EngramAccessInputError(`${field} must be a string when provided`);
2710
- }
2711
- const output = await op.run({
2712
- id: peerId,
2713
- kind: body.kind,
2714
- displayName: body.displayName,
2715
- notes: body.notes
2716
- }, {
2717
- service: this.service,
2718
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2719
- sourceConnector: this.resolveConnector(req)
2720
- });
2721
- this.respondJson(res, output.result.created ? 201 : 200, output.result);
2722
- return;
2723
- }
2724
- if (req.method === "DELETE") {
2725
- const forgetParam = parsed.searchParams.get("forget");
2726
- if (forgetParam === "true") {
2727
- const body = await this.readJsonBody(req);
2728
- const confirm = typeof body.confirm === "string" ? body.confirm : "";
2729
- if (confirm !== "yes") {
2730
- this.respondJson(res, 400, {
2731
- error: "confirm_required",
2732
- code: "confirm_required",
2733
- message: "DELETE ?forget=true requires { confirm: 'yes' } in the request body"
2734
- });
2735
- return;
2736
- }
2737
- const op2 = getOperation("peer_forget");
2738
- if (!op2) throw new EngramAccessInputError("access-boundary: operation not registered: peer_forget");
2739
- const output2 = await op2.run({ id: peerId, confirm: "yes" }, {
2740
- service: this.service,
2741
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2742
- sourceConnector: this.resolveConnector(req)
2743
- });
2744
- this.respondJson(res, 200, output2.result);
2745
- return;
2746
- }
2747
- const op = getOperation("peer_delete");
2748
- if (!op) throw new EngramAccessInputError("access-boundary: operation not registered: peer_delete");
2749
- const output = await op.run({ id: peerId }, {
2750
- service: this.service,
2751
- authenticatedPrincipal: this.resolveRequestPrincipal(req),
2752
- sourceConnector: this.resolveConnector(req)
2753
- });
2754
- this.respondJson(res, 200, output.result);
2755
- return;
2756
- }
2757
- this.respondJson(res, 405, { error: "method_not_allowed", code: "method_not_allowed" });
2758
- return;
2759
- }
2760
- if (req.method === "GET" && pathname === "/engram/v1/dreams/status") {
2761
- this.enforceTokenOp("dreams_status");
2762
- const { normalizeDreamsStatusWindowHours } = await import("./dreams-ledger-UCHHRQ4R.js");
2763
- const windowHoursRaw = parsed.searchParams.get("windowHours");
2764
- let windowHours;
2765
- try {
2766
- windowHours = normalizeDreamsStatusWindowHours(
2767
- windowHoursRaw !== null ? Number(windowHoursRaw) : void 0
2768
- );
2769
- } catch {
2770
- this.respondJson(res, 400, { error: "windowHours must be a positive integer" });
2771
- return;
2772
- }
2773
- const namespaceParam = parsed.searchParams.get("namespace");
2774
- const namespace = this.resolveNamespace(req, namespaceParam && namespaceParam.length > 0 ? namespaceParam : void 0);
2775
- const result = await this.service.dreamsStatus({
2776
- windowHours,
2777
- namespace,
2778
- principal: this.resolveRequestPrincipal(req)
2779
- });
2780
- this.respondJson(res, 200, result);
2781
- return;
2782
- }
2783
- if (req.method === "POST" && pathname === "/engram/v1/dreams/run") {
2784
- this.enforceTokenOp("dreams_run");
2785
- const body = await this.readJsonBody(req);
2786
- const VALID_PHASES = ["lightSleep", "rem", "deepSleep"];
2787
- const phase = typeof body.phase === "string" ? body.phase : void 0;
2788
- if (!phase || !VALID_PHASES.includes(phase)) {
2789
- this.respondJson(res, 400, {
2790
- error: `phase is required and must be one of: ${VALID_PHASES.join(", ")}`
2791
- });
2792
- return;
2793
- }
2794
- if ("dryRun" in body && body.dryRun !== void 0 && typeof body.dryRun !== "boolean") {
2795
- this.respondJson(res, 400, {
2796
- error: "dryRun must be a boolean when provided"
2797
- });
2798
- return;
2799
- }
2800
- if ("namespace" in body && body.namespace !== void 0 && typeof body.namespace !== "string") {
2801
- this.respondJson(res, 400, {
2802
- error: "namespace must be a string when provided"
2803
- });
2804
- return;
2805
- }
2806
- const dryRun = body.dryRun === true;
2807
- const namespace = this.resolveNamespace(req, typeof body.namespace === "string" ? body.namespace : void 0);
2808
- if (!dryRun) {
2809
- this.ensureWriteRateLimitAvailable(req);
2810
- }
2811
- const result = await this.service.dreamsRun({
2812
- phase,
2813
- dryRun,
2814
- namespace,
2815
- authenticatedPrincipal: this.resolveRequestPrincipal(req)
2816
- });
2817
- if (this.shouldCountWriteRateLimit(result)) {
2818
- this.recordWriteRateLimitHit(req);
2819
- }
2820
- this.respondJson(res, 200, result);
2821
- return;
2822
- }
2823
- if (pathname.startsWith("/engram/v1/admin/scope/inspect")) {
2824
- if (req.method !== "GET" && req.method !== "POST") {
2825
- this.respondJson(res, 405, { error: "method_not_allowed", code: "method_not_allowed" });
2826
- return;
2827
- }
2828
- this.requireOperatorToken();
2829
- const principal = this.resolveRequestPrincipal(req);
2830
- let sessionKey;
2831
- let namespaceOverride;
2832
- let operation;
2833
- if (req.method === "GET") {
2834
- sessionKey = parsed.searchParams.get("session") ?? void 0;
2835
- namespaceOverride = parsed.searchParams.get("namespace") ?? void 0;
2836
- operation = parsed.searchParams.get("operation") ?? void 0;
2837
- } else {
2838
- const body = await this.readJsonBody(req);
2839
- sessionKey = typeof body.sessionKey === "string" ? body.sessionKey : void 0;
2840
- namespaceOverride = typeof body.namespace === "string" ? body.namespace : void 0;
2841
- operation = typeof body.operation === "string" ? body.operation : void 0;
2842
- }
2843
- const inspection = await this.service.adminInspectScope({
2844
- sessionKey: sessionKey && sessionKey.length > 0 ? sessionKey : void 0,
2845
- namespace: namespaceOverride && namespaceOverride.length > 0 ? namespaceOverride : void 0,
2846
- principalOverride: principal,
2847
- operation
2848
- });
2849
- this.respondJson(res, 200, inspection);
2850
- return;
2851
- }
2852
- if (pathname === "/engram/v1/admin/namespaces") {
2853
- if (req.method !== "GET") {
2854
- this.respondJson(res, 405, { error: "method_not_allowed", code: "method_not_allowed" });
2855
- return;
2856
- }
2857
- this.requireOperatorToken();
2858
- const kind = optionalNamespaceKindQueryParam(parsed.searchParams.get("kind"));
2859
- const discoveredBy = parsed.searchParams.get("discoveredBy");
2860
- const result = await this.service.adminListNamespaces({
2861
- ...kind ? { kind } : {},
2862
- ...discoveredBy ? { discoveredBy } : {}
2863
- });
2864
- this.respondJson(res, 200, result);
2865
- return;
2866
- }
2867
- if (pathname === "/engram/v1/admin/maintenance-health") {
2868
- if (req.method !== "GET") {
2869
- this.respondJson(res, 405, { error: "method_not_allowed", code: "method_not_allowed" });
2870
- return;
2871
- }
2872
- this.requireOperatorToken();
2873
- const report = await this.service.adminMaintenanceHealth();
2874
- this.respondJson(res, 200, report);
2875
- return;
2876
- }
2877
- if (pathname === "/engram/v1/admin/transcript-audit") {
2878
- if (req.method !== "GET") {
2879
- this.respondJson(res, 405, { error: "method_not_allowed", code: "method_not_allowed" });
2880
- return;
2881
- }
2882
- this.requireOperatorToken();
2883
- const report = await this.service.adminTranscriptAudit();
2884
- this.respondJson(res, 200, report);
2885
- return;
2886
- }
2887
- if (pathname === "/engram/v1/admin/promote") {
2888
- if (req.method !== "POST") {
2889
- this.respondJson(res, 405, { error: "method_not_allowed", code: "method_not_allowed" });
2890
- return;
2891
- }
2892
- this.requireOperatorToken();
2893
- const body = await this.readJsonBody(req);
2894
- if (typeof body.sourceMemoryId !== "string" || body.sourceMemoryId.trim().length === 0) {
2895
- this.respondJson(res, 400, {
2896
- error: "invalid_request",
2897
- code: "invalid_request",
2898
- message: "sourceMemoryId is required"
2899
- });
2900
- return;
2901
- }
2902
- if (typeof body.reason !== "string" || body.reason.trim().length === 0) {
2903
- this.respondJson(res, 400, {
2904
- error: "invalid_request",
2905
- code: "invalid_request",
2906
- message: "reason is required for promotion"
2907
- });
2908
- return;
2909
- }
2910
- if (!Array.isArray(body.targets) || body.targets.length === 0) {
2911
- this.respondJson(res, 400, {
2912
- error: "invalid_request",
2913
- code: "invalid_request",
2914
- message: "targets must be a non-empty array"
2915
- });
2916
- return;
2917
- }
2918
- const VALID_TARGETS = ["teamProject", "serverShared", "userProject", "userGlobal", "explicit"];
2919
- const targets = [];
2920
- for (const entry of body.targets) {
2921
- if (typeof entry !== "object" || entry === null || typeof entry.kind !== "string") {
2922
- this.respondJson(res, 400, {
2923
- error: "invalid_request",
2924
- code: "invalid_request",
2925
- message: "each target must be an object with a 'kind' string"
2926
- });
2927
- return;
2928
- }
2929
- if (!VALID_TARGETS.includes(entry.kind)) {
2930
- this.respondJson(res, 400, {
2931
- error: "invalid_request",
2932
- code: "invalid_request",
2933
- message: `target.kind must be one of: ${VALID_TARGETS.join(", ")}`
2934
- });
2935
- return;
2936
- }
2937
- targets.push({
2938
- kind: entry.kind,
2939
- namespace: typeof entry.namespace === "string" ? entry.namespace : void 0
2940
- });
2941
- }
2942
- try {
2943
- this.ensureWriteRateLimitAvailable(req);
2944
- const result = await this.service.adminPromoteMemory({
2945
- sourceMemoryId: body.sourceMemoryId,
2946
- namespace: typeof body.namespace === "string" ? body.namespace : void 0,
2947
- principal: this.resolveRequestPrincipal(req),
2948
- sessionKey: typeof body.sessionKey === "string" ? body.sessionKey : void 0,
2949
- targets,
2950
- reason: body.reason
2951
- });
2952
- this.recordWriteRateLimitHit(req);
2953
- this.respondJson(res, 200, result);
2954
- } catch (err) {
2955
- if (err instanceof EngramAccessInputError) {
2956
- this.respondJson(res, 400, {
2957
- error: "invalid_request",
2958
- code: "invalid_request",
2959
- message: err.message
2960
- });
2961
- return;
2962
- }
2963
- throw err;
2964
- }
2965
- return;
2966
- }
2967
- this.respondJson(res, 404, { error: "not_found", code: "not_found" });
2968
- }
2969
- createRequestAbortSignal(req, res) {
2970
- const controller = new AbortController();
2971
- const abort = () => {
2972
- if (!controller.signal.aborted) controller.abort();
2973
- };
2974
- req.once("aborted", abort);
2975
- res.once("close", () => {
2976
- if (!res.writableEnded) abort();
2977
- });
2978
- return controller.signal;
2979
- }
2980
- /**
2981
- * SSE handler for /engram/v1/graph/events.
2982
- *
2983
- * Lifecycle:
2984
- * 1. Write SSE headers (Content-Type: text/event-stream).
2985
- * 2. Register this response in `sseClients`.
2986
- * 3. Resolve the namespace from the request and subscribe to THAT
2987
- * namespace's graph event bus (Codex P1: in multi-namespace
2988
- * deployments each namespace has its own bus keyed by its storage
2989
- * dir — subscribing to the global root leaks events across tenants).
2990
- * 4. On each event, add to a 200 ms batch; flush batch as a single SSE frame.
2991
- * 5. Send heartbeat every 25 s.
2992
- * 6. On client disconnect (req "close"), clean up timers and unsubscribe.
2993
- * 7. Register the cleanup callback in `sseCleanupFns` so `stop()` can
2994
- * release the heartbeat interval and bus subscription even when the
2995
- * client never disconnects (Cursor review thread `access-http.ts:232`).
2996
- */
2997
- async handleGraphEventsSSE(req, res) {
2998
- const parsed = new URL2(req.url ?? "/", `http://${hostToUrlAuthority(this.host)}`);
2999
- const namespaceParam = parsed.searchParams.get("namespace");
3000
- const namespace = this.resolveNamespace(req, namespaceParam && namespaceParam.length > 0 ? namespaceParam : void 0);
3001
- const principal = this.resolveRequestPrincipal(req);
3002
- const memoryDir = await this.service.getMemoryDirForNamespace(namespace, principal);
3003
- res.writeHead(200, {
3004
- "content-type": "text/event-stream; charset=utf-8",
3005
- "cache-control": "no-cache, no-store, must-revalidate",
3006
- "connection": "keep-alive",
3007
- "x-accel-buffering": "no",
3008
- // prevent nginx buffering
3009
- "transfer-encoding": "chunked"
3010
- });
3011
- const writeSSE = (payload) => {
3012
- try {
3013
- res.write(`data: ${JSON.stringify(payload)}
3014
-
3015
- `);
3016
- } catch {
3017
- }
3018
- };
3019
- writeSSE({ type: "connected" });
3020
- this.sseClients.add(res);
3021
- const flushBatch = () => {
3022
- const batch = this.ssePendingBatches.get(res);
3023
- if (!batch || batch.length === 0) return;
3024
- this.ssePendingBatches.delete(res);
3025
- this.sseBatchTimers.delete(res);
3026
- writeSSE({ type: "batch", events: batch });
3027
- };
3028
- const unsubscribe = subscribeGraphEvents(memoryDir, (event) => {
3029
- let batch = this.ssePendingBatches.get(res);
3030
- if (!batch) {
3031
- batch = [];
3032
- this.ssePendingBatches.set(res, batch);
3033
- }
3034
- batch.push(event);
3035
- if (!this.sseBatchTimers.has(res)) {
3036
- this.sseBatchTimers.set(res, setTimeout(flushBatch, 200));
3037
- }
3038
- });
3039
- const heartbeatInterval = setInterval(() => {
3040
- writeSSE({ type: "heartbeat" });
3041
- }, 25e3);
3042
- const cleanup = () => {
3043
- clearInterval(heartbeatInterval);
3044
- const timer = this.sseBatchTimers.get(res);
3045
- if (timer !== void 0) {
3046
- clearTimeout(timer);
3047
- this.sseBatchTimers.delete(res);
3048
- }
3049
- this.ssePendingBatches.delete(res);
3050
- unsubscribe();
3051
- this.sseClients.delete(res);
3052
- this.sseCleanupFns.delete(cleanup);
3053
- try {
3054
- res.end();
3055
- } catch {
3056
- }
3057
- };
3058
- this.sseCleanupFns.add(cleanup);
3059
- req.once("close", cleanup);
3060
- req.once("error", cleanup);
3061
- }
3062
- async handleMcpRequest(req, res, abortSignal) {
3063
- const headerVersion = req.headers["mcp-protocol-version"];
3064
- if (typeof headerVersion === "string" && headerVersion.length > 0) {
3065
- if (!MCP_SUPPORTED_PROTOCOL_VERSIONS.includes(headerVersion)) {
3066
- this.respondJson(res, 400, {
3067
- jsonrpc: "2.0",
3068
- id: null,
3069
- error: {
3070
- code: -32e3,
3071
- message: `unsupported MCP-Protocol-Version: ${headerVersion}; supported: ${MCP_SUPPORTED_PROTOCOL_VERSIONS.join(", ")}`
3072
- }
3073
- });
3074
- return;
3075
- }
3076
- }
3077
- const body = await this.readJsonBody(req);
3078
- const request = body;
3079
- const toolName = toDottedRemnicName(typeof request.params?.name === "string" ? request.params.name : "");
3080
- const toolArgs = request.params?.arguments;
3081
- const dreamsRunDryRun = (toolName === "engram.dreams_run" || toolName === "remnic.dreams_run") && toolArgs !== null && typeof toolArgs === "object" && !Array.isArray(toolArgs) && toolArgs.dryRun === true;
3082
- const memoryActionApplyDryRun = (toolName === "engram.memory_action_apply" || toolName === "remnic.memory_action_apply") && toolArgs !== null && typeof toolArgs === "object" && !Array.isArray(toolArgs) && toolArgs.dryRun === true;
3083
- const toolArgsSubcommand = toolArgs !== null && typeof toolArgs === "object" && !Array.isArray(toolArgs) && "subcommand" in toolArgs ? toolArgs.subcommand : void 0;
3084
- const codingDecisionWrite = (toolName === "engram.coding_decision" || toolName === "remnic.coding_decision") && (toolArgsSubcommand === "record" || toolArgsSubcommand === "supersede");
3085
- const codingArchitectureWrite = (toolName === "engram.coding_architecture" || toolName === "remnic.coding_architecture") && toolArgsSubcommand === "refresh";
3086
- const CODEGRAPH_WRITE_TOOLS = /* @__PURE__ */ new Set([
3087
- "engram.codegraph_index",
3088
- "remnic.codegraph_index",
3089
- "engram.codegraph_delete_project",
3090
- "remnic.codegraph_delete_project",
3091
- "engram.codegraph_ingest_traces",
3092
- "remnic.codegraph_ingest_traces"
3093
- ]);
3094
- const isCodegraphManageAdr = toolName === "engram.codegraph_manage_adr" || toolName === "remnic.codegraph_manage_adr";
3095
- const codegraphWrite = CODEGRAPH_WRITE_TOOLS.has(toolName) || isCodegraphManageAdr && (toolArgsSubcommand === "record" || toolArgsSubcommand === "supersede");
3096
- const supportPassportQuotaLimitedWrite = this.isSupportPassportQuotaLimitedWriteTool(toolName);
3097
- const isMcpWrite = request.method === "tools/call" && (toolName === "engram.memory_store" || toolName === "remnic.memory_store" || toolName === "engram.suggestion_submit" || toolName === "remnic.suggestion_submit" || toolName === "engram.observe" || toolName === "remnic.observe" || toolName === "engram.lcm_compaction_flush" || toolName === "remnic.lcm_compaction_flush" || toolName === "engram.extraction_force_flush" || toolName === "remnic.extraction_force_flush" || toolName === "engram.lcm_compaction_record" || toolName === "remnic.lcm_compaction_record" || toolName === "engram.capsule_export" || toolName === "remnic.capsule_export" || toolName === "engram.capsule_import" || toolName === "remnic.capsule_import" || toolName === "engram.entity_synthesis_run" || toolName === "remnic.entity_synthesis_run" || !dreamsRunDryRun && (toolName === "engram.dreams_run" || toolName === "remnic.dreams_run") || !memoryActionApplyDryRun && (toolName === "engram.memory_action_apply" || toolName === "remnic.memory_action_apply") || (toolName === "engram.memory_correct_apply" || toolName === "remnic.memory_correct_apply" || toolName === "engram.memory_correct_plan" || toolName === "remnic.memory_correct_plan") || codingDecisionWrite || codingArchitectureWrite || codegraphWrite || supportPassportQuotaLimitedWrite);
3098
- const observeSelfEnforcesQuota = toolName === "engram.observe" || toolName === "remnic.observe";
3099
- const extractionForceFlushWrite = toolName === "engram.extraction_force_flush" || toolName === "remnic.extraction_force_flush";
3100
- let writeRateLimitRecorded = false;
3101
- let supportPassportQuota;
3102
- const recordCommittedMcpWrite = extractionForceFlushWrite ? () => {
3103
- if (writeRateLimitRecorded) return;
3104
- writeRateLimitRecorded = true;
3105
- this.recordWriteRateLimitHit(req);
3106
- } : supportPassportQuotaLimitedWrite ? () => {
3107
- if (writeRateLimitRecorded) return;
3108
- writeRateLimitRecorded = true;
3109
- supportPassportQuota?.commit();
3110
- supportPassportQuota = void 0;
3111
- } : void 0;
3112
- if (isMcpWrite && !observeSelfEnforcesQuota) {
3113
- if (supportPassportQuotaLimitedWrite) supportPassportQuota = this.reserveWriteRateLimitSlot(req);
3114
- else this.ensureWriteRateLimitAvailable(req);
3115
- }
3116
- const sessionId = (() => {
3117
- const raw = req.headers["mcp-session-id"];
3118
- return typeof raw === "string" ? raw.trim() : void 0;
3119
- })();
3120
- const mcpCorrelationId = correlationIdStore.getStore() ?? randomUUID();
3121
- const requestIdentity = this.resolveRequestIdentity(req);
3122
- if (request.method === "tools/call" && this.mcpServer.toolAcceptsNamespace(toolName)) {
3123
- const mcpExplicitNamespace = toolArgs !== null && typeof toolArgs === "object" && !Array.isArray(toolArgs) && typeof toolArgs.namespace === "string" && toolArgs.namespace.length > 0 ? toolArgs.namespace : void 0;
3124
- enforceNamespaceAllowList(
3125
- tokenCapabilityStore.getStore(),
3126
- mcpExplicitNamespace ?? requestIdentity.namespace,
3127
- this.service.configRef?.defaultNamespace
3128
- );
3129
- }
3130
- const response = await this.mcpServer.handleRequest(request, {
3131
- principalOverride: requestIdentity.principal,
3132
- namespaceOverride: requestIdentity.namespace,
3133
- sessionKeyOverride: requestIdentity.sessionKey,
3134
- sessionId,
3135
- correlationId: mcpCorrelationId,
3136
- enforceWriteQuota: observeSelfEnforcesQuota ? () => this.ensureWriteRateLimitAvailable(req) : void 0,
3137
- recordWriteCommit: recordCommittedMcpWrite,
3138
- sourceConnector: this.resolveConnector(req),
3139
- abortSignal
3140
- }).catch((error) => {
3141
- supportPassportQuota?.release();
3142
- throw error;
3143
- });
3144
- if (isMcpWrite && response !== null) {
3145
- const result = response.result;
3146
- const isError = result?.isError === true;
3147
- const structured = result?.structuredContent;
3148
- const isRejectedCodegraph = structured?.ok === false;
3149
- const counts = structured ? this.shouldCountWriteRateLimit(structured) : true;
3150
- if (!writeRateLimitRecorded && !isError && !isRejectedCodegraph && counts) {
3151
- if (supportPassportQuota) {
3152
- supportPassportQuota.commit();
3153
- supportPassportQuota = void 0;
3154
- } else {
3155
- this.recordWriteRateLimitHit(req);
3156
- }
3157
- writeRateLimitRecorded = true;
3158
- }
3159
- }
3160
- supportPassportQuota?.release();
3161
- if (abortSignal.aborted) {
3162
- throw isAbortError(abortSignal.reason) ? abortSignal.reason : abortError("HTTP client disconnected");
3163
- }
3164
- if (response === null) {
3165
- res.statusCode = 202;
3166
- res.end();
3167
- return;
3168
- }
3169
- const assignedSessionId = this.mcpServer.popInitSessionId(mcpCorrelationId);
3170
- if (assignedSessionId) {
3171
- res.setHeader("mcp-session-id", assignedSessionId);
3172
- }
3173
- this.respondJson(res, 200, response);
3174
- }
3175
- respondJson(res, status, payload) {
3176
- const body = JSON.stringify(payload, null, 2);
3177
- res.statusCode = status;
3178
- res.setHeader("content-type", "application/json; charset=utf-8");
3179
- res.setHeader("content-length", String(Buffer.byteLength(body)));
3180
- const cid = correlationIdStore.getStore();
3181
- if (cid) {
3182
- res.setHeader("x-request-id", cid);
3183
- }
3184
- res.end(body);
3185
- }
3186
- respondBinary(res, status, body, headers = {}) {
3187
- res.statusCode = status;
3188
- res.setHeader("content-type", "application/octet-stream");
3189
- res.setHeader("content-length", String(body.length));
3190
- for (const [key, value] of Object.entries(headers)) {
3191
- res.setHeader(key, value);
3192
- }
3193
- const cid = correlationIdStore.getStore();
3194
- if (cid) {
3195
- res.setHeader("x-request-id", cid);
3196
- }
3197
- res.end(body);
3198
- }
3199
- async handleAdminConsole(req, res, pathname) {
3200
- if (req.method !== "GET") return false;
3201
- if (pathname === "/remnic/ui" || pathname === "/engram/ui") {
3202
- res.statusCode = 301;
3203
- res.setHeader("location", pathname + "/");
3204
- res.end();
3205
- return true;
3206
- }
3207
- if (pathname === "/remnic/ui/relay" || pathname === "/engram/ui/relay") {
3208
- res.statusCode = 301;
3209
- res.setHeader("location", pathname + "/");
3210
- res.end();
3211
- return true;
3212
- }
3213
- if (pathname === "/remnic/ui/" || pathname === "/engram/ui/") {
3214
- await this.respondAdminConsoleShell(req, res, pathname);
3215
- return true;
3216
- }
3217
- if (pathname === "/remnic/ui/relay/" || pathname === "/engram/ui/relay/") {
3218
- await this.respondAdminConsoleShell(req, res, pathname, "relay/index.html");
3219
- return true;
3220
- }
3221
- if (pathname === "/remnic/ui/app.js" || pathname === "/engram/ui/app.js") {
3222
- await this.respondStatic(res, path2.join(this.adminConsolePublicDir, "app.js"), "application/javascript; charset=utf-8");
3223
- return true;
3224
- }
3225
- const relayAsset = RELAY_ADMIN_CONSOLE_ASSETS.get(pathname.split("/").at(-1) ?? "");
3226
- const isRelayAsset = pathname.startsWith("/remnic/ui/relay/") || pathname.startsWith("/engram/ui/relay/");
3227
- if (isRelayAsset && relayAsset && pathname.split("/").length === 5) {
3228
- const fileName = pathname.split("/").at(-1);
3229
- if (fileName) {
3230
- await this.respondStatic(res, path2.join(this.adminConsolePublicDir, "relay", fileName), relayAsset);
3231
- return true;
3232
- }
3233
- }
3234
- return false;
3235
- }
3236
- async respondAdminConsoleShell(req, res, pathname, relativePath = "index.html", allowPrefill = true) {
3237
- try {
3238
- let body = await readFile(path2.join(this.adminConsolePublicDir, relativePath), "utf-8");
3239
- const matched = this.resolveAuthorizedEntry(req, pathname);
3240
- const canPrefillToken = allowPrefill && this.adminConsolePrefillToken && matched !== null && matched.capabilities === void 0 && this.timingSafeStringEqual(matched.token, this.adminConsolePrefillToken);
3241
- if (canPrefillToken) {
3242
- const serializedToken = serializeInlineScriptValue(this.adminConsolePrefillToken);
3243
- const script = `<script>(function(token,script){const key="__REMNIC_ADMIN_CONSOLE_PREFILL_TOKEN__";const clear=function(){token="";try{delete window[key]}catch{window[key]=""}};window.addEventListener("pagehide",clear,{once:true});window.addEventListener("beforeunload",clear,{once:true});try{if(new URLSearchParams(location.hash.slice(1)).has("secret")){clear();return}Object.defineProperty(window,key,{configurable:true,get:function(){const value=token;clear();return value}})}finally{if(script){script.textContent="";script.remove()}}})(${serializedToken},document.currentScript);</script>`;
3244
- body = body.includes("</head>") ? body.replace("</head>", `${script}</head>`) : `${script}${body}`;
3245
- res.setHeader("cache-control", "private, no-store");
3246
- }
3247
- res.setHeader("vary", "authorization");
3248
- res.statusCode = 200;
3249
- res.setHeader("content-type", "text/html; charset=utf-8");
3250
- res.setHeader("content-length", String(Buffer.byteLength(body)));
3251
- res.end(body);
3252
- } catch {
3253
- this.respondJson(res, 404, { error: "not_found" });
3254
- }
3255
- }
3256
- async respondStatic(res, filePath, contentType) {
3257
- try {
3258
- const body = await readFile(filePath, "utf-8");
3259
- res.statusCode = 200;
3260
- res.setHeader("content-type", contentType);
3261
- res.setHeader("content-length", String(Buffer.byteLength(body)));
3262
- res.end(body);
3263
- } catch {
3264
- this.respondJson(res, 404, { error: "not_found" });
3265
- }
3266
- }
3267
- async readJsonBody(req, maxBodyBytes = this.maxBodyBytes) {
3268
- const encoding = (this.readOptionalHeader(req, "content-encoding") ?? "identity").toLowerCase();
3269
- if (encoding !== "identity" && encoding !== "gzip") {
3270
- throw new HttpError(415, "unsupported_content_encoding", "unsupported_content_encoding");
3271
- }
3272
- const chunks = [];
3273
- let total = 0;
3274
- for await (const chunk of req) {
3275
- const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
3276
- total += buffer.length;
3277
- if (total > maxBodyBytes) {
3278
- throw new HttpError(413, "request_body_too_large", "request_body_too_large");
3279
- }
3280
- chunks.push(buffer);
3281
- }
3282
- if (chunks.length === 0) return {};
3283
- let body = Buffer.concat(chunks, total);
3284
- if (encoding === "gzip") {
3285
- try {
3286
- body = gunzipSync(body, { maxOutputLength: maxBodyBytes });
3287
- } catch (error) {
3288
- if (error.code === "ERR_BUFFER_TOO_LARGE") {
3289
- throw new HttpError(413, "request_body_too_large", "request_body_too_large");
3290
- }
3291
- throw new HttpError(400, "invalid_gzip_body", "invalid_gzip_body");
3292
- }
3293
- if (body.byteLength > maxBodyBytes) {
3294
- throw new HttpError(413, "request_body_too_large", "request_body_too_large");
3295
- }
3296
- }
3297
- const raw = body.toString("utf-8").trim();
3298
- if (raw.length === 0) return {};
3299
- let parsed;
3300
- try {
3301
- parsed = JSON.parse(raw);
3302
- } catch {
3303
- throw new HttpError(400, "invalid_json", "invalid_json");
3304
- }
3305
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
3306
- throw new HttpError(400, "invalid_json_object", "invalid_json_object");
3307
- }
3308
- return parsed;
3309
- }
3310
- async readBinaryBody(req, maxBytes) {
3311
- const chunks = [];
3312
- let total = 0;
3313
- for await (const chunk of req) {
3314
- const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
3315
- total += buffer.length;
3316
- if (total > maxBytes) {
3317
- throw new HttpError(413, "request_body_too_large", "request_body_too_large");
3318
- }
3319
- chunks.push(buffer);
3320
- }
3321
- return Buffer.concat(chunks, total);
3322
- }
3323
- readRequiredHeader(req, name) {
3324
- const value = this.readOptionalHeader(req, name);
3325
- if (value === void 0 || value.length === 0) {
3326
- throw new EngramAccessInputError(`${name} header is required`);
3327
- }
3328
- return value;
3329
- }
3330
- readOptionalHeader(req, name) {
3331
- const raw = req.headers[name.toLowerCase()];
3332
- if (Array.isArray(raw)) return raw[0]?.trim() || void 0;
3333
- return raw?.trim() || void 0;
3334
- }
3335
- readRequiredDecodedHeader(req, name) {
3336
- const raw = this.readRequiredHeader(req, name);
3337
- try {
3338
- return decodeURIComponent(raw);
3339
- } catch {
3340
- throw new EngramAccessInputError(`${name} header is not valid percent-encoded input`);
3341
- }
3342
- }
3343
- readRequiredIntegerHeader(req, name) {
3344
- const raw = this.readRequiredHeader(req, name);
3345
- const parsed = Number(raw);
3346
- if (!Number.isInteger(parsed) || parsed < 0) {
3347
- throw new EngramAccessInputError(`${name} header must be a non-negative integer`);
3348
- }
3349
- return parsed;
3350
- }
3351
- readOptionalIntegerHeader(req, name) {
3352
- const raw = this.readOptionalHeader(req, name);
3353
- if (raw === void 0) return void 0;
3354
- const parsed = Number(raw);
3355
- if (!Number.isInteger(parsed) || parsed < 0) {
3356
- throw new EngramAccessInputError(`${name} header must be a non-negative integer`);
3357
- }
3358
- return parsed;
3359
- }
3360
- readRequiredNumberHeader(req, name) {
3361
- const raw = this.readRequiredHeader(req, name);
3362
- const parsed = Number(raw);
3363
- if (!Number.isFinite(parsed) || parsed < 0) {
3364
- throw new EngramAccessInputError(`${name} header must be a non-negative finite number`);
3365
- }
3366
- return parsed;
3367
- }
3368
- parseOptionalBooleanHeader(req, name, defaultValue) {
3369
- const raw = this.readOptionalHeader(req, name);
3370
- if (raw === void 0) return defaultValue;
3371
- if (raw === "true") return true;
3372
- if (raw === "false") return false;
3373
- throw new EngramAccessInputError(`${name} header must be one of: true, false`);
3374
- }
3375
- lifecycleFlushDeps(req, res) {
3376
- const readValidatedBody = ((schemaName) => this.readValidatedBody(req, schemaName));
3377
- return {
3378
- service: this.service,
3379
- defaultNamespace: this.service.configRef?.defaultNamespace,
3380
- enforceTokenOp: (op) => this.enforceTokenOp(op),
3381
- readValidatedBody,
3382
- ensureWriteRateLimitAvailable: () => this.ensureWriteRateLimitAvailable(req),
3383
- resolveNamespace: (namespace) => this.resolveNamespace(req, namespace),
3384
- resolveRequestPrincipal: () => this.resolveRequestPrincipal(req),
3385
- recordWriteRateLimitHit: () => this.recordWriteRateLimitHit(req),
3386
- respondJson: (payload) => this.respondJson(res, 200, payload)
3387
- };
3388
- }
3389
- async readValidatedBody(req, schemaName, maxBodyBytes) {
3390
- const raw = await this.readJsonBody(req, maxBodyBytes);
3391
- const result = validateRequest(schemaName, raw);
3392
- if (!result.success) {
3393
- throw new HttpError(400, result.error.error, "validation_error", result.error.details);
3394
- }
3395
- return result.data;
3396
- }
3397
- /**
3398
- * Build the WWW-Authenticate challenge string for 401 responses.
3399
- * When `resourceMetadataUrl` is configured, includes the RFC 9728
3400
- * `resource_metadata` parameter so MCP clients (e.g. ChatGPT) can
3401
- * discover the OAuth 2.0 protected-resource metadata document.
3402
- * Otherwise the bare `Bearer` challenge is returned (unchanged).
3403
- */
3404
- bearerChallenge() {
3405
- if (this.resourceMetadataUrl) {
3406
- return `Bearer resource_metadata="${this.resourceMetadataUrl}"`;
3407
- }
3408
- return "Bearer";
3409
- }
3410
- isAuthorized(req, pathname) {
3411
- return this.resolveAuthorizedEntry(req, pathname) !== null;
3412
- }
3413
- /**
3414
- * Per-request cache of the matched token entry (the entry whose token
3415
- * matched the bearer credential, BEFORE any path-policy check). Pinning
3416
- * the entry to the first read means authorization, capability
3417
- * enforcement, and connector provenance all observe the SAME token-store
3418
- * snapshot — a rotation between the auth check and the write handler can
3419
- * no longer stamp the wrong connector (PR #1852 race).
3420
- */
3421
- matchedEntryCache = /* @__PURE__ */ new WeakMap();
3422
- /**
3423
- * Resolve the matched token entry for this request, caching the result so
3424
- * every downstream consumer (authorization, capability enforcement,
3425
- * connector provenance) sees the same snapshot entry (PR #1852).
3426
- */
3427
- resolveMatchedEntry(req, pathname) {
3428
- if (this.matchedEntryCache.has(req)) {
3429
- return this.matchedEntryCache.get(req) ?? null;
3430
- }
3431
- const entry = this.computeMatchedEntry(req, pathname);
3432
- this.matchedEntryCache.set(req, entry);
3433
- return entry;
3434
- }
3435
- /**
3436
- * Find the entry whose token matches the request's bearer credential.
3437
- * Static operator tokens (`authToken`/`authTokens`) and the string-token
3438
- * getter carry no capabilities ⇒ unrestricted. Dynamic entry-based tokens
3439
- * capture connector identity and capabilities. A corrupt or unreadable
3440
- * token store returns null rather than propagating an error — provenance
3441
- * must never block an already-authenticated request.
3442
- */
3443
- computeMatchedEntry(req, pathname) {
3444
- if (!this.authToken && this.authTokens.length === 0 && !this.authTokensGetter && !this.authTokenEntriesGetter) {
3445
- return null;
3446
- }
3447
- const raw = req.headers.authorization;
3448
- let candidate = null;
3449
- if (raw) {
3450
- const separator = raw.indexOf(" ");
3451
- if (separator > 0) {
3452
- const scheme = raw.slice(0, separator).toLowerCase();
3453
- if (scheme === "bearer") {
3454
- candidate = raw.slice(separator + 1).trim();
3455
- }
3456
- }
3457
- }
3458
- if (!candidate && pathname === "/engram/v1/graph/events") {
3459
- try {
3460
- const parsed = new URL2(req.url ?? "/", `http://${hostToUrlAuthority(this.host)}`);
3461
- const queryToken = parsed.searchParams.get("token");
3462
- if (queryToken && queryToken.length > 0) {
3463
- candidate = queryToken;
3464
- }
3465
- } catch {
3466
- }
3467
- }
3468
- if (!candidate) return null;
3469
- const token = candidate;
3470
- if (this.authToken && this.timingSafeStringEqual(token, this.authToken)) {
3471
- return { token };
3472
- }
3473
- for (const valid of this.authTokens) {
3474
- if (this.timingSafeStringEqual(token, valid)) return { token: valid };
3475
- }
3476
- if (this.authTokenEntriesGetter) {
3477
- try {
3478
- for (const entry of this.authTokenEntriesGetter()) {
3479
- if (!this.timingSafeStringEqual(token, entry.token)) continue;
3480
- return { token: entry.token, connector: entry.connector, capabilities: entry.capabilities };
3481
- }
3482
- } catch {
3483
- }
3484
- return null;
3485
- }
3486
- if (this.authTokensGetter) {
3487
- for (const valid of this.authTokensGetter()) {
3488
- if (this.timingSafeStringEqual(token, valid)) return { token: valid };
3489
- }
3490
- }
3491
- return null;
3492
- }
3493
- /**
3494
- * Resolve the presenting token's entry for an authorized request, or null.
3495
- * Applies the path-policy gate (if configured) on top of the per-request
3496
- * cached matched entry so the identity that is authorized is the same one
3497
- * whose connector later stamps frontmatter. Static operator tokens and the
3498
- * string-token getter carry no capabilities ⇒ unrestricted.
3499
- */
3500
- resolveAuthorizedEntry(req, pathname) {
3501
- const matched = this.resolveMatchedEntry(req, pathname);
3502
- if (!matched) return null;
3503
- if ("connector" in matched && this.tokenPathPolicy) {
3504
- if (typeof matched.connector !== "string" || matched.connector.length === 0) return null;
3505
- return this.tokenPathPolicy(matched.connector, pathname) ? matched : null;
3506
- }
3507
- return matched;
3508
- }
3509
- /**
3510
- * Resolve the presenting token's capabilities for this request, or
3511
- * undefined when the token is unrestricted (legacy / static operator /
3512
- * explicit-unrestricted record). Reads from the per-request cached matched
3513
- * entry so capabilities come from the same snapshot as authorization and
3514
- * connector provenance (PR #1852).
3515
- */
3516
- resolveTokenCapabilities(req, pathname) {
3517
- return this.resolveMatchedEntry(req, pathname)?.capabilities;
3518
- }
3519
- /**
3520
- * Enforce the presenting token's ops allow-list for an HTTP route that
3521
- * dispatches directly to the service (not through the boundary). Reads the
3522
- * per-request capability context set in `handle`; unrestricted tokens
3523
- * pass. Throws {@link EngramAccessForbiddenError} (→ 403) when a scoped
3524
- * token calls a non-permitted op (issue #1837).
3525
- */
3526
- enforceTokenOp(op) {
3527
- void getOperation(op);
3528
- assertOperationAllowed(tokenCapabilityStore.getStore(), op);
3529
- }
3530
- enforceTokenAnyOf(ops) {
3531
- for (const op of ops) void getOperation(op);
3532
- const capabilities = tokenCapabilityStore.getStore();
3533
- if (ops.some((op) => capabilityAllowsOp(capabilities, op))) return;
3534
- throw new EngramAccessForbiddenError(
3535
- `token is not permitted to call any required operation: ${ops.join(", ")}`
3536
- );
3537
- }
3538
- /**
3539
- * Operator/admin routes are unrestricted-only surfaces: a scoped
3540
- * (least-privileged) token must NOT reach them (issue #1837). Legacy and
3541
- * explicit-unrestricted tokens pass. Throws EngramAccessForbiddenError (→403).
3542
- */
3543
- requireOperatorToken() {
3544
- if (isCapabilityRestricted(tokenCapabilityStore.getStore())) {
3545
- throw new EngramAccessForbiddenError(
3546
- "token is scoped and may not access operator/admin routes"
3547
- );
3548
- }
3549
- }
3550
- /**
3551
- * Resolve the connector identity for the request's bearer token (Phase 1
3552
- * provenance). Reuses the per-request cached matched entry so a token
3553
- * rotation between the auth check and the write handler cannot stamp the
3554
- * wrong connector (PR #1852 race). Returns `undefined` for operator-
3555
- * supplied static tokens or when no entry getter is configured — those
3556
- * writes are operator-initiated.
3557
- */
3558
- resolveConnector(req) {
3559
- return this.resolveMatchedEntry(req)?.connector;
3560
- }
3561
- timingSafeStringEqual(a, b) {
3562
- const left = this.encodeSecret(a);
3563
- const right = this.encodeSecret(b);
3564
- if (!left || !right) return false;
3565
- return timingSafeEqual(left, right);
3566
- }
3567
- encodeSecret(value) {
3568
- const encoded = Buffer.from(value, "utf-8");
3569
- if (encoded.length > 1024) return null;
3570
- const out = Buffer.alloc(2 + 1024);
3571
- out.writeUInt16BE(encoded.length, 0);
3572
- encoded.copy(out, 2);
3573
- return out;
3574
- }
3575
- writeResponseStatus(response) {
3576
- if (response.dryRun === true) return 200;
3577
- if (response.status === "stored" || response.status === "queued_for_review") return 201;
3578
- return 200;
3579
- }
3580
- /**
3581
- * Admit a quota-counted write by RESERVING a limiter slot for the request
3582
- * (issue #3225 C4). A bare hasCapacity check admits N concurrent requests
3583
- * against the same committed count; the reservation holds in-flight
3584
- * capacity until the write commits at recordWriteRateLimitHit or the
3585
- * request ends. Idempotent per request, so a quota hook that fires after
3586
- * an earlier reservation is a no-op.
3587
- */
3588
- ensureWriteRateLimitAvailable(req) {
3589
- if (!req) {
3590
- if (!this.writeLimiter.hasCapacity()) {
3591
- throw new HttpError(429, "write_rate_limited", "write_rate_limited");
3592
- }
3593
- return;
3594
- }
3595
- if (this.pendingWriteQuotas.has(req)) return;
3596
- const reservation = this.writeLimiter.reserve(this.principalForRateLimit(req));
3597
- if (!reservation) {
3598
- throw new HttpError(429, "write_rate_limited", "write_rate_limited");
3599
- }
3600
- this.pendingWriteQuotas.set(req, reservation);
3601
- }
3602
- /** Commit a counted write at the durable boundary; consumes the request's reservation. */
3603
- recordWriteRateLimitHit(req) {
3604
- const pending = req ? this.pendingWriteQuotas.get(req) : void 0;
3605
- if (pending && req) {
3606
- this.pendingWriteQuotas.delete(req);
3607
- pending.commit();
3608
- return;
3609
- }
3610
- this.writeLimiter.record(this.principalForRateLimit(req));
3611
- }
3612
- /** Release an uncommitted reservation after a rejection, failure, or exempt outcome. */
3613
- releasePendingWriteQuota(req) {
3614
- const pending = this.pendingWriteQuotas.get(req);
3615
- if (!pending) return;
3616
- this.pendingWriteQuotas.delete(req);
3617
- pending.release();
3618
- }
3619
- reserveWriteRateLimitSlot(req) {
3620
- const reservation = this.writeLimiter.reserve(this.principalForRateLimit(req));
3621
- if (!reservation) {
3622
- throw new HttpError(429, "write_rate_limited", "write_rate_limited");
3623
- }
3624
- return reservation;
3625
- }
3626
- principalForRateLimit(req) {
3627
- if (!req) return void 0;
3628
- return this.resolveRequestPrincipal(req) ?? this.resolveConnector(req);
3629
- }
3630
- shouldCountWriteRateLimit(response) {
3631
- return response.dryRun !== true && response.idempotencyReplay !== true;
3632
- }
3633
- /** Map wearables validation errors to 400 (body lives in wearables/http-glue.ts). */
3634
- respondWearablesError(res, err) {
3635
- return respondWearablesErrorGlue(this.respondJson.bind(this), res, err);
3636
- }
3637
- };
3638
-
3639
- export {
3640
- MeetingsInputError,
3641
- resolveDefaultAdminConsolePublicDir,
3642
- EngramAccessHttpServer
3643
- };
3644
- //# sourceMappingURL=chunk-DKAA2IHQ.js.map