oh-my-codex 0.18.16 → 0.19.1

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 (232) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +5 -0
  4. package/crates/omx-sparkshell/tests/execution.rs +8 -1
  5. package/dist/agents/__tests__/definitions.test.js +5 -5
  6. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  7. package/dist/agents/__tests__/native-config.test.js +18 -14
  8. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  9. package/dist/agents/definitions.d.ts +2 -2
  10. package/dist/agents/definitions.d.ts.map +1 -1
  11. package/dist/agents/definitions.js +4 -4
  12. package/dist/agents/definitions.js.map +1 -1
  13. package/dist/agents/native-config.d.ts +1 -0
  14. package/dist/agents/native-config.d.ts.map +1 -1
  15. package/dist/agents/native-config.js +18 -16
  16. package/dist/agents/native-config.js.map +1 -1
  17. package/dist/auth/hotswap.d.ts.map +1 -1
  18. package/dist/auth/hotswap.js +17 -4
  19. package/dist/auth/hotswap.js.map +1 -1
  20. package/dist/autopilot/__tests__/planner-routing.test.js +2 -2
  21. package/dist/autopilot/__tests__/planner-routing.test.js.map +1 -1
  22. package/dist/autopilot/fsm.d.ts +1 -1
  23. package/dist/autopilot/fsm.d.ts.map +1 -1
  24. package/dist/autopilot/fsm.js +1 -1
  25. package/dist/autopilot/fsm.js.map +1 -1
  26. package/dist/cli/__tests__/auth.test.js +78 -3
  27. package/dist/cli/__tests__/auth.test.js.map +1 -1
  28. package/dist/cli/__tests__/codex-plugin-layout.test.js +30 -5
  29. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  30. package/dist/cli/__tests__/doctor-warning-copy.test.js +2 -0
  31. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  32. package/dist/cli/__tests__/index.test.js +96 -8
  33. package/dist/cli/__tests__/index.test.js.map +1 -1
  34. package/dist/cli/__tests__/launch-fallback.test.js +135 -1
  35. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  36. package/dist/cli/__tests__/mcp-parity.test.js +2 -4
  37. package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
  38. package/dist/cli/__tests__/mission.test.d.ts +2 -0
  39. package/dist/cli/__tests__/mission.test.d.ts.map +1 -0
  40. package/dist/cli/__tests__/mission.test.js +239 -0
  41. package/dist/cli/__tests__/mission.test.js.map +1 -0
  42. package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js +14 -0
  43. package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js.map +1 -1
  44. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +8 -0
  45. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  46. package/dist/cli/__tests__/resume.test.js +229 -1
  47. package/dist/cli/__tests__/resume.test.js.map +1 -1
  48. package/dist/cli/__tests__/session-scoped-runtime.test.js +113 -0
  49. package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
  50. package/dist/cli/__tests__/setup-install-mode.test.js +37 -0
  51. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  52. package/dist/cli/__tests__/ultragoal.test.js +22 -1
  53. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  54. package/dist/cli/__tests__/version.test.js +13 -1
  55. package/dist/cli/__tests__/version.test.js.map +1 -1
  56. package/dist/cli/auth.d.ts +1 -1
  57. package/dist/cli/auth.d.ts.map +1 -1
  58. package/dist/cli/auth.js +49 -9
  59. package/dist/cli/auth.js.map +1 -1
  60. package/dist/cli/index.d.ts +27 -4
  61. package/dist/cli/index.d.ts.map +1 -1
  62. package/dist/cli/index.js +263 -36
  63. package/dist/cli/index.js.map +1 -1
  64. package/dist/cli/mission.d.ts +36 -0
  65. package/dist/cli/mission.d.ts.map +1 -0
  66. package/dist/cli/mission.js +443 -0
  67. package/dist/cli/mission.js.map +1 -0
  68. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  69. package/dist/cli/plugin-marketplace.js +22 -1
  70. package/dist/cli/plugin-marketplace.js.map +1 -1
  71. package/dist/cli/ralph.d.ts.map +1 -1
  72. package/dist/cli/ralph.js +4 -0
  73. package/dist/cli/ralph.js.map +1 -1
  74. package/dist/cli/setup.d.ts.map +1 -1
  75. package/dist/cli/setup.js +3 -1
  76. package/dist/cli/setup.js.map +1 -1
  77. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +7 -0
  78. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -1
  79. package/dist/goal-workflows/codex-goal-snapshot.js +1 -1
  80. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
  81. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +12 -0
  82. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  83. package/dist/hooks/__tests__/keyword-detector.test.js +267 -7
  84. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  85. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  86. package/dist/hooks/keyword-detector.js +143 -30
  87. package/dist/hooks/keyword-detector.js.map +1 -1
  88. package/dist/hud/__tests__/reconcile.test.js +121 -0
  89. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  90. package/dist/hud/__tests__/state.test.js +141 -6
  91. package/dist/hud/__tests__/state.test.js.map +1 -1
  92. package/dist/hud/__tests__/tmux.test.js +1 -1
  93. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  94. package/dist/hud/reconcile.d.ts +3 -1
  95. package/dist/hud/reconcile.d.ts.map +1 -1
  96. package/dist/hud/reconcile.js +281 -151
  97. package/dist/hud/reconcile.js.map +1 -1
  98. package/dist/hud/render.d.ts.map +1 -1
  99. package/dist/hud/render.js +4 -2
  100. package/dist/hud/render.js.map +1 -1
  101. package/dist/hud/state.d.ts.map +1 -1
  102. package/dist/hud/state.js +51 -15
  103. package/dist/hud/state.js.map +1 -1
  104. package/dist/hud/tmux.d.ts.map +1 -1
  105. package/dist/hud/tmux.js +1 -4
  106. package/dist/hud/tmux.js.map +1 -1
  107. package/dist/hud/types.d.ts +5 -0
  108. package/dist/hud/types.d.ts.map +1 -1
  109. package/dist/hud/types.js.map +1 -1
  110. package/dist/leader/__tests__/contract.test.d.ts +2 -0
  111. package/dist/leader/__tests__/contract.test.d.ts.map +1 -0
  112. package/dist/leader/__tests__/contract.test.js +75 -0
  113. package/dist/leader/__tests__/contract.test.js.map +1 -0
  114. package/dist/leader/contract.d.ts +26 -0
  115. package/dist/leader/contract.d.ts.map +1 -0
  116. package/dist/leader/contract.js +97 -0
  117. package/dist/leader/contract.js.map +1 -0
  118. package/dist/mcp/__tests__/state-paths.test.js +32 -1
  119. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  120. package/dist/mcp/state-paths.d.ts +4 -0
  121. package/dist/mcp/state-paths.d.ts.map +1 -1
  122. package/dist/mcp/state-paths.js +6 -15
  123. package/dist/mcp/state-paths.js.map +1 -1
  124. package/dist/modes/base.d.ts.map +1 -1
  125. package/dist/modes/base.js +22 -7
  126. package/dist/modes/base.js.map +1 -1
  127. package/dist/notifications/__tests__/dispatcher.test.js +16 -0
  128. package/dist/notifications/__tests__/dispatcher.test.js.map +1 -1
  129. package/dist/notifications/__tests__/profiles.test.js +87 -0
  130. package/dist/notifications/__tests__/profiles.test.js.map +1 -1
  131. package/dist/notifications/config.d.ts +1 -0
  132. package/dist/notifications/config.d.ts.map +1 -1
  133. package/dist/notifications/config.js +60 -22
  134. package/dist/notifications/config.js.map +1 -1
  135. package/dist/notifications/dispatcher.d.ts.map +1 -1
  136. package/dist/notifications/dispatcher.js +6 -16
  137. package/dist/notifications/dispatcher.js.map +1 -1
  138. package/dist/pipeline/__tests__/stages.test.js +2 -0
  139. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  140. package/dist/pipeline/stages/ultragoal.d.ts.map +1 -1
  141. package/dist/pipeline/stages/ultragoal.js +6 -1
  142. package/dist/pipeline/stages/ultragoal.js.map +1 -1
  143. package/dist/question/__tests__/renderer.test.js +58 -4
  144. package/dist/question/__tests__/renderer.test.js.map +1 -1
  145. package/dist/question/renderer.d.ts +1 -1
  146. package/dist/question/renderer.d.ts.map +1 -1
  147. package/dist/question/renderer.js +73 -5
  148. package/dist/question/renderer.js.map +1 -1
  149. package/dist/ralplan/__tests__/consensus-gate.test.js +193 -0
  150. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  151. package/dist/ralplan/__tests__/runtime.test.js +202 -0
  152. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  153. package/dist/ralplan/consensus-gate.js +24 -3
  154. package/dist/ralplan/consensus-gate.js.map +1 -1
  155. package/dist/ralplan/runtime.d.ts +7 -0
  156. package/dist/ralplan/runtime.d.ts.map +1 -1
  157. package/dist/ralplan/runtime.js +63 -4
  158. package/dist/ralplan/runtime.js.map +1 -1
  159. package/dist/scripts/__tests__/codex-native-hook.test.js +6935 -2681
  160. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  161. package/dist/scripts/codex-native-hook.d.ts +1 -0
  162. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  163. package/dist/scripts/codex-native-hook.js +4442 -187
  164. package/dist/scripts/codex-native-hook.js.map +1 -1
  165. package/dist/scripts/generate-catalog-docs.js +1 -0
  166. package/dist/scripts/generate-catalog-docs.js.map +1 -1
  167. package/dist/state/__tests__/operations.test.js +1424 -0
  168. package/dist/state/__tests__/operations.test.js.map +1 -1
  169. package/dist/state/__tests__/planning-gate.test.js +410 -0
  170. package/dist/state/__tests__/planning-gate.test.js.map +1 -1
  171. package/dist/state/__tests__/skill-active.test.js +59 -0
  172. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  173. package/dist/state/operations.d.ts +10 -0
  174. package/dist/state/operations.d.ts.map +1 -1
  175. package/dist/state/operations.js +250 -14
  176. package/dist/state/operations.js.map +1 -1
  177. package/dist/state/skill-active.d.ts +4 -1
  178. package/dist/state/skill-active.d.ts.map +1 -1
  179. package/dist/state/skill-active.js +8 -6
  180. package/dist/state/skill-active.js.map +1 -1
  181. package/dist/state/workflow-transition.d.ts.map +1 -1
  182. package/dist/state/workflow-transition.js +642 -1
  183. package/dist/state/workflow-transition.js.map +1 -1
  184. package/dist/subagents/__tests__/tracker.test.js +248 -1
  185. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  186. package/dist/subagents/tracker.d.ts +60 -0
  187. package/dist/subagents/tracker.d.ts.map +1 -1
  188. package/dist/subagents/tracker.js +225 -1
  189. package/dist/subagents/tracker.js.map +1 -1
  190. package/dist/team/__tests__/model-contract.test.js +72 -4
  191. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  192. package/dist/team/__tests__/runtime.test.js +44 -2
  193. package/dist/team/__tests__/runtime.test.js.map +1 -1
  194. package/dist/team/__tests__/tmux-session.test.js +127 -8
  195. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  196. package/dist/team/__tests__/worker-runtime-identity.test.js +89 -0
  197. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  198. package/dist/team/model-contract.d.ts +3 -0
  199. package/dist/team/model-contract.d.ts.map +1 -1
  200. package/dist/team/model-contract.js +42 -5
  201. package/dist/team/model-contract.js.map +1 -1
  202. package/dist/team/runtime.d.ts +2 -1
  203. package/dist/team/runtime.d.ts.map +1 -1
  204. package/dist/team/runtime.js +21 -11
  205. package/dist/team/runtime.js.map +1 -1
  206. package/dist/team/scaling.d.ts.map +1 -1
  207. package/dist/team/scaling.js +13 -11
  208. package/dist/team/scaling.js.map +1 -1
  209. package/dist/team/tmux-session.d.ts +1 -0
  210. package/dist/team/tmux-session.d.ts.map +1 -1
  211. package/dist/team/tmux-session.js +63 -13
  212. package/dist/team/tmux-session.js.map +1 -1
  213. package/dist/ultragoal/__tests__/artifacts.test.js +21 -4
  214. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  215. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  216. package/dist/ultragoal/artifacts.js +75 -5
  217. package/dist/ultragoal/artifacts.js.map +1 -1
  218. package/dist/utils/__tests__/agents-model-table.test.js +2 -2
  219. package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
  220. package/package.json +1 -1
  221. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  222. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +19 -7
  223. package/plugins/oh-my-codex/skills/code-review/SKILL.md +16 -0
  224. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +23 -0
  225. package/plugins/oh-my-codex/skills/ultrawork/references/agent-tiers.md +3 -2
  226. package/skills/code-review/SKILL.md +16 -0
  227. package/skills/ecomode/references/agent-tiers.md +1 -1
  228. package/skills/ultragoal/SKILL.md +23 -0
  229. package/skills/ultrawork/references/agent-tiers.md +3 -2
  230. package/src/scripts/__tests__/codex-native-hook.test.ts +14843 -9586
  231. package/src/scripts/codex-native-hook.ts +4520 -194
  232. package/src/scripts/generate-catalog-docs.ts +1 -0
