Skip to content

feat(app-server): render live Spine agent trees - #3

Open
kabxx wants to merge 209 commits into
GhabiX:mainfrom
kabxx:feat/spine-desktop-live-tree
Open

feat(app-server): render live Spine agent trees#3
kabxx wants to merge 209 commits into
GhabiX:mainfrom
kabxx:feat/spine-desktop-live-tree

Conversation

@kabxx

@kabxx kabxx commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

This change adds a live-only Spine tree card for Codex Desktop.
It uses the existing MCP App/widget surface and keeps the feature isolated behind
the process-level CODEX_SPINE_APP_UI environment variable.

Motivation

Spine events already describe the active reasoning tree, nested agent work, and
spawn progress, but Desktop did not have a live per-turn visual surface for that
state. The implementation provides a stable card identity and structured widget
content without persisting the internal UI carrier into rollout history.

What changed

Core Spine spawn lifecycle

  • Emits explicit child progress through PendingInit, Running, and terminal
    states when the child has a real observable lifecycle.
  • Preserves the initial status returned by child creation instead of synthesizing
    Running, so pending and fast-terminal children remain truthful.
  • Preserves an observed child Interrupted or Shutdown status when the final
    spawn receipt collapses both cases into Aborted; only results without a more
    specific observed status use Interrupted as the conservative fallback.
  • Preserves typed spawn receipts, terminal outcomes, failure information, and
    memory salvage.
  • Serializes progress emission so concurrent child updates remain ordered.
  • Retains fallback status recovery when a child status watch closes unexpectedly.
  • Adds coverage for delayed children, fast-terminal children, partial start
    failures, capacity rejection, nested spawn, receipt ordering, and teardown.

App-server live UI state

  • Adds thread-local and cross-thread Spine UI state management.
  • Renders spawn calls attached to the hidden root epoch as top-level agent rows,
    while preserving normal task nesting and settled-result reconciliation.
  • Renders settled agents at their result-node positions so live agent rows,
    restored results, and ordinary tasks share one chronological tree order.
  • Keeps cumulative parent trees across turns while isolating active turn state.
  • Tracks parent/child routes, listener generations, revisions, rollback, reconnect,
    pending completion, and stale event invalidation.
  • Rotates route generations when a child listener is replaced, removes the stale
    subtree, rejects queued projections from the old listener, and accepts the new
    listener's state.
  • Serializes listener-exit and rollback route cleanup against listener replacement
    using the authoritative thread generation, so a stale listener cannot remove
    the incoming or outgoing routes needed by its replacement.
  • Validates both ThreadState Arc identity and listener generation during route
    cleanup, so an unloaded listener cannot delete routes created by a reloaded
    instance that reused the same generation number.
  • Forwards child subtrees to the correct parent turn and filters inherited nodes.
  • Falls back to the cumulative parent tree when a route is registered before the
    current turn's first snapshot, preventing inherited history from appearing
    inside a child agent row.
  • Uses revision and snapshot equality checks to suppress redundant widget updates.
  • Terminalizes an active carrier as failed if its Core event stream exits
    unexpectedly, without affecting normal listener replacement.
  • Terminalizes incomplete child agents when a parent turn is interrupted or its
    event stream fails, preserving terminal interrupted or error state in the
    cumulative tree so later live cards do not inherit running forever.
  • Treats an ambiguous restored Aborted result as Interrupted without
    overwriting an already observed Shutdown or any other terminal agent state.

