@askexenow/exe-os 0.8.85 → 0.8.86

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 (57) hide show
  1. package/dist/bin/cleanup-stale-review-tasks.js +57 -19
  2. package/dist/bin/cli.js +507 -337
  3. package/dist/bin/exe-agent-config.js +242 -0
  4. package/dist/bin/exe-agent.js +3 -3
  5. package/dist/bin/exe-boot.js +344 -346
  6. package/dist/bin/exe-dispatch.js +375 -250
  7. package/dist/bin/exe-forget.js +5 -1
  8. package/dist/bin/exe-gateway.js +260 -135
  9. package/dist/bin/exe-healthcheck.js +133 -1
  10. package/dist/bin/exe-heartbeat.js +72 -31
  11. package/dist/bin/exe-link.js +25 -2
  12. package/dist/bin/exe-new-employee.js +22 -0
  13. package/dist/bin/exe-pending-messages.js +55 -17
  14. package/dist/bin/exe-pending-reviews.js +57 -19
  15. package/dist/bin/exe-search.js +6 -2
  16. package/dist/bin/exe-session-cleanup.js +260 -135
  17. package/dist/bin/exe-start-codex.js +2598 -0
  18. package/dist/bin/exe-start.sh +15 -3
  19. package/dist/bin/exe-status.js +57 -19
  20. package/dist/bin/git-sweep.js +391 -266
  21. package/dist/bin/install.js +22 -0
  22. package/dist/bin/scan-tasks.js +394 -269
  23. package/dist/bin/setup.js +47 -2
  24. package/dist/gateway/index.js +257 -132
  25. package/dist/hooks/bug-report-worker.js +242 -117
  26. package/dist/hooks/commit-complete.js +389 -264
  27. package/dist/hooks/error-recall.js +6 -2
  28. package/dist/hooks/ingest-worker.js +314 -193
  29. package/dist/hooks/post-compact.js +84 -46
  30. package/dist/hooks/pre-compact.js +272 -147
  31. package/dist/hooks/pre-tool-use.js +104 -66
  32. package/dist/hooks/prompt-submit.js +126 -66
  33. package/dist/hooks/session-end.js +277 -152
  34. package/dist/hooks/session-start.js +70 -28
  35. package/dist/hooks/stop.js +90 -52
  36. package/dist/hooks/subagent-stop.js +84 -46
  37. package/dist/hooks/summary-worker.js +175 -114
  38. package/dist/index.js +296 -171
  39. package/dist/lib/agent-config.js +167 -0
  40. package/dist/lib/cloud-sync.js +25 -2
  41. package/dist/lib/exe-daemon.js +338 -213
  42. package/dist/lib/hybrid-search.js +7 -2
  43. package/dist/lib/messaging.js +95 -39
  44. package/dist/lib/runtime-table.js +16 -0
  45. package/dist/lib/session-wrappers.js +22 -0
  46. package/dist/lib/tasks.js +242 -117
  47. package/dist/lib/tmux-routing.js +314 -189
  48. package/dist/mcp/server.js +573 -274
  49. package/dist/mcp/tools/create-task.js +260 -135
  50. package/dist/mcp/tools/list-tasks.js +68 -30
  51. package/dist/mcp/tools/send-message.js +100 -44
  52. package/dist/mcp/tools/update-task.js +123 -67
  53. package/dist/runtime/index.js +276 -151
  54. package/dist/tui/App.js +479 -354
  55. package/package.json +1 -1
  56. package/src/commands/exe/agent-config.md +27 -0
  57. package/src/commands/exe/cc-doctor.md +10 -0
@@ -644,6 +644,57 @@ var init_provider_table = __esm({
644
644
  }
645
645
  });
646
646
 
647
+ // src/lib/runtime-table.ts
648
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
649
+ var init_runtime_table = __esm({
650
+ "src/lib/runtime-table.ts"() {
651
+ "use strict";
652
+ RUNTIME_TABLE = {
653
+ codex: {
654
+ binary: "codex",
655
+ launchMode: "exec",
656
+ autoApproveFlag: "--full-auto",
657
+ inlineFlag: "--no-alt-screen",
658
+ apiKeyEnv: "OPENAI_API_KEY",
659
+ defaultModel: "gpt-5.4"
660
+ }
661
+ };
662
+ DEFAULT_RUNTIME = "claude";
663
+ }
664
+ });
665
+
666
+ // src/lib/agent-config.ts
667
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
668
+ import path3 from "path";
669
+ function loadAgentConfig() {
670
+ if (!existsSync3(AGENT_CONFIG_PATH)) return {};
671
+ try {
672
+ return JSON.parse(readFileSync3(AGENT_CONFIG_PATH, "utf-8"));
673
+ } catch {
674
+ return {};
675
+ }
676
+ }
677
+ function getAgentRuntime(agentId) {
678
+ const config = loadAgentConfig();
679
+ const entry = config[agentId];
680
+ if (entry) return entry;
681
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
682
+ }
683
+ var AGENT_CONFIG_PATH, DEFAULT_MODELS;
684
+ var init_agent_config = __esm({
685
+ "src/lib/agent-config.ts"() {
686
+ "use strict";
687
+ init_config();
688
+ init_runtime_table();
689
+ AGENT_CONFIG_PATH = path3.join(EXE_AI_DIR, "agent-config.json");
690
+ DEFAULT_MODELS = {
691
+ claude: "claude-opus-4",
692
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
693
+ opencode: "minimax-m2.7"
694
+ };
695
+ }
696
+ });
697
+
647
698
  // src/lib/intercom-queue.ts
648
699
  var intercom_queue_exports = {};
