@askexenow/exe-os 0.9.217 → 0.9.219

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 (238) hide show
  1. package/deploy/compose/.env.customer.example +12 -2
  2. package/deploy/compose/.env.example +42 -16
  3. package/deploy/compose/PROTECTED_FILES.md +1 -0
  4. package/deploy/compose/README.md +1 -1
  5. package/deploy/compose/backup.sh +3 -1
  6. package/deploy/compose/clickhouse-config.xml +18 -0
  7. package/deploy/compose/docker-compose.yml +37 -13
  8. package/deploy/compose/gateway.json +12 -2
  9. package/deploy/compose/generate-env.ts +17 -1
  10. package/deploy/compose/init-db.sql +12 -0
  11. package/deploy/compose/restore.sh +1 -0
  12. package/deploy/compose/setup.sh +3 -1
  13. package/deploy/compose/uptime-check.sh +143 -0
  14. package/deploy/stack-manifests/v0.9.json +4 -4
  15. package/dist/{active-agent-R7QDYADX.js → active-agent-65SRKUVF.js} +2 -2
  16. package/dist/{active-agent-4XH2Q6KJ.js → active-agent-YLIAU6SX.js} +2 -2
  17. package/dist/{agentic-ontology-CHAWPTEY.js → agentic-ontology-VZWBROYO.js} +1 -1
  18. package/dist/{backfill-metadata-I6VR7JZ2.js → backfill-metadata-YUN6DXIE.js} +3 -3
  19. package/dist/{behaviors-YJ3HEQED.js → behaviors-4PGM7IJJ.js} +2 -2
  20. package/dist/bin/agentic-ontology-backfill.js +4 -4
  21. package/dist/bin/agentic-reflection-backfill.js +5 -5
  22. package/dist/bin/agentic-semantic-label.js +4 -4
  23. package/dist/bin/backfill-conversations.js +3 -3
  24. package/dist/bin/backfill-responses.js +3 -3
  25. package/dist/bin/backfill-vectors.js +4 -4
  26. package/dist/bin/bulk-sync-postgres.js +5 -5
  27. package/dist/bin/cleanup-stale-review-tasks.js +8 -8
  28. package/dist/bin/cli.js +30 -12
  29. package/dist/bin/exe-agent-config.js +1 -1
  30. package/dist/bin/exe-agent.js +6 -6
  31. package/dist/bin/exe-assign.js +4 -4
  32. package/dist/bin/exe-boot.js +15 -15
  33. package/dist/bin/exe-call.js +3 -3
  34. package/dist/bin/exe-cloud.js +3 -3
  35. package/dist/bin/exe-dispatch.js +8 -8
  36. package/dist/bin/exe-doctor.js +1 -1
  37. package/dist/bin/exe-export-behaviors.js +6 -6
  38. package/dist/bin/exe-forget.js +5 -5
  39. package/dist/bin/exe-gateway.js +38 -12
  40. package/dist/bin/exe-heartbeat.js +8 -8
  41. package/dist/bin/exe-kill.js +11 -11
  42. package/dist/bin/exe-launch-agent.js +15 -15
  43. package/dist/bin/exe-new-employee.js +5 -5
  44. package/dist/bin/exe-pending-messages.js +9 -9
  45. package/dist/bin/exe-pending-notifications.js +8 -8
  46. package/dist/bin/exe-pending-reviews.js +8 -8
  47. package/dist/bin/exe-rename.js +3 -3
  48. package/dist/bin/exe-review.js +10 -10
  49. package/dist/bin/exe-search.js +4 -4
  50. package/dist/bin/exe-session-cleanup.js +13 -13
  51. package/dist/bin/exe-settings.js +3 -3
  52. package/dist/bin/exe-start-codex.js +10 -10
  53. package/dist/bin/exe-start-opencode.js +7 -7
  54. package/dist/bin/exe-status.js +9 -9
  55. package/dist/bin/exe-team.js +2 -2
  56. package/dist/bin/git-sweep.js +8 -8
  57. package/dist/bin/graph-backfill.js +3 -3
  58. package/dist/bin/graph-export.js +4 -4
  59. package/dist/bin/import-history.js +4 -4
  60. package/dist/bin/install.js +5 -5
  61. package/dist/bin/intercom-check.js +4 -4
  62. package/dist/bin/mcp-sessions.js +2 -2
  63. package/dist/bin/orchestration-metrics.js +3 -3
  64. package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
  65. package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
  66. package/dist/bin/scan-tasks.js +8 -8
  67. package/dist/bin/setup.js +1 -1
  68. package/dist/bin/shard-migrate.js +3 -3
  69. package/dist/bin/stack-update.js +1 -1
  70. package/dist/bin/vps-health-gate.js +1 -1
  71. package/dist/{capacity-monitor-XFMZSJDB.js → capacity-monitor-A6TMCY5F.js} +9 -9
  72. package/dist/{catchup-brief-D5IZFZDO.js → catchup-brief-TU4C22MJ.js} +10 -10
  73. package/dist/{chunk-F54CVIXL.js → chunk-222EGJ6Z.js} +1 -1
  74. package/dist/{chunk-6UY62EGD.js → chunk-2F7URV5H.js} +4 -4
  75. package/dist/{chunk-2DRWIXOO.js → chunk-2OERQW4L.js} +2 -2
  76. package/dist/{chunk-4GXXYDNU.js → chunk-3XGZAFHE.js} +36 -0
  77. package/dist/{chunk-BN3HRSEU.js → chunk-443YQ2FS.js} +2 -2
  78. package/dist/{chunk-5QKUR5KS.js → chunk-4UHKYQZJ.js} +1 -1
  79. package/dist/{chunk-QOY6CTHV.js → chunk-55VWU47L.js} +2 -2
  80. package/dist/{chunk-ACOW43IN.js → chunk-56NWZFB2.js} +1 -1
  81. package/dist/{chunk-UO3IEPSY.js → chunk-5ZTZ3SHF.js} +233 -333
  82. package/dist/{chunk-GKNYEJQ3.js → chunk-7DYKIMN2.js} +1 -1
  83. package/dist/{chunk-5G643UWZ.js → chunk-7EUYGWPH.js} +117 -8
  84. package/dist/{chunk-FXD53Q32.js → chunk-7F5MJSWJ.js} +3 -3
  85. package/dist/{chunk-2QNMGFIF.js → chunk-7WEP4DNC.js} +2 -2
  86. package/dist/{chunk-S5TTV7PC.js → chunk-A24RUKU6.js} +1 -1
  87. package/dist/{chunk-NEGJAEZ7.js → chunk-AAMUHY6Y.js} +3 -3
  88. package/dist/{chunk-46WQ76VA.js → chunk-AMGEPNUB.js} +1 -1
  89. package/dist/{chunk-HURS5JVX.js → chunk-BCREJ6FD.js} +21 -3
  90. package/dist/{chunk-S2T7CE4P.js → chunk-BMRLKOGT.js} +1 -1
  91. package/dist/{chunk-S4I3UITZ.js → chunk-BXLA6CQM.js} +1 -1
  92. package/dist/{chunk-JXJV64JW.js → chunk-D7DRYXDE.js} +1 -1
  93. package/dist/{chunk-5VOC35A3.js → chunk-DAE4I3UN.js} +1 -1
  94. package/dist/{chunk-R766IOBQ.js → chunk-DF67VZ2H.js} +1 -1
  95. package/dist/{chunk-HW46Q4AR.js → chunk-DZE2MBOW.js} +257 -10
  96. package/dist/{chunk-RTGN42GD.js → chunk-E6KXZF4B.js} +2 -2
  97. package/dist/{chunk-IFSAR5IX.js → chunk-EB3AAVAK.js} +5 -5
  98. package/dist/{chunk-PKIOJQ2N.js → chunk-EUS7TKVR.js} +1 -1
  99. package/dist/{chunk-T43SC2XX.js → chunk-FJQPH2W3.js} +2 -2
  100. package/dist/{chunk-GEFZHKEN.js → chunk-GJFS27NL.js} +5 -5
  101. package/dist/{chunk-KEFHCL4U.js → chunk-GT5E5PW6.js} +1 -1
  102. package/dist/{chunk-XPKACO5A.js → chunk-GYIPOKH6.js} +8 -8
  103. package/dist/chunk-I3MJHU76.js +225 -0
  104. package/dist/{chunk-2IMUAL3X.js → chunk-IEAXNG3H.js} +8 -8
  105. package/dist/{chunk-YJNRRQOG.js → chunk-ILTZBCLW.js} +2 -2
  106. package/dist/{chunk-SDQSOQOE.js → chunk-JEBWC4VU.js} +2 -2
  107. package/dist/{chunk-7X6QKJGM.js → chunk-JHEABI3I.js} +2 -2
  108. package/dist/{chunk-R2MZDZTQ.js → chunk-JUWC7V2I.js} +3 -3
  109. package/dist/{chunk-6I7O6YW3.js → chunk-OM3CETSN.js} +3 -3
  110. package/dist/{chunk-WEJ45ZFF.js → chunk-PBI526CO.js} +2 -2
  111. package/dist/{chunk-I22ZH3XF.js → chunk-PXO6SF6L.js} +1 -1
  112. package/dist/{chunk-B7NO5U7H.js → chunk-Q4GGIM3A.js} +1 -1
  113. package/dist/{chunk-I6474EQW.js → chunk-QG6IPAT6.js} +1 -1
  114. package/dist/{chunk-NO5GHSNU.js → chunk-QPU5QQLX.js} +4 -3
  115. package/dist/{chunk-4ANCHFE2.js → chunk-R6JBGFSE.js} +1 -1
  116. package/dist/{chunk-ALVYUOBW.js → chunk-RQJDO6LP.js} +1 -1
  117. package/dist/{chunk-BFLBZGPZ.js → chunk-S5KQLCYU.js} +1 -1
  118. package/dist/{chunk-NUUAYDD6.js → chunk-SRJLG6RC.js} +1 -1
  119. package/dist/{chunk-O6LFXATW.js → chunk-SURCM7EO.js} +3 -3
  120. package/dist/{chunk-CIANVJ3G.js → chunk-UVUA2DQM.js} +61 -6
  121. package/dist/{chunk-64DYFA5X.js → chunk-V224L2GJ.js} +3 -3
  122. package/dist/{chunk-W7OCXE2L.js → chunk-V24ZGSQW.js} +1 -1
  123. package/dist/{chunk-RSHVFBKD.js → chunk-V2BQL663.js} +8 -8
  124. package/dist/{chunk-JJHJOGYR.js → chunk-WYPNZALJ.js} +1 -1
  125. package/dist/{chunk-RSVJQ4E2.js → chunk-YTBT3SW2.js} +1 -1
  126. package/dist/{chunk-CUKIMXQ7.js → chunk-YTPLK6QP.js} +1 -1
  127. package/dist/{co-occurrence-EWVCCFOI.js → co-occurrence-FBP2GTER.js} +1 -1
  128. package/dist/{crdt-sync-TSO4O67N.js → crdt-sync-KFPZWAIE.js} +1 -1
  129. package/dist/{crm-webhook-WMAW6ZT2.js → crm-webhook-56OJDKXK.js} +2 -2
  130. package/dist/{cto-delegation-gate-YNWMQEQB.js → cto-delegation-gate-5WNDH4IL.js} +7 -7
  131. package/dist/{daemon-orchestration-5KUKQOZ4.js → daemon-orchestration-KYK662AN.js} +14 -10
  132. package/dist/{exe-drift-KP5RZCXA.js → exe-drift-C254MZW4.js} +2 -2
  133. package/dist/{exe-export-RAYK76SO.js → exe-export-S56FIA5P.js} +4 -4
  134. package/dist/{exe-import-SAJBGUN5.js → exe-import-7F7Y4I6T.js} +4 -4
  135. package/dist/{exe-key-W3CME65M.js → exe-key-U7TAND3I.js} +1 -1
  136. package/dist/{fast-db-init-5VDTKVKX.js → fast-db-init-6DG7B7NO.js} +1 -1
  137. package/dist/gateway/index.js +6 -6
  138. package/dist/{git-staleness-M7WLXBAA.js → git-staleness-FDYT34WP.js} +1 -1
  139. package/dist/{git-task-sweep-GED54WTZ.js → git-task-sweep-EERTURH7.js} +8 -8
  140. package/dist/{global-procedures-EYWKFVOO.js → global-procedures-74U75Y2I.js} +2 -2
  141. package/dist/{graph-auto-extract-B4W4BEOZ.js → graph-auto-extract-VUBFSL6D.js} +1 -1
  142. package/dist/hooks/bug-report-worker.js +9 -9
  143. package/dist/hooks/codex-stop-task-finalizer.js +9 -9
  144. package/dist/hooks/commit-complete.js +10 -10
  145. package/dist/hooks/error-recall.js +5 -5
  146. package/dist/hooks/exe-heartbeat-hook.js +2 -2
  147. package/dist/hooks/ingest.js +5 -5
  148. package/dist/hooks/instructions-loaded.js +3 -3
  149. package/dist/hooks/manifest.json +19 -19
  150. package/dist/hooks/notification.js +3 -3
  151. package/dist/hooks/post-compact.js +9 -9
  152. package/dist/hooks/post-tool-combined.js +4 -4
  153. package/dist/hooks/pre-compact.js +10 -10
  154. package/dist/hooks/pre-tool-use.js +13 -13
  155. package/dist/hooks/prompt-submit.js +19 -19
  156. package/dist/hooks/session-end.js +13 -13
  157. package/dist/hooks/session-start.js +28 -9
  158. package/dist/hooks/stop.js +13 -13
  159. package/dist/hooks/subagent-stop.js +9 -9
  160. package/dist/hooks/summary-worker.js +13 -13
  161. package/dist/import-conversations-FO4IQCOP.js +23 -0
  162. package/dist/index.js +15 -15
  163. package/dist/{installer-EYRABPEC.js → installer-MWLYK37L.js} +4 -4
  164. package/dist/{installer-WVPFKMDW.js → installer-XIP332UC.js} +4 -4
  165. package/dist/{installer-X6XJE2EA.js → installer-XZ4H2J3J.js} +4 -4
  166. package/dist/lib/cloud-sync.js +3 -3
  167. package/dist/lib/consolidation.js +4 -4
  168. package/dist/lib/database.js +1 -1
  169. package/dist/lib/db.js +1 -1
  170. package/dist/lib/employee-templates.js +3 -3
  171. package/dist/lib/employees.js +1 -1
  172. package/dist/lib/exe-daemon.js +65 -25
  173. package/dist/lib/hybrid-search.js +4 -4
  174. package/dist/lib/identity.js +1 -1
  175. package/dist/lib/messaging.js +8 -8
  176. package/dist/lib/reminders.js +2 -2
  177. package/dist/lib/schedules.js +4 -4
  178. package/dist/lib/session-registry.js +3 -3
  179. package/dist/lib/skill-learning.js +3 -3
  180. package/dist/lib/store.js +3 -3
  181. package/dist/lib/task-router.js +2 -2
  182. package/dist/lib/tasks.js +8 -8
  183. package/dist/lib/tmux-routing.js +7 -7
  184. package/dist/lib/token-spend.js +2 -2
  185. package/dist/mcp/register-tools.js +47 -46
  186. package/dist/mcp/server.js +48 -47
  187. package/dist/mcp/tools/complete-reminder.js +3 -3
  188. package/dist/mcp/tools/create-reminder.js +3 -3
  189. package/dist/mcp/tools/create-task.js +10 -10
  190. package/dist/mcp/tools/deactivate-behavior.js +4 -4
  191. package/dist/mcp/tools/list-reminders.js +3 -3
  192. package/dist/mcp/tools/list-tasks.js +10 -10
  193. package/dist/mcp/tools/send-message.js +10 -10
  194. package/dist/mcp/tools/update-task.js +9 -9
  195. package/dist/{mcp-http-config-L4U52ZAF.js → mcp-http-config-GYVAS5HQ.js} +2 -2
  196. package/dist/{memory-cards-I55OBMVE.js → memory-cards-HU5WIUTY.js} +1 -1
  197. package/dist/{memory-graph-extractor-6M6J5FNX.js → memory-graph-extractor-PNRAGL4V.js} +2 -2
  198. package/dist/{memory-poisoning-defense-UXB75NB7.js → memory-poisoning-defense-KBBWI6ZL.js} +1 -1
  199. package/dist/{memory-reflection-3BMVNA6T.js → memory-reflection-UP56KTJ5.js} +1 -1
  200. package/dist/{notifications-SCHILQCQ.js → notifications-VWAMI4ZE.js} +7 -7
  201. package/dist/{orchestration-events-RDW7BTCK.js → orchestration-events-7XAYQ2MT.js} +2 -2
  202. package/dist/{orchestrator-NCEFZF32.js → orchestrator-D7NWJTJO.js} +9 -9
  203. package/dist/{plan-limits-YE5WKMQM.js → plan-limits-EA3VBZJ2.js} +2 -2
  204. package/dist/{projection-worker-ENYZ7THQ.js → projection-worker-HNI2TOO3.js} +1 -1
  205. package/dist/{reranker-QFLVGVRT.js → reranker-M52JJADT.js} +1 -1
  206. package/dist/{review-polling-ZKYFETP3.js → review-polling-YT3BEKPV.js} +8 -8
  207. package/dist/runtime/index.js +10 -10
  208. package/dist/{session-events-XCNQDXF4.js → session-events-7EN6W4EX.js} +8 -8
  209. package/dist/{session-kill-telemetry-SPB6LEI5.js → session-kill-telemetry-XIFWDG3L.js} +2 -2
  210. package/dist/{session-scope-FEZQJMD4.js → session-scope-7F3JL2RJ.js} +7 -7
  211. package/dist/{setup-wizard-KUYMMP35.js → setup-wizard-OT5R6PJ6.js} +1 -1
  212. package/dist/{skill-refinement-QHOS6CGQ.js → skill-refinement-N5RINY42.js} +1 -1
  213. package/dist/{stack-update-UCIPCTBO.js → stack-update-SASYGDOB.js} +1 -1
  214. package/dist/{task-enforcement-HVY36QRD.js → task-enforcement-DCBYO3R6.js} +7 -7
  215. package/dist/{task-scope-ODSOEGTH.js → task-scope-NM2C2QZX.js} +7 -7
  216. package/dist/{tasks-crud-RIUHZJGR.js → tasks-crud-AVVOQKZY.js} +17 -7
  217. package/dist/{tasks-review-EBUVFQUJ.js → tasks-review-QNBO3D7U.js} +7 -7
  218. package/dist/{telemetry-upload-DFISOB2V.js → telemetry-upload-ZJ4UP2RP.js} +3 -3
  219. package/dist/{token-budget-6TTILQW7.js → token-budget-SPQB44ZI.js} +1 -1
  220. package/dist/{tool-telemetry-BNZJICWG.js → tool-telemetry-EIRCR2VO.js} +1 -1
  221. package/dist/tui/App.js +15 -15
  222. package/dist/{tui-data-UM6LR4EQ.js → tui-data-5B6YA4VV.js} +7 -7
  223. package/dist/{worker-gate-K5YTO2H5.js → worker-gate-WQV3FR4R.js} +1 -1
  224. package/dist/{workflow-engine-WUU5OZ3N.js → workflow-engine-TZKS5QB7.js} +2 -2
  225. package/dist/{worktree-XZBJQWQZ.js → worktree-KI3X6POI.js} +3 -3
  226. package/package.json +1 -1
  227. package/release-notes.json +52 -52
  228. /package/dist/{chunk-MQA6K7DH.js → chunk-3OZ7LXUY.js} +0 -0
  229. /package/dist/{chunk-KVOTLCAX.js → chunk-4B7JKVUT.js} +0 -0
  230. /package/dist/{chunk-HNXHUG7A.js → chunk-HUVQ2NUX.js} +0 -0
  231. /package/dist/{chunk-Y2B6MB3J.js → chunk-PN7MTAYN.js} +0 -0
  232. /package/dist/{chunk-54JMB5UI.js → chunk-Q3YGPFZH.js} +0 -0
  233. /package/dist/{chunk-WRVFZ433.js → chunk-QSWZI7NH.js} +0 -0
  234. /package/dist/{chunk-4AJYZFDE.js → chunk-V3CEFPKS.js} +0 -0
  235. /package/dist/{chunk-K77FHRYS.js → chunk-V3NHFF3Z.js} +0 -0
  236. /package/dist/{chunk-DXB3CB2O.js → chunk-ZPQ2RUIU.js} +0 -0
  237. /package/dist/{core-memory-NZOLAIOE.js → core-memory-3XHQZ5EI.js} +0 -0
  238. /package/dist/{wiki-acl-QYGMJDUT.js → wiki-acl-QSO37KYW.js} +0 -0
