oh-my-codex 0.16.3 → 0.16.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 (278) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/README.md +3 -3
  4. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +9 -0
  5. package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -1
  6. package/dist/cli/__tests__/cleanup.test.js +27 -0
  7. package/dist/cli/__tests__/cleanup.test.js.map +1 -1
  8. package/dist/cli/__tests__/codex-plugin-layout.test.js +7 -5
  9. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  10. package/dist/cli/__tests__/doctor-warning-copy.test.js +101 -6
  11. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  12. package/dist/cli/__tests__/index.test.js +131 -2
  13. package/dist/cli/__tests__/index.test.js.map +1 -1
  14. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +2 -0
  15. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  16. package/dist/cli/__tests__/ralph.test.js +47 -0
  17. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  18. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +2 -2
  19. package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
  20. package/dist/cli/__tests__/setup-install-mode.test.js +272 -26
  21. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  22. package/dist/cli/__tests__/setup-refresh.test.js +85 -3
  23. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  24. package/dist/cli/__tests__/setup-scope.test.js +1 -1
  25. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  26. package/dist/cli/__tests__/setup-skills-overwrite.test.js +2 -1
  27. package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
  28. package/dist/cli/__tests__/team.test.js +108 -0
  29. package/dist/cli/__tests__/team.test.js.map +1 -1
  30. package/dist/cli/__tests__/ultragoal.test.js +69 -0
  31. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  32. package/dist/cli/__tests__/uninstall.test.js +54 -8
  33. package/dist/cli/__tests__/uninstall.test.js.map +1 -1
  34. package/dist/cli/cleanup.d.ts.map +1 -1
  35. package/dist/cli/cleanup.js +8 -4
  36. package/dist/cli/cleanup.js.map +1 -1
  37. package/dist/cli/codex-feature-probe.d.ts +9 -0
  38. package/dist/cli/codex-feature-probe.d.ts.map +1 -0
  39. package/dist/cli/codex-feature-probe.js +28 -0
  40. package/dist/cli/codex-feature-probe.js.map +1 -0
  41. package/dist/cli/doctor.d.ts +1 -0
  42. package/dist/cli/doctor.d.ts.map +1 -1
  43. package/dist/cli/doctor.js +152 -17
  44. package/dist/cli/doctor.js.map +1 -1
  45. package/dist/cli/index.d.ts +9 -2
  46. package/dist/cli/index.d.ts.map +1 -1
  47. package/dist/cli/index.js +135 -17
  48. package/dist/cli/index.js.map +1 -1
  49. package/dist/cli/mcp-parity.js +8 -8
  50. package/dist/cli/mcp-parity.js.map +1 -1
  51. package/dist/cli/plugin-marketplace.d.ts +3 -0
  52. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  53. package/dist/cli/plugin-marketplace.js +88 -0
  54. package/dist/cli/plugin-marketplace.js.map +1 -1
  55. package/dist/cli/ralph.d.ts.map +1 -1
  56. package/dist/cli/ralph.js +21 -0
  57. package/dist/cli/ralph.js.map +1 -1
  58. package/dist/cli/setup-preferences.d.ts +4 -0
  59. package/dist/cli/setup-preferences.d.ts.map +1 -1
  60. package/dist/cli/setup-preferences.js +7 -0
  61. package/dist/cli/setup-preferences.js.map +1 -1
  62. package/dist/cli/setup.d.ts +5 -3
  63. package/dist/cli/setup.d.ts.map +1 -1
  64. package/dist/cli/setup.js +114 -44
  65. package/dist/cli/setup.js.map +1 -1
  66. package/dist/cli/ultragoal.d.ts +1 -1
  67. package/dist/cli/ultragoal.d.ts.map +1 -1
  68. package/dist/cli/ultragoal.js +64 -5
  69. package/dist/cli/ultragoal.js.map +1 -1
  70. package/dist/cli/uninstall.d.ts +2 -0
  71. package/dist/cli/uninstall.d.ts.map +1 -1
  72. package/dist/cli/uninstall.js +12 -3
  73. package/dist/cli/uninstall.js.map +1 -1
  74. package/dist/config/__tests__/codex-feature-flags.test.d.ts +2 -0
  75. package/dist/config/__tests__/codex-feature-flags.test.d.ts.map +1 -0
  76. package/dist/config/__tests__/codex-feature-flags.test.js +35 -0
  77. package/dist/config/__tests__/codex-feature-flags.test.js.map +1 -0
  78. package/dist/config/__tests__/codex-hooks.test.js +7 -0
  79. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  80. package/dist/config/__tests__/generator-idempotent.test.js +70 -9
  81. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  82. package/dist/config/__tests__/generator-notify.test.js +116 -11
  83. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  84. package/dist/config/__tests__/wiki-config-contract.test.js +6 -3
  85. package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -1
  86. package/dist/config/codex-feature-flags.d.ts +21 -0
  87. package/dist/config/codex-feature-flags.d.ts.map +1 -0
  88. package/dist/config/codex-feature-flags.js +56 -0
  89. package/dist/config/codex-feature-flags.js.map +1 -0
  90. package/dist/config/codex-hooks.d.ts +2 -0
  91. package/dist/config/codex-hooks.d.ts.map +1 -1
  92. package/dist/config/codex-hooks.js +25 -3
  93. package/dist/config/codex-hooks.js.map +1 -1
  94. package/dist/config/generator.d.ts +11 -2
  95. package/dist/config/generator.d.ts.map +1 -1
  96. package/dist/config/generator.js +221 -123
  97. package/dist/config/generator.js.map +1 -1
  98. package/dist/config/omx-first-party-mcp.d.ts +3 -1
  99. package/dist/config/omx-first-party-mcp.d.ts.map +1 -1
  100. package/dist/config/omx-first-party-mcp.js +2 -2
  101. package/dist/config/omx-first-party-mcp.js.map +1 -1
  102. package/dist/hooks/__tests__/keyword-detector.test.js +92 -2
  103. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  104. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +125 -1
  105. package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
  106. package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts +2 -0
  107. package/dist/hooks/__tests__/skill-catalog-hygiene.test.d.ts.map +1 -0
  108. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js +84 -0
  109. package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -0
  110. package/dist/hooks/agents-overlay.js +2 -2
  111. package/dist/hooks/agents-overlay.js.map +1 -1
  112. package/dist/hooks/keyword-detector.d.ts +1 -0
  113. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  114. package/dist/hooks/keyword-detector.js +7 -5
  115. package/dist/hooks/keyword-detector.js.map +1 -1
  116. package/dist/hud/__tests__/state.test.js +164 -0
  117. package/dist/hud/__tests__/state.test.js.map +1 -1
  118. package/dist/hud/state.d.ts.map +1 -1
  119. package/dist/hud/state.js +4 -5
  120. package/dist/hud/state.js.map +1 -1
  121. package/dist/mcp/__tests__/state-paths.test.js +61 -0
  122. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  123. package/dist/mcp/__tests__/state-server.test.js +166 -0
  124. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  125. package/dist/mcp/state-paths.d.ts.map +1 -1
  126. package/dist/mcp/state-paths.js +23 -2
  127. package/dist/mcp/state-paths.js.map +1 -1
  128. package/dist/modes/__tests__/base-session-scope.test.js +22 -0
  129. package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
  130. package/dist/modes/__tests__/base-tmux-pane.test.js +57 -26
  131. package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
  132. package/dist/modes/base.d.ts.map +1 -1
  133. package/dist/modes/base.js +5 -0
  134. package/dist/modes/base.js.map +1 -1
  135. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts +2 -0
  136. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.d.ts.map +1 -0
  137. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js +316 -0
  138. package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -0
  139. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts +2 -0
  140. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.d.ts.map +1 -0
  141. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js +481 -0
  142. package/dist/planning/__tests__/approved-launch-hint-lineage-matrix.test.js.map +1 -0
  143. package/dist/planning/__tests__/artifacts.test.js +533 -4
  144. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  145. package/dist/planning/__tests__/context-pack-status.test.js +524 -0
  146. package/dist/planning/__tests__/context-pack-status.test.js.map +1 -1
  147. package/dist/planning/__tests__/markdown-structure.test.d.ts +2 -0
  148. package/dist/planning/__tests__/markdown-structure.test.d.ts.map +1 -0
  149. package/dist/planning/__tests__/markdown-structure.test.js +459 -0
  150. package/dist/planning/__tests__/markdown-structure.test.js.map +1 -0
  151. package/dist/planning/__tests__/ready-context-pack-role-refs.test.js +523 -1
  152. package/dist/planning/__tests__/ready-context-pack-role-refs.test.js.map +1 -1
  153. package/dist/planning/artifacts.d.ts +1 -1
  154. package/dist/planning/artifacts.d.ts.map +1 -1
  155. package/dist/planning/artifacts.js +227 -28
  156. package/dist/planning/artifacts.js.map +1 -1
  157. package/dist/planning/context-pack-status.d.ts +25 -0
  158. package/dist/planning/context-pack-status.d.ts.map +1 -1
  159. package/dist/planning/context-pack-status.js +272 -31
  160. package/dist/planning/context-pack-status.js.map +1 -1
  161. package/dist/planning/markdown-structure.d.ts +20 -0
  162. package/dist/planning/markdown-structure.d.ts.map +1 -0
  163. package/dist/planning/markdown-structure.js +137 -0
  164. package/dist/planning/markdown-structure.js.map +1 -0
  165. package/dist/ralph/__tests__/completion-audit.test.d.ts +2 -0
  166. package/dist/ralph/__tests__/completion-audit.test.d.ts.map +1 -0
  167. package/dist/ralph/__tests__/completion-audit.test.js +121 -0
  168. package/dist/ralph/__tests__/completion-audit.test.js.map +1 -0
  169. package/dist/ralph/completion-audit.d.ts +8 -0
  170. package/dist/ralph/completion-audit.d.ts.map +1 -0
  171. package/dist/ralph/completion-audit.js +99 -0
  172. package/dist/ralph/completion-audit.js.map +1 -0
  173. package/dist/scripts/__tests__/codex-native-hook.test.js +220 -13
  174. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  175. package/dist/scripts/__tests__/notify-dispatcher.test.d.ts +2 -0
  176. package/dist/scripts/__tests__/notify-dispatcher.test.d.ts.map +1 -0
  177. package/dist/scripts/__tests__/notify-dispatcher.test.js +126 -0
  178. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -0
  179. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  180. package/dist/scripts/codex-native-hook.js +133 -54
  181. package/dist/scripts/codex-native-hook.js.map +1 -1
  182. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  183. package/dist/scripts/codex-native-pre-post.js +4 -2
  184. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  185. package/dist/scripts/notify-dispatcher.js +30 -1
  186. package/dist/scripts/notify-dispatcher.js.map +1 -1
  187. package/dist/scripts/notify-hook.js +3 -1
  188. package/dist/scripts/notify-hook.js.map +1 -1
  189. package/dist/state/__tests__/workflow-transition.test.js +102 -27
  190. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  191. package/dist/state/operations.d.ts.map +1 -1
  192. package/dist/state/operations.js +9 -3
  193. package/dist/state/operations.js.map +1 -1
  194. package/dist/state/skill-active.d.ts +7 -0
  195. package/dist/state/skill-active.d.ts.map +1 -1
  196. package/dist/state/skill-active.js +25 -8
  197. package/dist/state/skill-active.js.map +1 -1
  198. package/dist/state/workflow-transition-reconcile.d.ts +1 -0
  199. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  200. package/dist/state/workflow-transition-reconcile.js +22 -15
  201. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  202. package/dist/state/workflow-transition.js +3 -3
  203. package/dist/state/workflow-transition.js.map +1 -1
  204. package/dist/team/__tests__/approved-execution.test.js +39 -0
  205. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  206. package/dist/team/__tests__/runtime.test.js +5 -0
  207. package/dist/team/__tests__/runtime.test.js.map +1 -1
  208. package/dist/team/__tests__/scaling.test.js +497 -2
  209. package/dist/team/__tests__/scaling.test.js.map +1 -1
  210. package/dist/team/__tests__/state-root.test.js +1 -1
  211. package/dist/team/__tests__/state-root.test.js.map +1 -1
  212. package/dist/team/__tests__/worker-bootstrap.test.js +8 -0
  213. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  214. package/dist/team/approved-execution.d.ts.map +1 -1
  215. package/dist/team/approved-execution.js +3 -0
  216. package/dist/team/approved-execution.js.map +1 -1
  217. package/dist/team/scaling.d.ts.map +1 -1
  218. package/dist/team/scaling.js +43 -0
  219. package/dist/team/scaling.js.map +1 -1
  220. package/dist/team/state-root.d.ts.map +1 -1
  221. package/dist/team/state-root.js +4 -0
  222. package/dist/team/state-root.js.map +1 -1
  223. package/dist/team/state.d.ts.map +1 -1
  224. package/dist/team/state.js +2 -6
  225. package/dist/team/state.js.map +1 -1
  226. package/dist/ultragoal/__tests__/artifacts.test.js +124 -1
  227. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  228. package/dist/ultragoal/__tests__/docs-contract.test.js +21 -0
  229. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
  230. package/dist/ultragoal/artifacts.d.ts +44 -2
  231. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  232. package/dist/ultragoal/artifacts.js +197 -13
  233. package/dist/ultragoal/artifacts.js.map +1 -1
  234. package/dist/wiki/lifecycle.js +1 -1
  235. package/dist/wiki/lifecycle.js.map +1 -1
  236. package/package.json +1 -1
  237. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  238. package/plugins/oh-my-codex/.mcp.json +5 -5
  239. package/plugins/oh-my-codex/skills/analyze/SKILL.md +0 -2
  240. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +2 -2
  241. package/plugins/oh-my-codex/skills/code-review/SKILL.md +1 -3
  242. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +5 -7
  243. package/plugins/oh-my-codex/skills/doctor/SKILL.md +2 -2
  244. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +3 -3
  245. package/plugins/oh-my-codex/skills/pipeline/SKILL.md +3 -3
  246. package/plugins/oh-my-codex/skills/plan/SKILL.md +3 -6
  247. package/plugins/oh-my-codex/skills/ralph/SKILL.md +9 -10
  248. package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +36 -3
  249. package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +21 -24
  250. package/plugins/oh-my-codex/skills/ultrawork/SKILL.md +8 -8
  251. package/plugins/oh-my-codex/skills/wiki/SKILL.md +13 -13
  252. package/skills/analyze/SKILL.md +0 -2
  253. package/skills/ask-claude/SKILL.md +5 -3
  254. package/skills/ask-gemini/SKILL.md +5 -3
  255. package/skills/autopilot/SKILL.md +2 -2
  256. package/skills/code-review/SKILL.md +1 -3
  257. package/skills/deep-interview/SKILL.md +5 -7
  258. package/skills/doctor/SKILL.md +2 -2
  259. package/skills/ecomode/SKILL.md +105 -1
  260. package/skills/frontend-ui-ux/SKILL.md +4 -26
  261. package/skills/git-master/SKILL.md +2 -4
  262. package/skills/omx-setup/SKILL.md +3 -3
  263. package/skills/pipeline/SKILL.md +3 -3
  264. package/skills/plan/SKILL.md +3 -6
  265. package/skills/ralph/SKILL.md +9 -10
  266. package/skills/swarm/SKILL.md +5 -3
  267. package/skills/tdd/SKILL.md +95 -1
  268. package/skills/ultragoal/SKILL.md +36 -3
  269. package/skills/ultraqa/SKILL.md +21 -24
  270. package/skills/ultrawork/SKILL.md +8 -8
  271. package/skills/web-clone/SKILL.md +348 -1
  272. package/skills/wiki/SKILL.md +13 -13
  273. package/src/scripts/__tests__/codex-native-hook.test.ts +231 -13
  274. package/src/scripts/__tests__/notify-dispatcher.test.ts +153 -0
  275. package/src/scripts/codex-native-hook.ts +160 -43
  276. package/src/scripts/codex-native-pre-post.ts +4 -1
  277. package/src/scripts/notify-dispatcher.ts +40 -1
  278. package/src/scripts/notify-hook.ts +3 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=notify-dispatcher.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify-dispatcher.test.d.ts","sourceRoot":"","sources":["../../../src/scripts/__tests__/notify-dispatcher.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,126 @@
1
+ import { describe, it } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { existsSync } from "node:fs";
4
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+ import { join } from "node:path";
7
+ import { spawnSync } from "node:child_process";
8
+ function runDispatcher(metadataPath) {
9
+ const dispatcherScript = join(process.cwd(), "dist", "scripts", "notify-dispatcher.js");
10
+ const result = spawnSync(process.execPath, [dispatcherScript, "--metadata", metadataPath, JSON.stringify({ type: "test" })], { encoding: "utf-8", windowsHide: true });
11
+ assert.equal(result.status, 0, result.stderr || result.stdout);
12
+ }
13
+ describe("notify dispatcher previousNotify guard", () => {
14
+ it("skips stale OMX-managed previousNotify dispatcher entries", () => {
15
+ const wd = mkdtempSync(join(tmpdir(), "omx-notify-dispatcher-stale-"));
16
+ try {
17
+ const oldPkgScripts = join(wd, "global", "oh-my-codex", "dist", "scripts");
18
+ mkdirSync(oldPkgScripts, { recursive: true });
19
+ const stalePreviousMarker = join(wd, "stale-previous-ran");
20
+ const omxMarker = join(wd, "omx-ran");
21
+ const staleDispatcher = join(oldPkgScripts, "notify-dispatcher.js");
22
+ const omxHook = join(wd, "current-notify-hook.js");
23
+ writeFileSync(staleDispatcher, `import { writeFileSync } from "node:fs"; writeFileSync(${JSON.stringify(stalePreviousMarker)}, "ran");\n`);
24
+ writeFileSync(omxHook, `import { writeFileSync } from "node:fs"; writeFileSync(${JSON.stringify(omxMarker)}, "ran");\n`);
25
+ const metadataPath = join(wd, "notify-dispatch.json");
26
+ writeFileSync(metadataPath, JSON.stringify({
27
+ managedBy: "oh-my-codex",
28
+ version: 1,
29
+ previousNotify: [process.execPath, staleDispatcher, "--metadata", metadataPath],
30
+ omxNotify: [process.execPath, omxHook],
31
+ dispatcherNotify: [
32
+ process.execPath,
33
+ join(process.cwd(), "dist", "scripts", "notify-dispatcher.js"),
34
+ "--metadata",
35
+ metadataPath,
36
+ ],
37
+ }));
38
+ runDispatcher(metadataPath);
39
+ assert.equal(existsSync(stalePreviousMarker), false);
40
+ assert.equal(readFileSync(omxMarker, "utf-8"), "ran");
41
+ }
42
+ finally {
43
+ rmSync(wd, { recursive: true, force: true });
44
+ }
45
+ });
46
+ it("skips stale OMX-managed previousNotify dispatcher entries behind node flags", () => {
47
+ const wd = mkdtempSync(join(tmpdir(), "omx-notify-dispatcher-flagged-stale-"));
48
+ try {
49
+ const oldPkgScripts = join(wd, "global", "oh-my-codex", "dist", "scripts");
50
+ mkdirSync(oldPkgScripts, { recursive: true });
51
+ const stalePreviousMarker = join(wd, "stale-previous-ran");
52
+ const omxMarker = join(wd, "omx-ran");
53
+ const staleDispatcher = join(oldPkgScripts, "notify-dispatcher.js");
54
+ const omxHook = join(wd, "current-notify-hook.js");
55
+ writeFileSync(staleDispatcher, `import { writeFileSync } from "node:fs"; writeFileSync(${JSON.stringify(stalePreviousMarker)}, "ran");\n`);
56
+ writeFileSync(omxHook, `import { writeFileSync } from "node:fs"; writeFileSync(${JSON.stringify(omxMarker)}, "ran");\n`);
57
+ const metadataPath = join(wd, "notify-dispatch.json");
58
+ writeFileSync(metadataPath, JSON.stringify({
59
+ managedBy: "oh-my-codex",
60
+ version: 1,
61
+ previousNotify: [
62
+ process.execPath,
63
+ "--no-warnings",
64
+ staleDispatcher,
65
+ "--metadata",
66
+ metadataPath,
67
+ ],
68
+ omxNotify: [process.execPath, omxHook],
69
+ }));
70
+ runDispatcher(metadataPath);
71
+ assert.equal(existsSync(stalePreviousMarker), false);
72
+ assert.equal(readFileSync(omxMarker, "utf-8"), "ran");
73
+ }
74
+ finally {
75
+ rmSync(wd, { recursive: true, force: true });
76
+ }
77
+ });
78
+ it("preserves and runs real user previousNotify entries", () => {
79
+ const wd = mkdtempSync(join(tmpdir(), "omx-notify-dispatcher-user-"));
80
+ try {
81
+ const userMarker = join(wd, "user-ran");
82
+ const omxMarker = join(wd, "omx-ran");
83
+ const userScript = join(wd, "user-notify.js");
84
+ const omxHook = join(wd, "current-notify-hook.js");
85
+ writeFileSync(userScript, `import { writeFileSync } from "node:fs"; writeFileSync(${JSON.stringify(userMarker)}, "ran");\n`);
86
+ writeFileSync(omxHook, `import { writeFileSync } from "node:fs"; writeFileSync(${JSON.stringify(omxMarker)}, "ran");\n`);
87
+ const metadataPath = join(wd, "notify-dispatch.json");
88
+ writeFileSync(metadataPath, JSON.stringify({
89
+ managedBy: "oh-my-codex",
90
+ version: 1,
91
+ previousNotify: [process.execPath, userScript],
92
+ omxNotify: [process.execPath, omxHook],
93
+ }));
94
+ runDispatcher(metadataPath);
95
+ assert.equal(readFileSync(userMarker, "utf-8"), "ran");
96
+ assert.equal(readFileSync(omxMarker, "utf-8"), "ran");
97
+ }
98
+ finally {
99
+ rmSync(wd, { recursive: true, force: true });
100
+ }
101
+ });
102
+ it("does not mistake real user notify arguments for managed entrypoints", () => {
103
+ const wd = mkdtempSync(join(tmpdir(), "omx-notify-dispatcher-user-arg-"));
104
+ try {
105
+ const userMarker = join(wd, "user-ran");
106
+ const userScript = join(wd, "user-notify.js");
107
+ writeFileSync(userScript, `import { writeFileSync } from "node:fs"; writeFileSync(${JSON.stringify(userMarker)}, process.argv.slice(2).join("\\n"));\n`);
108
+ const metadataPath = join(wd, "notify-dispatch.json");
109
+ writeFileSync(metadataPath, JSON.stringify({
110
+ managedBy: "oh-my-codex",
111
+ version: 1,
112
+ previousNotify: [
113
+ process.execPath,
114
+ userScript,
115
+ "/opt/homebrew/lib/node_modules/oh-my-codex/dist/scripts/notify-hook.js",
116
+ ],
117
+ }));
118
+ runDispatcher(metadataPath);
119
+ assert.match(readFileSync(userMarker, "utf-8"), /notify-hook\.js/);
120
+ }
121
+ finally {
122
+ rmSync(wd, { recursive: true, force: true });
123
+ }
124
+ });
125
+ });
126
+ //# sourceMappingURL=notify-dispatcher.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify-dispatcher.test.js","sourceRoot":"","sources":["../../../src/scripts/__tests__/notify-dispatcher.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"codex-native-hook.d.ts","sourceRoot":"","sources":["../../src/scripts/codex-native-hook.ts"],"names":[],"mappings":"AAsCA,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,8BAA8B,CAAC;AA4BtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AA4BlE,KAAK,kBAAkB,GACnB,cAAc,GACd,YAAY,GACZ,aAAa,GACb,kBAAkB,GAClB,YAAY,GACZ,aAAa,GACb,MAAM,CAAC;AAEX,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,UAAU,yBAAyB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6BAA6B,CAAC,EAAE,OAAO,2BAA2B,CAAC;CACpE;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACzC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5C;AA6PD,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,kBAAkB,GAAG,IAAI,GACvC,MAAM,GAAG,IAAI,CAmBf;AAqTD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IACP,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/C,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CACzC,GACL,MAAM,GAAG,IAAI,CAuBf;AA+hED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CA+PnC;AAOD,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAGT;AA2ED,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAuC3D"}
1
+ {"version":3,"file":"codex-native-hook.d.ts","sourceRoot":"","sources":["../../src/scripts/codex-native-hook.ts"],"names":[],"mappings":"AAsCA,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,8BAA8B,CAAC;AA4BtC,OAAO,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AA6BlE,KAAK,kBAAkB,GACnB,cAAc,GACd,YAAY,GACZ,aAAa,GACb,kBAAkB,GAClB,YAAY,GACZ,aAAa,GACb,MAAM,CAAC;AAEX,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD,UAAU,yBAAyB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6BAA6B,CAAC,EAAE,OAAO,2BAA2B,CAAC;CACpE;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACzC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5C;AA6PD,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,kBAAkB,GAAG,IAAI,GACvC,MAAM,GAAG,IAAI,CAmBf;AA6XD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IACP,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;IAC/C,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CACzC,GACL,MAAM,GAAG,IAAI,CAuBf;AAwkED,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,gBAAgB,EACzB,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,wBAAwB,CAAC,CAkQnC;AAOD,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAGT;AA2ED,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAuC3D"}
@@ -4,14 +4,14 @@ import { appendFile, mkdir, readFile, readdir, writeFile } from "fs/promises";
4
4
  import { extname, join, relative, resolve } from "path";
5
5
  import { pathToFileURL } from "url";
6
6
  import { readModeState, readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
7
- import { extractSessionIdFromInitializedStatePath, getSkillActiveStatePaths, listActiveSkills, readSkillActiveState, readVisibleSkillActiveState, } from "../state/skill-active.js";
7
+ import { extractSessionIdFromInitializedStatePath, getSkillActiveStatePathsForStateDir, listActiveSkills, readSkillActiveState, readVisibleSkillActiveStateForStateDir, } from "../state/skill-active.js";
8
8
  import { readSubagentSessionSummary, recordSubagentTurnForSession, } from "../subagents/tracker.js";
9
9
  import { resolveCanonicalTeamStateRoot, resolveWorkerNotifyTeamStateRootPath } from "../team/state-root.js";
10
10
  import { appendToLog, isSessionStateUsable, readSessionState, readUsableSessionState, reconcileNativeSessionStart, } from "../hooks/session.js";
11
11
  import { appendTeamEvent, readTeamLeaderAttention, readTeamManifestV2, readTeamPhase, writeTeamLeaderAttention, writeTeamPhase, } from "../team/state.js";
12
12
  import { omxNotepadPath, omxProjectMemoryPath } from "../utils/paths.js";
13
13
  import { findGitLayout } from "../utils/git-layout.js";
14
- import { getStateFilePath, getStatePath } from "../mcp/state-paths.js";
14
+ import { getBaseStateDir, getStateFilePath, getStatePath } from "../mcp/state-paths.js";
15
15
  import { detectKeywords, detectPrimaryKeyword, recordSkillActivation, } from "../hooks/keyword-detector.js";
16
16
  import { detectNativeStopStallPattern, loadAutoNudgeConfig, normalizeAutoNudgeSignatureText, resolveEffectiveAutoNudgeResponse, } from "./notify-hook/auto-nudge.js";
17
17
  import { SLOPPY_FALLBACK_GROUNDING_PATTERNS, SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS, SLOPPY_FALLBACK_PHRASE_PATTERNS, buildNativePostToolUseOutput, buildNativePreToolUseOutput, detectMcpTransportFailure, hasAnyPattern, } from "./codex-native-pre-post.js";
@@ -24,6 +24,7 @@ import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
24
24
  import { onPreCompact as buildWikiPreCompactContext, onSessionStart as buildWikiSessionStartContext, } from "../wiki/lifecycle.js";
25
25
  import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDecision } from "../autoresearch/skill-validation.js";
26
26
  import { readRunState } from "../runtime/run-state.js";
27
+ import { evaluateRalphCompletionAuditEvidence, isRalphCompletePhase } from "../ralph/completion-audit.js";
27
28
  import { getRunContinuationSnapshot, shouldContinueRun } from "../runtime/run-loop.js";
28
29
  import { triagePrompt } from "../hooks/triage-heuristic.js";
29
30
  import { readTriageConfig } from "../hooks/triage-config.js";
@@ -381,15 +382,15 @@ async function readCanonicalTerminalRunStateForStop(cwd, sessionId, mode) {
381
382
  const runRecord = runState;
382
383
  return shouldHonorCanonicalTerminalRunState(runRecord, mode) ? runRecord : null;
383
384
  }
384
- async function isVisibleRalphActiveForSession(cwd, sessionId) {
385
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
385
+ async function isVisibleRalphActiveForSession(stateDir, sessionId) {
386
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
386
387
  if (!canonicalState)
387
388
  return false;
388
389
  return listActiveSkills(canonicalState).some((entry) => (entry.skill === "ralph"
389
390
  && matchesSkillStopContext(entry, canonicalState, sessionId, "")));
390
391
  }
391
- async function hasConsistentRalphSkillActivation(cwd, sessionId) {
392
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
392
+ async function hasConsistentRalphSkillActivation(stateDir, sessionId) {
393
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
393
394
  if (!canonicalState)
394
395
  return true;
395
396
  const initializedMode = safeString(canonicalState.initialized_mode).trim();
@@ -400,8 +401,63 @@ async function hasConsistentRalphSkillActivation(cwd, sessionId) {
400
401
  return false;
401
402
  return true;
402
403
  }
403
- async function readActiveRalphState(stateDir, preferredSessionId, ownerContext) {
404
- const cwd = resolve(stateDir, "..", "..");
404
+ async function readRalphCompletionAuditBlockState(cwd, stateDir, preferredSessionId, ownerContext) {
405
+ const [rawSessionInfo, usableSessionInfo] = await Promise.all([
406
+ readSessionState(cwd),
407
+ readUsableSessionState(cwd),
408
+ ]);
409
+ const currentOmxSessionId = safeString(usableSessionInfo?.session_id).trim();
410
+ const currentNativeSessionId = safeString(usableSessionInfo?.native_session_id).trim();
411
+ const staleCurrentSessionId = rawSessionInfo && !isSessionStateUsable(rawSessionInfo, cwd)
412
+ ? safeString(rawSessionInfo.session_id).trim()
413
+ : "";
414
+ const sessionCandidates = [...new Set([
415
+ safeString(preferredSessionId).trim(),
416
+ currentOmxSessionId,
417
+ ].filter(Boolean))];
418
+ const evaluateCandidate = (state, path, sessionId) => {
419
+ if (!state || state.mode && safeString(state.mode) !== "ralph")
420
+ return null;
421
+ if (!isRalphCompletePhase(state.current_phase ?? state.currentPhase))
422
+ return null;
423
+ if (activeRalphStateMatchesStopOwner(state, {
424
+ sessionId,
425
+ payloadSessionId: safeString(ownerContext?.payloadSessionId).trim(),
426
+ threadId: safeString(ownerContext?.threadId).trim(),
427
+ currentNativeSessionId,
428
+ tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
429
+ }) !== true)
430
+ return null;
431
+ const audit = evaluateRalphCompletionAuditEvidence(state, cwd);
432
+ return audit.complete ? null : { state, path, reason: audit.reason };
433
+ };
434
+ for (const sessionId of sessionCandidates) {
435
+ if (staleCurrentSessionId && sessionId === staleCurrentSessionId)
436
+ continue;
437
+ const sessionScopedPath = getStateFilePath("ralph-state.json", cwd, sessionId);
438
+ const result = evaluateCandidate(await readJsonIfExists(sessionScopedPath), sessionScopedPath, sessionId);
439
+ if (result)
440
+ return result;
441
+ }
442
+ if (sessionCandidates.length > 0)
443
+ return null;
444
+ const directPath = join(stateDir, "ralph-state.json");
445
+ return evaluateCandidate(await readJsonIfExists(directPath), directPath, "");
446
+ }
447
+ async function reopenRalphCompletionAuditBlock(block) {
448
+ const nowIso = new Date().toISOString();
449
+ const next = {
450
+ ...block.state,
451
+ active: true,
452
+ current_phase: "verifying",
453
+ completion_audit_gate: "blocked",
454
+ completion_audit_missing_reason: block.reason,
455
+ completion_audit_blocked_at: nowIso,
456
+ };
457
+ delete next.completed_at;
458
+ await writeFile(block.path, JSON.stringify(next, null, 2));
459
+ }
460
+ async function readActiveRalphState(cwd, stateDir, preferredSessionId, ownerContext) {
405
461
  const [rawSessionInfo, usableSessionInfo] = await Promise.all([
406
462
  readSessionState(cwd),
407
463
  readUsableSessionState(cwd),
@@ -429,7 +485,7 @@ async function readActiveRalphState(stateDir, preferredSessionId, ownerContext)
429
485
  const sessionScoped = await readJsonIfExists(sessionScopedPath);
430
486
  if (sessionScoped?.active === true
431
487
  && isRalphStartingPhase(sessionScoped)
432
- && !(await isVisibleRalphActiveForSession(cwd, sessionId))) {
488
+ && !(await isVisibleRalphActiveForSession(stateDir, sessionId))) {
433
489
  continue;
434
490
  }
435
491
  if (sessionScoped?.active === true
@@ -441,7 +497,7 @@ async function readActiveRalphState(stateDir, preferredSessionId, ownerContext)
441
497
  currentNativeSessionId,
442
498
  tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
443
499
  })
444
- && await hasConsistentRalphSkillActivation(cwd, sessionId)) {
500
+ && await hasConsistentRalphSkillActivation(stateDir, sessionId)) {
445
501
  return { state: sessionScoped, path: sessionScopedPath };
446
502
  }
447
503
  }
@@ -1030,6 +1086,9 @@ function buildNativeOutsideTmuxTeamPromptBlockState(prompt, cwd, payload, sessio
1030
1086
  transition_error: "Codex App/native outside-tmux sessions cannot activate the tmux-only `team` workflow directly. Launch OMX CLI from an attached tmux shell first, then run `omx team ...` there.",
1031
1087
  };
1032
1088
  }
1089
+ function buildSkillStateCliInstruction(mode, statePath) {
1090
+ return `skill: ${mode} activated and initial state initialized at ${statePath}; use CLI-first state updates via \`omx state write/read/clear --input '<json>' --json\`; use omx_state MCP only when explicit MCP compatibility is enabled.`;
1091
+ }
1033
1092
  function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(), payload) {
1034
1093
  if (!prompt)
1035
1094
  return null;
@@ -1089,7 +1148,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1089
1148
  promptPriorityMessage,
1090
1149
  ultragoalPromptActivationNote,
1091
1150
  skillState.initialized_mode && skillState.initialized_state_path
1092
- ? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
1151
+ ? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
1093
1152
  : null,
1094
1153
  teamDetected
1095
1154
  ? buildTeamRuntimeInstruction(cwd, payload)
@@ -1100,7 +1159,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1100
1159
  }
1101
1160
  if (teamDetected) {
1102
1161
  const initializedStateMessage = skillState?.initialized_mode && skillState.initialized_state_path
1103
- ? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
1162
+ ? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
1104
1163
  : null;
1105
1164
  return [
1106
1165
  detectedKeywordMessage,
@@ -1126,7 +1185,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
1126
1185
  ? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
1127
1186
  : null,
1128
1187
  promptPriorityMessage,
1129
- `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`,
1188
+ buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path),
1130
1189
  deepInterviewPromptActivationNote,
1131
1190
  ultraworkPromptActivationNote,
1132
1191
  ultragoalPromptActivationNote,
@@ -1164,7 +1223,7 @@ async function resolveTeamWorkerStopDecision(cwd) {
1164
1223
  || parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_WORKER));
1165
1224
  if (!workerContext)
1166
1225
  return { kind: "unresolved", reason: "missing_worker_context" };
1167
- const blockWorkerStop = (reasonCode, detail, stateDirForDecision = join(cwd, ".omx", "state")) => ({
1226
+ const blockWorkerStop = (reasonCode, detail, stateDirForDecision = getBaseStateDir(cwd)) => ({
1168
1227
  kind: "blocked",
1169
1228
  stateDir: stateDirForDecision,
1170
1229
  workerContext,
@@ -1354,15 +1413,15 @@ async function buildGoalWorkflowReconciliationStopOutput(payload, cwd) {
1354
1413
  systemMessage,
1355
1414
  };
1356
1415
  }
1357
- async function readTeamModeStateForStop(cwd, sessionId) {
1416
+ async function readTeamModeStateForStop(cwd, stateDir, sessionId) {
1358
1417
  const normalizedSessionId = safeString(sessionId).trim();
1359
1418
  if (!normalizedSessionId) {
1360
1419
  return await readModeState("team", cwd);
1361
1420
  }
1362
- const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId);
1421
+ const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId, stateDir);
1363
1422
  if (scopedState)
1364
1423
  return scopedState;
1365
- const rootState = await readJsonIfExists(join(cwd, ".omx", "state", "team-state.json"));
1424
+ const rootState = await readJsonIfExists(join(stateDir, "team-state.json"));
1366
1425
  if (rootState?.active !== true)
1367
1426
  return null;
1368
1427
  const ownerSessionId = safeString(rootState.session_id).trim();
@@ -1375,7 +1434,7 @@ async function buildTeamStopOutput(cwd, sessionId) {
1375
1434
  if (await readCanonicalTerminalRunStateForStop(cwd, sessionId, "team")) {
1376
1435
  return null;
1377
1436
  }
1378
- const teamState = await readTeamModeStateForStop(cwd, sessionId);
1437
+ const teamState = await readTeamModeStateForStop(cwd, getBaseStateDir(cwd), sessionId);
1379
1438
  if (teamState?.active !== true)
1380
1439
  return null;
1381
1440
  const teamName = safeString(teamState.team_name).trim();
@@ -1425,10 +1484,10 @@ function hasReleaseReadinessMode(payload) {
1425
1484
  const mode = safeString(payload.mode).trim().toLowerCase();
1426
1485
  return mode === "release-readiness";
1427
1486
  }
1428
- async function hasReleaseReadinessStopMarker(cwd, sessionId, teamName) {
1487
+ async function hasReleaseReadinessStopMarker(cwd, stateDir, sessionId, teamName) {
1429
1488
  if (!sessionId)
1430
1489
  return false;
1431
- const markerState = await readStopSessionPinnedState("release-readiness-state.json", cwd, sessionId);
1490
+ const markerState = await readStopSessionPinnedState("release-readiness-state.json", cwd, sessionId, stateDir);
1432
1491
  if (markerState?.active !== true || markerState.stable_final_recommendation_emitted !== true) {
1433
1492
  return false;
1434
1493
  }
@@ -1463,8 +1522,10 @@ async function resolveInternalSessionIdForPayload(cwd, payloadSessionId) {
1463
1522
  return canonicalSessionId;
1464
1523
  return payloadSessionId;
1465
1524
  }
1466
- async function readStopSessionPinnedState(fileName, cwd, sessionId) {
1467
- const statePath = getStateFilePath(fileName, cwd, sessionId || undefined);
1525
+ async function readStopSessionPinnedState(fileName, cwd, sessionId, stateDir) {
1526
+ const statePath = stateDir && sessionId
1527
+ ? join(stateDir, "sessions", sessionId, fileName)
1528
+ : getStateFilePath(fileName, cwd, sessionId || undefined);
1468
1529
  return readJsonIfExists(statePath);
1469
1530
  }
1470
1531
  function matchesSkillStopContext(entry, state, sessionId, threadId) {
@@ -1499,8 +1560,8 @@ function modeStateMatchesSkillStopContext(state, cwd, sessionId) {
1499
1560
  }
1500
1561
  return true;
1501
1562
  }
1502
- async function readBlockingSkillForStop(cwd, sessionId, threadId, requiredSkill) {
1503
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
1563
+ async function readBlockingSkillForStop(cwd, stateDir, sessionId, threadId, requiredSkill) {
1564
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
1504
1565
  const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
1505
1566
  const candidateSkills = requiredSkill
1506
1567
  ? [requiredSkill]
@@ -1509,7 +1570,7 @@ async function readBlockingSkillForStop(cwd, sessionId, threadId, requiredSkill)
1509
1570
  const terminalRunState = await readCanonicalTerminalRunStateForStop(cwd, sessionId, skill);
1510
1571
  if (terminalRunState)
1511
1572
  continue;
1512
- const modeState = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId);
1573
+ const modeState = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId, stateDir);
1513
1574
  if (!modeState || modeState.active !== true)
1514
1575
  continue;
1515
1576
  if (!modeStateMatchesSkillStopContext(modeState, cwd, sessionId))
@@ -1557,16 +1618,16 @@ function isTerminalOrInactiveModeState(state) {
1557
1618
  const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
1558
1619
  return phase !== "" && TERMINAL_MODE_PHASES.has(phase);
1559
1620
  }
1560
- async function readSessionScopedModeStateForRootSkill(cwd, skill, sessionIds) {
1621
+ async function readSessionScopedModeStateForRootSkill(cwd, stateDir, skill, sessionIds) {
1561
1622
  for (const sessionId of sessionIds) {
1562
- const state = await readJsonIfExists(getStateFilePath(`${skill}-state.json`, cwd, sessionId));
1623
+ const state = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId, stateDir);
1563
1624
  if (state)
1564
1625
  return state;
1565
1626
  }
1566
1627
  return null;
1567
1628
  }
1568
- async function reconcileStaleRootSkillActiveStateForStop(cwd, sessionId) {
1569
- const { rootPath } = getSkillActiveStatePaths(cwd);
1629
+ async function reconcileStaleRootSkillActiveStateForStop(cwd, stateDir, sessionId) {
1630
+ const { rootPath } = getSkillActiveStatePathsForStateDir(stateDir);
1570
1631
  const rootState = await readSkillActiveState(rootPath);
1571
1632
  if (!rootState?.active)
1572
1633
  return;
@@ -1593,7 +1654,7 @@ async function reconcileStaleRootSkillActiveStateForStop(cwd, sessionId) {
1593
1654
  initializedSessionId,
1594
1655
  safeString(rootState.session_id),
1595
1656
  ]);
1596
- const modeState = await readSessionScopedModeStateForRootSkill(cwd, skill, candidateSessionIds);
1657
+ const modeState = await readSessionScopedModeStateForRootSkill(cwd, stateDir, skill, candidateSessionIds);
1597
1658
  if (isTerminalOrInactiveModeState(modeState)) {
1598
1659
  changed = true;
1599
1660
  continue;
@@ -1652,17 +1713,17 @@ function buildRalplanContinuationStatus(blocker, activeSubagentCount) {
1652
1713
  systemMessage: `OMX ralplan status: continue_from_artifact at phase ${phase}; continue from the current ralplan artifact and finish by stating whether ralplan is complete, paused for review, waiting for input, or still continuing.`,
1653
1714
  };
1654
1715
  }
1655
- async function readStopAutoNudgePhase(cwd, sessionId, threadId) {
1716
+ async function readStopAutoNudgePhase(cwd, stateDir, sessionId, threadId) {
1656
1717
  const normalizedSessionId = sessionId.trim();
1657
1718
  if (normalizedSessionId) {
1658
- const scopedModeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId);
1719
+ const scopedModeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId, stateDir);
1659
1720
  if (scopedModeState?.active === true
1660
1721
  && safeString(scopedModeState.current_phase).trim().toLowerCase() === "intent-first") {
1661
1722
  return "planning";
1662
1723
  }
1663
1724
  }
1664
1725
  else {
1665
- const rootModeState = await readJsonIfExists(join(cwd, ".omx", "state", "deep-interview-state.json"));
1726
+ const rootModeState = await readJsonIfExists(join(stateDir, "deep-interview-state.json"));
1666
1727
  if (rootModeState?.active === true
1667
1728
  && safeString(rootModeState.current_phase).trim().toLowerCase() === "intent-first") {
1668
1729
  return "planning";
@@ -1670,21 +1731,21 @@ async function readStopAutoNudgePhase(cwd, sessionId, threadId) {
1670
1731
  }
1671
1732
  if (!normalizedSessionId)
1672
1733
  return "";
1673
- const canonicalState = await readVisibleSkillActiveState(cwd, normalizedSessionId);
1734
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, normalizedSessionId);
1674
1735
  const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
1675
1736
  const deepInterview = visibleEntries.find((entry) => (entry.skill === "deep-interview"
1676
1737
  && matchesSkillStopContext(entry, canonicalState ?? {}, normalizedSessionId, threadId)));
1677
1738
  if (!deepInterview)
1678
1739
  return "";
1679
- const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId);
1740
+ const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, normalizedSessionId, stateDir);
1680
1741
  if (!modeState || modeState.active !== true)
1681
1742
  return "";
1682
1743
  const modePhase = safeString(modeState.current_phase).trim().toLowerCase();
1683
1744
  return modePhase === "intent-first" ? "planning" : "";
1684
1745
  }
1685
- async function buildDeepInterviewQuestionStopOutput(cwd, sessionId, threadId) {
1746
+ async function buildDeepInterviewQuestionStopOutput(cwd, stateDir, sessionId, threadId) {
1686
1747
  await reconcileDeepInterviewQuestionEnforcementFromAnsweredRecords(cwd, sessionId);
1687
- const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId);
1748
+ const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId, stateDir);
1688
1749
  if (!modeState)
1689
1750
  return null;
1690
1751
  const questionEnforcement = safeObject(modeState.question_enforcement);
@@ -1695,7 +1756,7 @@ async function buildDeepInterviewQuestionStopOutput(cwd, sessionId, threadId) {
1695
1756
  if (TERMINAL_MODE_PHASES.has(phase.toLowerCase()) || phase === "completing") {
1696
1757
  return null;
1697
1758
  }
1698
- const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
1759
+ const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
1699
1760
  if (canonicalState) {
1700
1761
  const blocker = listActiveSkills(canonicalState).find((entry) => (entry.skill === "deep-interview"
1701
1762
  && matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
@@ -1836,8 +1897,8 @@ async function findCanonicalActiveTeamForSession(cwd, sessionId) {
1836
1897
  }
1837
1898
  return null;
1838
1899
  }
1839
- async function resolveActiveTeamNameForStop(cwd, sessionId) {
1840
- const directState = await readTeamModeStateForStop(cwd, sessionId);
1900
+ async function resolveActiveTeamNameForStop(cwd, stateDir, sessionId) {
1901
+ const directState = await readTeamModeStateForStop(cwd, stateDir, sessionId);
1841
1902
  const directTeamName = safeString(directState?.team_name).trim();
1842
1903
  if (directState?.active === true && directTeamName)
1843
1904
  return directTeamName;
@@ -1847,11 +1908,11 @@ async function resolveActiveTeamNameForStop(cwd, sessionId) {
1847
1908
  async function maybeBuildReleaseReadinessFinalizeStopOutput(payload, cwd, stateDir, sessionId) {
1848
1909
  if (!sessionId)
1849
1910
  return { matched: false, output: null };
1850
- const teamName = await resolveActiveTeamNameForStop(cwd, sessionId);
1911
+ const teamName = await resolveActiveTeamNameForStop(cwd, stateDir, sessionId);
1851
1912
  if (!teamName)
1852
1913
  return { matched: false, output: null };
1853
1914
  const explicitReleaseReadinessContext = hasReleaseReadinessMode(payload)
1854
- || await hasReleaseReadinessStopMarker(cwd, sessionId, teamName);
1915
+ || await hasReleaseReadinessStopMarker(cwd, stateDir, sessionId, teamName);
1855
1916
  if (!explicitReleaseReadinessContext) {
1856
1917
  return { matched: false, output: null };
1857
1918
  }
@@ -1873,8 +1934,8 @@ async function maybeBuildReleaseReadinessFinalizeStopOutput(payload, cwd, stateD
1873
1934
  }, sessionId);
1874
1935
  return { matched: true, output };
1875
1936
  }
1876
- async function buildSkillStopOutput(cwd, sessionId, threadId) {
1877
- const blocker = await readBlockingSkillForStop(cwd, sessionId, threadId);
1937
+ async function buildSkillStopOutput(cwd, stateDir, sessionId, threadId) {
1938
+ const blocker = await readBlockingSkillForStop(cwd, stateDir, sessionId, threadId);
1878
1939
  if (!blocker)
1879
1940
  return null;
1880
1941
  const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
@@ -1983,18 +2044,33 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
1983
2044
  const canonicalSessionId = await resolveInternalSessionIdForPayload(cwd, sessionId);
1984
2045
  const threadId = readPayloadThreadId(payload);
1985
2046
  if (canonicalSessionId) {
1986
- await reconcileStaleRootSkillActiveStateForStop(cwd, canonicalSessionId);
2047
+ await reconcileStaleRootSkillActiveStateForStop(cwd, stateDir, canonicalSessionId);
1987
2048
  }
1988
2049
  const execFollowupOutput = await buildExecFollowupStopOutput(cwd, canonicalSessionId);
1989
2050
  if (execFollowupOutput)
1990
2051
  return execFollowupOutput;
2052
+ const ralphOwnerContext = {
2053
+ payloadSessionId: sessionId,
2054
+ threadId,
2055
+ tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
2056
+ };
2057
+ const ralphCompletionAuditBlock = options.skipRalphStopBlock === true
2058
+ ? null
2059
+ : await readRalphCompletionAuditBlockState(cwd, stateDir, canonicalSessionId, ralphOwnerContext);
2060
+ if (ralphCompletionAuditBlock) {
2061
+ await reopenRalphCompletionAuditBlock(ralphCompletionAuditBlock);
2062
+ const blockingPath = formatStopStatePath(cwd, ralphCompletionAuditBlock.path);
2063
+ const systemMessage = `OMX Ralph completion audit is missing required evidence (${ralphCompletionAuditBlock.reason}; state: ${blockingPath}); continue verification, record a prompt-to-artifact checklist plus verification evidence, and do not report complete yet.`;
2064
+ return await returnPersistentStopBlock(payload, stateDir, "ralph-completion-audit-stop", `${blockingPath}|${ralphCompletionAuditBlock.reason}`, {
2065
+ decision: "block",
2066
+ reason: systemMessage,
2067
+ stopReason: `ralph_completion_audit_${ralphCompletionAuditBlock.reason}`,
2068
+ systemMessage,
2069
+ }, canonicalSessionId, { allowRepeatDuringStopHook: true });
2070
+ }
1991
2071
  const ralphState = options.skipRalphStopBlock === true
1992
2072
  ? null
1993
- : await readActiveRalphState(stateDir, canonicalSessionId, {
1994
- payloadSessionId: sessionId,
1995
- threadId,
1996
- tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
1997
- });
2073
+ : await readActiveRalphState(cwd, stateDir, canonicalSessionId, ralphOwnerContext);
1998
2074
  if (!ralphState) {
1999
2075
  const autoresearchState = await readActiveAutoresearchState(cwd, canonicalSessionId);
2000
2076
  if (autoresearchState) {
@@ -2047,7 +2123,7 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
2047
2123
  return await returnPersistentStopBlock(payload, stateDir, "team-stop", safeString(teamOutput.stopReason), teamOutput, canonicalSessionId);
2048
2124
  }
2049
2125
  if (canonicalSessionId) {
2050
- const deepInterviewQuestionOutput = await buildDeepInterviewQuestionStopOutput(cwd, canonicalSessionId, threadId);
2126
+ const deepInterviewQuestionOutput = await buildDeepInterviewQuestionStopOutput(cwd, stateDir, canonicalSessionId, threadId);
2051
2127
  if (deepInterviewQuestionOutput) {
2052
2128
  return await returnPersistentStopBlock(payload, stateDir, "deep-interview-question-stop", deepInterviewQuestionOutput.obligationId, deepInterviewQuestionOutput.output, canonicalSessionId);
2053
2129
  }
@@ -2060,7 +2136,7 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
2060
2136
  if (repeatedCanonicalTeamOutput)
2061
2137
  return repeatedCanonicalTeamOutput;
2062
2138
  }
2063
- const skillOutput = await buildSkillStopOutput(cwd, canonicalSessionId, threadId);
2139
+ const skillOutput = await buildSkillStopOutput(cwd, stateDir, canonicalSessionId, threadId);
2064
2140
  if (skillOutput) {
2065
2141
  return await returnPersistentStopBlock(payload, stateDir, "skill-stop", safeString(skillOutput.stopReason), skillOutput, canonicalSessionId);
2066
2142
  }
@@ -2071,7 +2147,7 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
2071
2147
  return await returnPersistentStopBlock(payload, stateDir, "goal-workflow-reconciliation-stop", safeString(goalWorkflowStopOutput.stopReason), goalWorkflowStopOutput, canonicalSessionId, { allowRepeatDuringStopHook: true });
2072
2148
  }
2073
2149
  const autoNudgeConfig = await loadAutoNudgeConfig();
2074
- const autoNudgePhase = await readStopAutoNudgePhase(cwd, canonicalSessionId, threadId);
2150
+ const autoNudgePhase = await readStopAutoNudgePhase(cwd, stateDir, canonicalSessionId, threadId);
2075
2151
  if (autoNudgeConfig.enabled
2076
2152
  && detectNativeStopStallPattern(lastAssistantMessage, autoNudgeConfig.patterns, autoNudgePhase)) {
2077
2153
  const effectiveResponse = resolveEffectiveAutoNudgeResponse(autoNudgeConfig.response);
@@ -2109,7 +2185,9 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
2109
2185
  export async function dispatchCodexNativeHook(payload, options = {}) {
2110
2186
  const hookEventName = readHookEventName(payload);
2111
2187
  const cwd = options.cwd ?? (safeString(payload.cwd).trim() || process.cwd());
2112
- const stateDir = join(cwd, ".omx", "state");
2188
+ // Native hooks must use the same authoritative runtime state root as HUD/MCP
2189
+ // when boxed/team roots are active; do not bypass it with cwd/.omx/state.
2190
+ const stateDir = getBaseStateDir(cwd);
2113
2191
  await mkdir(stateDir, { recursive: true });
2114
2192
  const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
2115
2193
  let skillState = null;
@@ -2179,6 +2257,7 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
2179
2257
  if (prompt && !isSubagentPromptSubmit) {
2180
2258
  skillState = buildNativeOutsideTmuxTeamPromptBlockState(prompt, cwd, payload, sessionIdForState, threadId || undefined, turnId || undefined) ?? await recordSkillActivation({
2181
2259
  stateDir,
2260
+ sourceCwd: cwd,
2182
2261
  text: prompt,
2183
2262
  sessionId: sessionIdForState,
2184
2263
  threadId,