oh-my-codex 0.20.2 → 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 +36 -18
- package/Cargo.toml +1 -1
- package/README.md +28 -2
- package/crates/omx-runtime/Cargo.toml +4 -0
- package/crates/omx-runtime/src/main.rs +415 -2
- package/crates/omx-runtime/tests/execution.rs +483 -0
- package/crates/omx-runtime-core/src/dispatch.rs +96 -10
- package/crates/omx-runtime-core/src/engine.rs +839 -29
- package/crates/omx-runtime-core/src/lib.rs +8 -0
- package/dist/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/agents/__tests__/definitions.test.js +81 -0
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +25 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/native-config.d.ts +2 -1
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js.map +1 -1
- package/dist/auth/__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 +22 -2
- package/dist/auth/hotswap.d.ts.map +1 -1
- package/dist/auth/hotswap.js +107 -22
- 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 +63 -7
- 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 +150 -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 +135 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/exec.test.js +211 -0
- package/dist/cli/__tests__/exec.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +878 -463
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +1290 -127
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +8 -4
- 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 +132 -6
- package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +3 -3
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +3 -3
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +49 -164
- 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 +107 -109
- package/dist/cli/__tests__/ralplan.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +11 -10
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.js +1118 -43
- package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
- package/dist/cli/__tests__/session-search-help.test.js +25 -1
- package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
- package/dist/cli/__tests__/setup-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 +224 -7
- 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 +150 -8
- 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 +95 -4
- 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 +33 -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 +20 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +290 -3
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +234 -12
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2409 -746
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-parity.js +2 -2
- package/dist/cli/mcp-parity.js.map +1 -1
- package/dist/cli/native-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 +20 -5
- package/dist/cli/native-hook-claim-journal.d.ts.map +1 -1
- package/dist/cli/native-hook-claim-journal.js +67 -32
- 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 +5 -7
- package/dist/cli/ralplan.d.ts.map +1 -1
- package/dist/cli/ralplan.js +83 -134
- package/dist/cli/ralplan.js.map +1 -1
- package/dist/cli/session-search.d.ts +5 -0
- package/dist/cli/session-search.d.ts.map +1 -1
- package/dist/cli/session-search.js +87 -0
- package/dist/cli/session-search.js.map +1 -1
- package/dist/cli/setup-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 +8 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +119 -47
- 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 +9 -0
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +71 -30
- 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/compat/__tests__/rust-runtime-compat.test.js +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +503 -30
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +5 -3
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +67 -3
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/generator.d.ts +27 -21
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +610 -152
- package/dist/config/generator.js.map +1 -1
- package/dist/config/models.d.ts +12 -2
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +15 -1
- package/dist/config/models.js.map +1 -1
- package/dist/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 +5 -3
- 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 +176 -37
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +402 -545
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +39 -4
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +161 -26
- 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 +533 -14
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +159 -24
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js +482 -32
- package/dist/hooks/__tests__/notify-hook-team-tmux-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-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-tmux-heal.test.js +68 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js +7 -1
- package/dist/hooks/__tests__/notify-hook-tmux-scrollback.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +44 -4
- 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 +3130 -365
- 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 +204 -14
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +1500 -223
- 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 +124 -37
- package/dist/leader/__tests__/contract.test.js.map +1 -1
- package/dist/leader/contract.d.ts +17 -6
- package/dist/leader/contract.d.ts.map +1 -1
- package/dist/leader/contract.js +102 -26
- package/dist/leader/contract.js.map +1 -1
- package/dist/mcp/__tests__/code-intel-server.test.js +31 -3
- package/dist/mcp/__tests__/code-intel-server.test.js.map +1 -1
- package/dist/mcp/__tests__/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/__tests__/wiki-server.test.js +18 -0
- package/dist/mcp/__tests__/wiki-server.test.js.map +1 -1
- package/dist/mcp/code-intel-server.d.ts.map +1 -1
- package/dist/mcp/code-intel-server.js +13 -3
- package/dist/mcp/code-intel-server.js.map +1 -1
- package/dist/mcp/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/mcp/wiki-server.d.ts +11 -0
- package/dist/mcp/wiki-server.d.ts.map +1 -1
- package/dist/mcp/wiki-server.js +9 -2
- package/dist/mcp/wiki-server.js.map +1 -1
- package/dist/modes/__tests__/base-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/__tests__/base-tmux-pane.test.js +4 -4
- package/dist/modes/__tests__/base-tmux-pane.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/notifications/temp-contract.d.ts.map +1 -1
- package/dist/notifications/temp-contract.js +4 -0
- package/dist/notifications/temp-contract.js.map +1 -1
- package/dist/pipeline/__tests__/orchestrator.test.js +164 -42
- package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +72 -155
- 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 +73 -1316
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js +335 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
- package/dist/ralplan/__tests__/runtime.test.js +85 -101
- 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 +82 -338
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/ralplan/documented-leader-preflight.d.ts +41 -0
- package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
- package/dist/ralplan/documented-leader-preflight.js +360 -0
- package/dist/ralplan/documented-leader-preflight.js.map +1 -0
- package/dist/ralplan/runtime.d.ts +2 -1
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +67 -168
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/runtime/__tests__/bridge.test.js +42 -1
- package/dist/runtime/__tests__/bridge.test.js.map +1 -1
- package/dist/runtime/bridge.d.ts +12 -0
- package/dist/runtime/bridge.d.ts.map +1 -1
- package/dist/runtime/bridge.js +52 -1
- package/dist/runtime/bridge.js.map +1 -1
- package/dist/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 +16323 -8978
- 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__/reasoning-artifact-contract.test.d.ts +2 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.js +108 -0
- package/dist/scripts/__tests__/reasoning-artifact-contract.test.js.map +1 -0
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +63 -173
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -1
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +74 -159
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js +404 -13
- 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 +53 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +14448 -1480
- 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 +78 -115
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +74 -3
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/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-dispatch.d.ts +9 -0
- package/dist/scripts/notify-hook/team-dispatch.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.js +234 -79
- package/dist/scripts/notify-hook/team-dispatch.js.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +71 -16
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts +12 -4
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.js +365 -44
- package/dist/scripts/notify-hook/team-tmux-guard.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.js +42 -21
- package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker.d.ts +3 -0
- package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +82 -33
- 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 +32 -3
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook.js +79 -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 +80 -4
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +3162 -26
- 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 +61 -0
- package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +462 -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 +8 -0
- package/dist/state/mode-state-context.d.ts.map +1 -1
- package/dist/state/mode-state-context.js +73 -0
- 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 +53 -247
- 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 +58 -135
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +811 -632
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/api-interop.test.js +91 -1
- package/dist/team/__tests__/api-interop.test.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +58 -17
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
- package/dist/team/__tests__/idle-nudge.test.js +45 -15
- package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
- package/dist/team/__tests__/mcp-comm.test.js +197 -30
- package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +110 -3
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/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-cli.test.js +9 -0
- package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +3963 -428
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +2405 -203
- 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__/state.test.js +804 -36
- package/dist/team/__tests__/state.test.js.map +1 -1
- package/dist/team/__tests__/team-ops-contract.test.js +6 -0
- package/dist/team/__tests__/team-ops-contract.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +4099 -1039
- 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 +53 -44
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/__tests__/worktree.test.js +6 -0
- package/dist/team/__tests__/worktree.test.js.map +1 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +15 -9
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/exact-pane.d.ts +23 -0
- package/dist/team/exact-pane.d.ts.map +1 -0
- package/dist/team/exact-pane.js +133 -0
- package/dist/team/exact-pane.js.map +1 -0
- package/dist/team/idle-nudge.d.ts +12 -1
- package/dist/team/idle-nudge.d.ts.map +1 -1
- package/dist/team/idle-nudge.js +41 -14
- package/dist/team/idle-nudge.js.map +1 -1
- package/dist/team/model-contract.d.ts +2 -5
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +59 -17
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/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 +4 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +1527 -291
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts +13 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +1503 -387
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/dispatch.d.ts.map +1 -1
- package/dist/team/state/dispatch.js +26 -8
- package/dist/team/state/dispatch.js.map +1 -1
- package/dist/team/state/locks.d.ts.map +1 -1
- package/dist/team/state/locks.js +61 -13
- package/dist/team/state/locks.js.map +1 -1
- package/dist/team/state/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/tasks.d.ts.map +1 -1
- package/dist/team/state/tasks.js +4 -3
- package/dist/team/state/tasks.js.map +1 -1
- package/dist/team/state/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 +85 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +672 -129
- package/dist/team/state.js.map +1 -1
- package/dist/team/team-ops.d.ts +5 -0
- package/dist/team/team-ops.d.ts.map +1 -1
- package/dist/team/team-ops.js +4 -0
- package/dist/team/team-ops.js.map +1 -1
- package/dist/team/tmux-session.d.ts +163 -27
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +1682 -376
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worktree.d.ts.map +1 -1
- package/dist/team/worktree.js +4 -0
- package/dist/team/worktree.js.map +1 -1
- package/dist/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.d.ts +2 -0
- package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
- package/dist/utils/__tests__/file-durability.test.js +114 -0
- package/dist/utils/__tests__/file-durability.test.js.map +1 -0
- 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 +25 -0
- package/dist/utils/file-durability.d.ts.map +1 -0
- package/dist/utils/file-durability.js +67 -0
- package/dist/utils/file-durability.js.map +1 -0
- 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__/dev-merge-issue-close-workflow.test.js +3 -0
- package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
- package/dist/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/dist/wiki/lint.d.ts +4 -1
- package/dist/wiki/lint.d.ts.map +1 -1
- package/dist/wiki/lint.js +3 -2
- package/dist/wiki/lint.js.map +1 -1
- package/package.json +13 -5
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +105 -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 +6 -4
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +16 -17
- package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
- 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 +6 -4
- package/skills/ralplan/SKILL.md +16 -17
- package/skills/team/SKILL.md +13 -9
- package/skills/ultragoal/SKILL.md +3 -2
- package/src/scripts/__tests__/codex-native-hook.test.ts +8831 -951
- 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__/reasoning-artifact-contract.test.ts +129 -0
- package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +67 -185
- package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +79 -178
- package/src/scripts/__tests__/smoke-packed-install.test.ts +525 -16
- package/src/scripts/__tests__/verify-native-release-assets.test.ts +240 -0
- package/src/scripts/codex-native-hook.ts +14238 -1429
- 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 +91 -122
- package/src/scripts/notify-hook/auto-nudge.ts +71 -3
- package/src/scripts/notify-hook/state-io.ts +11 -0
- package/src/scripts/notify-hook/team-dispatch.ts +242 -75
- package/src/scripts/notify-hook/team-leader-nudge.ts +72 -22
- package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
- package/src/scripts/notify-hook/team-worker-stop.ts +43 -20
- package/src/scripts/notify-hook/team-worker.ts +82 -31
- package/src/scripts/notify-hook/tmux-injection.ts +33 -3
- package/src/scripts/notify-hook.ts +84 -82
- package/src/scripts/postinstall-advisory.ts +67 -0
- package/src/scripts/postinstall.ts +1 -1
- package/src/scripts/smoke-packed-install.ts +3608 -39
- 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
- package/dist/subagents/__tests__/adapted-role-binding.test.d.ts +0 -2
- package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +0 -1
- package/dist/subagents/__tests__/adapted-role-binding.test.js +0 -988
- package/dist/subagents/__tests__/adapted-role-binding.test.js.map +0 -1
- package/dist/subagents/adapted-role-binding.d.ts +0 -17
- package/dist/subagents/adapted-role-binding.d.ts.map +0 -1
- package/dist/subagents/adapted-role-binding.js +0 -75
- package/dist/subagents/adapted-role-binding.js.map +0 -1
|
@@ -1,13 +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, 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
|
+
import { buildResumeArgsWithPreservedFlags, stripHotswapArg } from "../../auth/hotswap.js";
|
|
12
|
+
import { writeSkillActiveStateCopiesForStateDir } from '../../state/skill-active.js';
|
|
11
13
|
import { mergeConfig, repairConfigIfNeeded } from "../../config/generator.js";
|
|
12
14
|
import { ensureReusableNodeModules } from "../../utils/repo-deps.js";
|
|
13
15
|
import { readAllState } from "../../hud/state.js";
|
|
@@ -16,6 +18,7 @@ import { HUD_TMUX_HEIGHT_LINES, HUD_TMUX_MIN_LAUNCH_WINDOW_HEIGHT_LINES } from "
|
|
|
16
18
|
import { createHudWatchPane as createSharedHudWatchPane, listCurrentWindowHudPaneIds } from "../../hud/tmux.js";
|
|
17
19
|
import { DEFAULT_FRONTIER_MODEL, getTeamLowComplexityModel, } from "../../config/models.js";
|
|
18
20
|
import { splitWorkerLaunchArgs } from "../../team/model-contract.js";
|
|
21
|
+
import { buildRegisterResizeHookArgs } from "../../team/tmux-session.js";
|
|
19
22
|
const testDir = dirname(fileURLToPath(import.meta.url));
|
|
20
23
|
const repoRoot = join(testDir, "..", "..", "..");
|
|
21
24
|
function normalizeDarwinTmpPath(value) {
|
|
@@ -66,6 +69,7 @@ describe("madmax state isolation", () => {
|
|
|
66
69
|
const runDir = createMadmaxIsolatedRoot(wd, ["--madmax", "--high"], env);
|
|
67
70
|
env.OMX_ROOT = runDir;
|
|
68
71
|
env.OMXBOX_ACTIVE = "1";
|
|
72
|
+
env.OMX_SOURCE_CWD = wd;
|
|
69
73
|
assert.equal(shouldAutoIsolateMadmaxLaunch("launch", ["--madmax", "--high"], env, wd), false);
|
|
70
74
|
assert.equal(shouldAutoIsolateMadmaxLaunch("launch", ["--madmax", "--xhigh"], env, wd), true, "changed launch semantics must not reuse an inherited boxed root");
|
|
71
75
|
}
|
|
@@ -77,29 +81,40 @@ describe("madmax state isolation", () => {
|
|
|
77
81
|
it("preserves explicit no-box behavior", () => {
|
|
78
82
|
assert.equal(shouldAutoIsolateMadmaxLaunch("launch", ["--madmax"], { OMX_NO_BOX: "1" }, "/repo"), false);
|
|
79
83
|
});
|
|
80
|
-
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 () => {
|
|
81
85
|
const sourceCwd = "/repo/source";
|
|
82
86
|
const worktreeCwd = "/repo/.worktrees/session";
|
|
83
|
-
const runDir = "
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
+
}
|
|
103
118
|
});
|
|
104
119
|
it("does not capture ordinary worktree or unboxed madmax launches", () => {
|
|
105
120
|
assert.equal(captureMadmaxWorktreeRuntimeContext({
|
|
@@ -195,7 +210,7 @@ describe("madmax state isolation", () => {
|
|
|
195
210
|
mkdirSync(lockPath, { recursive: true });
|
|
196
211
|
await writeFile(join(lockPath, "pid"), "2147483647");
|
|
197
212
|
let ran = false;
|
|
198
|
-
const result = withMadmaxDetachedContextLock(runs, contextKey, () => {
|
|
213
|
+
const result = await withMadmaxDetachedContextLock(runs, contextKey, () => {
|
|
199
214
|
ran = true;
|
|
200
215
|
return "acquired";
|
|
201
216
|
}, { maxAttempts: 2, retryMs: 0 });
|
|
@@ -220,7 +235,7 @@ describe("madmax state isolation", () => {
|
|
|
220
235
|
acquired_at: new Date().toISOString(),
|
|
221
236
|
})}\n`);
|
|
222
237
|
await writeFile(join(lockPath, "pid"), String(process.pid));
|
|
223
|
-
assert.
|
|
238
|
+
await assert.rejects(() => withMadmaxDetachedContextLock(runs, contextKey, () => "should-not-run", { maxAttempts: 1, retryMs: 0 }), (err) => {
|
|
224
239
|
assert.ok(err instanceof Error);
|
|
225
240
|
assert.match(err.message, /timed out waiting for madmax detached launch context lock/);
|
|
226
241
|
assert.match(err.message, new RegExp(`holder pid ${process.pid} is still running`));
|
|
@@ -237,6 +252,135 @@ describe("madmax state isolation", () => {
|
|
|
237
252
|
}
|
|
238
253
|
});
|
|
239
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
|
+
});
|
|
240
384
|
describe("resolveOmxRootForLaunch", () => {
|
|
241
385
|
it("preserves POSIX absolute OMX_ROOT", () => {
|
|
242
386
|
assert.equal(resolveOmxRootForLaunch("/repo", { OMX_ROOT: "/var/tmp/omx" }), "/var/tmp/omx");
|
|
@@ -322,15 +466,223 @@ describe("normalizeCodexLaunchArgs", () => {
|
|
|
322
466
|
'model_reasoning_effort="xhigh"',
|
|
323
467
|
]);
|
|
324
468
|
});
|
|
469
|
+
it("adds reasoning overrides before a literal -- marker", () => {
|
|
470
|
+
assert.deepEqual(normalizeCodexLaunchArgs(["--xhigh", "--", "--max"]), [
|
|
471
|
+
"-c",
|
|
472
|
+
'model_reasoning_effort="xhigh"',
|
|
473
|
+
"--",
|
|
474
|
+
"--max",
|
|
475
|
+
]);
|
|
476
|
+
});
|
|
477
|
+
it("adds bypass and reasoning overrides before preserving raw marker suffix args", () => {
|
|
478
|
+
assert.deepEqual(normalizeCodexLaunchArgs([
|
|
479
|
+
"--madmax",
|
|
480
|
+
"--xhigh",
|
|
481
|
+
"--",
|
|
482
|
+
"-c",
|
|
483
|
+
'model_reasoning_effort="ultra"',
|
|
484
|
+
"--max",
|
|
485
|
+
]), [
|
|
486
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
487
|
+
"-c",
|
|
488
|
+
'model_reasoning_effort="xhigh"',
|
|
489
|
+
"--",
|
|
490
|
+
"-c",
|
|
491
|
+
'model_reasoning_effort="ultra"',
|
|
492
|
+
"--max",
|
|
493
|
+
]);
|
|
494
|
+
});
|
|
325
495
|
it("uses the last reasoning shorthand when both are present", () => {
|
|
326
496
|
assert.deepEqual(normalizeCodexLaunchArgs(["--high", "--xhigh"]), [
|
|
327
497
|
"-c",
|
|
328
498
|
'model_reasoning_effort="xhigh"',
|
|
329
499
|
]);
|
|
330
500
|
});
|
|
331
|
-
it("rejects
|
|
332
|
-
|
|
333
|
-
|
|
501
|
+
it("rejects pre-marker max and ultra reasoning shorthands with approved guidance", () => {
|
|
502
|
+
const errors = [
|
|
503
|
+
[
|
|
504
|
+
"--max",
|
|
505
|
+
'Unsupported OMX launch shorthand "--max".\nNo --max shorthand exists; use agentReasoning for per-agent "max" or pass -c model_reasoning_effort=... directly to Codex.\nRun "omx help" for usage.',
|
|
506
|
+
],
|
|
507
|
+
[
|
|
508
|
+
"--ultra",
|
|
509
|
+
'Unsupported OMX launch shorthand "--ultra".\n"ultra" is not an OMX root or per-agent reasoning value and is not an alias for "max".\nRun "omx help" for usage.',
|
|
510
|
+
],
|
|
511
|
+
];
|
|
512
|
+
for (const [flag, message] of errors) {
|
|
513
|
+
assert.throws(() => normalizeCodexLaunchArgs([flag]), { message });
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
it("preserves literal max and ultra after -- with raw -c arguments", () => {
|
|
517
|
+
const args = [
|
|
518
|
+
"-c",
|
|
519
|
+
"model_reasoning_effort=MAX",
|
|
520
|
+
"--",
|
|
521
|
+
"--max",
|
|
522
|
+
"--ultra",
|
|
523
|
+
"-c",
|
|
524
|
+
'model_reasoning_effort="ultra"',
|
|
525
|
+
"-c",
|
|
526
|
+
"model_reasoning_effort=future",
|
|
527
|
+
];
|
|
528
|
+
assert.deepEqual(normalizeCodexLaunchArgs(args), args);
|
|
529
|
+
});
|
|
530
|
+
it("preserves post-marker OMX flags as literal Codex arguments", () => {
|
|
531
|
+
const args = [
|
|
532
|
+
"--",
|
|
533
|
+
"--worktree",
|
|
534
|
+
"post-marker-branch",
|
|
535
|
+
"--notify-temp",
|
|
536
|
+
"--discord",
|
|
537
|
+
"--custom",
|
|
538
|
+
"openclaw:ops",
|
|
539
|
+
"--spark",
|
|
540
|
+
"resume",
|
|
541
|
+
"--project",
|
|
542
|
+
"--codex-home",
|
|
543
|
+
"/tmp/literal-codex-home",
|
|
544
|
+
"--version",
|
|
545
|
+
];
|
|
546
|
+
const notifyTempResult = resolveNotifyTempContract(args, {});
|
|
547
|
+
assert.equal(notifyTempResult.contract.active, false);
|
|
548
|
+
assert.deepEqual(notifyTempResult.contract.canonicalSelectors, []);
|
|
549
|
+
assert.deepEqual(notifyTempResult.passthroughArgs, args);
|
|
550
|
+
assert.equal(resolveWorkerSparkModel(notifyTempResult.passthroughArgs), undefined);
|
|
551
|
+
assert.equal(isResumeCodexLaunch(args), false);
|
|
552
|
+
assert.equal(isCodexVersionRequest(args), false);
|
|
553
|
+
assert.deepEqual(parseResumeCodexHomeSelection(args), {
|
|
554
|
+
args,
|
|
555
|
+
explicitCodexHome: undefined,
|
|
556
|
+
projectOnly: false,
|
|
557
|
+
});
|
|
558
|
+
assert.deepEqual(normalizeCodexLaunchArgs(notifyTempResult.passthroughArgs), args);
|
|
559
|
+
});
|
|
560
|
+
it("parses resume-owned selectors only before the end-of-options marker", () => {
|
|
561
|
+
assert.deepEqual(parseResumeCodexHomeSelection([
|
|
562
|
+
"resume",
|
|
563
|
+
"--codex-home",
|
|
564
|
+
"/tmp/selected-codex-home",
|
|
565
|
+
"--project",
|
|
566
|
+
"--",
|
|
567
|
+
"--codex-home",
|
|
568
|
+
"/tmp/literal-codex-home",
|
|
569
|
+
"--project",
|
|
570
|
+
]), {
|
|
571
|
+
args: [
|
|
572
|
+
"resume",
|
|
573
|
+
"--",
|
|
574
|
+
"--codex-home",
|
|
575
|
+
"/tmp/literal-codex-home",
|
|
576
|
+
"--project",
|
|
577
|
+
],
|
|
578
|
+
explicitCodexHome: "/tmp/selected-codex-home",
|
|
579
|
+
projectOnly: true,
|
|
580
|
+
});
|
|
581
|
+
assert.equal(isResumeCodexLaunch(["resume", "--", "literal"]), true);
|
|
582
|
+
assert.equal(isCodexVersionRequest(["--version", "--", "literal"]), true);
|
|
583
|
+
});
|
|
584
|
+
it("removes hotswap only before the end-of-options marker", () => {
|
|
585
|
+
assert.deepEqual(stripHotswapArg(["--hotswap", "--", "--hotswap", "literal"]), ["--", "--hotswap", "literal"]);
|
|
586
|
+
});
|
|
587
|
+
it("preserves Codex launch authority and the first literal suffix when building quota resume args", () => {
|
|
588
|
+
assert.deepEqual(buildResumeArgsWithPreservedFlags([
|
|
589
|
+
"--model", "gpt-review",
|
|
590
|
+
"--model=gpt-review-fast",
|
|
591
|
+
"--config", "developer_instructions=enabled",
|
|
592
|
+
"--config=developer_instructions=overridden",
|
|
593
|
+
"--add-dir", "src",
|
|
594
|
+
"--remote", "ws://127.0.0.1:4500",
|
|
595
|
+
"--remote=ws://127.0.0.1:4501",
|
|
596
|
+
"--remote-auth-token-env", "CODEX_REMOTE_TOKEN",
|
|
597
|
+
"--remote-auth-token-env=CODEX_REMOTE_TOKEN_BACKUP",
|
|
598
|
+
"-i", "one.png",
|
|
599
|
+
"--image", "two.png",
|
|
600
|
+
"-i", "three.png,four.png",
|
|
601
|
+
"--image=five.png,six.png",
|
|
602
|
+
"-iseven.png,eight.png",
|
|
603
|
+
"--image=resume", "resume",
|
|
604
|
+
"--oss",
|
|
605
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
606
|
+
"--model", "gpt-resume",
|
|
607
|
+
"resume", "old-session",
|
|
608
|
+
"--last", "--all", "--include-non-interactive",
|
|
609
|
+
"--", "--hotswap", "--last", "--all", "--include-non-interactive", "--model", "opaque-model", "literal suffix",
|
|
610
|
+
], "session-123"), [
|
|
611
|
+
"resume", "session-123",
|
|
612
|
+
"--model", "gpt-review",
|
|
613
|
+
"--model=gpt-review-fast",
|
|
614
|
+
"--config", "developer_instructions=enabled",
|
|
615
|
+
"--config=developer_instructions=overridden",
|
|
616
|
+
"--add-dir", "src",
|
|
617
|
+
"--remote", "ws://127.0.0.1:4500",
|
|
618
|
+
"--remote=ws://127.0.0.1:4501",
|
|
619
|
+
"--remote-auth-token-env", "CODEX_REMOTE_TOKEN",
|
|
620
|
+
"--remote-auth-token-env=CODEX_REMOTE_TOKEN_BACKUP",
|
|
621
|
+
"-i", "one.png",
|
|
622
|
+
"--image", "two.png",
|
|
623
|
+
"-i", "three.png,four.png",
|
|
624
|
+
"--image=five.png,six.png",
|
|
625
|
+
"-iseven.png,eight.png",
|
|
626
|
+
"--image=resume",
|
|
627
|
+
"--oss",
|
|
628
|
+
"--dangerously-bypass-approvals-and-sandbox",
|
|
629
|
+
"--model", "gpt-resume",
|
|
630
|
+
"--", "--hotswap", "--last", "--all", "--include-non-interactive", "--model", "opaque-model", "literal suffix",
|
|
631
|
+
]);
|
|
632
|
+
});
|
|
633
|
+
it("removes resume selectors only when synthesizing an explicit session", () => {
|
|
634
|
+
for (const selectors of [
|
|
635
|
+
["--last"],
|
|
636
|
+
["--all"],
|
|
637
|
+
["--include-non-interactive"],
|
|
638
|
+
["--last", "--all"],
|
|
639
|
+
["--all", "--include-non-interactive"],
|
|
640
|
+
["--last", "--all", "--include-non-interactive"],
|
|
641
|
+
]) {
|
|
642
|
+
assert.deepEqual(buildResumeArgsWithPreservedFlags([
|
|
643
|
+
"resume",
|
|
644
|
+
...selectors,
|
|
645
|
+
"--model", "gpt-review",
|
|
646
|
+
"--remote", "ws://127.0.0.1:4500",
|
|
647
|
+
"--", ...selectors, "opaque suffix",
|
|
648
|
+
], "session-123"), [
|
|
649
|
+
"resume", "session-123",
|
|
650
|
+
"--model", "gpt-review",
|
|
651
|
+
"--remote", "ws://127.0.0.1:4500",
|
|
652
|
+
"--", ...selectors, "opaque suffix",
|
|
653
|
+
], JSON.stringify(selectors));
|
|
654
|
+
}
|
|
655
|
+
});
|
|
656
|
+
it("treats only split image values as variadic", () => {
|
|
657
|
+
assert.deepEqual([...CODEX_GLOBAL_OPTIONS_WITH_SPLIT_VALUE], [
|
|
658
|
+
["-a", "single"], ["--ask-for-approval", "single"], ["-c", "single"],
|
|
659
|
+
["--config", "single"], ["-C", "single"], ["--cd", "single"],
|
|
660
|
+
["-i", "variadic"], ["--image", "variadic"], ["-m", "single"],
|
|
661
|
+
["--model", "single"], ["-p", "single"], ["--profile", "single"],
|
|
662
|
+
["-s", "single"], ["--sandbox", "single"], ["--add-dir", "single"],
|
|
663
|
+
["--disable", "single"], ["--enable", "single"], ["--local-provider", "single"],
|
|
664
|
+
["--remote", "single"], ["--remote-auth-token-env", "single"],
|
|
665
|
+
]);
|
|
666
|
+
for (const args of [
|
|
667
|
+
["--model", "resume"], ["--remote", "resume"],
|
|
668
|
+
["--image", "resume"], ["-i", "resume"], ["--image=resume"], ["-iresume"], ["-i=resume"],
|
|
669
|
+
["-i", "one.png", "resume"], ["--image", "one.png", "resume"],
|
|
670
|
+
["-i", "one.png", "-i", "two.png", "resume"],
|
|
671
|
+
["--image", "one.png", "--image", "two.png", "resume"],
|
|
672
|
+
["-i", "one.png,two.png", "resume"], ["--image", "one.png,two.png", "resume"],
|
|
673
|
+
["exec", "resume"], ["--image", "one.png", "--", "resume"],
|
|
674
|
+
]) {
|
|
675
|
+
assert.equal(isResumeCodexLaunch(args), false, JSON.stringify(args));
|
|
676
|
+
}
|
|
677
|
+
for (const args of [
|
|
678
|
+
["--model", "gpt-review", "resume"],
|
|
679
|
+
["--image=one.png", "resume"], ["-ione.png", "resume"], ["-i=one.png", "resume"],
|
|
680
|
+
["--image", "one.png", "--model", "gpt-review", "resume"],
|
|
681
|
+
["--image=one.png", "--model=gpt-review", "resume"],
|
|
682
|
+
["-ione.png", "--remote", "ws://127.0.0.1:4500", "resume"],
|
|
683
|
+
]) {
|
|
684
|
+
assert.equal(isResumeCodexLaunch(args), true, JSON.stringify(args));
|
|
685
|
+
}
|
|
334
686
|
});
|
|
335
687
|
it("maps --xhigh --madmax to codex-native flags only", () => {
|
|
336
688
|
assert.deepEqual(normalizeCodexLaunchArgs(["--xhigh", "--madmax"]), [
|
|
@@ -484,6 +836,13 @@ describe("resolveNotifyTempContract", () => {
|
|
|
484
836
|
]);
|
|
485
837
|
assert.equal(parsed.contract.warnings.length >= 1, true);
|
|
486
838
|
});
|
|
839
|
+
it("does not activate or consume selectors after --", () => {
|
|
840
|
+
const args = ["--", "--notify-temp", "--discord", "--custom", "openclaw:ops"];
|
|
841
|
+
const parsed = resolveNotifyTempContract(args, {});
|
|
842
|
+
assert.equal(parsed.contract.active, false);
|
|
843
|
+
assert.deepEqual(parsed.contract.selectors, []);
|
|
844
|
+
assert.deepEqual(parsed.passthroughArgs, args);
|
|
845
|
+
});
|
|
487
846
|
it("activates from OMX_NOTIFY_TEMP=1 env parity", () => {
|
|
488
847
|
const parsed = resolveNotifyTempContract(["--model", "gpt-5"], {
|
|
489
848
|
OMX_NOTIFY_TEMP: "1",
|
|
@@ -634,6 +993,81 @@ describe("cleanupPostLaunchModeStateFiles", () => {
|
|
|
634
993
|
}
|
|
635
994
|
assert.deepEqual(warnings, []);
|
|
636
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
|
+
});
|
|
637
1071
|
it("normalizes stale terminal deep-interview locks during postLaunch cleanup", async () => {
|
|
638
1072
|
const wd = await mkdtemp(join(tmpdir(), "omx-postlaunch-di-terminal-locks-"));
|
|
639
1073
|
const sessionId = "sess-postlaunch-di-terminal-locks";
|
|
@@ -1141,6 +1575,10 @@ describe("resolveWorkerSparkModel", () => {
|
|
|
1141
1575
|
it("returns undefined for empty args", () => {
|
|
1142
1576
|
assert.equal(resolveWorkerSparkModel([]), undefined);
|
|
1143
1577
|
});
|
|
1578
|
+
it("returns undefined for spark flags after --", () => {
|
|
1579
|
+
assert.equal(resolveWorkerSparkModel(["--", "--spark"]), undefined);
|
|
1580
|
+
assert.equal(resolveWorkerSparkModel(["--", "--madmax-spark"]), undefined);
|
|
1581
|
+
});
|
|
1144
1582
|
it("reads low-complexity team model from config when codexHomeOverride is provided", async () => {
|
|
1145
1583
|
// Intentional legacy model fixture: verifies an explicit user override is routed to workers unchanged.
|
|
1146
1584
|
const codexHome = await mkdtemp(join(tmpdir(), "omx-codex-home-"));
|
|
@@ -1312,6 +1750,17 @@ describe("resolveCliInvocation", () => {
|
|
|
1312
1750
|
assert.match(HELP, /omx update --stable\s+Install\/rollback to npm stable \(oh-my-codex@latest\), then refresh setup/);
|
|
1313
1751
|
assert.match(HELP, /omx update --dev\s+Install the upstream dev branch, then refresh setup/);
|
|
1314
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
|
+
});
|
|
1757
|
+
it("advertises only the four supported root reasoning modes", () => {
|
|
1758
|
+
assert.match(HELP, /omx reasoning Show or set model reasoning effort \(low\|medium\|high\|xhigh\)/);
|
|
1759
|
+
assert.match(HELP, /--high\s+Launch Codex with high reasoning effort/);
|
|
1760
|
+
assert.match(HELP, /--xhigh\s+Launch Codex with xhigh reasoning effort/);
|
|
1761
|
+
assert.doesNotMatch(HELP, /--max/);
|
|
1762
|
+
assert.doesNotMatch(HELP, /--ultra/);
|
|
1763
|
+
});
|
|
1315
1764
|
it("advertises concise launch policy controls in top-level help", () => {
|
|
1316
1765
|
assert.match(HELP, /--direct\s+Launch the interactive leader directly/);
|
|
1317
1766
|
assert.match(HELP, /OMX_LAUNCH_POLICY=auto[\s\S]*Use the default policy/);
|
|
@@ -1830,6 +2279,10 @@ describe("project launch scope helpers", () => {
|
|
|
1830
2279
|
'trusted_hash = "sha256:setup-owned"',
|
|
1831
2280
|
"# End OMX-owned Codex hook trust state",
|
|
1832
2281
|
"",
|
|
2282
|
+
"# User-owned project trust source must remain external during launch repair.",
|
|
2283
|
+
`[projects."${wd}"] # retained external ownership`,
|
|
2284
|
+
'trust_level = "trusted"',
|
|
2285
|
+
"",
|
|
1833
2286
|
"# OMX-synced Codex project trust state (from runtime CODEX_HOME)",
|
|
1834
2287
|
`[projects."${wd}"]`,
|
|
1835
2288
|
'trust_level = "trusted"',
|
|
@@ -1850,6 +2303,9 @@ describe("project launch scope helpers", () => {
|
|
|
1850
2303
|
assert.equal(countMatches(runtimeConfig, new RegExp(`^${escapedProjectHookTrustHeader}$`, "gm")), 1);
|
|
1851
2304
|
assert.ok(runtimeConfig.includes(`[projects."${wd}"]`));
|
|
1852
2305
|
assert.ok(repairedProjectConfig.includes(`[projects."${wd}"]`));
|
|
2306
|
+
assert.match(repairedProjectConfig, /User-owned project trust source must remain external/);
|
|
2307
|
+
assert.match(repairedProjectConfig, new RegExp(`^${escapeRegExp(`[projects."${wd}"]`)} # retained external ownership$`, "m"));
|
|
2308
|
+
assert.equal(countMatches(repairedProjectConfig, new RegExp(`^${escapeRegExp(`[projects."${wd}"]`)}(?:\\s|$)`, "gm")), 1, "launch repair must remove only the marker-owned duplicate before the runtime mirror is written");
|
|
1853
2309
|
}
|
|
1854
2310
|
finally {
|
|
1855
2311
|
await rm(wd, { recursive: true, force: true });
|
|
@@ -1997,6 +2453,47 @@ describe("project launch scope helpers", () => {
|
|
|
1997
2453
|
await rm(wd, { recursive: true, force: true });
|
|
1998
2454
|
}
|
|
1999
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
|
+
});
|
|
2000
2497
|
});
|
|
2001
2498
|
describe("pointer launch aborts", () => {
|
|
2002
2499
|
it("does not launch, tag, or retain a runtime home when a pointer lock is held", async () => {
|
|
@@ -2086,7 +2583,7 @@ describe("pointer launch aborts", () => {
|
|
|
2086
2583
|
},
|
|
2087
2584
|
});
|
|
2088
2585
|
assert.equal(result.status, 1, result.stderr || result.stdout);
|
|
2089
|
-
assert.match(result.stderr, /
|
|
2586
|
+
assert.match(result.stderr, /Unable to establish launch directory capability/);
|
|
2090
2587
|
assert.equal(existsSync(codexLog), false);
|
|
2091
2588
|
assert.equal(existsSync(tmuxLog), false);
|
|
2092
2589
|
assert.equal(existsSync(join(wd, ".omx", "state", "sessions")), false);
|
|
@@ -2221,20 +2718,23 @@ describe("tmux HUD pane helpers", () => {
|
|
|
2221
2718
|
it("buildHudPaneCleanupTargets is a no-op guard when leaderPaneId is absent", () => {
|
|
2222
2719
|
assert.deepEqual(buildHudPaneCleanupTargets(["%3"], "%4"), ["%3", "%4"]);
|
|
2223
2720
|
});
|
|
2224
|
-
it("listCurrentWindowHudPaneIds
|
|
2721
|
+
it("listCurrentWindowHudPaneIds excludes snapshots without complete current ownership", () => {
|
|
2225
2722
|
const calls = [];
|
|
2226
|
-
const panes = listCurrentWindowHudPaneIds("%
|
|
2723
|
+
const panes = listCurrentWindowHudPaneIds("%1", (args) => {
|
|
2227
2724
|
calls.push(args);
|
|
2725
|
+
if (args.at(-1) === "#{pane_id}")
|
|
2726
|
+
return "%1\n%2\n";
|
|
2228
2727
|
return [
|
|
2229
|
-
"%
|
|
2230
|
-
"%
|
|
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",
|
|
2231
2730
|
].join("\n");
|
|
2232
2731
|
});
|
|
2233
|
-
assert.deepEqual(panes, [
|
|
2234
|
-
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], [
|
|
2235
2735
|
"list-panes",
|
|
2236
2736
|
"-t",
|
|
2237
|
-
"%
|
|
2737
|
+
"%1",
|
|
2238
2738
|
"-F",
|
|
2239
2739
|
[
|
|
2240
2740
|
"#{pane_id}",
|
|
@@ -2248,31 +2748,67 @@ describe("tmux HUD pane helpers", () => {
|
|
|
2248
2748
|
"#{window_height}",
|
|
2249
2749
|
"#{pane_start_command}",
|
|
2250
2750
|
"#{pane_current_path}",
|
|
2751
|
+
"#{pane_dead}",
|
|
2752
|
+
"#{pane_pid}",
|
|
2251
2753
|
].join("\x1f"),
|
|
2252
2754
|
]);
|
|
2253
2755
|
});
|
|
2254
|
-
it("createHudWatchPane
|
|
2756
|
+
it("createHudWatchPane rejects an incomplete synthetic source incarnation", () => {
|
|
2255
2757
|
const calls = [];
|
|
2256
|
-
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) => {
|
|
2257
2762
|
calls.push(args);
|
|
2258
|
-
|
|
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(" ")}`);
|
|
2259
2809
|
});
|
|
2260
|
-
assert.equal(paneId,
|
|
2261
|
-
assert.
|
|
2262
|
-
"split-window",
|
|
2263
|
-
"-v",
|
|
2264
|
-
"-l",
|
|
2265
|
-
"3",
|
|
2266
|
-
"-d",
|
|
2267
|
-
"-t",
|
|
2268
|
-
"%leader",
|
|
2269
|
-
"-c",
|
|
2270
|
-
"/repo",
|
|
2271
|
-
"-P",
|
|
2272
|
-
"-F",
|
|
2273
|
-
"#{pane_id}",
|
|
2274
|
-
"node /repo/dist/cli/omx.js hud --watch",
|
|
2275
|
-
]);
|
|
2810
|
+
assert.equal(paneId, null);
|
|
2811
|
+
assert.equal(calls.some((args) => args[0] === "if-shell"), false);
|
|
2276
2812
|
});
|
|
2277
2813
|
});
|
|
2278
2814
|
describe("detached tmux new-session sequencing", () => {
|
|
@@ -2341,7 +2877,7 @@ describe("detached tmux new-session sequencing", () => {
|
|
|
2341
2877
|
assert.equal(newSession.args.includes("-e") &&
|
|
2342
2878
|
newSession.args.some((arg) => arg === "OMX_ROOT=/tmp/omx-root"), true);
|
|
2343
2879
|
});
|
|
2344
|
-
it("
|
|
2880
|
+
it("clears unverified boxed env from detached tmux sessions", () => {
|
|
2345
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", {
|
|
2346
2882
|
OMXBOX_ACTIVE: "1",
|
|
2347
2883
|
OMX_SOURCE_CWD: "/tmp/source-project",
|
|
@@ -2349,11 +2885,11 @@ describe("detached tmux new-session sequencing", () => {
|
|
|
2349
2885
|
});
|
|
2350
2886
|
const newSession = steps.find((step) => step.name === "new-session");
|
|
2351
2887
|
assert.ok(newSession);
|
|
2352
|
-
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);
|
|
2353
2889
|
assert.equal(newSession.args.some((arg) => arg === "OMX_STATE_ROOT=/tmp/boxed-state-root"), false);
|
|
2354
2890
|
assert.equal(newSession.args.some((arg) => arg === "OMX_TMUX_HUD_OWNER=1"), true);
|
|
2355
|
-
assert.equal(newSession.args.some((arg) => arg === "OMXBOX_ACTIVE=1"),
|
|
2356
|
-
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);
|
|
2357
2893
|
});
|
|
2358
2894
|
it("buildDetachedSessionBootstrapSteps preserves OMX_STATE_ROOT identity when no root override is explicit", () => {
|
|
2359
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" });
|
|
@@ -2645,13 +3181,15 @@ exit 0
|
|
|
2645
3181
|
it("runCodex builds inside-tmux HUD command through explicit runtime-root resolver", async () => {
|
|
2646
3182
|
const source = await readFile(join(repoRoot, 'src', 'cli', 'index.ts'), 'utf-8');
|
|
2647
3183
|
assert.match(source, /const hudRuntimeRoot: HudRuntimeRootForLaunch = runtimeContext\s*\? \{ omxRoot: runtimeContext\.omxRoot, rootSource: 'omx-root-env' \}\s*: resolveHudRuntimeRootForLaunch\(cwd, process\.env\);/);
|
|
2648
|
-
assert.match(source, /const hudRuntimeEnv = \
|
|
2649
|
-
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/);
|
|
2650
3188
|
assert.match(source, /buildTmuxPaneCommand\("env",\s*\[\.\.\.hudEnvArgs,\s*"node",\s*omxBin,\s*"hud",\s*"--watch"\]\)/);
|
|
2651
3189
|
});
|
|
2652
3190
|
it("runCodex registers a HUD resize hook immediately for inside-tmux launches", async () => {
|
|
2653
3191
|
const source = await readFile(join(repoRoot, 'src', 'cli', 'index.ts'), 'utf-8');
|
|
2654
|
-
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*\}\)/);
|
|
2655
3193
|
assert.match(source, /if \(currentPaneId\) \{\s*unregisterHudResizeHook\(currentPaneId\);\s*\}/);
|
|
2656
3194
|
});
|
|
2657
3195
|
it("buildInsideTmuxHudHookEnv tags hook commands with session, owner, leader, and local root", () => {
|
|
@@ -2710,6 +3248,59 @@ exit 0
|
|
|
2710
3248
|
assert.equal(env.OMX_ROOT, "/repo");
|
|
2711
3249
|
assert.equal(env.OMX_ENTRY_PATH, "/repo/dist/cli/omx.js");
|
|
2712
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
|
+
});
|
|
2713
3304
|
it("registerDetachedHudLayoutReconcileHook reads TMUX from the detached leader pane before registering", () => {
|
|
2714
3305
|
const calls = [];
|
|
2715
3306
|
const readTargets = [];
|
|
@@ -2759,412 +3350,28 @@ exit 0
|
|
|
2759
3350
|
},
|
|
2760
3351
|
}), false);
|
|
2761
3352
|
});
|
|
2762
|
-
it("buildDetachedSessionBootstrapSteps starts native Windows
|
|
2763
|
-
const
|
|
2764
|
-
"omx.js",
|
|
2765
|
-
"hud",
|
|
2766
|
-
"--watch",
|
|
2767
|
-
]);
|
|
2768
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", "C:/project", "'codex' '--dangerously-bypass-approvals-and-sandbox'", hudCmd, "--model gpt-5", "C:/codex-home", null, true);
|
|
2769
|
-
assert.equal(steps[0]?.name, "new-session");
|
|
2770
|
-
assert.equal(steps[0]?.args.at(-1), "powershell.exe");
|
|
2771
|
-
assert.equal(steps[1]?.name, "split-and-capture-hud-pane");
|
|
2772
|
-
assert.equal(steps[1]?.args.at(-1), hudCmd);
|
|
2773
|
-
});
|
|
2774
|
-
it("buildDetachedWindowsBootstrapScript targets the resolved tmux-compatible command", () => {
|
|
2775
|
-
const script = buildDetachedWindowsBootstrapScript("omx-demo", "powershell.exe -NoLogo -NoExit -EncodedCommand abc", 2500, "C:\\Program Files\\psmux\\psmux.exe");
|
|
2776
|
-
assert.match(script, /const tmuxCommand = "C:\\\\Program Files\\\\psmux\\\\psmux\.exe";/);
|
|
2777
|
-
assert.match(script, /execFileSync\(tmuxCommand, \['send-keys'/);
|
|
2778
|
-
assert.doesNotMatch(script, /execFileSync\('tmux'/);
|
|
2779
|
-
});
|
|
2780
|
-
it("buildDetachedSessionBootstrapSteps kills detached tmux session on normal shell exit", () => {
|
|
2781
|
-
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");
|
|
2782
3355
|
const leaderCmd = steps[0]?.args.at(-1);
|
|
2783
3356
|
assert.equal(typeof leaderCmd, "string");
|
|
2784
|
-
assert.match(leaderCmd,
|
|
2785
|
-
assert.
|
|
2786
|
-
assert.
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
assert.match(leaderCmd, /<\&3 &/);
|
|
2792
|
-
assert.match(leaderCmd, /wait "\$omx_codex_pid";/);
|
|
2793
|
-
assert.match(leaderCmd, /kill -TERM "\$omx_codex_pid"/);
|
|
2794
|
-
assert.match(leaderCmd, /releaseTmuxExtendedKeysLease/);
|
|
2795
|
-
assert.match(leaderCmd, /if \[ "\$status" -eq 0 \]; then/);
|
|
2796
|
-
assert.match(leaderCmd, /tmux kill-session -t/);
|
|
2797
|
-
assert.match(leaderCmd, /"omx-demo"/);
|
|
2798
|
-
assert.match(leaderCmd, /codex exited immediately with code 0/);
|
|
2799
|
-
assert.match(leaderCmd, /codex exited with code/);
|
|
2800
|
-
assert.match(leaderCmd, /detached tmux session is being kept open/);
|
|
2801
|
-
assert.match(leaderCmd, /exit \$status/);
|
|
2802
|
-
});
|
|
2803
|
-
it("buildDetachedSessionBootstrapSteps finalizes postLaunch inside the detached leader when a session id is available", () => {
|
|
2804
|
-
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);
|
|
2805
3364
|
const leaderCmd = steps[0]?.args.at(-1);
|
|
2806
3365
|
assert.equal(typeof leaderCmd, "string");
|
|
2807
|
-
assert.match(leaderCmd, /
|
|
2808
|
-
assert.match(leaderCmd, /
|
|
2809
|
-
assert.
|
|
2810
|
-
assert.match(leaderCmd, /\/tmp\/project\/\.codex-project/);
|
|
2811
|
-
assert.match(leaderCmd, /\/tmp\/project\/\.omx\/runtime\/codex-home\/omx-session-123/);
|
|
2812
|
-
const helperIndex = leaderCmd.indexOf("runDetachedSessionPostLaunch");
|
|
2813
|
-
const signalGateIndex = leaderCmd.indexOf('if [ "$status" -eq 0 ]');
|
|
2814
|
-
assert.ok(helperIndex >= 0);
|
|
2815
|
-
assert.ok(signalGateIndex >= 0);
|
|
2816
|
-
assert.ok(helperIndex < signalGateIndex, "detached postLaunch helper must run before the signal-derived tmux kill-session gate");
|
|
2817
|
-
});
|
|
2818
|
-
it("detached leader command keeps stdin open for the Codex child", async () => {
|
|
2819
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-stdin-"));
|
|
2820
|
-
const fakeBin = join(cwd, "bin");
|
|
2821
|
-
const stdinLogPath = join(cwd, "stdin.log");
|
|
2822
|
-
try {
|
|
2823
|
-
await mkdir(fakeBin, { recursive: true });
|
|
2824
|
-
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
2825
|
-
if IFS= read -r line; then
|
|
2826
|
-
printf 'stdin:%s\n' "$line" > "${stdinLogPath}"
|
|
2827
|
-
else
|
|
2828
|
-
printf 'stdin:EOF\n' > "${stdinLogPath}"
|
|
2829
|
-
fi
|
|
2830
|
-
`);
|
|
2831
|
-
await chmod(join(fakeBin, "codex"), 0o755);
|
|
2832
|
-
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
2833
|
-
case "$1" in
|
|
2834
|
-
display-message)
|
|
2835
|
-
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
2836
|
-
printf '/tmp/tmux-test.sock\n'
|
|
2837
|
-
else
|
|
2838
|
-
printf '0\n'
|
|
2839
|
-
fi
|
|
2840
|
-
;;
|
|
2841
|
-
show-options)
|
|
2842
|
-
printf 'off\n'
|
|
2843
|
-
;;
|
|
2844
|
-
set-option|kill-session)
|
|
2845
|
-
;;
|
|
2846
|
-
esac
|
|
2847
|
-
exit 0
|
|
2848
|
-
`);
|
|
2849
|
-
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
2850
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", [], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
2851
|
-
const leaderCmd = steps[0]?.args.at(-1);
|
|
2852
|
-
assert.equal(typeof leaderCmd, "string");
|
|
2853
|
-
const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
|
|
2854
|
-
cwd,
|
|
2855
|
-
env: {
|
|
2856
|
-
...process.env,
|
|
2857
|
-
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
2858
|
-
HOME: cwd,
|
|
2859
|
-
},
|
|
2860
|
-
input: "hello from leader\n",
|
|
2861
|
-
encoding: "utf-8",
|
|
2862
|
-
});
|
|
2863
|
-
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
2864
|
-
const stdinLog = await readFile(stdinLogPath, "utf-8");
|
|
2865
|
-
assert.match(stdinLog, /stdin:hello from leader/);
|
|
2866
|
-
}
|
|
2867
|
-
finally {
|
|
2868
|
-
await rm(cwd, { recursive: true, force: true });
|
|
2869
|
-
}
|
|
2870
|
-
});
|
|
2871
|
-
it("detached leader command preserves cwd and cleanup without shell-quote breakage", async () => {
|
|
2872
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-"));
|
|
2873
|
-
const fakeBin = join(cwd, "bin");
|
|
2874
|
-
const logPath = join(cwd, "leader.log");
|
|
2875
|
-
try {
|
|
2876
|
-
await mkdir(fakeBin, { recursive: true });
|
|
2877
|
-
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
2878
|
-
printf 'codex:%s\\n' "$*" >> "${logPath}"
|
|
2879
|
-
printf 'codex-pwd:%s\\n' "$(pwd)" >> "${logPath}"
|
|
2880
|
-
printf 'codex-bridge-env:%s\\n' "\${OMX_HERMES_MCP_BRIDGE-unset}" >> "${logPath}"
|
|
2881
|
-
exit 0
|
|
2882
|
-
`);
|
|
2883
|
-
await chmod(join(fakeBin, "codex"), 0o755);
|
|
2884
|
-
await writeFile(join(cwd, ".profile"), "cd ..\n");
|
|
2885
|
-
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
2886
|
-
printf 'tmux:%s\\n' "$*" >> "${logPath}"
|
|
2887
|
-
case "$1" in
|
|
2888
|
-
display-message)
|
|
2889
|
-
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
2890
|
-
printf '/tmp/tmux-test.sock\\n'
|
|
2891
|
-
else
|
|
2892
|
-
printf '0\\n'
|
|
2893
|
-
fi
|
|
2894
|
-
;;
|
|
2895
|
-
show-options)
|
|
2896
|
-
printf 'off\\n'
|
|
2897
|
-
;;
|
|
2898
|
-
set-option|kill-session)
|
|
2899
|
-
;;
|
|
2900
|
-
esac
|
|
2901
|
-
exit 0
|
|
2902
|
-
`);
|
|
2903
|
-
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
2904
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", ["--dangerously-bypass-approvals-and-sandbox"], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
2905
|
-
const leaderCmd = steps[0]?.args.at(-1);
|
|
2906
|
-
assert.equal(typeof leaderCmd, "string");
|
|
2907
|
-
(await import("node:child_process")).execFileSync("/bin/sh", ["-c", leaderCmd], {
|
|
2908
|
-
cwd,
|
|
2909
|
-
env: {
|
|
2910
|
-
...process.env,
|
|
2911
|
-
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
2912
|
-
HOME: cwd,
|
|
2913
|
-
OMX_HERMES_MCP_BRIDGE: "1",
|
|
2914
|
-
},
|
|
2915
|
-
stdio: "ignore",
|
|
2916
|
-
});
|
|
2917
|
-
const log = await readFile(logPath, "utf-8");
|
|
2918
|
-
assert.match(log, /codex:--dangerously-bypass-approvals-and-sandbox/);
|
|
2919
|
-
assert.match(normalizeDarwinTmpPath(log), new RegExp(`codex-pwd:${escapeRegExp(normalizeDarwinTmpPath(cwd))}`));
|
|
2920
|
-
assert.match(log, /codex-bridge-env:unset/);
|
|
2921
|
-
assert.match(log, /tmux:display-message -p #\{socket_path\}/);
|
|
2922
|
-
assert.match(log, /tmux:show-options -sv extended-keys/);
|
|
2923
|
-
assert.match(log, /tmux:set-option -sq extended-keys always/);
|
|
2924
|
-
assert.match(log, /tmux:set-option -sq extended-keys off/);
|
|
2925
|
-
assert.match(log, /tmux:kill-session -t omx-demo/);
|
|
2926
|
-
}
|
|
2927
|
-
finally {
|
|
2928
|
-
await rm(cwd, { recursive: true, force: true });
|
|
2929
|
-
}
|
|
2930
|
-
});
|
|
2931
|
-
it("detached leader command preserves the detached tmux session on signal-derived exits", async () => {
|
|
2932
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-signal-"));
|
|
2933
|
-
const fakeBin = join(cwd, "bin");
|
|
2934
|
-
const logPath = join(cwd, "leader.log");
|
|
2935
|
-
try {
|
|
2936
|
-
await mkdir(fakeBin, { recursive: true });
|
|
2937
|
-
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
2938
|
-
printf 'codex:%s\\n' "$*" >> "${logPath}"
|
|
2939
|
-
exit 143
|
|
2940
|
-
`);
|
|
2941
|
-
await chmod(join(fakeBin, "codex"), 0o755);
|
|
2942
|
-
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
2943
|
-
printf 'tmux:%s\\n' "$*" >> "${logPath}"
|
|
2944
|
-
case "$1" in
|
|
2945
|
-
display-message)
|
|
2946
|
-
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
2947
|
-
printf '/tmp/tmux-test.sock\\n'
|
|
2948
|
-
else
|
|
2949
|
-
printf '0\\n'
|
|
2950
|
-
fi
|
|
2951
|
-
;;
|
|
2952
|
-
show-options)
|
|
2953
|
-
printf 'off\\n'
|
|
2954
|
-
;;
|
|
2955
|
-
set-option|kill-session)
|
|
2956
|
-
;;
|
|
2957
|
-
esac
|
|
2958
|
-
exit 0
|
|
2959
|
-
`);
|
|
2960
|
-
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
2961
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", ["--dangerously-bypass-approvals-and-sandbox"], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
2962
|
-
const leaderCmd = steps[0]?.args.at(-1);
|
|
2963
|
-
assert.equal(typeof leaderCmd, "string");
|
|
2964
|
-
const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
|
|
2965
|
-
cwd,
|
|
2966
|
-
env: {
|
|
2967
|
-
...process.env,
|
|
2968
|
-
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
2969
|
-
HOME: cwd,
|
|
2970
|
-
},
|
|
2971
|
-
encoding: "utf-8",
|
|
2972
|
-
});
|
|
2973
|
-
assert.equal(result.status, 143);
|
|
2974
|
-
const log = await readFile(logPath, "utf-8");
|
|
2975
|
-
assert.match(log, /codex:--dangerously-bypass-approvals-and-sandbox/);
|
|
2976
|
-
assert.match(log, /tmux:display-message -p #\{socket_path\}/);
|
|
2977
|
-
assert.match(log, /tmux:show-options -sv extended-keys/);
|
|
2978
|
-
assert.match(log, /tmux:set-option -sq extended-keys always/);
|
|
2979
|
-
assert.match(log, /tmux:set-option -sq extended-keys off/);
|
|
2980
|
-
assert.doesNotMatch(log, /tmux:kill-session -t omx-demo/);
|
|
2981
|
-
}
|
|
2982
|
-
finally {
|
|
2983
|
-
await rm(cwd, { recursive: true, force: true });
|
|
2984
|
-
}
|
|
2985
|
-
});
|
|
2986
|
-
it("detached leader command keeps child startup errors visible instead of killing the session", async () => {
|
|
2987
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-error-"));
|
|
2988
|
-
const fakeBin = join(cwd, "bin");
|
|
2989
|
-
const logPath = join(cwd, "leader.log");
|
|
2990
|
-
try {
|
|
2991
|
-
await mkdir(fakeBin, { recursive: true });
|
|
2992
|
-
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
2993
|
-
printf 'codex-stderr: unsupported startup flag\\n' >&2
|
|
2994
|
-
exit 42
|
|
2995
|
-
`);
|
|
2996
|
-
await chmod(join(fakeBin, "codex"), 0o755);
|
|
2997
|
-
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
2998
|
-
printf 'tmux:%s\\n' "$*" >> "${logPath}"
|
|
2999
|
-
case "$1" in
|
|
3000
|
-
display-message)
|
|
3001
|
-
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
3002
|
-
printf '/tmp/tmux-test.sock\\n'
|
|
3003
|
-
else
|
|
3004
|
-
printf '0\\n'
|
|
3005
|
-
fi
|
|
3006
|
-
;;
|
|
3007
|
-
show-options)
|
|
3008
|
-
printf 'off\\n'
|
|
3009
|
-
;;
|
|
3010
|
-
set-option|kill-session)
|
|
3011
|
-
;;
|
|
3012
|
-
esac
|
|
3013
|
-
exit 0
|
|
3014
|
-
`);
|
|
3015
|
-
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
3016
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", ["--bad-startup-flag"], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
3017
|
-
const leaderCmd = steps[0]?.args.at(-1);
|
|
3018
|
-
assert.equal(typeof leaderCmd, "string");
|
|
3019
|
-
const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
|
|
3020
|
-
cwd,
|
|
3021
|
-
env: {
|
|
3022
|
-
...process.env,
|
|
3023
|
-
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
3024
|
-
HOME: cwd,
|
|
3025
|
-
},
|
|
3026
|
-
input: "\n",
|
|
3027
|
-
encoding: "utf-8",
|
|
3028
|
-
});
|
|
3029
|
-
assert.equal(result.status, 42);
|
|
3030
|
-
assert.match(result.stderr, /codex-stderr: unsupported startup flag/);
|
|
3031
|
-
assert.match(result.stderr, /codex exited with code 42 during startup/);
|
|
3032
|
-
assert.match(result.stderr, /detached tmux session is being kept open/);
|
|
3033
|
-
const log = await readFile(logPath, "utf-8");
|
|
3034
|
-
assert.doesNotMatch(log, /tmux:kill-session -t omx-demo/);
|
|
3035
|
-
}
|
|
3036
|
-
finally {
|
|
3037
|
-
await rm(cwd, { recursive: true, force: true });
|
|
3038
|
-
}
|
|
3039
|
-
});
|
|
3040
|
-
it("detached leader command keeps immediate zero-code exits visible instead of silently closing", async () => {
|
|
3041
|
-
const cwd = await mkdtemp(join(tmpdir(), "omx-detached-leader-zero-"));
|
|
3042
|
-
const fakeBin = join(cwd, "bin");
|
|
3043
|
-
const logPath = join(cwd, "leader.log");
|
|
3044
|
-
try {
|
|
3045
|
-
await mkdir(fakeBin, { recursive: true });
|
|
3046
|
-
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
3047
|
-
printf 'codex-started-then-quit\\n' >&2
|
|
3048
|
-
exit 0
|
|
3049
|
-
`);
|
|
3050
|
-
await chmod(join(fakeBin, "codex"), 0o755);
|
|
3051
|
-
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
3052
|
-
printf 'tmux:%s\\n' "$*" >> "${logPath}"
|
|
3053
|
-
case "$1" in
|
|
3054
|
-
display-message)
|
|
3055
|
-
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
3056
|
-
printf '/tmp/tmux-test.sock\\n'
|
|
3057
|
-
else
|
|
3058
|
-
printf '0\\n'
|
|
3059
|
-
fi
|
|
3060
|
-
;;
|
|
3061
|
-
show-options)
|
|
3062
|
-
printf 'off\\n'
|
|
3063
|
-
;;
|
|
3064
|
-
set-option|kill-session)
|
|
3065
|
-
;;
|
|
3066
|
-
esac
|
|
3067
|
-
exit 0
|
|
3068
|
-
`);
|
|
3069
|
-
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
3070
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", [], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
3071
|
-
const leaderCmd = steps[0]?.args.at(-1);
|
|
3072
|
-
assert.equal(typeof leaderCmd, "string");
|
|
3073
|
-
const result = (await import("node:child_process")).spawnSync("/bin/sh", ["-c", leaderCmd], {
|
|
3074
|
-
cwd,
|
|
3075
|
-
env: {
|
|
3076
|
-
...process.env,
|
|
3077
|
-
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
3078
|
-
HOME: cwd,
|
|
3079
|
-
},
|
|
3080
|
-
input: "\n",
|
|
3081
|
-
encoding: "utf-8",
|
|
3082
|
-
});
|
|
3083
|
-
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
3084
|
-
assert.match(result.stderr, /codex-started-then-quit/);
|
|
3085
|
-
assert.match(result.stderr, /codex exited immediately with code 0 during startup/);
|
|
3086
|
-
assert.match(result.stderr, /detached tmux session is being kept open/);
|
|
3087
|
-
const log = await readFile(logPath, "utf-8");
|
|
3088
|
-
assert.match(log, /tmux:kill-session -t omx-demo/);
|
|
3089
|
-
}
|
|
3090
|
-
finally {
|
|
3091
|
-
await rm(cwd, { recursive: true, force: true });
|
|
3092
|
-
}
|
|
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/);
|
|
3093
3369
|
});
|
|
3094
|
-
it("
|
|
3095
|
-
const
|
|
3096
|
-
const
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
await mkdir(fakeBin, { recursive: true });
|
|
3100
|
-
await writeFile(join(fakeBin, "codex"), `#!/bin/sh
|
|
3101
|
-
echo $$ > "${pidFile}"
|
|
3102
|
-
trap '' HUP
|
|
3103
|
-
while true; do sleep 1; done
|
|
3104
|
-
`);
|
|
3105
|
-
await chmod(join(fakeBin, "codex"), 0o755);
|
|
3106
|
-
await writeFile(join(fakeBin, "tmux"), `#!/bin/sh
|
|
3107
|
-
case "$1" in
|
|
3108
|
-
display-message)
|
|
3109
|
-
if [ "$3" = '#{socket_path}' ] || [ "$4" = '#{socket_path}' ]; then
|
|
3110
|
-
printf '/tmp/tmux-test.sock\\n'
|
|
3111
|
-
else
|
|
3112
|
-
printf '0\\n'
|
|
3113
|
-
fi
|
|
3114
|
-
;;
|
|
3115
|
-
show-options) printf 'off\\n' ;;
|
|
3116
|
-
set-option|kill-session) ;;
|
|
3117
|
-
esac
|
|
3118
|
-
exit 0
|
|
3119
|
-
`);
|
|
3120
|
-
await chmod(join(fakeBin, "tmux"), 0o755);
|
|
3121
|
-
const steps = buildDetachedSessionBootstrapSteps("omx-demo", cwd, buildTmuxPaneCommand("codex", [], "/bin/sh"), "'node' '/tmp/omx.js' 'hud' '--watch'", null);
|
|
3122
|
-
const leaderCmd = steps[0]?.args.at(-1);
|
|
3123
|
-
assert.equal(typeof leaderCmd, "string");
|
|
3124
|
-
const { spawn } = await import("node:child_process");
|
|
3125
|
-
const child = spawn("/bin/sh", ["-c", `exec ${leaderCmd}`], {
|
|
3126
|
-
cwd,
|
|
3127
|
-
env: {
|
|
3128
|
-
...process.env,
|
|
3129
|
-
PATH: `${fakeBin}:/usr/bin:/bin`,
|
|
3130
|
-
HOME: cwd,
|
|
3131
|
-
},
|
|
3132
|
-
stdio: "ignore",
|
|
3133
|
-
detached: true,
|
|
3134
|
-
});
|
|
3135
|
-
try {
|
|
3136
|
-
for (let i = 0; i < 50; i += 1) {
|
|
3137
|
-
if (existsSync(pidFile))
|
|
3138
|
-
break;
|
|
3139
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
3140
|
-
}
|
|
3141
|
-
assert.ok(existsSync(pidFile), "codex pid file not written");
|
|
3142
|
-
const codexPid = Number.parseInt((await readFile(pidFile, "utf-8")).trim(), 10);
|
|
3143
|
-
assert.ok(codexPid > 0, "codex pid must be positive");
|
|
3144
|
-
assert.doesNotThrow(() => process.kill(codexPid, 0), "codex must be alive before signal");
|
|
3145
|
-
const leaderExit = once(child, "exit");
|
|
3146
|
-
process.kill(child.pid, "SIGHUP");
|
|
3147
|
-
await Promise.race([
|
|
3148
|
-
leaderExit,
|
|
3149
|
-
new Promise((_, reject) => setTimeout(() => reject(new Error("leader did not exit after SIGHUP")), 3000)),
|
|
3150
|
-
]);
|
|
3151
|
-
assert.throws(() => process.kill(codexPid, 0), (err) => typeof err === "object" &&
|
|
3152
|
-
err !== null &&
|
|
3153
|
-
"code" in err &&
|
|
3154
|
-
err.code === "ESRCH", "codex child must be terminated after leader SIGHUP");
|
|
3155
|
-
}
|
|
3156
|
-
finally {
|
|
3157
|
-
try {
|
|
3158
|
-
process.kill(child.pid, "SIGKILL");
|
|
3159
|
-
}
|
|
3160
|
-
catch {
|
|
3161
|
-
/* already dead */
|
|
3162
|
-
}
|
|
3163
|
-
}
|
|
3164
|
-
}
|
|
3165
|
-
finally {
|
|
3166
|
-
await rm(cwd, { recursive: true, force: true });
|
|
3167
|
-
}
|
|
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/);
|
|
3168
3375
|
});
|
|
3169
3376
|
it("withTmuxExtendedKeys enables tmux extended keys during codex launch and restores them afterwards", async () => {
|
|
3170
3377
|
const cwd = await mkdtemp(join(tmpdir(), "omx-tmux-lease-wrapper-"));
|
|
@@ -3719,6 +3926,45 @@ describe("buildWindowsPromptCommand", () => {
|
|
|
3719
3926
|
assert.equal(decoded, "$ErrorActionPreference = 'Stop'; & { & 'codex' '--dangerously-bypass-approvals-and-sandbox' '-c' 'model_reasoning_effort=\"high\"' 'it''s' }");
|
|
3720
3927
|
});
|
|
3721
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
|
+
});
|
|
3722
3968
|
describe("buildTmuxSessionName", () => {
|
|
3723
3969
|
it("uses detached fallback quietly outside git repos", () => {
|
|
3724
3970
|
const name = buildTmuxSessionName("/tmp/My Repo", "omx-1770992424158-abc123");
|
|
@@ -3940,6 +4186,26 @@ describe("injectModelInstructionsBypassArgs", () => {
|
|
|
3940
4186
|
'model_instructions_file="/tmp/my-project/AGENTS.md"',
|
|
3941
4187
|
]);
|
|
3942
4188
|
});
|
|
4189
|
+
it("inserts model instructions before the end-of-options marker", () => {
|
|
4190
|
+
const args = injectModelInstructionsBypassArgs("/tmp/my-project", ["--", "--spark", "literal"], {});
|
|
4191
|
+
assert.deepEqual(args, [
|
|
4192
|
+
"-c",
|
|
4193
|
+
'model_instructions_file="/tmp/my-project/AGENTS.md"',
|
|
4194
|
+
"--",
|
|
4195
|
+
"--spark",
|
|
4196
|
+
"literal",
|
|
4197
|
+
]);
|
|
4198
|
+
});
|
|
4199
|
+
it("does not treat a post-marker model instructions token as an OMX override", () => {
|
|
4200
|
+
const args = injectModelInstructionsBypassArgs("/tmp/my-project", ["--", "-c", 'model_instructions_file="/tmp/literal.md"'], {});
|
|
4201
|
+
assert.deepEqual(args, [
|
|
4202
|
+
"-c",
|
|
4203
|
+
'model_instructions_file="/tmp/my-project/AGENTS.md"',
|
|
4204
|
+
"--",
|
|
4205
|
+
"-c",
|
|
4206
|
+
'model_instructions_file="/tmp/literal.md"',
|
|
4207
|
+
]);
|
|
4208
|
+
});
|
|
3943
4209
|
it("does not append when bypass is disabled via env", () => {
|
|
3944
4210
|
const args = injectModelInstructionsBypassArgs("/tmp/my-project", ["--model", "gpt-5"], { OMX_BYPASS_DEFAULT_SYSTEM_PROMPT: "0" });
|
|
3945
4211
|
assert.deepEqual(args, ["--model", "gpt-5"]);
|
|
@@ -4002,4 +4268,153 @@ describe("isExistingTmuxWindowTooCrampedForLaunchHud (#2754)", () => {
|
|
|
4002
4268
|
assert.equal(isExistingTmuxWindowTooCrampedForLaunchHud(39, 40), true);
|
|
4003
4269
|
});
|
|
4004
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
|
+
});
|
|
4005
4420
|
//# sourceMappingURL=index.test.js.map
|