oh-my-codex 0.18.7 → 0.18.8

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 (259) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +5 -5
  4. package/crates/omx-sparkshell/tests/execution.rs +1 -1
  5. package/dist/agents/__tests__/native-config.test.js +42 -1
  6. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  7. package/dist/agents/definitions.d.ts +8 -0
  8. package/dist/agents/definitions.d.ts.map +1 -1
  9. package/dist/agents/definitions.js +1 -0
  10. package/dist/agents/definitions.js.map +1 -1
  11. package/dist/agents/native-config.d.ts +5 -1
  12. package/dist/agents/native-config.d.ts.map +1 -1
  13. package/dist/agents/native-config.js +17 -2
  14. package/dist/agents/native-config.js.map +1 -1
  15. package/dist/cli/__tests__/codex-plugin-layout.test.js +512 -1
  16. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  17. package/dist/cli/__tests__/doctor-warning-copy.test.js +39 -0
  18. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  19. package/dist/cli/__tests__/index.test.js +61 -5
  20. package/dist/cli/__tests__/index.test.js.map +1 -1
  21. package/dist/cli/__tests__/package-bin-contract.test.js +8 -4
  22. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  23. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +13 -0
  24. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  25. package/dist/cli/__tests__/ralph.test.js +14 -0
  26. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  27. package/dist/cli/__tests__/setup-install-mode.test.js +89 -0
  28. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  29. package/dist/cli/__tests__/setup-refresh.test.js +65 -0
  30. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  31. package/dist/cli/__tests__/state.test.js +21 -0
  32. package/dist/cli/__tests__/state.test.js.map +1 -1
  33. package/dist/cli/__tests__/team.test.js +2 -2
  34. package/dist/cli/__tests__/update.test.js +110 -2
  35. package/dist/cli/__tests__/update.test.js.map +1 -1
  36. package/dist/cli/doctor.d.ts.map +1 -1
  37. package/dist/cli/doctor.js +8 -1
  38. package/dist/cli/doctor.js.map +1 -1
  39. package/dist/cli/index.d.ts +11 -2
  40. package/dist/cli/index.d.ts.map +1 -1
  41. package/dist/cli/index.js +108 -15
  42. package/dist/cli/index.js.map +1 -1
  43. package/dist/cli/plugin-marketplace.d.ts +14 -2
  44. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  45. package/dist/cli/plugin-marketplace.js +62 -15
  46. package/dist/cli/plugin-marketplace.js.map +1 -1
  47. package/dist/cli/ralph.d.ts.map +1 -1
  48. package/dist/cli/ralph.js +3 -1
  49. package/dist/cli/ralph.js.map +1 -1
  50. package/dist/cli/setup-preferences.d.ts +2 -0
  51. package/dist/cli/setup-preferences.d.ts.map +1 -1
  52. package/dist/cli/setup-preferences.js +4 -0
  53. package/dist/cli/setup-preferences.js.map +1 -1
  54. package/dist/cli/setup.d.ts +3 -0
  55. package/dist/cli/setup.d.ts.map +1 -1
  56. package/dist/cli/setup.js +166 -27
  57. package/dist/cli/setup.js.map +1 -1
  58. package/dist/cli/state.d.ts.map +1 -1
  59. package/dist/cli/state.js +8 -1
  60. package/dist/cli/state.js.map +1 -1
  61. package/dist/cli/tmux-hook.d.ts.map +1 -1
  62. package/dist/cli/tmux-hook.js +16 -0
  63. package/dist/cli/tmux-hook.js.map +1 -1
  64. package/dist/cli/update.d.ts +2 -0
  65. package/dist/cli/update.d.ts.map +1 -1
  66. package/dist/cli/update.js +47 -3
  67. package/dist/cli/update.js.map +1 -1
  68. package/dist/config/__tests__/generator-notify.test.js +1 -0
  69. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  70. package/dist/config/generator.d.ts +2 -2
  71. package/dist/config/generator.d.ts.map +1 -1
  72. package/dist/config/generator.js +2 -2
  73. package/dist/config/generator.js.map +1 -1
  74. package/dist/config/team-mode.d.ts +12 -0
  75. package/dist/config/team-mode.d.ts.map +1 -0
  76. package/dist/config/team-mode.js +91 -0
  77. package/dist/config/team-mode.js.map +1 -0
  78. package/dist/hooks/__tests__/agents-overlay.test.js +88 -0
  79. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  80. package/dist/hooks/__tests__/code-review-skill-contract.test.js +8 -0
  81. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  82. package/dist/hooks/__tests__/keyword-detector.test.js +423 -3
  83. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  84. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +1 -1
  85. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  86. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +189 -0
  87. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  88. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +35 -2
  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 +3 -3
  91. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  92. package/dist/hooks/__tests__/skill-guidance-contract.test.js +21 -0
  93. package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
  94. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  95. package/dist/hooks/agents-overlay.js +36 -50
  96. package/dist/hooks/agents-overlay.js.map +1 -1
  97. package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +31 -0
  98. package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +1 -1
  99. package/dist/hooks/extensibility/plugin-runner.js +17 -21
  100. package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
  101. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  102. package/dist/hooks/keyword-detector.js +258 -12
  103. package/dist/hooks/keyword-detector.js.map +1 -1
  104. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  105. package/dist/hooks/prompt-guidance-contract.js +6 -0
  106. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  107. package/dist/hooks/session.d.ts +1 -0
  108. package/dist/hooks/session.d.ts.map +1 -1
  109. package/dist/hooks/session.js.map +1 -1
  110. package/dist/hud/__tests__/authority.test.js +435 -32
  111. package/dist/hud/__tests__/authority.test.js.map +1 -1
  112. package/dist/hud/__tests__/hud-tmux-injection.test.js +2 -1
  113. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  114. package/dist/hud/__tests__/index.test.js +42 -0
  115. package/dist/hud/__tests__/index.test.js.map +1 -1
  116. package/dist/hud/__tests__/reconcile.test.js +521 -15
  117. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  118. package/dist/hud/__tests__/render.test.js +61 -0
  119. package/dist/hud/__tests__/render.test.js.map +1 -1
  120. package/dist/hud/__tests__/state.test.js +132 -4
  121. package/dist/hud/__tests__/state.test.js.map +1 -1
  122. package/dist/hud/__tests__/tmux.test.js +180 -21
  123. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  124. package/dist/hud/authority.d.ts +5 -0
  125. package/dist/hud/authority.d.ts.map +1 -1
  126. package/dist/hud/authority.js +324 -28
  127. package/dist/hud/authority.js.map +1 -1
  128. package/dist/hud/index.d.ts +3 -2
  129. package/dist/hud/index.d.ts.map +1 -1
  130. package/dist/hud/index.js +42 -19
  131. package/dist/hud/index.js.map +1 -1
  132. package/dist/hud/reconcile.d.ts +3 -3
  133. package/dist/hud/reconcile.d.ts.map +1 -1
  134. package/dist/hud/reconcile.js +128 -19
  135. package/dist/hud/reconcile.js.map +1 -1
  136. package/dist/hud/render.d.ts.map +1 -1
  137. package/dist/hud/render.js +35 -0
  138. package/dist/hud/render.js.map +1 -1
  139. package/dist/hud/state.d.ts.map +1 -1
  140. package/dist/hud/state.js +61 -62
  141. package/dist/hud/state.js.map +1 -1
  142. package/dist/hud/tmux.d.ts +24 -6
  143. package/dist/hud/tmux.d.ts.map +1 -1
  144. package/dist/hud/tmux.js +136 -38
  145. package/dist/hud/tmux.js.map +1 -1
  146. package/dist/hud/types.d.ts +11 -0
  147. package/dist/hud/types.d.ts.map +1 -1
  148. package/dist/hud/types.js.map +1 -1
  149. package/dist/mcp/__tests__/state-paths.test.js +71 -1
  150. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  151. package/dist/mcp/state-paths.d.ts +32 -0
  152. package/dist/mcp/state-paths.d.ts.map +1 -1
  153. package/dist/mcp/state-paths.js +113 -17
  154. package/dist/mcp/state-paths.js.map +1 -1
  155. package/dist/mcp/state-server.d.ts +4 -4
  156. package/dist/scripts/__tests__/codex-native-hook.test.js +593 -11
  157. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  158. package/dist/scripts/__tests__/notify-state-io.test.js +72 -1
  159. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  160. package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts +2 -0
  161. package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts.map +1 -0
  162. package/dist/scripts/__tests__/notify-tmux-injection.test.js +57 -0
  163. package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -0
  164. package/dist/scripts/__tests__/run-test-files.test.js +74 -0
  165. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  166. package/dist/scripts/__tests__/verify-native-agents.test.js +65 -0
  167. package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
  168. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  169. package/dist/scripts/codex-native-hook.js +88 -31
  170. package/dist/scripts/codex-native-hook.js.map +1 -1
  171. package/dist/scripts/eval/eval-parity-smoke.js +1 -1
  172. package/dist/scripts/eval/eval-parity-smoke.js.map +1 -1
  173. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  174. package/dist/scripts/notify-hook/auto-nudge.js +3 -1
  175. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  176. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  177. package/dist/scripts/notify-hook/ralph-session-resume.js +3 -10
  178. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  179. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  180. package/dist/scripts/notify-hook/state-io.js +62 -38
  181. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  182. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  183. package/dist/scripts/notify-hook/team-leader-nudge.js +7 -0
  184. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  185. package/dist/scripts/notify-hook/tmux-injection.d.ts +7 -0
  186. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  187. package/dist/scripts/notify-hook/tmux-injection.js +24 -18
  188. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  189. package/dist/scripts/notify-hook.js +75 -11
  190. package/dist/scripts/notify-hook.js.map +1 -1
  191. package/dist/scripts/run-test-files.js +193 -22
  192. package/dist/scripts/run-test-files.js.map +1 -1
  193. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
  194. package/dist/scripts/sync-plugin-mirror.js +61 -3
  195. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  196. package/dist/scripts/verify-native-agents.d.ts.map +1 -1
  197. package/dist/scripts/verify-native-agents.js +58 -1
  198. package/dist/scripts/verify-native-agents.js.map +1 -1
  199. package/dist/state/__tests__/operations.test.js +113 -0
  200. package/dist/state/__tests__/operations.test.js.map +1 -1
  201. package/dist/state/__tests__/skill-active.test.js +3 -16
  202. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  203. package/dist/state/__tests__/workflow-transition.test.js +25 -0
  204. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  205. package/dist/state/operations.d.ts.map +1 -1
  206. package/dist/state/operations.js +57 -2
  207. package/dist/state/operations.js.map +1 -1
  208. package/dist/state/skill-active.d.ts.map +1 -1
  209. package/dist/state/skill-active.js +7 -39
  210. package/dist/state/skill-active.js.map +1 -1
  211. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  212. package/dist/state/workflow-transition-reconcile.js +10 -14
  213. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  214. package/dist/team/__tests__/runtime.test.js +1 -1
  215. package/dist/team/__tests__/runtime.test.js.map +1 -1
  216. package/dist/team/__tests__/scaling.test.js +9 -4
  217. package/dist/team/__tests__/scaling.test.js.map +1 -1
  218. package/dist/team/__tests__/tmux-session.test.js +195 -2
  219. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  220. package/dist/team/__tests__/worker-runtime-identity.test.js +4 -2
  221. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  222. package/dist/team/scaling.d.ts.map +1 -1
  223. package/dist/team/scaling.js +3 -2
  224. package/dist/team/scaling.js.map +1 -1
  225. package/dist/team/tmux-session.d.ts +2 -0
  226. package/dist/team/tmux-session.d.ts.map +1 -1
  227. package/dist/team/tmux-session.js +142 -12
  228. package/dist/team/tmux-session.js.map +1 -1
  229. package/dist/verification/__tests__/ci-rust-gates.test.js +81 -1
  230. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  231. package/package.json +8 -8
  232. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  233. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +334 -21
  234. package/plugins/oh-my-codex/hooks/hooks.json +1 -2
  235. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +3 -1
  236. package/plugins/oh-my-codex/skills/code-review/SKILL.md +7 -7
  237. package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -22
  238. package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +9 -0
  239. package/skills/autopilot/SKILL.md +3 -1
  240. package/skills/code-review/SKILL.md +7 -7
  241. package/skills/ralph/SKILL.md +22 -22
  242. package/skills/ultraqa/SKILL.md +9 -0
  243. package/src/scripts/__tests__/codex-native-hook.test.ts +686 -13
  244. package/src/scripts/__tests__/notify-state-io.test.ts +95 -0
  245. package/src/scripts/__tests__/notify-tmux-injection.test.ts +82 -0
  246. package/src/scripts/__tests__/run-test-files.test.ts +102 -0
  247. package/src/scripts/__tests__/verify-native-agents.test.ts +75 -0
  248. package/src/scripts/codex-native-hook.ts +105 -28
  249. package/src/scripts/demo-team-e2e.sh +10 -7
  250. package/src/scripts/eval/eval-parity-smoke.ts +1 -1
  251. package/src/scripts/notify-hook/auto-nudge.ts +3 -1
  252. package/src/scripts/notify-hook/ralph-session-resume.ts +2 -8
  253. package/src/scripts/notify-hook/state-io.ts +75 -37
  254. package/src/scripts/notify-hook/team-leader-nudge.ts +7 -0
  255. package/src/scripts/notify-hook/tmux-injection.ts +35 -19
  256. package/src/scripts/notify-hook.ts +91 -4
  257. package/src/scripts/run-test-files.ts +192 -22
  258. package/src/scripts/sync-plugin-mirror.ts +98 -9
  259. package/src/scripts/verify-native-agents.ts +65 -1
