@remnic/core 9.15.2 → 9.16.0

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 (314) hide show
  1. package/dist/access-admin-ops-surface.d.ts +8 -8
  2. package/dist/access-admin-ops-surface.js +7 -7
  3. package/dist/access-boundary.d.ts +8 -8
  4. package/dist/access-boundary.js +7 -7
  5. package/dist/access-cli.js +31 -30
  6. package/dist/access-cli.js.map +1 -1
  7. package/dist/access-http.d.ts +8 -8
  8. package/dist/access-http.js +11 -11
  9. package/dist/access-identity-continuity-surface.d.ts +6 -6
  10. package/dist/access-identity-continuity-surface.js +7 -7
  11. package/dist/access-lcm-surface.d.ts +8 -8
  12. package/dist/access-lcm-surface.js +7 -7
  13. package/dist/access-mcp.d.ts +8 -8
  14. package/dist/access-mcp.js +10 -10
  15. package/dist/access-memory-search-fanout.d.ts +2 -2
  16. package/dist/{access-namespace-preflight-B9e5GnyZ.d.ts → access-namespace-preflight-DwhEFYy3.d.ts} +1 -1
  17. package/dist/access-namespace-preflight.d.ts +3 -3
  18. package/dist/access-namespace-preflight.js +7 -7
  19. package/dist/access-observe-write-surface.d.ts +8 -8
  20. package/dist/access-observe-write-surface.js +7 -7
  21. package/dist/access-operations-batch.js +8 -8
  22. package/dist/access-operations.d.ts +11 -11
  23. package/dist/access-operations.js +9 -9
  24. package/dist/access-recall-concurrency.d.ts +8 -8
  25. package/dist/access-recall-concurrency.js +7 -7
  26. package/dist/access-recall-response.d.ts +8 -8
  27. package/dist/access-recall-response.js +7 -7
  28. package/dist/access-recall-surface.d.ts +8 -8
  29. package/dist/access-recall-surface.js +7 -7
  30. package/dist/access-schema.d.ts +76 -76
  31. package/dist/{access-service-DgmJd_A-.d.ts → access-service-CSq1Wp2Z.d.ts} +6 -6
  32. package/dist/access-service.d.ts +8 -8
  33. package/dist/access-service.js +7 -7
  34. package/dist/access-surface-catalog.d.ts +8 -8
  35. package/dist/action-confidence.d.ts +1 -1
  36. package/dist/active-memory-bridge.d.ts +1 -1
  37. package/dist/active-recall.d.ts +1 -1
  38. package/dist/active-recall.js +2 -1
  39. package/dist/active-recall.js.map +1 -1
  40. package/dist/behavior-learner.d.ts +1 -1
  41. package/dist/behavior-signals.d.ts +1 -1
  42. package/dist/bootstrap.d.ts +6 -6
  43. package/dist/briefing.d.ts +2 -2
  44. package/dist/briefing.js +3 -3
  45. package/dist/buffer-surprise-report.d.ts +1 -1
  46. package/dist/buffer.d.ts +2 -2
  47. package/dist/bulk-import/index.d.ts +3 -3
  48. package/dist/calibration.d.ts +1 -1
  49. package/dist/capabilities.d.ts +1 -1
  50. package/dist/{catalog-g_dfT6oL.d.ts → catalog-B9uga4sG.d.ts} +1 -1
  51. package/dist/causal-behavior.d.ts +1 -1
  52. package/dist/causal-consolidation.d.ts +1 -1
  53. package/dist/causal-consolidation.js +4 -4
  54. package/dist/causal-trajectory-graph.d.ts +1 -1
  55. package/dist/{chunk-IRX5UCGN.js → chunk-2OH3ZRYH.js} +3 -3
  56. package/dist/{chunk-YXTSUIXC.js → chunk-5EKOBRCA.js} +6 -1
  57. package/dist/chunk-5EKOBRCA.js.map +1 -0
  58. package/dist/{chunk-42DJZZUB.js → chunk-7LXSPIVG.js} +2 -2
  59. package/dist/{chunk-47WFEKT7.js → chunk-7PK4W4RN.js} +2 -2
  60. package/dist/{chunk-O7ACJAHR.js → chunk-7YJLCNPF.js} +2 -2
  61. package/dist/{chunk-6EJL2O3G.js → chunk-AFH7PIFU.js} +2 -2
  62. package/dist/{chunk-JN4N2TMQ.js → chunk-ANGUOE2N.js} +7 -7
  63. package/dist/{chunk-GQOKO77V.js → chunk-AWT7SCWQ.js} +2 -2
  64. package/dist/{chunk-PXJEL55X.js → chunk-BIRPR5N7.js} +2 -2
  65. package/dist/{chunk-NAY25BGS.js → chunk-BTK2PIFL.js} +6 -6
  66. package/dist/{chunk-SYCVU556.js → chunk-C2RMQRPN.js} +3 -3
  67. package/dist/{chunk-T4EAQPQQ.js → chunk-G2WCVFDC.js} +2 -2
  68. package/dist/{chunk-UJIBXOJA.js → chunk-GEDBTSYU.js} +5 -5
  69. package/dist/{chunk-CXFVZ3PK.js → chunk-GRJ5UOFO.js} +84 -658
  70. package/dist/chunk-GRJ5UOFO.js.map +1 -0
  71. package/dist/chunk-GSQASASV.js +429 -0
  72. package/dist/chunk-GSQASASV.js.map +1 -0
  73. package/dist/{chunk-WIKOHCQZ.js → chunk-I6ODVWQB.js} +2 -2
  74. package/dist/{chunk-H4MDUPER.js → chunk-IZHXSLO7.js} +2 -2
  75. package/dist/{chunk-RRS5CDB6.js → chunk-JOCXI2O5.js} +2 -2
  76. package/dist/{chunk-EEUZQQRJ.js → chunk-KGKYTCDO.js} +2 -2
  77. package/dist/{chunk-JTW4TGPV.js → chunk-L3HAUO7K.js} +2 -2
  78. package/dist/{chunk-P53HGVJS.js → chunk-LMVAIZR5.js} +2 -2
  79. package/dist/{chunk-KLXG4BBO.js → chunk-M6TZQDTX.js} +2 -2
  80. package/dist/{chunk-FJ5IQ3SI.js → chunk-N7DK67GZ.js} +1 -1
  81. package/dist/chunk-N7DK67GZ.js.map +1 -0
  82. package/dist/{chunk-U6QWUFSM.js → chunk-OXT5TQDS.js} +2 -2
  83. package/dist/{chunk-T4OTDMX4.js → chunk-QDTEH6BS.js} +2 -2
  84. package/dist/{chunk-5MDE5MIC.js → chunk-SESF64QR.js} +2 -2
  85. package/dist/{chunk-4E5H4FPC.js → chunk-TUNI4GB3.js} +3 -3
  86. package/dist/{chunk-XJT6LNLY.js → chunk-V6LYAXGW.js} +834 -25
  87. package/dist/chunk-V6LYAXGW.js.map +1 -0
  88. package/dist/{chunk-5HFIHWOF.js → chunk-VLIY55Q5.js} +17 -17
  89. package/dist/{chunk-BNH2VVWP.js → chunk-WIS5JTJR.js} +2 -2
  90. package/dist/{chunk-OOGGUXAG.js → chunk-WQ2I6ODB.js} +2 -2
  91. package/dist/{cli-BXCd2cgK.d.ts → cli-DYLGXxuu.d.ts} +5 -5
  92. package/dist/cli.d.ts +10 -10
  93. package/dist/cli.js +25 -24
  94. package/dist/compounding/engine.d.ts +2 -2
  95. package/dist/compounding/engine.js +3 -3
  96. package/dist/compounding/preference-consolidator.d.ts +1 -1
  97. package/dist/compression-optimizer.d.ts +1 -1
  98. package/dist/config.d.ts +1 -1
  99. package/dist/config.js +2 -1
  100. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  101. package/dist/connectors/codex-materialize-runner.js +3 -3
  102. package/dist/connectors/codex-materialize.d.ts +1 -1
  103. package/dist/connectors/index.d.ts +1 -1
  104. package/dist/connectors/index.js +3 -3
  105. package/dist/consolidation-provenance-check.d.ts +2 -2
  106. package/dist/consolidation-undo.d.ts +2 -2
  107. package/dist/contradiction/index.d.ts +2 -2
  108. package/dist/conversation-index/backend.d.ts +1 -1
  109. package/dist/conversation-index/chunker.d.ts +1 -1
  110. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  111. package/dist/conversation-index/indexer.d.ts +1 -1
  112. package/dist/conversation-index/search.d.ts +1 -1
  113. package/dist/day-summary.d.ts +1 -1
  114. package/dist/delinearize.d.ts +1 -1
  115. package/dist/direct-answer-wiring.d.ts +1 -1
  116. package/dist/direct-answer.d.ts +1 -1
  117. package/dist/embedding-fallback.d.ts +1 -1
  118. package/dist/enrichment/index.d.ts +1 -1
  119. package/dist/entity-retrieval.d.ts +2 -2
  120. package/dist/entity-retrieval.js +3 -3
  121. package/dist/entity-schema.d.ts +1 -1
  122. package/dist/explicit-capture.d.ts +6 -6
  123. package/dist/extraction-faithfulness.d.ts +1 -1
  124. package/dist/extraction-judge-telemetry.d.ts +1 -1
  125. package/dist/extraction-judge-training.d.ts +1 -1
  126. package/dist/extraction-judge.d.ts +1 -1
  127. package/dist/extraction.d.ts +1 -1
  128. package/dist/fallback-llm.d.ts +1 -1
  129. package/dist/graph-dashboard-diff.d.ts +1 -1
  130. package/dist/graph-dashboard-key.d.ts +1 -1
  131. package/dist/graph-dashboard-parser.d.ts +1 -1
  132. package/dist/graph-edge-reinforcement.d.ts +1 -1
  133. package/dist/graph-snapshot.d.ts +1 -1
  134. package/dist/graph.d.ts +1 -1
  135. package/dist/importance.d.ts +1 -1
  136. package/dist/importers/index.d.ts +1 -1
  137. package/dist/in-flight-reads.d.ts +1 -1
  138. package/dist/index.d.ts +693 -503
  139. package/dist/index.js +70 -45
  140. package/dist/intent.d.ts +1 -1
  141. package/dist/lcm/engine.d.ts +1 -1
  142. package/dist/lcm/index.d.ts +1 -1
  143. package/dist/lcm/tools.d.ts +1 -1
  144. package/dist/lifecycle.d.ts +1 -1
  145. package/dist/live-connectors-runner.d.ts +1 -1
  146. package/dist/local-llm.d.ts +1 -1
  147. package/dist/local-model-endpoint.d.ts +1 -1
  148. package/dist/maintenance/memory-governance.d.ts +1 -1
  149. package/dist/maintenance/memory-governance.js +3 -3
  150. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  151. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  152. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  153. package/dist/mcp-memory-inspector-app.d.ts +8 -8
  154. package/dist/memory-action-policy.d.ts +1 -1
  155. package/dist/memory-cache.d.ts +1 -1
  156. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  157. package/dist/memory-projection-store.d.ts +1 -1
  158. package/dist/memory-provenance.d.ts +1 -1
  159. package/dist/memory-worth-outcomes.d.ts +2 -2
  160. package/dist/models-json.d.ts +1 -1
  161. package/dist/namespaces/migrate.d.ts +3 -3
  162. package/dist/namespaces/migrate.js +4 -4
  163. package/dist/namespaces/principal.d.ts +1 -1
  164. package/dist/namespaces/search.d.ts +1 -1
  165. package/dist/namespaces/storage.d.ts +3 -3
  166. package/dist/namespaces/storage.js +3 -3
  167. package/dist/native-knowledge.d.ts +1 -1
  168. package/dist/offline-sync-impression-drain.d.ts +1 -1
  169. package/dist/operator-toolkit.d.ts +2 -2
  170. package/dist/operator-toolkit.js +9 -8
  171. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  172. package/dist/orchestration/maintenance.d.ts +10 -7
  173. package/dist/orchestration/maintenance.js +7 -6
  174. package/dist/{orchestrator-BHfWtvEJ.d.ts → orchestrator-BHx-DWCa.d.ts} +7 -5
  175. package/dist/orchestrator.d.ts +6 -6
  176. package/dist/orchestrator.js +31 -30
  177. package/dist/patterns-cli.d.ts +1 -1
  178. package/dist/{pipeline-CBYcnmDC.d.ts → pipeline-DDHSE876.d.ts} +1 -1
  179. package/dist/policy-runtime.d.ts +1 -1
  180. package/dist/proactive-contention.d.ts +1 -1
  181. package/dist/provenance.d.ts +1 -1
  182. package/dist/{qmd-BIq-jrV4.d.ts → qmd-C4WVh6BM.d.ts} +1 -1
  183. package/dist/qmd-preflight.d.ts +2 -2
  184. package/dist/qmd-recall-cache.d.ts +1 -1
  185. package/dist/qmd.d.ts +2 -2
  186. package/dist/recall-concurrency-config.d.ts +1 -1
  187. package/dist/recall-disclosure-escalation.d.ts +1 -1
  188. package/dist/recall-explain-renderer.d.ts +1 -1
  189. package/dist/recall-explain-renderer.js +3 -3
  190. package/dist/recall-memory-map.d.ts +1 -1
  191. package/dist/recall-planner-llm.d.ts +1 -1
  192. package/dist/recall-state.d.ts +1 -1
  193. package/dist/recall-tag-filter.d.ts +1 -1
  194. package/dist/recall-timings.d.ts +1 -1
  195. package/dist/recall-xray-cli.d.ts +1 -1
  196. package/dist/recall-xray-cli.js +4 -4
  197. package/dist/recall-xray-renderer.d.ts +1 -1
  198. package/dist/recall-xray-renderer.js +3 -3
  199. package/dist/recall-xray.d.ts +1 -1
  200. package/dist/recall-xray.js +2 -2
  201. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  202. package/dist/replay/normalizers/claude.d.ts +1 -1
  203. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  204. package/dist/replay/normalizers/shared.d.ts +1 -1
  205. package/dist/replay/runner.d.ts +1 -1
  206. package/dist/replay/types.d.ts +1 -1
  207. package/dist/resolve-auth-token.d.ts +1 -1
  208. package/dist/resume-bundles.js +3 -2
  209. package/dist/retrieval-agents.d.ts +2 -2
  210. package/dist/retrieval-tiers.d.ts +1 -1
  211. package/dist/routing/engine.d.ts +1 -1
  212. package/dist/routing/store.d.ts +1 -1
  213. package/dist/salvage-envelope.d.ts +1 -1
  214. package/dist/schemas.d.ts +76 -76
  215. package/dist/{scope-profiles-DDno-uvy.d.ts → scope-profiles-EqMXwzCz.d.ts} +1 -1
  216. package/dist/search/embed-helper.d.ts +1 -1
  217. package/dist/search/factory.d.ts +1 -1
  218. package/dist/search/index.d.ts +1 -1
  219. package/dist/search/lancedb-backend.d.ts +1 -1
  220. package/dist/search/meilisearch-backend.d.ts +1 -1
  221. package/dist/search/noop-backend.d.ts +1 -1
  222. package/dist/search/orama-backend.d.ts +1 -1
  223. package/dist/search/port.d.ts +1 -1
  224. package/dist/search/remote-backend.d.ts +1 -1
  225. package/dist/{semantic-consolidation-7RIJFDwY.d.ts → semantic-consolidation-CYpvwmQc.d.ts} +1 -1
  226. package/dist/semantic-consolidation.d.ts +2 -2
  227. package/dist/semantic-consolidation.js +4 -4
  228. package/dist/semantic-rule-promotion.js +3 -3
  229. package/dist/semantic-rule-verifier.d.ts +1 -1
  230. package/dist/semantic-rule-verifier.js +3 -3
  231. package/dist/session-observer-bands.d.ts +1 -1
  232. package/dist/session-observer-state.d.ts +1 -1
  233. package/dist/shared-context/manager.d.ts +9 -9
  234. package/dist/signal.d.ts +1 -1
  235. package/dist/{storage-DkphZiOJ.d.ts → storage-Dxd7zVZH.d.ts} +1 -1
  236. package/dist/storage.d.ts +2 -2
  237. package/dist/storage.js +2 -2
  238. package/dist/summarizer.d.ts +1 -1
  239. package/dist/summary-snapshot.d.ts +1 -1
  240. package/dist/temporal-supersession.d.ts +2 -2
  241. package/dist/temporal-timeline-recall.d.ts +1 -1
  242. package/dist/temporal-validity.d.ts +1 -1
  243. package/dist/threading.d.ts +1 -1
  244. package/dist/tier-migration.d.ts +2 -2
  245. package/dist/tier-routing.d.ts +1 -1
  246. package/dist/topics.d.ts +1 -1
  247. package/dist/transcript.d.ts +1 -1
  248. package/dist/transfer/types.d.ts +66 -66
  249. package/dist/trust-score-stage.d.ts +1 -1
  250. package/dist/trust-score.d.ts +1 -1
  251. package/dist/{types-2q_MRFcM.d.ts → types-OUCJALBK.d.ts} +98 -1
  252. package/dist/types.d.ts +1 -1
  253. package/dist/types.js +1 -1
  254. package/dist/utility-runtime.d.ts +1 -1
  255. package/dist/verified-recall.js +3 -3
  256. package/dist/write-envelope.d.ts +1 -1
  257. package/package.json +2 -2
  258. package/src/activity/config.test.ts +113 -0
  259. package/src/activity/config.ts +111 -0
  260. package/src/activity/digest.ts +2 -2
  261. package/src/activity/index.ts +6 -0
  262. package/src/activity/pipeline.test.ts +379 -0
  263. package/src/activity/pipeline.ts +223 -0
  264. package/src/activity/reindex.test.ts +97 -0
  265. package/src/activity/reindex.ts +40 -0
  266. package/src/activity/runner.test.ts +452 -0
  267. package/src/activity/runner.ts +237 -0
  268. package/src/activity/scheduler.test.ts +303 -0
  269. package/src/activity/scheduler.ts +147 -0
  270. package/src/activity/source-client.test.ts +208 -0
  271. package/src/activity/source-client.ts +159 -0
  272. package/src/activity/sync-registration.test.ts +134 -0
  273. package/src/activity/sync-registration.ts +139 -0
  274. package/src/activity/types.ts +17 -0
  275. package/src/config.test.ts +19 -0
  276. package/src/config.ts +3 -0
  277. package/src/orchestration/maintenance.test.ts +42 -42
  278. package/src/orchestration/maintenance.ts +37 -5
  279. package/src/orchestration/orchestrator-helpers.ts +36 -1
  280. package/src/orchestration/recall-internal.ts +6 -6
  281. package/src/orchestration/recall-search-pipeline.ts +15 -7
  282. package/src/orchestrator-background-writes.test.ts +2 -2
  283. package/src/orchestrator.ts +3 -3
  284. package/src/types.ts +4 -1
  285. package/dist/chunk-CXFVZ3PK.js.map +0 -1
  286. package/dist/chunk-FJ5IQ3SI.js.map +0 -1
  287. package/dist/chunk-XJT6LNLY.js.map +0 -1
  288. package/dist/chunk-YXTSUIXC.js.map +0 -1
  289. /package/dist/{chunk-IRX5UCGN.js.map → chunk-2OH3ZRYH.js.map} +0 -0
  290. /package/dist/{chunk-42DJZZUB.js.map → chunk-7LXSPIVG.js.map} +0 -0
  291. /package/dist/{chunk-47WFEKT7.js.map → chunk-7PK4W4RN.js.map} +0 -0
  292. /package/dist/{chunk-O7ACJAHR.js.map → chunk-7YJLCNPF.js.map} +0 -0
  293. /package/dist/{chunk-6EJL2O3G.js.map → chunk-AFH7PIFU.js.map} +0 -0
  294. /package/dist/{chunk-JN4N2TMQ.js.map → chunk-ANGUOE2N.js.map} +0 -0
  295. /package/dist/{chunk-GQOKO77V.js.map → chunk-AWT7SCWQ.js.map} +0 -0
  296. /package/dist/{chunk-PXJEL55X.js.map → chunk-BIRPR5N7.js.map} +0 -0
  297. /package/dist/{chunk-NAY25BGS.js.map → chunk-BTK2PIFL.js.map} +0 -0
  298. /package/dist/{chunk-SYCVU556.js.map → chunk-C2RMQRPN.js.map} +0 -0
  299. /package/dist/{chunk-T4EAQPQQ.js.map → chunk-G2WCVFDC.js.map} +0 -0
  300. /package/dist/{chunk-UJIBXOJA.js.map → chunk-GEDBTSYU.js.map} +0 -0
  301. /package/dist/{chunk-WIKOHCQZ.js.map → chunk-I6ODVWQB.js.map} +0 -0
  302. /package/dist/{chunk-H4MDUPER.js.map → chunk-IZHXSLO7.js.map} +0 -0
  303. /package/dist/{chunk-RRS5CDB6.js.map → chunk-JOCXI2O5.js.map} +0 -0
  304. /package/dist/{chunk-EEUZQQRJ.js.map → chunk-KGKYTCDO.js.map} +0 -0
  305. /package/dist/{chunk-JTW4TGPV.js.map → chunk-L3HAUO7K.js.map} +0 -0
  306. /package/dist/{chunk-P53HGVJS.js.map → chunk-LMVAIZR5.js.map} +0 -0
  307. /package/dist/{chunk-KLXG4BBO.js.map → chunk-M6TZQDTX.js.map} +0 -0
  308. /package/dist/{chunk-U6QWUFSM.js.map → chunk-OXT5TQDS.js.map} +0 -0
  309. /package/dist/{chunk-T4OTDMX4.js.map → chunk-QDTEH6BS.js.map} +0 -0
  310. /package/dist/{chunk-5MDE5MIC.js.map → chunk-SESF64QR.js.map} +0 -0
  311. /package/dist/{chunk-4E5H4FPC.js.map → chunk-TUNI4GB3.js.map} +0 -0
  312. /package/dist/{chunk-5HFIHWOF.js.map → chunk-VLIY55Q5.js.map} +0 -0
  313. /package/dist/{chunk-BNH2VVWP.js.map → chunk-WIS5JTJR.js.map} +0 -0
  314. /package/dist/{chunk-OOGGUXAG.js.map → chunk-WQ2I6ODB.js.map} +0 -0
