oh-my-codex 0.20.3 → 0.20.5
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 +35 -18
- package/Cargo.toml +1 -1
- package/README.md +2 -2
- package/crates/omx-runtime/Cargo.toml +3 -0
- package/crates/omx-runtime/src/main.rs +390 -0
- package/crates/omx-runtime/tests/execution.rs +264 -0
- package/dist/adapt/__tests__/herdr-target.test.d.ts +2 -0
- package/dist/adapt/__tests__/herdr-target.test.d.ts.map +1 -0
- package/dist/adapt/__tests__/herdr-target.test.js +72 -0
- package/dist/adapt/__tests__/herdr-target.test.js.map +1 -0
- package/dist/adapt/contracts.d.ts +1 -1
- package/dist/adapt/contracts.d.ts.map +1 -1
- package/dist/adapt/contracts.js +1 -1
- package/dist/adapt/contracts.js.map +1 -1
- package/dist/adapt/herdr/__tests__/authority.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/authority.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/authority.test.js +49 -0
- package/dist/adapt/herdr/__tests__/authority.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/bridge.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/bridge.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/bridge.test.js +176 -0
- package/dist/adapt/herdr/__tests__/bridge.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/sanitize.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/sanitize.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/sanitize.test.js +45 -0
- package/dist/adapt/herdr/__tests__/sanitize.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/semantic.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/semantic.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/semantic.test.js +130 -0
- package/dist/adapt/herdr/__tests__/semantic.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/seq-store.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/seq-store.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/seq-store.test.js +54 -0
- package/dist/adapt/herdr/__tests__/seq-store.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/socket-live.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/socket-live.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/socket-live.test.js +104 -0
- package/dist/adapt/herdr/__tests__/socket-live.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/team-map.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/team-map.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/team-map.test.js +46 -0
- package/dist/adapt/herdr/__tests__/team-map.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/transport.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/transport.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/transport.test.js +160 -0
- package/dist/adapt/herdr/__tests__/transport.test.js.map +1 -0
- package/dist/adapt/herdr/__tests__/wiring.test.d.ts +2 -0
- package/dist/adapt/herdr/__tests__/wiring.test.d.ts.map +1 -0
- package/dist/adapt/herdr/__tests__/wiring.test.js +118 -0
- package/dist/adapt/herdr/__tests__/wiring.test.js.map +1 -0
- package/dist/adapt/herdr/authority.d.ts +34 -0
- package/dist/adapt/herdr/authority.d.ts.map +1 -0
- package/dist/adapt/herdr/authority.js +71 -0
- package/dist/adapt/herdr/authority.js.map +1 -0
- package/dist/adapt/herdr/bridge.d.ts +84 -0
- package/dist/adapt/herdr/bridge.d.ts.map +1 -0
- package/dist/adapt/herdr/bridge.js +222 -0
- package/dist/adapt/herdr/bridge.js.map +1 -0
- package/dist/adapt/herdr/sanitize.d.ts +20 -0
- package/dist/adapt/herdr/sanitize.d.ts.map +1 -0
- package/dist/adapt/herdr/sanitize.js +70 -0
- package/dist/adapt/herdr/sanitize.js.map +1 -0
- package/dist/adapt/herdr/semantic.d.ts +67 -0
- package/dist/adapt/herdr/semantic.d.ts.map +1 -0
- package/dist/adapt/herdr/semantic.js +170 -0
- package/dist/adapt/herdr/semantic.js.map +1 -0
- package/dist/adapt/herdr/seq-store.d.ts +32 -0
- package/dist/adapt/herdr/seq-store.d.ts.map +1 -0
- package/dist/adapt/herdr/seq-store.js +126 -0
- package/dist/adapt/herdr/seq-store.js.map +1 -0
- package/dist/adapt/herdr/team-map.d.ts +30 -0
- package/dist/adapt/herdr/team-map.d.ts.map +1 -0
- package/dist/adapt/herdr/team-map.js +37 -0
- package/dist/adapt/herdr/team-map.js.map +1 -0
- package/dist/adapt/herdr/transport.d.ts +115 -0
- package/dist/adapt/herdr/transport.d.ts.map +1 -0
- package/dist/adapt/herdr/transport.js +260 -0
- package/dist/adapt/herdr/transport.js.map +1 -0
- package/dist/adapt/herdr/wiring.d.ts +24 -0
- package/dist/adapt/herdr/wiring.d.ts.map +1 -0
- package/dist/adapt/herdr/wiring.js +62 -0
- package/dist/adapt/herdr/wiring.js.map +1 -0
- package/dist/adapt/herdr.d.ts +19 -0
- package/dist/adapt/herdr.d.ts.map +1 -0
- package/dist/adapt/herdr.js +115 -0
- package/dist/adapt/herdr.js.map +1 -0
- package/dist/adapt/index.d.ts +4 -0
- package/dist/adapt/index.d.ts.map +1 -1
- package/dist/adapt/index.js +48 -0
- package/dist/adapt/index.js.map +1 -1
- package/dist/adapt/registry.d.ts.map +1 -1
- package/dist/adapt/registry.js +11 -0
- package/dist/adapt/registry.js.map +1 -1
- package/dist/auth/__tests__/hotswap.test.js +60 -1
- package/dist/auth/__tests__/hotswap.test.js.map +1 -1
- package/dist/auth/__tests__/storage.test.js +33 -1
- package/dist/auth/__tests__/storage.test.js.map +1 -1
- package/dist/auth/hotswap.d.ts +16 -2
- package/dist/auth/hotswap.d.ts.map +1 -1
- package/dist/auth/hotswap.js +22 -7
- package/dist/auth/hotswap.js.map +1 -1
- package/dist/auth/storage.js +1 -1
- package/dist/auth/storage.js.map +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js +86 -1333
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
- package/dist/autopilot/ralplan-gate.js +7 -6
- package/dist/autopilot/ralplan-gate.js.map +1 -1
- package/dist/cli/__tests__/agents-init.test.js +4 -23
- package/dist/cli/__tests__/agents-init.test.js.map +1 -1
- package/dist/cli/__tests__/api.test.js +61 -24
- package/dist/cli/__tests__/api.test.js.map +1 -1
- package/dist/cli/__tests__/auth.test.js +3 -1
- package/dist/cli/__tests__/auth.test.js.map +1 -1
- package/dist/cli/__tests__/codex-feature-probe.test.js +66 -1
- package/dist/cli/__tests__/codex-feature-probe.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +70 -2
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/detached-leader-teardown.test.d.ts +2 -0
- package/dist/cli/__tests__/detached-leader-teardown.test.d.ts.map +1 -0
- package/dist/cli/__tests__/detached-leader-teardown.test.js +473 -0
- package/dist/cli/__tests__/detached-leader-teardown.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-process-identity-readiness.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-process-identity-readiness.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-process-identity-readiness.test.js +103 -0
- package/dist/cli/__tests__/doctor-process-identity-readiness.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-state-root-binding.test.d.ts +2 -0
- package/dist/cli/__tests__/doctor-state-root-binding.test.d.ts.map +1 -0
- package/dist/cli/__tests__/doctor-state-root-binding.test.js +139 -0
- package/dist/cli/__tests__/doctor-state-root-binding.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js +35 -3
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/exec.test.js +75 -0
- package/dist/cli/__tests__/exec.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +621 -460
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +1020 -143
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +4 -3
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/native-assets.test.js +398 -16
- package/dist/cli/__tests__/native-assets.test.js.map +1 -1
- package/dist/cli/__tests__/native-hook-claim-journal.test.js +53 -5
- package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +2 -2
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +57 -57
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -1
- package/dist/cli/__tests__/ralplan-child-binding-3212.test.d.ts +2 -0
- package/dist/cli/__tests__/ralplan-child-binding-3212.test.d.ts.map +1 -0
- package/dist/cli/__tests__/ralplan-child-binding-3212.test.js +56 -0
- package/dist/cli/__tests__/ralplan-child-binding-3212.test.js.map +1 -0
- package/dist/cli/__tests__/ralplan.test.js +84 -12
- package/dist/cli/__tests__/ralplan.test.js.map +1 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.js +1118 -43
- package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +8 -55
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +183 -4
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js +93 -41
- package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +45 -1
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +117 -2
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +50 -5
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +669 -177
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +32 -2
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
- package/dist/cli/api.d.ts.map +1 -1
- package/dist/cli/api.js +9 -10
- package/dist/cli/api.js.map +1 -1
- package/dist/cli/codex-feature-probe.d.ts +16 -0
- package/dist/cli/codex-feature-probe.d.ts.map +1 -1
- package/dist/cli/codex-feature-probe.js +44 -0
- package/dist/cli/codex-feature-probe.js.map +1 -1
- package/dist/cli/codex-home.d.ts.map +1 -1
- package/dist/cli/codex-home.js +7 -4
- package/dist/cli/codex-home.js.map +1 -1
- package/dist/cli/doctor.d.ts +17 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +277 -3
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +231 -12
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2298 -735
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/native-assets.d.ts +25 -0
- package/dist/cli/native-assets.d.ts.map +1 -1
- package/dist/cli/native-assets.js +448 -51
- package/dist/cli/native-assets.js.map +1 -1
- package/dist/cli/native-hook-claim-journal.d.ts +8 -4
- package/dist/cli/native-hook-claim-journal.d.ts.map +1 -1
- package/dist/cli/native-hook-claim-journal.js +30 -8
- package/dist/cli/native-hook-claim-journal.js.map +1 -1
- package/dist/cli/package-manager-ownership.d.ts +63 -0
- package/dist/cli/package-manager-ownership.d.ts.map +1 -0
- package/dist/cli/package-manager-ownership.js +323 -0
- package/dist/cli/package-manager-ownership.js.map +1 -0
- package/dist/cli/ralph.js +2 -2
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/ralplan.d.ts +4 -2
- package/dist/cli/ralplan.d.ts.map +1 -1
- package/dist/cli/ralplan.js +76 -34
- package/dist/cli/ralplan.js.map +1 -1
- package/dist/cli/setup-preferences.d.ts +15 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -1
- package/dist/cli/setup-preferences.js +27 -1
- package/dist/cli/setup-preferences.js.map +1 -1
- package/dist/cli/setup.d.ts +3 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +55 -32
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/sparkshell.d.ts.map +1 -1
- package/dist/cli/sparkshell.js +52 -9
- package/dist/cli/sparkshell.js.map +1 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +2 -2
- package/dist/cli/ultragoal.js.map +1 -1
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +27 -28
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/cli/update-worker.d.ts +2 -0
- package/dist/cli/update-worker.d.ts.map +1 -0
- package/dist/cli/update-worker.js +142 -0
- package/dist/cli/update-worker.js.map +1 -0
- package/dist/cli/update.d.ts +14 -22
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +192 -241
- package/dist/cli/update.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +173 -29
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +3 -1
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/generator.d.ts +25 -3
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +113 -35
- package/dist/config/generator.js.map +1 -1
- package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +25 -0
- package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.d.ts +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.js +2 -0
- package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +3 -1
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +27 -4
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.d.ts +2 -0
- package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.js +172 -0
- package/dist/hooks/__tests__/issue-3293-autopilot-handoff.test.js.map +1 -0
- package/dist/hooks/__tests__/keyword-detector.test.js +172 -33
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +173 -11
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +1 -0
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +65 -17
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +27 -0
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js +10 -118
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js +7 -0
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +2 -2
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +4 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js +2 -1
- package/dist/hooks/__tests__/notify-hook-team-worker-fail-closed.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +1 -0
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js +9 -0
- package/dist/hooks/__tests__/prompt-guidance-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/session.test.js +2976 -557
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/agents-overlay.js +1 -1
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js +108 -2
- package/dist/hooks/extensibility/__tests__/dispatcher.test.js.map +1 -1
- package/dist/hooks/extensibility/__tests__/sdk.test.js +151 -1
- package/dist/hooks/extensibility/__tests__/sdk.test.js.map +1 -1
- package/dist/hooks/extensibility/dispatcher.d.ts.map +1 -1
- package/dist/hooks/extensibility/dispatcher.js +20 -3
- package/dist/hooks/extensibility/dispatcher.js.map +1 -1
- package/dist/hooks/extensibility/plugin-runner.js +1 -0
- package/dist/hooks/extensibility/plugin-runner.js.map +1 -1
- package/dist/hooks/extensibility/runtime.d.ts.map +1 -1
- package/dist/hooks/extensibility/runtime.js.map +1 -1
- package/dist/hooks/extensibility/sdk/paths.d.ts +1 -1
- package/dist/hooks/extensibility/sdk/paths.d.ts.map +1 -1
- package/dist/hooks/extensibility/sdk/paths.js +2 -2
- package/dist/hooks/extensibility/sdk/paths.js.map +1 -1
- package/dist/hooks/extensibility/sdk/runtime-state.d.ts +1 -1
- package/dist/hooks/extensibility/sdk/runtime-state.d.ts.map +1 -1
- package/dist/hooks/extensibility/sdk/runtime-state.js +55 -15
- package/dist/hooks/extensibility/sdk/runtime-state.js.map +1 -1
- package/dist/hooks/extensibility/sdk.d.ts +1 -0
- package/dist/hooks/extensibility/sdk.d.ts.map +1 -1
- package/dist/hooks/extensibility/sdk.js +1 -1
- package/dist/hooks/extensibility/sdk.js.map +1 -1
- package/dist/hooks/extensibility/types.d.ts +7 -0
- package/dist/hooks/extensibility/types.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +10 -3
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +116 -20
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/session.d.ts +180 -14
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +1402 -281
- package/dist/hooks/session.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +6 -6
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/index.test.js +26 -16
- package/dist/hud/__tests__/index.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +83 -78
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +36 -31
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +155 -198
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/tmux.d.ts +42 -4
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +1017 -139
- package/dist/hud/tmux.js.map +1 -1
- package/dist/leader/__tests__/contract.test.js +71 -34
- package/dist/leader/__tests__/contract.test.js.map +1 -1
- package/dist/leader/contract.d.ts +1 -6
- package/dist/leader/contract.d.ts.map +1 -1
- package/dist/leader/contract.js +25 -24
- package/dist/leader/contract.js.map +1 -1
- package/dist/mcp/__tests__/hermes-bridge.test.js +17 -0
- package/dist/mcp/__tests__/hermes-bridge.test.js.map +1 -1
- package/dist/mcp/__tests__/server-lifecycle.test.js +4 -4
- package/dist/mcp/__tests__/state-paths.test.js +735 -4
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/__tests__/state-server.test.js +1 -1
- package/dist/mcp/__tests__/state-server.test.js.map +1 -1
- package/dist/mcp/__tests__/trace-server.test.js +2 -2
- package/dist/mcp/__tests__/trace-server.test.js.map +1 -1
- package/dist/mcp/hermes-bridge.d.ts.map +1 -1
- package/dist/mcp/hermes-bridge.js +2 -2
- package/dist/mcp/hermes-bridge.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +78 -4
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +543 -40
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts +4 -4
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/modes/__tests__/base-autopilot-gates.test.js +36 -7
- package/dist/modes/__tests__/base-autopilot-gates.test.js.map +1 -1
- package/dist/modes/__tests__/base-multi-state-compat.test.js +1 -1
- package/dist/modes/__tests__/base-multi-state-compat.test.js.map +1 -1
- package/dist/modes/__tests__/base-session-scope.test.js +17 -8
- package/dist/modes/__tests__/base-session-scope.test.js.map +1 -1
- package/dist/modes/base.d.ts +5 -5
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/modes/base.js +76 -21
- package/dist/modes/base.js.map +1 -1
- package/dist/native-assets/archive.d.ts +17 -0
- package/dist/native-assets/archive.d.ts.map +1 -0
- package/dist/native-assets/archive.js +238 -0
- package/dist/native-assets/archive.js.map +1 -0
- package/dist/native-assets/policy.d.ts +42 -0
- package/dist/native-assets/policy.d.ts.map +1 -0
- package/dist/native-assets/policy.js +155 -0
- package/dist/native-assets/policy.js.map +1 -0
- package/dist/pipeline/__tests__/orchestrator.test.js +162 -40
- package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +66 -149
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +124 -18
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
- package/dist/pipeline/stages/ralplan.js +36 -8
- package/dist/pipeline/stages/ralplan.js.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js +70 -1437
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js +328 -39
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -1
- package/dist/ralplan/__tests__/runtime.test.js +82 -102
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts +9 -1
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +40 -304
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/ralplan/documented-leader-preflight.d.ts +15 -8
- package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -1
- package/dist/ralplan/documented-leader-preflight.js +356 -54
- package/dist/ralplan/documented-leader-preflight.js.map +1 -1
- package/dist/ralplan/runtime.d.ts +1 -1
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +68 -173
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/runtime/run-state.d.ts +5 -1
- package/dist/runtime/run-state.d.ts.map +1 -1
- package/dist/runtime/run-state.js +15 -8
- package/dist/runtime/run-state.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +14071 -8859
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/issue-3293-callsite-parity.test.d.ts +2 -0
- package/dist/scripts/__tests__/issue-3293-callsite-parity.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/issue-3293-callsite-parity.test.js +225 -0
- package/dist/scripts/__tests__/issue-3293-callsite-parity.test.js.map +1 -0
- package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.d.ts +2 -0
- package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.js +188 -0
- package/dist/scripts/__tests__/issue-3293-cancel-transaction.test.js.map +1 -0
- package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.d.ts +2 -0
- package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.js +259 -0
- package/dist/scripts/__tests__/issue-3293-hook-owned-cancel.test.js.map +1 -0
- package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.d.ts +2 -0
- package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.js +491 -0
- package/dist/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.js.map +1 -0
- package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.d.ts +2 -0
- package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.js +708 -0
- package/dist/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.js.map +1 -0
- package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.d.ts +2 -0
- package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.js +208 -0
- package/dist/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.js.map +1 -0
- package/dist/scripts/__tests__/notify-state-io.test.js +26 -0
- package/dist/scripts/__tests__/notify-state-io.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-tmux-injection.test.js +23 -1
- package/dist/scripts/__tests__/notify-tmux-injection.test.js.map +1 -1
- package/dist/scripts/__tests__/postinstall.test.js +19 -1
- package/dist/scripts/__tests__/postinstall.test.js.map +1 -1
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +25 -8
- 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 +60 -25
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js +155 -16
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
- package/dist/scripts/__tests__/verify-native-release-assets.test.d.ts +2 -0
- package/dist/scripts/__tests__/verify-native-release-assets.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/verify-native-release-assets.test.js +235 -0
- package/dist/scripts/__tests__/verify-native-release-assets.test.js.map +1 -0
- package/dist/scripts/codex-native-hook.d.ts +50 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +3559 -451
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/codex-native-pre-post.d.ts +9 -0
- package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
- package/dist/scripts/codex-native-pre-post.js +35 -12
- package/dist/scripts/codex-native-pre-post.js.map +1 -1
- package/dist/scripts/generate-native-release-manifest.js +80 -51
- package/dist/scripts/generate-native-release-manifest.js.map +1 -1
- package/dist/scripts/notify-fallback-watcher.js +28 -39
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +5 -0
- package/dist/scripts/notify-hook/state-io.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 +32 -3
- package/dist/scripts/notify-hook/team-leader-nudge.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 +23 -3
- package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +45 -5
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts +1 -0
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +4 -1
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +78 -82
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/postinstall-advisory.d.ts +14 -0
- package/dist/scripts/postinstall-advisory.d.ts.map +1 -0
- package/dist/scripts/postinstall-advisory.js +49 -0
- package/dist/scripts/postinstall-advisory.js.map +1 -0
- package/dist/scripts/postinstall.d.ts +1 -1
- package/dist/scripts/postinstall.d.ts.map +1 -1
- package/dist/scripts/postinstall.js +1 -1
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +32 -1
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +413 -60
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/scripts/sync-plugin-mirror.d.ts.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js +16 -1
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/scripts/verify-native-release-assets.js +37 -23
- package/dist/scripts/verify-native-release-assets.js.map +1 -1
- package/dist/state/__tests__/mode-state-context.test.js +12 -5
- package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +460 -850
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +400 -2
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/mode-state-context.d.ts +3 -0
- package/dist/state/mode-state-context.d.ts.map +1 -1
- package/dist/state/mode-state-context.js +27 -13
- package/dist/state/mode-state-context.js.map +1 -1
- package/dist/state/operations.d.ts +3 -0
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +144 -40
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.d.ts +18 -1
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +420 -23
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts +3 -0
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +10 -4
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/state/workflow-transition.d.ts.map +1 -1
- package/dist/state/workflow-transition.js +3 -1
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +27 -0
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -1
- package/dist/subagents/__tests__/tracker.test.js +837 -1048
- package/dist/subagents/__tests__/tracker.test.js.map +1 -1
- package/dist/subagents/tracker.d.ts +64 -60
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +809 -358
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +6 -7
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
- package/dist/team/__tests__/mcp-comm.test.js +104 -3
- package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
- package/dist/team/__tests__/notice-ledger.test.d.ts +2 -0
- package/dist/team/__tests__/notice-ledger.test.d.ts.map +1 -0
- package/dist/team/__tests__/notice-ledger.test.js +131 -0
- package/dist/team/__tests__/notice-ledger.test.js.map +1 -0
- package/dist/team/__tests__/pane-teardown-residue-e2e.test.d.ts +2 -0
- package/dist/team/__tests__/pane-teardown-residue-e2e.test.d.ts.map +1 -0
- package/dist/team/__tests__/pane-teardown-residue-e2e.test.js +410 -0
- package/dist/team/__tests__/pane-teardown-residue-e2e.test.js.map +1 -0
- package/dist/team/__tests__/phase-controller.test.js +20 -0
- package/dist/team/__tests__/phase-controller.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +725 -59
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +222 -237
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/state-root.test.js +117 -2
- package/dist/team/__tests__/state-root.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +887 -92
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/tmux-source-authority-realtmux.test.d.ts +2 -0
- package/dist/team/__tests__/tmux-source-authority-realtmux.test.d.ts.map +1 -0
- package/dist/team/__tests__/tmux-source-authority-realtmux.test.js +149 -0
- package/dist/team/__tests__/tmux-source-authority-realtmux.test.js.map +1 -0
- package/dist/team/__tests__/tmux-test-fixture.d.ts +69 -0
- package/dist/team/__tests__/tmux-test-fixture.d.ts.map +1 -1
- package/dist/team/__tests__/tmux-test-fixture.js +242 -15
- package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
- package/dist/team/__tests__/tmux-test-fixture.test.js +177 -1
- package/dist/team/__tests__/tmux-test-fixture.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +45 -72
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/notice-ledger.d.ts +54 -0
- package/dist/team/notice-ledger.d.ts.map +1 -0
- package/dist/team/notice-ledger.js +259 -0
- package/dist/team/notice-ledger.js.map +1 -0
- package/dist/team/phase-controller.d.ts.map +1 -1
- package/dist/team/phase-controller.js +2 -0
- package/dist/team/phase-controller.js.map +1 -1
- package/dist/team/runtime.d.ts +3 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +314 -142
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +161 -91
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/monitor.d.ts +10 -0
- package/dist/team/state/monitor.d.ts.map +1 -1
- package/dist/team/state/monitor.js +13 -0
- package/dist/team/state/monitor.js.map +1 -1
- package/dist/team/state/types.d.ts +15 -0
- package/dist/team/state/types.d.ts.map +1 -1
- package/dist/team/state/types.js.map +1 -1
- package/dist/team/state-root.d.ts +12 -0
- package/dist/team/state-root.d.ts.map +1 -1
- package/dist/team/state-root.js +177 -0
- package/dist/team/state-root.js.map +1 -1
- package/dist/team/state.d.ts +18 -0
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +36 -0
- package/dist/team/state.js.map +1 -1
- package/dist/team/tmux-session.d.ts +40 -4
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +513 -166
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +950 -2
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js +1 -0
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +35 -1
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +232 -46
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/utils/__tests__/file-durability.test.js +58 -1
- package/dist/utils/__tests__/file-durability.test.js.map +1 -1
- package/dist/utils/__tests__/paths.test.js +21 -1
- package/dist/utils/__tests__/paths.test.js.map +1 -1
- package/dist/utils/file-durability.d.ts +10 -0
- package/dist/utils/file-durability.d.ts.map +1 -1
- package/dist/utils/file-durability.js +34 -4
- package/dist/utils/file-durability.js.map +1 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +14 -6
- package/dist/utils/paths.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +49 -4
- package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js +60 -0
- package/dist/verification/__tests__/explore-harness-release-workflow.test.js.map +1 -1
- package/dist/verification/__tests__/native-release-manifest.test.js +157 -4
- package/dist/verification/__tests__/native-release-manifest.test.js.map +1 -1
- package/package.json +13 -5
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +81 -17
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +13 -5
- package/plugins/oh-my-codex/skills/cancel/SKILL.md +30 -85
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +5 -5
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +15 -17
- package/plugins/oh-my-codex/skills/ultragoal/SKILL.md +3 -2
- package/skills/autopilot/SKILL.md +13 -5
- package/skills/cancel/SKILL.md +30 -85
- package/skills/ralph/SKILL.md +5 -5
- package/skills/ralplan/SKILL.md +15 -17
- package/skills/ultragoal/SKILL.md +3 -2
- package/src/scripts/__tests__/codex-native-hook.test.ts +6091 -540
- package/src/scripts/__tests__/issue-3293-callsite-parity.test.ts +187 -0
- package/src/scripts/__tests__/issue-3293-cancel-transaction.test.ts +189 -0
- package/src/scripts/__tests__/issue-3293-hook-owned-cancel.test.ts +187 -0
- package/src/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.ts +591 -0
- package/src/scripts/__tests__/issue-3358-ultragoal-cancel-lifecycle.test.ts +701 -0
- package/src/scripts/__tests__/issue-3369-autopilot-ralplan-state-machine.test.ts +211 -0
- package/src/scripts/__tests__/notify-state-io.test.ts +29 -0
- package/src/scripts/__tests__/notify-tmux-injection.test.ts +26 -1
- package/src/scripts/__tests__/postinstall.test.ts +23 -1
- package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +27 -8
- package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +61 -27
- package/src/scripts/__tests__/smoke-packed-install.test.ts +209 -18
- package/src/scripts/__tests__/verify-native-release-assets.test.ts +240 -0
- package/src/scripts/codex-native-hook.ts +3638 -475
- package/src/scripts/codex-native-pre-post.ts +43 -12
- package/src/scripts/generate-native-release-manifest.ts +83 -79
- package/src/scripts/notify-fallback-watcher.ts +38 -38
- package/src/scripts/notify-hook/state-io.ts +11 -0
- package/src/scripts/notify-hook/team-leader-nudge.ts +32 -4
- package/src/scripts/notify-hook/team-worker-stop.ts +23 -4
- package/src/scripts/notify-hook/team-worker.ts +43 -5
- package/src/scripts/notify-hook/tmux-injection.ts +5 -1
- package/src/scripts/notify-hook.ts +83 -82
- package/src/scripts/postinstall-advisory.ts +67 -0
- package/src/scripts/postinstall.ts +1 -1
- package/src/scripts/smoke-packed-install.ts +446 -59
- package/src/scripts/sync-plugin-mirror.ts +17 -1
- package/src/scripts/verify-native-release-assets.ts +39 -31
- package/templates/AGENTS.md +1 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { afterEach, describe, it, mock } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, utimesSync } from "node:fs";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
4
5
|
import { chmod, lstat, mkdir, mkdtemp, readFile, readdir as fsReaddir, rm, stat, symlink, writeFile } from "node:fs/promises";
|
|
5
6
|
import { delimiter, dirname, join } from "node:path";
|
|
6
7
|
import { tmpdir } from "node:os";
|
|
7
8
|
import { fileURLToPath } from "node:url";
|
|
8
|
-
import { once } from "node:events";
|
|
9
9
|
import TOML from "@iarna/toml";
|
|
10
|
-
import { HELP, normalizeCodexLaunchArgs, buildTmuxShellCommand, buildTmuxPaneCommand, shouldSourceTmuxPaneShellRc, buildWindowsPromptCommand, buildTmuxSessionName, resolveCliInvocation, parseResumeCodexHomeSelection, isResumeCodexLaunch, CODEX_GLOBAL_OPTIONS_WITH_SPLIT_VALUE, isCodexVersionRequest, resolveUpdateChannelArg, commandOwnsLocalHelp, resolveCodexLaunchPolicy, resolveEffectiveLeaderLaunchPolicyOverride, resolveEnvLaunchPolicyOverride, resolveLeaderLaunchPolicyOverride, classifyCodexExecFailure, resolveSignalExitCode, parseTmuxPaneSnapshot, findHudWatchPaneIds, buildHudPaneCleanupTargets, readTopLevelTomlString, upsertTopLevelTomlString, collectInheritableTeamWorkerArgs, resolveTeamWorkerLaunchArgsEnv, injectModelInstructionsBypassArgs, resolveWorkerSparkModel, resolveSetupInstallModeArg, resolveSetupMcpModeArg, resolveSetupScopeArg, resolveSetupTeamModeArg, resolveSetupAgentsMergePolicyArg, resolveLaunchConfigRepairOptions, readPersistedSetupPreferences, readPersistedSetupScope, resolveCodexConfigPathForLaunch, resolveCodexHomeForLaunch, resolveProjectLocalCodexHomeForLaunch, shouldAutoIsolateMadmaxLaunch, createMadmaxIsolatedRoot, buildMadmaxDetachedLaunchContextKey, withMadmaxDetachedContextLock, resolveOmxRootForLaunch, resolveDisposableWorktreeOmxRootForLaunch, prepareCodexHomeForLaunch, captureMadmaxWorktreeRuntimeContext, persistProjectLaunchRuntimeAuthState, persistProjectLaunchRuntimeProjectTrustState, cleanupRuntimeCodexHome, runtimeCodexHomePath, buildDetachedSessionBootstrapSteps, buildDetachedTmuxSessionName, buildDetachedSessionFinalizeSteps, shouldAttachDetachedTmuxSession, buildDetachedSessionRollbackSteps, detectDetachedSessionWindowIndex, resolveNotifyTempContract, buildNotifyTempStartupMessages, buildNotifyFallbackWatcherEnv, shouldEnableNotifyFallbackWatcher, reapStaleNotifyFallbackWatcher, cleanupLaunchOrphanedMcpProcesses, reapPostLaunchOrphanedMcpProcesses, cleanupPostLaunchModeStateFiles, resolveBackgroundHelperLaunchMode, shouldDetachBackgroundHelper, resolveNotifyFallbackWatcherScript, resolveHookDerivedWatcherScript, resolveNotifyHookScript,
|
|
10
|
+
import { HELP, normalizeCodexLaunchArgs, buildTmuxShellCommand, buildTmuxPaneCommand, shouldSourceTmuxPaneShellRc, buildWindowsPromptCommand, buildWindowsDetachedChildCommand, buildTmuxSessionName, resolveCliInvocation, parseResumeCodexHomeSelection, isResumeCodexLaunch, CODEX_GLOBAL_OPTIONS_WITH_SPLIT_VALUE, isCodexVersionRequest, resolveUpdateChannelArg, commandOwnsLocalHelp, resolveCodexLaunchPolicy, resolveEffectiveLeaderLaunchPolicyOverride, resolveEnvLaunchPolicyOverride, resolveLeaderLaunchPolicyOverride, classifyCodexExecFailure, resolveSignalExitCode, parseTmuxPaneSnapshot, findHudWatchPaneIds, buildHudPaneCleanupTargets, readTopLevelTomlString, upsertTopLevelTomlString, collectInheritableTeamWorkerArgs, resolveTeamWorkerLaunchArgsEnv, injectModelInstructionsBypassArgs, resolveWorkerSparkModel, resolveSetupInstallModeArg, resolveSetupMcpModeArg, resolveSetupScopeArg, resolveSetupTeamModeArg, resolveSetupAgentsMergePolicyArg, resolveLaunchConfigRepairOptions, readPersistedSetupPreferences, readPersistedSetupScope, resolveCodexConfigPathForLaunch, resolveCodexHomeForLaunch, resolveProjectLocalCodexHomeForLaunch, shouldAutoIsolateMadmaxLaunch, createMadmaxIsolatedRoot, buildMadmaxDetachedLaunchContextKey, withMadmaxDetachedContextLock, executeDetachedLaunchStateMachine, resolveOmxRootForLaunch, resolveDisposableWorktreeOmxRootForLaunch, prepareCodexHomeForLaunch, captureMadmaxWorktreeRuntimeContext, persistProjectLaunchRuntimeAuthState, persistProjectLaunchRuntimeProjectTrustState, cleanupRuntimeCodexHome, runtimeCodexHomePath, buildDetachedSessionBootstrapSteps, buildDetachedTmuxSessionName, buildDetachedSessionFinalizeSteps, shouldAttachDetachedTmuxSession, buildDetachedSessionRollbackSteps, detectDetachedSessionWindowIndex, resolveNotifyTempContract, buildNotifyTempStartupMessages, buildNotifyFallbackWatcherEnv, shouldEnableNotifyFallbackWatcher, reapStaleNotifyFallbackWatcher, cleanupLaunchOrphanedMcpProcesses, reapPostLaunchOrphanedMcpProcesses, cleanupPostLaunchModeStateFiles, resolveBackgroundHelperLaunchMode, shouldDetachBackgroundHelper, resolveNotifyFallbackWatcherScript, resolveHookDerivedWatcherScript, resolveNotifyHookScript, acquireTmuxExtendedKeysLease, resolveNativeSessionName, releaseTmuxExtendedKeysLease, withTmuxExtendedKeys, serializeDetachedSessionParentEnv, buildInsideTmuxHudHookEnv, registerInsideTmuxHudResizeHook, buildDetachedHudHookEnv, registerDetachedHudLayoutReconcileHook, ensureOmxRuntimeCommandShim, omxRuntimeCommandShimPath, prependOmxRuntimeCommandShimToEnv, CODEX_SQLITE_HOME_ENV, DETACHED_TMUX_HISTORY_LIMIT, isExistingTmuxWindowTooCrampedForLaunchHud, guardDetachedHudDeferredMutation, DETACHED_LAUNCH_CONTROL_PLANE_KEYS, buildDetachedLaunchControlPlane, } from "../index.js";
|
|
11
11
|
import { buildResumeArgsWithPreservedFlags, stripHotswapArg } from "../../auth/hotswap.js";
|
|
12
|
+
import { writeSkillActiveStateCopiesForStateDir } from '../../state/skill-active.js';
|
|
12
13
|
import { mergeConfig, repairConfigIfNeeded } from "../../config/generator.js";
|
|
13
14
|
import { ensureReusableNodeModules } from "../../utils/repo-deps.js";
|
|
14
15
|
import { readAllState } from "../../hud/state.js";
|
|
@@ -17,6 +18,7 @@ import { HUD_TMUX_HEIGHT_LINES, HUD_TMUX_MIN_LAUNCH_WINDOW_HEIGHT_LINES } from "
|
|
|
17
18
|
import { createHudWatchPane as createSharedHudWatchPane, listCurrentWindowHudPaneIds } from "../../hud/tmux.js";
|
|
18
19
|
import { DEFAULT_FRONTIER_MODEL, getTeamLowComplexityModel, } from "../../config/models.js";
|
|
19
20
|
import { splitWorkerLaunchArgs } from "../../team/model-contract.js";
|
|
21
|
+
import { buildRegisterResizeHookArgs } from "../../team/tmux-session.js";
|
|
20
22
|
const testDir = dirname(fileURLToPath(import.meta.url));
|
|
21
23
|
const repoRoot = join(testDir, "..", "..", "..");
|
|
22
24
|
function normalizeDarwinTmpPath(value) {
|
|
@@ -67,6 +69,7 @@ describe("madmax state isolation", () => {
|
|
|
67
69
|
const runDir = createMadmaxIsolatedRoot(wd, ["--madmax", "--high"], env);
|
|
68
70
|
env.OMX_ROOT = runDir;
|
|
69
71
|
env.OMXBOX_ACTIVE = "1";
|
|
72
|
+
env.OMX_SOURCE_CWD = wd;
|
|
70
73
|
assert.equal(shouldAutoIsolateMadmaxLaunch("launch", ["--madmax", "--high"], env, wd), false);
|
|
71
74
|
assert.equal(shouldAutoIsolateMadmaxLaunch("launch", ["--madmax", "--xhigh"], env, wd), true, "changed launch semantics must not reuse an inherited boxed root");
|
|
72
75
|
}
|
|
@@ -78,29 +81,40 @@ describe("madmax state isolation", () => {
|
|
|
78
81
|
it("preserves explicit no-box behavior", () => {
|
|
79
82
|
assert.equal(shouldAutoIsolateMadmaxLaunch("launch", ["--madmax"], { OMX_NO_BOX: "1" }, "/repo"), false);
|
|
80
83
|
});
|
|
81
|
-
it("captures madmax worktree context from parsed worktree state, not remaining args", () => {
|
|
84
|
+
it("captures madmax worktree context from parsed worktree state, not remaining args", async () => {
|
|
82
85
|
const sourceCwd = "/repo/source";
|
|
83
86
|
const worktreeCwd = "/repo/.worktrees/session";
|
|
84
|
-
const runDir = "
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
87
|
+
const runDir = await mkdtemp(join(tmpdir(), "omx-run-issue-3043-"));
|
|
88
|
+
try {
|
|
89
|
+
const detachedContext = buildMadmaxDetachedLaunchContextKey(sourceCwd, ["--madmax", "--worktree", "--version"], runDir);
|
|
90
|
+
await writeFile(join(runDir, ".omxbox-run.json"), JSON.stringify({
|
|
91
|
+
cwd: runDir,
|
|
92
|
+
source_cwd: sourceCwd,
|
|
93
|
+
detached_launch_context: detachedContext,
|
|
94
|
+
}));
|
|
95
|
+
const context = captureMadmaxWorktreeRuntimeContext({
|
|
96
|
+
originalLaunchArgs: ["--madmax", "--worktree", "--version"],
|
|
97
|
+
worktreeEnabled: true,
|
|
98
|
+
sourceCwd,
|
|
99
|
+
worktreeCwd,
|
|
100
|
+
env: {
|
|
101
|
+
OMX_ROOT: runDir,
|
|
102
|
+
OMXBOX_ACTIVE: "1",
|
|
103
|
+
OMX_SOURCE_CWD: sourceCwd,
|
|
104
|
+
OMX_MADMAX_DETACHED_CONTEXT: detachedContext,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
assert.deepEqual(context, {
|
|
108
|
+
omxRoot: runDir,
|
|
109
|
+
sourceCwd,
|
|
110
|
+
worktreeCwd,
|
|
111
|
+
madmaxDetachedContext: detachedContext,
|
|
112
|
+
boxedActive: true,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
await rm(runDir, { recursive: true, force: true });
|
|
117
|
+
}
|
|
104
118
|
});
|
|
105
119
|
it("does not capture ordinary worktree or unboxed madmax launches", () => {
|
|
106
120
|
assert.equal(captureMadmaxWorktreeRuntimeContext({
|
|
@@ -196,7 +210,7 @@ describe("madmax state isolation", () => {
|
|
|
196
210
|
mkdirSync(lockPath, { recursive: true });
|
|
197
211
|
await writeFile(join(lockPath, "pid"), "2147483647");
|
|
198
212
|
let ran = false;
|
|
199
|
-
const result = withMadmaxDetachedContextLock(runs, contextKey, () => {
|
|
213
|
+
const result = await withMadmaxDetachedContextLock(runs, contextKey, () => {
|
|
200
214
|
ran = true;
|
|
201
215
|
return "acquired";
|
|
202
216
|
}, { maxAttempts: 2, retryMs: 0 });
|
|
@@ -221,7 +235,7 @@ describe("madmax state isolation", () => {
|
|
|
221
235
|
acquired_at: new Date().toISOString(),
|
|
222
236
|
})}\n`);
|
|
223
237
|
await writeFile(join(lockPath, "pid"), String(process.pid));
|
|
224
|
-
assert.
|
|
238
|
+
await assert.rejects(() => withMadmaxDetachedContextLock(runs, contextKey, () => "should-not-run", { maxAttempts: 1, retryMs: 0 }), (err) => {
|
|
225
239
|
assert.ok(err instanceof Error);
|
|
226
240
|
assert.match(err.message, /timed out waiting for madmax detached launch context lock/);
|
|
227
241
|
assert.match(err.message, new RegExp(`holder pid ${process.pid} is still running`));
|
|
@@ -238,6 +252,135 @@ describe("madmax state isolation", () => {
|
|
|
238
252
|
}
|
|
239
253
|
});
|
|
240
254
|
});
|
|
255
|
+
describe("detached launch state machine", () => {
|
|
256
|
+
function createDependencies(events, failAt) {
|
|
257
|
+
const step = async (name) => {
|
|
258
|
+
events.push(name);
|
|
259
|
+
if (failAt === name)
|
|
260
|
+
throw new Error(name);
|
|
261
|
+
};
|
|
262
|
+
return {
|
|
263
|
+
establish: async () => { await step("D1"); return "binding"; },
|
|
264
|
+
complete: async () => { await step("D2"); return { kind: "success" }; },
|
|
265
|
+
createInertSession: async () => { await step("D3"); return "inert"; },
|
|
266
|
+
capturePane: async () => { await step("D4"); return "%1"; },
|
|
267
|
+
updateNameMetadata: async () => { await step("D5"); return "committed-released"; },
|
|
268
|
+
updatePaneMetadata: async () => { await step("D6"); return "committed-released"; },
|
|
269
|
+
publishActiveRecord: async () => {
|
|
270
|
+
await step("D7");
|
|
271
|
+
return { bytes: "record", digest: "digest", nonce: "nonce" };
|
|
272
|
+
},
|
|
273
|
+
finalizeSetupFailure: async () => { await step("finalize-setup-failure"); },
|
|
274
|
+
releaseBarrier: async () => { await step("D9"); },
|
|
275
|
+
abortAndAwaitFinalization: async () => ({
|
|
276
|
+
acknowledged: true,
|
|
277
|
+
nonce: "nonce",
|
|
278
|
+
sessionId: "session",
|
|
279
|
+
sessionName: "session-name",
|
|
280
|
+
leaderPid: 123,
|
|
281
|
+
kind: "failed",
|
|
282
|
+
}),
|
|
283
|
+
attachOrReturn: async () => { await step("D10"); },
|
|
284
|
+
rollback: async () => { events.push("rollback"); },
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
it("uses the frozen D0 outcome without rediscovering transport or reuse", async () => {
|
|
288
|
+
const events = [];
|
|
289
|
+
const result = await executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, createDependencies(events));
|
|
290
|
+
assert.equal(result.kind, "attached");
|
|
291
|
+
assert.deepEqual(events, ["D1", "D2", "D3", "D4", "D5", "D6", "D7", "D9", "D10"]);
|
|
292
|
+
});
|
|
293
|
+
for (const platform of ["posix", "native-windows"]) {
|
|
294
|
+
it(`${platform}: releases only after atomic record and starts exactly once`, async () => {
|
|
295
|
+
const events = [];
|
|
296
|
+
const result = await executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: platform === "posix", report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, createDependencies(events));
|
|
297
|
+
assert.equal(result.kind, platform === "posix" ? "attached" : "returned");
|
|
298
|
+
assert.deepEqual(events, ["D1", "D2", "D3", "D4", "D5", "D6", "D7", "D9", "D10"]);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
for (const failure of ["D1", "D2", "D3", "D4", "D5", "D6", "D7"]) {
|
|
302
|
+
it(`rolls back verified pre-release ownership when ${failure} fails`, async () => {
|
|
303
|
+
const events = [];
|
|
304
|
+
await assert.rejects(executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, createDependencies(events, failure)), (err) => {
|
|
305
|
+
assert.ok(err instanceof Error);
|
|
306
|
+
assert.equal(err.report?.transitions.at(-1), failure);
|
|
307
|
+
return true;
|
|
308
|
+
});
|
|
309
|
+
assert.ok(events.includes("rollback"));
|
|
310
|
+
assert.equal(events.includes("D10"), false);
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
it("finalizes and closes exactly once before D2 rollback without transport effects", async () => {
|
|
314
|
+
const events = [];
|
|
315
|
+
const deps = createDependencies(events);
|
|
316
|
+
deps.complete = async () => {
|
|
317
|
+
events.push("D2");
|
|
318
|
+
return { kind: "failure", operation: "session-instructions", error: new Error("instructions") };
|
|
319
|
+
};
|
|
320
|
+
await assert.rejects(() => executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps), /preLaunch session-instructions failed: instructions/);
|
|
321
|
+
assert.deepEqual(events, ["D1", "D2", "finalize-setup-failure", "rollback"]);
|
|
322
|
+
});
|
|
323
|
+
it("rolls back proven pane authority when leader parsing fails before a report PID", async () => {
|
|
324
|
+
const events = [];
|
|
325
|
+
const deps = createDependencies(events, "D2");
|
|
326
|
+
deps.abortAndAwaitFinalization = async () => ({ acknowledged: false, rollbackAuthorized: true });
|
|
327
|
+
await assert.rejects(executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
|
|
328
|
+
assert.equal(events.includes("rollback"), true);
|
|
329
|
+
});
|
|
330
|
+
it("never rolls back, deletes records, or falls back after D10 attach failure", async () => {
|
|
331
|
+
const events = [];
|
|
332
|
+
await assert.rejects(executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, createDependencies(events, "D10")), (err) => {
|
|
333
|
+
assert.ok(err instanceof Error);
|
|
334
|
+
assert.equal(err.report?.transitions.at(-1), "D10");
|
|
335
|
+
return true;
|
|
336
|
+
});
|
|
337
|
+
assert.equal(events.includes("rollback"), false);
|
|
338
|
+
assert.deepEqual(events.slice(-2), ["D9", "D10"]);
|
|
339
|
+
});
|
|
340
|
+
it("preserves leader authority when D9 publication fails without an authenticated finalization acknowledgement", async () => {
|
|
341
|
+
const events = [];
|
|
342
|
+
const deps = createDependencies(events, "D9");
|
|
343
|
+
deps.abortAndAwaitFinalization = async () => {
|
|
344
|
+
events.push("abort-request");
|
|
345
|
+
return { acknowledged: false };
|
|
346
|
+
};
|
|
347
|
+
await assert.rejects(() => executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
|
|
348
|
+
assert.deepEqual(events.slice(-2), ["D9", "abort-request"]);
|
|
349
|
+
assert.equal(events.includes("rollback"), false);
|
|
350
|
+
});
|
|
351
|
+
it("permits rollback only after an authenticated D9 abort acknowledgement", async () => {
|
|
352
|
+
const events = [];
|
|
353
|
+
const deps = createDependencies(events, "D9");
|
|
354
|
+
deps.abortAndAwaitFinalization = async () => ({
|
|
355
|
+
acknowledged: true,
|
|
356
|
+
nonce: "nonce",
|
|
357
|
+
sessionId: "session",
|
|
358
|
+
sessionName: "session-name",
|
|
359
|
+
leaderPid: 123,
|
|
360
|
+
kind: "failed",
|
|
361
|
+
});
|
|
362
|
+
await assert.rejects(() => executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
|
|
363
|
+
assert.equal(events.includes("rollback"), true);
|
|
364
|
+
});
|
|
365
|
+
it("fails closed when a purported D9 acknowledgement omits its finalized authority binding", async () => {
|
|
366
|
+
const events = [];
|
|
367
|
+
const deps = createDependencies(events, "D9");
|
|
368
|
+
deps.abortAndAwaitFinalization = async () => ({ acknowledged: true });
|
|
369
|
+
await assert.rejects(() => executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
|
|
370
|
+
assert.equal(events.includes("rollback"), false);
|
|
371
|
+
});
|
|
372
|
+
it("preserves the authority-owning leader on a pre-D9 failure without finalized acknowledgement", async () => {
|
|
373
|
+
const events = [];
|
|
374
|
+
const deps = createDependencies(events, "D4");
|
|
375
|
+
deps.abortAndAwaitFinalization = async () => {
|
|
376
|
+
events.push("abort-request");
|
|
377
|
+
return { acknowledged: false };
|
|
378
|
+
};
|
|
379
|
+
await assert.rejects(() => executeDetachedLaunchStateMachine({ preflight: { kind: "available", shouldAttach: true, report: { transitions: ["D0"], rollback: { attempted: [], failures: [] } } } }, deps));
|
|
380
|
+
assert.deepEqual(events.slice(-2), ["D4", "abort-request"]);
|
|
381
|
+
assert.equal(events.includes("rollback"), false);
|
|
382
|
+
});
|
|
383
|
+
});
|
|
241
384
|
describe("resolveOmxRootForLaunch", () => {
|
|
242
385
|
it("preserves POSIX absolute OMX_ROOT", () => {
|
|
243
386
|
assert.equal(resolveOmxRootForLaunch("/repo", { OMX_ROOT: "/var/tmp/omx" }), "/var/tmp/omx");
|
|
@@ -850,6 +993,81 @@ describe("cleanupPostLaunchModeStateFiles", () => {
|
|
|
850
993
|
}
|
|
851
994
|
assert.deepEqual(warnings, []);
|
|
852
995
|
});
|
|
996
|
+
it('routes root skill-active cleanup through the locked root writer', async () => {
|
|
997
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-postlaunch-root-writer-'));
|
|
998
|
+
const sessionId = 'sess-root-writer';
|
|
999
|
+
const stateDir = join(wd, '.omx', 'state');
|
|
1000
|
+
const rootState = {
|
|
1001
|
+
version: 1,
|
|
1002
|
+
active: true,
|
|
1003
|
+
skill: 'ralph',
|
|
1004
|
+
session_id: sessionId,
|
|
1005
|
+
active_skills: [{ skill: 'ralph', phase: 'executing', active: true, session_id: sessionId }],
|
|
1006
|
+
};
|
|
1007
|
+
await mkdir(join(stateDir, 'sessions', sessionId), { recursive: true });
|
|
1008
|
+
await writeFile(join(stateDir, 'skill-active-state.json'), `${JSON.stringify(rootState, null, 2)}\n`, 'utf8');
|
|
1009
|
+
let writerCalls = 0;
|
|
1010
|
+
await cleanupPostLaunchModeStateFiles(wd, sessionId, {
|
|
1011
|
+
writeRootState: async (rootDir, update) => {
|
|
1012
|
+
writerCalls += 1;
|
|
1013
|
+
const concurrentRoot = {
|
|
1014
|
+
...rootState,
|
|
1015
|
+
active_skills: [
|
|
1016
|
+
...rootState.active_skills,
|
|
1017
|
+
{ skill: 'team', phase: 'running', active: true, session_id: 'sess-concurrent' },
|
|
1018
|
+
],
|
|
1019
|
+
};
|
|
1020
|
+
const nextState = update(concurrentRoot);
|
|
1021
|
+
assert.ok(nextState);
|
|
1022
|
+
await writeSkillActiveStateCopiesForStateDir(rootDir, nextState);
|
|
1023
|
+
},
|
|
1024
|
+
});
|
|
1025
|
+
assert.equal(writerCalls, 1);
|
|
1026
|
+
const persisted = JSON.parse(await readFile(join(stateDir, 'skill-active-state.json'), 'utf8'));
|
|
1027
|
+
assert.equal(persisted.active, true);
|
|
1028
|
+
assert.deepEqual(persisted.active_skills, [{ skill: 'team', phase: 'running', active: true, session_id: 'sess-concurrent' }]);
|
|
1029
|
+
});
|
|
1030
|
+
it('preserves a process-level session update before root scrub cleanup', async () => {
|
|
1031
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-postlaunch-process-scrub-'));
|
|
1032
|
+
const sessionId = 'sess-process-scrub';
|
|
1033
|
+
const stateDir = join(wd, '.omx', 'state');
|
|
1034
|
+
const rootPath = join(stateDir, 'skill-active-state.json');
|
|
1035
|
+
const rootState = {
|
|
1036
|
+
version: 1,
|
|
1037
|
+
active: true,
|
|
1038
|
+
skill: 'ralph',
|
|
1039
|
+
session_id: sessionId,
|
|
1040
|
+
active_skills: [{ skill: 'ralph', phase: 'executing', active: true, session_id: sessionId }],
|
|
1041
|
+
};
|
|
1042
|
+
try {
|
|
1043
|
+
await mkdir(join(stateDir, 'sessions', sessionId), { recursive: true });
|
|
1044
|
+
await writeFile(rootPath, `${JSON.stringify(rootState, null, 2)}\n`, 'utf8');
|
|
1045
|
+
const updateScript = `
|
|
1046
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
1047
|
+
import { writeSkillActiveStateCopiesForStateDir } from './dist/state/skill-active.js';
|
|
1048
|
+
const stateDir = process.env.STATE_DIR;
|
|
1049
|
+
const current = JSON.parse(await readFile(process.env.ROOT_PATH, 'utf8'));
|
|
1050
|
+
current.active_skills.push({ skill: 'team', phase: 'running', active: true, session_id: 'sess-process-concurrent' });
|
|
1051
|
+
await writeSkillActiveStateCopiesForStateDir(stateDir, current, undefined, current);
|
|
1052
|
+
`;
|
|
1053
|
+
const update = spawnSync(process.execPath, ['--input-type=module', '-e', updateScript], {
|
|
1054
|
+
cwd: repoRoot,
|
|
1055
|
+
env: { ...process.env, STATE_DIR: stateDir, ROOT_PATH: rootPath },
|
|
1056
|
+
encoding: 'utf8',
|
|
1057
|
+
});
|
|
1058
|
+
assert.equal(update.status, 0, update.stderr);
|
|
1059
|
+
const scrub = spawnSync(process.execPath, ['--input-type=module', '-e', `
|
|
1060
|
+
import { cleanupPostLaunchModeStateFiles } from './dist/cli/index.js';
|
|
1061
|
+
await cleanupPostLaunchModeStateFiles(${JSON.stringify(wd)}, ${JSON.stringify(sessionId)});
|
|
1062
|
+
`], { cwd: repoRoot, env: { ...process.env }, encoding: 'utf8' });
|
|
1063
|
+
assert.equal(scrub.status, 0, scrub.stderr);
|
|
1064
|
+
const persisted = JSON.parse(await readFile(rootPath, 'utf8'));
|
|
1065
|
+
assert.deepEqual(persisted.active_skills, [{ skill: 'team', phase: 'running', active: true, session_id: 'sess-process-concurrent' }]);
|
|
1066
|
+
}
|
|
1067
|
+
finally {
|
|
1068
|
+
await rm(wd, { recursive: true, force: true });
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
853
1071
|
it("normalizes stale terminal deep-interview locks during postLaunch cleanup", async () => {
|
|
854
1072
|
const wd = await mkdtemp(join(tmpdir(), "omx-postlaunch-di-terminal-locks-"));
|
|
855
1073
|
const sessionId = "sess-postlaunch-di-terminal-locks";
|
|
@@ -1532,6 +1750,10 @@ describe("resolveCliInvocation", () => {
|
|
|
1532
1750
|
assert.match(HELP, /omx update --stable\s+Install\/rollback to npm stable \(oh-my-codex@latest\), then refresh setup/);
|
|
1533
1751
|
assert.match(HELP, /omx update --dev\s+Install the upstream dev branch, then refresh setup/);
|
|
1534
1752
|
});
|
|
1753
|
+
it("scopes Ralplan authority preflight in top-level help", () => {
|
|
1754
|
+
assert.match(HELP, /omx ralplan\s+Adapted Ralplan authority support; preflight applies only when native role routing is unavailable\s+and adapted Ralplan authority is requested/);
|
|
1755
|
+
assert.doesNotMatch(HELP, /Record validated role intents/);
|
|
1756
|
+
});
|
|
1535
1757
|
it("advertises only the four supported root reasoning modes", () => {
|
|
1536
1758
|
assert.match(HELP, /omx reasoning Show or set model reasoning effort \(low\|medium\|high\|xhigh\)/);
|
|
1537
1759
|
assert.match(HELP, /--high\s+Launch Codex with high reasoning effort/);
|
|
@@ -2231,6 +2453,47 @@ describe("project launch scope helpers", () => {
|
|
|
2231
2453
|
await rm(wd, { recursive: true, force: true });
|
|
2232
2454
|
}
|
|
2233
2455
|
});
|
|
2456
|
+
it("walks upward to find a project-scoped setup for launch resolution", async () => {
|
|
2457
|
+
const wd = await mkdtemp(join(tmpdir(), "omx-launch-scope-"));
|
|
2458
|
+
try {
|
|
2459
|
+
await mkdir(join(wd, ".omx"), { recursive: true });
|
|
2460
|
+
await writeFile(join(wd, ".omx", "setup-scope.json"), JSON.stringify({ scope: "project" }));
|
|
2461
|
+
const nested = join(wd, "packages", "app", "src");
|
|
2462
|
+
await mkdir(nested, { recursive: true });
|
|
2463
|
+
assert.equal(resolveCodexHomeForLaunch(nested, {}), join(wd, ".codex"));
|
|
2464
|
+
assert.equal(resolveCodexConfigPathForLaunch(nested, {}), join(wd, ".codex", "config.toml"));
|
|
2465
|
+
}
|
|
2466
|
+
finally {
|
|
2467
|
+
await rm(wd, { recursive: true, force: true });
|
|
2468
|
+
}
|
|
2469
|
+
});
|
|
2470
|
+
it("does not redirect a launch outside any setup root into project CODEX_HOME", async () => {
|
|
2471
|
+
const wd = await mkdtemp(join(tmpdir(), "omx-launch-scope-"));
|
|
2472
|
+
try {
|
|
2473
|
+
await mkdir(join(wd, ".omx"), { recursive: true });
|
|
2474
|
+
await writeFile(join(wd, ".omx", "setup-scope.json"), JSON.stringify({ scope: "project" }));
|
|
2475
|
+
const outside = join(wd, "..", `outside-${Date.now()}`);
|
|
2476
|
+
await mkdir(outside, { recursive: true });
|
|
2477
|
+
assert.equal(resolveCodexHomeForLaunch(outside, {}), undefined);
|
|
2478
|
+
}
|
|
2479
|
+
finally {
|
|
2480
|
+
await rm(wd, { recursive: true, force: true });
|
|
2481
|
+
}
|
|
2482
|
+
});
|
|
2483
|
+
it("keeps a user-scope setup on the user config when launching from a subdirectory", async () => {
|
|
2484
|
+
const wd = await mkdtemp(join(tmpdir(), "omx-launch-scope-"));
|
|
2485
|
+
try {
|
|
2486
|
+
await mkdir(join(wd, ".omx"), { recursive: true });
|
|
2487
|
+
await writeFile(join(wd, ".omx", "setup-scope.json"), JSON.stringify({ scope: "user" }));
|
|
2488
|
+
const nested = join(wd, "sub");
|
|
2489
|
+
await mkdir(nested, { recursive: true });
|
|
2490
|
+
assert.equal(resolveCodexHomeForLaunch(nested, {}), undefined);
|
|
2491
|
+
assert.equal(resolveProjectLocalCodexHomeForLaunch(nested, {}), undefined);
|
|
2492
|
+
}
|
|
2493
|
+
finally {
|
|
2494
|
+
await rm(wd, { recursive: true, force: true });
|
|
2495
|
+
}
|
|
2496
|
+
});
|
|
2234
2497
|
});
|
|
2235
2498
|
describe("pointer launch aborts", () => {
|
|
2236
2499
|
it("does not launch, tag, or retain a runtime home when a pointer lock is held", async () => {
|
|
@@ -2320,7 +2583,7 @@ describe("pointer launch aborts", () => {
|
|
|
2320
2583
|
},
|
|
2321
2584
|
});
|
|
2322
2585
|
assert.equal(result.status, 1, result.stderr || result.stdout);
|
|
2323
|
-
assert.match(result.stderr, /
|
|
2586
|
+
assert.match(result.stderr, /Unable to establish launch directory capability/);
|
|
2324
2587
|
assert.equal(existsSync(codexLog), false);
|
|
2325
2588
|
assert.equal(existsSync(tmuxLog), false);
|
|
2326
2589
|
assert.equal(existsSync(join(wd, ".omx", "state", "sessions")), false);
|
|
@@ -2455,20 +2718,23 @@ describe("tmux HUD pane helpers", () => {
|
|
|
2455
2718
|
it("buildHudPaneCleanupTargets is a no-op guard when leaderPaneId is absent", () => {
|
|
2456
2719
|
assert.deepEqual(buildHudPaneCleanupTargets(["%3"], "%4"), ["%3", "%4"]);
|
|
2457
2720
|
});
|
|
2458
|
-
it("listCurrentWindowHudPaneIds
|
|
2721
|
+
it("listCurrentWindowHudPaneIds excludes snapshots without complete current ownership", () => {
|
|
2459
2722
|
const calls = [];
|
|
2460
|
-
const panes = listCurrentWindowHudPaneIds("%
|
|
2723
|
+
const panes = listCurrentWindowHudPaneIds("%1", (args) => {
|
|
2461
2724
|
calls.push(args);
|
|
2725
|
+
if (args.at(-1) === "#{pane_id}")
|
|
2726
|
+
return "%1\n%2\n";
|
|
2462
2727
|
return [
|
|
2463
|
-
"%
|
|
2464
|
-
"%
|
|
2728
|
+
"%1\x1fcodex\x1f0\x1f0\x1f100\x1f40\x1f39\x1f100\x1f40\x1fcodex\x1f/repo\x1f0\x1f101",
|
|
2729
|
+
"%2\x1fnode\x1f0\x1f40\x1f100\x1f3\x1f42\x1f100\x1f43\x1fexec env OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%1' node /tmp/bin/dist/cli/omx.js hud --watch\x1f/repo\x1f0\x1f202",
|
|
2465
2730
|
].join("\n");
|
|
2466
2731
|
});
|
|
2467
|
-
assert.deepEqual(panes, [
|
|
2468
|
-
assert.deepEqual(calls[0], [
|
|
2732
|
+
assert.deepEqual(panes, []);
|
|
2733
|
+
assert.deepEqual(calls[0], ["list-panes", "-t", "%1", "-F", "#{pane_id}"]);
|
|
2734
|
+
assert.deepEqual(calls[1], [
|
|
2469
2735
|
"list-panes",
|
|
2470
2736
|
"-t",
|
|
2471
|
-
"%
|
|
2737
|
+
"%1",
|
|
2472
2738
|
"-F",
|
|
2473
2739
|
[
|
|
2474
2740
|
"#{pane_id}",
|
|
@@ -2482,31 +2748,67 @@ describe("tmux HUD pane helpers", () => {
|
|
|
2482
2748
|
"#{window_height}",
|
|
2483
2749
|
"#{pane_start_command}",
|
|
2484
2750
|
"#{pane_current_path}",
|
|
2751
|
+
"#{pane_dead}",
|
|
2752
|
+
"#{pane_pid}",
|
|
2485
2753
|
].join("\x1f"),
|
|
2486
2754
|
]);
|
|
2487
2755
|
});
|
|
2488
|
-
it("createHudWatchPane
|
|
2756
|
+
it("createHudWatchPane rejects an incomplete synthetic source incarnation", () => {
|
|
2489
2757
|
const calls = [];
|
|
2490
|
-
const
|
|
2758
|
+
const options = new Map();
|
|
2759
|
+
let splitCreated = false;
|
|
2760
|
+
let splitMarker = "";
|
|
2761
|
+
const paneId = createSharedHudWatchPane("/repo", "node /repo/dist/cli/omx.js hud --watch", { heightLines: 3, targetPaneId: "%1" }, (args) => {
|
|
2491
2762
|
calls.push(args);
|
|
2492
|
-
|
|
2763
|
+
if (args[0] === "display-message") {
|
|
2764
|
+
const format = args.at(-1);
|
|
2765
|
+
if (format === "#{pane_id}\t#{pane_dead}\t#{pane_pid}\t#{session_id}\t#{window_id}") {
|
|
2766
|
+
return "%1\t0\t101\t$7\t@1";
|
|
2767
|
+
}
|
|
2768
|
+
if (format === "#{session_id}\t#{window_id}")
|
|
2769
|
+
return "$7\t@1";
|
|
2770
|
+
}
|
|
2771
|
+
if (args[0] === "list-panes") {
|
|
2772
|
+
const format = args.at(-1);
|
|
2773
|
+
const panes = splitCreated ? ["%1", "%2"] : ["%1"];
|
|
2774
|
+
if (format === "#{pane_id}")
|
|
2775
|
+
return `${panes.join("\n")}\n`;
|
|
2776
|
+
if (format === "#{pane_id}\t#{pane_start_command}") {
|
|
2777
|
+
return [
|
|
2778
|
+
"%1\tcodex",
|
|
2779
|
+
`%2\tOMX_TMUX_SPLIT_OPERATION_MARKER='${splitMarker}'; export OMX_TMUX_SPLIT_OPERATION_MARKER; exec env OMX_TMUX_HUD_OWNER=1 OMX_TMUX_HUD_LEADER_PANE='%1' node /repo/dist/cli/omx.js hud --watch`,
|
|
2780
|
+
].join("\n");
|
|
2781
|
+
}
|
|
2782
|
+
if (format === "#{pane_id}\t#{pane_dead}\t#{pane_pid}") {
|
|
2783
|
+
return "%1\t0\t101\n%2\t0\t202\n";
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
if (args[0] === "set-option") {
|
|
2787
|
+
options.set(args[2], args[3]);
|
|
2788
|
+
return "";
|
|
2789
|
+
}
|
|
2790
|
+
if (args[0] === "show-options")
|
|
2791
|
+
return `${options.get(args.at(-1)) ?? ""}\n`;
|
|
2792
|
+
if (args[0] === "if-shell") {
|
|
2793
|
+
assert.equal(args[1], "-F");
|
|
2794
|
+
assert.equal(args[2], "-t");
|
|
2795
|
+
assert.equal(args[3], "%1");
|
|
2796
|
+
assert.match(args[4], /#\{pane_id\},%1/);
|
|
2797
|
+
assert.match(args[4], /#\{pane_pid\},101/);
|
|
2798
|
+
assert.match(args[4], /#\{session_id\},\$1/);
|
|
2799
|
+
assert.match(args[4], /#\{window_id\},@1/);
|
|
2800
|
+
splitCreated = true;
|
|
2801
|
+
const marker = /OMX_TMUX_SPLIT_OPERATION_MARKER='([\w-]+)'/.exec(args[5]);
|
|
2802
|
+
const receipt = /display-message -p (__omx_hud_split_[\w-]+)/.exec(args[5]);
|
|
2803
|
+
assert.ok(marker, "guarded split must bind an operation marker");
|
|
2804
|
+
assert.ok(receipt, "guarded split must emit an exact receipt");
|
|
2805
|
+
splitMarker = marker[1];
|
|
2806
|
+
return `${receipt[1]}\n`;
|
|
2807
|
+
}
|
|
2808
|
+
throw new Error(`unexpected tmux command: ${args.join(" ")}`);
|
|
2493
2809
|
});
|
|
2494
|
-
assert.equal(paneId,
|
|
2495
|
-
assert.
|
|
2496
|
-
"split-window",
|
|
2497
|
-
"-v",
|
|
2498
|
-
"-l",
|
|
2499
|
-
"3",
|
|
2500
|
-
"-d",
|
|
2501
|
-
"-t",
|
|
2502
|
-
"%leader",
|
|
2503
|
-
"-c",
|
|
2504
|
-
"/repo",
|
|
2505
|
-
"-P",
|
|
2506
|
-
"-F",
|
|
2507
|
-
"#{pane_id}",
|
|
2508
|
-
"node /repo/dist/cli/omx.js hud --watch",
|
|
2509
|
-
]);
|
|
2810
|
+
assert.equal(paneId, null);
|
|
2811
|
+
assert.equal(calls.some((args) => args[0] === "if-shell"), false);
|
|
2510
2812
|
});
|
|
2511
2813
|
});
|
|
2512
2814
|
describe("detached tmux new-session sequencing", () => {
|
|
@@ -2575,7 +2877,7 @@ describe("detached tmux new-session sequencing", () => {
|
|
|
2575
2877
|
assert.equal(newSession.args.includes("-e") &&
|
|
2576
2878
|
newSession.args.some((arg) => arg === "OMX_ROOT=/tmp/omx-root"), true);
|
|
2577
2879
|
});
|
|
2578
|
-
it("
|
|
2880
|
+
it("clears unverified boxed env from detached tmux sessions", () => {
|
|
2579
2881
|
const steps = buildDetachedSessionBootstrapSteps("omx-demo", "/tmp/boxed-runtime", "'codex' '--model' 'gpt-5'", "'node' '/tmp/omx.js' 'hud' '--watch'", null, undefined, null, false, "sess-detached-managed", undefined, undefined, "/tmp/boxed-runtime", {
|
|
2580
2882
|
OMXBOX_ACTIVE: "1",
|
|
2581
2883
|
OMX_SOURCE_CWD: "/tmp/source-project",
|
|
@@ -2583,11 +2885,11 @@ describe("detached tmux new-session sequencing", () => {
|
|
|
2583
2885
|
});
|
|
2584
2886
|
const newSession = steps.find((step) => step.name === "new-session");
|
|
2585
2887
|
assert.ok(newSession);
|
|
2586
|
-
assert.equal(newSession.args.some((arg) => arg === "OMX_ROOT=/tmp/boxed-runtime"),
|
|
2888
|
+
assert.equal(newSession.args.some((arg) => arg === "OMX_ROOT=/tmp/boxed-runtime"), false);
|
|
2587
2889
|
assert.equal(newSession.args.some((arg) => arg === "OMX_STATE_ROOT=/tmp/boxed-state-root"), false);
|
|
2588
2890
|
assert.equal(newSession.args.some((arg) => arg === "OMX_TMUX_HUD_OWNER=1"), true);
|
|
2589
|
-
assert.equal(newSession.args.some((arg) => arg === "OMXBOX_ACTIVE=1"),
|
|
2590
|
-
assert.equal(newSession.args.some((arg) => arg === "OMX_SOURCE_CWD=/tmp/source-project"),
|
|
2891
|
+
assert.equal(newSession.args.some((arg) => arg === "OMXBOX_ACTIVE=1"), false);
|
|
2892
|
+
assert.equal(newSession.args.some((arg) => arg === "OMX_SOURCE_CWD=/tmp/source-project"), false);
|
|
2591
2893
|
});
|
|
2592
2894
|
it("buildDetachedSessionBootstrapSteps preserves OMX_STATE_ROOT identity when no root override is explicit", () => {
|
|
2593
2895
|
const steps = buildDetachedSessionBootstrapSteps("omx-demo", "/tmp/project", "'codex' '--model' 'gpt-5'", "'node' '/tmp/omx.js' 'hud' '--watch'", null, undefined, null, false, "sess-detached-managed", undefined, undefined, "/tmp/state-root", { OMX_STATE_ROOT: "/tmp/state-root" });
|
|
@@ -2879,13 +3181,15 @@ exit 0
|
|
|
2879
3181
|
it("runCodex builds inside-tmux HUD command through explicit runtime-root resolver", async () => {
|
|
2880
3182
|
const source = await readFile(join(repoRoot, 'src', 'cli', 'index.ts'), 'utf-8');
|
|
2881
3183
|
assert.match(source, /const hudRuntimeRoot: HudRuntimeRootForLaunch = runtimeContext\s*\? \{ omxRoot: runtimeContext\.omxRoot, rootSource: 'omx-root-env' \}\s*: resolveHudRuntimeRootForLaunch\(cwd, process\.env\);/);
|
|
2882
|
-
assert.match(source, /const hudRuntimeEnv = \
|
|
2883
|
-
assert.match(source, /
|
|
3184
|
+
assert.match(source, /const hudRuntimeEnv = launchOwnedControlPlane\s*\? Object\.fromEntries\([\s\S]*?\)\s*:\s*\{\s*\.\.\.buildHudRuntimeEnv\(/);
|
|
3185
|
+
assert.match(source, /const restoreInsideTmuxControlPlane = insideTmuxControlPlane\s*\? applyLaunchOwnedControlPlane\(insideTmuxControlPlane\)/);
|
|
3186
|
+
assert.match(source, /const restoreInsideTmuxControlPlane[\s\S]*?launch = await preLaunch/);
|
|
3187
|
+
assert.match(source, /\.\.\.runtimeEnvOverlay,\s*\.\.\.launchOwnedControlPlaneEnv/);
|
|
2884
3188
|
assert.match(source, /buildTmuxPaneCommand\("env",\s*\[\.\.\.hudEnvArgs,\s*"node",\s*omxBin,\s*"hud",\s*"--watch"\]\)/);
|
|
2885
3189
|
});
|
|
2886
3190
|
it("runCodex registers a HUD resize hook immediately for inside-tmux launches", async () => {
|
|
2887
3191
|
const source = await readFile(join(repoRoot, 'src', 'cli', 'index.ts'), 'utf-8');
|
|
2888
|
-
assert.match(source, /registerInsideTmuxHudResizeHook\(\{\s*hudPaneId,\s*currentPaneId,\s*cwd,\s*sessionId,\s*omxRootOverride,\s*baseEnv: runtimeHookEnv,\s*\}\)/);
|
|
3192
|
+
assert.match(source, /registerInsideTmuxHudResizeHook\(\{\s*hudPaneId,\s*currentPaneId,\s*cwd,\s*sessionId,\s*omxRootOverride: selectedOmxRootOverride,\s*baseEnv: runtimeHookEnv,\s*\}\)/);
|
|
2889
3193
|
assert.match(source, /if \(currentPaneId\) \{\s*unregisterHudResizeHook\(currentPaneId\);\s*\}/);
|
|
2890
3194
|
});
|
|
2891
3195
|
it("buildInsideTmuxHudHookEnv tags hook commands with session, owner, leader, and local root", () => {
|
|
@@ -2944,6 +3248,59 @@ exit 0
|
|
|
2944
3248
|
assert.equal(env.OMX_ROOT, "/repo");
|
|
2945
3249
|
assert.equal(env.OMX_ENTRY_PATH, "/repo/dist/cli/omx.js");
|
|
2946
3250
|
});
|
|
3251
|
+
describe("detached HUD deferred mutation guard", () => {
|
|
3252
|
+
const leader = {
|
|
3253
|
+
paneId: "%11",
|
|
3254
|
+
panePid: 1100,
|
|
3255
|
+
sessionName: "omx-detached",
|
|
3256
|
+
sessionId: "$1",
|
|
3257
|
+
sessionCreated: "1700000000",
|
|
3258
|
+
windowId: "@1",
|
|
3259
|
+
windowIndex: "0",
|
|
3260
|
+
ownerId: "instance-1",
|
|
3261
|
+
};
|
|
3262
|
+
const hud = {
|
|
3263
|
+
paneId: "%77",
|
|
3264
|
+
panePid: 7700,
|
|
3265
|
+
sessionId: "$1",
|
|
3266
|
+
windowId: "@1",
|
|
3267
|
+
operationMarker: "operation-1",
|
|
3268
|
+
};
|
|
3269
|
+
it("throws fail-closed when a deferred HUD payload has format escapes but no resize sink", () => {
|
|
3270
|
+
const sinkFree = ["run-shell", "-b",
|
|
3271
|
+
"tmux list-panes -a -F '#{pane_id}\t#{pane_dead}\t#{pane_pid}' >/dev/null 2>&1 || true"];
|
|
3272
|
+
assert.throws(() => guardDetachedHudDeferredMutation(leader, hud, sinkFree), /detached deferred HUD mutation lacks a recognized resize sink/);
|
|
3273
|
+
});
|
|
3274
|
+
it("guards immediate and delayed resize sinks as bare tmux command-list commands", () => {
|
|
3275
|
+
const args = buildRegisterResizeHookArgs("omx-detached:0", "omx_resize_detached", "%77", 2, 7700, "instance-1");
|
|
3276
|
+
const guarded = guardDetachedHudDeferredMutation(leader, hud, args);
|
|
3277
|
+
const hookCommand = guarded.at(-1);
|
|
3278
|
+
assert.match(hookCommand, /resize-pane -t %77 -y 2 ; display-message/);
|
|
3279
|
+
assert.doesNotMatch(hookCommand, /tmux resize-pane -t %77 -y 2 ; display-message/);
|
|
3280
|
+
assert.match(hookCommand, /tmux if-shell -F/);
|
|
3281
|
+
assert.match(hookCommand, /##\{pane_id\}/);
|
|
3282
|
+
assert.equal(countMatches(hookCommand, /tmux if-shell -F/g), 2);
|
|
3283
|
+
assert.equal(countMatches(hookCommand, /run-shell -b/g), 1);
|
|
3284
|
+
});
|
|
3285
|
+
it("escapes injected authority conditions so they survive the outer run-shell format pass", () => {
|
|
3286
|
+
const args = buildRegisterResizeHookArgs("omx-detached:0", "omx_resize_detached", "%77", 2, 7700, "instance-1");
|
|
3287
|
+
const hookCommand = guardDetachedHudDeferredMutation(leader, hud, args).at(-1);
|
|
3288
|
+
// run-shell applies exactly one outer tmux format pass to the stored payload.
|
|
3289
|
+
// Unescaped, tmux pre-expands these conditions against the hook's own pane and
|
|
3290
|
+
// collapses them to constants, denying the resize before the sink is parsed.
|
|
3291
|
+
for (const condition of [
|
|
3292
|
+
"##{==:##{pane_id},%11}",
|
|
3293
|
+
"##{==:##{pane_pid},1100}",
|
|
3294
|
+
"##{==:##{@omx_instance_id},instance-1}",
|
|
3295
|
+
"##{==:##{pane_id},%77}",
|
|
3296
|
+
"##{m:*OMX_DETACHED_HUD_OPERATION=operation-1*,##{pane_start_command}}",
|
|
3297
|
+
]) {
|
|
3298
|
+
assert.ok(hookCommand.includes(condition), `stored payload must escape ${condition}`);
|
|
3299
|
+
}
|
|
3300
|
+
assert.doesNotMatch(hookCommand, /(^|[^#])#\{==:#\{pane_id\},%77\}/);
|
|
3301
|
+
assert.doesNotMatch(hookCommand, /(^|[^#])#\{&&:/);
|
|
3302
|
+
});
|
|
3303
|
+
});
|
|
2947
3304
|
it("registerDetachedHudLayoutReconcileHook reads TMUX from the detached leader pane before registering", () => {
|
|
2948
3305
|
const calls = [];
|
|
2949
3306
|
const readTargets = [];
|
|
@@ -2993,412 +3350,28 @@ exit 0
|
|
|
2993
3350
|
},
|
|
2994
3351
|
}), false);
|
|
2995
3352
|
});
|
|
2996
|
-
it("buildDetachedSessionBootstrapSteps starts native Windows
|
|
2997
|
-
const
|
|
2998
|
-
"omx.js",
|
|
2999
|
-
"hud",
|
|
3000
|
-
"--watch",
|
|
3001
|
-
]);
|
|
3002
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", "C:/project", "'codex' '--dangerously-bypass-approvals-and-sandbox'", hudCmd, "--model gpt-5", "C:/codex-home", null, true);
|
|
3003
|
-
assert.equal(steps[0]?.name, "new-session");
|
|
3004
|
-
assert.equal(steps[0]?.args.at(-1), "powershell.exe");
|
|
3005
|
-
assert.equal(steps[1]?.name, "split-and-capture-hud-pane");
|
|
3006
|
-
assert.equal(steps[1]?.args.at(-1), hudCmd);
|
|
3007
|
-
});
|
|
3008
|
-
it("buildDetachedWindowsBootstrapScript targets the resolved tmux-compatible command", () => {
|
|
3009
|
-
const script = buildDetachedWindowsBootstrapScript("omx-demo", "powershell.exe -NoLogo -NoExit -EncodedCommand abc", 2500, "C:\\Program Files\\psmux\\psmux.exe");
|
|
3010
|
-
assert.match(script, /const tmuxCommand = "C:\\\\Program Files\\\\psmux\\\\psmux\.exe";/);
|
|
3011
|
-
assert.match(script, /execFileSync\(tmuxCommand, \['send-keys'/);
|
|
3012
|
-
assert.doesNotMatch(script, /execFileSync\('tmux'/);
|
|
3013
|
-
});
|
|
3014
|
-
it("buildDetachedSessionBootstrapSteps kills detached tmux session on normal shell exit", () => {
|
|
3015
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", "/tmp/project", "'codex' '--model' 'gpt-5'", "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
3353
|
+
it("buildDetachedSessionBootstrapSteps starts native Windows with the persistent internal leader", () => {
|
|
3354
|
+
const steps = buildDetachedSessionBootstrapSteps("omx-demo", "C:/project", buildWindowsDetachedChildCommand("codex", ["--model", "gpt-5"]), "'node' 'omx.js' 'hud' '--watch'", null, undefined, null, true, "omx-session-123", "C:/project/.codex", "C:/project/.omx/runtime/codex-home/omx-session-123", undefined, process.env, undefined, undefined, undefined, "C:/project/.omx/runtime/detached-release/omx-session-123.release", "C:/project/dist/cli/omx.js");
|
|
3016
3355
|
const leaderCmd = steps[0]?.args.at(-1);
|
|
3017
3356
|
assert.equal(typeof leaderCmd, "string");
|
|
3018
|
-
assert.match(leaderCmd,
|
|
3019
|
-
assert.
|
|
3020
|
-
assert.
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
assert.match(leaderCmd, /<\&3 &/);
|
|
3026
|
-
assert.match(leaderCmd, /wait "\$omx_codex_pid";/);
|
|
3027
|
-
assert.match(leaderCmd, /kill -TERM "\$omx_codex_pid"/);
|
|
3028
|
-
assert.match(leaderCmd, /releaseTmuxExtendedKeysLease/);
|
|
3029
|
-
assert.match(leaderCmd, /if \[ "\$status" -eq 0 \]; then/);
|
|
3030
|
-
assert.match(leaderCmd, /tmux kill-session -t/);
|
|
3031
|
-
assert.match(leaderCmd, /"omx-demo"/);
|
|
3032
|
-
assert.match(leaderCmd, /codex exited immediately with code 0/);
|
|
3033
|
-
assert.match(leaderCmd, /codex exited with code/);
|
|
3034
|
-
assert.match(leaderCmd, /detached tmux session is being kept open/);
|
|
3035
|
-
assert.match(leaderCmd, /exit \$status/);
|
|
3036
|
-
});
|
|
3037
|
-
it("buildDetachedSessionBootstrapSteps finalizes postLaunch inside the detached leader when a session id is available", () => {
|
|
3038
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", "/tmp/project", "'codex' '--model' 'gpt-5'", "'node' '/tmp/omx.js' 'hud' '--watch'", null, "/tmp/codex-home", null, false, "omx-session-123", "/tmp/project/.codex-project", "/tmp/project/.omx/runtime/codex-home/omx-session-123");
|
|
3357
|
+
assert.match(leaderCmd, /powershell\.exe -NoLogo -NoProfile -NonInteractive -Command/);
|
|
3358
|
+
assert.match(leaderCmd, /__detached-session-leader/);
|
|
3359
|
+
assert.doesNotMatch(leaderCmd, /__detached-post-launch|Test-Path -LiteralPath/);
|
|
3360
|
+
});
|
|
3361
|
+
it("buildDetachedSessionBootstrapSteps gives the POSIX leader persistent lifecycle ownership", () => {
|
|
3362
|
+
const releaseMarkerPath = "/tmp/project/.omx/runtime/detached-release/omx-session-123.release";
|
|
3363
|
+
const steps = buildDetachedSessionBootstrapSteps("omx-demo", "/tmp/project", "'codex' '--model' 'gpt-5'", "'node' '/tmp/omx.js' 'hud' '--watch'", null, "/tmp/codex-home", null, false, "omx-session-123", "/tmp/project/.codex-project", "/tmp/project/.omx/runtime/codex-home/omx-session-123", undefined, process.env, undefined, undefined, undefined, releaseMarkerPath);
|
|
3039
3364
|
const leaderCmd = steps[0]?.args.at(-1);
|
|
3040
3365
|
assert.equal(typeof leaderCmd, "string");
|
|
3041
|
-
assert.match(leaderCmd, /
|
|
3042
|
-
assert.match(leaderCmd, /
|
|
3043
|
-
assert.
|
|
3044
|
-
assert.match(leaderCmd, /\/tmp\/project\/\.codex-project/);
|
|
3045
|
-
assert.match(leaderCmd, /\/tmp\/project\/\.omx\/runtime\/codex-home\/omx-session-123/);
|
|
3046
|
-
const helperIndex = leaderCmd.indexOf("runDetachedSessionPostLaunch");
|
|
3047
|
-
const signalGateIndex = leaderCmd.indexOf('if [ "$status" -eq 0 ]');
|
|
3048
|
-
assert.ok(helperIndex >= 0);
|
|
3049
|
-
assert.ok(signalGateIndex >= 0);
|
|
3050
|
-
assert.ok(helperIndex < signalGateIndex, "detached postLaunch helper must run before the signal-derived tmux kill-session gate");
|
|
3051
|
-
});
|
|
3052
|
-
it("detached leader command keeps stdin open for the Codex child", async () => {
|
|
3053
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-stdin-"));
|
|
3054
|
-
const fakeBin = join(cwd, "bin");
|
|
3055
|
-
const stdinLogPath = join(cwd, "stdin.log");
|
|
3056
|
-
try {
|
|
3057
|
-
await mkdir(fakeBin, { recursive: true });
|
|
3058
|
-
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
3059
|
-
if IFS= read -r line; then
|
|
3060
|
-
printf 'stdin:%s\n' "$line" > "${stdinLogPath}"
|
|
3061
|
-
else
|
|
3062
|
-
printf 'stdin:EOF\n' > "${stdinLogPath}"
|
|
3063
|
-
fi
|
|
3064
|
-
`);
|
|
3065
|
-
await chmod(join(fakeBin, "codex"), 0o755);
|
|
3066
|
-
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
3067
|
-
case "$1" in
|
|
3068
|
-
display-message)
|
|
3069
|
-
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
3070
|
-
printf '/tmp/tmux-test.sock\n'
|
|
3071
|
-
else
|
|
3072
|
-
printf '0\n'
|
|
3073
|
-
fi
|
|
3074
|
-
;;
|
|
3075
|
-
show-options)
|
|
3076
|
-
printf 'off\n'
|
|
3077
|
-
;;
|
|
3078
|
-
set-option|kill-session)
|
|
3079
|
-
;;
|
|
3080
|
-
esac
|
|
3081
|
-
exit 0
|
|
3082
|
-
`);
|
|
3083
|
-
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
3084
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", [], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
3085
|
-
const leaderCmd = steps[0]?.args.at(-1);
|
|
3086
|
-
assert.equal(typeof leaderCmd, "string");
|
|
3087
|
-
const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
|
|
3088
|
-
cwd,
|
|
3089
|
-
env: {
|
|
3090
|
-
...process.env,
|
|
3091
|
-
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
3092
|
-
HOME: cwd,
|
|
3093
|
-
},
|
|
3094
|
-
input: "hello from leader\n",
|
|
3095
|
-
encoding: "utf-8",
|
|
3096
|
-
});
|
|
3097
|
-
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
3098
|
-
const stdinLog = await readFile(stdinLogPath, "utf-8");
|
|
3099
|
-
assert.match(stdinLog, /stdin:hello from leader/);
|
|
3100
|
-
}
|
|
3101
|
-
finally {
|
|
3102
|
-
await rm(cwd, { recursive: true, force: true });
|
|
3103
|
-
}
|
|
3104
|
-
});
|
|
3105
|
-
it("detached leader command preserves cwd and cleanup without shell-quote breakage", async () => {
|
|
3106
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-"));
|
|
3107
|
-
const fakeBin = join(cwd, "bin");
|
|
3108
|
-
const logPath = join(cwd, "leader.log");
|
|
3109
|
-
try {
|
|
3110
|
-
await mkdir(fakeBin, { recursive: true });
|
|
3111
|
-
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
3112
|
-
printf 'codex:%s\\n' "$*" >> "${logPath}"
|
|
3113
|
-
printf 'codex-pwd:%s\\n' "$(pwd)" >> "${logPath}"
|
|
3114
|
-
printf 'codex-bridge-env:%s\\n' "\${OMX_HERMES_MCP_BRIDGE-unset}" >> "${logPath}"
|
|
3115
|
-
exit 0
|
|
3116
|
-
`);
|
|
3117
|
-
await chmod(join(fakeBin, "codex"), 0o755);
|
|
3118
|
-
await writeFile(join(cwd, ".profile"), "cd ..\n");
|
|
3119
|
-
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
3120
|
-
printf 'tmux:%s\\n' "$*" >> "${logPath}"
|
|
3121
|
-
case "$1" in
|
|
3122
|
-
display-message)
|
|
3123
|
-
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
3124
|
-
printf '/tmp/tmux-test.sock\\n'
|
|
3125
|
-
else
|
|
3126
|
-
printf '0\\n'
|
|
3127
|
-
fi
|
|
3128
|
-
;;
|
|
3129
|
-
show-options)
|
|
3130
|
-
printf 'off\\n'
|
|
3131
|
-
;;
|
|
3132
|
-
set-option|kill-session)
|
|
3133
|
-
;;
|
|
3134
|
-
esac
|
|
3135
|
-
exit 0
|
|
3136
|
-
`);
|
|
3137
|
-
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
3138
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", ["--dangerously-bypass-approvals-and-sandbox"], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
3139
|
-
const leaderCmd = steps[0]?.args.at(-1);
|
|
3140
|
-
assert.equal(typeof leaderCmd, "string");
|
|
3141
|
-
(await import("node:child_process")).execFileSync("/bin/sh", ["-c", leaderCmd], {
|
|
3142
|
-
cwd,
|
|
3143
|
-
env: {
|
|
3144
|
-
...process.env,
|
|
3145
|
-
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
3146
|
-
HOME: cwd,
|
|
3147
|
-
OMX_HERMES_MCP_BRIDGE: "1",
|
|
3148
|
-
},
|
|
3149
|
-
stdio: "ignore",
|
|
3150
|
-
});
|
|
3151
|
-
const log = await readFile(logPath, "utf-8");
|
|
3152
|
-
assert.match(log, /codex:--dangerously-bypass-approvals-and-sandbox/);
|
|
3153
|
-
assert.match(normalizeDarwinTmpPath(log), new RegExp(`codex-pwd:${escapeRegExp(normalizeDarwinTmpPath(cwd))}`));
|
|
3154
|
-
assert.match(log, /codex-bridge-env:unset/);
|
|
3155
|
-
assert.match(log, /tmux:display-message -p #\{socket_path\}/);
|
|
3156
|
-
assert.match(log, /tmux:show-options -sv extended-keys/);
|
|
3157
|
-
assert.match(log, /tmux:set-option -sq extended-keys always/);
|
|
3158
|
-
assert.match(log, /tmux:set-option -sq extended-keys off/);
|
|
3159
|
-
assert.match(log, /tmux:kill-session -t omx-demo/);
|
|
3160
|
-
}
|
|
3161
|
-
finally {
|
|
3162
|
-
await rm(cwd, { recursive: true, force: true });
|
|
3163
|
-
}
|
|
3164
|
-
});
|
|
3165
|
-
it("detached leader command preserves the detached tmux session on signal-derived exits", async () => {
|
|
3166
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-signal-"));
|
|
3167
|
-
const fakeBin = join(cwd, "bin");
|
|
3168
|
-
const logPath = join(cwd, "leader.log");
|
|
3169
|
-
try {
|
|
3170
|
-
await mkdir(fakeBin, { recursive: true });
|
|
3171
|
-
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
3172
|
-
printf 'codex:%s\\n' "$*" >> "${logPath}"
|
|
3173
|
-
exit 143
|
|
3174
|
-
`);
|
|
3175
|
-
await chmod(join(fakeBin, "codex"), 0o755);
|
|
3176
|
-
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
3177
|
-
printf 'tmux:%s\\n' "$*" >> "${logPath}"
|
|
3178
|
-
case "$1" in
|
|
3179
|
-
display-message)
|
|
3180
|
-
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
3181
|
-
printf '/tmp/tmux-test.sock\\n'
|
|
3182
|
-
else
|
|
3183
|
-
printf '0\\n'
|
|
3184
|
-
fi
|
|
3185
|
-
;;
|
|
3186
|
-
show-options)
|
|
3187
|
-
printf 'off\\n'
|
|
3188
|
-
;;
|
|
3189
|
-
set-option|kill-session)
|
|
3190
|
-
;;
|
|
3191
|
-
esac
|
|
3192
|
-
exit 0
|
|
3193
|
-
`);
|
|
3194
|
-
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
3195
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", ["--dangerously-bypass-approvals-and-sandbox"], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
3196
|
-
const leaderCmd = steps[0]?.args.at(-1);
|
|
3197
|
-
assert.equal(typeof leaderCmd, "string");
|
|
3198
|
-
const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
|
|
3199
|
-
cwd,
|
|
3200
|
-
env: {
|
|
3201
|
-
...process.env,
|
|
3202
|
-
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
3203
|
-
HOME: cwd,
|
|
3204
|
-
},
|
|
3205
|
-
encoding: "utf-8",
|
|
3206
|
-
});
|
|
3207
|
-
assert.equal(result.status, 143);
|
|
3208
|
-
const log = await readFile(logPath, "utf-8");
|
|
3209
|
-
assert.match(log, /codex:--dangerously-bypass-approvals-and-sandbox/);
|
|
3210
|
-
assert.match(log, /tmux:display-message -p #\{socket_path\}/);
|
|
3211
|
-
assert.match(log, /tmux:show-options -sv extended-keys/);
|
|
3212
|
-
assert.match(log, /tmux:set-option -sq extended-keys always/);
|
|
3213
|
-
assert.match(log, /tmux:set-option -sq extended-keys off/);
|
|
3214
|
-
assert.doesNotMatch(log, /tmux:kill-session -t omx-demo/);
|
|
3215
|
-
}
|
|
3216
|
-
finally {
|
|
3217
|
-
await rm(cwd, { recursive: true, force: true });
|
|
3218
|
-
}
|
|
3219
|
-
});
|
|
3220
|
-
it("detached leader command keeps child startup errors visible instead of killing the session", async () => {
|
|
3221
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-error-"));
|
|
3222
|
-
const fakeBin = join(cwd, "bin");
|
|
3223
|
-
const logPath = join(cwd, "leader.log");
|
|
3224
|
-
try {
|
|
3225
|
-
await mkdir(fakeBin, { recursive: true });
|
|
3226
|
-
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
3227
|
-
printf 'codex-stderr: unsupported startup flag\\n' >&2
|
|
3228
|
-
exit 42
|
|
3229
|
-
`);
|
|
3230
|
-
await chmod(join(fakeBin, "codex"), 0o755);
|
|
3231
|
-
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
3232
|
-
printf 'tmux:%s\\n' "$*" >> "${logPath}"
|
|
3233
|
-
case "$1" in
|
|
3234
|
-
display-message)
|
|
3235
|
-
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
3236
|
-
printf '/tmp/tmux-test.sock\\n'
|
|
3237
|
-
else
|
|
3238
|
-
printf '0\\n'
|
|
3239
|
-
fi
|
|
3240
|
-
;;
|
|
3241
|
-
show-options)
|
|
3242
|
-
printf 'off\\n'
|
|
3243
|
-
;;
|
|
3244
|
-
set-option|kill-session)
|
|
3245
|
-
;;
|
|
3246
|
-
esac
|
|
3247
|
-
exit 0
|
|
3248
|
-
`);
|
|
3249
|
-
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
3250
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", ["--bad-startup-flag"], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
3251
|
-
const leaderCmd = steps[0]?.args.at(-1);
|
|
3252
|
-
assert.equal(typeof leaderCmd, "string");
|
|
3253
|
-
const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
|
|
3254
|
-
cwd,
|
|
3255
|
-
env: {
|
|
3256
|
-
...process.env,
|
|
3257
|
-
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
3258
|
-
HOME: cwd,
|
|
3259
|
-
},
|
|
3260
|
-
input: "\n",
|
|
3261
|
-
encoding: "utf-8",
|
|
3262
|
-
});
|
|
3263
|
-
assert.equal(result.status, 42);
|
|
3264
|
-
assert.match(result.stderr, /codex-stderr: unsupported startup flag/);
|
|
3265
|
-
assert.match(result.stderr, /codex exited with code 42 during startup/);
|
|
3266
|
-
assert.match(result.stderr, /detached tmux session is being kept open/);
|
|
3267
|
-
const log = await readFile(logPath, "utf-8");
|
|
3268
|
-
assert.doesNotMatch(log, /tmux:kill-session -t omx-demo/);
|
|
3269
|
-
}
|
|
3270
|
-
finally {
|
|
3271
|
-
await rm(cwd, { recursive: true, force: true });
|
|
3272
|
-
}
|
|
3273
|
-
});
|
|
3274
|
-
it("detached leader command keeps immediate zero-code exits visible instead of silently closing", async () => {
|
|
3275
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-zero-"));
|
|
3276
|
-
const fakeBin = join(cwd, "bin");
|
|
3277
|
-
const logPath = join(cwd, "leader.log");
|
|
3278
|
-
try {
|
|
3279
|
-
await mkdir(fakeBin, { recursive: true });
|
|
3280
|
-
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
3281
|
-
printf 'codex-started-then-quit\\n' >&2
|
|
3282
|
-
exit 0
|
|
3283
|
-
`);
|
|
3284
|
-
await chmod(join(fakeBin, "codex"), 0o755);
|
|
3285
|
-
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
3286
|
-
printf 'tmux:%s\\n' "$*" >> "${logPath}"
|
|
3287
|
-
case "$1" in
|
|
3288
|
-
display-message)
|
|
3289
|
-
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
3290
|
-
printf '/tmp/tmux-test.sock\\n'
|
|
3291
|
-
else
|
|
3292
|
-
printf '0\\n'
|
|
3293
|
-
fi
|
|
3294
|
-
;;
|
|
3295
|
-
show-options)
|
|
3296
|
-
printf 'off\\n'
|
|
3297
|
-
;;
|
|
3298
|
-
set-option|kill-session)
|
|
3299
|
-
;;
|
|
3300
|
-
esac
|
|
3301
|
-
exit 0
|
|
3302
|
-
`);
|
|
3303
|
-
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
3304
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", [], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
3305
|
-
const leaderCmd = steps[0]?.args.at(-1);
|
|
3306
|
-
assert.equal(typeof leaderCmd, "string");
|
|
3307
|
-
const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
|
|
3308
|
-
cwd,
|
|
3309
|
-
env: {
|
|
3310
|
-
...process.env,
|
|
3311
|
-
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
3312
|
-
HOME: cwd,
|
|
3313
|
-
},
|
|
3314
|
-
input: "\n",
|
|
3315
|
-
encoding: "utf-8",
|
|
3316
|
-
});
|
|
3317
|
-
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
3318
|
-
assert.match(result.stderr, /codex-started-then-quit/);
|
|
3319
|
-
assert.match(result.stderr, /codex exited immediately with code 0 during startup/);
|
|
3320
|
-
assert.match(result.stderr, /detached tmux session is being kept open/);
|
|
3321
|
-
const log = await readFile(logPath, "utf-8");
|
|
3322
|
-
assert.match(log, /tmux:kill-session -t omx-demo/);
|
|
3323
|
-
}
|
|
3324
|
-
finally {
|
|
3325
|
-
await rm(cwd, { recursive: true, force: true });
|
|
3326
|
-
}
|
|
3366
|
+
assert.match(leaderCmd, /^\/bin\/sh -c '/);
|
|
3367
|
+
assert.match(leaderCmd, /__detached-session-leader/);
|
|
3368
|
+
assert.doesNotMatch(leaderCmd, /__detached-post-launch|omx_codex_pid|while \[ ! -f/);
|
|
3327
3369
|
});
|
|
3328
|
-
it("
|
|
3329
|
-
const
|
|
3330
|
-
const
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
await mkdir(fakeBin, { recursive: true });
|
|
3334
|
-
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
3335
|
-
echo $$ > "${pidFile}"
|
|
3336
|
-
trap '' HUP
|
|
3337
|
-
while true; do sleep 1; done
|
|
3338
|
-
`);
|
|
3339
|
-
await chmod(join(fakeBin, "codex"), 0o755);
|
|
3340
|
-
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
3341
|
-
case "$1" in
|
|
3342
|
-
display-message)
|
|
3343
|
-
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
3344
|
-
printf '/tmp/tmux-test.sock\\n'
|
|
3345
|
-
else
|
|
3346
|
-
printf '0\\n'
|
|
3347
|
-
fi
|
|
3348
|
-
;;
|
|
3349
|
-
show-options) printf 'off\\n' ;;
|
|
3350
|
-
set-option|kill-session) ;;
|
|
3351
|
-
esac
|
|
3352
|
-
exit 0
|
|
3353
|
-
`);
|
|
3354
|
-
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
3355
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", [], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
3356
|
-
const leaderCmd = steps[0]?.args.at(-1);
|
|
3357
|
-
assert.equal(typeof leaderCmd, "string");
|
|
3358
|
-
const { spawn } = await import("node:child_process");
|
|
3359
|
-
const child = spawn("/bin/sh", ["-c", `exec ${leaderCmd}`], {
|
|
3360
|
-
cwd,
|
|
3361
|
-
env: {
|
|
3362
|
-
...process.env,
|
|
3363
|
-
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
3364
|
-
HOME: cwd,
|
|
3365
|
-
},
|
|
3366
|
-
stdio: "ignore",
|
|
3367
|
-
detached: true,
|
|
3368
|
-
});
|
|
3369
|
-
try {
|
|
3370
|
-
for (let i = 0; i < 50; i += 1) {
|
|
3371
|
-
if (existsSync(pidFile))
|
|
3372
|
-
break;
|
|
3373
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
3374
|
-
}
|
|
3375
|
-
assert.ok(existsSync(pidFile), "codex pid file not written");
|
|
3376
|
-
const codexPid = Number.parseInt((await readFile(pidFile, "utf-8")).trim(), 10);
|
|
3377
|
-
assert.ok(codexPid > 0, "codex pid must be positive");
|
|
3378
|
-
assert.doesNotThrow(() => process.kill(codexPid, 0), "codex must be alive before signal");
|
|
3379
|
-
const leaderExit = once(child, "exit");
|
|
3380
|
-
process.kill(child.pid, "SIGHUP");
|
|
3381
|
-
await Promise.race([
|
|
3382
|
-
leaderExit,
|
|
3383
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("leader did not exit after SIGHUP")), 3000)),
|
|
3384
|
-
]);
|
|
3385
|
-
assert.throws(() => process.kill(codexPid, 0), (err) => typeof err === "object" &&
|
|
3386
|
-
err !== null &&
|
|
3387
|
-
"code" in err &&
|
|
3388
|
-
err.code === "ESRCH", "codex child must be terminated after leader SIGHUP");
|
|
3389
|
-
}
|
|
3390
|
-
finally {
|
|
3391
|
-
try {
|
|
3392
|
-
process.kill(child.pid, "SIGKILL");
|
|
3393
|
-
}
|
|
3394
|
-
catch {
|
|
3395
|
-
/* already dead */
|
|
3396
|
-
}
|
|
3397
|
-
}
|
|
3398
|
-
}
|
|
3399
|
-
finally {
|
|
3400
|
-
await rm(cwd, { recursive: true, force: true });
|
|
3401
|
-
}
|
|
3370
|
+
it("keeps capability data out of the detached leader command payload", () => {
|
|
3371
|
+
const steps = buildDetachedSessionBootstrapSteps("omx-demo", "/tmp/project", "codex", "hud", null, undefined, null, false, "omx-session-123", undefined, undefined, undefined, process.env);
|
|
3372
|
+
const leaderCmd = steps[0]?.args.at(-1) ?? "";
|
|
3373
|
+
assert.match(leaderCmd, /__detached-session-leader/);
|
|
3374
|
+
assert.doesNotMatch(leaderCmd, /launch_lineage_token|directoryIdentity|FileHandle/);
|
|
3402
3375
|
});
|
|
3403
3376
|
it("withTmuxExtendedKeys enables tmux extended keys during codex launch and restores them afterwards", async () => {
|
|
3404
3377
|
const cwd = await mkdtemp(join(tmpdir(), "omx-tmux-lease-wrapper-"));
|
|
@@ -3953,6 +3926,45 @@ describe("buildWindowsPromptCommand", () => {
|
|
|
3953
3926
|
assert.equal(decoded, "$ErrorActionPreference = 'Stop'; & { & 'codex' '--dangerously-bypass-approvals-and-sandbox' '-c' 'model_reasoning_effort=\"high\"' 'it''s' }");
|
|
3954
3927
|
});
|
|
3955
3928
|
});
|
|
3929
|
+
describe("buildWindowsDetachedChildCommand", () => {
|
|
3930
|
+
it("exits with the Codex child status instead of retaining an interactive shell", () => {
|
|
3931
|
+
const result = buildWindowsDetachedChildCommand("codex", ["--model", "gpt-5"]);
|
|
3932
|
+
const prefix = "powershell.exe -NoLogo -NoProfile -NonInteractive -EncodedCommand ";
|
|
3933
|
+
assert.ok(result.startsWith(prefix));
|
|
3934
|
+
assert.doesNotMatch(result, /-NoExit/);
|
|
3935
|
+
assert.equal(Buffer.from(result.slice(prefix.length), "base64").toString("utf16le"), "$ErrorActionPreference = 'Stop'; & 'codex' '--model' 'gpt-5'; exit $LASTEXITCODE");
|
|
3936
|
+
});
|
|
3937
|
+
});
|
|
3938
|
+
describe("Windows detached leader environment", () => {
|
|
3939
|
+
it("serializes inherited parent values into the detached Windows leader command", () => {
|
|
3940
|
+
const steps = buildDetachedSessionBootstrapSteps("omx-test", "C:/project", "codex", "hud", null, undefined, null, true, "session-1", undefined, undefined, undefined, {
|
|
3941
|
+
PATH: "C:/runtime-shim;C:/parent/bin",
|
|
3942
|
+
CUSTOM_VALUE: "retained",
|
|
3943
|
+
CODEX_HOME: "C:/project/.codex-runtime",
|
|
3944
|
+
CODEX_SQLITE_HOME: "C:/project/.codex-sqlite",
|
|
3945
|
+
OMX_ROOT: "C:/project/.omx-run",
|
|
3946
|
+
OMX_CODEX_LAUNCH_ID: "launch-id",
|
|
3947
|
+
OMX_NOTIFY_TEMP_CONTRACT: "{\"active\":true}",
|
|
3948
|
+
OMX_TEAM_WORKER_LAUNCH_ARGS: "[\"--model\",\"gpt-5\"]",
|
|
3949
|
+
TMUX: "foreign",
|
|
3950
|
+
TMUX_PANE: "%9",
|
|
3951
|
+
TERM: "xterm-256color",
|
|
3952
|
+
});
|
|
3953
|
+
const command = steps[0]?.args.at(-1) ?? "";
|
|
3954
|
+
const encodedPayload = command.match(/__detached-session-leader ''([^']+)''/)?.[1];
|
|
3955
|
+
assert.ok(encodedPayload);
|
|
3956
|
+
const payload = JSON.parse(Buffer.from(encodedPayload, "base64url").toString("utf8"));
|
|
3957
|
+
assert.deepEqual(payload.parentEnv, {
|
|
3958
|
+
CODEX_HOME: "C:/project/.codex-runtime",
|
|
3959
|
+
CODEX_SQLITE_HOME: "C:/project/.codex-sqlite",
|
|
3960
|
+
CUSTOM_VALUE: "retained",
|
|
3961
|
+
OMX_CODEX_LAUNCH_ID: "launch-id",
|
|
3962
|
+
OMX_NOTIFY_TEMP_CONTRACT: "{\"active\":true}",
|
|
3963
|
+
OMX_TEAM_WORKER_LAUNCH_ARGS: "[\"--model\",\"gpt-5\"]",
|
|
3964
|
+
PATH: "C:/runtime-shim;C:/parent/bin",
|
|
3965
|
+
});
|
|
3966
|
+
});
|
|
3967
|
+
});
|
|
3956
3968
|
describe("buildTmuxSessionName", () => {
|
|
3957
3969
|
it("uses detached fallback quietly outside git repos", () => {
|
|
3958
3970
|
const name = buildTmuxSessionName("/tmp/My Repo", "omx-1770992424158-abc123");
|
|
@@ -4256,4 +4268,153 @@ describe("isExistingTmuxWindowTooCrampedForLaunchHud (#2754)", () => {
|
|
|
4256
4268
|
assert.equal(isExistingTmuxWindowTooCrampedForLaunchHud(39, 40), true);
|
|
4257
4269
|
});
|
|
4258
4270
|
});
|
|
4271
|
+
describe("detached control-plane binding", () => {
|
|
4272
|
+
it("bounds inside-tmux lifecycle propagation to the finite launch-owned control plane", async () => {
|
|
4273
|
+
const source = await readFile(join(repoRoot, "src", "cli", "index.ts"), "utf8");
|
|
4274
|
+
assert.match(source, /if \(launchPolicy === "inside-tmux"\) \{[\s\S]*?buildDetachedLaunchControlPlane\(/);
|
|
4275
|
+
assert.match(source, /const restoreInsideTmuxControlPlane = insideTmuxControlPlane\s*\? applyLaunchOwnedControlPlane\(insideTmuxControlPlane\)/);
|
|
4276
|
+
assert.match(source, /const codexBaseEnv = prependOmxRuntimeCommandShimToEnv\([\s\S]*?\.\.\.launchOwnedControlPlaneEnv/);
|
|
4277
|
+
assert.match(source, /const runtimeHookEnv = launchOwnedControlPlane\s*\?[\s\S]*?\.\.\.launchOwnedControlPlaneEnv/);
|
|
4278
|
+
});
|
|
4279
|
+
it("emits one stable assignment for every managed key and clears poisoned identity", () => {
|
|
4280
|
+
const env = {
|
|
4281
|
+
OMX_ROOT: "",
|
|
4282
|
+
OMX_STATE_ROOT: "",
|
|
4283
|
+
OMX_TEAM_STATE_ROOT: "",
|
|
4284
|
+
OMX_RUNS_DIR: "/foreign/runs",
|
|
4285
|
+
OMX_TEAM_LEADER_CWD: "/foreign",
|
|
4286
|
+
OMX_TEAM_WORKER: "foreign/worker-1",
|
|
4287
|
+
OMX_TEAM_INTERNAL_WORKER: "foreign/worker-1",
|
|
4288
|
+
};
|
|
4289
|
+
const controlPlane = buildDetachedLaunchControlPlane({ cwd: "/tmp/project", sessionId: "sess-binding", env });
|
|
4290
|
+
assert.deepEqual([...controlPlane.keys], [...DETACHED_LAUNCH_CONTROL_PLANE_KEYS]);
|
|
4291
|
+
assert.deepEqual(Object.keys(controlPlane.values), [...DETACHED_LAUNCH_CONTROL_PLANE_KEYS]);
|
|
4292
|
+
assert.equal(controlPlane.values.OMX_SESSION_ID, "sess-binding");
|
|
4293
|
+
assert.equal(controlPlane.values.OMX_TMUX_HUD_OWNER, "1");
|
|
4294
|
+
for (const key of [
|
|
4295
|
+
"CODEX_SESSION_ID", "SESSION_ID", "OMX_TMUX_HUD_LEADER_PANE", "OMX_ROOT", "OMX_STATE_ROOT",
|
|
4296
|
+
"OMX_TEAM_STATE_ROOT", "OMXBOX_ACTIVE", "OMX_SOURCE_CWD", "OMX_MADMAX_DETACHED_CONTEXT", "OMX_RUNS_DIR",
|
|
4297
|
+
"OMX_TEAM_LEADER_CWD", "OMX_TEAM_WORKER", "OMX_TEAM_INTERNAL_WORKER",
|
|
4298
|
+
])
|
|
4299
|
+
assert.equal(controlPlane.values[key], "", `${key} must be cleared`);
|
|
4300
|
+
const steps = buildDetachedSessionBootstrapSteps("omx-binding", "/tmp/project", "codex", "hud", null, undefined, null, false, "sess-binding", undefined, undefined, undefined, env, undefined, undefined, undefined, undefined, undefined, undefined, controlPlane);
|
|
4301
|
+
const newSession = steps.find((step) => step.name === "new-session");
|
|
4302
|
+
assert.ok(newSession);
|
|
4303
|
+
const managedAssignments = newSession.args.filter((arg) => DETACHED_LAUNCH_CONTROL_PLANE_KEYS.some((key) => arg.startsWith(`${key}=`)));
|
|
4304
|
+
assert.equal(managedAssignments.length, DETACHED_LAUNCH_CONTROL_PLANE_KEYS.length);
|
|
4305
|
+
assert.deepEqual(managedAssignments.map((assignment) => assignment.slice(0, assignment.indexOf("="))), [...DETACHED_LAUNCH_CONTROL_PLANE_KEYS]);
|
|
4306
|
+
});
|
|
4307
|
+
it("preserves selected explicit roots and valid Madmax runs identity", () => {
|
|
4308
|
+
const explicit = buildDetachedLaunchControlPlane({
|
|
4309
|
+
cwd: "/tmp/project", sessionId: "sess-explicit",
|
|
4310
|
+
env: { OMX_ROOT: "/tmp/selected-root", OMX_STATE_ROOT: "/tmp/stale-state" },
|
|
4311
|
+
});
|
|
4312
|
+
assert.equal(explicit.values.OMX_ROOT, "/tmp/selected-root");
|
|
4313
|
+
assert.equal(explicit.values.OMX_STATE_ROOT, "");
|
|
4314
|
+
assert.equal(explicit.values.OMX_RUNS_DIR, "");
|
|
4315
|
+
const madmax = buildDetachedLaunchControlPlane({
|
|
4316
|
+
cwd: "/tmp/project", sessionId: "sess-madmax",
|
|
4317
|
+
omxRootOverride: "/tmp/run-root",
|
|
4318
|
+
verifiedMadmaxContext: {
|
|
4319
|
+
root: "/tmp/run-root",
|
|
4320
|
+
sourceCwd: "/tmp/project",
|
|
4321
|
+
context: "ctx-madmax",
|
|
4322
|
+
runsRoot: "/tmp/outer-runs",
|
|
4323
|
+
},
|
|
4324
|
+
env: {
|
|
4325
|
+
OMX_ROOT: "/tmp/run-root", OMXBOX_ACTIVE: "1", OMX_SOURCE_CWD: "/tmp/project",
|
|
4326
|
+
OMX_MADMAX_DETACHED_CONTEXT: "ctx-madmax", OMX_RUNS_DIR: "/tmp/outer-runs",
|
|
4327
|
+
},
|
|
4328
|
+
});
|
|
4329
|
+
assert.deepEqual([madmax.values.OMX_ROOT, madmax.values.OMXBOX_ACTIVE, madmax.values.OMX_SOURCE_CWD,
|
|
4330
|
+
madmax.values.OMX_MADMAX_DETACHED_CONTEXT, madmax.values.OMX_RUNS_DIR], ["/tmp/run-root", "1", "/tmp/project", "ctx-madmax", "/tmp/outer-runs"]);
|
|
4331
|
+
const poisoned = buildDetachedLaunchControlPlane({
|
|
4332
|
+
cwd: "/tmp/project", sessionId: "sess-poisoned", omxRootOverride: "/foreign/root",
|
|
4333
|
+
env: {
|
|
4334
|
+
OMX_ROOT: "/foreign/root", OMXBOX_ACTIVE: "1", OMX_SOURCE_CWD: "/foreign/source",
|
|
4335
|
+
OMX_MADMAX_DETACHED_CONTEXT: "poisoned-context", OMX_RUNS_DIR: "/foreign/runs",
|
|
4336
|
+
},
|
|
4337
|
+
});
|
|
4338
|
+
assert.deepEqual([poisoned.values.OMX_ROOT, poisoned.values.OMX_SOURCE_CWD,
|
|
4339
|
+
poisoned.values.OMX_MADMAX_DETACHED_CONTEXT, poisoned.values.OMX_RUNS_DIR], ["", "", "", ""]);
|
|
4340
|
+
});
|
|
4341
|
+
it("preserves an explicit team root while clearing unverified boxed lineage and worker tuple", () => {
|
|
4342
|
+
const cases = [
|
|
4343
|
+
{
|
|
4344
|
+
name: "boxed guard without worker tuple",
|
|
4345
|
+
env: {
|
|
4346
|
+
OMX_TEAM_STATE_ROOT: "/tmp/explicit-team-root",
|
|
4347
|
+
OMX_ROOT: "/tmp/poison-root",
|
|
4348
|
+
OMX_STATE_ROOT: "/tmp/poison-state-root",
|
|
4349
|
+
OMXBOX_ACTIVE: "1",
|
|
4350
|
+
OMX_SOURCE_CWD: "/tmp/poison-source",
|
|
4351
|
+
OMX_MADMAX_DETACHED_CONTEXT: "poison-context",
|
|
4352
|
+
OMX_RUNS_DIR: "/tmp/poison-runs",
|
|
4353
|
+
},
|
|
4354
|
+
},
|
|
4355
|
+
{
|
|
4356
|
+
name: "boxed guard with poisoned worker tuple",
|
|
4357
|
+
env: {
|
|
4358
|
+
OMX_TEAM_STATE_ROOT: "/tmp/explicit-team-root",
|
|
4359
|
+
OMX_ROOT: "/tmp/poison-root",
|
|
4360
|
+
OMX_STATE_ROOT: "/tmp/poison-state-root",
|
|
4361
|
+
OMXBOX_ACTIVE: "1",
|
|
4362
|
+
OMX_SOURCE_CWD: "/tmp/poison-source",
|
|
4363
|
+
OMX_MADMAX_DETACHED_CONTEXT: "poison-context",
|
|
4364
|
+
OMX_RUNS_DIR: "/tmp/poison-runs",
|
|
4365
|
+
OMX_TEAM_LEADER_CWD: "/tmp/poison-leader",
|
|
4366
|
+
OMX_TEAM_WORKER: "poison-team/worker-1",
|
|
4367
|
+
OMX_TEAM_INTERNAL_WORKER: "poison-team/worker-2",
|
|
4368
|
+
},
|
|
4369
|
+
},
|
|
4370
|
+
];
|
|
4371
|
+
for (const testCase of cases) {
|
|
4372
|
+
const controlPlane = buildDetachedLaunchControlPlane({
|
|
4373
|
+
cwd: "/tmp/project",
|
|
4374
|
+
sessionId: `sess-${testCase.name.replaceAll(" ", "-")}`,
|
|
4375
|
+
env: testCase.env,
|
|
4376
|
+
});
|
|
4377
|
+
assert.equal(controlPlane.values.OMX_TEAM_STATE_ROOT, "/tmp/explicit-team-root", testCase.name);
|
|
4378
|
+
for (const key of [
|
|
4379
|
+
"OMX_ROOT", "OMX_STATE_ROOT", "OMXBOX_ACTIVE", "OMX_SOURCE_CWD",
|
|
4380
|
+
"OMX_MADMAX_DETACHED_CONTEXT", "OMX_RUNS_DIR", "OMX_TEAM_LEADER_CWD",
|
|
4381
|
+
"OMX_TEAM_WORKER", "OMX_TEAM_INTERNAL_WORKER",
|
|
4382
|
+
]) {
|
|
4383
|
+
assert.equal(controlPlane.values[key], "", `${testCase.name}: ${key} must be cleared`);
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
});
|
|
4387
|
+
it("keeps unrelated parent configuration while filtering exact managed replay names", () => {
|
|
4388
|
+
const serialized = serializeDetachedSessionParentEnv({
|
|
4389
|
+
OMX_ROOT: "/foreign/root", omx_root: "/foreign/mixed-case-root",
|
|
4390
|
+
OMX_TEAM_WORKER: "foreign/worker", Omx_Notify_Fallback: "1",
|
|
4391
|
+
PROVIDER_SENTINEL: "provider-value", Path: "/system/path",
|
|
4392
|
+
});
|
|
4393
|
+
assert.doesNotMatch(serialized, /export OMX_ROOT=/);
|
|
4394
|
+
assert.doesNotMatch(serialized, /export OMX_TEAM_WORKER=/);
|
|
4395
|
+
assert.match(serialized, /export omx_root='/);
|
|
4396
|
+
assert.match(serialized, /export PROVIDER_SENTINEL='provider-value'/);
|
|
4397
|
+
assert.match(serialized, /export Path='\/system\/path'/);
|
|
4398
|
+
assert.match(serialized, /export Omx_Notify_Fallback='1'/);
|
|
4399
|
+
});
|
|
4400
|
+
it("filters every managed Windows case variant while preserving unrelated configuration", () => {
|
|
4401
|
+
const env = {
|
|
4402
|
+
oMx_RoOt: "/foreign/root",
|
|
4403
|
+
OmX_TeAm_WorkEr: "foreign/worker",
|
|
4404
|
+
OMX_PROVIDER_URL: "https://provider.invalid",
|
|
4405
|
+
Path: "/system/path",
|
|
4406
|
+
Omx_Notify_Fallback: "1",
|
|
4407
|
+
};
|
|
4408
|
+
const steps = buildDetachedSessionBootstrapSteps("omx-windows-binding", "/tmp/project", "codex", "hud", null, undefined, null, true, "sess-windows", undefined, undefined, undefined, env);
|
|
4409
|
+
const command = steps[0]?.args.at(-1) ?? "";
|
|
4410
|
+
const encoded = /__detached-session-leader [^A-Za-z0-9_-]*([A-Za-z0-9_-]{40,})/.exec(command)?.[1];
|
|
4411
|
+
assert.ok(encoded);
|
|
4412
|
+
const payload = JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
|
|
4413
|
+
assert.equal(payload.parentEnv?.oMx_RoOt, undefined);
|
|
4414
|
+
assert.equal(payload.parentEnv?.OmX_TeAm_WorkEr, undefined);
|
|
4415
|
+
assert.equal(payload.parentEnv?.OMX_PROVIDER_URL, "https://provider.invalid");
|
|
4416
|
+
assert.equal(payload.parentEnv?.Path, "/system/path");
|
|
4417
|
+
assert.equal(payload.parentEnv?.Omx_Notify_Fallback, "1");
|
|
4418
|
+
});
|
|
4419
|
+
});
|
|
4259
4420
|
//# sourceMappingURL=index.test.js.map
|