@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
@@ -120,6 +120,7 @@ async function ensureSchema() {
120
120
  const client = getRawClient();
121
121
  await client.execute("PRAGMA journal_mode = WAL");
122
122
  await client.execute("PRAGMA busy_timeout = 30000");
123
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
123
124
  try {
124
125
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
125
126
  } catch {
@@ -931,9 +932,10 @@ var init_database = __esm({
931
932
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
932
933
  import { existsSync } from "fs";
933
934
  import path from "path";
935
+ import os from "os";
934
936
  import crypto from "crypto";
935
937
  function getKeyDir() {
936
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
938
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
937
939
  }
938
940
  function getKeyPath() {
939
941
  return path.join(getKeyDir(), "master.key");
@@ -977,15 +979,15 @@ var init_keychain = __esm({
977
979
  });
978
980
 
979
981
  // src/lib/config.ts
980
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
982
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
981
983
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
982
984
  import path2 from "path";
983
- import os from "os";
985
+ import os2 from "os";
984
986
  function resolveDataDir() {
985
987
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
986
988
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
987
- const newDir = path2.join(os.homedir(), ".exe-os");
988
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
989
+ const newDir = path2.join(os2.homedir(), ".exe-os");
990
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
989
991
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
990
992
  try {
991
993
  renameSync(legacyDir, newDir);
@@ -1072,7 +1074,7 @@ async function loadConfig() {
1072
1074
  normalizeAutoUpdate(migratedCfg);
1073
1075
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
1074
1076
  if (config.dbPath.startsWith("~")) {
1075
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
1077
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
1076
1078
  }
1077
1079
  return config;
1078
1080
  } catch {
@@ -1423,6 +1425,28 @@ __export(store_exports, {
1423
1425
  vectorToBlob: () => vectorToBlob,
1424
1426
  writeMemory: () => writeMemory
1425
1427
  });
1428
+ function isBusyError2(err) {
1429
+ if (err instanceof Error) {
1430
+ const msg = err.message.toLowerCase();
1431
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1432
+ }
1433
+ return false;
1434
+ }
1435
+ async function retryOnBusy2(fn, label) {
1436
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1437
+ try {
1438
+ return await fn();
1439
+ } catch (err) {
1440
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1441
+ process.stderr.write(
1442
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1443
+ `
1444
+ );
1445
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1446
+ }
1447
+ }
1448
+ throw new Error("unreachable");
1449
+ }
1426
1450
  async function initStore(options) {
1427
1451
  if (_flushTimer !== null) {
1428
1452
  clearInterval(_flushTimer);
@@ -1451,14 +1475,17 @@ async function initStore(options) {
1451
1475
  dbPath,
1452
1476
  encryptionKey: hexKey
1453
1477
  });
1454
- await ensureSchema();
1478
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1455
1479
  try {
1456
1480
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1457
1481
  initShardManager2(hexKey);
1458
1482
  } catch {
1459
1483
  }
1460
1484
  const client = getClient();
1461
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1485
+ const vResult = await retryOnBusy2(
1486
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1487
+ "version-query"
1488
+ );
1462
1489
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1463
1490
  }
1464
1491
  function classifyTier(record) {
@@ -1838,7 +1865,7 @@ async function getMemoryCardinality(agentId) {
1838
1865
  return 0;
1839
1866
  }
1840
1867
  }
1841
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1868
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1842
1869
  var init_store = __esm({
1843
1870
  "src/lib/store.ts"() {
1844
1871
  "use strict";
@@ -1846,6 +1873,8 @@ var init_store = __esm({
1846
1873
  init_database();
1847
1874
  init_keychain();
1848
1875
  init_config();
1876
+ INIT_MAX_RETRIES = 3;
1877
+ INIT_RETRY_DELAY_MS = 1e3;
1849
1878
  _pendingRecords = [];
1850
1879
  _batchSize = 20;
1851
1880
  _flushIntervalMs = 1e4;
@@ -10,15 +10,15 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // src/lib/config.ts
13
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
13
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
14
14
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
15
15
  import path2 from "path";
16
- import os from "os";
16
+ import os2 from "os";
17
17
  function resolveDataDir() {
18
18
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
19
19
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
20
- const newDir = path2.join(os.homedir(), ".exe-os");
21
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
20
+ const newDir = path2.join(os2.homedir(), ".exe-os");
21
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
22
22
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
23
23
  try {
24
24
  renameSync(legacyDir, newDir);
@@ -105,7 +105,7 @@ async function loadConfig() {
105
105
  normalizeAutoUpdate(migratedCfg);
106
106
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
107
107
  if (config.dbPath.startsWith("~")) {
108
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
108
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
109
109
  }
110
110
  return config;
111
111
  } catch {
@@ -526,6 +526,7 @@ async function ensureSchema() {
526
526
  const client = getRawClient();
527
527
  await client.execute("PRAGMA journal_mode = WAL");
528
528
  await client.execute("PRAGMA busy_timeout = 30000");
529
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
529
530
  try {
530
531
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
531
532
  } catch {
@@ -1327,11 +1328,12 @@ async function disposeDatabase() {
1327
1328
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1328
1329
  import { existsSync } from "fs";
1329
1330
  import path from "path";
1331
+ import os from "os";
1330
1332
  import crypto from "crypto";
1331
1333
  var SERVICE = "exe-mem";
1332
1334
  var ACCOUNT = "master-key";
1333
1335
  function getKeyDir() {
1334
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1336
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1335
1337
  }
1336
1338
  function getKeyPath() {
1337
1339
  return path.join(getKeyDir(), "master.key");
@@ -1368,6 +1370,30 @@ async function getMasterKey() {
1368
1370
 
1369
1371
  // src/lib/store.ts
1370
1372
  init_config();
1373
+ var INIT_MAX_RETRIES = 3;
1374
+ var INIT_RETRY_DELAY_MS = 1e3;
1375
+ function isBusyError2(err) {
1376
+ if (err instanceof Error) {
1377
+ const msg = err.message.toLowerCase();
1378
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1379
+ }
1380
+ return false;
1381
+ }
1382
+ async function retryOnBusy2(fn, label) {
1383
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1384
+ try {
1385
+ return await fn();
1386
+ } catch (err) {
1387
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1388
+ process.stderr.write(
1389
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1390
+ `
1391
+ );
1392
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1393
+ }
1394
+ }
1395
+ throw new Error("unreachable");
1396
+ }
1371
1397
  var _pendingRecords = [];
1372
1398
  var _batchSize = 20;
1373
1399
  var _flushIntervalMs = 1e4;
@@ -1402,14 +1428,17 @@ async function initStore(options) {
1402
1428
  dbPath,
1403
1429
  encryptionKey: hexKey
1404
1430
  });
1405
- await ensureSchema();
1431
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1406
1432
  try {
1407
1433
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1408
1434
  initShardManager2(hexKey);
1409
1435
  } catch {
1410
1436
  }
1411
1437
  const client = getClient();
1412
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1438
+ const vResult = await retryOnBusy2(
1439
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1440
+ "version-query"
1441
+ );
1413
1442
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1414
1443
  }
1415
1444
  async function flushBatch() {
@@ -10,15 +10,15 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // src/lib/config.ts
13
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
13
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
14
14
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
15
15
  import path2 from "path";
16
- import os from "os";
16
+ import os2 from "os";
17
17
  function resolveDataDir() {
18
18
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
19
19
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
20
- const newDir = path2.join(os.homedir(), ".exe-os");
21
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
20
+ const newDir = path2.join(os2.homedir(), ".exe-os");
21
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
22
22
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
23
23
  try {
24
24
  renameSync(legacyDir, newDir);
@@ -105,7 +105,7 @@ async function loadConfig() {
105
105
  normalizeAutoUpdate(migratedCfg);
106
106
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
107
107
  if (config.dbPath.startsWith("~")) {
108
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
108
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
109
109
  }
110
110
  return config;
111
111
  } catch {
@@ -530,6 +530,7 @@ async function ensureSchema() {
530
530
  const client = getRawClient();
531
531
  await client.execute("PRAGMA journal_mode = WAL");
532
532
  await client.execute("PRAGMA busy_timeout = 30000");
533
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
533
534
  try {
534
535
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
535
536
  } catch {
@@ -1331,11 +1332,12 @@ async function disposeDatabase() {
1331
1332
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1332
1333
  import { existsSync } from "fs";
1333
1334
  import path from "path";
1335
+ import os from "os";
1334
1336
  import crypto from "crypto";
1335
1337
  var SERVICE = "exe-mem";
1336
1338
  var ACCOUNT = "master-key";
1337
1339
  function getKeyDir() {
1338
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1340
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1339
1341
  }
1340
1342
  function getKeyPath() {
1341
1343
  return path.join(getKeyDir(), "master.key");
@@ -1372,6 +1374,30 @@ async function getMasterKey() {
1372
1374
 
1373
1375
  // src/lib/store.ts
1374
1376
  init_config();
1377
+ var INIT_MAX_RETRIES = 3;
1378
+ var INIT_RETRY_DELAY_MS = 1e3;
1379
+ function isBusyError2(err) {
1380
+ if (err instanceof Error) {
1381
+ const msg = err.message.toLowerCase();
1382
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1383
+ }
1384
+ return false;
1385
+ }
1386
+ async function retryOnBusy2(fn, label) {
1387
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1388
+ try {
1389
+ return await fn();
1390
+ } catch (err) {
1391
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1392
+ process.stderr.write(
1393
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1394
+ `
1395
+ );
1396
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1397
+ }
1398
+ }
1399
+ throw new Error("unreachable");
1400
+ }
1375
1401
  var _pendingRecords = [];
1376
1402
  var _batchSize = 20;
1377
1403
  var _flushIntervalMs = 1e4;
@@ -1406,14 +1432,17 @@ async function initStore(options) {
1406
1432
  dbPath,
1407
1433
  encryptionKey: hexKey
1408
1434
  });
1409
- await ensureSchema();
1435
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1410
1436
  try {
1411
1437
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1412
1438
  initShardManager2(hexKey);
1413
1439
  } catch {
1414
1440
  }
1415
1441
  const client = getClient();
1416
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1442
+ const vResult = await retryOnBusy2(
1443
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1444
+ "version-query"
1445
+ );
1417
1446
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1418
1447
  }
1419
1448
  async function flushBatch() {
@@ -25,7 +25,7 @@ import { execSync } from "child_process";
25
25
  import path2 from "path";
26
26
 
27
27
  // src/lib/config.ts
28
- import { readFile, writeFile, mkdir } from "fs/promises";
28
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
29
29
  import { readFileSync, existsSync, renameSync } from "fs";
30
30
  import path from "path";
31
31
  import os from "os";
@@ -111,6 +111,7 @@ async function ensureSchema() {
111
111
  const client = getRawClient();
112
112
  await client.execute("PRAGMA journal_mode = WAL");
113
113
  await client.execute("PRAGMA busy_timeout = 30000");
114
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
114
115
  try {
115
116
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
116
117
  } catch {
@@ -931,9 +932,10 @@ var init_memory = __esm({
931
932
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
932
933
  import { existsSync } from "fs";
933
934
  import path from "path";
935
+ import os from "os";
934
936
  import crypto from "crypto";
935
937
  function getKeyDir() {
936
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
938
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
937
939
  }
938
940
  function getKeyPath() {
939
941
  return path.join(getKeyDir(), "master.key");
@@ -977,15 +979,15 @@ var init_keychain = __esm({
977
979
  });
978
980
 
979
981
  // src/lib/config.ts
980
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
982
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
981
983
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
982
984
  import path2 from "path";
983
- import os from "os";
985
+ import os2 from "os";
984
986
  function resolveDataDir() {
985
987
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
986
988
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
987
- const newDir = path2.join(os.homedir(), ".exe-os");
988
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
989
+ const newDir = path2.join(os2.homedir(), ".exe-os");
990
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
989
991
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
990
992
  try {
991
993
  renameSync(legacyDir, newDir);
@@ -1072,7 +1074,7 @@ async function loadConfig() {
1072
1074
  normalizeAutoUpdate(migratedCfg);
1073
1075
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
1074
1076
  if (config.dbPath.startsWith("~")) {
1075
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
1077
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
1076
1078
  }
1077
1079
  return config;
1078
1080
  } catch {
@@ -1423,6 +1425,28 @@ __export(store_exports, {
1423
1425
  vectorToBlob: () => vectorToBlob,
1424
1426
  writeMemory: () => writeMemory
1425
1427
  });
1428
+ function isBusyError2(err) {
1429
+ if (err instanceof Error) {
1430
+ const msg = err.message.toLowerCase();
1431
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1432
+ }
1433
+ return false;
1434
+ }
1435
+ async function retryOnBusy2(fn, label) {
1436
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1437
+ try {
1438
+ return await fn();
1439
+ } catch (err) {
1440
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1441
+ process.stderr.write(
1442
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1443
+ `
1444
+ );
1445
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1446
+ }
1447
+ }
1448
+ throw new Error("unreachable");
1449
+ }
1426
1450
  async function initStore(options) {
1427
1451
  if (_flushTimer !== null) {
1428
1452
  clearInterval(_flushTimer);
@@ -1451,14 +1475,17 @@ async function initStore(options) {
1451
1475
  dbPath,
1452
1476
  encryptionKey: hexKey
1453
1477
  });
1454
- await ensureSchema();
1478
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1455
1479
  try {
1456
1480
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1457
1481
  initShardManager2(hexKey);
1458
1482
  } catch {
1459
1483
  }
1460
1484
  const client = getClient();
1461
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1485
+ const vResult = await retryOnBusy2(
1486
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1487
+ "version-query"
1488
+ );
1462
1489
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1463
1490
  }
1464
1491
  function classifyTier(record) {
@@ -1838,7 +1865,7 @@ async function getMemoryCardinality(agentId) {
1838
1865
  return 0;
1839
1866
  }
1840
1867
  }
1841
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1868
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1842
1869
  var init_store = __esm({
1843
1870
  "src/lib/store.ts"() {
1844
1871
  "use strict";
@@ -1846,6 +1873,8 @@ var init_store = __esm({
1846
1873
  init_database();
1847
1874
  init_keychain();
1848
1875
  init_config();
1876
+ INIT_MAX_RETRIES = 3;
1877
+ INIT_RETRY_DELAY_MS = 1e3;
1849
1878
  _pendingRecords = [];
1850
1879
  _batchSize = 20;
1851
1880
  _flushIntervalMs = 1e4;
@@ -1909,7 +1938,7 @@ var init_project_name = __esm({
1909
1938
  // src/bin/scan-tasks.ts
1910
1939
  import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
1911
1940
  import path5 from "path";
1912
- import os2 from "os";
1941
+ import os3 from "os";
1913
1942
 
1914
1943
  // src/lib/is-main.ts
1915
1944
  import { realpathSync } from "fs";
@@ -1928,7 +1957,7 @@ function isMainModule(importMetaUrl) {
1928
1957
  // src/bin/scan-tasks.ts
1929
1958
  function checkMcpHealth() {
1930
1959
  try {
1931
- const claudeJson = path5.join(os2.homedir(), ".claude.json");
1960
+ const claudeJson = path5.join(os3.homedir(), ".claude.json");
1932
1961
  if (!existsSync4(claudeJson)) {
1933
1962
  process.stderr.write(
1934
1963
  "\u26A0\uFE0F MCP config missing (~/.claude.json not found) \u2014 close_task won't work. Run /exe-setup\n"
package/dist/bin/setup.js CHANGED
@@ -31,7 +31,7 @@ __export(config_exports, {
31
31
  migrateConfig: () => migrateConfig,
32
32
  saveConfig: () => saveConfig
33
33
  });
34
- import { readFile, writeFile, mkdir } from "fs/promises";
34
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
35
35
  import { readFileSync, existsSync, renameSync } from "fs";
36
36
  import path from "path";
37
37
  import os from "os";
@@ -157,6 +157,9 @@ async function saveConfig(config) {
157
157
  await mkdir(dir, { recursive: true });
158
158
  const configPath = path.join(dir, "config.json");
159
159
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
160
+ if (config.cloud?.apiKey) {
161
+ await chmod(configPath, 384);
162
+ }
160
163
  }
161
164
  async function loadConfigFrom(configPath) {
162
165
  const raw = await readFile(configPath, "utf-8");
@@ -276,6 +279,10 @@ import path4 from "path";
276
279
  import { fileURLToPath } from "url";
277
280
  function handleData(chunk) {
278
281
  _buffer += chunk.toString();
282
+ if (_buffer.length > MAX_BUFFER) {
283
+ _buffer = "";
284
+ return;
285
+ }
279
286
  let newlineIdx;
280
287
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
281
288
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -583,7 +590,7 @@ function disconnectClient() {
583
590
  entry.resolve({ error: "Client disconnected" });
584
591
  }
585
592
  }
586
- 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;
593
+ 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;
587
594
  var init_exe_daemon_client = __esm({
588
595
  "src/lib/exe-daemon-client.ts"() {
589
596
  "use strict";
@@ -600,6 +607,7 @@ var init_exe_daemon_client = __esm({
600
607
  _requestCount = 0;
601
608
  HEALTH_CHECK_INTERVAL = 100;
602
609
  _pending = /* @__PURE__ */ new Map();
610
+ MAX_BUFFER = 1e7;
603
611
  }
604
612
  });
605
613
 
@@ -692,6 +700,14 @@ import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync5
692
700
  import { randomUUID as randomUUID2 } from "crypto";
693
701
  import path5 from "path";
694
702
  import { jwtVerify, importSPKI } from "jose";
703
+ async function fetchRetry(url, init) {
704
+ try {
705
+ return await fetch(url, init);
706
+ } catch {
707
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
708
+ return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
709
+ }
710
+ }
695
711
  function loadDeviceId() {
696
712
  const deviceJsonPath = path5.join(EXE_AI_DIR, "device.json");
697
713
  try {
@@ -723,7 +739,7 @@ function loadLicense() {
723
739
  }
724
740
  function saveLicense(apiKey) {
725
741
  mkdirSync(EXE_AI_DIR, { recursive: true });
726
- writeFileSync(LICENSE_PATH, apiKey.trim(), "utf8");
742
+ writeFileSync(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
727
743
  }
728
744
  async function verifyLicenseJwt(token) {
729
745
  try {
@@ -775,7 +791,7 @@ function cacheResponse(token) {
775
791
  async function validateLicense(apiKey, deviceId) {
776
792
  const did = deviceId ?? loadDeviceId();
777
793
  try {
778
- const res = await fetch(`${API_BASE}/auth/activate`, {
794
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
779
795
  method: "POST",
780
796
  headers: { "Content-Type": "application/json" },
781
797
  body: JSON.stringify({ apiKey, deviceId: did }),
@@ -866,7 +882,7 @@ async function assertVpsLicense(opts) {
866
882
  let explicitRejection = false;
867
883
  let transientFailure = false;
868
884
  try {
869
- const res = await fetch(`${API_BASE}/auth/activate`, {
885
+ const res = await fetchRetry(`${API_BASE}/auth/activate`, {
870
886
  method: "POST",
871
887
  headers: { "Content-Type": "application/json" },
872
888
  body: JSON.stringify({ apiKey, deviceId }),
@@ -968,7 +984,7 @@ function stopLicenseRevalidation() {
968
984
  _revalTimer = null;
969
985
  }
970
986
  }
971
- 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;
987
+ 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;
972
988
  var init_license = __esm({
973
989
  "src/lib/license.ts"() {
974
990
  "use strict";
@@ -977,6 +993,7 @@ var init_license = __esm({
977
993
  CACHE_PATH = path5.join(EXE_AI_DIR, "license-cache.json");
978
994
  DEVICE_ID_PATH = path5.join(EXE_AI_DIR, "device-id");
979
995
  API_BASE = "https://askexe.com/cloud";
996
+ RETRY_DELAY_MS = 500;
980
997
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
981
998
  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
982
999
  4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
@@ -1090,15 +1107,20 @@ function addEmployee(employees, employee) {
1090
1107
  }
1091
1108
  return [...employees, normalized];
1092
1109
  }
1110
+ function findExeBin() {
1111
+ try {
1112
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
1113
+ } catch {
1114
+ return null;
1115
+ }
1116
+ }
1093
1117
  function registerBinSymlinks(name) {
1094
1118
  const created = [];
1095
1119
  const skipped = [];
1096
1120
  const errors = [];
1097
- let exeBinPath;
1098
- try {
1099
- exeBinPath = execSync("which exe", { encoding: "utf-8" }).trim();
1100
- } catch {
1101
- errors.push("Could not find 'exe' in PATH");
1121
+ const exeBinPath = findExeBin();
1122
+ if (!exeBinPath) {
1123
+ errors.push("Could not find 'exe-os' in PATH");
1102
1124
  return { created, skipped, errors };
1103
1125
  }
1104
1126
  const binDir = path6.dirname(exeBinPath);
@@ -2443,19 +2465,20 @@ ${PLAN_MODE_COMPAT}
2443
2465
  init_config();
2444
2466
  import crypto2 from "crypto";
2445
2467
  import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3, unlinkSync as unlinkSync3 } from "fs";
2446
- import os2 from "os";
2468
+ import os3 from "os";
2447
2469
  import path8 from "path";
2448
2470
  import { createInterface } from "readline";
2449
2471
 
2450
2472
  // src/lib/keychain.ts
2451
- import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod } from "fs/promises";
2473
+ import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
2452
2474
  import { existsSync as existsSync2 } from "fs";
2453
2475
  import path2 from "path";
2476
+ import os2 from "os";
2454
2477
  import crypto from "crypto";
2455
2478
  var SERVICE = "exe-mem";
2456
2479
  var ACCOUNT = "master-key";
2457
2480
  function getKeyDir() {
2458
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(process.env.HOME ?? "/tmp", ".exe-os");
2481
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(os2.homedir(), ".exe-os");
2459
2482
  }
2460
2483
  function getKeyPath() {
2461
2484
  return path2.join(getKeyDir(), "master.key");
@@ -2503,7 +2526,7 @@ async function setMasterKey(key) {
2503
2526
  await mkdir2(dir, { recursive: true });
2504
2527
  const keyPath = getKeyPath();
2505
2528
  await writeFile2(keyPath, b64 + "\n", "utf-8");
2506
- await chmod(keyPath, 384);
2529
+ await chmod2(keyPath, 384);
2507
2530
  }
2508
2531
 
2509
2532
  // src/lib/model-downloader.ts
@@ -2594,7 +2617,7 @@ async function fileHash(filePath) {
2594
2617
  }
2595
2618
 
2596
2619
  // src/lib/setup-wizard.ts
2597
- var SETUP_STATE_PATH = path8.join(os2.homedir(), ".exe-os", "setup-state.json");
2620
+ var SETUP_STATE_PATH = path8.join(os3.homedir(), ".exe-os", "setup-state.json");
2598
2621
  function loadSetupState() {
2599
2622
  try {
2600
2623
  return JSON.parse(readFileSync6(SETUP_STATE_PATH, "utf8"));
@@ -2685,12 +2708,23 @@ async function runSetupWizard(opts = {}) {
2685
2708
  try {
2686
2709
  const { loadDeviceId: loadDeviceId2 } = await Promise.resolve().then(() => (init_license(), license_exports));
2687
2710
  const deviceId = loadDeviceId2();
2688
- const res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
2689
- method: "POST",
2690
- headers: { "Content-Type": "application/json" },
2691
- body: JSON.stringify({ deviceId }),
2692
- signal: AbortSignal.timeout(1e4)
2693
- });
2711
+ let res;
2712
+ try {
2713
+ res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
2714
+ method: "POST",
2715
+ headers: { "Content-Type": "application/json" },
2716
+ body: JSON.stringify({ deviceId }),
2717
+ signal: AbortSignal.timeout(1e4)
2718
+ });
2719
+ } catch {
2720
+ await new Promise((r) => setTimeout(r, 500));
2721
+ res = await fetch("https://askexe.com/cloud/auth/auto-provision", {
2722
+ method: "POST",
2723
+ headers: { "Content-Type": "application/json" },
2724
+ body: JSON.stringify({ deviceId }),
2725
+ signal: AbortSignal.timeout(1e4)
2726
+ });
2727
+ }
2694
2728
  if (res.ok) {
2695
2729
  const data = await res.json();
2696
2730
  if (data.apiKey) {
@@ -2748,7 +2782,7 @@ async function runSetupWizard(opts = {}) {
2748
2782
  await saveConfig(config);
2749
2783
  log("");
2750
2784
  try {
2751
- const claudeJsonPath = path8.join(os2.homedir(), ".claude.json");
2785
+ const claudeJsonPath = path8.join(os3.homedir(), ".claude.json");
2752
2786
  let claudeJson = {};
2753
2787
  try {
2754
2788
  claudeJson = JSON.parse(readFileSync6(claudeJsonPath, "utf8"));
@@ -2756,7 +2790,7 @@ async function runSetupWizard(opts = {}) {
2756
2790
  }
2757
2791
  if (!claudeJson.projects) claudeJson.projects = {};
2758
2792
  const projects = claudeJson.projects;
2759
- for (const dir of [process.cwd(), os2.homedir()]) {
2793
+ for (const dir of [process.cwd(), os3.homedir()]) {
2760
2794
  if (!projects[dir]) projects[dir] = {};
2761
2795
  projects[dir].hasTrustDialogAccepted = true;
2762
2796
  }