@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
@@ -25,6 +25,15 @@ var __copyProps = (to, from, except, desc) => {
25
25
  };
26
26
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
27
 
28
+ // src/types/memory.ts
29
+ var EMBEDDING_DIM;
30
+ var init_memory = __esm({
31
+ "src/types/memory.ts"() {
32
+ "use strict";
33
+ EMBEDDING_DIM = 1024;
34
+ }
35
+ });
36
+
28
37
  // src/lib/db-retry.ts
29
38
  function isBusyError(err) {
30
39
  if (err instanceof Error) {
@@ -74,9 +83,9 @@ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
74
83
  var init_db_retry = __esm({
75
84
  "src/lib/db-retry.ts"() {
76
85
  "use strict";
77
- MAX_RETRIES = 3;
78
- BASE_DELAY_MS = 200;
79
- MAX_JITTER_MS = 300;
86
+ MAX_RETRIES = 5;
87
+ BASE_DELAY_MS = 250;
88
+ MAX_JITTER_MS = 400;
80
89
  }
81
90
  });
82
91
 
@@ -119,6 +128,21 @@ var init_secure_files = __esm({
119
128
  });
120
129
 
121
130
  // src/lib/config.ts
131
+ var config_exports = {};
132
+ __export(config_exports, {
133
+ CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
134
+ CONFIG_PATH: () => CONFIG_PATH,
135
+ CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
136
+ DB_PATH: () => DB_PATH,
137
+ EXE_AI_DIR: () => EXE_AI_DIR,
138
+ LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
139
+ MODELS_DIR: () => MODELS_DIR,
140
+ loadConfig: () => loadConfig,
141
+ loadConfigFrom: () => loadConfigFrom,
142
+ loadConfigSync: () => loadConfigSync,
143
+ migrateConfig: () => migrateConfig,
144
+ saveConfig: () => saveConfig
145
+ });
122
146
  import { readFile, writeFile } from "fs/promises";
123
147
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
124
148
  import path from "path";
@@ -222,6 +246,46 @@ async function loadConfig() {
222
246
  return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
223
247
  }
224
248
  }
249
+ function loadConfigSync() {
250
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
251
+ const configPath = path.join(dir, "config.json");
252
+ if (!existsSync2(configPath)) {
253
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
254
+ }
255
+ try {
256
+ const raw = readFileSync(configPath, "utf-8");
257
+ let parsed = JSON.parse(raw);
258
+ parsed = migrateLegacyConfig(parsed);
259
+ const { config: migratedCfg } = migrateConfig(parsed);
260
+ normalizeScalingRoadmap(migratedCfg);
261
+ normalizeSessionLifecycle(migratedCfg);
262
+ normalizeAutoUpdate(migratedCfg);
263
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db"), ...migratedCfg };
264
+ } catch {
265
+ return { ...DEFAULT_CONFIG, dbPath: path.join(dir, "memories.db") };
266
+ }
267
+ }
268
+ async function saveConfig(config) {
269
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
270
+ await ensurePrivateDir(dir);
271
+ const configPath = path.join(dir, "config.json");
272
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
273
+ await enforcePrivateFile(configPath);
274
+ }
275
+ async function loadConfigFrom(configPath) {
276
+ const raw = await readFile(configPath, "utf-8");
277
+ try {
278
+ let parsed = JSON.parse(raw);
279
+ parsed = migrateLegacyConfig(parsed);
280
+ const { config: migratedCfg } = migrateConfig(parsed);
281
+ normalizeScalingRoadmap(migratedCfg);
282
+ normalizeSessionLifecycle(migratedCfg);
283
+ normalizeAutoUpdate(migratedCfg);
284
+ return { ...DEFAULT_CONFIG, ...migratedCfg };
285
+ } catch {
286
+ return { ...DEFAULT_CONFIG };
287
+ }
288
+ }
225
289
  var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
226
290
  var init_config = __esm({
227
291
  "src/lib/config.ts"() {
@@ -402,7 +466,7 @@ var init_agent_config = __esm({
402
466
  init_secure_files();
403
467
  AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
404
468
  KNOWN_RUNTIMES = {
405
- claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
469
+ claude: ["claude-opus-4.6", "claude-opus-4", "claude-sonnet-4.6", "claude-sonnet-4", "claude-haiku-4.5"],
406
470
  codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
407
471
  opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
408
472
  };
@@ -412,7 +476,7 @@ var init_agent_config = __esm({
412
476
  opencode: "OpenCode (open source)"
413
477
  };
414
478
  DEFAULT_MODELS = {
415
- claude: "claude-opus-4",
479
+ claude: "claude-opus-4.6",
416
480
  codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
417
481
  opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
418
482
  };
@@ -1259,7 +1323,687 @@ var init_database_adapter = __esm({
1259
1323
  }
1260
1324
  });
1261
1325
 
1326
+ // src/lib/daemon-auth.ts
1327
+ import crypto from "crypto";
1328
+ import path5 from "path";
1329
+ import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1330
+ function normalizeToken(token) {
1331
+ if (!token) return null;
1332
+ const trimmed = token.trim();
1333
+ return trimmed.length > 0 ? trimmed : null;
1334
+ }
1335
+ function readDaemonToken() {
1336
+ try {
1337
+ if (!existsSync5(DAEMON_TOKEN_PATH)) return null;
1338
+ return normalizeToken(readFileSync4(DAEMON_TOKEN_PATH, "utf8"));
1339
+ } catch {
1340
+ return null;
1341
+ }
1342
+ }
1343
+ function ensureDaemonToken(seed) {
1344
+ const existing = readDaemonToken();
1345
+ if (existing) return existing;
1346
+ const token = normalizeToken(seed) ?? crypto.randomBytes(32).toString("hex");
1347
+ ensurePrivateDirSync(EXE_AI_DIR);
1348
+ writeFileSync3(DAEMON_TOKEN_PATH, `${token}
1349
+ `, "utf8");
1350
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
1351
+ return token;
1352
+ }
1353
+ var DAEMON_TOKEN_PATH;
1354
+ var init_daemon_auth = __esm({
1355
+ "src/lib/daemon-auth.ts"() {
1356
+ "use strict";
1357
+ init_config();
1358
+ init_secure_files();
1359
+ DAEMON_TOKEN_PATH = path5.join(EXE_AI_DIR, "exed.token");
1360
+ }
1361
+ });
1362
+
1363
+ // src/lib/exe-daemon-client.ts
1364
+ import net from "net";
1365
+ import os4 from "os";
1366
+ import { spawn } from "child_process";
1367
+ import { randomUUID } from "crypto";
1368
+ import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
1369
+ import path6 from "path";
1370
+ import { fileURLToPath } from "url";
1371
+ function handleData(chunk) {
1372
+ _buffer += chunk.toString();
1373
+ if (_buffer.length > MAX_BUFFER) {
1374
+ _buffer = "";
1375
+ return;
1376
+ }
1377
+ let newlineIdx;
1378
+ while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1379
+ const line = _buffer.slice(0, newlineIdx).trim();
1380
+ _buffer = _buffer.slice(newlineIdx + 1);
1381
+ if (!line) continue;
1382
+ try {
1383
+ const response = JSON.parse(line);
1384
+ const id = response.id;
1385
+ if (!id) continue;
1386
+ const entry = _pending.get(id);
1387
+ if (entry) {
1388
+ clearTimeout(entry.timer);
1389
+ _pending.delete(id);
1390
+ entry.resolve(response);
1391
+ }
1392
+ } catch {
1393
+ }
1394
+ }
1395
+ }
1396
+ function cleanupStaleFiles() {
1397
+ if (existsSync6(PID_PATH)) {
1398
+ try {
1399
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1400
+ if (pid > 0) {
1401
+ try {
1402
+ process.kill(pid, 0);
1403
+ return;
1404
+ } catch {
1405
+ }
1406
+ }
1407
+ } catch {
1408
+ }
1409
+ try {
1410
+ unlinkSync2(PID_PATH);
1411
+ } catch {
1412
+ }
1413
+ try {
1414
+ unlinkSync2(SOCKET_PATH);
1415
+ } catch {
1416
+ }
1417
+ }
1418
+ }
1419
+ function findPackageRoot() {
1420
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
1421
+ const { root } = path6.parse(dir);
1422
+ while (dir !== root) {
1423
+ if (existsSync6(path6.join(dir, "package.json"))) return dir;
1424
+ dir = path6.dirname(dir);
1425
+ }
1426
+ return null;
1427
+ }
1428
+ function getAvailableMemoryGB() {
1429
+ if (process.platform === "darwin") {
1430
+ try {
1431
+ const { execSync: execSync7 } = __require("child_process");
1432
+ const vmstat = execSync7("vm_stat", { encoding: "utf8" });
1433
+ const pageSize = 16384;
1434
+ const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
1435
+ const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
1436
+ const free = vmstat.match(/Pages free:\s+(\d+)/);
1437
+ const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
1438
+ const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
1439
+ const freePages = free ? parseInt(free[1], 10) : 0;
1440
+ const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
1441
+ const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
1442
+ return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
1443
+ } catch {
1444
+ return os4.freemem() / (1024 * 1024 * 1024);
1445
+ }
1446
+ }
1447
+ return os4.freemem() / (1024 * 1024 * 1024);
1448
+ }
1449
+ function spawnDaemon() {
1450
+ const freeGB = getAvailableMemoryGB();
1451
+ const totalGB = os4.totalmem() / (1024 * 1024 * 1024);
1452
+ if (totalGB <= 8) {
1453
+ process.stderr.write(
1454
+ `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
1455
+ `
1456
+ );
1457
+ return;
1458
+ }
1459
+ if (totalGB <= 16 && freeGB < 2) {
1460
+ process.stderr.write(
1461
+ `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
1462
+ `
1463
+ );
1464
+ return;
1465
+ }
1466
+ const pkgRoot = findPackageRoot();
1467
+ if (!pkgRoot) {
1468
+ process.stderr.write("[exed-client] WARN: cannot find package root\n");
1469
+ return;
1470
+ }
1471
+ const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1472
+ if (!existsSync6(daemonPath)) {
1473
+ process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1474
+ `);
1475
+ return;
1476
+ }
1477
+ const resolvedPath = daemonPath;
1478
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
1479
+ process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1480
+ `);
1481
+ const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
1482
+ let stderrFd = "ignore";
1483
+ try {
1484
+ stderrFd = openSync(logPath, "a");
1485
+ } catch {
1486
+ }
1487
+ const heapCapMB = totalGB <= 8 ? 256 : 512;
1488
+ const nodeArgs = [`--max-old-space-size=${heapCapMB}`, resolvedPath];
1489
+ const child = spawn(process.execPath, nodeArgs, {
1490
+ detached: true,
1491
+ stdio: ["ignore", "ignore", stderrFd],
1492
+ env: {
1493
+ ...process.env,
1494
+ TMUX: void 0,
1495
+ // Daemon is global — must not inherit session scope
1496
+ TMUX_PANE: void 0,
1497
+ // Prevents resolveExeSession() from scoping to one session
1498
+ EXE_DAEMON_SOCK: SOCKET_PATH,
1499
+ EXE_DAEMON_PID: PID_PATH,
1500
+ [DAEMON_TOKEN_ENV]: daemonToken
1501
+ }
1502
+ });
1503
+ child.unref();
1504
+ if (typeof stderrFd === "number") {
1505
+ try {
1506
+ closeSync(stderrFd);
1507
+ } catch {
1508
+ }
1509
+ }
1510
+ }
1511
+ function acquireSpawnLock() {
1512
+ try {
1513
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
1514
+ closeSync(fd);
1515
+ return true;
1516
+ } catch {
1517
+ try {
1518
+ const stat = statSync(SPAWN_LOCK_PATH);
1519
+ if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
1520
+ try {
1521
+ unlinkSync2(SPAWN_LOCK_PATH);
1522
+ } catch {
1523
+ }
1524
+ try {
1525
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
1526
+ closeSync(fd);
1527
+ return true;
1528
+ } catch {
1529
+ }
1530
+ }
1531
+ } catch {
1532
+ }
1533
+ return false;
1534
+ }
1535
+ }
1536
+ function releaseSpawnLock() {
1537
+ try {
1538
+ unlinkSync2(SPAWN_LOCK_PATH);
1539
+ } catch {
1540
+ }
1541
+ }
1542
+ function connectToSocket() {
1543
+ return new Promise((resolve) => {
1544
+ if (_socket && _connected) {
1545
+ resolve(true);
1546
+ return;
1547
+ }
1548
+ const socket = net.createConnection({ path: SOCKET_PATH });
1549
+ const connectTimeout = setTimeout(() => {
1550
+ socket.destroy();
1551
+ resolve(false);
1552
+ }, 2e3);
1553
+ socket.on("connect", () => {
1554
+ clearTimeout(connectTimeout);
1555
+ _socket = socket;
1556
+ _connected = true;
1557
+ _buffer = "";
1558
+ socket.on("data", handleData);
1559
+ socket.on("close", () => {
1560
+ _connected = false;
1561
+ _socket = null;
1562
+ for (const [id, entry] of _pending) {
1563
+ clearTimeout(entry.timer);
1564
+ _pending.delete(id);
1565
+ entry.resolve({ error: "Connection closed" });
1566
+ }
1567
+ });
1568
+ socket.on("error", () => {
1569
+ _connected = false;
1570
+ _socket = null;
1571
+ });
1572
+ resolve(true);
1573
+ });
1574
+ socket.on("error", () => {
1575
+ clearTimeout(connectTimeout);
1576
+ resolve(false);
1577
+ });
1578
+ });
1579
+ }
1580
+ async function connectEmbedDaemon() {
1581
+ if (_socket && _connected) return true;
1582
+ if (await connectToSocket()) return true;
1583
+ if (acquireSpawnLock()) {
1584
+ try {
1585
+ cleanupStaleFiles();
1586
+ spawnDaemon();
1587
+ } finally {
1588
+ releaseSpawnLock();
1589
+ }
1590
+ }
1591
+ const start = Date.now();
1592
+ let delay2 = 100;
1593
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1594
+ await new Promise((r) => setTimeout(r, delay2));
1595
+ if (await connectToSocket()) return true;
1596
+ delay2 = Math.min(delay2 * 2, 3e3);
1597
+ }
1598
+ return false;
1599
+ }
1600
+ function sendRequest(texts, priority) {
1601
+ return sendDaemonRequest({ texts, priority });
1602
+ }
1603
+ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
1604
+ return new Promise((resolve) => {
1605
+ if (!_socket || !_connected) {
1606
+ resolve({ error: "Not connected" });
1607
+ return;
1608
+ }
1609
+ const id = randomUUID();
1610
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
1611
+ const timer = setTimeout(() => {
1612
+ _pending.delete(id);
1613
+ resolve({ error: "Request timeout" });
1614
+ }, timeoutMs);
1615
+ _pending.set(id, { resolve, timer });
1616
+ try {
1617
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
1618
+ } catch {
1619
+ clearTimeout(timer);
1620
+ _pending.delete(id);
1621
+ resolve({ error: "Write failed" });
1622
+ }
1623
+ });
1624
+ }
1625
+ async function pingDaemon() {
1626
+ if (!_socket || !_connected) return null;
1627
+ const response = await sendDaemonRequest({ type: "health" }, 5e3);
1628
+ if (response.health) {
1629
+ return response.health;
1630
+ }
1631
+ return null;
1632
+ }
1633
+ function killAndRespawnDaemon() {
1634
+ if (!acquireSpawnLock()) {
1635
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
1636
+ if (_socket) {
1637
+ _socket.destroy();
1638
+ _socket = null;
1639
+ }
1640
+ _connected = false;
1641
+ _buffer = "";
1642
+ return;
1643
+ }
1644
+ try {
1645
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
1646
+ if (existsSync6(PID_PATH)) {
1647
+ try {
1648
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1649
+ if (pid > 0) {
1650
+ try {
1651
+ process.kill(pid, "SIGKILL");
1652
+ } catch {
1653
+ }
1654
+ }
1655
+ } catch {
1656
+ }
1657
+ }
1658
+ if (_socket) {
1659
+ _socket.destroy();
1660
+ _socket = null;
1661
+ }
1662
+ _connected = false;
1663
+ _buffer = "";
1664
+ try {
1665
+ unlinkSync2(PID_PATH);
1666
+ } catch {
1667
+ }
1668
+ try {
1669
+ unlinkSync2(SOCKET_PATH);
1670
+ } catch {
1671
+ }
1672
+ spawnDaemon();
1673
+ } finally {
1674
+ releaseSpawnLock();
1675
+ }
1676
+ }
1677
+ function isDaemonTooYoung() {
1678
+ try {
1679
+ const stat = statSync(PID_PATH);
1680
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
1681
+ } catch {
1682
+ return false;
1683
+ }
1684
+ }
1685
+ async function retryThenRestart(doRequest, label) {
1686
+ const result = await doRequest();
1687
+ if (!result.error) {
1688
+ _consecutiveFailures = 0;
1689
+ return result;
1690
+ }
1691
+ _consecutiveFailures++;
1692
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
1693
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
1694
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
1695
+ `);
1696
+ await new Promise((r) => setTimeout(r, delayMs));
1697
+ if (!_connected) {
1698
+ if (!await connectToSocket()) continue;
1699
+ }
1700
+ const retry = await doRequest();
1701
+ if (!retry.error) {
1702
+ _consecutiveFailures = 0;
1703
+ return retry;
1704
+ }
1705
+ _consecutiveFailures++;
1706
+ }
1707
+ if (isDaemonTooYoung()) {
1708
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
1709
+ `);
1710
+ return { error: result.error };
1711
+ }
1712
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
1713
+ `);
1714
+ killAndRespawnDaemon();
1715
+ const start = Date.now();
1716
+ let delay2 = 200;
1717
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1718
+ await new Promise((r) => setTimeout(r, delay2));
1719
+ if (await connectToSocket()) break;
1720
+ delay2 = Math.min(delay2 * 2, 3e3);
1721
+ }
1722
+ if (!_connected) return { error: "Daemon restart failed" };
1723
+ const final = await doRequest();
1724
+ if (!final.error) _consecutiveFailures = 0;
1725
+ return final;
1726
+ }
1727
+ async function embedViaClient(text, priority = "high") {
1728
+ if (!_connected && !await connectEmbedDaemon()) return null;
1729
+ _requestCount++;
1730
+ if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
1731
+ const health = await pingDaemon();
1732
+ if (!health && !isDaemonTooYoung()) {
1733
+ process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
1734
+ `);
1735
+ killAndRespawnDaemon();
1736
+ const start = Date.now();
1737
+ let d = 200;
1738
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
1739
+ await new Promise((r) => setTimeout(r, d));
1740
+ if (await connectToSocket()) break;
1741
+ d = Math.min(d * 2, 3e3);
1742
+ }
1743
+ if (!_connected) return null;
1744
+ }
1745
+ }
1746
+ const result = await retryThenRestart(
1747
+ () => sendRequest([text], priority),
1748
+ "Embed"
1749
+ );
1750
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
1751
+ }
1752
+ function disconnectClient() {
1753
+ if (_socket) {
1754
+ _socket.destroy();
1755
+ _socket = null;
1756
+ }
1757
+ _connected = false;
1758
+ _buffer = "";
1759
+ for (const [id, entry] of _pending) {
1760
+ clearTimeout(entry.timer);
1761
+ _pending.delete(id);
1762
+ entry.resolve({ error: "Client disconnected" });
1763
+ }
1764
+ }
1765
+ function isClientConnected() {
1766
+ return _connected;
1767
+ }
1768
+ 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;
1769
+ var init_exe_daemon_client = __esm({
1770
+ "src/lib/exe-daemon-client.ts"() {
1771
+ "use strict";
1772
+ init_config();
1773
+ init_daemon_auth();
1774
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
1775
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
1776
+ SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
1777
+ SPAWN_LOCK_STALE_MS = 3e4;
1778
+ CONNECT_TIMEOUT_MS = 15e3;
1779
+ REQUEST_TIMEOUT_MS = 3e4;
1780
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
1781
+ _socket = null;
1782
+ _connected = false;
1783
+ _buffer = "";
1784
+ _requestCount = 0;
1785
+ _consecutiveFailures = 0;
1786
+ HEALTH_CHECK_INTERVAL = 100;
1787
+ MAX_RETRIES_BEFORE_RESTART = 3;
1788
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
1789
+ MIN_DAEMON_AGE_MS = 3e4;
1790
+ _pending = /* @__PURE__ */ new Map();
1791
+ MAX_BUFFER = 1e7;
1792
+ }
1793
+ });
1794
+
1795
+ // src/lib/daemon-protocol.ts
1796
+ function serializeValue(v) {
1797
+ if (v === null || v === void 0) return null;
1798
+ if (typeof v === "bigint") return Number(v);
1799
+ if (typeof v === "boolean") return v ? 1 : 0;
1800
+ if (v instanceof Uint8Array) {
1801
+ return { __blob: Buffer.from(v).toString("base64") };
1802
+ }
1803
+ if (ArrayBuffer.isView(v)) {
1804
+ return { __blob: Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString("base64") };
1805
+ }
1806
+ if (v instanceof ArrayBuffer) {
1807
+ return { __blob: Buffer.from(v).toString("base64") };
1808
+ }
1809
+ if (typeof v === "string" || typeof v === "number") return v;
1810
+ return String(v);
1811
+ }
1812
+ function deserializeValue(v) {
1813
+ if (v === null) return null;
1814
+ if (typeof v === "object" && v !== null && "__blob" in v) {
1815
+ const buf = Buffer.from(v.__blob, "base64");
1816
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
1817
+ }
1818
+ return v;
1819
+ }
1820
+ function deserializeResultSet(srs) {
1821
+ const rows = srs.rows.map((obj) => {
1822
+ const values = srs.columns.map(
1823
+ (col) => deserializeValue(obj[col] ?? null)
1824
+ );
1825
+ const row = values;
1826
+ for (let i = 0; i < srs.columns.length; i++) {
1827
+ const col = srs.columns[i];
1828
+ if (col !== void 0) {
1829
+ row[col] = values[i] ?? null;
1830
+ }
1831
+ }
1832
+ Object.defineProperty(row, "length", {
1833
+ value: values.length,
1834
+ enumerable: false
1835
+ });
1836
+ return row;
1837
+ });
1838
+ return {
1839
+ columns: srs.columns,
1840
+ columnTypes: srs.columnTypes ?? [],
1841
+ rows,
1842
+ rowsAffected: srs.rowsAffected,
1843
+ lastInsertRowid: srs.lastInsertRowid != null ? BigInt(srs.lastInsertRowid) : void 0,
1844
+ toJSON: () => ({
1845
+ columns: srs.columns,
1846
+ columnTypes: srs.columnTypes ?? [],
1847
+ rows: srs.rows,
1848
+ rowsAffected: srs.rowsAffected,
1849
+ lastInsertRowid: srs.lastInsertRowid
1850
+ })
1851
+ };
1852
+ }
1853
+ var init_daemon_protocol = __esm({
1854
+ "src/lib/daemon-protocol.ts"() {
1855
+ "use strict";
1856
+ }
1857
+ });
1858
+
1859
+ // src/lib/db-daemon-client.ts
1860
+ var db_daemon_client_exports = {};
1861
+ __export(db_daemon_client_exports, {
1862
+ createDaemonDbClient: () => createDaemonDbClient,
1863
+ initDaemonDbClient: () => initDaemonDbClient
1864
+ });
1865
+ function normalizeStatement2(stmt) {
1866
+ if (typeof stmt === "string") {
1867
+ return { sql: stmt, args: [] };
1868
+ }
1869
+ const sql = stmt.sql;
1870
+ let args = [];
1871
+ if (Array.isArray(stmt.args)) {
1872
+ args = stmt.args.map((v) => serializeValue(v));
1873
+ } else if (stmt.args && typeof stmt.args === "object") {
1874
+ const named = {};
1875
+ for (const [key, val] of Object.entries(stmt.args)) {
1876
+ named[key] = serializeValue(val);
1877
+ }
1878
+ return { sql, args: named };
1879
+ }
1880
+ return { sql, args };
1881
+ }
1882
+ function createDaemonDbClient(fallbackClient) {
1883
+ let _useDaemon = false;
1884
+ const client = {
1885
+ async execute(stmt) {
1886
+ if (!_useDaemon || !isClientConnected()) {
1887
+ return fallbackClient.execute(stmt);
1888
+ }
1889
+ const { sql, args } = normalizeStatement2(stmt);
1890
+ const response = await sendDaemonRequest({
1891
+ type: "db-execute",
1892
+ sql,
1893
+ args
1894
+ });
1895
+ if (response.error) {
1896
+ const errMsg = String(response.error);
1897
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
1898
+ process.stderr.write(`[db-daemon] Transport error (${errMsg}), falling back to direct
1899
+ `);
1900
+ return fallbackClient.execute(stmt);
1901
+ }
1902
+ throw new Error(errMsg);
1903
+ }
1904
+ if (response.db) {
1905
+ return deserializeResultSet(response.db);
1906
+ }
1907
+ process.stderr.write("[db-daemon] Unexpected response shape, falling back to direct\n");
1908
+ return fallbackClient.execute(stmt);
1909
+ },
1910
+ async batch(stmts, mode) {
1911
+ if (!_useDaemon || !isClientConnected()) {
1912
+ return fallbackClient.batch(stmts, mode);
1913
+ }
1914
+ const statements = stmts.map(normalizeStatement2);
1915
+ const response = await sendDaemonRequest({
1916
+ type: "db-batch",
1917
+ statements,
1918
+ mode: mode ?? "deferred"
1919
+ });
1920
+ if (response.error) {
1921
+ const errMsg = String(response.error);
1922
+ if (errMsg === "Not connected" || errMsg === "Request timeout" || errMsg === "Write failed" || errMsg === "DB not initialized") {
1923
+ process.stderr.write(`[db-daemon] Batch transport error (${errMsg}), falling back to direct
1924
+ `);
1925
+ return fallbackClient.batch(stmts, mode);
1926
+ }
1927
+ throw new Error(errMsg);
1928
+ }
1929
+ const batchResults = response["db-batch"];
1930
+ if (batchResults) {
1931
+ return batchResults.map(deserializeResultSet);
1932
+ }
1933
+ process.stderr.write("[db-daemon] Unexpected batch response shape, falling back to direct\n");
1934
+ return fallbackClient.batch(stmts, mode);
1935
+ },
1936
+ // Transaction support — delegate to fallback (transactions need direct connection)
1937
+ async transaction(mode) {
1938
+ return fallbackClient.transaction(mode);
1939
+ },
1940
+ // executeMultiple — delegate to fallback (used only for schema migrations)
1941
+ async executeMultiple(sql) {
1942
+ return fallbackClient.executeMultiple(sql);
1943
+ },
1944
+ // migrate — delegate to fallback
1945
+ async migrate(stmts) {
1946
+ return fallbackClient.migrate(stmts);
1947
+ },
1948
+ // Sync mode — delegate to fallback
1949
+ sync() {
1950
+ return fallbackClient.sync();
1951
+ },
1952
+ close() {
1953
+ _useDaemon = false;
1954
+ },
1955
+ get closed() {
1956
+ return fallbackClient.closed;
1957
+ },
1958
+ get protocol() {
1959
+ return fallbackClient.protocol;
1960
+ }
1961
+ };
1962
+ return {
1963
+ ...client,
1964
+ /** Enable daemon routing (call after confirming daemon is connected) */
1965
+ _enableDaemon() {
1966
+ _useDaemon = true;
1967
+ },
1968
+ /** Check if daemon routing is active */
1969
+ _isDaemonActive() {
1970
+ return _useDaemon && isClientConnected();
1971
+ }
1972
+ };
1973
+ }
1974
+ async function initDaemonDbClient(fallbackClient) {
1975
+ if (process.env.EXE_IS_DAEMON === "1") return null;
1976
+ const connected = await connectEmbedDaemon();
1977
+ if (!connected) {
1978
+ process.stderr.write("[db-daemon] Daemon unavailable \u2014 using direct SQLite\n");
1979
+ return null;
1980
+ }
1981
+ const client = createDaemonDbClient(fallbackClient);
1982
+ client._enableDaemon();
1983
+ process.stderr.write("[db-daemon] DB routing through daemon (single-writer)\n");
1984
+ return client;
1985
+ }
1986
+ var init_db_daemon_client = __esm({
1987
+ "src/lib/db-daemon-client.ts"() {
1988
+ "use strict";
1989
+ init_exe_daemon_client();
1990
+ init_daemon_protocol();
1991
+ }
1992
+ });
1993
+
1262
1994
  // src/lib/database.ts
1995
+ var database_exports = {};
1996
+ __export(database_exports, {
1997
+ disposeDatabase: () => disposeDatabase,
1998
+ disposeTurso: () => disposeTurso,
1999
+ ensureSchema: () => ensureSchema,
2000
+ getClient: () => getClient,
2001
+ getRawClient: () => getRawClient,
2002
+ initDaemonClient: () => initDaemonClient,
2003
+ initDatabase: () => initDatabase,
2004
+ initTurso: () => initTurso,
2005
+ isInitialized: () => isInitialized
2006
+ });
1263
2007
  import { createClient } from "@libsql/client";
1264
2008
  async function initDatabase(config) {
1265
2009
  if (_walCheckpointTimer) {
@@ -1298,15 +2042,18 @@ async function initDatabase(config) {
1298
2042
  });
1299
2043
  }, 3e4);
1300
2044
  _walCheckpointTimer.unref();
1301
- if (process.env.DATABASE_URL) {
2045
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
1302
2046
  _adapterClient = await createPrismaDbAdapter(_resilientClient);
1303
2047
  }
1304
2048
  }
2049
+ function isInitialized() {
2050
+ return _adapterClient !== null || _client !== null;
2051
+ }
1305
2052
  function getClient() {
1306
2053
  if (!_adapterClient) {
1307
2054
  throw new Error("Database client not initialized. Call initDatabase() first.");
1308
2055
  }
1309
- if (process.env.DATABASE_URL) {
2056
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
1310
2057
  return _adapterClient;
1311
2058
  }
1312
2059
  if (process.env.EXE_IS_DAEMON === "1") {
@@ -1317,6 +2064,22 @@ function getClient() {
1317
2064
  }
1318
2065
  return _resilientClient;
1319
2066
  }
2067
+ async function initDaemonClient() {
2068
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") return;
2069
+ if (process.env.EXE_IS_DAEMON === "1") return;
2070
+ if (process.env.VITEST) return;
2071
+ if (!_resilientClient) return;
2072
+ if (_daemonClient) return;
2073
+ try {
2074
+ const { initDaemonDbClient: initDaemonDbClient2 } = await Promise.resolve().then(() => (init_db_daemon_client(), db_daemon_client_exports));
2075
+ _daemonClient = await initDaemonDbClient2(_resilientClient);
2076
+ } catch (err) {
2077
+ process.stderr.write(
2078
+ `[database] Daemon client init failed (non-fatal): ${err instanceof Error ? err.message : String(err)}
2079
+ `
2080
+ );
2081
+ }
2082
+ }
1320
2083
  function getRawClient() {
1321
2084
  if (!_client) {
1322
2085
  throw new Error("Database client not initialized. Call initDatabase() first.");
@@ -1455,6 +2218,13 @@ async function ensureSchema() {
1455
2218
  });
1456
2219
  } catch {
1457
2220
  }
2221
+ try {
2222
+ await client.execute({
2223
+ sql: `ALTER TABLE behaviors ADD COLUMN vector F32_BLOB(${EMBEDDING_DIM})`,
2224
+ args: []
2225
+ });
2226
+ } catch {
2227
+ }
1458
2228
  try {
1459
2229
  await client.execute({
1460
2230
  sql: `ALTER TABLE tasks ADD COLUMN blocked_by TEXT`,
@@ -2299,19 +3069,40 @@ async function ensureSchema() {
2299
3069
  } catch {
2300
3070
  }
2301
3071
  }
2302
- var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso;
3072
+ async function disposeDatabase() {
3073
+ if (_walCheckpointTimer) {
3074
+ clearInterval(_walCheckpointTimer);
3075
+ _walCheckpointTimer = null;
3076
+ }
3077
+ if (_daemonClient) {
3078
+ _daemonClient.close();
3079
+ _daemonClient = null;
3080
+ }
3081
+ if (_adapterClient && _adapterClient !== _resilientClient) {
3082
+ _adapterClient.close();
3083
+ }
3084
+ _adapterClient = null;
3085
+ if (_client) {
3086
+ _client.close();
3087
+ _client = null;
3088
+ _resilientClient = null;
3089
+ }
3090
+ }
3091
+ var _client, _resilientClient, _walCheckpointTimer, _daemonClient, _adapterClient, initTurso, disposeTurso;
2303
3092
  var init_database = __esm({
2304
3093
  "src/lib/database.ts"() {
2305
3094
  "use strict";
2306
3095
  init_db_retry();
2307
3096
  init_employees();
2308
3097
  init_database_adapter();
3098
+ init_memory();
2309
3099
  _client = null;
2310
3100
  _resilientClient = null;
2311
3101
  _walCheckpointTimer = null;
2312
3102
  _daemonClient = null;
2313
3103
  _adapterClient = null;
2314
3104
  initTurso = initDatabase;
3105
+ disposeTurso = disposeDatabase;
2315
3106
  }
2316
3107
  });
2317
3108
 
@@ -2384,12 +3175,12 @@ __export(shard_manager_exports, {
2384
3175
  listShards: () => listShards,
2385
3176
  shardExists: () => shardExists
2386
3177
  });
2387
- import path6 from "path";
2388
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync } from "fs";
3178
+ import path8 from "path";
3179
+ import { existsSync as existsSync8, mkdirSync as mkdirSync2, readdirSync } from "fs";
2389
3180
  import { createClient as createClient2 } from "@libsql/client";
2390
3181
  function initShardManager(encryptionKey) {
2391
3182
  _encryptionKey = encryptionKey;
2392
- if (!existsSync6(SHARDS_DIR)) {
3183
+ if (!existsSync8(SHARDS_DIR)) {
2393
3184
  mkdirSync2(SHARDS_DIR, { recursive: true });
2394
3185
  }
2395
3186
  _shardingEnabled = true;
@@ -2419,7 +3210,7 @@ function getShardClient(projectName) {
2419
3210
  while (_shards.size >= MAX_OPEN_SHARDS) {
2420
3211
  evictLRU();
2421
3212
  }
2422
- const dbPath = path6.join(SHARDS_DIR, `${safeName}.db`);
3213
+ const dbPath = path8.join(SHARDS_DIR, `${safeName}.db`);
2423
3214
  const client = createClient2({
2424
3215
  url: `file:${dbPath}`,
2425
3216
  encryptionKey: _encryptionKey
@@ -2430,10 +3221,10 @@ function getShardClient(projectName) {
2430
3221
  }
2431
3222
  function shardExists(projectName) {
2432
3223
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
2433
- return existsSync6(path6.join(SHARDS_DIR, `${safeName}.db`));
3224
+ return existsSync8(path8.join(SHARDS_DIR, `${safeName}.db`));
2434
3225
  }
2435
3226
  function listShards() {
2436
- if (!existsSync6(SHARDS_DIR)) return [];
3227
+ if (!existsSync8(SHARDS_DIR)) return [];
2437
3228
  return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
2438
3229
  }
2439
3230
  async function ensureShardSchema(client) {
@@ -2680,7 +3471,7 @@ var init_shard_manager = __esm({
2680
3471
  "src/lib/shard-manager.ts"() {
2681
3472
  "use strict";
2682
3473
  init_config();
2683
- SHARDS_DIR = path6.join(EXE_AI_DIR, "shards");
3474
+ SHARDS_DIR = path8.join(EXE_AI_DIR, "shards");
2684
3475
  SHARD_IDLE_MS = 5 * 60 * 1e3;
2685
3476
  MAX_OPEN_SHARDS = 10;
2686
3477
  EVICTION_INTERVAL_MS = 60 * 1e3;
@@ -2800,6 +3591,68 @@ var init_platform_procedures = __esm({
2800
3591
  domain: "architecture",
2801
3592
  priority: "p0",
2802
3593
  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."
3594
+ },
3595
+ // --- MCP is the ONLY data interface ---
3596
+ {
3597
+ title: "MCP disconnect \u2014 ask the user, never work around it",
3598
+ domain: "workflow",
3599
+ priority: "p0",
3600
+ 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."
3601
+ },
3602
+ // --- MCP Tool Catalog (Layer 0 — every agent knows what tools exist) ---
3603
+ {
3604
+ title: "MCP tools \u2014 memory and search",
3605
+ domain: "tool-use",
3606
+ priority: "p1",
3607
+ 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)."
3608
+ },
3609
+ {
3610
+ title: "MCP tools \u2014 task orchestration",
3611
+ domain: "tool-use",
3612
+ priority: "p1",
3613
+ 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."
3614
+ },
3615
+ {
3616
+ title: "MCP tools \u2014 knowledge graph (GraphRAG)",
3617
+ domain: "tool-use",
3618
+ priority: "p1",
3619
+ 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."
3620
+ },
3621
+ {
3622
+ title: "MCP tools \u2014 identity, behavior, and decisions",
3623
+ domain: "tool-use",
3624
+ priority: "p1",
3625
+ 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."
3626
+ },
3627
+ {
3628
+ title: "MCP tools \u2014 communication and messaging",
3629
+ domain: "tool-use",
3630
+ priority: "p1",
3631
+ 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.)."
3632
+ },
3633
+ {
3634
+ title: "MCP tools \u2014 wiki, documents, and content",
3635
+ domain: "tool-use",
3636
+ priority: "p1",
3637
+ 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."
3638
+ },
3639
+ {
3640
+ title: "MCP tools \u2014 system, operations, and admin",
3641
+ domain: "tool-use",
3642
+ priority: "p1",
3643
+ 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."
3644
+ },
3645
+ {
3646
+ title: "MCP tools \u2014 config, licensing, and team",
3647
+ domain: "tool-use",
3648
+ priority: "p1",
3649
+ 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."
3650
+ },
3651
+ {
3652
+ title: "MCP tools \u2014 advanced (triggers, skills, orchestration)",
3653
+ domain: "tool-use",
3654
+ priority: "p1",
3655
+ 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."
2803
3656
  }
2804
3657
  ];
2805
3658
  PLATFORM_PROCEDURE_TITLES = new Set(
@@ -2816,7 +3669,7 @@ __export(global_procedures_exports, {
2816
3669
  loadGlobalProcedures: () => loadGlobalProcedures,
2817
3670
  storeGlobalProcedure: () => storeGlobalProcedure
2818
3671
  });
2819
- import { randomUUID } from "crypto";
3672
+ import { randomUUID as randomUUID2 } from "crypto";
2820
3673
  async function loadGlobalProcedures() {
2821
3674
  const client = getClient();
2822
3675
  const result = await client.execute({
@@ -2845,7 +3698,7 @@ ${sections.join("\n\n")}
2845
3698
  `;
2846
3699
  }
2847
3700
  async function storeGlobalProcedure(input) {
2848
- const id = randomUUID();
3701
+ const id = randomUUID2();
2849
3702
  const now = (/* @__PURE__ */ new Date()).toISOString();
2850
3703
  const client = getClient();
2851
3704
  await client.execute({
@@ -2880,12 +3733,12 @@ ${p.content}`).join("\n\n");
2880
3733
  });
2881
3734
 
2882
3735
  // src/lib/session-registry.ts
2883
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync as existsSync7 } from "fs";
2884
- import path7 from "path";
2885
- import os5 from "os";
3736
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync9 } from "fs";
3737
+ import path9 from "path";
3738
+ import os6 from "os";
2886
3739
  function registerSession(entry) {
2887
- const dir = path7.dirname(REGISTRY_PATH);
2888
- if (!existsSync7(dir)) {
3740
+ const dir = path9.dirname(REGISTRY_PATH);
3741
+ if (!existsSync9(dir)) {
2889
3742
  mkdirSync3(dir, { recursive: true });
2890
3743
  }
2891
3744
  const sessions = listSessions();
@@ -2895,11 +3748,11 @@ function registerSession(entry) {
2895
3748
  } else {
2896
3749
  sessions.push(entry);
2897
3750
  }
2898
- writeFileSync3(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
3751
+ writeFileSync4(REGISTRY_PATH, JSON.stringify(sessions, null, 2));
2899
3752
  }
2900
3753
  function listSessions() {
2901
3754
  try {
2902
- const raw = readFileSync4(REGISTRY_PATH, "utf8");
3755
+ const raw = readFileSync6(REGISTRY_PATH, "utf8");
2903
3756
  return JSON.parse(raw);
2904
3757
  } catch {
2905
3758
  return [];
@@ -2909,7 +3762,7 @@ var REGISTRY_PATH;
2909
3762
  var init_session_registry = __esm({
2910
3763
  "src/lib/session-registry.ts"() {
2911
3764
  "use strict";
2912
- REGISTRY_PATH = path7.join(os5.homedir(), ".exe-os", "session-registry.json");
3765
+ REGISTRY_PATH = path9.join(os6.homedir(), ".exe-os", "session-registry.json");
2913
3766
  }
2914
3767
  });
2915
3768
 
@@ -3180,17 +4033,17 @@ __export(intercom_queue_exports, {
3180
4033
  queueIntercom: () => queueIntercom,
3181
4034
  readQueue: () => readQueue
3182
4035
  });
3183
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
3184
- import path8 from "path";
3185
- import os6 from "os";
4036
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync10, mkdirSync as mkdirSync4 } from "fs";
4037
+ import path10 from "path";
4038
+ import os7 from "os";
3186
4039
  function ensureDir() {
3187
- const dir = path8.dirname(QUEUE_PATH);
3188
- if (!existsSync8(dir)) mkdirSync4(dir, { recursive: true });
4040
+ const dir = path10.dirname(QUEUE_PATH);
4041
+ if (!existsSync10(dir)) mkdirSync4(dir, { recursive: true });
3189
4042
  }
3190
4043
  function readQueue() {
3191
4044
  try {
3192
- if (!existsSync8(QUEUE_PATH)) return [];
3193
- return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
4045
+ if (!existsSync10(QUEUE_PATH)) return [];
4046
+ return JSON.parse(readFileSync7(QUEUE_PATH, "utf8"));
3194
4047
  } catch {
3195
4048
  return [];
3196
4049
  }
@@ -3198,7 +4051,7 @@ function readQueue() {
3198
4051
  function writeQueue(queue) {
3199
4052
  ensureDir();
3200
4053
  const tmp = `${QUEUE_PATH}.tmp`;
3201
- writeFileSync4(tmp, JSON.stringify(queue, null, 2));
4054
+ writeFileSync5(tmp, JSON.stringify(queue, null, 2));
3202
4055
  renameSync3(tmp, QUEUE_PATH);
3203
4056
  }
3204
4057
  function queueIntercom(targetSession, reason) {
@@ -3290,29 +4143,29 @@ var QUEUE_PATH, MAX_RETRIES2, TTL_MS, INTERCOM_LOG;
3290
4143
  var init_intercom_queue = __esm({
3291
4144
  "src/lib/intercom-queue.ts"() {
3292
4145
  "use strict";
3293
- QUEUE_PATH = path8.join(os6.homedir(), ".exe-os", "intercom-queue.json");
4146
+ QUEUE_PATH = path10.join(os7.homedir(), ".exe-os", "intercom-queue.json");
3294
4147
  MAX_RETRIES2 = 5;
3295
4148
  TTL_MS = 60 * 60 * 1e3;
3296
- INTERCOM_LOG = path8.join(os6.homedir(), ".exe-os", "intercom.log");
4149
+ INTERCOM_LOG = path10.join(os7.homedir(), ".exe-os", "intercom.log");
3297
4150
  }
3298
4151
  });
3299
4152
 
3300
4153
  // src/lib/license.ts
3301
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
3302
- import { randomUUID as randomUUID2 } from "crypto";
4154
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
4155
+ import { randomUUID as randomUUID3 } from "crypto";
3303
4156
  import { createRequire as createRequire2 } from "module";
3304
4157
  import { pathToFileURL as pathToFileURL2 } from "url";
3305
- import os7 from "os";
3306
- import path9 from "path";
4158
+ import os8 from "os";
4159
+ import path11 from "path";
3307
4160
  import { jwtVerify, importSPKI } from "jose";
3308
4161
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
3309
4162
  var init_license = __esm({
3310
4163
  "src/lib/license.ts"() {
3311
4164
  "use strict";
3312
4165
  init_config();
3313
- LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
3314
- CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
3315
- DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
4166
+ LICENSE_PATH = path11.join(EXE_AI_DIR, "license.key");
4167
+ CACHE_PATH = path11.join(EXE_AI_DIR, "license-cache.json");
4168
+ DEVICE_ID_PATH = path11.join(EXE_AI_DIR, "device-id");
3316
4169
  PLAN_LIMITS = {
3317
4170
  free: { devices: 1, employees: 1, memories: 5e3 },
3318
4171
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -3324,12 +4177,12 @@ var init_license = __esm({
3324
4177
  });
3325
4178
 
3326
4179
  // src/lib/plan-limits.ts
3327
- import { readFileSync as readFileSync7, existsSync as existsSync10 } from "fs";
3328
- import path10 from "path";
4180
+ import { readFileSync as readFileSync9, existsSync as existsSync12 } from "fs";
4181
+ import path12 from "path";
3329
4182
  function getLicenseSync() {
3330
4183
  try {
3331
- if (!existsSync10(CACHE_PATH2)) return freeLicense();
3332
- const raw = JSON.parse(readFileSync7(CACHE_PATH2, "utf8"));
4184
+ if (!existsSync12(CACHE_PATH2)) return freeLicense();
4185
+ const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
3333
4186
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
3334
4187
  const parts = raw.token.split(".");
3335
4188
  if (parts.length !== 3) return freeLicense();
@@ -3367,8 +4220,8 @@ function assertEmployeeLimitSync(rosterPath) {
3367
4220
  const filePath = rosterPath ?? EMPLOYEES_PATH;
3368
4221
  let count = 0;
3369
4222
  try {
3370
- if (existsSync10(filePath)) {
3371
- const raw = readFileSync7(filePath, "utf8");
4223
+ if (existsSync12(filePath)) {
4224
+ const raw = readFileSync9(filePath, "utf8");
3372
4225
  const employees = JSON.parse(raw);
3373
4226
  count = Array.isArray(employees) ? employees.length : 0;
3374
4227
  }
@@ -3397,12 +4250,12 @@ var init_plan_limits = __esm({
3397
4250
  this.name = "PlanLimitError";
3398
4251
  }
3399
4252
  };
3400
- CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
4253
+ CACHE_PATH2 = path12.join(EXE_AI_DIR, "license-cache.json");
3401
4254
  }
3402
4255
  });
3403
4256
 
3404
4257
  // src/lib/session-kill-telemetry.ts
3405
- import crypto from "crypto";
4258
+ import crypto2 from "crypto";
3406
4259
  async function recordSessionKill(input) {
3407
4260
  try {
3408
4261
  const client = getClient();
@@ -3412,7 +4265,7 @@ async function recordSessionKill(input) {
3412
4265
  ticks_idle, estimated_tokens_saved)
3413
4266
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
3414
4267
  args: [
3415
- crypto.randomUUID(),
4268
+ crypto2.randomUUID(),
3416
4269
  input.sessionName,
3417
4270
  input.agentId,
3418
4271
  (/* @__PURE__ */ new Date()).toISOString(),
@@ -3733,8 +4586,8 @@ __export(tasks_review_exports, {
3733
4586
  isStale: () => isStale,
3734
4587
  listPendingReviews: () => listPendingReviews
3735
4588
  });
3736
- import path11 from "path";
3737
- import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
4589
+ import path13 from "path";
4590
+ import { existsSync as existsSync13, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
3738
4591
  function formatAge(isoTimestamp) {
3739
4592
  if (!isoTimestamp) return "";
3740
4593
  const ms = Date.now() - new Date(isoTimestamp).getTime();
@@ -4003,11 +4856,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
4003
4856
  );
4004
4857
  }
4005
4858
  try {
4006
- const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
4007
- if (existsSync11(cacheDir)) {
4859
+ const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
4860
+ if (existsSync13(cacheDir)) {
4008
4861
  for (const f of readdirSync2(cacheDir)) {
4009
4862
  if (f.startsWith("review-notified-")) {
4010
- unlinkSync2(path11.join(cacheDir, f));
4863
+ unlinkSync3(path13.join(cacheDir, f));
4011
4864
  }
4012
4865
  }
4013
4866
  }
@@ -4031,7 +4884,7 @@ var init_tasks_review = __esm({
4031
4884
  // src/lib/tmux-routing.ts
4032
4885
  var tmux_routing_exports = {};
4033
4886
  __export(tmux_routing_exports, {
4034
- acquireSpawnLock: () => acquireSpawnLock,
4887
+ acquireSpawnLock: () => acquireSpawnLock2,
4035
4888
  employeeSessionName: () => employeeSessionName,
4036
4889
  ensureEmployee: () => ensureEmployee,
4037
4890
  extractRootExe: () => extractRootExe,
@@ -4047,20 +4900,20 @@ __export(tmux_routing_exports, {
4047
4900
  notifyParentExe: () => notifyParentExe,
4048
4901
  parseParentExe: () => parseParentExe,
4049
4902
  registerParentExe: () => registerParentExe,
4050
- releaseSpawnLock: () => releaseSpawnLock,
4903
+ releaseSpawnLock: () => releaseSpawnLock2,
4051
4904
  resolveExeSession: () => resolveExeSession,
4052
4905
  sendIntercom: () => sendIntercom,
4053
4906
  spawnEmployee: () => spawnEmployee,
4054
4907
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
4055
4908
  });
4056
4909
  import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
4057
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync3 } from "fs";
4058
- import path12 from "path";
4059
- import os8 from "os";
4060
- import { fileURLToPath } from "url";
4061
- import { unlinkSync as unlinkSync3 } from "fs";
4910
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync3 } from "fs";
4911
+ import path14 from "path";
4912
+ import os9 from "os";
4913
+ import { fileURLToPath as fileURLToPath2 } from "url";
4914
+ import { unlinkSync as unlinkSync4 } from "fs";
4062
4915
  function spawnLockPath(sessionName) {
4063
- return path12.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4916
+ return path14.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4064
4917
  }
4065
4918
  function isProcessAlive(pid) {
4066
4919
  try {
@@ -4070,14 +4923,14 @@ function isProcessAlive(pid) {
4070
4923
  return false;
4071
4924
  }
4072
4925
  }
4073
- function acquireSpawnLock(sessionName) {
4074
- if (!existsSync12(SPAWN_LOCK_DIR)) {
4926
+ function acquireSpawnLock2(sessionName) {
4927
+ if (!existsSync14(SPAWN_LOCK_DIR)) {
4075
4928
  mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
4076
4929
  }
4077
4930
  const lockFile = spawnLockPath(sessionName);
4078
- if (existsSync12(lockFile)) {
4931
+ if (existsSync14(lockFile)) {
4079
4932
  try {
4080
- const lock = JSON.parse(readFileSync8(lockFile, "utf8"));
4933
+ const lock = JSON.parse(readFileSync10(lockFile, "utf8"));
4081
4934
  const age = Date.now() - lock.timestamp;
4082
4935
  if (isProcessAlive(lock.pid) && age < 6e4) {
4083
4936
  return false;
@@ -4085,25 +4938,25 @@ function acquireSpawnLock(sessionName) {
4085
4938
  } catch {
4086
4939
  }
4087
4940
  }
4088
- writeFileSync6(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
4941
+ writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
4089
4942
  return true;
4090
4943
  }
4091
- function releaseSpawnLock(sessionName) {
4944
+ function releaseSpawnLock2(sessionName) {
4092
4945
  try {
4093
- unlinkSync3(spawnLockPath(sessionName));
4946
+ unlinkSync4(spawnLockPath(sessionName));
4094
4947
  } catch {
4095
4948
  }
4096
4949
  }
4097
4950
  function resolveBehaviorsExporterScript() {
4098
4951
  try {
4099
- const thisFile = fileURLToPath(import.meta.url);
4100
- const scriptPath = path12.join(
4101
- path12.dirname(thisFile),
4952
+ const thisFile = fileURLToPath2(import.meta.url);
4953
+ const scriptPath = path14.join(
4954
+ path14.dirname(thisFile),
4102
4955
  "..",
4103
4956
  "bin",
4104
4957
  "exe-export-behaviors.js"
4105
4958
  );
4106
- return existsSync12(scriptPath) ? scriptPath : null;
4959
+ return existsSync14(scriptPath) ? scriptPath : null;
4107
4960
  } catch {
4108
4961
  return null;
4109
4962
  }
@@ -4169,12 +5022,12 @@ function extractRootExe(name) {
4169
5022
  return parts.length > 0 ? parts[parts.length - 1] : null;
4170
5023
  }
4171
5024
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4172
- if (!existsSync12(SESSION_CACHE)) {
5025
+ if (!existsSync14(SESSION_CACHE)) {
4173
5026
  mkdirSync6(SESSION_CACHE, { recursive: true });
4174
5027
  }
4175
5028
  const rootExe = extractRootExe(parentExe) ?? parentExe;
4176
- const filePath = path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4177
- writeFileSync6(filePath, JSON.stringify({
5029
+ const filePath = path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
5030
+ writeFileSync7(filePath, JSON.stringify({
4178
5031
  parentExe: rootExe,
4179
5032
  dispatchedBy: dispatchedBy || rootExe,
4180
5033
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -4182,7 +5035,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4182
5035
  }
4183
5036
  function getParentExe(sessionKey) {
4184
5037
  try {
4185
- const data = JSON.parse(readFileSync8(path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
5038
+ const data = JSON.parse(readFileSync10(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4186
5039
  return data.parentExe || null;
4187
5040
  } catch {
4188
5041
  return null;
@@ -4190,8 +5043,8 @@ function getParentExe(sessionKey) {
4190
5043
  }
4191
5044
  function getDispatchedBy(sessionKey) {
4192
5045
  try {
4193
- const data = JSON.parse(readFileSync8(
4194
- path12.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
5046
+ const data = JSON.parse(readFileSync10(
5047
+ path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4195
5048
  "utf8"
4196
5049
  ));
4197
5050
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -4226,10 +5079,10 @@ function isEmployeeAlive(sessionName) {
4226
5079
  }
4227
5080
  function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
4228
5081
  const base = employeeSessionName(employeeName, exeSession);
4229
- if (!isAlive(base) && acquireSpawnLock(base)) return 0;
5082
+ if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
4230
5083
  for (let i = 2; i <= maxInstances; i++) {
4231
5084
  const candidate = employeeSessionName(employeeName, exeSession, i);
4232
- if (!isAlive(candidate) && acquireSpawnLock(candidate)) return i;
5085
+ if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
4233
5086
  }
4234
5087
  return null;
4235
5088
  }
@@ -4261,8 +5114,8 @@ async function verifyPaneAtCapacity(sessionName) {
4261
5114
  }
4262
5115
  function readDebounceState() {
4263
5116
  try {
4264
- if (!existsSync12(DEBOUNCE_FILE)) return {};
4265
- const raw = JSON.parse(readFileSync8(DEBOUNCE_FILE, "utf8"));
5117
+ if (!existsSync14(DEBOUNCE_FILE)) return {};
5118
+ const raw = JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
4266
5119
  const state = {};
4267
5120
  for (const [key, val] of Object.entries(raw)) {
4268
5121
  if (typeof val === "number") {
@@ -4278,8 +5131,8 @@ function readDebounceState() {
4278
5131
  }
4279
5132
  function writeDebounceState(state) {
4280
5133
  try {
4281
- if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
4282
- writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
5134
+ if (!existsSync14(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
5135
+ writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
4283
5136
  } catch {
4284
5137
  }
4285
5138
  }
@@ -4343,7 +5196,7 @@ function isSessionBusy(sessionName) {
4343
5196
  function isExeSession(sessionName) {
4344
5197
  const matchesBaseWithInstance = (baseName) => sessionName === baseName || sessionName.startsWith(baseName) && /^\d+$/.test(sessionName.slice(baseName.length));
4345
5198
  const coordinatorName = getCoordinatorName();
4346
- return matchesBaseWithInstance(coordinatorName) || matchesBaseWithInstance("exe");
5199
+ return matchesBaseWithInstance(coordinatorName);
4347
5200
  }
4348
5201
  function sendIntercom(targetSession) {
4349
5202
  const transport = getTransport();
@@ -4378,8 +5231,8 @@ function sendIntercom(targetSession) {
4378
5231
  try {
4379
5232
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
4380
5233
  const agent = baseAgentName(rawAgent);
4381
- const markerPath = path12.join(SESSION_CACHE, `current-task-${agent}.json`);
4382
- if (existsSync12(markerPath)) {
5234
+ const markerPath = path14.join(SESSION_CACHE, `current-task-${agent}.json`);
5235
+ if (existsSync14(markerPath)) {
4383
5236
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
4384
5237
  return "debounced";
4385
5238
  }
@@ -4389,8 +5242,8 @@ function sendIntercom(targetSession) {
4389
5242
  try {
4390
5243
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
4391
5244
  const agent = baseAgentName(rawAgent);
4392
- const taskDir = path12.join(process.cwd(), "exe", agent);
4393
- if (existsSync12(taskDir)) {
5245
+ const taskDir = path14.join(process.cwd(), "exe", agent);
5246
+ if (existsSync14(taskDir)) {
4394
5247
  const files = readdirSync3(taskDir).filter(
4395
5248
  (f) => f.endsWith(".md") && f !== "DONE.txt"
4396
5249
  );
@@ -4557,26 +5410,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4557
5410
  const transport = getTransport();
4558
5411
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
4559
5412
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
4560
- const logDir = path12.join(os8.homedir(), ".exe-os", "session-logs");
4561
- const logFile = path12.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4562
- if (!existsSync12(logDir)) {
5413
+ const logDir = path14.join(os9.homedir(), ".exe-os", "session-logs");
5414
+ const logFile = path14.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5415
+ if (!existsSync14(logDir)) {
4563
5416
  mkdirSync6(logDir, { recursive: true });
4564
5417
  }
4565
5418
  transport.kill(sessionName);
4566
5419
  let cleanupSuffix = "";
4567
5420
  try {
4568
- const thisFile = fileURLToPath(import.meta.url);
4569
- const cleanupScript = path12.join(path12.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
4570
- if (existsSync12(cleanupScript)) {
5421
+ const thisFile = fileURLToPath2(import.meta.url);
5422
+ const cleanupScript = path14.join(path14.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5423
+ if (existsSync14(cleanupScript)) {
4571
5424
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
4572
5425
  }
4573
5426
  } catch {
4574
5427
  }
4575
5428
  try {
4576
- const claudeJsonPath = path12.join(os8.homedir(), ".claude.json");
5429
+ const claudeJsonPath = path14.join(os9.homedir(), ".claude.json");
4577
5430
  let claudeJson = {};
4578
5431
  try {
4579
- claudeJson = JSON.parse(readFileSync8(claudeJsonPath, "utf8"));
5432
+ claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
4580
5433
  } catch {
4581
5434
  }
4582
5435
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -4584,17 +5437,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4584
5437
  const trustDir = opts?.cwd ?? projectDir;
4585
5438
  if (!projects[trustDir]) projects[trustDir] = {};
4586
5439
  projects[trustDir].hasTrustDialogAccepted = true;
4587
- writeFileSync6(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
5440
+ writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
4588
5441
  } catch {
4589
5442
  }
4590
5443
  try {
4591
- const settingsDir = path12.join(os8.homedir(), ".claude", "projects");
5444
+ const settingsDir = path14.join(os9.homedir(), ".claude", "projects");
4592
5445
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
4593
- const projSettingsDir = path12.join(settingsDir, normalizedKey);
4594
- const settingsPath = path12.join(projSettingsDir, "settings.json");
5446
+ const projSettingsDir = path14.join(settingsDir, normalizedKey);
5447
+ const settingsPath = path14.join(projSettingsDir, "settings.json");
4595
5448
  let settings = {};
4596
5449
  try {
4597
- settings = JSON.parse(readFileSync8(settingsPath, "utf8"));
5450
+ settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
4598
5451
  } catch {
4599
5452
  }
4600
5453
  const perms = settings.permissions ?? {};
@@ -4623,7 +5476,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4623
5476
  perms.allow = allow;
4624
5477
  settings.permissions = perms;
4625
5478
  mkdirSync6(projSettingsDir, { recursive: true });
4626
- writeFileSync6(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5479
+ writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
4627
5480
  }
4628
5481
  } catch {
4629
5482
  }
@@ -4638,8 +5491,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4638
5491
  let behaviorsFlag = "";
4639
5492
  let legacyFallbackWarned = false;
4640
5493
  if (!useExeAgent && !useBinSymlink) {
4641
- const identityPath = path12.join(
4642
- os8.homedir(),
5494
+ const identityPath = path14.join(
5495
+ os9.homedir(),
4643
5496
  ".exe-os",
4644
5497
  "identity",
4645
5498
  `${employeeName}.md`
@@ -4648,13 +5501,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4648
5501
  const hasAgentFlag = claudeSupportsAgentFlag();
4649
5502
  if (hasAgentFlag) {
4650
5503
  identityFlag = ` --agent ${employeeName}`;
4651
- } else if (existsSync12(identityPath)) {
5504
+ } else if (existsSync14(identityPath)) {
4652
5505
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
4653
5506
  legacyFallbackWarned = true;
4654
5507
  }
4655
5508
  const behaviorsFile = exportBehaviorsSync(
4656
5509
  employeeName,
4657
- path12.basename(spawnCwd),
5510
+ path14.basename(spawnCwd),
4658
5511
  sessionName
4659
5512
  );
4660
5513
  if (behaviorsFile) {
@@ -4669,16 +5522,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4669
5522
  }
4670
5523
  let sessionContextFlag = "";
4671
5524
  try {
4672
- const ctxDir = path12.join(os8.homedir(), ".exe-os", "session-cache");
5525
+ const ctxDir = path14.join(os9.homedir(), ".exe-os", "session-cache");
4673
5526
  mkdirSync6(ctxDir, { recursive: true });
4674
- const ctxFile = path12.join(ctxDir, `session-context-${sessionName}.md`);
5527
+ const ctxFile = path14.join(ctxDir, `session-context-${sessionName}.md`);
4675
5528
  const ctxContent = [
4676
5529
  `## Session Context`,
4677
5530
  `You are running in tmux session: ${sessionName}.`,
4678
5531
  `Your parent coordinator session is ${exeSession}.`,
4679
5532
  `Your employees (if any) use the -${exeSession} suffix.`
4680
5533
  ].join("\n");
4681
- writeFileSync6(ctxFile, ctxContent);
5534
+ writeFileSync7(ctxFile, ctxContent);
4682
5535
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
4683
5536
  } catch {
4684
5537
  }
@@ -4752,14 +5605,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4752
5605
  command: spawnCommand
4753
5606
  });
4754
5607
  if (spawnResult.error) {
4755
- releaseSpawnLock(sessionName);
5608
+ releaseSpawnLock2(sessionName);
4756
5609
  return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
4757
5610
  }
4758
5611
  transport.pipeLog(sessionName, logFile);
4759
5612
  try {
4760
5613
  const mySession = getMySession();
4761
- const dispatchInfo = path12.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
4762
- writeFileSync6(dispatchInfo, JSON.stringify({
5614
+ const dispatchInfo = path14.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5615
+ writeFileSync7(dispatchInfo, JSON.stringify({
4763
5616
  dispatchedBy: mySession,
4764
5617
  rootExe: exeSession,
4765
5618
  provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
@@ -4797,7 +5650,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4797
5650
  }
4798
5651
  }
4799
5652
  if (!booted) {
4800
- releaseSpawnLock(sessionName);
5653
+ releaseSpawnLock2(sessionName);
4801
5654
  const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
4802
5655
  return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
4803
5656
  }
@@ -4815,7 +5668,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4815
5668
  pid: 0,
4816
5669
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
4817
5670
  });
4818
- releaseSpawnLock(sessionName);
5671
+ releaseSpawnLock2(sessionName);
4819
5672
  return { sessionName };
4820
5673
  }
4821
5674
  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;
@@ -4833,15 +5686,15 @@ var init_tmux_routing = __esm({
4833
5686
  init_intercom_queue();
4834
5687
  init_plan_limits();
4835
5688
  init_employees();
4836
- SPAWN_LOCK_DIR = path12.join(os8.homedir(), ".exe-os", "spawn-locks");
4837
- SESSION_CACHE = path12.join(os8.homedir(), ".exe-os", "session-cache");
5689
+ SPAWN_LOCK_DIR = path14.join(os9.homedir(), ".exe-os", "spawn-locks");
5690
+ SESSION_CACHE = path14.join(os9.homedir(), ".exe-os", "session-cache");
4838
5691
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4839
5692
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
4840
5693
  VERIFY_PANE_LINES = 200;
4841
5694
  INTERCOM_DEBOUNCE_MS = 3e4;
4842
5695
  CODEX_DEBOUNCE_MS = 12e4;
4843
- INTERCOM_LOG2 = path12.join(os8.homedir(), ".exe-os", "intercom.log");
4844
- DEBOUNCE_FILE = path12.join(SESSION_CACHE, "intercom-debounce.json");
5696
+ INTERCOM_LOG2 = path14.join(os9.homedir(), ".exe-os", "intercom.log");
5697
+ DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
4845
5698
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4846
5699
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
4847
5700
  }
@@ -4881,20 +5734,20 @@ var init_task_scope = __esm({
4881
5734
  });
4882
5735
 
4883
5736
  // src/lib/notifications.ts
4884
- import crypto2 from "crypto";
4885
- import path13 from "path";
4886
- import os9 from "os";
5737
+ import crypto3 from "crypto";
5738
+ import path15 from "path";
5739
+ import os10 from "os";
4887
5740
  import {
4888
- readFileSync as readFileSync9,
5741
+ readFileSync as readFileSync11,
4889
5742
  readdirSync as readdirSync4,
4890
- unlinkSync as unlinkSync4,
4891
- existsSync as existsSync13,
5743
+ unlinkSync as unlinkSync5,
5744
+ existsSync as existsSync15,
4892
5745
  rmdirSync
4893
5746
  } from "fs";
4894
5747
  async function writeNotification(notification) {
4895
5748
  try {
4896
5749
  const client = getClient();
4897
- const id = crypto2.randomUUID();
5750
+ const id = crypto3.randomUUID();
4898
5751
  const now = (/* @__PURE__ */ new Date()).toISOString();
4899
5752
  const sessionScope = notification.sessionScope === void 0 ? getCurrentSessionScope() : notification.sessionScope;
4900
5753
  await client.execute({
@@ -4939,7 +5792,7 @@ var init_notifications = __esm({
4939
5792
 
4940
5793
  // src/lib/project-name.ts
4941
5794
  import { execSync as execSync5 } from "child_process";
4942
- import path14 from "path";
5795
+ import path16 from "path";
4943
5796
  function getProjectName(cwd) {
4944
5797
  const dir = cwd ?? process.cwd();
4945
5798
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -4952,7 +5805,7 @@ function getProjectName(cwd) {
4952
5805
  timeout: 2e3,
4953
5806
  stdio: ["pipe", "pipe", "pipe"]
4954
5807
  }).trim();
4955
- repoRoot = path14.dirname(gitCommonDir);
5808
+ repoRoot = path16.dirname(gitCommonDir);
4956
5809
  } catch {
4957
5810
  repoRoot = execSync5("git rev-parse --show-toplevel", {
4958
5811
  cwd: dir,
@@ -4961,11 +5814,11 @@ function getProjectName(cwd) {
4961
5814
  stdio: ["pipe", "pipe", "pipe"]
4962
5815
  }).trim();
4963
5816
  }
4964
- _cached2 = path14.basename(repoRoot);
5817
+ _cached2 = path16.basename(repoRoot);
4965
5818
  _cachedCwd = dir;
4966
5819
  return _cached2;
4967
5820
  } catch {
4968
- _cached2 = path14.basename(dir);
5821
+ _cached2 = path16.basename(dir);
4969
5822
  _cachedCwd = dir;
4970
5823
  return _cached2;
4971
5824
  }
@@ -5042,12 +5895,12 @@ var init_session_scope = __esm({
5042
5895
  });
5043
5896
 
5044
5897
  // src/lib/tasks-crud.ts
5045
- import crypto3 from "crypto";
5046
- import path15 from "path";
5047
- import os10 from "os";
5898
+ import crypto4 from "crypto";
5899
+ import path17 from "path";
5900
+ import os11 from "os";
5048
5901
  import { execSync as execSync6 } from "child_process";
5049
5902
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
5050
- import { existsSync as existsSync14, readFileSync as readFileSync10 } from "fs";
5903
+ import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
5051
5904
  async function writeCheckpoint(input) {
5052
5905
  const client = getClient();
5053
5906
  const row = await resolveTask(client, input.taskId);
@@ -5163,7 +6016,7 @@ async function resolveTask(client, identifier, scopeSession) {
5163
6016
  }
5164
6017
  async function createTaskCore(input) {
5165
6018
  const client = getClient();
5166
- const id = crypto3.randomUUID();
6019
+ const id = crypto4.randomUUID();
5167
6020
  const now = (/* @__PURE__ */ new Date()).toISOString();
5168
6021
  const slug = slugify(input.title);
5169
6022
  let earlySessionScope = null;
@@ -5172,15 +6025,20 @@ async function createTaskCore(input) {
5172
6025
  const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
5173
6026
  const resolved = resolveExeSession2();
5174
6027
  if (resolved && input.projectName) {
5175
- const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
5176
- const sessionProject = getSessionProject2(resolved);
5177
- if (sessionProject && sessionProject !== input.projectName) {
5178
- scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
5179
- process.stderr.write(`[create_task] ${scopeMismatchWarning}
5180
- `);
5181
- earlySessionScope = null;
5182
- } else {
6028
+ const isCoordinatorSession = !resolved.includes("-");
6029
+ if (isCoordinatorSession) {
5183
6030
  earlySessionScope = resolved;
6031
+ } else {
6032
+ const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
6033
+ const sessionProject = getSessionProject2(resolved);
6034
+ if (sessionProject && sessionProject !== input.projectName) {
6035
+ scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
6036
+ process.stderr.write(`[create_task] ${scopeMismatchWarning}
6037
+ `);
6038
+ earlySessionScope = null;
6039
+ } else {
6040
+ earlySessionScope = resolved;
6041
+ }
5184
6042
  }
5185
6043
  } else {
5186
6044
  earlySessionScope = resolved;
@@ -5241,8 +6099,8 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
5241
6099
  }
5242
6100
  if (input.baseDir) {
5243
6101
  try {
5244
- await mkdir4(path15.join(input.baseDir, "exe", "output"), { recursive: true });
5245
- await mkdir4(path15.join(input.baseDir, "exe", "research"), { recursive: true });
6102
+ await mkdir4(path17.join(input.baseDir, "exe", "output"), { recursive: true });
6103
+ await mkdir4(path17.join(input.baseDir, "exe", "research"), { recursive: true });
5246
6104
  await ensureArchitectureDoc(input.baseDir, input.projectName);
5247
6105
  await ensureGitignoreExe(input.baseDir);
5248
6106
  } catch {
@@ -5278,10 +6136,10 @@ ${scopeMismatchWarning}` : scopeMismatchWarning;
5278
6136
  });
5279
6137
  if (input.baseDir) {
5280
6138
  try {
5281
- const EXE_OS_DIR = path15.join(os10.homedir(), ".exe-os");
5282
- const mdPath = path15.join(EXE_OS_DIR, taskFile);
5283
- const mdDir = path15.dirname(mdPath);
5284
- if (!existsSync14(mdDir)) await mkdir4(mdDir, { recursive: true });
6139
+ const EXE_OS_DIR = path17.join(os11.homedir(), ".exe-os");
6140
+ const mdPath = path17.join(EXE_OS_DIR, taskFile);
6141
+ const mdDir = path17.dirname(mdPath);
6142
+ if (!existsSync16(mdDir)) await mkdir4(mdDir, { recursive: true });
5285
6143
  const reviewer = input.reviewer ?? input.assignedBy;
5286
6144
  const mdContent = `# ${input.title}
5287
6145
 
@@ -5581,9 +6439,9 @@ async function deleteTaskCore(taskId, _baseDir) {
5581
6439
  return { taskFile, assignedTo, assignedBy, taskSlug };
5582
6440
  }
5583
6441
  async function ensureArchitectureDoc(baseDir, projectName) {
5584
- const archPath = path15.join(baseDir, "exe", "ARCHITECTURE.md");
6442
+ const archPath = path17.join(baseDir, "exe", "ARCHITECTURE.md");
5585
6443
  try {
5586
- if (existsSync14(archPath)) return;
6444
+ if (existsSync16(archPath)) return;
5587
6445
  const template = [
5588
6446
  `# ${projectName} \u2014 System Architecture`,
5589
6447
  "",
@@ -5616,10 +6474,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
5616
6474
  }
5617
6475
  }
5618
6476
  async function ensureGitignoreExe(baseDir) {
5619
- const gitignorePath = path15.join(baseDir, ".gitignore");
6477
+ const gitignorePath = path17.join(baseDir, ".gitignore");
5620
6478
  try {
5621
- if (existsSync14(gitignorePath)) {
5622
- const content = readFileSync10(gitignorePath, "utf-8");
6479
+ if (existsSync16(gitignorePath)) {
6480
+ const content = readFileSync12(gitignorePath, "utf-8");
5623
6481
  if (/^\/?exe\/?$/m.test(content)) return;
5624
6482
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
5625
6483
  } else {
@@ -5650,7 +6508,7 @@ var init_tasks_crud = __esm({
5650
6508
  });
5651
6509
 
5652
6510
  // src/lib/tasks-chain.ts
5653
- import path16 from "path";
6511
+ import path18 from "path";
5654
6512
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
5655
6513
  async function cascadeUnblock(taskId, baseDir, now) {
5656
6514
  const client = getClient();
@@ -5667,7 +6525,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
5667
6525
  });
5668
6526
  for (const ur of unblockedRows.rows) {
5669
6527
  try {
5670
- const ubFile = path16.join(baseDir, String(ur.task_file));
6528
+ const ubFile = path18.join(baseDir, String(ur.task_file));
5671
6529
  let ubContent = await readFile4(ubFile, "utf-8");
5672
6530
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
5673
6531
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -5757,6 +6615,10 @@ async function dispatchTaskToEmployee(input) {
5757
6615
  if (transport.isAlive(sessionName)) {
5758
6616
  const result = sendIntercom(sessionName);
5759
6617
  const dispatched = result === "acknowledged" || result === "debounced" || result === "queued" ? "verified" : result === "delivered" ? "sent_unverified" : "session_dead";
6618
+ process.stderr.write(
6619
+ `[dispatch-audit] intercom \u2192 ${sessionName} | task="${input.title}" [${input.priority}] | result=${dispatched} (sendIntercom=${result})
6620
+ `
6621
+ );
5760
6622
  return { dispatched, session: sessionName, crossProject };
5761
6623
  } else {
5762
6624
  const projectDir = input.projectDir ?? process.cwd();
@@ -5765,11 +6627,15 @@ async function dispatchTaskToEmployee(input) {
5765
6627
  });
5766
6628
  if (result.status === "failed") {
5767
6629
  process.stderr.write(
5768
- `[dispatch] Failed to spawn ${input.assignedTo}: ${result.error}
6630
+ `[dispatch-audit] SPAWN FAILED \u2192 ${input.assignedTo} | task="${input.title}" [${input.priority}] | error=${result.error}
5769
6631
  `
5770
6632
  );
5771
6633
  return { dispatched: "session_missing" };
5772
6634
  }
6635
+ process.stderr.write(
6636
+ `[dispatch-audit] SPAWNED \u2192 ${result.sessionName} | task="${input.title}" [${input.priority}]
6637
+ `
6638
+ );
5773
6639
  return { dispatched: "spawned", session: result.sessionName, crossProject };
5774
6640
  }
5775
6641
  } catch {
@@ -5779,7 +6645,13 @@ async function dispatchTaskToEmployee(input) {
5779
6645
  function notifyTaskDone() {
5780
6646
  try {
5781
6647
  const key = getSessionKey();
5782
- if (key && !process.env.VITEST) notifyParentExe(key);
6648
+ if (key && !process.env.VITEST) {
6649
+ notifyParentExe(key);
6650
+ process.stderr.write(
6651
+ `[dispatch-audit] notifyTaskDone \u2192 parent coordinator (session=${key})
6652
+ `
6653
+ );
6654
+ }
5783
6655
  } catch {
5784
6656
  }
5785
6657
  }
@@ -5800,16 +6672,91 @@ var init_tasks_notify = __esm({
5800
6672
  }
5801
6673
  });
5802
6674
 
6675
+ // src/lib/embedder.ts
6676
+ var embedder_exports = {};
6677
+ __export(embedder_exports, {
6678
+ disposeEmbedder: () => disposeEmbedder,
6679
+ embed: () => embed,
6680
+ embedDirect: () => embedDirect,
6681
+ getEmbedder: () => getEmbedder
6682
+ });
6683
+ async function getEmbedder() {
6684
+ const ok = await connectEmbedDaemon();
6685
+ if (!ok) {
6686
+ throw new Error(
6687
+ "Could not connect to embedding daemon. Ensure the model is installed (run /exe-setup)."
6688
+ );
6689
+ }
6690
+ }
6691
+ async function embed(text) {
6692
+ const priority = process.env.EXE_EMBED_PRIORITY === "low" ? "low" : "high";
6693
+ const vector = await embedViaClient(text, priority);
6694
+ if (!vector) {
6695
+ throw new Error(
6696
+ "Embedding failed: daemon unavailable. Run /exe-setup to verify model installation."
6697
+ );
6698
+ }
6699
+ if (vector.length !== EMBEDDING_DIM) {
6700
+ throw new Error(
6701
+ `Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}. Ensure the correct Jina v5-small Q4_K_M GGUF model is installed.`
6702
+ );
6703
+ }
6704
+ return vector;
6705
+ }
6706
+ async function disposeEmbedder() {
6707
+ disconnectClient();
6708
+ }
6709
+ async function embedDirect(text) {
6710
+ const llamaCpp = await import("node-llama-cpp");
6711
+ const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
6712
+ const { existsSync: existsSync17 } = await import("fs");
6713
+ const path20 = await import("path");
6714
+ const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
6715
+ if (!existsSync17(modelPath)) {
6716
+ throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
6717
+ }
6718
+ const llama = await llamaCpp.getLlama();
6719
+ const model = await llama.loadModel({ modelPath });
6720
+ const context = await model.createEmbeddingContext();
6721
+ try {
6722
+ const embedding = await context.getEmbeddingFor(text);
6723
+ const vector = Array.from(embedding.vector);
6724
+ if (vector.length !== EMBEDDING_DIM) {
6725
+ throw new Error(
6726
+ `Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}.`
6727
+ );
6728
+ }
6729
+ return vector;
6730
+ } finally {
6731
+ await context.dispose();
6732
+ await model.dispose();
6733
+ }
6734
+ }
6735
+ var init_embedder = __esm({
6736
+ "src/lib/embedder.ts"() {
6737
+ "use strict";
6738
+ init_memory();
6739
+ init_exe_daemon_client();
6740
+ }
6741
+ });
6742
+
5803
6743
  // src/lib/behaviors.ts
5804
- import crypto4 from "crypto";
6744
+ import crypto5 from "crypto";
5805
6745
  async function storeBehavior(opts) {
5806
6746
  const client = getClient();
5807
- const id = crypto4.randomUUID();
6747
+ const id = crypto5.randomUUID();
5808
6748
  const now = (/* @__PURE__ */ new Date()).toISOString();
6749
+ let vector = null;
6750
+ try {
6751
+ const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
6752
+ const vec = await embed2(opts.content);
6753
+ vector = new Float32Array(vec);
6754
+ } catch {
6755
+ }
5809
6756
  await client.execute({
5810
- sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
5811
- VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
5812
- args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now]
6757
+ sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
6758
+ VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
6759
+ args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
5813
6760
  });
5814
6761
  return id;
5815
6762
  }
@@ -5833,7 +6780,7 @@ __export(skill_learning_exports, {
5833
6780
  storeTrajectory: () => storeTrajectory,
5834
6781
  sweepTrajectories: () => sweepTrajectories
5835
6782
  });
5836
- import crypto5 from "crypto";
6783
+ import crypto6 from "crypto";
5837
6784
  async function extractTrajectory(taskId, agentId) {
5838
6785
  const client = getClient();
5839
6786
  const result = await client.execute({
@@ -5862,11 +6809,11 @@ async function extractTrajectory(taskId, agentId) {
5862
6809
  return signature;
5863
6810
  }
5864
6811
  function hashSignature(signature) {
5865
- return crypto5.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
6812
+ return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
5866
6813
  }
5867
6814
  async function storeTrajectory(opts) {
5868
6815
  const client = getClient();
5869
- const id = crypto5.randomUUID();
6816
+ const id = crypto6.randomUUID();
5870
6817
  const now = (/* @__PURE__ */ new Date()).toISOString();
5871
6818
  const signatureHash = hashSignature(opts.signature);
5872
6819
  await client.execute({
@@ -6131,8 +7078,8 @@ __export(tasks_exports, {
6131
7078
  updateTaskStatus: () => updateTaskStatus,
6132
7079
  writeCheckpoint: () => writeCheckpoint
6133
7080
  });
6134
- import path17 from "path";
6135
- import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
7081
+ import path19 from "path";
7082
+ import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, unlinkSync as unlinkSync6 } from "fs";
6136
7083
  async function createTask(input) {
6137
7084
  const result = await createTaskCore(input);
6138
7085
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -6151,14 +7098,14 @@ async function updateTask(input) {
6151
7098
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
6152
7099
  try {
6153
7100
  const agent = String(row.assigned_to);
6154
- const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
6155
- const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
7101
+ const cacheDir = path19.join(EXE_AI_DIR, "session-cache");
7102
+ const cachePath = path19.join(cacheDir, `current-task-${agent}.json`);
6156
7103
  if (input.status === "in_progress") {
6157
7104
  mkdirSync7(cacheDir, { recursive: true });
6158
- writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
7105
+ writeFileSync8(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
6159
7106
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
6160
7107
  try {
6161
- unlinkSync5(cachePath);
7108
+ unlinkSync6(cachePath);
6162
7109
  } catch {
6163
7110
  }
6164
7111
  }
@@ -6313,21 +7260,22 @@ var init_tasks = __esm({
6313
7260
  });
6314
7261
 
6315
7262
  // src/lib/store.ts
6316
- import { createHash } from "crypto";
7263
+ init_memory();
6317
7264
  init_database();
7265
+ import { createHash } from "crypto";
6318
7266
 
6319
7267
  // src/lib/keychain.ts
6320
7268
  import { readFile as readFile3, writeFile as writeFile3, unlink, mkdir as mkdir3, chmod as chmod2 } from "fs/promises";
6321
- import { existsSync as existsSync5 } from "fs";
6322
- import path5 from "path";
6323
- import os4 from "os";
7269
+ import { existsSync as existsSync7 } from "fs";
7270
+ import path7 from "path";
7271
+ import os5 from "os";
6324
7272
  var SERVICE = "exe-mem";
6325
7273
  var ACCOUNT = "master-key";
6326
7274
  function getKeyDir() {
6327
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path5.join(os4.homedir(), ".exe-os");
7275
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path7.join(os5.homedir(), ".exe-os");
6328
7276
  }
6329
7277
  function getKeyPath() {
6330
- return path5.join(getKeyDir(), "master.key");
7278
+ return path7.join(getKeyDir(), "master.key");
6331
7279
  }
6332
7280
  async function tryKeytar() {
6333
7281
  try {
@@ -6348,9 +7296,9 @@ async function getMasterKey() {
6348
7296
  }
6349
7297
  }
6350
7298
  const keyPath = getKeyPath();
6351
- if (!existsSync5(keyPath)) {
7299
+ if (!existsSync7(keyPath)) {
6352
7300
  process.stderr.write(
6353
- `[keychain] Key not found at ${keyPath} (HOME=${os4.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
7301
+ `[keychain] Key not found at ${keyPath} (HOME=${os5.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
6354
7302
  `
6355
7303
  );
6356
7304
  return null;
@@ -6429,6 +7377,11 @@ async function initStore(options) {
6429
7377
  encryptionKey: hexKey
6430
7378
  });
6431
7379
  await retryOnBusy2(() => ensureSchema(), "ensureSchema");
7380
+ try {
7381
+ const { initDaemonClient: initDaemonClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
7382
+ await initDaemonClient2();
7383
+ } catch {
7384
+ }
6432
7385
  if (!options?.lightweight) {
6433
7386
  try {
6434
7387
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));