@askexenow/exe-os 0.9.295 → 0.9.296

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 (279) hide show
  1. package/deploy/compose/cloudflared/config.yml.example +14 -9
  2. package/deploy/compose/docker-compose.yml +86 -8
  3. package/deploy/compose/sso-edge/default.conf.template +87 -0
  4. package/deploy/compose/sso-edge/entrypoint.sh +23 -0
  5. package/deploy/compose/sso-edge/sso-redirect.conf +63 -0
  6. package/deploy/stack-manifests/v0.9.json +1 -1
  7. package/dist/active-agent-AFX2FODG.js +28 -0
  8. package/dist/active-agent-E2IJA7YX.js +27 -0
  9. package/dist/agentic-ontology-A2YUZK5O.js +25 -0
  10. package/dist/assets/com.askexe.exed.plist +4 -1
  11. package/dist/backfill-metadata-OC7EOD5U.js +600 -0
  12. package/dist/behaviors-H5ZOVHDH.js +46 -0
  13. package/dist/bin/agentic-ontology-backfill.js +5 -5
  14. package/dist/bin/agentic-reflection-backfill.js +6 -6
  15. package/dist/bin/agentic-semantic-label.js +5 -5
  16. package/dist/bin/backfill-conversations.js +6 -6
  17. package/dist/bin/backfill-responses.js +6 -6
  18. package/dist/bin/backfill-vectors.js +8 -8
  19. package/dist/bin/bulk-sync-postgres.js +7 -7
  20. package/dist/bin/cc-doctor.js +4 -4
  21. package/dist/bin/cleanup-stale-review-tasks.js +11 -11
  22. package/dist/bin/cli.js +16 -16
  23. package/dist/bin/deferred-daemon-restart.js +1 -1
  24. package/dist/bin/exe-agent-config.js +2 -2
  25. package/dist/bin/exe-agent.js +4 -4
  26. package/dist/bin/exe-assign.js +8 -8
  27. package/dist/bin/exe-boot.js +21 -18
  28. package/dist/bin/exe-call.js +4 -4
  29. package/dist/bin/exe-cloud.js +7 -7
  30. package/dist/bin/exe-dispatch.js +11 -11
  31. package/dist/bin/exe-doctor.js +3 -2
  32. package/dist/bin/exe-export-behaviors.js +7 -7
  33. package/dist/bin/exe-forget.js +6 -6
  34. package/dist/bin/exe-gateway.js +7 -7
  35. package/dist/bin/exe-healthcheck.js +6 -4
  36. package/dist/bin/exe-heartbeat.js +11 -11
  37. package/dist/bin/exe-kill.js +14 -14
  38. package/dist/bin/exe-launch-agent.js +18 -18
  39. package/dist/bin/exe-new-employee.js +6 -6
  40. package/dist/bin/exe-pending-messages.js +12 -12
  41. package/dist/bin/exe-pending-notifications.js +11 -11
  42. package/dist/bin/exe-pending-reviews.js +11 -11
  43. package/dist/bin/exe-rename.js +4 -4
  44. package/dist/bin/exe-review.js +13 -13
  45. package/dist/bin/exe-search.js +5 -5
  46. package/dist/bin/exe-session-cleanup.js +16 -16
  47. package/dist/bin/exe-settings.js +39 -9
  48. package/dist/bin/exe-start-codex.js +11 -11
  49. package/dist/bin/exe-start-opencode.js +8 -8
  50. package/dist/bin/exe-status.js +12 -12
  51. package/dist/bin/exe-team.js +3 -3
  52. package/dist/bin/git-sweep.js +12 -12
  53. package/dist/bin/graph-backfill.js +4 -4
  54. package/dist/bin/graph-export.js +5 -5
  55. package/dist/bin/import-history.js +7 -7
  56. package/dist/bin/install-launchd.js +13 -6
  57. package/dist/bin/install.js +26 -14
  58. package/dist/bin/intercom-check.js +4 -4
  59. package/dist/bin/mcp-sessions.js +2 -2
  60. package/dist/bin/orchestration-metrics.js +4 -4
  61. package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
  62. package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
  63. package/dist/bin/scan-tasks.js +11 -11
  64. package/dist/bin/setup.js +1 -1
  65. package/dist/bin/shard-migrate.js +4 -4
  66. package/dist/bin/stack-update.js +2 -2
  67. package/dist/bin/vps-health-gate.js +1 -1
  68. package/dist/capability-cards-4USI7CUW.js +89 -0
  69. package/dist/capacity-monitor-WLCBTEYR.js +51 -0
  70. package/dist/catchup-brief-ZR3NX6LZ.js +175 -0
  71. package/dist/chunk-22TVSRQQ.js +226 -0
  72. package/dist/chunk-2E43UXRH.js +395 -0
  73. package/dist/chunk-2PIGT6UJ.js +460 -0
  74. package/dist/chunk-3XTMW2MZ.js +535 -0
  75. package/dist/chunk-465PQFTH.js +262 -0
  76. package/dist/chunk-5CCXU2AW.js +129 -0
  77. package/dist/chunk-5D6MPWR7.js +1094 -0
  78. package/dist/chunk-5Q4MR6SL.js +123 -0
  79. package/dist/chunk-6327RBWR.js +345 -0
  80. package/dist/chunk-6MZZREZY.js +199 -0
  81. package/dist/chunk-7DI2Q4O5.js +1186 -0
  82. package/dist/chunk-7PW5VNIY.js +122 -0
  83. package/dist/chunk-7T7Y56HW.js +43 -0
  84. package/dist/chunk-7UHCWCLT.js +128 -0
  85. package/dist/chunk-A2ZUMF6L.js +1350 -0
  86. package/dist/chunk-AKV44JEH.js +185 -0
  87. package/dist/chunk-ANHWGX5N.js +735 -0
  88. package/dist/chunk-BQ3P4TKD.js +97 -0
  89. package/dist/chunk-BUZMT3KZ.js +604 -0
  90. package/dist/chunk-C2SBESBO.js +210 -0
  91. package/dist/chunk-CLSXZUZW.js +51 -0
  92. package/dist/chunk-CONHLVAR.js +1079 -0
  93. package/dist/chunk-D3WTZPFX.js +456 -0
  94. package/dist/chunk-DE6SOIYL.js +197 -0
  95. package/dist/chunk-EIVNMA3Q.js +284 -0
  96. package/dist/chunk-EJIF4FNT.js +12 -0
  97. package/dist/chunk-FDFOW564.js +171 -0
  98. package/dist/chunk-GZUBJ5EC.js +127 -0
  99. package/dist/chunk-HGZITN22.js +105 -0
  100. package/dist/chunk-HSRKDU6X.js +362 -0
  101. package/dist/chunk-IIEN2PHV.js +85 -0
  102. package/dist/chunk-JQ56VLMM.js +567 -0
  103. package/dist/chunk-JVHHXRFY.js +280 -0
  104. package/dist/chunk-JXCXGZ3S.js +55 -0
  105. package/dist/chunk-K5ZO532Q.js +4388 -0
  106. package/dist/chunk-K6CAAMXF.js +97 -0
  107. package/dist/chunk-KA26YTNU.js +81 -0
  108. package/dist/chunk-KMUW5C3R.js +381 -0
  109. package/dist/chunk-KOO3J5PV.js +20 -0
  110. package/dist/chunk-LSV7OFIH.js +290 -0
  111. package/dist/chunk-LSVFDVNY.js +1158 -0
  112. package/dist/chunk-LXDQTW32.js +230 -0
  113. package/dist/chunk-MEP7OUVZ.js +181 -0
  114. package/dist/chunk-MN2B2LKS.js +240 -0
  115. package/dist/chunk-N2EAYPYQ.js +1352 -0
  116. package/dist/chunk-N7I2A667.js +70 -0
  117. package/dist/chunk-NLZHVIOP.js +630 -0
  118. package/dist/chunk-NUH5TRZL.js +227 -0
  119. package/dist/chunk-OAHEIH3G.js +167 -0
  120. package/dist/chunk-OBHRQGCK.js +58 -0
  121. package/dist/chunk-ODFA7B2V.js +54 -0
  122. package/dist/chunk-OSNUP45F.js +731 -0
  123. package/dist/chunk-OTPRHBTO.js +33 -0
  124. package/dist/chunk-P6MUA4QU.js +157 -0
  125. package/dist/chunk-PGIOFKSK.js +2093 -0
  126. package/dist/chunk-PSE7VHWK.js +50 -0
  127. package/dist/chunk-QIFUVZFW.js +331 -0
  128. package/dist/chunk-RDPXKTVK.js +221 -0
  129. package/dist/chunk-RKYTYJGB.js +76 -0
  130. package/dist/chunk-RXLR6EFM.js +348 -0
  131. package/dist/chunk-SDB67PQJ.js +159 -0
  132. package/dist/chunk-SF2T7MP3.js +402 -0
  133. package/dist/chunk-SLU3FRFQ.js +2133 -0
  134. package/dist/chunk-SNDZJ5IV.js +214 -0
  135. package/dist/chunk-STEEAABW.js +448 -0
  136. package/dist/chunk-TUTWNHIQ.js +244 -0
  137. package/dist/chunk-UDP35QBR.js +30 -0
  138. package/dist/chunk-UKFHNJBI.js +85 -0
  139. package/dist/chunk-VC2DTK2X.js +382 -0
  140. package/dist/chunk-VRRAE5JX.js +836 -0
  141. package/dist/chunk-VVJTBQPR.js +38 -0
  142. package/dist/chunk-W3EQ362K.js +581 -0
  143. package/dist/chunk-WHIXIFHC.js +2242 -0
  144. package/dist/chunk-WRNGJJNR.js +377 -0
  145. package/dist/chunk-WUKHLCBE.js +3313 -0
  146. package/dist/chunk-WVPLHGDG.js +150 -0
  147. package/dist/chunk-XJZBSTL5.js +204 -0
  148. package/dist/chunk-Y3PMNUM5.js +304 -0
  149. package/dist/chunk-YHVS4QOV.js +14597 -0
  150. package/dist/chunk-YJ2OYAOC.js +668 -0
  151. package/dist/chunk-YYAD2GXX.js +128 -0
  152. package/dist/chunk-ZQML7EWE.js +333 -0
  153. package/dist/co-activation-XJLH46OX.js +74 -0
  154. package/dist/co-occurrence-GNN2X526.js +95 -0
  155. package/dist/code-context-index-OCPRLFG5.js +30 -0
  156. package/dist/core-memory-J4W2IYOF.js +110 -0
  157. package/dist/crdt-sync-QCBTSHIH.js +33 -0
  158. package/dist/crm-webhook-EM442VUW.js +10 -0
  159. package/dist/cto-delegation-gate-MLJMVHBK.js +280 -0
  160. package/dist/daemon-orchestration-2VNLZVTW.js +139 -0
  161. package/dist/db-backup-VUGFTPJ4.js +43 -0
  162. package/dist/doc-graph-extractor-PNRSFPSS.js +133 -0
  163. package/dist/dreaming-SK5VEQRF.js +34 -0
  164. package/dist/entity-boost-TQWWJUC2.js +375 -0
  165. package/dist/exe-drift-N34UPO7S.js +70 -0
  166. package/dist/exe-export-KACBKGVV.js +77 -0
  167. package/dist/exe-import-GXGDWACG.js +80 -0
  168. package/dist/exe-key-XPDOZBWW.js +673 -0
  169. package/dist/exe-snapshot-32GQKGQ5.js +338 -0
  170. package/dist/fast-db-init-F3TDD5VV.js +7 -0
  171. package/dist/gateway/index.js +8 -8
  172. package/dist/git-staleness-J45WNYRF.js +112 -0
  173. package/dist/git-task-sweep-BTGVQPFB.js +42 -0
  174. package/dist/global-procedures-6JCQWU4D.js +22 -0
  175. package/dist/graph-auto-extract-3ZQNXTPC.js +183 -0
  176. package/dist/hooks/bug-report-worker.js +13 -13
  177. package/dist/hooks/codex-stop-task-finalizer.js +13 -13
  178. package/dist/hooks/commit-complete.js +13 -13
  179. package/dist/hooks/error-recall.js +6 -6
  180. package/dist/hooks/exe-heartbeat-hook.js +3 -3
  181. package/dist/hooks/ingest-worker.js +3 -3
  182. package/dist/hooks/ingest.js +6 -6
  183. package/dist/hooks/instructions-loaded.js +4 -4
  184. package/dist/hooks/manifest.json +20 -20
  185. package/dist/hooks/notification.js +4 -4
  186. package/dist/hooks/post-compact.js +12 -12
  187. package/dist/hooks/post-tool-combined.js +6 -6
  188. package/dist/hooks/pre-compact.js +16 -16
  189. package/dist/hooks/pre-tool-use.js +16 -16
  190. package/dist/hooks/prompt-submit.js +24 -24
  191. package/dist/hooks/session-end.js +21 -21
  192. package/dist/hooks/session-start.js +12 -12
  193. package/dist/hooks/stop.js +19 -19
  194. package/dist/hooks/subagent-stop.js +12 -12
  195. package/dist/hooks/summary-worker.js +19 -19
  196. package/dist/index.js +19 -19
  197. package/dist/installer-5VPFY7SB.js +298 -0
  198. package/dist/installer-OENFPMA2.js +344 -0
  199. package/dist/installer-OIX4QOG5.js +40 -0
  200. package/dist/lib/cloud-sync.js +7 -7
  201. package/dist/lib/consolidation.js +6 -5
  202. package/dist/lib/database.js +2 -2
  203. package/dist/lib/db-daemon-client.js +2 -2
  204. package/dist/lib/db.js +2 -2
  205. package/dist/lib/embed-worker.js +1 -0
  206. package/dist/lib/embedder.js +7 -3
  207. package/dist/lib/employee-templates.js +4 -4
  208. package/dist/lib/employees.js +2 -2
  209. package/dist/lib/exe-daemon-client.js +2 -2
  210. package/dist/lib/exe-daemon.js +160 -79
  211. package/dist/lib/hybrid-search.js +5 -5
  212. package/dist/lib/identity.js +2 -2
  213. package/dist/lib/messaging.js +11 -11
  214. package/dist/lib/reminders.js +3 -3
  215. package/dist/lib/schedules.js +5 -5
  216. package/dist/lib/session-registry.js +4 -4
  217. package/dist/lib/skill-learning.js +6 -6
  218. package/dist/lib/store.js +4 -4
  219. package/dist/lib/task-router.js +3 -3
  220. package/dist/lib/tasks.js +12 -12
  221. package/dist/lib/tmux-routing.js +12 -10
  222. package/dist/lib/tmux-transport.js +1 -1
  223. package/dist/lib/token-spend.js +3 -3
  224. package/dist/lib/transport.js +2 -2
  225. package/dist/mcp/register-tools.js +62 -61
  226. package/dist/mcp/server.js +63 -62
  227. package/dist/mcp/tools/complete-reminder.js +4 -4
  228. package/dist/mcp/tools/create-reminder.js +4 -4
  229. package/dist/mcp/tools/create-task.js +14 -14
  230. package/dist/mcp/tools/deactivate-behavior.js +7 -7
  231. package/dist/mcp/tools/list-reminders.js +4 -4
  232. package/dist/mcp/tools/list-tasks.js +14 -14
  233. package/dist/mcp/tools/send-message.js +13 -13
  234. package/dist/mcp/tools/update-task.js +13 -13
  235. package/dist/mcp-http-config-PQTOLCTP.js +29 -0
  236. package/dist/memory-cards-4RVDZIY2.js +180 -0
  237. package/dist/memory-graph-extractor-L6YC7G4M.js +22 -0
  238. package/dist/memory-poisoning-defense-4YVJYH4G.js +224 -0
  239. package/dist/memory-queue-client-MVAUOZNJ.js +16 -0
  240. package/dist/memory-reflection-SHHDQNOH.js +244 -0
  241. package/dist/message-queue-client-DCKZT6X2.js +92 -0
  242. package/dist/notifications-JFR3G42W.js +47 -0
  243. package/dist/orchestration-events-MGCGPTDN.js +27 -0
  244. package/dist/orchestrator-DAFL2YZB.js +35 -0
  245. package/dist/pipeline-router-WWSZVPCH.js +15 -0
  246. package/dist/plan-limits-C7XCSDZC.js +28 -0
  247. package/dist/project-boot-N3NTBVLE.js +299 -0
  248. package/dist/projection-worker-MTPAPCWX.js +1084 -0
  249. package/dist/prospective-memory-BTIVUJSB.js +232 -0
  250. package/dist/reranker-UA6WVESJ.js +19 -0
  251. package/dist/retrieval-health-7XNZJEBF.js +12 -0
  252. package/dist/review-polling-4ALGMXC3.js +126 -0
  253. package/dist/runtime/index.js +13 -13
  254. package/dist/self-query-router-MROFQLQB.js +192 -0
  255. package/dist/session-events-CK44XOU4.js +38 -0
  256. package/dist/session-kill-telemetry-MT6ITDOG.js +31 -0
  257. package/dist/session-scope-3XDBWV65.js +88 -0
  258. package/dist/setup-wizard-X6DOD7MC.js +12 -0
  259. package/dist/skill-refinement-G2CCY3GM.js +159 -0
  260. package/dist/stack-update-JF7F56AS.js +84 -0
  261. package/dist/steward-gate-YF2CYXE7.js +15 -0
  262. package/dist/task-enforcement-YN6HK7NE.js +506 -0
  263. package/dist/task-scope-CVK6ISCZ.js +37 -0
  264. package/dist/tasks-crud-NTNET4JE.js +79 -0
  265. package/dist/tasks-notify-4LJVFPCV.js +40 -0
  266. package/dist/tasks-review-3V4WOIRG.js +49 -0
  267. package/dist/telemetry-upload-5PNUKGTM.js +741 -0
  268. package/dist/token-budget-E46G7ZAQ.js +86 -0
  269. package/dist/tool-capability-index-JDSMKJER.js +10 -0
  270. package/dist/tool-telemetry-J3NLS3LJ.js +17 -0
  271. package/dist/tui/App.js +18 -18
  272. package/dist/tui-data-6DOMUUCM.js +260 -0
  273. package/dist/wiki-acl-5UK37LKF.js +111 -0
  274. package/dist/worker-gate-FM7AEC7G.js +21 -0
  275. package/dist/workflow-engine-2EDUHUIY.js +28 -0
  276. package/dist/worktree-7YKKJIYR.js +28 -0
  277. package/dist/worktree-sweep-C3ELFGDN.js +21 -0
  278. package/package.json +1 -1
  279. package/release-notes.json +23 -23
