@askexenow/exe-os 0.9.166 → 0.9.167

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 (244) hide show
  1. package/deploy/compose/backup.sh +45 -7
  2. package/deploy/compose/setup.sh +7 -0
  3. package/deploy/stack-manifests/v0.9.json +40 -1
  4. package/dist/{active-agent-R2KMWMR6.js → active-agent-DGTIJN2U.js} +2 -2
  5. package/dist/{active-agent-CYMM3QQA.js → active-agent-HVMLG6FH.js} +2 -2
  6. package/dist/{agentic-ontology-GKAKYNPE.js → agentic-ontology-S54AFODT.js} +1 -1
  7. package/dist/{backfill-metadata-Z5SYUWAV.js → backfill-metadata-74IWETRF.js} +4 -3
  8. package/dist/{behaviors-QGU6XI5R.js → behaviors-LZVAVHTC.js} +2 -2
  9. package/dist/bin/agentic-ontology-backfill.js +5 -4
  10. package/dist/bin/agentic-reflection-backfill.js +6 -5
  11. package/dist/bin/agentic-semantic-label.js +5 -4
  12. package/dist/bin/backfill-conversations.js +5 -4
  13. package/dist/bin/backfill-responses.js +5 -4
  14. package/dist/bin/backfill-vectors.js +6 -5
  15. package/dist/bin/bulk-sync-postgres.js +6 -5
  16. package/dist/bin/cleanup-stale-review-tasks.js +9 -9
  17. package/dist/bin/cli.js +16 -13
  18. package/dist/bin/daily-summary.js +0 -217
  19. package/dist/bin/deferred-daemon-restart.js +8 -0
  20. package/dist/bin/exe-agent-config.js +1 -1
  21. package/dist/bin/exe-agent.js +10 -10
  22. package/dist/bin/exe-assign.js +7 -6
  23. package/dist/bin/exe-boot.js +20 -19
  24. package/dist/bin/exe-call.js +4 -4
  25. package/dist/bin/exe-cloud.js +3 -3
  26. package/dist/bin/exe-dispatch.js +9 -9
  27. package/dist/bin/exe-doctor.js +1 -1
  28. package/dist/bin/exe-export-behaviors.js +7 -6
  29. package/dist/bin/exe-forget.js +6 -5
  30. package/dist/bin/exe-gateway.js +5 -5
  31. package/dist/bin/exe-heartbeat.js +9 -9
  32. package/dist/bin/exe-kill.js +13 -12
  33. package/dist/bin/exe-launch-agent.js +11 -10
  34. package/dist/bin/exe-new-employee.js +6 -6
  35. package/dist/bin/exe-pending-messages.js +10 -10
  36. package/dist/bin/exe-pending-notifications.js +9 -9
  37. package/dist/bin/exe-pending-reviews.js +9 -9
  38. package/dist/bin/exe-rename.js +4 -4
  39. package/dist/bin/exe-review.js +12 -11
  40. package/dist/bin/exe-search.js +5 -4
  41. package/dist/bin/exe-session-cleanup.js +19 -14
  42. package/dist/bin/exe-settings.js +3 -3
  43. package/dist/bin/exe-start-codex.js +11 -10
  44. package/dist/bin/exe-start-opencode.js +8 -7
  45. package/dist/bin/exe-status.js +10 -10
  46. package/dist/bin/exe-team.js +2 -2
  47. package/dist/bin/git-sweep.js +9 -9
  48. package/dist/bin/graph-backfill.js +4 -3
  49. package/dist/bin/graph-export.js +5 -4
  50. package/dist/bin/import-history.js +171 -0
  51. package/dist/bin/install-launchd.js +41 -0
  52. package/dist/bin/install.js +50 -74
  53. package/dist/bin/intercom-check.js +4 -4
  54. package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
  55. package/dist/bin/postgres-agentic-semantic-backfill.js +4 -4
  56. package/dist/bin/pre-publish.js +1 -1
  57. package/dist/bin/scan-tasks.js +22 -12
  58. package/dist/bin/setup.js +1 -1
  59. package/dist/bin/shard-migrate.js +4 -3
  60. package/dist/bin/stack-update.js +61 -857
  61. package/dist/bin/vps-backup.js +170 -0
  62. package/dist/bin/vps-health-gate.js +232 -0
  63. package/dist/{capacity-monitor-ZEAE4WP2.js → capacity-monitor-JBZB2S4P.js} +10 -10
  64. package/dist/{catchup-brief-OGWCHENC.js → catchup-brief-HE2EMZS5.js} +12 -11
  65. package/dist/{chunk-DJJNB47C.js → chunk-27DO3EZO.js} +1 -1
  66. package/dist/{chunk-45FYZIHI.js → chunk-32YUET3Y.js} +2 -2
  67. package/dist/{chunk-Y75ECPO5.js → chunk-3FW5LUGI.js} +2 -2
  68. package/dist/{chunk-4OZGQZ4U.js → chunk-3M3O56VT.js} +636 -179
  69. package/dist/{chunk-77WQOD6J.js → chunk-4CXUZ4NI.js} +2 -2
  70. package/dist/{chunk-PBXWPHEK.js → chunk-4VEHJZ6R.js} +1 -1
  71. package/dist/{chunk-TH22QIEC.js → chunk-6A4COFDG.js} +1 -1
  72. package/dist/{chunk-ACBTCC2L.js → chunk-7OJH2A6I.js} +1 -1
  73. package/dist/{chunk-NHCOTCI6.js → chunk-A7SGEBXJ.js} +2 -2
  74. package/dist/{chunk-5MPQSNZF.js → chunk-AUTCT6AY.js} +1 -1
  75. package/dist/{chunk-OEKSTOTE.js → chunk-AZAZ2C75.js} +1 -1
  76. package/dist/chunk-CHCA3ZM2.js +167 -0
  77. package/dist/{chunk-X347L57O.js → chunk-CSTJQDOE.js} +4 -3
  78. package/dist/{chunk-B234R3VW.js → chunk-D7WLV6WD.js} +2 -2
  79. package/dist/{chunk-GMXF3AHJ.js → chunk-DGAONW36.js} +1 -1
  80. package/dist/chunk-EAT5YL3W.js +229 -0
  81. package/dist/{chunk-OD4H5YCJ.js → chunk-EKTQE2R5.js} +8 -8
  82. package/dist/{chunk-Z44PC42G.js → chunk-ELUBA7XL.js} +2 -2
  83. package/dist/{chunk-ZWS6XQER.js → chunk-F5AKOE4P.js} +7 -7
  84. package/dist/{chunk-T5YULDDO.js → chunk-FVI4UBKO.js} +27 -4
  85. package/dist/{chunk-ESRI7MFI.js → chunk-GAN7PW6G.js} +28 -24
  86. package/dist/{chunk-K4OWYJSP.js → chunk-GM2WZTG3.js} +2 -2
  87. package/dist/{chunk-TAB5QGIK.js → chunk-GZYQTPTF.js} +3 -3
  88. package/dist/{chunk-CXDU5DE3.js → chunk-IAUNGATJ.js} +1 -1
  89. package/dist/{chunk-YS63NS6M.js → chunk-IHSM5GR4.js} +1 -1
  90. package/dist/{chunk-23PTS2ZD.js → chunk-IP7KJAUW.js} +117 -15
  91. package/dist/{chunk-D6IMJAV2.js → chunk-J64P2LB2.js} +2 -2
  92. package/dist/{chunk-CXAVSQZM.js → chunk-JXMSCKRM.js} +1 -1
  93. package/dist/{chunk-RQMK3IQH.js → chunk-K4OTJP6N.js} +14 -7
  94. package/dist/{chunk-L7ROZR2H.js → chunk-KXAUMIOX.js} +1 -1
  95. package/dist/{chunk-TPC3LAP7.js → chunk-LGY2BIOT.js} +13 -0
  96. package/dist/{chunk-RPIDSBK7.js → chunk-LLHRJEE4.js} +3 -3
  97. package/dist/{chunk-6WG2VIKC.js → chunk-LM7H6XU4.js} +1 -1
  98. package/dist/{chunk-Y6GMKZZ2.js → chunk-LOFFGJSY.js} +150 -23
  99. package/dist/{chunk-W7SDGBEC.js → chunk-MFI5OXYW.js} +52 -84
  100. package/dist/{chunk-KNPEVPYG.js → chunk-MSSQWF6X.js} +2 -2
  101. package/dist/{chunk-QIQAO3VG.js → chunk-NEFFFKMD.js} +3 -3
  102. package/dist/{chunk-YUC552KZ.js → chunk-NEHONJJC.js} +3 -3
  103. package/dist/{chunk-KZ7SXZ2V.js → chunk-NFMQRLCD.js} +1 -1
  104. package/dist/{chunk-52HCNDPG.js → chunk-O4TATDOV.js} +1 -1
  105. package/dist/{chunk-AR3OYGLB.js → chunk-PEFBRL4S.js} +28 -6
  106. package/dist/{chunk-AEUXUEJG.js → chunk-PEXVU3HU.js} +5 -3
  107. package/dist/chunk-Q2G5C3HV.js +217 -0
  108. package/dist/{chunk-KOO56JVC.js → chunk-Q6N6LDEJ.js} +1 -1
  109. package/dist/{chunk-TXSJ2L5O.js → chunk-QI4IXJN7.js} +1 -1
  110. package/dist/{chunk-HLVQ5Y7B.js → chunk-RE4VLK45.js} +1 -1
  111. package/dist/{chunk-TF6SZGDT.js → chunk-SA2PH6WY.js} +1 -1
  112. package/dist/{chunk-5RSYY7BE.js → chunk-SJYOPYXH.js} +117 -9
  113. package/dist/{chunk-PJGHBANY.js → chunk-TTJE7CCU.js} +1 -1
  114. package/dist/{chunk-A7KEWR6S.js → chunk-TXWQPL2U.js} +1 -1
  115. package/dist/{chunk-XXSJ35J5.js → chunk-U5ZH52FB.js} +2 -2
  116. package/dist/{chunk-G4FDG3LK.js → chunk-UVNDLF74.js} +63 -40
  117. package/dist/{chunk-5OD3AFRW.js → chunk-V6RCZ25F.js} +1 -1
  118. package/dist/{chunk-LHMBIFKD.js → chunk-VYNNN2S3.js} +4 -4
  119. package/dist/chunk-WCYT54XP.js +934 -0
  120. package/dist/{chunk-5AMSQRHT.js → chunk-XGYSTVUH.js} +1 -1
  121. package/dist/{chunk-MKZBHM6A.js → chunk-XLWF3C4R.js} +4 -4
  122. package/dist/{chunk-YL36L2SN.js → chunk-Y7YHLV57.js} +1 -1
  123. package/dist/{chunk-HZC4MR4H.js → chunk-YBKB2PXY.js} +1 -1
  124. package/dist/{chunk-NWM3A4TK.js → chunk-ZDNLKXZA.js} +1 -1
  125. package/dist/{chunk-O7KW6QMH.js → chunk-ZW4TKQUM.js} +15 -5
  126. package/dist/{chunk-6BURHBE6.js → chunk-ZXB44R3E.js} +32 -11
  127. package/dist/co-occurrence-WCED475N.js +73 -0
  128. package/dist/{code-context-index-B6VIWPSF.js → code-context-index-LSZ3DKTJ.js} +2 -2
  129. package/dist/{crdt-sync-XA22KI3S.js → crdt-sync-PBXZTHZC.js} +1 -1
  130. package/dist/{crm-webhook-CIZNOEY4.js → crm-webhook-W7Q25VZU.js} +2 -2
  131. package/dist/{cto-delegation-gate-H5IULFRC.js → cto-delegation-gate-JKULOLMC.js} +8 -8
  132. package/dist/{daemon-orchestration-VO5XQIJL.js → daemon-orchestration-CHV6MB42.js} +13 -11
  133. package/dist/{exe-drift-DMT75WR3.js → exe-drift-PW36OULT.js} +2 -2
  134. package/dist/{exe-export-2RZWOSX6.js → exe-export-XQOD3KE6.js} +6 -5
  135. package/dist/{exe-import-NFNYATHL.js → exe-import-QOFP67LW.js} +6 -5
  136. package/dist/{exe-key-4D7CF3BU.js → exe-key-WQ34UZR6.js} +1 -1
  137. package/dist/{fast-db-init-LAEISZQ2.js → fast-db-init-UKETGWQI.js} +1 -1
  138. package/dist/gateway/index.js +6 -6
  139. package/dist/{git-staleness-M46AYLPP.js → git-staleness-ATV5CGAP.js} +1 -1
  140. package/dist/{git-task-sweep-PXOS56YT.js → git-task-sweep-KXZRIP4T.js} +9 -9
  141. package/dist/{global-procedures-KROQQX54.js → global-procedures-G6IKCYKM.js} +3 -3
  142. package/dist/{graph-auto-extract-QJ2BBJM2.js → graph-auto-extract-ZJXJOLE2.js} +1 -1
  143. package/dist/hooks/bug-report-worker.js +10 -10
  144. package/dist/hooks/codex-stop-task-finalizer.js +10 -10
  145. package/dist/hooks/commit-complete.js +11 -11
  146. package/dist/hooks/error-recall.js +8 -7
  147. package/dist/hooks/exe-heartbeat-hook.js +2 -2
  148. package/dist/hooks/ingest-worker.js +3 -3
  149. package/dist/hooks/ingest.js +9 -9
  150. package/dist/hooks/instructions-loaded.js +3 -3
  151. package/dist/hooks/notification.js +3 -3
  152. package/dist/hooks/post-compact.js +10 -10
  153. package/dist/hooks/post-tool-combined.js +5 -5
  154. package/dist/hooks/pre-compact.js +16 -16
  155. package/dist/hooks/pre-tool-use.js +14 -14
  156. package/dist/hooks/prompt-submit.js +30 -29
  157. package/dist/hooks/session-end.js +46 -25
  158. package/dist/hooks/session-start.js +48 -10
  159. package/dist/hooks/stop.js +17 -17
  160. package/dist/hooks/subagent-stop.js +10 -10
  161. package/dist/hooks/summary-worker.js +17 -16
  162. package/dist/index.js +17 -17
  163. package/dist/{installer-SDBLJBAB.js → installer-DE2LH5EC.js} +4 -4
  164. package/dist/{installer-ZA6QNQ4P.js → installer-M2MDS7HC.js} +4 -4
  165. package/dist/{installer-6KAY6LD6.js → installer-VE23YFXU.js} +4 -4
  166. package/dist/{intercom-queue-K3DVKSPJ.js → intercom-queue-RNM6EPGA.js} +1 -1
  167. package/dist/keyword-extractor-UJHFWVZE.js +11 -0
  168. package/dist/lib/cloud-sync.js +3 -3
  169. package/dist/lib/consolidation.js +5 -4
  170. package/dist/lib/database.js +1 -1
  171. package/dist/lib/db-daemon-client.js +1 -1
  172. package/dist/lib/db.js +1 -1
  173. package/dist/lib/embed-worker.js +98 -0
  174. package/dist/lib/embedder.js +2 -2
  175. package/dist/lib/employee-templates.js +4 -4
  176. package/dist/lib/employees.js +1 -1
  177. package/dist/lib/exe-daemon-client.js +1 -1
  178. package/dist/lib/exe-daemon.js +523 -500
  179. package/dist/lib/hybrid-search.js +5 -6
  180. package/dist/lib/identity.js +1 -1
  181. package/dist/lib/messaging.js +9 -9
  182. package/dist/lib/reminders.js +2 -2
  183. package/dist/lib/schedules.js +5 -4
  184. package/dist/lib/skill-learning.js +3 -3
  185. package/dist/lib/store.js +4 -3
  186. package/dist/lib/task-router.js +2 -2
  187. package/dist/lib/tasks.js +9 -9
  188. package/dist/lib/tmux-routing.js +8 -8
  189. package/dist/lib/tmux-transport.js +1 -1
  190. package/dist/lib/token-spend.js +2 -2
  191. package/dist/lib/transport.js +2 -2
  192. package/dist/lib/ws-client.js +3 -1
  193. package/dist/mcp/register-tools.js +54 -51
  194. package/dist/mcp/server.js +58 -55
  195. package/dist/mcp/tools/complete-reminder.js +3 -3
  196. package/dist/mcp/tools/create-reminder.js +3 -3
  197. package/dist/mcp/tools/create-task.js +11 -11
  198. package/dist/mcp/tools/deactivate-behavior.js +4 -4
  199. package/dist/mcp/tools/list-reminders.js +3 -3
  200. package/dist/mcp/tools/list-tasks.js +11 -11
  201. package/dist/mcp/tools/send-message.js +11 -11
  202. package/dist/mcp/tools/update-task.js +10 -10
  203. package/dist/{mcp-http-config-LK2EDOEJ.js → mcp-http-config-Z2E4VUOF.js} +2 -2
  204. package/dist/{memory-cards-V3DKSRWL.js → memory-cards-SFDKDIAW.js} +1 -1
  205. package/dist/memory-graph-extractor-YD4GNH7T.js +16 -0
  206. package/dist/{memory-poisoning-defense-3B75HS74.js → memory-poisoning-defense-VEGNFELN.js} +1 -1
  207. package/dist/{memory-queue-client-LFPZPPQA.js → memory-queue-client-5HB2XUH7.js} +2 -2
  208. package/dist/{memory-reflection-HTDAUUE5.js → memory-reflection-MTPRQNI6.js} +2 -2
  209. package/dist/{notifications-76VCYXWW.js → notifications-6TCE6OBG.js} +8 -8
  210. package/dist/{orchestrator-CBNSBI5P.js → orchestrator-W2GYJR23.js} +10 -10
  211. package/dist/{plan-limits-SOR3QXKV.js → plan-limits-4EP46323.js} +2 -2
  212. package/dist/{projection-worker-FK5YOEIL.js → projection-worker-EBUYNMU2.js} +1 -1
  213. package/dist/{review-polling-ZLNDUKL4.js → review-polling-2N7KQFZZ.js} +9 -9
  214. package/dist/runtime/index.js +15 -15
  215. package/dist/{session-events-CUSPL25D.js → session-events-K47FHAXJ.js} +9 -9
  216. package/dist/{session-kill-telemetry-FLBRHBDP.js → session-kill-telemetry-275YUXM5.js} +2 -2
  217. package/dist/{session-scope-PX2ABSJO.js → session-scope-XSFJZEER.js} +8 -8
  218. package/dist/{setup-wizard-Y6PBZGFX.js → setup-wizard-UEO7HYLQ.js} +1 -1
  219. package/dist/{skill-refinement-L7PGKCYO.js → skill-refinement-WXBTANDQ.js} +1 -1
  220. package/dist/stack-update-2B2UXREV.js +50 -0
  221. package/dist/{task-enforcement-7FUILB63.js → task-enforcement-2JIJSXPU.js} +14 -16
  222. package/dist/{task-scope-2N45TE32.js → task-scope-W73Z3XWE.js} +8 -8
  223. package/dist/{tasks-crud-ADLCGHGH.js → tasks-crud-HPJKI3QQ.js} +8 -8
  224. package/dist/{tasks-review-PJ2DUI6N.js → tasks-review-MXLMPGNZ.js} +8 -8
  225. package/dist/{token-budget-T5DFXVTM.js → token-budget-BA46CVHX.js} +1 -1
  226. package/dist/{tool-capability-index-6JJN6ZRC.js → tool-capability-index-42VVN5BS.js} +1 -1
  227. package/dist/{tool-telemetry-72PVO5HV.js → tool-telemetry-GZ5E2AUL.js} +1 -1
  228. package/dist/tui/App.js +22 -18
  229. package/dist/{tui-data-63JHE6EZ.js → tui-data-PVXWQCJX.js} +8 -8
  230. package/dist/{worker-gate-REVBJUZ6.js → worker-gate-WTTK64TK.js} +1 -1
  231. package/dist/{workflow-engine-W2WNHJG5.js → workflow-engine-LT3WTT7V.js} +2 -2
  232. package/package.json +1 -1
  233. package/release-notes.json +209 -209
  234. /package/dist/{chunk-BNOZUS6J.js → chunk-6VVCAVRT.js} +0 -0
  235. /package/dist/{chunk-IC7GKK6I.js → chunk-CWQZZ7X3.js} +0 -0
  236. /package/dist/{chunk-ZI2ZVERO.js → chunk-EIW5GOBW.js} +0 -0
  237. /package/dist/{chunk-2BGGDNRD.js → chunk-IPPJEM26.js} +0 -0
  238. /package/dist/{chunk-4ISDU5KR.js → chunk-K5UR73PM.js} +0 -0
  239. /package/dist/{chunk-ZWRTVUQ6.js → chunk-KIMO5S45.js} +0 -0
  240. /package/dist/{chunk-S2FX5KJ4.js → chunk-WBLILGAP.js} +0 -0
  241. /package/dist/{core-memory-PCJ3L46L.js → core-memory-RAC6M67J.js} +0 -0
  242. /package/dist/{entity-boost-GHFPE6A2.js → entity-boost-5FIRFRDC.js} +0 -0
  243. /package/dist/{message-queue-client-CHRQYBH5.js → message-queue-client-PTQ2S7D7.js} +0 -0
  244. /package/dist/{wiki-acl-QYRAYYVQ.js → wiki-acl-MSDRCIAI.js} +0 -0
