@astragenie/astramemory-local 0.7.2

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 (493) hide show
  1. package/CHANGELOG.md +341 -0
  2. package/README.md +419 -0
  3. package/dist/backup/retention.d.ts +15 -0
  4. package/dist/backup/retention.js +62 -0
  5. package/dist/backup/retention.js.map +1 -0
  6. package/dist/backup/snapshot.d.ts +21 -0
  7. package/dist/backup/snapshot.js +55 -0
  8. package/dist/backup/snapshot.js.map +1 -0
  9. package/dist/backup/verify.d.ts +23 -0
  10. package/dist/backup/verify.js +77 -0
  11. package/dist/backup/verify.js.map +1 -0
  12. package/dist/budget/tracker.d.ts +58 -0
  13. package/dist/budget/tracker.js +102 -0
  14. package/dist/budget/tracker.js.map +1 -0
  15. package/dist/capture/codex.d.ts +63 -0
  16. package/dist/capture/codex.js +0 -0
  17. package/dist/capture/codex.js.map +1 -0
  18. package/dist/cli/backup.d.ts +1 -0
  19. package/dist/cli/backup.js +112 -0
  20. package/dist/cli/backup.js.map +1 -0
  21. package/dist/cli/budget.d.ts +7 -0
  22. package/dist/cli/budget.js +44 -0
  23. package/dist/cli/budget.js.map +1 -0
  24. package/dist/cli/capture.d.ts +10 -0
  25. package/dist/cli/capture.js +113 -0
  26. package/dist/cli/capture.js.map +1 -0
  27. package/dist/cli/consolidate.d.ts +16 -0
  28. package/dist/cli/consolidate.js +146 -0
  29. package/dist/cli/consolidate.js.map +1 -0
  30. package/dist/cli/doctor.d.ts +1 -0
  31. package/dist/cli/doctor.js +54 -0
  32. package/dist/cli/doctor.js.map +1 -0
  33. package/dist/cli/entity-backfill.d.ts +10 -0
  34. package/dist/cli/entity-backfill.js +46 -0
  35. package/dist/cli/entity-backfill.js.map +1 -0
  36. package/dist/cli/hook-install.d.ts +45 -0
  37. package/dist/cli/hook-install.js +77 -0
  38. package/dist/cli/hook-install.js.map +1 -0
  39. package/dist/cli/index.d.ts +2 -0
  40. package/dist/cli/index.js +312 -0
  41. package/dist/cli/index.js.map +1 -0
  42. package/dist/cli/init.d.ts +16 -0
  43. package/dist/cli/init.js +431 -0
  44. package/dist/cli/init.js.map +1 -0
  45. package/dist/cli/mcp-stdio.d.ts +18 -0
  46. package/dist/cli/mcp-stdio.js +67 -0
  47. package/dist/cli/mcp-stdio.js.map +1 -0
  48. package/dist/cli/memory.d.ts +15 -0
  49. package/dist/cli/memory.js +52 -0
  50. package/dist/cli/memory.js.map +1 -0
  51. package/dist/cli/open-runtime-db.d.ts +15 -0
  52. package/dist/cli/open-runtime-db.js +37 -0
  53. package/dist/cli/open-runtime-db.js.map +1 -0
  54. package/dist/cli/pair.d.ts +29 -0
  55. package/dist/cli/pair.js +64 -0
  56. package/dist/cli/pair.js.map +1 -0
  57. package/dist/cli/providers.d.ts +10 -0
  58. package/dist/cli/providers.js +97 -0
  59. package/dist/cli/providers.js.map +1 -0
  60. package/dist/cli/queue-purge.d.ts +5 -0
  61. package/dist/cli/queue-purge.js +92 -0
  62. package/dist/cli/queue-purge.js.map +1 -0
  63. package/dist/cli/queue.d.ts +29 -0
  64. package/dist/cli/queue.js +73 -0
  65. package/dist/cli/queue.js.map +1 -0
  66. package/dist/cli/rebuild.d.ts +15 -0
  67. package/dist/cli/rebuild.js +70 -0
  68. package/dist/cli/rebuild.js.map +1 -0
  69. package/dist/cli/reembed-dim.d.ts +21 -0
  70. package/dist/cli/reembed-dim.js +199 -0
  71. package/dist/cli/reembed-dim.js.map +1 -0
  72. package/dist/cli/reinstall.d.ts +1 -0
  73. package/dist/cli/reinstall.js +205 -0
  74. package/dist/cli/reinstall.js.map +1 -0
  75. package/dist/cli/restore.d.ts +1 -0
  76. package/dist/cli/restore.js +167 -0
  77. package/dist/cli/restore.js.map +1 -0
  78. package/dist/cli/retag.d.ts +14 -0
  79. package/dist/cli/retag.js +62 -0
  80. package/dist/cli/retag.js.map +1 -0
  81. package/dist/cli/search.d.ts +66 -0
  82. package/dist/cli/search.js +174 -0
  83. package/dist/cli/search.js.map +1 -0
  84. package/dist/cli/serve.d.ts +9 -0
  85. package/dist/cli/serve.js +364 -0
  86. package/dist/cli/serve.js.map +1 -0
  87. package/dist/cli/service.d.ts +1 -0
  88. package/dist/cli/service.js +121 -0
  89. package/dist/cli/service.js.map +1 -0
  90. package/dist/cli/sync.d.ts +15 -0
  91. package/dist/cli/sync.js +61 -0
  92. package/dist/cli/sync.js.map +1 -0
  93. package/dist/cli/token.d.ts +24 -0
  94. package/dist/cli/token.js +77 -0
  95. package/dist/cli/token.js.map +1 -0
  96. package/dist/cli/wait-health.d.ts +4 -0
  97. package/dist/cli/wait-health.js +23 -0
  98. package/dist/cli/wait-health.js.map +1 -0
  99. package/dist/config/config.d.ts +127 -0
  100. package/dist/config/config.js +38 -0
  101. package/dist/config/config.js.map +1 -0
  102. package/dist/config/datadir.d.ts +30 -0
  103. package/dist/config/datadir.js +65 -0
  104. package/dist/config/datadir.js.map +1 -0
  105. package/dist/config/loader.d.ts +23 -0
  106. package/dist/config/loader.js +102 -0
  107. package/dist/config/loader.js.map +1 -0
  108. package/dist/config/migrate-dirs.d.ts +36 -0
  109. package/dist/config/migrate-dirs.js +132 -0
  110. package/dist/config/migrate-dirs.js.map +1 -0
  111. package/dist/config/persist-envs.d.ts +23 -0
  112. package/dist/config/persist-envs.js +119 -0
  113. package/dist/config/persist-envs.js.map +1 -0
  114. package/dist/config/resolve-runtime.d.ts +19 -0
  115. package/dist/config/resolve-runtime.js +53 -0
  116. package/dist/config/resolve-runtime.js.map +1 -0
  117. package/dist/config/secrets.d.ts +28 -0
  118. package/dist/config/secrets.js +38 -0
  119. package/dist/config/secrets.js.map +1 -0
  120. package/dist/config/sync-settings.d.ts +16 -0
  121. package/dist/config/sync-settings.js +34 -0
  122. package/dist/config/sync-settings.js.map +1 -0
  123. package/dist/config/writer.d.ts +19 -0
  124. package/dist/config/writer.js +121 -0
  125. package/dist/config/writer.js.map +1 -0
  126. package/dist/consolidate/consolidate.d.ts +80 -0
  127. package/dist/consolidate/consolidate.js +0 -0
  128. package/dist/consolidate/consolidate.js.map +1 -0
  129. package/dist/consolidate/proposals.d.ts +35 -0
  130. package/dist/consolidate/proposals.js +66 -0
  131. package/dist/consolidate/proposals.js.map +1 -0
  132. package/dist/contracts/atom-wire.d.ts +48 -0
  133. package/dist/contracts/atom-wire.js +55 -0
  134. package/dist/contracts/atom-wire.js.map +1 -0
  135. package/dist/contracts/embed.d.ts +41 -0
  136. package/dist/contracts/embed.js +20 -0
  137. package/dist/contracts/embed.js.map +1 -0
  138. package/dist/contracts/index.d.ts +5 -0
  139. package/dist/contracts/index.js +6 -0
  140. package/dist/contracts/index.js.map +1 -0
  141. package/dist/contracts/job.d.ts +113 -0
  142. package/dist/contracts/job.js +32 -0
  143. package/dist/contracts/job.js.map +1 -0
  144. package/dist/contracts/llm.d.ts +30 -0
  145. package/dist/contracts/llm.js +2 -0
  146. package/dist/contracts/llm.js.map +1 -0
  147. package/dist/contracts/memory.d.ts +47 -0
  148. package/dist/contracts/memory.js +5 -0
  149. package/dist/contracts/memory.js.map +1 -0
  150. package/dist/contracts/vector.d.ts +29 -0
  151. package/dist/contracts/vector.js +2 -0
  152. package/dist/contracts/vector.js.map +1 -0
  153. package/dist/distill/flatten-turns.d.ts +1 -0
  154. package/dist/distill/flatten-turns.js +50 -0
  155. package/dist/distill/flatten-turns.js.map +1 -0
  156. package/dist/distill/pipeline.d.ts +45 -0
  157. package/dist/distill/pipeline.js +113 -0
  158. package/dist/distill/pipeline.js.map +1 -0
  159. package/dist/distill/prompts/extract.d.ts +122 -0
  160. package/dist/distill/prompts/extract.js +67 -0
  161. package/dist/distill/prompts/extract.js.map +1 -0
  162. package/dist/distill/stages/01-cleanup.d.ts +9 -0
  163. package/dist/distill/stages/01-cleanup.js +67 -0
  164. package/dist/distill/stages/01-cleanup.js.map +1 -0
  165. package/dist/distill/stages/02-normalize.d.ts +9 -0
  166. package/dist/distill/stages/02-normalize.js +76 -0
  167. package/dist/distill/stages/02-normalize.js.map +1 -0
  168. package/dist/distill/stages/03-chunk.d.ts +22 -0
  169. package/dist/distill/stages/03-chunk.js +138 -0
  170. package/dist/distill/stages/03-chunk.js.map +1 -0
  171. package/dist/distill/stages/04-compact.d.ts +28 -0
  172. package/dist/distill/stages/04-compact.js +69 -0
  173. package/dist/distill/stages/04-compact.js.map +1 -0
  174. package/dist/distill/stages/05-extract.d.ts +35 -0
  175. package/dist/distill/stages/05-extract.js +101 -0
  176. package/dist/distill/stages/05-extract.js.map +1 -0
  177. package/dist/distill/stages/06-reduce.d.ts +16 -0
  178. package/dist/distill/stages/06-reduce.js +30 -0
  179. package/dist/distill/stages/06-reduce.js.map +1 -0
  180. package/dist/distill/stages/07-memory-normalize.d.ts +27 -0
  181. package/dist/distill/stages/07-memory-normalize.js +65 -0
  182. package/dist/distill/stages/07-memory-normalize.js.map +1 -0
  183. package/dist/distill/stages/08-embed-index.d.ts +31 -0
  184. package/dist/distill/stages/08-embed-index.js +82 -0
  185. package/dist/distill/stages/08-embed-index.js.map +1 -0
  186. package/dist/doctor/checks.d.ts +77 -0
  187. package/dist/doctor/checks.js +626 -0
  188. package/dist/doctor/checks.js.map +1 -0
  189. package/dist/doctor/hardening-checks.d.ts +9 -0
  190. package/dist/doctor/hardening-checks.js +182 -0
  191. package/dist/doctor/hardening-checks.js.map +1 -0
  192. package/dist/doctor/probes/embed-probe.d.ts +19 -0
  193. package/dist/doctor/probes/embed-probe.js +47 -0
  194. package/dist/doctor/probes/embed-probe.js.map +1 -0
  195. package/dist/doctor/probes/llm-chat-probe.d.ts +11 -0
  196. package/dist/doctor/probes/llm-chat-probe.js +41 -0
  197. package/dist/doctor/probes/llm-chat-probe.js.map +1 -0
  198. package/dist/doctor/probes/plugin-coexistence.d.ts +14 -0
  199. package/dist/doctor/probes/plugin-coexistence.js +60 -0
  200. package/dist/doctor/probes/plugin-coexistence.js.map +1 -0
  201. package/dist/doctor/runner.d.ts +17 -0
  202. package/dist/doctor/runner.js +53 -0
  203. package/dist/doctor/runner.js.map +1 -0
  204. package/dist/doctor/types.d.ts +12 -0
  205. package/dist/doctor/types.js +2 -0
  206. package/dist/doctor/types.js.map +1 -0
  207. package/dist/entity/backfill.d.ts +30 -0
  208. package/dist/entity/backfill.js +55 -0
  209. package/dist/entity/backfill.js.map +1 -0
  210. package/dist/entity/extract-entities.d.ts +27 -0
  211. package/dist/entity/extract-entities.js +86 -0
  212. package/dist/entity/extract-entities.js.map +1 -0
  213. package/dist/entity/normalize.d.ts +17 -0
  214. package/dist/entity/normalize.js +20 -0
  215. package/dist/entity/normalize.js.map +1 -0
  216. package/dist/eval/harness.d.ts +96 -0
  217. package/dist/eval/harness.js +119 -0
  218. package/dist/eval/harness.js.map +1 -0
  219. package/dist/eval/metrics.d.ts +23 -0
  220. package/dist/eval/metrics.js +44 -0
  221. package/dist/eval/metrics.js.map +1 -0
  222. package/dist/log/correlation.d.ts +24 -0
  223. package/dist/log/correlation.js +33 -0
  224. package/dist/log/correlation.js.map +1 -0
  225. package/dist/log/logger.d.ts +38 -0
  226. package/dist/log/logger.js +129 -0
  227. package/dist/log/logger.js.map +1 -0
  228. package/dist/log/scrub.d.ts +33 -0
  229. package/dist/log/scrub.js +91 -0
  230. package/dist/log/scrub.js.map +1 -0
  231. package/dist/mcp/server.d.ts +36 -0
  232. package/dist/mcp/server.js +553 -0
  233. package/dist/mcp/server.js.map +1 -0
  234. package/dist/memory-tool/adapter.d.ts +73 -0
  235. package/dist/memory-tool/adapter.js +269 -0
  236. package/dist/memory-tool/adapter.js.map +1 -0
  237. package/dist/pipeline/errors.d.ts +21 -0
  238. package/dist/pipeline/errors.js +34 -0
  239. package/dist/pipeline/errors.js.map +1 -0
  240. package/dist/pipeline/failure-classifier.d.ts +13 -0
  241. package/dist/pipeline/failure-classifier.js +72 -0
  242. package/dist/pipeline/failure-classifier.js.map +1 -0
  243. package/dist/pipeline/handler-ctx-ext.d.ts +23 -0
  244. package/dist/pipeline/handler-ctx-ext.js +19 -0
  245. package/dist/pipeline/handler-ctx-ext.js.map +1 -0
  246. package/dist/pipeline/handler.d.ts +20 -0
  247. package/dist/pipeline/handler.js +2 -0
  248. package/dist/pipeline/handler.js.map +1 -0
  249. package/dist/pipeline/handlers/cleanup.d.ts +14 -0
  250. package/dist/pipeline/handlers/cleanup.js +47 -0
  251. package/dist/pipeline/handlers/cleanup.js.map +1 -0
  252. package/dist/pipeline/handlers/consolidate.d.ts +8 -0
  253. package/dist/pipeline/handlers/consolidate.js +23 -0
  254. package/dist/pipeline/handlers/consolidate.js.map +1 -0
  255. package/dist/pipeline/handlers/distill-events.d.ts +15 -0
  256. package/dist/pipeline/handlers/distill-events.js +134 -0
  257. package/dist/pipeline/handlers/distill-events.js.map +1 -0
  258. package/dist/pipeline/handlers/distill.d.ts +17 -0
  259. package/dist/pipeline/handlers/distill.js +110 -0
  260. package/dist/pipeline/handlers/distill.js.map +1 -0
  261. package/dist/pipeline/handlers/reembed.d.ts +10 -0
  262. package/dist/pipeline/handlers/reembed.js +34 -0
  263. package/dist/pipeline/handlers/reembed.js.map +1 -0
  264. package/dist/pipeline/job-repo.d.ts +86 -0
  265. package/dist/pipeline/job-repo.js +168 -0
  266. package/dist/pipeline/job-repo.js.map +1 -0
  267. package/dist/pipeline/mock-providers.d.ts +49 -0
  268. package/dist/pipeline/mock-providers.js +175 -0
  269. package/dist/pipeline/mock-providers.js.map +1 -0
  270. package/dist/pipeline/registry.d.ts +15 -0
  271. package/dist/pipeline/registry.js +20 -0
  272. package/dist/pipeline/registry.js.map +1 -0
  273. package/dist/pipeline/worker.d.ts +41 -0
  274. package/dist/pipeline/worker.js +167 -0
  275. package/dist/pipeline/worker.js.map +1 -0
  276. package/dist/providers/embed/azure-openai.d.ts +25 -0
  277. package/dist/providers/embed/azure-openai.js +138 -0
  278. package/dist/providers/embed/azure-openai.js.map +1 -0
  279. package/dist/providers/embed/ollama.d.ts +17 -0
  280. package/dist/providers/embed/ollama.js +106 -0
  281. package/dist/providers/embed/ollama.js.map +1 -0
  282. package/dist/providers/index.d.ts +19 -0
  283. package/dist/providers/index.js +72 -0
  284. package/dist/providers/index.js.map +1 -0
  285. package/dist/providers/llm/azure-openai.d.ts +20 -0
  286. package/dist/providers/llm/azure-openai.js +135 -0
  287. package/dist/providers/llm/azure-openai.js.map +1 -0
  288. package/dist/providers/llm/ollama.d.ts +13 -0
  289. package/dist/providers/llm/ollama.js +113 -0
  290. package/dist/providers/llm/ollama.js.map +1 -0
  291. package/dist/providers/llm/pricing.d.ts +21 -0
  292. package/dist/providers/llm/pricing.js +22 -0
  293. package/dist/providers/llm/pricing.js.map +1 -0
  294. package/dist/recall/pack.d.ts +32 -0
  295. package/dist/recall/pack.js +90 -0
  296. package/dist/recall/pack.js.map +1 -0
  297. package/dist/recall/policy.d.ts +39 -0
  298. package/dist/recall/policy.js +96 -0
  299. package/dist/recall/policy.js.map +1 -0
  300. package/dist/redact/detectors.d.ts +20 -0
  301. package/dist/redact/detectors.js +85 -0
  302. package/dist/redact/detectors.js.map +1 -0
  303. package/dist/redact/entropy.d.ts +24 -0
  304. package/dist/redact/entropy.js +77 -0
  305. package/dist/redact/entropy.js.map +1 -0
  306. package/dist/redact/index.d.ts +47 -0
  307. package/dist/redact/index.js +165 -0
  308. package/dist/redact/index.js.map +1 -0
  309. package/dist/search/fuse.d.ts +108 -0
  310. package/dist/search/fuse.js +135 -0
  311. package/dist/search/fuse.js.map +1 -0
  312. package/dist/search/query.d.ts +28 -0
  313. package/dist/search/query.js +70 -0
  314. package/dist/search/query.js.map +1 -0
  315. package/dist/search/search.d.ts +164 -0
  316. package/dist/search/search.js +310 -0
  317. package/dist/search/search.js.map +1 -0
  318. package/dist/server/app.d.ts +17 -0
  319. package/dist/server/app.js +133 -0
  320. package/dist/server/app.js.map +1 -0
  321. package/dist/server/health-state.d.ts +29 -0
  322. package/dist/server/health-state.js +28 -0
  323. package/dist/server/health-state.js.map +1 -0
  324. package/dist/server/lib/network.d.ts +12 -0
  325. package/dist/server/lib/network.js +16 -0
  326. package/dist/server/lib/network.js.map +1 -0
  327. package/dist/server/lib/score-contract.d.ts +36 -0
  328. package/dist/server/lib/score-contract.js +54 -0
  329. package/dist/server/lib/score-contract.js.map +1 -0
  330. package/dist/server/lib/stable-stringify.d.ts +10 -0
  331. package/dist/server/lib/stable-stringify.js +27 -0
  332. package/dist/server/lib/stable-stringify.js.map +1 -0
  333. package/dist/server/lib/wire-meta.d.ts +7 -0
  334. package/dist/server/lib/wire-meta.js +29 -0
  335. package/dist/server/lib/wire-meta.js.map +1 -0
  336. package/dist/server/queries/dashboard.d.ts +142 -0
  337. package/dist/server/queries/dashboard.js +166 -0
  338. package/dist/server/queries/dashboard.js.map +1 -0
  339. package/dist/server/routes/consolidation.d.ts +14 -0
  340. package/dist/server/routes/consolidation.js +67 -0
  341. package/dist/server/routes/consolidation.js.map +1 -0
  342. package/dist/server/routes/dashboard-api-html.d.ts +15 -0
  343. package/dist/server/routes/dashboard-api-html.js +144 -0
  344. package/dist/server/routes/dashboard-api-html.js.map +1 -0
  345. package/dist/server/routes/dashboard-consolidation-html.d.ts +26 -0
  346. package/dist/server/routes/dashboard-consolidation-html.js +202 -0
  347. package/dist/server/routes/dashboard-consolidation-html.js.map +1 -0
  348. package/dist/server/routes/dashboard-html.d.ts +15 -0
  349. package/dist/server/routes/dashboard-html.js +365 -0
  350. package/dist/server/routes/dashboard-html.js.map +1 -0
  351. package/dist/server/routes/dashboard-jobs-html.d.ts +18 -0
  352. package/dist/server/routes/dashboard-jobs-html.js +186 -0
  353. package/dist/server/routes/dashboard-jobs-html.js.map +1 -0
  354. package/dist/server/routes/dashboard-search-html.d.ts +18 -0
  355. package/dist/server/routes/dashboard-search-html.js +189 -0
  356. package/dist/server/routes/dashboard-search-html.js.map +1 -0
  357. package/dist/server/routes/dashboard.d.ts +19 -0
  358. package/dist/server/routes/dashboard.js +68 -0
  359. package/dist/server/routes/dashboard.js.map +1 -0
  360. package/dist/server/routes/digest.d.ts +9 -0
  361. package/dist/server/routes/digest.js +37 -0
  362. package/dist/server/routes/digest.js.map +1 -0
  363. package/dist/server/routes/entities.d.ts +12 -0
  364. package/dist/server/routes/entities.js +46 -0
  365. package/dist/server/routes/entities.js.map +1 -0
  366. package/dist/server/routes/health.d.ts +14 -0
  367. package/dist/server/routes/health.js +100 -0
  368. package/dist/server/routes/health.js.map +1 -0
  369. package/dist/server/routes/ingest.d.ts +209 -0
  370. package/dist/server/routes/ingest.js +454 -0
  371. package/dist/server/routes/ingest.js.map +1 -0
  372. package/dist/server/routes/lifecycle.d.ts +21 -0
  373. package/dist/server/routes/lifecycle.js +132 -0
  374. package/dist/server/routes/lifecycle.js.map +1 -0
  375. package/dist/server/routes/mcp.d.ts +15 -0
  376. package/dist/server/routes/mcp.js +36 -0
  377. package/dist/server/routes/mcp.js.map +1 -0
  378. package/dist/server/routes/memory-tool.d.ts +14 -0
  379. package/dist/server/routes/memory-tool.js +28 -0
  380. package/dist/server/routes/memory-tool.js.map +1 -0
  381. package/dist/server/routes/memory.d.ts +7 -0
  382. package/dist/server/routes/memory.js +19 -0
  383. package/dist/server/routes/memory.js.map +1 -0
  384. package/dist/server/routes/recall.d.ts +15 -0
  385. package/dist/server/routes/recall.js +74 -0
  386. package/dist/server/routes/recall.js.map +1 -0
  387. package/dist/server/routes/search.d.ts +12 -0
  388. package/dist/server/routes/search.js +203 -0
  389. package/dist/server/routes/search.js.map +1 -0
  390. package/dist/server/routes/version.d.ts +2 -0
  391. package/dist/server/routes/version.js +11 -0
  392. package/dist/server/routes/version.js.map +1 -0
  393. package/dist/server/routes/why.d.ts +9 -0
  394. package/dist/server/routes/why.js +38 -0
  395. package/dist/server/routes/why.js.map +1 -0
  396. package/dist/service/index.d.ts +10 -0
  397. package/dist/service/index.js +25 -0
  398. package/dist/service/index.js.map +1 -0
  399. package/dist/service/install-flow.d.ts +18 -0
  400. package/dist/service/install-flow.js +47 -0
  401. package/dist/service/install-flow.js.map +1 -0
  402. package/dist/service/instance-lock.d.ts +26 -0
  403. package/dist/service/instance-lock.js +150 -0
  404. package/dist/service/instance-lock.js.map +1 -0
  405. package/dist/service/launchd.d.ts +11 -0
  406. package/dist/service/launchd.js +196 -0
  407. package/dist/service/launchd.js.map +1 -0
  408. package/dist/service/schtasks.d.ts +31 -0
  409. package/dist/service/schtasks.js +274 -0
  410. package/dist/service/schtasks.js.map +1 -0
  411. package/dist/service/shim.d.ts +21 -0
  412. package/dist/service/shim.js +80 -0
  413. package/dist/service/shim.js.map +1 -0
  414. package/dist/service/systemd.d.ts +11 -0
  415. package/dist/service/systemd.js +150 -0
  416. package/dist/service/systemd.js.map +1 -0
  417. package/dist/service/task-xml.d.ts +36 -0
  418. package/dist/service/task-xml.js +91 -0
  419. package/dist/service/task-xml.js.map +1 -0
  420. package/dist/service/types.d.ts +47 -0
  421. package/dist/service/types.js +2 -0
  422. package/dist/service/types.js.map +1 -0
  423. package/dist/storage/archival.d.ts +29 -0
  424. package/dist/storage/archival.js +47 -0
  425. package/dist/storage/archival.js.map +1 -0
  426. package/dist/storage/bearer-keystore.d.ts +34 -0
  427. package/dist/storage/bearer-keystore.js +75 -0
  428. package/dist/storage/bearer-keystore.js.map +1 -0
  429. package/dist/storage/db.d.ts +37 -0
  430. package/dist/storage/db.js +92 -0
  431. package/dist/storage/db.js.map +1 -0
  432. package/dist/storage/entities.d.ts +71 -0
  433. package/dist/storage/entities.js +141 -0
  434. package/dist/storage/entities.js.map +1 -0
  435. package/dist/storage/ingest-idempotency.d.ts +26 -0
  436. package/dist/storage/ingest-idempotency.js +29 -0
  437. package/dist/storage/ingest-idempotency.js.map +1 -0
  438. package/dist/storage/keystore.d.ts +64 -0
  439. package/dist/storage/keystore.js +194 -0
  440. package/dist/storage/keystore.js.map +1 -0
  441. package/dist/storage/memories.d.ts +51 -0
  442. package/dist/storage/memories.js +67 -0
  443. package/dist/storage/memories.js.map +1 -0
  444. package/dist/storage/memory-events.d.ts +145 -0
  445. package/dist/storage/memory-events.js +287 -0
  446. package/dist/storage/memory-events.js.map +1 -0
  447. package/dist/storage/migrate-encrypt.d.ts +16 -0
  448. package/dist/storage/migrate-encrypt.js +121 -0
  449. package/dist/storage/migrate-encrypt.js.map +1 -0
  450. package/dist/storage/migrate.d.ts +27 -0
  451. package/dist/storage/migrate.js +105 -0
  452. package/dist/storage/migrate.js.map +1 -0
  453. package/dist/storage/redaction-log.d.ts +18 -0
  454. package/dist/storage/redaction-log.js +27 -0
  455. package/dist/storage/redaction-log.js.map +1 -0
  456. package/dist/storage/usefulness.d.ts +115 -0
  457. package/dist/storage/usefulness.js +203 -0
  458. package/dist/storage/usefulness.js.map +1 -0
  459. package/dist/sync/conflict-resolve.d.ts +26 -0
  460. package/dist/sync/conflict-resolve.js +139 -0
  461. package/dist/sync/conflict-resolve.js.map +1 -0
  462. package/dist/sync/puller.d.ts +115 -0
  463. package/dist/sync/puller.js +173 -0
  464. package/dist/sync/puller.js.map +1 -0
  465. package/dist/sync/shipper.d.ts +112 -0
  466. package/dist/sync/shipper.js +189 -0
  467. package/dist/sync/shipper.js.map +1 -0
  468. package/dist/tag-hygiene/backfill.d.ts +50 -0
  469. package/dist/tag-hygiene/backfill.js +117 -0
  470. package/dist/tag-hygiene/backfill.js.map +1 -0
  471. package/dist/tag-hygiene/derive-repo.d.ts +9 -0
  472. package/dist/tag-hygiene/derive-repo.js +19 -0
  473. package/dist/tag-hygiene/derive-repo.js.map +1 -0
  474. package/dist/tag-hygiene/tier2-infer.d.ts +28 -0
  475. package/dist/tag-hygiene/tier2-infer.js +72 -0
  476. package/dist/tag-hygiene/tier2-infer.js.map +1 -0
  477. package/dist/vector/sqlite-vec.d.ts +16 -0
  478. package/dist/vector/sqlite-vec.js +49 -0
  479. package/dist/vector/sqlite-vec.js.map +1 -0
  480. package/migrations/001-init.sql +117 -0
  481. package/migrations/002-wire-v1.sql +16 -0
  482. package/migrations/003-expand-memory-types.sql +81 -0
  483. package/migrations/004-provenance.sql +4 -0
  484. package/migrations/005-security.sql +12 -0
  485. package/migrations/006-atom-v3.sql +28 -0
  486. package/migrations/007-memory-events.sql +30 -0
  487. package/migrations/008-consolidation.sql +31 -0
  488. package/migrations/009-tag-hygiene.sql +13 -0
  489. package/migrations/010-sync-pull.sql +53 -0
  490. package/migrations/011-embed-dim-migration.sql +28 -0
  491. package/migrations/012-entities.sql +36 -0
  492. package/migrations/013-archival.sql +50 -0
  493. package/package.json +50 -0
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Scoped projection-recompute (ADR-011 multi-device conflict policy,
3
+ * FEAT-407 pull merge slice).
4
+ *
5
+ * Only atom_ids that received a NEW remote event during a pull round need
6
+ * their projection recomputed — atoms with no incoming remote event keep
7
+ * the existing single-device fast path in memory-events.ts, unchanged
8
+ * (ADR-011 "Implications for FEAT-407" point 3). This module is that
9
+ * recompute function: a deterministic, order-independent reduce over the
10
+ * FULL local event set for one atom_id, implementing conflict classes
11
+ * A (concurrent-edit), B (edit-vs-supersede), C (edit-vs-erase).
12
+ *
13
+ * Deterministic tie-break key everywhere below: (created_at, event_id) —
14
+ * created_at first, event_id (immutable, globally-unique) breaks ties. No
15
+ * device is structurally favored (ADR-011).
16
+ */
17
+ import type { DB } from '../storage/db.js';
18
+ /**
19
+ * Recompute the materialized `memories` projection for one atom_id from its
20
+ * full local event set (local ∪ pulled-remote, already deduped by
21
+ * event_id). Order-independent: computed as a set-wise reduce, not a
22
+ * fold-in-arrival-order apply, so calling this any number of times, in any
23
+ * order relative to sibling atoms, converges to the same result (ADR-011
24
+ * convergence property).
25
+ */
26
+ export declare function recomputeProjection(db: DB, atomId: string): void;
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Scoped projection-recompute (ADR-011 multi-device conflict policy,
3
+ * FEAT-407 pull merge slice).
4
+ *
5
+ * Only atom_ids that received a NEW remote event during a pull round need
6
+ * their projection recomputed — atoms with no incoming remote event keep
7
+ * the existing single-device fast path in memory-events.ts, unchanged
8
+ * (ADR-011 "Implications for FEAT-407" point 3). This module is that
9
+ * recompute function: a deterministic, order-independent reduce over the
10
+ * FULL local event set for one atom_id, implementing conflict classes
11
+ * A (concurrent-edit), B (edit-vs-supersede), C (edit-vs-erase).
12
+ *
13
+ * Deterministic tie-break key everywhere below: (created_at, event_id) —
14
+ * created_at first, event_id (immutable, globally-unique) breaks ties. No
15
+ * device is structurally favored (ADR-011).
16
+ */
17
+ import { randomUUID } from 'node:crypto';
18
+ function loadEvents(db, atomId) {
19
+ return db
20
+ .prepare('SELECT * FROM memory_events WHERE atom_id = ? ORDER BY created_at ASC, event_id ASC')
21
+ .all(atomId);
22
+ }
23
+ function rowExists(db, atomId) {
24
+ return db.prepare('SELECT 1 FROM memories WHERE id = ?').get(atomId) !== undefined;
25
+ }
26
+ /** Rule C materialization: hard-delete the memories row + its vec row, mirroring MemoryEventRepo.erase()'s row-side effect. */
27
+ function hardDeleteAtom(db, atomId) {
28
+ const row = db.prepare('SELECT rowid FROM memories WHERE id = ?').get(atomId);
29
+ if (!row)
30
+ return false;
31
+ db.prepare('DELETE FROM memories_vec WHERE rowid = ?').run(BigInt(row.rowid));
32
+ db.prepare('DELETE FROM memories WHERE id = ?').run(atomId); // FTS trigger fires
33
+ return true;
34
+ }
35
+ function supersedeTarget(e) {
36
+ const payload = e.payload_json !== null ? JSON.parse(e.payload_json) : {};
37
+ const target = payload.superseded_by;
38
+ if (!target)
39
+ throw new Error(`supersede event ${e.event_id} on atom ${e.atom_id} is missing payload.superseded_by`);
40
+ return target;
41
+ }
42
+ /** max by (created_at, event_id) — the ADR-011 canonical tie-break. */
43
+ function maxByTieBreak(events) {
44
+ return events.reduce((best, e) => {
45
+ if (e.created_at > best.created_at)
46
+ return e;
47
+ if (e.created_at < best.created_at)
48
+ return best;
49
+ return e.event_id > best.event_id ? e : best;
50
+ });
51
+ }
52
+ /**
53
+ * Class A fork resolution: append a synthetic supersede event chaining the
54
+ * losing successor atom to the winning one, idempotent on replay (checks
55
+ * for an existing conflict_merge event with the same target before
56
+ * inserting). Reuses the exact event_type/payload shape the ordinary
57
+ * supersede() path uses (ADR-011: "using the exact same event type and
58
+ * materialization path memory-events.ts already has").
59
+ */
60
+ function chainLoserToWinner(db, loserAtomId, winnerAtomId, at) {
61
+ const already = db
62
+ .prepare(`SELECT 1 FROM memory_events
63
+ WHERE atom_id = ? AND event_type = 'supersede'
64
+ AND json_extract(payload_json, '$.superseded_by') = ?
65
+ AND json_extract(payload_json, '$.reason') = 'conflict_merge'`)
66
+ .get(loserAtomId, winnerAtomId);
67
+ if (!already) {
68
+ db.prepare(`INSERT INTO memory_events (event_id, event_type, atom_id, payload_json, content_hash, created_at)
69
+ VALUES (?, 'supersede', ?, ?, NULL, ?)`).run(randomUUID(), loserAtomId, JSON.stringify({ superseded_by: winnerAtomId, reason: 'conflict_merge' }), at);
70
+ }
71
+ // Recompute the loser's own projection (not just poke its row) so the
72
+ // loser's full rule set (e.g. its own concurrent erase_request, Rule C)
73
+ // still takes precedence over this synthetic chaining — recomputeProjection
74
+ // is idempotent and safe to call from within an already-open transaction
75
+ // (better-sqlite3 nests via SAVEPOINT).
76
+ recomputeProjection(db, loserAtomId);
77
+ }
78
+ /**
79
+ * Recompute the materialized `memories` projection for one atom_id from its
80
+ * full local event set (local ∪ pulled-remote, already deduped by
81
+ * event_id). Order-independent: computed as a set-wise reduce, not a
82
+ * fold-in-arrival-order apply, so calling this any number of times, in any
83
+ * order relative to sibling atoms, converges to the same result (ADR-011
84
+ * convergence property).
85
+ */
86
+ export function recomputeProjection(db, atomId) {
87
+ const tx = db.transaction(() => {
88
+ const events = loadEvents(db, atomId);
89
+ // Rule C: erasure always wins, unconditionally, regardless of any
90
+ // concurrent invalidate/supersede on the same atom_id.
91
+ if (events.some(e => e.event_type === 'erase_request')) {
92
+ hardDeleteAtom(db, atomId);
93
+ return;
94
+ }
95
+ if (!rowExists(db, atomId)) {
96
+ // Row already gone (erased earlier by this device, or never
97
+ // materialized) — nothing left to project onto.
98
+ return;
99
+ }
100
+ const supersedes = events.filter(e => e.event_type === 'supersede');
101
+ const invalidates = events.filter(e => e.event_type === 'invalidate');
102
+ if (supersedes.length === 0 && invalidates.length === 0) {
103
+ // Only create/promote_scope/usefulness events, or archive/restore —
104
+ // none need reconciliation here. archive/restore are intentionally
105
+ // NOT synced (shipper.ts listUnsynced excludes them: archival is a
106
+ // per-device retention decision, not a semantic memory change), so
107
+ // recomputeProjection — which only ever runs on atoms that received a
108
+ // NEW REMOTE event during a pull round — never sees them.
109
+ return;
110
+ }
111
+ const now = Date.now();
112
+ if (supersedes.length > 0) {
113
+ // Rule B (supersede beats invalidate) composed with Rule A (fork
114
+ // tie-break): resolve the fork first, then supersede wins regardless
115
+ // of any concurrent invalidate — so invalidates are simply not
116
+ // consulted once any supersede event exists.
117
+ const winner = maxByTieBreak(supersedes);
118
+ const winnerTarget = supersedeTarget(winner);
119
+ const validTo = Math.min(...supersedes.map(e => e.created_at));
120
+ db.prepare('UPDATE memories SET valid_to = ?, superseded_by = ?, updated_at = ? WHERE id = ?')
121
+ .run(validTo, winnerTarget, now, atomId);
122
+ for (const loser of supersedes) {
123
+ if (loser === winner)
124
+ continue;
125
+ const loserTarget = supersedeTarget(loser);
126
+ if (loserTarget === winnerTarget)
127
+ continue; // two devices happened to pick the same successor — nothing to chain
128
+ chainLoserToWinner(db, loserTarget, winnerTarget, Math.max(loser.created_at, now));
129
+ }
130
+ }
131
+ else {
132
+ // Rule A, invalidate-vs-invalidate: order-independent, first-invalidated-wins.
133
+ const validTo = Math.min(...invalidates.map(e => e.created_at));
134
+ db.prepare('UPDATE memories SET valid_to = ?, updated_at = ? WHERE id = ?').run(validTo, now, atomId);
135
+ }
136
+ });
137
+ tx();
138
+ }
139
+ //# sourceMappingURL=conflict-resolve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conflict-resolve.js","sourceRoot":"","sources":["../../src/sync/conflict-resolve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Sync puller (FEAT-407, ADR-011) — cloud/remote-device -> local merge.
3
+ * The mirror image of shipper.ts: shipper is local -> cloud (push, ADR-003,
4
+ * one-way); this module is cloud -> local (pull, additive, no push-side
5
+ * changes — ADR-011 point (e): "synthetic conflict events ride the existing
6
+ * shipper", no new push-side code).
7
+ *
8
+ * Merge shape (ADR-011 "Implications for FEAT-407"):
9
+ * 1. dedup-insert pulled events by event_id (AC-2 idempotent replay),
10
+ * 2. scoped projection-recompute (conflict-resolve.ts) for exactly the
11
+ * atom_ids that received a NEW event this round,
12
+ * 3. persist the watermark/cursor in the SAME transaction as (1)+(2), so
13
+ * a crash mid-pull cannot advance the watermark past events that were
14
+ * not actually durably merged (AC-4: resumable, no gap, no re-pull
15
+ * from zero).
16
+ *
17
+ * WIRE CONTRACT CAVEAT (be conservative — no live SaaS repo to consult):
18
+ * astramem-sync@1 (ADR-003) ships `memory_events` metadata ONLY — no atom
19
+ * body (text/type/hash/scope). That is sufficient for push (the cloud
20
+ * ledger materializes state from events it already trusts) but a `create`
21
+ * event pulled onto a device that has never seen this atom_id has nothing
22
+ * to materialize a `memories` row FROM. Real cross-service pull will need a
23
+ * cloud-side answer to "where does the atom body come from" that this repo
24
+ * does not currently define (ADR-003 explicitly scoped pull out: "NOT in
25
+ * v1: bidirectional sync, cross-device pull"). Rather than block on that,
26
+ * this module defines `atom_snapshot` as an OPTIONAL, additive field on the
27
+ * pulled event carrying just enough of the atom body to materialize a new
28
+ * row locally. This is a LOCAL, FEAT-407-side design choice, not a verified
29
+ * live cloud contract — flagged explicitly in the FEAT-407 completion
30
+ * report. `PullFetcher` is injectable specifically so tests can simulate
31
+ * this without a real cloud.
32
+ */
33
+ import type { DB } from '../storage/db.js';
34
+ import type { MemoryScope, MemoryType } from '../contracts/memory.js';
35
+ import type { WireSyncEvent } from './shipper.js';
36
+ /** Minimal atom body needed to materialize a brand-new `memories` row on pull. See file header caveat. */
37
+ export interface AtomSnapshot {
38
+ type: MemoryType;
39
+ text: string;
40
+ normalized_text: string;
41
+ hash: string;
42
+ scope: MemoryScope;
43
+ repo?: string | null;
44
+ project?: string | null;
45
+ branch?: string | null;
46
+ agent?: string | null;
47
+ session_id?: string | null;
48
+ importance?: number;
49
+ confidence?: number;
50
+ source_hash?: string | null;
51
+ evidence?: string | null;
52
+ }
53
+ export interface RemotePullEvent extends WireSyncEvent {
54
+ /** Required on pull (unlike push, where it's additive-optional) — it is the dedup key merge correctness depends on. */
55
+ event_id: string;
56
+ /** Present only for 'create' events the sender knows the puller may not have seen yet. See file header caveat. */
57
+ atom_snapshot?: AtomSnapshot | null;
58
+ }
59
+ export interface PullBatch {
60
+ events: RemotePullEvent[];
61
+ /** Opaque resume token. null means "no further pages" (nothing beyond this batch). */
62
+ next_cursor: string | null;
63
+ }
64
+ /** Injectable — the real implementation talks to the cloud; tests supply an in-memory fake event source. */
65
+ export type PullFetcher = (cursor: string | null, limit: number) => Promise<PullBatch>;
66
+ export interface PullOpts {
67
+ db: DB;
68
+ fetcher: PullFetcher;
69
+ batchSize?: number;
70
+ }
71
+ export interface PullOnceResult {
72
+ /** Events returned by the fetcher this round (before dedup). */
73
+ fetched: number;
74
+ /** Events actually newly inserted (post dedup-by-event_id). */
75
+ inserted: number;
76
+ /** Distinct atom_ids whose projection was recomputed this round. */
77
+ atomsRecomputed: number;
78
+ /** Cursor persisted at the end of this round. */
79
+ cursor: string | null;
80
+ /** true when the fetcher returned nothing to pull. */
81
+ idle: boolean;
82
+ }
83
+ /** Current persisted pull watermark, or null if pull has never run. */
84
+ export declare function getPullWatermark(db: DB): string | null;
85
+ /**
86
+ * One pull round: fetch a batch from `cursor`, dedup-insert by event_id,
87
+ * materialize any brand-new atoms, scoped-recompute touched atoms'
88
+ * projections, and persist the new watermark — all durably: the fetch
89
+ * itself is outside any transaction (network I/O can't hold a DB lock
90
+ * across a hung socket), but the merge + watermark advance is one atomic
91
+ * transaction (AC-4: a crash between fetch and merge simply re-fetches the
92
+ * same batch next round, which is safe because merge is idempotent).
93
+ */
94
+ export declare function pullOnce(opts: PullOpts): Promise<PullOnceResult>;
95
+ export interface DrainPullResult {
96
+ rounds: number;
97
+ totalFetched: number;
98
+ totalInserted: number;
99
+ cursor: string | null;
100
+ }
101
+ /**
102
+ * Drain every available page: calls pullOnce repeatedly until the fetcher
103
+ * reports idle (or a `next_cursor` of null after a non-empty batch). Bounds
104
+ * the number of rounds so a misbehaving fetcher can't loop forever.
105
+ */
106
+ export declare function drainPull(opts: PullOpts, maxRounds?: number): Promise<DrainPullResult>;
107
+ /**
108
+ * Real HTTP pull fetcher: GET <url>/sync/pull?cursor=&limit=. NOT exercised
109
+ * against a live cloud in this slice's tests (no SaaS repo available
110
+ * locally) — see the completion report's "requires-live-cloud" section.
111
+ * Response shape assumed to mirror SyncEnvelope's event shape plus
112
+ * `next_cursor`; this is this daemon's expectation of the pull contract,
113
+ * not a verified cloud implementation.
114
+ */
115
+ export declare function httpPullFetcher(url: string, token: string, fetchImpl?: typeof fetch): PullFetcher;
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Sync puller (FEAT-407, ADR-011) — cloud/remote-device -> local merge.
3
+ * The mirror image of shipper.ts: shipper is local -> cloud (push, ADR-003,
4
+ * one-way); this module is cloud -> local (pull, additive, no push-side
5
+ * changes — ADR-011 point (e): "synthetic conflict events ride the existing
6
+ * shipper", no new push-side code).
7
+ *
8
+ * Merge shape (ADR-011 "Implications for FEAT-407"):
9
+ * 1. dedup-insert pulled events by event_id (AC-2 idempotent replay),
10
+ * 2. scoped projection-recompute (conflict-resolve.ts) for exactly the
11
+ * atom_ids that received a NEW event this round,
12
+ * 3. persist the watermark/cursor in the SAME transaction as (1)+(2), so
13
+ * a crash mid-pull cannot advance the watermark past events that were
14
+ * not actually durably merged (AC-4: resumable, no gap, no re-pull
15
+ * from zero).
16
+ *
17
+ * WIRE CONTRACT CAVEAT (be conservative — no live SaaS repo to consult):
18
+ * astramem-sync@1 (ADR-003) ships `memory_events` metadata ONLY — no atom
19
+ * body (text/type/hash/scope). That is sufficient for push (the cloud
20
+ * ledger materializes state from events it already trusts) but a `create`
21
+ * event pulled onto a device that has never seen this atom_id has nothing
22
+ * to materialize a `memories` row FROM. Real cross-service pull will need a
23
+ * cloud-side answer to "where does the atom body come from" that this repo
24
+ * does not currently define (ADR-003 explicitly scoped pull out: "NOT in
25
+ * v1: bidirectional sync, cross-device pull"). Rather than block on that,
26
+ * this module defines `atom_snapshot` as an OPTIONAL, additive field on the
27
+ * pulled event carrying just enough of the atom body to materialize a new
28
+ * row locally. This is a LOCAL, FEAT-407-side design choice, not a verified
29
+ * live cloud contract — flagged explicitly in the FEAT-407 completion
30
+ * report. `PullFetcher` is injectable specifically so tests can simulate
31
+ * this without a real cloud.
32
+ */
33
+ import { recomputeProjection } from './conflict-resolve.js';
34
+ const DEFAULT_PULL_BATCH = 200;
35
+ /** Current persisted pull watermark, or null if pull has never run. */
36
+ export function getPullWatermark(db) {
37
+ const row = db.prepare('SELECT cursor FROM sync_pull_state WHERE id = 1').get();
38
+ return row?.cursor ?? null;
39
+ }
40
+ function setPullWatermark(db, cursor, at) {
41
+ db.prepare(`INSERT INTO sync_pull_state (id, cursor, updated_at) VALUES (1, ?, ?)
42
+ ON CONFLICT(id) DO UPDATE SET cursor = excluded.cursor, updated_at = excluded.updated_at`).run(cursor, at);
43
+ }
44
+ /**
45
+ * Dedup-insert one pulled event: `INSERT ... WHERE NOT EXISTS` on event_id
46
+ * (AC-2). Returns true iff a new row was actually inserted — callers use
47
+ * this to build the scoped "touched atom_ids" set for recompute (ADR-011
48
+ * point 3: recompute is scoped, not global).
49
+ */
50
+ function dedupInsertEvent(db, event) {
51
+ const payloadJson = event.payload_json !== null ? JSON.stringify(event.payload_json) : null;
52
+ const result = db
53
+ .prepare(`INSERT INTO memory_events (event_id, event_type, atom_id, payload_json, content_hash, created_at)
54
+ SELECT ?, ?, ?, ?, ?, ?
55
+ WHERE NOT EXISTS (SELECT 1 FROM memory_events WHERE event_id = ?)`)
56
+ .run(event.event_id, event.event_type, event.atom_id, payloadJson, event.content_hash, event.created_at, event.event_id);
57
+ return result.changes > 0;
58
+ }
59
+ /**
60
+ * Materialize a brand-new `memories` row for a pulled 'create' event whose
61
+ * atom_id this device has never seen, using the atom_id from the event as
62
+ * the row id (so later events referencing this atom_id line up). No-op
63
+ * (returns false) if the row already exists (idempotent) or no snapshot was
64
+ * provided (event predates atom_snapshot support, or the sender assumed the
65
+ * receiver already had it — nothing this module can materialize from).
66
+ */
67
+ function materializeAtomIfMissing(db, atomId, snapshot, createdAt) {
68
+ const exists = db.prepare('SELECT 1 FROM memories WHERE id = ?').get(atomId);
69
+ if (exists)
70
+ return false;
71
+ const now = Date.now();
72
+ db.prepare(`INSERT INTO memories
73
+ (id, type, text, normalized_text, repo, project, branch, agent, session_id,
74
+ importance, confidence, hash, created_at, updated_at, source_hash, evidence, valid_from, scope)
75
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(atomId, snapshot.type, snapshot.text, snapshot.normalized_text, snapshot.repo ?? null, snapshot.project ?? null, snapshot.branch ?? null, snapshot.agent ?? null, snapshot.session_id ?? null, snapshot.importance ?? 0.5, snapshot.confidence ?? 0.5, snapshot.hash, createdAt, now, snapshot.source_hash ?? null, snapshot.evidence ?? null, createdAt, snapshot.scope);
76
+ return true;
77
+ }
78
+ /**
79
+ * One pull round: fetch a batch from `cursor`, dedup-insert by event_id,
80
+ * materialize any brand-new atoms, scoped-recompute touched atoms'
81
+ * projections, and persist the new watermark — all durably: the fetch
82
+ * itself is outside any transaction (network I/O can't hold a DB lock
83
+ * across a hung socket), but the merge + watermark advance is one atomic
84
+ * transaction (AC-4: a crash between fetch and merge simply re-fetches the
85
+ * same batch next round, which is safe because merge is idempotent).
86
+ */
87
+ export async function pullOnce(opts) {
88
+ const batchSize = opts.batchSize ?? DEFAULT_PULL_BATCH;
89
+ const watermark = getPullWatermark(opts.db);
90
+ const batch = await opts.fetcher(watermark, batchSize);
91
+ if (batch.events.length === 0) {
92
+ return { fetched: 0, inserted: 0, atomsRecomputed: 0, cursor: watermark, idle: true };
93
+ }
94
+ let inserted = 0;
95
+ const touchedAtomIds = new Set();
96
+ const tx = opts.db.transaction(() => {
97
+ for (const event of batch.events) {
98
+ const wasInserted = dedupInsertEvent(opts.db, event);
99
+ if (!wasInserted)
100
+ continue;
101
+ inserted++;
102
+ touchedAtomIds.add(event.atom_id);
103
+ if (event.event_type === 'create' && event.atom_snapshot) {
104
+ materializeAtomIfMissing(opts.db, event.atom_id, event.atom_snapshot, event.created_at);
105
+ }
106
+ }
107
+ for (const atomId of touchedAtomIds) {
108
+ recomputeProjection(opts.db, atomId);
109
+ }
110
+ setPullWatermark(opts.db, batch.next_cursor, Date.now());
111
+ });
112
+ tx();
113
+ return {
114
+ fetched: batch.events.length,
115
+ inserted,
116
+ atomsRecomputed: touchedAtomIds.size,
117
+ cursor: batch.next_cursor,
118
+ idle: false,
119
+ };
120
+ }
121
+ /**
122
+ * Drain every available page: calls pullOnce repeatedly until the fetcher
123
+ * reports idle (or a `next_cursor` of null after a non-empty batch). Bounds
124
+ * the number of rounds so a misbehaving fetcher can't loop forever.
125
+ */
126
+ export async function drainPull(opts, maxRounds = 1000) {
127
+ let rounds = 0;
128
+ let totalFetched = 0;
129
+ let totalInserted = 0;
130
+ let cursor = getPullWatermark(opts.db);
131
+ while (rounds < maxRounds) {
132
+ const result = await pullOnce(opts);
133
+ rounds++;
134
+ totalFetched += result.fetched;
135
+ totalInserted += result.inserted;
136
+ cursor = result.cursor;
137
+ if (result.idle || result.cursor === null)
138
+ break;
139
+ }
140
+ return { rounds, totalFetched, totalInserted, cursor };
141
+ }
142
+ /**
143
+ * Real HTTP pull fetcher: GET <url>/sync/pull?cursor=&limit=. NOT exercised
144
+ * against a live cloud in this slice's tests (no SaaS repo available
145
+ * locally) — see the completion report's "requires-live-cloud" section.
146
+ * Response shape assumed to mirror SyncEnvelope's event shape plus
147
+ * `next_cursor`; this is this daemon's expectation of the pull contract,
148
+ * not a verified cloud implementation.
149
+ */
150
+ export function httpPullFetcher(url, token, fetchImpl = fetch) {
151
+ return async (cursor, limit) => {
152
+ const qs = new URLSearchParams({ limit: String(limit) });
153
+ if (cursor !== null)
154
+ qs.set('cursor', cursor);
155
+ const res = await fetchImpl(`${url.replace(/\/$/, '')}/sync/pull?${qs.toString()}`, {
156
+ method: 'GET',
157
+ headers: { authorization: `Bearer ${token}` },
158
+ signal: AbortSignal.timeout(10_000),
159
+ });
160
+ if (!res.ok) {
161
+ throw new Error(`sync pull failed: HTTP ${res.status}`);
162
+ }
163
+ const body = (await res.json());
164
+ if (!Array.isArray(body.events)) {
165
+ throw new Error('sync pull failed: response missing events[]');
166
+ }
167
+ return {
168
+ events: body.events,
169
+ next_cursor: typeof body.next_cursor === 'string' ? body.next_cursor : null,
170
+ };
171
+ };
172
+ }
173
+ //# sourceMappingURL=puller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"puller.js","sourceRoot":"","sources":["../../src/sync/puller.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Sync shipper (Wave 3d, ADR-003) — one-way, append-only, idempotent log
3
+ * shipping: local memory_events -> cloud POST /sync/events.
4
+ *
5
+ * The log IS the queue: unsynced rows are `synced_at IS NULL`; a batch is
6
+ * acked by the cloud's `{ acked_seq }` and marked in one UPDATE. Crash-safe
7
+ * by construction — replaying a batch is always safe (cloud dedups on
8
+ * content_hash and (device_id, seq)).
9
+ *
10
+ * Scope filter (ADR-009): only events whose atom is team/org-scoped ship.
11
+ * `personal` atoms never leave the machine. erase_request events for atoms
12
+ * whose row is already hard-deleted ship when their payload carries a
13
+ * team/org scope (3f writes the scope into the erase payload for exactly
14
+ * this reason).
15
+ *
16
+ * Offline: unlimited — failures leave rows unsynced; retry with exponential
17
+ * backoff capped at BACKOFF_CAP_MS.
18
+ */
19
+ import type { DB } from '../storage/db.js';
20
+ export declare const SYNC_PROTOCOL: "astramem-sync@1";
21
+ export interface WireSyncEvent {
22
+ seq: number;
23
+ event_type: string;
24
+ atom_id: string;
25
+ payload_json: Record<string, unknown> | null;
26
+ content_hash: string | null;
27
+ created_at: number;
28
+ /**
29
+ * Globally-unique event identity (ADR-011, migration 010). Additive field
30
+ * — no astramem-sync@1 version bump (ADR-001's additive-field evolution
31
+ * rule): older push clients that omit it are still valid envelopes; this
32
+ * daemon always populates it (migration 010 backfills every pre-existing
33
+ * row, and MemoryEventRepo.append() always assigns one going forward).
34
+ * It is the pull-side dedup key (FEAT-407 AC-2) and the ADR-011 conflict
35
+ * tie-break key (created_at, event_id).
36
+ */
37
+ event_id: string;
38
+ }
39
+ export interface SyncEnvelope {
40
+ protocol: typeof SYNC_PROTOCOL;
41
+ device_id: string;
42
+ workspace_id: string;
43
+ cursor: number;
44
+ events: WireSyncEvent[];
45
+ }
46
+ export interface ShipperOpts {
47
+ db: DB;
48
+ /** Cloud base URL, e.g. https://memory.example.com — POSTs to <url>/sync/events. */
49
+ url: string;
50
+ workspaceId: string;
51
+ deviceId: string;
52
+ /** Bearer for the cloud (device token). */
53
+ token: string;
54
+ batchSize?: number;
55
+ intervalMs?: number;
56
+ /** Injectable fetch for tests. */
57
+ fetchImpl?: typeof fetch;
58
+ }
59
+ export interface ShipperHandle {
60
+ /** Stop the loop. Resolves after any in-flight ship completes. */
61
+ stop(): Promise<void>;
62
+ }
63
+ /**
64
+ * Stable per-install device id, persisted as a plain file in the config dir
65
+ * (an identifier, not a secret — the device TOKEN lives in the keystore).
66
+ */
67
+ export declare function getOrCreateDeviceId(configDir: string): string;
68
+ interface UnsyncedRow {
69
+ seq: number;
70
+ event_id: string;
71
+ event_type: string;
72
+ atom_id: string;
73
+ payload_json: string | null;
74
+ content_hash: string | null;
75
+ created_at: number;
76
+ }
77
+ /**
78
+ * Unsynced, ship-eligible events in seq order (ADR-009 scope filter).
79
+ * LEFT JOIN: an erase_request may outlive its memories row — it ships when
80
+ * its payload records a team/org scope.
81
+ *
82
+ * `archive`/`restore` (FEAT-404) are excluded unconditionally, regardless of
83
+ * scope: archival is a per-device retention/storage-hygiene decision (one
84
+ * device's local SQLite-growth mitigation), not a semantic memory change,
85
+ * so it must never leave the machine. The sync wire schema
86
+ * (contracts/schemas/sync-envelope.v1.schema.json) also never lists these
87
+ * two types in its event_type enum — shipping one would fail schema
88
+ * validation on the wire, on top of being the wrong policy.
89
+ */
90
+ export declare function listUnsynced(db: DB, batchSize: number): UnsyncedRow[];
91
+ /** Last acked seq = highest synced event seq (0 when nothing has shipped). */
92
+ export declare function lastAckedSeq(db: DB): number;
93
+ export declare function buildEnvelope(deviceId: string, workspaceId: string, cursor: number, rows: UnsyncedRow[]): SyncEnvelope;
94
+ export interface ShipOnceResult {
95
+ shipped: number;
96
+ acked: number;
97
+ /** true when there was nothing eligible to ship. */
98
+ idle: boolean;
99
+ }
100
+ /**
101
+ * One shipping round: read a batch, POST it, mark acked rows synced.
102
+ * Throws on transport/HTTP failure — the caller owns retry/backoff.
103
+ */
104
+ export declare function shipOnce(opts: ShipperOpts): Promise<ShipOnceResult>;
105
+ /** Pure backoff schedule: base * 2^failures, capped. Exported for tests. */
106
+ export declare function backoffDelay(baseMs: number, consecutiveFailures: number): number;
107
+ /**
108
+ * Start the periodic shipper loop. Ships immediately, then every intervalMs;
109
+ * on failure the next attempt is delayed by exponential backoff.
110
+ */
111
+ export declare function startShipper(opts: ShipperOpts): ShipperHandle;
112
+ export {};