oh-my-codex 0.18.6 → 0.18.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (444) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +59 -10
  4. package/crates/omx-sparkshell/tests/execution.rs +1 -1
  5. package/dist/agents/__tests__/definitions.test.js +11 -0
  6. package/dist/agents/__tests__/definitions.test.js.map +1 -1
  7. package/dist/agents/__tests__/native-config.test.js +56 -6
  8. package/dist/agents/__tests__/native-config.test.js.map +1 -1
  9. package/dist/agents/definitions.d.ts +10 -0
  10. package/dist/agents/definitions.d.ts.map +1 -1
  11. package/dist/agents/definitions.js +5 -1
  12. package/dist/agents/definitions.js.map +1 -1
  13. package/dist/agents/native-config.d.ts +5 -1
  14. package/dist/agents/native-config.d.ts.map +1 -1
  15. package/dist/agents/native-config.js +19 -4
  16. package/dist/agents/native-config.js.map +1 -1
  17. package/dist/autopilot/__tests__/fsm.test.d.ts +2 -0
  18. package/dist/autopilot/__tests__/fsm.test.d.ts.map +1 -0
  19. package/dist/autopilot/__tests__/fsm.test.js +75 -0
  20. package/dist/autopilot/__tests__/fsm.test.js.map +1 -0
  21. package/dist/autopilot/__tests__/ralplan-gate.test.d.ts +2 -0
  22. package/dist/autopilot/__tests__/ralplan-gate.test.d.ts.map +1 -0
  23. package/dist/autopilot/__tests__/ralplan-gate.test.js +79 -0
  24. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -0
  25. package/dist/autopilot/deep-interview-gate.d.ts +18 -0
  26. package/dist/autopilot/deep-interview-gate.d.ts.map +1 -0
  27. package/dist/autopilot/deep-interview-gate.js +256 -0
  28. package/dist/autopilot/deep-interview-gate.js.map +1 -0
  29. package/dist/autopilot/fsm.d.ts +13 -0
  30. package/dist/autopilot/fsm.d.ts.map +1 -0
  31. package/dist/autopilot/fsm.js +70 -0
  32. package/dist/autopilot/fsm.js.map +1 -0
  33. package/dist/autopilot/ralplan-gate.d.ts +17 -0
  34. package/dist/autopilot/ralplan-gate.d.ts.map +1 -0
  35. package/dist/autopilot/ralplan-gate.js +61 -0
  36. package/dist/autopilot/ralplan-gate.js.map +1 -0
  37. package/dist/cli/__tests__/codex-plugin-layout.test.js +512 -1
  38. package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
  39. package/dist/cli/__tests__/doctor-warning-copy.test.js +39 -0
  40. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  41. package/dist/cli/__tests__/index.test.js +83 -7
  42. package/dist/cli/__tests__/index.test.js.map +1 -1
  43. package/dist/cli/__tests__/launch-fallback.test.js +175 -6
  44. package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
  45. package/dist/cli/__tests__/package-bin-contract.test.js +8 -4
  46. package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
  47. package/dist/cli/__tests__/question.test.js +100 -0
  48. package/dist/cli/__tests__/question.test.js.map +1 -1
  49. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +13 -0
  50. package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
  51. package/dist/cli/__tests__/ralph.test.js +14 -0
  52. package/dist/cli/__tests__/ralph.test.js.map +1 -1
  53. package/dist/cli/__tests__/setup-install-mode.test.js +89 -0
  54. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  55. package/dist/cli/__tests__/setup-refresh.test.js +83 -0
  56. package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
  57. package/dist/cli/__tests__/state.test.js +21 -0
  58. package/dist/cli/__tests__/state.test.js.map +1 -1
  59. package/dist/cli/__tests__/team.test.js +2 -2
  60. package/dist/cli/__tests__/team.test.js.map +1 -1
  61. package/dist/cli/__tests__/update.test.js +110 -2
  62. package/dist/cli/__tests__/update.test.js.map +1 -1
  63. package/dist/cli/doctor.d.ts.map +1 -1
  64. package/dist/cli/doctor.js +8 -1
  65. package/dist/cli/doctor.js.map +1 -1
  66. package/dist/cli/index.d.ts +14 -3
  67. package/dist/cli/index.d.ts.map +1 -1
  68. package/dist/cli/index.js +298 -50
  69. package/dist/cli/index.js.map +1 -1
  70. package/dist/cli/plugin-marketplace.d.ts +14 -2
  71. package/dist/cli/plugin-marketplace.d.ts.map +1 -1
  72. package/dist/cli/plugin-marketplace.js +62 -15
  73. package/dist/cli/plugin-marketplace.js.map +1 -1
  74. package/dist/cli/question.d.ts.map +1 -1
  75. package/dist/cli/question.js +36 -5
  76. package/dist/cli/question.js.map +1 -1
  77. package/dist/cli/ralph.d.ts.map +1 -1
  78. package/dist/cli/ralph.js +3 -1
  79. package/dist/cli/ralph.js.map +1 -1
  80. package/dist/cli/setup-preferences.d.ts +2 -0
  81. package/dist/cli/setup-preferences.d.ts.map +1 -1
  82. package/dist/cli/setup-preferences.js +4 -0
  83. package/dist/cli/setup-preferences.js.map +1 -1
  84. package/dist/cli/setup.d.ts +3 -0
  85. package/dist/cli/setup.d.ts.map +1 -1
  86. package/dist/cli/setup.js +166 -27
  87. package/dist/cli/setup.js.map +1 -1
  88. package/dist/cli/state.d.ts.map +1 -1
  89. package/dist/cli/state.js +8 -1
  90. package/dist/cli/state.js.map +1 -1
  91. package/dist/cli/tmux-hook.d.ts.map +1 -1
  92. package/dist/cli/tmux-hook.js +16 -0
  93. package/dist/cli/tmux-hook.js.map +1 -1
  94. package/dist/cli/update.d.ts +2 -0
  95. package/dist/cli/update.d.ts.map +1 -1
  96. package/dist/cli/update.js +47 -3
  97. package/dist/cli/update.js.map +1 -1
  98. package/dist/config/__tests__/deep-interview.test.js +7 -6
  99. package/dist/config/__tests__/deep-interview.test.js.map +1 -1
  100. package/dist/config/__tests__/generator-notify.test.js +1 -0
  101. package/dist/config/__tests__/generator-notify.test.js.map +1 -1
  102. package/dist/config/deep-interview.d.ts.map +1 -1
  103. package/dist/config/deep-interview.js +14 -4
  104. package/dist/config/deep-interview.js.map +1 -1
  105. package/dist/config/generator.d.ts +2 -2
  106. package/dist/config/generator.d.ts.map +1 -1
  107. package/dist/config/generator.js +2 -2
  108. package/dist/config/generator.js.map +1 -1
  109. package/dist/config/team-mode.d.ts +12 -0
  110. package/dist/config/team-mode.d.ts.map +1 -0
  111. package/dist/config/team-mode.js +91 -0
  112. package/dist/config/team-mode.js.map +1 -0
  113. package/dist/hooks/__tests__/agents-overlay.test.js +88 -0
  114. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  115. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +8 -0
  116. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  117. package/dist/hooks/__tests__/code-review-skill-contract.test.js +8 -0
  118. package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
  119. package/dist/hooks/__tests__/deep-interview-contract.test.js +10 -0
  120. package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
  121. package/dist/hooks/__tests__/keyword-detector.test.js +1072 -14
  122. package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
  123. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +64 -1
  124. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  125. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +189 -0
  126. package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
  127. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +35 -2
  128. package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
  129. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +3 -3
  130. package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
  131. package/dist/hooks/__tests__/session.test.js +25 -0
  132. package/dist/hooks/__tests__/session.test.js.map +1 -1
  133. package/dist/hooks/__tests__/skill-guidance-contract.test.js +21 -0
  134. package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
  135. package/dist/hooks/agents-overlay.d.ts.map +1 -1
  136. package/dist/hooks/agents-overlay.js +36 -50
  137. package/dist/hooks/agents-overlay.js.map +1 -1
  138. package/dist/hooks/deep-interview-config-instruction.js +1 -1
  139. package/dist/hooks/deep-interview-config-instruction.js.map +1 -1
  140. package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +31 -0
  141. package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +1 -1
  142. package/dist/hooks/extensibility/plugin-runner.js +17 -21
  143. package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
  144. package/dist/hooks/keyword-detector.d.ts +1 -0
  145. package/dist/hooks/keyword-detector.d.ts.map +1 -1
  146. package/dist/hooks/keyword-detector.js +428 -32
  147. package/dist/hooks/keyword-detector.js.map +1 -1
  148. package/dist/hooks/keyword-registry.d.ts.map +1 -1
  149. package/dist/hooks/keyword-registry.js +1 -0
  150. package/dist/hooks/keyword-registry.js.map +1 -1
  151. package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
  152. package/dist/hooks/prompt-guidance-contract.js +6 -0
  153. package/dist/hooks/prompt-guidance-contract.js.map +1 -1
  154. package/dist/hooks/session.d.ts +3 -0
  155. package/dist/hooks/session.d.ts.map +1 -1
  156. package/dist/hooks/session.js +13 -5
  157. package/dist/hooks/session.js.map +1 -1
  158. package/dist/hud/__tests__/authority.test.js +469 -31
  159. package/dist/hud/__tests__/authority.test.js.map +1 -1
  160. package/dist/hud/__tests__/hud-tmux-injection.test.js +2 -1
  161. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  162. package/dist/hud/__tests__/index.test.js +210 -2
  163. package/dist/hud/__tests__/index.test.js.map +1 -1
  164. package/dist/hud/__tests__/reconcile.test.js +588 -28
  165. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  166. package/dist/hud/__tests__/render.test.js +61 -0
  167. package/dist/hud/__tests__/render.test.js.map +1 -1
  168. package/dist/hud/__tests__/state.test.js +208 -0
  169. package/dist/hud/__tests__/state.test.js.map +1 -1
  170. package/dist/hud/__tests__/tmux.test.js +314 -22
  171. package/dist/hud/__tests__/tmux.test.js.map +1 -1
  172. package/dist/hud/authority.d.ts +5 -0
  173. package/dist/hud/authority.d.ts.map +1 -1
  174. package/dist/hud/authority.js +337 -30
  175. package/dist/hud/authority.js.map +1 -1
  176. package/dist/hud/index.d.ts +20 -2
  177. package/dist/hud/index.d.ts.map +1 -1
  178. package/dist/hud/index.js +103 -26
  179. package/dist/hud/index.js.map +1 -1
  180. package/dist/hud/reconcile.d.ts +3 -3
  181. package/dist/hud/reconcile.d.ts.map +1 -1
  182. package/dist/hud/reconcile.js +129 -20
  183. package/dist/hud/reconcile.js.map +1 -1
  184. package/dist/hud/render.d.ts.map +1 -1
  185. package/dist/hud/render.js +35 -0
  186. package/dist/hud/render.js.map +1 -1
  187. package/dist/hud/state.d.ts.map +1 -1
  188. package/dist/hud/state.js +64 -50
  189. package/dist/hud/state.js.map +1 -1
  190. package/dist/hud/tmux.d.ts +26 -6
  191. package/dist/hud/tmux.d.ts.map +1 -1
  192. package/dist/hud/tmux.js +173 -38
  193. package/dist/hud/tmux.js.map +1 -1
  194. package/dist/hud/types.d.ts +11 -0
  195. package/dist/hud/types.d.ts.map +1 -1
  196. package/dist/hud/types.js.map +1 -1
  197. package/dist/mcp/__tests__/hermes-bridge.test.js +203 -7
  198. package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -1
  199. package/dist/mcp/__tests__/state-paths.test.js +71 -1
  200. package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
  201. package/dist/mcp/__tests__/state-server.test.js +13 -1
  202. package/dist/mcp/__tests__/state-server.test.js.map +1 -1
  203. package/dist/mcp/hermes-bridge.d.ts +12 -2
  204. package/dist/mcp/hermes-bridge.d.ts.map +1 -1
  205. package/dist/mcp/hermes-bridge.js +83 -9
  206. package/dist/mcp/hermes-bridge.js.map +1 -1
  207. package/dist/mcp/state-paths.d.ts +32 -0
  208. package/dist/mcp/state-paths.d.ts.map +1 -1
  209. package/dist/mcp/state-paths.js +113 -17
  210. package/dist/mcp/state-paths.js.map +1 -1
  211. package/dist/mcp/state-server.d.ts +4 -4
  212. package/dist/modes/__tests__/base-autoresearch-contract.test.js +7 -1
  213. package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
  214. package/dist/pipeline/__tests__/stages.test.js +130 -0
  215. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  216. package/dist/pipeline/orchestrator.js +1 -1
  217. package/dist/pipeline/orchestrator.js.map +1 -1
  218. package/dist/pipeline/stages/ralplan.d.ts +1 -0
  219. package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
  220. package/dist/pipeline/stages/ralplan.js +14 -5
  221. package/dist/pipeline/stages/ralplan.js.map +1 -1
  222. package/dist/question/__tests__/deep-interview.test.js +160 -2
  223. package/dist/question/__tests__/deep-interview.test.js.map +1 -1
  224. package/dist/question/__tests__/policy.test.js +63 -3
  225. package/dist/question/__tests__/policy.test.js.map +1 -1
  226. package/dist/question/__tests__/renderer.test.js +191 -2
  227. package/dist/question/__tests__/renderer.test.js.map +1 -1
  228. package/dist/question/__tests__/state.test.js +94 -3
  229. package/dist/question/__tests__/state.test.js.map +1 -1
  230. package/dist/question/__tests__/ui.test.js +4 -0
  231. package/dist/question/__tests__/ui.test.js.map +1 -1
  232. package/dist/question/autopilot-wait.d.ts +12 -2
  233. package/dist/question/autopilot-wait.d.ts.map +1 -1
  234. package/dist/question/autopilot-wait.js +158 -47
  235. package/dist/question/autopilot-wait.js.map +1 -1
  236. package/dist/question/deep-interview.d.ts.map +1 -1
  237. package/dist/question/deep-interview.js +22 -6
  238. package/dist/question/deep-interview.js.map +1 -1
  239. package/dist/question/policy.d.ts.map +1 -1
  240. package/dist/question/policy.js +2 -5
  241. package/dist/question/policy.js.map +1 -1
  242. package/dist/question/renderer.d.ts +12 -0
  243. package/dist/question/renderer.d.ts.map +1 -1
  244. package/dist/question/renderer.js +87 -3
  245. package/dist/question/renderer.js.map +1 -1
  246. package/dist/question/state.d.ts +8 -1
  247. package/dist/question/state.d.ts.map +1 -1
  248. package/dist/question/state.js +54 -14
  249. package/dist/question/state.js.map +1 -1
  250. package/dist/question/types.d.ts +1 -1
  251. package/dist/question/types.d.ts.map +1 -1
  252. package/dist/question/ui.d.ts +1 -0
  253. package/dist/question/ui.d.ts.map +1 -1
  254. package/dist/question/ui.js +1 -0
  255. package/dist/question/ui.js.map +1 -1
  256. package/dist/ralplan/__tests__/runtime.test.js +191 -0
  257. package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
  258. package/dist/ralplan/consensus-gate.d.ts +9 -1
  259. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  260. package/dist/ralplan/consensus-gate.js +84 -2
  261. package/dist/ralplan/consensus-gate.js.map +1 -1
  262. package/dist/ralplan/runtime.d.ts +9 -0
  263. package/dist/ralplan/runtime.d.ts.map +1 -1
  264. package/dist/ralplan/runtime.js +32 -11
  265. package/dist/ralplan/runtime.js.map +1 -1
  266. package/dist/scripts/__tests__/codex-native-hook.test.js +2315 -280
  267. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  268. package/dist/scripts/__tests__/notify-state-io.test.js +72 -1
  269. package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
  270. package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts +2 -0
  271. package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts.map +1 -0
  272. package/dist/scripts/__tests__/notify-tmux-injection.test.js +57 -0
  273. package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -0
  274. package/dist/scripts/__tests__/run-test-files.test.js +74 -0
  275. package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
  276. package/dist/scripts/__tests__/verify-native-agents.test.js +65 -0
  277. package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
  278. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  279. package/dist/scripts/codex-native-hook.js +431 -56
  280. package/dist/scripts/codex-native-hook.js.map +1 -1
  281. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  282. package/dist/scripts/codex-native-pre-post.js +79 -1
  283. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  284. package/dist/scripts/eval/eval-parity-smoke.js +1 -1
  285. package/dist/scripts/eval/eval-parity-smoke.js.map +1 -1
  286. package/dist/scripts/hook-payload-guard.d.ts +9 -0
  287. package/dist/scripts/hook-payload-guard.d.ts.map +1 -0
  288. package/dist/scripts/hook-payload-guard.js +111 -0
  289. package/dist/scripts/hook-payload-guard.js.map +1 -0
  290. package/dist/scripts/notify-fallback-watcher.js +8 -1
  291. package/dist/scripts/notify-fallback-watcher.js.map +1 -1
  292. package/dist/scripts/notify-hook/__tests__/payload-guard.test.d.ts +2 -0
  293. package/dist/scripts/notify-hook/__tests__/payload-guard.test.d.ts.map +1 -0
  294. package/dist/scripts/notify-hook/__tests__/payload-guard.test.js +39 -0
  295. package/dist/scripts/notify-hook/__tests__/payload-guard.test.js.map +1 -0
  296. package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
  297. package/dist/scripts/notify-hook/auto-nudge.js +3 -1
  298. package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
  299. package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
  300. package/dist/scripts/notify-hook/ralph-session-resume.js +3 -10
  301. package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
  302. package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
  303. package/dist/scripts/notify-hook/state-io.js +62 -38
  304. package/dist/scripts/notify-hook/state-io.js.map +1 -1
  305. package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
  306. package/dist/scripts/notify-hook/team-leader-nudge.js +7 -0
  307. package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
  308. package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
  309. package/dist/scripts/notify-hook/team-worker-stop.js +234 -86
  310. package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
  311. package/dist/scripts/notify-hook/tmux-injection.d.ts +7 -0
  312. package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
  313. package/dist/scripts/notify-hook/tmux-injection.js +24 -18
  314. package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
  315. package/dist/scripts/notify-hook.js +86 -13
  316. package/dist/scripts/notify-hook.js.map +1 -1
  317. package/dist/scripts/run-test-files.js +193 -22
  318. package/dist/scripts/run-test-files.js.map +1 -1
  319. package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
  320. package/dist/scripts/sync-plugin-mirror.js +61 -3
  321. package/dist/scripts/sync-plugin-mirror.js.map +1 -1
  322. package/dist/scripts/verify-native-agents.d.ts.map +1 -1
  323. package/dist/scripts/verify-native-agents.js +58 -1
  324. package/dist/scripts/verify-native-agents.js.map +1 -1
  325. package/dist/state/__tests__/operations.test.js +1125 -1
  326. package/dist/state/__tests__/operations.test.js.map +1 -1
  327. package/dist/state/__tests__/skill-active.test.js +46 -1
  328. package/dist/state/__tests__/skill-active.test.js.map +1 -1
  329. package/dist/state/__tests__/workflow-transition.test.js +98 -7
  330. package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
  331. package/dist/state/operations.d.ts.map +1 -1
  332. package/dist/state/operations.js +159 -2
  333. package/dist/state/operations.js.map +1 -1
  334. package/dist/state/skill-active.js +6 -8
  335. package/dist/state/skill-active.js.map +1 -1
  336. package/dist/state/workflow-transition-reconcile.d.ts +6 -0
  337. package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
  338. package/dist/state/workflow-transition-reconcile.js +38 -15
  339. package/dist/state/workflow-transition-reconcile.js.map +1 -1
  340. package/dist/state/workflow-transition.d.ts.map +1 -1
  341. package/dist/state/workflow-transition.js +10 -3
  342. package/dist/state/workflow-transition.js.map +1 -1
  343. package/dist/subagents/__tests__/tracker.test.js +139 -0
  344. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  345. package/dist/subagents/tracker.d.ts +3 -0
  346. package/dist/subagents/tracker.d.ts.map +1 -1
  347. package/dist/subagents/tracker.js +41 -4
  348. package/dist/subagents/tracker.js.map +1 -1
  349. package/dist/team/__tests__/coordination-protocol.test.d.ts +2 -0
  350. package/dist/team/__tests__/coordination-protocol.test.d.ts.map +1 -0
  351. package/dist/team/__tests__/coordination-protocol.test.js +173 -0
  352. package/dist/team/__tests__/coordination-protocol.test.js.map +1 -0
  353. package/dist/team/__tests__/runtime.test.js +52 -3
  354. package/dist/team/__tests__/runtime.test.js.map +1 -1
  355. package/dist/team/__tests__/scaling.test.js +9 -4
  356. package/dist/team/__tests__/scaling.test.js.map +1 -1
  357. package/dist/team/__tests__/state.test.js +83 -0
  358. package/dist/team/__tests__/state.test.js.map +1 -1
  359. package/dist/team/__tests__/tmux-session.test.js +240 -2
  360. package/dist/team/__tests__/tmux-session.test.js.map +1 -1
  361. package/dist/team/__tests__/worker-bootstrap.test.js +84 -0
  362. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  363. package/dist/team/__tests__/worker-runtime-identity.test.js +4 -2
  364. package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
  365. package/dist/team/coordination-protocol.d.ts +14 -0
  366. package/dist/team/coordination-protocol.d.ts.map +1 -0
  367. package/dist/team/coordination-protocol.js +244 -0
  368. package/dist/team/coordination-protocol.js.map +1 -0
  369. package/dist/team/runtime.d.ts +1 -0
  370. package/dist/team/runtime.d.ts.map +1 -1
  371. package/dist/team/runtime.js +19 -3
  372. package/dist/team/runtime.js.map +1 -1
  373. package/dist/team/scaling.d.ts.map +1 -1
  374. package/dist/team/scaling.js +3 -2
  375. package/dist/team/scaling.js.map +1 -1
  376. package/dist/team/state/tasks.d.ts.map +1 -1
  377. package/dist/team/state/tasks.js +24 -0
  378. package/dist/team/state/tasks.js.map +1 -1
  379. package/dist/team/state/types.d.ts +21 -1
  380. package/dist/team/state/types.d.ts.map +1 -1
  381. package/dist/team/state/types.js.map +1 -1
  382. package/dist/team/state.d.ts +17 -1
  383. package/dist/team/state.d.ts.map +1 -1
  384. package/dist/team/state.js +12 -5
  385. package/dist/team/state.js.map +1 -1
  386. package/dist/team/team-ops.d.ts +1 -1
  387. package/dist/team/team-ops.d.ts.map +1 -1
  388. package/dist/team/team-ops.js.map +1 -1
  389. package/dist/team/tmux-session.d.ts +2 -0
  390. package/dist/team/tmux-session.d.ts.map +1 -1
  391. package/dist/team/tmux-session.js +161 -13
  392. package/dist/team/tmux-session.js.map +1 -1
  393. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  394. package/dist/team/worker-bootstrap.js +63 -0
  395. package/dist/team/worker-bootstrap.js.map +1 -1
  396. package/dist/utils/__tests__/agents-model-table.test.js +4 -2
  397. package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
  398. package/dist/utils/agents-model-table.d.ts.map +1 -1
  399. package/dist/utils/agents-model-table.js +3 -0
  400. package/dist/utils/agents-model-table.js.map +1 -1
  401. package/dist/verification/__tests__/ci-rust-gates.test.js +81 -1
  402. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  403. package/package.json +8 -8
  404. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  405. package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +334 -21
  406. package/plugins/oh-my-codex/hooks/hooks.json +1 -2
  407. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +13 -6
  408. package/plugins/oh-my-codex/skills/code-review/SKILL.md +7 -7
  409. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +9 -4
  410. package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -22
  411. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +12 -0
  412. package/plugins/oh-my-codex/skills/team/SKILL.md +16 -0
  413. package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +9 -0
  414. package/plugins/oh-my-codex/skills/worker/SKILL.md +14 -0
  415. package/skills/autopilot/SKILL.md +13 -6
  416. package/skills/code-review/SKILL.md +7 -7
  417. package/skills/deep-interview/SKILL.md +9 -4
  418. package/skills/ralph/SKILL.md +22 -22
  419. package/skills/ralplan/SKILL.md +12 -0
  420. package/skills/team/SKILL.md +16 -0
  421. package/skills/ultraqa/SKILL.md +9 -0
  422. package/skills/worker/SKILL.md +14 -0
  423. package/src/scripts/__tests__/codex-native-hook.test.ts +4435 -2083
  424. package/src/scripts/__tests__/notify-state-io.test.ts +95 -0
  425. package/src/scripts/__tests__/notify-tmux-injection.test.ts +82 -0
  426. package/src/scripts/__tests__/run-test-files.test.ts +102 -0
  427. package/src/scripts/__tests__/verify-native-agents.test.ts +75 -0
  428. package/src/scripts/codex-native-hook.ts +536 -51
  429. package/src/scripts/codex-native-pre-post.ts +80 -0
  430. package/src/scripts/demo-team-e2e.sh +10 -7
  431. package/src/scripts/eval/eval-parity-smoke.ts +1 -1
  432. package/src/scripts/hook-payload-guard.ts +113 -0
  433. package/src/scripts/notify-fallback-watcher.ts +8 -1
  434. package/src/scripts/notify-hook/__tests__/payload-guard.test.ts +41 -0
  435. package/src/scripts/notify-hook/auto-nudge.ts +3 -1
  436. package/src/scripts/notify-hook/ralph-session-resume.ts +2 -8
  437. package/src/scripts/notify-hook/state-io.ts +75 -37
  438. package/src/scripts/notify-hook/team-leader-nudge.ts +7 -0
  439. package/src/scripts/notify-hook/team-worker-stop.ts +193 -52
  440. package/src/scripts/notify-hook/tmux-injection.ts +35 -19
  441. package/src/scripts/notify-hook.ts +105 -6
  442. package/src/scripts/run-test-files.ts +192 -22
  443. package/src/scripts/sync-plugin-mirror.ts +98 -9
  444. package/src/scripts/verify-native-agents.ts +65 -1
