@remnic/core 9.3.638 → 9.3.640

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 (293) hide show
  1. package/dist/access-cli.js +31 -31
  2. package/dist/access-http.d.ts +56 -5
  3. package/dist/access-http.js +17 -17
  4. package/dist/access-mcp.d.ts +4 -4
  5. package/dist/access-mcp.js +16 -16
  6. package/dist/access-schema.d.ts +3 -0
  7. package/dist/access-schema.js +1 -1
  8. package/dist/{access-service-DvEvBv9_.d.ts → access-service-jyYute38.d.ts} +3 -2
  9. package/dist/access-service.d.ts +4 -4
  10. package/dist/access-service.js +14 -14
  11. package/dist/action-confidence.d.ts +1 -1
  12. package/dist/active-memory-bridge.d.ts +1 -1
  13. package/dist/active-recall.d.ts +1 -1
  14. package/dist/active-recall.js +1 -1
  15. package/dist/{auto-sync-RFADEHIQ.js → auto-sync-54QQHOG5.js} +4 -4
  16. package/dist/behavior-learner.d.ts +1 -1
  17. package/dist/behavior-signals.d.ts +1 -1
  18. package/dist/bootstrap.d.ts +3 -3
  19. package/dist/briefing.d.ts +1 -1
  20. package/dist/briefing.js +3 -3
  21. package/dist/buffer-surprise-report.d.ts +1 -1
  22. package/dist/buffer.d.ts +1 -1
  23. package/dist/calibration.d.ts +1 -1
  24. package/dist/calibration.js +1 -1
  25. package/dist/causal-behavior.d.ts +1 -1
  26. package/dist/causal-consolidation.d.ts +1 -1
  27. package/dist/causal-consolidation.js +5 -5
  28. package/dist/{chunk-3RDYU3JS.js → chunk-3AZDLWDW.js} +3 -3
  29. package/dist/{chunk-FPNQF475.js → chunk-3F7RMHC4.js} +1 -1
  30. package/dist/chunk-3F7RMHC4.js.map +1 -0
  31. package/dist/{chunk-KXTLEB4L.js → chunk-3SF7QNKF.js} +2 -2
  32. package/dist/{chunk-U2YPUWRB.js → chunk-4GYGEKUB.js} +53 -7
  33. package/dist/chunk-4GYGEKUB.js.map +1 -0
  34. package/dist/{chunk-6BDVBBBY.js → chunk-666A3MOW.js} +88 -30
  35. package/dist/chunk-666A3MOW.js.map +1 -0
  36. package/dist/{chunk-EDQVAMQI.js → chunk-6GIKAUTN.js} +2 -2
  37. package/dist/{chunk-4HFJQCJZ.js → chunk-7HYPN2GC.js} +2 -2
  38. package/dist/{chunk-Q6YIJGXJ.js → chunk-7OGJQP7T.js} +2 -2
  39. package/dist/{chunk-242XFZ36.js → chunk-7OH7KPF5.js} +2 -2
  40. package/dist/{chunk-RW5DGAGO.js → chunk-7V36WCUW.js} +2 -2
  41. package/dist/{chunk-7A2QKUUA.js → chunk-7XRSLMUD.js} +2 -2
  42. package/dist/{chunk-723OMPUI.js → chunk-A3BS64GV.js} +7 -1
  43. package/dist/chunk-A3BS64GV.js.map +1 -0
  44. package/dist/{chunk-JCLECECB.js → chunk-A3UPMOJZ.js} +2 -2
  45. package/dist/{chunk-QHXW3LZV.js → chunk-C3EJ3UIS.js} +3 -3
  46. package/dist/{chunk-YCX45GOQ.js → chunk-C4YAIWDG.js} +3 -3
  47. package/dist/{chunk-A3HKTPPV.js → chunk-C63WC454.js} +12 -1
  48. package/dist/chunk-C63WC454.js.map +1 -0
  49. package/dist/{chunk-4S3N6HFG.js → chunk-C6XO6ONP.js} +2 -2
  50. package/dist/{chunk-WD2W4234.js → chunk-CDHRUNBX.js} +11 -2
  51. package/dist/chunk-CDHRUNBX.js.map +1 -0
  52. package/dist/{chunk-YQNADJCT.js → chunk-CMZLMIZI.js} +2 -2
  53. package/dist/{chunk-KVDUDYEN.js → chunk-DDRNDPX4.js} +2 -2
  54. package/dist/{chunk-ORFGK3XI.js → chunk-E6ZDCOHM.js} +2 -2
  55. package/dist/{chunk-TKWGAOLV.js → chunk-EEC4PCG5.js} +2 -2
  56. package/dist/{chunk-PW6GURU3.js → chunk-EKIQBGAY.js} +2 -2
  57. package/dist/{chunk-YR6GIWWY.js → chunk-GQL52GQ5.js} +2 -2
  58. package/dist/{chunk-77NAFXUD.js → chunk-H3HDXD3U.js} +2 -2
  59. package/dist/{chunk-X7EJF46S.js → chunk-H4RWKZE2.js} +2 -2
  60. package/dist/{chunk-LASA46TQ.js → chunk-HRXCPKN4.js} +2 -2
  61. package/dist/{chunk-PUEAEQSN.js → chunk-ISMZIMD3.js} +2 -2
  62. package/dist/{chunk-LEG7XWS2.js → chunk-IXSTQGYV.js} +2 -2
  63. package/dist/{chunk-CMN5AWAZ.js → chunk-J2KFC47S.js} +2 -2
  64. package/dist/{chunk-OSPVAP56.js → chunk-JFHEEDRY.js} +20 -12
  65. package/dist/chunk-JFHEEDRY.js.map +1 -0
  66. package/dist/{chunk-RAGXUVQW.js → chunk-KVUG7FF7.js} +4 -4
  67. package/dist/{chunk-K47C6M2C.js → chunk-M75TBFKQ.js} +2 -2
  68. package/dist/{chunk-DEDTFRH2.js → chunk-MUKXANAM.js} +14 -3
  69. package/dist/chunk-MUKXANAM.js.map +1 -0
  70. package/dist/chunk-O75CRYGF.js +15 -0
  71. package/dist/chunk-O75CRYGF.js.map +1 -0
  72. package/dist/{chunk-YROCXMCK.js → chunk-RJGPH2Q3.js} +2 -2
  73. package/dist/{chunk-QDAWVW75.js → chunk-SBHQBC37.js} +2 -2
  74. package/dist/{chunk-NRST7W5Q.js → chunk-SEOTBRO4.js} +3 -3
  75. package/dist/{chunk-QYGIQ5NM.js → chunk-SHHT4JTC.js} +2 -2
  76. package/dist/{chunk-MNDFVT4F.js → chunk-UDZYZ5Q5.js} +12 -10
  77. package/dist/chunk-UDZYZ5Q5.js.map +1 -0
  78. package/dist/{chunk-C4KKM62E.js → chunk-UU5LHD6J.js} +2 -2
  79. package/dist/{chunk-I3YAA4BI.js → chunk-UZBK2JR6.js} +19 -19
  80. package/dist/{chunk-TJRV4MWX.js → chunk-WAKPKNEX.js} +172 -51
  81. package/dist/chunk-WAKPKNEX.js.map +1 -0
  82. package/dist/{chunk-7KRYJUTB.js → chunk-Y2XKJJEI.js} +3 -3
  83. package/dist/{chunk-2JIYTTQJ.js → chunk-YMI4NRV7.js} +5 -5
  84. package/dist/{cli-BJBKNWIS.d.ts → cli-A4AIhv9e.d.ts} +3 -3
  85. package/dist/cli.d.ts +5 -5
  86. package/dist/cli.js +31 -31
  87. package/dist/compat/checks.js +2 -2
  88. package/dist/compounding/engine.d.ts +1 -1
  89. package/dist/compounding/engine.js +3 -3
  90. package/dist/compounding/preference-consolidator.d.ts +1 -1
  91. package/dist/compression-optimizer.d.ts +1 -1
  92. package/dist/config.d.ts +1 -1
  93. package/dist/config.js +1 -1
  94. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  95. package/dist/connectors/codex-materialize-runner.js +3 -3
  96. package/dist/connectors/codex-materialize.d.ts +1 -1
  97. package/dist/connectors/index.d.ts +1 -1
  98. package/dist/connectors/index.js +5 -5
  99. package/dist/consolidation-provenance-check.d.ts +1 -1
  100. package/dist/consolidation-undo.d.ts +1 -1
  101. package/dist/contradiction/index.d.ts +1 -1
  102. package/dist/conversation-index/backend.d.ts +1 -1
  103. package/dist/conversation-index/backend.js +3 -3
  104. package/dist/conversation-index/chunker.d.ts +1 -1
  105. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  106. package/dist/conversation-index/faiss-adapter.js +2 -2
  107. package/dist/conversation-index/indexer.d.ts +1 -1
  108. package/dist/conversation-index/search.d.ts +1 -1
  109. package/dist/day-summary.d.ts +1 -1
  110. package/dist/delinearize.d.ts +1 -1
  111. package/dist/direct-answer-wiring.d.ts +1 -1
  112. package/dist/direct-answer.d.ts +1 -1
  113. package/dist/embedding-fallback.d.ts +1 -1
  114. package/dist/enrichment/index.d.ts +1 -1
  115. package/dist/entity-retrieval.d.ts +1 -1
  116. package/dist/entity-retrieval.js +3 -3
  117. package/dist/entity-schema.d.ts +1 -1
  118. package/dist/explicit-capture.d.ts +3 -3
  119. package/dist/extraction-judge-telemetry.d.ts +1 -1
  120. package/dist/extraction-judge-training.d.ts +1 -1
  121. package/dist/extraction-judge.d.ts +1 -1
  122. package/dist/extraction-judge.js +2 -2
  123. package/dist/extraction.d.ts +1 -1
  124. package/dist/extraction.js +4 -4
  125. package/dist/fallback-llm.d.ts +1 -1
  126. package/dist/fallback-llm.js +1 -1
  127. package/dist/identity-continuity.d.ts +1 -1
  128. package/dist/importance.d.ts +1 -1
  129. package/dist/index.d.ts +9 -9
  130. package/dist/index.js +45 -45
  131. package/dist/intent.d.ts +1 -1
  132. package/dist/lcm/engine.d.ts +1 -1
  133. package/dist/lcm/index.d.ts +1 -1
  134. package/dist/lcm/tools.d.ts +1 -1
  135. package/dist/lifecycle.d.ts +1 -1
  136. package/dist/live-connectors-runner.d.ts +1 -1
  137. package/dist/local-llm.d.ts +1 -1
  138. package/dist/local-llm.js +2 -2
  139. package/dist/maintenance/memory-governance-cron.d.ts +8 -0
  140. package/dist/maintenance/memory-governance-cron.js +1 -1
  141. package/dist/maintenance/memory-governance.d.ts +1 -1
  142. package/dist/maintenance/memory-governance.js +3 -3
  143. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  144. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  145. package/dist/mcp-memory-inspector-app.d.ts +4 -4
  146. package/dist/memory-action-policy.d.ts +1 -1
  147. package/dist/memory-cache.d.ts +1 -1
  148. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  149. package/dist/memory-projection-store.d.ts +1 -1
  150. package/dist/memory-provenance.d.ts +1 -1
  151. package/dist/memory-worth-outcomes.d.ts +1 -1
  152. package/dist/migrate/from-engram.js +2 -2
  153. package/dist/models-json.d.ts +1 -1
  154. package/dist/namespaces/migrate.d.ts +1 -1
  155. package/dist/namespaces/migrate.js +10 -10
  156. package/dist/namespaces/principal.d.ts +1 -1
  157. package/dist/namespaces/search.d.ts +1 -1
  158. package/dist/namespaces/search.js +6 -6
  159. package/dist/namespaces/storage.d.ts +1 -1
  160. package/dist/namespaces/storage.js +3 -3
  161. package/dist/native-knowledge.d.ts +1 -1
  162. package/dist/network/tailscale.js +2 -2
  163. package/dist/operator-toolkit.d.ts +1 -1
  164. package/dist/operator-toolkit.js +13 -13
  165. package/dist/{orchestrator-ibTTeoUa.d.ts → orchestrator-BEDTiuEO.d.ts} +11 -5
  166. package/dist/orchestrator.d.ts +3 -3
  167. package/dist/orchestrator.js +27 -27
  168. package/dist/patterns-cli.d.ts +1 -1
  169. package/dist/policy-runtime.d.ts +1 -1
  170. package/dist/qmd-recall-cache.d.ts +1 -1
  171. package/dist/qmd.d.ts +4 -1
  172. package/dist/qmd.js +2 -2
  173. package/dist/recall-disclosure-escalation.d.ts +1 -1
  174. package/dist/recall-explain-renderer.d.ts +1 -1
  175. package/dist/recall-explain-renderer.js +3 -3
  176. package/dist/recall-planner-llm.d.ts +1 -1
  177. package/dist/recall-planner-llm.js +1 -1
  178. package/dist/recall-state.d.ts +1 -1
  179. package/dist/recall-tag-filter.d.ts +1 -1
  180. package/dist/recall-xray-cli.d.ts +1 -1
  181. package/dist/recall-xray-cli.js +4 -4
  182. package/dist/recall-xray-renderer.d.ts +1 -1
  183. package/dist/recall-xray-renderer.js +3 -3
  184. package/dist/recall-xray.d.ts +1 -1
  185. package/dist/recall-xray.js +2 -2
  186. package/dist/resolve-auth-token.d.ts +1 -1
  187. package/dist/resume-bundles.js +2 -2
  188. package/dist/retrieval-agents.d.ts +1 -1
  189. package/dist/retrieval-tiers.d.ts +1 -1
  190. package/dist/routing/engine.d.ts +1 -1
  191. package/dist/routing/store.d.ts +1 -1
  192. package/dist/runtime/child-process.js +1 -1
  193. package/dist/search/embed-helper.d.ts +1 -1
  194. package/dist/search/factory.d.ts +1 -1
  195. package/dist/search/factory.js +5 -5
  196. package/dist/search/index.d.ts +1 -1
  197. package/dist/search/index.js +5 -5
  198. package/dist/search/lancedb-backend.d.ts +1 -1
  199. package/dist/search/meilisearch-backend.d.ts +1 -1
  200. package/dist/search/noop-backend.d.ts +1 -1
  201. package/dist/search/orama-backend.d.ts +1 -1
  202. package/dist/search/port.d.ts +1 -1
  203. package/dist/search/remote-backend.d.ts +1 -1
  204. package/dist/{semantic-consolidation-BPs6BURk.d.ts → semantic-consolidation-Byut1ufK.d.ts} +1 -1
  205. package/dist/semantic-consolidation.d.ts +2 -2
  206. package/dist/semantic-consolidation.js +4 -4
  207. package/dist/semantic-rule-promotion.js +3 -3
  208. package/dist/semantic-rule-verifier.d.ts +1 -1
  209. package/dist/semantic-rule-verifier.js +3 -3
  210. package/dist/session-observer-bands.d.ts +1 -1
  211. package/dist/session-observer-state.d.ts +1 -1
  212. package/dist/shared-context/manager.d.ts +1 -1
  213. package/dist/signal.d.ts +1 -1
  214. package/dist/storage.d.ts +1 -1
  215. package/dist/storage.js +2 -2
  216. package/dist/summarizer.d.ts +1 -1
  217. package/dist/summarizer.js +4 -4
  218. package/dist/summary-snapshot.d.ts +1 -1
  219. package/dist/temporal-supersession.d.ts +1 -1
  220. package/dist/temporal-validity.d.ts +1 -1
  221. package/dist/threading.d.ts +1 -1
  222. package/dist/tier-migration.d.ts +1 -1
  223. package/dist/tier-routing.d.ts +1 -1
  224. package/dist/topics.d.ts +1 -1
  225. package/dist/transcript.d.ts +1 -1
  226. package/dist/{types-CpMPD8xl.d.ts → types-BzA1FKbg.d.ts} +4 -0
  227. package/dist/types.d.ts +1 -1
  228. package/dist/types.js +1 -1
  229. package/dist/utility-runtime.d.ts +1 -1
  230. package/dist/verified-recall.js +3 -3
  231. package/package.json +3 -1
  232. package/src/access-http.test.ts +110 -2
  233. package/src/access-http.ts +117 -1
  234. package/src/access-mcp.ts +15 -6
  235. package/src/access-schema.ts +14 -0
  236. package/src/access-service.ts +4 -1
  237. package/src/config.ts +14 -0
  238. package/src/fallback-llm.ts +16 -0
  239. package/src/index.ts +9 -1
  240. package/src/maintenance/memory-governance-cron.ts +113 -30
  241. package/src/orchestrator.ts +197 -32
  242. package/src/qmd.test.ts +35 -0
  243. package/src/qmd.ts +16 -1
  244. package/src/runtime/child-process.test.ts +15 -0
  245. package/src/runtime/child-process.ts +9 -22
  246. package/src/types.ts +4 -0
  247. package/dist/chunk-6BDVBBBY.js.map +0 -1
  248. package/dist/chunk-723OMPUI.js.map +0 -1
  249. package/dist/chunk-A3HKTPPV.js.map +0 -1
  250. package/dist/chunk-DEDTFRH2.js.map +0 -1
  251. package/dist/chunk-FPNQF475.js.map +0 -1
  252. package/dist/chunk-MNDFVT4F.js.map +0 -1
  253. package/dist/chunk-NNVTUXEB.js +0 -23
  254. package/dist/chunk-NNVTUXEB.js.map +0 -1
  255. package/dist/chunk-OSPVAP56.js.map +0 -1
  256. package/dist/chunk-TJRV4MWX.js.map +0 -1
  257. package/dist/chunk-U2YPUWRB.js.map +0 -1
  258. package/dist/chunk-WD2W4234.js.map +0 -1
  259. /package/dist/{auto-sync-RFADEHIQ.js.map → auto-sync-54QQHOG5.js.map} +0 -0
  260. /package/dist/{chunk-3RDYU3JS.js.map → chunk-3AZDLWDW.js.map} +0 -0
  261. /package/dist/{chunk-KXTLEB4L.js.map → chunk-3SF7QNKF.js.map} +0 -0
  262. /package/dist/{chunk-EDQVAMQI.js.map → chunk-6GIKAUTN.js.map} +0 -0
  263. /package/dist/{chunk-4HFJQCJZ.js.map → chunk-7HYPN2GC.js.map} +0 -0
  264. /package/dist/{chunk-Q6YIJGXJ.js.map → chunk-7OGJQP7T.js.map} +0 -0
  265. /package/dist/{chunk-242XFZ36.js.map → chunk-7OH7KPF5.js.map} +0 -0
  266. /package/dist/{chunk-RW5DGAGO.js.map → chunk-7V36WCUW.js.map} +0 -0
  267. /package/dist/{chunk-7A2QKUUA.js.map → chunk-7XRSLMUD.js.map} +0 -0
  268. /package/dist/{chunk-JCLECECB.js.map → chunk-A3UPMOJZ.js.map} +0 -0
  269. /package/dist/{chunk-QHXW3LZV.js.map → chunk-C3EJ3UIS.js.map} +0 -0
  270. /package/dist/{chunk-YCX45GOQ.js.map → chunk-C4YAIWDG.js.map} +0 -0
  271. /package/dist/{chunk-4S3N6HFG.js.map → chunk-C6XO6ONP.js.map} +0 -0
  272. /package/dist/{chunk-YQNADJCT.js.map → chunk-CMZLMIZI.js.map} +0 -0
  273. /package/dist/{chunk-KVDUDYEN.js.map → chunk-DDRNDPX4.js.map} +0 -0
  274. /package/dist/{chunk-ORFGK3XI.js.map → chunk-E6ZDCOHM.js.map} +0 -0
  275. /package/dist/{chunk-TKWGAOLV.js.map → chunk-EEC4PCG5.js.map} +0 -0
  276. /package/dist/{chunk-PW6GURU3.js.map → chunk-EKIQBGAY.js.map} +0 -0
  277. /package/dist/{chunk-YR6GIWWY.js.map → chunk-GQL52GQ5.js.map} +0 -0
  278. /package/dist/{chunk-77NAFXUD.js.map → chunk-H3HDXD3U.js.map} +0 -0
  279. /package/dist/{chunk-X7EJF46S.js.map → chunk-H4RWKZE2.js.map} +0 -0
  280. /package/dist/{chunk-LASA46TQ.js.map → chunk-HRXCPKN4.js.map} +0 -0
  281. /package/dist/{chunk-PUEAEQSN.js.map → chunk-ISMZIMD3.js.map} +0 -0
  282. /package/dist/{chunk-LEG7XWS2.js.map → chunk-IXSTQGYV.js.map} +0 -0
  283. /package/dist/{chunk-CMN5AWAZ.js.map → chunk-J2KFC47S.js.map} +0 -0
  284. /package/dist/{chunk-RAGXUVQW.js.map → chunk-KVUG7FF7.js.map} +0 -0
  285. /package/dist/{chunk-K47C6M2C.js.map → chunk-M75TBFKQ.js.map} +0 -0
  286. /package/dist/{chunk-YROCXMCK.js.map → chunk-RJGPH2Q3.js.map} +0 -0
  287. /package/dist/{chunk-QDAWVW75.js.map → chunk-SBHQBC37.js.map} +0 -0
  288. /package/dist/{chunk-NRST7W5Q.js.map → chunk-SEOTBRO4.js.map} +0 -0
  289. /package/dist/{chunk-QYGIQ5NM.js.map → chunk-SHHT4JTC.js.map} +0 -0
  290. /package/dist/{chunk-C4KKM62E.js.map → chunk-UU5LHD6J.js.map} +0 -0
  291. /package/dist/{chunk-I3YAA4BI.js.map → chunk-UZBK2JR6.js.map} +0 -0
  292. /package/dist/{chunk-7KRYJUTB.js.map → chunk-Y2XKJJEI.js.map} +0 -0
  293. /package/dist/{chunk-2JIYTTQJ.js.map → chunk-YMI4NRV7.js.map} +0 -0
