@askexenow/exe-os 0.9.18 → 0.9.20

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.
package/dist/bin/cli.js CHANGED
@@ -12333,11 +12333,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
12333
12333
  if (pending instanceof Promise) {
12334
12334
  pending.then((count) => {
12335
12335
  if (count > 0) {
12336
- execSync8(
12337
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
12338
- { timeout: 3e3 }
12339
- );
12340
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
12336
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
12341
12337
  }
12342
12338
  }).catch(() => {
12343
12339
  });
@@ -12345,11 +12341,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
12345
12341
  }
12346
12342
  } catch {
12347
12343
  }
12348
- execSync8(
12349
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
12350
- { timeout: 3e3 }
12351
- );
12352
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
12344
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
12353
12345
  return true;
12354
12346
  } catch {
12355
12347
  return false;
@@ -15627,6 +15619,9 @@ async function runUpdate(cliArgs) {
15627
15619
  console.log(" Try: npm cache clean --force && npm install -g @askexenow/exe-os@latest");
15628
15620
  }
15629
15621
  console.log(" Hooks re-wired, daemon restarted automatically.");
15622
+ console.log("");
15623
+ console.log(" \x1B[33m\u26A1 Run /mcp in each active Claude Code session to pick up new tools.\x1B[0m");
15624
+ console.log(" \x1B[2m(MCP servers can't hot-reload \u2014 Claude Code needs to reconnect them.)\x1B[0m");
15630
15625
  try {
15631
15626
  const { existsSync: exists, readFileSync: readFile8 } = await import("fs");
15632
15627
  const p = await import("path");
@@ -7551,11 +7551,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
7551
7551
  if (pending instanceof Promise) {
7552
7552
  pending.then((count) => {
7553
7553
  if (count > 0) {
7554
- execSync7(
7555
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
7556
- { timeout: 3e3 }
7557
- );
7558
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
7554
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
7559
7555
  }
7560
7556
  }).catch(() => {
7561
7557
  });
@@ -7563,11 +7559,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
7563
7559
  }
7564
7560
  } catch {
7565
7561
  }
7566
- execSync7(
7567
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
7568
- { timeout: 3e3 }
7569
- );
7570
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
7562
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
7571
7563
  return true;
7572
7564
  } catch {
7573
7565
  return false;
@@ -6131,11 +6131,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6131
6131
  if (pending instanceof Promise) {
6132
6132
  pending.then((count) => {
6133
6133
  if (count > 0) {
6134
- execSync6(
6135
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6136
- { timeout: 3e3 }
6137
- );
6138
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
6134
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
6139
6135
  }
6140
6136
  }).catch(() => {
6141
6137
  });
@@ -6143,11 +6139,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6143
6139
  }
6144
6140
  } catch {
6145
6141
  }
6146
- execSync6(
6147
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6148
- { timeout: 3e3 }
6149
- );
6150
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
6142
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
6151
6143
  return true;
6152
6144
  } catch {
6153
6145
  return false;
@@ -10968,11 +10968,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
10968
10968
  if (pending instanceof Promise) {
10969
10969
  pending.then((count) => {
10970
10970
  if (count > 0) {
10971
- execSync6(
10972
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
10973
- { timeout: 3e3 }
10974
- );
10975
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
10971
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
10976
10972
  }
10977
10973
  }).catch(() => {
10978
10974
  });
@@ -10980,11 +10976,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
10980
10976
  }
10981
10977
  } catch {
10982
10978
  }
10983
- execSync6(
10984
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
10985
- { timeout: 3e3 }
10986
- );
10987
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
10979
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
10988
10980
  return true;
