oh-my-codex 0.11.12 → 0.11.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +23 -0
  4. package/README.vi.md +144 -185
  5. package/crates/omx-runtime-core/src/engine.rs +122 -4
  6. package/crates/omx-runtime-core/src/lib.rs +17 -0
  7. package/dist/cli/__tests__/autoresearch.test.js +11 -0
  8. package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
  9. package/dist/cli/__tests__/cleanup.test.js +117 -4
  10. package/dist/cli/__tests__/cleanup.test.js.map +1 -1
  11. package/dist/cli/__tests__/error-handling-warnings.test.js +13 -0
  12. package/dist/cli/__tests__/error-handling-warnings.test.js.map +1 -1
  13. package/dist/cli/__tests__/exec.test.js +6 -0
  14. package/dist/cli/__tests__/exec.test.js.map +1 -1
  15. package/dist/cli/__tests__/index.test.js +94 -1
  16. package/dist/cli/__tests__/index.test.js.map +1 -1
  17. package/dist/cli/__tests__/launch-fallback.test.js +3 -0
  18. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  19. package/dist/cli/__tests__/package-bin-contract.test.js +10 -0
  20. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  21. package/dist/cli/__tests__/packaged-script-resolution.test.js +4 -3
  22. package/dist/cli/__tests__/packaged-script-resolution.test.js.map +1 -1
  23. package/dist/cli/__tests__/resume.test.js +6 -0
  24. package/dist/cli/__tests__/resume.test.js.map +1 -1
  25. package/dist/cli/__tests__/setup-refresh.test.js +29 -12
  26. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  27. package/dist/cli/__tests__/star-prompt.test.js +16 -0
  28. package/dist/cli/__tests__/star-prompt.test.js.map +1 -1
  29. package/dist/cli/__tests__/uninstall.test.js +112 -1
  30. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  31. package/dist/cli/__tests__/windows-popup-loop-contract.test.d.ts +2 -0
  32. package/dist/cli/__tests__/windows-popup-loop-contract.test.d.ts.map +1 -0
  33. package/dist/cli/__tests__/windows-popup-loop-contract.test.js +30 -0
  34. package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -0
  35. package/dist/cli/cleanup.d.ts +2 -0
  36. package/dist/cli/cleanup.d.ts.map +1 -1
  37. package/dist/cli/cleanup.js +26 -1
  38. package/dist/cli/cleanup.js.map +1 -1
  39. package/dist/cli/index.d.ts +7 -0
  40. package/dist/cli/index.d.ts.map +1 -1
  41. package/dist/cli/index.js +161 -50
  42. package/dist/cli/index.js.map +1 -1
  43. package/dist/cli/setup.d.ts.map +1 -1
  44. package/dist/cli/setup.js +15 -14
  45. package/dist/cli/setup.js.map +1 -1
  46. package/dist/cli/star-prompt.d.ts.map +1 -1
  47. package/dist/cli/star-prompt.js +1 -0
  48. package/dist/cli/star-prompt.js.map +1 -1
  49. package/dist/cli/team.d.ts.map +1 -1
  50. package/dist/cli/team.js +5 -1
  51. package/dist/cli/team.js.map +1 -1
  52. package/dist/cli/uninstall.d.ts.map +1 -1
  53. package/dist/cli/uninstall.js +26 -0
  54. package/dist/cli/uninstall.js.map +1 -1
  55. package/dist/cli/update.d.ts.map +1 -1
  56. package/dist/cli/update.js +1 -0
  57. package/dist/cli/update.js.map +1 -1
  58. package/dist/config/__tests__/generator-idempotent.test.js +4 -4
  59. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  60. package/dist/config/__tests__/mcp-registry.test.js +13 -16
  61. package/dist/config/__tests__/mcp-registry.test.js.map +1 -1
  62. package/dist/config/mcp-registry.d.ts +1 -0
  63. package/dist/config/mcp-registry.d.ts.map +1 -1
  64. package/dist/config/mcp-registry.js +4 -4
  65. package/dist/config/mcp-registry.js.map +1 -1
  66. package/dist/config/models.d.ts +1 -0
  67. package/dist/config/models.d.ts.map +1 -1
  68. package/dist/config/models.js +39 -1
  69. package/dist/config/models.js.map +1 -1
  70. package/dist/hooks/__tests__/keyword-detector.test.js +12 -1
  71. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  72. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +499 -17
  73. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  74. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +140 -14
  75. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  76. package/dist/hooks/__tests__/notify-hook-modules.test.js +5 -0
  77. package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
  78. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.d.ts +2 -0
  79. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.d.ts.map +1 -0
  80. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +597 -0
  81. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -0
  82. package/dist/hooks/__tests__/notify-hook-regression-205.test.js +15 -1
  83. package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
  84. package/dist/hooks/__tests__/notify-hook-session-scope.test.js +73 -53
  85. package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
  86. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +193 -2
  87. package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
  88. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +183 -0
  89. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  90. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +255 -97
  91. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  92. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +0 -0
  93. package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
  94. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +46 -0
  95. package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
  96. package/dist/hooks/keyword-detector.d.ts +1 -0
  97. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  98. package/dist/hooks/keyword-detector.js +48 -0
  99. package/dist/hooks/keyword-detector.js.map +1 -1
  100. package/dist/hooks/session.d.ts.map +1 -1
  101. package/dist/hooks/session.js +1 -0
  102. package/dist/hooks/session.js.map +1 -1
  103. package/dist/hud/__tests__/state.test.js +70 -1
  104. package/dist/hud/__tests__/state.test.js.map +1 -1
  105. package/dist/hud/state.d.ts.map +1 -1
  106. package/dist/hud/state.js +10 -37
  107. package/dist/hud/state.js.map +1 -1
  108. package/dist/mcp/state-server.d.ts.map +1 -1
  109. package/dist/mcp/state-server.js +5 -0
  110. package/dist/mcp/state-server.js.map +1 -1
  111. package/dist/modes/__tests__/base-session-scope.test.js +46 -0
  112. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  113. package/dist/modes/base.d.ts.map +1 -1
  114. package/dist/modes/base.js +4 -0
  115. package/dist/modes/base.js.map +1 -1
  116. package/dist/notifications/__tests__/custom-alias-enablement.test.d.ts +2 -0
  117. package/dist/notifications/__tests__/custom-alias-enablement.test.d.ts.map +1 -0
  118. package/dist/notifications/__tests__/custom-alias-enablement.test.js +84 -0
  119. package/dist/notifications/__tests__/custom-alias-enablement.test.js.map +1 -0
  120. package/dist/notifications/__tests__/idle-cooldown.test.js +55 -0
  121. package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
  122. package/dist/notifications/idle-cooldown.d.ts +8 -6
  123. package/dist/notifications/idle-cooldown.d.ts.map +1 -1
  124. package/dist/notifications/idle-cooldown.js +53 -22
  125. package/dist/notifications/idle-cooldown.js.map +1 -1
  126. package/dist/notifications/notifier.js +1 -1
  127. package/dist/notifications/notifier.js.map +1 -1
  128. package/dist/notifications/reply-listener.d.ts.map +1 -1
  129. package/dist/notifications/reply-listener.js +1 -0
  130. package/dist/notifications/reply-listener.js.map +1 -1
  131. package/dist/openclaw/config.js +2 -2
  132. package/dist/openclaw/config.js.map +1 -1
  133. package/dist/runtime/bridge.d.ts +1 -0
  134. package/dist/runtime/bridge.d.ts.map +1 -1
  135. package/dist/runtime/bridge.js +2 -6
  136. package/dist/runtime/bridge.js.map +1 -1
  137. package/dist/scripts/notify-fallback-watcher.js +97 -59
  138. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  139. package/dist/scripts/notify-hook/auto-nudge.d.ts +2 -1
  140. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  141. package/dist/scripts/notify-hook/auto-nudge.js +72 -238
  142. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  143. package/dist/scripts/notify-hook/managed-tmux.d.ts +19 -0
  144. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -0
  145. package/dist/scripts/notify-hook/managed-tmux.js +320 -0
  146. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -0
  147. package/dist/scripts/notify-hook/ralph-session-resume.d.ts +22 -0
  148. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -0
  149. package/dist/scripts/notify-hook/ralph-session-resume.js +277 -0
  150. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -0
  151. package/dist/scripts/notify-hook/state-io.d.ts +1 -1
  152. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  153. package/dist/scripts/notify-hook/state-io.js +2 -10
  154. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  155. package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
  156. package/dist/scripts/notify-hook/team-dispatch.js +60 -59
  157. package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
  158. package/dist/scripts/notify-hook/team-leader-nudge.d.ts +2 -1
  159. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  160. package/dist/scripts/notify-hook/team-leader-nudge.js +13 -5
  161. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  162. package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
  163. package/dist/scripts/notify-hook/team-tmux-guard.js +1 -19
  164. package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
  165. package/dist/scripts/notify-hook/team-worker.js +4 -4
  166. package/dist/scripts/notify-hook/team-worker.js.map +1 -1
  167. package/dist/scripts/notify-hook/tmux-injection.d.ts +1 -1
  168. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  169. package/dist/scripts/notify-hook/tmux-injection.js +102 -35
  170. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  171. package/dist/scripts/notify-hook.js +144 -20
  172. package/dist/scripts/notify-hook.js.map +1 -1
  173. package/dist/scripts/tmux-hook-engine.d.ts +1 -0
  174. package/dist/scripts/tmux-hook-engine.d.ts.map +1 -1
  175. package/dist/scripts/tmux-hook-engine.js +3 -0
  176. package/dist/scripts/tmux-hook-engine.js.map +1 -1
  177. package/dist/team/__tests__/api-interop.test.js +96 -4
  178. package/dist/team/__tests__/api-interop.test.js.map +1 -1
  179. package/dist/team/__tests__/leader-activity.test.js +107 -2
  180. package/dist/team/__tests__/leader-activity.test.js.map +1 -1
  181. package/dist/team/__tests__/runtime-cli.test.js +32 -0
  182. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  183. package/dist/team/__tests__/runtime.test.js +148 -0
  184. package/dist/team/__tests__/runtime.test.js.map +1 -1
  185. package/dist/team/__tests__/shutdown-fallback.test.js +13 -0
  186. package/dist/team/__tests__/shutdown-fallback.test.js.map +1 -1
  187. package/dist/team/__tests__/state-root.test.js +11 -1
  188. package/dist/team/__tests__/state-root.test.js.map +1 -1
  189. package/dist/team/__tests__/state.test.js +16 -5
  190. package/dist/team/__tests__/state.test.js.map +1 -1
  191. package/dist/team/__tests__/tmux-session.test.js +460 -2
  192. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  193. package/dist/team/api-interop.d.ts.map +1 -1
  194. package/dist/team/api-interop.js +34 -7
  195. package/dist/team/api-interop.js.map +1 -1
  196. package/dist/team/commit-hygiene.d.ts +60 -0
  197. package/dist/team/commit-hygiene.d.ts.map +1 -0
  198. package/dist/team/commit-hygiene.js +232 -0
  199. package/dist/team/commit-hygiene.js.map +1 -0
  200. package/dist/team/leader-activity.d.ts.map +1 -1
  201. package/dist/team/leader-activity.js +17 -35
  202. package/dist/team/leader-activity.js.map +1 -1
  203. package/dist/team/runtime-cli.d.ts +9 -1
  204. package/dist/team/runtime-cli.d.ts.map +1 -1
  205. package/dist/team/runtime-cli.js +15 -6
  206. package/dist/team/runtime-cli.js.map +1 -1
  207. package/dist/team/runtime.d.ts +7 -2
  208. package/dist/team/runtime.d.ts.map +1 -1
  209. package/dist/team/runtime.js +391 -63
  210. package/dist/team/runtime.js.map +1 -1
  211. package/dist/team/state/dispatch.js +1 -1
  212. package/dist/team/state/dispatch.js.map +1 -1
  213. package/dist/team/state/mailbox.d.ts +1 -0
  214. package/dist/team/state/mailbox.d.ts.map +1 -1
  215. package/dist/team/state/mailbox.js +54 -8
  216. package/dist/team/state/mailbox.js.map +1 -1
  217. package/dist/team/state-root.d.ts +1 -1
  218. package/dist/team/state-root.d.ts.map +1 -1
  219. package/dist/team/state-root.js +8 -3
  220. package/dist/team/state-root.js.map +1 -1
  221. package/dist/team/state.d.ts.map +1 -1
  222. package/dist/team/state.js +66 -3
  223. package/dist/team/state.js.map +1 -1
  224. package/dist/team/tmux-session.d.ts.map +1 -1
  225. package/dist/team/tmux-session.js +69 -27
  226. package/dist/team/tmux-session.js.map +1 -1
  227. package/dist/utils/__tests__/platform-command.test.js +101 -2
  228. package/dist/utils/__tests__/platform-command.test.js.map +1 -1
  229. package/dist/utils/git-layout.d.ts +8 -0
  230. package/dist/utils/git-layout.d.ts.map +1 -0
  231. package/dist/utils/git-layout.js +58 -0
  232. package/dist/utils/git-layout.js.map +1 -0
  233. package/dist/utils/platform-command.d.ts.map +1 -1
  234. package/dist/utils/platform-command.js +32 -1
  235. package/dist/utils/platform-command.js.map +1 -1
  236. package/package.json +6 -6
  237. package/src/scripts/notify-fallback-watcher.ts +96 -58
  238. package/src/scripts/notify-hook/auto-nudge.ts +75 -230
  239. package/src/scripts/notify-hook/managed-tmux.ts +324 -0
  240. package/src/scripts/notify-hook/ralph-session-resume.ts +337 -0
  241. package/src/scripts/notify-hook/state-io.ts +2 -10
  242. package/src/scripts/notify-hook/team-dispatch.ts +70 -54
  243. package/src/scripts/notify-hook/team-leader-nudge.ts +19 -5
  244. package/src/scripts/notify-hook/team-tmux-guard.ts +0 -20
  245. package/src/scripts/notify-hook/team-worker.ts +4 -4
  246. package/src/scripts/notify-hook/tmux-injection.ts +103 -33
  247. package/src/scripts/notify-hook.ts +150 -21
  248. package/src/scripts/tmux-hook-engine.ts +4 -0
