@remnic/core 9.66.1 → 9.66.3

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 (606) hide show
  1. package/dist/access-admin-ops-surface.js +30 -30
  2. package/dist/access-authorization-probe.js +31 -31
  3. package/dist/access-boundary.js +30 -30
  4. package/dist/access-cli.js +96 -94
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +30 -30
  7. package/dist/access-http-query.js +32 -32
  8. package/dist/access-http.js +45 -45
  9. package/dist/access-identity-continuity-surface.js +30 -30
  10. package/dist/access-lcm-surface.js +30 -30
  11. package/dist/access-mcp.js +38 -38
  12. package/dist/access-namespace-preflight.js +30 -30
  13. package/dist/access-observe-write-surface.js +30 -30
  14. package/dist/access-operations-batch.js +33 -33
  15. package/dist/access-operations.js +37 -37
  16. package/dist/access-recall-concurrency.js +30 -30
  17. package/dist/access-recall-response.js +30 -30
  18. package/dist/access-recall-surface.js +30 -30
  19. package/dist/access-schema.js +3 -3
  20. package/dist/access-service.js +30 -30
  21. package/dist/access-surface-catalog.js +32 -32
  22. package/dist/active-recall.js +3 -3
  23. package/dist/briefing.js +13 -13
  24. package/dist/buffer.js +4 -4
  25. package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
  26. package/dist/causal-behavior.js +2 -2
  27. package/dist/causal-chain.js +2 -2
  28. package/dist/causal-consolidation.js +16 -16
  29. package/dist/causal-retrieval.js +2 -2
  30. package/dist/causal-trajectory-graph.js +2 -2
  31. package/dist/causal-trajectory.js +1 -1
  32. package/dist/{chunk-XKNHFDTT.js → chunk-3A2Z4OOS.js} +2 -2
  33. package/dist/{chunk-CHGG74CY.js → chunk-3HHTN5X7.js} +2 -2
  34. package/dist/{chunk-TCF4MU7L.js → chunk-3PO35HUU.js} +1218 -104
  35. package/dist/chunk-3PO35HUU.js.map +1 -0
  36. package/dist/{chunk-XOHYGIWF.js → chunk-3Z4SFJA5.js} +2 -2
  37. package/dist/{chunk-RDGKVH6T.js → chunk-4NKIGSJM.js} +2 -2
  38. package/dist/{chunk-GPFZFCRN.js → chunk-4PTTMTTQ.js} +1 -1
  39. package/dist/{chunk-VG4SSPAW.js → chunk-4XESAT4V.js} +2 -2
  40. package/dist/{chunk-S4CDUXQY.js → chunk-5KGLNBUB.js} +2 -2
  41. package/dist/{chunk-2X6BGKQL.js → chunk-5XRZANCW.js} +10 -10
  42. package/dist/{chunk-TCQRIIIN.js → chunk-6DODJYBY.js} +2 -2
  43. package/dist/{chunk-KWIGDKM2.js → chunk-73CAEYI3.js} +2 -2
  44. package/dist/{chunk-J2R275FN.js → chunk-7DXW5V2W.js} +2 -2
  45. package/dist/{chunk-LYUUA6QB.js → chunk-7JJHIVMG.js} +2 -2
  46. package/dist/{chunk-2FA7U5XY.js → chunk-7NNMRMLZ.js} +2 -2
  47. package/dist/{chunk-227UFSEQ.js → chunk-7RZJWZ46.js} +5 -5
  48. package/dist/{chunk-EWKVYCFP.js → chunk-B2OKCCXX.js} +1 -1
  49. package/dist/{chunk-NSTNGMSS.js → chunk-B7AGLMGP.js} +7 -7
  50. package/dist/chunk-BLUYQFRH.js +36 -0
  51. package/dist/chunk-BLUYQFRH.js.map +1 -0
  52. package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
  53. package/dist/{chunk-CEH2AFXG.js → chunk-C24UZSFW.js} +6 -6
  54. package/dist/{chunk-LTBKKSCX.js → chunk-C2JCVZWX.js} +2 -2
  55. package/dist/{chunk-65JOGLIS.js → chunk-D4R3OUZA.js} +2 -2
  56. package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
  57. package/dist/chunk-E4X2YX2F.js +22 -0
  58. package/dist/chunk-E4X2YX2F.js.map +1 -0
  59. package/dist/{chunk-IGZCAHFL.js → chunk-EGGNFLAT.js} +2 -2
  60. package/dist/{chunk-H2IRSXZN.js → chunk-ESDOAP7W.js} +3 -3
  61. package/dist/{chunk-D3UXGE5D.js → chunk-EZWIEFYD.js} +8 -8
  62. package/dist/{chunk-ZPS724LT.js → chunk-F5H5QINZ.js} +2 -2
  63. package/dist/{chunk-S4Z2OBTX.js → chunk-FPSOXMLK.js} +3 -3
  64. package/dist/chunk-FYDW33ZB.js +13 -0
  65. package/dist/chunk-FYDW33ZB.js.map +1 -0
  66. package/dist/{chunk-CX4OLNIH.js → chunk-G5UKF2GJ.js} +2 -2
  67. package/dist/{chunk-OJUEUQDJ.js → chunk-GGY2747C.js} +2 -2
  68. package/dist/{chunk-TNGH4ZYI.js → chunk-GNWLTCKC.js} +2 -2
  69. package/dist/{chunk-YWPLOJ7T.js → chunk-GZPGI45F.js} +2 -2
  70. package/dist/{chunk-NFP4KPTA.js → chunk-IDEPEHUG.js} +2 -2
  71. package/dist/{chunk-LJABGXC4.js → chunk-IN5EUWCA.js} +3 -3
  72. package/dist/{chunk-U6NGKS32.js → chunk-JJAOVJ3E.js} +9 -9
  73. package/dist/{chunk-RPIS5FQ7.js → chunk-JWCS3VYQ.js} +50 -50
  74. package/dist/{chunk-U65VJSCJ.js → chunk-KE23ZIEW.js} +13 -13
  75. package/dist/{chunk-NACLNNFQ.js → chunk-KSZKQTZS.js} +2 -2
  76. package/dist/{chunk-KJXCD7VR.js → chunk-KWJNEWTM.js} +5 -5
  77. package/dist/chunk-MAG6M3B6.js +51 -0
  78. package/dist/chunk-MAG6M3B6.js.map +1 -0
  79. package/dist/chunk-MKD3OMPS.js +20 -0
  80. package/dist/chunk-MKD3OMPS.js.map +1 -0
  81. package/dist/{chunk-UW4RYPWC.js → chunk-MLTGALDY.js} +2 -2
  82. package/dist/{chunk-MUEGIKKY.js → chunk-OKGQDI4Y.js} +17 -16
  83. package/dist/chunk-OKGQDI4Y.js.map +1 -0
  84. package/dist/{chunk-YMOPYG3K.js → chunk-OLT3YTDH.js} +4 -4
  85. package/dist/{chunk-2PSX5BJU.js → chunk-OPQKVVYG.js} +2 -2
  86. package/dist/{chunk-D6Q5EPOV.js → chunk-PGD3DXZP.js} +2 -2
  87. package/dist/chunk-Q3SHEAXR.js +80 -0
  88. package/dist/chunk-Q3SHEAXR.js.map +1 -0
  89. package/dist/{chunk-GBEOUCBB.js → chunk-Q6LC3ZD5.js} +2 -2
  90. package/dist/{chunk-QBNE55VA.js → chunk-QELYSDAU.js} +2 -2
  91. package/dist/{chunk-INUBU6I5.js → chunk-S6PRJGQL.js} +2 -2
  92. package/dist/{chunk-B4AOWRYF.js → chunk-TCJO7LOG.js} +5 -5
  93. package/dist/{chunk-SZMYGDSM.js → chunk-TO5U47YK.js} +2 -2
  94. package/dist/{chunk-GAJK3DQ5.js → chunk-U3L6GRUX.js} +2 -2
  95. package/dist/{chunk-4XLTUR6R.js → chunk-U4RGSLI4.js} +3 -3
  96. package/dist/{chunk-M5ERTI4I.js → chunk-UH53XVQ3.js} +2 -2
  97. package/dist/{chunk-NUF7VSEX.js → chunk-UXERIBOJ.js} +2 -2
  98. package/dist/{chunk-FFUMTH27.js → chunk-VA2CSIPC.js} +2 -2
  99. package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
  100. package/dist/{chunk-AF5H7TKI.js → chunk-VPJNGE2R.js} +2 -2
  101. package/dist/{chunk-OMX6TKXH.js → chunk-VTJZSLDR.js} +2 -2
  102. package/dist/{chunk-TBCDF556.js → chunk-WHFFAS4A.js} +4 -4
  103. package/dist/{chunk-7UOXCCCI.js → chunk-WVNETF4Q.js} +2 -2
  104. package/dist/{chunk-EH65UV2Z.js → chunk-XSTMPZMY.js} +2 -2
  105. package/dist/{chunk-PD66CVSD.js → chunk-Z3SQEEB6.js} +2 -2
  106. package/dist/{chunk-JKTV3JNX.js → chunk-ZBYOXIIF.js} +3 -3
  107. package/dist/{chunk-2ITZ74A6.js → chunk-ZC5IHL5X.js} +11 -11
  108. package/dist/{chunk-KI64TTBL.js → chunk-ZDMTCO7N.js} +2 -2
  109. package/dist/{chunk-C5IUOBCN.js → chunk-ZET6A3OF.js} +2 -2
  110. package/dist/{chunk-ZZZWEDF4.js → chunk-ZVAKRZZD.js} +2 -2
  111. package/dist/cli.js +67 -67
  112. package/dist/coding/export-okf-codegraph.js +13 -13
  113. package/dist/coding/pre-action-gate.js +2 -2
  114. package/dist/compounding/engine.js +13 -13
  115. package/dist/config.js +3 -3
  116. package/dist/connectors/codex-materialize-runner.js +13 -13
  117. package/dist/connectors/index.js +13 -13
  118. package/dist/convergence-refresh.js +15 -15
  119. package/dist/conversation-index/backend.js +2 -2
  120. package/dist/corpus-watermark.js +15 -15
  121. package/dist/entity-retrieval.js +13 -13
  122. package/dist/external-wiki-access.js +31 -31
  123. package/dist/extraction-span-bench.d.ts +14 -0
  124. package/dist/extraction-span-bench.js +17 -0
  125. package/dist/extraction-span-bench.js.map +1 -0
  126. package/dist/extraction-span-clip.d.ts +22 -0
  127. package/dist/extraction-span-clip.js +19 -0
  128. package/dist/extraction-span-clip.js.map +1 -0
  129. package/dist/extraction-span-contains.d.ts +12 -0
  130. package/dist/extraction-span-contains.js +17 -0
  131. package/dist/extraction-span-contains.js.map +1 -0
  132. package/dist/extraction-span-join.d.ts +23 -0
  133. package/dist/extraction-span-join.js +19 -0
  134. package/dist/extraction-span-join.js.map +1 -0
  135. package/dist/extraction-span-length.d.ts +11 -0
  136. package/dist/extraction-span-length.js +17 -0
  137. package/dist/extraction-span-length.js.map +1 -0
  138. package/dist/extraction-span-overlap.d.ts +14 -0
  139. package/dist/extraction-span-overlap.js +16 -0
  140. package/dist/extraction-span-overlap.js.map +1 -0
  141. package/dist/extraction-span-shift.d.ts +15 -0
  142. package/dist/extraction-span-shift.js +17 -0
  143. package/dist/extraction-span-shift.js.map +1 -0
  144. package/dist/extraction-span-slice.d.ts +12 -0
  145. package/dist/extraction-span-slice.js +17 -0
  146. package/dist/extraction-span-slice.js.map +1 -0
  147. package/dist/extraction-span-validate.d.ts +20 -0
  148. package/dist/extraction-span-validate.js +20 -0
  149. package/dist/extraction-span-validate.js.map +1 -0
  150. package/dist/extraction-span-wire.d.ts +17 -0
  151. package/dist/extraction-span-wire.js +13 -0
  152. package/dist/extraction-span-wire.js.map +1 -0
  153. package/dist/extraction-span.d.ts +17 -0
  154. package/dist/extraction-span.js +8 -0
  155. package/dist/extraction.js +16 -16
  156. package/dist/{graph-edge-decay-A4SDFMMS.js → graph-edge-decay-DGTBFALQ.js} +4 -4
  157. package/dist/graph-edge-decay-DGTBFALQ.js.map +1 -0
  158. package/dist/graph-snapshot.js +3 -3
  159. package/dist/graph.js +2 -2
  160. package/dist/harmonic-retrieval.js +1 -1
  161. package/dist/index.d.ts +387 -1
  162. package/dist/index.js +203 -122
  163. package/dist/lcm/engine.js +3 -3
  164. package/dist/lcm/index.js +9 -9
  165. package/dist/location/index.js +15 -15
  166. package/dist/maintenance/memory-governance.js +13 -13
  167. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +13 -13
  168. package/dist/maintenance/rebuild-memory-projection.js +14 -14
  169. package/dist/namespaces/migrate.js +14 -14
  170. package/dist/namespaces/search.js +9 -9
  171. package/dist/namespaces/storage.js +13 -13
  172. package/dist/operator-doctor-corpus.js +16 -16
  173. package/dist/operator-toolkit.js +26 -26
  174. package/dist/orchestration/maintenance.js +21 -21
  175. package/dist/orchestrator.js +98 -96
  176. package/dist/qmd.js +4 -4
  177. package/dist/recall-deep-access.d.ts +30 -0
  178. package/dist/recall-deep-access.js +50 -0
  179. package/dist/recall-deep-access.js.map +1 -0
  180. package/dist/recall-deep-budget-left.d.ts +8 -0
  181. package/dist/recall-deep-budget-left.js +15 -0
  182. package/dist/recall-deep-budget-left.js.map +1 -0
  183. package/dist/recall-deep-budget.d.ts +9 -0
  184. package/dist/recall-deep-budget.js +15 -0
  185. package/dist/recall-deep-budget.js.map +1 -0
  186. package/dist/recall-deep-expand.d.ts +12 -0
  187. package/dist/recall-deep-expand.js +17 -0
  188. package/dist/recall-deep-expand.js.map +1 -0
  189. package/dist/recall-deep-policy.d.ts +17 -0
  190. package/dist/recall-deep-policy.js +18 -0
  191. package/dist/recall-deep-policy.js.map +1 -0
  192. package/dist/recall-deep-query.d.ts +15 -0
  193. package/dist/recall-deep-query.js +12 -0
  194. package/dist/recall-deep-query.js.map +1 -0
  195. package/dist/recall-deep-refine.d.ts +12 -0
  196. package/dist/recall-deep-refine.js +17 -0
  197. package/dist/recall-deep-refine.js.map +1 -0
  198. package/dist/recall-deep-render.d.ts +11 -0
  199. package/dist/recall-deep-render.js +28 -0
  200. package/dist/recall-deep-render.js.map +1 -0
  201. package/dist/recall-deep-step-count.d.ts +8 -0
  202. package/dist/recall-deep-step-count.js +15 -0
  203. package/dist/recall-deep-step-count.js.map +1 -0
  204. package/dist/recall-deep-stop-reason.d.ts +17 -0
  205. package/dist/recall-deep-stop-reason.js +23 -0
  206. package/dist/recall-deep-stop-reason.js.map +1 -0
  207. package/dist/recall-deep-stop.d.ts +17 -0
  208. package/dist/recall-deep-stop.js +23 -0
  209. package/dist/recall-deep-stop.js.map +1 -0
  210. package/dist/recall-deep-working-set.d.ts +9 -0
  211. package/dist/recall-deep-working-set.js +14 -0
  212. package/dist/recall-deep-working-set.js.map +1 -0
  213. package/dist/recall-deep.js +6 -73
  214. package/dist/recall-deep.js.map +1 -1
  215. package/dist/recall-navigate-access.d.ts +39 -0
  216. package/dist/recall-navigate-access.js +31 -0
  217. package/dist/recall-navigate-access.js.map +1 -0
  218. package/dist/recall-navigate-action.d.ts +17 -0
  219. package/dist/recall-navigate-action.js +14 -0
  220. package/dist/recall-navigate-action.js.map +1 -0
  221. package/dist/recall-navigate-budget.d.ts +16 -0
  222. package/dist/recall-navigate-budget.js +18 -0
  223. package/dist/recall-navigate-budget.js.map +1 -0
  224. package/dist/recall-navigate-cost.d.ts +8 -0
  225. package/dist/recall-navigate-cost.js +15 -0
  226. package/dist/recall-navigate-cost.js.map +1 -0
  227. package/dist/recall-navigate-depth.d.ts +8 -0
  228. package/dist/recall-navigate-depth.js +15 -0
  229. package/dist/recall-navigate-depth.js.map +1 -0
  230. package/dist/recall-navigate-link.d.ts +17 -0
  231. package/dist/recall-navigate-link.js +20 -0
  232. package/dist/recall-navigate-link.js.map +1 -0
  233. package/dist/recall-navigate-max-children.d.ts +8 -0
  234. package/dist/recall-navigate-max-children.js +15 -0
  235. package/dist/recall-navigate-max-children.js.map +1 -0
  236. package/dist/recall-navigate-node.d.ts +15 -0
  237. package/dist/recall-navigate-node.js +13 -0
  238. package/dist/recall-navigate-node.js.map +1 -0
  239. package/dist/recall-navigate-path.d.ts +9 -0
  240. package/dist/recall-navigate-path.js +14 -0
  241. package/dist/recall-navigate-path.js.map +1 -0
  242. package/dist/recall-navigate-render.d.ts +15 -0
  243. package/dist/recall-navigate-render.js +23 -0
  244. package/dist/recall-navigate-render.js.map +1 -0
  245. package/dist/recall-navigate.js +6 -42
  246. package/dist/recall-navigate.js.map +1 -1
  247. package/dist/recall-state-view-current.d.ts +18 -0
  248. package/dist/recall-state-view-current.js +14 -0
  249. package/dist/recall-state-view-current.js.map +1 -0
  250. package/dist/recall-state-view-inject.d.ts +15 -0
  251. package/dist/recall-state-view-inject.js +16 -0
  252. package/dist/recall-state-view-inject.js.map +1 -0
  253. package/dist/recall-state-view-kind.d.ts +17 -0
  254. package/dist/recall-state-view-kind.js +14 -0
  255. package/dist/recall-state-view-kind.js.map +1 -0
  256. package/dist/recall-state-view-label.d.ts +17 -0
  257. package/dist/recall-state-view-label.js +16 -0
  258. package/dist/recall-state-view-label.js.map +1 -0
  259. package/dist/recall-state-view-render.d.ts +18 -0
  260. package/dist/recall-state-view-render.js +11 -0
  261. package/dist/recall-state-view-render.js.map +1 -0
  262. package/dist/recall-state-view-successor.d.ts +15 -0
  263. package/dist/recall-state-view-successor.js +13 -0
  264. package/dist/recall-state-view-successor.js.map +1 -0
  265. package/dist/recall-state-view-superseded.d.ts +15 -0
  266. package/dist/recall-state-view-superseded.js +14 -0
  267. package/dist/recall-state-view-superseded.js.map +1 -0
  268. package/dist/recall-state-view-transition.d.ts +18 -0
  269. package/dist/recall-state-view-transition.js +14 -0
  270. package/dist/recall-state-view-transition.js.map +1 -0
  271. package/dist/recall-state-view-widen.d.ts +13 -0
  272. package/dist/recall-state-view-widen.js +11 -0
  273. package/dist/recall-state-view-widen.js.map +1 -0
  274. package/dist/recall-state-view-wire.d.ts +3 -4
  275. package/dist/recall-state-view-wire.js +2 -1
  276. package/dist/resume-bundles.js +4 -4
  277. package/dist/search/factory.js +8 -8
  278. package/dist/search/index.js +14 -14
  279. package/dist/semantic-consolidation.js +14 -14
  280. package/dist/semantic-rule-promotion.js +13 -13
  281. package/dist/semantic-rule-verifier.js +13 -13
  282. package/dist/source-agent-qualifier.js +15 -15
  283. package/dist/storage.js +12 -12
  284. package/dist/support-passport/index.js +33 -33
  285. package/dist/transfer/backup.js +2 -2
  286. package/dist/transfer/capsule-export.js +2 -2
  287. package/dist/transfer/capsule-import.js +2 -2
  288. package/dist/transfer/export-okf.js +14 -14
  289. package/dist/verified-recall.js +13 -13
  290. package/dist/who-knows-cli.js +14 -14
  291. package/dist/who-knows.js +13 -13
  292. package/package.json +2 -2
  293. package/src/activity/dwell.test.ts +28 -0
  294. package/src/activity/dwell.ts +22 -0
  295. package/src/activity/from-ms.test.ts +25 -0
  296. package/src/activity/from-ms.ts +13 -0
  297. package/src/activity/heading-level.test.ts +23 -0
  298. package/src/activity/heading-level.ts +21 -0
  299. package/src/activity/heading-title.test.ts +21 -0
  300. package/src/activity/heading-title.ts +26 -0
  301. package/src/activity/index.ts +9 -0
  302. package/src/activity/journal-heading.test.ts +22 -0
  303. package/src/activity/journal-heading.ts +18 -0
  304. package/src/activity/journal-mode.test.ts +32 -0
  305. package/src/activity/journal-mode.ts +20 -0
  306. package/src/activity/journal-path.test.ts +25 -0
  307. package/src/activity/journal-path.ts +27 -0
  308. package/src/activity/journal-section.test.ts +37 -0
  309. package/src/activity/journal-section.ts +75 -0
  310. package/src/activity/journal-source.test.ts +37 -0
  311. package/src/activity/journal-source.ts +23 -0
  312. package/src/activity/journal-vault-read.test.ts +153 -0
  313. package/src/activity/journal-vault-read.ts +214 -0
  314. package/src/activity/journal.ts +1 -1
  315. package/src/activity/privacy-enabled.test.ts +30 -0
  316. package/src/activity/privacy-enabled.ts +33 -0
  317. package/src/activity/privacy-keys.test.ts +22 -0
  318. package/src/activity/privacy-keys.ts +12 -0
  319. package/src/activity/privacy-redact.test.ts +25 -0
  320. package/src/activity/privacy-redact.ts +23 -0
  321. package/src/activity/privacy-window.test.ts +49 -0
  322. package/src/activity/privacy-window.ts +32 -0
  323. package/src/activity/retain-days.test.ts +20 -0
  324. package/src/activity/retain-days.ts +14 -0
  325. package/src/activity/timeline/analysis-batch.test.ts +56 -0
  326. package/src/activity/timeline/analysis-batch.ts +45 -0
  327. package/src/activity/timeline/analysis-claim.test.ts +33 -0
  328. package/src/activity/timeline/analysis-claim.ts +16 -0
  329. package/src/activity/timeline/analysis-dup.test.ts +28 -0
  330. package/src/activity/timeline/analysis-dup.ts +10 -0
  331. package/src/activity/timeline/analysis-empty.test.ts +21 -0
  332. package/src/activity/timeline/analysis-empty.ts +14 -0
  333. package/src/activity/timeline/analysis-evidence.test.ts +32 -0
  334. package/src/activity/timeline/analysis-evidence.ts +20 -0
  335. package/src/activity/timeline/analysis-max.test.ts +26 -0
  336. package/src/activity/timeline/analysis-max.ts +14 -0
  337. package/src/activity/timeline/analysis-order.test.ts +28 -0
  338. package/src/activity/timeline/analysis-order.ts +10 -0
  339. package/src/activity/timeline/analysis-prompt.test.ts +50 -0
  340. package/src/activity/timeline/analysis-prompt.ts +24 -0
  341. package/src/activity/timeline/analysis.test.ts +413 -0
  342. package/src/activity/timeline/analysis.ts +362 -0
  343. package/src/activity/timeline/index.ts +13 -0
  344. package/src/activity/timeline/journal-recap-persist.test.ts +148 -0
  345. package/src/activity/timeline/journal-recap-persist.ts +37 -0
  346. package/src/activity/timeline/recap-card-id.test.ts +21 -0
  347. package/src/activity/timeline/recap-card-id.ts +16 -0
  348. package/src/activity/timeline/recap-date.test.ts +21 -0
  349. package/src/activity/timeline/recap-date.ts +19 -0
  350. package/src/activity/timeline/recap-empty.test.ts +21 -0
  351. package/src/activity/timeline/recap-empty.ts +14 -0
  352. package/src/activity/timeline/recap-export.test.ts +78 -0
  353. package/src/activity/timeline/recap-export.ts +41 -0
  354. package/src/activity/timeline/recap-id.test.ts +28 -0
  355. package/src/activity/timeline/recap-id.ts +10 -0
  356. package/src/activity/timeline/recap-markdown.test.ts +56 -0
  357. package/src/activity/timeline/recap-markdown.ts +26 -0
  358. package/src/activity/timeline/recap-sort-key.test.ts +18 -0
  359. package/src/activity/timeline/recap-sort-key.ts +14 -0
  360. package/src/activity/timeline/recap-tz.test.ts +35 -0
  361. package/src/activity/timeline/recap-tz.ts +19 -0
  362. package/src/activity/timeline/week-count.test.ts +19 -0
  363. package/src/activity/timeline/week-count.ts +13 -0
  364. package/src/activity/timeline/week-days.test.ts +26 -0
  365. package/src/activity/timeline/week-days.ts +23 -0
  366. package/src/activity/timeline/week-export.test.ts +59 -0
  367. package/src/activity/timeline/week-export.ts +44 -0
  368. package/src/activity/timeline/week-iso.test.ts +29 -0
  369. package/src/activity/timeline/week-iso.ts +20 -0
  370. package/src/activity/timeline/week-monday.test.ts +17 -0
  371. package/src/activity/timeline/week-monday.ts +12 -0
  372. package/src/activity/timeline/week-range.test.ts +23 -0
  373. package/src/activity/timeline/week-range.ts +26 -0
  374. package/src/activity/timeline/week-snapshot.test.ts +66 -0
  375. package/src/activity/timeline/week-snapshot.ts +40 -0
  376. package/src/activity/timeline/week-sunday.test.ts +17 -0
  377. package/src/activity/timeline/week-sunday.ts +12 -0
  378. package/src/activity/timeline/weekly-persist.test.ts +121 -0
  379. package/src/activity/timeline/weekly-persist.ts +103 -0
  380. package/src/activity/to-ms.test.ts +25 -0
  381. package/src/activity/to-ms.ts +13 -0
  382. package/src/activity/vault-begin.test.ts +20 -0
  383. package/src/activity/vault-begin.ts +13 -0
  384. package/src/activity/vault-end.test.ts +20 -0
  385. package/src/activity/vault-end.ts +13 -0
  386. package/src/activity/vault-frontmatter.test.ts +28 -0
  387. package/src/activity/vault-frontmatter.ts +39 -0
  388. package/src/activity/vault-join.test.ts +21 -0
  389. package/src/activity/vault-join.ts +31 -0
  390. package/src/activity/vault-marker.test.ts +23 -0
  391. package/src/activity/vault-marker.ts +27 -0
  392. package/src/activity/vault-name-trim.test.ts +22 -0
  393. package/src/activity/vault-name-trim.ts +16 -0
  394. package/src/activity/vault-path.test.ts +38 -0
  395. package/src/activity/vault-path.ts +59 -0
  396. package/src/activity/vault-publish.test.ts +102 -0
  397. package/src/activity/vault-publish.ts +92 -13
  398. package/src/activity/vault-region-pair.test.ts +26 -0
  399. package/src/activity/vault-region-pair.ts +24 -0
  400. package/src/activity/vault-region.test.ts +24 -0
  401. package/src/activity/vault-region.ts +24 -0
  402. package/src/activity/vault-suffix.test.ts +20 -0
  403. package/src/activity/vault-suffix.ts +12 -0
  404. package/src/activity/vault-wikilink.test.ts +27 -0
  405. package/src/activity/vault-wikilink.ts +28 -0
  406. package/src/activity/window-order.test.ts +27 -0
  407. package/src/activity/window-order.ts +24 -0
  408. package/src/dedup/merge-count.test.ts +19 -0
  409. package/src/dedup/merge-count.ts +13 -0
  410. package/src/dedup/merge-dup.test.ts +21 -0
  411. package/src/dedup/merge-dup.ts +16 -0
  412. package/src/dedup/merge-empty.test.ts +22 -0
  413. package/src/dedup/merge-empty.ts +18 -0
  414. package/src/dedup/merge-id.test.ts +18 -0
  415. package/src/dedup/merge-id.ts +14 -0
  416. package/src/dedup/merge-min-score.test.ts +37 -0
  417. package/src/dedup/merge-min-score.ts +22 -0
  418. package/src/dedup/merge-on-write-wire.test.ts +101 -0
  419. package/src/dedup/merge-on-write-wire.ts +34 -0
  420. package/src/dedup/merge-pair.test.ts +19 -0
  421. package/src/dedup/merge-pair.ts +13 -0
  422. package/src/dedup/merge-score.test.ts +20 -0
  423. package/src/dedup/merge-score.ts +14 -0
  424. package/src/dedup/merge-self.test.ts +19 -0
  425. package/src/dedup/merge-self.ts +17 -0
  426. package/src/dedup/merge-threshold.test.ts +46 -0
  427. package/src/dedup/merge-threshold.ts +26 -0
  428. package/src/dedup/merge-tie.test.ts +22 -0
  429. package/src/dedup/merge-tie.ts +13 -0
  430. package/src/extraction-span-bench.test.ts +27 -0
  431. package/src/extraction-span-bench.ts +28 -0
  432. package/src/extraction-span-clip.test.ts +71 -0
  433. package/src/extraction-span-clip.ts +26 -0
  434. package/src/extraction-span-contains.test.ts +30 -0
  435. package/src/extraction-span-contains.ts +20 -0
  436. package/src/extraction-span-join.test.ts +50 -0
  437. package/src/extraction-span-join.ts +30 -0
  438. package/src/extraction-span-length.test.ts +25 -0
  439. package/src/extraction-span-length.ts +16 -0
  440. package/src/extraction-span-overlap.test.ts +34 -0
  441. package/src/extraction-span-overlap.ts +23 -0
  442. package/src/extraction-span-shift.test.ts +25 -0
  443. package/src/extraction-span-shift.ts +20 -0
  444. package/src/extraction-span-slice.test.ts +32 -0
  445. package/src/extraction-span-slice.ts +20 -0
  446. package/src/extraction-span-validate.test.ts +42 -0
  447. package/src/extraction-span-validate.ts +27 -0
  448. package/src/extraction-span-wire.test.ts +20 -0
  449. package/src/extraction-span-wire.ts +17 -0
  450. package/src/extraction-span.test.ts +59 -0
  451. package/src/extraction-span.ts +39 -0
  452. package/src/index.ts +1 -0
  453. package/src/recall-deep-access.test.ts +58 -0
  454. package/src/recall-deep-access.ts +71 -0
  455. package/src/recall-deep-budget-left.test.ts +20 -0
  456. package/src/recall-deep-budget-left.ts +13 -0
  457. package/src/recall-deep-budget.test.ts +19 -0
  458. package/src/recall-deep-budget.ts +14 -0
  459. package/src/recall-deep-expand.test.ts +23 -0
  460. package/src/recall-deep-expand.ts +19 -0
  461. package/src/recall-deep-policy.test.ts +32 -0
  462. package/src/recall-deep-policy.ts +22 -0
  463. package/src/recall-deep-query.test.ts +23 -0
  464. package/src/recall-deep-query.ts +15 -0
  465. package/src/recall-deep-refine.test.ts +23 -0
  466. package/src/recall-deep-refine.ts +19 -0
  467. package/src/recall-deep-render.test.ts +68 -0
  468. package/src/recall-deep-render.ts +32 -0
  469. package/src/recall-deep-step-count.test.ts +20 -0
  470. package/src/recall-deep-step-count.ts +13 -0
  471. package/src/recall-deep-stop-reason.test.ts +46 -0
  472. package/src/recall-deep-stop-reason.ts +27 -0
  473. package/src/recall-deep-stop.test.ts +34 -0
  474. package/src/recall-deep-stop.ts +31 -0
  475. package/src/recall-deep-working-set.test.ts +21 -0
  476. package/src/recall-deep-working-set.ts +14 -0
  477. package/src/recall-navigate-access.test.ts +73 -0
  478. package/src/recall-navigate-access.ts +48 -0
  479. package/src/recall-navigate-action.test.ts +32 -0
  480. package/src/recall-navigate-action.ts +20 -0
  481. package/src/recall-navigate-budget.test.ts +23 -0
  482. package/src/recall-navigate-budget.ts +22 -0
  483. package/src/recall-navigate-cost.test.ts +20 -0
  484. package/src/recall-navigate-cost.ts +13 -0
  485. package/src/recall-navigate-depth.test.ts +20 -0
  486. package/src/recall-navigate-depth.ts +13 -0
  487. package/src/recall-navigate-link.test.ts +19 -0
  488. package/src/recall-navigate-link.ts +26 -0
  489. package/src/recall-navigate-max-children.test.ts +20 -0
  490. package/src/recall-navigate-max-children.ts +13 -0
  491. package/src/recall-navigate-node.test.ts +21 -0
  492. package/src/recall-navigate-node.ts +16 -0
  493. package/src/recall-navigate-path.test.ts +21 -0
  494. package/src/recall-navigate-path.ts +14 -0
  495. package/src/recall-navigate-render.test.ts +63 -0
  496. package/src/recall-navigate-render.ts +31 -0
  497. package/src/recall-state-view-current.test.ts +23 -0
  498. package/src/recall-state-view-current.ts +24 -0
  499. package/src/recall-state-view-inject.test.ts +32 -0
  500. package/src/recall-state-view-inject.ts +14 -0
  501. package/src/recall-state-view-kind.test.ts +19 -0
  502. package/src/recall-state-view-kind.ts +20 -0
  503. package/src/recall-state-view-label.test.ts +36 -0
  504. package/src/recall-state-view-label.ts +22 -0
  505. package/src/recall-state-view-render.test.ts +94 -0
  506. package/src/recall-state-view-render.ts +24 -0
  507. package/src/recall-state-view-successor.test.ts +21 -0
  508. package/src/recall-state-view-successor.ts +16 -0
  509. package/src/recall-state-view-superseded.test.ts +20 -0
  510. package/src/recall-state-view-superseded.ts +17 -0
  511. package/src/recall-state-view-transition.test.ts +30 -0
  512. package/src/recall-state-view-transition.ts +24 -0
  513. package/src/recall-state-view-widen.test.ts +66 -0
  514. package/src/recall-state-view-widen.ts +49 -0
  515. package/src/recall-state-view-wire.ts +6 -16
  516. package/src/shared-context/authority.test.ts +35 -0
  517. package/src/shared-context/authority.ts +25 -0
  518. package/src/shared-context/claim-key.test.ts +21 -0
  519. package/src/shared-context/claim-key.ts +16 -0
  520. package/src/shared-context/contradictions.test.ts +39 -0
  521. package/src/shared-context/contradictions.ts +32 -0
  522. package/src/shared-context/envelope-actor.test.ts +21 -0
  523. package/src/shared-context/envelope-actor.ts +16 -0
  524. package/src/shared-context/envelope-at.test.ts +19 -0
  525. package/src/shared-context/envelope-at.ts +17 -0
  526. package/src/shared-context/envelope-id.test.ts +21 -0
  527. package/src/shared-context/envelope-id.ts +16 -0
  528. package/src/shared-context/envelope-required.test.ts +24 -0
  529. package/src/shared-context/envelope-required.ts +15 -0
  530. package/src/shared-context/envelope-ttl.test.ts +29 -0
  531. package/src/shared-context/envelope-ttl.ts +35 -0
  532. package/src/shared-context/provenance.test.ts +35 -0
  533. package/src/shared-context/provenance.ts +68 -0
  534. package/src/shared-context/staleness.test.ts +36 -0
  535. package/src/shared-context/staleness.ts +38 -0
  536. package/src/shared-context/trajectories.test.ts +77 -0
  537. package/src/shared-context/trajectories.ts +49 -0
  538. package/dist/chunk-MUEGIKKY.js.map +0 -1
  539. package/dist/chunk-TCF4MU7L.js.map +0 -1
  540. package/dist/chunk-WFCWP4XU.js +0 -17
  541. package/dist/chunk-WFCWP4XU.js.map +0 -1
  542. /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
  543. /package/dist/{chunk-XKNHFDTT.js.map → chunk-3A2Z4OOS.js.map} +0 -0
  544. /package/dist/{chunk-CHGG74CY.js.map → chunk-3HHTN5X7.js.map} +0 -0
  545. /package/dist/{chunk-XOHYGIWF.js.map → chunk-3Z4SFJA5.js.map} +0 -0
  546. /package/dist/{chunk-RDGKVH6T.js.map → chunk-4NKIGSJM.js.map} +0 -0
  547. /package/dist/{chunk-GPFZFCRN.js.map → chunk-4PTTMTTQ.js.map} +0 -0
  548. /package/dist/{chunk-VG4SSPAW.js.map → chunk-4XESAT4V.js.map} +0 -0
  549. /package/dist/{chunk-S4CDUXQY.js.map → chunk-5KGLNBUB.js.map} +0 -0
  550. /package/dist/{chunk-2X6BGKQL.js.map → chunk-5XRZANCW.js.map} +0 -0
  551. /package/dist/{chunk-TCQRIIIN.js.map → chunk-6DODJYBY.js.map} +0 -0
  552. /package/dist/{chunk-KWIGDKM2.js.map → chunk-73CAEYI3.js.map} +0 -0
  553. /package/dist/{chunk-J2R275FN.js.map → chunk-7DXW5V2W.js.map} +0 -0
  554. /package/dist/{chunk-LYUUA6QB.js.map → chunk-7JJHIVMG.js.map} +0 -0
  555. /package/dist/{chunk-2FA7U5XY.js.map → chunk-7NNMRMLZ.js.map} +0 -0
  556. /package/dist/{chunk-227UFSEQ.js.map → chunk-7RZJWZ46.js.map} +0 -0
  557. /package/dist/{chunk-EWKVYCFP.js.map → chunk-B2OKCCXX.js.map} +0 -0
  558. /package/dist/{chunk-NSTNGMSS.js.map → chunk-B7AGLMGP.js.map} +0 -0
  559. /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  560. /package/dist/{chunk-CEH2AFXG.js.map → chunk-C24UZSFW.js.map} +0 -0
  561. /package/dist/{chunk-LTBKKSCX.js.map → chunk-C2JCVZWX.js.map} +0 -0
  562. /package/dist/{chunk-65JOGLIS.js.map → chunk-D4R3OUZA.js.map} +0 -0
  563. /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
  564. /package/dist/{chunk-IGZCAHFL.js.map → chunk-EGGNFLAT.js.map} +0 -0
  565. /package/dist/{chunk-H2IRSXZN.js.map → chunk-ESDOAP7W.js.map} +0 -0
  566. /package/dist/{chunk-D3UXGE5D.js.map → chunk-EZWIEFYD.js.map} +0 -0
  567. /package/dist/{chunk-ZPS724LT.js.map → chunk-F5H5QINZ.js.map} +0 -0
  568. /package/dist/{chunk-S4Z2OBTX.js.map → chunk-FPSOXMLK.js.map} +0 -0
  569. /package/dist/{chunk-CX4OLNIH.js.map → chunk-G5UKF2GJ.js.map} +0 -0
  570. /package/dist/{chunk-OJUEUQDJ.js.map → chunk-GGY2747C.js.map} +0 -0
  571. /package/dist/{chunk-TNGH4ZYI.js.map → chunk-GNWLTCKC.js.map} +0 -0
  572. /package/dist/{chunk-YWPLOJ7T.js.map → chunk-GZPGI45F.js.map} +0 -0
  573. /package/dist/{chunk-NFP4KPTA.js.map → chunk-IDEPEHUG.js.map} +0 -0
  574. /package/dist/{chunk-LJABGXC4.js.map → chunk-IN5EUWCA.js.map} +0 -0
  575. /package/dist/{chunk-U6NGKS32.js.map → chunk-JJAOVJ3E.js.map} +0 -0
  576. /package/dist/{chunk-RPIS5FQ7.js.map → chunk-JWCS3VYQ.js.map} +0 -0
  577. /package/dist/{chunk-U65VJSCJ.js.map → chunk-KE23ZIEW.js.map} +0 -0
  578. /package/dist/{chunk-NACLNNFQ.js.map → chunk-KSZKQTZS.js.map} +0 -0
  579. /package/dist/{chunk-KJXCD7VR.js.map → chunk-KWJNEWTM.js.map} +0 -0
  580. /package/dist/{chunk-UW4RYPWC.js.map → chunk-MLTGALDY.js.map} +0 -0
  581. /package/dist/{chunk-YMOPYG3K.js.map → chunk-OLT3YTDH.js.map} +0 -0
  582. /package/dist/{chunk-2PSX5BJU.js.map → chunk-OPQKVVYG.js.map} +0 -0
  583. /package/dist/{chunk-D6Q5EPOV.js.map → chunk-PGD3DXZP.js.map} +0 -0
  584. /package/dist/{chunk-GBEOUCBB.js.map → chunk-Q6LC3ZD5.js.map} +0 -0
  585. /package/dist/{chunk-QBNE55VA.js.map → chunk-QELYSDAU.js.map} +0 -0
  586. /package/dist/{chunk-INUBU6I5.js.map → chunk-S6PRJGQL.js.map} +0 -0
  587. /package/dist/{chunk-B4AOWRYF.js.map → chunk-TCJO7LOG.js.map} +0 -0
  588. /package/dist/{chunk-SZMYGDSM.js.map → chunk-TO5U47YK.js.map} +0 -0
  589. /package/dist/{chunk-GAJK3DQ5.js.map → chunk-U3L6GRUX.js.map} +0 -0
  590. /package/dist/{chunk-4XLTUR6R.js.map → chunk-U4RGSLI4.js.map} +0 -0
  591. /package/dist/{chunk-M5ERTI4I.js.map → chunk-UH53XVQ3.js.map} +0 -0
  592. /package/dist/{chunk-NUF7VSEX.js.map → chunk-UXERIBOJ.js.map} +0 -0
  593. /package/dist/{chunk-FFUMTH27.js.map → chunk-VA2CSIPC.js.map} +0 -0
  594. /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
  595. /package/dist/{chunk-AF5H7TKI.js.map → chunk-VPJNGE2R.js.map} +0 -0
  596. /package/dist/{chunk-OMX6TKXH.js.map → chunk-VTJZSLDR.js.map} +0 -0
  597. /package/dist/{chunk-TBCDF556.js.map → chunk-WHFFAS4A.js.map} +0 -0
  598. /package/dist/{chunk-7UOXCCCI.js.map → chunk-WVNETF4Q.js.map} +0 -0
  599. /package/dist/{chunk-EH65UV2Z.js.map → chunk-XSTMPZMY.js.map} +0 -0
  600. /package/dist/{chunk-PD66CVSD.js.map → chunk-Z3SQEEB6.js.map} +0 -0
  601. /package/dist/{chunk-JKTV3JNX.js.map → chunk-ZBYOXIIF.js.map} +0 -0
  602. /package/dist/{chunk-2ITZ74A6.js.map → chunk-ZC5IHL5X.js.map} +0 -0
  603. /package/dist/{chunk-KI64TTBL.js.map → chunk-ZDMTCO7N.js.map} +0 -0
  604. /package/dist/{chunk-C5IUOBCN.js.map → chunk-ZET6A3OF.js.map} +0 -0
  605. /package/dist/{chunk-ZZZWEDF4.js.map → chunk-ZVAKRZZD.js.map} +0 -0
  606. /package/dist/{graph-edge-decay-A4SDFMMS.js.map → extraction-span.js.map} +0 -0