10989
10981
  } catch {
10990
10982
  return false;
@@ -12269,7 +12261,7 @@ var WebhookServer = class {
12269
12261
  sendJson(res, 200, {
12270
12262
  status: "ok",
12271
12263
  uptime,
12272
- handlers: [...this.handlers.keys()]
12264
+ adapterCount: this.handlers.size
12273
12265
  });
12274
12266
  }
12275
12267
  async handleQuery(req, res) {
@@ -12422,13 +12414,24 @@ function matchesChannel(msgChannel, matchChannel) {
12422
12414
  const channels = Array.isArray(matchChannel) ? matchChannel : [matchChannel];
12423
12415
  return channels.includes(msgChannel);
12424
12416
  }
12417
+ var MAX_REGEX_LENGTH = 200;
12418
+ function safeRegExp(pattern, flags) {
12419
+ if (pattern.length > MAX_REGEX_LENGTH) return null;
12420
+ try {
12421
+ return new RegExp(pattern, flags);
12422
+ } catch {
12423
+ return null;
12424
+ }
12425
+ }
12425
12426
  function matchesSender(msgSender, matchSender) {
12426
12427
  if (!matchSender) return true;
12427
- return new RegExp(matchSender).test(msgSender);
12428
+ const re = safeRegExp(matchSender);
12429
+ return re ? re.test(msgSender) : false;
12428
12430
  }
12429
12431
  function matchesTextPattern(msgText, matchPattern) {
12430
12432
  if (!matchPattern) return true;
12431
- return new RegExp(matchPattern, "i").test(msgText);
12433
+ const re = safeRegExp(matchPattern, "i");
12434
+ return re ? re.test(msgText) : false;
12432
12435
  }
12433
12436
  function matchesRoute(msg, match) {
12434
12437
  return matchesPlatform(msg.platform, match.platform) && matchesChannel(msg.channelId, match.channelId) && matchesSender(msg.senderId, match.senderId) && matchesTextPattern(msg.text, match.textPattern);
@@ -4891,6 +4891,79 @@ function identityPathFor(agent) {
4891
4891
  }
4892
4892
  return exactPath;
4893
4893
  }
4894
+ var DEFAULT_ROLE_MCP_MAP = {
4895
+ coo: ["exe-browser", "brave-search"],
4896
+ cto: ["brave-search"],
4897
+ "principal engineer": [],
4898
+ "staff code reviewer": [],
4899
+ cmo: ["exe-browser", "brave-search"],
4900
+ "content production specialist": ["exe-create"],
4901
+ "ai product lead": ["brave-search"]
4902
+ };
4903
+ var ALWAYS_INCLUDE_SERVERS = ["exe-mem"];
4904
+ function collectAllMcpServers() {
4905
+ const servers = {};
4906
+ const sources = [
4907
+ path11.join(os7.homedir(), ".claude.json"),
4908
+ path11.join(os7.homedir(), ".claude", "settings.json")
4909
+ ];
4910
+ for (const src of sources) {
4911
+ try {
4912
+ if (!existsSync10(src)) continue;
4913
+ const data = JSON.parse(readFileSync7(src, "utf-8"));
4914
+ const block = data.mcpServers;
4915
+ if (!block) continue;
4916
+ for (const [name, cfg] of Object.entries(block)) {
4917
+ if (!servers[name]) servers[name] = cfg;
4918
+ }
4919
+ } catch {
4920
+ }
4921
+ }
4922
+ return servers;
4923
+ }
4924
+ function generateLeanMcpConfig(agent, role) {
4925
+ try {
4926
+ const allServers = collectAllMcpServers();
4927
+ if (Object.keys(allServers).length === 0) return null;
4928
+ const normalizedRole = role.toLowerCase();
4929
+ const extraServers = DEFAULT_ROLE_MCP_MAP[normalizedRole] ?? [];
4930
+ const allowedNames = /* @__PURE__ */ new Set([...ALWAYS_INCLUDE_SERVERS, ...extraServers]);
4931
+ const leanServers = {};
4932
+ for (const [name, cfg] of Object.entries(allServers)) {
4933
+ if (allowedNames.has(name)) {
4934
+ leanServers[name] = cfg;
4935
+ }
4936
+ }
4937
+ if (Object.keys(leanServers).length >= Object.keys(allServers).length) return null;
4938
+ if (!leanServers["exe-mem"]) {
4939
+ const packageRoot = path11.resolve(path11.dirname(new URL(import.meta.url).pathname), "..", "..");
4940
+ leanServers["exe-mem"] = {
4941
+ type: "stdio",
4942
+ command: "node",
4943
+ args: [path11.join(packageRoot, "dist", "mcp", "server.js")],
4944
+ env: {}
4945
+ };
4946
+ }
4947
+ const configDir = path11.join(os7.homedir(), ".exe-os", "mcp-configs");
4948
+ mkdirSync5(configDir, { recursive: true });
4949
+ const configPath = path11.join(configDir, `${agent}-lean.json`);
4950
+ writeFileSync6(configPath, JSON.stringify({ mcpServers: leanServers }, null, 2), "utf-8");
4951
+ const saved = Object.keys(allServers).length - Object.keys(leanServers).length;
4952
+ if (saved > 0) {
4953
+ process.stderr.write(
4954
+ `[exe-launch-agent] lean MCP: ${agent} (${role}) gets ${Object.keys(leanServers).length}/${Object.keys(allServers).length} servers (${saved} skipped)
4955
+ `
4956
+ );
4957
+ }
4958
+ return configPath;
4959
+ } catch (err) {
4960
+ process.stderr.write(
4961
+ `[exe-launch-agent] lean MCP config generation failed: ${err instanceof Error ? err.message : String(err)}
4962
+ `
4963
+ );
4964
+ return null;
4965
+ }
4966
+ }
4894
4967
  function leanMcpConfigFor(agent) {
4895
4968
  const p = path11.join(os7.homedir(), ".exe-os", "mcp-configs", `${agent}-lean.json`);
4896
4969
  return existsSync10(p) ? p : null;
@@ -5148,16 +5221,10 @@ async function main() {
5148
5221
  }
5149
5222
  }
5150
5223
  }
5151
- const plan = buildLaunchPlan(agent, behaviorsPath, passthrough, hasAgentFlag, provider);
5152
5224
  const memoryAgent = baseAgentName(agent);
5153
- process.env.AGENT_ID = memoryAgent;
5154
- if (!process.env.EXE_SESSION_KEY) {
5155
- process.env.EXE_SESSION_KEY = `launcher-${Date.now()}-${process.pid}`;
5156
- }
5157
- process.env.EXE_RUNTIME = "claude";
5158
5225
  const empRole = (() => {
5159
5226
  try {
5160
- const emps = __require("fs").readFileSync(
5227
+ const emps = readFileSync7(
5161
5228
  path11.join(os7.homedir(), ".exe-os", "exe-employees.json"),
5162
5229
  "utf-8"
5163
5230
  );
@@ -5169,6 +5236,13 @@ async function main() {
5169
5236
  return "employee";
5170
5237
  }
5171
5238
  })();
5239
+ generateLeanMcpConfig(memoryAgent, empRole);
5240
+ const plan = buildLaunchPlan(agent, behaviorsPath, passthrough, hasAgentFlag, provider);
5241
+ process.env.AGENT_ID = memoryAgent;
5242
+ if (!process.env.EXE_SESSION_KEY) {
5243
+ process.env.EXE_SESSION_KEY = `launcher-${Date.now()}-${process.pid}`;
5244
+ }
5245
+ process.env.EXE_RUNTIME = "claude";
5172
5246
  process.env.AGENT_ROLE = empRole;
5173
5247
  try {
5174
5248
  const { writeActiveAgent: writeActiveAgent2 } = await Promise.resolve().then(() => (init_active_agent(), active_agent_exports));
@@ -5206,6 +5280,7 @@ export {
5206
5280
  applyProviderEnv,
5207
5281
  buildLaunchPlan,
5208
5282
  ccSupportsFlag,
5283
+ generateLeanMcpConfig,
5209
5284
  parseBasename,
5210
5285
  resolveAgent
5211
5286
  };
@@ -7264,11 +7264,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName2, taskTit
7264
7264
  if (pending instanceof Promise) {
7265
7265
  pending.then((count) => {
7266
7266
  if (count > 0) {
7267
- execSync6(
7268
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
7269
- { timeout: 3e3 }
7270
- );
7271
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName2} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
7267
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName2} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
7272
7268
  }
7273
7269
  }).catch(() => {
7274
7270
  });
@@ -7276,11 +7272,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName2, taskTit
7276
7272
  }
7277
7273
  } catch {
7278
7274
  }
7279
- execSync6(
7280
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
7281
- { timeout: 3e3 }
7282
- );
7283
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName2} completed "${taskTitle.slice(0, 50)}")`);
7275
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName2} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
7284
7276
  return true;
7285
7277
  } catch {
7286
7278
  return false;
@@ -6048,11 +6048,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6048
6048
  if (pending instanceof Promise) {
6049
6049
  pending.then((count) => {
6050
6050
  if (count > 0) {
6051
- execSync6(
6052
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6053
- { timeout: 3e3 }
6054
- );
6055
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
6051
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
6056
6052
  }
6057
6053
  }).catch(() => {
6058
6054
  });
@@ -6060,11 +6056,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6060
6056
  }
6061
6057
  } catch {
6062
6058
  }
6063
- execSync6(
6064
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6065
- { timeout: 3e3 }
6066
- );
6067
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
6059
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
6068
6060
  return true;
6069
6061
  } catch {
6070
6062
  return false;
@@ -1597,7 +1597,7 @@ var init_installer2 = __esm({
1597
1597
 
1598
1598
  // src/bin/install.ts
1599
1599
  init_installer();
1600
- import { existsSync as existsSync10, readFileSync as readFileSync7, unlinkSync as unlinkSync3, readdirSync as readdirSync2, openSync, closeSync } from "fs";
1600
+ import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3, readdirSync as readdirSync2, openSync, closeSync } from "fs";
1601
1601
  import { spawn, execSync as execSync3 } from "child_process";
1602
1602
  import path9 from "path";
1603
1603
  import os7 from "os";
@@ -1777,6 +1777,13 @@ function restartDaemon() {
1777
1777
  }
1778
1778
  } catch {
1779
1779
  }
1780
+ try {
1781
+ const versionPath = path9.join(EXE_DIR, "mcp-version");
1782
+ writeFileSync6(versionPath, `deploy-${Date.now()}`);
1783
+ process.stderr.write(`exe-os: MCP version marker updated \u2014 servers will hot-reload within 10s
1784
+ `);
1785
+ } catch {
1786
+ }
1780
1787
  try {
1781
1788
  const wpDir = path9.join(EXE_DIR, "worker-pids");
1782
1789
  if (existsSync10(wpDir)) {
@@ -7041,11 +7041,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
7041
7041
  if (pending instanceof Promise) {
7042
7042
  pending.then((count) => {
7043
7043
  if (count > 0) {
7044
- execSync6(
7045
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
7046
- { timeout: 3e3 }
7047
- );
7048
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
7044
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
7049
7045
  }
7050
7046
  }).catch(() => {
7051
7047
  });
@@ -7053,11 +7049,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
7053
7049
  }
7054
7050
  } catch {
7055
7051
  }
7056
- execSync6(
7057
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
7058
- { timeout: 3e3 }
7059
- );
7060
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
7052
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
7061
7053
  return true;
7062
7054
  } catch {
7063
7055
  return false;
@@ -6119,11 +6119,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6119
6119
  if (pending instanceof Promise) {
6120
6120
  pending.then((count) => {
6121
6121
  if (count > 0) {
6122
- execSync6(
6123
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6124
- { timeout: 3e3 }
6125
- );
6126
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
6122
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
6127
6123
  }
6128
6124
  }).catch(() => {
6129
6125
  });
@@ -6131,11 +6127,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6131
6127
  }
6132
6128
  } catch {
6133
6129
  }
6134
- execSync6(
6135
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6136
- { timeout: 3e3 }
6137
- );
6138
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
6130
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
6139
6131
  return true;
6140
6132
  } catch {
6141
6133
  return false;
@@ -724,6 +724,9 @@ async function runUpdate(cliArgs) {
724
724
  console.log(" Try: npm cache clean --force && npm install -g @askexenow/exe-os@latest");
725
725
  }
726
726
  console.log(" Hooks re-wired, daemon restarted automatically.");
727
+ console.log("");
728
+ console.log(" \x1B[33m\u26A1 Run /mcp in each active Claude Code session to pick up new tools.\x1B[0m");
729
+ console.log(" \x1B[2m(MCP servers can't hot-reload \u2014 Claude Code needs to reconnect them.)\x1B[0m");
727
730
  try {
728
731
  const { existsSync: exists, readFileSync: readFile2 } = await import("fs");
729
732
  const p = await import("path");
@@ -8884,11 +8884,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
8884
8884
  if (pending instanceof Promise) {
8885
8885
  pending.then((count) => {
8886
8886
  if (count > 0) {
8887
- execSync6(
8888
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
8889
- { timeout: 3e3 }
8890
- );
8891
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
8887
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
8892
8888
  }
8893
8889
  }).catch(() => {
8894
8890
  });
@@ -8896,11 +8892,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
8896
8892
  }
8897
8893
  } catch {
8898
8894
  }
8899
- execSync6(
8900
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
8901
- { timeout: 3e3 }
8902
- );
8903
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
8895
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
8904
8896
  return true;
8905
8897
  } catch {
8906
8898
  return false;
@@ -9556,13 +9548,24 @@ function matchesChannel(msgChannel, matchChannel) {
9556
9548
  const channels = Array.isArray(matchChannel) ? matchChannel : [matchChannel];
9557
9549
  return channels.includes(msgChannel);
9558
9550
  }
9551
+ var MAX_REGEX_LENGTH = 200;
9552
+ function safeRegExp(pattern, flags) {
9553
+ if (pattern.length > MAX_REGEX_LENGTH) return null;
9554
+ try {
9555
+ return new RegExp(pattern, flags);
9556
+ } catch {
9557
+ return null;
9558
+ }
9559
+ }
9559
9560
  function matchesSender(msgSender, matchSender) {
9560
9561
  if (!matchSender) return true;
9561
- return new RegExp(matchSender).test(msgSender);
9562
+ const re = safeRegExp(matchSender);
9563
+ return re ? re.test(msgSender) : false;
9562
9564
  }
9563
9565
  function matchesTextPattern(msgText, matchPattern) {
9564
9566
  if (!matchPattern) return true;
9565
- return new RegExp(matchPattern, "i").test(msgText);
9567
+ const re = safeRegExp(matchPattern, "i");
9568
+ return re ? re.test(msgText) : false;
9566
9569
  }
9567
9570
  function matchesRoute(msg, match) {
9568
9571
  return matchesPlatform(msg.platform, match.platform) && matchesChannel(msg.channelId, match.channelId) && matchesSender(msg.senderId, match.senderId) && matchesTextPattern(msg.text, match.textPattern);
@@ -9602,6 +9605,12 @@ function validateGatewayConfig(config2) {
9602
9605
  if (!route.target) {
9603
9606
  warnings.push(`Route "${route.name}" has no target employee`);
9604
9607
  }
9608
+ if (route.match.senderId && !safeRegExp(route.match.senderId)) {
9609
+ warnings.push(`Route "${route.name}" has invalid senderId regex: ${route.match.senderId}`);
9610
+ }
9611
+ if (route.match.textPattern && !safeRegExp(route.match.textPattern, "i")) {
9612
+ warnings.push(`Route "${route.name}" has invalid textPattern regex: ${route.match.textPattern}`);
9613
+ }
9605
9614
  const isEmptyMatch = !route.match.platform && !route.match.channelId && !route.match.senderId && !route.match.textPattern;
9606
9615
  if (isEmptyMatch && config2.routes.indexOf(route) !== config2.routes.length - 1) {
9607
9616
  warnings.push(
@@ -5315,11 +5315,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
5315
5315
  if (pending instanceof Promise) {
5316
5316
  pending.then((count) => {
5317
5317
  if (count > 0) {
5318
- execSync4(
5319
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
5320
- { timeout: 3e3 }
5321
- );
5322
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
5318
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
5323
5319
  }
5324
5320
  }).catch(() => {
5325
5321
  });
@@ -5327,11 +5323,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
5327
5323
  }
5328
5324
  } catch {
5329
5325
  }
5330
- execSync4(
5331
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
5332
- { timeout: 3e3 }
5333
- );
5334
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
5326
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
5335
5327
  return true;
5336
5328
  } catch {
5337
5329
  return false;
@@ -6047,11 +6047,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6047
6047
  if (pending instanceof Promise) {
6048
6048
  pending.then((count) => {
6049
6049
  if (count > 0) {
6050
- execSync6(
6051
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6052
- { timeout: 3e3 }
6053
- );
6054
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
6050
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
6055
6051
  }
6056
6052
  }).catch(() => {
6057
6053
  });
@@ -6059,11 +6055,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6059
6055
  }
6060
6056
  } catch {
6061
6057
  }
6062
- execSync6(
6063
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6064
- { timeout: 3e3 }
6065
- );
6066
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
6058
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
6067
6059
  return true;
6068
6060
  } catch {
6069
6061
  return false;
@@ -6031,11 +6031,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6031
6031
  if (pending instanceof Promise) {
6032
6032
  pending.then((count) => {
6033
6033
  if (count > 0) {
6034
- execSync7(
6035
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6036
- { timeout: 3e3 }
6037
- );
6038
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
6034
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
6039
6035
  }
6040
6036
  }).catch(() => {
6041
6037
  });
@@ -6043,11 +6039,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6043
6039
  }
6044
6040
  } catch {
6045
6041
  }
6046
- execSync7(
6047
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6048
- { timeout: 3e3 }
6049
- );
6050
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
6042
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
6051
6043
  return true;
6052
6044
  } catch {
6053
6045
  return false;
@@ -8585,11 +8585,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
8585
8585
  if (pending instanceof Promise) {
8586
8586
  pending.then((count) => {
8587
8587
  if (count > 0) {
8588
- execSync9(
8589
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
8590
- { timeout: 3e3 }
8591
- );
8592
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
8588
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
8593
8589
  }
8594
8590
  }).catch(() => {
8595
8591
  });
@@ -8597,11 +8593,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
8597
8593
  }
8598
8594
  } catch {
8599
8595
  }
8600
- execSync9(
8601
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
8602
- { timeout: 3e3 }
8603
- );
8604
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
8596
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
8605
8597
  return true;
8606
8598
  } catch {
8607
8599
  return false;
@@ -10152,14 +10144,14 @@ process.stdin.on("end", async () => {
10152
10144
  const agentName = baseAgentName2(session.split("-")[0] ?? session);
10153
10145
  if (isSessionBusy2(session) || hasInProgressTask(agentName)) return true;
10154
10146
  const rtConfig = getAgentRuntime2(agentName);
10147
+ const nudgeMsg = "You have pending notifications. Run list_tasks to check for assigned work.";
10148
+ transport.sendKeys(session, nudgeMsg);
10155
10149
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
10156
- transport.sendKeys(session, "You have new notifications. Call list_tasks to check for assigned work.");
10157
10150
  try {
10158
- execSync10(`tmux send-keys -t ${session} Tab`, { encoding: "utf8", timeout: 2e3 });
10151
+ const { execFileSync: efs } = __require("child_process");
10152
+ efs("tmux", ["send-keys", "-t", session, "Tab"], { encoding: "utf8", timeout: 2e3 });
10159
10153
  } catch {
10160
10154
  }
10161
- } else {
10162
- transport.sendKeys(session, "/exe-intercom");
10163
10155
  }
10164
10156
  return true;
10165
10157
  } catch {
@@ -6240,11 +6240,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6240
6240
  if (pending instanceof Promise) {
6241
6241
  pending.then((count) => {
6242
6242
  if (count > 0) {
6243
- execSync7(
6244
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6245
- { timeout: 3e3 }
6246
- );
6247
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
6243
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
6248
6244
  }
6249
6245
  }).catch(() => {
6250
6246
  });
@@ -6252,11 +6248,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6252
6248
  }
6253
6249
  } catch {
6254
6250
  }
6255
- execSync7(
6256
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6257
- { timeout: 3e3 }
6258
- );
6259
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
6251
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
6260
6252
  return true;
6261
6253
  } catch {
6262
6254
  return false;
package/dist/index.js CHANGED
@@ -6420,11 +6420,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6420
6420
  if (pending instanceof Promise) {
6421
6421
  pending.then((count) => {
6422
6422
  if (count > 0) {
6423
- execSync7(
6424
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6425
- { timeout: 3e3 }
6426
- );
6427
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
6423
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
6428
6424
  }
6429
6425
  }).catch(() => {
6430
6426
  });
@@ -6432,11 +6428,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6432
6428
  }
6433
6429
  } catch {
6434
6430
  }
6435
- execSync7(
6436
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6437
- { timeout: 3e3 }
6438
- );
6439
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
6431
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
6440
6432
  return true;
6441
6433
  } catch {
6442
6434
  return false;
@@ -12303,13 +12295,24 @@ function matchesChannel(msgChannel, matchChannel) {
12303
12295
  const channels = Array.isArray(matchChannel) ? matchChannel : [matchChannel];
12304
12296
  return channels.includes(msgChannel);
12305
12297
  }
12298
+ var MAX_REGEX_LENGTH = 200;
12299
+ function safeRegExp(pattern, flags) {
12300
+ if (pattern.length > MAX_REGEX_LENGTH) return null;
12301
+ try {
12302
+ return new RegExp(pattern, flags);
12303
+ } catch {
12304
+ return null;
12305
+ }
12306
+ }
12306
12307
  function matchesSender(msgSender, matchSender) {
12307
12308
  if (!matchSender) return true;
12308
- return new RegExp(matchSender).test(msgSender);
12309
+ const re = safeRegExp(matchSender);
12310
+ return re ? re.test(msgSender) : false;
12309
12311
  }
12310
12312
  function matchesTextPattern(msgText, matchPattern) {
12311
12313
  if (!matchPattern) return true;
12312
- return new RegExp(matchPattern, "i").test(msgText);
12314
+ const re = safeRegExp(matchPattern, "i");
12315
+ return re ? re.test(msgText) : false;
12313
12316
  }
12314
12317
  function matchesRoute(msg, match) {
12315
12318
  return matchesPlatform(msg.platform, match.platform) && matchesChannel(msg.channelId, match.channelId) && matchesSender(msg.senderId, match.senderId) && matchesTextPattern(msg.text, match.textPattern);
@@ -12349,6 +12352,12 @@ function validateGatewayConfig(config2) {
12349
12352
  if (!route.target) {
12350
12353
  warnings.push(`Route "${route.name}" has no target employee`);
12351
12354
  }
12355
+ if (route.match.senderId && !safeRegExp(route.match.senderId)) {
12356
+ warnings.push(`Route "${route.name}" has invalid senderId regex: ${route.match.senderId}`);
12357
+ }
12358
+ if (route.match.textPattern && !safeRegExp(route.match.textPattern, "i")) {
12359
+ warnings.push(`Route "${route.name}" has invalid textPattern regex: ${route.match.textPattern}`);
12360
+ }
12352
12361
  const isEmptyMatch = !route.match.platform && !route.match.channelId && !route.match.senderId && !route.match.textPattern;
12353
12362
  if (isEmptyMatch && config2.routes.indexOf(route) !== config2.routes.length - 1) {
12354
12363
  warnings.push(
@@ -8046,11 +8046,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
8046
8046
  if (pending instanceof Promise) {
8047
8047
  pending.then((count) => {
8048
8048
  if (count > 0) {
8049
- execSync7(
8050
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
8051
- { timeout: 3e3 }
8052
- );
8053
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
8049
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
8054
8050
  }
8055
8051
  }).catch(() => {
8056
8052
  });
@@ -8058,11 +8054,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
8058
8054
  }
8059
8055
  } catch {
8060
8056
  }
8061
- execSync7(
8062
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
8063
- { timeout: 3e3 }
8064
- );
8065
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
8057
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
8066
8058
  return true;
8067
8059
  } catch {
8068
8060
  return false;
@@ -12447,15 +12439,10 @@ function isIdlePane(paneText) {
12447
12439
  }
12448
12440
  function sendNudge(transport, session, runtime, action) {
12449
12441
  const message = action === "nudge" ? "You have open tasks. Run list_tasks to find them." : "You have more open tasks to dispatch. Run list_tasks.";
12450
- if (runtime === "codex") {
12451
- if (transport.sendKeysLiteral) {
12452
- transport.sendKeysLiteral(session, message);
12453
- } else {
12454
- transport.sendKeys(session, message);
12455
- }
12442
+ if (transport.sendKeysLiteral) {
12443
+ transport.sendKeysLiteral(session, message);
12456
12444
  } else {
12457
- transport.sendKeys(session, "/exe-intercom");
12458
- transport.sendKeys(session, "Enter");
12445
+ transport.sendKeys(session, message);
12459
12446
  }
12460
12447
  process.stderr.write(
12461
12448
  `[enforcement-audit] SENT nudge to ${session} (${runtime}, ${action})
