@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
@@ -2194,10 +2194,10 @@ async function disposeEmbedder() {
2194
2194
  async function embedDirect(text) {
2195
2195
  const llamaCpp = await import("node-llama-cpp");
2196
2196
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
2197
- const { existsSync: existsSync15 } = await import("fs");
2198
- const path19 = await import("path");
2199
- const modelPath = path19.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
2200
- if (!existsSync15(modelPath)) {
2197
+ const { existsSync: existsSync16 } = await import("fs");
2198
+ const path20 = await import("path");
2199
+ const modelPath = path20.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
2200
+ if (!existsSync16(modelPath)) {
2201
2201
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
2202
2202
  }
2203
2203
  const llama = await llamaCpp.getLlama();
@@ -3285,8 +3285,8 @@ __export(wiki_client_exports, {
3285
3285
  listDocuments: () => listDocuments,
3286
3286
  listWorkspaces: () => listWorkspaces
3287
3287
  });
3288
- async function wikiFetch(config2, path19, method = "GET", body) {
3289
- const url = `${config2.baseUrl}/api/v1${path19}`;
3288
+ async function wikiFetch(config2, path20, method = "GET", body) {
3289
+ const url = `${config2.baseUrl}/api/v1${path20}`;
3290
3290
  const headers = {
3291
3291
  Authorization: `Bearer ${config2.apiKey}`,
3292
3292
  "Content-Type": "application/json"
@@ -3319,7 +3319,7 @@ async function wikiFetch(config2, path19, method = "GET", body) {
3319
3319
  }
3320
3320
  }
3321
3321
  if (!response.ok) {
3322
- throw new Error(`Wiki API ${method} ${path19}: ${response.status} ${response.statusText}`);
3322
+ throw new Error(`Wiki API ${method} ${path20}: ${response.status} ${response.statusText}`);
3323
3323
  }
3324
3324
  return response.json();
3325
3325
  } finally {
@@ -4634,18 +4634,69 @@ var init_provider_table = __esm({
4634
4634
  }
4635
4635
  });
4636
4636
 
4637
- // src/lib/intercom-queue.ts
4638
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
4637
+ // src/lib/runtime-table.ts
4638
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
4639
+ var init_runtime_table = __esm({
4640
+ "src/lib/runtime-table.ts"() {
4641
+ "use strict";
4642
+ RUNTIME_TABLE = {
4643
+ codex: {
4644
+ binary: "codex",
4645
+ launchMode: "exec",
4646
+ autoApproveFlag: "--full-auto",
4647
+ inlineFlag: "--no-alt-screen",
4648
+ apiKeyEnv: "OPENAI_API_KEY",
4649
+ defaultModel: "gpt-5.4"
4650
+ }
4651
+ };
4652
+ DEFAULT_RUNTIME = "claude";
4653
+ }
4654
+ });
4655
+
4656
+ // src/lib/agent-config.ts
4657
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
4639
4658
  import path8 from "path";
4659
+ function loadAgentConfig() {
4660
+ if (!existsSync7(AGENT_CONFIG_PATH)) return {};
4661
+ try {
4662
+ return JSON.parse(readFileSync6(AGENT_CONFIG_PATH, "utf-8"));
4663
+ } catch {
4664
+ return {};
4665
+ }
4666
+ }
4667
+ function getAgentRuntime(agentId) {
4668
+ const config2 = loadAgentConfig();
4669
+ const entry = config2[agentId];
4670
+ if (entry) return entry;
4671
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
4672
+ }
4673
+ var AGENT_CONFIG_PATH, DEFAULT_MODELS;
4674
+ var init_agent_config = __esm({
4675
+ "src/lib/agent-config.ts"() {
4676
+ "use strict";
4677
+ init_config();
4678
+ init_runtime_table();
4679
+ AGENT_CONFIG_PATH = path8.join(EXE_AI_DIR, "agent-config.json");
4680
+ DEFAULT_MODELS = {
4681
+ claude: "claude-opus-4",
4682
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
4683
+ opencode: "minimax-m2.7"
4684
+ };
4685
+ }
4686
+ });
4687
+
4688
+ // src/lib/intercom-queue.ts
4689
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
4690
+ import path9 from "path";
4640
4691
  import os6 from "os";
4641
4692
  function ensureDir() {
4642
- const dir = path8.dirname(QUEUE_PATH);
4643
- if (!existsSync7(dir)) mkdirSync4(dir, { recursive: true });
4693
+ const dir = path9.dirname(QUEUE_PATH);
4694
+ if (!existsSync8(dir)) mkdirSync5(dir, { recursive: true });
4644
4695
  }
4645
4696
  function readQueue() {
4646
4697
  try {
4647
- if (!existsSync7(QUEUE_PATH)) return [];
4648
- return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
4698
+ if (!existsSync8(QUEUE_PATH)) return [];
4699
+ return JSON.parse(readFileSync7(QUEUE_PATH, "utf8"));
4649
4700
  } catch {
4650
4701
  return [];
4651
4702
  }
@@ -4653,7 +4704,7 @@ function readQueue() {
4653
4704
  function writeQueue(queue) {
4654
4705
  ensureDir();
4655
4706
  const tmp = `${QUEUE_PATH}.tmp`;
4656
- writeFileSync3(tmp, JSON.stringify(queue, null, 2));
4707
+ writeFileSync4(tmp, JSON.stringify(queue, null, 2));
4657
4708
  renameSync3(tmp, QUEUE_PATH);
4658
4709
  }
4659
4710
  function queueIntercom(targetSession, reason) {
@@ -4677,25 +4728,25 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
4677
4728
  var init_intercom_queue = __esm({
4678
4729
  "src/lib/intercom-queue.ts"() {
4679
4730
  "use strict";
4680
- QUEUE_PATH = path8.join(os6.homedir(), ".exe-os", "intercom-queue.json");
4731
+ QUEUE_PATH = path9.join(os6.homedir(), ".exe-os", "intercom-queue.json");
4681
4732
  TTL_MS = 60 * 60 * 1e3;
4682
- INTERCOM_LOG = path8.join(os6.homedir(), ".exe-os", "intercom.log");
4733
+ INTERCOM_LOG = path9.join(os6.homedir(), ".exe-os", "intercom.log");
4683
4734
  }
4684
4735
  });
4685
4736
 
4686
4737
  // src/lib/license.ts
4687
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
4738
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync6 } from "fs";
4688
4739
  import { randomUUID as randomUUID11 } from "crypto";
4689
- import path9 from "path";
4740
+ import path10 from "path";
4690
4741
  import { jwtVerify, importSPKI } from "jose";
4691
4742
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
4692
4743
  var init_license = __esm({
4693
4744
  "src/lib/license.ts"() {
4694
4745
  "use strict";
4695
4746
  init_config();
4696
- LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
4697
- CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
4698
- DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
4747
+ LICENSE_PATH = path10.join(EXE_AI_DIR, "license.key");
4748
+ CACHE_PATH = path10.join(EXE_AI_DIR, "license-cache.json");
4749
+ DEVICE_ID_PATH = path10.join(EXE_AI_DIR, "device-id");
4699
4750
  PLAN_LIMITS = {
4700
4751
  free: { devices: 1, employees: 1, memories: 5e3 },
4701
4752
  pro: { devices: 3, employees: 5, memories: 1e5 },
@@ -4707,12 +4758,12 @@ var init_license = __esm({
4707
4758
  });
4708
4759
 
4709
4760
  // src/lib/plan-limits.ts
4710
- import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
4711
- import path10 from "path";
4761
+ import { readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
4762
+ import path11 from "path";
4712
4763
  function getLicenseSync() {
4713
4764
  try {
4714
- if (!existsSync9(CACHE_PATH2)) return freeLicense();
4715
- const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
4765
+ if (!existsSync10(CACHE_PATH2)) return freeLicense();
4766
+ const raw = JSON.parse(readFileSync9(CACHE_PATH2, "utf8"));
4716
4767
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
4717
4768
  const parts = raw.token.split(".");
4718
4769
  if (parts.length !== 3) return freeLicense();
@@ -4750,8 +4801,8 @@ function assertEmployeeLimitSync(rosterPath) {
4750
4801
  const filePath = rosterPath ?? EMPLOYEES_PATH;
4751
4802
  let count = 0;
4752
4803
  try {
4753
- if (existsSync9(filePath)) {
4754
- const raw = readFileSync8(filePath, "utf8");
4804
+ if (existsSync10(filePath)) {
4805
+ const raw = readFileSync9(filePath, "utf8");
4755
4806
  const employees = JSON.parse(raw);
4756
4807
  count = Array.isArray(employees) ? employees.length : 0;
4757
4808
  }
@@ -4780,19 +4831,19 @@ var init_plan_limits = __esm({
4780
4831
  this.name = "PlanLimitError";
4781
4832
  }
4782
4833
  };
4783
- CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
4834
+ CACHE_PATH2 = path11.join(EXE_AI_DIR, "license-cache.json");
4784
4835
  }
4785
4836
  });
4786
4837
 
4787
4838
  // src/lib/notifications.ts
4788
4839
  import crypto3 from "crypto";
4789
- import path11 from "path";
4840
+ import path12 from "path";
4790
4841
  import os7 from "os";
4791
4842
  import {
4792
- readFileSync as readFileSync9,
4843
+ readFileSync as readFileSync10,
4793
4844
  readdirSync as readdirSync2,
4794
4845
  unlinkSync as unlinkSync3,
4795
- existsSync as existsSync10,
4846
+ existsSync as existsSync11,
4796
4847
  rmdirSync
4797
4848
  } from "fs";
4798
4849
  async function writeNotification(notification) {
@@ -4896,11 +4947,11 @@ var init_task_scope = __esm({
4896
4947
 
4897
4948
  // src/lib/tasks-crud.ts
4898
4949
  import crypto5 from "crypto";
4899
- import path12 from "path";
4950
+ import path13 from "path";
4900
4951
  import os8 from "os";
4901
4952
  import { execSync as execSync4 } from "child_process";
4902
4953
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
4903
- import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
4954
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
4904
4955
  async function writeCheckpoint(input) {
4905
4956
  const client = getClient();
4906
4957
  const row = await resolveTask(client, input.taskId);
@@ -5075,8 +5126,8 @@ ${laneWarning}` : laneWarning;
5075
5126
  }
5076
5127
  if (input.baseDir) {
5077
5128
  try {
5078
- await mkdir4(path12.join(input.baseDir, "exe", "output"), { recursive: true });
5079
- await mkdir4(path12.join(input.baseDir, "exe", "research"), { recursive: true });
5129
+ await mkdir4(path13.join(input.baseDir, "exe", "output"), { recursive: true });
5130
+ await mkdir4(path13.join(input.baseDir, "exe", "research"), { recursive: true });
5080
5131
  await ensureArchitectureDoc(input.baseDir, input.projectName);
5081
5132
  await ensureGitignoreExe(input.baseDir);
5082
5133
  } catch {
@@ -5112,10 +5163,10 @@ ${laneWarning}` : laneWarning;
5112
5163
  });
5113
5164
  if (input.baseDir) {
5114
5165
  try {
5115
- const EXE_OS_DIR = path12.join(os8.homedir(), ".exe-os");
5116
- const mdPath = path12.join(EXE_OS_DIR, taskFile);
5117
- const mdDir = path12.dirname(mdPath);
5118
- if (!existsSync11(mdDir)) await mkdir4(mdDir, { recursive: true });
5166
+ const EXE_OS_DIR = path13.join(os8.homedir(), ".exe-os");
5167
+ const mdPath = path13.join(EXE_OS_DIR, taskFile);
5168
+ const mdDir = path13.dirname(mdPath);
5169
+ if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
5119
5170
  const reviewer = input.reviewer ?? input.assignedBy;
5120
5171
  const mdContent = `# ${input.title}
5121
5172
 
@@ -5140,7 +5191,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
5140
5191
  Do NOT let a failed commit or any error prevent you from calling update_task(done).
5141
5192
  `;
5142
5193
  await writeFile4(mdPath, mdContent, "utf-8");
5143
- } catch {
5194
+ } catch (err) {
5195
+ process.stderr.write(
5196
+ `[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
5197
+ `
5198
+ );
5144
5199
  }
5145
5200
  }
5146
5201
  return {
@@ -5400,9 +5455,9 @@ async function deleteTaskCore(taskId, _baseDir) {
5400
5455
  return { taskFile, assignedTo, assignedBy, taskSlug };
5401
5456
  }
5402
5457
  async function ensureArchitectureDoc(baseDir, projectName) {
5403
- const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
5458
+ const archPath = path13.join(baseDir, "exe", "ARCHITECTURE.md");
5404
5459
  try {
5405
- if (existsSync11(archPath)) return;
5460
+ if (existsSync12(archPath)) return;
5406
5461
  const template = [
5407
5462
  `# ${projectName} \u2014 System Architecture`,
5408
5463
  "",
@@ -5435,10 +5490,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
5435
5490
  }
5436
5491
  }
5437
5492
  async function ensureGitignoreExe(baseDir) {
5438
- const gitignorePath = path12.join(baseDir, ".gitignore");
5493
+ const gitignorePath = path13.join(baseDir, ".gitignore");
5439
5494
  try {
5440
- if (existsSync11(gitignorePath)) {
5441
- const content = readFileSync10(gitignorePath, "utf-8");
5495
+ if (existsSync12(gitignorePath)) {
5496
+ const content = readFileSync11(gitignorePath, "utf-8");
5442
5497
  if (/^\/?exe\/?$/m.test(content)) return;
5443
5498
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
5444
5499
  } else {
@@ -5469,8 +5524,8 @@ var init_tasks_crud = __esm({
5469
5524
  });
5470
5525
 
5471
5526
  // src/lib/tasks-review.ts
5472
- import path13 from "path";
5473
- import { existsSync as existsSync12, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
5527
+ import path14 from "path";
5528
+ import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
5474
5529
  async function countPendingReviews(sessionScope) {
5475
5530
  const client = getClient();
5476
5531
  if (sessionScope) {
@@ -5651,11 +5706,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
5651
5706
  );
5652
5707
  }
5653
5708
  try {
5654
- const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
5655
- if (existsSync12(cacheDir)) {
5709
+ const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
5710
+ if (existsSync13(cacheDir)) {
5656
5711
  for (const f of readdirSync3(cacheDir)) {
5657
5712
  if (f.startsWith("review-notified-")) {
5658
- unlinkSync4(path13.join(cacheDir, f));
5713
+ unlinkSync4(path14.join(cacheDir, f));
5659
5714
  }
5660
5715
  }
5661
5716
  }
@@ -5676,7 +5731,7 @@ var init_tasks_review = __esm({
5676
5731
  });
5677
5732
 
5678
5733
  // src/lib/tasks-chain.ts
5679
- import path14 from "path";
5734
+ import path15 from "path";
5680
5735
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
5681
5736
  async function cascadeUnblock(taskId, baseDir, now) {
5682
5737
  const client = getClient();
@@ -5693,7 +5748,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
5693
5748
  });
5694
5749
  for (const ur of unblockedRows.rows) {
5695
5750
  try {
5696
- const ubFile = path14.join(baseDir, String(ur.task_file));
5751
+ const ubFile = path15.join(baseDir, String(ur.task_file));
5697
5752
  let ubContent = await readFile4(ubFile, "utf-8");
5698
5753
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
5699
5754
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -5762,7 +5817,7 @@ var init_tasks_chain = __esm({
5762
5817
 
5763
5818
  // src/lib/project-name.ts
5764
5819
  import { execSync as execSync5 } from "child_process";
5765
- import path15 from "path";
5820
+ import path16 from "path";
5766
5821
  function getProjectName(cwd) {
5767
5822
  const dir = cwd ?? process.cwd();
5768
5823
  if (_cached2 && _cachedCwd === dir) return _cached2;
@@ -5775,7 +5830,7 @@ function getProjectName(cwd) {
5775
5830
  timeout: 2e3,
5776
5831
  stdio: ["pipe", "pipe", "pipe"]
5777
5832
  }).trim();
5778
- repoRoot = path15.dirname(gitCommonDir);
5833
+ repoRoot = path16.dirname(gitCommonDir);
5779
5834
  } catch {
5780
5835
  repoRoot = execSync5("git rev-parse --show-toplevel", {
5781
5836
  cwd: dir,
@@ -5784,11 +5839,11 @@ function getProjectName(cwd) {
5784
5839
  stdio: ["pipe", "pipe", "pipe"]
5785
5840
  }).trim();
5786
5841
  }
5787
- _cached2 = path15.basename(repoRoot);
5842
+ _cached2 = path16.basename(repoRoot);
5788
5843
  _cachedCwd = dir;
5789
5844
  return _cached2;
5790
5845
  } catch {
5791
- _cached2 = path15.basename(dir);
5846
+ _cached2 = path16.basename(dir);
5792
5847
  _cachedCwd = dir;
5793
5848
  return _cached2;
5794
5849
  }
@@ -6261,8 +6316,8 @@ __export(tasks_exports, {
6261
6316
  updateTaskStatus: () => updateTaskStatus,
6262
6317
  writeCheckpoint: () => writeCheckpoint
6263
6318
  });
6264
- import path16 from "path";
6265
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
6319
+ import path17 from "path";
6320
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, unlinkSync as unlinkSync5 } from "fs";
6266
6321
  async function createTask(input) {
6267
6322
  const result = await createTaskCore(input);
6268
6323
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -6281,11 +6336,11 @@ async function updateTask(input) {
6281
6336
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
6282
6337
  try {
6283
6338
  const agent = String(row.assigned_to);
6284
- const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
6285
- const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
6339
+ const cacheDir = path17.join(EXE_AI_DIR, "session-cache");
6340
+ const cachePath = path17.join(cacheDir, `current-task-${agent}.json`);
6286
6341
  if (input.status === "in_progress") {
6287
- mkdirSync6(cacheDir, { recursive: true });
6288
- writeFileSync5(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
6342
+ mkdirSync7(cacheDir, { recursive: true });
6343
+ writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
6289
6344
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
6290
6345
  try {
6291
6346
  unlinkSync5(cachePath);
@@ -6752,13 +6807,13 @@ __export(tmux_routing_exports, {
6752
6807
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
6753
6808
  });
6754
6809
  import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
6755
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, mkdirSync as mkdirSync7, existsSync as existsSync13, appendFileSync } from "fs";
6756
- import path17 from "path";
6810
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, existsSync as existsSync14, appendFileSync } from "fs";
6811
+ import path18 from "path";
6757
6812
  import os9 from "os";
6758
6813
  import { fileURLToPath as fileURLToPath2 } from "url";
6759
6814
  import { unlinkSync as unlinkSync6 } from "fs";
6760
6815
  function spawnLockPath(sessionName) {
6761
- return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
6816
+ return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
6762
6817
  }
6763
6818
  function isProcessAlive(pid) {
6764
6819
  try {
@@ -6769,13 +6824,13 @@ function isProcessAlive(pid) {
6769
6824
  }
6770
6825
  }
6771
6826
  function acquireSpawnLock2(sessionName) {
6772
- if (!existsSync13(SPAWN_LOCK_DIR)) {
6773
- mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
6827
+ if (!existsSync14(SPAWN_LOCK_DIR)) {
6828
+ mkdirSync8(SPAWN_LOCK_DIR, { recursive: true });
6774
6829
  }
6775
6830
  const lockFile = spawnLockPath(sessionName);
6776
- if (existsSync13(lockFile)) {
6831
+ if (existsSync14(lockFile)) {
6777
6832
  try {
6778
- const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
6833
+ const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
6779
6834
  const age = Date.now() - lock.timestamp;
6780
6835
  if (isProcessAlive(lock.pid) && age < 6e4) {
6781
6836
  return false;
@@ -6783,7 +6838,7 @@ function acquireSpawnLock2(sessionName) {
6783
6838
  } catch {
6784
6839
  }
6785
6840
  }
6786
- writeFileSync6(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
6841
+ writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
6787
6842
  return true;
6788
6843
  }
6789
6844
  function releaseSpawnLock2(sessionName) {
@@ -6795,13 +6850,13 @@ function releaseSpawnLock2(sessionName) {
6795
6850
  function resolveBehaviorsExporterScript() {
6796
6851
  try {
6797
6852
  const thisFile = fileURLToPath2(import.meta.url);
6798
- const scriptPath = path17.join(
6799
- path17.dirname(thisFile),
6853
+ const scriptPath = path18.join(
6854
+ path18.dirname(thisFile),
6800
6855
  "..",
6801
6856
  "bin",
6802
6857
  "exe-export-behaviors.js"
6803
6858
  );
6804
- return existsSync13(scriptPath) ? scriptPath : null;
6859
+ return existsSync14(scriptPath) ? scriptPath : null;
6805
6860
  } catch {
6806
6861
  return null;
6807
6862
  }
@@ -6867,12 +6922,12 @@ function extractRootExe(name) {
6867
6922
  return parts.length > 0 ? parts[parts.length - 1] : null;
6868
6923
  }
6869
6924
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
6870
- if (!existsSync13(SESSION_CACHE)) {
6871
- mkdirSync7(SESSION_CACHE, { recursive: true });
6925
+ if (!existsSync14(SESSION_CACHE)) {
6926
+ mkdirSync8(SESSION_CACHE, { recursive: true });
6872
6927
  }
6873
6928
  const rootExe = extractRootExe(parentExe) ?? parentExe;
6874
- const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
6875
- writeFileSync6(filePath, JSON.stringify({
6929
+ const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
6930
+ writeFileSync7(filePath, JSON.stringify({
6876
6931
  parentExe: rootExe,
6877
6932
  dispatchedBy: dispatchedBy || rootExe,
6878
6933
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -6880,7 +6935,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
6880
6935
  }
6881
6936
  function getParentExe(sessionKey) {
6882
6937
  try {
6883
- const data = JSON.parse(readFileSync11(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
6938
+ const data = JSON.parse(readFileSync12(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
6884
6939
  return data.parentExe || null;
6885
6940
  } catch {
6886
6941
  return null;
@@ -6888,8 +6943,8 @@ function getParentExe(sessionKey) {
6888
6943
  }
6889
6944
  function getDispatchedBy(sessionKey) {
6890
6945
  try {
6891
- const data = JSON.parse(readFileSync11(
6892
- path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
6946
+ const data = JSON.parse(readFileSync12(
6947
+ path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
6893
6948
  "utf8"
6894
6949
  ));
6895
6950
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -6950,32 +7005,50 @@ async function verifyPaneAtCapacity(sessionName) {
6950
7005
  }
6951
7006
  function readDebounceState() {
6952
7007
  try {
6953
- if (!existsSync13(DEBOUNCE_FILE)) return {};
6954
- return JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
7008
+ if (!existsSync14(DEBOUNCE_FILE)) return {};
7009
+ const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
7010
+ const state = {};
7011
+ for (const [key, val] of Object.entries(raw)) {
7012
+ if (typeof val === "number") {
7013
+ state[key] = { lastSent: val, pending: 0 };
7014
+ } else if (val && typeof val === "object" && "lastSent" in val) {
7015
+ state[key] = val;
7016
+ }
7017
+ }
7018
+ return state;
6955
7019
  } catch {
6956
7020
  return {};
6957
7021
  }
6958
7022
  }
6959
7023
  function writeDebounceState(state) {
6960
7024
  try {
6961
- if (!existsSync13(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
6962
- writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
7025
+ if (!existsSync14(SESSION_CACHE)) mkdirSync8(SESSION_CACHE, { recursive: true });
7026
+ writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
6963
7027
  } catch {
6964
7028
  }
6965
7029
  }
6966
7030
  function isDebounced(targetSession) {
6967
7031
  const state = readDebounceState();
6968
- const lastSent = state[targetSession] ?? 0;
6969
- return Date.now() - lastSent < INTERCOM_DEBOUNCE_MS;
7032
+ const entry = state[targetSession];
7033
+ const lastSent = entry?.lastSent ?? 0;
7034
+ if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
7035
+ if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
7036
+ state[targetSession].pending++;
7037
+ writeDebounceState(state);
7038
+ return true;
7039
+ }
7040
+ return false;
6970
7041
  }
6971
7042
  function recordDebounce(targetSession) {
6972
7043
  const state = readDebounceState();
6973
- state[targetSession] = Date.now();
7044
+ const batched = state[targetSession]?.pending ?? 0;
7045
+ state[targetSession] = { lastSent: Date.now(), pending: 0 };
6974
7046
  const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
6975
7047
  for (const key of Object.keys(state)) {
6976
- if ((state[key] ?? 0) < cutoff) delete state[key];
7048
+ if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
6977
7049
  }
6978
7050
  writeDebounceState(state);
7051
+ return batched;
6979
7052
  }
6980
7053
  function logIntercom(msg) {
6981
7054
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
@@ -7020,7 +7093,7 @@ function sendIntercom(targetSession) {
7020
7093
  return "skipped_exe";
7021
7094
  }
7022
7095
  if (isDebounced(targetSession)) {
7023
- logIntercom(`DEBOUNCE \u2192 ${targetSession} (cross-process file debounce)`);
7096
+ logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
7024
7097
  return "debounced";
7025
7098
  }
7026
7099
  try {
@@ -7032,14 +7105,14 @@ function sendIntercom(targetSession) {
7032
7105
  const sessionState = getSessionState(targetSession);
7033
7106
  if (sessionState === "no_claude") {
7034
7107
  queueIntercom(targetSession, "claude not running in session");
7035
- recordDebounce(targetSession);
7036
- logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
7108
+ const batched2 = recordDebounce(targetSession);
7109
+ logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
7037
7110
  return "queued";
7038
7111
  }
7039
7112
  if (sessionState === "thinking" || sessionState === "tool") {
7040
7113
  queueIntercom(targetSession, "session busy at send time");
7041
- recordDebounce(targetSession);
7042
- logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
7114
+ const batched2 = recordDebounce(targetSession);
7115
+ logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
7043
7116
  return "queued";
7044
7117
  }
7045
7118
  if (transport.isPaneInCopyMode(targetSession)) {
@@ -7047,8 +7120,8 @@ function sendIntercom(targetSession) {
7047
7120
  transport.sendKeys(targetSession, "q");
7048
7121
  }
7049
7122
  transport.sendKeys(targetSession, "/exe-intercom");
7050
- recordDebounce(targetSession);
7051
- logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
7123
+ const batched = recordDebounce(targetSession);
7124
+ logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
7052
7125
  return "delivered";
7053
7126
  } catch {
7054
7127
  logIntercom(`FAIL \u2192 ${targetSession}`);
@@ -7150,26 +7223,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7150
7223
  const transport = getTransport();
7151
7224
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
7152
7225
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
7153
- const logDir = path17.join(os9.homedir(), ".exe-os", "session-logs");
7154
- const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
7155
- if (!existsSync13(logDir)) {
7156
- mkdirSync7(logDir, { recursive: true });
7226
+ const logDir = path18.join(os9.homedir(), ".exe-os", "session-logs");
7227
+ const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
7228
+ if (!existsSync14(logDir)) {
7229
+ mkdirSync8(logDir, { recursive: true });
7157
7230
  }
7158
7231
  transport.kill(sessionName);
7159
7232
  let cleanupSuffix = "";
7160
7233
  try {
7161
7234
  const thisFile = fileURLToPath2(import.meta.url);
7162
- const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
7163
- if (existsSync13(cleanupScript)) {
7235
+ const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
7236
+ if (existsSync14(cleanupScript)) {
7164
7237
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
7165
7238
  }
7166
7239
  } catch {
7167
7240
  }
7168
7241
  try {
7169
- const claudeJsonPath = path17.join(os9.homedir(), ".claude.json");
7242
+ const claudeJsonPath = path18.join(os9.homedir(), ".claude.json");
7170
7243
  let claudeJson = {};
7171
7244
  try {
7172
- claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
7245
+ claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
7173
7246
  } catch {
7174
7247
  }
7175
7248
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -7177,17 +7250,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7177
7250
  const trustDir = opts?.cwd ?? projectDir;
7178
7251
  if (!projects[trustDir]) projects[trustDir] = {};
7179
7252
  projects[trustDir].hasTrustDialogAccepted = true;
7180
- writeFileSync6(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
7253
+ writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
7181
7254
  } catch {
7182
7255
  }
7183
7256
  try {
7184
- const settingsDir = path17.join(os9.homedir(), ".claude", "projects");
7257
+ const settingsDir = path18.join(os9.homedir(), ".claude", "projects");
7185
7258
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
7186
- const projSettingsDir = path17.join(settingsDir, normalizedKey);
7187
- const settingsPath = path17.join(projSettingsDir, "settings.json");
7259
+ const projSettingsDir = path18.join(settingsDir, normalizedKey);
7260
+ const settingsPath = path18.join(projSettingsDir, "settings.json");
7188
7261
  let settings = {};
7189
7262
  try {
7190
- settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
7263
+ settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
7191
7264
  } catch {
7192
7265
  }
7193
7266
  const perms = settings.permissions ?? {};
@@ -7215,20 +7288,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7215
7288
  if (changed) {
7216
7289
  perms.allow = allow;
7217
7290
  settings.permissions = perms;
7218
- mkdirSync7(projSettingsDir, { recursive: true });
7219
- writeFileSync6(settingsPath, JSON.stringify(settings, null, 2) + "\n");
7291
+ mkdirSync8(projSettingsDir, { recursive: true });
7292
+ writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
7220
7293
  }
7221
7294
  } catch {
7222
7295
  }
7223
7296
  const spawnCwd = opts?.cwd ?? projectDir;
7224
7297
  const useExeAgent = !!(opts?.model && opts?.provider);
7225
- const ccProvider = useExeAgent ? DEFAULT_PROVIDER : detectActiveProvider();
7298
+ const agentRtConfig = getAgentRuntime(employeeName);
7299
+ const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
7300
+ const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
7301
+ const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
7226
7302
  const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
7227
7303
  let identityFlag = "";
7228
7304
  let behaviorsFlag = "";
7229
7305
  let legacyFallbackWarned = false;
7230
7306
  if (!useExeAgent && !useBinSymlink) {
7231
- const identityPath = path17.join(
7307
+ const identityPath = path18.join(
7232
7308
  os9.homedir(),
7233
7309
  ".exe-os",
7234
7310
  "identity",
@@ -7238,13 +7314,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7238
7314
  const hasAgentFlag = claudeSupportsAgentFlag();
7239
7315
  if (hasAgentFlag) {
7240
7316
  identityFlag = ` --agent ${employeeName}`;
7241
- } else if (existsSync13(identityPath)) {
7317
+ } else if (existsSync14(identityPath)) {
7242
7318
  identityFlag = ` --append-system-prompt-file ${identityPath}`;
7243
7319
  legacyFallbackWarned = true;
7244
7320
  }
7245
7321
  const behaviorsFile = exportBehaviorsSync(
7246
7322
  employeeName,
7247
- path17.basename(spawnCwd),
7323
+ path18.basename(spawnCwd),
7248
7324
  sessionName
7249
7325
  );
7250
7326
  if (behaviorsFile) {
@@ -7259,16 +7335,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7259
7335
  }
7260
7336
  let sessionContextFlag = "";
7261
7337
  try {
7262
- const ctxDir = path17.join(os9.homedir(), ".exe-os", "session-cache");
7263
- mkdirSync7(ctxDir, { recursive: true });
7264
- const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
7338
+ const ctxDir = path18.join(os9.homedir(), ".exe-os", "session-cache");
7339
+ mkdirSync8(ctxDir, { recursive: true });
7340
+ const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
7265
7341
  const ctxContent = [
7266
7342
  `## Session Context`,
7267
7343
  `You are running in tmux session: ${sessionName}.`,
7268
7344
  `Your parent coordinator session is ${exeSession}.`,
7269
7345
  `Your employees (if any) use the -${exeSession} suffix.`
7270
7346
  ].join("\n");
7271
- writeFileSync6(ctxFile, ctxContent);
7347
+ writeFileSync7(ctxFile, ctxContent);
7272
7348
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
7273
7349
  } catch {
7274
7350
  }
@@ -7282,9 +7358,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7282
7358
  }
7283
7359
  }
7284
7360
  }
7361
+ if (useCodex) {
7362
+ const codexCfg = RUNTIME_TABLE.codex;
7363
+ if (codexCfg?.apiKeyEnv) {
7364
+ const keyVal = process.env[codexCfg.apiKeyEnv];
7365
+ if (keyVal) {
7366
+ envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
7367
+ }
7368
+ }
7369
+ envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
7370
+ }
7371
+ if (useOpencode) {
7372
+ const ocCfg = PROVIDER_TABLE.opencode;
7373
+ if (ocCfg?.apiKeyEnv) {
7374
+ const keyVal = process.env[ocCfg.apiKeyEnv];
7375
+ if (keyVal) {
7376
+ envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
7377
+ }
7378
+ }
7379
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
7380
+ }
7381
+ if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
7382
+ const defaultClaudeModel = DEFAULT_MODELS.claude;
7383
+ if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
7384
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
7385
+ }
7386
+ }
7285
7387
  let spawnCommand;
7286
7388
  if (useExeAgent) {
7287
7389
  spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
7390
+ } else if (useCodex) {
7391
+ process.stderr.write(
7392
+ `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
7393
+ `
7394
+ );
7395
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
7396
+ } else if (useOpencode) {
7397
+ const binName = `${employeeName}-opencode`;
7398
+ process.stderr.write(
7399
+ `[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
7400
+ `
7401
+ );
7402
+ spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
7288
7403
  } else if (useBinSymlink) {
7289
7404
  const binName = `${employeeName}-${ccProvider}`;
7290
7405
  process.stderr.write(
@@ -7306,11 +7421,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7306
7421
  transport.pipeLog(sessionName, logFile);
7307
7422
  try {
7308
7423
  const mySession = getMySession();
7309
- const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
7310
- writeFileSync6(dispatchInfo, JSON.stringify({
7424
+ const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
7425
+ writeFileSync7(dispatchInfo, JSON.stringify({
7311
7426
  dispatchedBy: mySession,
7312
7427
  rootExe: exeSession,
7313
- provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
7428
+ provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
7429
+ runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
7430
+ model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
7314
7431
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
7315
7432
  }));
7316
7433
  } catch {
@@ -7328,6 +7445,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7328
7445
  booted = true;
7329
7446
  break;
7330
7447
  }
7448
+ } else if (useCodex) {
7449
+ if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
7450
+ booted = true;
7451
+ break;
7452
+ }
7331
7453
  } else {
7332
7454
  if (pane.includes("Claude Code") || pane.includes("\u276F")) {
7333
7455
  booted = true;
@@ -7339,9 +7461,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7339
7461
  }
7340
7462
  if (!booted) {
7341
7463
  releaseSpawnLock2(sessionName);
7342
- return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
7464
+ const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
7465
+ return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
7343
7466
  }
7344
- if (!useExeAgent) {
7467
+ if (!useExeAgent && !useCodex) {
7345
7468
  try {
7346
7469
  transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
7347
7470
  } catch {
@@ -7368,17 +7491,19 @@ var init_tmux_routing = __esm({
7368
7491
  init_cc_agent_support();
7369
7492
  init_mcp_prefix();
7370
7493
  init_provider_table();
7494
+ init_agent_config();
7495
+ init_runtime_table();
7371
7496
  init_intercom_queue();
7372
7497
  init_plan_limits();
7373
7498
  init_employees();
7374
- SPAWN_LOCK_DIR = path17.join(os9.homedir(), ".exe-os", "spawn-locks");
7375
- SESSION_CACHE = path17.join(os9.homedir(), ".exe-os", "session-cache");
7499
+ SPAWN_LOCK_DIR = path18.join(os9.homedir(), ".exe-os", "spawn-locks");
7500
+ SESSION_CACHE = path18.join(os9.homedir(), ".exe-os", "session-cache");
7376
7501
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
7377
7502
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
7378
7503
  VERIFY_PANE_LINES = 200;
7379
7504
  INTERCOM_DEBOUNCE_MS = 3e4;
7380
- INTERCOM_LOG2 = path17.join(os9.homedir(), ".exe-os", "intercom.log");
7381
- DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
7505
+ INTERCOM_LOG2 = path18.join(os9.homedir(), ".exe-os", "intercom.log");
7506
+ DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
7382
7507
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
7383
7508
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
7384
7509
  }
@@ -11410,11 +11535,11 @@ async function ensureCRMContact(info) {
11410
11535
  }
11411
11536
 
11412
11537
  // src/automation/trigger-engine.ts
11413
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
11538
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as existsSync15, mkdirSync as mkdirSync9 } from "fs";
11414
11539
  import { randomUUID as randomUUID12 } from "crypto";
11415
- import path18 from "path";
11540
+ import path19 from "path";
11416
11541
  import os10 from "os";
11417
- var TRIGGERS_PATH = path18.join(os10.homedir(), ".exe-os", "triggers.json");
11542
+ var TRIGGERS_PATH = path19.join(os10.homedir(), ".exe-os", "triggers.json");
11418
11543
  var GRAPH_API_VERSION = "v21.0";
11419
11544
  function substituteTemplate(template, record) {
11420
11545
  return template.replace(
@@ -11468,9 +11593,9 @@ function evaluateConditions(conditions, record) {
11468
11593
  return conditions.every((c) => evaluateCondition(c, record));
11469
11594
  }
11470
11595
  function loadTriggers(project) {
11471
- if (!existsSync14(TRIGGERS_PATH)) return [];
11596
+ if (!existsSync15(TRIGGERS_PATH)) return [];
11472
11597
  try {
11473
- const raw = readFileSync12(TRIGGERS_PATH, "utf-8");
11598
+ const raw = readFileSync13(TRIGGERS_PATH, "utf-8");
11474
11599
  const all = JSON.parse(raw);
11475
11600
  if (!Array.isArray(all)) return [];
11476
11601
  if (project) {