@remnic/core 9.69.46 → 9.69.48

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 (601) hide show
  1. package/dist/access-admin-ops-surface.d.ts +15 -14
  2. package/dist/access-admin-ops-surface.js +43 -42
  3. package/dist/access-authorization-probe.d.ts +15 -14
  4. package/dist/access-authorization-probe.js +44 -43
  5. package/dist/access-boundary.d.ts +15 -14
  6. package/dist/access-boundary.js +43 -42
  7. package/dist/access-cli.js +120 -115
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-coding-context-resolution.d.ts +1 -1
  10. package/dist/access-extraction-force-flush.d.ts +15 -14
  11. package/dist/access-extraction-force-flush.js +43 -42
  12. package/dist/access-health-types.d.ts +1 -1
  13. package/dist/access-http-briefing.d.ts +15 -14
  14. package/dist/access-http-briefing.js +44 -43
  15. package/dist/access-http-lcm-compaction.d.ts +15 -14
  16. package/dist/access-http-lifecycle-flush.d.ts +15 -14
  17. package/dist/access-http-offline-stream.d.ts +15 -14
  18. package/dist/access-http-query.d.ts +2 -2
  19. package/dist/access-http-query.js +45 -44
  20. package/dist/access-http.d.ts +16 -15
  21. package/dist/access-http.js +68 -64
  22. package/dist/access-identity-continuity-surface.d.ts +11 -11
  23. package/dist/access-identity-continuity-surface.js +43 -42
  24. package/dist/access-lcm-surface.d.ts +15 -14
  25. package/dist/access-lcm-surface.js +43 -42
  26. package/dist/access-mcp-output-schemas.js +1 -1
  27. package/dist/access-mcp-shared-context-tools.d.ts +15 -14
  28. package/dist/access-mcp.d.ts +15 -14
  29. package/dist/access-mcp.js +56 -53
  30. package/dist/access-memory-search-fanout.d.ts +2 -2
  31. package/dist/{access-namespace-preflight-DDnsN22V.d.ts → access-namespace-preflight-CetF9ziA.d.ts} +1 -1
  32. package/dist/access-namespace-preflight.d.ts +3 -3
  33. package/dist/access-namespace-preflight.js +43 -42
  34. package/dist/access-observe-transcript.d.ts +11 -11
  35. package/dist/access-observe-write-category.d.ts +3 -3
  36. package/dist/access-observe-write-category.js +4 -4
  37. package/dist/access-observe-write-surface.d.ts +15 -14
  38. package/dist/access-observe-write-surface.js +43 -42
  39. package/dist/access-offline-manifest.d.ts +11 -11
  40. package/dist/access-offline-manifest.js +2 -2
  41. package/dist/access-operations-batch.js +45 -44
  42. package/dist/access-operations.d.ts +16 -15
  43. package/dist/access-operations.js +52 -50
  44. package/dist/access-recall-concurrency.d.ts +15 -14
  45. package/dist/access-recall-concurrency.js +43 -42
  46. package/dist/access-recall-response.d.ts +15 -14
  47. package/dist/access-recall-response.js +43 -42
  48. package/dist/access-recall-surface.d.ts +15 -14
  49. package/dist/access-recall-surface.js +43 -42
  50. package/dist/access-schema.d.ts +114 -114
  51. package/dist/access-schema.js +3 -3
  52. package/dist/{access-service-DGynYlbE.d.ts → access-service-DUkTWyZS.d.ts} +77 -74
  53. package/dist/access-service-helpers.d.ts +15 -14
  54. package/dist/access-service.d.ts +15 -14
  55. package/dist/access-service.js +43 -42
  56. package/dist/access-surface-catalog.d.ts +15 -14
  57. package/dist/access-surface-catalog.js +51 -44
  58. package/dist/access-surface-catalog.js.map +1 -1
  59. package/dist/access-wearables-meetings-surface.d.ts +4 -4
  60. package/dist/action-confidence.d.ts +1 -1
  61. package/dist/active-memory-bridge.d.ts +1 -1
  62. package/dist/active-recall.d.ts +1 -1
  63. package/dist/active-recall.js +3 -2
  64. package/dist/active-recall.js.map +1 -1
  65. package/dist/activity/digest.d.ts +1 -1
  66. package/dist/adapters/index.js +4 -4
  67. package/dist/adapters/registry.js +2 -2
  68. package/dist/ambient-provenance.d.ts +1 -1
  69. package/dist/artifact-search.d.ts +1 -1
  70. package/dist/background-generation.d.ts +1 -1
  71. package/dist/behavior-learner.d.ts +1 -1
  72. package/dist/behavior-signals.d.ts +1 -1
  73. package/dist/bootstrap.d.ts +11 -11
  74. package/dist/briefing-window.d.ts +2 -2
  75. package/dist/briefing.d.ts +2 -2
  76. package/dist/briefing.js +13 -13
  77. package/dist/buffer-surprise-report.d.ts +1 -1
  78. package/dist/buffer-turn-helpers.d.ts +1 -1
  79. package/dist/buffer.d.ts +2 -2
  80. package/dist/buffer.js +5 -5
  81. package/dist/bulk-import/index.d.ts +3 -3
  82. package/dist/calibration.d.ts +1 -1
  83. package/dist/capabilities.d.ts +1 -1
  84. package/dist/{catalog-Dl2hYqpS.d.ts → catalog-Bns9zpl3.d.ts} +1 -1
  85. package/dist/causal-behavior.d.ts +1 -1
  86. package/dist/causal-consolidation.d.ts +1 -1
  87. package/dist/causal-consolidation.js +14 -14
  88. package/dist/causal-trajectory-graph.d.ts +1 -1
  89. package/dist/{chunk-N46SNYMC.js → chunk-2B2RHLHI.js} +2 -2
  90. package/dist/{chunk-F5V7KRQS.js → chunk-2WMXZYMS.js} +2 -2
  91. package/dist/{chunk-KF57IQFX.js → chunk-32ZBIGID.js} +19 -15
  92. package/dist/chunk-32ZBIGID.js.map +1 -0
  93. package/dist/{chunk-ORQ7AZMU.js → chunk-3RKVSECD.js} +8 -8
  94. package/dist/{chunk-3NYM6JA4.js → chunk-4LPIVP77.js} +2 -2
  95. package/dist/{chunk-PW4HTWXL.js → chunk-55XWRL6F.js} +2 -2
  96. package/dist/{chunk-GGY7WKMB.js → chunk-5KBI2MOC.js} +1 -1
  97. package/dist/{chunk-IXMWTNNY.js → chunk-6A6HSXWV.js} +2 -2
  98. package/dist/{chunk-P7S7BVBJ.js → chunk-7DU2F7LM.js} +3 -3
  99. package/dist/{chunk-4HTQ76O4.js → chunk-7SJCSDQ3.js} +2 -2
  100. package/dist/{chunk-U4ESGVZ2.js → chunk-7YUX52XZ.js} +3 -3
  101. package/dist/{chunk-FMEP2RB5.js → chunk-B4WI7OF2.js} +2 -2
  102. package/dist/{chunk-EUP4AXIW.js → chunk-B6VDWDFN.js} +2 -2
  103. package/dist/{chunk-UOREPQ3C.js → chunk-BIXELUIR.js} +2 -2
  104. package/dist/{chunk-GYYLZOSO.js → chunk-BMBAA2M5.js} +13 -6
  105. package/dist/chunk-BMBAA2M5.js.map +1 -0
  106. package/dist/{chunk-AYZHHMTK.js → chunk-BNX2TUSF.js} +2 -2
  107. package/dist/{chunk-5ZXGH2ZL.js → chunk-BXOPUJ3W.js} +2 -2
  108. package/dist/{chunk-TE6B2UU6.js → chunk-CD7VTZIG.js} +2 -2
  109. package/dist/{chunk-EC5SVM6H.js → chunk-DE4ZGH2N.js} +2 -2
  110. package/dist/{chunk-YFVIA5A6.js → chunk-DRLZB3CN.js} +2 -2
  111. package/dist/{chunk-SRRZZHA5.js → chunk-EGOWY342.js} +2 -2
  112. package/dist/chunk-EIB4FJVR.js +46 -0
  113. package/dist/chunk-EIB4FJVR.js.map +1 -0
  114. package/dist/{chunk-W44T2KHG.js → chunk-EWBDJQ5Y.js} +3 -3
  115. package/dist/{chunk-JMBTNVMW.js → chunk-FGRJJBFO.js} +2 -2
  116. package/dist/{chunk-F3TURDFG.js → chunk-FPB5LQWW.js} +4 -1
  117. package/dist/{chunk-F3TURDFG.js.map → chunk-FPB5LQWW.js.map} +1 -1
  118. package/dist/{chunk-WGZHSQDV.js → chunk-G2XZFGYN.js} +4 -4
  119. package/dist/{chunk-CFIHVWYZ.js → chunk-GGROFIYX.js} +2 -2
  120. package/dist/{chunk-XLZZLUWL.js → chunk-HG74GEVY.js} +2 -2
  121. package/dist/chunk-HMDYSGY6.js +54 -0
  122. package/dist/chunk-HMDYSGY6.js.map +1 -0
  123. package/dist/{chunk-WUUGE7QC.js → chunk-HS75MCQF.js} +4 -4
  124. package/dist/{chunk-DO6R3LIE.js → chunk-I6IJDTRP.js} +2 -2
  125. package/dist/{chunk-7ZNJN5WZ.js → chunk-IHOXK2I6.js} +55 -6
  126. package/dist/chunk-IHOXK2I6.js.map +1 -0
  127. package/dist/{chunk-2NKY7OC6.js → chunk-IMJPJY7V.js} +2 -2
  128. package/dist/{chunk-TZ44E732.js → chunk-IO7K7GES.js} +2 -2
  129. package/dist/{chunk-7BNB7KHB.js → chunk-JIURESYF.js} +7 -7
  130. package/dist/{chunk-AYP2FZFA.js → chunk-JUOJGZPV.js} +2 -2
  131. package/dist/{chunk-4XTSKGLX.js → chunk-JYXLW22X.js} +3 -3
  132. package/dist/{chunk-HXDXR2FH.js → chunk-KPYMORPQ.js} +41 -31
  133. package/dist/chunk-KPYMORPQ.js.map +1 -0
  134. package/dist/{chunk-IPLYGWQF.js → chunk-KQAFEZQX.js} +5 -5
  135. package/dist/{chunk-AN2O4KZY.js → chunk-KVIMQAER.js} +2 -2
  136. package/dist/chunk-LI4APB6M.js +218 -0
  137. package/dist/chunk-LI4APB6M.js.map +1 -0
  138. package/dist/{chunk-VGRVJ6Z6.js → chunk-LIAM7U2J.js} +2 -2
  139. package/dist/{chunk-S3SND7SO.js → chunk-LIDJWAQV.js} +2 -2
  140. package/dist/{chunk-T5NI5K46.js → chunk-LYMJ36UI.js} +2 -2
  141. package/dist/{chunk-UGGOQKO6.js → chunk-MKJFPIVG.js} +93 -51
  142. package/dist/chunk-MKJFPIVG.js.map +1 -0
  143. package/dist/{chunk-UEJY6ZTQ.js → chunk-MKLDZNBD.js} +2 -2
  144. package/dist/{chunk-QUFJ3NJE.js → chunk-MNG536IS.js} +20 -20
  145. package/dist/{chunk-SW42MMAH.js → chunk-MQT3WP3F.js} +2 -2
  146. package/dist/{chunk-5EHPB6RL.js → chunk-MUNR43V3.js} +2 -2
  147. package/dist/{chunk-OV7GJ4KW.js → chunk-NNFB4CU4.js} +2 -2
  148. package/dist/{chunk-47C6DF43.js → chunk-OS4JVTHR.js} +3 -3
  149. package/dist/{chunk-W6YDAIVB.js → chunk-OSTXIFZW.js} +13 -2
  150. package/dist/chunk-OSTXIFZW.js.map +1 -0
  151. package/dist/chunk-P3YNEMYY.js +212 -0
  152. package/dist/chunk-P3YNEMYY.js.map +1 -0
  153. package/dist/{chunk-JOP7W24J.js → chunk-PA67GXYJ.js} +8 -8
  154. package/dist/{chunk-FSH6KRKO.js → chunk-PPJLJD2D.js} +2 -2
  155. package/dist/{chunk-AG6PANBG.js → chunk-PXJ6HE2W.js} +11 -11
  156. package/dist/{chunk-DDBLZJRV.js → chunk-QB4VXCFN.js} +2 -2
  157. package/dist/{chunk-L63M2PE4.js → chunk-QGAKUOKU.js} +2 -2
  158. package/dist/{chunk-YUQPUK6H.js → chunk-QLCMOHRE.js} +2 -2
  159. package/dist/{chunk-Q4AUIFUA.js → chunk-QMWKENNK.js} +4 -4
  160. package/dist/{chunk-66QOUYNS.js → chunk-R32AEEU3.js} +174 -57
  161. package/dist/chunk-R32AEEU3.js.map +1 -0
  162. package/dist/{chunk-TNQR7N52.js → chunk-S3YM4SZQ.js} +2 -1
  163. package/dist/chunk-S3YM4SZQ.js.map +1 -0
  164. package/dist/{chunk-RW4QNB4J.js → chunk-S45INSV3.js} +3 -1
  165. package/dist/{chunk-RW4QNB4J.js.map → chunk-S45INSV3.js.map} +1 -1
  166. package/dist/{chunk-EEHQY7ER.js → chunk-SK6HEQTW.js} +2 -2
  167. package/dist/{chunk-PGAB2V53.js → chunk-SMBWBEE7.js} +2 -2
  168. package/dist/{chunk-62NYUFNH.js → chunk-SS33RR4F.js} +8 -8
  169. package/dist/{chunk-4ECS33D4.js → chunk-T3T5HTZZ.js} +2 -2
  170. package/dist/{chunk-KG2OOAFF.js → chunk-TQSCR5DU.js} +6 -6
  171. package/dist/{chunk-D5DTZBTV.js → chunk-TW4MXM7H.js} +6 -6
  172. package/dist/chunk-TW4MXM7H.js.map +1 -0
  173. package/dist/{chunk-AS2NOPDM.js → chunk-V7IONNTJ.js} +2 -2
  174. package/dist/{chunk-HYWPUYLO.js → chunk-VV5GCNKG.js} +2 -2
  175. package/dist/{chunk-Y3XHIWVP.js → chunk-WL3PGPGU.js} +2 -2
  176. package/dist/{chunk-3UQIS2GC.js → chunk-WNMVUWI7.js} +2 -2
  177. package/dist/{chunk-66YUOI53.js → chunk-WQW6KPVC.js} +1 -1
  178. package/dist/chunk-WQW6KPVC.js.map +1 -0
  179. package/dist/{chunk-2AV75BJW.js → chunk-WVI43D54.js} +24 -1
  180. package/dist/chunk-WVI43D54.js.map +1 -0
  181. package/dist/{chunk-WOBMSMUK.js → chunk-WYU66IVZ.js} +3 -3
  182. package/dist/{chunk-A7GQBJP5.js → chunk-X2NFFBTO.js} +4 -4
  183. package/dist/{chunk-CWMMKFO7.js → chunk-XEHLYA5Z.js} +3 -3
  184. package/dist/{chunk-JIWZVOQM.js → chunk-XKHRNAK7.js} +2 -2
  185. package/dist/{chunk-VW5TGNJN.js → chunk-XXLH6KFO.js} +2 -2
  186. package/dist/chunk-XYGINDIJ.js +74 -0
  187. package/dist/chunk-XYGINDIJ.js.map +1 -0
  188. package/dist/{chunk-J5OXWG66.js → chunk-XZUHXKM3.js} +2 -2
  189. package/dist/{chunk-K4KE5MYE.js → chunk-Y5VT7BIT.js} +2 -2
  190. package/dist/{chunk-S7KDHFF2.js → chunk-YXWMZAAN.js} +2 -2
  191. package/dist/{chunk-EAZ3YZKN.js → chunk-ZEIJQ4I3.js} +2 -2
  192. package/dist/{cli-Cik3H7q1.d.ts → cli-Bbndoc6k.d.ts} +5 -5
  193. package/dist/cli.d.ts +17 -16
  194. package/dist/cli.js +90 -85
  195. package/dist/coding/export-okf-codegraph.d.ts +1 -1
  196. package/dist/coding/export-okf-codegraph.js +12 -12
  197. package/dist/coding/pre-action-gate.d.ts +1 -1
  198. package/dist/compounding/engine.d.ts +2 -2
  199. package/dist/compounding/engine.js +13 -13
  200. package/dist/compounding/preference-consolidator.d.ts +1 -1
  201. package/dist/compression-optimizer.d.ts +1 -1
  202. package/dist/config.d.ts +1 -1
  203. package/dist/config.js +3 -2
  204. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  205. package/dist/connectors/codex-materialize-runner.js +13 -13
  206. package/dist/connectors/codex-materialize.d.ts +1 -1
  207. package/dist/connectors/index.d.ts +1 -1
  208. package/dist/connectors/index.js +13 -13
  209. package/dist/consolidation-provenance-check.d.ts +2 -2
  210. package/dist/consolidation-undo.d.ts +2 -2
  211. package/dist/contradiction/index.d.ts +4 -4
  212. package/dist/converge-config.d.ts +1 -1
  213. package/dist/convergence-refresh.d.ts +2 -2
  214. package/dist/convergence-refresh.js +15 -15
  215. package/dist/conversation-index/backend.d.ts +1 -1
  216. package/dist/conversation-index/chunker.d.ts +1 -1
  217. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  218. package/dist/conversation-index/indexer.d.ts +1 -1
  219. package/dist/conversation-index/search.d.ts +1 -1
  220. package/dist/corpus-watermark.d.ts +1 -1
  221. package/dist/corpus-watermark.js +15 -15
  222. package/dist/cross-lingual-recall.d.ts +1 -1
  223. package/dist/day-summary.d.ts +1 -1
  224. package/dist/deep-recall-http-glue.d.ts +15 -14
  225. package/dist/deep-recall-http-glue.js +4 -4
  226. package/dist/deep-recall-mcp-tools.d.ts +15 -14
  227. package/dist/deep-recall-policy-llm.d.ts +2 -2
  228. package/dist/deep-recall-seeds.d.ts +1 -1
  229. package/dist/deep-recall.js +2 -2
  230. package/dist/delinearize.d.ts +1 -1
  231. package/dist/dependency-propagation-config.d.ts +1 -1
  232. package/dist/{dependency-propagation-delivery-BXaUoVUf.d.ts → dependency-propagation-delivery-B-LvcDaq.d.ts} +1 -1
  233. package/dist/direct-answer-wiring.d.ts +1 -1
  234. package/dist/direct-answer.d.ts +1 -1
  235. package/dist/embedding-fallback.d.ts +1 -1
  236. package/dist/enrichment/index.d.ts +1 -1
  237. package/dist/entity-origin-fields.d.ts +1 -1
  238. package/dist/entity-retrieval.d.ts +2 -2
  239. package/dist/entity-retrieval.js +14 -14
  240. package/dist/entity-schema.d.ts +1 -1
  241. package/dist/episodic-context.d.ts +1 -1
  242. package/dist/eval-trajectory-contracts.d.ts +1 -1
  243. package/dist/eval-trajectory-store.d.ts +1 -1
  244. package/dist/eval-trajectory.d.ts +1 -1
  245. package/dist/explicit-capture.d.ts +11 -11
  246. package/dist/external-wiki-access.d.ts +15 -14
  247. package/dist/external-wiki-access.js +44 -43
  248. package/dist/external-wiki-collection-registration.d.ts +1 -1
  249. package/dist/external-wiki-collection.d.ts +1 -1
  250. package/dist/external-wiki-mcp-tools.d.ts +15 -14
  251. package/dist/extraction-error-classification.d.ts +1 -1
  252. package/dist/extraction-evidence-class.d.ts +142 -0
  253. package/dist/extraction-evidence-class.js +113 -0
  254. package/dist/extraction-evidence-class.js.map +1 -0
  255. package/dist/extraction-faithfulness.d.ts +2 -2
  256. package/dist/extraction-judge-telemetry.d.ts +2 -2
  257. package/dist/extraction-judge-training.d.ts +2 -2
  258. package/dist/extraction-judge.d.ts +2 -2
  259. package/dist/extraction-liveness.d.ts +1 -1
  260. package/dist/extraction-normalization.d.ts +1 -1
  261. package/dist/extraction-prompt.d.ts +1 -1
  262. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  263. package/dist/extraction-source-grounding.d.ts +1 -1
  264. package/dist/extraction-source-grounding.js +2 -2
  265. package/dist/extraction-span-config.d.ts +1 -1
  266. package/dist/extraction-span-materialize.d.ts +1 -1
  267. package/dist/extraction-span-wire.d.ts +1 -1
  268. package/dist/extraction.d.ts +2 -2
  269. package/dist/extraction.js +21 -21
  270. package/dist/fallback-llm-codex-subscription.d.ts +1 -1
  271. package/dist/fallback-llm.d.ts +1 -1
  272. package/dist/{generic-recall-paths-BkVVEoeO.d.ts → generic-recall-paths-Bfc7zigS.d.ts} +1 -1
  273. package/dist/graph-append-error.d.ts +1 -1
  274. package/dist/graph-dashboard-diff.d.ts +1 -1
  275. package/dist/graph-dashboard-key.d.ts +1 -1
  276. package/dist/graph-dashboard-parser.d.ts +1 -1
  277. package/dist/graph-edge-reinforcement.d.ts +1 -1
  278. package/dist/graph-jsonl.d.ts +1 -1
  279. package/dist/graph-path-reconstruction.d.ts +1 -1
  280. package/dist/graph-path-scoring.d.ts +1 -1
  281. package/dist/graph-snapshot.d.ts +1 -1
  282. package/dist/graph.d.ts +1 -1
  283. package/dist/harmonic-retrieval.js +1 -1
  284. package/dist/importance.d.ts +1 -1
  285. package/dist/importers/index.d.ts +1 -1
  286. package/dist/importers/test-utils.d.ts +1 -1
  287. package/dist/in-flight-reads.d.ts +1 -1
  288. package/dist/{index-Zb6SUV4R.d.ts → index-C7IA1DxB.d.ts} +3 -3
  289. package/dist/index.d.ts +798 -707
  290. package/dist/index.js +184 -134
  291. package/dist/intent.d.ts +1 -1
  292. package/dist/lcm/engine.d.ts +1 -1
  293. package/dist/lcm/engine.js +2 -2
  294. package/dist/lcm/index.d.ts +1 -1
  295. package/dist/lcm/index.js +7 -7
  296. package/dist/lcm/tools.d.ts +1 -1
  297. package/dist/lifecycle.d.ts +1 -1
  298. package/dist/live-connectors-runner.d.ts +1 -1
  299. package/dist/{local-llm-DdrhmecM.d.ts → local-llm-DH4tWtck.d.ts} +1 -1
  300. package/dist/local-llm-helpers.d.ts +2 -2
  301. package/dist/local-llm.d.ts +2 -2
  302. package/dist/local-llm.js +2 -2
  303. package/dist/local-model-endpoint.d.ts +1 -1
  304. package/dist/location/index.d.ts +5 -5
  305. package/dist/location/index.js +15 -15
  306. package/dist/maintenance/memory-governance.d.ts +1 -1
  307. package/dist/maintenance/memory-governance.js +13 -13
  308. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  309. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +13 -13
  310. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  311. package/dist/maintenance/rebuild-memory-projection.js +14 -14
  312. package/dist/{maintenance-Du_VRvVg.d.ts → maintenance-n6yeTQhR.d.ts} +4 -4
  313. package/dist/mcp-memory-inspector-app.d.ts +15 -14
  314. package/dist/mcp-read-only-tools.js +1 -1
  315. package/dist/memory-action-policy.d.ts +1 -1
  316. package/dist/memory-browse-http-glue.d.ts +170 -0
  317. package/dist/memory-browse-http-glue.js +9 -0
  318. package/dist/memory-browse-http-glue.js.map +1 -0
  319. package/dist/memory-browse-mcp-tools.d.ts +160 -0
  320. package/dist/memory-browse-mcp-tools.js +8 -0
  321. package/dist/memory-browse-mcp-tools.js.map +1 -0
  322. package/dist/memory-browse-operations.d.ts +2 -0
  323. package/dist/memory-browse-operations.js +246 -0
  324. package/dist/memory-browse-operations.js.map +1 -0
  325. package/dist/memory-browse.d.ts +113 -0
  326. package/dist/memory-browse.js +49 -0
  327. package/dist/memory-browse.js.map +1 -0
  328. package/dist/memory-cache.d.ts +1 -1
  329. package/dist/memory-controller.d.ts +1 -1
  330. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  331. package/dist/memory-projection-store.d.ts +1 -1
  332. package/dist/memory-provenance.d.ts +1 -1
  333. package/dist/memory-snapshot.d.ts +1 -1
  334. package/dist/memory-subject-cli.d.ts +15 -14
  335. package/dist/memory-subject.d.ts +15 -14
  336. package/dist/memory-worth-outcomes.d.ts +2 -2
  337. package/dist/models-json.d.ts +1 -1
  338. package/dist/namespaces/migrate.d.ts +3 -3
  339. package/dist/namespaces/migrate.js +14 -14
  340. package/dist/namespaces/principal.d.ts +1 -1
  341. package/dist/namespaces/search.d.ts +1 -1
  342. package/dist/namespaces/search.js +6 -6
  343. package/dist/namespaces/storage.d.ts +3 -3
  344. package/dist/namespaces/storage.js +13 -13
  345. package/dist/native-knowledge.d.ts +1 -1
  346. package/dist/offline-sync-impression-drain.d.ts +1 -1
  347. package/dist/operator-doctor-corpus.d.ts +1 -1
  348. package/dist/operator-doctor-corpus.js +16 -16
  349. package/dist/operator-doctor-replica.d.ts +1 -1
  350. package/dist/operator-toolkit.d.ts +3 -3
  351. package/dist/operator-toolkit.js +23 -22
  352. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  353. package/dist/orchestration/maintenance.d.ts +4 -4
  354. package/dist/orchestration/maintenance.js +23 -23
  355. package/dist/{orchestrator-D9U3Zgjp.d.ts → orchestrator-Cirjo3ZB.d.ts} +10 -10
  356. package/dist/orchestrator.d.ts +11 -11
  357. package/dist/orchestrator.js +122 -117
  358. package/dist/patterns-cli.d.ts +1 -1
  359. package/dist/{pipeline-4lqKJZ_0.d.ts → pipeline-DK79FrgH.d.ts} +1 -1
  360. package/dist/policy-runtime.d.ts +1 -1
  361. package/dist/proactive-contention.d.ts +2 -2
  362. package/dist/provenance.d.ts +77 -2
  363. package/dist/provenance.js +7 -3
  364. package/dist/{public-http-AabioFLh.d.ts → public-http-DaHHhWRR.d.ts} +1 -1
  365. package/dist/{qmd-DnoWaXKb.d.ts → qmd-BT0ErlRS.d.ts} +1 -1
  366. package/dist/qmd-preflight.d.ts +2 -2
  367. package/dist/qmd-recall-cache.d.ts +1 -1
  368. package/dist/qmd.d.ts +2 -2
  369. package/dist/qmd.js +4 -4
  370. package/dist/recall-concurrency-config.d.ts +1 -1
  371. package/dist/recall-context-composition.d.ts +43 -3
  372. package/dist/recall-context-composition.js +5 -1
  373. package/dist/recall-disclosure-escalation.d.ts +1 -1
  374. package/dist/recall-explain-renderer.d.ts +1 -1
  375. package/dist/recall-explain-renderer.js +3 -3
  376. package/dist/recall-memory-map.d.ts +1 -1
  377. package/dist/recall-navigate-access.d.ts +1 -1
  378. package/dist/recall-navigate-access.js +2 -2
  379. package/dist/recall-navigate-disclosure.d.ts +1 -1
  380. package/dist/recall-navigate-disclosure.js +2 -2
  381. package/dist/recall-navigate.d.ts +1 -1
  382. package/dist/recall-navigate.js +2 -2
  383. package/dist/recall-navigation-http-glue.d.ts +15 -14
  384. package/dist/recall-navigation-mcp-tools.d.ts +15 -14
  385. package/dist/recall-navigation-operations.js +44 -43
  386. package/dist/recall-navigation-renderer.d.ts +1 -1
  387. package/dist/recall-navigation.d.ts +1 -1
  388. package/dist/recall-navigation.js +43 -42
  389. package/dist/recall-planner-llm.d.ts +1 -1
  390. package/dist/recall-state-view-admission.d.ts +2 -2
  391. package/dist/recall-state-view-admission.js +5 -5
  392. package/dist/recall-state-view-anchors.d.ts +1 -1
  393. package/dist/recall-state.d.ts +1 -1
  394. package/dist/recall-tag-filter.d.ts +1 -1
  395. package/dist/recall-timings.d.ts +1 -1
  396. package/dist/recall-xray-cli.d.ts +1 -1
  397. package/dist/recall-xray-cli.js +4 -4
  398. package/dist/recall-xray-renderer.d.ts +1 -1
  399. package/dist/recall-xray-renderer.js +3 -3
  400. package/dist/recall-xray.d.ts +1 -1
  401. package/dist/recall-xray.js +2 -2
  402. package/dist/reconcile/cursor.d.ts +1 -1
  403. package/dist/reconcile/manifest.d.ts +4 -1
  404. package/dist/reconcile/manifest.js +1 -1
  405. package/dist/reconcile/plan.d.ts +1 -1
  406. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  407. package/dist/replay/normalizers/claude.d.ts +1 -1
  408. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  409. package/dist/replay/normalizers/shared.d.ts +1 -1
  410. package/dist/replay/runner.d.ts +1 -1
  411. package/dist/replay/types.d.ts +1 -1
  412. package/dist/replica-divergence.d.ts +1 -1
  413. package/dist/replica-peers-config.d.ts +1 -1
  414. package/dist/resolve-auth-token.d.ts +1 -1
  415. package/dist/resume-bundles.js +4 -3
  416. package/dist/retrieval-agents.d.ts +2 -2
  417. package/dist/retrieval-tiers.d.ts +1 -1
  418. package/dist/routing/engine.d.ts +1 -1
  419. package/dist/routing/store.d.ts +1 -1
  420. package/dist/salvage-envelope.d.ts +1 -1
  421. package/dist/schemas.d.ts +160 -160
  422. package/dist/{scope-profiles-crazXegZ.d.ts → scope-profiles-BgPQx9iE.d.ts} +1 -1
  423. package/dist/search/embed-helper.d.ts +1 -1
  424. package/dist/search/factory.d.ts +1 -1
  425. package/dist/search/factory.js +5 -5
  426. package/dist/search/index.d.ts +1 -1
  427. package/dist/search/index.js +9 -9
  428. package/dist/search/lancedb-backend.d.ts +1 -1
  429. package/dist/search/meilisearch-backend.d.ts +1 -1
  430. package/dist/search/noop-backend.d.ts +1 -1
  431. package/dist/search/orama-backend.d.ts +1 -1
  432. package/dist/search/port.d.ts +1 -1
  433. package/dist/search/remote-backend.d.ts +1 -1
  434. package/dist/{semantic-consolidation-B09JI9WM.d.ts → semantic-consolidation-BrAWKVcm.d.ts} +1 -1
  435. package/dist/semantic-consolidation.d.ts +2 -2
  436. package/dist/semantic-consolidation.js +14 -14
  437. package/dist/semantic-rule-promotion.js +13 -13
  438. package/dist/semantic-rule-verifier.d.ts +1 -1
  439. package/dist/semantic-rule-verifier.js +13 -13
  440. package/dist/{service-ByjbCuj7.d.ts → service-CqHOTg87.d.ts} +2 -2
  441. package/dist/session-observer-bands.d.ts +1 -1
  442. package/dist/session-observer-state.d.ts +1 -1
  443. package/dist/shared-context/manager.d.ts +7 -7
  444. package/dist/signal.d.ts +1 -1
  445. package/dist/source-agent-qualifier.d.ts +1 -1
  446. package/dist/source-agent-qualifier.js +17 -17
  447. package/dist/standing-memory-block.d.ts +77 -0
  448. package/dist/standing-memory-block.js +40 -0
  449. package/dist/standing-memory-block.js.map +1 -0
  450. package/dist/standup/build.js +13 -13
  451. package/dist/{storage-CbdF077z.d.ts → storage-mD2YQ8_2.d.ts} +1 -1
  452. package/dist/storage.d.ts +2 -2
  453. package/dist/storage.js +12 -12
  454. package/dist/subject-config.d.ts +1 -1
  455. package/dist/summarizer.d.ts +1 -1
  456. package/dist/summarizer.js +3 -3
  457. package/dist/summary-snapshot.d.ts +1 -1
  458. package/dist/support-passport/index.d.ts +18 -17
  459. package/dist/support-passport/index.js +46 -45
  460. package/dist/{tagging-CF7DNV7_.d.ts → tagging-CiAVA-ID.d.ts} +2 -2
  461. package/dist/temporal-supersession.d.ts +2 -2
  462. package/dist/temporal-timeline-recall.d.ts +1 -1
  463. package/dist/temporal-timeline-recall.js +2 -2
  464. package/dist/temporal-validity.d.ts +1 -1
  465. package/dist/threading.d.ts +1 -1
  466. package/dist/tier-migration.d.ts +2 -2
  467. package/dist/tier-routing.d.ts +1 -1
  468. package/dist/topics.d.ts +1 -1
  469. package/dist/transcript.d.ts +1 -1
  470. package/dist/transfer/autodetect.js +1 -1
  471. package/dist/transfer/backup.js +1 -1
  472. package/dist/transfer/capsule-export.js +2 -2
  473. package/dist/transfer/export-okf.d.ts +1 -1
  474. package/dist/transfer/export-okf.js +13 -13
  475. package/dist/transfer/types.d.ts +78 -78
  476. package/dist/trust-score-stage.d.ts +1 -1
  477. package/dist/trust-score.d.ts +1 -1
  478. package/dist/{types-nmhnJpxx.d.ts → types-DgDaoY61.d.ts} +6 -0
  479. package/dist/types.d.ts +1 -1
  480. package/dist/types.js +1 -1
  481. package/dist/utility-runtime.d.ts +1 -1
  482. package/dist/verified-recall.js +13 -13
  483. package/dist/who-knows-cli.d.ts +15 -14
  484. package/dist/who-knows-cli.js +14 -14
  485. package/dist/who-knows.d.ts +15 -14
  486. package/dist/who-knows.js +13 -13
  487. package/dist/write-envelope.d.ts +1 -1
  488. package/package.json +2 -2
  489. package/src/access-boundary.ts +3 -0
  490. package/src/access-http.ts +2 -2
  491. package/src/access-mcp-output-schemas.ts +25 -0
  492. package/src/access-mcp.ts +3 -3
  493. package/src/access-operations.ts +2 -0
  494. package/src/access-service.ts +6 -1
  495. package/src/access-surface-catalog.test.ts +20 -0
  496. package/src/access-surface-catalog.ts +6 -0
  497. package/src/activity/timeline/index.ts +1 -0
  498. package/src/activity/timeline/journal-recap.test.ts +17 -0
  499. package/src/activity/timeline/journal-recap.ts +19 -6
  500. package/src/activity/timeline/recap-export-range.test.ts +289 -0
  501. package/src/activity/timeline/recap-export-range.ts +169 -0
  502. package/src/activity/timeline/recap-export.test.ts +73 -1
  503. package/src/activity/timeline/recap-export.ts +57 -4
  504. package/src/cli/memory-browse-commands.ts +77 -0
  505. package/src/cli.ts +2 -2
  506. package/src/config.ts +11 -0
  507. package/src/extraction-evidence-class.test.ts +355 -0
  508. package/src/extraction-evidence-class.ts +241 -0
  509. package/src/index.ts +1 -0
  510. package/src/mcp-read-only-tools.ts +3 -0
  511. package/src/memory-browse-http-glue.ts +93 -0
  512. package/src/memory-browse-mcp-tools.ts +57 -0
  513. package/src/memory-browse-operations.ts +88 -0
  514. package/src/memory-browse-surface.test.ts +500 -0
  515. package/src/memory-browse.ts +316 -0
  516. package/src/provenance.ts +3 -3
  517. package/src/recall-context-composition.test.ts +66 -0
  518. package/src/recall-context-composition.ts +102 -2
  519. package/src/reconcile/manifest.test.ts +27 -0
  520. package/src/reconcile/manifest.ts +7 -0
  521. package/src/standing-memory-block.test.ts +262 -0
  522. package/src/standing-memory-block.ts +331 -0
  523. package/src/types.ts +6 -0
  524. package/dist/chunk-2AV75BJW.js.map +0 -1
  525. package/dist/chunk-66QOUYNS.js.map +0 -1
  526. package/dist/chunk-66YUOI53.js.map +0 -1
  527. package/dist/chunk-7ZNJN5WZ.js.map +0 -1
  528. package/dist/chunk-D5DTZBTV.js.map +0 -1
  529. package/dist/chunk-GYYLZOSO.js.map +0 -1
  530. package/dist/chunk-HXDXR2FH.js.map +0 -1
  531. package/dist/chunk-KF57IQFX.js.map +0 -1
  532. package/dist/chunk-TNQR7N52.js.map +0 -1
  533. package/dist/chunk-UGGOQKO6.js.map +0 -1
  534. package/dist/chunk-W6YDAIVB.js.map +0 -1
  535. /package/dist/{chunk-N46SNYMC.js.map → chunk-2B2RHLHI.js.map} +0 -0
  536. /package/dist/{chunk-F5V7KRQS.js.map → chunk-2WMXZYMS.js.map} +0 -0
  537. /package/dist/{chunk-ORQ7AZMU.js.map → chunk-3RKVSECD.js.map} +0 -0
  538. /package/dist/{chunk-3NYM6JA4.js.map → chunk-4LPIVP77.js.map} +0 -0
  539. /package/dist/{chunk-PW4HTWXL.js.map → chunk-55XWRL6F.js.map} +0 -0
  540. /package/dist/{chunk-GGY7WKMB.js.map → chunk-5KBI2MOC.js.map} +0 -0
  541. /package/dist/{chunk-IXMWTNNY.js.map → chunk-6A6HSXWV.js.map} +0 -0
  542. /package/dist/{chunk-P7S7BVBJ.js.map → chunk-7DU2F7LM.js.map} +0 -0
  543. /package/dist/{chunk-4HTQ76O4.js.map → chunk-7SJCSDQ3.js.map} +0 -0
  544. /package/dist/{chunk-U4ESGVZ2.js.map → chunk-7YUX52XZ.js.map} +0 -0
  545. /package/dist/{chunk-FMEP2RB5.js.map → chunk-B4WI7OF2.js.map} +0 -0
  546. /package/dist/{chunk-EUP4AXIW.js.map → chunk-B6VDWDFN.js.map} +0 -0
  547. /package/dist/{chunk-UOREPQ3C.js.map → chunk-BIXELUIR.js.map} +0 -0
  548. /package/dist/{chunk-AYZHHMTK.js.map → chunk-BNX2TUSF.js.map} +0 -0
  549. /package/dist/{chunk-5ZXGH2ZL.js.map → chunk-BXOPUJ3W.js.map} +0 -0
  550. /package/dist/{chunk-TE6B2UU6.js.map → chunk-CD7VTZIG.js.map} +0 -0
  551. /package/dist/{chunk-EC5SVM6H.js.map → chunk-DE4ZGH2N.js.map} +0 -0
  552. /package/dist/{chunk-YFVIA5A6.js.map → chunk-DRLZB3CN.js.map} +0 -0
  553. /package/dist/{chunk-SRRZZHA5.js.map → chunk-EGOWY342.js.map} +0 -0
  554. /package/dist/{chunk-W44T2KHG.js.map → chunk-EWBDJQ5Y.js.map} +0 -0
  555. /package/dist/{chunk-JMBTNVMW.js.map → chunk-FGRJJBFO.js.map} +0 -0
  556. /package/dist/{chunk-WGZHSQDV.js.map → chunk-G2XZFGYN.js.map} +0 -0
  557. /package/dist/{chunk-CFIHVWYZ.js.map → chunk-GGROFIYX.js.map} +0 -0
  558. /package/dist/{chunk-XLZZLUWL.js.map → chunk-HG74GEVY.js.map} +0 -0
  559. /package/dist/{chunk-WUUGE7QC.js.map → chunk-HS75MCQF.js.map} +0 -0
  560. /package/dist/{chunk-DO6R3LIE.js.map → chunk-I6IJDTRP.js.map} +0 -0
  561. /package/dist/{chunk-2NKY7OC6.js.map → chunk-IMJPJY7V.js.map} +0 -0
  562. /package/dist/{chunk-TZ44E732.js.map → chunk-IO7K7GES.js.map} +0 -0
  563. /package/dist/{chunk-7BNB7KHB.js.map → chunk-JIURESYF.js.map} +0 -0
  564. /package/dist/{chunk-AYP2FZFA.js.map → chunk-JUOJGZPV.js.map} +0 -0
  565. /package/dist/{chunk-4XTSKGLX.js.map → chunk-JYXLW22X.js.map} +0 -0
  566. /package/dist/{chunk-IPLYGWQF.js.map → chunk-KQAFEZQX.js.map} +0 -0
  567. /package/dist/{chunk-AN2O4KZY.js.map → chunk-KVIMQAER.js.map} +0 -0
  568. /package/dist/{chunk-VGRVJ6Z6.js.map → chunk-LIAM7U2J.js.map} +0 -0
  569. /package/dist/{chunk-S3SND7SO.js.map → chunk-LIDJWAQV.js.map} +0 -0
  570. /package/dist/{chunk-T5NI5K46.js.map → chunk-LYMJ36UI.js.map} +0 -0
  571. /package/dist/{chunk-UEJY6ZTQ.js.map → chunk-MKLDZNBD.js.map} +0 -0
  572. /package/dist/{chunk-QUFJ3NJE.js.map → chunk-MNG536IS.js.map} +0 -0
  573. /package/dist/{chunk-SW42MMAH.js.map → chunk-MQT3WP3F.js.map} +0 -0
  574. /package/dist/{chunk-5EHPB6RL.js.map → chunk-MUNR43V3.js.map} +0 -0
  575. /package/dist/{chunk-OV7GJ4KW.js.map → chunk-NNFB4CU4.js.map} +0 -0
  576. /package/dist/{chunk-47C6DF43.js.map → chunk-OS4JVTHR.js.map} +0 -0
  577. /package/dist/{chunk-JOP7W24J.js.map → chunk-PA67GXYJ.js.map} +0 -0
  578. /package/dist/{chunk-FSH6KRKO.js.map → chunk-PPJLJD2D.js.map} +0 -0
  579. /package/dist/{chunk-AG6PANBG.js.map → chunk-PXJ6HE2W.js.map} +0 -0
  580. /package/dist/{chunk-DDBLZJRV.js.map → chunk-QB4VXCFN.js.map} +0 -0
  581. /package/dist/{chunk-L63M2PE4.js.map → chunk-QGAKUOKU.js.map} +0 -0
  582. /package/dist/{chunk-YUQPUK6H.js.map → chunk-QLCMOHRE.js.map} +0 -0
  583. /package/dist/{chunk-Q4AUIFUA.js.map → chunk-QMWKENNK.js.map} +0 -0
  584. /package/dist/{chunk-EEHQY7ER.js.map → chunk-SK6HEQTW.js.map} +0 -0
  585. /package/dist/{chunk-PGAB2V53.js.map → chunk-SMBWBEE7.js.map} +0 -0
  586. /package/dist/{chunk-62NYUFNH.js.map → chunk-SS33RR4F.js.map} +0 -0
  587. /package/dist/{chunk-4ECS33D4.js.map → chunk-T3T5HTZZ.js.map} +0 -0
  588. /package/dist/{chunk-KG2OOAFF.js.map → chunk-TQSCR5DU.js.map} +0 -0
  589. /package/dist/{chunk-AS2NOPDM.js.map → chunk-V7IONNTJ.js.map} +0 -0
  590. /package/dist/{chunk-HYWPUYLO.js.map → chunk-VV5GCNKG.js.map} +0 -0
  591. /package/dist/{chunk-Y3XHIWVP.js.map → chunk-WL3PGPGU.js.map} +0 -0
  592. /package/dist/{chunk-3UQIS2GC.js.map → chunk-WNMVUWI7.js.map} +0 -0
  593. /package/dist/{chunk-WOBMSMUK.js.map → chunk-WYU66IVZ.js.map} +0 -0
  594. /package/dist/{chunk-A7GQBJP5.js.map → chunk-X2NFFBTO.js.map} +0 -0
  595. /package/dist/{chunk-CWMMKFO7.js.map → chunk-XEHLYA5Z.js.map} +0 -0
  596. /package/dist/{chunk-JIWZVOQM.js.map → chunk-XKHRNAK7.js.map} +0 -0
  597. /package/dist/{chunk-VW5TGNJN.js.map → chunk-XXLH6KFO.js.map} +0 -0
  598. /package/dist/{chunk-J5OXWG66.js.map → chunk-XZUHXKM3.js.map} +0 -0
  599. /package/dist/{chunk-K4KE5MYE.js.map → chunk-Y5VT7BIT.js.map} +0 -0
  600. /package/dist/{chunk-S7KDHFF2.js.map → chunk-YXWMZAAN.js.map} +0 -0
  601. /package/dist/{chunk-EAZ3YZKN.js.map → chunk-ZEIJQ4I3.js.map} +0 -0