@@ -14215,14 +14202,15 @@ function startIntercomQueueDrain() {
14215
14202
  const { baseAgentName: baseAgentName2 } = (init_employees(), __toCommonJS(employees_exports));
14216
14203
  const agentName = baseAgentName2(session.split("-")[0] ?? session);
14217
14204
  const rtConfig = getAgentRuntime2(agentName);
14205
+ const nudgeMsg = "You have pending notifications. Run list_tasks to check for assigned work.";
14218
14206
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
14219
- transport.sendKeys(session, "You have new notifications. Call list_tasks to check for assigned work.");
14207
+ transport.sendKeys(session, nudgeMsg);
14220
14208
  try {
14221
14209
  __require("child_process").execSync(`tmux send-keys -t ${session} Tab`, { timeout: 2e3 });
14222
14210
  } catch {
14223
14211
  }
14224
14212
  } else {
14225
- transport.sendKeys(session, "/exe-intercom");
14213
+ transport.sendKeys(session, nudgeMsg);
14226
14214
  }
14227
14215
  return true;
14228
14216
  } catch {
package/dist/lib/tasks.js CHANGED
@@ -2142,11 +2142,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
2142
2142
  if (pending instanceof Promise) {
2143
2143
  pending.then((count) => {
2144
2144
  if (count > 0) {
2145
- execSync4(
2146
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
2147
- { timeout: 3e3 }
2148
- );
2149
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
2145
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
2150
2146
  }
2151
2147
  }).catch(() => {
2152
2148
  });
@@ -2154,11 +2150,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
2154
2150
  }
2155
2151
  } catch {
2156
2152
  }
