Repository navigation
chore: sync with upstream main through e5e6ad1e - #10
Conversation
Subscription keys return HTTP 403 client_attestation_denied when the request still looks like muse-build/1.3.0. Stamp the headers captured from Muse Code 1.4.14-R7309.1: muse-build/1.4.14, X-Client-Id tbh:exec, and a fresh session id set.
- Add `client.claude.show-codex-commentary` config and propagate the request-scoped display policy through HTTP and WebSocket Codex executors. - Mirror public Codex `commentary` messages into visible Claude text blocks alongside the `cpa:commentary:v1` thinking marker in streaming and non-streaming responses without altering genuine reasoning or token accounting. - Deduplicate only the exact adjacent mirrored commentary text block during Claude-to-Codex request replay when enabled. Closes: router-for-me#7129
- Add Capabilities.AuthProviders and MultiAuthProvider (Identifiers() []string) in sdk/pluginapi, bump sdk/pluginabi.SchemaVersion to 10, and forward AuthProviders in sdk/pluginhost - Extend internal/pluginhost to register, expose, and route multiple OAuth login identifiers per plugin across StartLogin, PollLogin, RefreshAuth, ParseAuths, and ExchangeCallback, while preserving per-credential oauth_provider/portal attribution for shared credential providers - Expose oauth_providers alongside primary oauth_provider in RegisteredPluginInfo and GET /v0/management/plugins, and support target-credential re-login (StartOAuthV8) across sibling portals Closes: router-for-me#7126
…ion.parts - Remove empty JSON part objects from request.systemInstruction.parts in ConvertGeminiRequestToAntigravity after system_instruction key normalization so upstream Cloud Code Assist does not fail Part.data oneof deserialization with HTTP 400. - Delete request.systemInstruction when all of its parts are empty objects while preserving zero-copy pass-through when all parts are non-empty. Closes: router-for-me#7136
…ystemInstruction sanitize
The flusher rebuilt its pending map from every release group on each release-flush tick. Groups are retained after acknowledgement, so a long-lived credential pool paid O(groups) allocations and lock time every 250ms even with no in-flight requests. Track unacknowledged groups in a dedicated pending map, return before allocating when it is empty, and arm the flush timer only while work is pending. The timer is re-armed by MarkDirty's wake signal, so release latency is unchanged. Tested with a regression test asserting an idle flush over 1,000 acknowledged groups performs zero allocations, plus a test that an acknowledged group leaves the pending set.
- Exempt HTTP 400 responses whose JSON error message starts with "Your credit balance is too low" from request-fault classification in clienterror. - Apply 30-minute payment_required cooldown, cooldown view reason mapping, and session-affinity rebinding so depleted Anthropic API keys fail over to fallback credentials. Closes: router-for-me#7120
…5.5 models - Add supported thinking levels (`low`, `medium`, `high`) to the Antigravity `claude-opus-5-5-high` and `claude-sonnet-5-5-high` model definitions so hybrid capability validation preserves explicit `thinkingLevel` settings instead of converting them to `thinkingBudget`. - Add regression tests covering `ApplyRequestThinking` and streaming/non-streaming Antigravity executor requests for both Claude 5.5 models. Closes: router-for-me#7133
Add an optional `connection-pool` block to the api-key config surface: group level on codex/claude/gemini/vertex/openai-compatibility entries and per key inside `keys`. There is deliberately no global switch, so enabling reuse for one key cannot change the transport lifecycle of another provider or OAuth identity. - `connection-pool` joins the v8 shared key fields, so a group block is flattened into every key and each key may override it. - A per-key block merges onto the group block: fields the key does not mention (or sets to null) keep inheriting the group value. Replacing the whole block would silently drop the group's `enabled: true` when a key only overrides a timeout. - The resolved block reaches executors on `Auth.ProxyConnectionPool` (deep-copied on clone so auth snapshots never share mutable state). Writing the block is the opt-in; only an explicit `enabled: false` turns pooling off for that key. - OpenAI-compatible keys use the same field-level inheritance against their provider group default, and watcher config diffs report connection-pool changes on hot reload.
Give the generic proxy-aware clients a reusable transport per api-key credential, driven by the `connection-pool` block added in the previous commit. - Pools are keyed by credential scope (auth ID, then path/source, then a digest of refresh/access/API-key material, then anonymous), normalized proxy target (authenticated proxies fold a SHA-256 digest of user:pass into the key so each proxy identity or sticky-session username keeps its own pool without storing plaintext secrets in the cache key or logs), and the resolved settings. Two keys therefore never share a TCP/TLS connection regardless of whether the proxy is unauthenticated, authenticated, direct, or inherited. - Capacity is a 512-entry LRU: one key keeps a warm pool, 100 keys keep one each, and 100000 keys fall back to the historical one-transport-per-request behavior when the cache is full. Each transport is bounded to MaxIdleConnsPerHost instead of the cloned default of 100 idle sockets, so idle sockets stay at capacity*max no matter how many keys exist. - Dead peers are detected without request timeouts: 30s TCP keepalive on every dialed connection (including SOCKS5, which used proxy.Direct before) plus the idle timeout, 300s by default. Measured against ollama.com, an idle pooled connection was still reusable after 600s; the 90s that used to bound this path was net/http's own IdleConnTimeout, not an upstream limit. - Explicit 0 or negative values disable keep-alives (including when proxy-url is empty), invalid durations are parsed once per pool instead of warning per request, and pooling works with or without an explicit proxy-url while keeping priority for context-injected RoundTrippers when proxy-url is empty. - Config snapshots carry a generation so a request holding an obsolete snapshot cannot repopulate the cache after a reload. Generation 0 means the config never passed the fence and stays poolable, so callers that build their own config are not silently downgraded.
Describe the api-keys `connection-pool` block in the shipped template: writing it is the opt-in, pools stay per key, authenticated proxy URLs are isolated by a digest of their proxy credentials, the defaults are a 300s idle window and two idle connections per host, and zero or negative values disable keep-alives. Note that no global pooling switch exists and that Antigravity keeps its own antigravity.connection-pool settings.
- Keep an empty connection-pool block as an opt-in. Synthesising it to nil
silently disabled pooling for `connection-pool: {}` and for keys inheriting
an empty group block.
- Fold the transport kind into the pool key and pool the Claude Code uTLS
transport per credential. Anthropic upstreams previously shared one
transport across keys and ignored the key's pool settings.
- Fence generations per publisher instead of process-wide. Building or
reloading one service no longer retires the pools another service is
still serving, which degraded it to one transport per request.
…utors, and close retired pools outside fence lock
…tion-pool feat(proxy): add opt-in per-api-key upstream connection pooling
Subscription keys return HTTP 403 client_attestation_denied when the request still looks like muse-build/1.3.0. Stamp the headers captured from Muse Code 1.4.14-R7309.1: muse-build/1.4.14, X-Client-Id tbh:exec, and a fresh session id set.
…ants - Resolve strict "<level>-fast" requests (parenthesis suffix or body thinking_level) to "<base>-<clamped-level>-fast" in ResolveDevinChatModelUID instead of silently falling back to the default effort. - Treat a bare "fast" request as the default-effort fast variant so "(fast)" no longer resolves to the plain default UID; private aliases, special families, and the lightning tier keep their existing handling. - Add regression coverage for the fast suffix and body-level forms plus pinning cases for private aliases and lightning models. Closes: router-for-me#7144
… manual refresh - Fail forced refreshes when rejected refresh tokens remain unchanged without disk rotation and preserve rejected access token markers across no-op refreshes. - Require a valid active access token when sign-in is required before restoring active routing status during refresh and lifecycle updates. - Return the redacted credential entry view from the v8 management credential refresh endpoint. Closes: router-for-me#7135
…lusher-idle fix(home): keep the release flusher idle when nothing is pending
…h the stream translator
Plugin executor streams whose output format equaled the requested format bypassed stream translation, so SSE-framed chunks ("data: {...}" plus "data: [DONE]") reached the OpenAI/Gemini handlers unchanged. Those handlers add their own "data:" prefix and terminal marker, producing a doubled "data: data:" prefix and a duplicated [DONE] on the wire.
Built-in executors always run TranslateStream, and the registered same-format translators strip that framing. Route same-format plugin executor streams through the same translation instead of returning the channel or payload unchanged. Formats without a same-format translator keep the existing behavior via the TranslateStream fallback, which returns the payload unchanged, and the OpenAI Responses usage-details fixup still runs after translation.
Co-Authored-By: Claude Code <noreply@anthropic.com>
- Add `IsClaudeSessionDatePinKVActive` and integrate persistent KV store backing for Claude session date pins. - Ignore `session-date-pin-days` and store pins permanently when local bbolt or Home KV is active. - Support concurrent CAS-based repair and transient outage recovery for pinned session dates. Closes: router-for-me#6799
- Start TTFT timing before invoking direct plugin executors in both non-streaming and streaming handler paths. - Record first-packet arrival for non-streaming responses and observe protocol-aware stream chunk TTFT events before publishing usage. Closes: router-for-me#7253
…mage-26 feat(codex): add support for gpt-image-2.6 models
…translator - Expose client-executed tool_search declarations and tools discovered in tool_search_output items as Chat Completions functions while preserving user tool names on collision. - Normalize replayed client tool_search_call and tool_search_output items into function calls and tool result summaries during request conversion. - Restore calls to the bridged search function as client tool_search_call items in streaming and non-streaming responses without emitting function argument delta events. Closes: router-for-me#7254
…e translator - Parse typed delta.content arrays in streaming responses to emit sequential thinking_delta and text_delta events with proper content block transitions. - Support thinking parts in non-streaming message.content arrays and emit top-level reasoning blocks before text blocks across both non-streaming paths. Closes: router-for-me#7255
…aude refusal identity, and Home stream error logs
…equest-fault-and-error-log fix(clienterror,logging): classify mid-stream cyber blocks as 400 and persist error logs
…ctly to Codex images endpoint
- Forward terminal 429 quota and model-cooldown errors with trusted Retry-After headers when websocket credential failover is exhausted. - Suppress 1012 HTTP replay on pinned continuation 429 failures when no alternative local or Home credential is eligible. - Add regression tests covering multi-credential rotation, single-credential exhaustion, expired alternatives, and Home failover. Closes: router-for-me#7256
…image-bypass-siwc fix(codex,auth): bypass SIWC for OpenAI image requests and route directly to Codex images endpoint
…asAlternativeTargetForModel
- Zero the dequeued queueItem slot in Manager.run before reslicing the queue so the backing array does not retain references to consumed request contexts and usage records while idle. - Add a weak-pointer garbage collection regression test verifying that consumed request context payloads and record reference fields are reclaimed when the manager is idle. Closes: router-for-me#7267
- Skip auto-injecting hosted image_generation tools on Responses-Lite models and strip unsupported complexity fields from declared image_generation tools on ChatGPT Codex /responses requests. - Route forced hosted image_generation turns on Responses-Lite models to the compatible GPT-Image-2 base model (default gpt-5.4-mini) with normalized reasoning configuration. - Map upstream image_generation_user_error terminal events to HTTP 400 Bad Request with actionable remediation guidance for unsupported complexity errors. Closes: router-for-me#7270
…super, and gpt-6-1-terra
… availability-neutral - Classify HTTP 501 and typed unsupported count_tokens errors as availability-neutral in isCountTokensEndpointNotFoundError while preserving explicit model-not-found, auth, and quota cooldowns. - Expose ErrorCode() on plugin RPC and auth error types so resultErrorFromError retains structured plugin error codes during count_tokens execution. - Add regression tests verifying that repeated count_tokens 501 responses neither suspend inference availability nor extend active model cooldowns. Closes: router-for-me#7271
…dels - Remove the unconditional X-Tbh-Contributor-Mode header from applyMetaClientIdentity so standard Meta model requests do not send the training opt-in signal. - Resolve the effective upstream model after payload rules and set X-Tbh-Contributor-Mode: 1 only when the target model ends with -contributor. - Add regression tests covering standard and -contributor models across Execute, ExecuteStream, and payload model overrides. Closes: router-for-me#7272
- Register gpt-6-chat-thinking with 272k context, 128k max completion tokens, and thinking levels (low/medium/high/xhigh/max) across four model groups. - Enable native web_search capability and configuration update support for the new model entry.
- Place Gemini tool results in user messages while preserving call pairing. - Omit incompatible thought signatures and normalize native Base64 for Go clients. Closes: router-for-me#7279
- Raise small Claude output budgets to the Codex minimum before payload rules. - Cover model selection and budget boundaries across HTTP and WebSocket paths. Closes: router-for-me#7282
- Include the trusted retry delay in Claude cooldown errors. - Verify response headers and stream messages across cooldown cases. Closes: router-for-me#7285
Treat preserved terminal responses as request-scoped unless the executor marks a credential-specific rejection. Reuse the existing request error policy to stop credential retries, unauthorized refresh, and cooldown without changing explicit continue rules or ordinary failover. End stopped Home stream selections without waiting for a redispatch release acknowledgement that could replace the original response. Cover local and Home execute, count, stream and bootstrap paths, exact response preservation, missing release acknowledgements, and terminal 401 refresh suppression. Refs: router-for-me/CLIProxyAPIHome#178
…minal-request-boundaries fix(auth): preserve terminal responses across execution paths
- Detect compaction_trigger in DevinExecutor Execute/ExecuteStream, generate the summary through the same Devin model, and synthesize a sealed single-item compaction response and SSE stream via the shared helpers. - Expand replayed CPA compaction capsules into summary context before translation and drop foreign compaction items, so repeated compaction keeps prior context. - Add a native Devin regression test covering streaming/non-streaming triggers, summary history and tool preservation, payload-rule final barrier, capsule replay, repeated compaction, and OriginalRequest-only triggers. Closes: router-for-me#7291
…sted-image-request-scope fix(codex): reject unsupported hosted image requests
Conflicts: - internal/pluginhost/host.go: fork side was a gofmt-only change; took upstream. - sdk/api/handlers/openai/openai_handlers.go: kept the fork's codexlive/auth imports for the transcription model listing; dropped tidwall gjson/sjson per upstream's migration to moego.
- Close the local bbolt store before TempDir cleanup in kvstore, home, and executor tests; Windows cannot remove an open database file. - Wait for in-flight websocket handlers before TempDir cleanup in TestServerResponsesWebsocketReauthorizesEachTurn (failed 7/30 on Windows). - Allow 3x the jittered initial retry delay in the heartbeat failover test, matching sibling tests (failed 2/15). - Record the reviewed envelope copy in kvstore/bolt_store.go in the no-copy invariant list; it writes into a freshly allocated buffer.
|
Important Review skippedToo many files! This PR contains 920 files, which is 620 over the limit of 300. To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch. Usage-priced reviews support at most 300 files.
|
Merges 121 upstream commits (through e5e6ad1) into the fork.
The main reason is upstream
0130d6f3(router-for-me#7197). It keeps a Fable-only 429 scoped to the Fable model when Anthropic omits the 5h headers. Without it, one Fable request on dietpi on 2026-11-07 benched the only Claude credential for 6h45m and blocked Opus and Sonnet.Conflicts
internal/pluginhost/host.go: the fork's change here was formatting only. Took upstream.sdk/api/handlers/openai/openai_handlers.go: kept the fork'scodexliveandauthimports, which the transcription model listing in/v1/modelsuses. Droppedtidwall/gjsonandsjsonbecause upstream moved tomoego.Fork features
The Codex transcription and live dictation routes, the transcription model in
/v1/models, and the quota dashboard all merged without conflicts.Test fixes
All of these also fail on plain upstream/main on Windows:
TestServerResponsesWebsocketReauthorizesEachTurnfailed 7/30 because the handler kept writing a log after the test ended. The test now waits for in-flight handlers (30/30 after).TestServiceHeartbeatFailoverDoesNotDrainBlockingScopefailed 2/15. It allowed 1s for a retry whose jittered delay can be up to 1s. It now allows 3x that, matching the similar tests next to it (20/20 after).TestInPlaceByteWritesAreReviewedflagged the newkvstore/bolt_store.goenvelope copy. The copy goes into a freshly allocated buffer, so it is added to the reviewed list.Verification
go build ./cmd/serverpasses.go test -count=1 -p 4 ./...passes in all 103 packages.-p, several timing-sensitive tests (TestServerRejectsIncompleteUnauthenticatedRequestBodiesPromptly, two management OAuth tests,TestDevinRemoteOAuthFlow) can fail when the machine is saturated. They pass on their own and at-p 4.