避免 Codex OAuth 模型归一化把 gpt-5.5-pro 降级为 gpt-5.5。\n\n同时补充 GPT-5.5 Pro 的计费回退与长上下文计费策略,保证保留上游模型名后仍使用现有 GPT-5.5/GPT-5.4 计费规则。\n\n验证:\n- go test ./internal/service/...\n- go build ./cmd/server/
PR #2068 dropped every reasoning item from input[] on the OAuth/codex path
(store=false). That silently discards encrypted_content -- the out-of-band
channel that carries reasoning context across turns under store=false --
degrading multi-turn agent reasoning with no visible error. Reported by
@neteroster on PR #2068.
The 404 that #2068 worked around ("Item with id 'rs_...' not found") is
triggered by the rs_* id lookup under store=false, not by the reasoning item
itself -- so the correct fix is to strip the id, not delete the item.
Verified end-to-end against the live chatgpt.com codex backend (gpt-5.5) and
a real OpenClaw container (api: openai-responses):
- bare rs_ id, no encrypted_content -> 404
- id stripped -> 200
- encrypted_content + id stripped -> 200, reasoning context preserved
- reasoning items require a summary field (missing -> 400)
- real OpenClaw multi-turn agent loop -> all /v1/responses 200, zero 404
Fix: keep the reasoning item, strip only the rs_* id (always, independent of
PreserveReferences), preserve encrypted_content/content/summary verbatim, and
backfill an empty summary when absent. Tool-call call_id pairing is untouched.
Also verified compaction_summary items (cmp_*, the other encrypted_content
carrier): they require encrypted_content (missing -> 400) and their id does not
404 when present (kept or stripped), so the existing generic path already
handles them safely -- no special-casing needed.
Adds regression tests for each verified reasoning contract.
Refs #1957, #2068
The errcheck linter flagged an unchecked type assertion on
item["type"].(string). Use the two-value form with require.True
to satisfy the linter and fail clearly on unexpected types.
Closes#1957
The OAuth path forwards client requests to chatgpt.com/backend-api/codex/responses,
where applyCodexOAuthTransform forces store=false (chatgpt.com's codex backend
rejects store=true). Reasoning items emitted under store=false are NEVER
persisted upstream, so any rs_* reference that a client carries forward in a
subsequent input[] array triggers a guaranteed upstream 404:
Item with id 'rs_...' not found. Items are not persisted when `store` is
set to false. Try again with `store` set to true, or remove this item
from your input.
sub2api wraps this as 502 "Upstream request failed" and the conversation
breaks on every multi-turn /v1/responses request that uses reasoning + tools
(reproducible with gpt-5.5; gpt-5.4 happens to dodge it because the upstream
does not emit reasoning items for that model).
Affected clients include any that follow the OpenAI Responses API spec and
replay prior assistant items verbatim — in practice this hit OpenClaw and
similar agent harnesses on every turn ≥2 with tool use.
The fix: in filterCodexInput, drop input items with type == "reasoning"
entirely. The model never reads reasoning summary text from input (only
encrypted_content can carry reasoning context across turns, and chatgpt.com
under store=false does not emit it), so this is a no-op for the model itself
and a clean removal of unreachable upstream lookups.
Scope is intentionally narrow:
* Only OAuth account requests (account.Type == AccountTypeOAuth) reach
applyCodexOAuthTransform / filterCodexInput.
* API-key accounts going to api.openai.com/v1/responses are unaffected
(store=true works there, rs_* persists, multi-turn already works).
* Anthropic / Gemini platform groups go through different transforms and
are unaffected.
* /v1/chat/completions is unaffected (no reasoning items).
* item_reference items (different type) are unaffected — only type ==
"reasoning" is dropped.
Verification:
* Existing tests pass: go test ./internal/service/ -run Codex|Tool|OAuth
* New regression test asserts reasoning items are dropped under both
preserveReferences=true and preserveReferences=false.
* End-to-end repro on gpt-5.5 multi-turn + tools: pre-patch 502, post-patch
200. Repro on gpt-5.4 unchanged. Three-turn deep loop on gpt-5.5 passes.
OAuth upstreams (ChatGPT) reject requests containing role:"system" in
the input array with HTTP 400 "System messages are not allowed". Extract
such items before forwarding and merge their content into the top-level
instructions field, prepending to any existing value.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The ChatGPT backend-api codex/responses endpoint requires `input` to be
an array, but the OpenAI Responses API spec allows it to be a plain string.
When a client sends a string input, sub2api now converts it to the expected
message array format. Empty/whitespace-only strings become an empty array
to avoid triggering a 400 "Input must be a list" error.
避免上游 Store 必须为 false 的错误
仅在缺失或 true 时写回 store
测试: go test ./internal/service -run TestApplyCodexOAuthTransform
测试: make test-backend(golangci-lint 已单独执行)