2157
- execSync4(
2158
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
2159
- { timeout: 3e3 }
2160
- );
2161
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
2153
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
2162
2154
  return true;
2163
2155
  } catch {
2164
2156
  return false;
@@ -4167,11 +4167,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
4167
4167
  if (pending instanceof Promise) {
4168
4168
  pending.then((count) => {
4169
4169
  if (count > 0) {
4170
- execSync6(
4171
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
4172
- { timeout: 3e3 }
4173
- );
4174
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
4170
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
4175
4171
  }
4176
4172
  }).catch(() => {
4177
4173
  });
@@ -4179,11 +4175,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
4179
4175
  }
4180
4176
  } catch {
4181
4177
  }
4182
- execSync6(
4183
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
4184
- { timeout: 3e3 }
4185
- );
4186
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
4178
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
4187
4179
  return true;
4188
4180
  } catch {
4189
4181
  return false;
@@ -5792,8 +5792,8 @@ async function hybridSearch(queryText, agentId, options) {
5792
5792
  try {
5793
5793
  const fs = await import("fs");
5794
5794
  const path44 = await import("path");
5795
- const os18 = await import("os");
5796
- const logPath = path44.join(os18.homedir(), ".exe-os", "search-quality.jsonl");
5795
+ const os19 = await import("os");
5796
+ const logPath = path44.join(os19.homedir(), ".exe-os", "search-quality.jsonl");
5797
5797
  fs.mkdirSync(path44.dirname(logPath), { recursive: true });
5798
5798
  fs.appendFileSync(logPath, JSON.stringify(logEntry) + "\n");
5799
5799
  } catch {
@@ -8491,11 +8491,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
8491
8491
  if (pending instanceof Promise) {
8492
8492
  pending.then((count) => {
8493
8493
  if (count > 0) {
8494
- execSync7(
8495
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
8496
- { timeout: 3e3 }
8497
- );
8498
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
8494
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
8499
8495
  }
8500
8496
  }).catch(() => {
8501
8497
  });
@@ -8503,11 +8499,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
8503
8499
  }
8504
8500
  } catch {
8505
8501
  }
8506
- execSync7(
8507
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
8508
- { timeout: 3e3 }
8509
- );
8510
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
8502
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
8511
8503
  return true;
8512
8504
  } catch {
8513
8505
  return false;
@@ -12134,8 +12126,9 @@ init_database();
12134
12126
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
12135
12127
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
12136
12128
  import { spawn as spawn4 } from "child_process";
12137
- import { existsSync as existsSync33, openSync as openSync3, mkdirSync as mkdirSync17, closeSync as closeSync3 } from "fs";
12129
+ import { existsSync as existsSync33, openSync as openSync3, mkdirSync as mkdirSync17, closeSync as closeSync3, readFileSync as readFileSync28 } from "fs";
12138
12130
  import path43 from "path";
12131
+ import os18 from "os";
12139
12132
  import { fileURLToPath as fileURLToPath5 } from "url";
12140
12133
 
12141
12134
  // src/mcp/tools/recall-my-memory.ts
@@ -18924,8 +18917,8 @@ function registerExportGraph(server2) {
18924
18917
  const html = await exportGraphHTML(client);
18925
18918
  const fs = await import("fs");
18926
18919
  const path44 = await import("path");
18927
- const os18 = await import("os");
18928
- const outDir = path44.join(os18.homedir(), ".exe-os", "exports");
18920
+ const os19 = await import("os");
18921
+ const outDir = path44.join(os19.homedir(), ".exe-os", "exports");
18929
18922
  fs.mkdirSync(outDir, { recursive: true });
18930
18923
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
18931
18924
  const filePath = path44.join(outDir, `graph-${timestamp}.html`);
@@ -23076,6 +23069,28 @@ try {
23076
23069
  }
23077
23070
  }, 3e4);
23078
23071
  _ppidWatchdog.unref();
23072
+ const MCP_VERSION_PATH = path43.join(os18.homedir(), ".exe-os", "mcp-version");
23073
+ let _currentMcpVersion = null;
23074
+ try {
23075
+ _currentMcpVersion = existsSync33(MCP_VERSION_PATH) ? readFileSync28(MCP_VERSION_PATH, "utf8").trim() : null;
23076
+ } catch {
23077
+ }
23078
+ const _versionWatchdog = setInterval(() => {
23079
+ try {
23080
+ if (!existsSync33(MCP_VERSION_PATH)) return;
23081
+ const diskVersion = readFileSync28(MCP_VERSION_PATH, "utf8").trim();
23082
+ if (_currentMcpVersion && diskVersion !== _currentMcpVersion) {
23083
+ process.stderr.write(
23084
+ `[exe-os] MCP version changed (${_currentMcpVersion} \u2192 ${diskVersion}). Hot-reloading...
23085
+ `
23086
+ );
23087
+ void shutdown("hot_reload");
23088
+ }
23089
+ if (!_currentMcpVersion) _currentMcpVersion = diskVersion;
23090
+ } catch {
23091
+ }
23092
+ }, 1e4);
23093
+ _versionWatchdog.unref();
23079
23094
  const BACKFILL_CHECK_MS = 5 * 60 * 1e3;
23080
23095
  _backfillTimer = setInterval(async () => {
23081
23096
  try {
@@ -2381,11 +2381,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
2381
2381
  if (pending instanceof Promise) {
2382
2382
  pending.then((count) => {
2383
2383
  if (count > 0) {
2384
- execSync4(
2385
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
2386
- { timeout: 3e3 }
2387
- );
2388
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
2384
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
2389
2385
  }
2390
2386
  }).catch(() => {
2391
2387
  });
@@ -2393,11 +2389,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
2393
2389
  }
2394
2390
  } catch {
2395
2391
  }
2396
- execSync4(
2397
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
2398
- { timeout: 3e3 }
2399
- );
2400
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
2392
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
2401
2393
  return true;
2402
2394
  } catch {
2403
2395
  return false;
@@ -2145,11 +2145,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
2145
2145
  if (pending instanceof Promise) {
2146
2146
  pending.then((count) => {
2147
2147
  if (count > 0) {
2148
- execSync4(
2149
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
2150
- { timeout: 3e3 }
2151
- );
2152
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
2148
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
2153
2149
  }
2154
2150
  }).catch(() => {
2155
2151
  });
@@ -2157,11 +2153,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
2157
2153
  }
2158
2154
  } catch {
2159
2155
  }
2160
- execSync4(
2161
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
2162
- { timeout: 3e3 }
2163
- );
2164
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
2156
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
2165
2157
  return true;
2166
2158
  } catch {
2167
2159
  return false;
@@ -6181,11 +6181,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6181
6181
  if (pending instanceof Promise) {
6182
6182
  pending.then((count) => {
6183
6183
  if (count > 0) {
6184
- execSync7(
6185
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6186
- { timeout: 3e3 }
6187
- );
6188
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
6184
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
6189
6185
  }
6190
6186
  }).catch(() => {
6191
6187
  });
@@ -6193,11 +6189,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6193
6189
  }
6194
6190
  } catch {
6195
6191
  }
6196
- execSync7(
6197
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6198
- { timeout: 3e3 }
6199
- );
6200
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
6192
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
6201
6193
  return true;
6202
6194
  } catch {
6203
6195
  return false;
package/dist/tui/App.js CHANGED
@@ -6785,11 +6785,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6785
6785
  if (pending instanceof Promise) {
6786
6786
  pending.then((count) => {
6787
6787
  if (count > 0) {
6788
- execSync7(
6789
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6790
- { timeout: 3e3 }
6791
- );
6792
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending)`);
6788
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}", ${count} reviews pending \u2014 hook will surface)`);
6793
6789
  }
6794
6790
  }).catch(() => {
6795
6791
  });
