@askexenow/exe-os 0.8.38 → 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 (91) hide show
  1. package/README.md +17 -8
  2. package/dist/bin/backfill-conversations.js +46 -10
  3. package/dist/bin/backfill-responses.js +46 -10
  4. package/dist/bin/backfill-vectors.js +42 -8
  5. package/dist/bin/cleanup-stale-review-tasks.js +37 -8
  6. package/dist/bin/cli.js +281 -154
  7. package/dist/bin/exe-agent.js +19 -4
  8. package/dist/bin/exe-assign.js +39 -5
  9. package/dist/bin/exe-boot.js +237 -111
  10. package/dist/bin/exe-call.js +11 -6
  11. package/dist/bin/exe-cloud.js +99 -28
  12. package/dist/bin/exe-dispatch.js +1 -1
  13. package/dist/bin/exe-doctor.js +37 -8
  14. package/dist/bin/exe-export-behaviors.js +39 -10
  15. package/dist/bin/exe-forget.js +38 -9
  16. package/dist/bin/exe-gateway.js +109 -42
  17. package/dist/bin/exe-heartbeat.js +49 -20
  18. package/dist/bin/exe-kill.js +39 -10
  19. package/dist/bin/exe-launch-agent.js +58 -22
  20. package/dist/bin/exe-link.js +184 -85
  21. package/dist/bin/exe-new-employee.js +21 -7
  22. package/dist/bin/exe-pending-messages.js +46 -17
  23. package/dist/bin/exe-pending-notifications.js +37 -8
  24. package/dist/bin/exe-pending-reviews.js +47 -18
  25. package/dist/bin/exe-rename.js +21 -7
  26. package/dist/bin/exe-review.js +34 -5
  27. package/dist/bin/exe-search.js +47 -10
  28. package/dist/bin/exe-session-cleanup.js +56 -19
  29. package/dist/bin/exe-settings.js +63 -2
  30. package/dist/bin/exe-status.js +34 -5
  31. package/dist/bin/exe-team.js +34 -5
  32. package/dist/bin/git-sweep.js +38 -9
  33. package/dist/bin/graph-backfill.js +37 -8
  34. package/dist/bin/graph-export.js +37 -8
  35. package/dist/bin/install.js +1 -1
  36. package/dist/bin/scan-tasks.js +40 -11
  37. package/dist/bin/setup.js +58 -24
  38. package/dist/bin/shard-migrate.js +37 -8
  39. package/dist/bin/wiki-sync.js +39 -9
  40. package/dist/gateway/index.js +102 -37
  41. package/dist/hooks/bug-report-worker.js +62 -28
  42. package/dist/hooks/commit-complete.js +38 -9
  43. package/dist/hooks/error-recall.js +49 -8
  44. package/dist/hooks/exe-heartbeat-hook.js +3 -2
  45. package/dist/hooks/ingest-worker.js +151 -37
  46. package/dist/hooks/ingest.js +74 -28
  47. package/dist/hooks/instructions-loaded.js +39 -9
  48. package/dist/hooks/notification.js +37 -7
  49. package/dist/hooks/post-compact.js +37 -7
  50. package/dist/hooks/pre-compact.js +35 -6
  51. package/dist/hooks/pre-tool-use.js +52 -14
  52. package/dist/hooks/prompt-ingest-worker.js +56 -10
  53. package/dist/hooks/prompt-submit.js +61 -23
  54. package/dist/hooks/response-ingest-worker.js +57 -11
  55. package/dist/hooks/session-end.js +43 -10
  56. package/dist/hooks/session-start.js +46 -8
  57. package/dist/hooks/stop.js +37 -7
  58. package/dist/hooks/subagent-stop.js +37 -7
  59. package/dist/hooks/summary-worker.js +317 -99
  60. package/dist/index.js +87 -22
  61. package/dist/lib/cloud-sync.js +172 -78
  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/employees.js +11 -6
  68. package/dist/lib/exe-daemon-client.js +6 -1
  69. package/dist/lib/exe-daemon.js +71 -28
  70. package/dist/lib/hybrid-search.js +47 -10
  71. package/dist/lib/identity.js +1 -1
  72. package/dist/lib/keychain.js +2 -1
  73. package/dist/lib/license.js +13 -4
  74. package/dist/lib/messaging.js +1 -1
  75. package/dist/lib/reminders.js +2 -2
  76. package/dist/lib/schedules.js +37 -8
  77. package/dist/lib/skill-learning.js +1 -1
  78. package/dist/lib/store.js +37 -8
  79. package/dist/lib/tasks.js +1 -1
  80. package/dist/lib/tmux-routing.js +1 -1
  81. package/dist/mcp/server.js +97 -43
  82. package/dist/mcp/tools/complete-reminder.js +1 -1
  83. package/dist/mcp/tools/create-task.js +14 -6
  84. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  85. package/dist/mcp/tools/list-reminders.js +1 -1
  86. package/dist/mcp/tools/list-tasks.js +1 -1
  87. package/dist/mcp/tools/send-message.js +1 -1
  88. package/dist/mcp/tools/update-task.js +1 -1
  89. package/dist/runtime/index.js +35 -6
  90. package/dist/tui/App.js +177 -95
  91. package/package.json +3 -3
