abmind 0.0.1 → 0.1.1

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 (533) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/LICENSE +190 -0
  3. package/README.md +191 -0
  4. package/dist/cli/abmind-backfill.d.ts +3 -0
  5. package/dist/cli/abmind-backfill.d.ts.map +1 -0
  6. package/dist/cli/abmind-backfill.js +64 -0
  7. package/dist/cli/abmind-backfill.js.map +1 -0
  8. package/dist/cli/abmind-backup.d.ts +5 -0
  9. package/dist/cli/abmind-backup.d.ts.map +1 -0
  10. package/dist/cli/abmind-backup.js +44 -0
  11. package/dist/cli/abmind-backup.js.map +1 -0
  12. package/dist/cli/abmind-bundle.d.ts +7 -0
  13. package/dist/cli/abmind-bundle.d.ts.map +1 -0
  14. package/dist/cli/abmind-bundle.js +17 -0
  15. package/dist/cli/abmind-bundle.js.map +1 -0
  16. package/dist/cli/abmind-edit.d.ts +10 -0
  17. package/dist/cli/abmind-edit.d.ts.map +1 -0
  18. package/dist/cli/abmind-edit.js +160 -0
  19. package/dist/cli/abmind-edit.js.map +1 -0
  20. package/dist/cli/abmind-embed.d.ts +3 -0
  21. package/dist/cli/abmind-embed.d.ts.map +1 -0
  22. package/dist/cli/abmind-embed.js +83 -0
  23. package/dist/cli/abmind-embed.js.map +1 -0
  24. package/dist/cli/abmind-expand.d.ts +6 -0
  25. package/dist/cli/abmind-expand.d.ts.map +1 -0
  26. package/dist/cli/abmind-expand.js +100 -0
  27. package/dist/cli/abmind-expand.js.map +1 -0
  28. package/dist/cli/abmind-hook-doctor.d.ts +8 -0
  29. package/dist/cli/abmind-hook-doctor.d.ts.map +1 -0
  30. package/dist/cli/abmind-hook-doctor.js +73 -0
  31. package/dist/cli/abmind-hook-doctor.js.map +1 -0
  32. package/dist/cli/abmind-hook-recall.d.ts +16 -0
  33. package/dist/cli/abmind-hook-recall.d.ts.map +1 -0
  34. package/dist/cli/abmind-hook-recall.js +123 -0
  35. package/dist/cli/abmind-hook-recall.js.map +1 -0
  36. package/dist/cli/abmind-hook-store.d.ts +17 -0
  37. package/dist/cli/abmind-hook-store.d.ts.map +1 -0
  38. package/dist/cli/abmind-hook-store.js +115 -0
  39. package/dist/cli/abmind-hook-store.js.map +1 -0
  40. package/dist/cli/abmind-hook-wakeup.d.ts +12 -0
  41. package/dist/cli/abmind-hook-wakeup.d.ts.map +1 -0
  42. package/dist/cli/abmind-hook-wakeup.js +143 -0
  43. package/dist/cli/abmind-hook-wakeup.js.map +1 -0
  44. package/dist/cli/abmind-ingest.d.ts +7 -0
  45. package/dist/cli/abmind-ingest.d.ts.map +1 -0
  46. package/dist/cli/abmind-ingest.js +96 -0
  47. package/dist/cli/abmind-ingest.js.map +1 -0
  48. package/dist/cli/abmind-install.d.ts +10 -0
  49. package/dist/cli/abmind-install.d.ts.map +1 -0
  50. package/dist/cli/abmind-install.js +240 -0
  51. package/dist/cli/abmind-install.js.map +1 -0
  52. package/dist/cli/abmind-mcp.d.ts +3 -0
  53. package/dist/cli/abmind-mcp.d.ts.map +1 -0
  54. package/dist/cli/abmind-mcp.js +26 -0
  55. package/dist/cli/abmind-mcp.js.map +1 -0
  56. package/dist/cli/abmind-memory-stats.d.ts +3 -0
  57. package/dist/cli/abmind-memory-stats.d.ts.map +1 -0
  58. package/dist/cli/abmind-memory-stats.js +45 -0
  59. package/dist/cli/abmind-memory-stats.js.map +1 -0
  60. package/dist/cli/abmind-migrate-openclaw.d.ts +12 -0
  61. package/dist/cli/abmind-migrate-openclaw.d.ts.map +1 -0
  62. package/dist/cli/abmind-migrate-openclaw.js +247 -0
  63. package/dist/cli/abmind-migrate-openclaw.js.map +1 -0
  64. package/dist/cli/abmind-recall.d.ts +8 -0
  65. package/dist/cli/abmind-recall.d.ts.map +1 -0
  66. package/dist/cli/abmind-recall.js +94 -0
  67. package/dist/cli/abmind-recall.js.map +1 -0
  68. package/dist/cli/abmind-reset.d.ts +11 -0
  69. package/dist/cli/abmind-reset.d.ts.map +1 -0
  70. package/dist/cli/abmind-reset.js +143 -0
  71. package/dist/cli/abmind-reset.js.map +1 -0
  72. package/dist/cli/abmind-restore.d.ts +5 -0
  73. package/dist/cli/abmind-restore.d.ts.map +1 -0
  74. package/dist/cli/abmind-restore.js +64 -0
  75. package/dist/cli/abmind-restore.js.map +1 -0
  76. package/dist/cli/abmind-retro-extract.d.ts +15 -0
  77. package/dist/cli/abmind-retro-extract.d.ts.map +1 -0
  78. package/dist/cli/abmind-retro-extract.js +113 -0
  79. package/dist/cli/abmind-retro-extract.js.map +1 -0
  80. package/dist/cli/abmind-rollback.d.ts +8 -0
  81. package/dist/cli/abmind-rollback.d.ts.map +1 -0
  82. package/dist/cli/abmind-rollback.js +76 -0
  83. package/dist/cli/abmind-rollback.js.map +1 -0
  84. package/dist/cli/abmind-secrets.d.ts +12 -0
  85. package/dist/cli/abmind-secrets.d.ts.map +1 -0
  86. package/dist/cli/abmind-secrets.js +83 -0
  87. package/dist/cli/abmind-secrets.js.map +1 -0
  88. package/dist/cli/abmind-sleep-apply.d.ts +3 -0
  89. package/dist/cli/abmind-sleep-apply.d.ts.map +1 -0
  90. package/dist/cli/abmind-sleep-apply.js +50 -0
  91. package/dist/cli/abmind-sleep-apply.js.map +1 -0
  92. package/dist/cli/abmind-sleep-report.d.ts +3 -0
  93. package/dist/cli/abmind-sleep-report.d.ts.map +1 -0
  94. package/dist/cli/abmind-sleep-report.js +40 -0
  95. package/dist/cli/abmind-sleep-report.js.map +1 -0
  96. package/dist/cli/abmind-sleep-state.d.ts +3 -0
  97. package/dist/cli/abmind-sleep-state.d.ts.map +1 -0
  98. package/dist/cli/abmind-sleep-state.js +33 -0
  99. package/dist/cli/abmind-sleep-state.js.map +1 -0
  100. package/dist/cli/abmind-sleep.d.ts +14 -0
  101. package/dist/cli/abmind-sleep.d.ts.map +1 -0
  102. package/dist/cli/abmind-sleep.js +186 -0
  103. package/dist/cli/abmind-sleep.js.map +1 -0
  104. package/dist/cli/abmind-status-runtime.d.ts +9 -0
  105. package/dist/cli/abmind-status-runtime.d.ts.map +1 -0
  106. package/dist/cli/abmind-status-runtime.js +132 -0
  107. package/dist/cli/abmind-status-runtime.js.map +1 -0
  108. package/dist/cli/abmind-status.d.ts +3 -0
  109. package/dist/cli/abmind-status.d.ts.map +1 -0
  110. package/dist/cli/abmind-status.js +45 -0
  111. package/dist/cli/abmind-status.js.map +1 -0
  112. package/dist/cli/abmind-store.d.ts +54 -0
  113. package/dist/cli/abmind-store.d.ts.map +1 -0
  114. package/dist/cli/abmind-store.js +251 -0
  115. package/dist/cli/abmind-store.js.map +1 -0
  116. package/dist/cli/abmind-update.d.ts +11 -0
  117. package/dist/cli/abmind-update.d.ts.map +1 -0
  118. package/dist/cli/abmind-update.js +160 -0
  119. package/dist/cli/abmind-update.js.map +1 -0
  120. package/dist/cli/abmind-wakeup.d.ts +3 -0
  121. package/dist/cli/abmind-wakeup.d.ts.map +1 -0
  122. package/dist/cli/abmind-wakeup.js +36 -0
  123. package/dist/cli/abmind-wakeup.js.map +1 -0
  124. package/dist/cli/abmind.d.ts +10 -0
  125. package/dist/cli/abmind.d.ts.map +1 -0
  126. package/dist/cli/abmind.js +100 -0
  127. package/dist/cli/abmind.js.map +1 -0
  128. package/dist/src/abm-v2-vocab.d.ts +10 -0
  129. package/dist/src/abm-v2-vocab.d.ts.map +1 -0
  130. package/dist/src/abm-v2-vocab.js +66 -0
  131. package/dist/src/abm-v2-vocab.js.map +1 -0
  132. package/dist/src/adapters/openclaw.d.ts +66 -0
  133. package/dist/src/adapters/openclaw.d.ts.map +1 -0
  134. package/dist/src/adapters/openclaw.js +97 -0
  135. package/dist/src/adapters/openclaw.js.map +1 -0
  136. package/dist/src/backend-factory.d.ts +9 -0
  137. package/dist/src/backend-factory.d.ts.map +1 -0
  138. package/dist/src/backend-factory.js +23 -0
  139. package/dist/src/backend-factory.js.map +1 -0
  140. package/dist/src/backup.d.ts +19 -0
  141. package/dist/src/backup.d.ts.map +1 -0
  142. package/dist/src/backup.js +191 -0
  143. package/dist/src/backup.js.map +1 -0
  144. package/dist/src/brain-patterns.d.ts +20 -0
  145. package/dist/src/brain-patterns.d.ts.map +1 -0
  146. package/dist/src/brain-patterns.js +72 -0
  147. package/dist/src/brain-patterns.js.map +1 -0
  148. package/dist/src/cli-entry.d.ts +10 -0
  149. package/dist/src/cli-entry.d.ts.map +1 -0
  150. package/dist/src/cli-entry.js +29 -0
  151. package/dist/src/cli-entry.js.map +1 -0
  152. package/dist/src/cli-flags.d.ts +29 -0
  153. package/dist/src/cli-flags.d.ts.map +1 -0
  154. package/dist/src/cli-flags.js +72 -0
  155. package/dist/src/cli-flags.js.map +1 -0
  156. package/dist/src/cli-runner-raw.d.ts +21 -0
  157. package/dist/src/cli-runner-raw.d.ts.map +1 -0
  158. package/dist/src/cli-runner-raw.js +41 -0
  159. package/dist/src/cli-runner-raw.js.map +1 -0
  160. package/dist/src/cli-runner.d.ts +26 -0
  161. package/dist/src/cli-runner.d.ts.map +1 -0
  162. package/dist/src/cli-runner.js +51 -0
  163. package/dist/src/cli-runner.js.map +1 -0
  164. package/dist/src/consolidation-search.d.ts +17 -0
  165. package/dist/src/consolidation-search.d.ts.map +1 -0
  166. package/dist/src/consolidation-search.js +83 -0
  167. package/dist/src/consolidation-search.js.map +1 -0
  168. package/dist/src/context-engine.d.ts +89 -0
  169. package/dist/src/context-engine.d.ts.map +1 -0
  170. package/dist/src/context-engine.js +216 -0
  171. package/dist/src/context-engine.js.map +1 -0
  172. package/dist/src/context-tier-renderer.d.ts +79 -0
  173. package/dist/src/context-tier-renderer.d.ts.map +1 -0
  174. package/dist/src/context-tier-renderer.js +223 -0
  175. package/dist/src/context-tier-renderer.js.map +1 -0
  176. package/dist/src/contradiction-checker.d.ts +16 -0
  177. package/dist/src/contradiction-checker.d.ts.map +1 -0
  178. package/dist/src/contradiction-checker.js +40 -0
  179. package/dist/src/contradiction-checker.js.map +1 -0
  180. package/dist/src/crypto.d.ts +15 -0
  181. package/dist/src/crypto.d.ts.map +1 -0
  182. package/dist/src/crypto.js +86 -0
  183. package/dist/src/crypto.js.map +1 -0
  184. package/dist/src/deploy-lib/cleanup.d.ts +38 -0
  185. package/dist/src/deploy-lib/cleanup.d.ts.map +1 -0
  186. package/dist/src/deploy-lib/cleanup.js +71 -0
  187. package/dist/src/deploy-lib/cleanup.js.map +1 -0
  188. package/dist/src/deploy-lib/index.d.ts +20 -0
  189. package/dist/src/deploy-lib/index.d.ts.map +1 -0
  190. package/dist/src/deploy-lib/index.js +20 -0
  191. package/dist/src/deploy-lib/index.js.map +1 -0
  192. package/dist/src/deploy-lib/lock.d.ts +37 -0
  193. package/dist/src/deploy-lib/lock.d.ts.map +1 -0
  194. package/dist/src/deploy-lib/lock.js +107 -0
  195. package/dist/src/deploy-lib/lock.js.map +1 -0
  196. package/dist/src/deploy-lib/manifest.d.ts +42 -0
  197. package/dist/src/deploy-lib/manifest.d.ts.map +1 -0
  198. package/dist/src/deploy-lib/manifest.js +38 -0
  199. package/dist/src/deploy-lib/manifest.js.map +1 -0
  200. package/dist/src/deploy-lib/paths.d.ts +29 -0
  201. package/dist/src/deploy-lib/paths.d.ts.map +1 -0
  202. package/dist/src/deploy-lib/paths.js +40 -0
  203. package/dist/src/deploy-lib/paths.js.map +1 -0
  204. package/dist/src/deploy-lib/releases.d.ts +52 -0
  205. package/dist/src/deploy-lib/releases.d.ts.map +1 -0
  206. package/dist/src/deploy-lib/releases.js +119 -0
  207. package/dist/src/deploy-lib/releases.js.map +1 -0
  208. package/dist/src/deploy-lib/safe-copy.d.ts +16 -0
  209. package/dist/src/deploy-lib/safe-copy.d.ts.map +1 -0
  210. package/dist/src/deploy-lib/safe-copy.js +32 -0
  211. package/dist/src/deploy-lib/safe-copy.js.map +1 -0
  212. package/dist/src/embedding-health.d.ts +12 -0
  213. package/dist/src/embedding-health.d.ts.map +1 -0
  214. package/dist/src/embedding-health.js +18 -0
  215. package/dist/src/embedding-health.js.map +1 -0
  216. package/dist/src/embedding-provider.d.ts +48 -0
  217. package/dist/src/embedding-provider.d.ts.map +1 -0
  218. package/dist/src/embedding-provider.js +145 -0
  219. package/dist/src/embedding-provider.js.map +1 -0
  220. package/dist/src/embedding-quantize.d.ts +11 -0
  221. package/dist/src/embedding-quantize.d.ts.map +1 -0
  222. package/dist/src/embedding-quantize.js +38 -0
  223. package/dist/src/embedding-quantize.js.map +1 -0
  224. package/dist/src/emotion-arc.d.ts +17 -0
  225. package/dist/src/emotion-arc.d.ts.map +1 -0
  226. package/dist/src/emotion-arc.js +62 -0
  227. package/dist/src/emotion-arc.js.map +1 -0
  228. package/dist/src/emotion-tagger.d.ts +8 -0
  229. package/dist/src/emotion-tagger.d.ts.map +1 -0
  230. package/dist/src/emotion-tagger.js +45 -0
  231. package/dist/src/emotion-tagger.js.map +1 -0
  232. package/dist/src/emotion-utils.d.ts +13 -0
  233. package/dist/src/emotion-utils.d.ts.map +1 -0
  234. package/dist/src/emotion-utils.js +76 -0
  235. package/dist/src/emotion-utils.js.map +1 -0
  236. package/dist/src/entity-graph.d.ts +33 -0
  237. package/dist/src/entity-graph.d.ts.map +1 -0
  238. package/dist/src/entity-graph.js +42 -0
  239. package/dist/src/entity-graph.js.map +1 -0
  240. package/dist/src/env-schema.d.ts +46 -0
  241. package/dist/src/env-schema.d.ts.map +1 -0
  242. package/dist/src/env-schema.js +93 -0
  243. package/dist/src/env-schema.js.map +1 -0
  244. package/dist/src/hook-helpers.d.ts +16 -0
  245. package/dist/src/hook-helpers.d.ts.map +1 -0
  246. package/dist/src/hook-helpers.js +64 -0
  247. package/dist/src/hook-helpers.js.map +1 -0
  248. package/dist/src/imemory-system.d.ts +138 -0
  249. package/dist/src/imemory-system.d.ts.map +1 -0
  250. package/dist/src/imemory-system.js +40 -0
  251. package/dist/src/imemory-system.js.map +1 -0
  252. package/dist/src/importance-flagger.d.ts +8 -0
  253. package/dist/src/importance-flagger.d.ts.map +1 -0
  254. package/dist/src/importance-flagger.js +24 -0
  255. package/dist/src/importance-flagger.js.map +1 -0
  256. package/dist/src/index.d.ts +79 -0
  257. package/dist/src/index.d.ts.map +1 -0
  258. package/dist/src/index.js +79 -0
  259. package/dist/src/index.js.map +1 -0
  260. package/dist/src/ingest-pipeline.d.ts +32 -0
  261. package/dist/src/ingest-pipeline.d.ts.map +1 -0
  262. package/dist/src/ingest-pipeline.js +58 -0
  263. package/dist/src/ingest-pipeline.js.map +1 -0
  264. package/dist/src/injection-scanner.d.ts +16 -0
  265. package/dist/src/injection-scanner.d.ts.map +1 -0
  266. package/dist/src/injection-scanner.js +182 -0
  267. package/dist/src/injection-scanner.js.map +1 -0
  268. package/dist/src/local-time.d.ts +12 -0
  269. package/dist/src/local-time.d.ts.map +1 -0
  270. package/dist/src/local-time.js +23 -0
  271. package/dist/src/local-time.js.map +1 -0
  272. package/dist/src/maintenance-service.d.ts +46 -0
  273. package/dist/src/maintenance-service.d.ts.map +1 -0
  274. package/dist/src/maintenance-service.js +280 -0
  275. package/dist/src/maintenance-service.js.map +1 -0
  276. package/dist/src/mcp-server.d.ts +6 -0
  277. package/dist/src/mcp-server.d.ts.map +1 -0
  278. package/dist/src/mcp-server.js +50 -0
  279. package/dist/src/mcp-server.js.map +1 -0
  280. package/dist/src/media-sanitizer.d.ts +11 -0
  281. package/dist/src/media-sanitizer.d.ts.map +1 -0
  282. package/dist/src/media-sanitizer.js +56 -0
  283. package/dist/src/media-sanitizer.js.map +1 -0
  284. package/dist/src/mem-config-env.d.ts +18 -0
  285. package/dist/src/mem-config-env.d.ts.map +1 -0
  286. package/dist/src/mem-config-env.js +43 -0
  287. package/dist/src/mem-config-env.js.map +1 -0
  288. package/dist/src/mem-env.d.ts +5 -0
  289. package/dist/src/mem-env.d.ts.map +1 -0
  290. package/dist/src/mem-env.js +19 -0
  291. package/dist/src/mem-env.js.map +1 -0
  292. package/dist/src/mem-logger.d.ts +20 -0
  293. package/dist/src/mem-logger.d.ts.map +1 -0
  294. package/dist/src/mem-logger.js +24 -0
  295. package/dist/src/mem-logger.js.map +1 -0
  296. package/dist/src/mem-paths.d.ts +20 -0
  297. package/dist/src/mem-paths.d.ts.map +1 -0
  298. package/dist/src/mem-paths.js +43 -0
  299. package/dist/src/mem-paths.js.map +1 -0
  300. package/dist/src/mem-types.d.ts +157 -0
  301. package/dist/src/mem-types.d.ts.map +1 -0
  302. package/dist/src/mem-types.js +2 -0
  303. package/dist/src/mem-types.js.map +1 -0
  304. package/dist/src/memory-backend.d.ts +65 -0
  305. package/dist/src/memory-backend.d.ts.map +1 -0
  306. package/dist/src/memory-backend.js +43 -0
  307. package/dist/src/memory-backend.js.map +1 -0
  308. package/dist/src/memory-compressor.d.ts +17 -0
  309. package/dist/src/memory-compressor.d.ts.map +1 -0
  310. package/dist/src/memory-compressor.js +147 -0
  311. package/dist/src/memory-compressor.js.map +1 -0
  312. package/dist/src/memory-config.d.ts +30 -0
  313. package/dist/src/memory-config.d.ts.map +1 -0
  314. package/dist/src/memory-config.js +62 -0
  315. package/dist/src/memory-config.js.map +1 -0
  316. package/dist/src/memory-db.d.ts +7 -0
  317. package/dist/src/memory-db.d.ts.map +1 -0
  318. package/dist/src/memory-db.js +173 -0
  319. package/dist/src/memory-db.js.map +1 -0
  320. package/dist/src/memory-editor.d.ts +32 -0
  321. package/dist/src/memory-editor.d.ts.map +1 -0
  322. package/dist/src/memory-editor.js +339 -0
  323. package/dist/src/memory-editor.js.map +1 -0
  324. package/dist/src/memory-index.d.ts +91 -0
  325. package/dist/src/memory-index.d.ts.map +1 -0
  326. package/dist/src/memory-index.js +310 -0
  327. package/dist/src/memory-index.js.map +1 -0
  328. package/dist/src/memory-ipc-client.d.ts +22 -0
  329. package/dist/src/memory-ipc-client.d.ts.map +1 -0
  330. package/dist/src/memory-ipc-client.js +75 -0
  331. package/dist/src/memory-ipc-client.js.map +1 -0
  332. package/dist/src/memory-ipc-server.d.ts +17 -0
  333. package/dist/src/memory-ipc-server.d.ts.map +1 -0
  334. package/dist/src/memory-ipc-server.js +86 -0
  335. package/dist/src/memory-ipc-server.js.map +1 -0
  336. package/dist/src/memory-manager.d.ts +155 -0
  337. package/dist/src/memory-manager.d.ts.map +1 -0
  338. package/dist/src/memory-manager.js +463 -0
  339. package/dist/src/memory-manager.js.map +1 -0
  340. package/dist/src/memory-renderer.d.ts +15 -0
  341. package/dist/src/memory-renderer.d.ts.map +1 -0
  342. package/dist/src/memory-renderer.js +126 -0
  343. package/dist/src/memory-renderer.js.map +1 -0
  344. package/dist/src/message-store.d.ts +42 -0
  345. package/dist/src/message-store.d.ts.map +1 -0
  346. package/dist/src/message-store.js +145 -0
  347. package/dist/src/message-store.js.map +1 -0
  348. package/dist/src/mmr.d.ts +17 -0
  349. package/dist/src/mmr.d.ts.map +1 -0
  350. package/dist/src/mmr.js +48 -0
  351. package/dist/src/mmr.js.map +1 -0
  352. package/dist/src/ollama-embed.d.ts +46 -0
  353. package/dist/src/ollama-embed.d.ts.map +1 -0
  354. package/dist/src/ollama-embed.js +139 -0
  355. package/dist/src/ollama-embed.js.map +1 -0
  356. package/dist/src/openclaw-plugin/engine.d.ts +87 -0
  357. package/dist/src/openclaw-plugin/engine.d.ts.map +1 -0
  358. package/dist/src/openclaw-plugin/engine.js +121 -0
  359. package/dist/src/openclaw-plugin/engine.js.map +1 -0
  360. package/dist/src/openclaw-plugin/hooks.d.ts +41 -0
  361. package/dist/src/openclaw-plugin/hooks.d.ts.map +1 -0
  362. package/dist/src/openclaw-plugin/hooks.js +155 -0
  363. package/dist/src/openclaw-plugin/hooks.js.map +1 -0
  364. package/dist/src/openclaw-plugin/index.d.ts +20 -0
  365. package/dist/src/openclaw-plugin/index.d.ts.map +1 -0
  366. package/dist/src/openclaw-plugin/index.js +151 -0
  367. package/dist/src/openclaw-plugin/index.js.map +1 -0
  368. package/dist/src/openclaw-plugin/message-adapter.d.ts +13 -0
  369. package/dist/src/openclaw-plugin/message-adapter.d.ts.map +1 -0
  370. package/dist/src/openclaw-plugin/message-adapter.js +49 -0
  371. package/dist/src/openclaw-plugin/message-adapter.js.map +1 -0
  372. package/dist/src/openclaw-plugin/prompt-builder.d.ts +17 -0
  373. package/dist/src/openclaw-plugin/prompt-builder.d.ts.map +1 -0
  374. package/dist/src/openclaw-plugin/prompt-builder.js +22 -0
  375. package/dist/src/openclaw-plugin/prompt-builder.js.map +1 -0
  376. package/dist/src/openclaw-plugin/public-artifacts.d.ts +20 -0
  377. package/dist/src/openclaw-plugin/public-artifacts.d.ts.map +1 -0
  378. package/dist/src/openclaw-plugin/public-artifacts.js +59 -0
  379. package/dist/src/openclaw-plugin/public-artifacts.js.map +1 -0
  380. package/dist/src/openclaw-plugin/runtime-adapter.d.ts +26 -0
  381. package/dist/src/openclaw-plugin/runtime-adapter.d.ts.map +1 -0
  382. package/dist/src/openclaw-plugin/runtime-adapter.js +161 -0
  383. package/dist/src/openclaw-plugin/runtime-adapter.js.map +1 -0
  384. package/dist/src/openclaw-plugin/session-mapper.d.ts +11 -0
  385. package/dist/src/openclaw-plugin/session-mapper.d.ts.map +1 -0
  386. package/dist/src/openclaw-plugin/session-mapper.js +17 -0
  387. package/dist/src/openclaw-plugin/session-mapper.js.map +1 -0
  388. package/dist/src/openclaw-plugin/tools.d.ts +32 -0
  389. package/dist/src/openclaw-plugin/tools.d.ts.map +1 -0
  390. package/dist/src/openclaw-plugin/tools.js +131 -0
  391. package/dist/src/openclaw-plugin/tools.js.map +1 -0
  392. package/dist/src/openclaw-plugin/types.d.ts +35 -0
  393. package/dist/src/openclaw-plugin/types.d.ts.map +1 -0
  394. package/dist/src/openclaw-plugin/types.js +2 -0
  395. package/dist/src/openclaw-plugin/types.js.map +1 -0
  396. package/dist/src/query-tokenizer.d.ts +33 -0
  397. package/dist/src/query-tokenizer.d.ts.map +1 -0
  398. package/dist/src/query-tokenizer.js +84 -0
  399. package/dist/src/query-tokenizer.js.map +1 -0
  400. package/dist/src/recall-benchmark.d.ts +13 -0
  401. package/dist/src/recall-benchmark.d.ts.map +1 -0
  402. package/dist/src/recall-benchmark.js +256 -0
  403. package/dist/src/recall-benchmark.js.map +1 -0
  404. package/dist/src/recall-engine.d.ts +81 -0
  405. package/dist/src/recall-engine.d.ts.map +1 -0
  406. package/dist/src/recall-engine.js +387 -0
  407. package/dist/src/recall-engine.js.map +1 -0
  408. package/dist/src/redact-secrets.d.ts +20 -0
  409. package/dist/src/redact-secrets.d.ts.map +1 -0
  410. package/dist/src/redact-secrets.js +42 -0
  411. package/dist/src/redact-secrets.js.map +1 -0
  412. package/dist/src/runtime-store.d.ts +47 -0
  413. package/dist/src/runtime-store.d.ts.map +1 -0
  414. package/dist/src/runtime-store.js +78 -0
  415. package/dist/src/runtime-store.js.map +1 -0
  416. package/dist/src/session-context.d.ts +10 -0
  417. package/dist/src/session-context.d.ts.map +1 -0
  418. package/dist/src/session-context.js +83 -0
  419. package/dist/src/session-context.js.map +1 -0
  420. package/dist/src/session-memory.d.ts +8 -0
  421. package/dist/src/session-memory.d.ts.map +1 -0
  422. package/dist/src/session-memory.js +45 -0
  423. package/dist/src/session-memory.js.map +1 -0
  424. package/dist/src/signature-generator.d.ts +15 -0
  425. package/dist/src/signature-generator.d.ts.map +1 -0
  426. package/dist/src/signature-generator.js +78 -0
  427. package/dist/src/signature-generator.js.map +1 -0
  428. package/dist/src/sleep/basic.d.ts +48 -0
  429. package/dist/src/sleep/basic.d.ts.map +1 -0
  430. package/dist/src/sleep/basic.js +147 -0
  431. package/dist/src/sleep/basic.js.map +1 -0
  432. package/dist/src/sleep/levels.d.ts +20 -0
  433. package/dist/src/sleep/levels.d.ts.map +1 -0
  434. package/dist/src/sleep/levels.js +11 -0
  435. package/dist/src/sleep/levels.js.map +1 -0
  436. package/dist/src/sleep/native.d.ts +41 -0
  437. package/dist/src/sleep/native.d.ts.map +1 -0
  438. package/dist/src/sleep/native.js +112 -0
  439. package/dist/src/sleep/native.js.map +1 -0
  440. package/dist/src/sleep/orchestrator.d.ts +64 -0
  441. package/dist/src/sleep/orchestrator.d.ts.map +1 -0
  442. package/dist/src/sleep/orchestrator.js +960 -0
  443. package/dist/src/sleep/orchestrator.js.map +1 -0
  444. package/dist/src/sleep/runtime.d.ts +15 -0
  445. package/dist/src/sleep/runtime.d.ts.map +1 -0
  446. package/dist/src/sleep/runtime.js +2 -0
  447. package/dist/src/sleep/runtime.js.map +1 -0
  448. package/dist/src/sleep/sleep-daily-summary.d.ts +64 -0
  449. package/dist/src/sleep/sleep-daily-summary.d.ts.map +1 -0
  450. package/dist/src/sleep/sleep-daily-summary.js +273 -0
  451. package/dist/src/sleep/sleep-daily-summary.js.map +1 -0
  452. package/dist/src/sleep/sleep-extract-daily.d.ts +12 -0
  453. package/dist/src/sleep/sleep-extract-daily.d.ts.map +1 -0
  454. package/dist/src/sleep/sleep-extract-daily.js +55 -0
  455. package/dist/src/sleep/sleep-extract-daily.js.map +1 -0
  456. package/dist/src/sleep/sleep-prompt-loader.d.ts +27 -0
  457. package/dist/src/sleep/sleep-prompt-loader.d.ts.map +1 -0
  458. package/dist/src/sleep/sleep-prompt-loader.js +116 -0
  459. package/dist/src/sleep/sleep-prompt-loader.js.map +1 -0
  460. package/dist/src/sleep/test-harness.d.ts +67 -0
  461. package/dist/src/sleep/test-harness.d.ts.map +1 -0
  462. package/dist/src/sleep/test-harness.js +121 -0
  463. package/dist/src/sleep/test-harness.js.map +1 -0
  464. package/dist/src/sleep/trigger.d.ts +3 -0
  465. package/dist/src/sleep/trigger.d.ts.map +1 -0
  466. package/dist/src/sleep/trigger.js +66 -0
  467. package/dist/src/sleep/trigger.js.map +1 -0
  468. package/dist/src/sleep-data-access.d.ts +54 -0
  469. package/dist/src/sleep-data-access.d.ts.map +1 -0
  470. package/dist/src/sleep-data-access.js +176 -0
  471. package/dist/src/sleep-data-access.js.map +1 -0
  472. package/dist/src/sleep-pipeline.d.ts +9 -0
  473. package/dist/src/sleep-pipeline.d.ts.map +1 -0
  474. package/dist/src/sleep-pipeline.js +8 -0
  475. package/dist/src/sleep-pipeline.js.map +1 -0
  476. package/dist/src/sleep-state-gatherer.d.ts +74 -0
  477. package/dist/src/sleep-state-gatherer.d.ts.map +1 -0
  478. package/dist/src/sleep-state-gatherer.js +275 -0
  479. package/dist/src/sleep-state-gatherer.js.map +1 -0
  480. package/dist/src/sqlite-backend.d.ts +23 -0
  481. package/dist/src/sqlite-backend.d.ts.map +1 -0
  482. package/dist/src/sqlite-backend.js +48 -0
  483. package/dist/src/sqlite-backend.js.map +1 -0
  484. package/dist/src/test-helpers.d.ts +3 -0
  485. package/dist/src/test-helpers.d.ts.map +1 -0
  486. package/dist/src/test-helpers.js +5 -0
  487. package/dist/src/test-helpers.js.map +1 -0
  488. package/dist/src/tier-llm-refinement.d.ts +35 -0
  489. package/dist/src/tier-llm-refinement.d.ts.map +1 -0
  490. package/dist/src/tier-llm-refinement.js +131 -0
  491. package/dist/src/tier-llm-refinement.js.map +1 -0
  492. package/dist/src/timeline-builder.d.ts +45 -0
  493. package/dist/src/timeline-builder.d.ts.map +1 -0
  494. package/dist/src/timeline-builder.js +147 -0
  495. package/dist/src/timeline-builder.js.map +1 -0
  496. package/dist/src/trigram-search.d.ts +28 -0
  497. package/dist/src/trigram-search.d.ts.map +1 -0
  498. package/dist/src/trigram-search.js +192 -0
  499. package/dist/src/trigram-search.js.map +1 -0
  500. package/dist/src/turn-classifier.d.ts +29 -0
  501. package/dist/src/turn-classifier.d.ts.map +1 -0
  502. package/dist/src/turn-classifier.js +186 -0
  503. package/dist/src/turn-classifier.js.map +1 -0
  504. package/dist/src/user-utils.d.ts +6 -0
  505. package/dist/src/user-utils.d.ts.map +1 -0
  506. package/dist/src/user-utils.js +27 -0
  507. package/dist/src/user-utils.js.map +1 -0
  508. package/dist/src/wake-up-builder.d.ts +12 -0
  509. package/dist/src/wake-up-builder.d.ts.map +1 -0
  510. package/dist/src/wake-up-builder.js +45 -0
  511. package/dist/src/wake-up-builder.js.map +1 -0
  512. package/dist/src/wake-up-renderer.d.ts +29 -0
  513. package/dist/src/wake-up-renderer.d.ts.map +1 -0
  514. package/dist/src/wake-up-renderer.js +182 -0
  515. package/dist/src/wake-up-renderer.js.map +1 -0
  516. package/openclaw.plugin.json +72 -0
  517. package/package.json +92 -5
  518. package/prompts/sleep/01-gc-noise.md +28 -0
  519. package/prompts/sleep/02-daily-summary.md +3 -0
  520. package/prompts/sleep/03-extract-from-daily.md +3 -0
  521. package/prompts/sleep/04-retrospective.md +30 -0
  522. package/prompts/sleep/06-feedback.md +17 -0
  523. package/prompts/sleep/07-topic-assignment.md +23 -0
  524. package/prompts/sleep/08-core-promotion.md +26 -0
  525. package/prompts/sleep/09-merge.md +19 -0
  526. package/prompts/sleep/10-translation.md +17 -0
  527. package/prompts/sleep/11-skill-review.md +12 -0
  528. package/prompts/sleep/12-core-knowledge.md +14 -0
  529. package/prompts/sleep/13-consolidation.md +11 -0
  530. package/prompts/sleep/14-emotion-context.md +17 -0
  531. package/prompts/sleep/15-contradiction-and-graph.md +48 -0
  532. package/prompts/sleep/16-rem-synthesis.md +31 -0
  533. package/prompts/sleep/basic.md +12 -0