@@ -6797,11 +6793,7 @@ function notifyCoordinatorTaskCompletion(coordinatorSession, agentName, taskTitl
6797
6793
  }
6798
6794
  } catch {
6799
6795
  }
6800
- execSync7(
6801
- `tmux send-keys -t ${JSON.stringify(coordinatorSession)} '/exe-intercom' Enter`,
6802
- { timeout: 3e3 }
6803
- );
6804
- logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}")`);
6796
+ logIntercom(`COMPLETION \u2192 ${coordinatorSession} (${agentName} completed "${taskTitle.slice(0, 50)}") \u2014 review count unavailable, skipping intercom (hook will catch it)`);
6805
6797
  return true;
6806
6798
  } catch {
6807
6799
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.9.18",
3
+ "version": "0.9.20",
4
4
  "description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
5
5
  "license": "CC-BY-NC-4.0",
6
6
  "type": "module",
@@ -67,14 +67,14 @@
67
67
  "test:watch": "vitest",
68
68
  "typecheck": "tsc --noEmit",
69
69
  "build": "tsup && mkdir -p dist/assets && cp src/assets/tmux.conf dist/assets/ && cp src/assets/ghostty.conf dist/assets/ && cp src/assets/statusline-command.sh dist/assets/ && cp src/bin/exe-start.sh dist/bin/exe-start.sh",
70
- "deploy": "node dist/bin/pre-build-guard.js 2>/dev/null; (kill $(cat ~/.exe-os/exed.pid 2>/dev/null) 2>/dev/null; pgrep -f exe-daemon.js | xargs kill 2>/dev/null; true) && tsup && mkdir -p dist/assets && cp src/assets/tmux.conf dist/assets/ && cp src/assets/ghostty.conf dist/assets/ && cp src/assets/statusline-command.sh dist/assets/ && cp src/bin/exe-start.sh dist/bin/exe-start.sh && npm install -g . && node dist/bin/install.js --global && echo '[exe-os] Deploy complete. MCP servers will auto-reconnect on next tool call.'",
70
+ "deploy": "node dist/bin/pre-build-guard.js 2>/dev/null; (kill $(cat ~/.exe-os/exed.pid 2>/dev/null) 2>/dev/null; pgrep -f exe-daemon.js | xargs kill 2>/dev/null; true) && tsup && mkdir -p dist/assets && cp src/assets/tmux.conf dist/assets/ && cp src/assets/ghostty.conf dist/assets/ && cp src/assets/statusline-command.sh dist/assets/ && cp src/bin/exe-start.sh dist/bin/exe-start.sh && npm install -g . && node dist/bin/install.js --global && echo '[exe-os] Deploy complete. Run /mcp in active sessions to reconnect.'",
71
71
  "postinstall": "node dist/bin/install.js --global 2>/dev/null || true",
72
72
  "prepublishOnly": "npm run typecheck && npm run build && node dist/bin/customer-readiness.js",
73
73
  "test:publish": "npx vitest run --maxWorkers=4 --exclude 'tests/tui/**' --exclude 'tests/lib/tmux-routing.test.ts' --exclude 'tests/lib/intercom-routing.test.ts' --exclude 'tests/gateway/**' --exclude 'tests/installer/setup-wizard.test.ts' --exclude 'tests/mcp/ingest-document.test.ts' --exclude 'tests/lib/hybrid-search.test.ts' --exclude 'tests/lib/worker-gate.test.ts'",
74
74
  "benchmark:longmemeval": "npx tsx tests/benchmarks/longmemeval.ts"
75
75
  },
76
76
  "dependencies": {
77
- "@anthropic-ai/sdk": "^0.80.0",
77
+ "@anthropic-ai/sdk": "^0.95.1",
78
78
  "@discordjs/voice": "^0.19.2",
79
79
  "@libsql/client": "^0.14.0",
80
80
  "@modelcontextprotocol/sdk": "^1.27.1",