package/dist/cli/index.js CHANGED
@@ -15,7 +15,7 @@ import { hudCommand } from "../hud/index.js";
15
15
  import { teamCommand } from "./team.js";
16
16
  import { ralphCommand } from "./ralph.js";
17
17
  import { askCommand } from "./ask.js";
18
- import { cleanupCommand } from "./cleanup.js";
18
+ import { cleanupCommand, cleanupOmxMcpProcesses, findLaunchSafeCleanupCandidates, } from "./cleanup.js";
19
19
  import { exploreCommand } from "./explore.js";
20
20
  import { sparkshellCommand } from "./sparkshell.js";
21
21
  import { agentsInitCommand } from "./agents-init.js";
@@ -28,7 +28,7 @@ import { maybeCheckAndPromptUpdate } from "./update.js";
28
28
  import { maybePromptGithubStar } from "./star-prompt.js";
29
29
  import { generateOverlay, removeSessionModelInstructionsFile, resolveSessionOrchestrationMode, sessionModelInstructionsPath, writeSessionModelInstructionsFile, } from "../hooks/agents-overlay.js";
30
30
  import { readSessionState, writeSessionStart, writeSessionEnd, resetSessionMetrics, } from "../hooks/session.js";
31
- import { buildClientAttachedReconcileHookName, buildReconcileHudResizeArgs, buildRegisterClientAttachedReconcileArgs, buildRegisterResizeHookArgs, buildResizeHookName, buildResizeHookTarget, buildScheduleDelayedHudResizeArgs, buildUnregisterClientAttachedReconcileArgs, buildUnregisterResizeHookArgs, enableMouseScrolling, isNativeWindows, isTmuxAvailable, } from "../team/tmux-session.js";
31
+ import { buildClientAttachedReconcileHookName, buildReconcileHudResizeArgs, buildRegisterClientAttachedReconcileArgs, buildRegisterResizeHookArgs, buildResizeHookName, buildResizeHookTarget, buildScheduleDelayedHudResizeArgs, buildUnregisterClientAttachedReconcileArgs, buildUnregisterResizeHookArgs, enableMouseScrolling, isMsysOrGitBash, isNativeWindows, isTmuxAvailable, } from "../team/tmux-session.js";
32
32
  import { getPackageRoot } from "../utils/package.js";
