@askexenow/exe-os 0.9.280 → 0.9.282

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 (436) hide show
  1. package/deploy/compose/.env.customer.example +1 -1
  2. package/deploy/compose/.env.default +2 -1
  3. package/deploy/compose/.env.example +1 -1
  4. package/deploy/compose/docker-compose.yml +74 -13
  5. package/deploy/compose/erp-nginx/nginx.conf +52 -0
  6. package/deploy/compose/generate-env.ts +1 -1
  7. package/deploy/compose/init-db.sql +13 -2
  8. package/deploy/stack-manifests/v0.9.json +11 -31
  9. package/dist/active-agent-6ZBHGHXF.js +26 -0
  10. package/dist/active-agent-E23SCYER.js +27 -0
  11. package/dist/active-agent-KMZT44S4.js +26 -0
  12. package/dist/active-agent-LFFTVROM.js +27 -0
  13. package/dist/agentic-ontology-PCZB5HV5.js +25 -0
  14. package/dist/agentic-ontology-PGGJN2ES.js +25 -0
  15. package/dist/assets/tmux.conf +2 -0
  16. package/dist/backfill-metadata-KQ4FEVUR.js +599 -0
  17. package/dist/backfill-metadata-Y3YWCHKJ.js +599 -0
  18. package/dist/behaviors-H4DZECKL.js +39 -0
  19. package/dist/behaviors-WIUTIJF6.js +39 -0
  20. package/dist/bin/agentic-ontology-backfill.js +5 -5
  21. package/dist/bin/agentic-reflection-backfill.js +6 -6
  22. package/dist/bin/agentic-semantic-label.js +5 -5
  23. package/dist/bin/backfill-conversations.js +4 -4
  24. package/dist/bin/backfill-responses.js +4 -4
  25. package/dist/bin/backfill-vectors.js +5 -5
  26. package/dist/bin/bulk-sync-postgres.js +7 -7
  27. package/dist/bin/cc-doctor.js +4 -4
  28. package/dist/bin/cleanup-stale-review-tasks.js +10 -10
  29. package/dist/bin/cli.js +15 -15
  30. package/dist/bin/exe-agent-config.js +2 -2
  31. package/dist/bin/exe-agent.js +4 -4
  32. package/dist/bin/exe-assign.js +5 -5
  33. package/dist/bin/exe-boot.js +17 -17
  34. package/dist/bin/exe-call.js +4 -4
  35. package/dist/bin/exe-cloud.js +5 -5
  36. package/dist/bin/exe-dispatch.js +10 -10
  37. package/dist/bin/exe-doctor.js +1 -1
  38. package/dist/bin/exe-export-behaviors.js +7 -7
  39. package/dist/bin/exe-forget.js +6 -6
  40. package/dist/bin/exe-gateway.js +7 -7
  41. package/dist/bin/exe-healthcheck.js +4 -4
  42. package/dist/bin/exe-heartbeat.js +10 -10
  43. package/dist/bin/exe-kill.js +13 -13
  44. package/dist/bin/exe-launch-agent.js +17 -17
  45. package/dist/bin/exe-new-employee.js +7 -7
  46. package/dist/bin/exe-pending-messages.js +11 -11
  47. package/dist/bin/exe-pending-notifications.js +10 -10
  48. package/dist/bin/exe-pending-reviews.js +10 -10
  49. package/dist/bin/exe-rename.js +4 -4
  50. package/dist/bin/exe-review.js +12 -12
  51. package/dist/bin/exe-search.js +5 -5
  52. package/dist/bin/exe-session-cleanup.js +15 -15
  53. package/dist/bin/exe-settings.js +5 -5
  54. package/dist/bin/exe-start-codex.js +11 -11
  55. package/dist/bin/exe-start-opencode.js +8 -8
  56. package/dist/bin/exe-status.js +11 -11
  57. package/dist/bin/exe-support.js +2 -2
  58. package/dist/bin/exe-team.js +3 -3
  59. package/dist/bin/exe-watchdog.js +17 -2
  60. package/dist/bin/git-sweep.js +11 -11
  61. package/dist/bin/graph-backfill.js +4 -4
  62. package/dist/bin/graph-export.js +5 -5
  63. package/dist/bin/import-history.js +7 -7
  64. package/dist/bin/install.js +8 -7
  65. package/dist/bin/intercom-check.js +4 -4
  66. package/dist/bin/mcp-sessions.js +2 -2
  67. package/dist/bin/orchestration-metrics.js +4 -4
  68. package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
  69. package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
  70. package/dist/bin/scan-tasks.js +10 -10
  71. package/dist/bin/setup.js +1 -1
  72. package/dist/bin/shard-migrate.js +4 -4
  73. package/dist/bin/stack-update.js +3 -3
  74. package/dist/bin/vps-health-gate.js +1 -1
  75. package/dist/capability-cards-F22XWGLB.js +88 -0
  76. package/dist/capability-cards-KCKITXXZ.js +88 -0
  77. package/dist/capacity-monitor-CHXGKVEO.js +50 -0
  78. package/dist/capacity-monitor-L3M5KB6T.js +50 -0
  79. package/dist/catchup-brief-BVZANLRZ.js +154 -0
  80. package/dist/catchup-brief-LDUGMC7S.js +154 -0
  81. package/dist/catchup-brief-NZDLL7SA.js +154 -0
  82. package/dist/catchup-brief-OOIXVGBA.js +154 -0
  83. package/dist/catchup-brief-UPXHDYTB.js +154 -0
  84. package/dist/chunk-2BI3FQKL.js +1876 -0
  85. package/dist/chunk-2CJUQGHH.js +362 -0
  86. package/dist/chunk-2EVYBMBJ.js +128 -0
  87. package/dist/chunk-2NQOZOXG.js +2113 -0
  88. package/dist/chunk-2QK5E3LB.js +128 -0
  89. package/dist/chunk-2VTCG4ZU.js +1352 -0
  90. package/dist/chunk-2YGI36DV.js +1119 -0
  91. package/dist/chunk-32BQN2QN.js +185 -0
  92. package/dist/chunk-33JMO4UV.js +157 -0
  93. package/dist/chunk-3DZAYLXY.js +377 -0
  94. package/dist/chunk-3MGBE7GR.js +76 -0
  95. package/dist/chunk-3MTV4FJL.js +271 -0
  96. package/dist/chunk-3NN7VQ27.js +1352 -0
  97. package/dist/chunk-4533HNOG.js +70 -0
  98. package/dist/chunk-456UW3MT.js +731 -0
  99. package/dist/chunk-4AU56XV2.js +58 -0
  100. package/dist/chunk-4MRP6EBR.js +280 -0
  101. package/dist/chunk-4MRWW52U.js +14219 -0
  102. package/dist/chunk-4WWECNAY.js +50 -0
  103. package/dist/chunk-4ZJDDR6L.js +171 -0
  104. package/dist/chunk-522EOPM6.js +382 -0
  105. package/dist/chunk-52UDAVZE.js +3208 -0
  106. package/dist/chunk-5FAMUB4X.js +204 -0
  107. package/dist/chunk-5PGZJQUI.js +58 -0
  108. package/dist/chunk-67TZJXNZ.js +262 -0
  109. package/dist/chunk-6FEZ7GN2.js +123 -0
  110. package/dist/chunk-6MOGND7S.js +14219 -0
  111. package/dist/chunk-6TRSVY7L.js +181 -0
  112. package/dist/chunk-6WCS7ZNK.js +85 -0
  113. package/dist/chunk-6WRYDREW.js +539 -0
  114. package/dist/chunk-72UA2FB3.js +181 -0
  115. package/dist/chunk-77DMFEOL.js +30 -0
  116. package/dist/chunk-7COXVQ5W.js +214 -0
  117. package/dist/chunk-7HLWBYH7.js +60 -0
  118. package/dist/chunk-ALSTZCWT.js +204 -0
  119. package/dist/chunk-ARUTDXZX.js +280 -0
  120. package/dist/chunk-AU47B4QY.js +129 -0
  121. package/dist/chunk-AXOREWVL.js +836 -0
  122. package/dist/chunk-B5MNC54V.js +127 -0
  123. package/dist/chunk-B74VSMKX.js +1350 -0
  124. package/dist/chunk-BHWLH44J.js +362 -0
  125. package/dist/chunk-BUPZ3HD2.js +85 -0
  126. package/dist/chunk-BWVLMA53.js +2113 -0
  127. package/dist/chunk-C5WLBKMJ.js +50 -0
  128. package/dist/chunk-C62T6R2A.js +97 -0
  129. package/dist/chunk-CV5KBAIK.js +33 -0
  130. package/dist/chunk-DAYSDWXA.js +1068 -0
  131. package/dist/chunk-DI4URIUB.js +227 -0
  132. package/dist/chunk-DNTCYFJ6.js +76 -0
  133. package/dist/chunk-DPDRRS7T.js +103 -0
  134. package/dist/chunk-DT3EV6CW.js +103 -0
  135. package/dist/chunk-DX45HDWY.js +1076 -0
  136. package/dist/chunk-E72BD6MG.js +284 -0
  137. package/dist/chunk-EAPUSVKS.js +375 -0
  138. package/dist/chunk-EFUANRRT.js +85 -0
  139. package/dist/chunk-EG2SCT5R.js +1352 -0
  140. package/dist/chunk-EJD2JU77.js +58 -0
  141. package/dist/chunk-EMXYUAVP.js +81 -0
  142. package/dist/chunk-ENM2TAAM.js +14219 -0
  143. package/dist/chunk-EPDRTPVP.js +1876 -0
  144. package/dist/chunk-EW6XDHID.js +221 -0
  145. package/dist/chunk-EYEGSAWZ.js +1094 -0
  146. package/dist/chunk-F6L33PAQ.js +231 -0
  147. package/dist/chunk-FC2SCTVE.js +38 -0
  148. package/dist/chunk-FSRKIZGZ.js +630 -0
  149. package/dist/chunk-FTG7I5CB.js +81 -0
  150. package/dist/chunk-GVFRLWX7.js +30 -0
  151. package/dist/chunk-H3XMZOWW.js +1119 -0
  152. package/dist/chunk-HAKXE6LN.js +123 -0
  153. package/dist/chunk-HLP3ZDTW.js +448 -0
  154. package/dist/chunk-HOYWKQAA.js +510 -0
  155. package/dist/chunk-HWDD64IW.js +712 -0
  156. package/dist/chunk-HWMCULHY.js +127 -0
  157. package/dist/chunk-HZZHRZPK.js +210 -0
  158. package/dist/chunk-ICRWTYNW.js +103 -0
  159. package/dist/chunk-J5MWPC33.js +167 -0
  160. package/dist/chunk-JBXANNNB.js +70 -0
  161. package/dist/chunk-JPBMIYWF.js +1352 -0
  162. package/dist/chunk-JY6EXBFI.js +373 -0
  163. package/dist/chunk-JZLIBXI7.js +14219 -0
  164. package/dist/chunk-JZPTKXJ6.js +668 -0
  165. package/dist/chunk-KEYMA4ZP.js +510 -0
  166. package/dist/chunk-KHGNN6GL.js +2078 -0
  167. package/dist/chunk-KVLB2PD6.js +97 -0
  168. package/dist/chunk-L3YBRBKL.js +1076 -0
  169. package/dist/chunk-L7VZ32NA.js +89 -0
  170. package/dist/chunk-LEHLADW4.js +221 -0
  171. package/dist/chunk-LPD4HILQ.js +262 -0
  172. package/dist/chunk-LSJ3ADDI.js +51 -0
  173. package/dist/chunk-LUMS2MAS.js +58 -0
  174. package/dist/chunk-LV4SEC6C.js +394 -0
  175. package/dist/chunk-LXXBEI4A.js +284 -0
  176. package/dist/chunk-M4NPCAIH.js +456 -0
  177. package/dist/chunk-MFJ62LQ5.js +157 -0
  178. package/dist/chunk-MGEZNKOD.js +836 -0
  179. package/dist/chunk-MJ7X5IBW.js +227 -0
  180. package/dist/chunk-MLGRWCY4.js +54 -0
  181. package/dist/chunk-MQZX57IY.js +348 -0
  182. package/dist/chunk-MY7MFF6J.js +103 -0
  183. package/dist/chunk-MYA2X5OY.js +185 -0
  184. package/dist/chunk-MYEABW5Z.js +630 -0
  185. package/dist/chunk-MZ2TDCAL.js +402 -0
  186. package/dist/chunk-NF3FRB7Z.js +271 -0
  187. package/dist/chunk-NXYIFEPV.js +539 -0
  188. package/dist/chunk-O2GVE5B5.js +58 -0
  189. package/dist/chunk-O5GUCDR2.js +456 -0
  190. package/dist/chunk-OBZNRECA.js +128 -0
  191. package/dist/chunk-OMZ2RLJG.js +214 -0
  192. package/dist/chunk-ORDHJRWN.js +299 -0
  193. package/dist/chunk-OSPIJMCD.js +210 -0
  194. package/dist/chunk-OUGWEH4J.js +240 -0
  195. package/dist/chunk-OV5MJQGC.js +1876 -0
  196. package/dist/chunk-PC635OAG.js +4318 -0
  197. package/dist/chunk-PH46R4J6.js +348 -0
  198. package/dist/chunk-PJP2EP7P.js +394 -0
  199. package/dist/chunk-PODFWH3V.js +333 -0
  200. package/dist/chunk-PPWH3SHR.js +1068 -0
  201. package/dist/chunk-PWPJK7KB.js +4318 -0
  202. package/dist/chunk-PXONZVG4.js +377 -0
  203. package/dist/chunk-QAOGJRZD.js +369 -0
  204. package/dist/chunk-QC3LAEI7.js +197 -0
  205. package/dist/chunk-QDWQDUWI.js +668 -0
  206. package/dist/chunk-QKJFD6BH.js +1350 -0
  207. package/dist/chunk-QNYVJGFM.js +345 -0
  208. package/dist/chunk-QQF3XGQ5.js +14219 -0
  209. package/dist/chunk-QRPFQNI3.js +150 -0
  210. package/dist/chunk-QSSU5XWD.js +731 -0
  211. package/dist/chunk-QXZAGVAV.js +2078 -0
  212. package/dist/chunk-RA54MW64.js +244 -0
  213. package/dist/chunk-RF7PUWXI.js +197 -0
  214. package/dist/chunk-S3INDYSO.js +244 -0
  215. package/dist/chunk-SLQVTHH5.js +369 -0
  216. package/dist/chunk-SY2B74KL.js +345 -0
  217. package/dist/chunk-T6QPXXXW.js +712 -0
  218. package/dist/chunk-TO5M5YCT.js +41 -0
  219. package/dist/chunk-TQ4VXUAF.js +129 -0
  220. package/dist/chunk-UAB7RQC4.js +41 -0
  221. package/dist/chunk-UMEIBDYW.js +97 -0
  222. package/dist/chunk-UXW5TB7Y.js +240 -0
  223. package/dist/chunk-VB2N5WOX.js +150 -0
  224. package/dist/chunk-VLZEMRG3.js +167 -0
  225. package/dist/chunk-VM3V6VK7.js +230 -0
  226. package/dist/chunk-VMCGKBHB.js +1352 -0
  227. package/dist/chunk-VNIYZAR5.js +128 -0
  228. package/dist/chunk-VYV4KOD2.js +85 -0
  229. package/dist/chunk-W4SRJBAT.js +171 -0
  230. package/dist/chunk-W5W3LZ3Q.js +54 -0
  231. package/dist/chunk-WDNZEOM3.js +38 -0
  232. package/dist/chunk-WUKEXVOR.js +3208 -0
  233. package/dist/chunk-X3Z35Q6L.js +373 -0
  234. package/dist/chunk-X4VOU6BQ.js +382 -0
  235. package/dist/chunk-X6EEVSVG.js +290 -0
  236. package/dist/chunk-XFHGWGNB.js +1094 -0
  237. package/dist/chunk-XKOLRWYA.js +33 -0
  238. package/dist/chunk-XMMIL3UD.js +402 -0
  239. package/dist/chunk-XWILC6VA.js +290 -0
  240. package/dist/chunk-Y4OQCX4C.js +97 -0
  241. package/dist/chunk-Y67VYYOA.js +231 -0
  242. package/dist/chunk-YGQCQTQH.js +230 -0
  243. package/dist/chunk-YGWFBN5A.js +299 -0
  244. package/dist/chunk-YMKUXZIG.js +379 -0
  245. package/dist/chunk-YOMLMT7E.js +230 -0
  246. package/dist/chunk-YPESIZOB.js +14219 -0
  247. package/dist/chunk-Z2CGCIU2.js +89 -0
  248. package/dist/chunk-ZLAWNHQR.js +448 -0
  249. package/dist/chunk-ZME5UQSN.js +333 -0
  250. package/dist/co-activation-NUEQYXE5.js +73 -0
  251. package/dist/co-activation-ZG5HLBCZ.js +73 -0
  252. package/dist/co-occurrence-7S5KWQB2.js +94 -0
  253. package/dist/co-occurrence-X5SWDXT2.js +94 -0
  254. package/dist/core-memory-GOPBRGGZ.js +110 -0
  255. package/dist/core-memory-XLCU6L5M.js +110 -0
  256. package/dist/crdt-sync-EPKHPGRZ.js +33 -0
  257. package/dist/crdt-sync-UIQJ5U7T.js +33 -0
  258. package/dist/crm-webhook-MKN23JNU.js +10 -0
  259. package/dist/crm-webhook-SM63BPXO.js +10 -0
  260. package/dist/cto-delegation-gate-PQY5TOVZ.js +279 -0
  261. package/dist/cto-delegation-gate-V5VVUR3G.js +279 -0
  262. package/dist/daemon-orchestration-C7AAS67Q.js +138 -0
  263. package/dist/daemon-orchestration-OBCAJB2H.js +138 -0
  264. package/dist/db-backup-F7VP4QRH.js +33 -0
  265. package/dist/db-backup-KVYC57W7.js +33 -0
  266. package/dist/doc-graph-extractor-H2ETEINP.js +132 -0
  267. package/dist/doc-graph-extractor-PCUZEYCH.js +132 -0
  268. package/dist/dreaming-4OZXSLE3.js +33 -0
  269. package/dist/dreaming-Z2RYEYNT.js +33 -0
  270. package/dist/exe-drift-GEWNIK7A.js +69 -0
  271. package/dist/exe-drift-XCGH7AFO.js +69 -0
  272. package/dist/exe-export-7DKAU5IP.js +75 -0
  273. package/dist/exe-export-BCHH6OE6.js +75 -0
  274. package/dist/exe-import-PDRIZVYF.js +78 -0
  275. package/dist/exe-import-ZCKUDFKL.js +78 -0
  276. package/dist/exe-key-FUWLLI3U.js +580 -0
  277. package/dist/exe-key-RKKNVUMP.js +580 -0
  278. package/dist/exe-snapshot-G4I5FQMK.js +337 -0
  279. package/dist/exe-snapshot-GWU7QTZK.js +337 -0
  280. package/dist/fast-db-init-6QG6YQNT.js +7 -0
  281. package/dist/fast-db-init-X2QDQUA4.js +7 -0
  282. package/dist/founder-context-TOMNUBGJ.js +96 -0
  283. package/dist/founder-context-UU3V6MAS.js +96 -0
  284. package/dist/gateway/index.js +8 -8
  285. package/dist/git-staleness-FEPFMZKF.js +111 -0
  286. package/dist/git-staleness-HYVYLCW3.js +111 -0
  287. package/dist/git-task-sweep-IRV52JIM.js +41 -0
  288. package/dist/git-task-sweep-T6BSM3GS.js +41 -0
  289. package/dist/global-procedures-3AURRMKO.js +21 -0
  290. package/dist/global-procedures-JPCYBZYC.js +21 -0
  291. package/dist/graph-auto-extract-OC3AOSMW.js +182 -0
  292. package/dist/graph-auto-extract-PVDYEJBY.js +182 -0
  293. package/dist/hooks/bug-report-worker.js +12 -12
  294. package/dist/hooks/codex-stop-task-finalizer.js +12 -12
  295. package/dist/hooks/commit-complete.js +12 -12
  296. package/dist/hooks/error-recall.js +6 -6
  297. package/dist/hooks/exe-heartbeat-hook.js +3 -3
  298. package/dist/hooks/ingest.js +6 -6
  299. package/dist/hooks/instructions-loaded.js +4 -4
  300. package/dist/hooks/manifest.json +19 -19
  301. package/dist/hooks/notification.js +4 -4
  302. package/dist/hooks/post-compact.js +11 -11
  303. package/dist/hooks/post-tool-combined.js +5 -5
  304. package/dist/hooks/pre-compact.js +12 -12
  305. package/dist/hooks/pre-tool-use.js +15 -15
  306. package/dist/hooks/prompt-submit.js +22 -22
  307. package/dist/hooks/session-end.js +16 -16
  308. package/dist/hooks/session-start.js +10 -10
  309. package/dist/hooks/stop.js +15 -15
  310. package/dist/hooks/subagent-stop.js +11 -11
  311. package/dist/hooks/summary-worker.js +15 -15
  312. package/dist/index.js +18 -18
  313. package/dist/installer-72XXLBRP.js +39 -0
  314. package/dist/installer-HDXG2BZN.js +343 -0
  315. package/dist/installer-JALMKPCS.js +297 -0
  316. package/dist/installer-Q46SNNLU.js +39 -0
  317. package/dist/installer-W7PIPRCX.js +343 -0
  318. package/dist/installer-Z7WQEOS7.js +297 -0
  319. package/dist/lib/cloud-sync.js +5 -5
  320. package/dist/lib/consolidation.js +5 -5
  321. package/dist/lib/database.js +2 -2
  322. package/dist/lib/db.js +2 -2
  323. package/dist/lib/employee-templates.js +4 -4
  324. package/dist/lib/employees.js +2 -2
  325. package/dist/lib/exe-daemon.js +45 -41
  326. package/dist/lib/hybrid-search.js +5 -5
  327. package/dist/lib/identity.js +2 -2
  328. package/dist/lib/license.js +1 -1
  329. package/dist/lib/messaging.js +10 -10
  330. package/dist/lib/reminders.js +3 -3
  331. package/dist/lib/schedules.js +5 -5
  332. package/dist/lib/session-registry.js +4 -4
  333. package/dist/lib/skill-learning.js +6 -6
  334. package/dist/lib/store.js +4 -4
  335. package/dist/lib/task-router.js +3 -3
  336. package/dist/lib/tasks.js +11 -11
  337. package/dist/lib/tmux-routing.js +9 -9
  338. package/dist/lib/token-spend.js +3 -3
  339. package/dist/license-gate-7JZCHOAG.js +14 -0
  340. package/dist/license-gate-OP4SKL4P.js +14 -0
  341. package/dist/mcp/register-tools.js +58 -58
  342. package/dist/mcp/server.js +59 -59
  343. package/dist/mcp/tools/complete-reminder.js +4 -4
  344. package/dist/mcp/tools/create-reminder.js +4 -4
  345. package/dist/mcp/tools/create-task.js +13 -13
  346. package/dist/mcp/tools/deactivate-behavior.js +7 -7
  347. package/dist/mcp/tools/list-reminders.js +4 -4
  348. package/dist/mcp/tools/list-tasks.js +13 -13
  349. package/dist/mcp/tools/send-message.js +12 -12
  350. package/dist/mcp/tools/update-task.js +12 -12
  351. package/dist/mcp-health-VULNT722.js +17 -0
  352. package/dist/mcp-health-WDOB6XUB.js +19 -0
  353. package/dist/mcp-http-config-2OZ7N74D.js +28 -0
  354. package/dist/mcp-http-config-4VXA5K73.js +28 -0
  355. package/dist/memory-cards-2K6QRZU6.js +179 -0
  356. package/dist/memory-cards-KSJF5OH2.js +179 -0
  357. package/dist/memory-graph-extractor-IJD5HWYT.js +21 -0
  358. package/dist/memory-graph-extractor-O4GAXOK5.js +21 -0
  359. package/dist/memory-poisoning-defense-2JRPWT5V.js +223 -0
  360. package/dist/memory-poisoning-defense-DH4A25NU.js +223 -0
  361. package/dist/memory-reflection-ISY2BBDB.js +243 -0
  362. package/dist/memory-reflection-Z5AQRR6H.js +243 -0
  363. package/dist/notifications-2VSWK2UJ.js +46 -0
  364. package/dist/notifications-4S253VQM.js +46 -0
  365. package/dist/oauth-server-D7D4574D.js +437 -0
  366. package/dist/oauth-server-MACN54SJ.js +437 -0
  367. package/dist/orchestration-events-BGP5RYQI.js +26 -0
  368. package/dist/orchestration-events-MDXUEVRZ.js +26 -0
  369. package/dist/orchestrator-DHK7RSSH.js +34 -0
  370. package/dist/orchestrator-R75WHQVA.js +34 -0
  371. package/dist/pipeline-router-4WUKQQEC.js +14 -0
  372. package/dist/pipeline-router-GWB2XK2Q.js +14 -0
  373. package/dist/plan-limits-NNJRAESF.js +27 -0
  374. package/dist/plan-limits-YTQW4UR4.js +27 -0
  375. package/dist/project-boot-46GZJTEX.js +299 -0
  376. package/dist/project-boot-PPHBBGIF.js +299 -0
  377. package/dist/projection-worker-UPAWXI7P.js +1034 -0
  378. package/dist/projection-worker-ZIKDYBW5.js +1034 -0
  379. package/dist/reranker-5ZBP2RRN.js +19 -0
  380. package/dist/reranker-E2MQIMJL.js +19 -0
  381. package/dist/reranker-GLSDJT3V.js +19 -0
  382. package/dist/reranker-LBBXWNOD.js +19 -0
  383. package/dist/reranker-XZ2EF4OH.js +19 -0
  384. package/dist/retrieval-health-JYRKPSII.js +7 -0
  385. package/dist/retrieval-health-OUV25J6S.js +7 -0
  386. package/dist/retrieval-health-U73JUAZL.js +7 -0
  387. package/dist/retrieval-health-WSZ7TYFF.js +7 -0
  388. package/dist/review-polling-62JV55ZT.js +125 -0
  389. package/dist/review-polling-CJXLWFWK.js +125 -0
  390. package/dist/runtime/index.js +12 -12
  391. package/dist/session-events-2ADD54VI.js +37 -0
  392. package/dist/session-events-QIJVBSKS.js +37 -0
  393. package/dist/session-kill-telemetry-HS6HD2YE.js +30 -0
  394. package/dist/session-kill-telemetry-MRT5FVSM.js +30 -0
  395. package/dist/session-scope-7ICYPC33.js +87 -0
  396. package/dist/session-scope-KMXD6EE6.js +87 -0
  397. package/dist/setup-wizard-B6GIT7YC.js +12 -0
  398. package/dist/setup-wizard-JUIJ4UZO.js +12 -0
  399. package/dist/skill-refinement-HIOX4VMC.js +158 -0
  400. package/dist/skill-refinement-T7JXRYUW.js +158 -0
  401. package/dist/stack-update-5KE6BZKQ.js +74 -0
  402. package/dist/stack-update-OP2RHP7N.js +74 -0
  403. package/dist/stack-update-VGCWDJEE.js +74 -0
  404. package/dist/steward-gate-L22WE3SY.js +14 -0
  405. package/dist/steward-gate-YKD2LUWN.js +14 -0
  406. package/dist/task-enforcement-5AOKXTY4.js +439 -0
  407. package/dist/task-enforcement-VO3YEGIO.js +439 -0
  408. package/dist/task-scope-YV2WPKRD.js +36 -0
  409. package/dist/task-scope-ZSXDZBRE.js +36 -0
  410. package/dist/tasks-crud-C6KADACT.js +78 -0
  411. package/dist/tasks-crud-NV6JEWGL.js +78 -0
  412. package/dist/tasks-notify-E22HSN6O.js +39 -0
  413. package/dist/tasks-notify-RPSEQ4WV.js +39 -0
  414. package/dist/tasks-review-V4ZLXOAZ.js +48 -0
  415. package/dist/tasks-review-ZVRI73JE.js +48 -0
  416. package/dist/telemetry-upload-LXUH7SKI.js +740 -0
  417. package/dist/telemetry-upload-TCDAZTUQ.js +740 -0
  418. package/dist/token-budget-OFBEZJTA.js +85 -0
  419. package/dist/token-budget-WAN57V6S.js +85 -0
  420. package/dist/tool-telemetry-UA3N32PK.js +17 -0
  421. package/dist/tool-telemetry-XXZJ35RR.js +17 -0
  422. package/dist/tui/App.js +17 -17
  423. package/dist/tui-data-46QLCJUE.js +259 -0
  424. package/dist/tui-data-ZDB7BLP2.js +259 -0
  425. package/dist/wiki-acl-HHSIBPF3.js +111 -0
  426. package/dist/wiki-acl-O65GZ2ZF.js +111 -0
  427. package/dist/worker-gate-27I4GAEZ.js +21 -0
  428. package/dist/worker-gate-DXU4HEPY.js +21 -0
  429. package/dist/workflow-engine-63EOEJ5Q.js +28 -0
  430. package/dist/workflow-engine-C6F2RMPN.js +28 -0
  431. package/dist/worktree-SFKKOMFD.js +27 -0
  432. package/dist/worktree-SVCE3S7X.js +27 -0
  433. package/dist/worktree-sweep-S3JHJTVP.js +20 -0
  434. package/dist/worktree-sweep-U3TIQ7WL.js +20 -0
  435. package/package.json +1 -1
  436. package/release-notes.json +175 -57