@@ -0,0 +1,226 @@
1
+ import {
2
+ getAgentContext
3
+ } from "./chunk-GJV3WDWM.js";
4
+
5
+ // src/lib/tmux-transport.ts
6
+ import { execFileSync } from "child_process";
7
+ import { writeFileSync, unlinkSync, mkdirSync } from "fs";
8
+ import { randomBytes } from "crypto";
9
+ import path from "path";
10
+ import os from "os";
11
+ var LONG_MESSAGE_THRESHOLD = 500;
12
+ var TMUX_STATUS_CACHE_TTL_MS = Number(process.env.EXE_TMUX_STATUS_CACHE_TTL_MS || 2e3);
13
+ var CODEX_SUBMIT_DELAY_MS = Number(process.env.EXE_CODEX_SUBMIT_DELAY_MS || 250);
14
+ function sleepSync(ms) {
15
+ if (ms <= 0) return;
16
+ try {
17
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
18
+ } catch {
19
+ }
20
+ }
21
+ var QUIET = {
22
+ encoding: "utf8",
23
+ stdio: ["pipe", "pipe", "pipe"],
24
+ timeout: 3e3
25
+ };
26
+ var TmuxTransport = class {
27
+ sessionsCache = { ts: 0, sessions: [] };
28
+ paneStatusCache = { ts: 0, bySession: /* @__PURE__ */ new Map(), byPaneId: /* @__PURE__ */ new Map() };
29
+ invalidateCaches() {
30
+ this.sessionsCache = { ts: 0, sessions: [] };
31
+ this.paneStatusCache = { ts: 0, bySession: /* @__PURE__ */ new Map(), byPaneId: /* @__PURE__ */ new Map() };
32
+ }
33
+ listSessionsCached() {
34
+ const now = Date.now();
35
+ if (now - this.sessionsCache.ts < TMUX_STATUS_CACHE_TTL_MS) return this.sessionsCache.sessions;
36
+ try {
37
+ const sessions = execFileSync("tmux", ["list-sessions", "-F", "#{session_name}"], QUIET).trim().split("\n").filter(Boolean);
38
+ this.sessionsCache = { ts: now, sessions };
39
+ return sessions;
40
+ } catch {
41
+ this.sessionsCache = { ts: now, sessions: [] };
42
+ return [];
43
+ }
44
+ }
45
+ listPaneStatusCached() {
46
+ const now = Date.now();
47
+ if (now - this.paneStatusCache.ts < TMUX_STATUS_CACHE_TTL_MS) return this.paneStatusCache;
48
+ const bySession = /* @__PURE__ */ new Map();
49
+ const byPaneId = /* @__PURE__ */ new Map();
50
+ try {
51
+ const out = execFileSync(
52
+ "tmux",
53
+ ["list-panes", "-a", "-F", "#{session_name} #{pane_id} #{pane_dead}"],
54
+ QUIET
55
+ );
56
+ for (const line of out.trim().split("\n").filter(Boolean)) {
57
+ const [session, paneId, paneDead] = line.split(" ");
58
+ if (!session) continue;
59
+ const dead = paneDead === "1" ? "1" : "0";
60
+ const panes = bySession.get(session) ?? [];
61
+ panes.push(dead);
62
+ bySession.set(session, panes);
63
+ if (paneId) byPaneId.set(paneId, dead);
64
+ }
65
+ } catch {
66
+ }
67
+ this.paneStatusCache = { ts: now, bySession, byPaneId };
68
+ return this.paneStatusCache;
69
+ }
70
+ getMySession() {
71
+ try {
72
+ return execFileSync("tmux", ["display-message", "-p", "#{session_name}"], QUIET).trim() || null;
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
77
+ listSessions() {
78
+ return this.listSessionsCached();
79
+ }
80
+ isAlive(target) {
81
+ try {
82
+ const paneStatus = this.listPaneStatusCached();
83
+ if (target.startsWith("%")) {
84
+ const dead = paneStatus.byPaneId.get(target);
85
+ return dead !== void 0 && dead !== "1";
86
+ }
87
+ const sessions = this.listSessionsCached();
88
+ if (!sessions.includes(target)) return false;
89
+ const paneDeaths = paneStatus.bySession.get(target);
90
+ if (!paneDeaths || paneDeaths.length === 0) return true;
91
+ return paneDeaths.some((dead) => dead !== "1");
92
+ } catch {
93
+ return false;
94
+ }
95
+ }
96
+ sendKeys(target, keys) {
97
+ execFileSync("tmux", ["send-keys", "-t", target, keys], QUIET);
98
+ execFileSync("tmux", ["send-keys", "-t", target, "Enter"], QUIET);
99
+ }
100
+ /**
101
+ * Send text as literal characters, then submit with Enter as a separate call.
102
+ * Fixes Codex intercom bug: long text + Enter in one send-keys blast causes
103
+ * Codex to drop the Enter. Splitting them ensures the text renders before
104
+ * the submit keystroke arrives.
105
+ *
106
+ * Runtime-aware (bug 21ab6018): for `runtime === "codex"`, the text is
107
+ * delivered first, then a deliberate `CODEX_SUBMIT_DELAY_MS` pause lets the
108
+ * Codex TUI commit the pasted input buffer, and only then is the submit Enter
109
+ * sent as a separate `C-m` keystroke. Without the pause the Enter is coalesced
110
+ * with the paste and dropped, stalling the review loop. Claude/OpenCode paths
111
+ * are unchanged (no delay) so they don't regress.
112
+ */
113
+ sendKeysLiteral(target, text, runtime) {
114
+ const isCodex = runtime === "codex";
115
+ if (text.length >= LONG_MESSAGE_THRESHOLD) {
116
+ const tmpDir = path.join(os.tmpdir(), "exe-os-intercom");
117
+ mkdirSync(tmpDir, { recursive: true });
118
+ const tmpFile = path.join(tmpDir, `msg-${randomBytes(8).toString("hex")}.txt`);
119
+ try {
120
+ writeFileSync(tmpFile, text, "utf-8");
121
+ execFileSync("tmux", ["load-buffer", tmpFile], QUIET);
122
+ execFileSync("tmux", ["paste-buffer", "-p", "-t", target], QUIET);
123
+ if (isCodex) sleepSync(CODEX_SUBMIT_DELAY_MS);
124
+ execFileSync("tmux", ["send-keys", "-t", target, isCodex ? "C-m" : "Enter"], QUIET);
125
+ } finally {
126
+ try {
127
+ unlinkSync(tmpFile);
128
+ } catch {
129
+ }
130
+ }
131
+ } else {
132
+ execFileSync("tmux", ["send-keys", "-t", target, "-l", text], QUIET);
133
+ if (isCodex) sleepSync(CODEX_SUBMIT_DELAY_MS);
134
+ execFileSync("tmux", ["send-keys", "-t", target, isCodex ? "C-m" : "Enter"], QUIET);
135
+ }
136
+ }
137
+ capturePane(target, lines) {
138
+ const args = ["capture-pane", "-t", target, "-p"];
139
+ if (lines) args.push("-S", `-${lines}`);
140
+ return execFileSync("tmux", args, QUIET);
141
+ }
142
+ isPaneInCopyMode(target) {
143
+ try {
144
+ const result = execFileSync(
145
+ "tmux",
146
+ ["display-message", "-p", "-t", target, "#{pane_in_mode}"],
147
+ QUIET
148
+ ).trim();
149
+ return result === "1";
150
+ } catch {
151
+ return false;
152
+ }
153
+ }
154
+ spawn(name, config) {
155
+ try {
156
+ const args = ["new-session", "-d", "-s", name, "-x", "200", "-y", "50"];
157
+ if (config.cwd) args.push("-c", config.cwd);
158
+ execFileSync("tmux", args);
159
+ execFileSync("tmux", ["send-keys", "-t", name, config.command, "Enter"]);
160
+ this.invalidateCaches();
161
+ return { sessionName: name };
162
+ } catch (e) {
163
+ return { sessionName: name, error: `spawn failed: ${e}` };
164
+ }
165
+ }
166
+ kill(target, callerScope) {
167
+ const resolvedCaller = this.resolveCallerScope(callerScope);
168
+ if (resolvedCaller && target.includes("-")) {
169
+ const targetScope = target.slice(target.lastIndexOf("-") + 1);
170
+ const cScope = resolvedCaller.includes("-") ? resolvedCaller.slice(resolvedCaller.lastIndexOf("-") + 1) : resolvedCaller;
171
+ if (targetScope !== cScope) {
172
+ process.stderr.write(
173
+ `[tmux-transport] BLOCKED kill: caller scope "${resolvedCaller}" tried to kill ${target} (scope ${targetScope} \u2260 ${cScope}). Session isolation enforced.
174
+ `
175
+ );
176
+ return;
177
+ }
178
+ }
179
+ try {
180
+ execFileSync("tmux", ["kill-session", "-t", target], QUIET);
181
+ } catch {
182
+ }
183
+ this.invalidateCaches();
184
+ }
185
+ /**
186
+ * Resolve the caller's coordinator scope for isolation enforcement.
187
+ * In the shared HTTP daemon, ambient tmux is meaningless — prefer the
188
+ * explicit/ALS/env scope. Falls back to ambient tmux for direct CLI callers.
189
+ */
190
+ resolveCallerScope(explicit) {
191
+ if (explicit) return explicit;
192
+ const isDaemon = process.env.EXE_IS_DAEMON === "1";
193
+ if (isDaemon) {
194
+ const alsHint = (() => {
195
+ try {
196
+ return getAgentContext()?.sessionHint || "";
197
+ } catch {
198
+ return "";
199
+ }
200
+ })();
201
+ const envHint = process.env.EXE_SESSION_NAME || process.env.EXE_SESSION || "";
202
+ const hint = alsHint || envHint;
203
+ if (hint) return hint;
204
+ return null;
205
+ }
206
+ return this.getMySession();
207
+ }
208
+ pipeLog(target, logFile) {
209
+ try {
210
+ const resolved = path.resolve(logFile);
211
+ const logsDir = path.join(os.homedir(), ".exe-os", "session-logs");
212
+ if (!resolved.startsWith(logsDir)) {
213
+ process.stderr.write(`[tmux-transport] pipeLog rejected: path escapes session-logs dir: ${logFile}
214
+ `);
215
+ return;
216
+ }
217
+ const safePath = logFile.replace(/'/g, "'\\''");
218
+ execFileSync("tmux", ["pipe-pane", "-t", target, `cat >> '${safePath}'`], QUIET);
219
+ } catch {
220
+ }
221
+ }
222
+ };
223
+
224
+ export {
225
+ TmuxTransport
226
+ };
@@ -0,0 +1,395 @@
1
+ import {
2
+ EXE_AI_DIR
3
+ } from "./chunk-R36FAN53.js";
4
+
5
+ // src/lib/db-backup.ts
6
+ import { existsSync, readdirSync, unlinkSync, statSync, statfsSync } from "fs";
7
+ import { copyFile, mkdir, unlink, rename } from "fs/promises";
8
+ import path from "path";
9
+ var BACKUP_DIR = path.join(EXE_AI_DIR, "backups");
10
+ var DEFAULT_KEEP_DAYS = 3;
11
+ var DB_NAMES = ["memories.db", "exe-mem.db", "exe-os.db", "exe.db"];
12
+ var REASON_COUNT_CAP = {
13
+ // Bug 727b7b1e: a daemon OOM crash loop made a ~1.5GB pre-restart backup on
14
+ // EVERY restart. 22 crashes = 11GB of pre-restart snapshots with no rotation.
15
+ // Keep only the 2 newest — enough to recover, small enough not to fill disk.
16
+ "pre-restart": 2,
17
+ "pre-consolidation": 5,
18
+ "pre-fix": 5,
19
+ "pre-restore": 5,
20
+ // Bug 91b1b167: daily snapshots are full uncompressed SQLCipher copies
21
+ // (~2-3GB apiece on large enterprise DBs). Time-based rotation alone left
22
+ // 19GB accumulating when mtimes drifted / the disk guard tripped. A hard
23
+ // count cap is a belt-and-braces safety net on top of day-based rotation.
24
+ "daily": 3
25
+ };
26
+ var MIN_FREE_BYTES = 5 * 1024 * 1024 * 1024;
27
+ var STALE_TMP_MAX_AGE_MS = 60 * 60 * 1e3;
28
+ function findActiveDb() {
29
+ for (const name of DB_NAMES) {
30
+ const p = path.join(EXE_AI_DIR, name);
31
+ if (existsSync(p)) return p;
32
+ }
33
+ return null;
34
+ }
35
+ async function createBackup(reason = "manual") {
36
+ return createBackupAsync(reason);
37
+ }
38
+ async function createBackupAsync(reason = "manual", opts = {}) {
39
+ const dbPath = findActiveDb();
40
+ if (!dbPath) return null;
41
+ await mkdir(BACKUP_DIR, { recursive: true });
42
+ try {
43
+ let dbSize = 0;
44
+ try {
45
+ dbSize = statSync(dbPath).size;
46
+ } catch {
47
+ }
48
+ const freeBytes = getFreeBytes(BACKUP_DIR);
49
+ if (!hasEnoughDiskSpace(freeBytes, dbSize)) {
50
+ process.stderr.write(
51
+ `[db-backup] DISK GUARD: skipping "${reason}" backup \u2014 ${Math.round(freeBytes / 1024 / 1024)}MB free, need >${Math.round(Math.max(MIN_FREE_BYTES, dbSize * 2) / 1024 / 1024)}MB. Disk is nearly full; refusing to make it worse.
52
+ `
53
+ );
54
+ return null;
55
+ }
56
+ } catch {
57
+ }
58
+ const dbName = path.basename(dbPath, ".db");
59
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
60
+ const backupName = `${dbName}-${reason}-${timestamp}.db`;
61
+ const backupPath = path.join(BACKUP_DIR, backupName);
62
+ const tmpBackupPath = `${backupPath}.tmp`;
63
+ const checkpointMode = opts.checkpointMode ?? "TRUNCATE";
64
+ try {
65
+ const { getClient } = await import("./lib/database.js");
66
+ const client = getClient();
67
+ await client.execute(`PRAGMA wal_checkpoint(${checkpointMode})`);
68
+ } catch {
69
+ try {
70
+ const { execFile: execFileCb } = await import("child_process");
71
+ const { promisify } = await import("util");
72
+ const execFileAsync = promisify(execFileCb);
73
+ await execFileAsync("sqlite3", [dbPath, `PRAGMA wal_checkpoint(${checkpointMode});`], { timeout: 3e4 });
74
+ } catch {
75
+ process.stderr.write(`[db-backup] WAL checkpoint skipped \u2014 proceeding with copy (backup may include stale WAL)
76
+ `);
77
+ }
78
+ }
79
+ try {
80
+ await unlink(tmpBackupPath);
81
+ } catch {
82
+ }
83
+ await copyFile(dbPath, tmpBackupPath);
84
+ await rename(tmpBackupPath, backupPath);
85
+ try {
86
+ const { getClient } = await import("./lib/database.js");
87
+ const client = getClient();
88
+ const chk = await client.execute("PRAGMA quick_check;");
89
+ const result = String(chk.rows[0]?.quick_check ?? chk.rows[0]?.[0] ?? "").trim();
90
+ if (result && result !== "ok") {
91
+ process.stderr.write(`[db-backup] WARNING: backup source failed quick_check (${reason}): ${result}
92
+ `);
93
+ try {
94
+ await unlink(backupPath);
95
+ } catch {
96
+ }
97
+ return null;
98
+ }
99
+ } catch {
100
+ process.stderr.write(`[db-backup] Backup validation skipped (encrypted DB or CLI context) \u2014 proceeding with checkpoint-consistent copy.
101
+ `);
102
+ }
103
+ const walPath = dbPath + "-wal";
104
+ if (existsSync(walPath)) {
105
+ try {
106
+ await copyFile(walPath, backupPath + "-wal.tmp");
107
+ await rename(backupPath + "-wal.tmp", backupPath + "-wal");
108
+ } catch {
109
+ }
110
+ }
111
+ const shmPath = dbPath + "-shm";
112
+ if (existsSync(shmPath)) {
113
+ try {
114
+ await copyFile(shmPath, backupPath + "-shm.tmp");
115
+ await rename(backupPath + "-shm.tmp", backupPath + "-shm");
116
+ } catch {
117
+ }
118
+ }
119
+ return backupPath;
120
+ }
121
+ function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
122
+ if (!existsSync(BACKUP_DIR)) return 0;
123
+ const cutoff = Date.now() - keepDays * 24 * 60 * 60 * 1e3;
124
+ let deleted = 0;
125
+ try {
126
+ const files = readdirSync(BACKUP_DIR);
127
+ for (const file of files) {
128
+ if (file.endsWith(".tmp")) continue;
129
+ if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
130
+ const filePath = path.join(BACKUP_DIR, file);
131
+ try {
132
+ const stat = statSync(filePath);
133
+ if (stat.mtimeMs < cutoff) {
134
+ unlinkSync(filePath);
135
+ deleted++;
136
+ }
137
+ } catch {
138
+ }
139
+ }
140
+ } catch {
141
+ }
142
+ return deleted;
143
+ }
144
+ function selectBackupsToDelete(backups, keep) {
145
+ if (keep < 0) keep = 0;
146
+ const sorted = [...backups].sort((a, b) => b.mtimeMs - a.mtimeMs);
147
+ return sorted.slice(keep).map((b) => b.path);
148
+ }
149
+ function selectStaleTmpFiles(tmpFiles, cutoffMs) {
150
+ return tmpFiles.filter((f) => f.mtimeMs < cutoffMs).map((f) => f.path);
151
+ }
152
+ function cleanupStaleTmp(maxAgeMs = STALE_TMP_MAX_AGE_MS) {
153
+ if (!existsSync(BACKUP_DIR)) return 0;
154
+ const cutoffMs = Date.now() - maxAgeMs;
155
+ let deleted = 0;
156
+ try {
157
+ const candidates = [];
158
+ for (const file of readdirSync(BACKUP_DIR)) {
159
+ if (!file.endsWith(".tmp")) continue;
160
+ const filePath = path.join(BACKUP_DIR, file);
161
+ try {
162
+ candidates.push({ path: filePath, mtimeMs: statSync(filePath).mtimeMs });
163
+ } catch {
164
+ }
165
+ }
166
+ for (const target of selectStaleTmpFiles(candidates, cutoffMs)) {
167
+ try {
168
+ unlinkSync(target);
169
+ deleted++;
170
+ } catch {
171
+ }
172
+ }
173
+ } catch {
174
+ }
175
+ return deleted;
176
+ }
177
+ function hasEnoughDiskSpace(freeBytes, backupSizeBytes, minFreeBytes = MIN_FREE_BYTES) {
178
+ const required = Math.max(minFreeBytes, backupSizeBytes * 2);
179
+ return freeBytes >= required;
180
+ }
181
+ function getFreeBytes(dir) {
182
+ try {
183
+ const s = statfsSync(dir);
184
+ return s.bavail * s.bsize;
185
+ } catch {
186
+ return Number.POSITIVE_INFINITY;
187
+ }
188
+ }
189
+ async function quickCheckBackupSource(source) {
190
+ try {
191
+ const { execFile: execFileCb } = await import("child_process");
192
+ const { promisify } = await import("util");
193
+ const execFileAsync = promisify(execFileCb);
194
+ const { stdout } = await execFileAsync("sqlite3", [source, "PRAGMA quick_check;"], { timeout: 3e4 });
195
+ const result = String(stdout ?? "").trim();
196
+ if (result !== "ok") {
197
+ throw new Error(result || "quick_check returned no result");
198
+ }
199
+ } catch (err) {
200
+ const msg = err instanceof Error ? err.message : String(err);
201
+ if (/file is not a database|not an error/i.test(msg)) {
202
+ process.stderr.write(`[db-backup] Backup source quick_check unavailable for encrypted backup (${path.basename(source)}); proceeding with atomic restore.
203
+ `);
204
+ return;
205
+ }
206
+ throw new Error(`Backup source failed quick_check: ${msg}`);
207
+ }
208
+ }
209
+ function rotateBackupsByReason(reason, keep) {
210
+ if (!existsSync(BACKUP_DIR)) return 0;
211
+ let deleted = 0;
212
+ try {
213
+ const files = readdirSync(BACKUP_DIR);
214
+ const matches = [];
215
+ for (const file of files) {
216
+ if (file.endsWith(".tmp")) continue;
217
+ if (!file.endsWith(".db")) continue;
218
+ if (!file.includes(`-${reason}-`)) continue;
219
+ const filePath = path.join(BACKUP_DIR, file);
220
+ try {
221
+ matches.push({ path: filePath, mtimeMs: statSync(filePath).mtimeMs });
222
+ } catch {
223
+ }
224
+ }
225
+ for (const target of selectBackupsToDelete(matches, keep)) {
226
+ for (const p of [target, target + "-wal", target + "-shm"]) {
227
+ try {
228
+ if (existsSync(p)) {
229
+ unlinkSync(p);
230
+ if (p === target) deleted++;
231
+ }
232
+ } catch {
233
+ }
234
+ }
235
+ }
236
+ } catch {
237
+ }
238
+ return deleted;
239
+ }
240
+ function enforceRetention(reason, keepDays = DEFAULT_KEEP_DAYS) {
241
+ let deleted = rotateBackups(keepDays);
242
+ const cap = REASON_COUNT_CAP[reason];
243
+ if (typeof cap === "number") {
244
+ deleted += rotateBackupsByReason(reason, cap);
245
+ }
246
+ deleted += cleanupStaleTmp();
247
+ return deleted;
248
+ }
249
+ var QUARANTINE_KEEP = 2;
250
+ function pruneCorruptQuarantine(keep = QUARANTINE_KEEP) {
251
+ if (!existsSync(EXE_AI_DIR)) return 0;
252
+ let deleted = 0;
253
+ try {
254
+ const matches = [];
255
+ for (const file of readdirSync(EXE_AI_DIR)) {
256
+ if (!/\.corrupt-\d+$/.test(file)) continue;
257
+ const filePath = path.join(EXE_AI_DIR, file);
258
+ try {
259
+ matches.push({ path: filePath, mtimeMs: statSync(filePath).mtimeMs });
260
+ } catch {
261
+ }
262
+ }
263
+ for (const target of selectBackupsToDelete(matches, keep)) {
264
+ for (const p of [target, target + "-wal", target + "-shm"]) {
265
+ try {
266
+ if (existsSync(p)) {
267
+ unlinkSync(p);
268
+ if (p === target) deleted++;
269
+ }
270
+ } catch {
271
+ }
272
+ }
273
+ }
274
+ } catch {
275
+ }
276
+ return deleted;
277
+ }
278
+ function cleanupCrashDebris() {
279
+ let deleted = 0;
280
+ deleted += rotateBackupsByReason("pre-restart", REASON_COUNT_CAP["pre-restart"] ?? 2);
281
+ deleted += cleanupStaleTmp();
282
+ deleted += pruneCorruptQuarantine();
283
+ return deleted;
284
+ }
285
+ function listBackups() {
286
+ if (!existsSync(BACKUP_DIR)) return [];
287
+ try {
288
+ const files = readdirSync(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith(".tmp") && !f.endsWith("-wal") && !f.endsWith("-shm"));
289
+ return files.map((name) => {
290
+ const p = path.join(BACKUP_DIR, name);
291
+ const stat = statSync(p);
292
+ return { path: p, name, size: stat.size, date: stat.mtime };
293
+ }).sort((a, b) => b.date.getTime() - a.date.getTime());
294
+ } catch {
295
+ return [];
296
+ }
297
+ }
298
+ function hasBackupToday(reason) {
299
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
300
+ const backups = listBackups();
301
+ return backups.some((b) => b.name.includes(reason) && b.name.includes(today.replace(/-/g, "-")));
302
+ }
303
+ function hasRecentBackup(reason, withinMs) {
304
+ try {
305
+ if (!existsSync(BACKUP_DIR)) return false;
306
+ const now = Date.now();
307
+ for (const file of readdirSync(BACKUP_DIR)) {
308
+ if (file.endsWith(".tmp")) continue;
309
+ if (!file.endsWith(".db")) continue;
310
+ if (!file.includes(`-${reason}-`)) continue;
311
+ try {
312
+ if (now - statSync(path.join(BACKUP_DIR, file)).mtimeMs <= withinMs) return true;
313
+ } catch {
314
+ }
315
+ }
316
+ } catch {
317
+ }
318
+ return false;
319
+ }
320
+ function getLatestBackup() {
321
+ const backups = listBackups();
322
+ return backups.length > 0 ? backups[0].path : null;
323
+ }
324
+ async function restoreBackup(backupPath) {
325
+ const dbPath = findActiveDb();
326
+ if (!dbPath) {
327
+ const target = path.join(EXE_AI_DIR, DB_NAMES[0]);
328
+ const source2 = backupPath ?? getLatestBackup();
329
+ if (!source2 || !existsSync(source2)) return null;
330
+ await copyFile(source2, target);
331
+ return { restored: path.basename(source2), preRestoreBackup: null };
332
+ }
333
+ const source = backupPath ?? getLatestBackup();
334
+ if (!source || !existsSync(source)) return null;
335
+ if (path.resolve(source) === path.resolve(dbPath)) return null;
336
+ await quickCheckBackupSource(source);
337
+ const preRestoreBackup = await createBackup("pre-restore");
338
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
339
+ const tmpDb = `${dbPath}.restore-${stamp}.tmp`;
340
+ const oldDb = `${dbPath}.pre-restore-active-${stamp}`;
341
+ await copyFile(source, tmpDb);
342
+ const walPath = source + "-wal";
343
+ const shmPath = source + "-shm";
344
+ const tmpWal = `${tmpDb}-wal`;
345
+ const tmpShm = `${tmpDb}-shm`;
346
+ if (existsSync(walPath)) {
347
+ try {
348
+ await copyFile(walPath, tmpWal);
349
+ } catch {
350
+ }
351
+ }
352
+ if (existsSync(shmPath)) {
353
+ try {
354
+ await copyFile(shmPath, tmpShm);
355
+ } catch {
356
+ }
357
+ }
358
+ try {
359
+ unlinkSync(dbPath + "-wal");
360
+ } catch {
361
+ }
362
+ try {
363
+ unlinkSync(dbPath + "-shm");
364
+ } catch {
365
+ }
366
+ await rename(dbPath, oldDb);
367
+ await rename(tmpDb, dbPath);
368
+ if (existsSync(tmpWal)) await rename(tmpWal, dbPath + "-wal");
369
+ if (existsSync(tmpShm)) await rename(tmpShm, dbPath + "-shm");
370
+ return { restored: path.basename(source), preRestoreBackup };
371
+ }
372
+ function getBackupDir() {
373
+ return BACKUP_DIR;
374
+ }
375
+
376
+ export {
377
+ findActiveDb,
378
+ createBackup,
379
+ createBackupAsync,
380
+ rotateBackups,
381
+ selectBackupsToDelete,
382
+ selectStaleTmpFiles,
383
+ cleanupStaleTmp,
384
+ hasEnoughDiskSpace,
385
+ rotateBackupsByReason,
386
+ enforceRetention,
387
+ pruneCorruptQuarantine,
388
+ cleanupCrashDebris,
389
+ listBackups,
390
+ hasBackupToday,
391
+ hasRecentBackup,
392
+ getLatestBackup,
393
+ restoreBackup,
394
+ getBackupDir
395
+ };