@@ -17,15 +17,10 @@ import {
17
17
  findScopedDuplicate,
18
18
  governMemoryRecord,
19
19
  schedulePostWriteMemoryHygiene
20
- } from "../chunk-S2FX5KJ4.js";
20
+ } from "../chunk-WBLILGAP.js";
21
21
  import {
22
22
  logRestartEvent
23
23
  } from "../chunk-PWMMIGVQ.js";
24
- import {
25
- getClient,
26
- isInitialized
27
- } from "../chunk-5RSYY7BE.js";
28
- import "../chunk-RVSGXGWG.js";
29
24
  import {
30
25
  EMBEDDING_DIM
31
26
  } from "../chunk-FXU7JOXK.js";
@@ -43,8 +38,10 @@ import v82 from "v8";
43
38
  import { createServer as createHttpServer } from "http";
44
39
  import { randomUUID } from "crypto";
45
40
  import { writeFileSync as writeFileSync2, unlinkSync, mkdirSync as mkdirSync2, existsSync as existsSync3, readFileSync as readFileSync2, chmodSync } from "fs";
46
- import { execFileSync as execFileSyncNode, execSync as execSyncNode } from "child_process";
41
+ import { execFileSync as execFileSyncNode, execSync as execSyncNode, execFile as execFileNode } from "child_process";
42
+ import { promisify } from "util";
47
43
  import path3 from "path";
44
+ import { fork } from "child_process";
48
45
 
49
46
  // src/lib/orchestration-metrics.ts
50
47
  import { writeFileSync, readFileSync, existsSync } from "fs";
@@ -391,9 +388,9 @@ var MODEL_FILE = "jina-embeddings-v5-small-q4_k_m.gguf";
391
388
  var IDLE_TIMEOUT_MS = parseInt(process.env.EXE_DAEMON_IDLE_TIMEOUT_MS || "0", 10);
392
389
  var REVIEW_POLL_INTERVAL_MS = 60 * 1e3;
393
390
  var DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
394
- var _context = null;
395
- var _model = null;
396
- var _llama = null;
391
+ var _embedWorker = null;
392
+ var _embedWorkerReady = false;
393
+ var _embedPendingCallbacks = /* @__PURE__ */ new Map();
397
394
  var _shuttingDown = false;
398
395
  var _drainingWrites = false;
399
396
  var _activeWrites = 0;
@@ -420,6 +417,58 @@ function getTmuxSessions() {
420
417
  }
421
418
  return _tmuxSessionCache;
422
419
  }
420
+ var _paneCacheMap = /* @__PURE__ */ new Map();
421
+ var PANE_CACHE_TTL_MS = 1e4;
422
+ var execFileAsync = promisify(execFileNode);
423
+ async function capturePaneAsync(session, lines = 20) {
424
+ const cacheKey = `${session}:${lines}`;
425
+ const cached = _paneCacheMap.get(cacheKey);
426
+ if (cached && Date.now() - cached.ts < PANE_CACHE_TTL_MS) return cached.text;
427
+ try {
428
+ const args = ["capture-pane", "-t", session, "-p", "-S", `-${lines}`];
429
+ const { stdout } = await execFileAsync("tmux", args, { encoding: "utf8", timeout: 3e3 });
430
+ const text = stdout ?? "";
431
+ _paneCacheMap.set(cacheKey, { text, ts: Date.now() });
432
+ return text;
433
+ } catch {
434
+ return "";
435
+ }
436
+ }
437
+ async function batchCapturePanes(sessions, lines = 20) {
438
+ const results = /* @__PURE__ */ new Map();
439
+ const CONCURRENCY = 4;
440
+ for (let i = 0; i < sessions.length; i += CONCURRENCY) {
441
+ const batch = sessions.slice(i, i + CONCURRENCY);
442
+ const settled = await Promise.allSettled(
443
+ batch.map(async (s) => ({ session: s, text: await capturePaneAsync(s, lines) }))
444
+ );
445
+ for (const r of settled) {
446
+ if (r.status === "fulfilled") results.set(r.value.session, r.value.text);
447
+ }
448
+ }
449
+ return results;
450
+ }
451
+ setInterval(() => {
452
+ const now = Date.now();
453
+ for (const [key, entry] of _paneCacheMap) {
454
+ if (now - entry.ts > PANE_CACHE_TTL_MS * 3) _paneCacheMap.delete(key);
455
+ }
456
+ }, 6e4).unref();
457
+ var _configCache = null;
458
+ var _configCacheTs = 0;
459
+ var CONFIG_CACHE_TTL_MS = 6e4;
460
+ var _loadConfigSyncFn = null;
461
+ async function getCachedConfig() {
462
+ const now = Date.now();
463
+ if (_configCache && now - _configCacheTs < CONFIG_CACHE_TTL_MS) return _configCache;
464
+ if (!_loadConfigSyncFn) {
465
+ const mod = await import("./config.js");
466
+ _loadConfigSyncFn = mod.loadConfigSync;
467
+ }
468
+ _configCache = _loadConfigSyncFn();
469
+ _configCacheTs = now;
470
+ return _configCache;
471
+ }
423
472
  var highQueue = [];
