@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
@@ -50,7 +50,7 @@ __export(config_exports, {
50
50
  migrateConfig: () => migrateConfig,
51
51
  saveConfig: () => saveConfig
52
52
  });
53
- import { readFile, writeFile, mkdir } from "fs/promises";
53
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
54
54
  import { readFileSync, existsSync, renameSync } from "fs";
55
55
  import path from "path";
56
56
  import os from "os";
@@ -176,6 +176,9 @@ async function saveConfig(config2) {
176
176
  await mkdir(dir, { recursive: true });
177
177
  const configPath = path.join(dir, "config.json");
178
178
  await writeFile(configPath, JSON.stringify(config2, null, 2) + "\n");
179
+ if (config2.cloud?.apiKey) {
180
+ await chmod(configPath, 384);
181
+ }
179
182
  }
180
183
  async function loadConfigFrom(configPath) {
181
184
  const raw = await readFile(configPath, "utf-8");
@@ -286,6 +289,10 @@ import path2 from "path";
286
289
  import { fileURLToPath } from "url";
287
290
  function handleData(chunk) {
288
291
  _buffer += chunk.toString();
292
+ if (_buffer.length > MAX_BUFFER) {
293
+ _buffer = "";
294
+ return;
295
+ }
289
296
  let newlineIdx;
290
297
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
291
298
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -593,7 +600,7 @@ function disconnectClient() {
593
600
  entry.resolve({ error: "Client disconnected" });
594
601
  }
595
602
  }
596
- 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;
603
+ 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;
597
604
  var init_exe_daemon_client = __esm({
598
605
  "src/lib/exe-daemon-client.ts"() {
599
606
  "use strict";
@@ -610,6 +617,7 @@ var init_exe_daemon_client = __esm({
610
617
  _requestCount = 0;
611
618
  HEALTH_CHECK_INTERVAL = 100;
612
619
  _pending = /* @__PURE__ */ new Map();
620
+ MAX_BUFFER = 1e7;
613
621
  }
614
622
  });
615
623
 
@@ -772,6 +780,7 @@ async function ensureSchema() {
772
780
  const client = getRawClient();
773
781
  await client.execute("PRAGMA journal_mode = WAL");
774
782
  await client.execute("PRAGMA busy_timeout = 30000");
783
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
775
784
  try {
776
785
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
777
786
  } catch {
@@ -1580,12 +1589,13 @@ var init_database = __esm({
1580
1589
  });
1581
1590
 
1582
1591
  // src/lib/keychain.ts
1583
- import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod } from "fs/promises";
1592
+ import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
1584
1593
  import { existsSync as existsSync3 } from "fs";
1585
1594
  import path3 from "path";
1595
+ import os2 from "os";
1586
1596
  import crypto from "crypto";
1587
1597
  function getKeyDir() {
1588
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(process.env.HOME ?? "/tmp", ".exe-os");
1598
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os2.homedir(), ".exe-os");
1589
1599
  }
1590
1600
  function getKeyPath() {
1591
1601
  return path3.join(getKeyDir(), "master.key");
@@ -1642,7 +1652,7 @@ __export(shard_manager_exports, {
1642
1652
  shardExists: () => shardExists
1643
1653
  });
1644
1654
  import path4 from "path";
1645
- import { existsSync as existsSync4, mkdirSync } from "fs";
1655
+ import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
1646
1656
  import { createClient as createClient2 } from "@libsql/client";
1647
1657
  function initShardManager(encryptionKey) {
1648
1658
  _encryptionKey = encryptionKey;
@@ -1681,8 +1691,7 @@ function shardExists(projectName) {
1681
1691
  }
1682
1692
  function listShards() {
1683
1693
  if (!existsSync4(SHARDS_DIR)) return [];
1684
- const { readdirSync: readdirSync8 } = __require("fs");
1685
- return readdirSync8(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1694
+ return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1686
1695
  }
1687
1696
  async function ensureShardSchema(client) {
1688
1697
  await client.execute("PRAGMA journal_mode = WAL");
@@ -1887,6 +1896,28 @@ __export(store_exports, {
1887
1896
  vectorToBlob: () => vectorToBlob,
1888
1897
  writeMemory: () => writeMemory
1889
1898
  });
1899
+ function isBusyError2(err) {
1900
+ if (err instanceof Error) {
1901
+ const msg = err.message.toLowerCase();
1902
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1903
+ }
1904
+ return false;
1905
+ }
1906
+ async function retryOnBusy2(fn, label) {
1907
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1908
+ try {
1909
+ return await fn();
1910
+ } catch (err) {
1911
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1912
+ process.stderr.write(
1913
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1914
+ `
1915
+ );
1916
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1917
+ }
1918
+ }
1919
+ throw new Error("unreachable");
1920
+ }
1890
1921
  async function initStore(options) {
1891
1922
  if (_flushTimer !== null) {
1892
1923
  clearInterval(_flushTimer);
@@ -1915,14 +1946,17 @@ async function initStore(options) {
1915
1946
  dbPath,
1916
1947
  encryptionKey: hexKey
1917
1948
  });
1918
- await ensureSchema();
1949
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1919
1950
  try {
1920
1951
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1921
1952
  initShardManager2(hexKey);
1922
1953
  } catch {
1923
1954
  }
1924
1955
  const client = getClient();
1925
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1956
+ const vResult = await retryOnBusy2(
1957
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1958
+ "version-query"
1959
+ );
1926
1960
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1927
1961
  }
1928
1962
  function classifyTier(record) {
@@ -1965,6 +1999,12 @@ async function writeMemory(record) {
1965
1999
  supersedes_id: record.supersedes_id ?? null
1966
2000
  };
1967
2001
  _pendingRecords.push(dbRow);
2002
+ const MAX_PENDING = 1e3;
2003
+ if (_pendingRecords.length > MAX_PENDING) {
2004
+ const dropped = _pendingRecords.length - MAX_PENDING;
2005
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
2006
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
2007
+ }
1968
2008
  if (_flushTimer === null) {
1969
2009
  _flushTimer = setInterval(() => {
1970
2010
  void flushBatch();
@@ -2296,7 +2336,7 @@ async function getMemoryCardinality(agentId) {
2296
2336
  return 0;
2297
2337
  }
2298
2338
  }
2299
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
2339
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
2300
2340
  var init_store = __esm({
2301
2341
  "src/lib/store.ts"() {
2302
2342
  "use strict";
@@ -2304,6 +2344,8 @@ var init_store = __esm({
2304
2344
  init_database();
2305
2345
  init_keychain();
2306
2346
  init_config();
2347
+ INIT_MAX_RETRIES = 3;
2348
+ INIT_RETRY_DELAY_MS = 1e3;
2307
2349
  _pendingRecords = [];
2308
2350
  _batchSize = 20;
2309
2351
  _flushIntervalMs = 1e4;
@@ -2459,7 +2501,7 @@ __export(file_grep_exports, {
2459
2501
  grepProjectFiles: () => grepProjectFiles
2460
2502
  });
2461
2503
  import { execSync as execSync2 } from "child_process";
2462
- import { readFileSync as readFileSync3, readdirSync, statSync as statSync2, existsSync as existsSync5 } from "fs";
2504
+ import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync5 } from "fs";
2463
2505
  import path6 from "path";
2464
2506
  import crypto2 from "crypto";
2465
2507
  function hasRipgrep() {
@@ -2604,7 +2646,7 @@ function collectFiles(root, patterns) {
2604
2646
  const basename = path6.basename(dir);
2605
2647
  if (EXCLUDE_DIRS.includes(basename)) return;
2606
2648
  try {
2607
- const entries = readdirSync(dir, { withFileTypes: true });
2649
+ const entries = readdirSync2(dir, { withFileTypes: true });
2608
2650
  for (const entry of entries) {
2609
2651
  if (files.length >= MAX_FILES) return;
2610
2652
  const rel = path6.join(relative, entry.name);
@@ -2836,7 +2878,7 @@ __export(active_agent_exports, {
2836
2878
  getAllActiveAgents: () => getAllActiveAgents,
2837
2879
  writeActiveAgent: () => writeActiveAgent
2838
2880
  });
2839
- import { readFileSync as readFileSync4, writeFileSync, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync as readdirSync2 } from "fs";
2881
+ import { readFileSync as readFileSync4, writeFileSync, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync as readdirSync3 } from "fs";
2840
2882
  import { execSync as execSync4 } from "child_process";
2841
2883
  import path8 from "path";
2842
2884
  function getMarkerPath() {
@@ -2907,7 +2949,7 @@ function getActiveAgent() {
2907
2949
  }
2908
2950
  function getAllActiveAgents() {
2909
2951
  try {
2910
- const files = readdirSync2(CACHE_DIR);
2952
+ const files = readdirSync3(CACHE_DIR);
2911
2953
  const sessions = [];
2912
2954
  for (const file of files) {
2913
2955
  if (!file.startsWith("active-agent-") || !file.endsWith(".json")) continue;
@@ -3050,15 +3092,20 @@ function addEmployee(employees, employee) {
3050
3092
  }
3051
3093
  return [...employees, normalized];
3052
3094
  }
3095
+ function findExeBin() {
3096
+ try {
3097
+ return execSync5(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
3098
+ } catch {
3099
+ return null;
3100
+ }
3101
+ }
3053
3102
  function registerBinSymlinks(name) {
3054
3103
  const created = [];
3055
3104
  const skipped = [];
3056
3105
  const errors = [];
3057
- let exeBinPath;
3058
- try {
3059
- exeBinPath = execSync5("which exe", { encoding: "utf-8" }).trim();
3060
- } catch {
3061
- errors.push("Could not find 'exe' in PATH");
3106
+ const exeBinPath = findExeBin();
3107
+ if (!exeBinPath) {
3108
+ errors.push("Could not find 'exe-os' in PATH");
3062
3109
  return { created, skipped, errors };
3063
3110
  }
3064
3111
  const binDir = path9.dirname(exeBinPath);
@@ -3108,12 +3155,22 @@ __export(license_exports, {
3108
3155
  loadLicense: () => loadLicense,
3109
3156
  mirrorLicenseKey: () => mirrorLicenseKey,
3110
3157
  saveLicense: () => saveLicense,
3158
+ startLicenseRevalidation: () => startLicenseRevalidation,
3159
+ stopLicenseRevalidation: () => stopLicenseRevalidation,
3111
3160
  validateLicense: () => validateLicense
3112
3161
  });
3113
3162
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
3114
3163
  import { randomUUID as randomUUID2 } from "crypto";
3115
3164
  import path10 from "path";
3116
3165
  import { jwtVerify, importSPKI } from "jose";
3166
+ async function fetchRetry(url, init) {
3167
+ try {
3168
+ return await fetch(url, init);
3169
+ } catch {
3170
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
3171
+ return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
3172
+ }
3173
+ }
3117
3174
  function loadDeviceId() {
3118
3175
  const deviceJsonPath = path10.join(EXE_AI_DIR, "device.json");
3119
3176
  try {
@@ -3145,7 +3202,7 @@ function loadLicense() {
3145
3202
  }
3146
3203
  function saveLicense(apiKey) {
3147
3204
  mkdirSync3(EXE_AI_DIR, { recursive: true });
3148
- writeFileSync2(LICENSE_PATH, apiKey.trim(), "utf8");
3205
+ writeFileSync2(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
3149
3206
  }
3150
3207
  async function verifyLicenseJwt(token) {
3151
3208
  try {
@@ -3197,7 +3254,7 @@ function cacheResponse(token) {
3197
3254
  async function validateLicense(apiKey, deviceId) {
3198
3255
  const did = deviceId ?? loadDeviceId();
3199
3256
  try {
3200
- const res = await fetch(`${API_BASE}/auth/activate`, {
3257
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
3201
3258
  method: "POST",
3202
3259
  headers: { "Content-Type": "application/json" },
3203
3260
  body: JSON.stringify({ apiKey, deviceId: did }),
@@ -3232,14 +3289,23 @@ async function validateLicense(apiKey, deviceId) {
3232
3289
  } catch {
3233
3290
  const cached = await getCachedLicense();
3234
3291
  if (cached) return cached;
3235
- return FREE_LICENSE;
3292
+ return { ...FREE_LICENSE, valid: false, error: "offline" };
3293
+ }
3294
+ }
3295
+ function getCacheAgeMs() {
3296
+ try {
3297
+ const { statSync: statSync4 } = __require("fs");
3298
+ const s = statSync4(CACHE_PATH);
3299
+ return Date.now() - s.mtimeMs;
3300
+ } catch {
3301
+ return Infinity;
3236
3302
  }
3237
3303
  }
3238
3304
  async function checkLicense() {
3239
3305
  const key = loadLicense();
3240
3306
  if (!key) return FREE_LICENSE;
3241
3307
  const cached = await getCachedLicense();
3242
- if (cached) return cached;
3308
+ if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
3243
3309
  const deviceId = loadDeviceId();
3244
3310
  return validateLicense(key, deviceId);
3245
3311
  }
@@ -3279,7 +3345,7 @@ async function assertVpsLicense(opts) {
3279
3345
  let explicitRejection = false;
3280
3346
  let transientFailure = false;
3281
3347
  try {
3282
- const res = await fetch(`${API_BASE}/auth/activate`, {
3348
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
3283
3349
  method: "POST",
3284
3350
  headers: { "Content-Type": "application/json" },
3285
3351
  body: JSON.stringify({ apiKey, deviceId }),
@@ -3360,7 +3426,28 @@ async function assertVpsLicense(opts) {
3360
3426
  `License validation unreachable for more than ${graceDays} days. Restore network connectivity to https://askexe.com/cloud and retry. This VPS image refuses to boot after the offline grace window.`
3361
3427
  );
3362
3428
  }
3363
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE;
3429
+ function startLicenseRevalidation(intervalMs = 36e5) {
3430
+ if (_revalTimer) return;
3431
+ _revalTimer = setInterval(async () => {
3432
+ try {
3433
+ const license = await checkLicense();
3434
+ if (!license.valid) {
3435
+ process.stderr.write("[exe-os] License expired or invalid \u2014 features may be restricted\n");
3436
+ }
3437
+ } catch {
3438
+ }
3439
+ }, intervalMs);
3440
+ if (_revalTimer && typeof _revalTimer === "object" && "unref" in _revalTimer) {
3441
+ _revalTimer.unref();
3442
+ }
3443
+ }
3444
+ function stopLicenseRevalidation() {
3445
+ if (_revalTimer) {
3446
+ clearInterval(_revalTimer);
3447
+ _revalTimer = null;
3448
+ }
3449
+ }
3450
+ 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, _revalTimer;
3364
3451
  var init_license = __esm({
3365
3452
  "src/lib/license.ts"() {
3366
3453
  "use strict";
@@ -3369,6 +3456,7 @@ var init_license = __esm({
3369
3456
  CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
3370
3457
  DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
3371
3458
  API_BASE = "https://askexe.com/cloud";
3459
+ RETRY_DELAY_MS = 500;
3372
3460
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
3373
3461
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
3374
3462
  4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
@@ -3390,6 +3478,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
3390
3478
  employeeLimit: 1,
3391
3479
  memoryLimit: 5e3
3392
3480
  };
3481
+ CACHE_MAX_AGE_MS = 36e5;
3482
+ _revalTimer = null;
3393
3483
  }
3394
3484
  });
3395
3485
 
@@ -3521,10 +3611,10 @@ var init_plan_limits = __esm({
3521
3611
  // src/lib/notifications.ts
3522
3612
  import crypto5 from "crypto";
3523
3613
  import path14 from "path";
3524
- import os2 from "os";
3614
+ import os3 from "os";
3525
3615
  import {
3526
3616
  readFileSync as readFileSync8,
3527
- readdirSync as readdirSync3,
3617
+ readdirSync as readdirSync4,
3528
3618
  unlinkSync as unlinkSync3,
3529
3619
  existsSync as existsSync10,
3530
3620
  rmdirSync
@@ -3974,7 +4064,7 @@ var init_tasks_crud = __esm({
3974
4064
  // src/lib/session-registry.ts
3975
4065
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync12 } from "fs";
3976
4066
  import path16 from "path";
3977
- import os3 from "os";
4067
+ import os4 from "os";
3978
4068
  function registerSession(entry) {
3979
4069
  const dir = path16.dirname(REGISTRY_PATH);
3980
4070
  if (!existsSync12(dir)) {
@@ -4001,7 +4091,7 @@ var REGISTRY_PATH;
4001
4091
  var init_session_registry = __esm({
4002
4092
  "src/lib/session-registry.ts"() {
4003
4093
  "use strict";
4004
- REGISTRY_PATH = path16.join(os3.homedir(), ".exe-os", "session-registry.json");
4094
+ REGISTRY_PATH = path16.join(os4.homedir(), ".exe-os", "session-registry.json");
4005
4095
  }
4006
4096
  });
4007
4097
 
@@ -4188,7 +4278,7 @@ var init_provider_table = __esm({
4188
4278
  // src/lib/intercom-queue.ts
4189
4279
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, renameSync as renameSync2, existsSync as existsSync13, mkdirSync as mkdirSync5 } from "fs";
4190
4280
  import path17 from "path";
4191
- import os4 from "os";
4281
+ import os5 from "os";
4192
4282
  function ensureDir() {
4193
4283
  const dir = path17.dirname(QUEUE_PATH);
4194
4284
  if (!existsSync13(dir)) mkdirSync5(dir, { recursive: true });
@@ -4228,9 +4318,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
4228
4318
  var init_intercom_queue = __esm({
4229
4319
  "src/lib/intercom-queue.ts"() {
4230
4320
  "use strict";
4231
- QUEUE_PATH = path17.join(os4.homedir(), ".exe-os", "intercom-queue.json");
4321
+ QUEUE_PATH = path17.join(os5.homedir(), ".exe-os", "intercom-queue.json");
4232
4322
  TTL_MS = 60 * 60 * 1e3;
4233
- INTERCOM_LOG = path17.join(os4.homedir(), ".exe-os", "intercom.log");
4323
+ INTERCOM_LOG = path17.join(os5.homedir(), ".exe-os", "intercom.log");
4234
4324
  }
4235
4325
  });
4236
4326
 
@@ -4238,7 +4328,7 @@ var init_intercom_queue = __esm({
4238
4328
  import { execFileSync as execFileSync2, execSync as execSync8 } from "child_process";
4239
4329
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync14, appendFileSync } from "fs";
4240
4330
  import path18 from "path";
4241
- import os5 from "os";
4331
+ import os6 from "os";
4242
4332
  import { fileURLToPath as fileURLToPath2 } from "url";
4243
4333
  import { unlinkSync as unlinkSync4 } from "fs";
4244
4334
  function spawnLockPath(sessionName) {
@@ -4543,7 +4633,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4543
4633
  const transport = getTransport();
4544
4634
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
4545
4635
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
4546
- const logDir = path18.join(os5.homedir(), ".exe-os", "session-logs");
4636
+ const logDir = path18.join(os6.homedir(), ".exe-os", "session-logs");
4547
4637
  const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4548
4638
  if (!existsSync14(logDir)) {
4549
4639
  mkdirSync6(logDir, { recursive: true });
@@ -4559,7 +4649,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4559
4649
  } catch {
4560
4650
  }
4561
4651
  try {
4562
- const claudeJsonPath = path18.join(os5.homedir(), ".claude.json");
4652
+ const claudeJsonPath = path18.join(os6.homedir(), ".claude.json");
4563
4653
  let claudeJson = {};
4564
4654
  try {
4565
4655
  claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
@@ -4574,7 +4664,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4574
4664
  } catch {
4575
4665
  }
4576
4666
  try {
4577
- const settingsDir = path18.join(os5.homedir(), ".claude", "projects");
4667
+ const settingsDir = path18.join(os6.homedir(), ".claude", "projects");
4578
4668
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
4579
4669
  const projSettingsDir = path18.join(settingsDir, normalizedKey);
4580
4670
  const settingsPath = path18.join(projSettingsDir, "settings.json");
@@ -4622,7 +4712,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4622
4712
  let legacyFallbackWarned = false;
4623
4713
  if (!useExeAgent && !useBinSymlink) {
4624
4714
  const identityPath2 = path18.join(
4625
- os5.homedir(),
4715
+ os6.homedir(),
4626
4716
  ".exe-os",
4627
4717
  "identity",
4628
4718
  `${employeeName}.md`
@@ -4652,7 +4742,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4652
4742
  }
4653
4743
  let sessionContextFlag = "";
4654
4744
  try {
4655
- const ctxDir = path18.join(os5.homedir(), ".exe-os", "session-cache");
4745
+ const ctxDir = path18.join(os6.homedir(), ".exe-os", "session-cache");
4656
4746
  mkdirSync6(ctxDir, { recursive: true });
4657
4747
  const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
4658
4748
  const ctxContent = [
@@ -4763,11 +4853,11 @@ var init_tmux_routing = __esm({
4763
4853
  init_provider_table();
4764
4854
  init_intercom_queue();
4765
4855
  init_plan_limits();
4766
- SPAWN_LOCK_DIR = path18.join(os5.homedir(), ".exe-os", "spawn-locks");
4767
- SESSION_CACHE = path18.join(os5.homedir(), ".exe-os", "session-cache");
4856
+ SPAWN_LOCK_DIR = path18.join(os6.homedir(), ".exe-os", "spawn-locks");
4857
+ SESSION_CACHE = path18.join(os6.homedir(), ".exe-os", "session-cache");
4768
4858
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4769
4859
  INTERCOM_DEBOUNCE_MS = 3e4;
4770
- INTERCOM_LOG2 = path18.join(os5.homedir(), ".exe-os", "intercom.log");
4860
+ INTERCOM_LOG2 = path18.join(os6.homedir(), ".exe-os", "intercom.log");
4771
4861
  DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
4772
4862
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4773
4863
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
@@ -4776,7 +4866,7 @@ var init_tmux_routing = __esm({
4776
4866
 
4777
4867
  // src/lib/tasks-review.ts
4778
4868
  import path19 from "path";
4779
- import { existsSync as existsSync15, readdirSync as readdirSync4, unlinkSync as unlinkSync5 } from "fs";
4869
+ import { existsSync as existsSync15, readdirSync as readdirSync5, unlinkSync as unlinkSync5 } from "fs";
4780
4870
  async function countPendingReviews() {
4781
4871
  const client = getClient();
4782
4872
  const result = await client.execute({
@@ -4898,7 +4988,7 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
4898
4988
  try {
4899
4989
  const cacheDir = path19.join(EXE_AI_DIR, "session-cache");
4900
4990
  if (existsSync15(cacheDir)) {
4901
- for (const f of readdirSync4(cacheDir)) {
4991
+ for (const f of readdirSync5(cacheDir)) {
4902
4992
  if (f.startsWith("review-notified-")) {
4903
4993
  unlinkSync5(path19.join(cacheDir, f));
4904
4994
  }
@@ -6681,50 +6771,59 @@ function registerRecallMyMemory(server2) {
6681
6771
  user_id,
6682
6772
  include_source
6683
6773
  }) => {
6684
- const { agentId } = getActiveAgent();
6685
- const searchOptions = {
6686
- projectName: project_name,
6687
- hasError: has_error,
6688
- toolName: tool_name,
6689
- limit,
6690
- since,
6691
- includeArchived: include_archived,
6692
- workspaceId: workspace_id,
6693
- includeSource: include_source,
6694
- ...user_id !== void 0 ? { userId: user_id } : {}
6695
- };
6696
- const results = await hybridSearch(query, agentId, searchOptions);
6697
- if (results.length === 0) {
6774
+ try {
6775
+ const { agentId } = getActiveAgent();
6776
+ const searchOptions = {
6777
+ projectName: project_name,
6778
+ hasError: has_error,
6779
+ toolName: tool_name,
6780
+ limit,
6781
+ since,
6782
+ includeArchived: include_archived,
6783
+ workspaceId: workspace_id,
6784
+ includeSource: include_source,
6785
+ ...user_id !== void 0 ? { userId: user_id } : {}
6786
+ };
6787
+ const results = await hybridSearch(query, agentId, searchOptions);
6788
+ if (results.length === 0) {
6789
+ return {
6790
+ content: [
6791
+ { type: "text", text: "No matching memories found." }
6792
+ ]
6793
+ };
6794
+ }
6795
+ const formatted = results.map((r) => {
6796
+ const header = `[${r.timestamp}] ${r.tool_name} (${r.project_name})${r.has_error ? " [ERROR]" : ""}`;
6797
+ const body = r.raw_text.slice(0, 500);
6798
+ const parts = [header];
6799
+ if (r.source_path) {
6800
+ const typeTag = r.source_type && r.source_type !== "text" ? ` [${r.source_type}]` : "";
6801
+ parts.push(`source: ${r.source_path}${typeTag}`);
6802
+ } else if (include_source) {
6803
+ const sourceLine = formatSourceLine(r);
6804
+ if (sourceLine) parts.push(sourceLine);
6805
+ }
6806
+ parts.push(body);
6807
+ return parts.join("\n");
6808
+ }).join("\n\n---\n\n");
6698
6809
  return {
6699
6810
  content: [
6700
- { type: "text", text: "No matching memories found." }
6811
+ {
6812
+ type: "text",
6813
+ text: `Found ${results.length} memories:
6814
+
6815
+ ${formatted}`
6816
+ }
6701
6817
  ]
6702
6818
  };
6819
+ } catch (err) {
6820
+ const msg = err instanceof Error ? err.message : String(err);
6821
+ const friendly = msg.includes("SQLITE_BUSY") ? "Memory system is busy \u2014 please retry." : "Failed to search memories. Please retry.";
6822
+ return {
6823
+ content: [{ type: "text", text: friendly }],
6824
+ isError: true
6825
+ };
6703
6826
  }
6704
- const formatted = results.map((r) => {
6705
- const header = `[${r.timestamp}] ${r.tool_name} (${r.project_name})${r.has_error ? " [ERROR]" : ""}`;
6706
- const body = r.raw_text.slice(0, 500);
6707
- const parts = [header];
6708
- if (r.source_path) {
6709
- const typeTag = r.source_type && r.source_type !== "text" ? ` [${r.source_type}]` : "";
6710
- parts.push(`source: ${r.source_path}${typeTag}`);
6711
- } else if (include_source) {
6712
- const sourceLine = formatSourceLine(r);
6713
- if (sourceLine) parts.push(sourceLine);
6714
- }
6715
- parts.push(body);
6716
- return parts.join("\n");
6717
- }).join("\n\n---\n\n");
6718
- return {
6719
- content: [
6720
- {
6721
- type: "text",
6722
- text: `Found ${results.length} memories:
6723
-
6724
- ${formatted}`
6725
- }
6726
- ]
6727
- };
6728
6827
  }
6729
6828
  );
6730
6829
  }
@@ -6751,36 +6850,45 @@ function registerAskTeamMemory(server2) {
6751
6850
  }
6752
6851
  },
6753
6852
  async ({ team_member, query, project_name, limit, since, include_archived }) => {
6754
- const results = await hybridSearch(query, team_member, {
6755
- projectName: project_name,
6756
- limit,
6757
- since,
6758
- includeArchived: include_archived
6759
- });
6760
- if (results.length === 0) {
6853
+ try {
6854
+ const results = await hybridSearch(query, team_member, {
6855
+ projectName: project_name,
6856
+ limit,
6857
+ since,
6858
+ includeArchived: include_archived
6859
+ });
6860
+ if (results.length === 0) {
6861
+ return {
6862
+ content: [
6863
+ {
6864
+ type: "text",
6865
+ text: `No memories found for team member '${team_member}'.`
6866
+ }
6867
+ ]
6868
+ };
6869
+ }
6870
+ const formatted = results.map(
6871
+ (r) => `[${r.timestamp}] ${r.tool_name} (${r.project_name})${r.has_error ? " [ERROR]" : ""}
6872
+ ${r.raw_text.slice(0, 500)}`
6873
+ ).join("\n\n---\n\n");
6761
6874
  return {
6762
6875
  content: [
6763
6876
  {
6764
6877
  type: "text",
6765
- text: `No memories found for team member '${team_member}'.`
6878
+ text: `From ${team_member}'s memories (${results.length} results):
6879
+
6880
+ ${formatted}`
6766
6881
  }
6767
6882
  ]
6768
6883
  };
6884
+ } catch (err) {
6885
+ const msg = err instanceof Error ? err.message : String(err);
6886
+ const friendly = msg.includes("SQLITE_BUSY") ? "Memory system is busy \u2014 please retry." : "Failed to search team memories. Please retry.";
6887
+ return {
6888
+ content: [{ type: "text", text: friendly }],
6889
+ isError: true
6890
+ };
6769
6891
  }
6770
- const formatted = results.map(
6771
- (r) => `[${r.timestamp}] ${r.tool_name} (${r.project_name})${r.has_error ? " [ERROR]" : ""}
6772
- ${r.raw_text.slice(0, 500)}`
6773
- ).join("\n\n---\n\n");
6774
- return {
6775
- content: [
6776
- {
6777
- type: "text",
6778
- text: `From ${team_member}'s memories (${results.length} results):
6779
-
6780
- ${formatted}`
6781
- }
6782
- ]
6783
- };
6784
6892
  }
6785
6893
  );
6786
6894
  }
@@ -7428,36 +7536,44 @@ function registerListTasks(server2) {
7428
7536
  }
7429
7537
  },
7430
7538
  async ({ assigned_to, status, project_name, priority }) => {
7431
- const resolvedProject = project_name === "all" ? void 0 : project_name ?? getProjectName();
7432
- const tasks = await listTasks({
7433
- assignedTo: assigned_to,
7434
- status,
7435
- projectName: resolvedProject,
7436
- priority
7437
- });
7438
- if (tasks.length === 0) {
7539
+ try {
7540
+ const resolvedProject = project_name === "all" ? void 0 : project_name ?? getProjectName();
7541
+ const tasks = await listTasks({
7542
+ assignedTo: assigned_to,
7543
+ status,
7544
+ projectName: resolvedProject,
7545
+ priority
7546
+ });
7547
+ if (tasks.length === 0) {
7548
+ return {
7549
+ content: [{ type: "text", text: "No tasks found." }]
7550
+ };
7551
+ }
7552
+ const lines = tasks.map((t) => {
7553
+ const cpIndicator = t.checkpointCount && t.checkpointCount > 0 ? ` [cp:${t.checkpointCount}]` : "";
7554
+ let budgetNote = "";
7555
+ if (t.budgetTokens !== null) {
7556
+ const pct = Math.round(t.tokensUsed / t.budgetTokens * 100);
7557
+ budgetNote = ` [${t.tokensUsed}/${t.budgetTokens} tokens, ${pct}%]`;
7558
+ }
7559
+ return `- [${t.priority.toUpperCase()}] ${t.title} (${t.projectName}) \u2014 ${t.status}${cpIndicator}${budgetNote} \u2192 ${t.assignedTo}`;
7560
+ });
7561
+ return {
7562
+ content: [
7563
+ {
7564
+ type: "text",
7565
+ text: `${tasks.length} task(s):
7566
+ ${lines.join("\n")}`
7567
+ }
7568
+ ]
7569
+ };
7570
+ } catch (err) {
7571
+ const msg = err instanceof Error ? err.message : String(err);
7439
7572
  return {
7440
- content: [{ type: "text", text: "No tasks found." }]
7573
+ content: [{ type: "text", text: `Failed to list tasks: ${msg}` }],
7574
+ isError: true
7441
7575
  };
7442
7576
  }
7443
- const lines = tasks.map((t) => {
7444
- const cpIndicator = t.checkpointCount && t.checkpointCount > 0 ? ` [cp:${t.checkpointCount}]` : "";
7445
- let budgetNote = "";
7446
- if (t.budgetTokens !== null) {
7447
- const pct = Math.round(t.tokensUsed / t.budgetTokens * 100);
7448
- budgetNote = ` [${t.tokensUsed}/${t.budgetTokens} tokens, ${pct}%]`;
7449
- }
7450
- return `- [${t.priority.toUpperCase()}] ${t.title} (${t.projectName}) \u2014 ${t.status}${cpIndicator}${budgetNote} \u2192 ${t.assignedTo}`;
7451
- });
7452
- return {
7453
- content: [
7454
- {
7455
- type: "text",
7456
- text: `${tasks.length} task(s):
7457
- ${lines.join("\n")}`
7458
- }
7459
- ]
7460
- };
7461
7577
  }
7462
7578
  );
7463
7579
  }
@@ -7590,7 +7706,7 @@ All tasks complete. No more open tasks in your queue.`;
7590
7706
  init_tasks();
7591
7707
  init_active_agent();
7592
7708
  import { z as z10 } from "zod";
7593
- var CLOSE_TASK_ALLOWED_AGENTS = /* @__PURE__ */ new Set(["exe", "ea"]);
7709
+ var CLOSE_TASK_ALLOWED_ROLES = /* @__PURE__ */ new Set(["COO", "CTO"]);
7594
7710
  function registerCloseTask(server2) {
7595
7711
  server2.registerTool(
7596
7712
  "close_task",
@@ -7605,7 +7721,7 @@ function registerCloseTask(server2) {
7605
7721
  },
7606
7722
  async ({ task_id, result, status }) => {
7607
7723
  const agent = getActiveAgent();
7608
- if (agent.agentId && !CLOSE_TASK_ALLOWED_AGENTS.has(agent.agentId)) {
7724
+ if (agent.agentId && agent.agentId !== "default" && !CLOSE_TASK_ALLOWED_ROLES.has(agent.agentRole ?? "")) {
7609
7725
  return {
7610
7726
  content: [
7611
7727
  {
@@ -7967,7 +8083,7 @@ async function createReminder(text, dueDate) {
7967
8083
  }
7968
8084
  async function listReminders(includeCompleted = false) {
7969
8085
  const client = getClient();
7970
- const sql = includeCompleted ? `SELECT id, text, created_at, due_date, completed_at FROM reminders ORDER BY due_date ASC NULLS LAST` : `SELECT id, text, created_at, due_date, completed_at FROM reminders WHERE completed_at IS NULL ORDER BY due_date ASC NULLS LAST`;
8086
+ const sql = includeCompleted ? `SELECT id, text, created_at, due_date, completed_at FROM reminders ORDER BY due_date ASC NULLS LAST LIMIT 500` : `SELECT id, text, created_at, due_date, completed_at FROM reminders WHERE completed_at IS NULL ORDER BY due_date ASC NULLS LAST LIMIT 500`;
7971
8087
  const result = await client.execute(sql);
7972
8088
  return result.rows.map((row) => ({
7973
8089
  id: String(row.id),
@@ -7986,7 +8102,7 @@ async function completeReminder(idOrText) {
7986
8102
  });
7987
8103
  if (result.rows.length === 0) {
7988
8104
  result = await client.execute({
7989
- sql: `SELECT id, text FROM reminders WHERE completed_at IS NULL AND text LIKE '%' || ? || '%'`,
8105
+ sql: `SELECT id, text FROM reminders WHERE completed_at IS NULL AND text LIKE '%' || ? || '%' LIMIT 1`,
7990
8106
  args: [idOrText]
7991
8107
  });
7992
8108
  }
@@ -8171,7 +8287,7 @@ import { z as z20 } from "zod";
8171
8287
  init_config();
8172
8288
  init_database();
8173
8289
  import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
8174
- import { readdirSync as readdirSync5 } from "fs";
8290
+ import { readdirSync as readdirSync6 } from "fs";
8175
8291
  import path22 from "path";
8176
8292
  import { createHash } from "crypto";
8177
8293
  var IDENTITY_DIR = path22.join(EXE_AI_DIR, "identity");
@@ -8254,7 +8370,7 @@ async function updateIdentity(agentId, content, updatedBy) {
8254
8370
  }
8255
8371
  function listIdentities() {
8256
8372
  ensureDir2();
8257
- const files = readdirSync5(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
8373
+ const files = readdirSync6(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
8258
8374
  const results = [];
8259
8375
  for (const file of files) {
8260
8376
  const agentId = file.replace(".md", "");
@@ -8339,7 +8455,7 @@ function registerUpdateIdentity(server2) {
8339
8455
  },
8340
8456
  async ({ agent_id, content }) => {
8341
8457
  const caller = getActiveAgent();
8342
- const allowed = caller.agentId === "exe" || caller.agentId === "default";
8458
+ const allowed = caller.agentId === "default" || caller.agentRole === "COO";
8343
8459
  if (!allowed) {
8344
8460
  return {
8345
8461
  content: [{
@@ -8388,7 +8504,7 @@ function registerDeactivateBehavior(server2) {
8388
8504
  },
8389
8505
  async ({ behavior_id }) => {
8390
8506
  const caller = getActiveAgent();
8391
- const allowed = caller.agentId === "exe" || caller.agentId === "default";
8507
+ const allowed = caller.agentId === "default" || caller.agentRole === "COO";
8392
8508
  if (!allowed) {
8393
8509
  return {
8394
8510
  content: [{
@@ -8725,10 +8841,12 @@ function registerIngestDocument(server2) {
8725
8841
  }]
8726
8842
  };
8727
8843
  } catch (err) {
8844
+ const raw = err instanceof Error ? err.message : String(err);
8845
+ const friendly = raw.includes("SQLITE_BUSY") ? "Memory system is busy, please retry in a moment." : raw.includes("SQLITE_") ? "A database error occurred. Please retry." : `ingest_document failed: ${raw}`;
8728
8846
  return {
8729
8847
  content: [{
8730
8848
  type: "text",
8731
- text: `ingest_document failed: ${err instanceof Error ? err.message : String(err)}`
8849
+ text: friendly
8732
8850
  }],
8733
8851
  isError: true
8734
8852
  };
@@ -8917,7 +9035,8 @@ async function gqlRequest(query, variables) {
8917
9035
  "Content-Type": "application/json",
8918
9036
  Authorization: `Bearer ${config.apiToken}`
8919
9037
  },
8920
- body: JSON.stringify({ query, variables })
9038
+ body: JSON.stringify({ query, variables }),
9039
+ signal: AbortSignal.timeout(3e4)
8921
9040
  });
8922
9041
  if (!res.ok) {
8923
9042
  throw new Error(`CRM GraphQL request failed: ${res.status} ${res.statusText}`);
@@ -9080,7 +9199,8 @@ async function lookupPhoneByName(name) {
9080
9199
  "Content-Type": "application/json",
9081
9200
  Authorization: `Bearer ${apiToken}`
9082
9201
  },
9083
- body: JSON.stringify({ query, variables })
9202
+ body: JSON.stringify({ query, variables }),
9203
+ signal: AbortSignal.timeout(3e4)
9084
9204
  });
9085
9205
  if (!res.ok) return null;
9086
9206
  const json = await res.json();
@@ -9105,11 +9225,12 @@ async function sendWhatsAppMessage(phoneNumberId, accessToken, to, text) {
9105
9225
  to,
9106
9226
  type: "text",
9107
9227
  text: { body: text }
9108
- })
9228
+ }),
9229
+ signal: AbortSignal.timeout(3e4)
9109
9230
  });
9110
9231
  if (!res.ok) {
9111
- const errBody = await res.text();
9112
- throw new Error(`WhatsApp send failed (${res.status}): ${errBody}`);
9232
+ await res.text();
9233
+ throw new Error(`WhatsApp message could not be delivered (HTTP ${res.status}). Check your WhatsApp configuration.`);
9113
9234
  }
9114
9235
  }
9115
9236
  function isPhoneNumber(value) {
@@ -9193,11 +9314,13 @@ function registerSendWhatsapp(server2) {
9193
9314
  });
9194
9315
  results.push({ recipient, phone, ok: true });
9195
9316
  } catch (err) {
9317
+ const raw = err instanceof Error ? err.message : String(err);
9318
+ const friendly = raw.includes("HTTP") ? raw : `Message delivery failed. Check WhatsApp configuration.`;
9196
9319
  results.push({
9197
9320
  recipient,
9198
9321
  phone,
9199
9322
  ok: false,
9200
- error: err instanceof Error ? err.message : String(err)
9323
+ error: friendly
9201
9324
  });
9202
9325
  }
9203
9326
  }
@@ -9219,8 +9342,8 @@ import { z as z29 } from "zod";
9219
9342
  import { readFileSync as readFileSync15, writeFileSync as writeFileSync10, existsSync as existsSync17, mkdirSync as mkdirSync9 } from "fs";
9220
9343
  import { randomUUID as randomUUID3 } from "crypto";
9221
9344
  import path23 from "path";
9222
- import os6 from "os";
9223
- var TRIGGERS_PATH = path23.join(os6.homedir(), ".exe-os", "triggers.json");
9345
+ import os7 from "os";
9346
+ var TRIGGERS_PATH = path23.join(os7.homedir(), ".exe-os", "triggers.json");
9224
9347
  function loadTriggers(project) {
9225
9348
  if (!existsSync17(TRIGGERS_PATH)) return [];
9226
9349
  try {
@@ -9522,14 +9645,14 @@ function registerListTriggers(server2) {
9522
9645
  import { z as z31 } from "zod";
9523
9646
 
9524
9647
  // src/automation/starter-packs/index.ts
9525
- import { readFileSync as readFileSync16, readdirSync as readdirSync6, existsSync as existsSync18 } from "fs";
9648
+ import { readFileSync as readFileSync16, readdirSync as readdirSync7, existsSync as existsSync18 } from "fs";
9526
9649
  import path24 from "path";
9527
9650
  import { fileURLToPath as fileURLToPath3 } from "url";
9528
9651
  var __dirname = path24.dirname(fileURLToPath3(import.meta.url));
9529
9652
  function listPacks() {
9530
9653
  const packsDir = path24.join(__dirname, ".");
9531
9654
  if (!existsSync18(packsDir)) return [];
9532
- return readdirSync6(packsDir, { withFileTypes: true }).filter(
9655
+ return readdirSync7(packsDir, { withFileTypes: true }).filter(
9533
9656
  (d) => d.isDirectory() && existsSync18(path24.join(packsDir, d.name, "custom-objects.json"))
9534
9657
  ).map((d) => d.name);
9535
9658
  }
@@ -9561,7 +9684,7 @@ function loadPack(industry) {
9561
9684
  }
9562
9685
  const wikiSeeds = [];
9563
9686
  if (existsSync18(wikiDir)) {
9564
- const files = readdirSync6(wikiDir).filter((f) => f.endsWith(".md"));
9687
+ const files = readdirSync7(wikiDir).filter((f) => f.endsWith(".md"));
9565
9688
  for (const file of files) {
9566
9689
  const content = readFileSync16(path24.join(wikiDir, file), "utf-8");
9567
9690
  const titleMatch = content.match(/^#\s+(.+)/m);
@@ -10736,7 +10859,7 @@ var HostingerApiClient = class {
10736
10859
  "Content-Type": "application/json",
10737
10860
  Accept: "application/json"
10738
10861
  };
10739
- const options = { method, headers };
10862
+ const options = { method, headers, signal: AbortSignal.timeout(3e4) };
10740
10863
  if (body) {
10741
10864
  options.body = JSON.stringify(body);
10742
10865
  }
@@ -11047,11 +11170,13 @@ function registerQueryConversations(server2) {
11047
11170
  ]
11048
11171
  };
11049
11172
  } catch (err) {
11173
+ const raw = err instanceof Error ? err.message : String(err);
11174
+ const friendly = raw.includes("SQLITE_BUSY") ? "Memory system is busy, please retry in a moment." : raw.includes("SQLITE_") ? "A database error occurred. Please retry." : `Error querying conversations: ${raw}`;
11050
11175
  return {
11051
11176
  content: [
11052
11177
  {
11053
11178
  type: "text",
11054
- text: `Error querying conversations: ${err instanceof Error ? err.message : String(err)}`
11179
+ text: friendly
11055
11180
  }
11056
11181
  ],
11057
11182
  isError: true
@@ -11063,13 +11188,13 @@ function registerQueryConversations(server2) {
11063
11188
 
11064
11189
  // src/mcp/tools/load-skill.ts
11065
11190
  import { z as z39 } from "zod";
11066
- import { readFileSync as readFileSync17, readdirSync as readdirSync7, statSync as statSync3 } from "fs";
11191
+ import { readFileSync as readFileSync17, readdirSync as readdirSync8, statSync as statSync3 } from "fs";
11067
11192
  import path27 from "path";
11068
11193
  import { homedir as homedir2 } from "os";
11069
11194
  var SKILLS_DIR = path27.join(homedir2(), ".claude", "skills");
11070
11195
  function listAvailableSkills() {
11071
11196
  try {
11072
- const entries = readdirSync7(SKILLS_DIR);
11197
+ const entries = readdirSync8(SKILLS_DIR);
11073
11198
  return entries.filter((entry) => {
11074
11199
  try {
11075
11200
  const entryPath = path27.join(SKILLS_DIR, entry);