Commit Graph

789 Commits

Author SHA1 Message Date
sususu
b3ed702e3a fix(claude): harden OAuth identity and native routing 2026-08-03 14:47:26 +08:00
sususu
fa6bc77f28 fix(claude): decode stacked response encodings 2026-08-03 14:47:26 +08:00
sususu
f63a925d15 fix(claude): replay measured OAuth wire, Fast and diagnostic profiles
Align the remaining measured OAuth wire profiles, including the ordered
connection writer in internal/httpwire that reproduces the observed header
sequence, and the refresh/profile response shapes in internal/auth/claude.

Replay the measured Fast path and keep diagnostic continuity across cloaked and
native requests.

Preserve the native direct token-counting shape so a caller that reaches
count_tokens itself is not reshaped into the cloaked form.

Scope cloak dates to the credential's timezone rather than the host's, so
currentDate matches what the real client would have sent for that account.
2026-08-03 14:47:26 +08:00
sususu
842dbe6385 perf(claude): batch OAuth tool name rewrites
Collect the tool-name edits and apply them in one pass instead of rewriting the
serialized body once per tool, without changing the resulting bytes.
2026-08-03 14:47:26 +08:00
sususu
a20626f1ee fix(claude): send Anthropic header names with the real client's casing
CPA negotiates ALPN http/1.1 with Anthropic, so header names are not lowercased
by HPACK and reach the server verbatim. The casing is therefore part of the
fingerprint, and six of the eighteen names CPA emits were wrong because Go
canonicalises everything passed through Header.Set: anthropic-beta,
anthropic-dangerous-direct-browser-access, anthropic-version, x-app,
x-client-request-id and X-Stainless-OS. Writing the map keys directly is the
only way to keep the original casing.

This also fixes ordering at no extra cost. Go sorts header names bytewise when
it serialises a request, and the captured order of the real client is exactly
that same bytewise sort. Host, User-Agent and Content-Length stay misplaced
because Go writes them ahead of the sorted block; a test records that gap and
will fail if it ever closes.

The pass runs at the send boundary rather than inside applyClaudeHeaders: the
rewritten keys are unreachable through Header.Get, and doing it earlier hid
these headers from the rest of the pipeline. The boundary is a single
doClaudeUpstreamRequest helper pinned by a structural test, after a mutation
check showed that dropping the call from the streaming path alone was otherwise
undetectable. Expected order is pinned by serialising the request and reading
the header lines back, not by inspecting the header map.
2026-08-03 14:47:26 +08:00
sususu
6912a74064 fix(claude): keep a fast-mode refusal request-scoped
An account without the fast-mode entitlement answers speed:"fast" with HTTP 429
and "Usage credits are required for fast mode." The generic pipeline treats any
429 as quota exhaustion: it marks the credential Quota.Exceeded, assigns an
exponential cooldown up to 30 minutes and rotates to the next credential, so a
single request could walk and cool down the entire Claude pool over a
capability the account will never have.

Classify it through the existing RequestScopedError contract instead, which
already suppresses cooldown, rotation and retry. The status and body are then
returned verbatim: the classifier exists to stop the generic pipeline from
handling the error, not to handle it.

Detection deliberately requires both "fast mode" and a credits phrase, so a
genuine rate limit, which never mentions fast mode, keeps its cooldown. Tests
cover both observed wordings, byte-exact pass-through, and the negative cases.
2026-08-03 14:47:26 +08:00
sususu
4fdf59c436 fix(claude): synchronize credential metadata and device pools
A single *Auth is shared by every concurrent request that selects the same
credential, so any path reaching into Auth.Metadata directly races the others.
The credential identity helpers initialized and wrote the map outside
claudeDevicePoolMu; only EnsureDeviceIDPool took the lock, so a lazy
`auth.Metadata = make(...)` racing a pool write could abort the whole process
with "concurrent map writes" instead of failing a request.

