@remnic/core 10.0.49 → 10.0.50

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 (338) hide show
  1. package/dist/access-admin-ops-surface.js +41 -40
  2. package/dist/access-authorization-probe.js +42 -41
  3. package/dist/access-boundary.js +41 -40
  4. package/dist/access-cli.js +107 -106
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +41 -40
  7. package/dist/access-http-briefing.d.ts +2 -0
  8. package/dist/access-http-briefing.d.ts.map +1 -1
  9. package/dist/access-http-briefing.js +42 -41
  10. package/dist/access-http-query.js +43 -42
  11. package/dist/access-http.d.ts.map +1 -1
  12. package/dist/access-http.js +59 -58
  13. package/dist/access-identity-continuity-surface.js +41 -40
  14. package/dist/access-lcm-surface.js +41 -40
  15. package/dist/access-mcp.js +51 -50
  16. package/dist/access-namespace-preflight.js +41 -40
  17. package/dist/access-observe-write-category.d.ts +1 -1
  18. package/dist/access-observe-write-category.d.ts.map +1 -1
  19. package/dist/access-observe-write-category.js +10 -10
  20. package/dist/access-observe-write-surface.d.ts.map +1 -1
  21. package/dist/access-observe-write-surface.js +41 -40
  22. package/dist/access-offline-manifest.js +4 -4
  23. package/dist/access-operations-batch.js +42 -41
  24. package/dist/access-operations.js +49 -48
  25. package/dist/access-recall-concurrency.js +41 -40
  26. package/dist/access-recall-response.js +41 -40
  27. package/dist/access-recall-surface.js +41 -40
  28. package/dist/access-schema.js +6 -6
  29. package/dist/access-service.d.ts +5 -0
  30. package/dist/access-service.d.ts.map +1 -1
  31. package/dist/access-service.js +41 -40
  32. package/dist/access-surface-catalog.js +43 -42
  33. package/dist/access-surface-catalog.js.map +1 -1
  34. package/dist/active-recall.js +14 -14
  35. package/dist/{auto-sync-BNCBMU6H.js → auto-sync-UQQ6YJSB.js} +3 -3
  36. package/dist/briefing-deadline.d.ts +7 -0
  37. package/dist/briefing-deadline.d.ts.map +1 -0
  38. package/dist/briefing-deadline.js +13 -0
  39. package/dist/briefing-window.d.ts +12 -4
  40. package/dist/briefing-window.d.ts.map +1 -1
  41. package/dist/briefing-window.js +3 -1
  42. package/dist/briefing.d.ts +2 -0
  43. package/dist/briefing.d.ts.map +1 -1
  44. package/dist/briefing.js +12 -11
  45. package/dist/buffer.js +3 -3
  46. package/dist/calibration.js +2 -2
  47. package/dist/{capsule-merge-RNQUP2VL.js → capsule-merge-D4BTIJGK.js} +3 -3
  48. package/dist/causal-consolidation.js +11 -11
  49. package/dist/{chunk-ESDOQEJM.js → chunk-2AKIVSHP.js} +2 -2
  50. package/dist/{chunk-MCS4LIXP.js → chunk-43TUWQNR.js} +2 -2
  51. package/dist/{chunk-2NLCDNGA.js → chunk-564YP3DW.js} +2 -2
  52. package/dist/{chunk-PBWJVRKE.js → chunk-5FUDSK6Z.js} +2 -2
  53. package/dist/{chunk-V4GPFCJ7.js → chunk-5G53JSRO.js} +2 -2
  54. package/dist/{chunk-WJCAAVZX.js → chunk-5X2H3X4J.js} +2 -2
  55. package/dist/{chunk-CPBYKOTC.js → chunk-6ZQX3R66.js} +2 -2
  56. package/dist/{chunk-AF4UIHON.js → chunk-74AY3S7U.js} +9 -9
  57. package/dist/{chunk-XSJL62LE.js → chunk-7J6A5MV2.js} +2 -2
  58. package/dist/{chunk-A3WXV7YU.js → chunk-7SAE6T36.js} +2 -2
  59. package/dist/{chunk-BWF3KNSQ.js → chunk-7YVER7ZO.js} +2 -2
  60. package/dist/{chunk-5IDQVE4U.js → chunk-A3UFV4K3.js} +6 -6
  61. package/dist/{chunk-LWRIGKFM.js → chunk-A5L5KGDI.js} +19 -1
  62. package/dist/chunk-A5L5KGDI.js.map +1 -0
  63. package/dist/{chunk-6TIVVOVU.js → chunk-BNJAVRNJ.js} +9 -9
  64. package/dist/{chunk-CMOYD5CA.js → chunk-CCL2OXFO.js} +2 -2
  65. package/dist/{chunk-7XFQSQYS.js → chunk-CRN3EPE2.js} +4 -3
  66. package/dist/chunk-CRN3EPE2.js.map +1 -0
  67. package/dist/{chunk-3NYIXVCT.js → chunk-DMKTOCC7.js} +2 -2
  68. package/dist/chunk-DMKTOCC7.js.map +1 -0
  69. package/dist/{chunk-FRXS6SC7.js → chunk-EKUI7I2X.js} +20 -4
  70. package/dist/chunk-EKUI7I2X.js.map +1 -0
  71. package/dist/{chunk-N6EM7ESC.js → chunk-EKYWZYZJ.js} +22 -14
  72. package/dist/chunk-EKYWZYZJ.js.map +1 -0
  73. package/dist/{chunk-FOKTW6GY.js → chunk-EWBXMGXR.js} +2 -2
  74. package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
  75. package/dist/{chunk-6W3QH3QW.js → chunk-GQIYL7OD.js} +388 -292
  76. package/dist/chunk-GQIYL7OD.js.map +1 -0
  77. package/dist/{chunk-E47UQBYW.js → chunk-GXVXBYRA.js} +3 -3
  78. package/dist/{chunk-RZDDNH63.js → chunk-HKOMHP6D.js} +27 -15
  79. package/dist/chunk-HKOMHP6D.js.map +1 -0
  80. package/dist/{chunk-AGCEJDG2.js → chunk-HPNXS6W6.js} +2 -2
  81. package/dist/{chunk-O4CA53UY.js → chunk-IMKOJVD4.js} +69 -63
  82. package/dist/chunk-IMKOJVD4.js.map +1 -0
  83. package/dist/{chunk-N5PISU26.js → chunk-IN6U2BS5.js} +2 -2
  84. package/dist/{chunk-HI53EK3Y.js → chunk-IUYXCTA7.js} +2 -2
  85. package/dist/{chunk-VGRBKEBH.js → chunk-JBWD3J5U.js} +3 -3
  86. package/dist/{chunk-YB3WN6XA.js → chunk-K5QQXVG6.js} +12 -12
  87. package/dist/{chunk-7LCGFUMB.js → chunk-KQAFEZQX.js} +4 -4
  88. package/dist/{chunk-HDT3DMUF.js → chunk-KW5YZELC.js} +2 -2
  89. package/dist/{chunk-DM6SOROF.js → chunk-LIBVANOF.js} +2 -2
  90. package/dist/{chunk-VKUMKVEU.js → chunk-LMMBV7QC.js} +4 -4
  91. package/dist/{chunk-NMOEOQV4.js → chunk-M2DOJBH7.js} +2 -2
  92. package/dist/{chunk-3INLTKTW.js → chunk-M4HRYLOA.js} +2 -2
  93. package/dist/{chunk-YY6PWFUC.js → chunk-M6TP6UYD.js} +3 -3
  94. package/dist/{chunk-BJTFLP3J.js → chunk-MLNXATFG.js} +2 -2
  95. package/dist/{chunk-C7WDWD35.js → chunk-MWURUSRE.js} +5 -5
  96. package/dist/{chunk-WIBWONL4.js → chunk-N74WJ7B2.js} +2 -2
  97. package/dist/{chunk-LCHFINL4.js → chunk-N7FNCE7F.js} +2 -2
  98. package/dist/{chunk-QZK6HG4J.js → chunk-NPLM7AFU.js} +2 -2
  99. package/dist/{chunk-CRJX4OMN.js → chunk-NSP5PIQH.js} +2 -2
  100. package/dist/{chunk-X52SXKVC.js → chunk-P7OVFGMU.js} +2 -2
  101. package/dist/{chunk-2J6BIOVN.js → chunk-PJCVI44H.js} +2 -2
  102. package/dist/{chunk-Q6PYCPHO.js → chunk-Q3PNQYAC.js} +2 -2
  103. package/dist/{chunk-D76AHITY.js → chunk-QS765SBL.js} +2 -2
  104. package/dist/{chunk-KNCR7APE.js → chunk-QTDZ2NYC.js} +7 -7
  105. package/dist/{chunk-YFXRLRKP.js → chunk-S4OZKPBS.js} +7 -11
  106. package/dist/{chunk-YFXRLRKP.js.map → chunk-S4OZKPBS.js.map} +1 -1
  107. package/dist/{chunk-HCPVL6SF.js → chunk-SJTSWG2Z.js} +3 -3
  108. package/dist/{chunk-SA6P2KS3.js → chunk-TEH5MDX5.js} +33 -17
  109. package/dist/chunk-TEH5MDX5.js.map +1 -0
  110. package/dist/{chunk-XFND63OB.js → chunk-TFWJXWL6.js} +2 -2
  111. package/dist/{chunk-SVJ5JVZO.js → chunk-TXKQBQYW.js} +3 -3
  112. package/dist/{chunk-WQFHHJWK.js → chunk-UALXWWGN.js} +2 -2
  113. package/dist/{chunk-3PVKV3XL.js → chunk-UNOAVHV4.js} +2 -2
  114. package/dist/{chunk-FYZXZUGU.js → chunk-UQBOPCDK.js} +2 -2
  115. package/dist/{chunk-SUKPM6DV.js → chunk-UWMZT2TY.js} +47 -4
  116. package/dist/chunk-UWMZT2TY.js.map +1 -0
  117. package/dist/{chunk-OD7ZXCUF.js → chunk-UXGXJUZ5.js} +7 -7
  118. package/dist/{chunk-NH37SP6V.js → chunk-UZVQFGAY.js} +81 -78
  119. package/dist/chunk-UZVQFGAY.js.map +1 -0
  120. package/dist/{chunk-2BY56HE6.js → chunk-VCOMACR6.js} +35 -35
  121. package/dist/{chunk-5FNZ3G6X.js → chunk-VKQI47DJ.js} +4 -4
  122. package/dist/{chunk-MPJDQC4G.js → chunk-WJCXUE2J.js} +2 -2
  123. package/dist/{chunk-C3HRYVGD.js → chunk-WV4UYDP6.js} +2 -2
  124. package/dist/{chunk-E3DL3CAL.js → chunk-WXJC2L66.js} +2 -2
  125. package/dist/chunk-X2KO4FK5.js +40 -0
  126. package/dist/chunk-X2KO4FK5.js.map +1 -0
  127. package/dist/{chunk-O7GNSHFF.js → chunk-XC3H7KON.js} +2 -2
  128. package/dist/{chunk-OOOX5C4X.js → chunk-XL2EE5W3.js} +4 -4
  129. package/dist/{chunk-NUK5XU3P.js → chunk-XTXWR6KQ.js} +25 -10
  130. package/dist/chunk-XTXWR6KQ.js.map +1 -0
  131. package/dist/{chunk-CZVNOTLR.js → chunk-XXR4A33N.js} +2 -2
  132. package/dist/{chunk-2AV2U2SZ.js → chunk-YDIOMDBW.js} +2 -2
  133. package/dist/{chunk-QFTNWDJ6.js → chunk-YECEEJUU.js} +2 -2
  134. package/dist/{chunk-BMG3SAXA.js → chunk-Z5277JWF.js} +3 -3
  135. package/dist/{chunk-SW3WHVKN.js → chunk-ZFNXJEUG.js} +2 -2
  136. package/dist/{chunk-VY6BD5KB.js → chunk-ZMO2S5RT.js} +12 -10
  137. package/dist/chunk-ZMO2S5RT.js.map +1 -0
  138. package/dist/{chunk-KMTMMBEB.js → chunk-ZNRTHYYW.js} +6 -6
  139. package/dist/{chunk-VCBS5GRQ.js → chunk-ZSMA67FV.js} +2 -2
  140. package/dist/cli.js +80 -79
  141. package/dist/coding/export-okf-codegraph.js +9 -9
  142. package/dist/compounding/engine.js +11 -11
  143. package/dist/config.js +14 -14
  144. package/dist/connectors/codex-materialize-runner.js +10 -10
  145. package/dist/connectors/index.js +10 -10
  146. package/dist/consolidation-provenance-check.js +4 -4
  147. package/dist/consolidation-undo.js +2 -2
  148. package/dist/convergence-refresh.js +12 -12
  149. package/dist/corpus-watermark.js +12 -12
  150. package/dist/correction/passive-correction-notifications.d.ts +1 -1
  151. package/dist/correction/passive-correction-notifications.d.ts.map +1 -1
  152. package/dist/dashboard-runtime.js +2 -2
  153. package/dist/deep-recall-http-glue.js +7 -7
  154. package/dist/{entity-canonical-id-migration-2YOLMOFC.js → entity-canonical-id-migration-ZBNRGUNV.js} +3 -3
  155. package/dist/entity-canonical-id-migration-ZBNRGUNV.js.map +1 -0
  156. package/dist/entity-retrieval.js +11 -11
  157. package/dist/explicit-capture.js +2 -2
  158. package/dist/external-wiki-access.js +42 -41
  159. package/dist/extraction.js +16 -16
  160. package/dist/{first-start-migration-QVD22WWS.js → first-start-migration-A6PHIBUQ.js} +4 -4
  161. package/dist/harmonic-retrieval.js +10 -10
  162. package/dist/index.d.ts +1 -0
  163. package/dist/index.d.ts.map +1 -1
  164. package/dist/index.js +130 -122
  165. package/dist/lcm/engine.js +2 -2
  166. package/dist/lcm/index.js +5 -5
  167. package/dist/local-llm.js +2 -2
  168. package/dist/location/index.js +15 -15
  169. package/dist/location/tagging.d.ts +7 -5
  170. package/dist/location/tagging.d.ts.map +1 -1
  171. package/dist/maintenance/memory-governance.js +10 -10
  172. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -10
  173. package/dist/maintenance/rebuild-memory-projection-runner.js +12 -12
  174. package/dist/maintenance/rebuild-memory-projection.js +11 -11
  175. package/dist/memory-browse-operations.js +42 -41
  176. package/dist/namespaces/migrate.js +11 -11
  177. package/dist/namespaces/storage.js +10 -10
  178. package/dist/offline-sync-file-io.d.ts +15 -0
  179. package/dist/offline-sync-file-io.d.ts.map +1 -1
  180. package/dist/offline-sync-file-io.js +9 -1
  181. package/dist/offline-sync-snapshot-stream.js +4 -4
  182. package/dist/offline-sync-upload-staging.js +2 -2
  183. package/dist/offline-sync.d.ts.map +1 -1
  184. package/dist/offline-sync.js +3 -3
  185. package/dist/operator-doctor-corpus.js +13 -13
  186. package/dist/operator-toolkit.js +24 -24
  187. package/dist/orchestration/maintenance.js +23 -23
  188. package/dist/orchestrator.d.ts +1 -0
  189. package/dist/orchestrator.d.ts.map +1 -1
  190. package/dist/orchestrator.js +109 -108
  191. package/dist/page-versioning.js +2 -2
  192. package/dist/recall-navigation-operations.js +42 -41
  193. package/dist/recall-navigation.js +41 -40
  194. package/dist/resume-bundles.js +15 -15
  195. package/dist/semantic-consolidation.js +15 -15
  196. package/dist/semantic-rule-promotion.js +10 -10
  197. package/dist/semantic-rule-verifier.js +10 -10
  198. package/dist/source-agent-qualifier.js +13 -13
  199. package/dist/standup/build.js +10 -10
  200. package/dist/storage/entity-canonical-id-migration-adapter.d.ts +1 -0
  201. package/dist/storage/entity-canonical-id-migration-adapter.d.ts.map +1 -1
  202. package/dist/storage/entity-canonical-id-migration.d.ts +7 -0
  203. package/dist/storage/entity-canonical-id-migration.d.ts.map +1 -1
  204. package/dist/storage/entity-canonical-id-references.d.ts +2 -0
  205. package/dist/storage/entity-canonical-id-references.d.ts.map +1 -1
  206. package/dist/storage/entity-migration-completion-cache.d.ts +46 -0
  207. package/dist/storage/entity-migration-completion-cache.d.ts.map +1 -0
  208. package/dist/storage/entity-store.d.ts +1 -8
  209. package/dist/storage/entity-store.d.ts.map +1 -1
  210. package/dist/storage/memory-read-store.d.ts +3 -2
  211. package/dist/storage/memory-read-store.d.ts.map +1 -1
  212. package/dist/storage/windowed-memory-read.d.ts +6 -0
  213. package/dist/storage/windowed-memory-read.d.ts.map +1 -1
  214. package/dist/storage.d.ts +2 -0
  215. package/dist/storage.d.ts.map +1 -1
  216. package/dist/storage.js +9 -9
  217. package/dist/summarizer.js +3 -3
  218. package/dist/support-passport/index.js +44 -43
  219. package/dist/transfer/capsule-export.js +2 -2
  220. package/dist/transfer/capsule-import.js +4 -4
  221. package/dist/transfer/export-okf.js +10 -10
  222. package/dist/verified-recall.js +10 -10
  223. package/dist/who-knows-cli.js +11 -11
  224. package/dist/who-knows.js +10 -10
  225. package/package.json +2 -2
  226. package/src/access-http-briefing.ts +4 -0
  227. package/src/access-http.test.ts +40 -11
  228. package/src/access-http.ts +6 -0
  229. package/src/access-observe-write-category.ts +1 -1
  230. package/src/access-observe-write-surface.ts +24 -19
  231. package/src/access-operations-batch.ts +18 -2
  232. package/src/access-service.ts +5 -0
  233. package/src/briefing-abort.test.ts +173 -0
  234. package/src/briefing-deadline.test.ts +84 -0
  235. package/src/briefing-deadline.ts +33 -0
  236. package/src/briefing-window-abort.test.ts +47 -0
  237. package/src/briefing-window.ts +33 -4
  238. package/src/briefing.ts +23 -16
  239. package/src/correction/passive-correction-notifications-abort.test.ts +42 -0
  240. package/src/correction/passive-correction-notifications.ts +4 -0
  241. package/src/entity-lazy-compatibility.test.ts +229 -0
  242. package/src/entity-migration-completion-notification.test.ts +148 -0
  243. package/src/index.ts +1 -1
  244. package/src/location/tagging.ts +14 -12
  245. package/src/offline-sync-file-io.ts +61 -2
  246. package/src/offline-sync.test.ts +313 -20
  247. package/src/offline-sync.ts +43 -21
  248. package/src/orchestrator-init.test.ts +49 -1
  249. package/src/orchestrator.ts +9 -8
  250. package/src/storage/entity-canonical-id-migration-adapter.ts +16 -1
  251. package/src/storage/entity-canonical-id-migration.ts +17 -8
  252. package/src/storage/entity-canonical-id-references.ts +24 -0
  253. package/src/storage/entity-migration-completion-cache.nofollow.test.ts +223 -0
  254. package/src/storage/entity-migration-completion-cache.ts +153 -0
  255. package/src/storage/entity-store.ts +58 -86
  256. package/src/storage/memory-read-store.ts +9 -3
  257. package/src/storage/windowed-memory-read.ts +8 -0
  258. package/src/storage-startup-nonblocking-migration.test.ts +183 -0
  259. package/src/storage.ts +7 -2
  260. package/src/testing/subjects/entity-migration-completion-cache.test.ts +90 -0
  261. package/dist/chunk-3NYIXVCT.js.map +0 -1
  262. package/dist/chunk-6W3QH3QW.js.map +0 -1
  263. package/dist/chunk-7XFQSQYS.js.map +0 -1
  264. package/dist/chunk-FRXS6SC7.js.map +0 -1
  265. package/dist/chunk-LWRIGKFM.js.map +0 -1
  266. package/dist/chunk-N6EM7ESC.js.map +0 -1
  267. package/dist/chunk-NH37SP6V.js.map +0 -1
  268. package/dist/chunk-NUK5XU3P.js.map +0 -1
  269. package/dist/chunk-O4CA53UY.js.map +0 -1
  270. package/dist/chunk-RZDDNH63.js.map +0 -1
  271. package/dist/chunk-SA6P2KS3.js.map +0 -1
  272. package/dist/chunk-SUKPM6DV.js.map +0 -1
  273. package/dist/chunk-VY6BD5KB.js.map +0 -1
  274. /package/dist/{auto-sync-BNCBMU6H.js.map → auto-sync-UQQ6YJSB.js.map} +0 -0
  275. /package/dist/{entity-canonical-id-migration-2YOLMOFC.js.map → briefing-deadline.js.map} +0 -0
  276. /package/dist/{capsule-merge-RNQUP2VL.js.map → capsule-merge-D4BTIJGK.js.map} +0 -0
  277. /package/dist/{chunk-ESDOQEJM.js.map → chunk-2AKIVSHP.js.map} +0 -0
  278. /package/dist/{chunk-MCS4LIXP.js.map → chunk-43TUWQNR.js.map} +0 -0
  279. /package/dist/{chunk-2NLCDNGA.js.map → chunk-564YP3DW.js.map} +0 -0
  280. /package/dist/{chunk-PBWJVRKE.js.map → chunk-5FUDSK6Z.js.map} +0 -0
  281. /package/dist/{chunk-V4GPFCJ7.js.map → chunk-5G53JSRO.js.map} +0 -0
  282. /package/dist/{chunk-WJCAAVZX.js.map → chunk-5X2H3X4J.js.map} +0 -0
  283. /package/dist/{chunk-CPBYKOTC.js.map → chunk-6ZQX3R66.js.map} +0 -0
  284. /package/dist/{chunk-AF4UIHON.js.map → chunk-74AY3S7U.js.map} +0 -0
  285. /package/dist/{chunk-XSJL62LE.js.map → chunk-7J6A5MV2.js.map} +0 -0
  286. /package/dist/{chunk-A3WXV7YU.js.map → chunk-7SAE6T36.js.map} +0 -0
  287. /package/dist/{chunk-BWF3KNSQ.js.map → chunk-7YVER7ZO.js.map} +0 -0
  288. /package/dist/{chunk-5IDQVE4U.js.map → chunk-A3UFV4K3.js.map} +0 -0
  289. /package/dist/{chunk-6TIVVOVU.js.map → chunk-BNJAVRNJ.js.map} +0 -0
  290. /package/dist/{chunk-CMOYD5CA.js.map → chunk-CCL2OXFO.js.map} +0 -0
  291. /package/dist/{chunk-FOKTW6GY.js.map → chunk-EWBXMGXR.js.map} +0 -0
  292. /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
  293. /package/dist/{chunk-E47UQBYW.js.map → chunk-GXVXBYRA.js.map} +0 -0
  294. /package/dist/{chunk-AGCEJDG2.js.map → chunk-HPNXS6W6.js.map} +0 -0
  295. /package/dist/{chunk-N5PISU26.js.map → chunk-IN6U2BS5.js.map} +0 -0
  296. /package/dist/{chunk-HI53EK3Y.js.map → chunk-IUYXCTA7.js.map} +0 -0
  297. /package/dist/{chunk-VGRBKEBH.js.map → chunk-JBWD3J5U.js.map} +0 -0
  298. /package/dist/{chunk-YB3WN6XA.js.map → chunk-K5QQXVG6.js.map} +0 -0
  299. /package/dist/{chunk-7LCGFUMB.js.map → chunk-KQAFEZQX.js.map} +0 -0
  300. /package/dist/{chunk-HDT3DMUF.js.map → chunk-KW5YZELC.js.map} +0 -0
  301. /package/dist/{chunk-DM6SOROF.js.map → chunk-LIBVANOF.js.map} +0 -0
  302. /package/dist/{chunk-VKUMKVEU.js.map → chunk-LMMBV7QC.js.map} +0 -0
  303. /package/dist/{chunk-NMOEOQV4.js.map → chunk-M2DOJBH7.js.map} +0 -0
  304. /package/dist/{chunk-3INLTKTW.js.map → chunk-M4HRYLOA.js.map} +0 -0
  305. /package/dist/{chunk-YY6PWFUC.js.map → chunk-M6TP6UYD.js.map} +0 -0
  306. /package/dist/{chunk-BJTFLP3J.js.map → chunk-MLNXATFG.js.map} +0 -0
  307. /package/dist/{chunk-C7WDWD35.js.map → chunk-MWURUSRE.js.map} +0 -0
  308. /package/dist/{chunk-WIBWONL4.js.map → chunk-N74WJ7B2.js.map} +0 -0
  309. /package/dist/{chunk-LCHFINL4.js.map → chunk-N7FNCE7F.js.map} +0 -0
  310. /package/dist/{chunk-QZK6HG4J.js.map → chunk-NPLM7AFU.js.map} +0 -0
  311. /package/dist/{chunk-CRJX4OMN.js.map → chunk-NSP5PIQH.js.map} +0 -0
  312. /package/dist/{chunk-X52SXKVC.js.map → chunk-P7OVFGMU.js.map} +0 -0
  313. /package/dist/{chunk-2J6BIOVN.js.map → chunk-PJCVI44H.js.map} +0 -0
  314. /package/dist/{chunk-Q6PYCPHO.js.map → chunk-Q3PNQYAC.js.map} +0 -0
  315. /package/dist/{chunk-D76AHITY.js.map → chunk-QS765SBL.js.map} +0 -0
  316. /package/dist/{chunk-KNCR7APE.js.map → chunk-QTDZ2NYC.js.map} +0 -0
  317. /package/dist/{chunk-HCPVL6SF.js.map → chunk-SJTSWG2Z.js.map} +0 -0
  318. /package/dist/{chunk-XFND63OB.js.map → chunk-TFWJXWL6.js.map} +0 -0
  319. /package/dist/{chunk-SVJ5JVZO.js.map → chunk-TXKQBQYW.js.map} +0 -0
  320. /package/dist/{chunk-WQFHHJWK.js.map → chunk-UALXWWGN.js.map} +0 -0
  321. /package/dist/{chunk-3PVKV3XL.js.map → chunk-UNOAVHV4.js.map} +0 -0
  322. /package/dist/{chunk-FYZXZUGU.js.map → chunk-UQBOPCDK.js.map} +0 -0
  323. /package/dist/{chunk-OD7ZXCUF.js.map → chunk-UXGXJUZ5.js.map} +0 -0
  324. /package/dist/{chunk-2BY56HE6.js.map → chunk-VCOMACR6.js.map} +0 -0
  325. /package/dist/{chunk-5FNZ3G6X.js.map → chunk-VKQI47DJ.js.map} +0 -0
  326. /package/dist/{chunk-MPJDQC4G.js.map → chunk-WJCXUE2J.js.map} +0 -0
  327. /package/dist/{chunk-C3HRYVGD.js.map → chunk-WV4UYDP6.js.map} +0 -0
  328. /package/dist/{chunk-E3DL3CAL.js.map → chunk-WXJC2L66.js.map} +0 -0
  329. /package/dist/{chunk-O7GNSHFF.js.map → chunk-XC3H7KON.js.map} +0 -0
  330. /package/dist/{chunk-OOOX5C4X.js.map → chunk-XL2EE5W3.js.map} +0 -0
  331. /package/dist/{chunk-CZVNOTLR.js.map → chunk-XXR4A33N.js.map} +0 -0
  332. /package/dist/{chunk-2AV2U2SZ.js.map → chunk-YDIOMDBW.js.map} +0 -0
  333. /package/dist/{chunk-QFTNWDJ6.js.map → chunk-YECEEJUU.js.map} +0 -0
  334. /package/dist/{chunk-BMG3SAXA.js.map → chunk-Z5277JWF.js.map} +0 -0
  335. /package/dist/{chunk-SW3WHVKN.js.map → chunk-ZFNXJEUG.js.map} +0 -0
  336. /package/dist/{chunk-KMTMMBEB.js.map → chunk-ZNRTHYYW.js.map} +0 -0
  337. /package/dist/{chunk-VCBS5GRQ.js.map → chunk-ZSMA67FV.js.map} +0 -0
  338. /package/dist/{first-start-migration-QVD22WWS.js.map → first-start-migration-A6PHIBUQ.js.map} +0 -0
