357 Commits
Author SHA1 Message Date
hkfires 3727d87cef feat(logging): add debug logging for OAuth selection in home execution 2026-07-25 09:17:20 +08:00
Luis Pater 42f36b94e0 fix(usage): harden canonical token normalization 2026-07-24 01:18:18 +08:00
Dylan fe8a616aa3 fix(usage): classify partial token accounting correctly 2026-07-24 00:31:32 +08:00
Dylan 416a080174 fix(usage): add normalized token accounting v2 2026-07-23 21:27:03 +08:00
Luis Pater e5729872fa Merge pull request #4522 from sususu98/fix/responses-ws-continuity 2026-07-23 14:31:41 +08:00
Luis Pater 3ecd4afe80 feat: add Home credential concurrency support 2026-07-23 13:42:08 +08:00
sususu e05ae09425 fix(responses): preserve context across websocket transport changes 2026-07-23 13:07:23 +08:00
sususu98 7329437297 fix(websocket): propagate upstream 1009 without credential fallback (#4444)
Preserve Codex and XAI upstream message-too-big closes across the Responses WebSocket boundary while treating them as request-scoped errors. Prevent CPA reconnect and credential cooldown/fallback, scope close state to the active connection, retain terminal errors under backpressure, and ensure downstream disconnect cleanup cannot wait behind a blocked writer.
2026-07-20 16:18:13 +08:00
Luis Pater 36ed0ca5ce feat(auth): add explicit handling for count_tokens endpoint errors and enhance model availability logic
- Implemented logic to differentiate between generic and explicit model-not-found errors for the `count_tokens` endpoint.
- Introduced `recordAvailabilityNeutralResult` to ensure models remain active for generic endpoint errors.
- Expanded test coverage to verify behavior for various 404 scenarios, including error propagation and model suspension handling.
- Refactored error-parsing utilities to identify structured and nested model-not-found errors.

Closes: #4410
2026-07-19 03:39:27 +08:00
hkfires 1e119afefc fix(config): rename SyncRevision to AuthRevision in PluginsConfig 2026-07-18 09:12:29 +08:00
Luis Pater f5746a9822 feat(oauth-model-alias): add support for display names in OAuth model aliases
- Introduced `display-name` field for human-readable model names in config and YAML files.
- Updated alias application logic to set and preserve display names during model alias mapping.
- Enhanced tests to validate display name behavior for various scenarios, including forks and upstream preservation.
- Refined `sanitizeOAuthModelAlias` to handle `display-name` with proper trimming and formatting.

Closes: #4398
2026-07-17 23:28:34 +08:00
Luis Pater 366271969a feat(logging): add CPA trace ID middleware and enhance metadata handling
- Introduced CPA trace ID middleware to inject trace IDs in response headers.
- Added support for CPA trace ID generation and validation.
- Updated metadata handling to include stable auth indices and trace ID callbacks for enhanced request tracking.
- Enhanced tests to verify CPA trace ID propagation and reserved header handling across middleware, metadata, and handlers.

Closes: #4370
2026-07-17 22:58:28 +08:00
hkfires 5243b38ab7 feat(pluginstore): implement plugin synchronization and validation
- Introduced PluginSyncRequest and PluginSyncResponse types for managing plugin synchronization.
- Added validation logic for plugin sync responses, including checks for schema version and expiration.
- Implemented functionality to clear sensitive data from plugin sync requests and responses.
- Enhanced error handling for HTTP requests related to plugin synchronization.
- Updated manifest validation to reject artifact URLs containing credentials or query parameters.
- Added tests for plugin synchronization, ensuring proper validation and error handling.
- Refactored existing code to support resolved authentication configurations for plugin downloads.
2026-07-17 08:09:05 +08:00
Luis Pater 09da52ad50 feat(translator): improve error handling and response conversion for incomplete statuses
- Enhanced response handling to support `response.incomplete` events across translator components and executors.
- Refactored `resultErrorFromError` to centralize error conversion and consolidate repeated logic for constructing custom errors.
- Updated handling of `max_output_tokens` and similar terminal reasons in OpenAI and Gemini translator workflows.
- Introduced `IsRequestScoped` and `IsRequestScopedError` to differentiate between request-scoped and non-request-scoped errors.
- Added comprehensive test cases for incomplete responses, terminal failures, and error propagation in both streaming and non-streaming conditions.

Closes: #3055
2026-07-16 04:38:19 +08:00
Luis Pater 768b4c49fd feat(usage): track generate flag with backward-compatible defaults
Propagate client generate metadata through usage records while keeping
legacy callers that omit the field enabled by default. Preserve an
existing context generate=false when metadata does not set the value,
and normalize omission to true only at publishing boundaries.
2026-07-15 21:30:09 +08:00
Luis Pater 35a5f06612 feat(config): resolve plugin directory paths and handle tilde expansion
- Added `ResolvePluginsDir` to normalize plugin directory paths, including tilde (`~`) expansion.
- Integrated directory resolution into config loading, runtime setup, and plugin management flows.
- Updated tests across components to validate correct handling of unresolved and expanded plugin paths.
- Added error handling for invalid or unresolved plugin directories to prevent runtime issues.

Closes: #4313
2026-07-15 16:53:02 +08:00
Luis Pater 411d7d41ee feat(auth): add SelectAuthByKind method and enhance Codex Alpha Search handling
- Implemented `SelectAuthByKind` to filter and select credentials by kind with retry logic and error handling.
- Updated Codex Alpha Search to enforce OAuth credentials and sanitize upstream request body.
- Added unit tests to validate `SelectAuthByKind` behavior across scenarios.
- Introduced `sanitizeCodexAlphaSearchBody` for removing sensitive fields from API payloads.

Closes #4289
2026-07-14 16:31:45 +08:00
Luis Pater 160a55612e feat(auth): add home force mapping logic with tests and alias result handling
- Implemented `homeForceMappingAliasResult` for handling forced alias mapping in the authentication process.
- Updated `resolveExecutionAliasResultForRequested` to incorporate home force mapping.
- Introduced new attributes (`home_force_mapping` and `home_original_alias`) for managing alias behavior.
- Extended `homeAuthDispatchResponse` with fields for force mapping and original alias.
- Added test cases to validate force mapping logic and edge cases.
2026-07-14 14:59:15 +08:00
sususu98 03d58c4402 fix(usage): collapse OpenAI service tier metadata (#4275)
* fix(usage): preserve request service tier metadata

* fix(usage): collapse request tier metadata

* fix(usage): emit only service_tier and response_service_tier

Collapse request tier to service_tier for usage records and queue payloads. Keep RequestServiceTier as a deprecated input-only alias for older plugin callers.
2026-07-14 14:19:13 +08:00
Luis Pater 423f3d5ff7 feat(xai): add xAI API key support with comprehensive handling and tests
- Introduced `xai-api-key` to configuration for supporting xAI provider integration.
- Added API key operations: creation, updates, deletion, and sanitization.
- Implemented backend enhancements for credential-specific routing, response filtering with model aliasing, and WebSocket handling for xAI services.
- Included extensive test coverage to validate configuration parsing, CRUD operations, and the new functionalities.
- Updated related modules to handle `xai-api-key`, including the watcher, management handlers, and configuration sanitizers.
2026-07-14 04:40:12 +08:00
Luis Pater 9c3f720710 feat(codex): implement session affinity and request logging for alpha search
- Added `SelectAuth` method to enhance flexible credential selection.
- Introduced session affinity mechanism for consistent credential reuse per session.
- Enabled detailed request/response logging for Codex alpha search API.
- Updated tests to validate session affinity behavior and log capture.

Closes: #4166
2026-07-12 22:59:14 +08:00
Luis Pater 3586d3e750 feat(config): add support for configurable model display names across all APIs
- Added `DisplayName` to model configuration for Codex, Claude, Gemini, Vertex, and OpenAI compatibility.
- Updated hash computation and model summary to account for `DisplayName`.
- Enhanced API handlers to use `DisplayName` for client-facing catalogs.
- Expanded tests to verify `DisplayName` usage, preservation, and fallback mechanisms.
- Updated documentation and configuration examples for `DisplayName` field.

Closes: #4245
2026-07-12 22:13:23 +08:00
Supra4E8C dc4be1674a feat(usage): report request and response service tiers 2026-07-11 00:49:30 +08:00
Luis Pater ec3aba23fa feat(auth): enable automatic credential refresh on unauthorized errors
- Added `tryRefreshAfterUnauthorized` to refresh OAuth credentials on 401 errors during requests.
- Implemented `refreshLocks` to prevent concurrent refreshes for the same auth ID.
- Updated auth/state handling to reset unauthorized model states and resume operations after a successful refresh.
- Enhanced refresh logic with error handling, synchronization, and state updates.

Closes: #4087
2026-07-09 03:08:02 +08:00
Luis Pater 078ed1787b feat(openai): add support for input/output modalities in Codex client models
- Added handling of input/output modalities metadata for Codex and compatibility clients.
- Updated model registry and configurations to support "text" and "image" modalities.
- Introduced new tests to validate input/output modalities registration and processing.

Closes: #3976
2026-07-08 00:21:03 +08:00
Luis Pater 3aa42a6f7d fix(auth): handle invalid_grant errors with retry suspension logic
- Added fallback and suspension handling for `invalid_grant` errors in auth conductor.
- Introduced helper methods to detect `invalid_grant` error patterns.
- Updated tests to validate `invalid_grant` fallback and suspension scenarios in execution and stream flows.

Closes: #4120
2026-07-07 12:19:35 +08:00
Luis Pater 8b9c4da245 feat(interactions): add support for Google Interactions
- Introduced API handlers and executor logic for Google Interactions
- Added request and response transformations for OpenAI and Claude Interactions.
- Integrated Gemini API with Interactions support.
- Updated tests to validate Interactions request parsing and error handling.
- Refactored translator logic for Interactions data flows.
2026-07-07 02:39:13 +08:00
Supra4E8C 0d23f79151 fix(auth): keep jittered cooldown waits within max-retry-interval
shouldRetryAfterError approves a retry using the pre-jitter wait, so the jitter added in waitForCooldown could push the actual sleep up to 2s past the configured max-retry-interval ceiling. Clamp the jitter range to the remaining headroom below maxWait so the documented maximum stays a hard bound; waits at the ceiling simply sleep unjittered.
2026-07-06 00:16:08 +08:00
Supra4E8C 270869dd20 fix(auth): escalate quota backoff once per cooldown window and jitter cooldown waits
Previously every 429 incremented the backoff level unconditionally, so N concurrent in-flight failures from a single quota event inflated the level by N at once (12+ failures jump straight to the 30 minute plateau even when the upstream limit clears in seconds). Failures that land while a previous quota window is still open now reuse that window instead of escalating, so the ladder advances at most once per window. Provider supplied retry hints still take effect unchanged.

waitForCooldown also slept until the exact recovery deadline, waking every waiting request in lockstep against the single recovered credential and re-escalating it in one burst. Cooldown waits now carry a small random jitter (up to wait/4, capped at 2s) to spread the wakeups.
2026-07-06 00:05:51 +08:00
sususu98 150e7f0dc5 fix(auth): repair force-mapped Responses SSE framing for WS forwarder
Force-mapping rewrites streaming OpenAI Responses SSE through StreamRewriter before the /v1/responses websocket forwarder. Antigravity/Gemini and Codex emit frames without reliable trailing newlines, so buffered chunks glued as ...}event:..., ...}data:..., or event:/data: without separators. The rewriter dropped pending tails and downstream WS synthesized 408 without response.completed even when upstream returned HTTP 200.

