oh-my-codex 0.12.4 → 0.12.6
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 +5 -5
- package/Cargo.toml +1 -1
- package/README.md +27 -3
- package/dist/cli/__tests__/ask.test.js +26 -0
- package/dist/cli/__tests__/ask.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +28 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +95 -8
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +102 -4
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +169 -0
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +31 -0
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +66 -2
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-refresh.test.js +51 -1
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +148 -3
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +14 -1
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/cleanup.js +1 -1
- package/dist/cli/cleanup.js.map +1 -1
- package/dist/cli/constants.d.ts +1 -0
- package/dist/cli/constants.d.ts.map +1 -1
- package/dist/cli/constants.js +1 -0
- package/dist/cli/constants.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +15 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/explore.d.ts +1 -0
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +49 -1
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +2 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +127 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-parity.d.ts +1 -1
- package/dist/cli/mcp-parity.d.ts.map +1 -1
- package/dist/cli/mcp-parity.js +24 -0
- package/dist/cli/mcp-parity.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +17 -5
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +80 -6
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +1 -0
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +60 -0
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/mcp-registry.test.js +61 -0
- package/dist/config/__tests__/mcp-registry.test.js.map +1 -1
- package/dist/config/__tests__/wiki-config-contract.test.d.ts +2 -0
- package/dist/config/__tests__/wiki-config-contract.test.d.ts.map +1 -0
- package/dist/config/__tests__/wiki-config-contract.test.js +19 -0
- package/dist/config/__tests__/wiki-config-contract.test.js.map +1 -0
- package/dist/config/generator.d.ts +1 -0
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +88 -3
- package/dist/config/generator.js.map +1 -1
- package/dist/config/mcp-registry.d.ts +2 -0
- package/dist/config/mcp-registry.d.ts.map +1 -1
- package/dist/config/mcp-registry.js +12 -0
- package/dist/config/mcp-registry.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +39 -0
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +297 -4
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +392 -22
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +166 -67
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +112 -2
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-modules.test.js +52 -12
- package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-regression-205.test.d.ts +2 -3
- package/dist/hooks/__tests__/notify-hook-regression-205.test.d.ts.map +1 -1
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js +18 -23
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js +33 -0
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +176 -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 +355 -7
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +90 -2
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/session.test.js +142 -2
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/wiki-docs-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/wiki-docs-contract.test.js +34 -0
- package/dist/hooks/__tests__/wiki-docs-contract.test.js.map +1 -0
- package/dist/hooks/agents-overlay.d.ts.map +1 -1
- package/dist/hooks/agents-overlay.js +0 -1
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js +32 -0
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/runtime.test.js +31 -0
- package/dist/hooks/extensibility/__tests__/runtime.test.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/sdk.test.js +33 -3
- package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
- package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
- package/dist/hooks/extensibility/dispatcher.js +41 -0
- package/dist/hooks/extensibility/dispatcher.js.map +1 -1
- package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -1
- package/dist/hooks/extensibility/sdk/runtime-state.js +7 -1
- package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -1
- package/dist/hooks/extensibility/types.d.ts +1 -0
- package/dist/hooks/extensibility/types.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +6 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +207 -10
- 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 +3 -0
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hooks/session.d.ts +14 -2
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +120 -16
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +111 -2
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +18 -21
- package/dist/hud/state.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.js +88 -1
- package/dist/mcp/__tests__/bootstrap.test.js.map +1 -1
- package/dist/mcp/__tests__/server-lifecycle.test.js +3 -0
- package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +30 -2
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +415 -0
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/__tests__/wiki-server.test.d.ts +2 -0
- package/dist/mcp/__tests__/wiki-server.test.d.ts.map +1 -0
- package/dist/mcp/__tests__/wiki-server.test.js +30 -0
- package/dist/mcp/__tests__/wiki-server.test.js.map +1 -0
- package/dist/mcp/bootstrap.d.ts +19 -1
- package/dist/mcp/bootstrap.d.ts.map +1 -1
- package/dist/mcp/bootstrap.js +185 -0
- package/dist/mcp/bootstrap.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +5 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +41 -11
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts +4 -4
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/state-server.js +49 -2
- package/dist/mcp/state-server.js.map +1 -1
- package/dist/mcp/wiki-server.d.ts +181 -0
- package/dist/mcp/wiki-server.d.ts.map +1 -0
- package/dist/mcp/wiki-server.js +235 -0
- package/dist/mcp/wiki-server.js.map +1 -0
- package/dist/modes/__tests__/base-autoresearch-contract.test.js +74 -2
- package/dist/modes/__tests__/base-autoresearch-contract.test.js.map +1 -1
- package/dist/modes/__tests__/base-multi-state-compat.test.d.ts +2 -0
- package/dist/modes/__tests__/base-multi-state-compat.test.d.ts.map +1 -0
- package/dist/modes/__tests__/base-multi-state-compat.test.js +38 -0
- package/dist/modes/__tests__/base-multi-state-compat.test.js.map +1 -0
- package/dist/modes/__tests__/base-tmux-pane.test.js +1 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
- package/dist/modes/base.d.ts +2 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +55 -31
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/__tests__/formatter.test.js +11 -0
- package/dist/notifications/__tests__/formatter.test.js.map +1 -1
- package/dist/notifications/__tests__/idle-cooldown.test.js +32 -1
- package/dist/notifications/__tests__/idle-cooldown.test.js.map +1 -1
- package/dist/notifications/__tests__/index.test.d.ts +2 -0
- package/dist/notifications/__tests__/index.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/index.test.js +113 -0
- package/dist/notifications/__tests__/index.test.js.map +1 -0
- package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts +2 -0
- package/dist/notifications/__tests__/lifecycle-dedupe.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/lifecycle-dedupe.test.js +86 -0
- package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -0
- package/dist/notifications/__tests__/reply-listener.test.js +174 -0
- package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts +2 -0
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +93 -0
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -0
- package/dist/notifications/__tests__/session-registry.test.js +48 -1
- package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
- package/dist/notifications/__tests__/session-status.test.d.ts +2 -0
- package/dist/notifications/__tests__/session-status.test.d.ts.map +1 -0
- package/dist/notifications/__tests__/session-status.test.js +159 -0
- package/dist/notifications/__tests__/session-status.test.js.map +1 -0
- package/dist/notifications/__tests__/tmux.test.js +58 -1
- package/dist/notifications/__tests__/tmux.test.js.map +1 -1
- package/dist/notifications/idle-cooldown.d.ts +11 -0
- package/dist/notifications/idle-cooldown.d.ts.map +1 -1
- package/dist/notifications/idle-cooldown.js +42 -8
- package/dist/notifications/idle-cooldown.js.map +1 -1
- package/dist/notifications/index.d.ts +1 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +41 -8
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/lifecycle-dedupe.d.ts +8 -0
- package/dist/notifications/lifecycle-dedupe.d.ts.map +1 -0
- package/dist/notifications/lifecycle-dedupe.js +112 -0
- package/dist/notifications/lifecycle-dedupe.js.map +1 -0
- package/dist/notifications/reply-listener.d.ts +10 -1
- package/dist/notifications/reply-listener.d.ts.map +1 -1
- package/dist/notifications/reply-listener.js +49 -11
- package/dist/notifications/reply-listener.js.map +1 -1
- package/dist/notifications/session-registry.d.ts.map +1 -1
- package/dist/notifications/session-registry.js +7 -1
- package/dist/notifications/session-registry.js.map +1 -1
- package/dist/notifications/session-status.d.ts +23 -0
- package/dist/notifications/session-status.d.ts.map +1 -0
- package/dist/notifications/session-status.js +187 -0
- package/dist/notifications/session-status.js.map +1 -0
- package/dist/notifications/tmux.d.ts +10 -0
- package/dist/notifications/tmux.d.ts.map +1 -1
- package/dist/notifications/tmux.js +59 -5
- package/dist/notifications/tmux.js.map +1 -1
- package/dist/notifications/types.d.ts +2 -0
- package/dist/notifications/types.d.ts.map +1 -1
- package/dist/openclaw/__tests__/index.test.js +84 -0
- package/dist/openclaw/__tests__/index.test.js.map +1 -1
- package/dist/openclaw/index.d.ts.map +1 -1
- package/dist/openclaw/index.js +7 -14
- package/dist/openclaw/index.js.map +1 -1
- package/dist/openclaw/types.d.ts +2 -2
- package/dist/openclaw/types.d.ts.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +692 -40
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts +2 -0
- package/dist/scripts/__tests__/hook-derived-watcher.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/hook-derived-watcher.test.js +87 -0
- package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +309 -77
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/hook-derived-watcher.js +43 -1
- package/dist/scripts/hook-derived-watcher.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +95 -21
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/active-team.d.ts +9 -0
- package/dist/scripts/notify-hook/active-team.d.ts.map +1 -0
- package/dist/scripts/notify-hook/active-team.js +44 -0
- package/dist/scripts/notify-hook/active-team.js.map +1 -0
- package/dist/scripts/notify-hook/auto-nudge.d.ts +5 -3
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +121 -78
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.js +18 -4
- package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
- package/dist/scripts/notify-hook/operational-events.d.ts.map +1 -1
- package/dist/scripts/notify-hook/operational-events.js +21 -0
- package/dist/scripts/notify-hook/operational-events.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 -2
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts +10 -1
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +56 -12
- package/dist/scripts/notify-hook/state-io.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +305 -167
- package/dist/scripts/notify-hook/team-dispatch.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 +87 -15
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +11 -2
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +26 -16
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/run-provider-advisor.js +20 -2
- package/dist/scripts/run-provider-advisor.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +1 -8
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +12 -68
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +113 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +35 -0
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.d.ts +2 -0
- package/dist/state/__tests__/workflow-transition.test.d.ts.map +1 -0
- package/dist/state/__tests__/workflow-transition.test.js +56 -0
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -0
- package/dist/state/operations.d.ts +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +88 -2
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts +2 -2
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +119 -33
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts +15 -0
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -0
- package/dist/state/workflow-transition-reconcile.js +100 -0
- package/dist/state/workflow-transition-reconcile.js.map +1 -0
- package/dist/state/workflow-transition.d.ts +22 -0
- package/dist/state/workflow-transition.d.ts.map +1 -0
- package/dist/state/workflow-transition.js +188 -0
- package/dist/state/workflow-transition.js.map +1 -0
- package/dist/team/__tests__/api-interop.test.js +90 -0
- package/dist/team/__tests__/api-interop.test.js.map +1 -1
- package/dist/team/__tests__/current-task-baseline.test.d.ts +2 -0
- package/dist/team/__tests__/current-task-baseline.test.d.ts.map +1 -0
- package/dist/team/__tests__/current-task-baseline.test.js +87 -0
- package/dist/team/__tests__/current-task-baseline.test.js.map +1 -0
- package/dist/team/__tests__/hardening-e2e.test.js +17 -0
- package/dist/team/__tests__/hardening-e2e.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +673 -65
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/shutdown-fallback.test.js +11 -1
- package/dist/team/__tests__/shutdown-fallback.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +447 -4
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +10 -1
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/current-task-baseline.d.ts +32 -0
- package/dist/team/current-task-baseline.d.ts.map +1 -0
- package/dist/team/current-task-baseline.js +85 -0
- package/dist/team/current-task-baseline.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.map +1 -1
- package/dist/team/leader-activity.d.ts +1 -0
- package/dist/team/leader-activity.d.ts.map +1 -1
- package/dist/team/leader-activity.js +4 -2
- package/dist/team/leader-activity.js.map +1 -1
- package/dist/team/progress-evidence.d.ts +2 -0
- package/dist/team/progress-evidence.d.ts.map +1 -0
- package/dist/team/progress-evidence.js +77 -0
- package/dist/team/progress-evidence.js.map +1 -0
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +269 -64
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +1 -1
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +2 -13
- package/dist/team/state.js.map +1 -1
- package/dist/team/tmux-session.d.ts +12 -3
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +174 -20
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worktree.d.ts +6 -1
- package/dist/team/worktree.d.ts.map +1 -1
- package/dist/team/worktree.js +28 -4
- package/dist/team/worktree.js.map +1 -1
- package/dist/utils/__tests__/agents-md.test.js +21 -1
- package/dist/utils/__tests__/agents-md.test.js.map +1 -1
- package/dist/utils/__tests__/repo-deps.test.d.ts +2 -0
- package/dist/utils/__tests__/repo-deps.test.d.ts.map +1 -0
- package/dist/utils/__tests__/repo-deps.test.js +71 -0
- package/dist/utils/__tests__/repo-deps.test.js.map +1 -0
- package/dist/utils/agents-md.d.ts +1 -0
- package/dist/utils/agents-md.d.ts.map +1 -1
- package/dist/utils/agents-md.js +7 -3
- package/dist/utils/agents-md.js.map +1 -1
- package/dist/utils/paths.d.ts +4 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +20 -0
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/repo-deps.d.ts +20 -0
- package/dist/utils/repo-deps.d.ts.map +1 -0
- package/dist/utils/repo-deps.js +78 -0
- package/dist/utils/repo-deps.js.map +1 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts +2 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.d.ts.map +1 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +54 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -0
- package/dist/wiki/__tests__/cjk-tokenize.test.d.ts +12 -0
- package/dist/wiki/__tests__/cjk-tokenize.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/cjk-tokenize.test.js +139 -0
- package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -0
- package/dist/wiki/__tests__/crlf-parse.test.d.ts +2 -0
- package/dist/wiki/__tests__/crlf-parse.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/crlf-parse.test.js +24 -0
- package/dist/wiki/__tests__/crlf-parse.test.js.map +1 -0
- package/dist/wiki/__tests__/escape-newline.test.d.ts +2 -0
- package/dist/wiki/__tests__/escape-newline.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/escape-newline.test.js +45 -0
- package/dist/wiki/__tests__/escape-newline.test.js.map +1 -0
- package/dist/wiki/__tests__/ingest.test.d.ts +5 -0
- package/dist/wiki/__tests__/ingest.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/ingest.test.js +181 -0
- package/dist/wiki/__tests__/ingest.test.js.map +1 -0
- package/dist/wiki/__tests__/lint.test.d.ts +5 -0
- package/dist/wiki/__tests__/lint.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/lint.test.js +163 -0
- package/dist/wiki/__tests__/lint.test.js.map +1 -0
- package/dist/wiki/__tests__/query.test.d.ts +5 -0
- package/dist/wiki/__tests__/query.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/query.test.js +141 -0
- package/dist/wiki/__tests__/query.test.js.map +1 -0
- package/dist/wiki/__tests__/reserved-file-guard.test.d.ts +2 -0
- package/dist/wiki/__tests__/reserved-file-guard.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/reserved-file-guard.test.js +44 -0
- package/dist/wiki/__tests__/reserved-file-guard.test.js.map +1 -0
- package/dist/wiki/__tests__/session-hooks.test.d.ts +5 -0
- package/dist/wiki/__tests__/session-hooks.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/session-hooks.test.js +36 -0
- package/dist/wiki/__tests__/session-hooks.test.js.map +1 -0
- package/dist/wiki/__tests__/slug-nonascii.test.d.ts +2 -0
- package/dist/wiki/__tests__/slug-nonascii.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/slug-nonascii.test.js +24 -0
- package/dist/wiki/__tests__/slug-nonascii.test.js.map +1 -0
- package/dist/wiki/__tests__/storage.test.d.ts +5 -0
- package/dist/wiki/__tests__/storage.test.d.ts.map +1 -0
- package/dist/wiki/__tests__/storage.test.js +278 -0
- package/dist/wiki/__tests__/storage.test.js.map +1 -0
- package/dist/wiki/__tests__/test-helpers.d.ts +31 -0
- package/dist/wiki/__tests__/test-helpers.d.ts.map +1 -0
- package/dist/wiki/__tests__/test-helpers.js +108 -0
- package/dist/wiki/__tests__/test-helpers.js.map +1 -0
- package/dist/wiki/index.d.ts +14 -0
- package/dist/wiki/index.d.ts.map +1 -0
- package/dist/wiki/index.js +17 -0
- package/dist/wiki/index.js.map +1 -0
- package/dist/wiki/ingest.d.ts +20 -0
- package/dist/wiki/ingest.d.ts.map +1 -0
- package/dist/wiki/ingest.js +115 -0
- package/dist/wiki/ingest.js.map +1 -0
- package/dist/wiki/lifecycle.d.ts +20 -0
- package/dist/wiki/lifecycle.d.ts.map +1 -0
- package/dist/wiki/lifecycle.js +212 -0
- package/dist/wiki/lifecycle.js.map +1 -0
- package/dist/wiki/lint.d.ts +25 -0
- package/dist/wiki/lint.d.ts.map +1 -0
- package/dist/wiki/lint.js +166 -0
- package/dist/wiki/lint.js.map +1 -0
- package/dist/wiki/query.d.ts +36 -0
- package/dist/wiki/query.d.ts.map +1 -0
- package/dist/wiki/query.js +138 -0
- package/dist/wiki/query.js.map +1 -0
- package/dist/wiki/storage.d.ts +33 -0
- package/dist/wiki/storage.d.ts.map +1 -0
- package/dist/wiki/storage.js +321 -0
- package/dist/wiki/storage.js.map +1 -0
- package/dist/wiki/types.d.ts +83 -0
- package/dist/wiki/types.d.ts.map +1 -0
- package/dist/wiki/types.js +15 -0
- package/dist/wiki/types.js.map +1 -0
- package/package.json +3 -1
- package/skills/configure-notifications/SKILL.md +1 -0
- package/skills/doctor/SKILL.md +11 -0
- package/skills/omx-setup/SKILL.md +1 -1
- package/skills/wiki/SKILL.md +57 -0
- package/src/scripts/__tests__/codex-native-hook.test.ts +920 -56
- package/src/scripts/__tests__/hook-derived-watcher.test.ts +111 -0
- package/src/scripts/codex-native-hook.ts +377 -83
- package/src/scripts/hook-derived-watcher.ts +43 -1
- package/src/scripts/notify-fallback-watcher.ts +99 -20
- package/src/scripts/notify-hook/active-team.ts +54 -0
- package/src/scripts/notify-hook/auto-nudge.ts +132 -79
- package/src/scripts/notify-hook/managed-tmux.ts +22 -4
- package/src/scripts/notify-hook/operational-events.ts +21 -0
- package/src/scripts/notify-hook/ralph-session-resume.ts +3 -2
- package/src/scripts/notify-hook/state-io.ts +89 -12
- package/src/scripts/notify-hook/team-dispatch.ts +326 -168
- package/src/scripts/notify-hook/team-leader-nudge.ts +91 -14
- package/src/scripts/notify-hook/tmux-injection.ts +11 -2
- package/src/scripts/notify-hook.ts +36 -22
- package/src/scripts/run-provider-advisor.ts +20 -2
- package/src/scripts/smoke-packed-install.ts +16 -83
- package/templates/AGENTS.md +3 -4
|
@@ -6,13 +6,41 @@ import { tmpdir } from "node:os";
|
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
8
8
|
import { buildManagedCodexHooksConfig } from "../../config/codex-hooks.js";
|
|
9
|
-
import { initTeamState, readTeamLeaderAttention, readTeamPhase, } from "../../team/state.js";
|
|
9
|
+
import { initTeamState, readTeamLeaderAttention, readTeamPhase, writeTeamLeaderAttention, } from "../../team/state.js";
|
|
10
10
|
import { dispatchCodexNativeHook, mapCodexHookEventToOmxEvent, resolveSessionOwnerPidFromAncestry, } from "../codex-native-hook.js";
|
|
11
|
+
import { writeSessionStart } from "../../hooks/session.js";
|
|
11
12
|
async function writeJson(path, value) {
|
|
12
13
|
await mkdir(dirname(path), { recursive: true }).catch(() => { });
|
|
13
14
|
await writeFile(path, JSON.stringify(value, null, 2));
|
|
14
15
|
}
|
|
16
|
+
async function writeReleaseReadinessLeaderAttention(teamName, sessionId, cwd, options) {
|
|
17
|
+
await writeTeamLeaderAttention(teamName, {
|
|
18
|
+
team_name: teamName,
|
|
19
|
+
updated_at: "2026-04-12T17:20:00.000Z",
|
|
20
|
+
source: "notify_hook",
|
|
21
|
+
leader_decision_state: "done_waiting_on_leader",
|
|
22
|
+
leader_attention_pending: true,
|
|
23
|
+
leader_attention_reason: "leader_session_stopped",
|
|
24
|
+
attention_reasons: ["leader_session_stopped"],
|
|
25
|
+
leader_stale: true,
|
|
26
|
+
leader_session_active: false,
|
|
27
|
+
leader_session_id: sessionId,
|
|
28
|
+
leader_session_stopped_at: "2026-04-12T17:20:00.000Z",
|
|
29
|
+
unread_leader_message_count: 0,
|
|
30
|
+
work_remaining: options.workRemaining,
|
|
31
|
+
stalled_for_ms: null,
|
|
32
|
+
}, cwd);
|
|
33
|
+
}
|
|
34
|
+
async function writeReleaseReadinessStateMarker(sessionId, teamName, cwd) {
|
|
35
|
+
await writeJson(join(cwd, ".omx", "state", "sessions", sessionId, "release-readiness-state.json"), {
|
|
36
|
+
active: true,
|
|
37
|
+
session_id: sessionId,
|
|
38
|
+
team_name: teamName,
|
|
39
|
+
stable_final_recommendation_emitted: true,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
15
42
|
const TEAM_STOP_COMMIT_GUIDANCE = " If system-generated worker auto-checkpoint commits exist, rewrite them into Lore-format final commits before merge/finalization.";
|
|
43
|
+
const DEFAULT_AUTO_NUDGE_RESPONSE = "continue with the current task only if it is already authorized";
|
|
16
44
|
const TEAM_ENV_KEYS = [
|
|
17
45
|
"OMX_TEAM_WORKER",
|
|
18
46
|
"OMX_TEAM_STATE_ROOT",
|
|
@@ -58,6 +86,19 @@ describe("codex native hook config", () => {
|
|
|
58
86
|
});
|
|
59
87
|
});
|
|
60
88
|
describe("codex native hook dispatch", () => {
|
|
89
|
+
it("emits deterministic JSON stdout when CLI stdin is malformed", () => {
|
|
90
|
+
const stdout = execFileSync(process.execPath, [join(process.cwd(), "dist", "scripts", "codex-native-hook.js")], {
|
|
91
|
+
cwd: process.cwd(),
|
|
92
|
+
input: "{",
|
|
93
|
+
encoding: "utf-8",
|
|
94
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
95
|
+
});
|
|
96
|
+
const output = JSON.parse(stdout.trim());
|
|
97
|
+
assert.equal(output.decision, "block");
|
|
98
|
+
assert.equal(output.reason, "OMX native hook received malformed JSON input. Preserve runtime state and inspect the emitting hook payload before retrying.");
|
|
99
|
+
assert.equal(output.hookSpecificOutput?.hookEventName, "Unknown");
|
|
100
|
+
assert.match(String(output.hookSpecificOutput?.additionalContext ?? ""), /stdin JSON parsing failed inside codex-native-hook:/);
|
|
101
|
+
});
|
|
61
102
|
it("maps Codex events onto OMX logical surfaces", () => {
|
|
62
103
|
assert.equal(mapCodexHookEventToOmxEvent("SessionStart"), "session-start");
|
|
63
104
|
assert.equal(mapCodexHookEventToOmxEvent("UserPromptSubmit"), "keyword-detector");
|
|
@@ -85,12 +126,55 @@ describe("codex native hook dispatch", () => {
|
|
|
85
126
|
});
|
|
86
127
|
const sessionState = JSON.parse(await readFile(join(cwd, ".omx", "state", "session.json"), "utf-8"));
|
|
87
128
|
assert.equal(sessionState.session_id, "sess-start-1");
|
|
129
|
+
assert.equal(sessionState.native_session_id, "sess-start-1");
|
|
88
130
|
assert.equal(sessionState.pid, 43210);
|
|
89
131
|
}
|
|
90
132
|
finally {
|
|
91
133
|
await rm(cwd, { recursive: true, force: true });
|
|
92
134
|
}
|
|
93
135
|
});
|
|
136
|
+
it("preserves canonical OMX session scope when native SessionStart arrives with a different id", async () => {
|
|
137
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-reconcile-"));
|
|
138
|
+
try {
|
|
139
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
140
|
+
const canonicalSessionId = "omx-launch-1";
|
|
141
|
+
const nativeSessionId = "codex-native-1";
|
|
142
|
+
await mkdir(join(stateDir, "sessions", canonicalSessionId), { recursive: true });
|
|
143
|
+
await writeSessionStart(cwd, canonicalSessionId);
|
|
144
|
+
await writeJson(join(stateDir, "sessions", canonicalSessionId, "hud-state.json"), {
|
|
145
|
+
last_turn_at: "2026-04-10T00:00:00.000Z",
|
|
146
|
+
turn_count: 1,
|
|
147
|
+
});
|
|
148
|
+
await dispatchCodexNativeHook({
|
|
149
|
+
hook_event_name: "SessionStart",
|
|
150
|
+
cwd,
|
|
151
|
+
session_id: nativeSessionId,
|
|
152
|
+
}, {
|
|
153
|
+
cwd,
|
|
154
|
+
sessionOwnerPid: process.pid,
|
|
155
|
+
});
|
|
156
|
+
const sessionState = JSON.parse(await readFile(join(stateDir, "session.json"), "utf-8"));
|
|
157
|
+
assert.equal(sessionState.session_id, canonicalSessionId);
|
|
158
|
+
assert.equal(sessionState.native_session_id, nativeSessionId);
|
|
159
|
+
assert.equal(sessionState.pid, process.pid);
|
|
160
|
+
const promptResult = await dispatchCodexNativeHook({
|
|
161
|
+
hook_event_name: "UserPromptSubmit",
|
|
162
|
+
cwd,
|
|
163
|
+
session_id: nativeSessionId,
|
|
164
|
+
thread_id: "thread-1",
|
|
165
|
+
turn_id: "turn-1",
|
|
166
|
+
prompt: "$ralplan fix hud scope drift",
|
|
167
|
+
}, { cwd });
|
|
168
|
+
assert.equal(promptResult.omxEventName, "keyword-detector");
|
|
169
|
+
assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "skill-active-state.json")), true);
|
|
170
|
+
assert.equal(existsSync(join(stateDir, "sessions", canonicalSessionId, "ralplan-state.json")), true);
|
|
171
|
+
assert.equal(existsSync(join(stateDir, "sessions", nativeSessionId, "skill-active-state.json")), false);
|
|
172
|
+
assert.equal(existsSync(join(stateDir, "sessions", nativeSessionId, "ralplan-state.json")), false);
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
await rm(cwd, { recursive: true, force: true });
|
|
176
|
+
}
|
|
177
|
+
});
|
|
94
178
|
it("appends .omx/ to repo-root .gitignore during SessionStart when missing", async () => {
|
|
95
179
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-session-gitignore-"));
|
|
96
180
|
try {
|
|
@@ -185,6 +269,40 @@ describe("codex native hook dispatch", () => {
|
|
|
185
269
|
await rm(cwd, { recursive: true, force: true });
|
|
186
270
|
}
|
|
187
271
|
});
|
|
272
|
+
it("does not expose submitted prompt text to keyword-detector hook plugins", async () => {
|
|
273
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-prompt-sanitized-"));
|
|
274
|
+
try {
|
|
275
|
+
await mkdir(join(cwd, ".omx", "hooks"), { recursive: true });
|
|
276
|
+
await writeFile(join(cwd, ".omx", "hooks", "capture-keyword-context.mjs"), `import { mkdir, writeFile } from "node:fs/promises";
|
|
277
|
+
import { dirname, join } from "node:path";
|
|
278
|
+
|
|
279
|
+
export async function onHookEvent(event) {
|
|
280
|
+
if (event.event !== "keyword-detector") return;
|
|
281
|
+
const outPath = join(process.cwd(), ".omx", "captured-keyword-context.json");
|
|
282
|
+
await mkdir(dirname(outPath), { recursive: true });
|
|
283
|
+
await writeFile(outPath, JSON.stringify(event.context, null, 2));
|
|
284
|
+
}
|
|
285
|
+
`, "utf-8");
|
|
286
|
+
await dispatchCodexNativeHook({
|
|
287
|
+
hook_event_name: "UserPromptSubmit",
|
|
288
|
+
cwd,
|
|
289
|
+
session_id: "sess-sanitized-1",
|
|
290
|
+
thread_id: "thread-sanitized-1",
|
|
291
|
+
turn_id: "turn-sanitized-1",
|
|
292
|
+
prompt: "$ralplan approve this blocker-sensitive request",
|
|
293
|
+
}, { cwd });
|
|
294
|
+
const captured = JSON.parse(await readFile(join(cwd, ".omx", "captured-keyword-context.json"), "utf-8"));
|
|
295
|
+
assert.equal(captured.prompt, undefined);
|
|
296
|
+
assert.equal(captured.payload?.prompt, undefined);
|
|
297
|
+
assert.equal(captured.payload?.input, undefined);
|
|
298
|
+
assert.equal(captured.payload?.user_prompt, undefined);
|
|
299
|
+
assert.equal(captured.payload?.userPrompt, undefined);
|
|
300
|
+
assert.equal(captured.payload?.text, undefined);
|
|
301
|
+
}
|
|
302
|
+
finally {
|
|
303
|
+
await rm(cwd, { recursive: true, force: true });
|
|
304
|
+
}
|
|
305
|
+
});
|
|
188
306
|
it("does not emit UserPromptSubmit routing context for unknown $tokens", async () => {
|
|
189
307
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-unknown-token-"));
|
|
190
308
|
try {
|
|
@@ -232,6 +350,95 @@ describe("codex native hook dispatch", () => {
|
|
|
232
350
|
await rm(cwd, { recursive: true, force: true });
|
|
233
351
|
}
|
|
234
352
|
});
|
|
353
|
+
it("returns actionable denial guidance for unsupported workflow overlaps on prompt submit", async () => {
|
|
354
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-transition-deny-"));
|
|
355
|
+
try {
|
|
356
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
357
|
+
await dispatchCodexNativeHook({
|
|
358
|
+
hook_event_name: "UserPromptSubmit",
|
|
359
|
+
cwd,
|
|
360
|
+
session_id: "sess-deny-1",
|
|
361
|
+
thread_id: "thread-deny-1",
|
|
362
|
+
turn_id: "turn-deny-1",
|
|
363
|
+
prompt: "$team ship this fix",
|
|
364
|
+
}, { cwd });
|
|
365
|
+
const denied = await dispatchCodexNativeHook({
|
|
366
|
+
hook_event_name: "UserPromptSubmit",
|
|
367
|
+
cwd,
|
|
368
|
+
session_id: "sess-deny-1",
|
|
369
|
+
thread_id: "thread-deny-1",
|
|
370
|
+
turn_id: "turn-deny-2",
|
|
371
|
+
prompt: "$autopilot also run this",
|
|
372
|
+
}, { cwd });
|
|
373
|
+
assert.match(JSON.stringify(denied.outputJson), /denied workflow keyword/i);
|
|
374
|
+
assert.match(JSON.stringify(denied.outputJson), /Unsupported workflow overlap: team \+ autopilot\./);
|
|
375
|
+
assert.match(JSON.stringify(denied.outputJson), /`omx state clear --mode <mode>`/);
|
|
376
|
+
assert.match(JSON.stringify(denied.outputJson), /`omx_state\.\*` MCP tools/);
|
|
377
|
+
assert.equal(existsSync(join(cwd, ".omx", "state", "sessions", "sess-deny-1", "autopilot-state.json")), false);
|
|
378
|
+
}
|
|
379
|
+
finally {
|
|
380
|
+
await rm(cwd, { recursive: true, force: true });
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
it("surfaces transition success output for allowlisted prompt-submit handoffs", async () => {
|
|
384
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-transition-success-"));
|
|
385
|
+
try {
|
|
386
|
+
const sessionDir = join(cwd, ".omx", "state", "sessions", "sess-handoff-1");
|
|
387
|
+
await mkdir(sessionDir, { recursive: true });
|
|
388
|
+
await writeJson(join(sessionDir, "deep-interview-state.json"), {
|
|
389
|
+
active: true,
|
|
390
|
+
mode: "deep-interview",
|
|
391
|
+
current_phase: "intent-first",
|
|
392
|
+
});
|
|
393
|
+
await writeJson(join(sessionDir, "skill-active-state.json"), {
|
|
394
|
+
active: true,
|
|
395
|
+
skill: "deep-interview",
|
|
396
|
+
phase: "planning",
|
|
397
|
+
session_id: "sess-handoff-1",
|
|
398
|
+
active_skills: [{ skill: "deep-interview", phase: "planning", active: true, session_id: "sess-handoff-1" }],
|
|
399
|
+
});
|
|
400
|
+
const result = await dispatchCodexNativeHook({
|
|
401
|
+
hook_event_name: "UserPromptSubmit",
|
|
402
|
+
cwd,
|
|
403
|
+
session_id: "sess-handoff-1",
|
|
404
|
+
thread_id: "thread-handoff-1",
|
|
405
|
+
turn_id: "turn-handoff-1",
|
|
406
|
+
prompt: "$ralplan implement the approved contract",
|
|
407
|
+
}, { cwd });
|
|
408
|
+
assert.match(JSON.stringify(result.outputJson), /mode transiting: deep-interview -> ralplan/);
|
|
409
|
+
const completed = JSON.parse(await readFile(join(sessionDir, "deep-interview-state.json"), "utf-8"));
|
|
410
|
+
assert.equal(completed.active, false);
|
|
411
|
+
assert.equal(completed.current_phase, "completed");
|
|
412
|
+
}
|
|
413
|
+
finally {
|
|
414
|
+
await rm(cwd, { recursive: true, force: true });
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
it("keeps the planning skill active when planning and execution workflows are invoked together", async () => {
|
|
418
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-planning-precedence-"));
|
|
419
|
+
try {
|
|
420
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
421
|
+
const result = await dispatchCodexNativeHook({
|
|
422
|
+
hook_event_name: "UserPromptSubmit",
|
|
423
|
+
cwd,
|
|
424
|
+
session_id: "sess-multi-1",
|
|
425
|
+
thread_id: "thread-multi-1",
|
|
426
|
+
turn_id: "turn-multi-1",
|
|
427
|
+
prompt: "$ralplan $team $ralph ship this fix",
|
|
428
|
+
}, { cwd });
|
|
429
|
+
const message = String(result.outputJson?.hookSpecificOutput?.additionalContext || '');
|
|
430
|
+
assert.match(message, /\$ralplan" -> ralplan/);
|
|
431
|
+
assert.match(message, /\$team" -> team/);
|
|
432
|
+
assert.match(message, /\$ralph" -> ralph/);
|
|
433
|
+
assert.doesNotMatch(message, /mode transiting:/);
|
|
434
|
+
assert.match(message, /planning preserved over simultaneous execution follow-up; deferred skills: team, ralph\./);
|
|
435
|
+
assert.match(message, /skill: ralplan activated and initial state initialized at \.omx\/state\/sessions\/sess-multi-1\/ralplan-state\.json; write subsequent updates via omx_state MCP\./);
|
|
436
|
+
assert.doesNotMatch(message, /Use the durable OMX team runtime via `omx team \.\.\.`/);
|
|
437
|
+
}
|
|
438
|
+
finally {
|
|
439
|
+
await rm(cwd, { recursive: true, force: true });
|
|
440
|
+
}
|
|
441
|
+
});
|
|
235
442
|
it("runs prompt-submit HUD reconciliation as a best-effort tmux-only side effect", async () => {
|
|
236
443
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reconcile-"));
|
|
237
444
|
const originalTmux = process.env.TMUX;
|
|
@@ -436,6 +643,47 @@ esac
|
|
|
436
643
|
await rm(cwd, { recursive: true, force: true });
|
|
437
644
|
}
|
|
438
645
|
});
|
|
646
|
+
it("marks canonical team state failed when native payload session ids differ during MCP transport death", async () => {
|
|
647
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-team-native-transport-"));
|
|
648
|
+
const previousCwd = process.cwd();
|
|
649
|
+
const canonicalSessionId = "omx-canonical-session";
|
|
650
|
+
const nativeSessionId = "codex-native-session";
|
|
651
|
+
try {
|
|
652
|
+
process.chdir(cwd);
|
|
653
|
+
await writeSessionStart(cwd, canonicalSessionId);
|
|
654
|
+
const sessionPath = join(cwd, ".omx", "state", "session.json");
|
|
655
|
+
const sessionState = JSON.parse(await readFile(sessionPath, "utf-8"));
|
|
656
|
+
await writeFile(sessionPath, JSON.stringify({
|
|
657
|
+
...sessionState,
|
|
658
|
+
native_session_id: nativeSessionId,
|
|
659
|
+
}, null, 2));
|
|
660
|
+
await initTeamState("transport-team", "task", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: canonicalSessionId });
|
|
661
|
+
await writeJson(join(cwd, ".omx", "state", "team-state.json"), {
|
|
662
|
+
active: true,
|
|
663
|
+
team_name: "transport-team",
|
|
664
|
+
current_phase: "team-exec",
|
|
665
|
+
});
|
|
666
|
+
await dispatchCodexNativeHook({
|
|
667
|
+
hook_event_name: "PostToolUse",
|
|
668
|
+
cwd,
|
|
669
|
+
session_id: nativeSessionId,
|
|
670
|
+
tool_name: "mcp__omx_state__state_write",
|
|
671
|
+
tool_use_id: "tool-mcp-transport-team-native",
|
|
672
|
+
tool_input: { mode: "team", active: true },
|
|
673
|
+
tool_response: "{\"error\":\"MCP transport closed\",\"details\":\"stdio pipe closed before response\"}",
|
|
674
|
+
}, { cwd });
|
|
675
|
+
const phase = await readTeamPhase("transport-team", cwd);
|
|
676
|
+
const attention = await readTeamLeaderAttention("transport-team", cwd);
|
|
677
|
+
assert.equal(phase?.current_phase, "failed");
|
|
678
|
+
assert.equal(attention?.leader_attention_reason, "mcp_transport_dead");
|
|
679
|
+
assert.equal(attention?.leader_attention_pending, true);
|
|
680
|
+
assert.equal(attention?.leader_session_id, canonicalSessionId);
|
|
681
|
+
}
|
|
682
|
+
finally {
|
|
683
|
+
process.chdir(previousCwd);
|
|
684
|
+
await rm(cwd, { recursive: true, force: true });
|
|
685
|
+
}
|
|
686
|
+
});
|
|
439
687
|
it("treats stderr-only informative non-zero output as reviewable instead of a generic failure", async () => {
|
|
440
688
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-posttool-informative-stderr-"));
|
|
441
689
|
try {
|
|
@@ -792,6 +1040,58 @@ esac
|
|
|
792
1040
|
await rm(cwd, { recursive: true, force: true });
|
|
793
1041
|
}
|
|
794
1042
|
});
|
|
1043
|
+
it("emits one concise final decision summary and auto-finalize guidance when release-readiness already has a stable final recommendation and no active worker tasks", async () => {
|
|
1044
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-release-readiness-finalize-"));
|
|
1045
|
+
try {
|
|
1046
|
+
await initTeamState("release-ready-team", "release readiness finalize", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-release-ready" });
|
|
1047
|
+
await writeReleaseReadinessLeaderAttention("release-ready-team", "sess-stop-release-ready", cwd, { workRemaining: false });
|
|
1048
|
+
await writeReleaseReadinessStateMarker("sess-stop-release-ready", "release-ready-team", cwd);
|
|
1049
|
+
const result = await dispatchCodexNativeHook({
|
|
1050
|
+
hook_event_name: "Stop",
|
|
1051
|
+
cwd,
|
|
1052
|
+
session_id: "sess-stop-release-ready",
|
|
1053
|
+
thread_id: "thread-stop-release-ready",
|
|
1054
|
+
turn_id: "turn-stop-release-ready-1",
|
|
1055
|
+
mode: "release-readiness",
|
|
1056
|
+
last_assistant_message: "Launch-ready: yes",
|
|
1057
|
+
}, { cwd });
|
|
1058
|
+
assert.equal(result.omxEventName, "stop");
|
|
1059
|
+
assert.deepEqual(result.outputJson, {
|
|
1060
|
+
decision: "block",
|
|
1061
|
+
reason: 'Stable final recommendation already reached with no active worker tasks. Emit exactly one concise final decision summary aligned to "Launch-ready: yes." with no filler or residual acknowledgements (for example "yes"), then stop.',
|
|
1062
|
+
stopReason: "release_readiness_auto_finalize",
|
|
1063
|
+
systemMessage: "OMX release-readiness detected a stable final recommendation with no active worker tasks; emit one concise final decision summary and finalize.",
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
finally {
|
|
1067
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1068
|
+
}
|
|
1069
|
+
});
|
|
1070
|
+
it("does not auto-finalize non-release team stops that happen to contain a stable recommendation summary", async () => {
|
|
1071
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-non-release-readiness-control-"));
|
|
1072
|
+
try {
|
|
1073
|
+
await initTeamState("general-review-team", "general team stop control", "executor", 1, cwd, undefined, { ...process.env, OMX_SESSION_ID: "sess-stop-general-review" });
|
|
1074
|
+
await writeReleaseReadinessLeaderAttention("general-review-team", "sess-stop-general-review", cwd, { workRemaining: false });
|
|
1075
|
+
const result = await dispatchCodexNativeHook({
|
|
1076
|
+
hook_event_name: "Stop",
|
|
1077
|
+
cwd,
|
|
1078
|
+
session_id: "sess-stop-general-review",
|
|
1079
|
+
thread_id: "thread-stop-general-review",
|
|
1080
|
+
turn_id: "turn-stop-general-review-1",
|
|
1081
|
+
last_assistant_message: "Launch-ready: yes",
|
|
1082
|
+
}, { cwd });
|
|
1083
|
+
assert.equal(result.omxEventName, "stop");
|
|
1084
|
+
assert.deepEqual(result.outputJson, {
|
|
1085
|
+
decision: "block",
|
|
1086
|
+
reason: `OMX team pipeline is still active (general-review-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`,
|
|
1087
|
+
stopReason: "team_team-exec",
|
|
1088
|
+
systemMessage: "OMX team pipeline is still active at phase team-exec.",
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
finally {
|
|
1092
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
795
1095
|
it("re-fires canonical-team Stop output for a later fresh Stop reply when coarse mode state is missing", async () => {
|
|
796
1096
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-canonical-refire-"));
|
|
797
1097
|
try {
|
|
@@ -936,6 +1236,42 @@ esac
|
|
|
936
1236
|
await rm(cwd, { recursive: true, force: true });
|
|
937
1237
|
}
|
|
938
1238
|
});
|
|
1239
|
+
it("blocks Stop from session-scoped team mode when session.json points to another session", async () => {
|
|
1240
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-team-session-mismatch-"));
|
|
1241
|
+
try {
|
|
1242
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1243
|
+
await mkdir(join(stateDir, "sessions", "sess-live-team"), { recursive: true });
|
|
1244
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-other-team" });
|
|
1245
|
+
await writeJson(join(stateDir, "sessions", "sess-live-team", "team-state.json"), {
|
|
1246
|
+
active: true,
|
|
1247
|
+
mode: "team",
|
|
1248
|
+
current_phase: "team-exec",
|
|
1249
|
+
team_name: "session-live-team",
|
|
1250
|
+
});
|
|
1251
|
+
await writeJson(join(stateDir, "team", "session-live-team", "phase.json"), {
|
|
1252
|
+
current_phase: "team-exec",
|
|
1253
|
+
max_fix_attempts: 3,
|
|
1254
|
+
current_fix_attempt: 0,
|
|
1255
|
+
transitions: [],
|
|
1256
|
+
updated_at: new Date().toISOString(),
|
|
1257
|
+
});
|
|
1258
|
+
const result = await dispatchCodexNativeHook({
|
|
1259
|
+
hook_event_name: "Stop",
|
|
1260
|
+
cwd,
|
|
1261
|
+
session_id: "sess-live-team",
|
|
1262
|
+
}, { cwd });
|
|
1263
|
+
assert.equal(result.omxEventName, "stop");
|
|
1264
|
+
assert.deepEqual(result.outputJson, {
|
|
1265
|
+
decision: "block",
|
|
1266
|
+
reason: `OMX team pipeline is still active (session-live-team) at phase team-exec; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`,
|
|
1267
|
+
stopReason: "team_team-exec",
|
|
1268
|
+
systemMessage: "OMX team pipeline is still active at phase team-exec.",
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
finally {
|
|
1272
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
939
1275
|
it("returns Stop continuation output for active ralplan skill with matching active mode state and without active subagents", async () => {
|
|
940
1276
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-"));
|
|
941
1277
|
try {
|
|
@@ -1074,7 +1410,7 @@ esac
|
|
|
1074
1410
|
await rm(cwd, { recursive: true, force: true });
|
|
1075
1411
|
}
|
|
1076
1412
|
});
|
|
1077
|
-
it("
|
|
1413
|
+
it("does not block Stop solely because deep-interview is active", async () => {
|
|
1078
1414
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-deep-interview-"));
|
|
1079
1415
|
try {
|
|
1080
1416
|
const stateDir = join(cwd, ".omx", "state");
|
|
@@ -1094,12 +1430,7 @@ esac
|
|
|
1094
1430
|
cwd,
|
|
1095
1431
|
session_id: "sess-stop-deep-interview",
|
|
1096
1432
|
}, { cwd });
|
|
1097
|
-
assert.
|
|
1098
|
-
decision: "block",
|
|
1099
|
-
reason: "OMX skill deep-interview is still active (phase: planning); continue until the current deep-interview workflow reaches a terminal state.",
|
|
1100
|
-
stopReason: "skill_deep-interview_planning",
|
|
1101
|
-
systemMessage: "OMX skill deep-interview is still active (phase: planning).",
|
|
1102
|
-
});
|
|
1433
|
+
assert.equal(result.outputJson, null);
|
|
1103
1434
|
}
|
|
1104
1435
|
finally {
|
|
1105
1436
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -1129,7 +1460,7 @@ esac
|
|
|
1129
1460
|
await rm(cwd, { recursive: true, force: true });
|
|
1130
1461
|
}
|
|
1131
1462
|
});
|
|
1132
|
-
it("returns Stop continuation output while Ralph is active", async () => {
|
|
1463
|
+
it("returns Stop continuation output while Ralph is active without an explicit session pin", async () => {
|
|
1133
1464
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-"));
|
|
1134
1465
|
try {
|
|
1135
1466
|
const stateDir = join(cwd, ".omx", "state");
|
|
@@ -1141,7 +1472,34 @@ esac
|
|
|
1141
1472
|
const result = await dispatchCodexNativeHook({
|
|
1142
1473
|
hook_event_name: "Stop",
|
|
1143
1474
|
cwd,
|
|
1144
|
-
|
|
1475
|
+
}, { cwd });
|
|
1476
|
+
assert.equal(result.omxEventName, "stop");
|
|
1477
|
+
assert.deepEqual(result.outputJson, {
|
|
1478
|
+
decision: "block",
|
|
1479
|
+
reason: "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
|
|
1480
|
+
stopReason: "ralph_executing",
|
|
1481
|
+
systemMessage: "OMX Ralph is still active (phase: executing); continue the task and gather fresh verification evidence before stopping.",
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
finally {
|
|
1485
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1486
|
+
}
|
|
1487
|
+
});
|
|
1488
|
+
it("blocks Stop from session-scoped Ralph state when session.json points to another session", async () => {
|
|
1489
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-session-mismatch-"));
|
|
1490
|
+
try {
|
|
1491
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1492
|
+
await mkdir(join(stateDir, "sessions", "sess-live-ralph"), { recursive: true });
|
|
1493
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-other-ralph" });
|
|
1494
|
+
await writeJson(join(stateDir, "sessions", "sess-live-ralph", "ralph-state.json"), {
|
|
1495
|
+
active: true,
|
|
1496
|
+
current_phase: "executing",
|
|
1497
|
+
session_id: "sess-live-ralph",
|
|
1498
|
+
});
|
|
1499
|
+
const result = await dispatchCodexNativeHook({
|
|
1500
|
+
hook_event_name: "Stop",
|
|
1501
|
+
cwd,
|
|
1502
|
+
session_id: "sess-live-ralph",
|
|
1145
1503
|
}, { cwd });
|
|
1146
1504
|
assert.equal(result.omxEventName, "stop");
|
|
1147
1505
|
assert.deepEqual(result.outputJson, {
|
|
@@ -1179,6 +1537,103 @@ esac
|
|
|
1179
1537
|
await rm(cwd, { recursive: true, force: true });
|
|
1180
1538
|
}
|
|
1181
1539
|
});
|
|
1540
|
+
it("does not block Stop from another session-scoped Ralph state when an explicit session_id has no active Ralph state", async () => {
|
|
1541
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-explicit-session-ralph-"));
|
|
1542
|
+
try {
|
|
1543
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1544
|
+
await mkdir(join(stateDir, "sessions", "sess-other"), { recursive: true });
|
|
1545
|
+
await writeJson(join(stateDir, "sessions", "sess-other", "ralph-state.json"), {
|
|
1546
|
+
active: true,
|
|
1547
|
+
current_phase: "starting",
|
|
1548
|
+
session_id: "sess-other",
|
|
1549
|
+
});
|
|
1550
|
+
const result = await dispatchCodexNativeHook({
|
|
1551
|
+
hook_event_name: "Stop",
|
|
1552
|
+
cwd,
|
|
1553
|
+
session_id: "sess-current",
|
|
1554
|
+
}, { cwd });
|
|
1555
|
+
assert.equal(result.omxEventName, "stop");
|
|
1556
|
+
assert.equal(result.outputJson, null);
|
|
1557
|
+
}
|
|
1558
|
+
finally {
|
|
1559
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1560
|
+
}
|
|
1561
|
+
});
|
|
1562
|
+
it("does not block Stop from root Ralph fallback when the current session has no scoped Ralph state", async () => {
|
|
1563
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-fallback-ralph-"));
|
|
1564
|
+
try {
|
|
1565
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1566
|
+
await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true });
|
|
1567
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-current", cwd });
|
|
1568
|
+
await writeJson(join(stateDir, "ralph-state.json"), {
|
|
1569
|
+
active: true,
|
|
1570
|
+
current_phase: "executing",
|
|
1571
|
+
});
|
|
1572
|
+
const result = await dispatchCodexNativeHook({
|
|
1573
|
+
hook_event_name: "Stop",
|
|
1574
|
+
cwd,
|
|
1575
|
+
session_id: "sess-current",
|
|
1576
|
+
}, { cwd });
|
|
1577
|
+
assert.equal(result.omxEventName, "stop");
|
|
1578
|
+
assert.equal(result.outputJson, null);
|
|
1579
|
+
}
|
|
1580
|
+
finally {
|
|
1581
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1582
|
+
}
|
|
1583
|
+
});
|
|
1584
|
+
it("does not block Stop when the current session Ralph state is cancelled even if stale root fallback remains", async () => {
|
|
1585
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-cancelled-session-ralph-"));
|
|
1586
|
+
try {
|
|
1587
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1588
|
+
await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true });
|
|
1589
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-current", cwd });
|
|
1590
|
+
await writeJson(join(stateDir, "sessions", "sess-current", "ralph-state.json"), {
|
|
1591
|
+
active: false,
|
|
1592
|
+
current_phase: "cancelled",
|
|
1593
|
+
completed_at: "2026-04-10T23:30:38.000Z",
|
|
1594
|
+
session_id: "sess-current",
|
|
1595
|
+
});
|
|
1596
|
+
await writeJson(join(stateDir, "ralph-state.json"), {
|
|
1597
|
+
active: true,
|
|
1598
|
+
current_phase: "starting",
|
|
1599
|
+
});
|
|
1600
|
+
const result = await dispatchCodexNativeHook({
|
|
1601
|
+
hook_event_name: "Stop",
|
|
1602
|
+
cwd,
|
|
1603
|
+
session_id: "sess-current",
|
|
1604
|
+
}, { cwd });
|
|
1605
|
+
assert.equal(result.omxEventName, "stop");
|
|
1606
|
+
assert.equal(result.outputJson, null);
|
|
1607
|
+
}
|
|
1608
|
+
finally {
|
|
1609
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1610
|
+
}
|
|
1611
|
+
});
|
|
1612
|
+
it("does not block Stop from root Ralph fallback when an explicit session_id is present and session.json points to another worktree", async () => {
|
|
1613
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-root-fallback-cwd-mismatch-"));
|
|
1614
|
+
try {
|
|
1615
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1616
|
+
await mkdir(stateDir, { recursive: true });
|
|
1617
|
+
await writeJson(join(stateDir, "session.json"), {
|
|
1618
|
+
session_id: "sess-elsewhere",
|
|
1619
|
+
cwd: join(cwd, "..", "different-worktree"),
|
|
1620
|
+
});
|
|
1621
|
+
await writeJson(join(stateDir, "ralph-state.json"), {
|
|
1622
|
+
active: true,
|
|
1623
|
+
current_phase: "executing",
|
|
1624
|
+
});
|
|
1625
|
+
const result = await dispatchCodexNativeHook({
|
|
1626
|
+
hook_event_name: "Stop",
|
|
1627
|
+
cwd,
|
|
1628
|
+
session_id: "sess-current",
|
|
1629
|
+
}, { cwd });
|
|
1630
|
+
assert.equal(result.omxEventName, "stop");
|
|
1631
|
+
assert.equal(result.outputJson, null);
|
|
1632
|
+
}
|
|
1633
|
+
finally {
|
|
1634
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1635
|
+
}
|
|
1636
|
+
});
|
|
1182
1637
|
it("does not re-block Ralph when Stop already continued once", async () => {
|
|
1183
1638
|
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-ralph-once-"));
|
|
1184
1639
|
try {
|
|
@@ -1210,12 +1665,12 @@ esac
|
|
|
1210
1665
|
hook_event_name: "Stop",
|
|
1211
1666
|
cwd,
|
|
1212
1667
|
session_id: "sess-stop-auto",
|
|
1213
|
-
last_assistant_message: "
|
|
1668
|
+
last_assistant_message: "Keep going and finish the cleanup.",
|
|
1214
1669
|
}, { cwd });
|
|
1215
1670
|
assert.equal(result.omxEventName, "stop");
|
|
1216
1671
|
assert.deepEqual(result.outputJson, {
|
|
1217
1672
|
decision: "block",
|
|
1218
|
-
reason:
|
|
1673
|
+
reason: DEFAULT_AUTO_NUDGE_RESPONSE,
|
|
1219
1674
|
stopReason: "auto_nudge",
|
|
1220
1675
|
systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.",
|
|
1221
1676
|
});
|
|
@@ -1235,7 +1690,7 @@ esac
|
|
|
1235
1690
|
session_id: "sess-stop-auto-once",
|
|
1236
1691
|
thread_id: "thread-stop-auto",
|
|
1237
1692
|
turn_id: "turn-stop-auto-1",
|
|
1238
|
-
last_assistant_message: "
|
|
1693
|
+
last_assistant_message: "Keep going and finish the cleanup.",
|
|
1239
1694
|
}, { cwd });
|
|
1240
1695
|
const result = await dispatchCodexNativeHook({
|
|
1241
1696
|
hook_event_name: "Stop",
|
|
@@ -1244,7 +1699,7 @@ esac
|
|
|
1244
1699
|
thread_id: "thread-stop-auto",
|
|
1245
1700
|
turn_id: "turn-stop-auto-1",
|
|
1246
1701
|
stop_hook_active: true,
|
|
1247
|
-
last_assistant_message: "
|
|
1702
|
+
last_assistant_message: "Keep going and finish the cleanup.",
|
|
1248
1703
|
}, { cwd });
|
|
1249
1704
|
assert.equal(result.omxEventName, "stop");
|
|
1250
1705
|
assert.equal(result.outputJson, null);
|
|
@@ -1264,7 +1719,7 @@ esac
|
|
|
1264
1719
|
session_id: "sess-stop-auto-refire",
|
|
1265
1720
|
thread_id: "thread-stop-auto-refire",
|
|
1266
1721
|
turn_id: "turn-stop-auto-refire-1",
|
|
1267
|
-
last_assistant_message: "
|
|
1722
|
+
last_assistant_message: "Keep going and finish the cleanup.",
|
|
1268
1723
|
}, { cwd });
|
|
1269
1724
|
const result = await dispatchCodexNativeHook({
|
|
1270
1725
|
hook_event_name: "Stop",
|
|
@@ -1273,12 +1728,12 @@ esac
|
|
|
1273
1728
|
thread_id: "thread-stop-auto-refire",
|
|
1274
1729
|
turn_id: "turn-stop-auto-refire-2",
|
|
1275
1730
|
stop_hook_active: true,
|
|
1276
|
-
last_assistant_message: "
|
|
1731
|
+
last_assistant_message: "Continue with the cleanup from here.",
|
|
1277
1732
|
}, { cwd });
|
|
1278
1733
|
assert.equal(result.omxEventName, "stop");
|
|
1279
1734
|
assert.deepEqual(result.outputJson, {
|
|
1280
1735
|
decision: "block",
|
|
1281
|
-
reason:
|
|
1736
|
+
reason: DEFAULT_AUTO_NUDGE_RESPONSE,
|
|
1282
1737
|
stopReason: "auto_nudge",
|
|
1283
1738
|
systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.",
|
|
1284
1739
|
});
|
|
@@ -1287,17 +1742,36 @@ esac
|
|
|
1287
1742
|
await rm(cwd, { recursive: true, force: true });
|
|
1288
1743
|
}
|
|
1289
1744
|
});
|
|
1290
|
-
it("
|
|
1291
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-
|
|
1745
|
+
it("does not auto-continue native Stop on permission-seeking prompts", async () => {
|
|
1746
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-permission-"));
|
|
1747
|
+
try {
|
|
1748
|
+
await mkdir(join(cwd, ".omx", "state"), { recursive: true });
|
|
1749
|
+
const result = await dispatchCodexNativeHook({
|
|
1750
|
+
hook_event_name: "Stop",
|
|
1751
|
+
cwd,
|
|
1752
|
+
session_id: "sess-stop-auto-permission",
|
|
1753
|
+
last_assistant_message: "Would you like me to continue with the cleanup?",
|
|
1754
|
+
}, { cwd });
|
|
1755
|
+
assert.equal(result.omxEventName, "stop");
|
|
1756
|
+
assert.equal(result.outputJson, null);
|
|
1757
|
+
}
|
|
1758
|
+
finally {
|
|
1759
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1760
|
+
}
|
|
1761
|
+
});
|
|
1762
|
+
it("does not auto-continue native Stop while deep-interview is waiting on an intent-first question", async () => {
|
|
1763
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-deep-interview-question-"));
|
|
1292
1764
|
try {
|
|
1293
1765
|
const stateDir = join(cwd, ".omx", "state");
|
|
1294
|
-
await mkdir(join(stateDir, "sessions", "sess-stop-auto-
|
|
1295
|
-
await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-auto-
|
|
1296
|
-
await writeJson(join(stateDir, "sessions", "sess-stop-auto-
|
|
1766
|
+
await mkdir(join(stateDir, "sessions", "sess-stop-auto-question"), { recursive: true });
|
|
1767
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-auto-question" });
|
|
1768
|
+
await writeJson(join(stateDir, "sessions", "sess-stop-auto-question", "skill-active-state.json"), {
|
|
1297
1769
|
version: 1,
|
|
1298
1770
|
active: true,
|
|
1299
1771
|
skill: "deep-interview",
|
|
1300
1772
|
phase: "planning",
|
|
1773
|
+
session_id: "sess-stop-auto-question",
|
|
1774
|
+
thread_id: "thread-stop-auto-question",
|
|
1301
1775
|
input_lock: {
|
|
1302
1776
|
active: true,
|
|
1303
1777
|
scope: "deep-interview-auto-approval",
|
|
@@ -1305,25 +1779,26 @@ esac
|
|
|
1305
1779
|
message: "Deep interview is active; auto-approval shortcuts are blocked until the interview finishes.",
|
|
1306
1780
|
},
|
|
1307
1781
|
});
|
|
1308
|
-
await writeJson(join(stateDir, "sessions", "sess-stop-auto-
|
|
1782
|
+
await writeJson(join(stateDir, "sessions", "sess-stop-auto-question", "deep-interview-state.json"), {
|
|
1309
1783
|
active: true,
|
|
1310
|
-
|
|
1784
|
+
mode: "deep-interview",
|
|
1785
|
+
current_phase: "intent-first",
|
|
1311
1786
|
});
|
|
1312
1787
|
const result = await dispatchCodexNativeHook({
|
|
1313
1788
|
hook_event_name: "Stop",
|
|
1314
1789
|
cwd,
|
|
1315
|
-
session_id: "sess-stop-auto-
|
|
1316
|
-
thread_id: "thread-stop-auto-
|
|
1317
|
-
turn_id: "turn-stop-auto-
|
|
1318
|
-
last_assistant_message:
|
|
1790
|
+
session_id: "sess-stop-auto-question",
|
|
1791
|
+
thread_id: "thread-stop-auto-question",
|
|
1792
|
+
turn_id: "turn-stop-auto-question-1",
|
|
1793
|
+
last_assistant_message: [
|
|
1794
|
+
"Round 2 | Target: Decision boundary | Ambiguity: 24%",
|
|
1795
|
+
"",
|
|
1796
|
+
"If an existing project spider still declares session_mode = \"owned\", should ZenX fail loudly so the stale attribute is removed, or should it ignore the attribute and initialize the session pool anyway?",
|
|
1797
|
+
"Keep going once I have your answer.",
|
|
1798
|
+
].join("\n"),
|
|
1319
1799
|
}, { cwd });
|
|
1320
1800
|
assert.equal(result.omxEventName, "stop");
|
|
1321
|
-
assert.
|
|
1322
|
-
decision: "block",
|
|
1323
|
-
reason: "OMX skill deep-interview is still active (phase: planning); continue until the current deep-interview workflow reaches a terminal state.",
|
|
1324
|
-
stopReason: "skill_deep-interview_planning",
|
|
1325
|
-
systemMessage: "OMX skill deep-interview is still active (phase: planning).",
|
|
1326
|
-
});
|
|
1801
|
+
assert.equal(result.outputJson, null);
|
|
1327
1802
|
}
|
|
1328
1803
|
finally {
|
|
1329
1804
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -1395,12 +1870,12 @@ esac
|
|
|
1395
1870
|
session_id: "sess-stop-auto-stale-root-mode",
|
|
1396
1871
|
thread_id: "thread-stop-auto-stale-root-mode",
|
|
1397
1872
|
turn_id: "turn-stop-auto-stale-root-mode-1",
|
|
1398
|
-
last_assistant_message: "
|
|
1873
|
+
last_assistant_message: "Keep going and finish the cleanup.",
|
|
1399
1874
|
}, { cwd });
|
|
1400
1875
|
assert.equal(result.omxEventName, "stop");
|
|
1401
1876
|
assert.deepEqual(result.outputJson, {
|
|
1402
1877
|
decision: "block",
|
|
1403
|
-
reason:
|
|
1878
|
+
reason: DEFAULT_AUTO_NUDGE_RESPONSE,
|
|
1404
1879
|
stopReason: "auto_nudge",
|
|
1405
1880
|
systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.",
|
|
1406
1881
|
});
|
|
@@ -1425,12 +1900,12 @@ esac
|
|
|
1425
1900
|
session_id: "sess-stop-auto-stale-root-skill",
|
|
1426
1901
|
thread_id: "thread-stop-auto-stale-root-skill",
|
|
1427
1902
|
turn_id: "turn-stop-auto-stale-root-skill-1",
|
|
1428
|
-
last_assistant_message: "
|
|
1903
|
+
last_assistant_message: "Keep going and finish the cleanup.",
|
|
1429
1904
|
}, { cwd });
|
|
1430
1905
|
assert.equal(result.omxEventName, "stop");
|
|
1431
1906
|
assert.deepEqual(result.outputJson, {
|
|
1432
1907
|
decision: "block",
|
|
1433
|
-
reason:
|
|
1908
|
+
reason: DEFAULT_AUTO_NUDGE_RESPONSE,
|
|
1434
1909
|
stopReason: "auto_nudge",
|
|
1435
1910
|
systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.",
|
|
1436
1911
|
});
|
|
@@ -1461,12 +1936,48 @@ esac
|
|
|
1461
1936
|
session_id: "sess-stop-auto-stale-root-lock",
|
|
1462
1937
|
thread_id: "thread-stop-auto-stale-root-lock",
|
|
1463
1938
|
turn_id: "turn-stop-auto-stale-root-lock-1",
|
|
1464
|
-
last_assistant_message: "
|
|
1939
|
+
last_assistant_message: "Keep going and finish the cleanup.",
|
|
1465
1940
|
}, { cwd });
|
|
1466
1941
|
assert.equal(result.omxEventName, "stop");
|
|
1467
1942
|
assert.deepEqual(result.outputJson, {
|
|
1468
1943
|
decision: "block",
|
|
1469
|
-
reason:
|
|
1944
|
+
reason: DEFAULT_AUTO_NUDGE_RESPONSE,
|
|
1945
|
+
stopReason: "auto_nudge",
|
|
1946
|
+
systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.",
|
|
1947
|
+
});
|
|
1948
|
+
}
|
|
1949
|
+
finally {
|
|
1950
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1951
|
+
}
|
|
1952
|
+
});
|
|
1953
|
+
it("does not suppress native auto-nudge from active root deep-interview state when the current scoped mode state is explicitly inactive", async () => {
|
|
1954
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-auto-nudge-inactive-scoped-mode-"));
|
|
1955
|
+
try {
|
|
1956
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
1957
|
+
await mkdir(join(stateDir, "sessions", "sess-stop-auto-inactive-mode"), { recursive: true });
|
|
1958
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-auto-inactive-mode" });
|
|
1959
|
+
await writeJson(join(stateDir, "sessions", "sess-stop-auto-inactive-mode", "deep-interview-state.json"), {
|
|
1960
|
+
active: false,
|
|
1961
|
+
mode: "deep-interview",
|
|
1962
|
+
current_phase: "completed",
|
|
1963
|
+
});
|
|
1964
|
+
await writeJson(join(stateDir, "deep-interview-state.json"), {
|
|
1965
|
+
active: true,
|
|
1966
|
+
mode: "deep-interview",
|
|
1967
|
+
current_phase: "intent-first",
|
|
1968
|
+
});
|
|
1969
|
+
const result = await dispatchCodexNativeHook({
|
|
1970
|
+
hook_event_name: "Stop",
|
|
1971
|
+
cwd,
|
|
1972
|
+
session_id: "sess-stop-auto-inactive-mode",
|
|
1973
|
+
thread_id: "thread-stop-auto-inactive-mode",
|
|
1974
|
+
turn_id: "turn-stop-auto-inactive-mode-1",
|
|
1975
|
+
last_assistant_message: "Keep going and finish the cleanup.",
|
|
1976
|
+
}, { cwd });
|
|
1977
|
+
assert.equal(result.omxEventName, "stop");
|
|
1978
|
+
assert.deepEqual(result.outputJson, {
|
|
1979
|
+
decision: "block",
|
|
1980
|
+
reason: DEFAULT_AUTO_NUDGE_RESPONSE,
|
|
1470
1981
|
stopReason: "auto_nudge",
|
|
1471
1982
|
systemMessage: "OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.",
|
|
1472
1983
|
});
|
|
@@ -1519,5 +2030,146 @@ esac
|
|
|
1519
2030
|
await rm(cwd, { recursive: true, force: true });
|
|
1520
2031
|
}
|
|
1521
2032
|
});
|
|
2033
|
+
it("does not block Stop from another session's stale root team state when no scoped team state exists", async () => {
|
|
2034
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-team-"));
|
|
2035
|
+
try {
|
|
2036
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
2037
|
+
await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true });
|
|
2038
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" });
|
|
2039
|
+
await writeJson(join(stateDir, "team-state.json"), {
|
|
2040
|
+
active: true,
|
|
2041
|
+
current_phase: "starting",
|
|
2042
|
+
team_name: "stale-root-team",
|
|
2043
|
+
session_id: "sess-other",
|
|
2044
|
+
});
|
|
2045
|
+
await writeJson(join(stateDir, "team", "stale-root-team", "phase.json"), {
|
|
2046
|
+
current_phase: "team-exec",
|
|
2047
|
+
max_fix_attempts: 3,
|
|
2048
|
+
current_fix_attempt: 0,
|
|
2049
|
+
transitions: [],
|
|
2050
|
+
updated_at: new Date().toISOString(),
|
|
2051
|
+
});
|
|
2052
|
+
const result = await dispatchCodexNativeHook({
|
|
2053
|
+
hook_event_name: "Stop",
|
|
2054
|
+
cwd,
|
|
2055
|
+
session_id: "sess-current",
|
|
2056
|
+
}, { cwd });
|
|
2057
|
+
assert.equal(result.omxEventName, "stop");
|
|
2058
|
+
assert.equal(result.outputJson, null);
|
|
2059
|
+
}
|
|
2060
|
+
finally {
|
|
2061
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2062
|
+
}
|
|
2063
|
+
});
|
|
2064
|
+
it("does not block Stop from orphaned team mode state after cleanup removed canonical team artifacts", async () => {
|
|
2065
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-orphaned-team-state-"));
|
|
2066
|
+
try {
|
|
2067
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
2068
|
+
await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true });
|
|
2069
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" });
|
|
2070
|
+
await writeJson(join(stateDir, "team-state.json"), {
|
|
2071
|
+
active: true,
|
|
2072
|
+
current_phase: "starting",
|
|
2073
|
+
team_name: "cleaned-team",
|
|
2074
|
+
session_id: "sess-current",
|
|
2075
|
+
});
|
|
2076
|
+
const result = await dispatchCodexNativeHook({
|
|
2077
|
+
hook_event_name: "Stop",
|
|
2078
|
+
cwd,
|
|
2079
|
+
session_id: "sess-current",
|
|
2080
|
+
}, { cwd });
|
|
2081
|
+
assert.equal(result.omxEventName, "stop");
|
|
2082
|
+
assert.equal(result.outputJson, null);
|
|
2083
|
+
}
|
|
2084
|
+
finally {
|
|
2085
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2086
|
+
}
|
|
2087
|
+
});
|
|
2088
|
+
it("prefers the current session team state over a stale root team fallback during Stop", async () => {
|
|
2089
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-current-session-team-preferred-"));
|
|
2090
|
+
try {
|
|
2091
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
2092
|
+
await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true });
|
|
2093
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" });
|
|
2094
|
+
await writeJson(join(stateDir, "sessions", "sess-current", "team-state.json"), {
|
|
2095
|
+
active: true,
|
|
2096
|
+
current_phase: "starting",
|
|
2097
|
+
team_name: "current-team",
|
|
2098
|
+
session_id: "sess-current",
|
|
2099
|
+
});
|
|
2100
|
+
await writeJson(join(stateDir, "team", "current-team", "phase.json"), {
|
|
2101
|
+
current_phase: "team-verify",
|
|
2102
|
+
max_fix_attempts: 3,
|
|
2103
|
+
current_fix_attempt: 1,
|
|
2104
|
+
transitions: [],
|
|
2105
|
+
updated_at: new Date().toISOString(),
|
|
2106
|
+
});
|
|
2107
|
+
await writeJson(join(stateDir, "team-state.json"), {
|
|
2108
|
+
active: true,
|
|
2109
|
+
current_phase: "starting",
|
|
2110
|
+
team_name: "stale-root-team",
|
|
2111
|
+
session_id: "sess-other",
|
|
2112
|
+
});
|
|
2113
|
+
await writeJson(join(stateDir, "team", "stale-root-team", "phase.json"), {
|
|
2114
|
+
current_phase: "team-exec",
|
|
2115
|
+
max_fix_attempts: 3,
|
|
2116
|
+
current_fix_attempt: 0,
|
|
2117
|
+
transitions: [],
|
|
2118
|
+
updated_at: new Date().toISOString(),
|
|
2119
|
+
});
|
|
2120
|
+
const result = await dispatchCodexNativeHook({
|
|
2121
|
+
hook_event_name: "Stop",
|
|
2122
|
+
cwd,
|
|
2123
|
+
session_id: "sess-current",
|
|
2124
|
+
}, { cwd });
|
|
2125
|
+
assert.equal(result.omxEventName, "stop");
|
|
2126
|
+
assert.deepEqual(result.outputJson, {
|
|
2127
|
+
decision: "block",
|
|
2128
|
+
reason: `OMX team pipeline is still active (current-team) at phase team-verify; continue coordinating until the team reaches a terminal phase.${TEAM_STOP_COMMIT_GUIDANCE}`,
|
|
2129
|
+
stopReason: "team_team-verify",
|
|
2130
|
+
systemMessage: "OMX team pipeline is still active at phase team-verify.",
|
|
2131
|
+
});
|
|
2132
|
+
}
|
|
2133
|
+
finally {
|
|
2134
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2135
|
+
}
|
|
2136
|
+
});
|
|
2137
|
+
it("does not fall back to active root team state when the current scoped team state is inactive", async () => {
|
|
2138
|
+
const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-inactive-scoped-team-"));
|
|
2139
|
+
try {
|
|
2140
|
+
const stateDir = join(cwd, ".omx", "state");
|
|
2141
|
+
await mkdir(join(stateDir, "sessions", "sess-current"), { recursive: true });
|
|
2142
|
+
await writeJson(join(stateDir, "session.json"), { session_id: "sess-current" });
|
|
2143
|
+
await writeJson(join(stateDir, "sessions", "sess-current", "team-state.json"), {
|
|
2144
|
+
active: false,
|
|
2145
|
+
current_phase: "complete",
|
|
2146
|
+
team_name: "scoped-finished-team",
|
|
2147
|
+
session_id: "sess-current",
|
|
2148
|
+
});
|
|
2149
|
+
await writeJson(join(stateDir, "team-state.json"), {
|
|
2150
|
+
active: true,
|
|
2151
|
+
current_phase: "starting",
|
|
2152
|
+
team_name: "root-fallback-team",
|
|
2153
|
+
session_id: "sess-current",
|
|
2154
|
+
});
|
|
2155
|
+
await writeJson(join(stateDir, "team", "root-fallback-team", "phase.json"), {
|
|
2156
|
+
current_phase: "team-exec",
|
|
2157
|
+
max_fix_attempts: 3,
|
|
2158
|
+
current_fix_attempt: 0,
|
|
2159
|
+
transitions: [],
|
|
2160
|
+
updated_at: new Date().toISOString(),
|
|
2161
|
+
});
|
|
2162
|
+
const result = await dispatchCodexNativeHook({
|
|
2163
|
+
hook_event_name: "Stop",
|
|
2164
|
+
cwd,
|
|
2165
|
+
session_id: "sess-current",
|
|
2166
|
+
}, { cwd });
|
|
2167
|
+
assert.equal(result.omxEventName, "stop");
|
|
2168
|
+
assert.equal(result.outputJson, null);
|
|
2169
|
+
}
|
|
2170
|
+
finally {
|
|
2171
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2172
|
+
}
|
|
2173
|
+
});
|
|
1522
2174
|
});
|
|
1523
2175
|
//# sourceMappingURL=codex-native-hook.test.js.map
|