oh-my-codex 0.20.0 → 0.20.2
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 +6 -6
- package/Cargo.toml +1 -1
- package/README.md +10 -1
- package/dist/adapt/__tests__/hermes.test.js.map +1 -1
- package/dist/adapt/contracts.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.map +1 -1
- package/dist/auth/hotswap.js +17 -4
- 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 +4 -4
- 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.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 +27 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-context-window-warning.test.js +83 -78
- package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-invalid-config.test.js +56 -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 +735 -14
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- 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 +216 -11
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +49 -3
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/mcp-parity.test.js +84 -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 +128 -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 +187 -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 +129 -0
- package/dist/cli/__tests__/ralplan.test.js.map +1 -0
- package/dist/cli/__tests__/resume.test.js +6 -1
- 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.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 +131 -5
- 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 +2264 -17
- 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 +168 -5
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/setup-scope.test.js +73 -6
- 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.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 +2067 -26
- 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.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 +20 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +636 -161
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +11 -3
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +108 -20
- package/dist/cli/index.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 +18 -0
- package/dist/cli/native-hook-claim-journal.d.ts.map +1 -0
- package/dist/cli/native-hook-claim-journal.js +239 -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 +14 -0
- package/dist/cli/ralplan.d.ts.map +1 -0
- package/dist/cli/ralplan.js +203 -0
- package/dist/cli/ralplan.js.map +1 -0
- 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 +53 -0
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +1403 -400
- 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 +8 -0
- package/dist/cli/uninstall.d.ts.map +1 -1
- package/dist/cli/uninstall.js +947 -99
- 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.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 +1049 -135
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +28 -49
- package/dist/config/__tests__/generator-notify.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 +40 -26
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +1159 -288
- package/dist/config/generator.js.map +1 -1
- package/dist/config/mcp-registry.js.map +1 -1
- package/dist/config/models.d.ts +2 -2
- package/dist/config/models.d.ts.map +1 -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 +1372 -2
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js +14 -0
- package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-all-workers-idle.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +669 -1
- 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.map +1 -1
- package/dist/hooks/__tests__/notify-hook-team-leader-nudge.test.js +8 -1
- 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.map +1 -1
- package/dist/hooks/__tests__/notify-hook-tmux-heal.test.js.map +1 -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.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 +532 -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 +139 -34
- package/dist/hooks/session.d.ts.map +1 -1
- package/dist/hooks/session.js +1239 -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 +146 -2
- package/dist/leader/__tests__/contract.test.js.map +1 -1
- package/dist/leader/contract.d.ts +26 -5
- package/dist/leader/contract.d.ts.map +1 -1
- package/dist/leader/contract.js +131 -7
- package/dist/leader/contract.js.map +1 -1
- package/dist/mcp/__tests__/bootstrap.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/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.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 +23 -23
- package/dist/mcp/wiki-server.d.ts.map +1 -1
- 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/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.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.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +8 -5
- 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 +403 -8
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/__tests__/runtime.test.js +112 -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 +155 -60
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/ralplan/runtime.d.ts +2 -2
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +1 -2
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/runtime/bridge.d.ts.map +1 -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 +5414 -801
- 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__/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 +205 -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 +175 -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 +848 -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.map +1 -1
- package/dist/scripts/codex-native-hook.js +825 -172
- 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 +136 -30
- 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 +39 -13
- 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 +1 -1
- 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-tmux-guard.d.ts +4 -4
- 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.d.ts +2 -2
- 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 +25 -16
- 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 +266 -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 +879 -2
- package/dist/scripts/smoke-packed-install.d.ts.map +1 -1
- package/dist/scripts/smoke-packed-install.js +1528 -7
- 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__/operations.test.js +64 -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.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__/adapted-role-binding.test.d.ts +2 -0
- package/dist/subagents/__tests__/adapted-role-binding.test.d.ts.map +1 -0
- package/dist/subagents/__tests__/adapted-role-binding.test.js +988 -0
- package/dist/subagents/__tests__/adapted-role-binding.test.js.map +1 -0
- 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 +273 -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/adapted-role-binding.d.ts +17 -0
- package/dist/subagents/adapted-role-binding.d.ts.map +1 -0
- package/dist/subagents/adapted-role-binding.js +75 -0
- package/dist/subagents/adapted-role-binding.js.map +1 -0
- 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 +172 -0
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +1077 -61
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/approved-execution.test.js.map +1 -1
- package/dist/team/__tests__/delivery-e2e-smoke.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +231 -1
- 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.test.js +401 -1
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +181 -0
- 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__/tmux-session.test.js +434 -4
- 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/api-interop.d.ts +2 -2
- package/dist/team/api-interop.d.ts.map +1 -1
- 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/followup-planner.js.map +1 -1
- package/dist/team/goal-workflow.js.map +1 -1
- package/dist/team/idle-nudge.d.ts.map +1 -1
- package/dist/team/model-contract.d.ts +20 -1
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +267 -30
- 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.map +1 -1
- package/dist/team/runtime.js +83 -52
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts +9 -7
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +69 -28
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/dispatch-lock.js.map +1 -1
- package/dist/team/state/dispatch.js.map +1 -1
- package/dist/team/state/events.js.map +1 -1
- package/dist/team/state/tasks.js.map +1 -1
- package/dist/team/state-root.js.map +1 -1
- 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/tmux-session.d.ts +2 -0
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +90 -42
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/worker-bootstrap.js.map +1 -1
- 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__/agents-md.test.js +16 -0
- package/dist/utils/__tests__/agents-md.test.js.map +1 -1
- package/dist/utils/agents-md.d.ts.map +1 -1
- package/dist/utils/agents-md.js +4 -3
- package/dist/utils/agents-md.js.map +1 -1
- package/dist/utils/agents-model-table.js.map +1 -1
- 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.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.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/skills/omx-setup/SKILL.md +10 -5
- package/plugins/oh-my-codex/skills/ralph/SKILL.md +4 -2
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -2
- package/skills/omx-setup/SKILL.md +10 -5
- package/skills/ralph/SKILL.md +4 -2
- package/skills/ralplan/SKILL.md +5 -2
- package/src/scripts/__tests__/codex-native-hook.test.ts +20497 -12377
- 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__/role-intent-bootstrap-e2e-3181.test.ts +215 -0
- package/src/scripts/__tests__/role-intent-durable-recovery-3181.test.ts +193 -0
- package/src/scripts/__tests__/smoke-packed-install.test.ts +991 -4
- package/src/scripts/codex-native-hook.ts +918 -172
- package/src/scripts/notify-fallback-delivery.ts +416 -0
- package/src/scripts/notify-fallback-watcher.ts +132 -40
- package/src/scripts/notify-hook/auto-nudge.ts +43 -12
- 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/tmux-injection.ts +38 -21
- package/src/scripts/notify-hook/visual-verdict.ts +22 -12
- package/src/scripts/notify-hook.ts +335 -90
- package/src/scripts/smoke-packed-install.ts +1829 -6
|
@@ -17,22 +17,45 @@ import {
|
|
|
17
17
|
} from "../state/skill-active.js";
|
|
18
18
|
import {
|
|
19
19
|
isTrustedSubagentThread,
|
|
20
|
+
OMX_ADAPTED_PROVENANCE,
|
|
21
|
+
attestLeaderThread,
|
|
20
22
|
readSubagentSessionSummary,
|
|
21
23
|
readSubagentSessionLedger,
|
|
22
24
|
readSubagentTrackingState,
|
|
25
|
+
readSubagentTrackingStateStrict,
|
|
26
|
+
recordSubagentTurn,
|
|
23
27
|
recordSubagentTurnForSession,
|
|
28
|
+
resolveInstalledRoleName,
|
|
24
29
|
} from "../subagents/tracker.js";
|
|
30
|
+
import {
|
|
31
|
+
bindAndPublishAdaptedRole,
|
|
32
|
+
NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_TTL_MS,
|
|
33
|
+
recoverAdaptedRoleBindings,
|
|
34
|
+
} from "../subagents/adapted-role-binding.js";
|
|
35
|
+
import { readRoleRoutingMarker, writeRoleRoutingMarker } from "../subagents/role-routing-marker.js";
|
|
25
36
|
import { resolveCanonicalTeamStateRoot, resolveWorkerNotifyTeamStateRootPath } from "../team/state-root.js";
|
|
26
37
|
import { inferTerminalLifecycleOutcome } from "../runtime/run-outcome.js";
|
|
27
38
|
import {
|
|
39
|
+
appendPromptSessionProvenanceRejection,
|
|
28
40
|
appendToLog,
|
|
41
|
+
isSessionPointerLaunchAbort,
|
|
29
42
|
isSessionStale,
|
|
30
43
|
isSessionStateUsable,
|
|
44
|
+
normalizeSessionId,
|
|
45
|
+
readSessionPointer,
|
|
31
46
|
readSessionState,
|
|
32
47
|
readUsableSessionState,
|
|
33
48
|
reconcileNativeSessionStart,
|
|
49
|
+
resolveSessionPointerContext,
|
|
34
50
|
type SessionState,
|
|
35
51
|
} from "../hooks/session.js";
|
|
52
|
+
import {
|
|
53
|
+
evaluateResolvedPromptTurn,
|
|
54
|
+
extractSelectedTargetOwnerEvidence,
|
|
55
|
+
preflightSelectedTargetOwner,
|
|
56
|
+
type PromptThreadFacts,
|
|
57
|
+
type ResolvedPromptTurnContext,
|
|
58
|
+
} from "../hooks/prompt-session-provenance.js";
|
|
36
59
|
import {
|
|
37
60
|
appendTeamEvent,
|
|
38
61
|
readTeamLeaderAttention,
|
|
@@ -42,13 +65,20 @@ import {
|
|
|
42
65
|
writeTeamLeaderAttention,
|
|
43
66
|
writeTeamPhase,
|
|
44
67
|
} from "../team/state.js";
|
|
45
|
-
import {
|
|
68
|
+
import { omxNotepadPath, resolveProjectMemoryPath } from "../utils/paths.js";
|
|
46
69
|
import { findGitLayout } from "../utils/git-layout.js";
|
|
47
|
-
import { getBaseStateDir, getStateFilePath, getStatePath, getAuthoritativeActiveStatePaths } from "../mcp/state-paths.js";
|
|
48
70
|
import {
|
|
49
|
-
|
|
50
|
-
|
|
71
|
+
getAuthoritativeActiveStatePaths,
|
|
72
|
+
getBaseStateDir,
|
|
73
|
+
getStateFilePath,
|
|
74
|
+
getStatePath,
|
|
75
|
+
resolveWritableStateScope,
|
|
76
|
+
WRITABLE_STATE_SCOPE_ERRORS,
|
|
77
|
+
} from "../mcp/state-paths.js";
|
|
78
|
+
import {
|
|
79
|
+
classifyKeywordInput,
|
|
51
80
|
recordSkillActivation,
|
|
81
|
+
type KeywordInputClassification,
|
|
52
82
|
type SkillActiveState,
|
|
53
83
|
} from "../hooks/keyword-detector.js";
|
|
54
84
|
import { buildDeepInterviewConfigInstruction } from "../hooks/deep-interview-config-instruction.js";
|
|
@@ -59,6 +89,7 @@ import {
|
|
|
59
89
|
normalizeAutoNudgeSignatureText,
|
|
60
90
|
resolveEffectiveAutoNudgeResponse,
|
|
61
91
|
} from "./notify-hook/auto-nudge.js";
|
|
92
|
+
import { probeActualTmuxInstanceEvidence, tmuxEvidenceBindsCandidate } from "./notify-hook/managed-tmux.js";
|
|
62
93
|
import {
|
|
63
94
|
SLOPPY_FALLBACK_GROUNDING_PATTERNS,
|
|
64
95
|
SLOPPY_FALLBACK_IMPLEMENTATION_CONTEXT_PATTERNS,
|
|
@@ -89,7 +120,6 @@ import {
|
|
|
89
120
|
onSessionStart as buildWikiSessionStartContext,
|
|
90
121
|
} from "../wiki/lifecycle.js";
|
|
91
122
|
import { readAutoresearchCompletionStatus, readAutoresearchModeStateForActiveDecision } from "../autoresearch/skill-validation.js";
|
|
92
|
-
import { AGENT_DEFINITIONS } from "../agents/definitions.js";
|
|
93
123
|
import { deriveAutopilotChildPhase, normalizeAutopilotPhase } from "../autopilot/fsm.js";
|
|
94
124
|
import {
|
|
95
125
|
CONDUCTOR_ORCHESTRATION_METADATA_PREFIXES,
|
|
@@ -98,9 +128,13 @@ import {
|
|
|
98
128
|
NATIVE_SUBAGENT_SUPPORT_BLOCKER_FILE,
|
|
99
129
|
actionKindForConductorArtifact,
|
|
100
130
|
authorizeConductorAction,
|
|
131
|
+
buildRoleRoutingUnavailableGuidance,
|
|
101
132
|
buildUnsupportedNativeSubagentGuidance,
|
|
102
133
|
classifyConductorArtifactKind,
|
|
134
|
+
isNativeSubagentSpawnToolName,
|
|
135
|
+
isRoleRoutingUnavailableEvidence,
|
|
103
136
|
isUnsupportedNativeSubagentEvidence,
|
|
137
|
+
parseRoleIntentCorrelationToken,
|
|
104
138
|
resolveNativeSubagentSupportStatus,
|
|
105
139
|
type NativeSubagentUnsupportedReason,
|
|
106
140
|
} from "../leader/contract.js";
|
|
@@ -130,7 +164,6 @@ import {
|
|
|
130
164
|
} from "../question/deep-interview.js";
|
|
131
165
|
import { readAutopilotDeepInterviewQuestionWaitState } from "../question/autopilot-wait.js";
|
|
132
166
|
import {
|
|
133
|
-
buildDocumentRefreshAdvisoryOutput,
|
|
134
167
|
evaluateFinalHandoffDocumentRefresh,
|
|
135
168
|
isFinalHandoffDocumentRefreshCandidate,
|
|
136
169
|
} from "../document-refresh/enforcer.js";
|
|
@@ -209,9 +242,6 @@ const RALPH_LIVE_RISK_PATTERNS = [
|
|
|
209
242
|
/\b(?:database|db|terraform|kubectl|kubernetes|aws|gcp|azure|external|destructive)\b/i,
|
|
210
243
|
/\b(?:telegram|vps|service|restart|send|notify|notification|notifications|cron)\b/i,
|
|
211
244
|
] as const;
|
|
212
|
-
const KNOWN_TYPED_AGENT_ROLES = new Set(Object.keys(AGENT_DEFINITIONS));
|
|
213
|
-
let installedTypedAgentRoleNamesCacheKey = "";
|
|
214
|
-
let installedTypedAgentRoleNamesCache: Set<string> = new Set();
|
|
215
245
|
const RALPH_TASK_TEXT_FIELDS = [
|
|
216
246
|
"task_description",
|
|
217
247
|
"taskDescription",
|
|
@@ -237,6 +267,19 @@ function safeString(value: unknown): string {
|
|
|
237
267
|
return typeof value === "string" ? value : "";
|
|
238
268
|
}
|
|
239
269
|
|
|
270
|
+
async function resolveVerifiedOwnerOmxSessionId(): Promise<string | undefined> {
|
|
271
|
+
const candidate = normalizeSessionId(process.env.OMX_SESSION_ID);
|
|
272
|
+
if (!candidate) return undefined;
|
|
273
|
+
const evidence = await probeActualTmuxInstanceEvidence(process.env.TMUX_PANE);
|
|
274
|
+
return tmuxEvidenceBindsCandidate(evidence, candidate) ? candidate : undefined;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function isImplicitWritableScopeFailure(error: unknown): boolean {
|
|
278
|
+
return error instanceof Error
|
|
279
|
+
&& (error.message === WRITABLE_STATE_SCOPE_ERRORS.unboundEnvironment
|
|
280
|
+
|| error.message === WRITABLE_STATE_SCOPE_ERRORS.unusableSession);
|
|
281
|
+
}
|
|
282
|
+
|
|
240
283
|
function safeObject(value: unknown): Record<string, unknown> {
|
|
241
284
|
return value && typeof value === "object" ? value as Record<string, unknown> : {};
|
|
242
285
|
}
|
|
@@ -332,8 +375,10 @@ interface NativeSubagentSessionStartMetadata {
|
|
|
332
375
|
parentThreadId: string;
|
|
333
376
|
agentNickname?: string;
|
|
334
377
|
agentRole?: string;
|
|
378
|
+
correlationToken?: string;
|
|
335
379
|
}
|
|
336
380
|
|
|
381
|
+
|
|
337
382
|
function readBoundedFirstLineSync(path: string): string {
|
|
338
383
|
const fd = openSync(path, "r");
|
|
339
384
|
try {
|
|
@@ -362,6 +407,20 @@ function readBoundedFirstLineSync(path: string): string {
|
|
|
362
407
|
}
|
|
363
408
|
}
|
|
364
409
|
|
|
410
|
+
function selectAuthoritativeTaskName(
|
|
411
|
+
threadSpawn: unknown,
|
|
412
|
+
subagent: unknown,
|
|
413
|
+
payload: unknown,
|
|
414
|
+
): { present: boolean; value: unknown } {
|
|
415
|
+
for (const obj of [threadSpawn, subagent, payload]) {
|
|
416
|
+
if (obj && typeof obj === "object" && "task_name" in obj) {
|
|
417
|
+
return { present: true, value: (obj as Record<string, unknown>).task_name };
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return { present: false, value: undefined };
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
|
|
365
424
|
function readNativeSubagentSessionStartMetadata(transcriptPath: string): NativeSubagentSessionStartMetadata | null {
|
|
366
425
|
const normalizedPath = transcriptPath.trim();
|
|
367
426
|
if (!normalizedPath) return null;
|
|
@@ -379,18 +438,44 @@ function readNativeSubagentSessionStartMetadata(transcriptPath: string): NativeS
|
|
|
379
438
|
const parentThreadId = safeString(threadSpawn.parent_thread_id).trim();
|
|
380
439
|
if (!parentThreadId) return null;
|
|
381
440
|
|
|
382
|
-
const
|
|
383
|
-
|
|
441
|
+
const agentNicknameCarrierValues = [
|
|
442
|
+
threadSpawn.agent_nickname ?? threadSpawn.agentNickname,
|
|
443
|
+
subagent.agent_nickname ?? subagent.agentNickname,
|
|
444
|
+
payload.agent_nickname ?? payload.agentNickname,
|
|
445
|
+
];
|
|
446
|
+
const agentNickname = safeString(agentNicknameCarrierValues[0]).trim();
|
|
447
|
+
const authoritativeTaskName = selectAuthoritativeTaskName(threadSpawn, subagent, payload);
|
|
448
|
+
const correlationToken = authoritativeTaskName.present
|
|
449
|
+
? parseRoleIntentCorrelationToken(authoritativeTaskName.value)
|
|
450
|
+
: undefined;
|
|
451
|
+
const agentRole = safeString(
|
|
452
|
+
threadSpawn.agent_role
|
|
453
|
+
?? threadSpawn.agentRole
|
|
454
|
+
?? threadSpawn.agent_type
|
|
455
|
+
?? threadSpawn.agentType
|
|
456
|
+
?? payload.agent_role
|
|
457
|
+
?? payload.agentRole
|
|
458
|
+
?? payload.agent_type
|
|
459
|
+
?? payload.agentType,
|
|
460
|
+
).trim();
|
|
384
461
|
return {
|
|
385
462
|
parentThreadId,
|
|
386
463
|
...(agentNickname ? { agentNickname } : {}),
|
|
387
464
|
...(agentRole ? { agentRole } : {}),
|
|
465
|
+
...(correlationToken ? { correlationToken } : {}),
|
|
388
466
|
};
|
|
467
|
+
|
|
389
468
|
} catch {
|
|
390
469
|
return null;
|
|
391
470
|
}
|
|
392
471
|
}
|
|
393
472
|
|
|
473
|
+
function reportRoleRoutingBindingFailure(error: unknown): void {
|
|
474
|
+
console.error(
|
|
475
|
+
`[omx] SECURITY: native adapted role binding did not complete; retained binding will be recovered: ${error instanceof Error ? error.message : String(error)}`,
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
|
|
394
479
|
async function recordNativeSubagentSessionStart(
|
|
395
480
|
cwd: string,
|
|
396
481
|
canonicalSessionId: string,
|
|
@@ -400,25 +485,79 @@ async function recordNativeSubagentSessionStart(
|
|
|
400
485
|
): Promise<void> {
|
|
401
486
|
const parentThreadId = metadata.parentThreadId.trim();
|
|
402
487
|
const childThreadId = childSessionId.trim();
|
|
488
|
+
const correlationSessionId = canonicalSessionId.trim() || parentThreadId;
|
|
403
489
|
const trackingSessionIds = [...new Set([
|
|
404
490
|
canonicalSessionId.trim(),
|
|
405
491
|
parentThreadId,
|
|
406
492
|
].filter(Boolean))];
|
|
407
|
-
|
|
408
|
-
|
|
493
|
+
let adaptedRoleIntent: {
|
|
494
|
+
role: string;
|
|
495
|
+
provenanceKind: typeof OMX_ADAPTED_PROVENANCE;
|
|
496
|
+
} | null = null;
|
|
497
|
+
|
|
498
|
+
if (!metadata.agentRole && correlationSessionId && parentThreadId) {
|
|
499
|
+
try {
|
|
500
|
+
const bound = bindAndPublishAdaptedRole(
|
|
501
|
+
cwd,
|
|
502
|
+
getBaseStateDir(cwd),
|
|
503
|
+
{
|
|
504
|
+
correlationSessionId,
|
|
505
|
+
parentThreadId,
|
|
506
|
+
correlationToken: metadata.correlationToken,
|
|
507
|
+
},
|
|
508
|
+
(state, intent) => {
|
|
509
|
+
let next = state;
|
|
510
|
+
for (const sessionId of trackingSessionIds) {
|
|
511
|
+
if (parentThreadId && parentThreadId !== childThreadId) {
|
|
512
|
+
next = recordSubagentTurn(next, {
|
|
513
|
+
sessionId,
|
|
514
|
+
threadId: parentThreadId,
|
|
515
|
+
kind: "leader",
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
next = recordSubagentTurn(next, {
|
|
519
|
+
sessionId,
|
|
520
|
+
threadId: childThreadId,
|
|
521
|
+
kind: "subagent",
|
|
522
|
+
...(parentThreadId && parentThreadId !== childThreadId ? { leaderThreadId: parentThreadId } : {}),
|
|
523
|
+
mode: intent.role,
|
|
524
|
+
role: intent.role,
|
|
525
|
+
provenanceKind: intent.provenanceKind,
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
return next;
|
|
529
|
+
},
|
|
530
|
+
);
|
|
531
|
+
adaptedRoleIntent = bound ? { role: bound.role, provenanceKind: OMX_ADAPTED_PROVENANCE } : null;
|
|
532
|
+
} catch (error) {
|
|
533
|
+
reportRoleRoutingBindingFailure(error);
|
|
534
|
+
throw error;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (!adaptedRoleIntent) {
|
|
539
|
+
for (const sessionId of trackingSessionIds) {
|
|
540
|
+
if (parentThreadId && parentThreadId !== childThreadId) {
|
|
541
|
+
await recordSubagentTurnForSession(cwd, {
|
|
542
|
+
sessionId,
|
|
543
|
+
threadId: parentThreadId,
|
|
544
|
+
kind: "leader",
|
|
545
|
+
}).catch(() => {});
|
|
546
|
+
}
|
|
409
547
|
await recordSubagentTurnForSession(cwd, {
|
|
410
548
|
sessionId,
|
|
411
|
-
threadId:
|
|
412
|
-
kind:
|
|
549
|
+
threadId: childThreadId,
|
|
550
|
+
kind: "subagent",
|
|
551
|
+
...(parentThreadId && parentThreadId !== childThreadId ? { leaderThreadId: parentThreadId } : {}),
|
|
552
|
+
mode: metadata.agentRole,
|
|
413
553
|
}).catch(() => {});
|
|
414
554
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}).catch(() => {});
|
|
555
|
+
refreshNativeSubagentRoleRoutingMarker(
|
|
556
|
+
cwd,
|
|
557
|
+
getBaseStateDir(cwd),
|
|
558
|
+
correlationSessionId,
|
|
559
|
+
parentThreadId,
|
|
560
|
+
);
|
|
422
561
|
}
|
|
423
562
|
await appendToLog(cwd, {
|
|
424
563
|
event: "subagent_session_start",
|
|
@@ -524,6 +663,32 @@ async function isNativeSubagentHook(
|
|
|
524
663
|
));
|
|
525
664
|
}
|
|
526
665
|
|
|
666
|
+
async function readNativePromptThreadFacts(
|
|
667
|
+
cwd: string,
|
|
668
|
+
nativeSessionId: string,
|
|
669
|
+
threadId: string,
|
|
670
|
+
currentSessionState?: SessionState | null,
|
|
671
|
+
): Promise<PromptThreadFacts | undefined> {
|
|
672
|
+
const candidateIds = [nativeSessionId, threadId].map((value) => value.trim()).filter(Boolean);
|
|
673
|
+
if (candidateIds.length === 0) return undefined;
|
|
674
|
+
const currentNativeIds = new Set([
|
|
675
|
+
safeString(currentSessionState?.native_session_id).trim(),
|
|
676
|
+
safeString(currentSessionState?.codex_session_id).trim(),
|
|
677
|
+
safeString(currentSessionState?.owner_codex_session_id).trim(),
|
|
678
|
+
].filter(Boolean));
|
|
679
|
+
const currentTargetAnchored = Boolean(nativeSessionId && currentNativeIds.has(nativeSessionId));
|
|
680
|
+
const trackingState = await readSubagentTrackingState(cwd).catch(() => null);
|
|
681
|
+
if (!trackingState) return currentTargetAnchored ? { currentTargetAnchored: true, rootOrDrift: true } : undefined;
|
|
682
|
+
const roots = Object.values(trackingState.sessions).flatMap((session) => (
|
|
683
|
+
candidateIds.some((candidate) => isTrustedSubagentThread(session, candidate))
|
|
684
|
+
? [session.session_id]
|
|
685
|
+
: []
|
|
686
|
+
));
|
|
687
|
+
return roots.length > 0 || currentTargetAnchored
|
|
688
|
+
? { trackerRootOwnerSessionIds: roots, currentTargetAnchored, rootOrDrift: currentTargetAnchored }
|
|
689
|
+
: undefined;
|
|
690
|
+
}
|
|
691
|
+
|
|
527
692
|
function shouldSuppressSubagentLifecycleHookDispatch(): boolean {
|
|
528
693
|
const config = getNotificationConfig();
|
|
529
694
|
if (config?.includeChildAgents === true) return false;
|
|
@@ -691,8 +856,8 @@ function readPromptText(payload: CodexHookPayload): string {
|
|
|
691
856
|
payload.userPrompt,
|
|
692
857
|
];
|
|
693
858
|
for (const candidate of candidates) {
|
|
694
|
-
const value = safeString(candidate)
|
|
695
|
-
if (value) return value;
|
|
859
|
+
const value = safeString(candidate);
|
|
860
|
+
if (value.trim()) return value;
|
|
696
861
|
}
|
|
697
862
|
return "";
|
|
698
863
|
}
|
|
@@ -2176,25 +2341,29 @@ function buildTeamHelpInstruction(cwd: string, payload?: CodexHookPayload): stri
|
|
|
2176
2341
|
}).teamHelpInstruction;
|
|
2177
2342
|
}
|
|
2178
2343
|
|
|
2344
|
+
function isNativeOutsideTmuxUserPrompt(cwd: string, payload: CodexHookPayload, sessionId?: string): boolean {
|
|
2345
|
+
const environment = resolveExecutionEnvironment(cwd, {
|
|
2346
|
+
hookEventName: "UserPromptSubmit",
|
|
2347
|
+
payload,
|
|
2348
|
+
canonicalSessionId: sessionId ?? "",
|
|
2349
|
+
nativeSessionId: safeString(payload.session_id ?? payload.sessionId).trim(),
|
|
2350
|
+
});
|
|
2351
|
+
return environment.launcher === "native" && environment.transport === "outside-tmux";
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2179
2354
|
function buildNativeOutsideTmuxTeamPromptBlockState(
|
|
2180
|
-
|
|
2355
|
+
classification: KeywordInputClassification,
|
|
2181
2356
|
cwd: string,
|
|
2182
2357
|
payload: CodexHookPayload,
|
|
2183
2358
|
sessionId?: string,
|
|
2184
2359
|
threadId?: string,
|
|
2185
2360
|
turnId?: string,
|
|
2186
2361
|
): SkillActiveState | null {
|
|
2187
|
-
|
|
2188
|
-
const match =
|
|
2362
|
+
const teamMode = readTeamModeConfig(cwd);
|
|
2363
|
+
const match = classification.matches.filter((entry) => teamMode.enabled || entry.skill !== "team")[0] ?? null;
|
|
2189
2364
|
if (match?.skill !== "team") return null;
|
|
2190
2365
|
|
|
2191
|
-
|
|
2192
|
-
hookEventName: "UserPromptSubmit",
|
|
2193
|
-
payload,
|
|
2194
|
-
canonicalSessionId: sessionId ?? "",
|
|
2195
|
-
nativeSessionId: safeString(payload.session_id ?? payload.sessionId).trim(),
|
|
2196
|
-
});
|
|
2197
|
-
if (!(environment.launcher === "native" && environment.transport === "outside-tmux")) return null;
|
|
2366
|
+
if (!isNativeOutsideTmuxUserPrompt(cwd, payload, sessionId)) return null;
|
|
2198
2367
|
|
|
2199
2368
|
const nowIso = new Date().toISOString();
|
|
2200
2369
|
return {
|
|
@@ -2230,13 +2399,19 @@ function buildAutopilotPromptActivationNote(
|
|
|
2230
2399
|
const nativeSubagentSupport = resolveNativeSubagentSupportStatus({
|
|
2231
2400
|
payload: options.payload,
|
|
2232
2401
|
persistedSupportBlocker: readJsonSyncIfExists(nativeSubagentSupportBlockerPath(stateDir)),
|
|
2402
|
+
persistedRoleRoutingMarker: readRoleRoutingMarker(stateDir, {
|
|
2403
|
+
cwd: options.cwd,
|
|
2404
|
+
sessionId: options.sessionId ?? "",
|
|
2405
|
+
}),
|
|
2233
2406
|
persistedCapacityBlocker: readJsonSyncIfExists(nativeSubagentCapacityBlockerPath(stateDir)),
|
|
2234
2407
|
cwd: options.cwd,
|
|
2235
2408
|
sessionId: options.sessionId,
|
|
2236
2409
|
});
|
|
2237
2410
|
const conductorGuidance = nativeSubagentSupport.status === "unsupported"
|
|
2238
2411
|
? buildUnsupportedNativeSubagentGuidance(nativeSubagentSupport)
|
|
2239
|
-
:
|
|
2412
|
+
: nativeSubagentSupport.status === "role_routing_unavailable"
|
|
2413
|
+
? buildRoleRoutingUnavailableGuidance(nativeSubagentSupport)
|
|
2414
|
+
: `${LEADER_CONDUCTOR_BLOCK} ${LEADER_CONDUCTOR_REUSE_AND_LEDGER_GUIDANCE}`;
|
|
2240
2415
|
return [
|
|
2241
2416
|
`Autopilot protocol: the durable default chain is $deep-interview -> $ralplan -> $ultragoal${teamHandoff} -> $code-review -> $ultraqa (deep-interview -> ralplan -> ultragoal -> code-review -> ultraqa).`,
|
|
2242
2417
|
"Start/resume at current_phase=deep-interview unless the task is clear and bounded; if deep-interview is intentionally skipped, persist and state an explicit deep_interview_gate.skip_reason before moving to ralplan.",
|
|
@@ -2259,30 +2434,40 @@ function formatExecutionHandoffList(cwd: string): string {
|
|
|
2259
2434
|
}
|
|
2260
2435
|
|
|
2261
2436
|
function buildAdditionalContextMessage(
|
|
2262
|
-
|
|
2437
|
+
classification: KeywordInputClassification,
|
|
2263
2438
|
skillState?: SkillActiveState | null,
|
|
2264
2439
|
cwd: string = process.cwd(),
|
|
2265
2440
|
payload?: CodexHookPayload,
|
|
2266
2441
|
): string | null {
|
|
2442
|
+
const prompt = classification.originalText;
|
|
2267
2443
|
if (!prompt) return null;
|
|
2268
2444
|
const promptPriorityMessage = buildPromptPriorityMessage(prompt);
|
|
2269
2445
|
if (payload && isTypedAgentRolePayload(payload)) {
|
|
2270
2446
|
return promptPriorityMessage;
|
|
2271
2447
|
}
|
|
2272
2448
|
const teamMode = readTeamModeConfig(cwd);
|
|
2273
|
-
const matches =
|
|
2449
|
+
const matches = classification.matches.filter((entry) => teamMode.enabled || entry.skill !== "team");
|
|
2274
2450
|
const match = matches[0] ?? null;
|
|
2275
2451
|
if (!match) {
|
|
2452
|
+
const markedQuestionAnswer = classification.reservedInput === "omx-question-answered";
|
|
2276
2453
|
const continuedSkill = safeString(skillState?.skill).trim();
|
|
2277
|
-
|
|
2454
|
+
const eligibleMarkedContinuation = markedQuestionAnswer
|
|
2455
|
+
&& skillState?.active === true
|
|
2456
|
+
&& (continuedSkill === "autopilot" || continuedSkill === "deep-interview");
|
|
2457
|
+
const eligibleOrdinaryContinuation = classification.reservedInput === null
|
|
2458
|
+
&& !classification.hasExplicitLikeInvocation
|
|
2459
|
+
&& skillState?.active === true
|
|
2460
|
+
&& Boolean(continuedSkill);
|
|
2461
|
+
if (!eligibleMarkedContinuation && !eligibleOrdinaryContinuation) return promptPriorityMessage;
|
|
2278
2462
|
const deepInterviewPromptActivationNote = skillState?.initialized_mode === "deep-interview"
|
|
2279
2463
|
? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
|
|
2280
2464
|
: null;
|
|
2281
2465
|
const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
|
|
2282
|
-
const markedQuestionAnswer = /^\s*\[omx question answered\]/i.test(prompt);
|
|
2283
2466
|
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer, cwd, payload, sessionId: safeString(skillState?.session_id).trim() });
|
|
2284
2467
|
return [
|
|
2285
|
-
|
|
2468
|
+
markedQuestionAnswer
|
|
2469
|
+
? `OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}"; workflow-like tokens inside the marked omx question answer are treated as answer text, not a new workflow activation.`
|
|
2470
|
+
: `OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}".`,
|
|
2286
2471
|
promptPriorityMessage,
|
|
2287
2472
|
skillState?.initialized_mode && skillState.initialized_state_path
|
|
2288
2473
|
? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
|
|
@@ -2296,29 +2481,6 @@ function buildAdditionalContextMessage(
|
|
|
2296
2481
|
const detectedKeywordMessage = matches.length > 1
|
|
2297
2482
|
? `OMX native UserPromptSubmit detected workflow keywords ${matches.map((entry) => `"${entry.keyword}" -> ${entry.skill}`).join(", ")}.`
|
|
2298
2483
|
: `OMX native UserPromptSubmit detected workflow keyword "${match.keyword}" -> ${match.skill}.`;
|
|
2299
|
-
const continuedSkill = safeString(skillState?.skill).trim();
|
|
2300
|
-
if (
|
|
2301
|
-
continuedSkill
|
|
2302
|
-
&& continuedSkill !== match.skill
|
|
2303
|
-
&& /^\s*\[omx question answered\]/i.test(prompt)
|
|
2304
|
-
) {
|
|
2305
|
-
const deepInterviewPromptActivationNote = skillState?.initialized_mode === "deep-interview"
|
|
2306
|
-
? buildDeepInterviewQuestionBridgeInstruction(cwd, payload)
|
|
2307
|
-
: null;
|
|
2308
|
-
const deepInterviewConfigPromptActivationNote = buildDeepInterviewConfigInstruction(cwd, skillState);
|
|
2309
|
-
const autopilotPromptActivationNote = buildAutopilotPromptActivationNote(skillState, { markedQuestionAnswer: true, cwd, payload, sessionId: safeString(skillState?.session_id).trim() });
|
|
2310
|
-
return [
|
|
2311
|
-
`OMX native UserPromptSubmit continued active workflow skill "${continuedSkill}"; workflow-like tokens inside the marked omx question answer are treated as answer text, not a new workflow activation.`,
|
|
2312
|
-
promptPriorityMessage,
|
|
2313
|
-
skillState?.initialized_mode && skillState.initialized_state_path
|
|
2314
|
-
? buildSkillStateCliInstruction(skillState.initialized_mode, skillState.initialized_state_path)
|
|
2315
|
-
: null,
|
|
2316
|
-
deepInterviewPromptActivationNote,
|
|
2317
|
-
deepInterviewConfigPromptActivationNote,
|
|
2318
|
-
autopilotPromptActivationNote,
|
|
2319
|
-
"Follow AGENTS.md routing and preserve workflow transition and planning-safety rules.",
|
|
2320
|
-
].filter(Boolean).join(" ");
|
|
2321
|
-
}
|
|
2322
2484
|
const activeSkills = Array.isArray(skillState?.active_skills)
|
|
2323
2485
|
? skillState.active_skills.map((entry) => entry.skill)
|
|
2324
2486
|
: [];
|
|
@@ -2740,8 +2902,6 @@ async function buildModeBasedStopOutput(
|
|
|
2740
2902
|
reason: `OMX ${mode} is still active (phase: ${phase}; ${diagnostic}); continue the task and gather fresh verification evidence before stopping.`,
|
|
2741
2903
|
stopReason: `${mode}_${phase}`,
|
|
2742
2904
|
systemMessage,
|
|
2743
|
-
statePath,
|
|
2744
|
-
canonicalDisagreement,
|
|
2745
2905
|
};
|
|
2746
2906
|
}
|
|
2747
2907
|
|
|
@@ -3164,33 +3324,68 @@ function readPayloadAgentRole(payload: CodexHookPayload): string {
|
|
|
3164
3324
|
).trim().toLowerCase();
|
|
3165
3325
|
}
|
|
3166
3326
|
|
|
3167
|
-
function
|
|
3168
|
-
const
|
|
3169
|
-
if (
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
}
|
|
3178
|
-
} catch {
|
|
3179
|
-
// Ignore missing or unreadable agent directories; built-in definitions remain authoritative.
|
|
3180
|
-
}
|
|
3181
|
-
}
|
|
3182
|
-
|
|
3183
|
-
installedTypedAgentRoleNamesCacheKey = cacheKey;
|
|
3184
|
-
installedTypedAgentRoleNamesCache = installedRoleNames;
|
|
3185
|
-
return installedTypedAgentRoleNamesCache;
|
|
3327
|
+
function readRequestedSpawnRole(payload: CodexHookPayload): string {
|
|
3328
|
+
const toolName = safeString(payload.tool_name).trim();
|
|
3329
|
+
if (!isNativeSubagentSpawnToolName(toolName)) return "";
|
|
3330
|
+
const toolInput = safeObject(payload.tool_input);
|
|
3331
|
+
return safeString(
|
|
3332
|
+
toolInput.agent_role
|
|
3333
|
+
?? toolInput.agentRole
|
|
3334
|
+
?? toolInput.agent_type
|
|
3335
|
+
?? toolInput.agentType,
|
|
3336
|
+
).trim().toLowerCase();
|
|
3186
3337
|
}
|
|
3187
3338
|
|
|
3188
3339
|
function isTypedAgentRolePayload(payload: CodexHookPayload): boolean {
|
|
3189
3340
|
const agentRole = readPayloadAgentRole(payload);
|
|
3190
|
-
return agentRole !== "" && (
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3341
|
+
return agentRole !== "" && resolveInstalledRoleName(agentRole) !== null;
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
// #3181: detect a runtime-set native subagent PreToolUse. The runtime sets
|
|
3345
|
+
// source.subagent.thread_spawn (not agent-controlled tool arguments), so the mere
|
|
3346
|
+
// STRUCTURAL PRESENCE of that carrier — even if its parent id is blank, malformed, or
|
|
3347
|
+
// null — marks a child/subagent turn that must never be attested as the session leader.
|
|
3348
|
+
// This is a negative security decision, so it fails closed on any present carrier rather
|
|
3349
|
+
// than requiring a well-formed parent id.
|
|
3350
|
+
function hasSubagentThreadSpawnProvenance(payload: CodexHookPayload): boolean {
|
|
3351
|
+
const source = payload.source;
|
|
3352
|
+
if (!source || typeof source !== "object") return false;
|
|
3353
|
+
const subagent = (source as Record<string, unknown>).subagent;
|
|
3354
|
+
if (!subagent || typeof subagent !== "object") return false;
|
|
3355
|
+
return Object.prototype.hasOwnProperty.call(subagent, "thread_spawn");
|
|
3356
|
+
}
|
|
3357
|
+
|
|
3358
|
+
// #3181: positive counter-evidence guard. A thread durably tracked as a subagent in ANY
|
|
3359
|
+
// session must never be attested as a leader, even if the current payload lacks typed-role
|
|
3360
|
+
// or thread_spawn provenance. This closes the source-less/untyped child escalation where a
|
|
3361
|
+
// child recorded at its SessionStart later self-promotes via a source-less PreToolUse.
|
|
3362
|
+
async function isThreadTrackedAsSubagent(cwd: string, threadId: string): Promise<boolean> {
|
|
3363
|
+
const id = threadId.trim();
|
|
3364
|
+
if (!id) return false;
|
|
3365
|
+
// Strict read: an unreadable/corrupt tracker fails closed (treated as "is a subagent")
|
|
3366
|
+
// so corrupt evidence cannot let a source-less child reconcile/attest as leader.
|
|
3367
|
+
const read = await readSubagentTrackingStateStrict(cwd);
|
|
3368
|
+
if (!read.ok) return true;
|
|
3369
|
+
for (const session of Object.values(read.state.sessions)) {
|
|
3370
|
+
if (session.threads?.[id]?.kind === "subagent") return true;
|
|
3371
|
+
}
|
|
3372
|
+
return false;
|
|
3373
|
+
}
|
|
3374
|
+
|
|
3375
|
+
function buildNativeUnknownRolePreToolUseOutput(
|
|
3376
|
+
payload: CodexHookPayload,
|
|
3377
|
+
): Record<string, unknown> | null {
|
|
3378
|
+
const requestedRole = readRequestedSpawnRole(payload);
|
|
3379
|
+
if (!requestedRole || resolveInstalledRoleName(requestedRole) !== null) return null;
|
|
3380
|
+
return {
|
|
3381
|
+
decision: "block",
|
|
3382
|
+
reason: "Native typed-subagent dispatch denied: supplied agent_type/agent_role is unknown or not installed.",
|
|
3383
|
+
hookSpecificOutput: {
|
|
3384
|
+
hookEventName: "PreToolUse",
|
|
3385
|
+
additionalContext:
|
|
3386
|
+
"Use an installed OMX role for native agent_type/agent_role dispatch. On a role-routing-unavailable surface, record a validated OMX adapted role intent before spawning without fabricating agent_type.",
|
|
3387
|
+
},
|
|
3388
|
+
};
|
|
3194
3389
|
}
|
|
3195
3390
|
|
|
3196
3391
|
function readPayloadTurnId(payload: CodexHookPayload): string {
|
|
@@ -3291,6 +3486,27 @@ async function recordNativeSubagentSupportBlocker(
|
|
|
3291
3486
|
}, null, 2));
|
|
3292
3487
|
}
|
|
3293
3488
|
|
|
3489
|
+
|
|
3490
|
+
function refreshNativeSubagentRoleRoutingMarker(
|
|
3491
|
+
cwd: string,
|
|
3492
|
+
stateDir: string,
|
|
3493
|
+
sessionId: string,
|
|
3494
|
+
parentThreadId?: string,
|
|
3495
|
+
): void {
|
|
3496
|
+
const marker = readRoleRoutingMarker(stateDir, {
|
|
3497
|
+
cwd,
|
|
3498
|
+
sessionId,
|
|
3499
|
+
...(parentThreadId ? { parentThreadId } : {}),
|
|
3500
|
+
});
|
|
3501
|
+
if (!marker) return;
|
|
3502
|
+
const nowMs = Date.now();
|
|
3503
|
+
writeRoleRoutingMarker(stateDir, {
|
|
3504
|
+
...marker,
|
|
3505
|
+
observed_at: new Date(nowMs).toISOString(),
|
|
3506
|
+
expires_at: new Date(nowMs + NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_TTL_MS).toISOString(),
|
|
3507
|
+
});
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3294
3510
|
function summarizeCapacityFailure(text: string): string {
|
|
3295
3511
|
const normalized = text.replace(/\s+/g, " ").trim();
|
|
3296
3512
|
if (!normalized) return "agent thread limit reached";
|
|
@@ -3390,12 +3606,14 @@ async function resolveInternalSessionIdForPayload(
|
|
|
3390
3606
|
cwd: string,
|
|
3391
3607
|
payloadSessionId: string,
|
|
3392
3608
|
stateDir?: string,
|
|
3609
|
+
pointerSessionState?: SessionState | null,
|
|
3610
|
+
allowUnboundPayloadFallback = true,
|
|
3393
3611
|
): Promise<string> {
|
|
3394
|
-
const currentSession = stateDir
|
|
3612
|
+
const currentSession = pointerSessionState ?? (stateDir
|
|
3395
3613
|
? await readUsableSessionStateFromStateDir(cwd, stateDir)
|
|
3396
|
-
: await readUsableSessionState(cwd);
|
|
3614
|
+
: await readUsableSessionState(cwd));
|
|
3397
3615
|
const canonicalSessionId = safeString(currentSession?.session_id).trim();
|
|
3398
|
-
if (!canonicalSessionId) return payloadSessionId;
|
|
3616
|
+
if (!canonicalSessionId) return allowUnboundPayloadFallback ? payloadSessionId : "";
|
|
3399
3617
|
|
|
3400
3618
|
const nativeSessionId = safeString(currentSession?.native_session_id).trim();
|
|
3401
3619
|
const ownerOmxSessionId = safeString(currentSession?.owner_omx_session_id).trim();
|
|
@@ -3403,7 +3621,7 @@ async function resolveInternalSessionIdForPayload(
|
|
|
3403
3621
|
if (payloadSessionId === canonicalSessionId) return canonicalSessionId;
|
|
3404
3622
|
if (nativeSessionId && payloadSessionId === nativeSessionId) return canonicalSessionId;
|
|
3405
3623
|
if (ownerOmxSessionId && payloadSessionId === ownerOmxSessionId) return canonicalSessionId;
|
|
3406
|
-
return payloadSessionId;
|
|
3624
|
+
return allowUnboundPayloadFallback ? payloadSessionId : "";
|
|
3407
3625
|
}
|
|
3408
3626
|
|
|
3409
3627
|
async function readRootSessionStateFromStateDir(stateDir: string): Promise<SessionState | null> {
|
|
@@ -3524,14 +3742,6 @@ const RALPLAN_EXECUTION_HANDOFF_SKILLS = new Set([
|
|
|
3524
3742
|
"ultraqa",
|
|
3525
3743
|
]);
|
|
3526
3744
|
|
|
3527
|
-
function isActiveDeepInterviewPhase(state: Record<string, unknown> | null): boolean {
|
|
3528
|
-
if (!state || state.active !== true) return false;
|
|
3529
|
-
const mode = safeString(state.mode).trim();
|
|
3530
|
-
if (mode && mode !== "deep-interview") return false;
|
|
3531
|
-
const phase = safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase();
|
|
3532
|
-
if (phase && (TERMINAL_MODE_PHASES.has(phase) || phase === "completing")) return false;
|
|
3533
|
-
return true;
|
|
3534
|
-
}
|
|
3535
3745
|
|
|
3536
3746
|
function isInactiveCompletedDeepInterviewPhase(state: Record<string, unknown> | null): boolean {
|
|
3537
3747
|
if (!state || state.active !== false) return false;
|
|
@@ -3653,8 +3863,14 @@ function isAllowedDeepInterviewArtifactPath(cwd: string, rawPath: string): boole
|
|
|
3653
3863
|
return isAllowedPlanningArtifactPath(cwd, rawPath, DEEP_INTERVIEW_ALLOWED_WRITE_PREFIXES);
|
|
3654
3864
|
}
|
|
3655
3865
|
|
|
3866
|
+
function isAllowedRalplanDraftPath(cwd: string, rawPath: string): boolean {
|
|
3867
|
+
const relativePath = normalizePlanningArtifactRelativePath(cwd, rawPath);
|
|
3868
|
+
return relativePath !== null && /^\.omx\/drafts\/[^/]+\.md$/.test(relativePath);
|
|
3869
|
+
}
|
|
3870
|
+
|
|
3656
3871
|
function isAllowedRalplanArtifactPath(cwd: string, rawPath: string): boolean {
|
|
3657
|
-
return
|
|
3872
|
+
return isAllowedRalplanDraftPath(cwd, rawPath)
|
|
3873
|
+
|| isAllowedPlanningArtifactPath(cwd, rawPath, RALPLAN_ALLOWED_WRITE_PREFIXES);
|
|
3658
3874
|
}
|
|
3659
3875
|
|
|
3660
3876
|
interface RalplanBeadsCommandClassification {
|
|
@@ -3874,9 +4090,72 @@ function resolveCommandRedirectTarget(target: string, assignments: Map<string, s
|
|
|
3874
4090
|
return resolved !== undefined ? resolved : target;
|
|
3875
4091
|
}
|
|
3876
4092
|
|
|
4093
|
+
// Masks redirect metacharacters (`<`/`>`) that appear INSIDE shell quotes so a
|
|
4094
|
+
// quoted regex/source value (e.g. `gh issue create --body '...>{1,2}...'` or
|
|
4095
|
+
// `omx state write --input '{"reason":"a>b"}'`) is not misread as a redirect
|
|
4096
|
+
// write target. Escaped quotes at the top level (`\'`, `\"`) are literal
|
|
4097
|
+
// characters and must NOT open a span, and `$'...'` ANSI-C quoting processes
|
|
4098
|
+
// backslash escapes (so `\'` does not close it) — otherwise a genuine `>`
|
|
4099
|
+
// redirect could be masked behind a false span and its write missed. Only
|
|
4100
|
+
// characters inside a real quoted span are masked; unquoted redirect operators
|
|
4101
|
+
// survive intact. Unterminated/ambiguous quoting fails closed: the original
|
|
4102
|
+
// command is returned unmasked so genuine redirects stay visible to the scan.
|
|
4103
|
+
function maskQuotedRedirectMetacharsForCommandScan(command: string): string {
|
|
4104
|
+
let masked = "";
|
|
4105
|
+
// null = unquoted; "'" = single quotes (no escapes); '"' = double quotes
|
|
4106
|
+
// (backslash escapes); "$'" = ANSI-C $'...' (backslash escapes, incl. \').
|
|
4107
|
+
let quote: "'" | "\"" | "$'" | null = null;
|
|
4108
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
4109
|
+
const char = command[index] ?? "";
|
|
4110
|
+
if (quote === null) {
|
|
4111
|
+
// A backslash escapes the next character at the top level, so an escaped
|
|
4112
|
+
// quote is a literal char and must not open a quoted span.
|
|
4113
|
+
if (char === "\\") {
|
|
4114
|
+
masked += char;
|
|
4115
|
+
const next = command[index + 1];
|
|
4116
|
+
if (next !== undefined) {
|
|
4117
|
+
masked += next;
|
|
4118
|
+
index += 1;
|
|
4119
|
+
}
|
|
4120
|
+
continue;
|
|
4121
|
+
}
|
|
4122
|
+
if (char === "$" && command[index + 1] === "'") {
|
|
4123
|
+
quote = "$'";
|
|
4124
|
+
masked += "$'";
|
|
4125
|
+
index += 1;
|
|
4126
|
+
continue;
|
|
4127
|
+
}
|
|
4128
|
+
if (char === "'" || char === "\"") quote = char;
|
|
4129
|
+
masked += char;
|
|
4130
|
+
continue;
|
|
4131
|
+
}
|
|
4132
|
+
if ((quote === "\"" || quote === "$'") && char === "\\") {
|
|
4133
|
+
// Escapes inside double/ANSI-C spans keep the backslash and its escaped
|
|
4134
|
+
// char (masking a redirect metachar so quoted data cannot be a false
|
|
4135
|
+
// target); the escaped char never closes the span.
|
|
4136
|
+
masked += char;
|
|
4137
|
+
const next = command[index + 1];
|
|
4138
|
+
if (next !== undefined) {
|
|
4139
|
+
masked += next === "<" || next === ">" ? "_" : next;
|
|
4140
|
+
index += 1;
|
|
4141
|
+
}
|
|
4142
|
+
continue;
|
|
4143
|
+
}
|
|
4144
|
+
const closesSpan = quote === "$'" ? char === "'" : char === quote;
|
|
4145
|
+
if (closesSpan) {
|
|
4146
|
+
quote = null;
|
|
4147
|
+
masked += char;
|
|
4148
|
+
continue;
|
|
4149
|
+
}
|
|
4150
|
+
masked += char === "<" || char === ">" ? "_" : char;
|
|
4151
|
+
}
|
|
4152
|
+
if (quote !== null) return command;
|
|
4153
|
+
return masked;
|
|
4154
|
+
}
|
|
4155
|
+
|
|
3877
4156
|
function extractDeepInterviewCommandRedirectTargets(command: string): string[] {
|
|
3878
4157
|
const targets: string[] = [];
|
|
3879
|
-
const commandOutsideHeredocBodies = stripHeredocBodiesForCommandScan(command);
|
|
4158
|
+
const commandOutsideHeredocBodies = maskQuotedRedirectMetacharsForCommandScan(stripHeredocBodiesForCommandScan(command));
|
|
3880
4159
|
for (const match of commandOutsideHeredocBodies.matchAll(/(?:^|[^>])>{1,2}\s*(["']?)([^\s&|;<>]+)\1/g)) {
|
|
3881
4160
|
const candidate = safeString(match[2]).trim();
|
|
3882
4161
|
if (candidate && !isNullDeviceRedirectTarget(candidate)) targets.push(candidate);
|
|
@@ -3974,7 +4253,7 @@ function findGitSubcommandIndex(words: string[], startIndex: number): number | n
|
|
|
3974
4253
|
}
|
|
3975
4254
|
|
|
3976
4255
|
|
|
3977
|
-
function commandHasDeepInterviewWriteIntent(command: string): boolean {
|
|
4256
|
+
function commandHasDeepInterviewWriteIntent(command: string, depth = 0): boolean {
|
|
3978
4257
|
return commandInvokesApplyPatch(command)
|
|
3979
4258
|
|| extractDeepInterviewCommandRedirectTargets(command).length > 0
|
|
3980
4259
|
|| /\btee\s+(?:-a\s+)?[^\s&|;]+/.test(command)
|
|
@@ -3985,7 +4264,19 @@ function commandHasDeepInterviewWriteIntent(command: string): boolean {
|
|
|
3985
4264
|
|| extractConductorBashMutations(command).length > 0
|
|
3986
4265
|
|| extractConductorInterpreterWrites(command).length > 0
|
|
3987
4266
|
|| commandHasDestructiveGitSubcommand(command)
|
|
3988
|
-
|| commandHasPackageInstallIntent(command)
|
|
4267
|
+
|| commandHasPackageInstallIntent(command)
|
|
4268
|
+
// Recurse into wrapped shells (`bash -lc "cat > f"`, `eval`, `env`) and
|
|
4269
|
+
// command substitutions so a real redirect INSIDE a nested command string
|
|
4270
|
+
// is still classified as write intent. This is required because quoted
|
|
4271
|
+
// redirect metacharacters are now masked at the outer scan (#3119): the
|
|
4272
|
+
// mask removes false positives from quoted DATA values while nested-command
|
|
4273
|
+
// recursion re-detects genuine redirects, preserving fail-closed blocking.
|
|
4274
|
+
// The depth guard mirrors evaluateConductorBashWrite's nesting bound;
|
|
4275
|
+
// extractors return strict substrings, so recursion always terminates.
|
|
4276
|
+
|| (depth < CONDUCTOR_BASH_MAX_NESTING_DEPTH && (
|
|
4277
|
+
extractNestedShellCommandStringsForStateScan(command).some((nested) => commandHasDeepInterviewWriteIntent(nested, depth + 1))
|
|
4278
|
+
|| extractNestedCommandSubstitutionStringsForStateScan(command).some((nested) => commandHasDeepInterviewWriteIntent(nested, depth + 1))
|
|
4279
|
+
));
|
|
3989
4280
|
}
|
|
3990
4281
|
|
|
3991
4282
|
function extractDeepInterviewCommandWriteTargets(command: string): string[] {
|
|
@@ -6738,6 +7029,128 @@ function isCompleteRalplanTerminalWritePayload(
|
|
|
6738
7029
|
return true;
|
|
6739
7030
|
}
|
|
6740
7031
|
|
|
7032
|
+
function hasUnquotedShellControlOrRedirection(command: string): boolean {
|
|
7033
|
+
let quote: "'" | '"' | null = null;
|
|
7034
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
7035
|
+
const char = command[index] ?? "";
|
|
7036
|
+
if (char === "\\" && quote !== "'") {
|
|
7037
|
+
index += 1;
|
|
7038
|
+
continue;
|
|
7039
|
+
}
|
|
7040
|
+
if (char === "'" || char === '"') {
|
|
7041
|
+
quote = quote === char ? null : quote ?? char;
|
|
7042
|
+
continue;
|
|
7043
|
+
}
|
|
7044
|
+
if (!quote && ";&|()<>\n\r".includes(char)) return true;
|
|
7045
|
+
}
|
|
7046
|
+
return false;
|
|
7047
|
+
}
|
|
7048
|
+
|
|
7049
|
+
function suppliedSessionAliasesMatch(payload: Record<string, unknown>, sessionId: string): boolean {
|
|
7050
|
+
return [
|
|
7051
|
+
payload.session_id,
|
|
7052
|
+
payload.owner_omx_session_id,
|
|
7053
|
+
payload.codex_session_id,
|
|
7054
|
+
payload.owner_codex_session_id,
|
|
7055
|
+
].filter((value) => value !== undefined)
|
|
7056
|
+
.every((value) => safeString(value).trim() === sessionId);
|
|
7057
|
+
}
|
|
7058
|
+
|
|
7059
|
+
function hasOnlyFinishedExplicitOutcomes(payload: Record<string, unknown>): boolean {
|
|
7060
|
+
const values = [
|
|
7061
|
+
payload.lifecycle_outcome,
|
|
7062
|
+
payload.lifecycleOutcome,
|
|
7063
|
+
payload.terminal_outcome,
|
|
7064
|
+
payload.terminalOutcome,
|
|
7065
|
+
payload.run_outcome,
|
|
7066
|
+
payload.runOutcome,
|
|
7067
|
+
].filter((value) => value !== undefined);
|
|
7068
|
+
return values.every((value) => inferTerminalLifecycleOutcome(
|
|
7069
|
+
{ lifecycle_outcome: value },
|
|
7070
|
+
{ includeQuestionEnforcement: false },
|
|
7071
|
+
) === "finished");
|
|
7072
|
+
}
|
|
7073
|
+
|
|
7074
|
+
function isCompleteDeepInterviewTerminalWritePayload(
|
|
7075
|
+
payload: Record<string, unknown>,
|
|
7076
|
+
activeState: Record<string, unknown>,
|
|
7077
|
+
sessionId: string,
|
|
7078
|
+
): boolean {
|
|
7079
|
+
if (!sessionId) return false;
|
|
7080
|
+
const activeMode = safeString(activeState.mode).trim().toLowerCase();
|
|
7081
|
+
if (activeMode && activeMode !== "deep-interview") return false;
|
|
7082
|
+
const activeSessionId = safeString(
|
|
7083
|
+
activeState.session_id
|
|
7084
|
+
?? activeState.owner_omx_session_id
|
|
7085
|
+
?? activeState.codex_session_id
|
|
7086
|
+
?? activeState.owner_codex_session_id,
|
|
7087
|
+
).trim();
|
|
7088
|
+
if (activeSessionId && activeSessionId !== sessionId) return false;
|
|
7089
|
+
|
|
7090
|
+
const mode = safeString(payload.mode).trim().toLowerCase();
|
|
7091
|
+
const phase = safeString(payload.current_phase ?? payload.currentPhase).trim().toLowerCase();
|
|
7092
|
+
const payloadSessionId = safeString(payload.session_id).trim();
|
|
7093
|
+
if (!hasOnlyFinishedExplicitOutcomes(payload)) return false;
|
|
7094
|
+
return mode === "deep-interview"
|
|
7095
|
+
&& payload.active === false
|
|
7096
|
+
&& phase === "complete"
|
|
7097
|
+
&& payloadSessionId === sessionId
|
|
7098
|
+
&& inferTerminalLifecycleOutcome(payload, { includeQuestionEnforcement: false }) === "finished";
|
|
7099
|
+
}
|
|
7100
|
+
|
|
7101
|
+
function isAllowedDeepInterviewTerminalStateWriteCommand(
|
|
7102
|
+
cwd: string,
|
|
7103
|
+
command: string,
|
|
7104
|
+
activeState: Record<string, unknown>,
|
|
7105
|
+
sessionId: string,
|
|
7106
|
+
): boolean {
|
|
7107
|
+
const rawWords = tokenizeShellWords(normalizeShellLineContinuations(command).trim());
|
|
7108
|
+
if (rawWords[0] !== "omx") return false;
|
|
7109
|
+
const canonicalCommand = canonicalizeOmxStateTransportCommand(command);
|
|
7110
|
+
if (hasUnquotedShellControlOrRedirection(command)) return false;
|
|
7111
|
+
if (hasUnsafeUnquotedHeredocExpansion(canonicalCommand)) return false;
|
|
7112
|
+
if (hasUnquotedShellSubstitution(canonicalCommand)) return false;
|
|
7113
|
+
if (splitStateScanSegments(canonicalCommand).length !== 1) return false;
|
|
7114
|
+
if (sourcesFileWrittenEarlierInSameCommand(cwd, canonicalCommand)) return false;
|
|
7115
|
+
if (findUnquotedOmxStateCommandIndexes(canonicalCommand, "clear").length > 0) return false;
|
|
7116
|
+
if (hasDynamicNestedShellExecution(canonicalCommand)) return false;
|
|
7117
|
+
if (extractDeepInterviewCommandWriteTargets(command).length > 0) return false;
|
|
7118
|
+
|
|
7119
|
+
const operations = collectOmxStateCommandOperations(command, "write");
|
|
7120
|
+
if (operations.length !== 1) return false;
|
|
7121
|
+
const operation = operations[0];
|
|
7122
|
+
if (!operation || operation.nested || operation.commandPrefix.trim() || hasPriorExecutableCommand(operation.prefix)) return false;
|
|
7123
|
+
const inlineInputCount = operation.args.filter((arg) => arg === "--input" || arg.startsWith("--input=")).length;
|
|
7124
|
+
if (inlineInputCount !== 1 || operation.args.some((arg) => arg === "--input-file" || arg.startsWith("--input-file="))) return false;
|
|
7125
|
+
const inlineInput = readStateWriteFlagValue(operation.args, "--input");
|
|
7126
|
+
let rawPayload: Record<string, unknown> | null = null;
|
|
7127
|
+
try {
|
|
7128
|
+
rawPayload = inlineInput ? safeObject(JSON.parse(inlineInput)) : null;
|
|
7129
|
+
} catch {
|
|
7130
|
+
rawPayload = null;
|
|
7131
|
+
}
|
|
7132
|
+
const nestedState = safeObject(rawPayload?.state);
|
|
7133
|
+
if (
|
|
7134
|
+
!rawPayload
|
|
7135
|
+
|| "workingDirectory" in rawPayload
|
|
7136
|
+
|| !suppliedSessionAliasesMatch(rawPayload, sessionId)
|
|
7137
|
+
|| !hasOnlyFinishedExplicitOutcomes(rawPayload)
|
|
7138
|
+
|| (nestedState && (
|
|
7139
|
+
"mode" in nestedState
|
|
7140
|
+
|| "session_id" in nestedState
|
|
7141
|
+
|| "workingDirectory" in nestedState
|
|
7142
|
+
|| "active" in nestedState
|
|
7143
|
+
|| "current_phase" in nestedState
|
|
7144
|
+
|| "currentPhase" in nestedState
|
|
7145
|
+
|| !suppliedSessionAliasesMatch(nestedState, sessionId)
|
|
7146
|
+
|| !hasOnlyFinishedExplicitOutcomes(nestedState)
|
|
7147
|
+
))
|
|
7148
|
+
) return false;
|
|
7149
|
+
|
|
7150
|
+
const payload = readStateWriteInputPayload(cwd, canonicalCommand, command);
|
|
7151
|
+
return payload ? isCompleteDeepInterviewTerminalWritePayload(payload, activeState, sessionId) : false;
|
|
7152
|
+
}
|
|
7153
|
+
|
|
6741
7154
|
function isAllowedRalplanTerminalStateWriteCommand(
|
|
6742
7155
|
cwd: string,
|
|
6743
7156
|
command: string,
|
|
@@ -6776,10 +7189,56 @@ function commandEndsPlanningPhase(cwd: string, command: string): boolean {
|
|
|
6776
7189
|
return payload ? isPlanningPhaseDeactivationPayload(payload) : true;
|
|
6777
7190
|
}
|
|
6778
7191
|
|
|
6779
|
-
function isAllowedDeepInterviewBashWrite(
|
|
7192
|
+
function isAllowedDeepInterviewBashWrite(
|
|
7193
|
+
cwd: string,
|
|
7194
|
+
command: string,
|
|
7195
|
+
activeState?: Record<string, unknown>,
|
|
7196
|
+
sessionId = "",
|
|
7197
|
+
): boolean {
|
|
7198
|
+
const stateWriteOperations = collectOmxStateCommandOperations(command, "write");
|
|
7199
|
+
const hasUnsafeRuntimeStateWrite = (words: string[]): boolean => {
|
|
7200
|
+
const stateWordIndex = words.indexOf("state");
|
|
7201
|
+
const writeWordIndex = stateWordIndex >= 0 ? words.indexOf("write", stateWordIndex + 1) : -1;
|
|
7202
|
+
if (writeWordIndex <= stateWordIndex) return false;
|
|
7203
|
+
const hasRuntimeBeforeState = words.slice(0, stateWordIndex).some((word) => {
|
|
7204
|
+
const base = shellWordBaseName(word);
|
|
7205
|
+
return isNodeInterpreterCommandWord(base) || base === "bun" || base === "tsx";
|
|
7206
|
+
});
|
|
7207
|
+
if (!hasRuntimeBeforeState) return false;
|
|
7208
|
+
|
|
7209
|
+
const inlineInput = readStateWriteFlagValue(words, "--input");
|
|
7210
|
+
let payload: Record<string, unknown> | null = null;
|
|
7211
|
+
try {
|
|
7212
|
+
const parsed = inlineInput ? safeObject(JSON.parse(inlineInput)) : null;
|
|
7213
|
+
const modeOverride = readStateWriteFlagValue(words, "--mode");
|
|
7214
|
+
payload = parsed
|
|
7215
|
+
? normalizeStateWriteClassificationPayload(modeOverride ? { ...parsed, mode: modeOverride } : parsed)
|
|
7216
|
+
: null;
|
|
7217
|
+
} catch {
|
|
7218
|
+
payload = null;
|
|
7219
|
+
}
|
|
7220
|
+
return (!payload && stateWriteOperations.length === 0)
|
|
7221
|
+
|| Boolean(payload && (
|
|
7222
|
+
safeString(payload.mode).trim().toLowerCase() !== "deep-interview"
|
|
7223
|
+
|| isPlanningPhaseDeactivationPayload(payload)
|
|
7224
|
+
));
|
|
7225
|
+
};
|
|
7226
|
+
const rawCommand = normalizeShellLineContinuations(command).trim();
|
|
7227
|
+
if ([rawCommand, canonicalizeOmxStateTransportCommand(rawCommand)]
|
|
7228
|
+
.some((candidate) => hasUnsafeRuntimeStateWrite(tokenizeShellWords(candidate)))) return false;
|
|
7229
|
+
if (stateWriteOperations.some((operation) => operation.nested)) return false;
|
|
6780
7230
|
if (isAllowedDeepInterviewRalplanHandoffCommand(cwd, command)) return true;
|
|
6781
7231
|
if (hasDeepInterviewRalplanHandoffStateMutation(cwd, command)) return false;
|
|
6782
|
-
if (commandEndsPlanningPhase(cwd, command))
|
|
7232
|
+
if (commandEndsPlanningPhase(cwd, command)) {
|
|
7233
|
+
return activeState
|
|
7234
|
+
? isAllowedDeepInterviewTerminalStateWriteCommand(cwd, command, activeState, sessionId)
|
|
7235
|
+
: false;
|
|
7236
|
+
}
|
|
7237
|
+
if (stateWriteOperations.length > 0) {
|
|
7238
|
+
const canonicalCommand = canonicalizeOmxStateTransportCommand(command);
|
|
7239
|
+
const payload = readStateWriteInputPayload(cwd, canonicalCommand, command);
|
|
7240
|
+
if (!payload || safeString(payload.mode).trim().toLowerCase() !== "deep-interview") return false;
|
|
7241
|
+
}
|
|
6783
7242
|
if (commandHasUntargetedPlanningForbiddenIntent(command)) return false;
|
|
6784
7243
|
if (firstPlanningTmpScriptExecutionTarget(cwd, command)) return false;
|
|
6785
7244
|
if (!commandHasDeepInterviewWriteIntent(command)) return true;
|
|
@@ -6804,13 +7263,11 @@ async function readActiveDeepInterviewStateForPreToolUse(
|
|
|
6804
7263
|
const modeState = sessionId
|
|
6805
7264
|
? await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId, stateDir)
|
|
6806
7265
|
: await readJsonIfExists(join(stateDir, "deep-interview-state.json"));
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
if (hasActiveDeepInterviewSkill) return modeState;
|
|
6813
|
-
}
|
|
7266
|
+
const hasActiveDeepInterviewSkill = listActiveSkills(canonicalState).some((entry) => (
|
|
7267
|
+
entry.skill === "deep-interview"
|
|
7268
|
+
&& matchesSkillStopContext(entry, canonicalState, sessionId, threadId)
|
|
7269
|
+
));
|
|
7270
|
+
if (hasActiveDeepInterviewSkill && modeState?.active === true) return modeState;
|
|
6814
7271
|
if (isInactiveCompletedDeepInterviewPhase(modeState)) return null;
|
|
6815
7272
|
|
|
6816
7273
|
const autopilotState = sessionId
|
|
@@ -6971,7 +7428,7 @@ async function buildRalplanPreToolUseBoundaryOutput(
|
|
|
6971
7428
|
resolvedSessionId?: string,
|
|
6972
7429
|
): Promise<Record<string, unknown> | null> {
|
|
6973
7430
|
const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim();
|
|
6974
|
-
if (await hasTrustedTypedSubagentProvenanceForPreToolUse(payload, cwd, sessionId)) return null;
|
|
7431
|
+
if (await hasTrustedTypedSubagentProvenanceForPreToolUse(payload, cwd, stateDir, sessionId)) return null;
|
|
6975
7432
|
const threadId = readPayloadThreadId(payload);
|
|
6976
7433
|
const activeState = await readActiveRalplanStateForPreToolUse(cwd, stateDir, sessionId, threadId);
|
|
6977
7434
|
if (!activeState) return null;
|
|
@@ -7025,7 +7482,7 @@ async function buildRalplanPreToolUseBoundaryOutput(
|
|
|
7025
7482
|
hookEventName: "PreToolUse",
|
|
7026
7483
|
additionalContext:
|
|
7027
7484
|
`${planningModeDescription}. `
|
|
7028
|
-
+ "Write only planning artifacts under `.omx/context/`, `.omx/plans/`, `.omx/specs/`, `.omx/tmp/`, required `.omx/state/` files, or tracker metadata under `.beads/`. "
|
|
7485
|
+
+ "Write only planning artifacts under `.omx/context/`, `.omx/plans/`, `.omx/specs/`, `.omx/tmp/`, required `.omx/state/` files, Markdown drafts under `.omx/drafts/*.md`, or tracker metadata under `.beads/`. "
|
|
7029
7486
|
+ "Do not edit implementation files or run implementation-focused writes from planning phases. "
|
|
7030
7487
|
+ `To execute, first process an explicit handoff such as ${formatExecutionHandoffList(cwd)}, which must emit terminal planning state before implementation begins.`,
|
|
7031
7488
|
},
|
|
@@ -7039,7 +7496,7 @@ async function buildDeepInterviewPreToolUseBoundaryOutput(
|
|
|
7039
7496
|
resolvedSessionId?: string,
|
|
7040
7497
|
): Promise<Record<string, unknown> | null> {
|
|
7041
7498
|
const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim();
|
|
7042
|
-
if (await hasTrustedTypedSubagentProvenanceForPreToolUse(payload, cwd, sessionId)) return null;
|
|
7499
|
+
if (await hasTrustedTypedSubagentProvenanceForPreToolUse(payload, cwd, stateDir, sessionId)) return null;
|
|
7043
7500
|
const threadId = readPayloadThreadId(payload);
|
|
7044
7501
|
const activeState = await readActiveDeepInterviewStateForPreToolUse(cwd, stateDir, sessionId, threadId);
|
|
7045
7502
|
if (!activeState) return null;
|
|
@@ -7051,7 +7508,7 @@ async function buildDeepInterviewPreToolUseBoundaryOutput(
|
|
|
7051
7508
|
let blockedDetail = "implementation/write tools are blocked until an explicit handoff workflow is activated";
|
|
7052
7509
|
|
|
7053
7510
|
if (toolName === "Bash") {
|
|
7054
|
-
blocked = !isAllowedDeepInterviewBashWrite(cwd, command);
|
|
7511
|
+
blocked = !isAllowedDeepInterviewBashWrite(cwd, command, activeState, sessionId);
|
|
7055
7512
|
if (blocked) {
|
|
7056
7513
|
blockedDetail = buildDeepInterviewBashBlockedDetail(cwd, command);
|
|
7057
7514
|
}
|
|
@@ -7210,17 +7667,76 @@ interface ActiveConductorState {
|
|
|
7210
7667
|
async function hasTrustedTypedSubagentProvenanceForPreToolUse(
|
|
7211
7668
|
payload: CodexHookPayload,
|
|
7212
7669
|
cwd: string,
|
|
7670
|
+
stateDir: string,
|
|
7213
7671
|
sessionId: string,
|
|
7672
|
+
options: { allowUntypedProvenance?: boolean } = {},
|
|
7214
7673
|
): Promise<boolean> {
|
|
7215
7674
|
if (hasTeamWorkerEnvironment()) return true;
|
|
7216
|
-
if (!isTypedAgentRolePayload(payload)) return false;
|
|
7217
7675
|
const trackingState = await readSubagentTrackingState(cwd).catch(() => null);
|
|
7218
7676
|
const session = trackingState?.sessions?.[sessionId];
|
|
7219
7677
|
if (!session) return false;
|
|
7220
7678
|
|
|
7221
7679
|
const payloadThreadId = readPayloadThreadId(payload);
|
|
7680
|
+
const trackedPayloadThread = payloadThreadId ? session.threads[payloadThreadId] : undefined;
|
|
7681
|
+
const hasTrackedAdaptedTypedRole = payloadThreadId !== ""
|
|
7682
|
+
&& isTrustedSubagentThread(session, payloadThreadId)
|
|
7683
|
+
&& trackedPayloadThread?.provenance_kind === OMX_ADAPTED_PROVENANCE
|
|
7684
|
+
&& resolveInstalledRoleName(trackedPayloadThread.role ?? "") !== null;
|
|
7685
|
+
|
|
7686
|
+
// Resolve the Main-root leader THREAD identity from the tracker's leader_thread_id
|
|
7687
|
+
// plus the canonical session's native_session_id (the leader's native thread). Only
|
|
7688
|
+
// genuine leader-thread identifiers may anchor the leader: owner_*_session_id are
|
|
7689
|
+
// session-level ids, not thread anchors, so they must NOT populate this set — their
|
|
7690
|
+
// mere presence would make it non-empty and suppress the fail-closed guard below
|
|
7691
|
+
// without ever excluding the leader thread (#3117 P4). The native_session_id anchor is
|
|
7692
|
+
// honored only when the root session pointer provably maps to the sessionId under
|
|
7693
|
+
// evaluation, so a stale/foreign session.json cannot supply another session's leader
|
|
7694
|
+
// anchor (#3117 P3); the tracker alone is insufficient because it can omit
|
|
7695
|
+
// leader_thread_id or corruptly label the leader kind:"subagent" (#3117 P2).
|
|
7696
|
+
const sessionState = await readRootSessionStateFromStateDir(stateDir).catch(() => null);
|
|
7697
|
+
const trackerLeaderThreadId = safeString(session.leader_thread_id).trim();
|
|
7698
|
+
const leaderIdentityAnchors = new Set<string>();
|
|
7699
|
+
if (trackerLeaderThreadId) leaderIdentityAnchors.add(trackerLeaderThreadId);
|
|
7700
|
+
if (sessionState && sessionId && payloadMatchesSessionPointer(sessionId, sessionState)) {
|
|
7701
|
+
const nativeSessionId = safeString(sessionState.native_session_id).trim();
|
|
7702
|
+
if (nativeSessionId) leaderIdentityAnchors.add(nativeSessionId);
|
|
7703
|
+
}
|
|
7704
|
+
|
|
7705
|
+
// Leader self-guard: the Main-root Conductor is never a delegated executor. Block it
|
|
7706
|
+
// ahead of every trust path, even when tracker or payload provenance is (possibly
|
|
7707
|
+
// corruptly) attached to the leader thread.
|
|
7708
|
+
if (payloadThreadId && leaderIdentityAnchors.has(payloadThreadId)) return false;
|
|
7709
|
+
|
|
7710
|
+
// Fail closed: without an authoritative leader anchor we cannot affirm the payload is
|
|
7711
|
+
// a non-leader delegated performer rather than a mislabeled leader, so we refuse trust
|
|
7712
|
+
// instead of inferring it from a corrupt tracker kind:"subagent" alone (#3117 P2).
|
|
7713
|
+
if (leaderIdentityAnchors.size === 0) return false;
|
|
7714
|
+
|
|
7715
|
+
// Planning boundary guards (ralplan, deep-interview) still require a recognized typed
|
|
7716
|
+
// agent role, so an untyped collaboration.spawn_agent child cannot write before an
|
|
7717
|
+
// execution handoff/approval. A tracker-recorded OMX-adapted child is the equivalent
|
|
7718
|
+
// of a typed role only when its own persisted role is installed and validated.
|
|
7719
|
+
if (
|
|
7720
|
+
options.allowUntypedProvenance !== true
|
|
7721
|
+
&& !isTypedAgentRolePayload(payload)
|
|
7722
|
+
&& !hasTrackedAdaptedTypedRole
|
|
7723
|
+
) {
|
|
7724
|
+
return false;
|
|
7725
|
+
}
|
|
7726
|
+
|
|
7727
|
+
// Tracker-backed provenance: the payload's own thread is a recorded, non-leader
|
|
7728
|
+
// subagent for this session — the non-spoofable anchor that lets native
|
|
7729
|
+
// collaboration.spawn_agent children/descendants edit under the Conductor guard even
|
|
7730
|
+
// without a recognized typed role (#3116). subagent-tracking.json is derived from
|
|
7731
|
+
// child SessionStart transcript metadata, not the live tool-call payload.
|
|
7222
7732
|
if (payloadThreadId && isTrustedSubagentThread(session, payloadThreadId)) return true;
|
|
7223
7733
|
|
|
7734
|
+
// Runtime-attached spawn provenance: trust a genuine spawned subagent turn whose
|
|
7735
|
+
// declared parent maps to this session's leader or a tracked thread. parentThreadId
|
|
7736
|
+
// comes from the runtime-set source.subagent.thread_spawn (not agent-controlled tool
|
|
7737
|
+
// arguments); an absent parent is rejected, the leader self-guard above blocks the
|
|
7738
|
+
// main root, and cross-session parents fail because they are not in this session's
|
|
7739
|
+
// tracked threads (#3116).
|
|
7224
7740
|
const source = safeObject(payload.source);
|
|
7225
7741
|
const subagent = safeObject(source.subagent);
|
|
7226
7742
|
const threadSpawn = safeObject(subagent.thread_spawn);
|
|
@@ -7231,9 +7747,7 @@ async function hasTrustedTypedSubagentProvenanceForPreToolUse(
|
|
|
7231
7747
|
?? threadSpawn.leaderThreadId,
|
|
7232
7748
|
).trim();
|
|
7233
7749
|
if (!parentThreadId) return false;
|
|
7234
|
-
|
|
7235
|
-
if (payloadThreadId && leaderThreadId && payloadThreadId === leaderThreadId) return false;
|
|
7236
|
-
return leaderThreadId === parentThreadId || parentThreadId in session.threads;
|
|
7750
|
+
return leaderIdentityAnchors.has(parentThreadId) || parentThreadId in session.threads;
|
|
7237
7751
|
}
|
|
7238
7752
|
|
|
7239
7753
|
function isActiveConductorModeState(state: Record<string, unknown> | null, mode: string, sessionId: string): boolean {
|
|
@@ -7262,7 +7776,7 @@ async function readActiveMainRootConductorStateForPreToolUse(
|
|
|
7262
7776
|
}
|
|
7263
7777
|
const threadId = readPayloadThreadId(payload);
|
|
7264
7778
|
if (!sessionId) return null;
|
|
7265
|
-
if (await hasTrustedTypedSubagentProvenanceForPreToolUse(payload, cwd, sessionId)) return null;
|
|
7779
|
+
if (await hasTrustedTypedSubagentProvenanceForPreToolUse(payload, cwd, stateDir, sessionId, { allowUntypedProvenance: true })) return null;
|
|
7266
7780
|
|
|
7267
7781
|
const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId);
|
|
7268
7782
|
if (!canonicalState) return null;
|
|
@@ -8116,6 +8630,10 @@ export async function buildConductorPreToolUseWriteGuardOutput(
|
|
|
8116
8630
|
const nativeSubagentSupport = resolveNativeSubagentSupportStatus({
|
|
8117
8631
|
payload,
|
|
8118
8632
|
persistedSupportBlocker: await readJsonIfExists(nativeSubagentSupportBlockerPath(stateDir)),
|
|
8633
|
+
persistedRoleRoutingMarker: readRoleRoutingMarker(stateDir, {
|
|
8634
|
+
cwd,
|
|
8635
|
+
sessionId: resolvedSessionId || readPayloadSessionId(payload),
|
|
8636
|
+
}),
|
|
8119
8637
|
persistedCapacityBlocker: await readJsonIfExists(nativeSubagentCapacityBlockerPath(stateDir)),
|
|
8120
8638
|
cwd,
|
|
8121
8639
|
sessionId: resolvedSessionId || readPayloadSessionId(payload),
|
|
@@ -8150,6 +8668,9 @@ export async function buildConductorPreToolUseWriteGuardOutput(
|
|
|
8150
8668
|
const unsupportedNativeGuidance = isUnsupportedNativeSubagentEvidence(nativeSubagentSupport)
|
|
8151
8669
|
? ` ${buildUnsupportedNativeSubagentGuidance(nativeSubagentSupport)} Treat the active conductor workflow as blocked/cancelled for native delegation recovery; do not call multi_agent_v1.close_agent.`
|
|
8152
8670
|
: "";
|
|
8671
|
+
const roleRoutingUnavailableGuidance = isRoleRoutingUnavailableEvidence(nativeSubagentSupport)
|
|
8672
|
+
? ` ${buildRoleRoutingUnavailableGuidance(nativeSubagentSupport)}`
|
|
8673
|
+
: "";
|
|
8153
8674
|
return {
|
|
8154
8675
|
decision: "block",
|
|
8155
8676
|
reason: `Main-root Conductor mode is active (${activeState.mode} phase: ${formatPhase(activeState.phase, "active")}); direct plan/code writes are blocked and must be delegated; ${blockedDetail}.`,
|
|
@@ -8160,11 +8681,12 @@ export async function buildConductorPreToolUseWriteGuardOutput(
|
|
|
8160
8681
|
+ "Use specialized agents for source edits and plan/spec authorship. "
|
|
8161
8682
|
+ `Main-root Conductor may write only orchestration metadata/transport/ledger artifacts under ${CONDUCTOR_ORCHESTRATION_METADATA_PREFIXES.join(", ")}; path location alone is not authorization for substantive deliverables. `
|
|
8162
8683
|
+ unsupportedNativeGuidance
|
|
8684
|
+
+ roleRoutingUnavailableGuidance
|
|
8163
8685
|
+ " Autopilot rework and typed subagent/worker lanes are exempt from this guard.",
|
|
8164
8686
|
},
|
|
8165
8687
|
};
|
|
8166
|
-
}
|
|
8167
8688
|
|
|
8689
|
+
}
|
|
8168
8690
|
function isInPlaceEditorCommand(word: string, commandName: string): boolean {
|
|
8169
8691
|
if (commandName === "sed") return word === "--in-place" || /^--in-place(?:=.+)?$/.test(word) || /^-[^-\s]*i(?:.*)?$/.test(word);
|
|
8170
8692
|
if (commandName === "perl") return word === "-i" || word === "-pi" || /^-pi(?:\..+)?$/.test(word) || /^-p.*i(?:\..+)?$/.test(word);
|
|
@@ -9250,14 +9772,15 @@ async function buildStopHookOutput(
|
|
|
9250
9772
|
payload: CodexHookPayload,
|
|
9251
9773
|
cwd: string,
|
|
9252
9774
|
stateDir: string,
|
|
9253
|
-
options: { skipRalphStopBlock?: boolean } = {},
|
|
9775
|
+
options: { skipAutoNudge?: boolean; skipRalphStopBlock?: boolean; canonicalSessionId?: string } = {},
|
|
9254
9776
|
): Promise<Record<string, unknown> | null> {
|
|
9255
9777
|
if (isStopExempt(payload)) {
|
|
9256
9778
|
return null;
|
|
9257
9779
|
}
|
|
9258
9780
|
|
|
9259
9781
|
const sessionId = readPayloadSessionId(payload);
|
|
9260
|
-
const canonicalSessionId =
|
|
9782
|
+
const canonicalSessionId = options.canonicalSessionId
|
|
9783
|
+
?? await resolveInternalSessionIdForPayload(cwd, sessionId);
|
|
9261
9784
|
const threadId = readPayloadThreadId(payload);
|
|
9262
9785
|
const suppressParentWorkflowStop = shouldSuppressParentWorkflowStopForSideConversation(payload);
|
|
9263
9786
|
if (canonicalSessionId) {
|
|
@@ -9308,7 +9831,7 @@ async function buildStopHookOutput(
|
|
|
9308
9831
|
}
|
|
9309
9832
|
const ralphState = options.skipRalphStopBlock === true
|
|
9310
9833
|
? null
|
|
9311
|
-
: await readActiveRalphState(cwd, stateDir, canonicalSessionId, ralphOwnerContext);
|
|
9834
|
+
: await readActiveRalphState(cwd, stateDir, sessionId || canonicalSessionId, ralphOwnerContext);
|
|
9312
9835
|
if (!ralphState) {
|
|
9313
9836
|
const autoresearchState = await readActiveAutoresearchState(cwd, canonicalSessionId);
|
|
9314
9837
|
if (autoresearchState) {
|
|
@@ -9492,7 +10015,8 @@ async function buildStopHookOutput(
|
|
|
9492
10015
|
const autoNudgePhase = await readStopAutoNudgePhase(cwd, stateDir, canonicalSessionId, threadId);
|
|
9493
10016
|
|
|
9494
10017
|
if (
|
|
9495
|
-
|
|
10018
|
+
options.skipAutoNudge !== true
|
|
10019
|
+
&& autoNudgeConfig.enabled
|
|
9496
10020
|
&& detectNativeStopStallPattern(lastAssistantMessage, autoNudgeConfig.patterns, autoNudgePhase)
|
|
9497
10021
|
) {
|
|
9498
10022
|
const effectiveResponse = resolveEffectiveAutoNudgeResponse(autoNudgeConfig.response);
|
|
@@ -9538,7 +10062,7 @@ async function buildStopHookOutput(
|
|
|
9538
10062
|
documentRefreshWarning.triggeringPaths.join("|"),
|
|
9539
10063
|
canonicalSessionId,
|
|
9540
10064
|
),
|
|
9541
|
-
|
|
10065
|
+
{ systemMessage: documentRefreshWarning.message },
|
|
9542
10066
|
canonicalSessionId,
|
|
9543
10067
|
{ allowRepeatDuringStopHook: false },
|
|
9544
10068
|
);
|
|
@@ -9569,13 +10093,39 @@ async function buildStopHookOutput(
|
|
|
9569
10093
|
);
|
|
9570
10094
|
}
|
|
9571
10095
|
|
|
10096
|
+
async function preflightNativePromptTarget(
|
|
10097
|
+
stateDir: string,
|
|
10098
|
+
context: ResolvedPromptTurnContext,
|
|
10099
|
+
): Promise<ResolvedPromptTurnContext> {
|
|
10100
|
+
if (context.status !== "authorized") return context;
|
|
10101
|
+
const targetDir = join(stateDir, "sessions", context.authorization.targetSessionId);
|
|
10102
|
+
const evidence: Array<{ ownerCodexSessionId?: unknown; targetSessionId?: unknown }> = [];
|
|
10103
|
+
let filenames: string[];
|
|
10104
|
+
try {
|
|
10105
|
+
filenames = await readdir(targetDir);
|
|
10106
|
+
} catch (error) {
|
|
10107
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return context;
|
|
10108
|
+
return preflightSelectedTargetOwner(context, [{ ownerCodexSessionId: {} }], "native", new Date().toISOString());
|
|
10109
|
+
}
|
|
10110
|
+
for (const filename of filenames) {
|
|
10111
|
+
if (!filename.endsWith("-state.json") && filename !== SKILL_ACTIVE_STATE_FILE) continue;
|
|
10112
|
+
try {
|
|
10113
|
+
const value = JSON.parse(await readFile(join(targetDir, filename), "utf8")) as unknown;
|
|
10114
|
+
evidence.push(...extractSelectedTargetOwnerEvidence(value));
|
|
10115
|
+
} catch {
|
|
10116
|
+
evidence.push({ ownerCodexSessionId: {} });
|
|
10117
|
+
}
|
|
10118
|
+
}
|
|
10119
|
+
return preflightSelectedTargetOwner(context, evidence, "native", new Date().toISOString());
|
|
10120
|
+
}
|
|
10121
|
+
|
|
9572
10122
|
export async function dispatchCodexNativeHook(
|
|
9573
10123
|
payload: CodexHookPayload,
|
|
9574
10124
|
options: NativeHookDispatchOptions = {},
|
|
9575
10125
|
): Promise<NativeHookDispatchResult> {
|
|
9576
10126
|
const hookEventName = readHookEventName(payload);
|
|
9577
10127
|
const cwd = options.cwd ?? (safeString(payload.cwd).trim() || process.cwd());
|
|
9578
|
-
if (hookEventName === "
|
|
10128
|
+
if (hookEventName === "PostCompact" && process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE === "1") {
|
|
9579
10129
|
return {
|
|
9580
10130
|
hookEventName,
|
|
9581
10131
|
omxEventName: mapCodexHookEventToOmxEvent(hookEventName),
|
|
@@ -9583,24 +10133,71 @@ export async function dispatchCodexNativeHook(
|
|
|
9583
10133
|
outputJson: null,
|
|
9584
10134
|
};
|
|
9585
10135
|
}
|
|
9586
|
-
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
10136
|
+
try {
|
|
10137
|
+
recoverAdaptedRoleBindings(cwd, getBaseStateDir(cwd));
|
|
10138
|
+
} catch {
|
|
10139
|
+
// Recovery is best-effort for unrelated hook events.
|
|
10140
|
+
}
|
|
10141
|
+
if (hookEventName === "Stop" && !hasNativeStopRuntimeSurface(cwd)) {
|
|
10142
|
+
return {
|
|
10143
|
+
hookEventName,
|
|
10144
|
+
omxEventName: mapCodexHookEventToOmxEvent(hookEventName),
|
|
10145
|
+
skillState: null,
|
|
10146
|
+
outputJson: null,
|
|
10147
|
+
};
|
|
9591
10148
|
}
|
|
10149
|
+
// Native hooks must use the exact pointer root selected for this dispatch.
|
|
10150
|
+
const pointerContext = resolveSessionPointerContext(cwd);
|
|
10151
|
+
const stateDir = pointerContext.baseStateDir;
|
|
9592
10152
|
|
|
9593
10153
|
const omxEventName = mapCodexHookEventToOmxEvent(hookEventName);
|
|
9594
10154
|
let skillState: SkillActiveState | null = null;
|
|
9595
10155
|
let triageAdditionalContext: string | null = null;
|
|
9596
10156
|
let goalWorkflowAdditionalContext: string | null = null;
|
|
9597
10157
|
let ultragoalSteeringAdditionalContext: string | null = null;
|
|
10158
|
+
let promptClassification: KeywordInputClassification | null = null;
|
|
9598
10159
|
|
|
9599
10160
|
const nativeSessionId = safeString(payload.session_id ?? payload.sessionId).trim();
|
|
9600
10161
|
const threadId = safeString(payload.thread_id ?? payload.threadId).trim();
|
|
9601
10162
|
const turnId = safeString(payload.turn_id ?? payload.turnId).trim();
|
|
9602
|
-
const
|
|
10163
|
+
const pointer = await readSessionPointer(pointerContext);
|
|
10164
|
+
const currentSessionState = pointer.status === "usable" ? pointer.state ?? null : null;
|
|
10165
|
+
let promptTurnContext: ResolvedPromptTurnContext | null = hookEventName === "UserPromptSubmit"
|
|
10166
|
+
? evaluateResolvedPromptTurn({
|
|
10167
|
+
producer: "native",
|
|
10168
|
+
payloadSessionId: payload.session_id ?? payload.sessionId,
|
|
10169
|
+
ownerEnvSessionId: undefined,
|
|
10170
|
+
selectedPointer: pointer,
|
|
10171
|
+
threadFacts: await readNativePromptThreadFacts(cwd, nativeSessionId, threadId, currentSessionState),
|
|
10172
|
+
nowIso: new Date().toISOString(),
|
|
10173
|
+
})
|
|
10174
|
+
: null;
|
|
10175
|
+
if (promptTurnContext) {
|
|
10176
|
+
promptTurnContext = await preflightNativePromptTarget(stateDir, promptTurnContext);
|
|
10177
|
+
}
|
|
10178
|
+
if (promptTurnContext?.status === "rejected") {
|
|
10179
|
+
await appendPromptSessionProvenanceRejection(pointerContext, promptTurnContext.diagnostic).catch(() => {});
|
|
10180
|
+
return { hookEventName, omxEventName, skillState: null, outputJson: null };
|
|
10181
|
+
}
|
|
10182
|
+
if (promptTurnContext?.status === "suppressed-target-child") {
|
|
10183
|
+
return { hookEventName, omxEventName, skillState: null, outputJson: null };
|
|
10184
|
+
}
|
|
10185
|
+
if (hookEventName !== "Stop") {
|
|
10186
|
+
await mkdir(stateDir, { recursive: true });
|
|
10187
|
+
}
|
|
10188
|
+
let allowImplicitSessionSideEffects = pointer.status === "usable" || pointer.status === "absent" || promptTurnContext?.status === "authorized";
|
|
10189
|
+
let stopAuthorizationFailure: { stopReason: string; reason: string } | null = allowImplicitSessionSideEffects
|
|
10190
|
+
? null
|
|
10191
|
+
: {
|
|
10192
|
+
stopReason: "session_pointer_unusable",
|
|
10193
|
+
reason: `OMX cannot authorize Stop while the selected session pointer is ${pointer.status}; repair the pointer evidence before continuing.`,
|
|
10194
|
+
};
|
|
9603
10195
|
let canonicalSessionId = safeString(currentSessionState?.session_id).trim();
|
|
10196
|
+
if (promptTurnContext?.status === "authorized") {
|
|
10197
|
+
canonicalSessionId = promptTurnContext.authorization.targetSessionId;
|
|
10198
|
+
}
|
|
10199
|
+
const allowPromptGlobalSideEffects = promptTurnContext?.status !== "authorized"
|
|
10200
|
+
|| promptTurnContext.authorization.globalSideEffects === "allow";
|
|
9604
10201
|
let resolvedNativeSessionId = nativeSessionId;
|
|
9605
10202
|
let skipCanonicalSessionStartContext = false;
|
|
9606
10203
|
let isSubagentSessionStart = false;
|
|
@@ -9660,23 +10257,61 @@ export async function dispatchCodexNativeHook(
|
|
|
9660
10257
|
);
|
|
9661
10258
|
}
|
|
9662
10259
|
} else {
|
|
9663
|
-
const
|
|
9664
|
-
|
|
9665
|
-
|
|
9666
|
-
|
|
9667
|
-
|
|
10260
|
+
const ownerOmxSessionId = await resolveVerifiedOwnerOmxSessionId();
|
|
10261
|
+
try {
|
|
10262
|
+
const sessionState = await reconcileNativeSessionStart(cwd, nativeSessionId, {
|
|
10263
|
+
context: pointerContext,
|
|
10264
|
+
pid: options.sessionOwnerPid ?? resolveSessionOwnerPid(payload),
|
|
10265
|
+
...(ownerOmxSessionId
|
|
10266
|
+
? { ownerOmxSessionId, ownerAliasVerified: true }
|
|
10267
|
+
: {}),
|
|
10268
|
+
});
|
|
10269
|
+
canonicalSessionId = safeString(sessionState.session_id).trim();
|
|
10270
|
+
resolvedNativeSessionId = safeString(sessionState.native_session_id).trim() || nativeSessionId;
|
|
10271
|
+
allowImplicitSessionSideEffects = true;
|
|
10272
|
+
stopAuthorizationFailure = null;
|
|
10273
|
+
// #3181: leader attestation is intentionally NOT performed here. This branch is
|
|
10274
|
+
// reached whenever readNativeSubagentSessionStartMetadata() returns null, which
|
|
10275
|
+
// conflates a genuine root start with an unreadable/malformed child transcript, so
|
|
10276
|
+
// it cannot positively classify a leader (a legitimate leader may also carry no
|
|
10277
|
+
// transcript). Leader attestation is performed only on the strictly-gated fresh
|
|
10278
|
+
// leader PreToolUse path below, which fires before the first in-turn role-intent
|
|
10279
|
+
// write. Fail closed here rather than risk a false-leader adoption.
|
|
10280
|
+
} catch (error) {
|
|
10281
|
+
if (!isSessionPointerLaunchAbort(error)) throw error;
|
|
10282
|
+
canonicalSessionId = "";
|
|
10283
|
+
resolvedNativeSessionId = nativeSessionId;
|
|
10284
|
+
skipCanonicalSessionStartContext = true;
|
|
10285
|
+
allowImplicitSessionSideEffects = false;
|
|
10286
|
+
stopAuthorizationFailure = {
|
|
10287
|
+
stopReason: "session_pointer_unusable",
|
|
10288
|
+
reason: `OMX cannot authorize Stop while the selected session pointer is ${pointer.status}; repair the pointer evidence before continuing.`,
|
|
10289
|
+
};
|
|
10290
|
+
}
|
|
9668
10291
|
}
|
|
9669
10292
|
} else if (!canonicalSessionId) {
|
|
9670
10293
|
canonicalSessionId = safeString(currentSessionState?.session_id).trim();
|
|
9671
10294
|
}
|
|
9672
10295
|
|
|
9673
10296
|
if (hookEventName === "Stop") {
|
|
9674
|
-
const
|
|
10297
|
+
const stopPayloadSessionId = readPayloadSessionId(payload);
|
|
9675
10298
|
const stopCanonicalSessionId = await resolveInternalSessionIdForPayload(
|
|
9676
10299
|
cwd,
|
|
9677
|
-
|
|
10300
|
+
stopPayloadSessionId,
|
|
10301
|
+
undefined,
|
|
10302
|
+
currentSessionState,
|
|
10303
|
+
pointer.status === "absent",
|
|
9678
10304
|
);
|
|
9679
|
-
if (stopCanonicalSessionId) {
|
|
10305
|
+
if (stopPayloadSessionId && !stopCanonicalSessionId) {
|
|
10306
|
+
canonicalSessionId = "";
|
|
10307
|
+
allowImplicitSessionSideEffects = false;
|
|
10308
|
+
if (!stopAuthorizationFailure) {
|
|
10309
|
+
stopAuthorizationFailure = {
|
|
10310
|
+
stopReason: "session_scope_unmatched",
|
|
10311
|
+
reason: `OMX cannot authorize Stop for unmatched session id ${stopPayloadSessionId}; the selected session pointer remains authoritative.`,
|
|
10312
|
+
};
|
|
10313
|
+
}
|
|
10314
|
+
} else if (stopCanonicalSessionId) {
|
|
9680
10315
|
canonicalSessionId = stopCanonicalSessionId;
|
|
9681
10316
|
}
|
|
9682
10317
|
if (canonicalSessionId && safeString(currentSessionState?.session_id).trim() === canonicalSessionId) {
|
|
@@ -9685,8 +10320,8 @@ export async function dispatchCodexNativeHook(
|
|
|
9685
10320
|
}
|
|
9686
10321
|
}
|
|
9687
10322
|
|
|
9688
|
-
|
|
9689
|
-
|
|
10323
|
+
let eventSessionId = canonicalSessionId || nativeSessionId || undefined;
|
|
10324
|
+
let sessionIdForState: string | null = canonicalSessionId || null;
|
|
9690
10325
|
let outputJson: Record<string, unknown> | null = null;
|
|
9691
10326
|
const typedAgentRolePayload = isTypedAgentRolePayload(payload);
|
|
9692
10327
|
const isSubagentPromptSubmit = hookEventName === "UserPromptSubmit"
|
|
@@ -9715,36 +10350,81 @@ export async function dispatchCodexNativeHook(
|
|
|
9715
10350
|
)),
|
|
9716
10351
|
)).some(Boolean)
|
|
9717
10352
|
: false;
|
|
10353
|
+
if (isSubagentStop && stopAuthorizationFailure?.stopReason === "session_scope_unmatched") {
|
|
10354
|
+
canonicalSessionId = normalizeSessionId(readPayloadSessionId(payload)) ?? "";
|
|
10355
|
+
allowImplicitSessionSideEffects = true;
|
|
10356
|
+
stopAuthorizationFailure = null;
|
|
10357
|
+
eventSessionId = canonicalSessionId || nativeSessionId || undefined;
|
|
10358
|
+
sessionIdForState = canonicalSessionId || null;
|
|
10359
|
+
}
|
|
9718
10360
|
const suppressNoisySubagentLifecycleDispatch =
|
|
9719
10361
|
(isSubagentSessionStart || isSubagentStop)
|
|
9720
10362
|
&& shouldSuppressSubagentLifecycleHookDispatch();
|
|
9721
10363
|
|
|
9722
10364
|
if (hookEventName === "UserPromptSubmit") {
|
|
9723
10365
|
const prompt = readPromptText(payload);
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
10366
|
+
if (!isSubagentPromptSubmit) {
|
|
10367
|
+
promptClassification = classifyKeywordInput(prompt);
|
|
10368
|
+
}
|
|
10369
|
+
goalWorkflowAdditionalContext = allowPromptGlobalSideEffects
|
|
10370
|
+
? await buildCompletedGoalCleanupPromptWarning(cwd, prompt).catch(() => null)
|
|
10371
|
+
?? await buildGoalWorkflowReconciliationPromptWarning(cwd, prompt).catch(() => null)
|
|
10372
|
+
: null;
|
|
10373
|
+
ultragoalSteeringAdditionalContext = prompt && !isSubagentPromptSubmit && allowImplicitSessionSideEffects && allowPromptGlobalSideEffects
|
|
9727
10374
|
? await applyUserPromptUltragoalSteering(cwd, prompt).catch((error) => `OMX native UserPromptSubmit rejected bounded .omx/ultragoal steering for G002-cli-and-prompt-submit-bridge: ${error instanceof Error ? error.message : String(error)}`)
|
|
9728
10375
|
: null;
|
|
9729
|
-
|
|
10376
|
+
let suppressActivationSeeding = !allowImplicitSessionSideEffects;
|
|
10377
|
+
if (promptTurnContext?.status === "authorized") {
|
|
10378
|
+
sessionIdForState = promptTurnContext.authorization.targetSessionId;
|
|
10379
|
+
} else if (prompt && !isSubagentPromptSubmit && allowImplicitSessionSideEffects) {
|
|
10380
|
+
const rawHookSessionId = canonicalSessionId || nativeSessionId;
|
|
10381
|
+
const normalizedHookSessionId = normalizeSessionId(rawHookSessionId);
|
|
10382
|
+
const explicitHookSessionId = currentSessionState ? undefined : normalizedHookSessionId;
|
|
10383
|
+
if (rawHookSessionId && !normalizedHookSessionId) {
|
|
10384
|
+
suppressActivationSeeding = true;
|
|
10385
|
+
} else {
|
|
10386
|
+
try {
|
|
10387
|
+
const writableScope = await resolveWritableStateScope(cwd, explicitHookSessionId);
|
|
10388
|
+
sessionIdForState = writableScope.sessionId ?? null;
|
|
10389
|
+
} catch (error) {
|
|
10390
|
+
if (!isImplicitWritableScopeFailure(error)) throw error;
|
|
10391
|
+
suppressActivationSeeding = true;
|
|
10392
|
+
}
|
|
10393
|
+
}
|
|
10394
|
+
}
|
|
10395
|
+
if (prompt && promptClassification && !isSubagentPromptSubmit && !suppressActivationSeeding) {
|
|
9730
10396
|
skillState = buildNativeOutsideTmuxTeamPromptBlockState(
|
|
9731
|
-
|
|
10397
|
+
promptClassification,
|
|
9732
10398
|
cwd,
|
|
9733
10399
|
payload,
|
|
9734
|
-
sessionIdForState,
|
|
10400
|
+
sessionIdForState || undefined,
|
|
9735
10401
|
threadId || undefined,
|
|
9736
10402
|
turnId || undefined,
|
|
9737
10403
|
) ?? await recordSkillActivation({
|
|
9738
10404
|
stateDir,
|
|
9739
10405
|
sourceCwd: cwd,
|
|
9740
10406
|
text: prompt,
|
|
9741
|
-
|
|
10407
|
+
classification: promptClassification,
|
|
10408
|
+
allowSecondaryTeam: !isNativeOutsideTmuxUserPrompt(cwd, payload, sessionIdForState || undefined),
|
|
10409
|
+
sessionId: sessionIdForState || undefined,
|
|
9742
10410
|
threadId,
|
|
9743
10411
|
turnId,
|
|
10412
|
+
resolvedPromptTurnContext: promptTurnContext ?? undefined,
|
|
10413
|
+
onProvenanceRejected: async (diagnostic) => {
|
|
10414
|
+
await appendPromptSessionProvenanceRejection(pointerContext, diagnostic).catch(() => {});
|
|
10415
|
+
},
|
|
9744
10416
|
});
|
|
9745
10417
|
}
|
|
9746
10418
|
// --- Triage classifier (advisory-only, non-keyword prompts) ---
|
|
9747
|
-
if (
|
|
10419
|
+
if (
|
|
10420
|
+
prompt
|
|
10421
|
+
&& skillState === null
|
|
10422
|
+
&& !isSubagentPromptSubmit
|
|
10423
|
+
&& allowPromptGlobalSideEffects
|
|
10424
|
+
&& promptClassification?.reservedInput === null
|
|
10425
|
+
&& promptClassification.hasExplicitLikeInvocation === false
|
|
10426
|
+
&& promptClassification.matches.length === 0
|
|
10427
|
+
) {
|
|
9748
10428
|
try {
|
|
9749
10429
|
if (readTriageConfig().enabled) {
|
|
9750
10430
|
const normalized = prompt.trim().toLowerCase();
|
|
@@ -9773,7 +10453,9 @@ export async function dispatchCodexNativeHook(
|
|
|
9773
10453
|
},
|
|
9774
10454
|
suppress_followup: true,
|
|
9775
10455
|
};
|
|
9776
|
-
|
|
10456
|
+
if (!suppressActivationSeeding) {
|
|
10457
|
+
writeTriageState({ cwd, sessionId: sessionIdForState || null, state: newState });
|
|
10458
|
+
}
|
|
9777
10459
|
} else if (decision.lane === "LIGHT") {
|
|
9778
10460
|
if (decision.destination === "explore") {
|
|
9779
10461
|
triageAdditionalContext =
|
|
@@ -9802,7 +10484,9 @@ export async function dispatchCodexNativeHook(
|
|
|
9802
10484
|
},
|
|
9803
10485
|
suppress_followup: true,
|
|
9804
10486
|
};
|
|
9805
|
-
|
|
10487
|
+
if (!suppressActivationSeeding) {
|
|
10488
|
+
writeTriageState({ cwd, sessionId: sessionIdForState || null, state: newState });
|
|
10489
|
+
}
|
|
9806
10490
|
}
|
|
9807
10491
|
}
|
|
9808
10492
|
// lane === "PASS": no context, no state write
|
|
@@ -9816,7 +10500,7 @@ export async function dispatchCodexNativeHook(
|
|
|
9816
10500
|
const skipHudReconcileForDoctorSmoke = process.env.OMX_NATIVE_HOOK_DOCTOR_SMOKE === "1";
|
|
9817
10501
|
const skipHudReconcileForTeamWorkerPane = !isSubagentPromptSubmit
|
|
9818
10502
|
&& await isConfirmedTeamWorkerPromptSubmitPane(cwd).catch(() => false);
|
|
9819
|
-
if (!skipHudReconcileForDoctorSmoke && !skipHudReconcileForTeamWorkerPane) {
|
|
10503
|
+
if (allowImplicitSessionSideEffects && allowPromptGlobalSideEffects && !skipHudReconcileForDoctorSmoke && !skipHudReconcileForTeamWorkerPane) {
|
|
9820
10504
|
const reconcileHudForPromptSubmitFn = options.reconcileHudForPromptSubmitFn ?? reconcileHudForPromptSubmit;
|
|
9821
10505
|
const hudSessionId = resolveHudReconcileSessionId(
|
|
9822
10506
|
currentSessionState,
|
|
@@ -9833,7 +10517,7 @@ export async function dispatchCodexNativeHook(
|
|
|
9833
10517
|
}
|
|
9834
10518
|
}
|
|
9835
10519
|
|
|
9836
|
-
if (omxEventName && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
|
|
10520
|
+
if (omxEventName && allowImplicitSessionSideEffects && allowPromptGlobalSideEffects && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
|
|
9837
10521
|
const baseContext = buildBaseContext(cwd, payload, hookEventName!, canonicalSessionId);
|
|
9838
10522
|
if (resolvedNativeSessionId) {
|
|
9839
10523
|
baseContext.native_session_id = resolvedNativeSessionId;
|
|
@@ -9874,12 +10558,14 @@ export async function dispatchCodexNativeHook(
|
|
|
9874
10558
|
})
|
|
9875
10559
|
: isSubagentPromptSubmit
|
|
9876
10560
|
? null
|
|
9877
|
-
:
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
|
|
10561
|
+
: promptClassification
|
|
10562
|
+
? [
|
|
10563
|
+
buildAdditionalContextMessage(promptClassification, skillState, cwd, payload),
|
|
10564
|
+
ultragoalSteeringAdditionalContext,
|
|
10565
|
+
goalWorkflowAdditionalContext,
|
|
10566
|
+
triageAdditionalContext,
|
|
10567
|
+
].filter((entry): entry is string => Boolean(entry)).join("\n\n") || null
|
|
10568
|
+
: null;
|
|
9883
10569
|
if (additionalContext) {
|
|
9884
10570
|
outputJson = {
|
|
9885
10571
|
hookSpecificOutput: {
|
|
@@ -9889,12 +10575,55 @@ export async function dispatchCodexNativeHook(
|
|
|
9889
10575
|
};
|
|
9890
10576
|
}
|
|
9891
10577
|
} else if (hookEventName === "PreToolUse") {
|
|
10578
|
+
// #3181 Phase-1 (PreToolUse): this hook fires before the shell tool call that runs
|
|
10579
|
+
// the first in-turn `omx ralplan role-intent write`. On a fresh App/outside-tmux
|
|
10580
|
+
// turn where SessionStart did not establish the pointer, reconcile the canonical
|
|
10581
|
+
// pointer and attest the leader here so the first command can bootstrap. Strictly
|
|
10582
|
+
// gated to a fresh (absent-pointer) LEADER turn: no canonical session yet, a present
|
|
10583
|
+
// native session id, a leader-shaped thread (absent or equal to the native session
|
|
10584
|
+
// id), and no subagent/typed-role provenance. Best-effort; never blocks PreToolUse.
|
|
10585
|
+
if (
|
|
10586
|
+
allowImplicitSessionSideEffects
|
|
10587
|
+
&& !canonicalSessionId
|
|
10588
|
+
&& nativeSessionId
|
|
10589
|
+
&& readPayloadAgentRole(payload) === ""
|
|
10590
|
+
&& !hasSubagentThreadSpawnProvenance(payload)
|
|
10591
|
+
) {
|
|
10592
|
+
const preToolUseLeaderThreadId = readPayloadThreadId(payload);
|
|
10593
|
+
if (
|
|
10594
|
+
preToolUseLeaderThreadId
|
|
10595
|
+
&& preToolUseLeaderThreadId === nativeSessionId
|
|
10596
|
+
&& !(await isThreadTrackedAsSubagent(cwd, nativeSessionId))
|
|
10597
|
+
) {
|
|
10598
|
+
try {
|
|
10599
|
+
const ownerOmxSessionId = await resolveVerifiedOwnerOmxSessionId();
|
|
10600
|
+
const sessionState = await reconcileNativeSessionStart(cwd, nativeSessionId, {
|
|
10601
|
+
context: pointerContext,
|
|
10602
|
+
pid: options.sessionOwnerPid ?? resolveSessionOwnerPid(payload),
|
|
10603
|
+
...(ownerOmxSessionId ? { ownerOmxSessionId, ownerAliasVerified: true } : {}),
|
|
10604
|
+
});
|
|
10605
|
+
const bootstrapSessionId = safeString(sessionState.session_id).trim();
|
|
10606
|
+
const bootstrapLeaderThreadId = safeString(sessionState.native_session_id).trim() || nativeSessionId;
|
|
10607
|
+
if (bootstrapSessionId && bootstrapLeaderThreadId) {
|
|
10608
|
+
canonicalSessionId = bootstrapSessionId;
|
|
10609
|
+
attestLeaderThread(cwd, {
|
|
10610
|
+
sessionId: bootstrapSessionId,
|
|
10611
|
+
leaderThreadId: bootstrapLeaderThreadId,
|
|
10612
|
+
source: 'native-pretooluse',
|
|
10613
|
+
});
|
|
10614
|
+
}
|
|
10615
|
+
} catch {
|
|
10616
|
+
// Best-effort bootstrap; PreToolUse must never fail on this.
|
|
10617
|
+
}
|
|
10618
|
+
}
|
|
10619
|
+
}
|
|
9892
10620
|
const payloadSessionId = readPayloadSessionId(payload);
|
|
9893
10621
|
const rootPointerConflict = await readLiveRootSessionPointerConflict(stateDir, payloadSessionId);
|
|
9894
10622
|
const preToolUseSessionId = payloadSessionId
|
|
9895
10623
|
? await resolveInternalSessionIdForPayload(cwd, payloadSessionId, stateDir)
|
|
9896
10624
|
: "";
|
|
9897
|
-
outputJson =
|
|
10625
|
+
outputJson = buildNativeUnknownRolePreToolUseOutput(payload)
|
|
10626
|
+
?? await buildDeepInterviewPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
|
|
9898
10627
|
?? await buildRalplanPreToolUseBoundaryOutput(payload, cwd, stateDir, preToolUseSessionId)
|
|
9899
10628
|
?? await buildPlanningRootPointerConflictPreToolUseOutput(payload, cwd, stateDir, rootPointerConflict)
|
|
9900
10629
|
?? await buildConductorPreToolUseWriteGuardOutput(payload, cwd, stateDir, preToolUseSessionId)
|
|
@@ -9902,17 +10631,34 @@ export async function dispatchCodexNativeHook(
|
|
|
9902
10631
|
?? buildMalformedPreToolUseBlockTestOutput(payload)
|
|
9903
10632
|
?? buildNativePreToolUseOutput(payload);
|
|
9904
10633
|
} else if (hookEventName === "PostToolUse") {
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
|
|
10634
|
+
if (allowImplicitSessionSideEffects) {
|
|
10635
|
+
await recordNativeSubagentCapacityBlocker(cwd, stateDir, payload).catch(() => {});
|
|
10636
|
+
await recordNativeSubagentSupportBlocker(cwd, stateDir, payload).catch(() => {});
|
|
10637
|
+
if (detectMcpTransportFailure(payload)) {
|
|
10638
|
+
await markTeamTransportFailure(cwd, payload);
|
|
10639
|
+
}
|
|
10640
|
+
await handleTeamWorkerPostToolUseSuccess(payload, cwd);
|
|
9909
10641
|
}
|
|
9910
10642
|
outputJson = buildNativePostToolUseOutput(payload);
|
|
9911
|
-
await handleTeamWorkerPostToolUseSuccess(payload, cwd);
|
|
9912
10643
|
} else if (hookEventName === "Stop") {
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
|
|
10644
|
+
if (allowImplicitSessionSideEffects) {
|
|
10645
|
+
outputJson = await buildStopHookOutput(payload, cwd, stateDir, {
|
|
10646
|
+
canonicalSessionId: canonicalSessionId || undefined,
|
|
10647
|
+
skipRalphStopBlock: isSubagentStop,
|
|
10648
|
+
skipAutoNudge: isSubagentStop,
|
|
10649
|
+
}) ?? await buildCompletedGoalCleanupStopOutput(payload, cwd);
|
|
10650
|
+
} else {
|
|
10651
|
+
const failure = stopAuthorizationFailure ?? {
|
|
10652
|
+
stopReason: "session_pointer_unusable",
|
|
10653
|
+
reason: "OMX cannot authorize Stop without a writable session authority.",
|
|
10654
|
+
};
|
|
10655
|
+
outputJson = {
|
|
10656
|
+
decision: "block",
|
|
10657
|
+
stopReason: failure.stopReason,
|
|
10658
|
+
reason: failure.reason,
|
|
10659
|
+
systemMessage: failure.reason,
|
|
10660
|
+
};
|
|
10661
|
+
}
|
|
9916
10662
|
}
|
|
9917
10663
|
|
|
9918
10664
|
return {
|