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
@@ -15,6 +15,7 @@ import { findGitLayout } from "../utils/git-layout.js";
15
15
  import { getBaseStateDir, getStateFilePath, getStatePath } from "../mcp/state-paths.js";
16
16
  import { detectKeywords, detectPrimaryKeyword, recordSkillActivation, } from "../hooks/keyword-detector.js";
17
17
  import { buildDeepInterviewConfigInstruction } from "../hooks/deep-interview-config-instruction.js";
18
+ import { readTeamModeConfig } from "../config/team-mode.js";
18
19
  import { detectNativeStopStallPattern, loadAutoNudgeConfig, normalizeAutoNudgeSignatureText, resolveEffectiveAutoNudgeResponse, } from "./notify-hook/auto-nudge.js";
19
20
  import { SLOPPY_FALLBACK_GROUNDING_PATTERNS, SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS, SLOPPY_FALLBACK_PHRASE_PATTERNS, buildNativePostToolUseOutput, buildNativePreToolUseOutput, detectMcpTransportFailure, hasAnyPattern, } from "./codex-native-pre-post.js";
20
21
  import { handleTeamWorkerPostToolUseSuccess } from "./notify-hook/team-worker-posttooluse.js";
@@ -80,6 +81,19 @@ function resolveHudReconcileSessionId(currentSessionState, canonicalSessionId, s
80
81
  return ownerOmxSessionId;
81
82
  return canonicalSessionId || sessionIdForState || undefined;
82
83
  }
84
+ function resolveHudReconcileSessionIds(currentSessionState, canonicalSessionId, sessionIdForState, nativeSessionId) {
85
+ const ownerOmxSessionId = safeString(currentSessionState?.owner_omx_session_id).trim();
86
+ return uniqueNonEmpty([
87
+ resolveHudReconcileSessionId(currentSessionState, canonicalSessionId, sessionIdForState),
88
+ canonicalSessionId ?? undefined,
89
+ sessionIdForState ?? undefined,
90
+ nativeSessionId ?? undefined,
91
+ safeString(currentSessionState?.session_id),
92
+ safeString(currentSessionState?.native_session_id),
93
+ OMX_OWNER_SESSION_ID_PATTERN.test(ownerOmxSessionId) ? ownerOmxSessionId : undefined,
94
+ safeString(currentSessionState?.owner_codex_session_id),
95
+ ]);
96
+ }
83
97
  function safeContextSnippet(value, maxLength = 300) {
84
98
  const text = safeString(value).replace(/\s+/g, " ").trim();
85
99
  if (text.length <= maxLength)
@@ -1384,6 +1398,8 @@ function buildTeamHelpInstruction(cwd, payload) {
1384
1398
  }).teamHelpInstruction;
1385
1399
  }
1386
1400
  function buildNativeOutsideTmuxTeamPromptBlockState(prompt, cwd, payload, sessionId, threadId, turnId) {
1401
+ if (!readTeamModeConfig(cwd).enabled)
1402
+ return null;
1387
1403
  const match = detectPrimaryKeyword(prompt);
1388
1404
  if (match?.skill !== "team")
1389
1405
  return null;
@@ -1418,8 +1434,11 @@ function buildSkillStateCliInstruction(mode, statePath) {
1418
1434
  function buildAutopilotPromptActivationNote(skillState, options = {}) {
1419
1435
  if (skillState?.initialized_mode !== "autopilot")
1420
1436
  return null;
1437
+ const teamHandoff = readTeamModeConfig(options.cwd).enabled
1438
+ ? " (+ $team if needed)"
1439
+ : "";
1421
1440
  return [
1422
- "Autopilot protocol: the durable default chain is $deep-interview -> $ralplan -> $ultragoal (+ $team if needed) -> $code-review -> $ultraqa (deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa).",
1441
+ `Autopilot protocol: the durable default chain is $deep-interview -> $ralplan -> $ultragoal${teamHandoff} -> $code-review -> $ultraqa (deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa).`,
1423
1442
  "Start/resume at current_phase=deep-interview unless the task is clear and bounded; if deep-interview is intentionally skipped, persist and state an explicit deep_interview_gate.skip_reason before moving to ralplan.",
1424
1443
  "Deep-interview is a structured question chain, not a one-question gate: after an omx question answer, re-score ambiguity against the active threshold, treat max_rounds as a cap, and crystallize once ambiguity is at or below threshold and readiness gates pass.",
1425
1444
  options.markedQuestionAnswer
@@ -1431,12 +1450,18 @@ function buildAutopilotPromptActivationNote(skillState, options = {}) {
1431
1450
  "When Codex goal tools are available, call get_goal/create_goal only from the active thread handoff and treat the active goal as the completion contract until code-review and ultraqa are clean.",
1432
1451
  ].filter(Boolean).join(" ");
1433
1452
  }
1453
+ function formatExecutionHandoffList(cwd) {
1454
+ return readTeamModeConfig(cwd).enabled
1455
+ ? "`$ultragoal`, `$team`, or `$ralph`"
1456
+ : "`$ultragoal` or `$ralph`";
1457
+ }
1434
1458
  function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(), payload) {
1435
1459
  if (!prompt)
1436
1460
  return null;
1437
1461
  const promptPriorityMessage = buildPromptPriorityMessage(prompt);
1438
- const matches = detectKeywords(prompt);
1439
- const match = detectPrimaryKeyword(prompt);
1462
+ const teamMode = readTeamModeConfig(cwd);
1463
+ const matches = detectKeywords(prompt).filter((entry) => teamMode.enabled || entry.skill !== "team");
1464
+ const match = matches[0] ?? null;
1440
1465
  if (!match) {
1441
1466
  const continuedSkill = safeString(skillState?.skill).trim();
1442
1467
  if (!continuedSkill)
@@ -1446,7 +1471,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1446
1471
  : null;
1447
1472
  const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
1448
1473
  const markedQuestionAnswer = /^\s*\[omx question answered\]/i.test(prompt);
1449
- const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer });
1474
+ const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer, cwd });
1450
1475
  return [
1451
1476
  `OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}".`,
1452
1477
  promptPriorityMessage,
@@ -1470,7 +1495,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1470
1495
  ? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
1471
1496
  : null;
1472
1497
  const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
1473
- const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer: true });
1498
+ const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer: true, cwd });
1474
1499
  return [
1475
1500
  `OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}"; workflow-like tokens inside the marked omx question answer are treated as answer text, not a new workflow activation.`,
1476
1501
  promptPriorityMessage,
@@ -1503,7 +1528,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1503
1528
  const ultragoalPromptActivationNote = match.skill === "ultragoal"
1504
1529
  ? "Ultragoal protocol: use `omx ultragoal create-goals` / `complete-goals` / `checkpoint` for `.omx/ultragoal` artifacts, then use Codex goal model tools only from the active agent handoff (`get_goal`, `create_goal`, `update_goal`) and never overwrite a different active Codex goal. Ultragoal does not call `/goal clear`; for multiple sequential ultragoal runs in one Codex session/thread, manually clear the completed Codex goal in the UI before creating the next aggregate goal."
1505
1530
  : null;
1506
- const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState);
1531
+ const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { cwd });
1507
1532
  const combinedTransitionMessage = (() => {
1508
1533
  if (!skillState?.transition_message)
1509
1534
  return null;
@@ -2012,8 +2037,10 @@ function readPayloadThreadId(payload) {
2012
2037
  function readPayloadTurnId(payload) {
2013
2038
  return safeString(payload.turn_id ?? payload.turnId).trim();
2014
2039
  }
2015
- async function resolveInternalSessionIdForPayload(cwd, payloadSessionId) {
2016
- const currentSession = await readUsableSessionState(cwd);
2040
+ async function resolveInternalSessionIdForPayload(cwd, payloadSessionId, stateDir) {
2041
+ const currentSession = stateDir
2042
+ ? await readUsableSessionStateFromStateDir(cwd, stateDir)
2043
+ : await readUsableSessionState(cwd);
2017
2044
  const canonicalSessionId = safeString(currentSession?.session_id).trim();
2018
2045
  if (!canonicalSessionId)
2019
2046
  return payloadSessionId;
@@ -2026,6 +2053,19 @@ async function resolveInternalSessionIdForPayload(cwd, payloadSessionId) {
2026
2053
  return canonicalSessionId;
2027
2054
  return payloadSessionId;
2028
2055
  }
2056
+ async function readUsableSessionStateFromStateDir(cwd, stateDir) {
2057
+ const sessionPath = join(stateDir, "session.json");
2058
+ if (!existsSync(sessionPath))
2059
+ return null;
2060
+ try {
2061
+ const content = await readFile(sessionPath, "utf-8");
2062
+ const state = JSON.parse(content);
2063
+ return isSessionStateUsable(state, cwd) ? state : null;
2064
+ }
2065
+ catch {
2066
+ return null;
2067
+ }
2068
+ }
2029
2069
  async function readStopSessionPinnedState(fileName, cwd, sessionId, stateDir) {
2030
2070
  const statePath = stateDir && sessionId
2031
2071
  ? join(stateDir, "sessions", sessionId, fileName)
@@ -2054,7 +2094,8 @@ const PLANNING_MODE_IMPLEMENTATION_TOOL_NAMES = new Set([
2054
2094
  ]);
2055
2095
  const DEEP_INTERVIEW_IMPLEMENTATION_TOOL_NAMES = PLANNING_MODE_IMPLEMENTATION_TOOL_NAMES;
2056
2096
  const RALPLAN_EXECUTION_HANDOFF_SKILLS = new Set([
2057
- "autopilot",
2097
+ // Autopilot is intentionally excluded: it supervises planning phases such as
2098
+ // ralplan/replan and is not by itself an execution authorization.
2058
2099
  "autoresearch",
2059
2100
  "ralph",
2060
2101
  "team",
@@ -2091,7 +2132,7 @@ function isActiveAutopilotRalplanPhase(state) {
2091
2132
  if (mode && mode !== "autopilot")
2092
2133
  return false;
2093
2134
  const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
2094
- return phase === "ralplan";
2135
+ return phase === "ralplan" || phase === "replan" || phase === "autopilot:replan";
2095
2136
  }
2096
2137
  function hasExplicitExecutionHandoffSkill(state, sessionId, threadId) {
2097
2138
  return listActiveSkills(state ?? {}).some((entry) => (RALPLAN_EXECUTION_HANDOFF_SKILLS.has(entry.skill)
@@ -2176,7 +2217,7 @@ async function readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionI
2176
2217
  ? await readVisibleSkillActiveStateForStateDir(stateDir, sessionId)
2177
2218
  : await readSkillActiveState(join(stateDir, SKILL_ACTIVE_STATE_FILE));
2178
2219
  if (!canonicalState)
2179
- return modeState;
2220
+ return null;
2180
2221
  const hasActiveDeepInterviewSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "deep-interview"
2181
2222
  && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
2182
2223
  return hasActiveDeepInterviewSkill ? modeState : null;
@@ -2192,7 +2233,7 @@ async function readActiveRalplanStateForPreToolUse(cwd, stateDir, sessionId, thr
2192
2233
  if (hasExplicitExecutionHandoffSkill(canonicalState, sessionId, threadId))
2193
2234
  return null;
2194
2235
  if (!canonicalState)
2195
- return modeState;
2236
+ return null;
2196
2237
  const hasActiveRalplanSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "ralplan"
2197
2238
  && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
2198
2239
  if (hasActiveRalplanSkill)
@@ -2209,7 +2250,7 @@ async function readActiveRalplanStateForPreToolUse(cwd, stateDir, sessionId, thr
2209
2250
  if (terminalAutopilotRunState)
2210
2251
  return null;
2211
2252
  if (!canonicalState)
2212
- return autopilotState;
2253
+ return null;
2213
2254
  const hasActiveAutopilotSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "autopilot"
2214
2255
  && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
2215
2256
  return hasActiveAutopilotSkill ? autopilotState : null;
@@ -2222,8 +2263,8 @@ function isAllowedRalplanBashWrite(cwd, command) {
2222
2263
  const targets = extractDeepInterviewCommandWriteTargets(command);
2223
2264
  return targets.length > 0 && targets.every((target) => isAllowedRalplanArtifactPath(cwd, target));
2224
2265
  }
2225
- async function buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir) {
2226
- const sessionId = readPayloadSessionId(payload);
2266
+ async function buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir, resolvedSessionId) {
2267
+ const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim();
2227
2268
  const threadId = readPayloadThreadId(payload);
2228
2269
  const activeState = await readActiveRalplanStateForPreToolUse(cwd, stateDir, sessionId, threadId);
2229
2270
  if (!activeState)
@@ -2242,17 +2283,25 @@ async function buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir) {
2242
2283
  if (!blocked)
2243
2284
  return null;
2244
2285
  const phase = formatPhase(activeState.current_phase ?? activeState.currentPhase, "planning");
2286
+ const activeMode = safeString(activeState.mode).trim().toLowerCase();
2287
+ const planningModeLabel = activeMode === "autopilot" ? "Autopilot planning" : "Ralplan";
2288
+ const planningModeDescription = activeMode === "autopilot"
2289
+ ? "Autopilot is supervising a planning phase"
2290
+ : "Ralplan is consensus-planning mode";
2245
2291
  return {
2246
2292
  decision: "block",
2247
- reason: `Ralplan is active (phase: ${phase}); implementation/write tools are blocked until an explicit execution handoff workflow is activated.`,
2293
+ reason: `${planningModeLabel} is active (phase: ${phase}); implementation/write tools are blocked until an explicit execution handoff workflow is activated.`,
2248
2294
  hookSpecificOutput: {
2249
2295
  hookEventName: "PreToolUse",
2250
- additionalContext: "Ralplan is consensus-planning mode. Write only planning artifacts under `.omx/context/`, `.omx/plans/`, `.omx/specs/`, or required `.omx/state/` files. Do not edit implementation files or run implementation-focused writes from ralplan. To execute, first process an explicit handoff such as `$ultragoal`, `$team`, or `$ralph`, which must emit terminal ralplan state before implementation begins.",
2296
+ additionalContext: `${planningModeDescription}. `
2297
+ + "Write only planning artifacts under `.omx/context/`, `.omx/plans/`, `.omx/specs/`, or required `.omx/state/` files. "
2298
+ + "Do not edit implementation files or run implementation-focused writes from planning phases. "
2299
+ + `To execute, first process an explicit handoff such as ${formatExecutionHandoffList(cwd)}, which must emit terminal planning state before implementation begins.`,
2251
2300
  },
2252
2301
  };
2253
2302
  }
2254
- async function buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir) {
2255
- const sessionId = readPayloadSessionId(payload);
2303
+ async function buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir, resolvedSessionId) {
2304
+ const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim();
2256
2305
  const threadId = readPayloadThreadId(payload);
2257
2306
  const activeState = await readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionId, threadId);
2258
2307
  if (!activeState)
@@ -2276,7 +2325,7 @@ async function buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir
2276
2325
  reason: `Deep-interview is active (phase: ${phase}); implementation/write tools are blocked until an explicit handoff workflow is activated.`,
2277
2326
  hookSpecificOutput: {
2278
2327
  hookEventName: "PreToolUse",
2279
- additionalContext: "Deep-interview is requirements/spec mode. Treat detailed user answers as interview/spec material, not implicit implementation authorization. You may write only deep-interview artifacts under `.omx/context/`, `.omx/interviews/`, `.omx/specs/`, or required `.omx/state/` files. To implement, first ask for or process an explicit transition such as `$ralplan`, `$autopilot`, `$ralph`, `$team`, or `$ultragoal`.",
2328
+ additionalContext: `Deep-interview is requirements/spec mode. Treat detailed user answers as interview/spec material, not implicit implementation authorization. You may write only deep-interview artifacts under \`.omx/context/\`, \`.omx/interviews/\`, \`.omx/specs/\`, or required \`.omx/state/\` files. To implement, first ask for or process an explicit transition such as \`$ralplan\`, \`$autopilot\`, ${formatExecutionHandoffList(cwd)}.`,
2280
2329
  },
2281
2330
  };
2282
2331
  }
@@ -2465,7 +2514,7 @@ async function reconcileStaleRootSkillActiveStateForStop(cwd, stateDir, sessionI
2465
2514
  }
2466
2515
  await writeFile(rootPath, JSON.stringify(nextRoot, null, 2));
2467
2516
  }
2468
- function buildRalplanContinuationStatus(blocker, activeSubagentCount) {
2517
+ function buildRalplanContinuationStatus(blocker, activeSubagentCount, cwd) {
2469
2518
  const phase = blocker.phase || "planning";
2470
2519
  const artifact = blocker.latestPlanPath
2471
2520
  ? ` Artifact: ${blocker.latestPlanPath}.`
@@ -2491,7 +2540,7 @@ function buildRalplanContinuationStatus(blocker, activeSubagentCount) {
2491
2540
  };
2492
2541
  }
2493
2542
  const completeHint = blocker.planningComplete
2494
- ? " The planning artifacts are present; if consensus is approved, emit terminal ralplan complete/approved handoff state and stop planning. Implementation must wait for an explicit $ultragoal, $team, or $ralph handoff."
2543
+ ? ` The planning artifacts are present; if consensus is approved, emit terminal ralplan complete/approved handoff state and stop planning. Implementation must wait for an explicit ${formatExecutionHandoffList(cwd).replaceAll("`", "")} handoff.`
2495
2544
  : "";
2496
2545
  return {
2497
2546
  reason: `Status: continue_from_artifact — ralplan is still active (phase: ${phase}) and has not emitted a terminal complete/paused/waiting status. Continue from the current ralplan artifact, resolve any review ambiguity conservatively or ask the user if needed, and proceed to the next planning/review step before stopping; do not begin implementation from ralplan.${artifact}${completeHint}`,
@@ -2650,6 +2699,9 @@ function readIsoTimeMs(value) {
2650
2699
  return Number.isFinite(parsed) ? parsed : null;
2651
2700
  }
2652
2701
  async function maybeBuildOrdinaryStopNoProgressOutput(payload, stateDir, canonicalSessionId) {
2702
+ const lastAssistantMessage = safeString(payload.last_assistant_message ?? payload.lastAssistantMessage).trim();
2703
+ if (!lastAssistantMessage)
2704
+ return null;
2653
2705
  const statePath = join(stateDir, NATIVE_STOP_STATE_FILE);
2654
2706
  const state = await readJsonIfExists(statePath) ?? {};
2655
2707
  const sessions = safeObject(state.sessions);
@@ -2679,9 +2731,6 @@ async function maybeBuildOrdinaryStopNoProgressOutput(payload, stateDir, canonic
2679
2731
  };
2680
2732
  await mkdir(stateDir, { recursive: true });
2681
2733
  await writeFile(statePath, JSON.stringify({ ...state, sessions }, null, 2));
2682
- const stopHookActive = payload.stop_hook_active === true || payload.stopHookActive === true;
2683
- if (!stopHookActive)
2684
- return null;
2685
2734
  const maxRepeats = parseBoundedPositiveInteger(process.env.OMX_NATIVE_STOP_NO_PROGRESS_MAX_REPEATS, ORDINARY_STOP_NO_PROGRESS_DEFAULT_MAX_REPEATS);
2686
2735
  const idleMs = parseBoundedNonNegativeInteger(process.env.OMX_NATIVE_STOP_NO_PROGRESS_IDLE_MS, ORDINARY_STOP_NO_PROGRESS_DEFAULT_IDLE_MS);
2687
2736
  const firstSeenMs = readIsoTimeMs(firstSeenAt) ?? Date.now();
@@ -2811,7 +2860,7 @@ async function buildSkillStopOutput(cwd, stateDir, sessionId, threadId) {
2811
2860
  const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
2812
2861
  const activeSubagentCount = subagentSummary?.activeSubagentThreadIds.length ?? 0;
2813
2862
  if (blocker.skill === "ralplan") {
2814
- const status = buildRalplanContinuationStatus(blocker, activeSubagentCount);
2863
+ const status = buildRalplanContinuationStatus(blocker, activeSubagentCount, cwd);
2815
2864
  return {
2816
2865
  decision: "block",
2817
2866
  reason: status.reason,
@@ -3076,7 +3125,9 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
3076
3125
  // Native hooks must use the same authoritative runtime state root as HUD/MCP
3077
3126
  // when boxed/team roots are active; do not bypass it with cwd/.omx/state.
3078
3127
  const stateDir = getBaseStateDir(cwd);
3079
- await mkdir(stateDir, { recursive: true });
3128
+ if (hookEventName !== "Stop") {
3129
+ await mkdir(stateDir, { recursive: true });
3130
+ }
3080
3131
  const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
3081
3132
  let skillState = null;
3082
3133
  let triageAdditionalContext = null;
@@ -3237,12 +3288,14 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
3237
3288
  triageAdditionalContext = null;
3238
3289
  }
3239
3290
  }
3291
+ const skipHudReconcileForDoctorSmoke = process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE === "1";
3240
3292
  const skipHudReconcileForTeamWorkerPane = !isSubagentPromptSubmit
3241
3293
  && await isConfirmedTeamWorkerPromptSubmitPane(cwd).catch(() => false);
3242
- if (!skipHudReconcileForTeamWorkerPane) {
3294
+ if (!skipHudReconcileForDoctorSmoke && !skipHudReconcileForTeamWorkerPane) {
3243
3295
  const reconcileHudForPromptSubmitFn = options.reconcileHudForPromptSubmitFn ?? reconcileHudForPromptSubmit;
3244
3296
  const hudSessionId = resolveHudReconcileSessionId(currentSessionState, canonicalSessionId, sessionIdForState);
3245
- await reconcileHudForPromptSubmitFn(cwd, { sessionId: hudSessionId }).catch(() => { });
3297
+ const hudSessionIds = resolveHudReconcileSessionIds(currentSessionState, canonicalSessionId, sessionIdForState, nativeSessionId);
3298
+ await reconcileHudForPromptSubmitFn(cwd, { sessionId: hudSessionId, sessionIds: hudSessionIds }).catch(() => { });
3246
3299
  }
3247
3300
  }
3248
3301
  if (omxEventName && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
@@ -3298,8 +3351,12 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
3298
3351
  }
3299
3352
  }
3300
3353
  else if (hookEventName === "PreToolUse") {
3301
- outputJson = await buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir)
3302
- ?? await buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir)
3354
+ const payloadSessionId = readPayloadSessionId(payload);
3355
+ const preToolUseSessionId = payloadSessionId
3356
+ ? await resolveInternalSessionIdForPayload(cwd, payloadSessionId, stateDir)
3357
+ : "";
3358
+ outputJson = await buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
3359
+ ?? await buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
3303
3360
  ?? buildNativePreToolUseOutput(payload);
3304
3361
  }
3305
3362
  else if (hookEventName === "PostToolUse") {