@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
@@ -2201,10 +2201,10 @@ async function disposeEmbedder() {
2201
2201
  async function embedDirect(text) {
2202
2202
  const llamaCpp = await import("node-llama-cpp");
2203
2203
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2204
- const { existsSync: existsSync16 } = await import("fs");
2205
- const path20 = await import("path");
2206
- const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
2207
- if (!existsSync16(modelPath)) {
2204
+ const { existsSync: existsSync17 } = await import("fs");
2205
+ const path21 = await import("path");
2206
+ const modelPath = path21.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
2207
+ if (!existsSync17(modelPath)) {
2208
2208
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
2209
2209
  }
2210
2210
  const llama = await llamaCpp.getLlama();
@@ -3292,8 +3292,8 @@ __export(wiki_client_exports, {
3292
3292
  listDocuments: () => listDocuments,
3293
3293
  listWorkspaces: () => listWorkspaces
3294
3294
  });
3295
- async function wikiFetch(config2, path20, method = "GET", body) {
3296
- const url = `${config2.baseUrl}/api/v1${path20}`;
3295
+ async function wikiFetch(config2, path21, method = "GET", body) {
3296
+ const url = `${config2.baseUrl}/api/v1${path21}`;
3297
3297
  const headers = {
3298
3298
  Authorization: `Bearer ${config2.apiKey}`,
3299
3299
  "Content-Type": "application/json"
@@ -3326,7 +3326,7 @@ async function wikiFetch(config2, path20, method = "GET", body) {
3326
3326
  }
3327
3327
  }
3328
3328
  if (!response.ok) {
3329
- throw new Error(`Wiki API ${method} ${path20}: ${response.status} ${response.statusText}`);
3329
+ throw new Error(`Wiki API ${method} ${path21}: ${response.status} ${response.statusText}`);
3330
3330
  }
3331
3331
  return response.json();
3332
3332
  } finally {
@@ -5916,9 +5916,9 @@ __export(webhook_exports, {
5916
5916
  WebhookAdapter: () => WebhookAdapter
5917
5917
  });
5918
5918
  import { randomUUID as randomUUID7 } from "crypto";
5919
- function resolvePath(obj, path20) {
5919
+ function resolvePath(obj, path21) {
5920
5920
  let current = obj;
5921
- for (const segment of path20.split(".")) {
5921
+ for (const segment of path21.split(".")) {
5922
5922
  if (current == null || typeof current !== "object") return void 0;
5923
5923
  current = current[segment];
5924
5924
  }
@@ -6315,18 +6315,69 @@ var init_provider_table = __esm({
6315
6315
  }
6316
6316
  });
6317
6317
 
6318
- // src/lib/intercom-queue.ts
6319
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
6318
+ // src/lib/runtime-table.ts
6319
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
6320
+ var init_runtime_table = __esm({
6321
+ "src/lib/runtime-table.ts"() {
6322
+ "use strict";
6323
+ RUNTIME_TABLE = {
6324
+ codex: {
6325
+ binary: "codex",
6326
+ launchMode: "exec",
6327
+ autoApproveFlag: "--full-auto",
6328
+ inlineFlag: "--no-alt-screen",
6329
+ apiKeyEnv: "OPENAI_API_KEY",
6330
+ defaultModel: "gpt-5.4"
6331
+ }
6332
+ };
6333
+ DEFAULT_RUNTIME = "claude";
6334
+ }
6335
+ });
6336
+
6337
+ // src/lib/agent-config.ts
6338
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
6320
6339
  import path9 from "path";
6340
+ function loadAgentConfig() {
6341
+ if (!existsSync8(AGENT_CONFIG_PATH)) return {};
6342
+ try {
6343
+ return JSON.parse(readFileSync7(AGENT_CONFIG_PATH, "utf-8"));
6344
+ } catch {
6345
+ return {};
6346
+ }
6347
+ }
6348
+ function getAgentRuntime(agentId) {
6349
+ const config2 = loadAgentConfig();
6350
+ const entry = config2[agentId];
6351
+ if (entry) return entry;
6352
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
6353
+ }
6354
+ var AGENT_CONFIG_PATH, DEFAULT_MODELS;
6355
+ var init_agent_config = __esm({
6356
+ "src/lib/agent-config.ts"() {
6357
+ "use strict";
6358
+ init_config();
6359
+ init_runtime_table();
6360
+ AGENT_CONFIG_PATH = path9.join(EXE_AI_DIR, "agent-config.json");
6361
+ DEFAULT_MODELS = {
6362
+ claude: "claude-opus-4",
6363
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
6364
+ opencode: "minimax-m2.7"
6365
+ };
6366
+ }
6367
+ });
6368
+
6369
+ // src/lib/intercom-queue.ts
6370
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, renameSync as renameSync3, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
6371
+ import path10 from "path";
6321
6372
  import os6 from "os";
6322
6373
  function ensureDir() {
6323
- const dir = path9.dirname(QUEUE_PATH);
6324
- if (!existsSync8(dir)) mkdirSync5(dir, { recursive: true });
6374
+ const dir = path10.dirname(QUEUE_PATH);
6375
+ if (!existsSync9(dir)) mkdirSync6(dir, { recursive: true });
6325
6376
  }
6326
6377
  function readQueue() {
6327
6378
  try {
6328
- if (!existsSync8(QUEUE_PATH)) return [];
6329
- return JSON.parse(readFileSync7(QUEUE_PATH, "utf8"));
6379
+ if (!existsSync9(QUEUE_PATH)) return [];
6380
+ return JSON.parse(readFileSync8(QUEUE_PATH, "utf8"));
6330
6381
  } catch {
6331
6382
  return [];
6332
6383
  }
@@ -6334,7 +6385,7 @@ function readQueue() {
6334
6385
  function writeQueue(queue) {
6335
6386
  ensureDir();
6336
6387
  const tmp = `${QUEUE_PATH}.tmp`;
6337
- writeFileSync4(tmp, JSON.stringify(queue, null, 2));
6388
+ writeFileSync5(tmp, JSON.stringify(queue, null, 2));
6338
6389
  renameSync3(tmp, QUEUE_PATH);
6339
6390
  }
6340
6391
  function queueIntercom(targetSession, reason) {
@@ -6358,19 +6409,19 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
6358
6409
  var init_intercom_queue = __esm({
6359
6410
  "src/lib/intercom-queue.ts"() {
6360
6411
  "use strict";
6361
- QUEUE_PATH = path9.join(os6.homedir(), ".exe-os", "intercom-queue.json");
6412
+ QUEUE_PATH = path10.join(os6.homedir(), ".exe-os", "intercom-queue.json");
6362
6413
  TTL_MS = 60 * 60 * 1e3;
6363
- INTERCOM_LOG = path9.join(os6.homedir(), ".exe-os", "intercom.log");
6414
+ INTERCOM_LOG = path10.join(os6.homedir(), ".exe-os", "intercom.log");
6364
6415
  }
6365
6416
  });
6366
6417
 
6367
6418
  // src/lib/plan-limits.ts
6368
- import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
6369
- import path10 from "path";
6419
+ import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
6420
+ import path11 from "path";
6370
6421
  function getLicenseSync() {
6371
6422
  try {
6372
- if (!existsSync9(CACHE_PATH2)) return freeLicense();
6373
- const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
6423
+ if (!existsSync10(CACHE_PATH2)) return freeLicense();
6424
+ const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
6374
6425
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
6375
6426
  const parts = raw.token.split(".");
6376
6427
  if (parts.length !== 3) return freeLicense();
@@ -6408,8 +6459,8 @@ function assertEmployeeLimitSync(rosterPath) {
6408
6459
  const filePath = rosterPath ?? EMPLOYEES_PATH;
6409
6460
  let count = 0;
6410
6461
  try {
6411
- if (existsSync9(filePath)) {
6412
- const raw = readFileSync8(filePath, "utf8");
6462
+ if (existsSync10(filePath)) {
6463
+ const raw = readFileSync9(filePath, "utf8");
6413
6464
  const employees = JSON.parse(raw);
6414
6465
  count = Array.isArray(employees) ? employees.length : 0;
6415
6466
  }
@@ -6438,19 +6489,19 @@ var init_plan_limits = __esm({
6438
6489
  this.name = "PlanLimitError";
6439
6490
  }
6440
6491
  };
6441
- CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
6492
+ CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
6442
6493
  }
6443
6494
  });
6444
6495
 
6445
6496
  // src/lib/notifications.ts
6446
6497
  import crypto3 from "crypto";
6447
- import path11 from "path";
6498
+ import path12 from "path";
6448
6499
  import os7 from "os";
6449
6500
  import {
6450
- readFileSync as readFileSync9,
6501
+ readFileSync as readFileSync10,
6451
6502
  readdirSync as readdirSync2,
6452
6503
  unlinkSync as unlinkSync3,
6453
- existsSync as existsSync10,
6504
+ existsSync as existsSync11,
6454
6505
  rmdirSync
6455
6506
  } from "fs";
6456
6507
  async function writeNotification(notification) {
@@ -6554,11 +6605,11 @@ var init_task_scope = __esm({
6554
6605
 
6555
6606
  // src/lib/tasks-crud.ts
6556
6607
  import crypto5 from "crypto";
6557
- import path12 from "path";
6608
+ import path13 from "path";
6558
6609
  import os8 from "os";
6559
6610
  import { execSync as execSync4 } from "child_process";
6560
6611
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
6561
- import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
6612
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
6562
6613
  async function writeCheckpoint(input) {
6563
6614
  const client = getClient();
6564
6615
  const row = await resolveTask(client, input.taskId);
@@ -6733,8 +6784,8 @@ ${laneWarning}` : laneWarning;
6733
6784
  }
6734
6785
  if (input.baseDir) {
6735
6786
  try {
6736
- await mkdir4(path12.join(input.baseDir, "exe", "output"), { recursive: true });
6737
- await mkdir4(path12.join(input.baseDir, "exe", "research"), { recursive: true });
6787
+ await mkdir4(path13.join(input.baseDir, "exe", "output"), { recursive: true });
6788
+ await mkdir4(path13.join(input.baseDir, "exe", "research"), { recursive: true });
6738
6789
  await ensureArchitectureDoc(input.baseDir, input.projectName);
6739
6790
  await ensureGitignoreExe(input.baseDir);
6740
6791
  } catch {
@@ -6770,10 +6821,10 @@ ${laneWarning}` : laneWarning;
6770
6821
  });
6771
6822
  if (input.baseDir) {
6772
6823
  try {
6773
- const EXE_OS_DIR = path12.join(os8.homedir(), ".exe-os");
6774
- const mdPath = path12.join(EXE_OS_DIR, taskFile);
6775
- const mdDir = path12.dirname(mdPath);
6776
- if (!existsSync11(mdDir)) await mkdir4(mdDir, { recursive: true });
6824
+ const EXE_OS_DIR = path13.join(os8.homedir(), ".exe-os");
6825
+ const mdPath = path13.join(EXE_OS_DIR, taskFile);
6826
+ const mdDir = path13.dirname(mdPath);
6827
+ if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
6777
6828
  const reviewer = input.reviewer ?? input.assignedBy;
6778
6829
  const mdContent = `# ${input.title}
6779
6830
 
@@ -6798,7 +6849,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
6798
6849
  Do NOT let a failed commit or any error prevent you from calling update_task(done).
6799
6850
  `;
6800
6851
  await writeFile4(mdPath, mdContent, "utf-8");
6801
- } catch {
6852
+ } catch (err) {
6853
+ process.stderr.write(
6854
+ `[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
6855
+ `
6856
+ );
6802
6857
  }
6803
6858
  }
6804
6859
  return {
@@ -7058,9 +7113,9 @@ async function deleteTaskCore(taskId, _baseDir) {
7058
7113
  return { taskFile, assignedTo, assignedBy, taskSlug };
7059
7114
  }
7060
7115
  async function ensureArchitectureDoc(baseDir, projectName) {
7061
- const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
7116
+ const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
7062
7117
  try {
7063
- if (existsSync11(archPath)) return;
7118
+ if (existsSync12(archPath)) return;
7064
7119
  const template = [
7065
7120
  `# ${projectName} \u2014 System Architecture`,
7066
7121
  "",
@@ -7093,10 +7148,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
7093
7148
  }
7094
7149
  }
7095
7150
  async function ensureGitignoreExe(baseDir) {
7096
- const gitignorePath = path12.join(baseDir, ".gitignore");
7151
+ const gitignorePath = path13.join(baseDir, ".gitignore");
7097
7152
  try {
7098
- if (existsSync11(gitignorePath)) {
7099
- const content = readFileSync10(gitignorePath, "utf-8");
7153
+ if (existsSync12(gitignorePath)) {
7154
+ const content = readFileSync11(gitignorePath, "utf-8");
7100
7155
  if (/^\/?exe\/?$/m.test(content)) return;
7101
7156
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
7102
7157
  } else {
@@ -7127,8 +7182,8 @@ var init_tasks_crud = __esm({
7127
7182
  });
7128
7183
 
7129
7184
  // src/lib/tasks-review.ts
7130
- import path13 from "path";
7131
- import { existsSync as existsSync12, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
7185
+ import path14 from "path";
7186
+ import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
7132
7187
  async function countPendingReviews(sessionScope) {
7133
7188
  const client = getClient();
7134
7189
  if (sessionScope) {
@@ -7309,11 +7364,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
7309
7364
  );
7310
7365
  }
7311
7366
  try {
7312
- const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
7313
- if (existsSync12(cacheDir)) {
7367
+ const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
7368
+ if (existsSync13(cacheDir)) {
7314
7369
  for (const f of readdirSync3(cacheDir)) {
7315
7370
  if (f.startsWith("review-notified-")) {
7316
- unlinkSync4(path13.join(cacheDir, f));
7371
+ unlinkSync4(path14.join(cacheDir, f));
7317
7372
  }
7318
7373
  }
7319
7374
  }
@@ -7334,7 +7389,7 @@ var init_tasks_review = __esm({
7334
7389
  });
7335
7390
 
7336
7391
  // src/lib/tasks-chain.ts
7337
- import path14 from "path";
7392
+ import path15 from "path";
7338
7393
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
7339
7394
  async function cascadeUnblock(taskId, baseDir, now) {
7340
7395
  const client = getClient();
@@ -7351,7 +7406,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
7351
7406
  });
7352
7407
  for (const ur of unblockedRows.rows) {
7353
7408
  try {
7354
- const ubFile = path14.join(baseDir, String(ur.task_file));
7409
+ const ubFile = path15.join(baseDir, String(ur.task_file));
7355
7410
  let ubContent = await readFile4(ubFile, "utf-8");
7356
7411
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
7357
7412
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -7420,7 +7475,7 @@ var init_tasks_chain = __esm({
7420
7475
 
7421
7476
  // src/lib/project-name.ts
7422
7477
  import { execSync as execSync5 } from "child_process";
7423
- import path15 from "path";
7478
+ import path16 from "path";
7424
7479
  function getProjectName(cwd) {
7425
7480
  const dir = cwd ?? process.cwd();
7426
7481
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -7433,7 +7488,7 @@ function getProjectName(cwd) {
7433
7488
  timeout: 2e3,
7434
7489
  stdio: ["pipe", "pipe", "pipe"]
7435
7490
  }).trim();
7436
- repoRoot = path15.dirname(gitCommonDir);
7491
+ repoRoot = path16.dirname(gitCommonDir);
7437
7492
  } catch {
7438
7493
  repoRoot = execSync5("git rev-parse --show-toplevel", {
7439
7494
  cwd: dir,
@@ -7442,11 +7497,11 @@ function getProjectName(cwd) {
7442
7497
  stdio: ["pipe", "pipe", "pipe"]
7443
7498
  }).trim();
7444
7499
  }
7445
- _cached2 = path15.basename(repoRoot);
7500
+ _cached2 = path16.basename(repoRoot);
7446
7501
  _cachedCwd = dir;
7447
7502
  return _cached2;
7448
7503
  } catch {
7449
- _cached2 = path15.basename(dir);
7504
+ _cached2 = path16.basename(dir);
7450
7505
  _cachedCwd = dir;
7451
7506
  return _cached2;
7452
7507
  }
@@ -7919,8 +7974,8 @@ __export(tasks_exports, {
7919
7974
  updateTaskStatus: () => updateTaskStatus,
7920
7975
  writeCheckpoint: () => writeCheckpoint
7921
7976
  });
7922
- import path16 from "path";
7923
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
7977
+ import path17 from "path";
7978
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
7924
7979
  async function createTask(input) {
7925
7980
  const result = await createTaskCore(input);
7926
7981
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -7939,11 +7994,11 @@ async function updateTask(input) {
7939
7994
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
7940
7995
  try {
7941
7996
  const agent = String(row.assigned_to);
7942
- const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
7943
- const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
7997
+ const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
7998
+ const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
7944
7999
  if (input.status === "in_progress") {
7945
- mkdirSync6(cacheDir, { recursive: true });
7946
- writeFileSync5(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
8000
+ mkdirSync7(cacheDir, { recursive: true });
8001
+ writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
7947
8002
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
7948
8003
  try {
7949
8004
  unlinkSync5(cachePath);
@@ -8410,13 +8465,13 @@ __export(tmux_routing_exports, {
8410
8465
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
8411
8466
  });
8412
8467
  import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
8413
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, existsSync as existsSync13, appendFileSync } from "fs";
8414
- import path17 from "path";
8468
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, existsSync as existsSync14, appendFileSync } from "fs";
8469
+ import path18 from "path";
8415
8470
  import os9 from "os";
8416
8471
  import { fileURLToPath as fileURLToPath2 } from "url";
8417
8472
  import { unlinkSync as unlinkSync6 } from "fs";
8418
8473
  function spawnLockPath(sessionName) {
8419
- return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
8474
+ return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
8420
8475
  }
8421
8476
  function isProcessAlive(pid) {
8422
8477
  try {
@@ -8427,13 +8482,13 @@ function isProcessAlive(pid) {
8427
8482
  }
8428
8483
  }
8429
8484
  function acquireSpawnLock2(sessionName) {
8430
- if (!existsSync13(SPAWN_LOCK_DIR)) {
8431
- mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
8485
+ if (!existsSync14(SPAWN_LOCK_DIR)) {
8486
+ mkdirSync8(SPAWN_LOCK_DIR, { recursive: true });
8432
8487
  }
8433
8488
  const lockFile = spawnLockPath(sessionName);
8434
- if (existsSync13(lockFile)) {
8489
+ if (existsSync14(lockFile)) {
8435
8490
  try {
8436
- const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
8491
+ const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
8437
8492
  const age = Date.now() - lock.timestamp;
8438
8493
  if (isProcessAlive(lock.pid) && age < 6e4) {
8439
8494
  return false;
@@ -8441,7 +8496,7 @@ function acquireSpawnLock2(sessionName) {
8441
8496
  } catch {
8442
8497
  }
8443
8498
  }
8444
- writeFileSync6(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
8499
+ writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
8445
8500
  return true;
8446
8501
  }
8447
8502
  function releaseSpawnLock2(sessionName) {
@@ -8453,13 +8508,13 @@ function releaseSpawnLock2(sessionName) {
8453
8508
  function resolveBehaviorsExporterScript() {
8454
8509
  try {
8455
8510
  const thisFile = fileURLToPath2(import.meta.url);
8456
- const scriptPath = path17.join(
8457
- path17.dirname(thisFile),
8511
+ const scriptPath = path18.join(
8512
+ path18.dirname(thisFile),
8458
8513
  "..",
8459
8514
  "bin",
8460
8515
  "exe-export-behaviors.js"
8461
8516
  );
8462
- return existsSync13(scriptPath) ? scriptPath : null;
8517
+ return existsSync14(scriptPath) ? scriptPath : null;
8463
8518
  } catch {
8464
8519
  return null;
8465
8520
  }
@@ -8525,12 +8580,12 @@ function extractRootExe(name) {
8525
8580
  return parts.length > 0 ? parts[parts.length - 1] : null;
8526
8581
  }
8527
8582
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
8528
- if (!existsSync13(SESSION_CACHE)) {
8529
- mkdirSync7(SESSION_CACHE, { recursive: true });
8583
+ if (!existsSync14(SESSION_CACHE)) {
8584
+ mkdirSync8(SESSION_CACHE, { recursive: true });
8530
8585
  }
8531
8586
  const rootExe = extractRootExe(parentExe) ?? parentExe;
8532
- const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
8533
- writeFileSync6(filePath, JSON.stringify({
8587
+ const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
8588
+ writeFileSync7(filePath, JSON.stringify({
8534
8589
  parentExe: rootExe,
8535
8590
  dispatchedBy: dispatchedBy || rootExe,
8536
8591
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -8538,7 +8593,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
8538
8593
  }
8539
8594
  function getParentExe(sessionKey) {
8540
8595
  try {
8541
- const data = JSON.parse(readFileSync11(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
8596
+ const data = JSON.parse(readFileSync12(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
8542
8597
  return data.parentExe || null;
8543
8598
  } catch {
8544
8599
  return null;
@@ -8546,8 +8601,8 @@ function getParentExe(sessionKey) {
8546
8601
  }
8547
8602
  function getDispatchedBy(sessionKey) {
8548
8603
  try {
8549
- const data = JSON.parse(readFileSync11(
8550
- path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
8604
+ const data = JSON.parse(readFileSync12(
8605
+ path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
8551
8606
  "utf8"
8552
8607
  ));
8553
8608
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -8608,32 +8663,50 @@ async function verifyPaneAtCapacity(sessionName) {
8608
8663
  }
8609
8664
  function readDebounceState() {
8610
8665
  try {
8611
- if (!existsSync13(DEBOUNCE_FILE)) return {};
8612
- return JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
8666
+ if (!existsSync14(DEBOUNCE_FILE)) return {};
8667
+ const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
8668
+ const state = {};
8669
+ for (const [key, val] of Object.entries(raw)) {
8670
+ if (typeof val === "number") {
8671
+ state[key] = { lastSent: val, pending: 0 };
8672
+ } else if (val && typeof val === "object" && "lastSent" in val) {
8673
+ state[key] = val;
8674
+ }
8675
+ }
8676
+ return state;
8613
8677
  } catch {
8614
8678
  return {};
8615
8679
  }
8616
8680
  }
8617
8681
  function writeDebounceState(state) {
8618
8682
  try {
8619
- if (!existsSync13(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
8620
- writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
8683
+ if (!existsSync14(SESSION_CACHE)) mkdirSync8(SESSION_CACHE, { recursive: true });
8684
+ writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
8621
8685
  } catch {
8622
8686
  }
8623
8687
  }
8624
8688
  function isDebounced(targetSession) {
8625
8689
  const state = readDebounceState();
8626
- const lastSent = state[targetSession] ?? 0;
8627
- return Date.now() - lastSent < INTERCOM_DEBOUNCE_MS;
8690
+ const entry = state[targetSession];
8691
+ const lastSent = entry?.lastSent ?? 0;
8692
+ if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
8693
+ if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
8694
+ state[targetSession].pending++;
8695
+ writeDebounceState(state);
8696
+ return true;
8697
+ }
8698
+ return false;
8628
8699
  }
8629
8700
  function recordDebounce(targetSession) {
8630
8701
  const state = readDebounceState();
8631
- state[targetSession] = Date.now();
8702
+ const batched = state[targetSession]?.pending ?? 0;
8703
+ state[targetSession] = { lastSent: Date.now(), pending: 0 };
8632
8704
  const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
8633
8705
  for (const key of Object.keys(state)) {
8634
- if ((state[key] ?? 0) < cutoff) delete state[key];
8706
+ if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
8635
8707
  }
8636
8708
  writeDebounceState(state);
8709
+ return batched;
8637
8710
  }
8638
8711
  function logIntercom(msg) {
8639
8712
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
@@ -8678,7 +8751,7 @@ function sendIntercom(targetSession) {
8678
8751
  return "skipped_exe";
8679
8752
  }
8680
8753
  if (isDebounced(targetSession)) {
8681
- logIntercom(`DEBOUNCE \u2192 ${targetSession} (cross-process file debounce)`);
8754
+ logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
8682
8755
  return "debounced";
8683
8756
  }
8684
8757
  try {
@@ -8690,14 +8763,14 @@ function sendIntercom(targetSession) {
8690
8763
  const sessionState = getSessionState(targetSession);
8691
8764
  if (sessionState === "no_claude") {
8692
8765
  queueIntercom(targetSession, "claude not running in session");
8693
- recordDebounce(targetSession);
8694
- logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
8766
+ const batched2 = recordDebounce(targetSession);
8767
+ logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
8695
8768
  return "queued";
8696
8769
  }
8697
8770
  if (sessionState === "thinking" || sessionState === "tool") {
8698
8771
  queueIntercom(targetSession, "session busy at send time");
8699
- recordDebounce(targetSession);
8700
- logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
8772
+ const batched2 = recordDebounce(targetSession);
8773
+ logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
8701
8774
  return "queued";
8702
8775
  }
8703
8776
  if (transport.isPaneInCopyMode(targetSession)) {
@@ -8705,8 +8778,8 @@ function sendIntercom(targetSession) {
8705
8778
  transport.sendKeys(targetSession, "q");
8706
8779
  }
8707
8780
  transport.sendKeys(targetSession, "/exe-intercom");
8708
- recordDebounce(targetSession);
8709
- logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
8781
+ const batched = recordDebounce(targetSession);
8782
+ logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
8710
8783
  return "delivered";
8711
8784
  } catch {
8712
8785
  logIntercom(`FAIL \u2192 ${targetSession}`);
@@ -8808,26 +8881,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
8808
8881
  const transport = getTransport();
8809
8882
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
8810
8883
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
8811
- const logDir = path17.join(os9.homedir(), ".exe-os", "session-logs");
8812
- const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
8813
- if (!existsSync13(logDir)) {
8814
- mkdirSync7(logDir, { recursive: true });
8884
+ const logDir = path18.join(os9.homedir(), ".exe-os", "session-logs");
8885
+ const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
8886
+ if (!existsSync14(logDir)) {
8887
+ mkdirSync8(logDir, { recursive: true });
8815
8888
  }
8816
8889
  transport.kill(sessionName);
8817
8890
  let cleanupSuffix = "";
8818
8891
  try {
8819
8892
  const thisFile = fileURLToPath2(import.meta.url);
8820
- const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
8821
- if (existsSync13(cleanupScript)) {
8893
+ const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
8894
+ if (existsSync14(cleanupScript)) {
8822
8895
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
8823
8896
  }
8824
8897
  } catch {
8825
8898
  }
8826
8899
  try {
8827
- const claudeJsonPath = path17.join(os9.homedir(), ".claude.json");
8900
+ const claudeJsonPath = path18.join(os9.homedir(), ".claude.json");
8828
8901
  let claudeJson = {};
8829
8902
  try {
8830
- claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
8903
+ claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
8831
8904
  } catch {
8832
8905
  }
8833
8906
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -8835,17 +8908,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
8835
8908
  const trustDir = opts?.cwd ?? projectDir;
8836
8909
  if (!projects[trustDir]) projects[trustDir] = {};
8837
8910
  projects[trustDir].hasTrustDialogAccepted = true;
8838
- writeFileSync6(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
8911
+ writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
8839
8912
  } catch {
8840
8913
  }
8841
8914
  try {
8842
- const settingsDir = path17.join(os9.homedir(), ".claude", "projects");
8915
+ const settingsDir = path18.join(os9.homedir(), ".claude", "projects");
8843
8916
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
8844
- const projSettingsDir = path17.join(settingsDir, normalizedKey);
8845
- const settingsPath = path17.join(projSettingsDir, "settings.json");
8917
+ const projSettingsDir = path18.join(settingsDir, normalizedKey);
8918
+ const settingsPath = path18.join(projSettingsDir, "settings.json");
8846
8919
  let settings = {};
8847
8920
  try {
8848
- settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
8921
+ settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
8849
8922
  } catch {
8850
8923
  }
8851
8924
  const perms = settings.permissions ?? {};
@@ -8873,20 +8946,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
8873
8946
  if (changed) {
8874
8947
  perms.allow = allow;
8875
8948
  settings.permissions = perms;
8876
- mkdirSync7(projSettingsDir, { recursive: true });
8877
- writeFileSync6(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8949
+ mkdirSync8(projSettingsDir, { recursive: true });
8950
+ writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
8878
8951
  }
8879
8952
  } catch {
8880
8953
  }
8881
8954
  const spawnCwd = opts?.cwd ?? projectDir;
8882
8955
  const useExeAgent = !!(opts?.model && opts?.provider);
8883
- const ccProvider = useExeAgent ? DEFAULT_PROVIDER : detectActiveProvider();
8956
+ const agentRtConfig = getAgentRuntime(employeeName);
8957
+ const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
8958
+ const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
8959
+ const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
8884
8960
  const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
8885
8961
  let identityFlag = "";
8886
8962
  let behaviorsFlag = "";
8887
8963
  let legacyFallbackWarned = false;
8888
8964
  if (!useExeAgent && !useBinSymlink) {
8889
- const identityPath = path17.join(
8965
+ const identityPath = path18.join(
8890
8966
  os9.homedir(),
8891
8967
  ".exe-os",
8892
8968
  "identity",
@@ -8896,13 +8972,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
8896
8972
  const hasAgentFlag = claudeSupportsAgentFlag();
8897
8973
  if (hasAgentFlag) {
8898
8974
  identityFlag = ` --agent ${employeeName}`;
8899
- } else if (existsSync13(identityPath)) {
8975
+ } else if (existsSync14(identityPath)) {
8900
8976
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
8901
8977
  legacyFallbackWarned = true;
8902
8978
  }
8903
8979
  const behaviorsFile = exportBehaviorsSync(
8904
8980
  employeeName,
8905
- path17.basename(spawnCwd),
8981
+ path18.basename(spawnCwd),
8906
8982
  sessionName
8907
8983
  );
8908
8984
  if (behaviorsFile) {
@@ -8917,16 +8993,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
8917
8993
  }
8918
8994
  let sessionContextFlag = "";
8919
8995
  try {
8920
- const ctxDir = path17.join(os9.homedir(), ".exe-os", "session-cache");
8921
- mkdirSync7(ctxDir, { recursive: true });
8922
- const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
8996
+ const ctxDir = path18.join(os9.homedir(), ".exe-os", "session-cache");
8997
+ mkdirSync8(ctxDir, { recursive: true });
8998
+ const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
8923
8999
  const ctxContent = [
8924
9000
  `## Session Context`,
8925
9001
  `You are running in tmux session: ${sessionName}.`,
8926
9002
  `Your parent coordinator session is ${exeSession}.`,
8927
9003
  `Your employees (if any) use the -${exeSession} suffix.`
8928
9004
  ].join("\n");
8929
- writeFileSync6(ctxFile, ctxContent);
9005
+ writeFileSync7(ctxFile, ctxContent);
8930
9006
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
8931
9007
  } catch {
8932
9008
  }
@@ -8940,9 +9016,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
8940
9016
  }
8941
9017
  }
8942
9018
  }
9019
+ if (useCodex) {
9020
+ const codexCfg = RUNTIME_TABLE.codex;
9021
+ if (codexCfg?.apiKeyEnv) {
9022
+ const keyVal = process.env[codexCfg.apiKeyEnv];
9023
+ if (keyVal) {
9024
+ envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
9025
+ }
9026
+ }
9027
+ envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
9028
+ }
9029
+ if (useOpencode) {
9030
+ const ocCfg = PROVIDER_TABLE.opencode;
9031
+ if (ocCfg?.apiKeyEnv) {
9032
+ const keyVal = process.env[ocCfg.apiKeyEnv];
9033
+ if (keyVal) {
9034
+ envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
9035
+ }
9036
+ }
9037
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
9038
+ }
9039
+ if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
9040
+ const defaultClaudeModel = DEFAULT_MODELS.claude;
9041
+ if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
9042
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
9043
+ }
9044
+ }
8943
9045
  let spawnCommand;
8944
9046
  if (useExeAgent) {
8945
9047
  spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
9048
+ } else if (useCodex) {
9049
+ process.stderr.write(
9050
+ `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
9051
+ `
9052
+ );
9053
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
9054
+ } else if (useOpencode) {
9055
+ const binName = `${employeeName}-opencode`;
9056
+ process.stderr.write(
9057
+ `[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
9058
+ `
9059
+ );
9060
+ spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
8946
9061
  } else if (useBinSymlink) {
8947
9062
  const binName = `${employeeName}-${ccProvider}`;
8948
9063
  process.stderr.write(
@@ -8964,11 +9079,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
8964
9079
  transport.pipeLog(sessionName, logFile);
8965
9080
  try {
8966
9081
  const mySession = getMySession();
8967
- const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
8968
- writeFileSync6(dispatchInfo, JSON.stringify({
9082
+ const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
9083
+ writeFileSync7(dispatchInfo, JSON.stringify({
8969
9084
  dispatchedBy: mySession,
8970
9085
  rootExe: exeSession,
8971
- provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
9086
+ provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
9087
+ runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
9088
+ model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
8972
9089
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
8973
9090
  }));
8974
9091
  } catch {
@@ -8986,6 +9103,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
8986
9103
  booted = true;
8987
9104
  break;
8988
9105
  }
9106
+ } else if (useCodex) {
9107
+ if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
9108
+ booted = true;
9109
+ break;
9110
+ }
8989
9111
  } else {
8990
9112
  if (pane.includes("Claude Code") || pane.includes("\u276F")) {
8991
9113
  booted = true;
@@ -8997,9 +9119,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
8997
9119
  }
8998
9120
  if (!booted) {
8999
9121
  releaseSpawnLock2(sessionName);
9000
- return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
9122
+ const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
9123
+ return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
9001
9124
  }
9002
- if (!useExeAgent) {
9125
+ if (!useExeAgent && !useCodex) {
9003
9126
  try {
9004
9127
  transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
9005
9128
  } catch {
@@ -9026,17 +9149,19 @@ var init_tmux_routing = __esm({
9026
9149
  init_cc_agent_support();
9027
9150
  init_mcp_prefix();
9028
9151
  init_provider_table();
9152
+ init_agent_config();
9153
+ init_runtime_table();
9029
9154
  init_intercom_queue();
9030
9155
  init_plan_limits();
9031
9156
  init_employees();
9032
- SPAWN_LOCK_DIR = path17.join(os9.homedir(), ".exe-os", "spawn-locks");
9033
- SESSION_CACHE = path17.join(os9.homedir(), ".exe-os", "session-cache");
9157
+ SPAWN_LOCK_DIR = path18.join(os9.homedir(), ".exe-os", "spawn-locks");
9158
+ SESSION_CACHE = path18.join(os9.homedir(), ".exe-os", "session-cache");
9034
9159
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
9035
9160
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
9036
9161
  VERIFY_PANE_LINES = 200;
9037
9162
  INTERCOM_DEBOUNCE_MS = 3e4;
9038
- INTERCOM_LOG2 = path17.join(os9.homedir(), ".exe-os", "intercom.log");
9039
- DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
9163
+ INTERCOM_LOG2 = path18.join(os9.homedir(), ".exe-os", "intercom.log");
9164
+ DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
9040
9165
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
9041
9166
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
9042
9167
  }
@@ -9294,9 +9419,9 @@ var init_messaging = __esm({
9294
9419
  });
9295
9420
 
9296
9421
  // src/automation/trigger-engine.ts
9297
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
9422
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as existsSync15, mkdirSync as mkdirSync9 } from "fs";
9298
9423
  import { randomUUID as randomUUID8 } from "crypto";
9299
- import path18 from "path";
9424
+ import path19 from "path";
9300
9425
  import os10 from "os";
9301
9426
  function substituteTemplate(template, record) {
9302
9427
  return template.replace(
@@ -9350,9 +9475,9 @@ function evaluateConditions(conditions, record) {
9350
9475
  return conditions.every((c) => evaluateCondition(c, record));
9351
9476
  }
9352
9477
  function loadTriggers(project) {
9353
- if (!existsSync14(TRIGGERS_PATH)) return [];
9478
+ if (!existsSync15(TRIGGERS_PATH)) return [];
9354
9479
  try {
9355
- const raw = readFileSync12(TRIGGERS_PATH, "utf-8");
9480
+ const raw = readFileSync13(TRIGGERS_PATH, "utf-8");
9356
9481
  const all = JSON.parse(raw);
9357
9482
  if (!Array.isArray(all)) return [];
9358
9483
  if (project) {
@@ -9592,7 +9717,7 @@ var TRIGGERS_PATH, GRAPH_API_VERSION;
9592
9717
  var init_trigger_engine = __esm({
9593
9718
  "src/automation/trigger-engine.ts"() {
9594
9719
  "use strict";
9595
- TRIGGERS_PATH = path18.join(os10.homedir(), ".exe-os", "triggers.json");
9720
+ TRIGGERS_PATH = path19.join(os10.homedir(), ".exe-os", "triggers.json");
9596
9721
  GRAPH_API_VERSION = "v21.0";
9597
9722
  }
9598
9723
  });
@@ -9655,8 +9780,8 @@ var init_crm_webhook = __esm({
9655
9780
  });
9656
9781
 
9657
9782
  // src/bin/exe-gateway.ts
9658
- import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
9659
- import path19 from "path";
9783
+ import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
9784
+ import path20 from "path";
9660
9785
  import os11 from "os";
9661
9786
 
9662
9787
  // src/gateway/webhook-server.ts
@@ -10623,18 +10748,18 @@ var BotRegistry = class {
10623
10748
 
10624
10749
  // src/bin/exe-gateway.ts
10625
10750
  init_employees();
10626
- var CONFIG_DIR = path19.join(os11.homedir(), ".exe-os");
10627
- var CONFIG_PATH3 = path19.join(CONFIG_DIR, "gateway.json");
10751
+ var CONFIG_DIR = path20.join(os11.homedir(), ".exe-os");
10752
+ var CONFIG_PATH3 = path20.join(CONFIG_DIR, "gateway.json");
10628
10753
  var DEFAULT_PORT = 3100;
10629
10754
  function loadConfig2() {
10630
- if (!existsSync15(CONFIG_PATH3)) {
10755
+ if (!existsSync16(CONFIG_PATH3)) {
10631
10756
  console.log(
10632
10757
  `[exe-gateway] No config at ${CONFIG_PATH3} \u2014 using defaults (port ${DEFAULT_PORT})`
10633
10758
  );
10634
10759
  return {};
10635
10760
  }
10636
10761
  try {
10637
- const raw = readFileSync13(CONFIG_PATH3, "utf-8");
10762
+ const raw = readFileSync14(CONFIG_PATH3, "utf-8");
10638
10763
  return JSON.parse(raw);
10639
10764
  } catch (err) {
10640
10765
  console.error(