Locking only the device-pool helpers was not enough: the account-profile and
refresh paths kept mutating the same map unguarded, which a concurrency probe
surfaced as data races. Widen the lock to the whole metadata map and route the
remaining call sites through new accessors in internal/auth/claude, including
the lazy map initialization, which needs a pointer to the field to stay inside
the critical section. claudeAccountProfileLookupDue now takes the already-read
timestamp so it cannot be handed an unsynchronized map.
2026-08-03 14:47:26 +08:00
sususu
a2933c7737 fix(claude): scope Anthropic beta and count_tokens policies
Claude Code builds Anthropic-Beta per request instead of sending a fixed list.
Captured from an isolated 2.1.220 profile pointed at api.anthropic.com through
a local proxy, over two rounds covering 11 model IDs and the [1m] variants:

  constant  claude-code, interleaved-thinking, redact-thinking,
            thinking-token-count, context-management, prompt-caching-scope
  tools     advanced-tool-use-2025-11-20 only when tools are declared
  model     mid-conversation-system-2026-04-07 only on models that accept a
            role=system turn
  [1m]      context-1m-2025-08-07, directly after claude-code-20250219 rather
            than at the end
  trailing  effort-2025-11-24, then server-side-fallback-2026-06-01

claude-sonnet-5 emits mid-conversation-system-2026-04-07, so it accepts a
role=system turn and must not sit in the legacy reminder whitelist.

count_tokens does not reuse the inference fingerprint. Running /context in an
interactive session issues 37 identical calls, which made the endpoint
observable for the first time: four betas only, and 21 headers rather than 22
because X-Stainless-Timeout is absent. The profile is selected from the request
path so no call site has to thread another flag.
2026-08-03 14:47:26 +08:00
sususu
afdd251cca fix(claude): preserve semantics in MCP tool aliases
Keep the opaque MCP alias reversible and meaning-preserving so a cloaked
caller's tool declarations, choices and history survive the round trip without
changing tool ownership.
2026-08-03 14:47:26 +08:00
sususu
ef89c6a69d fix(claude): reconstruct cloaked system prompts like the real client
Preserve a cloaked caller's own system prompt instead of discarding it, place
it as a mid-conversation system turn on models that accept one, and route the
remaining legacy models through system reminders.

Scope the legacy reminder whitelist to official model IDs. claude-opus-4-6-thinking
was dropped: Anthropic publishes no -thinking IDs, that one belongs to the
antigravity provider in models.json and is served by a different executor, so it
can never reach ClaudeExecutor cloaking. Keeping it implied that synthetic
suffixes are normalized here, which they are not, since thinking.ParseSuffix only
strips parenthesis suffixes. The map is anchored to the "claude" provider block
plus Anthropic's bare and "-latest" aliases, and now covers claude-opus-4-7.
2026-08-03 14:47:26 +08:00
sususu
f3e25ab2ba feat(claude): align OAuth wire identity and TLS with Claude Code 2.1.220
Detect confirmed CLI, sdk-cli and VSCode callers before mutation so native
software, system, tool, cache and beta shapes pass through, while unconfirmed
OAuth clients receive a coherent minimum CLI identity.

Persist each Claude OAuth credential's upstream account metadata and one stable
device ID, derive one stable session per agent conversation, and keep body and
header identity synchronized across Messages, streaming and count_tokens.

Alias every cloaked third-party custom tool through caller-stable opaque MCP
names and restore declarations, choices, history, references, non-stream
responses and SSE events without changing tool ownership.

Implement the Claude Code 2.1.220 CCH algorithm over the final serialized
request bytes, align currentDate and first-user cache layout, update the
official beta/header baseline, and use upstream count_tokens for OAuth and
first-party Anthropic credentials.

Match the 2.1.220 TLS ClientHello so the transport fingerprint agrees with the
identity the request now claims, and document the CLI defaults and automatic
OAuth signing / tool alias behaviour in config.example.yaml.
2026-08-03 14:47:26 +08:00
Luis Pater
134a66738c fix(codex): hydrate missing response.completed output item IDs
When a `response.completed` payload already includes output entries, fill in only missing/empty item `id`s from the streamed `output_item.done` data while keeping existing IDs untouched.

Closes: #4622
2026-08-03 06:19:56 +08:00
Luis Pater
13435c93d2 feat(executor): normalize OpenAI tool results for text-only compatibility models
- Add OpenAI-compat executor-time normalization for `tool` message content, converting non-string tool results to plain text and replacing image parts with a clear unsupported marker for models configured with text-only `input-modalities`.
- Apply normalization in both regular and streaming OpenAI-compat execution paths before prompt-cache processing.
- Update config example to document `[text]` `input-modalities` for upstreams that reject multimodal tool-result content.

