@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
package/README.md CHANGED
@@ -88,10 +88,19 @@ You talk to your COO. Your COO delegates. Each employee has:
88
88
  ### 1. Install
89
89
 
90
90
  ```bash
91
+ # macOS
92
+ xcode-select --install && brew install tmux
93
+ npm install -g @askexenow/exe-os
94
+
95
+ # Linux / WSL2
96
+ sudo apt install -y tmux git cmake g++ libsecret-1-dev curl
91
97
  npm install -g @askexenow/exe-os
98
+
99
+ # Windows — must use WSL2 (native Windows not supported)
100
+ # Run `wsl --install` in PowerShell first, then follow Linux steps inside WSL2
92
101
  ```
93
102
 
94
- Requires Node.js 20+ and tmux (`brew install tmux` on macOS).
103
+ Requires Node.js 22+ and tmux. See **[docs/install.md](docs/install.md)** for full platform-specific instructions and troubleshooting.
95
104
 
96
105
  ### 2. Run Setup
97
106
 
@@ -219,17 +228,17 @@ exe-os/
219
228
 
220
229
  ## Requirements
221
230
 
222
- - **Node.js 20+**
231
+ - **Node.js 22+**
223
232
  - **tmux** — for multi-agent session management
224
233
  - **Claude Code** subscription (Mode 1) or API key (Mode 2)
225
234
 
226
- ```bash
227
- # macOS
228
- brew install tmux node
235
+ | Platform | Quick Install |
236
+ |----------|-------------|
237
+ | macOS | `brew install tmux node` + Xcode CLI Tools |
238
+ | Linux | `apt install tmux git cmake g++ libsecret-1-dev` + Node.js 22+ |
239
+ | Windows | WSL2 required (`wsl --install`), then follow Linux steps |
229
240
 
230
- # Linux
231
- apt install tmux nodejs
232
- ```
241
+ Full guide: **[docs/install.md](docs/install.md)**
233
242
 
234
243
  ---
235
244
 
