- Add `ParsePluginExecutorResponseUsage` to extract token usage from non-streaming plugin responses across Claude, Gemini, Interactions, Antigravity, and OpenAI/Codex protocols.
- Add `ObservePluginExecutorStreamUsage` to observe and aggregate token usage across streaming chunks.
Closes: #5340
Track when executor calls cross an upstream transport boundary and use that
signal to keep model/provider errors from being replaced by later local
preparation, selection, or internal failures.
Mark HTTP, websocket, relay, and usage-tracked transports as upstream
attempts, while avoiding marks for local validation, logging, missing
sessions, and successful websocket handshakes before request send.
Parse relative auth expiry metadata and adjust Antigravity refresh timing.
- Introduce `WebSocketResponseObserver` capability and bump plugin ABI schema version to 4.
- Forward upstream WebSocket response frames from Codex and xAI executors to configured observers.
- Wire `WebSocketResponseObserver` across API handlers and plugin host dispatchers.
Closes: #5248
- Add new `plugin.quiesce` ABI method and propagate RPC error codes from plugin call failures.
- Invoke quiesce on the replaced plugin before loading a new version, then only activate replacement after quiesce succeeds.
- Improve hot-reload safety by serializing lifecycle transitions, cleaning up failed/canceled loads, and rolling back to the previous plugin state when replacement fails or is canceled.
Closes: #5134
- add shared `EnsureResponsesUsageDetails` helper to patch `usage` objects with:
- `output_tokens_details.reasoning_tokens = 0`
- `input_tokens_details.cached_tokens = 0`
- for both plain JSON and SSE `data:` frames, including multi-line frames
- apply the helper to OpenAI Response format outputs in non-stream and stream paths across executors/plugins so translated payloads consistently include required usage details
- update websocket/completion payload builders to emit default `usage` detail fields for prewarm/finish responses
Closes: #4985
- Bump plugin schema to version 3 and introduce `SchemaVersionStreamChunkOmitRequestBody`.
- Treat missing plugin schema versions as legacy during RPC registration (`0 -> 1`) and expose schema on plugin descriptors.
- In stream interception, keep request headers/bodies on header-init chunk and stop re-sending them on payload chunks for schema-v3+ plugins, with per-chunk cloning for legacy plugins.
Closes: #4876
- Added a plugin refresh-compat executor wrapper that forwards normal OpenAI-compat execution paths while routing `Refresh` to plugin `AuthProvider`/Home refresh logic.
- Updated refresh lookup to use the effective executor key from auth metadata so namespaced compatibility providers can resolve their refresh executors correctly.
- Changed OpenAI-compat registration to wrap built-in executors with the plugin-refresh wrapper when a matching plugin auth provider exists, while preserving bare executors otherwise.
- Made `OpenAICompatExecutor.Refresh` fail fast for OAuth-style credentials (with refresh tokens) instead of silently returning unchanged auth.
Closes: #4719
- Added support for weighted round-robin authentication scheduling strategy.
- Implemented credential weight validation for attributes and metadata, with strict error handling for invalid weights.
- Enhanced scheduler with smooth weighted state handling and proportional selection logic.
- Introduced tests for credential weight parsing, validation, and weighted round-robin behavior.
- Updated configuration to include `weight` field for credentials with range validation.
Closes: #4470
- Implemented a Go-based dynamic library plugin for request lifecycle management.
- Added concurrency controls, keyword-based request termination, and response handling.
- Supported optional capabilities for request interception and active lifecycle termination.
- Included tests for schema compatibility, concurrency limits, and policy-based termination.
- Added build instructions and configuration details in README.
- Updated host support for lifecycle plugin RPC methods.
Closes: #4568
- 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.
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.
- Added Manifest struct to encapsulate plugin metadata and installation details.
- Implemented ManifestFromRelease and ManifestFromPlugin functions for creating manifests from releases and plugins.
- Enhanced Plugin struct to include Versions and InstallPlan for direct installations.
- Introduced validation for direct install type, ensuring artifacts are correctly specified.
- Updated registry validation to support new schema version and direct install requirements.
- Added tests for parsing and validating direct install plugins, ensuring correct artifact handling.
- Introduced `activeRecords` and `activeRecordsFromSnapshot` to filter current plugin records more efficiently.
- Updated all instances of `Snapshot().records` across code and tests to use `activeRecords`.
- Added additional checks for `recordCurrent` and plugin identity validation in several plugin capability calls.
- Enhanced test logic to validate `activeRecords` usage and ensure consistent behavior.
- 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.
- 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.
- Added `rpcPluginError` to encapsulate plugin errors with HTTP status codes.
- Enhanced `decodeEnvelopeResult` to preserve and return detailed plugin errors with status codes.
- Introduced `isPluginErrorEnvelope` to identify plugin error envelopes.
- Updated plugin call logic in Unix and Windows loaders to differentiate plugin errors from system errors.
- Added unit tests to verify error handling and status code preservation.
- 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.
- 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.
- Changed default plugin `Enabled` state from `true` to `false` across configurations, runtime logic, and YAML defaults.
- Added helper function `enabledPluginConfigs` for generating plugin configs with `Enabled` set explicitly.
- Expanded unit tests in `pluginhost`, `config`, and `management` to validate behavior changes for disabled plugins, default settings, and skipped load scenarios.
* 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
Keep RPC streaming executor callback scopes alive until async streams close, detach nested host.model.execute_stream contexts from request cancellation, and clean up the stream bridge on stream completion.
- Introduced `auth_callbacks` for handling host authentication list, get, runtime, and save operations.
- Added extensive unit tests to validate functionality, including disk fallback and runtime-specific cases.
- Created example implementation in Go to demonstrate host callback integrations.
- Introduced `htmlsanitize` package for escaping HTML and handling JSON body sanitization to prevent XSS vulnerabilities.
- Integrated sanitization functions into plugin store, plugin host, and API management handlers to ensure all user-facing content is escaped.
- Added unit tests to verify proper escaping of HTML strings, JSON bodies, and nested data structures.
- Updated existing management and plugin-related tests to validate sanitization implementations.
- Introduced logic to handle plugin unloading during updates to prevent conflicts with loaded plugins.
- Preserved existing plugin configurations during updates, ensuring seamless transitions and maintaining custom fields.
- Added support for reloading the configuration after management saves changes.
- Enhanced unit tests to validate unloading, configuration preservation, and reloading behaviors.
- Introduced `SupportPluginHeaderValue` to indicate CGO build status (`1` for enabled, `0` for disabled).
- Updated API response headers in `handler.go` to include "X-CPA-SUPPORT-PLUGIN".
- Added unit tests to verify proper header behavior under varying conditions.
- Updated host model callback logic to skip originating plugin's interceptors during nested model executions.
- Added `SkipInterceptorPluginID` field to plugin API structs for controlling interceptor bypass behavior.
- Introduced supporting logic in host API handlers, plugin host registry, and callback contexts to identify and skip specific plugins.
- Enhanced unit tests across plugin host, API handlers, and execution paths to verify interceptor skipping behavior and plugin isolation.
- Revised documentation to clarify non-recursive behavior of host model callbacks and the use of `SkipInterceptorPluginID`.
- Added an example plugin `host-model-callback` in Go to summarize host model callbacks.
- Implemented `cliproxy_plugin_init`, `cliproxyPluginCall`, and other plugin functions for callback handling.
- Introduced API handlers for `ModelExecution` and `ModelExecutionStream` with support for both streaming and non-streaming requests.
- Included unit tests (`model_execution_test.go`) to validate execution logic and streaming responses.
- Introduced `applyRequestAfterAuthInterceptor` to modify requests after credential selection and before executor translation.
- Added `InterceptRequestAfterAuth` method across plugin adapters with corresponding tests for context validation.
- Enhanced format resolution logic (`requestToFormat`) to support additional providers and formats.
- Updated JavaScript handler to include a new `on_after_auth_request` hook for post-auth request handling.
- Refactored interceptor methods for clarity and better encapsulation of request/response lifecycles.
- Added `resources` field in `management.register` for defining browser-accessible resources.
- Updated examples and documentation to reflect resource-based paths under `/v0/resource/plugins/<pluginID>/...`.
- Replaced legacy `GET` menu routes with resource-based implementations for consistent plugin behavior.
- Enhanced request handling for resource paths, including proper response headers and streamlined test coverage.
- Added `HostCallbackID` to request, response, and stream chunk interceptors for enhanced context tracking.
- Updated JavaScript engine to support custom console logging with `HostCallbackID` forwarding.
- Introduced tests verifying proper integration of `HostCallbackID` in all interceptor flows and engine logging.
- Enhanced logging and error handling for consistent callback-related logic implementation.
- Added `pluginSchedulerState` interface with `HasScheduler` method for improved plugin scheduler state checks.
- Updated `Manager.hasPluginScheduler` to handle `HasScheduler` logic.
- Implemented and tested fast-path handling for inactive plugin schedulers, including mixed provider scenarios.
- Expanded unit test coverage to ensure correct behavior in various scheduler states.