Closes: #4737
2026-08-03 03:37:24 +08:00
Luis Pater
8d675e690d feature(codex): Normalize invalid sub2api message item IDs during Codex input sanitization
- Prefix non-compliant message input IDs with `msg_` while leaving valid/prefixed IDs unchanged.
- Apply normalization before length checks and shortening to keep ID handling deterministic and consistent across Codex request paths.
2026-08-03 02:45:49 +08:00
sususu
91561df0b1 fix(codex): align websocket cloaking headers 2026-08-01 13:01:33 +08:00
Luis Pater
08eb05ae87 feat(config): add support-prompt-cache-key option for OpenAI compatibility
- Introduced a new `support-prompt-cache-key` configuration option to enable optional prompt cache key derivation for supported models.
- Updated relevant API handlers and internal logic to handle the new configuration.
- Enhanced executor workflows to apply prompt cache keys conditionally during request processing.
- Added tests to validate prompt cache key behavior across various scenarios and providers.
- Updated configuration examples and documentation for the new option.

Closes: #4700
2026-08-01 11:39:50 +08:00
Luis Pater
3d4e160495 feat(executor): add conditional support for Claude fast mode beta
- Introduced `claudeFastModeBeta` constant and logic to append it conditionally based on the `speed` attribute in the request body.
- Ensured `fast-mode-2026-02-01` beta inclusion only when explicitly requested or if the `speed` is set to `fast`.
- Updated `applyClaudeHeaders` to dynamically handle fast-mode beta and adjust headers accordingly.
- Enhanced beta extraction and addition with a new helper `appendClaudeFastModeBeta`.
- Improved flexibility in beta handling by preserving requested overrides while ensuring compatibility with default configurations.

Closes: #4703
2026-08-01 10:17:36 +08:00
Luis Pater
8c37dd9862 feat(executor): introduce Kimi Thinking Replay cache and continuity mechanisms
- Added logic for caching and restoring Kimi "Thinking Replay" states to improve continuity across requests.
- Implemented `kimiThinkingReplayScope`, content replay preparation, and conditional cache storage based on replay validity.
- Introduced helper methods for replay content restoration, caching, and cleanup on errors.
- Enhanced executor workflows to utilize the "Thinking Replay" cache for both immediate and streaming response handling.
- Integrated periodic cleanup of expired and oversized replay cache entries.

Closes: #4695
2026-07-31 22:59:13 +08:00
hkfires
a63da8ae76 Revert "Merge pull request #4687 from router-for-me/fix/home-401-refresh-recovery"
This reverts commit 4a31513673, reversing
changes made to 7d00936acc.
2026-07-31 16:35:33 +08:00
sususu98
9d46c8d145 Merge pull request #4684 from sususu98/fix/thinking-summary-visibility
feat(thinking): preserve cross-protocol summary visibility
2026-07-31 14:12:00 +08:00
sususu
c4dcd8703a fix(thinking): respect final summary authority 2026-07-31 13:28:14 +08:00
sususu
0c2ec7da23 fix(thinking): honor normalized summary payloads 2026-07-31 12:45:13 +08:00
Supra4E8C
4a31513673 Merge pull request #4687 from router-for-me/fix/home-401-refresh-recovery
Fix/home 401 refresh recovery
2026-07-31 02:55:42 +08:00
Supra4E8C
4db8e12029 fix: recover Home OAuth credentials after 401 2026-07-31 01:34:11 +08:00
sususu
5d307c195d fix(thinking): close summary translation gaps 2026-07-30 23:26:20 +08:00
sususu
b3046d29b9 feat(thinking): preserve cross-protocol summary visibility 2026-07-30 21:53:30 +08:00
Luis Pater
a80e8082ef feat(codex): add disable-codex-cloaking config option and refine header management
- Introduced `disable-codex-cloaking` to allow disabling enforced `User-Agent` and `Originator` headers for Codex requests.
- Updated header application logic to conditionally include `codexUserAgent` and `codexOriginator` based on configuration.
- Enhanced config diff tracking to detect changes in `disable-codex-cloaking`.
- Expanded tests to cover new config behavior and header application scenarios.
2026-07-30 16:37:40 +08:00
Luis Pater
1c1d8efdd5 fix(translator): correct handling of json_schema and json_object response formats
- Adjusted `responseSchema` logic to only set schema for `json_schema` type while ensuring it is excluded for `json_object`.
- Updated tests to reflect changes in `responseSchema` handling, ensuring correct validation for both response formats.
- Renamed test function for better clarity regarding `json_object` behavior.