@@ -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 {
@@ -538,6 +538,7 @@ async function ensureSchema() {
538
538
  const client = getRawClient();
539
539
  await client.execute("PRAGMA journal_mode = WAL");
540
540
  await client.execute("PRAGMA busy_timeout = 30000");
541
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
541
542
  try {
542
543
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
543
544
  } catch {
@@ -1331,11 +1332,12 @@ async function ensureSchema() {
1331
1332
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1332
1333
  import { existsSync } from "fs";
1333
1334
  import path from "path";
1335
+ import os from "os";
1334
1336
  import crypto from "crypto";
1335
1337
  var SERVICE = "exe-mem";
1336
1338
  var ACCOUNT = "master-key";
1337
1339
  function getKeyDir() {
1338
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1340
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1339
1341
  }
1340
1342
  function getKeyPath() {
1341
1343
  return path.join(getKeyDir(), "master.key");
@@ -1372,6 +1374,30 @@ async function getMasterKey() {
1372
1374
 
1373
1375
  // src/lib/store.ts
1374
1376
  init_config();
1377
+ var INIT_MAX_RETRIES = 3;
1378
+ var INIT_RETRY_DELAY_MS = 1e3;
1379
+ function isBusyError2(err) {
1380
+ if (err instanceof Error) {
1381
+ const msg = err.message.toLowerCase();
1382
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1383
+ }
1384
+ return false;
1385
+ }
1386
+ async function retryOnBusy2(fn, label) {
1387
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1388
+ try {
1389
+ return await fn();
1390
+ } catch (err) {
1391
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1392
+ process.stderr.write(
1393
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1394
+ `
1395
+ );
1396
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1397
+ }
1398
+ }
1399
+ throw new Error("unreachable");
1400
+ }
1375
1401
  var _pendingRecords = [];
1376
1402
  var _batchSize = 20;
1377
1403
  var _flushIntervalMs = 1e4;
@@ -1406,14 +1432,17 @@ async function initStore(options) {
1406
1432
  dbPath,
1407
1433
  encryptionKey: hexKey
1408
1434
  });
1409
- await ensureSchema();
1435
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1410
1436
  try {
1411
1437
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1412
1438
  initShardManager2(hexKey);
1413
1439
  } catch {
1414
1440
  }
1415
1441
  const client = getClient();
1416
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1442
+ const vResult = await retryOnBusy2(
1443
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1444
+ "version-query"
1445
+ );
1417
1446
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1418
1447
  }
1419
1448
  function classifyTier(record) {
@@ -1630,8 +1659,13 @@ var _buffer = "";
1630
1659
  var _requestCount = 0;
1631
1660
  var HEALTH_CHECK_INTERVAL = 100;
1632
1661
  var _pending = /* @__PURE__ */ new Map();
1662
+ var MAX_BUFFER = 1e7;
1633
1663
  function handleData(chunk) {
1634
1664
  _buffer += chunk.toString();
1665
+ if (_buffer.length > MAX_BUFFER) {
1666
+ _buffer = "";
1667
+ return;
1668
+ }
1635
1669
  let newlineIdx;
1636
1670
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1637
1671
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -1945,10 +1979,12 @@ function isMainModule(importMetaUrl) {
1945
1979
  var TOOL_NAME = "backfill-conversation";
1946
1980
  var MIN_MESSAGES = 3;
1947
1981
  var MAX_SUMMARY_LENGTH = 4e3;
1982
+ var MAX_WALK_DEPTH = 10;
1948
1983
  async function findJsonlFiles(sinceDate, projectFilter) {
1949
1984
  const projectsDir = path5.join(homedir(), ".claude", "projects");
1950
1985
  const files = [];
1951
- async function walk(dir) {
1986
+ async function walk(dir, depth = 0) {
1987
+ if (depth > MAX_WALK_DEPTH) return;
1952
1988
  let entries;
1953
1989
  try {
1954
1990
  entries = await readdir(dir, { withFileTypes: true });
@@ -1959,7 +1995,7 @@ async function findJsonlFiles(sinceDate, projectFilter) {
1959
1995
  const full = path5.join(dir, entry.name);
1960
1996
  if (entry.isDirectory()) {
1961
1997
  if (entry.name === "subagents" || entry.name === "tool-results") continue;
1962
- await walk(full);
1998
+ await walk(full, depth + 1);
1963
1999
  } else if (entry.name.endsWith(".jsonl")) {
1964
2000
  try {
1965
2001
  const s = await stat(full);
@@ -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 {
@@ -537,6 +537,7 @@ async function ensureSchema() {
537
537
  const client = getRawClient();
538
538
  await client.execute("PRAGMA journal_mode = WAL");
539
539
  await client.execute("PRAGMA busy_timeout = 30000");
540
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
540
541
  try {
541
542
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
542
543
  } catch {
@@ -1330,11 +1331,12 @@ async function ensureSchema() {
1330
1331
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
1331
1332
  import { existsSync } from "fs";
1332
1333
  import path from "path";
1334
+ import os from "os";
1333
1335
  import crypto from "crypto";
1334
1336
  var SERVICE = "exe-mem";
1335
1337
  var ACCOUNT = "master-key";
1336
1338
  function getKeyDir() {
1337
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
1339
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
1338
1340
  }
1339
1341
  function getKeyPath() {
1340
1342
  return path.join(getKeyDir(), "master.key");
@@ -1371,6 +1373,30 @@ async function getMasterKey() {
1371
1373
 
1372
1374
  // src/lib/store.ts
1373
1375
  init_config();
1376
+ var INIT_MAX_RETRIES = 3;
1377
+ var INIT_RETRY_DELAY_MS = 1e3;
1378
+ function isBusyError2(err) {
1379
+ if (err instanceof Error) {
1380
+ const msg = err.message.toLowerCase();
1381
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1382
+ }
1383
+ return false;
1384
+ }
1385
+ async function retryOnBusy2(fn, label) {
1386
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
1387
+ try {
1388
+ return await fn();
1389
+ } catch (err) {
1390
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
1391
+ process.stderr.write(
1392
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
1393
+ `
1394
+ );
1395
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
1396
+ }
1397
+ }
1398
+ throw new Error("unreachable");
1399
+ }
1374
1400
  var _pendingRecords = [];
1375
1401
  var _batchSize = 20;
1376
1402
  var _flushIntervalMs = 1e4;
@@ -1405,14 +1431,17 @@ async function initStore(options) {
1405
1431
  dbPath,
1406
1432
  encryptionKey: hexKey
1407
1433
  });
1408
- await ensureSchema();
1434
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1409
1435
  try {
1410
1436
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1411
1437
  initShardManager2(hexKey);
1412
1438
  } catch {
1413
1439
  }
1414
1440
  const client = getClient();
1415
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
1441
+ const vResult = await retryOnBusy2(
1442
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
1443
+ "version-query"
1444
+ );
1416
1445
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1417
1446
  }
1418
1447
  function classifyTier(record) {
@@ -1629,8 +1658,13 @@ var _buffer = "";
1629
1658
  var _requestCount = 0;
1630
1659
  var HEALTH_CHECK_INTERVAL = 100;
1631
1660
  var _pending = /* @__PURE__ */ new Map();
1661
+ var MAX_BUFFER = 1e7;
1632
1662
  function handleData(chunk) {
1633
1663
  _buffer += chunk.toString();
1664
+ if (_buffer.length > MAX_BUFFER) {
1665
+ _buffer = "";
1666
+ return;
1667
+ }
1634
1668
  let newlineIdx;
1635
1669
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1636
1670
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -1945,11 +1979,13 @@ var MIN_LENGTH = 100;
1945
1979
  var MAX_LENGTH = 5e3;
1946
1980
  var SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1e3;
1947
1981
  process.env.EXE_EMBED_PRIORITY = "low";
1982
+ var MAX_WALK_DEPTH = 10;
1948
1983
  async function findRecentJsonlFiles() {
1949
1984
  const projectsDir = path5.join(homedir(), ".claude", "projects");
1950
1985
  const cutoff = Date.now() - SEVEN_DAYS_MS;
1951
1986
  const files = [];
1952
- async function walk(dir) {
1987
+ async function walk(dir, depth = 0) {
1988
+ if (depth > MAX_WALK_DEPTH) return;
1953
1989
  let entries;
1954
1990
  try {
1955
1991
  entries = await readdir(dir, { withFileTypes: true });
@@ -1959,7 +1995,7 @@ async function findRecentJsonlFiles() {
1959
1995
  for (const entry of entries) {
1960
1996
  const full = path5.join(dir, entry.name);
1961
1997
  if (entry.isDirectory()) {
1962
- await walk(full);
1998
+ await walk(full, depth + 1);
1963
1999
  } else if (entry.name.endsWith(".jsonl")) {
1964
2000
  try {
1965
2001
  const s = await stat(full);
@@ -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
  function vectorToBlob(vector) {
@@ -1429,8 +1458,13 @@ var _buffer = "";
1429
1458
  var _requestCount = 0;
1430
1459
  var HEALTH_CHECK_INTERVAL = 100;
1431
1460
  var _pending = /* @__PURE__ */ new Map();
1461
+ var MAX_BUFFER = 1e7;
1432
1462
  function handleData(chunk) {
1433
1463
  _buffer += chunk.toString();
1464
+ if (_buffer.length > MAX_BUFFER) {
1465
+ _buffer = "";
1466
+ return;
1467
+ }
1434
1468
  let newlineIdx;
1435
1469
  while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1436
1470
  const line = _buffer.slice(0, newlineIdx).trim();
@@ -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