@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
@@ -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");
@@ -1886,6 +1896,28 @@ __export(store_exports, {
1886
1896
  vectorToBlob: () => vectorToBlob,
1887
1897
  writeMemory: () => writeMemory
1888
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
+ }
1889
1921
  async function initStore(options) {
1890
1922
  if (_flushTimer !== null) {
1891
1923
  clearInterval(_flushTimer);
@@ -1914,14 +1946,17 @@ async function initStore(options) {
1914
1946
  dbPath,
1915
1947
  encryptionKey: hexKey
1916
1948
  });
1917
- await ensureSchema();
1949
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1918
1950
  try {
1919
1951
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1920
1952
  initShardManager2(hexKey);
1921
1953
  } catch {
1922
1954
  }
1923
1955
  const client = getClient();
1924
- 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
+ );
1925
1960
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1926
1961
  }
1927
1962
  function classifyTier(record) {
@@ -2301,7 +2336,7 @@ async function getMemoryCardinality(agentId) {
2301
2336
  return 0;
2302
2337
  }
2303
2338
  }
2304
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
2339
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
2305
2340
  var init_store = __esm({
2306
2341
  "src/lib/store.ts"() {
2307
2342
  "use strict";
@@ -2309,6 +2344,8 @@ var init_store = __esm({
2309
2344
  init_database();
2310
2345
  init_keychain();
2311
2346
  init_config();
2347
+ INIT_MAX_RETRIES = 3;
2348
+ INIT_RETRY_DELAY_MS = 1e3;
2312
2349
  _pendingRecords = [];
2313
2350
  _batchSize = 20;
2314
2351
  _flushIntervalMs = 1e4;
@@ -3055,15 +3092,20 @@ function addEmployee(employees, employee) {
3055
3092
  }
3056
3093
  return [...employees, normalized];
3057
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
+ }
3058
3102
  function registerBinSymlinks(name) {
3059
3103
  const created = [];
3060
3104
  const skipped = [];
3061
3105
  const errors = [];
3062
- let exeBinPath;
3063
- try {
3064
- exeBinPath = execSync5("which exe", { encoding: "utf-8" }).trim();
3065
- } catch {
3066
- 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");
3067
3109
  return { created, skipped, errors };
3068
3110
  }
3069
3111
  const binDir = path9.dirname(exeBinPath);
@@ -3121,6 +3163,14 @@ import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, existsS
3121
3163
  import { randomUUID as randomUUID2 } from "crypto";
3122
3164
  import path10 from "path";
3123
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
+ }
3124
3174
  function loadDeviceId() {
3125
3175
  const deviceJsonPath = path10.join(EXE_AI_DIR, "device.json");
3126
3176
  try {
@@ -3152,7 +3202,7 @@ function loadLicense() {
3152
3202
  }
3153
3203
  function saveLicense(apiKey) {
3154
3204
  mkdirSync3(EXE_AI_DIR, { recursive: true });
3155
- writeFileSync2(LICENSE_PATH, apiKey.trim(), "utf8");
3205
+ writeFileSync2(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
3156
3206
  }
3157
3207
  async function verifyLicenseJwt(token) {
3158
3208
  try {
@@ -3204,7 +3254,7 @@ function cacheResponse(token) {
3204
3254
  async function validateLicense(apiKey, deviceId) {
3205
3255
  const did = deviceId ?? loadDeviceId();
3206
3256
  try {
3207
- const res = await fetch(`${API_BASE}/auth/activate`, {
3257
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
3208
3258
  method: "POST",
3209
3259
  headers: { "Content-Type": "application/json" },
3210
3260
  body: JSON.stringify({ apiKey, deviceId: did }),
@@ -3295,7 +3345,7 @@ async function assertVpsLicense(opts) {
3295
3345
  let explicitRejection = false;
3296
3346
  let transientFailure = false;
3297
3347
  try {
3298
- const res = await fetch(`${API_BASE}/auth/activate`, {
3348
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
3299
3349
  method: "POST",
3300
3350
  headers: { "Content-Type": "application/json" },
3301
3351
  body: JSON.stringify({ apiKey, deviceId }),
@@ -3397,7 +3447,7 @@ function stopLicenseRevalidation() {
3397
3447
  _revalTimer = null;
3398
3448
  }
3399
3449
  }
3400
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS, _revalTimer;
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;
3401
3451
  var init_license = __esm({
3402
3452
  "src/lib/license.ts"() {
3403
3453
  "use strict";
@@ -3406,6 +3456,7 @@ var init_license = __esm({
3406
3456
  CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
3407
3457
  DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
3408
3458
  API_BASE = "https://askexe.com/cloud";
3459
+ RETRY_DELAY_MS = 500;
3409
3460
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
3410
3461
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
3411
3462
  4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
@@ -3560,7 +3611,7 @@ var init_plan_limits = __esm({
3560
3611
  // src/lib/notifications.ts
3561
3612
  import crypto5 from "crypto";
3562
3613
  import path14 from "path";
3563
- import os2 from "os";
3614
+ import os3 from "os";
3564
3615
  import {
3565
3616
  readFileSync as readFileSync8,
3566
3617
  readdirSync as readdirSync4,
@@ -4013,7 +4064,7 @@ var init_tasks_crud = __esm({
4013
4064
  // src/lib/session-registry.ts
4014
4065
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync12 } from "fs";
4015
4066
  import path16 from "path";
4016
- import os3 from "os";
4067
+ import os4 from "os";
4017
4068
  function registerSession(entry) {
4018
4069
  const dir = path16.dirname(REGISTRY_PATH);
4019
4070
  if (!existsSync12(dir)) {
@@ -4040,7 +4091,7 @@ var REGISTRY_PATH;
4040
4091
  var init_session_registry = __esm({
4041
4092
  "src/lib/session-registry.ts"() {
4042
4093
  "use strict";
4043
- REGISTRY_PATH = path16.join(os3.homedir(), ".exe-os", "session-registry.json");
4094
+ REGISTRY_PATH = path16.join(os4.homedir(), ".exe-os", "session-registry.json");
4044
4095
  }
4045
4096
  });
4046
4097
 
@@ -4227,7 +4278,7 @@ var init_provider_table = __esm({
4227
4278
  // src/lib/intercom-queue.ts
4228
4279
  import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, renameSync as renameSync2, existsSync as existsSync13, mkdirSync as mkdirSync5 } from "fs";
4229
4280
  import path17 from "path";
4230
- import os4 from "os";
4281
+ import os5 from "os";
4231
4282
  function ensureDir() {
4232
4283
  const dir = path17.dirname(QUEUE_PATH);
4233
4284
  if (!existsSync13(dir)) mkdirSync5(dir, { recursive: true });
@@ -4267,9 +4318,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
4267
4318
  var init_intercom_queue = __esm({
4268
4319
  "src/lib/intercom-queue.ts"() {
4269
4320
  "use strict";
4270
- QUEUE_PATH = path17.join(os4.homedir(), ".exe-os", "intercom-queue.json");
4321
+ QUEUE_PATH = path17.join(os5.homedir(), ".exe-os", "intercom-queue.json");
4271
4322
  TTL_MS = 60 * 60 * 1e3;
4272
- INTERCOM_LOG = path17.join(os4.homedir(), ".exe-os", "intercom.log");
4323
+ INTERCOM_LOG = path17.join(os5.homedir(), ".exe-os", "intercom.log");
4273
4324
  }
4274
4325
  });
4275
4326
 
@@ -4277,7 +4328,7 @@ var init_intercom_queue = __esm({
4277
4328
  import { execFileSync as execFileSync2, execSync as execSync8 } from "child_process";
4278
4329
  import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync14, appendFileSync } from "fs";
4279
4330
  import path18 from "path";
4280
- import os5 from "os";
4331
+ import os6 from "os";
4281
4332
  import { fileURLToPath as fileURLToPath2 } from "url";
4282
4333
  import { unlinkSync as unlinkSync4 } from "fs";
4283
4334
  function spawnLockPath(sessionName) {
@@ -4582,7 +4633,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4582
4633
  const transport = getTransport();
4583
4634
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
4584
4635
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
4585
- const logDir = path18.join(os5.homedir(), ".exe-os", "session-logs");
4636
+ const logDir = path18.join(os6.homedir(), ".exe-os", "session-logs");
4586
4637
  const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4587
4638
  if (!existsSync14(logDir)) {
4588
4639
  mkdirSync6(logDir, { recursive: true });
@@ -4598,7 +4649,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4598
4649
  } catch {
4599
4650
  }
4600
4651
  try {
4601
- const claudeJsonPath = path18.join(os5.homedir(), ".claude.json");
4652
+ const claudeJsonPath = path18.join(os6.homedir(), ".claude.json");
4602
4653
  let claudeJson = {};
4603
4654
  try {
4604
4655
  claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
@@ -4613,7 +4664,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4613
4664
  } catch {
4614
4665
  }
4615
4666
  try {
4616
- const settingsDir = path18.join(os5.homedir(), ".claude", "projects");
4667
+ const settingsDir = path18.join(os6.homedir(), ".claude", "projects");
4617
4668
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
4618
4669
  const projSettingsDir = path18.join(settingsDir, normalizedKey);
4619
4670
  const settingsPath = path18.join(projSettingsDir, "settings.json");
@@ -4661,7 +4712,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4661
4712
  let legacyFallbackWarned = false;
4662
4713
  if (!useExeAgent && !useBinSymlink) {
4663
4714
  const identityPath2 = path18.join(
4664
- os5.homedir(),
4715
+ os6.homedir(),
4665
4716
  ".exe-os",
4666
4717
  "identity",
4667
4718
  `${employeeName}.md`
@@ -4691,7 +4742,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4691
4742
  }
4692
4743
  let sessionContextFlag = "";
4693
4744
  try {
4694
- const ctxDir = path18.join(os5.homedir(), ".exe-os", "session-cache");
4745
+ const ctxDir = path18.join(os6.homedir(), ".exe-os", "session-cache");
4695
4746
  mkdirSync6(ctxDir, { recursive: true });
4696
4747
  const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
4697
4748
  const ctxContent = [
@@ -4802,11 +4853,11 @@ var init_tmux_routing = __esm({
4802
4853
  init_provider_table();
4803
4854
  init_intercom_queue();
4804
4855
  init_plan_limits();
4805
- SPAWN_LOCK_DIR = path18.join(os5.homedir(), ".exe-os", "spawn-locks");
4806
- 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");
4807
4858
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4808
4859
  INTERCOM_DEBOUNCE_MS = 3e4;
4809
- INTERCOM_LOG2 = path18.join(os5.homedir(), ".exe-os", "intercom.log");
4860
+ INTERCOM_LOG2 = path18.join(os6.homedir(), ".exe-os", "intercom.log");
4810
4861
  DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
4811
4862
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4812
4863
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
@@ -7655,7 +7706,7 @@ All tasks complete. No more open tasks in your queue.`;
7655
7706
  init_tasks();
7656
7707
  init_active_agent();
7657
7708
  import { z as z10 } from "zod";
7658
- var CLOSE_TASK_ALLOWED_AGENTS = /* @__PURE__ */ new Set(["exe", "ea"]);
7709
+ var CLOSE_TASK_ALLOWED_ROLES = /* @__PURE__ */ new Set(["COO", "CTO"]);
7659
7710
  function registerCloseTask(server2) {
7660
7711
  server2.registerTool(
7661
7712
  "close_task",
@@ -7670,7 +7721,7 @@ function registerCloseTask(server2) {
7670
7721
  },
7671
7722
  async ({ task_id, result, status }) => {
7672
7723
  const agent = getActiveAgent();
7673
- if (agent.agentId && !CLOSE_TASK_ALLOWED_AGENTS.has(agent.agentId)) {
7724
+ if (agent.agentId && agent.agentId !== "default" && !CLOSE_TASK_ALLOWED_ROLES.has(agent.agentRole ?? "")) {
7674
7725
  return {
7675
7726
  content: [
7676
7727
  {
@@ -8032,7 +8083,7 @@ async function createReminder(text, dueDate) {
8032
8083
  }
8033
8084
  async function listReminders(includeCompleted = false) {
8034
8085
  const client = getClient();
8035
- 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`;
8036
8087
  const result = await client.execute(sql);
8037
8088
  return result.rows.map((row) => ({
8038
8089
  id: String(row.id),
@@ -8051,7 +8102,7 @@ async function completeReminder(idOrText) {
8051
8102
  });
8052
8103
  if (result.rows.length === 0) {
8053
8104
  result = await client.execute({
8054
- 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`,
8055
8106
  args: [idOrText]
8056
8107
  });
8057
8108
  }
@@ -8404,7 +8455,7 @@ function registerUpdateIdentity(server2) {
8404
8455
  },
8405
8456
  async ({ agent_id, content }) => {
8406
8457
  const caller = getActiveAgent();
8407
- const allowed = caller.agentId === "exe" || caller.agentId === "default";
8458
+ const allowed = caller.agentId === "default" || caller.agentRole === "COO";
8408
8459
  if (!allowed) {
8409
8460
  return {
8410
8461
  content: [{
@@ -8453,7 +8504,7 @@ function registerDeactivateBehavior(server2) {
8453
8504
  },
8454
8505
  async ({ behavior_id }) => {
8455
8506
  const caller = getActiveAgent();
8456
- const allowed = caller.agentId === "exe" || caller.agentId === "default";
8507
+ const allowed = caller.agentId === "default" || caller.agentRole === "COO";
8457
8508
  if (!allowed) {
8458
8509
  return {
8459
8510
  content: [{
@@ -8984,7 +9035,8 @@ async function gqlRequest(query, variables) {
8984
9035
  "Content-Type": "application/json",
8985
9036
  Authorization: `Bearer ${config.apiToken}`
8986
9037
  },
8987
- body: JSON.stringify({ query, variables })
9038
+ body: JSON.stringify({ query, variables }),
9039
+ signal: AbortSignal.timeout(3e4)
8988
9040
  });
8989
9041
  if (!res.ok) {
8990
9042
  throw new Error(`CRM GraphQL request failed: ${res.status} ${res.statusText}`);
@@ -9147,7 +9199,8 @@ async function lookupPhoneByName(name) {
9147
9199
  "Content-Type": "application/json",
9148
9200
  Authorization: `Bearer ${apiToken}`
9149
9201
  },
9150
- body: JSON.stringify({ query, variables })
9202
+ body: JSON.stringify({ query, variables }),
9203
+ signal: AbortSignal.timeout(3e4)
9151
9204
  });
9152
9205
  if (!res.ok) return null;
9153
9206
  const json = await res.json();
@@ -9172,7 +9225,8 @@ async function sendWhatsAppMessage(phoneNumberId, accessToken, to, text) {
9172
9225
  to,
9173
9226
  type: "text",
9174
9227
  text: { body: text }
9175
- })
9228
+ }),
9229
+ signal: AbortSignal.timeout(3e4)
9176
9230
  });
9177
9231
  if (!res.ok) {
9178
9232
  await res.text();
@@ -9288,8 +9342,8 @@ import { z as z29 } from "zod";
9288
9342
  import { readFileSync as readFileSync15, writeFileSync as writeFileSync10, existsSync as existsSync17, mkdirSync as mkdirSync9 } from "fs";
9289
9343
  import { randomUUID as randomUUID3 } from "crypto";
9290
9344
  import path23 from "path";
9291
- import os6 from "os";
9292
- 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");
9293
9347
  function loadTriggers(project) {
9294
9348
  if (!existsSync17(TRIGGERS_PATH)) return [];
9295
9349
  try {
@@ -10805,7 +10859,7 @@ var HostingerApiClient = class {
10805
10859
  "Content-Type": "application/json",
10806
10860
  Accept: "application/json"
10807
10861
  };
10808
- const options = { method, headers };
10862
+ const options = { method, headers, signal: AbortSignal.timeout(3e4) };
10809
10863
  if (body) {
10810
10864
  options.body = JSON.stringify(body);
10811
10865
  }
@@ -24,7 +24,7 @@ async function completeReminder(idOrText) {
24
24
  });
25
25
  if (result.rows.length === 0) {
26
26
  result = await client.execute({
27
- sql: `SELECT id, text FROM reminders WHERE completed_at IS NULL AND text LIKE '%' || ? || '%'`,
27
+ sql: `SELECT id, text FROM reminders WHERE completed_at IS NULL AND text LIKE '%' || ? || '%' LIMIT 1`,
28
28
  args: [idOrText]
29
29
  });
30
30
  }
@@ -59,7 +59,7 @@ __export(config_exports, {
59
59
  migrateConfig: () => migrateConfig,
60
60
  saveConfig: () => saveConfig
61
61
  });
62
- import { readFile, writeFile, mkdir } from "fs/promises";
62
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
63
63
  import { readFileSync, existsSync, renameSync } from "fs";
64
64
  import path from "path";
65
65
  import os from "os";
@@ -185,6 +185,9 @@ async function saveConfig(config) {
185
185
  await mkdir(dir, { recursive: true });
186
186
  const configPath = path.join(dir, "config.json");
187
187
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
188
+ if (config.cloud?.apiKey) {
189
+ await chmod(configPath, 384);
190
+ }
188
191
  }
189
192
  async function loadConfigFrom(configPath) {
190
193
  const raw = await readFile(configPath, "utf-8");
@@ -602,15 +605,20 @@ function addEmployee(employees, employee) {
602
605
  }
603
606
  return [...employees, normalized];
604
607
  }
608
+ function findExeBin() {
609
+ try {
610
+ return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
611
+ } catch {
612
+ return null;
613
+ }
614
+ }
605
615
  function registerBinSymlinks(name) {
606
616
  const created = [];
607
617
  const skipped = [];
608
618
  const errors = [];
609
- let exeBinPath;
610
- try {
611
- exeBinPath = execSync2("which exe", { encoding: "utf-8" }).trim();
612
- } catch {
613
- errors.push("Could not find 'exe' in PATH");
619
+ const exeBinPath = findExeBin();
620
+ if (!exeBinPath) {
621
+ errors.push("Could not find 'exe-os' in PATH");
614
622
  return { created, skipped, errors };
615
623
  }
616
624
  const binDir = path4.dirname(exeBinPath);
@@ -30,7 +30,7 @@ import { execSync as execSync2 } from "child_process";
30
30
  import path2 from "path";
31
31
 
32
32
  // src/lib/config.ts
33
- import { readFile, writeFile, mkdir } from "fs/promises";
33
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
34
34
  import { readFileSync, existsSync, renameSync } from "fs";
35
35
  import path from "path";
36
36
  import os from "os";
@@ -216,7 +216,7 @@ function registerDeactivateBehavior(server) {
216
216
  },
217
217
  async ({ behavior_id }) => {
218
218
  const caller = getActiveAgent();
219
- const allowed = caller.agentId === "exe" || caller.agentId === "default";
219
+ const allowed = caller.agentId === "default" || caller.agentRole === "COO";
220
220
  if (!allowed) {
221
221
  return {
222
222
  content: [{
@@ -17,7 +17,7 @@ function getClient() {
17
17
  // src/lib/reminders.ts
18
18
  async function listReminders(includeCompleted = false) {
19
19
  const client = getClient();
20
- 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`;
20
+ 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`;
21
21
  const result = await client.execute(sql);
22
22
  return result.rows.map((row) => ({
23
23
  id: String(row.id),
@@ -28,7 +28,7 @@ var init_database = __esm({
28
28
  });
29
29
 
30
30
  // src/lib/config.ts
31
- import { readFile, writeFile, mkdir } from "fs/promises";
31
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
32
32
  import { readFileSync, existsSync, renameSync } from "fs";
33
33
  import path from "path";
34
34
  import os from "os";
@@ -342,7 +342,7 @@ var init_intercom_queue = __esm({
342
342
  });
343
343
 
344
344
  // src/lib/config.ts
345
- import { readFile, writeFile, mkdir } from "fs/promises";
345
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
346
346
  import { readFileSync as readFileSync3, existsSync as existsSync3, renameSync as renameSync2 } from "fs";
347
347
  import path3 from "path";
348
348
  import os3 from "os";
@@ -44,7 +44,7 @@ var init_database = __esm({
44
44
  });
45
45
 
46
46
  // src/lib/config.ts
47
- import { readFile, writeFile, mkdir } from "fs/promises";
47
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
48
48
  import { readFileSync, existsSync, renameSync } from "fs";
49
49
  import path from "path";
50
50
  import os from "os";
@@ -461,6 +461,7 @@ async function ensureSchema() {
461
461
  const client = getRawClient();
462
462
  await client.execute("PRAGMA journal_mode = WAL");
463
463
  await client.execute("PRAGMA busy_timeout = 30000");
464
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
464
465
  try {
465
466
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
466
467
  } catch {
@@ -1269,7 +1270,7 @@ var init_database = __esm({
1269
1270
  });
1270
1271
 
1271
1272
  // src/lib/config.ts
1272
- import { readFile, writeFile, mkdir } from "fs/promises";
1273
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
1273
1274
  import { readFileSync as readFileSync3, existsSync as existsSync3, renameSync as renameSync2 } from "fs";
1274
1275
  import path4 from "path";
1275
1276
  import os4 from "os";
@@ -3904,12 +3905,13 @@ var init_memory = __esm({
3904
3905
  });
3905
3906
 
3906
3907
  // src/lib/keychain.ts
3907
- import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod } from "fs/promises";
3908
+ import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
3908
3909
  import { existsSync as existsSync11 } from "fs";
3909
3910
  import path15 from "path";
3911
+ import os7 from "os";
3910
3912
  import crypto6 from "crypto";
3911
3913
  function getKeyDir() {
3912
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path15.join(process.env.HOME ?? "/tmp", ".exe-os");
3914
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path15.join(os7.homedir(), ".exe-os");
3913
3915
  }
3914
3916
  function getKeyPath() {
3915
3917
  return path15.join(getKeyDir(), "master.key");
@@ -4210,6 +4212,28 @@ __export(store_exports, {
4210
4212
  vectorToBlob: () => vectorToBlob,
4211
4213
  writeMemory: () => writeMemory
4212
4214
  });
4215
+ function isBusyError2(err) {
4216
+ if (err instanceof Error) {
4217
+ const msg = err.message.toLowerCase();
4218
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
4219
+ }
4220
+ return false;
4221
+ }
4222
+ async function retryOnBusy2(fn, label) {
4223
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
4224
+ try {
4225
+ return await fn();
4226
+ } catch (err) {
4227
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
4228
+ process.stderr.write(
4229
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
4230
+ `
4231
+ );
4232
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
4233
+ }
4234
+ }
4235
+ throw new Error("unreachable");
4236
+ }
4213
4237
  async function initStore(options) {
4214
4238
  if (_flushTimer !== null) {
4215
4239
  clearInterval(_flushTimer);
@@ -4238,14 +4262,17 @@ async function initStore(options) {
4238
4262
  dbPath,
4239
4263
  encryptionKey: hexKey
4240
4264
  });
4241
- await ensureSchema();
4265
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
4242
4266
  try {
4243
4267
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
4244
4268
  initShardManager2(hexKey);
4245
4269
  } catch {
4246
4270
  }
4247
4271
  const client = getClient();
4248
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
4272
+ const vResult = await retryOnBusy2(
4273
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
4274
+ "version-query"
4275
+ );
4249
4276
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
4250
4277
  }
4251
4278
  function classifyTier(record) {
@@ -4625,7 +4652,7 @@ async function getMemoryCardinality(agentId) {
4625
4652
  return 0;
4626
4653
  }
4627
4654
  }
4628
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
4655
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
4629
4656
  var init_store = __esm({
4630
4657
  "src/lib/store.ts"() {
4631
4658
  "use strict";
@@ -4633,6 +4660,8 @@ var init_store = __esm({
4633
4660
  init_database();
4634
4661
  init_keychain();
4635
4662
  init_config();
4663
+ INIT_MAX_RETRIES = 3;
4664
+ INIT_RETRY_DELAY_MS = 1e3;
4636
4665
  _pendingRecords = [];
4637
4666
  _batchSize = 20;
4638
4667
  _flushIntervalMs = 1e4;