@askexenow/exe-os 0.8.108 → 0.9.0

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
@@ -607,6 +607,7 @@ var init_preferences = __esm({
607
607
  // src/adapters/claude/installer.ts
608
608
  var installer_exports = {};
609
609
  __export(installer_exports, {
610
+ cleanOldShellFunctions: () => cleanOldShellFunctions,
610
611
  copySlashCommands: () => copySlashCommands,
611
612
  installStatusLine: () => installStatusLine,
612
613
  mergeHooks: () => mergeHooks,
@@ -1015,6 +1016,132 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1015
1016
  await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
1016
1017
  return { added, skipped };
1017
1018
  }
1019
+ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
1020
+ const rosterPath = path5.join(homeDir, ".exe-os", "exe-employees.json");
1021
+ if (!existsSync5(rosterPath)) return 0;
1022
+ let employees;
1023
+ try {
1024
+ employees = JSON.parse(await readFile3(rosterPath, "utf-8"));
1025
+ } catch {
1026
+ return 0;
1027
+ }
1028
+ if (employees.length === 0) return 0;
1029
+ const names = employees.map((e) => e.name);
1030
+ const funcPatterns = names.map((n) => {
1031
+ const funcDef = new RegExp(`^\\s*(function\\s+)?${escapeRegExp(n)}\\d+\\s*\\(\\)`, "m");
1032
+ const forLoop = new RegExp(`${escapeRegExp(n)}\\$\\{?[a-zA-Z_][a-zA-Z0-9_]*\\}?`, "m");
1033
+ return { name: n, funcDef, forLoop };
1034
+ });
1035
+ const rcFiles = [
1036
+ path5.join(homeDir, ".zshrc"),
1037
+ path5.join(homeDir, ".bashrc")
1038
+ ];
1039
+ const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
1040
+ let totalRemoved = 0;
1041
+ for (const rcPath of rcFiles) {
1042
+ if (!existsSync5(rcPath)) continue;
1043
+ let content;
1044
+ try {
1045
+ content = await readFile3(rcPath, "utf-8");
1046
+ } catch {
1047
+ continue;
1048
+ }
1049
+ if (content.includes(REMOVED_MARKER)) continue;
1050
+ const lines = content.split("\n");
1051
+ let changed = false;
1052
+ let inForLoop = false;
1053
+ let forLoopMatchesEmployee = false;
1054
+ for (let i = 0; i < lines.length; i++) {
1055
+ const line = lines[i];
1056
+ const trimmed = line.trim();
1057
+ if (trimmed.startsWith("#")) continue;
1058
+ if (/^\s*for\s+/.test(trimmed)) {
1059
+ inForLoop = true;
1060
+ forLoopMatchesEmployee = false;
1061
+ for (const { forLoop } of funcPatterns) {
1062
+ if (forLoop.test(trimmed)) {
1063
+ forLoopMatchesEmployee = true;
1064
+ break;
1065
+ }
1066
+ }
1067
+ if (forLoopMatchesEmployee) {
1068
+ lines[i] = `${REMOVED_MARKER}
1069
+ # ${line}`;
1070
+ changed = true;
1071
+ totalRemoved++;
1072
+ continue;
1073
+ }
1074
+ }
1075
+ if (inForLoop) {
1076
+ if (!forLoopMatchesEmployee) {
1077
+ for (const { forLoop } of funcPatterns) {
1078
+ if (forLoop.test(trimmed)) {
1079
+ forLoopMatchesEmployee = true;
1080
+ for (let j = i - 1; j >= 0; j--) {
1081
+ if (/^\s*for\s+/.test(lines[j].replace(/^#\s*/, ""))) {
1082
+ if (!lines[j].startsWith("#")) {
1083
+ lines[j] = `${REMOVED_MARKER}
1084
+ # ${lines[j]}`;
1085
+ totalRemoved++;
1086
+ }
1087
+ break;
1088
+ }
1089
+ }
1090
+ break;
1091
+ }
1092
+ }
1093
+ }
1094
+ if (forLoopMatchesEmployee && !trimmed.startsWith("#")) {
1095
+ lines[i] = `# ${line}`;
1096
+ changed = true;
1097
+ totalRemoved++;
1098
+ }
1099
+ if (trimmed === "done" || trimmed.startsWith("done;") || trimmed.startsWith("done ")) {
1100
+ inForLoop = false;
1101
+ forLoopMatchesEmployee = false;
1102
+ }
1103
+ continue;
1104
+ }
1105
+ for (const { funcDef } of funcPatterns) {
1106
+ if (funcDef.test(trimmed)) {
1107
+ lines[i] = `${REMOVED_MARKER}
1108
+ # ${line}`;
1109
+ changed = true;
1110
+ totalRemoved++;
1111
+ let braceDepth = 0;
1112
+ const hasBrace = trimmed.includes("{");
1113
+ if (hasBrace) braceDepth = 1;
1114
+ for (let j = i + 1; j < lines.length; j++) {
1115
+ const bodyLine = lines[j].trim();
1116
+ if (!hasBrace && braceDepth === 0) {
1117
+ if (bodyLine === "{") {
1118
+ braceDepth = 1;
1119
+ lines[j] = `# ${lines[j]}`;
1120
+ totalRemoved++;
1121
+ continue;
1122
+ }
1123
+ }
1124
+ if (braceDepth > 0) {
1125
+ braceDepth += (bodyLine.match(/{/g) ?? []).length;
1126
+ braceDepth -= (bodyLine.match(/}/g) ?? []).length;
1127
+ lines[j] = `# ${lines[j]}`;
1128
+ totalRemoved++;
1129
+ if (braceDepth <= 0) break;
1130
+ }
1131
+ }
1132
+ break;
1133
+ }
1134
+ }
1135
+ }
1136
+ if (changed) {
1137
+ await writeFile3(rcPath, lines.join("\n"));
1138
+ }
1139
+ }
1140
+ return totalRemoved;
1141
+ }
1142
+ function escapeRegExp(s) {
1143
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1144
+ }
1018
1145
  async function injectOrchestrationRules(homeDir) {
1019
1146
  const claudeDir = path5.join(homeDir, ".claude");
1020
1147
  const claudeMdPath = path5.join(claudeDir, "CLAUDE.md");
@@ -1117,6 +1244,13 @@ async function runInstaller(homeDir) {
1117
1244
  `Status line: ${statusLineResult}
1118
1245
  `
1119
1246
  );
1247
+ const shellFuncsCleaned = await cleanOldShellFunctions(resolvedHome);
1248
+ if (shellFuncsCleaned > 0) {
1249
+ process.stderr.write(
1250
+ `Shell cleanup: removed ${shellFuncsCleaned} old shell function(s) that were shadowing exe-os wrappers
1251
+ `
1252
+ );
1253
+ }
1120
1254
  process.stderr.write(`
1121
1255
  exe-os installed successfully.
1122
1256
  `);
@@ -10181,7 +10315,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
10181
10315
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
10182
10316
  `
10183
10317
  );
10184
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
10318
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
10185
10319
  } else if (useOpencode) {
10186
10320
  const binName = `${employeeName}-opencode`;
10187
10321
  process.stderr.write(
@@ -6268,7 +6268,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
6268
6268
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
6269
6269
  `
6270
6270
  );
6271
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
6271
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
6272
6272
  } else if (useOpencode) {
6273
6273
  const binName = `${employeeName}-opencode`;
6274
6274
  process.stderr.write(
@@ -4434,7 +4434,7 @@ function spawnEmployee(employeeName2, exeSession2, projectDir2, opts) {
4434
4434
  `[tmux-routing] agent-config: ${employeeName2} \u2192 codex (${agentRtConfig.model})
4435
4435
  `
4436
4436
  );
4437
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName2}${cleanupSuffix}`;
4437
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName2} --session ${sessionName}${cleanupSuffix}`;
4438
4438
  } else if (useOpencode) {
4439
4439
  const binName = `${employeeName2}-opencode`;
4440
4440
  process.stderr.write(
@@ -9159,7 +9159,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
9159
9159
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
9160
9160
  `
9161
9161
  );
9162
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
9162
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
9163
9163
  } else if (useOpencode) {
9164
9164
  const binName = `${employeeName}-opencode`;
9165
9165
  process.stderr.write(
@@ -1020,6 +1020,7 @@ var init_preferences = __esm({
1020
1020
  // src/adapters/claude/installer.ts
1021
1021
  var installer_exports = {};
1022
1022
  __export(installer_exports, {
1023
+ cleanOldShellFunctions: () => cleanOldShellFunctions,
1023
1024
  copySlashCommands: () => copySlashCommands,
1024
1025
  installStatusLine: () => installStatusLine,
1025
1026
  mergeHooks: () => mergeHooks,
@@ -1428,6 +1429,132 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
1428
1429
  await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
1429
1430
  return { added, skipped };
1430
1431
  }
1432
+ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
1433
+ const rosterPath = path9.join(homeDir, ".exe-os", "exe-employees.json");
1434
+ if (!existsSync9(rosterPath)) return 0;
1435
+ let employees;
1436
+ try {
1437
+ employees = JSON.parse(await readFile3(rosterPath, "utf-8"));
1438
+ } catch {
1439
+ return 0;
1440
+ }
1441
+ if (employees.length === 0) return 0;
1442
+ const names = employees.map((e) => e.name);
1443
+ const funcPatterns = names.map((n) => {
1444
+ const funcDef = new RegExp(`^\\s*(function\\s+)?${escapeRegExp(n)}\\d+\\s*\\(\\)`, "m");
1445
+ const forLoop = new RegExp(`${escapeRegExp(n)}\\$\\{?[a-zA-Z_][a-zA-Z0-9_]*\\}?`, "m");
1446
+ return { name: n, funcDef, forLoop };
1447
+ });
1448
+ const rcFiles = [
1449
+ path9.join(homeDir, ".zshrc"),
1450
+ path9.join(homeDir, ".bashrc")
1451
+ ];
1452
+ const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
1453
+ let totalRemoved = 0;
1454
+ for (const rcPath of rcFiles) {
1455
+ if (!existsSync9(rcPath)) continue;
1456
+ let content;
1457
+ try {
1458
+ content = await readFile3(rcPath, "utf-8");
1459
+ } catch {
1460
+ continue;
1461
+ }
1462
+ if (content.includes(REMOVED_MARKER)) continue;
1463
+ const lines = content.split("\n");
1464
+ let changed = false;
1465
+ let inForLoop = false;
1466
+ let forLoopMatchesEmployee = false;
1467
+ for (let i = 0; i < lines.length; i++) {
1468
+ const line = lines[i];
1469
+ const trimmed = line.trim();
1470
+ if (trimmed.startsWith("#")) continue;
1471
+ if (/^\s*for\s+/.test(trimmed)) {
1472
+ inForLoop = true;
1473
+ forLoopMatchesEmployee = false;
1474
+ for (const { forLoop } of funcPatterns) {
1475
+ if (forLoop.test(trimmed)) {
1476
+ forLoopMatchesEmployee = true;
1477
+ break;
1478
+ }
1479
+ }
1480
+ if (forLoopMatchesEmployee) {
1481
+ lines[i] = `${REMOVED_MARKER}
1482
+ # ${line}`;
1483
+ changed = true;
1484
+ totalRemoved++;
1485
+ continue;
1486
+ }
1487
+ }
1488
+ if (inForLoop) {
1489
+ if (!forLoopMatchesEmployee) {
1490
+ for (const { forLoop } of funcPatterns) {
1491
+ if (forLoop.test(trimmed)) {
1492
+ forLoopMatchesEmployee = true;
1493
+ for (let j = i - 1; j >= 0; j--) {
1494
+ if (/^\s*for\s+/.test(lines[j].replace(/^#\s*/, ""))) {
1495
+ if (!lines[j].startsWith("#")) {
1496
+ lines[j] = `${REMOVED_MARKER}
1497
+ # ${lines[j]}`;
1498
+ totalRemoved++;
1499
+ }
1500
+ break;
1501
+ }
1502
+ }
1503
+ break;
1504
+ }
1505
+ }
1506
+ }
1507
+ if (forLoopMatchesEmployee && !trimmed.startsWith("#")) {
1508
+ lines[i] = `# ${line}`;
1509
+ changed = true;
1510
+ totalRemoved++;
1511
+ }
1512
+ if (trimmed === "done" || trimmed.startsWith("done;") || trimmed.startsWith("done ")) {
1513
+ inForLoop = false;
1514
+ forLoopMatchesEmployee = false;
1515
+ }
1516
+ continue;
1517
+ }
1518
+ for (const { funcDef } of funcPatterns) {
1519
+ if (funcDef.test(trimmed)) {
1520
+ lines[i] = `${REMOVED_MARKER}
1521
+ # ${line}`;
1522
+ changed = true;
1523
+ totalRemoved++;
1524
+ let braceDepth = 0;
1525
+ const hasBrace = trimmed.includes("{");
1526
+ if (hasBrace) braceDepth = 1;
1527
+ for (let j = i + 1; j < lines.length; j++) {
1528
+ const bodyLine = lines[j].trim();
1529
+ if (!hasBrace && braceDepth === 0) {
1530
+ if (bodyLine === "{") {
1531
+ braceDepth = 1;
1532
+ lines[j] = `# ${lines[j]}`;
1533
+ totalRemoved++;
1534
+ continue;
1535
+ }
1536
+ }
1537
+ if (braceDepth > 0) {
1538
+ braceDepth += (bodyLine.match(/{/g) ?? []).length;
1539
+ braceDepth -= (bodyLine.match(/}/g) ?? []).length;
1540
+ lines[j] = `# ${lines[j]}`;
1541
+ totalRemoved++;
1542
+ if (braceDepth <= 0) break;
1543
+ }
1544
+ }
1545
+ break;
1546
+ }
1547
+ }
1548
+ }
1549
+ if (changed) {
1550
+ await writeFile3(rcPath, lines.join("\n"));
1551
+ }
1552
+ }
1553
+ return totalRemoved;
1554
+ }
1555
+ function escapeRegExp(s) {
1556
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1557
+ }
1431
1558
  async function injectOrchestrationRules(homeDir) {
1432
1559
  const claudeDir = path9.join(homeDir, ".claude");
1433
1560
  const claudeMdPath = path9.join(claudeDir, "CLAUDE.md");
@@ -1530,6 +1657,13 @@ async function runInstaller(homeDir) {
1530
1657
  `Status line: ${statusLineResult}
1531
1658
  `
1532
1659
  );
1660
+ const shellFuncsCleaned = await cleanOldShellFunctions(resolvedHome);
1661
+ if (shellFuncsCleaned > 0) {
1662
+ process.stderr.write(
1663
+ `Shell cleanup: removed ${shellFuncsCleaned} old shell function(s) that were shadowing exe-os wrappers
1664
+ `
1665
+ );
1666
+ }
1533
1667
  process.stderr.write(`
1534
1668
  exe-os installed successfully.
1535
1669
  `);
@@ -6270,7 +6270,7 @@ function spawnEmployee(employeeName, exeSession2, projectDir, opts) {
6270
6270
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
6271
6271
  `
6272
6272
  );
6273
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
6273
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
6274
6274
  } else if (useOpencode) {
6275
6275
  const binName = `${employeeName}-opencode`;
6276
6276
  process.stderr.write(
@@ -2791,8 +2791,17 @@ function resolveAgent(argv) {
2791
2791
  );
2792
2792
  }
2793
2793
  const agent = rest[idx + 1].toLowerCase();
2794
- const passthrough = [...rest.slice(0, idx), ...rest.slice(idx + 2)];
2795
- return { agent, passthrough };
2794
+ const withoutAgent = [...rest.slice(0, idx), ...rest.slice(idx + 2)];
2795
+ const sessIdx = withoutAgent.indexOf("--session");
2796
+ let sessionName;
2797
+ let passthrough;
2798
+ if (sessIdx !== -1 && withoutAgent[sessIdx + 1]) {
2799
+ sessionName = withoutAgent[sessIdx + 1];
2800
+ passthrough = [...withoutAgent.slice(0, sessIdx), ...withoutAgent.slice(sessIdx + 2)];
2801
+ } else {
2802
+ passthrough = withoutAgent;
2803
+ }
2804
+ return { agent, passthrough, sessionName };
2796
2805
  }
2797
2806
  function loadIdentity(agent) {
2798
2807
  const dir = path11.join(os9.homedir(), ".exe-os", "identity");
@@ -2839,8 +2848,9 @@ function writePromptFile(agent, identity, behaviorsPath) {
2839
2848
  async function main() {
2840
2849
  let agent;
2841
2850
  let passthrough;
2851
+ let sessionName;
2842
2852
  try {
2843
- ({ agent, passthrough } = resolveAgent(process.argv));
2853
+ ({ agent, passthrough, sessionName } = resolveAgent(process.argv));
2844
2854
  } catch (err) {
2845
2855
  process.stderr.write(`${err.message}
2846
2856
  `);
@@ -2941,6 +2951,37 @@ async function main() {
2941
2951
  `
2942
2952
  );
2943
2953
  }
2954
+ const WORKTREE_ROLES = /* @__PURE__ */ new Set(["Principal Engineer", "Staff Code Reviewer"]);
2955
+ let worktreePath = null;
2956
+ const worktreeName = sessionName ? sessionName.split("-")[0] : agent;
2957
+ process.stderr.write(`[exe-start-codex] agent=${agent} session=${sessionName ?? "none"} worktree=${worktreeName} role="${empRole}" worktree-eligible=${WORKTREE_ROLES.has(empRole)}
2958
+ `);
2959
+ if (WORKTREE_ROLES.has(empRole)) {
2960
+ try {
2961
+ const { execSync: es } = await import("child_process");
2962
+ const worktreeDir = path11.join(process.cwd(), ".worktrees", worktreeName);
2963
+ const branchName = `${worktreeName}/codex-${Date.now()}`;
2964
+ if (existsSync10(worktreeDir)) {
2965
+ worktreePath = worktreeDir;
2966
+ process.stderr.write(`[exe-start-codex] Reusing worktree at ${worktreeDir}
2967
+ `);
2968
+ } else {
2969
+ mkdirSync7(path11.dirname(worktreeDir), { recursive: true });
2970
+ es(`git worktree add "${worktreeDir}" -b "${branchName}" HEAD`, {
2971
+ encoding: "utf-8",
2972
+ timeout: 3e4
2973
+ });
2974
+ worktreePath = worktreeDir;
2975
+ process.stderr.write(`[exe-start-codex] Created worktree at ${worktreeDir} (branch: ${branchName})
2976
+ `);
2977
+ }
2978
+ } catch (err) {
2979
+ process.stderr.write(
2980
+ `[exe-start-codex] Worktree creation failed for ${agent}: ${err instanceof Error ? err.message : String(err)}. Falling back to main checkout.
2981
+ `
2982
+ );
2983
+ }
2984
+ }
2944
2985
  const effectiveModel = process.env.EXE_AGENT_MODEL ?? CODEX.defaultModel;
2945
2986
  const args = [
2946
2987
  "-m",
@@ -2949,6 +2990,9 @@ async function main() {
2949
2990
  CODEX.inlineFlag,
2950
2991
  ...passthrough
2951
2992
  ];
2993
+ if (worktreePath) {
2994
+ args.push("-C", worktreePath);
2995
+ }
2952
2996
  if (promptPath) {
2953
2997
  args.push("-c", `model_instructions_file="${promptPath}"`);
2954
2998
  }
@@ -4922,7 +4922,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4922
4922
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
4923
4923
  `
4924
4924
  );
4925
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
4925
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
4926
4926
  } else if (useOpencode) {
4927
4927
  const binName = `${employeeName}-opencode`;
4928
4928
  process.stderr.write(
@@ -804,6 +804,132 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
804
804
  await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
805
805
  return { added, skipped };
806
806
  }
807
+ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
808
+ const rosterPath = path5.join(homeDir, ".exe-os", "exe-employees.json");
809
+ if (!existsSync5(rosterPath)) return 0;
810
+ let employees;
811
+ try {
812
+ employees = JSON.parse(await readFile3(rosterPath, "utf-8"));
813
+ } catch {
814
+ return 0;
815
+ }
816
+ if (employees.length === 0) return 0;
817
+ const names = employees.map((e) => e.name);
818
+ const funcPatterns = names.map((n) => {
819
+ const funcDef = new RegExp(`^\\s*(function\\s+)?${escapeRegExp(n)}\\d+\\s*\\(\\)`, "m");
820
+ const forLoop = new RegExp(`${escapeRegExp(n)}\\$\\{?[a-zA-Z_][a-zA-Z0-9_]*\\}?`, "m");
821
+ return { name: n, funcDef, forLoop };
822
+ });
823
+ const rcFiles = [
824
+ path5.join(homeDir, ".zshrc"),
825
+ path5.join(homeDir, ".bashrc")
826
+ ];
827
+ const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
828
+ let totalRemoved = 0;
829
+ for (const rcPath of rcFiles) {
830
+ if (!existsSync5(rcPath)) continue;
831
+ let content;
832
+ try {
833
+ content = await readFile3(rcPath, "utf-8");
834
+ } catch {
835
+ continue;
836
+ }
837
+ if (content.includes(REMOVED_MARKER)) continue;
838
+ const lines = content.split("\n");
839
+ let changed = false;
840
+ let inForLoop = false;
841
+ let forLoopMatchesEmployee = false;
842
+ for (let i = 0; i < lines.length; i++) {
843
+ const line = lines[i];
844
+ const trimmed = line.trim();
845
+ if (trimmed.startsWith("#")) continue;
846
+ if (/^\s*for\s+/.test(trimmed)) {
847
+ inForLoop = true;
848
+ forLoopMatchesEmployee = false;
849
+ for (const { forLoop } of funcPatterns) {
850
+ if (forLoop.test(trimmed)) {
851
+ forLoopMatchesEmployee = true;
852
+ break;
853
+ }
854
+ }
855
+ if (forLoopMatchesEmployee) {
856
+ lines[i] = `${REMOVED_MARKER}
857
+ # ${line}`;
858
+ changed = true;
859
+ totalRemoved++;
860
+ continue;
861
+ }
862
+ }
863
+ if (inForLoop) {
864
+ if (!forLoopMatchesEmployee) {
865
+ for (const { forLoop } of funcPatterns) {
866
+ if (forLoop.test(trimmed)) {
867
+ forLoopMatchesEmployee = true;
868
+ for (let j = i - 1; j >= 0; j--) {
869
+ if (/^\s*for\s+/.test(lines[j].replace(/^#\s*/, ""))) {
870
+ if (!lines[j].startsWith("#")) {
871
+ lines[j] = `${REMOVED_MARKER}
872
+ # ${lines[j]}`;
873
+ totalRemoved++;
874
+ }
875
+ break;
876
+ }
877
+ }
878
+ break;
879
+ }
880
+ }
881
+ }
882
+ if (forLoopMatchesEmployee && !trimmed.startsWith("#")) {
883
+ lines[i] = `# ${line}`;
884
+ changed = true;
885
+ totalRemoved++;
886
+ }
887
+ if (trimmed === "done" || trimmed.startsWith("done;") || trimmed.startsWith("done ")) {
888
+ inForLoop = false;
889
+ forLoopMatchesEmployee = false;
890
+ }
891
+ continue;
892
+ }
893
+ for (const { funcDef } of funcPatterns) {
894
+ if (funcDef.test(trimmed)) {
895
+ lines[i] = `${REMOVED_MARKER}
896
+ # ${line}`;
897
+ changed = true;
898
+ totalRemoved++;
899
+ let braceDepth = 0;
900
+ const hasBrace = trimmed.includes("{");
901
+ if (hasBrace) braceDepth = 1;
902
+ for (let j = i + 1; j < lines.length; j++) {
903
+ const bodyLine = lines[j].trim();
904
+ if (!hasBrace && braceDepth === 0) {
905
+ if (bodyLine === "{") {
906
+ braceDepth = 1;
907
+ lines[j] = `# ${lines[j]}`;
908
+ totalRemoved++;
909
+ continue;
910
+ }
911
+ }
912
+ if (braceDepth > 0) {
913
+ braceDepth += (bodyLine.match(/{/g) ?? []).length;
914
+ braceDepth -= (bodyLine.match(/}/g) ?? []).length;
915
+ lines[j] = `# ${lines[j]}`;
916
+ totalRemoved++;
917
+ if (braceDepth <= 0) break;
918
+ }
919
+ }
920
+ break;
921
+ }
922
+ }
923
+ }
924
+ if (changed) {
925
+ await writeFile3(rcPath, lines.join("\n"));
926
+ }
927
+ }
928
+ return totalRemoved;
929
+ }
930
+ function escapeRegExp(s) {
931
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
932
+ }
807
933
  var EXE_SECTION_START = "<!-- exe-os:orchestration-start -->";
