@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
@@ -107,6 +107,7 @@ async function ensureSchema() {
107
107
  const client = getRawClient();
108
108
  await client.execute("PRAGMA journal_mode = WAL");
109
109
  await client.execute("PRAGMA busy_timeout = 30000");
110
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
110
111
  try {
111
112
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
112
113
  } catch {
@@ -907,15 +908,15 @@ var init_database = __esm({
907
908
  });
908
909
 
909
910
  // src/lib/config.ts
910
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
911
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
911
912
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
912
913
  import path2 from "path";
913
- import os from "os";
914
+ import os2 from "os";
914
915
  function resolveDataDir() {
915
916
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
916
917
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
917
- const newDir = path2.join(os.homedir(), ".exe-os");
918
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
918
+ const newDir = path2.join(os2.homedir(), ".exe-os");
919
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
919
920
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
920
921
  try {
921
922
  renameSync(legacyDir, newDir);
@@ -1002,7 +1003,7 @@ async function loadConfig() {
1002
1003
  normalizeAutoUpdate(migratedCfg);
1003
1004
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
1004
1005
  if (config.dbPath.startsWith("~")) {
1005
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
1006
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
1006
1007
  }
1007
1008
  return config;
1008
1009
  } catch {
@@ -1339,7 +1340,7 @@ var init_shard_manager = __esm({
1339
1340
  // src/lib/notifications.ts
1340
1341
  import crypto2 from "crypto";
1341
1342
  import path4 from "path";
1342
- import os2 from "os";
1343
+ import os3 from "os";
1343
1344
  import {
1344
1345
  readFileSync as readFileSync2,
1345
1346
  readdirSync as readdirSync2,
@@ -1652,15 +1653,20 @@ function addEmployee(employees, employee) {
1652
1653
  }
1653
1654
  return [...employees, normalized];
1654
1655
  }
1656
+ function findExeBin() {
1657
+ try {
1658
+ return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
1659
+ } catch {
1660
+ return null;
1661
+ }
1662
+ }
1655
1663
  function registerBinSymlinks(name) {
1656
1664
  const created = [];
1657
1665
  const skipped = [];
1658
1666
  const errors = [];
1659
- let exeBinPath;
1660
- try {
1661
- exeBinPath = execSync2("which exe", { encoding: "utf-8" }).trim();
1662
- } catch {
1663
- errors.push("Could not find 'exe' in PATH");
1667
+ const exeBinPath = findExeBin();
1668
+ if (!exeBinPath) {
1669
+ errors.push("Could not find 'exe-os' in PATH");
1664
1670
  return { created, skipped, errors };
1665
1671
  }
1666
1672
  const binDir = path6.dirname(exeBinPath);
@@ -1700,7 +1706,7 @@ var init_employees = __esm({
1700
1706
  // src/lib/session-registry.ts
1701
1707
  import { readFileSync as readFileSync5, writeFileSync, mkdirSync as mkdirSync2, existsSync as existsSync7 } from "fs";
1702
1708
  import path7 from "path";
1703
- import os3 from "os";
1709
+ import os4 from "os";
1704
1710
  function registerSession(entry) {
1705
1711
  const dir = path7.dirname(REGISTRY_PATH);
1706
1712
  if (!existsSync7(dir)) {
@@ -1727,7 +1733,7 @@ var REGISTRY_PATH;
1727
1733
  var init_session_registry = __esm({
1728
1734
  "src/lib/session-registry.ts"() {
1729
1735
  "use strict";
1730
- REGISTRY_PATH = path7.join(os3.homedir(), ".exe-os", "session-registry.json");
1736
+ REGISTRY_PATH = path7.join(os4.homedir(), ".exe-os", "session-registry.json");
1731
1737
  }
1732
1738
  });
1733
1739
 
@@ -1949,7 +1955,7 @@ var init_provider_table = __esm({
1949
1955
  // src/lib/intercom-queue.ts
1950
1956
  import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, renameSync as renameSync2, existsSync as existsSync8, mkdirSync as mkdirSync3 } from "fs";
1951
1957
  import path8 from "path";
1952
- import os4 from "os";
1958
+ import os5 from "os";
1953
1959
  function ensureDir() {
1954
1960
  const dir = path8.dirname(QUEUE_PATH);
1955
1961
  if (!existsSync8(dir)) mkdirSync3(dir, { recursive: true });
@@ -1989,9 +1995,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
1989
1995
  var init_intercom_queue = __esm({
1990
1996
  "src/lib/intercom-queue.ts"() {
1991
1997
  "use strict";
1992
- QUEUE_PATH = path8.join(os4.homedir(), ".exe-os", "intercom-queue.json");
1998
+ QUEUE_PATH = path8.join(os5.homedir(), ".exe-os", "intercom-queue.json");
1993
1999
  TTL_MS = 60 * 60 * 1e3;
1994
- INTERCOM_LOG = path8.join(os4.homedir(), ".exe-os", "intercom.log");
2000
+ INTERCOM_LOG = path8.join(os5.homedir(), ".exe-os", "intercom.log");
1995
2001
  }
1996
2002
  });
1997
2003
 
@@ -2100,7 +2106,7 @@ var init_plan_limits = __esm({
2100
2106
  import { execFileSync as execFileSync2, execSync as execSync5 } from "child_process";
2101
2107
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, existsSync as existsSync11, appendFileSync } from "fs";
2102
2108
  import path11 from "path";
2103
- import os5 from "os";
2109
+ import os6 from "os";
2104
2110
  import { fileURLToPath } from "url";
2105
2111
  import { unlinkSync as unlinkSync2 } from "fs";
2106
2112
  function spawnLockPath(sessionName) {
@@ -2372,7 +2378,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
2372
2378
  const transport = getTransport();
2373
2379
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
2374
2380
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
2375
- const logDir = path11.join(os5.homedir(), ".exe-os", "session-logs");
2381
+ const logDir = path11.join(os6.homedir(), ".exe-os", "session-logs");
2376
2382
  const logFile = path11.join(logDir, `${instanceLabel}-${Date.now()}.log`);
2377
2383
  if (!existsSync11(logDir)) {
2378
2384
  mkdirSync5(logDir, { recursive: true });
@@ -2388,7 +2394,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
2388
2394
  } catch {
2389
2395
  }
2390
2396
  try {
2391
- const claudeJsonPath = path11.join(os5.homedir(), ".claude.json");
2397
+ const claudeJsonPath = path11.join(os6.homedir(), ".claude.json");
2392
2398
  let claudeJson = {};
2393
2399
  try {
2394
2400
  claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
@@ -2403,7 +2409,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
2403
2409
  } catch {
2404
2410
  }
2405
2411
  try {
2406
- const settingsDir = path11.join(os5.homedir(), ".claude", "projects");
2412
+ const settingsDir = path11.join(os6.homedir(), ".claude", "projects");
2407
2413
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
2408
2414
  const projSettingsDir = path11.join(settingsDir, normalizedKey);
2409
2415
  const settingsPath = path11.join(projSettingsDir, "settings.json");
@@ -2451,7 +2457,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
2451
2457
  let legacyFallbackWarned = false;
2452
2458
  if (!useExeAgent && !useBinSymlink) {
2453
2459
  const identityPath = path11.join(
2454
- os5.homedir(),
2460
+ os6.homedir(),
2455
2461
  ".exe-os",
2456
2462
  "identity",
2457
2463
  `${employeeName}.md`
@@ -2481,7 +2487,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
2481
2487
  }
2482
2488
  let sessionContextFlag = "";
2483
2489
  try {
2484
- const ctxDir = path11.join(os5.homedir(), ".exe-os", "session-cache");
2490
+ const ctxDir = path11.join(os6.homedir(), ".exe-os", "session-cache");
2485
2491
  mkdirSync5(ctxDir, { recursive: true });
2486
2492
  const ctxFile = path11.join(ctxDir, `session-context-${sessionName}.md`);
2487
2493
  const ctxContent = [
@@ -2592,11 +2598,11 @@ var init_tmux_routing = __esm({
2592
2598
  init_provider_table();
2593
2599
  init_intercom_queue();
2594
2600
  init_plan_limits();
2595
- SPAWN_LOCK_DIR = path11.join(os5.homedir(), ".exe-os", "spawn-locks");
2596
- SESSION_CACHE = path11.join(os5.homedir(), ".exe-os", "session-cache");
2601
+ SPAWN_LOCK_DIR = path11.join(os6.homedir(), ".exe-os", "spawn-locks");
2602
+ SESSION_CACHE = path11.join(os6.homedir(), ".exe-os", "session-cache");
2597
2603
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
2598
2604
  INTERCOM_DEBOUNCE_MS = 3e4;
2599
- INTERCOM_LOG2 = path11.join(os5.homedir(), ".exe-os", "intercom.log");
2605
+ INTERCOM_LOG2 = path11.join(os6.homedir(), ".exe-os", "intercom.log");
2600
2606
  DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
2601
2607
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
2602
2608
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
@@ -2825,11 +2831,12 @@ init_database();
2825
2831
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
2826
2832
  import { existsSync } from "fs";
2827
2833
  import path from "path";
2834
+ import os from "os";
2828
2835
  import crypto from "crypto";
2829
2836
  var SERVICE = "exe-mem";
2830
2837
  var ACCOUNT = "master-key";
2831
2838
  function getKeyDir() {
2832
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
2839
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
2833
2840
  }
2834
2841
  function getKeyPath() {
2835
2842
  return path.join(getKeyDir(), "master.key");
@@ -2866,6 +2873,30 @@ async function getMasterKey() {
2866
2873
 
2867
2874
  // src/lib/store.ts
2868
2875
  init_config();
2876
+ var INIT_MAX_RETRIES = 3;
2877
+ var INIT_RETRY_DELAY_MS = 1e3;
2878
+ function isBusyError2(err) {
2879
+ if (err instanceof Error) {
2880
+ const msg = err.message.toLowerCase();
2881
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
2882
+ }
2883
+ return false;
2884
+ }
2885
+ async function retryOnBusy2(fn, label) {
2886
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
2887
+ try {
2888
+ return await fn();
2889
+ } catch (err) {
2890
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
2891
+ process.stderr.write(
2892
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
2893
+ `
2894
+ );
2895
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
2896
+ }
2897
+ }
2898
+ throw new Error("unreachable");
2899
+ }
2869
2900
  var _pendingRecords = [];
2870
2901
  var _batchSize = 20;
2871
2902
  var _flushIntervalMs = 1e4;
@@ -2900,14 +2931,17 @@ async function initStore(options) {
2900
2931
  dbPath,
2901
2932
  encryptionKey: hexKey
2902
2933
  });
2903
- await ensureSchema();
2934
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
2904
2935
  try {
2905
2936
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
2906
2937
  initShardManager2(hexKey);
2907
2938
  } catch {
2908
2939
  }
2909
2940
  const client = getClient();
2910
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
2941
+ const vResult = await retryOnBusy2(
2942
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
2943
+ "version-query"
2944
+ );
2911
2945
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
2912
2946
  }
2913
2947
 
@@ -119,6 +119,7 @@ async function ensureSchema() {
119
119
  const client = getRawClient();
120
120
  await client.execute("PRAGMA journal_mode = WAL");
121
121
  await client.execute("PRAGMA busy_timeout = 30000");
122
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
122
123
  try {
123
124
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
124
125
  } catch {
@@ -930,9 +931,10 @@ var init_database = __esm({
930
931
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
931
932
  import { existsSync } from "fs";
932
933
  import path from "path";
934
+ import os from "os";
933
935
  import crypto from "crypto";
934
936
  function getKeyDir() {
935
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
937
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
936
938
  }
937
939
  function getKeyPath() {
938
940
  return path.join(getKeyDir(), "master.key");
@@ -976,15 +978,15 @@ var init_keychain = __esm({
976
978
  });
977
979
 
978
980
  // src/lib/config.ts
979
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
981
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
980
982
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
981
983
  import path2 from "path";
982
- import os from "os";
984
+ import os2 from "os";
983
985
  function resolveDataDir() {
984
986
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
985
987
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
986
- const newDir = path2.join(os.homedir(), ".exe-os");
987
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
988
+ const newDir = path2.join(os2.homedir(), ".exe-os");
989
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
988
990
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
989
991
  try {
990
992
  renameSync(legacyDir, newDir);
@@ -1071,7 +1073,7 @@ async function loadConfig() {
1071
1073
  normalizeAutoUpdate(migratedCfg);
1072
1074
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
1073
1075
  if (config.dbPath.startsWith("~")) {
1074
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
1076
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
1075
1077
  }
1076
1078
  return config;
1077
1079
  } catch {
@@ -1422,6 +1424,28 @@ __export(store_exports, {
1422
1424
  vectorToBlob: () => vectorToBlob,
1423
1425
  writeMemory: () => writeMemory
1424
1426
  });
1427
+ function isBusyError2(err) {
1428
+ if (err instanceof Error) {
1429
+ const msg = err.message.toLowerCase();
1430
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1431
+ }
1432
+ return false;
1433
+ }
1434
+ async function retryOnBusy2(fn, label) {
1435
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1436
+ try {
1437
+ return await fn();
1438
+ } catch (err) {
1439
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1440
+ process.stderr.write(
1441
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1442
+ `
1443
+ );
1444
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1445
+ }
1446
+ }
1447
+ throw new Error("unreachable");
1448
+ }
1425
1449
  async function initStore(options) {
1426
1450
  if (_flushTimer !== null) {
1427
1451
  clearInterval(_flushTimer);
@@ -1450,14 +1474,17 @@ async function initStore(options) {
1450
1474
  dbPath,
1451
1475
  encryptionKey: hexKey
1452
1476
  });
1453
- await ensureSchema();
1477
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1454
1478
  try {
1455
1479
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1456
1480
  initShardManager2(hexKey);
1457
1481
  } catch {
1458
1482
  }
1459
1483
  const client = getClient();
1460
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1484
+ const vResult = await retryOnBusy2(
1485
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1486
+ "version-query"
1487
+ );
1461
1488
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1462
1489
  }
1463
1490
  function classifyTier(record) {
@@ -1837,7 +1864,7 @@ async function getMemoryCardinality(agentId) {
1837
1864
  return 0;
1838
1865
  }
1839
1866
  }
1840
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1867
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1841
1868
  var init_store = __esm({
1842
1869
  "src/lib/store.ts"() {
1843
1870
  "use strict";
@@ -1845,6 +1872,8 @@ var init_store = __esm({
1845
1872
  init_database();
1846
1873
  init_keychain();
1847
1874
  init_config();
1875
+ INIT_MAX_RETRIES = 3;
1876
+ INIT_RETRY_DELAY_MS = 1e3;
1848
1877
  _pendingRecords = [];
1849
1878
  _batchSize = 20;
1850
1879
  _flushIntervalMs = 1e4;
@@ -24,7 +24,7 @@ __export(config_exports, {
24
24
  migrateConfig: () => migrateConfig,
25
25
  saveConfig: () => saveConfig
26
26
  });
27
- import { readFile, writeFile, mkdir } from "fs/promises";
27
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
28
28
  import { readFileSync, existsSync, renameSync } from "fs";
29
29
  import path from "path";
30
30
  import os from "os";
@@ -150,6 +150,9 @@ async function saveConfig(config) {
150
150
  await mkdir(dir, { recursive: true });
151
151
  const configPath = path.join(dir, "config.json");
152
152
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
153
+ if (config.cloud?.apiKey) {
154
+ await chmod(configPath, 384);
155
+ }
153
156
  }
154
157
  async function loadConfigFrom(configPath) {
155
158
  const raw = await readFile(configPath, "utf-8");
@@ -348,6 +351,7 @@ async function ensureSchema() {
348
351
  const client = getRawClient();
349
352
  await client.execute("PRAGMA journal_mode = WAL");
350
353
  await client.execute("PRAGMA busy_timeout = 30000");
354
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
351
355
  try {
352
356
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
353
357
  } catch {
@@ -1156,12 +1160,13 @@ var init_database = __esm({
1156
1160
  });
1157
1161
 
1158
1162
  // src/lib/keychain.ts
1159
- import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod } from "fs/promises";
1163
+ import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
1160
1164
  import { existsSync as existsSync2 } from "fs";
1161
1165
  import path2 from "path";
1166
+ import os2 from "os";
1162
1167
  import crypto2 from "crypto";
1163
1168
  function getKeyDir() {
1164
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(process.env.HOME ?? "/tmp", ".exe-os");
1169
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path2.join(os2.homedir(), ".exe-os");
1165
1170
  }
1166
1171
  function getKeyPath() {
1167
1172
  return path2.join(getKeyDir(), "master.key");
@@ -1462,6 +1467,28 @@ __export(store_exports, {
1462
1467
  vectorToBlob: () => vectorToBlob,
1463
1468
  writeMemory: () => writeMemory
1464
1469
  });
1470
+ function isBusyError2(err) {
1471
+ if (err instanceof Error) {
1472
+ const msg = err.message.toLowerCase();
1473
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1474
+ }
1475
+ return false;
1476
+ }
1477
+ async function retryOnBusy2(fn, label) {
1478
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1479
+ try {
1480
+ return await fn();
1481
+ } catch (err) {
1482
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1483
+ process.stderr.write(
1484
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1485
+ `
1486
+ );
1487
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1488
+ }
1489
+ }
1490
+ throw new Error("unreachable");
1491
+ }
1465
1492
  async function initStore(options) {
1466
1493
  if (_flushTimer !== null) {
1467
1494
  clearInterval(_flushTimer);
@@ -1490,14 +1517,17 @@ async function initStore(options) {
1490
1517
  dbPath,
1491
1518
  encryptionKey: hexKey
1492
1519
  });
1493
- await ensureSchema();
1520
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1494
1521
  try {
1495
1522
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1496
1523
  initShardManager2(hexKey);
1497
1524
  } catch {
1498
1525
  }
1499
1526
  const client = getClient();
1500
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1527
+ const vResult = await retryOnBusy2(
1528
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1529
+ "version-query"
1530
+ );
1501
1531
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1502
1532
  }
1503
1533
  function classifyTier(record) {
@@ -1877,7 +1907,7 @@ async function getMemoryCardinality(agentId) {
1877
1907
  return 0;
1878
1908
  }
1879
1909
  }
1880
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1910
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1881
1911
  var init_store = __esm({
1882
1912
  "src/lib/store.ts"() {
1883
1913
  "use strict";
@@ -1885,6 +1915,8 @@ var init_store = __esm({
1885
1915
  init_database();
1886
1916
  init_keychain();
1887
1917
  init_config();
1918
+ INIT_MAX_RETRIES = 3;
1919
+ INIT_RETRY_DELAY_MS = 1e3;
1888
1920
  _pendingRecords = [];
1889
1921
  _batchSize = 20;
1890
1922
  _flushIntervalMs = 1e4;
@@ -1992,6 +2024,10 @@ import path4 from "path";
1992
2024
  import { fileURLToPath } from "url";
1993
2025
  function handleData(chunk) {
1994
2026
  _buffer += chunk.toString();
2027
+ if (_buffer.length > MAX_BUFFER) {
2028
+ _buffer = "";
2029
+ return;
2030
+ }
1995
2031
  let newlineIdx;
1996
2032
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1997
2033
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -2299,7 +2335,7 @@ function disconnectClient() {
2299
2335
  entry.resolve({ error: "Client disconnected" });
2300
2336
  }
2301
2337
  }
2302
- 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;
2338
+ 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;
2303
2339
  var init_exe_daemon_client = __esm({
2304
2340
  "src/lib/exe-daemon-client.ts"() {
2305
2341
  "use strict";
@@ -2316,6 +2352,7 @@ var init_exe_daemon_client = __esm({
2316
2352
  _requestCount = 0;
2317
2353
  HEALTH_CHECK_INTERVAL = 100;
2318
2354
  _pending = /* @__PURE__ */ new Map();
2355
+ MAX_BUFFER = 1e7;
2319
2356
  }
2320
2357
  });
2321
2358
 
@@ -3340,13 +3377,17 @@ var timeout = setTimeout(() => {
3340
3377
  process.exit(0);
3341
3378
  }, 5e3);
3342
3379
  timeout.unref();
3380
+ var MAX_INPUT_SIZE = 1e6;
3343
3381
  var input = "";
3344
3382
  process.stdin.setEncoding("utf8");
3345
3383
  process.stdin.on("data", (chunk) => {
3346
- input += chunk;
3384
+ if (input.length < MAX_INPUT_SIZE) input += chunk;
3347
3385
  });
3348
3386
  process.stdin.on("end", async () => {
3349
3387
  try {
3388
+ if (input.length >= MAX_INPUT_SIZE) {
3389
+ process.exit(0);
3390
+ }
3350
3391
  const data = JSON.parse(input);
3351
3392
  if (!detectError(data)) {
3352
3393
  process.exit(0);
@@ -9,7 +9,7 @@ import { execSync as execSync2 } from "child_process";
9
9
  import path2 from "path";
10
10
 
11
11
  // src/lib/config.ts
12
- import { readFile, writeFile, mkdir } from "fs/promises";
12
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
13
13
  import { readFileSync, existsSync, renameSync } from "fs";
14
14
  import path from "path";
15
15
  import os from "os";
@@ -188,10 +188,11 @@ var CLI_TIMEOUT_MS = 4e3;
188
188
  var EXE_AGENT_ID = "exe";
189
189
  var watchdog = setTimeout(() => process.exit(0), HOOK_TIMEOUT_MS);
190
190
  watchdog.unref();
191
+ var MAX_INPUT_SIZE = 1e6;
191
192
  var input = "";
192
193
  process.stdin.setEncoding("utf8");
193
194
  process.stdin.on("data", (chunk) => {
194
- input += chunk;
195
+ if (input.length < MAX_INPUT_SIZE) input += chunk;
195
196
  });
196
197
  process.stdin.on("end", () => {
197
198
  void input;