@@ -78,6 +78,7 @@ import { LocalLlmClient } from "./local-llm.js";
78
78
  import {
79
79
  FallbackLlmClient,
80
80
  fallbackLlmRuntimeContextFromConfig,
81
+ gatewayTaskChainOptions,
81
82
  } from "./fallback-llm.js";
82
83
  import {
83
84
  ensureDaySummaryCron,
@@ -765,6 +766,105 @@ function qmdResultPathCandidates(
765
766
  const COMPACTION_SIGNAL_MAX_AGE_MS = 60 * 60 * 1000; // 1 hour
766
767
  const DEFAULT_QMD_STARTUP_COLLECTION_CHECK_TIMEOUT_MS = 10_000;
767
768
 
769
+ type DaySummaryGatherOptions = {
770
+ timeZone?: string;
771
+ now?: Date;
772
+ };
773
+
774
+ function normalizeIanaTimeZone(value: unknown): string | undefined {
775
+ if (typeof value !== "string") return undefined;
776
+ const trimmed = value.trim();
777
+ if (!trimmed) return undefined;
778
+ try {
779
+ Intl.DateTimeFormat(undefined, { timeZone: trimmed });
780
+ return trimmed;
781
+ } catch {
782
+ return undefined;
783
+ }
784
+ }
785
+
786
+ function formatDateInTimeZone(date: Date, timeZone: string): string {
787
+ const parts = new Intl.DateTimeFormat("en-US", {
788
+ timeZone,
789
+ year: "numeric",
790
+ month: "2-digit",
791
+ day: "2-digit",
792
+ }).formatToParts(date);
793
+ const get = (type: string): string => parts.find((part) => part.type === type)?.value ?? "";
794
+ return `${get("year")}-${get("month")}-${get("day")}`;
795
+ }
796
+
797
+ function utcDateKey(date: Date): string {
798
+ return date.toISOString().slice(0, 10);
799
+ }
800
+
801
+ function utcDateKeysAround(date: Date): string[] {
802
+ const dayMs = 86_400_000;
803
+ const keys = [
804
+ utcDateKey(new Date(date.getTime() - dayMs)),
805
+ utcDateKey(date),
806
+ utcDateKey(new Date(date.getTime() + dayMs)),
807
+ ];
808
+ return keys.filter((value, index, array) => array.indexOf(value) === index);
809
+ }
810
+
811
+ function utcDateKeysForLocalDay(date: Date, timeZone: string): string[] {
812
+ const targetLocalDate = formatDateInTimeZone(date, timeZone);
813
+ const keys = new Set<string>();
814
+ const hourMs = 3_600_000;
815
+ const scanStart = date.getTime() - 48 * hourMs;
816
+ const scanEnd = date.getTime() + 48 * hourMs;
817
+ for (let ms = scanStart; ms <= scanEnd; ms += hourMs) {
818
+ const candidate = new Date(ms);
819
+ if (formatDateInTimeZone(candidate, timeZone) === targetLocalDate) {
820
+ keys.add(utcDateKey(candidate));
821
+ }
822
+ }
823
+ return keys.size > 0 ? [...keys].sort() : utcDateKeysAround(date);
824
+ }
825
+
826
+ function parseFiniteDate(value: unknown): Date | null {
827
+ if (typeof value !== "string" || value.trim().length === 0) return null;
828
+ const parsed = new Date(value);
829
+ return Number.isFinite(parsed.getTime()) ? parsed : null;
830
+ }
831
+
832
+ function filterHourlySummaryMarkdownForLocalDay(
833
+ raw: string,
834
+ utcDate: string,
835
+ timeZone: string,
836
+ targetLocalDate: string,
837
+ ): string | null {
838
+ const hourHeaderPattern = /^## ([01]\d|2[0-3]):00[ \t]*$/gm;
839
+ const matches = Array.from(raw.matchAll(hourHeaderPattern));
840
+ if (matches.length === 0) return null;
841
+
842
+ const firstSectionStart = matches[0]?.index ?? 0;
843
+ const preamble = raw.slice(0, firstSectionStart).trim();
844
+ const sections: string[] = [];
845
+ for (let index = 0; index < matches.length; index += 1) {
846
+ const match = matches[index];
847
+ const hour = match[1];
848
+ if (!hour) continue;
849
+ const sectionTimestamp = parseFiniteDate(`${utcDate}T${hour}:00:00.000Z`);
850
+ if (
851
+ !sectionTimestamp ||
852
+ formatDateInTimeZone(sectionTimestamp, timeZone) !== targetLocalDate
853
+ ) {
854
+ continue;
855
+ }
856
+ const sectionStart = match.index ?? 0;
857
+ const sectionEnd = matches[index + 1]?.index ?? raw.length;
858
+ const section = raw.slice(sectionStart, sectionEnd).trim();
859
+ if (section.length > 0) sections.push(section);
860
+ }
861
+
862
+ if (sections.length === 0) return null;
863
+ return [preamble, ...sections]
864
+ .filter((section) => section.length > 0)
865
+ .join("\n\n");
866
+ }
867
+
768
868
  type SearchCollectionState = "present" | "missing" | "unknown" | "skipped";
769
869
 
770
870
  function qmdStartupCollectionCheckTimeoutMs(): number {
@@ -2294,10 +2394,12 @@ export class Orchestrator {
2294
2394
  ? await this._fastGatewayLlm.chatCompletion(messages, {
2295
2395
  maxTokens: targetTokens * 2,
2296
2396
  timeoutMs: this.config.localLlmFastTimeoutMs,
2297
- agentId:
2298
- this.config.fastGatewayAgentId ||
2299
- this.config.gatewayAgentId ||
2300
- undefined,
2397
+ // LCM is latency-sensitive. A configured fast persona is the
2398
+ // explicit fast-tier route; otherwise use taskModelChain so LCM
2399
+ // avoids falling to an expensive gateway default. Issue #1473.
2400
+ ...(this.config.fastGatewayAgentId
2401
+ ? { agentId: this.config.fastGatewayAgentId }
2402
+ : gatewayTaskChainOptions(this.config)),
2301
2403
  })
2302
2404
  : await this.localLlm.chatCompletion(messages, {
2303
2405
  maxTokens: targetTokens * 2,
@@ -3206,8 +3308,10 @@ export class Orchestrator {
3206
3308
  }
3207
3309
 
3208
3310
  /**
3209
- * Auto-register the engram-day-summary cron job in OpenClaw if it doesn't exist.
3311
+ * Auto-register the engram-day-summary cron job in OpenClaw.
3312
+ * Reconciles model and timezone on every startup so config changes propagate.
3210
3313
  * Fire-and-forget — never blocks init or crashes on failure.
3314
+ * Issues #1474, #1475.
3211
3315
  */
3212
3316
  private async autoRegisterDaySummaryCron(): Promise<void> {
3213
3317
  const home = resolveHomeDir();
@@ -3220,15 +3324,57 @@ export class Orchestrator {
3220
3324
  );
3221
3325
  return;
3222
3326
  }
3223
- const created = await ensureDaySummaryCron(jobsPath, {
3224
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
3327
+
3328
+ // Resolve an OpenClaw cron-routing model only in gateway mode. In plugin
3329
+ // mode, summaryModel is a direct-client model id for Remnic's own LLM
3330
+ // calls and may be unroutable as an OpenClaw agentTurn model.
3331
+ const rawSummaryModel = this.config.summaryModel;
3332
+ const taskPrimary = this.config.taskModelChain?.primary;
3333
+ const isGateway = this.config.modelSource === "gateway";
3334
+ const model = isGateway ? (rawSummaryModel || taskPrimary || undefined) : undefined;
3335
+ // Attach task-chain fallbacks only when the model matches the task-chain
3336
+ // primary. If summaryModel is a distinct override, its fallbacks would
3337
+ // be unrelated to the task chain. Also append gateway default models as
3338
+ // tail fallbacks (de-duped) so a task-chain outage doesn't stop the cron
3339
+ // before reaching the gateway default chain. Mirrors hourly cron pattern.
3340
+ const fallbacks: string[] = [];
3341
+ if (model && taskPrimary && model === taskPrimary) {
3342
+ const seen = new Set<string>(model ? [model] : []);
3343
+ const addUnique = (value: string | undefined) => {
3344
+ if (typeof value !== "string") return;
3345
+ const trimmed = value.trim();
3346
+ if (trimmed.length > 0 && !seen.has(trimmed)) {
3347
+ seen.add(trimmed);
3348
+ fallbacks.push(trimmed);
3349
+ }
3350
+ };
3351
+ for (const fb of this.config.taskModelChain?.fallbacks ?? []) addUnique(fb);
3352
+ const gwDefaults = this.config.gatewayConfig?.agents?.defaults?.model;
3353
+ addUnique(gwDefaults?.primary);
3354
+ if (Array.isArray(gwDefaults?.fallbacks)) {
3355
+ for (const fb of gwDefaults.fallbacks) addUnique(fb);
3356
+ }
3357
+ }
3358
+
3359
+ // Resolve timezone: configurable override, then server default
3360
+ const timezone = this.config.daySummaryTimezone
3361
+ || Intl.DateTimeFormat().resolvedOptions().timeZone;
3362
+
3363
+ const result = await ensureDaySummaryCron(jobsPath, {
3364
+ timezone,
3365
+ ...(model ? { model } : {}),
3366
+ ...(fallbacks.length > 0 ? { fallbacks } : {}),
3225
3367
  });
3226
- if (created.created) {
3368
+ if (result.created) {
3227
3369
  log.info(
3228
- `day-summary cron auto-registered (${created.jobId}, 23:47 ${Intl.DateTimeFormat().resolvedOptions().timeZone})`,
3370
+ `day-summary cron auto-registered (${result.jobId}, 23:47 ${timezone}${model ? `, model: ${model}` : ""})`,
3371
+ );
3372
+ } else if (result.updated) {
3373
+ log.info(
3374
+ `day-summary cron reconciled (${result.jobId}, timezone: ${timezone}${model ? `, model: ${model}` : ""})`,
3229
3375
  );
3230
3376
  } else {
3231
- log.debug("day-summary cron already exists, skipping auto-register");
3377
+ log.debug("day-summary cron already up to date");
3232
3378
  }
3233
3379
  } catch (err) {
3234
3380
  log.debug(`day-summary cron auto-register error: ${err}`);
@@ -4149,8 +4295,9 @@ export class Orchestrator {
4149
4295
  */
4150
4296
  async generateDaySummaryAuto(
4151
4297
  namespace?: string,
4298
+ options: DaySummaryGatherOptions = {},
4152
4299
  ): Promise<DaySummaryResult | null> {
4153
- const gathered = await this.gatherTodayFacts(namespace);
4300
+ const gathered = await this.gatherTodayFacts(namespace, options);
4154
4301
  if (!gathered || !gathered.trim()) {
4155
4302
  log.warn("generateDaySummaryAuto: no facts found for today, skipping");
4156
4303
  return null;
@@ -4162,24 +4309,27 @@ export class Orchestrator {
4162
4309
  * Read today's facts and hourly summaries from storage, returning them
4163
4310
  * as a formatted string suitable for generateDaySummary().
4164
4311
  */
4165
- async gatherTodayFacts(namespace?: string): Promise<string> {
4312
+ async gatherTodayFacts(
4313
+ namespace?: string,
4314
+ options: DaySummaryGatherOptions = {},
4315
+ ): Promise<string> {
4166
4316
  const ns =
4167
4317
  namespace && namespace.length > 0
4168
4318
  ? namespace
4169
4319
  : this.config.defaultNamespace;
4170
4320
  const storage = await this.storageRouter.storageFor(ns);
4171
- // Facts are stored under UTC dates, but a local calendar day can span
4172
- // two UTC dates (e.g. 23:47 local in UTC-6 is 05:47 UTC the next day). To capture
4173
- // all facts for the local day, read from both the current UTC date and
4174
- // yesterday's UTC date (which covers the local day's morning hours).
4175
- const now = new Date();
4176
- const utcToday = now.toISOString().slice(0, 10);
4177
- const yesterday = new Date(now.getTime() - 86_400_000)
4178
- .toISOString()
4179
- .slice(0, 10);
4180
- const datesToScan = [yesterday, utcToday].filter(
4181
- (v, i, a) => a.indexOf(v) === i,
4182
- );
4321
+ const configuredTimeZone = normalizeIanaTimeZone(options.timeZone)
4322
+ ?? normalizeIanaTimeZone(this.config.daySummaryTimezone);
4323
+ const timeZone =
4324
+ configuredTimeZone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
4325
+ const now = options.now instanceof Date && Number.isFinite(options.now.getTime())
4326
+ ? options.now
4327
+ : new Date();
4328
+ const targetLocalDate = formatDateInTimeZone(now, timeZone);
4329
+ // Facts are stored under UTC date directories, while the summary target is
4330
+ // a local calendar day. Scan the UTC-date envelope that overlaps the local
4331
+ // day, then filter parseable fact timestamps to that configured local day.
4332
+ const datesToScan = utcDateKeysForLocalDay(now, timeZone);
4183
4333
  const factsBaseDir = path.join(storage.dir, "facts");
4184
4334
  const MAX_CHARS = 100_000;
4185
4335
 
@@ -4206,13 +4356,21 @@ export class Orchestrator {
4206
4356
  .slice(colonIdx + 1)
4207
4357
  .trim();
4208
4358
  }
4359
+ const created = fm.created || "unknown";
4360
+ const createdAt = parseFiniteDate(created);
4361
+ if (
4362
+ createdAt &&
4363
+ formatDateInTimeZone(createdAt, timeZone) !== targetLocalDate
4364
+ ) {
4365
+ continue;
4366
+ }
4209
4367
  facts.push({
4210
4368
  path: fullPath,
4211
4369
  frontmatter: {
4212
4370
  id: fm.id || path.basename(entry.name, ".md"),
4213
4371
  category: (fm.category as any) || "fact",
4214
- created: fm.created || "unknown",
4215
- updated: fm.updated || fm.created || "unknown",
4372
+ created,
4373
+ updated: fm.updated || created,
4216
4374
  source: fm.source || "unknown",
4217
4375
  confidence: parseFloat(fm.confidence || "0.8"),
4218
4376
  confidenceTier: (fm.confidenceTier as any) || "implied",
@@ -4230,9 +4388,10 @@ export class Orchestrator {
4230
4388
  }
4231
4389
 
4232
4390
  // Sort facts by created timestamp (most recent last) so truncation keeps newest
4233
- facts.sort((a, b) =>
4234
- a.frontmatter.created < b.frontmatter.created ? -1 : 1,
4235
- );
4391
+ facts.sort((a, b) => {
4392
+ if (a.frontmatter.created === b.frontmatter.created) return 0;
4393
+ return a.frontmatter.created < b.frontmatter.created ? -1 : 1;
4394
+ });
4236
4395
 
4237
4396
  // --- Read hourly summaries for the scanned dates ---
4238
4397
  const hourlySummaries: string[] = [];
@@ -4245,8 +4404,14 @@ export class Orchestrator {
4245
4404
  const summaryFile = path.join(hourlyBaseDir, sk.name, `${date}.md`);
4246
4405
  try {
4247
4406
  const raw = await readFile(summaryFile, "utf-8");
4248
- if (raw.trim().length > 0) {
4249
- hourlySummaries.push(raw.trim());
4407
+ const filtered = filterHourlySummaryMarkdownForLocalDay(
4408
+ raw,
4409
+ date,
4410
+ timeZone,
4411
+ targetLocalDate,
4412
+ );
4413
+ if (filtered) {
4414
+ hourlySummaries.push(filtered);
4250
4415
  }
4251
4416
  } catch {
4252
4417
  // No summary file for this session/date
@@ -4284,7 +4449,7 @@ export class Orchestrator {
4284
4449
  }
4285
4450
 
4286
4451
  log.info(
4287
- `gatherTodayFacts: collected ${facts.length} facts, ${hourlySummaries.length} hourly summaries (${formatted.length} chars)`,
4452
+ `gatherTodayFacts: collected ${facts.length} facts, ${hourlySummaries.length} hourly summaries for ${targetLocalDate} (${timeZone}, ${formatted.length} chars)`,
4288
4453
  );
4289
4454
 
4290
4455
  return formatted;
package/src/qmd.test.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
+ import os from "node:os";
3
+ import path from "node:path";
2
4
  import test from "node:test";
3
5
 
4
6
  import { parseConfig } from "./config.js";
@@ -148,6 +150,39 @@ test("post-install auto-upgrade probes PATH qmd before stale fallback paths", ()
148
150
  ]);
149
151
  });
150
152
 
153
+ test("QmdClient preserves configured qmdPath diagnostics when all probes fail", async () => {
154
+ const { QmdClient } = await import("./qmd.js");
155
+ const originalPath = process.env.PATH;
156
+ const originalWindowsPath = process.env.Path;
157
+ const missingQmdPath = path.join(
158
+ os.tmpdir(),
159
+ `remnic-missing-qmd-${process.pid}-${Date.now()}`,
160
+ "qmd.cmd",
161
+ );
162
+
163
+ process.env.PATH = "";
164
+ process.env.Path = "";
165
+ try {
166
+ const client = new QmdClient("test-collection", 10, {
167
+ qmdPath: missingQmdPath,
168
+ qmdFallbackPaths: [],
169
+ });
170
+
171
+ assert.equal(await client.probe(), false);
172
+ const status = client.debugStatus();
173
+
174
+ assert.ok(status.includes(`cliPath=${missingQmdPath}`), status);
175
+ assert.ok(status.includes("cliPathSource=configured"), status);
176
+ assert.ok(status.includes("cliProbeError="), status);
177
+ assert.ok(!status.includes("/opt/homebrew/bin/qmd"), status);
178
+ } finally {
179
+ if (originalPath === undefined) delete process.env.PATH;
180
+ else process.env.PATH = originalPath;
181
+ if (originalWindowsPath === undefined) delete process.env.Path;
182
+ else process.env.Path = originalWindowsPath;
183
+ }
184
+ });
185
+
151
186
  test("QmdClient applies chunk strategy to normal and forced embed args", async () => {
152
187
  const { QmdClient } = await import("./qmd.js");
153
188
  const client = new QmdClient("test", 5, {
package/src/qmd.ts CHANGED
@@ -42,6 +42,8 @@ export interface QmdClientOptions {
42
42
  qmdSearchStrategy?: QmdSearchStrategy;
43
43
  /** Subprocess fallback command; default "query" keeps LLM expansion. Issue #1335. */
44
44
  qmdSubprocessStrategy?: QmdSubprocessStrategy;
45
+ /** Override automatic qmd fallback probes; defaults to known install paths. */
46
+ qmdFallbackPaths?: string[];
45
47
  /** Per-call daemon search timeout in ms; default 8000. Issue #1335. */
46
48
  qmdDaemonTimeoutMs?: number;
47
49
  }
@@ -1248,6 +1250,7 @@ export class QmdClient implements SearchBackend {
1248
1250
  private readonly qmdIndexName?: string;
1249
1251
  private readonly qmdSearchStrategy: QmdSearchStrategy;
1250
1252
  private readonly qmdSubprocessStrategy: QmdSubprocessStrategy;
1253
+ private readonly qmdFallbackPaths: string[];
1251
1254
  private readonly daemonTimeoutMs: number;
1252
1255
  private readonly qmdRuntimeEnv: QmdRuntimeEnv;
1253
1256
  private qmdPathSource: "auto-path" | "auto-fallback" | "configured" = "auto-path";
@@ -1303,6 +1306,9 @@ export class QmdClient implements SearchBackend {
1303
1306
  : "hybrid";
1304
1307
  // Default "query" keeps `qmd query` (LLM expansion + rerank) per gotcha #7. Issue #1335.
1305
1308
  this.qmdSubprocessStrategy = opts?.qmdSubprocessStrategy === "search" ? "search" : "query";
1309
+ this.qmdFallbackPaths = opts?.qmdFallbackPaths
1310
+ ? opts.qmdFallbackPaths.map((candidate) => candidate.trim()).filter(Boolean)
1311
+ : QMD_FALLBACK_PATHS;
1306
1312
  // Default 8000ms preserves the historical hardcoded daemon timeout. Issue #1335.
1307
1313
  // Floor of 1000ms avoids absurdly small values; callers wanting CPU-only HyDE
1308
1314
  // headroom can raise this (e.g. 20000) without code changes.
@@ -1404,9 +1410,16 @@ export class QmdClient implements SearchBackend {
1404
1410
  }
1405
1411
 
1406
1412
  private async probeCli(): Promise<boolean> {
1413
+ let configuredProbeFailure: string | null = null;
1407
1414
  const markProbeFailure = (err: unknown): void => {
1408
1415
  this.lastCliProbeError = err instanceof Error ? err.message : String(err);
1409
1416
  };
1417
+ const restoreConfiguredProbeFailure = (): void => {
1418
+ if (!this.configuredQmdPath || !configuredProbeFailure) return;
1419
+ this.qmdPath = this.configuredQmdPath;
1420
+ this.qmdPathSource = "configured";
1421
+ this.lastCliProbeError = configuredProbeFailure;
1422
+ };
1410
1423
  const recordProbeSuccess = async (
1411
1424
  result: { stdout: string; stderr: string },
1412
1425
  qmdPath: string,
@@ -1428,6 +1441,7 @@ export class QmdClient implements SearchBackend {
1428
1441
  return true;
1429
1442
  } catch (err) {
1430
1443
  markProbeFailure(err);
1444
+ configuredProbeFailure = this.lastCliProbeError;
1431
1445
  // Do not hard-fail here: fall through to PATH/fallback probing.
1432
1446
  // This keeps recall healthy even when configured path is stale.
1433
1447
  this.logCliProbeWarning(
@@ -1444,7 +1458,7 @@ export class QmdClient implements SearchBackend {
1444
1458
  } catch (err) {
1445
1459
  markProbeFailure(err);
1446
1460
  // Try fallback paths
1447
- for (const fallbackPath of QMD_FALLBACK_PATHS) {
1461
+ for (const fallbackPath of this.qmdFallbackPaths) {
1448
1462
  try {
1449
1463
  const result = await runQmd(["--version"], QMD_PROBE_TIMEOUT_MS, fallbackPath, undefined, this.qmdRuntimeEnv);
1450
1464
  await recordProbeSuccess(result, fallbackPath, "auto-fallback");
@@ -1456,6 +1470,7 @@ export class QmdClient implements SearchBackend {
1456
1470
  }
1457
1471
  }
1458
1472
  this.available = false;
1473
+ restoreConfiguredProbeFailure();
1459
1474
  return false;
1460
1475
  }
1461
1476
  }
@@ -0,0 +1,15 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { launchProcessSync } from "./child-process.js";
5
+
6
+ test("launchProcessSync executes commands through the shared process wrapper", () => {
7
+ const result = launchProcessSync(
8
+ process.execPath,
9
+ ["-e", "process.stdout.write('ok')"],
10
+ { encoding: "utf8" },
11
+ );
12
+
13
+ assert.equal(result.status, 0);
14
+ assert.equal(result.stdout, "ok");
15
+ });
@@ -1,9 +1,5 @@
1
- import { createRequire } from "node:module";
1
+ import crossSpawn from "cross-spawn";
2
2
 
3
- const require = createRequire(import.meta.url);
4
- const PROCESS_MODULE_NAME = `node:${"child"}_${"process"}`;
5
-
6
- type ProcessModule = Record<string, unknown>;
7
3
  type ProcessOptions = Record<string, unknown>;
8
4
  type ProcessStream = {
9
5
  destroyed?: boolean;
@@ -22,9 +18,12 @@ type ProcessResult = {
22
18
  stderr?: string;
23
19
  };
24
20
 
25
- function loadModule(): ProcessModule {
26
- return require(PROCESS_MODULE_NAME) as ProcessModule;
27
- }
21
+ type SpawnApi = {
22
+ (command: string, args?: readonly string[], options?: ProcessOptions): CommandChildProcess;
23
+ sync: (command: string, args: readonly string[], options: ProcessOptions) => ProcessResult;
24
+ };
25
+
26
+ const spawnApi = crossSpawn as unknown as SpawnApi;
28
27
 
29
28
  export type CommandChildProcess = {
30
29
  exitCode?: number | null;
@@ -43,13 +42,7 @@ export function launchProcess(
43
42
  args: string[],
44
43
  options?: ProcessOptions,
45
44
  ): CommandChildProcess {
46
- const moduleApi = loadModule();
47
- const launch = moduleApi["spawn"] as (
48
- command: string,
49
- args?: readonly string[],
50
- options?: ProcessOptions,
51
- ) => CommandChildProcess;
52
- return launch(command, args, options);
45
+ return spawnApi(command, args, options);
53
46
  }
54
47
 
55
48
  export function launchProcessSync(
@@ -57,11 +50,5 @@ export function launchProcessSync(
57
50
  args: string[],
58
51
  options: ProcessOptions,
59
52
  ): ProcessResult {
60
- const moduleApi = loadModule();
61
- const launchSync = moduleApi["spawnSync"] as (
62
- command: string,
63
- args: readonly string[],
64
- options: ProcessOptions,
65
- ) => ProcessResult;
66
- return launchSync(command, args, options);
53
+ return spawnApi.sync(command, args, options);
67
54
  }
package/src/types.ts CHANGED
@@ -945,6 +945,8 @@ export interface PluginConfig {
945
945
  // Hourly summaries
946
946
  hourlySummariesEnabled: boolean;
947
947
  daySummaryEnabled: boolean;
948
+ /** Timezone override for the day summary cron (issue #1475). */
949
+ daySummaryTimezone?: string;
948
950
  /** If true, Engram may attempt to auto-register an hourly summary cron job (default off). */
949
951
  hourlySummaryCronAutoRegister: boolean;
950
952
  /** If true, Engram may attempt to auto-register the nightly governance cron job (default off). */
@@ -1280,6 +1282,8 @@ export interface PluginConfig {
1280
1282
  * consolidation. When set, this chain is resolved through gateway providers
1281
1283
  * instead of using gatewayAgentId or agents.defaults.model. All task paths
1282
1284
  * route through the shared `gatewayTaskChainOptions` helper.
1285
+ * LCM summarization is the one fast-tier exception: if `fastGatewayAgentId`
1286
+ * is configured, LCM uses that persona before falling back to this chain.
1283
1287
  *
1284
1288
  * Only takes effect when `modelSource: "gateway"`; ignored (with a startup
1285
1289
  * warning) under `modelSource: "plugin"`. See issue #1365 / PR #1370.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/maintenance/memory-governance-cron.ts"],"sourcesContent":["import { mkdir, readFile, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nconst DAY_SUMMARY_CRON_ID = \"engram-day-summary\";\nconst GOVERNANCE_CRON_ID = \"engram-nightly-governance\";\nconst PROCEDURAL_MINING_CRON_ID = \"engram-procedural-mining\";\nconst CONTRADICTION_SCAN_CRON_ID = \"engram-contradiction-scan\";\nconst PATTERN_REINFORCEMENT_CRON_ID = \"engram-pattern-reinforcement\";\nconst GRAPH_EDGE_DECAY_CRON_ID = \"engram-graph-edge-decay\";\n\ntype CronJobsShape =\n | Array<Record<string, unknown>>\n | {\n jobs: Array<Record<string, unknown>>;\n [key: string]: unknown;\n };\n\nasync function acquireCronJobsLock(jobsPath: string): Promise<() => Promise<void>> {\n const lockPath = `${jobsPath}.lock`;\n const start = Date.now();\n const staleMs = 30_000;\n const timeoutMs = 5_000;\n await mkdir(path.dirname(lockPath), { recursive: true });\n\n while (Date.now() - start < timeoutMs) {\n try {\n await mkdir(lockPath);\n return async () => {\n try {\n await rm(lockPath, { recursive: true, force: true });\n } catch {\n // Lock cleanup should not fail cron registration.\n }\n };\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code !== \"EEXIST\") {\n throw error;\n }\n try {\n const lockStat = await stat(lockPath);\n if (Date.now() - lockStat.mtimeMs > staleMs) {\n await rm(lockPath, { recursive: true, force: true });\n continue;\n }\n } catch {\n // Lock may have been released between stat/rm attempts.\n }\n await new Promise((resolve) => setTimeout(resolve, 25));\n }\n }\n\n throw new Error(`cron jobs lock acquisition timed out after ${timeoutMs}ms`);\n}\n\nfunction parseCronJobsShape(raw: string): { parsed: CronJobsShape; jobs: Array<Record<string, unknown>> } {\n const parsed = JSON.parse(raw) as CronJobsShape;\n const jobs = Array.isArray(parsed) ? parsed : Array.isArray(parsed.jobs) ? parsed.jobs : null;\n if (!jobs) {\n throw new Error(\"jobs.json has unexpected structure\");\n }\n return { parsed, jobs };\n}\n\nasync function writeCronJobsAtomic(jobsPath: string, value: CronJobsShape): Promise<void> {\n const tempPath = `${jobsPath}.${process.pid}.${Date.now()}.tmp`;\n await writeFile(tempPath, JSON.stringify(value, null, 2) + \"\\n\", \"utf-8\");\n await rename(tempPath, jobsPath);\n}\n\nexport async function ensureCronJob(\n jobsPath: string,\n jobId: string,\n buildJob: () => Record<string, unknown>,\n options: { updateExisting?: boolean; updateFields?: string[] } = {},\n): Promise<{ created: boolean; updated: boolean; jobId: string }> {\n const releaseLock = await acquireCronJobsLock(jobsPath);\n try {\n const raw = await readFile(jobsPath, \"utf-8\");\n const { parsed, jobs } = parseCronJobsShape(raw);\n\n const existingIndex = jobs.findIndex((job) => job.id === jobId);\n if (existingIndex >= 0) {\n if (!options.updateExisting) {\n return { created: false, updated: false, jobId };\n }\n\n const desired = buildJob();\n const existing = jobs[existingIndex];\n const next = mergeCronJobUpdate(existing, desired, options.updateFields);\n if (stableJson(existing) === stableJson(next)) {\n return { created: false, updated: false, jobId };\n }\n\n jobs[existingIndex] = next;\n const output = Array.isArray(parsed) ? jobs : { ...parsed, jobs };\n await writeCronJobsAtomic(jobsPath, output);\n return { created: false, updated: true, jobId };\n }\n\n jobs.push(buildJob());\n const output = Array.isArray(parsed) ? jobs : { ...parsed, jobs };\n await writeCronJobsAtomic(jobsPath, output);\n return { created: true, updated: false, jobId };\n } finally {\n await releaseLock();\n }\n}\n\nfunction mergeCronJobUpdate(\n existing: Record<string, unknown>,\n desired: Record<string, unknown>,\n updateFields?: string[],\n): Record<string, unknown> {\n if (!updateFields) {\n return desired;\n }\n\n const next = { ...existing };\n for (const field of updateFields) {\n if (Object.prototype.hasOwnProperty.call(desired, field)) {\n next[field] = desired[field];\n } else {\n delete next[field];\n }\n }\n return next;\n}\n\nfunction stableJson(value: unknown): string {\n if (Array.isArray(value)) {\n return `[${value.map((item) => stableJson(item)).join(\",\")}]`;\n }\n if (value && typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n return `{${Object.keys(record)\n .sort()\n .map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`)\n .join(\",\")}}`;\n }\n return JSON.stringify(value);\n}\n\nexport async function ensureDaySummaryCron(\n jobsPath: string,\n options: {\n timezone: string;\n agentId?: string;\n },\n): Promise<{ created: boolean; jobId: string }> {\n const agentId =\n typeof options.agentId === \"string\" && options.agentId.trim().length > 0\n ? options.agentId.trim()\n : \"main\";\n\n return ensureCronJob(jobsPath, DAY_SUMMARY_CRON_ID, () => ({\n id: DAY_SUMMARY_CRON_ID,\n agentId,\n name: \"Remnic Day Summary (auto)\",\n enabled: true,\n schedule: {\n kind: \"cron\",\n expr: \"47 23 * * *\",\n tz: options.timezone,\n },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n timeoutSeconds: 900,\n thinking: \"off\",\n message:\n \"You are OpenClaw automation. Call tool engram.day_summary with empty params (it will auto-gather today's facts). If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool.\",\n },\n delivery: { mode: \"none\" },\n }));\n}\n\nexport async function ensureNightlyGovernanceCron(\n jobsPath: string,\n options: {\n timezone: string;\n agentId?: string;\n recentDays?: number;\n maxMemories?: number;\n batchSize?: number;\n scheduleExpr?: string;\n },\n): Promise<{ created: boolean; jobId: string }> {\n const recentDays =\n typeof options.recentDays === \"number\" && Number.isFinite(options.recentDays)\n ? Math.max(1, Math.floor(options.recentDays))\n : 2;\n const maxMemories =\n typeof options.maxMemories === \"number\" && Number.isFinite(options.maxMemories)\n ? Math.max(1, Math.floor(options.maxMemories))\n : 500;\n const batchSize =\n typeof options.batchSize === \"number\" && Number.isFinite(options.batchSize)\n ? Math.max(1, Math.floor(options.batchSize))\n : 100;\n const scheduleExpr =\n typeof options.scheduleExpr === \"string\" && options.scheduleExpr.trim().length > 0\n ? options.scheduleExpr.trim()\n : \"23 2 * * *\";\n const agentId =\n typeof options.agentId === \"string\" && options.agentId.trim().length > 0\n ? options.agentId.trim()\n : \"main\";\n\n return ensureCronJob(jobsPath, GOVERNANCE_CRON_ID, () => ({\n id: GOVERNANCE_CRON_ID,\n agentId,\n name: \"Remnic Nightly Governance (batched)\",\n enabled: true,\n schedule: {\n kind: \"cron\",\n expr: scheduleExpr,\n tz: options.timezone,\n },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n timeoutSeconds: 900,\n thinking: \"off\",\n message:\n \"You are OpenClaw automation. Call the tool `engram.memory_governance_run` with params \" +\n `{\"mode\": \"apply\", \"recentDays\": ${recentDays}, \"maxMemories\": ${maxMemories}, \"batchSize\": ${batchSize}}` +\n \". If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool.\",\n },\n delivery: { mode: \"none\" },\n }));\n}\n\nexport async function ensureProceduralMiningCron(\n jobsPath: string,\n options: {\n timezone: string;\n agentId?: string;\n scheduleExpr?: string;\n },\n): Promise<{ created: boolean; jobId: string }> {\n const scheduleExpr =\n typeof options.scheduleExpr === \"string\" && options.scheduleExpr.trim().length > 0\n ? options.scheduleExpr.trim()\n : \"17 3 * * *\";\n const agentId =\n typeof options.agentId === \"string\" && options.agentId.trim().length > 0\n ? options.agentId.trim()\n : \"main\";\n\n return ensureCronJob(jobsPath, PROCEDURAL_MINING_CRON_ID, () => ({\n id: PROCEDURAL_MINING_CRON_ID,\n agentId,\n name: \"Remnic Procedural Mining (nightly)\",\n enabled: true,\n schedule: {\n kind: \"cron\",\n expr: scheduleExpr,\n tz: options.timezone,\n },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n timeoutSeconds: 900,\n thinking: \"off\",\n message:\n \"You are OpenClaw automation. Call tool `engram.procedure_mining_run` with empty params. \" +\n \"If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool.\",\n },\n delivery: { mode: \"none\" },\n }));\n}\n\nexport async function ensureContradictionScanCron(\n jobsPath: string,\n options: {\n timezone: string;\n agentId?: string;\n scheduleExpr?: string;\n },\n): Promise<{ created: boolean; jobId: string }> {\n const scheduleExpr =\n typeof options.scheduleExpr === \"string\" && options.scheduleExpr.trim().length > 0\n ? options.scheduleExpr.trim()\n : \"37 3 * * *\";\n const agentId =\n typeof options.agentId === \"string\" && options.agentId.trim().length > 0\n ? options.agentId.trim()\n : \"main\";\n\n return ensureCronJob(jobsPath, CONTRADICTION_SCAN_CRON_ID, () => ({\n id: CONTRADICTION_SCAN_CRON_ID,\n agentId,\n name: \"Remnic Contradiction Scan (nightly)\",\n enabled: true,\n schedule: {\n kind: \"cron\",\n expr: scheduleExpr,\n tz: options.timezone,\n },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n timeoutSeconds: 900,\n thinking: \"off\",\n message:\n \"You are OpenClaw automation. Call tool `engram.contradiction_scan_run` with empty params. \" +\n \"If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool.\",\n },\n delivery: { mode: \"none\" },\n }));\n}\n\nexport async function ensurePatternReinforcementCron(\n jobsPath: string,\n options: {\n timezone: string;\n agentId?: string;\n /**\n * Cron expression. Default `\"53 4 * * 0\"` — Sunday 04:53 in the\n * configured timezone, deliberately offset from sibling crons\n * (`23 2 * * *`, `17 3 * * *`, `37 3 * * *`) so the maintenance\n * passes don't pile up on the same minute.\n */\n scheduleExpr?: string;\n },\n): Promise<{ created: boolean; jobId: string }> {\n const scheduleExpr =\n typeof options.scheduleExpr === \"string\" && options.scheduleExpr.trim().length > 0\n ? options.scheduleExpr.trim()\n : \"53 4 * * 0\";\n const agentId =\n typeof options.agentId === \"string\" && options.agentId.trim().length > 0\n ? options.agentId.trim()\n : \"main\";\n\n return ensureCronJob(jobsPath, PATTERN_REINFORCEMENT_CRON_ID, () => ({\n id: PATTERN_REINFORCEMENT_CRON_ID,\n agentId,\n name: \"Remnic Pattern Reinforcement (weekly)\",\n enabled: true,\n schedule: {\n kind: \"cron\",\n expr: scheduleExpr,\n tz: options.timezone,\n },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n timeoutSeconds: 900,\n thinking: \"off\",\n message:\n \"You are OpenClaw automation. Call tool `engram.pattern_reinforcement_run` with empty params. \" +\n \"If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool.\",\n },\n delivery: { mode: \"none\" },\n }));\n}\n\n/**\n * Register a cron job that runs the graph-edge-decay maintenance pass\n * (issue #681 PR 2/3). Default schedule is weekly on Sunday 04:13 — the\n * cadence is configurable via `scheduleExpr`. Cadence is also expressible\n * in milliseconds via `Config.graphEdgeDecayCadenceMs`; the orchestrator\n * picks the right cron expression for sub-daily, daily, or weekly cadence.\n */\nexport async function ensureGraphEdgeDecayCron(\n jobsPath: string,\n options: {\n timezone: string;\n agentId?: string;\n scheduleExpr?: string;\n },\n): Promise<{ created: boolean; jobId: string }> {\n const scheduleExpr =\n typeof options.scheduleExpr === \"string\" && options.scheduleExpr.trim().length > 0\n ? options.scheduleExpr.trim()\n : \"13 4 * * 0\";\n const agentId =\n typeof options.agentId === \"string\" && options.agentId.trim().length > 0\n ? options.agentId.trim()\n : \"main\";\n\n const scheduleLabel = graphEdgeDecayScheduleLabel(scheduleExpr);\n\n return ensureCronJob(\n jobsPath,\n GRAPH_EDGE_DECAY_CRON_ID,\n () => ({\n id: GRAPH_EDGE_DECAY_CRON_ID,\n agentId,\n // Schedule label reflects the actual cron expression (`daily` /\n // `weekly` / `custom`) so cron dashboards do not show \"weekly\"\n // when the schedule is in fact daily — Cursor review on PR #729.\n name: `Remnic Graph Edge Decay (${scheduleLabel})`,\n enabled: true,\n schedule: {\n kind: \"cron\",\n expr: scheduleExpr,\n tz: options.timezone,\n },\n sessionTarget: \"isolated\",\n wakeMode: \"now\",\n payload: {\n kind: \"agentTurn\",\n timeoutSeconds: 900,\n thinking: \"off\",\n message:\n \"You are OpenClaw automation. Call tool `engram.graph_edge_decay_run` with empty params. \" +\n \"If successful output exactly NO_REPLY. On error output one concise line. Do NOT use message tool.\",\n },\n delivery: { mode: \"none\" },\n }),\n {\n updateExisting: true,\n updateFields: [\"name\", \"schedule\"],\n },\n );\n}\n\n/**\n * Pick a cron expression that approximates a cadence in milliseconds.\n *\n * - cadence <= 1 day → daily at 04:13. Sub-daily cadence is not natively\n * expressible in 5-field cron without hour/minute step patterns; daily\n * preserves the previous conservative behavior for those values.\n * - cadence > 1 day → weekly on Sunday 04:13. Multi-day intervals like\n * 2-6 days cannot be represented accurately by a portable 5-field cron\n * expression, and daily would run more often than requested.\n *\n * Operators who need finer-grained control should set `scheduleExpr`\n * directly via `ensureGraphEdgeDecayCron`.\n */\nexport function graphEdgeDecayCadenceToCronExpr(cadenceMs: number): string {\n if (!Number.isFinite(cadenceMs) || cadenceMs <= 0) {\n return \"13 4 * * 0\";\n }\n const day = 24 * 60 * 60 * 1000;\n if (cadenceMs <= day) return \"13 4 * * *\";\n return \"13 4 * * 0\";\n}\n\n/**\n * Derive a human-friendly schedule label (\"daily\" / \"weekly\" / \"custom\")\n * for the given cron expression. Used to keep the cron job name in\n * sync with the actual schedule (Cursor review on PR #729).\n */\nfunction graphEdgeDecayScheduleLabel(scheduleExpr: string): string {\n if (scheduleExpr === \"13 4 * * *\") return \"daily\";\n if (scheduleExpr === \"13 4 * * 0\") return \"weekly\";\n return \"custom\";\n}\n"],"mappings":"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","names":["output"]}