The Copilot API enforces per-account prompt token limits (128K individual,
168K business) that differ from the static 200K context length advertised
by the proxy. This mismatch caused Claude Code to accumulate context
beyond the actual limit, triggering "prompt token count exceeds the limit
of 128000" errors.
Changes:
- Extract max_prompt_tokens and max_output_tokens from the Copilot
/models API response (capabilities.limits) and use them as the
authoritative ContextLength and MaxCompletionTokens values
- Add CopilotModelLimits struct and Limits() helper to parse limits
from the existing Capabilities map
- Fix GitLab Duo context-1m beta header not being set when routing
through the Anthropic gateway (gitlab_duo_force_context_1m attr
was set but only gin headers were checked)
- Fix flaky parallel tests that shared global model registry state
- Strip SSE `data:` prefix before normalizing reasoning_text→reasoning_content
in streaming mode; re-wrap afterward for the translator
- Iterate all choices in normalizeGitHubCopilotReasoningField (not just
choices[0]) to support n>1 requests
- Remove over-broad tool-role fallback in isAgentInitiated that scanned
all messages for role:"tool", aligning with opencode's approach of only
detecting active tool loops — genuine user follow-ups after tool use are
no longer mis-classified as agent-initiated
- Add 5 reasoning normalization tests; update 2 X-Initiator tests to match
refined semantics
This commit addresses three issues with Claude Code through GitHub
Copilot:
1. **Premium request inflation**: Responses API requests were missing
Openai-Intent headers and proper defaults, causing Copilot to bill
each tool-loop continuation as a new premium request. Fixed by adding
isAgentInitiated() heuristic (checks for tool_result content or
preceding assistant tool_use), applying Responses API defaults
(store, include, reasoning.summary), and local tiktoken-based token
counting to avoid extra API calls.
2. **Context overflow**: Claude Code's modelSupports1M() hardcodes
opus-4-6 as 1M-capable, but Copilot only supports ~128K-200K.
Fixed by stripping the context-1m-2025-08-07 beta from translated
request bodies. Also forwards response headers in non-streaming
Execute() and registers the GET /copilot-quota management API route.
3. **Thinking not working**: Add ThinkingSupport with level-based
reasoning to Claude models in the static definitions. Normalize
Copilot's non-standard 'reasoning_text' response field to
'reasoning_content' before passing to the SDK translator. Use
caller-provided context in CountTokens instead of Background().
- Fix X-Initiator detection: check for any assistant/tool role
in messages instead of only the last message role, matching
the correct agent detection for multi-turn tool conversations
- Add x-github-api-version: 2025-04-01 header for API compatibility
- Support Business/Enterprise accounts by using Endpoints.API from
the Copilot token response instead of hardcoded base URL
- Fix Responses API vision detection: detect vision content before
input normalization removes the messages array
- Add 8 test cases covering the above fixes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add response.function_call_arguments.delta handler for tool call parameters
- Rewrite normalizeGitHubCopilotResponsesInput to produce structured input
array (message/function_call/function_call_output) instead of flattened
text, fixing infinite loop in multi-turn tool-use conversations
- Skip flattenAssistantContent for messages containing tool_use blocks,
preventing function_call items from being destroyed
- Add reasoning/thinking stream & non-stream support
- Fix stop_reason mapping (max_tokens/stop) and cached token reporting
- Update test to match new array-based input format
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The normalizeGitHubCopilotResponsesTools filter required type="function",
which dropped Claude-format tools (no type field, uses input_schema).
Relax the filter to accept tools without a type field and map input_schema
to parameters so tools are correctly sent to the upstream API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GitHub Copilot API rejects model names with suffixes (e.g. claude-opus-4.6(medium)).
The OAuthModelAlias resolution correctly maps aliases like 'opus(medium)' to
'claude-opus-4.6(medium)' preserving the suffix, but the executor must strip the
suffix before sending to the upstream API since Copilot only accepts bare model names.
Update normalizeModel in github_copilot_executor to strip suffixes using
thinking.ParseSuffix, matching the pattern used by other executors.
Also add test coverage for:
- OAuthModelAliasChannel github-copilot and kiro channel resolution
- Suffix preservation in alias resolution for github-copilot
- normalizeModel suffix stripping in github_copilot_executor