Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
92330b5
fix(server): add bounded client SSE frame buffer
Wibias Aug 7, 2026
5230c3c
fix(server): bound WebSocket SSE frame retention
Wibias Aug 7, 2026
aaaa4f3
fix(server): bound HTTP SSE terminal framing
Wibias Aug 7, 2026
dfe511b
test(server): cover client SSE frame bounds
Wibias Aug 7, 2026
40a6e5b
fix(server): widen typed-array tail storage
Wibias Aug 7, 2026
ac938a2
test(server): preserve raw split UTF-8 bytes
Wibias Aug 7, 2026
6d4c16a
fix(server): bound SSE framer allocation count
Wibias Aug 7, 2026
7ba0225
fix(server): bound inspector allocation count
Wibias Aug 7, 2026
c78de5a
fix(server): bound SSE frame-count amplification
Wibias Aug 7, 2026
af5c157
fix(server): release WS reader on framing errors
Wibias Aug 7, 2026
b9396b4
fix(server): harden SSE failure cleanup
Wibias Aug 7, 2026
3e4725d
test(server): cover SSE framing failure paths
Wibias Aug 7, 2026
b0f79aa
chore(server): remove no-op WS expression
Wibias Aug 7, 2026
e348caa
fix(server): widen failed-tail typed array
Wibias Aug 7, 2026
17dfaf8
fix(ws): preserve dropped-send rejection semantics
Wibias Aug 7, 2026
73b30b0
test(ws): preserve dropped-send rejection contract
Wibias Aug 7, 2026
33ac593
fix(sse): honour terminal before trailing framing errors
Wibias Aug 7, 2026
97e3c87
test(sse): keep committed terminal ahead of trailing overflow
Wibias Aug 7, 2026
e1ff3b9
docs(proxy): document Responses SSE frame limit
Wibias Aug 7, 2026
65bea21
docs(proxy): sync Japanese SSE frame limit
Wibias Aug 7, 2026
49dabd9
docs(proxy): sync Korean SSE frame limit
Wibias Aug 7, 2026
1f47d21
docs(proxy): sync Russian SSE frame limit
Wibias Aug 7, 2026
55d1340
docs(proxy): sync Chinese SSE frame limit
Wibias Aug 7, 2026
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
4 changes: 3 additions & 1 deletion docs-site/src/content/docs/ja/reference/proxy-formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ provider events → internal adapter events → client dialect

`stream: false` を指定するか、`stream` を指定しないと、同じアダプター イベントが 1 つの Responses JSON オブジェクトに収集されます。どちらの形式でも、選択したモデル、出力項目、端末の状態、使用状況が保存されます。

クライアント向け Responses SSE フレームは、SSE ブロック区切りの前の生バイトで測って 1 フレームあたり 4 MiB に制限されます。HTTP では、区切りなしでこの上限を超えたアップストリーム フレームは、合成 `response.failed` イベントと続く `data: [DONE]` でフェイルクローズします。Responses WebSocket ブリッジでは、同じ条件で 502 `websocket_protocol_error` を送信し、アップストリーム リーダーをキャンセルします。完全な Responses 終端フレームがすでに到着している場合はそれが優先され、その後のサイズ超過または不正なバイトは、完了したターンをトランスポート障害に置き換えず破棄されます。

すべての端末応答使用状況オブジェクトには、プロバイダーが詳細を報告しなかった場合でも、両方の詳細オブジェクトが含まれます。

