oh-my-codex 0.18.2 → 0.18.4

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 (233) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +1 -0
  4. package/dist/agents/__tests__/definitions.test.js +9 -0
  5. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  6. package/dist/agents/__tests__/native-config.test.js +1 -0
  7. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  8. package/dist/agents/definitions.d.ts.map +1 -1
  9. package/dist/agents/definitions.js +10 -0
  10. package/dist/agents/definitions.js.map +1 -1
  11. package/dist/auth/__tests__/config-sessions.test.d.ts +2 -0
  12. package/dist/auth/__tests__/config-sessions.test.d.ts.map +1 -0
  13. package/dist/auth/__tests__/config-sessions.test.js +48 -0
  14. package/dist/auth/__tests__/config-sessions.test.js.map +1 -0
  15. package/dist/auth/__tests__/quota-rotation.test.d.ts +2 -0
  16. package/dist/auth/__tests__/quota-rotation.test.d.ts.map +1 -0
  17. package/dist/auth/__tests__/quota-rotation.test.js +33 -0
  18. package/dist/auth/__tests__/quota-rotation.test.js.map +1 -0
  19. package/dist/auth/__tests__/redact.test.d.ts +2 -0
  20. package/dist/auth/__tests__/redact.test.d.ts.map +1 -0
  21. package/dist/auth/__tests__/redact.test.js +20 -0
  22. package/dist/auth/__tests__/redact.test.js.map +1 -0
  23. package/dist/auth/__tests__/storage.test.d.ts +2 -0
  24. package/dist/auth/__tests__/storage.test.d.ts.map +1 -0
  25. package/dist/auth/__tests__/storage.test.js +108 -0
  26. package/dist/auth/__tests__/storage.test.js.map +1 -0
  27. package/dist/auth/config.d.ts +9 -0
  28. package/dist/auth/config.d.ts.map +1 -0
  29. package/dist/auth/config.js +77 -0
  30. package/dist/auth/config.js.map +1 -0
  31. package/dist/auth/hotswap.d.ts +36 -0
  32. package/dist/auth/hotswap.d.ts.map +1 -0
  33. package/dist/auth/hotswap.js +159 -0
  34. package/dist/auth/hotswap.js.map +1 -0
  35. package/dist/auth/index.d.ts +8 -0
  36. package/dist/auth/index.d.ts.map +1 -0
  37. package/dist/auth/index.js +8 -0
  38. package/dist/auth/index.js.map +1 -0
  39. package/dist/auth/paths.d.ts +12 -0
  40. package/dist/auth/paths.d.ts.map +1 -0
  41. package/dist/auth/paths.js +78 -0
  42. package/dist/auth/paths.js.map +1 -0
  43. package/dist/auth/quota-detector.d.ts +10 -0
  44. package/dist/auth/quota-detector.d.ts.map +1 -0
  45. package/dist/auth/quota-detector.js +40 -0
  46. package/dist/auth/quota-detector.js.map +1 -0
  47. package/dist/auth/redact.d.ts +2 -0
  48. package/dist/auth/redact.d.ts.map +1 -0
  49. package/dist/auth/redact.js +26 -0
  50. package/dist/auth/redact.js.map +1 -0
  51. package/dist/auth/rotation.d.ts +9 -0
  52. package/dist/auth/rotation.d.ts.map +1 -0
  53. package/dist/auth/rotation.js +26 -0
  54. package/dist/auth/rotation.js.map +1 -0
  55. package/dist/auth/sessions.d.ts +15 -0
  56. package/dist/auth/sessions.d.ts.map +1 -0
  57. package/dist/auth/sessions.js +62 -0
  58. package/dist/auth/sessions.js.map +1 -0
  59. package/dist/auth/storage.d.ts +27 -0
  60. package/dist/auth/storage.d.ts.map +1 -0
  61. package/dist/auth/storage.js +111 -0
  62. package/dist/auth/storage.js.map +1 -0
  63. package/dist/cli/__tests__/auth.test.d.ts +2 -0
  64. package/dist/cli/__tests__/auth.test.d.ts.map +1 -0
  65. package/dist/cli/__tests__/auth.test.js +168 -0
  66. package/dist/cli/__tests__/auth.test.js.map +1 -0
  67. package/dist/cli/__tests__/doctor-warning-copy.test.js +88 -3
  68. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  69. package/dist/cli/__tests__/explore.test.js +28 -7
  70. package/dist/cli/__tests__/explore.test.js.map +1 -1
  71. package/dist/cli/__tests__/index.test.js +70 -2
  72. package/dist/cli/__tests__/index.test.js.map +1 -1
  73. package/dist/cli/__tests__/nested-help-routing.test.js +1 -0
  74. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  75. package/dist/cli/__tests__/setup-agents-overwrite.test.js +30 -1
  76. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  77. package/dist/cli/__tests__/setup-install-mode.test.js +103 -17
  78. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  79. package/dist/cli/__tests__/setup-scope.test.js +1 -1
  80. package/dist/cli/__tests__/sparkshell-cli.test.js +2 -2
  81. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  82. package/dist/cli/auth.d.ts +4 -0
  83. package/dist/cli/auth.d.ts.map +1 -0
  84. package/dist/cli/auth.js +89 -0
  85. package/dist/cli/auth.js.map +1 -0
  86. package/dist/cli/doctor.d.ts.map +1 -1
  87. package/dist/cli/doctor.js +128 -19
  88. package/dist/cli/doctor.js.map +1 -1
  89. package/dist/cli/explore.d.ts +1 -0
  90. package/dist/cli/explore.d.ts.map +1 -1
  91. package/dist/cli/explore.js +18 -0
  92. package/dist/cli/explore.js.map +1 -1
  93. package/dist/cli/index.d.ts +20 -2
  94. package/dist/cli/index.d.ts.map +1 -1
  95. package/dist/cli/index.js +114 -10
  96. package/dist/cli/index.js.map +1 -1
  97. package/dist/cli/question.d.ts.map +1 -1
  98. package/dist/cli/question.js +5 -1
  99. package/dist/cli/question.js.map +1 -1
  100. package/dist/cli/setup.d.ts.map +1 -1
  101. package/dist/cli/setup.js +29 -57
  102. package/dist/cli/setup.js.map +1 -1
  103. package/dist/config/__tests__/deep-interview.test.d.ts +2 -0
  104. package/dist/config/__tests__/deep-interview.test.d.ts.map +1 -0
  105. package/dist/config/__tests__/deep-interview.test.js +239 -0
  106. package/dist/config/__tests__/deep-interview.test.js.map +1 -0
  107. package/dist/config/__tests__/generator-idempotent.test.js +128 -5
  108. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  109. package/dist/config/deep-interview.d.ts +22 -0
  110. package/dist/config/deep-interview.d.ts.map +1 -0
  111. package/dist/config/deep-interview.js +151 -0
  112. package/dist/config/deep-interview.js.map +1 -0
  113. package/dist/config/generator.d.ts +13 -4
  114. package/dist/config/generator.d.ts.map +1 -1
  115. package/dist/config/generator.js +154 -40
  116. package/dist/config/generator.js.map +1 -1
  117. package/dist/hooks/__tests__/agents-overlay.test.js +9 -7
  118. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  119. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +10 -1
  120. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  121. package/dist/hooks/__tests__/consensus-execution-handoff.test.js +13 -0
  122. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  123. package/dist/hooks/__tests__/explore-routing.test.js +10 -12
  124. package/dist/hooks/__tests__/explore-routing.test.js.map +1 -1
  125. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js +13 -15
  126. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js.map +1 -1
  127. package/dist/hooks/__tests__/keyword-detector.test.js +301 -0
  128. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  129. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +33 -0
  130. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  131. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +60 -0
  132. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  133. package/dist/hooks/deep-interview-config-instruction.d.ts +3 -0
  134. package/dist/hooks/deep-interview-config-instruction.d.ts.map +1 -0
  135. package/dist/hooks/deep-interview-config-instruction.js +47 -0
  136. package/dist/hooks/deep-interview-config-instruction.js.map +1 -0
  137. package/dist/hooks/explore-routing.d.ts.map +1 -1
  138. package/dist/hooks/explore-routing.js +8 -13
  139. package/dist/hooks/explore-routing.js.map +1 -1
  140. package/dist/hooks/keyword-detector.d.ts +5 -0
  141. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  142. package/dist/hooks/keyword-detector.js +52 -8
  143. package/dist/hooks/keyword-detector.js.map +1 -1
  144. package/dist/hud/__tests__/hud-tmux-injection.test.js +19 -14
  145. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  146. package/dist/hud/__tests__/reconcile.test.js +117 -9
  147. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  148. package/dist/hud/__tests__/tmux.test.js +103 -1
  149. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  150. package/dist/hud/index.d.ts +1 -1
  151. package/dist/hud/index.d.ts.map +1 -1
  152. package/dist/hud/index.js +24 -2
  153. package/dist/hud/index.js.map +1 -1
  154. package/dist/hud/reconcile.d.ts +1 -1
  155. package/dist/hud/reconcile.d.ts.map +1 -1
  156. package/dist/hud/reconcile.js +23 -0
  157. package/dist/hud/reconcile.js.map +1 -1
  158. package/dist/hud/tmux.d.ts +7 -0
  159. package/dist/hud/tmux.d.ts.map +1 -1
  160. package/dist/hud/tmux.js +46 -9
  161. package/dist/hud/tmux.js.map +1 -1
  162. package/dist/question/__tests__/deep-interview.test.js +80 -7
  163. package/dist/question/__tests__/deep-interview.test.js.map +1 -1
  164. package/dist/question/__tests__/policy.test.js +83 -9
  165. package/dist/question/__tests__/policy.test.js.map +1 -1
  166. package/dist/question/autopilot-wait.d.ts +10 -0
  167. package/dist/question/autopilot-wait.d.ts.map +1 -0
  168. package/dist/question/autopilot-wait.js +134 -0
  169. package/dist/question/autopilot-wait.js.map +1 -0
  170. package/dist/question/deep-interview.d.ts +2 -0
  171. package/dist/question/deep-interview.d.ts.map +1 -1
  172. package/dist/question/deep-interview.js +4 -0
  173. package/dist/question/deep-interview.js.map +1 -1
  174. package/dist/question/policy.d.ts +1 -0
  175. package/dist/question/policy.d.ts.map +1 -1
  176. package/dist/question/policy.js +19 -0
  177. package/dist/question/policy.js.map +1 -1
  178. package/dist/scripts/__tests__/codex-native-hook.test.js +718 -0
  179. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  180. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  181. package/dist/scripts/codex-native-hook.js +69 -5
  182. package/dist/scripts/codex-native-hook.js.map +1 -1
  183. package/dist/scripts/notify-hook.js +13 -0
  184. package/dist/scripts/notify-hook.js.map +1 -1
  185. package/dist/state/__tests__/planning-gate.test.d.ts +2 -0
  186. package/dist/state/__tests__/planning-gate.test.d.ts.map +1 -0
  187. package/dist/state/__tests__/planning-gate.test.js +219 -0
  188. package/dist/state/__tests__/planning-gate.test.js.map +1 -0
  189. package/dist/state/workflow-transition.d.ts +23 -0
  190. package/dist/state/workflow-transition.d.ts.map +1 -1
  191. package/dist/state/workflow-transition.js +63 -0
  192. package/dist/state/workflow-transition.js.map +1 -1
  193. package/dist/subagents/__tests__/tracker.test.js +69 -0
  194. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  195. package/dist/subagents/tracker.d.ts +5 -0
  196. package/dist/subagents/tracker.d.ts.map +1 -1
  197. package/dist/subagents/tracker.js +16 -0
  198. package/dist/subagents/tracker.js.map +1 -1
  199. package/dist/team/__tests__/tmux-session.test.js +86 -0
  200. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  201. package/dist/team/tmux-session.d.ts.map +1 -1
  202. package/dist/team/tmux-session.js +7 -0
  203. package/dist/team/tmux-session.js.map +1 -1
  204. package/dist/ultragoal/__tests__/artifacts.test.js +126 -0
  205. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  206. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  207. package/dist/ultragoal/artifacts.js +126 -8
  208. package/dist/ultragoal/artifacts.js.map +1 -1
  209. package/package.json +1 -1
  210. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  211. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +2 -2
  212. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +11 -1
  213. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +4 -4
  214. package/plugins/oh-my-codex/skills/plan/SKILL.md +5 -5
  215. package/plugins/oh-my-codex/skills/ralph/SKILL.md +1 -1
  216. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +10 -6
  217. package/prompts/executor.md +1 -1
  218. package/prompts/explore-harness.md +2 -2
  219. package/prompts/explore.md +1 -1
  220. package/prompts/planner.md +1 -1
  221. package/prompts/scholastic.md +11 -0
  222. package/prompts/sisyphus-lite.md +1 -1
  223. package/skills/autopilot/SKILL.md +2 -2
  224. package/skills/deep-interview/SKILL.md +11 -1
  225. package/skills/omx-setup/SKILL.md +4 -4
  226. package/skills/plan/SKILL.md +5 -5
  227. package/skills/ralph/SKILL.md +1 -1
  228. package/skills/ralplan/SKILL.md +10 -6
  229. package/src/scripts/__tests__/codex-native-hook.test.ts +853 -0
  230. package/src/scripts/codex-native-hook.ts +73 -3
  231. package/src/scripts/notify-hook.ts +15 -0
  232. package/templates/AGENTS.md +3 -3
  233. package/templates/catalog-manifest.json +5 -0
@@ -1,11 +1,12 @@
1
1
  import { afterEach, describe, it, mock } from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { existsSync, mkdirSync, utimesSync } from "node:fs";
3
+ import { existsSync, mkdirSync, readFileSync, utimesSync } from "node:fs";
4
4
  import { chmod, mkdir, mkdtemp, readFile, rm, stat, writeFile } from "node:fs/promises";
5
5
  import { dirname, join } from "node:path";
6
6
  import { tmpdir } from "node:os";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { once } from "node:events";
9
+ import TOML from "@iarna/toml";
9
10
  import { HELP, normalizeCodexLaunchArgs, buildTmuxShellCommand, buildTmuxPaneCommand, shouldSourceTmuxPaneShellRc, buildWindowsPromptCommand, buildTmuxSessionName, resolveCliInvocation, commandOwnsLocalHelp, resolveCodexLaunchPolicy, resolveEffectiveLeaderLaunchPolicyOverride, resolveEnvLaunchPolicyOverride, resolveLeaderLaunchPolicyOverride, classifyCodexExecFailure, resolveSignalExitCode, parseTmuxPaneSnapshot, findHudWatchPaneIds, buildHudPaneCleanupTargets, readTopLevelTomlString, upsertTopLevelTomlString, collectInheritableTeamWorkerArgs, resolveTeamWorkerLaunchArgsEnv, injectModelInstructionsBypassArgs, resolveWorkerSparkModel, resolveSetupInstallModeArg, resolveSetupMcpModeArg, resolveSetupScopeArg, resolveLaunchConfigRepairOptions, readPersistedSetupPreferences, readPersistedSetupScope, resolveCodexConfigPathForLaunch, resolveCodexHomeForLaunch, resolveProjectLocalCodexHomeForLaunch, shouldAutoIsolateMadmaxLaunch, createMadmaxIsolatedRoot, buildMadmaxDetachedLaunchContextKey, withMadmaxDetachedContextLock, resolveOmxRootForLaunch, resolveDisposableWorktreeOmxRootForLaunch, prepareCodexHomeForLaunch, persistProjectLaunchRuntimeAuthState, persistProjectLaunchRuntimeProjectTrustState, runtimeCodexHomePath, buildDetachedSessionBootstrapSteps, buildDetachedTmuxSessionName, buildDetachedSessionFinalizeSteps, buildDetachedSessionRollbackSteps, detectDetachedSessionWindowIndex, resolveNotifyTempContract, buildNotifyTempStartupMessages, buildNotifyFallbackWatcherEnv, shouldEnableNotifyFallbackWatcher, reapStaleNotifyFallbackWatcher, cleanupLaunchOrphanedMcpProcesses, reapPostLaunchOrphanedMcpProcesses, cleanupPostLaunchModeStateFiles, resolveBackgroundHelperLaunchMode, shouldDetachBackgroundHelper, resolveNotifyFallbackWatcherScript, resolveHookDerivedWatcherScript, resolveNotifyHookScript, buildDetachedWindowsBootstrapScript, acquireTmuxExtendedKeysLease, resolveNativeSessionName, releaseTmuxExtendedKeysLease, withTmuxExtendedKeys, serializeDetachedSessionParentEnv, CODEX_SQLITE_HOME_ENV, } from "../index.js";