@@ -0,0 +1,225 @@
1
+ import {
2
+ conversationsToMemories,
3
+ detectFormat,
4
+ parseChatGPT,
5
+ parseClaude,
6
+ parseGeneric
7
+ } from "./chunk-Q2G5C3HV.js";
8
+ import {
9
+ extractMemoryGraph
10
+ } from "./chunk-7WEP4DNC.js";
11
+ import {
12
+ flushBatch,
13
+ writeMemory
14
+ } from "./chunk-IEAXNG3H.js";
15
+ import {
16
+ extractKeywords,
17
+ keywordsToString
18
+ } from "./chunk-CHCA3ZM2.js";
19
+ import {
20
+ getActiveAgent
21
+ } from "./chunk-BXLA6CQM.js";
22
+
23
+ // src/mcp/tools/import-conversations.ts
24
+ import { z } from "zod";
25
+ import crypto from "crypto";
26
+ import { readFile, readdir, stat } from "fs/promises";
27
+ import path from "path";
28
+ var DEFAULT_MAX_MEMORIES = 5e4;
29
+ var YIELD_INTERVAL = 50;
30
+ async function parseFile(filePath, format) {
31
+ const content = await readFile(filePath, "utf-8");
32
+ const detectedFormat = format === "auto" ? detectFormat(content, filePath) : format;
33
+ let conversations;
34
+ switch (detectedFormat) {
35
+ case "chatgpt":
36
+ conversations = parseChatGPT(content, filePath);
37
+ break;
38
+ case "claude":
39
+ conversations = parseClaude(content, filePath);
40
+ break;
41
+ case "markdown":
42
+ case "generic":
43
+ default:
44
+ conversations = parseGeneric(content, filePath);
45
+ break;
46
+ }
47
+ return { conversations, detectedFormat };
48
+ }
49
+ async function collectFiles(dirPath) {
50
+ const entries = await readdir(dirPath, { withFileTypes: true });
51
+ const files = [];
52
+ for (const entry of entries) {
53
+ const full = path.join(dirPath, entry.name);
54
+ if (entry.isDirectory()) {
55
+ files.push(...await collectFiles(full));
56
+ } else if (entry.isFile()) {
57
+ const ext = path.extname(entry.name).toLowerCase();
58
+ if ([".json", ".md", ".markdown", ".txt"].includes(ext)) {
59
+ files.push(full);
60
+ }
61
+ }
62
+ }
63
+ return files;
64
+ }
65
+ function registerImportConversations(server) {
66
+ server.registerTool(
67
+ "import_conversations",
68
+ {
69
+ title: "Import Conversations",
70
+ description: "Import conversation history from ChatGPT, Claude, or generic exports. Parses conversations, extracts keywords and graph entities, stores as memories. Supports conversations.json (ChatGPT), Claude JSON export, markdown files, and generic JSON arrays. Deduplicates by content hash.",
71
+ inputSchema: {
72
+ path: z.string().describe("File path or directory to import from"),
73
+ format: z.enum(["chatgpt", "claude", "generic", "markdown", "auto"]).default("auto").describe("Conversation format. 'auto' detects from content."),
74
+ project_name: z.string().optional().describe("Project name for memory scoping"),
75
+ dry_run: z.boolean().default(false).describe("Parse and report counts without storing"),
76
+ force: z.boolean().default(false).describe("Override the 50K memory safety cap"),
77
+ max_memories: z.number().optional().describe("Custom memory cap (default 50000)")
78
+ }
79
+ },
80
+ async ({ path: inputPath, format, project_name, dry_run, force, max_memories }) => {
81
+ const { agentId, agentRole } = getActiveAgent();
82
+ const cap = max_memories ?? DEFAULT_MAX_MEMORIES;
83
+ const resolvedPath = path.resolve(inputPath);
84
+ let files;
85
+ try {
86
+ const stats = await stat(resolvedPath);
87
+ if (stats.isDirectory()) {
88
+ files = await collectFiles(resolvedPath);
89
+ } else {
90
+ files = [resolvedPath];
91
+ }
92
+ } catch (err) {
93
+ return {
94
+ content: [{
95
+ type: "text",
96
+ text: `Path not found: ${resolvedPath}
97
+ ${err instanceof Error ? err.message : String(err)}`
98
+ }],
99
+ isError: true
100
+ };
101
+ }
102
+ if (files.length === 0) {
103
+ return {
104
+ content: [{
105
+ type: "text",
106
+ text: `No importable files found in ${resolvedPath}. Supported: .json, .md, .markdown, .txt`
107
+ }],
108
+ isError: true
109
+ };
110
+ }
111
+ let totalConversations = 0;
112
+ let allDetectedFormat = "generic";
113
+ const allConversations = [];
114
+ for (const file of files) {
115
+ try {
116
+ const { conversations, detectedFormat } = await parseFile(file, format);
117
+ allConversations.push(...conversations);
118
+ totalConversations += conversations.length;
119
+ allDetectedFormat = detectedFormat;
120
+ } catch (err) {
121
+ console.warn(`[import] Skipping ${file}: ${err instanceof Error ? err.message : String(err)}`);
122
+ }
123
+ }
124
+ if (allConversations.length === 0) {
125
+ return {
126
+ content: [{
127
+ type: "text",
128
+ text: `No conversations found in ${files.length} file(s). Format: ${format}. Path: ${resolvedPath}`
129
+ }]
130
+ };
131
+ }
132
+ const memories = conversationsToMemories(allConversations, allDetectedFormat);
133
+ if (memories.length > cap && !force) {
134
+ return {
135
+ content: [{
136
+ type: "text",
137
+ text: `Import would create ${memories.length} memories (cap: ${cap}). Use force=true to override.
138
+ Files: ${files.length} | Conversations: ${totalConversations} | Format: ${allDetectedFormat}`
139
+ }]
140
+ };
141
+ }
142
+ if (dry_run) {
143
+ return {
144
+ content: [{
145
+ type: "text",
146
+ text: `[dry run] Import summary:
147
+ Files: ${files.length}
148
+ Format: ${allDetectedFormat}
149
+ Conversations: ${totalConversations}
150
+ Memories to create: ${memories.length}
151
+ Path: ${resolvedPath}`
152
+ }]
153
+ };
154
+ }
155
+ let stored = 0;
156
+ let skipped = 0;
157
+ const projectName = project_name ?? "import";
158
+ const sessionId = process.env.SESSION_ID ?? "import";
159
+ for (let i = 0; i < memories.length; i++) {
160
+ const mem = memories[i];
161
+ const keywords = extractKeywords(mem.text);
162
+ const graphResult = extractMemoryGraph(
163
+ mem.text,
164
+ agentId,
165
+ projectName,
166
+ "import_conversations"
167
+ );
168
+ const memoryId = crypto.randomUUID();
169
+ try {
170
+ await writeMemory({
171
+ id: memoryId,
172
+ agent_id: agentId,
173
+ agent_role: agentRole,
174
+ session_id: sessionId,
175
+ timestamp: mem.metadata.original_timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
176
+ tool_name: "import_conversations",
177
+ project_name: projectName,
178
+ has_error: false,
179
+ raw_text: mem.text,
180
+ vector: null,
181
+ // NULL vectors — backfill later
182
+ source_path: mem.metadata.source_path,
183
+ source_type: "conversation_import",
184
+ memory_type: "conversation",
185
+ tier: 3,
186
+ // Passive ingestion
187
+ importance: 4,
188
+ // Imported conversations are moderate importance
189
+ intent: "import",
190
+ domain: "conversation",
191
+ keywords: keywordsToString(keywords),
192
+ referenced_entities: graphResult.entities.length > 0 ? JSON.stringify(graphResult.entities.map((e) => e.name)) : null
193
+ });
194
+ stored++;
195
+ } catch {
196
+ skipped++;
197
+ }
198
+ if ((i + 1) % YIELD_INTERVAL === 0) {
199
+ await flushBatch();
200
+ await new Promise((r) => setTimeout(r, 0));
201
+ if ((i + 1) % (YIELD_INTERVAL * 10) === 0) {
202
+ console.log(`[import] ${i + 1}/${memories.length} memories processed, ${stored} stored`);
203
+ }
204
+ }
205
+ }
206
+ await flushBatch();
207
+ return {
208
+ content: [{
209
+ type: "text",
210
+ text: `Import complete.
211
+ Files: ${files.length}
212
+ Format: ${allDetectedFormat}
213
+ Conversations: ${totalConversations}
214
+ Memories stored: ${stored}
215
+ Skipped (duplicates): ${skipped}
216
+ Path: ${resolvedPath}`
217
+ }]
218
+ };
219
+ }
220
+ );
221
+ }
222
+
223
+ export {
224
+ registerImportConversations
225
+ };
@@ -6,7 +6,7 @@ import {
6
6
  findScopedDuplicate,
7
7
  governMemoryRecord,
8
8
  schedulePostWriteMemoryHygiene
9
- } from "./chunk-Y2B6MB3J.js";
9
+ } from "./chunk-PN7MTAYN.js";
10
10
  import {
11
11
  orgBus
12
12
  } from "./chunk-MP2AFCGL.js";