```json
Expand Down Expand Up @@ -211,4 +213,4 @@ Anthropic オリジンの失敗は Anthropic のエラー エンベロープで

プロキシは、本物のバックエンド暗号文を不透明なものとして扱います。構造的に有効な暗号文はバイト単位で保存されます。opencodex は暗号文を復号したり、その内容を変換したり、別のプロバイダー用に再暗号化したりしません。

一部のエージェント フックはこれまで、プレーンテキストの制御テキストを `encrypted_content` スロットに配置していました。互換性を確保するために、プロキシは、構造的に有効な Fernet の実行を変更せずに保持しながら、プレーンテキストをテキスト部分に分割します。 `agent_message` が修復中にすべての暗号化された部分を失った場合、それは通常のユーザー メッセージになります。現在の v2 タスクが完全に暗号化されたままであるが、選択したルーティングされたターゲットがネイティブ ChatGPT 暗号文を読み取ることができない場合、opencodex は読み取り不能なバイトをそのプロバイダーに送信する代わりに `unreadable_encrypted_agent_task` で失敗します。ワーカー タスクに関するクライアントの動作については、[サブエージェントサーフェス](/guides/sub-agent-surface/) を参照してください。
一部のエージェント フックはこれまで、プレーンテキストの制御テキストを `encrypted_content` スロットに配置していました。互換性を確保するために、プロキシは、構造的に有効な Fernet の実行を変更せずに保持しながら、プレーンテキストをテキスト部分に分割します。 `agent_message` が修復中にすべての暗号化された部分を失った場合、それは通常のユーザー メッセージになります。現在の v2 タスクが完全に暗号化されたままであるが、選択したルーティングされたターゲットがネイティブ ChatGPT 暗号文を読み取ることができない場合、opencodex は読み取り不能なバイトをそのプロバイダーに送信する代わりに `unreadable_encrypted_agent_task` で失敗します。ワーカー タスクに関するクライアントの動作については、[サブエージェントサーフェス](/guides/sub-agent-surface/) を参照してください。
4 changes: 3 additions & 1 deletion docs-site/src/content/docs/ko/reference/proxy-formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ deltas, 그리고 정확히 하나의 종료 `response.completed`, `response.fai
`stream: false`이거나 `stream`이 없으면, 같은 adapter 이벤트가 하나의 Responses JSON 객체로 수집됩니다.
두 형식 모두 선택한 모델, output item, 종료 상태, usage를 보존합니다.

클라이언트로 전달되는 Responses SSE 프레임은 SSE 블록 구분자 앞의 원시 바이트 기준으로 프레임당 4 MiB로 제한됩니다. HTTP에서는 구분자 없이 이 한도를 초과한 업스트림 프레임을 합성 `response.failed` 이벤트와 이어지는 `data: [DONE]`으로 fail closed 처리합니다. Responses WebSocket 브리지에서는 같은 조건에서 502 `websocket_protocol_error`를 보내고 업스트림 reader를 취소합니다. 완전한 Responses 종료 프레임이 이미 수신된 경우에는 그 종료가 우선하며, 이후의 과도한 크기 또는 잘못된 바이트는 완료된 턴을 전송 오류로 바꾸지 않고 버립니다.

모든 종료 Responses usage 객체에는 제공자가 해당 세부 정보를 보고하지 않았더라도 두 상세 객체가 모두
포함됩니다.

Expand Down Expand Up @@ -263,4 +265,4 @@ OpenAI 스타일 `origin_rejected` body가 아니라 403 `permission_error`입
작업이 실제로 암호화된 상태이고 선택된 라우팅 대상이 네이티브 ChatGPT 암호문을 읽을 수 없다면,
opencodex는 읽을 수 없는 바이트를 프로바이더에 보내는 대신 `unreadable_encrypted_agent_task`로
실패합니다. worker task와 관련된 클라이언트 동작은 [서브에이전트 표면](/guides/sub-agent-surface/)을
참조하세요.
참조하세요.
9 changes: 8 additions & 1 deletion docs-site/src/content/docs/reference/proxy-formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ With `stream: true`, the response is `text/event-stream`. The bridge emits Respo
With `stream: false` or no `stream`, the same adapter events are collected into one Responses JSON
object. Both forms preserve the selected model, output items, terminal status, and usage.

Client-facing Responses SSE frames are limited to 4 MiB per frame, measured in raw bytes before the
SSE block delimiter. On HTTP, an unterminated upstream frame that exceeds the limit fails closed
with a synthetic `response.failed` event followed by `data: [DONE]`. On the Responses WebSocket
bridge, the same condition emits a 502 `websocket_protocol_error` and cancels the upstream reader.
A complete Responses terminal frame is authoritative: oversized or malformed trailing bytes after
that terminal are dropped rather than replacing the completed turn with a transport failure.

Every terminal Responses usage object includes both detail objects, even when the provider did not
report those details:

Expand Down Expand Up @@ -280,4 +287,4 @@ structurally valid Fernet runs unchanged. If an `agent_message` loses all encryp
that repair, it becomes a normal user message. If a current v2 task remains genuinely encrypted
but the selected routed target cannot read native ChatGPT ciphertext, opencodex fails with
`unreadable_encrypted_agent_task` instead of sending unreadable bytes to that provider. See
[Sub-agent Surface](/guides/sub-agent-surface/) for the client behavior around worker tasks.
[Sub-agent Surface](/guides/sub-agent-surface/) for the client behavior around worker tasks.
4 changes: 3 additions & 1 deletion docs-site/src/content/docs/ru/reference/proxy-formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Translated-adapter'ы обрабатывают только известные
При `stream: false` или при отсутствии `stream` те же события адаптера собираются в один JSON
Responses. Обе формы сохраняют выбранную модель, output item'ы, terminal status и usage.

Клиентские frame'ы Responses SSE ограничены 4 MiB на frame, считая сырые байты до разделителя SSE-блока. В HTTP незавершённый upstream-frame, превысивший этот предел, завершается fail-closed синтетическим событием `response.failed`, после которого идёт `data: [DONE]`. В мосте Responses WebSocket то же условие даёт 502 `websocket_protocol_error` и отменяет upstream-reader. Если полноценный terminal-frame Responses уже получен, он остаётся авторитетным: слишком большие или некорректные байты после него отбрасываются и не заменяют завершённый ход транспортной ошибкой.

Каждый terminal usage-объект Responses всегда включает оба detail-объекта, даже если провайдер их
не сообщил:

Expand Down Expand Up @@ -281,4 +283,4 @@ ciphertext сохраняется байт в байт: opencodex его не р
остаётся по-настоящему зашифрованной, а выбранная routed-цель не умеет читать ciphertext нативного
ChatGPT, opencodex завершит запрос ошибкой `unreadable_encrypted_agent_task`, вместо того чтобы
отправить нечитаемые байты этому провайдеру. О поведении клиента вокруг worker-task'ов см.
[Поверхность подагентов](/guides/sub-agent-surface/).
[Поверхность подагентов](/guides/sub-agent-surface/).
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ Responses 表示是这座桥的中心。原生兼容的路由可以跳过部分
当 `stream: false` 或未提供 `stream` 时,同样的适配器事件会被收集为一个 Responses JSON
对象。两种形式都会保留所选模型、输出项、终止状态和 usage。

面向客户端的 Responses SSE 帧按 SSE 块分隔符之前的原始字节计算,每帧限制为 4 MiB。对于 HTTP,未终止的上游帧一旦超过该限制,会以合成的 `response.failed` 事件并随后发送 `data: [DONE]` 的方式 fail closed。对于 Responses WebSocket 桥,相同情况会发送 502 `websocket_protocol_error` 并取消上游 reader。已经完整到达的 Responses 终止帧具有优先权;其后的超大或格式错误字节会被丢弃,而不会把已经完成的轮次替换为传输失败。

每个终止的 Responses usage 对象都包含两个 detail 对象,即使提供方没有报告这些细节:

```json
Expand Down Expand Up @@ -235,4 +237,4 @@ Anthropic 来源的失败会以 Anthropic 的错误封装呈现,因此该方
代理把真正的后端密文视为不透明数据。结构有效的密文会逐字节保留:opencodex 不会对其解密、翻译其内容,或为另一个提供方重新加密。

某些 agent hook 历史上会把明文控制文本放进 `encrypted_content` 槽。为兼容起见,代理会把那部分明文拆分为文本片段,同时保持任何结构有效的 Fernet 片段不变。如果一个 `agent_message` 在该修复过程中失去了所有加密部分,它就会变成普通的 user message。如果当前的 v2 task 仍然真的是加密的,但所选路由目标无法读取原生 ChatGPT 密文,opencodex 会以
`unreadable_encrypted_agent_task` 失败,而不是把不可读字节发送给该提供方。有关 worker task 周边的客户端行为,请参见 [Sub-agent Surface](/guides/sub-agent-surface/)。
`unreadable_encrypted_agent_task` 失败,而不是把不可读字节发送给该提供方。有关 worker task 周边的客户端行为,请参见 [Sub-agent Surface](/guides/sub-agent-surface/)。
Loading
Loading