10
11
  import { mergeConfig, repairConfigIfNeeded } from "../../config/generator.js";
11
12
  import { ensureReusableNodeModules } from "../../utils/repo-deps.js";
@@ -22,6 +23,9 @@ function normalizeDarwinTmpPath(value) {
22
23
  function escapeRegExp(value) {
23
24
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24
25
  }
26
+ function countMatches(text, pattern) {
27
+ return text.match(pattern)?.length ?? 0;
28
+ }
25
29
  function expectedLowComplexityModel(codexHomeOverride) {
26
30
  return getTeamLowComplexityModel(codexHomeOverride);
27
31
  }
@@ -1052,6 +1056,7 @@ describe("commandOwnsLocalHelp", () => {
1052
1056
  "question",
1053
1057
  "autoresearch",
1054
1058
  "deepinit",
1059
+ "explore",
1055
1060
  "hooks",
1056
1061
  "hud",
1057
1062
  "ralph",
@@ -1515,7 +1520,10 @@ describe("project launch scope helpers", () => {
1515
1520
  assert.ok(persistedProjectConfig.includes(`[projects."${wd}"]`), "expected workspace trust entry to be persisted to project config.toml");
1516
1521
  assert.ok(persistedProjectConfig.includes('trust_level = "trusted"'), "expected trust_level to be persisted to project config.toml");
1517
1522
  assert.doesNotMatch(persistedProjectConfig, /model_availability_nux/, "NUX counters must not leak into durable project config.toml");
1518
- assert.ok(persistedProjectConfig.includes(`[hooks.state."${join(projectCodexHome, "hooks.json")}:pre_tool_use:0:0"]`), "setup-owned project hook trust state must remain intact");
1523
+ const projectHookTrustHeader = `[hooks.state."${join(projectCodexHome, "hooks.json")}:pre_tool_use:0:0"]`;
1524
+ assert.ok(persistedProjectConfig.includes(projectHookTrustHeader), "setup-owned project hook trust state must remain intact");
1525
+ assert.equal(countMatches(persistedProjectConfig, new RegExp(`^${escapeRegExp(projectHookTrustHeader)}$`, "gm")), 1, "runtime trust sync must not duplicate setup-owned hook trust state");
1526
+ assert.doesNotThrow(() => TOML.parse(persistedProjectConfig));
1519
1527
  // 3. On a subsequent launch, the runtime mirror carries the persisted
1520
1528
  // project trust state forward — so Codex finds the workspace as
1521
1529
  // already-trusted and never re-prompts.
@@ -1524,12 +1532,63 @@ describe("project launch scope helpers", () => {
1524
1532
  const nextRuntimeCodexHome = runtimeCodexHomePath(wd, "session-2470-repeat");
1525
1533
  const nextRuntimeConfig = await readFile(join(nextRuntimeCodexHome, "config.toml"), "utf-8");
1526
1534
  assert.ok(nextRuntimeConfig.includes(`[projects."${wd}"]`), "next launch must inherit the persisted workspace trust entry");
1535
+ assert.equal(countMatches(nextRuntimeConfig, new RegExp(`^${escapeRegExp(projectHookTrustHeader)}$`, "gm")), 1, "next runtime config must remain parseable without duplicate hook trust tables");
1536
+ assert.doesNotThrow(() => TOML.parse(nextRuntimeConfig));
1527
1537
  assert.equal(existsSync(join(nextRuntimeCodexHome, "hooks.json")), false);
1528
1538
  }
1529
1539
  finally {
1530
1540
  await rm(wd, { recursive: true, force: true });
1531
1541
  }
1532
1542
  });
1543
+ it("repairs duplicate project hook trust state before relaunching project-scope Codex home (GH #2401)", async () => {
1544
+ const wd = await mkdtemp(join(tmpdir(), "omx-issue-2401-relaunch-"));
1545
+ try {
1546
+ const projectCodexHome = join(wd, ".codex");
1547
+ const projectConfigPath = join(projectCodexHome, "config.toml");
1548
+ const projectHooksPath = join(projectCodexHome, "hooks.json");
1549
+ const projectHookTrustHeader = `[hooks.state."${projectHooksPath}:post_compact:0:0"]`;
1550
+ const escapedProjectHookTrustHeader = escapeRegExp(projectHookTrustHeader);
1551
+ await mkdir(join(wd, ".omx"), { recursive: true });
1552
+ await mkdir(projectCodexHome, { recursive: true });
1553
+ await writeFile(join(wd, ".omx", "setup-scope.json"), JSON.stringify({ scope: "project" }));
1554
+ await writeFile(projectHooksPath, '{"hooks":{}}\n');
1555
+ await writeFile(projectConfigPath, [
1556
+ 'model = "gpt-5.5"',
1557
+ "",
1558
+ "[features]",
1559
+ "hooks = true",
1560
+ "",
1561
+ "# OMX-owned Codex hook trust state",
1562
+ "# Trusts only setup-managed native hook wrappers.",
1563
+ projectHookTrustHeader,
1564
+ 'trusted_hash = "sha256:setup-owned"',
1565
+ "# End OMX-owned Codex hook trust state",
1566
+ "",
1567
+ "# OMX-synced Codex project trust state (from runtime CODEX_HOME)",
1568
+ `[projects."${wd}"]`,
1569
+ 'trust_level = "trusted"',
1570
+ "",
1571
+ projectHookTrustHeader,
1572
+ 'trusted_hash = "sha256:setup-owned"',
1573
+ "",
1574
+ "# End OMX-synced Codex project trust state",
1575
+ "",
1576
+ ].join("\n"));
1577
+ assert.throws(() => TOML.parse(readFileSync(projectConfigPath, "utf-8")));
1578
+ await prepareCodexHomeForLaunch(wd, "session-relaunch", {});
1579
+ const repairedProjectConfig = await readFile(projectConfigPath, "utf-8");
1580
+ const runtimeConfig = await readFile(join(runtimeCodexHomePath(wd, "session-relaunch"), "config.toml"), "utf-8");
1581
+ assert.doesNotThrow(() => TOML.parse(repairedProjectConfig));
1582
+ assert.doesNotThrow(() => TOML.parse(runtimeConfig));
1583
+ assert.equal(countMatches(repairedProjectConfig, new RegExp(`^${escapedProjectHookTrustHeader}$`, "gm")), 1);
1584
+ assert.equal(countMatches(runtimeConfig, new RegExp(`^${escapedProjectHookTrustHeader}$`, "gm")), 1);
1585
+ assert.ok(runtimeConfig.includes(`[projects."${wd}"]`));
1586
+ assert.ok(repairedProjectConfig.includes(`[projects."${wd}"]`));
1587
+ }
1588
+ finally {
1589
+ await rm(wd, { recursive: true, force: true });
1590
+ }
1591
+ });
1533
1592
  it("keeps setup-owned hook trust state targeted at the project hooks path (GH #2470)", async () => {
1534
1593
  const wd = await mkdtemp(join(tmpdir(), "omx-launch-runtime-hook-trust-"));
1535
1594
  try {
@@ -1935,6 +1994,15 @@ describe("detached tmux new-session sequencing", () => {
1935
1994
  assert.doesNotMatch(argsText, /CUSTOM_LLM_API_KEY/);
1936
1995
  assert.doesNotMatch(argsText, /fake-provider-key/);
1937
1996
  });
1997
+ it("runCodex coalesces stale same-leader HUD panes across session ids", async () => {
1998
+ const source = await readFile(join(repoRoot, "src", "cli", "index.ts"), "utf8");
1999
+ assert.match(source, /const staleHudPaneIds = currentPaneId\s*\? listHudWatchPaneIdsInCurrentWindow\(currentPaneId, \{ sessionId, leaderPaneId: currentPaneId \}\)\s*: \[\];/);
2000
+ assert.doesNotMatch(source, /const staleHudPaneIds = listHudWatchPaneIdsInCurrentWindow\(currentPaneId, \{ leaderPaneId: currentPaneId \}\);/);
2001
+ });
2002
+ it("runCodex skips launch-time HUD cleanup when TMUX_PANE is unavailable", async () => {
2003
+ const source = await readFile(join(repoRoot, "src", "cli", "index.ts"), "utf8");
2004
+ assert.match(source, /const staleHudPaneIds = currentPaneId\s*\? listHudWatchPaneIdsInCurrentWindow\(currentPaneId, \{ sessionId, leaderPaneId: currentPaneId \}\)\s*: \[\];/);
2005
+ });
1938
2006
  it("runCodex builds inside-tmux HUD command with OMX_SESSION_ID and OMX_ROOT when set", async () => {
1939
2007
  const source = await readFile(join(repoRoot, 'src', 'cli', 'index.ts'), 'utf-8');
1940
2008
  assert.match(source, /const hudEnvArgs = \[\s*`OMX_SESSION_ID=\$\{sessionId\}`,\s*`\$\{OMX_TMUX_HUD_OWNER_ENV\}=1`,\s*\.\.\.\(currentPaneId \? \[`\$\{OMX_TMUX_HUD_LEADER_PANE_ENV\}=\$\{currentPaneId\}`\] : \[\]\),\s*\.\.\.\(omxRootOverride \? \[`OMX_ROOT=\$\{omxRootOverride\}`\] : \[\]\),\s*\]/);