@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
@@ -2,12 +2,6 @@ var __defProp = Object.defineProperty;
2
2
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
4
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
6
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
7
- }) : x)(function(x) {
8
- if (typeof require !== "undefined") return require.apply(this, arguments);
9
- throw Error('Dynamic require of "' + x + '" is not supported');
10
- });
11
5
  var __esm = (fn, res) => function __init() {
12
6
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
13
7
  };
@@ -61,7 +55,8 @@ async function gqlRequest(query, variables) {
61
55
  "Content-Type": "application/json",
62
56
  Authorization: `Bearer ${config.apiToken}`
63
57
  },
64
- body: JSON.stringify({ query, variables })
58
+ body: JSON.stringify({ query, variables }),
59
+ signal: AbortSignal.timeout(3e4)
65
60
  });
66
61
  if (!res.ok) {
67
62
  throw new Error(`CRM GraphQL request failed: ${res.status} ${res.statusText}`);
@@ -368,6 +363,7 @@ async function ensureSchema() {
368
363
  const client = getRawClient();
369
364
  await client.execute("PRAGMA journal_mode = WAL");
370
365
  await client.execute("PRAGMA busy_timeout = 30000");
366
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
371
367
  try {
372
368
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
373
369
  } catch {
@@ -1200,7 +1196,7 @@ __export(config_exports, {
1200
1196
  migrateConfig: () => migrateConfig,
1201
1197
  saveConfig: () => saveConfig
1202
1198
  });
1203
- import { readFile, writeFile, mkdir } from "fs/promises";
1199
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
1204
1200
  import { readFileSync, existsSync, renameSync } from "fs";
1205
1201
  import path from "path";
1206
1202
  import os from "os";
@@ -1326,6 +1322,9 @@ async function saveConfig(config2) {
1326
1322
  await mkdir(dir, { recursive: true });
1327
1323
  const configPath = path.join(dir, "config.json");
1328
1324
  await writeFile(configPath, JSON.stringify(config2, null, 2) + "\n");
1325
+ if (config2.cloud?.apiKey) {
1326
+ await chmod(configPath, 384);
1327
+ }
1329
1328
  }
1330
1329
  async function loadConfigFrom(configPath) {
1331
1330
  const raw = await readFile(configPath, "utf-8");
@@ -1436,6 +1435,10 @@ import path2 from "path";
1436
1435
  import { fileURLToPath } from "url";
1437
1436
  function handleData(chunk) {
1438
1437
  _buffer += chunk.toString();
1438
+ if (_buffer.length > MAX_BUFFER) {
1439
+ _buffer = "";
1440
+ return;
1441
+ }
1439
1442
  let newlineIdx;
1440
1443
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1441
1444
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -1743,7 +1746,7 @@ function disconnectClient() {
1743
1746
  entry.resolve({ error: "Client disconnected" });
1744
1747
  }
1745
1748
  }
1746
- 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;
1747
1750
  var init_exe_daemon_client = __esm({
1748
1751
  "src/lib/exe-daemon-client.ts"() {
1749
1752
  "use strict";
@@ -1760,6 +1763,7 @@ var init_exe_daemon_client = __esm({
1760
1763
  _requestCount = 0;
1761
1764
  HEALTH_CHECK_INTERVAL = 100;
1762
1765
  _pending = /* @__PURE__ */ new Map();
1766
+ MAX_BUFFER = 1e7;
1763
1767
  }
1764
1768
  });
1765
1769
 
@@ -1832,12 +1836,13 @@ var init_embedder = __esm({
1832
1836
  });
1833
1837
 
1834
1838
  // src/lib/keychain.ts
1835
- 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";
1836
1840
  import { existsSync as existsSync3 } from "fs";
1837
1841
  import path3 from "path";
1842
+ import os2 from "os";
1838
1843
  import crypto from "crypto";
1839
1844
  function getKeyDir() {
1840
- 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");
1841
1846
  }
1842
1847
  function getKeyPath() {
1843
1848
  return path3.join(getKeyDir(), "master.key");
@@ -1894,7 +1899,7 @@ __export(shard_manager_exports, {
1894
1899
  shardExists: () => shardExists
1895
1900
  });
1896
1901
  import path4 from "path";
1897
- import { existsSync as existsSync4, mkdirSync } from "fs";
1902
+ import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
1898
1903
  import { createClient as createClient2 } from "@libsql/client";
1899
1904
  function initShardManager(encryptionKey) {
1900
1905
  _encryptionKey = encryptionKey;
@@ -1933,8 +1938,7 @@ function shardExists(projectName) {
1933
1938
  }
1934
1939
  function listShards() {
1935
1940
  if (!existsSync4(SHARDS_DIR)) return [];
1936
- const { readdirSync: readdirSync3 } = __require("fs");
1937
- return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1941
+ return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1938
1942
  }
1939
1943
  async function ensureShardSchema(client) {
1940
1944
  await client.execute("PRAGMA journal_mode = WAL");
@@ -2139,6 +2143,28 @@ __export(store_exports, {
2139
2143
  vectorToBlob: () => vectorToBlob,
2140
2144
  writeMemory: () => writeMemory
2141
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
+ }
2142
2168
  async function initStore(options) {
2143
2169
  if (_flushTimer !== null) {
2144
2170
  clearInterval(_flushTimer);
@@ -2167,14 +2193,17 @@ async function initStore(options) {
2167
2193
  dbPath,
2168
2194
  encryptionKey: hexKey
2169
2195
  });
2170
- await ensureSchema();
2196
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
2171
2197
  try {
2172
2198
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
2173
2199
  initShardManager2(hexKey);
2174
2200
  } catch {
2175
2201
  }
2176
2202
  const client = getClient();
2177
- 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
+ );
2178
2207
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
2179
2208
  }
2180
2209
  function classifyTier(record) {
@@ -2217,6 +2246,12 @@ async function writeMemory(record) {
2217
2246
  supersedes_id: record.supersedes_id ?? null
2218
2247
  };
2219
2248
  _pendingRecords.push(dbRow);
2249
+ const MAX_PENDING = 1e3;
2250
+ if (_pendingRecords.length > MAX_PENDING) {
2251
+ const dropped = _pendingRecords.length - MAX_PENDING;
2252
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
2253
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
2254
+ }
2220
2255
  if (_flushTimer === null) {
2221
2256
  _flushTimer = setInterval(() => {
2222
2257
  void flushBatch();
@@ -2548,7 +2583,7 @@ async function getMemoryCardinality(agentId) {
2548
2583
  return 0;
2549
2584
  }
2550
2585
  }
2551
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
2586
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
2552
2587
  var init_store = __esm({
2553
2588
  "src/lib/store.ts"() {
2554
2589
  "use strict";
@@ -2556,6 +2591,8 @@ var init_store = __esm({
2556
2591
  init_database();
2557
2592
  init_keychain();
2558
2593
  init_config();
2594
+ INIT_MAX_RETRIES = 3;
2595
+ INIT_RETRY_DELAY_MS = 1e3;
2559
2596
  _pendingRecords = [];
2560
2597
  _batchSize = 20;
2561
2598
  _flushIntervalMs = 1e4;
@@ -2583,12 +2620,30 @@ async function wikiFetch(config2, path19, method = "GET", body) {
2583
2620
  const controller = new AbortController();
2584
2621
  const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS2);
2585
2622
  try {
2586
- const response = await fetch(url, {
2587
- method,
2588
- headers,
2589
- body: body ? JSON.stringify(body) : void 0,
2590
- signal: controller.signal
2591
- });
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
+ }
2592
2647
  if (!response.ok) {
2593
2648
  throw new Error(`Wiki API ${method} ${path19}: ${response.status} ${response.statusText}`);
2594
2649
  }
@@ -2746,7 +2801,7 @@ var init_whatsapp_accounts = __esm({
2746
2801
  // src/lib/session-registry.ts
2747
2802
  import { readFileSync as readFileSync4, writeFileSync, mkdirSync as mkdirSync3, existsSync as existsSync5 } from "fs";
2748
2803
  import path6 from "path";
2749
- import os2 from "os";
2804
+ import os4 from "os";
2750
2805
  function registerSession(entry) {
2751
2806
  const dir = path6.dirname(REGISTRY_PATH);
2752
2807
  if (!existsSync5(dir)) {
@@ -2773,7 +2828,7 @@ var REGISTRY_PATH;
2773
2828
  var init_session_registry = __esm({
2774
2829
  "src/lib/session-registry.ts"() {
2775
2830
  "use strict";
2776
- REGISTRY_PATH = path6.join(os2.homedir(), ".exe-os", "session-registry.json");
2831
+ REGISTRY_PATH = path6.join(os4.homedir(), ".exe-os", "session-registry.json");
2777
2832
  }
2778
2833
  });
2779
2834
 
@@ -2995,7 +3050,7 @@ var init_provider_table = __esm({
2995
3050
  // src/lib/intercom-queue.ts
2996
3051
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, renameSync as renameSync2, existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
2997
3052
  import path7 from "path";
2998
- import os3 from "os";
3053
+ import os5 from "os";
2999
3054
  function ensureDir() {
3000
3055
  const dir = path7.dirname(QUEUE_PATH);
3001
3056
  if (!existsSync6(dir)) mkdirSync4(dir, { recursive: true });
@@ -3035,9 +3090,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
3035
3090
  var init_intercom_queue = __esm({
3036
3091
  "src/lib/intercom-queue.ts"() {
3037
3092
  "use strict";
3038
- QUEUE_PATH = path7.join(os3.homedir(), ".exe-os", "intercom-queue.json");
3093
+ QUEUE_PATH = path7.join(os5.homedir(), ".exe-os", "intercom-queue.json");
3039
3094
  TTL_MS = 60 * 60 * 1e3;
3040
- INTERCOM_LOG = path7.join(os3.homedir(), ".exe-os", "intercom.log");
3095
+ INTERCOM_LOG = path7.join(os5.homedir(), ".exe-os", "intercom.log");
3041
3096
  }
3042
3097
  });
3043
3098
 
@@ -3178,7 +3233,7 @@ var init_plan_limits = __esm({
3178
3233
  import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
3179
3234
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6, existsSync as existsSync10, appendFileSync } from "fs";
3180
3235
  import path11 from "path";
3181
- import os4 from "os";
3236
+ import os6 from "os";
3182
3237
  import { fileURLToPath as fileURLToPath2 } from "url";
3183
3238
  import { unlinkSync as unlinkSync2 } from "fs";
3184
3239
  function spawnLockPath(sessionName) {
@@ -3483,7 +3538,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3483
3538
  const transport = getTransport();
3484
3539
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
3485
3540
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
3486
- const logDir = path11.join(os4.homedir(), ".exe-os", "session-logs");
3541
+ const logDir = path11.join(os6.homedir(), ".exe-os", "session-logs");
3487
3542
  const logFile = path11.join(logDir, `${instanceLabel}-${Date.now()}.log`);
3488
3543
  if (!existsSync10(logDir)) {
3489
3544
  mkdirSync6(logDir, { recursive: true });
@@ -3499,7 +3554,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3499
3554
  } catch {
3500
3555
  }
3501
3556
  try {
3502
- const claudeJsonPath = path11.join(os4.homedir(), ".claude.json");
3557
+ const claudeJsonPath = path11.join(os6.homedir(), ".claude.json");
3503
3558
  let claudeJson = {};
3504
3559
  try {
3505
3560
  claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
@@ -3514,7 +3569,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3514
3569
  } catch {
3515
3570
  }
3516
3571
  try {
3517
- const settingsDir = path11.join(os4.homedir(), ".claude", "projects");
3572
+ const settingsDir = path11.join(os6.homedir(), ".claude", "projects");
3518
3573
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
3519
3574
  const projSettingsDir = path11.join(settingsDir, normalizedKey);
3520
3575
  const settingsPath = path11.join(projSettingsDir, "settings.json");
@@ -3562,7 +3617,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3562
3617
  let legacyFallbackWarned = false;
3563
3618
  if (!useExeAgent && !useBinSymlink) {
3564
3619
  const identityPath = path11.join(
3565
- os4.homedir(),
3620
+ os6.homedir(),
3566
3621
  ".exe-os",
3567
3622
  "identity",
3568
3623
  `${employeeName}.md`
@@ -3592,7 +3647,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3592
3647
  }
3593
3648
  let sessionContextFlag = "";
3594
3649
  try {
3595
- const ctxDir = path11.join(os4.homedir(), ".exe-os", "session-cache");
3650
+ const ctxDir = path11.join(os6.homedir(), ".exe-os", "session-cache");
3596
3651
  mkdirSync6(ctxDir, { recursive: true });
3597
3652
  const ctxFile = path11.join(ctxDir, `session-context-${sessionName}.md`);
3598
3653
  const ctxContent = [
@@ -3703,11 +3758,11 @@ var init_tmux_routing = __esm({
3703
3758
  init_provider_table();
3704
3759
  init_intercom_queue();
3705
3760
  init_plan_limits();
3706
- SPAWN_LOCK_DIR = path11.join(os4.homedir(), ".exe-os", "spawn-locks");
3707
- SESSION_CACHE = path11.join(os4.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");
3708
3763
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
3709
3764
  INTERCOM_DEBOUNCE_MS = 3e4;
3710
- INTERCOM_LOG2 = path11.join(os4.homedir(), ".exe-os", "intercom.log");
3765
+ INTERCOM_LOG2 = path11.join(os6.homedir(), ".exe-os", "intercom.log");
3711
3766
  DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
3712
3767
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
3713
3768
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
@@ -3967,10 +4022,10 @@ var init_messaging = __esm({
3967
4022
  // src/lib/notifications.ts
3968
4023
  import crypto4 from "crypto";
3969
4024
  import path12 from "path";
3970
- import os5 from "os";
4025
+ import os7 from "os";
3971
4026
  import {
3972
4027
  readFileSync as readFileSync10,
3973
- readdirSync,
4028
+ readdirSync as readdirSync2,
3974
4029
  unlinkSync as unlinkSync3,
3975
4030
  existsSync as existsSync11,
3976
4031
  rmdirSync
@@ -4419,7 +4474,7 @@ var init_tasks_crud = __esm({
4419
4474
 
4420
4475
  // src/lib/tasks-review.ts
4421
4476
  import path14 from "path";
4422
- import { existsSync as existsSync13, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
4477
+ import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
4423
4478
  async function countPendingReviews() {
4424
4479
  const client = getClient();
4425
4480
  const result = await client.execute({
@@ -4541,7 +4596,7 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
4541
4596
  try {
4542
4597
  const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
4543
4598
  if (existsSync13(cacheDir)) {
4544
- for (const f of readdirSync2(cacheDir)) {
4599
+ for (const f of readdirSync3(cacheDir)) {
4545
4600
  if (f.startsWith("review-notified-")) {
4546
4601
  unlinkSync4(path14.join(cacheDir, f));
4547
4602
  }
@@ -6684,7 +6739,8 @@ var OllamaProvider = class {
6684
6739
  const res = await fetch(`${this.host}/api/chat`, {
6685
6740
  method: "POST",
6686
6741
  headers: { "Content-Type": "application/json" },
6687
- body: JSON.stringify(body)
6742
+ body: JSON.stringify(body),
6743
+ signal: AbortSignal.timeout(3e4)
6688
6744
  });
6689
6745
  if (!res.ok) {
6690
6746
  throw new Error(`Ollama API error: ${res.status} ${await res.text()}`);
@@ -7112,7 +7168,7 @@ var SignalAdapter = class {
7112
7168
  if (/^https?:\/\//i.test(trimmed)) {
7113
7169
  return trimmed.replace(/\/+$/, "");
7114
7170
  }
7115
- return `http://${trimmed}`.replace(/\/+$/, "");
7171
+ return `https://${trimmed}`.replace(/\/+$/, "");
7116
7172
  }
7117
7173
  async connect(config2) {
7118
7174
  this.baseUrl = this.normalizeBaseUrl(
@@ -7534,9 +7590,15 @@ var WebChatAdapter = class {
7534
7590
  res.end(JSON.stringify({ error: "Not found" }));
7535
7591
  }
7536
7592
  async handleChatRequest(req, res) {
7593
+ const MAX_BODY_SIZE = 1048576;
7537
7594
  let body = "";
7538
7595
  for await (const chunk of req) {
7539
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
+ }
7540
7602
  }
7541
7603
  let parsed;
7542
7604
  try {
@@ -8016,11 +8078,12 @@ var SlackAdapter = class {
8016
8078
  // src/gateway/adapters/imessage.ts
8017
8079
  import { execFile } from "child_process";
8018
8080
  import { promisify } from "util";
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 ?? "/Users",
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 os6 from "os";
8870
- var TRIGGERS_PATH = path18.join(os6.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();