@askexenow/exe-os 0.9.16 → 0.9.18

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 (97) hide show
  1. package/dist/bin/backfill-conversations.js +1242 -909
  2. package/dist/bin/backfill-responses.js +1245 -912
  3. package/dist/bin/backfill-vectors.js +1244 -906
  4. package/dist/bin/cleanup-stale-review-tasks.js +1870 -417
  5. package/dist/bin/cli.js +217 -107
  6. package/dist/bin/exe-agent-config.js +2 -2
  7. package/dist/bin/exe-agent.js +62 -0
  8. package/dist/bin/exe-assign.js +346 -10
  9. package/dist/bin/exe-boot.js +387 -32
  10. package/dist/bin/exe-call.js +72 -2
  11. package/dist/bin/exe-cloud.js +8 -0
  12. package/dist/bin/exe-dispatch.js +1821 -225
  13. package/dist/bin/exe-doctor.js +720 -52
  14. package/dist/bin/exe-export-behaviors.js +1429 -148
  15. package/dist/bin/exe-forget.js +1408 -34
  16. package/dist/bin/exe-gateway.js +1629 -1295
  17. package/dist/bin/exe-heartbeat.js +1899 -448
  18. package/dist/bin/exe-kill.js +1624 -346
  19. package/dist/bin/exe-launch-agent.js +726 -90
  20. package/dist/bin/exe-link.js +27 -8
  21. package/dist/bin/exe-new-employee.js +75 -9
  22. package/dist/bin/exe-pending-messages.js +2769 -1316
  23. package/dist/bin/exe-pending-notifications.js +2829 -1376
  24. package/dist/bin/exe-pending-reviews.js +2847 -1392
  25. package/dist/bin/exe-rename.js +89 -8
  26. package/dist/bin/exe-review.js +1494 -312
  27. package/dist/bin/exe-search.js +1608 -1300
  28. package/dist/bin/exe-session-cleanup.js +194 -91
  29. package/dist/bin/exe-settings.js +10 -2
  30. package/dist/bin/exe-start-codex.js +769 -120
  31. package/dist/bin/exe-start-opencode.js +763 -108
  32. package/dist/bin/exe-status.js +1887 -434
  33. package/dist/bin/exe-team.js +1782 -324
  34. package/dist/bin/git-sweep.js +408 -33
  35. package/dist/bin/graph-backfill.js +681 -27
  36. package/dist/bin/graph-export.js +1419 -141
  37. package/dist/bin/install.js +4 -8
  38. package/dist/bin/intercom-check.js +8641 -0
  39. package/dist/bin/scan-tasks.js +553 -38
  40. package/dist/bin/setup.js +82 -10
  41. package/dist/bin/shard-migrate.js +682 -28
  42. package/dist/gateway/index.js +1629 -1295
  43. package/dist/hooks/bug-report-worker.js +1136 -183
  44. package/dist/hooks/codex-stop-task-finalizer.js +1060 -107
  45. package/dist/hooks/commit-complete.js +408 -33
  46. package/dist/hooks/error-recall.js +1608 -1300
  47. package/dist/hooks/ingest-worker.js +250 -7966
  48. package/dist/hooks/ingest.js +707 -119
  49. package/dist/hooks/instructions-loaded.js +383 -20
  50. package/dist/hooks/notification.js +383 -20
  51. package/dist/hooks/post-compact.js +384 -21
  52. package/dist/hooks/{response-ingest-worker.js → post-tool-combined.js} +4157 -1716
  53. package/dist/hooks/pre-compact.js +486 -45
  54. package/dist/hooks/pre-tool-use.js +385 -22
  55. package/dist/hooks/prompt-submit.js +291 -96
  56. package/dist/hooks/session-end.js +490 -48
  57. package/dist/hooks/session-start.js +236 -22
  58. package/dist/hooks/stop.js +192 -50
  59. package/dist/hooks/subagent-stop.js +95 -18
  60. package/dist/hooks/summary-worker.js +205 -361
  61. package/dist/index.js +221 -105
  62. package/dist/lib/agent-config.js +2 -2
  63. package/dist/lib/cloud-sync.js +27 -8
  64. package/dist/lib/consolidation.js +437 -41
  65. package/dist/lib/database.js +20 -8
  66. package/dist/lib/db-daemon-client.js +2 -2
  67. package/dist/lib/db.js +20 -8
  68. package/dist/lib/device-registry.js +27 -8
  69. package/dist/lib/employee-templates.js +62 -0
  70. package/dist/lib/employees.js +2 -2
  71. package/dist/lib/exe-daemon.js +6703 -6259
  72. package/dist/lib/hybrid-search.js +1608 -1300
  73. package/dist/lib/identity.js +1 -1
  74. package/dist/lib/messaging.js +11 -3
  75. package/dist/lib/reminders.js +1 -1
  76. package/dist/lib/schedules.js +718 -26
  77. package/dist/lib/session-registry.js +11 -0
  78. package/dist/lib/skill-learning.js +639 -9
  79. package/dist/lib/store.js +676 -27
  80. package/dist/lib/tasks.js +665 -27
  81. package/dist/lib/tmux-routing.js +732 -94
  82. package/dist/lib/token-spend.js +1 -1
  83. package/dist/mcp/server.js +856 -383
  84. package/dist/mcp/tools/complete-reminder.js +1 -1
  85. package/dist/mcp/tools/create-reminder.js +1 -1
  86. package/dist/mcp/tools/create-task.js +616 -46
  87. package/dist/mcp/tools/deactivate-behavior.js +9 -1
  88. package/dist/mcp/tools/list-reminders.js +1 -1
  89. package/dist/mcp/tools/list-tasks.js +10 -2
  90. package/dist/mcp/tools/send-message.js +11 -3
  91. package/dist/mcp/tools/update-task.js +677 -39
  92. package/dist/runtime/index.js +425 -37
  93. package/dist/tui/App.js +365 -34
  94. package/package.json +5 -2
  95. package/src/commands/exe/intercom.md +6 -17
  96. package/dist/bin/wiki-sync.js +0 -2991
  97. package/dist/hooks/prompt-ingest-worker.js +0 -3979
@@ -328,12 +328,25 @@ async function ensurePrivateDir(dirPath) {
328
328
  } catch {
329
329
  }
330
330
  }
331
+ function ensurePrivateDirSync(dirPath) {
332
+ mkdirSync2(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
333
+ try {
334
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
335
+ } catch {
336
+ }
337
+ }
331
338
  async function enforcePrivateFile(filePath) {
332
339
  try {
333
340
  await chmod(filePath, PRIVATE_FILE_MODE);
334
341
  } catch {
335
342
  }
336
343
  }
344
+ function enforcePrivateFileSync(filePath) {
345
+ try {
346
+ if (existsSync2(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
347
+ } catch {
348
+ }
349
+ }
337
350
  var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
338
351
  var init_secure_files = __esm({
339
352
  "src/lib/secure-files.ts"() {
@@ -344,6 +357,21 @@ var init_secure_files = __esm({
344
357
  });
345
358
 
346
359
  // src/lib/config.ts
360
+ var config_exports = {};
361
+ __export(config_exports, {
362
+ CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
363
+ CONFIG_PATH: () => CONFIG_PATH,
364
+ CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
365
+ DB_PATH: () => DB_PATH,
366
+ EXE_AI_DIR: () => EXE_AI_DIR,
367
+ LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
368
+ MODELS_DIR: () => MODELS_DIR,
369
+ loadConfig: () => loadConfig,
370
+ loadConfigFrom: () => loadConfigFrom,
371
+ loadConfigSync: () => loadConfigSync,
372
+ migrateConfig: () => migrateConfig,
373
+ saveConfig: () => saveConfig
374
+ });
347
375
  import { readFile, writeFile } from "fs/promises";
348
376
  import { readFileSync as readFileSync2, existsSync as existsSync3, renameSync } from "fs";
349
377
  import path2 from "path";
@@ -447,6 +475,46 @@ async function loadConfig() {
447
475
  return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
448
476
  }
449
477
  }
478
+ function loadConfigSync() {
479
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
480
+ const configPath = path2.join(dir, "config.json");
481
+ if (!existsSync3(configPath)) {
482
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
483
+ }
484
+ try {
485
+ const raw = readFileSync2(configPath, "utf-8");
486
+ let parsed = JSON.parse(raw);
487
+ parsed = migrateLegacyConfig(parsed);
488
+ const { config: migratedCfg } = migrateConfig(parsed);
489
+ normalizeScalingRoadmap(migratedCfg);
490
+ normalizeSessionLifecycle(migratedCfg);
491
+ normalizeAutoUpdate(migratedCfg);
492
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
493
+ } catch {
494
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
495
+ }
496
+ }
497
+ async function saveConfig(config) {
498
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
499
+ await ensurePrivateDir(dir);
500
+ const configPath = path2.join(dir, "config.json");
501
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
502
+ await enforcePrivateFile(configPath);
503
+ }
504
+ async function loadConfigFrom(configPath) {
505
+ const raw = await readFile(configPath, "utf-8");
506
+ try {
507
+ let parsed = JSON.parse(raw);
508
+ parsed = migrateLegacyConfig(parsed);
509
+ const { config: migratedCfg } = migrateConfig(parsed);
510
+ normalizeScalingRoadmap(migratedCfg);
511
+ normalizeSessionLifecycle(migratedCfg);
512
+ normalizeAutoUpdate(migratedCfg);
513
+ return { ...DEFAULT_CONFIG, ...migratedCfg };
514
+ } catch {
515
+ return { ...DEFAULT_CONFIG };
516
+ }
517
+ }
450
518
  var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
451
519
  var init_config = __esm({
452
520
  "src/lib/config.ts"() {
@@ -583,7 +651,7 @@ var init_agent_config = __esm({
583
651
  init_secure_files();
584
652
  AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
585
653
  DEFAULT_MODELS = {
586
- claude: "claude-opus-4",
654
+ claude: "claude-opus-4.6",
587
655
  codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
588
656
  opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
589
657
  };
@@ -765,9 +833,9 @@ var MAX_RETRIES2, BASE_DELAY_MS, MAX_JITTER_MS;
765
833
  var init_db_retry = __esm({
766
834
  "src/lib/db-retry.ts"() {
767
835
  "use strict";
768
- MAX_RETRIES2 = 3;
769
- BASE_DELAY_MS = 200;
770
- MAX_JITTER_MS = 300;
836
+ MAX_RETRIES2 = 5;
837
+ BASE_DELAY_MS = 250;
838
+ MAX_JITTER_MS = 400;
771
839
  }
772
840
  });
773
841
 
@@ -1426,7 +1494,762 @@ var init_database_adapter = __esm({
1426
1494
  }
1427
1495
  });
1428
1496
 
1497
+ // src/types/memory.ts
1498
+ var EMBEDDING_DIM;
1499
+ var init_memory = __esm({
1500
+ "src/types/memory.ts"() {
1501
+ "use strict";
1502
+ EMBEDDING_DIM = 1024;
1503
+ }
1504
+ });
1505
+
1506
+ // src/lib/daemon-auth.ts
1507
+ import crypto from "crypto";
1508
+ import path7 from "path";
1509
+ import { existsSync as existsSync7, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
1510
+ function normalizeToken(token) {
1511
+ if (!token) return null;
1512
+ const trimmed = token.trim();
1513
+ return trimmed.length > 0 ? trimmed : null;
1514
+ }
1515
+ function readDaemonToken() {
1516
+ try {
1517
+ if (!existsSync7(DAEMON_TOKEN_PATH)) return null;
1518
+ return normalizeToken(readFileSync6(DAEMON_TOKEN_PATH, "utf8"));
1519
+ } catch {
1520
+ return null;
1521
+ }
1522
+ }
1523
+ function ensureDaemonToken(seed) {
1524
+ const existing = readDaemonToken();
1525
+ if (existing) return existing;
1526
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
1527
+ ensurePrivateDirSync(EXE_AI_DIR);
1528
+ writeFileSync5(DAEMON_TOKEN_PATH, `${token}
1529
+ `, "utf8");
1530
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
1531
+ return token;
1532
+ }
1533
+ var DAEMON_TOKEN_PATH;
1534
+ var init_daemon_auth = __esm({
1535
+ "src/lib/daemon-auth.ts"() {
1536
+ "use strict";
1537
+ init_config();
1538
+ init_secure_files();
1539
+ DAEMON_TOKEN_PATH = path7.join(EXE_AI_DIR, "exed.token");
1540
+ }
1541
+ });
1542
+
1543
+ // src/lib/exe-daemon-client.ts
1544
+ var exe_daemon_client_exports = {};
1545
+ __export(exe_daemon_client_exports, {
1546
+ connectEmbedDaemon: () => connectEmbedDaemon,
1547
+ disconnectClient: () => disconnectClient,
1548
+ embedBatchViaClient: () => embedBatchViaClient,
1549
+ embedViaClient: () => embedViaClient,
1550
+ isClientConnected: () => isClientConnected,
1551
+ pingDaemon: () => pingDaemon,
1552
+ sendDaemonRequest: () => sendDaemonRequest,
1553
+ sendIngestRequest: () => sendIngestRequest
1554
+ });
1555
+ import net from "net";
1556
+ import os6 from "os";
1557
+ import { spawn } from "child_process";
1558
+ import { randomUUID } from "crypto";
1559
+ import { existsSync as existsSync8, unlinkSync as unlinkSync2, readFileSync as readFileSync7, openSync, closeSync, statSync } from "fs";
1560
+ import path8 from "path";
1561
+ import { fileURLToPath } from "url";
1562
+ function handleData(chunk) {
1563
+ _buffer += chunk.toString();
1564
+ if (_buffer.length > MAX_BUFFER) {
1565
+ _buffer = "";
1566
+ return;
1567
+ }
1568
+ let newlineIdx;
1569
+ while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1570
+ const line = _buffer.slice(0, newlineIdx).trim();
1571
+ _buffer = _buffer.slice(newlineIdx + 1);
1572
+ if (!line) continue;
1573
+ try {
1574
+ const response = JSON.parse(line);
1575
+ const id = response.id;
1576
+ if (!id) continue;
1577
+ const entry = _pending.get(id);
1578
+ if (entry) {
1579
+ clearTimeout(entry.timer);
1580
+ _pending.delete(id);
1581
+ entry.resolve(response);
1582
+ }
1583
+ } catch {
1584
+ }
1585
+ }
1586
+ }
1587
+ function cleanupStaleFiles() {
1588
+ if (existsSync8(PID_PATH)) {
1589
+ try {
1590
+ const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
1591
+ if (pid > 0) {
1592
+ try {
1593
+ process.kill(pid, 0);
1594
+ return;
1595
+ } catch {
1596
+ }
1597
+ }
1598
+ } catch {
1599
+ }
1600
+ try {
1601
+ unlinkSync2(PID_PATH);
1602
+ } catch {
1603
+ }
1604
+ try {
1605
+ unlinkSync2(SOCKET_PATH);
1606
+ } catch {
1607
+ }
1608
+ }
1609
+ }
1610
+ function findPackageRoot() {
1611
+ let dir = path8.dirname(fileURLToPath(import.meta.url));
1612
+ const { root } = path8.parse(dir);
1613
+ while (dir !== root) {
1614
+ if (existsSync8(path8.join(dir, "package.json"))) return dir;
1615
+ dir = path8.dirname(dir);
1616
+ }
1617
+ return null;
1618
+ }
1619
+ function getAvailableMemoryGB() {
1620
+ if (process.platform === "darwin") {
1621
+ try {
1622
+ const { execSync: execSync7 } = __require("child_process");
1623
+ const vmstat = execSync7("vm_stat", { encoding: "utf8" });
1624
+ const pageSize = 16384;
1625
+ const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1626
+ const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1627
+ const free = vmstat.match(/Pages free:\s+(\d+)/);
1628
+ const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1629
+ const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1630
+ const freePages = free ? parseInt(free[1], 10) : 0;
1631
+ const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1632
+ const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1633
+ return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1634
+ } catch {
1635
+ return os6.freemem() / (1024 * 1024 * 1024);
1636
+ }
1637
+ }
1638
+ return os6.freemem() / (1024 * 1024 * 1024);
1639
+ }
1640
+ function spawnDaemon() {
1641
+ const freeGB = getAvailableMemoryGB();
1642
+ const totalGB = os6.totalmem() / (1024 * 1024 * 1024);
1643
+ if (totalGB <= 8) {
1644
+ process.stderr.write(
1645
+ `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1646
+ `
1647
+ );
1648
+ return;
1649
+ }
1650
+ if (totalGB <= 16 && freeGB < 2) {
1651
+ process.stderr.write(
1652
+ `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1653
+ `
1654
+ );
1655
+ return;
1656
+ }
1657
+ const pkgRoot = findPackageRoot();
1658
+ if (!pkgRoot) {
1659
+ process.stderr.write("[exed-client] WARN: cannot find package root\n");
1660
+ return;
1661
+ }
1662
+ const daemonPath = path8.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1663
+ if (!existsSync8(daemonPath)) {
1664
+ process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1665
+ `);
1666
+ return;
1667
+ }
1668
+ const resolvedPath = daemonPath;
1669
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
1670
+ process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1671
+ `);
1672
+ const logPath = path8.join(path8.dirname(SOCKET_PATH), "exed.log");
1673
+ let stderrFd = "ignore";
1674
+ try {
1675
+ stderrFd = openSync(logPath, "a");
1676
+ } catch {
1677
+ }
1678
+ const heapCapMB = totalGB <= 8 ? 256 : 512;
1679
+ const nodeArgs = [`--max-old-space-size=${heapCapMB}`, resolvedPath];
1680
+ const child = spawn(process.execPath, nodeArgs, {
1681
+ detached: true,
1682
+ stdio: ["ignore", "ignore", stderrFd],
1683
+ env: {
1684
+ ...process.env,
1685
+ TMUX: void 0,
1686
+ // Daemon is global — must not inherit session scope
1687
+ TMUX_PANE: void 0,
1688
+ // Prevents resolveExeSession() from scoping to one session
1689
+ EXE_DAEMON_SOCK: SOCKET_PATH,
1690
+ EXE_DAEMON_PID: PID_PATH,
1691
+ [DAEMON_TOKEN_ENV]: daemonToken
1692
+ }
1693
+ });
1694
+ child.unref();
1695
+ if (typeof stderrFd === "number") {
1696
+ try {
1697
+ closeSync(stderrFd);
1698
+ } catch {
1699
+ }
1700
+ }
1701
+ }
1702
+ function acquireSpawnLock() {
1703
+ try {
1704
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
1705
+ closeSync(fd);
1706
+ return true;
1707
+ } catch {
1708
+ try {
1709
+ const stat = statSync(SPAWN_LOCK_PATH);
1710
+ if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
1711
+ try {
1712
+ unlinkSync2(SPAWN_LOCK_PATH);
1713
+ } catch {
1714
+ }
1715
+ try {
1716
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
1717
+ closeSync(fd);
1718
+ return true;
1719
+ } catch {
1720
+ }
1721
+ }
1722
+ } catch {
1723
+ }
1724
+ return false;
1725
+ }
1726
+ }
1727
+ function releaseSpawnLock() {
1728
+ try {
1729
+ unlinkSync2(SPAWN_LOCK_PATH);
1730
+ } catch {
1731
+ }
1732
+ }
1733
+ function connectToSocket() {
1734
+ return new Promise((resolve) => {
1735
+ if (_socket && _connected) {
1736
+ resolve(true);
1737
+ return;
1738
+ }
1739
+ const socket = net.createConnection({ path: SOCKET_PATH });
1740
+ const connectTimeout = setTimeout(() => {
1741
+ socket.destroy();
1742
+ resolve(false);
1743
+ }, 2e3);
1744
+ socket.on("connect", () => {
1745
+ clearTimeout(connectTimeout);
1746
+ _socket = socket;
1747
+ _connected = true;
1748
+ _buffer = "";
1749
+ socket.on("data", handleData);
1750
+ socket.on("close", () => {
1751
+ _connected = false;
1752
+ _socket = null;
1753
+ for (const [id, entry] of _pending) {
1754
+ clearTimeout(entry.timer);
1755
+ _pending.delete(id);
1756
+ entry.resolve({ error: "Connection closed" });
1757
+ }
1758
+ });
1759
+ socket.on("error", () => {
1760
+ _connected = false;
1761
+ _socket = null;
1762
+ });
1763
+ resolve(true);
1764
+ });
1765
+ socket.on("error", () => {
1766
+ clearTimeout(connectTimeout);
1767
+ resolve(false);
1768
+ });
1769
+ });
1770
+ }
1771
+ async function connectEmbedDaemon() {
1772
+ if (_socket && _connected) return true;
1773
+ if (await connectToSocket()) return true;
1774
+ if (acquireSpawnLock()) {
1775
+ try {
1776
+ cleanupStaleFiles();
1777
+ spawnDaemon();
1778
+ } finally {
1779
+ releaseSpawnLock();
1780
+ }
1781
+ }
1782
+ const start = Date.now();
1783
+ let delay2 = 100;
1784
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1785
+ await new Promise((r) => setTimeout(r, delay2));
1786
+ if (await connectToSocket()) return true;
1787
+ delay2 = Math.min(delay2 * 2, 3e3);
1788
+ }
1789
+ return false;
1790
+ }
1791
+ function sendRequest(texts, priority) {
1792
+ return sendDaemonRequest({ texts, priority });
1793
+ }
1794
+ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1795
+ return new Promise((resolve) => {
1796
+ if (!_socket || !_connected) {
1797
+ resolve({ error: "Not connected" });
1798
+ return;
1799
+ }
1800
+ const id = randomUUID();
1801
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
1802
+ const timer = setTimeout(() => {
1803
+ _pending.delete(id);
1804
+ resolve({ error: "Request timeout" });
1805
+ }, timeoutMs);
1806
+ _pending.set(id, { resolve, timer });
1807
+ try {
1808
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
1809
+ } catch {
1810
+ clearTimeout(timer);
1811
+ _pending.delete(id);
1812
+ resolve({ error: "Write failed" });
1813
+ }
1814
+ });
1815
+ }
1816
+ async function pingDaemon() {
1817
+ if (!_socket || !_connected) return null;
1818
+ const response = await sendDaemonRequest({ type: "health" }, 5e3);
1819
+ if (response.health) {
1820
+ return response.health;
1821
+ }
1822
+ return null;
1823
+ }
1824
+ function killAndRespawnDaemon() {
1825
+ if (!acquireSpawnLock()) {
1826
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
1827
+ if (_socket) {
1828
+ _socket.destroy();
1829
+ _socket = null;
1830
+ }
1831
+ _connected = false;
1832
+ _buffer = "";
1833
+ return;
1834
+ }
1835
+ try {
1836
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
1837
+ if (existsSync8(PID_PATH)) {
1838
+ try {
1839
+ const pid = parseInt(readFileSync7(PID_PATH, "utf8").trim(), 10);
1840
+ if (pid > 0) {
1841
+ try {
1842
+ process.kill(pid, "SIGKILL");
1843
+ } catch {
1844
+ }
1845
+ }
1846
+ } catch {
1847
+ }
1848
+ }
1849
+ if (_socket) {
1850
+ _socket.destroy();
1851
+ _socket = null;
1852
+ }
1853
+ _connected = false;
1854
+ _buffer = "";
1855
+ try {
1856
+ unlinkSync2(PID_PATH);
1857
+ } catch {
1858
+ }
1859
+ try {
1860
+ unlinkSync2(SOCKET_PATH);
1861
+ } catch {
1862
+ }
1863
+ spawnDaemon();
1864
+ } finally {
1865
+ releaseSpawnLock();
1866
+ }
1867
+ }
1868
+ function isDaemonTooYoung() {
1869
+ try {
1870
+ const stat = statSync(PID_PATH);
1871
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
1872
+ } catch {
1873
+ return false;
1874
+ }
1875
+ }
1876
+ async function retryThenRestart(doRequest, label) {
1877
+ const result2 = await doRequest();
1878
+ if (!result2.error) {
1879
+ _consecutiveFailures = 0;
1880
+ return result2;
1881
+ }
1882
+ _consecutiveFailures++;
1883
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
1884
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
1885
+ process.stderr.write(`[exed-client] ${label} failed (${result2.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
1886
+ `);
1887
+ await new Promise((r) => setTimeout(r, delayMs));
1888
+ if (!_connected) {
1889
+ if (!await connectToSocket()) continue;
1890
+ }
1891
+ const retry = await doRequest();
1892
+ if (!retry.error) {
1893
+ _consecutiveFailures = 0;
1894
+ return retry;
1895
+ }
1896
+ _consecutiveFailures++;
1897
+ }
1898
+ if (isDaemonTooYoung()) {
1899
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
1900
+ `);
1901
+ return { error: result2.error };
1902
+ }
1903
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
1904
+ `);
1905
+ killAndRespawnDaemon();
1906
+ const start = Date.now();
1907
+ let delay2 = 200;
1908
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1909
+ await new Promise((r) => setTimeout(r, delay2));
1910
+ if (await connectToSocket()) break;
1911
+ delay2 = Math.min(delay2 * 2, 3e3);
1912
+ }
1913
+ if (!_connected) return { error: "Daemon restart failed" };
1914
+ const final = await doRequest();
1915
+ if (!final.error) _consecutiveFailures = 0;
1916
+ return final;
1917
+ }
1918
+ async function embedViaClient(text, priority = "high") {
1919
+ if (!_connected && !await connectEmbedDaemon()) return null;
1920
+ _requestCount++;
1921
+ if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
1922
+ const health = await pingDaemon();
1923
+ if (!health && !isDaemonTooYoung()) {
1924
+ process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
1925
+ `);
1926
+ killAndRespawnDaemon();
1927
+ const start = Date.now();
1928
+ let d = 200;
1929
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1930
+ await new Promise((r) => setTimeout(r, d));
1931
+ if (await connectToSocket()) break;
1932
+ d = Math.min(d * 2, 3e3);
1933
+ }
1934
+ if (!_connected) return null;
1935
+ }
1936
+ }
1937
+ const result2 = await retryThenRestart(
1938
+ () => sendRequest([text], priority),
1939
+ "Embed"
1940
+ );
1941
+ return !result2.error && result2.vectors?.[0] ? result2.vectors[0] : null;
1942
+ }
1943
+ async function embedBatchViaClient(texts, priority = "high") {
1944
+ if (!_connected && !await connectEmbedDaemon()) return null;
1945
+ _requestCount++;
1946
+ const result2 = await retryThenRestart(
1947
+ () => sendRequest(texts, priority),
1948
+ "Batch embed"
1949
+ );
1950
+ return !result2.error && result2.vectors ? result2.vectors : null;
1951
+ }
1952
+ function disconnectClient() {
1953
+ if (_socket) {
1954
+ _socket.destroy();
1955
+ _socket = null;
1956
+ }
1957
+ _connected = false;
1958
+ _buffer = "";
1959
+ for (const [id, entry] of _pending) {
1960
+ clearTimeout(entry.timer);
1961
+ _pending.delete(id);
1962
+ entry.resolve({ error: "Client disconnected" });
1963
+ }
1964
+ }
1965
+ function isClientConnected() {
1966
+ return _connected;
1967
+ }
1968
+ function sendIngestRequest(payload) {
1969
+ if (!_socket || !_connected) return false;
1970
+ try {
1971
+ const id = randomUUID();
1972
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
1973
+ _socket.write(JSON.stringify({ id, token, type: "ingest", ...payload }) + "\n");
1974
+ return true;
1975
+ } catch {
1976
+ return false;
1977
+ }
1978
+ }
1979
+ var SOCKET_PATH, PID_PATH, SPAWN_LOCK_PATH, SPAWN_LOCK_STALE_MS, CONNECT_TIMEOUT_MS, REQUEST_TIMEOUT_MS, DAEMON_TOKEN_ENV, _socket, _connected, _buffer, _requestCount, _consecutiveFailures, HEALTH_CHECK_INTERVAL, MAX_RETRIES_BEFORE_RESTART, RETRY_DELAYS_MS, MIN_DAEMON_AGE_MS, _pending, MAX_BUFFER;
1980
+ var init_exe_daemon_client = __esm({
1981
+ "src/lib/exe-daemon-client.ts"() {
1982
+ "use strict";
1983
+ init_config();
1984
+ init_daemon_auth();
1985
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path8.join(EXE_AI_DIR, "exed.sock");
1986
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path8.join(EXE_AI_DIR, "exed.pid");
1987
+ SPAWN_LOCK_PATH = path8.join(EXE_AI_DIR, "exed-spawn.lock");
1988
+ SPAWN_LOCK_STALE_MS = 3e4;
1989
+ CONNECT_TIMEOUT_MS = 15e3;
1990
+ REQUEST_TIMEOUT_MS = 3e4;
1991
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
1992
+ _socket = null;
1993
+ _connected = false;
1994
+ _buffer = "";
1995
+ _requestCount = 0;
1996
+ _consecutiveFailures = 0;
1997
+ HEALTH_CHECK_INTERVAL = 100;
1998
+ MAX_RETRIES_BEFORE_RESTART = 3;
1999
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
2000
+ MIN_DAEMON_AGE_MS = 3e4;
2001
+ _pending = /* @__PURE__ */ new Map();
2002
+ MAX_BUFFER = 1e7;
2003
+ }
2004
+ });
2005
+
2006
+ // src/lib/daemon-protocol.ts
2007
+ var daemon_protocol_exports = {};
2008
+ __export(daemon_protocol_exports, {
2009
+ deserializeArgs: () => deserializeArgs,
2010
+ deserializeResultSet: () => deserializeResultSet,
2011
+ deserializeValue: () => deserializeValue,
2012
+ serializeArgs: () => serializeArgs,
2013
+ serializeResultSet: () => serializeResultSet,
2014
+ serializeValue: () => serializeValue
2015
+ });
2016
+ function serializeValue(v) {
2017
+ if (v === null || v === void 0) return null;
2018
+ if (typeof v === "bigint") return Number(v);
2019
+ if (typeof v === "boolean") return v ? 1 : 0;
2020
+ if (v instanceof Uint8Array) {
2021
+ return { __blob: Buffer.from(v).toString("base64") };
2022
+ }
2023
+ if (ArrayBuffer.isView(v)) {
2024
+ return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
2025
+ }
2026
+ if (v instanceof ArrayBuffer) {
2027
+ return { __blob: Buffer.from(v).toString("base64") };
2028
+ }
2029
+ if (typeof v === "string" || typeof v === "number") return v;
2030
+ return String(v);
2031
+ }
2032
+ function deserializeValue(v) {
2033
+ if (v === null) return null;
2034
+ if (typeof v === "object" && v !== null && "__blob" in v) {
2035
+ const buf = Buffer.from(v.__blob, "base64");
2036
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
2037
+ }
2038
+ return v;
2039
+ }
2040
+ function serializeArgs(args) {
2041
+ return args.map(serializeValue);
2042
+ }
2043
+ function deserializeArgs(args) {
2044
+ return args.map(deserializeValue);
2045
+ }
2046
+ function serializeResultSet(rs) {
2047
+ const rows = [];
2048
+ for (const row of rs.rows) {
2049
+ const obj = {};
2050
+ for (let i = 0; i < rs.columns.length; i++) {
2051
+ const col = rs.columns[i];
2052
+ if (col !== void 0) {
2053
+ obj[col] = serializeValue(row[i]);
2054
+ }
2055
+ }
2056
+ rows.push(obj);
2057
+ }
2058
+ return {
2059
+ columns: [...rs.columns],
2060
+ columnTypes: [...rs.columnTypes ?? []],
2061
+ rows,
2062
+ rowsAffected: typeof rs.rowsAffected === "bigint" ? Number(rs.rowsAffected) : rs.rowsAffected ?? 0,
2063
+ lastInsertRowid: rs.lastInsertRowid != null ? typeof rs.lastInsertRowid === "bigint" ? Number(rs.lastInsertRowid) : rs.lastInsertRowid : null
2064
+ };
2065
+ }
2066
+ function deserializeResultSet(srs) {
2067
+ const rows = srs.rows.map((obj) => {
2068
+ const values = srs.columns.map(
2069
+ (col) => deserializeValue(obj[col] ?? null)
2070
+ );
2071
+ const row = values;
2072
+ for (let i = 0; i < srs.columns.length; i++) {
2073
+ const col = srs.columns[i];
2074
+ if (col !== void 0) {
2075
+ row[col] = values[i] ?? null;
2076
+ }
2077
+ }
2078
+ Object.defineProperty(row, "length", {
2079
+ value: values.length,
2080
+ enumerable: false
2081
+ });
2082
+ return row;
2083
+ });
2084
+ return {
2085
+ columns: srs.columns,
2086
+ columnTypes: srs.columnTypes ?? [],
2087
+ rows,
2088
+ rowsAffected: srs.rowsAffected,
2089
+ lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
2090
+ toJSON: () => ({
2091
+ columns: srs.columns,
2092
+ columnTypes: srs.columnTypes ?? [],
2093
+ rows: srs.rows,
2094
+ rowsAffected: srs.rowsAffected,
2095
+ lastInsertRowid: srs.lastInsertRowid
2096
+ })
2097
+ };
2098
+ }
2099
+ var init_daemon_protocol = __esm({
2100
+ "src/lib/daemon-protocol.ts"() {
2101
+ "use strict";
2102
+ }
2103
+ });
2104
+
2105
+ // src/lib/db-daemon-client.ts
2106
+ var db_daemon_client_exports = {};
2107
+ __export(db_daemon_client_exports, {
2108
+ createDaemonDbClient: () => createDaemonDbClient,
2109
+ initDaemonDbClient: () => initDaemonDbClient
2110
+ });
2111
+ function normalizeStatement2(stmt) {
2112
+ if (typeof stmt === "string") {
2113
+ return { sql: stmt, args: [] };
2114
+ }
2115
+ const sql = stmt.sql;
2116
+ let args = [];
2117
+ if (Array.isArray(stmt.args)) {
2118
+ args = stmt.args.map((v) => serializeValue(v));
2119
+ } else if (stmt.args && typeof stmt.args === "object") {
2120
+ const named = {};
2121
+ for (const [key, val] of Object.entries(stmt.args)) {
2122
+ named[key] = serializeValue(val);
2123
+ }
2124
+ return { sql, args: named };
2125
+ }
2126
+ return { sql, args };
2127
+ }
2128
+ function createDaemonDbClient(fallbackClient) {
2129
+ let _useDaemon = false;
2130
+ const client = {
2131
+ async execute(stmt) {
2132
+ if (!_useDaemon || !isClientConnected()) {
2133
+ return fallbackClient.execute(stmt);
2134
+ }
2135
+ const { sql, args } = normalizeStatement2(stmt);
2136
+ const response = await sendDaemonRequest({
2137
+ type: "db-execute",
2138
+ sql,
2139
+ args
2140
+ });
2141
+ if (response.error) {
2142
+ const errMsg = String(response.error);
2143
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
2144
+ process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
2145
+ `);
2146
+ return fallbackClient.execute(stmt);
2147
+ }
2148
+ throw new Error(errMsg);
2149
+ }
2150
+ if (response.db) {
2151
+ return deserializeResultSet(response.db);
2152
+ }
2153
+ process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
2154
+ return fallbackClient.execute(stmt);
2155
+ },
2156
+ async batch(stmts, mode) {
2157
+ if (!_useDaemon || !isClientConnected()) {
2158
+ return fallbackClient.batch(stmts, mode);
2159
+ }
2160
+ const statements = stmts.map(normalizeStatement2);
2161
+ const response = await sendDaemonRequest({
2162
+ type: "db-batch",
2163
+ statements,
2164
+ mode: mode ?? "deferred"
2165
+ });
2166
+ if (response.error) {
2167
+ const errMsg = String(response.error);
2168
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
2169
+ process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
2170
+ `);
2171
+ return fallbackClient.batch(stmts, mode);
2172
+ }
2173
+ throw new Error(errMsg);
2174
+ }
2175
+ const batchResults = response["db-batch"];
2176
+ if (batchResults) {
2177
+ return batchResults.map(deserializeResultSet);
2178
+ }
2179
+ process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
2180
+ return fallbackClient.batch(stmts, mode);
2181
+ },
2182
+ // Transaction support — delegate to fallback (transactions need direct connection)
2183
+ async transaction(mode) {
2184
+ return fallbackClient.transaction(mode);
2185
+ },
2186
+ // executeMultiple — delegate to fallback (used only for schema migrations)
2187
+ async executeMultiple(sql) {
2188
+ return fallbackClient.executeMultiple(sql);
2189
+ },
2190
+ // migrate — delegate to fallback
2191
+ async migrate(stmts) {
2192
+ return fallbackClient.migrate(stmts);
2193
+ },
2194
+ // Sync mode — delegate to fallback
2195
+ sync() {
2196
+ return fallbackClient.sync();
2197
+ },
2198
+ close() {
2199
+ _useDaemon = false;
2200
+ },
2201
+ get closed() {
2202
+ return fallbackClient.closed;
2203
+ },
2204
+ get protocol() {
2205
+ return fallbackClient.protocol;
2206
+ }
2207
+ };
2208
+ return {
2209
+ ...client,
2210
+ /** Enable daemon routing (call after confirming daemon is connected) */
2211
+ _enableDaemon() {
2212
+ _useDaemon = true;
2213
+ },
2214
+ /** Check if daemon routing is active */
2215
+ _isDaemonActive() {
2216
+ return _useDaemon && isClientConnected();
2217
+ }
2218
+ };
2219
+ }
2220
+ async function initDaemonDbClient(fallbackClient) {
2221
+ if (process.env.EXE_IS_DAEMON === "1") return null;
2222
+ const connected = await connectEmbedDaemon();
2223
+ if (!connected) {
2224
+ process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
2225
+ return null;
2226
+ }
2227
+ const client = createDaemonDbClient(fallbackClient);
2228
+ client._enableDaemon();
2229
+ process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
2230
+ return client;
2231
+ }
2232
+ var init_db_daemon_client = __esm({
2233
+ "src/lib/db-daemon-client.ts"() {
2234
+ "use strict";
2235
+ init_exe_daemon_client();
2236
+ init_daemon_protocol();
2237
+ }
2238
+ });
2239
+
1429
2240
  // src/lib/database.ts
2241
+ var database_exports = {};
2242
+ __export(database_exports, {
2243
+ disposeDatabase: () => disposeDatabase,
2244
+ disposeTurso: () => disposeTurso,
2245
+ ensureSchema: () => ensureSchema,
2246
+ getClient: () => getClient,
2247
+ getRawClient: () => getRawClient,
2248
+ initDaemonClient: () => initDaemonClient,
2249
+ initDatabase: () => initDatabase,
2250
+ initTurso: () => initTurso,
2251
+ isInitialized: () => isInitialized
2252
+ });
1430
2253
  import { createClient } from "@libsql/client";
1431
2254
  async function initDatabase(config) {
1432
2255
  if (_walCheckpointTimer) {
@@ -1465,15 +2288,18 @@ async function initDatabase(config) {
1465
2288
  });
1466
2289
  }, 3e4);
1467
2290
  _walCheckpointTimer.unref();
1468
- if (process.env.DATABASE_URL) {
2291
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
1469
2292
  _adapterClient = await createPrismaDbAdapter(_resilientClient);
1470
2293
  }
1471
2294
  }
2295
+ function isInitialized() {
2296
+ return _adapterClient !== null || _client !== null;
2297
+ }
1472
2298
  function getClient() {
1473
2299
  if (!_adapterClient) {
1474
2300
  throw new Error("Database client not initialized. Call initDatabase() first.");
1475
2301
  }
1476
- if (process.env.DATABASE_URL) {
2302
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
1477
2303
  return _adapterClient;
1478
2304
  }
1479
2305
  if (process.env.EXE_IS_DAEMON === "1") {
@@ -1484,6 +2310,22 @@ function getClient() {
1484
2310
  }
1485
2311
  return _resilientClient;
1486
2312
  }
2313
+ async function initDaemonClient() {
2314
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") return;
2315
+ if (process.env.EXE_IS_DAEMON === "1") return;
2316
+ if (process.env.VITEST) return;
2317
+ if (!_resilientClient) return;
2318
+ if (_daemonClient) return;
2319
+ try {
2320
+ const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
2321
+ _daemonClient = await initDaemonDbClient2(_resilientClient);
2322
+ } catch (err) {
2323
+ process.stderr.write(
2324
+ `[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
2325
+ `
2326
+ );
2327
+ }
2328
+ }
1487
2329
  function getRawClient() {
1488
2330
  if (!_client) {
1489
2331
  throw new Error("Database client not initialized. Call initDatabase() first.");
@@ -1622,6 +2464,13 @@ async function ensureSchema() {
1622
2464
  });
1623
2465
  } catch {
1624
2466
  }
2467
+ try {
2468
+ await client.execute({
2469
+ sql: `ALTER TABLE behaviors ADD COLUMN vector F32_BLOB(${EMBEDDING_DIM})`,
2470
+ args: []
2471
+ });
2472
+ } catch {
2473
+ }
1625
2474
  try {
1626
2475
  await client.execute({
1627
2476
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
@@ -2466,38 +3315,59 @@ async function ensureSchema() {
2466
3315
  } catch {
2467
3316
  }
2468
3317
  }
2469
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
3318
+ async function disposeDatabase() {
3319
+ if (_walCheckpointTimer) {
3320
+ clearInterval(_walCheckpointTimer);
3321
+ _walCheckpointTimer = null;
3322
+ }
3323
+ if (_daemonClient) {
3324
+ _daemonClient.close();
3325
+ _daemonClient = null;
3326
+ }
3327
+ if (_adapterClient && _adapterClient !== _resilientClient) {
3328
+ _adapterClient.close();
3329
+ }
3330
+ _adapterClient = null;
3331
+ if (_client) {
3332
+ _client.close();
3333
+ _client = null;
3334
+ _resilientClient = null;
3335
+ }
3336
+ }
3337
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
2470
3338
  var init_database = __esm({
2471
3339
  "src/lib/database.ts"() {
2472
3340
  "use strict";
2473
3341
  init_db_retry();
2474
3342
  init_employees();
2475
3343
  init_database_adapter();
3344
+ init_memory();
2476
3345
  _client = null;
2477
3346
  _resilientClient = null;
2478
3347
  _walCheckpointTimer = null;
2479
3348
  _daemonClient = null;
2480
3349
  _adapterClient = null;
2481
3350
  initTurso = initDatabase;
3351
+ disposeTurso = disposeDatabase;
2482
3352
  }
2483
3353
  });
2484
3354
 
2485
3355
  // src/lib/license.ts
2486
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
2487
- import { randomUUID } from "crypto";
3356
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
3357
+ import { randomUUID as randomUUID2 } from "crypto";
2488
3358
  import { createRequire as createRequire2 } from "module";
2489
3359
  import { pathToFileURL as pathToFileURL2 } from "url";
2490
- import os6 from "os";
2491
- import path7 from "path";
3360
+ import os7 from "os";
3361
+ import path9 from "path";
2492
3362
  import { jwtVerify, importSPKI } from "jose";
2493
3363
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
2494
3364
  var init_license = __esm({
2495
3365
  "src/lib/license.ts"() {
2496
3366
  "use strict";
2497
3367
  init_config();
2498
- LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
2499
- CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
2500
- DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
3368
+ LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
3369
+ CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
3370
+ DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
2501
3371
  PLAN_LIMITS = {
2502
3372
  free: { devices: 1, employees: 1, memories: 5e3 },
2503
3373
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -2509,12 +3379,12 @@ var init_license = __esm({
2509
3379
  });
2510
3380
 
2511
3381
  // src/lib/plan-limits.ts
2512
- import { readFileSync as readFileSync7, existsSync as existsSync8 } from "fs";
2513
- import path8 from "path";
3382
+ import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
3383
+ import path10 from "path";
2514
3384
  function getLicenseSync() {
2515
3385
  try {
2516
- if (!existsSync8(CACHE_PATH2)) return freeLicense();
2517
- const raw = JSON.parse(readFileSync7(CACHE_PATH2, "utf8"));
3386
+ if (!existsSync10(CACHE_PATH2)) return freeLicense();
3387
+ const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
2518
3388
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
2519
3389
  const parts = raw.token.split(".");
2520
3390
  if (parts.length !== 3) return freeLicense();
@@ -2552,8 +3422,8 @@ function assertEmployeeLimitSync(rosterPath) {
2552
3422
  const filePath = rosterPath ?? EMPLOYEES_PATH;
2553
3423
  let count = 0;
2554
3424
  try {
2555
- if (existsSync8(filePath)) {
2556
- const raw = readFileSync7(filePath, "utf8");
3425
+ if (existsSync10(filePath)) {
3426
+ const raw = readFileSync9(filePath, "utf8");
2557
3427
  const employees = JSON.parse(raw);
2558
3428
  count = Array.isArray(employees) ? employees.length : 0;
2559
3429
  }
@@ -2582,7 +3452,7 @@ var init_plan_limits = __esm({
2582
3452
  this.name = "PlanLimitError";
2583
3453
  }
2584
3454
  };
2585
- CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
3455
+ CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
2586
3456
  }
2587
3457
  });
2588
3458
 
@@ -2620,20 +3490,20 @@ var init_task_scope = __esm({
2620
3490
  });
2621
3491
 
2622
3492
  // src/lib/notifications.ts
2623
- import crypto from "crypto";
2624
- import path9 from "path";
2625
- import os7 from "os";
3493
+ import crypto2 from "crypto";
3494
+ import path11 from "path";
3495
+ import os8 from "os";
2626
3496
  import {
2627
- readFileSync as readFileSync8,
3497
+ readFileSync as readFileSync10,
2628
3498
  readdirSync,
2629
- unlinkSync as unlinkSync2,
2630
- existsSync as existsSync9,
3499
+ unlinkSync as unlinkSync3,
3500
+ existsSync as existsSync11,
2631
3501
  rmdirSync
2632
3502
  } from "fs";
2633
3503
  async function writeNotification(notification) {
2634
3504
  try {
2635
3505
  const client = getClient();
2636
- const id = crypto.randomUUID();
3506
+ const id = crypto2.randomUUID();
2637
3507
  const now = (/* @__PURE__ */ new Date()).toISOString();
2638
3508
  const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
2639
3509
  await client.execute({
@@ -2677,7 +3547,7 @@ var init_notifications = __esm({
2677
3547
  });
2678
3548
 
2679
3549
  // src/lib/session-kill-telemetry.ts
2680
- import crypto2 from "crypto";
3550
+ import crypto3 from "crypto";
2681
3551
  async function recordSessionKill(input) {
2682
3552
  try {
2683
3553
  const client = getClient();
@@ -2687,7 +3557,7 @@ async function recordSessionKill(input) {
2687
3557
  ticks_idle, estimated_tokens_saved)
2688
3558
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
2689
3559
  args: [
2690
- crypto2.randomUUID(),
3560
+ crypto3.randomUUID(),
2691
3561
  input.sessionName,
2692
3562
  input.agentId,
2693
3563
  (/* @__PURE__ */ new Date()).toISOString(),
@@ -2767,7 +3637,7 @@ var init_state_bus = __esm({
2767
3637
 
2768
3638
  // src/lib/project-name.ts
2769
3639
  import { execSync as execSync4 } from "child_process";
2770
- import path10 from "path";
3640
+ import path12 from "path";
2771
3641
  function getProjectName(cwd) {
2772
3642
  const dir = cwd ?? process.cwd();
2773
3643
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -2780,7 +3650,7 @@ function getProjectName(cwd) {
2780
3650
  timeout: 2e3,
2781
3651
  stdio: ["pipe", "pipe", "pipe"]
2782
3652
  }).trim();
2783
- repoRoot = path10.dirname(gitCommonDir);
3653
+ repoRoot = path12.dirname(gitCommonDir);
2784
3654
  } catch {
2785
3655
  repoRoot = execSync4("git rev-parse --show-toplevel", {
2786
3656
  cwd: dir,
@@ -2789,11 +3659,11 @@ function getProjectName(cwd) {
2789
3659
  stdio: ["pipe", "pipe", "pipe"]
2790
3660
  }).trim();
2791
3661
  }
2792
- _cached2 = path10.basename(repoRoot);
3662
+ _cached2 = path12.basename(repoRoot);
2793
3663
  _cachedCwd = dir;
2794
3664
  return _cached2;
2795
3665
  } catch {
2796
- _cached2 = path10.basename(dir);
3666
+ _cached2 = path12.basename(dir);
2797
3667
  _cachedCwd = dir;
2798
3668
  return _cached2;
2799
3669
  }
@@ -2870,12 +3740,12 @@ var init_session_scope = __esm({
2870
3740
  });
2871
3741
 
2872
3742
  // src/lib/tasks-crud.ts
2873
- import crypto3 from "crypto";
2874
- import path11 from "path";
2875
- import os8 from "os";
3743
+ import crypto4 from "crypto";
3744
+ import path13 from "path";
3745
+ import os9 from "os";
2876
3746
  import { execSync as execSync5 } from "child_process";
2877
3747
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
2878
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "fs";
3748
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
2879
3749
  async function writeCheckpoint(input) {
2880
3750
  const client = getClient();
2881
3751
  const row = await resolveTask(client, input.taskId);
@@ -2991,7 +3861,7 @@ async function resolveTask(client, identifier, scopeSession) {
2991
3861
  }
2992
3862
  async function createTaskCore(input) {
2993
3863
  const client = getClient();
2994
- const id = crypto3.randomUUID();
3864
+ const id = crypto4.randomUUID();
2995
3865
  const now = (/* @__PURE__ */ new Date()).toISOString();
2996
3866
  const slug = slugify(input.title);
2997
3867
  let earlySessionScope = null;
@@ -3000,15 +3870,20 @@ async function createTaskCore(input) {
3000
3870
  const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
3001
3871
  const resolved = resolveExeSession2();
3002
3872
  if (resolved && input.projectName) {
3003
- const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
3004
- const sessionProject = getSessionProject2(resolved);
3005
- if (sessionProject && sessionProject !== input.projectName) {
3006
- scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
3007
- process.stderr.write(`[create_task] ${scopeMismatchWarning}
3008
- `);
3009
- earlySessionScope = null;
3010
- } else {
3873
+ const isCoordinatorSession = !resolved.includes("-");
3874
+ if (isCoordinatorSession) {
3011
3875
  earlySessionScope = resolved;
3876
+ } else {
3877
+ const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
3878
+ const sessionProject = getSessionProject2(resolved);
3879
+ if (sessionProject && sessionProject !== input.projectName) {
3880
+ scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
3881
+ process.stderr.write(`[create_task] ${scopeMismatchWarning}
3882
+ `);
3883
+ earlySessionScope = null;
3884
+ } else {
3885
+ earlySessionScope = resolved;
3886
+ }
3012
3887
  }
3013
3888
  } else {
3014
3889
  earlySessionScope = resolved;
@@ -3069,8 +3944,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
3069
3944
  }
3070
3945
  if (input.baseDir) {
3071
3946
  try {
3072
- await mkdir3(path11.join(input.baseDir, "exe", "output"), { recursive: true });
3073
- await mkdir3(path11.join(input.baseDir, "exe", "research"), { recursive: true });
3947
+ await mkdir3(path13.join(input.baseDir, "exe", "output"), { recursive: true });
3948
+ await mkdir3(path13.join(input.baseDir, "exe", "research"), { recursive: true });
3074
3949
  await ensureArchitectureDoc(input.baseDir, input.projectName);
3075
3950
  await ensureGitignoreExe(input.baseDir);
3076
3951
  } catch {
@@ -3106,10 +3981,10 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
3106
3981
  });
3107
3982
  if (input.baseDir) {
3108
3983
  try {
3109
- const EXE_OS_DIR = path11.join(os8.homedir(), ".exe-os");
3110
- const mdPath = path11.join(EXE_OS_DIR, taskFile);
3111
- const mdDir = path11.dirname(mdPath);
3112
- if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
3984
+ const EXE_OS_DIR = path13.join(os9.homedir(), ".exe-os");
3985
+ const mdPath = path13.join(EXE_OS_DIR, taskFile);
3986
+ const mdDir = path13.dirname(mdPath);
3987
+ if (!existsSync12(mdDir)) await mkdir3(mdDir, { recursive: true });
3113
3988
  const reviewer = input.reviewer ?? input.assignedBy;
3114
3989
  const mdContent = `# ${input.title}
3115
3990
 
@@ -3409,9 +4284,9 @@ async function deleteTaskCore(taskId, _baseDir) {
3409
4284
  return { taskFile, assignedTo, assignedBy, taskSlug };
3410
4285
  }
3411
4286
  async function ensureArchitectureDoc(baseDir, projectName) {
3412
- const archPath = path11.join(baseDir, "exe", "ARCHITECTURE.md");
4287
+ const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
3413
4288
  try {
3414
- if (existsSync10(archPath)) return;
4289
+ if (existsSync12(archPath)) return;
3415
4290
  const template = [
3416
4291
  `# ${projectName} \u2014 System Architecture`,
3417
4292
  "",
@@ -3444,10 +4319,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
3444
4319
  }
3445
4320
  }
3446
4321
  async function ensureGitignoreExe(baseDir) {
3447
- const gitignorePath = path11.join(baseDir, ".gitignore");
4322
+ const gitignorePath = path13.join(baseDir, ".gitignore");
3448
4323
  try {
3449
- if (existsSync10(gitignorePath)) {
3450
- const content = readFileSync9(gitignorePath, "utf-8");
4324
+ if (existsSync12(gitignorePath)) {
4325
+ const content = readFileSync11(gitignorePath, "utf-8");
3451
4326
  if (/^\/?exe\/?$/m.test(content)) return;
3452
4327
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
3453
4328
  } else {
@@ -3490,8 +4365,8 @@ __export(tasks_review_exports, {
3490
4365
  isStale: () => isStale,
3491
4366
  listPendingReviews: () => listPendingReviews
3492
4367
  });
3493
- import path12 from "path";
3494
- import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
4368
+ import path14 from "path";
4369
+ import { existsSync as existsSync13, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
3495
4370
  function formatAge(isoTimestamp) {
3496
4371
  if (!isoTimestamp) return "";
3497
4372
  const ms = Date.now() - new Date(isoTimestamp).getTime();
@@ -3760,11 +4635,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3760
4635
  );
3761
4636
  }
3762
4637
  try {
3763
- const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
3764
- if (existsSync11(cacheDir)) {
4638
+ const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
4639
+ if (existsSync13(cacheDir)) {
3765
4640
  for (const f of readdirSync2(cacheDir)) {
3766
4641
  if (f.startsWith("review-notified-")) {
3767
- unlinkSync3(path12.join(cacheDir, f));
4642
+ unlinkSync4(path14.join(cacheDir, f));
3768
4643
  }
3769
4644
  }
3770
4645
  }
@@ -3786,7 +4661,7 @@ var init_tasks_review = __esm({
3786
4661
  });
3787
4662
 
3788
4663
  // src/lib/tasks-chain.ts
3789
- import path13 from "path";
4664
+ import path15 from "path";
3790
4665
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
3791
4666
  async function cascadeUnblock(taskId, baseDir, now) {
3792
4667
  const client = getClient();
@@ -3803,7 +4678,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
3803
4678
  });
3804
4679
  for (const ur of unblockedRows.rows) {
3805
4680
  try {
3806
- const ubFile = path13.join(baseDir, String(ur.task_file));
4681
+ const ubFile = path15.join(baseDir, String(ur.task_file));
3807
4682
  let ubContent = await readFile3(ubFile, "utf-8");
3808
4683
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
3809
4684
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -3893,6 +4768,10 @@ async function dispatchTaskToEmployee(input) {
3893
4768
  if (transport.isAlive(sessionName)) {
3894
4769
  const result2 = sendIntercom(sessionName);
3895
4770
  const dispatched = result2 === "acknowledged" || result2 === "debounced" || result2 === "queued" ? "verified" : result2 === "delivered" ? "sent_unverified" : "session_dead";
4771
+ process.stderr.write(
4772
+ `[dispatch-audit] intercom \u2192 ${sessionName} | task="${input.title}" [${input.priority}] | result=${dispatched} (sendIntercom=${result2})
4773
+ `
4774
+ );
3896
4775
  return { dispatched, session: sessionName, crossProject };
3897
4776
  } else {
3898
4777
  const projectDir2 = input.projectDir ?? process.cwd();
@@ -3901,11 +4780,15 @@ async function dispatchTaskToEmployee(input) {
3901
4780
  });
3902
4781
  if (result2.status === "failed") {
3903
4782
  process.stderr.write(
3904
- `[dispatch] Failed to spawn ${input.assignedTo}: ${result2.error}
4783
+ `[dispatch-audit] SPAWN FAILED \u2192 ${input.assignedTo} | task="${input.title}" [${input.priority}] | error=${result2.error}
3905
4784
  `
3906
4785
  );
3907
4786
  return { dispatched: "session_missing" };
3908
4787
  }
4788
+ process.stderr.write(
4789
+ `[dispatch-audit] SPAWNED \u2192 ${result2.sessionName} | task="${input.title}" [${input.priority}]
4790
+ `
4791
+ );
3909
4792
  return { dispatched: "spawned", session: result2.sessionName, crossProject };
3910
4793
  }
3911
4794
  } catch {
@@ -3915,7 +4798,13 @@ async function dispatchTaskToEmployee(input) {
3915
4798
  function notifyTaskDone() {
3916
4799
  try {
3917
4800
  const key = getSessionKey();
3918
- if (key && !process.env.VITEST) notifyParentExe(key);
4801
+ if (key && !process.env.VITEST) {
4802
+ notifyParentExe(key);
4803
+ process.stderr.write(
4804
+ `[dispatch-audit] notifyTaskDone \u2192 parent coordinator (session=${key})
4805
+ `
4806
+ );
4807
+ }
3919
4808
  } catch {
3920
4809
  }
3921
4810
  }
@@ -3936,16 +4825,91 @@ var init_tasks_notify = __esm({
3936
4825
  }
3937
4826
  });
3938
4827
 
4828
+ // src/lib/embedder.ts
4829
+ var embedder_exports = {};
4830
+ __export(embedder_exports, {
4831
+ disposeEmbedder: () => disposeEmbedder,
4832
+ embed: () => embed,
4833
+ embedDirect: () => embedDirect,
4834
+ getEmbedder: () => getEmbedder
4835
+ });
4836
+ async function getEmbedder() {
4837
+ const ok = await connectEmbedDaemon();
4838
+ if (!ok) {
4839
+ throw new Error(
4840
+ "Could not connect to embedding daemon. Ensure the model is installed (run /exe-setup)."
4841
+ );
4842
+ }
4843
+ }
4844
+ async function embed(text) {
4845
+ const priority = process.env.EXE_EMBED_PRIORITY === "low" ? "low" : "high";
4846
+ const vector = await embedViaClient(text, priority);
4847
+ if (!vector) {
4848
+ throw new Error(
4849
+ "Embedding failed: daemon unavailable. Run /exe-setup to verify model installation."
4850
+ );
4851
+ }
4852
+ if (vector.length !== EMBEDDING_DIM) {
4853
+ throw new Error(
4854
+ `Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}. Ensure the correct Jina v5-small Q4_K_M GGUF model is installed.`
4855
+ );
4856
+ }
4857
+ return vector;
4858
+ }
4859
+ async function disposeEmbedder() {
4860
+ disconnectClient();
4861
+ }
4862
+ async function embedDirect(text) {
4863
+ const llamaCpp = await import("node-llama-cpp");
4864
+ const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
4865
+ const { existsSync: existsSync17 } = await import("fs");
4866
+ const path20 = await import("path");
4867
+ const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
4868
+ if (!existsSync17(modelPath)) {
4869
+ throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
4870
+ }
4871
+ const llama = await llamaCpp.getLlama();
4872
+ const model = await llama.loadModel({ modelPath });
4873
+ const context = await model.createEmbeddingContext();
4874
+ try {
4875
+ const embedding = await context.getEmbeddingFor(text);
4876
+ const vector = Array.from(embedding.vector);
4877
+ if (vector.length !== EMBEDDING_DIM) {
4878
+ throw new Error(
4879
+ `Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}.`
4880
+ );
4881
+ }
4882
+ return vector;
4883
+ } finally {
4884
+ await context.dispose();
4885
+ await model.dispose();
4886
+ }
4887
+ }
4888
+ var init_embedder = __esm({
4889
+ "src/lib/embedder.ts"() {
4890
+ "use strict";
4891
+ init_memory();
4892
+ init_exe_daemon_client();
4893
+ }
4894
+ });
4895
+
3939
4896
  // src/lib/behaviors.ts
3940
- import crypto4 from "crypto";
4897
+ import crypto5 from "crypto";
3941
4898
  async function storeBehavior(opts) {
3942
4899
  const client = getClient();
3943
- const id = crypto4.randomUUID();
4900
+ const id = crypto5.randomUUID();
3944
4901
  const now = (/* @__PURE__ */ new Date()).toISOString();
4902
+ let vector = null;
4903
+ try {
4904
+ const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
4905
+ const vec = await embed2(opts.content);
4906
+ vector = new Float32Array(vec);
4907
+ } catch {
4908
+ }
3945
4909
  await client.execute({
3946
- sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
3947
- VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
3948
- args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now]
4910
+ sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
4911
+ VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
4912
+ args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
3949
4913
  });
3950
4914
  return id;
3951
4915
  }
@@ -3969,7 +4933,7 @@ __export(skill_learning_exports, {
3969
4933
  storeTrajectory: () => storeTrajectory,
3970
4934
  sweepTrajectories: () => sweepTrajectories
3971
4935
  });
3972
- import crypto5 from "crypto";
4936
+ import crypto6 from "crypto";
3973
4937
  async function extractTrajectory(taskId, agentId) {
3974
4938
  const client = getClient();
3975
4939
  const result2 = await client.execute({
@@ -3998,11 +4962,11 @@ async function extractTrajectory(taskId, agentId) {
3998
4962
  return signature;
3999
4963
  }
4000
4964
  function hashSignature(signature) {
4001
- return crypto5.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
4965
+ return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
4002
4966
  }
4003
4967
  async function storeTrajectory(opts) {
4004
4968
  const client = getClient();
4005
- const id = crypto5.randomUUID();
4969
+ const id = crypto6.randomUUID();
4006
4970
  const now = (/* @__PURE__ */ new Date()).toISOString();
4007
4971
  const signatureHash = hashSignature(opts.signature);
4008
4972
  await client.execute({
@@ -4267,8 +5231,8 @@ __export(tasks_exports, {
4267
5231
  updateTaskStatus: () => updateTaskStatus,
4268
5232
  writeCheckpoint: () => writeCheckpoint
4269
5233
  });
4270
- import path14 from "path";
4271
- import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
5234
+ import path16 from "path";
5235
+ import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
4272
5236
  async function createTask(input) {
4273
5237
  const result2 = await createTaskCore(input);
4274
5238
  if (!input.skipDispatch && result2.status !== "blocked" && !process.env.VITEST) {
@@ -4287,14 +5251,14 @@ async function updateTask(input) {
4287
5251
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
4288
5252
  try {
4289
5253
  const agent = String(row.assigned_to);
4290
- const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
4291
- const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
5254
+ const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
5255
+ const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
4292
5256
  if (input.status === "in_progress") {
4293
5257
  mkdirSync5(cacheDir, { recursive: true });
4294
- writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
5258
+ writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
4295
5259
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
4296
5260
  try {
4297
- unlinkSync4(cachePath);
5261
+ unlinkSync5(cachePath);
4298
5262
  } catch {
4299
5263
  }
4300
5264
  }
@@ -4736,7 +5700,7 @@ var init_capacity_monitor = __esm({
4736
5700
  // src/lib/tmux-routing.ts
4737
5701
  var tmux_routing_exports = {};
4738
5702
  __export(tmux_routing_exports, {
4739
- acquireSpawnLock: () => acquireSpawnLock,
5703
+ acquireSpawnLock: () => acquireSpawnLock2,
4740
5704
  employeeSessionName: () => employeeSessionName,
4741
5705
  ensureEmployee: () => ensureEmployee,
4742
5706
  extractRootExe: () => extractRootExe,
@@ -4752,20 +5716,20 @@ __export(tmux_routing_exports, {
4752
5716
  notifyParentExe: () => notifyParentExe,
4753
5717
  parseParentExe: () => parseParentExe,
4754
5718
  registerParentExe: () => registerParentExe,
4755
- releaseSpawnLock: () => releaseSpawnLock,
5719
+ releaseSpawnLock: () => releaseSpawnLock2,
4756
5720
  resolveExeSession: () => resolveExeSession,
4757
5721
  sendIntercom: () => sendIntercom,
4758
5722
  spawnEmployee: () => spawnEmployee,
4759
5723
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
4760
5724
  });
4761
5725
  import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
4762
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync3 } from "fs";
4763
- import path15 from "path";
4764
- import os9 from "os";
4765
- import { fileURLToPath } from "url";
4766
- import { unlinkSync as unlinkSync5 } from "fs";
5726
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync3 } from "fs";
5727
+ import path17 from "path";
5728
+ import os10 from "os";
5729
+ import { fileURLToPath as fileURLToPath2 } from "url";
5730
+ import { unlinkSync as unlinkSync6 } from "fs";
4767
5731
  function spawnLockPath(sessionName) {
4768
- return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
5732
+ return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4769
5733
  }
4770
5734
  function isProcessAlive(pid) {
4771
5735
  try {
@@ -4775,14 +5739,14 @@ function isProcessAlive(pid) {
4775
5739
  return false;
4776
5740
  }
4777
5741
  }
4778
- function acquireSpawnLock(sessionName) {
4779
- if (!existsSync12(SPAWN_LOCK_DIR)) {
5742
+ function acquireSpawnLock2(sessionName) {
5743
+ if (!existsSync14(SPAWN_LOCK_DIR)) {
4780
5744
  mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
4781
5745
  }
4782
5746
  const lockFile = spawnLockPath(sessionName);
4783
- if (existsSync12(lockFile)) {
5747
+ if (existsSync14(lockFile)) {
4784
5748
  try {
4785
- const lock = JSON.parse(readFileSync10(lockFile, "utf8"));
5749
+ const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
4786
5750
  const age = Date.now() - lock.timestamp;
4787
5751
  if (isProcessAlive(lock.pid) && age < 6e4) {
4788
5752
  return false;
@@ -4790,25 +5754,25 @@ function acquireSpawnLock(sessionName) {
4790
5754
  } catch {
4791
5755
  }
4792
5756
  }
4793
- writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
5757
+ writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
4794
5758
  return true;
4795
5759
  }
4796
- function releaseSpawnLock(sessionName) {
5760
+ function releaseSpawnLock2(sessionName) {
4797
5761
  try {
4798
- unlinkSync5(spawnLockPath(sessionName));
5762
+ unlinkSync6(spawnLockPath(sessionName));
4799
5763
  } catch {
4800
5764
  }
4801
5765
  }
4802
5766
  function resolveBehaviorsExporterScript() {
4803
5767
  try {
4804
- const thisFile = fileURLToPath(import.meta.url);
4805
- const scriptPath = path15.join(
4806
- path15.dirname(thisFile),
5768
+ const thisFile = fileURLToPath2(import.meta.url);
5769
+ const scriptPath = path17.join(
5770
+ path17.dirname(thisFile),
4807
5771
  "..",
4808
5772
  "bin",
4809
5773
  "exe-export-behaviors.js"
4810
5774
  );
4811
- return existsSync12(scriptPath) ? scriptPath : null;
5775
+ return existsSync14(scriptPath) ? scriptPath : null;
4812
5776
  } catch {
4813
5777
  return null;
4814
5778
  }
@@ -4874,12 +5838,12 @@ function extractRootExe(name) {
4874
5838
  return parts.length > 0 ? parts[parts.length - 1] : null;
4875
5839
  }
4876
5840
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4877
- if (!existsSync12(SESSION_CACHE)) {
5841
+ if (!existsSync14(SESSION_CACHE)) {
4878
5842
  mkdirSync6(SESSION_CACHE, { recursive: true });
4879
5843
  }
4880
5844
  const rootExe = extractRootExe(parentExe) ?? parentExe;
4881
- const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4882
- writeFileSync7(filePath, JSON.stringify({
5845
+ const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5846
+ writeFileSync8(filePath, JSON.stringify({
4883
5847
  parentExe: rootExe,
4884
5848
  dispatchedBy: dispatchedBy || rootExe,
4885
5849
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -4887,7 +5851,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4887
5851
  }
4888
5852
  function getParentExe(sessionKey) {
4889
5853
  try {
4890
- const data = JSON.parse(readFileSync10(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5854
+ const data = JSON.parse(readFileSync12(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4891
5855
  return data.parentExe || null;
4892
5856
  } catch {
4893
5857
  return null;
@@ -4895,8 +5859,8 @@ function getParentExe(sessionKey) {
4895
5859
  }
4896
5860
  function getDispatchedBy(sessionKey) {
4897
5861
  try {
4898
- const data = JSON.parse(readFileSync10(
4899
- path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5862
+ const data = JSON.parse(readFileSync12(
5863
+ path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4900
5864
  "utf8"
4901
5865
  ));
4902
5866
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -4931,10 +5895,10 @@ function isEmployeeAlive(sessionName) {
4931
5895
  }
4932
5896
  function findFreeInstance(employeeName2, exeSession2, maxInstances = 10, isAlive = isEmployeeAlive) {
4933
5897
  const base = employeeSessionName(employeeName2, exeSession2);
4934
- if (!isAlive(base) && acquireSpawnLock(base)) return 0;
5898
+ if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
4935
5899
  for (let i = 2; i <= maxInstances; i++) {
4936
5900
  const candidate = employeeSessionName(employeeName2, exeSession2, i);
4937
- if (!isAlive(candidate) && acquireSpawnLock(candidate)) return i;
5901
+ if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
4938
5902
  }
4939
5903
  return null;
4940
5904
  }
@@ -4966,8 +5930,8 @@ async function verifyPaneAtCapacity(sessionName) {
4966
5930
  }
4967
5931
  function readDebounceState() {
4968
5932
  try {
4969
- if (!existsSync12(DEBOUNCE_FILE)) return {};
4970
- const raw = JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
5933
+ if (!existsSync14(DEBOUNCE_FILE)) return {};
5934
+ const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
4971
5935
  const state = {};
4972
5936
  for (const [key, val] of Object.entries(raw)) {
4973
5937
  if (typeof val === "number") {
@@ -4983,8 +5947,8 @@ function readDebounceState() {
4983
5947
  }
4984
5948
  function writeDebounceState(state) {
4985
5949
  try {
4986
- if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
4987
- writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
5950
+ if (!existsSync14(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
5951
+ writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
4988
5952
  } catch {
4989
5953
  }
4990
5954
  }
@@ -5048,7 +6012,7 @@ function isSessionBusy(sessionName) {
5048
6012
  function isExeSession(sessionName) {
5049
6013
  const matchesBaseWithInstance = (baseName) => sessionName === baseName || sessionName.startsWith(baseName) && /^\d+$/.test(sessionName.slice(baseName.length));
5050
6014
  const coordinatorName = getCoordinatorName();
5051
- return matchesBaseWithInstance(coordinatorName) || matchesBaseWithInstance("exe");
6015
+ return matchesBaseWithInstance(coordinatorName);
5052
6016
  }
5053
6017
  function sendIntercom(targetSession) {
5054
6018
  const transport = getTransport();
@@ -5083,8 +6047,8 @@ function sendIntercom(targetSession) {
5083
6047
  try {
5084
6048
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
5085
6049
  const agent = baseAgentName(rawAgent);
5086
- const markerPath = path15.join(SESSION_CACHE, `current-task-${agent}.json`);
5087
- if (existsSync12(markerPath)) {
6050
+ const markerPath = path17.join(SESSION_CACHE, `current-task-${agent}.json`);
6051
+ if (existsSync14(markerPath)) {
5088
6052
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
5089
6053
  return "debounced";
5090
6054
  }
@@ -5094,8 +6058,8 @@ function sendIntercom(targetSession) {
5094
6058
  try {
5095
6059
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
5096
6060
  const agent = baseAgentName(rawAgent);
5097
- const taskDir = path15.join(process.cwd(), "exe", agent);
5098
- if (existsSync12(taskDir)) {
6061
+ const taskDir = path17.join(process.cwd(), "exe", agent);
6062
+ if (existsSync14(taskDir)) {
5099
6063
  const files = readdirSync3(taskDir).filter(
5100
6064
  (f) => f.endsWith(".md") && f !== "DONE.txt"
5101
6065
  );
@@ -5262,26 +6226,26 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
5262
6226
  const transport = getTransport();
5263
6227
  const sessionName = employeeSessionName(employeeName2, exeSession2, opts?.instance);
5264
6228
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName2}${opts.instance}` : employeeName2;
5265
- const logDir = path15.join(os9.homedir(), ".exe-os", "session-logs");
5266
- const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5267
- if (!existsSync12(logDir)) {
6229
+ const logDir = path17.join(os10.homedir(), ".exe-os", "session-logs");
6230
+ const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
6231
+ if (!existsSync14(logDir)) {
5268
6232
  mkdirSync6(logDir, { recursive: true });
5269
6233
  }
5270
6234
  transport.kill(sessionName);
5271
6235
  let cleanupSuffix = "";
5272
6236
  try {
5273
- const thisFile = fileURLToPath(import.meta.url);
5274
- const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5275
- if (existsSync12(cleanupScript)) {
6237
+ const thisFile = fileURLToPath2(import.meta.url);
6238
+ const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
6239
+ if (existsSync14(cleanupScript)) {
5276
6240
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName2}" "${exeSession2}"`;
5277
6241
  }
5278
6242
  } catch {
5279
6243
  }
5280
6244
  try {
5281
- const claudeJsonPath = path15.join(os9.homedir(), ".claude.json");
6245
+ const claudeJsonPath = path17.join(os10.homedir(), ".claude.json");
5282
6246
  let claudeJson = {};
5283
6247
  try {
5284
- claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
6248
+ claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
5285
6249
  } catch {
5286
6250
  }
5287
6251
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -5289,17 +6253,17 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
5289
6253
  const trustDir = opts?.cwd ?? projectDir2;
5290
6254
  if (!projects[trustDir]) projects[trustDir] = {};
5291
6255
  projects[trustDir].hasTrustDialogAccepted = true;
5292
- writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
6256
+ writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
5293
6257
  } catch {
5294
6258
  }
5295
6259
  try {
5296
- const settingsDir = path15.join(os9.homedir(), ".claude", "projects");
6260
+ const settingsDir = path17.join(os10.homedir(), ".claude", "projects");
5297
6261
  const normalizedKey = (opts?.cwd ?? projectDir2).replace(/\//g, "-").replace(/^-/, "");
5298
- const projSettingsDir = path15.join(settingsDir, normalizedKey);
5299
- const settingsPath = path15.join(projSettingsDir, "settings.json");
6262
+ const projSettingsDir = path17.join(settingsDir, normalizedKey);
6263
+ const settingsPath = path17.join(projSettingsDir, "settings.json");
5300
6264
  let settings = {};
5301
6265
  try {
5302
- settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
6266
+ settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
5303
6267
  } catch {
5304
6268
  }
5305
6269
  const perms = settings.permissions ?? {};
@@ -5328,7 +6292,7 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
5328
6292
  perms.allow = allow;
5329
6293
  settings.permissions = perms;
5330
6294
  mkdirSync6(projSettingsDir, { recursive: true });
5331
- writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
6295
+ writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5332
6296
  }
5333
6297
  } catch {
5334
6298
  }
@@ -5343,8 +6307,8 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
5343
6307
  let behaviorsFlag = "";
5344
6308
  let legacyFallbackWarned = false;
5345
6309
  if (!useExeAgent && !useBinSymlink) {
5346
- const identityPath = path15.join(
5347
- os9.homedir(),
6310
+ const identityPath = path17.join(
6311
+ os10.homedir(),
5348
6312
  ".exe-os",
5349
6313
  "identity",
5350
6314
  `${employeeName2}.md`
@@ -5353,13 +6317,13 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
5353
6317
  const hasAgentFlag = claudeSupportsAgentFlag();
5354
6318
  if (hasAgentFlag) {
5355
6319
  identityFlag = ` --agent ${employeeName2}`;
5356
- } else if (existsSync12(identityPath)) {
6320
+ } else if (existsSync14(identityPath)) {
5357
6321
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
5358
6322
  legacyFallbackWarned = true;
5359
6323
  }
5360
6324
  const behaviorsFile = exportBehaviorsSync(
5361
6325
  employeeName2,
5362
- path15.basename(spawnCwd),
6326
+ path17.basename(spawnCwd),
5363
6327
  sessionName
5364
6328
  );
5365
6329
  if (behaviorsFile) {
@@ -5374,16 +6338,16 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
5374
6338
  }
5375
6339
  let sessionContextFlag = "";
5376
6340
  try {
5377
- const ctxDir = path15.join(os9.homedir(), ".exe-os", "session-cache");
6341
+ const ctxDir = path17.join(os10.homedir(), ".exe-os", "session-cache");
5378
6342
  mkdirSync6(ctxDir, { recursive: true });
5379
- const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
6343
+ const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
5380
6344
  const ctxContent = [
5381
6345
  `## Session Context`,
5382
6346
  `You are running in tmux session: ${sessionName}.`,
5383
6347
  `Your parent coordinator session is ${exeSession2}.`,
5384
6348
  `Your employees (if any) use the -${exeSession2} suffix.`
5385
6349
  ].join("\n");
5386
- writeFileSync7(ctxFile, ctxContent);
6350
+ writeFileSync8(ctxFile, ctxContent);
5387
6351
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
5388
6352
  } catch {
5389
6353
  }
@@ -5457,14 +6421,14 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
5457
6421
  command: spawnCommand
5458
6422
  });
5459
6423
  if (spawnResult.error) {
5460
- releaseSpawnLock(sessionName);
6424
+ releaseSpawnLock2(sessionName);
5461
6425
  return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
5462
6426
  }
5463
6427
  transport.pipeLog(sessionName, logFile);
5464
6428
  try {
5465
6429
  const mySession = getMySession();
5466
- const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5467
- writeFileSync7(dispatchInfo, JSON.stringify({
6430
+ const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
6431
+ writeFileSync8(dispatchInfo, JSON.stringify({
5468
6432
  dispatchedBy: mySession,
5469
6433
  rootExe: exeSession2,
5470
6434
  provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
@@ -5502,7 +6466,7 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
5502
6466
  }
5503
6467
  }
5504
6468
  if (!booted) {
5505
- releaseSpawnLock(sessionName);
6469
+ releaseSpawnLock2(sessionName);
5506
6470
  const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
5507
6471
  return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
5508
6472
  }
@@ -5520,7 +6484,7 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
5520
6484
  pid: 0,
5521
6485
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
5522
6486
  });
5523
- releaseSpawnLock(sessionName);
6487
+ releaseSpawnLock2(sessionName);
5524
6488
  return { sessionName };
5525
6489
  }
5526
6490
  var SPAWN_LOCK_DIR, SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, VALID_SESSION_NAME, VERIFY_PANE_LINES, INTERCOM_DEBOUNCE_MS, CODEX_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
@@ -5538,20 +6502,77 @@ var init_tmux_routing = __esm({
5538
6502
  init_intercom_queue();
5539
6503
  init_plan_limits();
5540
6504
  init_employees();
5541
- SPAWN_LOCK_DIR = path15.join(os9.homedir(), ".exe-os", "spawn-locks");
5542
- SESSION_CACHE = path15.join(os9.homedir(), ".exe-os", "session-cache");
6505
+ SPAWN_LOCK_DIR = path17.join(os10.homedir(), ".exe-os", "spawn-locks");
6506
+ SESSION_CACHE = path17.join(os10.homedir(), ".exe-os", "session-cache");
5543
6507
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
5544
6508
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
5545
6509
  VERIFY_PANE_LINES = 200;
5546
6510
  INTERCOM_DEBOUNCE_MS = 3e4;
5547
6511
  CODEX_DEBOUNCE_MS = 12e4;
5548
- INTERCOM_LOG2 = path15.join(os9.homedir(), ".exe-os", "intercom.log");
5549
- DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
6512
+ INTERCOM_LOG2 = path17.join(os10.homedir(), ".exe-os", "intercom.log");
6513
+ DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
5550
6514
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5551
6515
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
5552
6516
  }
5553
6517
  });
5554
6518
 
6519
+ // src/lib/keychain.ts
6520
+ import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
6521
+ import { existsSync as existsSync15 } from "fs";
6522
+ import path18 from "path";
6523
+ import os11 from "os";
6524
+ function getKeyDir() {
6525
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path18.join(os11.homedir(), ".exe-os");
6526
+ }
6527
+ function getKeyPath() {
6528
+ return path18.join(getKeyDir(), "master.key");
6529
+ }
6530
+ async function tryKeytar() {
6531
+ try {
6532
+ return await import("keytar");
6533
+ } catch {
6534
+ return null;
6535
+ }
6536
+ }
6537
+ async function getMasterKey() {
6538
+ const keytar = await tryKeytar();
6539
+ if (keytar) {
6540
+ try {
6541
+ const stored = await keytar.getPassword(SERVICE, ACCOUNT);
6542
+ if (stored) {
6543
+ return Buffer.from(stored, "base64");
6544
+ }
6545
+ } catch {
6546
+ }
6547
+ }
6548
+ const keyPath = getKeyPath();
6549
+ if (!existsSync15(keyPath)) {
6550
+ process.stderr.write(
6551
+ `[keychain] Key not found at ${keyPath} (HOME=${os11.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
6552
+ `
6553
+ );
6554
+ return null;
6555
+ }
6556
+ try {
6557
+ const content = await readFile4(keyPath, "utf-8");
6558
+ return Buffer.from(content.trim(), "base64");
6559
+ } catch (err) {
6560
+ process.stderr.write(
6561
+ `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
6562
+ `
6563
+ );
6564
+ return null;
6565
+ }
6566
+ }
6567
+ var SERVICE, ACCOUNT;
6568
+ var init_keychain = __esm({
6569
+ "src/lib/keychain.ts"() {
6570
+ "use strict";
6571
+ SERVICE = "exe-mem";
6572
+ ACCOUNT = "master-key";
6573
+ }
6574
+ });
6575
+
5555
6576
  // src/lib/shard-manager.ts
5556
6577
  var shard_manager_exports = {};
5557
6578
  __export(shard_manager_exports, {
@@ -5566,12 +6587,12 @@ __export(shard_manager_exports, {
5566
6587
  listShards: () => listShards,
5567
6588
  shardExists: () => shardExists
5568
6589
  });
5569
- import path17 from "path";
5570
- import { existsSync as existsSync14, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
6590
+ import path19 from "path";
6591
+ import { existsSync as existsSync16, mkdirSync as mkdirSync7, readdirSync as readdirSync4 } from "fs";
5571
6592
  import { createClient as createClient2 } from "@libsql/client";
5572
6593
  function initShardManager(encryptionKey) {
5573
6594
  _encryptionKey = encryptionKey;
5574
- if (!existsSync14(SHARDS_DIR)) {
6595
+ if (!existsSync16(SHARDS_DIR)) {
5575
6596
  mkdirSync7(SHARDS_DIR, { recursive: true });
5576
6597
  }
5577
6598
  _shardingEnabled = true;
@@ -5601,7 +6622,7 @@ function getShardClient(projectName) {
5601
6622
  while (_shards.size >= MAX_OPEN_SHARDS) {
5602
6623
  evictLRU();
5603
6624
  }
5604
- const dbPath = path17.join(SHARDS_DIR, `${safeName}.db`);
6625
+ const dbPath = path19.join(SHARDS_DIR, `${safeName}.db`);
5605
6626
  const client = createClient2({
5606
6627
  url: `file:${dbPath}`,
5607
6628
  encryptionKey: _encryptionKey
@@ -5612,10 +6633,10 @@ function getShardClient(projectName) {
5612
6633
  }
5613
6634
  function shardExists(projectName) {
5614
6635
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
5615
- return existsSync14(path17.join(SHARDS_DIR, `${safeName}.db`));
6636
+ return existsSync16(path19.join(SHARDS_DIR, `${safeName}.db`));
5616
6637
  }
5617
6638
  function listShards() {
5618
- if (!existsSync14(SHARDS_DIR)) return [];
6639
+ if (!existsSync16(SHARDS_DIR)) return [];
5619
6640
  return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
5620
6641
  }
5621
6642
  async function ensureShardSchema(client) {
@@ -5862,7 +6883,7 @@ var init_shard_manager = __esm({
5862
6883
  "src/lib/shard-manager.ts"() {
5863
6884
  "use strict";
5864
6885
  init_config();
5865
- SHARDS_DIR = path17.join(EXE_AI_DIR, "shards");
6886
+ SHARDS_DIR = path19.join(EXE_AI_DIR, "shards");
5866
6887
  SHARD_IDLE_MS = 5 * 60 * 1e3;
5867
6888
  MAX_OPEN_SHARDS = 10;
5868
6889
  EVICTION_INTERVAL_MS = 60 * 1e3;
@@ -5982,6 +7003,68 @@ var init_platform_procedures = __esm({
5982
7003
  domain: "architecture",
5983
7004
  priority: "p0",
5984
7005
  content: "Tasks live in the DB. Intercom (tmux send-keys) is fire-and-forget \u2014 it may fail, get garbled, or arrive mid-work. Never rely on intercom for task delivery. The UserPromptSubmit hook checks the DB for new tasks on every prompt. Your operating procedures step 7 says check for next work. The daemon nudges idle agents as a speedup. If you have no tasks, you found them all."
7006
+ },
7007
+ // --- MCP is the ONLY data interface ---
7008
+ {
7009
+ title: "MCP disconnect \u2014 ask the user, never work around it",
7010
+ domain: "workflow",
7011
+ priority: "p0",
7012
+ content: "If MCP tools are unavailable, disconnected, or returning connection errors: STOP. Tell the user clearly: 'MCP server is disconnected. Please run /mcp to reconnect.' Do NOT attempt workarounds \u2014 no raw Node imports, no direct DB access, no CLI hacks, no daemon socket calls. MCP is the ONLY data interface. Working around it wastes time, hits bundling issues, and bypasses the contract boundary. Ask once, wait, proceed when reconnected."
7013
+ },
7014
+ // --- MCP Tool Catalog (Layer 0 — every agent knows what tools exist) ---
7015
+ {
7016
+ title: "MCP tools \u2014 memory and search",
7017
+ domain: "tool-use",
7018
+ priority: "p1",
7019
+ content: "recall_my_memory: search your own memories (semantic + FTS). ask_team_memory: search a colleague's memories by agent name. store_memory: persist a memory (decisions, summaries, context). commit_memory: high-importance memory that survives consolidation. search_everything: unified search across memories, tasks, entities, conversations. get_session_context: temporal window of memories around a timestamp. consolidate_memories: merge duplicate/related memories into insights. get_memory_cardinality: count memories per agent (health check)."
7020
+ },
7021
+ {
7022
+ title: "MCP tools \u2014 task orchestration",
7023
+ domain: "tool-use",
7024
+ priority: "p1",
7025
+ content: "create_task: dispatch work to an employee (auto-spawns session). The ONLY dispatch path. list_tasks: query tasks by status, assignee, project. get_task: fetch full task details by ID. update_task: change status (in_progress, done, blocked, cancelled) + add result summary. close_task: finalize a reviewed task (COO only). checkpoint_task: save progress state for crash recovery. resume_employee: re-spawn an employee session for an existing task."
7026
+ },
7027
+ {
7028
+ title: "MCP tools \u2014 knowledge graph (GraphRAG)",
7029
+ domain: "tool-use",
7030
+ priority: "p1",
7031
+ content: "query_relationships: find connections between entities in the knowledge graph. get_entity_neighbors: explore an entity's direct connections. get_hot_entities: find most-referenced entities (trending topics). get_graph_stats: graph health \u2014 entity/relationship counts, density. export_graph: export graph data for visualization. merge_entities: deduplicate entities (alias resolution). find_similar_trajectories: match tool-call patterns to past task solutions."
7032
+ },
7033
+ {
7034
+ title: "MCP tools \u2014 identity, behavior, and decisions",
7035
+ domain: "tool-use",
7036
+ priority: "p1",
7037
+ content: "get_identity: read an agent's exe.md (Layer 1 identity). update_identity: write an agent's exe.md. Identity > behavior \u2014 use for permanent rules. store_behavior: record a correction or pattern for an agent (Layer 2 expertise). list_behaviors: view an agent's active behaviors. deactivate_behavior: soft-delete a stale or conflicting behavior. store_decision: record an ADR (architectural decision record). get_decision: retrieve a past decision by query."
7038
+ },
7039
+ {
7040
+ title: "MCP tools \u2014 communication and messaging",
7041
+ domain: "tool-use",
7042
+ priority: "p1",
7043
+ content: "send_message: send supplementary context to another agent (NOT for actionable work \u2014 use create_task). acknowledge_messages: mark messages as read. send_whatsapp: send WhatsApp message via gateway (customer-facing alerts). query_conversations: search ingested conversations across all channels (WhatsApp, email, etc.)."
7044
+ },
7045
+ {
7046
+ title: "MCP tools \u2014 wiki, documents, and content",
7047
+ domain: "tool-use",
7048
+ priority: "p1",
7049
+ content: "create_wiki_page: create a wiki page in exe-wiki. list_wiki_pages: browse wiki pages. get_wiki_page: read a wiki page. update_wiki_page: edit a wiki page. ingest_document: import a file (PDF, MD, etc.) into memory as chunks. list_documents: browse ingested documents by workspace. purge_document: remove a document and its memory chunks. set_document_importance: adjust chunk importance scores. rerank_documents: re-score document relevance for a query."
7050
+ },
7051
+ {
7052
+ title: "MCP tools \u2014 system, operations, and admin",
7053
+ domain: "tool-use",
7054
+ priority: "p1",
7055
+ content: "get_agent_spend: token usage per agent/session (cost tracking). list_agent_sessions: view agent session history. get_session_kills: audit log of killed sessions. get_daemon_health: check exed daemon status. get_auto_wake_status: daemon auto-wake configuration. get_worker_gate: check worker deployment gates. run_memory_audit: health check \u2014 duplicates, null vectors, orphaned rows. run_consolidation: trigger sleep-time memory consolidation. cloud_sync: force a cloud sync cycle. backup_vps: trigger VPS backup."
7056
+ },
7057
+ {
7058
+ title: "MCP tools \u2014 config, licensing, and team",
7059
+ domain: "tool-use",
7060
+ priority: "p1",
7061
+ content: "set_agent_config: view/change per-agent runtime and model settings. list_employees: view the employee roster. add_person: add a person to the CRM contacts roster. list_people: browse CRM contacts. get_person: fetch contact details. get_license_status: check license validity. create_license: generate a new license key (admin). list_licenses: view all issued licenses. activate_license: activate a license on a device."
7062
+ },
7063
+ {
7064
+ title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
7065
+ domain: "tool-use",
7066
+ priority: "p1",
7067
+ content: "create_trigger: set up a scheduled recurring agent job (cron). list_triggers: view active triggers. load_skill: load a slash-command skill dynamically. apply_starter_pack: import a pre-built behavior + identity pack for a role. export_orchestration: export full org state (tasks, behaviors, identities) as portable JSON. import_orchestration: import org state into a new instance. deploy_client: deploy a customer client instance. query_company_brain: unified RAG query across all company knowledge. create_reminder: set a text reminder (shown in boot brief). list_reminders: view pending reminders. complete_reminder: mark a reminder done. global_procedure: manage Layer 0 procedures (actions: store, list, deactivate). Legacy aliases: store_global_procedure, list_global_procedures, deactivate_global_procedure."
5985
7068
  }
5986
7069
  ];
5987
7070
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -5998,7 +7081,7 @@ __export(global_procedures_exports, {
5998
7081
  loadGlobalProcedures: () => loadGlobalProcedures,
5999
7082
  storeGlobalProcedure: () => storeGlobalProcedure
6000
7083
  });
6001
- import { randomUUID as randomUUID2 } from "crypto";
7084
+ import { randomUUID as randomUUID3 } from "crypto";
6002
7085
  async function loadGlobalProcedures() {
6003
7086
  const client = getClient();
6004
7087
  const result2 = await client.execute({
@@ -6027,7 +7110,7 @@ ${sections.join("\n\n")}
6027
7110
  `;
6028
7111
  }
6029
7112
  async function storeGlobalProcedure(input) {
6030
- const id = randomUUID2();
7113
+ const id = randomUUID3();
6031
7114
  const now = (/* @__PURE__ */ new Date()).toISOString();
6032
7115
  const client = getClient();
6033
7116
  await client.execute({
@@ -6061,70 +7144,25 @@ ${p.content}`).join("\n\n");
6061
7144
  }
6062
7145
  });
6063
7146
 
6064
- // src/bin/exe-dispatch.ts
6065
- init_tmux_routing();
6066
- init_tasks_crud();
6067
-
6068
7147
  // src/lib/store.ts
7148
+ var store_exports = {};
7149
+ __export(store_exports, {
7150
+ attachDocumentMetadata: () => attachDocumentMetadata,
7151
+ buildRawVisibilityFilter: () => buildRawVisibilityFilter,
7152
+ buildWikiScopeFilter: () => buildWikiScopeFilter,
7153
+ classifyTier: () => classifyTier,
7154
+ disposeStore: () => disposeStore,
7155
+ flushBatch: () => flushBatch,
7156
+ flushTier3: () => flushTier3,
7157
+ getMemoryCardinality: () => getMemoryCardinality,
7158
+ initStore: () => initStore,
7159
+ reserveVersions: () => reserveVersions,
7160
+ searchMemories: () => searchMemories,
7161
+ updateMemoryStatus: () => updateMemoryStatus,
7162
+ vectorToBlob: () => vectorToBlob,
7163
+ writeMemory: () => writeMemory
7164
+ });
6069
7165
  import { createHash } from "crypto";
6070
- init_database();
6071
-
6072
- // src/lib/keychain.ts
6073
- import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
6074
- import { existsSync as existsSync13 } from "fs";
6075
- import path16 from "path";
6076
- import os10 from "os";
6077
- var SERVICE = "exe-mem";
6078
- var ACCOUNT = "master-key";
6079
- function getKeyDir() {
6080
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path16.join(os10.homedir(), ".exe-os");
6081
- }
6082
- function getKeyPath() {
6083
- return path16.join(getKeyDir(), "master.key");
6084
- }
6085
- async function tryKeytar() {
6086
- try {
6087
- return await import("keytar");
6088
- } catch {
6089
- return null;
6090
- }
6091
- }
6092
- async function getMasterKey() {
6093
- const keytar = await tryKeytar();
6094
- if (keytar) {
6095
- try {
6096
- const stored = await keytar.getPassword(SERVICE, ACCOUNT);
6097
- if (stored) {
6098
- return Buffer.from(stored, "base64");
6099
- }
6100
- } catch {
6101
- }
6102
- }
6103
- const keyPath = getKeyPath();
6104
- if (!existsSync13(keyPath)) {
6105
- process.stderr.write(
6106
- `[keychain] Key not found at ${keyPath} (HOME=${os10.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
6107
- `
6108
- );
6109
- return null;
6110
- }
6111
- try {
6112
- const content = await readFile4(keyPath, "utf-8");
6113
- return Buffer.from(content.trim(), "base64");
6114
- } catch (err) {
6115
- process.stderr.write(
6116
- `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
6117
- `
6118
- );
6119
- return null;
6120
- }
6121
- }
6122
-
6123
- // src/lib/store.ts
6124
- init_config();
6125
- init_state_bus();
6126
- var INIT_MAX_RETRIES = 3;
6127
- var INIT_RETRY_DELAY_MS = 1e3;
6128
7166
  function isBusyError2(err) {
6129
7167
  if (err instanceof Error) {
6130
7168
  const msg = err.message.toLowerCase();
@@ -6147,12 +7185,6 @@ async function retryOnBusy2(fn, label) {
6147
7185
  }
6148
7186
  throw new Error("unreachable");
6149
7187
  }
6150
- var _pendingRecords = [];
6151
- var _batchSize = 20;
6152
- var _flushIntervalMs = 1e4;
6153
- var _flushTimer = null;
6154
- var _flushing = false;
6155
- var _nextVersion = 1;
6156
7188
  async function initStore(options) {
6157
7189
  if (_flushTimer !== null) {
6158
7190
  clearInterval(_flushTimer);
@@ -6182,6 +7214,11 @@ async function initStore(options) {
6182
7214
  encryptionKey: hexKey
6183
7215
  });
6184
7216
  await retryOnBusy2(() => ensureSchema(), "ensureSchema");
7217
+ try {
7218
+ const { initDaemonClient: initDaemonClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
7219
+ await initDaemonClient2();
7220
+ } catch {
7221
+ }
6185
7222
  if (!options?.lightweight) {
6186
7223
  try {
6187
7224
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
@@ -6201,6 +7238,565 @@ async function initStore(options) {
6201
7238
  }
6202
7239
  }
6203
7240
  }
7241
+ function classifyTier(record) {
7242
+ if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
7243
+ if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
7244
+ return 3;
7245
+ }
7246
+ function inferFilePaths(record) {
7247
+ if (!["Read", "Write", "Edit"].includes(record.tool_name)) return null;
7248
+ const firstLine = record.raw_text.split("\n")[0] ?? "";
7249
+ const match = firstLine.match(/(\/[\w./-]+\.\w+)/);
7250
+ return match ? JSON.stringify([match[1]]) : null;
7251
+ }
7252
+ function inferCommitHash(record) {
7253
+ if (record.tool_name !== "Bash") return null;
7254
+ const match = record.raw_text.match(/\b([a-f0-9]{7,40})\b/);
7255
+ return match ? match[1] : null;
7256
+ }
7257
+ function inferLanguageType(record) {
7258
+ const text = record.raw_text;
7259
+ if (!text || text.length < 10) return null;
7260
+ const trimmed = text.trimStart();
7261
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) return "json";
7262
+ if (/\b(SELECT|INSERT|UPDATE|DELETE|CREATE TABLE|ALTER TABLE)\b/i.test(text)) return "sql";
7263
+ if (/\b(function |const |import |export |class |def |async |=>)\b/.test(text)) return "code";
7264
+ if (trimmed.startsWith("#") || trimmed.startsWith("*")) return "prose";
7265
+ return "mixed";
7266
+ }
7267
+ function inferDomain(record) {
7268
+ const proj = (record.project_name ?? "").toLowerCase();
7269
+ if (proj.includes("marketing") || proj.includes("content")) return "marketing";
7270
+ if (proj.includes("crm") || proj.includes("customer")) return "customer";
7271
+ return null;
7272
+ }
7273
+ async function writeMemory(record) {
7274
+ if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
7275
+ throw new Error(
7276
+ `Expected ${EMBEDDING_DIM}-dim vector, got ${record.vector.length}`
7277
+ );
7278
+ }
7279
+ const contentHash = createHash("md5").update(record.raw_text).digest("hex");
7280
+ if (_pendingRecords.some((r) => r.content_hash === contentHash && r.agent_id === record.agent_id)) {
7281
+ return;
7282
+ }
7283
+ try {
7284
+ const client = getClient();
7285
+ const existing = await client.execute({
7286
+ sql: "SELECT id FROM memories WHERE content_hash = ? AND agent_id = ? LIMIT 1",
7287
+ args: [contentHash, record.agent_id]
7288
+ });
7289
+ if (existing.rows.length > 0) return;
7290
+ } catch {
7291
+ }
7292
+ const dbRow = {
7293
+ id: record.id,
7294
+ agent_id: record.agent_id,
7295
+ agent_role: record.agent_role,
7296
+ session_id: record.session_id,
7297
+ timestamp: record.timestamp,
7298
+ tool_name: record.tool_name,
7299
+ project_name: record.project_name,
7300
+ has_error: record.has_error ? 1 : 0,
7301
+ raw_text: record.raw_text,
7302
+ vector: record.vector,
7303
+ version: 0,
7304
+ // Placeholder — assigned atomically at flush time
7305
+ task_id: record.task_id ?? null,
7306
+ importance: record.importance ?? 5,
7307
+ status: record.status ?? "active",
7308
+ confidence: record.confidence ?? 0.7,
7309
+ last_accessed: record.last_accessed ?? record.timestamp,
7310
+ workspace_id: record.workspace_id ?? null,
7311
+ document_id: record.document_id ?? null,
7312
+ user_id: record.user_id ?? null,
7313
+ char_offset: record.char_offset ?? null,
7314
+ page_number: record.page_number ?? null,
7315
+ source_path: record.source_path ?? null,
7316
+ source_type: record.source_type ?? null,
7317
+ tier: record.tier ?? classifyTier(record),
7318
+ supersedes_id: record.supersedes_id ?? null,
7319
+ draft: record.draft ? 1 : 0,
7320
+ memory_type: record.memory_type ?? "raw",
7321
+ trajectory: record.trajectory ? JSON.stringify(record.trajectory) : null,
7322
+ content_hash: contentHash,
7323
+ intent: record.intent ?? null,
7324
+ outcome: record.outcome ?? null,
7325
+ domain: record.domain ?? inferDomain(record),
7326
+ referenced_entities: record.referenced_entities ?? null,
7327
+ retrieval_count: record.retrieval_count ?? 0,
7328
+ chain_position: record.chain_position ?? null,
7329
+ review_status: record.review_status ?? null,
7330
+ context_window_pct: record.context_window_pct ?? null,
7331
+ file_paths: record.file_paths ?? inferFilePaths(record),
7332
+ commit_hash: record.commit_hash ?? inferCommitHash(record),
7333
+ duration_ms: record.duration_ms ?? null,
7334
+ token_cost: record.token_cost ?? null,
7335
+ audience: record.audience ?? null,
7336
+ language_type: record.language_type ?? inferLanguageType(record),
7337
+ parent_memory_id: record.parent_memory_id ?? null
7338
+ };
7339
+ _pendingRecords.push(dbRow);
7340
+ orgBus.emit({
7341
+ type: "memory_stored",
7342
+ agentId: record.agent_id,
7343
+ project: record.project_name,
7344
+ timestamp: record.timestamp
7345
+ });
7346
+ const MAX_PENDING = 1e3;
7347
+ if (_pendingRecords.length > MAX_PENDING) {
7348
+ const dropped = _pendingRecords.length - MAX_PENDING;
7349
+ _pendingRecords = _pendingRecords.slice(-MAX_PENDING);
7350
+ console.warn(`[store] Dropped ${dropped} oldest pending records (overflow)`);
7351
+ }
7352
+ if (_flushTimer === null) {
7353
+ _flushTimer = setInterval(() => {
7354
+ void flushBatch();
7355
+ }, _flushIntervalMs);
7356
+ if (_flushTimer && typeof _flushTimer === "object" && "unref" in _flushTimer) {
7357
+ _flushTimer.unref();
7358
+ }
7359
+ }
7360
+ if (_pendingRecords.length >= _batchSize) {
7361
+ await flushBatch();
7362
+ }
7363
+ }
7364
+ async function flushBatch() {
7365
+ if (_flushing || _pendingRecords.length === 0) return 0;
7366
+ _flushing = true;
7367
+ try {
7368
+ const batch = _pendingRecords.slice(0);
7369
+ const client = getClient();
7370
+ const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
7371
+ let baseVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
7372
+ for (const row of batch) {
7373
+ row.version = baseVersion++;
7374
+ }
7375
+ _nextVersion = baseVersion;
7376
+ const buildStmt = (row) => {
7377
+ const hasVector = row.vector !== null;
7378
+ const taskId = row.task_id ?? null;
7379
+ const importance = row.importance ?? 5;
7380
+ const status = row.status ?? "active";
7381
+ const confidence = row.confidence ?? 0.7;
7382
+ const lastAccessed = row.last_accessed ?? row.timestamp;
7383
+ const workspaceId = row.workspace_id ?? null;
7384
+ const documentId = row.document_id ?? null;
7385
+ const userId = row.user_id ?? null;
7386
+ const charOffset = row.char_offset ?? null;
7387
+ const pageNumber = row.page_number ?? null;
7388
+ const sourcePath = row.source_path ?? null;
7389
+ const sourceType = row.source_type ?? null;
7390
+ const tier = row.tier ?? 3;
7391
+ const supersedesId = row.supersedes_id ?? null;
7392
+ const draft = row.draft ? 1 : 0;
7393
+ const memoryType = row.memory_type ?? "raw";
7394
+ const trajectory = row.trajectory ?? null;
7395
+ const contentHash = row.content_hash ?? null;
7396
+ const intent = row.intent ?? null;
7397
+ const outcome = row.outcome ?? null;
7398
+ const domain = row.domain ?? null;
7399
+ const referencedEntities = row.referenced_entities ?? null;
7400
+ const retrievalCount = row.retrieval_count ?? 0;
7401
+ const chainPosition = row.chain_position ?? null;
7402
+ const reviewStatus = row.review_status ?? null;
7403
+ const contextWindowPct = row.context_window_pct ?? null;
7404
+ const filePaths = row.file_paths ?? null;
7405
+ const commitHash = row.commit_hash ?? null;
7406
+ const durationMs = row.duration_ms ?? null;
7407
+ const tokenCost = row.token_cost ?? null;
7408
+ const audience = row.audience ?? null;
7409
+ const languageType = row.language_type ?? null;
7410
+ const parentMemoryId = row.parent_memory_id ?? null;
7411
+ const cols = `id, agent_id, agent_role, session_id, timestamp,
7412
+ tool_name, project_name,
7413
+ has_error, raw_text, vector, version, task_id, importance, status,
7414
+ confidence, last_accessed,
7415
+ workspace_id, document_id, user_id, char_offset, page_number,
7416
+ source_path, source_type, tier, supersedes_id, draft, memory_type, trajectory, content_hash,
7417
+ intent, outcome, domain, referenced_entities, retrieval_count,
7418
+ chain_position, review_status, context_window_pct, file_paths, commit_hash,
7419
+ duration_ms, token_cost, audience, language_type, parent_memory_id`;
7420
+ const metaArgs = [
7421
+ intent,
7422
+ outcome,
7423
+ domain,
7424
+ referencedEntities,
7425
+ retrievalCount,
7426
+ chainPosition,
7427
+ reviewStatus,
7428
+ contextWindowPct,
7429
+ filePaths,
7430
+ commitHash,
7431
+ durationMs,
7432
+ tokenCost,
7433
+ audience,
7434
+ languageType,
7435
+ parentMemoryId
7436
+ ];
7437
+ const baseArgs = [
7438
+ row.id,
7439
+ row.agent_id,
7440
+ row.agent_role,
7441
+ row.session_id,
7442
+ row.timestamp,
7443
+ row.tool_name,
7444
+ row.project_name,
7445
+ row.has_error,
7446
+ row.raw_text
7447
+ ];
7448
+ const sharedArgs = [
7449
+ row.version,
7450
+ taskId,
7451
+ importance,
7452
+ status,
7453
+ confidence,
7454
+ lastAccessed,
7455
+ workspaceId,
7456
+ documentId,
7457
+ userId,
7458
+ charOffset,
7459
+ pageNumber,
7460
+ sourcePath,
7461
+ sourceType,
7462
+ tier,
7463
+ supersedesId,
7464
+ draft,
7465
+ memoryType,
7466
+ trajectory,
7467
+ contentHash
7468
+ ];
7469
+ return {
7470
+ sql: hasVector ? `INSERT OR IGNORE INTO memories (${cols})
7471
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories (${cols})
7472
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
7473
+ args: hasVector ? [...baseArgs, vectorToBlob(row.vector), ...sharedArgs, ...metaArgs] : [...baseArgs, ...sharedArgs, ...metaArgs]
7474
+ };
7475
+ };
7476
+ const globalClient = getClient();
7477
+ const globalStmts = batch.map(buildStmt);
7478
+ await globalClient.batch(globalStmts, "write");
7479
+ _pendingRecords.splice(0, batch.length);
7480
+ try {
7481
+ const { isShardingEnabled: isShardingEnabled2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
7482
+ if (isShardingEnabled2()) {
7483
+ const byProject = /* @__PURE__ */ new Map();
7484
+ for (const row of batch) {
7485
+ const proj = row.project_name || "unknown";
7486
+ if (!byProject.has(proj)) byProject.set(proj, []);
7487
+ byProject.get(proj).push(row);
7488
+ }
7489
+ for (const [project, rows] of byProject) {
7490
+ try {
7491
+ const shardClient = await getReadyShardClient2(project);
7492
+ const shardStmts = rows.map(buildStmt);
7493
+ await shardClient.batch(shardStmts, "write");
7494
+ } catch (err) {
7495
+ process.stderr.write(
7496
+ `[store] Shard write failed for ${project}: ${err instanceof Error ? err.message : String(err)}
7497
+ `
7498
+ );
7499
+ }
7500
+ }
7501
+ }
7502
+ } catch {
7503
+ }
7504
+ return batch.length;
7505
+ } finally {
7506
+ _flushing = false;
7507
+ }
7508
+ }
7509
+ function buildWikiScopeFilter(options, columnPrefix) {
7510
+ const args = [];
7511
+ let clause = "";
7512
+ if (options?.workspaceId !== void 0) {
7513
+ clause += ` AND ${columnPrefix}workspace_id = ?`;
7514
+ args.push(options.workspaceId);
7515
+ }
7516
+ if (options?.userId === void 0) {
7517
+ clause += ` AND ${columnPrefix}user_id IS NULL`;
7518
+ } else if (options.userId === null) {
7519
+ clause += ` AND ${columnPrefix}user_id IS NULL`;
7520
+ } else {
7521
+ clause += ` AND (${columnPrefix}user_id = ? OR ${columnPrefix}user_id IS NULL)`;
7522
+ args.push(options.userId);
7523
+ }
7524
+ return { clause, args };
7525
+ }
7526
+ function buildRawVisibilityFilter(options, columnPrefix) {
7527
+ if (options?.includeRaw === false) {
7528
+ return {
7529
+ clause: ` AND COALESCE(${columnPrefix}memory_type, 'raw') != 'raw'`,
7530
+ args: []
7531
+ };
7532
+ }
7533
+ return { clause: "", args: [] };
7534
+ }
7535
+ async function searchMemories(queryVector, agentId, options) {
7536
+ let client;
7537
+ try {
7538
+ const { isShardingEnabled: isShardingEnabled2, shardExists: shardExists2, getReadyShardClient: getReadyShardClient2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
7539
+ if (isShardingEnabled2() && options?.projectName && shardExists2(options.projectName)) {
7540
+ client = await getReadyShardClient2(options.projectName);
7541
+ } else {
7542
+ client = getClient();
7543
+ }
7544
+ } catch {
7545
+ client = getClient();
7546
+ }
7547
+ const limit = options?.limit ?? 10;
7548
+ const statusFilter = options?.includeArchived ? "" : `
7549
+ AND COALESCE(status, 'active') = 'active'`;
7550
+ const draftFilter = options?.includeDrafts ? "" : `
7551
+ AND (draft = 0 OR draft IS NULL)`;
7552
+ let sql = `SELECT id, agent_id, agent_role, session_id, timestamp,
7553
+ tool_name, project_name,
7554
+ has_error, raw_text, vector, importance, status,
7555
+ confidence, last_accessed,
7556
+ workspace_id, document_id, user_id,
7557
+ char_offset, page_number,
7558
+ source_path, source_type
7559
+ FROM memories
7560
+ WHERE agent_id = ?
7561
+ AND vector IS NOT NULL${statusFilter}${draftFilter}
7562
+ AND COALESCE(confidence, 0.7) >= 0.3`;
7563
+ const args = [agentId];
7564
+ const scope = buildWikiScopeFilter(options, "");
7565
+ sql += scope.clause;
7566
+ args.push(...scope.args);
7567
+ const rawVisibility = buildRawVisibilityFilter(options, "");
7568
+ sql += rawVisibility.clause;
7569
+ args.push(...rawVisibility.args);
7570
+ if (options?.projectName) {
7571
+ sql += ` AND project_name = ?`;
7572
+ args.push(options.projectName);
7573
+ }
7574
+ if (options?.toolName) {
7575
+ sql += ` AND tool_name = ?`;
7576
+ args.push(options.toolName);
7577
+ }
7578
+ if (options?.hasError !== void 0) {
7579
+ sql += ` AND has_error = ?`;
7580
+ args.push(options.hasError ? 1 : 0);
7581
+ }
7582
+ if (options?.since) {
7583
+ sql += ` AND timestamp >= ?`;
7584
+ args.push(options.since);
7585
+ }
7586
+ if (options?.memoryType) {
7587
+ sql += ` AND memory_type = ?`;
7588
+ args.push(options.memoryType);
7589
+ }
7590
+ sql += ` ORDER BY vector_distance_cos(vector, vector32(?))`;
7591
+ args.push(vectorToBlob(queryVector));
7592
+ sql += ` LIMIT ?`;
7593
+ args.push(limit);
7594
+ const result2 = await client.execute({ sql, args });
7595
+ return result2.rows.map((row) => ({
7596
+ id: row.id,
7597
+ agent_id: row.agent_id,
7598
+ agent_role: row.agent_role,
7599
+ session_id: row.session_id,
7600
+ timestamp: row.timestamp,
7601
+ tool_name: row.tool_name,
7602
+ project_name: row.project_name,
7603
+ has_error: row.has_error === 1,
7604
+ raw_text: row.raw_text,
7605
+ vector: row.vector == null ? [] : Array.isArray(row.vector) ? row.vector : Array.from(row.vector),
7606
+ importance: row.importance ?? 5,
7607
+ status: row.status ?? "active",
7608
+ confidence: row.confidence ?? 0.7,
7609
+ last_accessed: row.last_accessed ?? row.timestamp,
7610
+ workspace_id: row.workspace_id ?? null,
7611
+ document_id: row.document_id ?? null,
7612
+ user_id: row.user_id ?? null,
7613
+ char_offset: row.char_offset ?? null,
7614
+ page_number: row.page_number ?? null,
7615
+ source_path: row.source_path ?? null,
7616
+ source_type: row.source_type ?? null
7617
+ }));
7618
+ }
7619
+ async function attachDocumentMetadata(records) {
7620
+ const docIds = [
7621
+ ...new Set(
7622
+ records.map((r) => r.document_id).filter((id) => typeof id === "string" && id.length > 0)
7623
+ )
7624
+ ];
7625
+ if (docIds.length === 0) return records;
7626
+ try {
7627
+ const client = getClient();
7628
+ const placeholders = docIds.map(() => "?").join(",");
7629
+ const result2 = await client.execute({
7630
+ sql: `SELECT id, filename, mime, source_type, uploaded_at
7631
+ FROM documents
7632
+ WHERE id IN (${placeholders})`,
7633
+ args: docIds
7634
+ });
7635
+ const byId = /* @__PURE__ */ new Map();
7636
+ for (const row of result2.rows) {
7637
+ const id = row.id;
7638
+ byId.set(id, {
7639
+ document_id: id,
7640
+ filename: row.filename,
7641
+ mime: row.mime ?? null,
7642
+ source_type: row.source_type ?? null,
7643
+ uploaded_at: row.uploaded_at
7644
+ });
7645
+ }
7646
+ for (const record of records) {
7647
+ if (!record.document_id) continue;
7648
+ record.document_metadata = byId.get(record.document_id) ?? null;
7649
+ }
7650
+ } catch {
7651
+ }
7652
+ return records;
7653
+ }
7654
+ async function flushTier3(agentId, options) {
7655
+ const client = getClient();
7656
+ const maxAge = options?.maxAgeHours ?? 72;
7657
+ const cutoff = new Date(Date.now() - maxAge * 36e5).toISOString();
7658
+ if (options?.dryRun) {
7659
+ const result3 = await client.execute({
7660
+ sql: `SELECT COUNT(*) as cnt FROM memories
7661
+ WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
7662
+ args: [agentId, cutoff]
7663
+ });
7664
+ return { archived: Number(result3.rows[0]?.cnt ?? 0) };
7665
+ }
7666
+ const result2 = await client.execute({
7667
+ sql: `UPDATE memories SET status = 'archived'
7668
+ WHERE agent_id = ? AND tier = 3 AND status = 'active' AND timestamp < ?`,
7669
+ args: [agentId, cutoff]
7670
+ });
7671
+ return { archived: result2.rowsAffected };
7672
+ }
7673
+ async function disposeStore() {
7674
+ if (_flushTimer !== null) {
7675
+ clearInterval(_flushTimer);
7676
+ _flushTimer = null;
7677
+ }
7678
+ if (_pendingRecords.length > 0) {
7679
+ await flushBatch();
7680
+ }
7681
+ await disposeTurso();
7682
+ _pendingRecords = [];
7683
+ _nextVersion = 1;
7684
+ }
7685
+ function vectorToBlob(vector) {
7686
+ const f32 = vector instanceof Float32Array ? vector : new Float32Array(vector);
7687
+ return JSON.stringify(Array.from(f32));
7688
+ }
7689
+ async function updateMemoryStatus(id, status) {
7690
+ const client = getClient();
7691
+ await client.execute({
7692
+ sql: `UPDATE memories SET status = ? WHERE id = ?`,
7693
+ args: [status, id]
7694
+ });
7695
+ }
7696
+ function reserveVersions(count) {
7697
+ const reserved = [];
7698
+ for (let i = 0; i < count; i++) {
7699
+ reserved.push(_nextVersion++);
7700
+ }
7701
+ return reserved;
7702
+ }
7703
+ async function getMemoryCardinality(agentId) {
7704
+ try {
7705
+ const client = getClient();
7706
+ const result2 = await client.execute({
7707
+ sql: `SELECT COUNT(*) as cnt FROM memories WHERE agent_id = ? AND COALESCE(status, 'active') = 'active'`,
7708
+ args: [agentId]
7709
+ });
7710
+ return Number(result2.rows[0]?.cnt) || 0;
7711
+ } catch {
7712
+ return 0;
7713
+ }
7714
+ }
7715
+ var INIT_MAX_RETRIES, INIT_RETRY_DELAY_MS, _pendingRecords, _batchSize, _flushIntervalMs, _flushTimer, _flushing, _nextVersion;
7716
+ var init_store = __esm({
7717
+ "src/lib/store.ts"() {
7718
+ "use strict";
7719
+ init_memory();
7720
+ init_database();
7721
+ init_keychain();
7722
+ init_config();
7723
+ init_state_bus();
7724
+ INIT_MAX_RETRIES = 3;
7725
+ INIT_RETRY_DELAY_MS = 1e3;
7726
+ _pendingRecords = [];
7727
+ _batchSize = 20;
7728
+ _flushIntervalMs = 1e4;
7729
+ _flushTimer = null;
7730
+ _flushing = false;
7731
+ _nextVersion = 1;
7732
+ }
7733
+ });
7734
+
7735
+ // src/bin/exe-dispatch.ts
7736
+ init_tmux_routing();
7737
+ init_tasks_crud();
7738
+
7739
+ // src/bin/fast-db-init.ts
7740
+ async function fastDbInit() {
7741
+ const { isInitialized: isInitialized2, getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
7742
+ if (isInitialized2()) {
7743
+ return getClient2();
7744
+ }
7745
+ try {
7746
+ const { connectEmbedDaemon: connectEmbedDaemon2, sendDaemonRequest: sendDaemonRequest2, isClientConnected: isClientConnected2 } = await Promise.resolve().then(() => (init_exe_daemon_client(), exe_daemon_client_exports));
7747
+ const { deserializeResultSet: deserializeResultSet2 } = await Promise.resolve().then(() => (init_daemon_protocol(), daemon_protocol_exports));
7748
+ await connectEmbedDaemon2();
7749
+ if (isClientConnected2()) {
7750
+ const daemonClient = {
7751
+ async execute(stmt) {
7752
+ const sql = typeof stmt === "string" ? stmt : stmt.sql;
7753
+ const args = typeof stmt === "string" ? [] : Array.isArray(stmt.args) ? stmt.args : [];
7754
+ const resp = await sendDaemonRequest2({ type: "db-execute", sql, args });
7755
+ if (resp.error) throw new Error(String(resp.error));
7756
+ if (resp.db) return deserializeResultSet2(resp.db);
7757
+ throw new Error("Unexpected daemon response");
7758
+ },
7759
+ async batch(stmts, mode) {
7760
+ const statements = stmts.map((s) => {
7761
+ const sql = typeof s === "string" ? s : s.sql;
7762
+ const args = typeof s === "string" ? [] : Array.isArray(s.args) ? s.args : [];
7763
+ return { sql, args };
7764
+ });
7765
+ const resp = await sendDaemonRequest2({ type: "db-batch", statements, mode: mode ?? "deferred" });
7766
+ if (resp.error) throw new Error(String(resp.error));
7767
+ const batchResults = resp["db-batch"];
7768
+ if (batchResults) return batchResults.map(deserializeResultSet2);
7769
+ throw new Error("Unexpected daemon batch response");
7770
+ },
7771
+ async transaction(_mode) {
7772
+ throw new Error("Transactions not supported via daemon socket");
7773
+ },
7774
+ async executeMultiple(_sql) {
7775
+ throw new Error("executeMultiple not supported via daemon socket");
7776
+ },
7777
+ async migrate(_stmts) {
7778
+ throw new Error("migrate not supported via daemon socket");
7779
+ },
7780
+ sync() {
7781
+ return Promise.resolve(void 0);
7782
+ },
7783
+ close() {
7784
+ },
7785
+ get closed() {
7786
+ return false;
7787
+ },
7788
+ get protocol() {
7789
+ return "file";
7790
+ }
7791
+ };
7792
+ return daemonClient;
7793
+ }
7794
+ } catch {
7795
+ }
7796
+ const { initStore: initStore2 } = await Promise.resolve().then(() => (init_store(), store_exports));
7797
+ await initStore2({ lightweight: true });
7798
+ return getClient2();
7799
+ }
6204
7800
 
6205
7801
  // src/bin/exe-dispatch.ts
6206
7802
  init_employees();
@@ -6220,7 +7816,7 @@ if (!exeSession) {
6220
7816
  process.exit(1);
6221
7817
  }
6222
7818
  try {
6223
- await initStore();
7819
+ await fastDbInit();
6224
7820
  await createTaskCore({
6225
7821
  title: `Dispatch: ${employeeName} (CLI)`,
6226
7822
  assignedTo: employeeName,