@@ -10,15 +10,15 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // src/lib/config.ts
13
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
13
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
14
14
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
15
15
  import path2 from "path";
16
- import os from "os";
16
+ import os2 from "os";
17
17
  function resolveDataDir() {
18
18
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
19
19
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
20
- const newDir = path2.join(os.homedir(), ".exe-os");
21
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
20
+ const newDir = path2.join(os2.homedir(), ".exe-os");
21
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
22
22
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
23
23
  try {
24
24
  renameSync(legacyDir, newDir);
@@ -105,7 +105,7 @@ async function loadConfig() {
105
105
  normalizeAutoUpdate(migratedCfg);
106
106
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
107
107
  if (config.dbPath.startsWith("~")) {
108
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
108
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
109
109
  }
110
110
  return config;
111
111
  } catch {
@@ -526,6 +526,7 @@ async function ensureSchema() {
526
526
  const client = getRawClient();
527
527
  await client.execute("PRAGMA journal_mode = WAL");
528
528
  await client.execute("PRAGMA busy_timeout = 30000");
529
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
529
530
  try {
530
531
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
531
532
  } catch {
@@ -1327,11 +1328,12 @@ async function disposeDatabase() {
1327
1328
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1328
1329
  import { existsSync } from "fs";
1329
1330
  import path from "path";
1331
+ import os from "os";
1330
1332
  import crypto from "crypto";
1331
1333
  var SERVICE = "exe-mem";
1332
1334
  var ACCOUNT = "master-key";
1333
1335
  function getKeyDir() {
1334
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1336
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1335
1337
  }
1336
1338
  function getKeyPath() {
1337
1339
  return path.join(getKeyDir(), "master.key");
@@ -1368,6 +1370,30 @@ async function getMasterKey() {
1368
1370
 
1369
1371
  // src/lib/store.ts
1370
1372
  init_config();
1373
+ var INIT_MAX_RETRIES = 3;
1374
+ var INIT_RETRY_DELAY_MS = 1e3;
1375
+ function isBusyError2(err) {
1376
+ if (err instanceof Error) {
1377
+ const msg = err.message.toLowerCase();
1378
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1379
+ }
1380
+ return false;
1381
+ }
1382
+ async function retryOnBusy2(fn, label) {
1383
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1384
+ try {
1385
+ return await fn();
1386
+ } catch (err) {
1387
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1388
+ process.stderr.write(
1389
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1390
+ `
1391
+ );
1392
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1393
+ }
1394
+ }
1395
+ throw new Error("unreachable");
1396
+ }
1371
1397
  var _pendingRecords = [];
1372
1398
  var _batchSize = 20;
1373
1399
  var _flushIntervalMs = 1e4;
@@ -1402,14 +1428,17 @@ async function initStore(options) {
1402
1428
  dbPath,
1403
1429
  encryptionKey: hexKey
1404
1430
  });
1405
- await ensureSchema();
1431
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1406
1432
  try {
1407
1433
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1408
1434
  initShardManager2(hexKey);
1409
1435
  } catch {
1410
1436
  }
1411
1437
  const client = getClient();
1412
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1438
+ const vResult = await retryOnBusy2(
1439
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1440
+ "version-query"
1441
+ );
1413
1442
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1414
1443
  }
1415
1444
  async function flushBatch() {
@@ -10,15 +10,15 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // src/lib/config.ts
13
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
13
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
14
14
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
15
15
  import path2 from "path";
16
- import os from "os";
16
+ import os2 from "os";
17
17
  function resolveDataDir() {
18
18
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
19
19
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
20
- const newDir = path2.join(os.homedir(), ".exe-os");
21
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
20
+ const newDir = path2.join(os2.homedir(), ".exe-os");
21
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
22
22
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
23
23
  try {
24
24
  renameSync(legacyDir, newDir);
@@ -105,7 +105,7 @@ async function loadConfig() {
105
105
  normalizeAutoUpdate(migratedCfg);
106
106
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
107
107
  if (config.dbPath.startsWith("~")) {
108
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
108
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
109
109
  }
110
110
  return config;
111
111
  } catch {
@@ -526,6 +526,7 @@ async function ensureSchema() {
526
526
  const client = getRawClient();
527
527
  await client.execute("PRAGMA journal_mode = WAL");
528
528
  await client.execute("PRAGMA busy_timeout = 30000");
529
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
529
530
  try {
530
531
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
531
532
  } catch {
@@ -1327,11 +1328,12 @@ async function disposeDatabase() {
1327
1328
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1328
1329
  import { existsSync } from "fs";
1329
1330
  import path from "path";
1331
+ import os from "os";
1330
1332
  import crypto from "crypto";
1331
1333
  var SERVICE = "exe-mem";
1332
1334
  var ACCOUNT = "master-key";
1333
1335
  function getKeyDir() {
1334
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1336
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1335
1337
  }
1336
1338
  function getKeyPath() {
1337
1339
  return path.join(getKeyDir(), "master.key");
@@ -1368,6 +1370,30 @@ async function getMasterKey() {
1368
1370
 
1369
1371
  // src/lib/store.ts
1370
1372
  init_config();
1373
+ var INIT_MAX_RETRIES = 3;
1374
+ var INIT_RETRY_DELAY_MS = 1e3;
1375
+ function isBusyError2(err) {
1376
+ if (err instanceof Error) {
1377
+ const msg = err.message.toLowerCase();
1378
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1379
+ }
1380
+ return false;
1381
+ }
1382
+ async function retryOnBusy2(fn, label) {
1383
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1384
+ try {
1385
+ return await fn();
1386
+ } catch (err) {
1387
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1388
+ process.stderr.write(
1389
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1390
+ `
1391
+ );
1392
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1393
+ }
1394
+ }
1395
+ throw new Error("unreachable");
1396
+ }
1371
1397
  var _pendingRecords = [];
1372
1398
  var _batchSize = 20;
1373
1399
  var _flushIntervalMs = 1e4;
@@ -1402,14 +1428,17 @@ async function initStore(options) {
1402
1428
  dbPath,
1403
1429
  encryptionKey: hexKey
1404
1430
  });
1405
- await ensureSchema();
1431
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1406
1432
  try {
1407
1433
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1408
1434
  initShardManager2(hexKey);
1409
1435
  } catch {
1410
1436
  }
1411
1437
  const client = getClient();
1412
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1438
+ const vResult = await retryOnBusy2(
1439
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1440
+ "version-query"
1441
+ );
1413
1442
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1414
1443
  }
1415
1444
  async function flushBatch() {
@@ -1570,7 +1599,8 @@ async function wikiRequest(config, path4, method = "GET", body) {
1570
1599
  const response = await fetch(url, {
1571
1600
  method,
1572
1601
  headers,
1573
- body: body ? JSON.stringify(body) : void 0
1602
+ body: body ? JSON.stringify(body) : void 0,
1603
+ signal: AbortSignal.timeout(3e4)
1574
1604
  });
1575
1605
  if (!response.ok) {
1576
1606
  throw new Error(`Wiki API ${method} ${path4}: ${response.status} ${response.statusText}`);
@@ -55,7 +55,8 @@ async function gqlRequest(query, variables) {
55
55
  "Content-Type": "application/json",
56
56
  Authorization: `Bearer ${config.apiToken}`
57
57
  },
58
- body: JSON.stringify({ query, variables })
58
+ body: JSON.stringify({ query, variables }),
59
+ signal: AbortSignal.timeout(3e4)
59
60
  });
60
61
  if (!res.ok) {
61
62
  throw new Error(`CRM GraphQL request failed: ${res.status} ${res.statusText}`);
@@ -362,6 +363,7 @@ async function ensureSchema() {
362
363
  const client = getRawClient();
363
364
  await client.execute("PRAGMA journal_mode = WAL");
364
365
  await client.execute("PRAGMA busy_timeout = 30000");
366
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
365
367
  try {
366
368
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
367
369
  } catch {
@@ -1194,7 +1196,7 @@ __export(config_exports, {
1194
1196
  migrateConfig: () => migrateConfig,
1195
1197
  saveConfig: () => saveConfig
1196
1198
  });
1197
- import { readFile, writeFile, mkdir } from "fs/promises";
1199
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
1198
1200
  import { readFileSync, existsSync, renameSync } from "fs";
1199
1201
  import path from "path";
1200
1202
  import os from "os";
@@ -1320,6 +1322,9 @@ async function saveConfig(config2) {
1320
1322
  await mkdir(dir, { recursive: true });
1321
1323
  const configPath = path.join(dir, "config.json");
1322
1324
  await writeFile(configPath, JSON.stringify(config2, null, 2) + "\n");
1325
+ if (config2.cloud?.apiKey) {
1326
+ await chmod(configPath, 384);
1327
+ }
1323
1328
  }
1324
1329
  async function loadConfigFrom(configPath) {
1325
1330
  const raw = await readFile(configPath, "utf-8");
@@ -1430,6 +1435,10 @@ import path2 from "path";
1430
1435
  import { fileURLToPath } from "url";
1431
1436
  function handleData(chunk) {
1432
1437
  _buffer += chunk.toString();
1438
+ if (_buffer.length > MAX_BUFFER) {
1439
+ _buffer = "";
1440
+ return;
1441
+ }
1433
1442
  let newlineIdx;
1434
1443
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1435
1444
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -1737,7 +1746,7 @@ function disconnectClient() {
1737
1746
  entry.resolve({ error: "Client disconnected" });
1738
1747
  }
1739
1748
  }
1740
- 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;
1749
+ 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;
1741
1750
  var init_exe_daemon_client = __esm({
1742
1751
  "src/lib/exe-daemon-client.ts"() {
1743
1752
  "use strict";
@@ -1754,6 +1763,7 @@ var init_exe_daemon_client = __esm({
1754
1763
  _requestCount = 0;
1755
1764
  HEALTH_CHECK_INTERVAL = 100;
1756
1765
  _pending = /* @__PURE__ */ new Map();
1766
+ MAX_BUFFER = 1e7;
1757
1767
  }
1758
1768
  });
1759
1769
 
@@ -1826,12 +1836,13 @@ var init_embedder = __esm({
1826
1836
  });
1827
1837
 
1828
1838
  // src/lib/keychain.ts
1829
- import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod } from "fs/promises";
1839
+ import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
1830
1840
  import { existsSync as existsSync3 } from "fs";
1831
1841
  import path3 from "path";
1842
+ import os2 from "os";
1832
1843
  import crypto from "crypto";
1833
1844
  function getKeyDir() {
1834
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(process.env.HOME ?? "/tmp", ".exe-os");
1845
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os2.homedir(), ".exe-os");
1835
1846
  }
1836
1847
  function getKeyPath() {
1837
1848
  return path3.join(getKeyDir(), "master.key");
@@ -2132,6 +2143,28 @@ __export(store_exports, {
2132
2143
  vectorToBlob: () => vectorToBlob,
2133
2144
  writeMemory: () => writeMemory
2134
2145
  });
2146
+ function isBusyError2(err) {
2147
+ if (err instanceof Error) {
2148
+ const msg = err.message.toLowerCase();
2149
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
2150
+ }
2151
+ return false;
2152
+ }
2153
+ async function retryOnBusy2(fn, label) {
2154
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
2155
+ try {
2156
+ return await fn();
2157
+ } catch (err) {
2158
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
2159
+ process.stderr.write(
2160
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
2161
+ `
2162
+ );
2163
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
2164
+ }
2165
+ }
2166
+ throw new Error("unreachable");
2167
+ }
2135
2168
  async function initStore(options) {
2136
2169
  if (_flushTimer !== null) {
2137
2170
  clearInterval(_flushTimer);
@@ -2160,14 +2193,17 @@ async function initStore(options) {
2160
2193
  dbPath,
2161
2194
  encryptionKey: hexKey
2162
2195
  });
2163
- await ensureSchema();
2196
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
2164
2197
  try {
2165
2198
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
2166
2199
  initShardManager2(hexKey);
2167
2200
  } catch {
2168
2201
  }
2169
2202
  const client = getClient();
2170
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
2203
+ const vResult = await retryOnBusy2(
2204
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
2205
+ "version-query"
2206
+ );
2171
2207
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
2172
2208
  }
2173
2209
  function classifyTier(record) {
@@ -2547,7 +2583,7 @@ async function getMemoryCardinality(agentId) {
2547
2583
  return 0;
2548
2584
  }
2549
2585
  }
2550
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
2586
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
2551
2587
  var init_store = __esm({
2552
2588
  "src/lib/store.ts"() {
2553
2589
  "use strict";
@@ -2555,6 +2591,8 @@ var init_store = __esm({
2555
2591
  init_database();
2556
2592
  init_keychain();
2557
2593
  init_config();
2594
+ INIT_MAX_RETRIES = 3;
2595
+ INIT_RETRY_DELAY_MS = 1e3;
2558
2596
  _pendingRecords = [];
2559
2597
  _batchSize = 20;
2560
2598
  _flushIntervalMs = 1e4;
@@ -2582,12 +2620,30 @@ async function wikiFetch(config2, path19, method = "GET", body) {
2582
2620
  const controller = new AbortController();
2583
2621
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
2584
2622
  try {
2585
- const response = await fetch(url, {
2586
- method,
2587
- headers,
2588
- body: body ? JSON.stringify(body) : void 0,
2589
- signal: controller.signal
2590
- });
2623
+ let response;
2624
+ try {
2625
+ response = await fetch(url, {
2626
+ method,
2627
+ headers,
2628
+ body: body ? JSON.stringify(body) : void 0,
2629
+ signal: controller.signal
2630
+ });
2631
+ } catch {
2632
+ clearTimeout(timeout);
2633
+ const retryController = new AbortController();
2634
+ const retryTimeout = setTimeout(() => retryController.abort(), REQUEST_TIMEOUT_MS2);
2635
+ try {
2636
+ await new Promise((r) => setTimeout(r, 500));
2637
+ response = await fetch(url, {
2638
+ method,
2639
+ headers,
2640
+ body: body ? JSON.stringify(body) : void 0,
2641
+ signal: retryController.signal
2642
+ });
2643
+ } finally {
2644
+ clearTimeout(retryTimeout);
2645
+ }
2646
+ }
2591
2647
  if (!response.ok) {
2592
2648
  throw new Error(`Wiki API ${method} ${path19}: ${response.status} ${response.statusText}`);
2593
2649
  }
@@ -2745,7 +2801,7 @@ var init_whatsapp_accounts = __esm({
2745
2801
  // src/lib/session-registry.ts
2746
2802
  import { readFileSync as readFileSync4, writeFileSync, mkdirSync as mkdirSync3, existsSync as existsSync5 } from "fs";
2747
2803
  import path6 from "path";
2748
- import os3 from "os";
2804
+ import os4 from "os";
2749
2805
  function registerSession(entry) {
2750
2806
  const dir = path6.dirname(REGISTRY_PATH);
2751
2807
  if (!existsSync5(dir)) {
@@ -2772,7 +2828,7 @@ var REGISTRY_PATH;
2772
2828
  var init_session_registry = __esm({
2773
2829
  "src/lib/session-registry.ts"() {
2774
2830
  "use strict";
2775
- REGISTRY_PATH = path6.join(os3.homedir(), ".exe-os", "session-registry.json");
2831
+ REGISTRY_PATH = path6.join(os4.homedir(), ".exe-os", "session-registry.json");
2776
2832
  }
2777
2833
  });
2778
2834
 
@@ -2994,7 +3050,7 @@ var init_provider_table = __esm({
2994
3050
  // src/lib/intercom-queue.ts
2995
3051
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, renameSync as renameSync2, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
2996
3052
  import path7 from "path";
2997
- import os4 from "os";
3053
+ import os5 from "os";
2998
3054
  function ensureDir() {
2999
3055
  const dir = path7.dirname(QUEUE_PATH);
3000
3056
  if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
@@ -3034,9 +3090,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
3034
3090
  var init_intercom_queue = __esm({
3035
3091
  "src/lib/intercom-queue.ts"() {
3036
3092
  "use strict";
3037
- QUEUE_PATH = path7.join(os4.homedir(), ".exe-os", "intercom-queue.json");
3093
+ QUEUE_PATH = path7.join(os5.homedir(), ".exe-os", "intercom-queue.json");
3038
3094
  TTL_MS = 60 * 60 * 1e3;
3039
- INTERCOM_LOG = path7.join(os4.homedir(), ".exe-os", "intercom.log");
3095
+ INTERCOM_LOG = path7.join(os5.homedir(), ".exe-os", "intercom.log");
3040
3096
  }
3041
3097
  });
3042
3098
 
@@ -3177,7 +3233,7 @@ var init_plan_limits = __esm({
3177
3233
  import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
3178
3234
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6, existsSync as existsSync10, appendFileSync } from "fs";
3179
3235
  import path11 from "path";
3180
- import os5 from "os";
3236
+ import os6 from "os";
3181
3237
  import { fileURLToPath as fileURLToPath2 } from "url";
3182
3238
  import { unlinkSync as unlinkSync2 } from "fs";
3183
3239
  function spawnLockPath(sessionName) {
@@ -3482,7 +3538,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3482
3538
  const transport = getTransport();
3483
3539
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
3484
3540
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
3485
- const logDir = path11.join(os5.homedir(), ".exe-os", "session-logs");
3541
+ const logDir = path11.join(os6.homedir(), ".exe-os", "session-logs");
3486
3542
  const logFile = path11.join(logDir, `${instanceLabel}-${Date.now()}.log`);
3487
3543
  if (!existsSync10(logDir)) {
3488
3544
  mkdirSync6(logDir, { recursive: true });
@@ -3498,7 +3554,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3498
3554
  } catch {
3499
3555
  }
3500
3556
  try {
3501
- const claudeJsonPath = path11.join(os5.homedir(), ".claude.json");
3557
+ const claudeJsonPath = path11.join(os6.homedir(), ".claude.json");
3502
3558
  let claudeJson = {};
3503
3559
  try {
3504
3560
  claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
@@ -3513,7 +3569,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3513
3569
  } catch {
3514
3570
  }
3515
3571
  try {
3516
- const settingsDir = path11.join(os5.homedir(), ".claude", "projects");
3572
+ const settingsDir = path11.join(os6.homedir(), ".claude", "projects");
3517
3573
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
3518
3574
  const projSettingsDir = path11.join(settingsDir, normalizedKey);
3519
3575
  const settingsPath = path11.join(projSettingsDir, "settings.json");
@@ -3561,7 +3617,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3561
3617
  let legacyFallbackWarned = false;
3562
3618
  if (!useExeAgent && !useBinSymlink) {
3563
3619
  const identityPath = path11.join(
3564
- os5.homedir(),
3620
+ os6.homedir(),
3565
3621
  ".exe-os",
3566
3622
  "identity",
3567
3623
  `${employeeName}.md`
@@ -3591,7 +3647,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3591
3647
  }
3592
3648
  let sessionContextFlag = "";
3593
3649
  try {
3594
- const ctxDir = path11.join(os5.homedir(), ".exe-os", "session-cache");
3650
+ const ctxDir = path11.join(os6.homedir(), ".exe-os", "session-cache");
3595
3651
  mkdirSync6(ctxDir, { recursive: true });
3596
3652
  const ctxFile = path11.join(ctxDir, `session-context-${sessionName}.md`);
3597
3653
  const ctxContent = [
@@ -3702,11 +3758,11 @@ var init_tmux_routing = __esm({
3702
3758
  init_provider_table();
3703
3759
  init_intercom_queue();
3704
3760
  init_plan_limits();
3705
- SPAWN_LOCK_DIR = path11.join(os5.homedir(), ".exe-os", "spawn-locks");
3706
- SESSION_CACHE = path11.join(os5.homedir(), ".exe-os", "session-cache");
3761
+ SPAWN_LOCK_DIR = path11.join(os6.homedir(), ".exe-os", "spawn-locks");
3762
+ SESSION_CACHE = path11.join(os6.homedir(), ".exe-os", "session-cache");
3707
3763
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
3708
3764
  INTERCOM_DEBOUNCE_MS = 3e4;
3709
- INTERCOM_LOG2 = path11.join(os5.homedir(), ".exe-os", "intercom.log");
3765
+ INTERCOM_LOG2 = path11.join(os6.homedir(), ".exe-os", "intercom.log");
3710
3766
  DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
3711
3767
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
3712
3768
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
@@ -3966,7 +4022,7 @@ var init_messaging = __esm({
3966
4022
  // src/lib/notifications.ts
3967
4023
  import crypto4 from "crypto";
3968
4024
  import path12 from "path";
3969
- import os6 from "os";
4025
+ import os7 from "os";
3970
4026
  import {
3971
4027
  readFileSync as readFileSync10,
3972
4028
  readdirSync as readdirSync2,
@@ -6683,7 +6739,8 @@ var OllamaProvider = class {
6683
6739
  const res = await fetch(`${this.host}/api/chat`, {
6684
6740
  method: "POST",
6685
6741
  headers: { "Content-Type": "application/json" },
6686
- body: JSON.stringify(body)
6742
+ body: JSON.stringify(body),
6743
+ signal: AbortSignal.timeout(3e4)
6687
6744
  });
6688
6745
  if (!res.ok) {
6689
6746
  throw new Error(`Ollama API error: ${res.status} ${await res.text()}`);
@@ -7111,7 +7168,7 @@ var SignalAdapter = class {
7111
7168
  if (/^https?:\/\//i.test(trimmed)) {
7112
7169
  return trimmed.replace(/\/+$/, "");
7113
7170
  }
7114
- return `http://${trimmed}`.replace(/\/+$/, "");
7171
+ return `https://${trimmed}`.replace(/\/+$/, "");
7115
7172
  }
7116
7173
  async connect(config2) {
7117
7174
  this.baseUrl = this.normalizeBaseUrl(
@@ -7533,9 +7590,15 @@ var WebChatAdapter = class {
7533
7590
  res.end(JSON.stringify({ error: "Not found" }));
7534
7591
  }
7535
7592
  async handleChatRequest(req, res) {
7593
+ const MAX_BODY_SIZE = 1048576;
7536
7594
  let body = "";
7537
7595
  for await (const chunk of req) {
7538
7596
  body += chunk;
7597
+ if (body.length > MAX_BODY_SIZE) {
7598
+ res.writeHead(413, { "Content-Type": "application/json" });
7599
+ res.end(JSON.stringify({ error: "Request body too large" }));
7600
+ return;
7601
+ }
7539
7602
  }
7540
7603
  let parsed;
7541
7604
  try {
@@ -8015,12 +8078,12 @@ var SlackAdapter = class {
8015
8078
  // src/gateway/adapters/imessage.ts
8016
8079
  import { execFile } from "child_process";
8017
8080
  import { promisify } from "util";
8018
- import os2 from "os";
8081
+ import os3 from "os";
8019
8082
  import path5 from "path";
8020
8083
  var execFileAsync = promisify(execFile);
8021
8084
  var POLL_INTERVAL_MS = 5e3;
8022
8085
  var MESSAGES_DB_PATH = path5.join(
8023
- process.env.HOME ?? os2.homedir(),
8086
+ process.env.HOME ?? os3.homedir(),
8024
8087
  "Library/Messages/chat.db"
8025
8088
  );
8026
8089
  var IMessageAdapter = class {
@@ -8866,8 +8929,9 @@ async function ensureCRMContact(info) {
8866
8929
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync6, existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
8867
8930
  import { randomUUID as randomUUID11 } from "crypto";
8868
8931
  import path18 from "path";
8869
- import os7 from "os";
8870
- var TRIGGERS_PATH = path18.join(os7.homedir(), ".exe-os", "triggers.json");
8932
+ import os8 from "os";
8933
+ var TRIGGERS_PATH = path18.join(os8.homedir(), ".exe-os", "triggers.json");
8934
+ var GRAPH_API_VERSION = "v21.0";
8871
8935
  function substituteTemplate(template, record) {
8872
8936
  return template.replace(
8873
8937
  /\{\{(\w+(?:\.\w+)*)\}\}/g,
@@ -8945,7 +9009,7 @@ async function executeSendWhatsapp(params) {
8945
9009
  const message = params.message ?? params.text;
8946
9010
  if (!to || !message)
8947
9011
  throw new Error("send_whatsapp requires 'to' and 'message' params");
8948
- const url = `https://graph.facebook.com/v21.0/${account.phoneNumberId}/messages`;
9012
+ const url = `https://graph.facebook.com/${GRAPH_API_VERSION}/${account.phoneNumberId}/messages`;
8949
9013
  const res = await fetch(url, {
8950
9014
  method: "POST",
8951
9015
  headers: {
@@ -8957,7 +9021,8 @@ async function executeSendWhatsapp(params) {
8957
9021
  to,
8958
9022
  type: "text",
8959
9023
  text: { body: message }
8960
- })
9024
+ }),
9025
+ signal: AbortSignal.timeout(3e4)
8961
9026
  });
8962
9027
  if (!res.ok) {
8963
9028
  const errBody = await res.text();