@@ -0,0 +1,960 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * abmind sleep orchestrator — library function for overnight memory maintenance.
4
+ *
5
+ * Called via runSleepCycle({ runtime, level, ... }). Gathers system state,
6
+ * runs through a pipeline of prompt-driven steps (gc-noise, daily-summary,
7
+ * extract-from-daily, retrospective, etc.), persists audit log, returns result.
8
+ *
9
+ * Library-only — no CLI entry point here. Standalone entry lives in
10
+ * cli/abmind-sleep.ts.
11
+ *
12
+ * Flags (passed via RunOpts.flags):
13
+ * --dry-run Gather state + build prompts, print to stdout, skip LLM calls
14
+ * --verbose Detailed logging at each orchestration step
15
+ * --force Run housekeeping even if no messages since last sleep
16
+ */
17
+ import { localISO } from "../local-time.js";
18
+ import { getAbmindEnv } from "../env-schema.js";
19
+ import { join, basename } from "node:path";
20
+ import { appendFileSync, mkdirSync, existsSync, readdirSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
21
+ import { MemoryManager } from "../memory-manager.js";
22
+ import { loadMemoryConfig } from "../memory-config.js";
23
+ import { SleepStateGatherer } from "../sleep-state-gatherer.js";
24
+ import { loadSleepSteps, buildSleepVars, substituteVars } from "../sleep-pipeline.js";
25
+ import { buildDailySummary, writeDailyFile, LLMUnavailableError } from "../sleep-pipeline.js";
26
+ import { extractFromDaily } from "../sleep-pipeline.js";
27
+ import { logInfo, logWarn, logError } from "../mem-logger.js";
28
+ import { redactSecrets } from "../redact-secrets.js";
29
+ import { localDate } from "../local-time.js";
30
+ import { parseLevel, DEFAULT_LEVEL } from "./levels.js";
31
+ const TAG = "abmind-sleep";
32
+ /** Format a timestamp as YYYYMMDD (for lock file names). */
33
+ function toDateStr(ts) {
34
+ const d = new Date(ts);
35
+ return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}`;
36
+ }
37
+ /** Format a timestamp as YYYY-MM-DD (for daily file paths). */
38
+ function toIsoDate(ts) {
39
+ const d = new Date(ts);
40
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
41
+ }
42
+ /** Steps whose failure blocks watermark advance. Public so tests can derive reject targets. */
43
+ export const ESSENTIAL_STEPS = new Set(["daily-summary", "extract-from-daily", "retrospective"]);
44
+ const CATCHUP_MAX_AGE_DAYS = 3;
45
+ /** Thrown by runSleepCycle when memory layer fails to initialize. */
46
+ export class SleepInitError extends Error {
47
+ constructor(message) { super(message); this.name = "SleepInitError"; }
48
+ }
49
+ /** Thrown by runSleepCycle when the wall-clock timeout expires before completion. */
50
+ export class SleepTimeoutError extends Error {
51
+ constructor(message) { super(message); this.name = "SleepTimeoutError"; }
52
+ }
53
+ export function parseArgs(argv) {
54
+ const args = argv.slice(2);
55
+ const parsed = { dryRun: false, verbose: false, force: false };
56
+ for (const arg of args) {
57
+ switch (arg) {
58
+ case "--dry-run":
59
+ parsed.dryRun = true;
60
+ break;
61
+ case "--verbose":
62
+ parsed.verbose = true;
63
+ break;
64
+ case "--force":
65
+ parsed.force = true;
66
+ break;
67
+ }
68
+ }
69
+ return parsed;
70
+ }
71
+ // getPrimaryUserId moved to SleepDataAccess in memory package
72
+ function readStateFile(path) {
73
+ try {
74
+ if (!existsSync(path))
75
+ return null;
76
+ const raw = JSON.parse(readFileSync(path, "utf-8"));
77
+ if (typeof raw !== "object" || raw === null || !raw.steps)
78
+ return null;
79
+ // Backfill defaults for legacy lock files
80
+ if (!raw.status)
81
+ raw.status = "ongoing";
82
+ if (raw.llmCalls == null)
83
+ raw.llmCalls = 0;
84
+ return raw;
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ }
90
+ function writeStateFile(path, state) {
91
+ writeFileSync(path, JSON.stringify(state, null, 2));
92
+ }
93
+ // ── Wired pre-tasks (delegated to abmind MaintenanceService) ────────────────
94
+ async function runWiredPreTasks(sleepData, memoryDir, memory) {
95
+ const r = await memory.maintenance.runPreSleepTasks(memory, sleepData);
96
+ // Bridge-side: log rotation (not memory's concern)
97
+ let logsDeleted = 0;
98
+ try {
99
+ const logsDir = join(memoryDir, "..", "logs");
100
+ if (existsSync(logsDir)) {
101
+ const cutoff = Date.now() - 7 * 86400000;
102
+ for (const f of readdirSync(logsDir)) {
103
+ if (!f.startsWith("bridge-") || !f.endsWith(".log"))
104
+ continue;
105
+ const match = f.match(/bridge-(\d{4}-\d{2}-\d{2})\.log/);
106
+ if (match && new Date(match[1]).getTime() < cutoff) {
107
+ unlinkSync(join(logsDir, f));
108
+ logsDeleted++;
109
+ }
110
+ }
111
+ }
112
+ }
113
+ catch (err) {
114
+ logWarn(TAG, `[WIRED] log rotation: ${err instanceof Error ? err.message : String(err)}`);
115
+ }
116
+ return { purged: r.purged, deduped: r.deduped, embedded: r.embedded, anomaliesFixed: r.anomaliesFixed, walOk: r.walOk, ftsOk: r.ftsOk, logsDeleted };
117
+ }
118
+ function formatWiredResults(r) {
119
+ const parts = [];
120
+ if (r.purged > 0)
121
+ parts.push(`${r.purged} garbage purged`);
122
+ if (r.deduped > 0)
123
+ parts.push(`${r.deduped} dupes deleted`);
124
+ parts.push(`WAL ${r.walOk ? "ok" : "FAILED"}`);
125
+ parts.push(`FTS ${r.ftsOk ? "ok" : "FAILED"}`);
126
+ if (r.embedded > 0)
127
+ parts.push(`${r.embedded} embedded`);
128
+ if (r.anomaliesFixed > 0)
129
+ parts.push(`${r.anomaliesFixed} anomalies fixed`);
130
+ if (r.logsDeleted > 0)
131
+ parts.push(`${r.logsDeleted} old logs deleted`);
132
+ return parts.length > 0 ? parts.join(", ") : "nothing to do";
133
+ }
134
+ // ── Transport ───────────────────────────────────────────────────────────────
135
+ // (SleepRuntime is imported at the top of the file.)
136
+ const MAX_RETRIES = 3;
137
+ /** Budget tracker — shared across all sendWithRetry calls in a sleep cycle. */
138
+ class LlmBudget {
139
+ state;
140
+ statePath;
141
+ exhausted = false;
142
+ constructor(state, statePath) {
143
+ this.state = state;
144
+ this.statePath = statePath;
145
+ }
146
+ /** Increment counter, return false if budget exhausted. */
147
+ consume() {
148
+ this.state.llmCalls = (this.state.llmCalls ?? 0) + 1;
149
+ writeStateFile(this.statePath, this.state);
150
+ if (this.state.llmCalls > getAbmindEnv().sleepMaxLlmCalls) {
151
+ this.exhausted = true;
152
+ return false;
153
+ }
154
+ return true;
155
+ }
156
+ get calls() { return this.state.llmCalls ?? 0; }
157
+ }
158
+ async function sendWithRetry(runtime, prompt, stepName, _verbose, budget) {
159
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
160
+ if (budget && !budget.consume()) {
161
+ logWarn(TAG, `[BUDGET] LLM call limit (${getAbmindEnv().sleepMaxLlmCalls}) reached at step ${stepName} — suspending`);
162
+ return null;
163
+ }
164
+ try {
165
+ return await runtime.complete(prompt);
166
+ }
167
+ catch (err) {
168
+ const msg = err instanceof Error ? err.message : String(err);
169
+ logWarn(TAG, `Step ${stepName} attempt ${attempt}/${MAX_RETRIES} failed: ${msg}`);
170
+ if (attempt === MAX_RETRIES) {
171
+ logError(TAG, `Step ${stepName} failed after ${MAX_RETRIES} attempts, skipping`);
172
+ return null;
173
+ }
174
+ }
175
+ }
176
+ return null;
177
+ }
178
+ // ── Audit trail helpers ─────────────────────────────────────────────────────
179
+ function buildSnapshotSummary(snapshot) {
180
+ return [
181
+ `Working dirs: ${snapshot.workingDirs.length}`,
182
+ `Messages: ${snapshot.dbStats.messageCount}`,
183
+ `Embeddings: ${snapshot.dbStats.embeddingCount}`,
184
+ `Extracted memories: ${snapshot.dbStats.extractedMemoryCount}`,
185
+ `Disk: ${(snapshot.diskUsageBytes / 1024 / 1024).toFixed(1)} MB / ${(snapshot.diskBudgetBytes / 1024 / 1024).toFixed(0)} MB`,
186
+ `Topics: ${snapshot.topicFiles.length}`,
187
+ `FTS5: messages=${snapshot.fts5Health.messages_fts}, extracted=${snapshot.fts5Health.extracted_memories_fts}, original=${snapshot.fts5Health.extracted_memories_original_fts}`,
188
+ ].join(", ");
189
+ }
190
+ /**
191
+ * Parse outcome counts from the subagent's free-form text response.
192
+ *
193
+ * The subagent response is unstructured text, so we use best-effort regex
194
+ * matching for common patterns like "consolidated 3 files", "pruned 42
195
+ * messages", etc. Returns 0 for any count that can't be parsed.
196
+ */
197
+ function parseOutcomesFromResponse(response) {
198
+ const defaults = {
199
+ filesConsolidated: 0,
200
+ messagesPruned: 0,
201
+ embeddingsRemoved: 0,
202
+ sessionsCleaned: 0,
203
+ topicsMerged: 0,
204
+ topicsDeleted: 0,
205
+ };
206
+ if (!response)
207
+ return defaults;
208
+ const text = response.toLowerCase();
209
+ // Each pattern array contains regexes to try in order for a given outcome.
210
+ // We take the first match found. Patterns cover both "verb N noun" and
211
+ // "N noun verb" orderings that an LLM might produce.
212
+ const patterns = [
213
+ {
214
+ key: "filesConsolidated",
215
+ regexes: [
216
+ /consolidat\w*\s+(\d+)\s+(?:file|dir|working)/i,
217
+ /(\d+)\s+(?:file|dir|working\s*dir)\w*\s+consolidat/i,
218
+ /files?\s+consolidated\s*:\s*(\d+)/i,
219
+ ],
220
+ },
221
+ {
222
+ key: "messagesPruned",
223
+ regexes: [
224
+ /prun\w*\s+(\d+)\s+message/i,
225
+ /(\d+)\s+message\w*\s+prun/i,
226
+ /(?:delet|remov)\w*\s+(\d+)\s+message/i,
227
+ /(\d+)\s+message\w*\s+(?:delet|remov)/i,
228
+ /messages?\s+pruned\s*:\s*(\d+)/i,
229
+ ],
230
+ },
231
+ {
232
+ key: "embeddingsRemoved",
233
+ regexes: [
234
+ /(?:remov|delet|clean)\w*\s+(\d+)\s+embedding/i,
235
+ /(\d+)\s+embedding\w*\s+(?:remov|delet|clean)/i,
236
+ /embeddings?\s+removed\s*:\s*(\d+)/i,
237
+ ],
238
+ },
239
+ {
240
+ key: "sessionsCleaned",
241
+ regexes: [
242
+ /(?:clean|delet|remov)\w*\s+(\d+)\s+session/i,
243
+ /(\d+)\s+session\w*\s+(?:clean|delet|remov)/i,
244
+ /sessions?\s+cleaned\s*:\s*(\d+)/i,
245
+ ],
246
+ },
247
+ {
248
+ key: "topicsMerged",
249
+ regexes: [
250
+ /merg\w*\s+(\d+)\s+topic/i,
251
+ /(\d+)\s+topic\w*\s+merg/i,
252
+ /topics?\s+merged\s*:\s*(\d+)/i,
253
+ ],
254
+ },
255
+ {
256
+ key: "topicsDeleted",
257
+ regexes: [
258
+ /delet\w*\s+(\d+)\s+topic/i,
259
+ /(\d+)\s+topic\w*\s+delet/i,
260
+ /topics?\s+deleted\s*:\s*(\d+)/i,
261
+ ],
262
+ },
263
+ ];
264
+ for (const { key, regexes } of patterns) {
265
+ for (const regex of regexes) {
266
+ const match = text.match(regex);
267
+ if (match?.[1]) {
268
+ const parsed = parseInt(match[1], 10);
269
+ if (!isNaN(parsed) && parsed >= 0) {
270
+ defaults[key] = parsed;
271
+ break;
272
+ }
273
+ }
274
+ }
275
+ }
276
+ return defaults;
277
+ }
278
+ function writeAuditLog(memoryDir, entry) {
279
+ const sleepDir = join(memoryDir, "sleep");
280
+ mkdirSync(sleepDir, { recursive: true });
281
+ const suffix = [
282
+ ``,
283
+ `---`,
284
+ ``,
285
+ `## CLI Wrapper`,
286
+ ``,
287
+ `**Timestamp:** ${entry.timestamp}`,
288
+ `**Model:** ${entry.model}`,
289
+ ``,
290
+ `### State Snapshot`,
291
+ `${entry.stateSnapshotSummary}`,
292
+ ``,
293
+ `### Outcomes`,
294
+ `- Files consolidated: ${entry.outcomes.filesConsolidated}`,
295
+ `- Messages pruned: ${entry.outcomes.messagesPruned}`,
296
+ `- Embeddings removed: ${entry.outcomes.embeddingsRemoved}`,
297
+ `- Sessions cleaned: ${entry.outcomes.sessionsCleaned}`,
298
+ `- Topics merged: ${entry.outcomes.topicsMerged}`,
299
+ `- Topics deleted: ${entry.outcomes.topicsDeleted}`,
300
+ entry.error ? `\n### Error\n${entry.error}` : "",
301
+ ].join("\n");
302
+ // Find the subagent's audit file and append to it
303
+ const today = localDate().replace(/-/g, "");
304
+ try {
305
+ const files = readdirSync(sleepDir)
306
+ .filter(f => f.startsWith(`sleep_${today}`) && f.endsWith(".md"))
307
+ .sort();
308
+ if (files.length > 0) {
309
+ const target = join(sleepDir, files[files.length - 1]);
310
+ const existingLines = readFileSync(target, "utf-8").split("\n").length;
311
+ if (existingLines < 50) {
312
+ logWarn(TAG, `Sleep audit suspiciously short (${existingLines} lines) — subagent may have truncated`);
313
+ }
314
+ appendFileSync(target, redactSecrets(suffix), "utf-8");
315
+ return;
316
+ }
317
+ }
318
+ catch { /* fall through to standalone */ }
319
+ // Fallback: no subagent file found — write standalone
320
+ const now = new Date();
321
+ const dateStr = localDate().replace(/-/g, "");
322
+ const timeStr = now.toTimeString().slice(0, 5).replace(/:/g, "");
323
+ const filename = `sleep_${dateStr}_${timeStr}.md`;
324
+ writeFileSync(join(sleepDir, filename), redactSecrets(`# Sleep Audit Log${suffix}`), "utf-8");
325
+ }
326
+ function scanPreviousLocks(sleepDir, todayStr) {
327
+ if (!existsSync(sleepDir))
328
+ return [];
329
+ const locks = [];
330
+ const todayMs = dateStrToMs(todayStr);
331
+ for (const f of readdirSync(sleepDir)) {
332
+ const m = f.match(/^sleep_(\d{8})\.lock$/);
333
+ if (!m || m[1] === todayStr)
334
+ continue;
335
+ const state = readStateFile(join(sleepDir, f));
336
+ if (!state)
337
+ continue;
338
+ const ageDays = Math.round((todayMs - dateStrToMs(m[1])) / 86400000);
339
+ if (ageDays > 0)
340
+ locks.push({ path: join(sleepDir, f), dateStr: m[1], state, ageDays });
341
+ }
342
+ return locks.sort((a, b) => b.dateStr.localeCompare(a.dateStr)); // newest first
343
+ }
344
+ function dateStrToMs(ds) {
345
+ return new Date(`${ds.slice(0, 4)}-${ds.slice(4, 6)}-${ds.slice(6, 8)}T00:00:00`).getTime();
346
+ }
347
+ function dateStrToFormatted(ds) {
348
+ return `${ds.slice(0, 4)}-${ds.slice(4, 6)}-${ds.slice(6, 8)}`;
349
+ }
350
+ function failedEssentials(state) {
351
+ const failed = [];
352
+ for (const name of ESSENTIAL_STEPS) {
353
+ const s = state.steps[name];
354
+ if (!s || s.status === "failed" || s.status === "timeout" || s.status === "pending") {
355
+ failed.push(name);
356
+ }
357
+ }
358
+ return failed;
359
+ }
360
+ async function runCatchUp(locks, sleepData, memoryConfig, steps, flags, runtime, budget) {
361
+ for (const lock of locks) {
362
+ if (lock.ageDays > CATCHUP_MAX_AGE_DAYS) {
363
+ logError(TAG, `[CATCH-UP] Abandoning stale lock ${basename(lock.path)} — ${lock.ageDays} days old, data unrecoverable`);
364
+ unlinkSync(lock.path);
365
+ continue;
366
+ }
367
+ const needed = failedEssentials(lock.state);
368
+ if (needed.length === 0) {
369
+ logInfo(TAG, `[CATCH-UP] Cleaning up completed lock ${basename(lock.path)}`);
370
+ unlinkSync(lock.path);
371
+ continue;
372
+ }
373
+ logInfo(TAG, `[CATCH-UP] ${basename(lock.path)} — recovering: ${needed.join(", ")}`);
374
+ // 04a — daily summary with date-range
375
+ if (needed.includes("daily-summary")) {
376
+ const start = Date.now();
377
+ try {
378
+ const ctxWindow = getAbmindEnv().sleepCtxWindow;
379
+ const userId = sleepData.getPrimaryUserId();
380
+ const dayStart = dateStrToMs(lock.dateStr);
381
+ const dayEnd = dayStart + 86400000;
382
+ const summary = await buildDailySummary(sleepData.getDb(), (p) => sendWithRetry(runtime, p, "catch-up-04a", flags.verbose, budget).then(r => { if (r === null)
383
+ throw new LLMUnavailableError(); return r; }), {
384
+ ctxWindow, memoryDir: memoryConfig.memoryDir, userId, watermarkTs: 0,
385
+ dateRange: { startTs: dayStart, endTs: dayEnd },
386
+ });
387
+ if (summary) {
388
+ writeDailyFile(memoryConfig.memoryDir, dateStrToFormatted(lock.dateStr), summary);
389
+ lock.state.steps["daily-summary"] = { status: "ok", duration: Math.round((Date.now() - start) / 100) / 10 };
390
+ }
391
+ else {
392
+ lock.state.steps["daily-summary"] = { status: "skipped" };
393
+ }
394
+ logInfo(TAG, `[CATCH-UP] ✓ 04a-daily-summary for ${lock.dateStr} (${((Date.now() - start) / 1000).toFixed(1)}s)`);
395
+ }
396
+ catch (err) {
397
+ logWarn(TAG, `[CATCH-UP] ✗ 04a-daily-summary for ${lock.dateStr}: ${err instanceof Error ? err.message : String(err)}`);
398
+ lock.state.steps["daily-summary"] = { status: "failed", duration: Math.round((Date.now() - start) / 100) / 10 };
399
+ }
400
+ writeStateFile(lock.path, lock.state);
401
+ }
402
+ // 04b — extract from daily (needs daily file to exist)
403
+ if (needed.includes("extract-from-daily")) {
404
+ const dailyPath = join(memoryConfig.memoryDir, "daily", `daily_${dateStrToFormatted(lock.dateStr)}.md`);
405
+ if (!existsSync(dailyPath)) {
406
+ logInfo(TAG, `[CATCH-UP] ⏭ 04b — no daily file for ${lock.dateStr}`);
407
+ lock.state.steps["extract-from-daily"] = { status: "skipped" };
408
+ }
409
+ else {
410
+ const start = Date.now();
411
+ try {
412
+ const userId = sleepData.getPrimaryUserId();
413
+ const result = await extractFromDaily(dailyPath, userId, (p) => sendWithRetry(runtime, p, "catch-up-04b", flags.verbose, budget).then(r => { if (r === null)
414
+ throw new LLMUnavailableError(); return r; }));
415
+ lock.state.steps["extract-from-daily"] = { status: "ok", duration: Math.round((Date.now() - start) / 100) / 10 };
416
+ logInfo(TAG, `[CATCH-UP] ✓ 04b-extract-from-daily for ${lock.dateStr} (${((Date.now() - start) / 1000).toFixed(1)}s) — ${result.slice(0, 80)}`);
417
+ }
418
+ catch (err) {
419
+ logWarn(TAG, `[CATCH-UP] ✗ 04b for ${lock.dateStr}: ${err instanceof Error ? err.message : String(err)}`);
420
+ lock.state.steps["extract-from-daily"] = { status: "failed", duration: Math.round((Date.now() - start) / 100) / 10 };
421
+ }
422
+ }
423
+ writeStateFile(lock.path, lock.state);
424
+ }
425
+ // Prompt-driven essentials (retrospective — now includes extraction)
426
+ for (const stepName of ["retrospective"]) {
427
+ if (!needed.includes(stepName))
428
+ continue;
429
+ const step = steps.find(s => s.name === stepName);
430
+ if (!step) {
431
+ logWarn(TAG, `[CATCH-UP] Step file not found: ${stepName}`);
432
+ continue;
433
+ }
434
+ const start = Date.now();
435
+ const response = await sendWithRetry(runtime, step.rawPrompt, `catch-up-${stepName}`, flags.verbose, budget);
436
+ if (response) {
437
+ lock.state.steps[stepName] = { status: "ok", duration: Math.round((Date.now() - start) / 100) / 10 };
438
+ logInfo(TAG, `[CATCH-UP] ✓ ${stepName} (${((Date.now() - start) / 1000).toFixed(1)}s)`);
439
+ }
440
+ else {
441
+ lock.state.steps[stepName] = { status: "failed", duration: Math.round((Date.now() - start) / 100) / 10 };
442
+ logWarn(TAG, `[CATCH-UP] ✗ ${stepName}`);
443
+ }
444
+ writeStateFile(lock.path, lock.state);
445
+ }
446
+ // Final check: all essentials recovered?
447
+ const stillFailing = failedEssentials(lock.state);
448
+ if (stillFailing.length === 0) {
449
+ logInfo(TAG, `[CATCH-UP] ✅ ${basename(lock.path)} — all essentials recovered, lock deleted`);
450
+ unlinkSync(lock.path);
451
+ }
452
+ else {
453
+ logWarn(TAG, `[CATCH-UP] ${basename(lock.path)} — still failing: ${stillFailing.join(", ")} (failing ${lock.ageDays} day(s))`);
454
+ }
455
+ }
456
+ }
457
+ // ── Main orchestration ──────────────────────────────────────────────────────
458
+ /**
459
+ * Run the full sleep cycle. Extracted from main() for testability (#175).
460
+ * - Deterministic time injection via opts.now for decision sites
461
+ * - Throws SleepInitError / SleepTimeoutError instead of process.exit
462
+ * - Returns { ok, failCount } for observable outcomes
463
+ *
464
+ * Default args preserve current main() behavior exactly.
465
+ */
466
+ export async function runSleepCycle(opts) {
467
+ const flags = opts.flags ?? parseArgs(process.argv);
468
+ const now = opts.now ?? Date.now;
469
+ const timeoutMs = opts.timeoutMs ?? getAbmindEnv().sleepTimeoutMin * 60 * 1000;
470
+ const backoffMs = opts.backoffMs ?? ((n) => [10, 30, 60][Math.min(n, 2)] * 1000);
471
+ const runtime = opts.runtime;
472
+ if (flags.verbose) {
473
+ logInfo(TAG, "Verbose mode enabled");
474
+ }
475
+ const memoryConfig = { ...loadMemoryConfig(), ...opts.memoryConfigOverride };
476
+ const memory = new MemoryManager(memoryConfig);
477
+ try {
478
+ await memory.initialize();
479
+ }
480
+ catch (err) {
481
+ throw new SleepInitError(`Failed to initialize MemoryManager: ${err instanceof Error ? err.message : String(err)}`);
482
+ }
483
+ try {
484
+ const sleepData = memory.getSleepData();
485
+ // State file path — use opts.now for deterministic today derivation
486
+ const dateStr = toDateStr(now());
487
+ const statePath = join(memoryConfig.memoryDir, "sleep", `sleep_${dateStr}.lock`);
488
+ const existingState = readStateFile(statePath);
489
+ const isResume = existingState !== null && Object.values(existingState.steps).some(s => s.status === "ok");
490
+ // Gather state
491
+ // cron integration is caller-owned (bridge-only). Abmind library runs without cron awareness.
492
+ const cronFn = undefined;
493
+ const gatherer = new SleepStateGatherer(memory, memoryConfig, cronFn);
494
+ const snapshot = await gatherer.gather();
495
+ if (flags.verbose)
496
+ logInfo(TAG, `State gathered: ${buildSnapshotSummary(snapshot)}`);
497
+ // Guardrail: skip if no messages since last sleep (unless --force or resuming)
498
+ const msgCount = snapshot.dbStats.messagesSinceLastSleep;
499
+ if (msgCount === 0 && !flags.force && !isResume) {
500
+ logInfo(TAG, `[SLEEP] No messages since last sleep — nothing to process. Use --force to run housekeeping anyway.`);
501
+ return { ok: true, failCount: 0 };
502
+ }
503
+ // Wired pre-tasks (always run — fast, idempotent)
504
+ logInfo(TAG, `[SLEEP] Running wired pre-tasks${isResume ? " (resume)" : ""}...`);
505
+ const wiredResults = await runWiredPreTasks(sleepData, memoryConfig.memoryDir, memory);
506
+ logInfo(TAG, `[SLEEP] Wired: ${formatWiredResults(wiredResults)}`);
507
+ // Build candidate lists for conditional prompts
508
+ const candidates = sleepData.buildSleepCandidates();
509
+ logInfo(TAG, `[SLEEP] Candidates: topics=${candidates.untaggedMemories ? "yes" : "none"}, promote=${candidates.promotionCandidates ? "yes" : "none"}, contradict=${candidates.contradictions ? "yes" : "none"}, merge=${candidates.mergeCandidates ? "yes" : "none"}, translate=${candidates.translationIssues ? "yes" : "none"}, emotion-ctx=${candidates.emotionContextGaps ? "yes" : "none"}, feedback=${candidates.recallFeedback ? "yes" : "none"}`);
510
+ // Load step files + build vars
511
+ const vars = buildSleepVars(snapshot);
512
+ vars.WIRED_RESULTS = formatWiredResults(wiredResults);
513
+ // Inject candidate lists as template variables
514
+ vars.UNTAGGED_MEMORIES = candidates.untaggedMemories || "No untagged memories found.";
515
+ vars.PROMOTION_CANDIDATES = candidates.promotionCandidates || "No promotion candidates found.";
516
+ vars.CONTRADICTION_WARNINGS = candidates.contradictions || "";
517
+ vars.MERGE_CANDIDATES = candidates.mergeCandidates || "No merge candidates found.";
518
+ vars.TRANSLATION_ISSUES = candidates.translationIssues || "No translation issues found.";
519
+ vars.EMOTION_CONTEXT_GAPS = candidates.emotionContextGaps || "No emotion context gaps found.";
520
+ vars.RECALL_FEEDBACK = candidates.recallFeedback || "No recalls happened today.";
521
+ vars.WIRED_RESULTS = formatWiredResults(wiredResults);
522
+ vars.RESUME_CONTEXT = isResume
523
+ ? `This is a RESUMED sleep cycle. Steps already completed: ${Object.entries(existingState.steps).filter(([, s]) => s.status === "ok" || s.status === "skipped").map(([k]) => k).join(", ")}. Only pending/failed steps will run.`
524
+ : "Fresh sleep cycle — all steps will run.";
525
+ // Pre-query messages for retro (watermark-scoped, noise-stripped)
526
+ const lastSleepTs = snapshot.lastSleepTimestamp ?? 0;
527
+ try {
528
+ const garbagePath = join(memoryConfig.memoryDir, "garbage.json");
529
+ const garbageIds = new Set();
530
+ try {
531
+ const raw = JSON.parse(readFileSync(garbagePath, "utf-8"));
532
+ const entries = Array.isArray(raw) ? raw : (raw?.messages ?? []);
533
+ for (const e of entries) {
534
+ if (e?.messageId)
535
+ garbageIds.add(e.messageId);
536
+ }
537
+ }
538
+ catch { /* no garbage file */ }
539
+ const msgs = sleepData.getMessagesAfter(lastSleepTs);
540
+ const lines = msgs
541
+ .filter(m => !garbageIds.has(m.id) && !m.content.startsWith("[SYSTEM"))
542
+ .map(m => `[${m.role}]${m.emotion_score ? ` (emotion:${m.emotion_score})` : ""} ${m.content.slice(0, 500)}`);
543
+ vars.CLEAN_MESSAGES = lines.length > 0
544
+ ? `${lines.length} messages since last sleep:\n\n${lines.join("\n")}`
545
+ : "No messages since last sleep.";
546
+ logInfo(TAG, `[SLEEP] Pre-queried ${lines.length} messages for retro (${msgs.length} total, ${garbageIds.size} garbage filtered)`);
547
+ }
548
+ catch {
549
+ vars.CLEAN_MESSAGES = "Error loading messages — use abmind recall to search.";
550
+ }
551
+ // Set remaining missing vars
552
+ vars.MESSAGES_SINCE_WATERMARK = vars.CLEAN_MESSAGES; // same data, different name for gc-noise
553
+ vars.RETRO_PATH = join(memoryConfig.memoryDir, "daily", `daily_${toIsoDate(now())}.md`);
554
+ try {
555
+ const { getLatestConsolidationFile } = await import("../consolidation-search.js");
556
+ const latest = getLatestConsolidationFile(memoryConfig.memoryDir, "weekly");
557
+ vars.CONSOLIDATION_PATH = latest?.filePath ?? "No consolidation files yet.";
558
+ }
559
+ catch {
560
+ vars.CONSOLIDATION_PATH = "No consolidation files yet.";
561
+ }
562
+ const steps = loadSleepSteps();
563
+ // Merge snapshot vars + bridge vars into one map for JIT substitution
564
+ const snapshotVars = buildSleepVars(snapshot);
565
+ for (const [k, v] of Object.entries(snapshotVars))
566
+ vars[k] = vars[k] ?? v;
567
+ // Progress protocol — emit PROGRESS:<pct>:<label> on stdout
568
+ const totalSteps = steps.length;
569
+ let stepIndex = 0;
570
+ const emitProgress = (label) => {
571
+ const pct = Math.round((stepIndex / totalSteps) * 100);
572
+ process.stdout.write(`PROGRESS:${pct}:${label}\n`);
573
+ };
574
+ if (flags.dryRun) {
575
+ for (const step of steps)
576
+ process.stdout.write(`\n--- ${step.filename} ---\n${substituteVars(step.rawPrompt, vars)}\n`);
577
+ return { ok: true, failCount: 0 };
578
+ }
579
+ // Skip logic — candidate-driven (empty = skip)
580
+ const skipSet = new Set();
581
+ // Level tiering — controls which prompts are eligible.
582
+ // Precedence: opts.level > SLEEP_QUALITY env (legacy bridge path) > DEFAULT_LEVEL.
583
+ const quality = opts.level ?? (getAbmindEnv().sleepQuality ? parseLevel(getAbmindEnv().sleepQuality) : DEFAULT_LEVEL);
584
+ const curationDay = getAbmindEnv().sleepCurationDay;
585
+ const today = new Date(now()).toLocaleDateString("en", { weekday: "long" }).toLowerCase();
586
+ const isCurationDay = today === curationDay;
587
+ const BUDGET_ONLY = new Set(["gc-noise", "daily-summary", "extract-from-daily"]);
588
+ const WEEKLY_ONLY = new Set(["skill-review", "core-knowledge", "consolidation"]);
589
+ const ULTIMATE_ONLY = new Set(["rem-synthesis"]);
590
+ if (quality === "budget") {
591
+ for (const step of steps) {
592
+ if (!BUDGET_ONLY.has(step.name))
593
+ skipSet.add(step.name);
594
+ }
595
+ logInfo(TAG, `[SLEEP] Quality=budget — only essential extraction`);
596
+ }
597
+ else if (quality === "normal" && !isCurationDay) {
598
+ for (const name of WEEKLY_ONLY)
599
+ skipSet.add(name);
600
+ for (const name of ULTIMATE_ONLY)
601
+ skipSet.add(name);
602
+ logInfo(TAG, `[SLEEP] Quality=normal — weekly + ultimate prompts skipped (curation day: ${curationDay})`);
603
+ }
604
+ else if (quality === "normal" && isCurationDay) {
605
+ for (const name of ULTIMATE_ONLY)
606
+ skipSet.add(name);
607
+ logInfo(TAG, `[SLEEP] Quality=normal (curation day) — ultimate prompts skipped`);
608
+ }
609
+ else {
610
+ // ultimate: only skip REM on non-curation days
611
+ if (!isCurationDay)
612
+ skipSet.add("rem-synthesis");
613
+ logInfo(TAG, `[SLEEP] Quality=${quality}${isCurationDay ? " (curation day)" : ""} — all eligible`);
614
+ }
615
+ // Candidate-driven skips (empty = nothing to do)
616
+ if (!candidates.recallFeedback)
617
+ skipSet.add("feedback");
618
+ if (!candidates.untaggedMemories)
619
+ skipSet.add("topic-assignment");
620
+ if (!candidates.promotionCandidates)
621
+ skipSet.add("core-promotion");
622
+ if (!candidates.mergeCandidates)
623
+ skipSet.add("merge");
624
+ if (!candidates.translationIssues)
625
+ skipSet.add("translation-check");
626
+ if (!candidates.translationIssues)
627
+ skipSet.add("translation");
628
+ if (!candidates.emotionContextGaps)
629
+ skipSet.add("emotion-context");
630
+ if (!candidates.emotionContextGaps)
631
+ skipSet.add("emotion-context-backfill");
632
+ // Legacy skip names (old prompt files)
633
+ if (snapshot.topicFiles.length === 0)
634
+ skipSet.add("topic-reorg");
635
+ if (snapshot.dbStats.extractedMemoryCount < 10) {
636
+ skipSet.add("merge");
637
+ skipSet.add("darwinism");
638
+ }
639
+ if (snapshot.dbStats.extractedMemoryCount < 20)
640
+ skipSet.add("rem-synthesis");
641
+ try {
642
+ if (!existsSync(join(memoryConfig.memoryDir, "..", "received")))
643
+ skipSet.add("media-cleanup");
644
+ }
645
+ catch { /* */ }
646
+ try {
647
+ const shortCount = sleepData.getShortMessageCount();
648
+ if (shortCount === 0)
649
+ skipSet.add("gc-noise");
650
+ }
651
+ catch { /* */ }
652
+ // Initialize state file
653
+ const state = existingState ?? {
654
+ status: "ongoing",
655
+ pid: process.pid,
656
+ startedAt: now(),
657
+ llmCalls: 0,
658
+ wiredResults,
659
+ steps: {},
660
+ };
661
+ state.status = "ongoing";
662
+ state.pid = process.pid;
663
+ state.wiredResults = wiredResults;
664
+ // 20-min wall-clock timeout
665
+ const timeoutHandle = setTimeout(() => {
666
+ logError(TAG, `[SLEEP] ⏰ ${Math.round(timeoutMs / 60000)}-minute timeout reached — aborting`);
667
+ throw new SleepTimeoutError(`Sleep cycle timeout after ${Math.round(timeoutMs / 60000)} minutes`);
668
+ }, timeoutMs);
669
+ // Resolve model name for logging. Bridge wraps and passes via env; library default is "unknown".
670
+ const modelUsed = getAbmindEnv().sleepModelName;
671
+ let dreamySucceeded = true;
672
+ let dailySummaryPath = null;
673
+ try {
674
+ // ── LLM call budget (hard safety limit) ──
675
+ const budget = new LlmBudget(state, statePath);
676
+ // ── Catch-up: recover failed essentials from previous days ──
677
+ const sleepDir = join(memoryConfig.memoryDir, "sleep");
678
+ const previousLocks = scanPreviousLocks(sleepDir, dateStr);
679
+ if (previousLocks.length > 0) {
680
+ logInfo(TAG, `[CATCH-UP] Found ${previousLocks.length} previous lock(s)`);
681
+ await runCatchUp(previousLocks, sleepData, memoryConfig, steps, flags, runtime, budget);
682
+ }
683
+ emitProgress("starting");
684
+ let consecutiveFailures = 0;
685
+ // Create day directory for per-step logs
686
+ const stepLogDir = join(sleepDir, dateStr);
687
+ mkdirSync(stepLogDir, { recursive: true });
688
+ for (const step of steps) {
689
+ // Hard safety: LLM call budget exhausted → suspend
690
+ if (budget.exhausted) {
691
+ logWarn(TAG, `[BUDGET] Suspending sleep — ${budget.calls}/${getAbmindEnv().sleepMaxLlmCalls} LLM calls used`);
692
+ state.status = "suspended";
693
+ writeStateFile(statePath, state);
694
+ break;
695
+ }
696
+ emitProgress(step.name);
697
+ stepIndex++;
698
+ // Resume: skip already completed steps
699
+ if (isResume && existingState?.steps[step.name]?.status === "ok") {
700
+ logInfo(TAG, `[SLEEP] ⏭ ${step.name} — already done (resume)`);
701
+ continue;
702
+ }
703
+ if (isResume && existingState?.steps[step.name]?.status === "skipped") {
704
+ logInfo(TAG, `[SLEEP] ⏭ ${step.name} — skipped (resume)`);
705
+ continue;
706
+ }
707
+ // Skip logic (identity and report always run)
708
+ if (step.skippable && skipSet.has(step.name)) {
709
+ logInfo(TAG, `[SLEEP] ⏭ ${step.name} — skipped`);
710
+ state.steps[step.name] = { status: "skipped" };
711
+ writeStateFile(statePath, state);
712
+ continue;
713
+ }
714
+ const start = Date.now();
715
+ logInfo(TAG, `[SLEEP] → ${step.name}`);
716
+ state.steps[step.name] = { status: "pending" };
717
+ writeStateFile(statePath, state);
718
+ // Code-driven steps
719
+ if (step.name === "daily-summary") {
720
+ try {
721
+ const ctxWindow = getAbmindEnv().sleepCtxWindow;
722
+ const userId = sleepData.getPrimaryUserId();
723
+ const watermarkTs = sleepData.getExtractionWatermark(userId);
724
+ // Determine target date from first unprocessed message
725
+ const firstMsgTs = sleepData.getFirstMessageAfter(userId, watermarkTs);
726
+ const firstMsgDate = firstMsgTs ? new Date(firstMsgTs) : new Date(now());
727
+ const targetDate = `${firstMsgDate.getFullYear()}-${String(firstMsgDate.getMonth() + 1).padStart(2, "0")}-${String(firstMsgDate.getDate()).padStart(2, "0")}`;
728
+ const summary = await buildDailySummary(sleepData.getDb(), (p) => sendWithRetry(runtime, p, "daily-summary", flags.verbose, budget).then(r => { if (r === null)
729
+ throw new LLMUnavailableError(); return r; }), {
730
+ ctxWindow, memoryDir: memoryConfig.memoryDir, userId, watermarkTs,
731
+ });
732
+ if (summary) {
733
+ dailySummaryPath = writeDailyFile(memoryConfig.memoryDir, targetDate, summary);
734
+ state.steps[step.name] = { status: "ok", duration: Math.round((Date.now() - start) / 100) / 10, path: dailySummaryPath };
735
+ writeFileSync(join(stepLogDir, `${String(stepIndex).padStart(2, "0")}-${step.name}.md`), redactSecrets(summary), "utf-8");
736
+ }
737
+ else {
738
+ state.steps[step.name] = { status: "skipped" };
739
+ }
740
+ }
741
+ catch (err) {
742
+ logWarn(TAG, `[SLEEP] 04a failed: ${err instanceof Error ? err.message : String(err)}`);
743
+ state.steps[step.name] = { status: "failed", duration: Math.round((Date.now() - start) / 100) / 10 };
744
+ dreamySucceeded = false;
745
+ }
746
+ writeStateFile(statePath, state);
747
+ logInfo(TAG, `[SLEEP] ${state.steps[step.name]?.status === "ok" ? "✓" : "✗"} ${step.name} (${((Date.now() - start) / 1000).toFixed(1)}s)`);
748
+ continue;
749
+ }
750
+ if (step.name === "extract-from-daily") {
751
+ // Resume path: if daily-summary already completed in a prior run, the
752
+ // in-memory dailySummaryPath is null. Recover it from the lock's
753
+ // recorded path so extract-from-daily can still run. #181.
754
+ if (!dailySummaryPath) {
755
+ const priorPath = state.steps["daily-summary"]?.path;
756
+ if (priorPath && existsSync(priorPath)) {
757
+ dailySummaryPath = priorPath;
758
+ logInfo(TAG, `[SLEEP] ${step.name} — recovered daily path from lock (${priorPath})`);
759
+ }
760
+ }
761
+ if (!dailySummaryPath) {
762
+ state.steps[step.name] = { status: "skipped" };
763
+ writeStateFile(statePath, state);
764
+ logInfo(TAG, `[SLEEP] ⏭ ${step.name} — no daily summary`);
765
+ continue;
766
+ }
767
+ try {
768
+ const userId = sleepData.getPrimaryUserId();
769
+ const result = await extractFromDaily(dailySummaryPath, userId, (p) => sendWithRetry(runtime, p, "04b-extract", flags.verbose, budget).then(r => { if (r === null)
770
+ throw new LLMUnavailableError(); return r; }));
771
+ state.steps[step.name] = { status: "ok", duration: Math.round((Date.now() - start) / 100) / 10 };
772
+ writeFileSync(join(stepLogDir, `${String(stepIndex).padStart(2, "0")}-${step.name}.md`), redactSecrets(result), "utf-8");
773
+ logInfo(TAG, `[SLEEP] ✓ ${step.name} (${((Date.now() - start) / 1000).toFixed(1)}s) — ${result.slice(0, 80)}`);
774
+ }
775
+ catch (err) {
776
+ logWarn(TAG, `[SLEEP] 04b failed: ${err instanceof Error ? err.message : String(err)}`);
777
+ state.steps[step.name] = { status: "failed", duration: Math.round((Date.now() - start) / 100) / 10 };
778
+ dreamySucceeded = false;
779
+ }
780
+ writeStateFile(statePath, state);
781
+ continue;
782
+ }
783
+ // Standard prompt-driven step — JIT substitution
784
+ // Populate contradiction+graph vars JIT (needs today's extractions to exist)
785
+ if (step.name === "contradiction-and-graph") {
786
+ try {
787
+ const todayStart = new Date(now());
788
+ todayStart.setHours(0, 0, 0, 0);
789
+ const memDb = memory.getDatabase();
790
+ const newRows = (memDb?.prepare(`SELECT id, content_en, memory_type, topic, trust FROM extracted_memories WHERE created_at >= ? AND memory_type != 'observation' ORDER BY created_at DESC LIMIT 30`).all(todayStart.getTime()) ?? []);
791
+ if (newRows.length === 0) {
792
+ state.steps[step.name] = { status: "skipped" };
793
+ writeStateFile(statePath, state);
794
+ logInfo(TAG, `[SLEEP] ⏭ ${step.name} — no new extractions today`);
795
+ continue;
796
+ }
797
+ vars.NEW_EXTRACTIONS = newRows.map(r => `[id=${r.id}] (${r.memory_type}, trust=${r.trust}) ${r.content_en}`).join("\n");
798
+ // Find candidates: FTS5 top-5 per new memory, capped at 20 total
799
+ const candidateIds = new Set();
800
+ const candidateRows = [];
801
+ for (const nr of newRows.slice(0, 5)) {
802
+ const keywords = nr.content_en.split(/\s+/).filter(w => w.length > 3).slice(0, 3).join(" OR ");
803
+ if (!keywords)
804
+ continue;
805
+ try {
806
+ const matches = memDb.prepare(`SELECT em.id, em.content_en, em.memory_type, em.trust, em.credibility FROM extracted_memories em JOIN extracted_memories_fts fts ON em.id = fts.rowid WHERE extracted_memories_fts MATCH ? AND em.id != ? AND em.trust >= ? AND em.memory_type != 'observation' AND em.valid_to IS NULL LIMIT 5`).all(keywords, nr.id, nr.trust);
807
+ for (const m of matches) {
808
+ if (!candidateIds.has(m.id) && candidateIds.size < 20) {
809
+ candidateIds.add(m.id);
810
+ candidateRows.push(m);
811
+ }
812
+ }
813
+ }
814
+ catch { /* FTS query might fail on special chars — skip */ }
815
+ }
816
+ vars.CONTRADICTION_CANDIDATES = candidateRows.length > 0
817
+ ? candidateRows.map(r => `[id=${r.id}] (${r.memory_type}, trust=${r.trust}, cred=${r.credibility}) ${r.content_en}`).join("\n")
818
+ : "No existing memories with overlapping content found.";
819
+ }
820
+ catch (err) {
821
+ logWarn(TAG, `[SLEEP] contradiction-and-graph var prep failed: ${err instanceof Error ? err.message : String(err)}`);
822
+ state.steps[step.name] = { status: "skipped" };
823
+ writeStateFile(statePath, state);
824
+ continue;
825
+ }
826
+ }
827
+ // Populate REM sample vars JIT
828
+ if (step.name === "rem-synthesis") {
829
+ try {
830
+ const memDb = memory.getDatabase();
831
+ const sample = memDb?.prepare(`SELECT id, content_en, memory_type, created_at FROM extracted_memories WHERE trust >= 2 AND memory_type != 'observation' AND valid_to IS NULL ORDER BY RANDOM() LIMIT 10`).all() ?? [];
832
+ if (sample.length < 5) {
833
+ state.steps[step.name] = { status: "skipped" };
834
+ writeStateFile(statePath, state);
835
+ logInfo(TAG, `[SLEEP] ⏭ ${step.name} — not enough memories for REM`);
836
+ continue;
837
+ }
838
+ vars.REM_SAMPLE = sample.map(r => `[${r.memory_type}, ${new Date(r.created_at).toISOString().slice(0, 10)}] ${r.content_en}`).join("\n");
839
+ }
840
+ catch {
841
+ state.steps[step.name] = { status: "skipped" };
842
+ writeStateFile(statePath, state);
843
+ continue;
844
+ }
845
+ }
846
+ const prompt = substituteVars(step.rawPrompt, vars);
847
+ const ctxBefore = -1;
848
+ const response = await sendWithRetry(runtime, prompt, step.name, flags.verbose, budget);
849
+ const ctxAfter = -1;
850
+ const duration = Date.now() - start;
851
+ if (response) {
852
+ state.steps[step.name] = { status: "ok", duration: Math.round(duration / 100) / 10, ctxBefore, ctxAfter };
853
+ writeFileSync(join(stepLogDir, `${String(stepIndex).padStart(2, "0")}-${step.name}.md`), redactSecrets(response), "utf-8");
854
+ // Generic output chaining + explicit aliases
855
+ vars[step.name.toUpperCase().replace(/-/g, "_") + "_OUTPUT"] = response;
856
+ if (step.name === "retrospective")
857
+ vars.RETRO_CONTENT = response;
858
+ }
859
+ else {
860
+ state.steps[step.name] = { status: "failed", duration: Math.round(duration / 100) / 10, attempts: MAX_RETRIES, ctxBefore, ctxAfter };
861
+ dreamySucceeded = false;
862
+ }
863
+ writeStateFile(statePath, state);
864
+ logInfo(TAG, `[SLEEP] ${response ? "✓" : "✗"} ${step.name} (${(duration / 1000).toFixed(1)}s, ${response?.length ?? 0} chars)`);
865
+ // Backoff between steps: 10s → 30s → 60s on consecutive failures, reset on success
866
+ if (response) {
867
+ consecutiveFailures = 0;
868
+ }
869
+ else {
870
+ consecutiveFailures++;
871
+ }
872
+ const isEssential = step.name.startsWith("04") || step.name === "00-identity";
873
+ if (!isEssential) {
874
+ const delayMs = backoffMs(consecutiveFailures);
875
+ if (delayMs > 0) {
876
+ logInfo(TAG, `[SLEEP] Waiting ${Math.round(delayMs / 1000)}s before next step`);
877
+ await new Promise(r => setTimeout(r, delayMs));
878
+ }
879
+ }
880
+ }
881
+ }
882
+ finally {
883
+ clearTimeout(timeoutHandle);
884
+ // Runtime lifecycle is caller-owned — no shutdown call from the library.
885
+ }
886
+ // Set final status
887
+ if (state.status === "ongoing") {
888
+ state.status = dreamySucceeded ? "completed" : "failed";
889
+ writeStateFile(statePath, state);
890
+ }
891
+ // Advance extraction watermark — only when all steps succeeded
892
+ if (dreamySucceeded) {
893
+ try {
894
+ const count = sleepData.advanceExtractionWatermarks();
895
+ logInfo(TAG, `[SLEEP] Extraction watermark advanced for ${count} chat(s)`);
896
+ }
897
+ catch { /* non-fatal */ }
898
+ }
899
+ else {
900
+ logWarn(TAG, "[SLEEP] Watermark NOT advanced — essential steps failed, messages preserved for catch-up");
901
+ }
902
+ // Write audit
903
+ const stepEntries = Object.entries(state.steps);
904
+ const okCount = stepEntries.filter(([, s]) => s.status === "ok").length;
905
+ const failCount = stepEntries.filter(([, s]) => s.status === "failed" || s.status === "timeout").length;
906
+ const skipCount = stepEntries.filter(([, s]) => s.status === "skipped").length;
907
+ const totalDuration = (Date.now() - state.startedAt) / 1000;
908
+ const allResponses = stepEntries.map(([k, v]) => `[${k}] ${v.status}${v.duration ? ` (${v.duration}s)` : ""}`).join("\n");
909
+ try {
910
+ writeAuditLog(memoryConfig.memoryDir, {
911
+ timestamp: localISO(),
912
+ model: modelUsed,
913
+ stateSnapshotSummary: buildSnapshotSummary(snapshot),
914
+ subagentResponse: `Wired: ${formatWiredResults(wiredResults)}\n${allResponses}${vars.RETRO_CONTENT ? "\n\n--- Retrospective ---\n" + vars.RETRO_CONTENT : ""}`,
915
+ outcomes: { filesConsolidated: 0, messagesPruned: wiredResults.purged + wiredResults.deduped, embeddingsRemoved: 0, sessionsCleaned: 0, topicsMerged: 0, topicsDeleted: 0 },
916
+ });
917
+ }
918
+ catch (err) {
919
+ process.stderr.write(`Warning: Failed to write audit — ${err instanceof Error ? err.message : String(err)}\n`);
920
+ }
921
+ // Wired post-task: flush old messages (keep max 500, age out >7 days, garbage 12h)
922
+ if (dreamySucceeded) {
923
+ try {
924
+ // Flush garbage-marked messages
925
+ const garbagePath = join(memoryConfig.memoryDir, "garbage.json");
926
+ if (existsSync(garbagePath)) {
927
+ const raw = JSON.parse(readFileSync(garbagePath, "utf-8"));
928
+ const garbage = Array.isArray(raw) ? raw : (Array.isArray(raw?.messages) ? raw.messages : []);
929
+ if (garbage.length > 0) {
930
+ const ids = garbage.map(g => g.msg_id).filter((id) => typeof id === "number");
931
+ if (ids.length > 0) {
932
+ sleepData.deleteMessagesByIds(ids);
933
+ logInfo(TAG, `[SLEEP] Flushed ${ids.length} garbage messages`);
934
+ }
935
+ writeFileSync(garbagePath, "[]");
936
+ }
937
+ }
938
+ // Age out + cap
939
+ const { agedOut, capped } = sleepData.flushOldMessages({ maxAgeDays: 7, maxCount: 500 });
940
+ if (agedOut > 0)
941
+ logInfo(TAG, `[SLEEP] Flushed ${agedOut} messages >7d`);
942
+ if (capped > 0)
943
+ logInfo(TAG, `[SLEEP] Flushed ${capped} messages (cap 500)`);
944
+ }
945
+ catch (err) {
946
+ logWarn(TAG, `[WIRED] flush failed: ${err instanceof Error ? err.message : String(err)}`);
947
+ }
948
+ }
949
+ emitProgress("done");
950
+ logInfo(TAG, `[SLEEP] 🏁 ${okCount} ok, ${failCount} failed, ${skipCount} skipped | wired: ${formatWiredResults(wiredResults)} | ${totalDuration.toFixed(0)}s total`);
951
+ return { ok: failCount === 0, failCount };
952
+ }
953
+ finally {
954
+ memory.close();
955
+ }
956
+ }
957
+ // CLI entry + isDirectRun removed — the standalone entry point now lives in
958
+ // cli/abmind.ts as the `abmind sleep` subcommand. Library consumers call
959
+ // runSleepCycle(opts) directly with their own SleepRuntime.
960
+ //# sourceMappingURL=orchestrator.js.map