33
33
  import { codexConfigPath } from "../utils/paths.js";
34
34
  import { repairConfigIfNeeded } from "../config/generator.js";
@@ -40,13 +40,16 @@ import { collectInheritableTeamWorkerArgs as collectInheritableTeamWorkerArgsSha
40
40
  import { parseWorktreeMode, planWorktreeTarget, ensureWorktree, } from "../team/worktree.js";
41
41
  import { OMX_NOTIFY_TEMP_CONTRACT_ENV, parseNotifyTempContractFromArgs, serializeNotifyTempContract, } from "../notifications/temp-contract.js";
42
42
  export function resolveNotifyFallbackWatcherScript(pkgRoot = getPackageRoot()) {
43
- return join(pkgRoot, "dist", "scripts", "notify-fallback-watcher.js");
43
+ return resolveDistScript(pkgRoot, "notify-fallback-watcher.js");
44
44
  }
45
45
  export function resolveHookDerivedWatcherScript(pkgRoot = getPackageRoot()) {
46
- return join(pkgRoot, "dist", "scripts", "hook-derived-watcher.js");
46
+ return resolveDistScript(pkgRoot, "hook-derived-watcher.js");
47
47
  }
48
48
  export function resolveNotifyHookScript(pkgRoot = getPackageRoot()) {
49
- return join(pkgRoot, "dist", "scripts", "notify-hook.js");
49
+ return resolveDistScript(pkgRoot, "notify-hook.js");
50
+ }
51
+ function resolveDistScript(pkgRoot, scriptName) {
52
+ return join(pkgRoot, "dist", "scripts", scriptName);
50
53
  }
51
54
  const HELP = `
52
55
  oh-my-codex (omx) - Multi-agent orchestration for Codex CLI
@@ -1023,6 +1026,9 @@ export function buildTmuxSessionName(cwd, sessionId) {
1023
1026
  const name = `omx-${dirToken}-${branchToken}-${sessionToken}`;
1024
1027
  return name.length > 120 ? name.slice(0, 120) : name;
1025
1028
  }
1029
+ export function buildDetachedTmuxSessionName(cwd, sessionId) {
1030
+ return buildTmuxSessionName(cwd, sessionId);
1031
+ }
1026
1032
  function parsePaneIdFromTmuxOutput(rawOutput) {
1027
1033
  const paneId = rawOutput.split("\n")[0]?.trim() || "";
1028
1034
  return paneId.startsWith("%") ? paneId : null;
@@ -1191,17 +1197,39 @@ export function buildNotifyFallbackWatcherEnv(env = process.env, options = {}) {
1191
1197
  OMX_HUD_AUTHORITY: options.enableAuthority ? "1" : "0",
1192
1198
  };
1193
1199
  }
1200
+ export async function cleanupLaunchOrphanedMcpProcesses(dependencies = {}) {
1201
+ return cleanupOmxMcpProcesses([], {
1202
+ ...dependencies,
1203
+ selectCandidates: dependencies.selectCandidates ?? findLaunchSafeCleanupCandidates,
1204
+ writeLine: dependencies.writeLine ?? (() => { }),
1205
+ });
1206
+ }
1194
1207
  /**
1195
1208
  * preLaunch: Prepare environment before Codex starts.
1196
- * 1. Generate runtime overlay + write session-scoped model instructions file
1197
- * 2. Write session.json
1209
+ * 1. Best-effort launch-safe orphan cleanup for detached OMX MCP processes
1210
+ * 2. Generate runtime overlay + write session-scoped model instructions file
1211
+ * 3. Write session.json
1198
1212
  *
1199
- * Automatic stale-session cleanup is intentionally disabled here. Destructive
1200
- * cleanup must be explicit via `omx cleanup` so normal launches never reap
1201
- * files or processes from other OMX sessions.
1213
+ * Automatic broad stale-session cleanup remains disabled here. Only detached
1214
+ * OMX MCP processes without a live Codex ancestor are reaped so new launches
1215
+ * do not accumulate stale processes from prior crashed/closed sessions.
1202
1216
  */
1203
1217
  async function preLaunch(cwd, sessionId, notifyTempContract, codexHomeOverride, enableNotifyFallbackAuthority = false) {
1204
- // 1. Generate runtime overlay + write session-scoped model instructions file
1218
+ // 1. Best-effort launch-safe orphan cleanup
1219
+ try {
1220
+ const cleanup = await cleanupLaunchOrphanedMcpProcesses();
1221
+ if (cleanup.terminatedCount > 0) {
1222
+ console.log(`[omx] Reaped ${cleanup.terminatedCount} orphaned OMX MCP process(es) before launch.`);
1223
+ }
1224
+ if (cleanup.failedPids.length > 0) {
1225
+ console.warn(`[omx] Failed to reap ${cleanup.failedPids.length} orphaned OMX MCP process(es); continuing launch.`);
1226
+ }
1227
+ }
1228
+ catch (err) {
1229
+ process.stderr.write(`[cli/index] operation failed: ${err}\n`);
1230
+ // Non-fatal
1231
+ }
1232
+ // 2. Generate runtime overlay + write session-scoped model instructions file
1205
1233
  const orchestrationMode = await resolveSessionOrchestrationMode(cwd, sessionId);
1206
1234
  const overlay = await generateOverlay(cwd, sessionId, { orchestrationMode });
1207
1235
  const launchAppendix = await readLaunchAppendInstructions();
@@ -1211,10 +1239,10 @@ async function preLaunch(cwd, sessionId, notifyTempContract, codexHomeOverride,
1211
1239
  ${launchAppendix}`
1212
1240
  : overlay;
1213
1241
  await writeSessionModelInstructionsFile(cwd, sessionId, sessionInstructions);
1214
- // 2. Write session state
1242
+ // 3. Write session state
1215
1243
  await resetSessionMetrics(cwd);
1216
1244
  await writeSessionStart(cwd, sessionId);
1217
- // 3. Start notify fallback watcher (best effort)
1245
+ // 4. Start notify fallback watcher (best effort)
1218
1246
  try {
1219
1247
  await startNotifyFallbackWatcher(cwd, { codexHomeOverride, enableAuthority: enableNotifyFallbackAuthority, sessionId });
1220
1248
  }
@@ -1222,7 +1250,7 @@ ${launchAppendix}`
1222
1250
  process.stderr.write(`[cli/index] operation failed: ${err}\n`);
1223
1251
  // Non-fatal
1224
1252
  }
1225
- // 4. Start derived watcher (best effort, opt-in)
1253
+ // 5. Start derived watcher (best effort, opt-in)
1226
1254
  try {
1227
1255
  await startHookDerivedWatcher(cwd);
1228
1256
  }
@@ -1230,7 +1258,7 @@ ${launchAppendix}`
1230
1258
  process.stderr.write(`[cli/index] operation failed: ${err}\n`);
1231
1259
  // Non-fatal
1232
1260
  }
