Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/src/content/docs/providers/codex.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ WebSocket is the default transport. Set `CCP_CODEX_TRANSPORT=http` for HTTP SSE,

WebSocket setup honors `HTTP_PROXY` for `ws://`, `HTTPS_PROXY` for the default `wss://` endpoint, `ALL_PROXY` as a fallback, and `NO_PROXY` exclusions. A normal HTTP proxy can therefore carry the default WebSocket connection with CONNECT; TUN mode is not required. Set proxy variables before starting the process and restart after changing them. For example, setting `HTTPS_PROXY` to `http://127.0.0.1:7890` sends HTTPS/WSS destinations through the HTTP proxy at port 7890; it does not require an `https://` proxy URL.

`CCP_CODEX_PREVIOUS_RESPONSE_ID=1` enables append-only WebSocket continuation. It reuses a session connection and sends `previous_response_id` only when the translated request shape and transcript extension are safe. State is in memory, keyed by Claude Code session ID.
`CCP_CODEX_PREVIOUS_RESPONSE_ID=1` enables append-only WebSocket continuation. A valid identity containing only a Claude Code session ID owns the Main continuation for that session. Each valid direct Agent ID owns an independent continuation and reusable WebSocket within the same session. Nested Agents are keyed by their direct child ID; the parent ID is validated but does not become part of the owner key. The proxy sends `previous_response_id` only when the translated request shape and transcript extension are safe, and only on the exact live WebSocket that produced that response.

An absent, malformed, or ambiguous identity does not reject the HTTP request; that request proceeds without continuation or WebSocket reuse. If the originating socket is missing, dead, or has been replaced, the proxy retries once with the full translated input and without the stale response ID. Continuation and connection state is held only in memory and is lost when the proxy restarts.

Detected auto-review classifier subrequests are intentionally stateless even when valid session and Agent headers are present. They neither consume nor publish continuation or WebSocket ownership.

## Server compaction

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod project;
pub mod provider;
pub mod providers;
pub mod registry;
pub mod request_identity;
pub mod retry;
pub mod server;
pub mod session;
Expand Down
12 changes: 12 additions & 0 deletions src/provider.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::anthropic::schema::MessagesRequest;
use crate::monitor::MonitorHandle;
use crate::request_identity::ConversationIdentity;
use crate::traffic::TrafficCapture;
use anyhow::Result;
use async_trait::async_trait;
Expand All @@ -26,6 +27,17 @@ pub trait Provider: Send + Sync {
fn supported_models(&self) -> Vec<String>;
fn cli(&self) -> &'static dyn CliHandlers;
async fn handle_messages(&self, body: MessagesRequest, ctx: RequestContext) -> Response;

async fn handle_messages_with_conversation_identity(
&self,
body: MessagesRequest,
ctx: RequestContext,
conversation_identity: Option<ConversationIdentity>,
) -> Response {
let _ = conversation_identity;
self.handle_messages(body, ctx).await
}

async fn handle_count_tokens(&self, body: MessagesRequest, ctx: RequestContext) -> Response;

async fn generate_anthropic_stream(
Expand Down
1,403 changes: 1,242 additions & 161 deletions src/providers/codex/client.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/providers/codex/compaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub async fn request_compaction(
compaction_request.include = Some(vec!["reasoning.encrypted_content".to_string()]);

let response = client
.post_codex(&compaction_request, ctx, None)
.post_codex_for_owner(&compaction_request, ctx, None)
.await
.map_err(CompactionError::Upstream)?;
let compaction = parse_compaction_response(&response.body)?;
Expand Down
Loading