@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
@@ -96,6 +96,7 @@ async function ensureSchema() {
96
96
  const client = getRawClient();
97
97
  await client.execute("PRAGMA journal_mode = WAL");
98
98
  await client.execute("PRAGMA busy_timeout = 30000");
99
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
99
100
  try {
100
101
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
101
102
  } catch {
@@ -896,15 +897,15 @@ var init_database = __esm({
896
897
  });
897
898
 
898
899
  // src/lib/config.ts
899
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
900
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
900
901
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
901
902
  import path2 from "path";
902
- import os from "os";
903
+ import os2 from "os";
903
904
  function resolveDataDir() {
904
905
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
905
906
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
906
- const newDir = path2.join(os.homedir(), ".exe-os");
907
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
907
+ const newDir = path2.join(os2.homedir(), ".exe-os");
908
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
908
909
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
909
910
  try {
910
911
  renameSync(legacyDir, newDir);
@@ -991,7 +992,7 @@ async function loadConfig() {
991
992
  normalizeAutoUpdate(migratedCfg);
992
993
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
993
994
  if (config.dbPath.startsWith("~")) {
994
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
995
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
995
996
  }
996
997
  return config;
997
998
  } catch {
@@ -1327,12 +1328,12 @@ var init_shard_manager = __esm({
1327
1328
 
1328
1329
  // src/lib/session-registry.ts
1329
1330
  import path4 from "path";
1330
- import os2 from "os";
1331
+ import os3 from "os";
1331
1332
  var REGISTRY_PATH;
1332
1333
  var init_session_registry = __esm({
1333
1334
  "src/lib/session-registry.ts"() {
1334
1335
  "use strict";
1335
- REGISTRY_PATH = path4.join(os2.homedir(), ".exe-os", "session-registry.json");
1336
+ REGISTRY_PATH = path4.join(os3.homedir(), ".exe-os", "session-registry.json");
1336
1337
  }
1337
1338
  });
1338
1339
 
@@ -1383,14 +1384,14 @@ var init_provider_table = __esm({
1383
1384
  // src/lib/intercom-queue.ts
1384
1385
  import { readFileSync as readFileSync2, writeFileSync, renameSync as renameSync2, existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
1385
1386
  import path5 from "path";
1386
- import os3 from "os";
1387
+ import os4 from "os";
1387
1388
  var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
1388
1389
  var init_intercom_queue = __esm({
1389
1390
  "src/lib/intercom-queue.ts"() {
1390
1391
  "use strict";
1391
- QUEUE_PATH = path5.join(os3.homedir(), ".exe-os", "intercom-queue.json");
1392
+ QUEUE_PATH = path5.join(os4.homedir(), ".exe-os", "intercom-queue.json");
1392
1393
  TTL_MS = 60 * 60 * 1e3;
1393
- INTERCOM_LOG = path5.join(os3.homedir(), ".exe-os", "intercom.log");
1394
+ INTERCOM_LOG = path5.join(os4.homedir(), ".exe-os", "intercom.log");
1394
1395
  }
1395
1396
  });
1396
1397
 
@@ -1441,7 +1442,7 @@ var init_plan_limits = __esm({
1441
1442
 
1442
1443
  // src/lib/tmux-routing.ts
1443
1444
  import path9 from "path";
1444
- import os4 from "os";
1445
+ import os5 from "os";
1445
1446
  import { fileURLToPath } from "url";
1446
1447
  var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
1447
1448
  var init_tmux_routing = __esm({
@@ -1455,9 +1456,9 @@ var init_tmux_routing = __esm({
1455
1456
  init_provider_table();
1456
1457
  init_intercom_queue();
1457
1458
  init_plan_limits();
1458
- SPAWN_LOCK_DIR = path9.join(os4.homedir(), ".exe-os", "spawn-locks");
1459
- SESSION_CACHE = path9.join(os4.homedir(), ".exe-os", "session-cache");
1460
- INTERCOM_LOG2 = path9.join(os4.homedir(), ".exe-os", "intercom.log");
1459
+ SPAWN_LOCK_DIR = path9.join(os5.homedir(), ".exe-os", "spawn-locks");
1460
+ SESSION_CACHE = path9.join(os5.homedir(), ".exe-os", "session-cache");
1461
+ INTERCOM_LOG2 = path9.join(os5.homedir(), ".exe-os", "intercom.log");
1461
1462
  DEBOUNCE_FILE = path9.join(SESSION_CACHE, "intercom-debounce.json");
1462
1463
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
1463
1464
  }
@@ -1470,11 +1471,12 @@ init_database();
1470
1471
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1471
1472
  import { existsSync } from "fs";
1472
1473
  import path from "path";
1474
+ import os from "os";
1473
1475
  import crypto from "crypto";
1474
1476
  var SERVICE = "exe-mem";
1475
1477
  var ACCOUNT = "master-key";
1476
1478
  function getKeyDir() {
1477
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1479
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1478
1480
  }
1479
1481
  function getKeyPath() {
1480
1482
  return path.join(getKeyDir(), "master.key");
@@ -1511,6 +1513,30 @@ async function getMasterKey() {
1511
1513
 
1512
1514
  // src/lib/store.ts
1513
1515
  init_config();
1516
+ var INIT_MAX_RETRIES = 3;
1517
+ var INIT_RETRY_DELAY_MS = 1e3;
1518
+ function isBusyError2(err) {
1519
+ if (err instanceof Error) {
1520
+ const msg = err.message.toLowerCase();
1521
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1522
+ }
1523
+ return false;
1524
+ }
1525
+ async function retryOnBusy2(fn, label) {
1526
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1527
+ try {
1528
+ return await fn();
1529
+ } catch (err) {
1530
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1531
+ process.stderr.write(
1532
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1533
+ `
1534
+ );
1535
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1536
+ }
1537
+ }
1538
+ throw new Error("unreachable");
1539
+ }
1514
1540
  var _pendingRecords = [];
1515
1541
  var _batchSize = 20;
1516
1542
  var _flushIntervalMs = 1e4;
@@ -1545,14 +1571,17 @@ async function initStore(options) {
1545
1571
  dbPath,
1546
1572
  encryptionKey: hexKey
1547
1573
  });
1548
- await ensureSchema();
1574
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1549
1575
  try {
1550
1576
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1551
1577
  initShardManager2(hexKey);
1552
1578
  } catch {
1553
1579
  }
1554
1580
  const client = getClient();
1555
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1581
+ const vResult = await retryOnBusy2(
1582
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1583
+ "version-query"
1584
+ );
1556
1585
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1557
1586
  }
1558
1587
 
@@ -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 {
@@ -1319,11 +1320,12 @@ async function ensureSchema() {
1319
1320
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1320
1321
  import { existsSync } from "fs";
1321
1322
  import path from "path";
1323
+ import os from "os";
1322
1324
  import crypto from "crypto";
1323
1325
  var SERVICE = "exe-mem";
1324
1326
  var ACCOUNT = "master-key";
1325
1327
  function getKeyDir() {
1326
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1328
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1327
1329
  }
1328
1330
  function getKeyPath() {
1329
1331
  return path.join(getKeyDir(), "master.key");
@@ -1360,6 +1362,30 @@ async function getMasterKey() {
1360
1362
 
1361
1363
  // src/lib/store.ts
1362
1364
  init_config();
1365
+ var INIT_MAX_RETRIES = 3;
1366
+ var INIT_RETRY_DELAY_MS = 1e3;
1367
+ function isBusyError2(err) {
1368
+ if (err instanceof Error) {
1369
+ const msg = err.message.toLowerCase();
1370
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1371
+ }
1372
+ return false;
1373
+ }
1374
+ async function retryOnBusy2(fn, label) {
1375
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1376
+ try {
1377
+ return await fn();
1378
+ } catch (err) {
1379
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1380
+ process.stderr.write(
1381
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1382
+ `
1383
+ );
1384
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1385
+ }
1386
+ }
1387
+ throw new Error("unreachable");
1388
+ }
1363
1389
  var _pendingRecords = [];
1364
1390
  var _batchSize = 20;
1365
1391
  var _flushIntervalMs = 1e4;
@@ -1394,14 +1420,17 @@ async function initStore(options) {
1394
1420
  dbPath,
1395
1421
  encryptionKey: hexKey
1396
1422
  });
1397
- await ensureSchema();
1423
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1398
1424
  try {
1399
1425
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1400
1426
  initShardManager2(hexKey);
1401
1427
  } catch {
1402
1428
  }
1403
1429
  const client = getClient();
1404
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1430
+ const vResult = await retryOnBusy2(
1431
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1432
+ "version-query"
1433
+ );
1405
1434
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1406
1435
  }
1407
1436
 
@@ -97,6 +97,7 @@ async function ensureSchema() {
97
97
  const client = getRawClient();
98
98
  await client.execute("PRAGMA journal_mode = WAL");
99
99
  await client.execute("PRAGMA busy_timeout = 30000");
100
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
100
101
  try {
101
102
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
102
103
  } catch {
@@ -897,15 +898,15 @@ var init_database = __esm({
897
898
  });
898
899
 
899
900
  // src/lib/config.ts
900
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
901
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
901
902
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
902
903
  import path2 from "path";
903
- import os from "os";
904
+ import os2 from "os";
904
905
  function resolveDataDir() {
905
906
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
906
907
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
907
- const newDir = path2.join(os.homedir(), ".exe-os");
908
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
908
+ const newDir = path2.join(os2.homedir(), ".exe-os");
909
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
909
910
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
910
911
  try {
911
912
  renameSync(legacyDir, newDir);
@@ -992,7 +993,7 @@ async function loadConfig() {
992
993
  normalizeAutoUpdate(migratedCfg);
993
994
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
994
995
  if (config.dbPath.startsWith("~")) {
995
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
996
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
996
997
  }
997
998
  return config;
998
999
  } catch {
@@ -1343,7 +1344,7 @@ var init_employees = __esm({
1343
1344
  // src/lib/notifications.ts
1344
1345
  import crypto2 from "crypto";
1345
1346
  import path5 from "path";
1346
- import os2 from "os";
1347
+ import os3 from "os";
1347
1348
  import {
1348
1349
  readFileSync as readFileSync3,
1349
1350
  readdirSync as readdirSync2,
@@ -1373,12 +1374,12 @@ var init_tasks_crud = __esm({
1373
1374
 
1374
1375
  // src/lib/session-registry.ts
1375
1376
  import path7 from "path";
1376
- import os3 from "os";
1377
+ import os4 from "os";
1377
1378
  var REGISTRY_PATH;
1378
1379
  var init_session_registry = __esm({
1379
1380
  "src/lib/session-registry.ts"() {
1380
1381
  "use strict";
1381
- REGISTRY_PATH = path7.join(os3.homedir(), ".exe-os", "session-registry.json");
1382
+ REGISTRY_PATH = path7.join(os4.homedir(), ".exe-os", "session-registry.json");
1382
1383
  }
1383
1384
  });
1384
1385
 
@@ -1429,14 +1430,14 @@ var init_provider_table = __esm({
1429
1430
  // src/lib/intercom-queue.ts
1430
1431
  import { readFileSync as readFileSync5, writeFileSync, renameSync as renameSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
1431
1432
  import path8 from "path";
1432
- import os4 from "os";
1433
+ import os5 from "os";
1433
1434
  var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
1434
1435
  var init_intercom_queue = __esm({
1435
1436
  "src/lib/intercom-queue.ts"() {
1436
1437
  "use strict";
1437
- QUEUE_PATH = path8.join(os4.homedir(), ".exe-os", "intercom-queue.json");
1438
+ QUEUE_PATH = path8.join(os5.homedir(), ".exe-os", "intercom-queue.json");
1438
1439
  TTL_MS = 60 * 60 * 1e3;
1439
- INTERCOM_LOG = path8.join(os4.homedir(), ".exe-os", "intercom.log");
1440
+ INTERCOM_LOG = path8.join(os5.homedir(), ".exe-os", "intercom.log");
1440
1441
  }
1441
1442
  });
1442
1443
 
@@ -1473,7 +1474,7 @@ var init_plan_limits = __esm({
1473
1474
 
1474
1475
  // src/lib/tmux-routing.ts
1475
1476
  import path11 from "path";
1476
- import os5 from "os";
1477
+ import os6 from "os";
1477
1478
  import { fileURLToPath as fileURLToPath2 } from "url";
1478
1479
  var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
1479
1480
  var init_tmux_routing = __esm({
@@ -1487,9 +1488,9 @@ var init_tmux_routing = __esm({
1487
1488
  init_provider_table();
1488
1489
  init_intercom_queue();
1489
1490
  init_plan_limits();
1490
- SPAWN_LOCK_DIR = path11.join(os5.homedir(), ".exe-os", "spawn-locks");
1491
- SESSION_CACHE = path11.join(os5.homedir(), ".exe-os", "session-cache");
1492
- INTERCOM_LOG2 = path11.join(os5.homedir(), ".exe-os", "intercom.log");
1491
+ SPAWN_LOCK_DIR = path11.join(os6.homedir(), ".exe-os", "spawn-locks");
1492
+ SESSION_CACHE = path11.join(os6.homedir(), ".exe-os", "session-cache");
1493
+ INTERCOM_LOG2 = path11.join(os6.homedir(), ".exe-os", "intercom.log");
1493
1494
  DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
1494
1495
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
1495
1496
  }
@@ -1528,11 +1529,12 @@ init_database();
1528
1529
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1529
1530
  import { existsSync } from "fs";
1530
1531
  import path from "path";
1532
+ import os from "os";
1531
1533
  import crypto from "crypto";
1532
1534
  var SERVICE = "exe-mem";
1533
1535
  var ACCOUNT = "master-key";
1534
1536
  function getKeyDir() {
1535
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1537
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1536
1538
  }
1537
1539
  function getKeyPath() {
1538
1540
  return path.join(getKeyDir(), "master.key");
@@ -1569,6 +1571,30 @@ async function getMasterKey() {
1569
1571
 
1570
1572
  // src/lib/store.ts
1571
1573
  init_config();
1574
+ var INIT_MAX_RETRIES = 3;
1575
+ var INIT_RETRY_DELAY_MS = 1e3;
1576
+ function isBusyError2(err) {
1577
+ if (err instanceof Error) {
1578
+ const msg = err.message.toLowerCase();
1579
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1580
+ }
1581
+ return false;
1582
+ }
1583
+ async function retryOnBusy2(fn, label) {
1584
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1585
+ try {
1586
+ return await fn();
1587
+ } catch (err) {
1588
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1589
+ process.stderr.write(
1590
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1591
+ `
1592
+ );
1593
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1594
+ }
1595
+ }
1596
+ throw new Error("unreachable");
1597
+ }
1572
1598
  var _pendingRecords = [];
1573
1599
  var _batchSize = 20;
1574
1600
  var _flushIntervalMs = 1e4;
@@ -1603,14 +1629,17 @@ async function initStore(options) {
1603
1629
  dbPath,
1604
1630
  encryptionKey: hexKey
1605
1631
  });
1606
- await ensureSchema();
1632
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1607
1633
  try {
1608
1634
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1609
1635
  initShardManager2(hexKey);
1610
1636
  } catch {
1611
1637
  }
1612
1638
  const client = getClient();
1613
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1639
+ const vResult = await retryOnBusy2(
1640
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1641
+ "version-query"
1642
+ );
1614
1643
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1615
1644
  }
1616
1645
 
@@ -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,7 +932,7 @@ import { execSync } from "child_process";
931
932
  import path2 from "path";
932
933
 
933
934
  // src/lib/config.ts
934
- import { readFile, writeFile, mkdir } from "fs/promises";
935
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
935
936
  import { readFileSync, existsSync, renameSync } from "fs";
936
937
  import path from "path";
937
938
  import os from "os";
@@ -1053,15 +1054,20 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
1053
1054
  await mkdir2(path2.dirname(employeesPath), { recursive: true });
1054
1055
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
1055
1056
  }
1057
+ function findExeBin() {
1058
+ try {
1059
+ return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
1060
+ } catch {
1061
+ return null;
1062
+ }
1063
+ }
1056
1064
  function registerBinSymlinks(name) {
1057
1065
  const created = [];
1058
1066
  const skipped = [];
1059
1067
  const errors = [];
1060
- let exeBinPath;
1061
- try {
1062
- exeBinPath = execSync("which exe", { encoding: "utf-8" }).trim();
1063
- } catch {
1064
- errors.push("Could not find 'exe' in PATH");
1068
+ const exeBinPath = findExeBin();
1069
+ if (!exeBinPath) {
1070
+ errors.push("Could not find 'exe-os' in PATH");
1065
1071
  return { created, skipped, errors };
1066
1072
  }
1067
1073
  const binDir = path2.dirname(exeBinPath);
@@ -1240,9 +1246,17 @@ async function renameEmployee(oldName, newName, opts = {}) {
1240
1246
  return { success: false, error: err instanceof Error ? err.message : String(err) };
1241
1247
  }
1242
1248
  }
1249
+ function findExeBin2() {
1250
+ try {
1251
+ return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
1252
+ } catch {
1253
+ return null;
1254
+ }
1255
+ }
1243
1256
  function removeOldSymlinks(name) {
1244
1257
  try {
1245
- const exeBinPath = execSync2("which exe", { encoding: "utf-8" }).trim();
1258
+ const exeBinPath = findExeBin2();
1259
+ if (!exeBinPath) return;
1246
1260
  const binDir = path3.dirname(exeBinPath);
1247
1261
  for (const suffix of ["", "-opencode"]) {
1248
1262
  const linkPath = path3.join(binDir, `${name}${suffix}`);
@@ -10,7 +10,7 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // src/lib/config.ts
13
- import { readFile, writeFile, mkdir } from "fs/promises";
13
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
14
14
  import { readFileSync, existsSync, renameSync } from "fs";
15
15
  import path from "path";
16
16
  import os from "os";
@@ -554,6 +554,7 @@ async function ensureSchema() {
554
554
  const client = getRawClient();
555
555
  await client.execute("PRAGMA journal_mode = WAL");
556
556
  await client.execute("PRAGMA busy_timeout = 30000");
557
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
557
558
  try {
558
559
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
559
560
  } catch {
@@ -1344,14 +1345,15 @@ async function ensureSchema() {
1344
1345
  }
1345
1346
 
1346
1347
  // src/lib/keychain.ts
1347
- import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod } from "fs/promises";
1348
+ import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
1348
1349
  import { existsSync as existsSync3 } from "fs";
1349
1350
  import path3 from "path";
1351
+ import os2 from "os";
1350
1352
  import crypto from "crypto";
1351
1353
  var SERVICE = "exe-mem";
1352
1354
  var ACCOUNT = "master-key";
1353
1355
  function getKeyDir() {
1354
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(process.env.HOME ?? "/tmp", ".exe-os");
1356
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os2.homedir(), ".exe-os");
1355
1357
  }
1356
1358
  function getKeyPath() {
1357
1359
  return path3.join(getKeyDir(), "master.key");
@@ -1388,6 +1390,30 @@ async function getMasterKey() {
1388
1390
 
1389
1391
  // src/lib/store.ts
1390
1392
  init_config();
1393
+ var INIT_MAX_RETRIES = 3;
1394
+ var INIT_RETRY_DELAY_MS = 1e3;
1395
+ function isBusyError2(err) {
1396
+ if (err instanceof Error) {
1397
+ const msg = err.message.toLowerCase();
1398
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1399
+ }
1400
+ return false;
1401
+ }
1402
+ async function retryOnBusy2(fn, label) {
1403
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1404
+ try {
1405
+ return await fn();
1406
+ } catch (err) {
1407
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1408
+ process.stderr.write(
1409
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1410
+ `
1411
+ );
1412
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1413
+ }
1414
+ }
1415
+ throw new Error("unreachable");
1416
+ }
1391
1417
  var _pendingRecords = [];
1392
1418
  var _batchSize = 20;
1393
1419
  var _flushIntervalMs = 1e4;
@@ -1422,14 +1448,17 @@ async function initStore(options) {
1422
1448
  dbPath,
1423
1449
  encryptionKey: hexKey
1424
1450
  });
1425
- await ensureSchema();
1451
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1426
1452
  try {
1427
1453
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1428
1454
  initShardManager2(hexKey);
1429
1455
  } catch {
1430
1456
  }
1431
1457
  const client = getClient();
1432
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1458
+ const vResult = await retryOnBusy2(
1459
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1460
+ "version-query"
1461
+ );
1433
1462
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1434
1463
  }
1435
1464
  function classifyTier(record) {