package/dist/cli/index.js CHANGED
@@ -5,7 +5,7 @@
5
5
  import { execFileSync, spawn } from "child_process";
6
6
  import { basename, dirname, join, posix, resolve, win32 } from "path";
7
7
  import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, rmSync, statSync, writeFileSync } from "fs";
8
- import { copyFile, cp, lstat, mkdir, readFile, readdir, rm, symlink, writeFile } from "fs/promises";
8
+ import { copyFile, cp, lstat, mkdir, readFile, readdir, rm, stat, symlink, writeFile } from "fs/promises";
9
9
  import { constants as osConstants, homedir } from "os";
10
10
  import { createHash } from "crypto";
11
11
  import { setup, SETUP_MCP_MODES, SETUP_SCOPES, SETUP_TEAM_MODES, } from "./setup.js";
@@ -37,12 +37,14 @@ import { mcpServeCommand } from "./mcp-serve.js";
37
37
  import { adaptCommand } from "./adapt.js";
38
38
  import { listCommand } from "./list.js";
39
39
  import { authCommand } from "./auth.js";
40
+ import { missionCommand } from "./mission.js";
40
41
  import { runAuthHotswap } from "../auth/hotswap.js";
41
42
  import { MADMAX_FLAG, CODEX_BYPASS_FLAG, HIGH_REASONING_FLAG, XHIGH_REASONING_FLAG, SPARK_FLAG, MADMAX_SPARK_FLAG, CONFIG_FLAG, LONG_CONFIG_FLAG, } from "./constants.js";