Closes: #4667
2026-07-30 05:57:06 +08:00
Luis Pater
2b63d6bcda refactor(util): use structured options for JSON schema cleaning
- Replaced boolean parameters with a `jsonSchemaCleanOptions` struct in `cleanJSONSchema` to improve readability and scalability.
- Updated `CleanJSONSchemaForAntigravity` and related methods to utilize the new options struct.
- Enhanced flexibility for schema transformations with fine-grained control over operations like union flattening, enum type enforcement, and metadata removal.
- Added comprehensive tests to verify correct handling of unions and enum types in schemas.

Closes: #4666
2026-07-30 03:40:04 +08:00
Luis Pater
5dedb303f1 feat(util, executor, translator): enhance Antigravity schema handling and response formatting
- Introduced `CleanJSONSchemaForAntigravityResponse` to process schemas without adding tool-specific placeholders.
- Updated `cleanJSONSchema` with `removeGeminiMetadata` parameter for improved schema control.
- Refactored Antigravity schema sanitization to handle declaration and generation paths independently.
- Mapped OpenAI `response_format` to Antigravity settings, ensuring proper response schema cleaning.
- Expanded tests to verify proper placeholder-free response schema handling and metadata preservation.

Closes: #4652
2026-07-29 17:35:29 +08:00
Luis Pater
f32291436a refactor(executor): consolidate thinking.ApplyThinking into helps.ApplyRequestThinking
- Replaced instances of `thinking.ApplyThinking` with `helps.ApplyRequestThinking` across all executors for consistency.
- Updated `applyGeminiInteractionsThinking` to accept `cliproxyexecutor.Request` and `Options`.
- Centralized logic for request thinking application to `helps` package for improved maintainability.

Closes: #4618
2026-07-29 14:14:18 +08:00
sususu
20784c67ff fix(home): use Home's CAS command instead of EVAL for replay compare-and-swap
Client.KVCompareAndSwap sent Redis EVAL with a Lua script, but the Home RESP
subset does not implement EVAL, so Home replied "ERR unknown command 'eval'".
That broke the Antigravity and Codex reasoning replay caches in Home mode.

Switch the transport to Home's dedicated CAS command:

    CAS <key> <expected-exists 0|1> <expected-value> <new-value> [PX <ttl-ms>]

The semantics match the old script argument for argument, so KVCompareAndSwap's
signature and all its callers are unchanged. Omitting PX when ttl <= 0 mirrors
the script's SET-without-PX branch, which clears the TTL.

Deployments that predate CAS reject the command. Detect that by matching the
unsupported-command error, latch ErrCompareAndSwapUnsupported for the client
lifetime so later calls skip the round trip, and warn exactly once. The latch is
deliberately not carried across NewLifetime, so a Home upgrade takes effect on
the next reconnect rather than requiring a CPA restart.

Also stop replay-state failures from failing the request. A bare replay error has
no HTTP status, so resultErrorFromError does not classify it as request-scoped and
MarkResult marks the credential unavailable for that model, walking every
candidate credential until alias resolution has nothing left and returns 503. A
ledger miss is already a tolerated outcome, so degrade to "no replay this turn"
instead. The pairing failure still returns its 400.

Verified end to end against a real Home over RESP/mTLS on PostgreSQL with
Antigravity OAuth credentials: patched Home recreates the replay row through CAS
with its TTL, while a pre-CAS Home latches once, keeps returning 200 instead of
503, and records no credential error attributable to the replay path.

Refs router-for-me/CLIProxyAPIHome#79
2026-07-29 09:44:05 +08:00
Luis Pater
1778e8ce69 Merge pull request #4596 from rdself/coder/claude-stream-accept-header-v2
fix(claude): align headers with upstream streaming / 修复 Claude 请求头与上游流式模式不一致
2026-07-28 23:44:46 +08:00
sususu
a06e21b439 fix(antigravity): replay text thought signatures across context drift
A cached text signature is pinned to its part by that part's own content
fingerprint. Gemini validates a signature's own integrity and never its
binding to the surrounding history, so drift elsewhere in the conversation
cannot invalidate it. Gating the fingerprinted lookup on the context hash
only discarded reasoning the model then had to redo. The legacy positional
fallback has no such proof and stays gated.
2026-07-28 12:29:31 +08:00
sususu
d2c0c58b75 fix(antigravity): keep thought signatures when replay context drifts
Reasoning replay treated a changed history as proof that a cached thought
signature had become invalid, so a drifted context or a ledger miss dropped the
signature and left the call on the bypass sentinel. Once that happened the
damage cascaded: every later ledger item verifies its contextHash against the
restored bytes of all preceding contents, so one broken link cost the whole tail
of the conversation its reasoning.