@@ -0,0 +1,66 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { renderWeekSnapshot } from "./week-snapshot.js";
5
+
6
+ const WEEK_START = "2026-07-13";
7
+
8
+ test("empty days print heading + (empty)", () => {
9
+ assert.equal(
10
+ renderWeekSnapshot({
11
+ weekStart: WEEK_START,
12
+ timezone: "UTC",
13
+ days: [],
14
+ }),
15
+ [
16
+ "# Week snapshot",
17
+ "",
18
+ `- weekStart: ${WEEK_START}`,
19
+ "- timezone: UTC",
20
+ "",
21
+ "## Days",
22
+ "(empty)",
23
+ "",
24
+ ].join("\n"),
25
+ );
26
+ });
27
+
28
+ test("days are sorted by date regardless of input order", () => {
29
+ const markdown = renderWeekSnapshot({
30
+ weekStart: WEEK_START,
31
+ timezone: "UTC",
32
+ days: [{ date: "2026-07-15" }, { date: "2026-07-13" }, { date: "2026-07-14" }],
33
+ });
34
+ const headings = [...markdown.matchAll(/^## (.+)$/gm)].map((match) => match[1]);
35
+ assert.deepEqual(headings, ["2026-07-13", "2026-07-14", "2026-07-15"]);
36
+ assert.match(markdown, /## 2026-07-13\n\(empty\)/);
37
+ });
38
+
39
+ test("timezone is echoed verbatim, not normalized", () => {
40
+ const markdown = renderWeekSnapshot({
41
+ weekStart: WEEK_START,
42
+ timezone: "America/Chicago",
43
+ days: [],
44
+ });
45
+ assert.match(markdown, /^- timezone: America\/Chicago$/m);
46
+ assert.doesNotMatch(markdown, /America\/Chicago\/|UTC/);
47
+ });
48
+
49
+ test("input days are not mutated and reruns are stable", () => {
50
+ const input = [{ date: "2026-07-15" }, { date: "2026-07-13" }];
51
+ const first = renderWeekSnapshot({
52
+ weekStart: WEEK_START,
53
+ timezone: "UTC",
54
+ days: input,
55
+ });
56
+ const second = renderWeekSnapshot({
57
+ weekStart: WEEK_START,
58
+ timezone: "UTC",
59
+ days: input,
60
+ });
61
+ assert.deepEqual(
62
+ input.map((day) => day.date),
63
+ ["2026-07-15", "2026-07-13"],
64
+ );
65
+ assert.equal(first, second);
66
+ });
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Deterministic markdown week snapshot from daily totals (issue #2052 leftover).
3
+ *
4
+ * Pure: weekStart + timezone + days in, stable markdown out. No LLM, no I/O, no
5
+ * persistence. Days are sorted by date. Empty days print heading + (empty).
6
+ */
7
+
8
+ export interface WeekSnapshotDay {
9
+ date: string;
10
+ }
11
+
12
+ export interface WeekSnapshotOptions {
13
+ weekStart: string;
14
+ timezone: string;
15
+ days: readonly WeekSnapshotDay[];
16
+ }
17
+
18
+ function compareDates(a: WeekSnapshotDay, b: WeekSnapshotDay): number {
19
+ if (a.date < b.date) return -1;
20
+ if (a.date > b.date) return 1;
21
+ return 0;
22
+ }
23
+
24
+ /** Render a byte-stable Markdown week snapshot. */
25
+ export function renderWeekSnapshot(options: WeekSnapshotOptions): string {
26
+ const days = [...options.days].sort(compareDates);
27
+ const body =
28
+ days.length === 0
29
+ ? ["## Days", "(empty)"]
30
+ : days.flatMap((day) => [`## ${day.date}`, "(empty)", ""]);
31
+ return [
32
+ "# Week snapshot",
33
+ "",
34
+ `- weekStart: ${options.weekStart}`,
35
+ `- timezone: ${options.timezone}`,
36
+ "",
37
+ ...body,
38
+ "",
39
+ ].join("\n");
40
+ }
@@ -0,0 +1,17 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { isSundayIso } from "./week-sunday.js";
5
+
6
+ test("sunday UTC is true", () => {
7
+ assert.equal(isSundayIso("2026-07-12"), true);
8
+ });
9
+
10
+ test("monday UTC is false", () => {
11
+ assert.equal(isSundayIso("2026-07-13"), false);
12
+ });
13
+
14
+ test("invalid date throws", () => {
15
+ assert.throws(() => isSundayIso("2026-02-30"), /YYYY-MM-DD/);
16
+ assert.throws(() => isSundayIso("not-a-date"), /YYYY-MM-DD/);
17
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * UTC Sunday predicate for a YYYY-MM-DD date (issue #2052).
3
+ */
4
+ import { isValidActivityDate } from "../digest.js";
5
+
6
+ /** Parse `dateIso` as YYYY-MM-DD. Sunday UTC → true. */
7
+ export function isSundayIso(dateIso: string): boolean {
8
+ if (!isValidActivityDate(dateIso)) {
9
+ throw new RangeError(`Invalid date "${dateIso}"; expected YYYY-MM-DD.`);
10
+ }
11
+ return new Date(`${dateIso}T00:00:00Z`).getUTCDay() === 0;
12
+ }
@@ -0,0 +1,121 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, readFileSync, readdirSync, rmSync, statSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import path from "node:path";
5
+ import test from "node:test";
6
+
7
+ import { RECALL_FALLBACK_DIRS } from "../../utils/category-dir.js";
8
+ import type { TimelineCategory } from "./types.js";
9
+ import { persistWeeklySnapshot } from "./weekly-persist.js";
10
+ import { buildWeeklyActivitySummary } from "./weekly.js";
11
+
12
+ const WEEK_START = "2026-07-13T00:00:00.000Z";
13
+ const WEEK_END = "2026-07-20T00:00:00.000Z";
14
+ const OTHER_WEEK_START = "2026-07-20T00:00:00.000Z";
15
+ const OTHER_WEEK_END = "2026-07-27T00:00:00.000Z";
16
+
17
+ const CATEGORIES: TimelineCategory[] = [
18
+ { id: "development", name: "Development", color: "#333333", description: "d", order: 1 },
19
+ ];
20
+
21
+ function summary(weekStartUtc = WEEK_START, weekEndUtc = WEEK_END) {
22
+ return buildWeeklyActivitySummary([], {
23
+ timezone: "UTC",
24
+ weekStartUtc,
25
+ weekEndUtc,
26
+ categories: CATEGORIES,
27
+ });
28
+ }
29
+
30
+ function withMemoryDir(fn: (memoryDir: string) => void): void {
31
+ const memoryDir = mkdtempSync(path.join(tmpdir(), "remnic-weekly-persist-"));
32
+ try {
33
+ fn(memoryDir);
34
+ } finally {
35
+ rmSync(memoryDir, { recursive: true, force: true });
36
+ }
37
+ }
38
+
39
+ function assertOutsideRecallRoots(filePath: string, memoryDir: string): void {
40
+ const relative = path.relative(memoryDir, filePath);
41
+ assert.ok(relative.startsWith(`activity${path.sep}weekly${path.sep}`));
42
+ assert.ok(
43
+ !RECALL_FALLBACK_DIRS.some((dir) => relative === dir || relative.startsWith(`${dir}${path.sep}`)),
44
+ );
45
+ }
46
+
47
+ test("same inputs skip rewrite by content hash", () => {
48
+ withMemoryDir((memoryDir) => {
49
+ const input = {
50
+ memoryDir,
51
+ namespace: "team",
52
+ summary: summary(),
53
+ sourceRevision: "rev-1",
54
+ configHash: "cfg-1",
55
+ };
56
+ const first = persistWeeklySnapshot(input);
57
+ assert.equal(first.written, true);
58
+ assertOutsideRecallRoots(first.path, memoryDir);
59
+ const firstBytes = readFileSync(first.path, "utf8");
60
+ const firstStat = statSync(first.path);
61
+ const snapshot = JSON.parse(firstBytes) as { contentHash: string };
62
+ assert.equal(typeof snapshot.contentHash, "string");
63
+ assert.equal(snapshot.contentHash.length, 64);
64
+
65
+ const second = persistWeeklySnapshot(input);
66
+ assert.equal(second.written, false);
67
+ assert.equal(second.path, first.path);
68
+ assert.equal(readFileSync(first.path, "utf8"), firstBytes);
69
+ assert.equal(statSync(first.path).mtimeMs, firstStat.mtimeMs);
70
+ });
71
+ });
72
+
73
+ test("changed sourceRevision or configHash writes a new file and leaves other weeks", () => {
74
+ withMemoryDir((memoryDir) => {
75
+ const base = {
76
+ memoryDir,
77
+ namespace: "team",
78
+ summary: summary(),
79
+ sourceRevision: "rev-1",
80
+ configHash: "cfg-1",
81
+ };
82
+ const original = persistWeeklySnapshot(base);
83
+ const originalBytes = readFileSync(original.path, "utf8");
84
+
85
+ const revised = persistWeeklySnapshot({ ...base, sourceRevision: "rev-2" });
86
+ assert.notEqual(revised.path, original.path);
87
+ assert.equal(revised.written, true);
88
+ assert.equal(readFileSync(original.path, "utf8"), originalBytes);
89
+
90
+ const reconfigured = persistWeeklySnapshot({ ...base, configHash: "cfg-2" });
91
+ assert.notEqual(reconfigured.path, original.path);
92
+ assert.notEqual(reconfigured.path, revised.path);
93
+ assert.equal(readFileSync(original.path, "utf8"), originalBytes);
94
+
95
+ const otherWeek = persistWeeklySnapshot({
96
+ ...base,
97
+ summary: summary(OTHER_WEEK_START, OTHER_WEEK_END),
98
+ });
99
+ assert.notEqual(otherWeek.path, original.path);
100
+ assert.equal(readFileSync(original.path, "utf8"), originalBytes);
101
+ assert.equal(JSON.parse(readFileSync(otherWeek.path, "utf8")).summary.weekStartUtc, OTHER_WEEK_START);
102
+ });
103
+ });
104
+
105
+ test("snapshots are namespace scoped", () => {
106
+ withMemoryDir((memoryDir) => {
107
+ const shared = {
108
+ memoryDir,
109
+ summary: summary(),
110
+ sourceRevision: "rev-1",
111
+ configHash: "cfg-1",
112
+ };
113
+ const alpha = persistWeeklySnapshot({ ...shared, namespace: "alpha" });
114
+ const beta = persistWeeklySnapshot({ ...shared, namespace: "beta" });
115
+ assert.notEqual(alpha.path, beta.path);
116
+ assert.equal(JSON.parse(readFileSync(alpha.path, "utf8")).namespace, "alpha");
117
+ assert.equal(JSON.parse(readFileSync(beta.path, "utf8")).namespace, "beta");
118
+ assert.equal(readdirSync(path.dirname(alpha.path)).length, 1);
119
+ assert.equal(readdirSync(path.dirname(beta.path)).length, 1);
120
+ });
121
+ });
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Versioned weekly time/activity snapshot store (issue #2052).
3
+ *
4
+ * Files live at `<memoryDir>/activity/weekly/<ns>/<week>--<identity>.json`,
5
+ * outside recall fallback dirs. Same inputs skip rewrite by content hash.
6
+ * A new sourceRevision or configHash is a new file.
7
+ */
8
+ import { randomBytes } from "node:crypto";
9
+ import { mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
10
+ import path from "node:path";
11
+
12
+ import { namespaceIdentityToken } from "../../namespaces/identity.js";
13
+ import { hashActivityBody, isValidActivityDate } from "../digest.js";
14
+ import type { WeeklyActivitySummary } from "./weekly.js";
15
+
16
+ export const WEEKLY_SNAPSHOT_FORMAT_VERSION = 1;
17
+ export const WEEKLY_SNAPSHOT_DIR = path.join("activity", "weekly");
18
+
19
+ export interface PersistWeeklySnapshotInput {
20
+ memoryDir: string;
21
+ namespace: string;
22
+ summary: WeeklyActivitySummary;
23
+ sourceRevision: string;
24
+ configHash: string;
25
+ }
26
+
27
+ export interface PersistWeeklySnapshotResult {
28
+ path: string;
29
+ written: boolean;
30
+ }
31
+
32
+ function requireToken(value: string, name: string): string {
33
+ if (typeof value !== "string" || value.trim().length === 0) {
34
+ throw new TypeError(`${name} must be a non-empty string`);
35
+ }
36
+ return value.trim();
37
+ }
38
+
39
+ export function weeklySnapshotPath(input: PersistWeeklySnapshotInput): string {
40
+ const memoryDir = requireToken(input.memoryDir, "memoryDir");
41
+ const namespace = requireToken(input.namespace, "namespace");
42
+ const sourceRevision = requireToken(input.sourceRevision, "sourceRevision");
43
+ const configHash = requireToken(input.configHash, "configHash");
44
+ const weekStartUtc = input.summary?.weekStartUtc;
45
+ if (typeof weekStartUtc !== "string") {
46
+ throw new TypeError("summary.weekStartUtc must be an ISO timestamp");
47
+ }
48
+ const weekDate = weekStartUtc.slice(0, 10);
49
+ if (!isValidActivityDate(weekDate)) {
50
+ throw new RangeError("summary.weekStartUtc must be an ISO timestamp");
51
+ }
52
+ const identity = hashActivityBody(
53
+ `${input.summary.weekStartUtc}\0${input.summary.weekEndUtc}\0${sourceRevision}\0${configHash}`,
54
+ );
55
+ return path.join(
56
+ path.resolve(memoryDir),
57
+ WEEKLY_SNAPSHOT_DIR,
58
+ namespaceIdentityToken(namespace),
59
+ `${weekDate}--${identity}.json`,
60
+ );
61
+ }
62
+
63
+ /** Persist one weekly snapshot. Same bytes are a no-op. */
64
+ export function persistWeeklySnapshot(input: PersistWeeklySnapshotInput): PersistWeeklySnapshotResult {
65
+ const namespace = requireToken(input.namespace, "namespace");
66
+ const sourceRevision = requireToken(input.sourceRevision, "sourceRevision");
67
+ const configHash = requireToken(input.configHash, "configHash");
68
+ const filePath = weeklySnapshotPath(input);
69
+ const payload = {
70
+ formatVersion: WEEKLY_SNAPSHOT_FORMAT_VERSION,
71
+ namespace,
72
+ sourceRevision,
73
+ configHash,
74
+ summary: input.summary,
75
+ };
76
+ const contentHash = hashActivityBody(JSON.stringify(payload));
77
+ const text = `${JSON.stringify({ ...payload, contentHash })}\n`;
78
+
79
+ try {
80
+ if (readFileSync(filePath, "utf8") === text) {
81
+ return { path: filePath, written: false };
82
+ }
83
+ } catch (error) {
84
+ if (!(typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT")) {
85
+ throw error;
86
+ }
87
+ }
88
+
89
+ mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o700 });
90
+ const tmpPath = `${filePath}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`;
91
+ writeFileSync(tmpPath, text, { encoding: "utf8", mode: 0o600 });
92
+ try {
93
+ renameSync(tmpPath, filePath);
94
+ } catch (error) {
95
+ try {
96
+ unlinkSync(tmpPath);
97
+ } catch {
98
+ // Best-effort cleanup of the temp file.
99
+ }
100
+ throw error;
101
+ }
102
+ return { path: filePath, written: true };
103
+ }
@@ -0,0 +1,25 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { parseToMs } from "./to-ms.js";
5
+
6
+ test("toMs 0 is accepted", () => {
7
+ assert.equal(parseToMs(0), 0);
8
+ });
9
+
10
+ test("epoch toMs is accepted", () => {
11
+ const epoch = 1_700_000_000_000;
12
+ assert.equal(parseToMs(epoch), epoch);
13
+ });
14
+
15
+ test("negative toMs throws", () => {
16
+ assert.throws(() => parseToMs(-1), /non-negative/);
17
+ });
18
+
19
+ test("NaN toMs throws", () => {
20
+ assert.throws(() => parseToMs(Number.NaN), /finite/);
21
+ });
22
+
23
+ test("non-number toMs throws", () => {
24
+ assert.throws(() => parseToMs("0"), /finite/);
25
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Parse activity window toMs (issue #2053 leftover).
3
+ *
4
+ * Finite number ≥ 0 is returned. Negative, NaN, and non-number values throw.
5
+ */
6
+ export function parseToMs(value: unknown): number {
7
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
8
+ throw new RangeError(
9
+ `activity.toMs must be a finite non-negative number; got ${JSON.stringify(value)}`,
10
+ );
11
+ }
12
+ return value;
13
+ }
@@ -0,0 +1,20 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { isRegionBeginLine } from "./vault-begin.js";
5
+
6
+ test("isRegionBeginLine matches a trimmed begin comment", () => {
7
+ assert.equal(isRegionBeginLine("<!-- remnic:begin timeline -->", "timeline"), true);
8
+ assert.equal(isRegionBeginLine(" <!-- remnic:begin timeline -->\t", "timeline"), true);
9
+ });
10
+
11
+ test("isRegionBeginLine rejects a mismatch", () => {
12
+ assert.equal(isRegionBeginLine("<!-- remnic:end timeline -->", "timeline"), false);
13
+ assert.equal(isRegionBeginLine("<!-- remnic:begin weekly -->", "timeline"), false);
14
+ assert.equal(isRegionBeginLine("<!-- remnic:timeline:start -->", "timeline"), false);
15
+ assert.equal(isRegionBeginLine("<!-- remnic:begin timeline --> extra", "timeline"), false);
16
+ });
17
+
18
+ test("isRegionBeginLine throws on an empty name", () => {
19
+ assert.throws(() => isRegionBeginLine("<!-- remnic:begin -->", ""), /empty/i);
20
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Match a managed-region begin marker line (issue #1985).
3
+ *
4
+ * True iff the trimmed line equals `<!-- remnic:begin NAME -->`.
5
+ * Empty name throws.
6
+ */
7
+
8
+ export function isRegionBeginLine(line: string, name: string): boolean {
9
+ if (typeof name !== "string" || name.length === 0) {
10
+ throw new RangeError("Region name must be non-empty.");
11
+ }
12
+ return line.trim() === `<!-- remnic:begin ${name} -->`;
13
+ }
@@ -0,0 +1,20 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { isRegionEndLine } from "./vault-end.js";
5
+
6
+ test("isRegionEndLine matches a trimmed end comment", () => {
7
+ assert.equal(isRegionEndLine("<!-- remnic:end timeline -->", "timeline"), true);
8
+ assert.equal(isRegionEndLine(" <!-- remnic:end timeline -->\t", "timeline"), true);
9
+ });
10
+
11
+ test("isRegionEndLine rejects a mismatch", () => {
12
+ assert.equal(isRegionEndLine("<!-- remnic:begin timeline -->", "timeline"), false);
13
+ assert.equal(isRegionEndLine("<!-- remnic:end weekly -->", "timeline"), false);
14
+ assert.equal(isRegionEndLine("<!-- remnic:timeline:end -->", "timeline"), false);
15
+ assert.equal(isRegionEndLine("<!-- remnic:end timeline --> extra", "timeline"), false);
16
+ });
17
+
18
+ test("isRegionEndLine throws on an empty name", () => {
19
+ assert.throws(() => isRegionEndLine("<!-- remnic:end -->", ""), /empty/i);
20
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Match a managed-region end marker line (issue #1985).
3
+ *
4
+ * True iff the trimmed line equals `<!-- remnic:end NAME -->`.
5
+ * Empty name throws.
6
+ */
7
+
8
+ export function isRegionEndLine(line: string, name: string): boolean {
9
+ if (typeof name !== "string" || name.length === 0) {
10
+ throw new RangeError("Region name must be non-empty.");
11
+ }
12
+ return line.trim() === `<!-- remnic:end ${name} -->`;
13
+ }
@@ -0,0 +1,28 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { applyVaultFrontmatter } from "./vault-frontmatter.js";
5
+
6
+ test("applyVaultFrontmatter merges listed keys and sorts", () => {
7
+ const merged = applyVaultFrontmatter("zeta: old\nalpha: keep\n", {
8
+ zeta: "new",
9
+ remnic_cards: "3",
10
+ });
11
+ assert.equal(merged, "alpha: keep\nremnic_cards: 3\nzeta: new");
12
+ });
13
+
14
+ test("applyVaultFrontmatter rejects keys with newlines", () => {
15
+ assert.throws(
16
+ () => applyVaultFrontmatter("alpha: keep", { "bad\nkey": "1" }),
17
+ /newline/i,
18
+ );
19
+ assert.throws(
20
+ () => applyVaultFrontmatter("alpha: keep", { "bad\rkey": "1" }),
21
+ /newline/i,
22
+ );
23
+ });
24
+
25
+ test("applyVaultFrontmatter leaves the original body unchanged when updates are empty", () => {
26
+ const existing = "zeta: 1\n# comment stays\nalpha: 2\n";
27
+ assert.equal(applyVaultFrontmatter(existing, {}), existing);
28
+ });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Merge vault note frontmatter keys (issue #1985).
3
+ *
4
+ * Pure string helper. Empty updates leave the original YAML unchanged.
5
+ * Listed keys stay. Output keys are sorted. Keys with newlines are rejected.
6
+ */
7
+
8
+ function assertNoNewlineKey(key: string): void {
9
+ if (key.includes("\n") || key.includes("\r")) {
10
+ throw new RangeError("Vault frontmatter keys must not contain newlines.");
11
+ }
12
+ }
13
+
14
+ export function applyVaultFrontmatter(
15
+ existingYaml: string,
16
+ updates: Readonly<Record<string, string>>,
17
+ ): string {
18
+ const entries = Object.entries(updates);
19
+ if (entries.length === 0) return existingYaml;
20
+
21
+ const merged = new Map<string, string>();
22
+ for (const line of existingYaml.split("\n")) {
23
+ const idx = line.indexOf(":");
24
+ if (idx === -1) continue;
25
+ const key = line.slice(0, idx).trim();
26
+ if (key.length === 0) continue;
27
+ assertNoNewlineKey(key);
28
+ merged.set(key, line.slice(idx + 1).trim());
29
+ }
30
+ for (const [key, value] of entries) {
31
+ assertNoNewlineKey(key);
32
+ merged.set(key, value);
33
+ }
34
+
35
+ return [...merged.keys()]
36
+ .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
37
+ .map((key) => `${key}: ${merged.get(key)}`)
38
+ .join("\n");
39
+ }
@@ -0,0 +1,21 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { joinVaultSegments } from "./vault-join.js";
5
+
6
+ test("joinVaultSegments joins relative parts with /", () => {
7
+ assert.equal(
8
+ joinVaultSegments(["Daily Notes", "2026", "08", "2026-08-18.md"]),
9
+ "Daily Notes/2026/08/2026-08-18.md",
10
+ );
11
+ });
12
+
13
+ test("joinVaultSegments rejects ..", () => {
14
+ assert.throws(() => joinVaultSegments(["Daily Notes", "..", "secret"]), /\.\./);
15
+ assert.throws(() => joinVaultSegments(["foo/../bar"]), /\.\./);
16
+ });
17
+
18
+ test("joinVaultSegments rejects empty", () => {
19
+ assert.throws(() => joinVaultSegments([]), /empty/i);
20
+ assert.throws(() => joinVaultSegments(["Daily Notes", ""]), /empty/i);
21
+ });
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Join vault-relative path segments (issue #1985).
3
+ *
4
+ * Joins with `/`. Rejects an empty list, empty parts, `..`, absolute
5
+ * parts, backslash, and newline.
6
+ */
7
+ import path from "node:path";
8
+
9
+ export function joinVaultSegments(parts: readonly string[]): string {
10
+ if (parts.length === 0) {
11
+ throw new RangeError("Vault path parts must be a non-empty list.");
12
+ }
13
+ for (const part of parts) {
14
+ if (typeof part !== "string" || part.length === 0) {
15
+ throw new RangeError("Vault path part must be non-empty.");
16
+ }
17
+ if (part.includes("\\")) {
18
+ throw new RangeError("Vault path part must not contain a backslash.");
19
+ }
20
+ if (part.includes("\n") || part.includes("\r")) {
21
+ throw new RangeError("Vault path part must not contain a newline.");
22
+ }
23
+ if (path.posix.isAbsolute(part) || path.win32.isAbsolute(part)) {
24
+ throw new RangeError("Vault path part must be relative.");
25
+ }
26
+ if (part.split("/").includes("..")) {
27
+ throw new RangeError("Vault path part must not contain `..`.");
28
+ }
29
+ }
30
+ return parts.join("/");
31
+ }
@@ -0,0 +1,23 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { formatRegionMarkers } from "./vault-marker.js";
5
+
6
+ test("formatRegionMarkers returns begin and end HTML comments", () => {
7
+ assert.deepEqual(formatRegionMarkers("timeline"), {
8
+ begin: "<!-- remnic:begin timeline -->",
9
+ end: "<!-- remnic:end timeline -->",
10
+ });
11
+ });
12
+
13
+ test("formatRegionMarkers rejects an empty name", () => {
14
+ assert.throws(() => formatRegionMarkers(""), /empty/i);
15
+ });
16
+
17
+ test("formatRegionMarkers rejects a newline in the name", () => {
18
+ assert.throws(() => formatRegionMarkers("time\nline"), /newline/i);
19
+ });
20
+
21
+ test("formatRegionMarkers rejects marker text in the name", () => {
22
+ assert.throws(() => formatRegionMarkers("foo-->bar"), /-->/);
23
+ });
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Format managed-region HTML comment markers (issue #1985).
3
+ *
4
+ * Rejects empty names, newlines, and `-->` so the closer cannot leak
5
+ * out of `<!-- remnic:begin NAME -->`.
6
+ */
7
+
8
+ export type RegionMarkers = {
9
+ begin: string;
10
+ end: string;
11
+ };
12
+
13
+ export function formatRegionMarkers(name: string): RegionMarkers {
14
+ if (typeof name !== "string" || name.length === 0) {
15
+ throw new RangeError("Region name must be non-empty.");
16
+ }
17
+ if (name.includes("\n") || name.includes("\r")) {
18
+ throw new RangeError("Region name must not contain a newline.");
19
+ }
20
+ if (name.includes("-->")) {
21
+ throw new RangeError("Region name must not contain -->.");
22
+ }
23
+ return {
24
+ begin: `<!-- remnic:begin ${name} -->`,
25
+ end: `<!-- remnic:end ${name} -->`,
26
+ };
27
+ }
@@ -0,0 +1,22 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { trimRegionName } from "./vault-name-trim.js";
5
+
6
+ test("trimRegionName accepts a name", () => {
7
+ assert.equal(trimRegionName("timeline"), "timeline");
8
+ });
9
+
10
+ test("trimRegionName rejects an empty name", () => {
11
+ assert.throws(() => trimRegionName(""), /empty/i);
12
+ assert.throws(() => trimRegionName(" "), /empty/i);
13
+ });
14
+
15
+ test("trimRegionName rejects a newline in the name", () => {
16
+ assert.throws(() => trimRegionName("time\nline"), /newline/i);
17
+ assert.throws(() => trimRegionName("time\rline"), /newline/i);
18
+ });
19
+
20
+ test("trimRegionName trims surrounding whitespace", () => {
21
+ assert.equal(trimRegionName(" timeline "), "timeline");
22
+ });