Skip to content

Commit 622d60a

Browse files
committed
Merge remote-tracking branch 'origin/codex/wt3-exec' into codex/wt2-zero-leak-impl
2 parents a468652 + 0b30283 commit 622d60a

32 files changed

Lines changed: 823 additions & 103 deletions

devlog/_plan/260802_wt3_provider_wire/000_plan.md

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
# wt3 — Provider wire correctness (research)
22

3-
Worktree: `/Users/jun/.codex/worktrees/260802-wt3-provider-wire` (branch `codex/wt3-provider-wire`, off `dev`).
3+
Executing worktree: `/Users/jun/.codex/worktrees/8e2b/opencodex` (branch `codex/wt3-exec`, off dev@478354ee8). A spare prepared worktree also exists at `/Users/jun/.codex/worktrees/260802-wt3-provider-wire`.
44
Provider-adapter/wire bugs; all must-fix regardless of PR quality.
55

6+
## Roadmap map (work-phase → decade doc)
7+
8+
| Work-phase | Bug | Decade doc |
9+
|------------|-----|------------|
10+
| wp-a | A — Copilot mixed-wire (#746/#748) | `010_bug_a_copilot_mixed_wire.md` |
11+
| wp-b | B — DeepSeek service_tier (#860) + #875 triage | `020_bug_b_deepseek_service_tier.md` |
12+
| wp-c | C — Claude 1M windows (#839+#854) | `030_bug_c_claude_1m_windows.md` |
13+
| (follow-up, not this goal) | D — hosted image tools (#616/#837) | to be written when picked up |
14+
615
## Scope
716

817
### Bug A — PR #746 / issue #748: Copilot Responses-only models routed to chat completions
@@ -13,7 +22,7 @@ Provider-adapter/wire bugs; all must-fix regardless of PR quality.
1322

1423
### Bug B — PR #860 (+ issue #875): DeepSeek `service_tier` must be capability-gated
1524

16-
- Root cause: `fastMode` injects `service_tier` unconditionally on Responses routes; DeepSeek does not support the field. PR #860 adds a provider-level `supportsServiceTier` capability: canonical OpenAI Responses providers support it, DeepSeek explicitly rejects it (strip the field), unclassified custom providers keep caller-supplied values.
25+
- Root cause: `fastMode` injects `service_tier` unconditionally on Responses routes; DeepSeek does not support the field. PR #860 adds a provider-level `supportsServiceTier` capability: canonical OpenAI Responses providers support it, DeepSeek explicitly rejects it (strip the field), unclassified custom providers FAIL CLOSED (strip) unless explicitly configured with `supportsServiceTier: true` — the reviewed final-head semantics, which supersede the PR body's original "preserve caller-supplied values" wording.
1726
- Fresh corroboration: issue #875 (2026-08-02) "DeepSeek V4 Flash Responses route stalls after tool calls" — same wire family; executing session must check whether #875 is the same root cause or a second defect before closing either.
1827
- Grounding: `src/adapters/openai-responses.ts`, `src/server/responses/core.ts`, `src/types.ts`.
1928

@@ -31,11 +40,38 @@ Provider-adapter/wire bugs; all must-fix regardless of PR quality.
3140

3241
| # | Claim | Source | Status |
3342
|---|-------|--------|--------|
34-
| 1 | Copilot serves some models Responses-only | gpt-5.4 verified (BerriAI/litellm#23332, exact `unsupported_api_for_model` error); gpt-5.6-sol lead only (JetBrains LLM-29711: function tools + reasoning_effort rejected on `/chat/completions`); same pattern for gpt-5-codex (opencode #2758) | verified (5.4) / lead (sol) |
35-
| 2 | DeepSeek rejects/mishandles `service_tier` | No primary evidence either way (api-docs.deepseek.com does not list the field; Anthropic-compatible API marks it "Ignored" — different endpoint) | unresolvedcapability-gating is safe regardless; do not claim rejection without a live probe |
36-
| 3 | DeepSeek Responses route stalls after tool calls (hosted api.deepseek.com) | Stall reports are NIM/vLLM compatibility paths, not hosted; verified hosted failure mode is a 400 when `reasoning_content` is omitted after a tool call (official Thinking Mode docs; claude-code-router#1378) | contradicted as stated — #875 may be a `reasoning_content` echo defect, not #860's root cause; executing session must split them |
43+
| 1 | Copilot serves some models Responses-only | gpt-5.4 verified (BerriAI/litellm#23332, exact `unsupported_api_for_model` error); gpt-5.6-sol verified to the same standard (JetBrains LLM-29711: function tools + reasoning_effort rejected on `/chat/completions` + pi.dev Responses declaration + #748 field run); same pattern for gpt-5-codex (opencode #2758). Full per-model table below | verified (7 models built-in; nano lead-only, excluded) |
44+
| 2 | DeepSeek rejects/mishandles `service_tier` | Official Responses docs: field unsupported but unsupported params are SILENTLY IGNORED (api-docs.deepseek.com/guides/responses_api/, opened 2026-08-02 by researcher) | resolvedstrip as compatibility policy; NOT a 400 and NOT #875's cause |
45+
| 3 | DeepSeek Responses route stalls after tool calls (hosted api.deepseek.com) | Local root cause found: `sanitizeReasoningInputContent()` (`src/adapters/openai-responses.ts:35`, called :1027 for every Responses provider) blanks plaintext reasoning content on continuations; schema supports `reasoning_text` (`src/responses/schema.ts:23`); DeepSeek native contract accepts it. Residual: "no follow-up request sent" piece unexplained locally | verified local defect (separate from #860) + open external residual — fixed in wp-b, #875 commented not closed |
3746
| 4 | Claude Opus 4.6/4.7 + Sonnet 4.6 are documented at 1M context | Anthropic official: Opus 4.6 (1M beta, 2026-02-05), Opus 4.7 (1M, 2026-04-16, migration guide), Sonnet 4.6 (1M beta, 2026-02-17); model overview cross-check | verified |
3847

48+
## Bug A model evidence (consumed by `010_bug_a_copilot_mixed_wire.md`)
49+
50+
Selection rule: built-in = field report in issue #748 AND independent corroboration. Resolver lookup is exact normalized-ID (`trim().toLowerCase()`), so dated/bracket-suffixed IDs intentionally miss.
51+
52+
| Model | #748 field report | Independent corroboration | Status | Built-in |
53+
|---|---|---|---|---|
54+
| `gpt-5.3-codex` | yes (live run) | pi.dev/models/github-copilot/gpt-5-3-codex declares `openai-responses` | field-verified, corroborated | yes |
55+
| `gpt-5.4` | yes (exact tools+reasoning chat failure + successful Responses run) | BerriAI/litellm#23332 (`unsupported_api_for_model`); pi.dev/models/github-copilot/gpt-5-4 | verified Responses-required | yes |
56+
| `gpt-5.4-mini` | yes | pi.dev/models/github-copilot/gpt-5-4-mini | field-verified, corroborated | yes |
57+
| `gpt-5.5` | yes | pi.dev/models/github-copilot/gpt-5-5 | field-verified, corroborated | yes |
58+
| `gpt-5.6-luna` | yes | pi.dev/models/github-copilot/gpt-5-6-luna | field-verified, corroborated | yes |
59+
| `gpt-5.6-sol` | yes (chat rejection + successful Responses run) | pi.dev/models/github-copilot/gpt-5-6-sol declares `openai-responses`; JetBrains LLM-29711 independently shows chat rejects sol under function tools + reasoning_effort (the Codex-agent request shape) | field-verified, corroborated (audit round-2: same evidence class as luna/terra — excluding it applied the rule inconsistently) | yes |
60+
| `gpt-5.6-terra` | yes | pi.dev/models/github-copilot/gpt-5-6-terra | field-verified, corroborated | yes |
61+
| `gpt-5.4-nano` | NO — absent from the 2026-07-30 captured catalog, never field-run | GitHub supported-models list + pi.dev/models/github-copilot/gpt-5-4-nano | lead-only | NO (`modelAdapters` documented) |
62+
63+
Why nano is the only exclusion, given #748 reports seven models: the two-leg rule (field report AND independent corroboration) is applied uniformly — sol meets both legs exactly as luna/terra do, so it is in; nano has no field report (never present in the captured catalog), so it stays out regardless of catalog/metadata labels. Sol's demonstrated chat failure is request-shape-conditional (tools + reasoning), which is precisely the Codex-agent traffic this bug is about; its bare-string default is safe for text-only chat clients because inbound chat is translated to the verified-working Responses wire rather than dropped.
64+
65+
## Bug B research findings (consumed by `020_bug_b_deepseek_service_tier.md`)
66+
67+
2026-08-02, sol-medium researcher; sources inline.
68+
69+
- PR #860's capability design fits this tree and applies cleanly (`git apply --check` passed on the dev lineage). Its file map is adopted with two corrections from its open review threads: the canonical-`openai` test must prove REGISTRY BACKFILL (not hardcode the field), and localized docs must not keep contradictory blanket wording.
70+
- Official DeepSeek Responses docs list `service_tier` as unsupported but say unsupported Responses parameters are SILENTLY IGNORED (api-docs.deepseek.com/guides/responses_api/). Stripping remains sensible compatibility policy, but `service_tier` cannot explain #875's stall.
71+
- #875 root cause (local, separate from #860): the continuation store preserves reasoning items (`src/responses/state.ts:699`, `:806`, `:837`; recorder installed at `src/server/responses/core.ts:1554`), DeepSeek stateless cleanup (`src/adapters/openai-responses.ts:1003`) does not remove them, but then `sanitizeReasoningInputContent()` (`src/adapters/openai-responses.ts:35`, blanks every non-empty reasoning item's `content` to `[]` at :45-56) is invoked at `:1027` for EVERY Responses provider. The function is OpenAI/ChatGPT-backend-motivated but unscoped. The local schema explicitly supports plaintext `{type:"reasoning_text"}` (`src/responses/schema.ts:23`, `:52`), and DeepSeek's native Responses contract accepts plaintext reasoning content — so current ocx deterministically sends DeepSeek an emptied reasoning item on every continuation. DeepSeek's registry `preserveReasoningContentModels` protects only Chat-Completions serialization, not native passthrough.
72+
- Evidence calibration (audit round-1): DeepSeek's Responses docs confirm plaintext reasoning items are accepted and merged into adjacent assistant messages; the must-replay-on-tool-call-continuation rule is explicit only in the CHAT Thinking-Mode docs — mapping it to native Responses is an inference and is labeled as such in code comments.
73+
- Caveat recorded: the reasoning defect only fires once a follow-up request REACHES ocx; it cannot by itself explain #875's "no follow-up HTTP request sent at all" observation, which may be a separate client/SSE handoff issue. #875 stays open with a comment; the reasoning replay defect is fixed here as the local half.
74+
3975
## Out of scope
4076

4177
- New provider presets (covered by separate enhancement PRs).
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# 010 — Bug A: Copilot mixed-wire routing (#746 / #748)
2+
3+
Consumed by work-phase wp-a. Verified against dev@478354ee8. Model-by-model evidence and source URLs live in `000_plan.md` (claim ledger + evidence table) — this doc carries only the decision and its implementation consequences.
4+
5+
## Mechanism (decided)
6+
7+
The tree ALREADY owns the correct mechanism; this fix declares data, not new routing:
8+
9+
```text
10+
hard wire pin
11+
→ explicit user modelAdapters
12+
→ registry modelWireDefaults ← the fix adds entries here
13+
→ provider-wide adapter
14+
```
15+
16+
- `src/providers/registry.ts:101` — registry metadata owns mixed-wire defaults (`modelWireDefaults`).
17+
- `src/providers/registry.ts:140` — registry defaults stay separate from persisted user overrides.
18+
- `src/server/adapter-resolve.ts:14` — resolver implements the precedence above, preserving credentials/base URL through a copy.
19+
- `src/providers/registry.ts:1552` — registry defaults constrained to recognized destinations + the two OpenAI wires.
20+
- `src/server/responses/core.ts:1434` — final route resolves transport, then the effective model adapter.
21+
- `src/providers/github-copilot-transport.ts:29` — transport has no model argument; do NOT branch here.
22+
23+
Rejected alternatives (with reasons): provider-wide `openai-responses` (breaks Copilot's Claude/Gemini/GPT-4/gpt-5-mini chat models); transport-level switch (wrong owner, no model arg); runtime endpoint probing (quota-cost + nondeterminism; live discovery hints are not routing metadata); new config flag (`modelAdapters` is already the operator escape hatch).
24+
25+
## File map
26+
27+
- MODIFY `src/providers/registry.ts` (github-copilot entry at :1470) — add `modelWireDefaults` with the conservative verified set:
28+
`gpt-5.3-codex`, `gpt-5.4`, `gpt-5.4-mini`, `gpt-5.5`, `gpt-5.6-luna`, `gpt-5.6-sol`, `gpt-5.6-terra``"openai-responses"` (bare strings, every inbound — these models are Responses-required for agent traffic; translation keeps text-only chat clients working).
29+
- MODIFY the same entry's cold-start seed. Policy: ADDITIVE update, no removals (the seed is a cold-start fallback under `liveModels: true`; removals buy nothing and risk stale saved-config surprises). Exact before/after:
30+
- before: `models: ["gpt-4o", "gpt-4.1", "gpt-4.1-mini", "claude-sonnet-4", "gemini-2.5-pro"]`
31+
- after: `models: ["gpt-4o", "gpt-4.1", "gpt-4.1-mini", "claude-sonnet-4", "gemini-2.5-pro", "gpt-5-mini", "gpt-5.3-codex", "gpt-5.4", "gpt-5.4-mini", "gpt-5.5", "gpt-5.6-luna", "gpt-5.6-sol", "gpt-5.6-terra"]`
32+
- `defaultModel: "gpt-4o"` unchanged. `gpt-5-mini` is added as a verified CHAT model (present in #748's captured catalog, chat-served) — it keeps the chat regression fixture honest. `gpt-5.4-nano` is the ONLY lead-only model left out (no field run, absent from the captured catalog); it ships as a documented `modelAdapters` example. `providerConfigSeed()` copies this list into saved config (`src/providers/derive.ts:105`), so every added id has named evidence in `000_plan.md`.
33+
- NEW `tests/github-copilot-wire-defaults.test.ts` — focused suite (cases below).
34+
- DOCS `docs-site/src/content/docs/reference/configuration/providers.md` (the authoritative `modelAdapters` contract lives at its :79) + maintained locale equivalents (ko, ja, zh-cn, ru) — the table currently carries DeepSeek-only wording and would contradict the new Copilot behavior. `docs-site/src/content/docs/guides/providers.md` gets a short routing-precedence note naming the built-in Copilot defaults and the `modelAdapters` escape hatch for lead-only models.
35+
- NO CHANGES: `github-copilot-transport.ts`, `adapter-resolve.ts`, `types.ts`, `derive.ts`. The sampling/credential-replay parts of PR #746 are a separate parity/security unit — out of scope here.
36+
37+
## Selection rule (decision reference; full evidence in `000_plan.md`)
38+
39+
Built-in = field report in issue #748 AND independent corroboration. All seven Responses-required models meet it, including `gpt-5.6-sol`; `gpt-5.4-nano` alone fails it (no field-report leg) and stays out as a documented `modelAdapters` example. Lookup is exact normalized-ID (`trim().toLowerCase()`, `registry.ts:1568`) — no family/snapshot prefix matching.
40+
41+
## Acceptance + activation scenarios
42+
43+
1. `gpt-5.4` via the github-copilot preset resolves to the Responses wire and the upstream request goes to the Responses endpoint, never `/chat/completions`. Activation: captured-upstream-URL test (runtime-wire proof, not just resolver proof).
44+
2. All seven built-in models resolve Responses on all three inbound wires (Responses, Chat Completions, Anthropic inbound). Activation: parametrized resolver + URL tests.
45+
3. Explicit user `modelAdapters` override beats the registry default in BOTH directions: a listed Responses-default model (e.g. `gpt-5.4`) pinned back to chat proves the opt-out direction; an unlisted chat model (e.g. `gpt-5.4-nano`, or seeded `gpt-5-mini`) mapped to Responses proves the opt-in direction. Activation: precedence tests — note `gpt-5.6-sol` cannot serve as the opt-in case because it is itself a default.
46+
4. Chat-served Copilot models (`gpt-4o`, `gpt-4.1`, `gpt-4.1-mini`, `claude-sonnet-4`, `gemini-2.5-pro`, `gpt-5-mini`) still use chat completions. Activation: regression assertions on the seed's chat set (`gpt-5-mini` is newly seeded, not pre-existing — its assertion guards against accidental inclusion in `modelWireDefaults`).
47+
5. Unrelated providers are isolated (no wire change for non-copilot providers with same-named models). Activation: isolation test.
48+
6. Credentials/base URL preserved through the resolved copy. Activation: adapter-resolve test shape per `adapter-resolve.ts:14`.
49+
50+
## Verification gate
51+
52+
`bun test tests/github-copilot-wire-defaults.test.ts` + `bun run typecheck` + `bun run test` (registry is shared) + `bun run privacy:scan`.

devlog/_plan/260802_wt3_provider_wire/010_implementation.md

Lines changed: 0 additions & 53 deletions
This file was deleted.

0 commit comments

Comments
 (0)