@askexenow/exe-os 0.8.37 → 0.8.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +17 -8
  2. package/dist/bin/backfill-conversations.js +112 -70
  3. package/dist/bin/backfill-responses.js +53 -18
  4. package/dist/bin/backfill-vectors.js +43 -16
  5. package/dist/bin/cleanup-stale-review-tasks.js +38 -16
  6. package/dist/bin/cli.js +790 -468
  7. package/dist/bin/exe-agent.js +19 -4
  8. package/dist/bin/exe-assign.js +46 -13
  9. package/dist/bin/exe-boot.js +288 -129
  10. package/dist/bin/exe-call.js +20 -10
  11. package/dist/bin/exe-cloud.js +135 -30
  12. package/dist/bin/exe-dispatch.js +1 -1
  13. package/dist/bin/exe-doctor.js +38 -16
  14. package/dist/bin/exe-export-behaviors.js +43 -21
  15. package/dist/bin/exe-forget.js +39 -17
  16. package/dist/bin/exe-gateway.js +159 -50
  17. package/dist/bin/exe-heartbeat.js +53 -31
  18. package/dist/bin/exe-kill.js +40 -18
  19. package/dist/bin/exe-launch-agent.js +109 -36
  20. package/dist/bin/exe-link.js +196 -87
  21. package/dist/bin/exe-new-employee.js +56 -17
  22. package/dist/bin/exe-pending-messages.js +47 -25
  23. package/dist/bin/exe-pending-notifications.js +38 -16
  24. package/dist/bin/exe-pending-reviews.js +51 -29
  25. package/dist/bin/exe-rename.js +21 -7
  26. package/dist/bin/exe-review.js +41 -13
  27. package/dist/bin/exe-search.js +57 -21
  28. package/dist/bin/exe-session-cleanup.js +67 -31
  29. package/dist/bin/exe-settings.js +63 -2
  30. package/dist/bin/exe-status.js +35 -13
  31. package/dist/bin/exe-team.js +35 -13
  32. package/dist/bin/git-sweep.js +45 -17
  33. package/dist/bin/graph-backfill.js +38 -16
  34. package/dist/bin/graph-export.js +38 -16
  35. package/dist/bin/install.js +10 -1
  36. package/dist/bin/scan-tasks.js +47 -19
  37. package/dist/bin/setup.js +444 -259
  38. package/dist/bin/shard-migrate.js +38 -16
  39. package/dist/bin/wiki-sync.js +40 -17
  40. package/dist/gateway/index.js +113 -48
  41. package/dist/hooks/bug-report-worker.js +66 -39
  42. package/dist/hooks/commit-complete.js +45 -17
  43. package/dist/hooks/error-recall.js +60 -20
  44. package/dist/hooks/exe-heartbeat-hook.js +3 -2
  45. package/dist/hooks/ingest-worker.js +174 -45
  46. package/dist/hooks/ingest.js +74 -28
  47. package/dist/hooks/instructions-loaded.js +46 -17
  48. package/dist/hooks/notification.js +44 -15
  49. package/dist/hooks/post-compact.js +44 -15
  50. package/dist/hooks/pre-compact.js +42 -14
  51. package/dist/hooks/pre-tool-use.js +59 -22
  52. package/dist/hooks/prompt-ingest-worker.js +75 -14
  53. package/dist/hooks/prompt-submit.js +75 -32
  54. package/dist/hooks/response-ingest-worker.js +76 -15
  55. package/dist/hooks/session-end.js +54 -22
  56. package/dist/hooks/session-start.js +57 -20
  57. package/dist/hooks/stop.js +44 -15
  58. package/dist/hooks/subagent-stop.js +44 -15
  59. package/dist/hooks/summary-worker.js +339 -106
  60. package/dist/index.js +94 -23
  61. package/dist/lib/cloud-sync.js +191 -80
  62. package/dist/lib/config.js +4 -1
  63. package/dist/lib/consolidation.js +5 -4
  64. package/dist/lib/database.js +1 -0
  65. package/dist/lib/device-registry.js +2 -1
  66. package/dist/lib/embedder.js +9 -1
  67. package/dist/lib/employee-templates.js +5 -0
  68. package/dist/lib/employees.js +11 -6
  69. package/dist/lib/exe-daemon-client.js +6 -1
  70. package/dist/lib/exe-daemon.js +95 -36
  71. package/dist/lib/hybrid-search.js +57 -21
  72. package/dist/lib/identity-templates.js +16 -7
  73. package/dist/lib/identity.js +1 -1
  74. package/dist/lib/keychain.js +2 -1
  75. package/dist/lib/license.js +56 -6
  76. package/dist/lib/messaging.js +1 -1
  77. package/dist/lib/reminders.js +2 -2
  78. package/dist/lib/schedules.js +38 -16
  79. package/dist/lib/skill-learning.js +1 -1
  80. package/dist/lib/store.js +44 -16
  81. package/dist/lib/tasks.js +1 -1
  82. package/dist/lib/tmux-routing.js +1 -1
  83. package/dist/mcp/server.js +280 -155
  84. package/dist/mcp/tools/complete-reminder.js +1 -1
  85. package/dist/mcp/tools/create-task.js +14 -6
  86. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  87. package/dist/mcp/tools/list-reminders.js +1 -1
  88. package/dist/mcp/tools/list-tasks.js +36 -28
  89. package/dist/mcp/tools/send-message.js +1 -1
  90. package/dist/mcp/tools/update-task.js +1 -1
  91. package/dist/runtime/index.js +42 -8
  92. package/dist/tui/App.js +220 -99
  93. package/package.json +5 -3
@@ -1,12 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
- }) : x)(function(x) {
7
- if (typeof require !== "undefined") return require.apply(this, arguments);
8
- throw Error('Dynamic require of "' + x + '" is not supported');
9
- });
10
4
  var __esm = (fn, res) => function __init() {
11
5
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
6
  };
@@ -103,6 +97,7 @@ async function ensureSchema() {
103
97
  const client = getRawClient();
104
98
  await client.execute("PRAGMA journal_mode = WAL");
105
99
  await client.execute("PRAGMA busy_timeout = 30000");
100
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
106
101
  try {
107
102
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
108
103
  } catch {
@@ -903,15 +898,15 @@ var init_database = __esm({
903
898
  });
904
899
 
905
900
  // src/lib/config.ts
906
- 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";
907
902
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
908
903
  import path2 from "path";
909
- import os from "os";
904
+ import os2 from "os";
910
905
  function resolveDataDir() {
911
906
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
912
907
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
913
- const newDir = path2.join(os.homedir(), ".exe-os");
914
- 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");
915
910
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
916
911
  try {
917
912
  renameSync(legacyDir, newDir);
@@ -998,7 +993,7 @@ async function loadConfig() {
998
993
  normalizeAutoUpdate(migratedCfg);
999
994
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
1000
995
  if (config.dbPath.startsWith("~")) {
1001
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
996
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
1002
997
  }
1003
998
  return config;
1004
999
  } catch {
@@ -1105,7 +1100,7 @@ __export(shard_manager_exports, {
1105
1100
  shardExists: () => shardExists
1106
1101
  });
1107
1102
  import path3 from "path";
1108
- import { existsSync as existsSync3, mkdirSync } from "fs";
1103
+ import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
1109
1104
  import { createClient as createClient2 } from "@libsql/client";
1110
1105
  function initShardManager(encryptionKey) {
1111
1106
  _encryptionKey = encryptionKey;
@@ -1144,8 +1139,7 @@ function shardExists(projectName) {
1144
1139
  }
1145
1140
  function listShards() {
1146
1141
  if (!existsSync3(SHARDS_DIR)) return [];
1147
- const { readdirSync: readdirSync3 } = __require("fs");
1148
- return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1142
+ return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1149
1143
  }
1150
1144
  async function ensureShardSchema(client) {
1151
1145
  await client.execute("PRAGMA journal_mode = WAL");
@@ -1350,10 +1344,10 @@ var init_employees = __esm({
1350
1344
  // src/lib/notifications.ts
1351
1345
  import crypto2 from "crypto";
1352
1346
  import path5 from "path";
1353
- import os2 from "os";
1347
+ import os3 from "os";
1354
1348
  import {
1355
1349
  readFileSync as readFileSync3,
1356
- readdirSync,
1350
+ readdirSync as readdirSync2,
1357
1351
  unlinkSync,
1358
1352
  existsSync as existsSync5,
1359
1353
  rmdirSync
@@ -1380,12 +1374,12 @@ var init_tasks_crud = __esm({
1380
1374
 
1381
1375
  // src/lib/session-registry.ts
1382
1376
  import path7 from "path";
1383
- import os3 from "os";
1377
+ import os4 from "os";
1384
1378
  var REGISTRY_PATH;
1385
1379
  var init_session_registry = __esm({
1386
1380
  "src/lib/session-registry.ts"() {
1387
1381
  "use strict";
1388
- REGISTRY_PATH = path7.join(os3.homedir(), ".exe-os", "session-registry.json");
1382
+ REGISTRY_PATH = path7.join(os4.homedir(), ".exe-os", "session-registry.json");
1389
1383
  }
1390
1384
  });
1391
1385
 
@@ -1436,14 +1430,14 @@ var init_provider_table = __esm({
1436
1430
  // src/lib/intercom-queue.ts
1437
1431
  import { readFileSync as readFileSync5, writeFileSync, renameSync as renameSync2, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
1438
1432
  import path8 from "path";
1439
- import os4 from "os";
1433
+ import os5 from "os";
1440
1434
  var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
1441
1435
  var init_intercom_queue = __esm({
1442
1436
  "src/lib/intercom-queue.ts"() {
1443
1437
  "use strict";
1444
- QUEUE_PATH = path8.join(os4.homedir(), ".exe-os", "intercom-queue.json");
1438
+ QUEUE_PATH = path8.join(os5.homedir(), ".exe-os", "intercom-queue.json");
1445
1439
  TTL_MS = 60 * 60 * 1e3;
1446
- INTERCOM_LOG = path8.join(os4.homedir(), ".exe-os", "intercom.log");
1440
+ INTERCOM_LOG = path8.join(os5.homedir(), ".exe-os", "intercom.log");
1447
1441
  }
1448
1442
  });
1449
1443
 
@@ -1480,7 +1474,7 @@ var init_plan_limits = __esm({
1480
1474
 
1481
1475
  // src/lib/tmux-routing.ts
1482
1476
  import path11 from "path";
1483
- import os5 from "os";
1477
+ import os6 from "os";
1484
1478
  import { fileURLToPath as fileURLToPath2 } from "url";
1485
1479
  var SPAWN_LOCK_DIR, SESSION_CACHE, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS;
1486
1480
  var init_tmux_routing = __esm({
@@ -1494,9 +1488,9 @@ var init_tmux_routing = __esm({
1494
1488
  init_provider_table();
1495
1489
  init_intercom_queue();
1496
1490
  init_plan_limits();
1497
- SPAWN_LOCK_DIR = path11.join(os5.homedir(), ".exe-os", "spawn-locks");
1498
- SESSION_CACHE = path11.join(os5.homedir(), ".exe-os", "session-cache");
1499
- 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");
1500
1494
  DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
1501
1495
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
1502
1496
  }
@@ -1504,7 +1498,7 @@ var init_tmux_routing = __esm({
1504
1498
 
1505
1499
  // src/lib/tasks-review.ts
1506
1500
  import path12 from "path";
1507
- import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
1501
+ import { existsSync as existsSync10, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
1508
1502
  async function listPendingReviews(limit) {
1509
1503
  const client = getClient();
1510
1504
  const result = await client.execute({
@@ -1535,11 +1529,12 @@ init_database();
1535
1529
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1536
1530
  import { existsSync } from "fs";
1537
1531
  import path from "path";
1532
+ import os from "os";
1538
1533
  import crypto from "crypto";
1539
1534
  var SERVICE = "exe-mem";
1540
1535
  var ACCOUNT = "master-key";
1541
1536
  function getKeyDir() {
1542
- 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");
1543
1538
  }
1544
1539
  function getKeyPath() {
1545
1540
  return path.join(getKeyDir(), "master.key");
@@ -1576,6 +1571,30 @@ async function getMasterKey() {
1576
1571
 
1577
1572
  // src/lib/store.ts
1578
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
+ }
1579
1598
  var _pendingRecords = [];
1580
1599
  var _batchSize = 20;
1581
1600
  var _flushIntervalMs = 1e4;
@@ -1610,14 +1629,17 @@ async function initStore(options) {
1610
1629
  dbPath,
1611
1630
  encryptionKey: hexKey
1612
1631
  });
1613
- await ensureSchema();
1632
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1614
1633
  try {
1615
1634
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1616
1635
  initShardManager2(hexKey);
1617
1636
  } catch {
1618
1637
  }
1619
1638
  const client = getClient();
1620
- 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
+ );
1621
1643
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1622
1644
  }
1623
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}`);
@@ -1,12 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
- }) : x)(function(x) {
7
- if (typeof require !== "undefined") return require.apply(this, arguments);
8
- throw Error('Dynamic require of "' + x + '" is not supported');
9
- });
10
4
  var __esm = (fn, res) => function __init() {
11
5
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
6
  };
@@ -16,7 +10,7 @@ var __export = (target, all) => {
16
10
  };
17
11
 
18
12
  // src/lib/config.ts
19
- import { readFile, writeFile, mkdir } from "fs/promises";
13
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
20
14
  import { readFileSync, existsSync, renameSync } from "fs";
21
15
  import path from "path";
22
16
  import os from "os";
@@ -218,7 +212,7 @@ __export(shard_manager_exports, {
218
212
  shardExists: () => shardExists
219
213
  });
220
214
  import path4 from "path";
221
- import { existsSync as existsSync4, mkdirSync } from "fs";
215
+ import { existsSync as existsSync4, mkdirSync, readdirSync } from "fs";
222
216
  import { createClient as createClient2 } from "@libsql/client";
223
217
  function initShardManager(encryptionKey) {
224
218
  _encryptionKey = encryptionKey;
@@ -257,7 +251,6 @@ function shardExists(projectName) {
257
251
  }
258
252
  function listShards() {
259
253
  if (!existsSync4(SHARDS_DIR)) return [];
260
- const { readdirSync } = __require("fs");
261
254
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
262
255
  }
263
256
  async function ensureShardSchema(client) {
@@ -561,6 +554,7 @@ async function ensureSchema() {
561
554
  const client = getRawClient();
562
555
  await client.execute("PRAGMA journal_mode = WAL");
563
556
  await client.execute("PRAGMA busy_timeout = 30000");
557
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
564
558
  try {
565
559
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
566
560
  } catch {
@@ -1351,14 +1345,15 @@ async function ensureSchema() {
1351
1345
  }
1352
1346
 
1353
1347
  // src/lib/keychain.ts
1354
- 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";
1355
1349
  import { existsSync as existsSync3 } from "fs";
1356
1350
  import path3 from "path";
1351
+ import os2 from "os";
1357
1352
  import crypto from "crypto";
1358
1353
  var SERVICE = "exe-mem";
1359
1354
  var ACCOUNT = "master-key";
1360
1355
  function getKeyDir() {
1361
- 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");
1362
1357
  }
1363
1358
  function getKeyPath() {
1364
1359
  return path3.join(getKeyDir(), "master.key");
@@ -1395,6 +1390,30 @@ async function getMasterKey() {
1395
1390
 
1396
1391
  // src/lib/store.ts
1397
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
+ }
1398
1417
  var _pendingRecords = [];
1399
1418
  var _batchSize = 20;
1400
1419
  var _flushIntervalMs = 1e4;
@@ -1429,14 +1448,17 @@ async function initStore(options) {
1429
1448
  dbPath,
1430
1449
  encryptionKey: hexKey
1431
1450
  });
1432
- await ensureSchema();
1451
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1433
1452
  try {
1434
1453
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1435
1454
  initShardManager2(hexKey);
1436
1455
  } catch {
1437
1456
  }
1438
1457
  const client = getClient();
1439
- 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
+ );
1440
1462
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1441
1463
  }
1442
1464
  function classifyTier(record) {
@@ -1479,6 +1501,12 @@ async function writeMemory(record) {
1479
1501
  supersedes_id: record.supersedes_id ?? null
1480
1502
  };
1481
1503
  _pendingRecords.push(dbRow);
1504
+ const MAX_PENDING = 1e3;
1505
+ if (_pendingRecords.length > MAX_PENDING) {
1506
+ const dropped = _pendingRecords.length - MAX_PENDING;
1507
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
1508
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
1509
+ }
1482
1510
  if (_flushTimer === null) {
1483
1511
  _flushTimer = setInterval(() => {
1484
1512
  void flushBatch();
@@ -1,12 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
5
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
6
- }) : x)(function(x) {
7
- if (typeof require !== "undefined") return require.apply(this, arguments);
8
- throw Error('Dynamic require of "' + x + '" is not supported');
9
- });
10
4
  var __esm = (fn, res) => function __init() {
11
5
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
6
  };
@@ -112,6 +106,7 @@ async function ensureSchema() {
112
106
  const client = getRawClient();
113
107
  await client.execute("PRAGMA journal_mode = WAL");
114
108
  await client.execute("PRAGMA busy_timeout = 30000");
109
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
115
110
  try {
116
111
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
117
112
  } catch {
@@ -923,9 +918,10 @@ var init_database = __esm({
923
918
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
924
919
  import { existsSync } from "fs";
925
920
  import path from "path";
921
+ import os from "os";
926
922
  import crypto from "crypto";
927
923
  function getKeyDir() {
928
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
924
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
929
925
  }
930
926
  function getKeyPath() {
931
927
  return path.join(getKeyDir(), "master.key");
@@ -984,15 +980,15 @@ __export(config_exports, {
984
980
  migrateConfig: () => migrateConfig,
985
981
  saveConfig: () => saveConfig
986
982
  });
987
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
983
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
988
984
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
989
985
  import path2 from "path";
990
- import os from "os";
986
+ import os2 from "os";
991
987
  function resolveDataDir() {
992
988
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
993
989
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
994
- const newDir = path2.join(os.homedir(), ".exe-os");
995
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
990
+ const newDir = path2.join(os2.homedir(), ".exe-os");
991
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
996
992
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
997
993
  try {
998
994
  renameSync(legacyDir, newDir);
@@ -1079,7 +1075,7 @@ async function loadConfig() {
1079
1075
  normalizeAutoUpdate(migratedCfg);
1080
1076
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
1081
1077
  if (config.dbPath.startsWith("~")) {
1082
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
1078
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
1083
1079
  }
1084
1080
  return config;
1085
1081
  } catch {
@@ -1110,6 +1106,9 @@ async function saveConfig(config) {
1110
1106
  await mkdir2(dir, { recursive: true });
1111
1107
  const configPath = path2.join(dir, "config.json");
1112
1108
  await writeFile2(configPath, JSON.stringify(config, null, 2) + "\n");
1109
+ if (config.cloud?.apiKey) {
1110
+ await chmod2(configPath, 384);
1111
+ }
1113
1112
  }
1114
1113
  async function loadConfigFrom(configPath) {
1115
1114
  const raw = await readFile2(configPath, "utf-8");
@@ -1225,7 +1224,7 @@ __export(shard_manager_exports, {
1225
1224
  shardExists: () => shardExists
1226
1225
  });
1227
1226
  import path3 from "path";
1228
- import { existsSync as existsSync3, mkdirSync } from "fs";
1227
+ import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
1229
1228
  import { createClient as createClient2 } from "@libsql/client";
1230
1229
  function initShardManager(encryptionKey) {
1231
1230
  _encryptionKey = encryptionKey;
@@ -1264,8 +1263,7 @@ function shardExists(projectName) {
1264
1263
  }
1265
1264
  function listShards() {
1266
1265
  if (!existsSync3(SHARDS_DIR)) return [];
1267
- const { readdirSync: readdirSync2 } = __require("fs");
1268
- return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1266
+ return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1269
1267
  }
1270
1268
  async function ensureShardSchema(client) {
1271
1269
  await client.execute("PRAGMA journal_mode = WAL");
@@ -1470,6 +1468,28 @@ __export(store_exports, {
1470
1468
  vectorToBlob: () => vectorToBlob,
1471
1469
  writeMemory: () => writeMemory
1472
1470
  });
1471
+ function isBusyError2(err) {
1472
+ if (err instanceof Error) {
1473
+ const msg = err.message.toLowerCase();
1474
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1475
+ }
1476
+ return false;
1477
+ }
1478
+ async function retryOnBusy2(fn, label) {
1479
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1480
+ try {
1481
+ return await fn();
1482
+ } catch (err) {
1483
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1484
+ process.stderr.write(
1485
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1486
+ `
1487
+ );
1488
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1489
+ }
1490
+ }
1491
+ throw new Error("unreachable");
1492
+ }
1473
1493
  async function initStore(options) {
1474
1494
  if (_flushTimer !== null) {
1475
1495
  clearInterval(_flushTimer);
@@ -1498,14 +1518,17 @@ async function initStore(options) {
1498
1518
  dbPath,
1499
1519
  encryptionKey: hexKey
1500
1520
  });
1501
- await ensureSchema();
1521
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1502
1522
  try {
1503
1523
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1504
1524
  initShardManager2(hexKey);
1505
1525
  } catch {
1506
1526
  }
1507
1527
  const client = getClient();
1508
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1528
+ const vResult = await retryOnBusy2(
1529
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1530
+ "version-query"
1531
+ );
1509
1532
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1510
1533
  }
1511
1534
  function classifyTier(record) {
@@ -1548,6 +1571,12 @@ async function writeMemory(record) {
1548
1571
  supersedes_id: record.supersedes_id ?? null
1549
1572
  };
1550
1573
  _pendingRecords.push(dbRow);
1574
+ const MAX_PENDING = 1e3;
1575
+ if (_pendingRecords.length > MAX_PENDING) {
1576
+ const dropped = _pendingRecords.length - MAX_PENDING;
1577
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
1578
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
1579
+ }
1551
1580
  if (_flushTimer === null) {
1552
1581
  _flushTimer = setInterval(() => {
1553
1582
  void flushBatch();
@@ -1879,7 +1908,7 @@ async function getMemoryCardinality(agentId) {
1879
1908
  return 0;
1880
1909
  }
1881
1910
  }
1882
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1911
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1883
1912
  var init_store = __esm({
1884
1913
  "src/lib/store.ts"() {
1885
1914
  "use strict";
@@ -1887,6 +1916,8 @@ var init_store = __esm({
1887
1916
  init_database();
1888
1917
  init_keychain();
1889
1918
  init_config();
1919
+ INIT_MAX_RETRIES = 3;
1920
+ INIT_RETRY_DELAY_MS = 1e3;
1890
1921
  _pendingRecords = [];
1891
1922
  _batchSize = 20;
1892
1923
  _flushIntervalMs = 1e4;
@@ -1994,6 +2025,10 @@ import path4 from "path";
1994
2025
  import { fileURLToPath } from "url";
1995
2026
  function handleData(chunk) {
1996
2027
  _buffer += chunk.toString();
2028
+ if (_buffer.length > MAX_BUFFER) {
2029
+ _buffer = "";
2030
+ return;
2031
+ }
1997
2032
  let newlineIdx;
1998
2033
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1999
2034
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -2301,7 +2336,7 @@ function disconnectClient() {
2301
2336
  entry.resolve({ error: "Client disconnected" });
2302
2337
  }
2303
2338
  }
2304
- 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;
2339
+ 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;
2305
2340
  var init_exe_daemon_client = __esm({
2306
2341
  "src/lib/exe-daemon-client.ts"() {
2307
2342
  "use strict";
@@ -2318,6 +2353,7 @@ var init_exe_daemon_client = __esm({
2318
2353
  _requestCount = 0;
2319
2354
  HEALTH_CHECK_INTERVAL = 100;
2320
2355
  _pending = /* @__PURE__ */ new Map();
2356
+ MAX_BUFFER = 1e7;
2321
2357
  }
2322
2358
  });
2323
2359
 
@@ -2446,7 +2482,7 @@ __export(file_grep_exports, {
2446
2482
  grepProjectFiles: () => grepProjectFiles
2447
2483
  });
2448
2484
  import { execSync as execSync2 } from "child_process";
2449
- import { readFileSync as readFileSync3, readdirSync, statSync as statSync2, existsSync as existsSync5 } from "fs";
2485
+ import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2, existsSync as existsSync5 } from "fs";
2450
2486
  import path6 from "path";
2451
2487
  import crypto2 from "crypto";
2452
2488
  function hasRipgrep() {
@@ -2591,7 +2627,7 @@ function collectFiles(root, patterns) {
2591
2627
  const basename = path6.basename(dir);
2592
2628
  if (EXCLUDE_DIRS.includes(basename)) return;
2593
2629
  try {
2594
- const entries = readdirSync(dir, { withFileTypes: true });
2630
+ const entries = readdirSync2(dir, { withFileTypes: true });
2595
2631
  for (const entry of entries) {
2596
2632
  if (files.length >= MAX_FILES) return;
2597
2633
  const rel = path6.join(relative, entry.name);