oh-my-codex 0.18.6 → 0.18.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +6 -6
- package/Cargo.toml +1 -1
- package/README.md +59 -10
- package/crates/omx-sparkshell/tests/execution.rs +1 -1
- package/dist/agents/__tests__/definitions.test.js +11 -0
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +56 -6
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/definitions.d.ts +10 -0
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +5 -1
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/native-config.d.ts +5 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +19 -4
- package/dist/agents/native-config.js.map +1 -1
- package/dist/autopilot/__tests__/fsm.test.d.ts +2 -0
- package/dist/autopilot/__tests__/fsm.test.d.ts.map +1 -0
- package/dist/autopilot/__tests__/fsm.test.js +75 -0
- package/dist/autopilot/__tests__/fsm.test.js.map +1 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.d.ts +2 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.d.ts.map +1 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js +79 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -0
- package/dist/autopilot/deep-interview-gate.d.ts +18 -0
- package/dist/autopilot/deep-interview-gate.d.ts.map +1 -0
- package/dist/autopilot/deep-interview-gate.js +256 -0
- package/dist/autopilot/deep-interview-gate.js.map +1 -0
- package/dist/autopilot/fsm.d.ts +13 -0
- package/dist/autopilot/fsm.d.ts.map +1 -0
- package/dist/autopilot/fsm.js +70 -0
- package/dist/autopilot/fsm.js.map +1 -0
- package/dist/autopilot/ralplan-gate.d.ts +17 -0
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -0
- package/dist/autopilot/ralplan-gate.js +61 -0
- package/dist/autopilot/ralplan-gate.js.map +1 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js +512 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +39 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +83 -7
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +175 -6
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/package-bin-contract.test.js +8 -4
- package/dist/cli/__tests__/package-bin-contract.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js +100 -0
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +13 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +14 -0
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +89 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +83 -0
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/state.test.js +21 -0
- package/dist/cli/__tests__/state.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +2 -2
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +110 -2
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +8 -1
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +14 -3
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +298 -50
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/plugin-marketplace.d.ts +14 -2
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +62 -15
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/question.d.ts.map +1 -1
- package/dist/cli/question.js +36 -5
- package/dist/cli/question.js.map +1 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +3 -1
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/setup-preferences.d.ts +2 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -1
- package/dist/cli/setup-preferences.js +4 -0
- package/dist/cli/setup-preferences.js.map +1 -1
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +166 -27
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/state.d.ts.map +1 -1
- package/dist/cli/state.js +8 -1
- package/dist/cli/state.js.map +1 -1
- package/dist/cli/tmux-hook.d.ts.map +1 -1
- package/dist/cli/tmux-hook.js +16 -0
- package/dist/cli/tmux-hook.js.map +1 -1
- package/dist/cli/update.d.ts +2 -0
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +47 -3
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/deep-interview.test.js +7 -6
- package/dist/config/__tests__/deep-interview.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +1 -0
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/deep-interview.d.ts.map +1 -1
- package/dist/config/deep-interview.js +14 -4
- package/dist/config/deep-interview.js.map +1 -1
- package/dist/config/generator.d.ts +2 -2
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +2 -2
- package/dist/config/generator.js.map +1 -1
- package/dist/config/team-mode.d.ts +12 -0
- package/dist/config/team-mode.d.ts.map +1 -0
- package/dist/config/team-mode.js +91 -0
- package/dist/config/team-mode.js.map +1 -0
- package/dist/hooks/__tests__/agents-overlay.test.js +88 -0
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +8 -0
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.js +8 -0
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js +10 -0
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +1072 -14
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +64 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +189 -0
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +35 -2
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +3 -3
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/session.test.js +25 -0
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/__tests__/skill-guidance-contract.test.js +21 -0
- package/dist/hooks/__tests__/skill-guidance-contract.test.js.map +1 -1
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +36 -50
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/deep-interview-config-instruction.js +1 -1
- package/dist/hooks/deep-interview-config-instruction.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js +31 -0
- package/dist/hooks/extensibility/__tests__/plugin-runner.test.js.map +1 -1
- package/dist/hooks/extensibility/plugin-runner.js +17 -21
- package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +1 -0
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +428 -32
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/keyword-registry.d.ts.map +1 -1
- package/dist/hooks/keyword-registry.js +1 -0
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +6 -0
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hooks/session.d.ts +3 -0
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +13 -5
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/authority.test.js +469 -31
- package/dist/hud/__tests__/authority.test.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +2 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/index.test.js +210 -2
- package/dist/hud/__tests__/index.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +588 -28
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/render.test.js +61 -0
- package/dist/hud/__tests__/render.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +208 -0
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +314 -22
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/authority.d.ts +5 -0
- package/dist/hud/authority.d.ts.map +1 -1
- package/dist/hud/authority.js +337 -30
- package/dist/hud/authority.js.map +1 -1
- package/dist/hud/index.d.ts +20 -2
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +103 -26
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/reconcile.d.ts +3 -3
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +129 -20
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +35 -0
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +64 -50
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/tmux.d.ts +26 -6
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +173 -38
- package/dist/hud/tmux.js.map +1 -1
- package/dist/hud/types.d.ts +11 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/mcp/__tests__/hermes-bridge.test.js +203 -7
- package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +71 -1
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +13 -1
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/hermes-bridge.d.ts +12 -2
- package/dist/mcp/hermes-bridge.d.ts.map +1 -1
- package/dist/mcp/hermes-bridge.js +83 -9
- package/dist/mcp/hermes-bridge.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +32 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +113 -17
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts +4 -4
- package/dist/modes/__tests__/base-autoresearch-contract.test.js +7 -1
- package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +130 -0
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/orchestrator.js +1 -1
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/stages/ralplan.d.ts +1 -0
- package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
- package/dist/pipeline/stages/ralplan.js +14 -5
- package/dist/pipeline/stages/ralplan.js.map +1 -1
- package/dist/question/__tests__/deep-interview.test.js +160 -2
- package/dist/question/__tests__/deep-interview.test.js.map +1 -1
- package/dist/question/__tests__/policy.test.js +63 -3
- package/dist/question/__tests__/policy.test.js.map +1 -1
- package/dist/question/__tests__/renderer.test.js +191 -2
- package/dist/question/__tests__/renderer.test.js.map +1 -1
- package/dist/question/__tests__/state.test.js +94 -3
- package/dist/question/__tests__/state.test.js.map +1 -1
- package/dist/question/__tests__/ui.test.js +4 -0
- package/dist/question/__tests__/ui.test.js.map +1 -1
- package/dist/question/autopilot-wait.d.ts +12 -2
- package/dist/question/autopilot-wait.d.ts.map +1 -1
- package/dist/question/autopilot-wait.js +158 -47
- package/dist/question/autopilot-wait.js.map +1 -1
- package/dist/question/deep-interview.d.ts.map +1 -1
- package/dist/question/deep-interview.js +22 -6
- package/dist/question/deep-interview.js.map +1 -1
- package/dist/question/policy.d.ts.map +1 -1
- package/dist/question/policy.js +2 -5
- package/dist/question/policy.js.map +1 -1
- package/dist/question/renderer.d.ts +12 -0
- package/dist/question/renderer.d.ts.map +1 -1
- package/dist/question/renderer.js +87 -3
- package/dist/question/renderer.js.map +1 -1
- package/dist/question/state.d.ts +8 -1
- package/dist/question/state.d.ts.map +1 -1
- package/dist/question/state.js +54 -14
- package/dist/question/state.js.map +1 -1
- package/dist/question/types.d.ts +1 -1
- package/dist/question/types.d.ts.map +1 -1
- package/dist/question/ui.d.ts +1 -0
- package/dist/question/ui.d.ts.map +1 -1
- package/dist/question/ui.js +1 -0
- package/dist/question/ui.js.map +1 -1
- package/dist/ralplan/__tests__/runtime.test.js +191 -0
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts +9 -1
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +84 -2
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/ralplan/runtime.d.ts +9 -0
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +32 -11
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +2315 -280
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-state-io.test.js +72 -1
- package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts +2 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.js +57 -0
- package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -0
- package/dist/scripts/__tests__/run-test-files.test.js +74 -0
- package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
- package/dist/scripts/__tests__/verify-native-agents.test.js +65 -0
- package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +431 -56
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +79 -1
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/eval/eval-parity-smoke.js +1 -1
- package/dist/scripts/eval/eval-parity-smoke.js.map +1 -1
- package/dist/scripts/hook-payload-guard.d.ts +9 -0
- package/dist/scripts/hook-payload-guard.d.ts.map +1 -0
- package/dist/scripts/hook-payload-guard.js +111 -0
- package/dist/scripts/hook-payload-guard.js.map +1 -0
- package/dist/scripts/notify-fallback-watcher.js +8 -1
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/__tests__/payload-guard.test.d.ts +2 -0
- package/dist/scripts/notify-hook/__tests__/payload-guard.test.d.ts.map +1 -0
- package/dist/scripts/notify-hook/__tests__/payload-guard.test.js +39 -0
- package/dist/scripts/notify-hook/__tests__/payload-guard.test.js.map +1 -0
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +3 -1
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +3 -10
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +62 -38
- package/dist/scripts/notify-hook/state-io.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +7 -0
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.js +234 -86
- package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts +7 -0
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +24 -18
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +86 -13
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/run-test-files.js +193 -22
- package/dist/scripts/run-test-files.js.map +1 -1
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +61 -3
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/scripts/verify-native-agents.d.ts.map +1 -1
- package/dist/scripts/verify-native-agents.js +58 -1
- package/dist/scripts/verify-native-agents.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +1125 -1
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +46 -1
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +98 -7
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +159 -2
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.js +6 -8
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts +6 -0
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +38 -15
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/state/workflow-transition.d.ts.map +1 -1
- package/dist/state/workflow-transition.js +10 -3
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/subagents/__tests__/tracker.test.js +139 -0
- package/dist/subagents/__tests__/tracker.test.js.map +1 -1
- package/dist/subagents/tracker.d.ts +3 -0
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +41 -4
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/coordination-protocol.test.d.ts +2 -0
- package/dist/team/__tests__/coordination-protocol.test.d.ts.map +1 -0
- package/dist/team/__tests__/coordination-protocol.test.js +173 -0
- package/dist/team/__tests__/coordination-protocol.test.js.map +1 -0
- package/dist/team/__tests__/runtime.test.js +52 -3
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +9 -4
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +83 -0
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +240 -2
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +84 -0
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +4 -2
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/coordination-protocol.d.ts +14 -0
- package/dist/team/coordination-protocol.d.ts.map +1 -0
- package/dist/team/coordination-protocol.js +244 -0
- package/dist/team/coordination-protocol.js.map +1 -0
- package/dist/team/runtime.d.ts +1 -0
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +19 -3
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +3 -2
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/tasks.d.ts.map +1 -1
- package/dist/team/state/tasks.js +24 -0
- package/dist/team/state/tasks.js.map +1 -1
- package/dist/team/state/types.d.ts +21 -1
- package/dist/team/state/types.d.ts.map +1 -1
- package/dist/team/state/types.js.map +1 -1
- package/dist/team/state.d.ts +17 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +12 -5
- package/dist/team/state.js.map +1 -1
- package/dist/team/team-ops.d.ts +1 -1
- package/dist/team/team-ops.d.ts.map +1 -1
- package/dist/team/team-ops.js.map +1 -1
- package/dist/team/tmux-session.d.ts +2 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +161 -13
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +63 -0
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/utils/__tests__/agents-model-table.test.js +4 -2
- package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
- package/dist/utils/agents-model-table.d.ts.map +1 -1
- package/dist/utils/agents-model-table.js +3 -0
- package/dist/utils/agents-model-table.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +81 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/package.json +8 -8
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +334 -21
- package/plugins/oh-my-codex/hooks/hooks.json +1 -2
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +13 -6
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +7 -7
- package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +9 -4
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +22 -22
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +12 -0
- package/plugins/oh-my-codex/skills/team/SKILL.md +16 -0
- package/plugins/oh-my-codex/skills/ultraqa/SKILL.md +9 -0
- package/plugins/oh-my-codex/skills/worker/SKILL.md +14 -0
- package/skills/autopilot/SKILL.md +13 -6
- package/skills/code-review/SKILL.md +7 -7
- package/skills/deep-interview/SKILL.md +9 -4
- package/skills/ralph/SKILL.md +22 -22
- package/skills/ralplan/SKILL.md +12 -0
- package/skills/team/SKILL.md +16 -0
- package/skills/ultraqa/SKILL.md +9 -0
- package/skills/worker/SKILL.md +14 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +4435 -2083
- package/src/scripts/__tests__/notify-state-io.test.ts +95 -0
- package/src/scripts/__tests__/notify-tmux-injection.test.ts +82 -0
- package/src/scripts/__tests__/run-test-files.test.ts +102 -0
- package/src/scripts/__tests__/verify-native-agents.test.ts +75 -0
- package/src/scripts/codex-native-hook.ts +536 -51
- package/src/scripts/codex-native-pre-post.ts +80 -0
- package/src/scripts/demo-team-e2e.sh +10 -7
- package/src/scripts/eval/eval-parity-smoke.ts +1 -1
- package/src/scripts/hook-payload-guard.ts +113 -0
- package/src/scripts/notify-fallback-watcher.ts +8 -1
- package/src/scripts/notify-hook/__tests__/payload-guard.test.ts +41 -0
- package/src/scripts/notify-hook/auto-nudge.ts +3 -1
- package/src/scripts/notify-hook/ralph-session-resume.ts +2 -8
- package/src/scripts/notify-hook/state-io.ts +75 -37
- package/src/scripts/notify-hook/team-leader-nudge.ts +7 -0
- package/src/scripts/notify-hook/team-worker-stop.ts +193 -52
- package/src/scripts/notify-hook/tmux-injection.ts +35 -19
- package/src/scripts/notify-hook.ts +105 -6
- package/src/scripts/run-test-files.ts +192 -22
- package/src/scripts/sync-plugin-mirror.ts +98 -9
- package/src/scripts/verify-native-agents.ts +65 -1
|
@@ -6,11 +6,12 @@
|
|
|
6
6
|
* It must not depend on idle/heartbeat freshness or inferred progress stalls.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { existsSync } from 'fs';
|
|
10
|
+
import { appendFile, mkdir, rename, rm, stat, writeFile } from 'fs/promises';
|
|
11
|
+
import { dirname, join, relative } from 'path';
|
|
11
12
|
import { DEFAULT_MARKER, paneHasActiveTask } from '../tmux-hook-engine.js';
|
|
12
13
|
import { appendTeamDeliveryLog } from '../../team/delivery-log.js';
|
|
13
|
-
import { safeString, asNumber } from './utils.js';
|
|
14
|
+
import { safeString, asNumber, isTerminalPhase } from './utils.js';
|
|
14
15
|
import { readJsonIfExists } from './state-io.js';
|
|
15
16
|
import { logTmuxHookEvent } from './log.js';
|
|
16
17
|
import { evaluatePaneInjectionReadiness, queuePaneInput, sendPaneInput } from './team-tmux-guard.js';
|
|
@@ -21,6 +22,110 @@ const STOP_NUDGE_COOLDOWN_MS = 30_000;
|
|
|
21
22
|
const SOURCE_TYPE = 'worker_stop';
|
|
22
23
|
const LEADER_PANE_MISSING_NO_INJECTION_REASON = 'leader_pane_missing_no_injection';
|
|
23
24
|
const LEADER_PANE_SHELL_NO_INJECTION_REASON = 'leader_pane_shell_no_injection';
|
|
25
|
+
const TEAM_SHUTDOWN_NO_INJECTION_REASON = 'team_state_gone_or_shutdown';
|
|
26
|
+
const TEAM_LOCK_HELD_REASON = 'suppressed_team_lock_held';
|
|
27
|
+
|
|
28
|
+
async function teamStateAllowsWorkerStopNudge(stateDir, teamName) {
|
|
29
|
+
const teamDir = join(stateDir, 'team', teamName);
|
|
30
|
+
if (!existsSync(teamDir)) return false;
|
|
31
|
+
if (existsSync(join(teamDir, 'shutdown.json'))) return false;
|
|
32
|
+
|
|
33
|
+
const phase = await readJsonIfExists(join(teamDir, 'phase.json'), null);
|
|
34
|
+
const currentPhase = safeString(phase?.current_phase || phase?.phase || '').trim();
|
|
35
|
+
if (currentPhase && isTerminalPhase(currentPhase)) return false;
|
|
36
|
+
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function acquireTeamStopNudgeLock(teamDir, nowMs, cooldownMs) {
|
|
41
|
+
const lockDir = join(teamDir, 'worker-stop-nudge.lock');
|
|
42
|
+
const staleAfterMs = Math.max(cooldownMs, 30_000);
|
|
43
|
+
|
|
44
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
45
|
+
try {
|
|
46
|
+
await mkdir(lockDir);
|
|
47
|
+
await writeFile(join(lockDir, 'owner.json'), JSON.stringify({
|
|
48
|
+
pid: process.pid,
|
|
49
|
+
acquired_at_ms: nowMs,
|
|
50
|
+
acquired_at: new Date(nowMs).toISOString(),
|
|
51
|
+
}, null, 2)).catch(() => {});
|
|
52
|
+
return { acquired: true, lockDir };
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (error?.code === 'ENOENT') return { acquired: false, reason: TEAM_SHUTDOWN_NO_INJECTION_REASON };
|
|
55
|
+
if (error?.code !== 'EEXIST') return { acquired: false, reason: TEAM_LOCK_HELD_REASON };
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const lockStat = await stat(lockDir);
|
|
59
|
+
if ((nowMs - lockStat.mtimeMs) >= staleAfterMs) {
|
|
60
|
+
await rm(lockDir, { recursive: true, force: true });
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
return { acquired: false, reason: TEAM_LOCK_HELD_REASON };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { acquired: false, reason: TEAM_LOCK_HELD_REASON };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function releaseTeamStopNudgeLock(lockDir) {
|
|
74
|
+
if (!lockDir) return;
|
|
75
|
+
await rm(lockDir, { recursive: true, force: true }).catch(() => {});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function ensureDirectoryUnderExistingTeam(teamDir, targetDir) {
|
|
79
|
+
if (!existsSync(teamDir)) return false;
|
|
80
|
+
const rel = relative(teamDir, targetDir);
|
|
81
|
+
if (!rel) return true;
|
|
82
|
+
if (rel.startsWith('..')) throw new Error('state_path_outside_team_dir');
|
|
83
|
+
|
|
84
|
+
let current = teamDir;
|
|
85
|
+
for (const segment of rel.split(/[\\/]+/).filter(Boolean)) {
|
|
86
|
+
if (!existsSync(teamDir)) return false;
|
|
87
|
+
current = join(current, segment);
|
|
88
|
+
try {
|
|
89
|
+
await mkdir(current);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (error?.code === 'ENOENT' && !existsSync(teamDir)) return false;
|
|
92
|
+
if (error?.code !== 'EEXIST') throw error;
|
|
93
|
+
const currentStat = await stat(current);
|
|
94
|
+
if (!currentStat.isDirectory()) throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return existsSync(teamDir);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function writeStopNudgeStateIfTeamExists(teamDir, statePath, state) {
|
|
101
|
+
const stateDir = dirname(statePath);
|
|
102
|
+
const ready = await ensureDirectoryUnderExistingTeam(teamDir, stateDir);
|
|
103
|
+
if (!ready) return false;
|
|
104
|
+
try {
|
|
105
|
+
const tmpPath = `${statePath}.tmp.${process.pid}`;
|
|
106
|
+
await writeFile(tmpPath, JSON.stringify(state, null, 2));
|
|
107
|
+
await rename(tmpPath, statePath);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (error?.code === 'ENOENT' && !existsSync(teamDir)) return false;
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
return existsSync(teamDir);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function appendWorkerStopEventIfTeamExists(stateDir, teamName, event) {
|
|
116
|
+
const teamDir = join(stateDir, 'team', teamName);
|
|
117
|
+
const eventsDir = join(teamDir, 'events');
|
|
118
|
+
const ready = await ensureDirectoryUnderExistingTeam(teamDir, eventsDir);
|
|
119
|
+
if (!ready) return false;
|
|
120
|
+
const eventsPath = join(eventsDir, 'events.ndjson');
|
|
121
|
+
try {
|
|
122
|
+
await appendFile(eventsPath, JSON.stringify(event) + '\n');
|
|
123
|
+
} catch (error) {
|
|
124
|
+
if (error?.code === 'ENOENT' && !existsSync(teamDir)) return false;
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
return existsSync(teamDir);
|
|
128
|
+
}
|
|
24
129
|
|
|
25
130
|
function resolveWorkerStopCooldownMs() {
|
|
26
131
|
const raw = safeString(process.env.OMX_TEAM_WORKER_STOP_COOLDOWN_MS || '');
|
|
@@ -42,20 +147,6 @@ async function resolveCanonicalLeaderPaneId(leaderPaneId) {
|
|
|
42
147
|
return normalizedLeaderPaneId;
|
|
43
148
|
}
|
|
44
149
|
|
|
45
|
-
async function appendWorkerStopEvent(stateDir, teamName, event) {
|
|
46
|
-
const eventsDir = join(stateDir, 'team', teamName, 'events');
|
|
47
|
-
const eventsPath = join(eventsDir, 'events.ndjson');
|
|
48
|
-
await mkdir(eventsDir, { recursive: true }).catch(() => {});
|
|
49
|
-
await appendFile(eventsPath, JSON.stringify(event) + '\n').catch(() => {});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async function writeStopNudgeState(statePath, state) {
|
|
53
|
-
await mkdir(dirname(statePath), { recursive: true }).catch(() => {});
|
|
54
|
-
const tmpPath = `${statePath}.tmp.${process.pid}`;
|
|
55
|
-
await writeFile(tmpPath, JSON.stringify(state, null, 2));
|
|
56
|
-
await rename(tmpPath, statePath);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
150
|
async function recordDeferred({
|
|
60
151
|
stateDir,
|
|
61
152
|
logsDir,
|
|
@@ -69,23 +160,26 @@ async function recordDeferred({
|
|
|
69
160
|
paneCurrentCommand = '',
|
|
70
161
|
}) {
|
|
71
162
|
const nowIso = nextState.last_notified_at;
|
|
72
|
-
|
|
163
|
+
const teamDir = join(stateDir, 'team', teamName);
|
|
164
|
+
const wroteState = await writeStopNudgeStateIfTeamExists(teamDir, statePath, {
|
|
73
165
|
...nextState,
|
|
74
166
|
delivery: 'deferred',
|
|
75
167
|
reason,
|
|
76
168
|
pane_current_command: paneCurrentCommand || null,
|
|
77
169
|
}).catch(() => {});
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
170
|
+
if (wroteState) {
|
|
171
|
+
await appendWorkerStopEventIfTeamExists(stateDir, teamName, {
|
|
172
|
+
event_id: `worker-stop-deferred-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`,
|
|
173
|
+
team: teamName,
|
|
174
|
+
type: 'worker_stop_leader_nudge',
|
|
175
|
+
worker: workerName,
|
|
176
|
+
to_worker: 'leader-fixed',
|
|
177
|
+
delivery: 'deferred',
|
|
178
|
+
reason,
|
|
179
|
+
created_at: nowIso,
|
|
180
|
+
source_type: SOURCE_TYPE,
|
|
181
|
+
}).catch(() => {});
|
|
182
|
+
}
|
|
89
183
|
await logTmuxHookEvent(logsDir, {
|
|
90
184
|
timestamp: nowIso,
|
|
91
185
|
type: 'leader_notification_deferred',
|
|
@@ -119,30 +213,58 @@ export async function maybeNudgeLeaderForAllowedWorkerStop({
|
|
|
119
213
|
const { teamName, workerName } = workerContext || {};
|
|
120
214
|
if (!teamName || !workerName || !stateDir) return { ok: false, result: 'unresolved' };
|
|
121
215
|
|
|
122
|
-
const
|
|
216
|
+
const teamDir = join(stateDir, 'team', teamName);
|
|
217
|
+
const workerDir = join(teamDir, 'workers', workerName);
|
|
123
218
|
const statePath = join(workerDir, 'worker-stop-nudge.json');
|
|
219
|
+
const teamStatePath = join(teamDir, 'worker-stop-nudge.json');
|
|
124
220
|
const nowMs = Date.now();
|
|
125
221
|
const nowIso = new Date(nowMs).toISOString();
|
|
222
|
+
const cooldownMs = resolveWorkerStopCooldownMs();
|
|
223
|
+
const nextState = {
|
|
224
|
+
last_notified_at_ms: nowMs,
|
|
225
|
+
last_notified_at: nowIso,
|
|
226
|
+
team: teamName,
|
|
227
|
+
worker: workerName,
|
|
228
|
+
source_type: SOURCE_TYPE,
|
|
229
|
+
};
|
|
230
|
+
let tmuxSession = '';
|
|
231
|
+
let leaderPaneId = '';
|
|
232
|
+
|
|
233
|
+
if (!(await teamStateAllowsWorkerStopNudge(stateDir, teamName))) {
|
|
234
|
+
return { ok: true, result: TEAM_SHUTDOWN_NO_INJECTION_REASON };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const lock = await acquireTeamStopNudgeLock(teamDir, nowMs, cooldownMs);
|
|
238
|
+
if (!lock.acquired) {
|
|
239
|
+
return { ok: true, result: lock.reason || TEAM_LOCK_HELD_REASON };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
if (!(await teamStateAllowsWorkerStopNudge(stateDir, teamName))) {
|
|
244
|
+
return { ok: true, result: TEAM_SHUTDOWN_NO_INJECTION_REASON };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const teamExisting = (await readJsonIfExists(teamStatePath, null)) || {};
|
|
248
|
+
const teamLastNotifiedMs = asNumber(teamExisting.last_notified_at_ms) ?? 0;
|
|
249
|
+
if ((nowMs - teamLastNotifiedMs) < cooldownMs) {
|
|
250
|
+
return { ok: true, result: 'suppressed_team_cooldown' };
|
|
251
|
+
}
|
|
252
|
+
|
|
126
253
|
const existing = (await readJsonIfExists(statePath, null)) || {};
|
|
127
254
|
const lastNotifiedMs = asNumber(existing.last_notified_at_ms) ?? 0;
|
|
128
|
-
const cooldownMs = resolveWorkerStopCooldownMs();
|
|
129
255
|
if ((nowMs - lastNotifiedMs) < cooldownMs) {
|
|
130
256
|
return { ok: true, result: 'suppressed_cooldown' };
|
|
131
257
|
}
|
|
132
258
|
|
|
133
259
|
const teamInfo = await readTeamWorkersForIdleCheck(stateDir, teamName);
|
|
134
260
|
if (!teamInfo) return { ok: false, result: 'unresolved' };
|
|
135
|
-
|
|
261
|
+
({ tmuxSession, leaderPaneId } = teamInfo);
|
|
136
262
|
const tmuxTarget = await resolveCanonicalLeaderPaneId(leaderPaneId);
|
|
137
|
-
const nextState = {
|
|
138
|
-
last_notified_at_ms: nowMs,
|
|
139
|
-
last_notified_at: nowIso,
|
|
140
|
-
team: teamName,
|
|
141
|
-
worker: workerName,
|
|
142
|
-
source_type: SOURCE_TYPE,
|
|
143
|
-
};
|
|
144
263
|
|
|
145
264
|
if (!tmuxTarget) {
|
|
265
|
+
if (!(await teamStateAllowsWorkerStopNudge(stateDir, teamName))) {
|
|
266
|
+
return { ok: true, result: TEAM_SHUTDOWN_NO_INJECTION_REASON };
|
|
267
|
+
}
|
|
146
268
|
await recordDeferred({
|
|
147
269
|
stateDir,
|
|
148
270
|
logsDir,
|
|
@@ -164,6 +286,9 @@ export async function maybeNudgeLeaderForAllowedWorkerStop({
|
|
|
164
286
|
requireIdle: false,
|
|
165
287
|
});
|
|
166
288
|
if (!paneGuard.ok) {
|
|
289
|
+
if (!(await teamStateAllowsWorkerStopNudge(stateDir, teamName))) {
|
|
290
|
+
return { ok: true, result: TEAM_SHUTDOWN_NO_INJECTION_REASON };
|
|
291
|
+
}
|
|
167
292
|
await recordDeferred({
|
|
168
293
|
stateDir,
|
|
169
294
|
logsDir,
|
|
@@ -179,12 +304,15 @@ export async function maybeNudgeLeaderForAllowedWorkerStop({
|
|
|
179
304
|
return { ok: true, result: 'deferred' };
|
|
180
305
|
}
|
|
181
306
|
|
|
307
|
+
if (!(await teamStateAllowsWorkerStopNudge(stateDir, teamName))) {
|
|
308
|
+
return { ok: true, result: TEAM_SHUTDOWN_NO_INJECTION_REASON };
|
|
309
|
+
}
|
|
310
|
+
|
|
182
311
|
const prompt =
|
|
183
312
|
`[OMX] ${workerName} native Stop allowed. `
|
|
184
313
|
+ `Run \`omx team status ${teamName}\`, read worker messages/results, then assign next task, reconcile completion, or shut down. `
|
|
185
314
|
+ DEFAULT_MARKER;
|
|
186
315
|
|
|
187
|
-
try {
|
|
188
316
|
const leaderHasActiveTask = paneHasActiveTask(paneGuard.paneCapture);
|
|
189
317
|
let deliveryMode = 'sent';
|
|
190
318
|
if (leaderHasActiveTask) {
|
|
@@ -204,22 +332,28 @@ export async function maybeNudgeLeaderForAllowedWorkerStop({
|
|
|
204
332
|
if (!sendResult.ok) throw new Error(sendResult.error || sendResult.reason || 'send_failed');
|
|
205
333
|
}
|
|
206
334
|
|
|
207
|
-
|
|
335
|
+
const deliveryState = {
|
|
208
336
|
...nextState,
|
|
209
337
|
delivery: deliveryMode,
|
|
210
338
|
leader_pane_id: leaderPaneId || null,
|
|
211
339
|
tmux_target: tmuxTarget,
|
|
212
|
-
}
|
|
213
|
-
await
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
340
|
+
};
|
|
341
|
+
const wroteWorkerState = await writeStopNudgeStateIfTeamExists(teamDir, statePath, deliveryState);
|
|
342
|
+
const wroteTeamState = wroteWorkerState
|
|
343
|
+
? await writeStopNudgeStateIfTeamExists(teamDir, teamStatePath, deliveryState)
|
|
344
|
+
: false;
|
|
345
|
+
if (wroteTeamState) {
|
|
346
|
+
await appendWorkerStopEventIfTeamExists(stateDir, teamName, {
|
|
347
|
+
event_id: `worker-stop-nudge-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`,
|
|
348
|
+
team: teamName,
|
|
349
|
+
type: 'worker_stop_leader_nudge',
|
|
350
|
+
worker: workerName,
|
|
351
|
+
to_worker: 'leader-fixed',
|
|
352
|
+
delivery: deliveryMode,
|
|
353
|
+
created_at: nowIso,
|
|
354
|
+
source_type: SOURCE_TYPE,
|
|
355
|
+
}).catch(() => {});
|
|
356
|
+
}
|
|
223
357
|
await logTmuxHookEvent(logsDir, {
|
|
224
358
|
timestamp: nowIso,
|
|
225
359
|
type: 'worker_stop_leader_nudge',
|
|
@@ -241,6 +375,11 @@ export async function maybeNudgeLeaderForAllowedWorkerStop({
|
|
|
241
375
|
}).catch(() => {});
|
|
242
376
|
return { ok: true, result: deliveryMode };
|
|
243
377
|
} catch (err) {
|
|
378
|
+
if (!(await teamStateAllowsWorkerStopNudge(stateDir, teamName))) {
|
|
379
|
+
return { ok: true, result: TEAM_SHUTDOWN_NO_INJECTION_REASON };
|
|
380
|
+
}
|
|
381
|
+
// Worker Stop is already allowed before this helper runs; nudge failures are
|
|
382
|
+
// surfaced as deferred operational evidence instead of re-blocking Stop.
|
|
244
383
|
await recordDeferred({
|
|
245
384
|
stateDir,
|
|
246
385
|
logsDir,
|
|
@@ -253,5 +392,7 @@ export async function maybeNudgeLeaderForAllowedWorkerStop({
|
|
|
253
392
|
leaderPaneId,
|
|
254
393
|
});
|
|
255
394
|
return { ok: true, result: 'deferred' };
|
|
395
|
+
} finally {
|
|
396
|
+
await releaseTeamStopNudgeLock(lock.lockDir);
|
|
256
397
|
}
|
|
257
398
|
}
|
|
@@ -21,7 +21,7 @@ import { runProcess } from './process-runner.js';
|
|
|
21
21
|
import { logTmuxHookEvent } from './log.js';
|
|
22
22
|
import { resolveInvocationSessionId, resolveManagedCurrentPane, resolveManagedSessionContext, verifyManagedPaneTarget } from './managed-tmux.js';
|
|
23
23
|
import { evaluatePaneInjectionReadiness, mapPaneInjectionReadinessReason, sendPaneInput } from './team-tmux-guard.js';
|
|
24
|
-
import { listActiveSkills,
|
|
24
|
+
import { listActiveSkills, readVisibleSkillActiveStateForStateDir } from '../../state/skill-active.js';
|
|
25
25
|
import {
|
|
26
26
|
normalizeTmuxHookConfig,
|
|
27
27
|
pickActiveMode,
|
|
@@ -99,12 +99,14 @@ async function resolveCanonicalPaneFromPaneTarget(paneTarget: any, expectedCwd:
|
|
|
99
99
|
return finalizeResolvedPane(healedPaneId, 'healed_hud_pane_target', expectedCwd);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
async function resolvePreferredModePane(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
async function resolvePreferredModePane(
|
|
103
|
+
stateDir: string,
|
|
104
|
+
allowedModes: string[],
|
|
105
|
+
options: { includeRootFallback?: boolean } = {},
|
|
106
|
+
): Promise<{ mode: string; state: any; pane: string; stateDir: string } | null> {
|
|
107
|
+
const dirs = await getScopedStateDirsForCurrentSession(stateDir, undefined, {
|
|
108
|
+
includeRootFallback: options.includeRootFallback !== false,
|
|
109
|
+
}).catch(() => [stateDir]);
|
|
108
110
|
for (const dir of dirs) {
|
|
109
111
|
for (const mode of allowedModes || []) {
|
|
110
112
|
const path = join(dir, `${mode}-state.json`);
|
|
@@ -189,12 +191,18 @@ async function validateResolvedInjectionOwnership({
|
|
|
189
191
|
return { ok: true };
|
|
190
192
|
}
|
|
191
193
|
|
|
192
|
-
async function readVisibleAllowedModes(
|
|
194
|
+
export async function readVisibleAllowedModes(
|
|
193
195
|
cwd: string,
|
|
194
196
|
stateDir: string,
|
|
195
197
|
payload: any,
|
|
196
198
|
allowedModes: string[],
|
|
197
|
-
): Promise<{
|
|
199
|
+
): Promise<{
|
|
200
|
+
canonicalPresent: boolean;
|
|
201
|
+
activeSkillCount: number;
|
|
202
|
+
allowedSet: Set<string> | null;
|
|
203
|
+
preferredMode: string | null;
|
|
204
|
+
sessionScoped: boolean;
|
|
205
|
+
}> {
|
|
198
206
|
const candidateSessionIds = [
|
|
199
207
|
await readCurrentSessionId(stateDir).catch(() => undefined),
|
|
200
208
|
resolveInvocationSessionId(payload),
|
|
@@ -203,41 +211,49 @@ async function readVisibleAllowedModes(
|
|
|
203
211
|
.filter(Boolean);
|
|
204
212
|
|
|
205
213
|
for (const sessionId of candidateSessionIds) {
|
|
206
|
-
const canonicalState = await
|
|
214
|
+
const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
|
|
207
215
|
if (!canonicalState) continue;
|
|
208
216
|
|
|
217
|
+
const activeSkills = listActiveSkills(canonicalState);
|
|
209
218
|
const allowedSet = new Set(
|
|
210
|
-
|
|
219
|
+
activeSkills
|
|
211
220
|
.map((entry) => entry.skill)
|
|
212
221
|
.filter((skill) => allowedModes.includes(skill)),
|
|
213
222
|
);
|
|
214
223
|
return {
|
|
215
224
|
canonicalPresent: true,
|
|
225
|
+
activeSkillCount: activeSkills.length,
|
|
216
226
|
allowedSet,
|
|
217
227
|
preferredMode: pickActiveMode([...allowedSet], allowedModes),
|
|
228
|
+
sessionScoped: true,
|
|
218
229
|
};
|
|
219
230
|
}
|
|
220
231
|
|
|
221
232
|
if (candidateSessionIds.length === 0) {
|
|
222
|
-
const rootCanonicalState = await
|
|
233
|
+
const rootCanonicalState = await readVisibleSkillActiveStateForStateDir(stateDir).catch(() => null);
|
|
223
234
|
if (rootCanonicalState) {
|
|
235
|
+
const activeSkills = listActiveSkills(rootCanonicalState);
|
|
224
236
|
const allowedSet = new Set(
|
|
225
|
-
|
|
237
|
+
activeSkills
|
|
226
238
|
.map((entry) => entry.skill)
|
|
227
239
|
.filter((skill) => allowedModes.includes(skill)),
|
|
228
240
|
);
|
|
229
241
|
return {
|
|
230
242
|
canonicalPresent: true,
|
|
243
|
+
activeSkillCount: activeSkills.length,
|
|
231
244
|
allowedSet,
|
|
232
245
|
preferredMode: pickActiveMode([...allowedSet], allowedModes),
|
|
246
|
+
sessionScoped: false,
|
|
233
247
|
};
|
|
234
248
|
}
|
|
235
249
|
}
|
|
236
250
|
|
|
237
251
|
return {
|
|
238
252
|
canonicalPresent: false,
|
|
253
|
+
activeSkillCount: 0,
|
|
239
254
|
allowedSet: null,
|
|
240
255
|
preferredMode: null,
|
|
256
|
+
sessionScoped: candidateSessionIds.length > 0,
|
|
241
257
|
};
|
|
242
258
|
}
|
|
243
259
|
|
|
@@ -415,10 +431,11 @@ export async function handleTmuxInjection({
|
|
|
415
431
|
state.recent_keys = pruneRecentKeys(state.recent_keys, now);
|
|
416
432
|
const canonicalModeState = await readVisibleAllowedModes(cwd, stateDir, payload, config.allowed_modes).catch(() => ({
|
|
417
433
|
canonicalPresent: false,
|
|
434
|
+
activeSkillCount: 0,
|
|
418
435
|
allowedSet: null,
|
|
419
436
|
preferredMode: null,
|
|
420
437
|
}));
|
|
421
|
-
if (canonicalModeState.canonicalPresent && !canonicalModeState.preferredMode) {
|
|
438
|
+
if (canonicalModeState.canonicalPresent && canonicalModeState.activeSkillCount > 0 && !canonicalModeState.preferredMode) {
|
|
422
439
|
const nextState = {
|
|
423
440
|
...state,
|
|
424
441
|
last_reason: 'mode_not_allowed',
|
|
@@ -468,12 +485,10 @@ export async function handleTmuxInjection({
|
|
|
468
485
|
}
|
|
469
486
|
};
|
|
470
487
|
try {
|
|
471
|
-
const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir
|
|
488
|
+
const scopedDirs = await getScopedStateDirsForCurrentSession(stateDir, undefined, {
|
|
489
|
+
includeRootFallback: !canonicalModeState.canonicalPresent && !canonicalModeState.sessionScoped,
|
|
490
|
+
});
|
|
472
491
|
await scanActiveModeStateDirs(scopedDirs);
|
|
473
|
-
|
|
474
|
-
if (!pickActiveMode(activeModes, config.allowed_modes) && !scannedStateDirs.has(resolvePath(stateDir))) {
|
|
475
|
-
await scanActiveModeStateDirs([stateDir], true);
|
|
476
|
-
}
|
|
477
492
|
} catch {
|
|
478
493
|
// Non-fatal
|
|
479
494
|
}
|
|
@@ -483,6 +498,7 @@ export async function handleTmuxInjection({
|
|
|
483
498
|
canonicalModeState.canonicalPresent
|
|
484
499
|
? (canonicalModeState.preferredMode ? [canonicalModeState.preferredMode] : [])
|
|
485
500
|
: config.allowed_modes,
|
|
501
|
+
{ includeRootFallback: !canonicalModeState.sessionScoped },
|
|
486
502
|
).catch(() => null);
|
|
487
503
|
const mode = canonicalModeState.canonicalPresent
|
|
488
504
|
? canonicalModeState.preferredMode
|
|
@@ -68,6 +68,11 @@ import {
|
|
|
68
68
|
} from './notify-hook/team-worker.js';
|
|
69
69
|
import { DEFAULT_MARKER } from './tmux-hook-engine.js';
|
|
70
70
|
import { sameFilePath } from '../utils/paths.js';
|
|
71
|
+
import {
|
|
72
|
+
MAX_NOTIFY_ARGV_JSON_BYTES,
|
|
73
|
+
extractRawJsonStringField,
|
|
74
|
+
utf8ByteLength,
|
|
75
|
+
} from './hook-payload-guard.js';
|
|
71
76
|
|
|
72
77
|
const RALPH_ACTIVE_PROGRESS_PHASES = new Set([
|
|
73
78
|
'start',
|
|
@@ -257,6 +262,78 @@ function classifyIdleNotificationPhase(message: unknown): 'idle' | 'progress' |
|
|
|
257
262
|
return 'idle';
|
|
258
263
|
}
|
|
259
264
|
|
|
265
|
+
|
|
266
|
+
function isExplicitAutopilotActivationText(text: string): boolean {
|
|
267
|
+
return /(?:^|[^\w])\$autopilot\b/i.test(text)
|
|
268
|
+
|| /^\s*\/autopilot\b/i.test(text)
|
|
269
|
+
|| /^\s*(?:please\s+)?autopilot(?:\s+(?:this|mode|workflow|skill|loop|now))?\s*[.!]?\s*$/i.test(text)
|
|
270
|
+
|| /\b(?:use|run|start|enable|launch|invoke|activate|resume|continue)\s+(?:the\s+)?autopilot(?:\s+(?:mode|workflow|skill|loop|now))?\s*[.!]?\s*$/i.test(text)
|
|
271
|
+
|| /\bautopilot\s+(?:mode|workflow|skill|loop)\b/i.test(text);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function looksLikeAutopilotTerminalHandoff(text: string): boolean {
|
|
275
|
+
return /\bAutopilot complete\b/i.test(text)
|
|
276
|
+
|| /\btask_complete\b/i.test(text)
|
|
277
|
+
|| /\bautopilot\b[\s\S]{0,120}\b(?:complete|completed|finished)\b/i.test(text);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function isTerminalModeStateObject(value: unknown, mode: string): boolean {
|
|
281
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
282
|
+
const state = value as Record<string, unknown>;
|
|
283
|
+
if (safeString(state.mode).trim() !== mode) return false;
|
|
284
|
+
if (state.active === true) return false;
|
|
285
|
+
const phase = safeString(state.current_phase || state.currentPhase).trim().toLowerCase().replace(/_/g, '-');
|
|
286
|
+
if (['complete', 'completed', 'failed', 'cancelled', 'canceled', 'stopped', 'user-stopped'].includes(phase)) return true;
|
|
287
|
+
const outcome = safeString(state.run_outcome || state.outcome || state.lifecycle_outcome || state.terminal_outcome).trim().toLowerCase();
|
|
288
|
+
return ['finish', 'finished', 'complete', 'completed', 'failed', 'cancelled', 'canceled'].includes(outcome)
|
|
289
|
+
|| safeString(state.completed_at || state.completedAt).trim() !== '';
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function terminalStateMatchesNotifyTurn(state: Record<string, unknown>, payload: Record<string, unknown>): boolean {
|
|
293
|
+
const payloadTurnId = safeString(payload['turn-id'] || payload.turn_id || '').trim();
|
|
294
|
+
const stateTurnId = safeString(state.turn_id || state.turnId || '').trim();
|
|
295
|
+
const payloadThreadId = safeString(payload['thread-id'] || payload.thread_id || '').trim();
|
|
296
|
+
const stateThreadId = safeString(state.thread_id || state.threadId || '').trim();
|
|
297
|
+
|
|
298
|
+
if (payloadTurnId || stateTurnId) {
|
|
299
|
+
if (!payloadTurnId || !stateTurnId || payloadTurnId !== stateTurnId) return false;
|
|
300
|
+
return !payloadThreadId || !stateThreadId || payloadThreadId === stateThreadId;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return Boolean(payloadThreadId && stateThreadId && payloadThreadId === stateThreadId);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async function hasTerminalAutopilotStateForNotifyTurn(
|
|
307
|
+
stateDir: string,
|
|
308
|
+
sessionId: string,
|
|
309
|
+
payload: Record<string, unknown>,
|
|
310
|
+
): Promise<boolean> {
|
|
311
|
+
const state = await readScopedJsonIfExists(
|
|
312
|
+
stateDir,
|
|
313
|
+
'autopilot-state.json',
|
|
314
|
+
sessionId || undefined,
|
|
315
|
+
null,
|
|
316
|
+
{ includeRootFallback: true },
|
|
317
|
+
);
|
|
318
|
+
return isTerminalModeStateObject(state, 'autopilot')
|
|
319
|
+
&& terminalStateMatchesNotifyTurn(state as Record<string, unknown>, payload);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async function shouldSuppressAutopilotTerminalReplayActivation(
|
|
323
|
+
stateDir: string,
|
|
324
|
+
payload: Record<string, unknown>,
|
|
325
|
+
isAutopilotActivation: boolean,
|
|
326
|
+
sessionId: string,
|
|
327
|
+
): Promise<boolean> {
|
|
328
|
+
if (!isTurnCompletePayload(payload) && !isNotifyFallbackTaskCompletePayload(payload)) return false;
|
|
329
|
+
if (!isAutopilotActivation) return false;
|
|
330
|
+
|
|
331
|
+
const lastAssistantMessage = safeString(payload['last-assistant-message'] || payload.last_assistant_message || '');
|
|
332
|
+
if (!looksLikeAutopilotTerminalHandoff(lastAssistantMessage) && !isNotifyFallbackTaskCompletePayload(payload)) return false;
|
|
333
|
+
|
|
334
|
+
return hasTerminalAutopilotStateForNotifyTurn(stateDir, sessionId, payload);
|
|
335
|
+
}
|
|
336
|
+
|
|
260
337
|
function buildIdleNotificationFingerprint(payload: Record<string, unknown>): string {
|
|
261
338
|
const lastAssistantMessage = safeString(payload['last-assistant-message'] || payload.last_assistant_message || '');
|
|
262
339
|
const summary = summarizeIdleNotificationMessage(lastAssistantMessage);
|
|
@@ -285,6 +362,9 @@ async function main() {
|
|
|
285
362
|
if (!rawPayload || rawPayload.startsWith('-')) {
|
|
286
363
|
process.exit(0);
|
|
287
364
|
}
|
|
365
|
+
if (utf8ByteLength(rawPayload) > MAX_NOTIFY_ARGV_JSON_BYTES) {
|
|
366
|
+
process.exit(0);
|
|
367
|
+
}
|
|
288
368
|
|
|
289
369
|
let payload;
|
|
290
370
|
try {
|
|
@@ -633,16 +713,28 @@ async function main() {
|
|
|
633
713
|
|
|
634
714
|
// 4.45. Skill activation tracking: update skill-active-state.json before any nudge logic.
|
|
635
715
|
try {
|
|
636
|
-
const { recordSkillActivation } = await import('../hooks/keyword-detector.js');
|
|
716
|
+
const { detectKeywords, recordSkillActivation } = await import('../hooks/keyword-detector.js');
|
|
637
717
|
if (latestUserInput) {
|
|
718
|
+
const activationSessionId = getEffectiveSessionId();
|
|
719
|
+
const isAutopilotActivation = detectKeywords(latestUserInput)
|
|
720
|
+
.some((match) => match.skill === 'autopilot')
|
|
721
|
+
|| isExplicitAutopilotActivationText(latestUserInput);
|
|
722
|
+
const suppressTerminalReplay = await shouldSuppressAutopilotTerminalReplayActivation(
|
|
723
|
+
stateDir,
|
|
724
|
+
payload,
|
|
725
|
+
isAutopilotActivation,
|
|
726
|
+
activationSessionId,
|
|
727
|
+
);
|
|
728
|
+
if (!suppressTerminalReplay) {
|
|
638
729
|
await recordSkillActivation({
|
|
639
730
|
stateDir,
|
|
640
731
|
sourceCwd: cwd,
|
|
641
732
|
text: latestUserInput,
|
|
642
|
-
sessionId:
|
|
733
|
+
sessionId: activationSessionId,
|
|
643
734
|
threadId: payloadThreadId,
|
|
644
735
|
turnId: safeString(payload['turn-id'] || payload.turn_id || ''),
|
|
645
736
|
});
|
|
737
|
+
}
|
|
646
738
|
}
|
|
647
739
|
} catch {
|
|
648
740
|
// Non-fatal: keyword detector module may not be built yet
|
|
@@ -654,8 +746,11 @@ async function main() {
|
|
|
654
746
|
// Non-fatal: lifecycle sync should not block the hook
|
|
655
747
|
}
|
|
656
748
|
|
|
657
|
-
const
|
|
658
|
-
const
|
|
749
|
+
const effectiveSessionId = getEffectiveSessionId();
|
|
750
|
+
const deepInterviewStateActive = effectiveSessionId
|
|
751
|
+
? await isDeepInterviewStateActive(stateDir, effectiveSessionId)
|
|
752
|
+
: await isDeepInterviewStateActive(stateDir, undefined);
|
|
753
|
+
const deepInterviewInputLockActive = await isDeepInterviewInputLockActive(stateDir, effectiveSessionId);
|
|
659
754
|
|
|
660
755
|
// 4.55. Notify leader when individual worker transitions to idle (worker session only)
|
|
661
756
|
if (isTeamWorker && parsedTeamWorker && !deepInterviewStateActive) {
|
|
@@ -909,8 +1004,12 @@ async function logFatalNotifyHookError(err: unknown): Promise<void> {
|
|
|
909
1004
|
try {
|
|
910
1005
|
const rawPayload = process.argv[process.argv.length - 1];
|
|
911
1006
|
if (rawPayload && !rawPayload.startsWith('-')) {
|
|
912
|
-
|
|
913
|
-
|
|
1007
|
+
if (utf8ByteLength(rawPayload) <= MAX_NOTIFY_ARGV_JSON_BYTES) {
|
|
1008
|
+
const payload = JSON.parse(rawPayload) as Record<string, unknown>;
|
|
1009
|
+
cwd = safeString(payload.cwd || payload['cwd'] || cwd) || cwd;
|
|
1010
|
+
} else {
|
|
1011
|
+
cwd = extractRawJsonStringField(rawPayload, ['cwd']) || cwd;
|
|
1012
|
+
}
|
|
914
1013
|
}
|
|
915
1014
|
} catch {
|
|
916
1015
|
// Keep notification hook failures silent in Codex TUI surfaces.
|