Repository navigation
[Bug]: Shadow Call Intercept forces effort low on every gpt-5.6-luna request when luna is the target (or main model) — max turns silently downgraded #2706
Description
Client or integration
Codex CLI
Area
Proxy and routing
Summary
gpt-5.6-luna selected with [max] reasoning returns near-instant, shallow responses through the proxy, while the same model/effort directly against OpenAI thinks for a long time. Root cause: Shadow Call Intercept was enabled with gpt-5.6-luna itself as the target model, and the intercept's slug-only matching treats every luna request — main turns included — as a helper call, forcing reasoning.effort to low on the wire.
Mechanism (v2.33.0):
shouldInterceptShadowCall(src/lib/shadow-call.ts) matches the model slug alone, unconditionally (deliberate since In Codex 0.147.0, background helper requests use gpt-5.6-luna. Even with OpenCodex's Shadow Call Intercept enabled, some Luna requests still reached OpenAI. #1684 — helper calls cannot be distinguished by headers).gpt-5.6-lunais both the helper slug and a legitimate main model.- The rewrite in
src/server/responses/core.ts(~2306–2330) replaces the model and forces effort:parsed.options.reasoning = "low"+_rawBody.reasoning = { effort: "low" }. When the configured target is luna (self-target), the model rewrite is a no-op but the effort clamp still applies to every request. Self-interception is pure loss. logCtx.requestedEffortis captured at ~2296, before the rewrite — sousage.jsonl/ the Logs page showrequestedEffort: "max"while the wire effort waslow. UnlikeapplyEffortCap(which annotatesfrom->to), the shadow rewrite leaves the log stale, which makes this very hard to diagnose from logs.
Evidence from usage.jsonl (incident window Aug 26 20:29–22:21 local, 258 consecutive requests): every entry has resolvedModel: "gpt-5.6-luna", provider: "openai", requestedEffort: "max", and shadowCallRewrittenFrom: "gpt-5.6-luna". reasoningOutputTokens on those max turns: 13–31 (median ~24). Earlier non-intercepted max turns on the same setup reach 5,000+ reasoning tokens and 30+ minute turns. The four hard turns the user actually noticed were all inside the window.
Four concrete defects:
- No self-target guard (runtime):
shadowCallIntercept.modelmatching a shadow source model is accepted and silently degrades every request for that model tolow. The management API (/api/shadow-call-settings) validates shape but not self-intersection. - Slug-only matching hijacks luna main turns (design gap): with any target configured, a user whose main model is luna gets all main turns rerouted at
loweffort. [Bug]: Codex App now sends gpt-5.6-luna helper requests on every message and turn completion (previously only title generation) #2157 established Codex App emits luna helper calls on every turn, so operators are encouraged to enable the intercept — but nothing warns that this is incompatible with luna as a main model. - GUI offers the source model as target:
shadowCallModelOptions(gui/src/pages/dashboard-shared.ts:383) lists every active model, includingopenai/gpt-5.6-lunaitself. That is exactly how this config was reached from the dashboard. - Misleading logs:
requestedEffortis recorded pre-rewrite (see above).
Suggested fixes: reject (or skip at runtime) a target that matches a shadow source model; exclude shadow source models from the target dropdown; annotate the effort rewrite in the request log (max->low) like applyEffortCap does; document/warn that enabling the intercept while luna is the main model downgrades main turns.
Reproduction
ocx startwith the built-inopenaiforward provider logged in (2.33.0).- Dashboard → Models → enable Shadow Call Intercept; in the target dropdown pick
openai/gpt-5.6-luna(it is offered). - In Codex CLI, select
gpt-5.6-luna [max]as the main model and send a genuinely hard task (one that normally thinks for minutes). - Observe: responses return in seconds with shallow reasoning. Direct OpenAI with the same model/effort on the same task thinks for a long time.
- Inspect
~/.opencodex/usage.jsonl: every turn carriesshadowCallRewrittenFrom: "gpt-5.6-luna"andrequestedEffort: "max", withreasoningOutputTokensin the tens.
Variant of step 2 with any other target (e.g. zai/glm-5.3-flash) still reproduces the user-visible problem for luna-main users: every main turn is rerouted to the target model at low effort (per the unconditional matching introduced for #1684).
Version
2.33.0
Operating system
macOS 26.5.1
Provider and model
openai / gpt-5.6-luna
Logs or error output
# usage.jsonl entries during the incident window (aggregate; identifying fields removed)
# all 258 requests in the window, including the user's [max] main turns:
{
"provider": "openai",
"resolvedModel": "gpt-5.6-luna",
"requestedModel": "gpt-5.6-luna",
"shadowCallRewrittenFrom": "gpt-5.6-luna",
"requestedEffort": "max", # captured BEFORE the rewrite — misleading
"status": 200,
"durationMs": 9396,
"usage": { "inputTokens": 194311, "outputTokens": 101,
"reasoningOutputTokens": 13 } # wire effort was "low"
}
# earlier non-intercepted [max] turns, same provider/model/setup:
# reasoningOutputTokens up to 5083, durationMs up to 2203997 (~37 min)Screenshots and supporting files
n/a
Redacted configuration
{
"defaultProvider": "openai",
"shadowCallIntercept": {
"enabled": true,
"model": "openai/gpt-5.6-luna"
}
}(State at incident time; intercept has since been disabled, after which max reasoning behaves normally.)
Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
lidge-jun commentedon Aug 26, 2026
리뷰 · 우선순위 78 / 80
설명
이 이슈는 Shadow Call Intercept 대상 모델을
gpt-5.6-luna자신으로 둔 설정에서 luna 메인 턴까지 helper 로 취급되어reasoning.effort가low로 강제되는 버그를 보고합니다. 직접 OpenAI 로는 max 로 길게 생각하는데 프록시만 얕은 답이 나옵니다. 자기 자신으로 intercept 하면 모델 rewrite 는 no-op 이고 effort clamp 만 남아 순손실입니다.current
devHEADee182a1e0과 일치합니다.shouldInterceptShadowCall(src/lib/shadow-call.ts56-60줄) 는 슬러그만 보고 무조건 매칭합니다(#1684 이후 helper 헤더 구분 불가).src/server/responses/core.ts2410-2426줄은 매칭 시 모델을_sci.model으로 바꾸고parsed.options.reasoning = "low"와 raw body effort low 를 강제합니다.logCtx.requestedEffort는 그 직전(2403줄)에 캡처되어 로그상 원래 요청과 다를 수 있습니다.luna 가 source 이자 target 이면 모든 luna 요청이 low 로 떨어집니다. 사용자 체감이 큰 고심각도 라우팅 버그입니다. 설정 실수에 가깝지만 제품이 자기 타겟을 허용·저장한다면 가드가 필요합니다.
types.ts/config.ts분할로 닫을 대상이 아닙니다. 미리보기 배포는 계획에 없습니다.src/lib/shadow-call.ts:56 - shouldInterceptShadowCall 이 슬러그 단독 매칭입니다
src/server/responses/core.ts:2413 - intercept 시 모델 rewrite + effort low 강제입니다
src/server/responses/core.ts:2423 - self-target 에서도 effort clamp 가 남습니다
src/server/responses/core.ts:2403 - requestedEffort 가 clamp 전에 기록됩니다
경로/심볼 - source 와 target 이 같으면 순수 손실이므로 설정/런타임 거부가 필요합니다
메인테이너의 판단이 필요한 지점
너의 추천
열어 두세요. 최우선 버그에 가깝습니다. (1) target 모델이 source 매칭이면 intercept 를 no-op 하거나 설정을 거절하고, (2) 회귀 테스트로 luna self-target + max effort 가 low 로 안 바뀌는지 잠그세요. 문서에 luna 를 target 으로 두지 말라는 경고도 추가하세요. 미리보기 배포는 계획에 없습니다.
이 댓글은 grok-bot이 작성했습니다
lidge-jun commentedon Aug 28, 2026
Fixed on
devby commit d2e9920 (PR #2849).Three things changed:
lowunconditionally, which is what silently downgraded your max-turn requests.Documentation was corrected alongside it —
docs/shadow-call-intercept.mdand the configuration reference in all eight locales still claimed replacements run at low effort.Verified with
tests/responses-shadow-intercept.test.ts(20 pass) and the GUI picker test (6 pass), both driven RED first, plus typecheck, GUI lint, locale parity, and a 401-page docs build.This PR targeted
dev, so GitHub did not auto-close the issue — closing manually.test(responses): keep the #4129 combo fixture off the fixed-endpoint …
fix(responses): let a combo shadow-call target enter the failover loop
fix(responses): let a combo shadow-call target enter the failover loop
test(responses): keep the lidge-jun#4129 combo fixture off the fixed-…