649
700
  __export(intercom_queue_exports, {
@@ -652,17 +703,17 @@ __export(intercom_queue_exports, {
652
703
  queueIntercom: () => queueIntercom,
653
704
  readQueue: () => readQueue
654
705
  });
655
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, renameSync as renameSync2, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
656
- import path3 from "path";
706
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync2, existsSync as existsSync4, mkdirSync as mkdirSync3 } from "fs";
707
+ import path4 from "path";
657
708
  import os3 from "os";
658
709
  function ensureDir() {
659
- const dir = path3.dirname(QUEUE_PATH);
660
- if (!existsSync3(dir)) mkdirSync2(dir, { recursive: true });
710
+ const dir = path4.dirname(QUEUE_PATH);
711
+ if (!existsSync4(dir)) mkdirSync3(dir, { recursive: true });
661
712
  }
662
713
  function readQueue() {
663
714
  try {
664
- if (!existsSync3(QUEUE_PATH)) return [];
665
- return JSON.parse(readFileSync3(QUEUE_PATH, "utf8"));
715
+ if (!existsSync4(QUEUE_PATH)) return [];
716
+ return JSON.parse(readFileSync4(QUEUE_PATH, "utf8"));
666
717
  } catch {
667
718
  return [];
668
719
  }
@@ -670,7 +721,7 @@ function readQueue() {
670
721
  function writeQueue(queue) {
671
722
  ensureDir();
672
723
  const tmp = `${QUEUE_PATH}.tmp`;
673
- writeFileSync2(tmp, JSON.stringify(queue, null, 2));
724
+ writeFileSync3(tmp, JSON.stringify(queue, null, 2));
674
725
  renameSync2(tmp, QUEUE_PATH);
675
726
  }
676
727
  function queueIntercom(targetSession, reason) {
@@ -753,10 +804,10 @@ var QUEUE_PATH, MAX_RETRIES, TTL_MS, INTERCOM_LOG;
753
804
  var init_intercom_queue = __esm({
754
805
  "src/lib/intercom-queue.ts"() {
755
806
  "use strict";
756
- QUEUE_PATH = path3.join(os3.homedir(), ".exe-os", "intercom-queue.json");
807
+ QUEUE_PATH = path4.join(os3.homedir(), ".exe-os", "intercom-queue.json");
757
808
  MAX_RETRIES = 5;
758
809
  TTL_MS = 60 * 60 * 1e3;
759
- INTERCOM_LOG = path3.join(os3.homedir(), ".exe-os", "intercom.log");
810
+ INTERCOM_LOG = path4.join(os3.homedir(), ".exe-os", "intercom.log");
760
811
  }
761
812
  });
762
813
 
@@ -842,9 +893,9 @@ __export(employees_exports, {
842
893
  validateEmployeeName: () => validateEmployeeName
843
894
  });
844
895
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
845
- import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync4, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
896
+ import { existsSync as existsSync5, symlinkSync, readlinkSync, readFileSync as readFileSync5, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync4 } from "fs";
846
897
  import { execSync as execSync4 } from "child_process";
847
- import path4 from "path";
898
+ import path5 from "path";
848
899
  import os4 from "os";
849
900
  function normalizeRole(role) {
850
901
  return (role ?? "").trim().toLowerCase();
@@ -881,7 +932,7 @@ function validateEmployeeName(name) {
881
932
  return { valid: true };
882
933
  }
883
934
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
884
- if (!existsSync4(employeesPath)) {
935
+ if (!existsSync5(employeesPath)) {
885
936
  return [];
886
937
  }
887
938
  const raw = await readFile2(employeesPath, "utf-8");
@@ -892,13 +943,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
892
943
  }
893
944
  }
894
945
  async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
895
- await mkdir2(path4.dirname(employeesPath), { recursive: true });
946
+ await mkdir2(path5.dirname(employeesPath), { recursive: true });
896
947
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
897
948
  }
898
949
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
899
- if (!existsSync4(employeesPath)) return [];
950
+ if (!existsSync5(employeesPath)) return [];
900
951
  try {
901
- return JSON.parse(readFileSync4(employeesPath, "utf-8"));
952
+ return JSON.parse(readFileSync5(employeesPath, "utf-8"));
902
953
  } catch {
903
954
  return [];
904
955
  }
@@ -949,14 +1000,14 @@ async function normalizeRosterCase(rosterPath) {
949
1000
  emp.name = emp.name.toLowerCase();
950
1001
  changed = true;
951
1002
  try {
952
- const identityDir = path4.join(os4.homedir(), ".exe-os", "identity");
953
- const oldPath = path4.join(identityDir, `${oldName}.md`);
954
- const newPath = path4.join(identityDir, `${emp.name}.md`);
955
- if (existsSync4(oldPath) && !existsSync4(newPath)) {
1003
+ const identityDir = path5.join(os4.homedir(), ".exe-os", "identity");
1004
+ const oldPath = path5.join(identityDir, `${oldName}.md`);
1005
+ const newPath = path5.join(identityDir, `${emp.name}.md`);
1006
+ if (existsSync5(oldPath) && !existsSync5(newPath)) {
956
1007
  renameSync3(oldPath, newPath);
957
- } else if (existsSync4(oldPath) && oldPath !== newPath) {
958
- const content = readFileSync4(oldPath, "utf-8");
959
- writeFileSync3(newPath, content, "utf-8");
1008
+ } else if (existsSync5(oldPath) && oldPath !== newPath) {
1009
+ const content = readFileSync5(oldPath, "utf-8");
1010
+ writeFileSync4(newPath, content, "utf-8");
960
1011
  if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
961
1012
  unlinkSync(oldPath);
962
1013
  }
@@ -986,7 +1037,7 @@ function registerBinSymlinks(name) {
986
1037
  errors.push("Could not find 'exe-os' in PATH");
987
1038
  return { created, skipped, errors };
988
1039
  }
989
- const binDir = path4.dirname(exeBinPath);
1040
+ const binDir = path5.dirname(exeBinPath);
990
1041
  let target;
991
1042
  try {
992
1043
  target = readlinkSync(exeBinPath);
@@ -996,8 +1047,8 @@ function registerBinSymlinks(name) {
996
1047
  }
997
1048
  for (const suffix of ["", "-opencode"]) {
998
1049
  const linkName = `${name}${suffix}`;
999
- const linkPath = path4.join(binDir, linkName);
1000
- if (existsSync4(linkPath)) {
1050
+ const linkPath = path5.join(binDir, linkName);
1051
+ if (existsSync5(linkPath)) {
1001
1052
  skipped.push(linkName);
1002
1053
  continue;
1003
1054
  }
@@ -1015,7 +1066,7 @@ var init_employees = __esm({
1015
1066
  "src/lib/employees.ts"() {
1016
1067
  "use strict";
1017
1068
  init_config();
1018
- EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
1069
+ EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
1019
1070
  DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
1020
1071
  COORDINATOR_ROLE = "COO";
1021
1072
  MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
@@ -1026,8 +1077,8 @@ var init_employees = __esm({
1026
1077
  import net from "net";
1027
1078
  import { spawn } from "child_process";
1028
1079
  import { randomUUID } from "crypto";
1029
- import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
1030
- import path5 from "path";
1080
+ import { existsSync as existsSync6, unlinkSync as unlinkSync2, readFileSync as readFileSync6, openSync, closeSync, statSync } from "fs";
1081
+ import path6 from "path";
1031
1082
  import { fileURLToPath } from "url";
1032
1083
  function handleData(chunk) {
1033
1084
  _buffer += chunk.toString();
@@ -1055,9 +1106,9 @@ function handleData(chunk) {
1055
1106
  }
1056
1107
  }
1057
1108
  function cleanupStaleFiles() {
1058
- if (existsSync5(PID_PATH)) {
1109
+ if (existsSync6(PID_PATH)) {
1059
1110
  try {
1060
- const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1111
+ const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
1061
1112
  if (pid > 0) {
1062
1113
  try {
1063
1114
  process.kill(pid, 0);
@@ -1078,11 +1129,11 @@ function cleanupStaleFiles() {
1078
1129
  }
1079
1130
  }
1080
1131
  function findPackageRoot() {
1081
- let dir = path5.dirname(fileURLToPath(import.meta.url));
1082
- const { root } = path5.parse(dir);
1132
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
1133
+ const { root } = path6.parse(dir);
1083
1134
  while (dir !== root) {
1084
- if (existsSync5(path5.join(dir, "package.json"))) return dir;
1085
- dir = path5.dirname(dir);
1135
+ if (existsSync6(path6.join(dir, "package.json"))) return dir;
1136
+ dir = path6.dirname(dir);
1086
1137
  }
1087
1138
  return null;
1088
1139
  }
@@ -1092,8 +1143,8 @@ function spawnDaemon() {
1092
1143
  process.stderr.write("[exed-client] WARN: cannot find package root\n");
1093
1144
  return;
1094
1145
  }
1095
- const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1096
- if (!existsSync5(daemonPath)) {
1146
+ const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1147
+ if (!existsSync6(daemonPath)) {
1097
1148
  process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1098
1149
  `);
1099
1150
  return;
@@ -1101,7 +1152,7 @@ function spawnDaemon() {
1101
1152
  const resolvedPath = daemonPath;
1102
1153
  process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1103
1154
  `);
1104
- const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
1155
+ const logPath = path6.join(path6.dirname(SOCKET_PATH), "exed.log");
1105
1156
  let stderrFd = "ignore";
1106
1157
  try {
1107
1158
  stderrFd = openSync(logPath, "a");
@@ -1251,9 +1302,9 @@ async function pingDaemon() {
1251
1302
  }
1252
1303
  function killAndRespawnDaemon() {
1253
1304
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
1254
- if (existsSync5(PID_PATH)) {
1305
+ if (existsSync6(PID_PATH)) {
1255
1306
  try {
1256
- const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1307
+ const pid = parseInt(readFileSync6(PID_PATH, "utf8").trim(), 10);
1257
1308
  if (pid > 0) {
1258
1309
  try {
1259
1310
  process.kill(pid, "SIGKILL");
@@ -1340,9 +1391,9 @@ var init_exe_daemon_client = __esm({
1340
1391
  "src/lib/exe-daemon-client.ts"() {
1341
1392
  "use strict";
1342
1393
  init_config();
1343
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1344
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1345
- SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
1394
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path6.join(EXE_AI_DIR, "exed.sock");
1395
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path6.join(EXE_AI_DIR, "exed.pid");
1396
+ SPAWN_LOCK_PATH = path6.join(EXE_AI_DIR, "exed-spawn.lock");
1346
1397
  SPAWN_LOCK_STALE_MS = 3e4;
1347
1398
  CONNECT_TIMEOUT_MS = 15e3;
1348
1399
  REQUEST_TIMEOUT_MS = 3e4;
@@ -2505,18 +2556,18 @@ var init_database = __esm({
2505
2556
  });
2506
2557
 
2507
2558
  // src/lib/license.ts
2508
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
2559
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
2509
2560
  import { randomUUID as randomUUID2 } from "crypto";
2510
- import path6 from "path";
2561
+ import path7 from "path";
2511
2562
  import { jwtVerify, importSPKI } from "jose";
2512
2563
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
2513
2564
  var init_license = __esm({
2514
2565
  "src/lib/license.ts"() {
2515
2566
  "use strict";
2516
2567
  init_config();
2517
- LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
2518
- CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
2519
- DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
2568
+ LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
2569
+ CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
2570
+ DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
2520
2571
  PLAN_LIMITS = {
2521
2572
  free: { devices: 1, employees: 1, memories: 5e3 },
2522
2573
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -2528,12 +2579,12 @@ var init_license = __esm({
2528
2579
  });
2529
2580
 
2530
2581
  // src/lib/plan-limits.ts
2531
- import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
2532
- import path7 from "path";
2582
+ import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
2583
+ import path8 from "path";
2533
2584
  function getLicenseSync() {
2534
2585
  try {
2535
- if (!existsSync7(CACHE_PATH2)) return freeLicense();
2536
- const raw = JSON.parse(readFileSync7(CACHE_PATH2, "utf8"));
2586
+ if (!existsSync8(CACHE_PATH2)) return freeLicense();
2587
+ const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
2537
2588
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
2538
2589
  const parts = raw.token.split(".");
2539
2590
  if (parts.length !== 3) return freeLicense();
@@ -2571,8 +2622,8 @@ function assertEmployeeLimitSync(rosterPath) {
2571
2622
  const filePath = rosterPath ?? EMPLOYEES_PATH;
2572
2623
  let count = 0;
2573
2624
  try {
2574
- if (existsSync7(filePath)) {
2575
- const raw = readFileSync7(filePath, "utf8");
2625
+ if (existsSync8(filePath)) {
2626
+ const raw = readFileSync8(filePath, "utf8");
2576
2627
  const employees = JSON.parse(raw);
2577
2628
  count = Array.isArray(employees) ? employees.length : 0;
2578
2629
  }
@@ -2601,19 +2652,19 @@ var init_plan_limits = __esm({
2601
2652
  this.name = "PlanLimitError";
2602
2653
  }
2603
2654
  };
2604
- CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
2655
+ CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
2605
2656
  }
2606
2657
  });
2607
2658
 
2608
2659
  // src/lib/notifications.ts
2609
2660
  import crypto from "crypto";
2610
- import path8 from "path";
2661
+ import path9 from "path";
2611
2662
  import os5 from "os";
2612
2663
  import {
2613
- readFileSync as readFileSync8,
2664
+ readFileSync as readFileSync9,
2614
2665
  readdirSync,
2615
2666
  unlinkSync as unlinkSync3,
2616
- existsSync as existsSync8,
2667
+ existsSync as existsSync9,
2617
2668
  rmdirSync
2618
2669
  } from "fs";
2619
2670
  async function writeNotification(notification) {
@@ -2847,11 +2898,11 @@ var init_state_bus = __esm({
2847
2898
 
2848
2899
  // src/lib/tasks-crud.ts
2849
2900
  import crypto3 from "crypto";
2850
- import path9 from "path";
2901
+ import path10 from "path";
2851
2902
  import os6 from "os";
2852
2903
  import { execSync as execSync5 } from "child_process";
2853
2904
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
2854
- import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
2905
+ import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
2855
2906
  async function writeCheckpoint(input) {
2856
2907
  const client = getClient();
2857
2908
  const row = await resolveTask(client, input.taskId);
@@ -3026,8 +3077,8 @@ ${laneWarning}` : laneWarning;
3026
3077
  }
3027
3078
  if (input.baseDir) {
3028
3079
  try {
3029
- await mkdir3(path9.join(input.baseDir, "exe", "output"), { recursive: true });
3030
- await mkdir3(path9.join(input.baseDir, "exe", "research"), { recursive: true });
3080
+ await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
3081
+ await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
3031
3082
  await ensureArchitectureDoc(input.baseDir, input.projectName);
3032
3083
  await ensureGitignoreExe(input.baseDir);
3033
3084
  } catch {
@@ -3063,10 +3114,10 @@ ${laneWarning}` : laneWarning;
3063
3114
  });
3064
3115
  if (input.baseDir) {
3065
3116
  try {
3066
- const EXE_OS_DIR = path9.join(os6.homedir(), ".exe-os");
3067
- const mdPath = path9.join(EXE_OS_DIR, taskFile);
3068
- const mdDir = path9.dirname(mdPath);
3069
- if (!existsSync9(mdDir)) await mkdir3(mdDir, { recursive: true });
3117
+ const EXE_OS_DIR = path10.join(os6.homedir(), ".exe-os");
3118
+ const mdPath = path10.join(EXE_OS_DIR, taskFile);
3119
+ const mdDir = path10.dirname(mdPath);
3120
+ if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
3070
3121
  const reviewer = input.reviewer ?? input.assignedBy;
3071
3122
  const mdContent = `# ${input.title}
3072
3123
 
@@ -3091,7 +3142,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
3091
3142
  Do NOT let a failed commit or any error prevent you from calling update_task(done).
3092
3143
  `;
3093
3144
  await writeFile3(mdPath, mdContent, "utf-8");
3094
- } catch {
3145
+ } catch (err) {
3146
+ process.stderr.write(
3147
+ `[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
3148
+ `
3149
+ );
3095
3150
  }
3096
3151
  }
3097
3152
  return {
@@ -3351,9 +3406,9 @@ async function deleteTaskCore(taskId, _baseDir) {
3351
3406
  return { taskFile, assignedTo, assignedBy, taskSlug };
3352
3407
  }
3353
3408
  async function ensureArchitectureDoc(baseDir, projectName) {
3354
- const archPath = path9.join(baseDir, "exe", "ARCHITECTURE.md");
3409
+ const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
3355
3410
  try {
3356
- if (existsSync9(archPath)) return;
3411
+ if (existsSync10(archPath)) return;
3357
3412
  const template = [
3358
3413
  `# ${projectName} \u2014 System Architecture`,
3359
3414
  "",
@@ -3386,10 +3441,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
3386
3441
  }
3387
3442
  }
3388
3443
  async function ensureGitignoreExe(baseDir) {
3389
- const gitignorePath = path9.join(baseDir, ".gitignore");
3444
+ const gitignorePath = path10.join(baseDir, ".gitignore");
3390
3445
  try {
3391
- if (existsSync9(gitignorePath)) {
3392
- const content = readFileSync9(gitignorePath, "utf-8");
3446
+ if (existsSync10(gitignorePath)) {
3447
+ const content = readFileSync10(gitignorePath, "utf-8");
3393
3448
  if (/^\/?exe\/?$/m.test(content)) return;
3394
3449
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
3395
3450
  } else {
@@ -3430,8 +3485,8 @@ __export(tasks_review_exports, {
3430
3485
  getReviewChecklist: () => getReviewChecklist,
3431
3486
  listPendingReviews: () => listPendingReviews
3432
3487
  });
3433
- import path10 from "path";
3434
- import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
3488
+ import path11 from "path";
3489
+ import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
3435
3490
  async function countPendingReviews(sessionScope) {
3436
3491
  const client = getClient();
3437
3492
  if (sessionScope) {
@@ -3701,11 +3756,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
3701
3756
  );
3702
3757
  }
3703
3758
  try {
3704
- const cacheDir = path10.join(EXE_AI_DIR, "session-cache");
3705
- if (existsSync10(cacheDir)) {
3759
+ const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
3760
+ if (existsSync11(cacheDir)) {
3706
3761
  for (const f of readdirSync2(cacheDir)) {
3707
3762
  if (f.startsWith("review-notified-")) {
3708
- unlinkSync4(path10.join(cacheDir, f));
3763
+ unlinkSync4(path11.join(cacheDir, f));
3709
3764
  }
3710
3765
  }
3711
3766
  }
@@ -3726,7 +3781,7 @@ var init_tasks_review = __esm({
3726
3781
  });
3727
3782
 
3728
3783
  // src/lib/tasks-chain.ts
3729
- import path11 from "path";
3784
+ import path12 from "path";
3730
3785
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
3731
3786
  async function cascadeUnblock(taskId, baseDir, now) {
3732
3787
  const client = getClient();
@@ -3743,7 +3798,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
3743
3798
  });
3744
3799
  for (const ur of unblockedRows.rows) {
3745
3800
  try {
3746
- const ubFile = path11.join(baseDir, String(ur.task_file));
3801
+ const ubFile = path12.join(baseDir, String(ur.task_file));
3747
3802
  let ubContent = await readFile3(ubFile, "utf-8");
3748
3803
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
3749
3804
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -3812,7 +3867,7 @@ var init_tasks_chain = __esm({
3812
3867
 
3813
3868
  // src/lib/project-name.ts
3814
3869
  import { execSync as execSync6 } from "child_process";
3815
- import path12 from "path";
3870
+ import path13 from "path";
3816
3871
  function getProjectName(cwd) {
3817
3872
  const dir = cwd ?? process.cwd();
3818
3873
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -3825,7 +3880,7 @@ function getProjectName(cwd) {
3825
3880
  timeout: 2e3,
3826
3881
  stdio: ["pipe", "pipe", "pipe"]
3827
3882
  }).trim();
3828
- repoRoot = path12.dirname(gitCommonDir);
3883
+ repoRoot = path13.dirname(gitCommonDir);
3829
3884
  } catch {
3830
3885
  repoRoot = execSync6("git rev-parse --show-toplevel", {
3831
3886
  cwd: dir,
@@ -3834,11 +3889,11 @@ function getProjectName(cwd) {
3834
3889
  stdio: ["pipe", "pipe", "pipe"]
3835
3890
  }).trim();
3836
3891
  }
3837
- _cached2 = path12.basename(repoRoot);
3892
+ _cached2 = path13.basename(repoRoot);
3838
3893
  _cachedCwd = dir;
3839
3894
  return _cached2;
3840
3895
  } catch {
3841
- _cached2 = path12.basename(dir);
3896
+ _cached2 = path13.basename(dir);
3842
3897
  _cachedCwd = dir;
3843
3898
  return _cached2;
3844
3899
  }
@@ -4311,8 +4366,8 @@ __export(tasks_exports, {
4311
4366
  updateTaskStatus: () => updateTaskStatus,
4312
4367
  writeCheckpoint: () => writeCheckpoint
4313
4368
  });
4314
- import path13 from "path";
4315
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync5 } from "fs";
4369
+ import path14 from "path";
4370
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5 } from "fs";
4316
4371
  async function createTask(input) {
4317
4372
  const result = await createTaskCore(input);
4318
4373
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -4331,11 +4386,11 @@ async function updateTask(input) {
4331
4386
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
4332
4387
  try {
4333
4388
  const agent = String(row.assigned_to);
4334
- const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
4335
- const cachePath = path13.join(cacheDir, `current-task-${agent}.json`);
4389
+ const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
4390
+ const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
4336
4391
  if (input.status === "in_progress") {
4337
- mkdirSync4(cacheDir, { recursive: true });
4338
- writeFileSync5(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
4392
+ mkdirSync5(cacheDir, { recursive: true });
4393
+ writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
4339
4394
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
4340
4395
  try {
4341
4396
  unlinkSync5(cachePath);
@@ -4802,13 +4857,13 @@ __export(tmux_routing_exports, {
4802
4857
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
4803
4858
  });
4804
4859
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
4805
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync11, appendFileSync } from "fs";
4806
- import path14 from "path";
4860
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, existsSync as existsSync12, appendFileSync } from "fs";
4861
+ import path15 from "path";
4807
4862
  import os7 from "os";
4808
4863
  import { fileURLToPath as fileURLToPath2 } from "url";
4809
4864
  import { unlinkSync as unlinkSync6 } from "fs";
4810
4865
  function spawnLockPath(sessionName) {
4811
- return path14.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4866
+ return path15.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
4812
4867
  }
4813
4868
  function isProcessAlive(pid) {
4814
4869
  try {
@@ -4819,13 +4874,13 @@ function isProcessAlive(pid) {
4819
4874
  }
4820
4875
  }
4821
4876
  function acquireSpawnLock2(sessionName) {
4822
- if (!existsSync11(SPAWN_LOCK_DIR)) {
4823
- mkdirSync5(SPAWN_LOCK_DIR, { recursive: true });
4877
+ if (!existsSync12(SPAWN_LOCK_DIR)) {
4878
+ mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
4824
4879
  }
4825
4880
  const lockFile = spawnLockPath(sessionName);
4826
- if (existsSync11(lockFile)) {
4881
+ if (existsSync12(lockFile)) {
4827
4882
  try {
4828
- const lock = JSON.parse(readFileSync10(lockFile, "utf8"));
4883
+ const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
4829
4884
  const age = Date.now() - lock.timestamp;
4830
4885
  if (isProcessAlive(lock.pid) && age < 6e4) {
4831
4886
  return false;
@@ -4833,7 +4888,7 @@ function acquireSpawnLock2(sessionName) {
4833
4888
  } catch {
4834
4889
  }
4835
4890
  }
4836
- writeFileSync6(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
4891
+ writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
4837
4892
  return true;
4838
4893
  }
4839
4894
  function releaseSpawnLock2(sessionName) {
@@ -4845,13 +4900,13 @@ function releaseSpawnLock2(sessionName) {
4845
4900
  function resolveBehaviorsExporterScript() {
4846
4901
  try {
4847
4902
  const thisFile = fileURLToPath2(import.meta.url);
4848
- const scriptPath = path14.join(
4849
- path14.dirname(thisFile),
4903
+ const scriptPath = path15.join(
4904
+ path15.dirname(thisFile),
4850
4905
  "..",
4851
4906
  "bin",
4852
4907
  "exe-export-behaviors.js"
4853
4908
  );
4854
- return existsSync11(scriptPath) ? scriptPath : null;
4909
+ return existsSync12(scriptPath) ? scriptPath : null;
4855
4910
  } catch {
4856
4911
  return null;
4857
4912
  }
@@ -4917,12 +4972,12 @@ function extractRootExe(name) {
4917
4972
  return parts.length > 0 ? parts[parts.length - 1] : null;
4918
4973
  }
4919
4974
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4920
- if (!existsSync11(SESSION_CACHE)) {
4921
- mkdirSync5(SESSION_CACHE, { recursive: true });
4975
+ if (!existsSync12(SESSION_CACHE)) {
4976
+ mkdirSync6(SESSION_CACHE, { recursive: true });
4922
4977
  }
4923
4978
  const rootExe = extractRootExe(parentExe) ?? parentExe;
4924
- const filePath = path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4925
- writeFileSync6(filePath, JSON.stringify({
4979
+ const filePath = path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
4980
+ writeFileSync7(filePath, JSON.stringify({
4926
4981
  parentExe: rootExe,
4927
4982
  dispatchedBy: dispatchedBy || rootExe,
4928
4983
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -4930,7 +4985,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
4930
4985
  }
4931
4986
  function getParentExe(sessionKey) {
4932
4987
  try {
4933
- const data = JSON.parse(readFileSync10(path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4988
+ const data = JSON.parse(readFileSync11(path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
4934
4989
  return data.parentExe || null;
4935
4990
  } catch {
4936
4991
  return null;
@@ -4938,8 +4993,8 @@ function getParentExe(sessionKey) {
4938
4993
  }
4939
4994
  function getDispatchedBy(sessionKey) {
4940
4995
  try {
4941
- const data = JSON.parse(readFileSync10(
4942
- path14.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4996
+ const data = JSON.parse(readFileSync11(
4997
+ path15.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
4943
4998
  "utf8"
4944
4999
  ));
4945
5000
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -5000,32 +5055,50 @@ async function verifyPaneAtCapacity(sessionName) {
5000
5055
  }
5001
5056
  function readDebounceState() {
5002
5057
  try {
5003
- if (!existsSync11(DEBOUNCE_FILE)) return {};
5004
- return JSON.parse(readFileSync10(DEBOUNCE_FILE, "utf8"));
5058
+ if (!existsSync12(DEBOUNCE_FILE)) return {};
5059
+ const raw = JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
5060
+ const state = {};
5061
+ for (const [key, val] of Object.entries(raw)) {
5062
+ if (typeof val === "number") {
5063
+ state[key] = { lastSent: val, pending: 0 };
5064
+ } else if (val && typeof val === "object" && "lastSent" in val) {
5065
+ state[key] = val;
5066
+ }
5067
+ }
5068
+ return state;
5005
5069
  } catch {
5006
5070
  return {};
5007
5071
  }
5008
5072
  }
5009
5073
  function writeDebounceState(state) {
5010
5074
  try {
5011
- if (!existsSync11(SESSION_CACHE)) mkdirSync5(SESSION_CACHE, { recursive: true });
5012
- writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
5075
+ if (!existsSync12(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
5076
+ writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
5013
5077
  } catch {
5014
5078
  }
5015
5079
  }
5016
5080
  function isDebounced(targetSession) {
5017
5081
  const state = readDebounceState();
5018
- const lastSent = state[targetSession] ?? 0;
5019
- return Date.now() - lastSent < INTERCOM_DEBOUNCE_MS;
5082
+ const entry = state[targetSession];
5083
+ const lastSent = entry?.lastSent ?? 0;
5084
+ if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
5085
+ if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
5086
+ state[targetSession].pending++;
5087
+ writeDebounceState(state);
5088
+ return true;
5089
+ }
5090
+ return false;
5020
5091
  }
5021
5092
  function recordDebounce(targetSession) {
5022
5093
  const state = readDebounceState();
5023
- state[targetSession] = Date.now();
5094
+ const batched = state[targetSession]?.pending ?? 0;
5095
+ state[targetSession] = { lastSent: Date.now(), pending: 0 };
5024
5096
  const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
5025
5097
  for (const key of Object.keys(state)) {
5026
- if ((state[key] ?? 0) < cutoff) delete state[key];
5098
+ if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
5027
5099
  }
5028
5100
  writeDebounceState(state);
5101
+ return batched;
5029
5102
  }
5030
5103
  function logIntercom(msg) {
5031
5104
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
@@ -5070,7 +5143,7 @@ function sendIntercom(targetSession) {
5070
5143
  return "skipped_exe";
5071
5144
  }
5072
5145
  if (isDebounced(targetSession)) {
5073
- logIntercom(`DEBOUNCE \u2192 ${targetSession} (cross-process file debounce)`);
5146
+ logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
5074
5147
  return "debounced";
5075
5148
  }
5076
5149
  try {
@@ -5082,14 +5155,14 @@ function sendIntercom(targetSession) {
5082
5155
  const sessionState = getSessionState(targetSession);
5083
5156
  if (sessionState === "no_claude") {
5084
5157
  queueIntercom(targetSession, "claude not running in session");
5085
- recordDebounce(targetSession);
5086
- logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
5158
+ const batched2 = recordDebounce(targetSession);
5159
+ logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
5087
5160
  return "queued";
5088
5161
  }
5089
5162
  if (sessionState === "thinking" || sessionState === "tool") {
5090
5163
  queueIntercom(targetSession, "session busy at send time");
5091
- recordDebounce(targetSession);
5092
- logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
5164
+ const batched2 = recordDebounce(targetSession);
5165
+ logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
5093
5166
  return "queued";
5094
5167
  }
5095
5168
  if (transport.isPaneInCopyMode(targetSession)) {
@@ -5097,8 +5170,8 @@ function sendIntercom(targetSession) {
5097
5170
  transport.sendKeys(targetSession, "q");
5098
5171
  }
5099
5172
  transport.sendKeys(targetSession, "/exe-intercom");
5100
- recordDebounce(targetSession);
5101
- logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
5173
+ const batched = recordDebounce(targetSession);
5174
+ logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
5102
5175
  return "delivered";
5103
5176
  } catch {
5104
5177
  logIntercom(`FAIL \u2192 ${targetSession}`);
@@ -5200,26 +5273,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5200
5273
  const transport = getTransport();
5201
5274
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
5202
5275
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
5203
- const logDir = path14.join(os7.homedir(), ".exe-os", "session-logs");
5204
- const logFile = path14.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5205
- if (!existsSync11(logDir)) {
5206
- mkdirSync5(logDir, { recursive: true });
5276
+ const logDir = path15.join(os7.homedir(), ".exe-os", "session-logs");
5277
+ const logFile = path15.join(logDir, `${instanceLabel}-${Date.now()}.log`);
5278
+ if (!existsSync12(logDir)) {
5279
+ mkdirSync6(logDir, { recursive: true });
5207
5280
  }
5208
5281
  transport.kill(sessionName);
5209
5282
  let cleanupSuffix = "";
5210
5283
  try {
5211
5284
  const thisFile = fileURLToPath2(import.meta.url);
5212
- const cleanupScript = path14.join(path14.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5213
- if (existsSync11(cleanupScript)) {
5285
+ const cleanupScript = path15.join(path15.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
5286
+ if (existsSync12(cleanupScript)) {
5214
5287
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
5215
5288
  }
5216
5289
  } catch {
5217
5290
  }
5218
5291
  try {
5219
- const claudeJsonPath = path14.join(os7.homedir(), ".claude.json");
5292
+ const claudeJsonPath = path15.join(os7.homedir(), ".claude.json");
5220
5293
  let claudeJson = {};
5221
5294
  try {
5222
- claudeJson = JSON.parse(readFileSync10(claudeJsonPath, "utf8"));
5295
+ claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
5223
5296
  } catch {
5224
5297
  }
5225
5298
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -5227,17 +5300,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5227
5300
  const trustDir = opts?.cwd ?? projectDir;
5228
5301
  if (!projects[trustDir]) projects[trustDir] = {};
5229
5302
  projects[trustDir].hasTrustDialogAccepted = true;
5230
- writeFileSync6(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
5303
+ writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
5231
5304
  } catch {
5232
5305
  }
5233
5306
  try {
5234
- const settingsDir = path14.join(os7.homedir(), ".claude", "projects");
5307
+ const settingsDir = path15.join(os7.homedir(), ".claude", "projects");
5235
5308
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
5236
- const projSettingsDir = path14.join(settingsDir, normalizedKey);
5237
- const settingsPath = path14.join(projSettingsDir, "settings.json");
5309
+ const projSettingsDir = path15.join(settingsDir, normalizedKey);
5310
+ const settingsPath = path15.join(projSettingsDir, "settings.json");
5238
5311
  let settings = {};
5239
5312
  try {
5240
- settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
5313
+ settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
5241
5314
  } catch {
5242
5315
  }
5243
5316
  const perms = settings.permissions ?? {};
@@ -5265,20 +5338,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5265
5338
  if (changed) {
5266
5339
  perms.allow = allow;
5267
5340
  settings.permissions = perms;
5268
- mkdirSync5(projSettingsDir, { recursive: true });
5269
- writeFileSync6(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5341
+ mkdirSync6(projSettingsDir, { recursive: true });
5342
+ writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
5270
5343
  }
5271
5344
  } catch {
5272
5345
  }
5273
5346
  const spawnCwd = opts?.cwd ?? projectDir;
5274
5347
  const useExeAgent = !!(opts?.model && opts?.provider);
5275
- const ccProvider = useExeAgent ? DEFAULT_PROVIDER : detectActiveProvider();
5348
+ const agentRtConfig = getAgentRuntime(employeeName);
5349
+ const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
5350
+ const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
5351
+ const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
5276
5352
  const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
5277
5353
  let identityFlag = "";
5278
5354
  let behaviorsFlag = "";
5279
5355
  let legacyFallbackWarned = false;
5280
5356
  if (!useExeAgent && !useBinSymlink) {
5281
- const identityPath = path14.join(
5357
+ const identityPath = path15.join(
5282
5358
  os7.homedir(),
5283
5359
  ".exe-os",
5284
5360
  "identity",
@@ -5288,13 +5364,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5288
5364
  const hasAgentFlag = claudeSupportsAgentFlag();
5289
5365
  if (hasAgentFlag) {
5290
5366
  identityFlag = ` --agent ${employeeName}`;
5291
- } else if (existsSync11(identityPath)) {
5367
+ } else if (existsSync12(identityPath)) {
5292
5368
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
5293
5369
  legacyFallbackWarned = true;
5294
5370
  }
5295
5371
  const behaviorsFile = exportBehaviorsSync(
5296
5372
  employeeName,
5297
- path14.basename(spawnCwd),
5373
+ path15.basename(spawnCwd),
5298
5374
  sessionName
5299
5375
  );
5300
5376
  if (behaviorsFile) {
@@ -5309,16 +5385,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5309
5385
  }
5310
5386
  let sessionContextFlag = "";
5311
5387
  try {
5312
- const ctxDir = path14.join(os7.homedir(), ".exe-os", "session-cache");
5313
- mkdirSync5(ctxDir, { recursive: true });
5314
- const ctxFile = path14.join(ctxDir, `session-context-${sessionName}.md`);
5388
+ const ctxDir = path15.join(os7.homedir(), ".exe-os", "session-cache");
5389
+ mkdirSync6(ctxDir, { recursive: true });
5390
+ const ctxFile = path15.join(ctxDir, `session-context-${sessionName}.md`);
5315
5391
  const ctxContent = [
5316
5392
  `## Session Context`,
5317
5393
  `You are running in tmux session: ${sessionName}.`,
5318
5394
  `Your parent coordinator session is ${exeSession}.`,
5319
5395
  `Your employees (if any) use the -${exeSession} suffix.`
5320
5396
  ].join("\n");
5321
- writeFileSync6(ctxFile, ctxContent);
5397
+ writeFileSync7(ctxFile, ctxContent);
5322
5398
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
5323
5399
  } catch {
5324
5400
  }
@@ -5332,9 +5408,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5332
5408
  }
5333
5409
  }
5334
5410
  }
5411
+ if (useCodex) {
5412
+ const codexCfg = RUNTIME_TABLE.codex;
5413
+ if (codexCfg?.apiKeyEnv) {
5414
+ const keyVal = process.env[codexCfg.apiKeyEnv];
5415
+ if (keyVal) {
5416
+ envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
5417
+ }
5418
+ }
5419
+ envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
5420
+ }
5421
+ if (useOpencode) {
5422
+ const ocCfg = PROVIDER_TABLE.opencode;
5423
+ if (ocCfg?.apiKeyEnv) {
5424
+ const keyVal = process.env[ocCfg.apiKeyEnv];
5425
+ if (keyVal) {
5426
+ envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
5427
+ }
5428
+ }
5429
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
5430
+ }
5431
+ if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
5432
+ const defaultClaudeModel = DEFAULT_MODELS.claude;
5433
+ if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
5434
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
5435
+ }
5436
+ }
5335
5437
  let spawnCommand;
5336
5438
  if (useExeAgent) {
5337
5439
  spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
5440
+ } else if (useCodex) {
5441
+ process.stderr.write(
5442
+ `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
5443
+ `
5444
+ );
5445
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
5446
+ } else if (useOpencode) {
5447
+ const binName = `${employeeName}-opencode`;
5448
+ process.stderr.write(
5449
+ `[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
5450
+ `
5451
+ );
5452
+ spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
5338
5453
  } else if (useBinSymlink) {
5339
5454
  const binName = `${employeeName}-${ccProvider}`;
5340
5455
  process.stderr.write(
@@ -5356,11 +5471,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5356
5471
  transport.pipeLog(sessionName, logFile);
5357
5472
  try {
5358
5473
  const mySession = getMySession();
5359
- const dispatchInfo = path14.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5360
- writeFileSync6(dispatchInfo, JSON.stringify({
5474
+ const dispatchInfo = path15.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
5475
+ writeFileSync7(dispatchInfo, JSON.stringify({
5361
5476
  dispatchedBy: mySession,
5362
5477
  rootExe: exeSession,
5363
- provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
5478
+ provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
5479
+ runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
5480
+ model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
5364
5481
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
5365
5482
  }));
5366
5483
  } catch {
@@ -5378,6 +5495,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5378
5495
  booted = true;
5379
5496
  break;
5380
5497
  }
5498
+ } else if (useCodex) {
5499
+ if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
5500
+ booted = true;
5501
+ break;
5502
+ }
5381
5503
  } else {
5382
5504
  if (pane.includes("Claude Code") || pane.includes("\u276F")) {
5383
5505
  booted = true;
@@ -5389,9 +5511,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5389
5511
  }
5390
5512
  if (!booted) {
5391
5513
  releaseSpawnLock2(sessionName);
5392
- return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
5514
+ const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
5515
+ return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
5393
5516
  }
5394
- if (!useExeAgent) {
5517
+ if (!useExeAgent && !useCodex) {
5395
5518
  try {
5396
5519
  transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
5397
5520
  } catch {
@@ -5418,17 +5541,19 @@ var init_tmux_routing = __esm({
5418
5541
  init_cc_agent_support();
5419
5542
  init_mcp_prefix();
5420
5543
  init_provider_table();
5544
+ init_agent_config();
5545
+ init_runtime_table();
5421
5546
  init_intercom_queue();
5422
5547
  init_plan_limits();
5423
5548
  init_employees();
5424
- SPAWN_LOCK_DIR = path14.join(os7.homedir(), ".exe-os", "spawn-locks");
5425
- SESSION_CACHE = path14.join(os7.homedir(), ".exe-os", "session-cache");
5549
+ SPAWN_LOCK_DIR = path15.join(os7.homedir(), ".exe-os", "spawn-locks");
5550
+ SESSION_CACHE = path15.join(os7.homedir(), ".exe-os", "session-cache");
5426
5551
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
5427
5552
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
5428
5553
  VERIFY_PANE_LINES = 200;
5429
5554
  INTERCOM_DEBOUNCE_MS = 3e4;
5430
- INTERCOM_LOG2 = path14.join(os7.homedir(), ".exe-os", "intercom.log");
5431
- DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
5555
+ INTERCOM_LOG2 = path15.join(os7.homedir(), ".exe-os", "intercom.log");
5556
+ DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
5432
5557
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5433
5558
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
5434
5559
  }
@@ -5712,9 +5837,9 @@ __export(agent_signals_exports, {
5712
5837
  hasOpenTasks: () => hasOpenTasks,
5713
5838
  hasUnreadInbox: () => hasUnreadInbox
5714
5839
  });
5715
- import { readFileSync as readFileSync11, existsSync as existsSync12 } from "fs";
5840
+ import { readFileSync as readFileSync12, existsSync as existsSync13 } from "fs";
5716
5841
  import os8 from "os";
5717
- import path15 from "path";
5842
+ import path16 from "path";
5718
5843
  async function hasOpenTasks(client, agentId) {
5719
5844
  try {
5720
5845
  const scope = sessionScopeFilter(null);
@@ -5756,10 +5881,10 @@ async function hasUnreadInbox(client, agentId) {
5756
5881
  return CONSERVATIVE_ON_ERROR;
5757
5882
  }
5758
5883
  }
5759
- function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog = path15.join(os8.homedir(), ".exe-os", "intercom.log")) {
5760
- if (!existsSync12(intercomLog)) return false;
5884
+ function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog = path16.join(os8.homedir(), ".exe-os", "intercom.log")) {
5885
+ if (!existsSync13(intercomLog)) return false;
5761
5886
  try {
5762
- const raw = readFileSync11(intercomLog, "utf8");
5887
+ const raw = readFileSync12(intercomLog, "utf8");
5763
5888
  const lines = raw.split("\n");
5764
5889
  for (let i = lines.length - 1; i >= 0; i--) {
5765
5890
  const line = lines[i];
@@ -5832,7 +5957,7 @@ __export(daemon_orchestration_exports, {
5832
5957
  shouldNudgeEmployee: () => shouldNudgeEmployee
5833
5958
  });
5834
5959
  import { execSync as execSync9 } from "child_process";
5835
- import { existsSync as existsSync13, readFileSync as readFileSync12, writeFileSync as writeFileSync7 } from "fs";
5960
+ import { existsSync as existsSync14, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "fs";
5836
5961
  import { homedir } from "os";
5837
5962
  import { join } from "path";
5838
5963
  function shouldNudgeEmployee(sessionState, hasOpenTasks2, lastNudgeMs, nowMs, dedupMs) {
@@ -6023,8 +6148,8 @@ async function pollReviewNudge(deps, state) {
6023
6148
  function loadNudgeState() {
6024
6149
  const state = { lastNudge: /* @__PURE__ */ new Map() };
6025
6150
  try {
6026
- if (!existsSync13(NUDGE_STATE_PATH)) return state;
6027
- const raw = JSON.parse(readFileSync12(NUDGE_STATE_PATH, "utf8"));
6151
+ if (!existsSync14(NUDGE_STATE_PATH)) return state;
6152
+ const raw = JSON.parse(readFileSync13(NUDGE_STATE_PATH, "utf8"));
6028
6153
  if (Array.isArray(raw)) {
6029
6154
  for (const [key, val] of raw) {
6030
6155
  if (key && typeof val?.at === "number" && typeof val?.count === "number") {
@@ -6038,7 +6163,7 @@ function loadNudgeState() {
6038
6163
  }
6039
6164
  function saveNudgeState(state) {
6040
6165
  const entries = Array.from(state.lastNudge.entries());
6041
- writeFileSync7(NUDGE_STATE_PATH, JSON.stringify(entries), "utf8");
6166
+ writeFileSync8(NUDGE_STATE_PATH, JSON.stringify(entries), "utf8");
6042
6167
  }
6043
6168
  function createReviewNudgeRealDeps(getClient2) {
6044
6169
  return {
@@ -6380,14 +6505,14 @@ __export(keychain_exports, {
6380
6505
  setMasterKey: () => setMasterKey
6381
6506
  });
6382
6507
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
6383
- import { existsSync as existsSync14 } from "fs";
6384
- import path16 from "path";
6508
+ import { existsSync as existsSync15 } from "fs";
6509
+ import path17 from "path";
6385
6510
  import os9 from "os";
6386
6511
  function getKeyDir() {
6387
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path16.join(os9.homedir(), ".exe-os");
6512
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path17.join(os9.homedir(), ".exe-os");
6388
6513
  }
6389
6514
  function getKeyPath() {
6390
- return path16.join(getKeyDir(), "master.key");
6515
+ return path17.join(getKeyDir(), "master.key");
6391
6516
  }
6392
6517
  async function tryKeytar() {
6393
6518
  try {
@@ -6408,7 +6533,7 @@ async function getMasterKey() {
6408
6533
  }
6409
6534
  }
6410
6535
  const keyPath = getKeyPath();
6411
- if (!existsSync14(keyPath)) {
6536
+ if (!existsSync15(keyPath)) {
6412
6537
  process.stderr.write(
6413
6538
  `[keychain] Key not found at ${keyPath} (HOME=${os9.homedir()}, EXE_OS_DIR=${process.env.EXE_OS_DIR ?? "unset"})
6414
6539
  `
@@ -6451,7 +6576,7 @@ async function deleteMasterKey() {
6451
6576
  }
6452
6577
  }
6453
6578
  const keyPath = getKeyPath();
6454
- if (existsSync14(keyPath)) {
6579
+ if (existsSync15(keyPath)) {
6455
6580
  await unlink(keyPath);
6456
6581
  }
6457
6582
  }
@@ -6506,13 +6631,13 @@ __export(shard_manager_exports, {
6506
6631
  listShards: () => listShards,
6507
6632
  shardExists: () => shardExists
6508
6633
  });
6509
- import path17 from "path";
6510
- import { existsSync as existsSync15, mkdirSync as mkdirSync6, readdirSync as readdirSync3 } from "fs";
6634
+ import path18 from "path";
6635
+ import { existsSync as existsSync16, mkdirSync as mkdirSync7, readdirSync as readdirSync3 } from "fs";
6511
6636
  import { createClient as createClient2 } from "@libsql/client";
6512
6637
  function initShardManager(encryptionKey) {
6513
6638
  _encryptionKey = encryptionKey;
6514
- if (!existsSync15(SHARDS_DIR)) {
6515
- mkdirSync6(SHARDS_DIR, { recursive: true });
6639
+ if (!existsSync16(SHARDS_DIR)) {
6640
+ mkdirSync7(SHARDS_DIR, { recursive: true });
6516
6641
  }
6517
6642
  _shardingEnabled = true;
6518
6643
  }
@@ -6532,7 +6657,7 @@ function getShardClient(projectName) {
6532
6657
  }
6533
6658
  const cached = _shards.get(safeName);
6534
6659
  if (cached) return cached;
6535
- const dbPath = path17.join(SHARDS_DIR, `${safeName}.db`);
6660
+ const dbPath = path18.join(SHARDS_DIR, `${safeName}.db`);
6536
6661
  const client = createClient2({
6537
6662
  url: `file:${dbPath}`,
6538
6663
  encryptionKey: _encryptionKey
@@ -6542,10 +6667,10 @@ function getShardClient(projectName) {
6542
6667
  }
6543
6668
  function shardExists(projectName) {
6544
6669
  const safeName = projectName.replace(/[^a-zA-Z0-9_-]/g, "_");
6545
- return existsSync15(path17.join(SHARDS_DIR, `${safeName}.db`));
6670
+ return existsSync16(path18.join(SHARDS_DIR, `${safeName}.db`));
6546
6671
  }
6547
6672
  function listShards() {
6548
- if (!existsSync15(SHARDS_DIR)) return [];
6673
+ if (!existsSync16(SHARDS_DIR)) return [];
6549
6674
  return readdirSync3(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
6550
6675
  }
6551
6676
  async function ensureShardSchema(client) {
@@ -6731,7 +6856,7 @@ var init_shard_manager = __esm({
6731
6856
  "src/lib/shard-manager.ts"() {
6732
6857
  "use strict";
6733
6858
  init_config();
6734
- SHARDS_DIR = path17.join(EXE_AI_DIR, "shards");
6859
+ SHARDS_DIR = path18.join(EXE_AI_DIR, "shards");
6735
6860
  _shards = /* @__PURE__ */ new Map();
6736
6861
  _encryptionKey = null;
6737
6862
  _shardingEnabled = false;
@@ -8096,10 +8221,10 @@ async function disposeEmbedder() {
8096
8221
  async function embedDirect(text) {
8097
8222
  const llamaCpp = await import("node-llama-cpp");
8098
8223
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
8099
- const { existsSync: existsSync18 } = await import("fs");
8100
- const path22 = await import("path");
8101
- const modelPath = path22.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
8102
- if (!existsSync18(modelPath)) {
8224
+ const { existsSync: existsSync19 } = await import("fs");
8225
+ const path23 = await import("path");
8226
+ const modelPath = path23.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
8227
+ if (!existsSync19(modelPath)) {
8103
8228
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
8104
8229
  }
8105
8230
  const llama = await llamaCpp.getLlama();
@@ -8821,8 +8946,8 @@ __export(wiki_sync_exports, {
8821
8946
  listWorkspaces: () => listWorkspaces,
8822
8947
  syncMemories: () => syncMemories
8823
8948
  });
8824
- async function wikiRequest(config, path22, method = "GET", body) {
8825
- const url = `${config.wikiUrl}/api/v1${path22}`;
8949
+ async function wikiRequest(config, path23, method = "GET", body) {
8950
+ const url = `${config.wikiUrl}/api/v1${path23}`;
8826
8951
  const headers = {
8827
8952
  "Authorization": `Bearer ${config.wikiApiKey}`,
8828
8953
  "Content-Type": "application/json"
@@ -8834,7 +8959,7 @@ async function wikiRequest(config, path22, method = "GET", body) {
8834
8959
  signal: AbortSignal.timeout(3e4)
8835
8960
  });
8836
8961
  if (!response.ok) {
8837
- throw new Error(`Wiki API ${method} ${path22}: ${response.status} ${response.statusText}`);
8962
+ throw new Error(`Wiki API ${method} ${path23}: ${response.status} ${response.statusText}`);
8838
8963
  }
8839
8964
  return response.json();
8840
8965
  }
@@ -8946,7 +9071,7 @@ __export(token_spend_exports, {
8946
9071
  import { readdir } from "fs/promises";
8947
9072
  import { createReadStream } from "fs";
8948
9073
  import { createInterface } from "readline";
8949
- import path18 from "path";
9074
+ import path19 from "path";
8950
9075
  import os10 from "os";
8951
9076
  function getPricing(model) {
8952
9077
  if (MODEL_PRICING[model]) return MODEL_PRICING[model];
@@ -8970,18 +9095,18 @@ async function getAgentSpend(period = "7d") {
8970
9095
  for (const row of result.rows) {
8971
9096
  sessionAgent.set(row.session_uuid, row.agent_id);
8972
9097
  }
8973
- const claudeDir = path18.join(os10.homedir(), ".claude", "projects");
9098
+ const claudeDir = path19.join(os10.homedir(), ".claude", "projects");
8974
9099
  let projectDirs = [];
8975
9100
  try {
8976
9101
  const entries = await readdir(claudeDir);
8977
- projectDirs = entries.map((e) => path18.join(claudeDir, e));
9102
+ projectDirs = entries.map((e) => path19.join(claudeDir, e));
8978
9103
  } catch {
8979
9104
  return [];
8980
9105
  }
8981
9106
  const agentTotals = /* @__PURE__ */ new Map();
8982
9107
  for (const [sessionUuid, agentId] of sessionAgent) {
8983
9108
  for (const dir of projectDirs) {
8984
- const jsonlPath = path18.join(dir, `${sessionUuid}.jsonl`);
9109
+ const jsonlPath = path19.join(dir, `${sessionUuid}.jsonl`);
8985
9110
  try {
8986
9111
  const usage = await extractSessionUsage(jsonlPath);
8987
9112
  if (usage.input === 0 && usage.output === 0) continue;
@@ -9102,11 +9227,11 @@ __export(update_check_exports, {
9102
9227
  getRemoteVersion: () => getRemoteVersion
9103
9228
  });
9104
9229
  import { execSync as execSync11 } from "child_process";
9105
- import { readFileSync as readFileSync13 } from "fs";
9106
- import path19 from "path";
9230
+ import { readFileSync as readFileSync14 } from "fs";
9231
+ import path20 from "path";
9107
9232
  function getLocalVersion(packageRoot) {
9108
- const pkgPath = path19.join(packageRoot, "package.json");
9109
- const pkg = JSON.parse(readFileSync13(pkgPath, "utf-8"));
9233
+ const pkgPath = path20.join(packageRoot, "package.json");
9234
+ const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
9110
9235
  return pkg.version;
9111
9236
  }
9112
9237
  function getRemoteVersion() {
@@ -9178,12 +9303,12 @@ __export(device_registry_exports, {
9178
9303
  });
9179
9304
  import crypto8 from "crypto";
9180
9305
  import os11 from "os";
9181
- import { readFileSync as readFileSync14, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, existsSync as existsSync16 } from "fs";
9182
- import path20 from "path";
9306
+ import { readFileSync as readFileSync15, writeFileSync as writeFileSync9, mkdirSync as mkdirSync8, existsSync as existsSync17 } from "fs";
9307
+ import path21 from "path";
9183
9308
  function getDeviceInfo() {
9184
- if (existsSync16(DEVICE_JSON_PATH)) {
9309
+ if (existsSync17(DEVICE_JSON_PATH)) {
9185
9310
  try {
9186
- const raw = readFileSync14(DEVICE_JSON_PATH, "utf8");
9311
+ const raw = readFileSync15(DEVICE_JSON_PATH, "utf8");
9187
9312
  const data = JSON.parse(raw);
9188
9313
  if (data.deviceId && data.friendlyName && data.hostname) {
9189
9314
  return data;
@@ -9197,14 +9322,14 @@ function getDeviceInfo() {
9197
9322
  friendlyName: hostname.replace(/\./g, "-").toLowerCase(),
9198
9323
  hostname
9199
9324
  };
9200
- mkdirSync7(path20.dirname(DEVICE_JSON_PATH), { recursive: true });
9201
- writeFileSync8(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
9325
+ mkdirSync8(path21.dirname(DEVICE_JSON_PATH), { recursive: true });
9326
+ writeFileSync9(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
9202
9327
  return info;
9203
9328
  }
9204
9329
  function setFriendlyName(name) {
9205
9330
  const info = getDeviceInfo();
9206
9331
  info.friendlyName = name;
9207
- writeFileSync8(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
9332
+ writeFileSync9(DEVICE_JSON_PATH, JSON.stringify(info, null, 2));
9208
9333
  }
9209
9334
  async function resolveTargetDevice(targetAgent, targetProject) {
9210
9335
  const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
@@ -9238,7 +9363,7 @@ var init_device_registry = __esm({
9238
9363
  "src/lib/device-registry.ts"() {
9239
9364
  "use strict";
9240
9365
  init_config();
9241
- DEVICE_JSON_PATH = path20.join(EXE_AI_DIR, "device.json");
9366
+ DEVICE_JSON_PATH = path21.join(EXE_AI_DIR, "device.json");
9242
9367
  }
9243
9368
  });
9244
9369
 
@@ -9702,11 +9827,11 @@ init_memory();
9702
9827
  init_daemon_protocol();
9703
9828
  init_daemon_orchestration();
9704
9829
  import net2 from "net";
9705
- import { writeFileSync as writeFileSync9, unlinkSync as unlinkSync7, mkdirSync as mkdirSync8, existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
9706
- import path21 from "path";
9830
+ import { writeFileSync as writeFileSync10, unlinkSync as unlinkSync7, mkdirSync as mkdirSync9, existsSync as existsSync18, readFileSync as readFileSync16 } from "fs";
9831
+ import path22 from "path";
9707
9832
  import { getLlama } from "node-llama-cpp";
9708
- var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path21.join(EXE_AI_DIR, "exed.sock");
9709
- var PID_PATH2 = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path21.join(EXE_AI_DIR, "exed.pid");
9833
+ var SOCKET_PATH2 = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path22.join(EXE_AI_DIR, "exed.sock");
9834
+ var PID_PATH2 = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path22.join(EXE_AI_DIR, "exed.pid");
9710
9835
  var MODEL_FILE = "jina-embeddings-v5-small-q4_k_m.gguf";
9711
9836
  var IDLE_TIMEOUT_MS = 15 * 60 * 1e3;
9712
9837
  var REVIEW_POLL_INTERVAL_MS = 60 * 1e3;
@@ -9732,8 +9857,8 @@ function enqueue(queue, entry) {
9732
9857
  queue.push(entry);
9733
9858
  }
9734
9859
  async function loadModel() {
9735
- const modelPath = path21.join(MODELS_DIR, MODEL_FILE);
9736
- if (!existsSync17(modelPath)) {
9860
+ const modelPath = path22.join(MODELS_DIR, MODEL_FILE);
9861
+ if (!existsSync18(modelPath)) {
9737
9862
  process.stderr.write(`[exed] FATAL: model not found at ${modelPath}
9738
9863
  `);
9739
9864
  process.exit(1);
@@ -9902,12 +10027,12 @@ async function handleDbBatch(socket, requestId, statements, mode) {
9902
10027
  }
9903
10028
  }
9904
10029
  function startServer() {
9905
- mkdirSync8(path21.dirname(SOCKET_PATH2), { recursive: true });
10030
+ mkdirSync9(path22.dirname(SOCKET_PATH2), { recursive: true });
9906
10031
  for (const oldFile of ["embed.sock", "embed.pid"]) {
9907
- const oldPath = path21.join(path21.dirname(SOCKET_PATH2), oldFile);
10032
+ const oldPath = path22.join(path22.dirname(SOCKET_PATH2), oldFile);
9908
10033
  try {
9909
10034
  if (oldFile.endsWith(".pid")) {
9910
- const pid = parseInt(readFileSync15(oldPath, "utf8").trim(), 10);
10035
+ const pid = parseInt(readFileSync16(oldPath, "utf8").trim(), 10);
9911
10036
  if (pid > 0) try {
9912
10037
  process.kill(pid, "SIGKILL");
9913
10038
  } catch {
@@ -9996,7 +10121,7 @@ function startServer() {
9996
10121
  server.listen(SOCKET_PATH2, () => {
9997
10122
  process.stderr.write(`[exed] Listening on ${SOCKET_PATH2}
9998
10123
  `);
9999
- writeFileSync9(PID_PATH2, String(process.pid));
10124
+ writeFileSync10(PID_PATH2, String(process.pid));
10000
10125
  checkIdle();
10001
10126
  });
10002
10127
  }
@@ -10289,7 +10414,7 @@ function startWikiSync() {
10289
10414
  });
10290
10415
  }
10291
10416
  var AGENT_STATS_INTERVAL_MS = 60 * 1e3;
10292
- var AGENT_STATS_PATH = path21.join(EXE_AI_DIR, "agent-stats.json");
10417
+ var AGENT_STATS_PATH = path22.join(EXE_AI_DIR, "agent-stats.json");
10293
10418
  async function writeAgentStats() {
10294
10419
  if (!await ensureStoreForPolling()) return;
10295
10420
  try {
@@ -10347,7 +10472,7 @@ async function writeAgentStats() {
10347
10472
  pid: process.pid
10348
10473
  }
10349
10474
  };
10350
- writeFileSync9(AGENT_STATS_PATH, JSON.stringify(stats, null, 2), "utf8");
10475
+ writeFileSync10(AGENT_STATS_PATH, JSON.stringify(stats, null, 2), "utf8");
10351
10476
  } catch (err) {
10352
10477
  process.stderr.write(`[exed] Agent stats error: ${err instanceof Error ? err.message : String(err)}
10353
10478
  `);
@@ -10494,8 +10619,8 @@ process.on("SIGINT", () => void shutdown());
10494
10619
  process.on("SIGTERM", () => void shutdown());
10495
10620
  function checkExistingDaemon() {
10496
10621
  try {
10497
- if (!existsSync17(PID_PATH2)) return false;
10498
- const pid = parseInt(readFileSync15(PID_PATH2, "utf8").trim(), 10);
10622
+ if (!existsSync18(PID_PATH2)) return false;
10623
+ const pid = parseInt(readFileSync16(PID_PATH2, "utf8").trim(), 10);
10499
10624
  if (!pid || isNaN(pid)) return false;
10500
10625
  process.kill(pid, 0);
10501
10626
  process.stderr.write(`[exed] Another daemon is already running (PID ${pid}). Exiting.