Skip to content

Preserve Codex encrypted reasoning across Claude turns#52

Merged
raine merged 1 commit into
raine:mainfrom
mulfyx:fix/codex-reasoning-roundtrip
Jul 14, 2026
Merged

Preserve Codex encrypted reasoning across Claude turns#52
raine merged 1 commit into
raine:mainfrom
mulfyx:fix/codex-reasoning-roundtrip

Conversation

@mulfyx

@mulfyx mulfyx commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

CCP already requests reasoning.encrypted_content from the Codex Responses API, but the Claude translation previously discarded it:

  • outgoing Claude thinking blocks received an empty signature;
  • assistant thinking blocks were ignored when the next Claude request was translated back into Responses input.

As a result, encrypted reasoning could not survive stateless multi-turn replay when previous_response_id was disabled.

This PR round-trips the Codex reasoning item through a CCP-owned, namespaced Claude thinking signature:

Codex { id, encrypted_content }
  -> Claude thinking.signature
  -> next Codex reasoning input item

The encrypted payload is preserved byte-for-byte. CCP does not decrypt or interpret it.

Protocol rationale

OpenAI documents that reasoning items should be included in subsequent input when application code manages conversation history manually. Requesting reasoning.encrypted_content makes the opaque reasoning payload available for this purpose:

Claude's extended-thinking protocol provides an opaque signature field. During streaming, Anthropic specifies that signature_delta is emitted immediately before content_block_stop:

This PR uses that field only as a transport for a namespaced CCP envelope. Foreign, malformed, or oversized signatures are ignored.

Prior art

Other Codex-to-Claude adapters use the same general round-trip pattern.

CLIProxyAPI translates Claude thinking signatures into Codex encrypted_content, requests encrypted reasoning from Responses, and maps it back into Claude thinking blocks:

The pi-mono multi-provider adapter independently stores complete Responses reasoning items in thinkingSignature and restores them on the next request:

Implementation

  • Add a versioned ccp:codex:v1: signature envelope containing the reasoning item ID and encrypted payload.
  • Capture id and encrypted_content from Responses streaming events.
  • Emit signature_delta before content_block_stop.
  • Support signature-only thinking blocks when Codex returns encrypted reasoning without a visible summary.
  • Restore CCP-owned signatures as Responses reasoning items in their original transcript position.
  • Preserve encrypted content exactly without decoding or modifying it.
  • Ignore foreign, malformed, and oversized signatures.
  • Support both buffered and streaming translation paths.
  • Include signed thinking blocks in token estimation and request summaries.

Validation

  • cargo test --all-targets: 559 passed, 0 failed
  • rustfmt 1.96 --check: passed
  • Tested against the real Codex API through an isolated CCP instance on port 18766.
  • CCP_CODEX_PREVIOUS_RESPONSE_ID=0 was used to force full transcript replay.
  • The second upstream request contained exactly one restored reasoning item.
  • Its ID and encrypted payload matched the first response byte-for-byte.
  • The two-turn request completed successfully.
  • /v1/messages/count_tokens accepted the signed transcript.
  • The active proxy on port 18765 was not touched.

Scope

This PR only changes encrypted-reasoning round-trip behavior. It does not change HTTP transport, previous_response_id handling, Docker configuration, authentication, or diagnostic logging.

@raine raine merged commit c11e328 into raine:main Jul 14, 2026
1 check passed
@raine

raine commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Thanks

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.

2 participants