fix(v2): fail fast on unreadable routed agent tasks - #283
Conversation
|
Draft review — the fail-fast direction is right: current Feedback before marking ready:
With those locked down this is a good candidate for the #92 mitigation path. |
Ingwannu
left a comment
There was a problem hiding this comment.
The fail-fast direction is valuable, and pure Fernet-only routed task slots correctly stop with a non-retryable 400 before provider dispatch. However, the current guard still fails open for the mixed shape that existing compatibility logic explicitly produces: plaintext hook preamble plus an encrypted actual task slot.
readablePayloadAfterMarker() counts any text after Payload: as provider-readable. With a normal NEW_TASK envelope followed by [CXC-LEAF-GUARD] ... plaintext and then a Fernet slot, sanitization removes the ciphertext, the guard sees the leaf-guard preamble as readable payload, and the routed provider is called with HTTP 200 even though the actual task body is gone. The captured provider message contains only the envelope and leaf guard. This preserves the core #92 failure mode and contradicts the PR guarantee that unreadable routed tasks stop before dispatch.
Please classify/strip known compatibility preambles before deciding that a task payload is readable, and add the existing mixed-slot fixture as a fail-fast regression. Also pin a real fixture for any plaintext allowed before Message Type:; the current helper can false-reject meaningful pre-envelope text.
Full local current-dev merge verification otherwise passed (3,512 tests, typecheck, privacy scan, GUI lint/build). No malware, backdoor, ciphertext reflection, unsafe I/O, or dependency concern was found.
|
Post-merge Sol review note: This PR was merged then reverted from
The fail-fast concept is important for security and will be rebuilt with proper mixed-slot handling and structural Fernet validation. |
1 high blocker: mixed encrypted slots bypass the guard. 4 medium issues including loose Fernet validation and history-scanning scope.
Detect V2 Fernet-encrypted agent tasks that cannot be decrypted by non-ChatGPT providers and return 400 early, preventing cost storms. Fixes the mixed-slot bypass found by Sol review: control preambles like [CXC-LEAF-GUARD] are no longer treated as actionable task text. Co-authored-by: Mathias <66031173+MathiasHeinke@users.noreply.github.com> Based-on: PR #283
Summary
Fail fast when a Codex V2
agent_messagecontains a native ChatGPT Fernet task token but no provider-readable task payload and the selected route is external.Instead of forwarding an empty task to Grok, Claude, Kimi, or another routed provider, OpenCodex now returns a deterministic, non-retryable HTTP 400 before auth resolution, adapter construction, or provider dispatch. The error directs the caller to plaintext V2 agent-message delivery or a native ChatGPT model.
This is a companion safety fix for #92. It does not claim to recover the missing task body.
Root cause and scope
The native ChatGPT backend can decrypt backend-minted Fernet tokens. An external routed provider cannot. By the time OpenCodex receives this request, the plaintext task is absent, so the proxy has no cryptographically valid way to reconstruct it.
This PR intentionally does not attempt to decrypt or reinterpret that ciphertext. Doing so would require keys OpenCodex does not own and would blur the trust boundary. The actual end-to-end fix remains plaintext V2 task delivery in the Codex client.
The guard is deliberately narrow:
agent_messageinput is inspectedencrypted_contenttask with no readable payload is rejectedencrypted_contentcompatibility path from related PR fix: preserve routed sub-agent task payloads by sanitizing before parse #94 remains intactWhy fail fast
Without this guard, the model override can look successful while the routed child receives no actionable task. That can produce misleading worker output and repeated provider-cost attempts. A clear 400 makes the delivery mismatch observable and non-retryable until the upstream client emits plaintext task content.
Verification
bun test --isolate tests/multi-agent-compat.test.ts tests/v2-agent-message-failfast.test.ts— 31 pass, 0 failbun run typecheck— passbun run privacy:scan— passgit diff --check origin/dev...HEAD— passRefs #92. Related: #94.