Testing the assumption directly against daily-cloudcode-pa.googleapis.com shows
it does not hold. Gemini validates a thought signature's own integrity and
nothing else -- corrupting one byte returns "Corrupted thought signature", while
changing the system instruction, adding a tool, rewriting an earlier call's
args, rewriting a tool result, or swapping two turns' signatures are all
accepted. Only the newest functionCall group has to carry a signature at all,
and the bypass sentinel satisfies that.

Keeping the signature is worth doing rather than merely harmless: on an
otherwise identical request, replacing every signature with the sentinel raises
thoughtsTokenCount from 11-15 to 41-53, so a broken chain makes the model
re-reason from scratch.

Restore the signature on the identity-only path, and stop deleting the client's
in-band signature while degrading unresolved provenance IDs. Argument integrity
is unaffected: that is the opaque digest ID's job, not the signature's.
2026-07-28 12:29:31 +08:00
sususu
e47c43650c fix(antigravity): recover tool provenance instead of failing closed (#4599)
Since #4525 a Claude Code session against Antigravity Gemini could die
permanently with HTTP 400 "missing Claude tool provenance". The reserved
cpa_gemini_<digest> IDs live in the client transcript forever, so once the
replay ledger cannot resolve them every later request fails the same way and
the request never reaches upstream.

Live testing against gemini-3.6-flash-high confirmed four independent ways the
ledger lookup breaks, all producing the same 400:

  A  contents mutated earlier in the history  -> contextHash rejects an exact
     opaque-ID match, so proven identity is discarded along with the signature
  B  one extra system block                   -> the session key embeds the
     system lane, so the whole ledger bucket switches and comes back empty
  C  process restart or TTL expiry            -> empty bucket
  D  one mid-stream client abort              -> that turn never commits

D needs nothing but a single interrupted stream and kills the session from its
very first tool turn, which matches the reported symptom most closely. Parallel
tool calls are not the trigger; they only lengthen the history.

Split tool identity recovery from context-bound signature replay:

- antigravityFunctionCallProvenanceLocation resolves a call when the payload ID
  equals the opaque digest derived from a ledger item. That digest is
  sha256(call_id, name, args), so an exact match already proves the identity and
  the context hash adds nothing to it. Such a call is restored to its native
  id/name/args with its paired functionResponse, but deliberately stays
  unsigned - the cached signature belongs to a different history.
- Unresolved reserved IDs are now rewritten to deterministic neutral IDs rather
  than aborting the request. Pairing is preserved, untrusted signatures are
  dropped, and only genuinely malformed histories still error.
- antigravityRepairUnsignedFirstFunctionCalls re-asserts the leading-call
  signature invariant after replay, because the request-level sanitizer runs
  before it. Native signatures are never touched.

Also fixes a corruption the fail-closed check was masking: when a client changed
a call's arguments, replay inserted a duplicate native functionCall next to the
mutated one, leaving more calls than responses.

Adds forensic debug logging (hashed session keys and counts only) that
distinguishes a ledger miss from a context-hash rejection.

Verified live: happy path unchanged over 20 upstream requests and 100 parallel
call groups (0 residual reserved IDs, every group 1 native-signed + 4 unsigned);
all four failure modes now return 200; and a session survives a mid-stream abort
plus 5 further requests that previously died.
2026-07-28 12:29:31 +08:00
Randi
c405398a48 fix: align Claude headers with upstream streaming 2026-07-27 19:07:57 -04:00
Luis Pater
8423cce2d1 feat(executor): add configurable injection of x_search tool for xAI requests
- Introduced `InjectXSearch` in `XAIConfig` to enable automatic injection of the native `x_search` tool when not explicitly declared.
- Updated `XAIExecutor` to honor the `InjectXSearch` configuration, ensuring consistent tool availability.
- Enhanced configuration handling with support for dynamic diffing to track changes in `InjectXSearch`.
- Added comprehensive tests to validate `InjectXSearch` behavior, including preparation and tool choice synchronization.
- Updated example config and documentation to outline `InjectXSearch` usage.

Closes: #4339
2026-07-27 04:41:24 +08:00
Luis Pater
57ef784224 feat(executor): implement local token counting for Claude requests
- Added `CountClaudeInputTokens` for estimating token usage with O200kBase tokenizer.
- Enhanced `ClaudeExecutor` to handle token counting locally, reducing dependency on upstream services.
- Introduced validation for token count requests for improved error handling.
- Updated tests to cover local token counting behavior and invalid request handling.

Closes: #4103
2026-07-27 00:50:11 +08:00
Luis Pater
90c2ff90de fix(executor): add failure tracking and reporting for video execution
- Introduced `ExecutorUsageReporter` to monitor and report failures.
- Enhanced HTTP client with tracking capabilities.
- Ensured proper publishing of execution reports.

Closes: #4085
2026-07-27 00:01:49 +08:00
Luis Pater
411dc9a9c3 Merge pull request #4591 from cyclopentadiene/fix/claude-oauth-sse-tool-names
fix(executor): restore OAuth tool names for non-stream OpenAI responses
2026-07-26 22:25:38 +08:00
cyclopentadiene6
08e5515703 fix(executor): restore OAuth tool names from SSE responses 2026-07-26 23:28:18 +10:00
Luis Pater
fa42a9d434 Merge PR #4546 into dev 2026-07-26 19:11:32 +08:00
Luis Pater
42a00a2a65 Merge PR #4573: preserve tool_result ordering 2026-07-26 14:43:28 +08:00
Luis Pater
fe4ae4989c chore(pluginhost): refactor and remove unused interceptors and executor methods
- Removed deprecated interceptor and executor-related methods, including `callRequestInterceptor`, `callResponseInterceptor`, and `callStreamChunkInterceptor`.
- Consolidated unused logic and pruned redundant imports to streamline `adapters.go`.
- No functional changes.
2026-07-26 14:31:45 +08:00
Luis Pater
f6c32ec3ff feat(runtime): implement derived session identity features and tests
- Added utilities for generating stable session identities, including `DerivedSessionID`, `DerivedSessionUUID`, and `ProviderSessionUUID`.
- Introduced support for Antigravity's negative decimal `DerivedAntigravitySessionID` mapping.
- Enhanced metadata handling for provider-scoped UUID stabilization and consistent session identity inference.
- Added comprehensive tests for stability, namespace isolation, and preferred execution session prioritization.
2026-07-26 03:07:21 +08:00
Code_G
29406436c3 fix(executor): keep tool_result blocks first when injecting the cloaking reminder
`prependToFirstUserMessage` always inserted the `<system-reminder>` text block at
index 0 of the first user message's content array. When a client sends a history
that begins with an assistant `tool_use` turn, that first user message is the
`tool_result` carrier, and Anthropic requires those blocks to stay at the head of
the message. Prepending pushed them out of first position, so the upstream
rejected the whole request with:

    messages.N: `tool_use` ids were found without `tool_result` blocks
    immediately after: <id>

Append the reminder instead when the content array already leads with a
`tool_result` block; behaviour is unchanged for every other message shape.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 03:02:43 +09:00
sususu98
a4d18cb04a fix(antigravity): scope schema sanitization (#4571) 2026-07-26 01:52:20 +08:00
Luis Pater
41f6ea8950 feat(runtime): enhance response preparation for xAI executor output controls
- Added logic to `preserveXAIResponsesOutputControls` for retaining supported controls (`max_output_tokens`, `temperature`, `top_p`, `top_k`) during translation.
- Removed unsupported fields such as `stop` where necessary to align with xAI's Responses API.
- Updated tests to validate the preservation and removal of fields across various input scenarios.
- Improved payload configuration handling with additional unit tests for ensuring compatibility with model-specific overrides.

Closes: #4464
2026-07-26 00:56:57 +08:00
Luis Pater
27fc3169bb feat(runtime): improve executor binding logic and add config-based executor handling
- Added `UsesConfig` to `XAIAutoExecutor` for determining config-based binding.
- Refactored `Service` to support thread-safe executor registration, using a new mutex (`executorRegistrationMu`).
- Improved executor rebind logic to prevent unnecessary replacements unless forced or required by config updates.
- Updated handling of config updates to correctly replace stale XAI executors.
- Enhanced the test suite for edge cases in executor binding and replacement for multiple providers.

Closes: #4567
2026-07-26 00:16:48 +08:00