oh-my-codex 0.15.2 → 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/agents/__tests__/native-config.test.js +33 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- 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 +29 -2
- 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-context-window-warning.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-context-window-warning.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-context-window-warning.test.js +122 -0
- package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js +25 -2
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/exec.test.js +1 -0
- package/dist/cli/__tests__/exec.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +48 -18
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +222 -10
- 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__/mcp-serve.test.js +27 -1
- package/dist/cli/__tests__/mcp-serve.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__/ralph.test.js +59 -1
- package/dist/cli/__tests__/ralph.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 +20 -10
- 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 +242 -10
- 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 +95 -3
- 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 +21 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +268 -30
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-serve.d.ts +1 -0
- package/dist/cli/mcp-serve.d.ts.map +1 -1
- package/dist/cli/mcp-serve.js +8 -0
- package/dist/cli/mcp-serve.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 +2 -0
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +25 -1
- 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 +6 -0
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +113 -33
- 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 +39 -6
- 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 +14 -4
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +166 -66
- 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__/agents-overlay.test.js +59 -0
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/anti-slop-workflow.test.js +109 -18
- 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 +23 -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__/index.test.js +30 -14
- package/dist/hud/__tests__/index.test.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 +63 -2
- 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 +423 -14
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/stages/team-exec.d.ts +8 -4
- package/dist/pipeline/stages/team-exec.d.ts.map +1 -1
- package/dist/pipeline/stages/team-exec.js +181 -13
- package/dist/pipeline/stages/team-exec.js.map +1 -1
- package/dist/planning/__tests__/artifacts.test.js +261 -1
- package/dist/planning/__tests__/artifacts.test.js.map +1 -1
- package/dist/planning/artifact-names.d.ts +13 -0
- package/dist/planning/artifact-names.d.ts.map +1 -0
- package/dist/planning/artifact-names.js +108 -0
- package/dist/planning/artifact-names.js.map +1 -0
- package/dist/planning/artifacts.d.ts +23 -1
- package/dist/planning/artifacts.d.ts.map +1 -1
- package/dist/planning/artifacts.js +171 -59
- package/dist/planning/artifacts.js.map +1 -1
- package/dist/ralph/__tests__/persistence.test.js +21 -1
- package/dist/ralph/__tests__/persistence.test.js.map +1 -1
- package/dist/ralph/persistence.d.ts.map +1 -1
- package/dist/ralph/persistence.js +6 -4
- package/dist/ralph/persistence.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 +1749 -88
- 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 +570 -45
- 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 +341 -15
- 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-posttooluse.js +1 -1
- package/dist/scripts/notify-hook/team-worker-posttooluse.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/notify-hook.js +1 -1
- package/dist/scripts/notify-hook.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.d.ts +1 -0
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +10 -4
- 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 +76 -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 +279 -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__/model-contract.test.js +40 -9
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/repo-aware-decomposition.test.js +41 -0
- package/dist/team/__tests__/repo-aware-decomposition.test.js.map +1 -1
- 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-cli.test.js +24 -0
- package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +563 -72
- 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__/state.test.js +13 -0
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/team-identity.test.d.ts +2 -0
- package/dist/team/__tests__/team-identity.test.d.ts.map +1 -0
- package/dist/team/__tests__/team-identity.test.js +166 -0
- package/dist/team/__tests__/team-identity.test.js.map +1 -0
- package/dist/team/__tests__/tmux-session.test.js +58 -1
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +62 -0
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/api-interop.d.ts +1 -0
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +163 -132
- 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 +1 -1
- 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/repo-aware-decomposition.d.ts +3 -0
- package/dist/team/repo-aware-decomposition.d.ts.map +1 -1
- package/dist/team/repo-aware-decomposition.js +2 -0
- package/dist/team/repo-aware-decomposition.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-cli.d.ts +32 -2
- package/dist/team/runtime-cli.d.ts.map +1 -1
- package/dist/team/runtime-cli.js +78 -26
- package/dist/team/runtime-cli.js.map +1 -1
- package/dist/team/runtime.d.ts +7 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +383 -40
- 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/state.d.ts +9 -0
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +21 -0
- package/dist/team/state.js.map +1 -1
- package/dist/team/team-identity.d.ts +26 -0
- package/dist/team/team-identity.d.ts.map +1 -0
- package/dist/team/team-identity.js +169 -0
- package/dist/team/team-identity.js.map +1 -0
- package/dist/team/tmux-session.d.ts +18 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +65 -3
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +4 -0
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +28 -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/ai-slop-cleaner/SKILL.md +30 -5
- 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/ai-slop-cleaner/SKILL.md +30 -5
- 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 +2923 -1030
- 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 +696 -46
- package/src/scripts/codex-native-pre-post.ts +369 -16
- 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-posttooluse.ts +1 -1
- package/src/scripts/notify-hook/team-worker-stop.ts +246 -0
- package/src/scripts/notify-hook/team-worker.ts +23 -14
- package/src/scripts/notify-hook.ts +1 -1
- package/src/scripts/run-test-files.ts +20 -1
- package/src/scripts/sync-plugin-mirror.ts +13 -4
- 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";
|
|
@@ -107,9 +113,10 @@ export interface NativeHookDispatchResult {
|
|
|
107
113
|
outputJson: Record<string, unknown> | null;
|
|
108
114
|
}
|
|
109
115
|
|
|
110
|
-
const TERMINAL_MODE_PHASES = new Set(["complete", "failed", "cancelled"]);
|
|
116
|
+
const TERMINAL_MODE_PHASES = new Set(["complete", "completed", "failed", "cancelled"]);
|
|
111
117
|
const SKILL_STOP_BLOCKERS = new Set(["ralplan"]);
|
|
112
|
-
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"]);
|
|
113
120
|
const NATIVE_STOP_STATE_FILE = "native-stop-state.json";
|
|
114
121
|
const STABLE_FINAL_RECOMMENDATION_PATTERNS = [
|
|
115
122
|
/^\s*(?:launch|release|ship)-?ready\s*:\s*(?:yes|no)\b[^\n\r]*/im,
|
|
@@ -259,6 +266,26 @@ async function nativeSubagentSessionStartBelongsToCanonicalSession(
|
|
|
259
266
|
return summary.allThreadIds.includes(parentThreadId);
|
|
260
267
|
}
|
|
261
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
|
+
|
|
262
289
|
async function recordIgnoredNativeSubagentSessionStart(
|
|
263
290
|
cwd: string,
|
|
264
291
|
canonicalSessionId: string,
|
|
@@ -433,7 +460,7 @@ async function readActiveAutoresearchState(
|
|
|
433
460
|
): Promise<Record<string, unknown> | null> {
|
|
434
461
|
const normalizedSessionId = sessionId?.trim() || undefined;
|
|
435
462
|
if (!normalizedSessionId) return null;
|
|
436
|
-
const state = await
|
|
463
|
+
const state = await readAutoresearchModeStateForActiveDecision(cwd, normalizedSessionId);
|
|
437
464
|
if (state?.active !== true) return null;
|
|
438
465
|
if (!isNonTerminalPhase(state.current_phase ?? state.currentPhase ?? 'executing')) return null;
|
|
439
466
|
return state;
|
|
@@ -444,10 +471,59 @@ interface ActiveRalphStopState {
|
|
|
444
471
|
path: string;
|
|
445
472
|
}
|
|
446
473
|
|
|
474
|
+
interface RalphStopOwnershipContext {
|
|
475
|
+
sessionId: string;
|
|
476
|
+
payloadSessionId: string;
|
|
477
|
+
threadId: string;
|
|
478
|
+
currentNativeSessionId: string;
|
|
479
|
+
tmuxPaneId: string;
|
|
480
|
+
}
|
|
481
|
+
|
|
447
482
|
function isRalphStartingPhase(state: Record<string, unknown>): boolean {
|
|
448
483
|
return safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase() === "starting";
|
|
449
484
|
}
|
|
450
485
|
|
|
486
|
+
function hasValue(values: string[], value: string): boolean {
|
|
487
|
+
return value !== "" && values.some((candidate) => candidate === value);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function activeRalphStateMatchesStopOwner(
|
|
491
|
+
state: Record<string, unknown>,
|
|
492
|
+
context: RalphStopOwnershipContext,
|
|
493
|
+
): boolean {
|
|
494
|
+
const ownerOmxSessionId = safeString(state.owner_omx_session_id).trim();
|
|
495
|
+
if (ownerOmxSessionId && ownerOmxSessionId !== context.sessionId) {
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
const stateSessionId = safeString(state.session_id).trim();
|
|
500
|
+
if (!ownerOmxSessionId && stateSessionId && stateSessionId !== context.sessionId) {
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const codexOwnerSessionId = safeString(state.owner_codex_session_id).trim();
|
|
505
|
+
if (codexOwnerSessionId) {
|
|
506
|
+
const stopCodexSessionIds = [
|
|
507
|
+
context.payloadSessionId,
|
|
508
|
+
context.currentNativeSessionId,
|
|
509
|
+
context.sessionId,
|
|
510
|
+
].filter(Boolean);
|
|
511
|
+
if (!hasValue(stopCodexSessionIds, codexOwnerSessionId)) return false;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const stateThreadId = safeString(state.owner_codex_thread_id ?? state.thread_id).trim();
|
|
515
|
+
if (stateThreadId && context.threadId && stateThreadId !== context.threadId) {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const statePaneId = safeString(state.tmux_pane_id).trim();
|
|
520
|
+
if (statePaneId && context.tmuxPaneId && statePaneId !== context.tmuxPaneId) {
|
|
521
|
+
return false;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
return true;
|
|
525
|
+
}
|
|
526
|
+
|
|
451
527
|
function shouldHonorCanonicalTerminalRunState(
|
|
452
528
|
runState: Record<string, unknown> | null,
|
|
453
529
|
mode: string,
|
|
@@ -478,9 +554,27 @@ async function isVisibleRalphActiveForSession(cwd: string, sessionId: string): P
|
|
|
478
554
|
));
|
|
479
555
|
}
|
|
480
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
|
+
|
|
481
570
|
async function readActiveRalphState(
|
|
482
571
|
stateDir: string,
|
|
483
572
|
preferredSessionId?: string,
|
|
573
|
+
ownerContext?: {
|
|
574
|
+
payloadSessionId?: string;
|
|
575
|
+
threadId?: string;
|
|
576
|
+
tmuxPaneId?: string;
|
|
577
|
+
},
|
|
484
578
|
): Promise<ActiveRalphStopState | null> {
|
|
485
579
|
const cwd = resolve(stateDir, "..", "..");
|
|
486
580
|
const [rawSessionInfo, usableSessionInfo] = await Promise.all([
|
|
@@ -488,6 +582,7 @@ async function readActiveRalphState(
|
|
|
488
582
|
readUsableSessionState(cwd),
|
|
489
583
|
]);
|
|
490
584
|
const currentOmxSessionId = safeString(usableSessionInfo?.session_id).trim();
|
|
585
|
+
const currentNativeSessionId = safeString(usableSessionInfo?.native_session_id).trim();
|
|
491
586
|
const staleCurrentSessionId = rawSessionInfo && !isSessionStateUsable(rawSessionInfo, cwd)
|
|
492
587
|
? safeString(rawSessionInfo.session_id).trim()
|
|
493
588
|
: "";
|
|
@@ -515,7 +610,18 @@ async function readActiveRalphState(
|
|
|
515
610
|
) {
|
|
516
611
|
continue;
|
|
517
612
|
}
|
|
518
|
-
if (
|
|
613
|
+
if (
|
|
614
|
+
sessionScoped?.active === true
|
|
615
|
+
&& shouldContinueRun(sessionScoped)
|
|
616
|
+
&& activeRalphStateMatchesStopOwner(sessionScoped, {
|
|
617
|
+
sessionId,
|
|
618
|
+
payloadSessionId: safeString(ownerContext?.payloadSessionId).trim(),
|
|
619
|
+
threadId: safeString(ownerContext?.threadId).trim(),
|
|
620
|
+
currentNativeSessionId,
|
|
621
|
+
tmuxPaneId: safeString(ownerContext?.tmuxPaneId).trim(),
|
|
622
|
+
})
|
|
623
|
+
&& await hasConsistentRalphSkillActivation(cwd, sessionId)
|
|
624
|
+
) {
|
|
519
625
|
return { state: sessionScoped, path: sessionScopedPath };
|
|
520
626
|
}
|
|
521
627
|
}
|
|
@@ -643,6 +749,192 @@ function tryReadGitValue(cwd: string, args: string[]): string | null {
|
|
|
643
749
|
}
|
|
644
750
|
}
|
|
645
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
|
+
}
|
|
646
938
|
|
|
647
939
|
function localExcludeAlreadyIgnoresOmx(cwd: string): boolean {
|
|
648
940
|
const layout = findGitLayout(cwd);
|
|
@@ -1035,6 +1327,9 @@ function buildAdditionalContextMessage(
|
|
|
1035
1327
|
const ultraworkPromptActivationNote = skillState?.initialized_mode === "ultrawork"
|
|
1036
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."
|
|
1037
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;
|
|
1038
1333
|
const combinedTransitionMessage = (() => {
|
|
1039
1334
|
if (!skillState?.transition_message) return null;
|
|
1040
1335
|
if (matches.length <= 1 || activeSkills.length <= 1) return skillState.transition_message;
|
|
@@ -1061,6 +1356,7 @@ function buildAdditionalContextMessage(
|
|
|
1061
1356
|
? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
|
|
1062
1357
|
: null,
|
|
1063
1358
|
promptPriorityMessage,
|
|
1359
|
+
ultragoalPromptActivationNote,
|
|
1064
1360
|
skillState.initialized_mode && skillState.initialized_state_path
|
|
1065
1361
|
? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
|
|
1066
1362
|
: null,
|
|
@@ -1086,6 +1382,7 @@ function buildAdditionalContextMessage(
|
|
|
1086
1382
|
initializedStateMessage,
|
|
1087
1383
|
deepInterviewPromptActivationNote,
|
|
1088
1384
|
ultraworkPromptActivationNote,
|
|
1385
|
+
ultragoalPromptActivationNote,
|
|
1089
1386
|
buildTeamRuntimeInstruction(cwd, payload),
|
|
1090
1387
|
buildTeamHelpInstruction(cwd, payload),
|
|
1091
1388
|
"Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
|
|
@@ -1103,12 +1400,13 @@ function buildAdditionalContextMessage(
|
|
|
1103
1400
|
`skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`,
|
|
1104
1401
|
deepInterviewPromptActivationNote,
|
|
1105
1402
|
ultraworkPromptActivationNote,
|
|
1403
|
+
ultragoalPromptActivationNote,
|
|
1106
1404
|
ralphPromptActivationNote,
|
|
1107
1405
|
"Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
|
|
1108
1406
|
].join(" ");
|
|
1109
1407
|
}
|
|
1110
1408
|
|
|
1111
|
-
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(" ");
|
|
1112
1410
|
}
|
|
1113
1411
|
|
|
1114
1412
|
function parseTeamWorkerEnv(rawValue: string): { teamName: string; workerName: string } | null {
|
|
@@ -1124,23 +1422,79 @@ async function resolveTeamStateDirForWorkerContext(
|
|
|
1124
1422
|
cwd: string,
|
|
1125
1423
|
workerContext: { teamName: string; workerName: string },
|
|
1126
1424
|
): Promise<string | null> {
|
|
1127
|
-
|
|
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;
|
|
1128
1435
|
}
|
|
1129
1436
|
|
|
1130
1437
|
|
|
1131
|
-
|
|
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(
|
|
1132
1457
|
cwd: string,
|
|
1133
|
-
): Promise<
|
|
1134
|
-
const workerContext =
|
|
1135
|
-
|
|
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
|
+
});
|
|
1136
1483
|
|
|
1137
1484
|
const stateDir = await resolveTeamStateDirForWorkerContext(cwd, workerContext);
|
|
1138
|
-
if (!stateDir)
|
|
1485
|
+
if (!stateDir) {
|
|
1486
|
+
return blockWorkerStop("missing_state_dir", "team state root could not be resolved");
|
|
1487
|
+
}
|
|
1139
1488
|
const workerRoot = join(stateDir, "team", workerContext.teamName, "workers", workerContext.workerName);
|
|
1140
1489
|
const [identity, status] = await Promise.all([
|
|
1141
1490
|
readJsonIfExists(join(workerRoot, "identity.json")),
|
|
1142
1491
|
readJsonIfExists(join(workerRoot, "status.json")),
|
|
1143
1492
|
]);
|
|
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
|
+
}
|
|
1144
1498
|
|
|
1145
1499
|
const candidateTaskIds = new Set<string>();
|
|
1146
1500
|
const currentTaskId = safeString(status?.current_task_id).trim();
|
|
@@ -1151,27 +1505,66 @@ async function buildTeamWorkerStopOutput(
|
|
|
1151
1505
|
if (normalized) candidateTaskIds.add(normalized);
|
|
1152
1506
|
}
|
|
1153
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;
|
|
1154
1528
|
for (const taskId of candidateTaskIds) {
|
|
1155
1529
|
const task = await readJsonIfExists(
|
|
1156
1530
|
join(stateDir, "team", workerContext.teamName, "tasks", `task-${taskId}.json`),
|
|
1157
1531
|
);
|
|
1158
1532
|
const statusValue = safeString(task?.status).trim().toLowerCase();
|
|
1159
|
-
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
|
+
}
|
|
1160
1547
|
return {
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
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
|
+
},
|
|
1167
1560
|
};
|
|
1168
1561
|
}
|
|
1169
1562
|
|
|
1170
|
-
|
|
1171
|
-
}
|
|
1563
|
+
if (completedTaskCount === candidateTaskIds.size) {
|
|
1564
|
+
return { kind: "allowed", stateDir, workerContext };
|
|
1565
|
+
}
|
|
1172
1566
|
|
|
1173
|
-
|
|
1174
|
-
return parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_WORKER)) !== null;
|
|
1567
|
+
return blockWorkerStop("missing_completed_task_evidence", "no referenced worker task is completed", stateDir);
|
|
1175
1568
|
}
|
|
1176
1569
|
|
|
1177
1570
|
function isStopExempt(payload: CodexHookPayload): boolean {
|
|
@@ -1198,9 +1591,7 @@ async function buildModeBasedStopOutput(
|
|
|
1198
1591
|
cwd: string,
|
|
1199
1592
|
sessionId?: string,
|
|
1200
1593
|
): Promise<Record<string, unknown> | null> {
|
|
1201
|
-
const state = sessionId
|
|
1202
|
-
? await readModeStateForSession(mode, sessionId, cwd)
|
|
1203
|
-
: await readModeState(mode, cwd);
|
|
1594
|
+
const state = await readModeStateForActiveDecision(mode, sessionId?.trim() || undefined, cwd);
|
|
1204
1595
|
if (!state || !shouldContinueRun(state)) return null;
|
|
1205
1596
|
const phase = formatPhase(state.current_phase);
|
|
1206
1597
|
return {
|
|
@@ -1211,6 +1602,81 @@ async function buildModeBasedStopOutput(
|
|
|
1211
1602
|
};
|
|
1212
1603
|
}
|
|
1213
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
|
+
|
|
1214
1680
|
async function readTeamModeStateForStop(
|
|
1215
1681
|
cwd: string,
|
|
1216
1682
|
sessionId?: string,
|
|
@@ -1366,12 +1832,42 @@ function matchesSkillStopContext(
|
|
|
1366
1832
|
return true;
|
|
1367
1833
|
}
|
|
1368
1834
|
|
|
1835
|
+
function modeStateMatchesSkillStopContext(
|
|
1836
|
+
state: Record<string, unknown>,
|
|
1837
|
+
cwd: string,
|
|
1838
|
+
sessionId: string,
|
|
1839
|
+
): boolean {
|
|
1840
|
+
const stateSessionId = safeString(
|
|
1841
|
+
state.owner_omx_session_id
|
|
1842
|
+
?? state.session_id
|
|
1843
|
+
?? state.codex_session_id
|
|
1844
|
+
?? state.owner_codex_session_id,
|
|
1845
|
+
).trim();
|
|
1846
|
+
if (sessionId && stateSessionId && stateSessionId !== sessionId) return false;
|
|
1847
|
+
|
|
1848
|
+
const stateCwd = safeString(
|
|
1849
|
+
state.cwd
|
|
1850
|
+
?? state.workingDirectory
|
|
1851
|
+
?? state.working_directory
|
|
1852
|
+
?? state.project_path,
|
|
1853
|
+
).trim();
|
|
1854
|
+
if (stateCwd) {
|
|
1855
|
+
try {
|
|
1856
|
+
if (resolve(stateCwd) !== resolve(cwd)) return false;
|
|
1857
|
+
} catch {
|
|
1858
|
+
return false;
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
return true;
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1369
1865
|
async function readBlockingSkillForStop(
|
|
1370
1866
|
cwd: string,
|
|
1371
1867
|
sessionId: string,
|
|
1372
1868
|
threadId: string,
|
|
1373
1869
|
requiredSkill?: string,
|
|
1374
|
-
): Promise<{ skill: string; phase: string } | null> {
|
|
1870
|
+
): Promise<{ skill: string; phase: string; latestPlanPath?: string; planningComplete?: boolean; runOutcome?: string } | null> {
|
|
1375
1871
|
const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
|
|
1376
1872
|
const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
|
|
1377
1873
|
const candidateSkills = requiredSkill
|
|
@@ -1379,8 +1875,15 @@ async function readBlockingSkillForStop(
|
|
|
1379
1875
|
: [...SKILL_STOP_BLOCKERS];
|
|
1380
1876
|
|
|
1381
1877
|
for (const skill of candidateSkills) {
|
|
1878
|
+
const terminalRunState = await readCanonicalTerminalRunStateForStop(cwd, sessionId, skill);
|
|
1879
|
+
if (terminalRunState) continue;
|
|
1880
|
+
|
|
1382
1881
|
const modeState = await readStopSessionPinnedState(`${skill}-state.json`, cwd, sessionId);
|
|
1383
1882
|
if (!modeState || modeState.active !== true) continue;
|
|
1883
|
+
if (!modeStateMatchesSkillStopContext(modeState, cwd, sessionId)) continue;
|
|
1884
|
+
|
|
1885
|
+
const modeSnapshot = getRunContinuationSnapshot(modeState);
|
|
1886
|
+
if (modeSnapshot?.terminal === true) continue;
|
|
1384
1887
|
|
|
1385
1888
|
const phase = formatPhase(
|
|
1386
1889
|
modeState.current_phase,
|
|
@@ -1394,7 +1897,13 @@ async function readBlockingSkillForStop(
|
|
|
1394
1897
|
}
|
|
1395
1898
|
|
|
1396
1899
|
if (!canonicalState) {
|
|
1397
|
-
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
|
+
};
|
|
1398
1907
|
}
|
|
1399
1908
|
|
|
1400
1909
|
const blocker = visibleEntries.find((entry) => (
|
|
@@ -1406,12 +1915,65 @@ async function readBlockingSkillForStop(
|
|
|
1406
1915
|
return {
|
|
1407
1916
|
skill,
|
|
1408
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,
|
|
1409
1921
|
};
|
|
1410
1922
|
}
|
|
1411
1923
|
|
|
1412
1924
|
return null;
|
|
1413
1925
|
}
|
|
1414
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
|
+
|
|
1415
1977
|
async function readStopAutoNudgePhase(
|
|
1416
1978
|
cwd: string,
|
|
1417
1979
|
sessionId: string,
|
|
@@ -1506,7 +2068,12 @@ function resolveRepeatableStopSessionId(
|
|
|
1506
2068
|
payload: CodexHookPayload,
|
|
1507
2069
|
canonicalSessionId?: string,
|
|
1508
2070
|
): string {
|
|
1509
|
-
|
|
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";
|
|
1510
2077
|
}
|
|
1511
2078
|
|
|
1512
2079
|
function buildRepeatableStopSignature(
|
|
@@ -1517,8 +2084,11 @@ function buildRepeatableStopSignature(
|
|
|
1517
2084
|
): string {
|
|
1518
2085
|
const sessionId = resolveRepeatableStopSessionId(payload, canonicalSessionId) || "no-session";
|
|
1519
2086
|
const threadId = readPayloadThreadId(payload) || "no-thread";
|
|
1520
|
-
const turnId = readPayloadTurnId(payload);
|
|
1521
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);
|
|
1522
2092
|
const transcriptPath = safeString(payload.transcript_path ?? payload.transcriptPath).trim() || "no-transcript";
|
|
1523
2093
|
const lastAssistantMessage = normalizeAutoNudgeSignatureText(
|
|
1524
2094
|
payload.last_assistant_message ?? payload.lastAssistantMessage,
|
|
@@ -1734,7 +2304,19 @@ async function buildSkillStopOutput(
|
|
|
1734
2304
|
if (!blocker) return null;
|
|
1735
2305
|
|
|
1736
2306
|
const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
|
|
1737
|
-
|
|
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) {
|
|
1738
2320
|
return null;
|
|
1739
2321
|
}
|
|
1740
2322
|
|
|
@@ -1855,6 +2437,7 @@ async function buildStopHookOutput(
|
|
|
1855
2437
|
payload: CodexHookPayload,
|
|
1856
2438
|
cwd: string,
|
|
1857
2439
|
stateDir: string,
|
|
2440
|
+
options: { skipRalphStopBlock?: boolean } = {},
|
|
1858
2441
|
): Promise<Record<string, unknown> | null> {
|
|
1859
2442
|
if (isStopExempt(payload)) {
|
|
1860
2443
|
return null;
|
|
@@ -1865,7 +2448,13 @@ async function buildStopHookOutput(
|
|
|
1865
2448
|
const threadId = readPayloadThreadId(payload);
|
|
1866
2449
|
const execFollowupOutput = await buildExecFollowupStopOutput(cwd, canonicalSessionId);
|
|
1867
2450
|
if (execFollowupOutput) return execFollowupOutput;
|
|
1868
|
-
const ralphState =
|
|
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
|
+
});
|
|
1869
2458
|
if (!ralphState) {
|
|
1870
2459
|
const autoresearchState = await readActiveAutoresearchState(cwd, canonicalSessionId);
|
|
1871
2460
|
if (autoresearchState) {
|
|
@@ -1889,18 +2478,30 @@ async function buildStopHookOutput(
|
|
|
1889
2478
|
}
|
|
1890
2479
|
}
|
|
1891
2480
|
|
|
1892
|
-
const
|
|
1893
|
-
if (
|
|
2481
|
+
const teamWorkerDecision = await resolveTeamWorkerStopDecision(cwd);
|
|
2482
|
+
if (teamWorkerDecision.kind === "blocked") {
|
|
1894
2483
|
return await returnPersistentStopBlock(
|
|
1895
2484
|
payload,
|
|
1896
2485
|
stateDir,
|
|
1897
2486
|
"team-worker-stop",
|
|
1898
|
-
safeString(
|
|
1899
|
-
|
|
2487
|
+
safeString(teamWorkerDecision.output.stopReason),
|
|
2488
|
+
teamWorkerDecision.output,
|
|
1900
2489
|
canonicalSessionId,
|
|
1901
|
-
{ allowRepeatDuringStopHook:
|
|
2490
|
+
{ allowRepeatDuringStopHook: teamWorkerDecision.allowRepeatDuringStopHook },
|
|
1902
2491
|
);
|
|
1903
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
|
+
}
|
|
1904
2505
|
|
|
1905
2506
|
const autopilotOutput = await buildModeBasedStopOutput("autopilot", cwd, canonicalSessionId);
|
|
1906
2507
|
if (autopilotOutput) {
|
|
@@ -2013,6 +2614,18 @@ async function buildStopHookOutput(
|
|
|
2013
2614
|
const lastAssistantMessage = safeString(
|
|
2014
2615
|
payload.last_assistant_message ?? payload.lastAssistantMessage,
|
|
2015
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
|
+
}
|
|
2016
2629
|
const autoNudgeConfig = await loadAutoNudgeConfig();
|
|
2017
2630
|
const autoNudgePhase = await readStopAutoNudgePhase(cwd, canonicalSessionId, threadId);
|
|
2018
2631
|
|
|
@@ -2037,6 +2650,20 @@ async function buildStopHookOutput(
|
|
|
2037
2650
|
);
|
|
2038
2651
|
}
|
|
2039
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
|
+
|
|
2040
2667
|
if (isFinalHandoffDocumentRefreshCandidate(lastAssistantMessage)) {
|
|
2041
2668
|
const documentRefreshWarning = evaluateFinalHandoffDocumentRefresh(cwd, lastAssistantMessage);
|
|
2042
2669
|
if (documentRefreshWarning) {
|
|
@@ -2092,6 +2719,7 @@ export async function dispatchCodexNativeHook(
|
|
|
2092
2719
|
const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
|
|
2093
2720
|
let skillState: SkillActiveState | null = null;
|
|
2094
2721
|
let triageAdditionalContext: string | null = null;
|
|
2722
|
+
let goalWorkflowAdditionalContext: string | null = null;
|
|
2095
2723
|
|
|
2096
2724
|
const nativeSessionId = safeString(payload.session_id ?? payload.sessionId).trim();
|
|
2097
2725
|
const threadId = safeString(payload.thread_id ?? payload.threadId).trim();
|
|
@@ -2144,9 +2772,10 @@ export async function dispatchCodexNativeHook(
|
|
|
2144
2772
|
}
|
|
2145
2773
|
|
|
2146
2774
|
if (hookEventName === "Stop") {
|
|
2775
|
+
const inheritedSessionId = safeString(process.env.OMX_SESSION_ID || process.env.CODEX_SESSION_ID).trim();
|
|
2147
2776
|
const stopCanonicalSessionId = await resolveInternalSessionIdForPayload(
|
|
2148
2777
|
cwd,
|
|
2149
|
-
readPayloadSessionId(payload),
|
|
2778
|
+
readPayloadSessionId(payload) || inheritedSessionId,
|
|
2150
2779
|
);
|
|
2151
2780
|
if (stopCanonicalSessionId) {
|
|
2152
2781
|
canonicalSessionId = stopCanonicalSessionId;
|
|
@@ -2160,10 +2789,23 @@ export async function dispatchCodexNativeHook(
|
|
|
2160
2789
|
const eventSessionId = canonicalSessionId || nativeSessionId || undefined;
|
|
2161
2790
|
const sessionIdForState = canonicalSessionId || nativeSessionId;
|
|
2162
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;
|
|
2163
2804
|
|
|
2164
2805
|
if (hookEventName === "UserPromptSubmit") {
|
|
2165
2806
|
const prompt = readPromptText(payload);
|
|
2166
|
-
|
|
2807
|
+
goalWorkflowAdditionalContext = await buildGoalWorkflowReconciliationPromptWarning(cwd, prompt).catch(() => null);
|
|
2808
|
+
if (prompt && !isSubagentPromptSubmit) {
|
|
2167
2809
|
skillState = buildNativeOutsideTmuxTeamPromptBlockState(
|
|
2168
2810
|
prompt,
|
|
2169
2811
|
cwd,
|
|
@@ -2180,7 +2822,7 @@ export async function dispatchCodexNativeHook(
|
|
|
2180
2822
|
});
|
|
2181
2823
|
}
|
|
2182
2824
|
// --- Triage classifier (advisory-only, non-keyword prompts) ---
|
|
2183
|
-
if (prompt && skillState === null) {
|
|
2825
|
+
if (prompt && skillState === null && !isSubagentPromptSubmit) {
|
|
2184
2826
|
try {
|
|
2185
2827
|
if (readTriageConfig().enabled) {
|
|
2186
2828
|
const normalized = prompt.trim().toLowerCase();
|
|
@@ -2272,7 +2914,11 @@ export async function dispatchCodexNativeHook(
|
|
|
2272
2914
|
mode: safeString(payload.mode).trim() || undefined,
|
|
2273
2915
|
},
|
|
2274
2916
|
);
|
|
2275
|
-
await
|
|
2917
|
+
await dispatchHookEventRuntime({
|
|
2918
|
+
event,
|
|
2919
|
+
cwd,
|
|
2920
|
+
allowTeamWorkerSideEffects: false,
|
|
2921
|
+
});
|
|
2276
2922
|
}
|
|
2277
2923
|
|
|
2278
2924
|
if ((hookEventName === "SessionStart" && !skipCanonicalSessionStartContext) || hookEventName === "UserPromptSubmit") {
|
|
@@ -2283,7 +2929,9 @@ export async function dispatchCodexNativeHook(
|
|
|
2283
2929
|
canonicalSessionId,
|
|
2284
2930
|
nativeSessionId: resolvedNativeSessionId || nativeSessionId,
|
|
2285
2931
|
})
|
|
2286
|
-
:
|
|
2932
|
+
: isSubagentPromptSubmit
|
|
2933
|
+
? null
|
|
2934
|
+
: (buildAdditionalContextMessage(readPromptText(payload), skillState, cwd, payload) ?? goalWorkflowAdditionalContext ?? triageAdditionalContext);
|
|
2287
2935
|
if (additionalContext) {
|
|
2288
2936
|
outputJson = {
|
|
2289
2937
|
hookSpecificOutput: {
|
|
@@ -2301,7 +2949,9 @@ export async function dispatchCodexNativeHook(
|
|
|
2301
2949
|
outputJson = buildNativePostToolUseOutput(payload);
|
|
2302
2950
|
await handleTeamWorkerPostToolUseSuccess(payload, cwd);
|
|
2303
2951
|
} else if (hookEventName === "Stop") {
|
|
2304
|
-
outputJson = await buildStopHookOutput(payload, cwd, stateDir
|
|
2952
|
+
outputJson = await buildStopHookOutput(payload, cwd, stateDir, {
|
|
2953
|
+
skipRalphStopBlock: isSubagentStop,
|
|
2954
|
+
});
|
|
2305
2955
|
}
|
|
2306
2956
|
|
|
2307
2957
|
return {
|