@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,32 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { boundEvidence } from "./analysis-evidence.js";
5
+
6
+ function claim(observationId: number, title: string) {
7
+ return { observationId, title };
8
+ }
9
+
10
+ function observation(id: number) {
11
+ return { id };
12
+ }
13
+
14
+ test("drops claims whose observationId is missing", () => {
15
+ const kept = boundEvidence(
16
+ [claim(1, "first"), claim(99, "ghost"), claim(2, "second")],
17
+ [observation(1), observation(2)],
18
+ );
19
+ assert.deepEqual(kept, [claim(1, "first"), claim(2, "second")]);
20
+ });
21
+
22
+ test("keeps claim order", () => {
23
+ const kept = boundEvidence(
24
+ [claim(3, "third"), claim(1, "first"), claim(2, "second")],
25
+ [observation(1), observation(2), observation(3)],
26
+ );
27
+ assert.deepEqual(kept, [claim(3, "third"), claim(1, "first"), claim(2, "second")]);
28
+ });
29
+
30
+ test("empty observations return no claims", () => {
31
+ assert.deepEqual(boundEvidence([claim(1, "first")], []), []);
32
+ });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Drop analysis claims that do not cite a supplied observation (issue #2050).
3
+ */
4
+ export interface TimelineEvidenceClaim {
5
+ observationId: number;
6
+ }
7
+
8
+ export interface TimelineEvidenceObservation {
9
+ id: number;
10
+ }
11
+
12
+ /** Keep claims whose observationId is present. Preserve claim order. */
13
+ export function boundEvidence<T extends TimelineEvidenceClaim>(
14
+ claims: readonly T[],
15
+ observations: readonly TimelineEvidenceObservation[],
16
+ ): T[] {
17
+ if (observations.length === 0) return [];
18
+ const ids = new Set(observations.map((observation) => observation.id));
19
+ return claims.filter((claim) => ids.has(claim.observationId));
20
+ }
@@ -0,0 +1,26 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { parseMaxBatch } from "./analysis-max.js";
5
+
6
+ test("maxBatch 0 is allowed", () => {
7
+ assert.equal(parseMaxBatch(0), 0);
8
+ });
9
+
10
+ test("maxBatch 8 is returned", () => {
11
+ assert.equal(parseMaxBatch(8), 8);
12
+ });
13
+
14
+ test("negative maxBatch throws", () => {
15
+ assert.throws(() => parseMaxBatch(-1), /non-negative integer/);
16
+ });
17
+
18
+ test("float maxBatch throws", () => {
19
+ assert.throws(() => parseMaxBatch(1.5), /non-negative integer/);
20
+ });
21
+
22
+ test("non-number maxBatch throws RangeError", () => {
23
+ for (const bad of ["8", undefined, Number.NaN, null]) {
24
+ assert.throws(() => parseMaxBatch(bad), RangeError);
25
+ }
26
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Timeline analysis max-batch parse (issue #2050 leftover).
3
+ *
4
+ * Pure. 0 is allowed (empty batches). Negative or non-integer throws.
5
+ * Finite integer ≥ 0 is returned.
6
+ */
7
+ export function parseMaxBatch(value: unknown): number {
8
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value < 0) {
9
+ throw new RangeError(
10
+ `analysis maxBatch must be a non-negative integer; got ${JSON.stringify(value)}`,
11
+ );
12
+ }
13
+ return value;
14
+ }
@@ -0,0 +1,28 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { sortObservationIds } from "./analysis-order.js";
5
+
6
+ test("empty input returns an empty array", () => {
7
+ assert.deepEqual(sortObservationIds([]), []);
8
+ });
9
+
10
+ test("sorts ids with localeCompare", () => {
11
+ assert.deepEqual(sortObservationIds(["obs-c", "obs-a", "obs-b"]), [
12
+ "obs-a",
13
+ "obs-b",
14
+ "obs-c",
15
+ ]);
16
+ });
17
+
18
+ test("does not mutate the input array", () => {
19
+ const input = ["obs-c", "obs-a"];
20
+ const snapshot = input.slice();
21
+ const sorted = sortObservationIds(input);
22
+ assert.deepEqual(input, snapshot);
23
+ assert.notEqual(sorted, input);
24
+ });
25
+
26
+ test("drops empty strings", () => {
27
+ assert.deepEqual(sortObservationIds(["b", "", "a", ""]), ["a", "b"]);
28
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Stable observation-id order for timeline analysis (issue #2050 leftover).
3
+ *
4
+ * localeCompare sort. Empty input → []. Does not mutate input. Drops empty strings.
5
+ */
6
+
7
+ /** Return a new localeCompare-sorted id list. Empty strings are dropped. */
8
+ export function sortObservationIds(ids: readonly string[]): string[] {
9
+ return ids.filter((id) => id !== "").sort((a, b) => a.localeCompare(b));
10
+ }
@@ -0,0 +1,26 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { parseOverlap } from "./analysis-overlap.js";
5
+
6
+ test("overlap 0 is allowed", () => {
7
+ assert.equal(parseOverlap(0, 8), 0);
8
+ assert.equal(parseOverlap(0, 0), 0);
9
+ });
10
+
11
+ test("overlap 3 with maxBatch 8 is returned", () => {
12
+ assert.equal(parseOverlap(3, 8), 3);
13
+ });
14
+
15
+ test("overlap >= maxBatch throws when maxBatch > 0", () => {
16
+ assert.throws(() => parseOverlap(8, 8), /less than maxBatch/);
17
+ assert.throws(() => parseOverlap(5, 4), /less than maxBatch/);
18
+ });
19
+
20
+ test("negative overlap throws", () => {
21
+ assert.throws(() => parseOverlap(-1, 8), /non-negative integer/);
22
+ });
23
+
24
+ test("float overlap throws", () => {
25
+ assert.throws(() => parseOverlap(1.5, 8), /non-negative integer/);
26
+ });
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Timeline analysis overlap parse (issue #2050 leftover).
3
+ *
4
+ * Pure. 0 is allowed. Negative or non-integer throws.
5
+ * When maxBatch > 0, overlap must be less than maxBatch.
6
+ */
7
+ export function parseOverlap(value: unknown, maxBatch: number): number {
8
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value) || value < 0) {
9
+ throw new RangeError(
10
+ `analysis overlap must be a non-negative integer; got ${JSON.stringify(value)}`,
11
+ );
12
+ }
13
+ if (maxBatch > 0 && value >= maxBatch) {
14
+ throw new RangeError(`analysis overlap must be less than maxBatch; got ${value} >= ${maxBatch}`);
15
+ }
16
+ return value;
17
+ }
@@ -0,0 +1,50 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { buildAnalysisPrompt } from "./analysis-prompt.js";
5
+
6
+ test("empty observations render (empty)", () => {
7
+ const first = buildAnalysisPrompt({ observations: [] });
8
+ const second = buildAnalysisPrompt({ observations: [] });
9
+ assert.match(first, /\(empty\)/);
10
+ assert.equal(first, second);
11
+ assert.doesNotMatch(first, /\[\s*\{/);
12
+ });
13
+
14
+ test("observations sort by id, not input order", () => {
15
+ const prompt = buildAnalysisPrompt({
16
+ observations: [
17
+ { id: 3, capturedAtUtc: "2026-08-17T12:00:00.000Z" },
18
+ { id: 1, capturedAtUtc: "2026-08-17T10:00:00.000Z" },
19
+ { id: 2, capturedAtUtc: "2026-08-17T11:00:00.000Z" },
20
+ ],
21
+ });
22
+ const first = prompt.indexOf('"id":1');
23
+ const second = prompt.indexOf('"id":2');
24
+ const third = prompt.indexOf('"id":3');
25
+ assert.ok(first >= 0 && second > first && third > second);
26
+ assert.match(prompt, /2026-08-17T10:00:00.000Z/);
27
+ assert.match(prompt, /2026-08-17T11:00:00.000Z/);
28
+ assert.match(prompt, /2026-08-17T12:00:00.000Z/);
29
+ });
30
+
31
+ test("free-text body and extra fields do not leak", () => {
32
+ const prompt = buildAnalysisPrompt({
33
+ observations: [
34
+ {
35
+ id: 9,
36
+ capturedAtUtc: "2026-08-17T09:00:00.000Z",
37
+ body: "SECRET-BODY",
38
+ text: "SECRET-TEXT",
39
+ windowTitle: "SECRET-TITLE",
40
+ app: "SECRET-APP",
41
+ } as { id: number; capturedAtUtc: string },
42
+ ],
43
+ });
44
+ assert.equal(prompt.includes("SECRET-BODY"), false);
45
+ assert.equal(prompt.includes("SECRET-TEXT"), false);
46
+ assert.equal(prompt.includes("SECRET-TITLE"), false);
47
+ assert.equal(prompt.includes("SECRET-APP"), false);
48
+ assert.match(prompt, /"id":9/);
49
+ assert.match(prompt, /2026-08-17T09:00:00.000Z/);
50
+ });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Deterministic evidence-only analysis prompt (issue #2050).
3
+ * No LLM call. Prompt lists observation id and capturedAtUtc only.
4
+ */
5
+ export type AnalysisPromptObservation = {
6
+ id: number;
7
+ capturedAtUtc: string;
8
+ };
9
+
10
+ const INSTRUCTIONS =
11
+ "Use only supplied evidence. Preserve chronology. Do not invent people, places, or tasks. Return strict JSON.";
12
+
13
+ function listedObservation(observation: AnalysisPromptObservation): AnalysisPromptObservation {
14
+ return { id: observation.id, capturedAtUtc: observation.capturedAtUtc };
15
+ }
16
+
17
+ /** Build a deterministic prompt. Observations sort by id. Empty lists render as (empty). */
18
+ export function buildAnalysisPrompt(input: {
19
+ observations: readonly AnalysisPromptObservation[];
20
+ }): string {
21
+ const listed = [...input.observations].sort((a, b) => a.id - b.id).map(listedObservation);
22
+ const body = listed.length === 0 ? "(empty)" : JSON.stringify(listed);
23
+ return `${INSTRUCTIONS}\nObservations: ${body}`;
24
+ }
@@ -10,4 +10,12 @@ export * from "./journal-recap-persist.js";
10
10
  export * from "./weekly.js";
11
11
  export * from "./weekly-persist.js";
12
12
  export * from "./analysis.js";
13
+ export * from "./recap-export.js";
14
+ export * from "./recap-markdown.js";
15
+ export * from "./week-export.js";
16
+ export * from "./week-snapshot.js";
13
17
  export * from "./analysis-batch.js";
18
+ export * from "./analysis-evidence.js";
19
+ export * from "./analysis-prompt.js";
20
+ export * from "./analysis-claim.js";
21
+ export * from "./analysis-order.js";
@@ -0,0 +1,19 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { countRecapCards } from "./recap-card-count.js";
5
+
6
+ test("empty list is 0", () => {
7
+ assert.equal(countRecapCards([]), 0);
8
+ });
9
+
10
+ test("counts unique ids", () => {
11
+ assert.equal(countRecapCards(["card-a", "card-b", "card-a"]), 2);
12
+ assert.equal(countRecapCards(["card-a", "card-b", "card-c"]), 3);
13
+ });
14
+
15
+ test("drops empty strings and does not mutate", () => {
16
+ const cards = ["card-a", "", "card-b", "", "card-a"];
17
+ assert.equal(countRecapCards(cards), 2);
18
+ assert.deepEqual(cards, ["card-a", "", "card-b", "", "card-a"]);
19
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Recap card counter (issue #2051 leftover).
3
+ *
4
+ * Unique ids after dropping empty strings. Does not mutate input.
5
+ */
6
+
7
+ export function countRecapCards(cards: readonly string[]): number {
8
+ const unique = new Set<string>();
9
+ for (const id of cards) {
10
+ if (id !== "") unique.add(id);
11
+ }
12
+ return unique.size;
13
+ }
@@ -0,0 +1,21 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { parseRecapCardId } from "./recap-card-id.js";
5
+
6
+ test("ok card id returns trimmed id", () => {
7
+ assert.deepEqual(parseRecapCardId("card-1"), { ok: true, id: "card-1" });
8
+ });
9
+
10
+ test("empty card id is missing_id", () => {
11
+ assert.deepEqual(parseRecapCardId(""), { ok: false, error: "missing_id" });
12
+ assert.deepEqual(parseRecapCardId(" "), { ok: false, error: "missing_id" });
13
+ });
14
+
15
+ test("newline in card id is invalid_id", () => {
16
+ assert.deepEqual(parseRecapCardId("card-1\ncard-2"), { ok: false, error: "invalid_id" });
17
+ });
18
+
19
+ test("trims surrounding whitespace", () => {
20
+ assert.deepEqual(parseRecapCardId(" card-1 "), { ok: true, id: "card-1" });
21
+ });
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Parse a recap card id (issue #2051 leftover).
3
+ *
4
+ * Pure. Empty is missing_id. Newline is invalid_id.
5
+ */
6
+
7
+ export type ParseRecapCardIdResult =
8
+ | { ok: true; id: string }
9
+ | { ok: false; error: "missing_id" | "invalid_id" };
10
+
11
+ export function parseRecapCardId(value: string): ParseRecapCardIdResult {
12
+ const id = value.trim();
13
+ if (id.length === 0) return { ok: false, error: "missing_id" };
14
+ if (id.includes("\n")) return { ok: false, error: "invalid_id" };
15
+ return { ok: true, id };
16
+ }
@@ -0,0 +1,21 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { parseRecapDate } from "./recap-date.js";
5
+
6
+ test("accepts a trimmed YYYY-MM-DD date", () => {
7
+ assert.deepEqual(parseRecapDate("2026-08-19"), { ok: true, date: "2026-08-19" });
8
+ assert.deepEqual(parseRecapDate(" 2026-08-19 "), { ok: true, date: "2026-08-19" });
9
+ });
10
+
11
+ test("empty and whitespace-only values are missing_date", () => {
12
+ assert.deepEqual(parseRecapDate(""), { ok: false, error: "missing_date" });
13
+ assert.deepEqual(parseRecapDate(" "), { ok: false, error: "missing_date" });
14
+ });
15
+
16
+ test("rejects strings that are not YYYY-MM-DD", () => {
17
+ assert.deepEqual(parseRecapDate("20260819"), { ok: false, error: "invalid_date" });
18
+ assert.deepEqual(parseRecapDate("2026-8-19"), { ok: false, error: "invalid_date" });
19
+ assert.deepEqual(parseRecapDate("2026-08-19T00:00:00Z"), { ok: false, error: "invalid_date" });
20
+ assert.deepEqual(parseRecapDate("not-a-date"), { ok: false, error: "invalid_date" });
21
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Parse a recap local-day string (issue #2051 leftover).
3
+ *
4
+ * Trim first. Empty → missing_date. Bounded YYYY-MM-DD only (no ReDoS).
5
+ */
6
+
7
+ export type RecapDateResult =
8
+ | { ok: true; date: string }
9
+ | { ok: false; error: "missing_date" | "invalid_date" };
10
+
11
+ const RECAP_DATE = /^\d{4}-\d{2}-\d{2}$/;
12
+
13
+ /** Trim and accept YYYY-MM-DD, or return a typed error. */
14
+ export function parseRecapDate(value: string): RecapDateResult {
15
+ const date = value.trim();
16
+ if (date.length === 0) return { ok: false, error: "missing_date" };
17
+ if (!RECAP_DATE.test(date)) return { ok: false, error: "invalid_date" };
18
+ return { ok: true, date };
19
+ }
@@ -0,0 +1,21 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { isEmptyRecapCards } from "./recap-empty.js";
5
+
6
+ test("null, undefined, and empty array are empty", () => {
7
+ assert.equal(isEmptyRecapCards(null), true);
8
+ assert.equal(isEmptyRecapCards(undefined), true);
9
+ assert.equal(isEmptyRecapCards([]), true);
10
+ });
11
+
12
+ test("a non-empty array is not empty", () => {
13
+ assert.equal(isEmptyRecapCards([{ id: 1 }]), false);
14
+ assert.equal(isEmptyRecapCards([0]), false);
15
+ });
16
+
17
+ test("a non-array throws", () => {
18
+ assert.throws(() => isEmptyRecapCards({}), TypeError);
19
+ assert.throws(() => isEmptyRecapCards("cards"), TypeError);
20
+ assert.throws(() => isEmptyRecapCards(1), TypeError);
21
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Empty-set guard for timeline recap cards (issue #2051 leftover).
3
+ *
4
+ * null, undefined, and [] are empty. A non-array throws. Any other array is not empty.
5
+ */
6
+
7
+ /** True when the recap card list is absent or has no items. */
8
+ export function isEmptyRecapCards(cards: unknown): boolean {
9
+ if (cards == null) return true;
10
+ if (!Array.isArray(cards)) {
11
+ throw new TypeError("cards must be an array");
12
+ }
13
+ return cards.length === 0;
14
+ }
@@ -0,0 +1,78 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import type { TimelineCard } from "./types.js";
5
+ import { exportDeterministicRecap } from "./recap-export.js";
6
+
7
+ const DATE = "2026-08-17";
8
+
9
+ function card(
10
+ overrides: Partial<TimelineCard> & Pick<TimelineCard, "id">,
11
+ ): TimelineCard {
12
+ return {
13
+ kind: "activity",
14
+ title: "Title",
15
+ summary: "Summary",
16
+ categoryId: "work",
17
+ confidence: 0.9,
18
+ startUtc: "2026-08-17T09:00:00.000Z",
19
+ endUtc: "2026-08-17T10:00:00.000Z",
20
+ dayKey: DATE,
21
+ timezone: "UTC",
22
+ machine: "machine-a",
23
+ evidenceIds: [1],
24
+ evidenceRange: null,
25
+ ...overrides,
26
+ };
27
+ }
28
+
29
+ test("empty cards export date and timezone with an empty card list", () => {
30
+ const recap = exportDeterministicRecap({
31
+ date: DATE,
32
+ timezone: "UTC",
33
+ cards: [],
34
+ });
35
+ assert.deepEqual(recap, { date: DATE, timezone: "UTC", cards: [] });
36
+ assert.equal(JSON.stringify(recap), `{"date":"${DATE}","timezone":"UTC","cards":[]}`);
37
+ });
38
+
39
+ test("cards are sorted by id regardless of input order", () => {
40
+ const recap = exportDeterministicRecap({
41
+ date: DATE,
42
+ timezone: "UTC",
43
+ cards: [
44
+ card({ id: "card-c" }),
45
+ card({ id: "card-a" }),
46
+ card({ id: "card-b" }),
47
+ ],
48
+ });
49
+ assert.deepEqual(
50
+ recap.cards.map((c) => c.id),
51
+ ["card-a", "card-b", "card-c"],
52
+ );
53
+ });
54
+
55
+ test("timezone is echoed verbatim, not normalized", () => {
56
+ const recap = exportDeterministicRecap({
57
+ date: DATE,
58
+ timezone: "America/Chicago",
59
+ cards: [],
60
+ });
61
+ assert.equal(recap.timezone, "America/Chicago");
62
+ });
63
+
64
+ test("input cards are not mutated and reruns are stable", () => {
65
+ const input = [card({ id: "card-b" }), card({ id: "card-a" })];
66
+ const first = exportDeterministicRecap({
67
+ date: DATE,
68
+ timezone: "UTC",
69
+ cards: input,
70
+ });
71
+ const second = exportDeterministicRecap({
72
+ date: DATE,
73
+ timezone: "UTC",
74
+ cards: input,
75
+ });
76
+ assert.deepEqual(input.map((c) => c.id), ["card-b", "card-a"]);
77
+ assert.equal(JSON.stringify(first), JSON.stringify(second));
78
+ });
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Deterministic machine-readable recap export from timeline cards (issue #2051).
3
+ *
4
+ * Pure: cards + date + timezone in, stable JSON out. No LLM, no I/O, no
5
+ * persistence. Same cards always serialize to the same bytes, regardless of
6
+ * input array order: cards are sorted by id before serialization.
7
+ */
8
+ import type { TimelineCard } from "./types.js";
9
+
10
+ export interface DeterministicRecapOptions {
11
+ date: string;
12
+ timezone: string;
13
+ cards: readonly TimelineCard[];
14
+ }
15
+
16
+ export interface DeterministicRecap {
17
+ date: string;
18
+ timezone: string;
19
+ cards: TimelineCard[];
20
+ }
21
+
22
+ function compareCardIds(a: TimelineCard, b: TimelineCard): number {
23
+ if (a.id < b.id) return -1;
24
+ if (a.id > b.id) return 1;
25
+ return 0;
26
+ }
27
+
28
+ /**
29
+ * Build the byte-stable JSON recap document for one local day.
30
+ * Callers may `JSON.stringify` the returned object; construction order
31
+ * (date, timezone, cards) is fixed, so serialization is deterministic.
32
+ */
33
+ export function exportDeterministicRecap(
34
+ options: DeterministicRecapOptions,
35
+ ): DeterministicRecap {
36
+ return {
37
+ date: options.date,
38
+ timezone: options.timezone,
39
+ cards: [...options.cards].sort(compareCardIds),
40
+ };
41
+ }
@@ -0,0 +1,28 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { sortRecapCardIds } from "./recap-id.js";
5
+
6
+ test("empty input returns an empty array", () => {
7
+ assert.deepEqual(sortRecapCardIds([]), []);
8
+ });
9
+
10
+ test("sorts ids with localeCompare", () => {
11
+ assert.deepEqual(sortRecapCardIds(["card-c", "card-a", "card-b"]), [
12
+ "card-a",
13
+ "card-b",
14
+ "card-c",
15
+ ]);
16
+ });
17
+
18
+ test("does not mutate the input array", () => {
19
+ const input = ["card-c", "card-a"];
20
+ const snapshot = input.slice();
21
+ const sorted = sortRecapCardIds(input);
22
+ assert.deepEqual(input, snapshot);
23
+ assert.notEqual(sorted, input);
24
+ });
25
+
26
+ test("drops empty strings", () => {
27
+ assert.deepEqual(sortRecapCardIds(["b", "", "a", ""]), ["a", "b"]);
28
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Stable recap card-id order (issue #2051 leftover).
3
+ *
4
+ * localeCompare sort. Empty input → []. Does not mutate input. Drops empty strings.
5
+ */
6
+
7
+ /** Return a new localeCompare-sorted id list. Empty strings are dropped. */
8
+ export function sortRecapCardIds(ids: readonly string[]): string[] {
9
+ return ids.filter((id) => id !== "").sort((a, b) => a.localeCompare(b));
10
+ }
@@ -0,0 +1,56 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { renderRecapMarkdown } from "./recap-markdown.js";
5
+ import type { TimelineCard } from "./types.js";
6
+
7
+ const DATE = "2026-08-17";
8
+
9
+ function card(
10
+ overrides: Partial<TimelineCard> & Pick<TimelineCard, "id">,
11
+ ): TimelineCard {
12
+ return {
13
+ kind: "activity",
14
+ title: "Title",
15
+ summary: "Summary",
16
+ categoryId: "work",
17
+ confidence: 0.9,
18
+ startUtc: "2026-08-17T09:00:00.000Z",
19
+ endUtc: "2026-08-17T10:00:00.000Z",
20
+ dayKey: DATE,
21
+ timezone: "UTC",
22
+ machine: "machine-a",
23
+ evidenceIds: [1],
24
+ evidenceRange: null,
25
+ ...overrides,
26
+ };
27
+ }
28
+
29
+ test("empty cards print heading plus (empty)", () => {
30
+ assert.equal(
31
+ renderRecapMarkdown({ date: DATE, timezone: "UTC", cards: [] }),
32
+ [`# Recap — ${DATE} (UTC)`, "", "(empty)", ""].join("\n"),
33
+ );
34
+ });
35
+
36
+ test("cards are sorted by id regardless of input order", () => {
37
+ const input = [card({ id: "card-c" }), card({ id: "card-a" }), card({ id: "card-b" })];
38
+ const first = renderRecapMarkdown({ date: DATE, timezone: "UTC", cards: input });
39
+ const second = renderRecapMarkdown({ date: DATE, timezone: "UTC", cards: input });
40
+ assert.equal(
41
+ first,
42
+ [`# Recap — ${DATE} (UTC)`, "", "- card-a", "- card-b", "- card-c", ""].join("\n"),
43
+ );
44
+ assert.deepEqual(
45
+ input.map((row) => row.id),
46
+ ["card-c", "card-a", "card-b"],
47
+ );
48
+ assert.equal(first, second);
49
+ });
50
+
51
+ test("timezone is echoed verbatim, not normalized", () => {
52
+ assert.equal(
53
+ renderRecapMarkdown({ date: DATE, timezone: "America/Chicago", cards: [] }),
54
+ [`# Recap — ${DATE} (America/Chicago)`, "", "(empty)", ""].join("\n"),
55
+ );
56
+ });