@@ -15,7 +15,7 @@ import {
15
15
  ensureSchema,
16
16
  getClient,
17
17
  initTurso
18
- } from "./chunk-4GXXYDNU.js";
18
+ } from "./chunk-3XGZAFHE.js";
19
19
  import {
20
20
  EMBEDDING_DIM
21
21
  } from "./chunk-FXU7JOXK.js";
@@ -65,7 +65,7 @@ function recordShardFailure(project, error) {
65
65
  `[store] Shard circuit OPEN for "${project}" after ${state.failures} consecutive failures. Will retry in ${SHARD_CB_COOLDOWN_MS / 6e4}min. Last error: ${error}
66
66
  `
67
67
  );
68
- void import("./orchestration-events-RDW7BTCK.js").then(({ recordOrchestrationEventBestEffort }) => {
68
+ void import("./orchestration-events-7XAYQ2MT.js").then(({ recordOrchestrationEventBestEffort }) => {
69
69
  recordOrchestrationEventBestEffort({
70
70
  eventType: "shard.circuit_open",
71
71
  source: "store.recordShardFailure",
@@ -162,7 +162,7 @@ async function initStore(options) {
162
162
  );
163
163
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
164
164
  try {
165
- const { loadGlobalProcedures } = await import("./global-procedures-EYWKFVOO.js");
165
+ const { loadGlobalProcedures } = await import("./global-procedures-74U75Y2I.js");
166
166
  await loadGlobalProcedures();
167
167
  } catch (e) {
168
168
  logStoreWarn("catch", e);
@@ -289,7 +289,7 @@ async function writeMemory(record) {
289
289
  keywords: record.keywords ?? keywordsToString(extractKeywords(record.raw_text))
290
290
  };
291
291
  try {
292
- const { checkMemoryPoisoning } = await import("./memory-poisoning-defense-UXB75NB7.js");
292
+ const { checkMemoryPoisoning } = await import("./memory-poisoning-defense-KBBWI6ZL.js");
293
293
  const poisonResult = await checkMemoryPoisoning({
294
294
  memoryId: dbRow.id,
295
295
  vector: Array.isArray(dbRow.vector) ? dbRow.vector : void 0,
@@ -475,19 +475,19 @@ async function flushBatch() {
475
475
  }
476
476
  _nextVersion = baseVersion;
477
477
  try {
478
- const { insertMemoryCardsForBatch } = await import("./memory-cards-I55OBMVE.js");
478
+ const { insertMemoryCardsForBatch } = await import("./memory-cards-HU5WIUTY.js");
479
479
  await insertMemoryCardsForBatch(batch);
480
480
  } catch (e) {
481
481
  logStoreWarn("catch", e);
482
482
  }
483
483
  try {
484
- const { insertOntologyForBatch } = await import("./agentic-ontology-CHAWPTEY.js");
484
+ const { insertOntologyForBatch } = await import("./agentic-ontology-VZWBROYO.js");
485
485
  await insertOntologyForBatch(batch);
486
486
  } catch (e) {
487
487
  logStoreWarn("catch", e);
488
488
  }
489
489
  try {
490
- const { extractGraphForBatch } = await import("./memory-graph-extractor-6M6J5FNX.js");
490
+ const { extractGraphForBatch } = await import("./memory-graph-extractor-PNRAGL4V.js");
491
491
  await extractGraphForBatch(batch);
492
492
  } catch (e) {
493
493
  logStoreWarn("catch", e);
@@ -859,7 +859,7 @@ function formatReport(report, flags) {
859
859
  return lines.join("\n");
860
860
  }
861
861
  async function fixNullVectors() {
862
- const { tryAcquireWorkerSlot, registerWorkerPid } = await import("./worker-gate-K5YTO2H5.js");
862
+ const { tryAcquireWorkerSlot, registerWorkerPid } = await import("./worker-gate-WQV3FR4R.js");
863
863
  if (!tryAcquireWorkerSlot()) {
864
864
  process.stderr.write("[exe-doctor] Worker gate full \u2014 waiting for existing backfill workers to finish\n");
865
865
  await new Promise((r) => setTimeout(r, 5e3));
@@ -982,7 +982,7 @@ function splitAtSentences(text, maxChunkSize) {
982
982
  }
983
983
  async function main(argv = process.argv.slice(2)) {
984
984
  const flags = parseFlags(argv);
985
- const { fastDbInit } = await import("./fast-db-init-5VDTKVKX.js");
985
+ const { fastDbInit } = await import("./fast-db-init-6DG7B7NO.js");
986
986
  const client = await fastDbInit();
987
987
  const report = await runAudit(client, flags);
988
988
  console.log(formatReport(report, flags));
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  sessionScopeFilter
3
- } from "./chunk-HW46Q4AR.js";
3
+ } from "./chunk-DZE2MBOW.js";
4
4
 
5
5
  // src/lib/git-task-sweep.ts
6
6
  import { execSync } from "child_process";
@@ -178,7 +178,7 @@ async function sweepTasks(projectName, options = {}) {
178
178
  }
179
179
  if (!dryRun) {
180
180
  try {
181
- const { updateTaskStatus } = await import("./tasks-crud-RIUHZJGR.js");
181
+ const { updateTaskStatus } = await import("./tasks-crud-AVVOQKZY.js");
182
182
  await updateTaskStatus({
183
183
  taskId: task.id,
184
184
  status: "needs_review",
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  createCRMWebhookHandler,
15
15
  parseTwentyWebhook
16
- } from "./chunk-S2T7CE4P.js";
16
+ } from "./chunk-BMRLKOGT.js";
17
17
  import {
18
18
  WhatsAppAdapter
19
19
  } from "./chunk-ECSNSHZ7.js";
@@ -49,7 +49,7 @@ import {
49
49
  retryWithBackoff,
50
50
  routeMessage,
51
51
  validateGatewayConfig
52
- } from "./chunk-T43SC2XX.js";
52
+ } from "./chunk-FJQPH2W3.js";
53
53
  import {
54
54
  getAccountByName,
55
55
  getAccountByPhoneNumberId,
@@ -4,13 +4,13 @@ import {
4
4
  resolveExeSession,
5
5
  sendIntercom,
6
6
  strictSessionScopeFilter
7
- } from "./chunk-HW46Q4AR.js";
7
+ } from "./chunk-DZE2MBOW.js";
8
8
  import {
9
9
  recordOrchestrationEventBestEffort
10
- } from "./chunk-HURS5JVX.js";
10
+ } from "./chunk-BCREJ6FD.js";
11
11
  import {
12
12
  getClient
13
- } from "./chunk-4GXXYDNU.js";
13
+ } from "./chunk-3XGZAFHE.js";
14
14
 
15
15
  // src/lib/messaging.ts
16
16
  import crypto from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isCrdtSyncEnabled
3
- } from "./chunk-WRVFZ433.js";
3
+ } from "./chunk-QSWZI7NH.js";
4
4
  import {
5
5
  decryptSyncBlob,
6
6
  encryptSyncBlob,
@@ -12,7 +12,7 @@ import {
12
12
  loadEmployees,
13
13
  registerBinSymlinks,
14
14
  saveEmployees
15
- } from "./chunk-4GXXYDNU.js";
15
+ } from "./chunk-3XGZAFHE.js";
16
16
  import {
17
17
  loadDeviceId
18
18
  } from "./chunk-72DJ42IE.js";
@@ -489,7 +489,7 @@ async function cloudSync(config) {
489
489
  let pulled = 0;
490
490
  if (pullResult.records.length > 0) {
491
491
  if (isCrdtSyncEnabled()) {
492
- const { initCrdtDoc, importExistingMemories, readAllMemories } = await import("./crdt-sync-TSO4O67N.js");
492
+ const { initCrdtDoc, importExistingMemories, readAllMemories } = await import("./crdt-sync-KFPZWAIE.js");
493
493
  initCrdtDoc();
494
494
  importExistingMemories(
495
495
  pullResult.records.map((rec) => ({
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  sendMessage
3
- } from "./chunk-R2MZDZTQ.js";
3
+ } from "./chunk-JUWC7V2I.js";
4
4
  import {
5
5
  getActiveAgent
6
- } from "./chunk-S4I3UITZ.js";
6
+ } from "./chunk-BXLA6CQM.js";
7
7
 
8
8
  // src/mcp/tools/send-message.ts
9
9
  import { z } from "zod";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getClient
3
- } from "./chunk-4GXXYDNU.js";
3
+ } from "./chunk-3XGZAFHE.js";
4
4
 
5
5
  // src/lib/reminders.ts
6
6
  import crypto from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recordOrchestrationEventBestEffort
3
- } from "./chunk-HURS5JVX.js";
3
+ } from "./chunk-BCREJ6FD.js";
4
4
 
5
5
  // src/lib/session-registry.ts
6
6
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createReminder
3
- } from "./chunk-I22ZH3XF.js";
3
+ } from "./chunk-PXO6SF6L.js";
4
4
 
5
5
  // src/mcp/tools/create-reminder.ts
6
6
  import { z } from "zod";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-GNHN5HRQ.js";
4
4
  import {
5
5
  buildClaudeHttpMcpEntry
6
- } from "./chunk-S5TTV7PC.js";
6
+ } from "./chunk-A24RUKU6.js";
7
7
  import {
8
8
  EXE_HOOKS,
9
9
  isLegacyHomeDirHookCommand,
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-L3TB7CC3.js";
12
12
  import {
13
13
  ensureAllAgentSymlinks
14
- } from "./chunk-5QKUR5KS.js";
14
+ } from "./chunk-4UHKYQZJ.js";
15
15
  import {
16
16
  MCP_LEGACY_KEY,
17
17
  MCP_PRIMARY_KEY
@@ -864,6 +864,7 @@ function setupTmux(home) {
864
864
  const userTmuxConf = path.join(homeDir, ".tmux.conf");
865
865
  const backupPath = path.join(homeDir, ".tmux.conf.backup");
866
866
  const sourceLine = "source-file ~/.exe-os/tmux.conf";
867
+ const shellQuote = (value) => `'${value.replace(/'/g, "'\\''")}'`;
867
868
  const pkgRoot = resolvePackageRoot();
868
869
  const assetPath = path.join(pkgRoot, "dist", "assets", "tmux.conf");
869
870
  if (!existsSync(assetPath)) {
@@ -913,7 +914,7 @@ ${sourceLine}
913
914
  `);
914
915
  }
915
916
  try {
916
- execSync(`tmux source-file ${exeTmuxConf} 2>/dev/null`);
917
+ execSync(`tmux source-file ${shellQuote(userTmuxConf)} 2>/dev/null`);
917
918
  } catch {
918
919
  }
919
920
  process.stderr.write("exe-os: tmux config installed\n");
@@ -111,7 +111,7 @@ function releaseBackfillLock() {
111
111
  }
112
112
  async function getTaskAwareCapacity() {
113
113
  const { getClient } = await import("./lib/database.js");
114
- const { sessionScopeFilter } = await import("./task-scope-ODSOEGTH.js");
114
+ const { sessionScopeFilter } = await import("./task-scope-NM2C2QZX.js");
115
115
  const client = getClient();
116
116
  const scope = sessionScopeFilter();
117
117
  const result = await client.execute({
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getGlobalProceduresBlock
3
- } from "./chunk-JXJV64JW.js";
3
+ } from "./chunk-D7DRYXDE.js";
4
4
 
5
5
  // src/lib/employee-templates.ts
6
6
  var BASE_OPERATING_PROCEDURES = `
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getClient
3
- } from "./chunk-4GXXYDNU.js";
3
+ } from "./chunk-3XGZAFHE.js";
4
4
 
5
5
  // src/lib/behaviors.ts
6
6
  import crypto from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isCoordinatorRole
3
- } from "./chunk-4GXXYDNU.js";
3
+ } from "./chunk-3XGZAFHE.js";
4
4
 
5
5
  // src/lib/task-router.ts
6
6
  import { randomUUID } from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  routeTask
3
- } from "./chunk-NUUAYDD6.js";
3
+ } from "./chunk-SRJLG6RC.js";
4
4
  import {
5
5
  createTaskCore,
6
6
  employeeSessionName,
@@ -9,11 +9,11 @@ import {
9
9
  isEmployeeAlive,
10
10
  sessionScopeFilter,
11
11
  updateTaskStatus
12
- } from "./chunk-HW46Q4AR.js";
12
+ } from "./chunk-DZE2MBOW.js";
13
13
  import {
14
14
  DEFAULT_COORDINATOR_TEMPLATE_NAME,
15
15
  getCoordinatorName
16
- } from "./chunk-4GXXYDNU.js";
16
+ } from "./chunk-3XGZAFHE.js";
17
17
 
18
18
  // src/runtime/orchestrator.ts
19
19
  var STALE_THRESHOLD_MS = 2 * 60 * 60 * 1e3;
@@ -61,7 +61,7 @@ async function rollbackStackUpdate(options) {
61
61
  }
62
62
  writeFileSync(options.envFile, readFileSync(rollbackEnv), { mode: 384 });
63
63
  const composeArgs = ["compose", "--file", options.composeFile, "--env-file", options.envFile];
64
- exec("docker", [...composeArgs, "up", "-d"]);
64
+ exec("docker", [...composeArgs, "up", "-d", "--remove-orphans"]);
65
65
  return { status: "rolled_back", targetVersion: "previous", changes: [], backupEnvFile: rollbackEnv, lockFile: options.lockFile ?? path.join(path.dirname(options.envFile), ".exe-stack-lock.json") };
66
66
  }
67
67
  function parseStackManifest(raw, publicKey) {
@@ -414,6 +414,11 @@ function bootstrapStackHost(options) {
414
414
  }
415
415
  copyTemplateIfMissing("deploy/compose/docker-compose.yml", options.composeFile, createdFiles);
416
416
  copyTemplateIfMissing("deploy/compose/.env.customer.example", options.envFile, createdFiles);
417
+ copyTemplateIfMissing(
418
+ "deploy/compose/clickhouse-config.xml",
419
+ path.join(path.dirname(options.envFile), "clickhouse-config.xml"),
420
+ createdFiles
421
+ );
417
422
  const brandingDest = path.join(path.dirname(options.envFile), "branding.json");
418
423
  copyTemplateIfMissing("deploy/compose/gateway.json", path.join(path.dirname(options.envFile), "gateway.json"), createdFiles);
419
424
  if (!existsSync(brandingDest)) writeFileSync(brandingDest, JSON.stringify({ brandName: "Hygo", productName: "Hygo OS" }, null, 2) + "\n", { mode: 384 });
@@ -471,7 +476,8 @@ function areStackContainersRunning(composeFile, envFile) {
471
476
  }
472
477
  var CRITICAL_BIND_MOUNTS = [
473
478
  { file: ".env", description: "secrets and image tags" },
474
- { file: "gateway.json", description: "gateway connector config" }
479
+ { file: "gateway.json", description: "gateway connector config" },
480
+ { file: "clickhouse-config.xml", description: "ClickHouse memory/log guardrails" }
475
481
  ];
476
482
  var CRITICAL_VOLUMES = [
477
483
  "postgres_data",
@@ -509,6 +515,44 @@ function assertVolumesIntact() {
509
515
  console.warn("[stack-update] Data may have been lost. Check if 'docker compose down -v' was used.");
510
516
  }
511
517
  }
518
+ function warnLegacyDaemonVolume() {
519
+ const legacy = spawnSync("docker", ["volume", "inspect", "exe-os_exed_data"], {
520
+ stdio: ["pipe", "pipe", "pipe"],
521
+ timeout: 5e3
522
+ });
523
+ const current = spawnSync("docker", ["volume", "inspect", "exe-os_exe_os_data"], {
524
+ stdio: ["pipe", "pipe", "pipe"],
525
+ timeout: 5e3
526
+ });
527
+ if (legacy.status === 0 && current.status === 0) {
528
+ console.warn("[stack-update] \u26A0 Legacy volume exe-os_exed_data exists alongside exe-os_exe_os_data.");
529
+ console.warn("[stack-update] Keeping both volumes intact; after verifying exe_os_data has current daemon state, remove exed_data manually if desired.");
530
+ }
531
+ }
532
+ function applyPostgresInitSql(composeArgs, envFile, composeFile, exec) {
533
+ const initSql = path.join(path.dirname(composeFile), "init-db.sql");
534
+ if (!existsSync(initSql)) return;
535
+ const env = parseEnv(readFileSync(envFile, "utf8"));
536
+ const postgresUser = env.get("POSTGRES_USER") || "exe";
537
+ const postgresDb = env.get("POSTGRES_DB") || "exedb";
538
+ console.log("[stack-update] Applying idempotent Postgres init SQL for existing volumes...");
539
+ exec("docker", [
540
+ ...composeArgs,
541
+ "exec",
542
+ "-T",
543
+ "exe-db",
544
+ "psql",
545
+ "-v",
546
+ "ON_ERROR_STOP=1",
547
+ "-U",
548
+ postgresUser,
549
+ "-d",
550
+ postgresDb,
551
+ "-f",
552
+ "/docker-entrypoint-initdb.d/01-init.sql"
553
+ ]);
554
+ console.log("[stack-update] \u2713 Postgres init SQL applied");
555
+ }
512
556
  async function runStackUpdate(options) {
513
557
  const exec = options.exec ?? defaultExec;
514
558
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
@@ -518,6 +562,7 @@ async function runStackUpdate(options) {
518
562
  if (!options.dryRun) assertHostReadyForApply(report);
519
563
  }
520
564
  assertBindMountsExist(path.dirname(options.envFile));
565
+ warnLegacyDaemonVolume();
521
566
  if (!options.dryRun) {
522
567
  try {
523
568
  const { runPreflight } = await import("./preflight-EAH2MI76.js");
@@ -587,7 +632,7 @@ async function runStackUpdate(options) {
587
632
  mkdirSync(updateBackupDir, { recursive: true });
588
633
  const backupEnvFile = path.join(updateBackupDir, "env.bak");
589
634
  writeFileSync(backupEnvFile, envRaw, { mode: 384 });
590
- const protectedFiles = ["gateway.json", "branding.json"];
635
+ const protectedFiles = ["gateway.json", "branding.json", "clickhouse-config.xml"];
591
636
  for (const f of protectedFiles) {
592
637
  const src = path.join(stackDir, f);
593
638
  try {
@@ -690,9 +735,11 @@ async function runStackUpdate(options) {
690
735
  const RESTART_ORDER = [
691
736
  "exe-db",
692
737
  // data layer — must be healthy before apps
738
+ "gotrue",
739
+ // auth — depends on idempotent init-db.sql applied after exe-db
693
740
  "clickhouse",
694
741
  "redis",
695
- "exed",
742
+ "exe-os",
696
743
  // daemon — has its own health endpoint
697
744
  "exe-crm",
698
745
  // CRM app
@@ -706,6 +753,7 @@ async function runStackUpdate(options) {
706
753
  const preSnapshot = spawnSync("docker", ["ps", "--format", "json"], { encoding: "utf8", timeout: 1e4 });
707
754
  const preContainerCount = preSnapshot.stdout?.split("\n").filter(Boolean).length ?? 0;
708
755
  console.log(`[stack-update] Pre-update snapshot: ${preContainerCount} containers`);
756
+ let restartedAnyService = false;
709
757
  for (const service of RESTART_ORDER) {
710
758
  const psResult = spawnSync("docker", [...composeArgs, "ps", "--quiet", service], {
711
759
  encoding: "utf8",
@@ -713,7 +761,8 @@ async function runStackUpdate(options) {
713
761
  timeout: 1e4
714
762
  });
715
763
  if (psResult.status !== 0) continue;
716
- exec("docker", [...composeArgs, "up", "-d", "--no-deps", service]);
764
+ exec("docker", [...composeArgs, "up", "-d", "--remove-orphans", "--no-deps", service]);
765
+ restartedAnyService = true;
717
766
  const maxWait = 60;
718
767
  let healthy = false;
719
768
  for (let i = 0; i < maxWait; i++) {
@@ -736,8 +785,14 @@ async function runStackUpdate(options) {
736
785
  if (!healthy) {
737
786
  throw new Error(`Service ${service} failed health check after ${maxWait}s \u2014 aborting update`);
738
787
  }
788
+ if (service === "exe-db") {
789
+ applyPostgresInitSql(composeArgs, options.envFile, options.composeFile, exec);
790
+ }
739
791
  console.log(`[stack-update] \u2713 ${service} restarted and healthy`);
740
792
  }
793
+ if (!restartedAnyService) {
794
+ exec("docker", [...composeArgs, "up", "-d", "--remove-orphans"]);
795
+ }
741
796
  const postSnapshot = spawnSync("docker", ["ps", "--format", "json"], { encoding: "utf8", timeout: 1e4 });
742
797
  const postContainerCount = postSnapshot.stdout?.split("\n").filter(Boolean).length ?? 0;
743
798
  console.log(`[stack-update] Post-update snapshot: ${postContainerCount} containers (was ${preContainerCount})`);
@@ -785,7 +840,7 @@ async function runStackUpdate(options) {
785
840
  } catch (err) {
786
841
  writeFileSync(options.envFile, envRaw, { mode: 384 });
787
842
  try {
788
- exec("docker", [...composeArgs, "up", "-d"]);
843
+ exec("docker", [...composeArgs, "up", "-d", "--remove-orphans"]);
789
844
  } catch {
790
845
  }
791
846
  const reason = err instanceof Error ? err.message : String(err);