@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,5 +1,5 @@
1
1
  // src/lib/config.ts
2
- import { readFile, writeFile, mkdir } from "fs/promises";
2
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
3
3
  import { readFileSync, existsSync, renameSync } from "fs";
4
4
  import path from "path";
5
5
  import os from "os";
@@ -170,8 +170,8 @@ function loadConfigSync() {
170
170
 
171
171
  // src/adapters/claude/hooks/ingest.ts
172
172
  import { spawn } from "child_process";
173
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync2, openSync, closeSync } from "fs";
174
- import path3 from "path";
173
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync2, openSync, closeSync } from "fs";
174
+ import path4 from "path";
175
175
  import { fileURLToPath } from "url";
176
176
 
177
177
  // src/adapters/claude/active-agent.ts
@@ -412,6 +412,44 @@ function errorFingerprint(toolName, errorText) {
412
412
  return crypto.createHash("sha256").update(`${toolName}:${normalized}`).digest("hex").slice(0, 16);
413
413
  }
414
414
 
415
+ // src/lib/worker-gate.ts
416
+ import { readdirSync as readdirSync2, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2, mkdirSync as mkdirSync2 } from "fs";
417
+ import path3 from "path";
418
+ var WORKER_PID_DIR = path3.join(EXE_AI_DIR, "worker-pids");
419
+ var MAX_CONCURRENT_WORKERS = 3;
420
+ function tryAcquireWorkerSlot() {
421
+ try {
422
+ mkdirSync2(WORKER_PID_DIR, { recursive: true });
423
+ const files = readdirSync2(WORKER_PID_DIR);
424
+ let alive = 0;
425
+ for (const f of files) {
426
+ if (!f.endsWith(".pid")) continue;
427
+ const dashIdx = f.lastIndexOf("-");
428
+ const pid = parseInt(f.slice(dashIdx + 1).replace(".pid", ""), 10);
429
+ if (isNaN(pid)) continue;
430
+ try {
431
+ process.kill(pid, 0);
432
+ alive++;
433
+ } catch {
434
+ try {
435
+ unlinkSync2(path3.join(WORKER_PID_DIR, f));
436
+ } catch {
437
+ }
438
+ }
439
+ }
440
+ return alive < MAX_CONCURRENT_WORKERS;
441
+ } catch {
442
+ return true;
443
+ }
444
+ }
445
+ function registerWorkerPid(pid) {
446
+ try {
447
+ mkdirSync2(WORKER_PID_DIR, { recursive: true });
448
+ writeFileSync2(path3.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
449
+ } catch {
450
+ }
451
+ }
452
+
415
453
  // src/adapters/claude/hooks/ingest.ts
416
454
  if (!process.env.AGENT_ID) {
417
455
  process.env.AGENT_ID = "default";
@@ -420,7 +458,7 @@ if (!process.env.AGENT_ID) {
420
458
  if (!loadConfigSync().autoIngestion) {
421
459
  process.exit(0);
422
460
  }
423
- var WORKER_LOG_PATH = path3.join(EXE_AI_DIR, "workers.log");
461
+ var WORKER_LOG_PATH = path4.join(EXE_AI_DIR, "workers.log");
424
462
  function openWorkerLog() {
425
463
  try {
426
464
  return openSync(WORKER_LOG_PATH, "a");
@@ -432,9 +470,9 @@ var ALLOWED_TOOL_RE = /^(Bash|Edit|Write|Read|Glob|Grep|Agent|mcp__.*)$/;
432
470
  var WRITE_TOOL_RE = /^(Bash|Edit|Write)$/;
433
471
  var SUMMARY_INTERVAL = 25;
434
472
  var MIN_WRITES_FOR_SUMMARY = 3;
435
- var COUNTER_DIR = path3.join(EXE_AI_DIR, "session-cache");
473
+ var COUNTER_DIR = path4.join(EXE_AI_DIR, "session-cache");
436
474
  function getCounterPath(sessionId) {
437
- return path3.join(COUNTER_DIR, `counter-${sessionId}.json`);
475
+ return path4.join(COUNTER_DIR, `counter-${sessionId}.json`);
438
476
  }
439
477
  function loadCounter(sessionId) {
440
478
  try {
@@ -446,8 +484,8 @@ function loadCounter(sessionId) {
446
484
  }
447
485
  function saveCounter(sessionId, counter) {
448
486
  try {
449
- mkdirSync2(COUNTER_DIR, { recursive: true });
450
- writeFileSync2(getCounterPath(sessionId), JSON.stringify(counter));
487
+ mkdirSync3(COUNTER_DIR, { recursive: true });
488
+ writeFileSync3(getCounterPath(sessionId), JSON.stringify(counter));
451
489
  } catch {
452
490
  }
453
491
  }
@@ -466,7 +504,7 @@ process.stdin.on("end", () => {
466
504
  process.exit(0);
467
505
  }
468
506
  const agent = getActiveAgent();
469
- if (/^(Read|Write|Edit)$/.test(data.tool_name) && agent.agentId !== "exe" && agent.agentId !== "default") {
507
+ if (/^(Read|Write|Edit)$/.test(data.tool_name) && agent.agentRole !== "COO" && agent.agentId !== "default") {
470
508
  const filePath = data.tool_input?.file_path ?? "";
471
509
  const exeMatch = filePath.match(/exe\/([^/]+)\//);
472
510
  if (exeMatch && exeMatch[1] !== agent.agentId) {
@@ -483,7 +521,7 @@ Do NOT read, write, or modify files in another employee's folder.`
483
521
  process.stdout.write(warning);
484
522
  }
485
523
  }
486
- if (/^(Read|Glob|Grep|Bash)$/.test(data.tool_name) && agent.agentId === "exe") {
524
+ if (/^(Read|Glob|Grep|Bash)$/.test(data.tool_name) && agent.agentRole === "COO") {
487
525
  const target = data.tool_input?.file_path ?? data.tool_input?.path ?? data.tool_input?.pattern ?? String(data.tool_input?.command ?? "");
488
526
  if (/exe\/exe\//.test(target)) {
489
527
  const warning = JSON.stringify({
@@ -498,7 +536,7 @@ Open reviews are tracked in the tasks table: SELECT COUNT(*) FROM tasks WHERE as
498
536
  process.stdout.write(warning);
499
537
  }
500
538
  }
501
- if (/^(Write|Bash)$/.test(data.tool_name) && agent.agentId !== "exe" && agent.agentId !== "default") {
539
+ if (/^(Write|Bash)$/.test(data.tool_name) && agent.agentRole !== "COO" && agent.agentId !== "default") {
502
540
  const filePath = data.tool_input?.file_path ?? String(data.tool_input?.command ?? "");
503
541
  const outputMatch = filePath.match(/exe\/output\/([^/\-]+)-([^/]+)/);
504
542
  if (outputMatch) {
@@ -522,7 +560,7 @@ Your output files must start with: exe/output/${agent.agentId}-`
522
560
  const classification = classifyError(errorText);
523
561
  if (classification === "system" && data.session_id) {
524
562
  const fp = errorFingerprint(data.tool_name, errorText);
525
- const fpFilePath = path3.join(COUNTER_DIR, `bug-fingerprints-${data.session_id}.json`);
563
+ const fpFilePath = path4.join(COUNTER_DIR, `bug-fingerprints-${data.session_id}.json`);
526
564
  let fpData = {
527
565
  seen: {},
528
566
  taskCount: 0,
@@ -547,13 +585,13 @@ Your output files must start with: exe/output/${agent.agentId}-`
547
585
  fpData.seen[fp] = { count: 1, firstAt: now, lastAt: now };
548
586
  fpData.taskCount++;
549
587
  fpData.lastTaskAt = now;
550
- const bugWorkerPath = path3.resolve(
551
- path3.dirname(fileURLToPath(import.meta.url)),
588
+ const bugWorkerPath = path4.resolve(
589
+ path4.dirname(fileURLToPath(import.meta.url)),
552
590
  "bug-report-worker.js"
553
591
  );
554
- if (existsSync2(bugWorkerPath)) {
592
+ if (existsSync2(bugWorkerPath) && tryAcquireWorkerSlot()) {
555
593
  const stderrFd2 = openWorkerLog();
556
- const projectName = process.cwd().split(path3.sep).pop() ?? "unknown";
594
+ const projectName = process.cwd().split(path4.sep).pop() ?? "unknown";
557
595
  const bugToolInput = data.tool_input ?? {};
558
596
  const bugWorker = spawn(process.execPath, [bugWorkerPath], {
559
597
  detached: true,
@@ -569,6 +607,7 @@ Your output files must start with: exe/output/${agent.agentId}-`
569
607
  BUG_PROJECT_NAME: projectName
570
608
  }
571
609
  });
610
+ if (bugWorker.pid) registerWorkerPid(bugWorker.pid);
572
611
  bugWorker.unref();
573
612
  if (typeof stderrFd2 === "number") try {
574
613
  closeSync(stderrFd2);
@@ -580,13 +619,13 @@ Your output files must start with: exe/output/${agent.agentId}-`
580
619
  }
581
620
  }
582
621
  try {
583
- mkdirSync2(COUNTER_DIR, { recursive: true });
584
- writeFileSync2(fpFilePath, JSON.stringify(fpData));
622
+ mkdirSync3(COUNTER_DIR, { recursive: true });
623
+ writeFileSync3(fpFilePath, JSON.stringify(fpData));
585
624
  } catch {
586
625
  }
587
626
  }
588
627
  }
589
- if (data.session_id && agent.agentId !== "exe" && agent.agentId !== "default") {
628
+ if (data.session_id && agent.agentRole !== "COO" && agent.agentId !== "default") {
590
629
  const counter = loadCounter(data.session_id);
591
630
  counter.total++;
592
631
  if (WRITE_TOOL_RE.test(data.tool_name)) {
@@ -598,11 +637,11 @@ Your output files must start with: exe/output/${agent.agentId}-`
598
637
  }
599
638
  const callsSinceLastSummary = counter.total - counter.lastSummaryAt;
600
639
  if (callsSinceLastSummary >= SUMMARY_INTERVAL && counter.writes >= MIN_WRITES_FOR_SUMMARY) {
601
- const summaryWorkerPath = path3.resolve(
602
- path3.dirname(fileURLToPath(import.meta.url)),
640
+ const summaryWorkerPath = path4.resolve(
641
+ path4.dirname(fileURLToPath(import.meta.url)),
603
642
  "summary-worker.js"
604
643
  );
605
- if (existsSync2(summaryWorkerPath)) {
644
+ if (existsSync2(summaryWorkerPath) && tryAcquireWorkerSlot()) {
606
645
  const stderrFd2 = openWorkerLog();
607
646
  const summaryWorker = spawn(process.execPath, [summaryWorkerPath], {
608
647
  detached: true,
@@ -616,6 +655,7 @@ Your output files must start with: exe/output/${agent.agentId}-`
616
655
  EXE_SUMMARY_WRITES: String(counter.writes)
617
656
  }
618
657
  });
658
+ if (summaryWorker.pid) registerWorkerPid(summaryWorker.pid);
619
659
  summaryWorker.unref();
620
660
  if (typeof stderrFd2 === "number") try {
621
661
  closeSync(stderrFd2);
@@ -643,13 +683,13 @@ Your output files must start with: exe/output/${agent.agentId}-`
643
683
  const bashOutput = typeof data.tool_response === "string" ? data.tool_response : JSON.stringify(data.tool_response ?? "");
644
684
  const commitMatch = bashOutput.match(/\[(\S+)\s+([a-f0-9]{7,40})\]\s+(.+)/);
645
685
  if (commitMatch) {
646
- const commitWorkerPath = path3.resolve(
647
- path3.dirname(fileURLToPath(import.meta.url)),
686
+ const commitWorkerPath = path4.resolve(
687
+ path4.dirname(fileURLToPath(import.meta.url)),
648
688
  "commit-complete.js"
649
689
  );
650
- if (existsSync2(commitWorkerPath)) {
690
+ if (existsSync2(commitWorkerPath) && tryAcquireWorkerSlot()) {
651
691
  const stderrFd2 = openWorkerLog();
652
- const projectName = process.cwd().split(path3.sep).pop() ?? "unknown";
692
+ const projectName = process.cwd().split(path4.sep).pop() ?? "unknown";
653
693
  const commitWorker = spawn(process.execPath, [commitWorkerPath], {
654
694
  detached: true,
655
695
  stdio: ["ignore", "ignore", stderrFd2],
@@ -661,6 +701,7 @@ Your output files must start with: exe/output/${agent.agentId}-`
661
701
  PROJECT_NAME: projectName
662
702
  }
663
703
  });
704
+ if (commitWorker.pid) registerWorkerPid(commitWorker.pid);
664
705
  commitWorker.unref();
665
706
  if (typeof stderrFd2 === "number") try {
666
707
  closeSync(stderrFd2);
@@ -672,16 +713,21 @@ Your output files must start with: exe/output/${agent.agentId}-`
672
713
  if (!ALLOWED_TOOL_RE.test(data.tool_name)) {
673
714
  process.exit(0);
674
715
  }
675
- const workerPath = path3.resolve(
676
- path3.dirname(fileURLToPath(import.meta.url)),
716
+ const workerPath = path4.resolve(
717
+ path4.dirname(fileURLToPath(import.meta.url)),
677
718
  "ingest-worker.js"
678
719
  );
720
+ if (!tryAcquireWorkerSlot()) {
721
+ process.stderr.write("[ingest] Worker concurrency limit reached, skipping ingestion\n");
722
+ process.exit(0);
723
+ }
679
724
  const stderrFd = openWorkerLog();
680
725
  const worker = spawn(process.execPath, [workerPath], {
681
726
  detached: true,
682
727
  stdio: ["pipe", "ignore", stderrFd],
683
728
  env: { ...process.env, AGENT_ID: agent.agentId, AGENT_ROLE: agent.agentRole }
684
729
  });
730
+ if (worker.pid) registerWorkerPid(worker.pid);
685
731
  worker.stdin.write(input);
686
732
  worker.stdin.end();
687
733
  worker.unref();
@@ -1,11 +1,5 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
9
3
  var __esm = (fn, res) => function __init() {
10
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
5
  };
@@ -15,7 +9,7 @@ var __export = (target, all) => {
15
9
  };
16
10
 
17
11
  // src/lib/config.ts
18
- import { readFile, writeFile, mkdir } from "fs/promises";
12
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
19
13
  import { readFileSync, existsSync, renameSync } from "fs";
20
14
  import path from "path";
21
15
  import os from "os";
@@ -305,6 +299,7 @@ async function ensureSchema() {
305
299
  const client = getRawClient();
306
300
  await client.execute("PRAGMA journal_mode = WAL");
307
301
  await client.execute("PRAGMA busy_timeout = 30000");
302
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
308
303
  try {
309
304
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
310
305
  } catch {
@@ -1122,12 +1117,13 @@ var init_memory = __esm({
1122
1117
  });
1123
1118
 
1124
1119
  // src/lib/keychain.ts
1125
- import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod } from "fs/promises";
1120
+ import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
1126
1121
  import { existsSync as existsSync3 } from "fs";
1127
1122
  import path4 from "path";
1123
+ import os2 from "os";
1128
1124
  import crypto from "crypto";
1129
1125
  function getKeyDir() {
1130
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(process.env.HOME ?? "/tmp", ".exe-os");
1126
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path4.join(os2.homedir(), ".exe-os");
1131
1127
  }
1132
1128
  function getKeyPath() {
1133
1129
  return path4.join(getKeyDir(), "master.key");
@@ -1184,7 +1180,7 @@ __export(shard_manager_exports, {
1184
1180
  shardExists: () => shardExists
1185
1181
  });
1186
1182
  import path5 from "path";
1187
- import { existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
1183
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync3 } from "fs";
1188
1184
  import { createClient as createClient2 } from "@libsql/client";
1189
1185
  function initShardManager(encryptionKey) {
1190
1186
  _encryptionKey = encryptionKey;
@@ -1223,7 +1219,6 @@ function shardExists(projectName) {
1223
1219
  }
1224
1220
  function listShards() {
1225
1221
  if (!existsSync4(SHARDS_DIR)) return [];
1226
- const { readdirSync: readdirSync3 } = __require("fs");
1227
1222
  return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1228
1223
  }
1229
1224
  async function ensureShardSchema(client) {
@@ -1429,6 +1424,28 @@ __export(store_exports, {
1429
1424
  vectorToBlob: () => vectorToBlob,
1430
1425
  writeMemory: () => writeMemory
1431
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
+ }
1432
1449
  async function initStore(options) {
1433
1450
  if (_flushTimer !== null) {
1434
1451
  clearInterval(_flushTimer);
@@ -1457,14 +1474,17 @@ async function initStore(options) {
1457
1474
  dbPath,
1458
1475
  encryptionKey: hexKey
1459
1476
  });
1460
- await ensureSchema();
1477
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1461
1478
  try {
1462
1479
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1463
1480
  initShardManager2(hexKey);
1464
1481
  } catch {
1465
1482
  }
1466
1483
  const client = getClient();
1467
- 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
+ );
1468
1488
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1469
1489
  }
1470
1490
  function classifyTier(record) {
@@ -1507,6 +1527,12 @@ async function writeMemory(record) {
1507
1527
  supersedes_id: record.supersedes_id ?? null
1508
1528
  };
1509
1529
  _pendingRecords.push(dbRow);
1530
+ const MAX_PENDING = 1e3;
1531
+ if (_pendingRecords.length > MAX_PENDING) {
1532
+ const dropped = _pendingRecords.length - MAX_PENDING;
1533
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
1534
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
1535
+ }
1510
1536
  if (_flushTimer === null) {
1511
1537
  _flushTimer = setInterval(() => {
1512
1538
  void flushBatch();
@@ -1838,7 +1864,7 @@ async function getMemoryCardinality(agentId) {
1838
1864
  return 0;
1839
1865
  }
1840
1866
  }
1841
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1867
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1842
1868
  var init_store = __esm({
1843
1869
  "src/lib/store.ts"() {
1844
1870
  "use strict";
@@ -1846,6 +1872,8 @@ var init_store = __esm({
1846
1872
  init_database();
1847
1873
  init_keychain();
1848
1874
  init_config();
1875
+ INIT_MAX_RETRIES = 3;
1876
+ INIT_RETRY_DELAY_MS = 1e3;
1849
1877
  _pendingRecords = [];
1850
1878
  _batchSize = 20;
1851
1879
  _flushIntervalMs = 1e4;
@@ -2055,10 +2083,11 @@ var timeout = setTimeout(() => {
2055
2083
  process.exit(0);
2056
2084
  }, 5e3);
2057
2085
  timeout.unref();
2086
+ var MAX_INPUT_SIZE = 1e6;
2058
2087
  var input = "";
2059
2088
  process.stdin.setEncoding("utf8");
2060
2089
  process.stdin.on("data", (chunk) => {
2061
- input += chunk;
2090
+ if (input.length < MAX_INPUT_SIZE) input += chunk;
2062
2091
  });
2063
2092
  process.stdin.on("end", async () => {
2064
2093
  try {
@@ -2101,8 +2130,8 @@ process.stdin.on("end", async () => {
2101
2130
  try {
2102
2131
  const { readFileSync: readFileSync4, existsSync: existsSync5 } = await import("fs");
2103
2132
  const { join } = await import("path");
2104
- const os2 = await import("os");
2105
- const claudeMd = join(os2.homedir(), ".claude", "CLAUDE.md");
2133
+ const os3 = await import("os");
2134
+ const claudeMd = join(os3.homedir(), ".claude", "CLAUDE.md");
2106
2135
  if (existsSync5(claudeMd)) {
2107
2136
  const content = readFileSync4(claudeMd, "utf8");
2108
2137
  const hasOrchRules = content.includes("exe-os:orchestration-start");
@@ -1,11 +1,5 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
9
3
  var __esm = (fn, res) => function __init() {
10
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
5
  };
@@ -15,7 +9,7 @@ var __export = (target, all) => {
15
9
  };
16
10
 
17
11
  // src/lib/config.ts
18
- import { readFile, writeFile, mkdir } from "fs/promises";
12
+ import { readFile, writeFile, mkdir, chmod } from "fs/promises";
19
13
  import { readFileSync, existsSync, renameSync } from "fs";
20
14
  import path from "path";
21
15
  import os from "os";
@@ -314,6 +308,7 @@ async function ensureSchema() {
314
308
  const client = getRawClient();
315
309
  await client.execute("PRAGMA journal_mode = WAL");
316
310
  await client.execute("PRAGMA busy_timeout = 30000");
311
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
317
312
  try {
318
313
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
319
314
  } catch {
@@ -1122,12 +1117,13 @@ var init_database = __esm({
1122
1117
  });
1123
1118
 
1124
1119
  // src/lib/keychain.ts
1125
- import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod } from "fs/promises";
1120
+ import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
1126
1121
  import { existsSync as existsSync2 } from "fs";
1127
1122
  import path3 from "path";
1123
+ import os2 from "os";
1128
1124
  import crypto from "crypto";
1129
1125
  function getKeyDir() {
1130
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(process.env.HOME ?? "/tmp", ".exe-os");
1126
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path3.join(os2.homedir(), ".exe-os");
1131
1127
  }
1132
1128
  function getKeyPath() {
1133
1129
  return path3.join(getKeyDir(), "master.key");
@@ -1184,7 +1180,7 @@ __export(shard_manager_exports, {
1184
1180
  shardExists: () => shardExists
1185
1181
  });
1186
1182
  import path4 from "path";
1187
- import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
1183
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync as readdirSync2 } from "fs";
1188
1184
  import { createClient as createClient2 } from "@libsql/client";
1189
1185
  function initShardManager(encryptionKey) {
1190
1186
  _encryptionKey = encryptionKey;
@@ -1223,7 +1219,6 @@ function shardExists(projectName) {
1223
1219
  }
1224
1220
  function listShards() {
1225
1221
  if (!existsSync3(SHARDS_DIR)) return [];
1226
- const { readdirSync: readdirSync2 } = __require("fs");
1227
1222
  return readdirSync2(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1228
1223
  }
1229
1224
  async function ensureShardSchema(client) {
@@ -1429,6 +1424,28 @@ __export(store_exports, {
1429
1424
  vectorToBlob: () => vectorToBlob,
1430
1425
  writeMemory: () => writeMemory
1431
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
+ }
1432
1449
  async function initStore(options) {
1433
1450
  if (_flushTimer !== null) {
1434
1451
  clearInterval(_flushTimer);
@@ -1457,14 +1474,17 @@ async function initStore(options) {
1457
1474
  dbPath,
1458
1475
  encryptionKey: hexKey
1459
1476
  });
1460
- await ensureSchema();
1477
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
1461
1478
  try {
1462
1479
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
1463
1480
  initShardManager2(hexKey);
1464
1481
  } catch {
1465
1482
  }
1466
1483
  const client = getClient();
1467
- 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
+ );
1468
1488
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
1469
1489
  }
1470
1490
  function classifyTier(record) {
@@ -1507,6 +1527,12 @@ async function writeMemory(record) {
1507
1527
  supersedes_id: record.supersedes_id ?? null
1508
1528
  };
1509
1529
  _pendingRecords.push(dbRow);
1530
+ const MAX_PENDING = 1e3;
1531
+ if (_pendingRecords.length > MAX_PENDING) {
1532
+ const dropped = _pendingRecords.length - MAX_PENDING;
1533
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
1534
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
1535
+ }
1510
1536
  if (_flushTimer === null) {
1511
1537
  _flushTimer = setInterval(() => {
1512
1538
  void flushBatch();
@@ -1838,7 +1864,7 @@ async function getMemoryCardinality(agentId) {
1838
1864
  return 0;
1839
1865
  }
1840
1866
  }
1841
- var _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1867
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
1842
1868
  var init_store = __esm({
1843
1869
  "src/lib/store.ts"() {
1844
1870
  "use strict";
@@ -1846,6 +1872,8 @@ var init_store = __esm({
1846
1872
  init_database();
1847
1873
  init_keychain();
1848
1874
  init_config();
1875
+ INIT_MAX_RETRIES = 3;
1876
+ INIT_RETRY_DELAY_MS = 1e3;
1849
1877
  _pendingRecords = [];
1850
1878
  _batchSize = 20;
1851
1879
  _flushIntervalMs = 1e4;
@@ -1953,10 +1981,11 @@ var timeout = setTimeout(() => {
1953
1981
  process.exit(0);
1954
1982
  }, 5e3);
1955
1983
  timeout.unref();
1984
+ var MAX_INPUT_SIZE = 1e6;
1956
1985
  var input = "";
1957
1986
  process.stdin.setEncoding("utf8");
1958
1987
  process.stdin.on("data", (chunk) => {
1959
- input += chunk;
1988
+ if (input.length < MAX_INPUT_SIZE) input += chunk;
1960
1989
  });
1961
1990
  process.stdin.on("end", async () => {
1962
1991
  try {