@@ -0,0 +1,2113 @@
1
+ import {
2
+ isCrdtSyncEnabled
3
+ } from "./chunk-EW6XDHID.js";
4
+ import {
5
+ decryptSyncBlob,
6
+ encryptSyncBlob,
7
+ initSyncCrypto,
8
+ isSyncCryptoInitialized
9
+ } from "./chunk-LO6L5ADL.js";
10
+ import {
11
+ getClient,
12
+ loadEmployees,
13
+ registerBinSymlinks,
14
+ saveEmployees
15
+ } from "./chunk-52UDAVZE.js";
16
+ import {
17
+ loadDeviceId
18
+ } from "./chunk-EAPUSVKS.js";
19
+ import {
20
+ EXE_AI_DIR
21
+ } from "./chunk-T3B5RK4H.js";
22
+ import {
23
+ atomicWriteJsonSync,
24
+ atomicWriteSync,
25
+ enforcePrivateFileSync,
26
+ ensurePrivateDirSync
27
+ } from "./chunk-LYH5HE24.js";
28
+
29
+ // src/lib/cloud-sync.ts
30
+ import { readFileSync, writeFileSync, existsSync, readdirSync, mkdirSync, appendFileSync, unlinkSync, openSync, closeSync, statSync } from "fs";
31
+ import crypto from "crypto";
32
+ import path from "path";
33
+ import { homedir } from "os";
34
+
35
+ // src/lib/compress.ts
36
+ import { brotliCompressSync, brotliDecompressSync, constants } from "zlib";
37
+ function compress(input) {
38
+ if (input.length === 0) return Buffer.alloc(0);
39
+ return brotliCompressSync(input, {
40
+ params: {
41
+ [constants.BROTLI_PARAM_QUALITY]: 4
42
+ }
43
+ });
44
+ }
45
+ function decompress(input) {
46
+ if (input.length === 0) return Buffer.alloc(0);
47
+ return brotliDecompressSync(input);
48
+ }
49
+
50
+ // src/lib/cloud-sync.ts
51
+ var _cloudSyncInProgress = false;
52
+ function sqlSafe(v) {
53
+ return v === void 0 ? null : v;
54
+ }
55
+ function sqlSafeRequired(v, fallback) {
56
+ if (v === void 0 || v === null || v === "") return fallback;
57
+ return v;
58
+ }
59
+ function logError(msg) {
60
+ try {
61
+ const logPath = path.join(homedir(), ".exe-os", "workers.log");
62
+ appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
63
+ `);
64
+ } catch (e) {
65
+ process.stderr.write("[cloud-sync] logError write failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
66
+ }
67
+ }
68
+ var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
69
+ var FETCH_TIMEOUT_MS = 3e4;
70
+ var PUSH_BATCH_SIZE = 5e3;
71
+ var ROSTER_LOCK_PATH = path.join(EXE_AI_DIR, "roster-merge.lock");
72
+ var LOCK_STALE_MS = 3e4;
73
+ var _daemonCrdtSkipLogged = false;
74
+ function shouldUseCrdtMergeForCloudSync() {
75
+ if (!isCrdtSyncEnabled()) return false;
76
+ if (process.env.EXE_IS_DAEMON === "1" && process.env.EXE_DAEMON_CRDT_SYNC !== "1") {
77
+ return false;
78
+ }
79
+ return true;
80
+ }
81
+ function logDaemonCrdtSkipOnce() {
82
+ if (_daemonCrdtSkipLogged) return;
83
+ _daemonCrdtSkipLogged = true;
84
+ process.stderr.write(
85
+ "[cloud-sync] CRDT merge disabled inside daemon; using bounded SQL upsert path. Set EXE_DAEMON_CRDT_SYNC=1 only for diagnostics.\n"
86
+ );
87
+ }
88
+ var SYNC_COOLDOWN_MS = 1e4;
89
+ var _lastSyncPushVersion = -1;
90
+ var _lastSyncPushTimestamp = 0;
91
+ var _pgPromise = null;
92
+ var _pgFailed = false;
93
+ function isTruthyEnv(value) {
94
+ return /^(1|true|yes|on)$/i.test(value ?? "");
95
+ }
96
+ function loadPgClient() {
97
+ if (_pgFailed) return null;
98
+ const configPath = path.join(EXE_AI_DIR, "config.json");
99
+ let cloudPostgresUrl;
100
+ let configEnabled = false;
101
+ try {
102
+ if (existsSync(configPath)) {
103
+ const cfg = JSON.parse(readFileSync(configPath, "utf8"));
104
+ cloudPostgresUrl = cfg.cloud?.postgresUrl;
105
+ configEnabled = cfg.cloud?.syncToPostgres === true;
106
+ }
107
+ } catch (e) {
108
+ process.stderr.write("[cloud-sync] config.json parse failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
109
+ }
110
+ const envEnabled = isTruthyEnv(process.env.EXE_CLOUD_SYNC_TO_POSTGRES);
111
+ if (!envEnabled && !configEnabled) {
112
+ return null;
113
+ }
114
+ const url = process.env.DATABASE_URL || cloudPostgresUrl;
115
+ if (!url) {
116
+ _pgFailed = true;
117
+ return null;
118
+ }
119
+ if (!_pgPromise) {
120
+ _pgPromise = (async () => {
121
+ if (!process.env.DATABASE_URL) process.env.DATABASE_URL = url;
122
+ const { createRequire } = await import("module");
123
+ const { pathToFileURL } = await import("url");
124
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
125
+ if (explicitPath) {
126
+ const mod = await import(pathToFileURL(explicitPath).href);
127
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
128
+ if (!Ctor) throw new Error(`No PrismaClient at ${explicitPath}`);
129
+ return new Ctor();
130
+ }
131
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path.join(homedir(), "exe-db");
132
+ const packagePath = path.join(exeDbRoot, "package.json");
133
+ if (existsSync(packagePath)) {
134
+ const req = createRequire(packagePath);
135
+ const entry = req.resolve("@prisma/client");
136
+ const mod = await import(pathToFileURL(entry).href);
137
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
138
+ if (!Ctor) throw new Error("No PrismaClient");
139
+ return new Ctor();
140
+ }
141
+ const { Pool } = await import("pg");
142
+ const { pgSslConfig } = await import("./pg-ssl-7JXQFL4I.js");
143
+ const pool = new Pool({
144
+ connectionString: process.env.DATABASE_URL,
145
+ ...pgSslConfig(),
146
+ max: 5,
147
+ // Limit connection pool (default 10 is too many for single-tenant)
148
+ idleTimeoutMillis: 3e4
149
+ // Close idle connections after 30s
150
+ });
151
+ return {
152
+ async $queryRawUnsafe(query, ...values) {
153
+ const result = await pool.query(query, values);
154
+ return result.rows;
155
+ },
156
+ async $executeRawUnsafe(query, ...values) {
157
+ const result = await pool.query(query, values);
158
+ return result.rowCount ?? 0;
159
+ },
160
+ async $disconnect() {
161
+ await pool.end();
162
+ }
163
+ };
164
+ })().catch((err) => {
165
+ process.stderr.write(`[cloud-sync] Postgres connection failed: ${err instanceof Error ? err.message : String(err)}
166
+ `);
167
+ _pgFailed = true;
168
+ _pgPromise = null;
169
+ throw new Error("pg_unavailable");
170
+ });
171
+ }
172
+ return _pgPromise;
173
+ }
174
+ async function disposeCloudSync() {
175
+ if (_pgPromise) {
176
+ try {
177
+ const pg = await _pgPromise;
178
+ if (pg.$disconnect) await pg.$disconnect();
179
+ } catch {
180
+ }
181
+ _pgPromise = null;
182
+ }
183
+ }
184
+ async function pushToPostgres(records) {
185
+ const loader = loadPgClient();
186
+ if (!loader) return 0;
187
+ if (!isSyncCryptoInitialized()) {
188
+ process.stderr.write("[cloud-sync] Postgres raw_events push skipped: sync crypto is not initialized; refusing plaintext Company Brain payload.\n");
189
+ return 0;
190
+ }
191
+ let prisma;
192
+ try {
193
+ prisma = await loader;
194
+ } catch (e) {
195
+ process.stderr.write("[cloud-sync] Postgres client init failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
196
+ return 0;
197
+ }
198
+ let inserted = 0;
199
+ for (const rec of records) {
200
+ try {
201
+ const encryptedPayload = encryptSyncBlob(Buffer.from(JSON.stringify(rec), "utf8"));
202
+ const changed = await prisma.$executeRawUnsafe(
203
+ `INSERT INTO raw.raw_events (id, source, source_id, event_type, payload, metadata, timestamp)
204
+ VALUES (gen_random_uuid(), 'cloud_sync', $1, 'memory', $2::jsonb, $3::jsonb, $4)
205
+ ON CONFLICT (source, source_id, event_type) DO UPDATE SET
206
+ payload = EXCLUDED.payload,
207
+ metadata = EXCLUDED.metadata,
208
+ processed_at = NULL`,
209
+ String(rec.id ?? ""),
210
+ JSON.stringify({ e2ee: true, format: "exe-sync-blob", blob: encryptedPayload }),
211
+ JSON.stringify({ e2ee: true, format: "exe-sync-blob", plaintext_metadata: false }),
212
+ rec.timestamp ? new Date(String(rec.timestamp)) : /* @__PURE__ */ new Date()
213
+ );
214
+ inserted += Number(changed ?? 0);
215
+ } catch (e) {
216
+ process.stderr.write("[cloud-sync] Postgres raw_events insert failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
217
+ }
218
+ }
219
+ return inserted;
220
+ }
221
+ async function withRosterLock(fn) {
222
+ try {
223
+ const fd = openSync(ROSTER_LOCK_PATH, "wx");
224
+ closeSync(fd);
225
+ writeFileSync(ROSTER_LOCK_PATH, String(Date.now()));
226
+ } catch (err) {
227
+ if (err.code === "EEXIST") {
228
+ try {
229
+ const ts = parseInt(readFileSync(ROSTER_LOCK_PATH, "utf-8"), 10);
230
+ if (Date.now() - ts < LOCK_STALE_MS) {
231
+ throw new Error("Roster merge already in progress \u2014 another sync is running");
232
+ }
233
+ unlinkSync(ROSTER_LOCK_PATH);
234
+ const fd = openSync(ROSTER_LOCK_PATH, "wx");
235
+ closeSync(fd);
236
+ writeFileSync(ROSTER_LOCK_PATH, String(Date.now()));
237
+ } catch (retryErr) {
238
+ if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
239
+ throw new Error("Roster merge already in progress \u2014 another sync is running");
240
+ }
241
+ } else {
242
+ throw err;
243
+ }
244
+ }
245
+ try {
246
+ return await fn();
247
+ } finally {
248
+ try {
249
+ unlinkSync(ROSTER_LOCK_PATH);
250
+ } catch (e) {
251
+ process.stderr.write("[cloud-sync] roster lock cleanup failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
252
+ }
253
+ }
254
+ }
255
+ async function fetchWithRetry(url, init) {
256
+ const MAX_RETRIES = 3;
257
+ const BASE_DELAY_MS = 200;
258
+ let lastError;
259
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
260
+ try {
261
+ const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
262
+ const resp = await fetch(url, { ...init, signal });
263
+ if (resp && resp.status >= 500 && attempt < MAX_RETRIES) {
264
+ try {
265
+ await resp.text();
266
+ } catch {
267
+ }
268
+ await new Promise((r) => setTimeout(r, BASE_DELAY_MS * Math.pow(2, attempt)));
269
+ continue;
270
+ }
271
+ return resp;
272
+ } catch (err) {
273
+ lastError = err;
274
+ if (attempt === MAX_RETRIES) throw err;
275
+ await new Promise((r) => setTimeout(r, BASE_DELAY_MS * Math.pow(2, attempt)));
276
+ }
277
+ }
278
+ throw lastError;
279
+ }
280
+ function migrateEndpoint(endpoint) {
281
+ if (endpoint === "https://askexe.com/cloud" || endpoint === "https://askexe.com/cloud/") {
282
+ process.stderr.write(
283
+ "[cloud-sync] Auto-migrating endpoint from askexe.com/cloud to api.askexe.com\n"
284
+ );
285
+ return "https://api.askexe.com";
286
+ }
287
+ if (endpoint === "https://cloud.askexe.com" || endpoint === "https://cloud.askexe.com/") {
288
+ process.stderr.write(
289
+ "[cloud-sync] Auto-migrating endpoint from cloud.askexe.com to api.askexe.com\n"
290
+ );
291
+ return "https://api.askexe.com";
292
+ }
293
+ return endpoint;
294
+ }
295
+ var PRIVATE_IP_PATTERN = /^(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|169\.254\.|0\.|fc|fd)/i;
296
+ function assertSecureEndpoint(endpoint) {
297
+ if (endpoint.startsWith("https://")) return;
298
+ if (endpoint.startsWith("http://")) {
299
+ try {
300
+ const parsed = new URL(endpoint);
301
+ if (LOCALHOST_PATTERNS.test(parsed.hostname)) return;
302
+ if (PRIVATE_IP_PATTERN.test(parsed.hostname)) {
303
+ throw new Error(
304
+ `Private network endpoint rejected: "${endpoint}". Cloud sync cannot target internal IPs (10.x, 172.16-31.x, 192.168.x, link-local).`
305
+ );
306
+ }
307
+ } catch (e) {
308
+ if (e instanceof Error && e.message.includes("rejected")) throw e;
309
+ process.stderr.write("[cloud-sync] malformed endpoint URL parse: " + (e instanceof Error ? e.message : String(e)) + "\n");
310
+ return;
311
+ }
312
+ throw new Error(
313
+ `Insecure cloud endpoint rejected: "${endpoint}". Use https:// for remote hosts. Plain http:// is only allowed for localhost.`
314
+ );
315
+ }
316
+ }
317
+ async function cloudPush(records, maxVersion, config) {
318
+ if (records.length === 0) return true;
319
+ assertSecureEndpoint(config.endpoint);
320
+ try {
321
+ const json = JSON.stringify(records);
322
+ const compressed = compress(Buffer.from(json, "utf8"));
323
+ const blob = encryptSyncBlob(compressed);
324
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/push`, {
325
+ method: "POST",
326
+ headers: {
327
+ Authorization: `Bearer ${config.apiKey}`,
328
+ "Content-Type": "application/json",
329
+ "X-Device-Id": loadDeviceId(),
330
+ "X-Expected-Version": String(maxVersion)
331
+ },
332
+ body: JSON.stringify({ version: maxVersion, blob })
333
+ });
334
+ if (resp == null) {
335
+ logError("[cloud-sync] PUSH FAILED: no response from server");
336
+ return false;
337
+ }
338
+ if (resp.status === 409) {
339
+ logError("[cloud-sync] PUSH VERSION CONFLICT \u2014 re-pull required before next push");
340
+ return false;
341
+ }
342
+ return resp.ok;
343
+ } catch (err) {
344
+ logError(`[cloud-sync] PUSH FAILED: ${err instanceof Error ? err.message : String(err)}`);
345
+ return false;
346
+ }
347
+ }
348
+ async function cloudResetMemoryBlobs(config) {
349
+ assertSecureEndpoint(config.endpoint);
350
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/reset-memory`, {
351
+ method: "POST",
352
+ headers: {
353
+ Authorization: `Bearer ${config.apiKey}`,
354
+ "Content-Type": "application/json",
355
+ "X-Device-Id": loadDeviceId()
356
+ },
357
+ body: JSON.stringify({ confirm: "LOCAL DB IS SOURCE OF TRUTH" })
358
+ });
359
+ if (!resp.ok) {
360
+ throw new Error(`cloud reset failed: HTTP ${resp.status}`);
361
+ }
362
+ const data = await resp.json();
363
+ return { deleted: Number(data.deleted ?? 0), freedBytes: Number(data.freed_bytes ?? 0) };
364
+ }
365
+ async function cloudPull(sinceVersion, config) {
366
+ assertSecureEndpoint(config.endpoint);
367
+ const PULL_PAGE_LIMIT = 1e3;
368
+ try {
369
+ const allBlobs = [];
370
+ let currentSince = sinceVersion;
371
+ let maxVersion = sinceVersion;
372
+ let hasMore = true;
373
+ while (hasMore) {
374
+ const response = await fetchWithRetry(`${config.endpoint}/sync/pull`, {
375
+ method: "POST",
376
+ headers: {
377
+ Authorization: `Bearer ${config.apiKey}`,
378
+ "Content-Type": "application/json",
379
+ "X-Device-Id": loadDeviceId()
380
+ },
381
+ body: JSON.stringify({ since_version: currentSince, limit: PULL_PAGE_LIMIT })
382
+ });
383
+ if (response == null) {
384
+ logError("[cloud-sync] PULL FAILED: no response from server");
385
+ return { records: [], maxVersion: sinceVersion };
386
+ }
387
+ if (!response.ok) return { records: [], maxVersion: sinceVersion };
388
+ const data = await response.json();
389
+ const pageBlobs = data.blobs ?? [];
390
+ allBlobs.push(...pageBlobs);
391
+ maxVersion = Math.max(maxVersion, data.max_version ?? 0);
392
+ if (pageBlobs.length < PULL_PAGE_LIMIT) {
393
+ hasMore = false;
394
+ } else {
395
+ const lastBlobVersion = pageBlobs[pageBlobs.length - 1]?.version ?? 0;
396
+ if (lastBlobVersion <= currentSince) {
397
+ hasMore = false;
398
+ } else {
399
+ currentSince = lastBlobVersion;
400
+ }
401
+ }
402
+ }
403
+ const allRecords = [];
404
+ let maxReadableVersion = sinceVersion;
405
+ let skippedBlobs = 0;
406
+ for (const { version, blob } of allBlobs) {
407
+ try {
408
+ const compressed = decryptSyncBlob(blob);
409
+ const json = decompress(compressed).toString("utf8");
410
+ const records = JSON.parse(json);
411
+ allRecords.push(...records);
412
+ const recordMax = records.reduce((max, rec) => {
413
+ const v = Number(rec.version ?? 0);
414
+ return Number.isFinite(v) ? Math.max(max, v) : max;
415
+ }, 0);
416
+ const blobVersion = Number(version ?? 0);
417
+ maxReadableVersion = Math.max(
418
+ maxReadableVersion,
419
+ Number.isFinite(blobVersion) ? blobVersion : 0,
420
+ recordMax
421
+ );
422
+ } catch (e) {
423
+ process.stderr.write("[cloud-sync] blob decrypt/decompress failed (version " + version + "): " + (e instanceof Error ? e.message : String(e)) + "\n");
424
+ skippedBlobs++;
425
+ continue;
426
+ }
427
+ }
428
+ const finalMaxVersion = maxReadableVersion;
429
+ if (skippedBlobs > 0) {
430
+ logError(`[cloud-sync] PULL skipped ${skippedBlobs} undecryptable blob(s); cursor advancing to ${finalMaxVersion} (last readable: ${maxReadableVersion})`);
431
+ }
432
+ return { records: allRecords, maxVersion: finalMaxVersion };
433
+ } catch (err) {
434
+ logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
435
+ return { records: [], maxVersion: sinceVersion };
436
+ }
437
+ }
438
+ var CLOUD_REUPLOAD_REQUIRED_MESSAGE = "Cloud sync is blocked because this device rotated its memory encryption key. Run `exe-os cloud reupload` first to re-upload the cloud backup with the new key.";
439
+ async function getCloudReuploadRequired(client = getClient()) {
440
+ try {
441
+ await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
442
+ const result = await client.execute("SELECT key, value FROM sync_meta WHERE key IN ('cloud_reupload_required', 'cloud_relink_required')");
443
+ return result.rows.some((row) => String(row.value ?? "") === "1");
444
+ } catch (e) {
445
+ const msg = e instanceof Error ? e.message : String(e);
446
+ process.stderr.write("[cloud-sync] getCloudReuploadRequired query failed (blocking sync as precaution): " + msg + "\n");
447
+ throw new Error(`[cloud-sync] Key-rotation guard check failed: ${msg}`);
448
+ }
449
+ }
450
+ async function clearCloudReuploadRequired(client = getClient()) {
451
+ await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
452
+ await client.execute("INSERT INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '0') ON CONFLICT(key) DO UPDATE SET value = excluded.value");
453
+ await client.execute("INSERT INTO sync_meta (key, value) VALUES ('cloud_relink_required', '0') ON CONFLICT(key) DO UPDATE SET value = excluded.value");
454
+ await client.execute({
455
+ sql: "INSERT INTO sync_meta (key, value) VALUES ('cloud_reuploaded_at', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value",
456
+ args: [(/* @__PURE__ */ new Date()).toISOString()]
457
+ });
458
+ await client.execute("DELETE FROM sync_meta WHERE key IN ('last_cloud_pull_version', 'last_cloud_push_version')");
459
+ }
460
+ var getCloudRelinkRequired = getCloudReuploadRequired;
461
+ async function clearCloudRelinkRequired(client = getClient()) {
462
+ await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
463
+ await client.execute("INSERT INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '0') ON CONFLICT(key) DO UPDATE SET value = excluded.value");
464
+ await client.execute("INSERT INTO sync_meta (key, value) VALUES ('cloud_relink_required', '0') ON CONFLICT(key) DO UPDATE SET value = excluded.value");
465
+ await client.execute({
466
+ sql: "INSERT INTO sync_meta (key, value) VALUES ('cloud_relinked_at', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value",
467
+ args: [(/* @__PURE__ */ new Date()).toISOString()]
468
+ });
469
+ await client.execute("DELETE FROM sync_meta WHERE key IN ('last_cloud_pull_version', 'last_cloud_push_version')");
470
+ }
471
+ async function markCloudReuploadRequired(client = getClient()) {
472
+ await client.execute("CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)");
473
+ await client.execute("INSERT INTO sync_meta (key, value) VALUES ('cloud_reupload_required', '1') ON CONFLICT(key) DO UPDATE SET value = excluded.value");
474
+ }
475
+ function defaultOnProgress(stage, detail) {
476
+ const msg = detail ? `[cloud-sync] ${stage}: ${detail}` : `[cloud-sync] ${stage}`;
477
+ process.stderr.write(msg + "\n");
478
+ }
479
+ async function cloudSync(config, options) {
480
+ const onProgress = options?.onProgress ?? defaultOnProgress;
481
+ if (_cloudSyncInProgress) {
482
+ process.stderr.write("[cloud-sync] Cloud sync already in progress \u2014 skipping this request.\n");
483
+ return { pushed: 0, pulled: 0, totalMemories: 0, behaviors: { pushed: 0, pulled: 0 }, graphrag: { pushed: 0, pulled: 0 }, tasks: { pushed: 0, pulled: 0 }, conversations: { pushed: 0, pulled: 0 }, documents: { pushed: 0, pulled: 0 }, roster: { employees: 0, identities: 0 } };
484
+ }
485
+ _cloudSyncInProgress = true;
486
+ const syncStartTime = Date.now();
487
+ try {
488
+ config = { ...config, endpoint: migrateEndpoint(config.endpoint) };
489
+ onProgress("Cloud sync", "authenticating...");
490
+ if (!isSyncCryptoInitialized()) {
491
+ try {
492
+ const { getMasterKey } = await import("./lib/keychain.js");
493
+ const masterKey = await getMasterKey();
494
+ if (masterKey) {
495
+ initSyncCrypto(masterKey);
496
+ } else {
497
+ throw new Error("No master key found");
498
+ }
499
+ } catch (err) {
500
+ throw new Error(`[cloud-sync] Cannot initialize encryption: ${err instanceof Error ? err.message : String(err)}`);
501
+ }
502
+ }
503
+ let client;
504
+ try {
505
+ client = getClient();
506
+ } catch (e) {
507
+ process.stderr.write("[cloud-sync] getClient() failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
508
+ throw new Error("[cloud-sync] Database not initialized. Call initStore() before cloudSync().");
509
+ }
510
+ try {
511
+ if (await getCloudReuploadRequired(client)) throw new Error(CLOUD_REUPLOAD_REQUIRED_MESSAGE);
512
+ } catch (err) {
513
+ const msg = err instanceof Error ? err.message : String(err);
514
+ if (msg === CLOUD_REUPLOAD_REQUIRED_MESSAGE || msg.includes("key rotation") || msg.includes("guard check failed")) throw err;
515
+ }
516
+ try {
517
+ const { getRawClient } = await import("./lib/database.js");
518
+ await getRawClient().execute("PRAGMA wal_checkpoint(PASSIVE)");
519
+ } catch (e) {
520
+ process.stderr.write("[cloud-sync] WAL checkpoint failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
521
+ }
522
+ try {
523
+ await client.execute(
524
+ "CREATE TABLE IF NOT EXISTS sync_meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)"
525
+ );
526
+ } catch (e) {
527
+ logError(`[cloud-sync] sync_meta CREATE failed: ${e instanceof Error ? e.message : String(e)}`);
528
+ }
529
+ onProgress("Cloud sync", "pulling remote changes...");
530
+ const pullMeta = await client.execute(
531
+ "SELECT value FROM sync_meta WHERE key = 'last_cloud_pull_version'"
532
+ );
533
+ const lastPullVersion = pullMeta.rows.length > 0 ? Number(pullMeta.rows[0].value) : 0;
534
+ const pullResult = await cloudPull(lastPullVersion, config);
535
+ let pulled = 0;
536
+ if (pullResult.records.length > 0) {
537
+ if (shouldUseCrdtMergeForCloudSync()) {
538
+ const { initCrdtDoc, importExistingMemories, readAllMemories } = await import("./crdt-sync-UIQJ5U7T.js");
539
+ initCrdtDoc();
540
+ importExistingMemories(
541
+ pullResult.records.map((rec) => ({
542
+ id: String(rec.id ?? ""),
543
+ agent_id: rec.agent_id,
544
+ agent_role: rec.agent_role,
545
+ session_id: rec.session_id,
546
+ timestamp: rec.timestamp,
547
+ tool_name: rec.tool_name,
548
+ project_name: rec.project_name,
549
+ has_error: rec.has_error ?? 0,
550
+ raw_text: rec.raw_text ?? "",
551
+ version: rec.version ?? 0,
552
+ author_device_id: rec.author_device_id,
553
+ scope: rec.scope ?? "business"
554
+ }))
555
+ );
556
+ const pulledIds = new Set(pullResult.records.map((r) => String(r.id ?? "")));
557
+ const merged = readAllMemories().filter((rec) => pulledIds.has(rec.id));
558
+ const stmts = merged.map((rec) => ({
559
+ sql: `INSERT INTO memories
560
+ (id, agent_id, agent_role, session_id, timestamp,
561
+ tool_name, project_name, has_error, raw_text, version,
562
+ author_device_id, scope, memory_type, session_scope)
563
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
564
+ ON CONFLICT(id) DO UPDATE SET
565
+ agent_id = excluded.agent_id,
566
+ agent_role = excluded.agent_role,
567
+ session_id = excluded.session_id,
568
+ timestamp = excluded.timestamp,
569
+ tool_name = excluded.tool_name,
570
+ project_name = excluded.project_name,
571
+ has_error = excluded.has_error,
572
+ raw_text = excluded.raw_text,
573
+ version = excluded.version,
574
+ author_device_id = excluded.author_device_id,
575
+ scope = excluded.scope,
576
+ memory_type = COALESCE(excluded.memory_type, memories.memory_type),
577
+ session_scope = COALESCE(excluded.session_scope, memories.session_scope),
578
+ vector = COALESCE(memories.vector, excluded.vector),
579
+ importance = COALESCE(memories.importance, excluded.importance),
580
+ confidence = COALESCE(memories.confidence, excluded.confidence),
581
+ tier = COALESCE(memories.tier, excluded.tier),
582
+ strength = COALESCE(memories.strength, excluded.strength)`,
583
+ args: [
584
+ sqlSafe(rec.id),
585
+ sqlSafe(rec.agent_id),
586
+ sqlSafe(rec.agent_role),
587
+ sqlSafe(rec.session_id),
588
+ sqlSafe(rec.timestamp),
589
+ sqlSafe(rec.tool_name),
590
+ sqlSafe(rec.project_name),
591
+ sqlSafe(rec.has_error ?? 0),
592
+ sqlSafe(rec.raw_text ?? ""),
593
+ sqlSafe(rec.version ?? 0),
594
+ sqlSafe(rec.author_device_id),
595
+ sqlSafe(rec.scope ?? "business"),
596
+ sqlSafe(rec.memory_type),
597
+ sqlSafe(rec.session_scope)
598
+ ]
599
+ }));
600
+ if (stmts.length > 0) await client.batch(stmts, "write");
601
+ pulled = pullResult.records.length;
602
+ } else {
603
+ if (process.env.EXE_IS_DAEMON === "1" && isCrdtSyncEnabled()) {
604
+ logDaemonCrdtSkipOnce();
605
+ }
606
+ try {
607
+ const incomingIds = pullResult.records.map((r) => sqlSafe(r.id));
608
+ if (incomingIds.length > 0) {
609
+ const ph = incomingIds.map(() => "?").join(",");
610
+ const existing = await client.execute({
611
+ sql: `SELECT id, version, timestamp FROM memories WHERE id IN (${ph})`,
612
+ args: incomingIds
613
+ });
614
+ const localMap = new Map(existing.rows.map((r) => [String(r.id), r]));
615
+ for (const rec of pullResult.records) {
616
+ const local = localMap.get(String(rec.id));
617
+ if (local && Number(local.version) > 0 && Number(local.version) !== Number(rec.version ?? 0)) {
618
+ process.stderr.write(
619
+ `[cloud-sync] CONFLICT: memory ${String(rec.id).slice(0, 8)} \u2014 local v${local.version} vs remote v${rec.version ?? 0}. Remote wins (LWW).
620
+ `
621
+ );
622
+ client.execute({
623
+ sql: `INSERT OR IGNORE INTO sync_conflicts (id, memory_id, local_version, remote_version, local_timestamp, remote_timestamp, local_device_id, resolution, created_at)
624
+ VALUES (?, ?, ?, ?, ?, ?, ?, 'remote_wins', ?)`,
625
+ args: [
626
+ `conflict-${String(rec.id).slice(0, 8)}-${Date.now()}`,
627
+ String(rec.id),
628
+ Number(local.version),
629
+ Number(rec.version ?? 0),
630
+ String(local.timestamp ?? ""),
631
+ String(rec.timestamp ?? ""),
632
+ process.env.EXE_DEVICE_ID ?? "",
633
+ (/* @__PURE__ */ new Date()).toISOString()
634
+ ]
635
+ }).catch((err) => {
636
+ process.stderr.write(`[cloud-sync] conflict audit insert failed: ${err instanceof Error ? err.message : String(err)}
637
+ `);
638
+ });
639
+ }
640
+ }
641
+ }
642
+ } catch (e) {
643
+ process.stderr.write("[cloud-sync] conflict detection query failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
644
+ }
645
+ const localDeviceId = loadDeviceId();
646
+ const deviceFilteredRecords = pullResult.records.filter((rec) => {
647
+ const authorDevice = rec.author_device_id;
648
+ if (!authorDevice) return true;
649
+ if (String(authorDevice) === localDeviceId) return true;
650
+ return true;
651
+ });
652
+ const stmts = deviceFilteredRecords.map((rec) => ({
653
+ sql: `INSERT INTO memories
654
+ (id, agent_id, agent_role, session_id, timestamp,
655
+ tool_name, project_name, has_error, raw_text, version,
656
+ author_device_id, scope, memory_type, session_scope)
657
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
658
+ ON CONFLICT(id) DO UPDATE SET
659
+ agent_id = excluded.agent_id,
660
+ agent_role = excluded.agent_role,
661
+ session_id = excluded.session_id,
662
+ timestamp = excluded.timestamp,
663
+ tool_name = excluded.tool_name,
664
+ project_name = excluded.project_name,
665
+ has_error = excluded.has_error,
666
+ raw_text = excluded.raw_text,
667
+ version = excluded.version,
668
+ author_device_id = excluded.author_device_id,
669
+ scope = excluded.scope,
670
+ memory_type = COALESCE(excluded.memory_type, memories.memory_type),
671
+ session_scope = COALESCE(excluded.session_scope, memories.session_scope),
672
+ vector = COALESCE(memories.vector, excluded.vector),
673
+ importance = COALESCE(memories.importance, excluded.importance),
674
+ confidence = COALESCE(memories.confidence, excluded.confidence),
675
+ tier = COALESCE(memories.tier, excluded.tier),
676
+ strength = COALESCE(memories.strength, excluded.strength)`,
677
+ args: [
678
+ sqlSafe(rec.id),
679
+ sqlSafeRequired(rec.agent_id, "unknown"),
680
+ sqlSafeRequired(rec.agent_role, "employee"),
681
+ sqlSafeRequired(rec.session_id, "cloud-sync"),
682
+ sqlSafeRequired(rec.timestamp, (/* @__PURE__ */ new Date()).toISOString()),
683
+ sqlSafeRequired(rec.tool_name, "cloud_sync"),
684
+ sqlSafeRequired(rec.project_name, "unknown"),
685
+ sqlSafe(rec.has_error ?? 0),
686
+ sqlSafe(rec.raw_text ?? ""),
687
+ sqlSafe(rec.version ?? 0),
688
+ sqlSafe(rec.author_device_id),
689
+ sqlSafe(rec.scope ?? "business"),
690
+ sqlSafe(rec.memory_type),
691
+ sqlSafe(rec.session_scope)
692
+ ]
693
+ }));
694
+ let syncErrors = 0;
695
+ const BATCH_SIZE = 100;
696
+ for (let i = 0; i < stmts.length; i += BATCH_SIZE) {
697
+ const batch = stmts.slice(i, i + BATCH_SIZE);
698
+ try {
699
+ await client.batch(batch, "write");
700
+ } catch (batchErr) {
701
+ for (const stmt of batch) {
702
+ try {
703
+ await client.execute(stmt);
704
+ } catch (recErr) {
705
+ syncErrors++;
706
+ process.stderr.write(
707
+ `[cloud-sync] Skipped bad record (${recErr instanceof Error ? recErr.message : String(recErr)})
708
+ `
709
+ );
710
+ }
711
+ }
712
+ }
713
+ if (i + BATCH_SIZE < stmts.length) {
714
+ await new Promise((resolve) => setImmediate(resolve));
715
+ }
716
+ }
717
+ pulled = stmts.length - syncErrors;
718
+ if (syncErrors > 0) {
719
+ process.stderr.write(`[cloud-sync] Pull completed with ${syncErrors} skipped record(s)
720
+ `);
721
+ }
722
+ }
723
+ }
724
+ if (pulled > 0) {
725
+ try {
726
+ await pushToPostgres(pullResult.records);
727
+ } catch (e) {
728
+ process.stderr.write("[cloud-sync] Postgres push of pulled records failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
729
+ }
730
+ }
731
+ if (pullResult.maxVersion > lastPullVersion) {
732
+ await client.execute({
733
+ sql: "INSERT INTO sync_meta (key, value) VALUES ('last_cloud_pull_version', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value",
734
+ args: [String(pullResult.maxVersion)]
735
+ });
736
+ }
737
+ onProgress("Cloud sync", `pushing memories...`);
738
+ const pushMeta = await client.execute(
739
+ "SELECT value FROM sync_meta WHERE key = 'last_cloud_push_version'"
740
+ );
741
+ const lastPushVersion = pushMeta.rows.length > 0 ? Number(pushMeta.rows[0].value) : 0;
742
+ let pushed = 0;
743
+ let batchCursor = lastPushVersion;
744
+ const nowMs = Date.now();
745
+ const pushCooldownActive = lastPushVersion > 0 && lastPushVersion === _lastSyncPushVersion && nowMs - _lastSyncPushTimestamp < SYNC_COOLDOWN_MS;
746
+ if (pushCooldownActive) {
747
+ } else {
748
+ while (true) {
749
+ const recordsResult = await client.execute({
750
+ sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
751
+ tool_name, project_name, has_error, raw_text, version,
752
+ author_device_id, scope, session_scope
753
+ FROM memories
754
+ WHERE version > ?
755
+ AND (scope IS NULL OR scope != 'personal')
756
+ ORDER BY version ASC
757
+ LIMIT ?`,
758
+ args: [batchCursor, PUSH_BATCH_SIZE]
759
+ });
760
+ if (recordsResult.rows.length === 0) break;
761
+ const records = recordsResult.rows.map((row) => ({
762
+ id: row.id,
763
+ agent_id: row.agent_id,
764
+ agent_role: row.agent_role,
765
+ session_id: row.session_id,
766
+ timestamp: row.timestamp,
767
+ tool_name: row.tool_name,
768
+ project_name: row.project_name,
769
+ has_error: row.has_error,
770
+ raw_text: row.raw_text,
771
+ version: row.version,
772
+ author_device_id: row.author_device_id,
773
+ scope: row.scope,
774
+ session_scope: row.session_scope
775
+ }));
776
+ const maxVersion = Number(records[records.length - 1].version);
777
+ let pushOk = await cloudPush(records, maxVersion, config);
778
+ if (!pushOk) {
779
+ await cloudPull(lastPullVersion, config);
780
+ pushOk = await cloudPush(records, maxVersion, config);
781
+ if (!pushOk) break;
782
+ }
783
+ try {
784
+ await pushToPostgres(records);
785
+ } catch (e) {
786
+ process.stderr.write("[cloud-sync] Postgres push of local records failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
787
+ }
788
+ await client.execute({
789
+ sql: "INSERT INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value",
790
+ args: [String(maxVersion)]
791
+ });
792
+ pushed += records.length;
793
+ batchCursor = maxVersion;
794
+ _lastSyncPushVersion = maxVersion;
795
+ _lastSyncPushTimestamp = Date.now();
796
+ if (recordsResult.rows.length < PUSH_BATCH_SIZE) break;
797
+ await new Promise((resolve) => setImmediate(resolve));
798
+ }
799
+ }
800
+ onProgress("Cloud sync", "merging roster...");
801
+ try {
802
+ await cloudPushRoster(config);
803
+ } catch (err) {
804
+ logError(`[cloud-sync] Roster push: ${err instanceof Error ? err.message : String(err)}`);
805
+ }
806
+ try {
807
+ await cloudPullRoster(config);
808
+ } catch (err) {
809
+ logError(`[cloud-sync] Roster pull: ${err instanceof Error ? err.message : String(err)}`);
810
+ }
811
+ try {
812
+ await cloudPushGlobalProcedures(config);
813
+ } catch (err) {
814
+ logError(`[cloud-sync] Company procedures push: ${err instanceof Error ? err.message : String(err)}`);
815
+ }
816
+ try {
817
+ await cloudPullGlobalProcedures(config);
818
+ } catch (err) {
819
+ logError(`[cloud-sync] Company procedures pull: ${err instanceof Error ? err.message : String(err)}`);
820
+ }
821
+ const countRows = async (sql) => {
822
+ try {
823
+ return Number((await client.execute(sql)).rows[0]?.cnt ?? 0);
824
+ } catch (e) {
825
+ process.stderr.write("[cloud-sync] countRows failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
826
+ return 0;
827
+ }
828
+ };
829
+ onProgress("Cloud sync", "syncing behaviors...");
830
+ const behaviorsResult = { pushed: 0, pulled: 0 };
831
+ try {
832
+ await cloudPushBehaviors(config);
833
+ behaviorsResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM behaviors WHERE active = 1");
834
+ } catch (err) {
835
+ logError(`[cloud-sync] Behaviors push: ${err instanceof Error ? err.message : String(err)}`);
836
+ }
837
+ try {
838
+ const pullResult2 = await cloudPullBehaviors(config);
839
+ behaviorsResult.pulled = pullResult2.pulled;
840
+ } catch (err) {
841
+ logError(`[cloud-sync] Behaviors pull: ${err instanceof Error ? err.message : String(err)}`);
842
+ }
843
+ onProgress("Cloud sync", "syncing graph...");
844
+ const graphragResult = { pushed: 0, pulled: 0 };
845
+ try {
846
+ await cloudPushGraphRAG(config);
847
+ graphragResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM entities");
848
+ } catch (err) {
849
+ logError(`[cloud-sync] GraphRAG push: ${err instanceof Error ? err.message : String(err)}`);
850
+ }
851
+ try {
852
+ const pullResult2 = await cloudPullGraphRAG(config);
853
+ graphragResult.pulled = pullResult2.pulled;
854
+ } catch (err) {
855
+ logError(`[cloud-sync] GraphRAG pull: ${err instanceof Error ? err.message : String(err)}`);
856
+ }
857
+ onProgress("Cloud sync", "syncing tasks...");
858
+ const tasksResult = { pushed: 0, pulled: 0 };
859
+ try {
860
+ await cloudPushTasks(config);
861
+ tasksResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM tasks");
862
+ } catch (err) {
863
+ logError(`[cloud-sync] Tasks push: ${err instanceof Error ? err.message : String(err)}`);
864
+ }
865
+ try {
866
+ const pullResult2 = await cloudPullTasks(config);
867
+ tasksResult.pulled = pullResult2.pulled;
868
+ } catch (err) {
869
+ logError(`[cloud-sync] Tasks pull: ${err instanceof Error ? err.message : String(err)}`);
870
+ }
871
+ const conversationsResult = { pushed: 0, pulled: 0 };
872
+ try {
873
+ await cloudPushConversations(config);
874
+ conversationsResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM conversations");
875
+ } catch (err) {
876
+ logError(`[cloud-sync] Conversations push: ${err instanceof Error ? err.message : String(err)}`);
877
+ }
878
+ try {
879
+ const pullResult2 = await cloudPullConversations(config);
880
+ conversationsResult.pulled = pullResult2.pulled;
881
+ } catch (err) {
882
+ logError(`[cloud-sync] Conversations pull: ${err instanceof Error ? err.message : String(err)}`);
883
+ }
884
+ const schedulesResult = { pushed: 0, pulled: 0 };
885
+ try {
886
+ await cloudPushSchedules(config);
887
+ schedulesResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM schedules");
888
+ } catch (err) {
889
+ logError(`[cloud-sync] Schedules push: ${err instanceof Error ? err.message : String(err)}`);
890
+ }
891
+ try {
892
+ const pullResult2 = await cloudPullSchedules(config);
893
+ schedulesResult.pulled = pullResult2.pulled;
894
+ } catch (err) {
895
+ logError(`[cloud-sync] Schedules pull: ${err instanceof Error ? err.message : String(err)}`);
896
+ }
897
+ const trajectoriesResult = { pushed: 0, pulled: 0 };
898
+ try {
899
+ await cloudPushTrajectories(config);
900
+ trajectoriesResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM trajectories");
901
+ } catch (err) {
902
+ logError(`[cloud-sync] Trajectories push: ${err instanceof Error ? err.message : String(err)}`);
903
+ }
904
+ try {
905
+ const pullResult2 = await cloudPullTrajectories(config);
906
+ trajectoriesResult.pulled = pullResult2.pulled;
907
+ } catch (err) {
908
+ logError(`[cloud-sync] Trajectories pull: ${err instanceof Error ? err.message : String(err)}`);
909
+ }
910
+ const consolidationsResult = { pushed: 0, pulled: 0 };
911
+ try {
912
+ await cloudPushConsolidations(config);
913
+ consolidationsResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM consolidations");
914
+ } catch (err) {
915
+ logError(`[cloud-sync] Consolidations push: ${err instanceof Error ? err.message : String(err)}`);
916
+ }
917
+ try {
918
+ const pullResult2 = await cloudPullConsolidations(config);
919
+ consolidationsResult.pulled = pullResult2.pulled;
920
+ } catch (err) {
921
+ logError(`[cloud-sync] Consolidations pull: ${err instanceof Error ? err.message : String(err)}`);
922
+ }
923
+ const identityResult = { pushed: 0, pulled: 0 };
924
+ try {
925
+ await cloudPushIdentityMeta(config);
926
+ identityResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM identity");
927
+ } catch (err) {
928
+ logError(`[cloud-sync] Identity push: ${err instanceof Error ? err.message : String(err)}`);
929
+ }
930
+ try {
931
+ const pullResult2 = await cloudPullIdentityMeta(config);
932
+ identityResult.pulled = pullResult2.pulled;
933
+ } catch (err) {
934
+ logError(`[cloud-sync] Identity pull: ${err instanceof Error ? err.message : String(err)}`);
935
+ }
936
+ const documentsResult = { pushed: 0, pulled: 0 };
937
+ try {
938
+ await cloudPushDocuments(config);
939
+ documentsResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM documents");
940
+ } catch (err) {
941
+ logError(`[cloud-sync] Documents push: ${err instanceof Error ? err.message : String(err)}`);
942
+ }
943
+ try {
944
+ const pullResult2 = await cloudPullDocuments(config);
945
+ documentsResult.pulled = pullResult2.pulled;
946
+ } catch (err) {
947
+ logError(`[cloud-sync] Documents pull: ${err instanceof Error ? err.message : String(err)}`);
948
+ }
949
+ const coreMemoryResult = { pushed: 0, pulled: 0 };
950
+ try {
951
+ await cloudPushCoreMemory(config);
952
+ coreMemoryResult.pushed = await countRows("SELECT COUNT(*) as cnt FROM core_memory");
953
+ } catch (err) {
954
+ logError(`[cloud-sync] Core memory push: ${err instanceof Error ? err.message : String(err)}`);
955
+ }
956
+ try {
957
+ const pullResult2 = await cloudPullCoreMemory(config);
958
+ coreMemoryResult.pulled = pullResult2.pulled;
959
+ } catch (err) {
960
+ logError(`[cloud-sync] Core memory pull: ${err instanceof Error ? err.message : String(err)}`);
961
+ }
962
+ const rosterResult = { employees: 0, identities: 0 };
963
+ try {
964
+ const employees = await loadEmployees();
965
+ rosterResult.employees = employees.length;
966
+ const idDir = path.join(EXE_AI_DIR, "identity");
967
+ if (existsSync(idDir)) {
968
+ rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
969
+ }
970
+ } catch (e) {
971
+ process.stderr.write("[cloud-sync] roster count failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
972
+ }
973
+ const totalMemories = await countRows("SELECT COUNT(*) as cnt FROM memories WHERE status = 'active' OR status IS NULL");
974
+ try {
975
+ const { getLatestBackup } = await import("./db-backup-F7VP4QRH.js");
976
+ const latestBackup = getLatestBackup();
977
+ if (latestBackup) {
978
+ const backupSize = statSync(latestBackup).size;
979
+ const MAX_CLOUD_BACKUP_BYTES = 50 * 1024 * 1024;
980
+ if (backupSize <= MAX_CLOUD_BACKUP_BYTES) {
981
+ const backupData = readFileSync(latestBackup);
982
+ const deviceId = loadDeviceId() ?? "unknown";
983
+ const encrypted = encryptSyncBlob(backupData);
984
+ const backupRes = await fetchWithRetry(`${config.endpoint}/sync/push-db-backup`, {
985
+ method: "POST",
986
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${config.apiKey}` },
987
+ body: JSON.stringify({
988
+ device_id: deviceId,
989
+ filename: path.basename(latestBackup),
990
+ blob: encrypted,
991
+ size: backupData.length
992
+ })
993
+ });
994
+ if (backupRes && !backupRes.ok) {
995
+ logError(`[cloud-sync] DB backup upload failed: ${backupRes.status}`);
996
+ }
997
+ }
998
+ }
999
+ } catch (err) {
1000
+ logError(`[cloud-sync] DB backup upload error: ${err instanceof Error ? err.message : String(err)}`);
1001
+ }
1002
+ const codeContextResult = { pushed: 0, pulled: 0 };
1003
+ try {
1004
+ codeContextResult.pushed = await cloudPushCodeContext(config);
1005
+ } catch (err) {
1006
+ logError(`[cloud-sync] Code context push: ${err instanceof Error ? err.message : String(err)}`);
1007
+ }
1008
+ try {
1009
+ codeContextResult.pulled = await cloudPullCodeContext(config);
1010
+ } catch (err) {
1011
+ logError(`[cloud-sync] Code context pull: ${err instanceof Error ? err.message : String(err)}`);
1012
+ }
1013
+ const elapsedSec = ((Date.now() - syncStartTime) / 1e3).toFixed(1);
1014
+ onProgress("Cloud sync", `complete (pushed ${pushed}, pulled ${pulled}, elapsed ${elapsedSec}s)`);
1015
+ return {
1016
+ pushed,
1017
+ pulled,
1018
+ totalMemories,
1019
+ behaviors: behaviorsResult,
1020
+ graphrag: graphragResult,
1021
+ tasks: tasksResult,
1022
+ conversations: conversationsResult,
1023
+ documents: documentsResult,
1024
+ coreMemory: coreMemoryResult,
1025
+ roster: rosterResult,
1026
+ codeContext: codeContextResult,
1027
+ elapsedMs: Date.now() - syncStartTime
1028
+ };
1029
+ } finally {
1030
+ _cloudSyncInProgress = false;
1031
+ }
1032
+ }
1033
+ var ROSTER_DELETIONS_PATH = path.join(EXE_AI_DIR, "roster-deletions.json");
1034
+ function recordRosterDeletion(name) {
1035
+ let deletions = [];
1036
+ try {
1037
+ if (existsSync(ROSTER_DELETIONS_PATH)) {
1038
+ deletions = JSON.parse(readFileSync(ROSTER_DELETIONS_PATH, "utf-8"));
1039
+ }
1040
+ } catch (e) {
1041
+ process.stderr.write("[cloud-sync] roster-deletions.json read failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1042
+ }
1043
+ if (!deletions.includes(name)) deletions.push(name);
1044
+ atomicWriteJsonSync(ROSTER_DELETIONS_PATH, deletions);
1045
+ }
1046
+ function consumeRosterDeletions() {
1047
+ try {
1048
+ if (!existsSync(ROSTER_DELETIONS_PATH)) return [];
1049
+ const deletions = JSON.parse(readFileSync(ROSTER_DELETIONS_PATH, "utf-8"));
1050
+ atomicWriteJsonSync(ROSTER_DELETIONS_PATH, []);
1051
+ return deletions;
1052
+ } catch (e) {
1053
+ process.stderr.write("[cloud-sync] consumeRosterDeletions failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1054
+ return [];
1055
+ }
1056
+ }
1057
+ function buildRosterBlob(paths) {
1058
+ const rosterPath = paths?.rosterPath ?? path.join(EXE_AI_DIR, "exe-employees.json");
1059
+ const identityDir = paths?.identityDir ?? path.join(EXE_AI_DIR, "identity");
1060
+ const configPath = paths?.configPath ?? path.join(EXE_AI_DIR, "config.json");
1061
+ let roster = [];
1062
+ if (existsSync(rosterPath)) {
1063
+ try {
1064
+ roster = JSON.parse(readFileSync(rosterPath, "utf-8"));
1065
+ } catch (e) {
1066
+ process.stderr.write("[cloud-sync] roster file parse failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1067
+ }
1068
+ }
1069
+ const identities = {};
1070
+ if (existsSync(identityDir)) {
1071
+ for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
1072
+ try {
1073
+ identities[file] = readFileSync(path.join(identityDir, file), "utf-8");
1074
+ } catch (e) {
1075
+ process.stderr.write("[cloud-sync] identity file read failed (" + file + "): " + (e instanceof Error ? e.message : String(e)) + "\n");
1076
+ }
1077
+ }
1078
+ }
1079
+ let config;
1080
+ if (existsSync(configPath)) {
1081
+ try {
1082
+ config = JSON.parse(readFileSync(configPath, "utf-8"));
1083
+ } catch (e) {
1084
+ process.stderr.write("[cloud-sync] config.json read failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1085
+ }
1086
+ }
1087
+ let agentConfig;
1088
+ const agentConfigPath = path.join(EXE_AI_DIR, "agent-config.json");
1089
+ if (existsSync(agentConfigPath)) {
1090
+ try {
1091
+ agentConfig = JSON.parse(readFileSync(agentConfigPath, "utf-8"));
1092
+ } catch (e) {
1093
+ process.stderr.write("[cloud-sync] agent-config.json read failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1094
+ }
1095
+ }
1096
+ const deletedNames = consumeRosterDeletions();
1097
+ const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
1098
+ const hash = crypto.createHash("sha256").update(content).digest("hex").slice(0, 16);
1099
+ return { roster, identities, config, agentConfig, deletedNames, version: hash };
1100
+ }
1101
+ async function cloudPushRoster(config) {
1102
+ assertSecureEndpoint(config.endpoint);
1103
+ const blob = buildRosterBlob();
1104
+ if (blob.roster.length === 0) return true;
1105
+ try {
1106
+ const client = getClient();
1107
+ const meta = await client.execute(
1108
+ "SELECT value FROM sync_meta WHERE key = 'last_roster_push_version'"
1109
+ );
1110
+ const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
1111
+ if (blob.version === lastVersion) return true;
1112
+ } catch (e) {
1113
+ process.stderr.write("[cloud-sync] roster push version check failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1114
+ }
1115
+ try {
1116
+ const json = JSON.stringify(blob);
1117
+ const compressed = compress(Buffer.from(json, "utf8"));
1118
+ const encrypted = encryptSyncBlob(compressed);
1119
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/push-roster`, {
1120
+ method: "POST",
1121
+ headers: {
1122
+ Authorization: `Bearer ${config.apiKey}`,
1123
+ "Content-Type": "application/json",
1124
+ "X-Device-Id": loadDeviceId()
1125
+ },
1126
+ body: JSON.stringify({ blob: encrypted })
1127
+ });
1128
+ if (resp.ok) {
1129
+ try {
1130
+ const client = getClient();
1131
+ await client.execute({
1132
+ sql: "INSERT INTO sync_meta (key, value) VALUES ('last_roster_push_version', ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value",
1133
+ args: [String(blob.version)]
1134
+ });
1135
+ } catch (e) {
1136
+ process.stderr.write("[cloud-sync] roster push version update failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1137
+ }
1138
+ }
1139
+ return resp.ok;
1140
+ } catch (err) {
1141
+ process.stderr.write(`[cloud-sync] ROSTER PUSH FAILED: ${err instanceof Error ? err.message : String(err)}
1142
+ `);
1143
+ return false;
1144
+ }
1145
+ }
1146
+ async function cloudPullRoster(config) {
1147
+ assertSecureEndpoint(config.endpoint);
1148
+ try {
1149
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/pull-roster`, {
1150
+ method: "GET",
1151
+ headers: {
1152
+ Authorization: `Bearer ${config.apiKey}`,
1153
+ "X-Device-Id": loadDeviceId()
1154
+ }
1155
+ });
1156
+ if (!resp.ok) return { added: 0 };
1157
+ const data = await resp.json();
1158
+ if (!data.blob) return { added: 0 };
1159
+ const compressed = decryptSyncBlob(data.blob);
1160
+ const json = decompress(compressed).toString("utf8");
1161
+ const remote = JSON.parse(json);
1162
+ return mergeRosterFromRemote(remote);
1163
+ } catch (err) {
1164
+ process.stderr.write(`[cloud-sync] ROSTER PULL FAILED: ${err instanceof Error ? err.message : String(err)}
1165
+ `);
1166
+ return { added: 0 };
1167
+ }
1168
+ }
1169
+ function mergeConfig(remoteConfig, configPath) {
1170
+ const cfgPath = configPath ?? path.join(EXE_AI_DIR, "config.json");
1171
+ let local = {};
1172
+ if (existsSync(cfgPath)) {
1173
+ try {
1174
+ local = JSON.parse(readFileSync(cfgPath, "utf-8"));
1175
+ } catch (e) {
1176
+ process.stderr.write("[cloud-sync] mergeConfig local config parse failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1177
+ }
1178
+ }
1179
+ const merged = { ...remoteConfig, ...local };
1180
+ const dir = path.dirname(cfgPath);
1181
+ ensurePrivateDirSync(dir);
1182
+ atomicWriteSync(cfgPath, JSON.stringify(merged, null, 2) + "\n");
1183
+ enforcePrivateFileSync(cfgPath);
1184
+ }
1185
+ async function mergeRosterFromRemote(remote, paths) {
1186
+ return withRosterLock(async () => {
1187
+ const rosterPath = paths?.rosterPath ?? void 0;
1188
+ const identityDir = paths?.identityDir ?? path.join(EXE_AI_DIR, "identity");
1189
+ const localEmployees = await loadEmployees(rosterPath);
1190
+ const localNames = new Set(localEmployees.map((e) => e.name));
1191
+ let added = 0;
1192
+ let identitiesUpdated = 0;
1193
+ for (const remoteEmp of remote.roster) {
1194
+ if (!localNames.has(remoteEmp.name)) {
1195
+ localEmployees.push(remoteEmp);
1196
+ localNames.add(remoteEmp.name);
1197
+ added++;
1198
+ try {
1199
+ registerBinSymlinks(remoteEmp.name);
1200
+ } catch (e) {
1201
+ process.stderr.write("[cloud-sync] bin symlink registration failed for " + remoteEmp.name + ": " + (e instanceof Error ? e.message : String(e)) + "\n");
1202
+ }
1203
+ }
1204
+ const lookupKey = `${remoteEmp.name}.md`;
1205
+ const matchedKey = Object.keys(remote.identities).find(
1206
+ (k) => k.toLowerCase() === lookupKey.toLowerCase()
1207
+ ) ?? lookupKey;
1208
+ const remoteIdentity = remote.identities[matchedKey];
1209
+ if (remoteIdentity) {
1210
+ if (!existsSync(identityDir)) mkdirSync(identityDir, { recursive: true });
1211
+ const idPath = path.join(identityDir, `${remoteEmp.name}.md`);
1212
+ let localIdentity = null;
1213
+ try {
1214
+ localIdentity = existsSync(idPath) ? readFileSync(idPath, "utf-8") : null;
1215
+ } catch (e) {
1216
+ process.stderr.write("[cloud-sync] local identity file read failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1217
+ }
1218
+ if (localIdentity !== remoteIdentity) {
1219
+ atomicWriteSync(idPath, remoteIdentity);
1220
+ identitiesUpdated++;
1221
+ }
1222
+ }
1223
+ }
1224
+ let removed = 0;
1225
+ if (remote.deletedNames && remote.deletedNames.length > 0) {
1226
+ const toRemove = new Set(remote.deletedNames);
1227
+ const filtered = localEmployees.filter((e) => !toRemove.has(e.name));
1228
+ removed = localEmployees.length - filtered.length;
1229
+ if (removed > 0) {
1230
+ localEmployees.length = 0;
1231
+ localEmployees.push(...filtered);
1232
+ }
1233
+ }
1234
+ if (added > 0 || removed > 0) {
1235
+ await saveEmployees(localEmployees, rosterPath);
1236
+ }
1237
+ if (remote.config && Object.keys(remote.config).length > 0) {
1238
+ try {
1239
+ mergeConfig(remote.config, paths?.configPath);
1240
+ } catch (e) {
1241
+ process.stderr.write("[cloud-sync] config merge failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1242
+ }
1243
+ }
1244
+ if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
1245
+ try {
1246
+ const agentConfigPath = path.join(EXE_AI_DIR, "agent-config.json");
1247
+ let local = {};
1248
+ if (existsSync(agentConfigPath)) {
1249
+ try {
1250
+ local = JSON.parse(readFileSync(agentConfigPath, "utf-8"));
1251
+ } catch (e) {
1252
+ process.stderr.write("[cloud-sync] agent-config.json parse failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1253
+ }
1254
+ }
1255
+ const merged = { ...remote.agentConfig, ...local };
1256
+ ensurePrivateDirSync(path.dirname(agentConfigPath));
1257
+ atomicWriteSync(agentConfigPath, JSON.stringify(merged, null, 2) + "\n");
1258
+ enforcePrivateFileSync(agentConfigPath);
1259
+ } catch (e) {
1260
+ process.stderr.write("[cloud-sync] agent-config merge failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1261
+ }
1262
+ }
1263
+ return { added, identitiesUpdated };
1264
+ });
1265
+ }
1266
+ async function cloudPushBlob(route, data, metaKey, config) {
1267
+ if (data.length === 0) return { ok: true };
1268
+ assertSecureEndpoint(config.endpoint);
1269
+ const json = JSON.stringify(data);
1270
+ const version = parseInt(crypto.createHash("sha256").update(json).digest("hex").slice(0, 12), 16);
1271
+ try {
1272
+ const client = getClient();
1273
+ const meta = await client.execute({
1274
+ sql: "SELECT value FROM sync_meta WHERE key = ?",
1275
+ args: [metaKey]
1276
+ });
1277
+ const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
1278
+ if (version === lastVersion) return { ok: true };
1279
+ } catch (e) {
1280
+ process.stderr.write("[cloud-sync] pushBlob version check failed (" + metaKey + "): " + (e instanceof Error ? e.message : String(e)) + "\n");
1281
+ }
1282
+ try {
1283
+ const compressed = compress(Buffer.from(json, "utf8"));
1284
+ const encrypted = encryptSyncBlob(compressed);
1285
+ const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
1286
+ method: "POST",
1287
+ headers: {
1288
+ Authorization: `Bearer ${config.apiKey}`,
1289
+ "Content-Type": "application/json",
1290
+ "X-Device-Id": loadDeviceId()
1291
+ },
1292
+ body: JSON.stringify({ blob: encrypted })
1293
+ });
1294
+ if (resp.ok) {
1295
+ try {
1296
+ const client = getClient();
1297
+ await client.execute({
1298
+ sql: "INSERT INTO sync_meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value",
1299
+ args: [metaKey, String(version)]
1300
+ });
1301
+ } catch (e) {
1302
+ process.stderr.write("[cloud-sync] pushBlob version update failed (" + metaKey + "): " + (e instanceof Error ? e.message : String(e)) + "\n");
1303
+ }
1304
+ }
1305
+ return { ok: resp.ok };
1306
+ } catch (err) {
1307
+ logError(`[cloud-sync] PUSH ${route}: ${err instanceof Error ? err.message : String(err)}`);
1308
+ return { ok: false };
1309
+ }
1310
+ }
1311
+ async function cloudPullBlob(route, config) {
1312
+ assertSecureEndpoint(config.endpoint);
1313
+ try {
1314
+ const resp = await fetchWithRetry(`${config.endpoint}${route}`, {
1315
+ method: "GET",
1316
+ headers: {
1317
+ Authorization: `Bearer ${config.apiKey}`,
1318
+ "X-Device-Id": loadDeviceId()
1319
+ }
1320
+ });
1321
+ if (!resp.ok) return null;
1322
+ const data = await resp.json();
1323
+ if (!data.blob) return null;
1324
+ const compressed = decryptSyncBlob(data.blob);
1325
+ const json = decompress(compressed).toString("utf8");
1326
+ return JSON.parse(json);
1327
+ } catch (err) {
1328
+ logError(`[cloud-sync] PULL ${route}: ${err instanceof Error ? err.message : String(err)}`);
1329
+ return null;
1330
+ }
1331
+ }
1332
+ async function cloudPushGlobalProcedures(config) {
1333
+ const client = getClient();
1334
+ const result = await client.execute("SELECT * FROM company_procedures LIMIT 1000");
1335
+ const rows = result.rows;
1336
+ const { ok } = await cloudPushBlob(
1337
+ "/sync/push-global-procedures",
1338
+ rows,
1339
+ "last_company_procedures_push_version",
1340
+ config
1341
+ );
1342
+ return ok;
1343
+ }
1344
+ async function cloudPullGlobalProcedures(config) {
1345
+ const remoteProcs = await cloudPullBlob(
1346
+ "/sync/pull-global-procedures",
1347
+ config
1348
+ );
1349
+ if (!remoteProcs || remoteProcs.length === 0) return { pulled: 0 };
1350
+ const client = getClient();
1351
+ let pulled = 0;
1352
+ for (const p of remoteProcs) {
1353
+ try {
1354
+ await client.execute({
1355
+ sql: `INSERT INTO company_procedures
1356
+ (id, title, content, priority, domain, active, created_at, updated_at)
1357
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1358
+ ON CONFLICT(id) DO UPDATE SET
1359
+ title = excluded.title,
1360
+ content = excluded.content,
1361
+ priority = excluded.priority,
1362
+ domain = excluded.domain,
1363
+ active = excluded.active,
1364
+ updated_at = excluded.updated_at
1365
+ WHERE excluded.updated_at > company_procedures.updated_at`,
1366
+ args: [
1367
+ sqlSafe(p.id),
1368
+ sqlSafeRequired(p.title, "(untitled)"),
1369
+ sqlSafeRequired(p.content, ""),
1370
+ sqlSafe(p.priority ?? "p0"),
1371
+ sqlSafe(p.domain),
1372
+ sqlSafe(p.active ?? 1),
1373
+ sqlSafe(p.created_at ?? (/* @__PURE__ */ new Date()).toISOString()),
1374
+ sqlSafe(p.updated_at ?? (/* @__PURE__ */ new Date()).toISOString())
1375
+ ]
1376
+ });
1377
+ pulled++;
1378
+ } catch (e) {
1379
+ process.stderr.write(
1380
+ `[cloud-sync] Skipping bad company_procedure record ${String(p.id).slice(0, 8)}: ${e instanceof Error ? e.message : String(e)}
1381
+ `
1382
+ );
1383
+ }
1384
+ }
1385
+ return { pulled };
1386
+ }
1387
+ async function cloudPushBehaviors(config) {
1388
+ const client = getClient();
1389
+ const result = await client.execute("SELECT * FROM behaviors LIMIT 10000");
1390
+ const rows = result.rows;
1391
+ const { ok } = await cloudPushBlob(
1392
+ "/sync/push-behaviors",
1393
+ rows,
1394
+ "last_behaviors_push_version",
1395
+ config
1396
+ );
1397
+ return ok;
1398
+ }
1399
+ async function cloudPullBehaviors(config) {
1400
+ const remoteBehaviors = await cloudPullBlob(
1401
+ "/sync/pull-behaviors",
1402
+ config
1403
+ );
1404
+ if (!remoteBehaviors || remoteBehaviors.length === 0) return { pulled: 0 };
1405
+ const client = getClient();
1406
+ let pulled = 0;
1407
+ for (const behavior of remoteBehaviors) {
1408
+ try {
1409
+ await client.execute({
1410
+ sql: `INSERT INTO behaviors
1411
+ (id, agent_id, project_name, domain, content, active, priority, created_at, updated_at)
1412
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1413
+ ON CONFLICT(id) DO UPDATE SET
1414
+ active = excluded.active,
1415
+ priority = excluded.priority,
1416
+ updated_at = excluded.updated_at`,
1417
+ args: [
1418
+ sqlSafe(behavior.id),
1419
+ sqlSafeRequired(behavior.agent_id, "unknown"),
1420
+ sqlSafe(behavior.project_name),
1421
+ sqlSafe(behavior.domain ?? "general"),
1422
+ sqlSafeRequired(behavior.content, "(empty)"),
1423
+ sqlSafe(behavior.active ?? 1),
1424
+ sqlSafe(behavior.priority ?? "p1"),
1425
+ sqlSafe(behavior.created_at ?? (/* @__PURE__ */ new Date()).toISOString()),
1426
+ sqlSafe(behavior.updated_at ?? (/* @__PURE__ */ new Date()).toISOString())
1427
+ ]
1428
+ });
1429
+ pulled++;
1430
+ } catch (e) {
1431
+ process.stderr.write(
1432
+ `[cloud-sync] Skipped bad behavior record: ${e instanceof Error ? e.message : String(e)}
1433
+ `
1434
+ );
1435
+ }
1436
+ }
1437
+ return { pulled };
1438
+ }
1439
+ async function cloudPushGraphRAG(config) {
1440
+ const client = getClient();
1441
+ const [entities, relationships, aliases, entityMems, relMems, hyperedges, hyperedgeNodes] = await Promise.all([
1442
+ client.execute("SELECT * FROM entities LIMIT 50000"),
1443
+ client.execute("SELECT * FROM relationships LIMIT 50000"),
1444
+ client.execute("SELECT * FROM entity_aliases LIMIT 50000"),
1445
+ client.execute("SELECT * FROM entity_memories LIMIT 50000"),
1446
+ client.execute("SELECT * FROM relationship_memories LIMIT 50000"),
1447
+ client.execute("SELECT * FROM hyperedges LIMIT 50000"),
1448
+ client.execute("SELECT * FROM hyperedge_nodes LIMIT 50000")
1449
+ ]);
1450
+ const blob = {
1451
+ entities: entities.rows,
1452
+ relationships: relationships.rows,
1453
+ entity_aliases: aliases.rows,
1454
+ entity_memories: entityMems.rows,
1455
+ relationship_memories: relMems.rows,
1456
+ hyperedges: hyperedges.rows,
1457
+ hyperedge_nodes: hyperedgeNodes.rows
1458
+ };
1459
+ const { ok } = await cloudPushBlob(
1460
+ "/sync/push-graphrag",
1461
+ [blob],
1462
+ "last_graphrag_push_version",
1463
+ config
1464
+ );
1465
+ return ok;
1466
+ }
1467
+ async function cloudPullGraphRAG(config) {
1468
+ const data = await cloudPullBlob(
1469
+ "/sync/pull-graphrag",
1470
+ config
1471
+ );
1472
+ if (!data || data.length === 0) return { pulled: 0 };
1473
+ const blob = data[0];
1474
+ let pulled = 0;
1475
+ let client = null;
1476
+ try {
1477
+ client = getClient();
1478
+ } catch {
1479
+ process.stderr.write("[cloud-sync] SQLite unavailable for GraphRAG pull \u2014 using PostgreSQL projection only\n");
1480
+ }
1481
+ if (client && blob.entities.length > 0) {
1482
+ const stmts = blob.entities.map((e) => ({
1483
+ sql: `INSERT OR IGNORE INTO entities (id, name, type, first_seen, last_seen, properties)
1484
+ VALUES (?, ?, ?, ?, ?, ?)`,
1485
+ args: [sqlSafe(e.id), sqlSafe(e.name), sqlSafe(e.type), sqlSafe(e.first_seen), sqlSafe(e.last_seen), sqlSafe(e.properties ?? "{}")]
1486
+ }));
1487
+ await client.batch(stmts, "write");
1488
+ pulled += stmts.length;
1489
+ }
1490
+ if (client && blob.relationships.length > 0) {
1491
+ const stmts = blob.relationships.map((r) => ({
1492
+ sql: `INSERT OR IGNORE INTO relationships
1493
+ (id, source_entity_id, target_entity_id, type, weight, timestamp, properties, confidence, confidence_label)
1494
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1495
+ args: [
1496
+ sqlSafe(r.id),
1497
+ sqlSafe(r.source_entity_id),
1498
+ sqlSafe(r.target_entity_id),
1499
+ sqlSafe(r.type),
1500
+ sqlSafe(r.weight ?? 1),
1501
+ sqlSafe(r.timestamp),
1502
+ sqlSafe(r.properties ?? "{}"),
1503
+ sqlSafe(r.confidence ?? 1),
1504
+ sqlSafe(r.confidence_label ?? "extracted")
1505
+ ]
1506
+ }));
1507
+ await client.batch(stmts, "write");
1508
+ pulled += stmts.length;
1509
+ }
1510
+ if (client && blob.entity_aliases.length > 0) {
1511
+ const stmts = blob.entity_aliases.map((a) => ({
1512
+ sql: `INSERT OR IGNORE INTO entity_aliases (alias, canonical_entity_id) VALUES (?, ?)`,
1513
+ args: [sqlSafe(a.alias), sqlSafe(a.canonical_entity_id)]
1514
+ }));
1515
+ await client.batch(stmts, "write");
1516
+ pulled += stmts.length;
1517
+ }
1518
+ if (client && blob.entity_memories.length > 0) {
1519
+ const stmts = blob.entity_memories.map((em) => ({
1520
+ sql: `INSERT OR IGNORE INTO entity_memories (entity_id, memory_id) VALUES (?, ?)`,
1521
+ args: [sqlSafe(em.entity_id), sqlSafe(em.memory_id)]
1522
+ }));
1523
+ await client.batch(stmts, "write");
1524
+ pulled += stmts.length;
1525
+ }
1526
+ if (client && blob.relationship_memories.length > 0) {
1527
+ const stmts = blob.relationship_memories.map((rm) => ({
1528
+ sql: `INSERT OR IGNORE INTO relationship_memories (relationship_id, memory_id) VALUES (?, ?)`,
1529
+ args: [sqlSafe(rm.relationship_id), sqlSafe(rm.memory_id)]
1530
+ }));
1531
+ await client.batch(stmts, "write");
1532
+ pulled += stmts.length;
1533
+ }
1534
+ if (client && blob.hyperedges.length > 0) {
1535
+ const stmts = blob.hyperedges.map((h) => ({
1536
+ sql: `INSERT OR IGNORE INTO hyperedges (id, label, relation, confidence, timestamp)
1537
+ VALUES (?, ?, ?, ?, ?)`,
1538
+ args: [sqlSafe(h.id), sqlSafe(h.label), sqlSafe(h.relation), sqlSafe(h.confidence ?? 1), sqlSafe(h.timestamp)]
1539
+ }));
1540
+ await client.batch(stmts, "write");
1541
+ pulled += stmts.length;
1542
+ }
1543
+ if (client && blob.hyperedge_nodes.length > 0) {
1544
+ const stmts = blob.hyperedge_nodes.map((hn) => ({
1545
+ sql: `INSERT OR IGNORE INTO hyperedge_nodes (hyperedge_id, entity_id) VALUES (?, ?)`,
1546
+ args: [sqlSafe(hn.hyperedge_id), sqlSafe(hn.entity_id)]
1547
+ }));
1548
+ await client.batch(stmts, "write");
1549
+ pulled += stmts.length;
1550
+ }
1551
+ const pgPromise = loadPgClient();
1552
+ if (pgPromise) {
1553
+ try {
1554
+ const pg = await pgPromise;
1555
+ const S = "graph";
1556
+ for (const e of blob.entities) {
1557
+ await pg.$executeRawUnsafe(
1558
+ `INSERT INTO ${S}.entities (id, name, type, first_seen, last_seen, properties)
1559
+ VALUES ($1::uuid, $2, $3, $4::timestamptz, $5::timestamptz, $6::jsonb)
1560
+ ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, type = EXCLUDED.type,
1561
+ last_seen = EXCLUDED.last_seen, properties = EXCLUDED.properties`,
1562
+ e.id,
1563
+ e.name ?? "unknown",
1564
+ e.type ?? "unknown",
1565
+ e.first_seen ?? (/* @__PURE__ */ new Date()).toISOString(),
1566
+ e.last_seen ?? (/* @__PURE__ */ new Date()).toISOString(),
1567
+ e.properties ?? "{}"
1568
+ );
1569
+ }
1570
+ for (const r of blob.relationships) {
1571
+ await pg.$executeRawUnsafe(
1572
+ `INSERT INTO ${S}.relationships (id, source_entity_id, target_entity_id, type, confidence, properties)
1573
+ VALUES ($1::uuid, $2::uuid, $3::uuid, $4, $5, $6::jsonb)
1574
+ ON CONFLICT (id) DO NOTHING`,
1575
+ r.id,
1576
+ r.source_entity_id,
1577
+ r.target_entity_id,
1578
+ r.type ?? "related_to",
1579
+ r.confidence ?? 0.8,
1580
+ r.properties ?? "{}"
1581
+ );
1582
+ }
1583
+ for (const em of blob.entity_memories) {
1584
+ await pg.$executeRawUnsafe(
1585
+ `INSERT INTO ${S}.entity_memories (entity_id, memory_id)
1586
+ VALUES ($1::uuid, $2::uuid) ON CONFLICT DO NOTHING`,
1587
+ em.entity_id,
1588
+ em.memory_id
1589
+ );
1590
+ }
1591
+ process.stderr.write(`[cloud-sync] Projected ${blob.entities.length} entities + ${blob.relationships.length} relationships to PostgreSQL graph schema
1592
+ `);
1593
+ } catch (pgErr) {
1594
+ process.stderr.write(`[cloud-sync] PostgreSQL graph projection failed: ${pgErr instanceof Error ? pgErr.message : String(pgErr)}
1595
+ `);
1596
+ }
1597
+ }
1598
+ return { pulled };
1599
+ }
1600
+ async function cloudPushTasks(config) {
1601
+ const client = getClient();
1602
+ const result = await client.execute("SELECT * FROM tasks LIMIT 10000");
1603
+ const rows = result.rows;
1604
+ const { ok } = await cloudPushBlob(
1605
+ "/sync/push-tasks",
1606
+ rows,
1607
+ "last_tasks_push_version",
1608
+ config
1609
+ );
1610
+ return ok;
1611
+ }
1612
+ async function cloudPullTasks(config) {
1613
+ const remoteTasks = await cloudPullBlob(
1614
+ "/sync/pull-tasks",
1615
+ config
1616
+ );
1617
+ if (!remoteTasks || remoteTasks.length === 0) return { pulled: 0 };
1618
+ const client = getClient();
1619
+ const stmts = remoteTasks.map((t) => ({
1620
+ sql: `INSERT OR IGNORE INTO tasks
1621
+ (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, created_at, updated_at,
1622
+ blocked_by, parent_task_id, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at,
1623
+ reviewer, context, complexity, session_scope, spawn_runtime, spawn_model, result)
1624
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1625
+ args: [
1626
+ sqlSafe(t.id),
1627
+ sqlSafe(t.title),
1628
+ sqlSafe(t.assigned_to),
1629
+ sqlSafe(t.assigned_by),
1630
+ sqlSafe(t.project_name),
1631
+ sqlSafe(t.priority ?? "p1"),
1632
+ sqlSafe(t.status ?? "open"),
1633
+ sqlSafe(t.task_file),
1634
+ sqlSafe(t.created_at),
1635
+ sqlSafe(t.updated_at),
1636
+ sqlSafe(t.blocked_by),
1637
+ sqlSafe(t.parent_task_id),
1638
+ sqlSafe(t.budget_tokens),
1639
+ sqlSafe(t.budget_fallback_model),
1640
+ sqlSafe(t.tokens_used ?? 0),
1641
+ sqlSafe(t.tokens_warned_at),
1642
+ sqlSafe(t.reviewer),
1643
+ sqlSafe(t.context),
1644
+ sqlSafe(t.complexity),
1645
+ sqlSafe(t.session_scope),
1646
+ sqlSafe(t.spawn_runtime),
1647
+ sqlSafe(t.spawn_model),
1648
+ sqlSafe(t.result)
1649
+ ]
1650
+ }));
1651
+ await client.batch(stmts, "write");
1652
+ return { pulled: remoteTasks.length };
1653
+ }
1654
+ async function cloudPushConversations(config) {
1655
+ const client = getClient();
1656
+ const result = await client.execute("SELECT * FROM conversations LIMIT 50000");
1657
+ const rows = result.rows;
1658
+ const { ok } = await cloudPushBlob(
1659
+ "/sync/push-conversations",
1660
+ rows,
1661
+ "last_conversations_push_version",
1662
+ config
1663
+ );
1664
+ return ok;
1665
+ }
1666
+ async function cloudPullConversations(config) {
1667
+ const remoteConvos = await cloudPullBlob(
1668
+ "/sync/pull-conversations",
1669
+ config
1670
+ );
1671
+ if (!remoteConvos || remoteConvos.length === 0) return { pulled: 0 };
1672
+ const client = getClient();
1673
+ const stmts = remoteConvos.map((c) => ({
1674
+ sql: `INSERT INTO conversations
1675
+ (id, platform, external_id, sender_id, sender_name, sender_phone, sender_email,
1676
+ recipient_id, channel_id, thread_id, reply_to_id, content_text, content_media,
1677
+ content_metadata, agent_response, agent_name, timestamp, ingested_at)
1678
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1679
+ ON CONFLICT(id) DO UPDATE SET
1680
+ platform = excluded.platform,
1681
+ external_id = excluded.external_id,
1682
+ sender_id = excluded.sender_id,
1683
+ sender_name = excluded.sender_name,
1684
+ sender_phone = excluded.sender_phone,
1685
+ sender_email = excluded.sender_email,
1686
+ recipient_id = excluded.recipient_id,
1687
+ channel_id = excluded.channel_id,
1688
+ thread_id = excluded.thread_id,
1689
+ reply_to_id = excluded.reply_to_id,
1690
+ content_text = excluded.content_text,
1691
+ content_media = excluded.content_media,
1692
+ content_metadata = excluded.content_metadata,
1693
+ agent_response = excluded.agent_response,
1694
+ agent_name = excluded.agent_name,
1695
+ timestamp = excluded.timestamp,
1696
+ ingested_at = excluded.ingested_at
1697
+ WHERE COALESCE(excluded.ingested_at, excluded.timestamp, '') > COALESCE(conversations.ingested_at, conversations.timestamp, '')`,
1698
+ args: [
1699
+ sqlSafe(c.id),
1700
+ sqlSafe(c.platform),
1701
+ sqlSafe(c.external_id),
1702
+ sqlSafe(c.sender_id),
1703
+ sqlSafe(c.sender_name),
1704
+ sqlSafe(c.sender_phone),
1705
+ sqlSafe(c.sender_email),
1706
+ sqlSafe(c.recipient_id),
1707
+ sqlSafe(c.channel_id),
1708
+ sqlSafe(c.thread_id),
1709
+ sqlSafe(c.reply_to_id),
1710
+ sqlSafe(c.content_text),
1711
+ sqlSafe(c.content_media),
1712
+ sqlSafe(c.content_metadata),
1713
+ sqlSafe(c.agent_response),
1714
+ sqlSafe(c.agent_name),
1715
+ sqlSafe(c.timestamp),
1716
+ sqlSafe(c.ingested_at)
1717
+ ]
1718
+ }));
1719
+ await client.batch(stmts, "write");
1720
+ return { pulled: remoteConvos.length };
1721
+ }
1722
+ async function cloudPushDocuments(config) {
1723
+ const client = getClient();
1724
+ const [workspaces, documents] = await Promise.all([
1725
+ client.execute("SELECT * FROM workspaces LIMIT 1000"),
1726
+ client.execute("SELECT * FROM documents LIMIT 10000")
1727
+ ]);
1728
+ const blob = {
1729
+ workspaces: workspaces.rows,
1730
+ documents: documents.rows
1731
+ };
1732
+ const { ok } = await cloudPushBlob(
1733
+ "/sync/push-documents",
1734
+ [blob],
1735
+ "last_documents_push_version",
1736
+ config
1737
+ );
1738
+ return ok;
1739
+ }
1740
+ async function cloudPullDocuments(config) {
1741
+ const data = await cloudPullBlob(
1742
+ "/sync/pull-documents",
1743
+ config
1744
+ );
1745
+ if (!data || data.length === 0) return { pulled: 0 };
1746
+ const blob = data[0];
1747
+ const client = getClient();
1748
+ let pulled = 0;
1749
+ if (blob.workspaces.length > 0) {
1750
+ const stmts = blob.workspaces.map((w) => ({
1751
+ sql: `INSERT INTO workspaces (id, slug, name, owner_agent_id, created_at, metadata)
1752
+ VALUES (?, ?, ?, ?, ?, ?)
1753
+ ON CONFLICT(id) DO UPDATE SET
1754
+ slug = excluded.slug,
1755
+ name = excluded.name,
1756
+ owner_agent_id = excluded.owner_agent_id,
1757
+ metadata = excluded.metadata
1758
+ WHERE COALESCE(excluded.created_at, '') >= COALESCE(workspaces.created_at, '')`,
1759
+ args: [sqlSafe(w.id), sqlSafe(w.slug), sqlSafe(w.name), sqlSafe(w.owner_agent_id), sqlSafe(w.created_at), sqlSafe(w.metadata)]
1760
+ }));
1761
+ await client.batch(stmts, "write");
1762
+ pulled += stmts.length;
1763
+ }
1764
+ if (blob.documents.length > 0) {
1765
+ const stmts = blob.documents.map((d) => ({
1766
+ sql: `INSERT INTO documents
1767
+ (id, workspace_id, filename, mime, source_type, user_id, uploaded_at, metadata)
1768
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1769
+ ON CONFLICT(id) DO UPDATE SET
1770
+ workspace_id = excluded.workspace_id,
1771
+ filename = excluded.filename,
1772
+ mime = excluded.mime,
1773
+ source_type = excluded.source_type,
1774
+ user_id = excluded.user_id,
1775
+ uploaded_at = excluded.uploaded_at,
1776
+ metadata = excluded.metadata
1777
+ WHERE COALESCE(excluded.uploaded_at, '') > COALESCE(documents.uploaded_at, '')`,
1778
+ args: [
1779
+ sqlSafe(d.id),
1780
+ sqlSafe(d.workspace_id),
1781
+ sqlSafe(d.filename),
1782
+ sqlSafe(d.mime),
1783
+ sqlSafe(d.source_type),
1784
+ sqlSafe(d.user_id),
1785
+ sqlSafe(d.uploaded_at),
1786
+ sqlSafe(d.metadata)
1787
+ ]
1788
+ }));
1789
+ await client.batch(stmts, "write");
1790
+ pulled += stmts.length;
1791
+ }
1792
+ return { pulled };
1793
+ }
1794
+ var CODE_CONTEXT_DIR = path.join(EXE_AI_DIR, "code-context");
1795
+ async function cloudPushCodeContext(config) {
1796
+ assertSecureEndpoint(config.endpoint);
1797
+ if (!existsSync(CODE_CONTEXT_DIR)) return 0;
1798
+ const files = readdirSync(CODE_CONTEXT_DIR).filter(
1799
+ (f) => f.endsWith(".json") && !f.endsWith(".vectors.json") && !f.startsWith(".")
1800
+ );
1801
+ if (files.length === 0) return 0;
1802
+ const metaPath = path.join(CODE_CONTEXT_DIR, ".sync-meta.json");
1803
+ let syncMeta = {};
1804
+ if (existsSync(metaPath)) {
1805
+ try {
1806
+ syncMeta = JSON.parse(readFileSync(metaPath, "utf-8"));
1807
+ } catch (e) {
1808
+ process.stderr.write("[cloud-sync] code-context sync-meta parse failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1809
+ }
1810
+ }
1811
+ let pushed = 0;
1812
+ for (const file of files) {
1813
+ const filePath = path.join(CODE_CONTEXT_DIR, file);
1814
+ try {
1815
+ const stat = statSync(filePath);
1816
+ const lastPushed = syncMeta[file] ?? 0;
1817
+ if (stat.mtimeMs <= lastPushed) continue;
1818
+ const content = readFileSync(filePath, "utf-8");
1819
+ const header = content.substring(0, 300);
1820
+ if (header.includes("/tmp") || header.includes("/var/folders") || header.includes(".worktrees/")) continue;
1821
+ const compressed = compress(Buffer.from(content, "utf8"));
1822
+ const encrypted = encryptSyncBlob(compressed);
1823
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/push-code-context`, {
1824
+ method: "POST",
1825
+ headers: {
1826
+ Authorization: `Bearer ${config.apiKey}`,
1827
+ "Content-Type": "application/json",
1828
+ "X-Device-Id": loadDeviceId()
1829
+ },
1830
+ body: JSON.stringify({ key: file, blob: encrypted })
1831
+ });
1832
+ if (resp.ok) {
1833
+ syncMeta[file] = stat.mtimeMs;
1834
+ pushed++;
1835
+ }
1836
+ } catch (e) {
1837
+ process.stderr.write("[cloud-sync] code-context push failed (" + file + "): " + (e instanceof Error ? e.message : String(e)) + "\n");
1838
+ }
1839
+ }
1840
+ if (pushed > 0) {
1841
+ try {
1842
+ atomicWriteJsonSync(metaPath, syncMeta);
1843
+ } catch (e) {
1844
+ process.stderr.write("[cloud-sync] code-context sync-meta write failed: " + (e instanceof Error ? e.message : String(e)) + "\n");
1845
+ }
1846
+ }
1847
+ return pushed;
1848
+ }
1849
+ async function cloudPullCodeContext(config) {
1850
+ assertSecureEndpoint(config.endpoint);
1851
+ try {
1852
+ const resp = await fetchWithRetry(`${config.endpoint}/sync/pull-code-context`, {
1853
+ method: "GET",
1854
+ headers: {
1855
+ Authorization: `Bearer ${config.apiKey}`,
1856
+ "X-Device-Id": loadDeviceId()
1857
+ }
1858
+ });
1859
+ if (!resp.ok) return 0;
1860
+ const data = await resp.json();
1861
+ if (!data.indexes || data.indexes.length === 0) return 0;
1862
+ mkdirSync(CODE_CONTEXT_DIR, { recursive: true });
1863
+ let pulled = 0;
1864
+ for (const { key, blob } of data.indexes) {
1865
+ try {
1866
+ if (key.endsWith(".vectors.json")) continue;
1867
+ const localPath = path.join(CODE_CONTEXT_DIR, key);
1868
+ const compressed = decryptSyncBlob(blob);
1869
+ const content = decompress(compressed).toString("utf8");
1870
+ if (!existsSync(localPath)) {
1871
+ atomicWriteSync(localPath, content);
1872
+ pulled++;
1873
+ } else {
1874
+ const remoteHash = crypto.createHash("sha256").update(content).digest("hex");
1875
+ const localHash = crypto.createHash("sha256").update(readFileSync(localPath, "utf-8")).digest("hex");
1876
+ if (localHash !== remoteHash) {
1877
+ atomicWriteSync(localPath, content);
1878
+ pulled++;
1879
+ }
1880
+ }
1881
+ } catch (e) {
1882
+ process.stderr.write("[cloud-sync] code-context pull failed (" + key + "): " + (e instanceof Error ? e.message : String(e)) + "\n");
1883
+ }
1884
+ }
1885
+ return pulled;
1886
+ } catch (err) {
1887
+ process.stderr.write(`[cloud-sync] Code context pull failed: ${err instanceof Error ? err.message : String(err)}
1888
+ `);
1889
+ return 0;
1890
+ }
1891
+ }
1892
+ async function cloudPushSchedules(config) {
1893
+ const client = getClient();
1894
+ const result = await client.execute("SELECT * FROM schedules LIMIT 10000");
1895
+ const rows = result.rows;
1896
+ const { ok } = await cloudPushBlob("/sync/push-schedules", rows, "last_schedules_push_version", config);
1897
+ return ok;
1898
+ }
1899
+ async function cloudPullSchedules(config) {
1900
+ const remote = await cloudPullBlob("/sync/pull-schedules", config);
1901
+ if (!remote || remote.length === 0) return { pulled: 0 };
1902
+ const client = getClient();
1903
+ const stmts = remote.map((r) => ({
1904
+ sql: `INSERT INTO schedules
1905
+ (id, cron, description, job_type, prompt, assigned_to, project_name, active, use_crontab, created_at)
1906
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1907
+ ON CONFLICT(id) DO UPDATE SET
1908
+ cron = excluded.cron,
1909
+ description = excluded.description,
1910
+ job_type = excluded.job_type,
1911
+ prompt = excluded.prompt,
1912
+ assigned_to = excluded.assigned_to,
1913
+ project_name = excluded.project_name,
1914
+ active = excluded.active,
1915
+ use_crontab = excluded.use_crontab`,
1916
+ args: [
1917
+ sqlSafe(r.id),
1918
+ sqlSafe(r.cron),
1919
+ sqlSafe(r.description),
1920
+ sqlSafe(r.job_type ?? "report"),
1921
+ sqlSafe(r.prompt),
1922
+ sqlSafe(r.assigned_to),
1923
+ sqlSafe(r.project_name),
1924
+ sqlSafe(r.active ?? 1),
1925
+ sqlSafe(r.use_crontab ?? 0),
1926
+ sqlSafe(r.created_at)
1927
+ ]
1928
+ }));
1929
+ await client.batch(stmts, "write");
1930
+ return { pulled: remote.length };
1931
+ }
1932
+ async function cloudPushTrajectories(config) {
1933
+ const client = getClient();
1934
+ const result = await client.execute("SELECT * FROM trajectories LIMIT 50000");
1935
+ const rows = result.rows;
1936
+ const { ok } = await cloudPushBlob("/sync/push-trajectories", rows, "last_trajectories_push_version", config);
1937
+ return ok;
1938
+ }
1939
+ async function cloudPullTrajectories(config) {
1940
+ const remote = await cloudPullBlob("/sync/pull-trajectories", config);
1941
+ if (!remote || remote.length === 0) return { pulled: 0 };
1942
+ const client = getClient();
1943
+ const stmts = remote.map((r) => ({
1944
+ sql: `INSERT INTO trajectories
1945
+ (id, task_id, agent_id, project_name, task_title, signature, signature_hash, tool_count, skill_id, created_at)
1946
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1947
+ ON CONFLICT(id) DO UPDATE SET
1948
+ task_id = excluded.task_id,
1949
+ agent_id = excluded.agent_id,
1950
+ project_name = excluded.project_name,
1951
+ task_title = excluded.task_title,
1952
+ signature = excluded.signature,
1953
+ signature_hash = excluded.signature_hash,
1954
+ tool_count = excluded.tool_count,
1955
+ skill_id = excluded.skill_id,
1956
+ created_at = excluded.created_at
1957
+ WHERE COALESCE(excluded.created_at, '') >= COALESCE(trajectories.created_at, '')`,
1958
+ args: [
1959
+ sqlSafe(r.id),
1960
+ sqlSafe(r.task_id),
1961
+ sqlSafe(r.agent_id),
1962
+ sqlSafe(r.project_name),
1963
+ sqlSafe(r.task_title),
1964
+ sqlSafe(r.signature),
1965
+ sqlSafe(r.signature_hash),
1966
+ sqlSafe(r.tool_count ?? 0),
1967
+ sqlSafe(r.skill_id),
1968
+ sqlSafe(r.created_at)
1969
+ ]
1970
+ }));
1971
+ await client.batch(stmts, "write");
1972
+ return { pulled: remote.length };
1973
+ }
1974
+ async function cloudPushConsolidations(config) {
1975
+ const client = getClient();
1976
+ const result = await client.execute("SELECT * FROM consolidations LIMIT 50000");
1977
+ const rows = result.rows;
1978
+ const { ok } = await cloudPushBlob("/sync/push-consolidations", rows, "last_consolidations_push_version", config);
1979
+ return ok;
1980
+ }
1981
+ async function cloudPullConsolidations(config) {
1982
+ const remote = await cloudPullBlob("/sync/pull-consolidations", config);
1983
+ if (!remote || remote.length === 0) return { pulled: 0 };
1984
+ const client = getClient();
1985
+ const stmts = remote.map((r) => ({
1986
+ sql: `INSERT INTO consolidations
1987
+ (id, consolidated_memory_id, source_memory_id, created_at)
1988
+ VALUES (?, ?, ?, ?)
1989
+ ON CONFLICT(id) DO UPDATE SET
1990
+ consolidated_memory_id = excluded.consolidated_memory_id,
1991
+ source_memory_id = excluded.source_memory_id,
1992
+ created_at = excluded.created_at
1993
+ WHERE COALESCE(excluded.created_at, '') >= COALESCE(consolidations.created_at, '')`,
1994
+ args: [
1995
+ sqlSafe(r.id),
1996
+ sqlSafe(r.consolidated_memory_id),
1997
+ sqlSafe(r.source_memory_id),
1998
+ sqlSafe(r.created_at)
1999
+ ]
2000
+ }));
2001
+ await client.batch(stmts, "write");
2002
+ return { pulled: remote.length };
2003
+ }
2004
+ async function cloudPushIdentityMeta(config) {
2005
+ const client = getClient();
2006
+ const result = await client.execute("SELECT * FROM identity LIMIT 1000");
2007
+ const rows = result.rows;
2008
+ const { ok } = await cloudPushBlob("/sync/push-identity", rows, "last_identity_push_version", config);
2009
+ return ok;
2010
+ }
2011
+ async function cloudPullIdentityMeta(config) {
2012
+ const remote = await cloudPullBlob("/sync/pull-identity", config);
2013
+ if (!remote || remote.length === 0) return { pulled: 0 };
2014
+ const client = getClient();
2015
+ const stmts = remote.map((r) => ({
2016
+ sql: `INSERT INTO identity
2017
+ (agent_id, content_hash, updated_at, updated_by)
2018
+ VALUES (?, ?, ?, ?)
2019
+ ON CONFLICT(agent_id) DO UPDATE SET
2020
+ content_hash = excluded.content_hash,
2021
+ updated_at = excluded.updated_at,
2022
+ updated_by = excluded.updated_by
2023
+ WHERE COALESCE(excluded.updated_at, '') > COALESCE(identity.updated_at, '')`,
2024
+ args: [
2025
+ sqlSafe(r.agent_id),
2026
+ sqlSafe(r.content_hash),
2027
+ sqlSafe(r.updated_at),
2028
+ sqlSafe(r.updated_by)
2029
+ ]
2030
+ }));
2031
+ await client.batch(stmts, "write");
2032
+ return { pulled: remote.length };
2033
+ }
2034
+ async function cloudPushCoreMemory(config) {
2035
+ const client = getClient();
2036
+ const result = await client.execute("SELECT * FROM core_memory LIMIT 1000");
2037
+ const rows = result.rows;
2038
+ if (rows.length === 0) return true;
2039
+ const { ok } = await cloudPushBlob("/sync/push-core-memory", rows, "last_core_memory_push_version", config);
2040
+ return ok;
2041
+ }
2042
+ async function cloudPullCoreMemory(config) {
2043
+ const remote = await cloudPullBlob("/sync/pull-core-memory", config);
2044
+ if (!remote || remote.length === 0) return { pulled: 0 };
2045
+ const client = getClient();
2046
+ const stmts = remote.map((r) => ({
2047
+ sql: `INSERT INTO core_memory (agent_id, key, value, updated_at)
2048
+ VALUES (?, ?, ?, ?)
2049
+ ON CONFLICT(agent_id, key) DO UPDATE SET
2050
+ value = excluded.value,
2051
+ updated_at = excluded.updated_at
2052
+ WHERE excluded.updated_at > core_memory.updated_at`,
2053
+ args: [
2054
+ sqlSafe(r.agent_id),
2055
+ sqlSafe(r.key),
2056
+ sqlSafe(r.value),
2057
+ sqlSafe(r.updated_at)
2058
+ ]
2059
+ }));
2060
+ await client.batch(stmts, "write");
2061
+ return { pulled: remote.length };
2062
+ }
2063
+
2064
+ export {
2065
+ shouldUseCrdtMergeForCloudSync,
2066
+ loadPgClient,
2067
+ disposeCloudSync,
2068
+ pushToPostgres,
2069
+ migrateEndpoint,
2070
+ assertSecureEndpoint,
2071
+ cloudPush,
2072
+ cloudResetMemoryBlobs,
2073
+ cloudPull,
2074
+ CLOUD_REUPLOAD_REQUIRED_MESSAGE,
2075
+ getCloudReuploadRequired,
2076
+ clearCloudReuploadRequired,
2077
+ getCloudRelinkRequired,
2078
+ clearCloudRelinkRequired,
2079
+ markCloudReuploadRequired,
2080
+ cloudSync,
2081
+ recordRosterDeletion,
2082
+ buildRosterBlob,
2083
+ cloudPushRoster,
2084
+ cloudPullRoster,
2085
+ mergeConfig,
2086
+ mergeRosterFromRemote,
2087
+ cloudPushBlob,
2088
+ cloudPullBlob,
2089
+ cloudPushGlobalProcedures,
2090
+ cloudPullGlobalProcedures,
2091
+ cloudPushBehaviors,
2092
+ cloudPullBehaviors,
2093
+ cloudPushGraphRAG,
2094
+ cloudPullGraphRAG,
2095
+ cloudPushTasks,
2096
+ cloudPullTasks,
2097
+ cloudPushConversations,
2098
+ cloudPullConversations,
2099
+ cloudPushDocuments,
2100
+ cloudPullDocuments,
2101
+ cloudPushCodeContext,
2102
+ cloudPullCodeContext,
2103
+ cloudPushSchedules,
2104
+ cloudPullSchedules,
2105
+ cloudPushTrajectories,
2106
+ cloudPullTrajectories,
2107
+ cloudPushConsolidations,
2108
+ cloudPullConsolidations,
2109
+ cloudPushIdentityMeta,
2110
+ cloudPullIdentityMeta,
2111
+ cloudPushCoreMemory,
2112
+ cloudPullCoreMemory
2113
+ };