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
|
@@ -17,10 +17,18 @@ function isolatedChildEnv(fakeBinDir) {
|
|
|
17
17
|
WINDIR: process.env.WINDIR,
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
+
function liveExactPaneProof() {
|
|
21
|
+
return `if [[ "\${1:-}" == "list-panes" && "$#" -eq 4 && "\${2:-}" == "-a" && "\${3:-}" == "-F" && "\${4:-}" == "#{pane_id}\t#{pane_dead}\t#{pane_pid}" ]]; then
|
|
22
|
+
printf '%%42\\t0\\t4242\\n'
|
|
23
|
+
exit 0
|
|
24
|
+
fi`;
|
|
25
|
+
}
|
|
20
26
|
function buildFakeTmux(tmuxLogPath) {
|
|
21
27
|
const bufferPath = `${tmuxLogPath}.buffer`;
|
|
22
28
|
return `#!/usr/bin/env bash
|
|
23
29
|
set -eu
|
|
30
|
+
${liveExactPaneProof()}
|
|
31
|
+
|
|
24
32
|
printf '[%s]' "$@" >> "${tmuxLogPath}"
|
|
25
33
|
printf '\n' >> "${tmuxLogPath}"
|
|
26
34
|
cmd="$1"
|
|
@@ -49,6 +57,9 @@ function runSendPaneInputInChild(params) {
|
|
|
49
57
|
tmuxBin: join(params.fakeBinDir, 'tmux'),
|
|
50
58
|
typePrompt: params.typePrompt,
|
|
51
59
|
queueFirstSubmit: params.queueFirstSubmit,
|
|
60
|
+
exactPaneId: params.exactPaneId,
|
|
61
|
+
expectedPanePid: params.expectedPanePid,
|
|
62
|
+
expectedPaneOwnerId: params.expectedPaneOwnerId,
|
|
52
63
|
});
|
|
53
64
|
const script = `
|
|
54
65
|
const input = ${payload};
|
|
@@ -96,6 +107,7 @@ describe('notify-hook team tmux guard bridge', () => {
|
|
|
96
107
|
fakeBinDir,
|
|
97
108
|
moduleUrl,
|
|
98
109
|
paneTarget: '%42',
|
|
110
|
+
exactPaneId: '%42',
|
|
99
111
|
prompt: 'hello bridge',
|
|
100
112
|
submitKeyPresses: 2,
|
|
101
113
|
typePrompt: false,
|
|
@@ -107,9 +119,10 @@ describe('notify-hook team tmux guard bridge', () => {
|
|
|
107
119
|
assert.doesNotMatch(log, /paste-buffer/);
|
|
108
120
|
assert.doesNotMatch(log, /hello bridge/);
|
|
109
121
|
const lines = log.trim().split('\n').filter(Boolean);
|
|
110
|
-
assert.equal(lines.length,
|
|
111
|
-
assert.match(lines[0], /\[
|
|
122
|
+
assert.equal(lines.length, 3);
|
|
123
|
+
assert.match(lines[0], /\[display-message\]\[-p\]\[-t\]\[%42\]\[#\{pane_start_command\}\]/);
|
|
112
124
|
assert.match(lines[1], /\[send-keys\]\[-t\]\[%42\]\[C-m\]/);
|
|
125
|
+
assert.match(lines[2], /\[send-keys\]\[-t\]\[%42\]\[C-m\]/);
|
|
113
126
|
}
|
|
114
127
|
finally {
|
|
115
128
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -128,6 +141,7 @@ describe('notify-hook team tmux guard bridge', () => {
|
|
|
128
141
|
fakeBinDir,
|
|
129
142
|
moduleUrl,
|
|
130
143
|
paneTarget: '%42',
|
|
144
|
+
exactPaneId: '%42',
|
|
131
145
|
prompt: 'Read /tmp/team/mailbox/leader-fixed.json; new msg from worker-1. Review it; decide next step.',
|
|
132
146
|
submitKeyPresses: 2,
|
|
133
147
|
typePrompt: true,
|
|
@@ -137,16 +151,17 @@ describe('notify-hook team tmux guard bridge', () => {
|
|
|
137
151
|
assert.equal(result.error, undefined);
|
|
138
152
|
assert.match(result.stdout, /"ok":true/);
|
|
139
153
|
const lines = (await readFile(tmuxLogPath, 'utf-8')).trim().split('\n').filter(Boolean);
|
|
140
|
-
assert.equal(lines.length,
|
|
141
|
-
assert.match(lines[0], /\[
|
|
142
|
-
assert.match(lines[
|
|
143
|
-
assert.match(lines[1], /\[
|
|
144
|
-
assert.match(lines[2], /\[
|
|
145
|
-
assert.match(lines[3], /\[
|
|
146
|
-
assert.match(lines[4], /\[
|
|
147
|
-
assert.match(lines[5], /\[send-keys\]\[-t\]\[%42\]\[
|
|
154
|
+
assert.equal(lines.length, 9);
|
|
155
|
+
assert.match(lines[0], /\[display-message\]\[-p\]\[-t\]\[%42\]\[#\{pane_start_command\}\]/);
|
|
156
|
+
assert.match(lines[1], /\[set-buffer\]\[-b\]\[omx-pane-input-/);
|
|
157
|
+
assert.match(lines[1], /\[--\]\[Read \/tmp\/team\/mailbox\/leader-fixed\.json/);
|
|
158
|
+
assert.match(lines[2], /\[show-buffer\]\[-b\]\[omx-pane-input-/);
|
|
159
|
+
assert.match(lines[3], /\[send-keys\]\[-t\]\[%42\]\[C-u\]/);
|
|
160
|
+
assert.match(lines[4], /\[paste-buffer\]\[-t\]\[%42\]\[-b\]\[omx-pane-input-.*\]\[-p\]\[-d\]/);
|
|
161
|
+
assert.match(lines[5], /\[send-keys\]\[-t\]\[%42\]\[Tab\]/);
|
|
148
162
|
assert.match(lines[6], /\[send-keys\]\[-t\]\[%42\]\[C-m\]/);
|
|
149
|
-
assert.match(lines[7], /\[
|
|
163
|
+
assert.match(lines[7], /\[send-keys\]\[-t\]\[%42\]\[C-m\]/);
|
|
164
|
+
assert.match(lines[8], /\[delete-buffer\]\[-b\]\[omx-pane-input-/);
|
|
150
165
|
}
|
|
151
166
|
finally {
|
|
152
167
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -165,6 +180,7 @@ describe('notify-hook team tmux guard bridge', () => {
|
|
|
165
180
|
fakeBinDir,
|
|
166
181
|
moduleUrl,
|
|
167
182
|
paneTarget: '%42',
|
|
183
|
+
exactPaneId: '%42',
|
|
168
184
|
prompt: 'hello bridge',
|
|
169
185
|
submitKeyPresses: 1,
|
|
170
186
|
typePrompt: true,
|
|
@@ -179,9 +195,10 @@ describe('notify-hook team tmux guard bridge', () => {
|
|
|
179
195
|
assert.match(log, /\[send-keys\]\[-t\]\[%42\]\[C-u\]/);
|
|
180
196
|
assert.match(log, /\[paste-buffer\]\[-t\]\[%42\]\[-b\]\[omx-pane-input-.*\]\[-p\]\[-d\]/);
|
|
181
197
|
const lines = log.trim().split('\n').filter(Boolean);
|
|
182
|
-
assert.equal(lines.length,
|
|
183
|
-
assert.match(lines[
|
|
184
|
-
assert.match(lines[5], /\[
|
|
198
|
+
assert.equal(lines.length, 7);
|
|
199
|
+
assert.match(lines[0], /\[display-message\]\[-p\]\[-t\]\[%42\]\[#\{pane_start_command\}\]/);
|
|
200
|
+
assert.match(lines[5], /\[send-keys\]\[-t\]\[%42\]\[C-m\]/);
|
|
201
|
+
assert.match(lines[6], /\[delete-buffer\]\[-b\]\[omx-pane-input-/);
|
|
185
202
|
}
|
|
186
203
|
finally {
|
|
187
204
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -195,6 +212,8 @@ describe('notify-hook team tmux guard bridge', () => {
|
|
|
195
212
|
await mkdir(fakeBinDir, { recursive: true });
|
|
196
213
|
await writeFile(join(fakeBinDir, 'tmux'), `#!/usr/bin/env bash
|
|
197
214
|
set -eu
|
|
215
|
+
${liveExactPaneProof()}
|
|
216
|
+
|
|
198
217
|
printf '[%s]' "$@" >> "${tmuxLogPath}"
|
|
199
218
|
printf '\n' >> "${tmuxLogPath}"
|
|
200
219
|
cmd="$1"
|
|
@@ -214,6 +233,7 @@ exit 0
|
|
|
214
233
|
fakeBinDir,
|
|
215
234
|
moduleUrl,
|
|
216
235
|
paneTarget: '%42',
|
|
236
|
+
exactPaneId: '%42',
|
|
217
237
|
prompt: 'intended supervisor handoff',
|
|
218
238
|
submitKeyPresses: 1,
|
|
219
239
|
typePrompt: true,
|
|
@@ -242,6 +262,8 @@ exit 0
|
|
|
242
262
|
await mkdir(fakeBinDir, { recursive: true });
|
|
243
263
|
await writeFile(join(fakeBinDir, 'tmux'), `#!/usr/bin/env bash
|
|
244
264
|
set -eu
|
|
265
|
+
${liveExactPaneProof()}
|
|
266
|
+
|
|
245
267
|
printf '[%s]' "$@" >> "${tmuxLogPath}"
|
|
246
268
|
printf '\n' >> "${tmuxLogPath}"
|
|
247
269
|
cmd="$1"
|
|
@@ -261,6 +283,7 @@ exit 0
|
|
|
261
283
|
fakeBinDir,
|
|
262
284
|
moduleUrl,
|
|
263
285
|
paneTarget: '%42',
|
|
286
|
+
exactPaneId: '%42',
|
|
264
287
|
prompt: 'supervisor handoff after setup',
|
|
265
288
|
submitKeyPresses: 1,
|
|
266
289
|
typePrompt: true,
|
|
@@ -270,10 +293,11 @@ exit 0
|
|
|
270
293
|
assert.equal(parsed.ok, false);
|
|
271
294
|
assert.equal(parsed.reason, 'buffer_show_failed');
|
|
272
295
|
const lines = (await readFile(tmuxLogPath, 'utf-8')).trim().split('\n').filter(Boolean);
|
|
273
|
-
assert.match(lines[0] ?? '', /\[
|
|
274
|
-
assert.match(lines[1] ?? '', /\[
|
|
275
|
-
assert.match(lines[2] ?? '', /\[
|
|
276
|
-
assert.
|
|
296
|
+
assert.match(lines[0] ?? '', /\[display-message\]\[-p\]\[-t\]\[%42\]\[#\{pane_start_command\}\]/);
|
|
297
|
+
assert.match(lines[1] ?? '', /\[set-buffer\]\[-b\]\[omx-pane-input-/);
|
|
298
|
+
assert.match(lines[2] ?? '', /\[show-buffer\]\[-b\]\[omx-pane-input-/);
|
|
299
|
+
assert.match(lines[3] ?? '', /\[delete-buffer\]\[-b\]\[omx-pane-input-/);
|
|
300
|
+
assert.equal(lines.length, 4);
|
|
277
301
|
}
|
|
278
302
|
finally {
|
|
279
303
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -288,6 +312,8 @@ exit 0
|
|
|
288
312
|
await mkdir(fakeBinDir, { recursive: true });
|
|
289
313
|
await writeFile(join(fakeBinDir, 'tmux'), `#!/usr/bin/env bash
|
|
290
314
|
set -eu
|
|
315
|
+
${liveExactPaneProof()}
|
|
316
|
+
|
|
291
317
|
printf '[%s]' "$@" >> "${tmuxLogPath}"
|
|
292
318
|
printf '\n' >> "${tmuxLogPath}"
|
|
293
319
|
cmd="$1"
|
|
@@ -315,6 +341,7 @@ exit 0
|
|
|
315
341
|
fakeBinDir,
|
|
316
342
|
moduleUrl,
|
|
317
343
|
paneTarget: '%42',
|
|
344
|
+
exactPaneId: '%42',
|
|
318
345
|
prompt: 'supervisor handoff after verify',
|
|
319
346
|
submitKeyPresses: 1,
|
|
320
347
|
typePrompt: true,
|
|
@@ -324,12 +351,13 @@ exit 0
|
|
|
324
351
|
assert.equal(parsed.ok, false);
|
|
325
352
|
assert.equal(parsed.reason, 'buffer_paste_failed');
|
|
326
353
|
const lines = (await readFile(tmuxLogPath, 'utf-8')).trim().split('\n').filter(Boolean);
|
|
327
|
-
assert.match(lines[0] ?? '', /\[
|
|
328
|
-
assert.match(lines[1] ?? '', /\[
|
|
329
|
-
assert.match(lines[2] ?? '', /\[
|
|
330
|
-
assert.match(lines[3] ?? '', /\[
|
|
331
|
-
assert.match(lines[4] ?? '', /\[
|
|
332
|
-
assert.
|
|
354
|
+
assert.match(lines[0] ?? '', /\[display-message\]\[-p\]\[-t\]\[%42\]\[#\{pane_start_command\}\]/);
|
|
355
|
+
assert.match(lines[1] ?? '', /\[set-buffer\]\[-b\]\[omx-pane-input-/);
|
|
356
|
+
assert.match(lines[2] ?? '', /\[show-buffer\]\[-b\]\[omx-pane-input-/);
|
|
357
|
+
assert.match(lines[3] ?? '', /\[send-keys\]\[-t\]\[%42\]\[C-u\]/);
|
|
358
|
+
assert.match(lines[4] ?? '', /\[paste-buffer\]\[-t\]\[%42\]\[-b\]\[omx-pane-input-.*\]\[-p\]\[-d\]/);
|
|
359
|
+
assert.match(lines[5] ?? '', /\[delete-buffer\]\[-b\]\[omx-pane-input-/);
|
|
360
|
+
assert.equal(lines.length, 6);
|
|
333
361
|
}
|
|
334
362
|
finally {
|
|
335
363
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -343,6 +371,8 @@ exit 0
|
|
|
343
371
|
await mkdir(fakeBinDir, { recursive: true });
|
|
344
372
|
await writeFile(join(fakeBinDir, 'tmux'), `#!/usr/bin/env bash
|
|
345
373
|
set -eu
|
|
374
|
+
${liveExactPaneProof()}
|
|
375
|
+
|
|
346
376
|
echo "$@" >> "${tmuxLogPath}"
|
|
347
377
|
cmd="$1"
|
|
348
378
|
shift || true
|
|
@@ -370,6 +400,7 @@ exit 0
|
|
|
370
400
|
fakeBinDir,
|
|
371
401
|
moduleUrl,
|
|
372
402
|
paneTarget: '%42',
|
|
403
|
+
options: { exactPaneId: '%42' },
|
|
373
404
|
});
|
|
374
405
|
assert.equal(result.status, 0, result.stderr);
|
|
375
406
|
assert.equal(result.error, undefined);
|
|
@@ -383,7 +414,7 @@ exit 0
|
|
|
383
414
|
await rm(cwd, { recursive: true, force: true });
|
|
384
415
|
}
|
|
385
416
|
});
|
|
386
|
-
it('
|
|
417
|
+
it('fails closed without input effects when exact-pane readiness queries fail', async () => {
|
|
387
418
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-team-tmux-guard-'));
|
|
388
419
|
const fakeBinDir = join(cwd, 'fake-bin');
|
|
389
420
|
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
@@ -391,6 +422,7 @@ exit 0
|
|
|
391
422
|
await mkdir(fakeBinDir, { recursive: true });
|
|
392
423
|
await writeFile(join(fakeBinDir, 'tmux'), `#!/usr/bin/env bash
|
|
393
424
|
set -eu
|
|
425
|
+
${liveExactPaneProof()}
|
|
394
426
|
echo "$@" >> "${tmuxLogPath}"
|
|
395
427
|
cmd="$1"
|
|
396
428
|
shift || true
|
|
@@ -414,23 +446,441 @@ exit 0
|
|
|
414
446
|
`);
|
|
415
447
|
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
416
448
|
const moduleUrl = new URL('../../../dist/scripts/notify-hook/team-tmux-guard.js', import.meta.url).href;
|
|
417
|
-
const
|
|
449
|
+
const captureResult = runEvaluatePaneInjectionReadinessInChild({
|
|
450
|
+
fakeBinDir,
|
|
451
|
+
moduleUrl,
|
|
452
|
+
paneTarget: '%42',
|
|
453
|
+
options: { skipIfScrolling: true, exactPaneId: '%42' },
|
|
454
|
+
});
|
|
455
|
+
assert.equal(captureResult.status, 0, captureResult.stderr);
|
|
456
|
+
assert.equal(captureResult.error, undefined);
|
|
457
|
+
const captureParsed = JSON.parse(captureResult.stdout);
|
|
458
|
+
assert.equal(captureParsed.ok, false);
|
|
459
|
+
assert.equal(captureParsed.reason, 'pane_readiness_unverified');
|
|
460
|
+
assert.equal(captureParsed.readinessEvidence, 'capture_failed');
|
|
461
|
+
assert.doesNotMatch(await readFile(tmuxLogPath, 'utf-8'), /set-buffer|paste-buffer|send-keys|delete-buffer/);
|
|
462
|
+
await writeFile(join(fakeBinDir, 'tmux'), `#!/usr/bin/env bash
|
|
463
|
+
set -eu
|
|
464
|
+
${liveExactPaneProof()}
|
|
465
|
+
echo "$@" >> "${tmuxLogPath}"
|
|
466
|
+
if [[ "$1" == "display-message" ]]; then
|
|
467
|
+
echo "pane current command query failed" >&2
|
|
468
|
+
exit 1
|
|
469
|
+
fi
|
|
470
|
+
exit 0
|
|
471
|
+
`);
|
|
472
|
+
const commandResult = runEvaluatePaneInjectionReadinessInChild({
|
|
418
473
|
fakeBinDir,
|
|
419
474
|
moduleUrl,
|
|
420
475
|
paneTarget: '%42',
|
|
421
|
-
options: {
|
|
476
|
+
options: { exactPaneId: '%42' },
|
|
477
|
+
});
|
|
478
|
+
assert.equal(commandResult.status, 0, commandResult.stderr);
|
|
479
|
+
assert.equal(commandResult.error, undefined);
|
|
480
|
+
const commandParsed = JSON.parse(commandResult.stdout);
|
|
481
|
+
assert.equal(commandParsed.ok, false);
|
|
482
|
+
assert.equal(commandParsed.reason, 'pane_readiness_unverified');
|
|
483
|
+
assert.equal(commandParsed.readinessEvidence, 'start_command_failed');
|
|
484
|
+
assert.doesNotMatch(await readFile(tmuxLogPath, 'utf-8'), /set-buffer|paste-buffer|send-keys|delete-buffer/);
|
|
485
|
+
}
|
|
486
|
+
finally {
|
|
487
|
+
await rm(cwd, { recursive: true, force: true });
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
it('rejects an invalid explicit pane identity without any tmux action', async () => {
|
|
491
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-team-tmux-guard-'));
|
|
492
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
493
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
494
|
+
try {
|
|
495
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
496
|
+
await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(tmuxLogPath));
|
|
497
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
498
|
+
const moduleUrl = new URL('../../../dist/scripts/notify-hook/team-tmux-guard.js', import.meta.url).href;
|
|
499
|
+
const result = runSendPaneInputInChild({
|
|
500
|
+
fakeBinDir,
|
|
501
|
+
moduleUrl,
|
|
502
|
+
paneTarget: '%42',
|
|
503
|
+
exactPaneId: 'invalid-pane-id',
|
|
504
|
+
prompt: 'must not be sent',
|
|
505
|
+
submitKeyPresses: 1,
|
|
506
|
+
typePrompt: true,
|
|
422
507
|
});
|
|
423
508
|
assert.equal(result.status, 0, result.stderr);
|
|
424
|
-
assert.equal(result.error, undefined);
|
|
425
509
|
const parsed = JSON.parse(result.stdout);
|
|
426
|
-
assert.equal(parsed.ok,
|
|
427
|
-
assert.equal(parsed.reason, '
|
|
428
|
-
assert.equal(parsed.
|
|
429
|
-
assert.equal(
|
|
510
|
+
assert.equal(parsed.ok, false);
|
|
511
|
+
assert.equal(parsed.reason, 'exact_pane_unavailable');
|
|
512
|
+
assert.equal(parsed.exactPaneProof?.reason, 'invalid_pane_id');
|
|
513
|
+
assert.equal(await readFile(tmuxLogPath, 'utf-8').catch(() => ''), '');
|
|
514
|
+
}
|
|
515
|
+
finally {
|
|
516
|
+
await rm(cwd, { recursive: true, force: true });
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
it('stops before input effects when authoritative exact-pane query fails', async () => {
|
|
520
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-team-tmux-proof-fail-'));
|
|
521
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
522
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
523
|
+
try {
|
|
524
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
525
|
+
await writeFile(join(fakeBinDir, 'tmux'), `#!/usr/bin/env bash\nprintf '[%s]' "$@" >> "${tmuxLogPath}"\nprintf '\\n' >> "${tmuxLogPath}"\nexit 1\n`);
|
|
526
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
527
|
+
const moduleUrl = new URL('../../../dist/scripts/notify-hook/team-tmux-guard.js', import.meta.url).href;
|
|
528
|
+
const result = runSendPaneInputInChild({
|
|
529
|
+
fakeBinDir,
|
|
530
|
+
moduleUrl,
|
|
531
|
+
paneTarget: '%42',
|
|
532
|
+
exactPaneId: '%42',
|
|
533
|
+
prompt: 'must not be sent',
|
|
534
|
+
submitKeyPresses: 2,
|
|
535
|
+
typePrompt: true,
|
|
536
|
+
});
|
|
537
|
+
assert.equal(result.status, 0, result.stderr);
|
|
538
|
+
const parsed = JSON.parse(result.stdout);
|
|
539
|
+
assert.equal(parsed.ok, false);
|
|
540
|
+
assert.equal(parsed.reason, 'exact_pane_unavailable');
|
|
541
|
+
const log = await readFile(tmuxLogPath, 'utf8');
|
|
542
|
+
assert.match(log, /\[list-panes\]\[-a\]\[-F\]/);
|
|
543
|
+
assert.doesNotMatch(log, /set-buffer|paste-buffer|send-keys|delete-buffer/);
|
|
430
544
|
}
|
|
431
545
|
finally {
|
|
432
546
|
await rm(cwd, { recursive: true, force: true });
|
|
433
547
|
}
|
|
434
548
|
});
|
|
435
549
|
});
|
|
550
|
+
it('rejects omitted and mismatched exact pane identities before any tmux effect', async () => {
|
|
551
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-team-tmux-binding-'));
|
|
552
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
553
|
+
try {
|
|
554
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
555
|
+
const moduleUrl = new URL('../../../dist/scripts/notify-hook/team-tmux-guard.js', import.meta.url).href;
|
|
556
|
+
const omitted = runSendPaneInputInChild({
|
|
557
|
+
fakeBinDir,
|
|
558
|
+
moduleUrl,
|
|
559
|
+
paneTarget: '%42',
|
|
560
|
+
prompt: 'must not send',
|
|
561
|
+
submitKeyPresses: 1,
|
|
562
|
+
typePrompt: false,
|
|
563
|
+
});
|
|
564
|
+
assert.equal(omitted.status, 0, omitted.stderr);
|
|
565
|
+
assert.equal(JSON.parse(omitted.stdout).exactPaneProof.reason, 'missing_exact_pane_id');
|
|
566
|
+
const mismatched = runSendPaneInputInChild({
|
|
567
|
+
fakeBinDir,
|
|
568
|
+
moduleUrl,
|
|
569
|
+
paneTarget: '%42',
|
|
570
|
+
exactPaneId: '%43',
|
|
571
|
+
prompt: 'must not send',
|
|
572
|
+
submitKeyPresses: 1,
|
|
573
|
+
typePrompt: false,
|
|
574
|
+
});
|
|
575
|
+
assert.equal(mismatched.status, 0, mismatched.stderr);
|
|
576
|
+
assert.equal(JSON.parse(mismatched.stdout).exactPaneProof.reason, 'pane_target_mismatch');
|
|
577
|
+
}
|
|
578
|
+
finally {
|
|
579
|
+
await rm(cwd, { recursive: true, force: true });
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
it('rejects an exact live pane whose start command identifies the HUD', async () => {
|
|
583
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-team-tmux-hud-target-'));
|
|
584
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
585
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
586
|
+
try {
|
|
587
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
588
|
+
await writeFile(join(fakeBinDir, 'tmux'), `#!/bin/sh
|
|
589
|
+
set -eu
|
|
590
|
+
printf '%s\n' "$*" >> "${tmuxLogPath}"
|
|
591
|
+
if [ "$1" = "list-panes" ]; then printf '%%42\t0\t4242\n'; exit 0; fi
|
|
592
|
+
case "$*" in
|
|
593
|
+
*'#{pane_start_command}'*) echo 'node dist/cli/omx.js hud --watch' ;;
|
|
594
|
+
esac
|
|
595
|
+
`);
|
|
596
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
597
|
+
const moduleUrl = new URL('../../../dist/scripts/notify-hook/team-tmux-guard.js', import.meta.url).href;
|
|
598
|
+
const result = runSendPaneInputInChild({
|
|
599
|
+
fakeBinDir,
|
|
600
|
+
moduleUrl,
|
|
601
|
+
paneTarget: '%42',
|
|
602
|
+
exactPaneId: '%42',
|
|
603
|
+
prompt: 'must not send',
|
|
604
|
+
submitKeyPresses: 1,
|
|
605
|
+
typePrompt: false,
|
|
606
|
+
});
|
|
607
|
+
assert.equal(result.status, 0, result.stderr);
|
|
608
|
+
const parsed = JSON.parse(result.stdout);
|
|
609
|
+
assert.equal(parsed.ok, false);
|
|
610
|
+
assert.equal(parsed.reason, 'hud_pane_target');
|
|
611
|
+
assert.doesNotMatch(await readFile(tmuxLogPath, 'utf8'), /set-buffer|paste-buffer|send-keys/);
|
|
612
|
+
}
|
|
613
|
+
finally {
|
|
614
|
+
await rm(cwd, { recursive: true, force: true });
|
|
615
|
+
}
|
|
616
|
+
});
|
|
617
|
+
it('stops before input when a repeated exact proof observes a new pane PID', async () => {
|
|
618
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-team-tmux-pid-change-'));
|
|
619
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
620
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
621
|
+
const countPath = join(cwd, 'proof-count');
|
|
622
|
+
try {
|
|
623
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
624
|
+
await writeFile(join(fakeBinDir, 'tmux'), `#!/bin/sh
|
|
625
|
+
set -eu
|
|
626
|
+
printf '%s\n' "$*" >> "${tmuxLogPath}"
|
|
627
|
+
if [ "$1" = "list-panes" ]; then
|
|
628
|
+
count=0; [ ! -f "${countPath}" ] || count=$(cat "${countPath}")
|
|
629
|
+
count=$((count + 1)); printf '%s' "$count" > "${countPath}"
|
|
630
|
+
if [ "$count" -eq 1 ]; then printf '%%42\t0\t4242\n'; else printf '%%42\t0\t4343\n'; fi
|
|
631
|
+
fi
|
|
632
|
+
`);
|
|
633
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
634
|
+
const moduleUrl = new URL('../../../dist/scripts/notify-hook/team-tmux-guard.js', import.meta.url).href;
|
|
635
|
+
const result = runSendPaneInputInChild({
|
|
636
|
+
fakeBinDir,
|
|
637
|
+
moduleUrl,
|
|
638
|
+
paneTarget: '%42',
|
|
639
|
+
exactPaneId: '%42',
|
|
640
|
+
prompt: 'must not send',
|
|
641
|
+
submitKeyPresses: 1,
|
|
642
|
+
typePrompt: false,
|
|
643
|
+
});
|
|
644
|
+
assert.equal(result.status, 0, result.stderr);
|
|
645
|
+
const parsed = JSON.parse(result.stdout);
|
|
646
|
+
assert.equal(parsed.ok, false);
|
|
647
|
+
assert.equal(parsed.exactPaneProof.reason, 'pane_pid_changed');
|
|
648
|
+
assert.doesNotMatch(await readFile(tmuxLogPath, 'utf8'), /send-keys/);
|
|
649
|
+
}
|
|
650
|
+
finally {
|
|
651
|
+
await rm(cwd, { recursive: true, force: true });
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
it('rejects a same-PID owner takeover before any Team pane input effect', async () => {
|
|
655
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-team-tmux-owner-change-'));
|
|
656
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
657
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
658
|
+
try {
|
|
659
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
660
|
+
await writeFile(join(fakeBinDir, 'tmux'), `#!/bin/sh
|
|
661
|
+
set -eu
|
|
662
|
+
printf '%s\n' "$*" >> "${tmuxLogPath}"
|
|
663
|
+
if [ "$1" = "list-panes" ]; then printf '%%42\t0\t4242\n'; exit 0; fi
|
|
664
|
+
if [ "$1" = "display-message" ]; then echo codex; exit 0; fi
|
|
665
|
+
if [ "$1" = "show-option" ]; then echo team:foreign; exit 0; fi
|
|
666
|
+
`);
|
|
667
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
668
|
+
const moduleUrl = new URL('../../../dist/scripts/notify-hook/team-tmux-guard.js', import.meta.url).href;
|
|
669
|
+
const result = runSendPaneInputInChild({
|
|
670
|
+
fakeBinDir,
|
|
671
|
+
moduleUrl,
|
|
672
|
+
paneTarget: '%42',
|
|
673
|
+
exactPaneId: '%42',
|
|
674
|
+
expectedPanePid: 4242,
|
|
675
|
+
expectedPaneOwnerId: 'team:alpha',
|
|
676
|
+
prompt: 'must not send',
|
|
677
|
+
submitKeyPresses: 1,
|
|
678
|
+
typePrompt: false,
|
|
679
|
+
});
|
|
680
|
+
assert.equal(result.status, 0, result.stderr);
|
|
681
|
+
const parsed = JSON.parse(result.stdout);
|
|
682
|
+
assert.equal(parsed.ok, false);
|
|
683
|
+
assert.equal(parsed.exactPaneProof.reason, 'pane_owner_changed');
|
|
684
|
+
assert.doesNotMatch(await readFile(tmuxLogPath, 'utf8'), /send-keys|paste-buffer/);
|
|
685
|
+
}
|
|
686
|
+
finally {
|
|
687
|
+
await rm(cwd, { recursive: true, force: true });
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
it('rejects a replacement pane observed after a matching owner read', async () => {
|
|
691
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-team-tmux-owner-replacement-'));
|
|
692
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
693
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
694
|
+
const countPath = join(cwd, 'proof-count');
|
|
695
|
+
try {
|
|
696
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
697
|
+
await writeFile(join(fakeBinDir, 'tmux'), `#!/bin/sh
|
|
698
|
+
set -eu
|
|
699
|
+
printf '%s\n' "$*" >> "${tmuxLogPath}"
|
|
700
|
+
if [ "$1" = "list-panes" ]; then
|
|
701
|
+
count=0; [ ! -f "${countPath}" ] || count=$(cat "${countPath}")
|
|
702
|
+
count=$((count + 1)); printf '%s' "$count" > "${countPath}"
|
|
703
|
+
if [ "$count" -le 3 ]; then printf '%%42\t0\t4242\n'; else printf '%%42\t0\t4343\n'; fi
|
|
704
|
+
exit 0
|
|
705
|
+
fi
|
|
706
|
+
if [ "$1" = "display-message" ]; then echo codex; exit 0; fi
|
|
707
|
+
if [ "$1" = "show-option" ]; then echo team:alpha; exit 0; fi
|
|
708
|
+
`);
|
|
709
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
710
|
+
const moduleUrl = new URL('../../../dist/scripts/notify-hook/team-tmux-guard.js', import.meta.url).href;
|
|
711
|
+
const result = runSendPaneInputInChild({
|
|
712
|
+
fakeBinDir,
|
|
713
|
+
moduleUrl,
|
|
714
|
+
paneTarget: '%42',
|
|
715
|
+
exactPaneId: '%42',
|
|
716
|
+
expectedPanePid: 4242,
|
|
717
|
+
expectedPaneOwnerId: 'team:alpha',
|
|
718
|
+
prompt: 'must not send',
|
|
719
|
+
submitKeyPresses: 1,
|
|
720
|
+
typePrompt: false,
|
|
721
|
+
});
|
|
722
|
+
assert.equal(result.status, 0, result.stderr);
|
|
723
|
+
const parsed = JSON.parse(result.stdout);
|
|
724
|
+
assert.equal(parsed.ok, false);
|
|
725
|
+
assert.equal(parsed.exactPaneProof.reason, 'pane_pid_changed');
|
|
726
|
+
assert.doesNotMatch(await readFile(tmuxLogPath, 'utf8'), /send-keys|paste-buffer/);
|
|
727
|
+
}
|
|
728
|
+
finally {
|
|
729
|
+
await rm(cwd, { recursive: true, force: true });
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
it('rejects a foreign-owner Team pane before inspecting readiness content', async () => {
|
|
733
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-team-readiness-owner-change-'));
|
|
734
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
735
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
736
|
+
try {
|
|
737
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
738
|
+
await writeFile(join(fakeBinDir, 'tmux'), `#!/bin/sh
|
|
739
|
+
set -eu
|
|
740
|
+
printf '%s\n' "$*" >> "${tmuxLogPath}"
|
|
741
|
+
if [ "$1" = "list-panes" ]; then printf '%%42\t0\t4242\n'; exit 0; fi
|
|
742
|
+
if [ "$1" = "show-option" ]; then echo team:foreign; exit 0; fi
|
|
743
|
+
if [ "$1" = "display-message" ]; then echo foreign-command; exit 0; fi
|
|
744
|
+
if [ "$1" = "capture-pane" ]; then echo foreign-content; exit 0; fi
|
|
745
|
+
`);
|
|
746
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
747
|
+
const moduleUrl = new URL('../../../dist/scripts/notify-hook/team-tmux-guard.js', import.meta.url).href;
|
|
748
|
+
const result = runEvaluatePaneInjectionReadinessInChild({
|
|
749
|
+
fakeBinDir,
|
|
750
|
+
moduleUrl,
|
|
751
|
+
paneTarget: '%42',
|
|
752
|
+
options: {
|
|
753
|
+
exactPaneId: '%42',
|
|
754
|
+
expectedPanePid: 4242,
|
|
755
|
+
expectedPaneOwnerId: 'team:alpha',
|
|
756
|
+
expectedHudPaneId: '%99',
|
|
757
|
+
},
|
|
758
|
+
});
|
|
759
|
+
assert.equal(result.status, 0, result.stderr);
|
|
760
|
+
const parsed = JSON.parse(result.stdout);
|
|
761
|
+
assert.equal(parsed.ok, false);
|
|
762
|
+
assert.equal(parsed.exactPaneProof.reason, 'pane_owner_changed');
|
|
763
|
+
assert.equal(parsed.paneCapture, '');
|
|
764
|
+
assert.doesNotMatch(await readFile(tmuxLogPath, 'utf8'), /display-message|capture-pane/);
|
|
765
|
+
}
|
|
766
|
+
finally {
|
|
767
|
+
await rm(cwd, { recursive: true, force: true });
|
|
768
|
+
}
|
|
769
|
+
});
|
|
770
|
+
it('rejects a replacement Team pane after the owner proof and before readiness reads', async () => {
|
|
771
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-team-readiness-owner-replacement-'));
|
|
772
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
773
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
774
|
+
const countPath = join(cwd, 'proof-count');
|
|
775
|
+
try {
|
|
776
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
777
|
+
await writeFile(join(fakeBinDir, 'tmux'), `#!/bin/sh
|
|
778
|
+
set -eu
|
|
779
|
+
printf '%s\n' "$*" >> "${tmuxLogPath}"
|
|
780
|
+
if [ "$1" = "list-panes" ]; then
|
|
781
|
+
count=0; [ ! -f "${countPath}" ] || count=$(cat "${countPath}")
|
|
782
|
+
count=$((count + 1)); printf '%s' "$count" > "${countPath}"
|
|
783
|
+
if [ "$count" -eq 1 ]; then printf '%%42\t0\t4242\n'; else printf '%%42\t0\t4343\n'; fi
|
|
784
|
+
exit 0
|
|
785
|
+
fi
|
|
786
|
+
if [ "$1" = "show-option" ]; then echo team:alpha; exit 0; fi
|
|
787
|
+
if [ "$1" = "display-message" ]; then echo foreign-command; exit 0; fi
|
|
788
|
+
if [ "$1" = "capture-pane" ]; then echo foreign-content; exit 0; fi
|
|
789
|
+
`);
|
|
790
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
791
|
+
const moduleUrl = new URL('../../../dist/scripts/notify-hook/team-tmux-guard.js', import.meta.url).href;
|
|
792
|
+
const result = runEvaluatePaneInjectionReadinessInChild({
|
|
793
|
+
fakeBinDir,
|
|
794
|
+
moduleUrl,
|
|
795
|
+
paneTarget: '%42',
|
|
796
|
+
options: {
|
|
797
|
+
exactPaneId: '%42',
|
|
798
|
+
expectedPanePid: 4242,
|
|
799
|
+
expectedPaneOwnerId: 'team:alpha',
|
|
800
|
+
expectedHudPaneId: '%99',
|
|
801
|
+
},
|
|
802
|
+
});
|
|
803
|
+
assert.equal(result.status, 0, result.stderr);
|
|
804
|
+
const parsed = JSON.parse(result.stdout);
|
|
805
|
+
assert.equal(parsed.ok, false);
|
|
806
|
+
assert.equal(parsed.exactPaneProof.reason, 'pane_pid_changed');
|
|
807
|
+
assert.equal(parsed.paneCapture, '');
|
|
808
|
+
assert.doesNotMatch(await readFile(tmuxLogPath, 'utf8'), /display-message|capture-pane/);
|
|
809
|
+
}
|
|
810
|
+
finally {
|
|
811
|
+
await rm(cwd, { recursive: true, force: true });
|
|
812
|
+
}
|
|
813
|
+
});
|
|
814
|
+
it('rejects the configured Team HUD without reading its command or capture', async () => {
|
|
815
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-team-readiness-hud-'));
|
|
816
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
817
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
818
|
+
try {
|
|
819
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
820
|
+
await writeFile(join(fakeBinDir, 'tmux'), `#!/bin/sh
|
|
821
|
+
set -eu
|
|
822
|
+
printf '%s\n' "$*" >> "${tmuxLogPath}"
|
|
823
|
+
echo foreign-content
|
|
824
|
+
`);
|
|
825
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
826
|
+
const moduleUrl = new URL('../../../dist/scripts/notify-hook/team-tmux-guard.js', import.meta.url).href;
|
|
827
|
+
const result = runEvaluatePaneInjectionReadinessInChild({
|
|
828
|
+
fakeBinDir,
|
|
829
|
+
moduleUrl,
|
|
830
|
+
paneTarget: '%42',
|
|
831
|
+
options: {
|
|
832
|
+
exactPaneId: '%42',
|
|
833
|
+
expectedPanePid: 4242,
|
|
834
|
+
expectedPaneOwnerId: 'team:alpha',
|
|
835
|
+
expectedHudPaneId: '%42',
|
|
836
|
+
},
|
|
837
|
+
});
|
|
838
|
+
assert.equal(result.status, 0, result.stderr);
|
|
839
|
+
const parsed = JSON.parse(result.stdout);
|
|
840
|
+
assert.equal(parsed.ok, false);
|
|
841
|
+
assert.equal(parsed.reason, 'hud_pane_target');
|
|
842
|
+
assert.equal(parsed.paneCapture, '');
|
|
843
|
+
await assert.rejects(readFile(tmuxLogPath, 'utf8'));
|
|
844
|
+
}
|
|
845
|
+
finally {
|
|
846
|
+
await rm(cwd, { recursive: true, force: true });
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
it('authorizes Team ownership before reading the start command', async () => {
|
|
850
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-team-tmux-start-command-owner-'));
|
|
851
|
+
const fakeBinDir = join(cwd, 'fake-bin');
|
|
852
|
+
const tmuxLogPath = join(cwd, 'tmux.log');
|
|
853
|
+
try {
|
|
854
|
+
await mkdir(fakeBinDir, { recursive: true });
|
|
855
|
+
await writeFile(join(fakeBinDir, 'tmux'), `#!/bin/sh
|
|
856
|
+
set -eu
|
|
857
|
+
printf '%s\n' "$*" >> "${tmuxLogPath}"
|
|
858
|
+
if [ "$1" = "list-panes" ]; then printf '%%42\t0\t4242\n'; exit 0; fi
|
|
859
|
+
if [ "$1" = "show-option" ]; then echo team:foreign; exit 0; fi
|
|
860
|
+
if [ "$1" = "display-message" ]; then echo foreign-command; exit 0; fi
|
|
861
|
+
`);
|
|
862
|
+
await chmod(join(fakeBinDir, 'tmux'), 0o755);
|
|
863
|
+
const moduleUrl = new URL('../../../dist/scripts/notify-hook/team-tmux-guard.js', import.meta.url).href;
|
|
864
|
+
const result = runSendPaneInputInChild({
|
|
865
|
+
fakeBinDir,
|
|
866
|
+
moduleUrl,
|
|
867
|
+
paneTarget: '%42',
|
|
868
|
+
exactPaneId: '%42',
|
|
869
|
+
expectedPanePid: 4242,
|
|
870
|
+
expectedPaneOwnerId: 'team:alpha',
|
|
871
|
+
prompt: 'must not inspect or send',
|
|
872
|
+
submitKeyPresses: 1,
|
|
873
|
+
typePrompt: false,
|
|
874
|
+
});
|
|
875
|
+
assert.equal(result.status, 0, result.stderr);
|
|
876
|
+
const parsed = JSON.parse(result.stdout);
|
|
877
|
+
assert.equal(parsed.ok, false);
|
|
878
|
+
assert.equal(parsed.exactPaneProof.reason, 'pane_owner_changed');
|
|
879
|
+
const log = await readFile(tmuxLogPath, 'utf8');
|
|
880
|
+
assert.doesNotMatch(log, /display-message|send-keys|paste-buffer/);
|
|
881
|
+
}
|
|
882
|
+
finally {
|
|
883
|
+
await rm(cwd, { recursive: true, force: true });
|
|
884
|
+
}
|
|
885
|
+
});
|
|
436
886
|
//# sourceMappingURL=notify-hook-team-tmux-guard.test.js.map
|