@askexenow/exe-os 0.8.37 → 0.8.39

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 (93) hide show
  1. package/README.md +17 -8
  2. package/dist/bin/backfill-conversations.js +112 -70
  3. package/dist/bin/backfill-responses.js +53 -18
  4. package/dist/bin/backfill-vectors.js +43 -16
  5. package/dist/bin/cleanup-stale-review-tasks.js +38 -16
  6. package/dist/bin/cli.js +790 -468
  7. package/dist/bin/exe-agent.js +19 -4
  8. package/dist/bin/exe-assign.js +46 -13
  9. package/dist/bin/exe-boot.js +288 -129
  10. package/dist/bin/exe-call.js +20 -10
  11. package/dist/bin/exe-cloud.js +135 -30
  12. package/dist/bin/exe-dispatch.js +1 -1
  13. package/dist/bin/exe-doctor.js +38 -16
  14. package/dist/bin/exe-export-behaviors.js +43 -21
  15. package/dist/bin/exe-forget.js +39 -17
  16. package/dist/bin/exe-gateway.js +159 -50
  17. package/dist/bin/exe-heartbeat.js +53 -31
  18. package/dist/bin/exe-kill.js +40 -18
  19. package/dist/bin/exe-launch-agent.js +109 -36
  20. package/dist/bin/exe-link.js +196 -87
  21. package/dist/bin/exe-new-employee.js +56 -17
  22. package/dist/bin/exe-pending-messages.js +47 -25
  23. package/dist/bin/exe-pending-notifications.js +38 -16
  24. package/dist/bin/exe-pending-reviews.js +51 -29
  25. package/dist/bin/exe-rename.js +21 -7
  26. package/dist/bin/exe-review.js +41 -13
  27. package/dist/bin/exe-search.js +57 -21
  28. package/dist/bin/exe-session-cleanup.js +67 -31
  29. package/dist/bin/exe-settings.js +63 -2
  30. package/dist/bin/exe-status.js +35 -13
  31. package/dist/bin/exe-team.js +35 -13
  32. package/dist/bin/git-sweep.js +45 -17
  33. package/dist/bin/graph-backfill.js +38 -16
  34. package/dist/bin/graph-export.js +38 -16
  35. package/dist/bin/install.js +10 -1
  36. package/dist/bin/scan-tasks.js +47 -19
  37. package/dist/bin/setup.js +444 -259
  38. package/dist/bin/shard-migrate.js +38 -16
  39. package/dist/bin/wiki-sync.js +40 -17
  40. package/dist/gateway/index.js +113 -48
  41. package/dist/hooks/bug-report-worker.js +66 -39
  42. package/dist/hooks/commit-complete.js +45 -17
  43. package/dist/hooks/error-recall.js +60 -20
  44. package/dist/hooks/exe-heartbeat-hook.js +3 -2
  45. package/dist/hooks/ingest-worker.js +174 -45
  46. package/dist/hooks/ingest.js +74 -28
  47. package/dist/hooks/instructions-loaded.js +46 -17
  48. package/dist/hooks/notification.js +44 -15
  49. package/dist/hooks/post-compact.js +44 -15
  50. package/dist/hooks/pre-compact.js +42 -14
  51. package/dist/hooks/pre-tool-use.js +59 -22
  52. package/dist/hooks/prompt-ingest-worker.js +75 -14
  53. package/dist/hooks/prompt-submit.js +75 -32
  54. package/dist/hooks/response-ingest-worker.js +76 -15
  55. package/dist/hooks/session-end.js +54 -22
  56. package/dist/hooks/session-start.js +57 -20
  57. package/dist/hooks/stop.js +44 -15
  58. package/dist/hooks/subagent-stop.js +44 -15
  59. package/dist/hooks/summary-worker.js +339 -106
  60. package/dist/index.js +94 -23
  61. package/dist/lib/cloud-sync.js +191 -80
  62. package/dist/lib/config.js +4 -1
  63. package/dist/lib/consolidation.js +5 -4
  64. package/dist/lib/database.js +1 -0
  65. package/dist/lib/device-registry.js +2 -1
  66. package/dist/lib/embedder.js +9 -1
  67. package/dist/lib/employee-templates.js +5 -0
  68. package/dist/lib/employees.js +11 -6
  69. package/dist/lib/exe-daemon-client.js +6 -1
  70. package/dist/lib/exe-daemon.js +95 -36
  71. package/dist/lib/hybrid-search.js +57 -21
  72. package/dist/lib/identity-templates.js +16 -7
  73. package/dist/lib/identity.js +1 -1
  74. package/dist/lib/keychain.js +2 -1
  75. package/dist/lib/license.js +56 -6
  76. package/dist/lib/messaging.js +1 -1
  77. package/dist/lib/reminders.js +2 -2
  78. package/dist/lib/schedules.js +38 -16
  79. package/dist/lib/skill-learning.js +1 -1
  80. package/dist/lib/store.js +44 -16
  81. package/dist/lib/tasks.js +1 -1
  82. package/dist/lib/tmux-routing.js +1 -1
  83. package/dist/mcp/server.js +280 -155
  84. package/dist/mcp/tools/complete-reminder.js +1 -1
  85. package/dist/mcp/tools/create-task.js +14 -6
  86. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  87. package/dist/mcp/tools/list-reminders.js +1 -1
  88. package/dist/mcp/tools/list-tasks.js +36 -28
  89. package/dist/mcp/tools/send-message.js +1 -1
  90. package/dist/mcp/tools/update-task.js +1 -1
  91. package/dist/runtime/index.js +42 -8
  92. package/dist/tui/App.js +220 -99
  93. package/package.json +5 -3