424
473
  var lowQueue = [];
425
474
  var _processing = false;
@@ -454,41 +503,151 @@ async function loadModel() {
454
503
  return;
455
504
  }
456
505
  if (existsSync3(OOM_MARKER_PATH)) {
457
- process.stderr.write(
458
- `[exed] Skipping embeddings \u2014 previous OOM detected. Delete ${OOM_MARKER_PATH} to retry.
459
- `
460
- );
506
+ process.stderr.write(`[exed] Skipping embeddings \u2014 OOM marker exists. Delete ${OOM_MARKER_PATH} to retry.
507
+ `);
461
508
  return;
462
509
  }
463
510
  const totalMem = os2.totalmem();
464
- const freeMem = os2.freemem();
465
511
  if (totalMem <= MAX_SMALL_TOTAL_BYTES) {
466
- process.stderr.write(
467
- `[exed] Skipping embeddings \u2014 total RAM ${(totalMem / 1024 ** 3).toFixed(1)} GB <= 8 GB threshold.
468
- `
469
- );
512
+ process.stderr.write(`[exed] Skipping embeddings \u2014 total RAM ${(totalMem / 1024 ** 3).toFixed(1)} GB <= 8 GB.
513
+ `);
470
514
  return;
471
515
  }
472
- if (freeMem < MIN_FREE_MEM_BYTES) {
473
- process.stderr.write(
474
- `[exed] Skipping embeddings \u2014 free RAM ${(freeMem / 1024 ** 3).toFixed(1)} GB < 2 GB minimum.
475
- `
476
- );
516
+ if (os2.freemem() < MIN_FREE_MEM_BYTES) {
517
+ process.stderr.write(`[exed] Skipping embeddings \u2014 free RAM < 2 GB.
518
+ `);
477
519
  return;
478
520
  }
479
- process.stderr.write("[exed] Loading model...\n");
521
+ process.stderr.write("[exed] Spawning embed worker process...\n");
480
522
  try {
481
- const { getLlama } = await import("node-llama-cpp");
482
- _llama = await getLlama();
483
- _model = await _llama.loadModel({ modelPath });
484
- _context = await _model.createEmbeddingContext({ contextSize: 8192 });
485
- process.stderr.write("[exed] Model loaded and ready (contextSize=8192).\n");
523
+ const workerPath = path3.join(path3.dirname(new URL(import.meta.url).pathname), "embed-worker.js");
524
+ if (!existsSync3(workerPath)) {
525
+ process.stderr.write(`[exed] Embed worker not found at ${workerPath} \u2014 running without embeddings.
526
+ `);
527
+ return;
528
+ }
529
+ _embedWorker = fork(workerPath, [], {
530
+ stdio: ["ignore", "ignore", "inherit", "ipc"],
531
+ env: { ...process.env, EXE_EMBED_MODEL: MODEL_FILE }
532
+ });
533
+ _embedWorker.on("message", (msg) => {
534
+ if (msg.type === "ready") {
535
+ if (msg.dim && msg.dim > 0) {
536
+ _embedWorkerReady = true;
537
+ process.stderr.write(`[exed] Embed worker ready (dim=${msg.dim}, pid=${_embedWorker?.pid}).
538
+ `);
539
+ } else {
540
+ process.stderr.write(`[exed] Embed worker failed to load: ${msg.error ?? "unknown"}
541
+ `);
542
+ }
543
+ return;
544
+ }
545
+ if (msg.id) {
546
+ const cb = _embedPendingCallbacks.get(msg.id);
547
+ if (cb) {
548
+ _embedPendingCallbacks.delete(msg.id);
549
+ if (msg.error) cb.reject(new Error(msg.error));
550
+ else if (msg.vectors) cb.resolve(msg.vectors);
551
+ else cb.reject(new Error("Invalid worker response"));
552
+ }
553
+ }
554
+ });
555
+ _embedWorker.on("error", (err) => {
556
+ process.stderr.write(`[exed] Embed worker error: ${err.message}
557
+ `);
558
+ _embedWorkerReady = false;
559
+ for (const [id, cb] of _embedPendingCallbacks) {
560
+ cb.reject(new Error("Worker crashed"));
561
+ _embedPendingCallbacks.delete(id);
562
+ }
563
+ });
564
+ _embedWorker.on("exit", (code) => {
565
+ process.stderr.write(`[exed] Embed worker exited (code=${code}).
566
+ `);
567
+ _embedWorkerReady = false;
568
+ _embedWorker = null;
569
+ _embedRespawnCount++;
570
+ if (_embedRespawnCount <= 3) {
571
+ const delay = _embedRespawnCount * 1e4;
572
+ process.stderr.write(`[exed] Respawning embed worker in ${delay / 1e3}s (attempt ${_embedRespawnCount}/3).
573
+ `);
574
+ setTimeout(() => loadModel(), delay);
575
+ } else {
576
+ process.stderr.write(`[exed] Embed worker failed 3 times \u2014 running without embeddings.
577
+ `);
578
+ }
579
+ });
486
580
  } catch (err) {
487
- process.stderr.write(`[exed] Model load failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
581
+ process.stderr.write(`[exed] Embed worker spawn failed: ${err instanceof Error ? err.message : String(err)}
488
582
  `);
489
- process.stderr.write("[exed] Running without embeddings \u2014 memories will have null vectors.\n");
490
583
  }
491
584
  }
585
+ var _embedRespawnCount = 0;
586
+ setInterval(() => {
587
+ if (!_embedWorker || !_embedWorkerReady) return;
588
+ let onMsg = null;
589
+ const healthTimeout = setTimeout(() => {
590
+ process.stderr.write("[exed] Embed worker UNRESPONSIVE (10s health timeout) \u2014 killing.\n");
591
+ if (onMsg) try {
592
+ _embedWorker?.removeListener("message", onMsg);
593
+ } catch {
594
+ }
595
+ try {
596
+ _embedWorker?.kill("SIGKILL");
597
+ } catch {
598
+ }
599
+ }, 1e4);
600
+ try {
601
+ _embedWorker.send({ type: "health" });
602
+ onMsg = (msg) => {
603
+ if (msg.type === "health") {
604
+ clearTimeout(healthTimeout);
605
+ if (onMsg) try {
606
+ _embedWorker?.removeListener("message", onMsg);
607
+ } catch {
608
+ }
609
+ if (msg.rss && msg.rss > 4096) {
610
+ process.stderr.write(`[exed] Embed worker RSS ${msg.rss}MB > 4GB \u2014 killing to prevent OOM.
611
+ `);
612
+ try {
613
+ _embedWorker?.kill("SIGKILL");
614
+ } catch {
615
+ }
616
+ }
617
+ }
618
+ };
619
+ _embedWorker.on("message", onMsg);
620
+ } catch {
621
+ clearTimeout(healthTimeout);
622
+ }
623
+ }, 6e4);
624
+ function embedTexts(texts) {
625
+ return new Promise((resolve, reject) => {
626
+ if (!_embedWorker || !_embedWorkerReady) {
627
+ reject(new Error("Embed worker not available"));
628
+ return;
629
+ }
630
+ if (texts.length > 100) {
631
+ texts = texts.slice(0, 100);
632
+ }
633
+ const id = randomUUID();
634
+ const timeout = setTimeout(() => {
635
+ _embedPendingCallbacks.delete(id);
636
+ reject(new Error("Embed timeout (30s)"));
637
+ }, 3e4);
638
+ _embedPendingCallbacks.set(id, {
639
+ resolve: (v) => {
640
+ clearTimeout(timeout);
641
+ resolve(v);
642
+ },
643
+ reject: (e) => {
644
+ clearTimeout(timeout);
645
+ reject(e);
646
+ }
647
+ });
648
+ _embedWorker.send({ id, type: "embed", texts });
649
+ });
650
+ }
492
651
  var RSS_BACKPRESSURE_HIGH = 0.8;
493
652
  var RSS_BACKPRESSURE_LOW = 0.7;
494
653
  var _lowQueuePaused = false;
@@ -519,23 +678,14 @@ async function processQueue() {
519
678
  if (!entry) break;
520
679
  if (entry.socket.destroyed) continue;
521
680
  try {
522
- const vectors = [];
523
- if (!_context) {
681
+ if (!_embedWorkerReady) {
524
682
  sendResponse(entry.socket, {
525
683
  id: entry.request.id,
526
684
  error: "Model not loaded yet \u2014 embeddings unavailable"
527
685
  });
528
686
  continue;
529
687
  }
530
- for (const text of entry.request.texts) {
531
- const embedding = await _context.getEmbeddingFor(text);
532
- const vector = Array.from(embedding.vector);
533
- embedding.vector = null;
534
- if (vector.length !== EMBEDDING_DIM) {
535
- throw new Error(`Dimension mismatch: got ${vector.length}, expected ${EMBEDDING_DIM}`);
536
- }
537
- vectors.push(vector);
538
- }
688
+ const vectors = await embedTexts(entry.request.texts);
539
689
  _requestsServed++;
540
690
  sendResponse(entry.socket, { id: entry.request.id, vectors });
541
691
  } catch (err) {
@@ -544,7 +694,6 @@ async function processQueue() {
544
694
  error: err instanceof Error ? err.message : String(err)
545
695
  });
546
696
  }
547
- global.gc?.();
548
697
  await new Promise((r) => setTimeout(r, 0));
549
698
  }
550
699
  } finally {
@@ -553,11 +702,13 @@ async function processQueue() {
553
702
  }
554
703
  }
555
704
  function sendResponse(socket, data) {
556
- if (!socket.destroyed) {
557
- try {
558
- socket.write(JSON.stringify(data) + "\n");
559
- } catch {
705
+ if (socket.destroyed) return;
706
+ try {
707
+ const ok = socket.write(JSON.stringify(data) + "\n");
708
+ if (!ok) {
709
+ socket.destroy(new Error("Write backpressure \u2014 client too slow"));
560
710
  }
711
+ } catch {
561
712
  }
562
713
  }
563
714
  function resetIdleTimer() {
@@ -587,7 +738,7 @@ async function shutdown() {
587
738
  logRestartEvent("daemon_shutdown_start", { uptime_s: Math.floor((Date.now() - _startedAt2) / 1e3), active_writes: _activeWrites, active_connections: _activeConnections });
588
739
  resetIdleTimer();
589
740
  flushToDisk();
590
- if (process.platform === "darwin" && _llama && isRssOverThreshold(RSS_BACKPRESSURE_HIGH)) {
741
+ if (process.platform === "darwin" && _embedWorker && isRssOverThreshold(RSS_BACKPRESSURE_HIGH)) {
591
742
  process.stderr.write(
592
743
  `[exed] RSS CRITICAL during shutdown \u2014 fast Metal exit (skipping WAL/backup/shards).
593
744
  `
@@ -629,8 +780,8 @@ async function shutdown() {
629
780
  `);
630
781
  }
631
782
  try {
632
- const { getClient: getClient2 } = await import("./database.js");
633
- const client = getClient2();
783
+ const { getClient } = await import("./database.js");
784
+ const client = getClient();
634
785
  await client.execute("PRAGMA wal_checkpoint(TRUNCATE)");
635
786
  process.stderr.write("[exed] WAL checkpoint complete \u2014 all writes flushed to DB.\n");
636
787
  } catch (e) {
@@ -638,7 +789,7 @@ async function shutdown() {
638
789
  `);
639
790
  }
640
791
  try {
641
- const { stopProjectionWorker } = await import("../projection-worker-FK5YOEIL.js");
792
+ const { stopProjectionWorker } = await import("../projection-worker-EBUYNMU2.js");
642
793
  stopProjectionWorker();
643
794
  } catch {
644
795
  }
@@ -647,49 +798,13 @@ async function shutdown() {
647
798
  disposeShards();
648
799
  } catch {
649
800
  }
650
- if (process.platform === "darwin" && _llama) {
651
- try {
652
- unlinkSync(SOCKET_PATH);
653
- } catch (e) {
654
- process.stderr.write(`[exed] socket cleanup: ${e.message}
655
- `);
656
- }
657
- try {
658
- unlinkSync(PID_PATH);
659
- } catch (e) {
660
- process.stderr.write(`[exed] pid cleanup: ${e.message}
661
- `);
662
- }
663
- process.stderr.write("[exed] Shutdown complete (darwin fast native exit).\n");
664
- process.kill(process.pid, "SIGKILL");
665
- process.exit(0);
666
- }
667
- if (_context) {
668
- try {
669
- await _context.dispose();
670
- } catch (e) {
671
- process.stderr.write(`[exed] context dispose: ${e.message}
672
- `);
673
- }
674
- _context = null;
675
- }
676
- if (_model) {
677
- try {
678
- await _model.dispose();
679
- } catch (e) {
680
- process.stderr.write(`[exed] model dispose: ${e.message}
681
- `);
682
- }
683
- _model = null;
684
- }
685
- if (_llama) {
801
+ if (_embedWorker) {
686
802
  try {
687
- await _llama.dispose();
688
- } catch (e) {
689
- process.stderr.write(`[exed] llama dispose: ${e.message}
690
- `);
803
+ _embedWorker.kill("SIGTERM");
804
+ } catch {
691
805
  }
692
- _llama = null;
806
+ _embedWorker = null;
807
+ _embedWorkerReady = false;
693
808
  }
694
809
  try {
695
810
  unlinkSync(SOCKET_PATH);
@@ -707,16 +822,15 @@ async function shutdown() {
707
822
  process.exit(0);
708
823
  }
709
824
  async function handleHealthCheck(socket, requestId) {
710
- const healthy = _context !== null && _model !== null;
825
+ const healthy = _embedWorkerReady;
711
826
  let testOk = false;
712
- if (healthy) {
827
+ if (healthy && _embedWorker) {
713
828
  try {
714
- const testEmbed = await _context.getEmbeddingFor("health check");
715
- testOk = Array.from(testEmbed.vector).length === EMBEDDING_DIM;
829
+ const vectors = await embedTexts(["health check"]);
830
+ testOk = vectors.length > 0 && vectors[0].length === EMBEDDING_DIM;
716
831
  } catch (e) {
717
832
  process.stderr.write(`[exed] health check embed failed: ${e instanceof Error ? e.message : String(e)}
718
833
  `);
719
- testOk = false;
720
834
  }
721
835
  }
722
836
  const dbConnected = _storeInitialized;
@@ -761,8 +875,8 @@ async function handleDbExecute(socket, requestId, sql, args) {
761
875
  sendResponse(socket, { id: requestId, error: "DB not initialized" });
762
876
  return;
763
877
  }
764
- const { getClient: getClient2 } = await import("./database.js");
765
- const client = getClient2();
878
+ const { getClient } = await import("./database.js");
879
+ const client = getClient();
766
880
  const deserializedArgs = deserializeArgs(args);
767
881
  const result = await client.execute({ sql, args: deserializedArgs });
768
882
  _dbRequestsServed++;
@@ -787,8 +901,8 @@ async function handleDbBatch(socket, requestId, statements, mode) {
787
901
  sendResponse(socket, { id: requestId, error: "DB not initialized" });
788
902
  return;
789
903
  }
790
- const { getClient: getClient2 } = await import("./database.js");
791
- const client = getClient2();
904
+ const { getClient } = await import("./database.js");
905
+ const client = getClient();
792
906
  const stmts = statements.map((s) => ({
793
907
  sql: s.sql,
794
908
  args: deserializeArgs(s.args)
@@ -911,22 +1025,20 @@ async function _writeMemoryRecordInner(entry) {
911
1025
  });
912
1026
  if (duplicate) return duplicate;
913
1027
  let vectorBlob = null;
914
- if (_context && !governed.skipEmbedding) {
1028
+ if (_embedWorkerReady && !governed.skipEmbedding) {
915
1029
  try {
916
- const embedding = await _context.getEmbeddingFor(record.raw_text);
917
- const vector = Array.from(embedding.vector);
918
- embedding.vector = null;
919
- if (vector.length === EMBEDDING_DIM) {
1030
+ const vectors = await embedTexts([record.raw_text]);
1031
+ if (vectors.length > 0 && vectors[0].length === EMBEDDING_DIM) {
920
1032
  const { vectorToBlob } = await import("./store.js");
921
- vectorBlob = vectorToBlob(vector);
1033
+ vectorBlob = vectorToBlob(vectors[0]);
922
1034
  }
923
1035
  } catch (e) {
924
1036
  process.stderr.write(`[exed] embed failed for ingest (NULL vector, backfill later): ${e instanceof Error ? e.message : String(e)}
925
1037
  `);
926
1038
  }
927
1039
  }
928
- const { getClient: getClient2 } = await import("./database.js");
929
- const client = getClient2();
1040
+ const { getClient } = await import("./database.js");
1041
+ const client = getClient();
930
1042
  const hasVector = vectorBlob !== null;
931
1043
  await client.execute({
932
1044
  sql: hasVector ? `INSERT OR IGNORE INTO memories
@@ -984,7 +1096,7 @@ async function _writeMemoryRecordInner(entry) {
984
1096
  const vRow = await client.execute({ sql: "SELECT version FROM memories WHERE id = ?", args: [id] });
985
1097
  const version = Number(vRow.rows[0]?.version) || 0;
986
1098
  try {
987
- const { insertOntologyForMemory } = await import("../agentic-ontology-GKAKYNPE.js");
1099
+ const { insertOntologyForMemory } = await import("../agentic-ontology-S54AFODT.js");
988
1100
  await insertOntologyForMemory({
989
1101
  id,
990
1102
  agent_id: record.agent_id,
@@ -1058,7 +1170,7 @@ function startMessageQueueDrain() {
1058
1170
  fired("message_queue_drain");
1059
1171
  if (!await ensureStoreForPolling()) return;
1060
1172
  try {
1061
- const { claimMessageQueue, ackMessageQueue } = await import("../message-queue-client-CHRQYBH5.js");
1173
+ const { claimMessageQueue, ackMessageQueue } = await import("../message-queue-client-PTQ2S7D7.js");
1062
1174
  const lease = claimMessageQueue();
1063
1175
  if (!lease || lease.entries.length === 0) return;
1064
1176
  acted("message_queue_drain");
@@ -1339,8 +1451,10 @@ async function startMcpHttpServer() {
1339
1451
  if (sid !== keepSid && existingOwner === ownerKey) {
1340
1452
  const age = sessionLastSeen.has(sid) ? Math.round((Date.now() - sessionLastSeen.get(sid)) / 1e3) : -1;
1341
1453
  closeMcpSession2(sid, "duplicate_session_eviction");
1342
- process.stderr.write(`[exed] MCP duplicate session eviction: closed ${sid.slice(0, 8)} for ${ownerKey} (age=${age}s, replaced by ${keepSid?.slice(0, 8) ?? "none"})
1454
+ if (age > 5) {
1455
+ process.stderr.write(`[exed] MCP duplicate session eviction: closed ${sid.slice(0, 8)} for ${ownerKey} (age=${age}s, replaced by ${keepSid?.slice(0, 8) ?? "none"})
1343
1456
  `);
1457
+ }
1344
1458
  }
1345
1459
  }
1346
1460
  }, sweepStaleMcpSessions2 = function() {
@@ -1410,14 +1524,15 @@ async function startMcpHttpServer() {
1410
1524
  );
1411
1525
  }
1412
1526
  Promise.all([
1413
- import("../tool-capability-index-6JJN6ZRC.js"),
1527
+ import("../tool-capability-index-42VVN5BS.js"),
1414
1528
  import("../tool-gates-3IC7DK4R.js")
1415
1529
  ]).then(async ([{ getToolCapabilityIndex }, { TOOL_CATEGORIES }]) => {
1416
1530
  const gateState = getCachedLicenseGate();
1417
1531
  const index = getToolCapabilityIndex();
1418
1532
  await index.buildIndex(
1419
1533
  (fakeServer) => registerAllTools(fakeServer, gateState.license, gateState.hasKey),
1420
- TOOL_CATEGORIES
1534
+ TOOL_CATEGORIES,
1535
+ _embedWorkerReady ? embedTexts : void 0
1421
1536
  );
1422
1537
  }).catch((err) => {
1423
1538
  process.stderr.write(`[exed] Tool capability index failed: ${err.message}
@@ -1507,17 +1622,21 @@ async function startMcpHttpServer() {
1507
1622
  }
1508
1623
  try {
1509
1624
  const { getClient: getDbClient } = await import("./database.js");
1625
+ const { extractKeywords, keywordsToString } = await import("../keyword-extractor-UJHFWVZE.js");
1510
1626
  const client = getDbClient();
1627
+ const webhookText = `[${result.source}] ${result.eventType}: ${result.text}`;
1628
+ const webhookKeywords = keywordsToString(extractKeywords(webhookText));
1511
1629
  await client.execute({
1512
- sql: `INSERT INTO memories (id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, session_scope)
1513
- VALUES (?, 'webhook', 'system', ?, ?, ?, ?, 0, ?, 0, NULL)`,
1630
+ sql: `INSERT INTO memories (id, agent_id, agent_role, session_id, timestamp, tool_name, project_name, has_error, raw_text, version, session_scope, keywords)
1631
+ VALUES (?, 'webhook', 'system', ?, ?, ?, ?, 0, ?, 0, NULL, ?)`,
1514
1632
  args: [
1515
1633
  randomUUID(),
1516
1634
  `webhook-${serviceName}`,
1517
1635
  (/* @__PURE__ */ new Date()).toISOString(),
1518
1636
  `webhook:${result.source}`,
1519
1637
  result.source,
1520
- `[${result.source}] ${result.eventType}: ${result.text}`
1638
+ webhookText,
1639
+ webhookKeywords
1521
1640
  ]
1522
1641
  });
1523
1642
  } catch (e) {
@@ -1676,8 +1795,8 @@ async function startMcpHttpServer() {
1676
1795
  sessionLastSeen.set(sessionId, Date.now());
1677
1796
  } else if (!sessionId && req.method === "POST" && isInitializeRequest(parsedBody)) {
1678
1797
  try {
1679
- const { initDatabase, isInitialized: isInitialized2 } = await import("./database.js");
1680
- if (!isInitialized2()) {
1798
+ const { initDatabase, isInitialized } = await import("./database.js");
1799
+ if (!isInitialized()) {
1681
1800
  const { loadConfig } = await import("./config.js");
1682
1801
  const { getMasterKey } = await import("./keychain.js");
1683
1802
  const config = await loadConfig();
@@ -1749,7 +1868,7 @@ async function startMcpHttpServer() {
1749
1868
  };
1750
1869
  const sessionMcp = new McpServer({ name: "exe-os", version: "1.3.0" });
1751
1870
  const gateState = getCachedLicenseGate();
1752
- const { wrapServerWithTelemetry } = await import("../tool-telemetry-72PVO5HV.js");
1871
+ const { wrapServerWithTelemetry } = await import("../tool-telemetry-GZ5E2AUL.js");
1753
1872
  const wrappedMcp = wrapServerWithTelemetry(sessionMcp);
1754
1873
  registerAllTools(wrappedMcp, gateState.license, gateState.hasKey);
1755
1874
  await sessionMcp.connect(transport);
@@ -1768,8 +1887,8 @@ async function startMcpHttpServer() {
1768
1887
  activeSessions: transports.size
1769
1888
  });
1770
1889
  try {
1771
- const { initDatabase, isInitialized: isInitialized2 } = await import("./database.js");
1772
- if (!isInitialized2()) {
1890
+ const { initDatabase, isInitialized } = await import("./database.js");
1891
+ if (!isInitialized()) {
1773
1892
  const { loadConfig } = await import("./config.js");
1774
1893
  const { getMasterKey } = await import("./keychain.js");
1775
1894
  const config = await loadConfig();
@@ -1825,7 +1944,7 @@ async function startMcpHttpServer() {
1825
1944
  const sessionMcp = new McpServer({ name: "exe-os", version: "1.3.0" });
1826
1945
  sessionServers.set(recoveredSessionId, sessionMcp);
1827
1946
  const gateState = getCachedLicenseGate();
1828
- const { wrapServerWithTelemetry } = await import("../tool-telemetry-72PVO5HV.js");
1947
+ const { wrapServerWithTelemetry } = await import("../tool-telemetry-GZ5E2AUL.js");
1829
1948
  const wrappedMcp = wrapServerWithTelemetry(sessionMcp);
1830
1949
  registerAllTools(wrappedMcp, gateState.license, gateState.hasKey);
1831
1950
  await sessionMcp.connect(transport);
@@ -2001,8 +2120,8 @@ var _storeInitialized = false;
2001
2120
  async function ensureStoreForPolling() {
2002
2121
  if (_storeInitialized) return true;
2003
2122
  try {
2004
- const { initDatabase, isInitialized: isInitialized2 } = await import("./database.js");
2005
- if (!isInitialized2()) {
2123
+ const { initDatabase, isInitialized } = await import("./database.js");
2124
+ if (!isInitialized()) {
2006
2125
  const { loadConfig } = await import("./config.js");
2007
2126
  const { getMasterKey } = await import("./keychain.js");
2008
2127
  const config = await loadConfig();
@@ -2021,7 +2140,7 @@ async function ensureStoreForPolling() {
2021
2140
  }
2022
2141
  }
2023
2142
  async function startReviewPolling() {
2024
- const polling = await import("../review-polling-ZLNDUKL4.js");
2143
+ const polling = await import("../review-polling-2N7KQFZZ.js");
2025
2144
  const state = {
2026
2145
  lastIntercomSent: /* @__PURE__ */ new Map(),
2027
2146
  lastNudgeSent: /* @__PURE__ */ new Map(),
@@ -2031,8 +2150,8 @@ async function startReviewPolling() {
2031
2150
  fired("review_polling");
2032
2151
  if (!await ensureStoreForPolling()) return;
2033
2152
  try {
2034
- const { getClient: getClient2 } = await import("./database.js");
2035
- const deps = polling.createRealDeps(getClient2);
2153
+ const { getClient } = await import("./database.js");
2154
+ const deps = polling.createRealDeps(getClient);
2036
2155
  const sent = await polling.pollPendingReviews(deps, state);
2037
2156
  if (sent.length > 0) acted("review_polling");
2038
2157
  for (const s of sent) {
@@ -2055,9 +2174,9 @@ function startSessionTTL() {
2055
2174
  fired("session_ttl");
2056
2175
  if (!await ensureStoreForPolling()) return;
2057
2176
  try {
2058
- const { getClient: getClient2 } = await import("./database.js");
2059
- const { checkSessionTTL, createSessionTTLRealDeps } = await import("../daemon-orchestration-VO5XQIJL.js");
2060
- const deps = createSessionTTLRealDeps(getClient2);
2177
+ const { getClient } = await import("./database.js");
2178
+ const { checkSessionTTL, createSessionTTLRealDeps } = await import("../daemon-orchestration-CHV6MB42.js");
2179
+ const deps = createSessionTTLRealDeps(getClient);
2061
2180
  const killed = await checkSessionTTL(deps);
2062
2181
  if (killed.length > 0) acted("session_ttl");
2063
2182
  } catch (err) {
@@ -2070,105 +2189,7 @@ function startSessionTTL() {
2070
2189
  process.stderr.write(`[exed] Session TTL started (every ${SESSION_TTL_INTERVAL_MS / 6e4}m)
2071
2190
  `);
2072
2191
  }
2073
- var COO_RESTART_INTERVAL_MS = 6e4;
2074
- var _cooLastSeen = 0;
2075
2192
  var COO_RESTART_COOLDOWN_MS = 5 * 6e4;
2076
- function startCooAutoRestart() {
2077
- const tick = async () => {
2078
- fired("coo_restart");
2079
- try {
2080
- const { listTmuxSessions } = await import("./tmux-status.js");
2081
- const { isExeSession } = await import("./tmux-routing.js");
2082
- const sessions = listTmuxSessions();
2083
- const cooSessions = sessions.filter((s) => isExeSession(s));
2084
- if (cooSessions.length > 0) {
2085
- _cooLastSeen = Date.now();
2086
- try {
2087
- const { getClient: getClient2 } = await import("./database.js");
2088
- const { pollCoordinatorContextRestart, createCoordinatorRestartRealDeps } = await import("../daemon-orchestration-VO5XQIJL.js");
2089
- const deps = createCoordinatorRestartRealDeps(getClient2);
2090
- const restarted = await pollCoordinatorContextRestart(deps, process.cwd());
2091
- if (restarted.length > 0) {
2092
- acted("coordinator_context_restart");
2093
- process.stderr.write(`[exed] Coordinator context restart: ${restarted.join(", ")}
2094
- `);
2095
- }
2096
- } catch (err) {
2097
- process.stderr.write(`[exed] Coordinator context restart error: ${err instanceof Error ? err.message : String(err)}
2098
- `);
2099
- }
2100
- return;
2101
- }
2102
- if (_cooLastSeen === 0) return;
2103
- if (Date.now() - _cooLastSeen < COO_RESTART_COOLDOWN_MS) return;
2104
- process.stderr.write("[exed] COO session died \u2014 auto-restarting\n");
2105
- acted("coo_restart");
2106
- const { pushNotifyAsync: pushNotifyAsync2 } = await import("../push-notifications-VJZFCUFZ.js");
2107
- const { listSessions } = await import("./session-registry.js");
2108
- const allSessions = listSessions();
2109
- const lastCoo = allSessions.filter((s) => isExeSession(s.windowName)).sort((a, b) => (b.registeredAt ?? "").localeCompare(a.registeredAt ?? ""))[0];
2110
- const projectDir = lastCoo?.projectDir ?? process.cwd();
2111
- const { execSync: execSync2 } = await import("child_process");
2112
- const { getCoordinatorName } = await import("./employees.js");
2113
- const cooSession = lastCoo?.windowName ?? "exe1";
2114
- const coordinatorName = getCoordinatorName();
2115
- try {
2116
- const npmRoot = execSync2("npm root -g", { encoding: "utf-8", timeout: 5e3 }).trim();
2117
- const launchScript = `${npmRoot}/@askexenow/exe-os/dist/bin/exe-launch-agent.js`;
2118
- execSync2(
2119
- `tmux new-session -d -s "${cooSession}" -c "${projectDir}" "node ${launchScript} ${coordinatorName}" 2>/dev/null`,
2120
- { timeout: 1e4 }
2121
- );
2122
- setTimeout(() => {
2123
- try {
2124
- execSync2(
2125
- `tmux send-keys -t "${cooSession}" '/exe' Enter 2>/dev/null`,
2126
- { timeout: 5e3 }
2127
- );
2128
- process.stderr.write(`[exed] Sent /exe boot to COO session "${cooSession}"
2129
- `);
2130
- } catch {
2131
- }
2132
- }, 3e3);
2133
- _cooLastSeen = Date.now();
2134
- process.stderr.write(`[exed] COO session "${cooSession}" relaunched in ${projectDir} with identity
2135
- `);
2136
- pushNotifyAsync2({
2137
- event: "context_full",
2138
- title: "COO session recovered",
2139
- body: `Coordinator relaunched as ${coordinatorName} in ${projectDir}`,
2140
- priority: "normal"
2141
- });
2142
- logDaemonHealth({
2143
- event: "boot",
2144
- pid: process.pid,
2145
- message: `COO auto-restart: relaunched ${cooSession} with identity`,
2146
- data: { session: cooSession, projectDir, coordinator: coordinatorName }
2147
- });
2148
- } catch (err) {
2149
- process.stderr.write(`[exed] COO restart failed: ${err instanceof Error ? err.message : String(err)}
2150
- `);
2151
- }
2152
- } catch (err) {
2153
- process.stderr.write(`[exed] COO restart check error: ${err instanceof Error ? err.message : String(err)}
2154
- `);
2155
- }
2156
- };
2157
- const timer = setInterval(() => void tick(), COO_RESTART_INTERVAL_MS);
2158
- timer.unref();
2159
- void (async () => {
2160
- try {
2161
- const { listTmuxSessions } = await import("./tmux-status.js");
2162
- const { isExeSession } = await import("./tmux-routing.js");
2163
- if (listTmuxSessions().some((s) => isExeSession(s))) {
2164
- _cooLastSeen = Date.now();
2165
- }
2166
- } catch {
2167
- }
2168
- })();
2169
- process.stderr.write(`[exed] COO auto-restart started (every ${COO_RESTART_INTERVAL_MS / 1e3}s)
2170
- `);
2171
- }
2172
2193
  var IDLE_KILL_INTERVAL_MS = 30 * 1e3;
2173
2194
  var _idleTickCounts = /* @__PURE__ */ new Map();
2174
2195
  function startIdleKill() {
@@ -2183,7 +2204,7 @@ function startIdleKill() {
2183
2204
  const agentId = file.replace(".terminate", "");
2184
2205
  const signalPath = path3.join(signalDir, file);
2185
2206
  try {
2186
- const sessions = execSyncNode("tmux list-sessions -F '#{session_name}' 2>/dev/null", { encoding: "utf8", timeout: 2e3 }).trim().split("\n");
2207
+ const sessions = getTmuxSessions();
2187
2208
  const match = sessions.find((s) => s.startsWith(`${agentId}-`) || s.startsWith(`${agentId}2-`) || s.startsWith(`${agentId}3-`));
2188
2209
  if (match) {
2189
2210
  execSyncNode(`tmux kill-session -t ${match} 2>/dev/null`, { timeout: 2e3 });
@@ -2200,12 +2221,12 @@ function startIdleKill() {
2200
2221
  } catch {
2201
2222
  }
2202
2223
  try {
2203
- const { loadConfigSync } = await import("./config.js");
2204
- const cfg = loadConfigSync();
2205
- const { getClient: getClient2 } = await import("./database.js");
2206
- const { pollIdleKill, createIdleKillRealDeps } = await import("../daemon-orchestration-VO5XQIJL.js");
2224
+ const cfg = await getCachedConfig();
2225
+ if (!cfg) return;
2226
+ const { getClient } = await import("./database.js");
2227
+ const { pollIdleKill, createIdleKillRealDeps } = await import("../daemon-orchestration-CHV6MB42.js");
2207
2228
  const deps = createIdleKillRealDeps(
2208
- getClient2,
2229
+ getClient,
2209
2230
  cfg.sessionLifecycle.idleKillIntercomAckWindowMs
2210
2231
  );
2211
2232
  const killed = await pollIdleKill(deps, _idleTickCounts, {
@@ -2239,8 +2260,8 @@ function startConsolidation() {
2239
2260
  if (!config.consolidationEnabled) return;
2240
2261
  if (!process.env.ANTHROPIC_API_KEY) return;
2241
2262
  if (!await ensureStoreForPolling()) return;
2242
- const { getClient: getClient2 } = await import("./database.js");
2243
- const client = getClient2();
2263
+ const { getClient } = await import("./database.js");
2264
+ const client = getClient();
2244
2265
  const { countUnconsolidated, isUserIdle, runConsolidation } = await import("./consolidation.js");
2245
2266
  const count = await countUnconsolidated(client);
2246
2267
  if (count < 20) {
@@ -2307,6 +2328,7 @@ var CLOUD_SYNC_AUTO_RECOVERY_MS = 30 * 6e4;
2307
2328
  function startCloudSyncTimer() {
2308
2329
  const tick = async () => {
2309
2330
  fired("cloud_sync");
2331
+ if (_shuttingDown) return;
2310
2332
  if (_cloudSyncPaused) {
2311
2333
  if (Date.now() - _cloudSyncPausedAt > CLOUD_SYNC_AUTO_RECOVERY_MS) {
2312
2334
  process.stderr.write(`[exed] Cloud sync auto-recovery: retrying after ${Math.round(CLOUD_SYNC_AUTO_RECOVERY_MS / 6e4)}m pause
@@ -2418,7 +2440,7 @@ function startSkillRefinement() {
2418
2440
  fired("skill_refinement");
2419
2441
  try {
2420
2442
  if (!await ensureStoreForPolling()) return;
2421
- const { runSkillRefinement } = await import("../skill-refinement-L7PGKCYO.js");
2443
+ const { runSkillRefinement } = await import("../skill-refinement-WXBTANDQ.js");
2422
2444
  const result = await runSkillRefinement();
2423
2445
  if (result.reviewed > 0) {
2424
2446
  acted("skill_refinement");
@@ -2446,8 +2468,8 @@ function startGraphExtraction() {
2446
2468
  if (config.graphRagEnabled === false) return;
2447
2469
  if (!process.env.ANTHROPIC_API_KEY) return;
2448
2470
  if (!await ensureStoreForPolling()) return;
2449
- const { getClient: getClient2 } = await import("./database.js");
2450
- const client = getClient2();
2471
+ const { getClient } = await import("./database.js");
2472
+ const client = getClient();
2451
2473
  const { extractBatch } = await import("../graph-rag-G3EG5Q6L.js");
2452
2474
  const result = await extractBatch(client, 50, config.selfQueryModel);
2453
2475
  if (result.processed > 0) {
@@ -2474,8 +2496,8 @@ async function writeAgentStats() {
2474
2496
  if (!await ensureStoreForPolling()) return;
2475
2497
  try {
2476
2498
  acted("agent_stats");
2477
- const { getClient: getClient2 } = await import("./database.js");
2478
- const client = getClient2();
2499
+ const { getClient } = await import("./database.js");
2500
+ const client = getClient();
2479
2501
  const result = await client.execute({
2480
2502
  sql: `SELECT agent_id,
2481
2503
  COUNT(*) as total,
@@ -2547,8 +2569,8 @@ function startConfidenceDecay() {
2547
2569
  fired("confidence_decay");
2548
2570
  if (!await ensureStoreForPolling()) return;
2549
2571
  try {
2550
- const { getClient: getClient2 } = await import("./database.js");
2551
- const client = getClient2();
2572
+ const { getClient } = await import("./database.js");
2573
+ const client = getClient();
2552
2574
  const result = await client.execute({
2553
2575
  sql: `UPDATE memories
2554
2576
  SET confidence = MAX(0.3, COALESCE(confidence, 0.7) - 0.01)
@@ -2579,8 +2601,8 @@ function startReflectionSweep() {
2579
2601
  fired("reflection_sweep");
2580
2602
  if (!await ensureStoreForPolling()) return;
2581
2603
  try {
2582
- const { getClient: getClient2 } = await import("./database.js");
2583
- const client = getClient2();
2604
+ const { getClient } = await import("./database.js");
2605
+ const client = getClient();
2584
2606
  const agents = await client.execute(
2585
2607
  "SELECT DISTINCT agent_id FROM memory_cards WHERE active = 1 LIMIT 50"
2586
2608
  );
@@ -2588,7 +2610,7 @@ function startReflectionSweep() {
2588
2610
  for (const row of agents.rows) {
2589
2611
  const agentId = row.agent_id;
2590
2612
  try {
2591
- const { runReflection } = await import("../memory-reflection-HTDAUUE5.js");
2613
+ const { runReflection } = await import("../memory-reflection-MTPRQNI6.js");
2592
2614
  const result = await runReflection(agentId);
2593
2615
  totalInsights += result.contradictions + result.patterns + result.summaries;
2594
2616
  } catch (e) {
@@ -2617,7 +2639,7 @@ function startReflectionSweep() {
2617
2639
  if (memories > 100 && cards < memories * 0.1) {
2618
2640
  process.stderr.write(`[exed] Memory cards sparse (${cards} cards / ${memories} memories) \u2014 running backfill...
2619
2641
  `);
2620
- const { insertMemoryCardsForBatch } = await import("../memory-cards-V3DKSRWL.js");
2642
+ const { insertMemoryCardsForBatch } = await import("../memory-cards-SFDKDIAW.js");
2621
2643
  const batchSize = 500;
2622
2644
  let offset = 0;
2623
2645
  let totalCards = 0;
@@ -2639,6 +2661,7 @@ function startReflectionSweep() {
2639
2661
  }
2640
2662
  offset += batch.rows.length;
2641
2663
  if (offset > 5e4) break;
2664
+ if (offset % (batchSize * 5) === 0) await new Promise((r) => setTimeout(r, 100));
2642
2665
  }
2643
2666
  if (totalCards > 0) {
2644
2667
  process.stderr.write(`[exed] Card backfill: ${totalCards} cards from ${offset} memories
@@ -2688,9 +2711,9 @@ function startSoftDeletePurge() {
2688
2711
  fired("soft_delete_purge");
2689
2712
  if (!await ensureStoreForPolling()) return;
2690
2713
  try {
2691
- const { getClient: getClient2 } = await import("./database.js");
2714
+ const { getClient } = await import("./database.js");
2692
2715
  const { SOFT_DELETE_RETENTION_DAYS } = await import("./database.js");
2693
- const client = getClient2();
2716
+ const client = getClient();
2694
2717
  const cutoffDate = new Date(Date.now() - SOFT_DELETE_RETENTION_DAYS * 24 * 60 * 60 * 1e3).toISOString();
2695
2718
  const result = await client.execute({
2696
2719
  sql: `SELECT COUNT(*) as cnt FROM memories WHERE status = 'deleted' AND deleted_at IS NOT NULL AND deleted_at < ?`,
@@ -2722,7 +2745,7 @@ function startIntercomQueueDrain() {
2722
2745
  const tick = async () => {
2723
2746
  fired("intercom_queue_drain");
2724
2747
  try {
2725
- const { drainQueue } = await import("../intercom-queue-K3DVKSPJ.js");
2748
+ const { drainQueue } = await import("../intercom-queue-RNM6EPGA.js");
2726
2749
  const { isSessionBusy } = await import("./tmux-routing.js");
2727
2750
  const { sendIntercom: centralSendIntercom } = await import("./tmux-routing.js");
2728
2751
  const result = drainQueue(
@@ -2758,7 +2781,7 @@ function startOrphanReaper() {
2758
2781
  const tick = async () => {
2759
2782
  fired("orphan_reaper");
2760
2783
  try {
2761
- const { reapOrphanedMcpProcesses, createOrphanReaperRealDeps } = await import("../daemon-orchestration-VO5XQIJL.js");
2784
+ const { reapOrphanedMcpProcesses, createOrphanReaperRealDeps } = await import("../daemon-orchestration-CHV6MB42.js");
2762
2785
  const deps = createOrphanReaperRealDeps();
2763
2786
  const reaped = await reapOrphanedMcpProcesses(deps);
2764
2787
  if (reaped.length > 0) acted("orphan_reaper");
@@ -2782,7 +2805,7 @@ function startZombieAgentReaper() {
2782
2805
  const tick = async () => {
2783
2806
  fired("zombie_agent_reaper");
2784
2807
  try {
2785
- const { reapZombieAgentProcesses, createZombieAgentReaperRealDeps } = await import("../daemon-orchestration-VO5XQIJL.js");
2808
+ const { reapZombieAgentProcesses, createZombieAgentReaperRealDeps } = await import("../daemon-orchestration-CHV6MB42.js");
2786
2809
  const deps = createZombieAgentReaperRealDeps();
2787
2810
  const reaped = reapZombieAgentProcesses(deps);
2788
2811
  if (reaped.length > 0) acted("zombie_agent_reaper");
@@ -2805,7 +2828,7 @@ function startWorktreeReaper() {
2805
2828
  const tick = async () => {
2806
2829
  fired("worktree_reaper");
2807
2830
  try {
2808
- const { reapOrphanedWorktrees, createWorktreeReaperRealDeps } = await import("../daemon-orchestration-VO5XQIJL.js");
2831
+ const { reapOrphanedWorktrees, createWorktreeReaperRealDeps } = await import("../daemon-orchestration-CHV6MB42.js");
2809
2832
  const deps = await createWorktreeReaperRealDeps();
2810
2833
  const result = await reapOrphanedWorktrees(deps);
2811
2834
  if (result.pruned.length > 0) {
@@ -2829,105 +2852,14 @@ function startWorktreeReaper() {
2829
2852
  `);
2830
2853
  }
2831
2854
  var AUTO_WAKE_INTERVAL_MS = 60 * 1e3;
2832
- function startAutoWake() {
2833
- const tick = async () => {
2834
- fired("auto_wake");
2835
- if (process.env.EXE_AUTO_WAKE === "0") return;
2836
- if (!await ensureStoreForPolling()) return;
2837
- try {
2838
- const { getClient: getClient2 } = await import("./database.js");
2839
- const { pollOrphanedTasks, createAutoWakeRealDeps } = await import("../daemon-orchestration-VO5XQIJL.js");
2840
- const deps = createAutoWakeRealDeps(getClient2, process.cwd());
2841
- const woken = await pollOrphanedTasks(deps);
2842
- if (woken.length > 0) acted("auto_wake");
2843
- for (const agent of woken) {
2844
- process.stderr.write(`[exed] Auto-wake: spawned ${agent}
2845
- `);
2846
- }
2847
- try {
2848
- const signalBase = path3.join(os2.homedir(), ".exe-os", "task-signals");
2849
- if (existsSync3(signalBase)) {
2850
- const { readdirSync, statSync: statSyncFs } = await import("fs");
2851
- const { getSessionState, sendIntercom } = await import("./tmux-routing.js");
2852
- const sessionCache = path3.join(os2.homedir(), ".exe-os", "session-cache");
2853
- const sessions = execFileSyncNode("tmux", ["list-sessions", "-F", "#{session_name}"], { encoding: "utf8", timeout: 2e3 }).trim().split("\n").filter(Boolean);
2854
- const signalDirs = [];
2855
- for (const entry of readdirSync(signalBase, { withFileTypes: true })) {
2856
- if (entry.isDirectory()) signalDirs.push({ dir: path3.join(signalBase, entry.name), scope: entry.name });
2857
- }
2858
- signalDirs.push({ dir: signalBase, scope: null });
2859
- const runtimeFor = (sessionName) => {
2860
- try {
2861
- const dispatchInfoPath = path3.join(sessionCache, `dispatch-info-${sessionName}.json`);
2862
- if (!existsSync3(dispatchInfoPath)) return "claude";
2863
- const raw = JSON.parse(readFileSync2(dispatchInfoPath, "utf8"));
2864
- return raw.runtime ?? "claude";
2865
- } catch {
2866
- return "claude";
2867
- }
2868
- };
2869
- for (const { dir, scope } of signalDirs) {
2870
- let files = [];
2871
- try {
2872
- files = readdirSync(dir).filter((f) => f.endsWith(".pending") && !f.includes("review-"));
2873
- } catch {
2874
- continue;
2875
- }
2876
- for (const file of files) {
2877
- const signalPath = path3.join(dir, file);
2878
- const age = Date.now() - statSyncFs(signalPath).mtimeMs;
2879
- if (age > 5 * 60 * 1e3) continue;
2880
- const agentId = file.includes(".") ? file.split(".")[0] : file.replace(".pending", "");
2881
- const match = sessions.find((s) => {
2882
- const agentPart = s.split("-")[0] ?? s;
2883
- const base = agentPart.replace(/\d+$/, "");
2884
- if (base !== agentId && s !== agentId) return false;
2885
- if (!scope) return true;
2886
- return s.endsWith(`-${scope}`);
2887
- });
2888
- if (!match) continue;
2889
- const state = getSessionState(match);
2890
- if (state !== "idle") continue;
2891
- const runtime = runtimeFor(match);
2892
- if (runtime === "codex" || runtime === "opencode" || runtime === "exe-agent") {
2893
- execFileSyncNode("tmux", ["kill-session", "-t", match], { timeout: 3e3 });
2894
- acted("auto_wake");
2895
- process.stderr.write(
2896
- `[exed] Signal-wake: killed idle ${runtime} session ${match} for fresh task signal ${file}; auto-wake will respawn (signal age ${Math.round(age / 1e3)}s)
2897
- `
2898
- );
2899
- } else {
2900
- sendIntercom(match, { force: true });
2901
- process.stderr.write(`[exed] Signal-nudge: sent intercom to ${match} (signal age ${Math.round(age / 1e3)}s)
2902
- `);
2903
- }
2904
- }
2905
- }
2906
- }
2907
- } catch (err) {
2908
- process.stderr.write(`[exed] Signal-wake error: ${err instanceof Error ? err.message : String(err)}
2909
- `);
2910
- }
2911
- } catch (err) {
2912
- process.stderr.write(`[exed] Auto-wake error: ${err instanceof Error ? err.message : String(err)}
2913
- `);
2914
- }
2915
- };
2916
- setTimeout(() => {
2917
- const timer = setInterval(() => void tick(), AUTO_WAKE_INTERVAL_MS);
2918
- timer.unref();
2919
- }, 15e3);
2920
- process.stderr.write(`[exed] Auto-wake started (every ${AUTO_WAKE_INTERVAL_MS / 1e3}s, offset 15s)
2921
- `);
2922
- }
2923
2855
  var WAL_CHECKPOINT_INTERVAL_MS = 5 * 60 * 1e3;
2924
2856
  function startWalCheckpoint() {
2925
2857
  const tick = async () => {
2926
2858
  fired("wal_checkpoint");
2927
2859
  if (!await ensureStoreForPolling()) return;
2928
2860
  try {
2929
- const { getClient: getClient2 } = await import("./database.js");
2930
- const client = getClient2();
2861
+ const { getClient } = await import("./database.js");
2862
+ const client = getClient();
2931
2863
  await client.execute("PRAGMA wal_checkpoint(TRUNCATE)");
2932
2864
  acted("wal_checkpoint");
2933
2865
  } catch {
@@ -2944,9 +2876,9 @@ function startStuckTaskRelease() {
2944
2876
  fired("stuck_task_release");
2945
2877
  if (!await ensureStoreForPolling()) return;
2946
2878
  try {
2947
- const { getClient: getClient2 } = await import("./database.js");
2948
- const { releaseStuckTasks, createStuckTaskRealDeps } = await import("../daemon-orchestration-VO5XQIJL.js");
2949
- const deps = createStuckTaskRealDeps(getClient2);
2879
+ const { getClient } = await import("./database.js");
2880
+ const { releaseStuckTasks, createStuckTaskRealDeps } = await import("../daemon-orchestration-CHV6MB42.js");
2881
+ const deps = createStuckTaskRealDeps(getClient);
2950
2882
  const released = await releaseStuckTasks(deps);
2951
2883
  if (released.length > 0) {
2952
2884
  acted("stuck_task_release");
@@ -2986,18 +2918,23 @@ function startTaskEnforcementScanner() {
2986
2918
  const tick = async () => {
2987
2919
  fired("task_enforcement");
2988
2920
  try {
2989
- const { runTaskEnforcementTick } = await import("../task-enforcement-7FUILB63.js");
2921
+ const { runTaskEnforcementTick } = await import("../task-enforcement-2JIJSXPU.js");
2990
2922
  const { getTransport } = await import("./transport.js");
2991
2923
  const { loadAgentConfig } = await import("./agent-config.js");
2992
- const { getClient: getClient2 } = await import("./database.js");
2924
+ const { getClient } = await import("./database.js");
2993
2925
  const { loadEmployeesSync } = await import("./employees.js");
2994
- const { sessionScopeFilter } = await import("../task-scope-2N45TE32.js");
2926
+ const { sessionScopeFilter } = await import("../task-scope-W73Z3XWE.js");
2927
+ const transport = getTransport();
2928
+ const allSessions = getTmuxSessions().filter((s) => s.includes("-"));
2929
+ const paneCache = await batchCapturePanes(allSessions, 20);
2995
2930
  await runTaskEnforcementTick({
2996
- transport: getTransport(),
2931
+ transport,
2997
2932
  agentConfig: loadAgentConfig(),
2998
2933
  employees: loadEmployeesSync(),
2999
- client: await getClient2(),
3000
- scopeFilter: sessionScopeFilter()
2934
+ client: await getClient(),
2935
+ scopeFilter: sessionScopeFilter(),
2936
+ capturePaneAsync,
2937
+ paneCache
3001
2938
  });
3002
2939
  acted("task_enforcement");
3003
2940
  } catch (err) {
@@ -3095,11 +3032,9 @@ function startApiWatchdog() {
3095
3032
  const tick = async () => {
3096
3033
  fired("api_watchdog");
3097
3034
  try {
3098
- const { loadConfigSync } = await import("./config.js");
3099
- const cfg = loadConfigSync();
3100
- if (!cfg.apiWatchdog?.enabled) return;
3035
+ const cfg = await getCachedConfig();
3036
+ if (!cfg?.apiWatchdog?.enabled) return;
3101
3037
  const cooldownMs = (cfg.apiWatchdog.cooldownMinutes ?? 10) * 6e4;
3102
- const { execFileSync } = await import("child_process");
3103
3038
  const sessions = getTmuxSessions();
3104
3039
  if (sessions.length === 0) return;
3105
3040
  for (const session of sessions) {
@@ -3108,7 +3043,8 @@ function startApiWatchdog() {
3108
3043
  if (Date.now() - lastNudge < cooldownMs) continue;
3109
3044
  let pane;
3110
3045
  try {
3111
- pane = execFileSync("tmux", ["capture-pane", "-t", session, "-p", "-S", "-30"], { timeout: 3e3 }).toString();
3046
+ pane = await capturePaneAsync(session, 30);
3047
+ if (!pane) continue;
3112
3048
  } catch {
3113
3049
  continue;
3114
3050
  }
@@ -3127,7 +3063,7 @@ function startApiWatchdog() {
3127
3063
  process.stderr.write(`[exed] API watchdog KILLING ${session} after ${nudgeCount} failed nudges (matched: ${matchedPattern.source})
3128
3064
  `);
3129
3065
  try {
3130
- execFileSync("tmux", ["kill-session", "-t", session], { timeout: 3e3 });
3066
+ execFileSyncNode("tmux", ["kill-session", "-t", session], { timeout: 3e3 });
3131
3067
  _apiWatchdogNudgeCounts.delete(session);
3132
3068
  _apiWatchdogLastError.delete(session);
3133
3069
  } catch {
@@ -3135,7 +3071,7 @@ function startApiWatchdog() {
3135
3071
  continue;
3136
3072
  }
3137
3073
  try {
3138
- execFileSync("tmux", ["send-keys", "-t", session, "", "Enter"], { timeout: 2e3 });
3074
+ execFileSyncNode("tmux", ["send-keys", "-t", session, "", "Enter"], { timeout: 2e3 });
3139
3075
  _apiWatchdogLastNudge.set(session, Date.now());
3140
3076
  acted("api_watchdog");
3141
3077
  process.stderr.write(`[exed] API watchdog nudged ${session} (attempt ${nudgeCount}/3, matched: ${matchedPattern.source})
@@ -3197,35 +3133,6 @@ function startBackgroundJobGuardrails() {
3197
3133
  function handleSignalShutdown() {
3198
3134
  if (_shuttingDown) return;
3199
3135
  _shuttingDown = true;
3200
- if (process.platform === "darwin" && _llama) {
3201
- resetIdleTimer();
3202
- logRestartEvent("daemon_signal_received", { platform: "darwin", fast_exit: true, uptime_s: Math.floor((Date.now() - _startedAt2) / 1e3) });
3203
- logShutdown("darwin signal fast native exit", "SIGTERM/SIGINT");
3204
- try {
3205
- flushToDisk();
3206
- } catch {
3207
- }
3208
- try {
3209
- if (isInitialized()) {
3210
- const cl = getClient();
3211
- if (typeof cl.executeSync === "function") {
3212
- cl.executeSync("PRAGMA wal_checkpoint(PASSIVE)");
3213
- }
3214
- }
3215
- } catch {
3216
- }
3217
- try {
3218
- unlinkSync(SOCKET_PATH);
3219
- } catch {
3220
- }
3221
- try {
3222
- unlinkSync(PID_PATH);
3223
- } catch {
3224
- }
3225
- process.stderr.write("[exed] Shutdown complete (darwin signal fast native exit).\n");
3226
- process.kill(process.pid, "SIGKILL");
3227
- process.exit(0);
3228
- }
3229
3136
  logRestartEvent("daemon_signal_received", { platform: process.platform, fast_exit: false, uptime_s: Math.floor((Date.now() - _startedAt2) / 1e3) });
3230
3137
  logShutdown("graceful shutdown", "SIGTERM/SIGINT");
3231
3138
  void shutdown();
@@ -3244,8 +3151,8 @@ function startBugAutoFix() {
3244
3151
  const tick = async () => {
3245
3152
  fired("bug_autofix");
3246
3153
  try {
3247
- const { getClient: getClient2 } = await import("./database.js");
3248
- const client = await getClient2();
3154
+ const { getClient } = await import("./database.js");
3155
+ const client = await getClient();
3249
3156
  const result = await client.execute({
3250
3157
  sql: `SELECT id, title, priority, assigned_to, status FROM tasks
3251
3158
  WHERE priority IN ('p0', 'p1')
@@ -3257,7 +3164,10 @@ function startBugAutoFix() {
3257
3164
  args: []
3258
3165
  });
3259
3166
  if (result.rows.length === 0) return;
3260
- const ctoName = "yoshi";
3167
+ const { loadEmployeesSync: loadEmpSync } = await import("./employees.js");
3168
+ const empRoster = loadEmpSync();
3169
+ const ctoEmp = empRoster.find((e) => e.role === "CTO");
3170
+ const ctoName = ctoEmp?.name ?? "yoshi";
3261
3171
  for (const row of result.rows) {
3262
3172
  const taskId = String(row.id);
3263
3173
  if (_autoFixDispatched.has(taskId)) continue;
@@ -3355,7 +3265,6 @@ function checkExistingDaemon() {
3355
3265
  }
3356
3266
  return false;
3357
3267
  }
3358
- process.kill(pid, 0);
3359
3268
  try {
3360
3269
  const state = execSyncNode(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
3361
3270
  if (state.startsWith("Z")) {
@@ -3373,6 +3282,18 @@ function checkExistingDaemon() {
3373
3282
  return false;
3374
3283
  }
3375
3284
  } catch {
3285
+ process.stderr.write(`[exed] PID ${pid} no longer exists \u2014 cleaning stale PID file.
3286
+ `);
3287
+ try {
3288
+ unlinkSync(PID_PATH);
3289
+ } catch {
3290
+ }
3291
+ try {
3292
+ unlinkSync(SOCKET_PATH);
3293
+ } catch {
3294
+ }
3295
+ killOrphanDaemons();
3296
+ return false;
3376
3297
  }
3377
3298
  process.stderr.write(`[exed] Another daemon is already running (PID ${pid}). Exiting.
3378
3299
  `);
@@ -3415,16 +3336,25 @@ function startAutoUpdateCheck() {
3415
3336
  `
3416
3337
  );
3417
3338
  if (autoInstall) {
3418
- process.stderr.write("[exed] Auto-installing update...\n");
3419
- const { execSync: execSync2 } = await import("child_process");
3420
- execSync2("npm install -g @askexenow/exe-os@latest", {
3421
- timeout: 12e4,
3422
- stdio: ["pipe", "pipe", "pipe"]
3339
+ process.stderr.write("[exed] Auto-installing update (async \u2014 MCP stays responsive)...\n");
3340
+ const { spawn: spawnChild } = await import("child_process");
3341
+ const child = spawnChild("npm", ["install", "-g", "@askexenow/exe-os@latest"], {
3342
+ stdio: ["pipe", "pipe", "pipe"],
3343
+ timeout: 12e4
3344
+ });
3345
+ child.on("close", (code) => {
3346
+ if (code === 0) {
3347
+ process.stderr.write(`[exed] Updated to v${result.remoteVersion}. Restart daemon to apply.
3348
+ `);
3349
+ } else {
3350
+ process.stderr.write(`[exed] Auto-update failed (exit ${code}).
3351
+ `);
3352
+ }
3353
+ });
3354
+ child.on("error", (err) => {
3355
+ process.stderr.write(`[exed] Auto-update error: ${err.message}
3356
+ `);
3423
3357
  });
3424
- process.stderr.write(
3425
- `[exed] Updated to v${result.remoteVersion}. Restart daemon to apply.
3426
- `
3427
- );
3428
3358
  }
3429
3359
  void checkIntervalMs;
3430
3360
  } catch (err) {
@@ -3460,7 +3390,7 @@ try {
3460
3390
  chmodSync(DAEMON_BOOT_PATH, 420);
3461
3391
  } catch {
3462
3392
  }
3463
- await new Promise((r) => setTimeout(r, 50));
3393
+ await new Promise((r) => setTimeout(r, 500));
3464
3394
  try {
3465
3395
  const claimedPid = parseInt(readFileSync2(PID_PATH, "utf8").trim(), 10);
3466
3396
  if (claimedPid !== process.pid) {
@@ -3478,6 +3408,20 @@ try {
3478
3408
  process.exit(0);
3479
3409
  }
3480
3410
  process.env.EXE_IS_DAEMON = "1";
3411
+ if (process.platform === "darwin") {
3412
+ try {
3413
+ const oldPlist = path3.join(os2.homedir(), "Library", "LaunchAgents", "com.askexe.exed-keepalive.plist");
3414
+ if (existsSync3(oldPlist)) {
3415
+ try {
3416
+ execSyncNode(`launchctl unload "${oldPlist}" 2>/dev/null`, { timeout: 5e3 });
3417
+ } catch {
3418
+ }
3419
+ unlinkSync(oldPlist);
3420
+ process.stderr.write("[exed] Removed old keepalive plist (replaced by launchd KeepAlive).\n");
3421
+ }
3422
+ } catch {
3423
+ }
3424
+ }
3481
3425
  logBoot({ socketPath: SOCKET_PATH, pidPath: PID_PATH });
3482
3426
  try {
3483
3427
  const mcpRegPath = new URL("../mcp/register-tools.js", import.meta.url).href;
@@ -3495,17 +3439,89 @@ try {
3495
3439
  `
3496
3440
  );
3497
3441
  });
3442
+ {
3443
+ let lastTick = Date.now();
3444
+ const LAG_WARN_MS = 3e3;
3445
+ const LAG_CRITICAL_MS = 1e4;
3446
+ const lagTimer = setInterval(() => {
3447
+ const now = Date.now();
3448
+ const lag = now - lastTick - 2e3;
3449
+ lastTick = now;
3450
+ if (lag > LAG_CRITICAL_MS) {
3451
+ const msg = `[exed] EVENT LOOP BLOCKED ${(lag / 1e3).toFixed(1)}s \u2014 MCP UNRESPONSIVE. Sessions will die.`;
3452
+ process.stderr.write(msg + "\n");
3453
+ logDaemonHealth({ event: "event_loop_blocked", pid: process.pid, message: msg, data: { lagMs: lag } });
3454
+ try {
3455
+ writeFileSync2(path3.join(os2.homedir(), ".exe-os", "session-cache", "event-loop-blocked.marker"), `lag=${lag}ms at ${(/* @__PURE__ */ new Date()).toISOString()}
3456
+ `);
3457
+ } catch {
3458
+ }
3459
+ } else if (lag > LAG_WARN_MS) {
3460
+ process.stderr.write(`[exed] Event loop lag: ${(lag / 1e3).toFixed(1)}s (warn threshold: ${LAG_WARN_MS / 1e3}s)
3461
+ `);
3462
+ }
3463
+ }, 2e3);
3464
+ lagTimer.unref();
3465
+ }
3498
3466
  startMemoryQueueDrain();
3499
3467
  startMessageQueueDrain();
3500
3468
  startIntercomQueueDrain();
3501
3469
  startReviewPolling();
3502
3470
  startSessionTTL();
3503
3471
  startTaskEnforcementScanner();
3504
- startAutoWake();
3505
- startCooAutoRestart();
3506
3472
  startApiWatchdog();
3507
3473
  startRssWatchdog();
3508
3474
  setTimeout(startWalCheckpoint, 5e3);
3475
+ setTimeout(async () => {
3476
+ try {
3477
+ if (!await ensureStoreForPolling()) return;
3478
+ const { readdirSync, readFileSync: rfs } = await import("fs");
3479
+ const { getClient } = await import("./database.js");
3480
+ const client = getClient();
3481
+ const tasksRoot = path3.join(os2.homedir(), ".exe-os", "tasks");
3482
+ let synced = 0;
3483
+ const walk = (dir) => {
3484
+ try {
3485
+ for (const e of readdirSync(dir, { withFileTypes: true })) {
3486
+ const full = path3.join(dir, e.name);
3487
+ if (e.isDirectory()) walk(full);
3488
+ else if (e.name.endsWith(".md")) {
3489
+ try {
3490
+ const content = rfs(full, "utf8");
3491
+ const id = content.match(/\*\*ID:\*\*\s*([a-f0-9-]+)/)?.[1];
3492
+ const title = content.split("\n")[0]?.replace(/^# /, "") || "";
3493
+ const assignedTo = content.match(/\*\*Assigned to:\*\*\s*(\w+)/)?.[1];
3494
+ const assignedBy = content.match(/\*\*Assigned by:\*\*\s*(\w+)/)?.[1] || "exe";
3495
+ const project = content.match(/\*\*Project:\*\*\s*([\w-]+)/)?.[1] || "exe-os";
3496
+ const status = content.match(/\*\*Status:\*\*\s*(\w+)/)?.[1] || "open";
3497
+ const priority = content.match(/\*\*Priority:\*\*\s*(\w+)/)?.[1] || "p1";
3498
+ const scope = full.match(/tasks\/([^/]+)\//)?.[1] || null;
3499
+ if (id && assignedTo) {
3500
+ client.execute({
3501
+ sql: `INSERT OR IGNORE INTO tasks (id, title, assigned_to, assigned_by, project_name, status, priority, task_file, session_scope, created_at, updated_at)
3502
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
3503
+ args: [id, title, assignedTo, assignedBy, project, status, priority, full, scope, (/* @__PURE__ */ new Date()).toISOString(), (/* @__PURE__ */ new Date()).toISOString()]
3504
+ }).then(() => {
3505
+ synced++;
3506
+ }).catch(() => {
3507
+ });
3508
+ }
3509
+ } catch {
3510
+ }
3511
+ }
3512
+ }
3513
+ } catch {
3514
+ }
3515
+ };
3516
+ walk(tasksRoot);
3517
+ await new Promise((r) => setTimeout(r, 2e3));
3518
+ if (synced > 0) process.stderr.write(`[exed] Task re-sync: ${synced} missing DB rows restored from disk.
3519
+ `);
3520
+ } catch (e) {
3521
+ process.stderr.write(`[exed] Task re-sync error: ${e.message}
3522
+ `);
3523
+ }
3524
+ }, 8e3);
3509
3525
  setTimeout(startIdleKill, 1e4);
3510
3526
  setTimeout(startStuckTaskRelease, 15e3);
3511
3527
  setTimeout(startOrphanReaper, 2e4);
@@ -3513,8 +3529,8 @@ try {
3513
3529
  setTimeout(startAgentStats, 3e4);
3514
3530
  setTimeout(startConsolidation, 6e4);
3515
3531
  setTimeout(startSkillSweep, 9e4);
3516
- setTimeout(startSkillRefinement, 1e5);
3517
- setTimeout(startGraphExtraction, 12e4);
3532
+ setTimeout(startSkillRefinement, 18e4);
3533
+ setTimeout(startGraphExtraction, 27e4);
3518
3534
  setTimeout(startReflectionSweep, 15e4);
3519
3535
  setTimeout(startConfidenceDecay, 18e4);
3520
3536
  setTimeout(startSoftDeletePurge, 21e4);
@@ -3528,9 +3544,9 @@ try {
3528
3544
  fired("intercom_dedup_cleanup");
3529
3545
  try {
3530
3546
  if (!await ensureStoreForPolling()) return;
3531
- const { getClient: getClient2 } = await import("./database.js");
3547
+ const { getClient } = await import("./database.js");
3532
3548
  const { deduplicateIntercomMemories } = await import("./consolidation.js");
3533
- const count = await deduplicateIntercomMemories(getClient2());
3549
+ const count = await deduplicateIntercomMemories(getClient());
3534
3550
  if (count > 0) {
3535
3551
  acted("intercom_dedup_cleanup");
3536
3552
  process.stderr.write(`[exed] Intercom dedup: archived ${count} duplicate intercom memories
@@ -3545,9 +3561,9 @@ try {
3545
3561
  fired("trace_ttl_sweep");
3546
3562
  try {
3547
3563
  if (!await ensureStoreForPolling()) return;
3548
- const { getClient: getClient2 } = await import("./database.js");
3564
+ const { getClient } = await import("./database.js");
3549
3565
  const { sweepExpiredTraces } = await import("./consolidation.js");
3550
- const count = await sweepExpiredTraces(getClient2());
3566
+ const count = await sweepExpiredTraces(getClient());
3551
3567
  if (count > 0) {
3552
3568
  acted("trace_ttl_sweep");
3553
3569
  process.stderr.write(`[exed] Trace TTL: deleted ${count} expired raw tool traces (>7d, importance<7)
@@ -3562,7 +3578,7 @@ try {
3562
3578
  fired("orphan_task_cleanup");
3563
3579
  try {
3564
3580
  if (!await ensureStoreForPolling()) return;
3565
- const { cleanOrphanedTaskFiles } = await import("../tasks-crud-ADLCGHGH.js");
3581
+ const { cleanOrphanedTaskFiles } = await import("../tasks-crud-HPJKI3QQ.js");
3566
3582
  const count = await cleanOrphanedTaskFiles();
3567
3583
  if (count > 0) {
3568
3584
  acted("orphan_task_cleanup");
@@ -3591,100 +3607,107 @@ try {
3591
3607
  } catch {
3592
3608
  }
3593
3609
  }, 15 * 60 * 1e3);
3594
- const { startToolTelemetryFlush } = await import("../tool-telemetry-72PVO5HV.js");
3610
+ const { startToolTelemetryFlush } = await import("../tool-telemetry-GZ5E2AUL.js");
3595
3611
  startToolTelemetryFlush();
3596
3612
  process.stderr.write("[exed] Tool telemetry started (flush every 5m)\n");
3597
- const { startProjectionWorker } = await import("../projection-worker-FK5YOEIL.js");
3598
- startProjectionWorker();
3599
- try {
3600
- const { loadConfig } = await import("./config.js");
3601
- const config = await loadConfig();
3602
- if (config.cloud?.endpoint) {
3603
- const { getMasterKey } = await import("./keychain.js");
3604
- const masterKey = await getMasterKey();
3605
- if (masterKey) {
3606
- const { deriveWsAuthToken, deriveOrgId } = await import("./ws-auth.js");
3607
- const { getDeviceInfo } = await import("./device-registry.js");
3608
- const { createWsClient } = await import("./ws-client.js");
3609
- const { initStore } = await import("./store.js");
3610
- const { sendMessage, deliverLocalMessage, setWsClientSend } = await import("./messaging.js");
3611
- const { getClient: getClient2 } = await import("./database.js");
3612
- await initStore();
3613
- const wsClient = createWsClient({
3614
- endpoint: config.cloud.endpoint.replace(/^https?/, "wss") + "/ws",
3615
- orgId: deriveOrgId(masterKey),
3616
- authToken: deriveWsAuthToken(masterKey),
3617
- deviceInfo: getDeviceInfo()
3618
- }, {
3619
- onMessage: async (payload) => {
3620
- try {
3621
- const data = JSON.parse(payload);
3622
- const msg = await sendMessage({
3623
- fromAgent: data.fromAgent,
3624
- targetAgent: data.targetAgent,
3625
- targetProject: data.targetProject,
3626
- content: data.content,
3627
- priority: data.priority
3628
- });
3629
- await deliverLocalMessage(msg.id);
3630
- } catch (err) {
3631
- process.stderr.write(`[exed] WS message delivery failed: ${err instanceof Error ? err.message : String(err)}
3613
+ setTimeout(async () => {
3614
+ try {
3615
+ const { startProjectionWorker } = await import("../projection-worker-EBUYNMU2.js");
3616
+ startProjectionWorker();
3617
+ } catch {
3618
+ }
3619
+ }, 2e4);
3620
+ setTimeout(async () => {
3621
+ try {
3622
+ const { loadConfig } = await import("./config.js");
3623
+ const config = await loadConfig();
3624
+ if (config.cloud?.endpoint) {
3625
+ const { getMasterKey } = await import("./keychain.js");
3626
+ const masterKey = await getMasterKey();
3627
+ if (masterKey) {
3628
+ const { deriveWsAuthToken, deriveOrgId } = await import("./ws-auth.js");
3629
+ const { getDeviceInfo } = await import("./device-registry.js");
3630
+ const { createWsClient } = await import("./ws-client.js");
3631
+ const { initStore } = await import("./store.js");
3632
+ const { sendMessage, deliverLocalMessage, setWsClientSend } = await import("./messaging.js");
3633
+ const { getClient } = await import("./database.js");
3634
+ await initStore();
3635
+ const wsClient = createWsClient({
3636
+ endpoint: config.cloud.endpoint.replace(/^https?/, "wss") + "/ws",
3637
+ orgId: deriveOrgId(masterKey),
3638
+ authToken: deriveWsAuthToken(masterKey),
3639
+ deviceInfo: getDeviceInfo()
3640
+ }, {
3641
+ onMessage: async (payload) => {
3642
+ try {
3643
+ const data = JSON.parse(payload);
3644
+ const msg = await sendMessage({
3645
+ fromAgent: data.fromAgent,
3646
+ targetAgent: data.targetAgent,
3647
+ targetProject: data.targetProject,
3648
+ content: data.content,
3649
+ priority: data.priority
3650
+ });
3651
+ await deliverLocalMessage(msg.id);
3652
+ } catch (err) {
3653
+ process.stderr.write(`[exed] WS message delivery failed: ${err instanceof Error ? err.message : String(err)}
3632
3654
  `);
3633
- }
3634
- },
3635
- onRegistry: async (devices) => {
3636
- try {
3637
- const client = getClient2();
3638
- const now = (/* @__PURE__ */ new Date()).toISOString();
3639
- for (const d of devices) {
3640
- await client.execute({
3641
- sql: `INSERT INTO device_registry (device_id, friendly_name, hostname, projects, agents, connected, last_seen)
3655
+ }
3656
+ },
3657
+ onRegistry: async (devices) => {
3658
+ try {
3659
+ const client = getClient();
3660
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3661
+ for (const d of devices) {
3662
+ await client.execute({
3663
+ sql: `INSERT INTO device_registry (device_id, friendly_name, hostname, projects, agents, connected, last_seen)
3642
3664
  VALUES (?, ?, ?, ?, ?, 1, ?)
3643
3665
  ON CONFLICT(device_id) DO UPDATE SET
3644
3666
  friendly_name = ?, hostname = ?, projects = ?, agents = ?, connected = 1, last_seen = ?`,
3645
- args: [
3646
- d.deviceId,
3647
- d.friendlyName,
3648
- d.hostname,
3649
- JSON.stringify(d.projects),
3650
- JSON.stringify(d.agents),
3651
- now,
3652
- d.friendlyName,
3653
- d.hostname,
3654
- JSON.stringify(d.projects),
3655
- JSON.stringify(d.agents),
3656
- now
3657
- ]
3658
- });
3659
- }
3660
- const connectedIds = devices.map((d) => d.deviceId);
3661
- if (connectedIds.length > 0) {
3662
- const placeholders = connectedIds.map(() => "?").join(",");
3663
- await client.execute({
3664
- sql: `UPDATE device_registry SET connected = 0 WHERE device_id NOT IN (${placeholders})`,
3665
- args: connectedIds
3666
- });
3667
- }
3668
- } catch (err) {
3669
- process.stderr.write(`[exed] Registry update failed: ${err instanceof Error ? err.message : String(err)}
3667
+ args: [
3668
+ d.deviceId,
3669
+ d.friendlyName,
3670
+ d.hostname,
3671
+ JSON.stringify(d.projects),
3672
+ JSON.stringify(d.agents),
3673
+ now,
3674
+ d.friendlyName,
3675
+ d.hostname,
3676
+ JSON.stringify(d.projects),
3677
+ JSON.stringify(d.agents),
3678
+ now
3679
+ ]
3680
+ });
3681
+ }
3682
+ const connectedIds = devices.map((d) => d.deviceId);
3683
+ if (connectedIds.length > 0) {
3684
+ const placeholders = connectedIds.map(() => "?").join(",");
3685
+ await client.execute({
3686
+ sql: `UPDATE device_registry SET connected = 0 WHERE device_id NOT IN (${placeholders})`,
3687
+ args: connectedIds
3688
+ });
3689
+ }
3690
+ } catch (err) {
3691
+ process.stderr.write(`[exed] Registry update failed: ${err instanceof Error ? err.message : String(err)}
3670
3692
  `);
3693
+ }
3694
+ },
3695
+ onConnect: () => {
3696
+ process.stderr.write("[exed] WS connected to Exe Cloud\n");
3697
+ },
3698
+ onDisconnect: () => {
3699
+ process.stderr.write("[exed] WS disconnected from Exe Cloud\n");
3671
3700
  }
3672
- },
3673
- onConnect: () => {
3674
- process.stderr.write("[exed] WS connected to Exe Cloud\n");
3675
- },
3676
- onDisconnect: () => {
3677
- process.stderr.write("[exed] WS disconnected from Exe Cloud\n");
3678
- }
3679
- });
3680
- setWsClientSend((targetDevice, payload) => wsClient.send(targetDevice, payload));
3681
- process.stderr.write("[exed] WS client initialized\n");
3701
+ });
3702
+ setWsClientSend((targetDevice, payload) => wsClient.send(targetDevice, payload));
3703
+ process.stderr.write("[exed] WS client initialized\n");
3704
+ }
3682
3705
  }
3683
- }
3684
- } catch (err) {
3685
- process.stderr.write(`[exed] WS client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
3706
+ } catch (err) {
3707
+ process.stderr.write(`[exed] WS client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
3686
3708
  `);
3687
- }
3709
+ }
3710
+ }, 3e4);
3688
3711
  } catch (err) {
3689
3712
  process.stderr.write(`[exed] FATAL: ${err instanceof Error ? err.message : String(err)}
3690
3713
  `);