Internal MCP App surface

  • Exposes the internal server
    __codex_internal_spine_tree_ui__ only when
    CODEX_SPINE_APP_UI=1, true, or on.
  • Provides the read-only spine_tree tool and the embedded
    ui://spine/tree.html resource.
  • Keeps the HTML resource compiled into the binary and declared as Bazel
    compile-time data.
  • Uses a stable spine-ui-{turn_id} carrier identity so Desktop can upsert live
    updates for one turn.
  • Reports completed turns as Completed and aborted or listener-failed turns as
    Failed, with a structured terminal outcome and reason.
  • Applies completed, aborted, and failed outcomes to the top-level tree so a
    terminal carrier cannot leave its former active node displayed as Current.
  • Renders an Aborted result without live status metadata as Interrupted, so
    event ordering and process restart cannot manufacture a false Stopped state.
  • Preserves normal MCP routing when the feature is disabled or when a request is
    not for the reserved internal server/tool/resource.
  • Rejects configured MCP servers that collide with the reserved internal name.
  • Keeps the carrier live-only and out of rollout/thread history.

Code Mode and exec integration

Code Mode can invoke Spine controls inside an outer exec call. Those controls
arrive as a CustomToolCallOutput carrier rather than as a direct
FunctionCall. Previously, the app-server activation check saw only direct
function calls, so a nested spine.close could update Core state without opening
the live UI card for that turn.

The activation check now:

  • Preserves direct spine.open, spine.next, spine.close, and spine.spawn
    behavior.
  • Recognizes only the host-reserved spine.code_mode.output.v1 marker.
  • Requires a text body with the matching v1 schema.
  • Activates only when nested_spine_calls contains open, next, close, or
    spawn.
  • Ignores ordinary exec output, trim-only carriers, unmarked output, wrong
    schema versions, malformed JSON, and non-text bodies.

This parser is intentionally local to the app-server UI layer. Core remains the
source of truth for carrier validation and execution; the app-server check only
decides whether the live card should be mounted. Client-side injection of the
reserved carrier marker remains rejected.

File-by-file change map

The commit changes 21 files. The table below separates Base Codex integration
points from Spine-owned implementation and test files so future rebases can
review the real conflict surface directly.

File Change Purpose and impact
codex-rs/app-server/BUILD.bazel +1 Adds the embedded tree.html as compile-time data.
codex-rs/app-server/src/lib.rs +1 Registers the spine_ui module.
codex-rs/app-server/src/message_processor.rs +1 Shares ThreadStateManager with the MCP processor.
codex-rs/app-server/src/request_processors/mcp_processor.rs +23/-2 Adds the smallest MCP inventory/resource/tool interception points; non-matching requests fall through unchanged.
codex-rs/app-server/src/request_processors/thread_lifecycle.rs +80/-2 Hooks listener generation, event phases, commands, completion, failure-aware exit cleanup, and connection mounting into the Spine runtime.
codex-rs/app-server/src/spine_ui.rs +407 Defines per-thread Spine state, protocol constants, snapshots, revisions, spawn state, terminal handling, and exports the tree-affecting item check.
codex-rs/app-server/src/spine_ui/listener.rs +713 Converts Core events into state updates and live carrier notifications, including parent/child forwarding, terminal outcomes, generation-serialized route cleanup, abnormal-listener cleanup, and incomplete-agent terminalization.
codex-rs/app-server/src/spine_ui/mcp.rs +526 Implements the internal virtual MCP server, widget resource, structured terminal outcomes, stable carrier identity, and versioned Code Mode activation parser.
codex-rs/app-server/src/spine_ui/render.rs +153 Serializes Spine snapshots, spawn calls, and agent subtrees, and maps hidden root-epoch parents to the widget root.
codex-rs/app-server/src/spine_ui/tree.html +665 Implements the embedded live tree widget, revision filtering, chronological task/agent rendering, terminal top-level state, recursive subtrees, and Host height reporting.
codex-rs/app-server/src/spine_ui_tests.rs +729 Tests state/render contracts, settled-agent ordering, root-level and nested agents, stable identity, exact and fallback terminal outcomes, top-level terminal rendering, incomplete-agent terminalization, deduplication, generation behavior, and direct/Code Mode activation filtering.
codex-rs/app-server/src/thread_state.rs +17/-1 Adds the Spine runtime field, internal listener command, generation-aware lifecycle hooks, and cleanup integration.
codex-rs/app-server/src/thread_state_spine_ui.rs +412 Owns per-thread active/pending/terminal state, cumulative snapshots, route baselines, revisions, reconnect, rollback, exactly-once completion, and terminalization of incomplete agents.
codex-rs/app-server/src/thread_state_spine_ui_manager.rs +558 Owns cross-thread parent/child routes, baseline filtering, queued revision coalescing, child-listener route rotation, generation and Arc-identity checks, and invalidation.
codex-rs/app-server/src/thread_state_spine_ui_tests.rs +1985 Covers the thread state machine, parent/child routing, cumulative-baseline fallback, deterministic listener replacement/exit and unload/reload ABA interleavings, listener failure, aborted turns, carry-forward terminalization, rollback, reconnect, terminal ordering, and Code Mode activation timing.
codex-rs/app-server/tests/suite/v2/mod.rs +2 Registers the two Spine UI app-server integration suites.
codex-rs/app-server/tests/suite/v2/spine_ui_live.rs +352 Verifies live-only item notifications, zero-rollout behavior, resume, and parent/child structured content.
codex-rs/app-server/tests/suite/v2/spine_ui_mcp.rs +393 Verifies On/Off isolation, internal MCP inventory/resource/tool behavior, empty arguments, and reserved-name conflicts.
codex-rs/core/src/spine/spawn.rs +167/-109 Adds ordered truthful child progress emission, initial- and terminal-status preservation, status watching, typed receipts, and failure/capacity handling.
codex-rs/core/src/spine/spawn_tests.rs +98/-29 Tests initial- and terminal-status preservation, conservative aborted fallback, ordered observation, fast-terminal behavior, receipt outcomes, salvage, admission, and spawn coordination.
codex-rs/core/tests/suite/spine_spawn.rs +110 Adds delayed-child integration coverage for Running progress without requiring a child Spine node.

