Files
galaxy/specs/codex-warp-plugin/TECH.md
T

9.7 KiB

Codex Warp Plugin Tech Spec

Context

Warp already supports CLI agent status via structured OSC 777 plugin notifications. Codex is special because it previously only had native OSC 9 desktop notifications with plain text. This branch adds a feature-flagged Codex Warp plugin path while preserving OSC 9 as fallback. The key invariant changes:

  • listener.is_some() no longer means “structured plugin connected” for Codex. It can mean OSC 9 fallback listener.
  • Structured Codex plugin events must win over OSC 9 once seen. Otherwise Warp would process both OSC 777 and OSC 9 and notify twice.
  • UI that needs trustworthy state should use CLIAgentSession::supports_rich_status(), not listener.is_some(). Relevant code:
  • app/src/terminal/cli_agent_sessions/listener/mod.rs:14CLIAgentSessionHandler now mutably parses notifications so Codex can remember structured-plugin activation.
  • app/src/terminal/cli_agent_sessions/listener/mod.rs:87CodexSessionHandler parses both OSC 9 fallback and structured OSC 777 Codex events.
  • app/src/terminal/cli_agent_sessions/mod.rs:155has_structured_plugin() distinguishes structured OSC 777 from Codex OSC 9 fallback.
  • app/src/terminal/cli_agent_sessions/mod.rs:164supports_rich_status() centralizes the “safe to show fine-grained status” check.
  • app/src/terminal/cli_agent_sessions/plugin_manager/codex.rs:17 — Codex plugin constants: marketplace, user plugin key, platform plugin key, config dirs, min version.
  • app/src/terminal/cli_agent_sessions/plugin_manager/codex.rs:57 — plugin manager gates install/update/inspection on FeatureFlag::CodexPlugin.
  • app/src/terminal/cli_agent_sessions/plugin_manager/claude.rs:17 — Claude manager structure copied for Codex, with CLI/config differences.
  • app/src/terminal/cli_agent_sessions/plugin_manager/mod.rs:222 — Codex manager now receives shell path/type/PATH like Claude/Gemini.
  • app/src/terminal/view.rs:11680 — command-detected Codex still proactively registers a listener, but without seeding a plugin version.
  • app/src/terminal/view.rs:12753 — OSC 777 Codex events are ignored when CodexPlugin is disabled.
  • app/src/terminal/view.rs:12825 — listener registration without SessionStart never seeds a plugin version. Codex remains OSC 9 fallback until a real structured plugin event reports version.
  • crates/warp_features/src/lib.rs:789 and app/src/features.rs:480 — new CodexPlugin flag is wired into shared/app feature plumbing.

Proposed changes

1. Add CodexPlugin flag

Add FeatureFlag::CodexPlugin and enable it for dogfood builds. When disabled:

  • Codex keeps the existing native OSC 9 behavior.
  • structured Codex events are ignored.
  • install instructions remain the old “enable native Codex notifications” steps.
  • auto-install/update are disabled. When enabled:
  • Warp can install/update warp@codex-warp.
  • structured OSC 777 events unlock rich status.
  • OSC 9 remains fallback for older Codex clients.

This means we can test this before releasing into the wild.

2. Make Codex listener protocol-aware

CLIAgentSessionHandler::try_parse now takes &mut self. Codex uses that state to remember when it has seen a structured Codex plugin event. Codex parsing rules:

  • Try parse_event(title, body) first.
  • If it is a Codex structured event and CodexPlugin is enabled, mark structured_plugin_active = true and forward it.
  • If it is a Codex structured event but the flag is disabled, drop it and leave OSC 9 fallback active.
  • If it is a structured event for another agent, drop it.
  • If it is OSC 9 (title == None) and no structured plugin has been seen, convert text to Stop.
  • If structured plugin is active, ignore later OSC 9 so Warp does not emit duplicate status/notifications.

3. Move rich-status checks onto session state

The old agent_supports_rich_status(agent) helper was static and could not distinguish Codex plugin from Codex OSC 9 fallback. CLIAgentSession now owns the distinction:

  • has_structured_plugin() is true when a listener exists and, for Codex, a plugin version exists.
  • supports_rich_status() delegates to has_structured_plugin(). This works because the Codex structured plugin reports a version on session_start, while OSC 9 fallback does not. apply_event() now records payload.plugin_version from any event, and update_from_event() emits SessionUpdated when the version changes. This lets footer/UI surfaces re-evaluate chip/status state after the structured plugin connects.

