oh-my-codex 0.18.1 → 0.18.3

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 (310) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +4 -2
  4. package/dist/agents/__tests__/definitions.test.js +23 -0
  5. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  6. package/dist/agents/__tests__/native-config.test.js +20 -0
  7. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  8. package/dist/agents/definitions.d.ts.map +1 -1
  9. package/dist/agents/definitions.js +40 -0
  10. package/dist/agents/definitions.js.map +1 -1
  11. package/dist/agents/native-config.d.ts +1 -0
  12. package/dist/agents/native-config.d.ts.map +1 -1
  13. package/dist/agents/native-config.js +4 -0
  14. package/dist/agents/native-config.js.map +1 -1
  15. package/dist/auth/__tests__/config-sessions.test.d.ts +2 -0
  16. package/dist/auth/__tests__/config-sessions.test.d.ts.map +1 -0
  17. package/dist/auth/__tests__/config-sessions.test.js +48 -0
  18. package/dist/auth/__tests__/config-sessions.test.js.map +1 -0
  19. package/dist/auth/__tests__/quota-rotation.test.d.ts +2 -0
  20. package/dist/auth/__tests__/quota-rotation.test.d.ts.map +1 -0
  21. package/dist/auth/__tests__/quota-rotation.test.js +33 -0
  22. package/dist/auth/__tests__/quota-rotation.test.js.map +1 -0
  23. package/dist/auth/__tests__/redact.test.d.ts +2 -0
  24. package/dist/auth/__tests__/redact.test.d.ts.map +1 -0
  25. package/dist/auth/__tests__/redact.test.js +20 -0
  26. package/dist/auth/__tests__/redact.test.js.map +1 -0
  27. package/dist/auth/__tests__/storage.test.d.ts +2 -0
  28. package/dist/auth/__tests__/storage.test.d.ts.map +1 -0
  29. package/dist/auth/__tests__/storage.test.js +108 -0
  30. package/dist/auth/__tests__/storage.test.js.map +1 -0
  31. package/dist/auth/config.d.ts +9 -0
  32. package/dist/auth/config.d.ts.map +1 -0
  33. package/dist/auth/config.js +77 -0
  34. package/dist/auth/config.js.map +1 -0
  35. package/dist/auth/hotswap.d.ts +36 -0
  36. package/dist/auth/hotswap.d.ts.map +1 -0
  37. package/dist/auth/hotswap.js +159 -0
  38. package/dist/auth/hotswap.js.map +1 -0
  39. package/dist/auth/index.d.ts +8 -0
  40. package/dist/auth/index.d.ts.map +1 -0
  41. package/dist/auth/index.js +8 -0
  42. package/dist/auth/index.js.map +1 -0
  43. package/dist/auth/paths.d.ts +12 -0
  44. package/dist/auth/paths.d.ts.map +1 -0
  45. package/dist/auth/paths.js +78 -0
  46. package/dist/auth/paths.js.map +1 -0
  47. package/dist/auth/quota-detector.d.ts +10 -0
  48. package/dist/auth/quota-detector.d.ts.map +1 -0
  49. package/dist/auth/quota-detector.js +40 -0
  50. package/dist/auth/quota-detector.js.map +1 -0
  51. package/dist/auth/redact.d.ts +2 -0
  52. package/dist/auth/redact.d.ts.map +1 -0
  53. package/dist/auth/redact.js +26 -0
  54. package/dist/auth/redact.js.map +1 -0
  55. package/dist/auth/rotation.d.ts +9 -0
  56. package/dist/auth/rotation.d.ts.map +1 -0
  57. package/dist/auth/rotation.js +26 -0
  58. package/dist/auth/rotation.js.map +1 -0
  59. package/dist/auth/sessions.d.ts +15 -0
  60. package/dist/auth/sessions.d.ts.map +1 -0
  61. package/dist/auth/sessions.js +62 -0
  62. package/dist/auth/sessions.js.map +1 -0
  63. package/dist/auth/storage.d.ts +27 -0
  64. package/dist/auth/storage.d.ts.map +1 -0
  65. package/dist/auth/storage.js +111 -0
  66. package/dist/auth/storage.js.map +1 -0
  67. package/dist/catalog/__tests__/generator.test.js +4 -0
  68. package/dist/catalog/__tests__/generator.test.js.map +1 -1
  69. package/dist/cli/__tests__/auth.test.d.ts +2 -0
  70. package/dist/cli/__tests__/auth.test.d.ts.map +1 -0
  71. package/dist/cli/__tests__/auth.test.js +168 -0
  72. package/dist/cli/__tests__/auth.test.js.map +1 -0
  73. package/dist/cli/__tests__/doctor-warning-copy.test.js +112 -5
  74. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  75. package/dist/cli/__tests__/explore.test.js +20 -0
  76. package/dist/cli/__tests__/explore.test.js.map +1 -1
  77. package/dist/cli/__tests__/index.test.js +171 -21
  78. package/dist/cli/__tests__/index.test.js.map +1 -1
  79. package/dist/cli/__tests__/launch-fallback.test.js +51 -3
  80. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  81. package/dist/cli/__tests__/nested-help-routing.test.js +1 -0
  82. package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
  83. package/dist/cli/__tests__/question.test.js +2 -2
  84. package/dist/cli/__tests__/question.test.js.map +1 -1
  85. package/dist/cli/__tests__/setup-agents-overwrite.test.js +30 -1
  86. package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
  87. package/dist/cli/__tests__/setup-install-mode.test.js +47 -0
  88. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  89. package/dist/cli/auth.d.ts +4 -0
  90. package/dist/cli/auth.d.ts.map +1 -0
  91. package/dist/cli/auth.js +89 -0
  92. package/dist/cli/auth.js.map +1 -0
  93. package/dist/cli/doctor.d.ts.map +1 -1
  94. package/dist/cli/doctor.js +190 -7
  95. package/dist/cli/doctor.js.map +1 -1
  96. package/dist/cli/explore.d.ts.map +1 -1
  97. package/dist/cli/explore.js +12 -0
  98. package/dist/cli/explore.js.map +1 -1
  99. package/dist/cli/index.d.ts +27 -3
  100. package/dist/cli/index.d.ts.map +1 -1
  101. package/dist/cli/index.js +245 -47
  102. package/dist/cli/index.js.map +1 -1
  103. package/dist/cli/setup.d.ts.map +1 -1
  104. package/dist/cli/setup.js +11 -3
  105. package/dist/cli/setup.js.map +1 -1
  106. package/dist/config/__tests__/codex-hooks.test.js +3 -3
  107. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  108. package/dist/config/__tests__/deep-interview.test.d.ts +2 -0
  109. package/dist/config/__tests__/deep-interview.test.d.ts.map +1 -0
  110. package/dist/config/__tests__/deep-interview.test.js +239 -0
  111. package/dist/config/__tests__/deep-interview.test.js.map +1 -0
  112. package/dist/config/__tests__/generator-idempotent.test.js +123 -0
  113. package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
  114. package/dist/config/codex-hooks.d.ts +1 -0
  115. package/dist/config/codex-hooks.d.ts.map +1 -1
  116. package/dist/config/codex-hooks.js +2 -4
  117. package/dist/config/codex-hooks.js.map +1 -1
  118. package/dist/config/deep-interview.d.ts +22 -0
  119. package/dist/config/deep-interview.d.ts.map +1 -0
  120. package/dist/config/deep-interview.js +151 -0
  121. package/dist/config/deep-interview.js.map +1 -0
  122. package/dist/config/generator.d.ts +19 -2
  123. package/dist/config/generator.d.ts.map +1 -1
  124. package/dist/config/generator.js +198 -29
  125. package/dist/config/generator.js.map +1 -1
  126. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +21 -0
  127. package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -1
  128. package/dist/goal-workflows/codex-goal-snapshot.d.ts +3 -0
  129. package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
  130. package/dist/goal-workflows/codex-goal-snapshot.js +45 -2
  131. package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
  132. package/dist/hooks/__tests__/agents-overlay.test.js +2 -0
  133. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  134. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +17 -0
  135. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  136. package/dist/hooks/__tests__/explore-routing.test.js +1 -0
  137. package/dist/hooks/__tests__/explore-routing.test.js.map +1 -1
  138. package/dist/hooks/__tests__/keyword-detector.test.js +471 -15
  139. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  140. package/dist/hooks/__tests__/prometheus-strict-contract.test.d.ts +2 -0
  141. package/dist/hooks/__tests__/prometheus-strict-contract.test.d.ts.map +1 -0
  142. package/dist/hooks/__tests__/prometheus-strict-contract.test.js +320 -0
  143. package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -0
  144. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +12 -0
  145. package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
  146. package/dist/hooks/__tests__/research-workflow-boundaries.test.d.ts +2 -0
  147. package/dist/hooks/__tests__/research-workflow-boundaries.test.d.ts.map +1 -0
  148. package/dist/hooks/__tests__/research-workflow-boundaries.test.js +35 -0
  149. package/dist/hooks/__tests__/research-workflow-boundaries.test.js.map +1 -0
  150. package/dist/hooks/deep-interview-config-instruction.d.ts +3 -0
  151. package/dist/hooks/deep-interview-config-instruction.d.ts.map +1 -0
  152. package/dist/hooks/deep-interview-config-instruction.js +47 -0
  153. package/dist/hooks/deep-interview-config-instruction.js.map +1 -0
  154. package/dist/hooks/explore-routing.d.ts.map +1 -1
  155. package/dist/hooks/explore-routing.js +1 -0
  156. package/dist/hooks/explore-routing.js.map +1 -1
  157. package/dist/hooks/keyword-detector.d.ts +6 -1
  158. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  159. package/dist/hooks/keyword-detector.js +80 -14
  160. package/dist/hooks/keyword-detector.js.map +1 -1
  161. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  162. package/dist/hooks/keyword-registry.js +1 -0
  163. package/dist/hooks/keyword-registry.js.map +1 -1
  164. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  165. package/dist/hooks/prompt-guidance-contract.js +11 -0
  166. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  167. package/dist/hud/__tests__/hud-tmux-injection.test.js +22 -0
  168. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  169. package/dist/hud/__tests__/reconcile.test.js +213 -17
  170. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  171. package/dist/hud/__tests__/render.test.js +84 -0
  172. package/dist/hud/__tests__/render.test.js.map +1 -1
  173. package/dist/hud/__tests__/state.test.js +51 -1
  174. package/dist/hud/__tests__/state.test.js.map +1 -1
  175. package/dist/hud/__tests__/tmux.test.js +171 -23
  176. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  177. package/dist/hud/index.d.ts +1 -1
  178. package/dist/hud/index.d.ts.map +1 -1
  179. package/dist/hud/index.js +8 -3
  180. package/dist/hud/index.js.map +1 -1
  181. package/dist/hud/reconcile.d.ts +1 -1
  182. package/dist/hud/reconcile.d.ts.map +1 -1
  183. package/dist/hud/reconcile.js +14 -3
  184. package/dist/hud/reconcile.js.map +1 -1
  185. package/dist/hud/render.d.ts.map +1 -1
  186. package/dist/hud/render.js +26 -0
  187. package/dist/hud/render.js.map +1 -1
  188. package/dist/hud/state.d.ts +2 -1
  189. package/dist/hud/state.d.ts.map +1 -1
  190. package/dist/hud/state.js +62 -1
  191. package/dist/hud/state.js.map +1 -1
  192. package/dist/hud/tmux.d.ts +17 -3
  193. package/dist/hud/tmux.d.ts.map +1 -1
  194. package/dist/hud/tmux.js +96 -10
  195. package/dist/hud/tmux.js.map +1 -1
  196. package/dist/hud/types.d.ts +22 -0
  197. package/dist/hud/types.d.ts.map +1 -1
  198. package/dist/hud/types.js.map +1 -1
  199. package/dist/pipeline/__tests__/orchestrator.test.js +63 -1
  200. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  201. package/dist/pipeline/__tests__/stages.test.js +410 -4
  202. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  203. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  204. package/dist/pipeline/orchestrator.js +29 -2
  205. package/dist/pipeline/orchestrator.js.map +1 -1
  206. package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
  207. package/dist/pipeline/stages/ralplan.js +41 -6
  208. package/dist/pipeline/stages/ralplan.js.map +1 -1
  209. package/dist/question/__tests__/ui.test.js +43 -10
  210. package/dist/question/__tests__/ui.test.js.map +1 -1
  211. package/dist/question/deep-interview.d.ts +2 -0
  212. package/dist/question/deep-interview.d.ts.map +1 -1
  213. package/dist/question/deep-interview.js.map +1 -1
  214. package/dist/question/ui.d.ts +12 -0
  215. package/dist/question/ui.d.ts.map +1 -1
  216. package/dist/question/ui.js +83 -46
  217. package/dist/question/ui.js.map +1 -1
  218. package/dist/ralplan/__tests__/runtime.test.js +200 -10
  219. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  220. package/dist/ralplan/consensus-gate.d.ts +23 -0
  221. package/dist/ralplan/consensus-gate.d.ts.map +1 -0
  222. package/dist/ralplan/consensus-gate.js +212 -0
  223. package/dist/ralplan/consensus-gate.js.map +1 -0
  224. package/dist/ralplan/runtime.d.ts +25 -0
  225. package/dist/ralplan/runtime.d.ts.map +1 -1
  226. package/dist/ralplan/runtime.js +144 -8
  227. package/dist/ralplan/runtime.js.map +1 -1
  228. package/dist/scripts/__tests__/codex-native-hook.test.js +1034 -28
  229. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  230. package/dist/scripts/__tests__/docs-site-contract.test.d.ts +2 -0
  231. package/dist/scripts/__tests__/docs-site-contract.test.d.ts.map +1 -0
  232. package/dist/scripts/__tests__/docs-site-contract.test.js +42 -0
  233. package/dist/scripts/__tests__/docs-site-contract.test.js.map +1 -0
  234. package/dist/scripts/__tests__/notify-dispatcher.test.js +115 -2
  235. package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
  236. package/dist/scripts/__tests__/run-test-files.test.js +57 -0
  237. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  238. package/dist/scripts/__tests__/verify-native-agents.test.js +2 -2
  239. package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
  240. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  241. package/dist/scripts/codex-native-hook.js +238 -36
  242. package/dist/scripts/codex-native-hook.js.map +1 -1
  243. package/dist/scripts/notify-dispatcher.js +188 -4
  244. package/dist/scripts/notify-dispatcher.js.map +1 -1
  245. package/dist/scripts/run-test-files.js +13 -0
  246. package/dist/scripts/run-test-files.js.map +1 -1
  247. package/dist/state/__tests__/planning-gate.test.d.ts +2 -0
  248. package/dist/state/__tests__/planning-gate.test.d.ts.map +1 -0
  249. package/dist/state/__tests__/planning-gate.test.js +219 -0
  250. package/dist/state/__tests__/planning-gate.test.js.map +1 -0
  251. package/dist/state/__tests__/workflow-transition.test.js +6 -0
  252. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  253. package/dist/state/workflow-transition.d.ts +24 -1
  254. package/dist/state/workflow-transition.d.ts.map +1 -1
  255. package/dist/state/workflow-transition.js +70 -0
  256. package/dist/state/workflow-transition.js.map +1 -1
  257. package/dist/subagents/tracker.d.ts.map +1 -1
  258. package/dist/subagents/tracker.js +4 -3
  259. package/dist/subagents/tracker.js.map +1 -1
  260. package/dist/team/__tests__/runtime.test.js +36 -44
  261. package/dist/team/__tests__/runtime.test.js.map +1 -1
  262. package/dist/team/__tests__/tmux-session.test.js +144 -18
  263. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  264. package/dist/team/runtime.d.ts.map +1 -1
  265. package/dist/team/runtime.js +10 -20
  266. package/dist/team/runtime.js.map +1 -1
  267. package/dist/team/tmux-session.d.ts.map +1 -1
  268. package/dist/team/tmux-session.js +22 -6
  269. package/dist/team/tmux-session.js.map +1 -1
  270. package/dist/ultragoal/__tests__/artifacts.test.js +50 -0
  271. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  272. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  273. package/dist/ultragoal/artifacts.js +28 -2
  274. package/dist/ultragoal/artifacts.js.map +1 -1
  275. package/package.json +1 -1
  276. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  277. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +16 -4
  278. package/plugins/oh-my-codex/skills/autoresearch/SKILL.md +4 -0
  279. package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +1 -1
  280. package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +1 -1
  281. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +10 -0
  282. package/plugins/oh-my-codex/skills/pipeline/SKILL.md +1 -1
  283. package/plugins/oh-my-codex/skills/plan/SKILL.md +1 -1
  284. package/plugins/oh-my-codex/skills/prometheus-strict/README.md +35 -0
  285. package/plugins/oh-my-codex/skills/prometheus-strict/SKILL.md +219 -0
  286. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +24 -5
  287. package/prompts/prometheus-strict-metis.md +274 -0
  288. package/prompts/prometheus-strict-momus.md +82 -0
  289. package/prompts/prometheus-strict-oracle.md +107 -0
  290. package/prompts/researcher.md +22 -3
  291. package/prompts/scholastic.md +11 -0
  292. package/skills/autopilot/SKILL.md +16 -4
  293. package/skills/autoresearch/SKILL.md +4 -0
  294. package/skills/autoresearch-goal/SKILL.md +1 -1
  295. package/skills/best-practice-research/SKILL.md +1 -1
  296. package/skills/deep-interview/SKILL.md +10 -0
  297. package/skills/pipeline/SKILL.md +1 -1
  298. package/skills/plan/SKILL.md +1 -1
  299. package/skills/prometheus-strict/README.md +35 -0
  300. package/skills/prometheus-strict/SKILL.md +219 -0
  301. package/skills/ralplan/SKILL.md +24 -5
  302. package/src/scripts/__tests__/codex-native-hook.test.ts +1307 -61
  303. package/src/scripts/__tests__/docs-site-contract.test.ts +47 -0
  304. package/src/scripts/__tests__/notify-dispatcher.test.ts +132 -3
  305. package/src/scripts/__tests__/run-test-files.test.ts +67 -0
  306. package/src/scripts/__tests__/verify-native-agents.test.ts +2 -2
  307. package/src/scripts/codex-native-hook.ts +260 -31
  308. package/src/scripts/notify-dispatcher.ts +202 -4
  309. package/src/scripts/run-test-files.ts +13 -0
  310. package/templates/catalog-manifest.json +27 -0
@@ -0,0 +1,47 @@
1
+ import assert from 'node:assert/strict';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { describe, it } from 'node:test';
5
+
6
+ const root = process.cwd();
7
+
8
+ function read(path: string): string {
9
+ return readFileSync(join(root, path), 'utf-8');
10
+ }
11
+
12
+ const docsPages = ['docs/index.html', 'docs/getting-started.html', 'docs/agents.html', 'docs/skills.html', 'docs/integrations.html'];
13
+
14
+ describe('public docs site contract', () => {
15
+ it('keeps every repo-owned docs HTML page linked to an existing stylesheet', () => {
16
+ for (const page of docsPages) {
17
+ const content = read(page);
18
+ const hrefs = [...content.matchAll(/<link\s+[^>]*rel="stylesheet"[^>]*href="([^"]+)"/g)].map((match) => match[1]);
19
+ assert.ok(hrefs.length > 0, `${page} should link a stylesheet`);
20
+ for (const href of hrefs) {
21
+ assert.ok(!href.startsWith('http'), `${page} should use a repo-owned relative stylesheet, got ${href}`);
22
+ const resolved = join(root, dirname(page), href);
23
+ assert.ok(existsSync(resolved), `${page} stylesheet must exist: ${href}`);
24
+ }
25
+ }
26
+ });
27
+
28
+ it('defines CSS for the layout classes used by the static docs pages', () => {
29
+ const css = read('docs/style.css');
30
+ assert.match(css, /(^|\n)\.grid\s*,\s*\n\.card-grid\s*\{/, 'docs/style.css must style .grid cards used by docs pages');
31
+ for (const page of docsPages) {
32
+ const content = read(page);
33
+ if (content.includes('class="grid"')) {
34
+ assert.match(css, /(^|\n)\.grid\s*,/, `${page} uses .grid, so docs/style.css must style it`);
35
+ }
36
+ }
37
+ });
38
+
39
+ it('documents research workflow boundaries on the public skills page', () => {
40
+ const content = read('docs/skills.html');
41
+ assert.match(content, /Research and Planning Boundaries/);
42
+ assert.match(content, /\$best-practice-research[\s\S]*ordinary pre-planning wrapper/);
43
+ assert.match(content, /\$autoresearch[\s\S]*bounded validator-gated research deliverable/);
44
+ assert.match(content, /\$autoresearch-goal[\s\S]*Codex goal-mode version/);
45
+ assert.match(content, /Autoresearch findings gathered before planning should feed into <code>\$ralplan<\/code> as evidence/);
46
+ });
47
+ });
@@ -6,16 +6,145 @@ import { tmpdir } from "node:os";
6
6
  import { join } from "node:path";
7
7
  import { spawnSync } from "node:child_process";
8
8
 
9
- function runDispatcher(metadataPath: string): void {
9
+ function runDispatcher(
10
+ metadataPath: string,
11
+ payload: Record<string, unknown> = { type: "test" },
12
+ env: NodeJS.ProcessEnv = {},
13
+ ): void {
10
14
  const dispatcherScript = join(process.cwd(), "dist", "scripts", "notify-dispatcher.js");
11
15
  const result = spawnSync(
12
16
  process.execPath,
13
- [dispatcherScript, "--metadata", metadataPath, JSON.stringify({ type: "test" })],
14
- { encoding: "utf-8", windowsHide: true },
17
+ [dispatcherScript, "--metadata", metadataPath, JSON.stringify(payload)],
18
+ { encoding: "utf-8", env: { ...process.env, ...env }, windowsHide: true },
15
19
  );
16
20
  assert.equal(result.status, 0, result.stderr || result.stdout);
17
21
  }
18
22
 
23
+ describe("notify dispatcher turn-ended storm guard", () => {
24
+ it("coalesces rapid turn-ended dispatcher callbacks to one OMX notify run", () => {
25
+ const wd = mkdtempSync(join(tmpdir(), "omx-notify-dispatcher-rapid-"));
26
+ try {
27
+ const omxMarker = join(wd, "omx-ran");
28
+ const omxHook = join(wd, "current-notify-hook.js");
29
+ writeFileSync(omxHook, `import { appendFileSync } from "node:fs"; appendFileSync(${JSON.stringify(omxMarker)}, "omx|");\n`);
30
+ const metadataPath = join(wd, "notify-dispatch.json");
31
+ writeFileSync(metadataPath, JSON.stringify({ managedBy: "oh-my-codex", version: 1, omxNotify: [process.execPath, omxHook] }));
32
+
33
+ for (let index = 0; index < 10; index += 1) {
34
+ runDispatcher(metadataPath, { type: "agent-turn-complete", thread_id: "desktop-thread", turn_id: `queued-${index}` });
35
+ }
36
+
37
+ assert.equal(readFileSync(omxMarker, "utf-8"), "omx|");
38
+ } finally {
39
+ rmSync(wd, { recursive: true, force: true });
40
+ }
41
+ });
42
+
43
+ it("allows separate turn-ended identities to dispatch independently", () => {
44
+ const wd = mkdtempSync(join(tmpdir(), "omx-notify-dispatcher-identities-"));
45
+ try {
46
+ const omxMarker = join(wd, "omx-ran");
47
+ const omxHook = join(wd, "current-notify-hook.js");
48
+ writeFileSync(omxHook, `import { appendFileSync } from "node:fs"; appendFileSync(${JSON.stringify(omxMarker)}, "omx|");\n`);
49
+ const metadataPath = join(wd, "notify-dispatch.json");
50
+ writeFileSync(metadataPath, JSON.stringify({ managedBy: "oh-my-codex", version: 1, omxNotify: [process.execPath, omxHook] }));
51
+
52
+ runDispatcher(metadataPath, { type: "agent-turn-complete", thread_id: "desktop-thread-a", turn_id: "queued-a" });
53
+ runDispatcher(metadataPath, { type: "agent-turn-complete", thread_id: "desktop-thread-b", turn_id: "queued-b" });
54
+
55
+ assert.equal(readFileSync(omxMarker, "utf-8"), "omx|omx|");
56
+ } finally {
57
+ rmSync(wd, { recursive: true, force: true });
58
+ }
59
+ });
60
+
61
+ it("coalesces same-identity turn-ended callbacks after a slow notify hook completes", () => {
62
+ const wd = mkdtempSync(join(tmpdir(), "omx-notify-dispatcher-slow-"));
63
+ try {
64
+ const omxMarker = join(wd, "omx-ran");
65
+ const omxHook = join(wd, "current-notify-hook.js");
66
+ writeFileSync(omxHook, `import { appendFileSync } from "node:fs"; await new Promise((resolve) => setTimeout(resolve, 1500)); appendFileSync(${JSON.stringify(omxMarker)}, "omx|");\n`);
67
+ const metadataPath = join(wd, "notify-dispatch.json");
68
+ writeFileSync(metadataPath, JSON.stringify({ managedBy: "oh-my-codex", version: 1, omxNotify: [process.execPath, omxHook] }));
69
+ const env = { OMX_NOTIFY_DISPATCH_MIN_INTERVAL_MS: "1000" };
70
+
71
+ runDispatcher(metadataPath, { type: "agent-turn-complete", thread_id: "slow-thread", turn_id: "queued-a" }, env);
72
+ runDispatcher(metadataPath, { type: "agent-turn-complete", thread_id: "slow-thread", turn_id: "queued-b" }, env);
73
+
74
+ assert.equal(readFileSync(omxMarker, "utf-8"), "omx|");
75
+ } finally {
76
+ rmSync(wd, { recursive: true, force: true });
77
+ }
78
+ });
79
+
80
+ it("prunes expired turn-ended identity guard state", () => {
81
+ const wd = mkdtempSync(join(tmpdir(), "omx-notify-dispatcher-prune-"));
82
+ try {
83
+ const omxMarker = join(wd, "omx-ran");
84
+ const omxHook = join(wd, "current-notify-hook.js");
85
+ writeFileSync(omxHook, `import { appendFileSync } from "node:fs"; appendFileSync(${JSON.stringify(omxMarker)}, "omx|");\n`);
86
+ const metadataPath = join(wd, "notify-dispatch.json");
87
+ const guardPath = join(wd, "notify-dispatch.guard.json");
88
+ writeFileSync(metadataPath, JSON.stringify({ managedBy: "oh-my-codex", version: 1, omxNotify: [process.execPath, omxHook] }));
89
+ writeFileSync(guardPath, JSON.stringify({
90
+ lastDispatchByIdentity: {
91
+ "thread_id:expired": Date.now() - 11 * 60_000,
92
+ "thread_id:recent": Date.now(),
93
+ },
94
+ }));
95
+
96
+ runDispatcher(metadataPath, { type: "agent-turn-complete", thread_id: "fresh-thread", turn_id: "queued-fresh" });
97
+
98
+ const guard = JSON.parse(readFileSync(guardPath, "utf-8"));
99
+ assert.equal("thread_id:expired" in guard.lastDispatchByIdentity, false);
100
+ assert.equal(typeof guard.lastDispatchByIdentity["thread_id:recent"], "number");
101
+ assert.equal(typeof guard.lastDispatchByIdentity["thread_id:fresh-thread"], "number");
102
+ } finally {
103
+ rmSync(wd, { recursive: true, force: true });
104
+ }
105
+ });
106
+
107
+ it("drops stale turn-ended dispatcher callbacks before spawning notify hooks", () => {
108
+ const wd = mkdtempSync(join(tmpdir(), "omx-notify-dispatcher-stale-event-"));
109
+ try {
110
+ const omxMarker = join(wd, "omx-ran");
111
+ const omxHook = join(wd, "current-notify-hook.js");
112
+ writeFileSync(omxHook, `import { writeFileSync } from "node:fs"; writeFileSync(${JSON.stringify(omxMarker)}, "ran");\n`);
113
+ const metadataPath = join(wd, "notify-dispatch.json");
114
+ writeFileSync(metadataPath, JSON.stringify({ managedBy: "oh-my-codex", version: 1, omxNotify: [process.execPath, omxHook] }));
115
+
116
+ runDispatcher(metadataPath, {
117
+ type: "agent-turn-complete",
118
+ timestamp: new Date(Date.now() - 10 * 60_000).toISOString(),
119
+ thread_id: "desktop-thread",
120
+ turn_id: "stale-turn",
121
+ });
122
+
123
+ assert.equal(existsSync(omxMarker), false);
124
+ } finally {
125
+ rmSync(wd, { recursive: true, force: true });
126
+ }
127
+ });
128
+
129
+ it("preserves normal non-turn notify dispatches", () => {
130
+ const wd = mkdtempSync(join(tmpdir(), "omx-notify-dispatcher-normal-"));
131
+ try {
132
+ const omxMarker = join(wd, "omx-ran");
133
+ const omxHook = join(wd, "current-notify-hook.js");
134
+ writeFileSync(omxHook, `import { appendFileSync } from "node:fs"; appendFileSync(${JSON.stringify(omxMarker)}, "omx|");\n`);
135
+ const metadataPath = join(wd, "notify-dispatch.json");
136
+ writeFileSync(metadataPath, JSON.stringify({ managedBy: "oh-my-codex", version: 1, omxNotify: [process.execPath, omxHook] }));
137
+
138
+ runDispatcher(metadataPath, { type: "test", id: 1 });
139
+ runDispatcher(metadataPath, { type: "test", id: 2 });
140
+
141
+ assert.equal(readFileSync(omxMarker, "utf-8"), "omx|omx|");
142
+ } finally {
143
+ rmSync(wd, { recursive: true, force: true });
144
+ }
145
+ });
146
+ });
147
+
19
148
  describe("notify dispatcher previousNotify guard", () => {
20
149
  it("skips stale OMX-managed previousNotify dispatcher entries", () => {
21
150
  const wd = mkdtempSync(join(tmpdir(), "omx-notify-dispatcher-stale-"));
@@ -147,4 +147,71 @@ describe('run-test-files diagnostics', () => {
147
147
  rmSync(wd, { recursive: true, force: true });
148
148
  }
149
149
  });
150
+
151
+ it('sanitizes live OMX runtime state env from child test processes by default', () => {
152
+ const wd = mkdtempSync(join(tmpdir(), 'omx-run-test-files-'));
153
+ try {
154
+ const testsDir = join(wd, '__tests__');
155
+ mkdirSync(testsDir, { recursive: true });
156
+ writeFileSync(
157
+ join(testsDir, 'env-clean.test.js'),
158
+ [
159
+ "import { test } from 'node:test';",
160
+ "import assert from 'node:assert/strict';",
161
+ "test('runtime env is clean', () => {",
162
+ " assert.equal(process.env.OMX_ROOT, undefined);",
163
+ " assert.equal(process.env.OMX_STATE_ROOT, undefined);",
164
+ " assert.equal(process.env.OMX_TEAM_STATE_ROOT, undefined);",
165
+ " assert.equal(process.env.OMX_SESSION_ID, undefined);",
166
+ " assert.equal(process.env.CODEX_SESSION_ID, undefined);",
167
+ " assert.equal(process.env.SESSION_ID, undefined);",
168
+ "});",
169
+ '',
170
+ ].join('\n'),
171
+ );
172
+
173
+ const result = runCompiledRunner(wd, {
174
+ OMX_ROOT: '/tmp/live-omx-root',
175
+ OMX_STATE_ROOT: '/tmp/live-omx-state-root',
176
+ OMX_TEAM_STATE_ROOT: '/tmp/live-team-state-root',
177
+ OMX_SESSION_ID: 'live-omx-session',
178
+ CODEX_SESSION_ID: 'live-codex-session',
179
+ SESSION_ID: 'live-shell-session',
180
+ });
181
+
182
+ assert.equal(result.status, 0, result.stderr || result.stdout);
183
+ } finally {
184
+ rmSync(wd, { recursive: true, force: true });
185
+ }
186
+ });
187
+
188
+ it('can preserve live OMX runtime state env for explicit diagnostics', () => {
189
+ const wd = mkdtempSync(join(tmpdir(), 'omx-run-test-files-'));
190
+ try {
191
+ const testsDir = join(wd, '__tests__');
192
+ mkdirSync(testsDir, { recursive: true });
193
+ writeFileSync(
194
+ join(testsDir, 'env-preserve.test.js'),
195
+ [
196
+ "import { test } from 'node:test';",
197
+ "import assert from 'node:assert/strict';",
198
+ "test('runtime env is preserved', () => {",
199
+ " assert.equal(process.env.OMX_ROOT, '/tmp/live-omx-root');",
200
+ " assert.equal(process.env.OMX_SESSION_ID, 'live-omx-session');",
201
+ "});",
202
+ '',
203
+ ].join('\n'),
204
+ );
205
+
206
+ const result = runCompiledRunner(wd, {
207
+ OMX_NODE_TEST_PRESERVE_RUNTIME_ENV: '1',
208
+ OMX_ROOT: '/tmp/live-omx-root',
209
+ OMX_SESSION_ID: 'live-omx-session',
210
+ });
211
+
212
+ assert.equal(result.status, 0, result.stderr || result.stdout);
213
+ } finally {
214
+ rmSync(wd, { recursive: true, force: true });
215
+ }
216
+ });
150
217
  });
@@ -174,13 +174,13 @@ describe("verify-native-agents", () => {
174
174
  );
175
175
  });
176
176
 
177
- it("fails if the plugin manifest declares setup-owned native-agent fields", async () => {
177
+ it("allows plugin-scoped hooks but fails if the plugin manifest declares setup-owned native-agent fields", async () => {
178
178
  await rejectsWithCode("native_agent_plugin_boundary_violation", () =>
179
179
  verifyNativeAgents({
180
180
  manifest: manifest([{ name: "executor", category: "build", status: "active" }]),
181
181
  definitions: { executor: definition },
182
182
  promptNames: new Set(["executor"]),
183
- pluginManifest: { agents: "./agents/" },
183
+ pluginManifest: { agents: "./agents/", hooks: "./hooks/hooks.json" },
184
184
  }),
185
185
  );
186
186