@@ -46,6 +46,23 @@ async function writeJson(path, value) {
46
46
  await mkdir(dirname(path), { recursive: true }).catch(() => { });
47
47
  await writeFile(path, JSON.stringify(value, null, 2));
48
48
  }
49
+ async function writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId) {
50
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
51
+ await writeJson(join(stateDir, "session.json"), {
52
+ session_id: sessionId,
53
+ native_session_id: nativeSessionId,
54
+ cwd,
55
+ });
56
+ }
57
+ async function writeSessionSkillActiveState(stateDir, sessionId, skill, phase) {
58
+ await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
59
+ active: true,
60
+ skill,
61
+ phase,
62
+ session_id: sessionId,
63
+ active_skills: [{ skill, phase, active: true, session_id: sessionId }],
64
+ });
65
+ }
49
66
  async function setTeamPaneIds(cwd, teamName, paneIds) {
50
67
  for (const fileName of ["config.json", "manifest.v2.json"]) {
51
68
  const filePath = join(cwd, ".omx", "state", "team", teamName, fileName);
@@ -1280,12 +1297,16 @@ describe("codex native hook dispatch", () => {
1280
1297
  }, {
1281
1298
  cwd,
1282
1299
  reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => {
1283
- reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId };
1300
+ reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId, sessionIds: deps.sessionIds };
1284
1301
  return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
1285
1302
  },
1286
1303
  });
