Repository navigation
[Safety] Prevent or fork same-task switches across native and routed providers #2095
Description
What are you trying to accomplish?
Use an OpenCodex-routed model for quota supplementation without corrupting or destabilizing an existing native Codex task, while keeping the project folder and files unchanged.
What prevents this today?
A single Codex task can accumulate provider-specific reasoning items, tool-call identifiers, signatures, and terminal semantics. Switching that same task between a native OpenAI model and a non-OpenAI routed model can replay those items on the other wire. The result can be a 400 during compaction/continuation, a websocket turn that never reaches a terminal event, or a thread that needs to be abandoned.
OpenCodex has fixed individual cases (for example reasoning sanitization for the compact endpoint and provider-specific tool-call ID handling), but there is no general task-level boundary or warning that guarantees a safe native/routed switch. This report is based on a macOS Apple Silicon user observation; the exact error depends on the provider and turn history.
What should OpenCodex do?
When the effective provider or wire changes inside an existing task, OpenCodex should provide one of these explicit, safe behaviours:
- Default to a new task/fork with a clear warning that provider-specific history will not be replayed.
- Or, if the task remains in place, run a provider-aware history sanitizer with a fail-closed result and a visible diagnostic.
- Keep the original task and its history recoverable; do not silently rewrite it or claim that a switch succeeded when the next request is unsafe.
- Record the provider/wire boundary in diagnostics without recording prompts, tokens, or raw payloads.
Example usage or interface
1. Start a task with native OpenAI / gpt-5.6-terra.
2. Send a normal turn and a tool turn.
3. Switch the same task to an OpenCodex-routed Kimi/DeepSeek model.
4. Continue the task or trigger compaction.
5. Switch back to native OpenAI.
Expected: a safe fork/warning, or a verified sanitized replay.
Observed: provider-specific history can be replayed across wires and the task may fail or remain stuck.
Alternatives or workarounds
Create a new task in the same project folder whenever changing between native and routed providers. Keep the native task as the control task and use OpenCodex only in disposable or purpose-specific tasks.
Additional context
Related issues:
- kimi-routed turns store non-standard reasoning content (reasoning_text) in thread history — OpenAI remote compaction 400s (array_above_max_length), thread permanently bricked #234 — Kimi-routed reasoning content reached native remote compaction (fixed by fix(server): sanitize reasoning content in v1 compact endpoint #248 on
dev) - [Provider compatibility] Anthropic adapter forwards tool_use ids unsanitized, breaking cross-provider history replay (fix already exists in google.ts) #1767 — Anthropic tool-use IDs broke cross-provider replay (fixed by fix(anthropic): normalize tool call ids so cross-provider history replays #1780 on
dev) - [Compatibility] Add an opt-in Responses stream/terminal repair escape hatch for custom providers #1809 — custom Responses providers can still miss a terminal event
This request is intentionally a task-boundary/safety layer, not a claim that those individual fixes are absent. It contains no keys, tokens, OAuth data, task IDs, local paths, or personal data.
Checks
- I searched existing issues and documentation.
- This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
- I removed secrets and personal data.
Activity
coderabbitai commentedon Aug 19, 2026
🔗 Related PRs
#350 - fix(responses): reconstruct dropped SSE output items + dedupe replayed multi-agent guidance (
#334,#326) [merged]#889 - fix(providers): Copilot mixed-wire routing, DeepSeek service_tier + reasoning replay, Claude 4.6/4.7 1M windows [open]
#892 - fix: bound the six remaining retained-state leaks (
#820wave 2) [merged]#1361 - fix(responses): restore DeepSeek streaming and routed code-mode tools [merged]
#1606 - docs(devlog): routed tool-discovery profile plan with landing verification [open]
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.🧪 Issue enrichment is currently in open beta.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
lidge-jun commentedon Aug 19, 2026
리뷰 · 우선순위 24 / 80
같은 Codex 태스크 안에서 native OpenAI와 라우티드 모델을 오가면, 프로바이더 전용 reasoning·tool-call id·시그니처가 다른 와이어로 다시 나간다. compact가 400을 내거나 웹소켓 턴이 끝나지 않거나 스레드를 버려야 한다. 새 태스크를 만들면 우회된다. 기본 native 설치를 깨는 버그가 아니라 안전 레이어 요청이라 24다. 개별 케이스는
dev에 이미 고쳐져 있다.#234/#248 계열은 compact 경로의 reasoning 소독이다.
src/adapters/openai-responses.ts의sanitizeReasoningInputContent()가 라우티드 히스토리의 thinking을 네이티브 compact에 그대로 넣지 않게 한다. 같은 파일의 라우티드 compaction 재작성은compaction_trigger를 모르는 업스트림용이다. 이건 엔드포인트 단위 방어지, 태스크가 프로바이더를 바꿨다는 신호는 없다.#1767/#1780 계열은 tool-call 식별자다.
src/responses/thought-signature-replay.ts와src/server/responses-item-id-repair.ts가call_id를 스레드/계정/프로바이더 사이에서 재사용하면 안 된다고 전제한다. 가드는 재생과 id 보존이지, native↔routed 전환을 감지해 포크하지 않는다. #1809의 터미널 이벤트 누락도 같은 태스크 경계가 아니라 스트림 완료 문제다.지금 코드베이스에서 “이 태스크의 유효 프로바이더가 바뀌었다”를 한곳에서 판단하는 함수는 찾지 못했다. Codex 태스크 id는 클라이언트가 들고, OpenCodex는 요청마다 카탈로그 슬러그와 어댑터로 라우팅한다. 그래서 전환은 프록시가 태스크를 소유하지 않는 한 요청 경계에서만 보인다. 새 태스크/포크 UI를 Codex 앱에 강제하는 패치 위치는 추측하지 않는다.
원하는 동작은 네 가지다. 기본은 새 태스크/포크와 경고. 유지한다면 프로바이더를 아는 히스토리 소독 + fail-closed + 보이는 진단. 원본 히스토리는 남겨 두고 성공을 거짓으로 말하지 말 것. 진단에는 프롬프트/토큰/원문을 넣지 말 것. 워크어라운드(프로바이더 바꿀 때 같은 폴더에 새 태스크)가 현재 안전한 기본값이다.
해결방안
엔드포인트 소독을 더 늘리는 것만으로는 이 이슈를 닫지 않는다. 태스크 경계를 두려면 요청에서 이전 유효 프로바이더/와이어와 이번 라우트를 비교하는 기록이 먼저 필요하다. 그 저장소와 Codex 앱 포크 API를 이 리뷰에서 새로 짓지 않는다. 패치는 추측하지 않음. 당장은 compact/tool-id 소독(
sanitizeReasoningInputContent, item-id repair, thought-signature replay)을 유지하고, 문서와 진단에 “native↔routed 전환은 새 태스크”를 명시하는 편이 안전하다. 소독을 태스크 유지 경로로 쓸 경우에도 fail-closed여야 하고, 소독 실패를 성공으로 바꾸면 안 된다.이 댓글은 grok-bot이 작성했습니다
lidge-jun commentedon Aug 19, 2026
이건 막는 기능이 아님. 네이티브랑 라우티드 모델을 같은 작업에서 오가는 게 OpenCodex 본업임.
그 전환이 400 나거나 턴이 안 끝나지 않게 만드는 것도 본업임. 포크/경고를 새 이슈로 열 일 아님.
#234#1767처럼 깨지면 그 버그로 고침.이 이슈는 여기까지. 닫음.
이 댓글은 grok-bot이 작성했습니다