oh-my-codex 0.15.3 → 0.16.0
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 +10 -7
- package/Cargo.toml +1 -1
- package/README.md +3 -0
- package/crates/omx-explore/Cargo.toml +3 -0
- package/crates/omx-explore/src/main.rs +517 -16
- package/dist/autoresearch/goal.d.ts +90 -0
- package/dist/autoresearch/goal.d.ts.map +1 -0
- package/dist/autoresearch/goal.js +237 -0
- package/dist/autoresearch/goal.js.map +1 -0
- package/dist/autoresearch/skill-validation.d.ts +1 -0
- package/dist/autoresearch/skill-validation.d.ts.map +1 -1
- package/dist/autoresearch/skill-validation.js +10 -3
- package/dist/autoresearch/skill-validation.js.map +1 -1
- package/dist/catalog/__tests__/generator.test.js +9 -4
- package/dist/catalog/__tests__/generator.test.js.map +1 -1
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.js +20 -1
- package/dist/catalog/__tests__/plugin-bundle-ssot.test.js.map +1 -1
- package/dist/catalog/__tests__/schema.test.js +14 -3
- package/dist/catalog/__tests__/schema.test.js.map +1 -1
- package/dist/catalog/schema.js +1 -1
- package/dist/catalog/schema.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-goal.test.d.ts +2 -0
- package/dist/cli/__tests__/autoresearch-goal.test.d.ts.map +1 -0
- package/dist/cli/__tests__/autoresearch-goal.test.js +194 -0
- package/dist/cli/__tests__/autoresearch-goal.test.js.map +1 -0
- package/dist/cli/__tests__/cleanup.test.js +82 -1
- package/dist/cli/__tests__/cleanup.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +7 -4
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +23 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +8 -1
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +82 -3
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +58 -0
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/native-assets.test.js +26 -1
- package/dist/cli/__tests__/native-assets.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +2 -2
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/performance-goal.test.d.ts +2 -0
- package/dist/cli/__tests__/performance-goal.test.d.ts.map +1 -0
- package/dist/cli/__tests__/performance-goal.test.js +144 -0
- package/dist/cli/__tests__/performance-goal.test.js.map +1 -0
- package/dist/cli/__tests__/question.test.js +8 -0
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.d.ts +2 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.d.ts.map +1 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +31 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -0
- package/dist/cli/__tests__/ralph-prd-deep-interview.test.js +5 -4
- package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-prd-smoke.test.js +7 -0
- package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +57 -21
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +27 -8
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +18 -9
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skill-validation.test.js +11 -11
- package/dist/cli/__tests__/setup-skill-validation.test.js.map +1 -1
- package/dist/cli/__tests__/setup-skills-overwrite.test.js +12 -12
- package/dist/cli/__tests__/setup-skills-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +187 -0
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.d.ts +2 -0
- package/dist/cli/__tests__/ultragoal.test.d.ts.map +1 -0
- package/dist/cli/__tests__/ultragoal.test.js +106 -0
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -0
- package/dist/cli/__tests__/uninstall.test.js +11 -0
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/autoresearch-goal.d.ts +3 -0
- package/dist/cli/autoresearch-goal.d.ts.map +1 -0
- package/dist/cli/autoresearch-goal.js +175 -0
- package/dist/cli/autoresearch-goal.js.map +1 -0
- package/dist/cli/cleanup.d.ts +3 -1
- package/dist/cli/cleanup.d.ts.map +1 -1
- package/dist/cli/cleanup.js +42 -2
- package/dist/cli/cleanup.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +49 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +10 -2
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +6 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +145 -18
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/native-assets.js +1 -1
- package/dist/cli/native-assets.js.map +1 -1
- package/dist/cli/performance-goal.d.ts +3 -0
- package/dist/cli/performance-goal.d.ts.map +1 -0
- package/dist/cli/performance-goal.js +186 -0
- package/dist/cli/performance-goal.js.map +1 -0
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +8 -0
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +13 -6
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/team.d.ts +2 -0
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +72 -17
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/tmux-hook.d.ts.map +1 -1
- package/dist/cli/tmux-hook.js +2 -1
- package/dist/cli/tmux-hook.js.map +1 -1
- package/dist/cli/ultragoal.d.ts +3 -0
- package/dist/cli/ultragoal.d.ts.map +1 -0
- package/dist/cli/ultragoal.js +191 -0
- package/dist/cli/ultragoal.js.map +1 -0
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +4 -2
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +12 -1
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +5 -0
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/commit-lore-guard.d.ts +3 -0
- package/dist/config/commit-lore-guard.d.ts.map +1 -0
- package/dist/config/commit-lore-guard.js +9 -0
- package/dist/config/commit-lore-guard.js.map +1 -0
- package/dist/config/generator.d.ts +3 -2
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +52 -8
- package/dist/config/generator.js.map +1 -1
- package/dist/config/omx-first-party-mcp.d.ts +1 -0
- package/dist/config/omx-first-party-mcp.d.ts.map +1 -1
- package/dist/config/omx-first-party-mcp.js +4 -1
- package/dist/config/omx-first-party-mcp.js.map +1 -1
- package/dist/goal-workflows/__tests__/artifacts.test.d.ts +2 -0
- package/dist/goal-workflows/__tests__/artifacts.test.d.ts.map +1 -0
- package/dist/goal-workflows/__tests__/artifacts.test.js +96 -0
- package/dist/goal-workflows/__tests__/artifacts.test.js.map +1 -0
- package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.d.ts +2 -0
- package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.d.ts.map +1 -0
- package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +54 -0
- package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -0
- package/dist/goal-workflows/artifacts.d.ts +62 -0
- package/dist/goal-workflows/artifacts.d.ts.map +1 -0
- package/dist/goal-workflows/artifacts.js +132 -0
- package/dist/goal-workflows/artifacts.js.map +1 -0
- package/dist/goal-workflows/codex-goal-snapshot.d.ts +28 -0
- package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -0
- package/dist/goal-workflows/codex-goal-snapshot.js +110 -0
- package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -0
- package/dist/goal-workflows/handoff.d.ts +10 -0
- package/dist/goal-workflows/handoff.d.ts.map +1 -0
- package/dist/goal-workflows/handoff.js +31 -0
- package/dist/goal-workflows/handoff.js.map +1 -0
- package/dist/goal-workflows/validation.d.ts +13 -0
- package/dist/goal-workflows/validation.d.ts.map +1 -0
- package/dist/goal-workflows/validation.js +36 -0
- package/dist/goal-workflows/validation.js.map +1 -0
- package/dist/hooks/__tests__/anti-slop-workflow.test.js +3 -3
- package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +45 -32
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +3 -3
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +2 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +17 -24
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +3 -3
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
- package/dist/hooks/__tests__/task-size-detector.test.js +1 -1
- package/dist/hooks/__tests__/task-size-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/visual-ralph-skill.test.js +3 -3
- package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
- package/dist/hooks/__tests__/visual-verdict-loop.test.js +7 -11
- package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +2 -2
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +12 -13
- 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 +2 -10
- 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 +0 -4
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/session.js +2 -2
- package/dist/hooks/session.js.map +1 -1
- package/dist/hooks/task-size-detector.d.ts.map +1 -1
- package/dist/hooks/task-size-detector.js +1 -0
- package/dist/hooks/task-size-detector.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +29 -7
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/reconcile.d.ts +2 -1
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +12 -0
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +15 -2
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +54 -0
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +36 -0
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/bootstrap.d.ts +1 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +9 -7
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +17 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +36 -2
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/modes/__tests__/base-session-scope.test.js +26 -0
- package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
- package/dist/modes/base.d.ts +1 -0
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +35 -5
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/__tests__/http-client.test.d.ts +2 -0
- package/dist/notifications/__tests__/http-client.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/http-client.test.js +90 -0
- package/dist/notifications/__tests__/http-client.test.js.map +1 -0
- package/dist/notifications/__tests__/notifier.test.js +22 -60
- package/dist/notifications/__tests__/notifier.test.js.map +1 -1
- package/dist/notifications/dispatcher.d.ts.map +1 -1
- package/dist/notifications/dispatcher.js +35 -60
- package/dist/notifications/dispatcher.js.map +1 -1
- package/dist/notifications/http-client.d.ts +22 -0
- package/dist/notifications/http-client.d.ts.map +1 -0
- package/dist/notifications/http-client.js +298 -0
- package/dist/notifications/http-client.js.map +1 -0
- package/dist/notifications/notifier.d.ts +3 -2
- package/dist/notifications/notifier.d.ts.map +1 -1
- package/dist/notifications/notifier.js +17 -22
- package/dist/notifications/notifier.js.map +1 -1
- package/dist/openclaw/__tests__/dispatcher.test.js +62 -1
- package/dist/openclaw/__tests__/dispatcher.test.js.map +1 -1
- package/dist/openclaw/dispatcher.d.ts.map +1 -1
- package/dist/openclaw/dispatcher.js +3 -2
- package/dist/openclaw/dispatcher.js.map +1 -1
- package/dist/performance-goal/artifacts.d.ts +76 -0
- package/dist/performance-goal/artifacts.d.ts.map +1 -0
- package/dist/performance-goal/artifacts.js +221 -0
- package/dist/performance-goal/artifacts.js.map +1 -0
- package/dist/pipeline/__tests__/stages.test.js +30 -5
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/stages/team-exec.d.ts.map +1 -1
- package/dist/pipeline/stages/team-exec.js +2 -19
- package/dist/pipeline/stages/team-exec.js.map +1 -1
- package/dist/planning/__tests__/artifacts.test.js +16 -1
- package/dist/planning/__tests__/artifacts.test.js.map +1 -1
- package/dist/planning/artifacts.d.ts +1 -0
- package/dist/planning/artifacts.d.ts.map +1 -1
- package/dist/planning/artifacts.js +9 -12
- package/dist/planning/artifacts.js.map +1 -1
- package/dist/ralplan/__tests__/runtime.test.js +2 -0
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +6 -0
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +1516 -205
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/hook-derived-watcher.test.js +33 -1
- package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
- package/dist/scripts/__tests__/run-test-files.test.js +36 -0
- package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +497 -51
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts +7 -0
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +222 -19
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/hook-derived-watcher.js +2 -1
- package/dist/scripts/hook-derived-watcher.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +2 -1
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/orchestration-intent.d.ts +1 -2
- package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
- package/dist/scripts/notify-hook/orchestration-intent.js +2 -3
- package/dist/scripts/notify-hook/orchestration-intent.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts +0 -2
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +8 -60
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.d.ts +15 -0
- package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -0
- package/dist/scripts/notify-hook/team-worker-stop.js +224 -0
- package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -0
- package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +26 -18
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/run-test-files.js +17 -1
- package/dist/scripts/run-test-files.js.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +2 -2
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +26 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +35 -0
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/operations.d.ts +3 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +8 -4
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts +1 -0
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +54 -13
- package/dist/state/skill-active.js.map +1 -1
- package/dist/team/__tests__/api-interop.test.js +59 -0
- package/dist/team/__tests__/api-interop.test.js.map +1 -1
- package/dist/team/__tests__/approved-execution.test.d.ts +2 -0
- package/dist/team/__tests__/approved-execution.test.d.ts.map +1 -0
- package/dist/team/__tests__/approved-execution.test.js +124 -0
- package/dist/team/__tests__/approved-execution.test.js.map +1 -0
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +2 -4
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
- package/dist/team/__tests__/delivery-log.test.d.ts +2 -0
- package/dist/team/__tests__/delivery-log.test.d.ts.map +1 -0
- package/dist/team/__tests__/delivery-log.test.js +44 -0
- package/dist/team/__tests__/delivery-log.test.js.map +1 -0
- package/dist/team/__tests__/role-router.test.js +4 -4
- package/dist/team/__tests__/role-router.test.js.map +1 -1
- package/dist/team/__tests__/runtime-boxed-state.test.d.ts +2 -0
- package/dist/team/__tests__/runtime-boxed-state.test.d.ts.map +1 -0
- package/dist/team/__tests__/runtime-boxed-state.test.js +39 -0
- package/dist/team/__tests__/runtime-boxed-state.test.js.map +1 -0
- package/dist/team/__tests__/runtime.test.js +118 -6
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/state-root.test.js +13 -0
- package/dist/team/__tests__/state-root.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +3 -0
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +50 -0
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +4 -3
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/approved-execution.d.ts +37 -0
- package/dist/team/approved-execution.d.ts.map +1 -0
- package/dist/team/approved-execution.js +136 -0
- package/dist/team/approved-execution.js.map +1 -0
- package/dist/team/delivery-log.d.ts.map +1 -1
- package/dist/team/delivery-log.js +2 -1
- package/dist/team/delivery-log.js.map +1 -1
- package/dist/team/followup-planner.js +2 -2
- package/dist/team/followup-planner.js.map +1 -1
- package/dist/team/goal-workflow.d.ts +20 -0
- package/dist/team/goal-workflow.d.ts.map +1 -0
- package/dist/team/goal-workflow.js +57 -0
- package/dist/team/goal-workflow.js.map +1 -0
- package/dist/team/orchestrator.js +2 -2
- package/dist/team/orchestrator.js.map +1 -1
- package/dist/team/role-router.js +5 -5
- package/dist/team/role-router.js.map +1 -1
- package/dist/team/runtime.d.ts +6 -0
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +46 -6
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +2 -0
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +4 -2
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +2 -0
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +19 -2
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.d.ts +2 -0
- package/dist/ultragoal/__tests__/artifacts.test.d.ts.map +1 -0
- package/dist/ultragoal/__tests__/artifacts.test.js +93 -0
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -0
- package/dist/ultragoal/artifacts.d.ts +89 -0
- package/dist/ultragoal/artifacts.d.ts.map +1 -0
- package/dist/ultragoal/artifacts.js +233 -0
- package/dist/ultragoal/artifacts.js.map +1 -0
- package/dist/utils/__tests__/agents-model-table.test.js +3 -1
- package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +31 -1
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/agents-model-table.d.ts.map +1 -1
- package/dist/utils/agents-model-table.js +12 -1
- package/dist/utils/agents-model-table.js.map +1 -1
- package/dist/utils/paths.d.ts +2 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +23 -7
- package/dist/utils/paths.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +30 -19
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/package.json +5 -5
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/skills/ask/SKILL.md +58 -0
- package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +36 -0
- package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +2 -2
- package/plugins/oh-my-codex/skills/performance-goal/SKILL.md +65 -0
- package/plugins/oh-my-codex/skills/plan/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -3
- package/plugins/oh-my-codex/skills/team/SKILL.md +6 -2
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +49 -0
- package/plugins/oh-my-codex/skills/visual-ralph/SKILL.md +9 -9
- package/prompts/api-reviewer.md +1 -1
- package/prompts/code-reviewer.md +2 -0
- package/prompts/performance-reviewer.md +1 -1
- package/prompts/quality-reviewer.md +1 -1
- package/prompts/quality-strategist.md +2 -2
- package/prompts/style-reviewer.md +1 -1
- package/prompts/test-engineer.md +1 -1
- package/skills/ask/SKILL.md +58 -0
- package/skills/ask-claude/SKILL.md +3 -54
- package/skills/ask-gemini/SKILL.md +3 -54
- package/skills/autoresearch-goal/SKILL.md +36 -0
- package/skills/build-fix/SKILL.md +4 -139
- package/skills/deepsearch/SKILL.md +4 -32
- package/skills/ecomode/SKILL.md +4 -108
- package/skills/help/SKILL.md +4 -196
- package/skills/note/SKILL.md +4 -56
- package/skills/omx-setup/SKILL.md +2 -2
- package/skills/performance-goal/SKILL.md +65 -0
- package/skills/plan/SKILL.md +1 -1
- package/skills/ralph/SKILL.md +22 -3
- package/skills/ralph-init/SKILL.md +4 -40
- package/skills/review/SKILL.md +4 -32
- package/skills/security-review/SKILL.md +4 -294
- package/skills/swarm/SKILL.md +4 -19
- package/skills/tdd/SKILL.md +4 -100
- package/skills/team/SKILL.md +6 -2
- package/skills/trace/SKILL.md +4 -27
- package/skills/ultragoal/SKILL.md +49 -0
- package/skills/visual-ralph/SKILL.md +9 -9
- package/skills/visual-verdict/SKILL.md +4 -70
- package/skills/web-clone/SKILL.md +4 -18
- package/src/scripts/__tests__/codex-native-hook.test.ts +1654 -157
- package/src/scripts/__tests__/hook-derived-watcher.test.ts +45 -1
- package/src/scripts/__tests__/run-test-files.test.ts +46 -0
- package/src/scripts/codex-native-hook.ts +592 -52
- package/src/scripts/codex-native-pre-post.ts +252 -20
- package/src/scripts/hook-derived-watcher.ts +2 -1
- package/src/scripts/notify-fallback-watcher.ts +2 -1
- package/src/scripts/notify-hook/orchestration-intent.ts +1 -3
- package/src/scripts/notify-hook/team-leader-nudge.ts +7 -63
- package/src/scripts/notify-hook/team-worker-stop.ts +246 -0
- package/src/scripts/notify-hook/team-worker.ts +23 -14
- package/src/scripts/run-test-files.ts +20 -1
- package/src/scripts/sync-plugin-mirror.ts +2 -2
- package/templates/catalog-manifest.json +45 -27
- package/plugins/oh-my-codex/skills/ask-claude/SKILL.md +0 -61
- package/plugins/oh-my-codex/skills/ask-gemini/SKILL.md +0 -61
- package/plugins/oh-my-codex/skills/help/SKILL.md +0 -202
- package/plugins/oh-my-codex/skills/note/SKILL.md +0 -62
- package/plugins/oh-my-codex/skills/security-review/SKILL.md +0 -300
- package/plugins/oh-my-codex/skills/trace/SKILL.md +0 -33
- package/plugins/oh-my-codex/skills/visual-verdict/SKILL.md +0 -76
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { execFileSync } from "child_process";
|
|
2
2
|
import { closeSync, existsSync, openSync, readFileSync, readSync } from "fs";
|
|
3
3
|
import { mkdir, readFile, readdir, writeFile } from "fs/promises";
|
|
4
|
-
import { join, relative, resolve } from "path";
|
|
4
|
+
import { extname, join, relative, resolve } from "path";
|
|
5
5
|
import { pathToFileURL } from "url";
|
|
6
|
-
import { readModeState, readModeStateForSession, updateModeState } from "../modes/base.js";
|
|
6
|
+
import { readModeState, readModeStateForActiveDecision, readModeStateForSession, updateModeState } from "../modes/base.js";
|
|
7
7
|
import {
|
|
8
|
+
extractSessionIdFromInitializedStatePath,
|
|
8
9
|
listActiveSkills,
|
|
9
10
|
readVisibleSkillActiveState,
|
|
10
11
|
} from "../state/skill-active.js";
|
|
@@ -45,11 +46,16 @@ import {
|
|
|
45
46
|
resolveEffectiveAutoNudgeResponse,
|
|
46
47
|
} from "./notify-hook/auto-nudge.js";
|
|
47
48
|
import {
|
|
49
|
+
SLOPPY_FALLBACK_GROUNDING_PATTERNS,
|
|
50
|
+
SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS,
|
|
51
|
+
SLOPPY_FALLBACK_PHRASE_PATTERNS,
|
|
48
52
|
buildNativePostToolUseOutput,
|
|
49
53
|
buildNativePreToolUseOutput,
|
|
50
54
|
detectMcpTransportFailure,
|
|
55
|
+
hasAnyPattern,
|
|
51
56
|
} from "./codex-native-pre-post.js";
|
|
52
57
|
import { handleTeamWorkerPostToolUseSuccess } from "./notify-hook/team-worker-posttooluse.js";
|
|
58
|
+
import { maybeNudgeLeaderForAllowedWorkerStop } from "./notify-hook/team-worker-stop.js";
|
|
53
59
|
import {
|
|
54
60
|
resolveCodexExecutionSurface,
|
|
55
61
|
type CodexLauncherKind,
|
|
@@ -59,10 +65,10 @@ import {
|
|
|
59
65
|
buildNativeHookEvent,
|
|
60
66
|
} from "../hooks/extensibility/events.js";
|
|
61
67
|
import type { HookEventEnvelope } from "../hooks/extensibility/types.js";
|
|
62
|
-
import {
|
|
68
|
+
import { dispatchHookEventRuntime } from "../hooks/extensibility/runtime.js";
|
|
63
69
|
import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
|
|
64
70
|
import { onSessionStart as buildWikiSessionStartContext } from "../wiki/lifecycle.js";
|
|
65
|
-
import { readAutoresearchCompletionStatus,
|
|
71
|
+
import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDecision } from "../autoresearch/skill-validation.js";
|
|
66
72
|
import { readRunState } from "../runtime/run-state.js";
|
|
67
73
|
import { getRunContinuationSnapshot, shouldContinueRun } from "../runtime/run-loop.js";
|
|
68
74
|
import { triagePrompt } from "../hooks/triage-heuristic.js";
|
|
@@ -109,8 +115,8 @@ export interface NativeHookDispatchResult {
|
|
|
109
115
|
|
|
110
116
|
const TERMINAL_MODE_PHASES = new Set(["complete", "completed", "failed", "cancelled"]);
|
|
111
117
|
const SKILL_STOP_BLOCKERS = new Set(["ralplan"]);
|
|
112
|
-
const
|
|
113
|
-
const
|
|
118
|
+
const TEAM_STOP_BLOCKING_TASK_STATUSES = new Set(["pending", "in_progress", "blocked"]);
|
|
119
|
+
const TEAM_WORKER_TERMINAL_RUN_STATES = new Set(["done", "complete", "completed", "failed", "stopped", "cancelled"]);
|
|
114
120
|
const NATIVE_STOP_STATE_FILE = "native-stop-state.json";
|
|
115
121
|
const STABLE_FINAL_RECOMMENDATION_PATTERNS = [
|
|
116
122
|
/^\s*(?:launch|release|ship)-?ready\s*:\s*(?:yes|no)\b[^\n\r]*/im,
|
|
@@ -260,6 +266,26 @@ async function nativeSubagentSessionStartBelongsToCanonicalSession(
|
|
|
260
266
|
return summary.allThreadIds.includes(parentThreadId);
|
|
261
267
|
}
|
|
262
268
|
|
|
269
|
+
async function isNativeSubagentHook(
|
|
270
|
+
cwd: string,
|
|
271
|
+
canonicalSessionId: string,
|
|
272
|
+
nativeSessionId: string,
|
|
273
|
+
threadId: string,
|
|
274
|
+
): Promise<boolean> {
|
|
275
|
+
const sessionId = canonicalSessionId.trim();
|
|
276
|
+
if (!sessionId) return false;
|
|
277
|
+
|
|
278
|
+
const summary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
|
|
279
|
+
if (!summary) return false;
|
|
280
|
+
|
|
281
|
+
const candidateIds = [nativeSessionId, threadId]
|
|
282
|
+
.map((value) => value.trim())
|
|
283
|
+
.filter(Boolean);
|
|
284
|
+
if (candidateIds.length === 0) return false;
|
|
285
|
+
|
|
286
|
+
return candidateIds.some((id) => summary.allSubagentThreadIds.includes(id));
|
|
287
|
+
}
|
|
288
|
+
|
|
263
289
|
async function recordIgnoredNativeSubagentSessionStart(
|
|
264
290
|
cwd: string,
|
|
265
291
|
canonicalSessionId: string,
|
|
@@ -434,7 +460,7 @@ async function readActiveAutoresearchState(
|
|
|
434
460
|
): Promise<Record<string, unknown> | null> {
|
|
435
461
|
const normalizedSessionId = sessionId?.trim() || undefined;
|
|
436
462
|
if (!normalizedSessionId) return null;
|
|
437
|
-
const state = await
|
|
463
|
+
const state = await readAutoresearchModeStateForActiveDecision(cwd, normalizedSessionId);
|
|
438
464
|
if (state?.active !== true) return null;
|
|
439
465
|
if (!isNonTerminalPhase(state.current_phase ?? state.currentPhase ?? 'executing')) return null;
|
|
440
466
|
return state;
|
|
@@ -528,6 +554,19 @@ async function isVisibleRalphActiveForSession(cwd: string, sessionId: string): P
|
|
|
528
554
|
));
|
|
529
555
|
}
|
|
530
556
|
|
|
557
|
+
async function hasConsistentRalphSkillActivation(cwd: string, sessionId: string): Promise<boolean> {
|
|
558
|
+
const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
|
|
559
|
+
if (!canonicalState) return true;
|
|
560
|
+
|
|
561
|
+
const initializedMode = safeString(canonicalState.initialized_mode).trim();
|
|
562
|
+
if (initializedMode && initializedMode !== "ralph") return true;
|
|
563
|
+
|
|
564
|
+
const initializedPathSessionId = extractSessionIdFromInitializedStatePath(canonicalState.initialized_state_path);
|
|
565
|
+
if (initializedPathSessionId && initializedPathSessionId !== sessionId) return false;
|
|
566
|
+
|
|
567
|
+
return true;
|
|
568
|
+
}
|
|
569
|
+
|
|
531
570
|
async function readActiveRalphState(
|
|
532
571
|
stateDir: string,
|
|
533
572
|
preferredSessionId?: string,
|
|
@@ -581,6 +620,7 @@ async function readActiveRalphState(
|
|
|
581
620
|
currentNativeSessionId,
|
|
582
621
|
tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
|
|
583
622
|
})
|
|
623
|
+
&& await hasConsistentRalphSkillActivation(cwd, sessionId)
|
|
584
624
|
) {
|
|
585
625
|
return { state: sessionScoped, path: sessionScopedPath };
|
|
586
626
|
}
|
|
@@ -709,6 +749,192 @@ function tryReadGitValue(cwd: string, args: string[]): string | null {
|
|
|
709
749
|
}
|
|
710
750
|
}
|
|
711
751
|
|
|
752
|
+
interface SloppyFallbackDiffFinding {
|
|
753
|
+
path: string;
|
|
754
|
+
line: string;
|
|
755
|
+
source: "staged" | "unstaged" | "untracked";
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const SOURCE_DIFF_EXTENSIONS = new Set([
|
|
759
|
+
".c",
|
|
760
|
+
".cc",
|
|
761
|
+
".cjs",
|
|
762
|
+
".cpp",
|
|
763
|
+
".cs",
|
|
764
|
+
".cts",
|
|
765
|
+
".go",
|
|
766
|
+
".h",
|
|
767
|
+
".hpp",
|
|
768
|
+
".java",
|
|
769
|
+
".js",
|
|
770
|
+
".jsx",
|
|
771
|
+
".kt",
|
|
772
|
+
".mjs",
|
|
773
|
+
".mts",
|
|
774
|
+
".php",
|
|
775
|
+
".py",
|
|
776
|
+
".rb",
|
|
777
|
+
".rs",
|
|
778
|
+
".sh",
|
|
779
|
+
".swift",
|
|
780
|
+
".ts",
|
|
781
|
+
".tsx",
|
|
782
|
+
]);
|
|
783
|
+
|
|
784
|
+
function gitOutput(cwd: string, args: string[]): string {
|
|
785
|
+
try {
|
|
786
|
+
return execFileSync("git", args, {
|
|
787
|
+
cwd,
|
|
788
|
+
encoding: "utf-8",
|
|
789
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
790
|
+
windowsHide: true,
|
|
791
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
792
|
+
});
|
|
793
|
+
} catch {
|
|
794
|
+
return "";
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
function normalizeGitPath(path: string): string {
|
|
799
|
+
return path.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
function isDiffAuditableSourcePath(path: string): boolean {
|
|
803
|
+
const normalized = normalizeGitPath(path).toLowerCase();
|
|
804
|
+
if (!normalized || normalized.startsWith(".git/") || normalized.startsWith(".omx/")) return false;
|
|
805
|
+
if (/(^|\/)(?:docs?|documentation|changelog|changeset|\.github)(?:\/|$)/i.test(normalized)) return false;
|
|
806
|
+
if (/(^|\/)(?:__tests__|__test__|test|tests|spec|specs|fixtures?|mocks?)(?:\/|$)/i.test(normalized)) return false;
|
|
807
|
+
if (/(?:^|\/)[^\/]+\.(?:test|spec)\.[^.\/]+$/i.test(normalized)) return false;
|
|
808
|
+
if (/(?:^|\/)(?:readme|changelog|changes|license|notice)(?:\.[^\/]*)?$/i.test(normalized)) return false;
|
|
809
|
+
if (/\.(?:md|mdx|markdown|txt|rst|adoc|ya?ml|json|lock)$/i.test(normalized)) return false;
|
|
810
|
+
return SOURCE_DIFF_EXTENSIONS.has(extname(normalized));
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function isDiffHeaderLine(line: string): boolean {
|
|
814
|
+
return line.startsWith("+++") || line.startsWith("---") || line.startsWith("@@") || line.startsWith("diff --git ");
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
function isSuspiciousSloppyFallbackAddedLine(line: string, nearbyContext: string): boolean {
|
|
818
|
+
const trimmed = line.trim();
|
|
819
|
+
if (!trimmed) return false;
|
|
820
|
+
if (!hasAnyPattern(trimmed, SLOPPY_FALLBACK_PHRASE_PATTERNS)) return false;
|
|
821
|
+
if (!hasAnyPattern(trimmed, SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS)) return false;
|
|
822
|
+
if (hasAnyPattern(nearbyContext, SLOPPY_FALLBACK_GROUNDING_PATTERNS)) return false;
|
|
823
|
+
if (/compatib(?:le|ility)|fail-?safe|tested|regression|coverage|because|issue|PR\s*#?\d|#\d/i.test(nearbyContext)) return false;
|
|
824
|
+
return true;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
interface SloppyFallbackCandidateLine {
|
|
828
|
+
text: string;
|
|
829
|
+
added: boolean;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
function collectFindingsFromCandidateLines(
|
|
833
|
+
path: string,
|
|
834
|
+
lines: SloppyFallbackCandidateLine[],
|
|
835
|
+
source: SloppyFallbackDiffFinding["source"],
|
|
836
|
+
): SloppyFallbackDiffFinding[] {
|
|
837
|
+
if (!path || !isDiffAuditableSourcePath(path)) return [];
|
|
838
|
+
const findings: SloppyFallbackDiffFinding[] = [];
|
|
839
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
840
|
+
const candidate = lines[index];
|
|
841
|
+
if (!candidate?.added) continue;
|
|
842
|
+
const nearbyContext = lines
|
|
843
|
+
.slice(Math.max(0, index - 2), Math.min(lines.length, index + 3))
|
|
844
|
+
.map((line) => line.text)
|
|
845
|
+
.join("\n");
|
|
846
|
+
if (isSuspiciousSloppyFallbackAddedLine(candidate.text, nearbyContext)) {
|
|
847
|
+
findings.push({ path, line: candidate.text.trim(), source });
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
return findings;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
function collectSloppyFallbackFindingsFromPatch(
|
|
854
|
+
patch: string,
|
|
855
|
+
source: SloppyFallbackDiffFinding["source"],
|
|
856
|
+
): SloppyFallbackDiffFinding[] {
|
|
857
|
+
const findings: SloppyFallbackDiffFinding[] = [];
|
|
858
|
+
let currentPath = "";
|
|
859
|
+
let hunkLines: SloppyFallbackCandidateLine[] = [];
|
|
860
|
+
|
|
861
|
+
const flushHunk = () => {
|
|
862
|
+
findings.push(...collectFindingsFromCandidateLines(currentPath, hunkLines, source));
|
|
863
|
+
hunkLines = [];
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
for (const rawLine of patch.split(/\r?\n/)) {
|
|
867
|
+
const fileMatch = rawLine.match(/^diff --git a\/(.*?) b\/(.*)$/);
|
|
868
|
+
if (fileMatch) {
|
|
869
|
+
flushHunk();
|
|
870
|
+
currentPath = normalizeGitPath(fileMatch[2] || fileMatch[1] || "");
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
const renameMatch = rawLine.match(/^\+\+\+ b\/(.*)$/);
|
|
874
|
+
if (renameMatch) {
|
|
875
|
+
currentPath = normalizeGitPath(renameMatch[1] || currentPath);
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
878
|
+
if (rawLine.startsWith("@@")) {
|
|
879
|
+
flushHunk();
|
|
880
|
+
continue;
|
|
881
|
+
}
|
|
882
|
+
if (!currentPath || !isDiffAuditableSourcePath(currentPath) || isDiffHeaderLine(rawLine)) continue;
|
|
883
|
+
if (rawLine.startsWith("+")) {
|
|
884
|
+
hunkLines.push({ text: rawLine.slice(1), added: true });
|
|
885
|
+
} else if (rawLine.startsWith(" ")) {
|
|
886
|
+
hunkLines.push({ text: rawLine.slice(1), added: false });
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
flushHunk();
|
|
890
|
+
return findings;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function collectSloppyFallbackFindingsFromUntracked(cwd: string): SloppyFallbackDiffFinding[] {
|
|
894
|
+
const output = gitOutput(cwd, ["ls-files", "--others", "--exclude-standard", "-z"]);
|
|
895
|
+
if (!output) return [];
|
|
896
|
+
const findings: SloppyFallbackDiffFinding[] = [];
|
|
897
|
+
for (const rawPath of output.split("\0")) {
|
|
898
|
+
const path = normalizeGitPath(rawPath.trim());
|
|
899
|
+
if (!path || !isDiffAuditableSourcePath(path)) continue;
|
|
900
|
+
let content = "";
|
|
901
|
+
try {
|
|
902
|
+
content = readFileSync(join(cwd, path), "utf-8");
|
|
903
|
+
} catch {
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
findings.push(...collectFindingsFromCandidateLines(path, content.split(/\r?\n/).map((text) => ({ text, added: true })), "untracked"));
|
|
907
|
+
}
|
|
908
|
+
return findings;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
function findSloppyFallbackDiffFindings(cwd: string): SloppyFallbackDiffFinding[] {
|
|
912
|
+
const layout = findGitLayout(cwd);
|
|
913
|
+
if (!layout) return [];
|
|
914
|
+
const auditRoot = layout.worktreeRoot;
|
|
915
|
+
return [
|
|
916
|
+
...collectSloppyFallbackFindingsFromPatch(gitOutput(auditRoot, ["diff", "--cached", "--no-ext-diff", "--unified=3"]), "staged"),
|
|
917
|
+
...collectSloppyFallbackFindingsFromPatch(gitOutput(auditRoot, ["diff", "--no-ext-diff", "--unified=3"]), "unstaged"),
|
|
918
|
+
...collectSloppyFallbackFindingsFromUntracked(auditRoot),
|
|
919
|
+
];
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function buildSloppyFallbackDiffStopOutput(findings: SloppyFallbackDiffFinding[]): Record<string, unknown> | null {
|
|
923
|
+
if (findings.length === 0) return null;
|
|
924
|
+
const preview = findings
|
|
925
|
+
.slice(0, 3)
|
|
926
|
+
.map((finding) => `${finding.path} (${finding.source}): ${finding.line}`)
|
|
927
|
+
.join("; ");
|
|
928
|
+
const systemMessage =
|
|
929
|
+
`Sloppy fallback/workaround diff audit detected ungrounded fallback code in added source lines: ${preview}. `
|
|
930
|
+
+ "Continue by replacing the bypass/workaround with a grounded design, or add explicit compatibility/fail-safe/tested/issue rationale near the code if the fallback is intentional.";
|
|
931
|
+
return {
|
|
932
|
+
decision: "block",
|
|
933
|
+
reason: systemMessage,
|
|
934
|
+
stopReason: "sloppy_fallback_diff_audit",
|
|
935
|
+
systemMessage,
|
|
936
|
+
};
|
|
937
|
+
}
|
|
712
938
|
|
|
713
939
|
function localExcludeAlreadyIgnoresOmx(cwd: string): boolean {
|
|
714
940
|
const layout = findGitLayout(cwd);
|
|
@@ -1101,6 +1327,9 @@ function buildAdditionalContextMessage(
|
|
|
1101
1327
|
const ultraworkPromptActivationNote = skillState?.initialized_mode === "ultrawork"
|
|
1102
1328
|
? "Ultrawork protocol: ground the task before editing, define pass/fail acceptance criteria, keep shared-file work local, and use direct-tool plus background evidence lanes only for truly independent work. Direct ultrawork provides lightweight verification only; Ralph owns persistence and the full verified-completion promise."
|
|
1103
1329
|
: null;
|
|
1330
|
+
const ultragoalPromptActivationNote = match.skill === "ultragoal"
|
|
1331
|
+
? "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."
|
|
1332
|
+
: null;
|
|
1104
1333
|
const combinedTransitionMessage = (() => {
|
|
1105
1334
|
if (!skillState?.transition_message) return null;
|
|
1106
1335
|
if (matches.length <= 1 || activeSkills.length <= 1) return skillState.transition_message;
|
|
@@ -1127,6 +1356,7 @@ function buildAdditionalContextMessage(
|
|
|
1127
1356
|
? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
|
|
1128
1357
|
: null,
|
|
1129
1358
|
promptPriorityMessage,
|
|
1359
|
+
ultragoalPromptActivationNote,
|
|
1130
1360
|
skillState.initialized_mode && skillState.initialized_state_path
|
|
1131
1361
|
? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
|
|
1132
1362
|
: null,
|
|
@@ -1152,6 +1382,7 @@ function buildAdditionalContextMessage(
|
|
|
1152
1382
|
initializedStateMessage,
|
|
1153
1383
|
deepInterviewPromptActivationNote,
|
|
1154
1384
|
ultraworkPromptActivationNote,
|
|
1385
|
+
ultragoalPromptActivationNote,
|
|
1155
1386
|
buildTeamRuntimeInstruction(cwd, payload),
|
|
1156
1387
|
buildTeamHelpInstruction(cwd, payload),
|
|
1157
1388
|
"Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
|
|
@@ -1169,12 +1400,13 @@ function buildAdditionalContextMessage(
|
|
|
1169
1400
|
`skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`,
|
|
1170
1401
|
deepInterviewPromptActivationNote,
|
|
1171
1402
|
ultraworkPromptActivationNote,
|
|
1403
|
+
ultragoalPromptActivationNote,
|
|
1172
1404
|
ralphPromptActivationNote,
|
|
1173
1405
|
"Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
|
|
1174
1406
|
].join(" ");
|
|
1175
1407
|
}
|
|
1176
1408
|
|
|
1177
|
-
return [detectedKeywordMessage, promptPriorityMessage, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
|
|
1409
|
+
return [detectedKeywordMessage, promptPriorityMessage, ultragoalPromptActivationNote, "Follow AGENTS.md routing and preserve workflow transition and planning-safety rules."].filter(Boolean).join(" ");
|
|
1178
1410
|
}
|
|
1179
1411
|
|
|
1180
1412
|
function parseTeamWorkerEnv(rawValue: string): { teamName: string; workerName: string } | null {
|
|
@@ -1190,26 +1422,79 @@ async function resolveTeamStateDirForWorkerContext(
|
|
|
1190
1422
|
cwd: string,
|
|
1191
1423
|
workerContext: { teamName: string; workerName: string },
|
|
1192
1424
|
): Promise<string | null> {
|
|
1193
|
-
|
|
1425
|
+
const resolved = await resolveWorkerNotifyTeamStateRootPath(cwd, workerContext, process.env).catch(() => null);
|
|
1426
|
+
if (resolved) return resolved;
|
|
1427
|
+
const explicit = safeString(process.env.OMX_TEAM_STATE_ROOT).trim();
|
|
1428
|
+
if (explicit) {
|
|
1429
|
+
const candidate = resolve(cwd, explicit);
|
|
1430
|
+
const workerRoot = join(candidate, "team", workerContext.teamName, "workers", workerContext.workerName);
|
|
1431
|
+
if (existsSync(workerRoot)) return candidate;
|
|
1432
|
+
return candidate;
|
|
1433
|
+
}
|
|
1434
|
+
return null;
|
|
1194
1435
|
}
|
|
1195
1436
|
|
|
1196
1437
|
|
|
1197
|
-
|
|
1438
|
+
type TeamWorkerStopDecision =
|
|
1439
|
+
| {
|
|
1440
|
+
kind: "blocked";
|
|
1441
|
+
stateDir: string;
|
|
1442
|
+
workerContext: { teamName: string; workerName: string };
|
|
1443
|
+
output: Record<string, unknown>;
|
|
1444
|
+
allowRepeatDuringStopHook: boolean;
|
|
1445
|
+
}
|
|
1446
|
+
| {
|
|
1447
|
+
kind: "allowed";
|
|
1448
|
+
stateDir: string;
|
|
1449
|
+
workerContext: { teamName: string; workerName: string };
|
|
1450
|
+
}
|
|
1451
|
+
| {
|
|
1452
|
+
kind: "unresolved";
|
|
1453
|
+
reason: string;
|
|
1454
|
+
};
|
|
1455
|
+
|
|
1456
|
+
async function resolveTeamWorkerStopDecision(
|
|
1198
1457
|
cwd: string,
|
|
1199
|
-
): Promise<
|
|
1200
|
-
const workerContext =
|
|
1201
|
-
|
|
1458
|
+
): Promise<TeamWorkerStopDecision> {
|
|
1459
|
+
const workerContext =
|
|
1460
|
+
parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_INTERNAL_WORKER))
|
|
1461
|
+
|| parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_WORKER));
|
|
1462
|
+
if (!workerContext) return { kind: "unresolved", reason: "missing_worker_context" };
|
|
1463
|
+
|
|
1464
|
+
const blockWorkerStop = (
|
|
1465
|
+
reasonCode: string,
|
|
1466
|
+
detail: string,
|
|
1467
|
+
stateDirForDecision = join(cwd, ".omx", "state"),
|
|
1468
|
+
): TeamWorkerStopDecision => ({
|
|
1469
|
+
kind: "blocked",
|
|
1470
|
+
stateDir: stateDirForDecision,
|
|
1471
|
+
workerContext,
|
|
1472
|
+
allowRepeatDuringStopHook: false,
|
|
1473
|
+
output: {
|
|
1474
|
+
decision: "block",
|
|
1475
|
+
reason:
|
|
1476
|
+
`OMX team worker ${workerContext.workerName} Stop cannot be allowed for ${reasonCode}: ${detail}. ` +
|
|
1477
|
+
"Continue the assigned task, repair worker state, or report a concrete blocker before stopping.",
|
|
1478
|
+
stopReason: `team_worker_${workerContext.workerName}_${reasonCode}`,
|
|
1479
|
+
systemMessage:
|
|
1480
|
+
`OMX team worker ${workerContext.workerName} Stop lacks completed task evidence (${reasonCode}).`,
|
|
1481
|
+
},
|
|
1482
|
+
});
|
|
1202
1483
|
|
|
1203
1484
|
const stateDir = await resolveTeamStateDirForWorkerContext(cwd, workerContext);
|
|
1204
|
-
if (!stateDir)
|
|
1485
|
+
if (!stateDir) {
|
|
1486
|
+
return blockWorkerStop("missing_state_dir", "team state root could not be resolved");
|
|
1487
|
+
}
|
|
1205
1488
|
const workerRoot = join(stateDir, "team", workerContext.teamName, "workers", workerContext.workerName);
|
|
1206
1489
|
const [identity, status] = await Promise.all([
|
|
1207
1490
|
readJsonIfExists(join(workerRoot, "identity.json")),
|
|
1208
1491
|
readJsonIfExists(join(workerRoot, "status.json")),
|
|
1209
1492
|
]);
|
|
1210
|
-
|
|
1211
|
-
const
|
|
1212
|
-
if (!
|
|
1493
|
+
const workerRunState = safeString(status?.state).trim().toLowerCase();
|
|
1494
|
+
const workerRunStateIsTerminal = TEAM_WORKER_TERMINAL_RUN_STATES.has(workerRunState);
|
|
1495
|
+
if (!identity && !status && !existsSync(workerRoot)) {
|
|
1496
|
+
return blockWorkerStop("missing_worker_state", "worker identity/status state is missing", stateDir);
|
|
1497
|
+
}
|
|
1213
1498
|
|
|
1214
1499
|
const candidateTaskIds = new Set<string>();
|
|
1215
1500
|
const currentTaskId = safeString(status?.current_task_id).trim();
|
|
@@ -1220,27 +1505,66 @@ async function buildTeamWorkerStopOutput(
|
|
|
1220
1505
|
if (normalized) candidateTaskIds.add(normalized);
|
|
1221
1506
|
}
|
|
1222
1507
|
|
|
1508
|
+
const tasksDir = join(stateDir, "team", workerContext.teamName, "tasks");
|
|
1509
|
+
if (existsSync(tasksDir)) {
|
|
1510
|
+
const taskFiles = await readdir(tasksDir).catch(() => []);
|
|
1511
|
+
for (const entry of taskFiles) {
|
|
1512
|
+
if (!/^task-\d+\.json$/.test(entry)) continue;
|
|
1513
|
+
const task = await readJsonIfExists(join(tasksDir, entry));
|
|
1514
|
+
const taskOwner = safeString(task?.owner).trim();
|
|
1515
|
+
const taskClaimOwner = safeString(safeObject(task?.claim).owner).trim();
|
|
1516
|
+
if (taskOwner !== workerContext.workerName && taskClaimOwner !== workerContext.workerName) continue;
|
|
1517
|
+
const idFromFile = /^task-(\d+)\.json$/.exec(entry)?.[1] ?? "";
|
|
1518
|
+
const taskId = safeString(task?.id).trim() || idFromFile;
|
|
1519
|
+
if (taskId) candidateTaskIds.add(taskId);
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
if (candidateTaskIds.size === 0) {
|
|
1524
|
+
return blockWorkerStop("missing_task_assignment", "no current_task_id or assigned_tasks are recorded", stateDir);
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
let completedTaskCount = 0;
|
|
1223
1528
|
for (const taskId of candidateTaskIds) {
|
|
1224
1529
|
const task = await readJsonIfExists(
|
|
1225
1530
|
join(stateDir, "team", workerContext.teamName, "tasks", `task-${taskId}.json`),
|
|
1226
1531
|
);
|
|
1227
1532
|
const statusValue = safeString(task?.status).trim().toLowerCase();
|
|
1228
|
-
if (!statusValue
|
|
1533
|
+
if (!statusValue) {
|
|
1534
|
+
return blockWorkerStop(`missing_task_state_${taskId}`, `task ${taskId} has no readable status`, stateDir);
|
|
1535
|
+
}
|
|
1536
|
+
if (statusValue === "completed") {
|
|
1537
|
+
completedTaskCount += 1;
|
|
1538
|
+
continue;
|
|
1539
|
+
}
|
|
1540
|
+
if (!TEAM_STOP_BLOCKING_TASK_STATUSES.has(statusValue)) {
|
|
1541
|
+
return blockWorkerStop(
|
|
1542
|
+
`non_completed_task_${taskId}_${statusValue}`,
|
|
1543
|
+
`task ${taskId} is ${statusValue}, not completed`,
|
|
1544
|
+
stateDir,
|
|
1545
|
+
);
|
|
1546
|
+
}
|
|
1229
1547
|
return {
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1548
|
+
kind: "blocked",
|
|
1549
|
+
stateDir,
|
|
1550
|
+
workerContext,
|
|
1551
|
+
allowRepeatDuringStopHook: !workerRunStateIsTerminal,
|
|
1552
|
+
output: {
|
|
1553
|
+
decision: "block",
|
|
1554
|
+
reason:
|
|
1555
|
+
`OMX team worker ${workerContext.workerName} is still assigned non-terminal task ${taskId} (${statusValue}); continue the current assigned task or report a concrete blocker before stopping.`,
|
|
1556
|
+
stopReason: `team_worker_${workerContext.workerName}_${taskId}_${statusValue}`,
|
|
1557
|
+
systemMessage:
|
|
1558
|
+
`OMX team worker ${workerContext.workerName} is still assigned task ${taskId} (${statusValue}).`,
|
|
1559
|
+
},
|
|
1236
1560
|
};
|
|
1237
1561
|
}
|
|
1238
1562
|
|
|
1239
|
-
|
|
1240
|
-
}
|
|
1563
|
+
if (completedTaskCount === candidateTaskIds.size) {
|
|
1564
|
+
return { kind: "allowed", stateDir, workerContext };
|
|
1565
|
+
}
|
|
1241
1566
|
|
|
1242
|
-
|
|
1243
|
-
return parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_INTERNAL_WORKER || process.env.OMX_TEAM_WORKER)) !== null;
|
|
1567
|
+
return blockWorkerStop("missing_completed_task_evidence", "no referenced worker task is completed", stateDir);
|
|
1244
1568
|
}
|
|
1245
1569
|
|
|
1246
1570
|
function isStopExempt(payload: CodexHookPayload): boolean {
|
|
@@ -1267,9 +1591,7 @@ async function buildModeBasedStopOutput(
|
|
|
1267
1591
|
cwd: string,
|
|
1268
1592
|
sessionId?: string,
|
|
1269
1593
|
): Promise<Record<string, unknown> | null> {
|
|
1270
|
-
const state = sessionId
|
|
1271
|
-
? await readModeStateForSession(mode, sessionId, cwd)
|
|
1272
|
-
: await readModeState(mode, cwd);
|
|
1594
|
+
const state = await readModeStateForActiveDecision(mode, sessionId?.trim() || undefined, cwd);
|
|
1273
1595
|
if (!state || !shouldContinueRun(state)) return null;
|
|
1274
1596
|
const phase = formatPhase(state.current_phase);
|
|
1275
1597
|
return {
|
|
@@ -1280,6 +1602,81 @@ async function buildModeBasedStopOutput(
|
|
|
1280
1602
|
};
|
|
1281
1603
|
}
|
|
1282
1604
|
|
|
1605
|
+
function looksLikeGoalCompletionPrompt(text: string): boolean {
|
|
1606
|
+
return /\b(?:complete|checkpoint|finish|close|mark)\b.{0,80}\b(?:goal|ultragoal|performance-goal|autoresearch-goal)\b/i.test(text)
|
|
1607
|
+
|| /\bupdate_goal\s*\(/i.test(text)
|
|
1608
|
+
|| /\bomx\s+(?:ultragoal|performance-goal|autoresearch-goal)\s+(?:checkpoint|complete)\b/i.test(text);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Promise<{ workflow: string; command: string } | null> {
|
|
1612
|
+
const ultragoal = await readJsonIfExists(join(cwd, ".omx", "ultragoal", "goals.json"));
|
|
1613
|
+
const ultragoals = Array.isArray(ultragoal?.goals) ? ultragoal.goals.map(safeObject) : [];
|
|
1614
|
+
const activeUltragoal = ultragoals.find((goal) => safeString(goal.status) === "in_progress" || safeString(goal.id) === safeString(ultragoal?.activeGoalId));
|
|
1615
|
+
if (activeUltragoal) {
|
|
1616
|
+
return {
|
|
1617
|
+
workflow: "ultragoal",
|
|
1618
|
+
command: `omx ultragoal checkpoint --goal-id ${safeString(activeUltragoal.id) || "<goal-id>"} --status complete --codex-goal-json '<get_goal JSON or path>' --evidence '<evidence>'`,
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
const performanceRoot = join(cwd, ".omx", "goals", "performance");
|
|
1623
|
+
for (const entry of await readdir(performanceRoot, { withFileTypes: true }).catch(() => [])) {
|
|
1624
|
+
if (!entry.isDirectory()) continue;
|
|
1625
|
+
const state = await readJsonIfExists(join(performanceRoot, entry.name, "state.json"));
|
|
1626
|
+
const status = safeString(state?.status);
|
|
1627
|
+
if (state?.workflow === "performance-goal" && status && status !== "complete") {
|
|
1628
|
+
return {
|
|
1629
|
+
workflow: "performance-goal",
|
|
1630
|
+
command: `omx performance-goal complete --slug ${safeString(state.slug) || entry.name} --codex-goal-json '<get_goal JSON or path>' --evidence '<evidence>'`,
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
const autoresearchRoot = join(cwd, ".omx", "goals", "autoresearch");
|
|
1636
|
+
for (const entry of await readdir(autoresearchRoot, { withFileTypes: true }).catch(() => [])) {
|
|
1637
|
+
if (!entry.isDirectory()) continue;
|
|
1638
|
+
const mission = await readJsonIfExists(join(autoresearchRoot, entry.name, "mission.json"));
|
|
1639
|
+
const status = safeString(mission?.status);
|
|
1640
|
+
if (mission?.workflow === "autoresearch-goal" && status && status !== "complete") {
|
|
1641
|
+
return {
|
|
1642
|
+
workflow: "autoresearch-goal",
|
|
1643
|
+
command: `omx autoresearch-goal complete --slug ${safeString(mission.slug) || entry.name} --codex-goal-json '<get_goal JSON or path>'`,
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
return null;
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
async function buildGoalWorkflowReconciliationPromptWarning(cwd: string, prompt: string): Promise<string | null> {
|
|
1652
|
+
if (!looksLikeGoalCompletionPrompt(prompt)) return null;
|
|
1653
|
+
const requirement = await findActiveGoalWorkflowReconciliationRequirement(cwd);
|
|
1654
|
+
if (!requirement) return null;
|
|
1655
|
+
return [
|
|
1656
|
+
`OMX ${requirement.workflow} goal workflow requires Codex goal snapshot reconciliation before completion.`,
|
|
1657
|
+
"Call get_goal, pass the resulting JSON or a path with --codex-goal-json, and do not rely on hooks or shell commands to mutate Codex-owned goal state.",
|
|
1658
|
+
`Required command shape: ${requirement.command}.`,
|
|
1659
|
+
].join(" ");
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
async function buildGoalWorkflowReconciliationStopOutput(
|
|
1663
|
+
payload: CodexHookPayload,
|
|
1664
|
+
cwd: string,
|
|
1665
|
+
): Promise<Record<string, unknown> | null> {
|
|
1666
|
+
const lastAssistantMessage = safeString(payload.last_assistant_message ?? payload.lastAssistantMessage);
|
|
1667
|
+
if (!looksLikeGoalCompletionPrompt(lastAssistantMessage)) return null;
|
|
1668
|
+
const requirement = await findActiveGoalWorkflowReconciliationRequirement(cwd);
|
|
1669
|
+
if (!requirement) return null;
|
|
1670
|
+
const systemMessage =
|
|
1671
|
+
`OMX ${requirement.workflow} requires get_goal snapshot reconciliation before completion; call get_goal and pass --codex-goal-json to ${requirement.command}. Hooks must not mutate Codex goal state.`;
|
|
1672
|
+
return {
|
|
1673
|
+
decision: "block",
|
|
1674
|
+
reason: systemMessage,
|
|
1675
|
+
stopReason: `${requirement.workflow}_codex_goal_snapshot_required`,
|
|
1676
|
+
systemMessage,
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1283
1680
|
async function readTeamModeStateForStop(
|
|
1284
1681
|
cwd: string,
|
|
1285
1682
|
sessionId?: string,
|
|
@@ -1470,7 +1867,7 @@ async function readBlockingSkillForStop(
|
|
|
1470
1867
|
sessionId: string,
|
|
1471
1868
|
threadId: string,
|
|
1472
1869
|
requiredSkill?: string,
|
|
1473
|
-
): Promise<{ skill: string; phase: string } | null> {
|
|
1870
|
+
): Promise<{ skill: string; phase: string; latestPlanPath?: string; planningComplete?: boolean; runOutcome?: string } | null> {
|
|
1474
1871
|
const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
|
|
1475
1872
|
const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
|
|
1476
1873
|
const candidateSkills = requiredSkill
|
|
@@ -1500,7 +1897,13 @@ async function readBlockingSkillForStop(
|
|
|
1500
1897
|
}
|
|
1501
1898
|
|
|
1502
1899
|
if (!canonicalState) {
|
|
1503
|
-
return {
|
|
1900
|
+
return {
|
|
1901
|
+
skill,
|
|
1902
|
+
phase,
|
|
1903
|
+
latestPlanPath: safeString(modeState.latest_plan_path ?? modeState.latestPlanPath).trim() || undefined,
|
|
1904
|
+
planningComplete: modeState.planning_complete === true || modeState.planningComplete === true,
|
|
1905
|
+
runOutcome: safeString(modeState.run_outcome ?? modeState.outcome).trim() || undefined,
|
|
1906
|
+
};
|
|
1504
1907
|
}
|
|
1505
1908
|
|
|
1506
1909
|
const blocker = visibleEntries.find((entry) => (
|
|
@@ -1512,12 +1915,65 @@ async function readBlockingSkillForStop(
|
|
|
1512
1915
|
return {
|
|
1513
1916
|
skill,
|
|
1514
1917
|
phase: formatPhase(modeState.current_phase ?? blocker.phase ?? canonicalState.phase, "planning"),
|
|
1918
|
+
latestPlanPath: safeString(modeState.latest_plan_path ?? modeState.latestPlanPath).trim() || undefined,
|
|
1919
|
+
planningComplete: modeState.planning_complete === true || modeState.planningComplete === true,
|
|
1920
|
+
runOutcome: safeString(modeState.run_outcome ?? modeState.outcome).trim() || undefined,
|
|
1515
1921
|
};
|
|
1516
1922
|
}
|
|
1517
1923
|
|
|
1518
1924
|
return null;
|
|
1519
1925
|
}
|
|
1520
1926
|
|
|
1927
|
+
function buildRalplanContinuationStatus(
|
|
1928
|
+
blocker: { phase: string; latestPlanPath?: string; planningComplete?: boolean; runOutcome?: string },
|
|
1929
|
+
activeSubagentCount: number,
|
|
1930
|
+
): { reason: string; systemMessage: string; stopReasonSuffix: string } {
|
|
1931
|
+
const phase = blocker.phase || "planning";
|
|
1932
|
+
const artifact = blocker.latestPlanPath
|
|
1933
|
+
? ` Artifact: ${blocker.latestPlanPath}.`
|
|
1934
|
+
: " Artifact: use the latest `.omx/plans/` ralplan artifact if present.";
|
|
1935
|
+
|
|
1936
|
+
if (activeSubagentCount > 0) {
|
|
1937
|
+
return {
|
|
1938
|
+
reason:
|
|
1939
|
+
`Status: waiting — ralplan is waiting for ${activeSubagentCount} active native subagent thread(s) to finish (phase: ${phase}). Do not stop silently; wait for the subagent result, then continue from the current ralplan artifact and proceed to the next planning/review step.${artifact}`,
|
|
1940
|
+
stopReasonSuffix: "waiting_subagent",
|
|
1941
|
+
systemMessage:
|
|
1942
|
+
`OMX ralplan status: waiting for ${activeSubagentCount} active native subagent thread(s) at phase ${phase}; after they finish, continue from the current ralplan artifact and state the next status explicitly.`,
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
const normalizedPhase = phase.toLowerCase();
|
|
1947
|
+
const normalizedOutcome = (blocker.runOutcome ?? "").toLowerCase();
|
|
1948
|
+
const waitingForInput =
|
|
1949
|
+
normalizedOutcome === "blocked_on_user"
|
|
1950
|
+
|| normalizedPhase.includes("blocked")
|
|
1951
|
+
|| normalizedPhase.includes("input")
|
|
1952
|
+
|| normalizedPhase.includes("question");
|
|
1953
|
+
|
|
1954
|
+
if (waitingForInput) {
|
|
1955
|
+
return {
|
|
1956
|
+
reason:
|
|
1957
|
+
`Status: waiting_for_input — ralplan is paused for required user/operator input (phase: ${phase}). Ask the missing question or present the review choice explicitly before stopping.${artifact}`,
|
|
1958
|
+
stopReasonSuffix: "waiting_input",
|
|
1959
|
+
systemMessage:
|
|
1960
|
+
`OMX ralplan status: waiting for input at phase ${phase}; ask the required question or present the explicit review choice before stopping.`,
|
|
1961
|
+
};
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
const completeHint = blocker.planningComplete
|
|
1965
|
+
? " The planning artifacts are present; if consensus is approved, emit the final complete/approved handoff instead of stopping here."
|
|
1966
|
+
: "";
|
|
1967
|
+
|
|
1968
|
+
return {
|
|
1969
|
+
reason:
|
|
1970
|
+
`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}`,
|
|
1971
|
+
stopReasonSuffix: "continue_artifact",
|
|
1972
|
+
systemMessage:
|
|
1973
|
+
`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.`,
|
|
1974
|
+
};
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1521
1977
|
async function readStopAutoNudgePhase(
|
|
1522
1978
|
cwd: string,
|
|
1523
1979
|
sessionId: string,
|
|
@@ -1612,7 +2068,12 @@ function resolveRepeatableStopSessionId(
|
|
|
1612
2068
|
payload: CodexHookPayload,
|
|
1613
2069
|
canonicalSessionId?: string,
|
|
1614
2070
|
): string {
|
|
1615
|
-
|
|
2071
|
+
const inheritedSessionId = safeString(process.env.OMX_SESSION_ID || process.env.CODEX_SESSION_ID).trim();
|
|
2072
|
+
return canonicalSessionId?.trim() || readPayloadSessionId(payload) || inheritedSessionId || "";
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
function isStateLevelStopSignatureKind(kind: string): boolean {
|
|
2076
|
+
return kind === "team-worker-stop" || kind === "team-stop";
|
|
1616
2077
|
}
|
|
1617
2078
|
|
|
1618
2079
|
function buildRepeatableStopSignature(
|
|
@@ -1623,8 +2084,11 @@ function buildRepeatableStopSignature(
|
|
|
1623
2084
|
): string {
|
|
1624
2085
|
const sessionId = resolveRepeatableStopSessionId(payload, canonicalSessionId) || "no-session";
|
|
1625
2086
|
const threadId = readPayloadThreadId(payload) || "no-thread";
|
|
1626
|
-
const turnId = readPayloadTurnId(payload);
|
|
1627
2087
|
const normalizedDetail = normalizeAutoNudgeSignatureText(detail) || safeString(detail).trim().toLowerCase();
|
|
2088
|
+
if (isStateLevelStopSignatureKind(kind)) {
|
|
2089
|
+
return [kind, sessionId, threadId, normalizedDetail || "no-detail"].join("|");
|
|
2090
|
+
}
|
|
2091
|
+
const turnId = readPayloadTurnId(payload);
|
|
1628
2092
|
const transcriptPath = safeString(payload.transcript_path ?? payload.transcriptPath).trim() || "no-transcript";
|
|
1629
2093
|
const lastAssistantMessage = normalizeAutoNudgeSignatureText(
|
|
1630
2094
|
payload.last_assistant_message ?? payload.lastAssistantMessage,
|
|
@@ -1840,7 +2304,19 @@ async function buildSkillStopOutput(
|
|
|
1840
2304
|
if (!blocker) return null;
|
|
1841
2305
|
|
|
1842
2306
|
const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
|
|
1843
|
-
|
|
2307
|
+
const activeSubagentCount = subagentSummary?.activeSubagentThreadIds.length ?? 0;
|
|
2308
|
+
|
|
2309
|
+
if (blocker.skill === "ralplan") {
|
|
2310
|
+
const status = buildRalplanContinuationStatus(blocker, activeSubagentCount);
|
|
2311
|
+
return {
|
|
2312
|
+
decision: "block",
|
|
2313
|
+
reason: status.reason,
|
|
2314
|
+
stopReason: `skill_${blocker.skill}_${blocker.phase}_${status.stopReasonSuffix}`,
|
|
2315
|
+
systemMessage: status.systemMessage,
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
if (activeSubagentCount > 0) {
|
|
1844
2320
|
return null;
|
|
1845
2321
|
}
|
|
1846
2322
|
|
|
@@ -1961,6 +2437,7 @@ async function buildStopHookOutput(
|
|
|
1961
2437
|
payload: CodexHookPayload,
|
|
1962
2438
|
cwd: string,
|
|
1963
2439
|
stateDir: string,
|
|
2440
|
+
options: { skipRalphStopBlock?: boolean } = {},
|
|
1964
2441
|
): Promise<Record<string, unknown> | null> {
|
|
1965
2442
|
if (isStopExempt(payload)) {
|
|
1966
2443
|
return null;
|
|
@@ -1971,11 +2448,13 @@ async function buildStopHookOutput(
|
|
|
1971
2448
|
const threadId = readPayloadThreadId(payload);
|
|
1972
2449
|
const execFollowupOutput = await buildExecFollowupStopOutput(cwd, canonicalSessionId);
|
|
1973
2450
|
if (execFollowupOutput) return execFollowupOutput;
|
|
1974
|
-
const ralphState =
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
2451
|
+
const ralphState = options.skipRalphStopBlock === true
|
|
2452
|
+
? null
|
|
2453
|
+
: await readActiveRalphState(stateDir, canonicalSessionId, {
|
|
2454
|
+
payloadSessionId: sessionId,
|
|
2455
|
+
threadId,
|
|
2456
|
+
tmuxPaneId: safeString(process.env.TMUX_PANE).trim(),
|
|
2457
|
+
});
|
|
1979
2458
|
if (!ralphState) {
|
|
1980
2459
|
const autoresearchState = await readActiveAutoresearchState(cwd, canonicalSessionId);
|
|
1981
2460
|
if (autoresearchState) {
|
|
@@ -1999,18 +2478,30 @@ async function buildStopHookOutput(
|
|
|
1999
2478
|
}
|
|
2000
2479
|
}
|
|
2001
2480
|
|
|
2002
|
-
const
|
|
2003
|
-
if (
|
|
2481
|
+
const teamWorkerDecision = await resolveTeamWorkerStopDecision(cwd);
|
|
2482
|
+
if (teamWorkerDecision.kind === "blocked") {
|
|
2004
2483
|
return await returnPersistentStopBlock(
|
|
2005
2484
|
payload,
|
|
2006
2485
|
stateDir,
|
|
2007
2486
|
"team-worker-stop",
|
|
2008
|
-
safeString(
|
|
2009
|
-
|
|
2487
|
+
safeString(teamWorkerDecision.output.stopReason),
|
|
2488
|
+
teamWorkerDecision.output,
|
|
2010
2489
|
canonicalSessionId,
|
|
2011
|
-
{ allowRepeatDuringStopHook:
|
|
2490
|
+
{ allowRepeatDuringStopHook: teamWorkerDecision.allowRepeatDuringStopHook },
|
|
2012
2491
|
);
|
|
2013
2492
|
}
|
|
2493
|
+
if (teamWorkerDecision.kind === "allowed") {
|
|
2494
|
+
try {
|
|
2495
|
+
await maybeNudgeLeaderForAllowedWorkerStop({
|
|
2496
|
+
stateDir: teamWorkerDecision.stateDir,
|
|
2497
|
+
logsDir: join(cwd, ".omx", "logs"),
|
|
2498
|
+
workerContext: teamWorkerDecision.workerContext,
|
|
2499
|
+
});
|
|
2500
|
+
} catch (err) {
|
|
2501
|
+
void err;
|
|
2502
|
+
}
|
|
2503
|
+
return null;
|
|
2504
|
+
}
|
|
2014
2505
|
|
|
2015
2506
|
const autopilotOutput = await buildModeBasedStopOutput("autopilot", cwd, canonicalSessionId);
|
|
2016
2507
|
if (autopilotOutput) {
|
|
@@ -2123,6 +2614,18 @@ async function buildStopHookOutput(
|
|
|
2123
2614
|
const lastAssistantMessage = safeString(
|
|
2124
2615
|
payload.last_assistant_message ?? payload.lastAssistantMessage,
|
|
2125
2616
|
);
|
|
2617
|
+
const goalWorkflowStopOutput = await buildGoalWorkflowReconciliationStopOutput(payload, cwd);
|
|
2618
|
+
if (goalWorkflowStopOutput) {
|
|
2619
|
+
return await returnPersistentStopBlock(
|
|
2620
|
+
payload,
|
|
2621
|
+
stateDir,
|
|
2622
|
+
"goal-workflow-reconciliation-stop",
|
|
2623
|
+
safeString(goalWorkflowStopOutput.stopReason),
|
|
2624
|
+
goalWorkflowStopOutput,
|
|
2625
|
+
canonicalSessionId,
|
|
2626
|
+
{ allowRepeatDuringStopHook: true },
|
|
2627
|
+
);
|
|
2628
|
+
}
|
|
2126
2629
|
const autoNudgeConfig = await loadAutoNudgeConfig();
|
|
2127
2630
|
const autoNudgePhase = await readStopAutoNudgePhase(cwd, canonicalSessionId, threadId);
|
|
2128
2631
|
|
|
@@ -2147,6 +2650,20 @@ async function buildStopHookOutput(
|
|
|
2147
2650
|
);
|
|
2148
2651
|
}
|
|
2149
2652
|
|
|
2653
|
+
const sloppyFallbackDiffFindings = findSloppyFallbackDiffFindings(cwd);
|
|
2654
|
+
const sloppyFallbackDiffOutput = buildSloppyFallbackDiffStopOutput(sloppyFallbackDiffFindings);
|
|
2655
|
+
if (sloppyFallbackDiffOutput) {
|
|
2656
|
+
return await returnPersistentStopBlock(
|
|
2657
|
+
payload,
|
|
2658
|
+
stateDir,
|
|
2659
|
+
"sloppy-fallback-diff-stop",
|
|
2660
|
+
JSON.stringify(sloppyFallbackDiffFindings),
|
|
2661
|
+
sloppyFallbackDiffOutput,
|
|
2662
|
+
canonicalSessionId,
|
|
2663
|
+
{ allowRepeatDuringStopHook: true },
|
|
2664
|
+
);
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2150
2667
|
if (isFinalHandoffDocumentRefreshCandidate(lastAssistantMessage)) {
|
|
2151
2668
|
const documentRefreshWarning = evaluateFinalHandoffDocumentRefresh(cwd, lastAssistantMessage);
|
|
2152
2669
|
if (documentRefreshWarning) {
|
|
@@ -2202,6 +2719,7 @@ export async function dispatchCodexNativeHook(
|
|
|
2202
2719
|
const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
|
|
2203
2720
|
let skillState: SkillActiveState | null = null;
|
|
2204
2721
|
let triageAdditionalContext: string | null = null;
|
|
2722
|
+
let goalWorkflowAdditionalContext: string | null = null;
|
|
2205
2723
|
|
|
2206
2724
|
const nativeSessionId = safeString(payload.session_id ?? payload.sessionId).trim();
|
|
2207
2725
|
const threadId = safeString(payload.thread_id ?? payload.threadId).trim();
|
|
@@ -2254,9 +2772,10 @@ export async function dispatchCodexNativeHook(
|
|
|
2254
2772
|
}
|
|
2255
2773
|
|
|
2256
2774
|
if (hookEventName === "Stop") {
|
|
2775
|
+
const inheritedSessionId = safeString(process.env.OMX_SESSION_ID || process.env.CODEX_SESSION_ID).trim();
|
|
2257
2776
|
const stopCanonicalSessionId = await resolveInternalSessionIdForPayload(
|
|
2258
2777
|
cwd,
|
|
2259
|
-
readPayloadSessionId(payload),
|
|
2778
|
+
readPayloadSessionId(payload) || inheritedSessionId,
|
|
2260
2779
|
);
|
|
2261
2780
|
if (stopCanonicalSessionId) {
|
|
2262
2781
|
canonicalSessionId = stopCanonicalSessionId;
|
|
@@ -2270,10 +2789,23 @@ export async function dispatchCodexNativeHook(
|
|
|
2270
2789
|
const eventSessionId = canonicalSessionId || nativeSessionId || undefined;
|
|
2271
2790
|
const sessionIdForState = canonicalSessionId || nativeSessionId;
|
|
2272
2791
|
let outputJson: Record<string, unknown> | null = null;
|
|
2792
|
+
const isSubagentPromptSubmit = hookEventName === "UserPromptSubmit"
|
|
2793
|
+
? await isNativeSubagentHook(cwd, canonicalSessionId, nativeSessionId, threadId)
|
|
2794
|
+
: false;
|
|
2795
|
+
const isSubagentStop = hookEventName === "Stop"
|
|
2796
|
+
? (await Promise.all(
|
|
2797
|
+
[...new Set([
|
|
2798
|
+
canonicalSessionId,
|
|
2799
|
+
safeString(currentSessionState?.session_id).trim(),
|
|
2800
|
+
].filter(Boolean))]
|
|
2801
|
+
.map((candidateSessionId) => isNativeSubagentHook(cwd, candidateSessionId, nativeSessionId, threadId)),
|
|
2802
|
+
)).some(Boolean)
|
|
2803
|
+
: false;
|
|
2273
2804
|
|
|
2274
2805
|
if (hookEventName === "UserPromptSubmit") {
|
|
2275
2806
|
const prompt = readPromptText(payload);
|
|
2276
|
-
|
|
2807
|
+
goalWorkflowAdditionalContext = await buildGoalWorkflowReconciliationPromptWarning(cwd, prompt).catch(() => null);
|
|
2808
|
+
if (prompt && !isSubagentPromptSubmit) {
|
|
2277
2809
|
skillState = buildNativeOutsideTmuxTeamPromptBlockState(
|
|
2278
2810
|
prompt,
|
|
2279
2811
|
cwd,
|
|
@@ -2290,7 +2822,7 @@ export async function dispatchCodexNativeHook(
|
|
|
2290
2822
|
});
|
|
2291
2823
|
}
|
|
2292
2824
|
// --- Triage classifier (advisory-only, non-keyword prompts) ---
|
|
2293
|
-
if (prompt && skillState === null) {
|
|
2825
|
+
if (prompt && skillState === null && !isSubagentPromptSubmit) {
|
|
2294
2826
|
try {
|
|
2295
2827
|
if (readTriageConfig().enabled) {
|
|
2296
2828
|
const normalized = prompt.trim().toLowerCase();
|
|
@@ -2382,7 +2914,11 @@ export async function dispatchCodexNativeHook(
|
|
|
2382
2914
|
mode: safeString(payload.mode).trim() || undefined,
|
|
2383
2915
|
},
|
|
2384
2916
|
);
|
|
2385
|
-
await
|
|
2917
|
+
await dispatchHookEventRuntime({
|
|
2918
|
+
event,
|
|
2919
|
+
cwd,
|
|
2920
|
+
allowTeamWorkerSideEffects: false,
|
|
2921
|
+
});
|
|
2386
2922
|
}
|
|
2387
2923
|
|
|
2388
2924
|
if ((hookEventName === "SessionStart" && !skipCanonicalSessionStartContext) || hookEventName === "UserPromptSubmit") {
|
|
@@ -2393,7 +2929,9 @@ export async function dispatchCodexNativeHook(
|
|
|
2393
2929
|
canonicalSessionId,
|
|
2394
2930
|
nativeSessionId: resolvedNativeSessionId || nativeSessionId,
|
|
2395
2931
|
})
|
|
2396
|
-
:
|
|
2932
|
+
: isSubagentPromptSubmit
|
|
2933
|
+
? null
|
|
2934
|
+
: (buildAdditionalContextMessage(readPromptText(payload), skillState, cwd, payload) ?? goalWorkflowAdditionalContext ?? triageAdditionalContext);
|
|
2397
2935
|
if (additionalContext) {
|
|
2398
2936
|
outputJson = {
|
|
2399
2937
|
hookSpecificOutput: {
|
|
@@ -2411,7 +2949,9 @@ export async function dispatchCodexNativeHook(
|
|
|
2411
2949
|
outputJson = buildNativePostToolUseOutput(payload);
|
|
2412
2950
|
await handleTeamWorkerPostToolUseSuccess(payload, cwd);
|
|
2413
2951
|
} else if (hookEventName === "Stop") {
|
|
2414
|
-
outputJson = await buildStopHookOutput(payload, cwd, stateDir
|
|
2952
|
+
outputJson = await buildStopHookOutput(payload, cwd, stateDir, {
|
|
2953
|
+
skipRalphStopBlock: isSubagentStop,
|
|
2954
|
+
});
|
|
2415
2955
|
}
|
|
2416
2956
|
|
|
2417
2957
|
return {
|