The 10 modified Base files are the integration conflict surface; the 11 added
files are Spine-owned implementation or tests. No TUI or app-server protocol
crate files are changed.

Compatibility and scope

  • No app-server protocol types were added or changed.
  • No Desktop or TUI renderer changes are required.
  • Ordinary MCP payloads, authentication, routing, and execution are unchanged.
  • CODEX_SPINE_APP_UI=off or an unset variable leaves the existing behavior unchanged.
  • No rollout data is written for the internal UI carrier.
  • Parent cards intentionally do not wait for a late child terminal projection.
  • Cold resume rebuilds the cumulative Core tree rather than replaying old live
    carrier items.

Tests and checks

Passed on the current commit:

  • cargo +1.95.0 test -p codex-app-server --lib spine_ui: 56 passed.
  • cargo +1.95.0 test -p codex-app-server --test all spine_ui: 5 passed.
  • cargo +1.95.0 test -p codex-core spine::spawn: 20 passed.
  • PowerShell: $env:RUST_MIN_STACK='16777216'; cargo +1.95.0 test -p codex-core --test all spine_spawn -- --nocapture --test-threads=1: 11 passed.
  • cargo +1.95.0 check -p codex-app-server -p codex-core: passed.
  • cargo +1.95.0 clippy -p codex-app-server --lib --no-deps: passed.
  • just fmt: passed.
  • git diff --check: passed.
  • Windows Rust 1.95.0 produced codex-spine-83d7c2e9.exe, an x86_64 debug
    binary on the current commit
    (SHA-256: A57144E50D7D63ADC854F5A274AA04E8E05803F697C4AA073B1F06DE0A66A761).
  • macOS cargo +1.95.0 build --manifest-path codex-rs/Cargo.toml --release -p codex-cli:
    passed on the current commit and produced an arm64 release binary
    (SHA-256: 41460f804fa0e3ab8258ba4afa7ce3fc093054a80359c74e1f5386da6035e95b).
  • The macOS GUI/TUI launchers pass bash -n; their binary-hash-isolated
    CODEX_SQLITE_HOME initializes cleanly with all SQLite integrity checks passing.
  • New parser coverage includes direct controls, valid carriers, trim-only,
    unmarked, wrong schema, malformed, and non-text cases.
  • New state coverage verifies that a Code Mode carrier activates the turn before
    its following snapshot, while a trim carrier only seeds cumulative state.
  • Lifecycle coverage verifies stale child projection rejection, replacement-state
    forwarding, failed listener terminalization, aborted-turn outcomes, and truthful
    fast-terminal child status.
  • Status coverage verifies that live Interrupted and Shutdown remain distinct,
    while an Aborted result without live status metadata falls back to
    Interrupted and cannot overwrite a known terminal state.
  • Render coverage verifies root-epoch agents are emitted at the widget root and
    settled agents replace their result rows in place without duplication or
    chronological reordering.
  • Route coverage verifies inherited parent history is filtered even when spawn
    progress arrives before the current turn's first tree snapshot.