1287
1304
  assert.equal(promptResult.omxEventName, "keyword-detector");
1288
- assert.deepEqual(reconcileCall, { cwd, sessionId: ownerSessionId });
1305
+ assert.deepEqual(reconcileCall, {
1306
+ cwd,
1307
+ sessionId: ownerSessionId,
1308
+ sessionIds: [ownerSessionId, nativeSessionId],
1309
+ });
1289
1310
  }
1290
1311
  finally {
1291
1312
  await rm(cwd, { recursive: true, force: true });
@@ -1315,12 +1336,16 @@ describe("codex native hook dispatch", () => {
1315
1336
  }, {
1316
1337
  cwd,
1317
1338
  reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => {
1318
- reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId };
1339
+ reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId, sessionIds: deps.sessionIds };
1319
1340
  return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
1320
1341
  },
1321
1342
  });
1322
1343
  assert.equal(promptResult.omxEventName, "keyword-detector");
1323
- assert.deepEqual(reconcileCall, { cwd, sessionId: canonicalSessionId });
1344
+ assert.deepEqual(reconcileCall, {
1345
+ cwd,
1346
+ sessionId: canonicalSessionId,
1347
+ sessionIds: [canonicalSessionId, nativeSessionId],
1348
+ });
1324
1349
  }
1325
1350
  finally {
1326
1351
  await rm(cwd, { recursive: true, force: true });
@@ -2853,6 +2878,13 @@ standardMaxRounds = 15
2853
2878
  assert.match(message, /Do not advance from deep-interview to ralplan merely because the first question was answered/);
2854
2879
  assert.match(message, /Planner output has been reviewed sequentially by Architect and then Critic/);
2855
2880
  assert.match(message, /do not hand off to Ultragoal or implementation until .*ralplan_architect_review.*ralplan_critic_review/);
2881
+ const autopilotState = JSON.parse(await readFile(join(cwd, ".omx", "state", "sessions", "sess-autopilot-ralplan-gate", "autopilot-state.json"), "utf-8"));
2882
+ const snapshotPath = autopilotState.state?.handoff_artifacts?.context_snapshot_path ?? "";
2883
+ assert.match(snapshotPath, /^\.omx\/context\/implement-issue-2430-\d{8}T\d{6}Z\.md$/);
2884
+ const snapshot = await readFile(join(cwd, snapshotPath), "utf-8");
2885
+ assert.match(snapshot, /activation prompt \/ task seed: \$autopilot implement issue #2430/);
2886
+ assert.match(snapshot, /scope note: this seed captures the Autopilot activation prompt/);
2887
+ assert.match(snapshot, /constraints: follow deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa/);
2856
2888
  }
2857
2889
  finally {
2858
2890
  await rm(cwd, { recursive: true, force: true });
@@ -4172,7 +4204,54 @@ esac
4172
4204
  await rm(cwd, { recursive: true, force: true });
4173
4205
  }
4174
4206
  });
