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
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/fr/guides/claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ Le proxy traduit chaque requête Anthropic Messages API au format Codex Response
| Texte assistant | `output_text` |
| Assistant `tool_use` | `function_call` (`input` → JSON-stringifié `arguments`) |
| Utilisateur `tool_result` | `function_call_output` (`is_error` → préfixe `[tool error]`) |
| Relecture de `thinking` / `redacted_thinking` | Ignorée |
| Relecture de `thinking` / `redacted_thinking` | Éléments `reasoning` avec enveloppes `ocxr1` bornées pour les signatures et les contenus masqués |
| Outils fonctionnels | `{type: "function"}` (`web_search*` → `{type: "web_search"}`) |
| `tool_choice` | `auto`→`auto`, `none`→`none`, `any`→`required`, fonction nommée→`{type:"function",name}`, hébergée WebSearch/web_search→`{type:"web_search"}` |
| `max_tokens` | `max_output_tokens` |
Expand All @@ -516,7 +516,7 @@ Le proxy traduit chaque requête Anthropic Messages API au format Codex Response
| `response.created` | `message_start` + `ping` |
| Battement de coeur | `ping` |
| Deltas de texte | `content_block_start` → `content_block_delta` (texte) → `content_block_stop` |
| Résumé ou texte de raisonnement | Bloc `thinking` avec signature synthétique |
| Résumé ou texte de raisonnement | Bloc `thinking` avec la signature relue, ou une enveloppe de secours `ocxr1` bornée |
| Trames d'appel de fonction | Bloc `tool_use` avec `input_json_delta` |
| Événement terminal | `message_delta` → `message_stop` |
| EOF avant la borne | style 502 `api_error` |
Expand Down
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/guides/claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ The proxy translates every Anthropic Messages API request into the Codex Respons
| Assistant text | `output_text` |
| Assistant `tool_use` | `function_call` (`input` → JSON-stringified `arguments`) |
| User `tool_result` | `function_call_output` (`is_error` → `[tool error]` prefix) |
| `thinking` / `redacted_thinking` replay | Dropped |
| `thinking` / `redacted_thinking` replay | `reasoning` items with bounded `ocxr1` envelopes for signatures and redacted payloads |
| Function tools | `{type: "function"}` (`web_search*` → `{type: "web_search"}`) |
| `tool_choice` | `auto`→`auto`, `none`→`none`, `any`→`required`, named function→`{type:"function",name}`, hosted WebSearch/web_search→`{type:"web_search"}` |
| `max_tokens` | `max_output_tokens` |
Expand All @@ -530,7 +530,7 @@ name.
| `response.created` | `message_start` + `ping` |
| Heartbeat | `ping` |
| Text deltas | `content_block_start` → `content_block_delta` (text) → `content_block_stop` |
| Reasoning summary/text | `thinking` block with synthetic signature |
| Reasoning summary/text | `thinking` block with the replayed signature, or a bounded `ocxr1` fallback envelope |
| Function-call frames | `tool_use` block with `input_json_delta` |
| Terminal event | `message_delta` → `message_stop` |
| EOF before terminal | 502-style `api_error` |
Expand Down
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/ja/guides/claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ Claude Code の `/effort` 設定はアダプターでも維持されます。
| Assistant テキスト | `output_text` |
| Assistant `tool_use` | `function_call`(`input` → JSON 文字列に変換した `arguments`) |
| ユーザー `tool_result` | `function_call_output`(`is_error` → `[tool error]` 接頭辞) |
| `thinking` / `redacted_thinking` 再生 | 破棄 |
| `thinking` / `redacted_thinking` 再生 | シグネチャと秘匿ペイロードを境界付き `ocxr1` エンベロープに保持した `reasoning` 項目 |
| Function ツール | `{type: "function"}`(`web_search*` → `{type: "web_search"}`) |
| `tool_choice` | `auto`→`auto`、`none`→`none`、`any`→`required`、名前指定関数→`{type:"function",name}`、ホスト型 WebSearch/web_search→`{type:"web_search"}` |
| `max_tokens` | `max_output_tokens` |
Expand All @@ -384,7 +384,7 @@ role、`tool_use_id` のない `tool_result`、id/name のない `tool_use`、na
| `response.created` | `message_start` + `ping` |
| Heartbeat | `ping` |
| テキスト delta | `content_block_start` → `content_block_delta`(text) → `content_block_stop` |
| 推論要約/テキスト | 合成シグネチャ付きの `thinking` ブロック |
| 推論要約/テキスト | 再生されたシグネチャ、または境界付き `ocxr1` フォールバックを持つ `thinking` ブロック |
| Function-call フレーム | `input_json_delta` を持つ `tool_use` ブロック |
| 終了イベント | `message_delta` → `message_stop` |
| 終了前に EOF | 502 形式 `api_error` |
Expand Down
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/ko/guides/claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ Claude Code의 `/effort` 설정은 어댑터에서도 유지돼요.
| Assistant 텍스트 | `output_text` |
| Assistant `tool_use` | `function_call`(`input` → JSON 문자열로 변환한 `arguments`) |
| 사용자 `tool_result` | `function_call_output`(`is_error` → `[tool error]` 접두사) |
| `thinking` / `redacted_thinking` 재생 | 버려요 |
| `thinking` / `redacted_thinking` 재생 | 서명과 비공개 페이로드를 제한된 `ocxr1` 봉투에 담은 `reasoning` 항목 |
| Function 도구 | `{type: "function"}`(`web_search*` → `{type: "web_search"}`) |
| `tool_choice` | `auto`→`auto`, `none`→`none`, `any`→`required`, 이름 지정 함수→`{type:"function",name}`, 호스팅 WebSearch/web_search→`{type:"web_search"}` |
| `max_tokens` | `max_output_tokens` |
Expand All @@ -422,7 +422,7 @@ role, `tool_use_id` 없는 `tool_result`, id/name 없는 `tool_use`, name 없는
| `response.created` | `message_start` + `ping` |
| Heartbeat | `ping` |
| 텍스트 delta | `content_block_start` → `content_block_delta`(text) → `content_block_stop` |
| 추론 요약/텍스트 | 합성 signature가 있는 `thinking` 블록 |
| 추론 요약/텍스트 | 재생된 서명 또는 제한된 `ocxr1` 폴백이 있는 `thinking` 블록 |
| Function-call 프레임 | `input_json_delta`가 있는 `tool_use` 블록 |
| 종료 이벤트 | `message_delta` → `message_stop` |
| 종료 전에 EOF | 502 형식 `api_error` |
Expand Down
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/ru/guides/claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ Claude Code — это лишь учётные данные для доступ
| Текст ассистента | `output_text` |
| `tool_use` ассистента | `function_call` (`input` → `arguments` в виде JSON-строки) |
| `tool_result` пользователя | `function_call_output` (`is_error` → префикс `[tool error]`) |
| Повтор `thinking` / `redacted_thinking` | Отбрасывается |
| Повтор `thinking` / `redacted_thinking` | Элементы `reasoning` с ограниченными конвертами `ocxr1` для подписей и скрытых данных |
| Function-инструменты | `{type: "function"}` (`web_search*` → `{type: "web_search"}`) |
| `tool_choice` | `auto`→`auto`, `none`→`none`, `any`→`required`, именованная функция→`{type:"function",name}`, размещённый WebSearch/web_search→`{type:"web_search"}` |
| `max_tokens` | `max_output_tokens` |
Expand All @@ -410,7 +410,7 @@ id/name; именованный `tool_choice` без имени.
| `response.created` | `message_start` + `ping` |
| Heartbeat | `ping` |
| Текстовые дельты | `content_block_start` → `content_block_delta` (text) → `content_block_stop` |
| Резюме/текст рассуждений | Блок `thinking` с синтетической подписью |
| Резюме/текст рассуждений | Блок `thinking` с повторно переданной подписью или ограниченным резервным конвертом `ocxr1` |
| Кадры function-call | Блок `tool_use` с `input_json_delta` |
| Завершающее событие | `message_delta` → `message_stop` |
| EOF до завершающего события | `api_error` в стиле 502 |
Expand Down
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/tr/guides/claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ dönüştürür:
| Asistan metni | `output_text` |
| Asistan `tool_use` | `function_call` (`input` → JSON dizgeleştirilmiş `arguments`) |
| Kullanıcı `tool_result` | `function_call_output` (`is_error` → `[tool error]` öneki) |
| `thinking` / `redacted_thinking` tekrarı | Bırakılır |
| `thinking` / `redacted_thinking` tekrarı | İmzaları ve gizli yükleri sınırlı `ocxr1` zarflarında taşıyan `reasoning` öğeleri |
| Fonksiyon araçları | `{type: "function"}` (`web_search*` → `{type: "web_search"}`) |
| `tool_choice` | `auto`→`auto`, `none`→`none`, `any`→`required`, adlandırılmış fonksiyon→`{type:"function",name}`, barındırılan WebSearch/web_search→`{type:"web_search"}` |
| `max_tokens` | `max_output_tokens` |
Expand All @@ -599,7 +599,7 @@ kimlik/ad içermeyen `tool_use`; ad içermeyen adlandırılmış `tool_choice`.
| `response.created` | `message_start` + `ping` |
| Kalp atışı (Heartbeat) | `ping` |
| Metin farkları | `content_block_start` → `content_block_delta` (metin) → `content_block_stop` |
| Akıl yürütme özeti/metni | Sentetik imzalı `thinking` bloğu |
| Akıl yürütme özeti/metni | Tekrarlanan imzayı veya sınırlı bir `ocxr1` yedeğini taşıyan `thinking` bloğu |
| Fonksiyon çağrısı çerçeveleri | `input_json_delta` ile `tool_use` bloğu |
| Terminal olayı | `message_delta` → `message_stop` |
| Terminalden önce EOF | 502 tarzı `api_error` |
Expand Down
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/zh-cn/guides/claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ Claude Code 的 `/effort` 设置会完整保留并传递给适配器:
| Assistant 文本 | `output_text` |
| Assistant `tool_use` | `function_call`(`input` → JSON 字符串化的 `arguments`) |
| 用户 `tool_result` | `function_call_output`(`is_error` → `[tool error]` 前缀) |
| 重放 `thinking` / `redacted_thinking` | 丢弃 |
| 重放 `thinking` / `redacted_thinking` | `reasoning` 项;签名和脱敏载荷保存在有界 `ocxr1` 信封中 |
| Function 工具 | `{type: "function"}`(`web_search*` → `{type: "web_search"}`) |
| `tool_choice` | `auto`→`auto`,`none`→`none`,`any`→`required`,指定函数→`{type:"function",name}`,托管 WebSearch/web_search→`{type:"web_search"}` |
| `max_tokens` | `max_output_tokens` |
Expand All @@ -361,7 +361,7 @@ role;`tool_result` 缺少 `tool_use_id`;`tool_use` 缺少 id/name;指定
| `response.created` | `message_start` + `ping` |
| 心跳 | `ping` |
| 文本增量 | `content_block_start` → `content_block_delta`(文本)→ `content_block_stop` |
| 推理摘要/文本 | 带合成签名的 `thinking` 块 |
| 推理摘要/文本 | 带重放签名或有界 `ocxr1` 回退信封的 `thinking` 块 |
| Function-call 帧 | 带 `input_json_delta` 的 `tool_use` 块 |
| 终止事件 | `message_delta` → `message_stop` |
| 在终止事件前 EOF | 502 风格的 `api_error` |
Expand Down
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/zh-tw/guides/claude-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ Claude Code 的 `/effort` 設定會完整保留並傳遞給適配器:
| Assistant 文字 | `output_text` |
| Assistant `tool_use` | `function_call`(`input` → JSON 字串化的 `arguments`) |
| 使用者 `tool_result` | `function_call_output`(`is_error` → `[tool error]` 字首) |
| 重放 `thinking` / `redacted_thinking` | 丟棄 |
| 重放 `thinking` / `redacted_thinking` | `reasoning` 項目;簽名與遮蔽載荷保存在有界 `ocxr1` 信封中 |
| Function 工具 | `{type: "function"}`(`web_search*` → `{type: "web_search"}`) |
| `tool_choice` | `auto`→`auto`,`none`→`none`,`any`→`required`,指定名稱 function→`{type:"function",name}`,hosted WebSearch/web_search→`{type:"web_search"}` |
| `max_tokens` | `max_output_tokens` |
Expand All @@ -437,7 +437,7 @@ role;`tool_result` 缺少 `tool_use_id`;`tool_use` 缺少 id/name;指定
| `response.created` | `message_start` + `ping` |
| 心跳 | `ping` |
| 文字增量 | `content_block_start` → `content_block_delta`(文字)→ `content_block_stop` |
| 推理摘要/文字 | 帶合成簽名的 `thinking` 塊 |
| 推理摘要/文字 | 帶重播簽名或有界 `ocxr1` 備援信封的 `thinking` 塊 |
| Function-call 幀 | 帶 `input_json_delta` 的 `tool_use` 塊 |
| 終止事件 | `message_delta` → `message_stop` |
| 在終止事件前 EOF | 502 風格的 `api_error` |
Expand Down
2 changes: 2 additions & 0 deletions scripts/test-layout/layout.json
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@
"claude-outbound.test.ts": "claude-integration",
"claude-shell-hook.test.ts": "claude-integration",
"claude-sidecar-override.test.ts": "claude-integration",
"claude-source-envelope.test.ts": "claude-integration",
"claude-system-env-auto.test.ts": "claude-integration",
"cleanup-orphaned-workflows.test.ts": "ci-workflows",
"clearable-deadline.test.ts": "lib",
Expand Down Expand Up @@ -999,6 +1000,7 @@
"rate-limit-reset-credits.test.ts": "gui",
"rate-limit-retry.test.ts": "providers",
"reasoning-effort.test.ts": "codex-integration",
"reasoning-envelope.test.ts": "responses",
"reasoning-replay-identity.test.ts": "adapters",
"reasoning-replay-robustness.test.ts": "adapters",
"reasoning-replay-scope-source.test.ts": "lib",
Expand Down
28 changes: 23 additions & 5 deletions src/claude/inbound.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
* Design (devlog/260711_claude_inbound/010, 003_evidence.md):
* - translate-and-replay: the produced body MUST pass the real responsesRequestSchema
* parse so routing/OAuth/pool/failover are inherited unchanged.
* - thinking/redacted_thinking blocks on replay are DROPPED (v1 policy) — routed
* providers carry reasoning in Responses items/ocxr1 envelopes instead.
* - thinking/redacted_thinking replay is preserved in Responses reasoning items;
* signatures and redacted payloads travel in bounded ocxr1 envelopes.
* - thinking.budget_tokens is NEVER forwarded raw; it maps to an effort tier.
* - top_k is accepted and silently dropped (no Responses equivalent, CCR parity).
*/
Expand All @@ -17,6 +17,7 @@ export { resolveInboundModel, effortForThinkingBudget, effortFromOutputConfig, e
import { AnthropicRequestError, isRec, type Rec } from "./inbound-records";
import { resolveInboundModel, effortForThinkingBudget, effortFromOutputConfig, formatFromOutputConfig } from "./inbound-model-options";
import { systemToInstructions, toolsToResponses, toolChoiceToResponses } from "./inbound-content-options";
import { decodeReasoningEnvelope, encodeReasoningEnvelope, OCX_REASONING_PREFIX } from "../responses/reasoning-envelope";



Expand Down Expand Up @@ -234,9 +235,26 @@ function assistantMessageToItems(content: unknown, input: Rec[]): void {
input.push({ type: "function_call", call_id: raw.id, name: raw.name, arguments: JSON.stringify(raw.input ?? {}) });
break;
}
case "thinking":
case "redacted_thinking":
break; // v1 policy: dropped on replay (003 evidence — safe for routed providers)
case "thinking": {
flush();
const thinking = typeof raw.thinking === "string" ? raw.thinking : "";
const signature = typeof raw.signature === "string" ? raw.signature : "";
if (signature.startsWith(OCX_REASONING_PREFIX)) {
const owned = decodeReasoningEnvelope(signature);
if (!owned) throw new AnthropicRequestError("malformed ocxr1 reasoning signature");
if (Object.hasOwn(owned, "sig")) throw new AnthropicRequestError("OpenCodex reasoning continuity cannot be replayed as an Anthropic signature");
}
const encrypted = signature.length === 0 ? undefined : signature.startsWith(OCX_REASONING_PREFIX) ? signature : encodeReasoningEnvelope({ sig: signature });
if (thinking.length === 0 && !encrypted) break;
input.push({ type: "reasoning", id: `rs_${crypto.randomUUID().replace(/-/g, "")}`, summary: thinking.length > 0 ? [{ type: "summary_text", text: thinking }] : [], ...(encrypted ? { encrypted_content: encrypted } : {}) });
break;
}
case "redacted_thinking": {
flush();
const data = typeof raw.data === "string" ? raw.data : "";
if (data.length > 0) input.push({ type: "reasoning", id: `rs_${crypto.randomUUID().replace(/-/g, "")}`, summary: [], encrypted_content: encodeReasoningEnvelope({ red: [data] }) });
break;
}
default:
break;
}
Expand Down
Loading
Loading