efrazer-oai and others added 30 commits July 9, 2026 14:48
# Summary

GitHub's latest-release endpoint can return compact, single-line JSON.
The standalone installer treated release metadata as line-oriented text,
so those responses could make asset lookup fail even though the
requested assets were present.

The regression was introduced by
[#31056](openai/codex#31056). That change reused
the `/releases/latest` metadata response for both version resolution and
asset lookup, exposing the existing formatting-sensitive asset parser to
compact responses from that endpoint.

This change parses the release metadata once with a one-pass POSIX awk
scanner. The scanner tracks JSON strings and nesting, extracts the root
release tag plus direct asset name/digest pairs, and produces the same
result regardless of whitespace or object field order. It uses POSIX
`fold` to bound awk record sizes so compact responses stay fast across
awk implementations.

Fixes #31520.

## Changes

- replace line-oriented release metadata matching with structure-aware
parsing
- reuse the parsed metadata for latest-version and asset-digest lookup
- add regression coverage for compact JSON, reordered fields, nested
decoys, and JSON-looking release text

## Design decisions

- Keep the installer dependency-free by using standard POSIX tools
already required by the shell installer.
- Parse only the GitHub release fields the installer consumes, in one
pass, instead of vendoring a general JSON library.
- Preserve asset-object boundaries so nested or string-encoded `name`
and `digest` fields cannot be mistaken for release assets.

## Testing

- Tests: focused installer suite locally and on Linux.
- Smoke tests: real pretty and compact GitHub release metadata,
latest-release resolution, and checksum-asset selection.
- Portability: macOS awk plus Linux gawk, mawk, and nawk.
- Stress coverage: randomized formatting and field order, adversarial
nested/string content, and a synthetic 2,000-asset compact response.
Installer needs to symlink code-mode-host next to codex on install.
…ernal process (#31899)

## Why

Not every Codex distribution currently includes the
`codex-code-mode-host` companion binary. Enabling the process-host
feature should not make code mode unavailable on those surfaces while
packaging support is being completed.

## What changed

- Fall back to an in-process code-mode session only when spawning the
companion binary returns `io::ErrorKind::NotFound`.
- Keep permission, handshake, timeout, and other host failures visible
instead of silently falling back.
- Store the provider's owned-process/in-process choice as one
enum-backed state so later sessions reuse the fallback decision.
- Preserve the underlying spawn `io::Error` while retaining the host
path in the displayed error.
- Update provider, `CodeModeService`, and end-to-end coverage to verify
successful fallback execution.

## Test plan

- `just test -p codex-code-mode`
- `just test -p codex-core missing_process_host`
## Summary

- Revert c27a909508e09105c80cc162e250e623a96a8f82 ("Update auto review
prompting") in full on the `release/0.144` branch.
- Restore the prior Guardian policy template, review request layout, and
tool specifications.
- Restore the corresponding Guardian tests and snapshots.

## Why

The auto-review prompting update needs to be rolled back from the 0.144
release line. This PR contains the direct, conflict-free Git cherry-pick
with no additional product changes.

## Validation

- `just fmt`
- `just test -p codex-core guardian::tests` (58 passed)
- `just test -p codex-core` (2,806 passed; 137 environment-sensitive
failures caused by sandbox restrictions on local ports and process
operations)
- `git show --check`
- Add an `auto_review.policy` field to model catalog messages.
- Use the selected Guardian model's catalog policy for review-session instructions, while preserving the precedence of `guardian_policy_config` and falling back to the built-in policy when neither is present.
- Preserve auto-review messages when model instruction overrides remove catalog instruction templates.

- Cover configured-policy precedence, explicit empty catalog policies, catalog-message preservation, and propagation of the catalog policy into a prewarmed Guardian session.

GitOrigin-RevId: 26b61ae2958ea8325a64834dcf91f47e140d74b3
## Summary

Cherry-picks the seven commits from
[openai/codex-internal#1942](openai/codex-internal#1942)
onto `release/0.144`.

This backport:

1. enables dangerous-command detection in danger-full-access mode
2. expands literal Bash parsing so additional forced `rm` forms are
detected
3. returns a specific rejection reason to the model when a dangerous
command is denied

The commits applied without conflicts or release-only changes. `git
range-diff` confirms every cherry-picked patch matches the source PR
exactly.

## Validation

- `just test -p codex-shell-command` (141 passed)
- `just test -p codex-core exec_policy` (107 passed)
- `just test -p codex-core` outside the sandbox (2,947 passed; 4
unrelated environment/setup failures)
- 3 RMCP tests could not locate the test-only `test_stdio_server` binary
- 1 user-shell environment test observed the tool runner's mandated
`CODEX_SANDBOX_NETWORK_DISABLED=1`
- `just fix -p codex-core`
- `just fix -p codex-shell-command`
- `just fmt`
- `git diff --check`

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a live-only Spine tree card for Codex Desktop by introducing an internal, env-gated MCP “Spine UI” surface (__codex_internal_spine_tree_ui__) and wiring app-server thread/listener state so live Spine tree/spawn progress can be rendered per turn without persisting the UI carrier into rollout history. It also refines Core Spine spawn progress semantics (more truthful, ordered lifecycle reporting) and adds extensive unit/integration coverage.

Changes:

  • Core: reworks spine.spawn progress/receipt handling to preserve initial/observed statuses, serialize progress emission, and improve terminal-status handling; adds new spawn-progress integration coverage.
  • App-server: adds Spine UI state tracking, listener lifecycle hooks, and an internal MCP server/tool/resource that serves a compiled-in tree.html widget and emits live-only item notifications.
  • Tests: adds dedicated app-server suite tests for live-only behavior and MCP surface isolation/collisions, plus expanded Core spawn tests.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
codex-rs/app-server/BUILD.bazel Adds embedded tree.html as compile-time data for Bazel builds.
codex-rs/app-server/src/lib.rs Registers the new spine_ui module.
codex-rs/app-server/src/message_processor.rs Passes ThreadStateManager to MCP processor to enable Spine UI interception/state.
codex-rs/app-server/src/request_processors/mcp_processor.rs Intercepts reserved internal MCP resource/tool and injects reserved server/status handling.
codex-rs/app-server/src/request_processors/thread_lifecycle.rs Hooks Spine UI lifecycle into listener start/event/exit/failure and command handling.
codex-rs/app-server/src/spine_ui.rs Defines Spine UI state model, revisioning, activation detection, and helpers.
codex-rs/app-server/src/spine_ui/listener.rs Converts Core events + manager commands into Spine UI state updates and live notifications.
codex-rs/app-server/src/spine_ui/mcp.rs Implements internal MCP server/tool/resource, gating, reserved-name checks, and activation parsing.
codex-rs/app-server/src/spine_ui/render.rs Serializes Spine UI state into the structured widget payload schema.
codex-rs/app-server/src/spine_ui/tree.html Embedded widget UI: renders live Spine task/agent tree and handles updates.
codex-rs/app-server/src/spine_ui_tests.rs Unit tests for Spine UI activation, rendering, terminal behavior, dedupe, and invariants.
codex-rs/app-server/src/thread_state.rs Adds Spine UI runtime fields/commands and clears transient UI state on listener changes.
codex-rs/app-server/src/thread_state_spine_ui.rs Per-thread Spine UI runtime: active/pending/terminal state, mounting, revisions, completion.
codex-rs/app-server/src/thread_state_spine_ui_manager.rs Cross-thread routing/forwarding, generation handling, and route cleanup logic.
codex-rs/app-server/src/thread_state_spine_ui_tests.rs Large state-machine test suite covering routing, listener replacement, ABA, rollback/reconnect, etc.
codex-rs/app-server/tests/suite/v2/mod.rs Registers new Spine UI integration test modules.
codex-rs/app-server/tests/suite/v2/spine_ui_live.rs Integration coverage for live-only item notifications + cold-resume rebuild behavior.
codex-rs/app-server/tests/suite/v2/spine_ui_mcp.rs Integration coverage for MCP surface enable/disable, resource/tool behavior, and name collisions.
codex-rs/core/src/spine/spawn.rs Implements ordered spawn progress emitter + preserves initial/terminal statuses and receipt mapping.
codex-rs/core/src/spine/spawn_tests.rs Updates unit tests for terminal status watching and aborted-status mapping.
codex-rs/core/tests/suite/spine_spawn.rs Adds integration test for Running progress reporting without child Spine nodes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +548 to 553
progress_emitter.emit_initial().await;
for (ordinal, _, _, status) in &live {
progress_emitter
.emit_status(*ordinal, flat_tasks[*ordinal].0, status.clone())
.await;
}
Comment on lines +336 to +358
function validTreePayload(value) {
const pending = [{ value, isSubtree: false }];
while (pending.length) {
const candidate = pending.pop();
const current = candidate.value;
if (!current || typeof current !== "object" || current.schemaVersion !== 1) return false;
if (!Number.isSafeInteger(current.uiRevision) || current.uiRevision < 0) return false;
if (candidate.isSubtree && typeof current.threadId !== "string") return false;
if (!current.snapshot || typeof current.snapshot !== "object" || !Array.isArray(current.snapshot.nodes)) return false;
if (current.snapshot.nodes.some((node) => !node || typeof node !== "object"
|| typeof node.nodeId !== "string"
|| (node.summary != null && typeof node.summary !== "string")
|| (node.spawnOutcome != null && !["completed", "errored", "aborted"].includes(node.spawnOutcome)))) return false;
const spawnCalls = current.spawnCalls || [];
const agentSubtrees = current.agentSubtrees || [];
if (!Array.isArray(spawnCalls) || !Array.isArray(agentSubtrees)) return false;
if (spawnCalls.some((call) => !call || typeof call !== "object" || !Array.isArray(call.tasks)
|| call.tasks.some((task) => !task || typeof task !== "object"
|| !["pending", "running", "interrupted", "completed", "error", "shutdown", "not_found"].includes(task.status)))) return false;
agentSubtrees.forEach((subtree) => pending.push({ value: subtree, isSubtree: true }));
}
return true;
}
@jsmikelin

Copy link
Copy Markdown

This is actually PR #3 (feat: render live Spine agent trees) — looks like it was filed as an issue. The implementation is complete with the CODEX_SPINE_APP_UI env-gated live Spine tree card. Flagging for the maintainers (@GhabiX @Camsyn) to review and merge; merging will auto-close this. Happy to help with review or testing on Windows/macOS if useful.

@GhabiX
GhabiX force-pushed the main branch 2 times, most recently from 6c92152 to 2cc9644 Compare August 20, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants