oh-my-codex 0.16.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/Cargo.lock +5 -5
  2. package/Cargo.toml +1 -1
  3. package/crates/omx-explore/src/main.rs +434 -28
  4. package/dist/agents/__tests__/native-config.test.js +50 -0
  5. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  6. package/dist/agents/native-config.d.ts.map +1 -1
  7. package/dist/agents/native-config.js +3 -2
  8. package/dist/agents/native-config.js.map +1 -1
  9. package/dist/cli/__tests__/codex-plugin-layout.test.js +1 -0
  10. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  11. package/dist/cli/__tests__/explore.test.js +118 -1
  12. package/dist/cli/__tests__/explore.test.js.map +1 -1
  13. package/dist/cli/__tests__/imagegen-continuation.test.d.ts +2 -0
  14. package/dist/cli/__tests__/imagegen-continuation.test.d.ts.map +1 -0
  15. package/dist/cli/__tests__/imagegen-continuation.test.js +135 -0
  16. package/dist/cli/__tests__/imagegen-continuation.test.js.map +1 -0
  17. package/dist/cli/__tests__/index.test.js +182 -18
  18. package/dist/cli/__tests__/index.test.js.map +1 -1
  19. package/dist/cli/__tests__/launch-fallback.test.js +88 -2
  20. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  21. package/dist/cli/__tests__/ralph.test.js +62 -0
  22. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  23. package/dist/cli/__tests__/setup-install-mode.test.js +45 -0
  24. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  25. package/dist/cli/__tests__/team.test.js +465 -12
  26. package/dist/cli/__tests__/team.test.js.map +1 -1
  27. package/dist/cli/__tests__/ultragoal.test.js +40 -0
  28. package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
  29. package/dist/cli/explore.d.ts.map +1 -1
  30. package/dist/cli/explore.js +208 -8
  31. package/dist/cli/explore.js.map +1 -1
  32. package/dist/cli/index.d.ts +11 -3
  33. package/dist/cli/index.d.ts.map +1 -1
  34. package/dist/cli/index.js +124 -18
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cli/ralph.d.ts.map +1 -1
  37. package/dist/cli/ralph.js +37 -3
  38. package/dist/cli/ralph.js.map +1 -1
  39. package/dist/cli/setup.d.ts.map +1 -1
  40. package/dist/cli/setup.js +93 -4
  41. package/dist/cli/setup.js.map +1 -1
  42. package/dist/cli/team.d.ts +1 -0
  43. package/dist/cli/team.d.ts.map +1 -1
  44. package/dist/cli/team.js +42 -7
  45. package/dist/cli/team.js.map +1 -1
  46. package/dist/cli/ultragoal.d.ts +1 -1
  47. package/dist/cli/ultragoal.d.ts.map +1 -1
  48. package/dist/cli/ultragoal.js +6 -3
  49. package/dist/cli/ultragoal.js.map +1 -1
  50. package/dist/config/__tests__/codex-hooks.test.js +5 -0
  51. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  52. package/dist/config/__tests__/models.test.js +18 -1
  53. package/dist/config/__tests__/models.test.js.map +1 -1
  54. package/dist/config/codex-hooks.d.ts.map +1 -1
  55. package/dist/config/codex-hooks.js +4 -1
  56. package/dist/config/codex-hooks.js.map +1 -1
  57. package/dist/config/models.d.ts +6 -0
  58. package/dist/config/models.d.ts.map +1 -1
  59. package/dist/config/models.js +37 -0
  60. package/dist/config/models.js.map +1 -1
  61. package/dist/exec/followup.d.ts +1 -0
  62. package/dist/exec/followup.d.ts.map +1 -1
  63. package/dist/exec/followup.js +9 -3
  64. package/dist/exec/followup.js.map +1 -1
  65. package/dist/hooks/__tests__/anti-slop-workflow.test.js +19 -0
  66. package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
  67. package/dist/hooks/__tests__/consensus-execution-handoff.test.js +19 -2
  68. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  69. package/dist/hooks/__tests__/deep-interview-contract.test.js +40 -0
  70. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  71. package/dist/hooks/__tests__/foreground-isolation-contract.test.d.ts +2 -0
  72. package/dist/hooks/__tests__/foreground-isolation-contract.test.d.ts.map +1 -0
  73. package/dist/hooks/__tests__/foreground-isolation-contract.test.js +28 -0
  74. package/dist/hooks/__tests__/foreground-isolation-contract.test.js.map +1 -0
  75. package/dist/hooks/__tests__/session.test.js +32 -0
  76. package/dist/hooks/__tests__/session.test.js.map +1 -1
  77. package/dist/hooks/codebase-map.d.ts.map +1 -1
  78. package/dist/hooks/codebase-map.js +3 -2
  79. package/dist/hooks/codebase-map.js.map +1 -1
  80. package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
  81. package/dist/hooks/extensibility/dispatcher.js +6 -4
  82. package/dist/hooks/extensibility/dispatcher.js.map +1 -1
  83. package/dist/hooks/extensibility/logging.d.ts.map +1 -1
  84. package/dist/hooks/extensibility/logging.js +3 -2
  85. package/dist/hooks/extensibility/logging.js.map +1 -1
  86. package/dist/hooks/extensibility/sdk/paths.d.ts.map +1 -1
  87. package/dist/hooks/extensibility/sdk/paths.js +4 -3
  88. package/dist/hooks/extensibility/sdk/paths.js.map +1 -1
  89. package/dist/hooks/session.d.ts.map +1 -1
  90. package/dist/hooks/session.js +22 -12
  91. package/dist/hooks/session.js.map +1 -1
  92. package/dist/hud/__tests__/hud-tmux-injection.test.js +8 -7
  93. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  94. package/dist/hud/__tests__/reconcile.test.js +1 -1
  95. package/dist/hud/__tests__/state.test.js +24 -0
  96. package/dist/hud/__tests__/state.test.js.map +1 -1
  97. package/dist/hud/index.js +1 -1
  98. package/dist/hud/index.js.map +1 -1
  99. package/dist/hud/state.d.ts.map +1 -1
  100. package/dist/hud/state.js +22 -8
  101. package/dist/hud/state.js.map +1 -1
  102. package/dist/hud/tmux.js +1 -1
  103. package/dist/hud/tmux.js.map +1 -1
  104. package/dist/imagegen/continuation.d.ts +44 -0
  105. package/dist/imagegen/continuation.d.ts.map +1 -0
  106. package/dist/imagegen/continuation.js +220 -0
  107. package/dist/imagegen/continuation.js.map +1 -0
  108. package/dist/mcp/__tests__/bootstrap.test.js +47 -2
  109. package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
  110. package/dist/mcp/__tests__/server-lifecycle.test.js +49 -1
  111. package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
  112. package/dist/mcp/bootstrap.d.ts +2 -0
  113. package/dist/mcp/bootstrap.d.ts.map +1 -1
  114. package/dist/mcp/bootstrap.js +95 -15
  115. package/dist/mcp/bootstrap.js.map +1 -1
  116. package/dist/mcp/lifecycle-telemetry.d.ts +16 -0
  117. package/dist/mcp/lifecycle-telemetry.d.ts.map +1 -0
  118. package/dist/mcp/lifecycle-telemetry.js +95 -0
  119. package/dist/mcp/lifecycle-telemetry.js.map +1 -0
  120. package/dist/pipeline/__tests__/stages.test.js +274 -5
  121. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  122. package/dist/pipeline/stages/team-exec.d.ts +2 -0
  123. package/dist/pipeline/stages/team-exec.d.ts.map +1 -1
  124. package/dist/pipeline/stages/team-exec.js +51 -26
  125. package/dist/pipeline/stages/team-exec.js.map +1 -1
  126. package/dist/planning/__tests__/artifacts.test.js +138 -3
  127. package/dist/planning/__tests__/artifacts.test.js.map +1 -1
  128. package/dist/planning/__tests__/context-pack-status.test.d.ts +2 -0
  129. package/dist/planning/__tests__/context-pack-status.test.d.ts.map +1 -0
  130. package/dist/planning/__tests__/context-pack-status.test.js +271 -0
  131. package/dist/planning/__tests__/context-pack-status.test.js.map +1 -0
  132. package/dist/planning/artifacts.d.ts +12 -1
  133. package/dist/planning/artifacts.d.ts.map +1 -1
  134. package/dist/planning/artifacts.js +32 -9
  135. package/dist/planning/artifacts.js.map +1 -1
  136. package/dist/planning/context-pack-status.d.ts +42 -0
  137. package/dist/planning/context-pack-status.d.ts.map +1 -0
  138. package/dist/planning/context-pack-status.js +479 -0
  139. package/dist/planning/context-pack-status.js.map +1 -0
  140. package/dist/runtime/__tests__/process-tree.test.d.ts +2 -0
  141. package/dist/runtime/__tests__/process-tree.test.d.ts.map +1 -0
  142. package/dist/runtime/__tests__/process-tree.test.js +107 -0
  143. package/dist/runtime/__tests__/process-tree.test.js.map +1 -0
  144. package/dist/runtime/process-tree.d.ts +28 -0
  145. package/dist/runtime/process-tree.d.ts.map +1 -0
  146. package/dist/runtime/process-tree.js +230 -0
  147. package/dist/runtime/process-tree.js.map +1 -0
  148. package/dist/scripts/__tests__/codex-native-hook.test.js +205 -1
  149. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  150. package/dist/scripts/__tests__/notify-state-io.test.d.ts +2 -0
  151. package/dist/scripts/__tests__/notify-state-io.test.d.ts.map +1 -0
  152. package/dist/scripts/__tests__/notify-state-io.test.js +40 -0
  153. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -0
  154. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  155. package/dist/scripts/codex-native-hook.js +111 -7
  156. package/dist/scripts/codex-native-hook.js.map +1 -1
  157. package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
  158. package/dist/scripts/notify-hook/managed-tmux.js +6 -9
  159. package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
  160. package/dist/scripts/notify-hook/process-runner.d.ts.map +1 -1
  161. package/dist/scripts/notify-hook/process-runner.js +4 -1
  162. package/dist/scripts/notify-hook/process-runner.js.map +1 -1
  163. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  164. package/dist/scripts/notify-hook/state-io.js +4 -7
  165. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  166. package/dist/scripts/notify-hook.js +25 -3
  167. package/dist/scripts/notify-hook.js.map +1 -1
  168. package/dist/scripts/verify-native-agents.d.ts.map +1 -1
  169. package/dist/scripts/verify-native-agents.js +3 -1
  170. package/dist/scripts/verify-native-agents.js.map +1 -1
  171. package/dist/sidecar/__tests__/tmux.test.js +1 -1
  172. package/dist/sidecar/__tests__/tmux.test.js.map +1 -1
  173. package/dist/sidecar/tmux.js +1 -1
  174. package/dist/sidecar/tmux.js.map +1 -1
  175. package/dist/state/__tests__/workflow-transition.test.js +45 -1
  176. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  177. package/dist/state/workflow-transition-reconcile.js +2 -2
  178. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  179. package/dist/state/workflow-transition.js +2 -2
  180. package/dist/state/workflow-transition.js.map +1 -1
  181. package/dist/team/__tests__/approved-execution.test.js +96 -0
  182. package/dist/team/__tests__/approved-execution.test.js.map +1 -1
  183. package/dist/team/__tests__/followup-planner.test.js +16 -0
  184. package/dist/team/__tests__/followup-planner.test.js.map +1 -1
  185. package/dist/team/__tests__/model-contract.test.js +16 -0
  186. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  187. package/dist/team/__tests__/repo-aware-decomposition.test.js +20 -0
  188. package/dist/team/__tests__/repo-aware-decomposition.test.js.map +1 -1
  189. package/dist/team/__tests__/runtime-cli.test.js +16 -0
  190. package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
  191. package/dist/team/__tests__/runtime.test.js +209 -11
  192. package/dist/team/__tests__/runtime.test.js.map +1 -1
  193. package/dist/team/__tests__/scaling.test.js +110 -0
  194. package/dist/team/__tests__/scaling.test.js.map +1 -1
  195. package/dist/team/__tests__/tmux-session.test.js +9 -0
  196. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  197. package/dist/team/__tests__/worker-runtime-identity.test.js +6 -0
  198. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  199. package/dist/team/approved-execution.d.ts +13 -0
  200. package/dist/team/approved-execution.d.ts.map +1 -1
  201. package/dist/team/approved-execution.js +40 -22
  202. package/dist/team/approved-execution.js.map +1 -1
  203. package/dist/team/followup-planner.d.ts +1 -0
  204. package/dist/team/followup-planner.d.ts.map +1 -1
  205. package/dist/team/followup-planner.js +9 -9
  206. package/dist/team/followup-planner.js.map +1 -1
  207. package/dist/team/model-contract.d.ts +1 -1
  208. package/dist/team/model-contract.d.ts.map +1 -1
  209. package/dist/team/model-contract.js +4 -3
  210. package/dist/team/model-contract.js.map +1 -1
  211. package/dist/team/repo-aware-decomposition.d.ts +1 -0
  212. package/dist/team/repo-aware-decomposition.d.ts.map +1 -1
  213. package/dist/team/repo-aware-decomposition.js +5 -1
  214. package/dist/team/repo-aware-decomposition.js.map +1 -1
  215. package/dist/team/runtime-cli.d.ts +4 -0
  216. package/dist/team/runtime-cli.d.ts.map +1 -1
  217. package/dist/team/runtime-cli.js +14 -1
  218. package/dist/team/runtime-cli.js.map +1 -1
  219. package/dist/team/runtime.d.ts +1 -0
  220. package/dist/team/runtime.d.ts.map +1 -1
  221. package/dist/team/runtime.js +46 -16
  222. package/dist/team/runtime.js.map +1 -1
  223. package/dist/team/scaling.d.ts.map +1 -1
  224. package/dist/team/scaling.js +13 -6
  225. package/dist/team/scaling.js.map +1 -1
  226. package/dist/team/tmux-session.d.ts.map +1 -1
  227. package/dist/team/tmux-session.js +7 -0
  228. package/dist/team/tmux-session.js.map +1 -1
  229. package/dist/ultragoal/__tests__/artifacts.test.js +51 -0
  230. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  231. package/dist/ultragoal/__tests__/docs-contract.test.d.ts +2 -0
  232. package/dist/ultragoal/__tests__/docs-contract.test.d.ts.map +1 -0
  233. package/dist/ultragoal/__tests__/docs-contract.test.js +23 -0
  234. package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -0
  235. package/dist/ultragoal/artifacts.d.ts +2 -2
  236. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  237. package/dist/ultragoal/artifacts.js +37 -1
  238. package/dist/ultragoal/artifacts.js.map +1 -1
  239. package/dist/verification/__tests__/ci-rust-gates.test.js +44 -14
  240. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  241. package/package.json +1 -1
  242. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  243. package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +9 -0
  244. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +25 -2
  245. package/plugins/oh-my-codex/skills/plan/SKILL.md +7 -4
  246. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +13 -3
  247. package/plugins/oh-my-codex/skills/team/SKILL.md +2 -2
  248. package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +8 -0
  249. package/prompts/planner.md +1 -1
  250. package/skills/ai-slop-cleaner/SKILL.md +9 -0
  251. package/skills/deep-interview/SKILL.md +25 -2
  252. package/skills/plan/SKILL.md +7 -4
  253. package/skills/ralplan/SKILL.md +13 -3
  254. package/skills/team/SKILL.md +2 -2
  255. package/skills/visual-ralph/SKILL.md +8 -0
  256. package/src/scripts/__tests__/codex-native-hook.test.ts +231 -1
  257. package/src/scripts/__tests__/notify-state-io.test.ts +73 -0
  258. package/src/scripts/codex-native-hook.ts +129 -14
  259. package/src/scripts/notify-hook/managed-tmux.ts +6 -7
  260. package/src/scripts/notify-hook/process-runner.ts +4 -1
  261. package/src/scripts/notify-hook/state-io.ts +5 -7
  262. package/src/scripts/notify-hook.ts +26 -3
  263. package/src/scripts/verify-native-agents.ts +3 -1