4175
- it("reuses an existing owner-tagged HUD pane when UserPromptSubmit revives with the canonical session id", async () => {
4207
+ it("skips prompt-submit HUD reconciliation during doctor smoke validation", async () => {
4208
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-doctor-smoke-hud-"));
4209
+ const originalTmux = process.env.TMUX;
4210
+ const originalTmuxPane = process.env.TMUX_PANE;
4211
+ const originalHudOwner = process.env[OMX_TMUX_HUD_OWNER_ENV];
4212
+ const originalDoctorSmoke = process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE;
4213
+ try {
4214
+ process.env.TMUX = "1";
4215
+ process.env.TMUX_PANE = "%1";
4216
+ process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
4217
+ process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE = "1";
4218
+ let reconcileCalled = false;
4219
+ const result = await dispatchCodexNativeHook({
4220
+ hook_event_name: "UserPromptSubmit",
4221
+ cwd,
4222
+ session_id: "omx-doctor-plugin-hook-smoke",
4223
+ prompt: "$ralplan doctor plugin hook smoke test",
4224
+ }, {
4225
+ cwd,
4226
+ reconcileHudForPromptSubmitFn: async () => {
4227
+ reconcileCalled = true;
4228
+ return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
4229
+ },
4230
+ });
4231
+ assert.equal(result.omxEventName, "keyword-detector");
4232
+ assert.equal(reconcileCalled, false);
4233
+ }
4234
+ finally {
4235
+ if (originalTmux === undefined)
4236
+ delete process.env.TMUX;
4237
+ else
4238
+ process.env.TMUX = originalTmux;
4239
+ if (originalTmuxPane === undefined)
4240
+ delete process.env.TMUX_PANE;
4241
+ else
4242
+ process.env.TMUX_PANE = originalTmuxPane;
4243
+ if (originalHudOwner === undefined)
4244
+ delete process.env[OMX_TMUX_HUD_OWNER_ENV];
4245
+ else
4246
+ process.env[OMX_TMUX_HUD_OWNER_ENV] = originalHudOwner;
4247
+ if (originalDoctorSmoke === undefined)
4248
+ delete process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE;
4249
+ else
4250
+ process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE = originalDoctorSmoke;
4251
+ await rm(cwd, { recursive: true, force: true });
4252
+ }
4253
+ });
4254
+ it("recreates a leader-only HUD pane when UserPromptSubmit revives with the canonical session id", async () => {
4176
4255
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reuse-"));
4177
4256
  const originalTmux = process.env.TMUX;
4178
4257
  const originalTmuxPane = process.env.TMUX_PANE;
@@ -4219,8 +4298,8 @@ esac
4219
4298
  assert.equal(result.omxEventName, "keyword-detector");
4220
4299
  const tmuxCalls = await readFile(tmuxLog, "utf-8");
4221
4300
  assert.match(tmuxCalls, /list-panes -t %1 -F/);
4222
- assert.match(tmuxCalls, new RegExp(`resize-pane -t %2 -y ${HUD_TMUX_HEIGHT_LINES}`));
4223
- assert.doesNotMatch(tmuxCalls, /split-window/);
4301
+ assert.match(tmuxCalls, /split-window/);
4302
+ assert.match(tmuxCalls, new RegExp(`resize-pane -t %9 -y ${HUD_TMUX_HEIGHT_LINES}`));
4224
4303
  assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", canonicalSessionId, "ralplan-state.json")), true);
4225
4304
  assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", nativeSessionId, "ralplan-state.json")), false);
4226
4305
  }
@@ -11483,7 +11562,7 @@ exit 0
11483
11562
  }, { cwd });
11484
11563
  assert.equal(result.omxEventName, "pre-tool-use");
11485
11564
  assert.equal(result.outputJson?.decision, "block");
11486
- assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
11565
+ assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
11487
11566
  assert.match(String(result.outputJson?.hookSpecificOutput?.additionalContext ?? ""), /\$ultragoal.*\$team.*\$ralph/i);
11488
11567
  }
11489
11568
  finally {
@@ -11525,7 +11604,35 @@ exit 0
11525
11604
  }, { cwd });
11526
11605
  assert.equal(result.omxEventName, "pre-tool-use");
11527
11606
  assert.equal(result.outputJson?.decision, "block");
11528
- assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
11607
+ assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
11608
+ }
11609
+ finally {
11610
+ await rm(cwd, { recursive: true, force: true });
11611
+ }
11612
+ });
11613
+ it("does not block implementation writes from Autopilot ralplan detail state without canonical skill state", async () => {
11614
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-no-canonical-"));
11615
+ try {
11616
+ const stateDir = join(cwd, ".omx", "state");
11617
+ const sessionId = "sess-autopilot-ralplan-no-canonical";
11618
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
11619
+ await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
11620
+ await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
11621
+ active: true,
11622
+ mode: "autopilot",
11623
+ current_phase: "ralplan",
11624
+ session_id: sessionId,
11625
+ });
11626
+ const result = await dispatchCodexNativeHook({
11627
+ hook_event_name: "PreToolUse",
11628
+ cwd,
11629
+ session_id: sessionId,
11630
+ thread_id: "thread-autopilot-ralplan-no-canonical",
11631
+ tool_name: "Edit",
11632
+ tool_input: { file_path: "src/runtime.ts" },
11633
+ }, { cwd });
11634
+ assert.equal(result.omxEventName, "pre-tool-use");
11635
+ assert.equal(result.outputJson, null);
11529
11636
  }
11530
11637
  finally {
11531
11638
  await rm(cwd, { recursive: true, force: true });
@@ -11606,12 +11713,430 @@ exit 0
11606
11713
  }, { cwd });
11607
11714
  assert.equal(result.omxEventName, "pre-tool-use");
11608
11715
  assert.equal(result.outputJson?.decision, "block");
11609
- assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
11716
+ assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
11610
11717
  }
11611
11718
  finally {
11612
11719
  await rm(cwd, { recursive: true, force: true });
11613
11720
  }
11614
11721
  });
11722
+ it("blocks implementation writes when ralplan and Autopilot ralplan are both active", async () => {
11723
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-autopilot-mixed-planning-"));
11724
+ try {
11725
+ const stateDir = join(cwd, ".omx", "state");
11726
+ const sessionId = "sess-ralplan-autopilot-mixed-planning";
11727
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
11728
+ await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
11729
+ await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
11730
+ active: true,
11731
+ skill: "autopilot",
11732
+ phase: "ralplan",
11733
+ session_id: sessionId,
11734
+ active_skills: [
11735
+ { skill: "ralplan", phase: "planning", active: true, session_id: sessionId },
11736
+ { skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId },
11737
+ ],
11738
+ });
11739
+ await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
11740
+ active: true,
11741
+ mode: "ralplan",
11742
+ current_phase: "planning",
11743
+ session_id: sessionId,
11744
+ });
11745
+ await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
11746
+ active: true,
11747
+ mode: "autopilot",
11748
+ current_phase: "ralplan",
11749
+ session_id: sessionId,
11750
+ });
11751
+ const result = await dispatchCodexNativeHook({
11752
+ hook_event_name: "PreToolUse",
11753
+ cwd,
11754
+ session_id: sessionId,
11755
+ thread_id: "thread-ralplan-autopilot-mixed-planning",
11756
+ tool_name: "Edit",
11757
+ tool_input: { file_path: "src/runtime.ts" },
11758
+ }, { cwd });
11759
+ assert.equal(result.omxEventName, "pre-tool-use");
11760
+ assert.equal(result.outputJson?.decision, "block");
11761
+ assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
11762
+ }
11763
+ finally {
11764
+ await rm(cwd, { recursive: true, force: true });
11765
+ }
11766
+ });
11767
+ it("blocks implementation writes while Autopilot is supervising replan without handoff", async () => {
11768
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-replan-pretool-block-"));
11769
+ try {
11770
+ const stateDir = join(cwd, ".omx", "state");
11771
+ const sessionId = "sess-autopilot-replan-pretool-block";
11772
+ await mkdir(join(stateDir, "sessions", sessionId), { recursive: true });
11773
+ await writeJson(join(stateDir, "session.json"), { session_id: sessionId });
11774
+ await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
11775
+ active: true,
11776
+ skill: "autopilot",
11777
+ phase: "replan",
11778
+ session_id: sessionId,
11779
+ active_skills: [{ skill: "autopilot", phase: "replan", active: true, session_id: sessionId }],
11780
+ });
11781
+ await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
11782
+ active: true,
11783
+ mode: "autopilot",
11784
+ current_phase: "replan",
11785
+ session_id: sessionId,
11786
+ });
11787
+ const result = await dispatchCodexNativeHook({
11788
+ hook_event_name: "PreToolUse",
11789
+ cwd,
11790
+ session_id: sessionId,
11791
+ thread_id: "thread-autopilot-replan-pretool-block",
11792
+ tool_name: "Edit",
11793
+ tool_input: { file_path: "src/runtime.ts" },
11794
+ }, { cwd });
11795
+ assert.equal(result.omxEventName, "pre-tool-use");
11796
+ assert.equal(result.outputJson?.decision, "block");
11797
+ assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
11798
+ }
11799
+ finally {
11800
+ await rm(cwd, { recursive: true, force: true });
11801
+ }
11802
+ });
11803
+ it("blocks implementation writes when native Codex id maps to OMX Autopilot ralplan state", async () => {
11804
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-block-"));
11805
+ try {
11806
+ const stateDir = join(cwd, ".omx", "state");
11807
+ const sessionId = "sess-autopilot-ralplan-native-map-block";
11808
+ const nativeSessionId = "019e-autopilot-ralplan-native";
11809
+ await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
11810
+ await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
11811
+ await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
11812
+ active: true,
11813
+ mode: "autopilot",
11814
+ current_phase: "ralplan",
11815
+ session_id: sessionId,
11816
+ });
11817
+ const result = await dispatchCodexNativeHook({
11818
+ hook_event_name: "PreToolUse",
11819
+ cwd,
11820
+ session_id: nativeSessionId,
11821
+ thread_id: "thread-autopilot-ralplan-native-map-block",
11822
+ tool_name: "apply_patch",
11823
+ tool_input: { file_path: "src/runtime.ts" },
11824
+ }, { cwd });
11825
+ assert.equal(result.omxEventName, "pre-tool-use");
11826
+ assert.equal(result.outputJson?.decision, "block");
11827
+ assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
11828
+ }
11829
+ finally {
11830
+ await rm(cwd, { recursive: true, force: true });
11831
+ }
11832
+ });
11833
+ it("blocks bash implementation writes when native Codex id maps to OMX Autopilot ralplan state", async () => {
11834
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-bash-"));
11835
+ try {
11836
+ const stateDir = join(cwd, ".omx", "state");
11837
+ const sessionId = "sess-autopilot-ralplan-native-map-bash";
11838
+ const nativeSessionId = "019e-autopilot-ralplan-native-bash";
11839
+ await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
11840
+ await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
11841
+ await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
11842
+ active: true,
11843
+ mode: "autopilot",
11844
+ current_phase: "ralplan",
11845
+ session_id: sessionId,
11846
+ });
11847
+ const result = await dispatchCodexNativeHook({
11848
+ hook_event_name: "PreToolUse",
11849
+ cwd,
11850
+ session_id: nativeSessionId,
11851
+ thread_id: "thread-autopilot-ralplan-native-map-bash",
11852
+ tool_name: "Bash",
11853
+ tool_input: { command: "cat <<'EOF' > src/runtime.ts\nimplementation\nEOF" },
11854
+ }, { cwd });
11855
+ assert.equal(result.omxEventName, "pre-tool-use");
11856
+ assert.equal(result.outputJson?.decision, "block");
11857
+ assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
11858
+ }
11859
+ finally {
11860
+ await rm(cwd, { recursive: true, force: true });
11861
+ }
11862
+ });
11863
+ it("blocks standalone ralplan writes when native Codex id maps to OMX session state", async () => {
11864
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-block-"));
11865
+ try {
11866
+ const stateDir = join(cwd, ".omx", "state");
11867
+ const sessionId = "sess-ralplan-native-map-block";
11868
+ const nativeSessionId = "019e-ralplan-native-map";
11869
+ await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
11870
+ await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
11871
+ await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
11872
+ active: true,
11873
+ mode: "ralplan",
11874
+ current_phase: "planning",
11875
+ session_id: sessionId,
11876
+ });
11877
+ const result = await dispatchCodexNativeHook({
11878
+ hook_event_name: "PreToolUse",
11879
+ cwd,
11880
+ session_id: nativeSessionId,
11881
+ thread_id: "thread-ralplan-native-map-block",
11882
+ tool_name: "Edit",
11883
+ tool_input: { file_path: "src/runtime.ts" },
11884
+ }, { cwd });
11885
+ assert.equal(result.omxEventName, "pre-tool-use");
11886
+ assert.equal(result.outputJson?.decision, "block");
11887
+ assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
11888
+ }
11889
+ finally {
11890
+ await rm(cwd, { recursive: true, force: true });
11891
+ }
11892
+ });
11893
+ it("blocks deep-interview writes when native Codex id maps to OMX session state", async () => {
11894
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-deep-interview-native-map-block-"));
11895
+ try {
11896
+ const stateDir = join(cwd, ".omx", "state");
11897
+ const sessionId = "sess-deep-interview-native-map-block";
11898
+ const nativeSessionId = "019e-deep-interview-native-map";
11899
+ await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
11900
+ await writeSessionSkillActiveState(stateDir, sessionId, "deep-interview", "interview");
11901
+ await writeJson(join(stateDir, "sessions", sessionId, "deep-interview-state.json"), {
11902
+ active: true,
11903
+ mode: "deep-interview",
11904
+ current_phase: "interview",
11905
+ session_id: sessionId,
11906
+ });
11907
+ const result = await dispatchCodexNativeHook({
11908
+ hook_event_name: "PreToolUse",
11909
+ cwd,
11910
+ session_id: nativeSessionId,
11911
+ thread_id: "thread-deep-interview-native-map-block",
11912
+ tool_name: "Edit",
11913
+ tool_input: { file_path: "src/runtime.ts" },
11914
+ }, { cwd });
11915
+ assert.equal(result.omxEventName, "pre-tool-use");
11916
+ assert.equal(result.outputJson?.decision, "block");
11917
+ assert.match(String(result.outputJson?.reason ?? ""), /Deep-interview is active .*implementation\/write tools are blocked/i);
11918
+ }
11919
+ finally {
11920
+ await rm(cwd, { recursive: true, force: true });
11921
+ }
11922
+ });
11923
+ it("allows mapped ralplan planning artifact writes without execution handoff", async () => {
11924
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-artifact-"));
11925
+ try {
11926
+ const stateDir = join(cwd, ".omx", "state");
11927
+ const sessionId = "sess-ralplan-native-map-artifact";
11928
+ const nativeSessionId = "019e-ralplan-native-map-artifact";
11929
+ await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
11930
+ await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
11931
+ await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
11932
+ active: true,
11933
+ mode: "ralplan",
11934
+ current_phase: "planning",
11935
+ session_id: sessionId,
11936
+ });
11937
+ const result = await dispatchCodexNativeHook({
11938
+ hook_event_name: "PreToolUse",
11939
+ cwd,
11940
+ session_id: nativeSessionId,
11941
+ thread_id: "thread-ralplan-native-map-artifact",
11942
+ tool_name: "Bash",
11943
+ tool_input: { command: "cat <<'EOF' > .omx/plans/prd-native-map.md\nplanning\nEOF" },
11944
+ }, { cwd });
11945
+ assert.equal(result.omxEventName, "pre-tool-use");
11946
+ assert.equal(result.outputJson, null);
11947
+ }
11948
+ finally {
11949
+ await rm(cwd, { recursive: true, force: true });
11950
+ }
11951
+ });
11952
+ it("allows mapped implementation writes when explicit execution handoff is active", async () => {
11953
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-handoff-"));
11954
+ try {
11955
+ const stateDir = join(cwd, ".omx", "state");
11956
+ const sessionId = "sess-ralplan-native-map-handoff";
11957
+ const nativeSessionId = "019e-ralplan-native-map-handoff";
11958
+ await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
11959
+ await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), {
11960
+ active: true,
11961
+ skill: "ultragoal",
11962
+ phase: "planning",
11963
+ session_id: sessionId,
11964
+ active_skills: [
11965
+ { skill: "ralplan", phase: "planning", active: true, session_id: sessionId },
11966
+ { skill: "ultragoal", phase: "planning", active: true, session_id: sessionId },
11967
+ ],
11968
+ });
11969
+ await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
11970
+ active: true,
11971
+ mode: "ralplan",
11972
+ current_phase: "complete",
11973
+ session_id: sessionId,
11974
+ });
11975
+ await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), {
11976
+ active: true,
11977
+ mode: "ultragoal",
11978
+ current_phase: "planning",
11979
+ session_id: sessionId,
11980
+ });
11981
+ const result = await dispatchCodexNativeHook({
11982
+ hook_event_name: "PreToolUse",
11983
+ cwd,
11984
+ session_id: nativeSessionId,
11985
+ thread_id: "thread-ralplan-native-map-handoff",
11986
+ tool_name: "Edit",
11987
+ tool_input: { file_path: "src/runtime.ts" },
11988
+ }, { cwd });
11989
+ assert.equal(result.omxEventName, "pre-tool-use");
11990
+ assert.equal(result.outputJson, null);
11991
+ }
11992
+ finally {
11993
+ await rm(cwd, { recursive: true, force: true });
11994
+ }
11995
+ });
11996
+ it("allows mapped implementation writes when terminal Autopilot run-state shadows stale supervised ralplan state", async () => {
11997
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-native-map-terminal-"));
11998
+ try {
11999
+ const stateDir = join(cwd, ".omx", "state");
12000
+ const sessionId = "sess-autopilot-ralplan-native-map-terminal";
12001
+ const nativeSessionId = "019e-autopilot-ralplan-native-terminal";
12002
+ await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
12003
+ await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
12004
+ await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
12005
+ active: true,
12006
+ mode: "autopilot",
12007
+ current_phase: "ralplan",
12008
+ session_id: sessionId,
12009
+ });
12010
+ await writeJson(join(stateDir, "sessions", sessionId, "run-state.json"), {
12011
+ version: 1,
12012
+ active: false,
12013
+ mode: "autopilot",
12014
+ outcome: "finish",
12015
+ lifecycle_outcome: "finished",
12016
+ current_phase: "complete",
12017
+ completed_at: "2026-05-30T00:00:00.000Z",
12018
+ updated_at: "2026-05-30T00:00:00.000Z",
12019
+ });
12020
+ const result = await dispatchCodexNativeHook({
12021
+ hook_event_name: "PreToolUse",
12022
+ cwd,
12023
+ session_id: nativeSessionId,
12024
+ thread_id: "thread-autopilot-ralplan-native-map-terminal",
12025
+ tool_name: "Edit",
12026
+ tool_input: { file_path: "src/runtime.ts" },
12027
+ }, { cwd });
12028
+ assert.equal(result.omxEventName, "pre-tool-use");
12029
+ assert.equal(result.outputJson, null);
12030
+ }
12031
+ finally {
12032
+ await rm(cwd, { recursive: true, force: true });
12033
+ }
12034
+ });
12035
+ it("does not block unrelated native Codex ids when current OMX session mapping does not match", async () => {
12036
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-native-map-unrelated-"));
12037
+ try {
12038
+ const stateDir = join(cwd, ".omx", "state");
12039
+ const sessionId = "sess-ralplan-native-map-owner";
12040
+ const ownerNativeSessionId = "019e-ralplan-native-owner";
12041
+ await writeNativeMappedSessionState(cwd, stateDir, sessionId, ownerNativeSessionId);
12042
+ await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
12043
+ await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
12044
+ active: true,
12045
+ mode: "ralplan",
12046
+ current_phase: "planning",
12047
+ session_id: sessionId,
12048
+ });
12049
+ const result = await dispatchCodexNativeHook({
12050
+ hook_event_name: "PreToolUse",
12051
+ cwd,
12052
+ session_id: "019e-unrelated-native-session",
12053
+ thread_id: "thread-ralplan-native-map-unrelated",
12054
+ tool_name: "Edit",
12055
+ tool_input: { file_path: "src/runtime.ts" },
12056
+ }, { cwd });
12057
+ assert.equal(result.omxEventName, "pre-tool-use");
12058
+ assert.equal(result.outputJson, null);
12059
+ }
12060
+ finally {
12061
+ await rm(cwd, { recursive: true, force: true });
12062
+ }
12063
+ });
12064
+ it("blocks mapped Autopilot ralplan writes from the authoritative team state root", async () => {
12065
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-autopilot-ralplan-team-root-"));
12066
+ const teamStateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-"));
12067
+ const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
12068
+ try {
12069
+ process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
12070
+ const stateDir = teamStateRoot;
12071
+ const sessionId = "sess-autopilot-ralplan-team-root";
12072
+ const nativeSessionId = "019e-autopilot-ralplan-team-root";
12073
+ await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
12074
+ await writeSessionSkillActiveState(stateDir, sessionId, "autopilot", "ralplan");
12075
+ await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), {
12076
+ active: true,
12077
+ mode: "autopilot",
12078
+ current_phase: "ralplan",
12079
+ session_id: sessionId,
12080
+ });
12081
+ const result = await dispatchCodexNativeHook({
12082
+ hook_event_name: "PreToolUse",
12083
+ cwd,
12084
+ session_id: nativeSessionId,
12085
+ thread_id: "thread-autopilot-ralplan-team-root",
12086
+ tool_name: "Edit",
12087
+ tool_input: { file_path: "src/runtime.ts" },
12088
+ }, { cwd });
12089
+ assert.equal(result.omxEventName, "pre-tool-use");
12090
+ assert.equal(result.outputJson?.decision, "block");
12091
+ assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
12092
+ assert.equal(existsSync(join(cwd, ".omx", "state", "session.json")), false);
12093
+ }
12094
+ finally {
12095
+ if (typeof previousTeamStateRoot === "string")
12096
+ process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
12097
+ else
12098
+ delete process.env.OMX_TEAM_STATE_ROOT;
12099
+ await rm(cwd, { recursive: true, force: true });
12100
+ await rm(teamStateRoot, { recursive: true, force: true });
12101
+ }
12102
+ });
12103
+ it("does not block unrelated native Codex ids from the authoritative team state root", async () => {
12104
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-team-root-unrelated-"));
12105
+ const teamStateRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-team-root-unrelated-"));
12106
+ const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
12107
+ try {
12108
+ process.env.OMX_TEAM_STATE_ROOT = teamStateRoot;
12109
+ const stateDir = teamStateRoot;
12110
+ const sessionId = "sess-ralplan-team-root-owner";
12111
+ const nativeSessionId = "019e-ralplan-team-root-owner";
12112
+ await writeNativeMappedSessionState(cwd, stateDir, sessionId, nativeSessionId);
12113
+ await writeSessionSkillActiveState(stateDir, sessionId, "ralplan", "planning");
12114
+ await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), {
12115
+ active: true,
12116
+ mode: "ralplan",
12117
+ current_phase: "planning",
12118
+ session_id: sessionId,
12119
+ });
12120
+ const result = await dispatchCodexNativeHook({
12121
+ hook_event_name: "PreToolUse",
12122
+ cwd,
12123
+ session_id: "019e-unrelated-team-root-native",
12124
+ thread_id: "thread-ralplan-team-root-unrelated",
12125
+ tool_name: "Edit",
12126
+ tool_input: { file_path: "src/runtime.ts" },
12127
+ }, { cwd });
12128
+ assert.equal(result.omxEventName, "pre-tool-use");
12129
+ assert.equal(result.outputJson, null);
12130
+ }
12131
+ finally {
12132
+ if (typeof previousTeamStateRoot === "string")
12133
+ process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
12134
+ else
12135
+ delete process.env.OMX_TEAM_STATE_ROOT;
12136
+ await rm(cwd, { recursive: true, force: true });
12137
+ await rm(teamStateRoot, { recursive: true, force: true });
12138
+ }
12139
+ });
11615
12140
  it("allows ralplan planning artifact writes without execution handoff", async () => {
11616
12141
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-pretool-artifact-"));
11617
12142
  try {
@@ -11677,7 +12202,7 @@ exit 0
11677
12202
  }, { cwd });
11678
12203
  assert.equal(result.omxEventName, "pre-tool-use");
11679
12204
  assert.equal(result.outputJson?.decision, "block");
11680
- assert.match(String(result.outputJson?.reason ?? ""), /Ralplan is active .*implementation\/write tools are blocked/i);
12205
+ assert.match(String(result.outputJson?.reason ?? ""), /(?:Ralplan|Autopilot planning) is active .*implementation\/write tools are blocked/i);
11681
12206
  }
