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
package/dist/config/generator.js
CHANGED
|
@@ -16,7 +16,7 @@ import TOML from "@iarna/toml";
|
|
|
16
16
|
import { DEFAULT_FRONTIER_MODEL } from "./models.js";
|
|
17
17
|
import { DEFAULT_CODEX_HOOK_FEATURE_FLAG, CODEX_HOOK_FEATURE_FLAGS, CODEX_PLUGIN_SCOPED_HOOKS_FEATURE_FLAG, formatCodexHookFeatureFlagLine, normalizeCodexHookFeatureFlag, } from "./codex-feature-flags.js";
|
|
18
18
|
import { OMX_FIRST_PARTY_MCP_SERVER_NAMES, getOmxFirstPartySetupMcpServers, } from "./omx-first-party-mcp.js";
|
|
19
|
-
import { buildManagedCodexHookTrustState, escapeTomlBasicString, } from "./codex-hooks.js";
|
|
19
|
+
import { buildManagedCodexHookTrustState, escapeTomlBasicString, ManagedCodexHooksPlanError, scanManagedCodexHookTrustStateFromContent, } from "./codex-hooks.js";
|
|
20
20
|
function escapeTomlString(value) {
|
|
21
21
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
22
22
|
}
|
|
@@ -37,8 +37,8 @@ const LEGACY_SEEDED_MODEL_CONTEXT_WINDOW = 250000;
|
|
|
37
37
|
const LEGACY_SEEDED_MODEL_AUTO_COMPACT_TOKEN_LIMIT = 200000;
|
|
38
38
|
const OMX_SEEDED_BEHAVIORAL_DEFAULTS_START_MARKER = "# oh-my-codex seeded behavioral defaults (uninstall removes unchanged defaults)";
|
|
39
39
|
const OMX_SEEDED_BEHAVIORAL_DEFAULTS_END_MARKER = "# End oh-my-codex seeded behavioral defaults";
|
|
40
|
-
export const OMX_DEVELOPER_INSTRUCTIONS = "You have oh-my-codex installed. AGENTS.md is the orchestration brain and main control surface. Follow AGENTS.md for skill/keyword routing, $name workflow invocation, and role-specialized subagents; when
|
|
41
|
-
export const OMX_PLUGIN_DEVELOPER_INSTRUCTIONS = '<omx version="1">You have oh-my-codex installed through Codex plugin mode. AGENTS.md is the orchestration brain and main control surface. Follow AGENTS.md for skill/keyword routing and $name workflow invocation. When
|
|
40
|
+
export const OMX_DEVELOPER_INSTRUCTIONS = "You have oh-my-codex installed. AGENTS.md is the orchestration brain and main control surface. Follow AGENTS.md for skill/keyword routing, $name workflow invocation, and role-specialized subagents; when the native surface exposes `agent_type` role routing, set `agent_type` to an installed role and never omit it for OMX work. When it reports `role_routing_unavailable`, do not fabricate `agent_type`; before Ralplan planning, state, HUD, runtime, or delegation work, run `omx ralplan preflight --json` and stop on `unsupported_documented_leader_proof`. Never fake the role via a prompt label or infer authority from session/thread/pointer/transcript/cwd state. Use outcome-first, concise progress updates: state the target result, constraints, validation evidence, and stop condition before adding process detail. Native subagents live in .codex/agents and may handle independent parallel subtasks within one Codex session or team pane. Skills load from .codex/skills, not native-agent TOMLs. Treat installed prompts as narrower execution surfaces under AGENTS.md authority.";
|
|
41
|
+
export const OMX_PLUGIN_DEVELOPER_INSTRUCTIONS = '<omx version="1">You have oh-my-codex installed through Codex plugin mode. AGENTS.md is the orchestration brain and main control surface. Follow AGENTS.md for skill/keyword routing and $name workflow invocation. When the native surface exposes `agent_type` role routing, set `agent_type` to an installed role and never omit it for OMX work. When it reports `role_routing_unavailable`, do not fabricate `agent_type`; before Ralplan planning, state, HUD, runtime, or delegation work, run `omx ralplan preflight --json` and stop on `unsupported_documented_leader_proof`. Never fake the role via a prompt label or infer authority from session/thread/pointer/transcript/cwd state. Registered Codex plugin marketplace surfaces supply OMX workflows and plugin-scoped companion resources when the plugin is installed; native agent roles are installed as setup-owned Codex agent TOML files in plugin mode so agent_type routing works. User-installed skills may still live under ~/.codex/skills. Use outcome-first, concise progress updates: state the target result, constraints, validation evidence, and stop condition before adding process detail.</omx>';
|
|
42
42
|
const SHARED_MCP_REGISTRY_MARKER = "oh-my-codex (OMX) Shared MCP Registry Sync";
|
|
43
43
|
const SHARED_MCP_REGISTRY_END_MARKER = "# End oh-my-codex shared MCP registry sync";
|
|
44
44
|
const LEGACY_MULTI_AGENT_KEYS = [
|
|
@@ -136,6 +136,7 @@ const LEGACY_OMX_STATUS_LINE = statusLineForPreset(DEFAULT_STATUS_LINE_PRESET);
|
|
|
136
136
|
const OMX_PRESET_STATUS_LINE_VALUES = new Set(Object.keys(STATUS_LINE_PRESETS).map((preset) => statusLineForPreset(preset)));
|
|
137
137
|
const LEGACY_OMX_TEAM_RUN_TABLE_PATTERN = /^\s*\[mcp_servers\.(?:"omx_team_run"|omx_team_run)\]\s*$/m;
|
|
138
138
|
const OMX_CONFIG_MARKER = "oh-my-codex (OMX) Configuration";
|
|
139
|
+
const OMX_CONFIG_START_MARKER = `# ${OMX_CONFIG_MARKER}`;
|
|
139
140
|
const OMX_CONFIG_END_MARKER = "# End oh-my-codex";
|
|
140
141
|
const CODEX_MODEL_AVAILABILITY_NUX_TABLE_PATTERN = /^\s*\[tui\.model_availability_nux\]\s*(?:#.*)?$/;
|
|
141
142
|
const TOML_TABLE_HEADER_PATTERN = /^\s*\[\[?[^\]]+\]?\]\s*(?:#.*)?$/;
|
|
@@ -652,246 +653,1366 @@ const OMX_HOOK_TRUST_START_MARKER = "# OMX-owned Codex hook trust state";
|
|
|
652
653
|
const OMX_HOOK_TRUST_END_MARKER = "# End OMX-owned Codex hook trust state";
|
|
653
654
|
const OMX_PROJECT_TRUST_START_MARKER = "# OMX-synced Codex project trust state (from runtime CODEX_HOME)";
|
|
654
655
|
const OMX_PROJECT_TRUST_END_MARKER = "# End OMX-synced Codex project trust state";
|
|
655
|
-
function
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
const
|
|
661
|
-
|
|
662
|
-
|
|
656
|
+
function isPlainTomlRecord(value) {
|
|
657
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
|
|
658
|
+
}
|
|
659
|
+
function safeParseToml(content) {
|
|
660
|
+
try {
|
|
661
|
+
const parsed = TOML.parse(content);
|
|
662
|
+
return isPlainTomlRecord(parsed) ? parsed : undefined;
|
|
663
|
+
}
|
|
664
|
+
catch {
|
|
665
|
+
return undefined;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function projectHookTrustState(config) {
|
|
669
|
+
const hooks = safeParseToml(config)?.hooks;
|
|
670
|
+
const state = isPlainTomlRecord(hooks) ? hooks.state : undefined;
|
|
671
|
+
return isPlainTomlRecord(state) ? state : undefined;
|
|
672
|
+
}
|
|
673
|
+
function projectTrustSemanticView(config) {
|
|
674
|
+
const hasLeadingBom = config.startsWith("\uFEFF");
|
|
675
|
+
const source = hasLeadingBom ? config.slice(1) : config;
|
|
676
|
+
if (source.includes("\uFEFF"))
|
|
677
|
+
return undefined;
|
|
678
|
+
return { source, prefix: hasLeadingBom ? "\uFEFF" : "" };
|
|
679
|
+
}
|
|
680
|
+
function projectTrustMarkerRange(config) {
|
|
681
|
+
const lexical = analyzeTomlSource(config);
|
|
682
|
+
if (!lexical.isUnambiguous)
|
|
683
|
+
return undefined;
|
|
684
|
+
const lines = sourceLines(config);
|
|
685
|
+
const starts = lines.filter((line, index) => lexical.lineStartsOutsideMultiline[index] && line.content.trim() === OMX_PROJECT_TRUST_START_MARKER);
|
|
686
|
+
const ends = lines.filter((line, index) => lexical.lineStartsOutsideMultiline[index] && line.content.trim() === OMX_PROJECT_TRUST_END_MARKER);
|
|
687
|
+
if (starts.length === 0 && ends.length === 0)
|
|
688
|
+
return { start: config.length, end: config.length, payloadStart: config.length, payloadEnd: config.length };
|
|
689
|
+
if (starts.length !== 1 || ends.length !== 1 || starts[0].start >= ends[0].start)
|
|
690
|
+
return undefined;
|
|
691
|
+
const start = starts[0];
|
|
692
|
+
const end = ends[0];
|
|
693
|
+
return { start: start.start, end: end.end, payloadStart: start.end, payloadEnd: end.start };
|
|
694
|
+
}
|
|
695
|
+
function hasInlineProjectTrustMarkerComment(config) {
|
|
696
|
+
const lexical = analyzeTomlSource(config);
|
|
697
|
+
return sourceLines(config).some((line, index) => {
|
|
698
|
+
if (!lexical.lineStartsOutsideMultiline[index])
|
|
699
|
+
return false;
|
|
700
|
+
const trimmed = line.content.trim();
|
|
701
|
+
return (trimmed.startsWith(OMX_PROJECT_TRUST_START_MARKER) && trimmed !== OMX_PROJECT_TRUST_START_MARKER) ||
|
|
702
|
+
(trimmed.startsWith(OMX_PROJECT_TRUST_END_MARKER) && trimmed !== OMX_PROJECT_TRUST_END_MARKER);
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
function projectTrustHeaderPath(line) {
|
|
706
|
+
if (/^\s*\[\[/.test(line))
|
|
707
|
+
return undefined;
|
|
708
|
+
const header = parseTomlSourceTableHeader(line);
|
|
709
|
+
if (!header?.parsed)
|
|
710
|
+
return undefined;
|
|
711
|
+
const path = [];
|
|
712
|
+
let cursor = header.parsed;
|
|
713
|
+
while (isPlainTomlRecord(cursor)) {
|
|
714
|
+
const keys = Object.keys(cursor);
|
|
715
|
+
if (keys.length === 0)
|
|
716
|
+
return path;
|
|
717
|
+
if (keys.length !== 1)
|
|
663
718
|
return undefined;
|
|
664
|
-
|
|
665
|
-
|
|
719
|
+
const key = keys[0];
|
|
720
|
+
path.push(key);
|
|
721
|
+
cursor = cursor[key];
|
|
666
722
|
}
|
|
667
|
-
return
|
|
723
|
+
return path;
|
|
668
724
|
}
|
|
669
|
-
function
|
|
670
|
-
const
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
725
|
+
function dominantProjectTrustEol(source) {
|
|
726
|
+
const crlfCount = (source.match(/\r\n/g) ?? []).length;
|
|
727
|
+
const lfCount = (source.match(/(?<!\r)\n/g) ?? []).length;
|
|
728
|
+
return crlfCount > lfCount ? "\r\n" : "\n";
|
|
729
|
+
}
|
|
730
|
+
function projectTrustPayloadIsSemanticallyEmpty(payload) {
|
|
731
|
+
const parsed = safeParseToml(payload);
|
|
732
|
+
return parsed !== undefined && Object.keys(parsed).length === 0;
|
|
733
|
+
}
|
|
734
|
+
function projectTrustValueAtPath(source, path) {
|
|
735
|
+
let value = source;
|
|
736
|
+
for (const key of path) {
|
|
737
|
+
if (!isPlainTomlRecord(value))
|
|
738
|
+
return undefined;
|
|
739
|
+
value = value[key];
|
|
740
|
+
}
|
|
741
|
+
return value;
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Marker contents are an ownership boundary, not general TOML. Reject every
|
|
745
|
+
* source form that OMX did not emit rather than rebuilding it and losing data.
|
|
746
|
+
*/
|
|
747
|
+
function inventoryProjectTrustPayload(payload, projectHooksPath) {
|
|
748
|
+
const parsed = safeParseToml(payload);
|
|
749
|
+
const lexical = analyzeTomlSource(payload);
|
|
750
|
+
const headers = projectTrustHeaders(payload, { start: 0, end: payload.length });
|
|
751
|
+
if (!parsed || !lexical.isUnambiguous || !headers)
|
|
752
|
+
return undefined;
|
|
753
|
+
if (Object.keys(parsed).some((key) => key !== "projects" && key !== "hooks"))
|
|
754
|
+
return undefined;
|
|
755
|
+
const lines = sourceLines(payload);
|
|
756
|
+
const headerStarts = new Set(headers.map((header) => header.start));
|
|
757
|
+
for (const [index, line] of lines.entries()) {
|
|
758
|
+
if (!lexical.lineStartsOutsideMultiline[index])
|
|
759
|
+
return undefined;
|
|
760
|
+
const isHeader = headerStarts.has(line.start);
|
|
761
|
+
const isBlankOrComment = line.content.trim() === "" || /^\s*#/.test(line.content);
|
|
762
|
+
if (isHeader || isBlankOrComment) {
|
|
763
|
+
if (isHeader && lexical.lineHasTomlComment[index])
|
|
764
|
+
return undefined;
|
|
676
765
|
continue;
|
|
677
766
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
767
|
+
if (lexical.lineHasTomlComment[index])
|
|
768
|
+
return undefined;
|
|
769
|
+
const priorHeader = headers.some((header) => header.start < line.start);
|
|
770
|
+
if (!priorHeader)
|
|
771
|
+
return undefined;
|
|
772
|
+
}
|
|
773
|
+
const managedHeaders = [];
|
|
774
|
+
for (const header of headers) {
|
|
775
|
+
if (header.path[0] === "projects" && header.path.length >= 2) {
|
|
776
|
+
if (!isPlainTomlRecord(projectTrustValueAtPath(parsed, header.path)))
|
|
777
|
+
return undefined;
|
|
778
|
+
managedHeaders.push({ ...header, kind: "project" });
|
|
683
779
|
continue;
|
|
684
780
|
}
|
|
685
|
-
|
|
781
|
+
if (header.path[0] === "hooks" &&
|
|
782
|
+
header.path[1] === "state" &&
|
|
783
|
+
header.path.length === 3 &&
|
|
784
|
+
header.path[2].startsWith(`${projectHooksPath}:`)) {
|
|
785
|
+
const value = projectTrustValueAtPath(parsed, header.path);
|
|
786
|
+
if (!isPlainTomlRecord(value) || typeof value.trusted_hash !== "string" || value.trusted_hash.length === 0 || Object.keys(value).some((key) => key !== "trusted_hash"))
|
|
787
|
+
return undefined;
|
|
788
|
+
managedHeaders.push({ ...header, kind: "hook" });
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
return undefined;
|
|
686
792
|
}
|
|
687
|
-
|
|
793
|
+
if (!projectTrustPayloadProjectFamiliesAreValid(managedHeaders))
|
|
794
|
+
return undefined;
|
|
795
|
+
if (!projectTrustPayloadNestedStructuresAreExplicit(parsed, managedHeaders))
|
|
796
|
+
return undefined;
|
|
797
|
+
return { parsed, headers: managedHeaders };
|
|
688
798
|
}
|
|
689
|
-
function
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
799
|
+
function projectTrustSourceNestedStructuresAreExplicit(parsed, headers) {
|
|
800
|
+
const projects = parsed.projects;
|
|
801
|
+
if (projects === undefined)
|
|
802
|
+
return true;
|
|
803
|
+
if (!isPlainTomlRecord(projects))
|
|
804
|
+
return false;
|
|
805
|
+
const hasHeader = (path) => headers.some((header) => projectTrustEqual(header.path, path));
|
|
806
|
+
const visit = (value, path) => {
|
|
807
|
+
for (const [key, child] of Object.entries(value)) {
|
|
808
|
+
if (!isPlainTomlRecord(child))
|
|
809
|
+
continue;
|
|
810
|
+
const childPath = [...path, key];
|
|
811
|
+
if (!hasHeader(childPath) || !visit(child, childPath))
|
|
812
|
+
return false;
|
|
813
|
+
}
|
|
814
|
+
return true;
|
|
815
|
+
};
|
|
816
|
+
return Object.entries(projects).every(([key, value]) => isPlainTomlRecord(value) && hasHeader(["projects", key]) && visit(value, ["projects", key]));
|
|
694
817
|
}
|
|
695
|
-
function
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
818
|
+
function projectTrustPayloadNestedStructuresAreExplicit(parsed, headers) {
|
|
819
|
+
const hasHeader = (path) => headers.some((header) => header.kind === "project" && projectTrustEqual(header.path, path));
|
|
820
|
+
const visit = (value, path) => {
|
|
821
|
+
for (const [key, child] of Object.entries(value)) {
|
|
822
|
+
if (!isPlainTomlRecord(child))
|
|
823
|
+
continue;
|
|
824
|
+
const childPath = [...path, key];
|
|
825
|
+
if (!hasHeader(childPath) || !visit(child, childPath))
|
|
826
|
+
return false;
|
|
827
|
+
}
|
|
828
|
+
return true;
|
|
829
|
+
};
|
|
830
|
+
return headers
|
|
831
|
+
.filter((header) => header.kind === "project" && header.path.length === 2)
|
|
832
|
+
.every((header) => {
|
|
833
|
+
const value = projectTrustValueAtPath(parsed, header.path);
|
|
834
|
+
return isPlainTomlRecord(value) && visit(value, header.path);
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
function projectTrustPayloadTableSpan(source) {
|
|
838
|
+
const lines = sourceLines(source);
|
|
839
|
+
const lastBodyLine = lines
|
|
840
|
+
.slice(1)
|
|
841
|
+
.reverse()
|
|
842
|
+
.find((line) => line.content.trim() !== "" && !/^\s*#/.test(line.content));
|
|
843
|
+
if (!lastBodyLine)
|
|
844
|
+
return { source, gap: "" };
|
|
845
|
+
const bodyEnd = lastBodyLine.end;
|
|
846
|
+
return { source: source.slice(0, bodyEnd), gap: source.slice(bodyEnd) };
|
|
847
|
+
}
|
|
848
|
+
function projectTrustPayloadTableBodyHasCommentsOrBlanks(source) {
|
|
849
|
+
return sourceLines(source).some((line) => line.content.trim() === "" || /^\s*#/.test(line.content));
|
|
850
|
+
}
|
|
851
|
+
function reconcileProjectTrustPayload(payload, payloadInventory, externalProjects, runtimeProjects, runtimeHooksState, externalHookTrustState, projectHooksPath, eol) {
|
|
852
|
+
const { headers } = payloadInventory;
|
|
853
|
+
const payloadProjectKeys = new Set(headers.filter((header) => header.kind === "project").map((header) => header.path[1]));
|
|
854
|
+
const replacementByHeader = new Map();
|
|
855
|
+
for (const key of payloadProjectKeys) {
|
|
856
|
+
const projectHeaders = headers.filter((header) => header.kind === "project" && header.path[1] === key);
|
|
857
|
+
const parent = projectHeaders.find((header) => header.path.length === 2);
|
|
858
|
+
const payloadValue = projectTrustValueAtPath(payloadInventory.parsed, ["projects", key]);
|
|
859
|
+
const externalValue = externalProjects[key];
|
|
860
|
+
const runtimeValue = runtimeProjects[key];
|
|
861
|
+
if (externalValue !== undefined) {
|
|
862
|
+
if (!projectTrustEqual(payloadValue, externalValue) || (runtimeValue !== undefined && !projectTrustEqual(runtimeValue, externalValue)))
|
|
863
|
+
return undefined;
|
|
864
|
+
for (const header of projectHeaders)
|
|
865
|
+
replacementByHeader.set(header.start, undefined);
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
if (runtimeValue === undefined) {
|
|
869
|
+
for (const header of projectHeaders)
|
|
870
|
+
replacementByHeader.set(header.start, undefined);
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
if (!parent || projectHeaders.length !== 1 || !isPlainTomlRecord(runtimeValue))
|
|
874
|
+
return undefined;
|
|
875
|
+
const rendered = renderProjectTrust(key, runtimeValue);
|
|
876
|
+
if (!rendered)
|
|
877
|
+
return undefined;
|
|
878
|
+
replacementByHeader.set(parent.start, rendered.replaceAll("\n", eol));
|
|
879
|
+
}
|
|
880
|
+
for (const header of headers.filter((candidate) => candidate.kind === "hook")) {
|
|
881
|
+
const key = header.path[2];
|
|
882
|
+
const payloadValue = projectTrustValueAtPath(payloadInventory.parsed, header.path);
|
|
883
|
+
const runtimeValue = runtimeHooksState?.[key];
|
|
884
|
+
const externalValue = externalHookTrustState?.[key];
|
|
885
|
+
if (externalValue !== undefined) {
|
|
886
|
+
if (!projectTrustEqual(payloadValue, externalValue))
|
|
887
|
+
return undefined;
|
|
888
|
+
replacementByHeader.set(header.start, undefined);
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
891
|
+
if (!isPlainTomlRecord(runtimeValue) || typeof runtimeValue.trusted_hash !== "string" || runtimeValue.trusted_hash.length === 0) {
|
|
892
|
+
replacementByHeader.set(header.start, undefined);
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
895
|
+
replacementByHeader.set(header.start, `[hooks.state."${escapeTomlBasicString(key)}"]${eol}trusted_hash = "${escapeTomlBasicString(runtimeValue.trusted_hash)}"`);
|
|
896
|
+
}
|
|
897
|
+
const leadingGap = payload.slice(0, headers[0]?.start ?? payload.length);
|
|
898
|
+
const parts = headers.length === 0 ? [] : [leadingGap];
|
|
899
|
+
let trailingGap = headers.length === 0 ? leadingGap : "";
|
|
900
|
+
const appendPart = (part) => {
|
|
901
|
+
if (!part)
|
|
902
|
+
return;
|
|
903
|
+
const previous = parts.at(-1);
|
|
904
|
+
if (previous && !/(?:\r\n|\n)$/.test(previous) && !/^(?:\r\n|\n)/.test(part))
|
|
905
|
+
parts.push(eol);
|
|
906
|
+
parts.push(part);
|
|
907
|
+
};
|
|
908
|
+
for (const [index, header] of headers.entries()) {
|
|
909
|
+
const next = headers[index + 1];
|
|
910
|
+
const segmentEnd = next?.start ?? payload.length;
|
|
911
|
+
const replacement = replacementByHeader.get(header.start);
|
|
912
|
+
const span = projectTrustPayloadTableSpan(payload.slice(header.start, segmentEnd));
|
|
913
|
+
const tableBody = span.source.slice(header.end - header.start);
|
|
914
|
+
if (replacement !== undefined &&
|
|
915
|
+
replacement !== span.source &&
|
|
916
|
+
projectTrustPayloadTableBodyHasCommentsOrBlanks(tableBody)) {
|
|
917
|
+
return undefined;
|
|
918
|
+
}
|
|
919
|
+
if (!next) {
|
|
920
|
+
trailingGap = span.gap;
|
|
921
|
+
if (replacement !== undefined)
|
|
922
|
+
appendPart(replacement);
|
|
923
|
+
continue;
|
|
924
|
+
}
|
|
925
|
+
if (replacement !== undefined)
|
|
926
|
+
appendPart(replacement);
|
|
927
|
+
if (span.gap)
|
|
928
|
+
appendPart(span.gap);
|
|
699
929
|
}
|
|
700
|
-
|
|
930
|
+
const additions = [];
|
|
931
|
+
for (const [key, value] of Object.entries(runtimeProjects).sort(([left], [right]) => left.localeCompare(right))) {
|
|
932
|
+
if (externalProjects[key] !== undefined || payloadProjectKeys.has(key))
|
|
933
|
+
continue;
|
|
934
|
+
if (!isPlainTomlRecord(value))
|
|
935
|
+
return undefined;
|
|
936
|
+
const rendered = renderProjectTrust(key, value);
|
|
937
|
+
if (!rendered)
|
|
938
|
+
return undefined;
|
|
939
|
+
additions.push(`${rendered.replaceAll("\n", eol)}${eol}`);
|
|
940
|
+
}
|
|
941
|
+
for (const [key, value] of Object.entries(runtimeHooksState ?? {}).sort(([left], [right]) => left.localeCompare(right))) {
|
|
942
|
+
if (!key.startsWith(`${projectHooksPath}:`) || externalHookTrustState?.[key] !== undefined || headers.some((header) => header.kind === "hook" && header.path[2] === key))
|
|
943
|
+
continue;
|
|
944
|
+
if (!isPlainTomlRecord(value) || typeof value.trusted_hash !== "string" || value.trusted_hash.length === 0)
|
|
945
|
+
continue;
|
|
946
|
+
additions.push(`[hooks.state."${escapeTomlBasicString(key)}"]${eol}trusted_hash = "${escapeTomlBasicString(value.trusted_hash)}"${eol}`);
|
|
947
|
+
}
|
|
948
|
+
let reconciled = parts.join("");
|
|
949
|
+
if (additions.length > 0 && reconciled && !/(?:\r\n|\n)$/.test(reconciled))
|
|
950
|
+
reconciled += eol;
|
|
951
|
+
reconciled += additions.join("");
|
|
952
|
+
if (trailingGap && reconciled && !/(?:\r\n|\n)$/.test(reconciled) && !/^(?:\r\n|\n)/.test(trailingGap))
|
|
953
|
+
reconciled += eol;
|
|
954
|
+
reconciled += trailingGap;
|
|
955
|
+
reconciled = reconciled.replace(/(?:\r\n|\n)$/, "");
|
|
956
|
+
return Object.keys(safeParseToml(reconciled) ?? {}).length === 0 ? "" : reconciled;
|
|
957
|
+
}
|
|
958
|
+
function projectTrustHeaders(config, region) {
|
|
959
|
+
const lexical = analyzeTomlSource(config);
|
|
960
|
+
if (!lexical.isUnambiguous)
|
|
701
961
|
return undefined;
|
|
962
|
+
const lines = sourceLines(config);
|
|
963
|
+
const headers = [];
|
|
964
|
+
for (const [index, line] of lines.entries()) {
|
|
965
|
+
if (line.start < region.start || line.start >= region.end || !lexical.lineStartsOutsideMultiline[index])
|
|
966
|
+
continue;
|
|
967
|
+
if (!/^\s*\[/.test(line.content))
|
|
968
|
+
continue;
|
|
969
|
+
const path = projectTrustHeaderPath(line.content);
|
|
970
|
+
if (!path) {
|
|
971
|
+
if (/^\s*\[\[?\s*(?:projects|"projects"|'projects')/.test(line.content))
|
|
972
|
+
return undefined;
|
|
973
|
+
continue;
|
|
974
|
+
}
|
|
975
|
+
headers.push({ start: line.start, end: line.end, path });
|
|
702
976
|
}
|
|
977
|
+
return headers;
|
|
703
978
|
}
|
|
704
|
-
function
|
|
705
|
-
const keys = new Set();
|
|
979
|
+
function inventoryProjectTrustSource(config) {
|
|
706
980
|
const parsed = safeParseToml(config);
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
981
|
+
if (!parsed)
|
|
982
|
+
return undefined;
|
|
983
|
+
const projects = parsed.projects;
|
|
984
|
+
if (projects !== undefined && !isPlainTomlRecord(projects))
|
|
985
|
+
return undefined;
|
|
986
|
+
const headers = projectTrustHeaders(config, { start: 0, end: config.length });
|
|
987
|
+
if (!headers)
|
|
988
|
+
return undefined;
|
|
989
|
+
const projectHeaders = headers.filter((header) => header.path[0] === "projects");
|
|
990
|
+
if (projectHeaders.some((header) => header.path.length < 2))
|
|
991
|
+
return undefined;
|
|
992
|
+
for (const key of Object.keys(projects ?? {})) {
|
|
993
|
+
if (projectHeaders.filter((header) => header.path.length === 2 && header.path[1] === key).length !== 1) {
|
|
994
|
+
return undefined;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
if (!projectTrustSourceNestedStructuresAreExplicit(parsed, projectHeaders))
|
|
998
|
+
return undefined;
|
|
999
|
+
return { projects: projects ?? {}, headers };
|
|
1000
|
+
}
|
|
1001
|
+
function projectTrustCandidateIsSafe(candidate, external, runtimeProjects) {
|
|
1002
|
+
const marker = projectTrustMarkerRange(candidate);
|
|
1003
|
+
const inventory = inventoryProjectTrustSource(candidate);
|
|
1004
|
+
const externalInventory = inventoryProjectTrustSource(external);
|
|
1005
|
+
if (!inventory || !externalInventory)
|
|
1006
|
+
return false;
|
|
1007
|
+
const hasMarker = marker !== undefined && marker.start !== marker.end;
|
|
1008
|
+
const hasEquivalentExternalBytes = (actual) => {
|
|
1009
|
+
if (actual === external)
|
|
1010
|
+
return true;
|
|
1011
|
+
return splitProjectTrustTrailingEols(actual).body === splitProjectTrustTrailingEols(external).body;
|
|
1012
|
+
};
|
|
1013
|
+
if (hasMarker) {
|
|
1014
|
+
const candidateExternal = `${candidate.slice(0, marker.start)}${candidate.slice(marker.end)}`;
|
|
1015
|
+
if (!hasEquivalentExternalBytes(candidateExternal) && !(candidate.startsWith(external) && candidate.slice(marker.end).length === 0 && /^(?:\r\n|\n)+$/.test(candidate.slice(external.length, marker.start))))
|
|
1016
|
+
return false;
|
|
1017
|
+
}
|
|
1018
|
+
else if (!hasEquivalentExternalBytes(candidate)) {
|
|
1019
|
+
return false;
|
|
1020
|
+
}
|
|
1021
|
+
const expectedProjects = { ...externalInventory.projects, ...runtimeProjects };
|
|
1022
|
+
if (!projectTrustEqual(inventory.projects, expectedProjects))
|
|
1023
|
+
return false;
|
|
1024
|
+
for (const [key, value] of Object.entries(expectedProjects)) {
|
|
1025
|
+
if (!projectTrustEqual(inventory.projects[key], value))
|
|
1026
|
+
return false;
|
|
1027
|
+
if (inventory.headers.filter((header) => header.path[0] === "projects" && header.path.length === 2 && header.path[1] === key).length !== 1)
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
return true;
|
|
1031
|
+
}
|
|
1032
|
+
function projectTrustPayloadProjectFamiliesAreValid(headers) {
|
|
1033
|
+
const projectKeys = new Set(headers.filter((header) => header.kind === "project").map((header) => header.path[1]));
|
|
1034
|
+
for (const key of projectKeys) {
|
|
1035
|
+
const family = headers.filter((header) => header.kind === "project" && header.path[1] === key);
|
|
1036
|
+
const parents = family.filter((header) => header.path.length === 2);
|
|
1037
|
+
if (parents.length !== 1 || !projectTrustFamilyIsContiguous(headers, parents[0]))
|
|
1038
|
+
return false;
|
|
1039
|
+
}
|
|
1040
|
+
return true;
|
|
1041
|
+
}
|
|
1042
|
+
function projectTrustFamilyIsContiguous(headers, parent) {
|
|
1043
|
+
const first = headers.indexOf(parent);
|
|
1044
|
+
let ended = false;
|
|
1045
|
+
for (let index = first + 1; index < headers.length; index += 1) {
|
|
1046
|
+
const path = headers[index].path;
|
|
1047
|
+
const belongsToFamily = path[0] === "projects" && path[1] === parent.path[1];
|
|
1048
|
+
if (!belongsToFamily) {
|
|
1049
|
+
ended = true;
|
|
713
1050
|
continue;
|
|
714
|
-
|
|
1051
|
+
}
|
|
1052
|
+
if (ended || path.length < 3)
|
|
1053
|
+
return false;
|
|
715
1054
|
}
|
|
716
|
-
return
|
|
1055
|
+
return true;
|
|
717
1056
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
1057
|
+
function projectTrustEqual(left, right) {
|
|
1058
|
+
if (Object.is(left, right))
|
|
1059
|
+
return true;
|
|
1060
|
+
if (left instanceof Date || right instanceof Date) {
|
|
1061
|
+
return left instanceof Date && right instanceof Date && left.constructor === right.constructor && String(left) === String(right);
|
|
1062
|
+
}
|
|
1063
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
1064
|
+
return Array.isArray(left) && Array.isArray(right) && left.length === right.length && left.every((value, index) => projectTrustEqual(value, right[index]));
|
|
1065
|
+
}
|
|
1066
|
+
if (!isPlainTomlRecord(left) || !isPlainTomlRecord(right))
|
|
1067
|
+
return false;
|
|
1068
|
+
const leftKeys = Object.keys(left).sort();
|
|
1069
|
+
const rightKeys = Object.keys(right).sort();
|
|
1070
|
+
return leftKeys.length === rightKeys.length && leftKeys.every((key, index) => key === rightKeys[index] && projectTrustEqual(left[key], right[key]));
|
|
1071
|
+
}
|
|
1072
|
+
function canRenderFlatProjectTrust(entry) {
|
|
1073
|
+
const isValue = (value) => {
|
|
1074
|
+
if (Array.isArray(value))
|
|
1075
|
+
return value.every(isValue);
|
|
1076
|
+
return !isPlainTomlRecord(value);
|
|
1077
|
+
};
|
|
1078
|
+
return Object.values(entry).every(isValue);
|
|
1079
|
+
}
|
|
1080
|
+
function renderProjectTrust(projectKey, entry) {
|
|
1081
|
+
if (!canRenderFlatProjectTrust(entry))
|
|
1082
|
+
return undefined;
|
|
1083
|
+
const serialized = TOML.stringify({ [projectKey]: entry });
|
|
1084
|
+
const body = serialized.split(/\r?\n/).filter((line) => !/^\s*\[/.test(line) && line.trim() !== "").join("\n");
|
|
1085
|
+
return body ? `[projects."${escapeTomlBasicString(projectKey)}"]\n${body}` : undefined;
|
|
1086
|
+
}
|
|
1087
|
+
function splitProjectTrustTrailingEols(source) {
|
|
1088
|
+
const match = source.match(/(?:(?:\r\n|\n))*$/);
|
|
1089
|
+
const trailingEols = match?.[0] ?? "";
|
|
1090
|
+
return { body: source.slice(0, source.length - trailingEols.length), trailingEols };
|
|
1091
|
+
}
|
|
1092
|
+
function appendProjectTrustMarker(base, payload, eol, trailingEols) {
|
|
1093
|
+
if (!payload)
|
|
1094
|
+
return `${splitProjectTrustTrailingEols(base).body}${trailingEols}`;
|
|
1095
|
+
const { body } = splitProjectTrustTrailingEols(base);
|
|
1096
|
+
const separator = body ? `${eol}${eol}` : "";
|
|
1097
|
+
const block = `${OMX_PROJECT_TRUST_START_MARKER}${eol}${payload}${eol}${OMX_PROJECT_TRUST_END_MARKER}`;
|
|
1098
|
+
return `${body}${separator}${block}${trailingEols}`;
|
|
1099
|
+
}
|
|
1100
|
+
function reconcileProjectScopeTrustState(projectConfig, runtimeConfig, projectHooksPath, requireExistingPayload) {
|
|
1101
|
+
const durable = projectTrustSemanticView(projectConfig);
|
|
1102
|
+
const runtime = projectTrustSemanticView(runtimeConfig);
|
|
1103
|
+
if (!durable || !runtime)
|
|
1104
|
+
return projectConfig;
|
|
1105
|
+
if (hasInlineProjectTrustMarkerComment(durable.source) || hasInlineProjectTrustMarkerComment(runtime.source))
|
|
1106
|
+
return projectConfig;
|
|
1107
|
+
const parsedRuntime = safeParseToml(runtime.source);
|
|
1108
|
+
const runtimeInventory = inventoryProjectTrustSource(runtime.source);
|
|
1109
|
+
if (!parsedRuntime || !runtimeInventory)
|
|
1110
|
+
return projectConfig;
|
|
1111
|
+
const marker = projectTrustMarkerRange(durable.source);
|
|
1112
|
+
if (!marker)
|
|
1113
|
+
return projectConfig;
|
|
1114
|
+
const hasMarker = marker.start !== marker.end;
|
|
1115
|
+
const payload = durable.source.slice(marker.payloadStart, marker.payloadEnd);
|
|
1116
|
+
const payloadIsEmpty = projectTrustPayloadIsSemanticallyEmpty(payload);
|
|
1117
|
+
const payloadInventory = hasMarker
|
|
1118
|
+
? inventoryProjectTrustPayload(payload, projectHooksPath)
|
|
1119
|
+
: { parsed: {}, headers: [] };
|
|
1120
|
+
if (!payloadInventory)
|
|
1121
|
+
return projectConfig;
|
|
1122
|
+
if (requireExistingPayload && (!hasMarker || payloadIsEmpty))
|
|
1123
|
+
return projectConfig;
|
|
1124
|
+
const external = hasMarker
|
|
1125
|
+
? `${durable.source.slice(0, marker.start)}${durable.source.slice(marker.end)}`
|
|
1126
|
+
: durable.source;
|
|
1127
|
+
const trailingEols = splitProjectTrustTrailingEols(durable.source).trailingEols;
|
|
1128
|
+
const externalInventory = inventoryProjectTrustSource(external);
|
|
1129
|
+
if (!externalInventory)
|
|
1130
|
+
return projectConfig;
|
|
1131
|
+
const projects = runtimeInventory.projects;
|
|
1132
|
+
const externalProjects = externalInventory.projects;
|
|
1133
|
+
const headersBeforeMarker = projectTrustHeaders(durable.source, { start: 0, end: marker.start });
|
|
1134
|
+
const headersAfterMarker = projectTrustHeaders(durable.source, { start: marker.end, end: durable.source.length });
|
|
1135
|
+
if (!headersBeforeMarker || !headersAfterMarker)
|
|
1136
|
+
return projectConfig;
|
|
1137
|
+
const headers = [...headersBeforeMarker, ...headersAfterMarker];
|
|
1138
|
+
for (const key of Object.keys(externalProjects)) {
|
|
1139
|
+
const family = headers.filter((header) => header.path[0] === "projects" && header.path[1] === key);
|
|
1140
|
+
const parents = family.filter((header) => header.path.length === 2);
|
|
1141
|
+
if (parents.length !== 1)
|
|
1142
|
+
return projectConfig;
|
|
1143
|
+
const parentIsBeforeMarker = parents[0].start < marker.start;
|
|
1144
|
+
if (family.some((header) => (header.start < marker.start) !== parentIsBeforeMarker))
|
|
1145
|
+
return projectConfig;
|
|
1146
|
+
if (!projectTrustFamilyIsContiguous(headers, parents[0]))
|
|
1147
|
+
return projectConfig;
|
|
1148
|
+
}
|
|
1149
|
+
const eol = dominantProjectTrustEol(durable.source);
|
|
1150
|
+
const hooks = parsedRuntime.hooks;
|
|
1151
|
+
const hooksState = isPlainTomlRecord(hooks) && isPlainTomlRecord(hooks.state) ? hooks.state : undefined;
|
|
1152
|
+
const nextPayload = reconcileProjectTrustPayload(payload, payloadInventory, externalProjects, projects, hooksState, projectHookTrustState(external), projectHooksPath, eol);
|
|
1153
|
+
if (nextPayload === undefined)
|
|
1154
|
+
return projectConfig;
|
|
1155
|
+
if (!inventoryProjectTrustPayload(nextPayload, projectHooksPath))
|
|
1156
|
+
return projectConfig;
|
|
1157
|
+
const candidate = appendProjectTrustMarker(external, nextPayload, eol, trailingEols);
|
|
1158
|
+
if (!projectTrustCandidateIsSafe(candidate, external, projects))
|
|
1159
|
+
return projectConfig;
|
|
1160
|
+
return `${durable.prefix}${candidate}`;
|
|
1161
|
+
}
|
|
1162
|
+
/** Repairs only a uniquely parseable, nonempty project-sync payload before mirror creation. */
|
|
723
1163
|
export function repairProjectScopeTrustStateForLaunch(projectConfig, projectHooksPath) {
|
|
724
|
-
const
|
|
725
|
-
if (!
|
|
1164
|
+
const durable = projectTrustSemanticView(projectConfig);
|
|
1165
|
+
if (!durable)
|
|
1166
|
+
return projectConfig;
|
|
1167
|
+
const marker = projectTrustMarkerRange(durable.source);
|
|
1168
|
+
if (!marker || marker.start === marker.end)
|
|
726
1169
|
return projectConfig;
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
1170
|
+
const payload = durable.source.slice(marker.payloadStart, marker.payloadEnd);
|
|
1171
|
+
return reconcileProjectScopeTrustState(projectConfig, payload, projectHooksPath, true);
|
|
1172
|
+
}
|
|
1173
|
+
/** Conservatively persists runtime project and hook trust without rewriting external TOML. */
|
|
1174
|
+
export function syncProjectScopeTrustStateFromRuntime(projectConfig, runtimeConfig, projectHooksPath) {
|
|
1175
|
+
return reconcileProjectScopeTrustState(projectConfig, runtimeConfig, projectHooksPath, false);
|
|
1176
|
+
}
|
|
1177
|
+
function isMultilineTomlString(mode) {
|
|
1178
|
+
return mode === "multiline-basic" || mode === "multiline-literal";
|
|
730
1179
|
}
|
|
731
1180
|
/**
|
|
732
|
-
*
|
|
733
|
-
*
|
|
734
|
-
*
|
|
735
|
-
* persistence, those entries die with the runtime, so Codex prompts to trust
|
|
736
|
-
* the workspace and hooks on every launch (issue #2470).
|
|
737
|
-
*
|
|
738
|
-
* This function extracts only trust-state tables (`[projects."<cwd>"]` and
|
|
739
|
-
* `[hooks.state."<projectHooksPath>:..."]`) from the runtime config.toml and
|
|
740
|
-
* upserts them into the durable project config.toml inside a marker-fenced
|
|
741
|
-
* block, preserving any surrounding user-managed content and comments and
|
|
742
|
-
* ignoring Codex's NUX counters or other ephemeral runtime-only writes.
|
|
1181
|
+
* Tracks only lexical boundaries needed to safely associate source lines with
|
|
1182
|
+
* TOML statements. TOML's parser gives us semantics; this prevents source
|
|
1183
|
+
* repair from treating table-like text in a multiline value as syntax.
|
|
743
1184
|
*/
|
|
744
|
-
|
|
745
|
-
const
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
for (
|
|
754
|
-
|
|
1185
|
+
function analyzeTomlSource(config) {
|
|
1186
|
+
const lines = config.split(/\r?\n/);
|
|
1187
|
+
const lineStartsOutsideMultiline = [];
|
|
1188
|
+
const lineHasTomlComment = [];
|
|
1189
|
+
let mode;
|
|
1190
|
+
let isUnambiguous = true;
|
|
1191
|
+
for (const line of lines) {
|
|
1192
|
+
lineStartsOutsideMultiline.push(!isMultilineTomlString(mode));
|
|
1193
|
+
let hasComment = false;
|
|
1194
|
+
for (let index = 0; index < line.length; index += 1) {
|
|
1195
|
+
const character = line[index];
|
|
1196
|
+
if (mode === "basic") {
|
|
1197
|
+
if (character === "\\") {
|
|
1198
|
+
index += 1;
|
|
1199
|
+
}
|
|
1200
|
+
else if (character === "\"") {
|
|
1201
|
+
mode = undefined;
|
|
1202
|
+
}
|
|
755
1203
|
continue;
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
.filter((line) => !/^\s*\[/.test(line) && line.trim() !== "")
|
|
761
|
-
.join("\n");
|
|
762
|
-
if (body.length === 0)
|
|
1204
|
+
}
|
|
1205
|
+
if (mode === "literal") {
|
|
1206
|
+
if (character === "'")
|
|
1207
|
+
mode = undefined;
|
|
763
1208
|
continue;
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
1209
|
+
}
|
|
1210
|
+
if (mode === "multiline-basic" || mode === "multiline-literal") {
|
|
1211
|
+
const delimiter = mode === "multiline-basic" ? "\"" : "'";
|
|
1212
|
+
if (mode === "multiline-basic" && character === "\\") {
|
|
1213
|
+
index += 1;
|
|
1214
|
+
continue;
|
|
1215
|
+
}
|
|
1216
|
+
if (character !== delimiter)
|
|
1217
|
+
continue;
|
|
1218
|
+
let runEnd = index + 1;
|
|
1219
|
+
while (line[runEnd] === delimiter)
|
|
1220
|
+
runEnd += 1;
|
|
1221
|
+
if (runEnd - index >= 3) {
|
|
1222
|
+
// TOML permits one or two delimiter characters immediately before
|
|
1223
|
+
// the closing triple delimiter as string content.
|
|
1224
|
+
mode = undefined;
|
|
1225
|
+
index = runEnd - 1;
|
|
1226
|
+
}
|
|
772
1227
|
continue;
|
|
773
|
-
|
|
1228
|
+
}
|
|
1229
|
+
if (character === "#") {
|
|
1230
|
+
hasComment = true;
|
|
1231
|
+
break;
|
|
1232
|
+
}
|
|
1233
|
+
if (character !== "\"" && character !== "'")
|
|
774
1234
|
continue;
|
|
775
|
-
|
|
1235
|
+
const isMultiline = line[index + 1] === character && line[index + 2] === character;
|
|
1236
|
+
if (isMultiline) {
|
|
1237
|
+
mode = character === "\"" ? "multiline-basic" : "multiline-literal";
|
|
1238
|
+
index += 2;
|
|
1239
|
+
}
|
|
1240
|
+
else {
|
|
1241
|
+
mode = character === "\"" ? "basic" : "literal";
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
lineHasTomlComment.push(hasComment);
|
|
1245
|
+
if (mode === "basic" || mode === "literal")
|
|
1246
|
+
isUnambiguous = false;
|
|
1247
|
+
}
|
|
1248
|
+
if (isMultilineTomlString(mode))
|
|
1249
|
+
isUnambiguous = false;
|
|
1250
|
+
return { lines, lineStartsOutsideMultiline, lineHasTomlComment, isUnambiguous };
|
|
1251
|
+
}
|
|
1252
|
+
function skipTomlSourceWhitespace(line, index) {
|
|
1253
|
+
let cursor = index;
|
|
1254
|
+
while (line[cursor] === " " || line[cursor] === "\t")
|
|
1255
|
+
cursor += 1;
|
|
1256
|
+
return cursor;
|
|
1257
|
+
}
|
|
1258
|
+
function parseTomlSourceKeySegment(line, index) {
|
|
1259
|
+
const start = skipTomlSourceWhitespace(line, index);
|
|
1260
|
+
const first = line[start];
|
|
1261
|
+
if (!first)
|
|
1262
|
+
return undefined;
|
|
1263
|
+
if (first !== '"' && first !== "'") {
|
|
1264
|
+
const match = /^[A-Za-z0-9_-]+/.exec(line.slice(start));
|
|
1265
|
+
if (!match)
|
|
1266
|
+
return undefined;
|
|
1267
|
+
return { key: match[0], end: start + match[0].length, incomplete: false };
|
|
1268
|
+
}
|
|
1269
|
+
let cursor = start + 1;
|
|
1270
|
+
let escaped = false;
|
|
1271
|
+
for (; cursor < line.length; cursor += 1) {
|
|
1272
|
+
const character = line[cursor];
|
|
1273
|
+
if (first === '"' && escaped) {
|
|
1274
|
+
escaped = false;
|
|
1275
|
+
continue;
|
|
1276
|
+
}
|
|
1277
|
+
if (first === '"' && character === "\\") {
|
|
1278
|
+
escaped = true;
|
|
1279
|
+
continue;
|
|
1280
|
+
}
|
|
1281
|
+
if (character !== first)
|
|
1282
|
+
continue;
|
|
1283
|
+
const token = line.slice(start, cursor + 1);
|
|
1284
|
+
const parsed = safeParseToml(`${token} = true`);
|
|
1285
|
+
const keys = parsed ? Object.keys(parsed) : [];
|
|
1286
|
+
const hasKey = keys.length === 1;
|
|
1287
|
+
return {
|
|
1288
|
+
...(hasKey ? { key: keys[0] } : {}),
|
|
1289
|
+
end: cursor + 1,
|
|
1290
|
+
incomplete: !hasKey,
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
return { end: line.length, incomplete: true };
|
|
1294
|
+
}
|
|
1295
|
+
function isTomlSourceKey(segment, key) {
|
|
1296
|
+
return segment.key === key;
|
|
1297
|
+
}
|
|
1298
|
+
function findTomlSourceAssignmentSeparator(line) {
|
|
1299
|
+
let segment = parseTomlSourceKeySegment(line, 0);
|
|
1300
|
+
if (!segment || segment.incomplete)
|
|
1301
|
+
return undefined;
|
|
1302
|
+
let cursor = skipTomlSourceWhitespace(line, segment.end);
|
|
1303
|
+
while (line[cursor] === ".") {
|
|
1304
|
+
segment = parseTomlSourceKeySegment(line, cursor + 1);
|
|
1305
|
+
if (!segment || segment.incomplete)
|
|
1306
|
+
return undefined;
|
|
1307
|
+
cursor = skipTomlSourceWhitespace(line, segment.end);
|
|
1308
|
+
}
|
|
1309
|
+
return line[cursor] === "=" ? cursor : undefined;
|
|
1310
|
+
}
|
|
1311
|
+
function advancePastInlineTomlValue(line, index) {
|
|
1312
|
+
let braces = 0;
|
|
1313
|
+
let brackets = 0;
|
|
1314
|
+
let quote;
|
|
1315
|
+
let escaped = false;
|
|
1316
|
+
for (let cursor = index; cursor < line.length; cursor += 1) {
|
|
1317
|
+
const character = line[cursor];
|
|
1318
|
+
if (quote) {
|
|
1319
|
+
if (quote === '"' && escaped) {
|
|
1320
|
+
escaped = false;
|
|
1321
|
+
}
|
|
1322
|
+
else if (quote === '"' && character === "\\") {
|
|
1323
|
+
escaped = true;
|
|
1324
|
+
}
|
|
1325
|
+
else if (character === quote) {
|
|
1326
|
+
quote = undefined;
|
|
1327
|
+
}
|
|
1328
|
+
continue;
|
|
1329
|
+
}
|
|
1330
|
+
if (character === '"' || character === "'") {
|
|
1331
|
+
quote = character;
|
|
1332
|
+
continue;
|
|
1333
|
+
}
|
|
1334
|
+
if (character === "#")
|
|
1335
|
+
return line.length;
|
|
1336
|
+
if (character === "{") {
|
|
1337
|
+
braces += 1;
|
|
1338
|
+
continue;
|
|
1339
|
+
}
|
|
1340
|
+
if (character === "}") {
|
|
1341
|
+
if (braces === 0 && brackets === 0)
|
|
1342
|
+
return cursor;
|
|
1343
|
+
braces = Math.max(0, braces - 1);
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
if (character === "[") {
|
|
1347
|
+
brackets += 1;
|
|
1348
|
+
continue;
|
|
1349
|
+
}
|
|
1350
|
+
if (character === "]") {
|
|
1351
|
+
brackets = Math.max(0, brackets - 1);
|
|
1352
|
+
continue;
|
|
1353
|
+
}
|
|
1354
|
+
if (character === "," && braces === 0 && brackets === 0)
|
|
1355
|
+
return cursor + 1;
|
|
1356
|
+
}
|
|
1357
|
+
return line.length;
|
|
1358
|
+
}
|
|
1359
|
+
function inlineTomlTableMaySpellStateKey(line, index) {
|
|
1360
|
+
let cursor = index;
|
|
1361
|
+
while (cursor < line.length) {
|
|
1362
|
+
cursor = skipTomlSourceWhitespace(line, cursor);
|
|
1363
|
+
// A comment cannot close the surrounding inline table.
|
|
1364
|
+
if (line[cursor] === "#")
|
|
1365
|
+
return true;
|
|
1366
|
+
if (line[cursor] === "}")
|
|
1367
|
+
return false;
|
|
1368
|
+
const segment = parseTomlSourceKeySegment(line, cursor);
|
|
1369
|
+
if (!segment)
|
|
1370
|
+
return true;
|
|
1371
|
+
if (segment.incomplete || isTomlSourceKey(segment, "state"))
|
|
1372
|
+
return true;
|
|
1373
|
+
cursor = skipTomlSourceWhitespace(line, segment.end);
|
|
1374
|
+
if (line[cursor] !== "=")
|
|
1375
|
+
return true;
|
|
1376
|
+
cursor = advancePastInlineTomlValue(line, cursor + 1);
|
|
1377
|
+
if (line[cursor] === "}")
|
|
1378
|
+
return false;
|
|
1379
|
+
}
|
|
1380
|
+
// The unclosed inline table can acquire a state member on a following
|
|
1381
|
+
// unparsed line, so it is not safe to strip the containing marker block.
|
|
1382
|
+
return true;
|
|
1383
|
+
}
|
|
1384
|
+
function lineMaySpellHooksState(line) {
|
|
1385
|
+
let cursor = skipTomlSourceWhitespace(line, 0);
|
|
1386
|
+
if (line[cursor] === "[") {
|
|
1387
|
+
if (line[cursor + 1] === "[") {
|
|
1388
|
+
cursor = skipTomlSourceWhitespace(line, cursor + 2);
|
|
1389
|
+
}
|
|
1390
|
+
else {
|
|
1391
|
+
cursor = skipTomlSourceWhitespace(line, cursor + 1);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
const hooks = parseTomlSourceKeySegment(line, cursor);
|
|
1395
|
+
if (!hooks || !isTomlSourceKey(hooks, "hooks"))
|
|
1396
|
+
return false;
|
|
1397
|
+
cursor = skipTomlSourceWhitespace(line, hooks.end);
|
|
1398
|
+
if (line[cursor] === ".") {
|
|
1399
|
+
const state = parseTomlSourceKeySegment(line, cursor + 1);
|
|
1400
|
+
// A dangling dot can be completed by a following unparsed state key.
|
|
1401
|
+
return state === undefined || state.incomplete || isTomlSourceKey(state, "state");
|
|
1402
|
+
}
|
|
1403
|
+
if (line[cursor] !== "=")
|
|
1404
|
+
return false;
|
|
1405
|
+
cursor = skipTomlSourceWhitespace(line, cursor + 1);
|
|
1406
|
+
// A bare assignment or comment cannot prove that a following unparsed line
|
|
1407
|
+
// does not continue an inline hooks table with a state member.
|
|
1408
|
+
if (cursor >= line.length || line[cursor] === "#")
|
|
1409
|
+
return true;
|
|
1410
|
+
return line[cursor] === "{" && inlineTomlTableMaySpellStateKey(line, cursor + 1);
|
|
1411
|
+
}
|
|
1412
|
+
/**
|
|
1413
|
+
* Detects a potential array-table opener without accepting malformed spacing
|
|
1414
|
+
* or extra opening brackets as TOML syntax. Array tables rooted at `hooks`
|
|
1415
|
+
* have no proof-owned representation, so their first key segment is enough
|
|
1416
|
+
* to retain a managed marker block rather than strip it.
|
|
1417
|
+
*/
|
|
1418
|
+
function parseTomlSourceArrayTableOpener(line) {
|
|
1419
|
+
let cursor = skipTomlSourceWhitespace(line, 0);
|
|
1420
|
+
if (line[cursor] !== "[")
|
|
1421
|
+
return undefined;
|
|
1422
|
+
const hasExactAdjacentOpener = line[cursor + 1] === "[";
|
|
1423
|
+
if (hasExactAdjacentOpener) {
|
|
1424
|
+
cursor += 2;
|
|
1425
|
+
}
|
|
1426
|
+
else {
|
|
1427
|
+
cursor = skipTomlSourceWhitespace(line, cursor + 1);
|
|
1428
|
+
if (line[cursor] !== "[")
|
|
1429
|
+
return undefined;
|
|
1430
|
+
cursor += 1;
|
|
1431
|
+
}
|
|
1432
|
+
cursor = skipTomlSourceWhitespace(line, cursor);
|
|
1433
|
+
while (line[cursor] === "[") {
|
|
1434
|
+
cursor = skipTomlSourceWhitespace(line, cursor + 1);
|
|
1435
|
+
}
|
|
1436
|
+
const firstKey = parseTomlSourceKeySegment(line, cursor);
|
|
1437
|
+
return {
|
|
1438
|
+
firstKey,
|
|
1439
|
+
openerOnly: firstKey === undefined &&
|
|
1440
|
+
(cursor >= line.length || line[cursor] === "#"),
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
function hasIncompleteTomlSourceHooksKeyPrefix(line) {
|
|
1444
|
+
let cursor = skipTomlSourceWhitespace(line, 0);
|
|
1445
|
+
let tableHeader = false;
|
|
1446
|
+
let arrayTableHeader = false;
|
|
1447
|
+
if (line[cursor] === "[") {
|
|
1448
|
+
tableHeader = true;
|
|
1449
|
+
if (line[cursor + 1] === "[") {
|
|
1450
|
+
arrayTableHeader = true;
|
|
1451
|
+
cursor = skipTomlSourceWhitespace(line, cursor + 2);
|
|
1452
|
+
}
|
|
1453
|
+
else {
|
|
1454
|
+
cursor = skipTomlSourceWhitespace(line, cursor + 1);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
const hooks = parseTomlSourceKeySegment(line, cursor);
|
|
1458
|
+
if (!hooks || !isTomlSourceKey(hooks, "hooks"))
|
|
1459
|
+
return false;
|
|
1460
|
+
cursor = skipTomlSourceWhitespace(line, hooks.end);
|
|
1461
|
+
if (!tableHeader)
|
|
1462
|
+
return cursor >= line.length || line[cursor] === "#";
|
|
1463
|
+
if (line[cursor] !== "]" || (arrayTableHeader && line[cursor + 1] !== "]")) {
|
|
1464
|
+
return true;
|
|
1465
|
+
}
|
|
1466
|
+
const suffix = line.slice(cursor + (arrayTableHeader ? 2 : 1)).trimStart();
|
|
1467
|
+
return suffix.length > 0 && !suffix.startsWith("#");
|
|
1468
|
+
}
|
|
1469
|
+
function isTomlSourceHooksKeyContinuationSeparator(line) {
|
|
1470
|
+
const cursor = skipTomlSourceWhitespace(line, 0);
|
|
1471
|
+
return line[cursor] === "." || line[cursor] === "=" || line[cursor] === "[";
|
|
1472
|
+
}
|
|
1473
|
+
function isTomlSourceBlankOrComment(line) {
|
|
1474
|
+
return /^\s*(?:#.*)?$/.test(line);
|
|
1475
|
+
}
|
|
1476
|
+
function isTomlSourceStandaloneOpeningBracket(line) {
|
|
1477
|
+
const cursor = skipTomlSourceWhitespace(line, 0);
|
|
1478
|
+
if (line[cursor] !== "[")
|
|
1479
|
+
return false;
|
|
1480
|
+
const next = skipTomlSourceWhitespace(line, cursor + 1);
|
|
1481
|
+
return next >= line.length || line[next] === "#";
|
|
1482
|
+
}
|
|
1483
|
+
function firstTomlSourceKeyFollowingOpeningBrackets(line) {
|
|
1484
|
+
let cursor = skipTomlSourceWhitespace(line, 0);
|
|
1485
|
+
while (line[cursor] === "[") {
|
|
1486
|
+
cursor = skipTomlSourceWhitespace(line, cursor + 1);
|
|
1487
|
+
}
|
|
1488
|
+
return parseTomlSourceKeySegment(line, cursor);
|
|
1489
|
+
}
|
|
1490
|
+
function lineMaySpellStateKey(line) {
|
|
1491
|
+
const state = parseTomlSourceKeySegment(line, 0);
|
|
1492
|
+
return state !== undefined && (state.incomplete || isTomlSourceKey(state, "state"));
|
|
1493
|
+
}
|
|
1494
|
+
function isTomlSourceHooksTableHeader(parsed) {
|
|
1495
|
+
const hooks = parsed?.hooks;
|
|
1496
|
+
return parsed !== undefined &&
|
|
1497
|
+
Object.keys(parsed).length === 1 &&
|
|
1498
|
+
isPlainTomlRecord(hooks) &&
|
|
1499
|
+
Object.keys(hooks).length === 0;
|
|
1500
|
+
}
|
|
1501
|
+
function hasUnprovenManagedMarkerHooksStateSyntax(source, managedMarkerRanges, parsedStatementSpans, parsedAssignmentSpans) {
|
|
1502
|
+
const parsedLines = new Set();
|
|
1503
|
+
const parsedStatementStartLines = new Set();
|
|
1504
|
+
for (const span of parsedStatementSpans) {
|
|
1505
|
+
parsedStatementStartLines.add(span.start);
|
|
1506
|
+
for (let index = span.start; index < span.end; index += 1)
|
|
1507
|
+
parsedLines.add(index);
|
|
1508
|
+
}
|
|
1509
|
+
const parsedAssignmentLines = new Set();
|
|
1510
|
+
for (const span of parsedAssignmentSpans) {
|
|
1511
|
+
for (let index = span.start; index < span.end; index += 1) {
|
|
1512
|
+
parsedAssignmentLines.add(index);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
const managedContentLines = new Set();
|
|
1516
|
+
for (const range of managedMarkerRanges) {
|
|
1517
|
+
for (let index = range.start + 1; index < range.end; index += 1) {
|
|
1518
|
+
managedContentLines.add(index);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
let insideHooksTable = false;
|
|
1522
|
+
let hasIncompleteHooksKeyPrefix = false;
|
|
1523
|
+
let hasIncompleteHooksArrayTableOpener = false;
|
|
1524
|
+
for (let index = 0; index < source.lines.length; index += 1) {
|
|
1525
|
+
if (!source.lineStartsOutsideMultiline[index]) {
|
|
1526
|
+
hasIncompleteHooksKeyPrefix = false;
|
|
1527
|
+
hasIncompleteHooksArrayTableOpener = false;
|
|
1528
|
+
continue;
|
|
1529
|
+
}
|
|
1530
|
+
const line = source.lines[index] ?? "";
|
|
1531
|
+
const isParsedAssignmentLine = parsedAssignmentLines.has(index);
|
|
1532
|
+
if (!isParsedAssignmentLine) {
|
|
1533
|
+
const header = parseTomlSourceTableHeader(line);
|
|
1534
|
+
if (header?.parsed) {
|
|
1535
|
+
insideHooksTable = isTomlSourceHooksTableHeader(header.parsed);
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
if (!managedContentLines.has(index)) {
|
|
1539
|
+
hasIncompleteHooksKeyPrefix = false;
|
|
1540
|
+
hasIncompleteHooksArrayTableOpener = false;
|
|
1541
|
+
continue;
|
|
1542
|
+
}
|
|
1543
|
+
const isParsedStatementLine = parsedLines.has(index);
|
|
1544
|
+
const isParsedStatementStartLine = parsedStatementStartLines.has(index);
|
|
1545
|
+
if (hasIncompleteHooksKeyPrefix) {
|
|
1546
|
+
if (isTomlSourceBlankOrComment(line))
|
|
776
1547
|
continue;
|
|
777
|
-
|
|
778
|
-
|
|
1548
|
+
if (isTomlSourceHooksKeyContinuationSeparator(line))
|
|
1549
|
+
return true;
|
|
1550
|
+
hasIncompleteHooksKeyPrefix = false;
|
|
1551
|
+
}
|
|
1552
|
+
// Parsed statements can contain nested arrays whose standalone brackets and
|
|
1553
|
+
// quoted "hooks" values must not contribute to malformed-header tracking.
|
|
1554
|
+
if (hasIncompleteHooksArrayTableOpener && !isParsedStatementLine) {
|
|
1555
|
+
if (isTomlSourceBlankOrComment(line))
|
|
779
1556
|
continue;
|
|
780
|
-
|
|
1557
|
+
const firstKey = firstTomlSourceKeyFollowingOpeningBrackets(line);
|
|
1558
|
+
if (firstKey) {
|
|
1559
|
+
if (isTomlSourceKey(firstKey, "hooks"))
|
|
1560
|
+
return true;
|
|
1561
|
+
hasIncompleteHooksArrayTableOpener = false;
|
|
1562
|
+
}
|
|
781
1563
|
}
|
|
1564
|
+
const arrayTableOpener = !isParsedStatementLine || isParsedStatementStartLine
|
|
1565
|
+
? parseTomlSourceArrayTableOpener(line)
|
|
1566
|
+
: undefined;
|
|
1567
|
+
if (arrayTableOpener?.firstKey && isTomlSourceKey(arrayTableOpener.firstKey, "hooks")) {
|
|
1568
|
+
return true;
|
|
1569
|
+
}
|
|
1570
|
+
if (arrayTableOpener?.openerOnly && !isParsedStatementLine) {
|
|
1571
|
+
hasIncompleteHooksArrayTableOpener = true;
|
|
1572
|
+
}
|
|
1573
|
+
if (!isParsedStatementLine && isTomlSourceStandaloneOpeningBracket(line)) {
|
|
1574
|
+
hasIncompleteHooksArrayTableOpener = true;
|
|
1575
|
+
}
|
|
1576
|
+
if (isParsedStatementLine)
|
|
1577
|
+
continue;
|
|
1578
|
+
if (lineMaySpellHooksState(line) || (insideHooksTable && lineMaySpellStateKey(line))) {
|
|
1579
|
+
return true;
|
|
1580
|
+
}
|
|
1581
|
+
const hasIncompleteHooksPrefix = hasIncompleteTomlSourceHooksKeyPrefix(line);
|
|
1582
|
+
if (hasIncompleteHooksPrefix && /^\s*\[\[?/.test(line))
|
|
1583
|
+
return true;
|
|
1584
|
+
hasIncompleteHooksKeyPrefix = hasIncompleteHooksPrefix;
|
|
782
1585
|
}
|
|
783
|
-
|
|
784
|
-
return stripped.length === 0 ? "" : `${stripped}\n`;
|
|
785
|
-
}
|
|
786
|
-
const block = [
|
|
787
|
-
OMX_PROJECT_TRUST_START_MARKER,
|
|
788
|
-
...trustBlockLines,
|
|
789
|
-
OMX_PROJECT_TRUST_END_MARKER,
|
|
790
|
-
"",
|
|
791
|
-
].join("\n");
|
|
792
|
-
if (stripped.length === 0)
|
|
793
|
-
return block;
|
|
794
|
-
return `${stripped}\n\n${block}`;
|
|
1586
|
+
return false;
|
|
795
1587
|
}
|
|
796
|
-
function
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
1588
|
+
function managedHookTrustStateExpectations(...states) {
|
|
1589
|
+
const expectations = new Map();
|
|
1590
|
+
for (const state of states) {
|
|
1591
|
+
if (!state)
|
|
1592
|
+
continue;
|
|
1593
|
+
for (const [key, entry] of Object.entries(state)) {
|
|
1594
|
+
const hashes = expectations.get(key) ?? new Set();
|
|
1595
|
+
hashes.add(entry.trusted_hash);
|
|
1596
|
+
expectations.set(key, hashes);
|
|
1597
|
+
}
|
|
800
1598
|
}
|
|
801
|
-
|
|
1599
|
+
return expectations;
|
|
1600
|
+
}
|
|
1601
|
+
function managedHookTrustCoordinateFamily(key) {
|
|
1602
|
+
const match = /^(.*):([a-z_]+):\d+:\d+$/.exec(key);
|
|
1603
|
+
return match ? `${match[1]}:${match[2]}` : undefined;
|
|
1604
|
+
}
|
|
1605
|
+
function expectedManagedHookTrustHashes(expectations, key, allowCoordinateDrift) {
|
|
1606
|
+
const exact = expectations.get(key);
|
|
1607
|
+
if (exact || !allowCoordinateDrift)
|
|
1608
|
+
return exact;
|
|
1609
|
+
const family = managedHookTrustCoordinateFamily(key);
|
|
1610
|
+
if (!family)
|
|
802
1611
|
return undefined;
|
|
1612
|
+
const hashes = new Set();
|
|
1613
|
+
for (const [expectedKey, expectedHashes] of expectations) {
|
|
1614
|
+
if (managedHookTrustCoordinateFamily(expectedKey) !== family)
|
|
1615
|
+
continue;
|
|
1616
|
+
for (const hash of expectedHashes)
|
|
1617
|
+
hashes.add(hash);
|
|
803
1618
|
}
|
|
1619
|
+
return hashes.size > 0 ? hashes : undefined;
|
|
804
1620
|
}
|
|
805
|
-
function
|
|
806
|
-
|
|
807
|
-
if (!match)
|
|
1621
|
+
function tomlHooksStateValue(parsed) {
|
|
1622
|
+
if (!parsed)
|
|
808
1623
|
return undefined;
|
|
809
|
-
const
|
|
810
|
-
if (
|
|
1624
|
+
const hooks = parsed.hooks;
|
|
1625
|
+
if (!isPlainTomlRecord(hooks) || !Object.hasOwn(hooks, "state")) {
|
|
811
1626
|
return undefined;
|
|
812
|
-
|
|
1627
|
+
}
|
|
1628
|
+
return hooks.state;
|
|
813
1629
|
}
|
|
814
|
-
function
|
|
815
|
-
|
|
1630
|
+
function tomlHooksStateEntries(parsed) {
|
|
1631
|
+
const state = tomlHooksStateValue(parsed);
|
|
1632
|
+
return isPlainTomlRecord(state) ? state : undefined;
|
|
816
1633
|
}
|
|
817
|
-
function
|
|
818
|
-
|
|
819
|
-
|
|
1634
|
+
function hasOnlyManagedHookTrustStateField(parsed, key) {
|
|
1635
|
+
if (!parsed || Object.keys(parsed).length !== 1 || !Object.hasOwn(parsed, "hooks")) {
|
|
1636
|
+
return false;
|
|
1637
|
+
}
|
|
1638
|
+
const hooks = parsed.hooks;
|
|
1639
|
+
if (!isPlainTomlRecord(hooks) || Object.keys(hooks).length !== 1 || !Object.hasOwn(hooks, "state")) {
|
|
820
1640
|
return false;
|
|
821
|
-
|
|
822
|
-
|
|
1641
|
+
}
|
|
1642
|
+
const state = hooks.state;
|
|
1643
|
+
return isPlainTomlRecord(state) &&
|
|
1644
|
+
Object.keys(state).length === 1 &&
|
|
1645
|
+
Object.hasOwn(state, key);
|
|
823
1646
|
}
|
|
824
|
-
function
|
|
825
|
-
|
|
826
|
-
|
|
1647
|
+
function parseTomlSourceTableHeader(line) {
|
|
1648
|
+
const start = line.search(/\S/);
|
|
1649
|
+
if (start === -1 || line[start] !== "[")
|
|
1650
|
+
return undefined;
|
|
1651
|
+
const array = line[start + 1] === "[";
|
|
1652
|
+
let quote;
|
|
1653
|
+
let escaped = false;
|
|
1654
|
+
for (let index = start + (array ? 2 : 1); index < line.length; index += 1) {
|
|
1655
|
+
const character = line[index];
|
|
1656
|
+
if (quote === "\"") {
|
|
1657
|
+
if (escaped) {
|
|
1658
|
+
escaped = false;
|
|
1659
|
+
}
|
|
1660
|
+
else if (character === "\\") {
|
|
1661
|
+
escaped = true;
|
|
1662
|
+
}
|
|
1663
|
+
else if (character === quote) {
|
|
1664
|
+
quote = undefined;
|
|
1665
|
+
}
|
|
1666
|
+
continue;
|
|
1667
|
+
}
|
|
1668
|
+
if (quote === "'") {
|
|
1669
|
+
if (character === quote)
|
|
1670
|
+
quote = undefined;
|
|
1671
|
+
continue;
|
|
1672
|
+
}
|
|
1673
|
+
if (character === "\"" || character === "'") {
|
|
1674
|
+
quote = character;
|
|
1675
|
+
continue;
|
|
1676
|
+
}
|
|
1677
|
+
if (character !== "]")
|
|
1678
|
+
continue;
|
|
1679
|
+
const end = array ? index + 2 : index + 1;
|
|
1680
|
+
if (array && line[index + 1] !== "]")
|
|
1681
|
+
return undefined;
|
|
1682
|
+
const suffix = line.slice(end).trimStart();
|
|
1683
|
+
if (suffix.length > 0 && !suffix.startsWith("#"))
|
|
1684
|
+
return undefined;
|
|
1685
|
+
return { parsed: safeParseToml(line.slice(start, end)) };
|
|
827
1686
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
for (let
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1687
|
+
return undefined;
|
|
1688
|
+
}
|
|
1689
|
+
function sourceRangeHasTomlComment(source, start, end) {
|
|
1690
|
+
for (let index = start; index < end; index += 1) {
|
|
1691
|
+
if (source.lineHasTomlComment[index])
|
|
1692
|
+
return true;
|
|
1693
|
+
}
|
|
1694
|
+
return false;
|
|
1695
|
+
}
|
|
1696
|
+
function collectMarkerRanges(source, startMarker, endMarker, includeUnterminatedRange = false, inertAssignmentSpans = []) {
|
|
1697
|
+
const isInsideInertAssignment = (line) => inertAssignmentSpans.some((span) => span.start <= line && line < span.end);
|
|
1698
|
+
const ranges = [];
|
|
1699
|
+
for (let start = 0; start < source.lines.length; start += 1) {
|
|
1700
|
+
if (!source.lineStartsOutsideMultiline[start] ||
|
|
1701
|
+
isInsideInertAssignment(start) ||
|
|
1702
|
+
source.lines[start]?.trim() !== startMarker)
|
|
1703
|
+
continue;
|
|
1704
|
+
const end = source.lines.findIndex((line, index) => index > start &&
|
|
1705
|
+
source.lineStartsOutsideMultiline[index] &&
|
|
1706
|
+
!isInsideInertAssignment(index) &&
|
|
1707
|
+
line.trim() === endMarker);
|
|
1708
|
+
const nestedStart = source.lines.findIndex((line, index) => index > start &&
|
|
1709
|
+
source.lineStartsOutsideMultiline[index] &&
|
|
1710
|
+
!isInsideInertAssignment(index) &&
|
|
1711
|
+
line.trim() === startMarker);
|
|
1712
|
+
if (end === -1 || (nestedStart !== -1 && nestedStart < end)) {
|
|
1713
|
+
if (includeUnterminatedRange)
|
|
1714
|
+
ranges.push({ start, end: source.lines.length });
|
|
836
1715
|
continue;
|
|
837
1716
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
1717
|
+
ranges.push({ start, end });
|
|
1718
|
+
start = end;
|
|
1719
|
+
}
|
|
1720
|
+
return ranges;
|
|
1721
|
+
}
|
|
1722
|
+
function recordManagedMarkerHooksStateSourceSpan(spans, invalidSpans, start, end, parsed, insideManagedMarkerBlock) {
|
|
1723
|
+
if (!insideManagedMarkerBlock)
|
|
1724
|
+
return;
|
|
1725
|
+
const state = tomlHooksStateValue(parsed);
|
|
1726
|
+
if (state === undefined)
|
|
1727
|
+
return;
|
|
1728
|
+
const span = { start, end };
|
|
1729
|
+
spans.push(span);
|
|
1730
|
+
if (!isPlainTomlRecord(state) || Object.keys(state).length === 0) {
|
|
1731
|
+
invalidSpans.push(span);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
function addManagedHookTrustStateSourceRepresentations(representations, markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, source, start, end, parsed, insideManagedMarkerBlock, containingTableScope, fallbackKeys = []) {
|
|
1735
|
+
recordManagedMarkerHooksStateSourceSpan(markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, start, end, parsed, insideManagedMarkerBlock);
|
|
1736
|
+
const state = tomlHooksStateEntries(parsed);
|
|
1737
|
+
const hasComment = sourceRangeHasTomlComment(source, start, end);
|
|
1738
|
+
if (state) {
|
|
1739
|
+
const stateEntryCount = Object.keys(state).length;
|
|
1740
|
+
for (const [key, value] of Object.entries(state)) {
|
|
1741
|
+
const hasExactSemanticScope = hasOnlyManagedHookTrustStateField(parsed, key) &&
|
|
1742
|
+
(!containingTableScope ||
|
|
1743
|
+
(!containingTableScope.hasComment &&
|
|
1744
|
+
hasOnlyManagedHookTrustStateField(containingTableScope.parsed, key)));
|
|
1745
|
+
const entries = representations.get(key) ?? [];
|
|
1746
|
+
entries.push({
|
|
1747
|
+
start,
|
|
1748
|
+
end,
|
|
1749
|
+
stateEntryCount,
|
|
1750
|
+
value,
|
|
1751
|
+
hasComment,
|
|
1752
|
+
hasExactSemanticScope,
|
|
1753
|
+
insideManagedMarkerBlock,
|
|
1754
|
+
});
|
|
1755
|
+
representations.set(key, entries);
|
|
1756
|
+
}
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
for (const key of fallbackKeys) {
|
|
1760
|
+
const entries = representations.get(key) ?? [];
|
|
1761
|
+
entries.push({
|
|
1762
|
+
start,
|
|
1763
|
+
end,
|
|
1764
|
+
stateEntryCount: 0,
|
|
1765
|
+
value: undefined,
|
|
1766
|
+
hasComment,
|
|
1767
|
+
hasExactSemanticScope: false,
|
|
1768
|
+
insideManagedMarkerBlock,
|
|
1769
|
+
});
|
|
1770
|
+
representations.set(key, entries);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
function collectManagedHookTrustStateSourceRepresentations(config) {
|
|
1774
|
+
const source = analyzeTomlSource(config);
|
|
1775
|
+
const { lines } = source;
|
|
1776
|
+
const parsedAssignmentSpans = [];
|
|
1777
|
+
for (let cursor = 0; cursor < lines.length; cursor += 1) {
|
|
1778
|
+
if (!source.lineStartsOutsideMultiline[cursor])
|
|
1779
|
+
continue;
|
|
1780
|
+
const line = lines[cursor] ?? "";
|
|
1781
|
+
if (findTomlSourceAssignmentSeparator(line) === undefined)
|
|
1782
|
+
continue;
|
|
1783
|
+
for (let statementEnd = cursor + 1; statementEnd <= lines.length; statementEnd += 1) {
|
|
1784
|
+
if (!safeParseToml(lines.slice(cursor, statementEnd).join("\n")))
|
|
1785
|
+
continue;
|
|
1786
|
+
parsedAssignmentSpans.push({ start: cursor, end: statementEnd });
|
|
1787
|
+
cursor = statementEnd - 1;
|
|
1788
|
+
break;
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
const headers = [];
|
|
1792
|
+
for (const [index, line] of lines.entries()) {
|
|
1793
|
+
if (!source.lineStartsOutsideMultiline[index])
|
|
1794
|
+
continue;
|
|
1795
|
+
const header = parseTomlSourceTableHeader(line);
|
|
1796
|
+
if (parsedAssignmentSpans.some((span) => span.start < index && index < span.end))
|
|
1797
|
+
continue;
|
|
1798
|
+
if (header)
|
|
1799
|
+
headers.push({ index, header });
|
|
1800
|
+
}
|
|
1801
|
+
const boundaries = new Set(headers.map(({ index }) => index));
|
|
1802
|
+
for (const [index, line] of lines.entries()) {
|
|
1803
|
+
if (source.lineStartsOutsideMultiline[index] &&
|
|
1804
|
+
(line.trim() === OMX_HOOK_TRUST_END_MARKER ||
|
|
1805
|
+
line.trim() === OMX_CONFIG_END_MARKER)) {
|
|
1806
|
+
boundaries.add(index);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
const sortedBoundaries = [...boundaries].sort((left, right) => left - right);
|
|
1810
|
+
const representations = new Map();
|
|
1811
|
+
const markerContainedHooksStateSpans = [];
|
|
1812
|
+
const invalidMarkerContainedHooksStateSpans = [];
|
|
1813
|
+
const parsedStatementSpans = [];
|
|
1814
|
+
const managedMarkerRanges = [
|
|
1815
|
+
...collectMarkerRanges(source, OMX_HOOK_TRUST_START_MARKER, OMX_HOOK_TRUST_END_MARKER, false, parsedAssignmentSpans),
|
|
1816
|
+
...collectMarkerRanges(source, OMX_CONFIG_START_MARKER, OMX_CONFIG_END_MARKER, true, parsedAssignmentSpans),
|
|
1817
|
+
];
|
|
1818
|
+
const isInsideManagedMarkerBlock = (start, end) => managedMarkerRanges.some((range) => start > range.start && end <= range.end);
|
|
1819
|
+
const collectAssignments = (start, end, prefix, containingTableScope) => {
|
|
1820
|
+
for (let cursor = start; cursor < end;) {
|
|
1821
|
+
const line = lines[cursor] ?? "";
|
|
1822
|
+
if (!source.lineStartsOutsideMultiline[cursor] ||
|
|
1823
|
+
line.trim().length === 0 ||
|
|
1824
|
+
line.trim() === OMX_HOOK_TRUST_START_MARKER ||
|
|
1825
|
+
line.trim() === OMX_CONFIG_START_MARKER) {
|
|
1826
|
+
cursor += 1;
|
|
1827
|
+
continue;
|
|
1828
|
+
}
|
|
1829
|
+
let parsed;
|
|
1830
|
+
let statementEnd = cursor + 1;
|
|
1831
|
+
for (; statementEnd <= end; statementEnd += 1) {
|
|
1832
|
+
const statement = lines.slice(cursor, statementEnd).join("\n");
|
|
1833
|
+
parsed = safeParseToml(prefix ? `${prefix}\n${statement}` : statement);
|
|
1834
|
+
if (parsed)
|
|
1835
|
+
break;
|
|
843
1836
|
}
|
|
1837
|
+
if (!parsed) {
|
|
1838
|
+
cursor += 1;
|
|
1839
|
+
continue;
|
|
1840
|
+
}
|
|
1841
|
+
parsedStatementSpans.push({ start: cursor, end: statementEnd });
|
|
1842
|
+
addManagedHookTrustStateSourceRepresentations(representations, markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, source, cursor, statementEnd, parsed, isInsideManagedMarkerBlock(cursor, statementEnd), containingTableScope);
|
|
1843
|
+
cursor = statementEnd;
|
|
844
1844
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
1845
|
+
};
|
|
1846
|
+
const rootEnd = sortedBoundaries[0] ?? lines.length;
|
|
1847
|
+
collectAssignments(0, rootEnd, undefined);
|
|
1848
|
+
for (const { index, header } of headers) {
|
|
1849
|
+
const boundaryIndex = sortedBoundaries.findIndex((boundary) => boundary === index);
|
|
1850
|
+
const end = boundaryIndex === -1
|
|
1851
|
+
? lines.length
|
|
1852
|
+
: (sortedBoundaries[boundaryIndex + 1] ?? lines.length);
|
|
1853
|
+
const tableScope = {
|
|
1854
|
+
parsed: safeParseToml(lines.slice(index, end).join("\n")),
|
|
1855
|
+
hasComment: sourceRangeHasTomlComment(source, index, end),
|
|
1856
|
+
};
|
|
1857
|
+
if (tableScope.parsed)
|
|
1858
|
+
parsedStatementSpans.push({ start: index, end });
|
|
1859
|
+
const insideManagedMarkerBlock = isInsideManagedMarkerBlock(index, end);
|
|
1860
|
+
recordManagedMarkerHooksStateSourceSpan(markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, index, end, tableScope.parsed, insideManagedMarkerBlock);
|
|
1861
|
+
const headerState = tomlHooksStateEntries(header.parsed);
|
|
1862
|
+
const directKeys = Object.keys(headerState ?? {});
|
|
1863
|
+
if (directKeys.length > 0) {
|
|
1864
|
+
addManagedHookTrustStateSourceRepresentations(representations, markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, source, index, end, tableScope.parsed, insideManagedMarkerBlock, undefined, directKeys);
|
|
851
1865
|
continue;
|
|
852
1866
|
}
|
|
853
|
-
|
|
854
|
-
|
|
1867
|
+
collectAssignments(index + 1, end, lines[index], tableScope);
|
|
1868
|
+
}
|
|
1869
|
+
return {
|
|
1870
|
+
source,
|
|
1871
|
+
representations,
|
|
1872
|
+
managedMarkerRanges,
|
|
1873
|
+
markerContainedHooksStateSpans,
|
|
1874
|
+
invalidMarkerContainedHooksStateSpans,
|
|
1875
|
+
parsedStatementSpans,
|
|
1876
|
+
parsedAssignmentSpans,
|
|
1877
|
+
};
|
|
1878
|
+
}
|
|
1879
|
+
function isExactlyManagedHookTrustStateValue(value, expectedHashes) {
|
|
1880
|
+
return isPlainTomlRecord(value) &&
|
|
1881
|
+
Object.keys(value).length === 1 &&
|
|
1882
|
+
Object.hasOwn(value, "trusted_hash") &&
|
|
1883
|
+
typeof value.trusted_hash === "string" &&
|
|
1884
|
+
expectedHashes.has(value.trusted_hash);
|
|
1885
|
+
}
|
|
1886
|
+
function managedMarkerTrustStateConflicts(source, representations, managedMarkerRanges, markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, parsedStatementSpans, parsedAssignmentSpans, expectations) {
|
|
1887
|
+
const conflicts = new Set();
|
|
1888
|
+
const isInsideManagedMarkerRange = (span) => managedMarkerRanges.some((range) => span.start > range.start && span.end <= range.end);
|
|
1889
|
+
if (invalidMarkerContainedHooksStateSpans.some(isInsideManagedMarkerRange) ||
|
|
1890
|
+
hasUnprovenManagedMarkerHooksStateSyntax(source, managedMarkerRanges, [...markerContainedHooksStateSpans, ...parsedStatementSpans], parsedAssignmentSpans)) {
|
|
1891
|
+
conflicts.add("marker-contained hooks.state");
|
|
1892
|
+
}
|
|
1893
|
+
for (const [key, sourceRepresentations] of representations) {
|
|
1894
|
+
for (const representation of sourceRepresentations) {
|
|
1895
|
+
if (!isInsideManagedMarkerRange(representation))
|
|
1896
|
+
continue;
|
|
1897
|
+
const expectedHashes = expectedManagedHookTrustHashes(expectations, key, true);
|
|
1898
|
+
const isExactOwnedRepresentation = expectedHashes !== undefined &&
|
|
1899
|
+
representation.stateEntryCount === 1 &&
|
|
1900
|
+
!representation.hasComment &&
|
|
1901
|
+
representation.hasExactSemanticScope &&
|
|
1902
|
+
isExactlyManagedHookTrustStateValue(representation.value, expectedHashes);
|
|
1903
|
+
if (!source.isUnambiguous || !isExactOwnedRepresentation)
|
|
1904
|
+
conflicts.add(key);
|
|
855
1905
|
}
|
|
856
|
-
kept.push(...lines.slice(i, tableEnd));
|
|
857
|
-
i = tableEnd;
|
|
858
1906
|
}
|
|
859
|
-
return
|
|
1907
|
+
return conflicts;
|
|
860
1908
|
}
|
|
861
|
-
function
|
|
862
|
-
const lines =
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
1909
|
+
function removeEmptyManagedHookTrustStateMarkerBlocks(source, removed) {
|
|
1910
|
+
const { lines } = source;
|
|
1911
|
+
for (let start = 0; start < lines.length; start += 1) {
|
|
1912
|
+
if (!source.lineStartsOutsideMultiline[start] ||
|
|
1913
|
+
lines[start]?.trim() !== OMX_HOOK_TRUST_START_MARKER)
|
|
1914
|
+
continue;
|
|
1915
|
+
const end = lines.findIndex((line, index) => index > start &&
|
|
1916
|
+
source.lineStartsOutsideMultiline[index] &&
|
|
1917
|
+
line.trim() === OMX_HOOK_TRUST_END_MARKER);
|
|
1918
|
+
const nestedStart = lines.findIndex((line, index) => index > start &&
|
|
1919
|
+
source.lineStartsOutsideMultiline[index] &&
|
|
1920
|
+
line.trim() === OMX_HOOK_TRUST_START_MARKER);
|
|
1921
|
+
if (end === -1 || (nestedStart !== -1 && nestedStart < end))
|
|
869
1922
|
continue;
|
|
1923
|
+
let onlyOwnedMarkerContent = true;
|
|
1924
|
+
for (let index = start + 1; index < end; index += 1) {
|
|
1925
|
+
const trimmed = lines[index].trim();
|
|
1926
|
+
if (removed.has(index) ||
|
|
1927
|
+
trimmed.length === 0 ||
|
|
1928
|
+
trimmed === "# Trusts only setup-managed native hook wrappers.")
|
|
1929
|
+
continue;
|
|
1930
|
+
onlyOwnedMarkerContent = false;
|
|
1931
|
+
break;
|
|
870
1932
|
}
|
|
871
|
-
|
|
872
|
-
const nextStartIdx = lines.findIndex((line, index) => index > i && line.trim() === OMX_HOOK_TRUST_START_MARKER);
|
|
873
|
-
if (nextEndIdx === -1 || (nextStartIdx !== -1 && nextStartIdx < nextEndIdx)) {
|
|
874
|
-
kept.push(lines[i]);
|
|
875
|
-
i += 1;
|
|
1933
|
+
if (!onlyOwnedMarkerContent)
|
|
876
1934
|
continue;
|
|
1935
|
+
removed.add(start);
|
|
1936
|
+
removed.add(end);
|
|
1937
|
+
for (let index = start + 1; index < end; index += 1) {
|
|
1938
|
+
if (lines[index]?.trim() === "# Trusts only setup-managed native hook wrappers.") {
|
|
1939
|
+
removed.add(index);
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
start = end;
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
function stripProofManagedCodexHookTrustStateTables(config, expectations) {
|
|
1946
|
+
const { source, representations, managedMarkerRanges, markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, parsedStatementSpans, parsedAssignmentSpans, } = collectManagedHookTrustStateSourceRepresentations(config);
|
|
1947
|
+
const preservedConflictKeys = managedMarkerTrustStateConflicts(source, representations, managedMarkerRanges, markerContainedHooksStateSpans, invalidMarkerContainedHooksStateSpans, parsedStatementSpans, parsedAssignmentSpans, expectations);
|
|
1948
|
+
if (!source.isUnambiguous) {
|
|
1949
|
+
return { config, preservedConflictKeys };
|
|
1950
|
+
}
|
|
1951
|
+
const { lines } = source;
|
|
1952
|
+
const removals = new Map();
|
|
1953
|
+
const parsedConfigState = tomlHooksStateEntries(safeParseToml(config));
|
|
1954
|
+
for (const key of Object.keys(parsedConfigState ?? {})) {
|
|
1955
|
+
if (expectations.has(key) && !representations.has(key)) {
|
|
1956
|
+
preservedConflictKeys.add(key);
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
for (const [key, sourceRepresentations] of representations) {
|
|
1960
|
+
const isExactOwnedRepresentation = (representation) => {
|
|
1961
|
+
const expectedHashes = expectedManagedHookTrustHashes(expectations, key, representation.insideManagedMarkerBlock);
|
|
1962
|
+
return expectedHashes !== undefined &&
|
|
1963
|
+
representation.stateEntryCount === 1 &&
|
|
1964
|
+
!representation.hasComment &&
|
|
1965
|
+
representation.hasExactSemanticScope &&
|
|
1966
|
+
isExactlyManagedHookTrustStateValue(representation.value, expectedHashes);
|
|
1967
|
+
};
|
|
1968
|
+
if (!sourceRepresentations.some((representation) => expectedManagedHookTrustHashes(expectations, key, representation.insideManagedMarkerBlock) !== undefined))
|
|
1969
|
+
continue;
|
|
1970
|
+
const mayRemoveAll = sourceRepresentations.length === 1
|
|
1971
|
+
? isExactOwnedRepresentation(sourceRepresentations[0])
|
|
1972
|
+
: sourceRepresentations.every((representation) => representation.insideManagedMarkerBlock &&
|
|
1973
|
+
isExactOwnedRepresentation(representation));
|
|
1974
|
+
if (!mayRemoveAll) {
|
|
1975
|
+
preservedConflictKeys.add(key);
|
|
1976
|
+
continue;
|
|
1977
|
+
}
|
|
1978
|
+
for (const representation of sourceRepresentations) {
|
|
1979
|
+
removals.set(`${representation.start}:${representation.end}`, {
|
|
1980
|
+
start: representation.start,
|
|
1981
|
+
end: representation.end,
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
}
|
|
1985
|
+
if (removals.size === 0) {
|
|
1986
|
+
return { config, preservedConflictKeys };
|
|
1987
|
+
}
|
|
1988
|
+
const removed = new Set();
|
|
1989
|
+
for (const range of removals.values()) {
|
|
1990
|
+
for (let index = range.start; index < range.end; index += 1) {
|
|
1991
|
+
removed.add(index);
|
|
877
1992
|
}
|
|
878
|
-
i = nextEndIdx + 1;
|
|
879
1993
|
}
|
|
880
|
-
|
|
881
|
-
const proofStripped = options.managedTrustState
|
|
882
|
-
? stripProofManagedCodexHookTrustStateTables(withoutFenced, options.managedTrustState)
|
|
883
|
-
: { config: withoutFenced, preservedConflictKeys: new Set() };
|
|
1994
|
+
removeEmptyManagedHookTrustStateMarkerBlocks(source, removed);
|
|
884
1995
|
return {
|
|
885
|
-
config:
|
|
886
|
-
preservedConflictKeys
|
|
1996
|
+
config: lines.filter((_, index) => !removed.has(index)).join("\n").trimEnd(),
|
|
1997
|
+
preservedConflictKeys,
|
|
887
1998
|
};
|
|
888
1999
|
}
|
|
2000
|
+
function stripManagedCodexHookTrustStateForRefresh(config, priorManagedTrustState, finalManagedTrustState) {
|
|
2001
|
+
return stripProofManagedCodexHookTrustStateTables(config, managedHookTrustStateExpectations(priorManagedTrustState, finalManagedTrustState));
|
|
2002
|
+
}
|
|
2003
|
+
function rejectManagedCodexHookTrustStateConflicts(conflicts) {
|
|
2004
|
+
if (conflicts.size === 0)
|
|
2005
|
+
return;
|
|
2006
|
+
const keys = [...conflicts].sort((left, right) => left.localeCompare(right));
|
|
2007
|
+
throw new ManagedCodexHooksPlanError("managed_trust_key_conflict", `Refusing to replace existing Codex hook trust state for ${keys.join(", ")}. Remove or reconcile the conflicting [hooks.state] entry before running setup.`, { keys });
|
|
2008
|
+
}
|
|
889
2009
|
export function stripManagedCodexHookTrustState(config, options = {}) {
|
|
890
|
-
|
|
2010
|
+
const stripped = stripManagedCodexHookTrustStateForRefresh(config, options.priorManagedHookTrustState, options.managedTrustState ?? {});
|
|
2011
|
+
rejectManagedCodexHookTrustStateConflicts(stripped.preservedConflictKeys);
|
|
2012
|
+
return stripped.config;
|
|
891
2013
|
}
|
|
892
|
-
function renderManagedCodexHookTrustToml(managedTrustState
|
|
2014
|
+
function renderManagedCodexHookTrustToml(managedTrustState) {
|
|
893
2015
|
return Object.entries(managedTrustState)
|
|
894
|
-
.filter(([key]) => !excludedKeys.has(key))
|
|
895
2016
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
896
2017
|
.flatMap(([key, hookState]) => [
|
|
897
2018
|
`[hooks.state."${escapeTomlBasicString(key)}"]`,
|
|
@@ -901,21 +2022,92 @@ function renderManagedCodexHookTrustToml(managedTrustState, excludedKeys = new S
|
|
|
901
2022
|
.join("\n")
|
|
902
2023
|
.trimEnd();
|
|
903
2024
|
}
|
|
904
|
-
function
|
|
2025
|
+
function identicalLegacyHookTrustStateEntry(existing, expected) {
|
|
2026
|
+
if (!isPlainTomlRecord(existing))
|
|
2027
|
+
return false;
|
|
2028
|
+
if (!Object.hasOwn(existing, "trusted_hash") ||
|
|
2029
|
+
typeof existing.trusted_hash !== "string" ||
|
|
2030
|
+
existing.trusted_hash !== expected.trusted_hash ||
|
|
2031
|
+
Object.keys(existing).some((key) => key !== "trusted_hash" && key !== "enabled")) {
|
|
2032
|
+
return false;
|
|
2033
|
+
}
|
|
2034
|
+
return Object.hasOwn(existing, "enabled") === Object.hasOwn(expected, "enabled") &&
|
|
2035
|
+
existing.enabled === expected.enabled;
|
|
2036
|
+
}
|
|
2037
|
+
function legacyHookTrustStateConflict(keys, reason) {
|
|
2038
|
+
throw new ManagedCodexHooksPlanError("managed_trust_key_conflict", `Refusing to migrate legacy Codex hook trust state into conflicting config.toml entries for ${keys.join(", ")}.`, { keys: [...keys], ...(reason ? { reason } : {}) });
|
|
2039
|
+
}
|
|
2040
|
+
function appendLegacyHookTrustState(config, legacyTrustState) {
|
|
2041
|
+
if (!legacyTrustState || Object.keys(legacyTrustState).length === 0) {
|
|
2042
|
+
return config;
|
|
2043
|
+
}
|
|
2044
|
+
const parsed = safeParseToml(config);
|
|
2045
|
+
const keys = Object.keys(legacyTrustState).sort((left, right) => left.localeCompare(right));
|
|
2046
|
+
if (!parsed)
|
|
2047
|
+
legacyHookTrustStateConflict(keys, "config_parse_failure");
|
|
2048
|
+
const hooks = parsed.hooks;
|
|
2049
|
+
if (hooks !== undefined && !isPlainTomlRecord(hooks)) {
|
|
2050
|
+
legacyHookTrustStateConflict(keys, "hooks_table_invalid");
|
|
2051
|
+
}
|
|
2052
|
+
const hookState = isPlainTomlRecord(hooks) ? hooks.state : undefined;
|
|
2053
|
+
if (hookState !== undefined && !isPlainTomlRecord(hookState)) {
|
|
2054
|
+
legacyHookTrustStateConflict(keys, "hooks_state_table_invalid");
|
|
2055
|
+
}
|
|
2056
|
+
const existingTrustState = isPlainTomlRecord(hookState) ? hookState : {};
|
|
2057
|
+
const entriesToAppend = Object.entries(legacyTrustState)
|
|
2058
|
+
.filter(([key]) => !Object.hasOwn(existingTrustState, key));
|
|
2059
|
+
const conflictingKeys = Object.entries(legacyTrustState)
|
|
2060
|
+
.filter(([key, expected]) => Object.hasOwn(existingTrustState, key) &&
|
|
2061
|
+
!identicalLegacyHookTrustStateEntry(existingTrustState[key], expected))
|
|
2062
|
+
.map(([key]) => key)
|
|
2063
|
+
.sort((left, right) => left.localeCompare(right));
|
|
2064
|
+
if (conflictingKeys.length > 0)
|
|
2065
|
+
legacyHookTrustStateConflict(conflictingKeys);
|
|
2066
|
+
const trustToml = entriesToAppend
|
|
2067
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
2068
|
+
.flatMap(([key, state]) => [
|
|
2069
|
+
`[hooks.state."${escapeTomlBasicString(key)}"]`,
|
|
2070
|
+
`trusted_hash = "${escapeTomlBasicString(state.trusted_hash)}"`,
|
|
2071
|
+
...(typeof state.enabled === "boolean" ? [`enabled = ${state.enabled}`] : []),
|
|
2072
|
+
"",
|
|
2073
|
+
])
|
|
2074
|
+
.join("\n")
|
|
2075
|
+
.trimEnd();
|
|
2076
|
+
if (!trustToml)
|
|
2077
|
+
return config;
|
|
2078
|
+
const base = config.trimEnd();
|
|
2079
|
+
return [
|
|
2080
|
+
base,
|
|
2081
|
+
base ? "" : null,
|
|
2082
|
+
"# Migrated from legacy hooks.json state; kept in Codex config.toml because Codex 0.140 rejects top-level hooks.json state.",
|
|
2083
|
+
trustToml,
|
|
2084
|
+
"",
|
|
2085
|
+
].filter((line) => line !== null).join("\n");
|
|
2086
|
+
}
|
|
2087
|
+
function managedCodexHookOptionsFromMergeOptions(options) {
|
|
2088
|
+
return {
|
|
2089
|
+
codexHomeDir: options.codexHomeDir,
|
|
2090
|
+
platform: options.hookCommandPlatform,
|
|
2091
|
+
hooksContent: options.codexHooksContent,
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
function buildManagedCodexHookTrustStateForConfig(codexHooksFile, pkgRoot, options = {}, precomputedTrustState) {
|
|
2095
|
+
if (precomputedTrustState)
|
|
2096
|
+
return precomputedTrustState;
|
|
905
2097
|
if (!codexHooksFile)
|
|
906
2098
|
return {};
|
|
907
2099
|
return buildManagedCodexHookTrustState(codexHooksFile, pkgRoot, options);
|
|
908
2100
|
}
|
|
909
2101
|
export function upsertManagedCodexHookTrustState(config, pkgRoot, codexHooksFile, options = {}) {
|
|
910
|
-
const managedTrustState = buildManagedCodexHookTrustStateForConfig(codexHooksFile, pkgRoot, options);
|
|
911
|
-
const strippedResult =
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
const
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
return [
|
|
2102
|
+
const managedTrustState = buildManagedCodexHookTrustStateForConfig(codexHooksFile, pkgRoot, options, options.managedTrustState);
|
|
2103
|
+
const strippedResult = stripManagedCodexHookTrustStateForRefresh(config, options.priorManagedHookTrustState, managedTrustState);
|
|
2104
|
+
rejectManagedCodexHookTrustStateConflicts(strippedResult.preservedConflictKeys);
|
|
2105
|
+
const stripped = strippedResult.config.trimEnd();
|
|
2106
|
+
const hookTrustToml = renderManagedCodexHookTrustToml(managedTrustState);
|
|
2107
|
+
if (!hookTrustToml) {
|
|
2108
|
+
return appendLegacyHookTrustState(`${stripped}\n`, options.legacyHookTrustState);
|
|
2109
|
+
}
|
|
2110
|
+
return appendLegacyHookTrustState([
|
|
919
2111
|
stripped,
|
|
920
2112
|
"",
|
|
921
2113
|
OMX_HOOK_TRUST_START_MARKER,
|
|
@@ -923,7 +2115,7 @@ export function upsertManagedCodexHookTrustState(config, pkgRoot, codexHooksFile
|
|
|
923
2115
|
hookTrustToml,
|
|
924
2116
|
OMX_HOOK_TRUST_END_MARKER,
|
|
925
2117
|
"",
|
|
926
|
-
].filter((line, index) => index !== 0 || line.length > 0).join("\n");
|
|
2118
|
+
].filter((line, index) => index !== 0 || line.length > 0).join("\n"), options.legacyHookTrustState);
|
|
927
2119
|
}
|
|
928
2120
|
export function upsertPluginModeRuntimeFeatureFlags(config, codexHookFeatureFlag = DEFAULT_CODEX_HOOK_FEATURE_FLAG, options = {}) {
|
|
929
2121
|
const lines = config.split(/\r?\n/);
|
|
@@ -936,6 +2128,9 @@ export function upsertPluginModeRuntimeFeatureFlags(config, codexHookFeatureFlag
|
|
|
936
2128
|
const featureBlock = [
|
|
937
2129
|
"[features]",
|
|
938
2130
|
hookFeatureFlagLine,
|
|
2131
|
+
...(options.pluginScopedHooks && options.preserveNativeHooks
|
|
2132
|
+
? [formatCodexHookFeatureFlagLine(codexHookFeatureFlag)]
|
|
2133
|
+
: []),
|
|
939
2134
|
"goals = true",
|
|
940
2135
|
"",
|
|
941
2136
|
].join("\n");
|
|
@@ -959,9 +2154,15 @@ export function upsertPluginModeRuntimeFeatureFlags(config, codexHookFeatureFlag
|
|
|
959
2154
|
sectionEnd -= 1;
|
|
960
2155
|
}
|
|
961
2156
|
}
|
|
962
|
-
(
|
|
963
|
-
|
|
964
|
-
|
|
2157
|
+
if (options.pluginScopedHooks) {
|
|
2158
|
+
({ sectionEnd } = upsertPluginScopedHookFeatureFlagInSection(lines, featuresStart, sectionEnd));
|
|
2159
|
+
if (options.preserveNativeHooks) {
|
|
2160
|
+
({ sectionEnd } = upsertCodexHookFeatureFlagInSection(lines, featuresStart, sectionEnd, codexHookFeatureFlag));
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
else {
|
|
2164
|
+
({ sectionEnd } = upsertCodexHookFeatureFlagInSection(lines, featuresStart, sectionEnd, codexHookFeatureFlag));
|
|
2165
|
+
}
|
|
965
2166
|
let goalsIdx = -1;
|
|
966
2167
|
for (let i = featuresStart + 1; i < sectionEnd; i++) {
|
|
967
2168
|
if (/^\s*goals\s*=/.test(lines[i])) {
|
|
@@ -1425,35 +2626,45 @@ function upsertTuiStatusLine(config, preset = DEFAULT_STATUS_LINE_PRESET, option
|
|
|
1425
2626
|
// ---------------------------------------------------------------------------
|
|
1426
2627
|
// OMX [table] sections block (appended at end of file)
|
|
1427
2628
|
// ---------------------------------------------------------------------------
|
|
1428
|
-
export function stripExistingOmxBlocks(config) {
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
const
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
2629
|
+
export function stripExistingOmxBlocks(config, options = {}) {
|
|
2630
|
+
const inventory = collectManagedHookTrustStateSourceRepresentations(config);
|
|
2631
|
+
const { source } = inventory;
|
|
2632
|
+
const markerRanges = collectMarkerRanges(source, OMX_CONFIG_START_MARKER, OMX_CONFIG_END_MARKER, false, inventory.parsedAssignmentSpans);
|
|
2633
|
+
if (markerRanges.length === 0)
|
|
2634
|
+
return { cleaned: config, removed: 0 };
|
|
2635
|
+
const conflicts = managedMarkerTrustStateConflicts(source, inventory.representations, markerRanges, inventory.markerContainedHooksStateSpans, inventory.invalidMarkerContainedHooksStateSpans, inventory.parsedStatementSpans, inventory.parsedAssignmentSpans, managedHookTrustStateExpectations(options.priorManagedHookTrustState, options.managedTrustState));
|
|
2636
|
+
rejectManagedCodexHookTrustStateConflicts(conflicts);
|
|
2637
|
+
if (!source.isUnambiguous)
|
|
2638
|
+
return { cleaned: config, removed: 0 };
|
|
2639
|
+
const lines = sourceLines(config);
|
|
2640
|
+
if (markerRanges.some((range) => !lines[range.start] || !lines[range.end])) {
|
|
2641
|
+
return { cleaned: config, removed: 0 };
|
|
2642
|
+
}
|
|
2643
|
+
const removalRanges = markerRanges.map((range) => {
|
|
2644
|
+
const startLine = lines[range.start];
|
|
2645
|
+
const endLine = lines[range.end];
|
|
2646
|
+
const previousLine = lines[range.start - 1];
|
|
2647
|
+
const start = previousLine && /^# =+$/.test(previousLine.content.trim())
|
|
2648
|
+
? previousLine.start
|
|
2649
|
+
: startLine.start;
|
|
2650
|
+
return { start, end: endLine.end };
|
|
2651
|
+
});
|
|
2652
|
+
const segments = [];
|
|
2653
|
+
let cursor = 0;
|
|
2654
|
+
for (const range of removalRanges) {
|
|
2655
|
+
segments.push(config.slice(cursor, range.start));
|
|
2656
|
+
cursor = range.end;
|
|
2657
|
+
}
|
|
2658
|
+
segments.push(config.slice(cursor));
|
|
2659
|
+
const cleaned = segments
|
|
2660
|
+
.map((segment, index) => index === 0
|
|
2661
|
+
? segment.trimEnd()
|
|
2662
|
+
: index === segments.length - 1
|
|
2663
|
+
? segment.trimStart()
|
|
2664
|
+
: segment.trim())
|
|
2665
|
+
.filter(Boolean)
|
|
2666
|
+
.join("\n\n");
|
|
2667
|
+
return { cleaned, removed: markerRanges.length };
|
|
1457
2668
|
}
|
|
1458
2669
|
export function stripExistingSharedMcpRegistryBlock(config) {
|
|
1459
2670
|
let cleaned = config;
|
|
@@ -1688,7 +2899,7 @@ export function mergeSharedMcpRegistryBlock(config, servers, sourcePath) {
|
|
|
1688
2899
|
* OMX table-section block (MCP servers, TUI).
|
|
1689
2900
|
* Contains ONLY [table] sections — no bare keys.
|
|
1690
2901
|
*/
|
|
1691
|
-
function getOmxTablesBlock(pkgRoot, includeTui = true, statusLinePreset = DEFAULT_STATUS_LINE_PRESET, codexHooksFile, hookOptions = {}, includeFirstPartyMcp = false
|
|
2902
|
+
function getOmxTablesBlock(pkgRoot, includeTui = true, statusLinePreset = DEFAULT_STATUS_LINE_PRESET, codexHooksFile, hookOptions = {}, managedHookTrustState, includeFirstPartyMcp = false) {
|
|
1692
2903
|
const lines = [
|
|
1693
2904
|
"",
|
|
1694
2905
|
"# ============================================================",
|
|
@@ -1711,7 +2922,7 @@ function getOmxTablesBlock(pkgRoot, includeTui = true, statusLinePreset = DEFAUL
|
|
|
1711
2922
|
}
|
|
1712
2923
|
}
|
|
1713
2924
|
}
|
|
1714
|
-
const hookTrustToml = renderManagedCodexHookTrustToml(buildManagedCodexHookTrustStateForConfig(codexHooksFile, pkgRoot, hookOptions
|
|
2925
|
+
const hookTrustToml = renderManagedCodexHookTrustToml(buildManagedCodexHookTrustStateForConfig(codexHooksFile, pkgRoot, hookOptions, managedHookTrustState));
|
|
1715
2926
|
if (hookTrustToml) {
|
|
1716
2927
|
lines.push("");
|
|
1717
2928
|
lines.push("# OMX-owned Codex hook trust state");
|
|
@@ -1750,6 +2961,10 @@ function getOmxTablesBlock(pkgRoot, includeTui = true, statusLinePreset = DEFAUL
|
|
|
1750
2961
|
*/
|
|
1751
2962
|
export function buildMergedConfig(existingConfig, pkgRoot, options = {}) {
|
|
1752
2963
|
let existing = stripOmxSeededBehavioralDefaults(existingConfig);
|
|
2964
|
+
const hookOptions = managedCodexHookOptionsFromMergeOptions(options);
|
|
2965
|
+
const managedTrustState = buildManagedCodexHookTrustStateForConfig(options.codexHooksFile, pkgRoot, hookOptions, options.managedHookTrustState);
|
|
2966
|
+
const preflightHookTrustStrip = stripManagedCodexHookTrustStateForRefresh(existing, options.priorManagedHookTrustState, managedTrustState);
|
|
2967
|
+
rejectManagedCodexHookTrustStateConflicts(preflightHookTrustStrip.preservedConflictKeys);
|
|
1753
2968
|
const preservedFirstPartyMcpSections = options.preserveExistingFirstPartyMcp === true &&
|
|
1754
2969
|
options.includeFirstPartyMcp !== true
|
|
1755
2970
|
? extractFirstPartyOmxMcpSections(existing)
|
|
@@ -1758,7 +2973,10 @@ export function buildMergedConfig(existingConfig, pkgRoot, options = {}) {
|
|
|
1758
2973
|
const statusLinePreset = options.statusLinePreset ?? DEFAULT_STATUS_LINE_PRESET;
|
|
1759
2974
|
const customizedManagedTuiSections = extractCustomizedTuiSectionsFromOmxBlocks(existing);
|
|
1760
2975
|
if (existing.includes(OMX_CONFIG_MARKER)) {
|
|
1761
|
-
const stripped = stripExistingOmxBlocks(existing
|
|
2976
|
+
const stripped = stripExistingOmxBlocks(existing, {
|
|
2977
|
+
managedTrustState,
|
|
2978
|
+
priorManagedHookTrustState: options.priorManagedHookTrustState,
|
|
2979
|
+
});
|
|
1762
2980
|
existing = stripped.cleaned;
|
|
1763
2981
|
if (customizedManagedTuiSections.length > 0) {
|
|
1764
2982
|
existing = `${existing.trimEnd()}\n\n${customizedManagedTuiSections.join("\n\n")}\n`;
|
|
@@ -1777,13 +2995,8 @@ export function buildMergedConfig(existingConfig, pkgRoot, options = {}) {
|
|
|
1777
2995
|
existing = `${`notify = ${formatTomlStringArray(userNotifyToPreserve)}`}\n${existing.trimStart()}`;
|
|
1778
2996
|
}
|
|
1779
2997
|
existing = stripOrphanedManagedNotify(existing, pkgRoot).trimStart();
|
|
1780
|
-
const
|
|
1781
|
-
|
|
1782
|
-
platform: options.hookCommandPlatform,
|
|
1783
|
-
});
|
|
1784
|
-
const hookTrustStrip = stripManagedCodexHookTrustStateWithResult(existing, {
|
|
1785
|
-
managedTrustState,
|
|
1786
|
-
});
|
|
2998
|
+
const hookTrustStrip = stripManagedCodexHookTrustStateForRefresh(existing, options.priorManagedHookTrustState, managedTrustState);
|
|
2999
|
+
rejectManagedCodexHookTrustStateConflicts(hookTrustStrip.preservedConflictKeys);
|
|
1787
3000
|
existing = hookTrustStrip.config;
|
|
1788
3001
|
if (options.modelOverride) {
|
|
1789
3002
|
existing = stripRootLevelKeys(existing, ["model"]);
|
|
@@ -1803,17 +3016,15 @@ export function buildMergedConfig(existingConfig, pkgRoot, options = {}) {
|
|
|
1803
3016
|
const topLines = getOmxTopLevelLines(pkgRoot, existing, options.modelOverride, options.notifyCommand === undefined
|
|
1804
3017
|
? getDefaultNotifyCommand(pkgRoot)
|
|
1805
3018
|
: options.notifyCommand);
|
|
1806
|
-
const tablesBlock = getOmxTablesBlock(pkgRoot, includeTui && !tuiUpsert.hadExistingTui, statusLinePreset, options.codexHooksFile,
|
|
1807
|
-
codexHomeDir: options.codexHomeDir,
|
|
1808
|
-
platform: options.hookCommandPlatform,
|
|
1809
|
-
}, options.includeFirstPartyMcp === true, hookTrustStrip.preservedConflictKeys);
|
|
3019
|
+
const tablesBlock = getOmxTablesBlock(pkgRoot, includeTui && !tuiUpsert.hadExistingTui, statusLinePreset, options.codexHooksFile, hookOptions, managedTrustState, options.includeFirstPartyMcp === true);
|
|
1810
3020
|
const sharedRegistryBlock = getSharedMcpRegistryBlock(options.sharedMcpServers ?? [], options.sharedMcpRegistrySource, existing);
|
|
1811
3021
|
let body = existing.trim();
|
|
1812
3022
|
if (sharedRegistryBlock) {
|
|
1813
3023
|
body = body ? `${body}\n\n${sharedRegistryBlock}` : sharedRegistryBlock;
|
|
1814
3024
|
}
|
|
1815
3025
|
const bodySeparator = body.length > 0 && !/^\s*\[/.test(body) ? "\n" : "\n\n";
|
|
1816
|
-
|
|
3026
|
+
const merged = addDefaultLauncherMcpStartupTimeouts(topLines.join("\n") + bodySeparator + body + "\n" + tablesBlock);
|
|
3027
|
+
return appendLegacyHookTrustState(merged, options.legacyHookTrustState);
|
|
1817
3028
|
}
|
|
1818
3029
|
/**
|
|
1819
3030
|
* Detect and repair upgrade-era managed config incompatibilities in config.toml.
|
|
@@ -1826,6 +3037,33 @@ export function buildMergedConfig(existingConfig, pkgRoot, options = {}) {
|
|
|
1826
3037
|
*
|
|
1827
3038
|
* Returns `true` if a repair was performed.
|
|
1828
3039
|
*/
|
|
3040
|
+
function managedHookTrustProofRequiredForRepair(config) {
|
|
3041
|
+
const inventory = collectManagedHookTrustStateSourceRepresentations(config);
|
|
3042
|
+
return managedMarkerTrustStateConflicts(inventory.source, inventory.representations, inventory.managedMarkerRanges, inventory.markerContainedHooksStateSpans, inventory.invalidMarkerContainedHooksStateSpans, inventory.parsedStatementSpans, inventory.parsedAssignmentSpans, managedHookTrustStateExpectations()).size > 0;
|
|
3043
|
+
}
|
|
3044
|
+
async function launchRepairOptionsWithManagedHookTrustProof(configPath, config, options) {
|
|
3045
|
+
if (!managedHookTrustProofRequiredForRepair(config)) {
|
|
3046
|
+
return options;
|
|
3047
|
+
}
|
|
3048
|
+
const hooksPath = options.codexHooksFile ?? resolve(configPath, "..", "hooks.json");
|
|
3049
|
+
let hooksContent;
|
|
3050
|
+
try {
|
|
3051
|
+
hooksContent = await readFile(hooksPath, "utf-8");
|
|
3052
|
+
}
|
|
3053
|
+
catch (error) {
|
|
3054
|
+
throw new ManagedCodexHooksPlanError("invalid_document", `Cannot safely repair config.toml because the current hooks artifact at ${hooksPath} could not be read.`, { cause: String(error), hooksPath });
|
|
3055
|
+
}
|
|
3056
|
+
const trustScan = scanManagedCodexHookTrustStateFromContent(hooksContent, hooksPath, managedCodexHookOptionsFromMergeOptions(options));
|
|
3057
|
+
if (!trustScan.ok)
|
|
3058
|
+
throw trustScan.error;
|
|
3059
|
+
return {
|
|
3060
|
+
...options,
|
|
3061
|
+
codexHooksFile: hooksPath,
|
|
3062
|
+
codexHooksContent: hooksContent,
|
|
3063
|
+
managedHookTrustState: trustScan.trustState,
|
|
3064
|
+
priorManagedHookTrustState: trustScan.trustState,
|
|
3065
|
+
};
|
|
3066
|
+
}
|
|
1829
3067
|
export async function repairConfigIfNeeded(configPath, pkgRoot, options = {}) {
|
|
1830
3068
|
if (!existsSync(configPath))
|
|
1831
3069
|
return false;
|
|
@@ -1835,8 +3073,10 @@ export async function repairConfigIfNeeded(configPath, pkgRoot, options = {}) {
|
|
|
1835
3073
|
const hasLauncherTimeoutGap = findLauncherTimeoutRepairTargets(content).length > 0;
|
|
1836
3074
|
if (tuiCount <= 1 && !hasLegacyTeamRunTable && !hasLauncherTimeoutGap)
|
|
1837
3075
|
return false;
|
|
1838
|
-
// Managed config compatibility issue detected —
|
|
1839
|
-
|
|
3076
|
+
// Managed config compatibility issue detected — derive proof from the current
|
|
3077
|
+
// hooks artifact before any marker stripping can replace managed trust state.
|
|
3078
|
+
const repairOptions = await launchRepairOptionsWithManagedHookTrustProof(configPath, content, options);
|
|
3079
|
+
const repaired = buildMergedConfig(content, pkgRoot, repairOptions);
|
|
1840
3080
|
if (repaired === content)
|
|
1841
3081
|
return false;
|
|
1842
3082
|
await writeFile(configPath, repaired);
|
|
@@ -1847,12 +3087,6 @@ export async function mergeConfig(configPath, pkgRoot, options = {}) {
|
|
|
1847
3087
|
if (existsSync(configPath)) {
|
|
1848
3088
|
existing = await readFile(configPath, "utf-8");
|
|
1849
3089
|
}
|
|
1850
|
-
if (existing.includes("oh-my-codex (OMX) Configuration")) {
|
|
1851
|
-
const stripped = stripExistingOmxBlocks(existing);
|
|
1852
|
-
if (options.verbose && stripped.removed > 0) {
|
|
1853
|
-
console.log(" Updating existing OMX config block.");
|
|
1854
|
-
}
|
|
1855
|
-
}
|
|
1856
3090
|
const finalConfig = buildMergedConfig(existing, pkgRoot, options);
|
|
1857
3091
|
await writeFile(configPath, finalConfig);
|
|
1858
3092
|
if (options.verbose) {
|