1233
- // 5. Emit temp notification startup summary + warnings, then send session-start lifecycle notification (best effort)
1261
+ // 6. Emit temp notification startup summary + warnings, then send session-start lifecycle notification (best effort)
1234
1262
  try {
1235
1263
  if (notifyTempContract?.active) {
1236
1264
  process.env[OMX_NOTIFY_TEMP_CONTRACT_ENV] =
@@ -1259,7 +1287,7 @@ ${launchAppendix}`
1259
1287
  process.stderr.write(`[cli/index] operation failed: ${err}\n`);
1260
1288
  // Non-fatal: notification failures must never block launch
1261
1289
  }
1262
- // 6. Dispatch native hook event (best effort)
1290
+ // 7. Dispatch native hook event (best effort)
1263
1291
  try {
1264
1292
  await emitNativeHookEvent(cwd, "session-start", {
1265
1293
  session_id: sessionId,
@@ -1368,8 +1396,7 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, n
1368
1396
  const detachedWindowsCodexCmd = nativeWindows
1369
1397
  ? buildWindowsPromptCommand("codex", launchArgs)
1370
1398
  : null;
1371
- const tmuxSessionId = `omx-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1372
- const sessionName = buildTmuxSessionName(cwd, tmuxSessionId);
1399
+ const sessionName = buildDetachedTmuxSessionName(cwd, sessionId);
1373
1400
  let createdDetachedSession = false;
1374
1401
  let registeredHookTarget = null;
1375
1402
  let registeredHookName = null;
@@ -1707,6 +1734,66 @@ function notifyFallbackPidPath(cwd) {
1707
1734
  function hookDerivedWatcherPidPath(cwd) {
1708
1735
  return join(cwd, ".omx", "state", "hook-derived-watcher.pid");
1709
1736
  }
1737
+ export function shouldDetachBackgroundHelper(env = process.env, platform = process.platform) {
1738
+ // The long-running watcher/helper itself must stay detached so it can
1739
+ // survive parent loss. Windows Git Bash/MSYS uses a short hidden bootstrap
1740
+ // process so the detached helper is created without stealing focus.
1741
+ void env;
1742
+ void platform;
1743
+ return true;
1744
+ }
1745
+ export function resolveBackgroundHelperLaunchMode(env = process.env, platform = process.platform) {
1746
+ return platform === "win32" && isMsysOrGitBash(env, platform)
1747
+ ? "windows-msys-bootstrap"
1748
+ : "direct-detached";
1749
+ }
1750
+ export function buildWindowsMsysBackgroundHelperBootstrapScript(helperArgs, cwd) {
1751
+ const helperArgsLiteral = JSON.stringify(helperArgs);
1752
+ const cwdLiteral = JSON.stringify(cwd);
1753
+ return [
1754
+ "const { spawn } = require('child_process');",
1755
+ `const child = spawn(process.execPath, ${helperArgsLiteral}, { cwd: ${cwdLiteral}, detached: true, stdio: 'ignore', windowsHide: true, env: process.env });`,
1756
+ "if (!child.pid) process.exit(1);",
1757
+ "process.stdout.write(String(child.pid));",
1758
+ "child.unref();",
1759
+ ].join("");
1760
+ }
1761
+ async function launchBackgroundHelper(helperArgs, options) {
1762
+ const launchMode = resolveBackgroundHelperLaunchMode(options.env, process.platform);
1763
+ if (launchMode === "windows-msys-bootstrap") {
1764
+ const { spawnSync } = await import("child_process");
1765
+ const bootstrap = spawnSync(process.execPath, [
1766
+ "-e",
1767
+ buildWindowsMsysBackgroundHelperBootstrapScript(helperArgs, options.cwd),
1768
+ ], {
1769
+ cwd: options.cwd,
1770
+ encoding: "utf-8",
1771
+ stdio: ["ignore", "pipe", "pipe"],
1772
+ windowsHide: true,
1773
+ env: options.env,
1774
+ });
1775
+ if (bootstrap.error) {
1776
+ throw bootstrap.error;
1777
+ }
1778
+ if (bootstrap.status !== 0) {
1779
+ const detail = (bootstrap.stderr || bootstrap.stdout || "").trim();
1780
+ throw new Error(detail || `background helper bootstrap exited ${bootstrap.status}`);
1781
+ }
1782
+ const helperPid = Number.parseInt((bootstrap.stdout || "").trim(), 10);
1783
+ return Number.isFinite(helperPid) && helperPid > 0
1784
+ ? helperPid
1785
+ : undefined;
1786
+ }
1787
+ const child = spawn(process.execPath, helperArgs, {
1788
+ cwd: options.cwd,
1789
+ detached: shouldDetachBackgroundHelper(options.env, process.platform),
1790
+ stdio: "ignore",
1791
+ windowsHide: true,
1792
+ env: options.env,
1793
+ });
1794
+ child.unref();
1795
+ return child.pid;
1796
+ }
1710
1797
  function parseWatcherPidFile(content) {
1711
1798
  const trimmed = content.trim();
1712
1799
  if (!trimmed)
@@ -1769,31 +1856,44 @@ async function startNotifyFallbackWatcher(cwd, options = {}) {
1769
1856
  error: error instanceof Error ? error.message : String(error),
1770
1857
  });
1771
1858
  });
1772
- const child = spawn(process.execPath, [
1773
- watcherScript,
1774
- "--cwd",
1775
- cwd,
1776
- "--notify-script",
1777
- notifyScript,
1778
- "--pid-file",
1779
- pidPath,
1780
- "--parent-pid",
1781
- String(process.pid),
1782
- ...(process.env.OMX_NOTIFY_FALLBACK_MAX_LIFETIME_MS
1783
- ? ["--max-lifetime-ms", process.env.OMX_NOTIFY_FALLBACK_MAX_LIFETIME_MS]
1784
- : []),
1785
- ], {
1786
- cwd,
1787
- detached: true,
1788
- stdio: "ignore",
1789
- env: buildNotifyFallbackWatcherEnv(process.env, {
1790
- codexHomeOverride: options.codexHomeOverride,
1791
- enableAuthority: options.enableAuthority === true,
1792
- sessionId: options.sessionId,
1793
- }),
1859
+ const watcherEnv = buildNotifyFallbackWatcherEnv(process.env, {
1860
+ codexHomeOverride: options.codexHomeOverride,
1861
+ enableAuthority: options.enableAuthority === true,
1862
+ sessionId: options.sessionId,
1794
1863
  });
1795
- child.unref();
1796
- await writeFile(pidPath, JSON.stringify({ pid: child.pid, started_at: new Date().toISOString() }, null, 2)).catch((error) => {
1864
+ let watcherPid;
1865
+ try {
1866
+ watcherPid = await launchBackgroundHelper([
1867
+ watcherScript,
1868
+ "--cwd",
1869
+ cwd,
1870
+ "--notify-script",
1871
+ notifyScript,
1872
+ "--pid-file",
1873
+ pidPath,
1874
+ "--parent-pid",
1875
+ String(process.pid),
1876
+ ...(process.env.OMX_NOTIFY_FALLBACK_MAX_LIFETIME_MS
1877
+ ? [
1878
+ "--max-lifetime-ms",
1879
+ process.env.OMX_NOTIFY_FALLBACK_MAX_LIFETIME_MS,
1880
+ ]
1881
+ : []),
1882
+ ], {
1883
+ cwd,
1884
+ env: watcherEnv,
1885
+ });
1886
+ }
1887
+ catch (error) {
1888
+ console.warn("[omx] warning: failed to launch notify fallback watcher", {
1889
+ cwd,
1890
+ error: error instanceof Error ? error.message : String(error),
1891
+ });
1892
+ return;
1893
+ }
1894
+ if (!watcherPid)
1895
+ return;
1896
+ await writeFile(pidPath, JSON.stringify({ pid: watcherPid, started_at: new Date().toISOString() }, null, 2)).catch((error) => {
1797
1897
  console.warn("[omx] warning: failed to write notify fallback watcher pid file", {
1798
1898
  path: pidPath,
1799
1899
  error: error instanceof Error ? error.message : String(error),
@@ -1829,14 +1929,23 @@ async function startHookDerivedWatcher(cwd) {
1829
1929
  error: error instanceof Error ? error.message : String(error),
1830
1930
  });
1831
1931
  });
1832
- const child = spawn(process.execPath, [watcherScript, "--cwd", cwd], {
1833
- cwd,
1834
- detached: true,
1835
- stdio: "ignore",
1836
- env: process.env,
1837
- });
1838
- child.unref();
1839
- await writeFile(pidPath, JSON.stringify({ pid: child.pid, started_at: new Date().toISOString() }, null, 2)).catch((error) => {
1932
+ let watcherPid;
1933
+ try {
1934
+ watcherPid = await launchBackgroundHelper([watcherScript, "--cwd", cwd], {
1935
+ cwd,
1936
+ env: process.env,
1937
+ });
1938
+ }
1939
+ catch (error) {
1940
+ console.warn("[omx] warning: failed to launch hook-derived watcher", {
1941
+ cwd,
1942
+ error: error instanceof Error ? error.message : String(error),
1943
+ });
1944
+ return;
1945
+ }
1946
+ if (!watcherPid)
1947
+ return;
1948
+ await writeFile(pidPath, JSON.stringify({ pid: watcherPid, started_at: new Date().toISOString() }, null, 2)).catch((error) => {
1840
1949
  console.warn("[omx] warning: failed to write hook-derived watcher pid file", {
1841
1950
  path: pidPath,
1842
1951
  error: error instanceof Error ? error.message : String(error),
@@ -1904,6 +2013,7 @@ async function flushNotifyFallbackOnce(cwd, options = {}) {
1904
2013
  cwd,
1905
2014
  stdio: "ignore",
1906
2015
  timeout: 3000,
2016
+ windowsHide: true,
1907
2017
  env: buildNotifyFallbackWatcherEnv(process.env, {
1908
2018
  codexHomeOverride: options.codexHomeOverride,
1909
2019
  enableAuthority: options.enableAuthority === true,
@@ -1923,6 +2033,7 @@ async function flushHookDerivedWatcherOnce(cwd) {
1923
2033
  cwd,
1924
2034
  stdio: "ignore",
1925
2035
  timeout: 3000,
2036
+ windowsHide: true,
1926
2037
  env: {
1927
2038
  ...process.env,
1928
2039
  OMX_HOOK_DERIVED_SIGNALS: "1",