Files

123 lines
9.7 KiB
Markdown

# 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:14`](https://github.com/warpdotdev/warp/blob/68acdf7601cc1824d4f4dd250485aca8329efc17/app/src/terminal/cli_agent_sessions/listener/mod.rs#L14) — `CLIAgentSessionHandler` now mutably parses notifications so Codex can remember structured-plugin activation.
- [`app/src/terminal/cli_agent_sessions/listener/mod.rs:87`](https://github.com/warpdotdev/warp/blob/68acdf7601cc1824d4f4dd250485aca8329efc17/app/src/terminal/cli_agent_sessions/listener/mod.rs#L87) — `CodexSessionHandler` parses 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`](https://github.com/warpdotdev/warp/blob/68acdf7601cc1824d4f4dd250485aca8329efc17/app/src/terminal/cli_agent_sessions/plugin_manager/codex.rs#L17) — 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`](https://github.com/warpdotdev/warp/blob/68acdf7601cc1824d4f4dd250485aca8329efc17/app/src/terminal/cli_agent_sessions/plugin_manager/codex.rs#L57) — plugin manager gates install/update/inspection on `FeatureFlag::CodexPlugin`.
- [`app/src/terminal/cli_agent_sessions/plugin_manager/claude.rs:17`](https://github.com/warpdotdev/warp/blob/68acdf7601cc1824d4f4dd250485aca8329efc17/app/src/terminal/cli_agent_sessions/plugin_manager/claude.rs#L17) — Claude manager structure copied for Codex, with CLI/config differences.
- [`app/src/terminal/cli_agent_sessions/plugin_manager/mod.rs:222`](https://github.com/warpdotdev/warp/blob/68acdf7601cc1824d4f4dd250485aca8329efc17/app/src/terminal/cli_agent_sessions/plugin_manager/mod.rs#L222) — 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`](https://github.com/warpdotdev/warp/blob/68acdf7601cc1824d4f4dd250485aca8329efc17/crates/warp_features/src/lib.rs#L789) and [`app/src/features.rs:480`](https://github.com/warpdotdev/warp/blob/68acdf7601cc1824d4f4dd250485aca8329efc17/app/src/features.rs#L480) — 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.