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
|
@@ -2,13 +2,14 @@ import { execFileSync } from "child_process";
|
|
|
2
2
|
import { existsSync, readFileSync } from "fs";
|
|
3
3
|
import { mkdir, readFile, readdir, writeFile } from "fs/promises";
|
|
4
4
|
import { join, resolve } from "path";
|
|
5
|
-
import { readModeState, updateModeState } from "../modes/base.js";
|
|
5
|
+
import { readModeState, readModeStateForSession, updateModeState } from "../modes/base.js";
|
|
6
6
|
import {
|
|
7
7
|
listActiveSkills,
|
|
8
8
|
readVisibleSkillActiveState,
|
|
9
9
|
} from "../state/skill-active.js";
|
|
10
10
|
import { readSubagentSessionSummary } from "../subagents/tracker.js";
|
|
11
11
|
import { resolveCanonicalTeamStateRoot } from "../team/state-root.js";
|
|
12
|
+
import { readUsableSessionState, reconcileNativeSessionStart } from "../hooks/session.js";
|
|
12
13
|
import {
|
|
13
14
|
appendTeamEvent,
|
|
14
15
|
readTeamLeaderAttention,
|
|
@@ -17,8 +18,10 @@ import {
|
|
|
17
18
|
writeTeamLeaderAttention,
|
|
18
19
|
writeTeamPhase,
|
|
19
20
|
} from "../team/state.js";
|
|
20
|
-
import { omxNotepadPath, omxProjectMemoryPath
|
|
21
|
+
import { omxNotepadPath, omxProjectMemoryPath } from "../utils/paths.js";
|
|
22
|
+
import { getStateFilePath } from "../mcp/state-paths.js";
|
|
21
23
|
import {
|
|
24
|
+
detectKeywords,
|
|
22
25
|
detectPrimaryKeyword,
|
|
23
26
|
recordSkillActivation,
|
|
24
27
|
type SkillActiveState,
|
|
@@ -27,6 +30,7 @@ import {
|
|
|
27
30
|
detectStallPattern,
|
|
28
31
|
loadAutoNudgeConfig,
|
|
29
32
|
normalizeAutoNudgeSignatureText,
|
|
33
|
+
resolveEffectiveAutoNudgeResponse,
|
|
30
34
|
} from "./notify-hook/auto-nudge.js";
|
|
31
35
|
import {
|
|
32
36
|
buildNativePostToolUseOutput,
|
|
@@ -38,8 +42,8 @@ import {
|
|
|
38
42
|
} from "../hooks/extensibility/events.js";
|
|
39
43
|
import type { HookEventEnvelope } from "../hooks/extensibility/types.js";
|
|
40
44
|
import { dispatchHookEvent } from "../hooks/extensibility/dispatcher.js";
|
|
41
|
-
import { writeSessionStart } from "../hooks/session.js";
|
|
42
45
|
import { reconcileHudForPromptSubmit } from "../hud/reconcile.js";
|
|
46
|
+
import { onSessionStart as buildWikiSessionStartContext } from "../wiki/lifecycle.js";
|
|
43
47
|
|
|
44
48
|
type CodexHookEventName =
|
|
45
49
|
| "SessionStart"
|
|
@@ -64,9 +68,17 @@ export interface NativeHookDispatchResult {
|
|
|
64
68
|
|
|
65
69
|
const TERMINAL_RALPH_PHASES = new Set(["complete", "failed", "cancelled"]);
|
|
66
70
|
const TERMINAL_MODE_PHASES = new Set(["complete", "failed", "cancelled"]);
|
|
67
|
-
const SKILL_STOP_BLOCKERS = new Set(["ralplan"
|
|
71
|
+
const SKILL_STOP_BLOCKERS = new Set(["ralplan"]);
|
|
68
72
|
const TEAM_TERMINAL_TASK_STATUSES = new Set(["completed", "failed"]);
|
|
69
73
|
const NATIVE_STOP_STATE_FILE = "native-stop-state.json";
|
|
74
|
+
const STABLE_FINAL_RECOMMENDATION_PATTERNS = [
|
|
75
|
+
/^\s*(?:launch|release|ship)-?ready\s*:\s*(?:yes|no)\b[^\n\r]*/im,
|
|
76
|
+
/^\s*ready to release\s*:\s*(?:yes|no)\b[^\n\r]*/im,
|
|
77
|
+
/^\s*(?:final\s+)?recommendation\s*:\s*(?:yes|no|ship|hold|release|do not release|proceed|do not proceed)\b[^\n\r]*/im,
|
|
78
|
+
/^\s*decision\s*:\s*(?:yes|no|ship|hold|release|do not release|proceed|do not proceed)\b[^\n\r]*/im,
|
|
79
|
+
] as const;
|
|
80
|
+
const RELEASE_READINESS_FINALIZE_SYSTEM_MESSAGE =
|
|
81
|
+
"OMX release-readiness detected a stable final recommendation with no active worker tasks; emit one concise final decision summary and finalize.";
|
|
70
82
|
|
|
71
83
|
function safeString(value: unknown): string {
|
|
72
84
|
return typeof value === "string" ? value : "";
|
|
@@ -138,6 +150,21 @@ function readPromptText(payload: CodexHookPayload): string {
|
|
|
138
150
|
return "";
|
|
139
151
|
}
|
|
140
152
|
|
|
153
|
+
function sanitizePayloadForHookContext(
|
|
154
|
+
payload: CodexHookPayload,
|
|
155
|
+
hookEventName: CodexHookEventName,
|
|
156
|
+
): CodexHookPayload {
|
|
157
|
+
if (hookEventName !== "UserPromptSubmit") return payload;
|
|
158
|
+
|
|
159
|
+
const sanitized = { ...payload };
|
|
160
|
+
delete sanitized.prompt;
|
|
161
|
+
delete sanitized.input;
|
|
162
|
+
delete sanitized.user_prompt;
|
|
163
|
+
delete sanitized.userPrompt;
|
|
164
|
+
delete sanitized.text;
|
|
165
|
+
return sanitized;
|
|
166
|
+
}
|
|
167
|
+
|
|
141
168
|
function buildBaseContext(
|
|
142
169
|
cwd: string,
|
|
143
170
|
payload: CodexHookPayload,
|
|
@@ -148,10 +175,7 @@ function buildBaseContext(
|
|
|
148
175
|
project_path: cwd,
|
|
149
176
|
transcript_path: safeString(payload.transcript_path ?? payload.transcriptPath) || null,
|
|
150
177
|
source: safeString(payload.source),
|
|
151
|
-
payload,
|
|
152
|
-
...(hookEventName === "UserPromptSubmit"
|
|
153
|
-
? { prompt: readPromptText(payload) }
|
|
154
|
-
: {}),
|
|
178
|
+
payload: sanitizePayloadForHookContext(payload, hookEventName),
|
|
155
179
|
};
|
|
156
180
|
}
|
|
157
181
|
|
|
@@ -174,12 +198,20 @@ function formatPhase(value: unknown, fallback = "active"): string {
|
|
|
174
198
|
return phase || fallback;
|
|
175
199
|
}
|
|
176
200
|
|
|
177
|
-
async function readActiveRalphState(
|
|
178
|
-
|
|
201
|
+
async function readActiveRalphState(
|
|
202
|
+
stateDir: string,
|
|
203
|
+
preferredSessionId?: string,
|
|
204
|
+
): Promise<Record<string, unknown> | null> {
|
|
205
|
+
const sessionInfo = await readUsableSessionState(resolve(stateDir, "..", ".."));
|
|
179
206
|
const currentOmxSessionId = safeString(sessionInfo?.session_id).trim();
|
|
180
|
-
|
|
207
|
+
const sessionCandidates = [...new Set([
|
|
208
|
+
safeString(preferredSessionId).trim(),
|
|
209
|
+
currentOmxSessionId,
|
|
210
|
+
].filter(Boolean))];
|
|
211
|
+
|
|
212
|
+
for (const sessionId of sessionCandidates) {
|
|
181
213
|
const sessionScoped = await readJsonIfExists(
|
|
182
|
-
join(stateDir, "sessions",
|
|
214
|
+
join(stateDir, "sessions", sessionId, "ralph-state.json"),
|
|
183
215
|
);
|
|
184
216
|
if (
|
|
185
217
|
sessionScoped?.active === true
|
|
@@ -191,13 +223,13 @@ async function readActiveRalphState(stateDir: string): Promise<Record<string, un
|
|
|
191
223
|
}
|
|
192
224
|
}
|
|
193
225
|
|
|
226
|
+
if (sessionCandidates.length > 0) return null;
|
|
227
|
+
|
|
194
228
|
const direct = await readJsonIfExists(join(stateDir, "ralph-state.json"));
|
|
195
229
|
if (direct?.active === true && !TERMINAL_RALPH_PHASES.has(safeString(direct.current_phase).trim().toLowerCase())) {
|
|
196
230
|
return direct;
|
|
197
231
|
}
|
|
198
232
|
|
|
199
|
-
if (currentOmxSessionId) return null;
|
|
200
|
-
|
|
201
233
|
const sessionsRoot = join(stateDir, "sessions");
|
|
202
234
|
if (!existsSync(sessionsRoot)) return null;
|
|
203
235
|
const entries = await readdir(sessionsRoot, { withFileTypes: true }).catch(() => []);
|
|
@@ -415,6 +447,11 @@ async function buildSessionStartContext(
|
|
|
415
447
|
}
|
|
416
448
|
}
|
|
417
449
|
|
|
450
|
+
const wikiContext = buildWikiSessionStartContext({ cwd });
|
|
451
|
+
if (wikiContext.additionalContext) {
|
|
452
|
+
sections.push(wikiContext.additionalContext);
|
|
453
|
+
}
|
|
454
|
+
|
|
418
455
|
const subagentSummary = await readSubagentSessionSummary(cwd, sessionId).catch(() => null);
|
|
419
456
|
if (subagentSummary && subagentSummary.activeSubagentThreadIds.length > 0) {
|
|
420
457
|
sections.push(`[Subagents]\n- active subagent threads: ${subagentSummary.activeSubagentThreadIds.length}`);
|
|
@@ -425,31 +462,84 @@ async function buildSessionStartContext(
|
|
|
425
462
|
|
|
426
463
|
function buildAdditionalContextMessage(prompt: string, skillState?: SkillActiveState | null): string | null {
|
|
427
464
|
if (!prompt) return null;
|
|
465
|
+
const matches = detectKeywords(prompt);
|
|
428
466
|
const match = detectPrimaryKeyword(prompt);
|
|
429
467
|
if (!match) return null;
|
|
468
|
+
const detectedKeywordMessage = matches.length > 1
|
|
469
|
+
? `OMX native UserPromptSubmit detected workflow keywords ${matches.map((entry) => `"${entry.keyword}" -> ${entry.skill}`).join(", ")}.`
|
|
470
|
+
: `OMX native UserPromptSubmit detected workflow keyword "${match.keyword}" -> ${match.skill}.`;
|
|
471
|
+
const activeSkills = Array.isArray(skillState?.active_skills)
|
|
472
|
+
? skillState.active_skills.map((entry) => entry.skill)
|
|
473
|
+
: [];
|
|
474
|
+
const deferredSkills = Array.isArray(skillState?.deferred_skills)
|
|
475
|
+
? skillState.deferred_skills
|
|
476
|
+
: [];
|
|
477
|
+
const teamDetected = activeSkills.includes("team");
|
|
478
|
+
const combinedTransitionMessage = (() => {
|
|
479
|
+
if (!skillState?.transition_message) return null;
|
|
480
|
+
if (matches.length <= 1 || activeSkills.length <= 1) return skillState.transition_message;
|
|
481
|
+
const source = skillState.transition_message.match(/^mode transiting: (.+?) -> /)?.[1];
|
|
482
|
+
if (!source) return skillState.transition_message;
|
|
483
|
+
return `mode transiting: ${source} -> ${activeSkills.join(" + ")}`;
|
|
484
|
+
})();
|
|
485
|
+
|
|
486
|
+
if (skillState?.transition_error) {
|
|
487
|
+
return [
|
|
488
|
+
`OMX native UserPromptSubmit denied workflow keyword "${match.keyword}" -> ${match.skill}.`,
|
|
489
|
+
skillState.transition_error,
|
|
490
|
+
'Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.',
|
|
491
|
+
].join(' ');
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (skillState?.transition_message) {
|
|
495
|
+
return [
|
|
496
|
+
detectedKeywordMessage,
|
|
497
|
+
combinedTransitionMessage,
|
|
498
|
+
activeSkills.length > 1 ? `active skills: ${activeSkills.join(", ")}.` : null,
|
|
499
|
+
deferredSkills.length > 0
|
|
500
|
+
? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
|
|
501
|
+
: null,
|
|
502
|
+
skillState.initialized_mode && skillState.initialized_state_path
|
|
503
|
+
? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
|
|
504
|
+
: null,
|
|
505
|
+
teamDetected
|
|
506
|
+
? "Use the durable OMX team runtime via `omx team ...` for coordinated execution; do not replace it with in-process fanout."
|
|
507
|
+
: null,
|
|
508
|
+
teamDetected ? "If you need help, run `omx team --help`." : null,
|
|
509
|
+
'Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.',
|
|
510
|
+
].filter(Boolean).join(' ');
|
|
511
|
+
}
|
|
430
512
|
|
|
431
|
-
if (
|
|
513
|
+
if (teamDetected) {
|
|
432
514
|
const initializedStateMessage = skillState?.initialized_mode && skillState.initialized_state_path
|
|
433
515
|
? `skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`
|
|
434
516
|
: null;
|
|
435
517
|
return [
|
|
436
|
-
|
|
518
|
+
detectedKeywordMessage,
|
|
519
|
+
activeSkills.length > 1 ? `active skills: ${activeSkills.join(", ")}.` : null,
|
|
520
|
+
deferredSkills.length > 0
|
|
521
|
+
? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
|
|
522
|
+
: null,
|
|
437
523
|
initializedStateMessage,
|
|
438
524
|
"Use the durable OMX team runtime via `omx team ...` for coordinated execution; do not replace it with in-process fanout.",
|
|
439
525
|
"If you need help, run `omx team --help`.",
|
|
440
|
-
"Follow AGENTS.md routing and preserve
|
|
526
|
+
"Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
|
|
441
527
|
].filter(Boolean).join(" ");
|
|
442
528
|
}
|
|
443
529
|
|
|
444
530
|
if (skillState?.initialized_mode && skillState.initialized_state_path) {
|
|
445
531
|
return [
|
|
446
|
-
|
|
532
|
+
detectedKeywordMessage,
|
|
533
|
+
activeSkills.length > 1 ? `active skills: ${activeSkills.join(", ")}.` : null,
|
|
534
|
+
deferredSkills.length > 0
|
|
535
|
+
? `planning preserved over simultaneous execution follow-up; deferred skills: ${deferredSkills.join(", ")}.`
|
|
536
|
+
: null,
|
|
447
537
|
`skill: ${skillState.initialized_mode} activated and initial state initialized at ${skillState.initialized_state_path}; write subsequent updates via omx_state MCP.`,
|
|
448
|
-
"Follow AGENTS.md routing and preserve
|
|
538
|
+
"Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
|
|
449
539
|
].join(" ");
|
|
450
540
|
}
|
|
451
541
|
|
|
452
|
-
return
|
|
542
|
+
return `${detectedKeywordMessage} Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.`;
|
|
453
543
|
}
|
|
454
544
|
|
|
455
545
|
function parseTeamWorkerEnv(rawValue: string): { teamName: string; workerName: string } | null {
|
|
@@ -570,8 +660,11 @@ function isStopExempt(payload: CodexHookPayload): boolean {
|
|
|
570
660
|
async function buildModeBasedStopOutput(
|
|
571
661
|
mode: "autopilot" | "ultrawork" | "ultraqa",
|
|
572
662
|
cwd: string,
|
|
663
|
+
sessionId?: string,
|
|
573
664
|
): Promise<Record<string, unknown> | null> {
|
|
574
|
-
const state =
|
|
665
|
+
const state = sessionId
|
|
666
|
+
? await readModeStateForSession(mode, sessionId, cwd)
|
|
667
|
+
: await readModeState(mode, cwd);
|
|
575
668
|
if (state?.active !== true || !isNonTerminalPhase(state.current_phase)) return null;
|
|
576
669
|
const phase = formatPhase(state.current_phase);
|
|
577
670
|
return {
|
|
@@ -582,10 +675,39 @@ async function buildModeBasedStopOutput(
|
|
|
582
675
|
};
|
|
583
676
|
}
|
|
584
677
|
|
|
585
|
-
async function
|
|
586
|
-
|
|
678
|
+
async function readTeamModeStateForStop(
|
|
679
|
+
cwd: string,
|
|
680
|
+
sessionId?: string,
|
|
681
|
+
): Promise<Record<string, unknown> | null> {
|
|
682
|
+
const normalizedSessionId = safeString(sessionId).trim();
|
|
683
|
+
if (!normalizedSessionId) {
|
|
684
|
+
return await readModeState("team", cwd);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const scopedState = await readStopSessionPinnedState("team-state.json", cwd, normalizedSessionId);
|
|
688
|
+
if (scopedState) return scopedState;
|
|
689
|
+
|
|
690
|
+
const rootState = await readJsonIfExists(join(cwd, ".omx", "state", "team-state.json"));
|
|
691
|
+
if (rootState?.active !== true) return null;
|
|
692
|
+
|
|
693
|
+
const ownerSessionId = safeString(rootState.session_id).trim();
|
|
694
|
+
if (ownerSessionId && ownerSessionId !== normalizedSessionId) {
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
return rootState;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
async function buildTeamStopOutput(cwd: string, sessionId?: string): Promise<Record<string, unknown> | null> {
|
|
702
|
+
const teamState = await readTeamModeStateForStop(cwd, sessionId);
|
|
587
703
|
if (teamState?.active !== true) return null;
|
|
588
704
|
const teamName = safeString(teamState.team_name).trim();
|
|
705
|
+
if (teamName) {
|
|
706
|
+
const canonicalTeamDir = join(resolveCanonicalTeamStateRoot(cwd), "team", teamName);
|
|
707
|
+
if (!existsSync(canonicalTeamDir)) {
|
|
708
|
+
return null;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
589
711
|
const coarsePhase = teamState.current_phase;
|
|
590
712
|
const canonicalPhase = teamName ? (await readTeamPhase(teamName, cwd))?.current_phase ?? coarsePhase : coarsePhase;
|
|
591
713
|
if (!isNonTerminalPhase(canonicalPhase)) return null;
|
|
@@ -606,6 +728,54 @@ function buildTeamStopOutputForPhase(teamName: string, phase: string): Record<st
|
|
|
606
728
|
};
|
|
607
729
|
}
|
|
608
730
|
|
|
731
|
+
function extractStableFinalRecommendationSummary(message: string): string {
|
|
732
|
+
for (const pattern of STABLE_FINAL_RECOMMENDATION_PATTERNS) {
|
|
733
|
+
const match = pattern.exec(message);
|
|
734
|
+
if (!match) continue;
|
|
735
|
+
const summary = match[0]?.trim().replace(/\s+/g, " ");
|
|
736
|
+
if (!summary) continue;
|
|
737
|
+
return /[.!?]$/.test(summary) ? summary : `${summary}.`;
|
|
738
|
+
}
|
|
739
|
+
return "";
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function buildStableFinalRecommendationStopSignature(
|
|
743
|
+
payload: CodexHookPayload,
|
|
744
|
+
teamName: string,
|
|
745
|
+
summary: string,
|
|
746
|
+
): string {
|
|
747
|
+
const sessionId = readPayloadSessionId(payload) || "no-session";
|
|
748
|
+
const threadId = readPayloadThreadId(payload) || "no-thread";
|
|
749
|
+
const normalizedSummary = normalizeAutoNudgeSignatureText(summary) || summary.toLowerCase();
|
|
750
|
+
return ["release-readiness-finalize", sessionId, threadId, teamName, normalizedSummary].join("|");
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function hasReleaseReadinessMode(payload: CodexHookPayload): boolean {
|
|
754
|
+
const mode = safeString(payload.mode).trim().toLowerCase();
|
|
755
|
+
return mode === "release-readiness";
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
async function hasReleaseReadinessStopMarker(
|
|
759
|
+
cwd: string,
|
|
760
|
+
sessionId: string,
|
|
761
|
+
teamName: string,
|
|
762
|
+
): Promise<boolean> {
|
|
763
|
+
if (!sessionId) return false;
|
|
764
|
+
|
|
765
|
+
const markerState = await readStopSessionPinnedState("release-readiness-state.json", cwd, sessionId);
|
|
766
|
+
if (markerState?.active !== true || markerState.stable_final_recommendation_emitted !== true) {
|
|
767
|
+
return false;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
const markerTeamName = safeString(markerState.team_name).trim();
|
|
771
|
+
if (markerTeamName && markerTeamName !== teamName) return false;
|
|
772
|
+
|
|
773
|
+
const markerSessionId = safeString(markerState.session_id).trim();
|
|
774
|
+
if (markerSessionId && markerSessionId !== sessionId) return false;
|
|
775
|
+
|
|
776
|
+
return true;
|
|
777
|
+
}
|
|
778
|
+
|
|
609
779
|
function readPayloadSessionId(payload: CodexHookPayload): string {
|
|
610
780
|
return safeString(payload.session_id ?? payload.sessionId).trim();
|
|
611
781
|
}
|
|
@@ -618,29 +788,19 @@ function readPayloadTurnId(payload: CodexHookPayload): string {
|
|
|
618
788
|
return safeString(payload.turn_id ?? payload.turnId).trim();
|
|
619
789
|
}
|
|
620
790
|
|
|
621
|
-
async function
|
|
791
|
+
async function resolveInternalSessionIdForPayload(
|
|
622
792
|
cwd: string,
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
const
|
|
627
|
-
if (
|
|
628
|
-
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
))
|
|
635
|
-
: null;
|
|
636
|
-
if (
|
|
637
|
-
deepInterviewEntry
|
|
638
|
-
&& safeObject(canonicalState?.input_lock).active === true
|
|
639
|
-
) {
|
|
640
|
-
return true;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
return (await readBlockingSkillForStop(cwd, sessionId, threadId, "deep-interview")) !== null;
|
|
793
|
+
payloadSessionId: string,
|
|
794
|
+
): Promise<string> {
|
|
795
|
+
const currentSession = await readUsableSessionState(cwd);
|
|
796
|
+
const canonicalSessionId = safeString(currentSession?.session_id).trim();
|
|
797
|
+
if (!canonicalSessionId) return payloadSessionId;
|
|
798
|
+
|
|
799
|
+
const nativeSessionId = safeString(currentSession?.native_session_id).trim();
|
|
800
|
+
if (!payloadSessionId) return canonicalSessionId;
|
|
801
|
+
if (payloadSessionId === canonicalSessionId) return canonicalSessionId;
|
|
802
|
+
if (nativeSessionId && payloadSessionId === nativeSessionId) return canonicalSessionId;
|
|
803
|
+
return payloadSessionId;
|
|
644
804
|
}
|
|
645
805
|
|
|
646
806
|
async function readStopSessionPinnedState(
|
|
@@ -648,10 +808,7 @@ async function readStopSessionPinnedState(
|
|
|
648
808
|
cwd: string,
|
|
649
809
|
sessionId: string,
|
|
650
810
|
): Promise<Record<string, unknown> | null> {
|
|
651
|
-
const
|
|
652
|
-
const statePath = sessionId
|
|
653
|
-
? join(stateDir, "sessions", sessionId, fileName)
|
|
654
|
-
: join(stateDir, fileName);
|
|
811
|
+
const statePath = getStateFilePath(fileName, cwd, sessionId || undefined);
|
|
655
812
|
return readJsonIfExists(statePath);
|
|
656
813
|
}
|
|
657
814
|
|
|
@@ -716,6 +873,28 @@ async function readBlockingSkillForStop(
|
|
|
716
873
|
return null;
|
|
717
874
|
}
|
|
718
875
|
|
|
876
|
+
async function readStopAutoNudgePhase(
|
|
877
|
+
cwd: string,
|
|
878
|
+
sessionId: string,
|
|
879
|
+
threadId: string,
|
|
880
|
+
): Promise<string> {
|
|
881
|
+
if (!sessionId.trim()) return "";
|
|
882
|
+
|
|
883
|
+
const canonicalState = await readVisibleSkillActiveState(cwd, sessionId);
|
|
884
|
+
const visibleEntries = canonicalState ? listActiveSkills(canonicalState) : [];
|
|
885
|
+
const deepInterview = visibleEntries.find((entry) => (
|
|
886
|
+
entry.skill === "deep-interview"
|
|
887
|
+
&& matchesSkillStopContext(entry, canonicalState ?? {}, sessionId, threadId)
|
|
888
|
+
));
|
|
889
|
+
if (!deepInterview) return "";
|
|
890
|
+
|
|
891
|
+
const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId);
|
|
892
|
+
if (!modeState || modeState.active !== true) return "";
|
|
893
|
+
|
|
894
|
+
const modePhase = safeString(modeState.current_phase).trim().toLowerCase();
|
|
895
|
+
return modePhase === "intent-first" ? "planning" : "";
|
|
896
|
+
}
|
|
897
|
+
|
|
719
898
|
function buildRepeatableStopSignature(
|
|
720
899
|
payload: CodexHookPayload,
|
|
721
900
|
kind: string,
|
|
@@ -750,10 +929,6 @@ function buildRepeatableStopSignature(
|
|
|
750
929
|
].join("|");
|
|
751
930
|
}
|
|
752
931
|
|
|
753
|
-
async function readNativeStopState(stateDir: string): Promise<Record<string, unknown>> {
|
|
754
|
-
return await readJsonIfExists(join(stateDir, NATIVE_STOP_STATE_FILE)) ?? {};
|
|
755
|
-
}
|
|
756
|
-
|
|
757
932
|
function readNativeStopSessionKey(payload: CodexHookPayload): string {
|
|
758
933
|
return readPayloadSessionId(payload) || readPayloadThreadId(payload) || "global";
|
|
759
934
|
}
|
|
@@ -773,7 +948,8 @@ async function persistNativeStopSignature(
|
|
|
773
948
|
signature: string,
|
|
774
949
|
): Promise<void> {
|
|
775
950
|
if (!signature) return;
|
|
776
|
-
const
|
|
951
|
+
const statePath = join(stateDir, NATIVE_STOP_STATE_FILE);
|
|
952
|
+
const state = await readJsonIfExists(statePath) ?? {};
|
|
777
953
|
const sessions = safeObject(state.sessions);
|
|
778
954
|
const sessionKey = readNativeStopSessionKey(payload);
|
|
779
955
|
sessions[sessionKey] = {
|
|
@@ -781,7 +957,8 @@ async function persistNativeStopSignature(
|
|
|
781
957
|
last_signature: signature,
|
|
782
958
|
updated_at: new Date().toISOString(),
|
|
783
959
|
};
|
|
784
|
-
await
|
|
960
|
+
await mkdir(stateDir, { recursive: true });
|
|
961
|
+
await writeFile(statePath, JSON.stringify({
|
|
785
962
|
...state,
|
|
786
963
|
sessions,
|
|
787
964
|
}, null, 2));
|
|
@@ -796,7 +973,7 @@ async function maybeReturnRepeatableStopOutput(
|
|
|
796
973
|
if (!output) return null;
|
|
797
974
|
const stopHookActive = payload.stop_hook_active === true || payload.stopHookActive === true;
|
|
798
975
|
if (stopHookActive) {
|
|
799
|
-
const state = await
|
|
976
|
+
const state = await readJsonIfExists(join(stateDir, NATIVE_STOP_STATE_FILE)) ?? {};
|
|
800
977
|
const previousSignature = readPreviousNativeStopSignature(state, readNativeStopSessionKey(payload));
|
|
801
978
|
if (!signature || previousSignature === signature) {
|
|
802
979
|
return null;
|
|
@@ -838,6 +1015,66 @@ async function findCanonicalActiveTeamForSession(
|
|
|
838
1015
|
return null;
|
|
839
1016
|
}
|
|
840
1017
|
|
|
1018
|
+
async function resolveActiveTeamNameForStop(
|
|
1019
|
+
cwd: string,
|
|
1020
|
+
sessionId: string,
|
|
1021
|
+
): Promise<string> {
|
|
1022
|
+
const directState = await readTeamModeStateForStop(cwd, sessionId);
|
|
1023
|
+
const directTeamName = safeString(directState?.team_name).trim();
|
|
1024
|
+
if (directState?.active === true && directTeamName) return directTeamName;
|
|
1025
|
+
|
|
1026
|
+
const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, sessionId);
|
|
1027
|
+
return canonicalTeam?.teamName ?? "";
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
async function maybeBuildReleaseReadinessFinalizeStopOutput(
|
|
1031
|
+
payload: CodexHookPayload,
|
|
1032
|
+
cwd: string,
|
|
1033
|
+
stateDir: string,
|
|
1034
|
+
sessionId: string,
|
|
1035
|
+
): Promise<{ matched: boolean; output: Record<string, unknown> | null }> {
|
|
1036
|
+
if (!sessionId) return { matched: false, output: null };
|
|
1037
|
+
|
|
1038
|
+
const teamName = await resolveActiveTeamNameForStop(cwd, sessionId);
|
|
1039
|
+
if (!teamName) return { matched: false, output: null };
|
|
1040
|
+
|
|
1041
|
+
const explicitReleaseReadinessContext =
|
|
1042
|
+
hasReleaseReadinessMode(payload)
|
|
1043
|
+
|| await hasReleaseReadinessStopMarker(cwd, sessionId, teamName);
|
|
1044
|
+
if (!explicitReleaseReadinessContext) {
|
|
1045
|
+
return { matched: false, output: null };
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
const summary = extractStableFinalRecommendationSummary(
|
|
1049
|
+
safeString(payload.last_assistant_message ?? payload.lastAssistantMessage),
|
|
1050
|
+
);
|
|
1051
|
+
if (!summary) return { matched: false, output: null };
|
|
1052
|
+
|
|
1053
|
+
const leaderAttention = await readTeamLeaderAttention(teamName, cwd);
|
|
1054
|
+
if (
|
|
1055
|
+
!leaderAttention
|
|
1056
|
+
|| leaderAttention.leader_decision_state !== "done_waiting_on_leader"
|
|
1057
|
+
|| leaderAttention.work_remaining !== false
|
|
1058
|
+
) {
|
|
1059
|
+
return { matched: false, output: null };
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
const signature = buildStableFinalRecommendationStopSignature(payload, teamName, summary);
|
|
1063
|
+
const output = await maybeReturnRepeatableStopOutput(
|
|
1064
|
+
payload,
|
|
1065
|
+
stateDir,
|
|
1066
|
+
signature,
|
|
1067
|
+
{
|
|
1068
|
+
decision: "block",
|
|
1069
|
+
reason:
|
|
1070
|
+
`Stable final recommendation already reached with no active worker tasks. Emit exactly one concise final decision summary aligned to "${summary}" with no filler or residual acknowledgements (for example "yes"), then stop.`,
|
|
1071
|
+
stopReason: "release_readiness_auto_finalize",
|
|
1072
|
+
systemMessage: RELEASE_READINESS_FINALIZE_SYSTEM_MESSAGE,
|
|
1073
|
+
},
|
|
1074
|
+
);
|
|
1075
|
+
return { matched: true, output };
|
|
1076
|
+
}
|
|
1077
|
+
|
|
841
1078
|
async function buildSkillStopOutput(
|
|
842
1079
|
cwd: string,
|
|
843
1080
|
sessionId: string,
|
|
@@ -863,7 +1100,7 @@ async function findActiveTeamForTransportFailure(
|
|
|
863
1100
|
cwd: string,
|
|
864
1101
|
sessionId: string,
|
|
865
1102
|
): Promise<{ teamName: string; phase: string } | null> {
|
|
866
|
-
const teamState = await
|
|
1103
|
+
const teamState = await readModeStateForSession("team", sessionId, cwd);
|
|
867
1104
|
if (teamState?.active === true) {
|
|
868
1105
|
const teamName = safeString(teamState.team_name).trim();
|
|
869
1106
|
const coarsePhase = formatPhase(teamState.current_phase);
|
|
@@ -882,8 +1119,8 @@ async function markTeamTransportFailure(
|
|
|
882
1119
|
cwd: string,
|
|
883
1120
|
payload: CodexHookPayload,
|
|
884
1121
|
): Promise<void> {
|
|
885
|
-
const
|
|
886
|
-
const activeTeam = await findActiveTeamForTransportFailure(cwd,
|
|
1122
|
+
const canonicalSessionId = await resolveInternalSessionIdForPayload(cwd, readPayloadSessionId(payload));
|
|
1123
|
+
const activeTeam = await findActiveTeamForTransportFailure(cwd, canonicalSessionId);
|
|
887
1124
|
if (!activeTeam) return;
|
|
888
1125
|
|
|
889
1126
|
const nowIso = new Date().toISOString();
|
|
@@ -926,7 +1163,7 @@ async function markTeamTransportFailure(
|
|
|
926
1163
|
],
|
|
927
1164
|
leader_stale: existingAttention?.leader_stale ?? false,
|
|
928
1165
|
leader_session_active: existingAttention?.leader_session_active ?? true,
|
|
929
|
-
leader_session_id: existingAttention?.leader_session_id ?? (
|
|
1166
|
+
leader_session_id: existingAttention?.leader_session_id ?? (canonicalSessionId || null),
|
|
930
1167
|
leader_session_stopped_at: existingAttention?.leader_session_stopped_at ?? null,
|
|
931
1168
|
unread_leader_message_count: existingAttention?.unread_leader_message_count ?? 0,
|
|
932
1169
|
work_remaining: existingAttention?.work_remaining ?? true,
|
|
@@ -957,6 +1194,7 @@ async function markTeamTransportFailure(
|
|
|
957
1194
|
last_turn_at: nowIso,
|
|
958
1195
|
},
|
|
959
1196
|
cwd,
|
|
1197
|
+
canonicalSessionId || undefined,
|
|
960
1198
|
);
|
|
961
1199
|
} catch {
|
|
962
1200
|
// Canonical team state already carries the preserved failure for coarse-state-missing sessions.
|
|
@@ -973,30 +1211,39 @@ async function buildStopHookOutput(
|
|
|
973
1211
|
}
|
|
974
1212
|
|
|
975
1213
|
const sessionId = readPayloadSessionId(payload);
|
|
1214
|
+
const canonicalSessionId = await resolveInternalSessionIdForPayload(cwd, sessionId);
|
|
976
1215
|
const threadId = readPayloadThreadId(payload);
|
|
977
|
-
const ralphState = await readActiveRalphState(stateDir);
|
|
1216
|
+
const ralphState = await readActiveRalphState(stateDir, canonicalSessionId);
|
|
978
1217
|
const stopHookActive = payload.stop_hook_active === true || payload.stopHookActive === true;
|
|
979
1218
|
if (!ralphState) {
|
|
980
1219
|
const teamWorkerOutput = await buildTeamWorkerStopOutput(cwd);
|
|
981
1220
|
if (!stopHookActive && hasTeamWorkerContext()) return teamWorkerOutput;
|
|
982
1221
|
|
|
983
|
-
const autopilotOutput = await buildModeBasedStopOutput("autopilot", cwd);
|
|
1222
|
+
const autopilotOutput = await buildModeBasedStopOutput("autopilot", cwd, canonicalSessionId);
|
|
984
1223
|
if (!stopHookActive && autopilotOutput) return autopilotOutput;
|
|
985
1224
|
|
|
986
|
-
const ultraworkOutput = await buildModeBasedStopOutput("ultrawork", cwd);
|
|
1225
|
+
const ultraworkOutput = await buildModeBasedStopOutput("ultrawork", cwd, canonicalSessionId);
|
|
987
1226
|
if (!stopHookActive && ultraworkOutput) return ultraworkOutput;
|
|
988
1227
|
|
|
989
|
-
const ultraqaOutput = await buildModeBasedStopOutput("ultraqa", cwd);
|
|
1228
|
+
const ultraqaOutput = await buildModeBasedStopOutput("ultraqa", cwd, canonicalSessionId);
|
|
990
1229
|
if (!stopHookActive && ultraqaOutput) return ultraqaOutput;
|
|
991
1230
|
|
|
992
|
-
const
|
|
1231
|
+
const releaseReadinessFinalizeResult = await maybeBuildReleaseReadinessFinalizeStopOutput(
|
|
1232
|
+
payload,
|
|
1233
|
+
cwd,
|
|
1234
|
+
stateDir,
|
|
1235
|
+
canonicalSessionId,
|
|
1236
|
+
);
|
|
1237
|
+
if (releaseReadinessFinalizeResult.matched) return releaseReadinessFinalizeResult.output;
|
|
1238
|
+
|
|
1239
|
+
const teamOutput = await buildTeamStopOutput(cwd, canonicalSessionId);
|
|
993
1240
|
if (teamOutput) {
|
|
994
1241
|
const teamSignature = buildRepeatableStopSignature(payload, "team-stop", safeString(teamOutput.stopReason));
|
|
995
1242
|
return await maybeReturnRepeatableStopOutput(payload, stateDir, teamSignature, teamOutput);
|
|
996
1243
|
}
|
|
997
1244
|
|
|
998
|
-
if (
|
|
999
|
-
const canonicalTeam = await findCanonicalActiveTeamForSession(cwd,
|
|
1245
|
+
if (canonicalSessionId) {
|
|
1246
|
+
const canonicalTeam = await findCanonicalActiveTeamForSession(cwd, canonicalSessionId);
|
|
1000
1247
|
if (canonicalTeam) {
|
|
1001
1248
|
const canonicalTeamOutput = buildTeamStopOutputForPhase(
|
|
1002
1249
|
canonicalTeam.teamName,
|
|
@@ -1012,28 +1259,28 @@ async function buildStopHookOutput(
|
|
|
1012
1259
|
if (repeatedCanonicalTeamOutput) return repeatedCanonicalTeamOutput;
|
|
1013
1260
|
}
|
|
1014
1261
|
|
|
1015
|
-
const skillOutput = await buildSkillStopOutput(cwd,
|
|
1262
|
+
const skillOutput = await buildSkillStopOutput(cwd, canonicalSessionId, threadId);
|
|
1016
1263
|
if (!stopHookActive && skillOutput) return skillOutput;
|
|
1017
1264
|
}
|
|
1018
1265
|
|
|
1019
|
-
const deepInterviewActive = await isDeepInterviewSuppressedForStop(cwd, sessionId, threadId);
|
|
1020
1266
|
const lastAssistantMessage = safeString(
|
|
1021
1267
|
payload.last_assistant_message ?? payload.lastAssistantMessage,
|
|
1022
1268
|
);
|
|
1023
1269
|
const autoNudgeConfig = await loadAutoNudgeConfig();
|
|
1270
|
+
const autoNudgePhase = await readStopAutoNudgePhase(cwd, canonicalSessionId, threadId);
|
|
1024
1271
|
|
|
1025
1272
|
if (
|
|
1026
|
-
|
|
1027
|
-
&& autoNudgeConfig.
|
|
1028
|
-
&& detectStallPattern(lastAssistantMessage, autoNudgeConfig.patterns)
|
|
1273
|
+
autoNudgeConfig.enabled
|
|
1274
|
+
&& detectStallPattern(lastAssistantMessage, autoNudgeConfig.patterns, autoNudgePhase)
|
|
1029
1275
|
) {
|
|
1276
|
+
const effectiveResponse = resolveEffectiveAutoNudgeResponse(autoNudgeConfig.response);
|
|
1030
1277
|
return await maybeReturnRepeatableStopOutput(
|
|
1031
1278
|
payload,
|
|
1032
1279
|
stateDir,
|
|
1033
1280
|
buildRepeatableStopSignature(payload, "auto-nudge", lastAssistantMessage),
|
|
1034
1281
|
{
|
|
1035
1282
|
decision: "block",
|
|
1036
|
-
reason:
|
|
1283
|
+
reason: effectiveResponse,
|
|
1037
1284
|
stopReason: "auto_nudge",
|
|
1038
1285
|
systemMessage:
|
|
1039
1286
|
"OMX native Stop detected a stall/permission-style handoff and continued the turn automatically.",
|
|
@@ -1073,23 +1320,30 @@ export async function dispatchCodexNativeHook(
|
|
|
1073
1320
|
const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
|
|
1074
1321
|
let skillState: SkillActiveState | null = null;
|
|
1075
1322
|
|
|
1076
|
-
const
|
|
1323
|
+
const nativeSessionId = safeString(payload.session_id ?? payload.sessionId).trim();
|
|
1077
1324
|
const threadId = safeString(payload.thread_id ?? payload.threadId).trim();
|
|
1078
1325
|
const turnId = safeString(payload.turn_id ?? payload.turnId).trim();
|
|
1326
|
+
let canonicalSessionId = safeString((await readUsableSessionState(cwd))?.session_id).trim();
|
|
1079
1327
|
|
|
1080
|
-
if (hookEventName === "SessionStart" &&
|
|
1081
|
-
await
|
|
1328
|
+
if (hookEventName === "SessionStart" && nativeSessionId) {
|
|
1329
|
+
const sessionState = await reconcileNativeSessionStart(cwd, nativeSessionId, {
|
|
1082
1330
|
pid: options.sessionOwnerPid ?? resolveSessionOwnerPid(payload),
|
|
1083
1331
|
});
|
|
1332
|
+
canonicalSessionId = safeString(sessionState.session_id).trim();
|
|
1333
|
+
} else if (!canonicalSessionId) {
|
|
1334
|
+
canonicalSessionId = safeString((await readUsableSessionState(cwd))?.session_id).trim();
|
|
1084
1335
|
}
|
|
1085
1336
|
|
|
1337
|
+
const eventSessionId = canonicalSessionId || nativeSessionId || undefined;
|
|
1338
|
+
const sessionIdForState = canonicalSessionId || nativeSessionId;
|
|
1339
|
+
|
|
1086
1340
|
if (hookEventName === "UserPromptSubmit") {
|
|
1087
1341
|
const prompt = readPromptText(payload);
|
|
1088
1342
|
if (prompt) {
|
|
1089
1343
|
skillState = await recordSkillActivation({
|
|
1090
1344
|
stateDir,
|
|
1091
1345
|
text: prompt,
|
|
1092
|
-
sessionId,
|
|
1346
|
+
sessionId: sessionIdForState,
|
|
1093
1347
|
threadId,
|
|
1094
1348
|
turnId,
|
|
1095
1349
|
});
|
|
@@ -1098,11 +1352,19 @@ export async function dispatchCodexNativeHook(
|
|
|
1098
1352
|
}
|
|
1099
1353
|
|
|
1100
1354
|
if (omxEventName) {
|
|
1355
|
+
const baseContext = buildBaseContext(cwd, payload, hookEventName!);
|
|
1356
|
+
if (nativeSessionId) {
|
|
1357
|
+
baseContext.native_session_id = nativeSessionId;
|
|
1358
|
+
baseContext.codex_session_id = nativeSessionId;
|
|
1359
|
+
}
|
|
1360
|
+
if (canonicalSessionId) {
|
|
1361
|
+
baseContext.omx_session_id = canonicalSessionId;
|
|
1362
|
+
}
|
|
1101
1363
|
const event: HookEventEnvelope = buildNativeHookEvent(
|
|
1102
1364
|
omxEventName,
|
|
1103
|
-
|
|
1365
|
+
baseContext,
|
|
1104
1366
|
{
|
|
1105
|
-
session_id:
|
|
1367
|
+
session_id: eventSessionId,
|
|
1106
1368
|
thread_id: threadId || undefined,
|
|
1107
1369
|
turn_id: turnId || undefined,
|
|
1108
1370
|
mode: safeString(payload.mode).trim() || undefined,
|
|
@@ -1114,7 +1376,7 @@ export async function dispatchCodexNativeHook(
|
|
|
1114
1376
|
let outputJson: Record<string, unknown> | null = null;
|
|
1115
1377
|
if (hookEventName === "SessionStart" || hookEventName === "UserPromptSubmit") {
|
|
1116
1378
|
const additionalContext = hookEventName === "SessionStart"
|
|
1117
|
-
? await buildSessionStartContext(cwd,
|
|
1379
|
+
? await buildSessionStartContext(cwd, canonicalSessionId || nativeSessionId)
|
|
1118
1380
|
: buildAdditionalContextMessage(readPromptText(payload), skillState);
|
|
1119
1381
|
if (additionalContext) {
|
|
1120
1382
|
outputJson = {
|
|
@@ -1143,17 +1405,49 @@ export async function dispatchCodexNativeHook(
|
|
|
1143
1405
|
};
|
|
1144
1406
|
}
|
|
1145
1407
|
|
|
1146
|
-
|
|
1408
|
+
interface NativeHookCliReadResult {
|
|
1409
|
+
payload: CodexHookPayload;
|
|
1410
|
+
parseError: Error | null;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
async function readStdinJson(): Promise<NativeHookCliReadResult> {
|
|
1147
1414
|
const chunks: Buffer[] = [];
|
|
1148
1415
|
for await (const chunk of process.stdin) {
|
|
1149
1416
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
1150
1417
|
}
|
|
1151
1418
|
const raw = Buffer.concat(chunks).toString("utf-8").trim();
|
|
1152
|
-
|
|
1419
|
+
if (!raw) {
|
|
1420
|
+
return { payload: {}, parseError: null };
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
try {
|
|
1424
|
+
return {
|
|
1425
|
+
payload: safeObject(JSON.parse(raw)),
|
|
1426
|
+
parseError: null,
|
|
1427
|
+
};
|
|
1428
|
+
} catch (error) {
|
|
1429
|
+
return {
|
|
1430
|
+
payload: {},
|
|
1431
|
+
parseError: error instanceof Error ? error : new Error(String(error)),
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1153
1434
|
}
|
|
1154
1435
|
|
|
1155
1436
|
export async function runCodexNativeHookCli(): Promise<void> {
|
|
1156
|
-
const payload = await readStdinJson();
|
|
1437
|
+
const { payload, parseError } = await readStdinJson();
|
|
1438
|
+
if (parseError) {
|
|
1439
|
+
process.stdout.write(`${JSON.stringify({
|
|
1440
|
+
decision: "block",
|
|
1441
|
+
reason: "OMX native hook received malformed JSON input. Preserve runtime state and inspect the emitting hook payload before retrying.",
|
|
1442
|
+
hookSpecificOutput: {
|
|
1443
|
+
hookEventName: "Unknown",
|
|
1444
|
+
additionalContext:
|
|
1445
|
+
`stdin JSON parsing failed inside codex-native-hook: ${parseError.message}. Emit valid JSON from the native hook caller before retrying.`,
|
|
1446
|
+
},
|
|
1447
|
+
})}\n`);
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1157
1451
|
const result = await dispatchCodexNativeHook(payload);
|
|
1158
1452
|
if (result.outputJson) {
|
|
1159
1453
|
process.stdout.write(`${JSON.stringify(result.outputJson)}\n`);
|