11682
12207
  finally {
11683
12208
  await rm(cwd, { recursive: true, force: true });
@@ -12291,6 +12816,63 @@ describe("codex native hook triage integration", () => {
12291
12816
  await rm(cwd, { recursive: true, force: true });
12292
12817
  }
12293
12818
  });
12819
+ it("omits Team handoff guidance from autopilot prompt context when Team mode is disabled", async () => {
12820
+ const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-observable-no-team-"));
12821
+ try {
12822
+ await mkdir(join(cwd, ".omx", "state"), { recursive: true });
12823
+ await writeJson(join(cwd, ".omx", "setup-scope.json"), {
12824
+ scope: "project",
12825
+ teamMode: "disabled",
12826
+ });
12827
+ await writeSessionStart(cwd, "sess-autopilot-observable-no-team");
12828
+ const result = await dispatchCodexNativeHook({
12829
+ hook_event_name: "UserPromptSubmit",
12830
+ cwd,
12831
+ session_id: "sess-autopilot-observable-no-team",
12832
+ thread_id: "thread-autopilot-observable-no-team",
12833
+ turn_id: "turn-autopilot-observable-no-team",
12834
+ prompt: "$autopilot implement issue #2430",
12835
+ }, { cwd });
12836
+ assert.equal(result.skillState?.skill, "autopilot");
12837
+ const additionalContext = String(result.outputJson?.hookSpecificOutput?.additionalContext ?? "");
12838
+ assert.match(additionalContext, /detected workflow keyword "\$autopilot" -> autopilot/);
12839
+ assert.match(additionalContext, /\$deep-interview -> \$ralplan -> \$ultragoal -> \$code-review -> \$ultraqa/);
12840
+ assert.doesNotMatch(additionalContext, /\$team/);
12841
+ assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false);
12842
+ }
12843
+ finally {
12844
+ await rm(cwd, { recursive: true, force: true });
12845
+ }
12846
+ });
12847
+ it("ignores disabled $team before outside-tmux Team blocking so later workflows can activate", async () => {
12848
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-disabled-team-primary-"));
12849
+ try {
12850
+ await mkdir(join(cwd, ".omx", "state"), { recursive: true });
12851
+ await writeJson(join(cwd, ".omx", "setup-scope.json"), {
12852
+ scope: "project",
12853
+ teamMode: "disabled",
12854
+ });
12855
+ await writeSessionStart(cwd, "sess-disabled-team-primary");
12856
+ const result = await dispatchCodexNativeHook({
12857
+ hook_event_name: "UserPromptSubmit",
12858
+ cwd,
12859
+ session_id: "sess-disabled-team-primary",
12860
+ thread_id: "thread-disabled-team-primary",
12861
+ turn_id: "turn-disabled-team-primary",
12862
+ prompt: "$team $ralph fix this",
12863
+ }, { cwd });
12864
+ assert.equal(result.skillState?.skill, "ralph");
12865
+ assert.equal(result.skillState?.transition_error, undefined);
12866
+ assert.equal(existsSync(join(cwd, ".omx", "state", "team-state.json")), false);
12867
+ assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-disabled-team-primary", "ralph-state.json")), true);
12868
+ const additionalContext = String(result.outputJson?.hookSpecificOutput?.additionalContext ?? "");
12869
+ assert.match(additionalContext, /detected workflow keyword "\$ralph" -> ralph/);
12870
+ assert.doesNotMatch(additionalContext, /Codex App\/native outside-tmux sessions cannot activate/);
12871
+ }
12872
+ finally {
12873
+ await rm(cwd, { recursive: true, force: true });
12874
+ }
12875
+ });
12294
12876
  it("makes bare autopilot command activation observable in state and prompt guidance", async () => {
12295
12877
  const cwd = await mkdtemp(join(tmpdir(), "omx-autopilot-bare-observable-"));
12296
12878
  try {