oh-my-codex 0.20.2 → 0.20.3
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 +7 -6
- package/Cargo.toml +1 -1
- package/README.md +26 -0
- package/crates/omx-runtime/Cargo.toml +1 -0
- package/crates/omx-runtime/src/main.rs +25 -2
- package/crates/omx-runtime/tests/execution.rs +219 -0
- package/crates/omx-runtime-core/src/dispatch.rs +96 -10
- package/crates/omx-runtime-core/src/engine.rs +839 -29
- package/crates/omx-runtime-core/src/lib.rs +8 -0
- package/dist/agents/__tests__/definitions.test.js +81 -0
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +25 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/native-config.d.ts +2 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js.map +1 -1
- package/dist/auth/hotswap.d.ts +6 -0
- package/dist/auth/hotswap.d.ts.map +1 -1
- package/dist/auth/hotswap.js +85 -15
- package/dist/auth/hotswap.js.map +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/cli/__tests__/auth.test.js +60 -6
- package/dist/cli/__tests__/auth.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +80 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +103 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/exec.test.js +136 -0
- package/dist/cli/__tests__/exec.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +258 -4
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +287 -1
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +4 -1
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/native-hook-claim-journal.test.js +84 -6
- package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +3 -3
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +3 -3
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +56 -171
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -1
- package/dist/cli/__tests__/ralplan.test.js +37 -111
- package/dist/cli/__tests__/ralplan.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +11 -10
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-search-help.test.js +25 -1
- package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +42 -4
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +105 -7
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +46 -0
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +2 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
- package/dist/cli/doctor.d.ts +3 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +15 -2
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +4 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +138 -38
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-parity.js +2 -2
- package/dist/cli/mcp-parity.js.map +1 -1
- package/dist/cli/native-hook-claim-journal.d.ts +16 -5
- package/dist/cli/native-hook-claim-journal.d.ts.map +1 -1
- package/dist/cli/native-hook-claim-journal.js +42 -29
- package/dist/cli/native-hook-claim-journal.js.map +1 -1
- package/dist/cli/ralph.js +2 -2
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/ralplan.d.ts +4 -8
- package/dist/cli/ralplan.d.ts.map +1 -1
- package/dist/cli/ralplan.js +19 -112
- package/dist/cli/ralplan.js.map +1 -1
- package/dist/cli/session-search.d.ts +5 -0
- package/dist/cli/session-search.d.ts.map +1 -1
- package/dist/cli/session-search.js +87 -0
- package/dist/cli/session-search.js.map +1 -1
- package/dist/cli/setup.d.ts +5 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +80 -31
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/uninstall.d.ts +9 -0
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +61 -19
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +330 -1
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +2 -2
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +67 -3
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/generator.d.ts +4 -20
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +499 -119
- package/dist/config/generator.js.map +1 -1
- package/dist/config/models.d.ts +12 -2
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +15 -1
- package/dist/config/models.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +2 -2
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +4 -4
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +238 -543
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +38 -4
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +96 -9
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +531 -12
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +155 -23
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js +68 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +43 -4
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/session.test.js +348 -2
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/agents-overlay.js +1 -1
- package/dist/hooks/session.d.ts +26 -2
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +169 -13
- package/dist/hooks/session.js.map +1 -1
- package/dist/leader/__tests__/contract.test.js +57 -7
- package/dist/leader/__tests__/contract.test.js.map +1 -1
- package/dist/leader/contract.d.ts +16 -0
- package/dist/leader/contract.d.ts.map +1 -1
- package/dist/leader/contract.js +79 -4
- package/dist/leader/contract.js.map +1 -1
- package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
- package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
- package/dist/mcp/__tests__/wiki-server.test.js +18 -0
- package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
- package/dist/mcp/code-intel-server.d.ts.map +1 -1
- package/dist/mcp/code-intel-server.js +13 -3
- package/dist/mcp/code-intel-server.js.map +1 -1
- package/dist/mcp/wiki-server.d.ts +11 -0
- package/dist/mcp/wiki-server.d.ts.map +1 -1
- package/dist/mcp/wiki-server.js +9 -2
- package/dist/mcp/wiki-server.js.map +1 -1
- package/dist/modes/__tests__/base-tmux-pane.test.js +4 -4
- package/dist/modes/__tests__/base-tmux-pane.test.js.map +1 -1
- package/dist/notifications/temp-contract.d.ts.map +1 -1
- package/dist/notifications/temp-contract.js +4 -0
- package/dist/notifications/temp-contract.js.map +1 -1
- package/dist/pipeline/__tests__/orchestrator.test.js +4 -4
- package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +8 -8
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js +134 -10
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js +46 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
- package/dist/ralplan/__tests__/runtime.test.js +8 -4
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +58 -50
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/ralplan/documented-leader-preflight.d.ts +34 -0
- package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
- package/dist/ralplan/documented-leader-preflight.js +58 -0
- package/dist/ralplan/documented-leader-preflight.js.map +1 -0
- package/dist/ralplan/runtime.d.ts +1 -0
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +11 -7
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/runtime/__tests__/bridge.test.js +42 -1
- package/dist/runtime/__tests__/bridge.test.js.map +1 -1
- package/dist/runtime/bridge.d.ts +12 -0
- package/dist/runtime/bridge.d.ts.map +1 -1
- package/dist/runtime/bridge.js +52 -1
- package/dist/runtime/bridge.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +2620 -487
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts +2 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +46 -173
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -1
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +39 -159
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js +254 -2
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts +4 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +11013 -1153
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +82 -108
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +74 -3
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts +9 -0
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +234 -79
- 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 +39 -13
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts +12 -4
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.js +19 -18
- package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker.d.ts +3 -0
- package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +37 -28
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +28 -2
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +1 -0
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +48 -3
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +2796 -13
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/state/__tests__/mode-state-context.test.js +54 -0
- package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +2 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/mode-state-context.d.ts +5 -0
- package/dist/state/mode-state-context.d.ts.map +1 -1
- package/dist/state/mode-state-context.js +59 -0
- package/dist/state/mode-state-context.js.map +1 -1
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +24 -245
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -1
- package/dist/subagents/tracker.d.ts +1 -82
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +3 -275
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/api-interop.test.js +91 -1
- package/dist/team/__tests__/api-interop.test.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +52 -10
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
- package/dist/team/__tests__/idle-nudge.test.js +45 -15
- package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
- package/dist/team/__tests__/mcp-comm.test.js +93 -27
- package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +110 -3
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/runtime-cli.test.js +9 -0
- package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +3204 -335
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +2359 -142
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state.test.js +804 -36
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/team-ops-contract.test.js +6 -0
- package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +3049 -784
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +38 -2
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/__tests__/worktree.test.js +6 -0
- package/dist/team/__tests__/worktree.test.js.map +1 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +15 -9
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/exact-pane.d.ts +23 -0
- package/dist/team/exact-pane.d.ts.map +1 -0
- package/dist/team/exact-pane.js +133 -0
- package/dist/team/exact-pane.js.map +1 -0
- package/dist/team/idle-nudge.d.ts +12 -1
- package/dist/team/idle-nudge.d.ts.map +1 -1
- package/dist/team/idle-nudge.js +41 -14
- package/dist/team/idle-nudge.js.map +1 -1
- package/dist/team/model-contract.d.ts +2 -5
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +59 -17
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/runtime.d.ts +1 -0
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +1244 -180
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts +13 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +1430 -384
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/dispatch.d.ts.map +1 -1
- package/dist/team/state/dispatch.js +26 -8
- package/dist/team/state/dispatch.js.map +1 -1
- package/dist/team/state/locks.d.ts.map +1 -1
- package/dist/team/state/locks.js +61 -13
- package/dist/team/state/locks.js.map +1 -1
- package/dist/team/state/tasks.d.ts.map +1 -1
- package/dist/team/state/tasks.js +4 -3
- package/dist/team/state/tasks.js.map +1 -1
- package/dist/team/state.d.ts +67 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +636 -129
- package/dist/team/state.js.map +1 -1
- package/dist/team/team-ops.d.ts +5 -0
- package/dist/team/team-ops.d.ts.map +1 -1
- package/dist/team/team-ops.js +4 -0
- package/dist/team/team-ops.js.map +1 -1
- package/dist/team/tmux-session.d.ts +123 -23
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +1250 -291
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worktree.d.ts.map +1 -1
- package/dist/team/worktree.js +4 -0
- package/dist/team/worktree.js.map +1 -1
- package/dist/utils/__tests__/file-durability.test.d.ts +2 -0
- package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
- package/dist/utils/__tests__/file-durability.test.js +57 -0
- package/dist/utils/__tests__/file-durability.test.js.map +1 -0
- package/dist/utils/file-durability.d.ts +15 -0
- package/dist/utils/file-durability.d.ts.map +1 -0
- package/dist/utils/file-durability.js +37 -0
- package/dist/utils/file-durability.js.map +1 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +3 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
- package/dist/wiki/lint.d.ts +4 -1
- package/dist/wiki/lint.d.ts.map +1 -1
- package/dist/wiki/lint.js +3 -2
- package/dist/wiki/lint.js.map +1 -1
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +24 -0
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -4
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +3 -2
- package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
- package/skills/ralph/SKILL.md +6 -4
- package/skills/ralplan/SKILL.md +3 -2
- package/skills/team/SKILL.md +13 -9
- package/src/scripts/__tests__/codex-native-hook.test.ts +2826 -497
- package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
- package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +48 -185
- package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +44 -177
- package/src/scripts/__tests__/smoke-packed-install.test.ts +320 -2
- package/src/scripts/codex-native-hook.ts +10715 -1069
- package/src/scripts/notify-fallback-watcher.ts +84 -115
- package/src/scripts/notify-hook/auto-nudge.ts +71 -3
- package/src/scripts/notify-hook/team-dispatch.ts +242 -75
- package/src/scripts/notify-hook/team-leader-nudge.ts +40 -18
- package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
- package/src/scripts/notify-hook/team-worker-stop.ts +20 -16
- package/src/scripts/notify-hook/team-worker.ts +39 -26
- package/src/scripts/notify-hook/tmux-injection.ts +28 -2
- package/src/scripts/notify-hook.ts +1 -0
- package/src/scripts/smoke-packed-install.ts +3196 -14
- package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +0 -2
- package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +0 -1
- package/dist/subagents/__tests__/adapted-role-binding.test.js +0 -988
- package/dist/subagents/__tests__/adapted-role-binding.test.js.map +0 -1
- package/dist/subagents/adapted-role-binding.d.ts +0 -17
- package/dist/subagents/adapted-role-binding.d.ts.map +0 -1
- package/dist/subagents/adapted-role-binding.js +0 -75
- package/dist/subagents/adapted-role-binding.js.map +0 -1
|
@@ -7,7 +7,7 @@ import { join } from 'node:path';
|
|
|
7
7
|
import { spawn, spawnSync } from 'node:child_process';
|
|
8
8
|
import { randomUUID } from 'node:crypto';
|
|
9
9
|
import { initTeamState, enqueueDispatchRequest, readDispatchRequest } from '../../team/state.js';
|
|
10
|
-
import {
|
|
10
|
+
import { buildWindowsMsysBackgroundHelperBootstrapScript } from '../../cli/index.js';
|
|
11
11
|
import { writeSessionStart } from '../session.js';
|
|
12
12
|
const DEFAULT_AUTO_NUDGE_RESPONSE = 'continue with the current task only if it is already authorized';
|
|
13
13
|
const INHERITED_OMX_ENV_KEYS = [
|
|
@@ -104,13 +104,15 @@ async function writeCanonicalWatcherTeamFixture(wd, { teamName = 'dispatch-team'
|
|
|
104
104
|
},
|
|
105
105
|
tmux_session: `${teamName}:0`,
|
|
106
106
|
leader_pane_id: '%42',
|
|
107
|
+
leader_pane_pid: 4242,
|
|
108
|
+
tmux_pane_owner_id: `team:${teamName}`,
|
|
107
109
|
hud_pane_id: null,
|
|
108
110
|
resize_hook_name: null,
|
|
109
111
|
resize_hook_target: null,
|
|
110
112
|
worker_count: 1,
|
|
111
113
|
next_task_id: 1,
|
|
112
114
|
workers: [
|
|
113
|
-
{ name: 'worker-1', index: 1, pane_id: '%42', role: 'executor' },
|
|
115
|
+
{ name: 'worker-1', index: 1, pane_id: '%42', pid: 4242, role: 'executor' },
|
|
114
116
|
],
|
|
115
117
|
created_at: nowIso,
|
|
116
118
|
};
|
|
@@ -119,8 +121,12 @@ async function writeCanonicalWatcherTeamFixture(wd, { teamName = 'dispatch-team'
|
|
|
119
121
|
name: teamName,
|
|
120
122
|
tmux_session: `${teamName}:0`,
|
|
121
123
|
leader_pane_id: '%42',
|
|
124
|
+
leader_pane_pid: 4242,
|
|
125
|
+
tmux_pane_owner_id: `team:${teamName}`,
|
|
126
|
+
hud_pane_id: null,
|
|
127
|
+
hud_pane_pid: null,
|
|
122
128
|
workers: [
|
|
123
|
-
{ name: 'worker-1', pane_id: '%42' },
|
|
129
|
+
{ name: 'worker-1', pane_id: '%42', pid: 4242 },
|
|
124
130
|
],
|
|
125
131
|
}, null, 2));
|
|
126
132
|
await writeFile(join(teamDir, 'phase.json'), JSON.stringify({
|
|
@@ -129,6 +135,22 @@ async function writeCanonicalWatcherTeamFixture(wd, { teamName = 'dispatch-team'
|
|
|
129
135
|
transitions: terminal ? [{ from: 'team-exec', to: 'complete', at: nowIso }] : [],
|
|
130
136
|
}, null, 2));
|
|
131
137
|
}
|
|
138
|
+
async function bindCanonicalDispatchWorkerFixture(wd, { teamName = 'dispatch-team', paneId = '%42', panePid = 4242, ownerId = `team:${teamName}`, hudPaneId = null, } = {}) {
|
|
139
|
+
for (const fileName of ['config.json', 'manifest.v2.json']) {
|
|
140
|
+
const statePath = join(wd, '.omx', 'state', 'team', teamName, fileName);
|
|
141
|
+
const state = JSON.parse(await readFile(statePath, 'utf8'));
|
|
142
|
+
const workers = Array.isArray(state.workers) ? state.workers : [];
|
|
143
|
+
state.tmux_pane_owner_id = ownerId;
|
|
144
|
+
state.leader_pane_id = paneId;
|
|
145
|
+
state.leader_pane_pid = panePid;
|
|
146
|
+
state.hud_pane_id = hudPaneId;
|
|
147
|
+
state.hud_pane_pid = null;
|
|
148
|
+
state.workers = workers.map((worker, index) => (index === 0 && worker && typeof worker === 'object'
|
|
149
|
+
? { ...worker, pane_id: paneId, pid: panePid }
|
|
150
|
+
: worker));
|
|
151
|
+
await writeFile(statePath, JSON.stringify(state, null, 2));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
132
154
|
async function sleep(ms) {
|
|
133
155
|
await new Promise(resolve => setTimeout(resolve, ms));
|
|
134
156
|
}
|
|
@@ -171,6 +193,19 @@ set -eu
|
|
|
171
193
|
echo "$@" >> "${tmuxLogPath}"
|
|
172
194
|
cmd="$1"
|
|
173
195
|
shift || true
|
|
196
|
+
if [[ "$cmd" == "list-panes" && "$#" -eq 3 && "$1" == "-a" && "$2" == "-F" && "$3" == "#{pane_id}\t#{pane_dead}\t#{pane_pid}" ]]; then
|
|
197
|
+
printf '%%42\t0\t${options.panePid ?? 4242}\n'
|
|
198
|
+
${options.ralphPane ? `printf '${options.ralphPane.paneId.replace('%', '%%')}\\t0\\t${options.ralphPane.panePid}\\n'` : ''}
|
|
199
|
+
exit 0
|
|
200
|
+
fi
|
|
201
|
+
if [[ "$cmd" == "show-option" && "\${@: -1}" == "@omx_ralph_pane_owner_id" ]]; then
|
|
202
|
+
printf '%s\n' '${options.ralphPane?.paneOwner ?? options.paneOwner ?? 'ralph:owner'}'
|
|
203
|
+
exit 0
|
|
204
|
+
fi
|
|
205
|
+
if [[ "$cmd" == "show-option" && "\${@: -1}" == "@omx_team_pane_owner_id" ]]; then
|
|
206
|
+
printf '%s\n' '${options.paneOwner ?? 'team:dispatch-team'}'
|
|
207
|
+
exit 0
|
|
208
|
+
fi
|
|
174
209
|
if [[ "$cmd" == "capture-pane" ]]; then
|
|
175
210
|
if [[ -n "\${OMX_TEST_CAPTURE_SEQUENCE_FILE:-}" && -f "\${OMX_TEST_CAPTURE_SEQUENCE_FILE}" ]]; then
|
|
176
211
|
counterFile="\${OMX_TEST_CAPTURE_COUNTER_FILE:-\${OMX_TEST_CAPTURE_SEQUENCE_FILE}.idx}"
|
|
@@ -222,7 +257,7 @@ if [[ "$cmd" == "display-message" ]]; then
|
|
|
222
257
|
exit 0
|
|
223
258
|
fi
|
|
224
259
|
if [[ "$fmt" == "#S" ]]; then
|
|
225
|
-
echo "
|
|
260
|
+
echo "${options.paneSession ?? '${OMX_TEST_TMUX_SESSION_NAME:-session-test}'}"
|
|
226
261
|
exit 0
|
|
227
262
|
fi
|
|
228
263
|
exit 0
|
|
@@ -285,126 +320,6 @@ fi
|
|
|
285
320
|
exit 0
|
|
286
321
|
`;
|
|
287
322
|
}
|
|
288
|
-
function buildManagedRalphTmux(tmuxLogPath, options) {
|
|
289
|
-
const { cwd, managedSessionName, anchorPane, livePane, codexPanes, missingAnchor = false } = options;
|
|
290
|
-
const panes = (codexPanes && codexPanes.length > 0)
|
|
291
|
-
? codexPanes
|
|
292
|
-
: [{ paneId: livePane, active: true, currentCommand: 'codex', startCommand: 'codex' }];
|
|
293
|
-
const listPaneOutput = panes
|
|
294
|
-
.map((pane) => {
|
|
295
|
-
const paneId = pane.paneId;
|
|
296
|
-
const active = pane.active ? '1' : '0';
|
|
297
|
-
const currentCommand = pane.currentCommand || 'codex';
|
|
298
|
-
const startCommand = pane.startCommand || 'codex';
|
|
299
|
-
return `${paneId}\t${active}\t${currentCommand}\t${startCommand}`;
|
|
300
|
-
})
|
|
301
|
-
.join('\n');
|
|
302
|
-
const paneCommandBranches = panes
|
|
303
|
-
.map((pane) => {
|
|
304
|
-
const currentCommand = (pane.currentCommand || 'codex').replace(/"/g, '\\"');
|
|
305
|
-
const startCommand = (pane.startCommand || 'codex').replace(/"/g, '\\"');
|
|
306
|
-
return ` if [[ "$format" == "#{pane_current_command}" && "$target" == "${pane.paneId}" ]]; then
|
|
307
|
-
echo "${currentCommand}"
|
|
308
|
-
exit 0
|
|
309
|
-
fi
|
|
310
|
-
if [[ "$format" == "#{pane_start_command}" && "$target" == "${pane.paneId}" ]]; then
|
|
311
|
-
echo "${startCommand}"
|
|
312
|
-
exit 0
|
|
313
|
-
fi`;
|
|
314
|
-
})
|
|
315
|
-
.join('\n');
|
|
316
|
-
return `#!/usr/bin/env bash
|
|
317
|
-
set -eu
|
|
318
|
-
echo "$@" >> "${tmuxLogPath}"
|
|
319
|
-
cmd="$1"
|
|
320
|
-
shift || true
|
|
321
|
-
if [[ "$cmd" == "display-message" ]]; then
|
|
322
|
-
target=""
|
|
323
|
-
format=""
|
|
324
|
-
while [[ "$#" -gt 0 ]]; do
|
|
325
|
-
case "$1" in
|
|
326
|
-
-p) shift ;;
|
|
327
|
-
-t) target="$2"; shift 2 ;;
|
|
328
|
-
*) format="$1"; shift ;;
|
|
329
|
-
esac
|
|
330
|
-
done
|
|
331
|
-
if [[ "$target" == "${anchorPane}" && "${missingAnchor ? '1' : '0'}" == "1" ]]; then
|
|
332
|
-
echo "pane missing" >&2
|
|
333
|
-
exit 1
|
|
334
|
-
fi
|
|
335
|
-
if [[ "$format" == "#{pane_in_mode}" ]]; then
|
|
336
|
-
echo "0"
|
|
337
|
-
exit 0
|
|
338
|
-
fi
|
|
339
|
-
if [[ "$format" == "#{pane_id}" ]]; then
|
|
340
|
-
echo "$target"
|
|
341
|
-
exit 0
|
|
342
|
-
fi
|
|
343
|
-
if [[ "$format" == "#{pane_current_path}" ]]; then
|
|
344
|
-
echo "${cwd}"
|
|
345
|
-
exit 0
|
|
346
|
-
fi
|
|
347
|
-
${paneCommandBranches}
|
|
348
|
-
if [[ "$format" == "#S" ]]; then
|
|
349
|
-
if [[ "$target" == "${anchorPane}" || "$target" == "${livePane}" ]]; then
|
|
350
|
-
echo "${managedSessionName}"
|
|
351
|
-
exit 0
|
|
352
|
-
fi
|
|
353
|
-
echo "unknown target" >&2
|
|
354
|
-
exit 1
|
|
355
|
-
fi
|
|
356
|
-
exit 0
|
|
357
|
-
fi
|
|
358
|
-
if [[ "$cmd" == "list-panes" ]]; then
|
|
359
|
-
target=""
|
|
360
|
-
while [[ "$#" -gt 0 ]]; do
|
|
361
|
-
case "$1" in
|
|
362
|
-
-F) shift 2 ;;
|
|
363
|
-
-t) shift; target="$1" ;;
|
|
364
|
-
esac
|
|
365
|
-
shift || true
|
|
366
|
-
done
|
|
367
|
-
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
368
|
-
printf '%s\n' "${listPaneOutput}"
|
|
369
|
-
exit 0
|
|
370
|
-
fi
|
|
371
|
-
echo "can't find session" >&2
|
|
372
|
-
exit 1
|
|
373
|
-
fi
|
|
374
|
-
if [[ "$cmd" == "capture-pane" ]]; then
|
|
375
|
-
exit 0
|
|
376
|
-
fi
|
|
377
|
-
if [[ "$cmd" == "set-buffer" ]]; then
|
|
378
|
-
printf '%s' "\${@: -1}" > "${tmuxLogPath}.buffer"
|
|
379
|
-
exit 0
|
|
380
|
-
fi
|
|
381
|
-
if [[ "$cmd" == "show-buffer" ]]; then
|
|
382
|
-
if [[ -f "${tmuxLogPath}.buffer" ]]; then cat "${tmuxLogPath}.buffer"; fi
|
|
383
|
-
exit 0
|
|
384
|
-
fi
|
|
385
|
-
if [[ "$cmd" == "paste-buffer" ]]; then
|
|
386
|
-
target=""
|
|
387
|
-
while [[ "$#" -gt 0 ]]; do
|
|
388
|
-
case "$1" in
|
|
389
|
-
-t) target="$2"; shift 2 ;;
|
|
390
|
-
*) shift ;;
|
|
391
|
-
esac
|
|
392
|
-
done
|
|
393
|
-
if [[ -f "${tmuxLogPath}.buffer" ]]; then
|
|
394
|
-
echo "send-keys -t \${target} -l $(cat "${tmuxLogPath}.buffer")" >> "${tmuxLogPath}"
|
|
395
|
-
fi
|
|
396
|
-
exit 0
|
|
397
|
-
fi
|
|
398
|
-
if [[ "$cmd" == "delete-buffer" ]]; then
|
|
399
|
-
rm -f "${tmuxLogPath}.buffer"
|
|
400
|
-
exit 0
|
|
401
|
-
fi
|
|
402
|
-
if [[ "$cmd" == "send-keys" ]]; then
|
|
403
|
-
exit 0
|
|
404
|
-
fi
|
|
405
|
-
exit 0
|
|
406
|
-
`;
|
|
407
|
-
}
|
|
408
323
|
function buildCleanNotifyEnv(overrides = {}) {
|
|
409
324
|
return {
|
|
410
325
|
...process.env,
|
|
@@ -1011,6 +926,9 @@ describe('notify-fallback watcher', () => {
|
|
|
1011
926
|
active: true,
|
|
1012
927
|
current_phase: 'executing',
|
|
1013
928
|
tmux_pane_id: '%42',
|
|
929
|
+
tmux_pane_pid: 4242,
|
|
930
|
+
tmux_session_name: 'session-test',
|
|
931
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
1014
932
|
}, null, 2));
|
|
1015
933
|
await writeFile(join(wd, '.omx', 'state', 'team-state.json'), JSON.stringify({
|
|
1016
934
|
active: true,
|
|
@@ -1074,6 +992,9 @@ describe('notify-fallback watcher', () => {
|
|
|
1074
992
|
active: true,
|
|
1075
993
|
current_phase: 'executing',
|
|
1076
994
|
tmux_pane_id: '%42',
|
|
995
|
+
tmux_pane_pid: 4242,
|
|
996
|
+
tmux_session_name: 'session-test',
|
|
997
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
1077
998
|
}, null, 2));
|
|
1078
999
|
await writeFile(join(wd, '.omx', 'state', 'team-state.json'), JSON.stringify({
|
|
1079
1000
|
active: true,
|
|
@@ -1129,6 +1050,8 @@ describe('notify-fallback watcher', () => {
|
|
|
1129
1050
|
name: 'dispatch-team',
|
|
1130
1051
|
tmux_session: 'omx-team-dispatch-team',
|
|
1131
1052
|
leader_pane_id: '%42',
|
|
1053
|
+
leader_pane_pid: 4242,
|
|
1054
|
+
tmux_pane_owner_id: 'team:dispatch-team',
|
|
1132
1055
|
}, null, 2));
|
|
1133
1056
|
const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
|
|
1134
1057
|
const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
|
|
@@ -1296,6 +1219,45 @@ describe('notify-fallback watcher', () => {
|
|
|
1296
1219
|
await rm(wd, { recursive: true, force: true });
|
|
1297
1220
|
}
|
|
1298
1221
|
});
|
|
1222
|
+
it('does not keep leader mailbox nudges alive after the team becomes terminal', async () => {
|
|
1223
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-terminal-mailbox-'));
|
|
1224
|
+
const fakeBinDir = join(wd, 'fake-bin');
|
|
1225
|
+
const tmuxLogPath = join(wd, 'tmux.log');
|
|
1226
|
+
try {
|
|
1227
|
+
await writeCanonicalWatcherTeamFixture(wd, { coarseState: 'inactive', terminal: true });
|
|
1228
|
+
await mkdir(join(wd, '.omx', 'state', 'team', 'dispatch-team', 'mailbox'), { recursive: true });
|
|
1229
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
1230
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
1231
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
1232
|
+
await writeFile(join(wd, '.omx', 'state', 'team', 'dispatch-team', 'mailbox', 'leader-fixed.json'), JSON.stringify({
|
|
1233
|
+
worker: 'leader-fixed',
|
|
1234
|
+
messages: [{
|
|
1235
|
+
message_id: 'terminal-msg',
|
|
1236
|
+
from_worker: 'worker-1',
|
|
1237
|
+
to_worker: 'leader-fixed',
|
|
1238
|
+
body: 'must not wake a terminal team',
|
|
1239
|
+
created_at: new Date().toISOString(),
|
|
1240
|
+
}],
|
|
1241
|
+
}, null, 2));
|
|
1242
|
+
const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
|
|
1243
|
+
const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
|
|
1244
|
+
const result = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook], {
|
|
1245
|
+
encoding: 'utf-8',
|
|
1246
|
+
env: buildCleanNotifyEnv({
|
|
1247
|
+
PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
|
|
1248
|
+
OMX_SESSION_ID: 'sess-current',
|
|
1249
|
+
}),
|
|
1250
|
+
});
|
|
1251
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
1252
|
+
assert.equal(await readFile(tmuxLogPath, 'utf8').catch(() => ''), '');
|
|
1253
|
+
const logPath = join(wd, '.omx', 'logs', `notify-fallback-${new Date().toISOString().split('T')[0]}.jsonl`);
|
|
1254
|
+
const logEntries = await readJsonLines(logPath);
|
|
1255
|
+
assert.equal(logEntries.some((entry) => entry.type === 'leader_nudge_tick'), false);
|
|
1256
|
+
}
|
|
1257
|
+
finally {
|
|
1258
|
+
await rm(wd, { recursive: true, force: true });
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1299
1261
|
it('does not run stalled-worker leader nudges from the fallback watcher when the leader is not stale', async () => {
|
|
1300
1262
|
const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-worker-stall-nudge-'));
|
|
1301
1263
|
const fakeBinDir = join(wd, 'fake-bin');
|
|
@@ -1390,7 +1352,7 @@ if [[ "$cmd" == "list-panes" ]]; then
|
|
|
1390
1352
|
printf "%%42 12345\n%%10 12346\n%%11 12347\n"
|
|
1391
1353
|
exit 0
|
|
1392
1354
|
fi
|
|
1393
|
-
|
|
1355
|
+
printf "%%42\t0\t12345\n%%10\t0\t12346\n%%11\t0\t12347\n"
|
|
1394
1356
|
exit 0
|
|
1395
1357
|
fi
|
|
1396
1358
|
exit 0
|
|
@@ -1411,9 +1373,10 @@ exit 0
|
|
|
1411
1373
|
name: 'dispatch-team',
|
|
1412
1374
|
tmux_session: 'omx-team-dispatch-team',
|
|
1413
1375
|
leader_pane_id: '%42',
|
|
1376
|
+
leader_pane_pid: 12345,
|
|
1414
1377
|
workers: [
|
|
1415
|
-
{ name: 'worker-1', index: 1, pane_id: '%10' },
|
|
1416
|
-
{ name: 'worker-2', index: 2, pane_id: '%11' },
|
|
1378
|
+
{ name: 'worker-1', index: 1, pane_id: '%10', pid: 12346 },
|
|
1379
|
+
{ name: 'worker-2', index: 2, pane_id: '%11', pid: 12347 },
|
|
1417
1380
|
],
|
|
1418
1381
|
}, null, 2));
|
|
1419
1382
|
await writeFile(join(wd, '.omx', 'state', 'team', 'dispatch-team', 'tasks', 'task-1.json'), JSON.stringify({
|
|
@@ -1480,7 +1443,7 @@ exit 0
|
|
|
1480
1443
|
}),
|
|
1481
1444
|
});
|
|
1482
1445
|
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
1483
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf8');
|
|
1446
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf8').catch(() => '');
|
|
1484
1447
|
assert.doesNotMatch(tmuxLog, /worker panes stalled/);
|
|
1485
1448
|
assert.doesNotMatch(tmuxLog, /no progress 3m/);
|
|
1486
1449
|
assert.doesNotMatch(tmuxLog, /leader stale/);
|
|
@@ -1862,6 +1825,7 @@ exit 0
|
|
|
1862
1825
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
1863
1826
|
await writeFile(captureFile, '... ping ...');
|
|
1864
1827
|
await initTeamState('dispatch-team', 'task', 'executor', 1, wd);
|
|
1828
|
+
await bindCanonicalDispatchWorkerFixture(wd);
|
|
1865
1829
|
const queued = await enqueueDispatchRequest('dispatch-team', {
|
|
1866
1830
|
kind: 'inbox',
|
|
1867
1831
|
to_worker: 'worker-1',
|
|
@@ -1909,12 +1873,15 @@ exit 0
|
|
|
1909
1873
|
try {
|
|
1910
1874
|
await mkdir(stateDir, { recursive: true });
|
|
1911
1875
|
await mkdir(fakeBinDir, { recursive: true });
|
|
1912
|
-
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
1876
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
|
|
1913
1877
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
1914
1878
|
await writeFile(join(stateDir, 'ralph-state.json'), JSON.stringify({
|
|
1915
1879
|
active: true,
|
|
1916
1880
|
current_phase: 'executing',
|
|
1917
1881
|
tmux_pane_id: '%42',
|
|
1882
|
+
tmux_pane_pid: 4242,
|
|
1883
|
+
tmux_session_name: 'session-test',
|
|
1884
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
1918
1885
|
}, null, 2));
|
|
1919
1886
|
await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
|
|
1920
1887
|
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
@@ -1956,6 +1923,41 @@ exit 0
|
|
|
1956
1923
|
await rm(wd, { recursive: true, force: true });
|
|
1957
1924
|
}
|
|
1958
1925
|
});
|
|
1926
|
+
it('fails closed for recycled, incomplete, HUD, and taken-over Ralph pane bindings', async () => {
|
|
1927
|
+
const cases = [
|
|
1928
|
+
[{ tmux_pane_pid: 4242, tmux_session_name: 'session-test', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:owner', panePid: 4243 }],
|
|
1929
|
+
[{}, { paneOwner: 'ralph:owner' }],
|
|
1930
|
+
[{ tmux_pane_pid: 4242, tmux_session_name: 'session-test', tmux_pane_owner_id: 'team:dispatch-team' }, { paneOwner: 'team:dispatch-team' }],
|
|
1931
|
+
[{ tmux_pane_pid: 4242, tmux_session_name: 'session-test', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:owner', paneSession: 'other-session' }],
|
|
1932
|
+
[{ tmux_pane_pid: 4242, tmux_session_name: 'session-test', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:other' }],
|
|
1933
|
+
[{ tmux_pane_pid: 4242.9, tmux_session_name: 'session-test', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:owner' }],
|
|
1934
|
+
[{ tmux_pane_pid: '4.242e3', tmux_session_name: 'session-test', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:owner' }],
|
|
1935
|
+
[{ tmux_pane_pid: '9007199254740992', tmux_session_name: 'session-test', tmux_pane_owner_id: 'ralph:owner' }, { paneOwner: 'ralph:owner' }],
|
|
1936
|
+
];
|
|
1937
|
+
const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
|
|
1938
|
+
const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
|
|
1939
|
+
for (const [stateBinding, tmux] of cases) {
|
|
1940
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-binding-'));
|
|
1941
|
+
const stateDir = join(wd, '.omx', 'state');
|
|
1942
|
+
const fakeBinDir = join(wd, 'fake-bin');
|
|
1943
|
+
const tmuxLogPath = join(wd, 'tmux.log');
|
|
1944
|
+
try {
|
|
1945
|
+
await mkdir(stateDir, { recursive: true });
|
|
1946
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
1947
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, tmux));
|
|
1948
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
1949
|
+
await writeFile(join(stateDir, 'ralph-state.json'), JSON.stringify({ active: true, current_phase: 'executing', tmux_pane_id: '%42', ...stateBinding }));
|
|
1950
|
+
await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({ last_progress_at: new Date(Date.now() - 61_000).toISOString() }));
|
|
1951
|
+
await writeFile(join(stateDir, 'notify-fallback-state.json'), JSON.stringify({ ralph_continue_steer: { last_sent_at: new Date(Date.now() - 61_000).toISOString() } }));
|
|
1952
|
+
const result = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook], { encoding: 'utf-8', env: { ...buildCleanNotifyEnv(), PATH: `${fakeBinDir}:${process.env.PATH || ''}` } });
|
|
1953
|
+
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
1954
|
+
assert.doesNotMatch(await readFile(tmuxLogPath, 'utf-8').catch(() => ''), /send-keys -t %42/);
|
|
1955
|
+
}
|
|
1956
|
+
finally {
|
|
1957
|
+
await rm(wd, { recursive: true, force: true });
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1959
1961
|
it('suppresses Ralph continue steer when hud progress is still fresh after cooldown', async () => {
|
|
1960
1962
|
const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-progress-fresh-'));
|
|
1961
1963
|
const fakeBinDir = join(wd, 'fake-bin');
|
|
@@ -1971,6 +1973,9 @@ exit 0
|
|
|
1971
1973
|
active: true,
|
|
1972
1974
|
current_phase: 'executing',
|
|
1973
1975
|
tmux_pane_id: '%42',
|
|
1976
|
+
tmux_pane_pid: 4242,
|
|
1977
|
+
tmux_session_name: 'session-test',
|
|
1978
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
1974
1979
|
}, null, 2));
|
|
1975
1980
|
await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
|
|
1976
1981
|
last_progress_at: new Date(Date.now() - 5_000).toISOString(),
|
|
@@ -2007,12 +2012,15 @@ exit 0
|
|
|
2007
2012
|
try {
|
|
2008
2013
|
await mkdir(stateDir, { recursive: true });
|
|
2009
2014
|
await mkdir(fakeBinDir, { recursive: true });
|
|
2010
|
-
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
2015
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
|
|
2011
2016
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
2012
2017
|
await writeFile(join(stateDir, 'ralph-state.json'), JSON.stringify({
|
|
2013
2018
|
active: true,
|
|
2014
2019
|
current_phase: 'executing',
|
|
2015
2020
|
tmux_pane_id: '%42',
|
|
2021
|
+
tmux_pane_pid: 4242,
|
|
2022
|
+
tmux_session_name: 'session-test',
|
|
2023
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2016
2024
|
}, null, 2));
|
|
2017
2025
|
await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
|
|
2018
2026
|
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
@@ -2059,6 +2067,9 @@ exit 0
|
|
|
2059
2067
|
current_phase: 'starting',
|
|
2060
2068
|
started_at: new Date(Date.now() - 180_000).toISOString(),
|
|
2061
2069
|
tmux_pane_id: '%42',
|
|
2070
|
+
tmux_pane_pid: 4242,
|
|
2071
|
+
tmux_session_name: 'session-test',
|
|
2072
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2062
2073
|
}, null, 2));
|
|
2063
2074
|
await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
|
|
2064
2075
|
last_progress_at: new Date(Date.now() - 180_000).toISOString(),
|
|
@@ -2105,6 +2116,9 @@ exit 0
|
|
|
2105
2116
|
active: true,
|
|
2106
2117
|
current_phase: 'executing',
|
|
2107
2118
|
tmux_pane_id: '%42',
|
|
2119
|
+
tmux_pane_pid: 4242,
|
|
2120
|
+
tmux_session_name: 'session-test',
|
|
2121
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2108
2122
|
owner_omx_session_id: omxSessionId,
|
|
2109
2123
|
owner_codex_session_id: codexSessionId,
|
|
2110
2124
|
}, null, 2));
|
|
@@ -2179,6 +2193,9 @@ exit 0
|
|
|
2179
2193
|
active: true,
|
|
2180
2194
|
current_phase: 'executing',
|
|
2181
2195
|
tmux_pane_id: '%42',
|
|
2196
|
+
tmux_pane_pid: 4242,
|
|
2197
|
+
tmux_session_name: 'session-test',
|
|
2198
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2182
2199
|
}, null, 2));
|
|
2183
2200
|
await writeFile(statePath, JSON.stringify({
|
|
2184
2201
|
ralph_continue_steer: {
|
|
@@ -2245,7 +2262,7 @@ exit 0
|
|
|
2245
2262
|
const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], { encoding: 'utf-8', env });
|
|
2246
2263
|
assert.equal(run.status, 0, run.stderr || run.stdout);
|
|
2247
2264
|
const watcherState = JSON.parse(await readFile(statePath, 'utf-8'));
|
|
2248
|
-
assert.equal(watcherState.ralph_continue_steer?.last_reason, '
|
|
2265
|
+
assert.equal(watcherState.ralph_continue_steer?.last_reason, 'pane_binding_missing');
|
|
2249
2266
|
assert.equal(watcherState.ralph_continue_steer?.pane_id, '');
|
|
2250
2267
|
const tmuxLog = await readFile(tmuxLogPath, 'utf8').catch(() => '');
|
|
2251
2268
|
assert.equal(/send-keys -t %42 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/.test(tmuxLog), false);
|
|
@@ -2255,412 +2272,48 @@ exit 0
|
|
|
2255
2272
|
await rm(wd, { recursive: true, force: true });
|
|
2256
2273
|
}
|
|
2257
2274
|
});
|
|
2258
|
-
it('
|
|
2259
|
-
const
|
|
2260
|
-
const
|
|
2261
|
-
const
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
|
|
2301
|
-
const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], {
|
|
2302
|
-
encoding: 'utf-8',
|
|
2303
|
-
env: buildCleanNotifyEnv({
|
|
2304
|
-
PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
|
|
2305
|
-
}),
|
|
2306
|
-
});
|
|
2307
|
-
assert.equal(run.status, 0, run.stderr || run.stdout);
|
|
2308
|
-
const persistedRalph = JSON.parse(await readFile(ralphStatePath, 'utf-8'));
|
|
2309
|
-
assert.equal(persistedRalph.tmux_pane_id, livePane);
|
|
2310
|
-
assert.match(persistedRalph.tmux_pane_set_at ?? '', /^\d{4}-\d{2}-\d{2}T/);
|
|
2311
|
-
const watcherState = JSON.parse(await readFile(watcherStatePath, 'utf-8'));
|
|
2312
|
-
assert.equal(watcherState.ralph_continue_steer?.last_reason, 'sent');
|
|
2313
|
-
assert.equal(watcherState.ralph_continue_steer?.pane_id, livePane);
|
|
2314
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf8');
|
|
2315
|
-
assert.match(tmuxLog, /send-keys -t %42 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
|
|
2316
|
-
assert.doesNotMatch(tmuxLog, /send-keys -t %99 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
|
|
2317
|
-
}
|
|
2318
|
-
finally {
|
|
2319
|
-
await rm(wd, { recursive: true, force: true });
|
|
2320
|
-
}
|
|
2321
|
-
});
|
|
2322
|
-
it('preserves newer Ralph state fields when a pane rebound happens after the state file advances', async () => {
|
|
2323
|
-
const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-rebind-state-merge-'));
|
|
2324
|
-
const fakeBinDir = join(wd, 'fake-bin');
|
|
2325
|
-
const stateDir = join(wd, '.omx', 'state');
|
|
2326
|
-
const tmuxLogPath = join(wd, 'tmux.log');
|
|
2327
|
-
const watcherStatePath = join(stateDir, 'notify-fallback-state.json');
|
|
2328
|
-
const sessionId = 'sess-ralph-rebind-merge';
|
|
2329
|
-
const sessionStateDir = join(stateDir, 'sessions', sessionId);
|
|
2330
|
-
const ralphStatePath = join(sessionStateDir, 'ralph-state.json');
|
|
2331
|
-
const anchorPane = '%99';
|
|
2332
|
-
const livePane = '%42';
|
|
2333
|
-
try {
|
|
2334
|
-
await mkdir(sessionStateDir, { recursive: true });
|
|
2335
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2336
|
-
await writeSessionStart(wd, sessionId);
|
|
2337
|
-
const managedSessionName = buildTmuxSessionName(wd, sessionId);
|
|
2338
|
-
const fakeTmux = `#!/usr/bin/env bash
|
|
2339
|
-
set -eu
|
|
2340
|
-
echo "$@" >> "${tmuxLogPath}"
|
|
2341
|
-
cmd="$1"
|
|
2342
|
-
shift || true
|
|
2343
|
-
if [[ "$cmd" == "display-message" ]]; then
|
|
2344
|
-
target=""
|
|
2345
|
-
format=""
|
|
2346
|
-
while [[ "$#" -gt 0 ]]; do
|
|
2347
|
-
case "$1" in
|
|
2348
|
-
-p) shift ;;
|
|
2349
|
-
-t) target="$2"; shift 2 ;;
|
|
2350
|
-
*) format="$1"; shift ;;
|
|
2351
|
-
esac
|
|
2352
|
-
done
|
|
2353
|
-
if [[ "$format" == "#{pane_in_mode}" ]]; then
|
|
2354
|
-
echo "0"
|
|
2355
|
-
exit 0
|
|
2356
|
-
fi
|
|
2357
|
-
if [[ "$format" == "#{pane_id}" ]]; then
|
|
2358
|
-
echo "$target"
|
|
2359
|
-
exit 0
|
|
2360
|
-
fi
|
|
2361
|
-
if [[ "$format" == "#{pane_current_path}" ]]; then
|
|
2362
|
-
echo "${wd}"
|
|
2363
|
-
exit 0
|
|
2364
|
-
fi
|
|
2365
|
-
if [[ "$format" == "#{pane_current_command}" && "$target" == "${anchorPane}" ]]; then
|
|
2366
|
-
echo "sh"
|
|
2367
|
-
exit 0
|
|
2368
|
-
fi
|
|
2369
|
-
if [[ "$format" == "#{pane_start_command}" && "$target" == "${anchorPane}" ]]; then
|
|
2370
|
-
echo "bash"
|
|
2371
|
-
exit 0
|
|
2372
|
-
fi
|
|
2373
|
-
if [[ "$format" == "#S" && "$target" == "${anchorPane}" ]]; then
|
|
2374
|
-
echo "${managedSessionName}"
|
|
2375
|
-
exit 0
|
|
2376
|
-
fi
|
|
2377
|
-
exit 0
|
|
2378
|
-
fi
|
|
2379
|
-
if [[ "$cmd" == "list-panes" ]]; then
|
|
2380
|
-
target=""
|
|
2381
|
-
while [[ "$#" -gt 0 ]]; do
|
|
2382
|
-
case "$1" in
|
|
2383
|
-
-F) shift 2 ;;
|
|
2384
|
-
-t) shift; target="$1" ;;
|
|
2385
|
-
esac
|
|
2386
|
-
shift || true
|
|
2387
|
-
done
|
|
2388
|
-
if [[ "$target" == "${managedSessionName}" ]]; then
|
|
2389
|
-
cat > "${ralphStatePath}" <<'JSON'
|
|
2390
|
-
{
|
|
2391
|
-
"active": true,
|
|
2392
|
-
"current_phase": "reviewing",
|
|
2393
|
-
"iteration": 11,
|
|
2394
|
-
"owner_codex_session_id": "codex-updated-owner",
|
|
2395
|
-
"tmux_pane_id": "%99"
|
|
2396
|
-
}
|
|
2397
|
-
JSON
|
|
2398
|
-
printf "%%99\t0\tsh\tbash\n%%42\t1\tcodex\tcodex\n"
|
|
2399
|
-
exit 0
|
|
2400
|
-
fi
|
|
2401
|
-
echo "can't find session" >&2
|
|
2402
|
-
exit 1
|
|
2403
|
-
fi
|
|
2404
|
-
if [[ "$cmd" == "capture-pane" ]]; then
|
|
2405
|
-
exit 0
|
|
2406
|
-
fi
|
|
2407
|
-
if [[ "$cmd" == "set-buffer" ]]; then
|
|
2408
|
-
printf '%s' "\${@: -1}" > "${tmuxLogPath}.buffer"
|
|
2409
|
-
exit 0
|
|
2410
|
-
fi
|
|
2411
|
-
if [[ "$cmd" == "show-buffer" ]]; then
|
|
2412
|
-
if [[ -f "${tmuxLogPath}.buffer" ]]; then cat "${tmuxLogPath}.buffer"; fi
|
|
2413
|
-
exit 0
|
|
2414
|
-
fi
|
|
2415
|
-
if [[ "$cmd" == "paste-buffer" ]]; then
|
|
2416
|
-
target=""
|
|
2417
|
-
while [[ "$#" -gt 0 ]]; do
|
|
2418
|
-
case "$1" in
|
|
2419
|
-
-t) target="$2"; shift 2 ;;
|
|
2420
|
-
*) shift ;;
|
|
2421
|
-
esac
|
|
2422
|
-
done
|
|
2423
|
-
if [[ -f "${tmuxLogPath}.buffer" ]]; then
|
|
2424
|
-
echo "send-keys -t \${target} -l $(cat "${tmuxLogPath}.buffer")" >> "${tmuxLogPath}"
|
|
2425
|
-
fi
|
|
2426
|
-
exit 0
|
|
2427
|
-
fi
|
|
2428
|
-
if [[ "$cmd" == "delete-buffer" ]]; then
|
|
2429
|
-
rm -f "${tmuxLogPath}.buffer"
|
|
2430
|
-
exit 0
|
|
2431
|
-
fi
|
|
2432
|
-
if [[ "$cmd" == "send-keys" ]]; then
|
|
2433
|
-
exit 0
|
|
2434
|
-
fi
|
|
2435
|
-
exit 0
|
|
2436
|
-
`;
|
|
2437
|
-
await writeFile(join(fakeBinDir, 'tmux'), fakeTmux);
|
|
2438
|
-
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
2439
|
-
await writeFile(ralphStatePath, JSON.stringify({
|
|
2440
|
-
active: true,
|
|
2441
|
-
current_phase: 'executing',
|
|
2442
|
-
iteration: 1,
|
|
2443
|
-
owner_codex_session_id: 'codex-stale-owner',
|
|
2444
|
-
tmux_pane_id: anchorPane,
|
|
2445
|
-
}, null, 2));
|
|
2446
|
-
await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
|
|
2447
|
-
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
2448
|
-
}, null, 2));
|
|
2449
|
-
await writeFile(watcherStatePath, JSON.stringify({
|
|
2450
|
-
ralph_continue_steer: {
|
|
2451
|
-
last_sent_at: new Date(Date.now() - 61_000).toISOString(),
|
|
2452
|
-
},
|
|
2453
|
-
}, null, 2));
|
|
2454
|
-
const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
|
|
2455
|
-
const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
|
|
2456
|
-
const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], {
|
|
2457
|
-
encoding: 'utf-8',
|
|
2458
|
-
env: buildCleanNotifyEnv({
|
|
2459
|
-
PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
|
|
2460
|
-
}),
|
|
2461
|
-
});
|
|
2462
|
-
assert.equal(run.status, 0, run.stderr || run.stdout);
|
|
2463
|
-
const persistedRalph = JSON.parse(await readFile(ralphStatePath, 'utf-8'));
|
|
2464
|
-
assert.equal(persistedRalph.tmux_pane_id, livePane);
|
|
2465
|
-
assert.equal(persistedRalph.current_phase, 'reviewing');
|
|
2466
|
-
assert.equal(persistedRalph.iteration, 11);
|
|
2467
|
-
assert.equal(persistedRalph.owner_codex_session_id, 'codex-updated-owner');
|
|
2468
|
-
assert.match(persistedRalph.tmux_pane_set_at ?? '', /^\d{4}-\d{2}-\d{2}T/);
|
|
2469
|
-
}
|
|
2470
|
-
finally {
|
|
2471
|
-
await rm(wd, { recursive: true, force: true });
|
|
2472
|
-
}
|
|
2473
|
-
});
|
|
2474
|
-
it('keeps the verified Ralph anchor pane when another codex pane is focused in the same managed session', async () => {
|
|
2475
|
-
const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-keep-anchor-pane-'));
|
|
2476
|
-
const fakeBinDir = join(wd, 'fake-bin');
|
|
2477
|
-
const stateDir = join(wd, '.omx', 'state');
|
|
2478
|
-
const tmuxLogPath = join(wd, 'tmux.log');
|
|
2479
|
-
const watcherStatePath = join(stateDir, 'notify-fallback-state.json');
|
|
2480
|
-
const sessionId = 'sess-ralph-keep-anchor';
|
|
2481
|
-
const sessionStateDir = join(stateDir, 'sessions', sessionId);
|
|
2482
|
-
const ralphStatePath = join(sessionStateDir, 'ralph-state.json');
|
|
2483
|
-
const anchorPane = '%99';
|
|
2484
|
-
const livePane = '%42';
|
|
2485
|
-
try {
|
|
2486
|
-
await mkdir(sessionStateDir, { recursive: true });
|
|
2487
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2488
|
-
await writeSessionStart(wd, sessionId);
|
|
2489
|
-
const managedSessionName = buildTmuxSessionName(wd, sessionId);
|
|
2490
|
-
await writeFile(join(fakeBinDir, 'tmux'), buildManagedRalphTmux(tmuxLogPath, {
|
|
2491
|
-
cwd: wd,
|
|
2492
|
-
managedSessionName,
|
|
2493
|
-
anchorPane,
|
|
2494
|
-
livePane,
|
|
2495
|
-
codexPanes: [
|
|
2496
|
-
{ paneId: anchorPane, active: false, currentCommand: 'codex', startCommand: 'codex' },
|
|
2497
|
-
{ paneId: livePane, active: true, currentCommand: 'codex', startCommand: 'codex' },
|
|
2498
|
-
],
|
|
2499
|
-
}));
|
|
2500
|
-
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
2501
|
-
await writeFile(ralphStatePath, JSON.stringify({
|
|
2502
|
-
active: true,
|
|
2503
|
-
current_phase: 'executing',
|
|
2504
|
-
tmux_pane_id: anchorPane,
|
|
2505
|
-
}, null, 2));
|
|
2506
|
-
await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
|
|
2507
|
-
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
2508
|
-
}, null, 2));
|
|
2509
|
-
await writeFile(watcherStatePath, JSON.stringify({
|
|
2510
|
-
ralph_continue_steer: {
|
|
2511
|
-
last_sent_at: new Date(Date.now() - 61_000).toISOString(),
|
|
2512
|
-
},
|
|
2513
|
-
}, null, 2));
|
|
2514
|
-
const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
|
|
2515
|
-
const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
|
|
2516
|
-
const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], {
|
|
2517
|
-
encoding: 'utf-8',
|
|
2518
|
-
env: buildCleanNotifyEnv({
|
|
2519
|
-
PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
|
|
2520
|
-
}),
|
|
2521
|
-
});
|
|
2522
|
-
assert.equal(run.status, 0, run.stderr || run.stdout);
|
|
2523
|
-
const persistedRalph = JSON.parse(await readFile(ralphStatePath, 'utf-8'));
|
|
2524
|
-
assert.equal(persistedRalph.tmux_pane_id, anchorPane);
|
|
2525
|
-
assert.equal(typeof persistedRalph.tmux_pane_set_at, 'undefined');
|
|
2526
|
-
const watcherState = JSON.parse(await readFile(watcherStatePath, 'utf-8'));
|
|
2527
|
-
assert.equal(watcherState.ralph_continue_steer?.last_reason, 'sent');
|
|
2528
|
-
assert.equal(watcherState.ralph_continue_steer?.pane_id, anchorPane);
|
|
2529
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf8');
|
|
2530
|
-
assert.match(tmuxLog, /send-keys -t %99 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
|
|
2531
|
-
assert.doesNotMatch(tmuxLog, /send-keys -t %42 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
|
|
2532
|
-
}
|
|
2533
|
-
finally {
|
|
2534
|
-
await rm(wd, { recursive: true, force: true });
|
|
2535
|
-
}
|
|
2536
|
-
});
|
|
2537
|
-
it('rebinds a shell-degraded codex anchor to the live pane before continue steer', async () => {
|
|
2538
|
-
const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-rebind-degraded-codex-anchor-'));
|
|
2539
|
-
const fakeBinDir = join(wd, 'fake-bin');
|
|
2540
|
-
const stateDir = join(wd, '.omx', 'state');
|
|
2541
|
-
const tmuxLogPath = join(wd, 'tmux.log');
|
|
2542
|
-
const watcherStatePath = join(stateDir, 'notify-fallback-state.json');
|
|
2543
|
-
const sessionId = 'sess-ralph-degraded-codex-anchor';
|
|
2544
|
-
const sessionStateDir = join(stateDir, 'sessions', sessionId);
|
|
2545
|
-
const ralphStatePath = join(sessionStateDir, 'ralph-state.json');
|
|
2546
|
-
const anchorPane = '%99';
|
|
2547
|
-
const livePane = '%42';
|
|
2548
|
-
try {
|
|
2549
|
-
await mkdir(sessionStateDir, { recursive: true });
|
|
2550
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2551
|
-
await writeSessionStart(wd, sessionId);
|
|
2552
|
-
const managedSessionName = buildTmuxSessionName(wd, sessionId);
|
|
2553
|
-
await writeFile(join(fakeBinDir, 'tmux'), buildManagedRalphTmux(tmuxLogPath, {
|
|
2554
|
-
cwd: wd,
|
|
2555
|
-
managedSessionName,
|
|
2556
|
-
anchorPane,
|
|
2557
|
-
livePane,
|
|
2558
|
-
codexPanes: [
|
|
2559
|
-
{ paneId: anchorPane, active: true, currentCommand: 'bash', startCommand: 'codex --model gpt-5' },
|
|
2560
|
-
{ paneId: livePane, active: false, currentCommand: 'codex', startCommand: 'codex' },
|
|
2561
|
-
],
|
|
2562
|
-
}));
|
|
2563
|
-
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
2564
|
-
await writeFile(ralphStatePath, JSON.stringify({
|
|
2565
|
-
active: true,
|
|
2566
|
-
current_phase: 'executing',
|
|
2567
|
-
tmux_pane_id: anchorPane,
|
|
2568
|
-
}, null, 2));
|
|
2569
|
-
await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
|
|
2570
|
-
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
2571
|
-
}, null, 2));
|
|
2572
|
-
await writeFile(watcherStatePath, JSON.stringify({
|
|
2573
|
-
ralph_continue_steer: {
|
|
2574
|
-
last_sent_at: new Date(Date.now() - 61_000).toISOString(),
|
|
2575
|
-
},
|
|
2576
|
-
}, null, 2));
|
|
2577
|
-
const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
|
|
2578
|
-
const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
|
|
2579
|
-
const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], {
|
|
2580
|
-
encoding: 'utf-8',
|
|
2581
|
-
env: buildCleanNotifyEnv({
|
|
2582
|
-
PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
|
|
2583
|
-
}),
|
|
2584
|
-
});
|
|
2585
|
-
assert.equal(run.status, 0, run.stderr || run.stdout);
|
|
2586
|
-
const persistedRalph = JSON.parse(await readFile(ralphStatePath, 'utf-8'));
|
|
2587
|
-
assert.equal(persistedRalph.tmux_pane_id, livePane);
|
|
2588
|
-
assert.match(persistedRalph.tmux_pane_set_at ?? '', /^\d{4}-\d{2}-\d{2}T/);
|
|
2589
|
-
const watcherState = JSON.parse(await readFile(watcherStatePath, 'utf-8'));
|
|
2590
|
-
assert.equal(watcherState.ralph_continue_steer?.last_reason, 'sent');
|
|
2591
|
-
assert.equal(watcherState.ralph_continue_steer?.pane_id, livePane);
|
|
2592
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf8');
|
|
2593
|
-
assert.match(tmuxLog, /send-keys -t %42 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
|
|
2594
|
-
assert.doesNotMatch(tmuxLog, /send-keys -t %99 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
|
|
2595
|
-
}
|
|
2596
|
-
finally {
|
|
2597
|
-
await rm(wd, { recursive: true, force: true });
|
|
2598
|
-
}
|
|
2599
|
-
});
|
|
2600
|
-
it('falls back to the current managed session pane when the stored Ralph pane anchor is dead', async () => {
|
|
2601
|
-
const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-rebind-dead-anchor-'));
|
|
2602
|
-
const fakeBinDir = join(wd, 'fake-bin');
|
|
2603
|
-
const stateDir = join(wd, '.omx', 'state');
|
|
2604
|
-
const tmuxLogPath = join(wd, 'tmux.log');
|
|
2605
|
-
const watcherStatePath = join(stateDir, 'notify-fallback-state.json');
|
|
2606
|
-
const sessionId = 'sess-ralph-dead-anchor';
|
|
2607
|
-
const sessionStateDir = join(stateDir, 'sessions', sessionId);
|
|
2608
|
-
const ralphStatePath = join(sessionStateDir, 'ralph-state.json');
|
|
2609
|
-
const anchorPane = '%99';
|
|
2610
|
-
const livePane = '%42';
|
|
2611
|
-
try {
|
|
2612
|
-
await mkdir(sessionStateDir, { recursive: true });
|
|
2613
|
-
await mkdir(fakeBinDir, { recursive: true });
|
|
2614
|
-
await writeSessionStart(wd, sessionId);
|
|
2615
|
-
const managedSessionName = buildTmuxSessionName(wd, sessionId);
|
|
2616
|
-
await writeFile(join(fakeBinDir, 'tmux'), buildManagedRalphTmux(tmuxLogPath, {
|
|
2617
|
-
cwd: wd,
|
|
2618
|
-
managedSessionName,
|
|
2619
|
-
anchorPane,
|
|
2620
|
-
livePane,
|
|
2621
|
-
codexPanes: [
|
|
2622
|
-
{ paneId: '%41', active: false, currentCommand: 'codex', startCommand: 'codex' },
|
|
2623
|
-
{ paneId: livePane, active: true, currentCommand: 'codex', startCommand: 'codex' },
|
|
2624
|
-
],
|
|
2625
|
-
missingAnchor: true,
|
|
2626
|
-
}));
|
|
2627
|
-
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
2628
|
-
await writeFile(ralphStatePath, JSON.stringify({
|
|
2629
|
-
active: true,
|
|
2630
|
-
current_phase: 'executing',
|
|
2631
|
-
tmux_pane_id: anchorPane,
|
|
2632
|
-
}, null, 2));
|
|
2633
|
-
await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
|
|
2634
|
-
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
2635
|
-
}, null, 2));
|
|
2636
|
-
await writeFile(watcherStatePath, JSON.stringify({
|
|
2637
|
-
ralph_continue_steer: {
|
|
2638
|
-
last_sent_at: new Date(Date.now() - 61_000).toISOString(),
|
|
2639
|
-
},
|
|
2640
|
-
}, null, 2));
|
|
2641
|
-
const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
|
|
2642
|
-
const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
|
|
2643
|
-
const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], {
|
|
2644
|
-
encoding: 'utf-8',
|
|
2645
|
-
env: buildCleanNotifyEnv({
|
|
2646
|
-
PATH: `${fakeBinDir}:${process.env.PATH || ''}`,
|
|
2647
|
-
}),
|
|
2648
|
-
});
|
|
2649
|
-
assert.equal(run.status, 0, run.stderr || run.stdout);
|
|
2650
|
-
const persistedRalph = JSON.parse(await readFile(ralphStatePath, 'utf-8'));
|
|
2651
|
-
assert.equal(persistedRalph.tmux_pane_id, livePane);
|
|
2652
|
-
assert.match(persistedRalph.tmux_pane_set_at ?? '', /^\d{4}-\d{2}-\d{2}T/);
|
|
2653
|
-
const watcherState = JSON.parse(await readFile(watcherStatePath, 'utf-8'));
|
|
2654
|
-
assert.equal(watcherState.ralph_continue_steer?.last_reason, 'sent');
|
|
2655
|
-
assert.equal(watcherState.ralph_continue_steer?.pane_id, livePane);
|
|
2656
|
-
const tmuxLog = await readFile(tmuxLogPath, 'utf8');
|
|
2657
|
-
assert.match(tmuxLog, /display-message -p -t %99 #S/);
|
|
2658
|
-
assert.match(tmuxLog, /list-panes -s -t .*sess-ralph-dead-anchor/);
|
|
2659
|
-
assert.match(tmuxLog, /send-keys -t %42 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
|
|
2660
|
-
assert.doesNotMatch(tmuxLog, /send-keys -t %99 -l Ralph loop active continue \[OMX_TMUX_INJECT\]/);
|
|
2661
|
-
}
|
|
2662
|
-
finally {
|
|
2663
|
-
await rm(wd, { recursive: true, force: true });
|
|
2275
|
+
it('fails closed without rebinding stale, degraded, or missing Ralph pane anchors', async () => {
|
|
2276
|
+
const watcherScript = new URL('../../../dist/scripts/notify-fallback-watcher.js', import.meta.url).pathname;
|
|
2277
|
+
const notifyHook = new URL('../../../dist/scripts/notify-hook.js', import.meta.url).pathname;
|
|
2278
|
+
for (const scenario of ['stale shell anchor', 'degraded codex anchor', 'missing anchor']) {
|
|
2279
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-fallback-ralph-no-rebind-'));
|
|
2280
|
+
const fakeBinDir = join(wd, 'fake-bin');
|
|
2281
|
+
const stateDir = join(wd, '.omx', 'state');
|
|
2282
|
+
const tmuxLogPath = join(wd, 'tmux.log');
|
|
2283
|
+
const ralphStatePath = join(stateDir, 'ralph-state.json');
|
|
2284
|
+
try {
|
|
2285
|
+
await mkdir(stateDir, { recursive: true });
|
|
2286
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
2287
|
+
await writeFile(tmuxLogPath, '');
|
|
2288
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
|
|
2289
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
2290
|
+
await writeFile(ralphStatePath, JSON.stringify({
|
|
2291
|
+
active: true,
|
|
2292
|
+
current_phase: 'executing',
|
|
2293
|
+
tmux_pane_id: '%99',
|
|
2294
|
+
tmux_pane_pid: 4242,
|
|
2295
|
+
tmux_session_name: 'session-test',
|
|
2296
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2297
|
+
scenario,
|
|
2298
|
+
}, null, 2));
|
|
2299
|
+
await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({ last_progress_at: new Date(Date.now() - 61_000).toISOString() }));
|
|
2300
|
+
await writeFile(join(stateDir, 'notify-fallback-state.json'), JSON.stringify({ ralph_continue_steer: { last_sent_at: new Date(Date.now() - 61_000).toISOString() } }));
|
|
2301
|
+
const run = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook], {
|
|
2302
|
+
encoding: 'utf-8',
|
|
2303
|
+
env: buildCleanNotifyEnv({ PATH: `${fakeBinDir}:${process.env.PATH || ''}` }),
|
|
2304
|
+
});
|
|
2305
|
+
assert.equal(run.status, 0, run.stderr || run.stdout);
|
|
2306
|
+
const persistedRalph = JSON.parse(await readFile(ralphStatePath, 'utf-8'));
|
|
2307
|
+
assert.equal(persistedRalph.tmux_pane_id, '%99', `${scenario} must retain its stored pane identity`);
|
|
2308
|
+
const watcherState = JSON.parse(await readFile(join(stateDir, 'notify-fallback-state.json'), 'utf-8'));
|
|
2309
|
+
assert.equal(watcherState.ralph_continue_steer?.last_reason, 'pane_binding_changed');
|
|
2310
|
+
const tmuxLog = await readFile(tmuxLogPath, 'utf8');
|
|
2311
|
+
assert.doesNotMatch(tmuxLog, /send-keys -t %(42|99) -l Ralph loop active continue/);
|
|
2312
|
+
assert.doesNotMatch(tmuxLog, /list-panes -s -t/, 'watcher must not discover a replacement pane');
|
|
2313
|
+
}
|
|
2314
|
+
finally {
|
|
2315
|
+
await rm(wd, { recursive: true, force: true });
|
|
2316
|
+
}
|
|
2664
2317
|
}
|
|
2665
2318
|
});
|
|
2666
2319
|
it('sends the first Ralph continue steer immediately when persisted steer state is empty', async () => {
|
|
@@ -2672,12 +2325,15 @@ exit 0
|
|
|
2672
2325
|
try {
|
|
2673
2326
|
await mkdir(stateDir, { recursive: true });
|
|
2674
2327
|
await mkdir(fakeBinDir, { recursive: true });
|
|
2675
|
-
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
2328
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
|
|
2676
2329
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
2677
2330
|
await writeFile(join(stateDir, 'ralph-state.json'), JSON.stringify({
|
|
2678
2331
|
active: true,
|
|
2679
2332
|
current_phase: 'executing',
|
|
2680
2333
|
tmux_pane_id: '%42',
|
|
2334
|
+
tmux_pane_pid: 4242,
|
|
2335
|
+
tmux_session_name: 'session-test',
|
|
2336
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2681
2337
|
}, null, 2));
|
|
2682
2338
|
await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
|
|
2683
2339
|
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
@@ -2716,12 +2372,15 @@ exit 0
|
|
|
2716
2372
|
try {
|
|
2717
2373
|
await mkdir(stateDir, { recursive: true });
|
|
2718
2374
|
await mkdir(fakeBinDir, { recursive: true });
|
|
2719
|
-
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
2375
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
|
|
2720
2376
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
2721
2377
|
await writeFile(join(stateDir, 'ralph-state.json'), JSON.stringify({
|
|
2722
2378
|
active: true,
|
|
2723
2379
|
current_phase: 'executing',
|
|
2724
2380
|
tmux_pane_id: '%42',
|
|
2381
|
+
tmux_pane_pid: 4242,
|
|
2382
|
+
tmux_session_name: 'session-test',
|
|
2383
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2725
2384
|
}, null, 2));
|
|
2726
2385
|
await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
|
|
2727
2386
|
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
@@ -2767,6 +2426,9 @@ exit 0
|
|
|
2767
2426
|
current_phase: 'blocked_on_user',
|
|
2768
2427
|
completed_at: new Date().toISOString(),
|
|
2769
2428
|
tmux_pane_id: '%42',
|
|
2429
|
+
tmux_pane_pid: 4242,
|
|
2430
|
+
tmux_session_name: 'session-test',
|
|
2431
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2770
2432
|
}, null, 2));
|
|
2771
2433
|
await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
|
|
2772
2434
|
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
@@ -2806,12 +2468,15 @@ exit 0
|
|
|
2806
2468
|
try {
|
|
2807
2469
|
await mkdir(stateDir, { recursive: true });
|
|
2808
2470
|
await mkdir(fakeBinDir, { recursive: true });
|
|
2809
|
-
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
2471
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
|
|
2810
2472
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
2811
2473
|
await writeFile(ralphStatePath, JSON.stringify({
|
|
2812
2474
|
active: true,
|
|
2813
2475
|
current_phase: 'executing',
|
|
2814
2476
|
tmux_pane_id: '%42',
|
|
2477
|
+
tmux_pane_pid: 4242,
|
|
2478
|
+
tmux_session_name: 'session-test',
|
|
2479
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2815
2480
|
}, null, 2));
|
|
2816
2481
|
await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
|
|
2817
2482
|
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
@@ -2840,6 +2505,9 @@ exit 0
|
|
|
2840
2505
|
current_phase: 'complete',
|
|
2841
2506
|
completed_at: new Date().toISOString(),
|
|
2842
2507
|
tmux_pane_id: '%42',
|
|
2508
|
+
tmux_pane_pid: 4242,
|
|
2509
|
+
tmux_session_name: 'session-test',
|
|
2510
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2843
2511
|
}, null, 2));
|
|
2844
2512
|
const terminalRun = spawnSync(process.execPath, [watcherScript, '--once', '--cwd', wd, '--notify-script', notifyHook, '--poll-ms', '50'], { encoding: 'utf-8', env });
|
|
2845
2513
|
assert.equal(terminalRun.status, 0, terminalRun.stderr || terminalRun.stdout);
|
|
@@ -2871,6 +2539,9 @@ exit 0
|
|
|
2871
2539
|
current_phase: 'starting',
|
|
2872
2540
|
started_at: staleStartedAt,
|
|
2873
2541
|
tmux_pane_id: '%42',
|
|
2542
|
+
tmux_pane_pid: 4242,
|
|
2543
|
+
tmux_session_name: 'session-test',
|
|
2544
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2874
2545
|
}, null, 2));
|
|
2875
2546
|
await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
|
|
2876
2547
|
last_progress_at: new Date(Date.now() - 5 * 60_000).toISOString(),
|
|
@@ -2916,6 +2587,9 @@ exit 0
|
|
|
2916
2587
|
current_phase: 'executing',
|
|
2917
2588
|
run_outcome: 'blocked_on_user',
|
|
2918
2589
|
tmux_pane_id: '%42',
|
|
2590
|
+
tmux_pane_pid: 4242,
|
|
2591
|
+
tmux_session_name: 'session-test',
|
|
2592
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2919
2593
|
}, null, 2));
|
|
2920
2594
|
await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
|
|
2921
2595
|
last_progress_at: new Date(Date.now() - 5 * 60_000).toISOString(),
|
|
@@ -2954,12 +2628,15 @@ exit 0
|
|
|
2954
2628
|
try {
|
|
2955
2629
|
await mkdir(stateDir, { recursive: true });
|
|
2956
2630
|
await mkdir(fakeBinDir, { recursive: true });
|
|
2957
|
-
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
2631
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
|
|
2958
2632
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
2959
2633
|
await writeFile(join(stateDir, 'ralph-state.json'), JSON.stringify({
|
|
2960
2634
|
active: true,
|
|
2961
2635
|
current_phase: 'executing',
|
|
2962
2636
|
tmux_pane_id: '%42',
|
|
2637
|
+
tmux_pane_pid: 4242,
|
|
2638
|
+
tmux_session_name: 'session-test',
|
|
2639
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
2963
2640
|
}, null, 2));
|
|
2964
2641
|
await writeFile(join(stateDir, 'hud-state.json'), JSON.stringify({
|
|
2965
2642
|
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
@@ -3005,9 +2682,11 @@ exit 0
|
|
|
3005
2682
|
await mkdir(fakeBinDir, { recursive: true });
|
|
3006
2683
|
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, {
|
|
3007
2684
|
failSendKeysMatch: 'Ralph loop active continue',
|
|
2685
|
+
ralphPane: { paneId: '%43', panePid: 4343, paneOwner: 'ralph:owner' },
|
|
3008
2686
|
}));
|
|
3009
2687
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
3010
2688
|
await initTeamState('dispatch-team', 'task', 'executor', 1, wd);
|
|
2689
|
+
await bindCanonicalDispatchWorkerFixture(wd);
|
|
3011
2690
|
const queued = await enqueueDispatchRequest('dispatch-team', {
|
|
3012
2691
|
kind: 'inbox',
|
|
3013
2692
|
to_worker: 'worker-1',
|
|
@@ -3017,7 +2696,10 @@ exit 0
|
|
|
3017
2696
|
await writeFile(join(wd, '.omx', 'state', 'ralph-state.json'), JSON.stringify({
|
|
3018
2697
|
active: true,
|
|
3019
2698
|
current_phase: 'executing',
|
|
3020
|
-
tmux_pane_id: '%
|
|
2699
|
+
tmux_pane_id: '%43',
|
|
2700
|
+
tmux_pane_pid: 4343,
|
|
2701
|
+
tmux_session_name: 'session-test',
|
|
2702
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
3021
2703
|
}, null, 2));
|
|
3022
2704
|
await writeFile(join(wd, '.omx', 'state', 'hud-state.json'), JSON.stringify({
|
|
3023
2705
|
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
@@ -3084,6 +2766,7 @@ exit 0
|
|
|
3084
2766
|
'ready', 'ready', 'ping', 'ping', 'ping', 'ping', 'ping', 'ping',
|
|
3085
2767
|
].join('\n'));
|
|
3086
2768
|
await initTeamState('dispatch-team', 'task', 'executor', 1, wd);
|
|
2769
|
+
await bindCanonicalDispatchWorkerFixture(wd);
|
|
3087
2770
|
const queued = await enqueueDispatchRequest('dispatch-team', {
|
|
3088
2771
|
kind: 'inbox',
|
|
3089
2772
|
to_worker: 'worker-1',
|
|
@@ -3249,6 +2932,9 @@ exit 0
|
|
|
3249
2932
|
active: true,
|
|
3250
2933
|
current_phase: 'executing',
|
|
3251
2934
|
tmux_pane_id: '%42',
|
|
2935
|
+
tmux_pane_pid: 4242,
|
|
2936
|
+
tmux_session_name: 'session-test',
|
|
2937
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
3252
2938
|
}, null, 2));
|
|
3253
2939
|
await writeFile(watcherStatePath, JSON.stringify({
|
|
3254
2940
|
ralph_continue_steer: {
|
|
@@ -3292,6 +2978,9 @@ exit 0
|
|
|
3292
2978
|
active: true,
|
|
3293
2979
|
current_phase: 'executing',
|
|
3294
2980
|
tmux_pane_id: '%42',
|
|
2981
|
+
tmux_pane_pid: 4242,
|
|
2982
|
+
tmux_session_name: 'session-test',
|
|
2983
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
3295
2984
|
}, null, 2));
|
|
3296
2985
|
await writeFile(watcherStatePath, JSON.stringify({
|
|
3297
2986
|
ralph_continue_steer: {
|
|
@@ -3339,6 +3028,9 @@ exit 0
|
|
|
3339
3028
|
active: true,
|
|
3340
3029
|
current_phase: 'executing',
|
|
3341
3030
|
tmux_pane_id: '%42',
|
|
3031
|
+
tmux_pane_pid: 4242,
|
|
3032
|
+
tmux_session_name: 'session-test',
|
|
3033
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
3342
3034
|
}, null, 2));
|
|
3343
3035
|
await writeFile(join(sessionStateDir, 'hud-state.json'), JSON.stringify({
|
|
3344
3036
|
last_progress_at: new Date(Date.now() - 61_000).toISOString(),
|
|
@@ -3348,7 +3040,7 @@ exit 0
|
|
|
3348
3040
|
last_sent_at: new Date(Date.now() - 61_000).toISOString(),
|
|
3349
3041
|
},
|
|
3350
3042
|
}, null, 2));
|
|
3351
|
-
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
3043
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath, { paneOwner: 'ralph:owner' }));
|
|
3352
3044
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
3353
3045
|
const shortLivedParent = spawn(process.execPath, ['-e', 'setTimeout(() => process.exit(0), 10)'], {
|
|
3354
3046
|
stdio: 'ignore',
|
|
@@ -3383,6 +3075,9 @@ exit 0
|
|
|
3383
3075
|
current_phase: 'complete',
|
|
3384
3076
|
completed_at: new Date().toISOString(),
|
|
3385
3077
|
tmux_pane_id: '%42',
|
|
3078
|
+
tmux_pane_pid: 4242,
|
|
3079
|
+
tmux_session_name: 'session-test',
|
|
3080
|
+
tmux_pane_owner_id: 'ralph:owner',
|
|
3386
3081
|
}, null, 2));
|
|
3387
3082
|
await waitForExit(child, 4000);
|
|
3388
3083
|
assert.equal(child.exitCode, 0);
|