@askexenow/exe-os 0.8.85 → 0.8.87

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 +510 -340
  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 +50 -5
  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
@@ -679,10 +679,10 @@ async function disposeEmbedder() {
679
679
  async function embedDirect(text) {
680
680
  const llamaCpp = await import("node-llama-cpp");
681
681
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
682
- const { existsSync: existsSync30 } = await import("fs");
683
- const path37 = await import("path");
684
- const modelPath = path37.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
685
- if (!existsSync30(modelPath)) {
682
+ const { existsSync: existsSync31 } = await import("fs");
683
+ const path38 = await import("path");
684
+ const modelPath = path38.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
685
+ if (!existsSync31(modelPath)) {
686
686
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
687
687
  }
688
688
  const llama = await llamaCpp.getLlama();
@@ -4425,6 +4425,7 @@ __export(hybrid_search_exports, {
4425
4425
  estimateCardinality: () => estimateCardinality,
4426
4426
  hybridSearch: () => hybridSearch,
4427
4427
  lightweightSearch: () => lightweightSearch,
4428
+ recentRecords: () => recentRecords,
4428
4429
  rrfMerge: () => rrfMerge,
4429
4430
  rrfMergeMulti: () => rrfMergeMulti
4430
4431
  });
@@ -4485,7 +4486,7 @@ async function hybridSearch(queryText, agentId, options) {
4485
4486
  process.stderr.write("[hybrid-search] Embed daemon unavailable \u2014 FTS-only mode\n");
4486
4487
  }
4487
4488
  let grepPromise = Promise.resolve([]);
4488
- if (config2.fileGrepEnabled !== false) {
4489
+ if (config2.fileGrepEnabled === true) {
4489
4490
  try {
4490
4491
  const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
4491
4492
  const projectRoot = process.cwd();
@@ -4754,7 +4755,7 @@ async function ftsQuery(client, matchExpr, agentId, options, limit) {
4754
4755
  source_type: row.source_type ?? null
4755
4756
  }));
4756
4757
  }
4757
- async function recentRecords(agentId, options, limit) {
4758
+ async function recentRecords(agentId, options, limit, textFilter) {
4758
4759
  const client = getClient();
4759
4760
  const statusFilter = options?.includeArchived ? "" : `
4760
4761
  AND COALESCE(status, 'active') = 'active'`;
@@ -4794,6 +4795,10 @@ async function recentRecords(agentId, options, limit) {
4794
4795
  sql += ` AND memory_type = ?`;
4795
4796
  args.push(options.memoryType);
4796
4797
  }
4798
+ if (textFilter) {
4799
+ sql += ` AND raw_text LIKE '%' || ? || '%'`;
4800
+ args.push(textFilter);
4801
+ }
4797
4802
  sql += ` ORDER BY timestamp DESC LIMIT ?`;
4798
4803
  args.push(limit);
4799
4804
  const result = await client.execute({ sql, args });
@@ -5910,18 +5915,98 @@ var init_provider_table = __esm({
5910
5915
  }
5911
5916
  });
5912
5917
 
5913
- // src/lib/intercom-queue.ts
5914
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, renameSync as renameSync3, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
5918
+ // src/lib/runtime-table.ts
5919
+ var RUNTIME_TABLE, DEFAULT_RUNTIME;
5920
+ var init_runtime_table = __esm({
5921
+ "src/lib/runtime-table.ts"() {
5922
+ "use strict";
5923
+ RUNTIME_TABLE = {
5924
+ codex: {
5925
+ binary: "codex",
5926
+ launchMode: "exec",
5927
+ autoApproveFlag: "--full-auto",
5928
+ inlineFlag: "--no-alt-screen",
5929
+ apiKeyEnv: "OPENAI_API_KEY",
5930
+ defaultModel: "gpt-5.4"
5931
+ }
5932
+ };
5933
+ DEFAULT_RUNTIME = "claude";
5934
+ }
5935
+ });
5936
+
5937
+ // src/lib/agent-config.ts
5938
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "fs";
5915
5939
  import path16 from "path";
5940
+ function loadAgentConfig() {
5941
+ if (!existsSync12(AGENT_CONFIG_PATH)) return {};
5942
+ try {
5943
+ return JSON.parse(readFileSync10(AGENT_CONFIG_PATH, "utf-8"));
5944
+ } catch {
5945
+ return {};
5946
+ }
5947
+ }
5948
+ function saveAgentConfig(config2) {
5949
+ const dir = path16.dirname(AGENT_CONFIG_PATH);
5950
+ if (!existsSync12(dir)) mkdirSync5(dir, { recursive: true });
5951
+ writeFileSync7(AGENT_CONFIG_PATH, JSON.stringify(config2, null, 2) + "\n", "utf-8");
5952
+ }
5953
+ function getAgentRuntime(agentId) {
5954
+ const config2 = loadAgentConfig();
5955
+ const entry = config2[agentId];
5956
+ if (entry) return entry;
5957
+ return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
5958
+ }
5959
+ function setAgentRuntime(agentId, runtime, model) {
5960
+ const knownModels = KNOWN_RUNTIMES[runtime];
5961
+ if (!knownModels) {
5962
+ return {
5963
+ ok: false,
5964
+ error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
5965
+ };
5966
+ }
5967
+ if (!knownModels.includes(model)) {
5968
+ return {
5969
+ ok: false,
5970
+ error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
5971
+ };
5972
+ }
5973
+ const config2 = loadAgentConfig();
5974
+ config2[agentId] = { runtime, model };
5975
+ saveAgentConfig(config2);
5976
+ return { ok: true };
5977
+ }
5978
+ var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, DEFAULT_MODELS;
5979
+ var init_agent_config = __esm({
5980
+ "src/lib/agent-config.ts"() {
5981
+ "use strict";
5982
+ init_config();
5983
+ init_runtime_table();
5984
+ AGENT_CONFIG_PATH = path16.join(EXE_AI_DIR, "agent-config.json");
5985
+ KNOWN_RUNTIMES = {
5986
+ claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-3.5"],
5987
+ codex: ["gpt-5.4", "gpt-5.5", "o3", "o4-mini"],
5988
+ opencode: ["minimax-m2.7"]
5989
+ };
5990
+ DEFAULT_MODELS = {
5991
+ claude: "claude-opus-4",
5992
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
5993
+ opencode: "minimax-m2.7"
5994
+ };
5995
+ }
5996
+ });
5997
+
5998
+ // src/lib/intercom-queue.ts
5999
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, renameSync as renameSync3, existsSync as existsSync13, mkdirSync as mkdirSync6 } from "fs";
6000
+ import path17 from "path";
5916
6001
  import os6 from "os";
5917
6002
  function ensureDir() {
5918
- const dir = path16.dirname(QUEUE_PATH);
5919
- if (!existsSync12(dir)) mkdirSync5(dir, { recursive: true });
6003
+ const dir = path17.dirname(QUEUE_PATH);
6004
+ if (!existsSync13(dir)) mkdirSync6(dir, { recursive: true });
5920
6005
  }
5921
6006
  function readQueue() {
5922
6007
  try {
5923
- if (!existsSync12(QUEUE_PATH)) return [];
5924
- return JSON.parse(readFileSync10(QUEUE_PATH, "utf8"));
6008
+ if (!existsSync13(QUEUE_PATH)) return [];
6009
+ return JSON.parse(readFileSync11(QUEUE_PATH, "utf8"));
5925
6010
  } catch {
5926
6011
  return [];
5927
6012
  }
@@ -5929,7 +6014,7 @@ function readQueue() {
5929
6014
  function writeQueue(queue) {
5930
6015
  ensureDir();
5931
6016
  const tmp = `${QUEUE_PATH}.tmp`;
5932
- writeFileSync7(tmp, JSON.stringify(queue, null, 2));
6017
+ writeFileSync8(tmp, JSON.stringify(queue, null, 2));
5933
6018
  renameSync3(tmp, QUEUE_PATH);
5934
6019
  }
5935
6020
  function queueIntercom(targetSession, reason) {
@@ -5953,9 +6038,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
5953
6038
  var init_intercom_queue = __esm({
5954
6039
  "src/lib/intercom-queue.ts"() {
5955
6040
  "use strict";
5956
- QUEUE_PATH = path16.join(os6.homedir(), ".exe-os", "intercom-queue.json");
6041
+ QUEUE_PATH = path17.join(os6.homedir(), ".exe-os", "intercom-queue.json");
5957
6042
  TTL_MS = 60 * 60 * 1e3;
5958
- INTERCOM_LOG = path16.join(os6.homedir(), ".exe-os", "intercom.log");
6043
+ INTERCOM_LOG = path17.join(os6.homedir(), ".exe-os", "intercom.log");
5959
6044
  }
5960
6045
  });
5961
6046
 
@@ -6333,13 +6418,13 @@ __export(tmux_routing_exports, {
6333
6418
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
6334
6419
  });
6335
6420
  import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
6336
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6, existsSync as existsSync13, appendFileSync } from "fs";
6337
- import path17 from "path";
6421
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync } from "fs";
6422
+ import path18 from "path";
6338
6423
  import os7 from "os";
6339
6424
  import { fileURLToPath as fileURLToPath2 } from "url";
6340
6425
  import { unlinkSync as unlinkSync5 } from "fs";
6341
6426
  function spawnLockPath(sessionName) {
6342
- return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
6427
+ return path18.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
6343
6428
  }
6344
6429
  function isProcessAlive(pid) {
6345
6430
  try {
@@ -6350,13 +6435,13 @@ function isProcessAlive(pid) {
6350
6435
  }
6351
6436
  }
6352
6437
  function acquireSpawnLock2(sessionName) {
6353
- if (!existsSync13(SPAWN_LOCK_DIR)) {
6354
- mkdirSync6(SPAWN_LOCK_DIR, { recursive: true });
6438
+ if (!existsSync14(SPAWN_LOCK_DIR)) {
6439
+ mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
6355
6440
  }
6356
6441
  const lockFile = spawnLockPath(sessionName);
6357
- if (existsSync13(lockFile)) {
6442
+ if (existsSync14(lockFile)) {
6358
6443
  try {
6359
- const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
6444
+ const lock = JSON.parse(readFileSync12(lockFile, "utf8"));
6360
6445
  const age = Date.now() - lock.timestamp;
6361
6446
  if (isProcessAlive(lock.pid) && age < 6e4) {
6362
6447
  return false;
@@ -6364,7 +6449,7 @@ function acquireSpawnLock2(sessionName) {
6364
6449
  } catch {
6365
6450
  }
6366
6451
  }
6367
- writeFileSync8(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
6452
+ writeFileSync9(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
6368
6453
  return true;
6369
6454
  }
6370
6455
  function releaseSpawnLock2(sessionName) {
@@ -6376,13 +6461,13 @@ function releaseSpawnLock2(sessionName) {
6376
6461
  function resolveBehaviorsExporterScript() {
6377
6462
  try {
6378
6463
  const thisFile = fileURLToPath2(import.meta.url);
6379
- const scriptPath = path17.join(
6380
- path17.dirname(thisFile),
6464
+ const scriptPath = path18.join(
6465
+ path18.dirname(thisFile),
6381
6466
  "..",
6382
6467
  "bin",
6383
6468
  "exe-export-behaviors.js"
6384
6469
  );
6385
- return existsSync13(scriptPath) ? scriptPath : null;
6470
+ return existsSync14(scriptPath) ? scriptPath : null;
6386
6471
  } catch {
6387
6472
  return null;
6388
6473
  }
@@ -6448,12 +6533,12 @@ function extractRootExe(name) {
6448
6533
  return parts.length > 0 ? parts[parts.length - 1] : null;
6449
6534
  }
6450
6535
  function registerParentExe(sessionKey, parentExe, dispatchedBy) {
6451
- if (!existsSync13(SESSION_CACHE)) {
6452
- mkdirSync6(SESSION_CACHE, { recursive: true });
6536
+ if (!existsSync14(SESSION_CACHE)) {
6537
+ mkdirSync7(SESSION_CACHE, { recursive: true });
6453
6538
  }
6454
6539
  const rootExe = extractRootExe(parentExe) ?? parentExe;
6455
- const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
6456
- writeFileSync8(filePath, JSON.stringify({
6540
+ const filePath = path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
6541
+ writeFileSync9(filePath, JSON.stringify({
6457
6542
  parentExe: rootExe,
6458
6543
  dispatchedBy: dispatchedBy || rootExe,
6459
6544
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -6461,7 +6546,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
6461
6546
  }
6462
6547
  function getParentExe(sessionKey) {
6463
6548
  try {
6464
- const data = JSON.parse(readFileSync11(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
6549
+ const data = JSON.parse(readFileSync12(path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
6465
6550
  return data.parentExe || null;
6466
6551
  } catch {
6467
6552
  return null;
@@ -6469,8 +6554,8 @@ function getParentExe(sessionKey) {
6469
6554
  }
6470
6555
  function getDispatchedBy(sessionKey) {
6471
6556
  try {
6472
- const data = JSON.parse(readFileSync11(
6473
- path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
6557
+ const data = JSON.parse(readFileSync12(
6558
+ path18.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
6474
6559
  "utf8"
6475
6560
  ));
6476
6561
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -6531,32 +6616,50 @@ async function verifyPaneAtCapacity(sessionName) {
6531
6616
  }
6532
6617
  function readDebounceState() {
6533
6618
  try {
6534
- if (!existsSync13(DEBOUNCE_FILE)) return {};
6535
- return JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
6619
+ if (!existsSync14(DEBOUNCE_FILE)) return {};
6620
+ const raw = JSON.parse(readFileSync12(DEBOUNCE_FILE, "utf8"));
6621
+ const state = {};
6622
+ for (const [key, val] of Object.entries(raw)) {
6623
+ if (typeof val === "number") {
6624
+ state[key] = { lastSent: val, pending: 0 };
6625
+ } else if (val && typeof val === "object" && "lastSent" in val) {
6626
+ state[key] = val;
6627
+ }
6628
+ }
6629
+ return state;
6536
6630
  } catch {
6537
6631
  return {};
6538
6632
  }
6539
6633
  }
6540
6634
  function writeDebounceState(state) {
6541
6635
  try {
6542
- if (!existsSync13(SESSION_CACHE)) mkdirSync6(SESSION_CACHE, { recursive: true });
6543
- writeFileSync8(DEBOUNCE_FILE, JSON.stringify(state));
6636
+ if (!existsSync14(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
6637
+ writeFileSync9(DEBOUNCE_FILE, JSON.stringify(state));
6544
6638
  } catch {
6545
6639
  }
6546
6640
  }
6547
6641
  function isDebounced(targetSession) {
6548
6642
  const state = readDebounceState();
6549
- const lastSent = state[targetSession] ?? 0;
6550
- return Date.now() - lastSent < INTERCOM_DEBOUNCE_MS;
6643
+ const entry = state[targetSession];
6644
+ const lastSent = entry?.lastSent ?? 0;
6645
+ if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
6646
+ if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
6647
+ state[targetSession].pending++;
6648
+ writeDebounceState(state);
6649
+ return true;
6650
+ }
6651
+ return false;
6551
6652
  }
6552
6653
  function recordDebounce(targetSession) {
6553
6654
  const state = readDebounceState();
6554
- state[targetSession] = Date.now();
6655
+ const batched = state[targetSession]?.pending ?? 0;
6656
+ state[targetSession] = { lastSent: Date.now(), pending: 0 };
6555
6657
  const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
6556
6658
  for (const key of Object.keys(state)) {
6557
- if ((state[key] ?? 0) < cutoff) delete state[key];
6659
+ if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
6558
6660
  }
6559
6661
  writeDebounceState(state);
6662
+ return batched;
6560
6663
  }
6561
6664
  function logIntercom(msg) {
6562
6665
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
@@ -6601,7 +6704,7 @@ function sendIntercom(targetSession) {
6601
6704
  return "skipped_exe";
6602
6705
  }
6603
6706
  if (isDebounced(targetSession)) {
6604
- logIntercom(`DEBOUNCE \u2192 ${targetSession} (cross-process file debounce)`);
6707
+ logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
6605
6708
  return "debounced";
6606
6709
  }
6607
6710
  try {
@@ -6613,14 +6716,14 @@ function sendIntercom(targetSession) {
6613
6716
  const sessionState = getSessionState(targetSession);
6614
6717
  if (sessionState === "no_claude") {
6615
6718
  queueIntercom(targetSession, "claude not running in session");
6616
- recordDebounce(targetSession);
6617
- logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
6719
+ const batched2 = recordDebounce(targetSession);
6720
+ logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
6618
6721
  return "queued";
6619
6722
  }
6620
6723
  if (sessionState === "thinking" || sessionState === "tool") {
6621
6724
  queueIntercom(targetSession, "session busy at send time");
6622
- recordDebounce(targetSession);
6623
- logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
6725
+ const batched2 = recordDebounce(targetSession);
6726
+ logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
6624
6727
  return "queued";
6625
6728
  }
6626
6729
  if (transport.isPaneInCopyMode(targetSession)) {
@@ -6628,8 +6731,8 @@ function sendIntercom(targetSession) {
6628
6731
  transport.sendKeys(targetSession, "q");
6629
6732
  }
6630
6733
  transport.sendKeys(targetSession, "/exe-intercom");
6631
- recordDebounce(targetSession);
6632
- logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
6734
+ const batched = recordDebounce(targetSession);
6735
+ logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
6633
6736
  return "delivered";
6634
6737
  } catch {
6635
6738
  logIntercom(`FAIL \u2192 ${targetSession}`);
@@ -6731,26 +6834,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6731
6834
  const transport = getTransport();
6732
6835
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
6733
6836
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
6734
- const logDir = path17.join(os7.homedir(), ".exe-os", "session-logs");
6735
- const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
6736
- if (!existsSync13(logDir)) {
6737
- mkdirSync6(logDir, { recursive: true });
6837
+ const logDir = path18.join(os7.homedir(), ".exe-os", "session-logs");
6838
+ const logFile = path18.join(logDir, `${instanceLabel}-${Date.now()}.log`);
6839
+ if (!existsSync14(logDir)) {
6840
+ mkdirSync7(logDir, { recursive: true });
6738
6841
  }
6739
6842
  transport.kill(sessionName);
6740
6843
  let cleanupSuffix = "";
6741
6844
  try {
6742
6845
  const thisFile = fileURLToPath2(import.meta.url);
6743
- const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
6744
- if (existsSync13(cleanupScript)) {
6846
+ const cleanupScript = path18.join(path18.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
6847
+ if (existsSync14(cleanupScript)) {
6745
6848
  cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
6746
6849
  }
6747
6850
  } catch {
6748
6851
  }
6749
6852
  try {
6750
- const claudeJsonPath = path17.join(os7.homedir(), ".claude.json");
6853
+ const claudeJsonPath = path18.join(os7.homedir(), ".claude.json");
6751
6854
  let claudeJson = {};
6752
6855
  try {
6753
- claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
6856
+ claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
6754
6857
  } catch {
6755
6858
  }
6756
6859
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -6758,17 +6861,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6758
6861
  const trustDir = opts?.cwd ?? projectDir;
6759
6862
  if (!projects[trustDir]) projects[trustDir] = {};
6760
6863
  projects[trustDir].hasTrustDialogAccepted = true;
6761
- writeFileSync8(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
6864
+ writeFileSync9(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
6762
6865
  } catch {
6763
6866
  }
6764
6867
  try {
6765
- const settingsDir = path17.join(os7.homedir(), ".claude", "projects");
6868
+ const settingsDir = path18.join(os7.homedir(), ".claude", "projects");
6766
6869
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
6767
- const projSettingsDir = path17.join(settingsDir, normalizedKey);
6768
- const settingsPath = path17.join(projSettingsDir, "settings.json");
6870
+ const projSettingsDir = path18.join(settingsDir, normalizedKey);
6871
+ const settingsPath = path18.join(projSettingsDir, "settings.json");
6769
6872
  let settings = {};
6770
6873
  try {
6771
- settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
6874
+ settings = JSON.parse(readFileSync12(settingsPath, "utf8"));
6772
6875
  } catch {
6773
6876
  }
6774
6877
  const perms = settings.permissions ?? {};
@@ -6796,20 +6899,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6796
6899
  if (changed) {
6797
6900
  perms.allow = allow;
6798
6901
  settings.permissions = perms;
6799
- mkdirSync6(projSettingsDir, { recursive: true });
6800
- writeFileSync8(settingsPath, JSON.stringify(settings, null, 2) + "\n");
6902
+ mkdirSync7(projSettingsDir, { recursive: true });
6903
+ writeFileSync9(settingsPath, JSON.stringify(settings, null, 2) + "\n");
6801
6904
  }
6802
6905
  } catch {
6803
6906
  }
6804
6907
  const spawnCwd = opts?.cwd ?? projectDir;
6805
6908
  const useExeAgent = !!(opts?.model && opts?.provider);
6806
- const ccProvider = useExeAgent ? DEFAULT_PROVIDER : detectActiveProvider();
6909
+ const agentRtConfig = getAgentRuntime(employeeName);
6910
+ const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
6911
+ const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
6912
+ const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
6807
6913
  const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
6808
6914
  let identityFlag = "";
6809
6915
  let behaviorsFlag = "";
6810
6916
  let legacyFallbackWarned = false;
6811
6917
  if (!useExeAgent && !useBinSymlink) {
6812
- const identityPath2 = path17.join(
6918
+ const identityPath2 = path18.join(
6813
6919
  os7.homedir(),
6814
6920
  ".exe-os",
6815
6921
  "identity",
@@ -6819,13 +6925,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6819
6925
  const hasAgentFlag = claudeSupportsAgentFlag();
6820
6926
  if (hasAgentFlag) {
6821
6927
  identityFlag = ` --agent ${employeeName}`;
6822
- } else if (existsSync13(identityPath2)) {
6928
+ } else if (existsSync14(identityPath2)) {
6823
6929
  identityFlag = ` --append-system-prompt-file ${identityPath2}`;
6824
6930
  legacyFallbackWarned = true;
6825
6931
  }
6826
6932
  const behaviorsFile = exportBehaviorsSync(
6827
6933
  employeeName,
6828
- path17.basename(spawnCwd),
6934
+ path18.basename(spawnCwd),
6829
6935
  sessionName
6830
6936
  );
6831
6937
  if (behaviorsFile) {
@@ -6840,16 +6946,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6840
6946
  }
6841
6947
  let sessionContextFlag = "";
6842
6948
  try {
6843
- const ctxDir = path17.join(os7.homedir(), ".exe-os", "session-cache");
6844
- mkdirSync6(ctxDir, { recursive: true });
6845
- const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
6949
+ const ctxDir = path18.join(os7.homedir(), ".exe-os", "session-cache");
6950
+ mkdirSync7(ctxDir, { recursive: true });
6951
+ const ctxFile = path18.join(ctxDir, `session-context-${sessionName}.md`);
6846
6952
  const ctxContent = [
6847
6953
  `## Session Context`,
6848
6954
  `You are running in tmux session: ${sessionName}.`,
6849
6955
  `Your parent coordinator session is ${exeSession}.`,
6850
6956
  `Your employees (if any) use the -${exeSession} suffix.`
6851
6957
  ].join("\n");
6852
- writeFileSync8(ctxFile, ctxContent);
6958
+ writeFileSync9(ctxFile, ctxContent);
6853
6959
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
6854
6960
  } catch {
6855
6961
  }
@@ -6863,9 +6969,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6863
6969
  }
6864
6970
  }
6865
6971
  }
6972
+ if (useCodex) {
6973
+ const codexCfg = RUNTIME_TABLE.codex;
6974
+ if (codexCfg?.apiKeyEnv) {
6975
+ const keyVal = process.env[codexCfg.apiKeyEnv];
6976
+ if (keyVal) {
6977
+ envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
6978
+ }
6979
+ }
6980
+ envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
6981
+ }
6982
+ if (useOpencode) {
6983
+ const ocCfg = PROVIDER_TABLE.opencode;
6984
+ if (ocCfg?.apiKeyEnv) {
6985
+ const keyVal = process.env[ocCfg.apiKeyEnv];
6986
+ if (keyVal) {
6987
+ envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
6988
+ }
6989
+ }
6990
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
6991
+ }
6992
+ if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
6993
+ const defaultClaudeModel = DEFAULT_MODELS.claude;
6994
+ if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
6995
+ envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
6996
+ }
6997
+ }
6866
6998
  let spawnCommand;
6867
6999
  if (useExeAgent) {
6868
7000
  spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
7001
+ } else if (useCodex) {
7002
+ process.stderr.write(
7003
+ `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
7004
+ `
7005
+ );
7006
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
7007
+ } else if (useOpencode) {
7008
+ const binName = `${employeeName}-opencode`;
7009
+ process.stderr.write(
7010
+ `[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
7011
+ `
7012
+ );
7013
+ spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
6869
7014
  } else if (useBinSymlink) {
6870
7015
  const binName = `${employeeName}-${ccProvider}`;
6871
7016
  process.stderr.write(
@@ -6887,11 +7032,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6887
7032
  transport.pipeLog(sessionName, logFile);
6888
7033
  try {
6889
7034
  const mySession = getMySession();
6890
- const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
6891
- writeFileSync8(dispatchInfo, JSON.stringify({
7035
+ const dispatchInfo = path18.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
7036
+ writeFileSync9(dispatchInfo, JSON.stringify({
6892
7037
  dispatchedBy: mySession,
6893
7038
  rootExe: exeSession,
6894
- provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
7039
+ provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
7040
+ runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
7041
+ model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
6895
7042
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
6896
7043
  }));
6897
7044
  } catch {
@@ -6909,6 +7056,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6909
7056
  booted = true;
6910
7057
  break;
6911
7058
  }
7059
+ } else if (useCodex) {
7060
+ if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
7061
+ booted = true;
7062
+ break;
7063
+ }
6912
7064
  } else {
6913
7065
  if (pane.includes("Claude Code") || pane.includes("\u276F")) {
6914
7066
  booted = true;
@@ -6920,9 +7072,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6920
7072
  }
6921
7073
  if (!booted) {
6922
7074
  releaseSpawnLock2(sessionName);
6923
- return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
7075
+ const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
7076
+ return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
6924
7077
  }
6925
- if (!useExeAgent) {
7078
+ if (!useExeAgent && !useCodex) {
6926
7079
  try {
6927
7080
  transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
6928
7081
  } catch {
@@ -6949,17 +7102,19 @@ var init_tmux_routing = __esm({
6949
7102
  init_cc_agent_support();
6950
7103
  init_mcp_prefix();
6951
7104
  init_provider_table();
7105
+ init_agent_config();
7106
+ init_runtime_table();
6952
7107
  init_intercom_queue();
6953
7108
  init_plan_limits();
6954
7109
  init_employees();
6955
- SPAWN_LOCK_DIR = path17.join(os7.homedir(), ".exe-os", "spawn-locks");
6956
- SESSION_CACHE = path17.join(os7.homedir(), ".exe-os", "session-cache");
7110
+ SPAWN_LOCK_DIR = path18.join(os7.homedir(), ".exe-os", "spawn-locks");
7111
+ SESSION_CACHE = path18.join(os7.homedir(), ".exe-os", "session-cache");
6957
7112
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
6958
7113
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
6959
7114
  VERIFY_PANE_LINES = 200;
6960
7115
  INTERCOM_DEBOUNCE_MS = 3e4;
6961
- INTERCOM_LOG2 = path17.join(os7.homedir(), ".exe-os", "intercom.log");
6962
- DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
7116
+ INTERCOM_LOG2 = path18.join(os7.homedir(), ".exe-os", "intercom.log");
7117
+ DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
6963
7118
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
6964
7119
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
6965
7120
  }
@@ -7007,11 +7162,11 @@ __export(tasks_crud_exports, {
7007
7162
  writeCheckpoint: () => writeCheckpoint
7008
7163
  });
7009
7164
  import crypto6 from "crypto";
7010
- import path18 from "path";
7165
+ import path19 from "path";
7011
7166
  import os8 from "os";
7012
7167
  import { execSync as execSync8 } from "child_process";
7013
7168
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
7014
- import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
7169
+ import { existsSync as existsSync15, readFileSync as readFileSync13 } from "fs";
7015
7170
  async function writeCheckpoint(input) {
7016
7171
  const client = getClient();
7017
7172
  const row = await resolveTask(client, input.taskId);
@@ -7186,8 +7341,8 @@ ${laneWarning}` : laneWarning;
7186
7341
  }
7187
7342
  if (input.baseDir) {
7188
7343
  try {
7189
- await mkdir4(path18.join(input.baseDir, "exe", "output"), { recursive: true });
7190
- await mkdir4(path18.join(input.baseDir, "exe", "research"), { recursive: true });
7344
+ await mkdir4(path19.join(input.baseDir, "exe", "output"), { recursive: true });
7345
+ await mkdir4(path19.join(input.baseDir, "exe", "research"), { recursive: true });
7191
7346
  await ensureArchitectureDoc(input.baseDir, input.projectName);
7192
7347
  await ensureGitignoreExe(input.baseDir);
7193
7348
  } catch {
@@ -7223,10 +7378,10 @@ ${laneWarning}` : laneWarning;
7223
7378
  });
7224
7379
  if (input.baseDir) {
7225
7380
  try {
7226
- const EXE_OS_DIR = path18.join(os8.homedir(), ".exe-os");
7227
- const mdPath = path18.join(EXE_OS_DIR, taskFile);
7228
- const mdDir = path18.dirname(mdPath);
7229
- if (!existsSync14(mdDir)) await mkdir4(mdDir, { recursive: true });
7381
+ const EXE_OS_DIR = path19.join(os8.homedir(), ".exe-os");
7382
+ const mdPath = path19.join(EXE_OS_DIR, taskFile);
7383
+ const mdDir = path19.dirname(mdPath);
7384
+ if (!existsSync15(mdDir)) await mkdir4(mdDir, { recursive: true });
7230
7385
  const reviewer = input.reviewer ?? input.assignedBy;
7231
7386
  const mdContent = `# ${input.title}
7232
7387
 
@@ -7251,7 +7406,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
7251
7406
  Do NOT let a failed commit or any error prevent you from calling update_task(done).
7252
7407
  `;
7253
7408
  await writeFile4(mdPath, mdContent, "utf-8");
7254
- } catch {
7409
+ } catch (err) {
7410
+ process.stderr.write(
7411
+ `[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
7412
+ `
7413
+ );
7255
7414
  }
7256
7415
  }
7257
7416
  return {
@@ -7511,9 +7670,9 @@ async function deleteTaskCore(taskId, _baseDir) {
7511
7670
  return { taskFile, assignedTo, assignedBy, taskSlug };
7512
7671
  }
7513
7672
  async function ensureArchitectureDoc(baseDir, projectName) {
7514
- const archPath = path18.join(baseDir, "exe", "ARCHITECTURE.md");
7673
+ const archPath = path19.join(baseDir, "exe", "ARCHITECTURE.md");
7515
7674
  try {
7516
- if (existsSync14(archPath)) return;
7675
+ if (existsSync15(archPath)) return;
7517
7676
  const template = [
7518
7677
  `# ${projectName} \u2014 System Architecture`,
7519
7678
  "",
@@ -7546,10 +7705,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
7546
7705
  }
7547
7706
  }
7548
7707
  async function ensureGitignoreExe(baseDir) {
7549
- const gitignorePath = path18.join(baseDir, ".gitignore");
7708
+ const gitignorePath = path19.join(baseDir, ".gitignore");
7550
7709
  try {
7551
- if (existsSync14(gitignorePath)) {
7552
- const content = readFileSync12(gitignorePath, "utf-8");
7710
+ if (existsSync15(gitignorePath)) {
7711
+ const content = readFileSync13(gitignorePath, "utf-8");
7553
7712
  if (/^\/?exe\/?$/m.test(content)) return;
7554
7713
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
7555
7714
  } else {
@@ -7580,8 +7739,8 @@ var init_tasks_crud = __esm({
7580
7739
  });
7581
7740
 
7582
7741
  // src/lib/tasks-review.ts
7583
- import path19 from "path";
7584
- import { existsSync as existsSync15, readdirSync as readdirSync5, unlinkSync as unlinkSync6 } from "fs";
7742
+ import path20 from "path";
7743
+ import { existsSync as existsSync16, readdirSync as readdirSync5, unlinkSync as unlinkSync6 } from "fs";
7585
7744
  async function countPendingReviews(sessionScope) {
7586
7745
  const client = getClient();
7587
7746
  if (sessionScope) {
@@ -7762,11 +7921,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
7762
7921
  );
7763
7922
  }
7764
7923
  try {
7765
- const cacheDir = path19.join(EXE_AI_DIR, "session-cache");
7766
- if (existsSync15(cacheDir)) {
7924
+ const cacheDir = path20.join(EXE_AI_DIR, "session-cache");
7925
+ if (existsSync16(cacheDir)) {
7767
7926
  for (const f of readdirSync5(cacheDir)) {
7768
7927
  if (f.startsWith("review-notified-")) {
7769
- unlinkSync6(path19.join(cacheDir, f));
7928
+ unlinkSync6(path20.join(cacheDir, f));
7770
7929
  }
7771
7930
  }
7772
7931
  }
@@ -7787,7 +7946,7 @@ var init_tasks_review = __esm({
7787
7946
  });
7788
7947
 
7789
7948
  // src/lib/tasks-chain.ts
7790
- import path20 from "path";
7949
+ import path21 from "path";
7791
7950
  import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
7792
7951
  async function cascadeUnblock(taskId, baseDir, now) {
7793
7952
  const client = getClient();
@@ -7804,7 +7963,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
7804
7963
  });
7805
7964
  for (const ur of unblockedRows.rows) {
7806
7965
  try {
7807
- const ubFile = path20.join(baseDir, String(ur.task_file));
7966
+ const ubFile = path21.join(baseDir, String(ur.task_file));
7808
7967
  let ubContent = await readFile4(ubFile, "utf-8");
7809
7968
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
7810
7969
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -8358,8 +8517,8 @@ __export(tasks_exports, {
8358
8517
  updateTaskStatus: () => updateTaskStatus,
8359
8518
  writeCheckpoint: () => writeCheckpoint
8360
8519
  });
8361
- import path21 from "path";
8362
- import { writeFileSync as writeFileSync9, mkdirSync as mkdirSync7, unlinkSync as unlinkSync7 } from "fs";
8520
+ import path22 from "path";
8521
+ import { writeFileSync as writeFileSync10, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7 } from "fs";
8363
8522
  async function createTask(input) {
8364
8523
  const result = await createTaskCore(input);
8365
8524
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -8378,11 +8537,11 @@ async function updateTask(input) {
8378
8537
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
8379
8538
  try {
8380
8539
  const agent = String(row.assigned_to);
8381
- const cacheDir = path21.join(EXE_AI_DIR, "session-cache");
8382
- const cachePath = path21.join(cacheDir, `current-task-${agent}.json`);
8540
+ const cacheDir = path22.join(EXE_AI_DIR, "session-cache");
8541
+ const cachePath = path22.join(cacheDir, `current-task-${agent}.json`);
8383
8542
  if (input.status === "in_progress") {
8384
- mkdirSync7(cacheDir, { recursive: true });
8385
- writeFileSync9(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
8543
+ mkdirSync8(cacheDir, { recursive: true });
8544
+ writeFileSync10(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
8386
8545
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
8387
8546
  try {
8388
8547
  unlinkSync7(cachePath);
@@ -8548,17 +8707,17 @@ __export(identity_exports, {
8548
8707
  listIdentities: () => listIdentities,
8549
8708
  updateIdentity: () => updateIdentity
8550
8709
  });
8551
- import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "fs";
8710
+ import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "fs";
8552
8711
  import { readdirSync as readdirSync6 } from "fs";
8553
- import path22 from "path";
8712
+ import path23 from "path";
8554
8713
  import { createHash as createHash2 } from "crypto";
8555
8714
  function ensureDir2() {
8556
- if (!existsSync16(IDENTITY_DIR)) {
8557
- mkdirSync8(IDENTITY_DIR, { recursive: true });
8715
+ if (!existsSync17(IDENTITY_DIR)) {
8716
+ mkdirSync9(IDENTITY_DIR, { recursive: true });
8558
8717
  }
8559
8718
  }
8560
8719
  function identityPath(agentId) {
8561
- return path22.join(IDENTITY_DIR, `${agentId}.md`);
8720
+ return path23.join(IDENTITY_DIR, `${agentId}.md`);
8562
8721
  }
8563
8722
  function parseFrontmatter(raw) {
8564
8723
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
@@ -8599,8 +8758,8 @@ function contentHash(content) {
8599
8758
  }
8600
8759
  function getIdentity(agentId) {
8601
8760
  const filePath = identityPath(agentId);
8602
- if (!existsSync16(filePath)) return null;
8603
- const raw = readFileSync13(filePath, "utf-8");
8761
+ if (!existsSync17(filePath)) return null;
8762
+ const raw = readFileSync14(filePath, "utf-8");
8604
8763
  const { frontmatter, body } = parseFrontmatter(raw);
8605
8764
  return {
8606
8765
  agentId,
@@ -8614,7 +8773,7 @@ async function updateIdentity(agentId, content, updatedBy) {
8614
8773
  ensureDir2();
8615
8774
  const filePath = identityPath(agentId);
8616
8775
  const hash = contentHash(content);
8617
- writeFileSync10(filePath, content, "utf-8");
8776
+ writeFileSync11(filePath, content, "utf-8");
8618
8777
  try {
8619
8778
  const client = getClient();
8620
8779
  await client.execute({
@@ -8670,7 +8829,7 @@ var init_identity = __esm({
8670
8829
  "use strict";
8671
8830
  init_config();
8672
8831
  init_database();
8673
- IDENTITY_DIR = path22.join(EXE_AI_DIR, "identity");
8832
+ IDENTITY_DIR = path23.join(EXE_AI_DIR, "identity");
8674
8833
  }
8675
8834
  });
8676
8835
 
@@ -9353,13 +9512,13 @@ var init_messaging = __esm({
9353
9512
  });
9354
9513
 
9355
9514
  // src/gateway/whatsapp-accounts.ts
9356
- import { readFileSync as readFileSync14 } from "fs";
9515
+ import { readFileSync as readFileSync15 } from "fs";
9357
9516
  import { join } from "path";
9358
9517
  import { homedir } from "os";
9359
9518
  function loadAccounts() {
9360
9519
  if (cachedAccounts !== null) return cachedAccounts;
9361
9520
  try {
9362
- const raw = readFileSync14(CONFIG_PATH2, "utf8");
9521
+ const raw = readFileSync15(CONFIG_PATH2, "utf8");
9363
9522
  const parsed = JSON.parse(raw);
9364
9523
  if (!Array.isArray(parsed)) {
9365
9524
  console.warn("[whatsapp] Config is not an array, ignoring");
@@ -9772,8 +9931,8 @@ __export(wiki_client_exports, {
9772
9931
  listDocuments: () => listDocuments,
9773
9932
  listWorkspaces: () => listWorkspaces
9774
9933
  });
9775
- async function wikiFetch(config2, path37, method = "GET", body) {
9776
- const url = `${config2.baseUrl}/api/v1${path37}`;
9934
+ async function wikiFetch(config2, path38, method = "GET", body) {
9935
+ const url = `${config2.baseUrl}/api/v1${path38}`;
9777
9936
  const headers = {
9778
9937
  Authorization: `Bearer ${config2.apiKey}`,
9779
9938
  "Content-Type": "application/json"
@@ -9806,7 +9965,7 @@ async function wikiFetch(config2, path37, method = "GET", body) {
9806
9965
  }
9807
9966
  }
9808
9967
  if (!response.ok) {
9809
- throw new Error(`Wiki API ${method} ${path37}: ${response.status} ${response.statusText}`);
9968
+ throw new Error(`Wiki API ${method} ${path38}: ${response.status} ${response.statusText}`);
9810
9969
  }
9811
9970
  return response.json();
9812
9971
  } finally {
@@ -9915,14 +10074,14 @@ __export(worker_gate_exports, {
9915
10074
  tryAcquireBackfillLock: () => tryAcquireBackfillLock,
9916
10075
  tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
9917
10076
  });
9918
- import { readdirSync as readdirSync9, writeFileSync as writeFileSync14, unlinkSync as unlinkSync8, mkdirSync as mkdirSync11, existsSync as existsSync23 } from "fs";
9919
- import path30 from "path";
10077
+ import { readdirSync as readdirSync9, writeFileSync as writeFileSync15, unlinkSync as unlinkSync8, mkdirSync as mkdirSync12, existsSync as existsSync24 } from "fs";
10078
+ import path31 from "path";
9920
10079
  function tryAcquireWorkerSlot() {
9921
10080
  try {
9922
- mkdirSync11(WORKER_PID_DIR, { recursive: true });
10081
+ mkdirSync12(WORKER_PID_DIR, { recursive: true });
9923
10082
  const reservationId = `res-${process.pid}-${Date.now()}`;
9924
- const reservationPath = path30.join(WORKER_PID_DIR, `${reservationId}.pid`);
9925
- writeFileSync14(reservationPath, String(process.pid));
10083
+ const reservationPath = path31.join(WORKER_PID_DIR, `${reservationId}.pid`);
10084
+ writeFileSync15(reservationPath, String(process.pid));
9926
10085
  const files = readdirSync9(WORKER_PID_DIR);
9927
10086
  let alive = 0;
9928
10087
  for (const f of files) {
@@ -9939,7 +10098,7 @@ function tryAcquireWorkerSlot() {
9939
10098
  alive++;
9940
10099
  } catch {
9941
10100
  try {
9942
- unlinkSync8(path30.join(WORKER_PID_DIR, f));
10101
+ unlinkSync8(path31.join(WORKER_PID_DIR, f));
9943
10102
  } catch {
9944
10103
  }
9945
10104
  }
@@ -9962,21 +10121,21 @@ function tryAcquireWorkerSlot() {
9962
10121
  }
9963
10122
  function registerWorkerPid(pid) {
9964
10123
  try {
9965
- mkdirSync11(WORKER_PID_DIR, { recursive: true });
9966
- writeFileSync14(path30.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
10124
+ mkdirSync12(WORKER_PID_DIR, { recursive: true });
10125
+ writeFileSync15(path31.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
9967
10126
  } catch {
9968
10127
  }
9969
10128
  }
9970
10129
  function cleanupWorkerPid() {
9971
10130
  try {
9972
- unlinkSync8(path30.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
10131
+ unlinkSync8(path31.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
9973
10132
  } catch {
9974
10133
  }
9975
10134
  }
9976
10135
  function tryAcquireBackfillLock() {
9977
10136
  try {
9978
- mkdirSync11(WORKER_PID_DIR, { recursive: true });
9979
- if (existsSync23(BACKFILL_LOCK)) {
10137
+ mkdirSync12(WORKER_PID_DIR, { recursive: true });
10138
+ if (existsSync24(BACKFILL_LOCK)) {
9980
10139
  try {
9981
10140
  const pid = parseInt(
9982
10141
  __require("fs").readFileSync(BACKFILL_LOCK, "utf8").trim(),
@@ -9992,7 +10151,7 @@ function tryAcquireBackfillLock() {
9992
10151
  } catch {
9993
10152
  }
9994
10153
  }
9995
- writeFileSync14(BACKFILL_LOCK, String(process.pid));
10154
+ writeFileSync15(BACKFILL_LOCK, String(process.pid));
9996
10155
  return true;
9997
10156
  } catch {
9998
10157
  return true;
@@ -10009,9 +10168,9 @@ var init_worker_gate = __esm({
10009
10168
  "src/lib/worker-gate.ts"() {
10010
10169
  "use strict";
10011
10170
  init_config();
10012
- WORKER_PID_DIR = path30.join(EXE_AI_DIR, "worker-pids");
10171
+ WORKER_PID_DIR = path31.join(EXE_AI_DIR, "worker-pids");
10013
10172
  MAX_CONCURRENT_WORKERS = 3;
10014
- BACKFILL_LOCK = path30.join(WORKER_PID_DIR, "backfill.lock");
10173
+ BACKFILL_LOCK = path31.join(WORKER_PID_DIR, "backfill.lock");
10015
10174
  }
10016
10175
  });
10017
10176
 
@@ -10034,8 +10193,8 @@ __export(crdt_sync_exports, {
10034
10193
  rebuildFromDb: () => rebuildFromDb
10035
10194
  });
10036
10195
  import * as Y from "yjs";
10037
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync15, existsSync as existsSync26, mkdirSync as mkdirSync12, unlinkSync as unlinkSync9 } from "fs";
10038
- import path33 from "path";
10196
+ import { readFileSync as readFileSync21, writeFileSync as writeFileSync16, existsSync as existsSync27, mkdirSync as mkdirSync13, unlinkSync as unlinkSync9 } from "fs";
10197
+ import path34 from "path";
10039
10198
  import { homedir as homedir5 } from "os";
10040
10199
  function getStatePath() {
10041
10200
  return _statePathOverride ?? DEFAULT_STATE_PATH;
@@ -10047,9 +10206,9 @@ function initCrdtDoc() {
10047
10206
  if (doc) return doc;
10048
10207
  doc = new Y.Doc();
10049
10208
  const sp = getStatePath();
10050
- if (existsSync26(sp)) {
10209
+ if (existsSync27(sp)) {
10051
10210
  try {
10052
- const state = readFileSync20(sp);
10211
+ const state = readFileSync21(sp);
10053
10212
  Y.applyUpdate(doc, new Uint8Array(state));
10054
10213
  } catch {
10055
10214
  console.warn("[crdt-sync] WARN: corrupted state file, rebuilding from DB");
@@ -10191,10 +10350,10 @@ function persistState() {
10191
10350
  if (!doc) return;
10192
10351
  try {
10193
10352
  const sp = getStatePath();
10194
- const dir = path33.dirname(sp);
10195
- if (!existsSync26(dir)) mkdirSync12(dir, { recursive: true });
10353
+ const dir = path34.dirname(sp);
10354
+ if (!existsSync27(dir)) mkdirSync13(dir, { recursive: true });
10196
10355
  const state = Y.encodeStateAsUpdate(doc);
10197
- writeFileSync15(sp, Buffer.from(state));
10356
+ writeFileSync16(sp, Buffer.from(state));
10198
10357
  } catch {
10199
10358
  }
10200
10359
  }
@@ -10235,7 +10394,7 @@ var DEFAULT_STATE_PATH, _statePathOverride, doc;
10235
10394
  var init_crdt_sync = __esm({
10236
10395
  "src/lib/crdt-sync.ts"() {
10237
10396
  "use strict";
10238
- DEFAULT_STATE_PATH = path33.join(homedir5(), ".exe-os", "crdt-state.bin");
10397
+ DEFAULT_STATE_PATH = path34.join(homedir5(), ".exe-os", "crdt-state.bin");
10239
10398
  _statePathOverride = null;
10240
10399
  doc = null;
10241
10400
  }
@@ -10248,12 +10407,13 @@ init_database();
10248
10407
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10249
10408
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10250
10409
  import { spawn as spawn3 } from "child_process";
10251
- import { existsSync as existsSync29, openSync as openSync3, mkdirSync as mkdirSync14, closeSync as closeSync3 } from "fs";
10252
- import path36 from "path";
10410
+ import { existsSync as existsSync30, openSync as openSync3, mkdirSync as mkdirSync15, closeSync as closeSync3 } from "fs";
10411
+ import path37 from "path";
10253
10412
  import { fileURLToPath as fileURLToPath5 } from "url";
10254
10413
 
10255
10414
  // src/mcp/tools/recall-my-memory.ts
10256
10415
  init_hybrid_search();
10416
+ init_store();
10257
10417
  init_active_agent();
10258
10418
  import { z } from "zod";
10259
10419
  function formatSourceLine(record) {
@@ -10273,7 +10433,10 @@ function registerRecallMyMemory(server2) {
10273
10433
  title: "Recall My Memory",
10274
10434
  description: "Search your past work memories using semantic search. Returns relevant past tool calls, outputs, and decisions.",
10275
10435
  inputSchema: {
10276
- query: z.string().describe("What to search for in your memories"),
10436
+ query: z.string().optional().describe("What to search for in your memories"),
10437
+ recent: z.boolean().optional().default(false).describe(
10438
+ "When true, return most recent memories sorted by time (no semantic search). Use for session recovery after crashes."
10439
+ ),
10277
10440
  project_name: z.string().optional().describe("Filter by project name"),
10278
10441
  has_error: z.boolean().optional().describe("Filter for error-containing memories"),
10279
10442
  tool_name: z.string().optional().describe("Filter by tool name (Bash, Write, etc)"),
@@ -10293,6 +10456,7 @@ function registerRecallMyMemory(server2) {
10293
10456
  },
10294
10457
  async ({
10295
10458
  query,
10459
+ recent,
10296
10460
  project_name,
10297
10461
  has_error,
10298
10462
  tool_name,
@@ -10304,6 +10468,12 @@ function registerRecallMyMemory(server2) {
10304
10468
  include_source
10305
10469
  }) => {
10306
10470
  try {
10471
+ if (!recent && !query) {
10472
+ return {
10473
+ content: [{ type: "text", text: "query is required when recent mode is not enabled." }],
10474
+ isError: true
10475
+ };
10476
+ }
10307
10477
  const { agentId } = getActiveAgent();
10308
10478
  const searchOptions = {
10309
10479
  projectName: project_name,
@@ -10317,7 +10487,15 @@ function registerRecallMyMemory(server2) {
10317
10487
  includeDrafts: true,
10318
10488
  ...user_id !== void 0 ? { userId: user_id } : {}
10319
10489
  };
10320
- const results = await hybridSearch(query, agentId, searchOptions);
10490
+ let results;
10491
+ if (recent) {
10492
+ results = await recentRecords(agentId, searchOptions, limit, query);
10493
+ if (include_source && results.length > 0) {
10494
+ await attachDocumentMetadata(results);
10495
+ }
10496
+ } else {
10497
+ results = await hybridSearch(query, agentId, searchOptions);
10498
+ }
10321
10499
  if (results.length === 0) {
10322
10500
  return {
10323
10501
  content: [
@@ -10900,10 +11078,10 @@ function registerCreateTask(server2) {
10900
11078
  skipDispatch: true
10901
11079
  });
10902
11080
  try {
10903
- const { existsSync: existsSync30, mkdirSync: mkdirSync15, writeFileSync: writeFileSync17 } = await import("fs");
11081
+ const { existsSync: existsSync31, mkdirSync: mkdirSync16, writeFileSync: writeFileSync18 } = await import("fs");
10904
11082
  const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
10905
11083
  const idPath = identityPath2(assigned_to);
10906
- if (!existsSync30(idPath)) {
11084
+ if (!existsSync31(idPath)) {
10907
11085
  const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
10908
11086
  const employees = await loadEmployees2();
10909
11087
  const emp = employees.find((e) => e.name === assigned_to);
@@ -10912,8 +11090,8 @@ function registerCreateTask(server2) {
10912
11090
  const template = getTemplateForTitle2(emp.role);
10913
11091
  if (template) {
10914
11092
  const dir = (await import("path")).dirname(idPath);
10915
- if (!existsSync30(dir)) mkdirSync15(dir, { recursive: true });
10916
- writeFileSync17(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
11093
+ if (!existsSync31(dir)) mkdirSync16(dir, { recursive: true });
11094
+ writeFileSync18(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
10917
11095
  }
10918
11096
  }
10919
11097
  }
@@ -12745,15 +12923,15 @@ function registerSendWhatsapp(server2) {
12745
12923
  import { z as z29 } from "zod";
12746
12924
 
12747
12925
  // src/automation/trigger-engine.ts
12748
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync11, existsSync as existsSync17, mkdirSync as mkdirSync9 } from "fs";
12926
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync12, existsSync as existsSync18, mkdirSync as mkdirSync10 } from "fs";
12749
12927
  import { randomUUID as randomUUID4 } from "crypto";
12750
- import path23 from "path";
12928
+ import path24 from "path";
12751
12929
  import os9 from "os";
12752
- var TRIGGERS_PATH = path23.join(os9.homedir(), ".exe-os", "triggers.json");
12930
+ var TRIGGERS_PATH = path24.join(os9.homedir(), ".exe-os", "triggers.json");
12753
12931
  function loadTriggers(project) {
12754
- if (!existsSync17(TRIGGERS_PATH)) return [];
12932
+ if (!existsSync18(TRIGGERS_PATH)) return [];
12755
12933
  try {
12756
- const raw = readFileSync15(TRIGGERS_PATH, "utf-8");
12934
+ const raw = readFileSync16(TRIGGERS_PATH, "utf-8");
12757
12935
  const all = JSON.parse(raw);
12758
12936
  if (!Array.isArray(all)) return [];
12759
12937
  if (project) {
@@ -12765,9 +12943,9 @@ function loadTriggers(project) {
12765
12943
  }
12766
12944
  }
12767
12945
  function saveTriggers(triggers) {
12768
- const dir = path23.dirname(TRIGGERS_PATH);
12769
- if (!existsSync17(dir)) mkdirSync9(dir, { recursive: true });
12770
- writeFileSync11(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
12946
+ const dir = path24.dirname(TRIGGERS_PATH);
12947
+ if (!existsSync18(dir)) mkdirSync10(dir, { recursive: true });
12948
+ writeFileSync12(TRIGGERS_PATH, JSON.stringify(triggers, null, 2), "utf-8");
12771
12949
  }
12772
12950
  function createNewTrigger(input) {
12773
12951
  const triggers = loadTriggers();
@@ -13051,48 +13229,48 @@ function registerListTriggers(server2) {
13051
13229
  import { z as z31 } from "zod";
13052
13230
 
13053
13231
  // src/automation/starter-packs/index.ts
13054
- import { readFileSync as readFileSync16, readdirSync as readdirSync7, existsSync as existsSync18 } from "fs";
13055
- import path24 from "path";
13232
+ import { readFileSync as readFileSync17, readdirSync as readdirSync7, existsSync as existsSync19 } from "fs";
13233
+ import path25 from "path";
13056
13234
  import { fileURLToPath as fileURLToPath3 } from "url";
13057
- var __dirname = path24.dirname(fileURLToPath3(import.meta.url));
13235
+ var __dirname = path25.dirname(fileURLToPath3(import.meta.url));
13058
13236
  function listPacks() {
13059
- const packsDir = path24.join(__dirname, ".");
13060
- if (!existsSync18(packsDir)) return [];
13237
+ const packsDir = path25.join(__dirname, ".");
13238
+ if (!existsSync19(packsDir)) return [];
13061
13239
  return readdirSync7(packsDir, { withFileTypes: true }).filter(
13062
- (d) => d.isDirectory() && existsSync18(path24.join(packsDir, d.name, "custom-objects.json"))
13240
+ (d) => d.isDirectory() && existsSync19(path25.join(packsDir, d.name, "custom-objects.json"))
13063
13241
  ).map((d) => d.name);
13064
13242
  }
13065
13243
  function loadPack(industry) {
13066
- const packDir = path24.join(__dirname, industry);
13067
- const objectsPath = path24.join(packDir, "custom-objects.json");
13068
- const triggersPath = path24.join(packDir, "triggers.json");
13069
- const wikiDir = path24.join(packDir, "wiki-seeds");
13070
- const manifestPath = path24.join(packDir, "pack.json");
13071
- const identityContextPath = path24.join(packDir, "identity-context.md");
13072
- if (!existsSync18(objectsPath)) return null;
13244
+ const packDir = path25.join(__dirname, industry);
13245
+ const objectsPath = path25.join(packDir, "custom-objects.json");
13246
+ const triggersPath = path25.join(packDir, "triggers.json");
13247
+ const wikiDir = path25.join(packDir, "wiki-seeds");
13248
+ const manifestPath = path25.join(packDir, "pack.json");
13249
+ const identityContextPath = path25.join(packDir, "identity-context.md");
13250
+ if (!existsSync19(objectsPath)) return null;
13073
13251
  let customObjects = [];
13074
13252
  try {
13075
13253
  customObjects = JSON.parse(
13076
- readFileSync16(objectsPath, "utf-8")
13254
+ readFileSync17(objectsPath, "utf-8")
13077
13255
  );
13078
13256
  } catch {
13079
13257
  customObjects = [];
13080
13258
  }
13081
13259
  let triggers = [];
13082
- if (existsSync18(triggersPath)) {
13260
+ if (existsSync19(triggersPath)) {
13083
13261
  try {
13084
13262
  triggers = JSON.parse(
13085
- readFileSync16(triggersPath, "utf-8")
13263
+ readFileSync17(triggersPath, "utf-8")
13086
13264
  );
13087
13265
  } catch {
13088
13266
  triggers = [];
13089
13267
  }
13090
13268
  }
13091
13269
  const wikiSeeds = [];
13092
- if (existsSync18(wikiDir)) {
13270
+ if (existsSync19(wikiDir)) {
13093
13271
  const files = readdirSync7(wikiDir).filter((f) => f.endsWith(".md"));
13094
13272
  for (const file of files) {
13095
- const content = readFileSync16(path24.join(wikiDir, file), "utf-8");
13273
+ const content = readFileSync17(path25.join(wikiDir, file), "utf-8");
13096
13274
  const titleMatch = content.match(/^#\s+(.+)/m);
13097
13275
  wikiSeeds.push({
13098
13276
  filename: file,
@@ -13102,17 +13280,17 @@ function loadPack(industry) {
13102
13280
  }
13103
13281
  }
13104
13282
  let manifest = {};
13105
- if (existsSync18(manifestPath)) {
13283
+ if (existsSync19(manifestPath)) {
13106
13284
  try {
13107
- manifest = JSON.parse(readFileSync16(manifestPath, "utf-8"));
13285
+ manifest = JSON.parse(readFileSync17(manifestPath, "utf-8"));
13108
13286
  } catch {
13109
13287
  manifest = {};
13110
13288
  }
13111
13289
  }
13112
13290
  let identityContext = null;
13113
- if (existsSync18(identityContextPath)) {
13291
+ if (existsSync19(identityContextPath)) {
13114
13292
  try {
13115
- identityContext = readFileSync16(identityContextPath, "utf-8");
13293
+ identityContext = readFileSync17(identityContextPath, "utf-8");
13116
13294
  } catch {
13117
13295
  identityContext = null;
13118
13296
  }
@@ -13169,8 +13347,8 @@ function applyPack(industry, project) {
13169
13347
 
13170
13348
  // src/lib/client-coo.ts
13171
13349
  init_config();
13172
- import { existsSync as existsSync19, mkdirSync as mkdirSync10, writeFileSync as writeFileSync12 } from "fs";
13173
- import path25 from "path";
13350
+ import { existsSync as existsSync20, mkdirSync as mkdirSync11, writeFileSync as writeFileSync13 } from "fs";
13351
+ import path26 from "path";
13174
13352
 
13175
13353
  // src/lib/employee-templates.ts
13176
13354
  init_global_procedures();
@@ -13308,18 +13486,18 @@ var ClientCOOClobberError = class extends Error {
13308
13486
  var COO_ROLE = "Chief Operating Officer";
13309
13487
  var FEEDBACK_BEHAVIOR_CONTENT = "Tag exe-os issues with needs_improvement \u2014 this is the feedback loop that surfaces bugs, gaps, and friction to the founder each Monday.";
13310
13488
  async function provisionClientCOO(vars, opts = {}) {
13311
- const identityDir = opts.identityDir ?? path25.join(EXE_AI_DIR, "identity");
13489
+ const identityDir = opts.identityDir ?? path26.join(EXE_AI_DIR, "identity");
13312
13490
  const rosterPath = opts.employeesPath ?? EMPLOYEES_PATH;
13313
13491
  const storeFeedbackBehavior = opts.storeFeedbackBehavior ?? true;
13314
- const identityPath2 = path25.join(identityDir, `${vars.agent_name}.md`);
13315
- if (existsSync19(identityPath2)) {
13492
+ const identityPath2 = path26.join(identityDir, `${vars.agent_name}.md`);
13493
+ if (existsSync20(identityPath2)) {
13316
13494
  throw new ClientCOOClobberError(vars.agent_name, identityPath2);
13317
13495
  }
13318
13496
  const body = renderClientCOOTemplate(vars);
13319
- if (!existsSync19(identityDir)) {
13320
- mkdirSync10(identityDir, { recursive: true });
13497
+ if (!existsSync20(identityDir)) {
13498
+ mkdirSync11(identityDir, { recursive: true });
13321
13499
  }
13322
- writeFileSync12(identityPath2, body, "utf-8");
13500
+ writeFileSync13(identityPath2, body, "utf-8");
13323
13501
  const employees = await loadEmployees(rosterPath);
13324
13502
  const existing = employees.find((e) => e.name === vars.agent_name);
13325
13503
  let addedToRoster = false;
@@ -14209,8 +14387,8 @@ function registerUpdateWikiPage(server2) {
14209
14387
  import { z as z37 } from "zod";
14210
14388
  import { execFile } from "child_process";
14211
14389
  import { promisify } from "util";
14212
- import path26 from "path";
14213
- import { existsSync as existsSync20 } from "fs";
14390
+ import path27 from "path";
14391
+ import { existsSync as existsSync21 } from "fs";
14214
14392
 
14215
14393
  // src/lib/hostinger-api.ts
14216
14394
  var DEFAULT_BASE_URL = "https://developers.hostinger.com/api/vps/v1";
@@ -14258,9 +14436,9 @@ var HostingerApiClient = class {
14258
14436
  }
14259
14437
  this.lastRequestTime = Date.now();
14260
14438
  }
14261
- async request(method, path37, body) {
14439
+ async request(method, path38, body) {
14262
14440
  await this.rateLimit();
14263
- const url = `${this.baseUrl}${path37}`;
14441
+ const url = `${this.baseUrl}${path38}`;
14264
14442
  const headers = {
14265
14443
  Authorization: `Bearer ${this.apiKey}`,
14266
14444
  "Content-Type": "application/json",
@@ -14411,12 +14589,12 @@ async function waitForReady(client, vpsId) {
14411
14589
  }
14412
14590
  async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
14413
14591
  const safeClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "-");
14414
- const playbookDir = path26.resolve(process.cwd(), "infrastructure", "ansible");
14415
- const playbookPath = path26.join(playbookDir, "deploy.yml");
14416
- const inventoryPath = path26.join(playbookDir, "inventory", "hosts.yml");
14417
- const clientVarsPath = path26.join(playbookDir, "vars", `${safeClientName}.yml`);
14418
- const varsDir = path26.join(playbookDir, "vars");
14419
- if (!path26.resolve(clientVarsPath).startsWith(path26.resolve(varsDir))) {
14592
+ const playbookDir = path27.resolve(process.cwd(), "infrastructure", "ansible");
14593
+ const playbookPath = path27.join(playbookDir, "deploy.yml");
14594
+ const inventoryPath = path27.join(playbookDir, "inventory", "hosts.yml");
14595
+ const clientVarsPath = path27.join(playbookDir, "vars", `${safeClientName}.yml`);
14596
+ const varsDir = path27.join(playbookDir, "vars");
14597
+ if (!path27.resolve(clientVarsPath).startsWith(path27.resolve(varsDir))) {
14420
14598
  throw new Error(`Invalid client name for vars path: ${clientName}`);
14421
14599
  }
14422
14600
  const args = [
@@ -14432,7 +14610,7 @@ async function runAnsiblePlaybook(vpsIp, domain, sslEmail, clientName) {
14432
14610
  "-e",
14433
14611
  `client_name=${safeClientName}`
14434
14612
  ];
14435
- if (existsSync20(clientVarsPath)) {
14613
+ if (existsSync21(clientVarsPath)) {
14436
14614
  args.push("-e", `@${clientVarsPath}`);
14437
14615
  }
14438
14616
  try {
@@ -14595,18 +14773,18 @@ function registerQueryConversations(server2) {
14595
14773
 
14596
14774
  // src/mcp/tools/load-skill.ts
14597
14775
  import { z as z39 } from "zod";
14598
- import { readFileSync as readFileSync17, readdirSync as readdirSync8, statSync as statSync3 } from "fs";
14599
- import path27 from "path";
14776
+ import { readFileSync as readFileSync18, readdirSync as readdirSync8, statSync as statSync3 } from "fs";
14777
+ import path28 from "path";
14600
14778
  import { homedir as homedir2 } from "os";
14601
- var SKILLS_DIR = path27.join(homedir2(), ".claude", "skills");
14779
+ var SKILLS_DIR = path28.join(homedir2(), ".claude", "skills");
14602
14780
  function listAvailableSkills() {
14603
14781
  try {
14604
14782
  const entries = readdirSync8(SKILLS_DIR);
14605
14783
  return entries.filter((entry) => {
14606
14784
  try {
14607
- const entryPath = path27.join(SKILLS_DIR, entry);
14785
+ const entryPath = path28.join(SKILLS_DIR, entry);
14608
14786
  if (!statSync3(entryPath).isDirectory()) return false;
14609
- const skillFile = path27.join(entryPath, "SKILL.md");
14787
+ const skillFile = path28.join(entryPath, "SKILL.md");
14610
14788
  statSync3(skillFile);
14611
14789
  return true;
14612
14790
  } catch {
@@ -14649,10 +14827,10 @@ ${skills.map((s) => `- ${s}`).join("\n")}`
14649
14827
  }]
14650
14828
  };
14651
14829
  }
14652
- const sanitized = path27.basename(skill_name);
14653
- const skillFile = path27.join(SKILLS_DIR, sanitized, "SKILL.md");
14830
+ const sanitized = path28.basename(skill_name);
14831
+ const skillFile = path28.join(SKILLS_DIR, sanitized, "SKILL.md");
14654
14832
  try {
14655
- const content = readFileSync17(skillFile, "utf-8");
14833
+ const content = readFileSync18(skillFile, "utf-8");
14656
14834
  return {
14657
14835
  content: [{
14658
14836
  type: "text",
@@ -15291,7 +15469,7 @@ init_database();
15291
15469
  import { readdir } from "fs/promises";
15292
15470
  import { createReadStream } from "fs";
15293
15471
  import { createInterface } from "readline";
15294
- import path28 from "path";
15472
+ import path29 from "path";
15295
15473
  import os10 from "os";
15296
15474
  var MODEL_PRICING = {
15297
15475
  // Opus 4.5+ ($5/$25 — Anthropic price drop from original Opus 4)
@@ -15340,18 +15518,18 @@ async function getAgentSpend(period = "7d") {
15340
15518
  for (const row of result.rows) {
15341
15519
  sessionAgent.set(row.session_uuid, row.agent_id);
15342
15520
  }
15343
- const claudeDir = path28.join(os10.homedir(), ".claude", "projects");
15521
+ const claudeDir = path29.join(os10.homedir(), ".claude", "projects");
15344
15522
  let projectDirs = [];
15345
15523
  try {
15346
15524
  const entries = await readdir(claudeDir);
15347
- projectDirs = entries.map((e) => path28.join(claudeDir, e));
15525
+ projectDirs = entries.map((e) => path29.join(claudeDir, e));
15348
15526
  } catch {
15349
15527
  return [];
15350
15528
  }
15351
15529
  const agentTotals = /* @__PURE__ */ new Map();
15352
15530
  for (const [sessionUuid, agentId] of sessionAgent) {
15353
15531
  for (const dir of projectDirs) {
15354
- const jsonlPath = path28.join(dir, `${sessionUuid}.jsonl`);
15532
+ const jsonlPath = path29.join(dir, `${sessionUuid}.jsonl`);
15355
15533
  try {
15356
15534
  const usage = await extractSessionUsage(jsonlPath);
15357
15535
  if (usage.input === 0 && usage.output === 0) continue;
@@ -15929,12 +16107,12 @@ function registerExportGraph(server2) {
15929
16107
  }
15930
16108
  const html = await exportGraphHTML(client);
15931
16109
  const fs = await import("fs");
15932
- const path37 = await import("path");
16110
+ const path38 = await import("path");
15933
16111
  const os11 = await import("os");
15934
- const outDir = path37.join(os11.homedir(), ".exe-os", "exports");
16112
+ const outDir = path38.join(os11.homedir(), ".exe-os", "exports");
15935
16113
  fs.mkdirSync(outDir, { recursive: true });
15936
16114
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
15937
- const filePath = path37.join(outDir, `graph-${timestamp}.html`);
16115
+ const filePath = path38.join(outDir, `graph-${timestamp}.html`);
15938
16116
  fs.writeFileSync(filePath, html, "utf-8");
15939
16117
  return {
15940
16118
  content: [
@@ -16155,10 +16333,10 @@ function registerListAgentSessions(server2) {
16155
16333
 
16156
16334
  // src/mcp/tools/get-daemon-health.ts
16157
16335
  import { z as z54 } from "zod";
16158
- import { existsSync as existsSync21, readFileSync as readFileSync18 } from "fs";
16159
- import path29 from "path";
16336
+ import { existsSync as existsSync22, readFileSync as readFileSync19 } from "fs";
16337
+ import path30 from "path";
16160
16338
  import { homedir as homedir3 } from "os";
16161
- var PID_PATH2 = path29.join(homedir3(), ".exe-os", "exed.pid");
16339
+ var PID_PATH2 = path30.join(homedir3(), ".exe-os", "exed.pid");
16162
16340
  function formatUptime(seconds) {
16163
16341
  const h = Math.floor(seconds / 3600);
16164
16342
  const m = Math.floor(seconds % 3600 / 60);
@@ -16169,8 +16347,8 @@ function formatUptime(seconds) {
16169
16347
  }
16170
16348
  function isDaemonAlive() {
16171
16349
  try {
16172
- if (!existsSync21(PID_PATH2)) return { alive: false, pid: null };
16173
- const pid = parseInt(readFileSync18(PID_PATH2, "utf8").trim(), 10);
16350
+ if (!existsSync22(PID_PATH2)) return { alive: false, pid: null };
16351
+ const pid = parseInt(readFileSync19(PID_PATH2, "utf8").trim(), 10);
16174
16352
  if (isNaN(pid) || pid <= 0) return { alive: false, pid: null };
16175
16353
  process.kill(pid, 0);
16176
16354
  return { alive: true, pid };
@@ -16244,7 +16422,7 @@ init_tmux_routing();
16244
16422
  init_task_scope();
16245
16423
  init_employees();
16246
16424
  import { execSync as execSync10 } from "child_process";
16247
- import { existsSync as existsSync22, readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
16425
+ import { existsSync as existsSync23, readFileSync as readFileSync20, writeFileSync as writeFileSync14 } from "fs";
16248
16426
  import { homedir as homedir4 } from "os";
16249
16427
  import { join as join2 } from "path";
16250
16428
  var IDLE_NUDGE_DEDUP_MS = Number(process.env.EXE_NUDGE_INTERVAL_MS) || 6e4;
@@ -16346,15 +16524,15 @@ function registerGetAutoWakeStatus(server2) {
16346
16524
  init_worker_gate();
16347
16525
  init_config();
16348
16526
  import { z as z56 } from "zod";
16349
- import { readdirSync as readdirSync10, existsSync as existsSync24 } from "fs";
16350
- import path31 from "path";
16351
- var WORKER_PID_DIR2 = path31.join(EXE_AI_DIR, "worker-pids");
16527
+ import { readdirSync as readdirSync10, existsSync as existsSync25 } from "fs";
16528
+ import path32 from "path";
16529
+ var WORKER_PID_DIR2 = path32.join(EXE_AI_DIR, "worker-pids");
16352
16530
  function countAliveWorkers() {
16353
16531
  let alive = 0;
16354
16532
  let stale = 0;
16355
16533
  let reservations = 0;
16356
16534
  try {
16357
- if (!existsSync24(WORKER_PID_DIR2)) return { alive: 0, stale: 0, reservations: 0 };
16535
+ if (!existsSync25(WORKER_PID_DIR2)) return { alive: 0, stale: 0, reservations: 0 };
16358
16536
  const files = readdirSync10(WORKER_PID_DIR2);
16359
16537
  for (const f of files) {
16360
16538
  if (!f.endsWith(".pid")) continue;
@@ -16441,9 +16619,9 @@ function isMainModule(importMetaUrl) {
16441
16619
  }
16442
16620
 
16443
16621
  // src/bin/exe-doctor.ts
16444
- import { existsSync as existsSync25 } from "fs";
16622
+ import { existsSync as existsSync26 } from "fs";
16445
16623
  import { spawn as spawn2 } from "child_process";
16446
- import path32 from "path";
16624
+ import path33 from "path";
16447
16625
  import { randomUUID as randomUUID6 } from "crypto";
16448
16626
 
16449
16627
  // src/lib/conflict-detector.ts
@@ -16846,7 +17024,7 @@ async function auditOrphanedProjects(client) {
16846
17024
  for (const row of result.rows) {
16847
17025
  const name = row.project_name;
16848
17026
  const count = Number(row.cnt);
16849
- const exists = existsSync25(path32.join(home, name)) || existsSync25(path32.join(home, "..", name)) || existsSync25(path32.join(process.cwd(), "..", name));
17027
+ const exists = existsSync26(path33.join(home, name)) || existsSync26(path33.join(home, "..", name)) || existsSync26(path33.join(process.cwd(), "..", name));
16850
17028
  if (!exists) {
16851
17029
  orphans.push({ project_name: name, count });
16852
17030
  }
@@ -17004,7 +17182,7 @@ async function fixNullVectors() {
17004
17182
  }
17005
17183
  }
17006
17184
  const npmRoot = (await import("child_process")).execSync("npm root -g", { encoding: "utf8" }).trim();
17007
- const backfillPath = path32.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
17185
+ const backfillPath = path33.join(npmRoot, "exe-os", "dist", "bin", "backfill-vectors.js");
17008
17186
  return new Promise((resolve, reject) => {
17009
17187
  const child = spawn2("node", [backfillPath], { stdio: "inherit" });
17010
17188
  if (child.pid) registerWorkerPid2(child.pid);
@@ -17202,9 +17380,9 @@ import { z as z58 } from "zod";
17202
17380
 
17203
17381
  // src/lib/cloud-sync.ts
17204
17382
  init_database();
17205
- import { readFileSync as readFileSync21, writeFileSync as writeFileSync16, existsSync as existsSync27, readdirSync as readdirSync11, mkdirSync as mkdirSync13, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
17383
+ import { readFileSync as readFileSync22, writeFileSync as writeFileSync17, existsSync as existsSync28, readdirSync as readdirSync11, mkdirSync as mkdirSync14, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
17206
17384
  import crypto16 from "crypto";
17207
- import path34 from "path";
17385
+ import path35 from "path";
17208
17386
  import { homedir as homedir6 } from "os";
17209
17387
 
17210
17388
  // src/lib/crypto.ts
@@ -17278,7 +17456,7 @@ function sqlSafe(v) {
17278
17456
  }
17279
17457
  function logError(msg) {
17280
17458
  try {
17281
- const logPath = path34.join(homedir6(), ".exe-os", "workers.log");
17459
+ const logPath = path35.join(homedir6(), ".exe-os", "workers.log");
17282
17460
  appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
17283
17461
  `);
17284
17462
  } catch {
@@ -17287,24 +17465,24 @@ function logError(msg) {
17287
17465
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
17288
17466
  var FETCH_TIMEOUT_MS4 = 3e4;
17289
17467
  var PUSH_BATCH_SIZE = 5e3;
17290
- var ROSTER_LOCK_PATH = path34.join(EXE_AI_DIR, "roster-merge.lock");
17468
+ var ROSTER_LOCK_PATH = path35.join(EXE_AI_DIR, "roster-merge.lock");
17291
17469
  var LOCK_STALE_MS = 3e4;
17292
17470
  async function withRosterLock(fn) {
17293
17471
  try {
17294
17472
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
17295
17473
  closeSync2(fd);
17296
- writeFileSync16(ROSTER_LOCK_PATH, String(Date.now()));
17474
+ writeFileSync17(ROSTER_LOCK_PATH, String(Date.now()));
17297
17475
  } catch (err) {
17298
17476
  if (err.code === "EEXIST") {
17299
17477
  try {
17300
- const ts2 = parseInt(readFileSync21(ROSTER_LOCK_PATH, "utf-8"), 10);
17478
+ const ts2 = parseInt(readFileSync22(ROSTER_LOCK_PATH, "utf-8"), 10);
17301
17479
  if (Date.now() - ts2 < LOCK_STALE_MS) {
17302
17480
  throw new Error("Roster merge already in progress \u2014 another sync is running");
17303
17481
  }
17304
17482
  unlinkSync10(ROSTER_LOCK_PATH);
17305
17483
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
17306
17484
  closeSync2(fd);
17307
- writeFileSync16(ROSTER_LOCK_PATH, String(Date.now()));
17485
+ writeFileSync17(ROSTER_LOCK_PATH, String(Date.now()));
17308
17486
  } catch (retryErr) {
17309
17487
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
17310
17488
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -17678,8 +17856,8 @@ async function cloudSync(config2) {
17678
17856
  try {
17679
17857
  const employees = await loadEmployees();
17680
17858
  rosterResult.employees = employees.length;
17681
- const idDir = path34.join(EXE_AI_DIR, "identity");
17682
- if (existsSync27(idDir)) {
17859
+ const idDir = path35.join(EXE_AI_DIR, "identity");
17860
+ if (existsSync28(idDir)) {
17683
17861
  rosterResult.identities = readdirSync11(idDir).filter((f) => f.endsWith(".md")).length;
17684
17862
  }
17685
17863
  } catch {
@@ -17697,48 +17875,56 @@ async function cloudSync(config2) {
17697
17875
  roster: rosterResult
17698
17876
  };
17699
17877
  }
17700
- var ROSTER_DELETIONS_PATH = path34.join(EXE_AI_DIR, "roster-deletions.json");
17878
+ var ROSTER_DELETIONS_PATH = path35.join(EXE_AI_DIR, "roster-deletions.json");
17701
17879
  function consumeRosterDeletions() {
17702
17880
  try {
17703
- if (!existsSync27(ROSTER_DELETIONS_PATH)) return [];
17704
- const deletions = JSON.parse(readFileSync21(ROSTER_DELETIONS_PATH, "utf-8"));
17705
- writeFileSync16(ROSTER_DELETIONS_PATH, "[]");
17881
+ if (!existsSync28(ROSTER_DELETIONS_PATH)) return [];
17882
+ const deletions = JSON.parse(readFileSync22(ROSTER_DELETIONS_PATH, "utf-8"));
17883
+ writeFileSync17(ROSTER_DELETIONS_PATH, "[]");
17706
17884
  return deletions;
17707
17885
  } catch {
17708
17886
  return [];
17709
17887
  }
17710
17888
  }
17711
17889
  function buildRosterBlob(paths) {
17712
- const rosterPath = paths?.rosterPath ?? path34.join(EXE_AI_DIR, "exe-employees.json");
17713
- const identityDir = paths?.identityDir ?? path34.join(EXE_AI_DIR, "identity");
17714
- const configPath = paths?.configPath ?? path34.join(EXE_AI_DIR, "config.json");
17890
+ const rosterPath = paths?.rosterPath ?? path35.join(EXE_AI_DIR, "exe-employees.json");
17891
+ const identityDir = paths?.identityDir ?? path35.join(EXE_AI_DIR, "identity");
17892
+ const configPath = paths?.configPath ?? path35.join(EXE_AI_DIR, "config.json");
17715
17893
  let roster = [];
17716
- if (existsSync27(rosterPath)) {
17894
+ if (existsSync28(rosterPath)) {
17717
17895
  try {
17718
- roster = JSON.parse(readFileSync21(rosterPath, "utf-8"));
17896
+ roster = JSON.parse(readFileSync22(rosterPath, "utf-8"));
17719
17897
  } catch {
17720
17898
  }
17721
17899
  }
17722
17900
  const identities = {};
17723
- if (existsSync27(identityDir)) {
17901
+ if (existsSync28(identityDir)) {
17724
17902
  for (const file of readdirSync11(identityDir).filter((f) => f.endsWith(".md"))) {
17725
17903
  try {
17726
- identities[file] = readFileSync21(path34.join(identityDir, file), "utf-8");
17904
+ identities[file] = readFileSync22(path35.join(identityDir, file), "utf-8");
17727
17905
  } catch {
17728
17906
  }
17729
17907
  }
17730
17908
  }
17731
17909
  let config2;
17732
- if (existsSync27(configPath)) {
17910
+ if (existsSync28(configPath)) {
17733
17911
  try {
17734
- config2 = JSON.parse(readFileSync21(configPath, "utf-8"));
17912
+ config2 = JSON.parse(readFileSync22(configPath, "utf-8"));
17913
+ } catch {
17914
+ }
17915
+ }
17916
+ let agentConfig;
17917
+ const agentConfigPath = path35.join(EXE_AI_DIR, "agent-config.json");
17918
+ if (existsSync28(agentConfigPath)) {
17919
+ try {
17920
+ agentConfig = JSON.parse(readFileSync22(agentConfigPath, "utf-8"));
17735
17921
  } catch {
17736
17922
  }
17737
17923
  }
17738
17924
  const deletedNames = consumeRosterDeletions();
17739
- const content = JSON.stringify({ roster, identities, config: config2, deletedNames });
17925
+ const content = JSON.stringify({ roster, identities, config: config2, agentConfig, deletedNames });
17740
17926
  const hash = crypto16.createHash("sha256").update(content).digest("hex").slice(0, 16);
17741
- return { roster, identities, config: config2, deletedNames, version: hash };
17927
+ return { roster, identities, config: config2, agentConfig, deletedNames, version: hash };
17742
17928
  }
17743
17929
  async function cloudPushRoster(config2) {
17744
17930
  assertSecureEndpoint(config2.endpoint);
@@ -17807,23 +17993,23 @@ async function cloudPullRoster(config2) {
17807
17993
  }
17808
17994
  }
17809
17995
  function mergeConfig(remoteConfig, configPath) {
17810
- const cfgPath = configPath ?? path34.join(EXE_AI_DIR, "config.json");
17996
+ const cfgPath = configPath ?? path35.join(EXE_AI_DIR, "config.json");
17811
17997
  let local = {};
17812
- if (existsSync27(cfgPath)) {
17998
+ if (existsSync28(cfgPath)) {
17813
17999
  try {
17814
- local = JSON.parse(readFileSync21(cfgPath, "utf-8"));
18000
+ local = JSON.parse(readFileSync22(cfgPath, "utf-8"));
17815
18001
  } catch {
17816
18002
  }
17817
18003
  }
17818
18004
  const merged = { ...remoteConfig, ...local };
17819
- const dir = path34.dirname(cfgPath);
17820
- if (!existsSync27(dir)) mkdirSync13(dir, { recursive: true });
17821
- writeFileSync16(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
18005
+ const dir = path35.dirname(cfgPath);
18006
+ if (!existsSync28(dir)) mkdirSync14(dir, { recursive: true });
18007
+ writeFileSync17(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
17822
18008
  }
17823
18009
  async function mergeRosterFromRemote(remote, paths) {
17824
18010
  return withRosterLock(async () => {
17825
18011
  const rosterPath = paths?.rosterPath ?? void 0;
17826
- const identityDir = paths?.identityDir ?? path34.join(EXE_AI_DIR, "identity");
18012
+ const identityDir = paths?.identityDir ?? path35.join(EXE_AI_DIR, "identity");
17827
18013
  const localEmployees = await loadEmployees(rosterPath);
17828
18014
  const localNames = new Set(localEmployees.map((e) => e.name));
17829
18015
  let added = 0;
@@ -17844,15 +18030,15 @@ async function mergeRosterFromRemote(remote, paths) {
17844
18030
  ) ?? lookupKey;
17845
18031
  const remoteIdentity = remote.identities[matchedKey];
17846
18032
  if (remoteIdentity) {
17847
- if (!existsSync27(identityDir)) mkdirSync13(identityDir, { recursive: true });
17848
- const idPath = path34.join(identityDir, `${remoteEmp.name}.md`);
18033
+ if (!existsSync28(identityDir)) mkdirSync14(identityDir, { recursive: true });
18034
+ const idPath = path35.join(identityDir, `${remoteEmp.name}.md`);
17849
18035
  let localIdentity = null;
17850
18036
  try {
17851
- localIdentity = existsSync27(idPath) ? readFileSync21(idPath, "utf-8") : null;
18037
+ localIdentity = existsSync28(idPath) ? readFileSync22(idPath, "utf-8") : null;
17852
18038
  } catch {
17853
18039
  }
17854
18040
  if (localIdentity !== remoteIdentity) {
17855
- writeFileSync16(idPath, remoteIdentity, "utf-8");
18041
+ writeFileSync17(idPath, remoteIdentity, "utf-8");
17856
18042
  identitiesUpdated++;
17857
18043
  }
17858
18044
  }
@@ -17876,6 +18062,21 @@ async function mergeRosterFromRemote(remote, paths) {
17876
18062
  } catch {
17877
18063
  }
17878
18064
  }
18065
+ if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
18066
+ try {
18067
+ const agentConfigPath = path35.join(EXE_AI_DIR, "agent-config.json");
18068
+ let local = {};
18069
+ if (existsSync28(agentConfigPath)) {
18070
+ try {
18071
+ local = JSON.parse(readFileSync22(agentConfigPath, "utf-8"));
18072
+ } catch {
18073
+ }
18074
+ }
18075
+ const merged = { ...remote.agentConfig, ...local };
18076
+ writeFileSync17(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
18077
+ } catch {
18078
+ }
18079
+ }
17879
18080
  return { added, identitiesUpdated };
17880
18081
  });
17881
18082
  }
@@ -18598,11 +18799,11 @@ import { z as z62 } from "zod";
18598
18799
  // src/lib/people.ts
18599
18800
  init_config();
18600
18801
  import { readFile as readFile5, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
18601
- import { existsSync as existsSync28, readFileSync as readFileSync22 } from "fs";
18602
- import path35 from "path";
18603
- var PEOPLE_PATH = path35.join(EXE_AI_DIR, "people.json");
18802
+ import { existsSync as existsSync29, readFileSync as readFileSync23 } from "fs";
18803
+ import path36 from "path";
18804
+ var PEOPLE_PATH = path36.join(EXE_AI_DIR, "people.json");
18604
18805
  async function loadPeople() {
18605
- if (!existsSync28(PEOPLE_PATH)) return [];
18806
+ if (!existsSync29(PEOPLE_PATH)) return [];
18606
18807
  try {
18607
18808
  const raw = await readFile5(PEOPLE_PATH, "utf-8");
18608
18809
  return JSON.parse(raw);
@@ -18611,7 +18812,7 @@ async function loadPeople() {
18611
18812
  }
18612
18813
  }
18613
18814
  async function savePeople(people) {
18614
- await mkdir5(path35.dirname(PEOPLE_PATH), { recursive: true });
18815
+ await mkdir5(path36.dirname(PEOPLE_PATH), { recursive: true });
18615
18816
  await writeFile6(PEOPLE_PATH, JSON.stringify(people, null, 2) + "\n", "utf-8");
18616
18817
  }
18617
18818
  async function addPerson(person) {
@@ -18713,6 +18914,103 @@ Notes: ${person.notes}` : ""}`
18713
18914
  );
18714
18915
  }
18715
18916
 
18917
+ // src/mcp/tools/set-agent-config.ts
18918
+ init_active_agent();
18919
+ init_agent_config();
18920
+ init_runtime_table();
18921
+ init_employees();
18922
+ import { z as z63 } from "zod";
18923
+ function registerSetAgentConfig(server2) {
18924
+ server2.registerTool(
18925
+ "set_agent_config",
18926
+ {
18927
+ title: "Set Agent Config",
18928
+ description: "Set or view per-agent runtime + model configuration. Controls which runtime (claude, codex, opencode) and model each agent uses when dispatched. COO-only. Omit runtime/model to view current config for an agent or all agents.",
18929
+ inputSchema: {
18930
+ agent_id: z63.string().optional().describe("Agent name. Omit to view all agents."),
18931
+ runtime: z63.string().optional().describe("Runtime: claude, codex, or opencode"),
18932
+ model: z63.string().optional().describe("Model name (e.g. claude-opus-4, gpt-5.4, minimax-m2.7)")
18933
+ }
18934
+ },
18935
+ async ({ agent_id, runtime, model }) => {
18936
+ const { agentRole } = getActiveAgent();
18937
+ if (agentRole !== "COO") {
18938
+ return {
18939
+ content: [
18940
+ {
18941
+ type: "text",
18942
+ text: `Access denied. set_agent_config is COO-only. Your role: ${agentRole}`
18943
+ }
18944
+ ],
18945
+ isError: true
18946
+ };
18947
+ }
18948
+ if (!runtime || !model) {
18949
+ if (agent_id) {
18950
+ const cfg = getAgentRuntime(agent_id);
18951
+ return {
18952
+ content: [
18953
+ {
18954
+ type: "text",
18955
+ text: `${agent_id}: runtime=${cfg.runtime} model=${cfg.model}`
18956
+ }
18957
+ ]
18958
+ };
18959
+ }
18960
+ const config2 = loadAgentConfig();
18961
+ let employees = [];
18962
+ try {
18963
+ employees = loadEmployeesSync();
18964
+ } catch {
18965
+ }
18966
+ const lines = ["Agent Runtime Configuration", "\u2550".repeat(55)];
18967
+ lines.push(
18968
+ "Agent".padEnd(12) + "Role".padEnd(16) + "Runtime".padEnd(12) + "Model"
18969
+ );
18970
+ lines.push("\u2500".repeat(55));
18971
+ for (const emp of employees) {
18972
+ const entry = config2[emp.name];
18973
+ const rt = entry?.runtime ?? DEFAULT_RUNTIME;
18974
+ const mdl = entry?.model ?? DEFAULT_MODELS[DEFAULT_RUNTIME];
18975
+ const tag = entry ? "" : " (default)";
18976
+ lines.push(
18977
+ emp.name.padEnd(12) + (emp.role ?? "").padEnd(16) + rt.padEnd(12) + mdl + tag
18978
+ );
18979
+ }
18980
+ return {
18981
+ content: [{ type: "text", text: lines.join("\n") }]
18982
+ };
18983
+ }
18984
+ if (!agent_id) {
18985
+ return {
18986
+ content: [
18987
+ {
18988
+ type: "text",
18989
+ text: "agent_id is required when setting runtime/model."
18990
+ }
18991
+ ],
18992
+ isError: true
18993
+ };
18994
+ }
18995
+ const result = setAgentRuntime(agent_id, runtime, model);
18996
+ if (!result.ok) {
18997
+ return {
18998
+ content: [{ type: "text", text: result.error }],
18999
+ isError: true
19000
+ };
19001
+ }
19002
+ return {
19003
+ content: [
19004
+ {
19005
+ type: "text",
19006
+ text: `Set ${agent_id} \u2192 runtime=${runtime} model=${model}`
19007
+ }
19008
+ ]
19009
+ };
19010
+ }
19011
+ );
19012
+ }
19013
+
18716
19014
  // src/lib/telemetry.ts
18717
19015
  var ENABLED = process.env.EXE_TELEMETRY === "1";
18718
19016
  var initialized = false;
@@ -18844,6 +19142,7 @@ registerGetLicenseStatus(server);
18844
19142
  registerAddPerson(server);
18845
19143
  registerListPeople(server);
18846
19144
  registerGetPerson(server);
19145
+ registerSetAgentConfig(server);
18847
19146
  try {
18848
19147
  await initStore();
18849
19148
  process.stderr.write("[exe-os] MCP server starting...\n");
@@ -18904,14 +19203,14 @@ try {
18904
19203
  `
18905
19204
  );
18906
19205
  const thisFile = fileURLToPath5(import.meta.url);
18907
- const backfillPath = path36.resolve(
18908
- path36.dirname(thisFile),
19206
+ const backfillPath = path37.resolve(
19207
+ path37.dirname(thisFile),
18909
19208
  "../bin/backfill-vectors.js"
18910
19209
  );
18911
- if (existsSync29(backfillPath)) {
19210
+ if (existsSync30(backfillPath)) {
18912
19211
  const { EXE_AI_DIR: exeDir } = await Promise.resolve().then(() => (init_config(), config_exports));
18913
- const logPath = path36.join(exeDir, "workers.log");
18914
- mkdirSync14(path36.dirname(logPath), { recursive: true });
19212
+ const logPath = path37.join(exeDir, "workers.log");
19213
+ mkdirSync15(path37.dirname(logPath), { recursive: true });
18915
19214
  let logFd = "ignore";
18916
19215
  try {
18917
19216
  logFd = openSync3(logPath, "a");