42
43
  import { getBaseStateDir, getStateDir, listModeStateFilesWithScopePreference, } from "../mcp/state-paths.js";
43
44
  import { evaluateRalphCompletionAuditEvidence, isRalphCompletePhase } from "../ralph/completion-audit.js";
44
45
  import { readPersistedSetupPreferences, resolveCodexConfigPathForLaunch, resolveCodexHomeForLaunch, resolveProjectLocalCodexHomeForLaunch, } from "./codex-home.js";
45
46
  import { discoverProjectRuntimeCodexHomes } from "./project-runtime-codex-homes.js";
47
+ import { materializePackagedOmxPluginCache, packagedOmxPluginVersion, resolvePackagedOmxMarketplace, upsertLocalOmxMarketplaceRegistration, upsertLocalOmxPluginEnablement, } from "./plugin-marketplace.js";
46
48
  import { escapeTomlString, readTopLevelTomlString, upsertTopLevelTomlString } from "../utils/toml.js";
47
49
  export { readPersistedSetupPreferences, readPersistedSetupScope, resolveCodexConfigPathForLaunch, resolveCodexHomeForLaunch, resolveProjectLocalCodexHomeForLaunch, } from "./codex-home.js";
48
50
  import { SKILL_ACTIVE_STATE_MODE, extractSessionIdFromInitializedStatePath, getSkillActiveStatePathsForStateDir, listActiveSkills, readSkillActiveState, syncCanonicalSkillStateForMode, } from "../state/skill-active.js";
