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,10 +6,11 @@ import { join } from 'path';
|
|
|
6
6
|
import { tmpdir } from 'os';
|
|
7
7
|
import { existsSync } from 'fs';
|
|
8
8
|
import { HUD_TMUX_TEAM_HEIGHT_LINES } from '../../hud/constants.js';
|
|
9
|
-
import { initTeamState, createTask, readTeamConfig, saveTeamConfig, listMailboxMessages, listDispatchRequests, transitionDispatchRequest, updateWorkerHeartbeat, writeAtomic, readTask, readMonitorSnapshot, claimTask, transitionTaskStatus, writeWorkerStatus, } from '../state.js';
|
|
9
|
+
import { initTeamState, createTask, readTeamConfig, saveTeamConfig, listMailboxMessages, listDispatchRequests, transitionDispatchRequest, updateWorkerHeartbeat, writeAtomic, readTask, readMonitorSnapshot, claimTask, transitionTaskStatus, readWorkerStatus, writeWorkerStatus, } from '../state.js';
|
|
10
10
|
import { monitorTeam, shutdownTeam, resumeTeam, startTeam, assignTask, sendWorkerMessage, applyCreatedInteractiveSessionToConfig, resolveWorkerLaunchArgsFromEnv, shouldPrekillInteractiveShutdownProcessTrees, waitForWorkerStartupEvidence, waitForClaudeStartupEvidence, } from '../runtime.js';
|
|
11
11
|
import { resolveTeamLowComplexityDefaultModel } from '../model-contract.js';
|
|
12
12
|
import { readTeamEvents } from '../state/events.js';
|
|
13
|
+
import { sanitizeTeamName } from '../tmux-session.js';
|
|
13
14
|
async function initRepo() {
|
|
14
15
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-worktree-repo-'));
|
|
15
16
|
execFileSync('git', ['init'], { cwd, stdio: 'ignore' });
|
|
@@ -107,6 +108,29 @@ function withoutTeamWorkerEnv(fn) {
|
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
}
|
|
111
|
+
function withMockPromptModeCodexAllowed(fn) {
|
|
112
|
+
const previous = process.env.OMX_TEST_ALLOW_NONTTY_CODEX_PROMPT;
|
|
113
|
+
process.env.OMX_TEST_ALLOW_NONTTY_CODEX_PROMPT = '1';
|
|
114
|
+
let restoreImmediately = true;
|
|
115
|
+
const restore = () => {
|
|
116
|
+
if (typeof previous === 'string')
|
|
117
|
+
process.env.OMX_TEST_ALLOW_NONTTY_CODEX_PROMPT = previous;
|
|
118
|
+
else
|
|
119
|
+
delete process.env.OMX_TEST_ALLOW_NONTTY_CODEX_PROMPT;
|
|
120
|
+
};
|
|
121
|
+
try {
|
|
122
|
+
const result = fn();
|
|
123
|
+
if (result instanceof Promise) {
|
|
124
|
+
restoreImmediately = false;
|
|
125
|
+
return result.finally(restore);
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
if (restoreImmediately)
|
|
131
|
+
restore();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
110
134
|
async function waitForFileText(filePath, matcher, timeoutMs = 3_000) {
|
|
111
135
|
const deadline = Date.now() + timeoutMs;
|
|
112
136
|
while (Date.now() < deadline) {
|
|
@@ -150,6 +174,7 @@ async function withPromptModeCodexEnv(binDir, extraEnv, run) {
|
|
|
150
174
|
TMUX: undefined,
|
|
151
175
|
OMX_TEAM_WORKER_LAUNCH_MODE: 'prompt',
|
|
152
176
|
OMX_TEAM_WORKER_CLI: 'codex',
|
|
177
|
+
OMX_TEST_ALLOW_NONTTY_CODEX_PROMPT: '1',
|
|
153
178
|
...extraEnv,
|
|
154
179
|
};
|
|
155
180
|
for (const [key, value] of Object.entries(nextEnv)) {
|
|
@@ -488,6 +513,159 @@ describe('runtime', () => {
|
|
|
488
513
|
await rm(cwd, { recursive: true, force: true });
|
|
489
514
|
}
|
|
490
515
|
});
|
|
516
|
+
it('waitForWorkerStartupEvidence treats blocked worker status as settled progress even without a claimed task id', async () => {
|
|
517
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-codex-blocked-startup-'));
|
|
518
|
+
try {
|
|
519
|
+
await initTeamState('codex-blocked-startup', 'blocked startup evidence test', 'executor', 1, cwd);
|
|
520
|
+
await writeAtomic(join(cwd, '.omx', 'state', 'team', 'codex-blocked-startup', 'workers', 'worker-1', 'status.json'), JSON.stringify({
|
|
521
|
+
state: 'blocked',
|
|
522
|
+
reason: 'waiting on shared file',
|
|
523
|
+
updated_at: new Date().toISOString(),
|
|
524
|
+
}, null, 2));
|
|
525
|
+
const progress = await waitForWorkerStartupEvidence({
|
|
526
|
+
teamName: 'codex-blocked-startup',
|
|
527
|
+
workerName: 'worker-1',
|
|
528
|
+
workerCli: 'codex',
|
|
529
|
+
cwd,
|
|
530
|
+
timeoutMs: 25,
|
|
531
|
+
pollMs: 5,
|
|
532
|
+
});
|
|
533
|
+
assert.equal(progress, 'worker_progress');
|
|
534
|
+
}
|
|
535
|
+
finally {
|
|
536
|
+
await rm(cwd, { recursive: true, force: true });
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
it('startTeam rejects interactive startup when tmux fallback never produces worker startup evidence', async () => {
|
|
540
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-startup-no-evidence-'));
|
|
541
|
+
const prevTmux = process.env.TMUX;
|
|
542
|
+
const prevTmuxPane = process.env.TMUX_PANE;
|
|
543
|
+
const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
544
|
+
const prevWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
|
|
545
|
+
const prevSkipReadyWait = process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
546
|
+
let receiptFailer = null;
|
|
547
|
+
try {
|
|
548
|
+
await withMockTmuxFixture({
|
|
549
|
+
dirPrefix: 'omx-runtime-startup-no-evidence-bin-',
|
|
550
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
551
|
+
set -eu
|
|
552
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
553
|
+
case "$1" in
|
|
554
|
+
-V)
|
|
555
|
+
echo "tmux 3.4"
|
|
556
|
+
exit 0
|
|
557
|
+
;;
|
|
558
|
+
display-message)
|
|
559
|
+
case "$*" in
|
|
560
|
+
*"#{window_width}"*)
|
|
561
|
+
echo "120"
|
|
562
|
+
;;
|
|
563
|
+
*)
|
|
564
|
+
echo "leader:0 %1"
|
|
565
|
+
;;
|
|
566
|
+
esac
|
|
567
|
+
exit 0
|
|
568
|
+
;;
|
|
569
|
+
list-panes)
|
|
570
|
+
case "$*" in
|
|
571
|
+
*"-F #{pane_id}"*"#{pane_current_command}"*)
|
|
572
|
+
printf "%%1\\tzsh\\tzsh\\n"
|
|
573
|
+
exit 0
|
|
574
|
+
;;
|
|
575
|
+
*"#{pane_pid}"*)
|
|
576
|
+
echo "4321"
|
|
577
|
+
exit 0
|
|
578
|
+
;;
|
|
579
|
+
*"#{pane_dead} #{pane_pid}"*)
|
|
580
|
+
echo "0 4321"
|
|
581
|
+
exit 0
|
|
582
|
+
;;
|
|
583
|
+
*)
|
|
584
|
+
exit 0
|
|
585
|
+
;;
|
|
586
|
+
esac
|
|
587
|
+
;;
|
|
588
|
+
split-window)
|
|
589
|
+
case "$*" in
|
|
590
|
+
*" -h "*)
|
|
591
|
+
echo "%2"
|
|
592
|
+
;;
|
|
593
|
+
*)
|
|
594
|
+
echo "%3"
|
|
595
|
+
;;
|
|
596
|
+
esac
|
|
597
|
+
exit 0
|
|
598
|
+
;;
|
|
599
|
+
capture-pane)
|
|
600
|
+
printf 'OpenAI Codex\\n> '
|
|
601
|
+
exit 0
|
|
602
|
+
;;
|
|
603
|
+
send-keys|resize-pane|select-layout|set-window-option|select-pane|set-hook|run-shell|kill-pane|kill-session)
|
|
604
|
+
exit 0
|
|
605
|
+
;;
|
|
606
|
+
*)
|
|
607
|
+
exit 0
|
|
608
|
+
;;
|
|
609
|
+
esac
|
|
610
|
+
`,
|
|
611
|
+
binaries: [
|
|
612
|
+
{
|
|
613
|
+
name: 'codex',
|
|
614
|
+
content: '#!/bin/sh\nsleep 30\n',
|
|
615
|
+
},
|
|
616
|
+
],
|
|
617
|
+
}, async ({ tmuxLogPath }) => {
|
|
618
|
+
process.env.TMUX = 'leader-session';
|
|
619
|
+
process.env.TMUX_PANE = '%1';
|
|
620
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
|
|
621
|
+
process.env.OMX_TEAM_WORKER_CLI = 'codex';
|
|
622
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = '1';
|
|
623
|
+
receiptFailer = setInterval(() => {
|
|
624
|
+
void (async () => {
|
|
625
|
+
const requests = await listDispatchRequests('team-startup-no-evidence', cwd, { kind: 'inbox' }).catch(() => []);
|
|
626
|
+
for (const request of requests) {
|
|
627
|
+
if (request.status !== 'pending')
|
|
628
|
+
continue;
|
|
629
|
+
await transitionDispatchRequest('team-startup-no-evidence', request.request_id, 'pending', 'failed', { last_reason: 'test_failed_receipt' }, cwd).catch(() => { });
|
|
630
|
+
}
|
|
631
|
+
})();
|
|
632
|
+
}, 20);
|
|
633
|
+
await assert.rejects(() => withoutTeamWorkerEnv(() => startTeam('team-startup-no-evidence', 'interactive startup must observe worker evidence', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd)), /worker_notify_failed/);
|
|
634
|
+
if (receiptFailer) {
|
|
635
|
+
clearInterval(receiptFailer);
|
|
636
|
+
receiptFailer = null;
|
|
637
|
+
}
|
|
638
|
+
assert.equal(await readTeamConfig('team-startup-no-evidence', cwd), null);
|
|
639
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
640
|
+
assert.match(tmuxLog, /send-keys -t %2 -l --/);
|
|
641
|
+
});
|
|
642
|
+
}
|
|
643
|
+
finally {
|
|
644
|
+
if (receiptFailer)
|
|
645
|
+
clearInterval(receiptFailer);
|
|
646
|
+
if (typeof prevTmux === 'string')
|
|
647
|
+
process.env.TMUX = prevTmux;
|
|
648
|
+
else
|
|
649
|
+
delete process.env.TMUX;
|
|
650
|
+
if (typeof prevTmuxPane === 'string')
|
|
651
|
+
process.env.TMUX_PANE = prevTmuxPane;
|
|
652
|
+
else
|
|
653
|
+
delete process.env.TMUX_PANE;
|
|
654
|
+
if (typeof prevLaunchMode === 'string')
|
|
655
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = prevLaunchMode;
|
|
656
|
+
else
|
|
657
|
+
delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
658
|
+
if (typeof prevWorkerCli === 'string')
|
|
659
|
+
process.env.OMX_TEAM_WORKER_CLI = prevWorkerCli;
|
|
660
|
+
else
|
|
661
|
+
delete process.env.OMX_TEAM_WORKER_CLI;
|
|
662
|
+
if (typeof prevSkipReadyWait === 'string')
|
|
663
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = prevSkipReadyWait;
|
|
664
|
+
else
|
|
665
|
+
delete process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
666
|
+
await rm(cwd, { recursive: true, force: true });
|
|
667
|
+
}
|
|
668
|
+
});
|
|
491
669
|
it('resolveWorkerLaunchArgsFromEnv logs source=none/default-none when thinking is not explicit', () => {
|
|
492
670
|
const logs = [];
|
|
493
671
|
const originalLog = console.log;
|
|
@@ -860,6 +1038,14 @@ case "\${1:-}" in
|
|
|
860
1038
|
split-window)
|
|
861
1039
|
case "$*" in
|
|
862
1040
|
*" -h "*)
|
|
1041
|
+
mkdir -p "${cwd}/.omx/state/team/team-pane-pid/workers/worker-1"
|
|
1042
|
+
cat > "${cwd}/.omx/state/team/team-pane-pid/workers/worker-1/status.json" <<'EOF'
|
|
1043
|
+
{
|
|
1044
|
+
"state": "working",
|
|
1045
|
+
"current_task_id": "1",
|
|
1046
|
+
"updated_at": "2026-04-10T00:00:00.000Z"
|
|
1047
|
+
}
|
|
1048
|
+
EOF
|
|
863
1049
|
echo "%2"
|
|
864
1050
|
;;
|
|
865
1051
|
*)
|
|
@@ -922,15 +1108,409 @@ esac
|
|
|
922
1108
|
});
|
|
923
1109
|
it('startTeam saves interactive pane ids before readiness waits in source order', async () => {
|
|
924
1110
|
const source = await readFile(join(process.cwd(), 'src', 'team', 'runtime.ts'), 'utf-8');
|
|
925
|
-
const
|
|
926
|
-
const
|
|
927
|
-
const
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1111
|
+
const applyMatch = source.match(/applyCreatedInteractiveSessionToConfig\(\s*config,\s*createdSession,\s*workerPaneIds\s*\);/m);
|
|
1112
|
+
const saveMatch = source.match(/await saveTeamConfig\(config, leaderCwd\);/m);
|
|
1113
|
+
const readyMatch = source.match(/const ready = waitForWorkerReady\(/m);
|
|
1114
|
+
const applyIndex = applyMatch?.index ?? -1;
|
|
1115
|
+
const saveIndex = saveMatch?.index ?? -1;
|
|
1116
|
+
const readyIndex = readyMatch?.index ?? -1;
|
|
1117
|
+
assert.notEqual(applyMatch, null);
|
|
1118
|
+
assert.notEqual(saveMatch, null);
|
|
1119
|
+
assert.notEqual(readyMatch, null);
|
|
931
1120
|
assert.equal(applyIndex < saveIndex, true);
|
|
932
1121
|
assert.equal(saveIndex < readyIndex, true);
|
|
933
1122
|
});
|
|
1123
|
+
it('startTeam records recoverable startup issues per worker instead of failing launch early when panes stay alive', async () => {
|
|
1124
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-no-startup-evidence-'));
|
|
1125
|
+
const previousTmux = process.env.TMUX;
|
|
1126
|
+
const previousTmuxPane = process.env.TMUX_PANE;
|
|
1127
|
+
const previousLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
1128
|
+
const previousWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
|
|
1129
|
+
const previousSkipReadyWait = process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
1130
|
+
const previousStartupEvidenceTimeout = process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS;
|
|
1131
|
+
let runtime = null;
|
|
1132
|
+
const teamName = 'team-no-startup-evidence';
|
|
1133
|
+
try {
|
|
1134
|
+
await withMockTmuxFixture({
|
|
1135
|
+
dirPrefix: 'omx-runtime-no-startup-evidence-bin-',
|
|
1136
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
1137
|
+
set -eu
|
|
1138
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
1139
|
+
case "$1" in
|
|
1140
|
+
-V)
|
|
1141
|
+
echo "tmux 3.4"
|
|
1142
|
+
exit 0
|
|
1143
|
+
;;
|
|
1144
|
+
display-message)
|
|
1145
|
+
case "$*" in
|
|
1146
|
+
*"#{window_width}"*)
|
|
1147
|
+
echo "120"
|
|
1148
|
+
;;
|
|
1149
|
+
*)
|
|
1150
|
+
echo "leader:0 %1"
|
|
1151
|
+
;;
|
|
1152
|
+
esac
|
|
1153
|
+
exit 0
|
|
1154
|
+
;;
|
|
1155
|
+
list-panes)
|
|
1156
|
+
case "$*" in
|
|
1157
|
+
*"#{pane_dead}"*)
|
|
1158
|
+
echo "0"
|
|
1159
|
+
;;
|
|
1160
|
+
*"#{pane_dead} #{pane_pid}"*)
|
|
1161
|
+
echo "0 4242"
|
|
1162
|
+
;;
|
|
1163
|
+
*"pane_current_command"*)
|
|
1164
|
+
printf "%%1\\tnode\\t'codex'\\n"
|
|
1165
|
+
;;
|
|
1166
|
+
*"#{pane_pid}"*)
|
|
1167
|
+
echo "4242"
|
|
1168
|
+
;;
|
|
1169
|
+
*)
|
|
1170
|
+
exit 0
|
|
1171
|
+
;;
|
|
1172
|
+
esac
|
|
1173
|
+
exit 0
|
|
1174
|
+
;;
|
|
1175
|
+
capture-pane)
|
|
1176
|
+
exit 0
|
|
1177
|
+
;;
|
|
1178
|
+
split-window)
|
|
1179
|
+
case "$*" in
|
|
1180
|
+
*" -h "*)
|
|
1181
|
+
echo "%2"
|
|
1182
|
+
;;
|
|
1183
|
+
*)
|
|
1184
|
+
echo "%3"
|
|
1185
|
+
;;
|
|
1186
|
+
esac
|
|
1187
|
+
exit 0
|
|
1188
|
+
;;
|
|
1189
|
+
set-hook|run-shell|select-layout|set-window-option|select-pane|send-keys|kill-pane|kill-session|resize-pane)
|
|
1190
|
+
exit 0
|
|
1191
|
+
;;
|
|
1192
|
+
*)
|
|
1193
|
+
exit 0
|
|
1194
|
+
;;
|
|
1195
|
+
esac
|
|
1196
|
+
`,
|
|
1197
|
+
binaries: [
|
|
1198
|
+
{
|
|
1199
|
+
name: 'codex',
|
|
1200
|
+
content: `#!/usr/bin/env node
|
|
1201
|
+
process.stdin.resume();
|
|
1202
|
+
setTimeout(() => process.exit(0), 30000);
|
|
1203
|
+
process.on('SIGTERM', () => process.exit(0));
|
|
1204
|
+
`,
|
|
1205
|
+
},
|
|
1206
|
+
],
|
|
1207
|
+
}, async () => {
|
|
1208
|
+
delete process.env.TMUX;
|
|
1209
|
+
process.env.TMUX_PANE = '%1';
|
|
1210
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
|
|
1211
|
+
process.env.OMX_TEAM_WORKER_CLI = 'codex';
|
|
1212
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = '1';
|
|
1213
|
+
process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS = '500';
|
|
1214
|
+
runtime = await withoutTeamWorkerEnv(() => startTeam(teamName, 'interactive startup should keep evaluating per-worker startup issues while panes stay alive', 'executor', 2, [
|
|
1215
|
+
{ subject: 'worker-1 task', description: 'd', owner: 'worker-1' },
|
|
1216
|
+
{ subject: 'worker-2 task', description: 'd', owner: 'worker-2' },
|
|
1217
|
+
], cwd));
|
|
1218
|
+
const worker1Status = await readWorkerStatus(teamName, 'worker-1', cwd);
|
|
1219
|
+
const worker2Status = await readWorkerStatus(teamName, 'worker-2', cwd);
|
|
1220
|
+
assert.equal(worker1Status.state, 'unknown');
|
|
1221
|
+
assert.equal(worker2Status.state, 'unknown');
|
|
1222
|
+
assert.match(worker1Status.reason ?? '', /startup_no_evidence|fallback_attempted_but_unconfirmed/);
|
|
1223
|
+
assert.match(worker2Status.reason ?? '', /startup_no_evidence|fallback_attempted_but_unconfirmed/);
|
|
1224
|
+
const task1 = await readTask(teamName, '1', cwd);
|
|
1225
|
+
const task2 = await readTask(teamName, '2', cwd);
|
|
1226
|
+
assert.equal(task1?.status, 'pending');
|
|
1227
|
+
assert.equal(task2?.status, 'pending');
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
finally {
|
|
1231
|
+
if (runtime) {
|
|
1232
|
+
await shutdownTeam(teamName, cwd, { force: true }).catch(() => { });
|
|
1233
|
+
}
|
|
1234
|
+
if (typeof previousTmux === 'string')
|
|
1235
|
+
process.env.TMUX = previousTmux;
|
|
1236
|
+
else
|
|
1237
|
+
delete process.env.TMUX;
|
|
1238
|
+
if (typeof previousTmuxPane === 'string')
|
|
1239
|
+
process.env.TMUX_PANE = previousTmuxPane;
|
|
1240
|
+
else
|
|
1241
|
+
delete process.env.TMUX_PANE;
|
|
1242
|
+
if (typeof previousLaunchMode === 'string')
|
|
1243
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = previousLaunchMode;
|
|
1244
|
+
else
|
|
1245
|
+
delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
1246
|
+
if (typeof previousWorkerCli === 'string')
|
|
1247
|
+
process.env.OMX_TEAM_WORKER_CLI = previousWorkerCli;
|
|
1248
|
+
else
|
|
1249
|
+
delete process.env.OMX_TEAM_WORKER_CLI;
|
|
1250
|
+
if (typeof previousSkipReadyWait === 'string')
|
|
1251
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = previousSkipReadyWait;
|
|
1252
|
+
else
|
|
1253
|
+
delete process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
1254
|
+
if (typeof previousStartupEvidenceTimeout === 'string') {
|
|
1255
|
+
process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS = previousStartupEvidenceTimeout;
|
|
1256
|
+
}
|
|
1257
|
+
else {
|
|
1258
|
+
delete process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS;
|
|
1259
|
+
}
|
|
1260
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
it('startTeam still fails startup when the worker pane is dead/unrecoverable', async () => {
|
|
1264
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-dead-startup-pane-'));
|
|
1265
|
+
const previousTmux = process.env.TMUX;
|
|
1266
|
+
const previousTmuxPane = process.env.TMUX_PANE;
|
|
1267
|
+
const previousLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
1268
|
+
const previousWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
|
|
1269
|
+
const previousStartupEvidenceTimeout = process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS;
|
|
1270
|
+
try {
|
|
1271
|
+
await withMockTmuxFixture({
|
|
1272
|
+
dirPrefix: 'omx-runtime-dead-startup-pane-bin-',
|
|
1273
|
+
tmuxScript: () => `#!/bin/sh
|
|
1274
|
+
set -eu
|
|
1275
|
+
case "$1" in
|
|
1276
|
+
-V)
|
|
1277
|
+
echo "tmux 3.4"
|
|
1278
|
+
exit 0
|
|
1279
|
+
;;
|
|
1280
|
+
display-message)
|
|
1281
|
+
case "$*" in
|
|
1282
|
+
*"#{window_width}"*) echo "120" ;;
|
|
1283
|
+
*) echo "leader:0 %1" ;;
|
|
1284
|
+
esac
|
|
1285
|
+
exit 0
|
|
1286
|
+
;;
|
|
1287
|
+
list-panes)
|
|
1288
|
+
case "$*" in
|
|
1289
|
+
*"pane_current_command"*)
|
|
1290
|
+
printf "%%1\\tnode\\t'codex'\\n"
|
|
1291
|
+
;;
|
|
1292
|
+
*"#{pane_dead} #{pane_pid}"*)
|
|
1293
|
+
echo "1 4242"
|
|
1294
|
+
;;
|
|
1295
|
+
*"#{pane_pid}"*)
|
|
1296
|
+
echo "4242"
|
|
1297
|
+
;;
|
|
1298
|
+
*)
|
|
1299
|
+
exit 0
|
|
1300
|
+
;;
|
|
1301
|
+
esac
|
|
1302
|
+
exit 0
|
|
1303
|
+
;;
|
|
1304
|
+
capture-pane)
|
|
1305
|
+
exit 0
|
|
1306
|
+
;;
|
|
1307
|
+
split-window)
|
|
1308
|
+
case "$*" in
|
|
1309
|
+
*" -h "*) echo "%2" ;;
|
|
1310
|
+
*) echo "%3" ;;
|
|
1311
|
+
esac
|
|
1312
|
+
exit 0
|
|
1313
|
+
;;
|
|
1314
|
+
set-hook|run-shell|select-layout|set-window-option|select-pane|send-keys|kill-pane|kill-session|resize-pane)
|
|
1315
|
+
exit 0
|
|
1316
|
+
;;
|
|
1317
|
+
*)
|
|
1318
|
+
exit 0
|
|
1319
|
+
;;
|
|
1320
|
+
esac
|
|
1321
|
+
`,
|
|
1322
|
+
binaries: [{ name: 'codex', content: '#!/usr/bin/env node\nprocess.stdin.resume();\n' }],
|
|
1323
|
+
}, async () => {
|
|
1324
|
+
delete process.env.TMUX;
|
|
1325
|
+
process.env.TMUX_PANE = '%1';
|
|
1326
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
|
|
1327
|
+
process.env.OMX_TEAM_WORKER_CLI = 'codex';
|
|
1328
|
+
process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS = '500';
|
|
1329
|
+
await assert.rejects(() => withoutTeamWorkerEnv(() => startTeam('team-dead-startup-pane', 'dead pane should still fail startup', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd)), /Worker worker-1 did not become ready/);
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
finally {
|
|
1333
|
+
if (typeof previousTmux === 'string')
|
|
1334
|
+
process.env.TMUX = previousTmux;
|
|
1335
|
+
else
|
|
1336
|
+
delete process.env.TMUX;
|
|
1337
|
+
if (typeof previousTmuxPane === 'string')
|
|
1338
|
+
process.env.TMUX_PANE = previousTmuxPane;
|
|
1339
|
+
else
|
|
1340
|
+
delete process.env.TMUX_PANE;
|
|
1341
|
+
if (typeof previousLaunchMode === 'string')
|
|
1342
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = previousLaunchMode;
|
|
1343
|
+
else
|
|
1344
|
+
delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
1345
|
+
if (typeof previousWorkerCli === 'string')
|
|
1346
|
+
process.env.OMX_TEAM_WORKER_CLI = previousWorkerCli;
|
|
1347
|
+
else
|
|
1348
|
+
delete process.env.OMX_TEAM_WORKER_CLI;
|
|
1349
|
+
if (typeof previousStartupEvidenceTimeout === 'string') {
|
|
1350
|
+
process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS = previousStartupEvidenceTimeout;
|
|
1351
|
+
}
|
|
1352
|
+
else {
|
|
1353
|
+
delete process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS;
|
|
1354
|
+
}
|
|
1355
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1356
|
+
}
|
|
1357
|
+
});
|
|
1358
|
+
it('startTeam materializes all worker identity/inbox files before worker-1 startup evidence can block later workers', async () => {
|
|
1359
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-materialize-before-evidence-'));
|
|
1360
|
+
const previousTmux = process.env.TMUX;
|
|
1361
|
+
const previousTmuxPane = process.env.TMUX_PANE;
|
|
1362
|
+
const previousLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
1363
|
+
const previousWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
|
|
1364
|
+
const previousSkipReadyWait = process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
1365
|
+
const previousStartupEvidenceTimeout = process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS;
|
|
1366
|
+
try {
|
|
1367
|
+
await withMockTmuxFixture({
|
|
1368
|
+
dirPrefix: 'omx-runtime-materialize-before-evidence-bin-',
|
|
1369
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
1370
|
+
set -eu
|
|
1371
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
1372
|
+
case "$1" in
|
|
1373
|
+
-V)
|
|
1374
|
+
echo "tmux 3.4"
|
|
1375
|
+
exit 0
|
|
1376
|
+
;;
|
|
1377
|
+
display-message)
|
|
1378
|
+
case "$*" in
|
|
1379
|
+
*"#{window_width}"*)
|
|
1380
|
+
echo "120"
|
|
1381
|
+
;;
|
|
1382
|
+
*)
|
|
1383
|
+
echo "leader:0 %1"
|
|
1384
|
+
;;
|
|
1385
|
+
esac
|
|
1386
|
+
exit 0
|
|
1387
|
+
;;
|
|
1388
|
+
list-panes)
|
|
1389
|
+
case "$*" in
|
|
1390
|
+
*"#{pane_dead} #{pane_pid}"*)
|
|
1391
|
+
echo "0 4242"
|
|
1392
|
+
;;
|
|
1393
|
+
*"pane_current_command"*)
|
|
1394
|
+
printf "%%1\\tnode\\t'codex'\\n"
|
|
1395
|
+
;;
|
|
1396
|
+
*"-t %2"*"#{pane_pid}"*)
|
|
1397
|
+
echo "4242"
|
|
1398
|
+
;;
|
|
1399
|
+
*"-t %3"*"#{pane_pid}"*)
|
|
1400
|
+
echo "4343"
|
|
1401
|
+
;;
|
|
1402
|
+
*"-t %4"*"#{pane_pid}"*)
|
|
1403
|
+
echo "4444"
|
|
1404
|
+
;;
|
|
1405
|
+
*"#{pane_pid}"*)
|
|
1406
|
+
echo "4141"
|
|
1407
|
+
;;
|
|
1408
|
+
*)
|
|
1409
|
+
exit 0
|
|
1410
|
+
;;
|
|
1411
|
+
esac
|
|
1412
|
+
exit 0
|
|
1413
|
+
;;
|
|
1414
|
+
capture-pane)
|
|
1415
|
+
exit 0
|
|
1416
|
+
;;
|
|
1417
|
+
split-window)
|
|
1418
|
+
count_file="${cwd}/split-window-count"
|
|
1419
|
+
count=0
|
|
1420
|
+
if [ -f "$count_file" ]; then
|
|
1421
|
+
count=$(cat "$count_file")
|
|
1422
|
+
fi
|
|
1423
|
+
count=$((count + 1))
|
|
1424
|
+
printf '%s' "$count" > "$count_file"
|
|
1425
|
+
case "$count" in
|
|
1426
|
+
1) echo "%2" ;;
|
|
1427
|
+
2) echo "%3" ;;
|
|
1428
|
+
3) echo "%4" ;;
|
|
1429
|
+
*) echo "%5" ;;
|
|
1430
|
+
esac
|
|
1431
|
+
exit 0
|
|
1432
|
+
;;
|
|
1433
|
+
set-hook|run-shell|select-layout|set-window-option|select-pane|send-keys|kill-pane|kill-session|resize-pane)
|
|
1434
|
+
exit 0
|
|
1435
|
+
;;
|
|
1436
|
+
*)
|
|
1437
|
+
exit 0
|
|
1438
|
+
;;
|
|
1439
|
+
esac
|
|
1440
|
+
`,
|
|
1441
|
+
binaries: [
|
|
1442
|
+
{
|
|
1443
|
+
name: 'codex',
|
|
1444
|
+
content: `#!/usr/bin/env node
|
|
1445
|
+
process.stdin.resume();
|
|
1446
|
+
setTimeout(() => process.exit(0), 30000);
|
|
1447
|
+
process.on('SIGTERM', () => process.exit(0));
|
|
1448
|
+
`,
|
|
1449
|
+
},
|
|
1450
|
+
],
|
|
1451
|
+
}, async () => {
|
|
1452
|
+
const sanitizedTeamName = sanitizeTeamName('team-materialize-before-evidence');
|
|
1453
|
+
delete process.env.TMUX;
|
|
1454
|
+
process.env.TMUX_PANE = '%1';
|
|
1455
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'interactive';
|
|
1456
|
+
process.env.OMX_TEAM_WORKER_CLI = 'codex';
|
|
1457
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = '1';
|
|
1458
|
+
process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS = '500';
|
|
1459
|
+
const teamPromise = withoutTeamWorkerEnv(() => startTeam('team-materialize-before-evidence', 'later workers should materialize before startup evidence failure', 'executor', 2, [
|
|
1460
|
+
{ subject: 'w1', description: 'worker one', owner: 'worker-1' },
|
|
1461
|
+
{ subject: 'w2', description: 'worker two', owner: 'worker-2' },
|
|
1462
|
+
], cwd));
|
|
1463
|
+
const observedTeamPromise = teamPromise.then((runtime) => ({ ok: true, runtime }), (error) => ({ ok: false, error }));
|
|
1464
|
+
const workerOneIdentity = join(cwd, '.omx', 'state', 'team', sanitizedTeamName, 'workers', 'worker-1', 'identity.json');
|
|
1465
|
+
const workerTwoIdentity = join(cwd, '.omx', 'state', 'team', sanitizedTeamName, 'workers', 'worker-2', 'identity.json');
|
|
1466
|
+
const workerTwoInbox = join(cwd, '.omx', 'state', 'team', sanitizedTeamName, 'workers', 'worker-2', 'inbox.md');
|
|
1467
|
+
let materializedAllWorkers = false;
|
|
1468
|
+
for (let attempt = 0; attempt < 450; attempt += 1) {
|
|
1469
|
+
if (existsSync(workerOneIdentity)
|
|
1470
|
+
&& existsSync(workerTwoIdentity)
|
|
1471
|
+
&& existsSync(workerTwoInbox)) {
|
|
1472
|
+
materializedAllWorkers = true;
|
|
1473
|
+
break;
|
|
1474
|
+
}
|
|
1475
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1476
|
+
}
|
|
1477
|
+
assert.equal(materializedAllWorkers, true, 'worker-2 durable state should exist before worker-1 startup evidence failure rejects launch');
|
|
1478
|
+
const outcome = await observedTeamPromise;
|
|
1479
|
+
assert.equal(outcome.ok, false);
|
|
1480
|
+
assert.match(String(outcome.error), /worker_notify_failed:worker-1/);
|
|
1481
|
+
assert.equal(existsSync(join(cwd, '.omx', 'state', 'team', sanitizedTeamName)), false);
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
finally {
|
|
1485
|
+
if (typeof previousTmux === 'string')
|
|
1486
|
+
process.env.TMUX = previousTmux;
|
|
1487
|
+
else
|
|
1488
|
+
delete process.env.TMUX;
|
|
1489
|
+
if (typeof previousTmuxPane === 'string')
|
|
1490
|
+
process.env.TMUX_PANE = previousTmuxPane;
|
|
1491
|
+
else
|
|
1492
|
+
delete process.env.TMUX_PANE;
|
|
1493
|
+
if (typeof previousLaunchMode === 'string')
|
|
1494
|
+
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = previousLaunchMode;
|
|
1495
|
+
else
|
|
1496
|
+
delete process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
1497
|
+
if (typeof previousWorkerCli === 'string')
|
|
1498
|
+
process.env.OMX_TEAM_WORKER_CLI = previousWorkerCli;
|
|
1499
|
+
else
|
|
1500
|
+
delete process.env.OMX_TEAM_WORKER_CLI;
|
|
1501
|
+
if (typeof previousSkipReadyWait === 'string')
|
|
1502
|
+
process.env.OMX_TEAM_SKIP_READY_WAIT = previousSkipReadyWait;
|
|
1503
|
+
else
|
|
1504
|
+
delete process.env.OMX_TEAM_SKIP_READY_WAIT;
|
|
1505
|
+
if (typeof previousStartupEvidenceTimeout === 'string') {
|
|
1506
|
+
process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS = previousStartupEvidenceTimeout;
|
|
1507
|
+
}
|
|
1508
|
+
else {
|
|
1509
|
+
delete process.env.OMX_TEAM_STARTUP_EVIDENCE_TIMEOUT_MS;
|
|
1510
|
+
}
|
|
1511
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
934
1514
|
it('startTeam rejects dirty leader workspace before provisioning worker worktrees', async () => {
|
|
935
1515
|
const repo = await initRepo();
|
|
936
1516
|
const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
@@ -982,8 +1562,6 @@ sleep 5
|
|
|
982
1562
|
assert.equal(runtime.config.worker_launch_mode, 'prompt');
|
|
983
1563
|
assert.equal((runtime.config.workers[0]?.pid ?? 0) > 0, true);
|
|
984
1564
|
const expectedArgv = [
|
|
985
|
-
'--approval-mode',
|
|
986
|
-
'yolo',
|
|
987
1565
|
'-i',
|
|
988
1566
|
'Read .omx/state/team/team-gemini-prompt/workers/worker-1/inbox.md, start work now, report concrete progress, then continue assigned work or next feasible task.',
|
|
989
1567
|
];
|
|
@@ -1034,59 +1612,29 @@ sleep 5
|
|
|
1034
1612
|
await rm(cwd, { recursive: true, force: true });
|
|
1035
1613
|
}
|
|
1036
1614
|
});
|
|
1037
|
-
it('startTeam
|
|
1615
|
+
it('startTeam rejects codex prompt mode even when explicit launch args are provided', async () => {
|
|
1038
1616
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-codex-explicit-launch-'));
|
|
1039
1617
|
const binDir = join(cwd, 'bin');
|
|
1040
1618
|
const fakeCodexPath = join(binDir, 'codex');
|
|
1041
|
-
const capturePath = join(cwd, 'codex-argv.json');
|
|
1042
1619
|
await mkdir(binDir, { recursive: true });
|
|
1043
1620
|
await writeFile(fakeCodexPath, `#!/usr/bin/env node
|
|
1044
|
-
|
|
1045
|
-
fs.writeFileSync(process.env.OMX_CODEX_ARGV_CAPTURE_PATH, JSON.stringify(process.argv.slice(2), null, 2));
|
|
1046
|
-
process.stdin.resume();
|
|
1047
|
-
setTimeout(() => process.exit(0), 5000);
|
|
1048
|
-
process.on('SIGTERM', () => process.exit(0));
|
|
1621
|
+
process.exit(0);
|
|
1049
1622
|
`, { mode: 0o755 });
|
|
1050
1623
|
const prevPath = process.env.PATH;
|
|
1051
1624
|
const prevTmux = process.env.TMUX;
|
|
1052
1625
|
const prevLaunchMode = process.env.OMX_TEAM_WORKER_LAUNCH_MODE;
|
|
1053
1626
|
const prevWorkerCli = process.env.OMX_TEAM_WORKER_CLI;
|
|
1054
1627
|
const prevLaunchArgs = process.env.OMX_TEAM_WORKER_LAUNCH_ARGS;
|
|
1055
|
-
const prevCapture = process.env.OMX_CODEX_ARGV_CAPTURE_PATH;
|
|
1056
1628
|
process.env.PATH = `${binDir}:${prevPath ?? ''}`;
|
|
1057
1629
|
delete process.env.TMUX;
|
|
1058
1630
|
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'prompt';
|
|
1059
1631
|
process.env.OMX_TEAM_WORKER_CLI = 'codex';
|
|
1060
1632
|
process.env.OMX_TEAM_WORKER_LAUNCH_ARGS = '--model gpt-5.3-codex-spark -c model_reasoning_effort="low"';
|
|
1061
|
-
process.env.OMX_CODEX_ARGV_CAPTURE_PATH = capturePath;
|
|
1062
|
-
let runtime = null;
|
|
1063
1633
|
try {
|
|
1064
|
-
|
|
1065
|
-
assert.equal(
|
|
1066
|
-
assert.equal((runtime.config.workers[0]?.pid ?? 0) > 0, true);
|
|
1067
|
-
let argv = null;
|
|
1068
|
-
for (let attempt = 0; attempt < 50; attempt += 1) {
|
|
1069
|
-
if (existsSync(capturePath)) {
|
|
1070
|
-
argv = JSON.parse(await readFile(capturePath, 'utf-8'));
|
|
1071
|
-
break;
|
|
1072
|
-
}
|
|
1073
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
1074
|
-
}
|
|
1075
|
-
assert.ok(argv, 'codex argv capture file should be written');
|
|
1076
|
-
assert.equal(argv.includes('--dangerously-bypass-approvals-and-sandbox'), true);
|
|
1077
|
-
assert.equal(argv.filter((arg) => arg === '--dangerously-bypass-approvals-and-sandbox').length, 1);
|
|
1078
|
-
assert.equal(argv.includes('--model'), true);
|
|
1079
|
-
assert.equal(argv[argv.indexOf('--model') + 1], 'gpt-5.3-codex-spark');
|
|
1080
|
-
assert.equal(argv.includes('-c'), true);
|
|
1081
|
-
assert.equal(argv.includes('model_reasoning_effort="low"'), true);
|
|
1082
|
-
assert.equal(argv.some((arg) => arg.includes('model_instructions_file=')), true);
|
|
1083
|
-
await shutdownTeam(runtime.teamName, cwd, { force: true });
|
|
1084
|
-
runtime = null;
|
|
1634
|
+
await assert.rejects(() => withoutTeamWorkerEnv(() => startTeam('team-codex-explicit-launch', 'codex prompt-mode team bootstrap', 'explore', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd)), /prompt_mode_codex_requires_tty/);
|
|
1635
|
+
assert.equal(existsSync(join(cwd, '.omx', 'state', 'team', 'team-codex-explicit-launch')), false);
|
|
1085
1636
|
}
|
|
1086
1637
|
finally {
|
|
1087
|
-
if (runtime) {
|
|
1088
|
-
await shutdownTeam(runtime.teamName, cwd, { force: true }).catch(() => { });
|
|
1089
|
-
}
|
|
1090
1638
|
if (typeof prevPath === 'string')
|
|
1091
1639
|
process.env.PATH = prevPath;
|
|
1092
1640
|
else
|
|
@@ -1107,10 +1655,6 @@ process.on('SIGTERM', () => process.exit(0));
|
|
|
1107
1655
|
process.env.OMX_TEAM_WORKER_LAUNCH_ARGS = prevLaunchArgs;
|
|
1108
1656
|
else
|
|
1109
1657
|
delete process.env.OMX_TEAM_WORKER_LAUNCH_ARGS;
|
|
1110
|
-
if (typeof prevCapture === 'string')
|
|
1111
|
-
process.env.OMX_CODEX_ARGV_CAPTURE_PATH = prevCapture;
|
|
1112
|
-
else
|
|
1113
|
-
delete process.env.OMX_CODEX_ARGV_CAPTURE_PATH;
|
|
1114
1658
|
await rm(cwd, { recursive: true, force: true });
|
|
1115
1659
|
}
|
|
1116
1660
|
});
|
|
@@ -1147,10 +1691,10 @@ process.on('SIGTERM', () => process.exit(0));
|
|
|
1147
1691
|
process.env.OMX_ARGV_CAPTURE_DIR = captureDir;
|
|
1148
1692
|
let runtime = null;
|
|
1149
1693
|
try {
|
|
1150
|
-
runtime = await withoutTeamWorkerEnv(() => startTeam('team-role-routing', 'heuristic routing handoff', 'executor', 2, [
|
|
1694
|
+
runtime = await withMockPromptModeCodexAllowed(() => withoutTeamWorkerEnv(() => startTeam('team-role-routing', 'heuristic routing handoff', 'executor', 2, [
|
|
1151
1695
|
{ subject: 'test routing report only', description: 'test routing report only', owner: 'worker-1', role: 'test-engineer' },
|
|
1152
1696
|
{ subject: 'document routing report only', description: 'document routing report only', owner: 'worker-2', role: 'writer' },
|
|
1153
|
-
], cwd));
|
|
1697
|
+
], cwd)));
|
|
1154
1698
|
assert.equal(runtime.config.worker_launch_mode, 'prompt');
|
|
1155
1699
|
assert.equal(runtime.config.workers[0]?.role, 'test-engineer');
|
|
1156
1700
|
assert.equal(runtime.config.workers[1]?.role, 'writer');
|
|
@@ -1256,9 +1800,9 @@ process.on('SIGTERM', () => process.exit(0));
|
|
|
1256
1800
|
process.env.OMX_TEAM_WORKER_LAUNCH_ARGS = '--model gpt-5.4-mini-tuned';
|
|
1257
1801
|
let runtime = null;
|
|
1258
1802
|
try {
|
|
1259
|
-
runtime = await withoutTeamWorkerEnv(() => startTeam('team-mini-tuned-routing', 'mini tuned routing handoff', 'executor', 1, [
|
|
1803
|
+
runtime = await withMockPromptModeCodexAllowed(() => withoutTeamWorkerEnv(() => startTeam('team-mini-tuned-routing', 'mini tuned routing handoff', 'executor', 1, [
|
|
1260
1804
|
{ subject: 'document routing report only', description: 'document routing report only', owner: 'worker-1', role: 'writer' },
|
|
1261
|
-
], cwd));
|
|
1805
|
+
], cwd)));
|
|
1262
1806
|
const workerInstructions = await readFile(join(cwd, '.omx', 'state', 'team', runtime.teamName, 'workers', 'worker-1', 'AGENTS.md'), 'utf-8');
|
|
1263
1807
|
assert.match(workerInstructions, /You are operating as the \*\*writer\*\* role/);
|
|
1264
1808
|
assert.match(workerInstructions, /You are Writer\./);
|
|
@@ -1311,7 +1855,7 @@ process.on('SIGTERM', () => process.exit(0));
|
|
|
1311
1855
|
await rm(cwd, { recursive: true, force: true });
|
|
1312
1856
|
}
|
|
1313
1857
|
});
|
|
1314
|
-
it('startTeam
|
|
1858
|
+
it('startTeam rejects codex prompt mode without tmux with an explicit non-tty error', async () => {
|
|
1315
1859
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-prompt-'));
|
|
1316
1860
|
const binDir = join(cwd, 'bin');
|
|
1317
1861
|
const fakeCodexPath = join(binDir, 'codex');
|
|
@@ -1329,19 +1873,11 @@ process.on('SIGTERM', () => process.exit(0));
|
|
|
1329
1873
|
delete process.env.TMUX;
|
|
1330
1874
|
process.env.OMX_TEAM_WORKER_LAUNCH_MODE = 'prompt';
|
|
1331
1875
|
process.env.OMX_TEAM_WORKER_CLI = 'codex';
|
|
1332
|
-
let runtime = null;
|
|
1333
1876
|
try {
|
|
1334
|
-
|
|
1335
|
-
assert.equal(
|
|
1336
|
-
assert.equal(runtime.config.leader_pane_id, null);
|
|
1337
|
-
assert.equal((runtime.config.workers[0]?.pid ?? 0) > 0, true);
|
|
1338
|
-
await shutdownTeam(runtime.teamName, cwd, { force: true });
|
|
1339
|
-
runtime = null;
|
|
1877
|
+
await assert.rejects(() => withoutTeamWorkerEnv(() => startTeam('team-prompt', 'prompt-mode team bootstrap', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], cwd)), /prompt_mode_codex_requires_tty/);
|
|
1878
|
+
assert.equal(existsSync(join(cwd, '.omx', 'state', 'team', 'team-prompt')), false);
|
|
1340
1879
|
}
|
|
1341
1880
|
finally {
|
|
1342
|
-
if (runtime) {
|
|
1343
|
-
await shutdownTeam(runtime.teamName, cwd, { force: true }).catch(() => { });
|
|
1344
|
-
}
|
|
1345
1881
|
if (typeof prevPath === 'string')
|
|
1346
1882
|
process.env.PATH = prevPath;
|
|
1347
1883
|
else
|
|
@@ -1520,7 +2056,7 @@ process.on('SIGTERM', () => process.exit(0));
|
|
|
1520
2056
|
process.env.OMX_TEST_LOG_DIR = logDir;
|
|
1521
2057
|
let runtime = null;
|
|
1522
2058
|
try {
|
|
1523
|
-
runtime = await withoutTeamWorkerEnv(() => startTeam('team-detached-worktree-paths', 'detached worktree path resolution', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], repo, { worktreeMode: { enabled: true, detached: true, name: null } }));
|
|
2059
|
+
runtime = await withMockPromptModeCodexAllowed(() => withoutTeamWorkerEnv(() => startTeam('team-detached-worktree-paths', 'detached worktree path resolution', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], repo, { worktreeMode: { enabled: true, detached: true, name: null } })));
|
|
1524
2060
|
const workerPath = runtime.config.workers[0]?.worktree_path;
|
|
1525
2061
|
assert.ok(workerPath, 'detached worker should have a worktree path');
|
|
1526
2062
|
assert.notEqual(workerPath, repo);
|
|
@@ -1592,7 +2128,7 @@ process.on('SIGTERM', () => process.exit(0));
|
|
|
1592
2128
|
process.env.OMX_TEAM_WORKER_CLI = 'codex';
|
|
1593
2129
|
let runtime = null;
|
|
1594
2130
|
try {
|
|
1595
|
-
runtime = await withoutTeamWorkerEnv(() => startTeam('team-detached-worktree-shutdown', 'detached worktree shutdown cleanup', 'executor', 1, [], repo, { worktreeMode: { enabled: true, detached: true, name: null } }));
|
|
2131
|
+
runtime = await withMockPromptModeCodexAllowed(() => withoutTeamWorkerEnv(() => startTeam('team-detached-worktree-shutdown', 'detached worktree shutdown cleanup', 'executor', 1, [], repo, { worktreeMode: { enabled: true, detached: true, name: null } })));
|
|
1596
2132
|
const worktreePath = runtime.config.workers[0]?.worktree_path;
|
|
1597
2133
|
assert.ok(worktreePath, 'worker worktree path should be persisted');
|
|
1598
2134
|
assert.equal(runtime.config.workers[0]?.worktree_created, true);
|
|
@@ -1659,7 +2195,7 @@ process.on('SIGTERM', () => process.exit(0));
|
|
|
1659
2195
|
process.env.OMX_TEST_LOG_DIR = logDir;
|
|
1660
2196
|
let runtime = null;
|
|
1661
2197
|
try {
|
|
1662
|
-
runtime = await withoutTeamWorkerEnv(() => startTeam('team-detached-worktree-resume-metadata', 'detached worktree resume metadata', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], repo, { worktreeMode: { enabled: true, detached: true, name: null } }));
|
|
2198
|
+
runtime = await withMockPromptModeCodexAllowed(() => withoutTeamWorkerEnv(() => startTeam('team-detached-worktree-resume-metadata', 'detached worktree resume metadata', 'executor', 1, [{ subject: 's', description: 'd', owner: 'worker-1' }], repo, { worktreeMode: { enabled: true, detached: true, name: null } })));
|
|
1663
2199
|
const originalWorker = runtime.config.workers[0];
|
|
1664
2200
|
const originalWorktreePath = originalWorker?.worktree_path;
|
|
1665
2201
|
assert.ok(originalWorktreePath, 'worker worktree path should be persisted before resume');
|
|
@@ -3101,6 +3637,78 @@ esac
|
|
|
3101
3637
|
await rm(cwd, { recursive: true, force: true });
|
|
3102
3638
|
}
|
|
3103
3639
|
});
|
|
3640
|
+
it('shutdownTeam reconciles stale leader and hud pane ids before native Windows split-pane teardown', async () => {
|
|
3641
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-win32-stale-topology-'));
|
|
3642
|
+
const teamName = 'team-win32-stale-topo';
|
|
3643
|
+
try {
|
|
3644
|
+
await withNativeWindowsPlatform(async () => {
|
|
3645
|
+
await withMockTmuxFixture({
|
|
3646
|
+
dirPrefix: 'omx-runtime-shutdown-win32-stale-topology-bin-',
|
|
3647
|
+
tmuxScript: (tmuxLogPath) => `#!/bin/sh
|
|
3648
|
+
set -eu
|
|
3649
|
+
printf '%s\\n' "$*" >> "${tmuxLogPath}"
|
|
3650
|
+
case "$1" in
|
|
3651
|
+
-V)
|
|
3652
|
+
echo "tmux 3.4"
|
|
3653
|
+
exit 0
|
|
3654
|
+
;;
|
|
3655
|
+
list-panes)
|
|
3656
|
+
case "$*" in
|
|
3657
|
+
*"-F #{pane_dead} #{pane_pid}"*)
|
|
3658
|
+
exit 1
|
|
3659
|
+
;;
|
|
3660
|
+
*"-t leader:0 -F #{pane_id}"*"#{pane_current_command}"*)
|
|
3661
|
+
printf "%%21\\tpwsh\\tpwsh\\n%%22\\tnode\\tnode /tmp/bin/omx.js hud --watch\\n%%23\\tcodex\\tcodex\\n%%24\\tcodex\\tcodex\\n"
|
|
3662
|
+
exit 0
|
|
3663
|
+
;;
|
|
3664
|
+
*)
|
|
3665
|
+
exit 1
|
|
3666
|
+
;;
|
|
3667
|
+
esac
|
|
3668
|
+
;;
|
|
3669
|
+
split-window)
|
|
3670
|
+
printf '%%44\\n'
|
|
3671
|
+
exit 0
|
|
3672
|
+
;;
|
|
3673
|
+
kill-pane|resize-pane|select-pane)
|
|
3674
|
+
exit 0
|
|
3675
|
+
;;
|
|
3676
|
+
*)
|
|
3677
|
+
exit 0
|
|
3678
|
+
;;
|
|
3679
|
+
esac
|
|
3680
|
+
`,
|
|
3681
|
+
}, async ({ tmuxLogPath }) => {
|
|
3682
|
+
await initTeamState(teamName, 'shutdown win32 stale topology test', 'executor', 2, cwd);
|
|
3683
|
+
const config = await readTeamConfig(teamName, cwd);
|
|
3684
|
+
assert.ok(config);
|
|
3685
|
+
if (!config)
|
|
3686
|
+
return;
|
|
3687
|
+
config.tmux_session = 'leader:0';
|
|
3688
|
+
config.leader_pane_id = '%11';
|
|
3689
|
+
config.hud_pane_id = '%12';
|
|
3690
|
+
config.workers[0].pane_id = '%23';
|
|
3691
|
+
config.workers[1].pane_id = '%24';
|
|
3692
|
+
await saveTeamConfig(config, cwd);
|
|
3693
|
+
await shutdownTeam(teamName, cwd, { force: true });
|
|
3694
|
+
const teamRoot = join(cwd, '.omx', 'state', 'team', teamName);
|
|
3695
|
+
assert.equal(existsSync(teamRoot), false);
|
|
3696
|
+
assert.equal(await readMonitorSnapshot(teamName, cwd), null);
|
|
3697
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf-8');
|
|
3698
|
+
assert.doesNotMatch(tmuxLog, /list-panes -t %21 -F #\{pane_pid\}/);
|
|
3699
|
+
assert.doesNotMatch(tmuxLog, /kill-pane -t %21/);
|
|
3700
|
+
assert.match(tmuxLog, /kill-pane -t %22/);
|
|
3701
|
+
assert.match(tmuxLog, /kill-pane -t %23/);
|
|
3702
|
+
assert.match(tmuxLog, /kill-pane -t %24/);
|
|
3703
|
+
assert.match(tmuxLog, new RegExp(`split-window -v -l ${HUD_TMUX_TEAM_HEIGHT_LINES} -t %21 -d -P -F #\\{pane_id\\}`));
|
|
3704
|
+
assert.match(tmuxLog, /select-pane -t %21/);
|
|
3705
|
+
});
|
|
3706
|
+
});
|
|
3707
|
+
}
|
|
3708
|
+
finally {
|
|
3709
|
+
await rm(cwd, { recursive: true, force: true });
|
|
3710
|
+
}
|
|
3711
|
+
});
|
|
3104
3712
|
it('shutdownTeam skips prekill and keeps the leader pane alive on shared-session shutdown', async () => {
|
|
3105
3713
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-runtime-shutdown-shared-session-'));
|
|
3106
3714
|
try {
|