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(), notlistener.is_some(). Relevant code: app/src/terminal/cli_agent_sessions/listener/mod.rs:14—CLIAgentSessionHandlernow mutably parses notifications so Codex can remember structured-plugin activation.app/src/terminal/cli_agent_sessions/listener/mod.rs:87—CodexSessionHandlerparses both OSC 9 fallback and structured OSC 777 Codex events.app/src/terminal/cli_agent_sessions/mod.rs:155—has_structured_plugin()distinguishes structured OSC 777 from Codex OSC 9 fallback.app/src/terminal/cli_agent_sessions/mod.rs:164—supports_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 onFeatureFlag::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 whenCodexPluginis disabled.app/src/terminal/view.rs:12825— listener registration withoutSessionStartnever seeds a plugin version. Codex remains OSC 9 fallback until a real structured plugin event reports version.crates/warp_features/src/lib.rs:789andapp/src/features.rs:480— newCodexPluginflag 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
CodexPluginis enabled, markstructured_plugin_active = trueand 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 toStop. - 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 tohas_structured_plugin(). This works because the Codex structured plugin reports a version onsession_start, while OSC 9 fallback does not.apply_event()now recordspayload.plugin_versionfrom any event, andupdate_from_event()emitsSessionUpdatedwhen 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). RegisterPluginListenerafter 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, notclaude. - User plugin command is
codex plugin add warp@codex-warp, notclaude plugin install warp@claude-code-warp. - Update removes marketplace
codex-warp, re-addswarpdotdev/codex-warp, then runscodex plugin add warp@codex-warp. - User plugin key is
warp@codex-warp. - Config root is
$CODEX_HOMEor~/.codex. - Install state reads
[plugins."warp@codex-warp"].enabled = truefromconfig.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:
CodexSessionHandlerparses OSC 9 text asStop.- empty OSC 9 bodies are ignored.
- titled non-structured notifications are ignored.
- structured Codex events are ignored when
CodexPluginis 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
CodexPluginis off. - install detection reads
config.toml. - version detection picks latest cached plugin manifest version.
CODEX_HOMEoverrides the default~/.codexpath in install/update checks.
Suggested local validation:
cargo test -p warp --features test-util codexcargo test -p warp --features test-util cli_agent_sessions- Run Warp with
CodexPlugindisabled, start Codex with native notifications, verify one completion notification and no rich status. - Run Warp with
CodexPluginenabled 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.