@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
@@ -327,12 +327,25 @@ async function ensurePrivateDir(dirPath) {
327
327
  } catch {
328
328
  }
329
329
  }
330
+ function ensurePrivateDirSync(dirPath) {
331
+ mkdirSync2(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
332
+ try {
333
+ chmodSync(dirPath, PRIVATE_DIR_MODE);
334
+ } catch {
335
+ }
336
+ }
330
337
  async function enforcePrivateFile(filePath) {
331
338
  try {
332
339
  await chmod(filePath, PRIVATE_FILE_MODE);
333
340
  } catch {
334
341
  }
335
342
  }
343
+ function enforcePrivateFileSync(filePath) {
344
+ try {
345
+ if (existsSync2(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
346
+ } catch {
347
+ }
348
+ }
336
349
  var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
337
350
  var init_secure_files = __esm({
338
351
  "src/lib/secure-files.ts"() {
@@ -343,6 +356,21 @@ var init_secure_files = __esm({
343
356
  });
344
357
 
345
358
  // src/lib/config.ts
359
+ var config_exports = {};
360
+ __export(config_exports, {
361
+ CONFIG_MIGRATIONS: () => CONFIG_MIGRATIONS,
362
+ CONFIG_PATH: () => CONFIG_PATH,
363
+ CURRENT_CONFIG_VERSION: () => CURRENT_CONFIG_VERSION,
364
+ DB_PATH: () => DB_PATH,
365
+ EXE_AI_DIR: () => EXE_AI_DIR,
366
+ LEGACY_LANCE_PATH: () => LEGACY_LANCE_PATH,
367
+ MODELS_DIR: () => MODELS_DIR,
368
+ loadConfig: () => loadConfig,
369
+ loadConfigFrom: () => loadConfigFrom,
370
+ loadConfigSync: () => loadConfigSync,
371
+ migrateConfig: () => migrateConfig,
372
+ saveConfig: () => saveConfig
373
+ });
346
374
  import { readFile, writeFile } from "fs/promises";
347
375
  import { readFileSync as readFileSync2, existsSync as existsSync3, renameSync } from "fs";
348
376
  import path2 from "path";
@@ -446,6 +474,46 @@ async function loadConfig() {
446
474
  return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
447
475
  }
448
476
  }
477
+ function loadConfigSync() {
478
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
479
+ const configPath = path2.join(dir, "config.json");
480
+ if (!existsSync3(configPath)) {
481
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
482
+ }
483
+ try {
484
+ const raw = readFileSync2(configPath, "utf-8");
485
+ let parsed = JSON.parse(raw);
486
+ parsed = migrateLegacyConfig(parsed);
487
+ const { config: migratedCfg } = migrateConfig(parsed);
488
+ normalizeScalingRoadmap(migratedCfg);
489
+ normalizeSessionLifecycle(migratedCfg);
490
+ normalizeAutoUpdate(migratedCfg);
491
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
492
+ } catch {
493
+ return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
494
+ }
495
+ }
496
+ async function saveConfig(config) {
497
+ const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
498
+ await ensurePrivateDir(dir);
499
+ const configPath = path2.join(dir, "config.json");
500
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
501
+ await enforcePrivateFile(configPath);
502
+ }
503
+ async function loadConfigFrom(configPath) {
504
+ const raw = await readFile(configPath, "utf-8");
505
+ try {
506
+ let parsed = JSON.parse(raw);
507
+ parsed = migrateLegacyConfig(parsed);
508
+ const { config: migratedCfg } = migrateConfig(parsed);
509
+ normalizeScalingRoadmap(migratedCfg);
510
+ normalizeSessionLifecycle(migratedCfg);
511
+ normalizeAutoUpdate(migratedCfg);
512
+ return { ...DEFAULT_CONFIG, ...migratedCfg };
513
+ } catch {
514
+ return { ...DEFAULT_CONFIG };
515
+ }
516
+ }
449
517
  var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CONFIG_VERSION, DEFAULT_CONFIG, CONFIG_MIGRATIONS;
450
518
  var init_config = __esm({
451
519
  "src/lib/config.ts"() {
@@ -582,7 +650,7 @@ var init_agent_config = __esm({
582
650
  init_secure_files();
583
651
  AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
584
652
  DEFAULT_MODELS = {
585
- claude: "claude-opus-4",
653
+ claude: "claude-opus-4.6",
586
654
  codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
587
655
  opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
588
656
  };
@@ -814,13 +882,22 @@ var init_database_adapter = __esm({
814
882
  }
815
883
  });
816
884
 
885
+ // src/types/memory.ts
886
+ var EMBEDDING_DIM;
887
+ var init_memory = __esm({
888
+ "src/types/memory.ts"() {
889
+ "use strict";
890
+ EMBEDDING_DIM = 1024;
891
+ }
892
+ });
893
+
817
894
  // src/lib/database.ts
818
895
  import { createClient } from "@libsql/client";
819
896
  function getClient() {
820
897
  if (!_adapterClient) {
821
898
  throw new Error("Database client not initialized. Call initDatabase() first.");
822
899
  }
823
- if (process.env.DATABASE_URL) {
900
+ if (process.env.DATABASE_URL && process.env.EXE_USE_POSTGRES === "1") {
824
901
  return _adapterClient;
825
902
  }
826
903
  if (process.env.EXE_IS_DAEMON === "1") {
@@ -838,6 +915,7 @@ var init_database = __esm({
838
915
  init_db_retry();
839
916
  init_employees();
840
917
  init_database_adapter();
918
+ init_memory();
841
919
  _resilientClient = null;
842
920
  _daemonClient = null;
843
921
  _adapterClient = null;
@@ -1362,15 +1440,20 @@ async function createTaskCore(input) {
1362
1440
  const { resolveExeSession: resolveExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
1363
1441
  const resolved = resolveExeSession2();
1364
1442
  if (resolved && input.projectName) {
1365
- const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
1366
- const sessionProject = getSessionProject2(resolved);
1367
- if (sessionProject && sessionProject !== input.projectName) {
1368
- scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
1369
- process.stderr.write(`[create_task] ${scopeMismatchWarning}
1370
- `);
1371
- earlySessionScope = null;
1372
- } else {
1443
+ const isCoordinatorSession = !resolved.includes("-");
1444
+ if (isCoordinatorSession) {
1373
1445
  earlySessionScope = resolved;
1446
+ } else {
1447
+ const { getSessionProject: getSessionProject2 } = await Promise.resolve().then(() => (init_session_scope(), session_scope_exports));
1448
+ const sessionProject = getSessionProject2(resolved);
1449
+ if (sessionProject && sessionProject !== input.projectName) {
1450
+ scopeMismatchWarning = `session/project mismatch: session "${resolved}" owns "${sessionProject}" but task targets "${input.projectName}". Routed to default scope.`;
1451
+ process.stderr.write(`[create_task] ${scopeMismatchWarning}
1452
+ `);
1453
+ earlySessionScope = null;
1454
+ } else {
1455
+ earlySessionScope = resolved;
1456
+ }
1374
1457
  }
1375
1458
  } else {
1376
1459
  earlySessionScope = resolved;
@@ -2255,6 +2338,10 @@ async function dispatchTaskToEmployee(input) {
2255
2338
  if (transport.isAlive(sessionName)) {
2256
2339
  const result = sendIntercom(sessionName);
2257
2340
  const dispatched = result === "acknowledged" || result === "debounced" || result === "queued" ? "verified" : result === "delivered" ? "sent_unverified" : "session_dead";
2341
+ process.stderr.write(
2342
+ `[dispatch-audit] intercom \u2192 ${sessionName} | task="${input.title}" [${input.priority}] | result=${dispatched} (sendIntercom=${result})
2343
+ `
2344
+ );
2258
2345
  return { dispatched, session: sessionName, crossProject };
2259
2346
  } else {
2260
2347
  const projectDir = input.projectDir ?? process.cwd();
@@ -2263,11 +2350,15 @@ async function dispatchTaskToEmployee(input) {
2263
2350
  });
2264
2351
  if (result.status === "failed") {
2265
2352
  process.stderr.write(
2266
- `[dispatch] Failed to spawn ${input.assignedTo}: ${result.error}
2353
+ `[dispatch-audit] SPAWN FAILED \u2192 ${input.assignedTo} | task="${input.title}" [${input.priority}] | error=${result.error}
2267
2354
  `
2268
2355
  );
2269
2356
  return { dispatched: "session_missing" };
2270
2357
  }
2358
+ process.stderr.write(
2359
+ `[dispatch-audit] SPAWNED \u2192 ${result.sessionName} | task="${input.title}" [${input.priority}]
2360
+ `
2361
+ );
2271
2362
  return { dispatched: "spawned", session: result.sessionName, crossProject };
2272
2363
  }
2273
2364
  } catch {
@@ -2277,7 +2368,13 @@ async function dispatchTaskToEmployee(input) {
2277
2368
  function notifyTaskDone() {
2278
2369
  try {
2279
2370
  const key = getSessionKey();
2280
- if (key && !process.env.VITEST) notifyParentExe(key);
2371
+ if (key && !process.env.VITEST) {
2372
+ notifyParentExe(key);
2373
+ process.stderr.write(
2374
+ `[dispatch-audit] notifyTaskDone \u2192 parent coordinator (session=${key})
2375
+ `
2376
+ );
2377
+ }
2281
2378
  } catch {
2282
2379
  }
2283
2380
  }
@@ -2298,16 +2395,557 @@ var init_tasks_notify = __esm({
2298
2395
  }
2299
2396
  });
2300
2397
 
2301
- // src/lib/behaviors.ts
2398
+ // src/lib/daemon-auth.ts
2302
2399
  import crypto4 from "crypto";
2400
+ import path14 from "path";
2401
+ import { existsSync as existsSync12, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
2402
+ function normalizeToken(token) {
2403
+ if (!token) return null;
2404
+ const trimmed = token.trim();
2405
+ return trimmed.length > 0 ? trimmed : null;
2406
+ }
2407
+ function readDaemonToken() {
2408
+ try {
2409
+ if (!existsSync12(DAEMON_TOKEN_PATH)) return null;
2410
+ return normalizeToken(readFileSync10(DAEMON_TOKEN_PATH, "utf8"));
2411
+ } catch {
2412
+ return null;
2413
+ }
2414
+ }
2415
+ function ensureDaemonToken(seed) {
2416
+ const existing = readDaemonToken();
2417
+ if (existing) return existing;
2418
+ const token = normalizeToken(seed) ?? crypto4.randomBytes(32).toString("hex");
2419
+ ensurePrivateDirSync(EXE_AI_DIR);
2420
+ writeFileSync6(DAEMON_TOKEN_PATH, `${token}
2421
+ `, "utf8");
2422
+ enforcePrivateFileSync(DAEMON_TOKEN_PATH);
2423
+ return token;
2424
+ }
2425
+ var DAEMON_TOKEN_PATH;
2426
+ var init_daemon_auth = __esm({
2427
+ "src/lib/daemon-auth.ts"() {
2428
+ "use strict";
2429
+ init_config();
2430
+ init_secure_files();
2431
+ DAEMON_TOKEN_PATH = path14.join(EXE_AI_DIR, "exed.token");
2432
+ }
2433
+ });
2434
+
2435
+ // src/lib/exe-daemon-client.ts
2436
+ import net from "net";
2437
+ import os9 from "os";
2438
+ import { spawn } from "child_process";
2439
+ import { randomUUID as randomUUID2 } from "crypto";
2440
+ import { existsSync as existsSync13, unlinkSync as unlinkSync4, readFileSync as readFileSync11, openSync, closeSync, statSync } from "fs";
2441
+ import path15 from "path";
2442
+ import { fileURLToPath } from "url";
2443
+ function handleData(chunk) {
2444
+ _buffer += chunk.toString();
2445
+ if (_buffer.length > MAX_BUFFER) {
2446
+ _buffer = "";
2447
+ return;
2448
+ }
2449
+ let newlineIdx;
2450
+ while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
2451
+ const line = _buffer.slice(0, newlineIdx).trim();
2452
+ _buffer = _buffer.slice(newlineIdx + 1);
2453
+ if (!line) continue;
2454
+ try {
2455
+ const response = JSON.parse(line);
2456
+ const id = response.id;
2457
+ if (!id) continue;
2458
+ const entry = _pending.get(id);
2459
+ if (entry) {
2460
+ clearTimeout(entry.timer);
2461
+ _pending.delete(id);
2462
+ entry.resolve(response);
2463
+ }
2464
+ } catch {
2465
+ }
2466
+ }
2467
+ }
2468
+ function cleanupStaleFiles() {
2469
+ if (existsSync13(PID_PATH)) {
2470
+ try {
2471
+ const pid = parseInt(readFileSync11(PID_PATH, "utf8").trim(), 10);
2472
+ if (pid > 0) {
2473
+ try {
2474
+ process.kill(pid, 0);
2475
+ return;
2476
+ } catch {
2477
+ }
2478
+ }
2479
+ } catch {
2480
+ }
2481
+ try {
2482
+ unlinkSync4(PID_PATH);
2483
+ } catch {
2484
+ }
2485
+ try {
2486
+ unlinkSync4(SOCKET_PATH);
2487
+ } catch {
2488
+ }
2489
+ }
2490
+ }
2491
+ function findPackageRoot() {
2492
+ let dir = path15.dirname(fileURLToPath(import.meta.url));
2493
+ const { root } = path15.parse(dir);
2494
+ while (dir !== root) {
2495
+ if (existsSync13(path15.join(dir, "package.json"))) return dir;
2496
+ dir = path15.dirname(dir);
2497
+ }
2498
+ return null;
2499
+ }
2500
+ function getAvailableMemoryGB() {
2501
+ if (process.platform === "darwin") {
2502
+ try {
2503
+ const { execSync: execSync7 } = __require("child_process");
2504
+ const vmstat = execSync7("vm_stat", { encoding: "utf8" });
2505
+ const pageSize = 16384;
2506
+ const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
2507
+ const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
2508
+ const free = vmstat.match(/Pages free:\s+(\d+)/);
2509
+ const inactive = vmstat.match(/Pages inactive:\s+(\d+)/);
2510
+ const speculative = vmstat.match(/Pages speculative:\s+(\d+)/);
2511
+ const freePages = free ? parseInt(free[1], 10) : 0;
2512
+ const inactivePages = inactive ? parseInt(inactive[1], 10) : 0;
2513
+ const speculativePages = speculative ? parseInt(speculative[1], 10) : 0;
2514
+ return (freePages + inactivePages + speculativePages) * actualPageSize / (1024 * 1024 * 1024);
2515
+ } catch {
2516
+ return os9.freemem() / (1024 * 1024 * 1024);
2517
+ }
2518
+ }
2519
+ return os9.freemem() / (1024 * 1024 * 1024);
2520
+ }
2521
+ function spawnDaemon() {
2522
+ const freeGB = getAvailableMemoryGB();
2523
+ const totalGB = os9.totalmem() / (1024 * 1024 * 1024);
2524
+ if (totalGB <= 8) {
2525
+ process.stderr.write(
2526
+ `[exed-client] SKIP: ${totalGB.toFixed(0)}GB system \u2014 embedding daemon disabled. Using keyword search only. Minimum 16GB recommended for vector search.
2527
+ `
2528
+ );
2529
+ return;
2530
+ }
2531
+ if (totalGB <= 16 && freeGB < 2) {
2532
+ process.stderr.write(
2533
+ `[exed-client] SKIP: low memory (${freeGB.toFixed(1)}GB available / ${totalGB.toFixed(0)}GB total). Embedding daemon not started \u2014 using keyword search only.
2534
+ `
2535
+ );
2536
+ return;
2537
+ }
2538
+ const pkgRoot = findPackageRoot();
2539
+ if (!pkgRoot) {
2540
+ process.stderr.write("[exed-client] WARN: cannot find package root\n");
2541
+ return;
2542
+ }
2543
+ const daemonPath = path15.join(pkgRoot, "dist", "lib", "exe-daemon.js");
2544
+ if (!existsSync13(daemonPath)) {
2545
+ process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
2546
+ `);
2547
+ return;
2548
+ }
2549
+ const resolvedPath = daemonPath;
2550
+ const daemonToken = ensureDaemonToken(process.env[DAEMON_TOKEN_ENV] ?? null);
2551
+ process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
2552
+ `);
2553
+ const logPath = path15.join(path15.dirname(SOCKET_PATH), "exed.log");
2554
+ let stderrFd = "ignore";
2555
+ try {
2556
+ stderrFd = openSync(logPath, "a");
2557
+ } catch {
2558
+ }
2559
+ const heapCapMB = totalGB <= 8 ? 256 : 512;
2560
+ const nodeArgs = [`--max-old-space-size=${heapCapMB}`, resolvedPath];
2561
+ const child = spawn(process.execPath, nodeArgs, {
2562
+ detached: true,
2563
+ stdio: ["ignore", "ignore", stderrFd],
2564
+ env: {
2565
+ ...process.env,
2566
+ TMUX: void 0,
2567
+ // Daemon is global — must not inherit session scope
2568
+ TMUX_PANE: void 0,
2569
+ // Prevents resolveExeSession() from scoping to one session
2570
+ EXE_DAEMON_SOCK: SOCKET_PATH,
2571
+ EXE_DAEMON_PID: PID_PATH,
2572
+ [DAEMON_TOKEN_ENV]: daemonToken
2573
+ }
2574
+ });
2575
+ child.unref();
2576
+ if (typeof stderrFd === "number") {
2577
+ try {
2578
+ closeSync(stderrFd);
2579
+ } catch {
2580
+ }
2581
+ }
2582
+ }
2583
+ function acquireSpawnLock() {
2584
+ try {
2585
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
2586
+ closeSync(fd);
2587
+ return true;
2588
+ } catch {
2589
+ try {
2590
+ const stat = statSync(SPAWN_LOCK_PATH);
2591
+ if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
2592
+ try {
2593
+ unlinkSync4(SPAWN_LOCK_PATH);
2594
+ } catch {
2595
+ }
2596
+ try {
2597
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
2598
+ closeSync(fd);
2599
+ return true;
2600
+ } catch {
2601
+ }
2602
+ }
2603
+ } catch {
2604
+ }
2605
+ return false;
2606
+ }
2607
+ }
2608
+ function releaseSpawnLock() {
2609
+ try {
2610
+ unlinkSync4(SPAWN_LOCK_PATH);
2611
+ } catch {
2612
+ }
2613
+ }
2614
+ function connectToSocket() {
2615
+ return new Promise((resolve) => {
2616
+ if (_socket && _connected) {
2617
+ resolve(true);
2618
+ return;
2619
+ }
2620
+ const socket = net.createConnection({ path: SOCKET_PATH });
2621
+ const connectTimeout = setTimeout(() => {
2622
+ socket.destroy();
2623
+ resolve(false);
2624
+ }, 2e3);
2625
+ socket.on("connect", () => {
2626
+ clearTimeout(connectTimeout);
2627
+ _socket = socket;
2628
+ _connected = true;
2629
+ _buffer = "";
2630
+ socket.on("data", handleData);
2631
+ socket.on("close", () => {
2632
+ _connected = false;
2633
+ _socket = null;
2634
+ for (const [id, entry] of _pending) {
2635
+ clearTimeout(entry.timer);
2636
+ _pending.delete(id);
2637
+ entry.resolve({ error: "Connection closed" });
2638
+ }
2639
+ });
2640
+ socket.on("error", () => {
2641
+ _connected = false;
2642
+ _socket = null;
2643
+ });
2644
+ resolve(true);
2645
+ });
2646
+ socket.on("error", () => {
2647
+ clearTimeout(connectTimeout);
2648
+ resolve(false);
2649
+ });
2650
+ });
2651
+ }
2652
+ async function connectEmbedDaemon() {
2653
+ if (_socket && _connected) return true;
2654
+ if (await connectToSocket()) return true;
2655
+ if (acquireSpawnLock()) {
2656
+ try {
2657
+ cleanupStaleFiles();
2658
+ spawnDaemon();
2659
+ } finally {
2660
+ releaseSpawnLock();
2661
+ }
2662
+ }
2663
+ const start = Date.now();
2664
+ let delay = 100;
2665
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2666
+ await new Promise((r) => setTimeout(r, delay));
2667
+ if (await connectToSocket()) return true;
2668
+ delay = Math.min(delay * 2, 3e3);
2669
+ }
2670
+ return false;
2671
+ }
2672
+ function sendRequest(texts, priority) {
2673
+ return sendDaemonRequest({ texts, priority });
2674
+ }
2675
+ function sendDaemonRequest(payload, timeoutMs = REQUEST_TIMEOUT_MS) {
2676
+ return new Promise((resolve) => {
2677
+ if (!_socket || !_connected) {
2678
+ resolve({ error: "Not connected" });
2679
+ return;
2680
+ }
2681
+ const id = randomUUID2();
2682
+ const token = process.env[DAEMON_TOKEN_ENV] ?? readDaemonToken();
2683
+ const timer = setTimeout(() => {
2684
+ _pending.delete(id);
2685
+ resolve({ error: "Request timeout" });
2686
+ }, timeoutMs);
2687
+ _pending.set(id, { resolve, timer });
2688
+ try {
2689
+ _socket.write(JSON.stringify({ id, token, ...payload }) + "\n");
2690
+ } catch {
2691
+ clearTimeout(timer);
2692
+ _pending.delete(id);
2693
+ resolve({ error: "Write failed" });
2694
+ }
2695
+ });
2696
+ }
2697
+ async function pingDaemon() {
2698
+ if (!_socket || !_connected) return null;
2699
+ const response = await sendDaemonRequest({ type: "health" }, 5e3);
2700
+ if (response.health) {
2701
+ return response.health;
2702
+ }
2703
+ return null;
2704
+ }
2705
+ function killAndRespawnDaemon() {
2706
+ if (!acquireSpawnLock()) {
2707
+ process.stderr.write("[exed-client] Another process is already restarting daemon \u2014 skipping\n");
2708
+ if (_socket) {
2709
+ _socket.destroy();
2710
+ _socket = null;
2711
+ }
2712
+ _connected = false;
2713
+ _buffer = "";
2714
+ return;
2715
+ }
2716
+ try {
2717
+ process.stderr.write("[exed-client] Killing daemon for restart...\n");
2718
+ if (existsSync13(PID_PATH)) {
2719
+ try {
2720
+ const pid = parseInt(readFileSync11(PID_PATH, "utf8").trim(), 10);
2721
+ if (pid > 0) {
2722
+ try {
2723
+ process.kill(pid, "SIGKILL");
2724
+ } catch {
2725
+ }
2726
+ }
2727
+ } catch {
2728
+ }
2729
+ }
2730
+ if (_socket) {
2731
+ _socket.destroy();
2732
+ _socket = null;
2733
+ }
2734
+ _connected = false;
2735
+ _buffer = "";
2736
+ try {
2737
+ unlinkSync4(PID_PATH);
2738
+ } catch {
2739
+ }
2740
+ try {
2741
+ unlinkSync4(SOCKET_PATH);
2742
+ } catch {
2743
+ }
2744
+ spawnDaemon();
2745
+ } finally {
2746
+ releaseSpawnLock();
2747
+ }
2748
+ }
2749
+ function isDaemonTooYoung() {
2750
+ try {
2751
+ const stat = statSync(PID_PATH);
2752
+ return Date.now() - stat.mtimeMs < MIN_DAEMON_AGE_MS;
2753
+ } catch {
2754
+ return false;
2755
+ }
2756
+ }
2757
+ async function retryThenRestart(doRequest, label) {
2758
+ const result = await doRequest();
2759
+ if (!result.error) {
2760
+ _consecutiveFailures = 0;
2761
+ return result;
2762
+ }
2763
+ _consecutiveFailures++;
2764
+ for (let i = 0; i < MAX_RETRIES_BEFORE_RESTART; i++) {
2765
+ const delayMs = RETRY_DELAYS_MS[i] ?? 5e3;
2766
+ process.stderr.write(`[exed-client] ${label} failed (${result.error}), retry ${i + 1}/${MAX_RETRIES_BEFORE_RESTART} in ${delayMs}ms
2767
+ `);
2768
+ await new Promise((r) => setTimeout(r, delayMs));
2769
+ if (!_connected) {
2770
+ if (!await connectToSocket()) continue;
2771
+ }
2772
+ const retry = await doRequest();
2773
+ if (!retry.error) {
2774
+ _consecutiveFailures = 0;
2775
+ return retry;
2776
+ }
2777
+ _consecutiveFailures++;
2778
+ }
2779
+ if (isDaemonTooYoung()) {
2780
+ process.stderr.write(`[exed-client] ${label}: daemon too young (< ${MIN_DAEMON_AGE_MS / 1e3}s) \u2014 skipping restart
2781
+ `);
2782
+ return { error: result.error };
2783
+ }
2784
+ process.stderr.write(`[exed-client] ${label}: ${_consecutiveFailures} consecutive failures \u2014 restarting daemon
2785
+ `);
2786
+ killAndRespawnDaemon();
2787
+ const start = Date.now();
2788
+ let delay = 200;
2789
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2790
+ await new Promise((r) => setTimeout(r, delay));
2791
+ if (await connectToSocket()) break;
2792
+ delay = Math.min(delay * 2, 3e3);
2793
+ }
2794
+ if (!_connected) return { error: "Daemon restart failed" };
2795
+ const final = await doRequest();
2796
+ if (!final.error) _consecutiveFailures = 0;
2797
+ return final;
2798
+ }
2799
+ async function embedViaClient(text, priority = "high") {
2800
+ if (!_connected && !await connectEmbedDaemon()) return null;
2801
+ _requestCount++;
2802
+ if (_requestCount % HEALTH_CHECK_INTERVAL === 0) {
2803
+ const health = await pingDaemon();
2804
+ if (!health && !isDaemonTooYoung()) {
2805
+ process.stderr.write(`[exed-client] Periodic health check failed at request ${_requestCount} \u2014 restarting daemon
2806
+ `);
2807
+ killAndRespawnDaemon();
2808
+ const start = Date.now();
2809
+ let d = 200;
2810
+ while (Date.now() - start < CONNECT_TIMEOUT_MS) {
2811
+ await new Promise((r) => setTimeout(r, d));
2812
+ if (await connectToSocket()) break;
2813
+ d = Math.min(d * 2, 3e3);
2814
+ }
2815
+ if (!_connected) return null;
2816
+ }
2817
+ }
2818
+ const result = await retryThenRestart(
2819
+ () => sendRequest([text], priority),
2820
+ "Embed"
2821
+ );
2822
+ return !result.error && result.vectors?.[0] ? result.vectors[0] : null;
2823
+ }
2824
+ function disconnectClient() {
2825
+ if (_socket) {
2826
+ _socket.destroy();
2827
+ _socket = null;
2828
+ }
2829
+ _connected = false;
2830
+ _buffer = "";
2831
+ for (const [id, entry] of _pending) {
2832
+ clearTimeout(entry.timer);
2833
+ _pending.delete(id);
2834
+ entry.resolve({ error: "Client disconnected" });
2835
+ }
2836
+ }
2837
+ 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;
2838
+ var init_exe_daemon_client = __esm({
2839
+ "src/lib/exe-daemon-client.ts"() {
2840
+ "use strict";
2841
+ init_config();
2842
+ init_daemon_auth();
2843
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path15.join(EXE_AI_DIR, "exed.sock");
2844
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path15.join(EXE_AI_DIR, "exed.pid");
2845
+ SPAWN_LOCK_PATH = path15.join(EXE_AI_DIR, "exed-spawn.lock");
2846
+ SPAWN_LOCK_STALE_MS = 3e4;
2847
+ CONNECT_TIMEOUT_MS = 15e3;
2848
+ REQUEST_TIMEOUT_MS = 3e4;
2849
+ DAEMON_TOKEN_ENV = "EXE_DAEMON_TOKEN";
2850
+ _socket = null;
2851
+ _connected = false;
2852
+ _buffer = "";
2853
+ _requestCount = 0;
2854
+ _consecutiveFailures = 0;
2855
+ HEALTH_CHECK_INTERVAL = 100;
2856
+ MAX_RETRIES_BEFORE_RESTART = 3;
2857
+ RETRY_DELAYS_MS = [1e3, 3e3, 5e3];
2858
+ MIN_DAEMON_AGE_MS = 3e4;
2859
+ _pending = /* @__PURE__ */ new Map();
2860
+ MAX_BUFFER = 1e7;
2861
+ }
2862
+ });
2863
+
2864
+ // src/lib/embedder.ts
2865
+ var embedder_exports = {};
2866
+ __export(embedder_exports, {
2867
+ disposeEmbedder: () => disposeEmbedder,
2868
+ embed: () => embed,
2869
+ embedDirect: () => embedDirect,
2870
+ getEmbedder: () => getEmbedder
2871
+ });
2872
+ async function getEmbedder() {
2873
+ const ok = await connectEmbedDaemon();
2874
+ if (!ok) {
2875
+ throw new Error(
2876
+ "Could not connect to embedding daemon. Ensure the model is installed (run /exe-setup)."
2877
+ );
2878
+ }
2879
+ }
2880
+ async function embed(text) {
2881
+ const priority = process.env.EXE_EMBED_PRIORITY === "low" ? "low" : "high";
2882
+ const vector = await embedViaClient(text, priority);
2883
+ if (!vector) {
2884
+ throw new Error(
2885
+ "Embedding failed: daemon unavailable. Run /exe-setup to verify model installation."
2886
+ );
2887
+ }
2888
+ if (vector.length !== EMBEDDING_DIM) {
2889
+ throw new Error(
2890
+ `Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}. Ensure the correct Jina v5-small Q4_K_M GGUF model is installed.`
2891
+ );
2892
+ }
2893
+ return vector;
2894
+ }
2895
+ async function disposeEmbedder() {
2896
+ disconnectClient();
2897
+ }
2898
+ async function embedDirect(text) {
2899
+ const llamaCpp = await import("node-llama-cpp");
2900
+ const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2901
+ const { existsSync: existsSync15 } = await import("fs");
2902
+ const path18 = await import("path");
2903
+ const modelPath = path18.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
2904
+ if (!existsSync15(modelPath)) {
2905
+ throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
2906
+ }
2907
+ const llama = await llamaCpp.getLlama();
2908
+ const model = await llama.loadModel({ modelPath });
2909
+ const context = await model.createEmbeddingContext();
2910
+ try {
2911
+ const embedding = await context.getEmbeddingFor(text);
2912
+ const vector = Array.from(embedding.vector);
2913
+ if (vector.length !== EMBEDDING_DIM) {
2914
+ throw new Error(
2915
+ `Embedding dimension mismatch: expected ${EMBEDDING_DIM}, got ${vector.length}.`
2916
+ );
2917
+ }
2918
+ return vector;
2919
+ } finally {
2920
+ await context.dispose();
2921
+ await model.dispose();
2922
+ }
2923
+ }
2924
+ var init_embedder = __esm({
2925
+ "src/lib/embedder.ts"() {
2926
+ "use strict";
2927
+ init_memory();
2928
+ init_exe_daemon_client();
2929
+ }
2930
+ });
2931
+
2932
+ // src/lib/behaviors.ts
2933
+ import crypto5 from "crypto";
2303
2934
  async function storeBehavior(opts) {
2304
2935
  const client = getClient();
2305
- const id = crypto4.randomUUID();
2936
+ const id = crypto5.randomUUID();
2306
2937
  const now = (/* @__PURE__ */ new Date()).toISOString();
2938
+ let vector = null;
2939
+ try {
2940
+ const { embed: embed2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
2941
+ const vec = await embed2(opts.content);
2942
+ vector = new Float32Array(vec);
2943
+ } catch {
2944
+ }
2307
2945
  await client.execute({
2308
- sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at)
2309
- VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?)`,
2310
- args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now]
2946
+ sql: `INSERT INTO behaviors (id, agent_id, project_name, domain, priority, content, active, created_at, updated_at, vector)
2947
+ VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, ?)`,
2948
+ args: [id, opts.agentId, opts.projectName ?? null, opts.domain ?? null, opts.priority ?? "p1", opts.content, now, now, vector ? vector.buffer : null]
2311
2949
  });
2312
2950
  return id;
2313
2951
  }
@@ -2331,7 +2969,7 @@ __export(skill_learning_exports, {
2331
2969
  storeTrajectory: () => storeTrajectory,
2332
2970
  sweepTrajectories: () => sweepTrajectories
2333
2971
  });
2334
- import crypto5 from "crypto";
2972
+ import crypto6 from "crypto";
2335
2973
  async function extractTrajectory(taskId, agentId) {
2336
2974
  const client = getClient();
2337
2975
  const result = await client.execute({
@@ -2360,11 +2998,11 @@ async function extractTrajectory(taskId, agentId) {
2360
2998
  return signature;
2361
2999
  }
2362
3000
  function hashSignature(signature) {
2363
- return crypto5.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
3001
+ return crypto6.createHash("sha256").update(signature.join("|")).digest("hex").slice(0, 16);
2364
3002
  }
2365
3003
  async function storeTrajectory(opts) {
2366
3004
  const client = getClient();
2367
- const id = crypto5.randomUUID();
3005
+ const id = crypto6.randomUUID();
2368
3006
  const now = (/* @__PURE__ */ new Date()).toISOString();
2369
3007
  const signatureHash = hashSignature(opts.signature);
2370
3008
  await client.execute({
@@ -2629,8 +3267,8 @@ __export(tasks_exports, {
2629
3267
  updateTaskStatus: () => updateTaskStatus,
2630
3268
  writeCheckpoint: () => writeCheckpoint
2631
3269
  });
2632
- import path14 from "path";
2633
- import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
3270
+ import path16 from "path";
3271
+ import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
2634
3272
  async function createTask(input) {
2635
3273
  const result = await createTaskCore(input);
2636
3274
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -2649,14 +3287,14 @@ async function updateTask(input) {
2649
3287
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
2650
3288
  try {
2651
3289
  const agent = String(row.assigned_to);
2652
- const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
2653
- const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
3290
+ const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
3291
+ const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
2654
3292
  if (input.status === "in_progress") {
2655
3293
  mkdirSync5(cacheDir, { recursive: true });
2656
- writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
3294
+ writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
2657
3295
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled" || input.status === "closed") {
2658
3296
  try {
2659
- unlinkSync4(cachePath);
3297
+ unlinkSync5(cachePath);
2660
3298
  } catch {
2661
3299
  }
2662
3300
  }
@@ -3098,7 +3736,7 @@ var init_capacity_monitor = __esm({
3098
3736
  // src/lib/tmux-routing.ts
3099
3737
  var tmux_routing_exports = {};
3100
3738
  __export(tmux_routing_exports, {
3101
- acquireSpawnLock: () => acquireSpawnLock,
3739
+ acquireSpawnLock: () => acquireSpawnLock2,
3102
3740
  employeeSessionName: () => employeeSessionName,
3103
3741
  ensureEmployee: () => ensureEmployee,
3104
3742
  extractRootExe: () => extractRootExe,
@@ -3114,20 +3752,20 @@ __export(tmux_routing_exports, {
3114
3752
  notifyParentExe: () => notifyParentExe,
3115
3753
  parseParentExe: () => parseParentExe,
3116
3754
  registerParentExe: () => registerParentExe,
3117
- releaseSpawnLock: () => releaseSpawnLock,
3755
+ releaseSpawnLock: () => releaseSpawnLock2,
3118
3756
  resolveExeSession: () => resolveExeSession,
3119
3757
  sendIntercom: () => sendIntercom,
3120
3758
  spawnEmployee: () => spawnEmployee,
3121
3759
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
3122
3760
  });
3123
3761
  import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
3124
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync, readdirSync as readdirSync3 } from "fs";
3125
- import path15 from "path";
3126
- import os9 from "os";
3127
- import { fileURLToPath } from "url";
3128
- import { unlinkSync as unlinkSync5 } from "fs";
3762
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, existsSync as existsSync14, appendFileSync, readdirSync as readdirSync3 } from "fs";
3763
+ import path17 from "path";
3764
+ import os10 from "os";
3765
+ import { fileURLToPath as fileURLToPath2 } from "url";
3766
+ import { unlinkSync as unlinkSync6 } from "fs";
3129
3767
  function spawnLockPath(sessionName) {
3130
- return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
3768
+ return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
3131
3769
  }
3132
3770
  function isProcessAlive(pid) {
3133
3771
  try {
@@ -3137,14 +3775,14 @@ function isProcessAlive(pid) {
3137
3775
  return false;
3138
3776
  }
3139
3777
  }
3140
- function acquireSpawnLock(sessionName) {
3141
- if (!existsSync12(SPAWN_LOCK_DIR)) {
3778
+ function acquireSpawnLock2(sessionName) {
3779
+ if (!existsSync14(SPAWN_LOCK_DIR)) {
3142
3780
  mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
3143
3781
  }
3144
3782
  const lockFile = spawnLockPath(sessionName);
3145
- if (existsSync12(lockFile)) {
3783
+ if (existsSync14(lockFile)) {
3146
3784
  try {
3147
- const lock = JSON.parse(readFileSync10(lockFile, "utf8"));
3785
+ const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
3148
3786
  const age = Date.now() - lock.timestamp;
3149
3787
  if (isProcessAlive(lock.pid) && age < 6e4) {
3150
3788
  return false;
@@ -3152,25 +3790,25 @@ function acquireSpawnLock(sessionName) {
3152
3790
  } catch {
3153
3791
  }
3154
3792
  }
3155
- writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
3793
+ writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
3156
3794
  return true;
3157
3795
  }
3158
- function releaseSpawnLock(sessionName) {
3796
+ function releaseSpawnLock2(sessionName) {
3159
3797
  try {
3160
- unlinkSync5(spawnLockPath(sessionName));
3798
+ unlinkSync6(spawnLockPath(sessionName));
3161
3799
  } catch {
3162
3800
  }
3163
3801
  }
3164
3802
  function resolveBehaviorsExporterScript() {
3165
3803
  try {
3166
- const thisFile = fileURLToPath(import.meta.url);
3167
- const scriptPath = path15.join(
3168
- path15.dirname(thisFile),
3804
+ const thisFile = fileURLToPath2(import.meta.url);
3805
+ const scriptPath = path17.join(
3806
+ path17.dirname(thisFile),
3169
3807
  "..",
3170
3808
  "bin",
3171
3809
  "exe-export-behaviors.js"
3172
3810
  );
3173
- return existsSync12(scriptPath) ? scriptPath : null;
3811
+ return existsSync14(scriptPath) ? scriptPath : null;
3174
3812
  } catch {
3175
3813
  return null;
3176
3814
  }
@@ -3236,12 +3874,12 @@ function extractRootExe(name) {
3236
3874
  return parts.length > 0 ? parts[parts.length - 1] : null;
3237
3875
  }
3238
3876
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
3239
- if (!existsSync12(SESSION_CACHE)) {
3877
+ if (!existsSync14(SESSION_CACHE)) {
3240
3878
  mkdirSync6(SESSION_CACHE, { recursive: true });
3241
3879
  }
3242
3880
  const rootExe = extractRootExe(parentExe) ?? parentExe;
3243
- const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
3244
- writeFileSync7(filePath, JSON.stringify({
3881
+ const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
3882
+ writeFileSync8(filePath, JSON.stringify({
3245
3883
  parentExe: rootExe,
3246
3884
  dispatchedBy: dispatchedBy || rootExe,
3247
3885
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -3249,7 +3887,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
3249
3887
  }
3250
3888
  function getParentExe(sessionKey) {
3251
3889
  try {
3252
- const data = JSON.parse(readFileSync10(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
3890
+ const data = JSON.parse(readFileSync12(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
3253
3891
  return data.parentExe || null;
3254
3892
  } catch {
3255
3893
  return null;
@@ -3257,8 +3895,8 @@ function getParentExe(sessionKey) {
3257
3895
  }
3258
3896
  function getDispatchedBy(sessionKey) {
3259
3897
  try {
3260
- const data = JSON.parse(readFileSync10(
3261
- path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
3898
+ const data = JSON.parse(readFileSync12(
3899
+ path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
3262
3900
  "utf8"
3263
3901
  ));
3264
3902
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -3293,10 +3931,10 @@ function isEmployeeAlive(sessionName) {
3293
3931
  }
3294
3932
  function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive = isEmployeeAlive) {
3295
3933
  const base = employeeSessionName(employeeName, exeSession);
3296
- if (!isAlive(base) && acquireSpawnLock(base)) return 0;
3934
+ if (!isAlive(base) && acquireSpawnLock2(base)) return 0;
3297
3935
  for (let i = 2; i <= maxInstances; i++) {
3298
3936
  const candidate = employeeSessionName(employeeName, exeSession, i);
3299
- if (!isAlive(candidate) && acquireSpawnLock(candidate)) return i;
3937
+ if (!isAlive(candidate) && acquireSpawnLock2(candidate)) return i;
3300
3938
  }
3301
3939
  return null;
3302
3940
  }
@@ -3328,8 +3966,8 @@ async function verifyPaneAtCapacity(sessionName) {
3328
3966
  }
3329
3967
  function readDebounceState() {
3330
3968
  try {
3331
- if (!existsSync12(DEBOUNCE_FILE)) return {};
3332
- const raw = JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
3969
+ if (!existsSync14(DEBOUNCE_FILE)) return {};
3970
+ const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
3333
3971
  const state = {};
3334
3972
  for (const [key, val] of Object.entries(raw)) {
3335
3973
  if (typeof val === "number") {
@@ -3345,8 +3983,8 @@ function readDebounceState() {
3345
3983
  }
3346
3984
  function writeDebounceState(state) {
3347
3985
  try {
3348
- if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
3349
- writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
3986
+ if (!existsSync14(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
3987
+ writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
3350
3988
  } catch {
3351
3989
  }
3352
3990
  }
@@ -3410,7 +4048,7 @@ function isSessionBusy(sessionName) {
3410
4048
  function isExeSession(sessionName) {
3411
4049
  const matchesBaseWithInstance = (baseName) => sessionName === baseName || sessionName.startsWith(baseName) && /^\d+$/.test(sessionName.slice(baseName.length));
3412
4050
  const coordinatorName = getCoordinatorName();
3413
- return matchesBaseWithInstance(coordinatorName) || matchesBaseWithInstance("exe");
4051
+ return matchesBaseWithInstance(coordinatorName);
3414
4052
  }
3415
4053
  function sendIntercom(targetSession) {
3416
4054
  const transport = getTransport();
@@ -3445,8 +4083,8 @@ function sendIntercom(targetSession) {
3445
4083
  try {
3446
4084
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
3447
4085
  const agent = baseAgentName(rawAgent);
3448
- const markerPath = path15.join(SESSION_CACHE, `current-task-${agent}.json`);
3449
- if (existsSync12(markerPath)) {
4086
+ const markerPath = path17.join(SESSION_CACHE, `current-task-${agent}.json`);
4087
+ if (existsSync14(markerPath)) {
3450
4088
  logIntercom(`SKIP \u2192 ${targetSession} (has in_progress task marker + not idle \u2014 will auto-chain)`);
3451
4089
  return "debounced";
3452
4090
  }
@@ -3456,8 +4094,8 @@ function sendIntercom(targetSession) {
3456
4094
  try {
3457
4095
  const rawAgent = targetSession.split("-")[0] ?? targetSession;
3458
4096
  const agent = baseAgentName(rawAgent);
3459
- const taskDir = path15.join(process.cwd(), "exe", agent);
3460
- if (existsSync12(taskDir)) {
4097
+ const taskDir = path17.join(process.cwd(), "exe", agent);
4098
+ if (existsSync14(taskDir)) {
3461
4099
  const files = readdirSync3(taskDir).filter(
3462
4100
  (f) => f.endsWith(".md") && f !== "DONE.txt"
3463
4101
  );
@@ -3624,26 +4262,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3624
4262
  const transport = getTransport();
3625
4263
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
3626
4264
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
3627
- const logDir = path15.join(os9.homedir(), ".exe-os", "session-logs");
3628
- const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
3629
- if (!existsSync12(logDir)) {
4265
+ const logDir = path17.join(os10.homedir(), ".exe-os", "session-logs");
4266
+ const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
4267
+ if (!existsSync14(logDir)) {
3630
4268
  mkdirSync6(logDir, { recursive: true });
3631
4269
  }
3632
4270
  transport.kill(sessionName);
3633
4271
  let cleanupSuffix = "";
3634
4272
  try {
3635
- const thisFile = fileURLToPath(import.meta.url);
3636
- const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
3637
- if (existsSync12(cleanupScript)) {
4273
+ const thisFile = fileURLToPath2(import.meta.url);
4274
+ const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
4275
+ if (existsSync14(cleanupScript)) {
3638
4276
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
3639
4277
  }
3640
4278
  } catch {
3641
4279
  }
3642
4280
  try {
3643
- const claudeJsonPath = path15.join(os9.homedir(), ".claude.json");
4281
+ const claudeJsonPath = path17.join(os10.homedir(), ".claude.json");
3644
4282
  let claudeJson = {};
3645
4283
  try {
3646
- claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
4284
+ claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
3647
4285
  } catch {
3648
4286
  }
3649
4287
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -3651,17 +4289,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3651
4289
  const trustDir = opts?.cwd ?? projectDir;
3652
4290
  if (!projects[trustDir]) projects[trustDir] = {};
3653
4291
  projects[trustDir].hasTrustDialogAccepted = true;
3654
- writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
4292
+ writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
3655
4293
  } catch {
3656
4294
  }
3657
4295
  try {
3658
- const settingsDir = path15.join(os9.homedir(), ".claude", "projects");
4296
+ const settingsDir = path17.join(os10.homedir(), ".claude", "projects");
3659
4297
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
3660
- const projSettingsDir = path15.join(settingsDir, normalizedKey);
3661
- const settingsPath = path15.join(projSettingsDir, "settings.json");
4298
+ const projSettingsDir = path17.join(settingsDir, normalizedKey);
4299
+ const settingsPath = path17.join(projSettingsDir, "settings.json");
3662
4300
  let settings = {};
3663
4301
  try {
3664
- settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
4302
+ settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
3665
4303
  } catch {
3666
4304
  }
3667
4305
  const perms = settings.permissions ?? {};
@@ -3690,7 +4328,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3690
4328
  perms.allow = allow;
3691
4329
  settings.permissions = perms;
3692
4330
  mkdirSync6(projSettingsDir, { recursive: true });
3693
- writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
4331
+ writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3694
4332
  }
3695
4333
  } catch {
3696
4334
  }
@@ -3705,8 +4343,8 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3705
4343
  let behaviorsFlag = "";
3706
4344
  let legacyFallbackWarned = false;
3707
4345
  if (!useExeAgent && !useBinSymlink) {
3708
- const identityPath = path15.join(
3709
- os9.homedir(),
4346
+ const identityPath = path17.join(
4347
+ os10.homedir(),
3710
4348
  ".exe-os",
3711
4349
  "identity",
3712
4350
  `${employeeName}.md`
@@ -3715,13 +4353,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3715
4353
  const hasAgentFlag = claudeSupportsAgentFlag();
3716
4354
  if (hasAgentFlag) {
3717
4355
  identityFlag = ` --agent ${employeeName}`;
3718
- } else if (existsSync12(identityPath)) {
4356
+ } else if (existsSync14(identityPath)) {
3719
4357
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
3720
4358
  legacyFallbackWarned = true;
3721
4359
  }
3722
4360
  const behaviorsFile = exportBehaviorsSync(
3723
4361
  employeeName,
3724
- path15.basename(spawnCwd),
4362
+ path17.basename(spawnCwd),
3725
4363
  sessionName
3726
4364
  );
3727
4365
  if (behaviorsFile) {
@@ -3736,16 +4374,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3736
4374
  }
3737
4375
  let sessionContextFlag = "";
3738
4376
  try {
3739
- const ctxDir = path15.join(os9.homedir(), ".exe-os", "session-cache");
4377
+ const ctxDir = path17.join(os10.homedir(), ".exe-os", "session-cache");
3740
4378
  mkdirSync6(ctxDir, { recursive: true });
3741
- const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
4379
+ const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
3742
4380
  const ctxContent = [
3743
4381
  `## Session Context`,
3744
4382
  `You are running in tmux session: ${sessionName}.`,
3745
4383
  `Your parent coordinator session is ${exeSession}.`,
3746
4384
  `Your employees (if any) use the -${exeSession} suffix.`
3747
4385
  ].join("\n");
3748
- writeFileSync7(ctxFile, ctxContent);
4386
+ writeFileSync8(ctxFile, ctxContent);
3749
4387
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
3750
4388
  } catch {
3751
4389
  }
@@ -3819,14 +4457,14 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3819
4457
  command: spawnCommand
3820
4458
  });
3821
4459
  if (spawnResult.error) {
3822
- releaseSpawnLock(sessionName);
4460
+ releaseSpawnLock2(sessionName);
3823
4461
  return { sessionName, error: `tmux new-session failed: ${spawnResult.error}` };
3824
4462
  }
3825
4463
  transport.pipeLog(sessionName, logFile);
3826
4464
  try {
3827
4465
  const mySession = getMySession();
3828
- const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
3829
- writeFileSync7(dispatchInfo, JSON.stringify({
4466
+ const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
4467
+ writeFileSync8(dispatchInfo, JSON.stringify({
3830
4468
  dispatchedBy: mySession,
3831
4469
  rootExe: exeSession,
3832
4470
  provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
@@ -3864,7 +4502,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3864
4502
  }
3865
4503
  }
3866
4504
  if (!booted) {
3867
- releaseSpawnLock(sessionName);
4505
+ releaseSpawnLock2(sessionName);
3868
4506
  const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
3869
4507
  return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
3870
4508
  }
@@ -3882,7 +4520,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3882
4520
  pid: 0,
3883
4521
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
3884
4522
  });
3885
- releaseSpawnLock(sessionName);
4523
+ releaseSpawnLock2(sessionName);
3886
4524
  return { sessionName };
3887
4525
  }
3888
4526
  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;
@@ -3899,22 +4537,22 @@ var init_tmux_routing = __esm({
3899
4537
  init_intercom_queue();
3900
4538
  init_plan_limits();
3901
4539
  init_employees();
3902
- SPAWN_LOCK_DIR = path15.join(os9.homedir(), ".exe-os", "spawn-locks");
3903
- SESSION_CACHE = path15.join(os9.homedir(), ".exe-os", "session-cache");
4540
+ SPAWN_LOCK_DIR = path17.join(os10.homedir(), ".exe-os", "spawn-locks");
4541
+ SESSION_CACHE = path17.join(os10.homedir(), ".exe-os", "session-cache");
3904
4542
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
3905
4543
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
3906
4544
  VERIFY_PANE_LINES = 200;
3907
4545
  INTERCOM_DEBOUNCE_MS = 3e4;
3908
4546
  CODEX_DEBOUNCE_MS = 12e4;
3909
- INTERCOM_LOG2 = path15.join(os9.homedir(), ".exe-os", "intercom.log");
3910
- DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
4547
+ INTERCOM_LOG2 = path17.join(os10.homedir(), ".exe-os", "intercom.log");
4548
+ DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
3911
4549
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
3912
4550
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
3913
4551
  }
3914
4552
  });
3915
4553
  init_tmux_routing();
3916
4554
  export {
3917
- acquireSpawnLock,
4555
+ acquireSpawnLock2 as acquireSpawnLock,
3918
4556
  employeeSessionName,
3919
4557
  ensureEmployee,
3920
4558
  extractRootExe,
@@ -3930,7 +4568,7 @@ export {
3930
4568
  notifyParentExe,
3931
4569
  parseParentExe,
3932
4570
  registerParentExe,
3933
- releaseSpawnLock,
4571
+ releaseSpawnLock2 as releaseSpawnLock,
3934
4572
  resolveExeSession,
3935
4573
  sendIntercom,
3936
4574
  spawnEmployee,