oh-my-codex 0.20.1 → 0.20.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +7 -6
- package/Cargo.toml +1 -1
- package/README.md +36 -1
- package/crates/omx-runtime/Cargo.toml +1 -0
- package/crates/omx-runtime/src/main.rs +25 -2
- package/crates/omx-runtime/tests/execution.rs +219 -0
- package/crates/omx-runtime-core/src/dispatch.rs +96 -10
- package/crates/omx-runtime-core/src/engine.rs +839 -29
- package/crates/omx-runtime-core/src/lib.rs +8 -0
- package/dist/adapt/__tests__/hermes.test.js.map +1 -1
- package/dist/adapt/contracts.d.ts.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.d.ts +2 -0
- package/dist/auth/__tests__/hotswap.test.d.ts.map +1 -0
- package/dist/auth/__tests__/hotswap.test.js +183 -0
- package/dist/auth/__tests__/hotswap.test.js.map +1 -0
- package/dist/auth/hotswap.d.ts +6 -0
- package/dist/auth/hotswap.d.ts.map +1 -1
- package/dist/auth/hotswap.js +102 -19
- package/dist/auth/hotswap.js.map +1 -1
- package/dist/auth/paths.js.map +1 -1
- package/dist/auth/storage.js.map +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js +5 -5
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/autopilot/completion-gate.js.map +1 -1
- package/dist/autopilot/fsm.d.ts +1 -1
- package/dist/autopilot/fsm.d.ts.map +1 -1
- package/dist/autoresearch/goal.js.map +1 -1
- package/dist/autoresearch/runtime.js.map +1 -1
- package/dist/capabilities/lockfile.js.map +1 -1
- package/dist/catalog/reader.js.map +1 -1
- package/dist/catalog/skill-mirror.js.map +1 -1
- package/dist/cli/__tests__/adapt-help.test.js.map +1 -1
- package/dist/cli/__tests__/agents-init.test.js.map +1 -1
- package/dist/cli/__tests__/agents.test.js.map +1 -1
- package/dist/cli/__tests__/api.test.js.map +1 -1
- package/dist/cli/__tests__/ask.test.js.map +1 -1
- package/dist/cli/__tests__/auth.test.js +60 -6
- package/dist/cli/__tests__/auth.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch-guided.test.js.map +1 -1
- package/dist/cli/__tests__/autoresearch.test.js.map +1 -1
- package/dist/cli/__tests__/capabilities.test.js.map +1 -1
- package/dist/cli/__tests__/codex-plugin-layout.test.js +80 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-invalid-config.test.js +47 -4
- package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-team.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +752 -15
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/exec.test.js +136 -0
- package/dist/cli/__tests__/exec.test.js.map +1 -1
- package/dist/cli/__tests__/imagegen-continuation.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +473 -14
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +336 -4
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +87 -12
- package/dist/cli/__tests__/mcp-parity.test.js.map +1 -1
- package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts +2 -0
- package/dist/cli/__tests__/native-hook-claim-journal.test.d.ts.map +1 -0
- package/dist/cli/__tests__/native-hook-claim-journal.test.js +206 -0
- package/dist/cli/__tests__/native-hook-claim-journal.test.js.map +1 -0
- package/dist/cli/__tests__/nested-help-routing.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-deslop-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js +6 -0
- package/dist/cli/__tests__/ralph-goal-mode-contract.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-prd-deep-interview.test.js.map +1 -1
- package/dist/cli/__tests__/ralph-prd-smoke.test.js.map +1 -1
- package/dist/cli/__tests__/ralph.test.js +8 -3
- package/dist/cli/__tests__/ralph.test.js.map +1 -1
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts +2 -0
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.d.ts.map +1 -0
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js +72 -0
- package/dist/cli/__tests__/ralplan-bootstrap-3181.test.js.map +1 -0
- package/dist/cli/__tests__/ralplan.test.d.ts +2 -0
- package/dist/cli/__tests__/ralplan.test.d.ts.map +1 -0
- package/dist/cli/__tests__/ralplan.test.js +55 -0
- package/dist/cli/__tests__/ralplan.test.js.map +1 -0
- package/dist/cli/__tests__/resume.test.js +17 -11
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.js +25 -0
- 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__/session-search.test.js.map +1 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js +79 -0
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-gh-star.test.js.map +1 -1
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js +77 -3
- package/dist/cli/__tests__/setup-hooks-shared-ownership.test.js.map +1 -1
- package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts +2 -0
- package/dist/cli/__tests__/setup-hooks-trust-e2e.test.d.ts.map +1 -0
- package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js +285 -0
- package/dist/cli/__tests__/setup-hooks-trust-e2e.test.js.map +1 -0
- package/dist/cli/__tests__/setup-install-mode.test.js +2579 -294
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-preferences.test.d.ts +2 -0
- package/dist/cli/__tests__/setup-preferences.test.d.ts.map +1 -0
- package/dist/cli/__tests__/setup-preferences.test.js +79 -0
- package/dist/cli/__tests__/setup-preferences.test.js.map +1 -0
- package/dist/cli/__tests__/setup-refresh.test.js +33 -2
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +46 -3
- package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
- package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
- package/dist/cli/__tests__/state.test.js +37 -0
- package/dist/cli/__tests__/state.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +105 -7
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +61 -1
- package/dist/cli/__tests__/ultragoal.test.js.map +1 -1
- package/dist/cli/__tests__/uninstall.test.js +1817 -24
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/__tests__/update.test.js +58 -0
- package/dist/cli/__tests__/update.test.js.map +1 -1
- package/dist/cli/__tests__/url.test.js.map +1 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js +2 -1
- package/dist/cli/__tests__/windows-popup-loop-contract.test.js.map +1 -1
- package/dist/cli/adapt.js.map +1 -1
- package/dist/cli/agents-init.js.map +1 -1
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/api.js.map +1 -1
- package/dist/cli/ask.d.ts +1 -1
- package/dist/cli/ask.d.ts.map +1 -1
- package/dist/cli/ask.js.map +1 -1
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/autoresearch-guided.js.map +1 -1
- package/dist/cli/autoresearch-intake.js.map +1 -1
- package/dist/cli/capabilities.js.map +1 -1
- package/dist/cli/cleanup.js.map +1 -1
- package/dist/cli/codex-feature-probe.js.map +1 -1
- package/dist/cli/codex-home.js.map +1 -1
- package/dist/cli/doctor.d.ts +21 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +607 -130
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +14 -3
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +245 -57
- 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/mcp-serve.js.map +1 -1
- package/dist/cli/mission.js.map +1 -1
- package/dist/cli/native-assets.js.map +1 -1
- package/dist/cli/native-hook-claim-journal.d.ts +29 -0
- package/dist/cli/native-hook-claim-journal.d.ts.map +1 -0
- package/dist/cli/native-hook-claim-journal.js +252 -0
- package/dist/cli/native-hook-claim-journal.js.map +1 -0
- package/dist/cli/omx.js.map +1 -1
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/project-runtime-codex-homes.js.map +1 -1
- package/dist/cli/question.js.map +1 -1
- package/dist/cli/ralph.d.ts.map +1 -1
- package/dist/cli/ralph.js +3 -2
- package/dist/cli/ralph.js.map +1 -1
- package/dist/cli/ralplan.d.ts +10 -0
- package/dist/cli/ralplan.d.ts.map +1 -0
- package/dist/cli/ralplan.js +110 -0
- package/dist/cli/ralplan.js.map +1 -0
- 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 +11 -0
- package/dist/cli/setup-preferences.d.ts.map +1 -1
- package/dist/cli/setup-preferences.js +32 -0
- package/dist/cli/setup-preferences.js.map +1 -1
- package/dist/cli/setup.d.ts +58 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +1450 -398
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/sparkshell.js.map +1 -1
- package/dist/cli/star-prompt.js.map +1 -1
- package/dist/cli/state.d.ts.map +1 -1
- package/dist/cli/state.js +2 -1
- package/dist/cli/state.js.map +1 -1
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/ultragoal.d.ts +5 -1
- package/dist/cli/ultragoal.d.ts.map +1 -1
- package/dist/cli/ultragoal.js +34 -2
- package/dist/cli/ultragoal.js.map +1 -1
- package/dist/cli/uninstall.d.ts +17 -0
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +986 -95
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/cli/update.d.ts.map +1 -1
- package/dist/cli/update.js +8 -1
- package/dist/cli/update.js.map +1 -1
- package/dist/cli/url.js.map +1 -1
- package/dist/compat/__tests__/doctor-contract.test.js +5 -1
- package/dist/compat/__tests__/doctor-contract.test.js.map +1 -1
- package/dist/compat/__tests__/rust-runtime-compat.test.js +1 -1
- package/dist/compat/__tests__/rust-runtime-compat.test.js.map +1 -1
- package/dist/config/__tests__/codex-hooks.test.js +1287 -11
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +1179 -32
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +6 -1
- 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/codex-feature-flags.d.ts.map +1 -1
- package/dist/config/codex-feature-flags.js.map +1 -1
- package/dist/config/codex-hooks.d.ts +124 -1
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +2142 -265
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/commit-lore-guard.js.map +1 -1
- package/dist/config/generator.d.ts +23 -34
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +1480 -246
- package/dist/config/generator.js.map +1 -1
- package/dist/config/mcp-registry.js.map +1 -1
- package/dist/config/models.d.ts +14 -4
- 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/config/omx-first-party-mcp.js.map +1 -1
- package/dist/config/team-mode.d.ts.map +1 -1
- package/dist/document-refresh/__tests__/enforcer.test.js.map +1 -1
- package/dist/document-refresh/enforcer.js.map +1 -1
- package/dist/exec/followup.js.map +1 -1
- package/dist/goal-workflows/artifacts.js.map +1 -1
- package/dist/hooks/__tests__/agents-overlay.test.js +7 -4
- package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
- package/dist/hooks/__tests__/analyze-routing-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/analyze-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/anti-slop-workflow.test.js.map +1 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +9 -3
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/code-review-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
- package/dist/hooks/__tests__/deep-interview-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/design-skill.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +1376 -6
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +252 -543
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js +38 -4
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +765 -10
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-cross-worktree-heartbeat.test.js +2 -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 +142 -1
- package/dist/hooks/__tests__/notify-hook-managed-tmux.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-modules.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js +48 -2
- package/dist/hooks/__tests__/notify-hook-non-omx-guard.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +61 -44
- package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-regression-205.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js +3 -2
- package/dist/hooks/__tests__/notify-hook-session-idle-dedupe.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js +243 -4
- package/dist/hooks/__tests__/notify-hook-session-scope.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js +531 -12
- package/dist/hooks/__tests__/notify-hook-team-dispatch.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +163 -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-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-visual-verdict.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js +43 -4
- package/dist/hooks/__tests__/notify-hook-worker-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/openclaw-setup-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/pre-context-gate-skills.test.js.map +1 -1
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-fragments.test.js.map +1 -1
- package/dist/hooks/__tests__/prompt-guidance-test-helpers.js.map +1 -1
- package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts +2 -0
- package/dist/hooks/__tests__/prompt-session-provenance.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/prompt-session-provenance.test.js +82 -0
- package/dist/hooks/__tests__/prompt-session-provenance.test.js.map +1 -0
- package/dist/hooks/__tests__/session.test.js +878 -11
- package/dist/hooks/__tests__/session.test.js.map +1 -1
- package/dist/hooks/__tests__/skill-catalog-hygiene.test.js.map +1 -1
- package/dist/hooks/__tests__/team-runtime-gating-docs-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/visual-ralph-skill.test.js.map +1 -1
- package/dist/hooks/__tests__/visual-verdict-loop.test.js.map +1 -1
- package/dist/hooks/agents-overlay.js +5 -5
- package/dist/hooks/agents-overlay.js.map +1 -1
- package/dist/hooks/code-simplifier/index.js.map +1 -1
- package/dist/hooks/explore-routing.js.map +1 -1
- package/dist/hooks/extensibility/dispatcher.js.map +1 -1
- package/dist/hooks/extensibility/events.js.map +1 -1
- package/dist/hooks/extensibility/loader.js.map +1 -1
- package/dist/hooks/extensibility/plugin-runner-stdin.js.map +1 -1
- package/dist/hooks/extensibility/sdk/logging.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +33 -7
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +2228 -119
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/keyword-registry.d.ts +12 -0
- package/dist/hooks/keyword-registry.d.ts.map +1 -1
- package/dist/hooks/keyword-registry.js +41 -0
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hooks/prompt-session-provenance.d.ts +87 -0
- package/dist/hooks/prompt-session-provenance.d.ts.map +1 -0
- package/dist/hooks/prompt-session-provenance.js +180 -0
- package/dist/hooks/prompt-session-provenance.js.map +1 -0
- package/dist/hooks/session.d.ts +163 -34
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +1395 -317
- package/dist/hooks/session.js.map +1 -1
- package/dist/hooks/task-size-detector.js.map +1 -1
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/authority.js.map +1 -1
- package/dist/hud/constants.js.map +1 -1
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/tmux.js.map +1 -1
- package/dist/leader/__tests__/contract.test.js +153 -1
- package/dist/leader/__tests__/contract.test.js.map +1 -1
- package/dist/leader/contract.d.ts +42 -5
- package/dist/leader/contract.d.ts.map +1 -1
- package/dist/leader/contract.js +184 -3
- package/dist/leader/contract.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.test.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__/server-lifecycle.test.js +15 -2
- package/dist/mcp/__tests__/server-lifecycle.test.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +98 -1
- package/dist/mcp/__tests__/state-paths.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/bootstrap.js.map +1 -1
- package/dist/mcp/code-intel-server.d.ts +58 -58
- 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.js.map +1 -1
- package/dist/mcp/hermes-server.d.ts +70 -70
- package/dist/mcp/hermes-server.d.ts.map +1 -1
- package/dist/mcp/lifecycle-telemetry.js.map +1 -1
- package/dist/mcp/memory-server.d.ts +34 -34
- package/dist/mcp/memory-server.d.ts.map +1 -1
- package/dist/mcp/memory-server.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +14 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +96 -20
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/mcp/state-server.d.ts +47 -47
- package/dist/mcp/state-server.d.ts.map +1 -1
- package/dist/mcp/trace-server.d.ts +4 -4
- package/dist/mcp/trace-server.d.ts.map +1 -1
- package/dist/mcp/trace-server.js.map +1 -1
- package/dist/mcp/wiki-server.d.ts +34 -23
- 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-session-scope.test.js +53 -1
- 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.map +1 -1
- package/dist/modes/base.js +5 -5
- package/dist/modes/base.js.map +1 -1
- package/dist/notifications/__tests__/lifecycle-dedupe.test.js.map +1 -1
- package/dist/notifications/__tests__/reply-listener.test.js.map +1 -1
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js +177 -85
- package/dist/notifications/__tests__/session-idle-tail-dedupe.test.js.map +1 -1
- package/dist/notifications/__tests__/session-registry.test.js.map +1 -1
- package/dist/notifications/__tests__/template-engine.test.js.map +1 -1
- package/dist/notifications/__tests__/verbosity.test.js.map +1 -1
- package/dist/notifications/http-client.js.map +1 -1
- package/dist/notifications/index.d.ts +4 -1
- package/dist/notifications/index.d.ts.map +1 -1
- package/dist/notifications/index.js +3 -9
- package/dist/notifications/index.js.map +1 -1
- package/dist/notifications/lifecycle-dedupe.js.map +1 -1
- package/dist/notifications/reply-listener.d.ts.map +1 -1
- package/dist/notifications/reply-listener.js.map +1 -1
- package/dist/notifications/session-registry.js.map +1 -1
- package/dist/notifications/session-status.js.map +1 -1
- package/dist/notifications/temp-contract.d.ts.map +1 -1
- package/dist/notifications/temp-contract.js +4 -0
- package/dist/notifications/temp-contract.js.map +1 -1
- package/dist/notifications/tmux-detector.js.map +1 -1
- package/dist/notifications/tmux.js.map +1 -1
- package/dist/openclaw/config.js.map +1 -1
- package/dist/openclaw/dispatcher.js.map +1 -1
- package/dist/pipeline/__tests__/orchestrator.test.js +4 -4
- package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +16 -13
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/stages/code-review.js.map +1 -1
- package/dist/pipeline/stages/ralph-verify.js.map +1 -1
- package/dist/pipeline/stages/ralplan.js.map +1 -1
- package/dist/pipeline/stages/team-exec.js.map +1 -1
- package/dist/pipeline/stages/ultragoal.js.map +1 -1
- package/dist/pipeline/stages/ultraqa.js.map +1 -1
- package/dist/planning/__tests__/approved-execution-lifecycle-matrix.test.js.map +1 -1
- package/dist/planning/artifact-names.js.map +1 -1
- package/dist/planning/artifacts.js.map +1 -1
- package/dist/question/__tests__/state.test.js.map +1 -1
- package/dist/question/__tests__/ui.test.js.map +1 -1
- package/dist/question/autopilot-wait.js.map +1 -1
- package/dist/question/client.d.ts.map +1 -1
- package/dist/question/client.js.map +1 -1
- package/dist/question/deep-interview.js.map +1 -1
- package/dist/question/events.js.map +1 -1
- package/dist/question/renderer.js.map +1 -1
- package/dist/question/state.d.ts.map +1 -1
- package/dist/question/state.js.map +1 -1
- package/dist/question/ui.js.map +1 -1
- package/dist/ralph/contract.d.ts +1 -1
- package/dist/ralph/contract.d.ts.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js +527 -8
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts +2 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.d.ts.map +1 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js +46 -0
- package/dist/ralplan/__tests__/documented-leader-preflight.test.js.map +1 -0
- package/dist/ralplan/__tests__/runtime.test.js +116 -14
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts +3 -3
- package/dist/ralplan/consensus-gate.d.ts.map +1 -1
- package/dist/ralplan/consensus-gate.js +173 -70
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/ralplan/documented-leader-preflight.d.ts +34 -0
- package/dist/ralplan/documented-leader-preflight.d.ts.map +1 -0
- package/dist/ralplan/documented-leader-preflight.js +58 -0
- package/dist/ralplan/documented-leader-preflight.js.map +1 -0
- package/dist/ralplan/runtime.d.ts +3 -2
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +12 -9
- 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/process-tree.js.map +1 -1
- package/dist/runtime/run-loop.js.map +1 -1
- package/dist/runtime/run-outcome.d.ts +3 -3
- package/dist/runtime/run-outcome.d.ts.map +1 -1
- package/dist/runtime/run-outcome.js.map +1 -1
- package/dist/runtime/run-state.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +6657 -995
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts +2 -0
- package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.d.ts.map +1 -0
- package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js +634 -0
- package/dist/scripts/__tests__/fixtures/issue-3138-installed-smoke.js.map +1 -0
- package/dist/scripts/__tests__/generate-release-body.test.js.map +1 -1
- package/dist/scripts/__tests__/hook-derived-watcher.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
- package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts +2 -0
- package/dist/scripts/__tests__/notify-fallback-delivery.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/notify-fallback-delivery.test.js +174 -0
- package/dist/scripts/__tests__/notify-fallback-delivery.test.js.map +1 -0
- package/dist/scripts/__tests__/notify-state-io.test.js +57 -1
- package/dist/scripts/__tests__/notify-state-io.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.d.ts +2 -0
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js +78 -0
- package/dist/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.js.map +1 -0
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts +2 -0
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js +55 -0
- package/dist/scripts/__tests__/role-intent-durable-recovery-3181.test.js.map +1 -0
- package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
- package/dist/scripts/__tests__/smoke-packed-install.test.js +1100 -4
- package/dist/scripts/__tests__/smoke-packed-install.test.js.map +1 -1
- package/dist/scripts/__tests__/test-reply-listener-live.test.js.map +1 -1
- package/dist/scripts/build-api.js.map +1 -1
- package/dist/scripts/build-explore-harness.js.map +1 -1
- package/dist/scripts/build-sparkshell.js.map +1 -1
- package/dist/scripts/cleanup-explore-harness.js.map +1 -1
- package/dist/scripts/codex-execution-surface.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts +4 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +11577 -1184
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/generate-native-release-manifest.js.map +1 -1
- package/dist/scripts/generate-release-body.js.map +1 -1
- package/dist/scripts/hook-payload-guard.d.ts.map +1 -1
- package/dist/scripts/hook-payload-guard.js.map +1 -1
- package/dist/scripts/notify-fallback-delivery.d.ts +48 -0
- package/dist/scripts/notify-fallback-delivery.d.ts.map +1 -0
- package/dist/scripts/notify-fallback-delivery.js +419 -0
- package/dist/scripts/notify-fallback-delivery.js.map +1 -0
- package/dist/scripts/notify-fallback-watcher.js +218 -138
- package/dist/scripts/notify-fallback-watcher.js.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.d.ts +18 -9
- package/dist/scripts/notify-hook/auto-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/auto-nudge.js +112 -15
- package/dist/scripts/notify-hook/auto-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.d.ts +23 -0
- package/dist/scripts/notify-hook/managed-tmux.d.ts.map +1 -1
- package/dist/scripts/notify-hook/managed-tmux.js +122 -12
- package/dist/scripts/notify-hook/managed-tmux.js.map +1 -1
- package/dist/scripts/notify-hook/orchestration-intent.d.ts +7 -7
- package/dist/scripts/notify-hook/orchestration-intent.d.ts.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts +5 -2
- package/dist/scripts/notify-hook/ralph-session-resume.d.ts.map +1 -1
- package/dist/scripts/notify-hook/ralph-session-resume.js +33 -37
- package/dist/scripts/notify-hook/ralph-session-resume.js.map +1 -1
- package/dist/scripts/notify-hook/state-io.d.ts +17 -0
- package/dist/scripts/notify-hook/state-io.d.ts.map +1 -1
- package/dist/scripts/notify-hook/state-io.js +73 -11
- package/dist/scripts/notify-hook/state-io.js.map +1 -1
- package/dist/scripts/notify-hook/team-dispatch.d.ts +10 -1
- 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 +2 -2
- package/dist/scripts/notify-hook/team-leader-nudge.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-leader-nudge.js +39 -13
- package/dist/scripts/notify-hook/team-leader-nudge.js.map +1 -1
- package/dist/scripts/notify-hook/team-tmux-guard.d.ts +16 -8
- 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-posttooluse.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.d.ts +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker-stop.js +19 -18
- package/dist/scripts/notify-hook/team-worker-stop.js.map +1 -1
- package/dist/scripts/notify-hook/team-worker.d.ts +5 -2
- package/dist/scripts/notify-hook/team-worker.d.ts.map +1 -1
- package/dist/scripts/notify-hook/team-worker.js +37 -28
- package/dist/scripts/notify-hook/team-worker.js.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts +8 -1
- package/dist/scripts/notify-hook/tmux-injection.d.ts.map +1 -1
- package/dist/scripts/notify-hook/tmux-injection.js +53 -18
- package/dist/scripts/notify-hook/tmux-injection.js.map +1 -1
- package/dist/scripts/notify-hook/visual-verdict.d.ts +1 -1
- package/dist/scripts/notify-hook/visual-verdict.d.ts.map +1 -1
- package/dist/scripts/notify-hook/visual-verdict.js +15 -13
- package/dist/scripts/notify-hook/visual-verdict.js.map +1 -1
- package/dist/scripts/notify-hook.d.ts +24 -1
- package/dist/scripts/notify-hook.d.ts.map +1 -1
- package/dist/scripts/notify-hook.js +267 -90
- package/dist/scripts/notify-hook.js.map +1 -1
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/scripts/prompt-inventory.js.map +1 -1
- package/dist/scripts/smoke-packed-install.d.ts +927 -5
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +4317 -13
- package/dist/scripts/smoke-packed-install.js.map +1 -1
- package/dist/scripts/sync-plugin-mirror.js.map +1 -1
- package/dist/scripts/test-reply-listener-live.js.map +1 -1
- package/dist/scripts/test-sparkshell.js.map +1 -1
- package/dist/scripts/verify-native-agents.js.map +1 -1
- package/dist/session-history/friction.js.map +1 -1
- package/dist/session-history/search.js.map +1 -1
- package/dist/sidecar/__tests__/boundary.test.js.map +1 -1
- package/dist/sidecar/collector.js.map +1 -1
- package/dist/sidecar/index.js.map +1 -1
- package/dist/sidecar/render.js.map +1 -1
- package/dist/sidecar/tmux.js.map +1 -1
- package/dist/state/__tests__/mode-state-context.test.js +54 -0
- package/dist/state/__tests__/mode-state-context.test.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +66 -15
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +19 -1
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +61 -0
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/mode-state-context.d.ts +5 -0
- package/dist/state/mode-state-context.d.ts.map +1 -1
- package/dist/state/mode-state-context.js +59 -0
- package/dist/state/mode-state-context.js.map +1 -1
- package/dist/state/operations.d.ts +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +49 -55
- package/dist/state/operations.js.map +1 -1
- package/dist/state/paths.d.ts +1 -1
- package/dist/state/paths.d.ts.map +1 -1
- package/dist/state/paths.js +1 -1
- package/dist/state/paths.js.map +1 -1
- package/dist/state/skill-active.d.ts +13 -1
- package/dist/state/skill-active.d.ts.map +1 -1
- package/dist/state/skill-active.js +43 -4
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/terminal-normalization.js.map +1 -1
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +20 -4
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/state/workflow-transition.d.ts +1 -1
- package/dist/state/workflow-transition.d.ts.map +1 -1
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts +2 -0
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.d.ts.map +1 -0
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.js +52 -0
- package/dist/subagents/__tests__/leader-bootstrap-3181.test.js.map +1 -0
- package/dist/subagents/__tests__/tracker.test.js +1550 -10
- package/dist/subagents/__tests__/tracker.test.js.map +1 -1
- package/dist/subagents/role-routing-marker.d.ts +10 -0
- package/dist/subagents/role-routing-marker.d.ts.map +1 -0
- package/dist/subagents/role-routing-marker.js +136 -0
- package/dist/subagents/role-routing-marker.js.map +1 -0
- package/dist/subagents/tracker.d.ts +91 -0
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +806 -62
- 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__/approved-execution.test.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js +52 -10
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
- package/dist/team/__tests__/idle-nudge.test.js +45 -15
- package/dist/team/__tests__/idle-nudge.test.js.map +1 -1
- package/dist/team/__tests__/mcp-comm.test.js +93 -27
- package/dist/team/__tests__/mcp-comm.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +341 -4
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/role-router.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 +3624 -355
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +2516 -118
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- 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 +3664 -969
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/tmux-test-fixture.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +38 -2
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/__tests__/worktree.test.js +6 -0
- package/dist/team/__tests__/worktree.test.js.map +1 -1
- package/dist/team/api-interop.d.ts +2 -2
- 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/commit-hygiene.js.map +1 -1
- package/dist/team/contracts.d.ts +5 -5
- package/dist/team/contracts.d.ts.map +1 -1
- package/dist/team/delivery-log.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/followup-planner.js.map +1 -1
- package/dist/team/goal-workflow.js.map +1 -1
- 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 +18 -2
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +314 -35
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/orchestrator.js.map +1 -1
- package/dist/team/pane-status.js.map +1 -1
- package/dist/team/phase-controller.js.map +1 -1
- package/dist/team/reminder-intents.d.ts +1 -1
- package/dist/team/reminder-intents.d.ts.map +1 -1
- package/dist/team/reminder-intents.js.map +1 -1
- package/dist/team/runtime-cli.js.map +1 -1
- package/dist/team/runtime.d.ts +1 -0
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +1326 -231
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts +22 -8
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +1463 -376
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/dispatch-lock.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/events.js.map +1 -1
- package/dist/team/state/locks.d.ts.map +1 -1
- package/dist/team/state/locks.js +61 -13
- package/dist/team/state/locks.js.map +1 -1
- package/dist/team/state/tasks.d.ts.map +1 -1
- package/dist/team/state/tasks.js +4 -3
- package/dist/team/state/tasks.js.map +1 -1
- package/dist/team/state-root.js.map +1 -1
- package/dist/team/state.d.ts +67 -1
- package/dist/team/state.d.ts.map +1 -1
- package/dist/team/state.js +636 -129
- package/dist/team/state.js.map +1 -1
- package/dist/team/team-identity.d.ts.map +1 -1
- package/dist/team/team-identity.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 +125 -23
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +1340 -333
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.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.map +1 -1
- package/dist/ultragoal/__tests__/docs-contract.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +4 -1
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/url-reader/index.js.map +1 -1
- package/dist/utils/__tests__/file-durability.test.d.ts +2 -0
- package/dist/utils/__tests__/file-durability.test.d.ts.map +1 -0
- package/dist/utils/__tests__/file-durability.test.js +57 -0
- package/dist/utils/__tests__/file-durability.test.js.map +1 -0
- package/dist/utils/agents-model-table.js.map +1 -1
- package/dist/utils/file-durability.d.ts +15 -0
- package/dist/utils/file-durability.d.ts.map +1 -0
- package/dist/utils/file-durability.js +37 -0
- package/dist/utils/file-durability.js.map +1 -0
- package/dist/utils/package.js.map +1 -1
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/platform-command.js.map +1 -1
- package/dist/utils/repo-deps.js.map +1 -1
- package/dist/utils/version.js.map +1 -1
- package/dist/verification/__tests__/ci-rust-gates.test.js +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/verifier.js.map +1 -1
- package/dist/visual/constants.d.ts +1 -1
- package/dist/visual/constants.d.ts.map +1 -1
- package/dist/vscode/__tests__/index.test.js.map +1 -1
- package/dist/vscode/index.js.map +1 -1
- package/dist/wiki/__tests__/cjk-tokenize.test.js.map +1 -1
- package/dist/wiki/__tests__/lint.test.js.map +1 -1
- package/dist/wiki/__tests__/query.test.js.map +1 -1
- package/dist/wiki/__tests__/storage.test.js.map +1 -1
- package/dist/wiki/__tests__/test-helpers.d.ts.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/dist/wiki/query.js.map +1 -1
- package/dist/wiki/storage.js.map +1 -1
- package/package.json +2 -2
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +24 -0
- package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +10 -5
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +6 -2
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +6 -2
- package/plugins/oh-my-codex/skills/team/SKILL.md +13 -9
- package/skills/omx-setup/SKILL.md +10 -5
- package/skills/ralph/SKILL.md +6 -2
- package/skills/ralplan/SKILL.md +6 -2
- package/skills/team/SKILL.md +13 -9
- package/src/scripts/__tests__/codex-native-hook.test.ts +20048 -10827
- package/src/scripts/__tests__/fixtures/issue-3138-installed-smoke.ts +734 -0
- package/src/scripts/__tests__/notify-fallback-delivery.test.ts +177 -0
- package/src/scripts/__tests__/notify-state-io.test.ts +73 -0
- package/src/scripts/__tests__/reasoning-artifact-contract.test.ts +129 -0
- package/src/scripts/__tests__/role-intent-bootstrap-e2e-3181.test.ts +78 -0
- package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +60 -0
- package/src/scripts/__tests__/smoke-packed-install.test.ts +1309 -4
- package/src/scripts/codex-native-hook.ts +11429 -1163
- package/src/scripts/notify-fallback-delivery.ts +416 -0
- package/src/scripts/notify-fallback-watcher.ts +216 -155
- package/src/scripts/notify-hook/auto-nudge.ts +113 -14
- package/src/scripts/notify-hook/managed-tmux.ts +149 -12
- package/src/scripts/notify-hook/ralph-session-resume.ts +37 -36
- package/src/scripts/notify-hook/state-io.ts +100 -13
- package/src/scripts/notify-hook/team-dispatch.ts +242 -75
- package/src/scripts/notify-hook/team-leader-nudge.ts +40 -18
- package/src/scripts/notify-hook/team-tmux-guard.ts +366 -39
- package/src/scripts/notify-hook/team-worker-stop.ts +20 -16
- package/src/scripts/notify-hook/team-worker.ts +39 -26
- package/src/scripts/notify-hook/tmux-injection.ts +66 -23
- package/src/scripts/notify-hook/visual-verdict.ts +22 -12
- package/src/scripts/notify-hook.ts +336 -90
- package/src/scripts/smoke-packed-install.ts +5154 -149
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { describe, it } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import {
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { once } from 'node:events';
|
|
4
5
|
import { existsSync } from 'node:fs';
|
|
6
|
+
import { mkdir, mkdtemp, readFile, readdir, rename, rm, rmdir, symlink, writeFile } from 'node:fs/promises';
|
|
5
7
|
import { tmpdir } from 'node:os';
|
|
6
8
|
import { join } from 'node:path';
|
|
7
|
-
import {
|
|
9
|
+
import { __createDefaultPidProbeForTests, appendPromptSessionProvenanceRejection, __resetSessionPointerTransactionDependenciesForTests, __setSessionPointerTransactionDependenciesForTests, inspectSessionPointerLock, isSessionPointerLaunchAbort, isSessionStale, readSessionPointer, readSessionState, readUsableSessionState, reconcileNativeSessionStart, recoverSessionPointerLock, resetSessionMetrics, resolveSessionPointerContext, writeSessionEnd, writeSessionStart, } from '../session.js';
|
|
8
10
|
function todayIsoDate() {
|
|
9
11
|
return new Date().toISOString().slice(0, 10);
|
|
10
12
|
}
|
|
@@ -17,6 +19,66 @@ function makeState(overrides = {}) {
|
|
|
17
19
|
...overrides,
|
|
18
20
|
};
|
|
19
21
|
}
|
|
22
|
+
const TEST_TOKEN = 'transaction_token_123456';
|
|
23
|
+
const FOREIGN_TOKEN = 'foreign_token_123456789';
|
|
24
|
+
const SUCCESSOR_TOKEN = 'successor_token_123456789';
|
|
25
|
+
function codedError(code) {
|
|
26
|
+
return Object.assign(new Error(code), { code });
|
|
27
|
+
}
|
|
28
|
+
async function withPointerDependencies(overrides, run) {
|
|
29
|
+
__setSessionPointerTransactionDependenciesForTests(overrides);
|
|
30
|
+
try {
|
|
31
|
+
await run();
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
__resetSessionPointerTransactionDependenciesForTests();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
async function withOwnerEnvironment(sessionId, run) {
|
|
38
|
+
const previous = process.env.OMX_SESSION_ID;
|
|
39
|
+
if (sessionId === undefined)
|
|
40
|
+
delete process.env.OMX_SESSION_ID;
|
|
41
|
+
else
|
|
42
|
+
process.env.OMX_SESSION_ID = sessionId;
|
|
43
|
+
try {
|
|
44
|
+
await run();
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
if (previous === undefined)
|
|
48
|
+
delete process.env.OMX_SESSION_ID;
|
|
49
|
+
else
|
|
50
|
+
process.env.OMX_SESSION_ID = previous;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function matchingProcessIdentity() {
|
|
54
|
+
return { status: 'matching', startTicks: 1 };
|
|
55
|
+
}
|
|
56
|
+
async function writeLockOwner(cwd, owner) {
|
|
57
|
+
const context = resolveSessionPointerContext(cwd);
|
|
58
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
59
|
+
await writeFile(join(context.lockPath, 'owner.json'), JSON.stringify(owner), 'utf-8');
|
|
60
|
+
return context.lockPath;
|
|
61
|
+
}
|
|
62
|
+
function validLockOwner(overrides = {}) {
|
|
63
|
+
return {
|
|
64
|
+
version: 1,
|
|
65
|
+
token: TEST_TOKEN,
|
|
66
|
+
pid: process.pid,
|
|
67
|
+
platform: 'linux',
|
|
68
|
+
pid_start_ticks: 1,
|
|
69
|
+
created_at: '2026-07-14T00:00:00.000Z',
|
|
70
|
+
...overrides,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async function linuxProcessStartTicks(pid) {
|
|
74
|
+
const stat = await readFile(`/proc/${pid}/stat`, 'utf-8');
|
|
75
|
+
const closingParen = stat.lastIndexOf(')');
|
|
76
|
+
assert.notEqual(closingParen, -1);
|
|
77
|
+
const fieldsAfterCommand = stat.slice(closingParen + 2).trim().split(/\s+/);
|
|
78
|
+
const startTicks = Number.parseInt(fieldsAfterCommand[19] ?? '', 10);
|
|
79
|
+
assert.equal(Number.isInteger(startTicks), true);
|
|
80
|
+
return startTicks;
|
|
81
|
+
}
|
|
20
82
|
describe('session lifecycle manager', () => {
|
|
21
83
|
it('resets session metrics files with zeroed counters', async () => {
|
|
22
84
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-metrics-'));
|
|
@@ -104,6 +166,55 @@ describe('session lifecycle manager', () => {
|
|
|
104
166
|
await rm(cwd, { recursive: true, force: true });
|
|
105
167
|
}
|
|
106
168
|
});
|
|
169
|
+
it('emits the session-end warning only after successful end finalization', async () => {
|
|
170
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-end-durability-'));
|
|
171
|
+
const originalWrite = process.stderr.write;
|
|
172
|
+
const warnings = [];
|
|
173
|
+
process.stderr.write = ((value) => {
|
|
174
|
+
warnings.push(value);
|
|
175
|
+
return true;
|
|
176
|
+
});
|
|
177
|
+
try {
|
|
178
|
+
await writeSessionEnd(cwd, 'sess-end-durability', {
|
|
179
|
+
platform: 'win32',
|
|
180
|
+
regularFileSync: async () => { throw codedError('EPERM'); },
|
|
181
|
+
});
|
|
182
|
+
assert.deepEqual(warnings, [
|
|
183
|
+
'[omx] warning: Windows EPERM regular-file fsync unsupported in session pointer end; operation succeeded with degraded durability.\n',
|
|
184
|
+
]);
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
process.stderr.write = originalWrite;
|
|
188
|
+
await rm(cwd, { recursive: true, force: true });
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
it('keeps session-end durability warnings silent when finalization or release fails', async () => {
|
|
192
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-end-durability-failure-'));
|
|
193
|
+
const originalWrite = process.stderr.write;
|
|
194
|
+
const warnings = [];
|
|
195
|
+
process.stderr.write = ((value) => {
|
|
196
|
+
warnings.push(value);
|
|
197
|
+
return true;
|
|
198
|
+
});
|
|
199
|
+
const regularFileSync = async () => { throw codedError('EPERM'); };
|
|
200
|
+
try {
|
|
201
|
+
await withPointerDependencies({
|
|
202
|
+
fs: {
|
|
203
|
+
rmdir: async () => { throw new Error('release failure'); },
|
|
204
|
+
},
|
|
205
|
+
}, async () => {
|
|
206
|
+
await assert.rejects(writeSessionEnd(cwd, 'sess-end-release-failure', {
|
|
207
|
+
platform: 'win32',
|
|
208
|
+
regularFileSync,
|
|
209
|
+
}));
|
|
210
|
+
});
|
|
211
|
+
assert.deepEqual(warnings, []);
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
process.stderr.write = originalWrite;
|
|
215
|
+
await rm(cwd, { recursive: true, force: true });
|
|
216
|
+
}
|
|
217
|
+
});
|
|
107
218
|
it('does not delete the current session pointer when ending a different session', async () => {
|
|
108
219
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-end-owner-'));
|
|
109
220
|
try {
|
|
@@ -249,7 +360,7 @@ describe('session lifecycle manager', () => {
|
|
|
249
360
|
nativeSessionId: 'codex-native-old',
|
|
250
361
|
});
|
|
251
362
|
await reconcileNativeSessionStart(cwd, 'codex-native-new', {
|
|
252
|
-
pid:
|
|
363
|
+
pid: process.pid,
|
|
253
364
|
platform: 'win32',
|
|
254
365
|
});
|
|
255
366
|
await writeSessionEnd(cwd, 'omx-owner-session');
|
|
@@ -356,22 +467,26 @@ describe('session lifecycle manager', () => {
|
|
|
356
467
|
await rm(cwd, { recursive: true, force: true });
|
|
357
468
|
}
|
|
358
469
|
});
|
|
359
|
-
it('
|
|
360
|
-
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-native-
|
|
470
|
+
it('preserves foreign selected-pointer evidence instead of replacing it during native reconciliation', async () => {
|
|
471
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-native-foreign-'));
|
|
361
472
|
try {
|
|
362
473
|
const statePath = join(cwd, '.omx', 'state', 'session.json');
|
|
363
474
|
await resetSessionMetrics(cwd);
|
|
364
475
|
await writeFile(statePath, JSON.stringify({
|
|
365
476
|
session_id: 'sess-other-worktree',
|
|
366
477
|
cwd: join(cwd, '..', 'different-worktree'),
|
|
478
|
+
pid: process.pid,
|
|
479
|
+
platform: 'win32',
|
|
367
480
|
}), 'utf-8');
|
|
368
|
-
|
|
369
|
-
|
|
481
|
+
await assert.rejects(reconcileNativeSessionStart(cwd, 'codex-fallback-1', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
|
|
482
|
+
&& error.code === 'session_pointer_unusable'
|
|
483
|
+
&& error.pointerStatus === 'foreign-cwd');
|
|
484
|
+
assert.equal(await readFile(statePath, 'utf-8'), JSON.stringify({
|
|
485
|
+
session_id: 'sess-other-worktree',
|
|
486
|
+
cwd: join(cwd, '..', 'different-worktree'),
|
|
487
|
+
pid: process.pid,
|
|
370
488
|
platform: 'win32',
|
|
371
|
-
});
|
|
372
|
-
assert.equal(reconciled.session_id, 'codex-fallback-1');
|
|
373
|
-
assert.equal(reconciled.native_session_id, 'codex-fallback-1');
|
|
374
|
-
assert.equal(reconciled.pid, 67890);
|
|
489
|
+
}));
|
|
375
490
|
}
|
|
376
491
|
finally {
|
|
377
492
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -501,4 +616,756 @@ describe('isSessionStale', () => {
|
|
|
501
616
|
assert.equal(stale, false);
|
|
502
617
|
});
|
|
503
618
|
});
|
|
619
|
+
describe('session pointer transaction', () => {
|
|
620
|
+
it('makes root resolution failures pathless typed launch aborts', async () => {
|
|
621
|
+
await assert.rejects(writeSessionStart('bad\0cwd', 'sess-context'), (error) => isSessionPointerLaunchAbort(error)
|
|
622
|
+
&& error.code === 'session_pointer_context_failure'
|
|
623
|
+
&& error.operation === 'pointer-context-resolve'
|
|
624
|
+
&& !('pointerPath' in error));
|
|
625
|
+
});
|
|
626
|
+
it('classifies only the exact selected pointer as absent, usable, stale, indeterminate, malformed, or foreign', async () => {
|
|
627
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-pointer-status-'));
|
|
628
|
+
try {
|
|
629
|
+
const context = resolveSessionPointerContext(cwd);
|
|
630
|
+
await withPointerDependencies({
|
|
631
|
+
probePid: () => 'alive',
|
|
632
|
+
readProcessIdentity: () => matchingProcessIdentity(),
|
|
633
|
+
}, async () => {
|
|
634
|
+
assert.equal((await readSessionPointer(context)).status, 'absent');
|
|
635
|
+
await mkdir(context.baseStateDir, { recursive: true });
|
|
636
|
+
await writeFile(context.sessionPath, JSON.stringify({
|
|
637
|
+
session_id: 'sess-usable',
|
|
638
|
+
started_at: '2026-07-14T00:00:00.000Z',
|
|
639
|
+
cwd,
|
|
640
|
+
pid: process.pid,
|
|
641
|
+
platform: 'linux',
|
|
642
|
+
pid_start_ticks: 1,
|
|
643
|
+
}), 'utf-8');
|
|
644
|
+
assert.equal((await readSessionPointer(context)).status, 'usable');
|
|
645
|
+
__setSessionPointerTransactionDependenciesForTests({
|
|
646
|
+
probePid: () => 'dead',
|
|
647
|
+
readProcessIdentity: () => matchingProcessIdentity(),
|
|
648
|
+
});
|
|
649
|
+
assert.equal((await readSessionPointer(context)).status, 'stale-dead');
|
|
650
|
+
__setSessionPointerTransactionDependenciesForTests({
|
|
651
|
+
probePid: () => 'indeterminate',
|
|
652
|
+
readProcessIdentity: () => matchingProcessIdentity(),
|
|
653
|
+
});
|
|
654
|
+
assert.equal((await readSessionPointer(context)).status, 'identity-indeterminate');
|
|
655
|
+
await writeFile(context.sessionPath, '{ not-json', 'utf-8');
|
|
656
|
+
assert.equal((await readSessionPointer(context)).status, 'malformed');
|
|
657
|
+
await writeFile(context.sessionPath, JSON.stringify({
|
|
658
|
+
session_id: 'sess-foreign',
|
|
659
|
+
started_at: '2026-07-14T00:00:00.000Z',
|
|
660
|
+
cwd: join(cwd, '..', 'foreign-worktree'),
|
|
661
|
+
pid: process.pid,
|
|
662
|
+
platform: 'win32',
|
|
663
|
+
}), 'utf-8');
|
|
664
|
+
assert.equal((await readSessionPointer(context)).status, 'foreign-cwd');
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
finally {
|
|
668
|
+
__resetSessionPointerTransactionDependenciesForTests();
|
|
669
|
+
await rm(cwd, { recursive: true, force: true });
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
it('maps only ESRCH to a definitely dead default PID probe', () => {
|
|
673
|
+
const alive = __createDefaultPidProbeForTests(() => { });
|
|
674
|
+
const esrch = __createDefaultPidProbeForTests(() => { throw codedError('ESRCH'); });
|
|
675
|
+
const eperm = __createDefaultPidProbeForTests(() => { throw codedError('EPERM'); });
|
|
676
|
+
const unknown = __createDefaultPidProbeForTests(() => { throw codedError('EACCES'); });
|
|
677
|
+
const noCode = __createDefaultPidProbeForTests(() => { throw new Error('unknown'); });
|
|
678
|
+
const primitive = __createDefaultPidProbeForTests(() => { throw 'unknown'; });
|
|
679
|
+
assert.equal(alive(1), 'alive');
|
|
680
|
+
assert.equal(esrch(1), 'dead');
|
|
681
|
+
assert.equal(eperm(1), 'indeterminate');
|
|
682
|
+
assert.equal(unknown(1), 'indeterminate');
|
|
683
|
+
assert.equal(noCode(1), 'indeterminate');
|
|
684
|
+
assert.equal(primitive(1), 'indeterminate');
|
|
685
|
+
});
|
|
686
|
+
it('never reaps dead, reused, indeterminate, missing, or malformed lock evidence', async () => {
|
|
687
|
+
const cases = [
|
|
688
|
+
{ name: 'dead', owner: validLockOwner(), probePid: 'dead', expected: 'dead' },
|
|
689
|
+
{ name: 'reused', owner: validLockOwner(), probePid: 'alive', identity: { status: 'reused', startTicks: 2 }, expected: 'reused' },
|
|
690
|
+
{ name: 'indeterminate-probe', owner: validLockOwner(), probePid: 'indeterminate', expected: 'identity-indeterminate' },
|
|
691
|
+
{ name: 'indeterminate-identity', owner: validLockOwner(), probePid: 'alive', identity: { status: 'indeterminate' }, expected: 'identity-indeterminate' },
|
|
692
|
+
{ name: 'unsubstantiated-reuse', owner: validLockOwner(), probePid: 'alive', identity: { status: 'reused', startTicks: 1 }, expected: 'identity-indeterminate' },
|
|
693
|
+
{ name: 'missing-required-hash', owner: validLockOwner({ pid_cmdline_hash: 'a'.repeat(64) }), probePid: 'alive', identity: matchingProcessIdentity(), expected: 'identity-indeterminate' },
|
|
694
|
+
{ name: 'missing', probePid: 'alive', expected: 'missing' },
|
|
695
|
+
{ name: 'malformed', owner: { version: 1 }, probePid: 'alive', expected: 'malformed' },
|
|
696
|
+
];
|
|
697
|
+
for (const testCase of cases) {
|
|
698
|
+
const cwd = await mkdtemp(join(tmpdir(), `omx-session-lock-${testCase.name}-`));
|
|
699
|
+
try {
|
|
700
|
+
const context = resolveSessionPointerContext(cwd);
|
|
701
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
702
|
+
if (testCase.owner) {
|
|
703
|
+
await writeFile(join(context.lockPath, 'owner.json'), JSON.stringify(testCase.owner), 'utf-8');
|
|
704
|
+
}
|
|
705
|
+
const before = await readdir(context.lockPath);
|
|
706
|
+
await withPointerDependencies({
|
|
707
|
+
token: () => TEST_TOKEN,
|
|
708
|
+
probePid: () => testCase.probePid,
|
|
709
|
+
readProcessIdentity: () => testCase.identity ?? matchingProcessIdentity(),
|
|
710
|
+
}, async () => {
|
|
711
|
+
await assert.rejects(writeSessionStart(cwd, 'sess-lock', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
|
|
712
|
+
&& error.code === 'session_pointer_lock_recovery_required'
|
|
713
|
+
&& error.lockOwnerStatus === testCase.expected);
|
|
714
|
+
});
|
|
715
|
+
assert.deepEqual(await readdir(context.lockPath), before);
|
|
716
|
+
}
|
|
717
|
+
finally {
|
|
718
|
+
await rm(cwd, { recursive: true, force: true });
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
});
|
|
722
|
+
it('atomically quarantines dead pre-rename temporary owner evidence and stays idempotent', async () => {
|
|
723
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-dead-temp-'));
|
|
724
|
+
try {
|
|
725
|
+
const context = resolveSessionPointerContext(cwd);
|
|
726
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
727
|
+
await writeFile(join(context.lockPath, `owner.${TEST_TOKEN}.tmp`), JSON.stringify(validLockOwner()), 'utf-8');
|
|
728
|
+
await withPointerDependencies({ token: () => SUCCESSOR_TOKEN, probePid: () => 'dead' }, async () => {
|
|
729
|
+
const inspected = await inspectSessionPointerLock(cwd);
|
|
730
|
+
assert.equal(inspected.status, 'dead');
|
|
731
|
+
assert.equal(inspected.evidenceSource, 'owner-temp');
|
|
732
|
+
assert.equal(inspected.safeToRecover, true);
|
|
733
|
+
const recovered = await recoverSessionPointerLock(cwd);
|
|
734
|
+
assert.equal(recovered.recovered, true);
|
|
735
|
+
assert.equal(recovered.action, 'quarantined');
|
|
736
|
+
assert.equal(existsSync(context.lockPath), false);
|
|
737
|
+
assert.equal(existsSync(recovered.quarantinePath), true);
|
|
738
|
+
const repeated = await recoverSessionPointerLock(cwd);
|
|
739
|
+
assert.equal(repeated.recovered, false);
|
|
740
|
+
assert.equal(repeated.action, 'none');
|
|
741
|
+
assert.equal(repeated.status, 'absent');
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
finally {
|
|
745
|
+
await rm(cwd, { recursive: true, force: true });
|
|
746
|
+
}
|
|
747
|
+
});
|
|
748
|
+
it('does not recover a dead canonical owner because owner.json is not an exact acquisition claim', async () => {
|
|
749
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-dead-canonical-'));
|
|
750
|
+
try {
|
|
751
|
+
const context = resolveSessionPointerContext(cwd);
|
|
752
|
+
await writeLockOwner(cwd, validLockOwner());
|
|
753
|
+
await withPointerDependencies({ token: () => SUCCESSOR_TOKEN, probePid: () => 'dead' }, async () => {
|
|
754
|
+
const recovered = await recoverSessionPointerLock(cwd);
|
|
755
|
+
assert.equal(recovered.status, 'dead');
|
|
756
|
+
assert.equal(recovered.evidenceSource, 'owner.json');
|
|
757
|
+
assert.equal(recovered.safeToRecover, false);
|
|
758
|
+
assert.equal(recovered.recovered, false);
|
|
759
|
+
assert.equal(recovered.action, 'none');
|
|
760
|
+
assert.equal(existsSync(context.lockPath), true);
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
finally {
|
|
764
|
+
await rm(cwd, { recursive: true, force: true });
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
it('distinguishes a paused live pre-rename owner from the same owner after SIGKILL', async (t) => {
|
|
768
|
+
if (process.platform !== 'linux') {
|
|
769
|
+
t.skip('Linux process start identity is required for deterministic PID reuse protection.');
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-sigkill-'));
|
|
773
|
+
const child = spawn(process.execPath, ['-e', 'setInterval(() => {}, 1_000)'], { stdio: 'ignore' });
|
|
774
|
+
try {
|
|
775
|
+
assert.ok(child.pid);
|
|
776
|
+
await once(child, 'spawn');
|
|
777
|
+
const context = resolveSessionPointerContext(cwd);
|
|
778
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
779
|
+
await writeFile(join(context.lockPath, `owner.${TEST_TOKEN}.tmp`), JSON.stringify(validLockOwner({
|
|
780
|
+
pid: child.pid,
|
|
781
|
+
pid_start_ticks: await linuxProcessStartTicks(child.pid),
|
|
782
|
+
})), 'utf-8');
|
|
783
|
+
const paused = await recoverSessionPointerLock(cwd);
|
|
784
|
+
assert.equal(paused.status, 'live');
|
|
785
|
+
assert.equal(paused.recovered, false);
|
|
786
|
+
assert.equal(paused.action, 'none');
|
|
787
|
+
assert.equal(existsSync(context.lockPath), true);
|
|
788
|
+
child.kill('SIGKILL');
|
|
789
|
+
const [exitCode, signal] = await once(child, 'exit');
|
|
790
|
+
assert.equal(exitCode, null);
|
|
791
|
+
assert.equal(signal, 'SIGKILL');
|
|
792
|
+
const orphaned = await recoverSessionPointerLock(cwd);
|
|
793
|
+
assert.equal(orphaned.status, 'dead');
|
|
794
|
+
assert.equal(orphaned.recovered, true);
|
|
795
|
+
assert.equal(orphaned.action, 'quarantined');
|
|
796
|
+
assert.equal(existsSync(context.lockPath), false);
|
|
797
|
+
}
|
|
798
|
+
finally {
|
|
799
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
800
|
+
child.kill('SIGKILL');
|
|
801
|
+
await rm(cwd, { recursive: true, force: true });
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
it('fails closed for live pre-rename, PID reuse, malformed, and ambiguous lock evidence', async () => {
|
|
805
|
+
const cases = [
|
|
806
|
+
{ name: 'live-temp', files: [[`owner.${TEST_TOKEN}.tmp`, JSON.stringify(validLockOwner())]], probePid: 'alive', identity: matchingProcessIdentity() },
|
|
807
|
+
{ name: 'reused', files: [['owner.json', JSON.stringify(validLockOwner())]], probePid: 'alive', identity: { status: 'matching', startTicks: 2 } },
|
|
808
|
+
{ name: 'malformed', files: [['owner.json', '{']], probePid: 'dead' },
|
|
809
|
+
{ name: 'ambiguous', files: [['owner.json', JSON.stringify(validLockOwner())], [`owner.${SUCCESSOR_TOKEN}.tmp`, JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN }))]], probePid: 'dead' },
|
|
810
|
+
];
|
|
811
|
+
for (const testCase of cases) {
|
|
812
|
+
const cwd = await mkdtemp(join(tmpdir(), `omx-session-lock-recovery-${testCase.name}-`));
|
|
813
|
+
try {
|
|
814
|
+
const context = resolveSessionPointerContext(cwd);
|
|
815
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
816
|
+
await Promise.all(testCase.files.map(async ([name, contents]) => await writeFile(join(context.lockPath, name), contents, 'utf-8')));
|
|
817
|
+
await withPointerDependencies({
|
|
818
|
+
token: () => SUCCESSOR_TOKEN,
|
|
819
|
+
probePid: () => testCase.probePid,
|
|
820
|
+
readProcessIdentity: () => testCase.identity ?? matchingProcessIdentity(),
|
|
821
|
+
}, async () => {
|
|
822
|
+
const before = await readdir(context.lockPath);
|
|
823
|
+
const recovered = await recoverSessionPointerLock(cwd);
|
|
824
|
+
assert.equal(recovered.recovered, false);
|
|
825
|
+
assert.equal(recovered.action, 'none');
|
|
826
|
+
assert.equal(recovered.safeToRecover, false);
|
|
827
|
+
assert.deepEqual(await readdir(context.lockPath), before);
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
finally {
|
|
831
|
+
await rm(cwd, { recursive: true, force: true });
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
it('claims stale evidence before quarantine so a successor cannot be mistaken for it', async () => {
|
|
836
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-race-'));
|
|
837
|
+
try {
|
|
838
|
+
const context = resolveSessionPointerContext(cwd);
|
|
839
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
840
|
+
await writeFile(join(context.lockPath, `owner.${TEST_TOKEN}.tmp`), JSON.stringify(validLockOwner()), 'utf-8');
|
|
841
|
+
const renames = [];
|
|
842
|
+
let successorBlocked = false;
|
|
843
|
+
await withPointerDependencies({
|
|
844
|
+
token: () => SUCCESSOR_TOKEN,
|
|
845
|
+
probePid: () => 'dead',
|
|
846
|
+
fs: {
|
|
847
|
+
rename: async (from, to) => {
|
|
848
|
+
renames.push([from, to]);
|
|
849
|
+
await rename(from, to);
|
|
850
|
+
if (from.endsWith(`owner.${TEST_TOKEN}.tmp`)) {
|
|
851
|
+
await assert.rejects(mkdir(context.lockPath), { code: 'EEXIST' });
|
|
852
|
+
successorBlocked = true;
|
|
853
|
+
}
|
|
854
|
+
},
|
|
855
|
+
},
|
|
856
|
+
}, async () => {
|
|
857
|
+
const recovered = await recoverSessionPointerLock(cwd);
|
|
858
|
+
assert.equal(recovered.recovered, true);
|
|
859
|
+
});
|
|
860
|
+
assert.equal(successorBlocked, true);
|
|
861
|
+
assert.match(renames[0][0], /owner\.transaction_token_123456\.tmp$/);
|
|
862
|
+
assert.match(renames[0][1], new RegExp(`owner\\.${TEST_TOKEN}\\.${SUCCESSOR_TOKEN}\\.recovery$`));
|
|
863
|
+
assert.match(renames[1][0], new RegExp(`owner\\.${TEST_TOKEN}\\.${SUCCESSOR_TOKEN}\\.recovery$`));
|
|
864
|
+
assert.equal(renames[1][1].includes('.quarantine.'), true);
|
|
865
|
+
}
|
|
866
|
+
finally {
|
|
867
|
+
await rm(cwd, { recursive: true, force: true });
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
it('does not claim a successor lock when the inspected orphan is displaced before the exact temp rename', async () => {
|
|
871
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-successor-race-'));
|
|
872
|
+
try {
|
|
873
|
+
const context = resolveSessionPointerContext(cwd);
|
|
874
|
+
const staleTemp = join(context.lockPath, `owner.${TEST_TOKEN}.tmp`);
|
|
875
|
+
const successorTemp = join(context.lockPath, `owner.${SUCCESSOR_TOKEN}.tmp`);
|
|
876
|
+
const displacedPath = `${context.lockPath}.displaced`;
|
|
877
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
878
|
+
await writeFile(staleTemp, JSON.stringify(validLockOwner()), 'utf-8');
|
|
879
|
+
let displaced = false;
|
|
880
|
+
await withPointerDependencies({
|
|
881
|
+
token: () => SUCCESSOR_TOKEN,
|
|
882
|
+
probePid: () => 'dead',
|
|
883
|
+
fs: {
|
|
884
|
+
rename: async (from, to) => {
|
|
885
|
+
if (!displaced && from === staleTemp) {
|
|
886
|
+
displaced = true;
|
|
887
|
+
await rename(context.lockPath, displacedPath);
|
|
888
|
+
await mkdir(context.lockPath);
|
|
889
|
+
await writeFile(successorTemp, JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN })), 'utf-8');
|
|
890
|
+
}
|
|
891
|
+
await rename(from, to);
|
|
892
|
+
},
|
|
893
|
+
},
|
|
894
|
+
}, async () => {
|
|
895
|
+
const recovered = await recoverSessionPointerLock(cwd);
|
|
896
|
+
assert.equal(recovered.recovered, false);
|
|
897
|
+
assert.equal(recovered.action, 'none');
|
|
898
|
+
assert.match(recovered.reason, /changed before recovery claim/i);
|
|
899
|
+
});
|
|
900
|
+
assert.equal(displaced, true);
|
|
901
|
+
assert.deepEqual(await readdir(context.lockPath), [`owner.${SUCCESSOR_TOKEN}.tmp`]);
|
|
902
|
+
assert.equal(await readFile(successorTemp, 'utf-8'), JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN })));
|
|
903
|
+
}
|
|
904
|
+
finally {
|
|
905
|
+
await rm(cwd, { recursive: true, force: true });
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
it('does not quarantine a successor live lock displaced into the canonical path after claim revalidation', async () => {
|
|
909
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-recovery-post-claim-race-'));
|
|
910
|
+
try {
|
|
911
|
+
const context = resolveSessionPointerContext(cwd);
|
|
912
|
+
const staleTemp = join(context.lockPath, `owner.${TEST_TOKEN}.tmp`);
|
|
913
|
+
const successorTemp = join(context.lockPath, `owner.${SUCCESSOR_TOKEN}.tmp`);
|
|
914
|
+
const displacedPath = `${context.lockPath}.claimed-displaced`;
|
|
915
|
+
await mkdir(context.lockPath, { recursive: true });
|
|
916
|
+
await writeFile(staleTemp, JSON.stringify(validLockOwner()), 'utf-8');
|
|
917
|
+
let displaced = false;
|
|
918
|
+
await withPointerDependencies({
|
|
919
|
+
token: () => SUCCESSOR_TOKEN,
|
|
920
|
+
probePid: () => 'dead',
|
|
921
|
+
fs: {
|
|
922
|
+
rmdir: async (path) => {
|
|
923
|
+
if (!displaced && path === context.lockPath) {
|
|
924
|
+
displaced = true;
|
|
925
|
+
await rename(context.lockPath, displacedPath);
|
|
926
|
+
await mkdir(context.lockPath);
|
|
927
|
+
await writeFile(successorTemp, JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN })), 'utf-8');
|
|
928
|
+
}
|
|
929
|
+
await rmdir(path);
|
|
930
|
+
},
|
|
931
|
+
},
|
|
932
|
+
}, async () => {
|
|
933
|
+
const recovered = await recoverSessionPointerLock(cwd);
|
|
934
|
+
assert.equal(recovered.recovered, false);
|
|
935
|
+
assert.equal(recovered.action, 'none');
|
|
936
|
+
assert.match(recovered.reason, /canonical lock directory was not empty/i);
|
|
937
|
+
assert.ok(recovered.quarantinePath);
|
|
938
|
+
});
|
|
939
|
+
assert.equal(displaced, true);
|
|
940
|
+
assert.deepEqual(await readdir(context.lockPath), [`owner.${SUCCESSOR_TOKEN}.tmp`]);
|
|
941
|
+
assert.equal(await readFile(successorTemp, 'utf-8'), JSON.stringify(validLockOwner({ token: SUCCESSOR_TOKEN })));
|
|
942
|
+
}
|
|
943
|
+
finally {
|
|
944
|
+
await rm(cwd, { recursive: true, force: true });
|
|
945
|
+
}
|
|
946
|
+
});
|
|
947
|
+
it('retries a positively matching live owner with the bounded 25/50/100 schedule before timing out', async () => {
|
|
948
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-lock-timeout-'));
|
|
949
|
+
try {
|
|
950
|
+
await writeLockOwner(cwd, validLockOwner());
|
|
951
|
+
const delays = [];
|
|
952
|
+
let now = 0;
|
|
953
|
+
await withPointerDependencies({
|
|
954
|
+
nowMs: () => now,
|
|
955
|
+
sleep: async (ms) => { delays.push(ms); now += ms; },
|
|
956
|
+
token: () => TEST_TOKEN,
|
|
957
|
+
probePid: () => 'alive',
|
|
958
|
+
readProcessIdentity: () => matchingProcessIdentity(),
|
|
959
|
+
}, async () => {
|
|
960
|
+
await assert.rejects(writeSessionStart(cwd, 'sess-timeout', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
|
|
961
|
+
&& error.code === 'session_pointer_lock_timeout'
|
|
962
|
+
&& error.lockOwnerStatus === 'live');
|
|
963
|
+
});
|
|
964
|
+
assert.deepEqual(delays.slice(0, 3), [25, 50, 100]);
|
|
965
|
+
assert.equal(delays.at(-1), 25);
|
|
966
|
+
assert.equal(existsSync(resolveSessionPointerContext(cwd).lockPath), true);
|
|
967
|
+
}
|
|
968
|
+
finally {
|
|
969
|
+
await rm(cwd, { recursive: true, force: true });
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
it('rolls back only its unpublished owner artifacts and reports rollback ambiguity', async () => {
|
|
973
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-owner-publish-'));
|
|
974
|
+
try {
|
|
975
|
+
const context = resolveSessionPointerContext(cwd);
|
|
976
|
+
await withPointerDependencies({
|
|
977
|
+
token: () => TEST_TOKEN,
|
|
978
|
+
fs: {
|
|
979
|
+
writeFile: async (path, data, options) => {
|
|
980
|
+
if (path.endsWith(`owner.${TEST_TOKEN}.tmp`))
|
|
981
|
+
throw new Error('owner write failure');
|
|
982
|
+
await writeFile(path, data, options);
|
|
983
|
+
},
|
|
984
|
+
},
|
|
985
|
+
}, async () => {
|
|
986
|
+
await assert.rejects(writeSessionStart(cwd, 'sess-owner-publish', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
|
|
987
|
+
&& error.code === 'session_pointer_io_failure'
|
|
988
|
+
&& error.operation === 'lock-owner-publish');
|
|
989
|
+
});
|
|
990
|
+
assert.equal(existsSync(context.lockPath), false);
|
|
991
|
+
await withPointerDependencies({
|
|
992
|
+
token: () => TEST_TOKEN,
|
|
993
|
+
fs: {
|
|
994
|
+
writeFile: async (path, data, options) => {
|
|
995
|
+
if (path.endsWith(`owner.${TEST_TOKEN}.tmp`))
|
|
996
|
+
throw new Error('owner write failure');
|
|
997
|
+
await writeFile(path, data, options);
|
|
998
|
+
},
|
|
999
|
+
rmdir: async (path) => {
|
|
1000
|
+
if (path === context.lockPath)
|
|
1001
|
+
throw new Error('keep lock evidence');
|
|
1002
|
+
await rm(path, { recursive: false });
|
|
1003
|
+
},
|
|
1004
|
+
},
|
|
1005
|
+
}, async () => {
|
|
1006
|
+
await assert.rejects(writeSessionStart(cwd, 'sess-owner-residue', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
|
|
1007
|
+
&& error.code === 'session_pointer_lock_recovery_required'
|
|
1008
|
+
&& error.primaryOperation === 'lock-owner-publish'
|
|
1009
|
+
&& error.secondaryFailures?.[0]?.phase === 'remove-unpublished-lock');
|
|
1010
|
+
});
|
|
1011
|
+
assert.equal(existsSync(context.lockPath), true);
|
|
1012
|
+
}
|
|
1013
|
+
finally {
|
|
1014
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1015
|
+
}
|
|
1016
|
+
});
|
|
1017
|
+
it('publishes the owner and pointer on Windows when regular-file fsync reports EPERM', async () => {
|
|
1018
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-windows-eperm-'));
|
|
1019
|
+
let syncCalls = 0;
|
|
1020
|
+
const regularFileSync = async (platform) => {
|
|
1021
|
+
assert.equal(platform, 'win32');
|
|
1022
|
+
syncCalls += 1;
|
|
1023
|
+
throw codedError('EPERM');
|
|
1024
|
+
};
|
|
1025
|
+
try {
|
|
1026
|
+
const state = await writeSessionStart(cwd, 'sess-windows-eperm', {
|
|
1027
|
+
platform: 'win32',
|
|
1028
|
+
regularFileSync,
|
|
1029
|
+
});
|
|
1030
|
+
const context = resolveSessionPointerContext(cwd);
|
|
1031
|
+
assert.equal(state.session_id, 'sess-windows-eperm');
|
|
1032
|
+
assert.equal(syncCalls, 2, 'owner publication and pointer publication must both attempt fsync');
|
|
1033
|
+
assert.equal(existsSync(join(context.lockPath, 'owner.json')), false, 'lock owner is removed after commit');
|
|
1034
|
+
assert.equal(JSON.parse(await readFile(context.sessionPath, 'utf8')).session_id, 'sess-windows-eperm');
|
|
1035
|
+
}
|
|
1036
|
+
finally {
|
|
1037
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
it('emits one post-release warning for a degraded session start and stays silent for synced or failed transactions', async () => {
|
|
1041
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-durability-warning-'));
|
|
1042
|
+
const originalWrite = process.stderr.write;
|
|
1043
|
+
const warnings = [];
|
|
1044
|
+
process.stderr.write = ((value) => {
|
|
1045
|
+
warnings.push({ value, lockExists: existsSync(resolveSessionPointerContext(cwd).lockPath) });
|
|
1046
|
+
return true;
|
|
1047
|
+
});
|
|
1048
|
+
const regularFileSync = async () => { throw codedError('EPERM'); };
|
|
1049
|
+
try {
|
|
1050
|
+
await writeSessionStart(cwd, 'sess-degraded-start', { platform: 'win32', regularFileSync });
|
|
1051
|
+
assert.deepEqual(warnings, [{
|
|
1052
|
+
value: '[omx] warning: Windows EPERM regular-file fsync unsupported in session pointer start/reconcile; operation succeeded with degraded durability.\n',
|
|
1053
|
+
lockExists: false,
|
|
1054
|
+
}]);
|
|
1055
|
+
warnings.length = 0;
|
|
1056
|
+
await writeSessionStart(cwd, 'sess-degraded-start', { platform: 'win32' });
|
|
1057
|
+
assert.deepEqual(warnings, []);
|
|
1058
|
+
await withPointerDependencies({
|
|
1059
|
+
fs: {
|
|
1060
|
+
rename: async (from, to) => {
|
|
1061
|
+
if (to === resolveSessionPointerContext(cwd).sessionPath)
|
|
1062
|
+
throw new Error('rename failure');
|
|
1063
|
+
await rename(from, to);
|
|
1064
|
+
},
|
|
1065
|
+
},
|
|
1066
|
+
}, async () => {
|
|
1067
|
+
await assert.rejects(writeSessionStart(cwd, 'sess-degraded-start', { platform: 'win32', regularFileSync }));
|
|
1068
|
+
});
|
|
1069
|
+
assert.deepEqual(warnings, []);
|
|
1070
|
+
}
|
|
1071
|
+
finally {
|
|
1072
|
+
process.stderr.write = originalWrite;
|
|
1073
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1074
|
+
}
|
|
1075
|
+
});
|
|
1076
|
+
it('types state-directory, pointer-read, owner-sync, owner-rename, and invalid-token failures before commit', async () => {
|
|
1077
|
+
const failures = [
|
|
1078
|
+
{
|
|
1079
|
+
name: 'state-dir',
|
|
1080
|
+
operation: 'state-dir-create',
|
|
1081
|
+
dependencies: (context) => ({
|
|
1082
|
+
fs: {
|
|
1083
|
+
mkdir: async (path, options) => {
|
|
1084
|
+
if (path === context.baseStateDir)
|
|
1085
|
+
throw new Error('state directory failure');
|
|
1086
|
+
await mkdir(path, options);
|
|
1087
|
+
},
|
|
1088
|
+
},
|
|
1089
|
+
}),
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
name: 'pointer-read',
|
|
1093
|
+
operation: 'pointer-read',
|
|
1094
|
+
dependencies: (context) => ({
|
|
1095
|
+
token: () => TEST_TOKEN,
|
|
1096
|
+
fs: {
|
|
1097
|
+
readFile: async (path, encoding) => {
|
|
1098
|
+
if (path === context.sessionPath)
|
|
1099
|
+
throw new Error('pointer read failure');
|
|
1100
|
+
return await readFile(path, encoding);
|
|
1101
|
+
},
|
|
1102
|
+
},
|
|
1103
|
+
}),
|
|
1104
|
+
},
|
|
1105
|
+
{
|
|
1106
|
+
name: 'owner-sync',
|
|
1107
|
+
operation: 'lock-owner-publish',
|
|
1108
|
+
dependencies: (context) => ({
|
|
1109
|
+
token: () => TEST_TOKEN,
|
|
1110
|
+
fs: {
|
|
1111
|
+
openAndSync: async (path) => {
|
|
1112
|
+
if (path === join(context.lockPath, `owner.${TEST_TOKEN}.tmp`))
|
|
1113
|
+
throw new Error('owner sync failure');
|
|
1114
|
+
return 'synced';
|
|
1115
|
+
},
|
|
1116
|
+
},
|
|
1117
|
+
}),
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
name: 'owner-rename',
|
|
1121
|
+
operation: 'lock-owner-publish',
|
|
1122
|
+
dependencies: (context) => ({
|
|
1123
|
+
token: () => TEST_TOKEN,
|
|
1124
|
+
fs: {
|
|
1125
|
+
rename: async (from, to) => {
|
|
1126
|
+
if (from === join(context.lockPath, `owner.${TEST_TOKEN}.tmp`))
|
|
1127
|
+
throw new Error('owner rename failure');
|
|
1128
|
+
await rename(from, to);
|
|
1129
|
+
},
|
|
1130
|
+
},
|
|
1131
|
+
}),
|
|
1132
|
+
},
|
|
1133
|
+
];
|
|
1134
|
+
for (const failure of failures) {
|
|
1135
|
+
const cwd = await mkdtemp(join(tmpdir(), `omx-session-${failure.name}-`));
|
|
1136
|
+
try {
|
|
1137
|
+
const context = resolveSessionPointerContext(cwd);
|
|
1138
|
+
await withPointerDependencies(failure.dependencies(context), async () => {
|
|
1139
|
+
await assert.rejects(writeSessionStart(cwd, 'sess-precommit', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
|
|
1140
|
+
&& error.code === 'session_pointer_io_failure'
|
|
1141
|
+
&& error.operation === failure.operation);
|
|
1142
|
+
});
|
|
1143
|
+
assert.equal(existsSync(context.lockPath), false);
|
|
1144
|
+
}
|
|
1145
|
+
finally {
|
|
1146
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-invalid-token-'));
|
|
1150
|
+
try {
|
|
1151
|
+
const context = resolveSessionPointerContext(cwd);
|
|
1152
|
+
await withPointerDependencies({ token: () => 'bad' }, async () => {
|
|
1153
|
+
await assert.rejects(writeSessionStart(cwd, 'sess-invalid-token', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
|
|
1154
|
+
&& error.code === 'session_pointer_io_failure'
|
|
1155
|
+
&& error.operation === 'lock-owner-publish');
|
|
1156
|
+
});
|
|
1157
|
+
assert.equal(existsSync(context.lockPath), false);
|
|
1158
|
+
assert.equal(existsSync(`${context.sessionPath}.tmp-bad`), false);
|
|
1159
|
+
}
|
|
1160
|
+
finally {
|
|
1161
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
it('types pointer write, sync, and rename failures and removes only the owned temporary file', async () => {
|
|
1165
|
+
const phases = [
|
|
1166
|
+
{ operation: 'pointer-temp-write', fail: 'write' },
|
|
1167
|
+
{ operation: 'pointer-fsync', fail: 'sync' },
|
|
1168
|
+
{ operation: 'pointer-rename', fail: 'rename' },
|
|
1169
|
+
];
|
|
1170
|
+
for (const phase of phases) {
|
|
1171
|
+
const cwd = await mkdtemp(join(tmpdir(), `omx-session-${phase.fail}-`));
|
|
1172
|
+
try {
|
|
1173
|
+
const context = resolveSessionPointerContext(cwd);
|
|
1174
|
+
const tempPath = `${context.sessionPath}.tmp-${TEST_TOKEN}`;
|
|
1175
|
+
await withPointerDependencies({
|
|
1176
|
+
token: () => TEST_TOKEN,
|
|
1177
|
+
fs: {
|
|
1178
|
+
writeFile: async (path, data, options) => {
|
|
1179
|
+
await writeFile(path, data, options);
|
|
1180
|
+
if (phase.fail === 'write' && path === tempPath)
|
|
1181
|
+
throw new Error('pointer write failure');
|
|
1182
|
+
},
|
|
1183
|
+
openAndSync: async (path) => {
|
|
1184
|
+
if (phase.fail === 'sync' && path === tempPath)
|
|
1185
|
+
throw new Error('pointer sync failure');
|
|
1186
|
+
return 'synced';
|
|
1187
|
+
},
|
|
1188
|
+
rename: async (from, to) => {
|
|
1189
|
+
if (phase.fail === 'rename' && from === tempPath && to === context.sessionPath) {
|
|
1190
|
+
throw new Error('pointer rename failure');
|
|
1191
|
+
}
|
|
1192
|
+
await rename(from, to);
|
|
1193
|
+
},
|
|
1194
|
+
},
|
|
1195
|
+
}, async () => {
|
|
1196
|
+
await assert.rejects(writeSessionStart(cwd, 'sess-failure', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
|
|
1197
|
+
&& error.code === 'session_pointer_io_failure'
|
|
1198
|
+
&& error.operation === phase.operation);
|
|
1199
|
+
});
|
|
1200
|
+
assert.equal(existsSync(tempPath), false);
|
|
1201
|
+
assert.equal(existsSync(context.lockPath), false);
|
|
1202
|
+
}
|
|
1203
|
+
finally {
|
|
1204
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
it('preserves owned pointer residue and ordered cleanup evidence when cleanup or release cannot complete', async () => {
|
|
1209
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-pointer-residue-'));
|
|
1210
|
+
try {
|
|
1211
|
+
const context = resolveSessionPointerContext(cwd);
|
|
1212
|
+
const tempPath = `${context.sessionPath}.tmp-${TEST_TOKEN}`;
|
|
1213
|
+
await withPointerDependencies({
|
|
1214
|
+
token: () => TEST_TOKEN,
|
|
1215
|
+
fs: {
|
|
1216
|
+
writeFile: async (path, data, options) => {
|
|
1217
|
+
await writeFile(path, data, options);
|
|
1218
|
+
if (path === tempPath)
|
|
1219
|
+
throw new Error('pointer write failure');
|
|
1220
|
+
},
|
|
1221
|
+
unlink: async (path) => {
|
|
1222
|
+
if (path === tempPath)
|
|
1223
|
+
throw new Error('pointer temp cleanup failure');
|
|
1224
|
+
await rm(path, { force: false });
|
|
1225
|
+
},
|
|
1226
|
+
rmdir: async (path) => {
|
|
1227
|
+
if (path.endsWith(`.release-${TEST_TOKEN}`))
|
|
1228
|
+
throw new Error('release cleanup failure');
|
|
1229
|
+
await rm(path, { recursive: false });
|
|
1230
|
+
},
|
|
1231
|
+
},
|
|
1232
|
+
}, async () => {
|
|
1233
|
+
await assert.rejects(writeSessionStart(cwd, 'sess-residue', { platform: 'win32' }), (error) => isSessionPointerLaunchAbort(error)
|
|
1234
|
+
&& error.code === 'session_pointer_lock_recovery_required'
|
|
1235
|
+
&& error.primaryOperation === 'pointer-temp-write'
|
|
1236
|
+
&& error.secondaryFailures?.map((failure) => failure.phase).join(',') === 'remove-pointer-temp,remove-release-dir'
|
|
1237
|
+
&& error.secondaryFailures?.[0]?.evidencePath === tempPath);
|
|
1238
|
+
});
|
|
1239
|
+
assert.equal(existsSync(tempPath), true);
|
|
1240
|
+
assert.equal(existsSync(`${context.lockPath}.release-${TEST_TOKEN}`), true);
|
|
1241
|
+
}
|
|
1242
|
+
finally {
|
|
1243
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1244
|
+
}
|
|
1245
|
+
});
|
|
1246
|
+
it('leaves foreign pointer temps inert and a successor commits after explicit lock recovery', async () => {
|
|
1247
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-pointer-successor-'));
|
|
1248
|
+
try {
|
|
1249
|
+
const context = resolveSessionPointerContext(cwd);
|
|
1250
|
+
const ownTempPath = `${context.sessionPath}.tmp-${TEST_TOKEN}`;
|
|
1251
|
+
const foreignTempPath = `${context.sessionPath}.tmp-${FOREIGN_TOKEN}`;
|
|
1252
|
+
await mkdir(context.baseStateDir, { recursive: true });
|
|
1253
|
+
await writeFile(foreignTempPath, 'foreign transaction evidence', 'utf-8');
|
|
1254
|
+
await withPointerDependencies({
|
|
1255
|
+
token: () => TEST_TOKEN,
|
|
1256
|
+
fs: {
|
|
1257
|
+
writeFile: async (path, data, options) => {
|
|
1258
|
+
await writeFile(path, data, options);
|
|
1259
|
+
if (path === ownTempPath)
|
|
1260
|
+
throw new Error('pointer write failure');
|
|
1261
|
+
},
|
|
1262
|
+
unlink: async (path) => {
|
|
1263
|
+
if (path === ownTempPath)
|
|
1264
|
+
throw new Error('keep own residue');
|
|
1265
|
+
await rm(path, { force: false });
|
|
1266
|
+
},
|
|
1267
|
+
rename: async (from, to) => {
|
|
1268
|
+
if (from === context.lockPath)
|
|
1269
|
+
throw new Error('preserve canonical lock');
|
|
1270
|
+
await rename(from, to);
|
|
1271
|
+
},
|
|
1272
|
+
},
|
|
1273
|
+
}, async () => {
|
|
1274
|
+
await assert.rejects(writeSessionStart(cwd, 'sess-first', { platform: 'win32' }), isSessionPointerLaunchAbort);
|
|
1275
|
+
});
|
|
1276
|
+
assert.equal(existsSync(foreignTempPath), true);
|
|
1277
|
+
assert.equal(existsSync(context.lockPath), true);
|
|
1278
|
+
// This is documented stopped-session operator recovery, deliberately not
|
|
1279
|
+
// product transaction behavior.
|
|
1280
|
+
await rm(context.lockPath, { recursive: true, force: true });
|
|
1281
|
+
await withPointerDependencies({ token: () => SUCCESSOR_TOKEN }, async () => {
|
|
1282
|
+
await writeSessionStart(cwd, 'sess-successor', { platform: 'win32' });
|
|
1283
|
+
});
|
|
1284
|
+
assert.equal((await readSessionState(cwd))?.session_id, 'sess-successor');
|
|
1285
|
+
assert.equal(existsSync(ownTempPath), true);
|
|
1286
|
+
assert.equal(existsSync(foreignTempPath), true);
|
|
1287
|
+
assert.equal(existsSync(context.lockPath), false);
|
|
1288
|
+
}
|
|
1289
|
+
finally {
|
|
1290
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
it('binds an owner alias only on a verified same-native/absent transition and never during replacement', async () => {
|
|
1294
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-owner-alias-'));
|
|
1295
|
+
try {
|
|
1296
|
+
const nativeOnly = await reconcileNativeSessionStart(cwd, 'native-first', {
|
|
1297
|
+
platform: 'win32',
|
|
1298
|
+
ownerOmxSessionId: 'omx-owner',
|
|
1299
|
+
});
|
|
1300
|
+
assert.equal(nativeOnly.owner_omx_session_id, undefined);
|
|
1301
|
+
await withOwnerEnvironment('omx-owner', async () => {
|
|
1302
|
+
const bound = await reconcileNativeSessionStart(cwd, 'native-first', {
|
|
1303
|
+
platform: 'win32',
|
|
1304
|
+
ownerAliasVerified: true,
|
|
1305
|
+
});
|
|
1306
|
+
assert.equal(bound.session_id, 'native-first');
|
|
1307
|
+
assert.equal(bound.owner_omx_session_id, 'omx-owner');
|
|
1308
|
+
const replacement = await reconcileNativeSessionStart(cwd, 'native-second', {
|
|
1309
|
+
platform: 'win32',
|
|
1310
|
+
ownerAliasVerified: true,
|
|
1311
|
+
});
|
|
1312
|
+
assert.equal(replacement.session_id, 'native-second');
|
|
1313
|
+
assert.equal(replacement.owner_omx_session_id, 'omx-owner');
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
finally {
|
|
1317
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
it('preserves pointer evidence when history cannot be appended and rejects unusable end states before history or HUD cleanup', async () => {
|
|
1321
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-session-end-preserve-'));
|
|
1322
|
+
try {
|
|
1323
|
+
const context = resolveSessionPointerContext(cwd);
|
|
1324
|
+
await writeSessionStart(cwd, 'sess-history', { platform: 'win32' });
|
|
1325
|
+
await mkdir(join(cwd, '.omx', 'logs'), { recursive: true });
|
|
1326
|
+
await rm(join(cwd, '.omx', 'logs'), { recursive: true, force: true });
|
|
1327
|
+
await writeFile(join(cwd, '.omx', 'logs'), 'not-a-directory', 'utf-8');
|
|
1328
|
+
await assert.rejects(writeSessionEnd(cwd, 'sess-history'));
|
|
1329
|
+
assert.equal((await readSessionState(cwd))?.session_id, 'sess-history');
|
|
1330
|
+
await rm(join(cwd, '.omx', 'logs'), { force: true });
|
|
1331
|
+
await writeFile(context.sessionPath, '{ malformed', 'utf-8');
|
|
1332
|
+
await assert.rejects(writeSessionEnd(cwd, 'sess-history'), (error) => isSessionPointerLaunchAbort(error)
|
|
1333
|
+
&& error.code === 'session_pointer_unusable'
|
|
1334
|
+
&& error.pointerStatus === 'malformed');
|
|
1335
|
+
assert.equal(await readFile(context.sessionPath, 'utf-8'), '{ malformed');
|
|
1336
|
+
assert.equal(existsSync(join(cwd, '.omx', 'logs', 'session-history.jsonl')), false);
|
|
1337
|
+
}
|
|
1338
|
+
finally {
|
|
1339
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1340
|
+
}
|
|
1341
|
+
});
|
|
1342
|
+
});
|
|
1343
|
+
describe('prompt provenance diagnostics', () => {
|
|
1344
|
+
it('writes exactly one redacted record at the supplied selected root', async () => {
|
|
1345
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-provenance-log-'));
|
|
1346
|
+
try {
|
|
1347
|
+
const stateDir = join(cwd, 'selected', '.omx', 'state');
|
|
1348
|
+
await appendPromptSessionProvenanceRejection({
|
|
1349
|
+
cwd,
|
|
1350
|
+
baseStateDir: stateDir,
|
|
1351
|
+
rootSource: 'cwd-default',
|
|
1352
|
+
sessionPath: join(stateDir, 'session.json'),
|
|
1353
|
+
lockPath: join(stateDir, 'session.json.lock'),
|
|
1354
|
+
}, {
|
|
1355
|
+
reason: 'payload_session_invalid',
|
|
1356
|
+
producer: 'native',
|
|
1357
|
+
selectedRootStatus: 'malformed',
|
|
1358
|
+
timestamp: '2026-07-14T00:00:00.000Z',
|
|
1359
|
+
});
|
|
1360
|
+
const log = await readFile(join(cwd, 'selected', '.omx', 'logs', `omx-${todayIsoDate()}.jsonl`), 'utf-8');
|
|
1361
|
+
assert.equal(log.trim().split('\n').length, 1);
|
|
1362
|
+
assert.match(log, /"event":"prompt_session_provenance_rejected"/);
|
|
1363
|
+
assert.equal(log.includes(cwd), false);
|
|
1364
|
+
assert.equal(existsSync(join(cwd, '.omx', 'logs', `omx-${todayIsoDate()}.jsonl`)), false);
|
|
1365
|
+
}
|
|
1366
|
+
finally {
|
|
1367
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1368
|
+
}
|
|
1369
|
+
});
|
|
1370
|
+
});
|
|
504
1371
|
//# sourceMappingURL=session.test.js.map
|