@@ -15,11 +15,13 @@ import {
15
15
  EngramAccessInputError,
16
16
  type EngramAccessRecallRequest,
17
17
  type EngramAccessService,
18
+ type EngramAccessBriefingRequest,
18
19
  } from "./access-service.js";
19
20
  import {
20
21
  tokenCapabilityStore,
21
22
  type TokenCapabilities,
22
23
  } from "./access-token-capabilities.js";
24
+ import { BriefingTimeoutError } from "./briefing-deadline.js";
23
25
  import { parseConfig } from "./config.js";
24
26
  import { readPair, writePair } from "./contradiction/contradiction-review.js";
25
27
  import { createChatSession } from "./chat/chat-session.js";
@@ -859,17 +861,23 @@ test("HTTP briefing endpoint dispatches through the briefing operation", async (
859
861
  assert.equal(body.format, "markdown");
860
862
  assert.equal(body.markdown, "# Briefing");
861
863
  assert.deepEqual(body.json.sections.activeThreads, []);
862
- assert.deepEqual(calls, [
863
- {
864
- since: "24h",
865
- focus: undefined,
866
- includeLocation: undefined,
867
- namespace: "team",
868
- format: "markdown",
869
- maxFollowups: 5,
870
- principal: "panel-user",
871
- },
872
- ]);
864
+ assert.equal(calls.length, 1);
865
+ // SAFETY: the registered handler builds this request after validating the HTTP body; assertions below check its values.
866
+ const captured = calls[0] as EngramAccessBriefingRequest;
867
+ const { abortSignal, ...briefingRequest } = captured;
868
+ assert.ok(
869
+ abortSignal instanceof AbortSignal,
870
+ "the briefing request carries the HTTP request lifetime signal",
871
+ );
872
+ assert.deepEqual(briefingRequest, {
873
+ since: "24h",
874
+ focus: undefined,
875
+ includeLocation: undefined,
876
+ namespace: "team",
877
+ format: "markdown",
878
+ maxFollowups: 5,
879
+ principal: "panel-user",
880
+ });
873
881
 
874
882
  const invalid = await fetch(
875
883
  `http://127.0.0.1:${status.port}/engram/v1/briefing`,
@@ -890,6 +898,27 @@ test("HTTP briefing endpoint dispatches through the briefing operation", async (
890
898
  }
891
899
  });
892
900
 
901
+ test("HTTP briefing endpoint answers 504 briefing_timeout when the request budget is exhausted", async () => {
902
+ const service: EngramAccessService = Object.assign(Object.create(null), {
903
+ briefing: async () => {
904
+ throw new BriefingTimeoutError(30_000);
905
+ },
906
+ });
907
+ const server = new EngramAccessHttpServer({ service, port: 0, authToken: "test-token", adminConsoleEnabled: false });
908
+ const status = await server.start();
909
+ try {
910
+ const response = await fetch(`http://127.0.0.1:${status.port}/engram/v1/briefing`, {
911
+ method: "POST",
912
+ headers: { authorization: "Bearer test-token", "content-type": "application/json" },
913
+ body: JSON.stringify({}),
914
+ });
915
+ assert.equal(response.status, 504);
916
+ assert.deepEqual(await response.json(), { error: "briefing timed out after 30000ms", code: "briefing_timeout" });
917
+ } finally {
918
+ await server.stop();
919
+ }
920
+ });
921
+
893
922
  test("HTTP contradiction scan uses writable namespace resolver", async () => {
894
923
  const resolverCalls: Array<{
895
924
  namespace: string | undefined;
@@ -10,6 +10,7 @@ import { log } from "./logger.js";
10
10
  import { resolveNamespaceCapabilities } from "./capabilities.js";
11
11
  import { WriteRateLimiter, type WriteRateLimitReservation } from "./write-rate-limiter.js";
12
12
  import { abortError, isAbortError } from "./abort-error.js";
13
+ import { BriefingTimeoutError } from "./briefing-deadline.js";
13
14
  import { EngramAccessForbiddenError } from "./access-errors.js";
14
15
  import {
15
16
  EngramAccessInputError,
@@ -500,6 +501,10 @@ export class EngramAccessHttpServer extends ReviewDeckAccessHttpBase {
500
501
  res.once("close", abortDisconnectedRequest);
501
502
  correlationIdStore.run(correlationId, () => {
502
503
  void this.handle(req, res, correlationId, abortController.signal).catch((err) => {
504
+ if (err instanceof BriefingTimeoutError) {
505
+ this.respondJson(res, 504, { error: err.message, code: "briefing_timeout" });
506
+ return;
507
+ }
503
508
  if (isAbortError(err)) {
504
509
  if (!res.destroyed && !res.writableEnded) {
505
510
  res.end();
@@ -1979,6 +1984,7 @@ export class EngramAccessHttpServer extends ReviewDeckAccessHttpBase {
1979
1984
  service: this.service,
1980
1985
  principal: this.resolveRequestPrincipal(req),
1981
1986
  respondJson: (status, payload) => this.respondJson(res, status, payload),
1987
+ abortSignal,
1982
1988
  });
1983
1989
  return;
1984
1990
  }
@@ -15,7 +15,7 @@ import {
15
15
  } from "./access-schema.js";
16
16
 
17
17
  export { reapplyCategoryCoercion };
18
- export { applyBriefingLocationContext } from "./location/tagging.js";
18
+ export { buildWithBriefingLocation } from "./location/tagging.js";
19
19
 
20
20
  export function splitCanonicalWriteRequest<T extends { rawCategory?: string; category?: string }>(
21
21
  request: T,
@@ -29,8 +29,9 @@ import {
29
29
  type MemoryScopePlan,
30
30
  NamespaceNotWritableError,
31
31
  } from "./access-service.js";
32
- import { applyBriefingLocationContext, reapplyCategoryCoercion, splitCanonicalWriteRequest } from "./access-observe-write-category.js";
32
+ import { buildWithBriefingLocation, reapplyCategoryCoercion, splitCanonicalWriteRequest } from "./access-observe-write-category.js";
33
33
  import { abortError } from "./abort-error.js";
34
+ import { withBriefingDeadline } from "./briefing-deadline.js";
34
35
  import { extractionForceFlush } from "./access-extraction-force-flush.js";
35
36
  import { ObserveTranscriptPersister, observeTranscriptSessionKey } from "./access-observe-transcript.js";
36
37
  import { FileCalendarSource, buildBriefing, parseBriefingFocus, parseBriefingWindow } from "./briefing.js";
@@ -410,7 +411,6 @@ export class AccessObserveWriteSurface {
410
411
  }
411
412
 
412
413
  const namespace = this.deps.resolveReadableNamespace(request.namespace, request.principal);
413
- const storage = await this.deps.orchestrator.getStorage(namespace);
414
414
 
415
415
  const token =
416
416
  typeof request.since === "string" && request.since.trim().length > 0
@@ -457,23 +457,28 @@ export class AccessObserveWriteSurface {
457
457
  ? new FileCalendarSource(config.briefing.calendarSource)
458
458
  : undefined;
459
459
 
460
- const result = await applyBriefingLocationContext(await buildBriefing({
461
- storage,
462
- namespace,
463
- window,
464
- focus,
465
- calendarSource,
466
- maxFollowups,
467
- allowLlm: config.briefing.llmFollowups,
468
- openaiApiKey: config.openaiApiKey,
469
- openaiBaseUrl: config.openaiBaseUrl,
470
- model: config.model,
471
- // Without a direct OpenAI key, route follow-ups through the configured
472
- // LLM chain (gateway model source or local LLM) — same fallback every
473
- // other LLM feature uses. A configured key keeps its precedence so
474
- // existing deployments are unchanged.
475
- followupGenerator: config.openaiApiKey ? undefined : this.deps.orchestrator.briefingChainFollowupGenerator,
476
- }), request.includeLocation, storage.dir, window.from.toISOString(), config.location);
460
+ // Storage acquisition and the briefing body share one request budget.
461
+ const result = await withBriefingDeadline(async (signal) => {
462
+ const storage = await this.deps.orchestrator.getStorage(namespace);
463
+ return buildWithBriefingLocation(request.includeLocation, storage.dir, window.from.toISOString(), config.location, () => buildBriefing({
464
+ storage,
465
+ namespace,
466
+ window,
467
+ focus,
468
+ calendarSource,
469
+ maxFollowups,
470
+ allowLlm: config.briefing.llmFollowups,
471
+ openaiApiKey: config.openaiApiKey,
472
+ openaiBaseUrl: config.openaiBaseUrl,
473
+ model: config.model,
474
+ // Without a direct OpenAI key, route follow-ups through the configured
475
+ // LLM chain (gateway model source or local LLM) — same fallback every
476
+ // other LLM feature uses. A configured key keeps its precedence so
477
+ // existing deployments are unchanged.
478
+ followupGenerator: config.openaiApiKey ? undefined : this.deps.orchestrator.briefingChainFollowupGenerator,
479
+ abortSignal: signal,
480
+ }));
481
+ }, request.abortSignal);
477
482
 
478
483
  return {
479
484
  format,
@@ -23,7 +23,7 @@ import {
23
23
  retainedCategoryAlias,
24
24
  suggestionSubmitRequestSchema,
25
25
  } from "./access-schema.js";
26
- import { EngramAccessInputError, type EngramAccessService } from "./access-service.js";
26
+ import { EngramAccessInputError, type EngramAccessService, type EngramAccessBriefingRequest } from "./access-service.js";
27
27
  import { enforceNamespaceAllowList, tokenCapabilityStore } from "./access-token-capabilities.js";
28
28
  import { projectTagProjectId } from "./coding/coding-namespace.js";
29
29
  import {
@@ -381,7 +381,23 @@ defineOperation({ name: "memory_promote", description: "Promote memory.", schema
381
381
  defineOperation({ name: "memory_outcome", description: "Record outcome.", schema: strictSchema({ memoryId: S.str, outcome: S.str, namespace: S.str, sessionKey: S.str, timestamp: S.str }), handler: async (input, ctx) => { const o = optStr(input.outcome); if (o !== "success" && o !== "failure") throw new EngramAccessInputError("memory_outcome: outcome must be \"success\" or \"failure\"; got " + JSON.stringify(o)); return { result: await ctx.service.memoryOutcome({ memoryId: optStr(input.memoryId) ?? "", outcome: o, namespace: optStr(input.namespace), sessionKey: optStr(input.sessionKey), timestamp: optStr(input.timestamp), principal: ctx.authenticatedPrincipal }) }; } });
382
382
  defineOperation({ name: "memory_action_apply", description: "Apply action.", schema: strictSchema({ action: S.str, outcome: S.str, reason: S.str, memoryId: S.str, namespace: S.str, sessionKey: S.str, content: S.str, category: S.str, linkTargetId: S.str, linkType: S.str, linkStrength: S.num, artifactType: S.str, execute: S.bool, sourcePrompt: S.str, dryRun: S.bool }), handler: async (input, ctx) => ({ result: await ctx.service.memoryActionApply({ action: optStr(input.action) ?? "", outcome: optStr(input.outcome), reason: optStr(input.reason), memoryId: optStr(input.memoryId) ?? "", namespace: optStr(input.namespace), sessionKey: optStr(input.sessionKey), content: optStr(input.content), category: optStr(input.category), linkTargetId: optStr(input.linkTargetId), linkType: optStr(input.linkType), linkStrength: optNum(input.linkStrength), artifactType: optStr(input.artifactType), execute: optBool(input.execute), sourcePrompt: optStr(input.sourcePrompt), dryRun: input.dryRun === true, principal: ctx.authenticatedPrincipal }) }) });
383
383
  defineOperation({ name: "context_checkpoint", description: "Context checkpoint.", schema: strictSchema({ sessionKey: S.str, context: S.str, namespace: S.str }), handler: async (input, ctx) => ({ result: await ctx.service.contextCheckpoint({ sessionKey: optStr(input.sessionKey) ?? "", context: optStr(input.context) ?? "", namespace: optStr(input.namespace), principal: ctx.authenticatedPrincipal }) }) });
384
- defineOperation({ name: "briefing", description: "Daily briefing.", schema: strictSchema({ since: S.str, focus: S.str, namespace: S.str, includeLocation: S.bool, format: S.str, maxFollowups: S.num }), handler: async (input, ctx) => { const rf = optStr(input.format); const fe = validateBriefingFormat(rf); if (fe) throw new EngramAccessInputError(fe); return { result: await ctx.service.briefing({ since: optStr(input.since), focus: optStr(input.focus), namespace: optStr(input.namespace), includeLocation: optBool(input.includeLocation), format: rf as "json" | "markdown" | undefined, maxFollowups: optNum(input.maxFollowups), principal: ctx.authenticatedPrincipal }) }; } });
384
+ defineOperation({
385
+ name: "briefing", description: "Daily briefing.",
386
+ schema: strictSchema({ since: S.str, focus: S.str, namespace: S.str, includeLocation: S.bool, format: S.str, maxFollowups: S.num }),
387
+ handler: async (input, ctx) => {
388
+ const rf = optStr(input.format);
389
+ const fe = validateBriefingFormat(rf);
390
+ if (fe) throw new EngramAccessInputError(fe);
391
+ // SAFETY: validateBriefingFormat rejects every format except json, markdown, or undefined.
392
+ const request: EngramAccessBriefingRequest = {
393
+ since: optStr(input.since), focus: optStr(input.focus), namespace: optStr(input.namespace),
394
+ includeLocation: optBool(input.includeLocation), format: rf as "json" | "markdown" | undefined,
395
+ maxFollowups: optNum(input.maxFollowups), principal: ctx.authenticatedPrincipal,
396
+ };
397
+ if (ctx.abortSignal) request.abortSignal = ctx.abortSignal;
398
+ return { result: await ctx.service.briefing(request) };
399
+ },
400
+ });
385
401
 
386
402
  // === CONTRADICTION/REVIEW ===
387
403
  defineOperation({ name: "review_list", description: "List review pairs.", schema: strictSchema({ filter: S.str, namespace: S.str, limit: S.num }),
@@ -507,6 +507,11 @@ export interface EngramAccessBriefingRequest extends LocationContextRequestOptio
507
507
  maxFollowups?: number;
508
508
  /** Caller principal for namespace access checks. Transport-bound — never from untrusted payloads. */
509
509
  principal?: string;
510
+ /**
511
+ * Transport request lifetime. When it aborts, the briefing stops and
512
+ * rejects with that AbortError instead of running on (issue #3139).
513
+ */
514
+ abortSignal?: AbortSignal;
510
515
  }
511
516
 
512
517
  /** Response for `remnic_briefing`. */
@@ -0,0 +1,173 @@
1
+ import assert from "node:assert/strict";
2
+ import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import test, { type TestContext } from "node:test";
6
+ import { abortError } from "./abort-error.js";
7
+ import fs from "node:fs/promises";
8
+ import { syncBuiltinESMExports } from "node:module";
9
+ import { AccessObserveWriteSurface } from "./access-observe-write-surface.js";
10
+ import { Orchestrator } from "./orchestrator.js";
11
+ import { buildBriefing, type ParsedBriefingWindow } from "./briefing.js";
12
+ import type { CorpusReadOptions } from "./corpus-read-cancellation.js";
13
+ import { StorageManager } from "./storage.js";
14
+ import type { WindowedMemoryReadOptions, WindowedMemoryReadResult } from "./storage/windowed-memory-read.js";
15
+ import type { EntityFile, MemoryFile } from "./types.js";
16
+
17
+ const WINDOW: ParsedBriefingWindow = {
18
+ from: new Date("2026-04-10T00:00:00.000Z"),
19
+ to: new Date("2026-04-11T00:00:00.000Z"),
20
+ label: "test-window",
21
+ };
22
+ const MEMORY: MemoryFile = {
23
+ path: "/synthetic/mem.md",
24
+ frontmatter: {
25
+ id: "synthetic-mem",
26
+ category: "fact",
27
+ created: "2026-04-10T01:00:00.000Z",
28
+ updated: "2026-04-10T02:00:00.000Z",
29
+ source: "test",
30
+ confidence: 0.9,
31
+ confidenceTier: "explicit",
32
+ tags: [],
33
+ },
34
+ content: "synthetic briefing memory",
35
+ };
36
+ const ENTITY: EntityFile = {
37
+ name: "SyntheticEntity",
38
+ type: "project",
39
+ updated: "2026-04-10T03:00:00.000Z",
40
+ facts: ["synthetic fact"],
41
+ timeline: [],
42
+ relationships: [],
43
+ activity: [],
44
+ aliases: [],
45
+ };
46
+
47
+ function makeStorage(t: TestContext): StorageManager {
48
+ const dir = mkdtempSync(path.join(os.tmpdir(), "remnic-briefing-abort-"));
49
+ t.after(() => rmSync(dir, { recursive: true, force: true }));
50
+ const storage = new StorageManager(dir);
51
+ t.mock.method(storage, "readMemoriesWindow", async (_options: WindowedMemoryReadOptions) => ({
52
+ memories: [MEMORY],
53
+ filePaths: [MEMORY.path],
54
+ }));
55
+ t.mock.method(storage, "readAllEntityFiles", async (_options?: CorpusReadOptions) => [ENTITY]);
56
+ return storage;
57
+ }
58
+
59
+ function makeStallingStorage(t: TestContext) {
60
+ const storage = makeStorage(t);
61
+ const fallback = Promise.withResolvers<void>();
62
+ // The fallback releases broken signal plumbing too, so the regression cannot hang.
63
+ t.mock.method(
64
+ storage,
65
+ "readMemoriesWindow",
66
+ (options: WindowedMemoryReadOptions): Promise<WindowedMemoryReadResult> => {
67
+ const signal = options.abortSignal;
68
+ if (!signal) return fallback.promise.then(() => ({ memories: [], filePaths: [] }));
69
+ const read = Promise.withResolvers<WindowedMemoryReadResult>();
70
+ const onAbort = () => read.reject(abortError("corpus read aborted"));
71
+ if (signal.aborted) {
72
+ onAbort();
73
+ return read.promise;
74
+ }
75
+ signal.addEventListener("abort", onAbort, { once: true });
76
+ void fallback.promise.then(() => {
77
+ signal.removeEventListener("abort", onAbort);
78
+ read.resolve({ memories: [], filePaths: [] });
79
+ });
80
+ return read.promise;
81
+ }
82
+ );
83
+ return { storage, releaseFallback: fallback.resolve };
84
+ }
85
+
86
+ test("buildBriefing rejects with an AbortError when the caller's cancel signal fires mid-read", async (t) => {
87
+ const { storage, releaseFallback } = makeStallingStorage(t);
88
+ const controller = new AbortController();
89
+ const call = buildBriefing({ storage, window: WINDOW, allowLlm: false, abortSignal: controller.signal });
90
+ controller.abort();
91
+ releaseFallback();
92
+ await assert.rejects(call, { name: "AbortError", message: /abort|cancel/i });
93
+ });
94
+
95
+ test("buildBriefing rejects when the caller's cancel signal is already aborted", async (t) => {
96
+ const { storage, releaseFallback } = makeStallingStorage(t);
97
+ const controller = new AbortController();
98
+ controller.abort();
99
+ const call = buildBriefing({ storage, window: WINDOW, allowLlm: false, abortSignal: controller.signal });
100
+ releaseFallback();
101
+ await assert.rejects(call, { name: "AbortError" });
102
+ });
103
+
104
+ test("a cancelled briefing does not drain the correction-notification queue", async (t) => {
105
+ const storage = makeStorage(t);
106
+ const queueDir = path.join(storage.dir, "state", "corrections");
107
+ mkdirSync(queueDir, { recursive: true });
108
+ const queueFile = path.join(queueDir, "notifications.jsonl");
109
+ writeFileSync(
110
+ queueFile,
111
+ `${JSON.stringify({ planId: "synthetic-plan", summary: "synthetic correction", undoCommand: "remnic correct plan synthetic-plan", createdAt: "2026-04-10T04:00:00.000Z" })}\n`
112
+ );
113
+ const controller = new AbortController();
114
+ const call = buildBriefing({
115
+ storage,
116
+ window: WINDOW,
117
+ abortSignal: controller.signal,
118
+ // Cancel after reads, just before the queue-draining phase.
119
+ followupGenerator: async () => {
120
+ controller.abort();
121
+ return [];
122
+ },
123
+ });
124
+ await assert.rejects(call, { name: "AbortError" });
125
+ assert.ok(existsSync(queueFile), "a cancelled briefing must not consume correction notifications");
126
+ });
127
+
128
+ test("buildBriefing returns a non-empty briefing when nothing is cancelled", async (t) => {
129
+ const result = await buildBriefing({ storage: makeStorage(t), window: WINDOW, allowLlm: false });
130
+ assert.equal(result.sections.activeThreads.length, 1);
131
+ assert.equal(result.sections.recentEntities.length, 1);
132
+ assert.ok(result.markdown.length > 0);
133
+ assert.deepEqual(result.window, { from: "2026-04-10T00:00:00.000Z", to: "2026-04-11T00:00:00.000Z" });
134
+ });
135
+
136
+ test("an HTTP/MCP briefing cancelled during the location read keeps the notification queue", async (t) => {
137
+ const storage = makeStorage(t);
138
+ const queueDir = path.join(storage.dir, "state", "corrections");
139
+ mkdirSync(queueDir, { recursive: true });
140
+ const queueFile = path.join(queueDir, "notifications.jsonl");
141
+ writeFileSync(
142
+ queueFile,
143
+ `${JSON.stringify({ planId: "synthetic-plan", summary: "synthetic correction", undoCommand: "remnic correct plan synthetic-plan", createdAt: "2026-04-10T04:00:00.000Z" })}\n`
144
+ );
145
+ const controller = new AbortController();
146
+ const read: (file: string, encoding: "utf-8") => Promise<string> = fs.readFile;
147
+ t.mock.method(fs, "readFile", async (file: string) => {
148
+ if (file.endsWith(path.join("state", "locations", "sync.json"))) controller.abort(abortError("deadline"));
149
+ return read(file, "utf-8");
150
+ });
151
+ syncBuiltinESMExports();
152
+ t.after(() => {
153
+ t.mock.restoreAll();
154
+ syncBuiltinESMExports();
155
+ });
156
+ // Object.create yields untyped values, so no assertion is needed: briefing() reads only these members.
157
+ const orchestrator: Orchestrator = Object.assign(Object.create(Orchestrator.prototype), {
158
+ config: {
159
+ briefing: { enabled: true, defaultWindow: "24h", defaultFormat: "markdown", maxFollowups: 0, llmFollowups: false },
160
+ location: { enabled: true, tagging: { enabled: true }, timezone: "UTC" },
161
+ },
162
+ getStorage: async () => storage,
163
+ });
164
+ const surface = new AccessObserveWriteSurface(
165
+ Object.assign(Object.create(null), { orchestrator, resolveReadableNamespace: () => "default" }),
166
+ );
167
+
168
+ await assert.rejects(
169
+ surface.briefing({ includeLocation: true, abortSignal: controller.signal }),
170
+ { name: "AbortError" },
171
+ );
172
+ assert.ok(existsSync(queueFile), "a briefing cancelled during the location read must not consume notifications");
173
+ });
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Issue #3139 — the briefing's default deadline.
3
+ *
4
+ * The budget is a server-side limit, not a client cancellation: exhausting it
5
+ * must surface as an explicit TimeoutError, never as an empty result.
6
+ */
7
+
8
+ import assert from "node:assert/strict";
9
+ import test from "node:test";
10
+
11
+ import {
12
+ BRIEFING_DEADLINE_MS,
13
+ BriefingTimeoutError,
14
+ withBriefingDeadline,
15
+ } from "./briefing-deadline.js";
16
+
17
+ test("withBriefingDeadline rejects with a TimeoutError when the default budget is exhausted", async (t) => {
18
+ t.mock.timers.enable({ apis: ["setTimeout", "Date"], now: 0 });
19
+
20
+ const pending = withBriefingDeadline(
21
+ () => Promise.withResolvers<never>().promise,
22
+ );
23
+
24
+ t.mock.timers.tick(BRIEFING_DEADLINE_MS);
25
+
26
+ await assert.rejects(pending, (err) => {
27
+ assert.ok(
28
+ err instanceof BriefingTimeoutError,
29
+ "budget exhaustion must surface as a timeout",
30
+ );
31
+ assert.equal(err.name, "TimeoutError");
32
+ assert.equal(err.deadlineMs, BRIEFING_DEADLINE_MS);
33
+ return true;
34
+ });
35
+ });
36
+
37
+ test("withBriefingDeadline clears its timer once the run settles", async (t) => {
38
+ t.mock.timers.enable({ apis: ["setTimeout", "Date"], now: 0 });
39
+ let observed: AbortSignal | undefined;
40
+
41
+ const result = await withBriefingDeadline(async (signal) => {
42
+ observed = signal;
43
+ return "briefing";
44
+ });
45
+
46
+ assert.equal(result, "briefing");
47
+ t.mock.timers.tick(BRIEFING_DEADLINE_MS);
48
+ assert.equal(
49
+ observed?.aborted,
50
+ false,
51
+ "the budget timer must be cleared when the run settles, not left to fire afterwards",
52
+ );
53
+ });
54
+
55
+ test("withBriefingDeadline preserves work failures and clears their timer", async (t) => {
56
+ t.mock.timers.enable({ apis: ["setTimeout", "Date"], now: 0 });
57
+ const failure = new Error("synthetic work failure");
58
+ const signals: AbortSignal[] = [];
59
+
60
+ const pending = withBriefingDeadline(async (signal) => {
61
+ signals.push(signal);
62
+ throw failure;
63
+ });
64
+
65
+ await assert.rejects(pending, (err) => err === failure);
66
+ assert.equal(signals.length, 1);
67
+ t.mock.timers.tick(BRIEFING_DEADLINE_MS);
68
+ assert.equal(signals[0].aborted, false);
69
+ });
70
+
71
+ test("withBriefingDeadline normalizes arbitrary caller cancellation to AbortError", async () => {
72
+ const controller = new AbortController();
73
+ const pending = withBriefingDeadline(
74
+ () => Promise.withResolvers<never>().promise,
75
+ controller.signal,
76
+ );
77
+ controller.abort(new Error("synthetic caller cancellation"));
78
+
79
+ await assert.rejects(pending, (err) => {
80
+ assert.ok(err instanceof Error);
81
+ assert.equal(err.name, "AbortError");
82
+ return true;
83
+ });
84
+ });
@@ -0,0 +1,33 @@
1
+ import { isAbortError, raceAbort } from "./abort-error.js";
2
+
3
+ export const BRIEFING_DEADLINE_MS = 30_000;
4
+
5
+ export class BriefingTimeoutError extends Error {
6
+ constructor(readonly deadlineMs: number) {
7
+ super(`briefing timed out after ${deadlineMs}ms`);
8
+ this.name = "TimeoutError";
9
+ }
10
+ }
11
+
12
+ export async function withBriefingDeadline<T>(
13
+ run: (signal: AbortSignal) => Promise<T>,
14
+ abortSignal?: AbortSignal
15
+ ): Promise<T> {
16
+ const controller = new AbortController();
17
+ const signal = abortSignal ? AbortSignal.any([abortSignal, controller.signal]) : controller.signal;
18
+ const timer = setTimeout(
19
+ () => controller.abort(new BriefingTimeoutError(BRIEFING_DEADLINE_MS)),
20
+ BRIEFING_DEADLINE_MS
21
+ );
22
+ try {
23
+ signal.throwIfAborted();
24
+ return await raceAbort(run(signal), signal, "briefing aborted");
25
+ } catch (err) {
26
+ // raceAbort normalizes cancellation; retain the distinct deadline reason.
27
+ const reason: unknown = signal.reason;
28
+ if (signal.aborted && (reason instanceof BriefingTimeoutError || isAbortError(reason))) throw reason;
29
+ throw err;
30
+ } finally {
31
+ clearTimeout(timer);
32
+ }
33
+ }
@@ -0,0 +1,47 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { safeReadMemories } from "./briefing-window.js";
4
+ import type { CorpusReadOptions } from "./corpus-read-cancellation.js";
5
+ import type { MemoryFile } from "./types.js";
6
+
7
+ test("caller cancellation reaches the signal-aware legacy briefing scan", async () => {
8
+ const controller = new AbortController();
9
+ const started = Promise.withResolvers<AbortSignal>();
10
+ const release = Promise.withResolvers<void>();
11
+ const reader = {
12
+ supportsAbortSignal: true,
13
+ async readAllMemories(options?: CorpusReadOptions): Promise<MemoryFile[]> {
14
+ assert.ok(options?.abortSignal);
15
+ started.resolve(options.abortSignal);
16
+ await release.promise;
17
+ options.abortSignal.throwIfAborted();
18
+ return [];
19
+ },
20
+ };
21
+ const result = safeReadMemories(
22
+ reader,
23
+ {
24
+ from: new Date("2026-04-10T00:00:00.000Z"),
25
+ to: new Date("2026-04-11T00:00:00.000Z"),
26
+ label: "yesterday",
27
+ },
28
+ { signal: controller.signal }
29
+ ).then(
30
+ () => ({ ok: true as const }),
31
+ (error) => ({ ok: false as const, error })
32
+ );
33
+ try {
34
+ const scanSignal = await started.promise;
35
+ assert.equal(scanSignal.aborted, false);
36
+ controller.abort();
37
+ release.resolve();
38
+ const outcome = await result;
39
+ assert.equal(outcome.ok, false, "a cancelled scan must reject, not return an empty corpus");
40
+ assert.ok(outcome.error instanceof Error);
41
+ assert.equal(outcome.error.name, "AbortError");
42
+ assert.equal(scanSignal.aborted, true, "the underlying scan must stop too");
43
+ } finally {
44
+ release.resolve();
45
+ await result;
46
+ }
47
+ });
@@ -1,11 +1,12 @@
1
1
  import { log } from "./logger.js";
2
- import type { MemoryFile } from "./types.js";
2
+ import type { EntityFile, MemoryFile } from "./types.js";
3
3
  import { isAbortError } from "./abort-error.js";
4
4
  import type { CorpusReadOptions } from "./corpus-read-cancellation.js";
5
5
  import type { ParsedBriefingWindow } from "./briefing.js";
6
+ import type { WindowedMemoryReadOptions } from "./storage/windowed-memory-read.js";
6
7
 
7
8
  type BriefingMemoryReader = {
8
- readMemoriesWindow?: (options: { updatedAfter?: Date }) => Promise<{ memories: MemoryFile[] }>;
9
+ readMemoriesWindow?: (options: WindowedMemoryReadOptions) => Promise<{ memories: MemoryFile[] }>;
9
10
  readAllMemories: ((options?: CorpusReadOptions) => Promise<MemoryFile[]>) & {
10
11
  supportsAbortSignal?: boolean;
11
12
  };
@@ -30,6 +31,12 @@ export const BRIEFING_FULL_READ_FALLBACK_MS = 30_000;
30
31
  export interface SafeReadMemoriesOptions {
31
32
  /** Test seam overriding {@link BRIEFING_FULL_READ_FALLBACK_MS}. */
32
33
  fallbackDeadlineMs?: number;
34
+ /**
35
+ * Caller cancellation. The signal reaches the storage read so it stops at
36
+ * its next boundary, and a cancelled read is rethrown rather than degraded
37
+ * into an empty corpus (issue #3139).
38
+ */
39
+ signal?: AbortSignal;
33
40
  }
34
41
 
35
42
  type BriefingReadMode = "windowed" | "full-read-fallback";
@@ -138,6 +145,12 @@ export async function safeReadMemories(
138
145
  resultCount = memories.length;
139
146
  return memories;
140
147
  } catch (err) {
148
+ if (isAbortError(err) && options.signal?.aborted) {
149
+ // Caller cancellation is not a read failure: rethrow it so the briefing
150
+ // rejects instead of degrading into an empty corpus (issue #3139). The
151
+ // own fallback deadline is reported as BriefingReadTimedOut below.
152
+ throw err;
153
+ }
141
154
  if (err instanceof BriefingReadTimedOut) {
142
155
  outcome = "timeout";
143
156
  bestEffortLog(
@@ -174,7 +187,9 @@ async function readBriefingMemories(
174
187
  // the full corpus on cache misses; keep the full-read fallback for custom
175
188
  // StorageManager-compatible callers that predate readMemoriesWindow().
176
189
  if (mode === "windowed") {
177
- const result = await storage.readMemoriesWindow!({ updatedAfter: window.from });
190
+ const readOptions: WindowedMemoryReadOptions = { updatedAfter: window.from };
191
+ if (options.signal) readOptions.abortSignal = options.signal;
192
+ const result = await storage.readMemoriesWindow!(readOptions);
178
193
  return result.memories;
179
194
  }
180
195
  // Refuse legacy adapters that do not explicitly declare cancellation support (#2827).
@@ -195,12 +210,13 @@ async function readBriefingMemories(
195
210
  deadlineFired = true;
196
211
  controller.abort();
197
212
  }, deadlineMs);
213
+ const signal = options.signal ? AbortSignal.any([options.signal, controller.signal]) : controller.signal;
198
214
  try {
199
215
  // Direct await, no Promise.race: the signal IS the deadline. A
200
216
  // signal-aware read settles on abort (AbortError at its next scan
201
217
  // boundary, #2307), so nothing is left scanning in the background once
202
218
  // this function returns.
203
- const memories = await storage.readAllMemories({ abortSignal: controller.signal });
219
+ const memories = await storage.readAllMemories({ abortSignal: signal });
204
220
  if (deadlineFired) {
205
221
  // A double that resolves partial rows instead of rejecting on abort
206
222
  // must not have them served as a complete corpus.
@@ -214,3 +230,16 @@ async function readBriefingMemories(
214
230
  clearTimeout(timer);
215
231
  }
216
232
  }
233
+
234
+ export async function safeReadEntities(
235
+ storage: { readAllEntityFiles(options?: CorpusReadOptions): Promise<EntityFile[]> },
236
+ signal: AbortSignal,
237
+ ): Promise<EntityFile[]> {
238
+ try {
239
+ return await storage.readAllEntityFiles({ abortSignal: signal });
240
+ } catch (err) {
241
+ if (isAbortError(err) && signal.aborted) throw err;
242
+ log.warn(`briefing: readAllEntityFiles failed: ${err}`);
243
+ return [];
244
+ }
245
+ }