@@ -6,15 +6,28 @@
6
6
  * valid file body. Does not invent people, mood, or productivity claims.
7
7
  */
8
8
  import { activityDayWindow } from "../digest.js";
9
- import type { TimelineCard } from "./types.js";
9
+ import type { TimelineCardKind } from "./types.js";
10
10
 
11
+ /**
12
+ * Minimal card shape the renderer reads. `TimelineCard` is assignable, and
13
+ * so is the privacy-projected export card whose `title` is stripped
14
+ * (issue #2051): a title-less card renders its stable id instead.
15
+ */
16
+ export interface JournalRenderCard {
17
+ id: string;
18
+ kind: TimelineCardKind;
19
+ title?: string;
20
+ categoryId: string;
21
+ startUtc: string;
22
+ endUtc: string;
23
+ }
11
24
  export interface DeterministicJournalOptions {
12
25
  date: string;
13
26
  timezone: string;
14
27
  }
15
28
 
16
29
  interface ClippedCard {
17
- card: TimelineCard;
30
+ card: JournalRenderCard;
18
31
  startMs: number;
19
32
  endMs: number;
20
33
  durationMs: number;
@@ -25,7 +38,7 @@ function formatMinutes(ms: number): string {
25
38
  }
26
39
 
27
40
  function clipToWindow(
28
- cards: readonly TimelineCard[],
41
+ cards: readonly JournalRenderCard[],
29
42
  winStart: number,
30
43
  winEnd: number,
31
44
  ): ClippedCard[] {
@@ -42,7 +55,7 @@ function clipToWindow(
42
55
  clipped.sort((left, right) => {
43
56
  const byStart = left.startMs - right.startMs;
44
57
  if (byStart !== 0) return byStart;
45
- const byTitle = left.card.title.localeCompare(right.card.title);
58
+ const byTitle = (left.card.title ?? "").localeCompare(right.card.title ?? "");
46
59
  if (byTitle !== 0) return byTitle;
47
60
  return left.card.id.localeCompare(right.card.id);
48
61
  });
@@ -91,7 +104,7 @@ function countGaps(
91
104
 
92
105
  /** Render a byte-stable Markdown journal for one local day. */
93
106
  export function renderDeterministicJournal(
94
- cards: readonly TimelineCard[],
107
+ cards: readonly JournalRenderCard[],
95
108
  options: DeterministicJournalOptions,
96
109
  ): string {
97
110
  const window = activityDayWindow(options.date, options.timezone);
@@ -108,7 +121,7 @@ export function renderDeterministicJournal(
108
121
  const cardLines =
109
122
  clipped.length === 0
110
123
  ? ["_No cards._"]
111
- : clipped.map((row) => `- ${formatMinutes(row.durationMs)} ${row.card.title}`);
124
+ : clipped.map((row) => `- ${formatMinutes(row.durationMs)} ${row.card.title ?? row.card.id}`);
112
125
  return [
113
126
  `# Journal — ${options.date} (${options.timezone})`,
114
127
  "",
@@ -0,0 +1,289 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import type { TimelineCard } from "./types.js";
5
+ import {
6
+ DEFAULT_RECAP_RANGE_MAX_DAYS,
7
+ exportDeterministicRecapRange,
8
+ renderDeterministicJournalRange,
9
+ } from "./recap-export-range.js";
10
+ import { renderDeterministicJournal } from "./journal-recap.js";
11
+
12
+ const TZ = "UTC";
13
+
14
+ function card(
15
+ overrides: Partial<TimelineCard> & Pick<TimelineCard, "id" | "dayKey">,
16
+ ): TimelineCard {
17
+ return {
18
+ kind: "activity",
19
+ title: "Untitled",
20
+ summary: "none",
21
+ categoryId: "development",
22
+ confidence: 1,
23
+ startUtc: `${overrides.dayKey}T09:00:00.000Z`,
24
+ endUtc: `${overrides.dayKey}T10:00:00.000Z`,
25
+ timezone: TZ,
26
+ machine: "ws-a",
27
+ evidenceIds: [],
28
+ evidenceRange: null,
29
+ ...overrides,
30
+ };
31
+ }
32
+
33
+ function okRange(options: Parameters<typeof exportDeterministicRecapRange>[0]) {
34
+ const result = exportDeterministicRecapRange(options);
35
+ assert.equal(result.ok, true);
36
+ assert.ok(result.ok);
37
+ return result.range;
38
+ }
39
+
40
+ test("empty range days are exported with empty card lists", () => {
41
+ const range = okRange({
42
+ startDate: "2026-08-17",
43
+ endDate: "2026-08-18",
44
+ timezone: TZ,
45
+ cards: [],
46
+ });
47
+ assert.deepEqual(
48
+ range.days.map((d) => [d.date, d.cards.length]),
49
+ [
50
+ ["2026-08-17", 0],
51
+ ["2026-08-18", 0],
52
+ ],
53
+ );
54
+ });
55
+
56
+ test("cards land on their dayKey; out-of-range cards are dropped", () => {
57
+ const range = okRange({
58
+ startDate: "2026-08-17",
59
+ endDate: "2026-08-18",
60
+ timezone: TZ,
61
+ cards: [
62
+ card({ id: "b", dayKey: "2026-08-18" }),
63
+ card({ id: "a", dayKey: "2026-08-17" }),
64
+ card({ id: "x", dayKey: "2026-08-16" }),
65
+ card({ id: "y", dayKey: "2026-08-19" }),
66
+ ],
67
+ });
68
+ assert.deepEqual(
69
+ range.days.map((d) => d.cards.map((c) => c.id)),
70
+ [["a"], ["b"]],
71
+ );
72
+ });
73
+
74
+ test("determinism: same inputs, shuffled card order, identical JSON bytes", () => {
75
+ const cards = [
76
+ card({ id: "c2", dayKey: "2026-08-17" }),
77
+ card({ id: "c1", dayKey: "2026-08-18" }),
78
+ card({ id: "c3", dayKey: "2026-08-17" }),
79
+ ];
80
+ const first = JSON.stringify(
81
+ exportDeterministicRecapRange({
82
+ startDate: "2026-08-17",
83
+ endDate: "2026-08-18",
84
+ timezone: TZ,
85
+ cards,
86
+ }),
87
+ );
88
+ const second = JSON.stringify(
89
+ exportDeterministicRecapRange({
90
+ startDate: "2026-08-17",
91
+ endDate: "2026-08-18",
92
+ timezone: TZ,
93
+ cards: [cards[1], cards[2], cards[0]],
94
+ }),
95
+ );
96
+ assert.equal(first, second);
97
+ assert.equal(
98
+ first,
99
+ JSON.stringify(
100
+ exportDeterministicRecapRange({
101
+ startDate: "2026-08-17",
102
+ endDate: "2026-08-18",
103
+ timezone: TZ,
104
+ cards,
105
+ }),
106
+ ),
107
+ );
108
+ });
109
+
110
+ test("privacy boundary: titles are omitted by default in range exports", () => {
111
+ const range = okRange({
112
+ startDate: "2026-08-17",
113
+ endDate: "2026-08-17",
114
+ timezone: TZ,
115
+ cards: [card({ id: "a", dayKey: "2026-08-17", title: "Secret Window Title" })],
116
+ });
117
+ const json = JSON.stringify(range);
118
+ assert.equal(json.includes("Secret Window Title"), false);
119
+ assert.equal(range.days[0].cards[0].title, undefined);
120
+ });
121
+
122
+ test("privacy boundary: includeObservations keeps titles in range exports", () => {
123
+ const range = okRange({
124
+ startDate: "2026-08-17",
125
+ endDate: "2026-08-17",
126
+ timezone: TZ,
127
+ cards: [card({ id: "a", dayKey: "2026-08-17", title: "Secret Window Title" })],
128
+ includeObservations: true,
129
+ });
130
+ assert.equal(range.days[0].cards[0].title, "Secret Window Title");
131
+ });
132
+
133
+ test("markdown: redacted days render card ids, never titles", () => {
134
+ const rendered = renderDeterministicJournalRange({
135
+ startDate: "2026-08-17",
136
+ endDate: "2026-08-17",
137
+ timezone: TZ,
138
+ cards: [card({ id: "card-a", dayKey: "2026-08-17", title: "Secret Window Title" })],
139
+ });
140
+ assert.ok(rendered.ok);
141
+ assert.ok(rendered.ok);
142
+ assert.equal(rendered.markdown.includes("Secret Window Title"), false);
143
+ assert.equal(rendered.markdown.includes("- 60m card-a"), true);
144
+ });
145
+
146
+ test("markdown: a single-day range equals the single-day renderer", () => {
147
+ const cards = [card({ id: "a", dayKey: "2026-08-17", title: "Terminal" })];
148
+ const rendered = renderDeterministicJournalRange({
149
+ startDate: "2026-08-17",
150
+ endDate: "2026-08-17",
151
+ timezone: TZ,
152
+ cards,
153
+ includeObservations: true,
154
+ });
155
+ assert.ok(rendered.ok);
156
+ assert.ok(rendered.ok);
157
+ assert.equal(
158
+ rendered.markdown,
159
+ renderDeterministicJournal(cards, { date: "2026-08-17", timezone: TZ }),
160
+ );
161
+ });
162
+
163
+ test("markdown: multi-day ranges emit ascending per-day sections", () => {
164
+ const rendered = renderDeterministicJournalRange({
165
+ startDate: "2026-08-17",
166
+ endDate: "2026-08-19",
167
+ timezone: TZ,
168
+ cards: [],
169
+ });
170
+ assert.ok(rendered.ok);
171
+ assert.ok(rendered.ok);
172
+ assert.equal(rendered.dayCount, 3);
173
+ const headings = rendered.markdown
174
+ .split("\n")
175
+ .filter((line) => line.startsWith("# Journal — "));
176
+ assert.deepEqual(headings, [
177
+ "# Journal — 2026-08-17 (UTC)",
178
+ "# Journal — 2026-08-18 (UTC)",
179
+ "# Journal — 2026-08-19 (UTC)",
180
+ ]);
181
+ });
182
+
183
+ test("invalid and inverted ranges fail with typed errors", () => {
184
+ const cases: Array<
185
+ [Parameters<typeof exportDeterministicRecapRange>[0], string]
186
+ > = [
187
+ [{ startDate: "", endDate: "2026-08-18", timezone: TZ, cards: [] }, "invalid_start_date"],
188
+ [{ startDate: "2026-8-17", endDate: "2026-08-18", timezone: TZ, cards: [] }, "invalid_start_date"],
189
+ [{ startDate: "2026-02-30", endDate: "2026-03-01", timezone: TZ, cards: [] }, "invalid_start_date"],
190
+ [{ startDate: "2026-08-17", endDate: "2026-13-01", timezone: TZ, cards: [] }, "invalid_end_date"],
191
+ [{ startDate: "2026-08-18", endDate: "2026-08-17", timezone: TZ, cards: [] }, "end_before_start"],
192
+ ];
193
+ for (const [options, error] of cases) {
194
+ const result = exportDeterministicRecapRange(options);
195
+ assert.equal(result.ok, false, JSON.stringify(options));
196
+ assert.ok(!result.ok);
197
+ assert.deepEqual(
198
+ { error: result.error, limit: result.limit },
199
+ { error, limit: undefined },
200
+ );
201
+ }
202
+ });
203
+
204
+ test("range bound: spans past the default 366 days fail with the limit", () => {
205
+ const result = exportDeterministicRecapRange({
206
+ startDate: "2026-01-01",
207
+ endDate: "2027-01-02",
208
+ timezone: TZ,
209
+ cards: [],
210
+ });
211
+ assert.ok(!result.ok);
212
+ assert.deepEqual(
213
+ { error: result.error, limit: result.limit },
214
+ { error: "range_too_large", limit: DEFAULT_RECAP_RANGE_MAX_DAYS },
215
+ );
216
+ // Exactly the bound still passes.
217
+ const atBound = exportDeterministicRecapRange({
218
+ startDate: "2026-01-01",
219
+ endDate: "2026-12-31",
220
+ timezone: TZ,
221
+ cards: [],
222
+ });
223
+ assert.ok(atBound.ok);
224
+ assert.ok(atBound.ok);
225
+ assert.equal(atBound.range.days.length, 365);
226
+ });
227
+
228
+ test("range bound: maxDays override is enforced and validated", () => {
229
+ const denied = exportDeterministicRecapRange({
230
+ startDate: "2026-08-17",
231
+ endDate: "2026-08-20",
232
+ timezone: TZ,
233
+ cards: [],
234
+ maxDays: 2,
235
+ });
236
+ assert.ok(!denied.ok);
237
+ assert.equal(denied.error, "range_too_large");
238
+ assert.equal(denied.limit, 2);
239
+ assert.throws(
240
+ () =>
241
+ exportDeterministicRecapRange({
242
+ startDate: "2026-08-17",
243
+ endDate: "2026-08-17",
244
+ timezone: TZ,
245
+ cards: [],
246
+ maxDays: 0,
247
+ }),
248
+ RangeError,
249
+ );
250
+ });
251
+
252
+ test("a card spanning midnight attributes only to its stored dayKey", () => {
253
+ const range = okRange({
254
+ startDate: "2026-08-17",
255
+ endDate: "2026-08-18",
256
+ timezone: TZ,
257
+ cards: [
258
+ card({
259
+ id: "spans",
260
+ dayKey: "2026-08-17",
261
+ startUtc: "2026-08-17T23:00:00.000Z",
262
+ endUtc: "2026-08-18T01:00:00.000Z",
263
+ }),
264
+ ],
265
+ includeObservations: true,
266
+ });
267
+ assert.deepEqual(
268
+ range.days.map((d) => d.cards.map((c) => c.id)),
269
+ [["spans"], []],
270
+ );
271
+ // The markdown render clips the spanning card to each day window: it
272
+ // contributes 60m to day 1 and nothing to day 2.
273
+ const day2 = renderDeterministicJournalRange({
274
+ startDate: "2026-08-18",
275
+ endDate: "2026-08-18",
276
+ timezone: TZ,
277
+ cards: [
278
+ card({
279
+ id: "spans",
280
+ dayKey: "2026-08-17",
281
+ startUtc: "2026-08-17T23:00:00.000Z",
282
+ endUtc: "2026-08-18T01:00:00.000Z",
283
+ }),
284
+ ],
285
+ });
286
+ assert.ok(day2.ok);
287
+ assert.ok(day2.ok);
288
+ assert.equal(day2.markdown.includes("- 60m spans"), false);
289
+ });
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Deterministic recap export over a bounded date range (issue #2051).
3
+ *
4
+ * Pure: cards + inclusive [startDate, endDate] + timezone in, stable JSON or
5
+ * Markdown out. No LLM, no I/O. Every date in the range gets a day entry —
6
+ * empty days included — and cards land on their stored `dayKey`, so reruns
7
+ * with the same inputs always produce the same bytes regardless of input
8
+ * card order. Cards whose `dayKey` falls outside the range are not exported.
9
+ *
10
+ * The range is bounded: at most `maxDays` days (default 366) per call, so a
11
+ * swapped start/end or a multi-year span fails with a typed error instead of
12
+ * producing an unbounded document.
13
+ */
14
+ import { isValidActivityDate } from "../digest.js";
15
+ import { parseRecapDate } from "./recap-date.js";
16
+ import { renderDeterministicJournal } from "./journal-recap.js";
17
+ import {
18
+ projectCardForRecapExport,
19
+ type RecapExportCard,
20
+ } from "./recap-export.js";
21
+ import type { TimelineCard } from "./types.js";
22
+
23
+ export const DEFAULT_RECAP_RANGE_MAX_DAYS = 366;
24
+
25
+ export interface DeterministicRecapRangeOptions {
26
+ /** Inclusive first local day, YYYY-MM-DD. */
27
+ startDate: string;
28
+ /** Inclusive last local day, YYYY-MM-DD. */
29
+ endDate: string;
30
+ timezone: string;
31
+ cards: readonly TimelineCard[];
32
+ /** Include observation-derived card content (`title`). Default false. */
33
+ includeObservations?: boolean;
34
+ /** Hard upper bound on exported days; default 366. */
35
+ maxDays?: number;
36
+ }
37
+
38
+ export interface DeterministicRecapDay {
39
+ date: string;
40
+ timezone: string;
41
+ cards: RecapExportCard[];
42
+ }
43
+
44
+ export interface DeterministicRecapRange {
45
+ startDate: string;
46
+ endDate: string;
47
+ timezone: string;
48
+ days: DeterministicRecapDay[];
49
+ }
50
+
51
+ export type DeterministicRecapRangeResult =
52
+ | { ok: true; range: DeterministicRecapRange }
53
+ | {
54
+ ok: false;
55
+ error:
56
+ | "invalid_start_date"
57
+ | "invalid_end_date"
58
+ | "end_before_start"
59
+ | "range_too_large";
60
+ /** The enforced day bound, present on range_too_large. */
61
+ limit?: number;
62
+ };
63
+
64
+ export type DeterministicRecapRangeMarkdownResult =
65
+ | { ok: true; markdown: string; dayCount: number }
66
+ | Extract<DeterministicRecapRangeResult, { ok: false }>;
67
+
68
+ function compareCardIds(a: RecapExportCard, b: RecapExportCard): number {
69
+ if (a.id < b.id) return -1;
70
+ if (a.id > b.id) return 1;
71
+ return 0;
72
+ }
73
+
74
+ /** UTC-midnight stepping over validated real calendar dates. */
75
+ function* isoDays(startDate: string, endDate: string): Generator<string> {
76
+ const endMs = Date.parse(`${endDate}T00:00:00Z`);
77
+ let cursor = new Date(`${startDate}T00:00:00Z`);
78
+ while (cursor.getTime() <= endMs) {
79
+ yield cursor.toISOString().slice(0, 10);
80
+ cursor = new Date(cursor.getTime() + 86_400_000);
81
+ }
82
+ }
83
+
84
+ function dayCount(startDate: string, endDate: string): number {
85
+ return (
86
+ (Date.parse(`${endDate}T00:00:00Z`) - Date.parse(`${startDate}T00:00:00Z`)) /
87
+ 86_400_000 +
88
+ 1
89
+ );
90
+ }
91
+
92
+ /**
93
+ * Build the byte-stable JSON recap document for an inclusive local-day
94
+ * range. Day entries are ascending; day cards are sorted by id; per-card
95
+ * key order is fixed by `projectCardForRecapExport`.
96
+ */
97
+ export function exportDeterministicRecapRange(
98
+ options: DeterministicRecapRangeOptions,
99
+ ): DeterministicRecapRangeResult {
100
+ const start = parseRecapDate(options.startDate);
101
+ if (!start.ok || !isValidActivityDate(start.date)) {
102
+ return { ok: false, error: "invalid_start_date" };
103
+ }
104
+ const end = parseRecapDate(options.endDate);
105
+ if (!end.ok || !isValidActivityDate(end.date)) {
106
+ return { ok: false, error: "invalid_end_date" };
107
+ }
108
+ if (Date.parse(`${end.date}T00:00:00Z`) < Date.parse(`${start.date}T00:00:00Z`)) {
109
+ return { ok: false, error: "end_before_start" };
110
+ }
111
+ const limit = options.maxDays ?? DEFAULT_RECAP_RANGE_MAX_DAYS;
112
+ if (!Number.isInteger(limit) || limit < 1) {
113
+ throw new RangeError(`maxDays must be a positive integer; got ${options.maxDays}`);
114
+ }
115
+ if (dayCount(start.date, end.date) > limit) {
116
+ return { ok: false, error: "range_too_large", limit };
117
+ }
118
+
119
+ const includeObservations = options.includeObservations === true;
120
+ const byDay = new Map<string, RecapExportCard[]>();
121
+ for (const card of options.cards) {
122
+ if (card.dayKey < start.date || card.dayKey > end.date) continue;
123
+ const bucket = byDay.get(card.dayKey);
124
+ const projected = projectCardForRecapExport(card, includeObservations);
125
+ if (bucket === undefined) byDay.set(card.dayKey, [projected]);
126
+ else bucket.push(projected);
127
+ }
128
+
129
+ const days: DeterministicRecapDay[] = [];
130
+ for (const date of isoDays(start.date, end.date)) {
131
+ days.push({
132
+ date,
133
+ timezone: options.timezone,
134
+ cards: (byDay.get(date) ?? []).sort(compareCardIds),
135
+ });
136
+ }
137
+ return {
138
+ ok: true,
139
+ range: {
140
+ startDate: start.date,
141
+ endDate: end.date,
142
+ timezone: options.timezone,
143
+ days,
144
+ },
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Render the byte-stable Markdown recap for an inclusive local-day range:
150
+ * one `renderDeterministicJournal` section per day, ascending. Validation
151
+ * and bounds match `exportDeterministicRecapRange`.
152
+ */
153
+ export function renderDeterministicJournalRange(
154
+ options: DeterministicRecapRangeOptions,
155
+ ): DeterministicRecapRangeMarkdownResult {
156
+ const exported = exportDeterministicRecapRange(options);
157
+ if (!exported.ok) return exported;
158
+ const sections = exported.range.days.map((day) =>
159
+ renderDeterministicJournal(day.cards, {
160
+ date: day.date,
161
+ timezone: day.timezone,
162
+ }),
163
+ );
164
+ return {
165
+ ok: true,
166
+ markdown: sections.join("\n"),
167
+ dayCount: exported.range.days.length,
168
+ };
169
+ }
@@ -2,7 +2,10 @@ import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
3
 
4
4
  import type { TimelineCard } from "./types.js";
5
- import { exportDeterministicRecap } from "./recap-export.js";
5
+ import {
6
+ exportDeterministicRecap,
7
+ projectCardForRecapExport,
8
+ } from "./recap-export.js";
6
9
 
7
10
  const DATE = "2026-08-17";
8
11
 
@@ -76,3 +79,72 @@ test("input cards are not mutated and reruns are stable", () => {
76
79
  assert.deepEqual(input.map((c) => c.id), ["card-b", "card-a"]);
77
80
  assert.equal(JSON.stringify(first), JSON.stringify(second));
78
81
  });
82
+
83
+ test("privacy boundary: default export omits observation-derived titles", () => {
84
+ const recap = exportDeterministicRecap({
85
+ date: DATE,
86
+ timezone: "UTC",
87
+ cards: [card({ id: "card-a", title: "Secret Window Title" })],
88
+ });
89
+ const json = JSON.stringify(recap);
90
+ assert.equal(json.includes("Secret Window Title"), false);
91
+ assert.equal(recap.cards[0].title, undefined);
92
+ // Evidence references survive: the export points at content, never copies it.
93
+ assert.deepEqual(recap.cards[0].evidenceIds, [1]);
94
+ assert.equal(recap.cards[0].dayKey, DATE);
95
+ });
96
+
97
+ test("privacy boundary: includeObservations keeps the title", () => {
98
+ const recap = exportDeterministicRecap({
99
+ date: DATE,
100
+ timezone: "UTC",
101
+ cards: [card({ id: "card-a", title: "Secret Window Title" })],
102
+ includeObservations: true,
103
+ });
104
+ assert.equal(recap.cards[0].title, "Secret Window Title");
105
+ });
106
+
107
+ test("manualEdit provenance is kept and sorts last in the projected card", () => {
108
+ const projected = projectCardForRecapExport(
109
+ card({
110
+ id: "card-a",
111
+ manualEdit: { categoryId: "focus", editedAtUtc: "2026-08-17T12:00:00.000Z" },
112
+ }),
113
+ false,
114
+ );
115
+ assert.equal(projected.manualEdit?.categoryId, "focus");
116
+ assert.equal(Object.keys(projected).at(-1), "manualEdit");
117
+ });
118
+ test("projectCardForRecapExport emits a fixed key order without the title", () => {
119
+ const projected = projectCardForRecapExport(card({ id: "card-a" }), false);
120
+ assert.deepEqual(Object.keys(projected), [
121
+ "id",
122
+ "kind",
123
+ "summary",
124
+ "categoryId",
125
+ "confidence",
126
+ "startUtc",
127
+ "endUtc",
128
+ "dayKey",
129
+ "timezone",
130
+ "machine",
131
+ "evidenceIds",
132
+ "evidenceRange",
133
+ ]);
134
+ });
135
+
136
+
137
+ test("projected exports are byte-identical across reruns and shuffled input", () => {
138
+ const cards = [card({ id: "c3" }), card({ id: "c1" }), card({ id: "c2" })];
139
+ const first = JSON.stringify(
140
+ exportDeterministicRecap({ date: DATE, timezone: "UTC", cards }),
141
+ );
142
+ const second = JSON.stringify(
143
+ exportDeterministicRecap({
144
+ date: DATE,
145
+ timezone: "UTC",
146
+ cards: [cards[2], cards[0], cards[1]],
147
+ }),
148
+ );
149
+ assert.equal(first, second);
150
+ });
@@ -4,6 +4,12 @@
4
4
  * Pure: cards + date + timezone in, stable JSON out. No LLM, no I/O, no
5
5
  * persistence. Same cards always serialize to the same bytes, regardless of
6
6
  * input array order: cards are sorted by id before serialization.
7
+ *
8
+ * Privacy boundary (issue #2053 policy): a card `title` is captured
9
+ * observation content (the source window title). It is excluded unless the
10
+ * export explicitly permits observations — the same default as
11
+ * `activity.exportIncludeObservations`. Evidence references (store row ids,
12
+ * identity hashes) are always kept: they point at content without copying it.
7
13
  */
8
14
  import type { TimelineCard } from "./types.js";
9
15
 
@@ -11,15 +17,58 @@ export interface DeterministicRecapOptions {
11
17
  date: string;
12
18
  timezone: string;
13
19
  cards: readonly TimelineCard[];
20
+ /**
21
+ * Include observation-derived card content (`title`). Default false,
22
+ * matching the `activity.exportIncludeObservations` policy default.
23
+ */
24
+ includeObservations?: boolean;
25
+ }
26
+
27
+ /** Card projected for export: `title` present only when the policy permits. */
28
+ export interface RecapExportCard extends Omit<TimelineCard, "title"> {
29
+ title?: string;
14
30
  }
15
31
 
16
32
  export interface DeterministicRecap {
17
33
  date: string;
18
34
  timezone: string;
19
- cards: TimelineCard[];
35
+ cards: RecapExportCard[];
36
+ }
37
+
38
+ /**
39
+ * Project one card for export with a fixed key order. Observation-derived
40
+ * `title` is dropped unless `includeObservations`; user-authored provenance
41
+ * (manual edits, pause reasons inside `summary`) stays, and evidence
42
+ * references always stay.
43
+ */
44
+ export function projectCardForRecapExport(
45
+ card: TimelineCard,
46
+ includeObservations: boolean,
47
+ ): RecapExportCard {
48
+ const projected: RecapExportCard = {
49
+ id: card.id,
50
+ kind: card.kind,
51
+ summary: card.summary,
52
+ categoryId: card.categoryId,
53
+ confidence: card.confidence,
54
+ startUtc: card.startUtc,
55
+ endUtc: card.endUtc,
56
+ dayKey: card.dayKey,
57
+ timezone: card.timezone,
58
+ machine: card.machine,
59
+ evidenceIds: card.evidenceIds,
60
+ evidenceRange: card.evidenceRange,
61
+ };
62
+ if (card.manualEdit !== undefined) {
63
+ projected.manualEdit = card.manualEdit;
64
+ }
65
+ if (includeObservations) {
66
+ projected.title = card.title;
67
+ }
68
+ return projected;
20
69
  }
21
70
 
22
- function compareCardIds(a: TimelineCard, b: TimelineCard): number {
71
+ function compareCardIds(a: RecapExportCard, b: RecapExportCard): number {
23
72
  if (a.id < b.id) return -1;
24
73
  if (a.id > b.id) return 1;
25
74
  return 0;
@@ -28,14 +77,18 @@ function compareCardIds(a: TimelineCard, b: TimelineCard): number {
28
77
  /**
29
78
  * Build the byte-stable JSON recap document for one local day.
30
79
  * Callers may `JSON.stringify` the returned object; construction order
31
- * (date, timezone, cards) is fixed, so serialization is deterministic.
80
+ * (date, timezone, cards, and the per-card key order) is fixed, so
81
+ * serialization is deterministic.
32
82
  */
33
83
  export function exportDeterministicRecap(
34
84
  options: DeterministicRecapOptions,
35
85
  ): DeterministicRecap {
86
+ const includeObservations = options.includeObservations === true;
36
87
  return {
37
88
  date: options.date,
38
89
  timezone: options.timezone,
39
- cards: [...options.cards].sort(compareCardIds),
90
+ cards: options.cards
91
+ .map((card) => projectCardForRecapExport(card, includeObservations))
92
+ .sort(compareCardIds),
40
93
  };
41
94
  }