Created
July 30, 2026 07:36
-
-
Save arikon/a4afbc0cd2cdcf5f33b2509f3332bba8 to your computer and use it in GitHub Desktop.
OMX #3354 native Ultragoal receipt transport v3 with Codex CLI acceptance and session-race hardening
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/docs/codex-native-hooks.md b/docs/codex-native-hooks.md | |
| index 75f72bcc..e2932e93 100644 | |
| --- a/docs/codex-native-hooks.md | |
| +++ b/docs/codex-native-hooks.md | |
| @@ -101,6 +101,30 @@ uninspected script fail closed. For statically inspectable source, ordinary arra | |
| object reads and benign reflection do not become mutation authority through broad | |
| raw-command matching. | |
| +Codex Desktop may run two live user chats in the same workspace while the | |
| +compatibility `session.json` pointer names only one of them. For an exact | |
| +identityless payload with an exact `session_id`, the hook may recover Main-root | |
| +identity from the unique matching live rollout under | |
| +`$CODEX_HOME/sessions`. The first `session_meta` record must bind that exact id | |
| +and canonical cwd, declare `thread_source: "user"`, `source: "vscode"` or | |
| +`source: "exec"`, and | |
| +`originator: "Codex Desktop"`, and contain no subagent source. The rollout must | |
| +be a regular, unlinked, non-group/world-writable file with a bounded first | |
| +record. Identity and capability come from one pinned per-dispatch snapshot; | |
| +post-read device, inode, mode, link-count, size, mtime, and ctime must remain | |
| +unchanged. Conflicting snake/camel aliases, archived, ambiguous, malformed, | |
| +linked, writable, child, mutated, or spoofed rollouts fail closed and do not | |
| +weaken tracker or child-provenance checks. | |
| + | |
| +The hook payload may omit `thread_id`/`threadId`. If either alias is present, | |
| +every present alias must be a non-empty string and all values must reduce to the | |
| +exact root `session_id`; malformed, empty, null, numeric, or conflicting claims | |
| +fail closed. UserPromptSubmit and PreToolUse additionally require one valid | |
| +payload turn claim matching the latest complete `turn_context` record and its | |
| +canonical cwd. An unterminated final JSONL record invalidates turn authority and | |
| +typed-spawn capability instead of falling back to an older context. Stop remains | |
| +turn-independent only on the stale-dead, exact-cwd, session-scoped path. | |
| + | |
| Official Team worker roots may omit both `agent_id` and legacy `thread_id`. | |
| After Main-root exclusion, OMX preserves their established exemption only when | |
| the Team environment agrees with the durable worker identity, Team config, and | |
| @@ -120,8 +144,20 @@ while the Conductor boundary is active. | |
| Planning boundaries (`ralplan`, `deep-interview`) remain fail-closed for mutation | |
| transports: only their documented planning artifact paths and non-deactivating | |
| -state operations are allowed. No assignment-backed grant, prompt-derived scope, | |
| -or child-write allowance exists in this Option C implementation. | |
| +state operations are allowed. They do not accept an Ultragoal assignment-backed | |
| +grant, prompt-derived scope, or child-write allowance. | |
| + | |
| +Standalone Ultragoal on a native Codex surface outside tmux has three configured | |
| +backends: `off`, `host`, and `local-experimental`. `off` preserves the no-owner | |
| +activation block. `host` is the production-safe integration point, but it fails | |
| +closed because Codex does not currently expose a documented host-issued native | |
| +executor receipt API. `local-experimental` requires an ordered bootstrap: | |
| +typed executor spawn, root authorization through | |
| +`omx ultragoal native-bootstrap authorize`, then activation. The assignment is | |
| +a short-lived same-user guardrail, not host authentication or an ADR 3212 | |
| +hostile-child boundary. See | |
| +[`native-subagent-assignments.md`](native-subagent-assignments.md) for exact | |
| +configuration, lifecycle, reconciliation, and recovery behavior. | |
| ### Exact direct-cancel recovery exception | |
| diff --git a/docs/native-subagent-assignments.md b/docs/native-subagent-assignments.md | |
| new file mode 100644 | |
| index 00000000..64651a4b | |
| --- /dev/null | |
| +++ b/docs/native-subagent-assignments.md | |
| @@ -0,0 +1,253 @@ | |
| +# Native subagent assignments | |
| + | |
| +The Codex native hook keeps typed native children read-only unless the active | |
| +root Conductor establishes an exact, session-scoped assignment. This transport | |
| +does not grant Team-worker identity or authority. | |
| + | |
| +## Backends | |
| + | |
| +Configure the transport in `~/.omx/config.json`: | |
| + | |
| +```json | |
| +{ | |
| + "nativeUltragoal": { | |
| + "backend": "local-experimental", | |
| + "assignmentTtlSeconds": 900 | |
| + } | |
| +} | |
| +``` | |
| + | |
| +`assignmentTtlSeconds` accepts an integer from 30 through 3600 and defaults to | |
| +900. `OMX_NATIVE_ULTRAGOAL_BACKEND` overrides the configured backend. The | |
| +override is fail-closed: a present non-empty invalid value resolves to `off` | |
| +instead of falling back to a configured authority backend. The | |
| +legacy `OMX_NATIVE_SUBAGENT_ASSIGNMENTS=on` setting maps to | |
| +`local-experimental` only when neither the new environment override nor an | |
| +explicit configured backend is present. | |
| + | |
| +The available backends are: | |
| + | |
| +- `off` (default): native Ultragoal activation outside tmux remains blocked | |
| + because no authorized executor is available. | |
| +- `host`: reserved for a documented, unforgeable receipt issued by the Codex | |
| + host. OMX has no such host receipt API today, so this backend fails closed | |
| + with `OMX-ULTRAGOAL-HOST-RECEIPT-UNAVAILABLE`. | |
| +- `local-experimental`: enables short-lived local assignments after OMX checks | |
| + the native tracker. This is a bounded same-user workflow guardrail, not host | |
| + authentication and not an ADR 3212 hostile-child boundary. | |
| + | |
| +## Ordered bootstrap | |
| + | |
| +On Codex native surfaces outside tmux, a fresh standalone `$ultragoal` | |
| +activation stays inactive until the executor assignment is ready. Use this | |
| +order: | |
| + | |
| +1. Invoke `$ultragoal`. The hook verifies typed `spawn_agent` support, creates | |
| + a session bootstrap, and returns `OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED` without | |
| + activating Conductor mode. | |
| +2. Spawn one direct native child with `agent_type: "executor"`, instructing it | |
| + to wait without tool calls for a follow-up after activation. When the native | |
| + API returns only its stable `agent_path`, retain that exact path; OMX can | |
| + resolve the canonical child ID from the live rollout receipt. | |
| +3. Authorize only the required paths and actions: | |
| + | |
| + ```sh | |
| + omx ultragoal native-bootstrap authorize \ | |
| + --session-id <session-id> \ | |
| + --root-thread-id <root-thread-id> \ | |
| + --bootstrap-generation <generation-from-bootstrap-required> \ | |
| + --child-path /root/<typed-executor-agent-path> \ | |
| + --path-prefix <repo-relative-path> \ | |
| + --json | |
| + ``` | |
| + | |
| + Repeat `--path-prefix` for additional scopes. Add `--path-write-only` to | |
| + omit `bash-write`. Use `--ttl-seconds <30-3600>` only when the configured | |
| + TTL is unsuitable. Every `native-bootstrap` action requires an explicit | |
| + `--session-id` equal to the canonical current runtime session; pointer-only | |
| + or cross-session commands fail closed. `--bootstrap-generation` is the exact | |
| + generation printed by the bootstrap-required response; stale commands cannot | |
| + authorize a replacement generation. `--child-id` remains available when the | |
| + native API exposes the canonical ID directly. Exactly one of `--child-id` | |
| + and `--child-path` is accepted. | |
| +4. Retry the canonical Ultragoal activation. `PreToolUse` moves the receipt to | |
| + `activating`; only a successful activation `PostToolUse` publishes `active`. | |
| + The hook proceeds only after the | |
| + bootstrap generation, assignment, tracker session, root thread, exact child | |
| + ID, direct parent, role, status, lifetime, and path scope all validate. Then | |
| + start the authorized executor work with `followup_task` for the retained | |
| + agent path. | |
| + | |
| +The child remains read-only until activation is confirmed. A crash before the | |
| +activation tool runs leaves `ready`; a rejected or interrupted activation leaves | |
| +`activating`. Both are safe and retryable while the assignment and tracked | |
| +executor remain valid. If the receipt is revoked, expires, or loses tracker | |
| +authority before confirmation, the hook revokes the grant and terminalizes the | |
| +new Ultragoal state as `failed` instead of leaving Conductor active without an | |
| +executor. | |
| + | |
| +## Location | |
| + | |
| +The bootstrap for root session `<session-id>` is stored at: | |
| + | |
| +```text | |
| +.omx/state/sessions/<session-id>/native-ultragoal-bootstrap.json | |
| +``` | |
| + | |
| +The assignment for child `<child-agent-id>` is stored at: | |
| + | |
| +```text | |
| +.omx/state/sessions/<session-id>/native-assignments/<encodeURIComponent(child-agent-id)>.json | |
| +``` | |
| + | |
| +`omx ultragoal native-bootstrap authorize` is the supported writer. The native | |
| +hook rejects the canonical command when a tracked child invokes it and rejects | |
| +child writes to bootstrap or `native-assignments` paths, even when ordinary | |
| +path scope would otherwise match. This is still a same-user experimental | |
| +boundary: the CLI cannot authenticate an arbitrary local OS process as | |
| +Main-root without a host-issued receipt. | |
| + | |
| +## Active assignment schema | |
| + | |
| +The JSON object must contain exactly these keys: | |
| + | |
| +```json | |
| +{ | |
| + "schema_version": 1, | |
| + "kind": "native-subagent-assignment", | |
| + "lifecycle": "active", | |
| + "root_session_id": "exact-root-session-id", | |
| + "root_thread_id": "exact-root-thread-id", | |
| + "child_agent_id": "exact-canonical-child-agent-id", | |
| + "agent_type": "executor", | |
| + "allowed_actions": ["path-write", "bash-write"], | |
| + "path_prefixes": ["tmp/native-probe"], | |
| + "issued_at": "2026-07-23T20:00:00.000Z", | |
| + "expires_at": "2026-07-23T20:10:00.000Z", | |
| + "bootstrap_generation": "exact-bootstrap-generation" | |
| +} | |
| +``` | |
| + | |
| +The hook checks the root session and leader thread against the active session. | |
| +A directly attested child must be `kind: "subagent"`, carry `mode: "executor"`, | |
| +`provenance_kind: "native_subagent"`, and exact `direct_child_parent_id` / | |
| +`direct_child_root_id` bindings to Main-root. With the `local-experimental` | |
| +backend only, Codex App v2 may instead use the unique live child rollout | |
| +`session_meta` as assignment-local evidence when SessionStart did not persist a | |
| +direct-child attestation. The tracker must still contain the exact root session | |
| +and Main-root leader binding. The receipt must be under the live `sessions` | |
| +tree, fall within the current bootstrap lifetime without a future timestamp, | |
| +and bind the exact child, root session, direct parent, canonical cwd, depth `1`, | |
| +and `executor` role. | |
| +Archived, stale, ambiguous, writable, linked, malformed, or oversized receipts | |
| +fail closed. This fallback creates only a descriptive tracker entry; it does not | |
| +grant persisted reopen authority or synthesize child availability. | |
| + | |
| +`--child-path` is a receipt lookup transport, never an authority claim. OMX | |
| +scans only the live `CODEX_HOME/sessions` tree and requires one safe unique | |
| +rollout whose `session_meta` binds the exact path, canonical child UUID, | |
| +session/root alias, direct root parent, depth `1`, `executor` role, canonical | |
| +workspace cwd, and timestamp within the current bootstrap window. The rollout | |
| +filename must end in that same child UUID. Missing, duplicate, stale, foreign, | |
| +wrong-role, wrong-depth, wrong-cwd, group/other-writable, symlinked, or | |
| +hard-linked candidates fail closed. The resolved UUID is then passed through | |
| +the same assignment and activation validation as an explicit `--child-id`; | |
| +the path itself is never persisted as child authority. | |
| + | |
| +For the independent Codex Desktop root chat itself, the same safe live-rollout | |
| +boundary may also supply missing typed-spawn capability evidence from the latest | |
| +bounded `turn_context`. Its exact current `turn_id` and canonical cwd must match | |
| +the hook payload, with `multi_agent_mode: "explicitRequestOnly"` and | |
| +`multi_agent_version: "v2"`. `session_meta.dynamic_tools` is not capability | |
| +authority. All snake/camel aliases must reduce to one non-empty value, and the | |
| +same pinned per-dispatch transcript snapshot supplies both root identity and | |
| +capability. This fallback does not accept archived, ambiguous, or concurrently | |
| +mutated rollouts and never supplies child provenance. Session-scoped prompt | |
| +handling uses that exact root id, so an inactive terminal marker belonging to a | |
| +different live workspace chat is not reused by the bootstrap guard. | |
| + | |
| +Any tracker contradiction remains an unconditional veto: revoked reopen | |
| +authority, completion, a non-available explicit status, a non-`executor` role, | |
| +or foreign parent/root evidence cannot be repaired by transcript fallback. | |
| +The live tool payload must independently provide one unambiguous top-level | |
| +`executor` role through the native `agent_role`/`agent_type` aliases. | |
| +Nested-only role evidence or conflicting aliases cannot authorize an assignment. | |
| +The filename must encode that same canonical child ID, and | |
| +`bootstrap_generation` must match the current session bootstrap. | |
| + | |
| +Assignments are fail-closed when unreadable, malformed, expired, issued too far | |
| +in the future, longer than 24 hours, symlinked, hard-linked, larger than 64 KiB, | |
| +or writable by group/other. Unknown keys, duplicate actions or prefixes, | |
| +ambiguous paths, symlink traversal, `.omx`, and `.git` scopes are rejected. | |
| +Deleting the file or changing `lifecycle` away from `active` revokes the grant. | |
| + | |
| +## Lifecycle, revocation, and recovery | |
| + | |
| +Inspect and reconcile the current bootstrap with: | |
| + | |
| +```sh | |
| +omx ultragoal native-bootstrap status --session-id <session-id> --json | |
| +``` | |
| + | |
| +Status reconciliation returns readiness only while the exact assignment and | |
| +its direct tracker attestation or generation-bound live App receipt remain | |
| +valid. An expired assignment cannot authorize a mutation. Reconciliation | |
| +revokes an active assignment when the child evidence is missing, archived, | |
| +stale, completed, unavailable, foreign to the root, or has the wrong role. | |
| +Fresh activation also reconciles before it proceeds. | |
| + | |
| +All lifecycle mutations for one session are serialized by a session-scoped | |
| +lock. Activation revalidates tracker authority, generation, file identity, | |
| +schema, scope, and expiry under that lock and returns a positive receipt before | |
| +the hook permits state activation. Concurrent revoke cannot be overwritten by | |
| +a stale authorize or activation write. Revoked and expired generations require | |
| +an explicit new bootstrap before another authorization; authorization never | |
| +rotates a generation implicitly. An already-active receipt is idempotently | |
| +revalidated so a rejected or interrupted state write can be retried safely. | |
| + | |
| +Explicitly revoke the bootstrap and its assignment with: | |
| + | |
| +```sh | |
| +omx ultragoal native-bootstrap revoke \ | |
| + --session-id <session-id> \ | |
| + --child-id <child-agent-id> \ | |
| + --reason <reason> \ | |
| + --json | |
| +``` | |
| + | |
| +`--child-id` is optional when the bootstrap already records the child. After | |
| +revocation, expiry, or tracker invalidation, the child returns to the normal | |
| +read-only policy. Re-run `authorize` with the explicit current `--session-id`, | |
| +root thread, and a live strictly tracked executor to renew or replace the lease; | |
| +when standalone Ultragoal state is still active, the CLI restores bootstrap | |
| +status to `active` only after revalidation. Terminal `Stop`, exact root | |
| +cancellation, and a new fresh run revoke or rotate the old generation. An | |
| +assignment from an older generation cannot be reused. | |
| + | |
| +## Actions | |
| + | |
| +- `path-write`: direct file-edit tools within `path_prefixes`. | |
| +- `bash-write`: one statically analyzable shell mutation within | |
| + `path_prefixes`. | |
| + | |
| +Assignments never authorize VCS, PR, network, deployment, state, orchestration, | |
| +goal, assignment, or Team-worker operations. A `"."` prefix still excludes | |
| +`.git` and `.omx`. Shell substitution, | |
| +unresolved arithmetic, dynamic nested execution, unsafe heredocs, unsafe | |
| +nameref/allexport state, arbitrary PATH mutation, unresolved interpreter | |
| +writes, and path/symlink escapes remain blocked. | |
| + | |
| +Without a valid assignment, typed native children continue to receive | |
| +`OWNER_CONFIRMATION_REQUIRED`. | |
| + | |
| +## Root orchestration transports | |
| + | |
| +An active, session-bound Main-root Conductor may write the existing bounded | |
| +metadata roots, including `.omx/state`, `.omx/ultragoal`, `.omx/ralph`, | |
| +`.omx/team`, `.omx/mailbox`, `.omx/handoff`, `.omx/handoffs`, `.omx/goals`, | |
| +`.omx/notepad`, `.omx/wiki`, and `.beads`. | |
| + | |
| +The hook structurally recognizes canonical `omx state read`, `omx state write`, | |
| +`omx state clear`, and `omx ultragoal checkpoint` commands before generic shell | |
| +mutation analysis. Exact session/cwd/mode binding and all existing injection, | |
| +PATH, symlink, and protected-state-gate checks still apply. | |
| diff --git a/plugins/oh-my-codex/skills/code-review/SKILL.md b/plugins/oh-my-codex/skills/code-review/SKILL.md | |
| index 06bb78da..c28800e9 100644 | |
| --- a/plugins/oh-my-codex/skills/code-review/SKILL.md | |
| +++ b/plugins/oh-my-codex/skills/code-review/SKILL.md | |
| @@ -33,6 +33,7 @@ Delegates to the `code-reviewer` and `architect` agents in parallel for a two-la | |
| - **`architect` lane** - owns the devil's-advocate / design-tradeoff perspective | |
| - Both lanes run in parallel on a clean context with explicit scope and artifacts, and produce distinct outputs before final synthesis | |
| - If either lane cannot be launched or does not return evidence, report `independent review unavailable`; do **not** substitute the current/authoring lane, and do **not** approve or mark the review merge-ready. | |
| + - Inside an active Conductor workflow, collect evidence through native read-only tools. If a Bash fallback is necessary on macOS, use root-owned absolute readers such as `/bin/cat`, `/usr/bin/find ... -print`, `/usr/bin/sed` with a read-only program, or `/usr/bin/git --no-pager diff --no-ext-diff --no-textconv -- .`. Do not use a bare Homebrew/NVM command such as `rg`: a user-writable `PATH` entry cannot provide fail-closed executable identity. | |
| 3. **Review Categories** | |
| - **Security** - Hardcoded secrets, injection risks, XSS, CSRF | |
| @@ -98,6 +99,8 @@ Review code changes for quality, security, and maintainability. | |
| This is the code/spec/security lane. Do not absorb architectural ownership. | |
| +Conductor read-only constraint: prefer native read tools. If Bash is necessary on macOS, use only root-owned absolute readers such as `/bin/cat`, `/usr/bin/find ... -print`, read-only `/usr/bin/sed`, or `/usr/bin/git --no-pager diff --no-ext-diff --no-textconv -- .`. Never use bare/Homebrew `rg`, `nl`, unsafe Git forms, shell chaining, or memory lookup. If the leader supplied sufficient empty-diff evidence, review that evidence directly without extra tool calls. | |
| + | |
| Scope: [git diff or specific files] | |
| Review Checklist: | |
| @@ -121,6 +124,8 @@ task( | |
| Review the same code changes from the architecture/tradeoff perspective. | |
| +Conductor read-only constraint: prefer native read tools. If Bash is necessary on macOS, use only root-owned absolute readers such as `/bin/cat`, `/usr/bin/find ... -print`, read-only `/usr/bin/sed`, or `/usr/bin/git --no-pager diff --no-ext-diff --no-textconv -- .`. Never use bare/Homebrew `rg`, `nl`, unsafe Git forms, shell chaining, or memory lookup. If the leader supplied sufficient empty-diff evidence, review that evidence directly without extra tool calls. | |
| + | |
| Scope: [git diff or specific files] | |
| Focus: | |
| diff --git a/plugins/oh-my-codex/skills/ultragoal/SKILL.md b/plugins/oh-my-codex/skills/ultragoal/SKILL.md | |
| index 3a22a0ed..e7ea2443 100644 | |
| --- a/plugins/oh-my-codex/skills/ultragoal/SKILL.md | |
| +++ b/plugins/oh-my-codex/skills/ultragoal/SKILL.md | |
| @@ -22,8 +22,9 @@ Existing aggregate plans with the legacy enumerated objective are migrated to th | |
| Ultragoal is both a tracked workflow skill and the Autopilot durable-implementation child phase. Keep the phase/HUD contract explicit at workflow boundaries: | |
| -- **Standalone `$ultragoal` activation**: ensure `.omx/state[/sessions/<session>]/ultragoal-state.json` exists with `mode:"ultragoal"`, `active:true`, and a non-empty `current_phase` such as `planning` before or while goals are created. This state is a lightweight HUD/runtime declaration; `.omx/ultragoal/goals.json` and `ledger.jsonl` remain the durable goal source of truth. | |
| +- **Standalone `$ultragoal` activation**: route the write with `mode:"ultragoal"` and ensure `.omx/state[/sessions/<session>]/ultragoal-state.json` exists with `active:true` and a non-empty `current_phase` such as `planning` before or while goals are created. The state backend may omit the routing-only `mode` field from the persisted JSON; the canonical filename supplies that identity, while a present conflicting `mode` is invalid. This state is a lightweight HUD/runtime declaration; `.omx/ultragoal/goals.json` and `ledger.jsonl` remain the durable goal source of truth. | |
| - **During execution**: update `current_phase` to the smallest accurate phase (`planning`, `executing`, `verifying`, `reviewing`, `checkpointing`, or `blocked`) when the visible workflow phase changes. | |
| +- **Codex App outside tmux**: `nativeUltragoal.backend` defaults to `off`. `host` remains fail-closed until Codex exposes a documented host-issued native executor receipt API. `local-experimental` is an explicit same-user guardrail, not host authentication or an ADR 3212 hostile-child boundary. When typed `spawn_agent` support is advertised, keep Ultragoal inactive while Main-root follows the ordered bootstrap: invoke `$ultragoal`; spawn one exact typed `executor` instructed to wait without tool calls for a post-activation follow-up; retain its returned `agent_path`; run `omx ultragoal native-bootstrap authorize --session-id <session-id> --root-thread-id <root-thread-id> --bootstrap-generation <generation> --child-path <agent-path> --path-prefix <repo-relative-path> --json`; retry the canonical Ultragoal activation; then start the authorized work with `followup_task`. Use `--child-id` only when the host directly exposes the canonical child ID. Keep paths/actions minimal; use `omx ultragoal native-bootstrap status` to reconcile/recover and `omx ultragoal native-bootstrap revoke` when the executor is no longer needed. Follow `docs/native-subagent-assignments.md` for configuration and lifecycle details. | |
| - **Inside active Autopilot**: keep `mode:"autopilot"` active and set the supervised phase to `current_phase:"ultragoal"`; do not start a peer Autopilot replacement. Ultragoal's own mode state may still exist as child-phase detail, but Autopilot owns the parent phase. | |
| - **On handoff to code-review**: persist implementation/test/ledger evidence under Autopilot `handoff_artifacts.ultragoal`, then set Autopilot `current_phase:"code-review"`. | |
| - **On completion/blocker**: set standalone Ultragoal `active:false,current_phase:"complete"` only when all durable goals are complete; otherwise keep it active with a blocker/review-blocked phase and ledger evidence. | |
| diff --git a/skills/code-review/SKILL.md b/skills/code-review/SKILL.md | |
| index 06bb78da..c28800e9 100644 | |
| --- a/skills/code-review/SKILL.md | |
| +++ b/skills/code-review/SKILL.md | |
| @@ -33,6 +33,7 @@ Delegates to the `code-reviewer` and `architect` agents in parallel for a two-la | |
| - **`architect` lane** - owns the devil's-advocate / design-tradeoff perspective | |
| - Both lanes run in parallel on a clean context with explicit scope and artifacts, and produce distinct outputs before final synthesis | |
| - If either lane cannot be launched or does not return evidence, report `independent review unavailable`; do **not** substitute the current/authoring lane, and do **not** approve or mark the review merge-ready. | |
| + - Inside an active Conductor workflow, collect evidence through native read-only tools. If a Bash fallback is necessary on macOS, use root-owned absolute readers such as `/bin/cat`, `/usr/bin/find ... -print`, `/usr/bin/sed` with a read-only program, or `/usr/bin/git --no-pager diff --no-ext-diff --no-textconv -- .`. Do not use a bare Homebrew/NVM command such as `rg`: a user-writable `PATH` entry cannot provide fail-closed executable identity. | |
| 3. **Review Categories** | |
| - **Security** - Hardcoded secrets, injection risks, XSS, CSRF | |
| @@ -98,6 +99,8 @@ Review code changes for quality, security, and maintainability. | |
| This is the code/spec/security lane. Do not absorb architectural ownership. | |
| +Conductor read-only constraint: prefer native read tools. If Bash is necessary on macOS, use only root-owned absolute readers such as `/bin/cat`, `/usr/bin/find ... -print`, read-only `/usr/bin/sed`, or `/usr/bin/git --no-pager diff --no-ext-diff --no-textconv -- .`. Never use bare/Homebrew `rg`, `nl`, unsafe Git forms, shell chaining, or memory lookup. If the leader supplied sufficient empty-diff evidence, review that evidence directly without extra tool calls. | |
| + | |
| Scope: [git diff or specific files] | |
| Review Checklist: | |
| @@ -121,6 +124,8 @@ task( | |
| Review the same code changes from the architecture/tradeoff perspective. | |
| +Conductor read-only constraint: prefer native read tools. If Bash is necessary on macOS, use only root-owned absolute readers such as `/bin/cat`, `/usr/bin/find ... -print`, read-only `/usr/bin/sed`, or `/usr/bin/git --no-pager diff --no-ext-diff --no-textconv -- .`. Never use bare/Homebrew `rg`, `nl`, unsafe Git forms, shell chaining, or memory lookup. If the leader supplied sufficient empty-diff evidence, review that evidence directly without extra tool calls. | |
| + | |
| Scope: [git diff or specific files] | |
| Focus: | |
| diff --git a/skills/ultragoal/SKILL.md b/skills/ultragoal/SKILL.md | |
| index 3a22a0ed..e7ea2443 100644 | |
| --- a/skills/ultragoal/SKILL.md | |
| +++ b/skills/ultragoal/SKILL.md | |
| @@ -22,8 +22,9 @@ Existing aggregate plans with the legacy enumerated objective are migrated to th | |
| Ultragoal is both a tracked workflow skill and the Autopilot durable-implementation child phase. Keep the phase/HUD contract explicit at workflow boundaries: | |
| -- **Standalone `$ultragoal` activation**: ensure `.omx/state[/sessions/<session>]/ultragoal-state.json` exists with `mode:"ultragoal"`, `active:true`, and a non-empty `current_phase` such as `planning` before or while goals are created. This state is a lightweight HUD/runtime declaration; `.omx/ultragoal/goals.json` and `ledger.jsonl` remain the durable goal source of truth. | |
| +- **Standalone `$ultragoal` activation**: route the write with `mode:"ultragoal"` and ensure `.omx/state[/sessions/<session>]/ultragoal-state.json` exists with `active:true` and a non-empty `current_phase` such as `planning` before or while goals are created. The state backend may omit the routing-only `mode` field from the persisted JSON; the canonical filename supplies that identity, while a present conflicting `mode` is invalid. This state is a lightweight HUD/runtime declaration; `.omx/ultragoal/goals.json` and `ledger.jsonl` remain the durable goal source of truth. | |
| - **During execution**: update `current_phase` to the smallest accurate phase (`planning`, `executing`, `verifying`, `reviewing`, `checkpointing`, or `blocked`) when the visible workflow phase changes. | |
| +- **Codex App outside tmux**: `nativeUltragoal.backend` defaults to `off`. `host` remains fail-closed until Codex exposes a documented host-issued native executor receipt API. `local-experimental` is an explicit same-user guardrail, not host authentication or an ADR 3212 hostile-child boundary. When typed `spawn_agent` support is advertised, keep Ultragoal inactive while Main-root follows the ordered bootstrap: invoke `$ultragoal`; spawn one exact typed `executor` instructed to wait without tool calls for a post-activation follow-up; retain its returned `agent_path`; run `omx ultragoal native-bootstrap authorize --session-id <session-id> --root-thread-id <root-thread-id> --bootstrap-generation <generation> --child-path <agent-path> --path-prefix <repo-relative-path> --json`; retry the canonical Ultragoal activation; then start the authorized work with `followup_task`. Use `--child-id` only when the host directly exposes the canonical child ID. Keep paths/actions minimal; use `omx ultragoal native-bootstrap status` to reconcile/recover and `omx ultragoal native-bootstrap revoke` when the executor is no longer needed. Follow `docs/native-subagent-assignments.md` for configuration and lifecycle details. | |
| - **Inside active Autopilot**: keep `mode:"autopilot"` active and set the supervised phase to `current_phase:"ultragoal"`; do not start a peer Autopilot replacement. Ultragoal's own mode state may still exist as child-phase detail, but Autopilot owns the parent phase. | |
| - **On handoff to code-review**: persist implementation/test/ledger evidence under Autopilot `handoff_artifacts.ultragoal`, then set Autopilot `current_phase:"code-review"`. | |
| - **On completion/blocker**: set standalone Ultragoal `active:false,current_phase:"complete"` only when all durable goals are complete; otherwise keep it active with a blocker/review-blocked phase and ledger evidence. | |
| diff --git a/src/cli/__tests__/state.test.ts b/src/cli/__tests__/state.test.ts | |
| index 9cd8028d..b3cb8b08 100644 | |
| --- a/src/cli/__tests__/state.test.ts | |
| +++ b/src/cli/__tests__/state.test.ts | |
| @@ -1,10 +1,105 @@ | |
| import { describe, it } from 'node:test'; | |
| import assert from 'node:assert/strict'; | |
| -import { mkdtemp, rm, writeFile } from 'node:fs/promises'; | |
| +import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'; | |
| import { tmpdir } from 'node:os'; | |
| import { join } from 'node:path'; | |
| import { stateCommand } from '../state.js'; | |
| +import { __setWritableStateScopeTestHooksForTests } from '../../mcp/state-paths.js'; | |
| +import { recordNativeSubagentAuthorityObservation } from '../../subagents/tracker.js'; | |
| +import { | |
| + beginNativeUltragoalBootstrap, | |
| + findReadyLocalNativeExecutorAssignment, | |
| + issueLocalNativeExecutorAssignment, | |
| + markNativeUltragoalBootstrapActive, | |
| + NATIVE_ULTRAGOAL_BOOTSTRAP_FILE, | |
| + prepareNativeUltragoalBootstrapActivation, | |
| + readNativeUltragoalBootstrap, | |
| + revokeLocalNativeExecutorAssignment, | |
| +} from '../../ultragoal/native-bootstrap.js'; | |
| + | |
| +async function withNativeUltragoalActivationFixture<T>( | |
| + run: (fixture: { cwd: string; stateDir: string; sessionId: string; childAgentId: string }) => Promise<T>, | |
| + options: { activating?: boolean; pointerLiveness?: 'identity-indeterminate' | 'stale-dead' } = {}, | |
| +): Promise<T> { | |
| + const temporaryCwd = await mkdtemp(join(tmpdir(), 'omx-state-native-activation-')); | |
| + const previousCwd = process.cwd(); | |
| + const envNames = [ | |
| + 'CODEX_THREAD_ID', | |
| + 'CODEX_SESSION_ID', | |
| + 'SESSION_ID', | |
| + 'OMX_SESSION_ID', | |
| + 'OMX_TEAM_WORKER', | |
| + 'OMX_TEAM_INTERNAL_WORKER', | |
| + ] as const; | |
| + const previousEnv = Object.fromEntries(envNames.map((name) => [name, process.env[name]])); | |
| + const previousExitCode = process.exitCode; | |
| + try { | |
| + process.chdir(temporaryCwd); | |
| + const cwd = process.cwd(); | |
| + for (const name of envNames) delete process.env[name]; | |
| + const sessionId = 'native-activation-root'; | |
| + const childAgentId = 'native-activation-child'; | |
| + process.env.CODEX_THREAD_ID = sessionId; | |
| + const stateDir = join(cwd, '.omx', 'state'); | |
| + await mkdir(stateDir, { recursive: true }); | |
| + const bootstrap = await beginNativeUltragoalBootstrap({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId: sessionId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 300, | |
| + }); | |
| + recordNativeSubagentAuthorityObservation(cwd, { | |
| + sessionIds: [sessionId], | |
| + childThreadId: childAgentId, | |
| + parentThreadId: sessionId, | |
| + rootNativeSessionId: sessionId, | |
| + authorityEvidence: 'valid', | |
| + mode: 'executor', | |
| + }); | |
| + await issueLocalNativeExecutorAssignment({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId: sessionId, | |
| + childAgentId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ['src'], | |
| + ttlSeconds: 300, | |
| + }); | |
| + if (options.activating !== false) { | |
| + const prepared = await prepareNativeUltragoalBootstrapActivation({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + childAgentId, | |
| + }); | |
| + assert.equal(prepared.ok, true); | |
| + } | |
| + await writeFile(join(stateDir, 'session.json'), JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd, | |
| + state_root: stateDir, | |
| + ...(options.pointerLiveness === 'stale-dead' | |
| + ? { pid: 2_147_483_647 } | |
| + : { pid_start_ticks: 1 }), | |
| + })); | |
| + process.exitCode = undefined; | |
| + return await run({ cwd, stateDir, sessionId, childAgentId }); | |
| + } finally { | |
| + process.chdir(previousCwd); | |
| + process.exitCode = previousExitCode; | |
| + for (const name of envNames) { | |
| + const value = previousEnv[name]; | |
| + if (typeof value === 'string') process.env[name] = value; | |
| + else delete process.env[name]; | |
| + } | |
| + await rm(temporaryCwd, { recursive: true, force: true }); | |
| + } | |
| +} | |
| describe('stateCommand', () => { | |
| it('prints help for empty args', async () => { | |
| @@ -226,4 +321,355 @@ describe('stateCommand', () => { | |
| }, | |
| ); | |
| }); | |
| + | |
| + it('binds canonical native Ultragoal activation to exact indeterminate and stale root sessions', async () => { | |
| + for (const pointerLiveness of ['identity-indeterminate', 'stale-dead'] as const) { | |
| + await withNativeUltragoalActivationFixture(async ({ stateDir, sessionId }) => { | |
| + const stdout: string[] = []; | |
| + const stderr: string[] = []; | |
| + await stateCommand([ | |
| + 'write', | |
| + '--input', | |
| + JSON.stringify({ mode: 'ultragoal', active: true, current_phase: 'planning' }), | |
| + '--json', | |
| + ], { | |
| + stdout: (line) => stdout.push(line), | |
| + stderr: (line) => stderr.push(line), | |
| + }); | |
| + | |
| + assert.equal(process.exitCode, undefined); | |
| + assert.deepEqual(stderr, []); | |
| + assert.equal(stdout.length, 1); | |
| + const state = JSON.parse(await readFile( | |
| + join(stateDir, 'sessions', sessionId, 'ultragoal-state.json'), | |
| + 'utf-8', | |
| + )) as Record<string, unknown>; | |
| + assert.equal(state.active, true); | |
| + assert.equal(state.current_phase, 'planning'); | |
| + }, { pointerLiveness }); | |
| + } | |
| + }); | |
| + | |
| + it('allows an exact active-session planning to executing update after bootstrap activation', async () => { | |
| + await withNativeUltragoalActivationFixture(async ({ cwd, stateDir, sessionId, childAgentId }) => { | |
| + const write = async (payload: Record<string, unknown>): Promise<string[]> => { | |
| + process.exitCode = undefined; | |
| + const stderr: string[] = []; | |
| + await stateCommand(['write', '--input', JSON.stringify(payload), '--json'], { | |
| + stdout: () => undefined, | |
| + stderr: (line) => stderr.push(line), | |
| + }); | |
| + return stderr; | |
| + }; | |
| + | |
| + assert.deepEqual(await write({ | |
| + mode: 'ultragoal', | |
| + active: true, | |
| + current_phase: 'planning', | |
| + }), []); | |
| + assert.equal((await markNativeUltragoalBootstrapActive({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + childAgentId, | |
| + })).ok, true); | |
| + | |
| + assert.deepEqual(await write({ | |
| + mode: 'ultragoal', | |
| + active: true, | |
| + current_phase: 'executing', | |
| + session_id: sessionId, | |
| + }), []); | |
| + const state = JSON.parse(await readFile( | |
| + join(stateDir, 'sessions', sessionId, 'ultragoal-state.json'), | |
| + 'utf-8', | |
| + )) as Record<string, unknown>; | |
| + assert.equal(state.active, true); | |
| + assert.equal(state.current_phase, 'executing'); | |
| + | |
| + const foreignSessionId = 'foreign-active-update'; | |
| + const foreignError = await write({ | |
| + mode: 'ultragoal', | |
| + active: true, | |
| + current_phase: 'executing', | |
| + session_id: foreignSessionId, | |
| + }); | |
| + assert.equal(process.exitCode, 1); | |
| + assert.match(foreignError.join('\n'), /native_ultragoal_activation_receipt_invalid/); | |
| + await assert.rejects( | |
| + readFile(join(stateDir, 'sessions', foreignSessionId, 'ultragoal-state.json')), | |
| + /ENOENT/, | |
| + ); | |
| + }); | |
| + }); | |
| + | |
| + it('binds the exact terminal write to a completed aggregate checkpoint and revoked executor', async () => { | |
| + await withNativeUltragoalActivationFixture(async ({ cwd, stateDir, sessionId, childAgentId }) => { | |
| + const write = async (payload: Record<string, unknown>): Promise<string[]> => { | |
| + process.exitCode = undefined; | |
| + const stderr: string[] = []; | |
| + await stateCommand(['write', '--input', JSON.stringify(payload), '--json'], { | |
| + stdout: () => undefined, | |
| + stderr: (line) => stderr.push(line), | |
| + }); | |
| + return stderr; | |
| + }; | |
| + assert.deepEqual(await write({ mode: 'ultragoal', active: true, current_phase: 'planning' }), []); | |
| + assert.equal((await markNativeUltragoalBootstrapActive({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + childAgentId, | |
| + })).ok, true); | |
| + assert.deepEqual(await write({ mode: 'ultragoal', active: true, current_phase: 'checkpointing' }), []); | |
| + await mkdir(join(cwd, '.omx', 'ultragoal'), { recursive: true }); | |
| + await writeFile(join(cwd, '.omx', 'ultragoal', 'goals.json'), JSON.stringify({ | |
| + version: 1, | |
| + goals: [{ id: 'G001-terminal', status: 'complete' }], | |
| + aggregateCompletion: { status: 'complete', evidence: 'проверки пройдены' }, | |
| + })); | |
| + | |
| + const terminal = { mode: 'ultragoal', active: false, current_phase: 'complete' }; | |
| + assert.match((await write(terminal)).join('\n'), /native_ultragoal_terminal_receipt_invalid/); | |
| + await revokeLocalNativeExecutorAssignment({ stateDir, sessionId, reason: 'aggregate_complete' }); | |
| + assert.equal((await readNativeUltragoalBootstrap(stateDir, sessionId))?.status, 'revoked'); | |
| + assert.equal(await findReadyLocalNativeExecutorAssignment({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId: sessionId, | |
| + }), null); | |
| + let mutated = false; | |
| + __setWritableStateScopeTestHooksForTests({ | |
| + beforeScopeRevalidation: async () => { | |
| + if (mutated) return; | |
| + mutated = true; | |
| + await writeFile(join(cwd, '.omx', 'ultragoal', 'goals.json'), JSON.stringify({ | |
| + version: 1, | |
| + goals: [{ id: 'G001-terminal', status: 'in_progress' }], | |
| + aggregateCompletion: { status: 'complete' }, | |
| + })); | |
| + }, | |
| + }); | |
| + try { | |
| + assert.match((await write(terminal)).join('\n'), /native_ultragoal_terminal_receipt_invalid/); | |
| + assert.equal((JSON.parse(await readFile( | |
| + join(stateDir, 'sessions', sessionId, 'ultragoal-state.json'), | |
| + 'utf-8', | |
| + )) as Record<string, unknown>).current_phase, 'checkpointing'); | |
| + } finally { | |
| + __setWritableStateScopeTestHooksForTests({}); | |
| + } | |
| + await writeFile(join(cwd, '.omx', 'ultragoal', 'goals.json'), JSON.stringify({ | |
| + version: 1, | |
| + goals: [{ id: 'G001-terminal', status: 'complete' }], | |
| + aggregateCompletion: { status: 'complete', evidence: 'проверки пройдены' }, | |
| + })); | |
| + assert.deepEqual(await write(terminal), []); | |
| + const state = JSON.parse(await readFile( | |
| + join(stateDir, 'sessions', sessionId, 'ultragoal-state.json'), | |
| + 'utf-8', | |
| + )) as Record<string, unknown>; | |
| + assert.equal(state.active, false); | |
| + assert.equal(state.current_phase, 'complete'); | |
| + }); | |
| + }); | |
| + | |
| + it('does not authorize arbitrary state fields through the native activation fallback', async () => { | |
| + await withNativeUltragoalActivationFixture(async ({ stateDir, sessionId }) => { | |
| + const stderr: string[] = []; | |
| + await stateCommand([ | |
| + 'write', | |
| + '--input', | |
| + JSON.stringify({ | |
| + mode: 'ultragoal', | |
| + active: true, | |
| + current_phase: 'planning', | |
| + state: { arbitrary: true }, | |
| + }), | |
| + '--json', | |
| + ], { | |
| + stdout: () => undefined, | |
| + stderr: (line) => stderr.push(line), | |
| + }); | |
| + | |
| + assert.equal(process.exitCode, 1); | |
| + assert.match(stderr.join('\n'), /native_ultragoal_activation_receipt_invalid/); | |
| + await assert.rejects( | |
| + readFile(join(stateDir, 'sessions', sessionId, 'ultragoal-state.json')), | |
| + /ENOENT/, | |
| + ); | |
| + }); | |
| + }); | |
| + | |
| + it('requires an activating bootstrap with a valid ready assignment', async () => { | |
| + await withNativeUltragoalActivationFixture(async ({ stateDir, sessionId }) => { | |
| + const stderr: string[] = []; | |
| + await stateCommand([ | |
| + 'write', | |
| + '--input', | |
| + JSON.stringify({ mode: 'ultragoal', active: true, current_phase: 'planning' }), | |
| + '--json', | |
| + ], { | |
| + stdout: () => undefined, | |
| + stderr: (line) => stderr.push(line), | |
| + }); | |
| + | |
| + assert.equal(process.exitCode, 1); | |
| + assert.match(stderr.join('\n'), /native_ultragoal_activation_receipt_invalid/); | |
| + await assert.rejects( | |
| + readFile(join(stateDir, 'sessions', sessionId, 'ultragoal-state.json')), | |
| + /ENOENT/, | |
| + ); | |
| + }, { activating: false }); | |
| + }); | |
| + | |
| + it('rejects mismatched root-thread and Team-worker activation fallback identities', async () => { | |
| + for (const configure of [ | |
| + () => { process.env.CODEX_THREAD_ID = 'foreign-root-thread'; }, | |
| + () => { process.env.OMX_TEAM_WORKER = 'native-team/worker-1'; }, | |
| + ]) { | |
| + await withNativeUltragoalActivationFixture(async ({ stateDir, sessionId }) => { | |
| + configure(); | |
| + const stderr: string[] = []; | |
| + await stateCommand([ | |
| + 'write', | |
| + '--input', | |
| + JSON.stringify({ mode: 'ultragoal', active: true, current_phase: 'planning' }), | |
| + '--json', | |
| + ], { | |
| + stdout: () => undefined, | |
| + stderr: (line) => stderr.push(line), | |
| + }); | |
| + | |
| + assert.equal(process.exitCode, 1); | |
| + assert.match(stderr.join('\n'), /native_ultragoal_activation_receipt_invalid/); | |
| + await assert.rejects( | |
| + readFile(join(stateDir, 'sessions', sessionId, 'ultragoal-state.json')), | |
| + /ENOENT/, | |
| + ); | |
| + }); | |
| + } | |
| + }); | |
| + | |
| + it('never lets a foreign explicit session_id bypass native activation verification', async () => { | |
| + await withNativeUltragoalActivationFixture(async ({ stateDir }) => { | |
| + const foreignSessionId = 'foreign-explicit-session'; | |
| + const payloads = [ | |
| + { | |
| + mode: 'ultragoal', | |
| + active: true, | |
| + current_phase: 'planning', | |
| + session_id: foreignSessionId, | |
| + }, | |
| + { | |
| + mode: 'ultragoal', | |
| + active: true, | |
| + current_phase: 'planning', | |
| + session_id: foreignSessionId, | |
| + extra: 'bypass-attempt', | |
| + }, | |
| + { | |
| + mode: 'ultragoal', | |
| + session_id: foreignSessionId, | |
| + state: { active: true, current_phase: 'planning' }, | |
| + }, | |
| + { | |
| + mode: 'ultragoal', | |
| + active: true, | |
| + current_phase: 'executing', | |
| + session_id: foreignSessionId, | |
| + }, | |
| + { | |
| + mode: 'ultragoal', | |
| + session_id: foreignSessionId, | |
| + state: { active: true, currentPhase: 'executing' }, | |
| + }, | |
| + ]; | |
| + for (const payload of payloads) { | |
| + process.exitCode = undefined; | |
| + const stderr: string[] = []; | |
| + await stateCommand([ | |
| + 'write', | |
| + '--input', | |
| + JSON.stringify(payload), | |
| + '--json', | |
| + ], { | |
| + stdout: () => undefined, | |
| + stderr: (line) => stderr.push(line), | |
| + }); | |
| + | |
| + assert.equal(process.exitCode, 1); | |
| + assert.match(stderr.join('\n'), /native_ultragoal_activation_receipt_invalid/); | |
| + } | |
| + await assert.rejects( | |
| + readFile(join(stateDir, 'sessions', foreignSessionId, 'ultragoal-state.json')), | |
| + /ENOENT/, | |
| + ); | |
| + }); | |
| + }); | |
| + | |
| + it('preserves existing explicit-session behavior for non-activation writes', async () => { | |
| + const input = { | |
| + mode: 'ultragoal', | |
| + active: false, | |
| + current_phase: 'complete', | |
| + session_id: 'existing-explicit-session', | |
| + }; | |
| + let captured: Record<string, unknown> | undefined; | |
| + await stateCommand(['write', '--input', JSON.stringify(input), '--json'], { | |
| + stdout: () => undefined, | |
| + stderr: () => undefined, | |
| + execute: async (_operation, rawInput) => { | |
| + captured = rawInput; | |
| + return { payload: { success: true } }; | |
| + }, | |
| + }); | |
| + assert.deepEqual(captured, input); | |
| + }); | |
| + | |
| + it('revalidates revoke and expiry races under the native lifecycle lock before commit', async () => { | |
| + for (const mutation of ['revoke', 'expire'] as const) { | |
| + await withNativeUltragoalActivationFixture(async ({ stateDir, sessionId }) => { | |
| + const bootstrapPath = join( | |
| + stateDir, | |
| + 'sessions', | |
| + sessionId, | |
| + NATIVE_ULTRAGOAL_BOOTSTRAP_FILE, | |
| + ); | |
| + let mutated = false; | |
| + __setWritableStateScopeTestHooksForTests({ | |
| + beforeScopeRevalidation: async () => { | |
| + if (mutated) return; | |
| + mutated = true; | |
| + const bootstrap = JSON.parse(await readFile(bootstrapPath, 'utf-8')) as Record<string, unknown>; | |
| + await writeFile(bootstrapPath, JSON.stringify(mutation === 'revoke' | |
| + ? { ...bootstrap, status: 'revoked' } | |
| + : { ...bootstrap, expires_at: '2000-01-01T00:00:00.000Z' })); | |
| + }, | |
| + }); | |
| + try { | |
| + const stderr: string[] = []; | |
| + await stateCommand([ | |
| + 'write', | |
| + '--input', | |
| + JSON.stringify({ mode: 'ultragoal', active: true, current_phase: 'planning' }), | |
| + '--json', | |
| + ], { | |
| + stdout: () => undefined, | |
| + stderr: (line) => stderr.push(line), | |
| + }); | |
| + | |
| + assert.equal(process.exitCode, 1); | |
| + assert.match(stderr.join('\n'), /native_ultragoal_activation_receipt_invalid/); | |
| + await assert.rejects( | |
| + readFile(join(stateDir, 'sessions', sessionId, 'ultragoal-state.json')), | |
| + /ENOENT/, | |
| + ); | |
| + } finally { | |
| + __setWritableStateScopeTestHooksForTests({}); | |
| + } | |
| + }); | |
| + } | |
| + }); | |
| }); | |
| diff --git a/src/cli/__tests__/ultragoal.test.ts b/src/cli/__tests__/ultragoal.test.ts | |
| index 74c6d1f2..72cbb3a9 100644 | |
| --- a/src/cli/__tests__/ultragoal.test.ts | |
| +++ b/src/cli/__tests__/ultragoal.test.ts | |
| @@ -14,6 +14,10 @@ import { | |
| NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_FILE, | |
| writeRoleRoutingMarker, | |
| } from '../../subagents/role-routing-marker.js'; | |
| +import { | |
| + beginNativeUltragoalBootstrap, | |
| + nativeExecutorAssignmentPath, | |
| +} from '../../ultragoal/native-bootstrap.js'; | |
| import { ultragoalCommand, ULTRAGOAL_HELP } from '../ultragoal.js'; | |
| async function withCwd<T>(run: (cwd: string) => Promise<T>): Promise<T> { | |
| @@ -67,6 +71,33 @@ async function capture(run: () => Promise<void>): Promise<{ stdout: string[]; st | |
| } | |
| } | |
| +async function withNativeBootstrapEnv( | |
| + values: Partial<Record<'CODEX_HOME' | 'CODEX_THREAD_ID' | 'CODEX_SESSION_ID' | 'SESSION_ID' | 'OMX_SESSION_ID' | 'OMX_TEAM_WORKER' | 'OMX_TEAM_INTERNAL_WORKER', string>>, | |
| + run: () => Promise<void>, | |
| +): Promise<void> { | |
| + const names = [ | |
| + 'CODEX_HOME', | |
| + 'CODEX_THREAD_ID', | |
| + 'CODEX_SESSION_ID', | |
| + 'SESSION_ID', | |
| + 'OMX_SESSION_ID', | |
| + 'OMX_TEAM_WORKER', | |
| + 'OMX_TEAM_INTERNAL_WORKER', | |
| + ] as const; | |
| + const previous = Object.fromEntries(names.map((name) => [name, process.env[name]])); | |
| + try { | |
| + for (const name of names) delete process.env[name]; | |
| + for (const [name, value] of Object.entries(values)) process.env[name] = value; | |
| + await run(); | |
| + } finally { | |
| + for (const name of names) { | |
| + const value = previous[name]; | |
| + if (typeof value === 'string') process.env[name] = value; | |
| + else delete process.env[name]; | |
| + } | |
| + } | |
| +} | |
| + | |
| describe('cli/ultragoal', () => { | |
| it('does not create durable artifacts when OMX_SESSION_ID is unbound', async () => { | |
| await withCwd(async (cwd) => { | |
| @@ -144,6 +175,7 @@ describe('cli/ultragoal', () => { | |
| ['next'], | |
| ['start-next'], | |
| ['checkpoint', '--goal-id', 'G001-first', '--status', 'complete', '--evidence', 'worker evidence'], | |
| + ['native-bootstrap', 'revoke', '--session-id', 'worker-session'], | |
| ]; | |
| const envCases: Array<[string, string]> = [ | |
| ['OMX_TEAM_WORKER', 'display-team/worker-1'], | |
| @@ -214,6 +246,337 @@ describe('cli/ultragoal', () => { | |
| assert.match(ULTRAGOAL_HELP, /quality-gate-json/); | |
| assert.match(ULTRAGOAL_HELP, /ai-slop-cleaner/); | |
| assert.match(ULTRAGOAL_HELP, /code-review/); | |
| + assert.match(ULTRAGOAL_HELP, /native-bootstrap <authorize\|status\|revoke>/); | |
| + assert.match(ULTRAGOAL_HELP, /--child-id <id> \| --child-path <agent-path>/); | |
| + }); | |
| + | |
| + it('authorizes, reports, and revokes an experimental native executor bootstrap', async () => { | |
| + await withCwd(async (cwd) => { | |
| + const previousBackend = process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = 'local-experimental'; | |
| + const stateDir = join(cwd, '.omx', 'state'); | |
| + const sessionId = 'native-cli-session'; | |
| + const rootThreadId = 'native-cli-root'; | |
| + const childAgentId = '019faf84-1111-7000-8000-000000000001'; | |
| + const childAgentPath = '/root/native_cli_executor'; | |
| + const codexHome = join(cwd, 'codex-home'); | |
| + try { | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(join(stateDir, 'session.json'), `${JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: rootThreadId, | |
| + cwd, | |
| + })}\n`); | |
| + await writeFile(join(stateDir, 'subagent-tracking.json'), `${JSON.stringify({ | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + [sessionId]: { | |
| + session_id: sessionId, | |
| + leader_thread_id: rootThreadId, | |
| + updated_at: new Date().toISOString(), | |
| + threads: { | |
| + [rootThreadId]: { | |
| + thread_id: rootThreadId, | |
| + kind: 'leader', | |
| + first_seen_at: new Date().toISOString(), | |
| + last_seen_at: new Date().toISOString(), | |
| + turn_count: 1, | |
| + }, | |
| + [childAgentId]: { | |
| + thread_id: childAgentId, | |
| + kind: 'subagent', | |
| + first_seen_at: new Date().toISOString(), | |
| + last_seen_at: new Date().toISOString(), | |
| + turn_count: 1, | |
| + mode: 'executor', | |
| + status: 'available', | |
| + provenance_kind: 'native_subagent', | |
| + direct_child_parent_id: rootThreadId, | |
| + direct_child_root_id: rootThreadId, | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + }, null, 2)}\n`); | |
| + const initialBootstrap = await beginNativeUltragoalBootstrap({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 900, | |
| + }); | |
| + const rolloutDir = join(codexHome, 'sessions', '2026', '07', '29'); | |
| + await mkdir(rolloutDir, { recursive: true }); | |
| + await writeFile(join(rolloutDir, `rollout-test-${childAgentId}.jsonl`), `${JSON.stringify({ | |
| + timestamp: initialBootstrap.created_at, | |
| + type: 'session_meta', | |
| + payload: { | |
| + id: childAgentId, | |
| + session_id: sessionId, | |
| + cwd, | |
| + source: { | |
| + subagent: { | |
| + thread_spawn: { | |
| + parent_thread_id: rootThreadId, | |
| + depth: 1, | |
| + agent_path: childAgentPath, | |
| + agent_role: 'executor', | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + })}\n`); | |
| + | |
| + await withNativeBootstrapEnv({ CODEX_HOME: codexHome, CODEX_THREAD_ID: rootThreadId }, async () => { | |
| + const crossSession = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'status', '--session-id', 'foreign-session', '--json', | |
| + ])); | |
| + assert.equal(crossSession.exitCode, 1); | |
| + assert.match(crossSession.stderr.join('\n'), /does not match the canonical current runtime session/); | |
| + const omittedSession = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'status', '--json', | |
| + ])); | |
| + assert.equal(omittedSession.exitCode, 1); | |
| + assert.match(omittedSession.stderr.join('\n'), /require an explicit --session-id/); | |
| + | |
| + const missingChild = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'authorize', | |
| + '--session-id', sessionId, | |
| + '--root-thread-id', rootThreadId, | |
| + '--bootstrap-generation', initialBootstrap.generation, | |
| + '--path-prefix', 'src', | |
| + ])); | |
| + assert.equal(missingChild.exitCode, 1); | |
| + assert.match(missingChild.stderr.join('\n'), /exactly one of --child-id or --child-path/); | |
| + | |
| + const conflictingChild = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'authorize', | |
| + '--session-id', sessionId, | |
| + '--root-thread-id', rootThreadId, | |
| + '--child-id', childAgentId, | |
| + '--child-path', childAgentPath, | |
| + '--bootstrap-generation', initialBootstrap.generation, | |
| + '--path-prefix', 'src', | |
| + ])); | |
| + assert.equal(conflictingChild.exitCode, 1); | |
| + assert.match(conflictingChild.stderr.join('\n'), /exactly one of --child-id or --child-path/); | |
| + | |
| + const authorized = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'authorize', | |
| + '--session-id', sessionId, | |
| + '--root-thread-id', rootThreadId, | |
| + '--child-path', childAgentPath, | |
| + '--bootstrap-generation', initialBootstrap.generation, | |
| + '--path-prefix', 'src', | |
| + '--json', | |
| + ])); | |
| + assert.equal(authorized.exitCode, undefined, authorized.stderr.join('\n')); | |
| + assert.equal(JSON.parse(authorized.stdout.join('\n')).assignment.child_agent_id, childAgentId); | |
| + assert.equal(existsSync(nativeExecutorAssignmentPath(stateDir, sessionId, childAgentId)), true); | |
| + | |
| + const status = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'status', '--session-id', sessionId, '--json', | |
| + ])); | |
| + const statusPayload = JSON.parse(status.stdout.join('\n')) as { | |
| + bootstrap: { status: string }; | |
| + readiness: { ready: boolean }; | |
| + }; | |
| + assert.equal(statusPayload.bootstrap.status, 'ready'); | |
| + assert.equal(statusPayload.readiness.ready, true); | |
| + | |
| + const revoked = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'revoke', '--session-id', sessionId, '--json', | |
| + ])); | |
| + assert.equal(revoked.exitCode, undefined); | |
| + assert.equal(JSON.parse(revoked.stdout.join('\n')).status, 'revoked'); | |
| + | |
| + await mkdir(join(stateDir, 'sessions', sessionId), { recursive: true }); | |
| + await writeFile(join(stateDir, 'sessions', sessionId, 'ultragoal-state.json'), `${JSON.stringify({ | |
| + mode: 'ultragoal', | |
| + active: true, | |
| + current_phase: 'executing', | |
| + session_id: sessionId, | |
| + })}\n`); | |
| + const recoveryBootstrap = await beginNativeUltragoalBootstrap({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 900, | |
| + }); | |
| + const recovered = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'authorize', | |
| + '--session-id', sessionId, | |
| + '--root-thread-id', rootThreadId, | |
| + '--child-id', childAgentId, | |
| + '--bootstrap-generation', recoveryBootstrap.generation, | |
| + '--path-prefix', 'src', | |
| + '--json', | |
| + ])); | |
| + assert.equal(recovered.exitCode, undefined); | |
| + const recoveredStatus = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'status', '--session-id', sessionId, '--json', | |
| + ])); | |
| + assert.equal(JSON.parse(recoveredStatus.stdout.join('\n')).bootstrap.status, 'active'); | |
| + }); | |
| + } finally { | |
| + if (typeof previousBackend === 'string') process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = previousBackend; | |
| + else delete process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + } | |
| + }); | |
| + }); | |
| + | |
| + it('uses an exact CODEX_THREAD_ID binding for native-bootstrap with an identity-indeterminate pointer', async () => { | |
| + await withCwd(async () => { | |
| + const sessionId = 'native-bootstrap-indeterminate'; | |
| + const runtimeCwd = process.cwd(); | |
| + const stateDir = join(runtimeCwd, '.omx', 'state'); | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(join(stateDir, 'session.json'), `${JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd: runtimeCwd, | |
| + state_root: stateDir, | |
| + pid_start_ticks: 1, | |
| + })}\n`); | |
| + | |
| + await withNativeBootstrapEnv({ CODEX_THREAD_ID: sessionId }, async () => { | |
| + const result = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'status', '--session-id', sessionId, '--json', | |
| + ])); | |
| + | |
| + assert.equal(result.exitCode, undefined, result.stderr.join('\n')); | |
| + assert.equal(JSON.parse(result.stdout.join('\n')).bootstrap, null); | |
| + }); | |
| + }); | |
| + }); | |
| + | |
| + it('rejects the native-bootstrap fallback when CODEX_THREAD_ID does not match', async () => { | |
| + await withCwd(async () => { | |
| + const sessionId = 'native-bootstrap-thread-owner'; | |
| + const runtimeCwd = process.cwd(); | |
| + const stateDir = join(runtimeCwd, '.omx', 'state'); | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(join(stateDir, 'session.json'), `${JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd: runtimeCwd, | |
| + state_root: stateDir, | |
| + pid_start_ticks: 1, | |
| + })}\n`); | |
| + | |
| + await withNativeBootstrapEnv({ CODEX_THREAD_ID: 'native-bootstrap-child-thread' }, async () => { | |
| + const result = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'status', '--session-id', sessionId, '--json', | |
| + ])); | |
| + | |
| + assert.equal(result.exitCode, 1); | |
| + assert.match(result.stderr.join('\n'), /canonical current runtime session/); | |
| + }); | |
| + }); | |
| + }); | |
| + | |
| + it('rejects the native-bootstrap fallback when CODEX_THREAD_ID is absent', async () => { | |
| + await withCwd(async () => { | |
| + const sessionId = 'native-bootstrap-missing-thread'; | |
| + const runtimeCwd = process.cwd(); | |
| + const stateDir = join(runtimeCwd, '.omx', 'state'); | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(join(stateDir, 'session.json'), `${JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd: runtimeCwd, | |
| + state_root: stateDir, | |
| + pid_start_ticks: 1, | |
| + })}\n`); | |
| + | |
| + await withNativeBootstrapEnv({}, async () => { | |
| + const result = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'status', '--session-id', sessionId, '--json', | |
| + ])); | |
| + | |
| + assert.equal(result.exitCode, 1); | |
| + assert.match(result.stderr.join('\n'), /canonical current runtime session/); | |
| + }); | |
| + }); | |
| + }); | |
| + | |
| + it('rejects the native-bootstrap fallback when native_session_id does not match', async () => { | |
| + await withCwd(async () => { | |
| + const sessionId = 'native-bootstrap-native-owner'; | |
| + const runtimeCwd = process.cwd(); | |
| + const stateDir = join(runtimeCwd, '.omx', 'state'); | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(join(stateDir, 'session.json'), `${JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: 'foreign-native-session', | |
| + cwd: runtimeCwd, | |
| + state_root: stateDir, | |
| + pid_start_ticks: 1, | |
| + })}\n`); | |
| + | |
| + await withNativeBootstrapEnv({ CODEX_THREAD_ID: sessionId }, async () => { | |
| + const result = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'status', '--session-id', sessionId, '--json', | |
| + ])); | |
| + | |
| + assert.equal(result.exitCode, 1); | |
| + assert.match(result.stderr.join('\n'), /canonical current runtime session/); | |
| + }); | |
| + }); | |
| + }); | |
| + | |
| + it('rejects the native-bootstrap fallback when the pointer cwd does not match exactly', async () => { | |
| + await withCwd(async () => { | |
| + const sessionId = 'native-bootstrap-cwd-owner'; | |
| + const runtimeCwd = process.cwd(); | |
| + const stateDir = join(runtimeCwd, '.omx', 'state'); | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(join(stateDir, 'session.json'), `${JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd: join(runtimeCwd, 'nested'), | |
| + state_root: stateDir, | |
| + })}\n`); | |
| + | |
| + await withNativeBootstrapEnv({ CODEX_THREAD_ID: sessionId }, async () => { | |
| + const result = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'status', '--session-id', sessionId, '--json', | |
| + ])); | |
| + | |
| + assert.equal(result.exitCode, 1); | |
| + assert.match(result.stderr.join('\n'), /canonical current runtime session/); | |
| + }); | |
| + }); | |
| + }); | |
| + | |
| + it('rejects the native-bootstrap fallback from a Team worker identity', async () => { | |
| + await withCwd(async () => { | |
| + const sessionId = 'native-bootstrap-team-owner'; | |
| + const runtimeCwd = process.cwd(); | |
| + const stateDir = join(runtimeCwd, '.omx', 'state'); | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(join(stateDir, 'session.json'), `${JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd: runtimeCwd, | |
| + state_root: stateDir, | |
| + })}\n`); | |
| + | |
| + await withNativeBootstrapEnv({ | |
| + CODEX_THREAD_ID: sessionId, | |
| + OMX_TEAM_WORKER: 'native-team/worker-1', | |
| + }, async () => { | |
| + const result = await capture(() => ultragoalCommand([ | |
| + 'native-bootstrap', 'status', '--session-id', sessionId, '--json', | |
| + ])); | |
| + | |
| + assert.equal(result.exitCode, 1); | |
| + assert.match(result.stderr.join('\n'), /leader-owned/i); | |
| + }); | |
| + }); | |
| }); | |
| it('creates and starts goals through the command surface', async () => { | |
| diff --git a/src/cli/state.ts b/src/cli/state.ts | |
| index 79298ade..508f0d11 100644 | |
| --- a/src/cli/state.ts | |
| +++ b/src/cli/state.ts | |
| @@ -1,6 +1,16 @@ | |
| -import { readFile } from 'node:fs/promises'; | |
| +import { constants as fsConstants } from 'node:fs'; | |
| +import { lstat, open, readFile } from 'node:fs/promises'; | |
| +import { join } from 'node:path'; | |
| +import { classifySessionStateLiveness, isSessionStateAuthoritativeForCwd, type SessionState } from '../hooks/session.js'; | |
| +import { getBaseStateDirWithSource, normalizeSessionId, resolveWorkingDirectoryForState } from '../mcp/state-paths.js'; | |
| import { executeStateOperation, type StateOperationName } from '../state/operations.js'; | |
| +import { | |
| + findReadyLocalNativeExecutorAssignment, | |
| + readNativeUltragoalBootstrap, | |
| + withNativeUltragoalActivationAuthorization, | |
| +} from '../ultragoal/native-bootstrap.js'; | |
| +import { sameFilePath } from '../utils/paths.js'; | |
| const STATE_HELP = `Usage: omx state <read|write|clear|list-active|get-status> [--input <json> | --input-file <path>] [--mode <mode>] [--json] | |
| @@ -64,6 +74,242 @@ function parseStateInputJson( | |
| return { ...(parsed as Record<string, unknown>) }; | |
| } | |
| +async function readSafeSelectedSessionPointer(path: string): Promise<SessionState | null> { | |
| + const metadata = await lstat(path).catch(() => null); | |
| + if ( | |
| + !metadata | |
| + || !metadata.isFile() | |
| + || metadata.isSymbolicLink() | |
| + || metadata.nlink !== 1 | |
| + || (metadata.mode & 0o022) !== 0 | |
| + || metadata.size > 64 * 1024 | |
| + ) return null; | |
| + | |
| + const handle = await open(path, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0)).catch(() => null); | |
| + if (!handle) return null; | |
| + try { | |
| + const openedMetadata = await handle.stat(); | |
| + if ( | |
| + !openedMetadata.isFile() | |
| + || openedMetadata.dev !== metadata.dev | |
| + || openedMetadata.ino !== metadata.ino | |
| + || openedMetadata.nlink !== 1 | |
| + || (openedMetadata.mode & 0o022) !== 0 | |
| + || openedMetadata.size > 64 * 1024 | |
| + ) return null; | |
| + const contents = await handle.readFile('utf-8'); | |
| + const finalMetadata = await handle.stat(); | |
| + if ( | |
| + finalMetadata.dev !== openedMetadata.dev | |
| + || finalMetadata.ino !== openedMetadata.ino | |
| + || finalMetadata.nlink !== 1 | |
| + || (finalMetadata.mode & 0o022) !== 0 | |
| + || finalMetadata.size !== openedMetadata.size | |
| + || finalMetadata.mtimeMs !== openedMetadata.mtimeMs | |
| + || finalMetadata.ctimeMs !== openedMetadata.ctimeMs | |
| + ) return null; | |
| + const parsed = JSON.parse(contents) as unknown; | |
| + return parsed && typeof parsed === 'object' && !Array.isArray(parsed) | |
| + ? parsed as SessionState | |
| + : null; | |
| + } catch { | |
| + return null; | |
| + } finally { | |
| + await handle.close().catch(() => {}); | |
| + } | |
| +} | |
| + | |
| +async function readSafeJsonRecord(path: string): Promise<Record<string, unknown> | null> { | |
| + const value = await readSafeSelectedSessionPointer(path); | |
| + return value as Record<string, unknown> | null; | |
| +} | |
| + | |
| +function isCanonicalNativeUltragoalActivation(input: Record<string, unknown>): boolean { | |
| + const keys = Object.keys(input).sort().join(','); | |
| + return (keys === 'active,current_phase,mode' || keys === 'active,current_phase,mode,session_id') | |
| + && typeof input.current_phase === 'string' | |
| + && isNativeUltragoalActivationIntent(input); | |
| +} | |
| + | |
| +function isNativeUltragoalActivationIntent(input: Record<string, unknown>): boolean { | |
| + const nested = input.state && typeof input.state === 'object' && !Array.isArray(input.state) | |
| + ? input.state as Record<string, unknown> | |
| + : {}; | |
| + const active = Object.prototype.hasOwnProperty.call(nested, 'active') | |
| + ? nested.active | |
| + : input.active; | |
| + const phase = nested.current_phase | |
| + ?? nested.currentPhase | |
| + ?? input.current_phase | |
| + ?? input.currentPhase; | |
| + const normalizedPhase = typeof phase === 'string' ? phase.trim().toLowerCase() : ''; | |
| + return input.mode === 'ultragoal' | |
| + && active === true | |
| + && normalizedPhase !== '' | |
| + && !['complete', 'completed', 'cancelled', 'canceled', 'failed', 'cleared'].includes(normalizedPhase); | |
| +} | |
| + | |
| +function isCanonicalNativeUltragoalTerminalIntent(input: Record<string, unknown>): boolean { | |
| + const keys = Object.keys(input).sort().join(','); | |
| + return keys === 'active,current_phase,mode' | |
| + && input.mode === 'ultragoal' | |
| + && input.active === false | |
| + && input.current_phase === 'complete'; | |
| +} | |
| + | |
| +function hasTeamWorkerIdentity(): boolean { | |
| + return Boolean(process.env.OMX_TEAM_WORKER?.trim() || process.env.OMX_TEAM_INTERNAL_WORKER?.trim()); | |
| +} | |
| + | |
| +async function sessionHasActiveUltragoalState( | |
| + stateDir: string, | |
| + sessionId: string, | |
| +): Promise<boolean> { | |
| + try { | |
| + const state = JSON.parse(await readFile( | |
| + join(stateDir, 'sessions', sessionId, 'ultragoal-state.json'), | |
| + 'utf-8', | |
| + )) as Record<string, unknown>; | |
| + const phase = typeof state.current_phase === 'string' ? state.current_phase.trim().toLowerCase() : ''; | |
| + return state.active === true | |
| + && phase !== '' | |
| + && !['complete', 'completed', 'cancelled', 'canceled', 'failed', 'cleared'].includes(phase); | |
| + } catch { | |
| + return false; | |
| + } | |
| +} | |
| + | |
| +async function resolveNativeUltragoalActivationSession( | |
| + input: Record<string, unknown>, | |
| +): Promise<{ | |
| + cwd: string; | |
| + stateDir: string; | |
| + sessionId: string; | |
| + childAgentId: string; | |
| + bootstrapStatus: 'activating' | 'active'; | |
| +} | null> { | |
| + if (!isCanonicalNativeUltragoalActivation(input) || hasTeamWorkerIdentity()) return null; | |
| + const threadId = process.env.CODEX_THREAD_ID; | |
| + if (!threadId || normalizeSessionId(threadId) !== threadId) return null; | |
| + if ( | |
| + Object.prototype.hasOwnProperty.call(input, 'session_id') | |
| + && input.session_id !== threadId | |
| + ) return null; | |
| + | |
| + const cwd = resolveWorkingDirectoryForState(); | |
| + const { baseStateDir } = getBaseStateDirWithSource(cwd); | |
| + const pointer = await readSafeSelectedSessionPointer(join(baseStateDir, 'session.json')); | |
| + if ( | |
| + !pointer | |
| + || pointer.session_id !== threadId | |
| + || normalizeSessionId(pointer.session_id) !== threadId | |
| + || pointer.native_session_id !== threadId | |
| + || normalizeSessionId(pointer.native_session_id) !== threadId | |
| + || !isSessionStateAuthoritativeForCwd(pointer, cwd) | |
| + || !['identity-indeterminate', 'stale-dead'].includes(classifySessionStateLiveness(pointer)) | |
| + ) return null; | |
| + const pointerStateRoot = typeof pointer.state_root === 'string' | |
| + ? pointer.state_root | |
| + : join(cwd, '.omx', 'state'); | |
| + if (!sameFilePath(pointerStateRoot, baseStateDir)) return null; | |
| + | |
| + const existingActiveState = await sessionHasActiveUltragoalState(baseStateDir, threadId); | |
| + const bootstrapStatus = existingActiveState ? 'active' : 'activating'; | |
| + if (bootstrapStatus === 'activating' && input.current_phase !== 'planning') return null; | |
| + const bootstrapBefore = await readNativeUltragoalBootstrap(baseStateDir, threadId); | |
| + if ( | |
| + !bootstrapBefore | |
| + || bootstrapBefore.status !== bootstrapStatus | |
| + || bootstrapBefore.session_id !== threadId | |
| + || bootstrapBefore.root_thread_id !== threadId | |
| + || !bootstrapBefore.child_agent_id | |
| + ) return null; | |
| + const assignment = await findReadyLocalNativeExecutorAssignment({ | |
| + cwd, | |
| + stateDir: baseStateDir, | |
| + sessionId: threadId, | |
| + rootThreadId: threadId, | |
| + }); | |
| + if (!assignment || assignment.child_agent_id !== bootstrapBefore.child_agent_id) return null; | |
| + const bootstrapAfter = await readNativeUltragoalBootstrap(baseStateDir, threadId); | |
| + if ( | |
| + !bootstrapAfter | |
| + || bootstrapAfter.status !== bootstrapStatus | |
| + || bootstrapAfter.generation !== bootstrapBefore.generation | |
| + || bootstrapAfter.child_agent_id !== bootstrapBefore.child_agent_id | |
| + ) return null; | |
| + return { | |
| + cwd, | |
| + stateDir: baseStateDir, | |
| + sessionId: threadId, | |
| + childAgentId: assignment.child_agent_id, | |
| + bootstrapStatus, | |
| + }; | |
| +} | |
| + | |
| +async function resolveNativeUltragoalTerminalSession( | |
| + input: Record<string, unknown>, | |
| + allowPersistedTerminal = false, | |
| +): Promise<{ sessionId: string } | null> { | |
| + if (!isCanonicalNativeUltragoalTerminalIntent(input) || hasTeamWorkerIdentity()) return null; | |
| + const threadId = process.env.CODEX_THREAD_ID; | |
| + if (!threadId || normalizeSessionId(threadId) !== threadId) return null; | |
| + | |
| + const cwd = resolveWorkingDirectoryForState(); | |
| + const { baseStateDir } = getBaseStateDirWithSource(cwd); | |
| + const pointer = await readSafeSelectedSessionPointer(join(baseStateDir, 'session.json')); | |
| + if ( | |
| + !pointer | |
| + || pointer.session_id !== threadId | |
| + || pointer.native_session_id !== threadId | |
| + || !isSessionStateAuthoritativeForCwd(pointer, cwd) | |
| + || !['usable', 'identity-indeterminate'].includes(classifySessionStateLiveness(pointer)) | |
| + ) return null; | |
| + const pointerStateRoot = typeof pointer.state_root === 'string' | |
| + ? pointer.state_root | |
| + : join(cwd, '.omx', 'state'); | |
| + if (!sameFilePath(pointerStateRoot, baseStateDir)) return null; | |
| + | |
| + const state = await readSafeJsonRecord(join(baseStateDir, 'sessions', threadId, 'ultragoal-state.json')); | |
| + const persistedStateIsAuthorized = Boolean( | |
| + state | |
| + && ( | |
| + (state.active === true && state.current_phase === 'checkpointing') | |
| + || (allowPersistedTerminal && state.active === false && state.current_phase === 'complete') | |
| + ) | |
| + && (typeof state.session_id !== 'string' || state.session_id === threadId), | |
| + ); | |
| + if (!persistedStateIsAuthorized) return null; | |
| + const plan = await readSafeJsonRecord(join(cwd, '.omx', 'ultragoal', 'goals.json')); | |
| + const goals = Array.isArray(plan?.goals) ? plan.goals : []; | |
| + const aggregateCompletion = plan?.aggregateCompletion && typeof plan.aggregateCompletion === 'object' | |
| + && !Array.isArray(plan.aggregateCompletion) | |
| + ? plan.aggregateCompletion as Record<string, unknown> | |
| + : null; | |
| + if ( | |
| + goals.length === 0 | |
| + || goals.some((goal) => !goal || typeof goal !== 'object' || Array.isArray(goal) | |
| + || (goal as Record<string, unknown>).status !== 'complete') | |
| + || aggregateCompletion?.status !== 'complete' | |
| + ) return null; | |
| + | |
| + const bootstrap = await readNativeUltragoalBootstrap(baseStateDir, threadId); | |
| + if ( | |
| + !bootstrap | |
| + || bootstrap.status !== 'revoked' | |
| + || bootstrap.session_id !== threadId | |
| + || bootstrap.root_thread_id !== threadId | |
| + || !bootstrap.child_agent_id | |
| + ) return null; | |
| + const assignment = await findReadyLocalNativeExecutorAssignment({ | |
| + cwd, | |
| + stateDir: baseStateDir, | |
| + sessionId: threadId, | |
| + rootThreadId: threadId, | |
| + }); | |
| + return assignment ? null : { sessionId: threadId }; | |
| +} | |
| + | |
| export async function stateCommand( | |
| args: string[], | |
| deps: StateCommandDependencies = {}, | |
| @@ -164,7 +410,57 @@ export async function stateCommand( | |
| input = { ...input, mode: modeValue }; | |
| } | |
| - const result = await execute(operation, input); | |
| + let result; | |
| + if (operation === 'state_write' && isNativeUltragoalActivationIntent(input)) { | |
| + const authorization = await resolveNativeUltragoalActivationSession(input); | |
| + if (!authorization) { | |
| + result = { | |
| + payload: { error: 'native_ultragoal_activation_receipt_invalid' }, | |
| + isError: true, | |
| + }; | |
| + } else { | |
| + const activation = await withNativeUltragoalActivationAuthorization( | |
| + authorization, | |
| + async (assertNativeAuthorized) => { | |
| + const assertAuthorized = async (): Promise<void> => { | |
| + await assertNativeAuthorized(); | |
| + if ( | |
| + authorization.bootstrapStatus === 'active' | |
| + && !await sessionHasActiveUltragoalState(authorization.stateDir, authorization.sessionId) | |
| + ) throw new Error('native_ultragoal_activation_receipt_invalid'); | |
| + }; | |
| + await assertAuthorized(); | |
| + return execute(operation, { | |
| + ...input, | |
| + session_id: authorization.sessionId, | |
| + }, { | |
| + beforeStateWriteCommit: assertAuthorized, | |
| + }); | |
| + }, | |
| + ); | |
| + result = activation.ok | |
| + ? activation.value | |
| + : { | |
| + payload: { error: 'native_ultragoal_activation_receipt_invalid' }, | |
| + isError: true, | |
| + }; | |
| + } | |
| + } else if (operation === 'state_write' && isCanonicalNativeUltragoalTerminalIntent(input)) { | |
| + const authorization = await resolveNativeUltragoalTerminalSession(input); | |
| + let terminalStateCommitObserved = false; | |
| + result = authorization | |
| + ? await execute(operation, { ...input, session_id: authorization.sessionId }, { | |
| + beforeStateWriteCommit: async () => { | |
| + if (!await resolveNativeUltragoalTerminalSession(input, terminalStateCommitObserved)) { | |
| + throw new Error('native_ultragoal_terminal_receipt_invalid'); | |
| + } | |
| + terminalStateCommitObserved = true; | |
| + }, | |
| + }) | |
| + : { payload: { error: 'native_ultragoal_terminal_receipt_invalid' }, isError: true }; | |
| + } else { | |
| + result = await execute(operation, input); | |
| + } | |
| const body = JSON.stringify(result.payload, null, json ? 0 : 2); | |
| if (result.isError) { | |
| diff --git a/src/cli/ultragoal.ts b/src/cli/ultragoal.ts | |
| index 64d2e356..38b034a2 100644 | |
| --- a/src/cli/ultragoal.ts | |
| +++ b/src/cli/ultragoal.ts | |
| @@ -11,8 +11,11 @@ import { | |
| NATIVE_SUBAGENT_SUPPORT_BLOCKER_FILE, | |
| resolveNativeSubagentSupportStatus, | |
| } from '../leader/contract.js'; | |
| -import { resolveRuntimeStateScope } from '../mcp/state-paths.js'; | |
| +import { isSessionStateAuthoritativeForCwd, type SessionState } from '../hooks/session.js'; | |
| +import { normalizeSessionId, resolveRuntimeStateScope } from '../mcp/state-paths.js'; | |
| +import { resolveNativeUltragoalConfig } from '../config/native-ultragoal.js'; | |
| import { readRoleRoutingMarker } from '../subagents/role-routing-marker.js'; | |
| +import { sameFilePath } from '../utils/paths.js'; | |
| import { | |
| addUltragoalGoal, | |
| buildCodexGoalInstruction, | |
| @@ -33,6 +36,32 @@ import { | |
| ULTRAGOAL_STEERING_SOURCES, | |
| UltragoalError, | |
| } from '../ultragoal/artifacts.js'; | |
| +import { | |
| + issueLocalNativeExecutorAssignment, | |
| + markNativeUltragoalBootstrapActive, | |
| + prepareNativeUltragoalBootstrapActivation, | |
| + readNativeUltragoalBootstrap, | |
| + reconcileLocalNativeExecutorAssignments, | |
| + resolveLocalNativeExecutorChildReceipt, | |
| + revokeLocalNativeExecutorAssignment, | |
| +} from '../ultragoal/native-bootstrap.js'; | |
| + | |
| +async function sessionHasActiveStandaloneUltragoal(stateDir: string, sessionId: string): Promise<boolean> { | |
| + try { | |
| + const parsed = JSON.parse(await readFile( | |
| + join(stateDir, 'sessions', sessionId, 'ultragoal-state.json'), | |
| + 'utf-8', | |
| + )) as Record<string, unknown>; | |
| + const phase = String(parsed.current_phase ?? parsed.currentPhase ?? '').trim().toLowerCase(); | |
| + return parsed.mode === 'ultragoal' | |
| + && parsed.active === true | |
| + && parsed.session_id === sessionId | |
| + && phase !== '' | |
| + && !['complete', 'completed', 'cancelled', 'failed'].includes(phase); | |
| + } catch { | |
| + return false; | |
| + } | |
| +} | |
| export const ULTRAGOAL_HELP = `omx ultragoal - Durable repo-native multi-goal workflow over Codex goal mode | |
| @@ -46,6 +75,8 @@ Usage: | |
| omx ultragoal steer --directive-json <json-or-path> [--json] | |
| omx ultragoal checkpoint --goal-id <id> --status <complete|failed|blocked> [--evidence <text>] [--codex-goal-json <json-or-path>] [--quality-gate-json <json-or-path>] [--json] | |
| omx ultragoal status [--codex-goal-json <json-or-path>] [--json] | |
| + omx ultragoal native-bootstrap <authorize|status|revoke> [options] [--json] | |
| + omx ultragoal native-bootstrap authorize --session-id <id> --root-thread-id <id> (--child-id <id> | --child-path <agent-path>) --bootstrap-generation <id> --path-prefix <path> [--json] | |
| Aliases: | |
| create -> create-goals, complete|next|start-next -> complete-goals | |
| @@ -336,6 +367,7 @@ const ULTRAGOAL_MUTATING_COMMANDS = new Set([ | |
| 'next', | |
| 'start-next', | |
| 'checkpoint', | |
| + 'native-bootstrap', | |
| ]); | |
| function readTeamWorkerIdentity(env: NodeJS.ProcessEnv = process.env): string | null { | |
| @@ -345,6 +377,37 @@ function readTeamWorkerIdentity(env: NodeJS.ProcessEnv = process.env): string | | |
| return internalIdentity || null; | |
| } | |
| +async function resolveNativeBootstrapRuntimeScope(cwd: string, requestedSessionId: string) { | |
| + const scope = await resolveRuntimeStateScope(cwd); | |
| + if (scope.sessionId || readTeamWorkerIdentity()) return scope; | |
| + | |
| + const codexThreadId = process.env.CODEX_THREAD_ID; | |
| + if ( | |
| + codexThreadId !== requestedSessionId | |
| + || normalizeSessionId(codexThreadId) !== requestedSessionId | |
| + ) return scope; | |
| + | |
| + const pointer = await readJsonIfExists<Record<string, unknown>>(join(scope.baseStateDir, 'session.json')); | |
| + if ( | |
| + !pointer | |
| + || Array.isArray(pointer) | |
| + || pointer.session_id !== requestedSessionId | |
| + || normalizeSessionId(pointer.session_id) !== requestedSessionId | |
| + || pointer.native_session_id !== requestedSessionId | |
| + || normalizeSessionId(pointer.native_session_id) !== requestedSessionId | |
| + || !isSessionStateAuthoritativeForCwd(pointer as unknown as SessionState, scope.cwd) | |
| + ) { | |
| + return scope; | |
| + } | |
| + | |
| + const pointerStateRoot = typeof pointer.state_root === 'string' | |
| + ? pointer.state_root | |
| + : join(scope.cwd, '.omx', 'state'); | |
| + if (!sameFilePath(pointerStateRoot, scope.baseStateDir)) return scope; | |
| + | |
| + return resolveRuntimeStateScope(cwd, requestedSessionId); | |
| +} | |
| + | |
| function assertUltragoalMutationAllowedFromCurrentProcess(command: string): void { | |
| if (!ULTRAGOAL_MUTATING_COMMANDS.has(command)) return; | |
| const workerIdentity = readTeamWorkerIdentity(); | |
| @@ -374,6 +437,133 @@ export async function ultragoalCommand(args: string[], deps: UltragoalCommandDep | |
| assertUltragoalMutationAllowedFromCurrentProcess(command); | |
| + if (command === 'native-bootstrap') { | |
| + const action = rest[0] ?? 'status'; | |
| + const actionArgs = rest.slice(1); | |
| + const config = resolveNativeUltragoalConfig(); | |
| + const requestedSessionId = readValue(actionArgs, '--session-id'); | |
| + if (!requestedSessionId) { | |
| + throw new UltragoalError('Native bootstrap commands require an explicit --session-id.'); | |
| + } | |
| + const scope = await resolveNativeBootstrapRuntimeScope(cwd, requestedSessionId); | |
| + if (!scope.sessionId) { | |
| + throw new UltragoalError('Native bootstrap requires a canonical current runtime session.'); | |
| + } | |
| + if (requestedSessionId !== scope.sessionId) { | |
| + throw new UltragoalError('Requested --session-id does not match the canonical current runtime session.'); | |
| + } | |
| + const sessionId = scope.sessionId; | |
| + const rootThreadId = readValue(actionArgs, '--root-thread-id'); | |
| + | |
| + if (action === 'authorize') { | |
| + if (config.backend !== 'local-experimental') { | |
| + throw new UltragoalError('native-bootstrap authorize requires nativeUltragoal.backend="local-experimental".'); | |
| + } | |
| + const requestedChildAgentId = readValue(actionArgs, '--child-id'); | |
| + const childAgentPath = readValue(actionArgs, '--child-path'); | |
| + const bootstrapGeneration = readValue(actionArgs, '--bootstrap-generation'); | |
| + const pathPrefixes = readRepeated(actionArgs, '--path-prefix'); | |
| + if (!rootThreadId) throw new UltragoalError('Missing --root-thread-id.'); | |
| + if (Boolean(requestedChildAgentId) === Boolean(childAgentPath)) { | |
| + throw new UltragoalError('Pass exactly one of --child-id or --child-path.'); | |
| + } | |
| + if (!bootstrapGeneration) throw new UltragoalError('Missing --bootstrap-generation.'); | |
| + if (pathPrefixes.length === 0) throw new UltragoalError('Pass at least one --path-prefix.'); | |
| + let childAgentId = requestedChildAgentId; | |
| + if (childAgentPath) { | |
| + const receiptBootstrap = await readNativeUltragoalBootstrap(scope.baseStateDir, sessionId); | |
| + if ( | |
| + !receiptBootstrap | |
| + || receiptBootstrap.generation !== bootstrapGeneration | |
| + || receiptBootstrap.root_thread_id !== rootThreadId | |
| + ) throw new UltragoalError('child_transcript_receipt_unbound'); | |
| + const resolvedChild = await resolveLocalNativeExecutorChildReceipt({ | |
| + cwd: scope.cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + agentPath: childAgentPath, | |
| + receiptNotBefore: receiptBootstrap.created_at, | |
| + receiptNotAfter: receiptBootstrap.expires_at, | |
| + }); | |
| + if (!resolvedChild.ok) throw new UltragoalError(resolvedChild.reason); | |
| + childAgentId = resolvedChild.childAgentId; | |
| + } | |
| + if (!childAgentId) throw new UltragoalError('Unable to resolve native executor child identity.'); | |
| + const ttlRaw = readValue(actionArgs, '--ttl-seconds'); | |
| + const ttlSeconds = ttlRaw === undefined ? config.assignmentTtlSeconds : Number(ttlRaw); | |
| + try { | |
| + const assignment = await issueLocalNativeExecutorAssignment({ | |
| + cwd: scope.cwd, | |
| + stateDir: scope.baseStateDir, | |
| + sessionId, | |
| + rootThreadId, | |
| + childAgentId, | |
| + expectedGeneration: bootstrapGeneration, | |
| + pathPrefixes, | |
| + ttlSeconds, | |
| + allowedActions: hasFlag(actionArgs, '--path-write-only') | |
| + ? ['path-write'] | |
| + : ['path-write', 'bash-write'], | |
| + }); | |
| + if (await sessionHasActiveStandaloneUltragoal(scope.baseStateDir, sessionId)) { | |
| + const currentBootstrap = await readNativeUltragoalBootstrap(scope.baseStateDir, sessionId); | |
| + if (currentBootstrap?.status !== 'active') { | |
| + const prepared = await prepareNativeUltragoalBootstrapActivation({ | |
| + cwd: scope.cwd, | |
| + stateDir: scope.baseStateDir, | |
| + sessionId, | |
| + childAgentId, | |
| + }); | |
| + if (!prepared.ok) throw new UltragoalError('native_ultragoal_activation_receipt_invalid'); | |
| + const confirmed = await markNativeUltragoalBootstrapActive({ | |
| + cwd: scope.cwd, | |
| + stateDir: scope.baseStateDir, | |
| + sessionId, | |
| + childAgentId, | |
| + }); | |
| + if (!confirmed.ok) throw new UltragoalError('native_ultragoal_activation_receipt_invalid'); | |
| + } | |
| + } | |
| + if (json) printJson({ ok: true, backend: config.backend, assignment }); | |
| + else console.log(`native Ultragoal executor authorized: ${assignment.child_agent_id}`); | |
| + } catch (error) { | |
| + throw new UltragoalError(error instanceof Error ? error.message : String(error)); | |
| + } | |
| + return; | |
| + } | |
| + | |
| + if (action === 'revoke') { | |
| + await revokeLocalNativeExecutorAssignment({ | |
| + stateDir: scope.baseStateDir, | |
| + sessionId, | |
| + childAgentId: readValue(actionArgs, '--child-id'), | |
| + reason: readValue(actionArgs, '--reason') ?? 'root_requested_revoke', | |
| + }); | |
| + if (json) printJson({ ok: true, sessionId, status: 'revoked' }); | |
| + else console.log(`native Ultragoal bootstrap revoked: ${sessionId}`); | |
| + return; | |
| + } | |
| + | |
| + if (action === 'status') { | |
| + const bootstrap = await readNativeUltragoalBootstrap(scope.baseStateDir, sessionId); | |
| + const readiness = bootstrap?.root_thread_id | |
| + ? await reconcileLocalNativeExecutorAssignments({ | |
| + cwd: scope.cwd, | |
| + stateDir: scope.baseStateDir, | |
| + sessionId, | |
| + rootThreadId: bootstrap.root_thread_id, | |
| + }) | |
| + : { ready: false }; | |
| + if (json) printJson({ backend: config.backend, bootstrap, readiness }); | |
| + else console.log(bootstrap | |
| + ? `native Ultragoal bootstrap: ${bootstrap.status}${readiness.ready ? ' (ready)' : ''}` | |
| + : 'native Ultragoal bootstrap: absent'); | |
| + return; | |
| + } | |
| + | |
| + throw new UltragoalError('Invalid native-bootstrap action; expected authorize, status, or revoke.'); | |
| + } | |
| + | |
| if (command === 'create' || command === 'create-goals') { | |
| const briefFile = readValue(rest, '--brief-file'); | |
| const brief = readValue(rest, '--brief') | |
| diff --git a/src/config/__tests__/native-ultragoal.test.ts b/src/config/__tests__/native-ultragoal.test.ts | |
| new file mode 100644 | |
| index 00000000..e4bb6532 | |
| --- /dev/null | |
| +++ b/src/config/__tests__/native-ultragoal.test.ts | |
| @@ -0,0 +1,95 @@ | |
| +import assert from 'node:assert/strict'; | |
| +import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'; | |
| +import { tmpdir } from 'node:os'; | |
| +import { join } from 'node:path'; | |
| +import { describe, it } from 'node:test'; | |
| +import { resolveNativeUltragoalConfig } from '../native-ultragoal.js'; | |
| + | |
| +describe('native Ultragoal config', () => { | |
| + it('defaults off and reads the explicit global config', async () => { | |
| + const homeDir = await mkdtemp(join(tmpdir(), 'omx-native-ultragoal-config-')); | |
| + try { | |
| + assert.deepEqual(resolveNativeUltragoalConfig({ homeDir, env: {} }), { | |
| + backend: 'off', | |
| + assignmentTtlSeconds: 900, | |
| + source: 'default', | |
| + }); | |
| + await mkdir(join(homeDir, '.omx'), { recursive: true }); | |
| + await writeFile(join(homeDir, '.omx', 'config.json'), JSON.stringify({ | |
| + nativeUltragoal: { | |
| + backend: 'local-experimental', | |
| + assignmentTtlSeconds: 300, | |
| + }, | |
| + })); | |
| + assert.deepEqual(resolveNativeUltragoalConfig({ homeDir, env: {} }), { | |
| + backend: 'local-experimental', | |
| + assignmentTtlSeconds: 300, | |
| + source: 'config', | |
| + }); | |
| + } finally { | |
| + await rm(homeDir, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('keeps invalid config fail-closed and preserves explicit env precedence', async () => { | |
| + const homeDir = await mkdtemp(join(tmpdir(), 'omx-native-ultragoal-config-invalid-')); | |
| + try { | |
| + await mkdir(join(homeDir, '.omx'), { recursive: true }); | |
| + await writeFile(join(homeDir, '.omx', 'config.json'), JSON.stringify({ | |
| + nativeUltragoal: { backend: 'local', assignmentTtlSeconds: 1 }, | |
| + })); | |
| + assert.equal(resolveNativeUltragoalConfig({ homeDir, env: {} }).backend, 'off'); | |
| + assert.deepEqual(resolveNativeUltragoalConfig({ | |
| + homeDir, | |
| + env: { OMX_NATIVE_ULTRAGOAL_BACKEND: 'host' }, | |
| + }), { | |
| + backend: 'host', | |
| + assignmentTtlSeconds: 900, | |
| + source: 'env', | |
| + }); | |
| + assert.equal(resolveNativeUltragoalConfig({ | |
| + homeDir, | |
| + env: { OMX_NATIVE_SUBAGENT_ASSIGNMENTS: 'on' }, | |
| + }).backend, 'off'); | |
| + await writeFile(join(homeDir, '.omx', 'config.json'), '{ malformed'); | |
| + assert.deepEqual(resolveNativeUltragoalConfig({ | |
| + homeDir, | |
| + env: { OMX_NATIVE_SUBAGENT_ASSIGNMENTS: 'on' }, | |
| + }), { | |
| + backend: 'off', | |
| + assignmentTtlSeconds: 900, | |
| + source: 'config', | |
| + }); | |
| + } finally { | |
| + await rm(homeDir, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('keeps explicit new config ahead of the legacy assignment opt-in', async () => { | |
| + const homeDir = await mkdtemp(join(tmpdir(), 'omx-native-ultragoal-config-legacy-')); | |
| + try { | |
| + await mkdir(join(homeDir, '.omx'), { recursive: true }); | |
| + await writeFile(join(homeDir, '.omx', 'config.json'), JSON.stringify({ | |
| + nativeUltragoal: { backend: 'host', assignmentTtlSeconds: 120 }, | |
| + })); | |
| + assert.deepEqual(resolveNativeUltragoalConfig({ | |
| + homeDir, | |
| + env: { OMX_NATIVE_SUBAGENT_ASSIGNMENTS: 'on' }, | |
| + }), { | |
| + backend: 'host', | |
| + assignmentTtlSeconds: 120, | |
| + source: 'config', | |
| + }); | |
| + assert.deepEqual(resolveNativeUltragoalConfig({ | |
| + homeDir, | |
| + env: { OMX_NATIVE_ULTRAGOAL_BACKEND: 'local' }, | |
| + }), { | |
| + backend: 'off', | |
| + assignmentTtlSeconds: 120, | |
| + source: 'env', | |
| + }); | |
| + } finally { | |
| + await rm(homeDir, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| +}); | |
| diff --git a/src/config/native-ultragoal.ts b/src/config/native-ultragoal.ts | |
| new file mode 100644 | |
| index 00000000..83da9374 | |
| --- /dev/null | |
| +++ b/src/config/native-ultragoal.ts | |
| @@ -0,0 +1,125 @@ | |
| +import { existsSync, readFileSync } from 'node:fs'; | |
| +import { homedir } from 'node:os'; | |
| +import { join } from 'node:path'; | |
| + | |
| +export type NativeUltragoalBackend = 'off' | 'host' | 'local-experimental'; | |
| + | |
| +export interface NativeUltragoalRuntimeConfig { | |
| + backend: NativeUltragoalBackend; | |
| + assignmentTtlSeconds: number; | |
| + source: 'default' | 'config' | 'env' | 'legacy-env'; | |
| +} | |
| + | |
| +interface NativeUltragoalConfigFile { | |
| + nativeUltragoal?: { | |
| + backend?: unknown; | |
| + assignmentTtlSeconds?: unknown; | |
| + }; | |
| +} | |
| + | |
| +const DEFAULT_ASSIGNMENT_TTL_SECONDS = 15 * 60; | |
| +const MIN_ASSIGNMENT_TTL_SECONDS = 30; | |
| +const MAX_ASSIGNMENT_TTL_SECONDS = 60 * 60; | |
| + | |
| +function normalizeBackend(value: unknown): NativeUltragoalBackend | null { | |
| + if (typeof value !== 'string') return null; | |
| + const normalized = value.trim().toLowerCase(); | |
| + if (normalized === 'off' || normalized === 'host' || normalized === 'local-experimental') return normalized; | |
| + return null; | |
| +} | |
| + | |
| +function normalizeAssignmentTtlSeconds(value: unknown): number | null { | |
| + if (typeof value !== 'number' || !Number.isInteger(value)) return null; | |
| + if (value < MIN_ASSIGNMENT_TTL_SECONDS || value > MAX_ASSIGNMENT_TTL_SECONDS) return null; | |
| + return value; | |
| +} | |
| + | |
| +function readConfigFile(homeDir: string): | |
| + | { status: 'absent' } | |
| + | { status: 'invalid' } | |
| + | { status: 'valid'; value: NativeUltragoalConfigFile } { | |
| + const path = join(homeDir, '.omx', 'config.json'); | |
| + if (!existsSync(path)) return { status: 'absent' }; | |
| + try { | |
| + const parsed = JSON.parse(readFileSync(path, 'utf-8')) as unknown; | |
| + return parsed && typeof parsed === 'object' && !Array.isArray(parsed) | |
| + ? { status: 'valid', value: parsed as NativeUltragoalConfigFile } | |
| + : { status: 'invalid' }; | |
| + } catch { | |
| + return { status: 'invalid' }; | |
| + } | |
| +} | |
| + | |
| +export function resolveNativeUltragoalConfig(options: { | |
| + homeDir?: string; | |
| + env?: NodeJS.ProcessEnv; | |
| +} = {}): NativeUltragoalRuntimeConfig { | |
| + const env = options.env ?? process.env; | |
| + const configResult = readConfigFile(options.homeDir ?? homedir()); | |
| + const config = configResult.status === 'valid' ? configResult.value : null; | |
| + const hasExplicitConfiguredBackend = config?.nativeUltragoal?.backend !== undefined; | |
| + const configuredBackend = normalizeBackend(config?.nativeUltragoal?.backend); | |
| + const assignmentTtlSeconds = normalizeAssignmentTtlSeconds( | |
| + config?.nativeUltragoal?.assignmentTtlSeconds, | |
| + ) ?? DEFAULT_ASSIGNMENT_TTL_SECONDS; | |
| + const rawEnvBackend = env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + const explicitEnvBackend = normalizeBackend(rawEnvBackend); | |
| + if (explicitEnvBackend) { | |
| + return { | |
| + backend: explicitEnvBackend, | |
| + assignmentTtlSeconds, | |
| + source: 'env', | |
| + }; | |
| + } | |
| + if (typeof rawEnvBackend === 'string' && rawEnvBackend.trim() !== '') { | |
| + return { | |
| + backend: 'off', | |
| + assignmentTtlSeconds, | |
| + source: 'env', | |
| + }; | |
| + } | |
| + | |
| + if (configResult.status === 'invalid') { | |
| + return { | |
| + backend: 'off', | |
| + assignmentTtlSeconds, | |
| + source: 'config', | |
| + }; | |
| + } | |
| + | |
| + if (configuredBackend) { | |
| + return { | |
| + backend: configuredBackend, | |
| + assignmentTtlSeconds, | |
| + source: 'config', | |
| + }; | |
| + } | |
| + | |
| + if (hasExplicitConfiguredBackend) { | |
| + return { | |
| + backend: 'off', | |
| + assignmentTtlSeconds, | |
| + source: 'config', | |
| + }; | |
| + } | |
| + | |
| + if (String(env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS ?? '').trim().toLowerCase() === 'on') { | |
| + return { | |
| + backend: 'local-experimental', | |
| + assignmentTtlSeconds, | |
| + source: 'legacy-env', | |
| + }; | |
| + } | |
| + | |
| + return { | |
| + backend: 'off', | |
| + assignmentTtlSeconds, | |
| + source: 'default', | |
| + }; | |
| +} | |
| + | |
| +export function nativeUltragoalBackendIsLocalExperimental( | |
| + options: Parameters<typeof resolveNativeUltragoalConfig>[0] = {}, | |
| +): boolean { | |
| + return resolveNativeUltragoalConfig(options).backend === 'local-experimental'; | |
| +} | |
| diff --git a/src/hooks/__tests__/notify-hook-auto-nudge.test.ts b/src/hooks/__tests__/notify-hook-auto-nudge.test.ts | |
| index 44fd9f87..d50bfccf 100644 | |
| --- a/src/hooks/__tests__/notify-hook-auto-nudge.test.ts | |
| +++ b/src/hooks/__tests__/notify-hook-auto-nudge.test.ts | |
| @@ -2383,6 +2383,120 @@ exit 0 | |
| }); | |
| }); | |
| + it('does not reactivate completed ultragoal from the same completed-turn input replay', async () => { | |
| + await withTempWorkingDir(async (cwd) => { | |
| + const omxDir = join(cwd, '.omx'); | |
| + const stateDir = join(omxDir, 'state'); | |
| + const logsDir = join(omxDir, 'logs'); | |
| + const codexHome = join(cwd, 'codex-home'); | |
| + const fakeBinDir = join(cwd, 'fake-bin'); | |
| + const sessionStateDir = join(stateDir, 'sessions', 'sess-managed'); | |
| + const terminalCompletedAt = '2026-07-30T00:06:49.168Z'; | |
| + | |
| + await mkdir(logsDir, { recursive: true }); | |
| + await mkdir(sessionStateDir, { recursive: true }); | |
| + await mkdir(codexHome, { recursive: true }); | |
| + await mkdir(fakeBinDir, { recursive: true }); | |
| + | |
| + await writeJson(join(codexHome, '.omx-config.json'), { | |
| + autoNudge: { enabled: true, delaySec: 0, stallMs: 0 }, | |
| + }); | |
| + await writeManagedSessionState(stateDir, cwd); | |
| + await writeJson(join(sessionStateDir, 'ultragoal-state.json'), { | |
| + active: false, | |
| + current_phase: 'complete', | |
| + run_outcome: 'finished', | |
| + completed_at: terminalCompletedAt, | |
| + }); | |
| + await writeJson(join(stateDir, 'native-stop-state.json'), { | |
| + sessions: { | |
| + 'sess-managed': { | |
| + ordinary_no_progress_guard: { | |
| + last_turn_id: 'turn-ultragoal-complete', | |
| + last_thread_id: null, | |
| + }, | |
| + }, | |
| + }, | |
| + }); | |
| + | |
| + await writeFile(join(fakeBinDir, 'tmux'), buildFakeTmux(join(cwd, 'tmux.log'))); | |
| + await chmod(join(fakeBinDir, 'tmux'), 0o755); | |
| + | |
| + const result = runNotifyHook(cwd, fakeBinDir, codexHome, { | |
| + type: 'agent-turn-complete', | |
| + 'thread-id': 'thread-ultragoal-complete', | |
| + 'turn-id': 'turn-ultragoal-complete', | |
| + 'input-messages': ['$ultragoal Выполни полный native acceptance.'], | |
| + 'last-assistant-message': 'Native Ultragoal acceptance завершён до terminal completion.', | |
| + }); | |
| + assert.equal(result.status, 0, `hook failed: ${result.stderr || result.stdout}`); | |
| + | |
| + const ultragoalState = JSON.parse(await readFile(join(sessionStateDir, 'ultragoal-state.json'), 'utf-8')) as { | |
| + mode?: string; | |
| + active: boolean; | |
| + current_phase: string; | |
| + started_at?: string; | |
| + completed_at?: string; | |
| + run_outcome?: string; | |
| + }; | |
| + assert.equal(ultragoalState.mode, undefined, 'notify must preserve the CLI state-file shape without reseeding mode'); | |
| + assert.equal(ultragoalState.active, false); | |
| + assert.equal(ultragoalState.current_phase, 'complete'); | |
| + assert.equal(ultragoalState.run_outcome, 'finished'); | |
| + assert.equal(ultragoalState.completed_at, terminalCompletedAt); | |
| + assert.equal(ultragoalState.started_at, undefined, 'notify must not create a new planning run'); | |
| + }); | |
| + }); | |
| + | |
| + it('allows a later ultragoal prompt after completed state to reach normal activation', async () => { | |
| + await withTempWorkingDir(async (cwd) => { | |
| + const stateDir = join(cwd, '.omx', 'state'); | |
| + const sessionId = 'sess-ultragoal-restart'; | |
| + const sessionStateDir = join(stateDir, 'sessions', sessionId); | |
| + await mkdir(sessionStateDir, { recursive: true }); | |
| + await writeJson(join(sessionStateDir, 'ultragoal-state.json'), { | |
| + mode: 'ultragoal', | |
| + active: false, | |
| + current_phase: 'complete', | |
| + completed_at: '2026-07-30T00:06:49.168Z', | |
| + session_id: sessionId, | |
| + }); | |
| + await writeJson(join(stateDir, 'native-stop-state.json'), { | |
| + sessions: { | |
| + [sessionId]: { | |
| + ordinary_no_progress_guard: { | |
| + last_turn_id: 'turn-ultragoal-complete', | |
| + }, | |
| + }, | |
| + }, | |
| + }); | |
| + | |
| + let writerCalls = 0; | |
| + const result = await recordNotifySkillActivation({ | |
| + stateDir, | |
| + sourceCwd: cwd, | |
| + text: '$ultragoal Запусти следующую задачу.', | |
| + sessionId, | |
| + threadId: 'thread-ultragoal-complete', | |
| + turnId: 'turn-ultragoal-restart', | |
| + payload: { | |
| + type: 'agent-turn-complete', | |
| + 'thread-id': 'thread-ultragoal-complete', | |
| + 'turn-id': 'turn-ultragoal-restart', | |
| + 'last-assistant-message': 'Starting the next Ultragoal run.', | |
| + }, | |
| + }, { | |
| + recordSkillActivation: async () => { | |
| + writerCalls += 1; | |
| + return null; | |
| + }, | |
| + }); | |
| + | |
| + assert.equal(result, null); | |
| + assert.equal(writerCalls, 1, 'a different turn must not be mistaken for terminal replay'); | |
| + }); | |
| + }); | |
| + | |
| it('fails a later fresh autopilot prompt after completed autopilot state when receipt verification is unavailable', async () => { | |
| await withTempWorkingDir(async (cwd) => { | |
| const omxDir = join(cwd, '.omx'); | |
| diff --git a/src/mcp/__tests__/state-paths.test.ts b/src/mcp/__tests__/state-paths.test.ts | |
| index cd01715c..863a644b 100644 | |
| --- a/src/mcp/__tests__/state-paths.test.ts | |
| +++ b/src/mcp/__tests__/state-paths.test.ts | |
| @@ -43,7 +43,9 @@ const isolatedEnvKeys = [ | |
| 'OMX_ROOT', | |
| 'OMX_STATE_ROOT', | |
| 'OMX_TEAM_STATE_ROOT', | |
| + 'OMX_TEAM_WORKER', | |
| 'OMX_SESSION_ID', | |
| + 'CODEX_THREAD_ID', | |
| 'CODEX_SESSION_ID', | |
| 'SESSION_ID', | |
| 'TMUX', | |
| @@ -1063,6 +1065,144 @@ describe('state paths', () => { | |
| await rm(wd, { recursive: true, force: true }); | |
| } | |
| }); | |
| + it('recovers an App-native identity-indeterminate pointer from an exact host CODEX_THREAD_ID', async () => { | |
| + const wd = await mkRealTemp('omx-writable-indeterminate-codex-root-'); | |
| + __setSessionPointerTransactionDependenciesForTests({ probePid: () => 'indeterminate' }); | |
| + try { | |
| + const stateDir = getBaseStateDir(wd); | |
| + const sessionId = '019faf7d-ffbb-70d2-b58f-9bb83e752007'; | |
| + const nativeRootId = sessionId; | |
| + await mkdir(stateDir, { recursive: true }); | |
| + const pointerBody = JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: nativeRootId, | |
| + cwd: wd, | |
| + pid: 8388607, | |
| + }); | |
| + await writeFile(join(stateDir, 'session.json'), pointerBody); | |
| + process.env.CODEX_THREAD_ID = nativeRootId; | |
| + | |
| + await assert.rejects( | |
| + () => resolveWritableStateScope(wd), | |
| + (error: unknown) => (error as Error).message === WRITABLE_STATE_SCOPE_ERRORS.unusableSession, | |
| + ); | |
| + await assert.rejects( | |
| + () => resolveWritableStateScope(wd, undefined, { nativeUltragoalBackend: 'host' }), | |
| + (error: unknown) => (error as Error).message === WRITABLE_STATE_SCOPE_ERRORS.unusableSession, | |
| + ); | |
| + assert.deepEqual(await resolveWritableStateScope(wd, undefined, { | |
| + nativeUltragoalBackend: 'local-experimental', | |
| + }), { | |
| + source: 'session', | |
| + sessionId, | |
| + stateDir: join(stateDir, 'sessions', sessionId), | |
| + }); | |
| + assert.equal(await readFile(join(stateDir, 'session.json'), 'utf8'), pointerBody); | |
| + } finally { | |
| + __resetSessionPointerTransactionDependenciesForTests(); | |
| + await rm(wd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('uses session_id as the App-native root only when native_session_id is absent', async () => { | |
| + const wd = await mkRealTemp('omx-writable-indeterminate-codex-session-'); | |
| + __setSessionPointerTransactionDependenciesForTests({ probePid: () => 'indeterminate' }); | |
| + try { | |
| + const stateDir = getBaseStateDir(wd); | |
| + const sessionId = '019faf7e-0002-7000-8000-000000000002'; | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(join(stateDir, 'session.json'), JSON.stringify({ | |
| + session_id: sessionId, | |
| + cwd: wd, | |
| + pid: 8388607, | |
| + })); | |
| + process.env.CODEX_THREAD_ID = sessionId; | |
| + assert.equal((await resolveWritableStateScope(wd, undefined, { | |
| + nativeUltragoalBackend: 'local-experimental', | |
| + })).sessionId, sessionId); | |
| + | |
| + await writeFile(join(stateDir, 'session.json'), JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: '', | |
| + cwd: wd, | |
| + pid: 8388607, | |
| + })); | |
| + await assert.rejects( | |
| + () => resolveWritableStateScope(wd, undefined, { nativeUltragoalBackend: 'local-experimental' }), | |
| + (error: unknown) => (error as Error).message === WRITABLE_STATE_SCOPE_ERRORS.unusableSession, | |
| + ); | |
| + } finally { | |
| + __resetSessionPointerTransactionDependenciesForTests(); | |
| + await rm(wd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('rejects child, malformed, conflicting, override, and non-exact-cwd native claims', async () => { | |
| + const wd = await mkRealTemp('omx-writable-indeterminate-codex-deny-'); | |
| + __setSessionPointerTransactionDependenciesForTests({ probePid: () => 'indeterminate' }); | |
| + try { | |
| + const stateDir = getBaseStateDir(wd); | |
| + const sessionId = '019faf7e-0003-7000-8000-000000000003'; | |
| + const nativeRootId = sessionId; | |
| + const childId = '019faf7e-0005-7000-8000-000000000005'; | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(join(stateDir, 'session.json'), JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: nativeRootId, | |
| + cwd: wd, | |
| + pid: 8388607, | |
| + })); | |
| + | |
| + for (const claim of [childId, 'not-a-uuid', nativeRootId.toUpperCase()]) { | |
| + process.env.CODEX_THREAD_ID = claim; | |
| + await assert.rejects( | |
| + () => resolveWritableStateScope(wd, undefined, { nativeUltragoalBackend: 'local-experimental' }), | |
| + (error: unknown) => (error as Error).message === WRITABLE_STATE_SCOPE_ERRORS.unusableSession, | |
| + claim, | |
| + ); | |
| + } | |
| + | |
| + process.env.CODEX_THREAD_ID = nativeRootId; | |
| + process.env.OMX_SESSION_ID = childId; | |
| + await assert.rejects( | |
| + () => resolveWritableStateScope(wd, undefined, { nativeUltragoalBackend: 'local-experimental' }), | |
| + (error: unknown) => (error as Error).message === WRITABLE_STATE_SCOPE_ERRORS.unusableSession, | |
| + ); | |
| + delete process.env.OMX_SESSION_ID; | |
| + | |
| + process.env.OMX_ROOT = wd; | |
| + await assert.rejects( | |
| + () => resolveWritableStateScope(wd, undefined, { nativeUltragoalBackend: 'local-experimental' }), | |
| + (error: unknown) => (error as Error).message === WRITABLE_STATE_SCOPE_ERRORS.unusableSession, | |
| + ); | |
| + delete process.env.OMX_ROOT; | |
| + | |
| + process.env.OMX_TEAM_WORKER = 'team/worker-1'; | |
| + await assert.rejects( | |
| + () => resolveWritableStateScope(wd, undefined, { nativeUltragoalBackend: 'local-experimental' }), | |
| + (error: unknown) => (error as Error).message === WRITABLE_STATE_SCOPE_ERRORS.unusableSession, | |
| + ); | |
| + delete process.env.OMX_TEAM_WORKER; | |
| + | |
| + const nested = join(wd, 'nested'); | |
| + const nestedStateDir = join(nested, '.omx', 'state'); | |
| + await mkdir(nestedStateDir, { recursive: true }); | |
| + await writeFile(join(nestedStateDir, 'session.json'), JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: nativeRootId, | |
| + cwd: wd, | |
| + state_root: nestedStateDir, | |
| + pid: 8388607, | |
| + })); | |
| + await assert.rejects( | |
| + () => resolveWritableStateScope(nested, undefined, { nativeUltragoalBackend: 'local-experimental' }), | |
| + (error: unknown) => (error as Error).message === WRITABLE_STATE_SCOPE_ERRORS.unusableSession, | |
| + ); | |
| + } finally { | |
| + __resetSessionPointerTransactionDependenciesForTests(); | |
| + await rm(wd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| it('fails closed for an identity-indeterminate pointer even with an env binding', async () => { | |
| const wd = await mkRealTemp('omx-writable-indeterminate-env-'); | |
| __setSessionPointerTransactionDependenciesForTests({ probePid: () => 'indeterminate' }); | |
| @@ -1194,21 +1334,22 @@ describe('writable state scope recovery and revalidation', () => { | |
| const stateDir = getBaseStateDir(wd); | |
| const sessionPath = join(stateDir, 'session.json'); | |
| await mkdir(stateDir, { recursive: true }); | |
| - await writeFile(sessionPath, JSON.stringify({ session_id: 'sess-indeterminate', cwd: wd, pid: 8388607 })); | |
| - process.env.OMX_SESSION_ID = 'sess-indeterminate'; | |
| + const sessionId = '019faf7e-0006-7000-8000-000000000006'; | |
| + await writeFile(sessionPath, JSON.stringify({ session_id: sessionId, native_session_id: sessionId, cwd: wd, pid: 8388607 })); | |
| + process.env.CODEX_THREAD_ID = sessionId; | |
| __setWritableStateScopeTestHooksForTests({ | |
| beforeRecoveryReread: async () => { | |
| - await writeFile(sessionPath, JSON.stringify({ session_id: 'sess-indeterminate', cwd: wd, pid: 9999999 })); | |
| + await writeFile(sessionPath, JSON.stringify({ session_id: sessionId, native_session_id: sessionId, cwd: wd, pid: 9999999 })); | |
| }, | |
| }); | |
| await assert.rejects( | |
| - () => resolveWritableStateScope(wd), | |
| + () => resolveWritableStateScope(wd, undefined, { nativeUltragoalBackend: 'local-experimental' }), | |
| (error: unknown) => { | |
| assert.equal((error as Error).message, WRITABLE_STATE_SCOPE_ERRORS.unusableSession); | |
| return true; | |
| }, | |
| ); | |
| - assert.equal(existsSync(join(stateDir, 'sessions', 'sess-indeterminate')), false); | |
| + assert.equal(existsSync(join(stateDir, 'sessions', sessionId)), false); | |
| } finally { | |
| __setWritableStateScopeTestHooksForTests({}); | |
| __resetSessionPointerTransactionDependenciesForTests(); | |
| diff --git a/src/mcp/state-paths.ts b/src/mcp/state-paths.ts | |
| index 1e0c6395..94d9205c 100644 | |
| --- a/src/mcp/state-paths.ts | |
| +++ b/src/mcp/state-paths.ts | |
| @@ -17,6 +17,8 @@ const OMX_ROOT_ENV = 'OMX_ROOT'; | |
| const OMX_STATE_ROOT_ENV = 'OMX_STATE_ROOT'; | |
| const OMX_TEAM_STATE_ROOT_ENV = 'OMX_TEAM_STATE_ROOT'; | |
| const OMX_SESSION_ID_ENV = 'OMX_SESSION_ID'; | |
| +const CODEX_THREAD_ID_ENV = 'CODEX_THREAD_ID'; | |
| +const CANONICAL_CODEX_THREAD_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/; | |
| export const WRITABLE_STATE_SCOPE_ERRORS = { | |
| unusableSession: 'Cannot resolve writable state scope: session.json is present but unusable.', | |
| @@ -595,6 +597,10 @@ export interface WritableStateScopeTestHooks { | |
| onRecoveryStabilityReread?: (event: Readonly<{ ordinal: number; raw: string | undefined }>) => void; | |
| } | |
| +export interface WritableStateScopeOptions { | |
| + nativeUltragoalBackend?: 'off' | 'host' | 'local-experimental'; | |
| +} | |
| + | |
| let selectedPointerRecoveryHookForTests: (() => Promise<void>) | undefined; | |
| let writableScopeRevalidationHookForTests: WritableStateScopeTestHooks['beforeScopeRevalidation']; | |
| let selectedDecisionSnapshotReadHookForTests: WritableStateScopeTestHooks['onSelectedDecisionSnapshotRead']; | |
| @@ -625,6 +631,7 @@ export function __setWritableStateScopeTestHooksForTests(hooks: WritableStateSco | |
| export async function resolveWritableStateScope( | |
| workingDirectory?: string, | |
| explicitSessionId?: string, | |
| + options: WritableStateScopeOptions = {}, | |
| ): Promise<ResolvedStateScope> { | |
| const cwd = resolveWorkingDirectoryForState(workingDirectory); | |
| const baseStateDir = getBaseStateDir(cwd); | |
| @@ -656,13 +663,55 @@ export async function resolveWritableStateScope( | |
| raw: snapshot.raw, | |
| }); | |
| } | |
| - if (snapshot && envSessionId) { | |
| + if (snapshot) { | |
| const canonicalPointerSessionId = normalizeSessionId(snapshot.state.session_id); | |
| + const rawOmxSessionId = process.env[OMX_SESSION_ID_ENV]; | |
| + const explicitOmxSessionIdPresent = typeof rawOmxSessionId === 'string' && rawOmxSessionId.trim() !== ''; | |
| + const outsideTmux = !(process.env.TMUX?.trim()); | |
| + const rawCodexThreadId = process.env[CODEX_THREAD_ID_ENV]; | |
| + const codexThreadClaimPresent = typeof rawCodexThreadId === 'string' && rawCodexThreadId.trim() !== ''; | |
| + const codexThreadId = typeof rawCodexThreadId === 'string' | |
| + && CANONICAL_CODEX_THREAD_ID_PATTERN.test(rawCodexThreadId.trim()) | |
| + ? rawCodexThreadId.trim() | |
| + : undefined; | |
| + const pointerSessionIsCanonicalCodexThread = typeof snapshot.state.session_id === 'string' | |
| + && CANONICAL_CODEX_THREAD_ID_PATTERN.test(snapshot.state.session_id.trim()); | |
| + const pointerHasNativeSessionId = Object.prototype.hasOwnProperty.call(snapshot.state, 'native_session_id'); | |
| + const nativeRootThreadId = pointerHasNativeSessionId | |
| + ? (typeof snapshot.state.native_session_id === 'string' | |
| + && snapshot.state.native_session_id.trim() === canonicalPointerSessionId | |
| + ? snapshot.state.native_session_id.trim() | |
| + : undefined) | |
| + : canonicalPointerSessionId; | |
| + const hasStateRootOverride = [OMX_TEAM_STATE_ROOT_ENV, OMX_ROOT_ENV, OMX_STATE_ROOT_ENV] | |
| + .some((key) => typeof process.env[key] === 'string' && process.env[key]!.trim() !== ''); | |
| + const exactCodexNativeRecovery = Boolean( | |
| + liveness === 'identity-indeterminate' | |
| + && options.nativeUltragoalBackend === 'local-experimental' | |
| + && outsideTmux | |
| + && !(process.env.OMX_TEAM_WORKER?.trim()) | |
| + && codexThreadClaimPresent | |
| + && codexThreadId | |
| + && nativeRootThreadId | |
| + && canonicalPointerSessionId | |
| + && pointerSessionIsCanonicalCodexThread | |
| + && codexThreadId === canonicalPointerSessionId | |
| + && nativeRootThreadId === canonicalPointerSessionId | |
| + && canonicalizeExistingPath(resolvePath(cwd)) === snapshot.recordedCwd | |
| + && !hasStateRootOverride | |
| + && (!explicitOmxSessionIdPresent || envSessionId === canonicalPointerSessionId), | |
| + ); | |
| + const exactLegacyIndeterminateRecovery = Boolean( | |
| + liveness === 'identity-indeterminate' | |
| + && envSessionId === canonicalPointerSessionId | |
| + && (!outsideTmux || !codexThreadClaimPresent), | |
| + ); | |
| const recoveryEligible = Boolean( | |
| canonicalPointerSessionId | |
| && ( | |
| - liveness === 'stale-dead' | |
| - || (liveness === 'identity-indeterminate' && envSessionId === canonicalPointerSessionId) | |
| + (liveness === 'stale-dead' && envSessionId) | |
| + || exactLegacyIndeterminateRecovery | |
| + || exactCodexNativeRecovery | |
| ), | |
| ); | |
| if (recoveryEligible) { | |
| @@ -678,10 +727,14 @@ export async function resolveWritableStateScope( | |
| }); | |
| } | |
| if (reread === snapshot.raw) { | |
| + const recoveredSessionId = exactCodexNativeRecovery | |
| + ? canonicalPointerSessionId | |
| + : envSessionId; | |
| + if (!recoveredSessionId) throw new Error(WRITABLE_STATE_SCOPE_ERRORS.unusableSession); | |
| return { | |
| source: 'session', | |
| - sessionId: envSessionId, | |
| - stateDir: join(baseStateDir, 'sessions', envSessionId), | |
| + sessionId: recoveredSessionId, | |
| + stateDir: join(baseStateDir, 'sessions', recoveredSessionId), | |
| }; | |
| } | |
| } | |
| diff --git a/src/scripts/__tests__/codex-native-assignment.test.ts b/src/scripts/__tests__/codex-native-assignment.test.ts | |
| new file mode 100644 | |
| index 00000000..20c579dd | |
| --- /dev/null | |
| +++ b/src/scripts/__tests__/codex-native-assignment.test.ts | |
| @@ -0,0 +1,461 @@ | |
| +import assert from "node:assert/strict"; | |
| +import { chmod, mkdir, mkdtemp, rm, symlink, writeFile } from "node:fs/promises"; | |
| +import { tmpdir } from "node:os"; | |
| +import { dirname, join, resolve } from "node:path"; | |
| +import { afterEach, beforeEach, describe, it } from "node:test"; | |
| +import { dispatchCodexNativeHook } from "../codex-native-hook.js"; | |
| + | |
| +async function writeJson(path: string, value: unknown): Promise<void> { | |
| + await mkdir(dirname(path), { recursive: true }); | |
| + await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, "utf-8"); | |
| +} | |
| + | |
| +async function createActiveConductorFixture(phase = "executing"): Promise<{ | |
| + cwd: string; | |
| + stateDir: string; | |
| + sessionId: string; | |
| + leaderThreadId: string; | |
| + childAgentId: string; | |
| +}> { | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-assignment-")); | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-native-assignment"; | |
| + const leaderThreadId = "thread-native-assignment-leader"; | |
| + const childAgentId = "agent-native-assignment-executor"; | |
| + const now = new Date().toISOString(); | |
| + const bootstrapGeneration = "bootstrap-generation-native-assignment"; | |
| + | |
| + await writeJson(join(stateDir, "session.json"), { | |
| + session_id: sessionId, | |
| + native_session_id: leaderThreadId, | |
| + cwd, | |
| + }); | |
| + await writeJson(join(stateDir, "subagent-tracking.json"), { | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + [sessionId]: { | |
| + session_id: sessionId, | |
| + leader_thread_id: leaderThreadId, | |
| + updated_at: now, | |
| + threads: { | |
| + [leaderThreadId]: { | |
| + thread_id: leaderThreadId, | |
| + kind: "leader", | |
| + first_seen_at: now, | |
| + last_seen_at: now, | |
| + turn_count: 1, | |
| + }, | |
| + [childAgentId]: { | |
| + thread_id: childAgentId, | |
| + kind: "subagent", | |
| + mode: "executor", | |
| + status: "available", | |
| + provenance_kind: "native_subagent", | |
| + direct_child_parent_id: leaderThreadId, | |
| + direct_child_root_id: leaderThreadId, | |
| + leader_thread_id: leaderThreadId, | |
| + first_seen_at: now, | |
| + last_seen_at: now, | |
| + turn_count: 1, | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + }); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { | |
| + active: true, | |
| + skill: "ultragoal", | |
| + phase, | |
| + session_id: sessionId, | |
| + active_skills: [ | |
| + { skill: "ultragoal", phase, active: true, session_id: sessionId }, | |
| + ], | |
| + }); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { | |
| + active: true, | |
| + mode: "ultragoal", | |
| + current_phase: phase, | |
| + session_id: sessionId, | |
| + }); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "native-ultragoal-bootstrap.json"), { | |
| + schema_version: 1, | |
| + kind: "native-ultragoal-bootstrap", | |
| + generation: bootstrapGeneration, | |
| + backend: "local-experimental", | |
| + status: "active", | |
| + session_id: sessionId, | |
| + root_thread_id: leaderThreadId, | |
| + child_agent_id: childAgentId, | |
| + created_at: now, | |
| + updated_at: now, | |
| + expires_at: new Date(Date.now() + 10 * 60_000).toISOString(), | |
| + }); | |
| + | |
| + return { cwd, stateDir, sessionId, leaderThreadId, childAgentId }; | |
| +} | |
| + | |
| +function preToolUse( | |
| + cwd: string, | |
| + sessionId: string, | |
| + identity: Record<string, unknown>, | |
| + toolName: string, | |
| + toolInput: Record<string, unknown>, | |
| +) { | |
| + return dispatchCodexNativeHook({ | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + ...identity, | |
| + tool_name: toolName, | |
| + tool_input: toolInput, | |
| + }, { cwd }); | |
| +} | |
| + | |
| +describe("native Conductor assignment transport", () => { | |
| + const originalAssignmentOptIn = process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS; | |
| + | |
| + beforeEach(() => { | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + }); | |
| + | |
| + afterEach(() => { | |
| + if (originalAssignmentOptIn === undefined) delete process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS; | |
| + else process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = originalAssignmentOptIn; | |
| + }); | |
| + | |
| + it("requires typed tracker authority and rejects missing or conflicting role evidence", async () => { | |
| + const fixture = await createActiveConductorFixture(); | |
| + const { cwd, stateDir, sessionId, leaderThreadId, childAgentId } = fixture; | |
| + const assignmentPath = join( | |
| + stateDir, | |
| + "sessions", | |
| + sessionId, | |
| + "native-assignments", | |
| + `${encodeURIComponent(childAgentId)}.json`, | |
| + ); | |
| + const issuedAt = new Date(); | |
| + const expiresAt = new Date(issuedAt.getTime() + 10 * 60_000); | |
| + const trackingPath = join(stateDir, "subagent-tracking.json"); | |
| + const trackedChild = { | |
| + thread_id: childAgentId, | |
| + kind: "subagent", | |
| + leader_thread_id: leaderThreadId, | |
| + first_seen_at: issuedAt.toISOString(), | |
| + last_seen_at: issuedAt.toISOString(), | |
| + turn_count: 1, | |
| + }; | |
| + | |
| + try { | |
| + await writeJson(assignmentPath, { | |
| + schema_version: 1, | |
| + kind: "native-subagent-assignment", | |
| + lifecycle: "active", | |
| + root_session_id: sessionId, | |
| + root_thread_id: leaderThreadId, | |
| + child_agent_id: childAgentId, | |
| + agent_type: "executor", | |
| + allowed_actions: ["path-write"], | |
| + path_prefixes: ["src/assigned"], | |
| + issued_at: issuedAt.toISOString(), | |
| + expires_at: expiresAt.toISOString(), | |
| + bootstrap_generation: "bootstrap-generation-native-assignment", | |
| + }); | |
| + await writeJson(trackingPath, { | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + [sessionId]: { | |
| + session_id: sessionId, | |
| + leader_thread_id: leaderThreadId, | |
| + updated_at: issuedAt.toISOString(), | |
| + threads: { | |
| + [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, | |
| + [childAgentId]: trackedChild, | |
| + }, | |
| + }, | |
| + }, | |
| + }); | |
| + | |
| + const realTypedChild = await preToolUse( | |
| + cwd, | |
| + sessionId, | |
| + { agent_id: childAgentId, agent_type: "executor" }, | |
| + "Write", | |
| + { file_path: "src/assigned/real-typed-child.txt", content: "ok\n" }, | |
| + ); | |
| + assert.equal(realTypedChild.outputJson?.decision, "block"); | |
| + assert.match(String(realTypedChild.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); | |
| + | |
| + const missingTypedRole = await preToolUse( | |
| + cwd, | |
| + sessionId, | |
| + { | |
| + agent_id: childAgentId, | |
| + source: { subagent: { thread_spawn: { agent_role: "executor" } } }, | |
| + }, | |
| + "Write", | |
| + { file_path: "src/assigned/missing-role.txt", content: "must-not-write\n" }, | |
| + ); | |
| + assert.equal(missingTypedRole.outputJson?.decision, "block"); | |
| + assert.match(String(missingTypedRole.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); | |
| + | |
| + const conflictingTypedAliases = await preToolUse( | |
| + cwd, | |
| + sessionId, | |
| + { agent_id: childAgentId, agent_role: "executor", agent_type: "writer" }, | |
| + "Write", | |
| + { file_path: "src/assigned/conflicting-aliases.txt", content: "must-not-write\n" }, | |
| + ); | |
| + assert.equal(conflictingTypedAliases.outputJson?.decision, "block"); | |
| + assert.match(String(conflictingTypedAliases.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); | |
| + | |
| + await writeJson(trackingPath, { | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + [sessionId]: { | |
| + session_id: sessionId, | |
| + leader_thread_id: leaderThreadId, | |
| + updated_at: issuedAt.toISOString(), | |
| + threads: { | |
| + [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, | |
| + [childAgentId]: { ...trackedChild, mode: "writer" }, | |
| + }, | |
| + }, | |
| + }, | |
| + }); | |
| + const conflictingTrackerRole = await preToolUse( | |
| + cwd, | |
| + sessionId, | |
| + { agent_id: childAgentId, agent_role: "executor" }, | |
| + "Write", | |
| + { file_path: "src/assigned/conflicting-role.txt", content: "must-not-write\n" }, | |
| + ); | |
| + assert.equal(conflictingTrackerRole.outputJson?.decision, "block"); | |
| + assert.match(String(conflictingTrackerRole.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("allows only an exact live typed-child path grant and keeps all negative contracts fail-closed", async () => { | |
| + const fixture = await createActiveConductorFixture(); | |
| + const { cwd, stateDir, sessionId, leaderThreadId, childAgentId } = fixture; | |
| + const assignmentPath = join( | |
| + stateDir, | |
| + "sessions", | |
| + sessionId, | |
| + "native-assignments", | |
| + `${encodeURIComponent(childAgentId)}.json`, | |
| + ); | |
| + const issuedAt = new Date(); | |
| + const expiresAt = new Date(issuedAt.getTime() + 10 * 60_000); | |
| + | |
| + try { | |
| + await writeJson(assignmentPath, { | |
| + schema_version: 1, | |
| + kind: "native-subagent-assignment", | |
| + lifecycle: "active", | |
| + root_session_id: sessionId, | |
| + root_thread_id: leaderThreadId, | |
| + child_agent_id: childAgentId, | |
| + agent_type: "executor", | |
| + allowed_actions: ["path-write"], | |
| + path_prefixes: ["src/assigned"], | |
| + issued_at: issuedAt.toISOString(), | |
| + expires_at: expiresAt.toISOString(), | |
| + bootstrap_generation: "bootstrap-generation-native-assignment", | |
| + }); | |
| + | |
| + const exactChild = { agent_id: childAgentId, agent_role: "executor" }; | |
| + const allowed = await preToolUse( | |
| + cwd, | |
| + sessionId, | |
| + exactChild, | |
| + "Write", | |
| + { file_path: "src/assigned/result.txt", content: "ok\n" }, | |
| + ); | |
| + assert.equal(allowed.outputJson, null); | |
| + | |
| + for (const [name, identity, filePath] of [ | |
| + ["outside-scope", exactChild, "src/outside.txt"], | |
| + ["assignment-self-mutation", exactChild, assignmentPath], | |
| + ["foreign-child", { agent_id: "agent-native-assignment-foreign", agent_role: "executor" }, "src/assigned/result.txt"], | |
| + ["role-mismatch", { agent_id: childAgentId, agent_role: "writer" }, "src/assigned/result.txt"], | |
| + ["ungranted-writer", { agent_id: "agent-native-assignment-writer", agent_role: "writer" }, "src/assigned/result.txt"], | |
| + ] as const) { | |
| + const denied = await preToolUse( | |
| + cwd, | |
| + sessionId, | |
| + identity, | |
| + "Write", | |
| + { file_path: filePath, content: `${name}\n` }, | |
| + ); | |
| + assert.equal(denied.outputJson?.decision, "block", name); | |
| + assert.match(String(denied.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, name); | |
| + } | |
| + | |
| + await writeJson(assignmentPath, { | |
| + schema_version: 1, | |
| + kind: "native-subagent-assignment", | |
| + lifecycle: "active", | |
| + root_session_id: "foreign-session", | |
| + root_thread_id: leaderThreadId, | |
| + child_agent_id: childAgentId, | |
| + agent_type: "executor", | |
| + allowed_actions: ["path-write"], | |
| + path_prefixes: ["src/assigned"], | |
| + issued_at: issuedAt.toISOString(), | |
| + expires_at: expiresAt.toISOString(), | |
| + bootstrap_generation: "bootstrap-generation-native-assignment", | |
| + unexpected_scope_expansion: true, | |
| + }); | |
| + const malformed = await preToolUse( | |
| + cwd, | |
| + sessionId, | |
| + exactChild, | |
| + "Write", | |
| + { file_path: "src/assigned/malformed.txt", content: "malformed\n" }, | |
| + ); | |
| + assert.equal(malformed.outputJson?.decision, "block"); | |
| + assert.match(String(malformed.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); | |
| + | |
| + await mkdir(join(cwd, "src", "assigned"), { recursive: true }); | |
| + const escapedTarget = await mkdtemp(join(tmpdir(), "omx-native-assignment-escape-")); | |
| + await symlink(escapedTarget, join(cwd, "src", "assigned", "link")); | |
| + await writeJson(assignmentPath, { | |
| + schema_version: 1, | |
| + kind: "native-subagent-assignment", | |
| + lifecycle: "active", | |
| + root_session_id: sessionId, | |
| + root_thread_id: leaderThreadId, | |
| + child_agent_id: childAgentId, | |
| + agent_type: "executor", | |
| + allowed_actions: ["path-write", "bash-write"], | |
| + path_prefixes: ["src/assigned"], | |
| + issued_at: issuedAt.toISOString(), | |
| + expires_at: expiresAt.toISOString(), | |
| + bootstrap_generation: "bootstrap-generation-native-assignment", | |
| + }); | |
| + const allowedBash = await preToolUse( | |
| + cwd, | |
| + sessionId, | |
| + exactChild, | |
| + "Bash", | |
| + { command: "printf ok > src/assigned/bash.txt" }, | |
| + ); | |
| + assert.equal(allowedBash.outputJson, null); | |
| + for (const [name, command] of [ | |
| + ["bash-outside-scope", "printf no > src/outside-bash.txt"], | |
| + ["bash-symlink-escape", "printf no > src/assigned/link/escaped.txt"], | |
| + ["bash-substitution-poison", "printf \"$(printf no)\" > src/assigned/poison.txt"], | |
| + ] as const) { | |
| + const denied = await preToolUse(cwd, sessionId, exactChild, "Bash", { command }); | |
| + assert.equal(denied.outputJson?.decision, "block", name); | |
| + assert.match(String(denied.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, name); | |
| + } | |
| + await rm(escapedTarget, { recursive: true, force: true }); | |
| + | |
| + await writeJson(assignmentPath, { | |
| + schema_version: 1, | |
| + kind: "native-subagent-assignment", | |
| + lifecycle: "active", | |
| + root_session_id: sessionId, | |
| + root_thread_id: leaderThreadId, | |
| + child_agent_id: childAgentId, | |
| + agent_type: "executor", | |
| + allowed_actions: ["path-write"], | |
| + path_prefixes: ["src/assigned"], | |
| + issued_at: new Date(issuedAt.getTime() - 20 * 60_000).toISOString(), | |
| + expires_at: new Date(issuedAt.getTime() - 10 * 60_000).toISOString(), | |
| + }); | |
| + const stale = await preToolUse( | |
| + cwd, | |
| + sessionId, | |
| + exactChild, | |
| + "Write", | |
| + { file_path: "src/assigned/stale.txt", content: "stale\n" }, | |
| + ); | |
| + assert.equal(stale.outputJson?.decision, "block"); | |
| + assert.match(String(stale.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); | |
| + | |
| + await writeJson(assignmentPath, { | |
| + schema_version: 1, | |
| + kind: "native-subagent-assignment", | |
| + lifecycle: "active", | |
| + root_session_id: sessionId, | |
| + root_thread_id: leaderThreadId, | |
| + child_agent_id: childAgentId, | |
| + agent_type: "executor", | |
| + allowed_actions: ["path-write", "bash-write"], | |
| + path_prefixes: ["."], | |
| + issued_at: issuedAt.toISOString(), | |
| + expires_at: expiresAt.toISOString(), | |
| + bootstrap_generation: "bootstrap-generation-native-assignment", | |
| + }); | |
| + for (const [name, toolName, toolInput] of [ | |
| + ["git-path", "Write", { file_path: ".git/config", content: "must-not-write\n" }], | |
| + ["omx-goal-path", "Write", { file_path: ".omx/ultragoal/goals.json", content: "{}\n" }], | |
| + ["git-bash", "Bash", { command: "printf no > .git/config" }], | |
| + ["omx-goal-bash", "Bash", { command: "printf no > .omx/ultragoal/goals.json" }], | |
| + ] as const) { | |
| + const denied = await preToolUse(cwd, sessionId, exactChild, toolName, toolInput); | |
| + assert.equal(denied.outputJson?.decision, "block", name); | |
| + assert.match(String(denied.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/, name); | |
| + } | |
| + | |
| + await writeJson(assignmentPath, { | |
| + schema_version: 1, | |
| + kind: "native-subagent-assignment", | |
| + lifecycle: "active", | |
| + root_session_id: sessionId, | |
| + root_thread_id: leaderThreadId, | |
| + child_agent_id: childAgentId, | |
| + agent_type: "executor", | |
| + allowed_actions: ["path-write"], | |
| + path_prefixes: ["src/assigned"], | |
| + issued_at: issuedAt.toISOString(), | |
| + expires_at: expiresAt.toISOString(), | |
| + }); | |
| + await chmod(assignmentPath, 0o666); | |
| + const writableByOthers = await preToolUse( | |
| + cwd, | |
| + sessionId, | |
| + exactChild, | |
| + "Write", | |
| + { file_path: "src/assigned/world-writable-grant.txt", content: "must-not-write\n" }, | |
| + ); | |
| + assert.equal(writableByOthers.outputJson?.decision, "block"); | |
| + assert.match(String(writableByOthers.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("allows only Main-root to issue an assignment in the exact active session scope", async () => { | |
| + const fixture = await createActiveConductorFixture(); | |
| + const { cwd, sessionId, leaderThreadId } = fixture; | |
| + | |
| + try { | |
| + const root = { agent_id: leaderThreadId, thread_id: leaderThreadId }; | |
| + | |
| + const rootAssignment = await preToolUse(cwd, sessionId, root, "Write", { | |
| + file_path: `.omx/state/sessions/${sessionId}/native-assignments/root-created.json`, | |
| + content: "{}\n", | |
| + }); | |
| + assert.equal(rootAssignment.outputJson, null, "root-assignment-create"); | |
| + const foreignSessionAssignment = await preToolUse(cwd, sessionId, root, "Write", { | |
| + file_path: ".omx/state/sessions/foreign-session/native-assignments/root-created.json", | |
| + content: "{}\n", | |
| + }); | |
| + assert.equal(foreignSessionAssignment.outputJson?.decision, "block", "foreign-session-assignment"); | |
| + const protectedGate = await preToolUse(cwd, sessionId, root, "Write", { | |
| + file_path: `.omx/state/sessions/${sessionId}/ultragoal-state.json`, | |
| + content: "{}\n", | |
| + }); | |
| + assert.equal(protectedGate.outputJson?.decision, "block", "protected-state-gate"); | |
| + | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| +}); | |
| diff --git a/src/scripts/__tests__/codex-native-hook.test.ts b/src/scripts/__tests__/codex-native-hook.test.ts | |
| index 59ae97fc..13d782dc 100644 | |
| --- a/src/scripts/__tests__/codex-native-hook.test.ts | |
| +++ b/src/scripts/__tests__/codex-native-hook.test.ts | |
| @@ -33684,6 +33684,125 @@ PY`, | |
| } | |
| }); | |
| + it("allows only exact leader-owned Ultragoal metadata CLI commands", async () => withCleanAmbientNodeRuntimeEnvironment(async () => { | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-leader-metadata-")); | |
| + const originalPath = process.env.PATH; | |
| + const originalOmxEnvironment = Object.fromEntries( | |
| + Object.entries(process.env).filter(([name]) => /^(?:OMX|GJC)_/.test(name)), | |
| + ); | |
| + try { | |
| + for (const name of Object.keys(process.env)) { | |
| + if (/^(?:OMX|GJC)_/.test(name)) delete process.env[name]; | |
| + } | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-ultragoal-leader-metadata"; | |
| + const leaderThreadId = "thread-ultragoal-leader-metadata"; | |
| + await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); | |
| + await writeCanonicalLeaderFixture(stateDir, sessionId, leaderThreadId, cwd); | |
| + await writeSessionSkillActiveState(stateDir, sessionId, "ultragoal", "planning"); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { | |
| + active: true, | |
| + mode: "ultragoal", | |
| + current_phase: "planning", | |
| + session_id: sessionId, | |
| + }); | |
| + const trustedPackageBin = join(cwd, "node_modules", ".bin", "omx"); | |
| + await mkdir(dirname(trustedPackageBin), { recursive: true }); | |
| + await symlink(realpathSync(resolve(process.cwd(), "dist", "cli", "omx.js")), trustedPackageBin); | |
| + const lookalikeOmx = join(cwd, "lookalike", "bin", "omx"); | |
| + await mkdir(dirname(lookalikeOmx), { recursive: true }); | |
| + await writeFile(lookalikeOmx, "#!/bin/sh\nexit 0\n", "utf-8"); | |
| + await chmod(lookalikeOmx, 0o755); | |
| + process.env.PATH = `${dirname(trustedPackageBin)}:${dirname(process.execPath)}:/usr/bin:/bin`; | |
| + process.env.OMX_ROOT = cwd; | |
| + | |
| + const dispatchBash = (command: string, child = false) => dispatchCodexNativeHook({ | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: child ? "thread-ultragoal-metadata-child" : leaderThreadId, | |
| + agent_id: child ? "thread-ultragoal-metadata-child" : leaderThreadId, | |
| + ...(child ? { agent_role: "executor" } : {}), | |
| + tool_name: "Bash", | |
| + tool_input: { command }, | |
| + }, { cwd }); | |
| + | |
| + for (const command of [ | |
| + "omx ultragoal create-goals --brief 'Ship the scoped change' --goal 'Implement::Make the change' --goal 'Verify::Run tests' --codex-goal-mode per-story --json", | |
| + `${trustedPackageBin} ultragoal create-goals --brief 'Ship the scoped change through the installed CLI' --json`, | |
| + "omx ultragoal create --brief='Ship the scoped change' --json", | |
| + "omx ultragoal complete-goals --retry-failed --json", | |
| + "omx ultragoal complete --json", | |
| + "omx ultragoal next", | |
| + "omx ultragoal start-next --retry-failed", | |
| + "omx ultragoal status --codex-goal-json .omx/ultragoal/codex-goal.json --json", | |
| + "omx ultragoal steer --kind annotate_ledger --evidence 'executor and verification completed' --rationale 'make completed chronology durable before review' --json", | |
| + "omx ultragoal steer --kind=annotate_ledger --target-goal-id G001-safe --evidence 'executor and verification completed' --rationale 'make completed chronology durable before review' --json", | |
| + 'omx ultragoal steer --kind annotate_ledger --evidence "planning activated; stdout UTF-8 JSON string was ?? prompt.txt followed by newline; exit code was 0" --rationale "make evidence durable before review" --json', | |
| + 'omx ultragoal steer --kind annotate_ledger --evidence "stdout UTF-8 JSON string \\"?? prompt.txt\\\\n\\" with exit code 0" --rationale "make evidence durable before review" --json', | |
| + "omx ultragoal checkpoint --goal-id G001-safe --status complete --evidence 'tests and reviews passed' --codex-goal-json .omx/ultragoal/codex-goal.json --quality-gate-json .omx/ultragoal/quality-gate.json --json", | |
| + "omx ultragoal checkpoint --goal-id G001-safe --status failed --evidence 'executor command was blocked' --json", | |
| + ]) { | |
| + const result = await dispatchBash(command); | |
| + assert.equal(result.outputJson, null, command); | |
| + } | |
| + | |
| + const child = await dispatchBash("omx ultragoal create-goals --brief 'Child must not create' --json", true); | |
| + assert.equal(child.outputJson?.decision, "block"); | |
| + assert.match(String(child.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); | |
| + | |
| + for (const command of [ | |
| + "omx ultragoal create-goals --brief 'Forced overwrite' --force --json", | |
| + "omx ultragoal create-goals --brief-file plan.md --json", | |
| + "omx ultragoal create-goals positional-brief --json", | |
| + "omx ultragoal create-goals --brief 'Unknown option' --unknown --json", | |
| + "omx ultragoal create-goals --brief legit --goal --force --json", | |
| + "omx ultragoal create-goals --brief --force --json", | |
| + "omx ultragoal create-goals --brief legit --codex-goal-mode --force --json", | |
| + "omx ultragoal create-goals --brief \"$(touch src/pwned.ts)\" --json", | |
| + "omx ultragoal create-goals --brief 'Chained' --json; touch src/pwned.ts", | |
| + "PATH=/usr/bin:/bin omx ultragoal create-goals --brief 'Prefixed' --json", | |
| + "./omx ultragoal create-goals --brief 'Impostor' --json", | |
| + `${lookalikeOmx} ultragoal create-goals --brief 'Absolute impostor' --json`, | |
| + "omx ultragoal complete-goals --force --json", | |
| + "omx ultragoal complete-goals --retry-failed=1 --json", | |
| + "omx ultragoal status --codex-goal-json --json", | |
| + "omx ultragoal status --codex-goal-json $(touch src/pwned.ts) --json", | |
| + "omx ultragoal steer --kind add_subgoal --evidence evidence --rationale rationale --json", | |
| + "omx ultragoal steer --kind annotate_ledger --evidence evidence --json", | |
| + "omx ultragoal steer --kind annotate_ledger --evidence \"$(touch src/pwned.ts)\" --rationale rationale --json", | |
| + "omx ultragoal steer --kind annotate_ledger --evidence ?? --rationale rationale --json", | |
| + "omx ultragoal steer --kind annotate_ledger --evidence evidence --rationale rationale --json; touch src/pwned.ts", | |
| + "omx ultragoal checkpoint --goal-id G001-unsafe --status complete --evidence incomplete --codex-goal-json .omx/ultragoal/codex-goal.json --json", | |
| + "omx ultragoal checkpoint --goal-id G001-unsafe --status failed --evidence bad --quality-gate-json .omx/ultragoal/quality-gate.json --json", | |
| + "omx ultragoal checkpoint --goal-id G001-unsafe --status unexpected --evidence bad --json", | |
| + ]) { | |
| + const result = await dispatchBash(command); | |
| + assert.equal(result.outputJson?.decision, "block", command); | |
| + assert.match(String(result.outputJson?.reason ?? ""), /Main-root Conductor mode is active/, command); | |
| + } | |
| + | |
| + const rawWrite = await dispatchCodexNativeHook({ | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: leaderThreadId, | |
| + agent_id: leaderThreadId, | |
| + tool_name: "Write", | |
| + tool_input: { file_path: "src/pwned.ts", content: "owned\n" }, | |
| + }, { cwd }); | |
| + assert.equal(rawWrite.outputJson?.decision, "block"); | |
| + } finally { | |
| + for (const name of Object.keys(process.env)) { | |
| + if (/^(?:OMX|GJC)_/.test(name)) delete process.env[name]; | |
| + } | |
| + Object.assign(process.env, originalOmxEnvironment); | |
| + if (originalPath === undefined) delete process.env.PATH; | |
| + else process.env.PATH = originalPath; | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + })); | |
| + | |
| it("allows the exact installed omx CLI by absolute path for Main-root read-only commands (#3333)", async () => { | |
| const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-absolute-cli-readonly-")); | |
| const installRoot = await mkdtemp(join(tmpdir(), "omx-native-hook-user-install-")); | |
| @@ -34391,7 +34510,7 @@ PY`, | |
| assert.doesNotMatch(String(metadataBash.outputJson?.reason ?? ""), /Main-root/, command); | |
| } | |
| - const readOnlyBash = await dispatchCodexNativeHook( | |
| + const dispatchChildBash = (command: string) => dispatchCodexNativeHook( | |
| { | |
| hook_event_name: "PreToolUse", | |
| cwd, | |
| @@ -34399,11 +34518,36 @@ PY`, | |
| thread_id: childThreadId, | |
| agent_role: "executor", | |
| tool_name: "Bash", | |
| - tool_input: { command: "cat src/conductor-owned.ts" }, | |
| + tool_input: { command }, | |
| }, | |
| { cwd }, | |
| ); | |
| - assert.equal(readOnlyBash.outputJson, null); | |
| + const inheritedGitRuntime = Object.entries(process.env).filter(([name]) => ( | |
| + name === "BASH_FUNC_git%%" || name.startsWith("GIT_") | |
| + )); | |
| + for (const [name] of inheritedGitRuntime) delete process.env[name]; | |
| + try { | |
| + for (const command of [ | |
| + "cat src/conductor-owned.ts", | |
| + "/usr/bin/sed -n '1,28{=;p;}' .omx/ultragoal/goals.json", | |
| + "/usr/bin/find .omx -type f -print", | |
| + "/usr/bin/find . -path ./.git -prune -o -path ./.omx -prune -o -type f -print", | |
| + "/usr/bin/git --no-pager diff --no-ext-diff --no-textconv -- .", | |
| + ]) { | |
| + assert.equal((await dispatchChildBash(command)).outputJson, null, command); | |
| + } | |
| + for (const command of [ | |
| + "rg --files -uu .omx", | |
| + "rg -n -i 'goal|review' .omx/ultragoal", | |
| + "sed -i '' -e 's/a/b/' prompt.txt", | |
| + "find .omx -type f -exec sh -c 'printf pwn > prompt.txt' \\;", | |
| + "/usr/bin/git --no-pager diff --no-ext-diff --no-textconv -- .; git reset --hard", | |
| + ]) { | |
| + assert.equal((await dispatchChildBash(command)).outputJson?.decision, "block", command); | |
| + } | |
| + } finally { | |
| + for (const [name, value] of inheritedGitRuntime) process.env[name] = value; | |
| + } | |
| } finally { | |
| if (originalOmxRoot === undefined) delete process.env.OMX_ROOT; | |
| else process.env.OMX_ROOT = originalOmxRoot; | |
| diff --git a/src/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.ts b/src/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.ts | |
| index bc832ed9..8313fcdc 100644 | |
| --- a/src/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.ts | |
| +++ b/src/scripts/__tests__/issue-3311-ultragoal-native-outside-tmux.test.ts | |
| @@ -1,18 +1,32 @@ | |
| import assert from "node:assert/strict"; | |
| import { existsSync, readFileSync } from "node:fs"; | |
| -import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; | |
| +import { appendFile, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; | |
| import { tmpdir } from "node:os"; | |
| import { dirname, join } from "node:path"; | |
| -import { afterEach, describe, it } from "node:test"; | |
| -import { dispatchCodexNativeHook } from "../codex-native-hook.js"; | |
| +import { afterEach, beforeEach, describe, it } from "node:test"; | |
| +import { | |
| + __isAuthorizedActiveUltragoalPhaseTransitionFallbackForTests, | |
| + buildConductorPreToolUseWriteGuardOutput, | |
| + dispatchCodexNativeHook, | |
| +} from "../codex-native-hook.js"; | |
| +import { | |
| + beginNativeUltragoalBootstrap, | |
| + findReadyLocalNativeExecutorAssignment, | |
| + issueLocalNativeExecutorAssignment, | |
| + markNativeUltragoalBootstrapActive, | |
| + prepareNativeUltragoalBootstrapActivation, | |
| + readNativeUltragoalBootstrap, | |
| + revokeLocalNativeExecutorAssignment, | |
| + validateLocalNativeExecutorIdentity, | |
| +} from "../../ultragoal/native-bootstrap.js"; | |
| // Regression coverage for OMX #3311: on native Codex App / native-hook | |
| // surfaces outside tmux, standalone Ultragoal must not activate an | |
| // execution-blocking Main-root Conductor state when no authorized executor | |
| -// will ever be reachable (Team is tmux-only; native child/descendant | |
| -// provenance intentionally grants no write authority per #3127). The guard | |
| -// under test refuses the *activation* write itself; it must never affect | |
| -// already-active sessions, non-ultragoal modes, non-native launchers, or | |
| +// will ever be reachable. Team is tmux-only; native child/descendant provenance | |
| +// grants no write authority unless the root issues an exact scoped assignment. | |
| +// The guard under test refuses the *activation* write itself; it must never | |
| +// affect already-active sessions, non-ultragoal modes, non-native launchers, or | |
| // attached-tmux transport. | |
| async function writeJson(path: string, value: unknown): Promise<void> { | |
| @@ -35,6 +49,8 @@ async function writeLeaderSessionFixture( | |
| await writeJson(join(stateDir, "session.json"), { | |
| session_id: sessionId, | |
| native_session_id: leaderThreadId, | |
| + owner_codex_session_id: sessionId, | |
| + target_session_id: sessionId, | |
| cwd, | |
| }); | |
| await writeJson(join(stateDir, "subagent-tracking.json"), { | |
| @@ -43,20 +59,89 @@ async function writeLeaderSessionFixture( | |
| [sessionId]: { | |
| session_id: sessionId, | |
| leader_thread_id: leaderThreadId, | |
| + updated_at: "2026-07-29T16:54:54.000Z", | |
| threads: { | |
| - [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, | |
| + [leaderThreadId]: { | |
| + thread_id: leaderThreadId, | |
| + kind: "leader", | |
| + first_seen_at: "2026-07-29T16:54:54.000Z", | |
| + last_seen_at: "2026-07-29T16:54:54.000Z", | |
| + turn_count: 1, | |
| + }, | |
| }, | |
| }, | |
| }, | |
| }); | |
| } | |
| +function rootTranscriptRecord( | |
| + sessionId: string, | |
| + cwd: string, | |
| + turnId: string, | |
| + overrides: Record<string, unknown> = {}, | |
| + turnOverrides: Record<string, unknown> = {}, | |
| +): string { | |
| + return `${JSON.stringify({ | |
| + type: "session_meta", | |
| + payload: { | |
| + id: sessionId, | |
| + cwd, | |
| + thread_source: "user", | |
| + source: "vscode", | |
| + originator: "Codex Desktop", | |
| + dynamic_tools: [ | |
| + { name: "codex_app" }, | |
| + { name: "plugin_management" }, | |
| + ], | |
| + ...overrides, | |
| + }, | |
| + })}\n${JSON.stringify({ | |
| + type: "turn_context", | |
| + payload: { | |
| + turn_id: turnId, | |
| + cwd, | |
| + collaboration_mode: { mode: "default", settings: { model: "gpt-5.6-sol" } }, | |
| + multi_agent_mode: "explicitRequestOnly", | |
| + multi_agent_version: "v2", | |
| + ...turnOverrides, | |
| + }, | |
| + })}\n`; | |
| +} | |
| + | |
| +async function writeRootTranscript(codexHome: string, sessionId: string, content: string): Promise<string> { | |
| + const path = join(codexHome, "sessions", "2026", "07", "29", `rollout-2026-07-29T20-00-00-${sessionId}.jsonl`); | |
| + await mkdir(dirname(path), { recursive: true }); | |
| + await writeFile(path, content); | |
| + return path; | |
| +} | |
| + | |
| describe("issue-3311: standalone Ultragoal native-App outside-tmux activation guard", () => { | |
| const originalTmux = process.env.TMUX; | |
| + const originalAssignmentOptIn = process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS; | |
| + const originalCodexHome = process.env.CODEX_HOME; | |
| + const originalBackend = process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + const originalCodexThreadId = process.env.CODEX_THREAD_ID; | |
| + const originalOmxSessionId = process.env.OMX_SESSION_ID; | |
| + | |
| + beforeEach(() => { | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "off"; | |
| + delete process.env.CODEX_THREAD_ID; | |
| + delete process.env.OMX_SESSION_ID; | |
| + }); | |
| afterEach(() => { | |
| if (originalTmux === undefined) delete process.env.TMUX; | |
| else process.env.TMUX = originalTmux; | |
| + if (originalAssignmentOptIn === undefined) delete process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS; | |
| + else process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = originalAssignmentOptIn; | |
| + if (originalCodexHome === undefined) delete process.env.CODEX_HOME; | |
| + else process.env.CODEX_HOME = originalCodexHome; | |
| + if (originalBackend === undefined) delete process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + else process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = originalBackend; | |
| + if (originalCodexThreadId === undefined) delete process.env.CODEX_THREAD_ID; | |
| + else process.env.CODEX_THREAD_ID = originalCodexThreadId; | |
| + if (originalOmxSessionId === undefined) delete process.env.OMX_SESSION_ID; | |
| + else process.env.OMX_SESSION_ID = originalOmxSessionId; | |
| }); | |
| it("blocks fresh standalone-ultragoal Conductor activation on native App outside tmux (Bash state write)", async () => { | |
| @@ -88,8 +173,73 @@ describe("issue-3311: standalone Ultragoal native-App outside-tmux activation gu | |
| (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null) | |
| ?.hookSpecificOutput?.additionalContext || "", | |
| ); | |
| - assert.match(context, /native child\/descendant provenance does not grant write authority/); | |
| - assert.doesNotMatch(context, /write-assignment|assignment-backed|scoped native .*grant/i); | |
| + assert.match(context, /typed native spawn surface backed by scoped write assignments/); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("keeps fresh activation inactive until the advertised executor is authorized", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3319-supported-bash-")); | |
| + try { | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-3319-supported-bash"; | |
| + const leaderThreadId = "thread-3319-supported-bash"; | |
| + await writeLeaderSessionFixture(stateDir, sessionId, leaderThreadId, cwd); | |
| + | |
| + const result = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: leaderThreadId, | |
| + agent_id: leaderThreadId, | |
| + source: "native", | |
| + available_tools: ["collaboration.spawn_agent"], | |
| + tool_name: "Bash", | |
| + tool_input: { command: `omx state write --input '${ACTIVATION_INPUT}' --json` }, | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + | |
| + assert.equal(result.outputJson?.decision, "block"); | |
| + assert.match(String(result.outputJson?.reason ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("keeps fresh activation blocked when native subagents are advertised without typed role routing", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3319-untyped-bash-")); | |
| + try { | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-3319-untyped-bash"; | |
| + const leaderThreadId = "thread-3319-untyped-bash"; | |
| + await writeLeaderSessionFixture(stateDir, sessionId, leaderThreadId, cwd); | |
| + | |
| + const result = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: leaderThreadId, | |
| + agent_id: leaderThreadId, | |
| + source: "native", | |
| + capabilities: { native_subagents: true }, | |
| + tool_name: "Bash", | |
| + tool_input: { command: `omx state write --input '${ACTIVATION_INPUT}' --json` }, | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + | |
| + assert.equal(result.outputJson?.decision, "block"); | |
| + assert.match(String(result.outputJson?.reason ?? ""), /OMX-ULTRAGOAL-(?:NO-OWNER|BOOTSTRAP-REQUIRED)/); | |
| } finally { | |
| await rm(cwd, { recursive: true, force: true }); | |
| } | |
| @@ -124,8 +274,7 @@ describe("issue-3311: standalone Ultragoal native-App outside-tmux activation gu | |
| (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null) | |
| ?.hookSpecificOutput?.additionalContext || "", | |
| ); | |
| - assert.match(context, /native child\/descendant provenance does not grant write authority/); | |
| - assert.doesNotMatch(context, /write-assignment|assignment-backed|scoped native .*grant/i); | |
| + assert.match(context, /typed native spawn surface backed by scoped write assignments/); | |
| } finally { | |
| await rm(cwd, { recursive: true, force: true }); | |
| } | |
| @@ -163,8 +312,7 @@ describe("issue-3311: standalone Ultragoal native-App outside-tmux activation gu | |
| (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null) | |
| ?.hookSpecificOutput?.additionalContext || "", | |
| ); | |
| - assert.match(context, /native child\/descendant provenance does not grant write authority/); | |
| - assert.doesNotMatch(context, /write-assignment|assignment-backed|scoped native .*grant/i); | |
| + assert.match(context, /typed native spawn surface backed by scoped write assignments/); | |
| } finally { | |
| await rm(cwd, { recursive: true, force: true }); | |
| } | |
| @@ -336,6 +484,130 @@ describe("issue-3311: standalone Ultragoal native-App outside-tmux activation gu | |
| } | |
| }); | |
| + it("does not let a tracked native child authorize its own bootstrap assignment", async () => { | |
| + delete process.env.TMUX; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-bootstrap-self-auth-")); | |
| + try { | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-bootstrap-self-auth"; | |
| + const leaderThreadId = "thread-bootstrap-self-auth-root"; | |
| + const childThreadId = "thread-bootstrap-self-auth-child"; | |
| + await writeLeaderSessionFixture(stateDir, sessionId, leaderThreadId, cwd); | |
| + await writeJson(join(stateDir, "subagent-tracking.json"), { | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + [sessionId]: { | |
| + session_id: sessionId, | |
| + leader_thread_id: leaderThreadId, | |
| + threads: { | |
| + [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, | |
| + [childThreadId]: { | |
| + thread_id: childThreadId, | |
| + kind: "subagent", | |
| + mode: "executor", | |
| + status: "available", | |
| + provenance_kind: "native_subagent", | |
| + direct_child_parent_id: leaderThreadId, | |
| + direct_child_root_id: leaderThreadId, | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + }); | |
| + | |
| + const result = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: childThreadId, | |
| + agent_id: childThreadId, | |
| + agent_type: "executor", | |
| + source: "native", | |
| + tool_name: "Bash", | |
| + tool_input: { | |
| + command: `omx ultragoal native-bootstrap authorize --session-id ${sessionId} --root-thread-id ${leaderThreadId} --child-id ${childThreadId} --path-prefix src`, | |
| + }, | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + | |
| + assert.equal(result.outputJson?.decision, "block"); | |
| + assert.match(String(result.outputJson?.reason ?? ""), /OWNER_CONFIRMATION_REQUIRED/); | |
| + assert.match(String(result.outputJson?.reason ?? ""), /root-owned/); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("keeps the spawned executor read-only while the bootstrap is awaiting authorization", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-bootstrap-pending-child-")); | |
| + try { | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-bootstrap-pending-child"; | |
| + const leaderThreadId = "thread-bootstrap-pending-root"; | |
| + const childThreadId = "thread-bootstrap-pending-child"; | |
| + await writeLeaderSessionFixture(stateDir, sessionId, leaderThreadId, cwd); | |
| + await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: leaderThreadId, | |
| + source: "native", | |
| + available_tools: ["collaboration.spawn_agent"], | |
| + prompt: "$ultragoal execute this plan", | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + await writeJson(join(stateDir, "subagent-tracking.json"), { | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + [sessionId]: { | |
| + session_id: sessionId, | |
| + leader_thread_id: leaderThreadId, | |
| + threads: { | |
| + [leaderThreadId]: { thread_id: leaderThreadId, kind: "leader" }, | |
| + [childThreadId]: { | |
| + thread_id: childThreadId, | |
| + kind: "subagent", | |
| + mode: "executor", | |
| + status: "available", | |
| + provenance_kind: "native_subagent", | |
| + direct_child_parent_id: leaderThreadId, | |
| + direct_child_root_id: leaderThreadId, | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + }); | |
| + | |
| + const result = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: childThreadId, | |
| + agent_id: childThreadId, | |
| + agent_type: "executor", | |
| + source: "native", | |
| + tool_name: "Write", | |
| + tool_input: { file_path: join(cwd, "src", "pending-write.ts"), content: "blocked" }, | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + | |
| + assert.equal(result.outputJson?.decision, "block"); | |
| + assert.match(String(result.outputJson?.reason ?? ""), /bootstrap is not active/); | |
| + assert.match(String(result.outputJson?.reason ?? ""), /remain read-only/); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| it("does not block autopilot-supervised ultragoal activation (mode is autopilot, not standalone ultragoal)", async () => { | |
| delete process.env.TMUX; | |
| const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3311-autopilot-")); | |
| @@ -427,163 +699,1667 @@ describe("issue-3311: standalone Ultragoal native-App outside-tmux activation gu | |
| } | |
| }); | |
| - it("allows the primary '$ultragoal' UserPromptSubmit activation when attached to tmux (Team is reachable)", async () => { | |
| - process.env.TMUX = "/tmp/tmux-3311-prompt,12345,0"; | |
| - const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3311-prompt-tmux-")); | |
| + it("keeps '$ultragoal' prompt activation inactive until the advertised executor is authorized", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3319-supported-prompt-")); | |
| try { | |
| await mkdir(join(cwd, ".omx", "state"), { recursive: true }); | |
| - | |
| const result = await dispatchCodexNativeHook( | |
| { | |
| hook_event_name: "UserPromptSubmit", | |
| cwd, | |
| - session_id: "sess-3311-prompt-tmux", | |
| - thread_id: "thread-3311-prompt-tmux", | |
| - turn_id: "turn-3311-prompt-tmux", | |
| + session_id: "sess-3319-supported-prompt", | |
| + thread_id: "thread-3319-supported-prompt", | |
| + turn_id: "turn-3319-supported-prompt", | |
| source: "native", | |
| + available_tools: ["collaboration.spawn_agent"], | |
| prompt: "$ultragoal split this launch into durable goals", | |
| }, | |
| { cwd }, | |
| ); | |
| - assert.equal(result.skillState?.skill, "ultragoal"); | |
| - assert.equal(result.skillState?.initialized_mode, "ultragoal"); | |
| - assert.equal(result.skillState?.transition_error, undefined); | |
| + assert.notEqual(result.skillState?.initialized_mode, "ultragoal"); | |
| + assert.match(String(result.skillState?.transition_error ?? result.outputJson?.reason ?? ""), /BOOTSTRAP|inactive|authorization/i); | |
| assert.equal( | |
| - existsSync(join(cwd, ".omx", "state", "sessions", "sess-3311-prompt-tmux", "ultragoal-state.json")), | |
| - true, | |
| + existsSync(join(cwd, ".omx", "state", "sessions", "sess-3319-supported-prompt", "ultragoal-state.json")), | |
| + false, | |
| ); | |
| } finally { | |
| await rm(cwd, { recursive: true, force: true }); | |
| } | |
| }); | |
| - it("does not re-block continuation of an already-active '$ultragoal' UserPromptSubmit session outside tmux", async () => { | |
| + it("recognizes the Codex App v2 multi-agent marker when tool inventory is omitted", async () => { | |
| delete process.env.TMUX; | |
| - const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3311-prompt-continue-")); | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-app-v2-prompt-")); | |
| try { | |
| const stateDir = join(cwd, ".omx", "state"); | |
| - const sessionId = "sess-3311-prompt-continue"; | |
| - const leaderThreadId = "thread-3311-prompt-continue"; | |
| - await writeLeaderSessionFixture(stateDir, sessionId, leaderThreadId, cwd); | |
| - await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { | |
| - active: true, | |
| - skill: "ultragoal", | |
| - phase: "planning", | |
| - session_id: sessionId, | |
| - active_skills: [{ skill: "ultragoal", phase: "planning", active: true, session_id: sessionId }], | |
| - }); | |
| - await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { | |
| - active: true, | |
| - mode: "ultragoal", | |
| - current_phase: "planning", | |
| - session_id: sessionId, | |
| - }); | |
| + const sessionId = "sess-app-v2-prompt"; | |
| + const rootThreadId = "thread-app-v2-root"; | |
| + await writeLeaderSessionFixture(stateDir, sessionId, rootThreadId, cwd); | |
| const result = await dispatchCodexNativeHook( | |
| { | |
| hook_event_name: "UserPromptSubmit", | |
| cwd, | |
| session_id: sessionId, | |
| - thread_id: leaderThreadId, | |
| - turn_id: "turn-3311-prompt-continue", | |
| + thread_id: rootThreadId, | |
| source: "native", | |
| - prompt: "$ultragoal continue with the next milestone", | |
| + multi_agent_mode: "explicitRequestOnly", | |
| + multi_agent_version: "v2", | |
| + prompt: "$ultragoal execute the durable plan", | |
| }, | |
| { cwd }, | |
| ); | |
| - assert.doesNotMatch(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-NO-OWNER/); | |
| + assert.equal(result.skillState?.active, false); | |
| + assert.match(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/); | |
| + const bootstrap = await readNativeUltragoalBootstrap(stateDir, sessionId); | |
| + assert.equal(bootstrap?.status, "awaiting-executor"); | |
| + assert.equal(bootstrap?.root_thread_id, rootThreadId); | |
| } finally { | |
| await rm(cwd, { recursive: true, force: true }); | |
| } | |
| }); | |
| - it("does not block the primary '$ultragoal' UserPromptSubmit activation from a non-native (CLI) launcher outside tmux", async () => { | |
| + it("uses an exact safe root transcript despite a competing live workspace pointer", async () => { | |
| delete process.env.TMUX; | |
| - const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3311-prompt-cli-")); | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-transcript-pointer-")); | |
| try { | |
| - await mkdir(join(cwd, ".omx", "state"), { recursive: true }); | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "019f-root-transcript-session"; | |
| + const turnId = "019f-root-transcript-turn"; | |
| + const foreignSessionId = "019f-foreign-live-session"; | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + await writeJson(join(stateDir, "session.json"), { | |
| + session_id: foreignSessionId, | |
| + native_session_id: foreignSessionId, | |
| + cwd, | |
| + pid: process.pid, | |
| + }); | |
| + await writeJson(join(stateDir, "sessions", foreignSessionId, "skill-active-state.json"), { | |
| + version: 1, | |
| + active: false, | |
| + skill: "ultragoal", | |
| + phase: "complete", | |
| + session_id: foreignSessionId, | |
| + active_skills: [], | |
| + transition_error: "foreign terminal marker", | |
| + }); | |
| + await writeRootTranscript(process.env.CODEX_HOME, sessionId, rootTranscriptRecord(sessionId, cwd, turnId)); | |
| - const result = await dispatchCodexNativeHook( | |
| - { | |
| - hook_event_name: "UserPromptSubmit", | |
| - cwd, | |
| - session_id: "sess-3311-prompt-cli", | |
| - thread_id: "thread-3311-prompt-cli", | |
| - turn_id: "turn-3311-prompt-cli", | |
| - source: "cli", | |
| - prompt: "$ultragoal split this launch into durable goals", | |
| - }, | |
| - { cwd }, | |
| - ); | |
| + const prompt = await dispatchCodexNativeHook({ | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + turn_id: turnId, | |
| + source: "native", | |
| + prompt: "$ultragoal execute the durable plan", | |
| + }, { cwd }); | |
| - assert.equal(result.skillState?.initialized_mode, "ultragoal"); | |
| - assert.equal(result.skillState?.transition_error, undefined); | |
| - } finally { | |
| - await rm(cwd, { recursive: true, force: true }); | |
| - } | |
| - }); | |
| + assert.equal(prompt.skillState?.active, false); | |
| + assert.match(String(prompt.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/); | |
| + assert.doesNotMatch(JSON.stringify(prompt), /foreign terminal marker/); | |
| + const bootstrap = await readNativeUltragoalBootstrap(stateDir, sessionId); | |
| + assert.equal(bootstrap?.root_thread_id, sessionId); | |
| + assert.equal(bootstrap?.status, "awaiting-executor"); | |
| - // --- Autopilot-supervised child recognition must not be intercepted --- | |
| - // #3311 repair-2: the prompt guard must refuse only a truly *standalone* | |
| - // fresh Ultragoal activation. When Autopilot is already active and | |
| - // supervising a child phase (e.g. ralplan), a "$ultragoal" prompt is a | |
| - // supervised-child transition handled entirely by recordSkillActivation's | |
| - // own isAutopilotSupervisedChildSkill branch; it must remain reachable on | |
| - // native App outside tmux exactly as it was on the pre-#3311-fix baseline. | |
| + const bootstrapStatus = await dispatchCodexNativeHook({ | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + turn_id: turnId, | |
| + tool_name: "Bash", | |
| + tool_input: { command: "omx ultragoal native-bootstrap status --json" }, | |
| + }, { cwd }); | |
| + assert.equal(bootstrapStatus.outputJson, null); | |
| - it("does not intercept Autopilot-supervised '$ultragoal' child-phase recognition on native App outside tmux", async () => { | |
| - delete process.env.TMUX; | |
| - const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3311-autopilot-supervised-")); | |
| - try { | |
| - const stateDir = join(cwd, ".omx", "state"); | |
| - const sessionId = "sess-3311-autopilot-supervised"; | |
| - const threadId = "thread-3311-autopilot-supervised"; | |
| - await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); | |
| await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { | |
| + version: 1, | |
| active: true, | |
| - skill: "autopilot", | |
| - phase: "ralplan", | |
| + skill: "ultragoal", | |
| + phase: "executing", | |
| session_id: sessionId, | |
| - active_skills: [{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId }], | |
| + thread_id: sessionId, | |
| + active_skills: [{ skill: "ultragoal", active: true, phase: "executing", session_id: sessionId }], | |
| }); | |
| - await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { | |
| + await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { | |
| active: true, | |
| - mode: "autopilot", | |
| - current_phase: "ralplan", | |
| + mode: "ultragoal", | |
| + current_phase: "executing", | |
| session_id: sessionId, | |
| - state: { | |
| - handoff_artifacts: { | |
| - ralplan_consensus_gate: { required: true, complete: false }, | |
| - }, | |
| - }, | |
| }); | |
| + const write = await dispatchCodexNativeHook({ | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + turn_id: turnId, | |
| + tool_name: "Edit", | |
| + tool_input: { file_path: "src/root-write.ts" }, | |
| + }, { cwd }); | |
| + assert.equal(write.outputJson?.decision, "block"); | |
| + assert.match(String(write.outputJson?.reason ?? ""), /Main-root Conductor mode is active/); | |
| + for (const [name, identityClaim] of [ | |
| + ["agent-id", { agent_id: "claimed-agent" }], | |
| + ["agent-id-camel", { agentId: "claimed-agent" }], | |
| + ["owner", { owner_codex_thread_id: sessionId }], | |
| + ["agent-role", { agent_role: "executor" }], | |
| + ["agent-role-camel", { agentRole: "executor" }], | |
| + ["agent-type", { agent_type: "executor" }], | |
| + ["agent-type-camel", { agentType: "executor" }], | |
| + ["thread-number", { thread_id: 42 }], | |
| + ["thread-empty", { thread_id: "" }], | |
| + ["thread-null", { thread_id: null }], | |
| + ] as const) { | |
| + const claimed = await dispatchCodexNativeHook({ | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: sessionId, | |
| + turn_id: turnId, | |
| + tool_name: "Edit", | |
| + tool_input: { file_path: "src/claimed-root-write.ts" }, | |
| + ...identityClaim, | |
| + }, { cwd }); | |
| + assert.equal(claimed.outputJson?.decision, "block", name); | |
| + assert.doesNotMatch(String(claimed.outputJson?.reason ?? ""), /Main-root Conductor mode is active/, name); | |
| + } | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| - const result = await dispatchCodexNativeHook( | |
| - { | |
| + it("accepts Codex Desktop exec root transcripts and rejects unknown source or originator", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-exec-source-")); | |
| + try { | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + await writeJson(join(stateDir, "session.json"), { | |
| + session_id: "019f-competing-exec-source", | |
| + native_session_id: "019f-competing-exec-source", | |
| + cwd, | |
| + pid: process.pid, | |
| + }); | |
| + for (const [name, transcriptOverrides, accepted] of [ | |
| + ["exec", { source: "exec" }, true], | |
| + ["unknown", { source: "unknown" }, false], | |
| + ["wrong-originator", { source: "exec", originator: "Other Client" }, false], | |
| + ] as const) { | |
| + const sessionId = `019f-root-source-${name}`; | |
| + const turnId = `019f-root-source-${name}-turn`; | |
| + await writeRootTranscript(process.env.CODEX_HOME, sessionId, rootTranscriptRecord( | |
| + sessionId, | |
| + cwd, | |
| + turnId, | |
| + transcriptOverrides, | |
| + )); | |
| + const result = await dispatchCodexNativeHook({ | |
| hook_event_name: "UserPromptSubmit", | |
| cwd, | |
| session_id: sessionId, | |
| - thread_id: threadId, | |
| - turn_id: "turn-3311-autopilot-supervised", | |
| + turn_id: turnId, | |
| source: "native", | |
| - prompt: "$ultragoal turn the approved plan into durable goals", | |
| - }, | |
| - { cwd }, | |
| - ); | |
| + prompt: "$ultragoal execute", | |
| + }, { cwd }); | |
| + if (accepted) { | |
| + assert.match(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/, name); | |
| + assert.equal((await readNativeUltragoalBootstrap(stateDir, sessionId))?.root_thread_id, sessionId, name); | |
| + } else { | |
| + assert.doesNotMatch(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/, name); | |
| + assert.equal(await readNativeUltragoalBootstrap(stateDir, sessionId), null, name); | |
| + } | |
| + } | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| - assert.equal(result.omxEventName, "keyword-detector"); | |
| - assert.equal(result.skillState?.active, true); | |
| - assert.equal(result.skillState?.skill, "autopilot"); | |
| - assert.equal(result.skillState?.supervised_child_skill, "ultragoal"); | |
| - assert.equal(result.skillState?.transition_error, undefined); | |
| - assert.doesNotMatch(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-NO-OWNER/); | |
| - const message = String( | |
| - (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null) | |
| - ?.hookSpecificOutput?.additionalContext || "", | |
| - ); | |
| - assert.doesNotMatch(message, /OMX-ULTRAGOAL-NO-OWNER/); | |
| + it("rejects child, spoofed, ambiguous, and archived root transcript fallbacks", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-transcript-reject-")); | |
| + try { | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + const cases = [ | |
| + ["child", { source: { subagent: { thread_spawn: { parent_thread_id: "parent" } } } }], | |
| + ["spoof", { cwd: join(cwd, "foreign") }], | |
| + ] as const; | |
| + for (const [name, overrides] of cases) { | |
| + const sessionId = `019f-root-${name}`; | |
| + const turnId = `019f-root-${name}-turn`; | |
| + await writeRootTranscript(process.env.CODEX_HOME, sessionId, rootTranscriptRecord(sessionId, cwd, turnId, overrides)); | |
| + const result = await dispatchCodexNativeHook({ | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: sessionId, | |
| + turn_id: turnId, | |
| + source: "native", | |
| + prompt: "$ultragoal execute", | |
| + }, { cwd }); | |
| + assert.doesNotMatch(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/, name); | |
| + assert.equal(await readNativeUltragoalBootstrap(join(cwd, ".omx", "state"), sessionId), null, name); | |
| + } | |
| + | |
| + const ambiguousId = "019f-root-ambiguous"; | |
| + const ambiguousTurnId = "019f-root-ambiguous-turn"; | |
| + await writeRootTranscript(process.env.CODEX_HOME, ambiguousId, rootTranscriptRecord(ambiguousId, cwd, ambiguousTurnId)); | |
| + const duplicate = join(process.env.CODEX_HOME, "sessions", "duplicate", `rollout-copy-${ambiguousId}.jsonl`); | |
| + await mkdir(dirname(duplicate), { recursive: true }); | |
| + await writeFile(duplicate, rootTranscriptRecord(ambiguousId, cwd, ambiguousTurnId)); | |
| + const archivedId = "019f-root-archived"; | |
| + const archivedTurnId = "019f-root-archived-turn"; | |
| + const archived = join(process.env.CODEX_HOME, "archived_sessions", `rollout-${archivedId}.jsonl`); | |
| + await mkdir(dirname(archived), { recursive: true }); | |
| + await writeFile(archived, rootTranscriptRecord(archivedId, cwd, archivedTurnId)); | |
| + | |
| + for (const [name, sessionId] of [["ambiguous", ambiguousId], ["archived", archivedId]] as const) { | |
| + const result = await dispatchCodexNativeHook({ | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: sessionId, | |
| + turn_id: name === "ambiguous" ? ambiguousTurnId : archivedTurnId, | |
| + source: "native", | |
| + prompt: "$ultragoal execute", | |
| + }, { cwd }); | |
| + assert.doesNotMatch(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/, name); | |
| + assert.equal(await readNativeUltragoalBootstrap(join(cwd, ".omx", "state"), sessionId), null, name); | |
| + } | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("does not trust session_meta dynamic tools or a stale turn_context", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-turn-context-reject-")); | |
| + try { | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + const sessionId = "019f-root-stale-context"; | |
| + const turnId = "019f-root-current-turn"; | |
| + await writeRootTranscript(process.env.CODEX_HOME, sessionId, rootTranscriptRecord( | |
| + sessionId, | |
| + cwd, | |
| + "019f-root-stale-turn", | |
| + { dynamic_tools: [{ name: "collaboration.spawn_agent" }] }, | |
| + )); | |
| + const result = await dispatchCodexNativeHook({ | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: sessionId, | |
| + turn_id: turnId, | |
| + source: "native", | |
| + prompt: "$ultragoal execute", | |
| + }, { cwd }); | |
| + assert.doesNotMatch(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/); | |
| + assert.equal(await readNativeUltragoalBootstrap(join(cwd, ".omx", "state"), sessionId), null); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("requires the exact latest turn_context for root Prompt and PreTool authority", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-current-turn-reject-")); | |
| + try { | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + await writeJson(join(stateDir, "session.json"), { | |
| + session_id: "019f-competing-current-turn", | |
| + native_session_id: "019f-competing-current-turn", | |
| + cwd, | |
| + pid: process.pid, | |
| + }); | |
| + for (const name of ["missing", "mismatched", "malformed-companion", "stale-latest", "partial-newer"] as const) { | |
| + const sessionId = `019f-root-turn-${name}`; | |
| + const currentTurnId = `019f-root-turn-${name}-current`; | |
| + let transcript = rootTranscriptRecord(sessionId, cwd, currentTurnId); | |
| + if (name === "stale-latest") { | |
| + transcript += `${JSON.stringify({ | |
| + type: "turn_context", | |
| + payload: { | |
| + turn_id: `${currentTurnId}-newer`, | |
| + cwd, | |
| + multi_agent_mode: "explicitRequestOnly", | |
| + multi_agent_version: "v2", | |
| + }, | |
| + })}\n`; | |
| + } else if (name === "partial-newer") { | |
| + transcript += "{\"type\":\"turn_context\",\"payload\":{"; | |
| + } | |
| + await writeRootTranscript(process.env.CODEX_HOME, sessionId, transcript); | |
| + const turnClaim = name === "missing" | |
| + ? {} | |
| + : name === "malformed-companion" | |
| + ? { turn_id: currentTurnId, turnId: 42 } | |
| + : { turn_id: name === "mismatched" ? `${currentTurnId}-foreign` : currentTurnId }; | |
| + const prompt = await dispatchCodexNativeHook({ | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + ...turnClaim, | |
| + source: "native", | |
| + prompt: "$ultragoal execute", | |
| + }, { cwd }); | |
| + assert.doesNotMatch(String(prompt.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/, name); | |
| + | |
| + await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { | |
| + version: 1, | |
| + active: true, | |
| + skill: "ultragoal", | |
| + phase: "executing", | |
| + session_id: sessionId, | |
| + active_skills: [{ skill: "ultragoal", active: true, phase: "executing", session_id: sessionId }], | |
| + }); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { | |
| + active: true, | |
| + mode: "ultragoal", | |
| + current_phase: "executing", | |
| + session_id: sessionId, | |
| + }); | |
| + | |
| + const preTool = await dispatchCodexNativeHook({ | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + ...turnClaim, | |
| + tool_name: "Edit", | |
| + tool_input: { file_path: "src/untrusted-root.ts" }, | |
| + }, { cwd }); | |
| + assert.equal(preTool.outputJson?.decision, "block", name); | |
| + assert.doesNotMatch(String(preTool.outputJson?.reason ?? ""), /Main-root Conductor mode is active/, name); | |
| + } | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("fails closed on conflicting root transcript aliases", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-alias-conflict-")); | |
| + try { | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + const cases = [ | |
| + ["session", { session_id: "same", sessionId: "foreign" }, {}], | |
| + ["session-malformed", { session_id: "same", sessionId: null }, {}], | |
| + ["thread-source", { threadSource: "subagent" }, {}], | |
| + ["thread-source-malformed", { threadSource: null }, {}], | |
| + ["turn", {}, { turnId: "foreign" }], | |
| + ["mode", {}, { multiAgentMode: "disabled" }], | |
| + ["version", {}, { multiAgentVersion: "disabled" }], | |
| + ] as const; | |
| + for (const [name, sessionOverrides, turnOverrides] of cases) { | |
| + const sessionId = `019f-root-alias-${name}`; | |
| + const turnId = `019f-root-alias-${name}-turn`; | |
| + const effectiveSessionOverrides = name.startsWith("session") | |
| + ? { ...sessionOverrides, session_id: sessionId } | |
| + : sessionOverrides; | |
| + await writeRootTranscript(process.env.CODEX_HOME, sessionId, rootTranscriptRecord( | |
| + sessionId, | |
| + cwd, | |
| + turnId, | |
| + effectiveSessionOverrides, | |
| + turnOverrides, | |
| + )); | |
| + const result = await dispatchCodexNativeHook({ | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: sessionId, | |
| + turn_id: turnId, | |
| + source: "native", | |
| + prompt: "$ultragoal execute", | |
| + }, { cwd }); | |
| + assert.doesNotMatch(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/, name); | |
| + assert.equal(await readNativeUltragoalBootstrap(join(cwd, ".omx", "state"), sessionId), null, name); | |
| + } | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("rejects a root transcript mutated after its pinned read", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-pinned-race-")); | |
| + try { | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + const sessionId = "019f-root-pinned-race"; | |
| + const turnId = "019f-root-pinned-race-turn"; | |
| + await writeRootTranscript(process.env.CODEX_HOME, sessionId, rootTranscriptRecord(sessionId, cwd, turnId)); | |
| + let postReadCalls = 0; | |
| + const result = await dispatchCodexNativeHook({ | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: sessionId, | |
| + turn_id: turnId, | |
| + source: "native", | |
| + prompt: "$ultragoal execute", | |
| + }, { | |
| + cwd, | |
| + rootTranscriptPostReadHook: async (path) => { | |
| + postReadCalls += 1; | |
| + await appendFile(path, `${JSON.stringify({ type: "event_msg", payload: { type: "mutation" } })}\n`); | |
| + }, | |
| + }); | |
| + assert.equal(postReadCalls, 1); | |
| + assert.doesNotMatch(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/); | |
| + assert.equal(await readNativeUltragoalBootstrap(join(cwd, ".omx", "state"), sessionId), null); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("fails closed when root transcript traversal errors or exhausts its entry cap", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-scan-incomplete-")); | |
| + try { | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + for (const name of ["error", "cap"] as const) { | |
| + const sessionId = `019f-root-scan-${name}`; | |
| + const turnId = `019f-root-scan-${name}-turn`; | |
| + await writeRootTranscript(process.env.CODEX_HOME, sessionId, rootTranscriptRecord(sessionId, cwd, turnId)); | |
| + const result = await dispatchCodexNativeHook({ | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + turn_id: turnId, | |
| + source: "native", | |
| + prompt: "$ultragoal execute", | |
| + }, name === "error" ? { | |
| + cwd, | |
| + rootTranscriptBeforeReadDirHook: () => { | |
| + throw new Error("injected_readdir_error"); | |
| + }, | |
| + } : { | |
| + cwd, | |
| + rootTranscriptScanMaxEntries: 1, | |
| + }); | |
| + assert.doesNotMatch(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/, name); | |
| + assert.equal(await readNativeUltragoalBootstrap(join(cwd, ".omx", "state"), sessionId), null, name); | |
| + } | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("authorizes an exact root Stop against an exact-cwd stale-dead pointer", async () => { | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-stop-stale-pointer-")); | |
| + try { | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "019f-root-stop-live"; | |
| + const turnId = "019f-root-stop-turn"; | |
| + await writeJson(join(stateDir, "session.json"), { | |
| + session_id: "019f-dead-competing-pointer", | |
| + native_session_id: "019f-dead-competing-pointer", | |
| + started_at: "2026-01-01T00:00:00.000Z", | |
| + cwd, | |
| + pid: 999_999, | |
| + platform: process.platform, | |
| + }); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { | |
| + version: 1, | |
| + active: true, | |
| + skill: "autopilot", | |
| + phase: "executing", | |
| + session_id: sessionId, | |
| + thread_id: sessionId, | |
| + active_skills: [{ skill: "autopilot", active: true, phase: "executing", session_id: sessionId }], | |
| + }); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { | |
| + active: true, | |
| + mode: "autopilot", | |
| + current_phase: "executing", | |
| + session_id: sessionId, | |
| + }); | |
| + await writeRootTranscript(process.env.CODEX_HOME, sessionId, rootTranscriptRecord(sessionId, cwd, turnId)); | |
| + | |
| + const result = await dispatchCodexNativeHook({ | |
| + hook_event_name: "Stop", | |
| + cwd, | |
| + session_id: sessionId, | |
| + turn_id: turnId, | |
| + }, { cwd }); | |
| + | |
| + assert.equal(result.outputJson?.decision, "block"); | |
| + assert.doesNotMatch(String(result.outputJson?.reason ?? ""), /selected session pointer is stale-dead/); | |
| + assert.match(String(result.outputJson?.reason ?? result.outputJson?.systemMessage ?? ""), /autopilot/i); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("keeps spoofed root Stop transcripts fail closed against a stale-dead pointer", async () => { | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-root-stop-reject-")); | |
| + try { | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + await writeJson(join(stateDir, "session.json"), { | |
| + session_id: "019f-dead-stop-pointer", | |
| + native_session_id: "019f-dead-stop-pointer", | |
| + started_at: "2026-01-01T00:00:00.000Z", | |
| + cwd, | |
| + pid: 999_999, | |
| + platform: process.platform, | |
| + }); | |
| + const cases = [ | |
| + ["spoof", { id: "foreign-root" }, {}], | |
| + ["child", { source: { subagent: { thread_spawn: { parent_thread_id: "parent" } } } }, {}], | |
| + ["aliases", { threadSource: "subagent" }, {}], | |
| + ["payload-aliases", {}, { sessionId: "foreign-session" }], | |
| + ["payload-session-malformed", {}, { sessionId: null }], | |
| + ["thread-mismatch", {}, { thread_id: "foreign-thread" }], | |
| + ["thread-conflict", {}, { thread_id: "__SESSION__", threadId: "foreign-thread" }], | |
| + ["thread-number", {}, { thread_id: 42 }], | |
| + ["thread-empty", {}, { thread_id: "" }], | |
| + ["thread-null", {}, { thread_id: null }], | |
| + ["agent-id", {}, { agent_id: "claimed-agent" }], | |
| + ["agent-id-camel", {}, { agentId: "claimed-agent" }], | |
| + ["agent-empty", {}, { agent_id: "" }], | |
| + ["agent-null", {}, { agent_id: null }], | |
| + ["agent-object", {}, { agent_id: { id: "claimed-agent" } }], | |
| + ["owner", {}, { owner_codex_thread_id: "claimed-owner" }], | |
| + ["owner-empty", {}, { owner_codex_thread_id: "" }], | |
| + ["owner-null", {}, { owner_codex_thread_id: null }], | |
| + ["owner-object", {}, { owner_codex_thread_id: { id: "claimed-owner" } }], | |
| + ["agent-role", {}, { agent_role: "executor" }], | |
| + ["agent-role-camel", {}, { agentRole: "executor" }], | |
| + ["agent-role-malformed", {}, { agent_role: null }], | |
| + ["agent-type", {}, { agent_type: "executor" }], | |
| + ["agent-type-camel", {}, { agentType: "executor" }], | |
| + ["agent-type-malformed", {}, { agent_type: { type: "executor" } }], | |
| + ["cwd", { cwd: join(cwd, "foreign") }, {}], | |
| + ] as const; | |
| + for (const [name, overrides, payloadOverrides] of cases) { | |
| + const sessionId = `019f-root-stop-${name}`; | |
| + const turnId = `019f-root-stop-${name}-turn`; | |
| + const effectivePayloadOverrides = Object.fromEntries( | |
| + Object.entries(payloadOverrides).map(([key, value]) => [key, value === "__SESSION__" ? sessionId : value]), | |
| + ); | |
| + await writeRootTranscript(process.env.CODEX_HOME, sessionId, rootTranscriptRecord(sessionId, cwd, turnId, overrides)); | |
| + const result = await dispatchCodexNativeHook({ | |
| + hook_event_name: "Stop", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: sessionId, | |
| + turn_id: turnId, | |
| + ...effectivePayloadOverrides, | |
| + }, { cwd }); | |
| + assert.equal(result.outputJson?.decision, "block", name); | |
| + assert.match(String(result.outputJson?.reason ?? ""), /selected session pointer is stale-dead/, name); | |
| + } | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("rejects mismatched and ambiguous Codex App transcript receipts", async () => { | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-app-v2-receipt-deny-")); | |
| + try { | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-app-v2-receipt-deny"; | |
| + const rootThreadId = "thread-app-v2-receipt-root"; | |
| + const wrongRoleChildId = "thread-app-v2-wrong-role"; | |
| + const conflictingRoleChildId = "thread-app-v2-conflicting-role"; | |
| + const ambiguousChildId = "thread-app-v2-ambiguous"; | |
| + const archivedChildId = "thread-app-v2-archived"; | |
| + const futureChildId = "thread-app-v2-future"; | |
| + const missingSessionChildId = "thread-app-v2-missing-session"; | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + await writeLeaderSessionFixture(stateDir, sessionId, rootThreadId, cwd); | |
| + | |
| + const receiptNotBefore = "2026-07-29T16:54:54.000Z"; | |
| + const receiptNotAfter = "2026-07-30T16:54:54.000Z"; | |
| + const transcript = ( | |
| + childAgentId: string, | |
| + role: string, | |
| + timestamp = receiptNotBefore, | |
| + transcriptSessionId = sessionId, | |
| + ) => `${JSON.stringify({ | |
| + timestamp, | |
| + type: "session_meta", | |
| + payload: { | |
| + id: childAgentId, | |
| + session_id: transcriptSessionId, | |
| + cwd, | |
| + source: { | |
| + subagent: { | |
| + thread_spawn: { | |
| + parent_thread_id: rootThreadId, | |
| + depth: 1, | |
| + agent_role: role, | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + })}\n`; | |
| + const wrongRolePath = join(process.env.CODEX_HOME, "sessions", `rollout-${wrongRoleChildId}.jsonl`); | |
| + await mkdir(dirname(wrongRolePath), { recursive: true }); | |
| + await writeFile(wrongRolePath, transcript(wrongRoleChildId, "reviewer")); | |
| + assert.deepEqual(await validateLocalNativeExecutorIdentity({ | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + childAgentId: wrongRoleChildId, | |
| + receiptNotBefore, | |
| + receiptNotAfter, | |
| + }), { ok: false, reason: "child_transcript_receipt_mismatch" }); | |
| + | |
| + const conflictingRolePath = join(process.env.CODEX_HOME, "sessions", `rollout-${conflictingRoleChildId}.jsonl`); | |
| + await writeFile(conflictingRolePath, transcript(conflictingRoleChildId, "executor")); | |
| + const tracking = JSON.parse(readFileSync(join(stateDir, "subagent-tracking.json"), "utf-8")) as { | |
| + sessions: Record<string, { threads: Record<string, Record<string, unknown>> }>; | |
| + }; | |
| + tracking.sessions[sessionId]!.threads[conflictingRoleChildId] = { | |
| + thread_id: conflictingRoleChildId, | |
| + kind: "subagent", | |
| + mode: "reviewer", | |
| + first_seen_at: receiptNotBefore, | |
| + last_seen_at: receiptNotBefore, | |
| + turn_count: 1, | |
| + }; | |
| + await writeJson(join(stateDir, "subagent-tracking.json"), tracking); | |
| + assert.deepEqual(await validateLocalNativeExecutorIdentity({ | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + childAgentId: conflictingRoleChildId, | |
| + receiptNotBefore, | |
| + receiptNotAfter, | |
| + }), { ok: false, reason: "child_role_mismatch" }); | |
| + | |
| + const livePath = join(process.env.CODEX_HOME, "sessions", "live", `rollout-${ambiguousChildId}.jsonl`); | |
| + const duplicatePath = join(process.env.CODEX_HOME, "sessions", "duplicate", `rollout-${ambiguousChildId}.jsonl`); | |
| + await mkdir(dirname(livePath), { recursive: true }); | |
| + await mkdir(dirname(duplicatePath), { recursive: true }); | |
| + await writeFile(livePath, transcript(ambiguousChildId, "executor")); | |
| + await writeFile(duplicatePath, transcript(ambiguousChildId, "executor")); | |
| + assert.deepEqual(await validateLocalNativeExecutorIdentity({ | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + childAgentId: ambiguousChildId, | |
| + receiptNotBefore, | |
| + receiptNotAfter, | |
| + }), { ok: false, reason: "child_transcript_receipt_ambiguous" }); | |
| + | |
| + const archivedPath = join(process.env.CODEX_HOME, "archived_sessions", `rollout-${archivedChildId}.jsonl`); | |
| + await mkdir(dirname(archivedPath), { recursive: true }); | |
| + await writeFile(archivedPath, transcript(archivedChildId, "executor")); | |
| + assert.deepEqual(await validateLocalNativeExecutorIdentity({ | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + childAgentId: archivedChildId, | |
| + receiptNotBefore, | |
| + receiptNotAfter, | |
| + }), { ok: false, reason: "child_transcript_receipt_missing" }); | |
| + | |
| + const futurePath = join(process.env.CODEX_HOME, "sessions", `rollout-${futureChildId}.jsonl`); | |
| + await writeFile(futurePath, transcript(futureChildId, "executor", "9999-01-01T00:00:00.000Z")); | |
| + assert.deepEqual(await validateLocalNativeExecutorIdentity({ | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + childAgentId: futureChildId, | |
| + receiptNotBefore, | |
| + receiptNotAfter: "9999-12-31T23:59:59.999Z", | |
| + }), { ok: false, reason: "child_transcript_receipt_mismatch" }); | |
| + | |
| + const missingSessionPath = join(process.env.CODEX_HOME, "sessions", `rollout-${missingSessionChildId}.jsonl`); | |
| + await writeFile(missingSessionPath, transcript( | |
| + missingSessionChildId, | |
| + "executor", | |
| + receiptNotBefore, | |
| + "missing-session", | |
| + )); | |
| + assert.deepEqual(await validateLocalNativeExecutorIdentity({ | |
| + cwd, | |
| + sessionId: "missing-session", | |
| + rootThreadId, | |
| + childAgentId: missingSessionChildId, | |
| + receiptNotBefore, | |
| + receiptNotAfter, | |
| + }), { ok: false, reason: "leader_session_mismatch" }); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("completes prompt bootstrap through typed assignment, activation, and scoped executor write", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-bootstrap-e2e-")); | |
| + try { | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-bootstrap-e2e"; | |
| + const rootThreadId = "thread-bootstrap-e2e-root"; | |
| + const childThreadId = "thread-bootstrap-e2e-child"; | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + await writeLeaderSessionFixture(stateDir, sessionId, rootThreadId, cwd); | |
| + const firstPrompt = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: rootThreadId, | |
| + source: "native", | |
| + available_tools: ["collaboration.spawn_agent"], | |
| + prompt: "$ultragoal execute the durable plan", | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + assert.equal(firstPrompt.skillState?.active, false); | |
| + const bootstrap = await readNativeUltragoalBootstrap(stateDir, sessionId); | |
| + assert.ok(bootstrap); | |
| + | |
| + const childTranscriptPath = join( | |
| + process.env.CODEX_HOME, | |
| + "sessions", | |
| + "2026", | |
| + "07", | |
| + "29", | |
| + `rollout-2026-07-29T18-56-08-${childThreadId}.jsonl`, | |
| + ); | |
| + await mkdir(dirname(childTranscriptPath), { recursive: true }); | |
| + await writeFile(childTranscriptPath, `${JSON.stringify({ | |
| + timestamp: bootstrap.created_at, | |
| + type: "session_meta", | |
| + payload: { | |
| + id: childThreadId, | |
| + session_id: sessionId, | |
| + cwd, | |
| + source: { | |
| + subagent: { | |
| + thread_spawn: { | |
| + parent_thread_id: rootThreadId, | |
| + depth: 1, | |
| + agent_path: "/root/ultragoal_executor", | |
| + agent_role: "executor", | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + })}\n`); | |
| + const tracking = JSON.parse( | |
| + readFileSync(join(stateDir, "subagent-tracking.json"), "utf-8"), | |
| + ) as { sessions?: Record<string, { threads?: Record<string, Record<string, unknown>> }> }; | |
| + assert.equal( | |
| + tracking.sessions?.[sessionId]?.threads?.[childThreadId], | |
| + undefined, | |
| + "assignment-local transcript evidence must not grant persisted reopen authority", | |
| + ); | |
| + await issueLocalNativeExecutorAssignment({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId, | |
| + childAgentId: childThreadId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ["src/native-flow"], | |
| + ttlSeconds: 300, | |
| + }); | |
| + const assignedTracking = JSON.parse( | |
| + readFileSync(join(stateDir, "subagent-tracking.json"), "utf-8"), | |
| + ) as { sessions?: Record<string, { threads?: Record<string, Record<string, unknown>> }> }; | |
| + const descriptiveChild = assignedTracking.sessions?.[sessionId]?.threads?.[childThreadId]; | |
| + assert.equal(descriptiveChild?.mode, "executor"); | |
| + assert.equal(descriptiveChild?.status, undefined); | |
| + assert.equal(descriptiveChild?.provenance_kind, undefined); | |
| + assert.equal(descriptiveChild?.direct_child_parent_id, undefined); | |
| + assert.deepEqual(await validateLocalNativeExecutorIdentity({ | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + childAgentId: childThreadId, | |
| + receiptNotBefore: bootstrap.created_at, | |
| + receiptNotAfter: bootstrap.expires_at, | |
| + }), { ok: true }); | |
| + | |
| + const activated = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: rootThreadId, | |
| + source: "native", | |
| + available_tools: ["collaboration.spawn_agent"], | |
| + prompt: "$ultragoal execute the durable plan", | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + assert.equal(activated.skillState?.active, true); | |
| + assert.equal(activated.skillState?.initialized_mode, "ultragoal"); | |
| + assert.equal( | |
| + (await findReadyLocalNativeExecutorAssignment({ cwd, stateDir, sessionId, rootThreadId }))?.child_agent_id, | |
| + childThreadId, | |
| + ); | |
| + | |
| + const write = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: childThreadId, | |
| + agent_id: childThreadId, | |
| + agent_type: "executor", | |
| + source: "native", | |
| + tool_name: "Write", | |
| + tool_input: { file_path: join(cwd, "src", "native-flow", "result.ts"), content: "ok" }, | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + assert.equal(write.outputJson, null); | |
| + | |
| + await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { | |
| + mode: "ultragoal", | |
| + active: false, | |
| + current_phase: "complete", | |
| + session_id: sessionId, | |
| + }); | |
| + const terminalWrite = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: childThreadId, | |
| + agent_id: childThreadId, | |
| + agent_type: "executor", | |
| + source: "native", | |
| + tool_name: "Write", | |
| + tool_input: { file_path: join(cwd, "src", "native-flow", "late.ts"), content: "no" }, | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + assert.equal(terminalWrite.outputJson?.decision, "block"); | |
| + assert.equal((await readNativeUltragoalBootstrap(stateDir, sessionId))?.status, "revoked"); | |
| + await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "Stop", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: rootThreadId, | |
| + source: "native", | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + assert.equal((await readNativeUltragoalBootstrap(stateDir, sessionId))?.status, "revoked"); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("keeps the executor read-only until successful activation PostToolUse", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-bootstrap-two-phase-")); | |
| + try { | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-bootstrap-two-phase"; | |
| + const rootThreadId = "thread-bootstrap-two-phase-root"; | |
| + const childThreadId = "thread-bootstrap-two-phase-child"; | |
| + await writeLeaderSessionFixture(stateDir, sessionId, rootThreadId, cwd); | |
| + const activationPayload = { | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: rootThreadId, | |
| + agent_id: rootThreadId, | |
| + source: "native", | |
| + available_tools: ["collaboration.spawn_agent"], | |
| + tool_name: "Bash", | |
| + tool_input: { command: `omx state write --input '${ACTIVATION_INPUT}' --json` }, | |
| + }; | |
| + await dispatchCodexNativeHook({ hook_event_name: "PreToolUse", ...activationPayload }, { cwd }); | |
| + const bootstrap = await readNativeUltragoalBootstrap(stateDir, sessionId); | |
| + assert.ok(bootstrap); | |
| + await writeJson(join(stateDir, "subagent-tracking.json"), { | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + [sessionId]: { | |
| + session_id: sessionId, | |
| + leader_thread_id: rootThreadId, | |
| + updated_at: "2026-07-29T16:54:54.000Z", | |
| + threads: { | |
| + [rootThreadId]: { | |
| + thread_id: rootThreadId, | |
| + kind: "leader", | |
| + first_seen_at: "2026-07-29T16:54:54.000Z", | |
| + last_seen_at: "2026-07-29T16:54:54.000Z", | |
| + turn_count: 1, | |
| + }, | |
| + [childThreadId]: { | |
| + thread_id: childThreadId, | |
| + kind: "subagent", | |
| + mode: "executor", | |
| + status: "available", | |
| + first_seen_at: "2026-07-29T16:54:54.000Z", | |
| + last_seen_at: "2026-07-29T16:54:54.000Z", | |
| + turn_count: 1, | |
| + provenance_kind: "native_subagent", | |
| + direct_child_parent_id: rootThreadId, | |
| + direct_child_root_id: rootThreadId, | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + }); | |
| + await issueLocalNativeExecutorAssignment({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId, | |
| + childAgentId: childThreadId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ["src/native-flow"], | |
| + ttlSeconds: 300, | |
| + }); | |
| + | |
| + const prepared = await dispatchCodexNativeHook( | |
| + { hook_event_name: "PreToolUse", ...activationPayload }, | |
| + { cwd }, | |
| + ); | |
| + assert.equal(prepared.outputJson, null); | |
| + assert.equal((await readNativeUltragoalBootstrap(stateDir, sessionId))?.status, "activating"); | |
| + const prematureWrite = await dispatchCodexNativeHook({ | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: childThreadId, | |
| + agent_id: childThreadId, | |
| + agent_type: "executor", | |
| + source: "native", | |
| + tool_name: "Write", | |
| + tool_input: { file_path: join(cwd, "src", "native-flow", "premature.ts"), content: "no" }, | |
| + }, { cwd }); | |
| + assert.equal(prematureWrite.outputJson?.decision, "block"); | |
| + | |
| + await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { | |
| + active: true, | |
| + current_phase: "executing", | |
| + run_outcome: "continue", | |
| + }); | |
| + await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", ...activationPayload }, { cwd }); | |
| + assert.equal((await readNativeUltragoalBootstrap(stateDir, sessionId))?.status, "active"); | |
| + | |
| + await revokeLocalNativeExecutorAssignment({ | |
| + stateDir, | |
| + sessionId, | |
| + reason: "activation_receipt_race", | |
| + }); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { | |
| + mode: "ultragoal", | |
| + active: true, | |
| + current_phase: "executing", | |
| + session_id: sessionId, | |
| + }); | |
| + await dispatchCodexNativeHook({ hook_event_name: "PostToolUse", ...activationPayload }, { cwd }); | |
| + const failedState = JSON.parse(readFileSync( | |
| + join(stateDir, "sessions", sessionId, "ultragoal-state.json"), | |
| + "utf-8", | |
| + )) as { active: boolean; current_phase: string; error?: string }; | |
| + assert.equal(failedState.active, false); | |
| + assert.equal(failedState.current_phase, "failed"); | |
| + assert.equal(failedState.error, "native_ultragoal_activation_receipt_invalid"); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("allows exact root phase transitions for an active session when pointer PID identity is indeterminate", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-active-phase-fallback-")); | |
| + try { | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-active-phase-fallback"; | |
| + const turnId = "turn-active-phase-fallback"; | |
| + const childThreadId = "thread-active-phase-fallback-child"; | |
| + process.env.CODEX_HOME = join(cwd, "codex-home"); | |
| + await writeJson(join(stateDir, "session.json"), { | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + owner_codex_session_id: sessionId, | |
| + target_session_id: sessionId, | |
| + cwd, | |
| + state_root: stateDir, | |
| + pid: process.pid, | |
| + platform: "linux", | |
| + }); | |
| + await writeRootTranscript(process.env.CODEX_HOME, sessionId, rootTranscriptRecord(sessionId, cwd, turnId)); | |
| + const bootstrap = await beginNativeUltragoalBootstrap({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId: sessionId, | |
| + backend: "local-experimental", | |
| + ttlSeconds: 300, | |
| + }); | |
| + await writeJson(join(stateDir, "subagent-tracking.json"), { | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + [sessionId]: { | |
| + session_id: sessionId, | |
| + leader_thread_id: sessionId, | |
| + updated_at: bootstrap.created_at, | |
| + threads: { | |
| + [sessionId]: { | |
| + thread_id: sessionId, | |
| + kind: "leader", | |
| + first_seen_at: bootstrap.created_at, | |
| + last_seen_at: bootstrap.created_at, | |
| + turn_count: 1, | |
| + }, | |
| + [childThreadId]: { | |
| + thread_id: childThreadId, | |
| + kind: "subagent", | |
| + mode: "executor", | |
| + status: "available", | |
| + first_seen_at: bootstrap.created_at, | |
| + last_seen_at: bootstrap.created_at, | |
| + turn_count: 1, | |
| + provenance_kind: "native_subagent", | |
| + direct_child_parent_id: sessionId, | |
| + direct_child_root_id: sessionId, | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + }); | |
| + const childTranscriptPath = join( | |
| + process.env.CODEX_HOME, | |
| + "sessions", | |
| + "2026", | |
| + "07", | |
| + "29", | |
| + `rollout-2026-07-29T20-00-01-${childThreadId}.jsonl`, | |
| + ); | |
| + await mkdir(dirname(childTranscriptPath), { recursive: true }); | |
| + await writeFile(childTranscriptPath, `${JSON.stringify({ | |
| + timestamp: bootstrap.created_at, | |
| + type: "session_meta", | |
| + payload: { | |
| + id: childThreadId, | |
| + session_id: sessionId, | |
| + cwd, | |
| + source: { | |
| + subagent: { | |
| + thread_spawn: { | |
| + parent_thread_id: sessionId, | |
| + depth: 1, | |
| + agent_path: "/root/active_phase_executor", | |
| + agent_role: "executor", | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + })}\n`); | |
| + await issueLocalNativeExecutorAssignment({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId: sessionId, | |
| + childAgentId: childThreadId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ["src"], | |
| + ttlSeconds: 300, | |
| + }); | |
| + assert.equal((await prepareNativeUltragoalBootstrapActivation({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + childAgentId: childThreadId, | |
| + })).ok, true); | |
| + assert.equal((await markNativeUltragoalBootstrapActive({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + childAgentId: childThreadId, | |
| + })).ok, true); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { | |
| + active: true, | |
| + current_phase: "executing", | |
| + run_outcome: "continue", | |
| + }); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { | |
| + version: 1, | |
| + active: true, | |
| + skill: "ultragoal", | |
| + phase: "executing", | |
| + session_id: sessionId, | |
| + thread_id: sessionId, | |
| + active_skills: [{ skill: "ultragoal", active: true, phase: "executing", session_id: sessionId }], | |
| + }); | |
| + | |
| + const exactPhasePayload = { | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + turn_id: turnId, | |
| + source: "native", | |
| + tool_name: "Bash", | |
| + tool_input: { | |
| + command: `omx state write --input '${JSON.stringify({ | |
| + mode: "ultragoal", | |
| + active: true, | |
| + current_phase: "verifying", | |
| + })}' --json`, | |
| + }, | |
| + }; | |
| + const identityIndeterminatePointer = { | |
| + status: "identity-indeterminate" as const, | |
| + state: { | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd, | |
| + state_root: stateDir, | |
| + pid: process.pid, | |
| + platform: "darwin" as const, | |
| + started_at: bootstrap.created_at, | |
| + }, | |
| + }; | |
| + const rootTranscript = { sessionId, hasExactTurnContext: true, hasTypedNativeSpawn: true }; | |
| + const stillBlockedWithoutFallbackPointer = await buildConductorPreToolUseWriteGuardOutput( | |
| + exactPhasePayload, | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + cwd, | |
| + rootTranscript, | |
| + ); | |
| + assert.equal(stillBlockedWithoutFallbackPointer?.decision, "block"); | |
| + assert.equal(await buildConductorPreToolUseWriteGuardOutput( | |
| + exactPhasePayload, | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + cwd, | |
| + rootTranscript, | |
| + identityIndeterminatePointer, | |
| + ), null); | |
| + assert.equal(await buildConductorPreToolUseWriteGuardOutput( | |
| + exactPhasePayload, | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + cwd, | |
| + rootTranscript, | |
| + { ...identityIndeterminatePointer, status: "usable" as const }, | |
| + ), null); | |
| + | |
| + const dispatchPhase = async ( | |
| + phase: string, | |
| + extraPayload: Record<string, unknown> = {}, | |
| + identity: Record<string, unknown> = {}, | |
| + ) => dispatchCodexNativeHook({ | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + turn_id: turnId, | |
| + source: "native", | |
| + ...identity, | |
| + tool_name: "Bash", | |
| + tool_input: { | |
| + command: `omx state write --input '${JSON.stringify({ | |
| + mode: "ultragoal", | |
| + active: true, | |
| + current_phase: phase, | |
| + ...extraPayload, | |
| + })}' --json`, | |
| + }, | |
| + }, { cwd }); | |
| + | |
| + for (const phase of ["planning", "executing", "verifying", "reviewing", "checkpointing", "blocked"]) { | |
| + assert.equal((await dispatchPhase(phase)).outputJson, null, `root phase ${phase} must be allowed`); | |
| + } | |
| + | |
| + const unsafe = await dispatchPhase("verifying", { reason: "smuggled" }); | |
| + assert.equal(unsafe.outputJson?.decision, "block"); | |
| + assert.match(String(unsafe.outputJson?.reason ?? ""), /Main-root Conductor mode is active/); | |
| + | |
| + const child = await dispatchPhase("verifying", {}, { | |
| + agent_id: childThreadId, | |
| + agent_type: "executor", | |
| + thread_id: childThreadId, | |
| + }); | |
| + assert.equal(child.outputJson?.decision, "block"); | |
| + | |
| + const childGitPayload = { | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + turn_id: turnId, | |
| + source: "native", | |
| + agent_id: childThreadId, | |
| + agent_type: "executor", | |
| + thread_id: childThreadId, | |
| + tool_name: "Bash", | |
| + }; | |
| + const dispatchChildGit = (command: string) => dispatchCodexNativeHook({ | |
| + ...childGitPayload, | |
| + tool_input: { command }, | |
| + }, { cwd }); | |
| + | |
| + const previousGitPager = process.env.GIT_PAGER; | |
| + delete process.env.GIT_PAGER; | |
| + try { | |
| + const childGitStatus = await dispatchChildGit("git status --short"); | |
| + assert.equal(childGitStatus.outputJson, null); | |
| + | |
| + for (const command of [ | |
| + "git reset --hard", | |
| + "git status --short; git reset --hard", | |
| + "git status --short$(printf x)", | |
| + "git status --short\ngit reset --hard", | |
| + ]) { | |
| + assert.equal((await dispatchChildGit(command)).outputJson?.decision, "block", command); | |
| + } | |
| + | |
| + for (const [name, value] of [ | |
| + ["BASH_FUNC_git%%", "() { printf owned > src/pwned.ts; }"], | |
| + ["GIT_EXTERNAL_DIFF", "/tmp/attacker-git-diff"], | |
| + ] as const) { | |
| + const previous = process.env[name]; | |
| + process.env[name] = value; | |
| + try { | |
| + assert.equal((await dispatchChildGit("git status --short")).outputJson?.decision, "block", name); | |
| + } finally { | |
| + if (previous === undefined) delete process.env[name]; | |
| + else process.env[name] = previous; | |
| + } | |
| + } | |
| + } finally { | |
| + if (previousGitPager === undefined) delete process.env.GIT_PAGER; | |
| + else process.env.GIT_PAGER = previousGitPager; | |
| + } | |
| + | |
| + await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { | |
| + active: true, | |
| + current_phase: "checkpointing", | |
| + run_outcome: "continue", | |
| + }); | |
| + await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { | |
| + version: 1, | |
| + goals: [{ id: "G001-terminal", status: "complete" }], | |
| + aggregateCompletion: { | |
| + status: "complete", | |
| + completedAt: "2026-07-29T20:10:00.000Z", | |
| + evidence: "tests and independent reviews passed", | |
| + }, | |
| + }); | |
| + const terminalPayload = (input: Record<string, unknown>, identity: Record<string, unknown> = {}) => ({ | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + turn_id: turnId, | |
| + source: "native", | |
| + ...identity, | |
| + tool_name: "Bash", | |
| + tool_input: { | |
| + command: `omx state write --input '${JSON.stringify(input)}' --json`, | |
| + }, | |
| + }); | |
| + const exactTerminalInput = { | |
| + mode: "ultragoal", | |
| + active: false, | |
| + current_phase: "complete", | |
| + }; | |
| + assert.equal( | |
| + (await dispatchCodexNativeHook(terminalPayload(exactTerminalInput), { cwd })).outputJson?.decision, | |
| + "block", | |
| + "terminal transition must remain blocked until executor authority is revoked", | |
| + ); | |
| + await revokeLocalNativeExecutorAssignment({ | |
| + stateDir, | |
| + sessionId, | |
| + reason: "aggregate_complete", | |
| + }); | |
| + assert.equal( | |
| + (await dispatchCodexNativeHook(terminalPayload(exactTerminalInput), { cwd })).outputJson, | |
| + null, | |
| + "exact root terminal transition must be allowed after aggregate checkpoint and executor revocation", | |
| + ); | |
| + await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { | |
| + version: 1, | |
| + goals: [{ id: "G001-terminal", status: "in_progress" }], | |
| + aggregateCompletion: { status: "complete" }, | |
| + }); | |
| + assert.equal( | |
| + (await dispatchCodexNativeHook(terminalPayload(exactTerminalInput), { cwd })).outputJson?.decision, | |
| + "block", | |
| + "terminal transition must remain blocked when any repo-native goal is incomplete", | |
| + ); | |
| + await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), { | |
| + version: 1, | |
| + goals: [{ id: "G001-terminal", status: "complete" }], | |
| + aggregateCompletion: { status: "complete", evidence: "проверки пройдены" }, | |
| + }); | |
| + assert.equal( | |
| + (await dispatchCodexNativeHook(terminalPayload(exactTerminalInput), { cwd })).outputJson, | |
| + null, | |
| + "UTF-8 aggregate evidence must not invalidate byte-size pinning", | |
| + ); | |
| + assert.equal( | |
| + (await dispatchCodexNativeHook(terminalPayload({ ...exactTerminalInput, reason: "smuggled" }), { cwd })).outputJson?.decision, | |
| + "block", | |
| + ); | |
| + assert.equal( | |
| + (await dispatchCodexNativeHook(terminalPayload(exactTerminalInput, { | |
| + agent_id: childThreadId, | |
| + agent_type: "executor", | |
| + thread_id: childThreadId, | |
| + }), { cwd })).outputJson?.decision, | |
| + "block", | |
| + ); | |
| + | |
| + await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { | |
| + mode: "ultragoal", | |
| + active: false, | |
| + current_phase: "complete", | |
| + session_id: sessionId, | |
| + }); | |
| + const terminal = await dispatchPhase("verifying"); | |
| + assert.equal(terminal.outputJson?.decision, "block"); | |
| + assert.match(String(terminal.outputJson?.reason ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/); | |
| + | |
| + assert.equal(await __isAuthorizedActiveUltragoalPhaseTransitionFallbackForTests({ | |
| + payload: { | |
| + hook_event_name: "PreToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + turn_id: turnId, | |
| + source: "native", | |
| + tool_name: "Bash", | |
| + tool_input: { command: `omx state write --input '${ACTIVATION_INPUT}' --json` }, | |
| + }, | |
| + policyCwd: cwd, | |
| + stateDir, | |
| + sessionId, | |
| + pointer: { | |
| + status: "identity-indeterminate", | |
| + state: { | |
| + session_id: "foreign-active-phase-session", | |
| + native_session_id: "foreign-active-phase-session", | |
| + cwd, | |
| + state_root: stateDir, | |
| + pid: process.pid, | |
| + platform: "linux", | |
| + started_at: bootstrap.created_at, | |
| + }, | |
| + }, | |
| + rootTranscript: { sessionId, hasExactTurnContext: true, hasTypedNativeSpawn: true }, | |
| + }), false); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("allows the primary '$ultragoal' UserPromptSubmit activation when attached to tmux (Team is reachable)", async () => { | |
| + process.env.TMUX = "/tmp/tmux-3311-prompt,12345,0"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3311-prompt-tmux-")); | |
| + try { | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-3311-prompt-tmux"; | |
| + await mkdir(stateDir, { recursive: true }); | |
| + | |
| + const result = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: "thread-3311-prompt-tmux", | |
| + turn_id: "turn-3311-prompt-tmux", | |
| + source: "native", | |
| + prompt: "$ultragoal split this launch into durable goals", | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + | |
| + assert.equal(result.skillState?.active, true); | |
| + assert.equal(result.skillState?.skill, "ultragoal"); | |
| + assert.equal(result.skillState?.initialized_mode, "ultragoal"); | |
| + assert.equal(result.skillState?.transition_error, undefined); | |
| + const detailState = JSON.parse(readFileSync( | |
| + join(stateDir, "sessions", sessionId, "ultragoal-state.json"), | |
| + "utf-8", | |
| + )) as { active?: boolean; current_phase?: string; error?: string }; | |
| + assert.equal(detailState.active, true); | |
| + assert.equal(detailState.current_phase, "planning"); | |
| + assert.equal(detailState.error, undefined); | |
| + assert.equal(await readNativeUltragoalBootstrap(stateDir, sessionId), null); | |
| + | |
| + await dispatchCodexNativeHook({ | |
| + hook_event_name: "PostToolUse", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: "thread-3311-prompt-tmux", | |
| + source: "native", | |
| + tool_name: "Bash", | |
| + tool_input: { command: `omx state write --input '${ACTIVATION_INPUT}' --json` }, | |
| + }, { cwd }); | |
| + const postToolState = JSON.parse(readFileSync( | |
| + join(stateDir, "sessions", sessionId, "ultragoal-state.json"), | |
| + "utf-8", | |
| + )) as { active?: boolean; current_phase?: string; error?: string }; | |
| + assert.equal(postToolState.active, true); | |
| + assert.equal(postToolState.current_phase, "planning"); | |
| + assert.equal(postToolState.error, undefined); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("does not re-block continuation of an already-active '$ultragoal' UserPromptSubmit session outside tmux", async () => { | |
| + delete process.env.TMUX; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3311-prompt-continue-")); | |
| + try { | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-3311-prompt-continue"; | |
| + const leaderThreadId = "thread-3311-prompt-continue"; | |
| + await writeLeaderSessionFixture(stateDir, sessionId, leaderThreadId, cwd); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { | |
| + active: true, | |
| + skill: "ultragoal", | |
| + phase: "planning", | |
| + session_id: sessionId, | |
| + active_skills: [{ skill: "ultragoal", phase: "planning", active: true, session_id: sessionId }], | |
| + }); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "ultragoal-state.json"), { | |
| + active: true, | |
| + mode: "ultragoal", | |
| + current_phase: "planning", | |
| + session_id: sessionId, | |
| + }); | |
| + | |
| + const result = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: leaderThreadId, | |
| + turn_id: "turn-3311-prompt-continue", | |
| + source: "native", | |
| + prompt: "$ultragoal continue with the next milestone", | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + | |
| + assert.doesNotMatch(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-NO-OWNER/); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("does not block the primary '$ultragoal' UserPromptSubmit activation from a non-native (CLI) launcher outside tmux", async () => { | |
| + delete process.env.TMUX; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3311-prompt-cli-")); | |
| + try { | |
| + await mkdir(join(cwd, ".omx", "state"), { recursive: true }); | |
| + | |
| + const result = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: "sess-3311-prompt-cli", | |
| + thread_id: "thread-3311-prompt-cli", | |
| + turn_id: "turn-3311-prompt-cli", | |
| + source: "cli", | |
| + prompt: "$ultragoal split this launch into durable goals", | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + | |
| + assert.equal(result.skillState?.initialized_mode, "ultragoal"); | |
| + assert.equal(result.skillState?.transition_error, undefined); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + // --- Autopilot-supervised child recognition must not be intercepted --- | |
| + // #3311 repair-2: the prompt guard must refuse only a truly *standalone* | |
| + // fresh Ultragoal activation. When Autopilot is already active and | |
| + // supervising a child phase (e.g. ralplan), a "$ultragoal" prompt is a | |
| + // supervised-child transition handled entirely by recordSkillActivation's | |
| + // own isAutopilotSupervisedChildSkill branch; it must remain reachable on | |
| + // native App outside tmux exactly as it was on the pre-#3311-fix baseline. | |
| + | |
| + it("does not intercept Autopilot-supervised '$ultragoal' child-phase recognition on native App outside tmux", async () => { | |
| + delete process.env.TMUX; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-3311-autopilot-supervised-")); | |
| + try { | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-3311-autopilot-supervised"; | |
| + const threadId = "thread-3311-autopilot-supervised"; | |
| + await mkdir(join(stateDir, "sessions", sessionId), { recursive: true }); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { | |
| + active: true, | |
| + skill: "autopilot", | |
| + phase: "ralplan", | |
| + session_id: sessionId, | |
| + active_skills: [{ skill: "autopilot", phase: "ralplan", active: true, session_id: sessionId }], | |
| + }); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "autopilot-state.json"), { | |
| + active: true, | |
| + mode: "autopilot", | |
| + current_phase: "ralplan", | |
| + session_id: sessionId, | |
| + state: { | |
| + handoff_artifacts: { | |
| + ralplan_consensus_gate: { required: true, complete: false }, | |
| + }, | |
| + }, | |
| + }); | |
| + | |
| + const result = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: threadId, | |
| + turn_id: "turn-3311-autopilot-supervised", | |
| + source: "native", | |
| + prompt: "$ultragoal turn the approved plan into durable goals", | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + | |
| + assert.equal(result.omxEventName, "keyword-detector"); | |
| + assert.equal(result.skillState?.active, true); | |
| + assert.equal(result.skillState?.skill, "autopilot"); | |
| + assert.equal(result.skillState?.supervised_child_skill, "ultragoal"); | |
| + assert.equal(result.skillState?.transition_error, undefined); | |
| + assert.doesNotMatch(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-NO-OWNER/); | |
| + const message = String( | |
| + (result.outputJson as { hookSpecificOutput?: { additionalContext?: string } } | null) | |
| + ?.hookSpecificOutput?.additionalContext || "", | |
| + ); | |
| + assert.doesNotMatch(message, /OMX-ULTRAGOAL-NO-OWNER/); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it("keeps a completed standalone Ralplan to Ultragoal handoff inactive until executor authorization", async () => { | |
| + delete process.env.TMUX; | |
| + process.env.OMX_NATIVE_SUBAGENT_ASSIGNMENTS = "on"; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = "local-experimental"; | |
| + const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ralplan-ultragoal-bootstrap-")); | |
| + try { | |
| + const stateDir = join(cwd, ".omx", "state"); | |
| + const sessionId = "sess-ralplan-ultragoal-bootstrap"; | |
| + const threadId = "thread-ralplan-ultragoal-bootstrap"; | |
| + await writeLeaderSessionFixture(stateDir, sessionId, threadId, cwd); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "skill-active-state.json"), { | |
| + active: false, | |
| + skill: "ralplan", | |
| + phase: "complete", | |
| + session_id: sessionId, | |
| + owner_codex_session_id: sessionId, | |
| + target_session_id: sessionId, | |
| + active_skills: [{ | |
| + skill: "ralplan", | |
| + phase: "complete", | |
| + active: false, | |
| + session_id: sessionId, | |
| + owner_codex_session_id: sessionId, | |
| + }], | |
| + }); | |
| + await writeJson(join(stateDir, "sessions", sessionId, "ralplan-state.json"), { | |
| + active: false, | |
| + mode: "ralplan", | |
| + current_phase: "complete", | |
| + session_id: sessionId, | |
| + owner_codex_session_id: sessionId, | |
| + target_session_id: sessionId, | |
| + }); | |
| + | |
| + const result = await dispatchCodexNativeHook( | |
| + { | |
| + hook_event_name: "UserPromptSubmit", | |
| + cwd, | |
| + session_id: sessionId, | |
| + thread_id: threadId, | |
| + source: "native", | |
| + available_tools: ["collaboration.spawn_agent"], | |
| + prompt: "$ultragoal execute the approved plan", | |
| + }, | |
| + { cwd }, | |
| + ); | |
| + | |
| + assert.equal(result.skillState?.active, false); | |
| + assert.equal(result.skillState?.skill, "ultragoal"); | |
| + assert.match(String(result.skillState?.transition_error ?? ""), /OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED/); | |
| + assert.equal( | |
| + existsSync(join(stateDir, "sessions", sessionId, "ultragoal-state.json")), | |
| + false, | |
| + ); | |
| } finally { | |
| await rm(cwd, { recursive: true, force: true }); | |
| } | |
| diff --git a/src/scripts/codex-native-hook.ts b/src/scripts/codex-native-hook.ts | |
| index 8c74a5d4..b93d0d90 100644 | |
| --- a/src/scripts/codex-native-hook.ts | |
| +++ b/src/scripts/codex-native-hook.ts | |
| @@ -1,5 +1,5 @@ | |
| import { execFileSync } from "child_process"; | |
| -import { accessSync, closeSync, constants as fsConstants, existsSync, lstatSync, openSync, readFileSync, readSync, readdirSync, realpathSync, statSync } from "fs"; | |
| +import { accessSync, closeSync, constants as fsConstants, existsSync, lstatSync, openSync, readFileSync, readSync, readdirSync, realpathSync, statSync, type Dirent } from "fs"; | |
| import { appendFile, lstat, mkdir, open, readFile, readdir, stat, unlink, writeFile } from "fs/promises"; | |
| import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve } from "path"; | |
| import { createHash } from "crypto"; | |
| @@ -54,6 +54,7 @@ import { | |
| reconcileNativeSessionStart, | |
| resolveSessionPointerContext, | |
| type SessionStartOptions, | |
| + type SessionPointerReadResult, | |
| writeNativeSessionOwner, | |
| type SessionState, | |
| } from "../hooks/session.js"; | |
| @@ -76,6 +77,7 @@ import { | |
| import { parseTeamNoticeLedgerPrompt, reconcileTeamNoticeLedger } from "../team/notice-ledger.js"; | |
| import { | |
| canonicalizeComparablePath, | |
| + codexHome, | |
| omxNotepadPath, | |
| resolveProjectMemoryPath, | |
| sameFilePath, | |
| @@ -97,6 +99,7 @@ import { | |
| } from "../hooks/keyword-detector.js"; | |
| import { buildDeepInterviewConfigInstruction } from "../hooks/deep-interview-config-instruction.js"; | |
| import { readTeamModeConfig } from "../config/team-mode.js"; | |
| +import { resolveNativeUltragoalConfig } from "../config/native-ultragoal.js"; | |
| import { | |
| detectNativeStopStallPattern, | |
| loadAutoNudgeConfig, | |
| @@ -165,6 +168,20 @@ import { | |
| steerUltragoal, | |
| type UltragoalSteeringProposal, | |
| } from "../ultragoal/artifacts.js"; | |
| +import { | |
| + beginNativeUltragoalBootstrap, | |
| + DOCUMENTED_HOST_RECEIPT_UNAVAILABLE, | |
| + findReadyLocalNativeExecutorAssignment, | |
| + markNativeUltragoalBootstrapActive, | |
| + NATIVE_ASSIGNMENT_DIRECTORY, | |
| + nativeExecutorAssignmentFileIsSafe, | |
| + parseActiveLocalNativeExecutorAssignment, | |
| + prepareNativeUltragoalBootstrapActivation, | |
| + readNativeUltragoalBootstrap, | |
| + reconcileLocalNativeExecutorAssignments, | |
| + revokeLocalNativeExecutorAssignment, | |
| + validateLocalNativeExecutorIdentity, | |
| +} from "../ultragoal/native-bootstrap.js"; | |
| import { triagePrompt } from "../hooks/triage-heuristic.js"; | |
| import { readTriageConfig } from "../hooks/triage-config.js"; | |
| import { | |
| @@ -206,6 +223,11 @@ interface NativeHookDispatchOptions { | |
| /** @internal Scoped deterministic SessionStart durability seam for native-hook tests. */ | |
| sessionStartOptions?: Pick<SessionStartOptions, 'platform' | 'regularFileSync'>; | |
| reconcileHudForPromptSubmitFn?: typeof reconcileHudForPromptSubmit; | |
| + /** @internal Deterministic pinned-transcript race seam for native-hook tests. */ | |
| + rootTranscriptPostReadHook?: (path: string) => void | Promise<void>; | |
| + /** @internal Deterministic transcript traversal seams for native-hook tests. */ | |
| + rootTranscriptBeforeReadDirHook?: (path: string) => void | Promise<void>; | |
| + rootTranscriptScanMaxEntries?: number; | |
| } | |
| export interface NativeHookDispatchResult { | |
| @@ -281,6 +303,9 @@ const RALPH_INTENT_STOPWORDS = new Set([ | |
| "ralph", "continue", "resume", "finish", "complete", "fix", "issue", | |
| ]); | |
| const MAX_SESSION_META_LINE_BYTES = 256 * 1024; | |
| +const MAX_ROOT_TRANSCRIPT_FIRST_LINE_BYTES = 64 * 1024; | |
| +const MAX_ROOT_TRANSCRIPT_TAIL_BYTES = 8 * 1024 * 1024; | |
| +const MAX_ROOT_TRANSCRIPT_SCAN_ENTRIES = 10_000; | |
| function safeString(value: unknown): string { | |
| return typeof value === "string" ? value : ""; | |
| @@ -428,6 +453,224 @@ function readBoundedFirstLineSync(path: string): string { | |
| } | |
| } | |
| +interface SafeRootTranscriptMetadata { | |
| + sessionId: string; | |
| + hasExactTurnContext: boolean; | |
| + hasTypedNativeSpawn: boolean; | |
| +} | |
| + | |
| +function readStrictStringAliasClaim( | |
| + record: Record<string, unknown>, | |
| + keys: readonly string[], | |
| +): { present: boolean; valid: boolean; values: string[] } { | |
| + const presentKeys = keys.filter((key) => Object.prototype.hasOwnProperty.call(record, key)); | |
| + if (presentKeys.length === 0) return { present: false, valid: true, values: [] }; | |
| + const values: string[] = []; | |
| + for (const key of presentKeys) { | |
| + const value = record[key]; | |
| + if (typeof value !== "string" || !value.trim()) return { present: true, valid: false, values: [] }; | |
| + values.push(value.trim()); | |
| + } | |
| + return { present: true, valid: new Set(values).size === 1, values: [...new Set(values)] }; | |
| +} | |
| + | |
| +async function findLiveRootTranscriptPaths( | |
| + sessionId: string, | |
| + options: { | |
| + beforeReadDirHook?: (path: string) => void | Promise<void>; | |
| + maxEntries?: number; | |
| + } = {}, | |
| +): Promise<string[] | null> { | |
| + const suffix = `-${sessionId}.jsonl`; | |
| + const matches: string[] = []; | |
| + const pending = [{ path: join(codexHome(), "sessions"), depth: 0 }]; | |
| + const maxEntries = options.maxEntries ?? MAX_ROOT_TRANSCRIPT_SCAN_ENTRIES; | |
| + let visited = 0; | |
| + | |
| + while (pending.length > 0 && visited < maxEntries) { | |
| + const current = pending.shift()!; | |
| + let entries: Dirent[]; | |
| + try { | |
| + if (options.beforeReadDirHook) await options.beforeReadDirHook(current.path); | |
| + entries = await readdir(current.path, { withFileTypes: true }); | |
| + } catch { | |
| + return null; | |
| + } | |
| + if (visited + entries.length > maxEntries) return null; | |
| + visited += entries.length; | |
| + for (const entry of entries) { | |
| + const path = join(current.path, entry.name); | |
| + if (entry.isDirectory() && current.depth < 4) { | |
| + pending.push({ path, depth: current.depth + 1 }); | |
| + } else if (entry.isFile() && entry.name.endsWith(suffix)) { | |
| + matches.push(path); | |
| + if (matches.length > 1) return matches; | |
| + } | |
| + } | |
| + } | |
| + return pending.length === 0 ? matches : null; | |
| +} | |
| + | |
| +async function readSafeRootTranscriptMetadata( | |
| + payload: CodexHookPayload, | |
| + cwd: string, | |
| + postReadHook?: (path: string) => void | Promise<void>, | |
| + scanOptions: { | |
| + beforeReadDirHook?: (path: string) => void | Promise<void>; | |
| + maxEntries?: number; | |
| + } = {}, | |
| +): Promise<SafeRootTranscriptMetadata | null> { | |
| + const hasAnyAlias = (keys: readonly string[]): boolean => keys.some((key) => ( | |
| + Object.prototype.hasOwnProperty.call(payload, key) | |
| + )); | |
| + if ( | |
| + payloadHasConflictingIdentityAliases(payload) | |
| + || hasSubagentThreadSpawnProvenance(payload) | |
| + || hasAnyAlias([ | |
| + "owner_codex_thread_id", | |
| + "owner_omx_session_id", | |
| + "owner_codex_session_id", | |
| + "native_owner_session_id", | |
| + ]) | |
| + || hasAnyAlias([ | |
| + "agent_id", | |
| + "agentId", | |
| + "agent_role", | |
| + "agentRole", | |
| + "agent_type", | |
| + "agentType", | |
| + ]) | |
| + ) return null; | |
| + const sessionClaim = readStrictStringAliasClaim(payload, ["session_id", "sessionId"]); | |
| + const sessionId = sessionClaim.values[0] ?? ""; | |
| + const threadClaim = readStrictStringAliasClaim(payload, ["thread_id", "threadId"]); | |
| + if ( | |
| + !sessionClaim.valid | |
| + || !sessionClaim.present | |
| + || !sessionId | |
| + || !threadClaim.valid | |
| + || (threadClaim.present && threadClaim.values[0] !== sessionId) | |
| + ) return null; | |
| + | |
| + const paths = await findLiveRootTranscriptPaths(sessionId, scanOptions); | |
| + if (!paths || paths.length !== 1) return null; | |
| + const path = paths[0]!; | |
| + const metadata = await lstat(path).catch(() => null); | |
| + if ( | |
| + !metadata | |
| + || !metadata.isFile() | |
| + || metadata.isSymbolicLink() | |
| + || metadata.nlink !== 1 | |
| + || (metadata.mode & 0o022) !== 0 | |
| + ) return null; | |
| + | |
| + const handle = await open(path, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0)).catch(() => null); | |
| + if (!handle) return null; | |
| + try { | |
| + const openedMetadata = await handle.stat(); | |
| + if ( | |
| + !openedMetadata.isFile() | |
| + || openedMetadata.nlink !== 1 | |
| + || (openedMetadata.mode & 0o022) !== 0 | |
| + || openedMetadata.dev !== metadata.dev | |
| + || openedMetadata.ino !== metadata.ino | |
| + ) return null; | |
| + const snapshot = await (async (): Promise<SafeRootTranscriptMetadata | null> => { | |
| + const buffer = Buffer.alloc(MAX_ROOT_TRANSCRIPT_FIRST_LINE_BYTES); | |
| + const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0); | |
| + const newline = buffer.subarray(0, bytesRead).indexOf(0x0a); | |
| + if (newline < 0) return null; | |
| + const record = safeObject(JSON.parse(buffer.subarray(0, newline).toString("utf8"))); | |
| + if (safeString(record?.type).trim() !== "session_meta") return null; | |
| + const transcriptPayload = safeObject(record?.payload); | |
| + if (!transcriptPayload || safeString(transcriptPayload.id).trim() !== sessionId) return null; | |
| + const transcriptSessionClaim = readStrictStringAliasClaim(transcriptPayload, ["session_id", "sessionId"]); | |
| + if (!transcriptSessionClaim.valid || (transcriptSessionClaim.present && transcriptSessionClaim.values[0] !== sessionId)) return null; | |
| + const threadSourceClaim = readStrictStringAliasClaim(transcriptPayload, ["thread_source", "threadSource"]); | |
| + if (!threadSourceClaim.valid || !threadSourceClaim.present || threadSourceClaim.values[0] !== "user") return null; | |
| + const source = transcriptPayload.source; | |
| + if (!["vscode", "exec"].includes(safeString(source).trim())) return null; | |
| + if (safeString(transcriptPayload.originator).trim() !== "Codex Desktop") return null; | |
| + const sourceObject = safeObject(source); | |
| + if (sourceObject && Object.prototype.hasOwnProperty.call(sourceObject, "subagent")) return null; | |
| + const transcriptCwd = safeString(transcriptPayload.cwd).trim(); | |
| + if (!transcriptCwd) return null; | |
| + try { | |
| + if (realpathSync(transcriptCwd) !== realpathSync(cwd)) return null; | |
| + } catch { | |
| + return null; | |
| + } | |
| + | |
| + const tailBytes = Math.min(openedMetadata.size, MAX_ROOT_TRANSCRIPT_TAIL_BYTES); | |
| + const tailOffset = openedMetadata.size - tailBytes; | |
| + const tailBuffer = Buffer.alloc(tailBytes); | |
| + const { bytesRead: tailBytesRead } = await handle.read(tailBuffer, 0, tailBytes, tailOffset); | |
| + let tailText = tailBuffer.subarray(0, tailBytesRead).toString("utf8"); | |
| + if (tailOffset > 0) { | |
| + const firstNewline = tailText.indexOf("\n"); | |
| + if (firstNewline < 0) return { sessionId, hasExactTurnContext: false, hasTypedNativeSpawn: false }; | |
| + tailText = tailText.slice(firstNewline + 1); | |
| + } | |
| + if (!tailText.endsWith("\n")) { | |
| + return { sessionId, hasExactTurnContext: false, hasTypedNativeSpawn: false }; | |
| + } | |
| + const completeTail = tailText; | |
| + let latestTurnContext: Record<string, unknown> | null = null; | |
| + for (const line of completeTail.split("\n")) { | |
| + if (!line) continue; | |
| + try { | |
| + const candidate = safeObject(JSON.parse(line)); | |
| + if (safeString(candidate?.type).trim() === "turn_context") latestTurnContext = candidate; | |
| + } catch { | |
| + return { sessionId, hasExactTurnContext: false, hasTypedNativeSpawn: false }; | |
| + } | |
| + } | |
| + const turnContextPayload = safeObject(latestTurnContext?.payload); | |
| + const payloadTurnClaim = readStrictStringAliasClaim(payload, ["turn_id", "turnId"]); | |
| + const contextTurnClaim = readStrictStringAliasClaim(turnContextPayload ?? {}, ["turn_id", "turnId"]); | |
| + const multiAgentModeClaim = readStrictStringAliasClaim(turnContextPayload ?? {}, ["multi_agent_mode", "multiAgentMode"]); | |
| + const multiAgentVersionClaim = readStrictStringAliasClaim(turnContextPayload ?? {}, ["multi_agent_version", "multiAgentVersion"]); | |
| + const contextCwd = safeString(turnContextPayload?.cwd).trim(); | |
| + let exactContextCwd = false; | |
| + try { | |
| + exactContextCwd = realpathSync(contextCwd) === realpathSync(cwd); | |
| + } catch { | |
| + exactContextCwd = false; | |
| + } | |
| + const hasExactTurnContext = payloadTurnClaim.valid | |
| + && payloadTurnClaim.present | |
| + && contextTurnClaim.valid | |
| + && contextTurnClaim.present | |
| + && contextTurnClaim.values[0] === payloadTurnClaim.values[0] | |
| + && exactContextCwd; | |
| + const hasTypedNativeSpawn = hasExactTurnContext | |
| + && multiAgentModeClaim.valid | |
| + && multiAgentModeClaim.present | |
| + && multiAgentModeClaim.values[0] === "explicitRequestOnly" | |
| + && multiAgentVersionClaim.valid | |
| + && multiAgentVersionClaim.present | |
| + && multiAgentVersionClaim.values[0] === "v2"; | |
| + return { sessionId, hasExactTurnContext, hasTypedNativeSpawn }; | |
| + })(); | |
| + if (postReadHook) await postReadHook(path); | |
| + const postReadMetadata = await handle.stat(); | |
| + if ( | |
| + postReadMetadata.dev !== openedMetadata.dev | |
| + || postReadMetadata.ino !== openedMetadata.ino | |
| + || postReadMetadata.mode !== openedMetadata.mode | |
| + || postReadMetadata.nlink !== openedMetadata.nlink | |
| + || postReadMetadata.size !== openedMetadata.size | |
| + || postReadMetadata.mtimeMs !== openedMetadata.mtimeMs | |
| + || postReadMetadata.ctimeMs !== openedMetadata.ctimeMs | |
| + ) return null; | |
| + return snapshot; | |
| + } catch { | |
| + return null; | |
| + } finally { | |
| + await handle.close().catch(() => {}); | |
| + } | |
| +} | |
| + | |
| @@ -2661,6 +2904,43 @@ function buildNativeOutsideTmuxTeamPromptBlockState( | |
| // of an already-active standalone Ultragoal session) are left untouched, so | |
| // resumption, prior-plan continuation, and non-standalone Ultragoal remain | |
| // exactly as before. | |
| +function nativeSubagentAssignmentsOptedIn(): boolean { | |
| + return resolveNativeUltragoalConfig().backend === "local-experimental"; | |
| +} | |
| + | |
| +function payloadAdvertisesCodexAppV2NativeSpawn(payload: CodexHookPayload): boolean { | |
| + const multiAgentMode = safeString(payload.multi_agent_mode ?? payload.multiAgentMode).trim().toLowerCase(); | |
| + const multiAgentVersion = safeString(payload.multi_agent_version ?? payload.multiAgentVersion).trim().toLowerCase(); | |
| + return multiAgentMode === "explicitrequestonly" && multiAgentVersion === "v2"; | |
| +} | |
| + | |
| +function payloadAdvertisesTypedNativeSpawn( | |
| + payload: CodexHookPayload, | |
| + rootTranscript: SafeRootTranscriptMetadata | null = null, | |
| +): boolean { | |
| + const capabilities = safeObject(payload.omx_runtime_capabilities ?? payload.capabilities); | |
| + const nativeSubagents = capabilities?.native_subagents ?? capabilities?.nativeSubagents; | |
| + const multiAgent = capabilities?.multi_agent_v1 ?? capabilities?.multiAgentV1; | |
| + const roleRouting = capabilities?.role_routing ?? capabilities?.roleRouting; | |
| + if (roleRouting === true && (nativeSubagents === true || multiAgent === true)) return true; | |
| + | |
| + // Codex App v2 exposes typed collaboration tools to the model while its | |
| + // native hook payload omits the legacy available_tools/capabilities inventory. | |
| + // Treat only the exact host-issued v2 marker as positive surface evidence; | |
| + // partial or older multi-agent markers remain unknown/fail-closed. | |
| + if (payloadAdvertisesCodexAppV2NativeSpawn(payload)) return true; | |
| + | |
| + const availableTools = payload.available_tools ?? payload.availableTools ?? payload.tools; | |
| + if (Array.isArray(availableTools) && availableTools.some((tool) => { | |
| + const record = safeObject(tool); | |
| + const name = typeof tool === "string" | |
| + ? tool | |
| + : safeString(record?.name ?? record?.tool_name ?? record?.toolName).trim(); | |
| + return isNativeSubagentSpawnToolName(name); | |
| + })) return true; | |
| + return rootTranscript?.hasTypedNativeSpawn === true; | |
| +} | |
| + | |
| async function buildNativeOutsideTmuxUltragoalPromptBlockState( | |
| classification: KeywordInputClassification, | |
| cwd: string, | |
| @@ -2669,6 +2949,7 @@ async function buildNativeOutsideTmuxUltragoalPromptBlockState( | |
| sessionId?: string, | |
| threadId?: string, | |
| turnId?: string, | |
| + rootTranscript: SafeRootTranscriptMetadata | null = null, | |
| ): Promise<SkillActiveState | null> { | |
| const match = classification.matches.find((entry) => entry.skill === "ultragoal") ?? null; | |
| if (!match) return null; | |
| @@ -2686,12 +2967,96 @@ async function buildNativeOutsideTmuxUltragoalPromptBlockState( | |
| // planning guards). Fall through unchanged to recordSkillActivation's own | |
| // transition/continuation/supervision logic. | |
| const canonicalState = await readVisibleSkillActiveStateForStateDir(stateDir, sessionId); | |
| - const hasAnyActiveTrackedSkill = canonicalState | |
| - ? listActiveSkills(canonicalState).some((entry) => ( | |
| + const activeTrackedSkills = canonicalState | |
| + ? listActiveSkills(canonicalState).filter((entry) => ( | |
| matchesSkillStopContext(entry, canonicalState, sessionId, threadId ?? "") | |
| )) | |
| - : false; | |
| - if (hasAnyActiveTrackedSkill) return null; | |
| + : []; | |
| + if (activeTrackedSkills.length > 0) return null; | |
| + | |
| + const rootThreadId = safeString(threadId ?? readPayloadThreadId(payload)).trim() | |
| + || rootTranscript?.sessionId | |
| + || ""; | |
| + const nativeConfig = resolveNativeUltragoalConfig(); | |
| + const nativeSupport = resolveNativeSubagentSupportStatus({ | |
| + payload, | |
| + persistedSupportBlocker: await readJsonIfExists(nativeSubagentSupportBlockerPath(stateDir)), | |
| + persistedRoleRoutingMarker: readRoleRoutingMarker(stateDir, { cwd, sessionId }), | |
| + persistedCapacityBlocker: await readJsonIfExists(nativeSubagentCapacityBlockerPath(stateDir)), | |
| + cwd, | |
| + sessionId, | |
| + }); | |
| + if (nativeConfig.backend === "local-experimental" && rootThreadId) { | |
| + const ready = await reconcileLocalNativeExecutorAssignments({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId, | |
| + }); | |
| + if (ready.ready && ready.childAgentId) return null; | |
| + if ( | |
| + payloadAdvertisesTypedNativeSpawn(payload, rootTranscript) | |
| + && ( | |
| + nativeSupport.status === "supported" | |
| + || payloadAdvertisesCodexAppV2NativeSpawn(payload) | |
| + || rootTranscript?.hasTypedNativeSpawn === true | |
| + ) | |
| + ) { | |
| + const bootstrap = await beginNativeUltragoalBootstrap({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId, | |
| + backend: nativeConfig.backend, | |
| + ttlSeconds: nativeConfig.assignmentTtlSeconds, | |
| + }); | |
| + const nowIso = new Date().toISOString(); | |
| + return { | |
| + version: 1, | |
| + active: false, | |
| + skill: "ultragoal", | |
| + keyword: match.keyword, | |
| + phase: "bootstrap", | |
| + activated_at: nowIso, | |
| + updated_at: nowIso, | |
| + source: "keyword-detector", | |
| + session_id: sessionId, | |
| + thread_id: rootThreadId, | |
| + turn_id: turnId, | |
| + active_skills: [], | |
| + transition_error: buildNativeUltragoalBootstrapRequiredReason( | |
| + sessionId, | |
| + rootThreadId, | |
| + bootstrap.generation, | |
| + ), | |
| + }; | |
| + } | |
| + } else if (nativeConfig.backend === "host" && rootThreadId) { | |
| + await beginNativeUltragoalBootstrap({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId, | |
| + backend: nativeConfig.backend, | |
| + ttlSeconds: nativeConfig.assignmentTtlSeconds, | |
| + }); | |
| + const nowIso = new Date().toISOString(); | |
| + return { | |
| + version: 1, | |
| + active: false, | |
| + skill: "ultragoal", | |
| + keyword: match.keyword, | |
| + phase: "bootstrap", | |
| + activated_at: nowIso, | |
| + updated_at: nowIso, | |
| + source: "keyword-detector", | |
| + session_id: sessionId, | |
| + thread_id: rootThreadId, | |
| + turn_id: turnId, | |
| + active_skills: [], | |
| + transition_error: ULTRAGOAL_HOST_RECEIPT_UNAVAILABLE_REASON, | |
| + }; | |
| + } | |
| const nowIso = new Date().toISOString(); | |
| return { | |
| @@ -2711,6 +3076,40 @@ async function buildNativeOutsideTmuxUltragoalPromptBlockState( | |
| }; | |
| } | |
| +async function revokeTerminalNativeUltragoalBootstrap( | |
| + cwd: string, | |
| + stateDir: string, | |
| + sessionId: string, | |
| +): Promise<void> { | |
| + if (!sessionId) return; | |
| + const bootstrap = await readNativeUltragoalBootstrap(stateDir, sessionId); | |
| + if (!bootstrap || bootstrap.status === "revoked") return; | |
| + const state = await readStopSessionPinnedState("ultragoal-state.json", cwd, sessionId, stateDir); | |
| + const phase = safeString(state?.current_phase ?? state?.currentPhase).trim().toLowerCase(); | |
| + if (state?.active === false || TERMINAL_MODE_PHASES.has(phase)) { | |
| + await revokeLocalNativeExecutorAssignment({ | |
| + stateDir, | |
| + sessionId, | |
| + reason: `ultragoal_${phase || "inactive"}`, | |
| + }); | |
| + } | |
| +} | |
| + | |
| +async function failNativeUltragoalActivation( | |
| + cwd: string, | |
| + stateDir: string, | |
| + sessionId: string, | |
| + reason: string, | |
| +): Promise<void> { | |
| + await revokeLocalNativeExecutorAssignment({ stateDir, sessionId, reason }); | |
| + await updateModeState("ultragoal", { | |
| + active: false, | |
| + current_phase: "failed", | |
| + error: reason, | |
| + last_turn_at: new Date().toISOString(), | |
| + }, cwd, sessionId); | |
| +} | |
| + | |
| function buildSkillStateCliInstruction(mode: string, statePath: string): string { | |
| return `skill: ${mode} activated and initial state initialized at ${statePath}; use CLI-first state updates via \`omx state write/read/clear --input '<json>' --json\`; use omx_state MCP only when explicit MCP compatibility is enabled.`; | |
| } | |
| @@ -4049,6 +4448,15 @@ function resolveConductorPolicyRoot(stateDir: string, fallbackCwd: string): Cond | |
| return { cwd: canonicalFallback, valid: !statePresent, statePresent, externalStateRoot: statePresent }; | |
| } | |
| +function readDirectPayloadAgentRole(payload: CodexHookPayload): string { | |
| + const roles = [...new Set([ | |
| + "agent_role", | |
| + "agentRole", | |
| + "agent_type", | |
| + "agentType", | |
| + ].map((key) => safeString(payload[key]).trim().toLowerCase()).filter(Boolean))]; | |
| + return roles.length === 1 ? roles[0] ?? "" : ""; | |
| +} | |
| function readPayloadAgentRole(payload: CodexHookPayload): string { | |
| const directRole = safeString( | |
| @@ -5467,7 +5875,11 @@ function classifyPreToolUseMutationTransport( | |
| const command = readPreToolUseCommand(payload); | |
| if ( | |
| readPreToolUseRawCommand(payload) === command | |
| - && (isAllowedOmxReadOnlyCommand(command, cwd) || isAllowedGhReadOnlyCommand(command) || isAllowedVersionProbeCommand(command)) | |
| + && ( | |
| + isAllowedOmxReadOnlyCommand(command, cwd) | |
| + || isAllowedGhReadOnlyCommand(command) | |
| + || isAllowedVersionProbeCommand(command) | |
| + ) | |
| ) return "read-only"; | |
| return commandHasDeepInterviewWriteIntent(command, 0, cwd) || collectOmxStateCommandOperations(command, "write").length > 0 || commandHasNestedCliMutationIntent(command) || classifyConductorExecutableRuntime(command, 0, cwd) !== null | |
| ? "bash" | |
| @@ -5487,7 +5899,6 @@ function classifyPreToolUseMutationTransport( | |
| return "goal-lifecycle"; | |
| } | |
| const canonicalToolName = canonicalizeNativeCollaborationToolName(toolName); | |
| - | |
| if ( | |
| CONDUCTOR_ORCHESTRATION_TOOL_NAMES.has(canonicalToolName) | |
| || canonicalToolName.startsWith("collaboration.") | |
| @@ -9443,6 +9854,39 @@ function isSingleLiteralShellInvocation(command: string): boolean { | |
| return splitShellCommandSegments(stripHeredocBodiesForCommandScan(normalizedCommand)).length === 1; | |
| } | |
| +function hasUnquotedShellFilenameExpansion(command: string): boolean { | |
| + let quote: "'" | '"' | null = null; | |
| + let braceDepth = 0; | |
| + let braceHasComma = false; | |
| + for (let index = 0; index < command.length; index += 1) { | |
| + const char = command[index] ?? ""; | |
| + if (char === "\\" && quote !== "'") { | |
| + index += 1; | |
| + continue; | |
| + } | |
| + if (char === "'" || char === '"') { | |
| + quote = quote === char ? null : quote ?? char; | |
| + continue; | |
| + } | |
| + if (quote) continue; | |
| + if (char === "*" || char === "?" || char === "[") return true; | |
| + if (char === "{") { | |
| + braceDepth += 1; | |
| + if (braceDepth === 1) braceHasComma = false; | |
| + continue; | |
| + } | |
| + if (char === "," && braceDepth > 0) { | |
| + braceHasComma = true; | |
| + continue; | |
| + } | |
| + if (char === "}" && braceDepth > 0) { | |
| + braceDepth -= 1; | |
| + if (braceDepth === 0 && braceHasComma) return true; | |
| + } | |
| + } | |
| + return false; | |
| +} | |
| + | |
| function literalInvocationWords(command: string): string[] { | |
| return tokenizeShellWords(normalizeShellLineContinuations(command).trim()).map(shellWordLiteral); | |
| } | |
| @@ -9504,6 +9948,7 @@ async function handleDirectOmxCancel(input: { | |
| command: string; rawCommand: string; cwd: string; stateDir: string; | |
| canonicalSessionId: string; payload: CodexHookPayload; allowForce: boolean; | |
| activeState: Record<string, unknown>; | |
| + rootTranscript?: SafeRootTranscriptMetadata | null; | |
| }): Promise<DirectCancelResult> { | |
| const cancelLike = /^[ \t]*omx[ \t]+cancel\b/.test(input.command); | |
| if (!cancelLike) return { kind: "not-direct-cancel" }; | |
| @@ -9520,7 +9965,13 @@ async function handleDirectOmxCancel(input: { | |
| if (!input.canonicalSessionId || aliases.length !== 1 || aliases[0] !== input.canonicalSessionId || payloadHasConflictingIdentityAliases(input.payload)) { | |
| return { kind: "denied", reason: "session_binding", output: directCancelOutput("session_binding") }; | |
| } | |
| - const actor = await resolvePreToolUseWriteActor(input.payload, input.cwd, input.stateDir, input.canonicalSessionId); | |
| + const actor = await resolvePreToolUseWriteActor( | |
| + input.payload, | |
| + input.cwd, | |
| + input.stateDir, | |
| + input.canonicalSessionId, | |
| + input.rootTranscript, | |
| + ); | |
| if (actor !== "main-root") return { kind: "denied", reason: "actor_authority", output: directCancelOutput("actor_authority") }; | |
| const recovery = await readHookCancelTransactionRecoveryState({ stateDir: input.stateDir, canonicalSessionId: input.canonicalSessionId }); | |
| @@ -10424,6 +10875,7 @@ async function buildRalplanPreToolUseBoundaryOutput( | |
| resolvedSessionId?: string, | |
| executionCwd = cwd, | |
| authorityCwd = executionCwd, | |
| + rootTranscript: SafeRootTranscriptMetadata | null = null, | |
| ): Promise<Record<string, unknown> | null> { | |
| const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim(); | |
| const threadId = readPayloadThreadId(payload); | |
| @@ -10440,7 +10892,7 @@ async function buildRalplanPreToolUseBoundaryOutput( | |
| const command = readPreToolUseCommand(payload); | |
| const pathCandidates = readPreToolUsePathCandidates(payload); | |
| const mutationTransport = classifyPreToolUseMutationTransport(payload, toolName); | |
| - const actor = await resolvePreToolUseWriteActor(payload, authorityCwd, stateDir, sessionId); | |
| + const actor = await resolvePreToolUseWriteActor(payload, authorityCwd, stateDir, sessionId, rootTranscript); | |
| if (actor === "team-worker") { | |
| if (teamWorkerMutationTargetsProtectedWorkflowState(payload, toolName, command, executionCwd, stateDir)) { | |
| return buildTeamWorkerProtectedStateDeny( | |
| @@ -10528,15 +10980,27 @@ async function buildRalplanPreToolUseBoundaryOutput( | |
| }; | |
| } | |
| -function buildRawProtectedWorkflowStatePathOutput( | |
| +async function buildRawProtectedWorkflowStatePathOutput( | |
| payload: CodexHookPayload, | |
| cwd: string, | |
| stateDir: string, | |
| -): Record<string, unknown> | null { | |
| + authoritativeSessionId = "", | |
| + activeConductor = false, | |
| + rootTranscript: SafeRootTranscriptMetadata | null = null, | |
| +): Promise<Record<string, unknown> | null> { | |
| const toolName = safeString(payload.tool_name).trim(); | |
| if (classifyPreToolUseMutationTransport(payload, toolName, cwd) !== "path") return null; | |
| const candidates = collectImplementationToolPathCandidates(payload, toolName, readPreToolUsePathCandidates(payload)); | |
| - const protectedPath = candidates.find((candidate) => isRawProtectedPlanningStateCandidate(stateDir, cwd, candidate)); | |
| + const protectedCandidates = candidates.filter((candidate) => isRawProtectedPlanningStateCandidate(stateDir, cwd, candidate)); | |
| + if ( | |
| + nativeSubagentAssignmentsOptedIn() | |
| + && activeConductor | |
| + && authoritativeSessionId | |
| + && protectedCandidates.length > 0 | |
| + && protectedCandidates.every((candidate) => isSessionBoundNativeAssignmentPath(cwd, candidate, authoritativeSessionId)) | |
| + && await resolvePreToolUseWriteActor(payload, cwd, stateDir, authoritativeSessionId, rootTranscript) === "main-root" | |
| + ) return null; | |
| + const protectedPath = protectedCandidates[0]; | |
| if (protectedPath === undefined) return null; | |
| return { | |
| decision: "block", | |
| @@ -10555,6 +11019,7 @@ async function buildDeepInterviewPreToolUseBoundaryOutput( | |
| resolvedSessionId?: string, | |
| executionCwd = cwd, | |
| authorityCwd = executionCwd, | |
| + rootTranscript: SafeRootTranscriptMetadata | null = null, | |
| ): Promise<Record<string, unknown> | null> { | |
| const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim(); | |
| const threadId = readPayloadThreadId(payload); | |
| @@ -10571,7 +11036,7 @@ async function buildDeepInterviewPreToolUseBoundaryOutput( | |
| const command = readPreToolUseCommand(payload); | |
| const pathCandidates = readPreToolUsePathCandidates(payload); | |
| const mutationTransport = classifyPreToolUseMutationTransport(payload, toolName); | |
| - const actor = await resolvePreToolUseWriteActor(payload, authorityCwd, stateDir, sessionId); | |
| + const actor = await resolvePreToolUseWriteActor(payload, authorityCwd, stateDir, sessionId, rootTranscript); | |
| if (actor === "team-worker") { | |
| if (teamWorkerMutationTargetsProtectedWorkflowState(payload, toolName, command, executionCwd, stateDir)) { | |
| return buildTeamWorkerProtectedStateDeny( | |
| @@ -10799,6 +11264,99 @@ interface ActiveConductorState { | |
| type PreToolUseWriteActor = "main-root" | "native-child" | "provenance-conflict" | "team-worker"; | |
| +type NativeAssignmentAction = "path-write" | "bash-write"; | |
| + | |
| +interface NativeSubagentAssignment { | |
| + schema_version: 1; | |
| + kind: "native-subagent-assignment"; | |
| + lifecycle: "active"; | |
| + root_session_id: string; | |
| + root_thread_id: string; | |
| + child_agent_id: string; | |
| + agent_type: string; | |
| + allowed_actions: NativeAssignmentAction[]; | |
| + path_prefixes: string[]; | |
| + issued_at: string; | |
| + expires_at: string; | |
| + bootstrap_generation: string; | |
| +} | |
| + | |
| + | |
| +function nativeAssignmentPath(stateDir: string, sessionId: string, childAgentId: string): string { | |
| + return join( | |
| + stateDir, | |
| + "sessions", | |
| + sessionId, | |
| + NATIVE_ASSIGNMENT_DIRECTORY, | |
| + `${encodeURIComponent(childAgentId)}.json`, | |
| + ); | |
| +} | |
| + | |
| +function isNativeAssignmentPath(stateDir: string, rawPath: string, executionCwd: string): boolean { | |
| + const absolute = isAbsolute(rawPath) ? resolve(rawPath) : resolve(executionCwd, rawPath); | |
| + const assignmentRoot = resolve(stateDir, "sessions"); | |
| + const relativePath = relative(assignmentRoot, absolute).replace(/\\/g, "/"); | |
| + return relativePath !== "" | |
| + && !relativePath.startsWith("../") | |
| + && /^.+\/native-assignments(?:\/|$)/.test(relativePath); | |
| +} | |
| + | |
| +async function readNativeSubagentAssignment( | |
| + payload: CodexHookPayload, | |
| + cwd: string, | |
| + stateDir: string, | |
| + sessionId: string, | |
| +): Promise<NativeSubagentAssignment | null> { | |
| + if (!nativeSubagentAssignmentsOptedIn()) return null; | |
| + const childAgentId = readPayloadAgentId(payload); | |
| + const agentType = readDirectPayloadAgentRole(payload); | |
| + if (!childAgentId || !agentType || resolveInstalledRoleName(agentType, undefined, cwd) === null) return null; | |
| + | |
| + const trackingState = await readSubagentTrackingState(cwd).catch(() => null); | |
| + const trackedSession = safeObject(trackingState?.sessions?.[sessionId]); | |
| + const bootstrap = await readNativeUltragoalBootstrap(stateDir, sessionId); | |
| + const leaderThreadId = safeString(trackedSession?.leader_thread_id ?? bootstrap?.root_thread_id).trim(); | |
| + if (!leaderThreadId) return null; | |
| + const identity = await validateLocalNativeExecutorIdentity({ | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId: leaderThreadId, | |
| + childAgentId, | |
| + receiptNotBefore: bootstrap?.created_at, | |
| + receiptNotAfter: bootstrap?.expires_at, | |
| + }); | |
| + if (!identity.ok) return null; | |
| + | |
| + const path = nativeAssignmentPath(stateDir, sessionId, childAgentId); | |
| + if (!await nativeExecutorAssignmentFileIsSafe(stateDir, path)) return null; | |
| + let raw: Record<string, unknown> | null = null; | |
| + try { | |
| + raw = safeObject(JSON.parse(readFileSync(path, "utf-8"))); | |
| + } catch { | |
| + return null; | |
| + } | |
| + const assignment = await parseActiveLocalNativeExecutorAssignment(raw, { | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId: leaderThreadId, | |
| + childAgentId, | |
| + agentType, | |
| + }); | |
| + if (!assignment) return null; | |
| + const ready = await findReadyLocalNativeExecutorAssignment({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId: leaderThreadId, | |
| + }); | |
| + if ( | |
| + !ready | |
| + || ready.child_agent_id !== assignment.child_agent_id | |
| + || ready.bootstrap_generation !== assignment.bootstrap_generation | |
| + ) return null; | |
| + return assignment; | |
| +} | |
| + | |
| function hasSubagentThreadSpawnProvenance(payload: CodexHookPayload): boolean { | |
| const source = payload.source; | |
| if (!source || typeof source !== "object") return false; | |
| @@ -10812,6 +11370,7 @@ async function resolvePreToolUseWriteActor( | |
| cwd: string, | |
| stateDir: string, | |
| sessionId: string, | |
| + rootTranscript: SafeRootTranscriptMetadata | null = null, | |
| ): Promise<PreToolUseWriteActor> { | |
| if (payloadHasConflictingIdentityAliases(payload)) return "provenance-conflict"; | |
| const unofficialAgentId = safeString(payload.agentId).trim(); | |
| @@ -10827,13 +11386,24 @@ async function resolvePreToolUseWriteActor( | |
| const leaderAnchors = new Set<string>(); | |
| const trackerLeader = safeString(session?.leader_thread_id).trim(); | |
| if (trackerLeader) leaderAnchors.add(trackerLeader); | |
| - if (leaderAnchors.has(payloadAgentId) || leaderAnchors.has(payloadThreadId)) return "main-root"; | |
| + if (leaderAnchors.has(payloadAgentId) || leaderAnchors.has(payloadThreadId)) { | |
| + const hasCompanionIdentityClaim = payloadHasOwnerIdentityClaim(payload) | |
| + || ["agent_role", "agentRole", "agent_type", "agentType"].some((key) => ( | |
| + Object.prototype.hasOwnProperty.call(payload, key) | |
| + )); | |
| + return hasCompanionIdentityClaim ? "provenance-conflict" : "main-root"; | |
| + } | |
| const rootSessionState = await readRootSessionStateFromStateDir(stateDir).catch(() => null); | |
| const payloadSessionId = readPayloadSessionId(payload); | |
| const leaderNativeSessionId = rootSessionState && payloadMatchesSessionPointer(sessionId, rootSessionState) | |
| ? safeString(rootSessionState.native_session_id).trim() | |
| : ""; | |
| if (!payloadAgentId && !payloadThreadId && leaderNativeSessionId && payloadSessionId === leaderNativeSessionId) return "main-root"; | |
| + if ( | |
| + !payloadAgentId | |
| + && rootTranscript?.sessionId === payloadSessionId | |
| + && rootTranscript.hasExactTurnContext | |
| + ) return "main-root"; | |
| if (hasSubagentThreadSpawnProvenance(payload)) return "native-child"; | |
| if (payloadHasOwnerIdentityClaim(payload)) return "native-child"; | |
| @@ -10930,6 +11500,25 @@ const ULTRAGOAL_NO_OWNER_DENY_REASON = | |
| + "is tmux-only, and native child/descendant provenance intentionally does not grant write authority (see #3127). " | |
| + "Activating Conductor mode here would deadlock with only `omx cancel` reachable."; | |
| +const ULTRAGOAL_HOST_RECEIPT_UNAVAILABLE_REASON = | |
| + `OMX-ULTRAGOAL-HOST-RECEIPT-UNAVAILABLE: ${DOCUMENTED_HOST_RECEIPT_UNAVAILABLE}. ` | |
| + + "Host-backed native executor authority remains fail-closed until Codex exposes a documented receipt verifier."; | |
| + | |
| +function buildNativeUltragoalBootstrapRequiredReason( | |
| + sessionId: string, | |
| + rootThreadId: string, | |
| + generation: string, | |
| +): string { | |
| + return "OMX-ULTRAGOAL-BOOTSTRAP-REQUIRED: typed native execution is available, but Ultragoal activation remains " | |
| + + "inactive until an exact executor is spawned and authorized. Spawn one typed `executor` with instructions to " | |
| + + "wait without tool calls for a follow-up after activation. Retain the returned agent path, then run " | |
| + + "`omx ultragoal native-bootstrap authorize " | |
| + + `--session-id ${JSON.stringify(sessionId)} --root-thread-id ${JSON.stringify(rootThreadId)} ` | |
| + + `--bootstrap-generation ${JSON.stringify(generation)} ` | |
| + + "--child-path <spawned-agent-path> --path-prefix . --json`, then retry the canonical Ultragoal state activation " | |
| + + "and use `followup_task` to start the authorized executor work. "; | |
| +} | |
| + | |
| function collectUltragoalActivationCandidatePayloads( | |
| payload: CodexHookPayload, | |
| toolName: string, | |
| @@ -10965,12 +11554,284 @@ function isUltragoalConductorActivationPayload(input: Record<string, unknown> | | |
| return isNonTerminalPhase(flattened.current_phase ?? flattened.currentPhase); | |
| } | |
| +const ACTIVE_ULTRAGOAL_PHASE_TRANSITION_PHASES = new Set([ | |
| + "planning", | |
| + "executing", | |
| + "verifying", | |
| + "reviewing", | |
| + "checkpointing", | |
| + "blocked", | |
| +]); | |
| + | |
| +function isExactActiveUltragoalPhaseTransitionPayload(input: Record<string, unknown>): boolean { | |
| + const keys = Object.keys(input); | |
| + if ( | |
| + keys.length !== 3 | |
| + || !keys.includes("mode") | |
| + || !keys.includes("active") | |
| + || (keys.includes("current_phase") === keys.includes("currentPhase")) | |
| + ) return false; | |
| + const phase = safeString(input.current_phase ?? input.currentPhase).trim().toLowerCase(); | |
| + return input.mode === "ultragoal" | |
| + && input.active === true | |
| + && ACTIVE_ULTRAGOAL_PHASE_TRANSITION_PHASES.has(phase); | |
| +} | |
| + | |
| +function isExactActiveUltragoalPhaseTransitionTransport( | |
| + payload: CodexHookPayload, | |
| + cwd: string, | |
| +): boolean { | |
| + const toolName = safeString(payload.tool_name).trim(); | |
| + if (toolName === "mcp__omx_state__state_write") { | |
| + const input = safeObject(payload.tool_input); | |
| + return Boolean(input && isExactActiveUltragoalPhaseTransitionPayload(input)); | |
| + } | |
| + if (toolName !== "Bash") return false; | |
| + const command = readPreToolUseCommand(payload); | |
| + if (!isSingleLiteralShellInvocation(command)) return false; | |
| + const words = tokenizeConductorShellWords(command); | |
| + const invocations = collectShellCliInvocations(words); | |
| + if (invocations.length !== 1 || invocations[0]?.commandIndex !== 0) return false; | |
| + if (shellWordLiteral(words[0] ?? "") !== "omx") return false; | |
| + if (!isStaticallyValidatedOmxStateWriteInvocation(words, 0)) return false; | |
| + const payloads = readAllStateWriteInputPayloads(cwd, command); | |
| + return payloads.length === 1 && isExactActiveUltragoalPhaseTransitionPayload(payloads[0]!); | |
| +} | |
| + | |
| +function isExactTerminalUltragoalStatePayload(input: Record<string, unknown>): boolean { | |
| + const keys = Object.keys(input); | |
| + return keys.length === 3 | |
| + && keys.includes("mode") | |
| + && keys.includes("active") | |
| + && keys.includes("current_phase") | |
| + && input.mode === "ultragoal" | |
| + && input.active === false | |
| + && input.current_phase === "complete"; | |
| +} | |
| + | |
| +function isExactTerminalUltragoalStateTransport( | |
| + payload: CodexHookPayload, | |
| + cwd: string, | |
| +): boolean { | |
| + const toolName = safeString(payload.tool_name).trim(); | |
| + if (toolName === "mcp__omx_state__state_write") { | |
| + const input = safeObject(payload.tool_input); | |
| + return Boolean(input && isExactTerminalUltragoalStatePayload(input)); | |
| + } | |
| + if (toolName !== "Bash") return false; | |
| + const command = readPreToolUseCommand(payload); | |
| + if (!isSingleLiteralShellInvocation(command)) return false; | |
| + const words = tokenizeConductorShellWords(command); | |
| + const invocations = collectShellCliInvocations(words); | |
| + if (invocations.length !== 1 || invocations[0]?.commandIndex !== 0) return false; | |
| + if (shellWordLiteral(words[0] ?? "") !== "omx") return false; | |
| + if (!isStaticallyValidatedOmxStateWriteInvocation(words, 0)) return false; | |
| + const payloads = readAllStateWriteInputPayloads(cwd, command); | |
| + return payloads.length === 1 && isExactTerminalUltragoalStatePayload(payloads[0]!); | |
| +} | |
| + | |
| +async function readPinnedJsonObject(path: string): Promise<Record<string, unknown> | null> { | |
| + const metadata = await lstat(path).catch(() => null); | |
| + if ( | |
| + !metadata | |
| + || !metadata.isFile() | |
| + || metadata.isSymbolicLink() | |
| + || metadata.nlink !== 1 | |
| + || (metadata.mode & 0o022) !== 0 | |
| + ) return null; | |
| + const handle = await open(path, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0)).catch(() => null); | |
| + if (!handle) return null; | |
| + try { | |
| + const openedMetadata = await handle.stat(); | |
| + if ( | |
| + !openedMetadata.isFile() | |
| + || openedMetadata.nlink !== 1 | |
| + || (openedMetadata.mode & 0o022) !== 0 | |
| + || openedMetadata.dev !== metadata.dev | |
| + || openedMetadata.ino !== metadata.ino | |
| + ) return null; | |
| + const raw = await handle.readFile({ encoding: "utf-8" }); | |
| + const finalMetadata = await lstat(path).catch(() => null); | |
| + if ( | |
| + !finalMetadata | |
| + || !finalMetadata.isFile() | |
| + || finalMetadata.isSymbolicLink() | |
| + || finalMetadata.nlink !== 1 | |
| + || (finalMetadata.mode & 0o022) !== 0 | |
| + || finalMetadata.dev !== metadata.dev | |
| + || finalMetadata.ino !== metadata.ino | |
| + || finalMetadata.size !== metadata.size | |
| + || Buffer.byteLength(raw, "utf-8") !== metadata.size | |
| + ) return null; | |
| + return safeObject(JSON.parse(raw)); | |
| + } catch { | |
| + return null; | |
| + } finally { | |
| + await handle.close().catch(() => {}); | |
| + } | |
| +} | |
| + | |
| +async function readPinnedActiveUltragoalSessionState( | |
| + stateDir: string, | |
| + sessionId: string, | |
| +): Promise<Record<string, unknown> | null> { | |
| + const path = join(stateDir, "sessions", sessionId, "ultragoal-state.json"); | |
| + const metadata = await lstat(path).catch(() => null); | |
| + if ( | |
| + !metadata | |
| + || !metadata.isFile() | |
| + || metadata.isSymbolicLink() | |
| + || metadata.nlink !== 1 | |
| + || (metadata.mode & 0o022) !== 0 | |
| + ) return null; | |
| + const handle = await open(path, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0)).catch(() => null); | |
| + if (!handle) return null; | |
| + try { | |
| + const openedMetadata = await handle.stat(); | |
| + if ( | |
| + !openedMetadata.isFile() | |
| + || openedMetadata.nlink !== 1 | |
| + || (openedMetadata.mode & 0o022) !== 0 | |
| + || openedMetadata.dev !== metadata.dev | |
| + || openedMetadata.ino !== metadata.ino | |
| + ) return null; | |
| + const raw = await handle.readFile({ encoding: "utf-8" }); | |
| + const state = safeObject(JSON.parse(raw)); | |
| + const persistedMode = safeString(state?.mode).trim(); | |
| + const persistedSessionAliases = state ? [ | |
| + state.session_id, | |
| + state.owner_omx_session_id, | |
| + state.codex_session_id, | |
| + state.owner_codex_session_id, | |
| + ].filter((value) => value !== undefined) : []; | |
| + if ( | |
| + !state | |
| + || (persistedMode !== "" && persistedMode !== "ultragoal") | |
| + || state.active !== true | |
| + || persistedSessionAliases.some((value) => safeString(value).trim() !== sessionId) | |
| + || !ACTIVE_ULTRAGOAL_PHASE_TRANSITION_PHASES.has( | |
| + safeString(state.current_phase ?? state.currentPhase).trim().toLowerCase(), | |
| + ) | |
| + ) return null; | |
| + return state; | |
| + } catch { | |
| + return null; | |
| + } finally { | |
| + await handle.close().catch(() => {}); | |
| + } | |
| +} | |
| + | |
| +async function isAuthorizedTerminalUltragoalStateFallback(input: { | |
| + payload: CodexHookPayload; | |
| + policyCwd: string; | |
| + stateDir: string; | |
| + sessionId: string; | |
| + pointer: SessionPointerReadResult | null; | |
| + rootTranscript: SafeRootTranscriptMetadata | null; | |
| +}): Promise<boolean> { | |
| + const { payload, policyCwd, stateDir, sessionId, pointer, rootTranscript } = input; | |
| + if (!rootTranscript?.hasExactTurnContext || rootTranscript.sessionId !== sessionId) return false; | |
| + if ( | |
| + !pointer | |
| + || (pointer.status !== "usable" && pointer.status !== "identity-indeterminate") | |
| + || !pointer.state | |
| + ) return false; | |
| + const pointerState = pointer.state; | |
| + if (!payloadMatchesSessionPointer(sessionId, pointerState)) return false; | |
| + if (!safeString(pointerState.cwd).trim() || !isSessionStateAuthoritativeForCwd(pointerState, policyCwd)) return false; | |
| + const pointerStateRoot = safeString(pointerState.state_root).trim(); | |
| + if (!pointerStateRoot || !sameFilePath(pointerStateRoot, stateDir)) return false; | |
| + const pointerNativeSessionId = safeString(pointerState.native_session_id).trim(); | |
| + if (pointerNativeSessionId && pointerNativeSessionId !== rootTranscript.sessionId) return false; | |
| + if (!isExactTerminalUltragoalStateTransport(payload, policyCwd)) return false; | |
| + | |
| + const activeState = await readPinnedActiveUltragoalSessionState(stateDir, sessionId); | |
| + if (safeString(activeState?.current_phase ?? activeState?.currentPhase).trim().toLowerCase() !== "checkpointing") { | |
| + return false; | |
| + } | |
| + const plan = await readPinnedJsonObject(join(policyCwd, ".omx", "ultragoal", "goals.json")); | |
| + const goals = Array.isArray(plan?.goals) ? plan.goals : []; | |
| + const aggregateCompletion = safeObject(plan?.aggregateCompletion); | |
| + if ( | |
| + goals.length === 0 | |
| + || goals.some((goal) => safeString(safeObject(goal)?.status).trim().toLowerCase() !== "complete") | |
| + || safeString(aggregateCompletion?.status).trim().toLowerCase() !== "complete" | |
| + ) return false; | |
| + | |
| + const bootstrap = await readNativeUltragoalBootstrap(stateDir, sessionId); | |
| + if ( | |
| + !bootstrap | |
| + || bootstrap.status !== "revoked" | |
| + || bootstrap.session_id !== sessionId | |
| + || bootstrap.root_thread_id !== rootTranscript.sessionId | |
| + || !bootstrap.child_agent_id | |
| + ) return false; | |
| + const assignment = await findReadyLocalNativeExecutorAssignment({ | |
| + cwd: policyCwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId: rootTranscript.sessionId, | |
| + }); | |
| + return assignment === null; | |
| +} | |
| + | |
| +async function isAuthorizedActiveUltragoalPhaseTransitionFallback(input: { | |
| + payload: CodexHookPayload; | |
| + policyCwd: string; | |
| + stateDir: string; | |
| + sessionId: string; | |
| + pointer: SessionPointerReadResult | null; | |
| + rootTranscript: SafeRootTranscriptMetadata | null; | |
| +}): Promise<boolean> { | |
| + const { payload, policyCwd, stateDir, sessionId, pointer, rootTranscript } = input; | |
| + if (!rootTranscript?.hasExactTurnContext || rootTranscript.sessionId !== sessionId) return false; | |
| + if ( | |
| + !pointer | |
| + || (pointer.status !== "usable" && pointer.status !== "identity-indeterminate") | |
| + || !pointer.state | |
| + ) return false; | |
| + const pointerState = pointer.state; | |
| + if (!payloadMatchesSessionPointer(sessionId, pointerState)) return false; | |
| + if (!safeString(pointerState.cwd).trim() || !isSessionStateAuthoritativeForCwd(pointerState, policyCwd)) return false; | |
| + const pointerStateRoot = safeString(pointerState.state_root).trim(); | |
| + if (!pointerStateRoot || !sameFilePath(pointerStateRoot, stateDir)) return false; | |
| + const pointerNativeSessionId = safeString(pointerState.native_session_id).trim(); | |
| + if (pointerNativeSessionId && pointerNativeSessionId !== rootTranscript.sessionId) return false; | |
| + if (!isExactActiveUltragoalPhaseTransitionTransport(payload, policyCwd)) return false; | |
| + if (!await readPinnedActiveUltragoalSessionState(stateDir, sessionId)) return false; | |
| + const bootstrap = await readNativeUltragoalBootstrap(stateDir, sessionId); | |
| + if ( | |
| + !bootstrap | |
| + || bootstrap.status !== "active" | |
| + || bootstrap.session_id !== sessionId | |
| + || bootstrap.root_thread_id !== rootTranscript.sessionId | |
| + || !bootstrap.child_agent_id | |
| + ) return false; | |
| + const assignment = await findReadyLocalNativeExecutorAssignment({ | |
| + cwd: policyCwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId: rootTranscript.sessionId, | |
| + }); | |
| + return Boolean( | |
| + assignment | |
| + && assignment.child_agent_id === bootstrap.child_agent_id | |
| + && assignment.bootstrap_generation === bootstrap.generation, | |
| + ); | |
| +} | |
| + | |
| +/** @internal Test-only authority evaluator for the session-scoped phase fallback. */ | |
| +export const __isAuthorizedActiveUltragoalPhaseTransitionFallbackForTests = | |
| + isAuthorizedActiveUltragoalPhaseTransitionFallback; | |
| + | |
| async function buildUltragoalNoOwnerActivationGuardOutput( | |
| payload: CodexHookPayload, | |
| cwd: string, | |
| stateDir: string, | |
| resolvedSessionId?: string, | |
| policyCwd = cwd, | |
| + rootTranscript: SafeRootTranscriptMetadata | null = null, | |
| + pointer: SessionPointerReadResult | null = null, | |
| ): Promise<Record<string, unknown> | null> { | |
| const toolName = safeString(payload.tool_name).trim(); | |
| if (toolName !== "Bash" && toolName !== "mcp__omx_state__state_write") return null; | |
| @@ -10980,14 +11841,40 @@ async function buildUltragoalNoOwnerActivationGuardOutput( | |
| const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim(); | |
| if (!sessionId) return null; | |
| + if ( | |
| + rootTranscript?.hasExactTurnContext | |
| + && rootTranscript.sessionId === sessionId | |
| + && pointer?.state | |
| + && !payloadMatchesSessionPointer(sessionId, pointer.state) | |
| + && isExactActiveUltragoalPhaseTransitionTransport(payload, policyCwd) | |
| + ) { | |
| + return { | |
| + decision: "block", | |
| + reason: "OMX-ULTRAGOAL-FOREIGN-SESSION: the exact root phase transition does not match the selected workspace session pointer.", | |
| + hookSpecificOutput: { | |
| + hookEventName: "PreToolUse", | |
| + additionalContext: "The selected session pointer belongs to another Ultragoal session; phase writes remain fail-closed.", | |
| + }, | |
| + }; | |
| + } | |
| + | |
| // Already-active sessions (phase updates, resumption, or recovery on an | |
| // existing stuck state) remain governed by the existing Conductor write | |
| // guard and `omx cancel`, not this pre-activation refusal. | |
| if (await readActiveConductorStateForPreToolUse(payload, policyCwd, stateDir, sessionId)) return null; | |
| - const writeActor = await resolvePreToolUseWriteActor(payload, cwd, stateDir, sessionId); | |
| + const writeActor = await resolvePreToolUseWriteActor(payload, cwd, stateDir, sessionId, rootTranscript); | |
| if (writeActor !== "main-root") return null; | |
| + if (await isAuthorizedActiveUltragoalPhaseTransitionFallback({ | |
| + payload, | |
| + policyCwd, | |
| + stateDir, | |
| + sessionId, | |
| + pointer, | |
| + rootTranscript, | |
| + })) return null; | |
| + | |
| const executionSurface = resolveCodexExecutionSurface(cwd, { | |
| hookEventName: "PreToolUse", | |
| payload, | |
| @@ -10996,21 +11883,164 @@ async function buildUltragoalNoOwnerActivationGuardOutput( | |
| }); | |
| if (executionSurface.launcher !== "native" || executionSurface.transport !== "outside-tmux") return null; | |
| + const rootThreadId = readPayloadThreadId(payload) || rootTranscript?.sessionId || ""; | |
| + const nativeConfig = resolveNativeUltragoalConfig(); | |
| + if (nativeConfig.backend === "host") { | |
| + return { | |
| + decision: "block", | |
| + reason: ULTRAGOAL_HOST_RECEIPT_UNAVAILABLE_REASON, | |
| + hookSpecificOutput: { | |
| + hookEventName: "PreToolUse", | |
| + additionalContext: ULTRAGOAL_HOST_RECEIPT_UNAVAILABLE_REASON, | |
| + }, | |
| + }; | |
| + } | |
| + if (nativeConfig.backend === "local-experimental" && rootThreadId) { | |
| + let bootstrap = await readNativeUltragoalBootstrap(stateDir, sessionId); | |
| + if (!bootstrap) { | |
| + const nativeSupport = resolveNativeSubagentSupportStatus({ | |
| + payload, | |
| + persistedSupportBlocker: await readJsonIfExists(nativeSubagentSupportBlockerPath(stateDir)), | |
| + persistedRoleRoutingMarker: readRoleRoutingMarker(stateDir, { cwd, sessionId }), | |
| + persistedCapacityBlocker: await readJsonIfExists(nativeSubagentCapacityBlockerPath(stateDir)), | |
| + cwd, | |
| + sessionId, | |
| + }); | |
| + if ( | |
| + payloadAdvertisesTypedNativeSpawn(payload, rootTranscript) | |
| + && ( | |
| + nativeSupport.status === "supported" | |
| + || payloadAdvertisesCodexAppV2NativeSpawn(payload) | |
| + || rootTranscript?.hasTypedNativeSpawn === true | |
| + ) | |
| + ) { | |
| + bootstrap = await beginNativeUltragoalBootstrap({ | |
| + cwd: policyCwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId, | |
| + backend: nativeConfig.backend, | |
| + ttlSeconds: nativeConfig.assignmentTtlSeconds, | |
| + }); | |
| + } | |
| + } | |
| + const ready = await findReadyLocalNativeExecutorAssignment({ | |
| + cwd: policyCwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId, | |
| + }); | |
| + if (ready) { | |
| + const activation = await prepareNativeUltragoalBootstrapActivation({ | |
| + cwd: policyCwd, | |
| + stateDir, | |
| + sessionId, | |
| + childAgentId: ready.child_agent_id, | |
| + }); | |
| + if (activation.ok) return null; | |
| + } | |
| + const reason = bootstrap | |
| + ? buildNativeUltragoalBootstrapRequiredReason(sessionId, rootThreadId, bootstrap.generation) | |
| + : ULTRAGOAL_NO_OWNER_DENY_REASON; | |
| + return { | |
| + decision: "block", | |
| + reason, | |
| + hookSpecificOutput: { | |
| + hookEventName: "PreToolUse", | |
| + additionalContext: reason, | |
| + }, | |
| + }; | |
| + } | |
| + | |
| return { | |
| decision: "block", | |
| reason: ULTRAGOAL_NO_OWNER_DENY_REASON, | |
| hookSpecificOutput: { | |
| hookEventName: "PreToolUse", | |
| additionalContext: | |
| - "Standalone Ultragoal Conductor mode requires an attached tmux session (Team execution) on native Codex App " | |
| - + "outside tmux; native child/descendant provenance does not grant write authority. Do not set ultragoal " | |
| - + "active/current_phase here. Proceed with direct bounded implementation for this task without " | |
| + "Standalone Ultragoal Conductor mode requires either an attached tmux session (Team execution) or a positively " | |
| + + "advertised typed native spawn surface backed by scoped write assignments; neither is proven on this native " | |
| + + "Codex App outside-tmux event. Do not set " | |
| + + "ultragoal active/current_phase here. Proceed with direct bounded implementation for this task without " | |
| + "entering Ultragoal's Conductor-gated planning mode, or re-run from an attached tmux OMX CLI shell so " | |
| + "`omx team` is available. `omx cancel` remains available if a stuck Conductor state already exists.", | |
| }, | |
| }; | |
| } | |
| +async function buildNativeUltragoalBootstrapMutationGuardOutput( | |
| + payload: CodexHookPayload, | |
| + cwd: string, | |
| + stateDir: string, | |
| + sessionId: string, | |
| + rootTranscript: SafeRootTranscriptMetadata | null = null, | |
| +): Promise<Record<string, unknown> | null> { | |
| + if (safeString(payload.tool_name).trim() !== "Bash") return null; | |
| + const command = readPreToolUseCommand(payload); | |
| + if (!/\bomx\s+ultragoal\s+native-bootstrap\b/.test(command)) return null; | |
| + const actor = await resolvePreToolUseWriteActor(payload, cwd, stateDir, sessionId, rootTranscript); | |
| + if (actor === "main-root") return null; | |
| + const reason = actor === "native-child" | |
| + ? "OWNER_CONFIRMATION_REQUIRED: native Ultragoal bootstrap metadata is root-owned; a native child cannot authorize, revoke, or reconcile its own assignment." | |
| + : "PROVENANCE_DENIED: native Ultragoal bootstrap mutation requires exact Main-root session provenance."; | |
| + return { | |
| + decision: "block", | |
| + reason, | |
| + hookSpecificOutput: { | |
| + hookEventName: "PreToolUse", | |
| + additionalContext: reason, | |
| + }, | |
| + }; | |
| +} | |
| + | |
| +function isExactNativeUltragoalBootstrapCommand(command: string): boolean { | |
| + if (nativeAssignedBashHasUnsafeStructure(".", command)) return false; | |
| + const words = tokenizeConductorShellWords(command).map(shellWordLiteral); | |
| + return words.length >= 4 | |
| + && basename(words[0] ?? "") === "omx" | |
| + && words[1] === "ultragoal" | |
| + && words[2] === "native-bootstrap" | |
| + && ["authorize", "status", "revoke"].includes(words[3] ?? ""); | |
| +} | |
| + | |
| +async function buildPendingNativeUltragoalExecutorGuardOutput( | |
| + payload: CodexHookPayload, | |
| + cwd: string, | |
| + stateDir: string, | |
| + sessionId: string, | |
| + rootTranscript: SafeRootTranscriptMetadata | null = null, | |
| +): Promise<Record<string, unknown> | null> { | |
| + if (!sessionId) return null; | |
| + let bootstrap = await readNativeUltragoalBootstrap(stateDir, sessionId); | |
| + if (!bootstrap || bootstrap.status === "revoked" || bootstrap.status === "expired") { | |
| + return null; | |
| + } | |
| + if (bootstrap.status === "active") { | |
| + const state = await readStopSessionPinnedState("ultragoal-state.json", cwd, sessionId, stateDir); | |
| + if (isActiveConductorModeState(state, "ultragoal", sessionId)) return null; | |
| + await revokeLocalNativeExecutorAssignment({ | |
| + stateDir, | |
| + sessionId, | |
| + reason: "ultragoal_state_not_active", | |
| + }); | |
| + bootstrap = await readNativeUltragoalBootstrap(stateDir, sessionId); | |
| + } | |
| + const actor = await resolvePreToolUseWriteActor(payload, cwd, stateDir, sessionId, rootTranscript); | |
| + if (actor !== "native-child") return null; | |
| + const toolName = safeString(payload.tool_name).trim(); | |
| + if (classifyPreToolUseMutationTransport(payload, toolName, cwd) === "read-only") return null; | |
| + if (await resolveAuthorizedSendMessageChildToLeader(payload, toolName, cwd, sessionId)) return null; | |
| + const reason = "OWNER_CONFIRMATION_REQUIRED: native Ultragoal bootstrap is not active; spawned executors remain read-only until the exact assignment/receipt validates and Main-root completes activation."; | |
| + return { | |
| + decision: "block", | |
| + reason, | |
| + hookSpecificOutput: { | |
| + hookEventName: "PreToolUse", | |
| + additionalContext: reason, | |
| + }, | |
| + }; | |
| +} | |
| + | |
| function normalizeRepoRelativePath(cwd: string, rawPath: string): string | null { | |
| const candidate = rawPath.trim(); | |
| if (!candidate || isUnresolvedVariableTarget(candidate)) return null; | |
| @@ -11071,6 +12101,14 @@ function conductorPathTraversesLink(cwd: string, relativePath: string): boolean | |
| return false; | |
| } | |
| +function isSessionBoundNativeAssignmentPath(cwd: string, rawPath: string, sessionId: string): boolean { | |
| + const relativePath = normalizeRepoRelativePath(cwd, rawPath); | |
| + const match = relativePath | |
| + ? /^\.omx\/state\/sessions\/([^/]+)\/native-assignments\/[^/]+\.json$/.exec(relativePath) | |
| + : null; | |
| + return sessionId !== "" && match?.[1] === sessionId; | |
| +} | |
| + | |
| function isAllowedConductorMetadataPath(cwd: string, rawPath: string): boolean { | |
| const relativePath = normalizeRepoRelativePath(cwd, rawPath); | |
| if (!relativePath || conductorPathnameExpansionIsAmbiguous(relativePath)) return false; | |
| @@ -11942,6 +12980,18 @@ function isPositivelyReadOnlyGitCommand(words: string[], commandIndex: number): | |
| } | |
| const subcommand = shellWordLiteral(args[index] ?? ""); | |
| if (!subcommand) return false; | |
| + if (subcommand === "status") { | |
| + return index === 0 | |
| + && args.length === 2 | |
| + && shellWordLiteral(args[1] ?? "") === "--short"; | |
| + } | |
| + if (subcommand === "diff") { | |
| + return args.length === index + 5 | |
| + && shellWordLiteral(args[index + 1] ?? "") === "--no-ext-diff" | |
| + && shellWordLiteral(args[index + 2] ?? "") === "--no-textconv" | |
| + && shellWordLiteral(args[index + 3] ?? "") === "--" | |
| + && shellWordLiteral(args[index + 4] ?? "") === "."; | |
| + } | |
| const allowedOptions = new Map<string, Set<string>>([ | |
| ["cat-file", new Set(["-e", "-t", "-s", "--exists", "--batch", "--batch-check", "--batch-command", "--buffer", "--follow-symlinks", "--allow-unknown-type", "--unordered"])], | |
| ["ls-files", new Set(["-c", "--cached", "-d", "--deleted", "-m", "--modified", "-o", "--others", "-i", "--ignored", "--exclude-standard", "--directory", "--no-empty-directory", "--full-name", "--stage", "--debug", "--eol", "--deduplicate", "--error-unmatch"])], | |
| @@ -12349,7 +13399,35 @@ function sedScriptIsPositivelyReadOnly(script: string): boolean { | |
| const normalized = shellWordLiteral(script).trim(); | |
| const substitution = parseConductorSingleSubstitutionProgram(normalized); | |
| if (substitution) return !/[ew]/.test(substitution.flags); | |
| - return /^[0-9$.,+~\-]*(?:p|P|q|Q|n|N|l|d|=)$/.test(normalized); | |
| + return /^[0-9$.,+~\-\s;{}pPqQnNld=]+$/.test(normalized) | |
| + && /(?:p|P|q|Q|n|N|l|d|=)/.test(normalized); | |
| +} | |
| + | |
| +function isPositivelyClassifiedFindCommand(words: string[], commandIndex: number): boolean { | |
| + const args = collectConductorInvocationWords(words, commandIndex); | |
| + let sawPath = false; | |
| + for (let index = 0; index < args.length; index += 1) { | |
| + const raw = args[index] ?? ""; | |
| + const word = shellWordLiteral(raw); | |
| + if (!word || isDynamicNestedCommandString(word) || /[$`]/.test(raw)) return false; | |
| + if (!word.startsWith("-") && !new Set(["!", "(", ")"]).has(word)) { | |
| + if (!/^[A-Za-z0-9._~/:=@,+{}-]+$/.test(word)) return false; | |
| + sawPath = true; | |
| + continue; | |
| + } | |
| + if (new Set(["!", "(", ")", "-a", "-and", "-o", "-or", "-print", "-print0", "-prune", "-empty", "-false", "-true"]).has(word)) continue; | |
| + if (new Set(["-type", "-name", "-iname", "-path", "-ipath", "-maxdepth", "-mindepth"]).has(word)) { | |
| + const valueRaw = args[index + 1] ?? ""; | |
| + const value = shellWordLiteral(valueRaw); | |
| + if (!value || isDynamicNestedCommandString(value) || /[$`]/.test(valueRaw)) return false; | |
| + if ((word === "-maxdepth" || word === "-mindepth") && !/^[0-9]+$/.test(value)) return false; | |
| + if (word === "-type" && !/^[bcdpfls]$/.test(value)) return false; | |
| + index += 1; | |
| + continue; | |
| + } | |
| + return false; | |
| + } | |
| + return sawPath; | |
| } | |
| function isPositivelyClassifiedSedCommand(words: string[], commandIndex: number): boolean { | |
| @@ -12948,7 +14026,12 @@ function inspectConductorRuntimeExecutions(command: string, cwd?: string, depth | |
| inspection.uninspectedCommandNames.push(commandName); | |
| } | |
| } else if (commandName === "git") { | |
| - if (commandSetsGitHelper || gitCommandHasUnsafeRuntimeEnvironment(words, commandIndex) || !isPositivelyReadOnlyGitCommand(words, commandIndex)) { | |
| + if ( | |
| + commandSetsGitHelper | |
| + || conductorRedirectProducerMayBeShadowed(topLevelCommand, "git") | |
| + || gitCommandHasUnsafeRuntimeEnvironment(words, commandIndex) | |
| + || !isPositivelyReadOnlyGitCommand(words, commandIndex) | |
| + ) { | |
| inspection.uninspectedOtherRuntimeCount += 1; | |
| inspection.uninspectedCommandNames.push(commandName); | |
| } | |
| @@ -12962,6 +14045,11 @@ function inspectConductorRuntimeExecutions(command: string, cwd?: string, depth | |
| inspection.uninspectedOtherRuntimeCount += 1; | |
| inspection.uninspectedCommandNames.push(commandName); | |
| } | |
| + } else if (commandName === "find") { | |
| + if (!isPositivelyClassifiedFindCommand(words, commandIndex)) { | |
| + inspection.uninspectedOtherRuntimeCount += 1; | |
| + inspection.uninspectedCommandNames.push(commandName); | |
| + } | |
| } else if ( | |
| (commandName === "npm" || commandName === "pnpm" || commandName === "yarn") | |
| && packageManagerInvokesScriptRuntime(words, commandIndex) | |
| @@ -16096,6 +17184,132 @@ function conductorOrchestrationWordIsStatic(word: string): boolean { | |
| && !/(?:^|[^\\])[<>]\(/.test(word); | |
| } | |
| +function isExactLeaderOwnedUltragoalMetadataCommand( | |
| + command: string, | |
| + rawCommand: string, | |
| + cwd: string, | |
| +): boolean { | |
| + if ( | |
| + command !== rawCommand | |
| + || !isSingleLiteralShellInvocation(command) | |
| + || hasUnquotedShellFilenameExpansion(command) | |
| + ) return false; | |
| + const words = tokenizeConductorShellWords(command); | |
| + if (words.length < 3) return false; | |
| + if (!words.every(conductorOrchestrationWordIsStatic)) return false; | |
| + const commandWord = shellWordLiteral(words[0] ?? ""); | |
| + if (commandWord === "omx") { | |
| + if (!omxOrGjcExecutionContextIsTrusted(command, cwd, ["omx"])) return false; | |
| + } else { | |
| + if (!isAbsolute(commandWord) || !commandWord.includes("/") || commandNameFromShellWord(commandWord) !== "omx") return false; | |
| + if (commandHasUnsafeConductorShellState(command, cwd)) return false; | |
| + if (commandHasUnsafeDynamicLoaderEnvironment(command) || commandHasUnsafeLeadingRuntimeEnvironment(command)) return false; | |
| + const unsafeInheritedNames = [...OMX_GJC_TRUSTED_CONTEXT_UNSAFE_INHERITED_ENV_NAMES, ...CONDUCTOR_NODE_OUTPUT_ENVIRONMENT_NAMES]; | |
| + if (unsafeInheritedNames.some((name) => safeString(process.env[name]).trim() !== "")) return false; | |
| + const state = resolveConductorCommandPathState(words, 0, 0, createConductorRuntimeShellState(cwd)); | |
| + if (!conductorSlashCommandIsTrusted(commandWord, state, cwd)) return false; | |
| + } | |
| + | |
| + const args = words.slice(1); | |
| + if (shellWordLiteral(args[0] ?? "") !== "ultragoal") return false; | |
| + const subcommand = shellWordLiteral(args[1] ?? ""); | |
| + const trailing = args.slice(2); | |
| + | |
| + const parseOptions = ( | |
| + repeatableValueOptions: ReadonlySet<string>, | |
| + uniqueValueOptions: ReadonlySet<string>, | |
| + flagOptions: ReadonlySet<string>, | |
| + ): Map<string, string[]> | null => { | |
| + const seen = new Map<string, string[]>(); | |
| + for (let index = 0; index < trailing.length; index += 1) { | |
| + const rawOption = trailing[index] ?? ""; | |
| + const option = shellWordLiteral(rawOption); | |
| + if (!option || !option.startsWith("--") || option === "--") return null; | |
| + if (flagOptions.has(option)) { | |
| + if (seen.has(option)) return null; | |
| + seen.set(option, []); | |
| + continue; | |
| + } | |
| + const name = option.split("=", 1)[0] ?? ""; | |
| + if (!repeatableValueOptions.has(name) && !uniqueValueOptions.has(name)) return null; | |
| + if (uniqueValueOptions.has(name) && seen.has(name)) return null; | |
| + const inlineValue = option.startsWith(`${name}=`); | |
| + const rawValue = inlineValue ? rawOption.slice(rawOption.indexOf("=") + 1) : trailing[index + 1] ?? ""; | |
| + const value = shellWordLiteral(rawValue); | |
| + if ( | |
| + !value | |
| + || !conductorOrchestrationWordIsStatic(rawValue) | |
| + || (!inlineValue && value.startsWith("-")) | |
| + ) return null; | |
| + if (option === name) index += 1; | |
| + seen.set(name, [...(seen.get(name) ?? []), value]); | |
| + } | |
| + return seen; | |
| + }; | |
| + | |
| + if (subcommand === "create" || subcommand === "create-goals") { | |
| + if (trailing.some((word) => ["--force", "--brief-file", "--from-stdin"].includes(shellWordLiteral(word)))) return false; | |
| + const parsed = parseOptions(new Set(["--goal"]), new Set(["--brief", "--codex-goal-mode"]), new Set(["--json"])); | |
| + const goalMode = parsed?.get("--codex-goal-mode")?.[0]; | |
| + return Boolean(parsed?.has("--brief")) | |
| + && (goalMode === undefined || ["aggregate", "per-story", "per_story"].includes(goalMode)); | |
| + } | |
| + if (["complete", "complete-goals", "next", "start-next"].includes(subcommand)) { | |
| + return parseOptions(new Set(), new Set(), new Set(["--retry-failed", "--json"])) !== null; | |
| + } | |
| + if (subcommand === "status") { | |
| + return parseOptions(new Set(), new Set(["--codex-goal-json"]), new Set(["--json"])) !== null; | |
| + } | |
| + if (subcommand === "steer") { | |
| + const parsed = parseOptions( | |
| + new Set(), | |
| + new Set(["--kind", "--target-goal-id", "--evidence", "--rationale"]), | |
| + new Set(["--json"]), | |
| + ); | |
| + return parsed?.get("--kind")?.[0] === "annotate_ledger" | |
| + && parsed.has("--evidence") | |
| + && parsed.has("--rationale") | |
| + && parsed.has("--json"); | |
| + } | |
| + if (subcommand === "checkpoint") { | |
| + const parsed = parseOptions( | |
| + new Set(), | |
| + new Set(["--goal-id", "--status", "--evidence", "--codex-goal-json", "--quality-gate-json"]), | |
| + new Set(["--json"]), | |
| + ); | |
| + if ( | |
| + !parsed?.has("--goal-id") | |
| + || !parsed.has("--status") | |
| + || !parsed.has("--evidence") | |
| + || !parsed.has("--json") | |
| + ) return false; | |
| + const status = parsed.get("--status")?.[0]; | |
| + if (status === "complete") { | |
| + return parsed.has("--codex-goal-json") && parsed.has("--quality-gate-json"); | |
| + } | |
| + if (status === "blocked") { | |
| + return parsed.has("--codex-goal-json") && !parsed.has("--quality-gate-json"); | |
| + } | |
| + return status === "failed" | |
| + && !parsed.has("--codex-goal-json") | |
| + && !parsed.has("--quality-gate-json"); | |
| + } | |
| + return false; | |
| +} | |
| + | |
| +function isUltragoalMetadataCommandAttempt(command: string): boolean { | |
| + const words = tokenizeConductorShellWords(command); | |
| + const commandIndex = skipShellCommandPositionPrefixWords(words, 0); | |
| + if (commandNameFromShellWord(words[commandIndex] ?? "") !== "omx") return false; | |
| + const args = collectConductorInvocationWords(words, commandIndex).map(shellWordLiteral); | |
| + return args[0] === "ultragoal" | |
| + && [ | |
| + "create", "create-goals", | |
| + "complete", "complete-goals", "next", "start-next", | |
| + "status", "steer", "checkpoint", | |
| + ].includes(args[1] ?? ""); | |
| +} | |
| + | |
| function isStaticallyRecognizedConductorOrchestrationMutation( | |
| commandName: string, | |
| words: string[], | |
| @@ -19822,6 +21036,85 @@ function conductorStatePayloadPreservesActiveGuard( | |
| return phase !== "" && isNonTerminalPhase(phase); | |
| } | |
| +function nativeAssignmentTargetIsWithinScope( | |
| + cwd: string, | |
| + rawTarget: string, | |
| + pathPrefixes: readonly string[], | |
| +): boolean { | |
| + const relativeTarget = normalizePlanningArtifactRelativePath(cwd, rawTarget); | |
| + if (!relativeTarget || conductorPathnameExpansionIsAmbiguous(relativeTarget)) return false; | |
| + if (conductorPathTraversesLink(cwd, relativeTarget)) return false; | |
| + if ( | |
| + relativeTarget === ".git" | |
| + || relativeTarget.startsWith(".git/") | |
| + || relativeTarget === ".omx" | |
| + || relativeTarget.startsWith(".omx/") | |
| + ) return false; | |
| + return pathPrefixes.some((prefix) => ( | |
| + prefix === "." | |
| + || relativeTarget === prefix | |
| + || relativeTarget.startsWith(`${prefix}/`) | |
| + )); | |
| +} | |
| + | |
| +function nativeAssignedBashHasUnsafeStructure(cwd: string, command: string): boolean { | |
| + const normalized = normalizeShellLineContinuations(command); | |
| + if ( | |
| + splitConductorShellSegments(stripHeredocBodiesForCommandScan(normalized)).length !== 1 | |
| + || hasUnresolvedShellArithmeticExpansion(normalized) | |
| + || hasUnquotedShellSubstitution(normalized) | |
| + || hasDynamicNestedShellExecution(normalized) | |
| + || hasUnsafeUnquotedHeredocExpansion(normalized) | |
| + || commandHasUnsafeConductorShellState(normalized, cwd) | |
| + || conductorCommandMayMutatePathResolution(normalized) | |
| + ) return true; | |
| + const interpreterWrites = extractConductorInterpreterWrites(normalized); | |
| + return interpreterWrites.some((write) => write.unresolved || write.targets.length === 0); | |
| +} | |
| + | |
| +async function nativeChildAssignmentAllowsMutation( | |
| + payload: CodexHookPayload, | |
| + cwd: string, | |
| + stateDir: string, | |
| + sessionId: string, | |
| + toolName: string, | |
| + mutationTransport: PreToolUseMutationTransport, | |
| +): Promise<boolean> { | |
| + if ( | |
| + mutationTransport === "state" | |
| + || mutationTransport === "orchestration" | |
| + || mutationTransport === "goal-lifecycle" | |
| + || mutationTransport === "unknown" | |
| + ) { | |
| + return false; | |
| + } | |
| + const assignment = await readNativeSubagentAssignment(payload, cwd, stateDir, sessionId); | |
| + if (!assignment) return false; | |
| + | |
| + if (mutationTransport === "path") { | |
| + if (!assignment.allowed_actions.includes("path-write")) return false; | |
| + const candidates = collectImplementationToolPathCandidates( | |
| + payload, | |
| + toolName, | |
| + readPreToolUsePathCandidates(payload), | |
| + ); | |
| + return candidates.length > 0 | |
| + && candidates.every((candidate) => ( | |
| + !isNativeAssignmentPath(stateDir, candidate, cwd) | |
| + && nativeAssignmentTargetIsWithinScope(cwd, candidate, assignment.path_prefixes) | |
| + )); | |
| + } | |
| + if (mutationTransport !== "bash") return false; | |
| + | |
| + const command = readPreToolUseCommand(payload); | |
| + if (nativeAssignedBashHasUnsafeStructure(cwd, command)) return false; | |
| + if (!assignment.allowed_actions.includes("bash-write")) return false; | |
| + const targets = extractDeepInterviewCommandWriteTargets(command, cwd, cwd); | |
| + if (targets.some((target) => isNativeAssignmentPath(stateDir, target, cwd))) return false; | |
| + return targets.length > 0 && targets.every((target) => ( | |
| + nativeAssignmentTargetIsWithinScope(cwd, target, assignment.path_prefixes) | |
| + )); | |
| +} | |
| function buildConductorSessionProvenanceDeny( | |
| activeState: ActiveConductorState, | |
| @@ -19846,14 +21139,38 @@ export async function buildConductorPreToolUseWriteGuardOutput( | |
| stateDir: string, | |
| resolvedSessionId?: string, | |
| policyCwd = cwd, | |
| + rootTranscript: SafeRootTranscriptMetadata | null = null, | |
| + pointer: SessionPointerReadResult | null = null, | |
| ): Promise<Record<string, unknown> | null> { | |
| const activeState = await readActiveConductorStateForPreToolUse(payload, policyCwd, stateDir, resolvedSessionId); | |
| if (!activeState) return null; | |
| const sessionId = safeString(resolvedSessionId ?? readPayloadSessionId(payload)).trim(); | |
| - const writeActor = await resolvePreToolUseWriteActor(payload, cwd, stateDir, sessionId); | |
| + const writeActor = await resolvePreToolUseWriteActor(payload, cwd, stateDir, sessionId, rootTranscript); | |
| if (writeActor === "provenance-conflict") { | |
| return buildConductorSessionProvenanceDeny(activeState, "payload identity aliases conflict"); | |
| } | |
| + if ( | |
| + writeActor === "main-root" | |
| + && await isAuthorizedActiveUltragoalPhaseTransitionFallback({ | |
| + payload, | |
| + policyCwd, | |
| + stateDir, | |
| + sessionId, | |
| + pointer, | |
| + rootTranscript, | |
| + }) | |
| + ) return null; | |
| + if ( | |
| + writeActor === "main-root" | |
| + && await isAuthorizedTerminalUltragoalStateFallback({ | |
| + payload, | |
| + policyCwd, | |
| + stateDir, | |
| + sessionId, | |
| + pointer, | |
| + rootTranscript, | |
| + }) | |
| + ) return null; | |
| const nativeSubagentSupport = resolveNativeSubagentSupportStatus({ | |
| payload, | |
| persistedSupportBlocker: await readJsonIfExists(nativeSubagentSupportBlockerPath(stateDir)), | |
| @@ -19871,9 +21188,26 @@ export async function buildConductorPreToolUseWriteGuardOutput( | |
| const command = readPreToolUseCommand(payload); | |
| const pathCandidates = readPreToolUsePathCandidates(payload); | |
| const mutationTransport = classifyPreToolUseMutationTransport(payload, toolName, cwd); | |
| + const leaderMetadataCommandAttempt = writeActor === "main-root" | |
| + && toolName === "Bash" | |
| + && isUltragoalMetadataCommandAttempt(command); | |
| - let blocked = false; | |
| - let blockedDetail = "Main-root Conductor write is not delegated"; | |
| + if ( | |
| + writeActor === "main-root" | |
| + && toolName === "Bash" | |
| + && ( | |
| + isExactNativeUltragoalBootstrapCommand(command) | |
| + || ( | |
| + leaderMetadataCommandAttempt | |
| + && isExactLeaderOwnedUltragoalMetadataCommand(command, readPreToolUseRawCommand(payload), policyCwd) | |
| + ) | |
| + ) | |
| + ) return null; | |
| + | |
| + let blocked = leaderMetadataCommandAttempt; | |
| + let blockedDetail = leaderMetadataCommandAttempt | |
| + ? "Ultragoal metadata command does not match the exact leader-owned option schema" | |
| + : "Main-root Conductor write is not delegated"; | |
| let nativeChildMutationAttempt = false; | |
| if (toolName === "Bash") { | |
| @@ -19925,7 +21259,10 @@ export async function buildConductorPreToolUseWriteGuardOutput( | |
| blocked = true; | |
| blockedDetail = describeConductorBlockedWrite(toolName, undefined, toolPathCandidates.length); | |
| } else { | |
| - const blockedPath = toolPathCandidates.find((candidate) => !isAllowedConductorMetadataExecutionPath(cwd, policyCwd, candidate)); | |
| + const blockedPath = toolPathCandidates.find((candidate) => ( | |
| + !isSessionBoundNativeAssignmentPath(policyCwd, candidate, sessionId) | |
| + && !isAllowedConductorMetadataExecutionPath(cwd, policyCwd, candidate) | |
| + )); | |
| blocked = blockedPath !== undefined; | |
| if (blockedPath !== undefined) { | |
| blockedDetail = describeConductorBlockedWrite(toolName, blockedPath, toolPathCandidates.length); | |
| @@ -19945,11 +21282,23 @@ export async function buildConductorPreToolUseWriteGuardOutput( | |
| stateDir, | |
| policyCwd, | |
| ); | |
| - if (writeActor === "team-worker" && !teamWorkerProtectedStateTarget) return null; | |
| + if (writeActor === "team-worker" && mutationTransport !== "goal-lifecycle" && !teamWorkerProtectedStateTarget) return null; | |
| if (writeActor === "team-worker" && teamWorkerProtectedStateTarget && !blocked) { | |
| blocked = true; | |
| blockedDetail = "Bash targets protected workflow state outside authorized Team-worker scope"; | |
| } | |
| + if ( | |
| + writeActor === "native-child" | |
| + && nativeChildMutationAttempt | |
| + && await nativeChildAssignmentAllowsMutation( | |
| + payload, | |
| + policyCwd, | |
| + stateDir, | |
| + sessionId, | |
| + toolName, | |
| + mutationTransport, | |
| + ) | |
| + ) return null; | |
| if (!blocked && (writeActor !== "native-child" || !nativeChildMutationAttempt)) return null; | |
| if (!blocked && nativeChildMutationAttempt && writeActor === "native-child") { | |
| blockedDetail = toolName === "Bash" | |
| @@ -21573,9 +22922,16 @@ export async function dispatchCodexNativeHook( | |
| : ""; | |
| const threadId = safeString(payload.thread_id ?? payload.threadId).trim(); | |
| const turnId = safeString(payload.turn_id ?? payload.turnId).trim(); | |
| + const safeRootTranscript = hookEventName === "UserPromptSubmit" || hookEventName === "PreToolUse" || hookEventName === "Stop" | |
| + ? await readSafeRootTranscriptMetadata(payload, cwd, options.rootTranscriptPostReadHook, { | |
| + beforeReadDirHook: options.rootTranscriptBeforeReadDirHook, | |
| + maxEntries: options.rootTranscriptScanMaxEntries, | |
| + }) | |
| + : null; | |
| + const safeRootTurnTranscript = safeRootTranscript?.hasExactTurnContext ? safeRootTranscript : null; | |
| const pointer = await readSessionPointer(pointerContext); | |
| const currentSessionState = pointer.status === "usable" ? pointer.state ?? null : null; | |
| - let promptTurnContext: ResolvedPromptTurnContext | null = hookEventName === "UserPromptSubmit" | |
| + let promptTurnContext: ResolvedPromptTurnContext | null = hookEventName === "UserPromptSubmit" && !safeRootTurnTranscript | |
| ? evaluateResolvedPromptTurn({ | |
| producer: "native", | |
| payloadSessionId: payload.session_id ?? payload.sessionId, | |
| @@ -21608,9 +22964,14 @@ export async function dispatchCodexNativeHook( | |
| let canonicalSessionId = safeString(currentSessionState?.session_id).trim(); | |
| if (promptTurnContext?.status === "authorized") { | |
| canonicalSessionId = promptTurnContext.authorization.targetSessionId; | |
| + } else if (safeRootTurnTranscript) { | |
| + canonicalSessionId = safeRootTurnTranscript.sessionId; | |
| } | |
| let allowGlobalSideEffects = promptTurnContext?.status !== "authorized" | |
| || promptTurnContext.authorization.globalSideEffects === "allow"; | |
| + if (safeRootTurnTranscript && currentSessionState && !payloadMatchesSessionPointer(safeRootTurnTranscript.sessionId, currentSessionState)) { | |
| + allowGlobalSideEffects = false; | |
| + } | |
| let resolvedNativeSessionId = nativeSessionId; | |
| let skipCanonicalSessionStartContext = false; | |
| let isSubagentSessionStart = false; | |
| @@ -21801,8 +23162,16 @@ export async function dispatchCodexNativeHook( | |
| if (hookEventName === "Stop") { | |
| const stopPayloadSessionId = readPayloadSessionId(payload); | |
| const authorizedWorkerStopSessionId = authoritativeWorkerPayloadSessionId; | |
| + const rootTranscriptAuthorizesStaleStop = !declaredTeamWorker | |
| + && pointer.status === "stale-dead" | |
| + && pointer.state !== null | |
| + && pointer.state !== undefined | |
| + && isSessionStateAuthoritativeForCwd(pointer.state, cwd) | |
| + && safeRootTranscript?.sessionId === stopPayloadSessionId; | |
| const stopCanonicalSessionId = declaredTeamWorker && !authorizedWorkerStopSessionId | |
| ? "" | |
| + : rootTranscriptAuthorizesStaleStop | |
| + ? stopPayloadSessionId | |
| : await resolveInternalSessionIdForPayload( | |
| cwd, | |
| authorizedWorkerStopSessionId || stopPayloadSessionId, | |
| @@ -21843,6 +23212,13 @@ export async function dispatchCodexNativeHook( | |
| } else if (stopCanonicalSessionId) { | |
| canonicalSessionId = stopCanonicalSessionId; | |
| } | |
| + if (rootTranscriptAuthorizesStaleStop) { | |
| + canonicalSessionId = stopPayloadSessionId; | |
| + resolvedNativeSessionId = stopPayloadSessionId; | |
| + allowImplicitSessionSideEffects = true; | |
| + allowGlobalSideEffects = false; | |
| + stopAuthorizationFailure = null; | |
| + } | |
| if (authorizedWorkerStopSessionId && stopCanonicalSessionId === authorizedWorkerStopSessionId) { | |
| allowImplicitSessionSideEffects = true; | |
| stopAuthorizationFailure = null; | |
| @@ -21919,7 +23295,7 @@ export async function dispatchCodexNativeHook( | |
| } else if (prompt && !isSubagentPromptSubmit && allowImplicitSessionSideEffects) { | |
| const rawHookSessionId = canonicalSessionId || nativeSessionId; | |
| const normalizedHookSessionId = normalizeSessionId(rawHookSessionId); | |
| - const explicitHookSessionId = currentSessionState ? undefined : normalizedHookSessionId; | |
| + const explicitHookSessionId = safeRootTurnTranscript?.sessionId ?? (currentSessionState ? undefined : normalizedHookSessionId); | |
| if (rawHookSessionId && !normalizedHookSessionId) { | |
| suppressActivationSeeding = true; | |
| } else { | |
| @@ -21948,6 +23324,7 @@ export async function dispatchCodexNativeHook( | |
| sessionIdForState || undefined, | |
| threadId || undefined, | |
| turnId || undefined, | |
| + safeRootTurnTranscript, | |
| ) ?? await recordSkillActivation({ | |
| stateDir, | |
| sourceCwd: cwd, | |
| @@ -21963,6 +23340,29 @@ export async function dispatchCodexNativeHook( | |
| }, | |
| }); | |
| } | |
| + if ( | |
| + skillState?.active === true | |
| + && skillState.initialized_mode === "ultragoal" | |
| + && sessionIdForState | |
| + && isNativeOutsideTmuxUserPrompt(cwd, payload, sessionIdForState) | |
| + ) { | |
| + const bootstrap = await readNativeUltragoalBootstrap(stateDir, sessionIdForState); | |
| + let receiptConfirmed = bootstrap?.status === "active"; | |
| + if (bootstrap && ["ready", "activating"].includes(bootstrap.status) && bootstrap.child_agent_id) { | |
| + const confirmed = await markNativeUltragoalBootstrapActive({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId: sessionIdForState, | |
| + childAgentId: bootstrap.child_agent_id, | |
| + }); | |
| + receiptConfirmed = confirmed.ok; | |
| + } | |
| + if (!receiptConfirmed && resolveNativeUltragoalConfig().backend === "local-experimental") { | |
| + const reason = "native_ultragoal_activation_receipt_invalid"; | |
| + await failNativeUltragoalActivation(cwd, stateDir, sessionIdForState, reason); | |
| + skillState = { ...skillState, active: false, transition_error: reason }; | |
| + } | |
| + } | |
| // --- Triage classifier (advisory-only, non-keyword prompts) --- | |
| if ( | |
| prompt | |
| @@ -22186,6 +23586,8 @@ export async function dispatchCodexNativeHook( | |
| stateDir, | |
| payloadSessionId, | |
| payloadPolicyCwd, | |
| + safeRootTranscript, | |
| + pointer, | |
| ) | |
| : null; | |
| const directCancelCommand = safeString(payload.tool_name).trim() === "Bash" | |
| @@ -22203,8 +23605,18 @@ export async function dispatchCodexNativeHook( | |
| activeState: sessionBinding.valid | |
| ? (await readStopSessionPinnedState("autopilot-state.json", policyCwd, sessionBinding.canonicalSessionId, stateDir) ?? {}) | |
| : {}, | |
| + rootTranscript: safeRootTranscript, | |
| }); | |
| - if (directCancel.kind !== "not-direct-cancel") outputJson = directCancel.output; | |
| + if (directCancel.kind !== "not-direct-cancel") { | |
| + if (directCancel.kind === "handled" && sessionBinding.canonicalSessionId) { | |
| + await revokeLocalNativeExecutorAssignment({ | |
| + stateDir, | |
| + sessionId: sessionBinding.canonicalSessionId, | |
| + reason: "root_cancelled", | |
| + }); | |
| + } | |
| + outputJson = directCancel.output; | |
| + } | |
| } | |
| if (!outputJson && !policyRoot.valid && policyRoot.statePresent && mutationTransport !== "read-only") { | |
| outputJson = buildConductorSessionProvenanceDeny( | |
| @@ -22303,6 +23715,8 @@ export async function dispatchCodexNativeHook( | |
| stateDir, | |
| payloadSessionId, | |
| policyCwd, | |
| + safeRootTranscript, | |
| + pointer, | |
| ) | |
| : null; | |
| outputJson = buildNativeUnknownRolePreToolUseOutput(payload, policyCwd) | |
| @@ -22326,12 +23740,21 @@ export async function dispatchCodexNativeHook( | |
| })(); | |
| outputJson = nativePreToolUseSpecificDeny | |
| ?? buildNativeUnknownRolePreToolUseOutput(payload, policyCwd) | |
| + ?? await buildPendingNativeUltragoalExecutorGuardOutput( | |
| + payload, | |
| + policyCwd, | |
| + stateDir, | |
| + preToolUseSessionId, | |
| + safeRootTranscript, | |
| + ) | |
| ?? await buildDeepInterviewPreToolUseBoundaryOutput( | |
| payload, | |
| policyCwd, | |
| stateDir, | |
| preToolUseSessionId, | |
| cwd, | |
| + cwd, | |
| + safeRootTranscript, | |
| ) | |
| ?? await buildRalplanPreToolUseBoundaryOutput( | |
| payload, | |
| @@ -22339,6 +23762,8 @@ export async function dispatchCodexNativeHook( | |
| stateDir, | |
| preToolUseSessionId, | |
| cwd, | |
| + cwd, | |
| + safeRootTranscript, | |
| ) | |
| ?? await buildPlanningRootPointerConflictPreToolUseOutput( | |
| payload, | |
| @@ -22346,12 +23771,21 @@ export async function dispatchCodexNativeHook( | |
| stateDir, | |
| rootPointerConflict, | |
| ) | |
| + ?? await buildNativeUltragoalBootstrapMutationGuardOutput( | |
| + payload, | |
| + policyCwd, | |
| + stateDir, | |
| + preToolUseSessionId, | |
| + safeRootTranscript, | |
| + ) | |
| ?? await buildUltragoalNoOwnerActivationGuardOutput( | |
| payload, | |
| cwd, | |
| stateDir, | |
| preToolUseSessionId, | |
| policyCwd, | |
| + safeRootTranscript, | |
| + pointer, | |
| ) | |
| ?? await buildConductorPreToolUseWriteGuardOutput( | |
| payload, | |
| @@ -22359,8 +23793,17 @@ export async function dispatchCodexNativeHook( | |
| stateDir, | |
| preToolUseSessionId, | |
| policyCwd, | |
| + safeRootTranscript, | |
| + pointer, | |
| + ) | |
| + ?? await buildRawProtectedWorkflowStatePathOutput( | |
| + payload, | |
| + policyCwd, | |
| + stateDir, | |
| + preToolUseSessionId, | |
| + guardedConductorState !== null, | |
| + safeRootTranscript, | |
| ) | |
| - ?? buildRawProtectedWorkflowStatePathOutput(payload, policyCwd, stateDir) | |
| ?? await buildNativeSubagentCapacityCloseGuardOutput(payload, policyCwd, stateDir) | |
| ?? buildMalformedPreToolUseBlockTestOutput(payload) | |
| ?? buildNativePreToolUseOutput(payload); | |
| @@ -22375,6 +23818,34 @@ export async function dispatchCodexNativeHook( | |
| } | |
| await handleTeamWorkerPostToolUseSuccess(payload, cwd); | |
| } | |
| + if (canonicalSessionId && isNativeOutsideTmuxUserPrompt(cwd, payload, canonicalSessionId)) { | |
| + const toolName = safeString(payload.tool_name).trim(); | |
| + const activationCandidates = collectUltragoalActivationCandidatePayloads(payload, toolName, cwd); | |
| + if (activationCandidates.some((candidate) => isUltragoalConductorActivationPayload(candidate))) { | |
| + const bootstrap = await readNativeUltragoalBootstrap(stateDir, canonicalSessionId); | |
| + const state = await readStopSessionPinnedState("ultragoal-state.json", cwd, canonicalSessionId, stateDir); | |
| + if (isActiveConductorModeState(state, "ultragoal", canonicalSessionId)) { | |
| + let receiptConfirmed = bootstrap?.status === "active"; | |
| + if (bootstrap?.status === "activating" && bootstrap.child_agent_id) { | |
| + const confirmed = await markNativeUltragoalBootstrapActive({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId: canonicalSessionId, | |
| + childAgentId: bootstrap.child_agent_id, | |
| + }); | |
| + receiptConfirmed = confirmed.ok; | |
| + } | |
| + if (!receiptConfirmed && resolveNativeUltragoalConfig().backend === "local-experimental") { | |
| + await failNativeUltragoalActivation( | |
| + cwd, | |
| + stateDir, | |
| + canonicalSessionId, | |
| + "native_ultragoal_activation_receipt_invalid", | |
| + ); | |
| + } | |
| + } | |
| + } | |
| + } | |
| outputJson = buildNativePostToolUseOutput(payload); | |
| } else if (hookEventName === "Stop") { | |
| if (declaredTeamWorkerStopOnly) { | |
| @@ -22413,6 +23884,9 @@ export async function dispatchCodexNativeHook( | |
| }; | |
| } | |
| } | |
| + if (!declaredTeamWorkerStopOnly && canonicalSessionId) { | |
| + await revokeTerminalNativeUltragoalBootstrap(policyCwd, stateDir, canonicalSessionId); | |
| + } | |
| } | |
| return { | |
| diff --git a/src/scripts/notify-hook.ts b/src/scripts/notify-hook.ts | |
| index 6c7e2cbc..42ab6e28 100644 | |
| --- a/src/scripts/notify-hook.ts | |
| +++ b/src/scripts/notify-hook.ts | |
| @@ -301,7 +301,8 @@ function looksLikeAutopilotTerminalHandoff(text: string): boolean { | |
| function isTerminalModeStateObject(value: unknown, mode: string): boolean { | |
| if (!value || typeof value !== 'object' || Array.isArray(value)) return false; | |
| const state = value as Record<string, unknown>; | |
| - if (safeString(state.mode).trim() !== mode) return false; | |
| + const persistedMode = safeString(state.mode).trim(); | |
| + if (persistedMode && persistedMode !== mode) return false; | |
| if (state.active === true) return false; | |
| const phase = safeString(state.current_phase || state.currentPhase).trim().toLowerCase().replace(/_/g, '-'); | |
| if (['complete', 'completed', 'failed', 'cancelled', 'canceled', 'stopped', 'user-stopped'].includes(phase)) return true; | |
| @@ -324,20 +325,40 @@ function terminalStateMatchesNotifyTurn(state: Record<string, unknown>, payload: | |
| return Boolean(payloadThreadId && stateThreadId && payloadThreadId === stateThreadId); | |
| } | |
| -async function hasTerminalAutopilotStateForNotifyTurn( | |
| +async function nativeStopReceiptMatchesNotifyTurn( | |
| stateDir: string, | |
| sessionId: string, | |
| payload: Record<string, unknown>, | |
| +): Promise<boolean> { | |
| + const payloadTurnId = safeString(payload['turn-id'] || payload.turn_id || '').trim(); | |
| + if (!sessionId || !payloadTurnId) return false; | |
| + const rootState = await readJsonFileIfObject(join(stateDir, 'native-stop-state.json')); | |
| + const sessions = rootState?.sessions; | |
| + if (!sessions || typeof sessions !== 'object' || Array.isArray(sessions)) return false; | |
| + const session = (sessions as Record<string, unknown>)[sessionId]; | |
| + if (!session || typeof session !== 'object' || Array.isArray(session)) return false; | |
| + const guard = (session as Record<string, unknown>).ordinary_no_progress_guard; | |
| + if (!guard || typeof guard !== 'object' || Array.isArray(guard)) return false; | |
| + return safeString((guard as Record<string, unknown>).last_turn_id).trim() === payloadTurnId; | |
| +} | |
| + | |
| +async function hasTerminalModeStateForNotifyTurn( | |
| + stateDir: string, | |
| + sessionId: string, | |
| + payload: Record<string, unknown>, | |
| + mode: string, | |
| ): Promise<boolean> { | |
| const state = await readScopedJsonIfExists( | |
| stateDir, | |
| - 'autopilot-state.json', | |
| + `${mode}-state.json`, | |
| sessionId || undefined, | |
| null, | |
| { includeRootFallback: true }, | |
| ); | |
| - return isTerminalModeStateObject(state, 'autopilot') | |
| - && terminalStateMatchesNotifyTurn(state as Record<string, unknown>, payload); | |
| + if (!isTerminalModeStateObject(state, mode)) return false; | |
| + if (terminalStateMatchesNotifyTurn(state as Record<string, unknown>, payload)) return true; | |
| + return mode === 'ultragoal' | |
| + && await nativeStopReceiptMatchesNotifyTurn(stateDir, sessionId, payload); | |
| } | |
| async function shouldSuppressAutopilotTerminalReplayActivation( | |
| @@ -352,7 +373,24 @@ async function shouldSuppressAutopilotTerminalReplayActivation( | |
| const lastAssistantMessage = safeString(payload['last-assistant-message'] || payload.last_assistant_message || ''); | |
| if (!looksLikeAutopilotTerminalHandoff(lastAssistantMessage) && !isNotifyFallbackTaskCompletePayload(payload)) return false; | |
| - return hasTerminalAutopilotStateForNotifyTurn(stateDir, sessionId, payload); | |
| + return hasTerminalModeStateForNotifyTurn(stateDir, sessionId, payload, 'autopilot'); | |
| +} | |
| + | |
| +async function shouldSuppressUltragoalTerminalReplayActivation( | |
| + stateDir: string, | |
| + payload: Record<string, unknown>, | |
| + isUltragoalActivation: boolean, | |
| + sessionId: string, | |
| +): Promise<boolean> { | |
| + if (!isTurnCompletePayload(payload) && !isNotifyFallbackTaskCompletePayload(payload)) return false; | |
| + if (!isUltragoalActivation) return false; | |
| + | |
| + // Native Codex notify replays the completed turn's original input after the | |
| + // final assistant message. A same-turn terminal state is stronger evidence | |
| + // than prose in that message and must win over the replayed `$ultragoal` | |
| + // keyword. A later turn has a different turn id and remains eligible to | |
| + // start a new run through the ordinary activation path. | |
| + return hasTerminalModeStateForNotifyTurn(stateDir, sessionId, payload, 'ultragoal'); | |
| } | |
| export interface NotifySkillActivationInput { | |
| @@ -425,6 +463,13 @@ export async function recordNotifySkillActivation( | |
| input.sessionId || '', | |
| ); | |
| if (terminalAutopilotReplay && runtimeMatches[0]?.skill === 'autopilot') return null; | |
| + const terminalUltragoalReplay = await shouldSuppressUltragoalTerminalReplayActivation( | |
| + input.stateDir, | |
| + input.payload, | |
| + runtimeMatches.some((match) => match.skill === 'ultragoal'), | |
| + input.sessionId || '', | |
| + ); | |
| + if (terminalUltragoalReplay && runtimeMatches[0]?.skill === 'ultragoal') return null; | |
| return (dependencies.recordSkillActivation ?? recordSkillActivation)({ | |
| stateDir: input.stateDir, | |
| diff --git a/src/state/operations.ts b/src/state/operations.ts | |
| index 3b3c99a5..c60433ff 100644 | |
| --- a/src/state/operations.ts | |
| +++ b/src/state/operations.ts | |
| @@ -134,6 +134,10 @@ export interface StateOperationResponse { | |
| isError?: boolean; | |
| } | |
| +export interface StateOperationExecutionOptions { | |
| + beforeStateWriteCommit?: () => Promise<void>; | |
| +} | |
| + | |
| const stateWriteQueues = new Map<string, Promise<void>>(); | |
| async function withStateWriteLock<T>(path: string, fn: () => Promise<T>): Promise<T> { | |
| @@ -769,6 +773,7 @@ async function readSessionDetailTransitionModes( | |
| export async function executeStateOperation( | |
| name: StateOperationName, | |
| rawArgs: Record<string, unknown> = {}, | |
| + executionOptions: StateOperationExecutionOptions = {}, | |
| ): Promise<StateOperationResponse> { | |
| let cwd: string; | |
| let explicitSessionId: string | undefined; | |
| @@ -802,13 +807,17 @@ export async function executeStateOperation( | |
| const mode = validateStateModeSegment(rawArgs.mode); | |
| const { baseStateDir, rootSource } = getBaseStateDirWithSource(cwd); | |
| await initializeStateEnvironment(cwd, effectiveSessionId, rootSource); | |
| - const beforeCommit = createWritableCommitRevalidator({ | |
| + const revalidateWritableScope = createWritableCommitRevalidator({ | |
| operation: 'state_write', | |
| cwd, | |
| explicitSessionId, | |
| capturedScope: stateScope, | |
| baseStateDir, | |
| }); | |
| + const beforeCommit: BeforeWritableCommit = async (event) => { | |
| + await revalidateWritableScope(event); | |
| + await executionOptions.beforeStateWriteCommit?.(); | |
| + }; | |
| // Write to the exact resolved scope directory; never recompute the | |
| // target root/path after authorization. | |
| const path = join(stateScope.stateDir, getStateFilename(mode)); | |
| diff --git a/src/subagents/__tests__/tracker.test.ts b/src/subagents/__tests__/tracker.test.ts | |
| index f5ace531..977adffa 100644 | |
| --- a/src/subagents/__tests__/tracker.test.ts | |
| +++ b/src/subagents/__tests__/tracker.test.ts | |
| @@ -5,7 +5,7 @@ import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises'; | |
| import { hostname, tmpdir } from 'node:os'; | |
| import { join } from 'node:path'; | |
| import { describe, it } from 'node:test'; | |
| -import { __setCrossProcessPublishBarrierForTest, __setCrossProcessQuarantineBarrierForTest, consumeDirectChildReopenContext, CrossProcessLockLostError, buildSubagentResumeLedger, CROSS_PROCESS_LOCK_ARTIFACT_SWEEP_CAP, CROSS_PROCESS_LOCK_LEASE_MS, createSubagentTrackingState, crossProcessLockPath, DESCRIPTIVE_ADAPTED_PROVENANCE, fenceNativeSubagentAuthorities, forceGlobalAuthorityDenial, recordNativeSubagentAuthorityObservation, recordSubagentTurn, recordSubagentTurnForSession, NATIVE_SUBAGENT_PROVENANCE, readProcessStartIdentity, readSubagentTrackingState, readSubagentTrackingStateStrict, repairPersistedRootIdentity, revokeNativeSubagentAuthorities, selectReusableSubagentEntry, subagentTrackingPath, summarizeSubagentSession, withCrossProcessFileLockSync, writeSubagentTrackingState } from '../tracker.js'; | |
| +import { acquireCrossProcessFileLock, __setCrossProcessPublishBarrierForTest, __setCrossProcessQuarantineBarrierForTest, consumeDirectChildReopenContext, CrossProcessLockLostError, buildSubagentResumeLedger, CROSS_PROCESS_LOCK_ARTIFACT_SWEEP_CAP, CROSS_PROCESS_LOCK_LEASE_MS, createSubagentTrackingState, crossProcessLockPath, DESCRIPTIVE_ADAPTED_PROVENANCE, ensureExactDescriptiveLeaderForSession, fenceNativeSubagentAuthorities, forceGlobalAuthorityDenial, recordNativeSubagentAuthorityObservation, recordSubagentTurn, recordSubagentTurnForSession, NATIVE_SUBAGENT_PROVENANCE, readProcessStartIdentity, readSubagentTrackingState, readSubagentTrackingStateStrict, repairPersistedRootIdentity, revokeNativeSubagentAuthorities, selectReusableSubagentEntry, subagentTrackingPath, summarizeSubagentSession, withCrossProcessFileLockSync, writeSubagentTrackingState } from '../tracker.js'; | |
| import { NATIVE_SUBAGENT_ROLE_ROUTING_MARKER_FILE, readRoleRoutingMarker, writeRoleRoutingMarker } from '../role-routing-marker.js'; | |
| const CROSS_PROCESS_LOCK_HOLDER_SOURCE = ` | |
| @@ -919,6 +919,184 @@ describe('subagents/tracker', () => { | |
| } | |
| }); | |
| + it('keeps an exact descriptive leader byte-for-byte without clearing completion or status', () => { | |
| + const cwd = mkdtempSync(join(tmpdir(), 'omx-native-root-preserve-')); | |
| + try { | |
| + const path = subagentTrackingPath(cwd); | |
| + mkdirSync(join(cwd, '.omx', 'state'), { recursive: true }); | |
| + const timestamp = '2026-07-29T20:00:00.000Z'; | |
| + const raw = `${JSON.stringify({ | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + canonical: { | |
| + session_id: 'canonical', | |
| + leader_thread_id: 'root', | |
| + updated_at: timestamp, | |
| + threads: { | |
| + root: { | |
| + thread_id: 'root', | |
| + kind: 'leader', | |
| + status: 'closed', | |
| + completed_at: timestamp, | |
| + completion_source: 'task_complete', | |
| + first_seen_at: timestamp, | |
| + last_seen_at: timestamp, | |
| + turn_count: 7, | |
| + }, | |
| + child: { | |
| + thread_id: 'child', | |
| + kind: 'subagent', | |
| + provenance_kind: NATIVE_SUBAGENT_PROVENANCE, | |
| + direct_child_root_id: 'root', | |
| + direct_child_parent_id: 'root', | |
| + status: 'available', | |
| + first_seen_at: timestamp, | |
| + last_seen_at: timestamp, | |
| + turn_count: 1, | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + }, null, 2)}\n`; | |
| + writeFileSync(path, raw); | |
| + __setCrossProcessPublishBarrierForTest(() => { | |
| + throw new Error('exact root must not publish'); | |
| + }); | |
| + const result = ensureExactDescriptiveLeaderForSession(cwd, { | |
| + sessionId: 'canonical', | |
| + rootThreadId: 'root', | |
| + timestamp: '2026-07-29T21:00:00.000Z', | |
| + }); | |
| + assert.equal(result.sessions.canonical?.threads.root?.status, 'closed'); | |
| + assert.equal(result.sessions.canonical?.threads.root?.completed_at, timestamp); | |
| + assert.equal(readFileSync(path, 'utf8'), raw); | |
| + | |
| + const conflicting = JSON.parse(raw); | |
| + conflicting.sessions.canonical.threads.child.direct_child_parent_id = 'foreign-parent'; | |
| + const conflictingRaw = `${JSON.stringify(conflicting, null, 2)}\n`; | |
| + writeFileSync(path, conflictingRaw); | |
| + assert.throws(() => ensureExactDescriptiveLeaderForSession(cwd, { | |
| + sessionId: 'canonical', | |
| + rootThreadId: 'root', | |
| + }), /native_ultragoal_root_tracker_conflict/); | |
| + assert.equal(readFileSync(path, 'utf8'), conflictingRaw); | |
| + } finally { | |
| + __setCrossProcessPublishBarrierForTest(null); | |
| + rmSync(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('rejects a root id that is a trusted subagent in a foreign session without publishing', () => { | |
| + const cwd = mkdtempSync(join(tmpdir(), 'omx-native-root-global-collision-')); | |
| + try { | |
| + const path = subagentTrackingPath(cwd); | |
| + mkdirSync(join(cwd, '.omx', 'state'), { recursive: true }); | |
| + const timestamp = '2026-07-29T20:00:00.000Z'; | |
| + const raw = `${JSON.stringify({ | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + foreign: { | |
| + session_id: 'foreign', | |
| + leader_thread_id: 'foreign-root', | |
| + updated_at: timestamp, | |
| + threads: { | |
| + 'foreign-root': { | |
| + thread_id: 'foreign-root', | |
| + kind: 'leader', | |
| + first_seen_at: timestamp, | |
| + last_seen_at: timestamp, | |
| + turn_count: 1, | |
| + }, | |
| + root: { | |
| + thread_id: 'root', | |
| + kind: 'subagent', | |
| + provenance_kind: NATIVE_SUBAGENT_PROVENANCE, | |
| + direct_child_root_id: 'foreign-root', | |
| + direct_child_parent_id: 'foreign-root', | |
| + status: 'available', | |
| + first_seen_at: timestamp, | |
| + last_seen_at: timestamp, | |
| + turn_count: 1, | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + }, null, 2)}\n`; | |
| + writeFileSync(path, raw); | |
| + assert.throws(() => ensureExactDescriptiveLeaderForSession(cwd, { | |
| + sessionId: 'canonical', | |
| + rootThreadId: 'root', | |
| + }), /native_ultragoal_root_tracker_conflict/); | |
| + assert.equal(readFileSync(path, 'utf8'), raw); | |
| + assert.equal(JSON.parse(readFileSync(path, 'utf8')).sessions.canonical, undefined); | |
| + } finally { | |
| + rmSync(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('serializes descriptive root publication before a competing authority conflict', async () => { | |
| + const cwd = mkdtempSync(join(tmpdir(), 'omx-native-root-authority-race-')); | |
| + const readyPath = join(cwd, 'authority-contended'); | |
| + const resultPath = join(cwd, 'authority-result.json'); | |
| + let competitor: ReturnType<typeof spawn> | undefined; | |
| + try { | |
| + const timestamp = '2026-07-29T20:00:00.000Z'; | |
| + mkdirSync(join(cwd, '.omx', 'state'), { recursive: true }); | |
| + writeFileSync(subagentTrackingPath(cwd), JSON.stringify({ | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + canonical: { | |
| + session_id: 'canonical', | |
| + updated_at: timestamp, | |
| + threads: { | |
| + child: { | |
| + thread_id: 'child', | |
| + kind: 'subagent', | |
| + provenance_kind: NATIVE_SUBAGENT_PROVENANCE, | |
| + direct_child_root_id: 'root', | |
| + direct_child_parent_id: 'root', | |
| + status: 'available', | |
| + first_seen_at: timestamp, | |
| + last_seen_at: timestamp, | |
| + turn_count: 1, | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + })); | |
| + __setCrossProcessPublishBarrierForTest(() => { | |
| + competitor = spawnAuthorityCompetitor(cwd, 'revoke', readyPath, resultPath); | |
| + waitForFileSync(readyPath, 10_000); | |
| + }); | |
| + ensureExactDescriptiveLeaderForSession(cwd, { | |
| + sessionId: 'canonical', | |
| + rootThreadId: 'root', | |
| + }); | |
| + __setCrossProcessPublishBarrierForTest(null); | |
| + assert.ok(competitor); | |
| + await waitForChildExit(competitor); | |
| + const result = JSON.parse(readFileSync(resultPath, 'utf8')); | |
| + assert.equal(result.observedContention, true); | |
| + assert.equal(result.result, 'revoked'); | |
| + const state = JSON.parse(readFileSync(subagentTrackingPath(cwd), 'utf8')); | |
| + assert.equal(state.sessions.canonical.leader_thread_id, 'root'); | |
| + assert.equal(state.sessions.canonical.threads.root.kind, 'leader'); | |
| + assert.equal(state.sessions.canonical.threads.child.reopen_authority_revoked, true); | |
| + assert.throws(() => ensureExactDescriptiveLeaderForSession(cwd, { | |
| + sessionId: 'canonical', | |
| + rootThreadId: 'other-root', | |
| + }), /native_ultragoal_root_tracker_conflict/); | |
| + assert.equal( | |
| + JSON.parse(readFileSync(subagentTrackingPath(cwd), 'utf8')).sessions.canonical.threads.child.reopen_authority_revoked, | |
| + true, | |
| + ); | |
| + } finally { | |
| + __setCrossProcessPublishBarrierForTest(null); | |
| + await stopChild(competitor); | |
| + rmSync(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| it('serializes a queued reopen behind revocation publication', async () => { | |
| const cwd = mkdtempSync(join(tmpdir(), 'omx-3284-tracker-contended-revoke-first-')); | |
| const readyPath = join(cwd, 'reopen-contended'); | |
| @@ -1880,6 +2058,31 @@ describe('subagents/tracker', () => { | |
| } | |
| }); | |
| + it('can preserve a live local async owner beyond the legacy lease', async () => { | |
| + const cwd = await mkdtemp(join(tmpdir(), 'omx-subagent-tracker-')); | |
| + const resourcePath = join(cwd, 'lock-resource'); | |
| + const lockPath = crossProcessLockPath(resourcePath); | |
| + try { | |
| + const liveClaim = crossProcessLockClaim( | |
| + 'live-async-owner', | |
| + process.pid, | |
| + Date.now() - CROSS_PROCESS_LOCK_LEASE_MS - 1, | |
| + ); | |
| + await writeFile(lockPath, liveClaim); | |
| + await assert.rejects( | |
| + acquireCrossProcessFileLock(resourcePath, { | |
| + maxAttempts: 1, | |
| + retryMs: 1, | |
| + preserveLiveLocalOwner: true, | |
| + }), | |
| + /Timed out waiting for cross-process lock/, | |
| + ); | |
| + assert.equal(readFileSync(lockPath, 'utf-8'), liveClaim); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| it('does not steal a live same-host identity-matched claim after its lease or mistake its token for ours', async () => { | |
| const cwd = await mkdtemp(join(tmpdir(), 'omx-subagent-tracker-')); | |
| const resourcePath = join(cwd, 'lock-resource'); | |
| diff --git a/src/subagents/tracker.ts b/src/subagents/tracker.ts | |
| index 7823b252..dfb5d234 100644 | |
| --- a/src/subagents/tracker.ts | |
| +++ b/src/subagents/tracker.ts | |
| @@ -567,12 +567,16 @@ function isCrossProcessLockOlderThanLease(acquiredAtMs: number, nowMs: number): | |
| return acquiredAtMs < nowMs - CROSS_PROCESS_LOCK_LEASE_MS; | |
| } | |
| -function isCrossProcessLockReclaimable(claim: CrossProcessLockClaim): boolean { | |
| +function isCrossProcessLockReclaimable( | |
| + claim: CrossProcessLockClaim, | |
| + options: { preserveLiveLocalOwner?: boolean } = {}, | |
| +): boolean { | |
| if (claim.host !== hostname()) return isCrossProcessLockOlderThanLease(claim.acquiredAtMs, Date.now()); | |
| if (isCrossProcessLockOwnerDead(claim.pid)) return true; | |
| const currentPidStartId = readProcessStartIdentity(claim.pid); | |
| if (claim.pidStartId && currentPidStartId) return currentPidStartId !== claim.pidStartId; | |
| + if (options.preserveLiveLocalOwner) return false; | |
| return isCrossProcessLockOlderThanLease(claim.acquiredAtMs, Date.now()); | |
| } | |
| @@ -624,9 +628,13 @@ function restoreQuarantinedCrossProcessLock(lockPath: string, quarantinedPath: s | |
| removeCrossProcessLockFile(quarantinedPath); | |
| } | |
| -function recoverCrossProcessFileLock(lockPath: string, observed: CrossProcessLockState): boolean { | |
| +function recoverCrossProcessFileLock( | |
| + lockPath: string, | |
| + observed: CrossProcessLockState, | |
| + options: { preserveLiveLocalOwner?: boolean } = {}, | |
| +): boolean { | |
| if (observed.kind === 'missing') return true; | |
| - if (observed.kind === 'claim' && !isCrossProcessLockReclaimable(observed.claim)) return false; | |
| + if (observed.kind === 'claim' && !isCrossProcessLockReclaimable(observed.claim, options)) return false; | |
| const quarantinedPath = `${lockPath}.${Date.now()}.${randomUUID()}.quarantine`; | |
| try { | |
| @@ -644,7 +652,11 @@ function recoverCrossProcessFileLock(lockPath: string, observed: CrossProcessLoc | |
| && captured.kind === 'claim' | |
| && captured.claim.token === observed.claim.token; | |
| const capturedRecoverable = captured.kind === 'malformed' | |
| - || (capturedExpectedClaim && captured.kind === 'claim' && isCrossProcessLockReclaimable(captured.claim)); | |
| + || ( | |
| + capturedExpectedClaim | |
| + && captured.kind === 'claim' | |
| + && isCrossProcessLockReclaimable(captured.claim, options) | |
| + ); | |
| if (!capturedRecoverable) { | |
| restoreQuarantinedCrossProcessLock(lockPath, quarantinedPath); | |
| return true; | |
| @@ -742,6 +754,47 @@ export function __setCrossProcessQuarantineBarrierForTest( | |
| crossProcessQuarantineBarrier = barrier; | |
| } | |
| +export type CrossProcessFileLockHandle = { | |
| + assertOwnership(): void; | |
| + release(): void; | |
| +}; | |
| + | |
| +/** Acquire the shared recoverable lock for an async caller that owns its writes. */ | |
| +export async function acquireCrossProcessFileLock( | |
| + resourcePath: string, | |
| + options: { maxAttempts?: number; retryMs?: number; preserveLiveLocalOwner?: boolean } = {}, | |
| +): Promise<CrossProcessFileLockHandle> { | |
| + const lockPath = crossProcessLockPath(resourcePath); | |
| + const maxAttempts = typeof options.maxAttempts === 'number' && Number.isFinite(options.maxAttempts) | |
| + ? Math.max(1, Math.floor(options.maxAttempts)) | |
| + : DEFAULT_CROSS_PROCESS_LOCK_MAX_ATTEMPTS; | |
| + const retryMs = typeof options.retryMs === 'number' && Number.isFinite(options.retryMs) | |
| + ? Math.max(1, Math.floor(options.retryMs)) | |
| + : DEFAULT_CROSS_PROCESS_LOCK_RETRY_MS; | |
| + mkdirSync(dirname(lockPath), { recursive: true }); | |
| + const token = randomUUID(); | |
| + for (let attempt = 0; attempt < maxAttempts; attempt += 1) { | |
| + if ( | |
| + tryAcquireCrossProcessFileLock(lockPath, token) | |
| + || ( | |
| + recoverCrossProcessFileLock(lockPath, readCrossProcessLockState(lockPath), options) | |
| + && tryAcquireCrossProcessFileLock(lockPath, token) | |
| + ) | |
| + ) { | |
| + return { | |
| + assertOwnership: () => assertCrossProcessFileLockOwnership(lockPath, token), | |
| + release: () => releaseCrossProcessFileLock(lockPath, token), | |
| + }; | |
| + } | |
| + if (attempt < maxAttempts - 1) { | |
| + await new Promise((resolveDelay) => { | |
| + setTimeout(resolveDelay, Math.min(25, retryMs * 2 ** Math.min(attempt, 4))); | |
| + }); | |
| + } | |
| + } | |
| + throw new Error(`Timed out waiting for cross-process lock at ${lockPath}`); | |
| +} | |
| + | |
| export function withCrossProcessFileLockSync<T>( | |
| resourcePath: string, | |
| operation: (context: CrossProcessFileLockContext) => T, | |
| @@ -1077,6 +1130,81 @@ export async function recordSubagentTurnForSession(cwd: string, input: RecordSub | |
| return next; | |
| }); | |
| } | |
| + | |
| +export function ensureExactDescriptiveLeaderForSession(cwd: string, input: { | |
| + sessionId: string; | |
| + rootThreadId: string; | |
| + timestamp?: string; | |
| +}): SubagentTrackingState { | |
| + const sessionId = input.sessionId.trim(); | |
| + const rootThreadId = input.rootThreadId.trim(); | |
| + if (!sessionId || !rootThreadId) throw new Error('native_ultragoal_root_tracker_conflict'); | |
| + | |
| + return withCrossProcessFileLockSync(subagentTrackingPath(cwd), (context) => { | |
| + const strict = readSubagentTrackingStateStrictSync(cwd); | |
| + if (!strict.ok) throw new Error('Malformed subagent tracker authority state'); | |
| + const current = strict.state; | |
| + const session = current.sessions[sessionId]; | |
| + const root = session?.threads[rootThreadId]; | |
| + const conflictingAuthority = Object.values(session?.threads ?? {}).some((thread) => { | |
| + if (thread.thread_id === rootThreadId) { | |
| + return thread.provenance_kind === NATIVE_SUBAGENT_PROVENANCE | |
| + || thread.direct_child_parent_id !== undefined | |
| + || thread.direct_child_root_id !== undefined | |
| + || thread.reopen_authority_revoked !== undefined | |
| + || thread.reopen_authority_conflict_reason !== undefined | |
| + || thread.reopen_authority_conflict_at !== undefined; | |
| + } | |
| + if (thread.provenance_kind === NATIVE_SUBAGENT_PROVENANCE) { | |
| + return thread.direct_child_root_id !== rootThreadId | |
| + || thread.direct_child_parent_id !== rootThreadId; | |
| + } | |
| + return thread.direct_child_parent_id !== undefined | |
| + || thread.direct_child_root_id !== undefined | |
| + || thread.reopen_authority_revoked !== undefined | |
| + || thread.reopen_authority_conflict_reason !== undefined | |
| + || thread.reopen_authority_conflict_at !== undefined; | |
| + }); | |
| + if ( | |
| + hasLeaderSubagentCollision(current, rootThreadId) | |
| + || (session?.leader_thread_id && session.leader_thread_id !== rootThreadId) | |
| + || Object.values(session?.threads ?? {}).some((thread) => ( | |
| + thread.kind === 'leader' && thread.thread_id !== rootThreadId | |
| + )) | |
| + || root?.kind === 'subagent' | |
| + || conflictingAuthority | |
| + ) throw new Error('native_ultragoal_root_tracker_conflict'); | |
| + | |
| + if (session?.leader_thread_id === rootThreadId && root?.kind === 'leader') return current; | |
| + | |
| + const timestamp = input.timestamp ?? new Date().toISOString(); | |
| + const nextRoot: TrackedSubagentThread = root ?? { | |
| + thread_id: rootThreadId, | |
| + kind: 'leader', | |
| + first_seen_at: timestamp, | |
| + last_seen_at: timestamp, | |
| + turn_count: 1, | |
| + }; | |
| + const next: SubagentTrackingState = { | |
| + ...current, | |
| + sessions: { | |
| + ...current.sessions, | |
| + [sessionId]: { | |
| + session_id: sessionId, | |
| + leader_thread_id: rootThreadId, | |
| + updated_at: session?.updated_at ?? timestamp, | |
| + threads: { | |
| + ...session?.threads, | |
| + [rootThreadId]: nextRoot, | |
| + }, | |
| + }, | |
| + }, | |
| + }; | |
| + context.assertOwnership(); | |
| + writeSubagentTrackingStateSync(cwd, next, context.publish); | |
| + return next; | |
| + }); | |
| +} | |
| export function recordNativeSubagentAuthorityObservation( | |
| cwd: string, | |
| observation: NativeSubagentAuthorityObservation, | |
| @@ -1182,6 +1310,12 @@ function readAuthorityFence(cwd: string): { ok: boolean; ids: Set<string> } { | |
| } | |
| } | |
| +/** True when durable authority state requires fail-closed denial for this child. */ | |
| +export function isNativeSubagentAuthorityFenced(cwd: string, childThreadId: string): boolean { | |
| + const fence = readAuthorityFence(cwd); | |
| + return !fence.ok || fence.ids.has(childThreadId.trim()); | |
| +} | |
| + | |
| /** Upper bound on fenced ids, so repeated failures cannot grow the fence without limit. */ | |
| export const AUTHORITY_FENCE_MAX_IDS = 512; | |
| diff --git a/src/ultragoal/__tests__/artifacts.test.ts b/src/ultragoal/__tests__/artifacts.test.ts | |
| index ecc90f7c..dedb81c9 100644 | |
| --- a/src/ultragoal/__tests__/artifacts.test.ts | |
| +++ b/src/ultragoal/__tests__/artifacts.test.ts | |
| @@ -29,6 +29,7 @@ import { | |
| type UltragoalSteeringProposal, | |
| } from '../artifacts.js'; | |
| import { LEADER_CONDUCTOR_BLOCK, buildUnsupportedNativeSubagentGuidance } from '../../leader/contract.js'; | |
| +import { __setWritableStateScopeTestHooksForTests } from '../../mcp/state-paths.js'; | |
| import { steeringFixtures, type SteeringFixtureProposal } from './steering-fixtures.js'; | |
| const tmpdir = (): string => realpathSync(osTmpdir()); | |
| @@ -3213,6 +3214,8 @@ describe('ultragoal artifacts', () => { | |
| it('allows durable Ultragoal mutations for an exact-match identity-indeterminate pointer and rejects mismatches', async () => { | |
| await withTempRepo(async (cwd) => { | |
| const previousEnv = process.env.OMX_SESSION_ID; | |
| + const previousCodexThreadId = process.env.CODEX_THREAD_ID; | |
| + const previousBackend = process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| const sessionId = 'sess-indeterminate'; | |
| const stateDir = join(cwd, '.omx', 'state'); | |
| try { | |
| @@ -3225,6 +3228,8 @@ describe('ultragoal artifacts', () => { | |
| })); | |
| __setSessionPointerTransactionDependenciesForTests({ probePid: () => 'indeterminate' }); | |
| process.env.OMX_SESSION_ID = sessionId; | |
| + delete process.env.CODEX_THREAD_ID; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = 'off'; | |
| const plan = await createUltragoalPlan(cwd, { brief: '- Ship the indeterminate recovery' }); | |
| assert.equal(plan.goals.length, 1); | |
| @@ -3254,6 +3259,249 @@ describe('ultragoal artifacts', () => { | |
| } finally { | |
| if (typeof previousEnv === 'string') process.env.OMX_SESSION_ID = previousEnv; | |
| else delete process.env.OMX_SESSION_ID; | |
| + if (typeof previousCodexThreadId === 'string') process.env.CODEX_THREAD_ID = previousCodexThreadId; | |
| + else delete process.env.CODEX_THREAD_ID; | |
| + if (typeof previousBackend === 'string') process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = previousBackend; | |
| + else delete process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + __resetSessionPointerTransactionDependenciesForTests(); | |
| + } | |
| + }); | |
| + }); | |
| + | |
| + it('opts App-native recovery into local-experimental Ultragoal artifacts only', async () => { | |
| + await withTempRepo(async (cwd) => { | |
| + const previousBackend = process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + const previousCodexThreadId = process.env.CODEX_THREAD_ID; | |
| + const previousOmxSessionId = process.env.OMX_SESSION_ID; | |
| + const sessionId = '019faf7e-0010-7000-8000-000000000010'; | |
| + const stateDir = join(cwd, '.omx', 'state'); | |
| + try { | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(join(stateDir, 'session.json'), JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd, | |
| + state_root: stateDir, | |
| + pid: 8388607, | |
| + })); | |
| + __setSessionPointerTransactionDependenciesForTests({ probePid: () => 'indeterminate' }); | |
| + delete process.env.OMX_SESSION_ID; | |
| + process.env.CODEX_THREAD_ID = sessionId; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = 'local-experimental'; | |
| + | |
| + const plan = await createUltragoalPlan(cwd, { brief: '- Ship native App recovery' }); | |
| + assert.equal(plan.goals.length, 1); | |
| + assert.equal(existsSync(join(cwd, '.omx', 'ultragoal', 'goals.json')), true); | |
| + assert.deepEqual(await assertUltragoalWritableLifecycleAuthority(cwd), { | |
| + kind: 'resolved', | |
| + source: 'session', | |
| + sessionId, | |
| + stateDir: join(stateDir, 'sessions', sessionId), | |
| + }); | |
| + | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = 'host'; | |
| + await assert.rejects( | |
| + () => addUltragoalGoal(cwd, { title: 'Must not persist', objective: 'Host backend cannot use local recovery.' }), | |
| + /writable lifecycle authority/, | |
| + ); | |
| + } finally { | |
| + if (typeof previousBackend === 'string') process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = previousBackend; | |
| + else delete process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + if (typeof previousCodexThreadId === 'string') process.env.CODEX_THREAD_ID = previousCodexThreadId; | |
| + else delete process.env.CODEX_THREAD_ID; | |
| + if (typeof previousOmxSessionId === 'string') process.env.OMX_SESSION_ID = previousOmxSessionId; | |
| + else delete process.env.OMX_SESSION_ID; | |
| + __resetSessionPointerTransactionDependenciesForTests(); | |
| + } | |
| + }); | |
| + }); | |
| + | |
| + it('revalidates the App-native pointer before the first durable artifact commit', async () => { | |
| + await withTempRepo(async (cwd) => { | |
| + const previousBackend = process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + const previousCodexThreadId = process.env.CODEX_THREAD_ID; | |
| + const sessionId = '019faf7e-0011-7000-8000-000000000011'; | |
| + const stateDir = join(cwd, '.omx', 'state'); | |
| + const sessionPath = join(stateDir, 'session.json'); | |
| + let recoveryReadCount = 0; | |
| + try { | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(sessionPath, JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd, | |
| + state_root: stateDir, | |
| + pid: 8388607, | |
| + })); | |
| + __setSessionPointerTransactionDependenciesForTests({ probePid: () => 'indeterminate' }); | |
| + process.env.CODEX_THREAD_ID = sessionId; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = 'local-experimental'; | |
| + __setWritableStateScopeTestHooksForTests({ | |
| + beforeRecoveryReread: async () => { | |
| + recoveryReadCount += 1; | |
| + if (recoveryReadCount === 3) { | |
| + await writeFile(sessionPath, JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd, | |
| + state_root: stateDir, | |
| + pid: 9999999, | |
| + })); | |
| + } | |
| + }, | |
| + }); | |
| + | |
| + await assert.rejects( | |
| + () => createUltragoalPlan(cwd, { brief: '- Must not commit after pointer drift' }), | |
| + /writable lifecycle authority/, | |
| + ); | |
| + assert.equal(existsSync(join(cwd, '.omx', 'ultragoal', 'brief.md')), false); | |
| + assert.equal(existsSync(join(cwd, '.omx', 'ultragoal', 'goals.json')), false); | |
| + } finally { | |
| + __setWritableStateScopeTestHooksForTests({}); | |
| + if (typeof previousBackend === 'string') process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = previousBackend; | |
| + else delete process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + if (typeof previousCodexThreadId === 'string') process.env.CODEX_THREAD_ID = previousCodexThreadId; | |
| + else delete process.env.CODEX_THREAD_ID; | |
| + __resetSessionPointerTransactionDependenciesForTests(); | |
| + } | |
| + }); | |
| + }); | |
| + | |
| + it('fails annotate_ledger closed when App-native authority changes immediately before plan commit', async () => { | |
| + await withTempRepo(async (cwd) => { | |
| + const previousBackend = process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + const previousCodexThreadId = process.env.CODEX_THREAD_ID; | |
| + const previousOmxSessionId = process.env.OMX_SESSION_ID; | |
| + const sessionId = '019faf7e-0012-7000-8000-000000000012'; | |
| + const stateDir = join(cwd, '.omx', 'state'); | |
| + const sessionPath = join(stateDir, 'session.json'); | |
| + const goalsPath = join(cwd, '.omx', 'ultragoal', 'goals.json'); | |
| + const ledgerPath = join(cwd, '.omx', 'ultragoal', 'ledger.jsonl'); | |
| + let recoveryReadCount = 0; | |
| + try { | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(sessionPath, JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd, | |
| + state_root: stateDir, | |
| + pid: 8388607, | |
| + })); | |
| + __setSessionPointerTransactionDependenciesForTests({ probePid: () => 'indeterminate' }); | |
| + delete process.env.OMX_SESSION_ID; | |
| + process.env.CODEX_THREAD_ID = sessionId; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = 'local-experimental'; | |
| + | |
| + await createUltragoalPlan(cwd, { brief: '- Preserve commit-time authority' }); | |
| + const beforeGoals = await readFile(goalsPath, 'utf-8'); | |
| + const beforeLedger = await readFile(ledgerPath, 'utf-8'); | |
| + __setWritableStateScopeTestHooksForTests({ | |
| + beforeRecoveryReread: async () => { | |
| + recoveryReadCount += 1; | |
| + // annotate_ledger has no plan mutation: before-lock, after-lock, | |
| + // pre-mkdir, then the ledger's final pre-append authority check. | |
| + if (recoveryReadCount === 4) { | |
| + await writeFile(sessionPath, JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd, | |
| + state_root: stateDir, | |
| + pid: 9999999, | |
| + })); | |
| + } | |
| + }, | |
| + }); | |
| + | |
| + await assert.rejects( | |
| + () => steerUltragoal(cwd, { | |
| + kind: 'annotate_ledger', | |
| + source: 'cli', | |
| + evidence: 'commit-time race evidence', | |
| + rationale: 'record evidence without changing the goal graph', | |
| + }), | |
| + /writable lifecycle authority/, | |
| + ); | |
| + assert.equal(await readFile(goalsPath, 'utf-8'), beforeGoals); | |
| + assert.equal(await readFile(ledgerPath, 'utf-8'), beforeLedger); | |
| + } finally { | |
| + __setWritableStateScopeTestHooksForTests({}); | |
| + if (typeof previousBackend === 'string') process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = previousBackend; | |
| + else delete process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + if (typeof previousCodexThreadId === 'string') process.env.CODEX_THREAD_ID = previousCodexThreadId; | |
| + else delete process.env.CODEX_THREAD_ID; | |
| + if (typeof previousOmxSessionId === 'string') process.env.OMX_SESSION_ID = previousOmxSessionId; | |
| + else delete process.env.OMX_SESSION_ID; | |
| + __resetSessionPointerTransactionDependenciesForTests(); | |
| + } | |
| + }); | |
| + }); | |
| + | |
| + it('fails a final checkpoint closed when App-native authority changes immediately before plan commit', async () => { | |
| + await withTempRepo(async (cwd) => { | |
| + const previousBackend = process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + const previousCodexThreadId = process.env.CODEX_THREAD_ID; | |
| + const previousOmxSessionId = process.env.OMX_SESSION_ID; | |
| + const sessionId = '019faf7e-0013-7000-8000-000000000013'; | |
| + const stateDir = join(cwd, '.omx', 'state'); | |
| + const sessionPath = join(stateDir, 'session.json'); | |
| + const goalsPath = join(cwd, '.omx', 'ultragoal', 'goals.json'); | |
| + const ledgerPath = join(cwd, '.omx', 'ultragoal', 'ledger.jsonl'); | |
| + let recoveryReadCount = 0; | |
| + try { | |
| + await mkdir(stateDir, { recursive: true }); | |
| + await writeFile(sessionPath, JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd, | |
| + state_root: stateDir, | |
| + pid: 8388607, | |
| + })); | |
| + __setSessionPointerTransactionDependenciesForTests({ probePid: () => 'indeterminate' }); | |
| + delete process.env.OMX_SESSION_ID; | |
| + process.env.CODEX_THREAD_ID = sessionId; | |
| + process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = 'local-experimental'; | |
| + | |
| + const created = await createUltragoalPlan(cwd, { brief: '- Preserve final checkpoint authority' }); | |
| + const started = await startNextUltragoal(cwd); | |
| + const beforeGoals = await readFile(goalsPath, 'utf-8'); | |
| + const beforeLedger = await readFile(ledgerPath, 'utf-8'); | |
| + __setWritableStateScopeTestHooksForTests({ | |
| + beforeRecoveryReread: async () => { | |
| + recoveryReadCount += 1; | |
| + // Revoke the selected pointer at writePlan's final pre-rename check. | |
| + if (recoveryReadCount === 5) { | |
| + await writeFile(sessionPath, JSON.stringify({ | |
| + session_id: sessionId, | |
| + native_session_id: sessionId, | |
| + cwd, | |
| + state_root: stateDir, | |
| + pid: 9999999, | |
| + })); | |
| + } | |
| + }, | |
| + }); | |
| + | |
| + await assert.rejects( | |
| + () => checkpointUltragoal(cwd, { | |
| + goalId: started.goal!.id, | |
| + status: 'complete', | |
| + evidence: 'implementation and verification complete for the active ultragoal', | |
| + codexGoal: { goal: { objective: created.codexObjective, status: 'complete' } }, | |
| + qualityGate: cleanQualityGate(), | |
| + }), | |
| + /writable lifecycle authority/, | |
| + ); | |
| + assert.equal(await readFile(goalsPath, 'utf-8'), beforeGoals); | |
| + assert.equal(await readFile(ledgerPath, 'utf-8'), beforeLedger); | |
| + } finally { | |
| + __setWritableStateScopeTestHooksForTests({}); | |
| + if (typeof previousBackend === 'string') process.env.OMX_NATIVE_ULTRAGOAL_BACKEND = previousBackend; | |
| + else delete process.env.OMX_NATIVE_ULTRAGOAL_BACKEND; | |
| + if (typeof previousCodexThreadId === 'string') process.env.CODEX_THREAD_ID = previousCodexThreadId; | |
| + else delete process.env.CODEX_THREAD_ID; | |
| + if (typeof previousOmxSessionId === 'string') process.env.OMX_SESSION_ID = previousOmxSessionId; | |
| + else delete process.env.OMX_SESSION_ID; | |
| __resetSessionPointerTransactionDependenciesForTests(); | |
| } | |
| }); | |
| diff --git a/src/ultragoal/__tests__/native-bootstrap.test.ts b/src/ultragoal/__tests__/native-bootstrap.test.ts | |
| new file mode 100644 | |
| index 00000000..233edbc5 | |
| --- /dev/null | |
| +++ b/src/ultragoal/__tests__/native-bootstrap.test.ts | |
| @@ -0,0 +1,755 @@ | |
| +import assert from 'node:assert/strict'; | |
| +import { chmod, link, mkdir, mkdtemp, readFile, rm, symlink, writeFile } from 'node:fs/promises'; | |
| +import { tmpdir } from 'node:os'; | |
| +import { dirname, join } from 'node:path'; | |
| +import { describe, it } from 'node:test'; | |
| +import { | |
| + beginNativeUltragoalBootstrap, | |
| + DOCUMENTED_HOST_RECEIPT_UNAVAILABLE, | |
| + findReadyLocalNativeExecutorAssignment, | |
| + issueLocalNativeExecutorAssignment, | |
| + markNativeUltragoalBootstrapActive, | |
| + nativeExecutorAssignmentPath, | |
| + prepareNativeUltragoalBootstrapActivation, | |
| + readNativeUltragoalBootstrap, | |
| + reconcileLocalNativeExecutorAssignments, | |
| + resolveLocalNativeExecutorChildReceipt, | |
| + revokeLocalNativeExecutorAssignment, | |
| + unavailableHostNativeExecutorReceiptVerifier, | |
| + validateLocalNativeExecutorIdentity, | |
| +} from '../native-bootstrap.js'; | |
| + | |
| +async function writeJson(path: string, value: unknown): Promise<void> { | |
| + await mkdir(dirname(path), { recursive: true }); | |
| + await writeFile(path, `${JSON.stringify(value, null, 2)}\n`); | |
| +} | |
| + | |
| +async function fixture() { | |
| + const cwd = await mkdtemp(join(tmpdir(), 'omx-native-bootstrap-')); | |
| + const stateDir = join(cwd, '.omx', 'state'); | |
| + const sessionId = 'session-native-bootstrap'; | |
| + const rootThreadId = 'root-native-bootstrap'; | |
| + const childAgentId = 'child-native-bootstrap'; | |
| + const now = new Date('2026-07-28T12:00:00.000Z'); | |
| + await writeJson(join(stateDir, 'subagent-tracking.json'), { | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + [sessionId]: { | |
| + session_id: sessionId, | |
| + leader_thread_id: rootThreadId, | |
| + updated_at: now.toISOString(), | |
| + threads: { | |
| + [rootThreadId]: { | |
| + thread_id: rootThreadId, | |
| + kind: 'leader', | |
| + first_seen_at: now.toISOString(), | |
| + last_seen_at: now.toISOString(), | |
| + turn_count: 1, | |
| + }, | |
| + [childAgentId]: { | |
| + thread_id: childAgentId, | |
| + kind: 'subagent', | |
| + mode: 'executor', | |
| + status: 'available', | |
| + provenance_kind: 'native_subagent', | |
| + direct_child_parent_id: rootThreadId, | |
| + direct_child_root_id: rootThreadId, | |
| + first_seen_at: now.toISOString(), | |
| + last_seen_at: now.toISOString(), | |
| + turn_count: 1, | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + }); | |
| + return { cwd, stateDir, sessionId, rootThreadId, childAgentId, now }; | |
| +} | |
| + | |
| +async function writeChildRollout(input: { | |
| + codexHome: string; | |
| + childId: string; | |
| + timestamp: string; | |
| + cwd: string; | |
| + sessionId: string; | |
| + rootThreadId: string; | |
| + agentPath: string; | |
| + depth?: number; | |
| + role?: string; | |
| + directory?: string; | |
| +}): Promise<string> { | |
| + const path = join( | |
| + input.codexHome, | |
| + 'sessions', | |
| + input.directory ?? '2026/07/29', | |
| + `rollout-test-${input.childId}.jsonl`, | |
| + ); | |
| + await mkdir(dirname(path), { recursive: true }); | |
| + await writeFile(path, `${JSON.stringify({ | |
| + timestamp: input.timestamp, | |
| + type: 'session_meta', | |
| + payload: { | |
| + id: input.childId, | |
| + session_id: input.sessionId, | |
| + cwd: input.cwd, | |
| + source: { | |
| + subagent: { | |
| + thread_spawn: { | |
| + parent_thread_id: input.rootThreadId, | |
| + depth: input.depth ?? 1, | |
| + agent_path: input.agentPath, | |
| + agent_role: input.role ?? 'executor', | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + })}\n`); | |
| + return path; | |
| +} | |
| + | |
| +describe('native Ultragoal bootstrap', () => { | |
| + it('seeds only the exact descriptive root tracker and fails closed on leader contradiction', async () => { | |
| + const cwd = await mkdtemp(join(tmpdir(), 'omx-native-bootstrap-root-seed-')); | |
| + const stateDir = join(cwd, '.omx', 'state'); | |
| + const sessionId = 'session-root-seed'; | |
| + const rootThreadId = 'root-thread-seed'; | |
| + try { | |
| + await beginNativeUltragoalBootstrap({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId, | |
| + rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 300, | |
| + }); | |
| + const tracking = JSON.parse(await readFile(join(stateDir, 'subagent-tracking.json'), 'utf8')) as { | |
| + sessions: Record<string, { | |
| + leader_thread_id?: string; | |
| + threads: Record<string, Record<string, unknown>>; | |
| + }>; | |
| + }; | |
| + const root = tracking.sessions[sessionId]?.threads[rootThreadId]; | |
| + assert.equal(tracking.sessions[sessionId]?.leader_thread_id, rootThreadId); | |
| + assert.equal(root?.kind, 'leader'); | |
| + assert.equal(root?.provenance_kind, undefined); | |
| + assert.equal(root?.direct_child_parent_id, undefined); | |
| + assert.equal(root?.direct_child_root_id, undefined); | |
| + | |
| + const conflictingSessionId = 'session-root-conflict'; | |
| + const conflictTimestamp = new Date().toISOString(); | |
| + await writeJson(join(stateDir, 'subagent-tracking.json'), { | |
| + schemaVersion: 1, | |
| + sessions: { | |
| + [conflictingSessionId]: { | |
| + session_id: conflictingSessionId, | |
| + leader_thread_id: 'foreign-root', | |
| + updated_at: conflictTimestamp, | |
| + threads: { | |
| + 'foreign-root': { | |
| + thread_id: 'foreign-root', | |
| + kind: 'leader', | |
| + first_seen_at: conflictTimestamp, | |
| + last_seen_at: conflictTimestamp, | |
| + turn_count: 1, | |
| + }, | |
| + }, | |
| + }, | |
| + }, | |
| + }); | |
| + await assert.rejects(beginNativeUltragoalBootstrap({ | |
| + cwd, | |
| + stateDir, | |
| + sessionId: conflictingSessionId, | |
| + rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 300, | |
| + }), /native_ultragoal_root_tracker_conflict/); | |
| + assert.equal(await readNativeUltragoalBootstrap(stateDir, conflictingSessionId), null); | |
| + const afterConflict = JSON.parse(await readFile(join(stateDir, 'subagent-tracking.json'), 'utf8')) as { | |
| + sessions: Record<string, { threads: Record<string, unknown> }>; | |
| + }; | |
| + assert.equal(afterConflict.sessions[conflictingSessionId]?.threads[rootThreadId], undefined); | |
| + } finally { | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('resolves a safe unique child rollout from an exact agent path without granting tracker authority', async () => { | |
| + const cwd = await mkdtemp(join(tmpdir(), 'omx-native-child-path-')); | |
| + const codexHome = join(cwd, 'codex-home'); | |
| + const previousCodexHome = process.env.CODEX_HOME; | |
| + process.env.CODEX_HOME = codexHome; | |
| + const childId = '019faf7d-ffbb-70d2-b58f-9bb83e752007'; | |
| + const sessionId = 'session-child-path'; | |
| + const rootThreadId = 'root-child-path'; | |
| + const agentPath = '/root/ultragoal_native_executor'; | |
| + const timestamp = '2026-07-29T20:00:00.000Z'; | |
| + try { | |
| + await writeChildRollout({ | |
| + codexHome, | |
| + childId, | |
| + timestamp, | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + agentPath, | |
| + }); | |
| + assert.deepEqual(await resolveLocalNativeExecutorChildReceipt({ | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + agentPath, | |
| + receiptNotBefore: '2026-07-29T19:59:00.000Z', | |
| + receiptNotAfter: '2026-07-29T20:01:00.000Z', | |
| + now: new Date(timestamp), | |
| + }), { ok: true, childAgentId: childId }); | |
| + await assert.rejects(readFile(join(cwd, '.omx', 'state', 'subagent-tracking.json')), /ENOENT/); | |
| + } finally { | |
| + if (previousCodexHome === undefined) delete process.env.CODEX_HOME; | |
| + else process.env.CODEX_HOME = previousCodexHome; | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('rejects missing, ambiguous, stale, foreign, malformed, writable, and linked child-path receipts', async () => { | |
| + const cwd = await mkdtemp(join(tmpdir(), 'omx-native-child-path-deny-')); | |
| + const codexHome = join(cwd, 'codex-home'); | |
| + const previousCodexHome = process.env.CODEX_HOME; | |
| + process.env.CODEX_HOME = codexHome; | |
| + const sessionId = 'session-child-path-deny'; | |
| + const rootThreadId = 'root-child-path-deny'; | |
| + const timestamp = '2026-07-29T20:00:00.000Z'; | |
| + const window = { | |
| + receiptNotBefore: '2026-07-29T19:59:00.000Z', | |
| + receiptNotAfter: '2026-07-29T20:01:00.000Z', | |
| + now: new Date(timestamp), | |
| + }; | |
| + const resolvePath = (agentPath: string) => resolveLocalNativeExecutorChildReceipt({ | |
| + cwd, sessionId, rootThreadId, agentPath, ...window, | |
| + }); | |
| + try { | |
| + await mkdir(join(codexHome, 'sessions'), { recursive: true }); | |
| + assert.deepEqual(await resolvePath('/root/missing'), { | |
| + ok: false, reason: 'child_transcript_receipt_missing', | |
| + }); | |
| + | |
| + const unreadableDirectory = join(codexHome, 'sessions', 'unreadable'); | |
| + await mkdir(unreadableDirectory, { recursive: true }); | |
| + await chmod(unreadableDirectory, 0o000); | |
| + assert.deepEqual(await resolvePath('/root/missing'), { | |
| + ok: false, reason: 'child_transcript_receipt_scan_incomplete', | |
| + }); | |
| + await beginNativeUltragoalBootstrap({ | |
| + cwd, | |
| + stateDir: join(cwd, '.omx', 'state'), | |
| + sessionId, | |
| + rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 300, | |
| + now: new Date(timestamp), | |
| + }); | |
| + assert.deepEqual(await validateLocalNativeExecutorIdentity({ | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + childAgentId: '019faf7d-0099-7000-8000-000000000099', | |
| + receiptNotBefore: window.receiptNotBefore, | |
| + receiptNotAfter: window.receiptNotAfter, | |
| + }), { ok: false, reason: 'child_transcript_receipt_scan_incomplete' }); | |
| + await chmod(unreadableDirectory, 0o700); | |
| + | |
| + const invalidCases = [ | |
| + { name: 'stale', childId: '019faf7d-0001-7000-8000-000000000001', timestamp: '2026-07-29T19:58:59.000Z' }, | |
| + { name: 'parent', childId: '019faf7d-0002-7000-8000-000000000002', rootThreadId: 'foreign-root' }, | |
| + { name: 'depth', childId: '019faf7d-0003-7000-8000-000000000003', depth: 2 }, | |
| + { name: 'role', childId: '019faf7d-0004-7000-8000-000000000004', role: 'reviewer' }, | |
| + { name: 'cwd', childId: '019faf7d-0005-7000-8000-000000000005', cwd: codexHome }, | |
| + { name: 'session', childId: '019faf7d-0006-7000-8000-000000000006', sessionId: 'foreign-session' }, | |
| + { name: 'id', childId: 'not-a-canonical-uuid' }, | |
| + ]; | |
| + for (const invalid of invalidCases) { | |
| + const agentPath = `/root/deny_${invalid.name}`; | |
| + await writeChildRollout({ | |
| + codexHome, | |
| + childId: invalid.childId, | |
| + timestamp: invalid.timestamp ?? timestamp, | |
| + cwd: invalid.cwd ?? cwd, | |
| + sessionId: invalid.sessionId ?? sessionId, | |
| + rootThreadId: invalid.rootThreadId ?? rootThreadId, | |
| + agentPath, | |
| + depth: invalid.depth, | |
| + role: invalid.role, | |
| + directory: invalid.name, | |
| + }); | |
| + assert.deepEqual(await resolvePath(agentPath), { | |
| + ok: false, reason: 'child_transcript_receipt_missing', | |
| + }, invalid.name); | |
| + } | |
| + | |
| + const writablePath = await writeChildRollout({ | |
| + codexHome, | |
| + childId: '019faf7d-0007-7000-8000-000000000007', | |
| + timestamp, | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + agentPath: '/root/deny_writable', | |
| + directory: 'writable', | |
| + }); | |
| + await chmod(writablePath, 0o666); | |
| + assert.deepEqual(await resolvePath('/root/deny_writable'), { | |
| + ok: false, reason: 'child_transcript_receipt_missing', | |
| + }); | |
| + | |
| + const linkedSource = await writeChildRollout({ | |
| + codexHome, | |
| + childId: '019faf7d-0008-7000-8000-000000000008', | |
| + timestamp, | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + agentPath: '/root/deny_linked', | |
| + directory: 'linked', | |
| + }); | |
| + await link(linkedSource, `${linkedSource}.copy`); | |
| + assert.deepEqual(await resolvePath('/root/deny_linked'), { | |
| + ok: false, reason: 'child_transcript_receipt_missing', | |
| + }); | |
| + | |
| + const symlinkTarget = await writeChildRollout({ | |
| + codexHome, | |
| + childId: '019faf7d-0009-7000-8000-000000000009', | |
| + timestamp, | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + agentPath: '/root/deny_symlink', | |
| + directory: 'outside-live-tree', | |
| + }); | |
| + const symlinkPath = join(codexHome, 'sessions', `rollout-link-019faf7d-0009-7000-8000-000000000009.jsonl`); | |
| + await symlink(symlinkTarget, symlinkPath); | |
| + await rm(symlinkTarget); | |
| + assert.deepEqual(await resolvePath('/root/deny_symlink'), { | |
| + ok: false, reason: 'child_transcript_receipt_missing', | |
| + }); | |
| + | |
| + for (const [index, childId] of [ | |
| + '019faf7d-0010-7000-8000-000000000010', | |
| + '019faf7d-0011-7000-8000-000000000011', | |
| + ].entries()) { | |
| + await writeChildRollout({ | |
| + codexHome, | |
| + childId, | |
| + timestamp, | |
| + cwd, | |
| + sessionId, | |
| + rootThreadId, | |
| + agentPath: '/root/deny_ambiguous', | |
| + directory: `ambiguous-${index}`, | |
| + }); | |
| + } | |
| + assert.deepEqual(await resolvePath('/root/deny_ambiguous'), { | |
| + ok: false, reason: 'child_transcript_receipt_ambiguous', | |
| + }); | |
| + } finally { | |
| + if (previousCodexHome === undefined) delete process.env.CODEX_HOME; | |
| + else process.env.CODEX_HOME = previousCodexHome; | |
| + await rm(cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + it('keeps the host backend explicitly unavailable without a documented verifier', async () => { | |
| + const f = await fixture(); | |
| + try { | |
| + const state = await beginNativeUltragoalBootstrap({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + backend: 'host', | |
| + ttlSeconds: 300, | |
| + now: f.now, | |
| + }); | |
| + assert.equal(state.status, 'host-receipt-unavailable'); | |
| + assert.equal(state.reason, DOCUMENTED_HOST_RECEIPT_UNAVAILABLE); | |
| + assert.deepEqual(await unavailableHostNativeExecutorReceiptVerifier.verifyActivationReceipt({ | |
| + cwd: f.cwd, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + }), { ok: false, reason: DOCUMENTED_HOST_RECEIPT_UNAVAILABLE }); | |
| + } finally { | |
| + await rm(f.cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('orders local bootstrap before assignment and activation', async () => { | |
| + const f = await fixture(); | |
| + try { | |
| + const bootstrap = await beginNativeUltragoalBootstrap({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 900, | |
| + now: f.now, | |
| + }); | |
| + assert.equal(bootstrap.status, 'awaiting-executor'); | |
| + assert.equal(await findReadyLocalNativeExecutorAssignment({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + now: f.now, | |
| + }), null); | |
| + | |
| + await assert.rejects( | |
| + issueLocalNativeExecutorAssignment({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + childAgentId: f.childAgentId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ['$UNRESOLVED/*'], | |
| + ttlSeconds: 300, | |
| + now: f.now, | |
| + }), | |
| + /invalid_native_assignment_path_prefixes/, | |
| + ); | |
| + | |
| + const assignment = await issueLocalNativeExecutorAssignment({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + childAgentId: f.childAgentId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ['src', 'tests'], | |
| + ttlSeconds: 300, | |
| + now: f.now, | |
| + }); | |
| + assert.equal(assignment.bootstrap_generation, bootstrap.generation); | |
| + assert.equal((await readNativeUltragoalBootstrap(f.stateDir, f.sessionId))?.status, 'ready'); | |
| + assert.equal((await findReadyLocalNativeExecutorAssignment({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + now: f.now, | |
| + }))?.child_agent_id, f.childAgentId); | |
| + | |
| + const prepared = await prepareNativeUltragoalBootstrapActivation({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + childAgentId: f.childAgentId, | |
| + now: f.now, | |
| + }); | |
| + assert.equal(prepared.ok, true); | |
| + assert.equal((await readNativeUltragoalBootstrap(f.stateDir, f.sessionId))?.status, 'activating'); | |
| + const activation = await markNativeUltragoalBootstrapActive({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + childAgentId: f.childAgentId, | |
| + now: f.now, | |
| + }); | |
| + assert.equal(activation.ok, true); | |
| + assert.equal((await readNativeUltragoalBootstrap(f.stateDir, f.sessionId))?.status, 'active'); | |
| + assert.equal((await markNativeUltragoalBootstrapActive({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + childAgentId: f.childAgentId, | |
| + now: f.now, | |
| + })).ok, true); | |
| + | |
| + const renewed = await issueLocalNativeExecutorAssignment({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + childAgentId: f.childAgentId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ['src'], | |
| + ttlSeconds: 600, | |
| + now: new Date(f.now.getTime() + 60_000), | |
| + }); | |
| + assert.equal(renewed.bootstrap_generation, bootstrap.generation); | |
| + assert.equal((await readNativeUltragoalBootstrap(f.stateDir, f.sessionId))?.status, 'active'); | |
| + } finally { | |
| + await rm(f.cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('revokes a local lease when tracker lifecycle closes and supports explicit revoke', async () => { | |
| + const f = await fixture(); | |
| + try { | |
| + const bootstrap = await beginNativeUltragoalBootstrap({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 900, | |
| + now: f.now, | |
| + }); | |
| + await issueLocalNativeExecutorAssignment({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + childAgentId: f.childAgentId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ['src'], | |
| + ttlSeconds: 300, | |
| + now: f.now, | |
| + }); | |
| + const trackerPath = join(f.stateDir, 'subagent-tracking.json'); | |
| + const tracker = JSON.parse(await readFile(trackerPath, 'utf-8')) as { | |
| + sessions: Record<string, { threads: Record<string, { status: string }> }>; | |
| + }; | |
| + tracker.sessions[f.sessionId].threads[f.childAgentId].status = 'closed'; | |
| + await writeJson(trackerPath, tracker); | |
| + assert.deepEqual(await reconcileLocalNativeExecutorAssignments({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + now: f.now, | |
| + }), { ready: false }); | |
| + const assignment = JSON.parse(await readFile( | |
| + nativeExecutorAssignmentPath(f.stateDir, f.sessionId, f.childAgentId), | |
| + 'utf-8', | |
| + )) as { lifecycle: string; revoke_reason?: string }; | |
| + assert.equal(assignment.lifecycle, 'revoked'); | |
| + assert.equal(assignment.revoke_reason, 'child_closed'); | |
| + | |
| + await revokeLocalNativeExecutorAssignment({ | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + reason: 'root_cancelled', | |
| + now: f.now, | |
| + }); | |
| + assert.equal((await readNativeUltragoalBootstrap(f.stateDir, f.sessionId))?.status, 'revoked'); | |
| + } finally { | |
| + await rm(f.cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('denies explicit and fail-closed durable authority fences', async () => { | |
| + const f = await fixture(); | |
| + try { | |
| + const bootstrap = await beginNativeUltragoalBootstrap({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 900, | |
| + now: f.now, | |
| + }); | |
| + const fencePath = join(f.stateDir, 'subagent-tracking.json.authority-fence.json'); | |
| + await writeJson(fencePath, { ids: [f.childAgentId] }); | |
| + await assert.rejects(issueLocalNativeExecutorAssignment({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + childAgentId: f.childAgentId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ['src'], | |
| + ttlSeconds: 300, | |
| + now: f.now, | |
| + }), /child_authority_fenced/); | |
| + | |
| + await writeFile(fencePath, '{ malformed'); | |
| + await assert.rejects(issueLocalNativeExecutorAssignment({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + childAgentId: f.childAgentId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ['src'], | |
| + ttlSeconds: 300, | |
| + now: f.now, | |
| + }), /child_authority_fenced/); | |
| + } finally { | |
| + await rm(f.cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('rotates expired bootstrap generations and cannot activate without the exact assignment file', async () => { | |
| + const f = await fixture(); | |
| + try { | |
| + const first = await beginNativeUltragoalBootstrap({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 30, | |
| + now: f.now, | |
| + }); | |
| + await issueLocalNativeExecutorAssignment({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + childAgentId: f.childAgentId, | |
| + expectedGeneration: first.generation, | |
| + pathPrefixes: ['src'], | |
| + ttlSeconds: 30, | |
| + now: f.now, | |
| + }); | |
| + const assignmentPath = nativeExecutorAssignmentPath(f.stateDir, f.sessionId, f.childAgentId); | |
| + await rm(assignmentPath); | |
| + const missingAssignmentActivation = await markNativeUltragoalBootstrapActive({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + childAgentId: f.childAgentId, | |
| + now: f.now, | |
| + }); | |
| + assert.equal(missingAssignmentActivation.ok, false); | |
| + assert.equal((await readNativeUltragoalBootstrap(f.stateDir, f.sessionId))?.status, 'ready'); | |
| + | |
| + const second = await beginNativeUltragoalBootstrap({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 30, | |
| + now: new Date(f.now.getTime() + 31_000), | |
| + }); | |
| + assert.notEqual(second.generation, first.generation); | |
| + assert.equal(second.status, 'awaiting-executor'); | |
| + } finally { | |
| + await rm(f.cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('serializes authorize before revoke so stale readiness cannot resurrect authority', async () => { | |
| + const f = await fixture(); | |
| + try { | |
| + const bootstrap = await beginNativeUltragoalBootstrap({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 900, | |
| + now: f.now, | |
| + }); | |
| + const concurrent = await Promise.allSettled([ | |
| + issueLocalNativeExecutorAssignment({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + childAgentId: f.childAgentId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ['src'], | |
| + ttlSeconds: 300, | |
| + now: f.now, | |
| + }), | |
| + revokeLocalNativeExecutorAssignment({ | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + reason: 'concurrent_root_revoke', | |
| + now: f.now, | |
| + }), | |
| + ]); | |
| + if (concurrent[0].status === 'rejected') { | |
| + assert.match(String(concurrent[0].reason), /native_ultragoal_bootstrap_not_ready/); | |
| + } | |
| + assert.equal(concurrent[1].status, 'fulfilled'); | |
| + assert.equal((await readNativeUltragoalBootstrap(f.stateDir, f.sessionId))?.status, 'revoked'); | |
| + const assignmentPath = nativeExecutorAssignmentPath(f.stateDir, f.sessionId, f.childAgentId); | |
| + if (concurrent[0].status === 'fulfilled') { | |
| + const assignment = JSON.parse(await readFile(assignmentPath, 'utf-8')) as { | |
| + lifecycle: string; | |
| + revoke_reason?: string; | |
| + }; | |
| + assert.equal(assignment.lifecycle, 'revoked'); | |
| + assert.equal(assignment.revoke_reason, 'concurrent_root_revoke'); | |
| + } else { | |
| + await assert.rejects(readFile(assignmentPath), /ENOENT/); | |
| + } | |
| + const replacement = await beginNativeUltragoalBootstrap({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 900, | |
| + now: f.now, | |
| + }); | |
| + await assert.rejects(issueLocalNativeExecutorAssignment({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + childAgentId: f.childAgentId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ['src'], | |
| + ttlSeconds: 300, | |
| + now: f.now, | |
| + }), /native_ultragoal_bootstrap_not_ready/); | |
| + assert.notEqual(replacement.generation, bootstrap.generation); | |
| + } finally { | |
| + await rm(f.cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| + | |
| + it('recovers one dead lifecycle lock without letting concurrent mutations overlap', async () => { | |
| + const f = await fixture(); | |
| + try { | |
| + const bootstrap = await beginNativeUltragoalBootstrap({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + backend: 'local-experimental', | |
| + ttlSeconds: 900, | |
| + now: f.now, | |
| + }); | |
| + const lockPath = join( | |
| + f.stateDir, | |
| + 'sessions', | |
| + f.sessionId, | |
| + 'native-ultragoal-bootstrap.json.lock', | |
| + ); | |
| + await writeJson(lockPath, { pid: 2_147_483_647, token: 'dead-owner' }); | |
| + const concurrent = await Promise.allSettled([ | |
| + issueLocalNativeExecutorAssignment({ | |
| + cwd: f.cwd, | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + rootThreadId: f.rootThreadId, | |
| + childAgentId: f.childAgentId, | |
| + expectedGeneration: bootstrap.generation, | |
| + pathPrefixes: ['src'], | |
| + ttlSeconds: 300, | |
| + now: f.now, | |
| + }), | |
| + revokeLocalNativeExecutorAssignment({ | |
| + stateDir: f.stateDir, | |
| + sessionId: f.sessionId, | |
| + reason: 'dead_lock_concurrent_revoke', | |
| + now: f.now, | |
| + }), | |
| + ]); | |
| + if (concurrent[0].status === 'rejected') { | |
| + assert.match(String(concurrent[0].reason), /native_ultragoal_bootstrap_not_ready/); | |
| + } | |
| + assert.equal(concurrent[1].status, 'fulfilled'); | |
| + assert.equal((await readNativeUltragoalBootstrap(f.stateDir, f.sessionId))?.status, 'revoked'); | |
| + await assert.rejects(readFile(lockPath), /ENOENT/); | |
| + await assert.rejects(readFile(`${lockPath}.reaper`), /ENOENT/); | |
| + } finally { | |
| + await rm(f.cwd, { recursive: true, force: true }); | |
| + } | |
| + }); | |
| +}); | |
| diff --git a/src/ultragoal/artifacts.ts b/src/ultragoal/artifacts.ts | |
| index 9e4ca72f..920e1f3c 100644 | |
| --- a/src/ultragoal/artifacts.ts | |
| +++ b/src/ultragoal/artifacts.ts | |
| @@ -3,6 +3,7 @@ import { appendFile, mkdir, open, readFile, rename, rm, writeFile } from 'node:f | |
| import { isAbsolute, join, relative } from 'node:path'; | |
| import { normalizeSessionId, resolveWritableStateScope, WRITABLE_STATE_SCOPE_ERRORS } from '../mcp/state-paths.js'; | |
| import type { ResolvedStateScope } from '../mcp/state-paths.js'; | |
| +import { resolveNativeUltragoalConfig } from '../config/native-ultragoal.js'; | |
| import { | |
| formatCodexGoalReconciliation, | |
| buildCompletedCodexGoalRemediation, | |
| @@ -874,7 +875,9 @@ export async function assertUltragoalWritableLifecycleAuthority( | |
| ): Promise<UltragoalWritableAuthority> { | |
| try { | |
| const suppliedEnvironmentSessionId = process.env.OMX_SESSION_ID?.trim(); | |
| - const scope = await resolveWritableStateScope(cwd); | |
| + const scope = await resolveWritableStateScope(cwd, undefined, { | |
| + nativeUltragoalBackend: resolveNativeUltragoalConfig().backend, | |
| + }); | |
| if ( | |
| options.allowUnboundEnvironment === false | |
| && suppliedEnvironmentSessionId | |
| @@ -933,7 +936,7 @@ function describeWritableAuthority(authority: UltragoalWritableAuthority): strin | |
| async function withUltragoalMutationLock<T>( | |
| cwd: string, | |
| - operation: () => Promise<T>, | |
| + operation: (beforeCommit: () => Promise<void>) => Promise<T>, | |
| options: { allowUnboundEnvironment?: boolean } = {}, | |
| ): Promise<T> { | |
| const beforeLock = await assertUltragoalWritableLifecycleAuthority(cwd, options); | |
| @@ -964,15 +967,29 @@ async function withUltragoalMutationLock<T>( | |
| `Refusing durable ultragoal mutation after writable lifecycle authority drift while waiting for the mutation lock: before lock ${describeWritableAuthority(beforeLock)}; after lock ${describeWritableAuthority(afterLock)}.`, | |
| ); | |
| } | |
| - return await operation(); | |
| + const beforeCommit = async (): Promise<void> => { | |
| + const current = await assertUltragoalWritableLifecycleAuthority(cwd, options); | |
| + if (!writableAuthorityEquals(afterLock, current)) { | |
| + throw new UltragoalError( | |
| + `Refusing durable ultragoal mutation after writable lifecycle authority drift before commit: locked ${describeWritableAuthority(afterLock)}; current ${describeWritableAuthority(current)}.`, | |
| + ); | |
| + } | |
| + }; | |
| + return await operation(beforeCommit); | |
| } finally { | |
| await handle.close().catch(() => undefined); | |
| await rm(lockPath, { force: true }).catch(() => undefined); | |
| } | |
| } | |
| -async function appendLedger(cwd: string, entry: UltragoalLedgerEntry): Promise<void> { | |
| +async function appendLedger( | |
| + cwd: string, | |
| + entry: UltragoalLedgerEntry, | |
| + beforeCommit: () => Promise<void>, | |
| +): Promise<void> { | |
| + await beforeCommit(); | |
| await mkdir(ultragoalDir(cwd), { recursive: true }); | |
| + await beforeCommit(); | |
| const path = ultragoalLedgerPath(cwd); | |
| await appendFile(path, `${JSON.stringify(entry)}\n`); | |
| } | |
| @@ -1014,7 +1031,11 @@ function requiresAggregateObjectiveMigration(plan: UltragoalPlan, statePathPrefi | |
| } | |
| /** Durable legacy-objective migration; caller MUST hold the mutation lock. */ | |
| -async function migrateAggregateObjectiveUnderLock(cwd: string, parsed: UltragoalPlan): Promise<UltragoalPlan> { | |
| +async function migrateAggregateObjectiveUnderLock( | |
| + cwd: string, | |
| + parsed: UltragoalPlan, | |
| + beforeCommit: () => Promise<void>, | |
| +): Promise<UltragoalPlan> { | |
| const statePathPrefix = ultragoalStatePathPrefix(cwd); | |
| if (!requiresAggregateObjectiveMigration(parsed, statePathPrefix)) return parsed; | |
| const canonicalStatePathMigration = requiresCanonicalStatePathMigration(parsed, statePathPrefix); | |
| @@ -1024,7 +1045,7 @@ async function migrateAggregateObjectiveUnderLock(cwd: string, parsed: Ultragoal | |
| parsed.statePathPrefix = statePathPrefix === '' ? undefined : statePathPrefix; | |
| parsed.codexObjectiveAliases = Array.from(new Set([...(parsed.codexObjectiveAliases ?? []), previousObjective].filter((value): value is string => typeof value === 'string' && value.length > 0))); | |
| parsed.updatedAt = now; | |
| - await writePlan(cwd, parsed); | |
| + await writePlan(cwd, parsed, beforeCommit); | |
| await appendLedger(cwd, { | |
| ts: now, | |
| event: 'aggregate_objective_migrated', | |
| @@ -1033,13 +1054,16 @@ async function migrateAggregateObjectiveUnderLock(cwd: string, parsed: Ultragoal | |
| : 'Migrated legacy enumerated aggregate Codex objective to the stable pointer objective.', | |
| before: { codexObjective: previousObjective }, | |
| after: { codexObjective: parsed.codexObjective }, | |
| - }); | |
| + }, beforeCommit); | |
| return parsed; | |
| } | |
| /** Locked plan read for mutators that already hold the mutation lock. */ | |
| -async function readUltragoalPlanUnderLock(cwd: string): Promise<UltragoalPlan> { | |
| - return migrateAggregateObjectiveUnderLock(cwd, await readUltragoalPlanFile(cwd)); | |
| +async function readUltragoalPlanUnderLock( | |
| + cwd: string, | |
| + beforeCommit: () => Promise<void>, | |
| +): Promise<UltragoalPlan> { | |
| + return migrateAggregateObjectiveUnderLock(cwd, await readUltragoalPlanFile(cwd), beforeCommit); | |
| } | |
| export async function readUltragoalPlan(cwd: string): Promise<UltragoalPlan> { | |
| @@ -1048,19 +1072,30 @@ export async function readUltragoalPlan(cwd: string): Promise<UltragoalPlan> { | |
| // The legacy objective migration is a durable story transition: it requires | |
| // writable lifecycle authority and the mutation lock, and re-reads the plan | |
| // under the lock so a concurrent mutator cannot be clobbered or duplicated. | |
| - return withUltragoalMutationLock(cwd, async () => readUltragoalPlanUnderLock(cwd)); | |
| + return withUltragoalMutationLock(cwd, async (beforeCommit) => readUltragoalPlanUnderLock(cwd, beforeCommit)); | |
| } | |
| -async function writePlan(cwd: string, plan: UltragoalPlan): Promise<void> { | |
| +async function writePlan( | |
| + cwd: string, | |
| + plan: UltragoalPlan, | |
| + beforeCommit: () => Promise<void>, | |
| +): Promise<void> { | |
| + await beforeCommit(); | |
| await mkdir(ultragoalDir(cwd), { recursive: true }); | |
| const path = ultragoalGoalsPath(cwd); | |
| const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`; | |
| - await writeFile(tmpPath, `${JSON.stringify(plan, null, 2)}\n`); | |
| - await rename(tmpPath, path); | |
| + await beforeCommit(); | |
| + try { | |
| + await writeFile(tmpPath, `${JSON.stringify(plan, null, 2)}\n`); | |
| + await beforeCommit(); | |
| + await rename(tmpPath, path); | |
| + } finally { | |
| + await rm(tmpPath, { force: true }).catch(() => undefined); | |
| + } | |
| } | |
| export async function createUltragoalPlan(cwd: string, options: CreateUltragoalOptions): Promise<UltragoalPlan> { | |
| - return withUltragoalMutationLock(cwd, async () => { | |
| + return withUltragoalMutationLock(cwd, async (beforeCommit) => { | |
| if (!options.force && existsSync(ultragoalGoalsPath(cwd))) { | |
| throw new UltragoalError(`Refusing to overwrite existing ${ULTRAGOAL_DIR}/${ULTRAGOAL_GOALS}; pass --force to recreate it.`); | |
| } | |
| @@ -1096,11 +1131,14 @@ export async function createUltragoalPlan(cwd: string, options: CreateUltragoalO | |
| plan.codexObjective = aggregateCodexObjective(statePathPrefix); | |
| } | |
| + await beforeCommit(); | |
| await mkdir(ultragoalDir(cwd), { recursive: true }); | |
| + await beforeCommit(); | |
| await writeFile(ultragoalBriefPath(cwd), options.brief.endsWith('\n') ? options.brief : `${options.brief}\n`); | |
| - await writePlan(cwd, plan); | |
| + await writePlan(cwd, plan, beforeCommit); | |
| + await beforeCommit(); | |
| await writeFile(ultragoalLedgerPath(cwd), ''); | |
| - await appendLedger(cwd, { ts: now, event: 'plan_created', message: `${candidates.length} goal(s) created` }); | |
| + await appendLedger(cwd, { ts: now, event: 'plan_created', message: `${candidates.length} goal(s) created` }, beforeCommit); | |
| return plan; | |
| }, { allowUnboundEnvironment: false }); | |
| } | |
| @@ -1170,14 +1208,14 @@ function appendGoalToPlan(plan: UltragoalPlan, options: AddUltragoalGoalOptions | |
| } | |
| export async function addUltragoalGoal(cwd: string, options: AddUltragoalGoalOptions): Promise<{ plan: UltragoalPlan; goal: UltragoalItem }> { | |
| - return withUltragoalMutationLock(cwd, async () => { | |
| - const plan = await readUltragoalPlanUnderLock(cwd); | |
| + return withUltragoalMutationLock(cwd, async (beforeCommit) => { | |
| + const plan = await readUltragoalPlanUnderLock(cwd, beforeCommit); | |
| if (plan.aggregateCompletion?.status === 'complete') { | |
| throw new UltragoalError('Cannot add a goal to an already completed aggregate ultragoal plan; start a new plan for post-terminal work.'); | |
| } | |
| const now = iso(options.now); | |
| const goal = appendGoalToPlan(plan, options); | |
| - await writePlan(cwd, plan); | |
| + await writePlan(cwd, plan, beforeCommit); | |
| await appendLedger(cwd, { | |
| ts: now, | |
| event: 'goal_added', | |
| @@ -1185,7 +1223,7 @@ export async function addUltragoalGoal(cwd: string, options: AddUltragoalGoalOpt | |
| status: goal.status, | |
| evidence: options.evidence, | |
| message: goal.title, | |
| - }); | |
| + }, beforeCommit); | |
| return { plan, goal }; | |
| }); | |
| } | |
| @@ -1457,8 +1495,8 @@ function applySteeringMutation(plan: UltragoalPlan, proposal: UltragoalSteeringP | |
| } | |
| export async function steerUltragoal(cwd: string, proposal: UltragoalSteeringProposal, options: { now?: Date; directiveText?: string } = {}): Promise<SteerUltragoalResult> { | |
| - return withUltragoalMutationLock(cwd, async () => { | |
| - const plan = await readUltragoalPlanUnderLock(cwd); | |
| + return withUltragoalMutationLock(cwd, async (beforeCommit) => { | |
| + const plan = await readUltragoalPlanUnderLock(cwd, beforeCommit); | |
| const existing = proposal.idempotencyKey | |
| ? (await readSteeringLedgerEntries(cwd)).find((entry) => entry.event === 'steering_accepted' && (entry.idempotencyKey === proposal.idempotencyKey || entry.steering?.idempotencyKey === proposal.idempotencyKey) && entry.steering) | |
| : undefined; | |
| @@ -1499,7 +1537,7 @@ export async function steerUltragoal(cwd: string, proposal: UltragoalSteeringPro | |
| idempotencyKey: proposal.idempotencyKey, | |
| }; | |
| - if (invariant.accepted) await writePlan(cwd, plan); | |
| + if (invariant.accepted && proposal.kind !== 'annotate_ledger') await writePlan(cwd, plan, beforeCommit); | |
| await appendLedger(cwd, { | |
| ts: now, | |
| event: invariant.accepted ? 'steering_accepted' : 'steering_rejected', | |
| @@ -1510,7 +1548,7 @@ export async function steerUltragoal(cwd: string, proposal: UltragoalSteeringPro | |
| mutationKind: proposal.kind, | |
| before: audit.before, | |
| after: audit.after, | |
| - }); | |
| + }, beforeCommit); | |
| return { plan, accepted: invariant.accepted, audit, rejectedReasons: invariant.rejectedReasons, deduped: false }; | |
| }); | |
| @@ -1730,20 +1768,20 @@ function validateQualityGate(value: unknown, requiredInvariants: readonly Requir | |
| } | |
| export async function startNextUltragoal(cwd: string, options: StartNextOptions = {}): Promise<{ plan: UltragoalPlan; goal: UltragoalItem | null; resumed: boolean; done: boolean }> { | |
| - return withUltragoalMutationLock(cwd, async () => { | |
| - const plan = await readUltragoalPlanUnderLock(cwd); | |
| + return withUltragoalMutationLock(cwd, async (beforeCommit) => { | |
| + const plan = await readUltragoalPlanUnderLock(cwd, beforeCommit); | |
| const now = iso(options.now); | |
| if (plan.aggregateCompletion?.status === 'complete') return { plan, goal: null, resumed: false, done: true }; | |
| const existing = plan.goals.find((goal) => goal.status === 'in_progress' && isScheduleEligibleGoal(goal)); | |
| if (existing) { | |
| - await appendLedger(cwd, { ts: now, event: 'goal_resumed', goalId: existing.id, status: existing.status, message: 'Resuming active ultragoal' }); | |
| + await appendLedger(cwd, { ts: now, event: 'goal_resumed', goalId: existing.id, status: existing.status, message: 'Resuming active ultragoal' }, beforeCommit); | |
| return { plan, goal: existing, resumed: true, done: false }; | |
| } | |
| let next = plan.goals.find((goal) => goal.status === 'pending' && isScheduleEligible(goal)); | |
| if (!next && options.retryFailed) { | |
| next = plan.goals.find((goal) => goal.status === 'failed' && !goal.nonRetriable && isScheduleEligible(goal)); | |
| - if (next) await appendLedger(cwd, { ts: now, event: 'goal_retried', goalId: next.id, status: 'pending', message: next.failureReason }); | |
| + if (next) await appendLedger(cwd, { ts: now, event: 'goal_retried', goalId: next.id, status: 'pending', message: next.failureReason }, beforeCommit); | |
| } | |
| if (!next) return { plan, goal: null, resumed: false, done: isUltragoalDone(plan) }; | |
| @@ -1756,15 +1794,15 @@ export async function startNextUltragoal(cwd: string, options: StartNextOptions | |
| next.updatedAt = now; | |
| plan.activeGoalId = next.id; | |
| plan.updatedAt = now; | |
| - await writePlan(cwd, plan); | |
| - await appendLedger(cwd, { ts: now, event: 'goal_started', goalId: next.id, status: next.status, message: `Attempt ${next.attempt}` }); | |
| + await writePlan(cwd, plan, beforeCommit); | |
| + await appendLedger(cwd, { ts: now, event: 'goal_started', goalId: next.id, status: next.status, message: `Attempt ${next.attempt}` }, beforeCommit); | |
| return { plan, goal: next, resumed: false, done: false }; | |
| }); | |
| } | |
| export async function checkpointUltragoal(cwd: string, options: CheckpointOptions): Promise<UltragoalPlan> { | |
| - return withUltragoalMutationLock(cwd, async () => { | |
| - const plan = await readUltragoalPlanUnderLock(cwd); | |
| + return withUltragoalMutationLock(cwd, async (beforeCommit) => { | |
| + const plan = await readUltragoalPlanUnderLock(cwd, beforeCommit); | |
| const goal = plan.goals.find((candidate) => candidate.id === options.goalId); | |
| if (!goal) throw new UltragoalError(`Unknown ultragoal id: ${options.goalId}`); | |
| if (plan.aggregateCompletion?.status === 'complete' && options.status !== 'complete') { | |
| @@ -1781,7 +1819,7 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| goal.failureReason = assertNonEmpty(options.evidence, '--evidence'); | |
| plan.activeGoalId = goal.id; | |
| plan.updatedAt = now; | |
| - await writePlan(cwd, plan); | |
| + await writePlan(cwd, plan, beforeCommit); | |
| await appendLedger(cwd, { | |
| ts: now, | |
| event: 'goal_blocked', | |
| @@ -1790,7 +1828,7 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| evidence: options.evidence, | |
| codexGoal: options.codexGoal, | |
| message: 'Codex get_goal was unavailable due to a DB/schema/context error; strict completion reconciliation is deferred until get_goal works.', | |
| - }); | |
| + }, beforeCommit); | |
| return plan; | |
| } | |
| if (!snapshot?.available) { | |
| @@ -1811,7 +1849,7 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| goal.updatedAt = now; | |
| plan.activeGoalId = goal.id; | |
| plan.updatedAt = now; | |
| - await writePlan(cwd, plan); | |
| + await writePlan(cwd, plan, beforeCommit); | |
| await appendLedger(cwd, { | |
| ts: now, | |
| event: 'goal_blocked', | |
| @@ -1820,7 +1858,7 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| evidence, | |
| codexGoal: options.codexGoal, | |
| message: 'Native Codex goal status is blocked for the matching objective; recorded a non-terminal goal_blocked checkpoint. Continue work without treating this as complete or failed; re-check get_goal when the blocker clears.', | |
| - }); | |
| + }, beforeCommit); | |
| return plan; | |
| } | |
| if (snapshot.status !== 'complete') { | |
| @@ -1834,7 +1872,7 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| if (safeCompletedAggregateBlocker) goal.failureReason = assertNonEmpty(options.evidence, '--evidence'); | |
| plan.activeGoalId = goal.id; | |
| plan.updatedAt = now; | |
| - await writePlan(cwd, plan); | |
| + await writePlan(cwd, plan, beforeCommit); | |
| await appendLedger(cwd, { | |
| ts: now, | |
| event: 'goal_blocked', | |
| @@ -1845,7 +1883,7 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| message: safeCompletedAggregateBlocker | |
| ? 'Completed aggregate Codex goal is already terminal while the repo-native microgoal remains in progress; recorded a non-terminal safe-recovery blocker to avoid repeating an impossible checkpoint loop.' | |
| : undefined, | |
| - }); | |
| + }, beforeCommit); | |
| return plan; | |
| } | |
| let aggregateCompletion: UltragoalAggregateCompletion | undefined; | |
| @@ -1942,7 +1980,7 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| plan.aggregateCompletion = aggregateCompletion; | |
| if (plan.activeGoalId === goal.id) delete plan.activeGoalId; | |
| plan.updatedAt = now; | |
| - await writePlan(cwd, plan); | |
| + await writePlan(cwd, plan, beforeCommit); | |
| await appendLedger(cwd, { | |
| ts: now, | |
| event: 'goal_completed', | |
| @@ -1952,7 +1990,7 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| codexGoal: options.codexGoal, | |
| qualityGate, | |
| message: 'Active repo-native microgoal completed while reconciling a completed task-scoped aggregate Codex goal snapshot.', | |
| - }); | |
| + }, beforeCommit); | |
| await appendLedger(cwd, { | |
| ts: now, | |
| event: 'aggregate_completed', | |
| @@ -1962,7 +2000,7 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| codexGoal: options.codexGoal, | |
| qualityGate, | |
| message: 'Aggregate ultragoal plan completed via task-scoped Codex goal snapshot; checkpointed active microgoal row was reconciled to complete.', | |
| - }); | |
| + }, beforeCommit); | |
| return plan; | |
| } | |
| goal.status = options.status; | |
| @@ -2008,7 +2046,7 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| if (plan.activeGoalId === goal.id) delete plan.activeGoalId; | |
| } | |
| plan.updatedAt = now; | |
| - await writePlan(cwd, plan); | |
| + await writePlan(cwd, plan, beforeCommit); | |
| const blockerEvent = goal.status === 'needs_user_decision'; | |
| await appendLedger(cwd, { | |
| ts: now, | |
| @@ -2024,7 +2062,7 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| message: blockerEvent | |
| ? `Blocked on repeated external authorization. Required decision: ${goal.requiredExternalDecision}.` | |
| : undefined, | |
| - }); | |
| + }, beforeCommit); | |
| if (options.status === 'complete' && goal.resolvesReviewBlockedGoalId) { | |
| const resolvedParent = plan.goals.find((candidate) => candidate.id === goal.resolvesReviewBlockedGoalId); | |
| if (resolvedParent?.reviewBlockerResolution?.status === 'complete' && resolvedParent.reviewBlockerResolution.resolverGoalId === goal.id) { | |
| @@ -2037,7 +2075,7 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| codexGoal: options.codexGoal, | |
| qualityGate, | |
| message: `Review-blocked final story resolved by ${goal.id}; original failed review remains in prior final_review_failed/goal_review_blocked ledger entries.`, | |
| - }); | |
| + }, beforeCommit); | |
| } | |
| } | |
| if (normalFinalAggregateCompletion) { | |
| @@ -2050,15 +2088,15 @@ export async function checkpointUltragoal(cwd: string, options: CheckpointOption | |
| codexGoal: options.codexGoal, | |
| qualityGate, | |
| message: 'Aggregate ultragoal plan completed with a clean final quality gate.', | |
| - }); | |
| + }, beforeCommit); | |
| } | |
| return plan; | |
| }); | |
| } | |
| export async function recordFinalReviewBlockers(cwd: string, options: RecordFinalReviewBlockersOptions): Promise<{ plan: UltragoalPlan; blockedGoal: UltragoalItem; addedGoal: UltragoalItem }> { | |
| - return withUltragoalMutationLock(cwd, async () => { | |
| - const plan = await readUltragoalPlanUnderLock(cwd); | |
| + return withUltragoalMutationLock(cwd, async (beforeCommit) => { | |
| + const plan = await readUltragoalPlanUnderLock(cwd, beforeCommit); | |
| const goal = plan.goals.find((candidate) => candidate.id === options.goalId); | |
| if (!goal) throw new UltragoalError(`Unknown ultragoal id: ${options.goalId}`); | |
| assertNonEmpty(options.evidence, '--evidence'); | |
| @@ -2102,7 +2140,7 @@ export async function recordFinalReviewBlockers(cwd: string, options: RecordFina | |
| if (plan.activeGoalId === goal.id) delete plan.activeGoalId; | |
| plan.updatedAt = now; | |
| - await writePlan(cwd, plan); | |
| + await writePlan(cwd, plan, beforeCommit); | |
| await appendLedger(cwd, { | |
| ts: now, | |
| event: 'final_review_failed', | |
| @@ -2113,7 +2151,7 @@ export async function recordFinalReviewBlockers(cwd: string, options: RecordFina | |
| message: aggregateMode | |
| ? 'Final aggregate code-review was not clean; blocker story was appended while Codex goal remains active.' | |
| : 'Final per-story code-review was not clean; blocker story was appended and may require an available Codex goal context.', | |
| - }); | |
| + }, beforeCommit); | |
| await appendLedger(cwd, { | |
| ts: now, | |
| event: 'goal_added', | |
| @@ -2121,7 +2159,7 @@ export async function recordFinalReviewBlockers(cwd: string, options: RecordFina | |
| status: addedGoal.status, | |
| evidence: options.evidence, | |
| message: addedGoal.title, | |
| - }); | |
| + }, beforeCommit); | |
| await appendLedger(cwd, { | |
| ts: now, | |
| event: 'goal_review_blocked', | |
| @@ -2129,7 +2167,7 @@ export async function recordFinalReviewBlockers(cwd: string, options: RecordFina | |
| status: goal.status, | |
| evidence: options.evidence, | |
| codexGoal: options.codexGoal, | |
| - }); | |
| + }, beforeCommit); | |
| return { plan, blockedGoal: goal, addedGoal }; | |
| }); | |
| } | |
| diff --git a/src/ultragoal/native-bootstrap.ts b/src/ultragoal/native-bootstrap.ts | |
| new file mode 100644 | |
| index 00000000..0d0079e3 | |
| --- /dev/null | |
| +++ b/src/ultragoal/native-bootstrap.ts | |
| @@ -0,0 +1,1106 @@ | |
| +import { randomUUID } from 'node:crypto'; | |
| +import { constants as fsConstants, type Dirent } from 'node:fs'; | |
| +import { access, chmod, lstat, mkdir, open, readFile, readdir, realpath, rename, unlink, writeFile } from 'node:fs/promises'; | |
| +import { homedir } from 'node:os'; | |
| +import { dirname, isAbsolute, join, relative, resolve } from 'node:path'; | |
| +import { | |
| + acquireCrossProcessFileLock, | |
| + ensureExactDescriptiveLeaderForSession, | |
| + isNativeSubagentAuthorityFenced, | |
| + NATIVE_SUBAGENT_PROVENANCE, | |
| + recordSubagentTurnForSession, | |
| + readSubagentTrackingState, | |
| +} from '../subagents/tracker.js'; | |
| +import type { NativeUltragoalBackend } from '../config/native-ultragoal.js'; | |
| + | |
| +export const NATIVE_ULTRAGOAL_BOOTSTRAP_FILE = 'native-ultragoal-bootstrap.json'; | |
| +export const NATIVE_ASSIGNMENT_DIRECTORY = 'native-assignments'; | |
| +export const DOCUMENTED_HOST_RECEIPT_UNAVAILABLE = 'documented_host_native_executor_receipt_unavailable'; | |
| + | |
| +export type NativeUltragoalBootstrapStatus = | |
| + | 'awaiting-executor' | |
| + | 'ready' | |
| + | 'activating' | |
| + | 'active' | |
| + | 'revoked' | |
| + | 'expired' | |
| + | 'host-receipt-unavailable'; | |
| + | |
| +export interface NativeUltragoalBootstrapState { | |
| + schema_version: 1; | |
| + kind: 'native-ultragoal-bootstrap'; | |
| + generation: string; | |
| + backend: NativeUltragoalBackend; | |
| + status: NativeUltragoalBootstrapStatus; | |
| + session_id: string; | |
| + root_thread_id: string; | |
| + child_agent_id?: string; | |
| + created_at: string; | |
| + updated_at: string; | |
| + expires_at: string; | |
| + reason?: string; | |
| +} | |
| + | |
| +export interface LocalNativeExecutorAssignment { | |
| + schema_version: 1; | |
| + kind: 'native-subagent-assignment'; | |
| + lifecycle: 'active' | 'revoked' | 'expired'; | |
| + root_session_id: string; | |
| + root_thread_id: string; | |
| + child_agent_id: string; | |
| + agent_type: 'executor'; | |
| + allowed_actions: Array<'path-write' | 'bash-write'>; | |
| + path_prefixes: string[]; | |
| + issued_at: string; | |
| + expires_at: string; | |
| + bootstrap_generation: string; | |
| + revoked_at?: string; | |
| + revoke_reason?: string; | |
| +} | |
| + | |
| +export interface HostNativeExecutorReceiptVerifier { | |
| + readonly available: boolean; | |
| + verifyActivationReceipt(input: { | |
| + cwd: string; | |
| + sessionId: string; | |
| + rootThreadId: string; | |
| + }): Promise<{ ok: true; childAgentId: string } | { ok: false; reason: string }>; | |
| +} | |
| + | |
| +const ACTIVE_ASSIGNMENT_SCHEMA_KEYS = new Set([ | |
| + 'schema_version', | |
| + 'kind', | |
| + 'lifecycle', | |
| + 'root_session_id', | |
| + 'root_thread_id', | |
| + 'child_agent_id', | |
| + 'agent_type', | |
| + 'allowed_actions', | |
| + 'path_prefixes', | |
| + 'issued_at', | |
| + 'expires_at', | |
| + 'bootstrap_generation', | |
| +]); | |
| +const MAX_ASSIGNMENT_LIFETIME_MS = 24 * 60 * 60 * 1000; | |
| + | |
| +export const unavailableHostNativeExecutorReceiptVerifier: HostNativeExecutorReceiptVerifier = { | |
| + available: false, | |
| + async verifyActivationReceipt() { | |
| + return { ok: false, reason: DOCUMENTED_HOST_RECEIPT_UNAVAILABLE }; | |
| + }, | |
| +}; | |
| + | |
| +function bootstrapPath(stateDir: string, sessionId: string): string { | |
| + return join(stateDir, 'sessions', sessionId, NATIVE_ULTRAGOAL_BOOTSTRAP_FILE); | |
| +} | |
| + | |
| +async function withNativeUltragoalLifecycleLock<T>( | |
| + stateDir: string, | |
| + sessionId: string, | |
| + operation: () => Promise<T>, | |
| +): Promise<T> { | |
| + const handle = await acquireCrossProcessFileLock(bootstrapPath(stateDir, sessionId), { | |
| + maxAttempts: 200, | |
| + retryMs: 25, | |
| + preserveLiveLocalOwner: true, | |
| + }); | |
| + try { | |
| + const result = await operation(); | |
| + handle.assertOwnership(); | |
| + return result; | |
| + } finally { | |
| + handle.release(); | |
| + } | |
| +} | |
| + | |
| +export function nativeExecutorAssignmentPath(stateDir: string, sessionId: string, childAgentId: string): string { | |
| + return join( | |
| + stateDir, | |
| + 'sessions', | |
| + sessionId, | |
| + NATIVE_ASSIGNMENT_DIRECTORY, | |
| + `${encodeURIComponent(childAgentId)}.json`, | |
| + ); | |
| +} | |
| + | |
| +async function writeJsonAtomic(path: string, value: unknown, mode = 0o600): Promise<void> { | |
| + await mkdir(dirname(path), { recursive: true, mode: 0o700 }); | |
| + const tmpPath = `${path}.tmp.${process.pid}.${Date.now()}.${randomUUID()}`; | |
| + await writeFile(tmpPath, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf-8', mode }); | |
| + try { | |
| + await chmod(tmpPath, mode); | |
| + await rename(tmpPath, path); | |
| + } catch (error) { | |
| + await unlink(tmpPath).catch(() => {}); | |
| + throw error; | |
| + } | |
| +} | |
| + | |
| +async function readJsonObject(path: string): Promise<Record<string, unknown> | null> { | |
| + try { | |
| + const parsed = JSON.parse(await readFile(path, 'utf-8')) as unknown; | |
| + return parsed && typeof parsed === 'object' && !Array.isArray(parsed) | |
| + ? parsed as Record<string, unknown> | |
| + : null; | |
| + } catch { | |
| + return null; | |
| + } | |
| +} | |
| + | |
| +function readString(value: unknown): string { | |
| + return typeof value === 'string' ? value.trim() : ''; | |
| +} | |
| + | |
| +function normalizePrefix(cwd: string, rawPrefix: string): string | null { | |
| + const trimmed = rawPrefix.trim().replace(/\\/g, '/'); | |
| + if (trimmed === '.') return '.'; | |
| + if (!trimmed || isAbsolute(trimmed) || trimmed.includes('\0') || /[$`*?\[\]{}]/.test(trimmed)) return null; | |
| + const absolute = resolve(cwd, trimmed); | |
| + const normalized = relative(cwd, absolute).replace(/\\/g, '/'); | |
| + if (!normalized || normalized === '..' || normalized.startsWith('../')) return null; | |
| + if ( | |
| + normalized === '.git' | |
| + || normalized.startsWith('.git/') | |
| + || normalized === '.omx' | |
| + || normalized.startsWith('.omx/') | |
| + ) return null; | |
| + return normalized; | |
| +} | |
| + | |
| +async function pathPrefixTraversesUnsafeLink(cwd: string, relativePath: string): Promise<boolean> { | |
| + if (relativePath === '.') return false; | |
| + let current = resolve(cwd); | |
| + for (const segment of relativePath.split('/').filter(Boolean)) { | |
| + current = join(current, segment); | |
| + try { | |
| + const metadata = await lstat(current); | |
| + if (metadata.isSymbolicLink() || (!metadata.isDirectory() && metadata.nlink > 1)) return true; | |
| + } catch (error) { | |
| + if ((error as NodeJS.ErrnoException).code === 'ENOENT') continue; | |
| + return true; | |
| + } | |
| + } | |
| + return false; | |
| +} | |
| + | |
| +export async function parseActiveLocalNativeExecutorAssignment( | |
| + raw: Record<string, unknown> | null, | |
| + input: { | |
| + cwd: string; | |
| + sessionId: string; | |
| + rootThreadId: string; | |
| + childAgentId: string; | |
| + agentType: string; | |
| + nowMs?: number; | |
| + }, | |
| +): Promise<(LocalNativeExecutorAssignment & { lifecycle: 'active' }) | null> { | |
| + if (!raw || Object.keys(raw).some((key) => !ACTIVE_ASSIGNMENT_SCHEMA_KEYS.has(key))) return null; | |
| + if ( | |
| + raw.schema_version !== 1 | |
| + || raw.kind !== 'native-subagent-assignment' | |
| + || raw.lifecycle !== 'active' | |
| + || readString(raw.root_session_id) !== input.sessionId | |
| + || readString(raw.root_thread_id) !== input.rootThreadId | |
| + || readString(raw.child_agent_id) !== input.childAgentId | |
| + || readString(raw.agent_type).toLowerCase() !== 'executor' | |
| + || input.agentType.trim().toLowerCase() !== 'executor' | |
| + || !readString(raw.bootstrap_generation) | |
| + ) return null; | |
| + | |
| + const nowMs = input.nowMs ?? Date.now(); | |
| + const issuedAt = Date.parse(readString(raw.issued_at)); | |
| + const expiresAt = Date.parse(readString(raw.expires_at)); | |
| + if ( | |
| + !Number.isFinite(issuedAt) | |
| + || !Number.isFinite(expiresAt) | |
| + || issuedAt > nowMs + 30_000 | |
| + || expiresAt <= nowMs | |
| + || expiresAt <= issuedAt | |
| + || expiresAt - issuedAt > MAX_ASSIGNMENT_LIFETIME_MS | |
| + ) return null; | |
| + | |
| + const allowedActions = Array.isArray(raw.allowed_actions) | |
| + ? raw.allowed_actions.map((value) => readString(value)) | |
| + : []; | |
| + if ( | |
| + allowedActions.length === 0 | |
| + || allowedActions.length > 2 | |
| + || new Set(allowedActions).size !== allowedActions.length | |
| + || allowedActions.some((action) => action !== 'path-write' && action !== 'bash-write') | |
| + ) return null; | |
| + | |
| + const pathPrefixes = Array.isArray(raw.path_prefixes) | |
| + ? raw.path_prefixes.map((value) => normalizePrefix(input.cwd, readString(value))) | |
| + : []; | |
| + if ( | |
| + pathPrefixes.length === 0 | |
| + || pathPrefixes.length > 32 | |
| + || pathPrefixes.some((prefix) => prefix === null) | |
| + || new Set(pathPrefixes).size !== pathPrefixes.length | |
| + ) return null; | |
| + for (const prefix of pathPrefixes as string[]) { | |
| + if (await pathPrefixTraversesUnsafeLink(input.cwd, prefix)) return null; | |
| + } | |
| + | |
| + return { | |
| + schema_version: 1, | |
| + kind: 'native-subagent-assignment', | |
| + lifecycle: 'active', | |
| + root_session_id: input.sessionId, | |
| + root_thread_id: input.rootThreadId, | |
| + child_agent_id: input.childAgentId, | |
| + agent_type: 'executor', | |
| + allowed_actions: allowedActions as Array<'path-write' | 'bash-write'>, | |
| + path_prefixes: pathPrefixes as string[], | |
| + issued_at: new Date(issuedAt).toISOString(), | |
| + expires_at: new Date(expiresAt).toISOString(), | |
| + bootstrap_generation: readString(raw.bootstrap_generation), | |
| + }; | |
| +} | |
| + | |
| +function parseBootstrap(raw: Record<string, unknown> | null): NativeUltragoalBootstrapState | null { | |
| + if ( | |
| + raw?.schema_version !== 1 | |
| + || raw.kind !== 'native-ultragoal-bootstrap' | |
| + || !readString(raw.generation) | |
| + || !['off', 'host', 'local-experimental'].includes(readString(raw.backend)) | |
| + || ![ | |
| + 'awaiting-executor', 'ready', 'activating', 'active', 'revoked', 'expired', 'host-receipt-unavailable', | |
| + ].includes(readString(raw.status)) | |
| + || !readString(raw.session_id) | |
| + || !readString(raw.root_thread_id) | |
| + || !readString(raw.created_at) | |
| + || !readString(raw.updated_at) | |
| + || !readString(raw.expires_at) | |
| + || !Number.isFinite(Date.parse(readString(raw.expires_at))) | |
| + ) return null; | |
| + return raw as unknown as NativeUltragoalBootstrapState; | |
| +} | |
| + | |
| +function parseAssignment(raw: Record<string, unknown> | null): LocalNativeExecutorAssignment | null { | |
| + if ( | |
| + raw?.schema_version !== 1 | |
| + || raw.kind !== 'native-subagent-assignment' | |
| + || !['active', 'revoked', 'expired'].includes(readString(raw.lifecycle)) | |
| + || raw.agent_type !== 'executor' | |
| + || !Array.isArray(raw.allowed_actions) | |
| + || !Array.isArray(raw.path_prefixes) | |
| + || !readString(raw.root_session_id) | |
| + || !readString(raw.root_thread_id) | |
| + || !readString(raw.child_agent_id) | |
| + || !readString(raw.issued_at) | |
| + || !readString(raw.expires_at) | |
| + || !readString(raw.bootstrap_generation) | |
| + || !Number.isFinite(Date.parse(readString(raw.expires_at))) | |
| + ) return null; | |
| + return raw as unknown as LocalNativeExecutorAssignment; | |
| +} | |
| + | |
| +async function beginNativeUltragoalBootstrapUnlocked(input: { | |
| + cwd: string; | |
| + stateDir: string; | |
| + sessionId: string; | |
| + rootThreadId: string; | |
| + backend: NativeUltragoalBackend; | |
| + ttlSeconds: number; | |
| + now?: Date; | |
| +}): Promise<NativeUltragoalBootstrapState> { | |
| + const now = input.now ?? new Date(); | |
| + const cwd = input.cwd; | |
| + ensureExactDescriptiveLeaderForSession(cwd, { | |
| + sessionId: input.sessionId, | |
| + rootThreadId: input.rootThreadId, | |
| + timestamp: now.toISOString(), | |
| + }); | |
| + const existing = parseBootstrap(await readJsonObject(bootstrapPath(input.stateDir, input.sessionId))); | |
| + if ( | |
| + existing | |
| + && existing.session_id === input.sessionId | |
| + && existing.root_thread_id === input.rootThreadId | |
| + && existing.backend === input.backend | |
| + && Date.parse(existing.expires_at) > now.getTime() | |
| + && ['awaiting-executor', 'ready', 'activating', 'active'].includes(existing.status) | |
| + ) return existing; | |
| + | |
| + if (existing?.child_agent_id) { | |
| + const staleAssignmentPath = nativeExecutorAssignmentPath( | |
| + input.stateDir, | |
| + input.sessionId, | |
| + existing.child_agent_id, | |
| + ); | |
| + const staleAssignment = parseAssignment(await readJsonObject(staleAssignmentPath)); | |
| + if (staleAssignment?.lifecycle === 'active') { | |
| + await transitionAssignment( | |
| + staleAssignmentPath, | |
| + staleAssignment, | |
| + Date.parse(existing.expires_at) <= now.getTime() ? 'expired' : 'revoked', | |
| + 'bootstrap_replaced', | |
| + now, | |
| + ); | |
| + } | |
| + } | |
| + | |
| + const state: NativeUltragoalBootstrapState = { | |
| + schema_version: 1, | |
| + kind: 'native-ultragoal-bootstrap', | |
| + generation: randomUUID(), | |
| + backend: input.backend, | |
| + status: input.backend === 'host' ? 'host-receipt-unavailable' : 'awaiting-executor', | |
| + session_id: input.sessionId, | |
| + root_thread_id: input.rootThreadId, | |
| + created_at: now.toISOString(), | |
| + updated_at: now.toISOString(), | |
| + expires_at: new Date(now.getTime() + input.ttlSeconds * 1000).toISOString(), | |
| + ...(input.backend === 'host' ? { reason: DOCUMENTED_HOST_RECEIPT_UNAVAILABLE } : {}), | |
| + }; | |
| + await writeJsonAtomic(bootstrapPath(input.stateDir, input.sessionId), state); | |
| + return state; | |
| +} | |
| + | |
| +export async function beginNativeUltragoalBootstrap( | |
| + input: Parameters<typeof beginNativeUltragoalBootstrapUnlocked>[0], | |
| +): Promise<NativeUltragoalBootstrapState> { | |
| + return withNativeUltragoalLifecycleLock(input.stateDir, input.sessionId, () => ( | |
| + beginNativeUltragoalBootstrapUnlocked(input) | |
| + )); | |
| +} | |
| + | |
| +export async function readNativeUltragoalBootstrap( | |
| + stateDir: string, | |
| + sessionId: string, | |
| +): Promise<NativeUltragoalBootstrapState | null> { | |
| + return parseBootstrap(await readJsonObject(bootstrapPath(stateDir, sessionId))); | |
| +} | |
| + | |
| +export async function validateLocalNativeExecutorIdentity(input: { | |
| + cwd: string; | |
| + sessionId: string; | |
| + rootThreadId: string; | |
| + childAgentId: string; | |
| + receiptNotBefore?: string; | |
| + receiptNotAfter?: string; | |
| +}): Promise<{ ok: true } | { ok: false; reason: string }> { | |
| + const tracking = await readSubagentTrackingState(input.cwd).catch(() => null); | |
| + const session = tracking?.sessions?.[input.sessionId]; | |
| + const child = session?.threads?.[input.childAgentId]; | |
| + if (!session || session.leader_thread_id !== input.rootThreadId) { | |
| + return { ok: false, reason: 'leader_session_mismatch' }; | |
| + } | |
| + if (isNativeSubagentAuthorityFenced(input.cwd, input.childAgentId)) { | |
| + return { ok: false, reason: 'child_authority_fenced' }; | |
| + } | |
| + if (child) { | |
| + if (child.kind !== 'subagent') return { ok: false, reason: 'child_not_tracked' }; | |
| + if (child.reopen_authority_revoked === true) return { ok: false, reason: 'child_authority_revoked' }; | |
| + if (child.status && child.status !== 'available') return { ok: false, reason: `child_${child.status}` }; | |
| + if (child.completed_at) return { ok: false, reason: 'child_completed' }; | |
| + const role = readString(child.mode || child.role).toLowerCase(); | |
| + if (role && role !== 'executor') return { ok: false, reason: 'child_role_mismatch' }; | |
| + const carriesAuthority = child.provenance_kind === NATIVE_SUBAGENT_PROVENANCE | |
| + || child.direct_child_parent_id !== undefined | |
| + || child.direct_child_root_id !== undefined; | |
| + if (carriesAuthority) { | |
| + if (role !== 'executor') return { ok: false, reason: 'child_role_mismatch' }; | |
| + if (child.provenance_kind !== NATIVE_SUBAGENT_PROVENANCE) { | |
| + return { ok: false, reason: 'child_native_authority_unproven' }; | |
| + } | |
| + if (child.direct_child_parent_id !== input.rootThreadId) { | |
| + return { ok: false, reason: 'child_parent_mismatch' }; | |
| + } | |
| + if (child.direct_child_root_id !== input.rootThreadId) { | |
| + return { ok: false, reason: 'child_root_mismatch' }; | |
| + } | |
| + return { ok: true }; | |
| + } | |
| + } | |
| + | |
| + if (!readString(input.receiptNotBefore) || !readString(input.receiptNotAfter)) { | |
| + return { ok: false, reason: 'child_transcript_receipt_unbound' }; | |
| + } | |
| + | |
| + // Codex App v2 currently puts the canonical child id and typed direct-parent | |
| + // receipt in the child rollout session_meta, while SessionStart reports the | |
| + // root session id. local-experimental may consume that same-user receipt for | |
| + // this scoped assignment only; it never promotes the child into persisted | |
| + // reopen authority in subagent-tracking.json. | |
| + const transcriptReceipt = await validateLocalCodexAppExecutorTranscript(input); | |
| + if (transcriptReceipt.ok) return transcriptReceipt; | |
| + return { | |
| + ok: false, | |
| + reason: transcriptReceipt.reason || (child ? 'child_native_authority_unproven' : 'child_not_tracked'), | |
| + }; | |
| +} | |
| + | |
| +const MAX_LOCAL_TRANSCRIPT_FIRST_LINE_BYTES = 64 * 1024; | |
| +const MAX_LOCAL_TRANSCRIPT_SCAN_ENTRIES = 10_000; | |
| +const CANONICAL_CODEX_THREAD_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/; | |
| + | |
| +export type LocalNativeExecutorChildReceiptResolution = | |
| + | { ok: true; childAgentId: string } | |
| + | { ok: false; reason: string }; | |
| + | |
| +function readUniqueStringAlias( | |
| + value: Record<string, unknown> | null, | |
| + aliases: string[], | |
| +): { present: false } | { present: true; valid: false } | { present: true; valid: true; value: string } { | |
| + if (!value) return { present: false }; | |
| + const present = aliases.filter((alias) => Object.prototype.hasOwnProperty.call(value, alias)); | |
| + if (present.length === 0) return { present: false }; | |
| + const values = present.map((alias) => readString(value[alias])); | |
| + if (values.some((candidate) => !candidate) || new Set(values).size !== 1) { | |
| + return { present: true, valid: false }; | |
| + } | |
| + return { present: true, valid: true, value: values[0]! }; | |
| +} | |
| + | |
| +async function readBoundedTranscriptSessionMeta(path: string): Promise<Record<string, unknown> | null> { | |
| + const metadata = await lstat(path).catch(() => null); | |
| + if ( | |
| + !metadata | |
| + || !metadata.isFile() | |
| + || metadata.isSymbolicLink() | |
| + || metadata.nlink !== 1 | |
| + || (metadata.mode & 0o022) !== 0 | |
| + ) return null; | |
| + | |
| + const handle = await open(path, fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0)).catch(() => null); | |
| + if (!handle) return null; | |
| + try { | |
| + const openedMetadata = await handle.stat(); | |
| + if ( | |
| + !openedMetadata.isFile() | |
| + || openedMetadata.nlink !== 1 | |
| + || (openedMetadata.mode & 0o022) !== 0 | |
| + || openedMetadata.dev !== metadata.dev | |
| + || openedMetadata.ino !== metadata.ino | |
| + ) return null; | |
| + const buffer = Buffer.alloc(MAX_LOCAL_TRANSCRIPT_FIRST_LINE_BYTES); | |
| + const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0); | |
| + const newline = buffer.subarray(0, bytesRead).indexOf(0x0a); | |
| + if (newline < 0) return null; | |
| + const line = buffer.subarray(0, newline).toString('utf8').trim(); | |
| + if (!line) return null; | |
| + const parsed = JSON.parse(line) as unknown; | |
| + const finalMetadata = await handle.stat(); | |
| + if ( | |
| + finalMetadata.dev !== openedMetadata.dev | |
| + || finalMetadata.ino !== openedMetadata.ino | |
| + || finalMetadata.mode !== openedMetadata.mode | |
| + || finalMetadata.nlink !== openedMetadata.nlink | |
| + || finalMetadata.size !== openedMetadata.size | |
| + || finalMetadata.mtimeMs !== openedMetadata.mtimeMs | |
| + || finalMetadata.ctimeMs !== openedMetadata.ctimeMs | |
| + ) return null; | |
| + return parsed && typeof parsed === 'object' && !Array.isArray(parsed) | |
| + ? parsed as Record<string, unknown> | |
| + : null; | |
| + } catch { | |
| + return null; | |
| + } finally { | |
| + await handle.close().catch(() => {}); | |
| + } | |
| +} | |
| + | |
| +async function findLocalCodexTranscriptPaths( | |
| + childAgentId: string, | |
| +): Promise<{ paths: string[]; complete: boolean }> { | |
| + const codexHome = process.env.CODEX_HOME?.trim() || join(homedir(), '.codex'); | |
| + const suffix = `-${childAgentId}.jsonl`; | |
| + const matches: string[] = []; | |
| + let visited = 0; | |
| + const pending = [{ path: join(codexHome, 'sessions'), depth: 0 }]; | |
| + | |
| + while (pending.length > 0) { | |
| + const current = pending.shift()!; | |
| + let entries: Dirent[]; | |
| + try { | |
| + entries = await readdir(current.path, { withFileTypes: true }); | |
| + } catch { | |
| + return { paths: matches, complete: false }; | |
| + } | |
| + if (visited + entries.length > MAX_LOCAL_TRANSCRIPT_SCAN_ENTRIES) { | |
| + return { paths: matches, complete: false }; | |
| + } | |
| + visited += entries.length; | |
| + for (const entry of entries) { | |
| + const path = join(current.path, entry.name); | |
| + if (entry.isDirectory() && current.depth < 4) { | |
| + pending.push({ path, depth: current.depth + 1 }); | |
| + } else if (entry.isFile() && entry.name.endsWith(suffix)) { | |
| + matches.push(path); | |
| + if (matches.length > 1) return { paths: matches, complete: true }; | |
| + } | |
| + } | |
| + } | |
| + return { paths: matches, complete: true }; | |
| +} | |
| + | |
| +async function findLiveLocalCodexTranscriptPaths(): Promise<{ paths: string[]; complete: boolean }> { | |
| + const codexHome = process.env.CODEX_HOME?.trim() || join(homedir(), '.codex'); | |
| + const matches: string[] = []; | |
| + let visited = 0; | |
| + const pending = [{ path: join(codexHome, 'sessions'), depth: 0 }]; | |
| + | |
| + while (pending.length > 0) { | |
| + const current = pending.shift()!; | |
| + let entries: Dirent[]; | |
| + try { | |
| + entries = await readdir(current.path, { withFileTypes: true }); | |
| + } catch { | |
| + return { paths: matches, complete: false }; | |
| + } | |
| + if (visited + entries.length > MAX_LOCAL_TRANSCRIPT_SCAN_ENTRIES) { | |
| + return { paths: matches, complete: false }; | |
| + } | |
| + visited += entries.length; | |
| + for (const entry of entries) { | |
| + const path = join(current.path, entry.name); | |
| + if (entry.isDirectory() && current.depth < 4) { | |
| + pending.push({ path, depth: current.depth + 1 }); | |
| + } else if (entry.isFile() && entry.name.startsWith('rollout-') && entry.name.endsWith('.jsonl')) { | |
| + matches.push(path); | |
| + } | |
| + } | |
| + } | |
| + return { paths: matches, complete: true }; | |
| +} | |
| + | |
| +/** | |
| + * Resolve a typed executor's canonical child id from the App-owned rollout. | |
| + * The supplied agent path is only a lookup key; every authority-bearing field | |
| + * is re-attested from one safe, unique, generation-bounded session_meta record. | |
| + */ | |
| +export async function resolveLocalNativeExecutorChildReceipt(input: { | |
| + cwd: string; | |
| + sessionId: string; | |
| + rootThreadId: string; | |
| + agentPath: string; | |
| + receiptNotBefore: string; | |
| + receiptNotAfter: string; | |
| + now?: Date; | |
| +}): Promise<LocalNativeExecutorChildReceiptResolution> { | |
| + const requestedAgentPath = readString(input.agentPath); | |
| + const notBefore = Date.parse(readString(input.receiptNotBefore)); | |
| + const notAfter = Date.parse(readString(input.receiptNotAfter)); | |
| + if ( | |
| + !requestedAgentPath | |
| + || !Number.isFinite(notBefore) | |
| + || !Number.isFinite(notAfter) | |
| + || notAfter < notBefore | |
| + ) return { ok: false, reason: 'child_transcript_receipt_unbound' }; | |
| + | |
| + const canonicalInputCwd = await realpath(input.cwd).catch(() => ''); | |
| + if (!canonicalInputCwd) return { ok: false, reason: 'child_transcript_receipt_cwd_invalid' }; | |
| + const nowMs = (input.now ?? new Date()).getTime(); | |
| + const matches: string[] = []; | |
| + | |
| + const scan = await findLiveLocalCodexTranscriptPaths(); | |
| + if (!scan.complete) return { ok: false, reason: 'child_transcript_receipt_scan_incomplete' }; | |
| + for (const path of scan.paths) { | |
| + const record = await readBoundedTranscriptSessionMeta(path); | |
| + if (!record || record.type !== 'session_meta') continue; | |
| + const payload = record.payload && typeof record.payload === 'object' && !Array.isArray(record.payload) | |
| + ? record.payload as Record<string, unknown> | |
| + : null; | |
| + const source = payload?.source && typeof payload.source === 'object' && !Array.isArray(payload.source) | |
| + ? payload.source as Record<string, unknown> | |
| + : null; | |
| + const subagent = source?.subagent && typeof source.subagent === 'object' && !Array.isArray(source.subagent) | |
| + ? source.subagent as Record<string, unknown> | |
| + : null; | |
| + const spawn = subagent?.thread_spawn && typeof subagent.thread_spawn === 'object' && !Array.isArray(subagent.thread_spawn) | |
| + ? subagent.thread_spawn as Record<string, unknown> | |
| + : null; | |
| + const agentPath = readUniqueStringAlias(spawn, ['agent_path', 'agentPath']); | |
| + if (!agentPath.present || !agentPath.valid || agentPath.value !== requestedAgentPath) continue; | |
| + | |
| + const childId = readUniqueStringAlias(payload, ['id']); | |
| + const sessionClaim = readUniqueStringAlias(payload, ['session_id', 'sessionId']); | |
| + const cwdClaim = readUniqueStringAlias(payload, ['cwd']); | |
| + const parentClaim = readUniqueStringAlias(spawn, ['parent_thread_id', 'parentThreadId']); | |
| + const roleClaim = readUniqueStringAlias(spawn, ['agent_role', 'agentRole', 'agent_type', 'agentType']); | |
| + const timestamp = Date.parse(readString(record.timestamp)); | |
| + const canonicalReceiptCwd = cwdClaim.present && cwdClaim.valid | |
| + ? await realpath(cwdClaim.value).catch(() => '') | |
| + : ''; | |
| + if ( | |
| + !childId.present || !childId.valid || !CANONICAL_CODEX_THREAD_ID.test(childId.value) | |
| + || !path.endsWith(`-${childId.value}.jsonl`) | |
| + || !sessionClaim.present || !sessionClaim.valid | |
| + || (sessionClaim.value !== input.sessionId && sessionClaim.value !== input.rootThreadId) | |
| + || !parentClaim.present || !parentClaim.valid || parentClaim.value !== input.rootThreadId | |
| + || spawn?.depth !== 1 | |
| + || !roleClaim.present || !roleClaim.valid || roleClaim.value.toLowerCase() !== 'executor' | |
| + || !canonicalReceiptCwd || canonicalReceiptCwd !== canonicalInputCwd | |
| + || !Number.isFinite(timestamp) || timestamp < notBefore || timestamp > notAfter | |
| + || timestamp > nowMs + 30_000 | |
| + ) continue; | |
| + matches.push(childId.value); | |
| + if (matches.length > 1) return { ok: false, reason: 'child_transcript_receipt_ambiguous' }; | |
| + } | |
| + | |
| + return matches.length === 1 | |
| + ? { ok: true, childAgentId: matches[0]! } | |
| + : { ok: false, reason: 'child_transcript_receipt_missing' }; | |
| +} | |
| + | |
| +async function validateLocalCodexAppExecutorTranscript(input: { | |
| + cwd: string; | |
| + sessionId: string; | |
| + rootThreadId: string; | |
| + childAgentId: string; | |
| + receiptNotBefore?: string; | |
| + receiptNotAfter?: string; | |
| +}): Promise<{ ok: true } | { ok: false; reason: string }> { | |
| + const scan = await findLocalCodexTranscriptPaths(input.childAgentId); | |
| + if (!scan.complete) return { ok: false, reason: 'child_transcript_receipt_scan_incomplete' }; | |
| + const paths = scan.paths; | |
| + if (paths.length === 0) return { ok: false, reason: 'child_transcript_receipt_missing' }; | |
| + if (paths.length !== 1) return { ok: false, reason: 'child_transcript_receipt_ambiguous' }; | |
| + const record = await readBoundedTranscriptSessionMeta(paths[0]!); | |
| + if (!record || record.type !== 'session_meta') { | |
| + return { ok: false, reason: 'child_transcript_receipt_invalid' }; | |
| + } | |
| + const payload = record.payload && typeof record.payload === 'object' && !Array.isArray(record.payload) | |
| + ? record.payload as Record<string, unknown> | |
| + : null; | |
| + const source = payload?.source && typeof payload.source === 'object' && !Array.isArray(payload.source) | |
| + ? payload.source as Record<string, unknown> | |
| + : null; | |
| + const subagent = source?.subagent && typeof source.subagent === 'object' && !Array.isArray(source.subagent) | |
| + ? source.subagent as Record<string, unknown> | |
| + : null; | |
| + const spawn = subagent?.thread_spawn && typeof subagent.thread_spawn === 'object' && !Array.isArray(subagent.thread_spawn) | |
| + ? subagent.thread_spawn as Record<string, unknown> | |
| + : null; | |
| + const receiptSessionId = readString(payload?.session_id ?? payload?.sessionId); | |
| + const receiptCwd = readString(payload?.cwd); | |
| + const receiptTimestamp = Date.parse(readString(record.timestamp)); | |
| + const receiptNotBefore = Date.parse(readString(input.receiptNotBefore)); | |
| + const receiptNotAfter = Date.parse(readString(input.receiptNotAfter)); | |
| + const [canonicalReceiptCwd, canonicalInputCwd] = receiptCwd | |
| + ? await Promise.all([realpath(receiptCwd).catch(() => ''), realpath(input.cwd).catch(() => '')]) | |
| + : ['', '']; | |
| + const role = readString(spawn?.agent_role ?? spawn?.agentRole ?? spawn?.agent_type ?? spawn?.agentType).toLowerCase(); | |
| + if ( | |
| + readString(payload?.id) !== input.childAgentId | |
| + || (receiptSessionId !== input.sessionId && receiptSessionId !== input.rootThreadId) | |
| + || !Number.isFinite(receiptTimestamp) | |
| + || !Number.isFinite(receiptNotBefore) | |
| + || !Number.isFinite(receiptNotAfter) | |
| + || receiptTimestamp < receiptNotBefore | |
| + || receiptTimestamp > receiptNotAfter | |
| + || receiptTimestamp > Date.now() + 30_000 | |
| + || !canonicalReceiptCwd | |
| + || canonicalReceiptCwd !== canonicalInputCwd | |
| + || readString(spawn?.parent_thread_id ?? spawn?.parentThreadId) !== input.rootThreadId | |
| + || spawn?.depth !== 1 | |
| + || role !== 'executor' | |
| + ) return { ok: false, reason: 'child_transcript_receipt_mismatch' }; | |
| + return { ok: true }; | |
| +} | |
| + | |
| +async function issueLocalNativeExecutorAssignmentUnlocked(input: { | |
| + cwd: string; | |
| + stateDir: string; | |
| + sessionId: string; | |
| + rootThreadId: string; | |
| + childAgentId: string; | |
| + expectedGeneration: string; | |
| + pathPrefixes: string[]; | |
| + ttlSeconds: number; | |
| + allowedActions?: Array<'path-write' | 'bash-write'>; | |
| + now?: Date; | |
| +}): Promise<LocalNativeExecutorAssignment> { | |
| + const now = input.now ?? new Date(); | |
| + if (!Number.isInteger(input.ttlSeconds) || input.ttlSeconds < 30 || input.ttlSeconds > 3600) { | |
| + throw new Error('invalid_native_assignment_ttl'); | |
| + } | |
| + let bootstrap = await readNativeUltragoalBootstrap(input.stateDir, input.sessionId); | |
| + const wasActive = bootstrap?.status === 'active'; | |
| + if ( | |
| + !bootstrap | |
| + || bootstrap.backend !== 'local-experimental' | |
| + || bootstrap.session_id !== input.sessionId | |
| + || bootstrap.root_thread_id !== input.rootThreadId | |
| + || bootstrap.generation !== input.expectedGeneration | |
| + || !['awaiting-executor', 'ready', 'active'].includes(bootstrap.status) | |
| + || Date.parse(bootstrap.expires_at) <= now.getTime() | |
| + ) throw new Error('native_ultragoal_bootstrap_not_ready'); | |
| + | |
| + const tracked = await validateLocalNativeExecutorIdentity({ | |
| + ...input, | |
| + receiptNotBefore: bootstrap.created_at, | |
| + receiptNotAfter: bootstrap.expires_at, | |
| + }); | |
| + if (!tracked.ok) throw new Error(tracked.reason); | |
| + const normalizedPrefixes = input.pathPrefixes.map((prefix) => normalizePrefix(input.cwd, prefix)); | |
| + if ( | |
| + normalizedPrefixes.length === 0 | |
| + || normalizedPrefixes.length > 32 | |
| + || normalizedPrefixes.some((prefix) => prefix === null) | |
| + || new Set(normalizedPrefixes).size !== normalizedPrefixes.length | |
| + ) throw new Error('invalid_native_assignment_path_prefixes'); | |
| + for (const prefix of normalizedPrefixes as string[]) { | |
| + if (await pathPrefixTraversesUnsafeLink(input.cwd, prefix)) { | |
| + throw new Error('invalid_native_assignment_path_prefixes'); | |
| + } | |
| + } | |
| + const actions = input.allowedActions ?? ['path-write', 'bash-write']; | |
| + if ( | |
| + actions.length === 0 | |
| + || actions.length > 2 | |
| + || new Set(actions).size !== actions.length | |
| + || actions.some((action) => action !== 'path-write' && action !== 'bash-write') | |
| + ) throw new Error('invalid_native_assignment_actions'); | |
| + await recordSubagentTurnForSession(input.cwd, { | |
| + sessionId: input.sessionId, | |
| + threadId: input.childAgentId, | |
| + kind: 'subagent', | |
| + leaderThreadId: input.rootThreadId, | |
| + mode: 'executor', | |
| + }); | |
| + const requestedExpiry = now.getTime() + input.ttlSeconds * 1000; | |
| + const expiresAt = wasActive | |
| + ? requestedExpiry | |
| + : Math.min(requestedExpiry, Date.parse(bootstrap.expires_at)); | |
| + const assignment: LocalNativeExecutorAssignment = { | |
| + schema_version: 1, | |
| + kind: 'native-subagent-assignment', | |
| + lifecycle: 'active', | |
| + root_session_id: input.sessionId, | |
| + root_thread_id: input.rootThreadId, | |
| + child_agent_id: input.childAgentId, | |
| + agent_type: 'executor', | |
| + allowed_actions: actions, | |
| + path_prefixes: normalizedPrefixes as string[], | |
| + issued_at: now.toISOString(), | |
| + expires_at: new Date(expiresAt).toISOString(), | |
| + bootstrap_generation: bootstrap.generation, | |
| + }; | |
| + await writeJsonAtomic( | |
| + nativeExecutorAssignmentPath(input.stateDir, input.sessionId, input.childAgentId), | |
| + assignment, | |
| + ); | |
| + await writeJsonAtomic(bootstrapPath(input.stateDir, input.sessionId), { | |
| + ...bootstrap, | |
| + status: wasActive ? 'active' : 'ready', | |
| + child_agent_id: input.childAgentId, | |
| + updated_at: now.toISOString(), | |
| + ...(wasActive ? { expires_at: new Date(expiresAt).toISOString(), reason: undefined } : {}), | |
| + }); | |
| + return assignment; | |
| +} | |
| + | |
| +export async function issueLocalNativeExecutorAssignment( | |
| + input: Parameters<typeof issueLocalNativeExecutorAssignmentUnlocked>[0], | |
| +): Promise<LocalNativeExecutorAssignment> { | |
| + return withNativeUltragoalLifecycleLock(input.stateDir, input.sessionId, () => ( | |
| + issueLocalNativeExecutorAssignmentUnlocked(input) | |
| + )); | |
| +} | |
| + | |
| +export async function nativeExecutorAssignmentFileIsSafe(stateDir: string, path: string): Promise<boolean> { | |
| + try { | |
| + const relativePath = relative(resolve(stateDir), resolve(path)).replace(/\\/g, '/'); | |
| + if (!relativePath || relativePath === '..' || relativePath.startsWith('../')) return false; | |
| + let current = resolve(stateDir); | |
| + for (const segment of relativePath.split('/').filter(Boolean)) { | |
| + current = join(current, segment); | |
| + const metadata = await lstat(current); | |
| + if (metadata.isSymbolicLink()) return false; | |
| + if (current === resolve(path)) { | |
| + if (!metadata.isFile() || metadata.nlink !== 1 || (metadata.mode & 0o022) !== 0 || metadata.size > 64 * 1024) { | |
| + return false; | |
| + } | |
| + } else if (!metadata.isDirectory()) return false; | |
| + } | |
| + await access(path, fsConstants.R_OK); | |
| + return true; | |
| + } catch { | |
| + return false; | |
| + } | |
| +} | |
| + | |
| +async function transitionAssignment( | |
| + path: string, | |
| + assignment: LocalNativeExecutorAssignment, | |
| + lifecycle: 'revoked' | 'expired', | |
| + reason: string, | |
| + now = new Date(), | |
| +): Promise<LocalNativeExecutorAssignment> { | |
| + const next: LocalNativeExecutorAssignment = { | |
| + ...assignment, | |
| + lifecycle, | |
| + revoked_at: now.toISOString(), | |
| + revoke_reason: reason, | |
| + }; | |
| + await writeJsonAtomic(path, next); | |
| + return next; | |
| +} | |
| + | |
| +async function findReadyLocalNativeExecutorAssignmentUnlocked(input: { | |
| + cwd: string; | |
| + stateDir: string; | |
| + sessionId: string; | |
| + rootThreadId: string; | |
| + now?: Date; | |
| +}): Promise<LocalNativeExecutorAssignment | null> { | |
| + const now = input.now ?? new Date(); | |
| + const bootstrap = await readNativeUltragoalBootstrap(input.stateDir, input.sessionId); | |
| + if ( | |
| + !bootstrap | |
| + || bootstrap.backend !== 'local-experimental' | |
| + || bootstrap.root_thread_id !== input.rootThreadId | |
| + || !bootstrap.child_agent_id | |
| + || !['ready', 'activating', 'active'].includes(bootstrap.status) | |
| + || Date.parse(bootstrap.expires_at) <= now.getTime() | |
| + ) return null; | |
| + const path = nativeExecutorAssignmentPath(input.stateDir, input.sessionId, bootstrap.child_agent_id); | |
| + if (!await nativeExecutorAssignmentFileIsSafe(input.stateDir, path)) return null; | |
| + const rawAssignment = await readJsonObject(path); | |
| + const transitionableAssignment = parseAssignment(rawAssignment); | |
| + if ( | |
| + transitionableAssignment?.lifecycle === 'active' | |
| + && Date.parse(transitionableAssignment.expires_at) <= now.getTime() | |
| + ) { | |
| + await transitionAssignment(path, transitionableAssignment, 'expired', 'assignment_expired', now); | |
| + return null; | |
| + } | |
| + const assignment = await parseActiveLocalNativeExecutorAssignment(rawAssignment, { | |
| + cwd: input.cwd, | |
| + sessionId: input.sessionId, | |
| + rootThreadId: input.rootThreadId, | |
| + childAgentId: bootstrap.child_agent_id, | |
| + agentType: 'executor', | |
| + nowMs: now.getTime(), | |
| + }); | |
| + if (!assignment || assignment.bootstrap_generation !== bootstrap.generation) return null; | |
| + const tracked = await validateLocalNativeExecutorIdentity({ | |
| + cwd: input.cwd, | |
| + sessionId: input.sessionId, | |
| + rootThreadId: input.rootThreadId, | |
| + childAgentId: assignment.child_agent_id, | |
| + receiptNotBefore: bootstrap.created_at, | |
| + receiptNotAfter: bootstrap.expires_at, | |
| + }); | |
| + if (!tracked.ok) { | |
| + await transitionAssignment(path, assignment, 'revoked', tracked.reason, now); | |
| + return null; | |
| + } | |
| + return assignment; | |
| +} | |
| + | |
| +export async function findReadyLocalNativeExecutorAssignment( | |
| + input: Parameters<typeof findReadyLocalNativeExecutorAssignmentUnlocked>[0], | |
| +): Promise<LocalNativeExecutorAssignment | null> { | |
| + return withNativeUltragoalLifecycleLock(input.stateDir, input.sessionId, () => ( | |
| + findReadyLocalNativeExecutorAssignmentUnlocked(input) | |
| + )); | |
| +} | |
| + | |
| +async function validateNativeUltragoalActivationReceipt(input: { | |
| + cwd: string; | |
| + stateDir: string; | |
| + sessionId: string; | |
| + childAgentId: string; | |
| + now?: Date; | |
| +}, allowedStatuses: NativeUltragoalBootstrapStatus[]): Promise<{ | |
| + ok: true; | |
| + assignment: LocalNativeExecutorAssignment; | |
| + bootstrap: NativeUltragoalBootstrapState; | |
| +} | { ok: false }> { | |
| + const bootstrap = await readNativeUltragoalBootstrap(input.stateDir, input.sessionId); | |
| + if ( | |
| + !bootstrap | |
| + || !allowedStatuses.includes(bootstrap.status) | |
| + || bootstrap.child_agent_id !== input.childAgentId | |
| + ) return { ok: false }; | |
| + const now = input.now ?? new Date(); | |
| + if (Date.parse(bootstrap.expires_at) <= now.getTime()) return { ok: false }; | |
| + const assignmentPath = nativeExecutorAssignmentPath(input.stateDir, input.sessionId, input.childAgentId); | |
| + if (!await nativeExecutorAssignmentFileIsSafe(input.stateDir, assignmentPath)) return { ok: false }; | |
| + const assignment = await parseActiveLocalNativeExecutorAssignment(await readJsonObject(assignmentPath), { | |
| + cwd: input.cwd, | |
| + sessionId: input.sessionId, | |
| + rootThreadId: bootstrap.root_thread_id, | |
| + childAgentId: input.childAgentId, | |
| + agentType: 'executor', | |
| + nowMs: now.getTime(), | |
| + }); | |
| + if ( | |
| + !assignment | |
| + || assignment.bootstrap_generation !== bootstrap.generation | |
| + ) return { ok: false }; | |
| + const tracked = await validateLocalNativeExecutorIdentity({ | |
| + cwd: input.cwd, | |
| + sessionId: input.sessionId, | |
| + rootThreadId: bootstrap.root_thread_id, | |
| + childAgentId: input.childAgentId, | |
| + receiptNotBefore: bootstrap.created_at, | |
| + receiptNotAfter: bootstrap.expires_at, | |
| + }); | |
| + if (!tracked.ok) return { ok: false }; | |
| + return { ok: true, assignment, bootstrap }; | |
| +} | |
| + | |
| +export async function withNativeUltragoalActivationAuthorization<T>(input: { | |
| + cwd: string; | |
| + stateDir: string; | |
| + sessionId: string; | |
| + childAgentId: string; | |
| + bootstrapStatus?: 'activating' | 'active'; | |
| +}, run: (assertAuthorized: () => Promise<void>) => Promise<T>): Promise< | |
| + { ok: true; value: T } | { ok: false } | |
| +> { | |
| + return withNativeUltragoalLifecycleLock(input.stateDir, input.sessionId, async () => { | |
| + const bootstrapStatus = input.bootstrapStatus ?? 'activating'; | |
| + const initial = await validateNativeUltragoalActivationReceipt(input, [bootstrapStatus]); | |
| + if (!initial.ok) return { ok: false }; | |
| + const expectedGeneration = initial.bootstrap.generation; | |
| + const expectedAssignmentId = initial.assignment.child_agent_id; | |
| + const assertAuthorized = async (): Promise<void> => { | |
| + const current = await validateNativeUltragoalActivationReceipt(input, [bootstrapStatus]); | |
| + if ( | |
| + !current.ok | |
| + || current.bootstrap.generation !== expectedGeneration | |
| + || current.assignment.child_agent_id !== expectedAssignmentId | |
| + ) throw new Error('native_ultragoal_activation_receipt_invalid'); | |
| + }; | |
| + return { ok: true, value: await run(assertAuthorized) }; | |
| + }); | |
| +} | |
| + | |
| +async function prepareNativeUltragoalBootstrapActivationUnlocked(input: { | |
| + cwd: string; | |
| + stateDir: string; | |
| + sessionId: string; | |
| + childAgentId: string; | |
| + now?: Date; | |
| +}): Promise<{ ok: true; assignment: LocalNativeExecutorAssignment } | { ok: false }> { | |
| + const validated = await validateNativeUltragoalActivationReceipt(input, ['ready', 'activating']); | |
| + if (!validated.ok) return validated; | |
| + const { assignment, bootstrap } = validated; | |
| + if (bootstrap.status !== 'activating') { | |
| + await writeJsonAtomic(bootstrapPath(input.stateDir, input.sessionId), { | |
| + ...bootstrap, | |
| + status: 'activating', | |
| + updated_at: (input.now ?? new Date()).toISOString(), | |
| + }); | |
| + } | |
| + return { ok: true, assignment }; | |
| +} | |
| + | |
| +export async function prepareNativeUltragoalBootstrapActivation( | |
| + input: Parameters<typeof prepareNativeUltragoalBootstrapActivationUnlocked>[0], | |
| +): Promise<{ ok: true; assignment: LocalNativeExecutorAssignment } | { ok: false }> { | |
| + return withNativeUltragoalLifecycleLock(input.stateDir, input.sessionId, () => ( | |
| + prepareNativeUltragoalBootstrapActivationUnlocked(input) | |
| + )); | |
| +} | |
| + | |
| +async function markNativeUltragoalBootstrapActiveUnlocked( | |
| + input: Parameters<typeof prepareNativeUltragoalBootstrapActivationUnlocked>[0], | |
| +): Promise<{ ok: true; assignment: LocalNativeExecutorAssignment } | { ok: false }> { | |
| + const validated = await validateNativeUltragoalActivationReceipt(input, ['ready', 'activating', 'active']); | |
| + if (!validated.ok) return validated; | |
| + const { assignment, bootstrap } = validated; | |
| + if (bootstrap.status !== 'active') { | |
| + await writeJsonAtomic(bootstrapPath(input.stateDir, input.sessionId), { | |
| + ...bootstrap, | |
| + status: 'active', | |
| + updated_at: (input.now ?? new Date()).toISOString(), | |
| + }); | |
| + } | |
| + return { ok: true, assignment }; | |
| +} | |
| + | |
| +export async function markNativeUltragoalBootstrapActive( | |
| + input: Parameters<typeof markNativeUltragoalBootstrapActiveUnlocked>[0], | |
| +): Promise<{ ok: true; assignment: LocalNativeExecutorAssignment } | { ok: false }> { | |
| + return withNativeUltragoalLifecycleLock(input.stateDir, input.sessionId, () => ( | |
| + markNativeUltragoalBootstrapActiveUnlocked(input) | |
| + )); | |
| +} | |
| + | |
| +async function revokeLocalNativeExecutorAssignmentUnlocked(input: { | |
| + stateDir: string; | |
| + sessionId: string; | |
| + childAgentId?: string; | |
| + reason: string; | |
| + now?: Date; | |
| +}): Promise<void> { | |
| + const now = input.now ?? new Date(); | |
| + const bootstrap = await readNativeUltragoalBootstrap(input.stateDir, input.sessionId); | |
| + const childAgentId = input.childAgentId ?? bootstrap?.child_agent_id; | |
| + if (childAgentId) { | |
| + const path = nativeExecutorAssignmentPath(input.stateDir, input.sessionId, childAgentId); | |
| + const assignment = parseAssignment(await readJsonObject(path)); | |
| + if (assignment?.lifecycle === 'active') { | |
| + await transitionAssignment(path, assignment, 'revoked', input.reason, now); | |
| + } | |
| + } | |
| + if (bootstrap) { | |
| + await writeJsonAtomic(bootstrapPath(input.stateDir, input.sessionId), { | |
| + ...bootstrap, | |
| + status: 'revoked', | |
| + updated_at: now.toISOString(), | |
| + reason: input.reason, | |
| + }); | |
| + } | |
| +} | |
| + | |
| +export async function revokeLocalNativeExecutorAssignment( | |
| + input: Parameters<typeof revokeLocalNativeExecutorAssignmentUnlocked>[0], | |
| +): Promise<void> { | |
| + return withNativeUltragoalLifecycleLock(input.stateDir, input.sessionId, () => ( | |
| + revokeLocalNativeExecutorAssignmentUnlocked(input) | |
| + )); | |
| +} | |
| + | |
| +async function reconcileLocalNativeExecutorAssignmentsUnlocked(input: { | |
| + cwd: string; | |
| + stateDir: string; | |
| + sessionId: string; | |
| + rootThreadId: string; | |
| + now?: Date; | |
| +}): Promise<{ ready: boolean; childAgentId?: string }> { | |
| + const bootstrap = await readNativeUltragoalBootstrap(input.stateDir, input.sessionId); | |
| + const directory = join(input.stateDir, 'sessions', input.sessionId, NATIVE_ASSIGNMENT_DIRECTORY); | |
| + const entries = await readdir(directory).catch(() => []); | |
| + for (const entry of entries) { | |
| + if (!entry.endsWith('.json')) continue; | |
| + const path = join(directory, entry); | |
| + const assignment = parseAssignment(await readJsonObject(path)); | |
| + if (!assignment || assignment.lifecycle !== 'active') continue; | |
| + if ( | |
| + !bootstrap | |
| + || assignment.bootstrap_generation !== bootstrap.generation | |
| + || assignment.child_agent_id !== bootstrap.child_agent_id | |
| + ) { | |
| + await transitionAssignment(path, assignment, 'revoked', 'bootstrap_replaced', input.now); | |
| + continue; | |
| + } | |
| + const tracked = await validateLocalNativeExecutorIdentity({ | |
| + cwd: input.cwd, | |
| + sessionId: input.sessionId, | |
| + rootThreadId: input.rootThreadId, | |
| + childAgentId: assignment.child_agent_id, | |
| + receiptNotBefore: bootstrap.created_at, | |
| + receiptNotAfter: bootstrap.expires_at, | |
| + }); | |
| + if (!tracked.ok) await transitionAssignment(path, assignment, 'revoked', tracked.reason, input.now); | |
| + } | |
| + const assignment = await findReadyLocalNativeExecutorAssignmentUnlocked(input); | |
| + return assignment | |
| + ? { ready: true, childAgentId: assignment.child_agent_id } | |
| + : { ready: false }; | |
| +} | |
| + | |
| +export async function reconcileLocalNativeExecutorAssignments( | |
| + input: Parameters<typeof reconcileLocalNativeExecutorAssignmentsUnlocked>[0], | |
| +): Promise<{ ready: boolean; childAgentId?: string }> { | |
| + return withNativeUltragoalLifecycleLock(input.stateDir, input.sessionId, () => ( | |
| + reconcileLocalNativeExecutorAssignmentsUnlocked(input) | |
| + )); | |
| +} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment