@remnic/core 9.66.2 → 9.66.4

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 (547) hide show
  1. package/dist/access-admin-ops-surface.js +31 -31
  2. package/dist/access-audit.js +2 -2
  3. package/dist/access-authorization-probe.js +32 -32
  4. package/dist/access-boundary.js +31 -31
  5. package/dist/access-cli.js +97 -97
  6. package/dist/access-extraction-force-flush.js +31 -31
  7. package/dist/access-http-query.js +33 -33
  8. package/dist/access-http.js +49 -49
  9. package/dist/access-identity-continuity-surface.js +31 -31
  10. package/dist/access-lcm-surface.js +31 -31
  11. package/dist/access-mcp.js +39 -39
  12. package/dist/access-namespace-preflight.js +31 -31
  13. package/dist/access-observe-write-surface.js +31 -31
  14. package/dist/access-offline-manifest.js +3 -3
  15. package/dist/access-operations-batch.js +34 -34
  16. package/dist/access-operations.js +38 -38
  17. package/dist/access-recall-concurrency.js +31 -31
  18. package/dist/access-recall-response.js +31 -31
  19. package/dist/access-recall-surface.js +31 -31
  20. package/dist/access-schema.js +5 -5
  21. package/dist/access-service.js +31 -31
  22. package/dist/access-surface-catalog.js +33 -33
  23. package/dist/active-recall.js +5 -5
  24. package/dist/adapters/index.js +7 -7
  25. package/dist/adapters/registry.js +3 -3
  26. package/dist/briefing.js +8 -8
  27. package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-GWVG7LGC.js} +2 -2
  28. package/dist/causal-behavior.js +2 -2
  29. package/dist/causal-chain.js +2 -2
  30. package/dist/causal-consolidation.js +11 -11
  31. package/dist/causal-retrieval.js +2 -2
  32. package/dist/causal-trajectory-graph.js +2 -2
  33. package/dist/causal-trajectory.js +1 -1
  34. package/dist/{chunk-36HJ36FK.js → chunk-27JH2KKM.js} +3 -3
  35. package/dist/{chunk-NZPFCP5A.js → chunk-2PSX5BJU.js} +2 -2
  36. package/dist/{chunk-RCPKBW7K.js → chunk-322MSJYS.js} +5 -5
  37. package/dist/{chunk-C3ZYZKDJ.js → chunk-3E4REX7E.js} +2 -2
  38. package/dist/{chunk-Y2YM4C75.js → chunk-4BRBSON5.js} +2 -2
  39. package/dist/{chunk-B4B3QDV3.js → chunk-4EX26ZS3.js} +24 -24
  40. package/dist/{chunk-ER2UPUMG.js → chunk-562Q236T.js} +2 -2
  41. package/dist/{chunk-F2JXJABQ.js → chunk-5BIOXQGN.js} +2 -2
  42. package/dist/{chunk-DSEHPUID.js → chunk-5C3OQ253.js} +2 -2
  43. package/dist/{chunk-LSR5LQXN.js → chunk-5VLXQFOS.js} +2 -2
  44. package/dist/{chunk-2QSGJTDG.js → chunk-636UUVOZ.js} +10 -10
  45. package/dist/{chunk-AI5M4FVQ.js → chunk-6DTQSLER.js} +2 -2
  46. package/dist/{chunk-CDLPF3PX.js → chunk-6IL5FFHH.js} +2 -2
  47. package/dist/{chunk-VKDBDJIX.js → chunk-6L6V2OIX.js} +3 -3
  48. package/dist/{chunk-ZXROBS53.js → chunk-6NA527W2.js} +48 -48
  49. package/dist/{chunk-R2Q6FTOX.js → chunk-7IEFFOIC.js} +2 -2
  50. package/dist/{chunk-4PG2CKI6.js → chunk-AQHDMA4A.js} +2 -2
  51. package/dist/{chunk-RKNJBZ55.js → chunk-AU7Q3LSC.js} +7 -7
  52. package/dist/{chunk-6BYJQ3L2.js → chunk-BE4Y6GK6.js} +8 -8
  53. package/dist/{chunk-PW3GKWH3.js → chunk-BQVWC67C.js} +4 -4
  54. package/dist/{chunk-KQAFEZQX.js → chunk-BUKK5SWA.js} +2 -2
  55. package/dist/{chunk-UVYL5G7A.js → chunk-C3LUYZLX.js} +3 -3
  56. package/dist/{chunk-JMXHVJ2V.js → chunk-DFWO37KT.js} +14 -14
  57. package/dist/{chunk-WKWY7YF3.js → chunk-F5FHDDOV.js} +2 -2
  58. package/dist/{chunk-U4HZEW2B.js → chunk-FH4BB7F4.js} +7 -7
  59. package/dist/{chunk-UHQE37NI.js → chunk-G5TYMG7R.js} +2 -2
  60. package/dist/{chunk-PP7R754B.js → chunk-GPFZFCRN.js} +1 -1
  61. package/dist/{chunk-EH7BNIUN.js → chunk-HAF3PUMO.js} +3 -3
  62. package/dist/{chunk-J2R275FN.js → chunk-HQJM3BKH.js} +2 -2
  63. package/dist/{chunk-O7ERGZDA.js → chunk-I7NBTRLR.js} +2 -2
  64. package/dist/{chunk-6545JDIM.js → chunk-IHK5KTOC.js} +2 -2
  65. package/dist/{chunk-25OLNWYG.js → chunk-IP7KIW7W.js} +2 -2
  66. package/dist/{chunk-XJJYSX6F.js → chunk-JCUG7AXZ.js} +4 -4
  67. package/dist/{chunk-OERZXNSK.js → chunk-JGXMA6HL.js} +2 -2
  68. package/dist/{chunk-CQ6IWZDI.js → chunk-JVDAIASW.js} +2 -2
  69. package/dist/{chunk-F4W5HTED.js → chunk-K3CQQNMX.js} +2 -2
  70. package/dist/{chunk-D2YNNDML.js → chunk-KWIGDKM2.js} +2 -2
  71. package/dist/{chunk-227UFSEQ.js → chunk-LEPOP46W.js} +4 -4
  72. package/dist/{chunk-OLBZ2WGG.js → chunk-LTBKKSCX.js} +2 -2
  73. package/dist/{chunk-VSMSYDIH.js → chunk-MARZW7DB.js} +2 -2
  74. package/dist/chunk-MKD3OMPS.js +20 -0
  75. package/dist/chunk-MKD3OMPS.js.map +1 -0
  76. package/dist/{chunk-W5IYHQA5.js → chunk-MS2I7JVN.js} +3 -3
  77. package/dist/{chunk-BLHKR4GJ.js → chunk-NHACZAM3.js} +2 -2
  78. package/dist/{chunk-2VZHB5US.js → chunk-NTZ3452G.js} +2 -2
  79. package/dist/{chunk-65HXVEVG.js → chunk-OT3GGOFW.js} +2 -2
  80. package/dist/{chunk-2NLLXCJG.js → chunk-OWHERGF2.js} +2 -2
  81. package/dist/{chunk-OUKBMQ2O.js → chunk-PD66CVSD.js} +2 -2
  82. package/dist/{chunk-EQRD4NMH.js → chunk-PMMNP5GZ.js} +2 -2
  83. package/dist/{chunk-47WWTCJY.js → chunk-RNRDH4ZO.js} +308 -94
  84. package/dist/chunk-RNRDH4ZO.js.map +1 -0
  85. package/dist/{chunk-PRRUK2ZH.js → chunk-RNVXKTIA.js} +2 -2
  86. package/dist/{chunk-ZBIEX4XN.js → chunk-SACAOBDB.js} +1 -1
  87. package/dist/{chunk-JJ6ZEFXC.js → chunk-SNFWXG5G.js} +2 -2
  88. package/dist/{chunk-NTFXBILO.js → chunk-SY7ASLC6.js} +5 -5
  89. package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
  90. package/dist/{chunk-X7Y7WX73.js → chunk-UVYI6VIX.js} +1 -1
  91. package/dist/{chunk-YZ5VC2JJ.js → chunk-VVWPY5U7.js} +2 -2
  92. package/dist/{chunk-OIDICRH7.js → chunk-W7O7PH5E.js} +2 -2
  93. package/dist/{chunk-RH3XLQG4.js → chunk-WTRFM4SF.js} +2 -2
  94. package/dist/{chunk-6PUF7LUO.js → chunk-Y5U72X3D.js} +2 -2
  95. package/dist/{chunk-FFWONAAW.js → chunk-Y7LFWOX6.js} +3 -3
  96. package/dist/{chunk-D3DUA64S.js → chunk-YJDKSDIU.js} +2 -2
  97. package/dist/{chunk-BIJAWZYW.js → chunk-YMOPYG3K.js} +6 -6
  98. package/dist/{chunk-ZMVW2HJJ.js → chunk-YUYARGKM.js} +2 -2
  99. package/dist/{chunk-UMZUKOJU.js → chunk-Z3YEGSR4.js} +4 -4
  100. package/dist/{chunk-D6Q5EPOV.js → chunk-ZYKNDRUJ.js} +3 -3
  101. package/dist/cli.js +73 -73
  102. package/dist/coding/export-okf-codegraph.js +8 -8
  103. package/dist/coding/pre-action-gate.js +2 -2
  104. package/dist/compounding/engine.js +8 -8
  105. package/dist/config.js +5 -5
  106. package/dist/connectors/codex-materialize-runner.js +8 -8
  107. package/dist/connectors/index.js +8 -8
  108. package/dist/convergence-refresh.js +10 -10
  109. package/dist/conversation-index/backend.js +2 -2
  110. package/dist/corpus-watermark.js +10 -10
  111. package/dist/entity-retrieval.js +8 -8
  112. package/dist/external-wiki-access.js +32 -32
  113. package/dist/extraction-span-bench.d.ts +14 -0
  114. package/dist/extraction-span-bench.js +17 -0
  115. package/dist/extraction-span-bench.js.map +1 -0
  116. package/dist/extraction-span-clip.d.ts +22 -0
  117. package/dist/extraction-span-clip.js +19 -0
  118. package/dist/extraction-span-clip.js.map +1 -0
  119. package/dist/extraction-span-contains.d.ts +12 -0
  120. package/dist/extraction-span-contains.js +17 -0
  121. package/dist/extraction-span-contains.js.map +1 -0
  122. package/dist/extraction-span-gap.d.ts +14 -0
  123. package/dist/extraction-span-gap.js +18 -0
  124. package/dist/extraction-span-gap.js.map +1 -0
  125. package/dist/extraction-span-join.d.ts +23 -0
  126. package/dist/extraction-span-join.js +19 -0
  127. package/dist/extraction-span-join.js.map +1 -0
  128. package/dist/extraction-span-length.d.ts +11 -0
  129. package/dist/extraction-span-length.js +17 -0
  130. package/dist/extraction-span-length.js.map +1 -0
  131. package/dist/extraction-span-overlap.d.ts +14 -0
  132. package/dist/extraction-span-overlap.js +16 -0
  133. package/dist/extraction-span-overlap.js.map +1 -0
  134. package/dist/extraction-span-shift.d.ts +15 -0
  135. package/dist/extraction-span-shift.js +17 -0
  136. package/dist/extraction-span-shift.js.map +1 -0
  137. package/dist/extraction-span-slice.d.ts +12 -0
  138. package/dist/extraction-span-slice.js +17 -0
  139. package/dist/extraction-span-slice.js.map +1 -0
  140. package/dist/extraction-span-validate.d.ts +20 -0
  141. package/dist/extraction-span-validate.js +20 -0
  142. package/dist/extraction-span-validate.js.map +1 -0
  143. package/dist/extraction.js +8 -8
  144. package/dist/{graph-edge-decay-FXGELBFA.js → graph-edge-decay-A4SDFMMS.js} +4 -4
  145. package/dist/graph-snapshot.js +3 -3
  146. package/dist/graph.js +3 -3
  147. package/dist/harmonic-retrieval.js +1 -1
  148. package/dist/index.d.ts +181 -1
  149. package/dist/index.js +140 -112
  150. package/dist/lcm/engine.js +2 -2
  151. package/dist/lcm/index.js +7 -7
  152. package/dist/location/index.js +10 -10
  153. package/dist/maintenance/memory-governance.js +8 -8
  154. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +8 -8
  155. package/dist/maintenance/rebuild-memory-projection.js +9 -9
  156. package/dist/namespaces/migrate.js +9 -9
  157. package/dist/namespaces/search.js +6 -6
  158. package/dist/namespaces/storage.js +8 -8
  159. package/dist/offline-sync.js +2 -2
  160. package/dist/operator-doctor-corpus.js +11 -11
  161. package/dist/operator-toolkit.js +23 -23
  162. package/dist/orchestration/maintenance.js +16 -16
  163. package/dist/orchestrator.js +97 -97
  164. package/dist/recall-deep-budget-left.d.ts +8 -0
  165. package/dist/recall-deep-budget-left.js +15 -0
  166. package/dist/recall-deep-budget-left.js.map +1 -0
  167. package/dist/recall-deep-budget.d.ts +9 -0
  168. package/dist/recall-deep-budget.js +15 -0
  169. package/dist/recall-deep-budget.js.map +1 -0
  170. package/dist/recall-deep-expand.d.ts +12 -0
  171. package/dist/recall-deep-expand.js +17 -0
  172. package/dist/recall-deep-expand.js.map +1 -0
  173. package/dist/recall-deep-policy.d.ts +17 -0
  174. package/dist/recall-deep-policy.js +18 -0
  175. package/dist/recall-deep-policy.js.map +1 -0
  176. package/dist/recall-deep-query.d.ts +15 -0
  177. package/dist/recall-deep-query.js +12 -0
  178. package/dist/recall-deep-query.js.map +1 -0
  179. package/dist/recall-deep-refine.d.ts +12 -0
  180. package/dist/recall-deep-refine.js +17 -0
  181. package/dist/recall-deep-refine.js.map +1 -0
  182. package/dist/recall-deep-step-count.d.ts +8 -0
  183. package/dist/recall-deep-step-count.js +15 -0
  184. package/dist/recall-deep-step-count.js.map +1 -0
  185. package/dist/recall-deep-stop-reason.d.ts +17 -0
  186. package/dist/recall-deep-stop-reason.js +23 -0
  187. package/dist/recall-deep-stop-reason.js.map +1 -0
  188. package/dist/recall-deep-stop.d.ts +17 -0
  189. package/dist/recall-deep-stop.js +23 -0
  190. package/dist/recall-deep-stop.js.map +1 -0
  191. package/dist/recall-deep-working-set.d.ts +9 -0
  192. package/dist/recall-deep-working-set.js +14 -0
  193. package/dist/recall-deep-working-set.js.map +1 -0
  194. package/dist/recall-navigate-access.d.ts +39 -0
  195. package/dist/recall-navigate-access.js +31 -0
  196. package/dist/recall-navigate-access.js.map +1 -0
  197. package/dist/recall-navigate-action.d.ts +17 -0
  198. package/dist/recall-navigate-action.js +14 -0
  199. package/dist/recall-navigate-action.js.map +1 -0
  200. package/dist/recall-navigate-budget.d.ts +16 -0
  201. package/dist/recall-navigate-budget.js +18 -0
  202. package/dist/recall-navigate-budget.js.map +1 -0
  203. package/dist/recall-navigate-cost.d.ts +8 -0
  204. package/dist/recall-navigate-cost.js +15 -0
  205. package/dist/recall-navigate-cost.js.map +1 -0
  206. package/dist/recall-navigate-depth.d.ts +8 -0
  207. package/dist/recall-navigate-depth.js +15 -0
  208. package/dist/recall-navigate-depth.js.map +1 -0
  209. package/dist/recall-navigate-link.d.ts +17 -0
  210. package/dist/recall-navigate-link.js +20 -0
  211. package/dist/recall-navigate-link.js.map +1 -0
  212. package/dist/recall-navigate-max-children.d.ts +8 -0
  213. package/dist/recall-navigate-max-children.js +15 -0
  214. package/dist/recall-navigate-max-children.js.map +1 -0
  215. package/dist/recall-navigate-node.d.ts +15 -0
  216. package/dist/recall-navigate-node.js +13 -0
  217. package/dist/recall-navigate-node.js.map +1 -0
  218. package/dist/recall-navigate-path.d.ts +9 -0
  219. package/dist/recall-navigate-path.js +14 -0
  220. package/dist/recall-navigate-path.js.map +1 -0
  221. package/dist/recall-navigate-render.d.ts +15 -0
  222. package/dist/recall-navigate-render.js +23 -0
  223. package/dist/recall-navigate-render.js.map +1 -0
  224. package/dist/recall-state-view-current.d.ts +18 -0
  225. package/dist/recall-state-view-current.js +14 -0
  226. package/dist/recall-state-view-current.js.map +1 -0
  227. package/dist/recall-state-view-inject.d.ts +15 -0
  228. package/dist/recall-state-view-inject.js +16 -0
  229. package/dist/recall-state-view-inject.js.map +1 -0
  230. package/dist/recall-state-view-kind.d.ts +17 -0
  231. package/dist/recall-state-view-kind.js +14 -0
  232. package/dist/recall-state-view-kind.js.map +1 -0
  233. package/dist/recall-state-view-label.d.ts +17 -0
  234. package/dist/recall-state-view-label.js +16 -0
  235. package/dist/recall-state-view-label.js.map +1 -0
  236. package/dist/recall-state-view-render.js +3 -14
  237. package/dist/recall-state-view-render.js.map +1 -1
  238. package/dist/recall-state-view-successor.d.ts +15 -0
  239. package/dist/recall-state-view-successor.js +13 -0
  240. package/dist/recall-state-view-successor.js.map +1 -0
  241. package/dist/recall-state-view-superseded.d.ts +15 -0
  242. package/dist/recall-state-view-superseded.js +14 -0
  243. package/dist/recall-state-view-superseded.js.map +1 -0
  244. package/dist/recall-state-view-transition.d.ts +18 -0
  245. package/dist/recall-state-view-transition.js +14 -0
  246. package/dist/recall-state-view-transition.js.map +1 -0
  247. package/dist/resume-bundles.js +6 -6
  248. package/dist/search/factory.js +5 -5
  249. package/dist/search/index.js +9 -9
  250. package/dist/semantic-consolidation.js +9 -9
  251. package/dist/semantic-rule-promotion.js +8 -8
  252. package/dist/semantic-rule-verifier.js +8 -8
  253. package/dist/source-agent-qualifier.js +8 -8
  254. package/dist/storage.js +7 -7
  255. package/dist/support-passport/index.js +34 -34
  256. package/dist/transfer/backup.js +2 -2
  257. package/dist/transfer/capsule-export.js +2 -2
  258. package/dist/transfer/capsule-import.js +2 -2
  259. package/dist/transfer/export-okf.js +9 -9
  260. package/dist/verified-recall.js +8 -8
  261. package/dist/who-knows-cli.js +9 -9
  262. package/dist/who-knows.js +8 -8
  263. package/package.json +2 -2
  264. package/src/activity/dwell.test.ts +28 -0
  265. package/src/activity/dwell.ts +22 -0
  266. package/src/activity/from-ms.test.ts +25 -0
  267. package/src/activity/from-ms.ts +13 -0
  268. package/src/activity/heading-level.test.ts +23 -0
  269. package/src/activity/heading-level.ts +21 -0
  270. package/src/activity/heading-title.test.ts +21 -0
  271. package/src/activity/heading-title.ts +26 -0
  272. package/src/activity/index.ts +6 -0
  273. package/src/activity/journal-heading.test.ts +22 -0
  274. package/src/activity/journal-heading.ts +18 -0
  275. package/src/activity/journal-mode.test.ts +32 -0
  276. package/src/activity/journal-mode.ts +20 -0
  277. package/src/activity/journal-path.test.ts +25 -0
  278. package/src/activity/journal-path.ts +27 -0
  279. package/src/activity/journal-section.test.ts +37 -0
  280. package/src/activity/journal-section.ts +75 -0
  281. package/src/activity/journal-source.test.ts +37 -0
  282. package/src/activity/journal-source.ts +23 -0
  283. package/src/activity/privacy-enabled.test.ts +30 -0
  284. package/src/activity/privacy-enabled.ts +33 -0
  285. package/src/activity/privacy-keys.test.ts +22 -0
  286. package/src/activity/privacy-keys.ts +12 -0
  287. package/src/activity/privacy-redact.test.ts +25 -0
  288. package/src/activity/privacy-redact.ts +23 -0
  289. package/src/activity/privacy-window.test.ts +49 -0
  290. package/src/activity/privacy-window.ts +32 -0
  291. package/src/activity/retain-days.test.ts +20 -0
  292. package/src/activity/retain-days.ts +14 -0
  293. package/src/activity/timeline/analysis-claim.test.ts +33 -0
  294. package/src/activity/timeline/analysis-claim.ts +16 -0
  295. package/src/activity/timeline/analysis-dup.test.ts +28 -0
  296. package/src/activity/timeline/analysis-dup.ts +10 -0
  297. package/src/activity/timeline/analysis-empty.test.ts +21 -0
  298. package/src/activity/timeline/analysis-empty.ts +14 -0
  299. package/src/activity/timeline/analysis-evidence.test.ts +32 -0
  300. package/src/activity/timeline/analysis-evidence.ts +20 -0
  301. package/src/activity/timeline/analysis-max.test.ts +26 -0
  302. package/src/activity/timeline/analysis-max.ts +14 -0
  303. package/src/activity/timeline/analysis-order.test.ts +28 -0
  304. package/src/activity/timeline/analysis-order.ts +10 -0
  305. package/src/activity/timeline/analysis-overlap.test.ts +26 -0
  306. package/src/activity/timeline/analysis-overlap.ts +17 -0
  307. package/src/activity/timeline/analysis-prompt.test.ts +50 -0
  308. package/src/activity/timeline/analysis-prompt.ts +24 -0
  309. package/src/activity/timeline/index.ts +8 -0
  310. package/src/activity/timeline/recap-card-count.test.ts +19 -0
  311. package/src/activity/timeline/recap-card-count.ts +13 -0
  312. package/src/activity/timeline/recap-card-id.test.ts +21 -0
  313. package/src/activity/timeline/recap-card-id.ts +16 -0
  314. package/src/activity/timeline/recap-date.test.ts +21 -0
  315. package/src/activity/timeline/recap-date.ts +19 -0
  316. package/src/activity/timeline/recap-empty.test.ts +21 -0
  317. package/src/activity/timeline/recap-empty.ts +14 -0
  318. package/src/activity/timeline/recap-export.test.ts +78 -0
  319. package/src/activity/timeline/recap-export.ts +41 -0
  320. package/src/activity/timeline/recap-id.test.ts +28 -0
  321. package/src/activity/timeline/recap-id.ts +10 -0
  322. package/src/activity/timeline/recap-markdown.test.ts +56 -0
  323. package/src/activity/timeline/recap-markdown.ts +26 -0
  324. package/src/activity/timeline/recap-sort-key.test.ts +18 -0
  325. package/src/activity/timeline/recap-sort-key.ts +14 -0
  326. package/src/activity/timeline/recap-tz.test.ts +35 -0
  327. package/src/activity/timeline/recap-tz.ts +19 -0
  328. package/src/activity/timeline/week-count.test.ts +19 -0
  329. package/src/activity/timeline/week-count.ts +13 -0
  330. package/src/activity/timeline/week-days.test.ts +26 -0
  331. package/src/activity/timeline/week-days.ts +23 -0
  332. package/src/activity/timeline/week-export.test.ts +59 -0
  333. package/src/activity/timeline/week-export.ts +44 -0
  334. package/src/activity/timeline/week-iso.test.ts +29 -0
  335. package/src/activity/timeline/week-iso.ts +20 -0
  336. package/src/activity/timeline/week-monday.test.ts +17 -0
  337. package/src/activity/timeline/week-monday.ts +12 -0
  338. package/src/activity/timeline/week-range.test.ts +23 -0
  339. package/src/activity/timeline/week-range.ts +26 -0
  340. package/src/activity/timeline/week-snapshot.test.ts +66 -0
  341. package/src/activity/timeline/week-snapshot.ts +40 -0
  342. package/src/activity/timeline/week-span.test.ts +21 -0
  343. package/src/activity/timeline/week-span.ts +18 -0
  344. package/src/activity/timeline/week-sunday.test.ts +17 -0
  345. package/src/activity/timeline/week-sunday.ts +12 -0
  346. package/src/activity/to-ms.test.ts +25 -0
  347. package/src/activity/to-ms.ts +13 -0
  348. package/src/activity/vault-begin.test.ts +20 -0
  349. package/src/activity/vault-begin.ts +13 -0
  350. package/src/activity/vault-end.test.ts +20 -0
  351. package/src/activity/vault-end.ts +13 -0
  352. package/src/activity/vault-frontmatter.test.ts +28 -0
  353. package/src/activity/vault-frontmatter.ts +39 -0
  354. package/src/activity/vault-join.test.ts +21 -0
  355. package/src/activity/vault-join.ts +31 -0
  356. package/src/activity/vault-marker.test.ts +23 -0
  357. package/src/activity/vault-marker.ts +27 -0
  358. package/src/activity/vault-name-trim.test.ts +22 -0
  359. package/src/activity/vault-name-trim.ts +16 -0
  360. package/src/activity/vault-region-pair.test.ts +26 -0
  361. package/src/activity/vault-region-pair.ts +24 -0
  362. package/src/activity/vault-region.test.ts +24 -0
  363. package/src/activity/vault-region.ts +24 -0
  364. package/src/activity/vault-suffix.test.ts +20 -0
  365. package/src/activity/vault-suffix.ts +12 -0
  366. package/src/activity/vault-wikilink.test.ts +27 -0
  367. package/src/activity/vault-wikilink.ts +28 -0
  368. package/src/activity/window-order.test.ts +27 -0
  369. package/src/activity/window-order.ts +24 -0
  370. package/src/dedup/merge-count.test.ts +19 -0
  371. package/src/dedup/merge-count.ts +13 -0
  372. package/src/dedup/merge-dup.test.ts +21 -0
  373. package/src/dedup/merge-dup.ts +16 -0
  374. package/src/dedup/merge-empty.test.ts +22 -0
  375. package/src/dedup/merge-empty.ts +18 -0
  376. package/src/dedup/merge-id.test.ts +18 -0
  377. package/src/dedup/merge-id.ts +14 -0
  378. package/src/dedup/merge-min-score.test.ts +37 -0
  379. package/src/dedup/merge-min-score.ts +22 -0
  380. package/src/dedup/merge-pair.test.ts +19 -0
  381. package/src/dedup/merge-pair.ts +13 -0
  382. package/src/dedup/merge-score.test.ts +20 -0
  383. package/src/dedup/merge-score.ts +14 -0
  384. package/src/dedup/merge-self.test.ts +19 -0
  385. package/src/dedup/merge-self.ts +17 -0
  386. package/src/dedup/merge-threshold.test.ts +46 -0
  387. package/src/dedup/merge-threshold.ts +26 -0
  388. package/src/dedup/merge-tie.test.ts +22 -0
  389. package/src/dedup/merge-tie.ts +13 -0
  390. package/src/extraction-span-bench.test.ts +27 -0
  391. package/src/extraction-span-bench.ts +28 -0
  392. package/src/extraction-span-clip.test.ts +71 -0
  393. package/src/extraction-span-clip.ts +26 -0
  394. package/src/extraction-span-contains.test.ts +30 -0
  395. package/src/extraction-span-contains.ts +20 -0
  396. package/src/extraction-span-gap.test.ts +29 -0
  397. package/src/extraction-span-gap.ts +25 -0
  398. package/src/extraction-span-join.test.ts +50 -0
  399. package/src/extraction-span-join.ts +30 -0
  400. package/src/extraction-span-length.test.ts +25 -0
  401. package/src/extraction-span-length.ts +16 -0
  402. package/src/extraction-span-overlap.test.ts +34 -0
  403. package/src/extraction-span-overlap.ts +23 -0
  404. package/src/extraction-span-shift.test.ts +25 -0
  405. package/src/extraction-span-shift.ts +20 -0
  406. package/src/extraction-span-slice.test.ts +32 -0
  407. package/src/extraction-span-slice.ts +20 -0
  408. package/src/extraction-span-validate.test.ts +42 -0
  409. package/src/extraction-span-validate.ts +27 -0
  410. package/src/recall-deep-budget-left.test.ts +20 -0
  411. package/src/recall-deep-budget-left.ts +13 -0
  412. package/src/recall-deep-budget.test.ts +19 -0
  413. package/src/recall-deep-budget.ts +14 -0
  414. package/src/recall-deep-expand.test.ts +23 -0
  415. package/src/recall-deep-expand.ts +19 -0
  416. package/src/recall-deep-policy.test.ts +32 -0
  417. package/src/recall-deep-policy.ts +22 -0
  418. package/src/recall-deep-query.test.ts +23 -0
  419. package/src/recall-deep-query.ts +15 -0
  420. package/src/recall-deep-refine.test.ts +23 -0
  421. package/src/recall-deep-refine.ts +19 -0
  422. package/src/recall-deep-step-count.test.ts +20 -0
  423. package/src/recall-deep-step-count.ts +13 -0
  424. package/src/recall-deep-stop-reason.test.ts +46 -0
  425. package/src/recall-deep-stop-reason.ts +27 -0
  426. package/src/recall-deep-stop.test.ts +34 -0
  427. package/src/recall-deep-stop.ts +31 -0
  428. package/src/recall-deep-working-set.test.ts +21 -0
  429. package/src/recall-deep-working-set.ts +14 -0
  430. package/src/recall-navigate-access.test.ts +73 -0
  431. package/src/recall-navigate-access.ts +48 -0
  432. package/src/recall-navigate-action.test.ts +32 -0
  433. package/src/recall-navigate-action.ts +20 -0
  434. package/src/recall-navigate-budget.test.ts +23 -0
  435. package/src/recall-navigate-budget.ts +22 -0
  436. package/src/recall-navigate-cost.test.ts +20 -0
  437. package/src/recall-navigate-cost.ts +13 -0
  438. package/src/recall-navigate-depth.test.ts +20 -0
  439. package/src/recall-navigate-depth.ts +13 -0
  440. package/src/recall-navigate-link.test.ts +19 -0
  441. package/src/recall-navigate-link.ts +26 -0
  442. package/src/recall-navigate-max-children.test.ts +20 -0
  443. package/src/recall-navigate-max-children.ts +13 -0
  444. package/src/recall-navigate-node.test.ts +21 -0
  445. package/src/recall-navigate-node.ts +16 -0
  446. package/src/recall-navigate-path.test.ts +21 -0
  447. package/src/recall-navigate-path.ts +14 -0
  448. package/src/recall-navigate-render.test.ts +63 -0
  449. package/src/recall-navigate-render.ts +31 -0
  450. package/src/recall-state-view-current.test.ts +23 -0
  451. package/src/recall-state-view-current.ts +24 -0
  452. package/src/recall-state-view-inject.test.ts +32 -0
  453. package/src/recall-state-view-inject.ts +14 -0
  454. package/src/recall-state-view-kind.test.ts +19 -0
  455. package/src/recall-state-view-kind.ts +20 -0
  456. package/src/recall-state-view-label.test.ts +36 -0
  457. package/src/recall-state-view-label.ts +22 -0
  458. package/src/recall-state-view-successor.test.ts +21 -0
  459. package/src/recall-state-view-successor.ts +16 -0
  460. package/src/recall-state-view-superseded.test.ts +20 -0
  461. package/src/recall-state-view-superseded.ts +17 -0
  462. package/src/recall-state-view-transition.test.ts +30 -0
  463. package/src/recall-state-view-transition.ts +24 -0
  464. package/src/shared-context/authority.test.ts +35 -0
  465. package/src/shared-context/authority.ts +25 -0
  466. package/src/shared-context/claim-key.test.ts +21 -0
  467. package/src/shared-context/claim-key.ts +16 -0
  468. package/src/shared-context/contradictions.test.ts +39 -0
  469. package/src/shared-context/contradictions.ts +32 -0
  470. package/src/shared-context/envelope-actor.test.ts +21 -0
  471. package/src/shared-context/envelope-actor.ts +16 -0
  472. package/src/shared-context/envelope-at.test.ts +19 -0
  473. package/src/shared-context/envelope-at.ts +17 -0
  474. package/src/shared-context/envelope-id.test.ts +21 -0
  475. package/src/shared-context/envelope-id.ts +16 -0
  476. package/src/shared-context/envelope-required.test.ts +24 -0
  477. package/src/shared-context/envelope-required.ts +15 -0
  478. package/src/shared-context/envelope-ttl.test.ts +29 -0
  479. package/src/shared-context/envelope-ttl.ts +35 -0
  480. package/src/shared-context/provenance.test.ts +35 -0
  481. package/src/shared-context/provenance.ts +68 -0
  482. package/dist/chunk-47WWTCJY.js.map +0 -1
  483. /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
  484. /package/dist/{chunk-36HJ36FK.js.map → chunk-27JH2KKM.js.map} +0 -0
  485. /package/dist/{chunk-NZPFCP5A.js.map → chunk-2PSX5BJU.js.map} +0 -0
  486. /package/dist/{chunk-RCPKBW7K.js.map → chunk-322MSJYS.js.map} +0 -0
  487. /package/dist/{chunk-C3ZYZKDJ.js.map → chunk-3E4REX7E.js.map} +0 -0
  488. /package/dist/{chunk-Y2YM4C75.js.map → chunk-4BRBSON5.js.map} +0 -0
  489. /package/dist/{chunk-B4B3QDV3.js.map → chunk-4EX26ZS3.js.map} +0 -0
  490. /package/dist/{chunk-ER2UPUMG.js.map → chunk-562Q236T.js.map} +0 -0
  491. /package/dist/{chunk-F2JXJABQ.js.map → chunk-5BIOXQGN.js.map} +0 -0
  492. /package/dist/{chunk-DSEHPUID.js.map → chunk-5C3OQ253.js.map} +0 -0
  493. /package/dist/{chunk-LSR5LQXN.js.map → chunk-5VLXQFOS.js.map} +0 -0
  494. /package/dist/{chunk-2QSGJTDG.js.map → chunk-636UUVOZ.js.map} +0 -0
  495. /package/dist/{chunk-AI5M4FVQ.js.map → chunk-6DTQSLER.js.map} +0 -0
  496. /package/dist/{chunk-CDLPF3PX.js.map → chunk-6IL5FFHH.js.map} +0 -0
  497. /package/dist/{chunk-VKDBDJIX.js.map → chunk-6L6V2OIX.js.map} +0 -0
  498. /package/dist/{chunk-ZXROBS53.js.map → chunk-6NA527W2.js.map} +0 -0
  499. /package/dist/{chunk-R2Q6FTOX.js.map → chunk-7IEFFOIC.js.map} +0 -0
  500. /package/dist/{chunk-4PG2CKI6.js.map → chunk-AQHDMA4A.js.map} +0 -0
  501. /package/dist/{chunk-RKNJBZ55.js.map → chunk-AU7Q3LSC.js.map} +0 -0
  502. /package/dist/{chunk-6BYJQ3L2.js.map → chunk-BE4Y6GK6.js.map} +0 -0
  503. /package/dist/{chunk-PW3GKWH3.js.map → chunk-BQVWC67C.js.map} +0 -0
  504. /package/dist/{chunk-KQAFEZQX.js.map → chunk-BUKK5SWA.js.map} +0 -0
  505. /package/dist/{chunk-UVYL5G7A.js.map → chunk-C3LUYZLX.js.map} +0 -0
  506. /package/dist/{chunk-JMXHVJ2V.js.map → chunk-DFWO37KT.js.map} +0 -0
  507. /package/dist/{chunk-WKWY7YF3.js.map → chunk-F5FHDDOV.js.map} +0 -0
  508. /package/dist/{chunk-U4HZEW2B.js.map → chunk-FH4BB7F4.js.map} +0 -0
  509. /package/dist/{chunk-UHQE37NI.js.map → chunk-G5TYMG7R.js.map} +0 -0
  510. /package/dist/{chunk-PP7R754B.js.map → chunk-GPFZFCRN.js.map} +0 -0
  511. /package/dist/{chunk-EH7BNIUN.js.map → chunk-HAF3PUMO.js.map} +0 -0
  512. /package/dist/{chunk-J2R275FN.js.map → chunk-HQJM3BKH.js.map} +0 -0
  513. /package/dist/{chunk-O7ERGZDA.js.map → chunk-I7NBTRLR.js.map} +0 -0
  514. /package/dist/{chunk-6545JDIM.js.map → chunk-IHK5KTOC.js.map} +0 -0
  515. /package/dist/{chunk-25OLNWYG.js.map → chunk-IP7KIW7W.js.map} +0 -0
  516. /package/dist/{chunk-XJJYSX6F.js.map → chunk-JCUG7AXZ.js.map} +0 -0
  517. /package/dist/{chunk-OERZXNSK.js.map → chunk-JGXMA6HL.js.map} +0 -0
  518. /package/dist/{chunk-CQ6IWZDI.js.map → chunk-JVDAIASW.js.map} +0 -0
  519. /package/dist/{chunk-F4W5HTED.js.map → chunk-K3CQQNMX.js.map} +0 -0
  520. /package/dist/{chunk-D2YNNDML.js.map → chunk-KWIGDKM2.js.map} +0 -0
  521. /package/dist/{chunk-227UFSEQ.js.map → chunk-LEPOP46W.js.map} +0 -0
  522. /package/dist/{chunk-OLBZ2WGG.js.map → chunk-LTBKKSCX.js.map} +0 -0
  523. /package/dist/{chunk-VSMSYDIH.js.map → chunk-MARZW7DB.js.map} +0 -0
  524. /package/dist/{chunk-W5IYHQA5.js.map → chunk-MS2I7JVN.js.map} +0 -0
  525. /package/dist/{chunk-BLHKR4GJ.js.map → chunk-NHACZAM3.js.map} +0 -0
  526. /package/dist/{chunk-2VZHB5US.js.map → chunk-NTZ3452G.js.map} +0 -0
  527. /package/dist/{chunk-65HXVEVG.js.map → chunk-OT3GGOFW.js.map} +0 -0
  528. /package/dist/{chunk-2NLLXCJG.js.map → chunk-OWHERGF2.js.map} +0 -0
  529. /package/dist/{chunk-OUKBMQ2O.js.map → chunk-PD66CVSD.js.map} +0 -0
  530. /package/dist/{chunk-EQRD4NMH.js.map → chunk-PMMNP5GZ.js.map} +0 -0
  531. /package/dist/{chunk-PRRUK2ZH.js.map → chunk-RNVXKTIA.js.map} +0 -0
  532. /package/dist/{chunk-ZBIEX4XN.js.map → chunk-SACAOBDB.js.map} +0 -0
  533. /package/dist/{chunk-JJ6ZEFXC.js.map → chunk-SNFWXG5G.js.map} +0 -0
  534. /package/dist/{chunk-NTFXBILO.js.map → chunk-SY7ASLC6.js.map} +0 -0
  535. /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
  536. /package/dist/{chunk-X7Y7WX73.js.map → chunk-UVYI6VIX.js.map} +0 -0
  537. /package/dist/{chunk-YZ5VC2JJ.js.map → chunk-VVWPY5U7.js.map} +0 -0
  538. /package/dist/{chunk-OIDICRH7.js.map → chunk-W7O7PH5E.js.map} +0 -0
  539. /package/dist/{chunk-RH3XLQG4.js.map → chunk-WTRFM4SF.js.map} +0 -0
  540. /package/dist/{chunk-6PUF7LUO.js.map → chunk-Y5U72X3D.js.map} +0 -0
  541. /package/dist/{chunk-FFWONAAW.js.map → chunk-Y7LFWOX6.js.map} +0 -0
  542. /package/dist/{chunk-D3DUA64S.js.map → chunk-YJDKSDIU.js.map} +0 -0
  543. /package/dist/{chunk-BIJAWZYW.js.map → chunk-YMOPYG3K.js.map} +0 -0
  544. /package/dist/{chunk-ZMVW2HJJ.js.map → chunk-YUYARGKM.js.map} +0 -0
  545. /package/dist/{chunk-UMZUKOJU.js.map → chunk-Z3YEGSR4.js.map} +0 -0
  546. /package/dist/{chunk-D6Q5EPOV.js.map → chunk-ZYKNDRUJ.js.map} +0 -0
  547. /package/dist/{graph-edge-decay-FXGELBFA.js.map → graph-edge-decay-A4SDFMMS.js.map} +0 -0
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Deterministic Markdown recap from timeline cards (issue #2051 leftover).
3
+ *
4
+ * Pure: date + timezone + cards in, Markdown out. No LLM, no I/O, no
5
+ * persistence. Cards are sorted by id. Empty cards print (empty).
6
+ */
7
+ import type { TimelineCard } from "./types.js";
8
+
9
+ export interface RecapMarkdownOptions {
10
+ date: string;
11
+ timezone: string;
12
+ cards: readonly TimelineCard[];
13
+ }
14
+
15
+ function compareCardIds(a: TimelineCard, b: TimelineCard): number {
16
+ if (a.id < b.id) return -1;
17
+ if (a.id > b.id) return 1;
18
+ return 0;
19
+ }
20
+
21
+ /** Render a byte-stable Markdown recap for one local day. */
22
+ export function renderRecapMarkdown(options: RecapMarkdownOptions): string {
23
+ const cards = [...options.cards].sort(compareCardIds);
24
+ const body = cards.length === 0 ? ["(empty)"] : cards.map((card) => `- ${card.id}`);
25
+ return [`# Recap — ${options.date} (${options.timezone})`, "", ...body, ""].join("\n");
26
+ }
@@ -0,0 +1,18 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { recapSortKey } from "./recap-sort-key.js";
5
+
6
+ test("ok card id is the sort key", () => {
7
+ assert.equal(recapSortKey({ id: "card-1" }), "card-1");
8
+ });
9
+
10
+ test("missing id throws", () => {
11
+ assert.throws(() => recapSortKey({}), /missing recap card id/);
12
+ assert.throws(() => recapSortKey({ id: undefined }), /missing recap card id/);
13
+ assert.throws(() => recapSortKey({ id: null }), /missing recap card id/);
14
+ });
15
+
16
+ test("empty id throws", () => {
17
+ assert.throws(() => recapSortKey({ id: "" }), /empty recap card id/);
18
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Recap card sort key (issue #2051 leftover).
3
+ *
4
+ * Returns card.id. Missing or empty id throws.
5
+ */
6
+ export function recapSortKey(card: { id?: string | null }): string {
7
+ if (card.id == null) {
8
+ throw new Error("missing recap card id");
9
+ }
10
+ if (card.id === "") {
11
+ throw new Error("empty recap card id");
12
+ }
13
+ return card.id;
14
+ }
@@ -0,0 +1,35 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { normalizeRecapTimezone } from "./recap-tz.js";
5
+
6
+ test("accepts a trimmed IANA-shaped timezone", () => {
7
+ assert.deepEqual(normalizeRecapTimezone("America/Chicago"), {
8
+ ok: true,
9
+ timezone: "America/Chicago",
10
+ });
11
+ });
12
+
13
+ test("empty and whitespace-only values are missing_timezone", () => {
14
+ assert.deepEqual(normalizeRecapTimezone(""), { ok: false, error: "missing_timezone" });
15
+ assert.deepEqual(normalizeRecapTimezone(" "), { ok: false, error: "missing_timezone" });
16
+ });
17
+
18
+ test("rejects characters outside the bounded class", () => {
19
+ assert.deepEqual(normalizeRecapTimezone("America/Chicago!"), {
20
+ ok: false,
21
+ error: "invalid_timezone",
22
+ });
23
+ assert.deepEqual(normalizeRecapTimezone("America Chicago"), {
24
+ ok: false,
25
+ error: "invalid_timezone",
26
+ });
27
+ assert.deepEqual(normalizeRecapTimezone("A".repeat(65)), {
28
+ ok: false,
29
+ error: "invalid_timezone",
30
+ });
31
+ });
32
+
33
+ test("trims surrounding whitespace before validating", () => {
34
+ assert.deepEqual(normalizeRecapTimezone(" UTC "), { ok: true, timezone: "UTC" });
35
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Normalize a recap timezone string (issue #2051 leftover).
3
+ *
4
+ * Trim first. Empty → missing_timezone. Bounded charset only (no ReDoS).
5
+ */
6
+
7
+ export type RecapTimezoneResult =
8
+ | { ok: true; timezone: string }
9
+ | { ok: false; error: "missing_timezone" | "invalid_timezone" };
10
+
11
+ const RECAP_TIMEZONE = /^[A-Za-z0-9_+/-]{1,64}$/;
12
+
13
+ /** Trim and accept an IANA-shaped timezone, or return a typed error. */
14
+ export function normalizeRecapTimezone(value: string): RecapTimezoneResult {
15
+ const timezone = value.trim();
16
+ if (timezone.length === 0) return { ok: false, error: "missing_timezone" };
17
+ if (!RECAP_TIMEZONE.test(timezone)) return { ok: false, error: "invalid_timezone" };
18
+ return { ok: true, timezone };
19
+ }
@@ -0,0 +1,19 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { countWeekDays } from "./week-count.js";
5
+
6
+ test("empty list is 0", () => {
7
+ assert.equal(countWeekDays([]), 0);
8
+ });
9
+
10
+ test("counts unique YYYY-MM-DD dates", () => {
11
+ assert.equal(countWeekDays(["2026-08-17", "2026-08-18", "2026-08-17"]), 2);
12
+ assert.equal(countWeekDays(["2026-08-17", "2026-08-18", "2026-08-19"]), 3);
13
+ });
14
+
15
+ test("drops empty strings and does not mutate", () => {
16
+ const dates = ["2026-08-17", "", "2026-08-18", "", "2026-08-17"];
17
+ assert.equal(countWeekDays(dates), 2);
18
+ assert.deepEqual(dates, ["2026-08-17", "", "2026-08-18", "", "2026-08-17"]);
19
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Week-day counter (issue #2052 leftover).
3
+ *
4
+ * Unique YYYY-MM-DD strings after dropping empty strings. Does not mutate input.
5
+ */
6
+
7
+ export function countWeekDays(dates: readonly string[]): number {
8
+ const unique = new Set<string>();
9
+ for (const date of dates) {
10
+ if (date !== "") unique.add(date);
11
+ }
12
+ return unique.size;
13
+ }
@@ -0,0 +1,26 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { listWeekDates } from "./week-days.js";
5
+
6
+ test("monday week is seven inclusive dates from start", () => {
7
+ assert.deepEqual(listWeekDates({ weekStartIso: "2026-07-13" }), [
8
+ "2026-07-13",
9
+ "2026-07-14",
10
+ "2026-07-15",
11
+ "2026-07-16",
12
+ "2026-07-17",
13
+ "2026-07-18",
14
+ "2026-07-19",
15
+ ]);
16
+ });
17
+
18
+ test("invalid date throws", () => {
19
+ assert.throws(() => listWeekDates({ weekStartIso: "2026-02-30" }), /YYYY-MM-DD/);
20
+ assert.throws(() => listWeekDates({ weekStartIso: "not-a-date" }), /YYYY-MM-DD/);
21
+ });
22
+
23
+ test("week always has length 7", () => {
24
+ assert.equal(listWeekDates({ weekStartIso: "2026-07-13" }).length, 7);
25
+ assert.equal(listWeekDates({ weekStartIso: "2026-01-28" }).length, 7);
26
+ });
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Inclusive 7-day week from a YYYY-MM-DD start (issue #2052).
3
+ */
4
+ import { isValidActivityDate } from "../digest.js";
5
+
6
+ export interface WeekDaysInput {
7
+ weekStartIso: string;
8
+ }
9
+
10
+ /** Parse `weekStartIso` as YYYY-MM-DD. Return `[start, start+6]`. */
11
+ export function listWeekDates({ weekStartIso }: WeekDaysInput): string[] {
12
+ if (!isValidActivityDate(weekStartIso)) {
13
+ throw new RangeError(`Invalid week start "${weekStartIso}"; expected YYYY-MM-DD.`);
14
+ }
15
+ const start = new Date(`${weekStartIso}T00:00:00Z`);
16
+ const dates: string[] = [];
17
+ for (let offset = 0; offset < 7; offset++) {
18
+ const day = new Date(start);
19
+ day.setUTCDate(start.getUTCDate() + offset);
20
+ dates.push(day.toISOString().slice(0, 10));
21
+ }
22
+ return dates;
23
+ }
@@ -0,0 +1,59 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { exportDeterministicWeek } from "./week-export.js";
5
+
6
+ const WEEK_START = "2026-07-13";
7
+
8
+ test("empty days export weekStart and timezone with an empty day list", () => {
9
+ const week = exportDeterministicWeek({
10
+ weekStart: WEEK_START,
11
+ timezone: "UTC",
12
+ days: [],
13
+ });
14
+ assert.deepEqual(week, { weekStart: WEEK_START, timezone: "UTC", days: [] });
15
+ assert.equal(
16
+ JSON.stringify(week),
17
+ `{"weekStart":"${WEEK_START}","timezone":"UTC","days":[]}`,
18
+ );
19
+ });
20
+
21
+ test("days are sorted by date regardless of input order", () => {
22
+ const week = exportDeterministicWeek({
23
+ weekStart: WEEK_START,
24
+ timezone: "UTC",
25
+ days: [{ date: "2026-07-15" }, { date: "2026-07-13" }, { date: "2026-07-14" }],
26
+ });
27
+ assert.deepEqual(
28
+ week.days.map((day) => day.date),
29
+ ["2026-07-13", "2026-07-14", "2026-07-15"],
30
+ );
31
+ });
32
+
33
+ test("timezone is echoed verbatim, not normalized", () => {
34
+ const week = exportDeterministicWeek({
35
+ weekStart: WEEK_START,
36
+ timezone: "America/Chicago",
37
+ days: [],
38
+ });
39
+ assert.equal(week.timezone, "America/Chicago");
40
+ });
41
+
42
+ test("input days are not mutated and reruns are stable", () => {
43
+ const input = [{ date: "2026-07-15" }, { date: "2026-07-13" }];
44
+ const first = exportDeterministicWeek({
45
+ weekStart: WEEK_START,
46
+ timezone: "UTC",
47
+ days: input,
48
+ });
49
+ const second = exportDeterministicWeek({
50
+ weekStart: WEEK_START,
51
+ timezone: "UTC",
52
+ days: input,
53
+ });
54
+ assert.deepEqual(
55
+ input.map((day) => day.date),
56
+ ["2026-07-15", "2026-07-13"],
57
+ );
58
+ assert.equal(JSON.stringify(first), JSON.stringify(second));
59
+ });
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Deterministic machine-readable week export from daily totals (issue #2052).
3
+ *
4
+ * Pure: weekStart + timezone + days in, stable JSON out. No LLM, no I/O, no
5
+ * persistence. Same days always serialize to the same bytes, regardless of
6
+ * input array order: days are sorted by date before serialization.
7
+ */
8
+
9
+ export interface DeterministicWeekDay {
10
+ date: string;
11
+ }
12
+
13
+ export interface DeterministicWeekOptions {
14
+ weekStart: string;
15
+ timezone: string;
16
+ days: readonly DeterministicWeekDay[];
17
+ }
18
+
19
+ export interface DeterministicWeek {
20
+ weekStart: string;
21
+ timezone: string;
22
+ days: DeterministicWeekDay[];
23
+ }
24
+
25
+ function compareDates(a: DeterministicWeekDay, b: DeterministicWeekDay): number {
26
+ if (a.date < b.date) return -1;
27
+ if (a.date > b.date) return 1;
28
+ return 0;
29
+ }
30
+
31
+ /**
32
+ * Build the byte-stable JSON week document.
33
+ * Callers may `JSON.stringify` the returned object; construction order
34
+ * (weekStart, timezone, days) is fixed, so serialization is deterministic.
35
+ */
36
+ export function exportDeterministicWeek(
37
+ options: DeterministicWeekOptions,
38
+ ): DeterministicWeek {
39
+ return {
40
+ weekStart: options.weekStart,
41
+ timezone: options.timezone,
42
+ days: [...options.days].sort(compareDates),
43
+ };
44
+ }
@@ -0,0 +1,29 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { parseIsoDate } from "./week-iso.js";
5
+
6
+ test("accepts a trimmed YYYY-MM-DD date", () => {
7
+ assert.deepEqual(parseIsoDate("2026-08-19"), { ok: true, date: "2026-08-19" });
8
+ assert.deepEqual(parseIsoDate(" 2026-08-19 "), { ok: true, date: "2026-08-19" });
9
+ });
10
+
11
+ test("empty and whitespace-only values are missing_date", () => {
12
+ assert.deepEqual(parseIsoDate(""), { ok: false, error: "missing_date" });
13
+ assert.deepEqual(parseIsoDate(" "), { ok: false, error: "missing_date" });
14
+ });
15
+
16
+ test("rejects strings that are not YYYY-MM-DD", () => {
17
+ assert.deepEqual(parseIsoDate("20260819"), { ok: false, error: "invalid_date" });
18
+ assert.deepEqual(parseIsoDate("2026-8-19"), { ok: false, error: "invalid_date" });
19
+ assert.deepEqual(parseIsoDate("2026-08-19T00:00:00Z"), { ok: false, error: "invalid_date" });
20
+ assert.deepEqual(parseIsoDate("not-a-date"), { ok: false, error: "invalid_date" });
21
+ });
22
+
23
+ test("rejects impossible calendar days", () => {
24
+ assert.deepEqual(parseIsoDate("2026-02-30"), { ok: false, error: "invalid_date" });
25
+ assert.deepEqual(parseIsoDate("2026-13-01"), { ok: false, error: "invalid_date" });
26
+ assert.deepEqual(parseIsoDate("2026-00-10"), { ok: false, error: "invalid_date" });
27
+ // Real leap day still passes the round-trip.
28
+ assert.deepEqual(parseIsoDate("2024-02-29"), { ok: true, date: "2024-02-29" });
29
+ });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Parse a YYYY-MM-DD week date (issue #2052 leftover).
3
+ *
4
+ * Trim first. Empty → missing_date. Real YYYY-MM-DD calendar days only
5
+ * (no ReDoS, no impossible dates like 2026-02-30).
6
+ */
7
+
8
+ import { isValidActivityDate } from "../digest.js";
9
+
10
+ export type IsoDateResult =
11
+ | { ok: true; date: string }
12
+ | { ok: false; error: "missing_date" | "invalid_date" };
13
+
14
+ /** Trim and accept a real YYYY-MM-DD calendar day, or return a typed error. */
15
+ export function parseIsoDate(value: string): IsoDateResult {
16
+ const date = value.trim();
17
+ if (date.length === 0) return { ok: false, error: "missing_date" };
18
+ if (!isValidActivityDate(date)) return { ok: false, error: "invalid_date" };
19
+ return { ok: true, date };
20
+ }
@@ -0,0 +1,17 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { isMondayIso } from "./week-monday.js";
5
+
6
+ test("monday UTC is true", () => {
7
+ assert.equal(isMondayIso("2026-07-13"), true);
8
+ });
9
+
10
+ test("tuesday UTC is false", () => {
11
+ assert.equal(isMondayIso("2026-07-14"), false);
12
+ });
13
+
14
+ test("invalid date throws", () => {
15
+ assert.throws(() => isMondayIso("2026-02-30"), /YYYY-MM-DD/);
16
+ assert.throws(() => isMondayIso("not-a-date"), /YYYY-MM-DD/);
17
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * UTC Monday predicate for a YYYY-MM-DD date (issue #2052).
3
+ */
4
+ import { isValidActivityDate } from "../digest.js";
5
+
6
+ /** Parse `dateIso` as YYYY-MM-DD. Monday UTC → true. */
7
+ export function isMondayIso(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() === 1;
12
+ }
@@ -0,0 +1,23 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { weekRange } from "./week-range.js";
5
+
6
+ test("monday week is [start, start+7)", () => {
7
+ assert.deepEqual(weekRange({ weekStartIso: "2026-07-13" }), {
8
+ start: "2026-07-13",
9
+ endExclusive: "2026-07-20",
10
+ });
11
+ });
12
+
13
+ test("invalid date throws", () => {
14
+ assert.throws(() => weekRange({ weekStartIso: "2026-02-30" }), /YYYY-MM-DD/);
15
+ assert.throws(() => weekRange({ weekStartIso: "not-a-date" }), /YYYY-MM-DD/);
16
+ });
17
+
18
+ test("exclusive end is the next week start", () => {
19
+ const first = weekRange({ weekStartIso: "2026-07-13" });
20
+ const next = weekRange({ weekStartIso: first.endExclusive });
21
+ assert.equal(next.start, first.endExclusive);
22
+ assert.equal(next.endExclusive, "2026-07-27");
23
+ });
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Half-open week range from a YYYY-MM-DD start (issue #2052).
3
+ */
4
+ import { isValidActivityDate } from "../digest.js";
5
+
6
+ export interface WeekRangeInput {
7
+ weekStartIso: string;
8
+ }
9
+
10
+ export interface WeekRange {
11
+ start: string;
12
+ endExclusive: string;
13
+ }
14
+
15
+ /** Parse `weekStartIso` as YYYY-MM-DD. Return `[start, start+7)`. */
16
+ export function weekRange({ weekStartIso }: WeekRangeInput): WeekRange {
17
+ if (!isValidActivityDate(weekStartIso)) {
18
+ throw new RangeError(`Invalid week start "${weekStartIso}"; expected YYYY-MM-DD.`);
19
+ }
20
+ const parsed = new Date(`${weekStartIso}T00:00:00Z`);
21
+ parsed.setUTCDate(parsed.getUTCDate() + 7);
22
+ return {
23
+ start: weekStartIso,
24
+ endExclusive: parsed.toISOString().slice(0, 10),
25
+ };
26
+ }
@@ -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,21 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { daysBetweenIso } from "./week-span.js";
5
+
6
+ test("seven-day week is [start, end)", () => {
7
+ assert.equal(daysBetweenIso("2026-07-13", "2026-07-20"), 7);
8
+ });
9
+
10
+ test("same day throws", () => {
11
+ assert.throws(() => daysBetweenIso("2026-07-13", "2026-07-13"), /end/);
12
+ });
13
+
14
+ test("inverted range throws", () => {
15
+ assert.throws(() => daysBetweenIso("2026-07-20", "2026-07-13"), /end/);
16
+ });
17
+
18
+ test("invalid date throws", () => {
19
+ assert.throws(() => daysBetweenIso("2026-02-30", "2026-03-02"), /YYYY-MM-DD/);
20
+ assert.throws(() => daysBetweenIso("2026-07-13", "not-a-date"), /YYYY-MM-DD/);
21
+ });
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Half-open day span between two YYYY-MM-DD dates (issue #2052 leftover).
3
+ *
4
+ * Inclusive start, exclusive end. Invalid dates throw. end<=start throws.
5
+ */
6
+ import { isValidActivityDate } from "../digest.js";
7
+
8
+ export function daysBetweenIso(startIso: string, endIso: string): number {
9
+ if (!isValidActivityDate(startIso) || !isValidActivityDate(endIso)) {
10
+ throw new RangeError(`Invalid date; expected YYYY-MM-DD.`);
11
+ }
12
+ const start = Date.parse(`${startIso}T00:00:00Z`);
13
+ const end = Date.parse(`${endIso}T00:00:00Z`);
14
+ if (end <= start) {
15
+ throw new RangeError("end must be after start");
16
+ }
17
+ return (end - start) / 86_400_000;
18
+ }
@@ -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,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
+ }