@@ -65,7 +67,7 @@ rememberOmxLaunchContext({ argv1: process.argv[1], cwd: process.cwd(), env: proc
65
67
  import { classifySpawnError, resolveTmuxBinaryForPlatform, spawnPlatformCommandSync, } from "../utils/platform-command.js";
66
68
  import { buildHookEvent } from "../hooks/extensibility/events.js";
67
69
  import { dispatchHookEvent } from "../hooks/extensibility/dispatcher.js";
68
- import { collectInheritableTeamWorkerArgs as collectInheritableTeamWorkerArgsShared, resolveTeamWorkerLaunchArgs, resolveTeamLowComplexityDefaultModel, } from "../team/model-contract.js";
70
+ import { collectInheritableTeamWorkerArgs as collectInheritableTeamWorkerArgsShared, parseTeamWorkerLaunchArgs, resolveTeamWorkerLaunchArgs, resolveTeamLowComplexityDefaultModel, TEAM_WORKER_INHERITED_MODEL_ENV, } from "../team/model-contract.js";
69
71
  import { parseWorktreeMode, planWorktreeTarget, ensureWorktree, } from "../team/worktree.js";
70
72
  import { ensureReusableNodeModules } from "../utils/repo-deps.js";
71
73
  import { OMX_NOTIFY_TEMP_CONTRACT_ENV, parseNotifyTempContractFromArgs, serializeNotifyTempContract, } from "../notifications/temp-contract.js";
@@ -91,6 +93,8 @@ Usage:
91
93
  omx exec Run codex exec non-interactively with OMX AGENTS/overlay injection
92
94
  omx exec inject <session-id> --prompt <text>
93
95
  Queue audited follow-up instructions for a running non-interactive exec job
96
+ omx mission <file>
97
+ Run a prompt/checklist file sequentially through omx exec with durable summary
94
98
  omx imagegen continuation <session-id> --artifact <name>
95
99
  Queue a Stop-hook continuation for built-in image generation turns
96
100
  omx setup Install skills, prompts, CLI-first config, and scope-specific AGENTS.md
@@ -260,6 +264,7 @@ const NESTED_HELP_COMMANDS = new Set([
260
264
  "agents-init",
261
265
  "deepinit",
262
266
  "exec",
267
+ "mission",
263
268
  "imagegen",
264
269
  "hooks",
265
270
  "list",
@@ -667,26 +672,43 @@ async function materializeProjectLaunchRuntimeHistoryEntries(runtimeCodexHome, s
667
672
  await copyFile(source, destination);
668
673
  }
669
674
  }
670
- async function mergeProjectLaunchRuntimeHistoryEntries(runtimeCodexHome, sourceCodexHome) {
675
+ async function mergeProjectLaunchRuntimeHistoryEntries(runtimeCodexHome, sourceCodexHome, mergedHistorySourceRealpaths) {
671
676
  for (const entryName of PROJECT_LAUNCH_DURABLE_HISTORY_ENTRY_NAMES) {
672
677
  const source = join(sourceCodexHome, entryName);
673
678
  if (!existsSync(source))
674
679
  continue;
680
+ const sourceRealpath = realpathSync(source);
681
+ if (mergedHistorySourceRealpaths.has(sourceRealpath))
682
+ continue;
675
683
  const destination = join(runtimeCodexHome, entryName);
676
- const sourceStat = await lstat(source);
684
+ const sourceStat = await stat(source);
677
685
  if (sourceStat.isDirectory()) {
678
686
  await mkdir(destination, { recursive: true });
679
687
  await cp(source, destination, { recursive: true, force: true, dereference: true });
688
+ mergedHistorySourceRealpaths.add(sourceRealpath);
680
689
  continue;
681
690
  }
691
+ if (entryName === "sessions")
692
+ continue;
693
+ if (!sourceStat.isFile())
694
+ continue;
682
695
  if (existsSync(destination)) {
696
+ const destinationStat = await stat(destination);
697
+ if (!destinationStat.isFile()) {
698
+ await rm(destination, { recursive: true, force: true });
699
+ await copyFile(source, destination);
700
+ mergedHistorySourceRealpaths.add(sourceRealpath);
701
+ continue;
702
+ }
683
703
  const existing = await readFile(destination, "utf-8").catch(() => "");
684
704
  const addition = await readFile(source, "utf-8");
685
705
  const separator = existing === "" || existing.endsWith("\n") || addition === "" ? "" : "\n";
686
706
  await writeFile(destination, `${existing}${separator}${addition}`, "utf-8");
707
+ mergedHistorySourceRealpaths.add(sourceRealpath);
687
708
  continue;
688
709
  }
689
710
  await copyFile(source, destination);
711
+ mergedHistorySourceRealpaths.add(sourceRealpath);
690
712
  }
691
713
  }
692
714
  export async function persistProjectLaunchRuntimeAuthState(runtimeCodexHome, projectCodexHome) {
@@ -730,9 +752,15 @@ export async function prepareRuntimeCodexHomeForProjectLaunch(cwd, sessionId, pr
730
752
  }
731
753
  await ensureProjectLaunchRuntimeHistoryLinks(runtimeCodexHome, projectCodexHome);
732
754
  if (options.includeHistoryArtifacts === true && (options.extraHistoryCodexHomes?.length ?? 0) > 0) {
755
+ const mergedHistorySourceRealpaths = new Set();
756
+ for (const entryName of PROJECT_LAUNCH_DURABLE_HISTORY_ENTRY_NAMES) {
757
+ const source = join(projectCodexHome, entryName);
758
+ if (existsSync(source))
759
+ mergedHistorySourceRealpaths.add(realpathSync(source));
760
+ }
733
761
  await materializeProjectLaunchRuntimeHistoryEntries(runtimeCodexHome, projectCodexHome);
734
762
  for (const extraCodexHome of options.extraHistoryCodexHomes ?? []) {
735
- await mergeProjectLaunchRuntimeHistoryEntries(runtimeCodexHome, extraCodexHome);
763
+ await mergeProjectLaunchRuntimeHistoryEntries(runtimeCodexHome, extraCodexHome, mergedHistorySourceRealpaths);
736
764
  }
737
765
  }
738
766
  return runtimeCodexHome;
@@ -793,13 +821,46 @@ export function parseResumeCodexHomeSelection(args) {
793
821
  projectOnly,
794
822
  };
795
823
  }
824
+ export async function preflightResumeOmxPluginState(codexHomeDir, pkgRoot = getPackageRoot()) {
825
+ const selectedCodexHomeDir = codexHomeDir && codexHomeDir.trim() !== ""
826
+ ? codexHomeDir
827
+ : join(homedir(), ".codex");
828
+ const packagedMarketplace = await resolvePackagedOmxMarketplace(pkgRoot);
829
+ if (!packagedMarketplace) {
830
+ return { status: "unavailable", prunedStaleDirs: [], configUpdated: false };
831
+ }
832
+ const materialized = await materializePackagedOmxPluginCache(selectedCodexHomeDir, packagedMarketplace);
833
+ const version = materialized.version ?? (await packagedOmxPluginVersion(packagedMarketplace)) ?? undefined;
834
+ const currentCacheDir = materialized.cacheDir ?? (version ? join(selectedCodexHomeDir, "plugins", "cache", "oh-my-codex-local", "oh-my-codex", version) : undefined);
835
+ const prunedStaleDirs = [];
836
+ const configPath = join(selectedCodexHomeDir, "config.toml");
837
+ const existingConfig = existsSync(configPath) ? await readFile(configPath, "utf-8") : "";
838
+ const nextConfig = upsertLocalOmxMarketplaceRegistration(upsertLocalOmxPluginEnablement(existingConfig), pkgRoot);
839
+ const configUpdated = nextConfig !== existingConfig;
840
+ if (configUpdated) {
841
+ await mkdir(dirname(configPath), { recursive: true });
842
+ await writeFile(configPath, nextConfig, "utf-8");
843
+ }
844
+ return {
845
+ status: "prepared",
846
+ version,
847
+ cacheDir: currentCacheDir,
848
+ prunedStaleDirs,
849
+ configUpdated,
850
+ };
851
+ }
852
+ function isResumeCodexLaunch(args) {
853
+ return args.includes("resume");
854
+ }
796
855
  async function prepareResumeCodexHomeForLaunch(cwd, sessionId, args, env = process.env) {
797
856
  const selection = parseResumeCodexHomeSelection(args);
798
857
  if (selection.explicitCodexHome) {
858
+ const codexHomeOverride = resolve(selection.explicitCodexHome);
859
+ await preflightResumeOmxPluginState(codexHomeOverride);
799
860
  return {
800
861
  args: selection.args,
801
862
  prepared: {
802
- codexHomeOverride: resolve(selection.explicitCodexHome),
863
+ codexHomeOverride,
803
864
  },
804
865
  };
805
866
  }
@@ -809,6 +870,7 @@ async function prepareResumeCodexHomeForLaunch(cwd, sessionId, args, env = proce
809
870
  const emptyRuntimeCodexHome = runtimeCodexHomePath(cwd, sessionId);
810
871
  await rm(emptyRuntimeCodexHome, { recursive: true, force: true });
811
872
  await mkdir(join(emptyRuntimeCodexHome, "sessions"), { recursive: true });
873
+ await preflightResumeOmxPluginState(emptyRuntimeCodexHome);
812
874
  return {
813
875
  args: selection.args,
814
876
  prepared: {
@@ -821,6 +883,7 @@ async function prepareResumeCodexHomeForLaunch(cwd, sessionId, args, env = proce
821
883
  includeHistoryArtifacts: true,
822
884
  extraHistoryCodexHomes: projectHomes.slice(1).map((home) => home.path),
823
885
  });
886
+ await preflightResumeOmxPluginState(runtimeCodexHome);
824
887
  return {
825
888
  args: selection.args,
826
889
  prepared: {
@@ -832,6 +895,7 @@ async function prepareResumeCodexHomeForLaunch(cwd, sessionId, args, env = proce
832
895
  includeHistoryArtifacts: true,
833
896
  extraHistoryCodexHomes: projectHomes.map((home) => home.path),
834
897
  });
898
+ await preflightResumeOmxPluginState(prepared.codexHomeOverride);
835
899
  return { args: selection.args, prepared };
836
900
  }
837
901
  export async function persistProjectLaunchRuntimeProjectTrustState(runtimeCodexHome, projectCodexHome) {
@@ -1362,20 +1426,88 @@ export function resolveDisposableWorktreeOmxRootForLaunch(ensuredWorktree, env =
1362
1426
  return undefined;
1363
1427
  return ensuredWorktree.repoRoot;
1364
1428
  }
1429
+ function buildMadmaxWorktreeRuntimeEnvOverlay(runtimeContext) {
1430
+ if (!runtimeContext)
1431
+ return {};
1432
+ return {
1433
+ OMX_ROOT: runtimeContext.omxRoot,
1434
+ ...(runtimeContext.omxStateRoot ? { OMX_STATE_ROOT: runtimeContext.omxStateRoot } : {}),
1435
+ ...(runtimeContext.boxedActive ? { OMXBOX_ACTIVE: "1" } : {}),
1436
+ OMX_SOURCE_CWD: runtimeContext.sourceCwd,
1437
+ ...(runtimeContext.madmaxDetachedContext
1438
+ ? { [OMX_MADMAX_DETACHED_CONTEXT_ENV]: runtimeContext.madmaxDetachedContext }
1439
+ : {}),
1440
+ };
1441
+ }
1442
+ export function captureMadmaxWorktreeRuntimeContext(options) {
1443
+ const env = options.env ?? process.env;
1444
+ if (!options.worktreeEnabled)
1445
+ return undefined;
1446
+ if (!launchArgsRequestMadmaxIsolation(options.originalLaunchArgs))
1447
+ return undefined;
1448
+ if (env.OMXBOX_ACTIVE !== "1")
1449
+ return undefined;
1450
+ const inheritedRoot = resolveInheritedMadmaxRoot(env);
1451
+ if (!inheritedRoot)
1452
+ return undefined;
1453
+ const sourceCwd = env.OMX_SOURCE_CWD?.trim() || options.sourceCwd;
1454
+ const worktreeCwd = options.worktreeCwd?.trim();
1455
+ const omxStateRoot = env.OMX_STATE_ROOT?.trim();
1456
+ const madmaxDetachedContext = env[OMX_MADMAX_DETACHED_CONTEXT_ENV]?.trim();
1457
+ return {
1458
+ omxRoot: resolveLaunchPath(options.sourceCwd, inheritedRoot),
1459
+ ...(omxStateRoot ? { omxStateRoot: resolveLaunchPath(options.sourceCwd, omxStateRoot) } : {}),
1460
+ sourceCwd,
1461
+ ...(worktreeCwd && worktreeCwd !== sourceCwd ? { worktreeCwd } : {}),
1462
+ ...(madmaxDetachedContext ? { madmaxDetachedContext } : {}),
1463
+ boxedActive: true,
1464
+ };
1465
+ }
1365
1466
  function applyDisposableWorktreeOmxRootForLaunch(ensuredWorktree, env = process.env) {
1366
1467
  const omxRootOverride = resolveDisposableWorktreeOmxRootForLaunch(ensuredWorktree, env);
1367
1468
  if (!omxRootOverride)
1368
1469
  return;
1369
1470
  env.OMX_ROOT = omxRootOverride;
1370
1471
  }
1371
- export function shouldAutoIsolateMadmaxLaunch(command, launchArgs, env = process.env) {
1472
+ function launchArgRequestsDisposableWorktree(arg) {
1473
+ return arg === "--worktree" ||
1474
+ arg === "-w" ||
1475
+ arg.startsWith("--worktree=") ||
1476
+ // Covers both `-w=<name>` and `-w<name>`; an explicit `-w=` check would be a
1477
+ // strict subset of this clause, so it is omitted as redundant.
1478
+ (arg.startsWith("-w") && arg.length > 2);
1479
+ }
1480
+ function launchArgsRequestMadmaxIsolation(launchArgs) {
1481
+ return launchArgs.some((arg) => arg === MADMAX_FLAG || arg === MADMAX_SPARK_FLAG);
1482
+ }
1483
+ function launchArgsRequestDisposableWorktree(launchArgs) {
1484
+ return launchArgs.some((arg) => launchArgRequestsDisposableWorktree(arg));
1485
+ }
1486
+ function clearInheritedMadmaxRootForDisposableWorktreeLaunch(launchArgs, env = process.env) {
1487
+ if (!launchArgsRequestDisposableWorktree(launchArgs))
1488
+ return;
1489
+ if (env.OMXBOX_ACTIVE !== "1")
1490
+ return;
1491
+ delete env.OMX_ROOT;
1492
+ delete env.OMX_STATE_ROOT;
1493
+ delete env.OMXBOX_ACTIVE;
1494
+ delete env.OMX_SOURCE_CWD;
1495
+ delete env[OMX_MADMAX_DETACHED_CONTEXT_ENV];
1496
+ }
1497
+ export function shouldAutoIsolateMadmaxLaunch(command, launchArgs, env = process.env, cwd = process.cwd()) {
1372
1498
  if (command !== "launch" && command !== "exec")
1373
1499
  return false;
1374
- if (env.OMX_NO_BOX === "1" || env.OMXBOX_ACTIVE === "1")
1500
+ if (env.OMX_NO_BOX === "1")
1375
1501
  return false;
1376
- if (env.OMX_ROOT || env.OMX_STATE_ROOT)
1502
+ if (!launchArgsRequestMadmaxIsolation(launchArgs))
1377
1503
  return false;
1378
- return launchArgs.some((arg) => arg === MADMAX_FLAG || arg === MADMAX_SPARK_FLAG);
1504
+ const inheritedContext = env[OMX_MADMAX_DETACHED_CONTEXT_ENV]?.trim();
1505
+ if (env.OMXBOX_ACTIVE === "1" && inheritedContext && !resolveInheritedMadmaxRoot(env)) {
1506
+ return false;
1507
+ }
1508
+ if (madmaxInheritedContextMatchesLaunch(cwd, launchArgs, env))
1509
+ return false;
1510
+ return true;
1379
1511
  }
1380
1512
  function sanitizeRunIdSegment(value) {
1381
1513
  return value.replace(/[^a-zA-Z0-9._-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
@@ -1474,6 +1606,7 @@ function readMadmaxDetachedActiveRecord(recordPath) {
1474
1606
  tmux_session_name: parsed.tmux_session_name,
1475
1607
  ...(typeof parsed.session_id === "string" ? { session_id: parsed.session_id } : {}),
1476
1608
  ...(typeof parsed.tmux_pane_id === "string" ? { tmux_pane_id: parsed.tmux_pane_id } : {}),
1609
+ ...(typeof parsed.worktree_cwd === "string" ? { worktree_cwd: parsed.worktree_cwd } : {}),
1477
1610
  };
1478
1611
  }
1479
1612
  catch {
@@ -1607,6 +1740,43 @@ export function withMadmaxDetachedContextLock(runsRoot, contextKey, run, options
1607
1740
  }
1608
1741
  throw new MadmaxDetachedGuardError(`timed out waiting for madmax detached launch context lock: ${lockPath} (${lastDiagnostic})`);
1609
1742
  }
1743
+ function readMadmaxRunMetadata(runRoot) {
1744
+ try {
1745
+ const parsed = JSON.parse(readFileSync(join(runRoot, ".omxbox-run.json"), "utf-8"));
1746
+ return {
1747
+ ...(typeof parsed.cwd === "string" ? { cwd: parsed.cwd } : {}),
1748
+ ...(typeof parsed.detached_launch_context === "string"
1749
+ ? { detached_launch_context: parsed.detached_launch_context }
1750
+ : {}),
1751
+ };
1752
+ }
1753
+ catch {
1754
+ return null;
1755
+ }
1756
+ }
1757
+ function resolveInheritedMadmaxRoot(env) {
1758
+ const root = env.OMX_ROOT?.trim() || env.OMX_STATE_ROOT?.trim();
1759
+ return root || undefined;
1760
+ }
1761
+ function madmaxInheritedContextMatchesLaunch(cwd, launchArgs, env) {
1762
+ if (env.OMXBOX_ACTIVE !== "1")
1763
+ return false;
1764
+ const context = env[OMX_MADMAX_DETACHED_CONTEXT_ENV]?.trim();
1765
+ if (!context)
1766
+ return false;
1767
+ const inheritedRoot = resolveInheritedMadmaxRoot(env);
1768
+ if (!inheritedRoot)
1769
+ return false;
1770
+ const metadata = readMadmaxRunMetadata(inheritedRoot);
1771
+ if (!metadata)
1772
+ return false;
1773
+ if (metadata.cwd && metadata.cwd !== inheritedRoot)
1774
+ return false;
1775
+ if (metadata.detached_launch_context !== context)
1776
+ return false;
1777
+ const expectedContext = buildMadmaxDetachedLaunchContextKey(cwd, [...launchArgs], inheritedRoot);
1778
+ return expectedContext === context;
1779
+ }
1610
1780
  function isMadmaxDetachedGuardEnabled(env) {
1611
1781
  return env.OMXBOX_ACTIVE === "1" && typeof env[OMX_MADMAX_DETACHED_CONTEXT_ENV] === "string";
1612
1782
  }
@@ -1651,7 +1821,7 @@ export function createMadmaxIsolatedRoot(sourceCwd, argv, env = process.env) {
1651
1821
  return runDir;
1652
1822
  }
1653
1823
  function activateMadmaxIsolationIfNeeded(command, launchArgs, cwd, env = process.env) {
1654
- if (!shouldAutoIsolateMadmaxLaunch(command, launchArgs, env))
1824
+ if (!shouldAutoIsolateMadmaxLaunch(command, launchArgs, env, cwd))
1655
1825
  return;
1656
1826
  const runDir = createMadmaxIsolatedRoot(cwd, launchArgs, env);
1657
1827
  env.OMX_ROOT = runDir;
@@ -1663,6 +1833,7 @@ export async function main(args) {
1663
1833
  const knownCommands = new Set([
1664
1834
  "launch",
1665
1835
  "exec",
1836
+ "mission",
1666
1837
  "imagegen",
1667
1838
  "setup",
1668
1839
  "update",
@@ -1804,6 +1975,18 @@ export async function main(args) {
1804
1975
  await execWithOverlay(launchArgs);
1805
1976
  }
1806
1977
  break;
1978
+ case "mission":
1979
+ await missionCommand(args.slice(1), {
1980
+ runTask: async (prompt, codexArgs) => {
1981
+ const priorExitCode = process.exitCode;
1982
+ process.exitCode = undefined;
1983
+ await execWithOverlay([...codexArgs, prompt]);
1984
+ const exitCode = typeof process.exitCode === "number" ? process.exitCode : 0;
1985
+ process.exitCode = priorExitCode;
1986
+ return exitCode;
1987
+ },
1988
+ });
1989
+ break;
1807
1990
  case "imagegen":
1808
1991
  await imagegenCommand(args.slice(1));
1809
1992
  break;
@@ -1926,6 +2109,11 @@ async function readStaleCurrentAutopilotStatus(cwd) {
1926
2109
  return null;
1927
2110
  return { phase: phase ?? "active" };
1928
2111
  }
2112
+ function formatDurableUltragoalStatusForCli(status) {
2113
+ return status === "failed"
2114
+ ? "ultragoal: FAILED (phase: failed)"
2115
+ : `ultragoal: ACTIVE (phase: ${status})`;
2116
+ }
1929
2117
  async function showStatus() {
1930
2118
  const { readFile } = await import("fs/promises");
1931
2119
  const cwd = process.cwd();
@@ -1962,7 +2150,7 @@ async function showStatus() {
1962
2150
  const ultragoalState = await readUltragoalState(cwd).catch(() => null);
1963
2151
  if (states.length === 0) {
1964
2152
  if (ultragoalState?.active) {
1965
- console.log(`ultragoal: ACTIVE (phase: ${ultragoalState.status})`);
2153
+ console.log(formatDurableUltragoalStatusForCli(ultragoalState.status ?? "active"));
1966
2154
  return;
1967
2155
  }
1968
2156
  const staleAutopilot = await readStaleCurrentAutopilotStatus(cwd);
@@ -1973,6 +2161,7 @@ async function showStatus() {
1973
2161
  console.log("No active modes.");
1974
2162
  return;
1975
2163
  }
2164
+ let hasAuthoritativeActiveUltragoalMode = false;
1976
2165
  for (const path of states) {
1977
2166
  const content = await readFile(path, "utf-8");
1978
2167
  let state;
@@ -1985,12 +2174,15 @@ async function showStatus() {
1985
2174
  }
1986
2175
  const file = basename(path);
1987
2176
  const mode = file.replace("-state.json", "");
1988
- if (mode === "ultragoal" && ultragoalState?.active)
2177
+ if (mode === "ultragoal" && state.active === true) {
2178
+ hasAuthoritativeActiveUltragoalMode = true;
2179
+ }
2180
+ if (mode === "ultragoal" && ultragoalState?.active && state.active !== true)
1989
2181
  continue;
1990
2182
  console.log(`${mode}: ${state.active === true ? "ACTIVE" : "inactive"} (phase: ${String(state.current_phase || "n/a")})`);
1991
2183
  }
1992
- if (ultragoalState?.active) {
1993
- console.log(`ultragoal: ACTIVE (phase: ${ultragoalState.status})`);
2184
+ if (ultragoalState?.active && !hasAuthoritativeActiveUltragoalMode) {
2185
+ console.log(formatDurableUltragoalStatusForCli(ultragoalState.status ?? "active"));
1994
2186
  }
1995
2187
  if (!hasAuthoritativeActiveMode && !ultragoalState?.active) {
1996
2188
  const staleAutopilot = await readStaleCurrentAutopilotStatus(cwd);
@@ -2066,6 +2258,7 @@ export async function launchWithAuthHotswap(args) {
2066
2258
  }
2067
2259
  }
2068
2260
  }
2261
+ clearInheritedMadmaxRootForDisposableWorktreeLaunch(parsedWorktree.remainingArgs);
2069
2262
  applyDisposableWorktreeOmxRootForLaunch(ensuredLaunchWorktree);
2070
2263
  try {
2071
2264
  await maybeCheckAndPromptUpdate(cwd);
@@ -2165,6 +2358,14 @@ export async function launchWithHud(args) {
2165
2358
  }
2166
2359
  }
2167
2360
  }
2361
+ const madmaxWorktreeRuntimeContext = captureMadmaxWorktreeRuntimeContext({
2362
+ originalLaunchArgs: args,
2363
+ worktreeEnabled: Boolean(parsedWorktree.mode.enabled && ensuredLaunchWorktree?.enabled),
2364
+ sourceCwd: launchCwd,
2365
+ worktreeCwd: ensuredLaunchWorktree?.enabled ? ensuredLaunchWorktree.worktreePath : undefined,
2366
+ env: process.env,
2367
+ });
2368
+ clearInheritedMadmaxRootForDisposableWorktreeLaunch(parsedWorktree.remainingArgs);
2168
2369
  applyDisposableWorktreeOmxRootForLaunch(ensuredLaunchWorktree);
2169
2370
  const sessionId = `omx-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
2170
2371
  try {
@@ -2195,14 +2396,14 @@ export async function launchWithHud(args) {
2195
2396
  catch {
2196
2397
  // Non-fatal: repair failure must not block launch
2197
2398
  }
2198
- const resumePrepared = normalizedArgs[0] === "resume"
2399
+ const resumePrepared = isResumeCodexLaunch(normalizedArgs)
2199
2400
  ? await prepareResumeCodexHomeForLaunch(launchCwd, sessionId, normalizedArgs, process.env)
2200
2401
  : null;
2201
2402
  if (resumePrepared) {
2202
2403
  normalizedArgs = resumePrepared.args;
2203
2404
  }
2204
2405
  const preparedCodexHome = resumePrepared?.prepared ?? await prepareCodexHomeForLaunch(launchCwd, sessionId, process.env, {
2205
- includeHistoryArtifacts: normalizedArgs[0] === "resume",
2406
+ includeHistoryArtifacts: isResumeCodexLaunch(normalizedArgs),
2206
2407
  });
2207
2408
  const codexHomeOverride = preparedCodexHome.codexHomeOverride;
2208
2409
  const sqliteHomeOverride = preparedCodexHome.sqliteHomeOverride;
@@ -2221,7 +2422,7 @@ export async function launchWithHud(args) {
2221
2422
  const notifyTempContractRaw = notifyTempResult.contract.active
2222
2423
  ? serializeNotifyTempContract(notifyTempResult.contract)
2223
2424
  : null;
2224
- const launchResult = runCodex(cwd, normalizedArgs, sessionId, workerSparkModel, codexHomeOverride, sqliteHomeOverride, notifyTempContractRaw, effectiveExplicitLaunchPolicy, projectLocalCodexHomeForCleanup, preparedCodexHome.runtimeCodexHomeForCleanup);
2425
+ const launchResult = runCodex(cwd, normalizedArgs, sessionId, workerSparkModel, codexHomeOverride, sqliteHomeOverride, notifyTempContractRaw, effectiveExplicitLaunchPolicy, projectLocalCodexHomeForCleanup, preparedCodexHome.runtimeCodexHomeForCleanup, madmaxWorktreeRuntimeContext);
2225
2426
  postLaunchHandledExternally = launchResult.postLaunchHandledExternally;
2226
2427
  }
2227
2428
  finally {
@@ -2264,6 +2465,7 @@ export async function execWithOverlay(args) {
2264
2465
  }
2265
2466
  }
2266
2467
  }
2468
+ clearInheritedMadmaxRootForDisposableWorktreeLaunch(parsedWorktree.remainingArgs);
2267
2469
  applyDisposableWorktreeOmxRootForLaunch(ensuredLaunchWorktree);
2268
2470
  const sessionId = `omx-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
2269
2471
  try {
@@ -2982,7 +3184,7 @@ export function withTmuxExtendedKeys(cwd, run, execFileSyncImpl = (file, tmuxArg
2982
3184
  releaseTmuxExtendedKeysLease(cwd, leaseHandle, execFileSyncImpl);
2983
3185
  }
2984
3186
  }
2985
- export function buildDetachedSessionBootstrapSteps(sessionName, cwd, codexCmd, hudCmd, workerLaunchArgs, codexHomeOverride, notifyTempContractRaw, nativeWindows = false, sessionId, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, omxRootOverride, env = process.env, sqliteHomeOverride, parentEnvFilePath) {
3187
+ export function buildDetachedSessionBootstrapSteps(sessionName, cwd, codexCmd, hudCmd, workerLaunchArgs, codexHomeOverride, notifyTempContractRaw, nativeWindows = false, sessionId, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, omxRootOverride, env = process.env, sqliteHomeOverride, parentEnvFilePath, inheritedWorkerModel) {
2986
3188
  const detachedLeaderCmd = nativeWindows
2987
3189
  ? "powershell.exe"
2988
3190
  : buildDetachedSessionLeaderCommand(cwd, sessionName, codexCmd, sessionId, codexHomeOverride, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, parentEnvFilePath);
@@ -3013,17 +3215,19 @@ export function buildDetachedSessionBootstrapSteps(sessionName, cwd, codexCmd, h
3013
3215
  sessionName,
3014
3216
  "-c",
3015
3217
  cwd,
3016
- ...(workerLaunchArgs
3017
- ? ["-e", `${TEAM_WORKER_LAUNCH_ARGS_ENV}=${workerLaunchArgs}`]
3018
- : []),
3218
+ ...(workerLaunchArgs ? ["-e", `${TEAM_WORKER_LAUNCH_ARGS_ENV}=${workerLaunchArgs}`] : []),
3019
3219
  ...Object.entries(hudRuntimeEnv).map(([key, value]) => ["-e", `${key}=${value}`]).flat(),
3020
3220
  ...(codexHomeOverride ? ["-e", `CODEX_HOME=${codexHomeOverride}`] : []),
3021
3221
  ...(sqliteHomeOverride ? ["-e", `${CODEX_SQLITE_HOME_ENV}=${sqliteHomeOverride}`] : []),
3022
3222
  ...(env.OMXBOX_ACTIVE ? ["-e", `OMXBOX_ACTIVE=${env.OMXBOX_ACTIVE}`] : []),
3023
3223
  ...(env.OMX_SOURCE_CWD ? ["-e", `OMX_SOURCE_CWD=${env.OMX_SOURCE_CWD}`] : []),
3224
+ ...(env[OMX_MADMAX_DETACHED_CONTEXT_ENV]
3225
+ ? ["-e", `${OMX_MADMAX_DETACHED_CONTEXT_ENV}=${env[OMX_MADMAX_DETACHED_CONTEXT_ENV]}`]
3226
+ : []),
3024
3227
  ...(notifyTempContractRaw
3025
3228
  ? ["-e", `${OMX_NOTIFY_TEMP_CONTRACT_ENV}=${notifyTempContractRaw}`]
3026
3229
  : []),
3230
+ ...(inheritedWorkerModel ? ["-e", `${TEAM_WORKER_INHERITED_MODEL_ENV}=${inheritedWorkerModel}`] : []),
3027
3231
  detachedLeaderCmd,
3028
3232
  ];
3029
3233
  const splitCaptureArgs = [
@@ -3606,42 +3810,59 @@ ${launchAppendix}${dirtyWorktreeGuidance}`
3606
3810
  * runCodex: Launch Codex CLI (blocks until exit).
3607
3811
  * All 3 paths (new tmux, existing tmux, no tmux) block via execSync/execFileSync.
3608
3812
  */
3609
- function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, sqliteHomeOverride, notifyTempContractRaw, explicitLaunchPolicy, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup) {
3813
+ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, sqliteHomeOverride, notifyTempContractRaw, explicitLaunchPolicy, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, runtimeContext) {
3610
3814
  const launchArgs = injectModelInstructionsBypassArgs(cwd, args, process.env, sessionModelInstructionsPath(cwd, sessionId));
3611
3815
  const nativeWindows = isNativeWindows();
3612
3816
  const omxBin = resolveOmxCliEntryPath({ argv1: process.argv[1], cwd, env: process.env });
3613
3817
  if (!omxBin) {
3614
3818
  throw new Error("Unable to resolve OMX launcher path for tmux HUD bootstrap");
3615
3819
  }
3616
- const omxRootOverride = resolveOmxRootForLaunch(cwd, process.env);
3820
+ const runtimeEnvOverlay = buildMadmaxWorktreeRuntimeEnvOverlay(runtimeContext);
3821
+ const omxRootOverride = runtimeContext?.omxRoot ?? resolveOmxRootForLaunch(cwd, process.env);
3617
3822
  const currentPaneId = process.env.TMUX_PANE;
3618
- const hudRuntimeRoot = resolveHudRuntimeRootForLaunch(cwd, process.env);
3619
- const hudEnvArgs = Object.entries(buildHudRuntimeEnv({
3620
- sessionId,
3621
- leaderPaneId: currentPaneId,
3622
- ...hudRuntimeRoot,
3623
- }).env).map(([key, value]) => `${key}=${value}`);
3823
+ const hudRuntimeRoot = runtimeContext
3824
+ ? { omxRoot: runtimeContext.omxRoot, rootSource: 'omx-root-env' }
3825
+ : resolveHudRuntimeRootForLaunch(cwd, process.env);
3826
+ const hudRuntimeEnv = {
3827
+ ...buildHudRuntimeEnv({
3828
+ sessionId,
3829
+ leaderPaneId: currentPaneId,
3830
+ ...hudRuntimeRoot,
3831
+ }).env,
3832
+ ...runtimeEnvOverlay,
3833
+ };
3834
+ const hudEnvArgs = Object.entries(hudRuntimeEnv).map(([key, value]) => `${key}=${value}`);
3624
3835
  const hudCmd = nativeWindows
3625
3836
  ? buildWindowsPromptCommand("node", [omxBin, "hud", "--watch"])
3626
3837
  : buildTmuxPaneCommand("env", [...hudEnvArgs, "node", omxBin, "hud", "--watch"]);
3627
3838
  const inheritLeaderFlags = process.env[TEAM_INHERIT_LEADER_FLAGS_ENV] !== "0";
3839
+ const inheritedWorkerLaunchArgs = inheritLeaderFlags
3840
+ ? collectInheritableTeamWorkerArgsShared(launchArgs)
3841
+ : [];
3842
+ const inheritedWorkerModel = parseTeamWorkerLaunchArgs(inheritedWorkerLaunchArgs).modelOverride ?? undefined;
3628
3843
  const workerLaunchArgs = resolveTeamWorkerLaunchArgsEnv(process.env[TEAM_WORKER_LAUNCH_ARGS_ENV], launchArgs, inheritLeaderFlags, workerDefaultModel);
3629
3844
  const codexBaseEnv = prependOmxRuntimeCommandShimToEnv(cwd, {
3630
3845
  ...stripHermesMcpBridgeEnv(process.env),
3631
3846
  ...(codexHomeOverride ? { CODEX_HOME: codexHomeOverride } : {}),
3632
3847
  ...(sqliteHomeOverride ? { [CODEX_SQLITE_HOME_ENV]: sqliteHomeOverride } : {}),
3633
3848
  ...(omxRootOverride ? { OMX_ROOT: omxRootOverride } : {}),
3849
+ ...runtimeEnvOverlay,
3634
3850
  }, omxBin);
3635
3851
  const codexEnvWithSession = {
3636
3852
  ...codexBaseEnv,
3637
3853
  ...buildHudRuntimeEnv({ sessionId }).env,
3638
3854
  };
3639
3855
  const codexEnv = workerLaunchArgs
3640
- ? { ...codexEnvWithSession, [TEAM_WORKER_LAUNCH_ARGS_ENV]: workerLaunchArgs }
3856
+ ? {
3857
+ ...codexEnvWithSession,
3858
+ [TEAM_WORKER_LAUNCH_ARGS_ENV]: workerLaunchArgs,
3859
+ ...(inheritedWorkerModel ? { [TEAM_WORKER_INHERITED_MODEL_ENV]: inheritedWorkerModel } : {}),
3860
+ }
3641
3861
  : codexEnvWithSession;
3642
3862
  const codexEnvWithNotify = notifyTempContractRaw
3643
3863
  ? { ...codexEnv, [OMX_NOTIFY_TEMP_CONTRACT_ENV]: notifyTempContractRaw }
3644
3864
  : codexEnv;
3865
+ const runtimeHookEnv = { ...process.env, ...runtimeEnvOverlay };
3645
3866
  const { launchPolicy } = resolveTmuxAwareLaunchPolicy(explicitLaunchPolicy, nativeWindows);
3646
3867
  if (isCodexVersionRequest(launchArgs)) {
3647
3868
  runCodexBlocking(cwd, launchArgs, codexEnvWithNotify);
@@ -3679,6 +3900,7 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, s
3679
3900
  cwd,
3680
3901
  sessionId,
3681
3902
  omxRootOverride,
3903
+ baseEnv: runtimeHookEnv,
3682
3904
  });
3683
3905
  }
3684
3906
  catch (err) {
@@ -3705,6 +3927,7 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, s
3705
3927
  cwd,
3706
3928
  sessionId,
3707
3929
  omxRootOverride,
3930
+ baseEnv: runtimeHookEnv,
3708
3931
  });
3709
3932
  }
3710
3933
  catch (err) {
@@ -3771,7 +3994,7 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, s
3771
3994
  : null;
3772
3995
  const sessionName = buildDetachedTmuxSessionName(cwd, sessionId);
3773
3996
  const launchDetachedSession = () => {
3774
- const contextKey = process.env[OMX_MADMAX_DETACHED_CONTEXT_ENV]?.trim();
3997
+ const contextKey = runtimeContext?.madmaxDetachedContext ?? process.env[OMX_MADMAX_DETACHED_CONTEXT_ENV]?.trim();
3775
3998
  const runsRoot = resolveMadmaxRunsRoot(process.env);
3776
3999
  const activeRecordPath = contextKey
3777
4000
  ? madmaxDetachedActiveRecordPath(runsRoot, contextKey)
@@ -3836,7 +4059,7 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, s
3836
4059
  if (!nativeWindows) {
3837
4060
  detachedParentEnvFilePath = writeDetachedSessionParentEnvFile(cwd, sessionId, codexEnvWithNotify);
3838
4061
  }
3839
- const bootstrapSteps = buildDetachedSessionBootstrapSteps(sessionName, cwd, codexCmd, hudCmd, workerLaunchArgs, codexHomeOverride, notifyTempContractRaw, nativeWindows, sessionId, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, omxRootOverride, process.env, sqliteHomeOverride, detachedParentEnvFilePath);
4062
+ const bootstrapSteps = buildDetachedSessionBootstrapSteps(sessionName, cwd, codexCmd, hudCmd, workerLaunchArgs, codexHomeOverride, notifyTempContractRaw, nativeWindows, sessionId, projectLocalCodexHomeForCleanup, runtimeCodexHomeForCleanup, omxRootOverride, runtimeHookEnv, sqliteHomeOverride, detachedParentEnvFilePath, inheritedWorkerModel);
3840
4063
  for (const step of bootstrapSteps) {
3841
4064
  const output = execTmuxFileSync(step.args, {
3842
4065
  stdio: "pipe",
@@ -3853,9 +4076,10 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, s
3853
4076
  version: 1,
3854
4077
  context_key: contextKey,
3855
4078
  created_at: new Date().toISOString(),
3856
- source_cwd: process.env.OMX_SOURCE_CWD || cwd,
4079
+ source_cwd: runtimeContext?.sourceCwd ?? process.env.OMX_SOURCE_CWD ?? cwd,
4080
+ ...(runtimeContext?.worktreeCwd ? { worktree_cwd: runtimeContext.worktreeCwd } : {}),
3857
4081
  argv: args,
3858
- run_dir: process.env.OMX_ROOT || cwd,
4082
+ run_dir: runtimeContext?.omxRoot ?? process.env.OMX_ROOT ?? cwd,
3859
4083
  tmux_session_name: sessionName,
3860
4084
  session_id: sessionId,
3861
4085
  tmux_pane_id: leaderPaneId,
@@ -3924,6 +4148,7 @@ function runCodex(cwd, args, sessionId, workerDefaultModel, codexHomeOverride, s
3924
4148
  sessionId,
3925
4149
  omxBin,
3926
4150
  omxRootOverride,
4151
+ baseEnv: runtimeHookEnv,
3927
4152
  });
3928
4153
  }
3929
4154
  }
@@ -4663,6 +4888,7 @@ async function listHookVisibleRunDirStateRefs(cwd) {
4663
4888
  return;
4664
4889
  const record = raw;
4665
4890
  const sourceCwd = typeof record.source_cwd === "string" ? record.source_cwd.trim() : "";
4891
+ const worktreeCwd = typeof record.worktree_cwd === "string" ? record.worktree_cwd.trim() : "";
4666
4892
  const runDir = typeof record.run_dir === "string"
4667
4893
  ? record.run_dir.trim()
4668
4894
  : typeof record.cwd === "string"
@@ -4671,7 +4897,8 @@ async function listHookVisibleRunDirStateRefs(cwd) {
4671
4897
  if (!sourceCwd || !runDir)
4672
4898
  return;
4673
4899
  try {
4674
- if (canonicalizePathForRunDirMatch(sourceCwd) !== canonicalCwd)
4900
+ if (canonicalizePathForRunDirMatch(sourceCwd) !== canonicalCwd &&
4901
+ (!worktreeCwd || canonicalizePathForRunDirMatch(worktreeCwd) !== canonicalCwd))
4675
4902
  return;
4676
4903
  const resolvedRunDir = resolve(runDir);
4677
4904
  if (resolvedRunDir !== canonicalRunsRoot