@@ -9,19 +9,22 @@
9
9
  * skill/workflow state. AGENTS.md now carries the behavioral fallback contract
10
10
  * rather than the full keyword/state table.
11
11
  */
12
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
12
+ import { constants as fsConstants } from 'node:fs';
13
+ import { access, lstat, mkdir, readFile, realpath, writeFile } from 'node:fs/promises';
13
14
  import { withModeRuntimeContext } from '../state/mode-state-context.js';
14
- import { dirname, join } from 'node:path';
15
+ import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
15
16
  import { classifyTaskSize, isHeavyMode } from './task-size-detector.js';
16
17
  import { isApprovedExecutionFollowupShortcut } from '../team/followup-planner.js';
17
18
  import { isPlanningComplete, readPlanningArtifacts } from '../planning/artifacts.js';
18
19
  import { hasDurableRalplanConsensusEvidenceForCwd } from '../ralplan/consensus-gate.js';
19
20
  import { KEYWORD_TRIGGER_DEFINITIONS, compareKeywordMatches } from './keyword-registry.js';
21
+ import { readTeamModeConfig } from '../config/team-mode.js';
20
22
  import { SKILL_ACTIVE_STATE_FILE, listActiveSkills, writeSkillActiveStateCopiesForStateDir, } from '../state/skill-active.js';
21
23
  import { buildWorkflowTransitionError, evaluateWorkflowTransition, isTrackedWorkflowMode, } from '../state/workflow-transition.js';
22
24
  import { reconcileWorkflowTransition } from '../state/workflow-transition-reconcile.js';
23
25
  import { clearDeepInterviewQuestionObligation, } from '../question/deep-interview.js';
24
26
  import { buildDeepInterviewConfigStateFields, resolveDeepInterviewRuntimeConfig, } from '../config/deep-interview.js';
27
+ import { inferTerminalLifecycleOutcome } from '../runtime/run-outcome.js';
25
28
  const ACTIVE_SKILL_CONTINUATION_PATTERNS = [
26
29
  /^[\\/]?\s*keep going(?:\s+now)?[.!]?\s*$/i,
27
30
  /^[\\/]?\s*continue(?:\s+now)?[.!]?\s*$/i,
@@ -57,6 +60,198 @@ const STATEFUL_SKILL_SEED_CONFIG = {
57
60
  ultrawork: { mode: 'ultrawork', initialPhase: 'planning' },
58
61
  ultraqa: { mode: 'ultraqa', initialPhase: 'planning' },
59
62
  };
63
+ function slugifyAutopilotTask(text) {
64
+ const slug = text
65
+ .replace(/(?:^|\s)\$?(?:oh-my-codex:)?autopilot\b/gi, ' ')
66
+ .replace(/[^A-Za-z0-9]+/g, '-')
67
+ .replace(/^-+|-+$/g, '')
68
+ .toLowerCase()
69
+ .slice(0, 48)
70
+ .replace(/-+$/g, '');
71
+ return slug || 'autopilot-task';
72
+ }
73
+ function utcCompactTimestamp(nowIso) {
74
+ const parsed = new Date(nowIso);
75
+ if (Number.isNaN(parsed.getTime())) {
76
+ throw new Error(`Invalid Autopilot context timestamp: ${nowIso}`);
77
+ }
78
+ return parsed.toISOString().replace(/[-:]/g, '').replace(/\.\d{3}Z$/, 'Z');
79
+ }
80
+ function isSafeAutopilotContextSnapshotPath(value) {
81
+ const path = safeString(value).trim();
82
+ const contextPrefix = '.omx/context/';
83
+ const snapshotName = path.startsWith(contextPrefix) ? path.slice(contextPrefix.length) : '';
84
+ return path.startsWith('.omx/context/')
85
+ && path.endsWith('.md')
86
+ && !isAbsolute(path)
87
+ && !path.split('/').includes('..')
88
+ && !path.includes('\\')
89
+ && snapshotName !== ''
90
+ && !snapshotName.includes('/');
91
+ }
92
+ function isAutopilotRecoverySnapshotPath(path) {
93
+ return path.startsWith('.omx/context/autopilot-recovery-');
94
+ }
95
+ const MAX_REUSABLE_AUTOPILOT_CONTEXT_SNAPSHOT_BYTES = 1024 * 1024;
96
+ async function isReadableAutopilotContextSnapshotPath(sourceCwd, value) {
97
+ if (!isSafeAutopilotContextSnapshotPath(value))
98
+ return false;
99
+ const contextDir = await ensureSafeAutopilotContextDir(sourceCwd);
100
+ const absolutePath = join(sourceCwd, value);
101
+ try {
102
+ const snapshotStat = await lstat(absolutePath);
103
+ if (!snapshotStat.isFile() || snapshotStat.isSymbolicLink())
104
+ return false;
105
+ if (snapshotStat.size > MAX_REUSABLE_AUTOPILOT_CONTEXT_SNAPSHOT_BYTES)
106
+ return false;
107
+ const contextRealPath = await realpath(contextDir);
108
+ const snapshotRealPath = await realpath(absolutePath);
109
+ const relativeToContext = relative(contextRealPath, snapshotRealPath);
110
+ if (relativeToContext === '' || relativeToContext.startsWith('..') || isAbsolute(relativeToContext))
111
+ return false;
112
+ await access(absolutePath, fsConstants.R_OK);
113
+ return true;
114
+ }
115
+ catch {
116
+ return false;
117
+ }
118
+ }
119
+ function isRecord(value) {
120
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
121
+ }
122
+ function normalizeAutopilotContextSnapshotCandidate(candidate) {
123
+ if (isRecord(candidate.value)) {
124
+ const path = safeString(candidate.value.path).trim();
125
+ const kind = safeString(candidate.value.kind).trim();
126
+ if (!path || !['canonical', 'legacy', 'recovery'].includes(kind))
127
+ return null;
128
+ if (kind === 'recovery' || isAutopilotRecoverySnapshotPath(path))
129
+ return null;
130
+ return { path, kind };
131
+ }
132
+ const path = safeString(candidate.value).trim();
133
+ if (!path)
134
+ return null;
135
+ if (isAutopilotRecoverySnapshotPath(path))
136
+ return null;
137
+ return { path, kind: candidate.kind ?? 'legacy' };
138
+ }
139
+ async function findReusableAutopilotContextSnapshotPath(sourceCwd, candidates) {
140
+ for (const candidate of candidates) {
141
+ const normalized = normalizeAutopilotContextSnapshotCandidate(candidate);
142
+ if (!normalized || isAutopilotRecoverySnapshotPath(normalized.path) || !isSafeAutopilotContextSnapshotPath(normalized.path))
143
+ continue;
144
+ if (await isReadableAutopilotContextSnapshotPath(sourceCwd, normalized.path))
145
+ return normalized;
146
+ }
147
+ return undefined;
148
+ }
149
+ const AUTOPILOT_CONTEXT_RECOVERY_REASON_MESSAGES = {
150
+ 'missing-or-unsafe-legacy-context-snapshot': 'no safe legacy Autopilot context snapshot path was available during continuation.',
151
+ 'missing-autopilot-mode-state': 'active Autopilot skill state existed but no matching Autopilot mode state was available during continuation.',
152
+ 'malformed-autopilot-mode-state': 'active Autopilot mode state could not be parsed during continuation.',
153
+ 'nonpreservable-autopilot-mode-state-missing-current-phase': 'active Autopilot mode state was missing current_phase during continuation.',
154
+ };
155
+ async function ensureSafeAutopilotContextDir(sourceCwd) {
156
+ const rootRealPath = await realpath(sourceCwd);
157
+ const omxDir = join(sourceCwd, '.omx');
158
+ await mkdir(omxDir, { recursive: true });
159
+ if ((await lstat(omxDir)).isSymbolicLink()) {
160
+ throw new Error('Unsafe Autopilot context directory: .omx is a symbolic link');
161
+ }
162
+ const contextDir = join(omxDir, 'context');
163
+ await mkdir(contextDir, { recursive: true });
164
+ if ((await lstat(contextDir)).isSymbolicLink()) {
165
+ throw new Error('Unsafe Autopilot context directory: .omx/context is a symbolic link');
166
+ }
167
+ const contextRealPath = await realpath(contextDir);
168
+ const relativeToRoot = relative(rootRealPath, contextRealPath);
169
+ if (relativeToRoot === '' || relativeToRoot.startsWith('..') || isAbsolute(relativeToRoot)) {
170
+ throw new Error('Unsafe Autopilot context directory: resolved path escapes repository root');
171
+ }
172
+ return contextDir;
173
+ }
174
+ async function writeUniqueAutopilotContextSnapshot(sourceCwd, slug, nowIso, body) {
175
+ const contextDir = await ensureSafeAutopilotContextDir(sourceCwd);
176
+ const timestamp = utcCompactTimestamp(nowIso);
177
+ for (let attempt = 0; attempt < 100; attempt += 1) {
178
+ const suffix = attempt === 0 ? '' : `-${attempt + 1}`;
179
+ const filename = `${slug}-${timestamp}${suffix}.md`;
180
+ const relativePath = `.omx/context/${filename}`;
181
+ const absolutePath = resolve(contextDir, filename);
182
+ try {
183
+ await writeFile(absolutePath, body, { encoding: 'utf-8', flag: 'wx' });
184
+ return relativePath;
185
+ }
186
+ catch (error) {
187
+ if (error.code === 'EEXIST')
188
+ continue;
189
+ throw error;
190
+ }
191
+ }
192
+ throw new Error(`Unable to allocate unique Autopilot context snapshot for ${slug}`);
193
+ }
194
+ async function ensureAutopilotContextSnapshot(sourceCwd, nowIso, activationText, existingSnapshot, options = {}) {
195
+ if (existingSnapshot) {
196
+ if (isSafeAutopilotContextSnapshotPath(existingSnapshot.path)) {
197
+ return {
198
+ path: existingSnapshot.path,
199
+ kind: existingSnapshot.kind,
200
+ original_task_status: existingSnapshot.kind === 'legacy' ? 'legacy-unverified' : 'activation-prompt',
201
+ };
202
+ }
203
+ throw new Error(`Unsafe Autopilot context snapshot path: ${existingSnapshot.path}`);
204
+ }
205
+ if (options.allowTaskSnapshotCreation === false) {
206
+ const slug = 'autopilot-recovery';
207
+ const continuationInput = activationText.trim() || '<empty>';
208
+ const reason = options.recoveryReason ?? 'missing-or-unsafe-legacy-context-snapshot';
209
+ const body = [
210
+ '# Autopilot context recovery',
211
+ '',
212
+ '- recovery status: degraded',
213
+ `- recovery reason: ${reason}`,
214
+ `- reason detail: ${AUTOPILOT_CONTEXT_RECOVERY_REASON_MESSAGES[reason]}`,
215
+ `- continuation input: ${continuationInput}`,
216
+ '- original task status: unavailable',
217
+ '- original task seed: unavailable; do not treat the continuation input as the task seed.',
218
+ '- required follow-up: re-establish or confirm the intended task context before downstream handoff.',
219
+ '',
220
+ ].join('\n');
221
+ const path = await writeUniqueAutopilotContextSnapshot(sourceCwd, slug, nowIso, body);
222
+ return {
223
+ path,
224
+ kind: 'recovery',
225
+ original_task_status: 'unavailable',
226
+ recovery: {
227
+ status: 'degraded',
228
+ reason,
229
+ recovered_at: nowIso,
230
+ source: 'keyword-detector',
231
+ },
232
+ };
233
+ }
234
+ const slug = slugifyAutopilotTask(activationText);
235
+ const taskSeed = activationText.trim() || '$autopilot';
236
+ const body = [
237
+ `# Autopilot task seed: ${slug}`,
238
+ '',
239
+ `- activation prompt / task seed: ${taskSeed}`,
240
+ '- original task status: activation-prompt',
241
+ '- scope note: this seed captures the Autopilot activation prompt and is not guaranteed to include prior conversation context.',
242
+ '- desired outcome: complete the requested Autopilot workflow correctly with durable gate evidence.',
243
+ '- known facts/evidence: Autopilot was activated from a UserPromptSubmit keyword.',
244
+ '- constraints: follow deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa; do not skip gates without persisted evidence.',
245
+ '- unknowns/open questions: to be resolved by the deep-interview gate.',
246
+ '- likely codebase touchpoints: to be discovered during pre-context intake and planning.',
247
+ '',
248
+ ].join('\n');
249
+ return {
250
+ path: await writeUniqueAutopilotContextSnapshot(sourceCwd, slug, nowIso, body),
251
+ kind: 'canonical',
252
+ original_task_status: 'activation-prompt',
253
+ };
254
+ }
60
255
  function createDeepInterviewInputLock(nowIso, previous) {
61
256
  return {
62
257
  active: true,
@@ -117,12 +312,21 @@ async function readExistingDeepInterviewState(statePath) {
117
312
  }
118
313
  }
119
314
  async function readJsonStateIfExists(path) {
315
+ return (await readJsonStateWithStatus(path)).state;
316
+ }
317
+ async function readJsonStateWithStatus(path) {
120
318
  try {
121
319
  const raw = await readFile(path, 'utf-8');
122
- return JSON.parse(raw);
320
+ const parsed = JSON.parse(raw);
321
+ if (!isRecord(parsed))
322
+ return { state: null, status: 'malformed' };
323
+ return { state: parsed, status: 'ok' };
123
324
  }
124
- catch {
125
- return null;
325
+ catch (error) {
326
+ if (error.code === 'ENOENT') {
327
+ return { state: null, status: 'missing' };
328
+ }
329
+ return { state: null, status: 'malformed' };
126
330
  }
127
331
  }
128
332
  export async function persistDeepInterviewModeState(stateDir, nextSkill, nowIso, previousSkill, input) {
@@ -192,20 +396,39 @@ function resolveSeedStateFilePath(stateDir, mode, sessionId, scope = 'session')
192
396
  relativePath: `.omx/state/${mode}-state.json`,
193
397
  };
194
398
  }
195
- async function persistStatefulSkillSeedState(stateDir, nextSkill, nowIso, previousSkill) {
399
+ function isResettableTerminalModeState(state, expectedMode) {
400
+ if (!state || safeString(state.mode).trim() !== expectedMode)
401
+ return false;
402
+ const phase = safeString(state.current_phase).trim().toLowerCase().replace(/_/g, '-');
403
+ const terminalPhases = expectedMode === 'ralph'
404
+ ? ['blocked-on-user', 'complete', 'completed', 'failed', 'cancelled', 'canceled', 'stopped', 'user-stopped']
405
+ : ['complete', 'completed', 'failed', 'cancelled', 'canceled', 'stopped', 'user-stopped'];
406
+ if (terminalPhases.includes(phase))
407
+ return true;
408
+ const lifecycleOutcome = inferTerminalLifecycleOutcome(state, { includeQuestionEnforcement: false });
409
+ return lifecycleOutcome === 'finished'
410
+ || lifecycleOutcome === 'failed'
411
+ || lifecycleOutcome === 'userinterlude'
412
+ || (expectedMode === 'ralph' && lifecycleOutcome === 'blocked');
413
+ }
414
+ async function persistStatefulSkillSeedState(stateDir, nextSkill, nowIso, previousSkill, activationText, sourceCwd, options = {}) {
196
415
  const config = STATEFUL_SKILL_SEED_CONFIG[nextSkill.skill];
197
416
  if (!config)
198
417
  return nextSkill;
199
418
  const { absolutePath, relativePath } = resolveSeedStateFilePath(stateDir, config.mode, nextSkill.session_id, config.scope);
200
- const existingModeState = await readJsonStateIfExists(absolutePath);
419
+ const existingModeStateResult = await readJsonStateWithStatus(absolutePath);
420
+ const existingModeState = existingModeStateResult.state;
201
421
  const sameActiveSkill = previousSkill?.skill === nextSkill.skill && previousSkill.active;
202
422
  const existingModeMatches = safeString(existingModeState?.mode).trim() === config.mode;
203
423
  const existingPhase = safeString(existingModeState?.current_phase).trim();
424
+ const existingModeTerminal = existingModeMatches
425
+ && isResettableTerminalModeState(existingModeState, config.mode);
204
426
  const preserveExistingModeState = existingModeMatches
205
427
  && existingPhase !== ''
428
+ && !existingModeTerminal
206
429
  && (sameActiveSkill
207
430
  || (config.mode === 'team' && existingModeState?.active === true));
208
- const startedAt = previousSkill?.skill === nextSkill.skill && previousSkill.active
431
+ const startedAt = previousSkill?.skill === nextSkill.skill && previousSkill.active && !existingModeTerminal
209
432
  ? safeString(existingModeState?.started_at).trim() || previousSkill.activated_at || nowIso
210
433
  : preserveExistingModeState
211
434
  ? safeString(existingModeState?.started_at).trim() || nowIso
@@ -228,20 +451,47 @@ async function persistStatefulSkillSeedState(stateDir, nextSkill, nowIso, previo
228
451
  if (config.includeIteration) {
229
452
  const defaultIteration = config.mode === 'autopilot' ? 1 : 0;
230
453
  const defaultMaxIterations = config.mode === 'autopilot' ? 10 : 50;
231
- baseState.iteration = typeof existingModeState?.iteration === 'number' ? existingModeState.iteration : defaultIteration;
232
- baseState.max_iterations = typeof existingModeState?.max_iterations === 'number' ? existingModeState.max_iterations : defaultMaxIterations;
454
+ const reusableModeState = preserveExistingModeState ? existingModeState : null;
455
+ baseState.iteration = typeof reusableModeState?.iteration === 'number' ? reusableModeState.iteration : defaultIteration;
456
+ baseState.max_iterations = typeof reusableModeState?.max_iterations === 'number' ? reusableModeState.max_iterations : defaultMaxIterations;
233
457
  }
234
458
  if (config.mode === 'deep-interview') {
235
459
  Object.assign(baseState, buildDeepInterviewConfigStateFields(nextSkill.deep_interview_config));
236
460
  }
237
461
  if (config.mode === 'autopilot') {
238
- const existingState = (existingModeState?.state && typeof existingModeState.state === 'object')
239
- ? existingModeState.state
462
+ const reusableModeState = preserveExistingModeState ? existingModeState : null;
463
+ const existingStateRaw = (reusableModeState?.state && typeof reusableModeState.state === 'object')
464
+ ? reusableModeState.state
240
465
  : {};
466
+ const { context_snapshot_path: legacyStateContextSnapshotPath, context_snapshot_recovery: _legacyContextSnapshotRecovery, ...existingState } = existingStateRaw;
241
467
  const existingHandoffs = (existingState.handoff_artifacts && typeof existingState.handoff_artifacts === 'object')
242
468
  ? existingState.handoff_artifacts
243
469
  : {};
244
- baseState.review_cycle = typeof existingModeState?.review_cycle === 'number' ? existingModeState.review_cycle : 0;
470
+ let recoveryReason = 'missing-or-unsafe-legacy-context-snapshot';
471
+ if (options.activeContinuation === true && !preserveExistingModeState) {
472
+ if (existingModeStateResult.status === 'missing') {
473
+ recoveryReason = 'missing-autopilot-mode-state';
474
+ }
475
+ else if (existingModeStateResult.status === 'malformed') {
476
+ recoveryReason = 'malformed-autopilot-mode-state';
477
+ }
478
+ else if (existingModeMatches && existingPhase === '') {
479
+ recoveryReason = 'nonpreservable-autopilot-mode-state-missing-current-phase';
480
+ }
481
+ }
482
+ const existingContextSnapshotPath = await findReusableAutopilotContextSnapshotPath(sourceCwd, [
483
+ { value: existingHandoffs.context_snapshot },
484
+ { value: existingHandoffs.context_snapshot_path, kind: 'legacy' },
485
+ { value: legacyStateContextSnapshotPath, kind: 'legacy' },
486
+ { value: reusableModeState?.context_snapshot_path, kind: 'legacy' },
487
+ ]);
488
+ const contextSnapshot = await ensureAutopilotContextSnapshot(sourceCwd, nowIso, activationText || safeString(nextSkill.keyword) || '$autopilot', existingContextSnapshotPath, {
489
+ allowTaskSnapshotCreation: !(preserveExistingModeState || options.activeContinuation === true),
490
+ recoveryReason,
491
+ });
492
+ const contextSnapshotPath = contextSnapshot.path;
493
+ baseState.review_cycle = typeof reusableModeState?.review_cycle === 'number' ? reusableModeState.review_cycle : 0;
494
+ delete baseState.context_snapshot_path;
245
495
  baseState.state = {
246
496
  ...existingState,
247
497
  phase_cycle: Array.isArray(existingState.phase_cycle) ? existingState.phase_cycle : ['deep-interview', 'ralplan', 'ultragoal', 'code-review', 'ultraqa'],
@@ -261,6 +511,13 @@ async function persistStatefulSkillSeedState(stateDir, nextSkill, nowIso, previo
261
511
  code_review: null,
262
512
  ultraqa: null,
263
513
  ...existingHandoffs,
514
+ context_snapshot_path: contextSnapshotPath,
515
+ context_snapshot: {
516
+ path: contextSnapshotPath,
517
+ kind: contextSnapshot.kind,
518
+ ...(contextSnapshot.original_task_status ? { original_task_status: contextSnapshot.original_task_status } : {}),
519
+ ...(contextSnapshot.recovery ? { recovery: contextSnapshot.recovery } : {}),
520
+ },
264
521
  },
265
522
  review_verdict: Object.prototype.hasOwnProperty.call(existingState, 'review_verdict')
266
523
  ? existingState.review_verdict
@@ -271,6 +528,7 @@ async function persistStatefulSkillSeedState(stateDir, nextSkill, nowIso, previo
271
528
  return_to_ralplan_reason: Object.prototype.hasOwnProperty.call(existingState, 'return_to_ralplan_reason')
272
529
  ? existingState.return_to_ralplan_reason
273
530
  : null,
531
+ ...(contextSnapshot.recovery ? { context_snapshot_recovery: contextSnapshot.recovery } : {}),
274
532
  deep_interview_gate: (existingState.deep_interview_gate && typeof existingState.deep_interview_gate === 'object')
275
533
  ? existingState.deep_interview_gate
276
534
  : {
@@ -307,7 +565,7 @@ const KEYWORD_MAP = KEYWORD_TRIGGER_DEFINITIONS.map((entry) => ({
307
565
  skill: entry.skill,
308
566
  priority: entry.priority,
309
567
  }));
310
- const KEYWORDS_REQUIRING_INTENT = new Set(['ralph', 'team', 'stop', 'abort', 'parallel', 'autoresearch', 'ultragoal']);
568
+ const KEYWORDS_REQUIRING_INTENT = new Set(['ralph', 'team', 'stop', 'abort', 'parallel', 'autoresearch', 'ultragoal', 'autopilot']);
311
569
  const DEEP_INTERVIEW_ACTIVATION_PATTERNS = [
312
570
  /(?:^|[^\w])\$(?:deep-interview)\b/i,
313
571
  /\/prompts:deep-interview\b/i,
@@ -379,6 +637,13 @@ const KEYWORD_INTENT_PATTERNS = {
379
637
  /\b(?:use|run|start|enable|launch|invoke|activate|resume|continue)\s+(?:the\s+)?ultragoal\b/i,
380
638
  /\bultragoal\s+(?:mode|workflow|skill|loop|plan|goals?)\b/i,
381
639
  ],
640
+ autopilot: [
641
+ /(?:^|[^\w])\$(?:autopilot)\b/i,
642
+ /^\s*\/autopilot\b/i,
643
+ /^\s*(?:please\s+)?autopilot(?:\s+(?:this|mode|workflow|skill|loop|now))?\s*[.!]?\s*$/i,
644
+ /\b(?:use|run|start|enable|launch|invoke|activate|resume|continue)\s+(?:the\s+)?autopilot(?:\s+(?:mode|workflow|skill|loop|now))?\s*[.!]?\s*$/i,
645
+ /\bautopilot\s+(?:mode|workflow|skill|loop)\b/i,
646
+ ],
382
647
  };
383
648
  function hasExplicitPromptsInvocation(text) {
384
649
  return /(?:^|\s)\/prompts:[\w.-]+(?=[\s.,!?;:]|$)/i.test(text);
@@ -497,6 +762,13 @@ export function detectPrimaryKeyword(text) {
497
762
  const matches = detectKeywords(text);
498
763
  return matches.length > 0 ? matches[0] : null;
499
764
  }
765
+ function filterMatchesForTeamMode(matches, teamEnabled) {
766
+ return teamEnabled ? matches : matches.filter((entry) => entry.skill !== 'team');
767
+ }
768
+ function detectPrimaryKeywordForTeamMode(text, teamEnabled) {
769
+ const matches = filterMatchesForTeamMode(detectKeywords(text), teamEnabled);
770
+ return matches[0] ?? null;
771
+ }
500
772
  function isActiveSkillContinuationPrompt(text) {
501
773
  const normalized = text.trim();
502
774
  if (!normalized)
@@ -509,13 +781,56 @@ function isNamedActiveSkillContinuationPrompt(text, skill) {
509
781
  return false;
510
782
  return new RegExp(`^[\\\\/]?\\s*${normalizedSkill}\\b(?:\\s+(?:keep\\s+going|continue|resume))(?:\\s+now)?[.!]?\\s*$`, 'i').test(text.trim());
511
783
  }
784
+ function isOmxQuestionAnsweredPrompt(text) {
785
+ return /^\s*\[omx question answered\]/i.test(text.trim());
786
+ }
512
787
  function shouldReusePreviousSkillForContinuation(text, previous) {
513
788
  const previousSkill = safeString(previous?.skill).trim();
514
789
  if (!previousSkill || previous?.active !== true || !isTrackedWorkflowMode(previousSkill)) {
515
790
  return false;
516
791
  }
517
792
  return isActiveSkillContinuationPrompt(text)
518
- || isNamedActiveSkillContinuationPrompt(text, previousSkill);
793
+ || isNamedActiveSkillContinuationPrompt(text, previousSkill)
794
+ || ((previousSkill === 'autopilot' || previousSkill === 'deep-interview') && isOmxQuestionAnsweredPrompt(text));
795
+ }
796
+ function isAutopilotSupervisedChildSkill(skill) {
797
+ return skill === 'code-review'
798
+ || skill === 'ultraqa'
799
+ || skill === 'ralplan'
800
+ || skill === 'ultragoal'
801
+ || skill === 'deep-interview';
802
+ }
803
+ const AUTOPILOT_SUPERVISED_TRACKED_CHILD_SKILLS = [
804
+ 'deep-interview',
805
+ 'ralplan',
806
+ 'ultragoal',
807
+ 'ultraqa',
808
+ ];
809
+ async function reconcileAutopilotSupervisedChildModeStates(cwd, stateDir, sessionId, childSkill, nowIso) {
810
+ if (!isTrackedWorkflowMode(childSkill))
811
+ return [];
812
+ const activeChildModes = [];
813
+ for (const mode of AUTOPILOT_SUPERVISED_TRACKED_CHILD_SKILLS) {
814
+ const candidatePaths = [
815
+ resolveSeedStateFilePath(stateDir, mode, sessionId).absolutePath,
816
+ ];
817
+ for (const candidatePath of candidatePaths) {
818
+ const existing = await readJsonStateIfExists(candidatePath);
819
+ if (!existing || existing.active !== true || safeString(existing.mode).trim() !== mode)
820
+ continue;
821
+ activeChildModes.push(mode);
822
+ break;
823
+ }
824
+ }
825
+ const transition = await reconcileWorkflowTransition(cwd, childSkill, {
826
+ action: 'activate',
827
+ baseStateDir: stateDir,
828
+ currentModes: activeChildModes,
829
+ nowIso,
830
+ sessionId,
831
+ source: 'autopilot-supervised-child',
832
+ });
833
+ return transition.completedPaths;
519
834
  }
520
835
  function isDeepInterviewRuntimeConfig(value) {
521
836
  if (!value || typeof value !== 'object' || Array.isArray(value))
@@ -535,10 +850,12 @@ function resolveContinuationKeywordMatch(text, previous, fallbackMatch) {
535
850
  if (!previousSkill || previous?.active !== true || !isTrackedWorkflowMode(previousSkill)) {
536
851
  return fallbackMatch;
537
852
  }
538
- if (parseExplicitSkillInvocations(normalizeWorkflowKeyboardTypos(text)).sawExplicitLikeInvocation) {
853
+ const markedQuestionAnswerContinuation = (previousSkill === 'autopilot' || previousSkill === 'deep-interview')
854
+ && isOmxQuestionAnsweredPrompt(text);
855
+ if (!markedQuestionAnswerContinuation && parseExplicitSkillInvocations(normalizeWorkflowKeyboardTypos(text)).sawExplicitLikeInvocation) {
539
856
  return fallbackMatch;
540
857
  }
541
- if (!shouldReusePreviousSkillForContinuation(text, previous) && !safeString(fallbackMatch?.keyword).trim().startsWith('$')) {
858
+ if (!markedQuestionAnswerContinuation && !shouldReusePreviousSkillForContinuation(text, previous) && !safeString(fallbackMatch?.keyword).trim().startsWith('$')) {
542
859
  return fallbackMatch;
543
860
  }
544
861
  return {
@@ -584,12 +901,13 @@ export async function recordSkillActivation(input) {
584
901
  const previousRoot = await readExistingSkillState(rootStatePath);
585
902
  const previousSession = sessionStatePath ? await readExistingSkillState(sessionStatePath) : null;
586
903
  const previous = input.sessionId ? previousSession : previousRoot;
587
- const match = resolveContinuationKeywordMatch(input.text, previous, detectPrimaryKeyword(input.text));
904
+ const teamMode = readTeamModeConfig(sourceCwd);
905
+ const match = resolveContinuationKeywordMatch(input.text, previous, detectPrimaryKeywordForTeamMode(input.text, teamMode.enabled));
588
906
  if (!match)
589
907
  return null;
590
908
  const nowIso = input.nowIso ?? new Date().toISOString();
591
909
  const hadDeepInterviewLock = previous?.skill === 'deep-interview' && previous?.input_lock?.active === true;
592
- const matches = detectKeywords(input.text);
910
+ const matches = filterMatchesForTeamMode(detectKeywords(input.text), teamMode.enabled);
593
911
  const hasCancelIntent = matches.some((entry) => entry.skill === 'cancel');
594
912
  if (hasCancelIntent && hadDeepInterviewLock) {
595
913
  const state = {
@@ -619,7 +937,14 @@ export async function recordSkillActivation(input) {
619
937
  const sameSkill = previous?.active === true && previous.skill === match.skill;
620
938
  const sameKeyword = previous?.keyword?.toLowerCase() === match.keyword.toLowerCase();
621
939
  const sameSkillContinuation = sameSkill && shouldReusePreviousSkillForContinuation(input.text, previous);
622
- const preserveActivatedAt = sameSkill && (sameKeyword || sameSkillContinuation);
940
+ const matchedSeedConfig = STATEFUL_SKILL_SEED_CONFIG[match.skill];
941
+ const matchedModeState = matchedSeedConfig
942
+ ? await readJsonStateIfExists(resolveSeedStateFilePath(input.stateDir, matchedSeedConfig.mode, input.sessionId, matchedSeedConfig.scope).absolutePath)
943
+ : null;
944
+ const matchedModeTerminal = matchedSeedConfig
945
+ ? isResettableTerminalModeState(matchedModeState, matchedSeedConfig.mode)
946
+ : false;
947
+ const preserveActivatedAt = sameSkill && !matchedModeTerminal && (sameKeyword || sameSkillContinuation);
623
948
  const previousEntries = listActiveSkills(previous ?? {});
624
949
  const previousWorkflowEntries = previousEntries.filter((entry) => (isTrackedWorkflowMode(entry.skill)
625
950
  && (!input.sessionId
@@ -627,12 +952,16 @@ export async function recordSkillActivation(input) {
627
952
  || safeString(entry.session_id).trim() === safeString(input.sessionId).trim())));
628
953
  const isTrackedWorkflowMatch = isTrackedWorkflowMode(match.skill);
629
954
  const trackedMatchSkill = isTrackedWorkflowMatch ? match.skill : null;
955
+ const markedQuestionAnswerContinuation = sameSkill
956
+ && (match.skill === 'autopilot' || match.skill === 'deep-interview')
957
+ && isOmxQuestionAnsweredPrompt(input.text);
630
958
  const normalizedInputText = isTrackedWorkflowMatch
631
959
  ? normalizeWorkflowKeyboardTypos(input.text)
632
960
  : input.text;
633
- const workflowMatches = isTrackedWorkflowMatch
961
+ const workflowMatches = isTrackedWorkflowMatch && !markedQuestionAnswerContinuation
634
962
  ? parseExplicitSkillInvocations(normalizedInputText).matches
635
963
  .map((entry) => entry.skill)
964
+ .filter((skill) => teamMode.enabled || skill !== 'team')
636
965
  .filter(isTrackedWorkflowMode)
637
966
  : [];
638
967
  const resolvedWorkflowRequest = isTrackedWorkflowMatch
@@ -651,6 +980,49 @@ export async function recordSkillActivation(input) {
651
980
  const deepInterviewConfig = willActivateDeepInterview
652
981
  ? reusableDeepInterviewConfig ?? resolveDeepInterviewRuntimeConfig({ cwd: sourceCwd, text: input.text })
653
982
  : null;
983
+ if (previous?.active === true && previous.skill === 'autopilot' && isAutopilotSupervisedChildSkill(match.skill)) {
984
+ const nextState = {
985
+ ...previous,
986
+ version: 1,
987
+ active: true,
988
+ updated_at: nowIso,
989
+ source: 'keyword-detector',
990
+ session_id: input.sessionId ?? previous.session_id,
991
+ thread_id: input.threadId ?? previous.thread_id,
992
+ turn_id: input.turnId ?? previous.turn_id,
993
+ active_skills: listActiveSkills(previous).map((entry) => (entry.skill === 'autopilot'
994
+ ? {
995
+ ...entry,
996
+ active: true,
997
+ updated_at: nowIso,
998
+ session_id: input.sessionId ?? entry.session_id,
999
+ thread_id: input.threadId ?? entry.thread_id,
1000
+ turn_id: input.turnId ?? entry.turn_id,
1001
+ }
1002
+ : entry)),
1003
+ supervised_child_keyword: match.keyword,
1004
+ supervised_child_skill: match.skill,
1005
+ };
1006
+ try {
1007
+ await reconcileAutopilotSupervisedChildModeStates(sourceCwd, input.stateDir, input.sessionId ?? previous.session_id, match.skill, nowIso);
1008
+ await writeSkillActiveStateCopiesForStateDir(input.stateDir, nextState, input.sessionId, selectRootSkillStateCopy(previousRoot, nextState, input.sessionId));
1009
+ }
1010
+ catch (error) {
1011
+ return {
1012
+ ...previous,
1013
+ version: 1,
1014
+ active: true,
1015
+ updated_at: nowIso,
1016
+ source: 'keyword-detector',
1017
+ session_id: input.sessionId ?? previous.session_id,
1018
+ thread_id: input.threadId ?? previous.thread_id,
1019
+ turn_id: input.turnId ?? previous.turn_id,
1020
+ active_skills: listActiveSkills(previous),
1021
+ transition_error: error instanceof Error ? error.message : String(error),
1022
+ };
1023
+ }
1024
+ return nextState;
1025
+ }
654
1026
  if (isTrackedWorkflowMatch) {
655
1027
  let nextWorkflowEntries = previousWorkflowEntries.map((entry) => ({ ...entry }));
656
1028
  const transitionMessages = [];
@@ -676,13 +1048,35 @@ export async function recordSkillActivation(input) {
676
1048
  };
677
1049
  }
678
1050
  if (decision.autoCompleteModes.length > 0) {
679
- const transition = await reconcileWorkflowTransition(sourceCwd, requestedMode, {
680
- action: 'activate',
681
- sessionId: input.sessionId,
682
- source: 'keyword-detector',
683
- baseStateDir: input.stateDir,
684
- currentModes: nextWorkflowEntries.map((entry) => entry.skill),
685
- });
1051
+ let transition;
1052
+ try {
1053
+ transition = await reconcileWorkflowTransition(sourceCwd, requestedMode, {
1054
+ action: 'activate',
1055
+ sessionId: input.sessionId,
1056
+ source: 'keyword-detector',
1057
+ baseStateDir: input.stateDir,
1058
+ currentModes: nextWorkflowEntries.map((entry) => entry.skill),
1059
+ });
1060
+ }
1061
+ catch (error) {
1062
+ return {
1063
+ ...(previous ?? {}),
1064
+ version: 1,
1065
+ active: previous?.active ?? nextWorkflowEntries.length > 0,
1066
+ skill: previous?.skill || match.skill,
1067
+ keyword: previous?.keyword || match.keyword,
1068
+ phase: previous?.phase || initialWorkflowPhaseForMode(trackedMatchSkill),
1069
+ activated_at: previous?.activated_at || nowIso,
1070
+ updated_at: nowIso,
1071
+ source: 'keyword-detector',
1072
+ session_id: input.sessionId ?? previous?.session_id,
1073
+ thread_id: input.threadId ?? previous?.thread_id,
1074
+ turn_id: input.turnId ?? previous?.turn_id,
1075
+ active_skills: previousEntries,
1076
+ ...(previous?.input_lock ? { input_lock: previous.input_lock } : {}),
1077
+ transition_error: error instanceof Error ? error.message : String(error),
1078
+ };
1079
+ }
686
1080
  if (transition.transitionMessage) {
687
1081
  transitionMessages.push(transition.transitionMessage);
688
1082
  }
@@ -691,7 +1085,7 @@ export async function recordSkillActivation(input) {
691
1085
  nextWorkflowEntries = nextWorkflowEntries.filter((entry) => (isTrackedWorkflowMode(entry.skill) && survivingSkills.has(entry.skill)));
692
1086
  const existingEntry = nextWorkflowEntries.find((entry) => entry.skill === requestedMode);
693
1087
  if (existingEntry) {
694
- existingEntry.phase = requestedMode === match.skill && !sameSkill
1088
+ existingEntry.phase = requestedMode === match.skill && (!sameSkill || matchedModeTerminal)
695
1089
  ? initialWorkflowPhaseForMode(requestedMode)
696
1090
  : existingEntry.phase;
697
1091
  existingEntry.active = true;
@@ -753,7 +1147,7 @@ export async function recordSkillActivation(input) {
753
1147
  activated_at: requestedEntry.activated_at || workflowState.activated_at,
754
1148
  updated_at: requestedEntry.updated_at || workflowState.updated_at,
755
1149
  ...(requestedEntry.skill === 'deep-interview' && deepInterviewConfig ? { deep_interview_config: deepInterviewConfig } : {}),
756
- }, nowIso, previous);
1150
+ }, nowIso, previous, input.text, sourceCwd, { activeContinuation: requestedEntry.skill === 'autopilot' && sameSkillContinuation });
757
1151
  if (requestedEntry.skill === workflowState.skill) {
758
1152
  nextState = {
759
1153
  ...workflowState,
@@ -798,7 +1192,7 @@ export async function recordSkillActivation(input) {
798
1192
  ...(match.skill === 'deep-interview' && deepInterviewConfig ? { deep_interview_config: deepInterviewConfig } : {}),
799
1193
  };
800
1194
  try {
801
- const nextState = await persistStatefulSkillSeedState(input.stateDir, state, nowIso, previous);
1195
+ const nextState = await persistStatefulSkillSeedState(input.stateDir, state, nowIso, previous, input.text, sourceCwd, { activeContinuation: match.skill === 'autopilot' && sameSkillContinuation });
802
1196
  nextState.active_skills = buildActiveSkills(nextState);
803
1197
  await writeSkillActiveStateCopiesForStateDir(input.stateDir, nextState, input.sessionId, selectRootSkillStateCopy(previousRoot, nextState, input.sessionId));
804
1198
  await persistDeepInterviewModeState(input.stateDir, nextState, nowIso, previous, input);
@@ -914,7 +1308,9 @@ export function applyRalplanGate(keywords, text, options = {}) {
914
1308
  return { keywords, gateApplied: false, gatedKeywords: [] };
915
1309
  }
916
1310
  const planningComplete = isPlanningComplete(readPlanningArtifacts(options.cwd ?? process.cwd()));
917
- const consensusComplete = hasDurableRalplanConsensusEvidenceForCwd(options.cwd ?? process.cwd());
1311
+ const consensusComplete = hasDurableRalplanConsensusEvidenceForCwd(options.cwd ?? process.cwd(), {
1312
+ requireNativeSubagents: options.requireNativeSubagents,
1313
+ });
918
1314
  const shortFollowupBypasses = executionKeywords.filter((keyword) => {
919
1315
  if (keyword !== 'team' && keyword !== 'ralph')
920
1316
  return false;