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.
- package/Cargo.lock +6 -6
- package/Cargo.toml +1 -1
- package/README.md +59 -10
- package/crates/omx-sparkshell/tests/execution.rs +1 -1
- package/dist/agents/__tests__/definitions.test.js +11 -0
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +56 -6
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/definitions.d.ts +10 -0
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +5 -1
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/native-config.d.ts +5 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +19 -4
- package/dist/agents/native-config.js.map +1 -1
- package/dist/autopilot/__tests__/fsm.test.d.ts +2 -0
- package/dist/autopilot/__tests__/fsm.test.d.ts.map +1 -0
- package/dist/autopilot/__tests__/fsm.test.js +75 -0
- package/dist/autopilot/__tests__/fsm.test.js.map +1 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.d.ts +2 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.d.ts.map +1 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js +79 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -0
- package/dist/autopilot/deep-interview-gate.d.ts +18 -0
- package/dist/autopilot/deep-interview-gate.d.ts.map +1 -0
- package/dist/autopilot/deep-interview-gate.js +256 -0
- package/dist/autopilot/deep-interview-gate.js.map +1 -0
- package/dist/autopilot/fsm.d.ts +13 -0
- package/dist/autopilot/fsm.d.ts.map +1 -0
- package/dist/autopilot/fsm.js +70 -0
- package/dist/autopilot/fsm.js.map +1 -0
- package/dist/autopilot/ralplan-gate.d.ts +17 -0
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -0
- package/dist/autopilot/ralplan-gate.js +61 -0
- package/dist/autopilot/ralplan-gate.js.map +1 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js +512 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +39 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +83 -7
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +175 -6
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +8 -4
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js +100 -0
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +13 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +14 -0
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +89 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +83 -0
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/state.test.js +21 -0
- package/dist/cli/__tests__/state.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +2 -2
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +110 -2
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +8 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +14 -3
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +298 -50
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +14 -2
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +62 -15
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/question.d.ts.map +1 -1
- package/dist/cli/question.js +36 -5
- package/dist/cli/question.js.map +1 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +3 -1
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/setup-preferences.d.ts +2 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -1
- package/dist/cli/setup-preferences.js +4 -0
- package/dist/cli/setup-preferences.js.map +1 -1
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +166 -27
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/state.d.ts.map +1 -1
- package/dist/cli/state.js +8 -1
- package/dist/cli/state.js.map +1 -1
- package/dist/cli/tmux-hook.d.ts.map +1 -1
- package/dist/cli/tmux-hook.js +16 -0
- package/dist/cli/tmux-hook.js.map +1 -1
- package/dist/cli/update.d.ts +2 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +47 -3
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/deep-interview.test.js +7 -6
- package/dist/config/__tests__/deep-interview.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +1 -0
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/deep-interview.d.ts.map +1 -1
- package/dist/config/deep-interview.js +14 -4
- package/dist/config/deep-interview.js.map +1 -1
- package/dist/config/generator.d.ts +2 -2
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +2 -2
- package/dist/config/generator.js.map +1 -1
- package/dist/config/team-mode.d.ts +12 -0
- package/dist/config/team-mode.d.ts.map +1 -0
- package/dist/config/team-mode.js +91 -0
- package/dist/config/team-mode.js.map +1 -0
- package/dist/hooks/__tests__/agents-overlay.test.js +88 -0
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +8 -0
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.js +8 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js +10 -0
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +1072 -14
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +64 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +189 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +35 -2
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +3 -3
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/session.test.js +25 -0
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/__tests__/skill-guidance-contract.test.js +21 -0
- package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +36 -50
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/deep-interview-config-instruction.js +1 -1
- package/dist/hooks/deep-interview-config-instruction.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +31 -0
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +1 -1
- package/dist/hooks/extensibility/plugin-runner.js +17 -21
- package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +1 -0
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +428 -32
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/keyword-registry.d.ts.map +1 -1
- package/dist/hooks/keyword-registry.js +1 -0
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +6 -0
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/session.d.ts +3 -0
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +13 -5
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/authority.test.js +469 -31
- package/dist/hud/__tests__/authority.test.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +2 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/index.test.js +210 -2
- package/dist/hud/__tests__/index.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +588 -28
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/render.test.js +61 -0
- package/dist/hud/__tests__/render.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +208 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +314 -22
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/authority.d.ts +5 -0
- package/dist/hud/authority.d.ts.map +1 -1
- package/dist/hud/authority.js +337 -30
- package/dist/hud/authority.js.map +1 -1
- package/dist/hud/index.d.ts +20 -2
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +103 -26
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/reconcile.d.ts +3 -3
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +129 -20
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +35 -0
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +64 -50
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/tmux.d.ts +26 -6
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +173 -38
- package/dist/hud/tmux.js.map +1 -1
- package/dist/hud/types.d.ts +11 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/mcp/__tests__/hermes-bridge.test.js +203 -7
- package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +71 -1
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +13 -1
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/hermes-bridge.d.ts +12 -2
- package/dist/mcp/hermes-bridge.d.ts.map +1 -1
- package/dist/mcp/hermes-bridge.js +83 -9
- package/dist/mcp/hermes-bridge.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +32 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +113 -17
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts +4 -4
- package/dist/modes/__tests__/base-autoresearch-contract.test.js +7 -1
- package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +130 -0
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/orchestrator.js +1 -1
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/stages/ralplan.d.ts +1 -0
- package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
- package/dist/pipeline/stages/ralplan.js +14 -5
- package/dist/pipeline/stages/ralplan.js.map +1 -1
- package/dist/question/__tests__/deep-interview.test.js +160 -2
- package/dist/question/__tests__/deep-interview.test.js.map +1 -1
- package/dist/question/__tests__/policy.test.js +63 -3
- package/dist/question/__tests__/policy.test.js.map +1 -1
- package/dist/question/__tests__/renderer.test.js +191 -2
- package/dist/question/__tests__/renderer.test.js.map +1 -1
- package/dist/question/__tests__/state.test.js +94 -3
- package/dist/question/__tests__/state.test.js.map +1 -1
- package/dist/question/__tests__/ui.test.js +4 -0
- package/dist/question/__tests__/ui.test.js.map +1 -1
- package/dist/question/autopilot-wait.d.ts +12 -2
- package/dist/question/autopilot-wait.d.ts.map +1 -1
- package/dist/question/autopilot-wait.js +158 -47
- package/dist/question/autopilot-wait.js.map +1 -1
- package/dist/question/deep-interview.d.ts.map +1 -1
- package/dist/question/deep-interview.js +22 -6
- package/dist/question/deep-interview.js.map +1 -1
- package/dist/question/policy.d.ts.map +1 -1
- package/dist/question/policy.js +2 -5
- package/dist/question/policy.js.map +1 -1
- package/dist/question/renderer.d.ts +12 -0
- package/dist/question/renderer.d.ts.map +1 -1
- package/dist/question/renderer.js +87 -3
- package/dist/question/renderer.js.map +1 -1
- package/dist/question/state.d.ts +8 -1
- package/dist/question/state.d.ts.map +1 -1
- package/dist/question/state.js +54 -14
- package/dist/question/state.js.map +1 -1
- package/dist/question/types.d.ts +1 -1
- package/dist/question/types.d.ts.map +1 -1
- package/dist/question/ui.d.ts +1 -0
- package/dist/question/ui.d.ts.map +1 -1
- package/dist/question/ui.js +1 -0
- package/dist/question/ui.js.map +1 -1
- package/dist/ralplan/__tests__/runtime.test.js +191 -0
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts +9 -1
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +84 -2
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/ralplan/runtime.d.ts +9 -0
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +32 -11
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +2315 -280
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-state-io.test.js +72 -1
- package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts +2 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.js +57 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -0
- package/dist/scripts/__tests__/run-test-files.test.js +74 -0
- package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
- package/dist/scripts/__tests__/verify-native-agents.test.js +65 -0
- package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +431 -56
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +79 -1
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/eval/eval-parity-smoke.js +1 -1
- package/dist/scripts/eval/eval-parity-smoke.js.map +1 -1
- package/dist/scripts/hook-payload-guard.d.ts +9 -0
- package/dist/scripts/hook-payload-guard.d.ts.map +1 -0
- package/dist/scripts/hook-payload-guard.js +111 -0
- package/dist/scripts/hook-payload-guard.js.map +1 -0
- package/dist/scripts/notify-fallback-watcher.js +8 -1
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/__tests__/payload-guard.test.d.ts +2 -0
- package/dist/scripts/notify-hook/__tests__/payload-guard.test.d.ts.map +1 -0
- package/dist/scripts/notify-hook/__tests__/payload-guard.test.js +39 -0
- package/dist/scripts/notify-hook/__tests__/payload-guard.test.js.map +1 -0
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +3 -1
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +3 -10
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +62 -38
- package/dist/scripts/notify-hook/state-io.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +7 -0
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.js +234 -86
- package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts +7 -0
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +24 -18
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +86 -13
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/run-test-files.js +193 -22
- package/dist/scripts/run-test-files.js.map +1 -1
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +61 -3
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/scripts/verify-native-agents.d.ts.map +1 -1
- package/dist/scripts/verify-native-agents.js +58 -1
- package/dist/scripts/verify-native-agents.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +1125 -1
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +46 -1
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +98 -7
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +159 -2
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.js +6 -8
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts +6 -0
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +38 -15
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/state/workflow-transition.d.ts.map +1 -1
- package/dist/state/workflow-transition.js +10 -3
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/subagents/__tests__/tracker.test.js +139 -0
- package/dist/subagents/__tests__/tracker.test.js.map +1 -1
- package/dist/subagents/tracker.d.ts +3 -0
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +41 -4
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/coordination-protocol.test.d.ts +2 -0
- package/dist/team/__tests__/coordination-protocol.test.d.ts.map +1 -0
- package/dist/team/__tests__/coordination-protocol.test.js +173 -0
- package/dist/team/__tests__/coordination-protocol.test.js.map +1 -0
- package/dist/team/__tests__/runtime.test.js +52 -3
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +9 -4
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +83 -0
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +240 -2
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +84 -0
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +4 -2
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/coordination-protocol.d.ts +14 -0
- package/dist/team/coordination-protocol.d.ts.map +1 -0
- package/dist/team/coordination-protocol.js +244 -0
- package/dist/team/coordination-protocol.js.map +1 -0
- package/dist/team/runtime.d.ts +1 -0
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +19 -3
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +3 -2
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/tasks.d.ts.map +1 -1
- package/dist/team/state/tasks.js +24 -0
- package/dist/team/state/tasks.js.map +1 -1
- package/dist/team/state/types.d.ts +21 -1
- package/dist/team/state/types.d.ts.map +1 -1
- package/dist/team/state/types.js.map +1 -1
- package/dist/team/state.d.ts +17 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +12 -5
- package/dist/team/state.js.map +1 -1
- package/dist/team/team-ops.d.ts +1 -1
- package/dist/team/team-ops.d.ts.map +1 -1
- package/dist/team/team-ops.js.map +1 -1
- package/dist/team/tmux-session.d.ts +2 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +161 -13
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +63 -0
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/utils/__tests__/agents-model-table.test.js +4 -2
- package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
- package/dist/utils/agents-model-table.d.ts.map +1 -1
- package/dist/utils/agents-model-table.js +3 -0
- package/dist/utils/agents-model-table.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +81 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/package.json +8 -8
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +334 -21
- package/plugins/oh-my-codex/hooks/hooks.json +1 -2
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +13 -6
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +7 -7
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +9 -4
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -22
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +12 -0
- package/plugins/oh-my-codex/skills/team/SKILL.md +16 -0
- package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +9 -0
- package/plugins/oh-my-codex/skills/worker/SKILL.md +14 -0
- package/skills/autopilot/SKILL.md +13 -6
- package/skills/code-review/SKILL.md +7 -7
- package/skills/deep-interview/SKILL.md +9 -4
- package/skills/ralph/SKILL.md +22 -22
- package/skills/ralplan/SKILL.md +12 -0
- package/skills/team/SKILL.md +16 -0
- package/skills/ultraqa/SKILL.md +9 -0
- package/skills/worker/SKILL.md +14 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +4435 -2083
- package/src/scripts/__tests__/notify-state-io.test.ts +95 -0
- package/src/scripts/__tests__/notify-tmux-injection.test.ts +82 -0
- package/src/scripts/__tests__/run-test-files.test.ts +102 -0
- package/src/scripts/__tests__/verify-native-agents.test.ts +75 -0
- package/src/scripts/codex-native-hook.ts +536 -51
- package/src/scripts/codex-native-pre-post.ts +80 -0
- package/src/scripts/demo-team-e2e.sh +10 -7
- package/src/scripts/eval/eval-parity-smoke.ts +1 -1
- package/src/scripts/hook-payload-guard.ts +113 -0
- package/src/scripts/notify-fallback-watcher.ts +8 -1
- package/src/scripts/notify-hook/__tests__/payload-guard.test.ts +41 -0
- package/src/scripts/notify-hook/auto-nudge.ts +3 -1
- package/src/scripts/notify-hook/ralph-session-resume.ts +2 -8
- package/src/scripts/notify-hook/state-io.ts +75 -37
- package/src/scripts/notify-hook/team-leader-nudge.ts +7 -0
- package/src/scripts/notify-hook/team-worker-stop.ts +193 -52
- package/src/scripts/notify-hook/tmux-injection.ts +35 -19
- package/src/scripts/notify-hook.ts +105 -6
- package/src/scripts/run-test-files.ts +192 -22
- package/src/scripts/sync-plugin-mirror.ts +98 -9
- package/src/scripts/verify-native-agents.ts +65 -1
|
@@ -4,8 +4,9 @@ import { appendFile, mkdir, readFile, readdir, stat, writeFile } from "fs/promis
|
|
|
4
4
|
import { extname, join, relative, resolve } from "path";
|
|
5
5
|
import { pathToFileURL } from "url";
|
|
6
6
|
import { readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
|
|
7
|
+
import { redactAuthSecrets } from "../auth/redact.js";
|
|
7
8
|
import { SKILL_ACTIVE_STATE_FILE, extractSessionIdFromInitializedStatePath, getSkillActiveStatePathsForStateDir, listActiveSkills, readSkillActiveState, readVisibleSkillActiveStateForStateDir, } from "../state/skill-active.js";
|
|
8
|
-
import { readSubagentSessionSummary, readSubagentTrackingState, recordSubagentTurnForSession, } from "../subagents/tracker.js";
|
|
9
|
+
import { isTrustedSubagentThread, readSubagentSessionSummary, readSubagentTrackingState, recordSubagentTurnForSession, } from "../subagents/tracker.js";
|
|
9
10
|
import { resolveCanonicalTeamStateRoot, resolveWorkerNotifyTeamStateRootPath } from "../team/state-root.js";
|
|
10
11
|
import { appendToLog, isSessionStateUsable, readSessionState, readUsableSessionState, reconcileNativeSessionStart, } from "../hooks/session.js";
|
|
11
12
|
import { appendTeamEvent, readTeamLeaderAttention, readTeamConfig, readTeamManifestV2, readTeamPhase, writeTeamLeaderAttention, writeTeamPhase, } from "../team/state.js";
|
|
@@ -14,6 +15,7 @@ import { findGitLayout } from "../utils/git-layout.js";
|
|
|
14
15
|
import { getBaseStateDir, getStateFilePath, getStatePath } from "../mcp/state-paths.js";
|
|
15
16
|
import { detectKeywords, detectPrimaryKeyword, recordSkillActivation, } from "../hooks/keyword-detector.js";
|
|
16
17
|
import { buildDeepInterviewConfigInstruction } from "../hooks/deep-interview-config-instruction.js";
|
|
18
|
+
import { readTeamModeConfig } from "../config/team-mode.js";
|
|
17
19
|
import { detectNativeStopStallPattern, loadAutoNudgeConfig, normalizeAutoNudgeSignatureText, resolveEffectiveAutoNudgeResponse, } from "./notify-hook/auto-nudge.js";
|
|
18
20
|
import { SLOPPY_FALLBACK_GROUNDING_PATTERNS, SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS, SLOPPY_FALLBACK_PHRASE_PATTERNS, buildNativePostToolUseOutput, buildNativePreToolUseOutput, detectMcpTransportFailure, hasAnyPattern, } from "./codex-native-pre-post.js";
|
|
19
21
|
import { handleTeamWorkerPostToolUseSuccess } from "./notify-hook/team-worker-posttooluse.js";
|
|
@@ -36,6 +38,7 @@ import { isPendingDeepInterviewQuestionEnforcement, reconcileDeepInterviewQuesti
|
|
|
36
38
|
import { readAutopilotDeepInterviewQuestionWaitState } from "../question/autopilot-wait.js";
|
|
37
39
|
import { buildDocumentRefreshAdvisoryOutput, evaluateFinalHandoffDocumentRefresh, isFinalHandoffDocumentRefreshCandidate, } from "../document-refresh/enforcer.js";
|
|
38
40
|
import { buildExecFollowupStopOutput } from "../exec/followup.js";
|
|
41
|
+
import { MAX_NATIVE_STDIN_JSON_BYTES, extractRawCodexHookEventName, } from "./hook-payload-guard.js";
|
|
39
42
|
const TERMINAL_MODE_PHASES = new Set(["complete", "completed", "failed", "cancelled"]);
|
|
40
43
|
const SKILL_STOP_BLOCKERS = new Set(["ralplan"]);
|
|
41
44
|
const TEAM_STOP_BLOCKING_TASK_STATUSES = new Set(["pending", "in_progress", "blocked"]);
|
|
@@ -45,6 +48,7 @@ const ORDINARY_STOP_NO_PROGRESS_DEFAULT_MAX_REPEATS = 8;
|
|
|
45
48
|
const RALPH_ORPHANED_STARTING_STALE_MS = 15 * 60_000;
|
|
46
49
|
const ORDINARY_STOP_NO_PROGRESS_DEFAULT_IDLE_MS = 10 * 60_000;
|
|
47
50
|
const ORDINARY_STOP_NO_PROGRESS_MAX_MESSAGE_LENGTH = 240;
|
|
51
|
+
const OMX_OWNER_SESSION_ID_PATTERN = /^omx-[A-Za-z0-9_-]{1,60}$/;
|
|
48
52
|
const STABLE_FINAL_RECOMMENDATION_PATTERNS = [
|
|
49
53
|
/^\s*(?:launch|release|ship)-?ready\s*:\s*(?:yes|no)\b[^\n\r]*/im,
|
|
50
54
|
/^\s*ready to release\s*:\s*(?:yes|no)\b[^\n\r]*/im,
|
|
@@ -71,6 +75,25 @@ function safeString(value) {
|
|
|
71
75
|
function safeObject(value) {
|
|
72
76
|
return value && typeof value === "object" ? value : {};
|
|
73
77
|
}
|
|
78
|
+
function resolveHudReconcileSessionId(currentSessionState, canonicalSessionId, sessionIdForState) {
|
|
79
|
+
const ownerOmxSessionId = safeString(currentSessionState?.owner_omx_session_id).trim();
|
|
80
|
+
if (OMX_OWNER_SESSION_ID_PATTERN.test(ownerOmxSessionId))
|
|
81
|
+
return ownerOmxSessionId;
|
|
82
|
+
return canonicalSessionId || sessionIdForState || undefined;
|
|
83
|
+
}
|
|
84
|
+
function resolveHudReconcileSessionIds(currentSessionState, canonicalSessionId, sessionIdForState, nativeSessionId) {
|
|
85
|
+
const ownerOmxSessionId = safeString(currentSessionState?.owner_omx_session_id).trim();
|
|
86
|
+
return uniqueNonEmpty([
|
|
87
|
+
resolveHudReconcileSessionId(currentSessionState, canonicalSessionId, sessionIdForState),
|
|
88
|
+
canonicalSessionId ?? undefined,
|
|
89
|
+
sessionIdForState ?? undefined,
|
|
90
|
+
nativeSessionId ?? undefined,
|
|
91
|
+
safeString(currentSessionState?.session_id),
|
|
92
|
+
safeString(currentSessionState?.native_session_id),
|
|
93
|
+
OMX_OWNER_SESSION_ID_PATTERN.test(ownerOmxSessionId) ? ownerOmxSessionId : undefined,
|
|
94
|
+
safeString(currentSessionState?.owner_codex_session_id),
|
|
95
|
+
]);
|
|
96
|
+
}
|
|
74
97
|
function safeContextSnippet(value, maxLength = 300) {
|
|
75
98
|
const text = safeString(value).replace(/\s+/g, " ").trim();
|
|
76
99
|
if (text.length <= maxLength)
|
|
@@ -134,18 +157,25 @@ function readNativeSubagentSessionStartMetadata(transcriptPath) {
|
|
|
134
157
|
}
|
|
135
158
|
}
|
|
136
159
|
async function recordNativeSubagentSessionStart(cwd, canonicalSessionId, childSessionId, metadata, transcriptPath) {
|
|
160
|
+
const parentThreadId = metadata.parentThreadId.trim();
|
|
161
|
+
const childThreadId = childSessionId.trim();
|
|
137
162
|
const trackingSessionIds = [...new Set([
|
|
138
163
|
canonicalSessionId.trim(),
|
|
139
|
-
|
|
164
|
+
parentThreadId,
|
|
140
165
|
].filter(Boolean))];
|
|
141
166
|
for (const sessionId of trackingSessionIds) {
|
|
167
|
+
if (parentThreadId && parentThreadId !== childThreadId) {
|
|
168
|
+
await recordSubagentTurnForSession(cwd, {
|
|
169
|
+
sessionId,
|
|
170
|
+
threadId: parentThreadId,
|
|
171
|
+
kind: 'leader',
|
|
172
|
+
}).catch(() => { });
|
|
173
|
+
}
|
|
142
174
|
await recordSubagentTurnForSession(cwd, {
|
|
143
175
|
sessionId,
|
|
144
|
-
threadId:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
sessionId,
|
|
148
|
-
threadId: childSessionId,
|
|
176
|
+
threadId: childThreadId,
|
|
177
|
+
kind: 'subagent',
|
|
178
|
+
...(parentThreadId && parentThreadId !== childThreadId ? { leaderThreadId: parentThreadId } : {}),
|
|
149
179
|
mode: metadata.agentRole,
|
|
150
180
|
}).catch(() => { });
|
|
151
181
|
}
|
|
@@ -176,17 +206,42 @@ async function nativeSubagentSessionStartBelongsToCanonicalSession(cwd, canonica
|
|
|
176
206
|
return true;
|
|
177
207
|
return summary.allThreadIds.includes(parentThreadId);
|
|
178
208
|
}
|
|
179
|
-
async function isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, threadId) {
|
|
180
|
-
const
|
|
209
|
+
async function isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, threadId, canonicalLeaderNativeSessionId = "") {
|
|
210
|
+
const nativeId = nativeSessionId.trim();
|
|
211
|
+
const promptThreadId = threadId.trim();
|
|
212
|
+
const candidateIds = [nativeId, promptThreadId]
|
|
181
213
|
.map((value) => value.trim())
|
|
182
214
|
.filter(Boolean);
|
|
183
215
|
if (candidateIds.length === 0)
|
|
184
216
|
return false;
|
|
185
217
|
const sessionId = canonicalSessionId.trim();
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
218
|
+
const currentLeaderNativeSessionId = canonicalLeaderNativeSessionId.trim();
|
|
219
|
+
const summary = sessionId
|
|
220
|
+
? await readSubagentSessionSummary(cwd, sessionId).catch(() => null)
|
|
221
|
+
: null;
|
|
222
|
+
const currentLeaderIds = new Set([
|
|
223
|
+
currentLeaderNativeSessionId,
|
|
224
|
+
summary?.leaderThreadId?.trim(),
|
|
225
|
+
].filter(Boolean));
|
|
226
|
+
if (summary
|
|
227
|
+
&& candidateIds.some((id) => !currentLeaderIds.has(id) && summary.allSubagentThreadIds.includes(id))) {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
// Native UserPromptSubmit can carry a per-turn thread_id that differs from
|
|
231
|
+
// the long-lived native session id. Treat the current canonical native
|
|
232
|
+
// session as the leader before consulting stale/global tracker state.
|
|
233
|
+
if (sessionId
|
|
234
|
+
&& currentLeaderNativeSessionId
|
|
235
|
+
&& (nativeId === currentLeaderNativeSessionId
|
|
236
|
+
|| (!nativeId && promptThreadId === currentLeaderNativeSessionId))) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
if (summary) {
|
|
240
|
+
const leaderThreadId = summary.leaderThreadId?.trim();
|
|
241
|
+
if (leaderThreadId
|
|
242
|
+
&& (nativeId === leaderThreadId
|
|
243
|
+
|| (!nativeId && promptThreadId === leaderThreadId))) {
|
|
244
|
+
return false;
|
|
190
245
|
}
|
|
191
246
|
}
|
|
192
247
|
// Native Codex resume can report the child native session as the canonical
|
|
@@ -199,7 +254,7 @@ async function isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, th
|
|
|
199
254
|
const trackingState = await readSubagentTrackingState(cwd).catch(() => null);
|
|
200
255
|
if (!trackingState)
|
|
201
256
|
return false;
|
|
202
|
-
return Object.values(trackingState.sessions).some((session) => (candidateIds.some((id) => session
|
|
257
|
+
return Object.values(trackingState.sessions).some((session) => (candidateIds.some((id) => isTrustedSubagentThread(session, id))));
|
|
203
258
|
}
|
|
204
259
|
function shouldSuppressSubagentLifecycleHookDispatch() {
|
|
205
260
|
const config = getNotificationConfig();
|
|
@@ -1343,6 +1398,8 @@ function buildTeamHelpInstruction(cwd, payload) {
|
|
|
1343
1398
|
}).teamHelpInstruction;
|
|
1344
1399
|
}
|
|
1345
1400
|
function buildNativeOutsideTmuxTeamPromptBlockState(prompt, cwd, payload, sessionId, threadId, turnId) {
|
|
1401
|
+
if (!readTeamModeConfig(cwd).enabled)
|
|
1402
|
+
return null;
|
|
1346
1403
|
const match = detectPrimaryKeyword(prompt);
|
|
1347
1404
|
if (match?.skill !== "team")
|
|
1348
1405
|
return null;
|
|
@@ -1374,23 +1431,37 @@ function buildNativeOutsideTmuxTeamPromptBlockState(prompt, cwd, payload, sessio
|
|
|
1374
1431
|
function buildSkillStateCliInstruction(mode, statePath) {
|
|
1375
1432
|
return `skill: ${mode} activated and initial state initialized at ${statePath}; use CLI-first state updates via \`omx state write/read/clear --input '<json>' --json\`; use omx_state MCP only when explicit MCP compatibility is enabled.`;
|
|
1376
1433
|
}
|
|
1377
|
-
function buildAutopilotPromptActivationNote(skillState) {
|
|
1434
|
+
function buildAutopilotPromptActivationNote(skillState, options = {}) {
|
|
1378
1435
|
if (skillState?.initialized_mode !== "autopilot")
|
|
1379
1436
|
return null;
|
|
1437
|
+
const teamHandoff = readTeamModeConfig(options.cwd).enabled
|
|
1438
|
+
? " (+ $team if needed)"
|
|
1439
|
+
: "";
|
|
1380
1440
|
return [
|
|
1381
|
-
|
|
1441
|
+
`Autopilot protocol: the durable default chain is $deep-interview -> $ralplan -> $ultragoal${teamHandoff} -> $code-review -> $ultraqa (deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa).`,
|
|
1382
1442
|
"Start/resume at current_phase=deep-interview unless the task is clear and bounded; if deep-interview is intentionally skipped, persist and state an explicit deep_interview_gate.skip_reason before moving to ralplan.",
|
|
1443
|
+
"Deep-interview is a structured question chain, not a one-question gate: after an omx question answer, re-score ambiguity against the active threshold, treat max_rounds as a cap, and crystallize once ambiguity is at or below threshold and readiness gates pass.",
|
|
1444
|
+
options.markedQuestionAnswer
|
|
1445
|
+
? "This turn is a marked omx question answer. Treat ordinary selected option/freeform answer text as interview input, then re-score. Do not close merely because the first question was answered; if ambiguity is at or below threshold and readiness gates pass, write interview_complete evidence and hand off. Ask another deep-interview follow-up only when a readiness gate remains unresolved and the answer would materially change execution."
|
|
1446
|
+
: null,
|
|
1447
|
+
"Do not advance from deep-interview to ralplan merely because the first question was answered; persist explicit interview_complete evidence before setting current_phase=ralplan, and do advance when threshold plus readiness gates are satisfied.",
|
|
1383
1448
|
"The ralplan phase is not complete until Planner output has been reviewed sequentially by Architect and then Critic; do not hand off to Ultragoal or implementation until the ralplan state/artifact records both ralplan_architect_review and ralplan_critic_review with approval or an explicit blocker.",
|
|
1384
1449
|
"Do not silently fall back to ordinary $plan/ralplan-only handling; keep autopilot-state.json, skill-active-state.json, HUD/statusline, and Codex goal-mode handoff guidance visible while the workflow is active.",
|
|
1385
1450
|
"When Codex goal tools are available, call get_goal/create_goal only from the active thread handoff and treat the active goal as the completion contract until code-review and ultraqa are clean.",
|
|
1386
|
-
].join(" ");
|
|
1451
|
+
].filter(Boolean).join(" ");
|
|
1452
|
+
}
|
|
1453
|
+
function formatExecutionHandoffList(cwd) {
|
|
1454
|
+
return readTeamModeConfig(cwd).enabled
|
|
1455
|
+
? "`$ultragoal`, `$team`, or `$ralph`"
|
|
1456
|
+
: "`$ultragoal` or `$ralph`";
|
|
1387
1457
|
}
|
|
1388
1458
|
function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(), payload) {
|
|
1389
1459
|
if (!prompt)
|
|
1390
1460
|
return null;
|
|
1391
1461
|
const promptPriorityMessage = buildPromptPriorityMessage(prompt);
|
|
1392
|
-
const
|
|
1393
|
-
const
|
|
1462
|
+
const teamMode = readTeamModeConfig(cwd);
|
|
1463
|
+
const matches = detectKeywords(prompt).filter((entry) => teamMode.enabled || entry.skill !== "team");
|
|
1464
|
+
const match = matches[0] ?? null;
|
|
1394
1465
|
if (!match) {
|
|
1395
1466
|
const continuedSkill = safeString(skillState?.skill).trim();
|
|
1396
1467
|
if (!continuedSkill)
|
|
@@ -1399,6 +1470,8 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
|
|
|
1399
1470
|
? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
|
|
1400
1471
|
: null;
|
|
1401
1472
|
const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
|
|
1473
|
+
const markedQuestionAnswer = /^\s*\[omx question answered\]/i.test(prompt);
|
|
1474
|
+
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer, cwd });
|
|
1402
1475
|
return [
|
|
1403
1476
|
`OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}".`,
|
|
1404
1477
|
promptPriorityMessage,
|
|
@@ -1407,12 +1480,34 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
|
|
|
1407
1480
|
: null,
|
|
1408
1481
|
deepInterviewPromptActivationNote,
|
|
1409
1482
|
deepInterviewConfigPromptActivationNote,
|
|
1483
|
+
autopilotPromptActivationNote,
|
|
1410
1484
|
"Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
|
|
1411
1485
|
].filter(Boolean).join(" ");
|
|
1412
1486
|
}
|
|
1413
1487
|
const detectedKeywordMessage = matches.length > 1
|
|
1414
1488
|
? `OMX native UserPromptSubmit detected workflow keywords ${matches.map((entry) => `"${entry.keyword}" -> ${entry.skill}`).join(", ")}.`
|
|
1415
1489
|
: `OMX native UserPromptSubmit detected workflow keyword "${match.keyword}" -> ${match.skill}.`;
|
|
1490
|
+
const continuedSkill = safeString(skillState?.skill).trim();
|
|
1491
|
+
if (continuedSkill
|
|
1492
|
+
&& continuedSkill !== match.skill
|
|
1493
|
+
&& /^\s*\[omx question answered\]/i.test(prompt)) {
|
|
1494
|
+
const deepInterviewPromptActivationNote = skillState?.initialized_mode === "deep-interview"
|
|
1495
|
+
? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
|
|
1496
|
+
: null;
|
|
1497
|
+
const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
|
|
1498
|
+
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer: true, cwd });
|
|
1499
|
+
return [
|
|
1500
|
+
`OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}"; workflow-like tokens inside the marked omx question answer are treated as answer text, not a new workflow activation.`,
|
|
1501
|
+
promptPriorityMessage,
|
|
1502
|
+
skillState?.initialized_mode && skillState.initialized_state_path
|
|
1503
|
+
? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
|
|
1504
|
+
: null,
|
|
1505
|
+
deepInterviewPromptActivationNote,
|
|
1506
|
+
deepInterviewConfigPromptActivationNote,
|
|
1507
|
+
autopilotPromptActivationNote,
|
|
1508
|
+
"Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
|
|
1509
|
+
].filter(Boolean).join(" ");
|
|
1510
|
+
}
|
|
1416
1511
|
const activeSkills = Array.isArray(skillState?.active_skills)
|
|
1417
1512
|
? skillState.active_skills.map((entry) => entry.skill)
|
|
1418
1513
|
: [];
|
|
@@ -1433,7 +1528,7 @@ function buildAdditionalContextMessage(prompt, skillState, cwd = process.cwd(),
|
|
|
1433
1528
|
const ultragoalPromptActivationNote = match.skill === "ultragoal"
|
|
1434
1529
|
? "Ultragoal protocol: use `omx ultragoal create-goals` / `complete-goals` / `checkpoint` for `.omx/ultragoal` artifacts, then use Codex goal model tools only from the active agent handoff (`get_goal`, `create_goal`, `update_goal`) and never overwrite a different active Codex goal. Ultragoal does not call `/goal clear`; for multiple sequential ultragoal runs in one Codex session/thread, manually clear the completed Codex goal in the UI before creating the next aggregate goal."
|
|
1435
1530
|
: null;
|
|
1436
|
-
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState);
|
|
1531
|
+
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { cwd });
|
|
1437
1532
|
const combinedTransitionMessage = (() => {
|
|
1438
1533
|
if (!skillState?.transition_message)
|
|
1439
1534
|
return null;
|
|
@@ -1942,8 +2037,10 @@ function readPayloadThreadId(payload) {
|
|
|
1942
2037
|
function readPayloadTurnId(payload) {
|
|
1943
2038
|
return safeString(payload.turn_id ?? payload.turnId).trim();
|
|
1944
2039
|
}
|
|
1945
|
-
async function resolveInternalSessionIdForPayload(cwd, payloadSessionId) {
|
|
1946
|
-
const currentSession =
|
|
2040
|
+
async function resolveInternalSessionIdForPayload(cwd, payloadSessionId, stateDir) {
|
|
2041
|
+
const currentSession = stateDir
|
|
2042
|
+
? await readUsableSessionStateFromStateDir(cwd, stateDir)
|
|
2043
|
+
: await readUsableSessionState(cwd);
|
|
1947
2044
|
const canonicalSessionId = safeString(currentSession?.session_id).trim();
|
|
1948
2045
|
if (!canonicalSessionId)
|
|
1949
2046
|
return payloadSessionId;
|
|
@@ -1956,6 +2053,19 @@ async function resolveInternalSessionIdForPayload(cwd, payloadSessionId) {
|
|
|
1956
2053
|
return canonicalSessionId;
|
|
1957
2054
|
return payloadSessionId;
|
|
1958
2055
|
}
|
|
2056
|
+
async function readUsableSessionStateFromStateDir(cwd, stateDir) {
|
|
2057
|
+
const sessionPath = join(stateDir, "session.json");
|
|
2058
|
+
if (!existsSync(sessionPath))
|
|
2059
|
+
return null;
|
|
2060
|
+
try {
|
|
2061
|
+
const content = await readFile(sessionPath, "utf-8");
|
|
2062
|
+
const state = JSON.parse(content);
|
|
2063
|
+
return isSessionStateUsable(state, cwd) ? state : null;
|
|
2064
|
+
}
|
|
2065
|
+
catch {
|
|
2066
|
+
return null;
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
1959
2069
|
async function readStopSessionPinnedState(fileName, cwd, sessionId, stateDir) {
|
|
1960
2070
|
const statePath = stateDir && sessionId
|
|
1961
2071
|
? join(stateDir, "sessions", sessionId, fileName)
|
|
@@ -1968,13 +2078,31 @@ const DEEP_INTERVIEW_ALLOWED_WRITE_PREFIXES = [
|
|
|
1968
2078
|
".omx/specs",
|
|
1969
2079
|
".omx/state",
|
|
1970
2080
|
];
|
|
1971
|
-
const
|
|
2081
|
+
const RALPLAN_ALLOWED_WRITE_PREFIXES = [
|
|
2082
|
+
".omx/context",
|
|
2083
|
+
".omx/plans",
|
|
2084
|
+
".omx/specs",
|
|
2085
|
+
".omx/state",
|
|
2086
|
+
];
|
|
2087
|
+
const PLANNING_MODE_IMPLEMENTATION_TOOL_NAMES = new Set([
|
|
1972
2088
|
"Write",
|
|
1973
2089
|
"Edit",
|
|
1974
2090
|
"MultiEdit",
|
|
2091
|
+
"NotebookEdit",
|
|
1975
2092
|
"apply_patch",
|
|
1976
2093
|
"ApplyPatch",
|
|
1977
2094
|
]);
|
|
2095
|
+
const DEEP_INTERVIEW_IMPLEMENTATION_TOOL_NAMES = PLANNING_MODE_IMPLEMENTATION_TOOL_NAMES;
|
|
2096
|
+
const RALPLAN_EXECUTION_HANDOFF_SKILLS = new Set([
|
|
2097
|
+
// Autopilot is intentionally excluded: it supervises planning phases such as
|
|
2098
|
+
// ralplan/replan and is not by itself an execution authorization.
|
|
2099
|
+
"autoresearch",
|
|
2100
|
+
"ralph",
|
|
2101
|
+
"team",
|
|
2102
|
+
"ultragoal",
|
|
2103
|
+
"ultrawork",
|
|
2104
|
+
"ultraqa",
|
|
2105
|
+
]);
|
|
1978
2106
|
function isActiveDeepInterviewPhase(state) {
|
|
1979
2107
|
if (!state || state.active !== true)
|
|
1980
2108
|
return false;
|
|
@@ -1986,7 +2114,31 @@ function isActiveDeepInterviewPhase(state) {
|
|
|
1986
2114
|
return false;
|
|
1987
2115
|
return true;
|
|
1988
2116
|
}
|
|
1989
|
-
function
|
|
2117
|
+
function isActiveRalplanPhase(state) {
|
|
2118
|
+
if (!state || state.active !== true)
|
|
2119
|
+
return false;
|
|
2120
|
+
const mode = safeString(state.mode).trim();
|
|
2121
|
+
if (mode && mode !== "ralplan")
|
|
2122
|
+
return false;
|
|
2123
|
+
const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
|
|
2124
|
+
if (phase && (TERMINAL_MODE_PHASES.has(phase) || phase === "completing"))
|
|
2125
|
+
return false;
|
|
2126
|
+
return true;
|
|
2127
|
+
}
|
|
2128
|
+
function isActiveAutopilotRalplanPhase(state) {
|
|
2129
|
+
if (!state || state.active !== true)
|
|
2130
|
+
return false;
|
|
2131
|
+
const mode = safeString(state.mode).trim();
|
|
2132
|
+
if (mode && mode !== "autopilot")
|
|
2133
|
+
return false;
|
|
2134
|
+
const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
|
|
2135
|
+
return phase === "ralplan" || phase === "replan" || phase === "autopilot:replan";
|
|
2136
|
+
}
|
|
2137
|
+
function hasExplicitExecutionHandoffSkill(state, sessionId, threadId) {
|
|
2138
|
+
return listActiveSkills(state ?? {}).some((entry) => (RALPLAN_EXECUTION_HANDOFF_SKILLS.has(entry.skill)
|
|
2139
|
+
&& matchesSkillStopContext(entry, state ?? {}, sessionId, threadId)));
|
|
2140
|
+
}
|
|
2141
|
+
function isAllowedPlanningArtifactPath(cwd, rawPath, allowedPrefixes) {
|
|
1990
2142
|
const trimmed = rawPath.trim().replace(/^['"]|['"]$/g, "");
|
|
1991
2143
|
if (!trimmed || trimmed.includes("\0"))
|
|
1992
2144
|
return false;
|
|
@@ -2000,7 +2152,13 @@ function isAllowedDeepInterviewArtifactPath(cwd, rawPath) {
|
|
|
2000
2152
|
}
|
|
2001
2153
|
if (!relativePath || relativePath.startsWith("..") || relativePath.startsWith("/"))
|
|
2002
2154
|
return false;
|
|
2003
|
-
return
|
|
2155
|
+
return allowedPrefixes.some((prefix) => (relativePath === prefix || relativePath.startsWith(`${prefix}/`)));
|
|
2156
|
+
}
|
|
2157
|
+
function isAllowedDeepInterviewArtifactPath(cwd, rawPath) {
|
|
2158
|
+
return isAllowedPlanningArtifactPath(cwd, rawPath, DEEP_INTERVIEW_ALLOWED_WRITE_PREFIXES);
|
|
2159
|
+
}
|
|
2160
|
+
function isAllowedRalplanArtifactPath(cwd, rawPath) {
|
|
2161
|
+
return isAllowedPlanningArtifactPath(cwd, rawPath, RALPLAN_ALLOWED_WRITE_PREFIXES);
|
|
2004
2162
|
}
|
|
2005
2163
|
function readPreToolUseCommand(payload) {
|
|
2006
2164
|
const toolInput = safeObject(payload.tool_input);
|
|
@@ -2059,13 +2217,91 @@ async function readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionI
|
|
|
2059
2217
|
? await readVisibleSkillActiveStateForStateDir(stateDir, sessionId)
|
|
2060
2218
|
: await readSkillActiveState(join(stateDir, SKILL_ACTIVE_STATE_FILE));
|
|
2061
2219
|
if (!canonicalState)
|
|
2062
|
-
return
|
|
2220
|
+
return null;
|
|
2063
2221
|
const hasActiveDeepInterviewSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "deep-interview"
|
|
2064
2222
|
&& matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
|
|
2065
2223
|
return hasActiveDeepInterviewSkill ? modeState : null;
|
|
2066
2224
|
}
|
|
2067
|
-
async function
|
|
2068
|
-
const
|
|
2225
|
+
async function readActiveRalplanStateForPreToolUse(cwd, stateDir, sessionId, threadId) {
|
|
2226
|
+
const modeState = sessionId
|
|
2227
|
+
? await readStopSessionPinnedState("ralplan-state.json", cwd, sessionId, stateDir)
|
|
2228
|
+
: await readJsonIfExists(join(stateDir, "ralplan-state.json"));
|
|
2229
|
+
const canonicalState = sessionId
|
|
2230
|
+
? await readVisibleSkillActiveStateForStateDir(stateDir, sessionId)
|
|
2231
|
+
: await readSkillActiveState(join(stateDir, SKILL_ACTIVE_STATE_FILE));
|
|
2232
|
+
if (isActiveRalplanPhase(modeState) && modeState && modeStateMatchesSkillStopContext(modeState, cwd, sessionId)) {
|
|
2233
|
+
if (hasExplicitExecutionHandoffSkill(canonicalState, sessionId, threadId))
|
|
2234
|
+
return null;
|
|
2235
|
+
if (!canonicalState)
|
|
2236
|
+
return null;
|
|
2237
|
+
const hasActiveRalplanSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "ralplan"
|
|
2238
|
+
&& matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
|
|
2239
|
+
if (hasActiveRalplanSkill)
|
|
2240
|
+
return modeState;
|
|
2241
|
+
}
|
|
2242
|
+
const autopilotState = sessionId
|
|
2243
|
+
? await readStopSessionPinnedState("autopilot-state.json", cwd, sessionId, stateDir)
|
|
2244
|
+
: await readJsonIfExists(join(stateDir, "autopilot-state.json"));
|
|
2245
|
+
if (!isActiveAutopilotRalplanPhase(autopilotState) || !autopilotState)
|
|
2246
|
+
return null;
|
|
2247
|
+
if (!modeStateMatchesSkillStopContext(autopilotState, cwd, sessionId))
|
|
2248
|
+
return null;
|
|
2249
|
+
const terminalAutopilotRunState = await readCanonicalTerminalRunStateForStop(cwd, sessionId, "autopilot");
|
|
2250
|
+
if (terminalAutopilotRunState)
|
|
2251
|
+
return null;
|
|
2252
|
+
if (!canonicalState)
|
|
2253
|
+
return null;
|
|
2254
|
+
const hasActiveAutopilotSkill = listActiveSkills(canonicalState).some((entry) => (entry.skill === "autopilot"
|
|
2255
|
+
&& matchesSkillStopContext(entry, canonicalState, sessionId, threadId)));
|
|
2256
|
+
return hasActiveAutopilotSkill ? autopilotState : null;
|
|
2257
|
+
}
|
|
2258
|
+
function isAllowedRalplanBashWrite(cwd, command) {
|
|
2259
|
+
if (!commandHasDeepInterviewWriteIntent(command))
|
|
2260
|
+
return true;
|
|
2261
|
+
if (/\bomx\s+(?:state\s+(?:write|read|clear)|question)\b/.test(command))
|
|
2262
|
+
return true;
|
|
2263
|
+
const targets = extractDeepInterviewCommandWriteTargets(command);
|
|
2264
|
+
return targets.length > 0 && targets.every((target) => isAllowedRalplanArtifactPath(cwd, target));
|
|
2265
|
+
}
|
|
2266
|
+
async function buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir, resolvedSessionId) {
|
|
2267
|
+
const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim();
|
|
2268
|
+
const threadId = readPayloadThreadId(payload);
|
|
2269
|
+
const activeState = await readActiveRalplanStateForPreToolUse(cwd, stateDir, sessionId, threadId);
|
|
2270
|
+
if (!activeState)
|
|
2271
|
+
return null;
|
|
2272
|
+
const toolName = safeString(payload.tool_name).trim();
|
|
2273
|
+
const command = readPreToolUseCommand(payload);
|
|
2274
|
+
const pathCandidates = readPreToolUsePathCandidates(payload);
|
|
2275
|
+
let blocked = false;
|
|
2276
|
+
if (toolName === "Bash") {
|
|
2277
|
+
blocked = !isAllowedRalplanBashWrite(cwd, command);
|
|
2278
|
+
}
|
|
2279
|
+
else if (PLANNING_MODE_IMPLEMENTATION_TOOL_NAMES.has(toolName)) {
|
|
2280
|
+
blocked = pathCandidates.length === 0
|
|
2281
|
+
|| !pathCandidates.every((candidate) => isAllowedRalplanArtifactPath(cwd, candidate));
|
|
2282
|
+
}
|
|
2283
|
+
if (!blocked)
|
|
2284
|
+
return null;
|
|
2285
|
+
const phase = formatPhase(activeState.current_phase ?? activeState.currentPhase, "planning");
|
|
2286
|
+
const activeMode = safeString(activeState.mode).trim().toLowerCase();
|
|
2287
|
+
const planningModeLabel = activeMode === "autopilot" ? "Autopilot planning" : "Ralplan";
|
|
2288
|
+
const planningModeDescription = activeMode === "autopilot"
|
|
2289
|
+
? "Autopilot is supervising a planning phase"
|
|
2290
|
+
: "Ralplan is consensus-planning mode";
|
|
2291
|
+
return {
|
|
2292
|
+
decision: "block",
|
|
2293
|
+
reason: `${planningModeLabel} is active (phase: ${phase}); implementation/write tools are blocked until an explicit execution handoff workflow is activated.`,
|
|
2294
|
+
hookSpecificOutput: {
|
|
2295
|
+
hookEventName: "PreToolUse",
|
|
2296
|
+
additionalContext: `${planningModeDescription}. `
|
|
2297
|
+
+ "Write only planning artifacts under `.omx/context/`, `.omx/plans/`, `.omx/specs/`, or required `.omx/state/` files. "
|
|
2298
|
+
+ "Do not edit implementation files or run implementation-focused writes from planning phases. "
|
|
2299
|
+
+ `To execute, first process an explicit handoff such as ${formatExecutionHandoffList(cwd)}, which must emit terminal planning state before implementation begins.`,
|
|
2300
|
+
},
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
async function buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir, resolvedSessionId) {
|
|
2304
|
+
const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim();
|
|
2069
2305
|
const threadId = readPayloadThreadId(payload);
|
|
2070
2306
|
const activeState = await readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionId, threadId);
|
|
2071
2307
|
if (!activeState)
|
|
@@ -2089,7 +2325,7 @@ async function buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir
|
|
|
2089
2325
|
reason: `Deep-interview is active (phase: ${phase}); implementation/write tools are blocked until an explicit handoff workflow is activated.`,
|
|
2090
2326
|
hookSpecificOutput: {
|
|
2091
2327
|
hookEventName: "PreToolUse",
|
|
2092
|
-
additionalContext:
|
|
2328
|
+
additionalContext: `Deep-interview is requirements/spec mode. Treat detailed user answers as interview/spec material, not implicit implementation authorization. You may write only deep-interview artifacts under \`.omx/context/\`, \`.omx/interviews/\`, \`.omx/specs/\`, or required \`.omx/state/\` files. To implement, first ask for or process an explicit transition such as \`$ralplan\`, \`$autopilot\`, ${formatExecutionHandoffList(cwd)}.`,
|
|
2093
2329
|
},
|
|
2094
2330
|
};
|
|
2095
2331
|
}
|
|
@@ -2278,7 +2514,7 @@ async function reconcileStaleRootSkillActiveStateForStop(cwd, stateDir, sessionI
|
|
|
2278
2514
|
}
|
|
2279
2515
|
await writeFile(rootPath, JSON.stringify(nextRoot, null, 2));
|
|
2280
2516
|
}
|
|
2281
|
-
function buildRalplanContinuationStatus(blocker, activeSubagentCount) {
|
|
2517
|
+
function buildRalplanContinuationStatus(blocker, activeSubagentCount, cwd) {
|
|
2282
2518
|
const phase = blocker.phase || "planning";
|
|
2283
2519
|
const artifact = blocker.latestPlanPath
|
|
2284
2520
|
? ` Artifact: ${blocker.latestPlanPath}.`
|
|
@@ -2304,12 +2540,12 @@ function buildRalplanContinuationStatus(blocker, activeSubagentCount) {
|
|
|
2304
2540
|
};
|
|
2305
2541
|
}
|
|
2306
2542
|
const completeHint = blocker.planningComplete
|
|
2307
|
-
?
|
|
2543
|
+
? ` The planning artifacts are present; if consensus is approved, emit terminal ralplan complete/approved handoff state and stop planning. Implementation must wait for an explicit ${formatExecutionHandoffList(cwd).replaceAll("`", "")} handoff.`
|
|
2308
2544
|
: "";
|
|
2309
2545
|
return {
|
|
2310
|
-
reason: `Status: continue_from_artifact — ralplan is still active (phase: ${phase}) and has not emitted a terminal complete/paused/waiting status. Continue from the current ralplan artifact, resolve any review ambiguity conservatively or ask the user if needed, and proceed to the next planning/review step before stopping.${artifact}${completeHint}`,
|
|
2546
|
+
reason: `Status: continue_from_artifact — ralplan is still active (phase: ${phase}) and has not emitted a terminal complete/paused/waiting status. Continue from the current ralplan artifact, resolve any review ambiguity conservatively or ask the user if needed, and proceed to the next planning/review step before stopping; do not begin implementation from ralplan.${artifact}${completeHint}`,
|
|
2311
2547
|
stopReasonSuffix: "continue_artifact",
|
|
2312
|
-
systemMessage: `OMX ralplan status: continue_from_artifact at phase ${phase}; continue from the current ralplan artifact and finish by stating whether ralplan is complete, paused for review, waiting for input, or still continuing.`,
|
|
2548
|
+
systemMessage: `OMX ralplan status: continue_from_artifact at phase ${phase}; continue from the current ralplan artifact and finish by stating whether ralplan is complete, paused for review, waiting for input, or still continuing; do not begin implementation from ralplan.`,
|
|
2313
2549
|
};
|
|
2314
2550
|
}
|
|
2315
2551
|
async function readStopAutoNudgePhase(cwd, stateDir, sessionId, threadId) {
|
|
@@ -2463,6 +2699,9 @@ function readIsoTimeMs(value) {
|
|
|
2463
2699
|
return Number.isFinite(parsed) ? parsed : null;
|
|
2464
2700
|
}
|
|
2465
2701
|
async function maybeBuildOrdinaryStopNoProgressOutput(payload, stateDir, canonicalSessionId) {
|
|
2702
|
+
const lastAssistantMessage = safeString(payload.last_assistant_message ?? payload.lastAssistantMessage).trim();
|
|
2703
|
+
if (!lastAssistantMessage)
|
|
2704
|
+
return null;
|
|
2466
2705
|
const statePath = join(stateDir, NATIVE_STOP_STATE_FILE);
|
|
2467
2706
|
const state = await readJsonIfExists(statePath) ?? {};
|
|
2468
2707
|
const sessions = safeObject(state.sessions);
|
|
@@ -2492,9 +2731,6 @@ async function maybeBuildOrdinaryStopNoProgressOutput(payload, stateDir, canonic
|
|
|
2492
2731
|
};
|
|
2493
2732
|
await mkdir(stateDir, { recursive: true });
|
|
2494
2733
|
await writeFile(statePath, JSON.stringify({ ...state, sessions }, null, 2));
|
|
2495
|
-
const stopHookActive = payload.stop_hook_active === true || payload.stopHookActive === true;
|
|
2496
|
-
if (!stopHookActive)
|
|
2497
|
-
return null;
|
|
2498
2734
|
const maxRepeats = parseBoundedPositiveInteger(process.env.OMX_NATIVE_STOP_NO_PROGRESS_MAX_REPEATS, ORDINARY_STOP_NO_PROGRESS_DEFAULT_MAX_REPEATS);
|
|
2499
2735
|
const idleMs = parseBoundedNonNegativeInteger(process.env.OMX_NATIVE_STOP_NO_PROGRESS_IDLE_MS, ORDINARY_STOP_NO_PROGRESS_DEFAULT_IDLE_MS);
|
|
2500
2736
|
const firstSeenMs = readIsoTimeMs(firstSeenAt) ?? Date.now();
|
|
@@ -2624,7 +2860,7 @@ async function buildSkillStopOutput(cwd, stateDir, sessionId, threadId) {
|
|
|
2624
2860
|
const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
|
|
2625
2861
|
const activeSubagentCount = subagentSummary?.activeSubagentThreadIds.length ?? 0;
|
|
2626
2862
|
if (blocker.skill === "ralplan") {
|
|
2627
|
-
const status = buildRalplanContinuationStatus(blocker, activeSubagentCount);
|
|
2863
|
+
const status = buildRalplanContinuationStatus(blocker, activeSubagentCount, cwd);
|
|
2628
2864
|
return {
|
|
2629
2865
|
decision: "block",
|
|
2630
2866
|
reason: status.reason,
|
|
@@ -2878,10 +3114,20 @@ async function buildStopHookOutput(payload, cwd, stateDir, options = {}) {
|
|
|
2878
3114
|
export async function dispatchCodexNativeHook(payload, options = {}) {
|
|
2879
3115
|
const hookEventName = readHookEventName(payload);
|
|
2880
3116
|
const cwd = options.cwd ?? (safeString(payload.cwd).trim() || process.cwd());
|
|
3117
|
+
if (hookEventName === "Stop" && !hasNativeStopRuntimeSurface(cwd)) {
|
|
3118
|
+
return {
|
|
3119
|
+
hookEventName,
|
|
3120
|
+
omxEventName: mapCodexHookEventToOmxEvent(hookEventName),
|
|
3121
|
+
skillState: null,
|
|
3122
|
+
outputJson: null,
|
|
3123
|
+
};
|
|
3124
|
+
}
|
|
2881
3125
|
// Native hooks must use the same authoritative runtime state root as HUD/MCP
|
|
2882
3126
|
// when boxed/team roots are active; do not bypass it with cwd/.omx/state.
|
|
2883
3127
|
const stateDir = getBaseStateDir(cwd);
|
|
2884
|
-
|
|
3128
|
+
if (hookEventName !== "Stop") {
|
|
3129
|
+
await mkdir(stateDir, { recursive: true });
|
|
3130
|
+
}
|
|
2885
3131
|
const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
|
|
2886
3132
|
let skillState = null;
|
|
2887
3133
|
let triageAdditionalContext = null;
|
|
@@ -2938,14 +3184,16 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
|
|
|
2938
3184
|
const sessionIdForState = canonicalSessionId || nativeSessionId;
|
|
2939
3185
|
let outputJson = null;
|
|
2940
3186
|
const isSubagentPromptSubmit = hookEventName === "UserPromptSubmit"
|
|
2941
|
-
? await isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, threadId)
|
|
3187
|
+
? await isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, threadId, safeString(currentSessionState?.native_session_id).trim())
|
|
2942
3188
|
: false;
|
|
2943
3189
|
const isSubagentStop = hookEventName === "Stop"
|
|
2944
3190
|
? (await Promise.all([...new Set([
|
|
2945
3191
|
canonicalSessionId,
|
|
2946
3192
|
safeString(currentSessionState?.session_id).trim(),
|
|
2947
3193
|
].filter(Boolean))]
|
|
2948
|
-
.map((candidateSessionId) => isNativeSubagentHook(cwd, candidateSessionId, nativeSessionId, threadId)
|
|
3194
|
+
.map((candidateSessionId) => isNativeSubagentHook(cwd, candidateSessionId, nativeSessionId, threadId, candidateSessionId === safeString(currentSessionState?.session_id).trim()
|
|
3195
|
+
? safeString(currentSessionState?.native_session_id).trim()
|
|
3196
|
+
: "")))).some(Boolean)
|
|
2949
3197
|
: false;
|
|
2950
3198
|
const suppressNoisySubagentLifecycleDispatch = (isSubagentSessionStart || isSubagentStop)
|
|
2951
3199
|
&& shouldSuppressSubagentLifecycleHookDispatch();
|
|
@@ -3040,11 +3288,14 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
|
|
|
3040
3288
|
triageAdditionalContext = null;
|
|
3041
3289
|
}
|
|
3042
3290
|
}
|
|
3291
|
+
const skipHudReconcileForDoctorSmoke = process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE === "1";
|
|
3043
3292
|
const skipHudReconcileForTeamWorkerPane = !isSubagentPromptSubmit
|
|
3044
3293
|
&& await isConfirmedTeamWorkerPromptSubmitPane(cwd).catch(() => false);
|
|
3045
|
-
if (!skipHudReconcileForTeamWorkerPane) {
|
|
3294
|
+
if (!skipHudReconcileForDoctorSmoke && !skipHudReconcileForTeamWorkerPane) {
|
|
3046
3295
|
const reconcileHudForPromptSubmitFn = options.reconcileHudForPromptSubmitFn ?? reconcileHudForPromptSubmit;
|
|
3047
|
-
|
|
3296
|
+
const hudSessionId = resolveHudReconcileSessionId(currentSessionState, canonicalSessionId, sessionIdForState);
|
|
3297
|
+
const hudSessionIds = resolveHudReconcileSessionIds(currentSessionState, canonicalSessionId, sessionIdForState, nativeSessionId);
|
|
3298
|
+
await reconcileHudForPromptSubmitFn(cwd, { sessionId: hudSessionId, sessionIds: hudSessionIds }).catch(() => { });
|
|
3048
3299
|
}
|
|
3049
3300
|
}
|
|
3050
3301
|
if (omxEventName && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
|
|
@@ -3100,7 +3351,12 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
|
|
|
3100
3351
|
}
|
|
3101
3352
|
}
|
|
3102
3353
|
else if (hookEventName === "PreToolUse") {
|
|
3103
|
-
|
|
3354
|
+
const payloadSessionId = readPayloadSessionId(payload);
|
|
3355
|
+
const preToolUseSessionId = payloadSessionId
|
|
3356
|
+
? await resolveInternalSessionIdForPayload(cwd, payloadSessionId, stateDir)
|
|
3357
|
+
: "";
|
|
3358
|
+
outputJson = await buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
|
|
3359
|
+
?? await buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
|
|
3104
3360
|
?? buildNativePreToolUseOutput(payload);
|
|
3105
3361
|
}
|
|
3106
3362
|
else if (hookEventName === "PostToolUse") {
|
|
@@ -3122,6 +3378,28 @@ export async function dispatchCodexNativeHook(payload, options = {}) {
|
|
|
3122
3378
|
outputJson,
|
|
3123
3379
|
};
|
|
3124
3380
|
}
|
|
3381
|
+
function hasNativeStopRuntimeSurface(cwd) {
|
|
3382
|
+
if (existsSync(join(cwd, ".omx")))
|
|
3383
|
+
return true;
|
|
3384
|
+
if (findGitLayout(cwd))
|
|
3385
|
+
return true;
|
|
3386
|
+
const omxRoot = safeString(process.env.OMX_ROOT).trim();
|
|
3387
|
+
if (omxRoot && existsSync(join(omxRoot, ".omx")))
|
|
3388
|
+
return true;
|
|
3389
|
+
const stateRoot = safeString(process.env.OMX_STATE_ROOT).trim();
|
|
3390
|
+
if (stateRoot && existsSync(stateRoot))
|
|
3391
|
+
return true;
|
|
3392
|
+
return [
|
|
3393
|
+
process.env.OMX_SESSION_ID,
|
|
3394
|
+
process.env.OMX_TEAM_INTERNAL_WORKER,
|
|
3395
|
+
process.env.OMX_TEAM_WORKER,
|
|
3396
|
+
process.env.OMX_TEAM_STATE_ROOT,
|
|
3397
|
+
process.env.OMX_TEAM_LEADER_CWD,
|
|
3398
|
+
process.env.OMX_NOTIFY_HOOK_TRUSTED_MANAGED_CWD,
|
|
3399
|
+
process.env.OMX_TMUX_HUD_OWNER,
|
|
3400
|
+
process.env.OMX_TMUX_HUD_LEADER_PANE,
|
|
3401
|
+
].some((value) => safeString(value).trim() !== "");
|
|
3402
|
+
}
|
|
3125
3403
|
export function isCodexNativeHookMainModule(moduleUrl, argv1) {
|
|
3126
3404
|
if (!argv1)
|
|
3127
3405
|
return false;
|
|
@@ -3129,30 +3407,129 @@ export function isCodexNativeHookMainModule(moduleUrl, argv1) {
|
|
|
3129
3407
|
}
|
|
3130
3408
|
async function readStdinJson() {
|
|
3131
3409
|
const chunks = [];
|
|
3410
|
+
let totalBytes = 0;
|
|
3411
|
+
let oversized = false;
|
|
3132
3412
|
for await (const chunk of process.stdin) {
|
|
3133
|
-
|
|
3413
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
|
|
3414
|
+
totalBytes += buffer.byteLength;
|
|
3415
|
+
if (totalBytes > MAX_NATIVE_STDIN_JSON_BYTES) {
|
|
3416
|
+
const remaining = Math.max(0, MAX_NATIVE_STDIN_JSON_BYTES - (totalBytes - buffer.byteLength));
|
|
3417
|
+
if (remaining > 0)
|
|
3418
|
+
chunks.push(Buffer.from(buffer.subarray(0, remaining)));
|
|
3419
|
+
oversized = true;
|
|
3420
|
+
process.stdin.destroy();
|
|
3421
|
+
break;
|
|
3422
|
+
}
|
|
3423
|
+
chunks.push(buffer);
|
|
3134
3424
|
}
|
|
3135
3425
|
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
3426
|
+
const rawHookEventName = extractRawCodexHookEventName(raw);
|
|
3427
|
+
if (oversized) {
|
|
3428
|
+
return {
|
|
3429
|
+
payload: {},
|
|
3430
|
+
parseError: null,
|
|
3431
|
+
rawInput: raw,
|
|
3432
|
+
oversized: true,
|
|
3433
|
+
rawHookEventName,
|
|
3434
|
+
};
|
|
3435
|
+
}
|
|
3136
3436
|
if (!raw) {
|
|
3137
|
-
return { payload: {}, parseError: null };
|
|
3437
|
+
return { payload: {}, parseError: null, rawInput: raw, oversized: false, rawHookEventName };
|
|
3138
3438
|
}
|
|
3139
3439
|
try {
|
|
3140
3440
|
return {
|
|
3141
3441
|
payload: safeObject(JSON.parse(raw)),
|
|
3142
3442
|
parseError: null,
|
|
3443
|
+
rawInput: raw,
|
|
3444
|
+
oversized: false,
|
|
3445
|
+
rawHookEventName,
|
|
3143
3446
|
};
|
|
3144
3447
|
}
|
|
3145
3448
|
catch (error) {
|
|
3146
3449
|
return {
|
|
3147
3450
|
payload: {},
|
|
3148
3451
|
parseError: error instanceof Error ? error : new Error(String(error)),
|
|
3452
|
+
rawInput: raw,
|
|
3453
|
+
oversized: false,
|
|
3454
|
+
rawHookEventName,
|
|
3149
3455
|
};
|
|
3150
3456
|
}
|
|
3151
3457
|
}
|
|
3458
|
+
function inferHookEventNameFromMalformedInput(raw) {
|
|
3459
|
+
const match = raw.match(/(?:\"|['"])?hook[_-]?event[_-]?name(?:\"|['"])?\s*:\s*(?:\"|['"])?(SessionStart|PreToolUse|PostToolUse|UserPromptSubmit|PreCompact|PostCompact|Stop)\b/i);
|
|
3460
|
+
const value = match?.[1];
|
|
3461
|
+
if (!value)
|
|
3462
|
+
return null;
|
|
3463
|
+
return readHookEventName({ hook_event_name: value });
|
|
3464
|
+
}
|
|
3465
|
+
function buildMalformedStdinHookOutput(parseError, rawInput) {
|
|
3466
|
+
const reason = "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.";
|
|
3467
|
+
const systemMessage = `${reason} stdin JSON parsing failed inside codex-native-hook: ${parseError.message}.`;
|
|
3468
|
+
if (inferHookEventNameFromMalformedInput(rawInput) === "Stop") {
|
|
3469
|
+
return {
|
|
3470
|
+
decision: "block",
|
|
3471
|
+
reason,
|
|
3472
|
+
stopReason: "native_hook_stdin_parse_error",
|
|
3473
|
+
systemMessage,
|
|
3474
|
+
};
|
|
3475
|
+
}
|
|
3476
|
+
return {
|
|
3477
|
+
continue: false,
|
|
3478
|
+
stopReason: "native_hook_stdin_parse_error",
|
|
3479
|
+
systemMessage,
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3482
|
+
async function buildOversizedStopActiveWorkflowOutput(cwd) {
|
|
3483
|
+
const currentSession = await readUsableSessionState(cwd);
|
|
3484
|
+
const currentSessionId = safeString(currentSession?.session_id).trim()
|
|
3485
|
+
|| safeString(process.env.OMX_SESSION_ID || process.env.CODEX_SESSION_ID).trim();
|
|
3486
|
+
if (!currentSessionId)
|
|
3487
|
+
return null;
|
|
3488
|
+
if (await readCanonicalTerminalRunStateForStop(cwd, currentSessionId, "autopilot"))
|
|
3489
|
+
return null;
|
|
3490
|
+
const autopilotState = await readModeStateForActiveDecision("autopilot", currentSessionId, cwd);
|
|
3491
|
+
if (!autopilotState || !shouldContinueRun(autopilotState))
|
|
3492
|
+
return null;
|
|
3493
|
+
const phase = formatPhase(autopilotState.current_phase);
|
|
3494
|
+
const reason = `OMX native Stop received oversized stdin before parsing while the current session has active OMX autopilot state (phase: ${phase}); continue once with a compact response or reduce hook payload size so normal Stop gates can run.`;
|
|
3495
|
+
return {
|
|
3496
|
+
decision: "block",
|
|
3497
|
+
reason,
|
|
3498
|
+
stopReason: "native_stop_stdin_oversized_active_workflow",
|
|
3499
|
+
systemMessage: "OMX native Stop rejected oversized stdin before parsing; active current-session workflow state is present, so Stop is blocked instead of silently allowing termination.",
|
|
3500
|
+
};
|
|
3501
|
+
}
|
|
3502
|
+
async function buildOversizedStdinHookOutput(rawHookEventName, cwd) {
|
|
3503
|
+
if (rawHookEventName === "Stop") {
|
|
3504
|
+
return await buildOversizedStopActiveWorkflowOutput(cwd) ?? {};
|
|
3505
|
+
}
|
|
3506
|
+
const systemMessage = `OMX native hook rejected oversized stdin JSON before parsing; maxBytes=${MAX_NATIVE_STDIN_JSON_BYTES}.`;
|
|
3507
|
+
return {
|
|
3508
|
+
continue: false,
|
|
3509
|
+
stopReason: "native_hook_stdin_oversized",
|
|
3510
|
+
systemMessage,
|
|
3511
|
+
};
|
|
3512
|
+
}
|
|
3152
3513
|
function writeNativeHookJsonStdout(output) {
|
|
3153
3514
|
process.stdout.write(`${JSON.stringify(output)}\n`);
|
|
3154
3515
|
}
|
|
3155
|
-
|
|
3516
|
+
function redactMalformedHookPreview(rawInput) {
|
|
3517
|
+
const withoutControls = rawInput.replace(/[\u0000-\u001f\u007f-\u009f]/g, "");
|
|
3518
|
+
const withoutAuthSecrets = redactAuthSecrets(withoutControls);
|
|
3519
|
+
return withoutAuthSecrets
|
|
3520
|
+
.replace(/(["']?(?:prompt|user_prompt|input|text)["']?\s*:\s*)(["'])(?:\\.|(?!\2)[^\\])*\2/gi, "$1$2[REDACTED]$2")
|
|
3521
|
+
.replace(/(["']?(?:prompt|user_prompt|input|text)["']?\s*:\s*)(["'])(?:\\.|[^\\])*$/gi, "$1$2[REDACTED]$2")
|
|
3522
|
+
.replace(/(["']?(?:prompt|user_prompt|input|text)["']?\s*:\s*)(?!["'])[^,}]*/gi, "$1[REDACTED]");
|
|
3523
|
+
}
|
|
3524
|
+
function buildRawInputLogFields(rawInput) {
|
|
3525
|
+
if (!rawInput)
|
|
3526
|
+
return {};
|
|
3527
|
+
return {
|
|
3528
|
+
raw_input_length: Buffer.byteLength(rawInput, "utf-8"),
|
|
3529
|
+
raw_input_prefix: redactMalformedHookPreview(rawInput).slice(0, 240),
|
|
3530
|
+
};
|
|
3531
|
+
}
|
|
3532
|
+
async function logNativeHookCliError(cwd, type, error, payload = {}, details = {}) {
|
|
3156
3533
|
const logsDir = join(cwd || process.cwd(), ".omx", "logs");
|
|
3157
3534
|
await mkdir(logsDir, { recursive: true }).catch(() => { });
|
|
3158
3535
|
const logPath = join(logsDir, `native-hook-${new Date().toISOString().split("T")[0]}.jsonl`);
|
|
@@ -3164,6 +3541,7 @@ async function logNativeHookCliError(cwd, type, error, payload = {}) {
|
|
|
3164
3541
|
thread_id: readPayloadThreadId(payload) || undefined,
|
|
3165
3542
|
turn_id: readPayloadTurnId(payload) || undefined,
|
|
3166
3543
|
error: error instanceof Error ? error.message : String(error),
|
|
3544
|
+
...details,
|
|
3167
3545
|
}) + "\n").catch(() => { });
|
|
3168
3546
|
}
|
|
3169
3547
|
function isStopDispatchFailureTestTrigger(payload) {
|
|
@@ -3186,17 +3564,14 @@ function buildStopDispatchFailureOutput(error) {
|
|
|
3186
3564
|
};
|
|
3187
3565
|
}
|
|
3188
3566
|
export async function runCodexNativeHookCli() {
|
|
3189
|
-
const { payload, parseError } = await readStdinJson();
|
|
3567
|
+
const { payload, parseError, rawInput, oversized, rawHookEventName } = await readStdinJson();
|
|
3568
|
+
if (oversized) {
|
|
3569
|
+
writeNativeHookJsonStdout(await buildOversizedStdinHookOutput(rawHookEventName, process.cwd()));
|
|
3570
|
+
return;
|
|
3571
|
+
}
|
|
3190
3572
|
if (parseError) {
|
|
3191
|
-
await logNativeHookCliError(process.cwd(), "native_hook_stdin_parse_error", parseError);
|
|
3192
|
-
writeNativeHookJsonStdout(
|
|
3193
|
-
decision: "block",
|
|
3194
|
-
reason: "OMX native hook received malformed JSON input. Preserve runtime state, inspect the emitting hook payload yourself, and retry with valid JSON.",
|
|
3195
|
-
hookSpecificOutput: {
|
|
3196
|
-
hookEventName: "Unknown",
|
|
3197
|
-
additionalContext: `stdin JSON parsing failed inside codex-native-hook: ${parseError.message}. Emit valid JSON from the native hook caller before retrying.`,
|
|
3198
|
-
},
|
|
3199
|
-
});
|
|
3573
|
+
await logNativeHookCliError(process.cwd(), "native_hook_stdin_parse_error", parseError, {}, buildRawInputLogFields(rawInput));
|
|
3574
|
+
writeNativeHookJsonStdout(buildMalformedStdinHookOutput(parseError, rawInput));
|
|
3200
3575
|
return;
|
|
3201
3576
|
}
|
|
3202
3577
|
try {
|