4. Keep Codex command detection fallback

Codex command detection still registers a listener immediately, because OSC 9 has no SessionStart sentinel. Important distinction:

  • Command-detected Codex calls register_cli_agent_listener_without_session_start_event(CLIAgent::Codex, ctx).
  • RegisterPluginListener after install/update uses the same helper and also does not seed a plugin version.
  • Installing the plugin mid-session does not make the running Codex process emit OSC 777. Codex must be restarted to load hooks and start structured notifications.
  • Until restart, Warp should keep treating the session as OSC 9 fallback. Disk install state can drive install/update/restart UI, but not has_structured_plugin().

5. Update UI consumers to use rich-status semantics

All surfaces that previously treated listener.is_some() plus static agent support as rich-status now use session.supports_rich_status():

  • footer plugin-chip suppression and update-chip logic
  • auto show/hide of rich input
  • close-on-submit behavior
  • terminal icon status
  • vertical tabs summary/detail status This prevents OSC 9 fallback Codex sessions from showing precise statuses or suppressing plugin install chips too early.

6. Add Codex plugin manager

The Codex manager follows the Claude manager shape:

  • shell-aware LocalCommandExecutor
  • PATH override support for tools installed by shell managers
  • marketplace add/remove/install/update flow
  • local install detection
  • cached manifest version detection
  • platform plugin install hook

Key Codex differences:

  • CLI binary is codex, not claude.
  • User plugin command is codex plugin add warp@codex-warp, not claude plugin install warp@claude-code-warp.
  • Update removes marketplace codex-warp, re-adds warpdotdev/codex-warp, then runs codex plugin add warp@codex-warp.
  • User plugin key is warp@codex-warp.
  • Config root is $CODEX_HOME or ~/.codex.
  • Install state reads [plugins."warp@codex-warp"].enabled = true from config.toml.
  • Version state reads cached manifests under plugins/cache/codex-warp/warp/*/.codex-plugin/plugin.json.
  • Success copy says restart Codex, not /reload-plugins.

Testing and validation

Unit coverage added/updated:

  • CodexSessionHandler parses OSC 9 text as Stop.
  • empty OSC 9 bodies are ignored.
  • titled non-structured notifications are ignored.
  • structured Codex events are ignored when CodexPlugin is disabled.
  • after one structured Codex event, later OSC 9 is ignored.
  • structured events for other agents are ignored by the Codex handler.
  • Codex plugin manager toggles install/update support by CodexPlugin.
  • plugin install/update instruction commands match Codex command names.
  • native fallback instructions remain when CodexPlugin is off.
  • install detection reads config.toml.
  • version detection picks latest cached plugin manifest version.
  • CODEX_HOME overrides the default ~/.codex path in install/update checks.

Suggested local validation:

  • cargo test -p warp --features test-util codex
  • cargo test -p warp --features test-util cli_agent_sessions
  • Run Warp with CodexPlugin disabled, start Codex with native notifications, verify one completion notification and no rich status.
  • Run Warp with CodexPlugin enabled and plugin active after Codex restart, verify permission requests show rich blocked state.
  • In plugin mode, verify Codex emitting both OSC 777 and OSC 9 does not produce duplicate notifications.
  • Verify footer install/update/restart UI remains visible for OSC 9 fallback, then hides after structured plugin connects after restart.
  • Verify vertical tabs and agent icon do not show rich status for OSC 9 fallback.

Parallelization

No sub-agents recommended for implementation. The change is tightly coupled around one invariant: whether a Codex session is OSC 9 fallback or structured OSC 777 plugin-backed. Best review split:

  • Protocol/session review: listener/mod.rs, cli_agent_sessions/mod.rs, terminal/view.rs.
  • Plugin manager review: plugin_manager/codex.rs, codex_tests.rs, plugin_manager/mod.rs.
  • UI surface review: footer, rich input, agent icon, vertical tabs. These can be reviewed in parallel, but changes should land as one PR because the protocol invariant must stay consistent across all consumers.