@@ -1,13 +1,16 @@
1
1
  import { execFileSync } from "child_process";
2
2
  import { closeSync, existsSync, openSync, readFileSync, readSync } from "fs";
3
- import { mkdir, readFile, readdir, writeFile } from "fs/promises";
3
+ 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
7
  import {
8
8
  extractSessionIdFromInitializedStatePath,
9
+ getSkillActiveStatePaths,
9
10
  listActiveSkills,
11
+ readSkillActiveState,
10
12
  readVisibleSkillActiveState,
13
+ type SkillActiveStateLike,
11
14
  } from "../state/skill-active.js";
12
15
  import {
13
16
  readSubagentSessionSummary,
@@ -652,6 +655,7 @@ function readParentPid(pid: number): number | null {
652
655
  const raw = execFileSync("ps", ["-o", "ppid=", "-p", String(pid)], {
653
656
  encoding: "utf-8",
654
657
  stdio: ["ignore", "pipe", "ignore"],
658
+ windowsHide: true,
655
659
  }).trim();
656
660
  const ppid = Number.parseInt(raw, 10);
657
661
  return Number.isFinite(ppid) && ppid > 0 ? ppid : null;
@@ -671,6 +675,7 @@ function readProcessCommand(pid: number): string {
671
675
  return execFileSync("ps", ["-o", "command=", "-p", String(pid)], {
672
676
  encoding: "utf-8",
673
677
  stdio: ["ignore", "pipe", "ignore"],
678
+ windowsHide: true,
674
679
  }).trim();
675
680
  } catch {
676
681
  return "";
@@ -1924,6 +1929,87 @@ async function readBlockingSkillForStop(
1924
1929
  return null;
1925
1930
  }
1926
1931
 
1932
+ function uniqueNonEmpty(values: Array<string | undefined>): string[] {
1933
+ return [...new Set(values.map((value) => safeString(value).trim()).filter(Boolean))];
1934
+ }
1935
+
1936
+ function isTerminalOrInactiveModeState(state: Record<string, unknown> | null): boolean {
1937
+ if (!state) return true;
1938
+ if (state.active !== true) return true;
1939
+ if (getRunContinuationSnapshot(state)?.terminal === true) return true;
1940
+ const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
1941
+ return phase !== "" && TERMINAL_MODE_PHASES.has(phase);
1942
+ }
1943
+
1944
+ async function readSessionScopedModeStateForRootSkill(
1945
+ cwd: string,
1946
+ skill: string,
1947
+ sessionIds: string[],
1948
+ ): Promise<Record<string, unknown> | null> {
1949
+ for (const sessionId of sessionIds) {
1950
+ const state = await readJsonIfExists(getStateFilePath(`${skill}-state.json`, cwd, sessionId));
1951
+ if (state) return state;
1952
+ }
1953
+ return null;
1954
+ }
1955
+
1956
+ async function reconcileStaleRootSkillActiveStateForStop(
1957
+ cwd: string,
1958
+ sessionId: string,
1959
+ ): Promise<void> {
1960
+ const { rootPath } = getSkillActiveStatePaths(cwd);
1961
+ const rootState = await readSkillActiveState(rootPath);
1962
+ if (!rootState?.active) return;
1963
+
1964
+ const initializedSessionId = extractSessionIdFromInitializedStatePath(rootState.initialized_state_path);
1965
+ const rootSessionIds = uniqueNonEmpty([
1966
+ sessionId,
1967
+ safeString(rootState.session_id),
1968
+ initializedSessionId,
1969
+ ...listActiveSkills(rootState).map((entry) => safeString(entry.session_id)),
1970
+ ]);
1971
+ if (rootSessionIds.length === 0) return;
1972
+
1973
+ const activeEntries = listActiveSkills(rootState);
1974
+ let changed = false;
1975
+ const keptEntries = [];
1976
+ for (const entry of activeEntries) {
1977
+ const skill = safeString(entry.skill).trim();
1978
+ if (!skill) continue;
1979
+ const entrySessionId = safeString(entry.session_id).trim();
1980
+ const candidateSessionIds = uniqueNonEmpty([
1981
+ entrySessionId,
1982
+ sessionId,
1983
+ initializedSessionId,
1984
+ safeString(rootState.session_id),
1985
+ ]);
1986
+ const modeState = await readSessionScopedModeStateForRootSkill(cwd, skill, candidateSessionIds);
1987
+ if (isTerminalOrInactiveModeState(modeState)) {
1988
+ changed = true;
1989
+ continue;
1990
+ }
1991
+ keptEntries.push(entry);
1992
+ }
1993
+
1994
+ if (!changed) return;
1995
+
1996
+ const nowIso = new Date().toISOString();
1997
+ const nextRoot: SkillActiveStateLike = {
1998
+ ...rootState,
1999
+ active: keptEntries.length > 0,
2000
+ skill: keptEntries[0]?.skill ?? safeString(rootState.skill).trim(),
2001
+ phase: keptEntries[0]?.phase ?? safeString(rootState.phase).trim(),
2002
+ updated_at: nowIso,
2003
+ active_skills: keptEntries,
2004
+ reconciled_at: nowIso,
2005
+ reconciliation_reason: "stop_hook_session_state_terminal",
2006
+ };
2007
+ if (keptEntries.length === 0) {
2008
+ nextRoot.phase = "inactive";
2009
+ }
2010
+ await writeFile(rootPath, JSON.stringify(nextRoot, null, 2));
2011
+ }
2012
+
1927
2013
  function buildRalplanContinuationStatus(
1928
2014
  blocker: { phase: string; latestPlanPath?: string; planningComplete?: boolean; runOutcome?: string },
1929
2015
  activeSubagentCount: number,
@@ -2446,6 +2532,9 @@ async function buildStopHookOutput(
2446
2532
  const sessionId = readPayloadSessionId(payload);
2447
2533
  const canonicalSessionId = await resolveInternalSessionIdForPayload(cwd, sessionId);
2448
2534
  const threadId = readPayloadThreadId(payload);
2535
+ if (canonicalSessionId) {
2536
+ await reconcileStaleRootSkillActiveStateForStop(cwd, canonicalSessionId);
2537
+ }
2449
2538
  const execFollowupOutput = await buildExecFollowupStopOutput(cwd, canonicalSessionId);
2450
2539
  if (execFollowupOutput) return execFollowupOutput;
2451
2540
  const ralphState = options.skipRalphStopBlock === true
@@ -3002,12 +3091,40 @@ function writeNativeHookJsonStdout(output: Record<string, unknown>): void {
3002
3091
  process.stdout.write(`${JSON.stringify(output)}\n`);
3003
3092
  }
3004
3093
 
3094
+ async function logNativeHookCliError(
3095
+ cwd: string,
3096
+ type: string,
3097
+ error: unknown,
3098
+ payload: CodexHookPayload = {},
3099
+ ): Promise<void> {
3100
+ const logsDir = join(cwd || process.cwd(), ".omx", "logs");
3101
+ await mkdir(logsDir, { recursive: true }).catch(() => {});
3102
+ const logPath = join(logsDir, `native-hook-${new Date().toISOString().split("T")[0]}.jsonl`);
3103
+ await appendFile(
3104
+ logPath,
3105
+ JSON.stringify({
3106
+ timestamp: new Date().toISOString(),
3107
+ type,
3108
+ hook_event_name: readHookEventName(payload) ?? "Unknown",
3109
+ session_id: readPayloadSessionId(payload) || undefined,
3110
+ thread_id: readPayloadThreadId(payload) || undefined,
3111
+ turn_id: readPayloadTurnId(payload) || undefined,
3112
+ error: error instanceof Error ? error.message : String(error),
3113
+ }) + "\n",
3114
+ ).catch(() => {});
3115
+ }
3116
+
3005
3117
  function isStopDispatchFailureTestTrigger(payload: CodexHookPayload): boolean {
3006
3118
  return process.env.NODE_ENV === "test"
3007
3119
  && process.env.OMX_NATIVE_HOOK_TEST_THROW_STOP_DISPATCH === "1"
3008
3120
  && readHookEventName(payload) === "Stop";
3009
3121
  }
3010
3122
 
3123
+ function isDispatchFailureTestTrigger(): boolean {
3124
+ return process.env.NODE_ENV === "test"
3125
+ && process.env.OMX_NATIVE_HOOK_TEST_THROW_DISPATCH === "1";
3126
+ }
3127
+
3011
3128
  function buildStopDispatchFailureOutput(error: unknown): Record<string, unknown> {
3012
3129
  const detail = error instanceof Error ? error.message : String(error);
3013
3130
  const reason =
@@ -3023,6 +3140,7 @@ function buildStopDispatchFailureOutput(error: unknown): Record<string, unknown>
3023
3140
  export async function runCodexNativeHookCli(): Promise<void> {
3024
3141
  const { payload, parseError } = await readStdinJson();
3025
3142
  if (parseError) {
3143
+ await logNativeHookCliError(process.cwd(), "native_hook_stdin_parse_error", parseError);
3026
3144
  writeNativeHookJsonStdout({
3027
3145
  decision: "block",
3028
3146
  reason: "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.",
@@ -3039,6 +3157,9 @@ export async function runCodexNativeHookCli(): Promise<void> {
3039
3157
  if (isStopDispatchFailureTestTrigger(payload)) {
3040
3158
  throw new Error("test-induced Stop dispatch failure");
3041
3159
  }
3160
+ if (isDispatchFailureTestTrigger()) {
3161
+ throw new Error("test-induced dispatch failure");
3162
+ }
3042
3163
 
3043
3164
  const result = await dispatchCodexNativeHook(payload);
3044
3165
  if (result.outputJson) {
@@ -3047,25 +3168,19 @@ export async function runCodexNativeHookCli(): Promise<void> {
3047
3168
  writeNativeHookJsonStdout({});
3048
3169
  }
3049
3170
  } catch (error) {
3050
- if (readHookEventName(payload) !== "Stop") {
3051
- throw error;
3171
+ const cwd = safeString(payload.cwd).trim() || process.cwd();
3172
+ await logNativeHookCliError(cwd, "native_hook_dispatch_error", error, payload);
3173
+ if (readHookEventName(payload) === "Stop") {
3174
+ writeNativeHookJsonStdout(buildStopDispatchFailureOutput(error));
3175
+ } else {
3176
+ process.exitCode = 1;
3052
3177
  }
3053
- process.stderr.write(
3054
- `[omx] codex-native Stop hook dispatch failed: ${
3055
- error instanceof Error ? error.message : String(error)
3056
- }\n`,
3057
- );
3058
- writeNativeHookJsonStdout(buildStopDispatchFailureOutput(error));
3059
3178
  }
3060
3179
  }
3061
3180
 
3062
3181
  if (isCodexNativeHookMainModule(import.meta.url, process.argv[1])) {
3063
3182
  runCodexNativeHookCli().catch((error) => {
3064
- process.stderr.write(
3065
- `[omx] codex-native-hook failed: ${
3066
- error instanceof Error ? error.message : String(error)
3067
- }\n`,
3068
- );
3069
3183
  process.exitCode = 1;
3184
+ void logNativeHookCliError(process.cwd(), "native_hook_fatal_error", error);
3070
3185
  });
3071
3186
  }
@@ -75,6 +75,7 @@ function readCurrentTmuxSessionName(): string {
75
75
  encoding: 'utf-8',
76
76
  stdio: ['ignore', 'pipe', 'ignore'],
77
77
  timeout: 2000,
78
+ windowsHide: true,
78
79
  }).trim();
79
80
  } catch {
80
81
  return '';
@@ -104,13 +105,10 @@ async function readTmuxPaneInstanceId(paneTarget: string): Promise<string> {
104
105
  }
105
106
 
106
107
  function warnPaneInstanceFallback(paneTarget: string): void {
107
- const target = safeString(paneTarget).trim();
108
- if (!target) return;
109
- try {
110
- console.warn(`[omx] missing ${OMX_PANE_INSTANCE_OPTION} on ${target}; falling back to ${OMX_INSTANCE_OPTION}`);
111
- } catch {
112
- // warning is best effort only
113
- }
108
+ // Notify hooks run inside Codex foreground hook surfaces. Keep this
109
+ // compatibility fallback silent; downstream tmux-hook events still record
110
+ // skipped/failed injection decisions in .omx/logs.
111
+ void paneTarget;
114
112
  }
115
113
 
116
114
  export async function resolveTmuxSessionForInstance(instanceId: string): Promise<string> {
@@ -146,6 +144,7 @@ function readParentPid(pid: number): number | null {
146
144
  const raw = execFileSync('ps', ['-o', 'ppid=', '-p', String(pid)], {
147
145
  encoding: 'utf-8',
148
146
  timeout: 2000,
147
+ windowsHide: true,
149
148
  }).trim();
150
149
  const ppid = Number(raw);
151
150
  return Number.isFinite(ppid) && ppid > 0 ? ppid : null;
@@ -10,7 +10,10 @@ export function runProcess(command: string, args: string[], timeoutMs = 3000): P
10
10
  const relaxingTestTmuxTimeout = command === 'tmux' && process.env.OMX_TEST_RELAX_TMUX_TIMEOUT === '1';
11
11
  const executable = usingTestTmux ? process.env.OMX_TEST_TMUX_BIN as string : command;
12
12
  const effectiveTimeoutMs = usingTestTmux || relaxingTestTmuxTimeout ? Math.max(timeoutMs, 10_000) : timeoutMs;
13
- const child = spawn(executable, args, { stdio: ['ignore', 'pipe', 'pipe'] });
13
+ const child = spawn(executable, args, {
14
+ stdio: ['ignore', 'pipe', 'pipe'],
15
+ windowsHide: true,
16
+ });
14
17
  let stdout = '';
15
18
  let stderr = '';
16
19
  let finished = false;
@@ -52,18 +52,16 @@ export async function resolveScopedStateDir(
52
52
  baseStateDir: string,
53
53
  explicitSessionId?: string,
54
54
  ): Promise<string> {
55
+ const normalizedExplicit = safeString(explicitSessionId).trim();
56
+ if (SESSION_ID_PATTERN.test(normalizedExplicit)) {
57
+ return join(baseStateDir, 'sessions', normalizedExplicit);
58
+ }
59
+
55
60
  const currentSessionId = await readCurrentSessionId(baseStateDir);
56
61
  if (currentSessionId) {
57
62
  return join(baseStateDir, 'sessions', currentSessionId);
58
63
  }
59
64
 
60
- const normalizedExplicit = safeString(explicitSessionId).trim();
61
- if (SESSION_ID_PATTERN.test(normalizedExplicit)) {
62
- const explicitDir = join(baseStateDir, 'sessions', normalizedExplicit);
63
- if (existsSync(explicitDir)) {
64
- return explicitDir;
65
- }
66
- }
67
65
  return baseStateDir;
68
66
  }
69
67
 
@@ -771,8 +771,31 @@ async function main() {
771
771
  }
772
772
  }
773
773
 
774
+ async function logFatalNotifyHookError(err: unknown): Promise<void> {
775
+ let cwd = process.cwd();
776
+ try {
777
+ const rawPayload = process.argv[process.argv.length - 1];
778
+ if (rawPayload && !rawPayload.startsWith('-')) {
779
+ const payload = JSON.parse(rawPayload) as Record<string, unknown>;
780
+ cwd = safeString(payload.cwd || payload['cwd'] || cwd) || cwd;
781
+ }
782
+ } catch {
783
+ // Keep notification hook failures silent in Codex TUI surfaces.
784
+ }
785
+
786
+ const logsDir = join(cwd, '.omx', 'logs');
787
+ await mkdir(logsDir, { recursive: true }).catch(() => {});
788
+ const logPath = join(logsDir, `notify-hook-${new Date().toISOString().split('T')[0]}.jsonl`);
789
+ await appendFile(logPath, JSON.stringify({
790
+ timestamp: new Date().toISOString(),
791
+ type: 'notify_hook_fatal_error',
792
+ error: err instanceof Error ? err.message : String(err),
793
+ }) + '\n').catch(() => {});
794
+ }
795
+
774
796
  main().catch((err) => {
775
- process.exitCode = 1;
776
- // eslint-disable-next-line no-console
777
- console.error('[notify-hook] fatal error:', err);
797
+ // Notify hooks are auxiliary background work. Avoid printing stack traces into
798
+ // Codex TUI/PowerShell foreground panes; record diagnostics in .omx/logs.
799
+ process.exitCode = 0;
800
+ void logFatalNotifyHookError(err);
778
801
  });
@@ -188,7 +188,9 @@ export async function verifyNativeAgents(
188
188
  const promptContent = options.promptNames
189
189
  ? `${name} prompt fixture`
190
190
  : await readFile(promptPath, "utf-8");
191
- const toml = generateAgentToml(agent, promptContent);
191
+ const toml = generateAgentToml(agent, promptContent, {
192
+ codexHomeOverride: join(root, ".omx", "verify-native-agents-codex-home"),
193
+ });
192
194
  assertTomlStructure(name, agent, toml);
193
195
  }
194
196