- safeReplaceGlued for }event: and }data: when data JSON is complete\n- Finish flush with glue normalization and line-wise fallback\n- Newline between pending event: and next data: line (Codex scanner lines)\n- Regression tests: Antigravity sim, Codex data lines, force-map WS forward
2026-06-29 18:11:44 +08:00
sususu98 eb2e1e33d4 fix(auth): rewrite API key alias response models (#4002) 2026-06-25 22:32:48 +08:00
sususu98 a7250275c1 fix(oauth): force-map responses to config alias not request suffix (#3983)
* fix(oauth): force-map responses to config alias not request suffix

Rewrite client-visible model fields using oauth-model-alias Alias from config
(e.g. gpt-5.4-fast) instead of mirroring the requested model string when the
client adds thinking suffixes (e.g. gpt-5.4-fast(high)). Upstream routing
still preserves suffixes on the resolved upstream model.

* test(oauth): gate response rewrite on ForceMapping and document OriginalAlias

- Document that OriginalAlias is only applied when ForceMapping is true
- Set OriginalAlias to config alias only when force-mapping is enabled
- Add tests: no non-stream/stream payload rewrite when ForceMapping is false

* test(oauth): assert disabled force mapping response
2026-06-24 16:57:03 +08:00
sususu98 fd93ee035d feat(oauth): add force-mapping for upstream response model rewrite
Add OAuthModelAlias.ForceMapping to rewrite client-visible model fields in non-stream and SSE stream responses while leaving request-log upstream payloads unchanged.

- Wire config, watcher diff, and conductor execute/stream paths
- Rewrite force-mapped responses in Antigravity credits fallback paths
- Prevent buffered SSE chunks from being emitted by line-wise fallback
- Preserve thinking suffixes for same-base force-mapping aliases
- Cover codex, antigravity, kimi, and xai with live-derived fixtures and regression tests
- Document a conservative antigravity-only force-mapping example in config.example.yaml
2026-06-24 14:51:35 +08:00
Luis Pater 70053beadb feat(auth): refactor credential kind detection and add dynamic source classification
- Introduced `AuthKind` and `AuthSourceKind` methods for improved credential type and source classification.
- Replaced legacy fallback logic with normalized attribute-based handling in `AuthKind`.
- Consolidated metadata inspection for both API key and OAuth attributes.
- Updated calls to replace direct `AccountInfo` usage with `AuthKind` and `AuthSourceKind`.
- Enhanced unit tests to validate explicit and fallback credential resolution scenarios.
2026-06-23 23:21:33 +08:00
hkfires e9a11db7b9 feat(home): enhance plugin management and synchronization
- Added NodeID field to HomeConfig for better identification.
- Updated ConfigFromJWT to populate NodeID from claims.
- Introduced new Redis keys for managing plugin status and tasks.
- Implemented RPushPluginStatus and GetPluginTasks methods in the client for handling plugin-related data.
- Enhanced Sync functionality to include detailed reporting on plugin installation and deletion tasks.
- Added error handling and reporting for plugin synchronization failures.
- Created tests for new plugin synchronization and management features.
- Improved the overall structure and readability of the plugin synchronization logic.
2026-06-23 20:42:33 +08:00
hkfires c58da381c7 feat(plugins): sync home plugin manifests 2026-06-22 22:25:44 +08:00
Luis Pater 079ec51f50 feat(cliproxy): optimize API key alias rebuild with deferred execution and caching
- Added `RefreshAPIKeyModelAlias` for explicit alias table rebuilds.
- Introduced deferred rebuild support with `WithDeferredAPIKeyModelAliasRebuild` and context flag validation.
- Implemented `openAICompatibilityRegistrationCache` to streamline OpenAI compatibility model registrations.
- Updated executor and model registration workflows to utilize cached compatibility data, improving efficiency in batch operations.
- Adjusted max worker limits dynamically based on model categories.

Closes: #3953
2026-06-22 08:41:52 +08:00
Luis Pater babef2a1ef feat(cliproxy): add unregisterOpenAICompatExecutor and sync runtime configuration
- Implemented `unregisterOpenAICompatExecutor` to remove OpenAI-compatible executors dynamically.
- Updated `ensureExecutorsForAuth` to unregister incompatible executors while handling plugin candidates.
- Adjusted runtime configuration logic to ensure proper synchronization during updates with `syncPluginModelRuntime`.
2026-06-21 11:21:05 +08:00
Luis Pater 1f21f946ba feat(api): implement support for multi-auth expansion in plugin systems
- Added logic to expand single auth JSON payloads into multiple plugin virtual auth records.
- Updated related API endpoints such as `PatchAuthFileStatus` and `DeleteAuthFile` to handle plugin virtual auths with rollback mechanisms.
- Introduced `NormalizePluginOAuthCallbackProvider` and other normalization functions for better handling of OAuth callbacks.
- Enhanced tests to validate multi-auth parsing, rollback behavior, and API response consistency.
2026-06-21 07:24:29 +08:00
Luis Pater 5771abbc81 feat(management): add ResetQuota endpoint and auth manager quota reset functionality
- Introduced a new `/reset-quota` API endpoint in the management handler to clear quota and cooldown state for auth records.
- Implemented `ResetQuota` method in the auth manager to handle runtime and registry state resets for affected models.
- Added tests to validate quota reset behavior, including proper state updates and registry consistency.
- Refactored utility functions to support deduplication and registered models handling in quota resets.

Closes: #3866
2026-06-20 13:49:40 +08:00
Luis Pater f66376f0b9 feat(auth): add per-auth OAuth model alias support
- Introduced `SetOAuthModelAliasesAttribute` and `OAuthModelAliasesFromAttributes` for managing per-auth model aliases.
- Enhanced OAuth model resolution logic to prioritize per-auth aliases over global aliases.
- Updated metadata handling to extract and sanitize per-account model aliases.
- Added tests to validate alias precedence, empty attributes, and conflict scenarios.

Closes: #3764
2026-06-20 11:31:57 +08:00
Luis Pater 4c78e40da4 feat(auth): unify provider key handling with OpenAI compatibility support
- Replaced direct `strings.ToLower` usage with `util.OpenAICompatibleProviderKey` for generating provider keys.
- Updated auth and executor workflows to use namespaced keys for OpenAI-compatible providers.
- Adjusted tests to validate namespaced key handling, including new test cases for provider registration and execution logic.
- Added `OpenAICompatibleProviderKey` helper in `util` for consistent key transformations.

Closes: #3600
2026-06-20 00:58:20 +08:00
Luis Pater 041a065b2f Merge branch 'remove-gemini-cli' into dev
# Conflicts:
#	internal/api/handlers/management/auth_files.go
#	internal/thinking/provider/geminicli/apply.go
2026-06-19 14:40:29 +08:00
Luis Pater 07c297a51b feat(auth): add persistent cooldown state management with file-backed store
- Introduced `CooldownStateStore` interface for managing independent cooldown state persistence.
- Implemented `FileCooldownStateStore` for storing cooldown states as per-auth `.cds` files with atomic writes and stale file cleanup.
- Enhanced `Manager` to support restoring state from `CooldownStateStore` and persisting state changes during auth updates.
- Updated tests to validate cooldown state saving, loading, concurrency handling, and error scenarios.

Closes: #3368
2026-06-19 05:34:44 +08:00
Luis Pater d33ac5e1e9 feat(auth): add transient error cooldown configuration and adjust retry logic
- Introduced `SetTransientErrorCooldownSeconds` to enable configurable cooldowns for transient errors (e.g., 408/500/502/503/504).
- Updated retry scheduling logic to use the new `nextTransientErrorRetryAfter` function.
- Modified config parsing to include `transient-error-cooldown-seconds` with support for disabling or defaulting to legacy behavior.
- Expanded tests to validate transient cooldown logic with various configurations and edge cases.

Closes: #3315
2026-06-19 04:26:04 +08:00
Luis Pater 1d0551a991 feat(config): improve config reload handling and introduce async management save hook
- Refactored `ConfigReloadHook` to use `reloadConfigFromWatcher` for consistency.
- Added async `reloadConfigAfterManagementSaveAsync` to handle post-save operations.
- Introduced `ReloadConfigIfChanged` in watcher for manual trigger support.
- Enhanced config reload paths to separate auth synthesis from standard updates.
- Updated `applyConfigUpdate` logic to allow more granular reload behaviors.

Closes: #3235
2026-06-19 03:35:09 +08:00
Luis Pater 052f1934b8 fix(auth): classify transport errors as home_unavailable with retryable flag
- Updated error handling in `RPopAuth` to distinguish `auth_not_found` from transport errors.
- Added a new test, `TestPickNextViaHomeClassifiesTransportErrorsAsHomeUnavailable`, to validate correct error classification and retryable property.
2026-06-19 02:54:48 +08:00
Luis Pater 78ba8ba731 chore: remove Gemini CLI-related translator packages and logic
- Deleted `geminicli` provider and related `Apply` logic.
- Removed all translator packages specific to Gemini CLI (Claude, Codex integrations).
- Purged associated test files for Gemini CLI translation.
- Removed `GeminiAuthenticator` and all associated authentication logic (OAuth flows, token handling, refresh logic).
- Deleted internal/executor Gemini OAuth support, including bearer token handling and runtime API logic.
- Purged all tests, configs, and command-line flags specific to Gemini OAuth flows.
- Updated documentation and aliases to reflect Gemini removal.
- Renamed `parseRetryDelay` to `ParseRetryDelay` and `deleteJSONField` to `DeleteJSONField`.
- Updated references in `antigravity_executor` and tests to use the new `helps` package.
- Adjusted import paths and test cases to ensure compatibility with the new location.
- Updated README files to reflect changes in the retry logic references.
- Updated `.github/ISSUE_TEMPLATE/bug_report.md` to remove deprecated Gemini CLI mention.
2026-06-18 13:33:10 +08:00
sususu98 87132e54d7 feat(plugin): add ModelRouter before auth with single-slot routing targets (#3865)
* feat(plugin): add ModelRouter before auth with single-slot routing targets

## Motivation

Plugins that need to change execution based on the **original inbound request**
(protocol format, raw body, headers, query, stream flag, metadata, etc.) often
resorted to virtual/trampoline models or routing inside interceptors. This
commit adds **ModelRouter**: a pluggable layer **before** model-to-provider
resolution and AuthManager credential selection, so plugins can declare who
executes a request without spoofing the client model name.

This is a **new capability**, not a bugfix on the existing chain. With no
ModelRouter plugins loaded, behavior matches upstream.

## Pipeline placement

- `execute`, `stream`, and `count` (and image paths via AuthManager) call
  `applyModelRouter()` before building `coreexecutor.Request`.
- Routing runs **before** the request interceptor (before auth), so routers see
  the client’s original context. After a plugin executor is chosen, the existing
  **after-auth interceptor → response/stream interceptor** chain still applies.
- Internal `ExecuteModel` / `ExecuteModelStream` (host callbacks) support
  `SkipRouterPluginID` so nested calls do not re-enter the same router.

## Routing API (single slot, mutually exclusive)

`ModelRouteResponse` uses **one target slot** to avoid ambiguity when both
`TargetExecutorPluginID` and `TargetProvider` were set and the host ignored one:

| Field | Meaning |
|-------|---------|
| `Handled` | `false`: this router declines; try the next router or default path |
| `TargetKind` | `self` \| `executor` \| `provider` (pick one) |
| `Target` | `self`/`executor`: plugin ID; `provider`: built-in provider key |
| `TargetModel` | Optional on `provider` only; empty keeps client `RequestedModel` |
| `Reason` | Optional diagnostic text |

- **self**: the router plugin’s own executor (`Target` normalized to the router’s plugin ID).
- **executor**: another plugin’s executor; host pre-checks with `executorPluginReady()`
  (executor declared and provider identifier resolvable) to avoid handled routes that 500 at execution.
- **provider**: skip registry model resolution; fixed built-in AuthManager path; optional
  `TargetModel` for execution model only—**does not** change outward requested-model metadata.

Routers run in **descending plugin priority** (tie-break: ascending plugin ID). Panic, error,
invalid target, or unavailable executor/provider → log and **fall through to the next router**;
if none handle, use the original provider+auth flow.

## Context exposed to routers

`ModelRouteRequest` includes:

- `SourceFormat`, `RequestedModel`, `Stream`
- `Headers`, `Query`, `Body` (defensive copies)
- `Metadata` (best-effort read-only context snapshot)
- `AvailableProviders`: built-in provider keys with at least one **non-disabled** auth
  (`AuthManager.AvailableProviders()`). **Does not** reflect per-model cooldown or transient
  unavailability—treat as an optimistic snapshot.

Adds `AuthManager.HasProviderAuth()` and `AvailableProviders()`, excluding `Disabled` and
`StatusDisabled` auths consistently with credential selection.

## Host and RPC

- Go plugins: `pluginapi.ModelRouter` + `RouteModel()`.
- RPC plugins: `pluginabi.MethodModelRoute` (`model.route`), capability flag `model_router`.
- `pluginhost.Host` implements `RouteModel` / `RouteModelExcept`; handlers use
  `SetModelRouterHost` or a `PluginHost` type assertion; **direct executor** paths use
  `ExecutePluginExecutor*` / `CountPluginExecutor`.
- No bundled example ModelRouter plugin; capability is active only when a third-party plugin
  declares `model_router` and loads.

## Plugin RPC schema (policy A, upstream-aligned)

- `pluginabi.SchemaVersion` stays **1**: capability additions (`model_router`, `model.route`)
  do not bump the number; increment only on breaking RPC JSON changes.
- Host sends `schema_version` at register; reject only if the plugin declares a **higher**
  version than the host.
- No unpublished “ModelRouter requires schema ≥ 3” gate (v3 single-slot API was never public).
- Existing plugins and examples without `model_router` (`schema_version: 1`) need no changes.
- RPC ModelRouter: `schema_version: 1` + `model_router: true` + implement `model.route`.

## Path consistency within this commit

- Provider routes reuse image-only model checks (e.g. `gpt-image-2`) on the normalized model,
  same as the default AuthManager path.
- `count` aligned with execute/stream: `SkipRouterPluginID`, query/headers injection,
  interceptor skip semantics.
- Handlers: `modelRoutersEnabled` treats hosts without `HasModelRouters` as disabled
  (same as before ModelRouter existed); `pluginhost.Host` implements the detector.
- API docs: `ModelRouter` explicitly includes built-in **provider** targets (in addition to
  plugin executors and the router’s own executor).

## Testing

go test ./internal/pluginhost ./sdk/api/handlers ./sdk/pluginapi ./sdk/pluginabi ./sdk/cliproxy/auth
go build -o test-output ./cmd/server && rm test-output
go test ./...

* fix(handlers): address ModelRouter review feedback

- Use modelExecutionQuery for plugin executor and AuthManager paths so
  inbound URL query matches router/header behavior
- Guard queryFromContext when gin Request.URL is nil
- Read plugin executor stream chunks via nextStreamChunk to exit on cancel
- Drop redundant clonePluginMetadata on capability record meta

Tests cover query propagation, stream cancel, and nil URL safety.

* feat(plugin): add Claude web search router example

Add a Claude Code web_search ModelRouter example that can route matching Claude requests through Antigravity, Codex, xAI, or Tavily.

The plugin includes executor orchestration, backend fallback/penalty handling, Tavily API key support, Claude-compatible response assembly, stream forwarding, and focused unit coverage for detection, fallback routing, model resolution, penalties, stream forwarding, and Tavily behavior.

Verification: go test -count=1 ./... in examples/plugin/claude-web-search-router/go; go build -buildmode=c-shared for the plugin; go build ./cmd/server; live local CPA curl coverage for plugin load, four explicit routes, fallback, and Codex spark routing.

* fix(pluginhost): validate executor routes before fallback

* fix(pluginhost): skip oauth-only executor routes
2026-06-16 19:15:34 +08:00
Luis Pater f85768eef3 feat(auth): add config API key exclusion management with tests
- Implemented helper methods `IsConfigAPIKeyAuth` and `toggleConfigAPIKeyExcludedAll` for managing config API key exclusions.
- Updated API request handling to support enabling/disabling config API key exclusion patterns.
- Added test coverage to validate exclusion toggling logic and persistence behavior.
- Refactored duplicate code for identifying config API key auth entries into reusable utilities.
2026-06-15 11:14:05 +08:00