@@ -79,6 +79,17 @@ var init_db_retry = __esm({
79
79
  });
80
80
 
81
81
  // src/lib/database.ts
82
+ var database_exports = {};
83
+ __export(database_exports, {
84
+ disposeDatabase: () => disposeDatabase,
85
+ disposeTurso: () => disposeTurso,
86
+ ensureSchema: () => ensureSchema,
87
+ getClient: () => getClient,
88
+ getRawClient: () => getRawClient,
89
+ initDatabase: () => initDatabase,
90
+ initTurso: () => initTurso,
91
+ isInitialized: () => isInitialized
92
+ });
82
93
  import { createClient } from "@libsql/client";
83
94
  async function initDatabase(config) {
84
95
  if (_client) {
@@ -114,6 +125,7 @@ async function ensureSchema() {
114
125
  const client = getRawClient();
115
126
  await client.execute("PRAGMA journal_mode = WAL");
116
127
  await client.execute("PRAGMA busy_timeout = 30000");
128
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
117
129
  try {
118
130
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
119
131
  } catch {
@@ -902,7 +914,14 @@ async function ensureSchema() {
902
914
  }
903
915
  }
904
916
  }
905
- var _client, _resilientClient, initTurso;
917
+ async function disposeDatabase() {
918
+ if (_client) {
919
+ _client.close();
920
+ _client = null;
921
+ _resilientClient = null;
922
+ }
923
+ }
924
+ var _client, _resilientClient, initTurso, disposeTurso;
906
925
  var init_database = __esm({
907
926
  "src/lib/database.ts"() {
908
927
  "use strict";
@@ -910,6 +929,7 @@ var init_database = __esm({
910
929
  _client = null;
911
930
  _resilientClient = null;
912
931
  initTurso = initDatabase;
932
+ disposeTurso = disposeDatabase;
913
933
  }
914
934
  });
915
935
 
@@ -925,9 +945,10 @@ __export(keychain_exports, {
925
945
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
926
946
  import { existsSync } from "fs";
927
947
  import path from "path";
948
+ import os from "os";
928
949
  import crypto from "crypto";
929
950
  function getKeyDir() {
930
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
951
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
931
952
  }
932
953
  function getKeyPath() {
933
954
  return path.join(getKeyDir(), "master.key");
@@ -1075,15 +1096,15 @@ __export(config_exports, {
1075
1096
  migrateConfig: () => migrateConfig,
1076
1097
  saveConfig: () => saveConfig
1077
1098
  });
1078
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1099
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
1079
1100
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
1080
1101
  import path2 from "path";
1081
- import os from "os";
1102
+ import os2 from "os";
1082
1103
  function resolveDataDir() {
1083
1104
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
1084
1105
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
1085
- const newDir = path2.join(os.homedir(), ".exe-os");
1086
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
1106
+ const newDir = path2.join(os2.homedir(), ".exe-os");
1107
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
1087
1108
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
1088
1109
  try {
1089
1110
  renameSync(legacyDir, newDir);
@@ -1170,7 +1191,7 @@ async function loadConfig() {
1170
1191
  normalizeAutoUpdate(migratedCfg);
1171
1192
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
1172
1193
  if (config.dbPath.startsWith("~")) {
1173
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
1194
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
1174
1195
  }
1175
1196
  return config;
1176
1197
  } catch {
@@ -1201,6 +1222,9 @@ async function saveConfig(config) {
1201
1222
  await mkdir2(dir, { recursive: true });
1202
1223
  const configPath = path2.join(dir, "config.json");
1203
1224
  await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
1225
+ if (config.cloud?.apiKey) {
1226
+ await chmod2(configPath, 384);
1227
+ }
1204
1228
  }
1205
1229
  async function loadConfigFrom(configPath) {
1206
1230
  const raw = await readFile2(configPath, "utf-8");
@@ -1316,7 +1340,7 @@ __export(shard_manager_exports, {
1316
1340
  shardExists: () => shardExists
1317
1341
  });
1318
1342
  import path3 from "path";
1319
- import { existsSync as existsSync3, mkdirSync } from "fs";
1343
+ import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
1320
1344
  import { createClient as createClient2 } from "@libsql/client";
1321
1345
  function initShardManager(encryptionKey) {
1322
1346
  _encryptionKey = encryptionKey;
@@ -1355,8 +1379,7 @@ function shardExists(projectName) {
1355
1379
  }
1356
1380
  function listShards() {
1357
1381
  if (!existsSync3(SHARDS_DIR)) return [];
1358
- const { readdirSync: readdirSync3 } = __require("fs");
1359
- return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1382
+ return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1360
1383
  }
1361
1384
  async function ensureShardSchema(client) {
1362
1385
  await client.execute("PRAGMA journal_mode = WAL");
@@ -1564,15 +1587,20 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
1564
1587
  await mkdir3(path5.dirname(employeesPath), { recursive: true });
1565
1588
  await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
1566
1589
  }
1590
+ function findExeBin() {
1591
+ try {
1592
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
1593
+ } catch {
1594
+ return null;
1595
+ }
1596
+ }
1567
1597
  function registerBinSymlinks(name) {
1568
1598
  const created = [];
1569
1599
  const skipped = [];
1570
1600
  const errors = [];
1571
- let exeBinPath;
1572
- try {
1573
- exeBinPath = execSync("which exe", { encoding: "utf-8" }).trim();
1574
- } catch {
1575
- errors.push("Could not find 'exe' in PATH");
1601
+ const exeBinPath = findExeBin();
1602
+ if (!exeBinPath) {
1603
+ errors.push("Could not find 'exe-os' in PATH");
1576
1604
  return { created, skipped, errors };
1577
1605
  }
1578
1606
  const binDir = path5.dirname(exeBinPath);
@@ -1613,6 +1641,14 @@ import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync6
1613
1641
  import { randomUUID } from "crypto";
1614
1642
  import path6 from "path";
1615
1643
  import { jwtVerify, importSPKI } from "jose";
1644
+ async function fetchRetry(url, init) {
1645
+ try {
1646
+ return await fetch(url, init);
1647
+ } catch {
1648
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
1649
+ return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
1650
+ }
1651
+ }
1616
1652
  function loadDeviceId() {
1617
1653
  const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
1618
1654
  try {
@@ -1683,7 +1719,7 @@ function cacheResponse(token) {
1683
1719
  async function validateLicense(apiKey, deviceId) {
1684
1720
  const did = deviceId ?? loadDeviceId();
1685
1721
  try {
1686
- const res = await fetch(`${API_BASE}/auth/activate`, {
1722
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
1687
1723
  method: "POST",
1688
1724
  headers: { "Content-Type": "application/json" },
1689
1725
  body: JSON.stringify({ apiKey, deviceId: did }),
@@ -1718,14 +1754,23 @@ async function validateLicense(apiKey, deviceId) {
1718
1754
  } catch {
1719
1755
  const cached = await getCachedLicense();
1720
1756
  if (cached) return cached;
1721
- return FREE_LICENSE;
1757
+ return { ...FREE_LICENSE, valid: false, error: "offline" };
1758
+ }
1759
+ }
1760
+ function getCacheAgeMs() {
1761
+ try {
1762
+ const { statSync: statSync2 } = __require("fs");
1763
+ const s = statSync2(CACHE_PATH);
1764
+ return Date.now() - s.mtimeMs;
1765
+ } catch {
1766
+ return Infinity;
1722
1767
  }
1723
1768
  }
1724
1769
  async function checkLicense() {
1725
1770
  const key = loadLicense();
1726
1771
  if (!key) return FREE_LICENSE;
1727
1772
  const cached = await getCachedLicense();
1728
- if (cached) return cached;
1773
+ if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
1729
1774
  const deviceId = loadDeviceId();
1730
1775
  return validateLicense(key, deviceId);
1731
1776
  }
@@ -1739,7 +1784,7 @@ function isFeatureAllowed(license, feature) {
1739
1784
  return license.plan === "team" || license.plan === "agency" || license.plan === "enterprise";
1740
1785
  }
1741
1786
  }
1742
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE;
1787
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS;
1743
1788
  var init_license = __esm({
1744
1789
  "src/lib/license.ts"() {
1745
1790
  "use strict";
@@ -1748,6 +1793,7 @@ var init_license = __esm({
1748
1793
  CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
1749
1794
  DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
1750
1795
  API_BASE = "https://askexe.com/cloud";
1796
+ RETRY_DELAY_MS = 500;
1751
1797
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
1752
1798
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
1753
1799
  4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
@@ -1769,6 +1815,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
1769
1815
  employeeLimit: 1,
1770
1816
  memoryLimit: 5e3
1771
1817
  };
1818
+ CACHE_MAX_AGE_MS = 36e5;
1772
1819
  }
1773
1820
  });
1774
1821
 
@@ -1906,6 +1953,10 @@ import path8 from "path";
1906
1953
  import { fileURLToPath } from "url";
1907
1954
  function handleData(chunk) {
1908
1955
  _buffer += chunk.toString();
1956
+ if (_buffer.length > MAX_BUFFER) {
1957
+ _buffer = "";
1958
+ return;
1959
+ }
1909
1960
  let newlineIdx;
1910
1961
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1911
1962
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -2213,7 +2264,7 @@ function disconnectClient() {
2213
2264
  entry.resolve({ error: "Client disconnected" });
2214
2265
  }
2215
2266
  }
2216
- var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending;
2267
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, _socket, _connected, _buffer, _requestCount, HEALTH_CHECK_INTERVAL, _pending, MAX_BUFFER;
2217
2268
  var init_exe_daemon_client = __esm({
2218
2269
  "src/lib/exe-daemon-client.ts"() {
2219
2270
  "use strict";
@@ -2230,6 +2281,7 @@ var init_exe_daemon_client = __esm({
2230
2281
  _requestCount = 0;
2231
2282
  HEALTH_CHECK_INTERVAL = 100;
2232
2283
  _pending = /* @__PURE__ */ new Map();
2284
+ MAX_BUFFER = 1e7;
2233
2285
  }
2234
2286
  });
2235
2287
 
@@ -2271,8 +2323,8 @@ async function embedDirect(text) {
2271
2323
  const llamaCpp = await import("node-llama-cpp");
2272
2324
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2273
2325
  const { existsSync: existsSync11 } = await import("fs");
2274
- const path11 = await import("path");
2275
- const modelPath = path11.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
2326
+ const path12 = await import("path");
2327
+ const modelPath = path12.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
2276
2328
  if (!existsSync11(modelPath)) {
2277
2329
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
2278
2330
  }
@@ -2383,6 +2435,7 @@ var init_compress = __esm({
2383
2435
  // src/lib/cloud-sync.ts
2384
2436
  var cloud_sync_exports = {};
2385
2437
  __export(cloud_sync_exports, {
2438
+ assertSecureEndpoint: () => assertSecureEndpoint,
2386
2439
  buildRosterBlob: () => buildRosterBlob,
2387
2440
  cloudPull: () => cloudPull,
2388
2441
  cloudPullBehaviors: () => cloudPullBehaviors,
@@ -2402,9 +2455,10 @@ __export(cloud_sync_exports, {
2402
2455
  cloudPushTasks: () => cloudPushTasks,
2403
2456
  cloudSync: () => cloudSync,
2404
2457
  mergeConfig: () => mergeConfig,
2405
- mergeRosterFromRemote: () => mergeRosterFromRemote
2458
+ mergeRosterFromRemote: () => mergeRosterFromRemote,
2459
+ recordRosterDeletion: () => recordRosterDeletion
2406
2460
  });
2407
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync9, readdirSync as readdirSync2, mkdirSync as mkdirSync3, appendFileSync } from "fs";
2461
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync2, existsSync as existsSync9, readdirSync as readdirSync3, mkdirSync as mkdirSync3, appendFileSync, unlinkSync as unlinkSync3 } from "fs";
2408
2462
  import path9 from "path";
2409
2463
  import { homedir } from "os";
2410
2464
  function logError(msg) {
@@ -2415,16 +2469,47 @@ function logError(msg) {
2415
2469
  } catch {
2416
2470
  }
2417
2471
  }
2472
+ async function withRosterLock(fn) {
2473
+ if (existsSync9(ROSTER_LOCK_PATH)) {
2474
+ try {
2475
+ const ts = parseInt(readFileSync7(ROSTER_LOCK_PATH, "utf-8"), 10);
2476
+ if (Date.now() - ts < LOCK_STALE_MS) {
2477
+ throw new Error("Roster merge already in progress \u2014 another sync is running");
2478
+ }
2479
+ } catch (err) {
2480
+ if (err instanceof Error && err.message.includes("already in progress")) throw err;
2481
+ }
2482
+ }
2483
+ writeFileSync2(ROSTER_LOCK_PATH, String(Date.now()));
2484
+ try {
2485
+ return await fn();
2486
+ } finally {
2487
+ try {
2488
+ unlinkSync3(ROSTER_LOCK_PATH);
2489
+ } catch {
2490
+ }
2491
+ }
2492
+ }
2418
2493
  async function fetchWithRetry(url, init) {
2419
- const attempt = async () => {
2420
- const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
2421
- return fetch(url, { ...init, signal });
2422
- };
2423
- const resp = await attempt();
2424
- if (resp.status >= 500) {
2425
- return attempt();
2494
+ const MAX_RETRIES2 = 3;
2495
+ const BASE_DELAY_MS2 = 200;
2496
+ let lastError;
2497
+ for (let attempt = 0; attempt <= MAX_RETRIES2; attempt++) {
2498
+ try {
2499
+ const signal = AbortSignal.timeout(FETCH_TIMEOUT_MS);
2500
+ const resp = await fetch(url, { ...init, signal });
2501
+ if (resp.status >= 500 && attempt < MAX_RETRIES2) {
2502
+ await new Promise((r) => setTimeout(r, BASE_DELAY_MS2 * Math.pow(2, attempt)));
2503
+ continue;
2504
+ }
2505
+ return resp;
2506
+ } catch (err) {
2507
+ lastError = err;
2508
+ if (attempt === MAX_RETRIES2) throw err;
2509
+ await new Promise((r) => setTimeout(r, BASE_DELAY_MS2 * Math.pow(2, attempt)));
2510
+ }
2426
2511
  }
2427
- return resp;
2512
+ throw lastError;
2428
2513
  }
2429
2514
  function assertSecureEndpoint(endpoint) {
2430
2515
  if (endpoint.startsWith("https://")) return;
@@ -2452,10 +2537,15 @@ async function cloudPush(records, maxVersion, config) {
2452
2537
  headers: {
2453
2538
  Authorization: `Bearer ${config.apiKey}`,
2454
2539
  "Content-Type": "application/json",
2455
- "X-Device-Id": loadDeviceId()
2540
+ "X-Device-Id": loadDeviceId(),
2541
+ "X-Expected-Version": String(maxVersion)
2456
2542
  },
2457
2543
  body: JSON.stringify({ version: maxVersion, blob })
2458
2544
  });
2545
+ if (resp.status === 409) {
2546
+ logError("[cloud-sync] PUSH VERSION CONFLICT \u2014 re-pull required before next push");
2547
+ return false;
2548
+ }
2459
2549
  return resp.ok;
2460
2550
  } catch (err) {
2461
2551
  logError(`[cloud-sync] PUSH FAILED: ${err instanceof Error ? err.message : String(err)}`);
@@ -2542,18 +2632,21 @@ async function cloudSync(config) {
2542
2632
  "SELECT value FROM sync_meta WHERE key = 'last_cloud_push_version'"
2543
2633
  );
2544
2634
  const lastPushVersion = pushMeta.rows.length > 0 ? Number(pushMeta.rows[0].value) : 0;
2545
- const recordsResult = await client.execute({
2546
- sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
2547
- tool_name, project_name, has_error, raw_text, version,
2548
- author_device_id, scope
2549
- FROM memories
2550
- WHERE version > ?
2551
- AND (scope IS NULL OR scope != 'personal')
2552
- ORDER BY version ASC`,
2553
- args: [lastPushVersion]
2554
- });
2555
2635
  let pushed = 0;
2556
- if (recordsResult.rows.length > 0) {
2636
+ let batchCursor = lastPushVersion;
2637
+ while (true) {
2638
+ const recordsResult = await client.execute({
2639
+ sql: `SELECT id, agent_id, agent_role, session_id, timestamp,
2640
+ tool_name, project_name, has_error, raw_text, version,
2641
+ author_device_id, scope
2642
+ FROM memories
2643
+ WHERE version > ?
2644
+ AND (scope IS NULL OR scope != 'personal')
2645
+ ORDER BY version ASC
2646
+ LIMIT ?`,
2647
+ args: [batchCursor, PUSH_BATCH_SIZE]
2648
+ });
2649
+ if (recordsResult.rows.length === 0) break;
2557
2650
  const records = recordsResult.rows.map((row) => ({
2558
2651
  id: row.id,
2559
2652
  agent_id: row.agent_id,
@@ -2570,13 +2663,14 @@ async function cloudSync(config) {
2570
2663
  }));
2571
2664
  const maxVersion = Number(records[records.length - 1].version);
2572
2665
  const pushOk = await cloudPush(records, maxVersion, config);
2573
- if (pushOk) {
2574
- await client.execute({
2575
- sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
2576
- args: [String(maxVersion)]
2577
- });
2578
- pushed = records.length;
2579
- }
2666
+ if (!pushOk) break;
2667
+ await client.execute({
2668
+ sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_cloud_push_version', ?)",
2669
+ args: [String(maxVersion)]
2670
+ });
2671
+ pushed += records.length;
2672
+ batchCursor = maxVersion;
2673
+ if (recordsResult.rows.length < PUSH_BATCH_SIZE) break;
2580
2674
  }
2581
2675
  try {
2582
2676
  await cloudPushRoster(config);
@@ -2658,6 +2752,27 @@ async function cloudSync(config) {
2658
2752
  documents: documentsResult
2659
2753
  };
2660
2754
  }
2755
+ function recordRosterDeletion(name) {
2756
+ let deletions = [];
2757
+ try {
2758
+ if (existsSync9(ROSTER_DELETIONS_PATH)) {
2759
+ deletions = JSON.parse(readFileSync7(ROSTER_DELETIONS_PATH, "utf-8"));
2760
+ }
2761
+ } catch {
2762
+ }
2763
+ if (!deletions.includes(name)) deletions.push(name);
2764
+ writeFileSync2(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
2765
+ }
2766
+ function consumeRosterDeletions() {
2767
+ try {
2768
+ if (!existsSync9(ROSTER_DELETIONS_PATH)) return [];
2769
+ const deletions = JSON.parse(readFileSync7(ROSTER_DELETIONS_PATH, "utf-8"));
2770
+ writeFileSync2(ROSTER_DELETIONS_PATH, "[]");
2771
+ return deletions;
2772
+ } catch {
2773
+ return [];
2774
+ }
2775
+ }
2661
2776
  function buildRosterBlob(paths) {
2662
2777
  const rosterPath = paths?.rosterPath ?? path9.join(EXE_AI_DIR, "exe-employees.json");
2663
2778
  const identityDir = paths?.identityDir ?? path9.join(EXE_AI_DIR, "identity");
@@ -2671,7 +2786,7 @@ function buildRosterBlob(paths) {
2671
2786
  }
2672
2787
  const identities = {};
2673
2788
  if (existsSync9(identityDir)) {
2674
- for (const file of readdirSync2(identityDir).filter((f) => f.endsWith(".md"))) {
2789
+ for (const file of readdirSync3(identityDir).filter((f) => f.endsWith(".md"))) {
2675
2790
  try {
2676
2791
  identities[file] = readFileSync7(path9.join(identityDir, file), "utf-8");
2677
2792
  } catch {
@@ -2685,9 +2800,10 @@ function buildRosterBlob(paths) {
2685
2800
  } catch {
2686
2801
  }
2687
2802
  }
2688
- const content = JSON.stringify({ roster, identities, config });
2803
+ const deletedNames = consumeRosterDeletions();
2804
+ const content = JSON.stringify({ roster, identities, config, deletedNames });
2689
2805
  const hash = Buffer.from(content).length;
2690
- return { roster, identities, config, version: hash };
2806
+ return { roster, identities, config, deletedNames, version: hash };
2691
2807
  }
2692
2808
  async function cloudPushRoster(config) {
2693
2809
  assertSecureEndpoint(config.endpoint);
@@ -2770,38 +2886,50 @@ function mergeConfig(remoteConfig, configPath) {
2770
2886
  writeFileSync2(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
2771
2887
  }
2772
2888
  async function mergeRosterFromRemote(remote, paths) {
2773
- const rosterPath = paths?.rosterPath ?? void 0;
2774
- const identityDir = paths?.identityDir ?? path9.join(EXE_AI_DIR, "identity");
2775
- const localEmployees = await loadEmployees(rosterPath);
2776
- const localNames = new Set(localEmployees.map((e) => e.name));
2777
- let added = 0;
2778
- for (const remoteEmp of remote.roster) {
2779
- if (localNames.has(remoteEmp.name)) continue;
2780
- localEmployees.push(remoteEmp);
2781
- localNames.add(remoteEmp.name);
2782
- added++;
2783
- if (remote.identities[`${remoteEmp.name}.md`]) {
2784
- if (!existsSync9(identityDir)) mkdirSync3(identityDir, { recursive: true });
2785
- const idPath = path9.join(identityDir, `${remoteEmp.name}.md`);
2786
- if (!existsSync9(idPath)) {
2787
- writeFileSync2(idPath, remote.identities[`${remoteEmp.name}.md`], "utf-8");
2889
+ return withRosterLock(async () => {
2890
+ const rosterPath = paths?.rosterPath ?? void 0;
2891
+ const identityDir = paths?.identityDir ?? path9.join(EXE_AI_DIR, "identity");
2892
+ const localEmployees = await loadEmployees(rosterPath);
2893
+ const localNames = new Set(localEmployees.map((e) => e.name));
2894
+ let added = 0;
2895
+ for (const remoteEmp of remote.roster) {
2896
+ if (localNames.has(remoteEmp.name)) continue;
2897
+ localEmployees.push(remoteEmp);
2898
+ localNames.add(remoteEmp.name);
2899
+ added++;
2900
+ if (remote.identities[`${remoteEmp.name}.md`]) {
2901
+ if (!existsSync9(identityDir)) mkdirSync3(identityDir, { recursive: true });
2902
+ const idPath = path9.join(identityDir, `${remoteEmp.name}.md`);
2903
+ if (!existsSync9(idPath)) {
2904
+ writeFileSync2(idPath, remote.identities[`${remoteEmp.name}.md`], "utf-8");
2905
+ }
2906
+ }
2907
+ try {
2908
+ registerBinSymlinks(remoteEmp.name);
2909
+ } catch {
2788
2910
  }
2789
2911
  }
2790
- try {
2791
- registerBinSymlinks(remoteEmp.name);
2792
- } catch {
2912
+ let removed = 0;
2913
+ if (remote.deletedNames && remote.deletedNames.length > 0) {
2914
+ const toRemove = new Set(remote.deletedNames);
2915
+ const filtered = localEmployees.filter((e) => !toRemove.has(e.name));
2916
+ removed = localEmployees.length - filtered.length;
2917
+ if (removed > 0) {
2918
+ localEmployees.length = 0;
2919
+ localEmployees.push(...filtered);
2920
+ }
2793
2921
  }
2794
- }
2795
- if (added > 0) {
2796
- await saveEmployees(localEmployees, rosterPath);
2797
- }
2798
- if (remote.config && Object.keys(remote.config).length > 0) {
2799
- try {
2800
- mergeConfig(remote.config, paths?.configPath);
2801
- } catch {
2922
+ if (added > 0 || removed > 0) {
2923
+ await saveEmployees(localEmployees, rosterPath);
2802
2924
  }
2803
- }
2804
- return { added };
2925
+ if (remote.config && Object.keys(remote.config).length > 0) {
2926
+ try {
2927
+ mergeConfig(remote.config, paths?.configPath);
2928
+ } catch {
2929
+ }
2930
+ }
2931
+ return { added };
2932
+ });
2805
2933
  }
2806
2934
  async function cloudPushBlob(route, data, metaKey, config) {
2807
2935
  if (data.length === 0) return { ok: true };
@@ -2869,7 +2997,7 @@ async function cloudPullBlob(route, config) {
2869
2997
  }
2870
2998
  async function cloudPushBehaviors(config) {
2871
2999
  const client = getClient();
2872
- const result = await client.execute("SELECT * FROM behaviors");
3000
+ const result = await client.execute("SELECT * FROM behaviors LIMIT 10000");
2873
3001
  const rows = result.rows;
2874
3002
  const { ok } = await cloudPushBlob(
2875
3003
  "/sync/push-behaviors",
@@ -2917,13 +3045,13 @@ async function cloudPullBehaviors(config) {
2917
3045
  async function cloudPushGraphRAG(config) {
2918
3046
  const client = getClient();
2919
3047
  const [entities, relationships, aliases, entityMems, relMems, hyperedges, hyperedgeNodes] = await Promise.all([
2920
- client.execute("SELECT * FROM entities"),
2921
- client.execute("SELECT * FROM relationships"),
2922
- client.execute("SELECT * FROM entity_aliases"),
2923
- client.execute("SELECT * FROM entity_memories"),
2924
- client.execute("SELECT * FROM relationship_memories"),
2925
- client.execute("SELECT * FROM hyperedges"),
2926
- client.execute("SELECT * FROM hyperedge_nodes")
3048
+ client.execute("SELECT * FROM entities LIMIT 50000"),
3049
+ client.execute("SELECT * FROM relationships LIMIT 50000"),
3050
+ client.execute("SELECT * FROM entity_aliases LIMIT 50000"),
3051
+ client.execute("SELECT * FROM entity_memories LIMIT 50000"),
3052
+ client.execute("SELECT * FROM relationship_memories LIMIT 50000"),
3053
+ client.execute("SELECT * FROM hyperedges LIMIT 50000"),
3054
+ client.execute("SELECT * FROM hyperedge_nodes LIMIT 50000")
2927
3055
  ]);
2928
3056
  const blob = {
2929
3057
  entities: entities.rows,
@@ -3025,7 +3153,7 @@ async function cloudPullGraphRAG(config) {
3025
3153
  }
3026
3154
  async function cloudPushTasks(config) {
3027
3155
  const client = getClient();
3028
- const result = await client.execute("SELECT * FROM tasks");
3156
+ const result = await client.execute("SELECT * FROM tasks LIMIT 10000");
3029
3157
  const rows = result.rows;
3030
3158
  const { ok } = await cloudPushBlob(
3031
3159
  "/sync/push-tasks",
@@ -3071,7 +3199,7 @@ async function cloudPullTasks(config) {
3071
3199
  }
3072
3200
  async function cloudPushConversations(config) {
3073
3201
  const client = getClient();
3074
- const result = await client.execute("SELECT * FROM conversations");
3202
+ const result = await client.execute("SELECT * FROM conversations LIMIT 50000");
3075
3203
  const rows = result.rows;
3076
3204
  const { ok } = await cloudPushBlob(
3077
3205
  "/sync/push-conversations",
@@ -3121,8 +3249,8 @@ async function cloudPullConversations(config) {
3121
3249
  async function cloudPushDocuments(config) {
3122
3250
  const client = getClient();
3123
3251
  const [workspaces, documents] = await Promise.all([
3124
- client.execute("SELECT * FROM workspaces"),
3125
- client.execute("SELECT * FROM documents")
3252
+ client.execute("SELECT * FROM workspaces LIMIT 1000"),
3253
+ client.execute("SELECT * FROM documents LIMIT 10000")
3126
3254
  ]);
3127
3255
  const blob = {
3128
3256
  workspaces: workspaces.rows,
@@ -3175,7 +3303,7 @@ async function cloudPullDocuments(config) {
3175
3303
  }
3176
3304
  return { pulled };
3177
3305
  }
3178
- var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS;
3306
+ var LOCALHOST_PATTERNS, FETCH_TIMEOUT_MS, PUSH_BATCH_SIZE, ROSTER_LOCK_PATH, LOCK_STALE_MS, ROSTER_DELETIONS_PATH;
3179
3307
  var init_cloud_sync = __esm({
3180
3308
  "src/lib/cloud-sync.ts"() {
3181
3309
  "use strict";
@@ -3188,6 +3316,68 @@ var init_cloud_sync = __esm({
3188
3316
  init_employees();
3189
3317
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
3190
3318
  FETCH_TIMEOUT_MS = 3e4;
3319
+ PUSH_BATCH_SIZE = 5e3;
3320
+ ROSTER_LOCK_PATH = path9.join(EXE_AI_DIR, "roster-merge.lock");
3321
+ LOCK_STALE_MS = 3e4;
3322
+ ROSTER_DELETIONS_PATH = path9.join(EXE_AI_DIR, "roster-deletions.json");
3323
+ }
3324
+ });
3325
+
3326
+ // src/lib/worker-gate.ts
3327
+ var worker_gate_exports = {};
3328
+ __export(worker_gate_exports, {
3329
+ MAX_CONCURRENT_WORKERS: () => MAX_CONCURRENT_WORKERS,
3330
+ cleanupWorkerPid: () => cleanupWorkerPid,
3331
+ registerWorkerPid: () => registerWorkerPid,
3332
+ tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
3333
+ });
3334
+ import { readdirSync as readdirSync4, writeFileSync as writeFileSync3, unlinkSync as unlinkSync4, mkdirSync as mkdirSync4 } from "fs";
3335
+ import path10 from "path";
3336
+ function tryAcquireWorkerSlot() {
3337
+ try {
3338
+ mkdirSync4(WORKER_PID_DIR, { recursive: true });
3339
+ const files = readdirSync4(WORKER_PID_DIR);
3340
+ let alive = 0;
3341
+ for (const f of files) {
3342
+ if (!f.endsWith(".pid")) continue;
3343
+ const dashIdx = f.lastIndexOf("-");
3344
+ const pid = parseInt(f.slice(dashIdx + 1).replace(".pid", ""), 10);
3345
+ if (isNaN(pid)) continue;
3346
+ try {
3347
+ process.kill(pid, 0);
3348
+ alive++;
3349
+ } catch {
3350
+ try {
3351
+ unlinkSync4(path10.join(WORKER_PID_DIR, f));
3352
+ } catch {
3353
+ }
3354
+ }
3355
+ }
3356
+ return alive < MAX_CONCURRENT_WORKERS;
3357
+ } catch {
3358
+ return true;
3359
+ }
3360
+ }
3361
+ function registerWorkerPid(pid) {
3362
+ try {
3363
+ mkdirSync4(WORKER_PID_DIR, { recursive: true });
3364
+ writeFileSync3(path10.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
3365
+ } catch {
3366
+ }
3367
+ }
3368
+ function cleanupWorkerPid() {
3369
+ try {
3370
+ unlinkSync4(path10.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
3371
+ } catch {
3372
+ }
3373
+ }
3374
+ var WORKER_PID_DIR, MAX_CONCURRENT_WORKERS;
3375
+ var init_worker_gate = __esm({
3376
+ "src/lib/worker-gate.ts"() {
3377
+ "use strict";
3378
+ init_config();
3379
+ WORKER_PID_DIR = path10.join(EXE_AI_DIR, "worker-pids");
3380
+ MAX_CONCURRENT_WORKERS = 3;
3191
3381
  }
3192
3382
  });
3193
3383
 
@@ -3196,6 +3386,30 @@ init_memory();
3196
3386
  init_database();
3197
3387
  init_keychain();
3198
3388
  init_config();
3389
+ var INIT_MAX_RETRIES = 3;
3390
+ var INIT_RETRY_DELAY_MS = 1e3;
3391
+ function isBusyError2(err) {
3392
+ if (err instanceof Error) {
3393
+ const msg = err.message.toLowerCase();
3394
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
3395
+ }
3396
+ return false;
3397
+ }
3398
+ async function retryOnBusy2(fn, label) {
3399
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
3400
+ try {
3401
+ return await fn();
3402
+ } catch (err) {
3403
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
3404
+ process.stderr.write(
3405
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
3406
+ `
3407
+ );
3408
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
3409
+ }
3410
+ }
3411
+ throw new Error("unreachable");
3412
+ }
3199
3413
  var _pendingRecords = [];
3200
3414
  var _batchSize = 20;
3201
3415
  var _flushIntervalMs = 1e4;
@@ -3230,14 +3444,17 @@ async function initStore(options) {
3230
3444
  dbPath,
3231
3445
  encryptionKey: hexKey
3232
3446
  });
3233
- await ensureSchema();
3447
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
3234
3448
  try {
3235
3449
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
3236
3450
  initShardManager2(hexKey);
3237
3451
  } catch {
3238
3452
  }
3239
3453
  const client = getClient();
3240
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
3454
+ const vResult = await retryOnBusy2(
3455
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
3456
+ "version-query"
3457
+ );
3241
3458
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
3242
3459
  }
3243
3460
  function classifyTier(record) {
@@ -3280,6 +3497,12 @@ async function writeMemory(record) {
3280
3497
  supersedes_id: record.supersedes_id ?? null
3281
3498
  };
3282
3499
  _pendingRecords.push(dbRow);
3500
+ const MAX_PENDING = 1e3;
3501
+ if (_pendingRecords.length > MAX_PENDING) {
3502
+ const dropped = _pendingRecords.length - MAX_PENDING;
3503
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
3504
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
3505
+ }
3283
3506
  if (_flushTimer === null) {
3284
3507
  _flushTimer = setInterval(() => {
3285
3508
  void flushBatch();
@@ -3436,10 +3659,10 @@ import crypto4 from "crypto";
3436
3659
  init_database();
3437
3660
  import crypto2 from "crypto";
3438
3661
  import path4 from "path";
3439
- import os2 from "os";
3662
+ import os3 from "os";
3440
3663
  import {
3441
3664
  readFileSync as readFileSync2,
3442
- readdirSync,
3665
+ readdirSync as readdirSync2,
3443
3666
  unlinkSync,
3444
3667
  existsSync as existsSync4,
3445
3668
  rmdirSync
@@ -3471,8 +3694,8 @@ async function writeNotification(notification) {
3471
3694
 
3472
3695
  // src/adapters/claude/hooks/summary-worker.ts
3473
3696
  import { execSync as execSync2 } from "child_process";
3474
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
3475
- import path10 from "path";
3697
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, openSync as openSync2, closeSync as closeSync2 } from "fs";
3698
+ import path11 from "path";
3476
3699
  async function main() {
3477
3700
  const agentId = process.env.AGENT_ID ?? "default";
3478
3701
  const agentRole = process.env.AGENT_ROLE ?? "employee";
@@ -3596,16 +3819,16 @@ async function main() {
3596
3819
  }
3597
3820
  try {
3598
3821
  const { EXE_AI_DIR: EXE_AI_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3599
- const flagPath = path10.join(EXE_AI_DIR2, "session-cache", "needs-backfill");
3822
+ const flagPath = path11.join(EXE_AI_DIR2, "session-cache", "needs-backfill");
3600
3823
  if (existsSync10(flagPath)) {
3601
3824
  const { spawn: spawn2 } = await import("child_process");
3602
3825
  const { fileURLToPath: fileURLToPath2 } = await import("url");
3603
3826
  const thisFile = fileURLToPath2(import.meta.url);
3604
- const backfillPath = path10.resolve(path10.dirname(thisFile), "backfill-vectors.js");
3827
+ const backfillPath = path11.resolve(path11.dirname(thisFile), "backfill-vectors.js");
3605
3828
  if (existsSync10(backfillPath)) {
3606
3829
  const { EXE_AI_DIR: exeDir2 } = await Promise.resolve().then(() => (init_config(), config_exports));
3607
- const bLogPath = path10.join(exeDir2, "workers.log");
3608
- mkdirSync4(path10.dirname(bLogPath), { recursive: true });
3830
+ const bLogPath = path11.join(exeDir2, "workers.log");
3831
+ mkdirSync5(path11.dirname(bLogPath), { recursive: true });
3609
3832
  const bLogFd = openSync2(bLogPath, "a");
3610
3833
  const child = spawn2(process.execPath, [backfillPath], {
3611
3834
  detached: true,
@@ -3678,9 +3901,19 @@ async function main() {
3678
3901
  process.stderr.write("[summary-worker] orphan scan failed: " + (err instanceof Error ? err.message : String(err)) + "\n");
3679
3902
  }
3680
3903
  }
3904
+ try {
3905
+ const { getRawClient: getRawClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
3906
+ await getRawClient2().execute("PRAGMA wal_checkpoint(PASSIVE)");
3907
+ } catch {
3908
+ }
3681
3909
  }
3682
3910
  main().catch((err) => {
3683
3911
  process.stderr.write("[summary-worker] FATAL: " + (err instanceof Error ? err.message : String(err)) + "\n");
3684
- }).finally(() => {
3912
+ }).finally(async () => {
3913
+ try {
3914
+ const { cleanupWorkerPid: cleanupWorkerPid2 } = await Promise.resolve().then(() => (init_worker_gate(), worker_gate_exports));
3915
+ cleanupWorkerPid2();
3916
+ } catch {
3917
+ }
3685
3918
  process.exit(0);
3686
3919
  });