808
934
  var EXE_SECTION_END = "<!-- exe-os:orchestration-end -->";
809
935
  var ORCHESTRATION_RULES = `${EXE_SECTION_START}
@@ -919,6 +1045,13 @@ async function runInstaller(homeDir) {
919
1045
  `Status line: ${statusLineResult}
920
1046
  `
921
1047
  );
1048
+ const shellFuncsCleaned = await cleanOldShellFunctions(resolvedHome);
1049
+ if (shellFuncsCleaned > 0) {
1050
+ process.stderr.write(
1051
+ `Shell cleanup: removed ${shellFuncsCleaned} old shell function(s) that were shadowing exe-os wrappers
1052
+ `
1053
+ );
1054
+ }
922
1055
  process.stderr.write(`
923
1056
  exe-os installed successfully.
924
1057
  `);
@@ -4927,7 +4927,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4927
4927
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
4928
4928
  `
4929
4929
  );
4930
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
4930
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
4931
4931
  } else if (useOpencode) {
4932
4932
  const binName = `${employeeName}-opencode`;
4933
4933
  process.stderr.write(
@@ -7501,7 +7501,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7501
7501
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
7502
7502
  `
7503
7503
  );
7504
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
7504
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
7505
7505
  } else if (useOpencode) {
7506
7506
  const binName = `${employeeName}-opencode`;
7507
7507
  process.stderr.write(
@@ -3433,7 +3433,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3433
3433
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
3434
3434
  `
3435
3435
  );
3436
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
3436
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
3437
3437
  } else if (useOpencode) {
3438
3438
  const binName = `${employeeName}-opencode`;
3439
3439
  process.stderr.write(
@@ -4921,7 +4921,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4921
4921
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
4922
4922
  `
4923
4923
  );
4924
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
4924
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
4925
4925
  } else if (useOpencode) {
4926
4926
  const binName = `${employeeName}-opencode`;
4927
4927
  process.stderr.write(
@@ -4300,7 +4300,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4300
4300
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
4301
4301
  `
4302
4302
  );
4303
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
4303
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
4304
4304
  } else if (useOpencode) {
4305
4305
  const binName = `${employeeName}-opencode`;
4306
4306
  process.stderr.write(
@@ -4905,7 +4905,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
4905
4905
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
4906
4906
  `
4907
4907
  );
4908
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
4908
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
4909
4909
  } else if (useOpencode) {
4910
4910
  const binName = `${employeeName}-opencode`;
4911
4911
  process.stderr.write(
@@ -7405,7 +7405,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7405
7405
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
7406
7406
  `
7407
7407
  );
7408
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
7408
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
7409
7409
  } else if (useOpencode) {
7410
7410
  const binName = `${employeeName}-opencode`;
7411
7411
  process.stderr.write(
@@ -5107,7 +5107,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5107
5107
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
5108
5108
  `
5109
5109
  );
5110
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
5110
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
5111
5111
  } else if (useOpencode) {
5112
5112
  const binName = `${employeeName}-opencode`;
5113
5113
  process.stderr.write(
package/dist/index.js CHANGED
@@ -5339,7 +5339,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5339
5339
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
5340
5340
  `
5341
5341
  );
5342
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
5342
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
5343
5343
  } else if (useOpencode) {
5344
5344
  const binName = `${employeeName}-opencode`;
5345
5345
  process.stderr.write(
@@ -0,0 +1,117 @@
1
+ // src/lib/cloudflare-dns.ts
2
+ var CLOUDFLARE_API_BASE_URL = "https://api.cloudflare.com/client/v4";
3
+ var DNS_RECORD_TYPE_A = "A";
4
+ var DEFAULT_PROXIED = true;
5
+ var DEFAULT_TTL = 1;
6
+ var REQUEST_TIMEOUT_MS = 3e4;
7
+ var UNKNOWN_ERROR_CODE = "unknown";
8
+ async function createARecord(cfApiToken, zoneId, domain, ip, opts) {
9
+ const proxied = opts?.proxied ?? DEFAULT_PROXIED;
10
+ const ttl = opts?.ttl ?? DEFAULT_TTL;
11
+ const response = await requestCloudflare(cfApiToken, zoneId, {
12
+ method: "POST",
13
+ body: {
14
+ type: DNS_RECORD_TYPE_A,
15
+ name: domain,
16
+ content: ip,
17
+ proxied,
18
+ ttl
19
+ }
20
+ });
21
+ return mapDnsRecord(response);
22
+ }
23
+ async function deleteARecord(cfApiToken, zoneId, recordId) {
24
+ await requestCloudflare(cfApiToken, zoneId, {
25
+ method: "DELETE",
26
+ path: `/dns_records/${recordId}`
27
+ });
28
+ }
29
+ async function findARecord(cfApiToken, zoneId, name) {
30
+ const query = new URLSearchParams({
31
+ type: DNS_RECORD_TYPE_A,
32
+ name
33
+ });
34
+ const records = await requestCloudflare(cfApiToken, zoneId, {
35
+ method: "GET",
36
+ query
37
+ });
38
+ const record = records[0];
39
+ return record ? mapDnsRecord(record) : null;
40
+ }
41
+ async function requestCloudflare(cfApiToken, zoneId, options) {
42
+ const response = await fetch(buildUrl(zoneId, options.path, options.query), {
43
+ method: options.method,
44
+ headers: buildHeaders(cfApiToken),
45
+ body: options.body ? JSON.stringify(options.body) : void 0,
46
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
47
+ });
48
+ const envelope = await parseEnvelope(response);
49
+ if (!response.ok || !envelope.success) {
50
+ throw toCloudflareError(response, envelope);
51
+ }
52
+ return envelope.result;
53
+ }
54
+ function buildUrl(zoneId, path = "/dns_records", query) {
55
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
56
+ const url = new URL(
57
+ `${CLOUDFLARE_API_BASE_URL}/zones/${zoneId}${normalizedPath}`
58
+ );
59
+ if (query) {
60
+ url.search = query.toString();
61
+ }
62
+ return url.toString();
63
+ }
64
+ function buildHeaders(cfApiToken) {
65
+ return {
66
+ Authorization: `Bearer ${cfApiToken}`,
67
+ "Content-Type": "application/json",
68
+ Accept: "application/json"
69
+ };
70
+ }
71
+ async function parseEnvelope(response) {
72
+ try {
73
+ return await response.json();
74
+ } catch {
75
+ return {
76
+ success: false,
77
+ errors: [
78
+ {
79
+ code: UNKNOWN_ERROR_CODE,
80
+ message: `HTTP ${response.status}: ${response.statusText}`
81
+ }
82
+ ],
83
+ messages: [],
84
+ result: null
85
+ };
86
+ }
87
+ }
88
+ function toCloudflareError(response, envelope) {
89
+ const firstError = envelope.errors[0];
90
+ const errorCode = String(firstError?.code ?? UNKNOWN_ERROR_CODE);
91
+ const message = firstError?.message ?? `HTTP ${response.status}: ${response.statusText}`;
92
+ return new CloudflareError(response.status, errorCode, message);
93
+ }
94
+ function mapDnsRecord(record) {
95
+ return {
96
+ recordId: record.id,
97
+ name: record.name,
98
+ ip: record.content,
99
+ proxied: record.proxied
100
+ };
101
+ }
102
+ var CloudflareError = class extends Error {
103
+ status;
104
+ errorCode;
105
+ constructor(status, errorCode, message) {
106
+ super(message);
107
+ this.name = "CloudflareError";
108
+ this.status = status;
109
+ this.errorCode = errorCode;
110
+ }
111
+ };
112
+ export {
113
+ CloudflareError,
114
+ createARecord,
115
+ deleteARecord,
116
+ findARecord
117
+ };
@@ -2937,11 +2937,6 @@ var init_session_kill_telemetry = __esm({
2937
2937
  });
2938
2938
 
2939
2939
  // src/lib/task-scope.ts
2940
- var task_scope_exports = {};
2941
- __export(task_scope_exports, {
2942
- getCurrentSessionScope: () => getCurrentSessionScope,
2943
- sessionScopeFilter: () => sessionScopeFilter
2944
- });
2945
2940
  function getCurrentSessionScope() {
2946
2941
  try {
2947
2942
  return resolveExeSession();
@@ -5611,7 +5606,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5611
5606
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
5612
5607
  `
5613
5608
  );
5614
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
5609
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
5615
5610
  } else if (useOpencode) {
5616
5611
  const binName = `${employeeName}-opencode`;
5617
5612
  process.stderr.write(
@@ -6482,6 +6477,7 @@ async function pollOrphanedTasks(deps, nowMs = Date.now()) {
6482
6477
  const agent = deps.parseAgentFromSession(session);
6483
6478
  if (agent) liveAgents.add(agent);
6484
6479
  }
6480
+ const coordinatorSessions = liveSessions.filter((s) => isExeSession(s));
6485
6481
  let tasksByAgent;
6486
6482
  try {
6487
6483
  tasksByAgent = await deps.queryTasksByAgent();
@@ -6490,12 +6486,17 @@ async function pollOrphanedTasks(deps, nowMs = Date.now()) {
6490
6486
  }
6491
6487
  const agentTasks = /* @__PURE__ */ new Map();
6492
6488
  for (const t of tasksByAgent) {
6493
- const existing = agentTasks.get(t.agentId) ?? [];
6494
- existing.push({ taskId: t.taskId, priority: t.priority });
6495
- agentTasks.set(t.agentId, existing);
6489
+ const scope = t.sessionScope || coordinatorSessions[0] || null;
6490
+ if (!scope) continue;
6491
+ const key = `${t.agentId}::${scope}`;
6492
+ const existing = agentTasks.get(key) ?? [];
6493
+ existing.push({ taskId: t.taskId, priority: t.priority, sessionScope: scope });
6494
+ agentTasks.set(key, existing);
6496
6495
  }
6497
6496
  const woken = [];
6498
- for (const [agentId, tasks] of agentTasks) {
6497
+ for (const [key, tasks] of agentTasks) {
6498
+ const agentId = key.split("::")[0];
6499
+ const sessionScope = tasks[0].sessionScope;
6499
6500
  if (!shouldAutoWake({
6500
6501
  agentId,
6501
6502
  hasRunningSession: liveAgents.has(agentId),
@@ -6522,11 +6523,11 @@ async function pollOrphanedTasks(deps, nowMs = Date.now()) {
6522
6523
  continue;
6523
6524
  }
6524
6525
  process.stderr.write(
6525
- `[auto-wake] ${agentId} has ${tasks.length} pending task(s) but no session \u2014 spawning (attempt ${retries + 1} for task ${topTask.taskId})
6526
+ `[auto-wake] ${agentId} has ${tasks.length} pending task(s) but no session \u2014 spawning in ${sessionScope} (attempt ${retries + 1} for task ${topTask.taskId})
6526
6527
  `
6527
6528
  );
6528
6529
  try {
6529
- const result = deps.ensureEmployee(agentId);
6530
+ const result = deps.ensureEmployee(agentId, sessionScope);
6530
6531
  if (result.status === "failed") {
6531
6532
  process.stderr.write(
6532
6533
  `[auto-wake] Failed to spawn ${agentId}: ${result.error ?? "unknown"}
@@ -6546,7 +6547,7 @@ async function pollOrphanedTasks(deps, nowMs = Date.now()) {
6546
6547
  }
6547
6548
  return woken;
6548
6549
  }
6549
- function createAutoWakeRealDeps(getClient2, sessionScope, projectDir) {
6550
+ function createAutoWakeRealDeps(getClient2, projectDir) {
6550
6551
  return {
6551
6552
  listTmuxSessions: () => {
6552
6553
  const { listTmuxSessions: listTmuxSessions2 } = (init_tmux_status(), __toCommonJS(tmux_status_exports));
@@ -6554,24 +6555,24 @@ function createAutoWakeRealDeps(getClient2, sessionScope, projectDir) {
6554
6555
  },
6555
6556
  queryTasksByAgent: async () => {
6556
6557
  const client = getClient2();
6557
- const scope = sessionScopeFilter(sessionScope);
6558
6558
  const result = await client.execute({
6559
- sql: `SELECT assigned_to, id, priority FROM tasks
6560
- WHERE status IN ('open', 'in_progress')${scope.sql}
6559
+ sql: `SELECT assigned_to, id, priority, session_scope FROM tasks
6560
+ WHERE status IN ('open', 'in_progress')
6561
6561
  ORDER BY
6562
6562
  CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END,
6563
6563
  created_at ASC`,
6564
- args: [...scope.args]
6564
+ args: []
6565
6565
  });
6566
6566
  return result.rows.map((r) => ({
6567
6567
  agentId: String(r.assigned_to),
6568
6568
  taskId: String(r.id),
6569
- priority: String(r.priority)
6569
+ priority: String(r.priority),
6570
+ sessionScope: r.session_scope ? String(r.session_scope) : null
6570
6571
  }));
6571
6572
  },
6572
- ensureEmployee: (agentName) => {
6573
+ ensureEmployee: (agentName, scope) => {
6573
6574
  const { ensureEmployee: ensureEmployee2 } = (init_tmux_routing(), __toCommonJS(tmux_routing_exports));
6574
- return ensureEmployee2(agentName, sessionScope, projectDir);
6575
+ return ensureEmployee2(agentName, scope, projectDir);
6575
6576
  },
6576
6577
  markTaskBlocked: async (taskId, reason) => {
6577
6578
  const client = getClient2();
@@ -10800,9 +10801,7 @@ function startAutoWake() {
10800
10801
  try {
10801
10802
  const { getClient: getClient2 } = await Promise.resolve().then(() => (init_database(), database_exports));
10802
10803
  const { pollOrphanedTasks: pollOrphanedTasks2, createAutoWakeRealDeps: createAutoWakeRealDeps2 } = await Promise.resolve().then(() => (init_daemon_orchestration(), daemon_orchestration_exports));
10803
- const { getCurrentSessionScope: getCurrentSessionScope2 } = await Promise.resolve().then(() => (init_task_scope(), task_scope_exports));
10804
- const sessionScope = getCurrentSessionScope2() ?? "";
10805
- const deps = createAutoWakeRealDeps2(getClient2, sessionScope, process.cwd());
10804
+ const deps = createAutoWakeRealDeps2(getClient2, process.cwd());
10806
10805
  const woken = await pollOrphanedTasks2(deps);
10807
10806
  for (const agent of woken) {
10808
10807
  process.stderr.write(`[exed] Auto-wake: spawned ${agent}
package/dist/lib/tasks.js CHANGED
@@ -1846,7 +1846,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
1846
1846
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
1847
1847
  `
1848
1848
  );
1849
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
1849
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
1850
1850
  } else if (useOpencode) {
1851
1851
  const binName = `${employeeName}-opencode`;
1852
1852
  process.stderr.write(
@@ -3426,7 +3426,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3426
3426
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
3427
3427
  `
3428
3428
  );
3429
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
3429
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
3430
3430
  } else if (useOpencode) {
3431
3431
  const binName = `${employeeName}-opencode`;
3432
3432
  process.stderr.write(
@@ -7156,7 +7156,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
7156
7156
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
7157
7157
  `
7158
7158
  );
7159
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
7159
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
7160
7160
  } else if (useOpencode) {
7161
7161
  const binName = `${employeeName}-opencode`;
7162
7162
  process.stderr.write(
@@ -2051,7 +2051,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
2051
2051
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
2052
2052
  `
2053
2053
  );
2054
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
2054
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
2055
2055
  } else if (useOpencode) {
2056
2056
  const binName = `${employeeName}-opencode`;
2057
2057
  process.stderr.write(
@@ -5038,7 +5038,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5038
5038
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
5039
5039
  `
5040
5040
  );
5041
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
5041
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
5042
5042
  } else if (useOpencode) {
5043
5043
  const binName = `${employeeName}-opencode`;
5044
5044
  process.stderr.write(
package/dist/tui/App.js CHANGED
@@ -5544,7 +5544,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
5544
5544
  `[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
5545
5545
  `
5546
5546
  );
5547
- spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
5547
+ spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName} --session ${sessionName}${cleanupSuffix}`;
5548
5548
  } else if (useOpencode) {
5549
5549
  const binName = `${employeeName}-opencode`;
5550
5550
  process.stderr.write(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.8.108",
3
+ "version": "0.9.0",
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",