@@ -9,11 +9,20 @@ import {
9
9
  } from "./chunk-666A3MOW.js";
10
10
  import {
11
11
  rebuildMemoryLifecycleLedger
12
- } from "./chunk-P53HGVJS.js";
12
+ } from "./chunk-LMVAIZR5.js";
13
13
  import {
14
14
  planNamespaceMaintenance,
15
15
  runNamespaceMaintenanceBatchPlan
16
- } from "./chunk-5MDE5MIC.js";
16
+ } from "./chunk-SESF64QR.js";
17
+ import {
18
+ activityDayWindow,
19
+ activityDigestPath,
20
+ composeActivityDigestBody,
21
+ composeActivityDigestMeta,
22
+ isValidActivityDate,
23
+ serializeActivityDigest,
24
+ validateActivityBaseUrl
25
+ } from "./chunk-GSQASASV.js";
17
26
  import {
18
27
  STATE_FILE_MAX_DECRYPT_BYTES,
19
28
  drainPendingLifecycleLedgerIfAny,
@@ -26,6 +35,10 @@ import {
26
35
  assertPathInsideRoot,
27
36
  listContainedSpillFiles
28
37
  } from "./chunk-GCASGYIO.js";
38
+ import {
39
+ displayErrorDetail,
40
+ openBetterSqlite3
41
+ } from "./chunk-RL6NEVXT.js";
29
42
  import {
30
43
  resolveHomeDir
31
44
  } from "./chunk-JUC24CTX.js";
@@ -51,8 +64,770 @@ import {
51
64
 
52
65
  // src/orchestration/maintenance.ts
53
66
  import { existsSync } from "fs";
54
- import { appendFile, lstat, mkdir, readFile, readdir, realpath, stat, writeFile } from "fs/promises";
67
+ import { appendFile, lstat, mkdir as mkdir3, readFile as readFile2, readdir, realpath, stat, writeFile as writeFile2 } from "fs/promises";
68
+ import path3 from "path";
69
+
70
+ // src/activity/reindex.ts
71
+ async function refreshActivityIndex(qmd, collection, signal) {
72
+ const execution = signal === void 0 ? void 0 : { signal };
73
+ if (typeof qmd.updateCollectionStrict === "function" && collection.trim().length > 0) {
74
+ await qmd.updateCollectionStrict(collection, execution);
75
+ return;
76
+ }
77
+ if (typeof qmd.updateStrict === "function") {
78
+ await qmd.updateStrict(execution);
79
+ return;
80
+ }
81
+ await qmd.update(execution);
82
+ }
83
+
84
+ // src/activity/store.ts
85
+ import { mkdirSync } from "fs";
86
+ import { mkdir } from "fs/promises";
55
87
  import path from "path";
88
+ var ACTIVITY_SCHEMA_VERSION = 1;
89
+ var MAX_SEARCH_RESULTS = 100;
90
+ function activityDatabasePath(memoryDir) {
91
+ return path.join(memoryDir, "state", "activity.sqlite");
92
+ }
93
+ async function ensureActivityStateDir(memoryDir) {
94
+ await mkdir(path.join(memoryDir, "state"), { recursive: true });
95
+ }
96
+ function openActivityDatabase(memoryDir) {
97
+ mkdirSync(path.join(memoryDir, "state"), { recursive: true });
98
+ const db = openBetterSqlite3(activityDatabasePath(memoryDir));
99
+ db.pragma("journal_mode = WAL");
100
+ db.pragma("busy_timeout = 5000");
101
+ db.pragma("synchronous = NORMAL");
102
+ applySchema(db);
103
+ return db;
104
+ }
105
+ function applyActivitySchema(db) {
106
+ applySchema(db);
107
+ }
108
+ function applySchema(db) {
109
+ db.exec(`
110
+ CREATE TABLE IF NOT EXISTS activity_meta (
111
+ key TEXT PRIMARY KEY,
112
+ value TEXT NOT NULL
113
+ );
114
+
115
+ CREATE TABLE IF NOT EXISTS activity_snapshots (
116
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
117
+ machine TEXT NOT NULL,
118
+ captured_at_utc TEXT NOT NULL,
119
+ app_name TEXT NOT NULL,
120
+ window_title TEXT NOT NULL,
121
+ browser_url TEXT,
122
+ text TEXT NOT NULL,
123
+ text_source TEXT NOT NULL,
124
+ content_hash TEXT NOT NULL,
125
+ simhash TEXT
126
+ );
127
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_activity_snapshots_dedup
128
+ ON activity_snapshots(machine, captured_at_utc, content_hash);
129
+ CREATE INDEX IF NOT EXISTS idx_activity_snapshots_time
130
+ ON activity_snapshots(captured_at_utc, id);
131
+
132
+ CREATE TABLE IF NOT EXISTS activity_sync_state (
133
+ machine TEXT PRIMARY KEY,
134
+ cursor TEXT,
135
+ updated_at_utc TEXT NOT NULL
136
+ );
137
+ `);
138
+ const hasFts = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='activity_snapshots_fts'").get();
139
+ if (!hasFts) {
140
+ db.exec(`
141
+ CREATE VIRTUAL TABLE activity_snapshots_fts USING fts5(
142
+ text, app_name, window_title, browser_url
143
+ );
144
+ `);
145
+ }
146
+ db.prepare("INSERT OR REPLACE INTO activity_meta (key, value) VALUES ('schema_version', ?)").run(
147
+ String(ACTIVITY_SCHEMA_VERSION)
148
+ );
149
+ }
150
+ function canonicalizeUtc(iso) {
151
+ const parsed = Date.parse(iso);
152
+ return Number.isFinite(parsed) ? new Date(parsed).toISOString() : iso;
153
+ }
154
+ function assertRequiredSnapshotFields(s) {
155
+ const required = [
156
+ ["machine", s.machine],
157
+ ["app", s.app],
158
+ ["windowTitle", s.windowTitle],
159
+ ["text", s.text],
160
+ ["textSource", s.textSource],
161
+ ["contentHash", s.contentHash]
162
+ ];
163
+ for (const [field, value] of required) {
164
+ if (typeof value !== "string") {
165
+ throw new RangeError(`activity: snapshot field "${field}" is required (got ${value === null ? "null" : typeof value}).`);
166
+ }
167
+ }
168
+ if (s.machine.length === 0 || s.contentHash.length === 0) {
169
+ throw new RangeError('activity: snapshot "machine" and "contentHash" must be non-empty.');
170
+ }
171
+ if (s.textSource !== "ax" && s.textSource !== "ocr") {
172
+ throw new RangeError(`activity: snapshot "textSource" must be "ax" or "ocr" (got "${s.textSource}").`);
173
+ }
174
+ }
175
+ function assertValidUtcInstant(iso, what) {
176
+ const m = iso.match(
177
+ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d+)?(?:Z|[+-](?:0\d:[0-5]\d|1[0-3]:[0-5]\d|14:00))$/
178
+ );
179
+ if (m === null || !Number.isFinite(Date.parse(iso))) {
180
+ throw new RangeError(`activity: invalid ${what} "${iso}".`);
181
+ }
182
+ const [year, month, day, hour, minute, second] = m.slice(1).map(Number);
183
+ const daysInMonth = month >= 1 && month <= 12 ? new Date(Date.UTC(year, month, 0)).getUTCDate() : 0;
184
+ if (day < 1 || day > daysInMonth || hour > 23 || minute > 59 || second > 59) {
185
+ throw new RangeError(`activity: ${what} "${iso}" is not a real calendar instant.`);
186
+ }
187
+ }
188
+ function isRecord(value) {
189
+ return typeof value === "object" && value !== null;
190
+ }
191
+ function str(value) {
192
+ return typeof value === "string" ? value : "";
193
+ }
194
+ function optStr(value) {
195
+ return typeof value === "string" && value.length > 0 ? value : void 0;
196
+ }
197
+ function ftsMatchFor(query) {
198
+ if (typeof query !== "string") return null;
199
+ const tokens = query.match(/[\p{L}\p{N}_]+/gu);
200
+ if (!tokens || tokens.length === 0) return null;
201
+ return tokens.map((token) => `"${token}"`).join(" ");
202
+ }
203
+ function rowToSnapshot(row) {
204
+ if (!isRecord(row)) {
205
+ throw new Error("activity store: unexpected non-object row");
206
+ }
207
+ const textSource = row.text_source === "ocr" ? "ocr" : "ax";
208
+ return {
209
+ id: typeof row.id === "number" ? row.id : void 0,
210
+ machine: str(row.machine),
211
+ capturedAtUtc: str(row.captured_at_utc),
212
+ app: str(row.app_name),
213
+ windowTitle: str(row.window_title),
214
+ ...optStr(row.browser_url) !== void 0 ? { browserUrl: optStr(row.browser_url) } : {},
215
+ text: str(row.text),
216
+ textSource,
217
+ contentHash: str(row.content_hash),
218
+ ...optStr(row.simhash) !== void 0 ? { simhash: optStr(row.simhash) } : {}
219
+ };
220
+ }
221
+ var ActivityStore = class _ActivityStore {
222
+ db;
223
+ constructor(db) {
224
+ this.db = db;
225
+ }
226
+ static open(memoryDir) {
227
+ return new _ActivityStore(openActivityDatabase(memoryDir));
228
+ }
229
+ /**
230
+ * Insert a snapshot, idempotent on (machine, captured_at_utc, content_hash):
231
+ * the same screen content recurring at a *different* time is kept; only an
232
+ * exact re-ingestion of the same capture dedups. Returns `inserted: false`
233
+ * for a duplicate. The FTS row is written only on a real insert (atomically),
234
+ * so it never drifts from the base table.
235
+ */
236
+ insertSnapshot(snapshot) {
237
+ const runInsert = this.db.transaction((s) => {
238
+ assertValidUtcInstant(s.capturedAtUtc, "capture timestamp");
239
+ assertRequiredSnapshotFields(s);
240
+ const capturedAtUtc = canonicalizeUtc(s.capturedAtUtc);
241
+ const info = this.db.prepare(
242
+ `INSERT OR IGNORE INTO activity_snapshots
243
+ (machine, captured_at_utc, app_name, window_title, browser_url, text, text_source, content_hash, simhash)
244
+ VALUES (@machine, @captured_at_utc, @app_name, @window_title, @browser_url, @text, @text_source, @content_hash, @simhash)`
245
+ ).run({
246
+ machine: s.machine,
247
+ captured_at_utc: capturedAtUtc,
248
+ app_name: s.app,
249
+ window_title: s.windowTitle,
250
+ browser_url: s.browserUrl ?? null,
251
+ text: s.text,
252
+ text_source: s.textSource,
253
+ content_hash: s.contentHash,
254
+ simhash: s.simhash ?? null
255
+ });
256
+ if (info.changes === 0) {
257
+ const existing = this.db.prepare("SELECT id FROM activity_snapshots WHERE machine = ? AND captured_at_utc = ? AND content_hash = ?").get(s.machine, capturedAtUtc, s.contentHash);
258
+ const id2 = isRecord(existing) && typeof existing.id === "number" ? existing.id : -1;
259
+ return { inserted: false, id: id2 };
260
+ }
261
+ const id = Number(info.lastInsertRowid);
262
+ this.db.prepare(
263
+ `INSERT INTO activity_snapshots_fts (rowid, text, app_name, window_title, browser_url)
264
+ VALUES (?, ?, ?, ?, ?)`
265
+ ).run(id, s.text, s.app, s.windowTitle, s.browserUrl ?? "");
266
+ return { inserted: true, id };
267
+ });
268
+ return runInsert(snapshot);
269
+ }
270
+ /** Snapshots whose capture instant is in the half-open [start, end) window. */
271
+ listSnapshotsForDay(machine, startUtcInclusive, endUtcExclusive) {
272
+ assertValidUtcInstant(startUtcInclusive, "range start");
273
+ assertValidUtcInstant(endUtcExclusive, "range end");
274
+ const start = canonicalizeUtc(startUtcInclusive);
275
+ const end = canonicalizeUtc(endUtcExclusive);
276
+ const rows = machine === null ? this.db.prepare(
277
+ `SELECT * FROM activity_snapshots
278
+ WHERE captured_at_utc >= ? AND captured_at_utc < ?
279
+ ORDER BY captured_at_utc ASC, id ASC`
280
+ ).all(start, end) : this.db.prepare(
281
+ `SELECT * FROM activity_snapshots
282
+ WHERE machine = ? AND captured_at_utc >= ? AND captured_at_utc < ?
283
+ ORDER BY captured_at_utc ASC, id ASC`
284
+ ).all(machine, start, end);
285
+ return rows.map(rowToSnapshot);
286
+ }
287
+ getCursor(machine) {
288
+ const row = this.db.prepare("SELECT cursor FROM activity_sync_state WHERE machine = ?").get(machine);
289
+ return isRecord(row) && typeof row.cursor === "string" ? row.cursor : null;
290
+ }
291
+ setCursor(machine, cursor, updatedAtUtc = (/* @__PURE__ */ new Date()).toISOString()) {
292
+ this.db.prepare(
293
+ `INSERT INTO activity_sync_state (machine, cursor, updated_at_utc)
294
+ VALUES (?, ?, ?)
295
+ ON CONFLICT(machine) DO UPDATE SET cursor = excluded.cursor, updated_at_utc = excluded.updated_at_utc`
296
+ ).run(machine, cursor, updatedAtUtc);
297
+ }
298
+ /** Full-text search over snapshot text/app/window/url; newest first. */
299
+ searchSnapshots(query, limit) {
300
+ const capped = Number.isInteger(limit) && limit > 0 ? Math.min(limit, MAX_SEARCH_RESULTS) : 20;
301
+ const match = ftsMatchFor(query);
302
+ if (match === null) return [];
303
+ const rows = this.db.prepare(
304
+ `SELECT s.* FROM activity_snapshots_fts f
305
+ JOIN activity_snapshots s ON s.id = f.rowid
306
+ WHERE activity_snapshots_fts MATCH ?
307
+ ORDER BY s.captured_at_utc DESC, s.id DESC
308
+ LIMIT ?`
309
+ ).all(match, capped);
310
+ return rows.map(rowToSnapshot);
311
+ }
312
+ /** Retention: drop snapshots captured strictly before `cutoffUtc`. */
313
+ pruneOlderThan(cutoffUtc) {
314
+ assertValidUtcInstant(cutoffUtc, "prune cutoff");
315
+ const ids = this.db.prepare("SELECT id FROM activity_snapshots WHERE captured_at_utc < ?").all(canonicalizeUtc(cutoffUtc)).map((row) => isRecord(row) && typeof row.id === "number" ? row.id : -1).filter((id) => id >= 0);
316
+ const deleteFts = this.db.prepare("DELETE FROM activity_snapshots_fts WHERE rowid = ?");
317
+ const deleteRow = this.db.prepare("DELETE FROM activity_snapshots WHERE id = ?");
318
+ const tx = this.db.transaction((rowIds) => {
319
+ for (const id of rowIds) {
320
+ deleteFts.run(id);
321
+ deleteRow.run(id);
322
+ }
323
+ });
324
+ tx(ids);
325
+ return ids.length;
326
+ }
327
+ close() {
328
+ this.db.close();
329
+ }
330
+ };
331
+
332
+ // src/activity/pipeline.ts
333
+ import { mkdir as mkdir2, readFile, rename, writeFile } from "fs/promises";
334
+ import { randomUUID } from "crypto";
335
+ import path2 from "path";
336
+ var MAX_SYNC_PAGES = 1e4;
337
+ function activityCursorKey(machine, date) {
338
+ return `${machine}\0${date}`;
339
+ }
340
+ var digestLocks = /* @__PURE__ */ new Map();
341
+ function withDigestLock(key, fn) {
342
+ const prior = digestLocks.get(key) ?? Promise.resolve();
343
+ const next = prior.then(fn, fn);
344
+ const tail = next.then(
345
+ () => void 0,
346
+ () => void 0
347
+ );
348
+ digestLocks.set(key, tail);
349
+ void tail.then(() => {
350
+ if (digestLocks.get(key) === tail) digestLocks.delete(key);
351
+ });
352
+ return next;
353
+ }
354
+ function snapshotForMachine(snapshot, machine) {
355
+ return {
356
+ machine,
357
+ capturedAtUtc: snapshot.capturedAtUtc,
358
+ app: snapshot.app,
359
+ windowTitle: snapshot.windowTitle,
360
+ ...snapshot.browserUrl === void 0 ? {} : { browserUrl: snapshot.browserUrl },
361
+ text: snapshot.text,
362
+ textSource: snapshot.textSource,
363
+ contentHash: snapshot.contentHash,
364
+ ...snapshot.simhash === void 0 ? {} : { simhash: snapshot.simhash }
365
+ };
366
+ }
367
+ async function writeDigestIfChanged(memoryDir, date, serialized) {
368
+ const target = activityDigestPath(memoryDir, date);
369
+ try {
370
+ if (await readFile(target, "utf8") === serialized) return false;
371
+ } catch (error) {
372
+ if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") throw error;
373
+ }
374
+ await mkdir2(path2.dirname(target), { recursive: true, mode: 448 });
375
+ const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;
376
+ await writeFile(temporary, serialized, { encoding: "utf8", mode: 384 });
377
+ await rename(temporary, target);
378
+ return true;
379
+ }
380
+ async function syncActivitySource(source, options) {
381
+ if (!isValidActivityDate(options.date)) {
382
+ throw new RangeError(`Invalid activity date "${options.date}"; expected a real YYYY-MM-DD day.`);
383
+ }
384
+ if (source.machineLabel.trim().length === 0) {
385
+ throw new RangeError("activity source machine label must not be empty");
386
+ }
387
+ const maxPages = options.maxPages ?? MAX_SYNC_PAGES;
388
+ if (!Number.isInteger(maxPages) || maxPages < 1) {
389
+ throw new RangeError("activity sync maxPages must be a positive integer");
390
+ }
391
+ const cursorKey = activityCursorKey(source.machineLabel, options.date);
392
+ let cursor = options.store.getCursor(cursorKey);
393
+ let pages = 0;
394
+ let fetched = 0;
395
+ let inserted = 0;
396
+ let duplicates = 0;
397
+ const seenCursors = /* @__PURE__ */ new Set();
398
+ let completed = false;
399
+ while (pages < maxPages) {
400
+ options.signal?.throwIfAborted();
401
+ const page = await source.fetchSnapshots({
402
+ date: options.date,
403
+ timezone: options.timezone,
404
+ cursor,
405
+ signal: options.signal
406
+ });
407
+ pages += 1;
408
+ fetched += page.snapshots.length;
409
+ for (const snapshot of page.snapshots) {
410
+ const result = options.store.insertSnapshot(snapshotForMachine(snapshot, source.machineLabel));
411
+ if (result.inserted) inserted += 1;
412
+ else duplicates += 1;
413
+ }
414
+ if (page.nextCursor === null) {
415
+ completed = true;
416
+ break;
417
+ }
418
+ if (typeof page.nextCursor !== "string" || page.nextCursor.length === 0) {
419
+ throw new TypeError("activity source returned an invalid cursor");
420
+ }
421
+ if (seenCursors.has(page.nextCursor)) {
422
+ throw new Error("activity source returned a repeated cursor");
423
+ }
424
+ seenCursors.add(page.nextCursor);
425
+ cursor = page.nextCursor;
426
+ }
427
+ if (!completed) {
428
+ throw new Error(`activity source exceeded ${maxPages} pages`);
429
+ }
430
+ options.signal?.throwIfAborted();
431
+ const digestWritten = await withDigestLock(`${options.memoryDir}\0${options.date}`, async () => {
432
+ options.signal?.throwIfAborted();
433
+ const { startUtc, endUtc } = activityDayWindow(options.date, options.timezone);
434
+ const snapshots = options.store.listSnapshotsForDay(null, startUtc, endUtc);
435
+ const body = composeActivityDigestBody(options.date, options.timezone, snapshots);
436
+ const machines = [...new Set(snapshots.map((snapshot) => snapshot.machine))].sort(
437
+ (a, b) => a < b ? -1 : a > b ? 1 : 0
438
+ );
439
+ const serialized = serializeActivityDigest(composeActivityDigestMeta(options.date, machines, snapshots, body), body);
440
+ return writeDigestIfChanged(options.memoryDir, options.date, serialized);
441
+ });
442
+ options.signal?.throwIfAborted();
443
+ options.store.setCursor(cursorKey, cursor);
444
+ let reindexError;
445
+ if (digestWritten && options.afterWrites !== void 0) {
446
+ try {
447
+ await options.afterWrites(options.signal);
448
+ } catch (error) {
449
+ reindexError = error instanceof Error && error.message.startsWith("QMD ") ? error.message : displayErrorDetail(error) || "reindex failed";
450
+ }
451
+ }
452
+ return {
453
+ machine: source.machineLabel,
454
+ fetched,
455
+ inserted,
456
+ duplicates,
457
+ cursor,
458
+ digestWritten,
459
+ ...reindexError === void 0 ? {} : { reindexError }
460
+ };
461
+ }
462
+
463
+ // src/activity/source-client.ts
464
+ var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
465
+ function isRecord2(value) {
466
+ return typeof value === "object" && value !== null;
467
+ }
468
+ function stringField(value, field) {
469
+ const result = value[field];
470
+ if (typeof result !== "string" || result.length === 0) {
471
+ throw new TypeError(`activity source response has no valid ${field}`);
472
+ }
473
+ return result;
474
+ }
475
+ function contentStringField(value, field) {
476
+ const result = value[field];
477
+ if (typeof result !== "string") {
478
+ throw new TypeError(`activity source response has invalid ${field}`);
479
+ }
480
+ return result;
481
+ }
482
+ function optionalStringField(value, field) {
483
+ const result = value[field];
484
+ if (result === void 0 || result === null) return void 0;
485
+ if (typeof result !== "string") throw new TypeError(`activity source response has invalid ${field}`);
486
+ return result;
487
+ }
488
+ function snapshotFromWire(value, machine) {
489
+ if (!isRecord2(value)) throw new TypeError("activity source returned a non-object snapshot");
490
+ const textSource = stringField(value, "textSource");
491
+ if (textSource !== "ax" && textSource !== "ocr") {
492
+ throw new TypeError("activity source response has invalid textSource");
493
+ }
494
+ const browserUrl = optionalStringField(value, "browserUrl");
495
+ const simhash = optionalStringField(value, "simhash");
496
+ return {
497
+ machine,
498
+ capturedAtUtc: stringField(value, "capturedAtUtc"),
499
+ app: contentStringField(value, "app"),
500
+ windowTitle: contentStringField(value, "windowTitle"),
501
+ ...browserUrl === void 0 ? {} : { browserUrl },
502
+ text: contentStringField(value, "text"),
503
+ textSource,
504
+ contentHash: stringField(value, "contentHash"),
505
+ ...simhash === void 0 ? {} : { simhash }
506
+ };
507
+ }
508
+ function responsePage(value, machine) {
509
+ if (!isRecord2(value) || !Array.isArray(value.snapshots)) {
510
+ throw new TypeError("activity source response has no snapshots array");
511
+ }
512
+ const nextCursor = value.nextCursor === void 0 ? null : value.nextCursor;
513
+ if (nextCursor !== null && typeof nextCursor !== "string") {
514
+ throw new TypeError("activity source response has invalid nextCursor");
515
+ }
516
+ return {
517
+ snapshots: value.snapshots.map((snapshot) => snapshotFromWire(snapshot, machine)),
518
+ nextCursor
519
+ };
520
+ }
521
+ function requestUrl(baseUrl, path4, query) {
522
+ const url = new URL(path4, baseUrl);
523
+ const parts = Object.entries(query).filter((entry) => entry[1] !== void 0).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
524
+ url.search = parts.join("&");
525
+ return url.toString();
526
+ }
527
+ var ActivityHttpSourceClient = class {
528
+ machineLabel;
529
+ baseUrl;
530
+ token;
531
+ timeoutMs;
532
+ constructor(options) {
533
+ if (options.machineLabel.trim().length === 0) throw new RangeError("activity source machine label must not be empty");
534
+ if (options.timeoutMs !== void 0 && (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0)) {
535
+ throw new RangeError("activity source timeoutMs must be a positive number");
536
+ }
537
+ this.machineLabel = options.machineLabel;
538
+ this.baseUrl = validateActivityBaseUrl(options.baseUrl).toString();
539
+ this.token = options.token;
540
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
541
+ }
542
+ async request(url, signal) {
543
+ const timeout = AbortSignal.timeout(this.timeoutMs);
544
+ const composed = signal === void 0 ? timeout : AbortSignal.any([signal, timeout]);
545
+ const response = await fetch(url, {
546
+ headers: this.token === void 0 ? void 0 : { authorization: `Bearer ${this.token}` },
547
+ signal: composed
548
+ });
549
+ if (!response.ok) throw new Error(`activity source HTTP ${response.status}`);
550
+ return response;
551
+ }
552
+ async verify(signal) {
553
+ try {
554
+ await this.request(requestUrl(this.baseUrl, "/v1/health", {}), signal);
555
+ return { ok: true };
556
+ } catch (error) {
557
+ const detail = error instanceof Error && error.message.startsWith("activity source HTTP ") ? error.message.slice("activity source ".length) : displayErrorDetail(error) || "request failed";
558
+ return { ok: false, detail };
559
+ }
560
+ }
561
+ async fetchSnapshots(opts) {
562
+ const response = await this.request(
563
+ requestUrl(this.baseUrl, "/v1/snapshots", {
564
+ date: opts.date,
565
+ timezone: opts.timezone,
566
+ cursor: opts.cursor ?? void 0
567
+ }),
568
+ opts.signal
569
+ );
570
+ return responsePage(await response.json(), this.machineLabel);
571
+ }
572
+ };
573
+
574
+ // src/activity/runner.ts
575
+ function localDateInTimezone(instant, timezone) {
576
+ try {
577
+ const parts = new Intl.DateTimeFormat("en-CA", {
578
+ timeZone: timezone,
579
+ year: "numeric",
580
+ month: "2-digit",
581
+ day: "2-digit"
582
+ }).formatToParts(instant);
583
+ const get = (type) => parts.find((part) => part.type === type)?.value ?? "";
584
+ return `${get("year")}-${get("month")}-${get("day")}`;
585
+ } catch {
586
+ return instant.toISOString().slice(0, 10);
587
+ }
588
+ }
589
+ function previousIsoDate(date) {
590
+ const parsed = /* @__PURE__ */ new Date(`${date}T00:00:00Z`);
591
+ parsed.setUTCDate(parsed.getUTCDate() - 1);
592
+ return parsed.toISOString().slice(0, 10);
593
+ }
594
+ function resolveActivitySyncDates(syncDays, timezone, now) {
595
+ const dates = [];
596
+ let cursor = localDateInTimezone(now, timezone);
597
+ for (let count = 0; count < syncDays; count++) {
598
+ dates.unshift(cursor);
599
+ cursor = previousIsoDate(cursor);
600
+ }
601
+ return dates;
602
+ }
603
+ function sanitizedSyncError(error) {
604
+ return error instanceof Error && /^(activity|Invalid activity)/.test(error.message) ? error.message : displayErrorDetail(error) || "activity sync failed";
605
+ }
606
+ async function runActivitySyncOnce(options) {
607
+ const ranAt = typeof options.now === "function" ? options.now() : options.now ?? /* @__PURE__ */ new Date();
608
+ if (!options.config.enabled) {
609
+ return {
610
+ ranAt: ranAt.toISOString(),
611
+ enabled: false,
612
+ ranCount: 0,
613
+ errorCount: 0,
614
+ reindexErrorCount: 0,
615
+ totalInserted: 0,
616
+ results: []
617
+ };
618
+ }
619
+ const dates = resolveActivitySyncDates(options.config.syncDays, options.config.timezone, ranAt);
620
+ const createSourceClient = options.createSourceClient ?? ((source) => new ActivityHttpSourceClient({
621
+ machineLabel: source.machineLabel,
622
+ baseUrl: source.baseUrl,
623
+ ...source.token === void 0 ? {} : { token: source.token }
624
+ }));
625
+ const ownStore = options.store === void 0;
626
+ const store = options.store ?? ActivityStore.open(options.memoryDir);
627
+ const results = [];
628
+ try {
629
+ for (const sourceConfig of options.config.sources) {
630
+ const item = {
631
+ machineLabel: sourceConfig.machineLabel,
632
+ ran: false,
633
+ fetched: 0,
634
+ inserted: 0,
635
+ duplicates: 0,
636
+ digestsWritten: 0,
637
+ // Cursors are now per (machine, date); report the last synced day's
638
+ // cursor. Null until a day advances it.
639
+ cursor: null
640
+ };
641
+ try {
642
+ const client = createSourceClient(sourceConfig);
643
+ for (const date of dates) {
644
+ try {
645
+ const result = await syncActivitySource(client, {
646
+ date,
647
+ timezone: options.config.timezone,
648
+ memoryDir: options.memoryDir,
649
+ store,
650
+ signal: options.signal,
651
+ ...options.reindexSearch === void 0 ? {} : { afterWrites: options.reindexSearch }
652
+ });
653
+ item.fetched += result.fetched;
654
+ item.inserted += result.inserted;
655
+ item.duplicates += result.duplicates;
656
+ if (result.digestWritten) item.digestsWritten += 1;
657
+ item.cursor = result.cursor;
658
+ item.ran = true;
659
+ if (result.reindexError !== void 0) item.reindexError = result.reindexError;
660
+ } catch (dateError) {
661
+ item.error = sanitizedSyncError(dateError);
662
+ if (options.signal?.aborted) break;
663
+ }
664
+ }
665
+ } catch (error) {
666
+ item.error = sanitizedSyncError(error);
667
+ }
668
+ results.push(item);
669
+ if (options.signal?.aborted) break;
670
+ }
671
+ } finally {
672
+ if (ownStore) store.close();
673
+ }
674
+ return {
675
+ ranAt: ranAt.toISOString(),
676
+ enabled: true,
677
+ ranCount: results.filter((item) => item.ran).length,
678
+ errorCount: results.filter((item) => item.error !== void 0).length,
679
+ reindexErrorCount: results.filter((item) => item.reindexError !== void 0).length,
680
+ totalInserted: results.reduce((sum, item) => sum + item.inserted, 0),
681
+ results
682
+ };
683
+ }
684
+
685
+ // src/activity/scheduler.ts
686
+ var ACTIVITY_SYNC_DEFAULT_INTERVAL_MS = 9e5;
687
+ var ActivitySyncScheduler = class {
688
+ config;
689
+ memoryDir;
690
+ intervalMs;
691
+ invoke;
692
+ setTimer;
693
+ clearTimer;
694
+ onRun;
695
+ onError;
696
+ handle = null;
697
+ stopped = false;
698
+ inFlight = false;
699
+ inFlightPromise = null;
700
+ abortController = new AbortController();
701
+ constructor(options) {
702
+ this.config = options.config;
703
+ this.memoryDir = options.memoryDir;
704
+ this.intervalMs = options.intervalMs ?? ACTIVITY_SYNC_DEFAULT_INTERVAL_MS;
705
+ this.invoke = options.invoke ?? ((signal) => runActivitySyncOnce({
706
+ config: this.config,
707
+ memoryDir: this.memoryDir,
708
+ signal,
709
+ ...options.reindexSearch === void 0 ? {} : { reindexSearch: options.reindexSearch }
710
+ }));
711
+ this.setTimer = options.setTimer ?? ((fn, ms) => {
712
+ const timer = setInterval(fn, ms);
713
+ timer.unref?.();
714
+ return timer;
715
+ });
716
+ this.clearTimer = options.clearTimer ?? ((handle) => clearInterval(handle));
717
+ this.onRun = options.onRun;
718
+ this.onError = options.onError;
719
+ }
720
+ /**
721
+ * Arm the periodic sync. Disabled config (default-off) registers nothing.
722
+ * Idempotent: a second call while running (or after stop) re-arms nothing.
723
+ */
724
+ start() {
725
+ if (this.handle !== null || this.stopped || !this.config.enabled) {
726
+ return { registered: this.handle !== null, intervalMs: this.intervalMs };
727
+ }
728
+ this.handle = this.setTimer(() => this.tick(), this.intervalMs);
729
+ return { registered: true, intervalMs: this.intervalMs };
730
+ }
731
+ /**
732
+ * Cancel the periodic sync and abort any in-flight tick, then resolve once it
733
+ * has unwound. The abort + timer-clear + stop-latch happen synchronously, so
734
+ * even a caller that cannot await (a sync teardown) still signals the running
735
+ * sync to stop; async callers can await the returned drain. Idempotent.
736
+ */
737
+ async stop() {
738
+ this.stopped = true;
739
+ this.abortController.abort();
740
+ if (this.handle !== null) {
741
+ this.clearTimer(this.handle);
742
+ this.handle = null;
743
+ }
744
+ await this.inFlightPromise?.catch(() => void 0);
745
+ }
746
+ tick() {
747
+ if (this.stopped || this.inFlight) return;
748
+ this.inFlight = true;
749
+ this.inFlightPromise = this.invoke(this.abortController.signal).then((summary) => this.onRun?.(summary)).catch((error) => this.onError?.(error)).finally(() => {
750
+ this.inFlight = false;
751
+ this.inFlightPromise = null;
752
+ });
753
+ }
754
+ };
755
+
756
+ // src/activity/sync-registration.ts
757
+ var ActivitySyncRegistrar = class {
758
+ constructor(deps) {
759
+ this.deps = deps;
760
+ this.createScheduler = deps.createScheduler ?? ((options) => new ActivitySyncScheduler(options));
761
+ }
762
+ deps;
763
+ scheduler = null;
764
+ /** Latched by dispose() so a draining tick's onRun cannot re-arm maintenance. */
765
+ disposed = false;
766
+ createScheduler;
767
+ /** True while a scheduler is armed (test/telemetry observability). */
768
+ get armed() {
769
+ return this.scheduler !== null;
770
+ }
771
+ /**
772
+ * Arm (or re-arm) the periodic sync for the current lifecycle. Idempotent and
773
+ * safe to call repeatedly; always stops a prior scheduler first.
774
+ */
775
+ async register(signal) {
776
+ const prior = this.scheduler;
777
+ this.scheduler = null;
778
+ await prior?.stop();
779
+ if (signal.aborted) return;
780
+ if (this.deps.secureStoreEnabled && this.deps.config.enabled) {
781
+ log.warn(
782
+ "activity sync disabled: secure store is enabled but activity digests/SQLite are not encrypted at rest"
783
+ );
784
+ return;
785
+ }
786
+ this.disposed = false;
787
+ try {
788
+ const scheduler = this.createScheduler({
789
+ config: this.deps.config,
790
+ memoryDir: this.deps.memoryDir,
791
+ intervalMs: this.deps.config.autoSyncIntervalMinutes * 6e4,
792
+ // Strict refresh after each digest write (rule 31): bypasses the
793
+ // min-interval gate and throws on a real failure.
794
+ reindexSearch: (sig) => refreshActivityIndex(this.deps.getQmd(), this.deps.qmdCollection, sig),
795
+ // A failed reindex (QMD down) leaves the next unchanged-digest tick
796
+ // skipping afterWrites, so route the retry through the durable,
797
+ // throttled QMD path — but never after teardown.
798
+ onRun: (summary) => {
799
+ if (summary.reindexErrorCount > 0 && !this.disposed) {
800
+ log.warn(
801
+ `activity sync: ${summary.reindexErrorCount} source(s) had a failed reindex; queuing a maintenance retry`
802
+ );
803
+ this.deps.requestReindexRetry();
804
+ }
805
+ }
806
+ });
807
+ this.scheduler = scheduler;
808
+ scheduler.start();
809
+ if (signal.aborted) {
810
+ await scheduler.stop();
811
+ this.scheduler = null;
812
+ }
813
+ } catch (err) {
814
+ log.debug(`activity sync scheduler start failed (non-fatal): ${err}`);
815
+ }
816
+ }
817
+ /**
818
+ * Latch teardown and drain the scheduler. Latching first ensures a draining
819
+ * tick's onRun cannot queue a retry; the drain is awaited so no in-flight
820
+ * tick keeps writing after teardown.
821
+ */
822
+ async dispose() {
823
+ this.disposed = true;
824
+ const drain = this.scheduler?.stop();
825
+ this.scheduler = null;
826
+ await drain;
827
+ }
828
+ };
829
+
830
+ // src/orchestration/maintenance.ts
56
831
  function qmdMaintenanceSkipReasonForError(error) {
57
832
  const message = error instanceof Error ? error.message : String(error);
58
833
  return /^QMD (?:update|embed) skipped by .*min-interval gate$/.test(message) ? "throttled" : null;
@@ -60,6 +835,14 @@ function qmdMaintenanceSkipReasonForError(error) {
60
835
  var MaintenanceScheduler = class {
61
836
  constructor(deps) {
62
837
  this.deps = deps;
838
+ this.activityRegistrar = new ActivitySyncRegistrar({
839
+ config: deps.config.activity,
840
+ memoryDir: deps.config.memoryDir,
841
+ qmdCollection: deps.config.qmdCollection,
842
+ secureStoreEnabled: resolveRecallAuxiliaryCapabilities(deps.config).secureStore,
843
+ getQmd: () => deps.getQmd(),
844
+ requestReindexRetry: () => this.requestQmdMaintenanceForTool("activity-reindex-retry")
845
+ });
63
846
  }
64
847
  deps;
65
848
  // ── Consolidation scheduling state ──
@@ -81,6 +864,9 @@ var MaintenanceScheduler = class {
81
864
  // the bounding + post-write verification deterministically without a 400MB
82
865
  // fixture.
83
866
  lifecycleLedgerMaxBytes = STATE_FILE_MAX_DECRYPT_BYTES;
867
+ // ── Activity (screen-capture) sync scheduler (issue #1900) ──
868
+ // The full arm/re-arm/teardown lifecycle lives in ActivitySyncRegistrar.
869
+ activityRegistrar;
84
870
  // ───────────────────────────────────────────────────────────────────────
85
871
  // Cron auto-registration
86
872
  // ───────────────────────────────────────────────────────────────────────
@@ -93,7 +879,7 @@ var MaintenanceScheduler = class {
93
879
  * Returns when all eligible registrations have completed so callers that
94
880
  * `await deferredReady` can rely on jobs.json being current.
95
881
  */
96
- async autoRegisterCrons(_signal) {
882
+ async autoRegisterCrons(signal) {
97
883
  if (resolveRecallAuxiliaryCapabilities(this.deps.config).daySummary) {
98
884
  try {
99
885
  await this.autoRegisterDaySummaryCron();
@@ -136,10 +922,11 @@ var MaintenanceScheduler = class {
136
922
  log.debug(`graph edge decay cron auto-register failed (non-fatal): ${err}`);
137
923
  }
138
924
  }
925
+ await this.activityRegistrar.register(signal);
139
926
  }
140
927
  async autoRegisterDaySummaryCron() {
141
928
  const home = resolveHomeDir();
142
- const jobsPath = path.join(home, ".openclaw", "cron", "jobs.json");
929
+ const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
143
930
  try {
144
931
  if (!existsSync(jobsPath)) {
145
932
  log.debug("day-summary cron: jobs.json not found, skipping auto-register");
@@ -190,7 +977,7 @@ var MaintenanceScheduler = class {
190
977
  }
191
978
  async autoRegisterNightlyGovernanceCron() {
192
979
  const home = resolveHomeDir();
193
- const jobsPath = path.join(home, ".openclaw", "cron", "jobs.json");
980
+ const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
194
981
  try {
195
982
  if (!existsSync(jobsPath)) {
196
983
  log.debug("nightly governance cron: jobs.json not found, skipping auto-register");
@@ -212,7 +999,7 @@ var MaintenanceScheduler = class {
212
999
  }
213
1000
  async autoRegisterProceduralMiningCron() {
214
1001
  const home = resolveHomeDir();
215
- const jobsPath = path.join(home, ".openclaw", "cron", "jobs.json");
1002
+ const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
216
1003
  try {
217
1004
  if (!existsSync(jobsPath)) {
218
1005
  log.debug("procedural mining cron: jobs.json not found, skipping auto-register");
@@ -232,7 +1019,7 @@ var MaintenanceScheduler = class {
232
1019
  }
233
1020
  async autoRegisterContradictionScanCron() {
234
1021
  const home = resolveHomeDir();
235
- const jobsPath = path.join(home, ".openclaw", "cron", "jobs.json");
1022
+ const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
236
1023
  try {
237
1024
  if (!existsSync(jobsPath)) {
238
1025
  log.debug("contradiction scan cron: jobs.json not found, skipping auto-register");
@@ -252,7 +1039,7 @@ var MaintenanceScheduler = class {
252
1039
  }
253
1040
  async autoRegisterPatternReinforcementCron() {
254
1041
  const home = resolveHomeDir();
255
- const jobsPath = path.join(home, ".openclaw", "cron", "jobs.json");
1042
+ const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
256
1043
  try {
257
1044
  if (!existsSync(jobsPath)) {
258
1045
  log.debug("pattern reinforcement cron: jobs.json not found, skipping auto-register");
@@ -272,7 +1059,7 @@ var MaintenanceScheduler = class {
272
1059
  }
273
1060
  async autoRegisterGraphEdgeDecayCron() {
274
1061
  const home = resolveHomeDir();
275
- const jobsPath = path.join(home, ".openclaw", "cron", "jobs.json");
1062
+ const jobsPath = path3.join(home, ".openclaw", "cron", "jobs.json");
276
1063
  try {
277
1064
  if (!existsSync(jobsPath)) {
278
1065
  log.debug("graph edge decay cron: jobs.json not found, skipping auto-register");
@@ -536,7 +1323,7 @@ var MaintenanceScheduler = class {
536
1323
  */
537
1324
  async hasOverCapEncryptedLifecycleLedger(targets) {
538
1325
  for (const target of targets) {
539
- const ledgerPath = path.join(target.memoryDir, "state", "memory-lifecycle-ledger.jsonl");
1326
+ const ledgerPath = path3.join(target.memoryDir, "state", "memory-lifecycle-ledger.jsonl");
540
1327
  let size;
541
1328
  try {
542
1329
  size = (await stat(ledgerPath)).size;
@@ -588,7 +1375,7 @@ var MaintenanceScheduler = class {
588
1375
  * containment guard on every spill file it reads or deletes.
589
1376
  */
590
1377
  async drainPendingLifecycleAppendsForPath(memoryDir) {
591
- const ledgerPath = path.join(memoryDir, "state", "memory-lifecycle-ledger.jsonl");
1378
+ const ledgerPath = path3.join(memoryDir, "state", "memory-lifecycle-ledger.jsonl");
592
1379
  const spillDir = pendingLifecycleLedgerDir(ledgerPath);
593
1380
  let spillFiles;
594
1381
  let claimedOrphans;
@@ -602,14 +1389,14 @@ var MaintenanceScheduler = class {
602
1389
  if (spillFiles.length === 0 && claimedOrphans.length === 0) return;
603
1390
  const io = {
604
1391
  writeSecure: async (filePath, payload) => {
605
- await mkdir(path.dirname(filePath), { recursive: true });
606
- await writeFile(filePath, payload, "utf-8");
1392
+ await mkdir3(path3.dirname(filePath), { recursive: true });
1393
+ await writeFile2(filePath, payload, "utf-8");
607
1394
  },
608
1395
  readSecure: async (filePath) => {
609
1396
  if (await probeEncryptedRegularFileHeader(filePath)) {
610
1397
  throw new Error(`fallback lifecycle drain deferred: encrypted spill at ${filePath}`);
611
1398
  }
612
- return readFile(filePath, "utf-8");
1399
+ return readFile2(filePath, "utf-8");
613
1400
  }
614
1401
  };
615
1402
  try {
@@ -623,7 +1410,7 @@ var MaintenanceScheduler = class {
623
1410
  await appendFile(ledgerPath, payload, "utf-8");
624
1411
  },
625
1412
  async () => {
626
- await mkdir(path.dirname(ledgerPath), { recursive: true });
1413
+ await mkdir3(path3.dirname(ledgerPath), { recursive: true });
627
1414
  }
628
1415
  );
629
1416
  } catch (err) {
@@ -641,7 +1428,7 @@ var MaintenanceScheduler = class {
641
1428
  const targets = [];
642
1429
  const seen = /* @__PURE__ */ new Set();
643
1430
  const addTarget = (memoryDir, storage) => {
644
- const key = path.resolve(memoryDir);
1431
+ const key = path3.resolve(memoryDir);
645
1432
  if (seen.has(key)) return;
646
1433
  seen.add(key);
647
1434
  targets.push({ memoryDir, storage });
@@ -670,7 +1457,7 @@ var MaintenanceScheduler = class {
670
1457
  }
671
1458
  }
672
1459
  if (resolveNamespaceCapabilities(this.deps.config).namespaces) {
673
- const namespacesBase = path.join(this.deps.config.memoryDir, "namespaces");
1460
+ const namespacesBase = path3.join(this.deps.config.memoryDir, "namespaces");
674
1461
  let memoryDirReal;
675
1462
  try {
676
1463
  memoryDirReal = await realpath(this.deps.config.memoryDir);
@@ -695,7 +1482,7 @@ var MaintenanceScheduler = class {
695
1482
  }
696
1483
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
697
1484
  if (!entry.isDirectory() || entry.isSymbolicLink()) continue;
698
- const childPath = path.join(namespacesBase, entry.name);
1485
+ const childPath = path3.join(namespacesBase, entry.name);
699
1486
  try {
700
1487
  assertPathInsideRoot(memoryDirReal, await realpath(childPath), childPath);
701
1488
  } catch (err) {
@@ -707,7 +1494,7 @@ var MaintenanceScheduler = class {
707
1494
  if (resolveNamespaceStorage && routeNamespace !== null) {
708
1495
  try {
709
1496
  const storage = await resolveNamespaceStorage(routeNamespace);
710
- if (path.resolve(storage.dir) === path.resolve(childPath)) {
1497
+ if (path3.resolve(storage.dir) === path3.resolve(childPath)) {
711
1498
  addTarget(storage.dir, storage);
712
1499
  continue;
713
1500
  }
@@ -738,7 +1525,7 @@ var MaintenanceScheduler = class {
738
1525
  * an over-cap rewrite never arms the throttle.
739
1526
  */
740
1527
  async compactLifecycleLedgerTarget(target, threshold) {
741
- const ledgerPath = path.join(target.memoryDir, "state", "memory-lifecycle-ledger.jsonl");
1528
+ const ledgerPath = path3.join(target.memoryDir, "state", "memory-lifecycle-ledger.jsonl");
742
1529
  let size = 0;
743
1530
  try {
744
1531
  size = (await stat(ledgerPath)).size;
@@ -823,10 +1610,19 @@ var MaintenanceScheduler = class {
823
1610
  // Lifecycle
824
1611
  // ───────────────────────────────────────────────────────────────────────
825
1612
  /**
826
- * Clear any pending debounce timer. Called from the orchestrator's
827
- * destroy() so a late tick does not fire on a torn-down instance.
1613
+ * Clear any pending debounce timer and drain the activity sync scheduler.
1614
+ * Called from the orchestrator's destroy() so a late tick does not fire on a
1615
+ * torn-down instance and no in-flight activity tick keeps writing after
1616
+ * teardown. Async: awaits the scheduler's abort+drain before resolving.
828
1617
  */
829
- dispose() {
1618
+ async dispose() {
1619
+ const activityDrain = this.activityRegistrar.dispose();
1620
+ if (this.qmdMaintenanceTimer) {
1621
+ clearTimeout(this.qmdMaintenanceTimer);
1622
+ this.qmdMaintenanceTimer = null;
1623
+ }
1624
+ this.qmdMaintenancePending = false;
1625
+ await activityDrain;
830
1626
  if (this.qmdMaintenanceTimer) {
831
1627
  clearTimeout(this.qmdMaintenanceTimer);
832
1628
  this.qmdMaintenanceTimer = null;
@@ -836,6 +1632,19 @@ var MaintenanceScheduler = class {
836
1632
  };
837
1633
 
838
1634
  export {
1635
+ refreshActivityIndex,
1636
+ activityDatabasePath,
1637
+ ensureActivityStateDir,
1638
+ openActivityDatabase,
1639
+ applyActivitySchema,
1640
+ ActivityStore,
1641
+ activityCursorKey,
1642
+ syncActivitySource,
1643
+ ActivityHttpSourceClient,
1644
+ resolveActivitySyncDates,
1645
+ runActivitySyncOnce,
1646
+ ACTIVITY_SYNC_DEFAULT_INTERVAL_MS,
1647
+ ActivitySyncScheduler,
839
1648
  MaintenanceScheduler
840
1649
  };
841
- //# sourceMappingURL=chunk-XJT6LNLY.js.map
1650
+ //# sourceMappingURL=chunk-V6LYAXGW.js.map