Skip to content

v1.6.3 - #4970

Merged
akshaydeo merged 139 commits into
mainfrom
dev
Jul 6, 2026
Merged

v1.6.3#4970
akshaydeo merged 139 commits into
mainfrom
dev

Conversation

@akshaydeo

Copy link
Copy Markdown
Contributor

✨ Features

  • DeepSeek Provider - Added DeepSeek as a first-class provider with dedicated request handling and thinking-mode gating
  • AWS Bedrock Mantle Provider - Added bedrock_mantle as a first-class provider with SigV4 key config, native-Anthropic and OpenAI-compatible routing, DB migration, and UI support
  • OAuth 2.1 Gateway Auth for MCP - Added a full OAuth2 authorization server for /mcp: discovery endpoints, dynamic client registration, authorize/token with PKCE and refresh token rotation, consent page, JWT Bearer authentication, session listing/revocation with sweep worker, OAuth Grants UI, and mcp_server_auth_mode config
  • Virtual Key Expiry - Added an expiry field to virtual keys with governance enforcement
  • ClickHouse Log Store (Beta) - Added ClickHouse support for the log store, including a hybrid store mode. This feature is in beta and may have some corner cases.
  • IPv6 Support - Added IPv6 support to the HTTP transport
  • Per-MCP-Server Tool Timeout - Added per-MCP-server tool execution timeout configuration (thanks @Purvi09!)
  • OpenAI Responses Lifecycle APIs - Added missing OpenAI Responses lifecycle methods with explicit per-verb governance flags (thanks @17jmumford!)
  • MCP Clients Filtering & Pagination - Added connection_type, auth_type, state, virtual_key, and server/client_id filters with pagination and a faceted filter sidebar on the MCP clients page
  • Deprecated Model Marking - Models are now marked is_deprecated in pricing and catalog APIs instead of being filtered out of responses
  • Log Attribution Columns - Added user, team, customer, and business-unit name columns to the logs list with multi-value attribution cells
  • Latency on Errors - Error responses now carry latency information
  • Env-Store Virtual Key Values - Virtual key values now use schemas.SecretVar, enabling env-store references
  • Connector Multi-Attribution - Connectors can now attach multiple teams, customers, and business units
  • Supplemental External Budgets - Added support for externally resolved supplemental budgets not tracked against a virtual key
  • Cost Recalculation Progress - Cost recalculation now streams progress via SSE with batch processing
  • Vendor-Prefix Pricing Fallback - Extended Bedrock vendor-prefix pricing fallback to OpenAI, Google, and xAI models
  • Complexity Router Improvements - Added stemming alongside exact keyword match and a no-signal fallback to the complexity analyzer
  • MCP VK Header - Added x-goog-api-key as a supported virtual-key header on the MCP auth path

🐞 Fixed

  • Anthropic Redacted Thinking - Round-trip redacted_thinking blocks on chat completions so tool-use turns with extended thinking replay correctly (thanks @fus3r!)
  • Bedrock Streaming Block Boundaries - Emit contentBlockStop events on the Bedrock ConverseStream egress (thanks @fus3r!)
  • Cache Token Accounting - Report cached_tokens as reads only per the OpenAI spec so cache writes are not billed as reads (thanks @fus3r!)
  • Streaming Retries & Fallbacks - Clear the per-attempt stream close claim so streaming retries and fallbacks work after SSE-embedded provider errors (thanks @fus3r!)
  • Governance Team IDs - Decode URL-encoded team IDs in fetch, update, and delete endpoints (thanks @nnNyx!)
  • Semantic Cache Keys - Resolve semantic cache internal embedding keys like external requests (thanks @nnNyx!)
  • Gemini Batch Responses - Surface Gemini batch inline responses from the response field instead of dest (thanks @nnNyx!)
  • Governance Rate-Limit CPU - Skip O(N) reference refresh on request-time rate-limit and budget reset
  • Tier Cost Calculation - Evaluate tier costs via input tokens instead of total tokens
  • Cancelled Requests - Fixed stats and log state for cancelled requests
  • Billing on Failed Streams - Fixed billing on failed Responses stream requests for Anthropic and Bedrock, and cost for image generation and edit streaming
  • Custom Provider Budgets - Custom providers with spaces in their names can now set budgets
  • Model Parameters URL - Honor model_parameters_url changes in config.json like pricing_url (thanks @jeremym-tanium!)
  • Bedrock Truncation Signal - Signal Bedrock max_output_tokens truncation on the Responses API (thanks @jeremym-tanium!)
  • MCP Reconnect - Fixed MCP clients registering as connected with an empty tool set when ListTools fails during startup (thanks @HackToHell!)
  • MCP Tool Ordering - Deterministic MCP tool ordering for prompt cache stability
  • Vertex gs:// Images - Pass through gs:// image URLs on Vertex Gemini (thanks @G-XD!)
  • Hybrid Log Token Usage - Rebuild token usage from denormalized columns in the hybrid log list (thanks @G-XD!)
  • Anthropic Files - Preserve file ID document sources (thanks @mmacvicar!) and forward file IDs and content type on the Anthropic files integration
  • Gemini Upload MIME Type - Preserve file upload MIME types (thanks @mmacvicar!)
  • Content Logging Bypass - Sanitize ErrorDetailsParsed so raw payloads honor disable_content_logging (thanks @citrocat!), plus error-detail sanitization on the log update path
  • Trace Store Memory Leak - Sweep orphaned deferred spans in trace store TTL cleanup (thanks @citrocat!) and complete deferred LLM spans on streaming goroutine exit
  • Claude Code Passthrough Streaming - Consistent content_block indices for server tools (thanks @surki!)
  • Codex Tool Search Round-Trip - Preserve codex tool_search_call and tool_search_output input items on the Responses API (thanks @raghu-nandan-bs!)
  • Gemini Fixes - Guard tool call config, fix the 2.5-pro thinking budget value, OpenAI-through signature compatibility, and video reference field mapping (thanks @vojthor!)
  • DeepSeek Thinking - Convert thinking to disabled when tool choice is required
  • OpenAI Integration - Propagate max_tokens from the OpenAI integration and pass chunking_strategy through as an extra param
  • Bedrock Error Types - Fixed error type setting in all integrations for Bedrock
  • Perplexity Responses - Fixed Perplexity Responses API compatibility
  • Secret Detection - Set SecretTypePlainText for plain-text JSON and non-prefixed secret values, and check whether virtual key values are secrets
  • Empty Tool Results - Fixed empty tool call result insertion failures
  • Error Redaction - Redact decoder details from invalid request payload errors
  • Vertex Idle Timeout - Fixed idle timeout wiring in the Vertex path
  • Web Fetch - Assorted web fetch fixes
  • MCP Token Refresh - Skip background token refresh for disabled or unconfigured MCP clients and exclude terminal-status OAuth configs from the refresh query
  • SSO Login Loop - Fixed an endless login loop on SSO
  • UI Fixes - Governance form calendar-aligned toggle gating, dashboard array query params, MCP sessions table scrolling with sticky header, audit logs layout, and model catalog key aliases displayed as model names

🐙 Closed GitHub Issues

  • #2347 - MCP tool ordering is non-deterministic, breaking prefix-based prompt caching
  • #3106 - Governance team delete/fetch fails for SCIM-synced team IDs containing spaces or URL-sensitive characters
  • #3121 - OpenAI responses.retrieve() not supported
  • #3139 - Bifrost adds non-standard reasoning/reasoning_details fields to chat completions when using a custom provider for deepseek v4 models
  • #3357 - Bifrost billing discrepancy for cancelled requests
  • #3951 - Gemini batch: inline responses (dest.inlinedResponses) are silently dropped, leaving output_file_id null
  • #4262 - Bedrock ConverseStream egress never emits contentBlockStop (breaks strands streaming)
  • #4314 - MCP client registered as connected with empty tool set when ListTools fails during connect/reconnect
  • #4402 - Vertex provider drops image blocks whose URL uses gs:// scheme
  • #4446 - Add per MCP server level tool timeout configuration
  • #4679 - Bedrock Responses API does not signal max_output_tokens truncation
  • #4689 - Custom providers cannot set budget
  • #4720 - chunking_strategy is dropped for OpenAI-compatible transcription requests
  • #4721 - Logs table Tokens column shows N/A when hybrid object storage is enabled
  • #4756 - semantic_cache internal embedding path bypasses plugin pipeline, causing "no keys found" while direct /v1/embeddings works
  • #4777 - Image generation stream: completed chunk returns empty output_tokens_details, causing under-billing
  • #4788 - DeepSeek Anthropic-compatible provider causes "stream closed" error in v1.6.0 (regression from v1.5.16)
  • #4816 - /v1 chat completions folds cache-write tokens into prompt_tokens_details.cached_tokens
  • #4851 - v1.6.2 governance rate-limit reset causes high CPU in BumpRateLimitUsage/updateRateLimitReferences
  • #4863 - model_parameters_url in config.json is ignored after the DB value is set
  • #4868 - Memory leak: orphaned deferred spans in TraceStore are never TTL-swept
  • #4872 - Raw request/response payloads bypass disable_content_logging via ErrorDetailsParsed
  • #4942 - redacted_thinking blocks are dropped on chat completions, breaking tool-use replay with extended thinking

impoiler and others added 30 commits July 1, 2026 05:23
)

## Summary

Fixes the layout of the Audit Logs page to correctly fill the viewport and apply the appropriate background and border styles.

## Changes

- Replaced `h-[calc(100dvh-1rem)]` with `h-[calc(100vh-16px)]` for consistent viewport height calculation
- Swapped `mx-auto flex flex-col p-4` utility classes for `no-border-parent bg-background flex` to align with the layout conventions used elsewhere in the app
- Added missing newline at end of file

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

Navigate to the Audit Logs page and verify:
- The page fills the full viewport height without overflow or clipping
- The background color and border styling match the rest of the workspace layout

```sh
cd ui
pnpm i || npm i
pnpm build || npm run build
```

## Screenshots/Recordings

Add before/after screenshots showing the corrected Audit Logs page layout.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

Adds collapsible tag support to the `TagInput` component and applies it to the keyword lists on the Complexity Router page. When a keyword list exceeds a configurable limit, tags beyond that limit are hidden behind a gradient overlay with a "Show more" toggle, keeping the UI compact while still allowing full access to all tags.

## Changes

- Added `collapsedTagLimit` and `expandButtonTestId` props to `TagInput`. When `collapsedTagLimit` is provided, the component renders in a collapsible layout: tags beyond the limit are hidden with a fade gradient, and "Show more" / "Show less" buttons toggle the expanded state. The collapsed state auto-resets when the tag count drops back to or below the limit.
- Set `KEYWORD_COLLAPSED_LIMIT = 8` on the Complexity Router page and passed it along with a `expandButtonTestId` to each keyword `TagInput`.
- Standardized border radius tokens from `rounded-lg`/`rounded-md`/`rounded-full` to `rounded-sm` across the Complexity Router page for visual consistency.
- Reformatted `index.html` inline shell skeleton from a single minified line to readable, indented HTML and CSS.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

```sh
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build
```

1. Navigate to the Complexity Router page.
2. Add more than 8 keywords to any keyword list.
3. Verify that tags beyond 8 are hidden with a gradient overlay and a "Show more" button appears.
4. Click "Show more" and confirm all tags are visible with a "Show less" button.
5. Click "Show less" and confirm the list collapses again.
6. Remove tags until 8 or fewer remain and confirm the list stays expanded without the toggle controls.

## Screenshots/Recordings

Before/after screenshots of the keyword lists with collapse behavior recommended.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…#4722)

* fix: rebuild token_usage from denormalized columns in hybrid log list

* refactor: inline hybrid token usage reconstruction

* fix: preserve malformed serialized token usage state

---------

Co-authored-by: gexiangdong <xiangdong.ge@pandasofcaribbean.com>
…hatRequestBody` and `AnthropicProviderRequestDefaultsMap` (#3309)

## Summary

This PR consolidates Anthropic-family request building across all providers (Anthropic native, Azure, Vertex, Bedrock) into two shared builder functions — `BuildAnthropicChatRequestBody` and `BuildAnthropicResponsesRequestBody` — eliminating duplicated inline logic and provider-specific wrapper helpers that previously scattered the same field-stripping, beta-header injection, and model-field manipulation across multiple files.

## Changes

- Introduced `AnthropicProviderRequestDefaults` and `AnthropicProviderRequestDefaultsMap` to encode static, per-provider request-shaping flags (e.g. `DeleteModelField`, `DeleteStreamField`, `AddAnthropicVersion`, `InjectBetaHeadersIntoBody`) in one place. Callers no longer pass these flags directly; the builder looks them up by `cfg.Provider`.
- Renamed `Deployment` to `Model` in `AnthropicRequestBuildConfig` for clarity, since all providers now use the same field for model/deployment overrides.
- Added `BuildAnthropicChatRequestBody` as the chat-completion analogue of `BuildAnthropicResponsesRequestBody`, covering both raw-body and typed paths, including field stripping, beta-header injection, streaming flag handling, and `fallbacks` deletion.
- Bedrock now routes Anthropic models through the Anthropic Messages API format (`invoke` / `invoke-with-response-stream` endpoints) for both chat and responses, rather than the Bedrock Converse API. This includes proper response parsing via `AcquireAnthropicMessageResponse` and streaming via `AnthropicStreamState` / `AnthropicResponsesStreamState`.
- Removed private wrapper functions `getRequestBodyForResponses` (Anthropic), `getRequestBodyForAnthropicResponses` (Azure, Vertex), and the inline `CheckContextAndGetRequestBody` closures for Anthropic models in Vertex and Azure, replacing all call sites with direct `BuildAnthropicChatRequestBody` / `BuildAnthropicResponsesRequestBody` calls.
- Exported `AcquireAnthropicResponsesStreamState`, `ReleaseAnthropicResponsesStreamState`, `AcquireAnthropicMessageResponse`, and `ReleaseAnthropicMessageResponse` so Bedrock can reuse the Anthropic stream state pool.
- Removed `DefaultVertexAnthropicVersion` constant from the Vertex package; the canonical version string now lives in `AnthropicProviderRequestDefaultsMap`.
- Bedrock's `releaseBedrockChatResponse` now zeroes the struct before returning it to the pool.
- `stripUnsupportedAnthropicFields` is now called inside `BuildAnthropicResponsesRequestBody` on the typed path, making field stripping symmetric across raw and typed paths and across both APIs.

## Type of change

- [ ] Bug fix
- [ ] Feature
- [x] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/providers/anthropic/...
go test ./core/providers/azure/...
go test ./core/providers/bedrock/...
go test ./core/providers/vertex/...
go test ./...
```

Run integration tests against Anthropic, Azure (Anthropic models), Vertex (Claude models), and Bedrock (Claude models) for chat completion, streaming, responses, responses streaming, and count-tokens endpoints. Verify that raw-body passthrough requests produce the same field stripping and beta-header injection as typed requests.

## Breaking changes

- [x] Yes
- [ ] No

`AnthropicRequestBuildConfig` has a breaking field rename: `Deployment` → `Model`. Any external code constructing this struct directly must update the field name. The static shaping flags (`DeleteModelField`, `DeleteRegionField`, `AddAnthropicVersion`, `AnthropicVersion`, `StripCacheControlScope`, `RemapToolVersions`, `InjectBetaHeadersIntoBody`) have been removed from `AnthropicRequestBuildConfig` and are now looked up internally via `AnthropicProviderRequestDefaultsMap`; callers that set these fields must remove them.

## Related issues

## Security considerations

No new auth flows, secrets handling, or PII exposure introduced. Field stripping ensures provider-unsupported fields are not forwarded to external APIs.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…hropicResponsesRequest` and make `completeRequest` a package-level func shared by Anthropic, Azure, and Bedrock providers (#4394)

## Summary

The Anthropic provider's unary request logic was duplicated across the Anthropic, Azure, Bedrock, and Vertex providers. This PR extracts the core non-streaming request execution into a package-level `completeRequest` function and introduces two exported handler functions — `HandleAnthropicChatCompletionRequest` and `HandleAnthropicResponsesRequest` — that encapsulate the full build → send → parse pipeline for chat completions and the Responses API respectively. Azure and Bedrock now delegate directly to these shared handlers for Anthropic-family models instead of reimplementing request dispatch, response parsing, and raw request/response handling inline.

A secondary bug fix is included: the large-response streaming client was being activated for count-tokens requests (which should always be buffered) and skipped for all other requests — the condition was inverted.

## Changes

- Extracted `completeRequest` as a package-level function accepting explicit `client`, `headers`, `extraHeaders`, `betaHeaderOverrides`, `providerName`, and `logger` arguments, removing the method receiver dependency so it can be called by other providers.
- Added `anthropicRequestHeaders` as a provider method to build the `x-api-key` / `anthropic-version` header map, shared across `TextCompletion`, `ChatCompletion`, `Responses`, and `CountTokens`.
- Introduced `HandleAnthropicChatCompletionRequest` and `HandleAnthropicResponsesRequest` as exported functions that perform the full unary request lifecycle (body build, HTTP send, large-response detection, response parse, raw request/response attachment). These are now called by the Anthropic, Azure, and Bedrock providers.
- Removed `completeMantleRequest` from Bedrock — its logic is now covered by `completeRequest` inside the shared handlers.
- Azure's `ChatCompletion` and `Responses` methods now branch early for Anthropic-family models, calling the shared handlers with Azure-specific auth headers, and fall through to the OpenAI-compatible path otherwise, eliminating the post-response model-family branch.
- Fixed the inverted condition in `completeRequest` that caused the large-response streaming client to be used for count-tokens requests instead of being skipped for them.
- `AnthropicRequestBuildConfig` now carries `BetaHeaderOverrides` so callers do not need to pass it separately.

## Type of change

- [ ] Bug fix
- [ ] Feature
- [x] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./...
```

Validate that chat completions and Responses API requests succeed for Anthropic-family models routed through the Azure and Bedrock providers, and that count-tokens requests return buffered responses without triggering large-response mode.

## Breaking changes

- [ ] Yes
- [x] No

## Security considerations

Auth headers (`x-api-key`, Bearer tokens, SigV4-signed headers) are applied last in `completeRequest`, after network-config extra headers, ensuring they cannot be overridden by user-supplied configuration. No new secrets or PII handling paths are introduced.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…atible providers from `schemas.Key` to `map[string]string` auth header param (#4425)

## Summary

This PR standardizes how Bearer token authentication headers are constructed across all OpenAI-compatible providers. Previously, each call site independently built the `Authorization: Bearer <token>` header map with duplicated inline logic. A new `BearerAuthHeader(key)` helper is introduced in the OpenAI package and used uniformly everywhere.

Additionally, the Azure provider's private `completeRequest` method is removed. Its non-Anthropic request paths (text completion, chat completion, responses, embedding, compaction) are now delegated directly to the shared `Handle*` functions in the OpenAI package, consistent with how other providers already work. The `Handle*` functions themselves are updated to accept a pre-built `authHeader map[string]string` instead of a raw `schemas.Key`, making them provider-agnostic and compatible with non-Bearer auth schemes (e.g., Azure API key headers, SigV4).

## Changes

- Added `BearerAuthHeader(key schemas.Key) map[string]string` to the OpenAI provider package, which returns an `Authorization: Bearer <token>` header map, or an empty map when the key carries no value.
- Updated all `Handle*Request` and `handleOpenAILargePayloadPassthrough` function signatures to accept `authHeader map[string]string` instead of `schemas.Key`, applying the map directly to request headers.
- Replaced all inline `var authHeader map[string]string` + conditional assignment blocks across Cerebras, Fireworks, Groq, HuggingFace, Mistral, Nebius, Ollama, Opencode, OpenRouter, Parasail, Perplexity, SGL, VLLM, xAI, and Bedrock with calls to `openai.BearerAuthHeader(key)`.
- Removed the Azure provider's `completeRequest` method and replaced its usage in `TextCompletion`, `ChatCompletion`, `Responses`, `Embedding`, and `Compaction` with direct calls to the corresponding shared OpenAI `Handle*` functions, passing Azure-specific auth headers and pre-resolved endpoint URLs.

## Type of change

- [ ] Bug fix
- [ ] Feature
- [x] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./...
```

Verify that all OpenAI-compatible providers (OpenAI, Azure, Cerebras, Fireworks, Groq, HuggingFace, Mistral, Nebius, Ollama, Opencode, OpenRouter, Parasail, Perplexity, SGL, VLLM, xAI, Bedrock Mantle) continue to authenticate correctly and that requests succeed for text completion, chat completion, responses, embeddings, and compaction endpoints.

## Breaking changes

- [ ] Yes
- [x] No

## Security considerations

The `BearerAuthHeader` helper preserves the existing behavior of omitting the `Authorization` header when the key value is empty, which is intentional for providers that supply auth via other mechanisms (e.g., extra headers or SigV4 signing). No secrets are logged or exposed.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
… closure passed through request handlers (#4735)

## Summary

Replaces the pre-build-and-sign approach for Bedrock Mantle SigV4 authentication with a `BodySigner` callback that is invoked after the request handler has marshaled the body. This ensures the signature always covers the exact bytes sent on the wire, eliminating the previous double-marshal pattern where the body was built once for signing and again inside the handler.

## Changes

- Introduces a new `BodySigner` type (`func(jsonData []byte) (map[string]string, *schemas.BifrostError)`) in `core/providers/utils/bodysigner.go`. Handlers call it after building the request body and apply the returned headers to the outgoing request.
- Adds the `signer` parameter to `HandleOpenAIChatCompletionRequest`, `HandleOpenAIChatCompletionStreaming`, `HandleOpenAIResponsesRequest`, `HandleOpenAIResponsesStreaming`, `HandleAnthropicChatCompletionRequest`, `HandleAnthropicChatCompletionStreaming`, `HandleAnthropicResponsesRequest`, and `HandleAnthropicResponsesStream`. All existing callers pass `nil`.
- Rewrites Bedrock Mantle's SigV4 paths (`mantleChatCompletions`, `mantleChatCompletionsStream`, `mantleResponses`, `mantleResponsesStream`) to construct a `BodySigner` closure when no API key is present, instead of pre-building the body, signing it, and merging the signature headers into `extraHeaders`. The Bearer path no longer needs a separate early-return branch.
- Removes the now-unnecessary `maps` import and the intermediate `extraHeaders` map copies in the Mantle code paths.

## Type of change

- [ ] Bug fix
- [x] Refactor
- [ ] Feature
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./...
```

For Bedrock Mantle with SigV4 (empty key value), verify that requests to chat completions, streaming chat completions, responses, and streaming responses are signed correctly and accepted by the Bedrock endpoint. For Bearer key paths, confirm that no signing is attempted and the `Authorization` header is set as expected.

## Breaking changes

- [ ] Yes
- [x] No

## Security considerations

The `BodySigner` callback signs the exact serialized bytes that are placed on the wire. Previously, the body was serialized twice (once for signing, once inside the handler), which could in theory produce a signature mismatch if marshaling were non-deterministic. This change closes that gap by signing after the final body is set.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…ropic and OpenAI-compatible routing (#4736)

## Summary

Introduces `bedrock_mantle` as a first-class, standalone provider that owns the Bedrock Mantle surface (`bedrock-mantle.{region}.api.aws`). Previously, Mantle routing was handled as an internal routing decision inside the existing `bedrock` provider. The new provider gives operators a dedicated configuration surface for Claude (native Anthropic Messages API), OpenAI-compatible models (gpt-*), and Gemma models served through Mantle, without requiring a full Bedrock setup.

## Changes

- Added `schemas.BedrockMantle` (`"bedrock_mantle"`) as a new `ModelProvider` constant and registered it in `StandardProviders`, `dynamicallyConfigurableProviders`, `CanProviderKeyValueBeEmpty`, and `isKeySkippingAllowed`.
- Added `BedrockMantleKeyConfig` to the `Key` struct, carrying AWS credentials and region for SigV4 auth against the `bedrock-mantle` service. The existing `BedrockKeyConfig` is unchanged.
- Introduced the `core/providers/bedrockmantle` package implementing the full `Provider` interface. Chat, streaming chat, Responses, and streaming Responses dispatch by model family: Anthropic-family models use the native Anthropic Messages surface (`/anthropic/v1/messages`); all others use the OpenAI-compatible surface (`/v1` or `/openai/v1`). All other operations return unsupported-operation errors.
- Refactored `signAWSRequest` in the `bedrock` package to accept a `*BedrockKeyConfig` instead of individual credential fields, eliminating the now-redundant `signAWSRequestFromKey` wrapper. All call sites updated accordingly.
- Exported `SignMantleV4Headers` (previously `mantleSigV4Headers`, a method on `BedrockProvider`) so the new `bedrockmantle` package can sign requests without depending on the internal Bedrock provider struct. The function now supports both `BedrockKeyConfig` and `BedrockMantleKeyConfig` by mapping the latter into a synthetic `BedrockKeyConfig` for signing, and correctly handles GET requests (nil body) for the list-models path.
- Extended the Anthropic chat and Responses request builders to convert native structured outputs to tool calls for `BedrockMantle`, matching the existing `Vertex` workaround.
- Added `BedrockMantle` to the comprehensive LLM test harness (`ComprehensiveTestAccount`) with key config, provider config, and a full test file covering the supported scenarios (chat, streaming, tool calls, vision, structured outputs, prompt caching, reasoning, list models) and explicitly disabling unsupported ones.
- Marked `isMantleModel` in `bedrock/mantle.go` as deprecated in favour of the new provider.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Set AWS credentials and run the new provider test:

```sh
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...   # optional, for temporary credentials
export AWS_REGION=us-east-1

go test ./core/providers/bedrockmantle/... -v -run TestBedrockMantle
```

To run the full suite (skips Bedrock Mantle automatically when credentials are absent):

```sh
go test ./...
```

Configure a `bedrock_mantle` provider by supplying a `BedrockMantleKeyConfig` (or a Bearer API key in `Value`) with the desired region. The region can also be embedded as a prefix in the model ID (e.g. `us-west-2/anthropic.claude-haiku-4-5`) or set at the alias level via `AliasConfig.Region`.

## Breaking changes

- [ ] Yes
- [x] No

The `signAWSRequest` signature change is internal to the `bedrock` package and does not affect any public API. The `isMantleModel` function is deprecated but not removed.

## Security considerations

AWS credentials for `BedrockMantleKeyConfig` follow the same `SecretVar` resolution pattern used by `BedrockKeyConfig` (env-var references, never inlined literals). SigV4 signing is performed per-request on the exact body bytes that are sent, so the signature always covers what is transmitted. When a Bearer API key is present it takes precedence and no AWS credentials are required.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…on, and UI support (#4737)

Adds `bedrock_mantle` as a first-class provider, enabling Bifrost to route requests to AWS Bedrock through a Mantle proxy endpoint. The provider supports the same SigV4 credential options as the existing Bedrock provider (inherited IAM role, explicit access/secret key, session token, AssumeRole) as well as a Bearer API key authentication mode.

- Added `BedrockMantle` to the Anthropic passthrough allowlist in `clearAnthropicPassthroughForNonNativeProvider` so raw request bodies are preserved when routing through Bedrock Mantle.
- Added `BedrockMantleKeyConfig` redaction logic in `clientconfig.go`, mirroring the existing Bedrock redaction pattern.
- Added a new `migrationAddBedrockMantleKeyColumns` database migration that introduces seven `bedrock_mantle_*` SigV4 credential columns to the `config_keys` table.
- Extended `TableKey` with the seven Bedrock Mantle credential fields, along with `BeforeSave` serialization and `AfterFind` reconstruction hooks.
- Updated `mergeUpdatedKey` in the HTTP handler to correctly restore redacted Bedrock Mantle credential fields during key updates.
- Fixed `isClaudeModel` in the Anthropic integration to recognize `bedrock_mantle` (previously incorrectly matched `bedrock`) as a provider that can serve Claude models.
- Included `BedrockMantleKeyConfig` in the key hash inputs used by `mergeProviderKeys` and `reconcileProviderKeys` for config file/DB reconciliation.
- Added Bedrock Mantle credential redaction to `GetAllKeys`.
- Extended `config.schema.json` with `bedrock_mantle_key` and `provider_with_bedrock_mantle_config` definitions and registered `bedrock_mantle` as a valid provider name throughout the schema.
- Added UI support: provider icon (reusing the Bedrock SVG mark with a distinct gradient ID), model placeholder text, `isKeyRequiredByProvider` entry, label, form schema (`BedrockMantleKeyConfigSchema`), type definitions (`BedrockMantleKeyConfig`, `DefaultBedrockMantleKeyConfig`), and a full authentication method tab UI (IAM Role / Explicit Credentials / API Key) matching the Bedrock provider UX.
- Added `bedrock_mantle` to the Anthropic beta-headers provider family and the provider config sheet's Anthropic family list.
- Stripped the internal `_auth_type` field from `bedrock_mantle_key_config` before submitting the form payload.

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

```sh
go version
go test ./...

cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build
```

Configure a `bedrock_mantle` provider in `config.json` or via the UI with one of the three auth methods:

- **IAM Role (Inherited):** set only `region`; leave access/secret key empty.
- **Explicit Credentials:** set `access_key`, `secret_key`, and `region`; optionally set `session_token`, `role_arn`, `external_id`, and `session_name`.
- **API Key:** set `region` and provide a Bearer token as the key `value`.

Send a request targeting a Claude model through the `bedrock_mantle` provider and verify the response is returned correctly and that credentials are redacted in the UI and API responses.

_Add before/after screenshots of the new Bedrock Mantle provider form and icon in the UI._

- [ ] Yes
- [x] No

_Link related issues and discussions._

- All seven Bedrock Mantle credential fields (`access_key`, `secret_key`, `session_token`, `region`, `role_arn`, `external_id`, `role_session_name`) are stored as `SecretVar` and are redacted in API responses and the UI, consistent with the existing Bedrock provider handling.
- The `_auth_type` discriminator field is stripped from the payload before it is persisted or transmitted.

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

Adds documentation for the AWS Bedrock Mantle provider, a distinct AWS endpoint (`bedrock-mantle.{region}.api.aws`) that exposes Claude models via the native Anthropic Messages API and OpenAI-family/Gemma models via an OpenAI-compatible API — all addressable through a single `bedrock_mantle/<model>` prefix in Bifrost.

## Changes

- Added a new `bedrock-mantle.mdx` provider page covering model ID formats, supported operations, all three authentication modes (SigV4 with explicit credentials, IAM role/inherited credentials, and Bearer API key), IAM role assumption via `role_arn`, and usage examples.
- Added the Bedrock Mantle configuration block to the `providers.mdx` config reference, with tabs for Static Credentials, IAM Role, and API Key (Bearer) auth modes.
- Added Bedrock Mantle to the provider capability matrix in `overview.mdx`.
- Registered `bedrock-mantle` in `docs.json` so it appears in the sidebar navigation.

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [x] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [x] Docs

## How to test

Navigate to the Bedrock Mantle provider page and config reference in the rendered docs and verify:

- The sidebar entry for `bedrock-mantle` appears between `bedrock` and `cerebras`.
- All three auth tabs (Static Credentials, IAM Role, API Key) render correctly in both the provider page and the config reference.
- The capability matrix row for `bedrock_mantle/<model>` is present and accurate.
- Cross-links between the provider page and the config reference resolve correctly.

## Breaking changes

- [x] No

## Security considerations

Authentication credentials (`access_key`, `secret_key`, `session_token`, API keys) are documented using the `env.*` indirection pattern, consistent with how other providers handle secrets. No credentials are hardcoded in examples.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…ment config (#4739)

Adds E2E test configuration and capability definitions for the `bedrock_mantle` provider, enabling it to be tested through the Bifrost V1 API test suite.

- Added `bedrock_mantle` to `provider-capabilities.json` with `chat_completions`, `chat_completions_with_tools`, `responses`, `responses_with_tools`, and `list_models` enabled
- Added a new Postman environment file (`bifrost-v1-bedrock-mantle.postman_environment.json`) configured to use `anthropic.claude-opus-4-8` as the default model and `us-east-1` as the default region, with secret placeholders for API key, access key, secret key, and session token

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

Run the E2E test suite targeting the `bedrock_mantle` provider using the new Postman environment:

```sh

newman run tests/e2e/api/bifrost-v1.postman_collection.json \
  -e tests/e2e/api/provider_config/bifrost-v1-bedrock-mantle.postman_environment.json \
  --env-var "bedrock_mantle_api_key=<your_api_key>" \
  --env-var "bedrock_mantle_access_key=<your_access_key>" \
  --env-var "bedrock_mantle_secret_key=<your_secret_key>"
```

Expected outcome: chat completions, tool-use, responses, and model listing tests pass; all unsupported capability tests are skipped or return expected errors.

N/A

- [ ] Yes
- [x] No

N/A

The Postman environment file stores API key, access key, secret key, and session token as `secret` type fields with empty default values, ensuring credentials are not committed to the repository.

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…d toggle when budget is alignable (#4724)

## Summary

The calendar alignment toggle in the provider governance form was previously shown whenever any budget existed. This PR restricts its visibility and submission to only when at least one budget uses a calendar-alignable reset period (day, week, month, or year).

## Changes

- Introduced a `showCalendarAlignment` derived boolean that checks whether any configured budget has a reset duration supported by `supportsCalendarAlignment`.
- Replaced the previous condition (`watchedBudgets.length > 0`) with `showCalendarAlignment` to control rendering of the calendar alignment toggle.
- Updated the form submission payload so that `calendar_aligned` is only set to `true` when at least one budget actually supports calendar alignment — preventing the flag from being submitted for incompatible budget configurations.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

1. Navigate to a provider's governance settings in the UI.
2. Add a budget with a reset duration that does **not** support calendar alignment (e.g., hourly). Verify the calendar alignment toggle does **not** appear.
3. Add or change a budget to use a calendar-alignable period (e.g., daily, weekly, monthly, yearly). Verify the toggle **does** appear.
4. Enable the toggle and save. Confirm `calendar_aligned: true` is included in the submitted payload.
5. Remove all calendar-alignable budgets and save. Confirm `calendar_aligned` is not set to `true` in the payload.

```sh
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build
```

## Screenshots/Recordings

_Before:_ Calendar alignment toggle appears whenever any budget is present, regardless of reset period.

_After:_ Calendar alignment toggle only appears when at least one budget uses a day/week/month/year reset period.

## Breaking changes

- [x] No

## Related issues

## Security considerations

None.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

Fixes a bug where updating or deleting provider-level governance for a custom provider whose name contains a space (e.g. `"OpenRouter Base"`) would return a 404. The UI percent-encodes the provider name in the URL path (`OpenRouter%20Base`), but the handler was comparing the raw encoded string directly against the stored provider name, causing the lookup to fail. Closes #4689

## Changes

- `updateProviderGovernance` and `deleteProviderGovernance` now call `url.PathUnescape` on the `provider_name` path parameter before using it, matching the decoded name against what is stored in the config store.
- Returns a `400` if the path parameter contains an invalid percent-encoding sequence.
- Added a regression test (`TestProviderGovernance_DecodesEncodedProviderName`) that seeds a provider with a space in its name, issues a PUT and DELETE using the percent-encoded path param, and asserts both succeed and persist correctly.
- Added a guard test (`TestProviderGovernance_UnknownProviderStill404`) to confirm that a genuinely unknown provider still returns 404 after the decode change.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./transports/bifrost-http/handlers/... -run TestProviderGovernance
```

Expected output: all three `TestProviderGovernance_*` tests pass. Specifically:

- `TestProviderGovernance_DecodesEncodedProviderName` — PUT and DELETE with `OpenRouter%20Base` return `200`.
- `TestProviderGovernance_UnknownProviderStill404` — PUT with an unknown encoded name returns `404`.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

- Resolves #4689

## Security considerations

`url.PathUnescape` is used rather than `url.QueryUnescape` to correctly handle path-encoded characters. Invalid encoding sequences are rejected with a `400` rather than passed through, preventing malformed input from reaching the config store.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
Signed-off-by: Akshay Deo <akshay@akshaydeo.com>
Co-authored-by: Akshay Deo <akshay@akshaydeo.com>
* Fix mcp reconnect failure on startup

* test: assert failed MCP client cleanup

---------

Co-authored-by: Gowtham <692171+HackToHell@users.noreply.github.com>
…put items (#4121)

* fix(responses): preserve codex tool_search_call/tool_search_output input items

Bifrost's Responses input deserializer rejected codex's tool-search follow-up
request with HTTP 400 "openai responses request input is neither a string nor an
array of responses messages", which hung/failed the agent turn. The fix teaches
ResponsesMessage about the two tool_search item types and round-trips them
verbatim. Background, since tool_search is non-obvious:

How codex's tool_search works (the path that hits this bug)
-----------------------------------------------------------
codex normally sends every MCP tool inline in the request `tools[]` as
`{type:"function", ...}`. But when a model's catalog has
`supports_search_tool: true` AND the tool count crosses
DIRECT_MCP_TOOL_EXPOSURE_THRESHOLD (= 100) — e.g. an agent wired to several MCP
servers — codex stops sending them inline and "defers" them behind a discovery
tool:
  should_defer = supports_search_tool && (ToolSearchAlwaysDeferMcpTools || n >= 100)

The deferred flow is a two-request round-trip:

  1. Request 1: codex hides the deferred tools and instead declares one tool:
       {"type":"tool_search","execution":"client","description":"...",
        "parameters":{query, limit}}
     `execution:"client"` means the model does NOT run the search — codex does.

  2. The model emits a `tool_search_call` with `arguments` = {query, limit}.

  3. codex runs the search CLIENT-SIDE: a BM25 index over the deferred tool
     metadata (codex's ToolSearchHandler, core/src/tools/handlers/tool_search.rs,
     using the `bm25` crate). It picks the top-N matching tools.

  4. Request 2 (follow-up): codex appends two items to `input[]`:
       - {"type":"tool_search_call",   "call_id":..., "execution":"client",
          "arguments":{...}}
       - {"type":"tool_search_output", "call_id":..., "status":"completed",
          "execution":"client", "tools":[ {type:"function", ...the matches} ]}
     and also surfaces the discovered tools in `tools[]`. The model can now call
     them. This repeats as the model needs more tools.

Root cause
----------
ResponsesMessage (the element type of the Responses `input` array AND the
response `Output` array) doesn't model `tool_search_call` / `tool_search_output`:

  - The call's `arguments` is a JSON OBJECT, whereas function_call's `arguments`
    is a JSON STRING. So it cannot decode into ResponsesToolMessage.Arguments
    (*string) -> sonic.Unmarshal of the whole []ResponsesMessage errors ->
    OpenAIResponsesRequestInput.UnmarshalJSON falls through to the "neither a
    string nor an array" 400. The entire request dies before reaching OpenAI.
  - The output's `tools` array is also unmodeled (would be dropped/mangled,
    which OpenAI then rejects with "Missing input[N].tools[0].type").

OpenAI's Responses API supports both items natively (verified end-to-end against
the gateway: the tool_search tool spec is accepted and echoed; OpenAI validates
arguments-as-object and tools[].type). So this is purely a Bifrost modelling gap,
in the same family as the tool-type allowlist that already lists
ResponsesToolTypeToolSearch / ResponsesToolTypeNamespace — just a different code
path (input-item deserialization vs the request tools[] allowlist).

Fix
---
Add ResponsesMessageTypeToolSearchCall / ResponsesMessageTypeToolSearchOutput and
give ResponsesMessage custom (Un)MarshalJSON that preserves these two item types
verbatim (original bytes in, original bytes out), so the object `arguments` and
the `tools` array survive intact. Every other item type defers to the default
struct (de)coding, unchanged. One change covers both directions because request
input and response Output are both []ResponsesMessage.

Impact: unblocks codex tool-search deferral (multi-MCP-server / >=100-tool agents)
through Bifrost. Verified with a round-trip test reproducing the exact follow-up
payload, plus the existing providers/openai and schemas suites (no regressions).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(responses): reset ResponsesMessage receiver in UnmarshalJSON

Clear the receiver at the top of ResponsesMessage.UnmarshalJSON so a reused
instance never retains a stale rawToolSearch (or other field) from a prior
decode. Without this, unmarshalling a tool_search item and then a normal
message into the same value would leave the preserved bytes in place, and
MarshalJSON would re-emit them. Not reachable via the array-decode path (each
element starts zero), but a cheap, defensive correctness fix.

Addresses CodeRabbit review on PR #4121.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(openapi): correct prompt_cache_retention enum to in_memory

The chat schema declared the enum as [in-memory, 24h], but OpenAI's
actual accepted values are in_memory (underscore) and 24h. The hyphenated
form was a typo from when the enum was first added and never matched
OpenAI, so spec-generated clients produced Literal['in-memory', '24h']
and rejected the valid value with a pydantic literal_error.

The Go runtime treats prompt_cache_retention as a pass-through *string,
so no behavior changes — only the spec enum, the regenerated openapi.json,
and the doc comment are corrected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Signed-off-by: Akshay Deo <akshay@akshaydeo.com>
Co-authored-by: Suresh Kumar Ponnusamy <suresh@atomicwork.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Akshay Deo <akshay@akshaydeo.com>
## Summary

Fixes a bug in the Vertex provider where the code path for handling Gemini/Gemma model families was duplicated, with the non-streaming branch incorrectly using `ToGeminiChatCompletionRequest` (without image URL scheme support) while the streaming branch used `ToGeminiChatCompletionRequestWithImageURLSchemes`. This consolidates the logic so both paths use the image URL scheme-aware converter.

## Changes

- Replaced `ToGeminiChatCompletionRequest` with `ToGeminiChatCompletionRequestWithImageURLSchemes` in the non-streaming Gemini/Gemma branch, making it consistent with the streaming branch
- Removed the duplicate non-streaming Gemini/Gemma and OpenAI handler blocks that had been incorrectly separated from the streaming path, consolidating them into a single unified code path

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Send a chat completion request to the Vertex provider using a Gemini or Gemma model with image URL content. Verify that image URLs are correctly processed in both streaming and non-streaming modes.

```sh
go test ./core/providers/vertex/...
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

Consolidates the two separate `UnmarshalJSON` implementations on `ResponsesMessage` into a single method that handles both the verbatim `tool_search` preservation and the `arguments` normalization logic. Previously, the file contained a duplicate `UnmarshalJSON` definition — the first handled `tool_search` items and fell back to a plain `sonic.Unmarshal`, while the second (the correct one) handled argument normalization. The duplicate caused the normalization path to be unreachable for non-`tool_search` items, meaning `tool_search_call` items with object-typed `arguments` would silently fail mid-stream and hang streaming clients.

## Changes

- Removed the redundant first `UnmarshalJSON` that short-circuited to `sonic.Unmarshal` without normalizing `arguments`, leaving only the correct implementation that handles both the `rawToolSearch` early-return and the `arguments` object-to-string normalization.
- Relocated `MarshalJSON` to follow `UnmarshalJSON` for logical grouping.
- The fix ensures `tool_search_call` items whose `arguments` field is a JSON object (e.g. `{}` while in-progress, `{"query":"...","limit":10}` when completed) are correctly stringified into the `*string` field expected by `ResponsesToolMessage`, preventing decode failures that previously dropped items silently.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/schemas/...
```

Validate by sending a request that triggers `tool_search_call` streaming events and confirming that items with both `{}` (in-progress) and `{"query":"...","limit":10}` (completed) `arguments` values are decoded without error and do not hang the streaming client.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

Adds a `server` (client ID) filter to the MCP clients list endpoint and UI, allowing users to filter the MCP clients table to a specific server. The filter state is persisted in the URL via query parameters, enabling shareable and bookmarkable filtered views.

## Changes

- Added `ClientID` field to `MCPClientsQueryParams` and applied it as a `WHERE client_id = ?` clause in `GetMCPClientsPaginated`
- Exposed the filter via a new `server` query parameter on `GET /api/mcp/clients`
- Migrated the MCP registry page from local `useState` to `nuqs` `useQueryStates`, storing `search`, `server`, and `offset` in the URL
- Added `server` prop and `onServerFilterClear` callback to `MCPClientsTable`, rendering a dismissible "Server filter" badge/button when the filter is active
- Extended `GetMCPClientsParams` type and the RTK Query API call to pass the `server` parameter through to the backend
- `hasActiveFilters` now accounts for both `debouncedSearch` and `server`, preventing the empty state from showing while a server filter is active

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

1. Navigate to the MCP Registry page.
2. Confirm that `search`, `server`, and `offset` appear in the URL and survive a page refresh.
3. Set a `server` query param (e.g. `?server=<client_id>`) directly in the URL and verify the table filters to only that client.
4. Click the "Server filter" dismiss button and confirm the filter clears and the URL updates.
5. Verify that the empty state is not shown when a server filter is active but returns no results.

```sh
# Core/Transports
go test ./framework/configstore/... ./transports/bifrost-http/...

# UI
cd ui
pnpm i
pnpm build
```

## Screenshots/Recordings

_Add before/after screenshots showing the server filter badge and URL state._

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

_Link related issues here._

## Security considerations

The `client_id` filter is applied as a parameterised query (`WHERE client_id = ?`), so there is no SQL injection risk. No secrets or PII are exposed through the new filter parameter.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

Error messages returned on JSON decode failures were leaking internal decoder details (e.g., field names, Go type information, and `cannot unmarshal` messages) back to API callers. This replaces all such messages with a single, generic `"Invalid request payload"` string to avoid exposing implementation internals.

## Changes

- Replaced all `fmt.Sprintf("invalid/Invalid request format: %v", err)` and similar patterns across handlers (`config`, `featureflags`, `governance`, `inference`, `mcp`, `mcp_per_user_headers`, `mcpinference`, `provider_keys`, `providers`, `session`) with the static string `"Invalid request payload"`.
- Removed now-unused `fmt` import from `mcpinference.go`.
- Added `requestpayload_test.go` with two tests that assert the generic message is returned and that decoder internals (`cannot unmarshal`, field names, Go struct details) are not present in the response body or error string.
- Updated the existing `governance_test.go` assertion for the unknown-field case to expect `"Invalid request payload"` instead of `"unknown field"`.

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./transports/bifrost-http/handlers/...
```

Confirm that:
- `TestSessionLoginInvalidPayloadDoesNotExposeDecoderDetails` passes and the response body contains `"Invalid request payload"` with no decoder internals.
- `TestPrepareRequestInvalidPayloadDoesNotExposeDecoderDetails` passes and the returned error is exactly `"invalid request payload"`.
- `TestComplexityAnalyzerConfigPutRejectsInvalidPayloads` passes with the updated `"Invalid request payload"` expectation for the unknown-field case.

## Breaking changes

- [ ] Yes
- [x] No

## Security considerations

Decoder error messages from Go's `encoding/json` and `sonic` can expose internal struct field names, type information, and value details. Returning these verbatim in HTTP responses constitutes an information disclosure risk. This change ensures all parse-failure responses return a fixed, opaque message regardless of the underlying decode error.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…4708)

## Summary

Briefly explain the purpose of this PR and the problem it solves.

## Changes

- What was changed and why
- Any notable design decisions or trade-offs

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Describe the steps to validate this change. Include commands and expected outcomes.

```sh
# Core/Transports
go version
go test ./...

# UI
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build
```

If adding new configs or environment variables, document them here.

## Screenshots/Recordings

If UI changes, add before/after screenshots or short clips.

## Breaking changes

- [ ] Yes
- [ ] No

If yes, describe impact and migration instructions.

## Related issues

Link related issues and discussions. Example: Closes #123

## Security considerations

Note any security implications (auth, secrets, PII, sandboxing, etc.).

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…y analyzer's logic (#4791)

## Summary

Briefly explain the purpose of this PR and the problem it solves.

## Changes

- What was changed and why
- Any notable design decisions or trade-offs

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Describe the steps to validate this change. Include commands and expected outcomes.

```sh
# Core/Transports
go version
go test ./...

# UI
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build
```

If adding new configs or environment variables, document them here.

## Screenshots/Recordings

If UI changes, add before/after screenshots or short clips.

## Breaking changes

- [ ] Yes
- [ ] No

If yes, describe impact and migration instructions.

## Related issues

Link related issues and discussions. Example: Closes #123

## Security considerations

Note any security implications (auth, secrets, PII, sandboxing, etc.).

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…undant prefixes (#4747)

## Summary

Renames several handler files in the `bifrost-http` transport package to use a consistent naming convention, removing underscores in favor of camelCase-style concatenated names.

## Changes

- `mcp_per_user_headers.go` → `mcpheaders.go`
- `oauth2.go` → `mcpoauth2.go`
- `mcp_sessions.go` → `mcpsessions.go`
- `mcp_sessions_test.go` → `mcpsessions_test.go`
- `temp_token_scopes.go` → `temptokens.go`

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI

## Affected areas

- [ ] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go version
go test ./...
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…`MCPServerAuthMode` config (#4505)

## Summary

This PR introduces the foundational OAuth 2.1 authorization server infrastructure for Bifrost's `/mcp` endpoint. It adds a configurable `mcp_server_auth_mode` that controls how inbound MCP clients are authenticated, enabling Bifrost to act as a spec-compliant OAuth 2.1 AS with RFC-mandated discovery endpoints, a JWKS endpoint, and a persistent RS256 signing keypair.

## Changes

- **`MCPServerAuthMode`** — new `varchar` column on `TableClientConfig` with three modes:
  - `headers` (default): existing VK/api-key/session header auth only; discovery endpoints return 404.
  - `both`: accepts both header credentials and Bifrost-issued JWTs; discovery endpoints are live.
  - `oauth`: Bifrost JWTs only; header credentials are rejected on `/mcp`. **Breaking for existing VK-based MCP integrations.**
- **`OAuth2ServerConfig`** — new JSON blob column on `TableClientConfig` holding AS-specific settings (`IssuerURL`, `AuthCodeTTL`, `AccessTokenTTL`). Serialized via `BeforeSave`/`AfterFind` hooks. Only meaningful when mode is `both` or `oauth`.
- **`OAuth2SigningKey`** — RS2048 keypair generated on first use and persisted in `governance_config` under `oauth2_signing_key`. The private key PEM is encrypted at rest via `framework/encrypt` when encryption is enabled.
- **`GetOAuth2SigningKey`** — new `ConfigStore` interface method that lazily generates and persists the signing keypair on first call, always returning a usable key.
- **`OAuth2DiscoveryHandler`** — serves the three well-known discovery endpoints:
  - `GET /.well-known/oauth-protected-resource[/{path}]` (RFC 9728)
  - `GET /.well-known/oauth-authorization-server[/{path}]` (RFC 8414)
  - `GET /.well-known/jwks.json` (RFC 7517)
  All three return 404 when `MCPServerAuthMode` is `headers`. Routes are always registered; the mode flag is the feature toggle.
- **`oauth2IssuerURL` / `oauth2ServerCfg`** — utility helpers that resolve the effective issuer URL (configured `IssuerURL` or request-derived fallback) and AS config defaults.
- **`OAuth2ConsentScopeName`** — new temp-token scope for binding browser sessions to pending authorization requests on the public consent page.
- **Database migration** `add_oauth2_server_tables` — adds `mcp_server_auth_mode` and `oauth2_server_config_json` columns to `config_client`.
- **Config schema** — `mcp_server_auth_mode` and `oauth2_server_config` added to `config.schema.json` with full descriptions and validation.
- **`IsMCPOAuthDiscoveryEnabled`** — helper on `ClientConfig` that returns true when mode is `both` or `oauth`.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./framework/configstore/... ./transports/bifrost-http/...
```

1. Start Bifrost with `mcp_server_auth_mode` unset (or `"headers"`). Confirm `GET /.well-known/oauth-authorization-server` returns 404.
2. Set `mcp_server_auth_mode` to `"both"` and restart. Confirm:
   - `GET /.well-known/oauth-authorization-server` returns a valid JSON document with `issuer`, `authorization_endpoint`, `token_endpoint`, etc.
   - `GET /.well-known/oauth-protected-resource` returns a document pointing to `/mcp`.
   - `GET /.well-known/jwks.json` returns a JWKS with one RS256 key entry.
3. Confirm the signing keypair is persisted in `governance_config` and survives a restart (same `kid` returned).
4. Set `mcp_server_auth_mode` to `"oauth"` and confirm header-credential MCP requests are rejected.

**New config fields:**

| Field | Type | Default | Description |
|---|---|---|---|
| `mcp_server_auth_mode` | `"headers"` \| `"both"` \| `"oauth"` | `"headers"` | Inbound MCP client auth mode |
| `oauth2_server_config.issuer_url` | string / env var | _(request host)_ | Stable AS issuer URL |
| `oauth2_server_config.auth_code_ttl` | int (seconds) | `600` | Authorization code lifetime |
| `oauth2_server_config.access_token_ttl` | int (seconds) | `600` | JWT Bearer token lifetime |

## Breaking changes

- [x] Yes
- [ ] No

Setting `mcp_server_auth_mode` to `"oauth"` disables VK/api-key/session header authentication on `/mcp`. Existing virtual-key MCP integrations will stop working. Use `"both"` for a non-breaking migration path that accepts both credential types simultaneously.

## Security considerations

- The RS256 private key is encrypted at rest using `framework/encrypt` when encryption is enabled. The plaintext key is only held in memory during the signing operation.
- The `OAuth2ConsentScopeName` temp token is the sole credential binding a browser session to a pending authorization request on the public (unauthenticated) consent page — it must be treated as a short-lived secret.
- Refresh tokens have no timer-based expiry; they are invalidated only by rotation on use, subject liveness checks, explicit revocation, or enforcement policy changes.
- Multi-host / reverse-proxy deployments must set a stable `issuer_url`; omitting it causes token verification failures when the `Host` header differs across nodes.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
… and refresh token rotation (#4506)

## Summary

This PR implements the downstream OAuth2 token issuance flow, enabling Bifrost to act as a full OAuth2 authorization server. It adds the three core RFC-compliant endpoints (Dynamic Client Registration, Authorization, and Token), the backing database tables and store methods, and the consent temp-token scope needed to bind the consent UI to a specific authorization request.

## Changes

- **New DB tables** (`oauth2_clients`, `oauth2_authorize_requests`, `oauth2_refresh_tokens`) added via a new `add_oauth2_issuance_tables` migration step, with GORM model definitions in `tables/oauth2_issuance.go`. `TableOAuth2Client` serializes `redirect_uris` and `grant_types` as JSON columns with `BeforeSave`/`AfterFind` hooks.
- **`ConfigStore` interface** extended with methods for creating/fetching OAuth2 clients, managing authorize requests (including code-hash lookup and expiry sweeping), and refresh token operations (`GetOAuth2RefreshTokenByHash`, `ConsumeOAuth2AuthorizeRequest`, `RotateOAuth2RefreshToken`).
- **`RDBConfigStore`** implements all new interface methods. `ConsumeOAuth2AuthorizeRequest` and `RotateOAuth2RefreshToken` are wrapped in transactions so failures leave the grant in a retryable state.
- **`OAuth2IssuanceHandler`** (`handlers/oauth2_issuance.go`) wires three public routes:
  - `POST /oauth2/register` — RFC 7591 DCR; only public clients (`token_endpoint_auth_method=none`) are accepted.
  - `GET /oauth2/authorize` — PKCE-S256 (RFC 7636) + resource indicator (RFC 8707); creates a pending authorize request and redirects to the consent UI with an optional scoped temp token.
  - `POST /oauth2/token` — handles `authorization_code` and `refresh_token` grants; issues RS256 JWTs signed with the persisted signing key and rotates refresh tokens on every use.
- **Stolen-token detection** via `FamilyID` on refresh tokens: all tokens descended from the same authorization grant share a family ID, enabling full family revocation when a revoked token is re-presented (RFC 9700 §2.2.2).
- **Loopback redirect URI matching** follows RFC 8252 §7.3 (port-agnostic for `localhost`/`127.0.0.1`).
- **`oauth2ConsentScope`** temp-token scope registered at startup, binding consent-page API calls to a single authorize request ID via path substitution.
- **Server bootstrap** pre-warms the OAuth2 signing key when MCP OAuth discovery is enabled, so JWKS and JWT signing are ready before the first request.
- `github.com/golang-jwt/jwt/v5` promoted from indirect to a direct dependency.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./framework/configstore/... ./transports/bifrost-http/...
```

1. Start Bifrost with MCP OAuth discovery enabled and a configured config store.
2. Register a client:
   ```sh
   curl -X POST http://localhost:PORT/oauth2/register \
     -H "Content-Type: application/json" \
     -d '{"client_name":"test","redirect_uris":["http://localhost:8080/callback"]}'
   ```
3. Initiate an authorization request via `GET /oauth2/authorize` with `response_type=code`, `code_challenge` (S256), `resource`, and the returned `client_id`.
4. Complete the consent flow and exchange the auth code at `POST /oauth2/token` with `grant_type=authorization_code` and the PKCE verifier.
5. Refresh the access token using `grant_type=refresh_token` and verify the old refresh token is revoked and a new one is issued.

## Breaking changes

- [x] No

## Security considerations

- Refresh tokens are stored as SHA-256 hashes only; plaintext is returned to the client once and never persisted.
- Auth codes are single-use: `ConsumeOAuth2AuthorizeRequest` atomically transitions the request to `code_issued` and creates the refresh token in one transaction.
- Refresh token rotation is atomic; if rotation fails the old token remains valid and the client can retry safely.
- Stolen-token detection revokes the entire token family when a previously-rotated (revoked) token is presented, per RFC 9700 §2.2.2.
- Only public clients are supported; no client secrets are accepted or stored.
- PKCE S256 is mandatory; plain challenge method is rejected.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…resolution modes (#4507)

## Summary

Introduces the OAuth2 consent flow API, enabling MCP clients to complete the OAuth2 authorization code flow by presenting a consent page where users can select how they want to identify themselves (via virtual key, session, or user identity).

## Changes

- Added `OAuth2ConsentHandler` with two endpoints:
  - `GET /api/oauth2/consent/flows/{id}` — returns flow details, available identity modes, and the currently logged-in user (if a valid session exists)
  - `PUT /api/oauth2/consent/flows/{id}` — resolves identity, mints an authorization code (storing only its SHA256 hash), marks the flow as consented, invalidates the temp token, and returns the redirect URL with the code and state per RFC 6749 §4.1.2 and RFC 9207
- Added `OAuth2IdentityResolver` interface as an optional extension point for user identity resolution. When nil, only `vk` and `session` modes are offered. When provided, a `user` mode becomes available if the resolver reports it is configured.
- VK identity resolution includes a user-binding upgrade path: if a VK is bound to a specific user and an identity provider is configured, the currently logged-in session must match the VK owner before the upgrade proceeds.
- Session mode tokens are server-minted and never client-asserted.
- The consent handler is registered in `RegisterAPIRoutes`, defaulting to a nil identity resolver if one has not been pre-assigned to `BifrostHTTPServer.OAuth2ConsentHandler`.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./...
```

1. Start the server and initiate an OAuth2 authorization request to obtain a flow ID and a consent temp token.
2. Call `GET /api/oauth2/consent/flows/{id}` with the temp token — verify `client_name`, `available_modes`, `expires_at`, and optionally `logged_in_user` are returned.
3. Call `PUT /api/oauth2/consent/flows/{id}` with `{"mode": "vk", "value": "<plaintext_vk>"}` — verify the response contains a `redirect_url` with `code`, `state`, and `iss` query parameters.
4. Confirm the flow status is updated to `consented` and the temp token is invalidated (a second PUT should fail).
5. Repeat with `mode: session` (when `EnforceAuthOnInference` is false) and `mode: user` (when an identity resolver is configured).

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

N/A

## Security considerations

- Authorization codes are generated with 32 bytes of cryptographic randomness; only the SHA256 hash is persisted — the plaintext is transmitted once via the redirect URI and never stored (RFC 6749 §4.1.2).
- The issuer is included in the redirect per RFC 9207 to allow clients to validate the authorization server.
- The consent temp token is invalidated immediately after a successful submission to prevent replay.
- User-bound VKs require a matching active session before the identity upgrade is granted, preventing possession of a VK alone from impersonating a bound user.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…dation and context injection (#4508)

## Summary

This PR adds JWT Bearer token authentication to the `/mcp` endpoint, enabling Bifrost-issued JWTs to be used as a first-class authentication mechanism alongside existing VK/header credentials. It also introduces a session validation gate that prevents per-user upstream flows (OAuth and header submission) from being initiated unless the caller's identity has been actively verified for the current request.

## Changes

- Added `extractBearerJWT` to detect and extract JWT Bearer tokens from the `Authorization` header, distinguishing them from VK credentials by the `eyJ` prefix.
- Added `verifyMCPJWT` to validate RS256-signed JWTs against the active signing key, enforcing expiry, issued-at, key ID, and RFC 8707 audience checks against the `/mcp` resource URL.
- Added `injectJWTContext` to translate verified JWT claims (`bf_mode=user|vk|session`) into the same `BifrostContext` keys that header-based auth sets, ensuring downstream resolvers (governance, per-user OAuth, tool-group filtering) work without modification.
- Introduced `mcpAuthResult` to carry the authenticated MCP server, JWT claims, and resolved VK out of `getMCPServerForRequest`, replacing the previous single return value.
- Refactored `getMCPServerForRequest` to implement a prioritized auth flow: JWT → oauth-strict rejection → VK/header → anonymous dev mode. In `oauth` strict mode, non-JWT requests are rejected with a `WWW-Authenticate` header per RFC 9728.
- Added `BifrostContextKeyOAuth2JWTAuthenticated` and `BifrostContextKeyOAuth2JWTSessionValidated` context keys. The session-validated key is set only when a user-mode JWT's `sub` is confirmed against an active dashboard session.
- Added a session validation gate in both `per_user_oauth.go` and `per_user_headers.go`: when a request is JWT-authenticated in user mode, upstream flows cannot be initiated unless `BifrostContextKeyOAuth2JWTSessionValidated` is present.
- Extracted `ensureVKMCPServerByValue` to allow VK server lookup by value from both the header path and the JWT VK path.
- Removed the stale comment block about Bifrost not being an OAuth authorization server.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go version
go test ./...
```

- Send a request to `/mcp` with a valid Bifrost-issued JWT in `Authorization: Bearer <token>` and confirm the appropriate context keys are set and the correct scoped server is used.
- Send a request with `bf_mode=user` JWT and no active dashboard session; confirm a `401` is returned with a message indicating no active session.
- Send a request with `bf_mode=user` JWT and a mismatched session user; confirm rejection.
- Configure `MCPServerAuthMode=oauth` and send a request with a VK header; confirm rejection with a `WWW-Authenticate` header.
- Attempt to initiate a per-user OAuth or header submission flow with a user-mode JWT that has not been session-validated; confirm the flow is blocked.
- Confirm anonymous dev-mode access (`EnforceAuthOnInference=false`, no credentials) still works.

## Breaking changes

- [x] Yes
- [ ] No

`getMCPServerForRequest` now returns `*mcpAuthResult` instead of `*server.MCPServer`. Any code calling this method directly must be updated. When `MCPServerAuthMode` is set to `oauth`, header-based credentials are no longer accepted at the `/mcp` endpoint.

## Security considerations

- JWT verification enforces RS256 signature, expiry, issued-at, key ID, and RFC 8707 audience binding to the `/mcp` resource URL, preventing token reuse across resources.
- User-mode JWTs require an active dashboard session whose user ID matches `bf_sub`, preventing replay of valid tokens after session expiry.
- The session validation gate on per-user upstream flows ensures that even a structurally valid JWT cannot trigger credential submission flows without a confirmed live session.
- `WWW-Authenticate` headers are emitted on auth failures in discovery-enabled modes, per RFC 9728.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…iveness check, and sweep worker (#4509)

## Summary

This PR adds OAuth2 session management capabilities including stolen-token detection with automatic family revocation, a Connected Clients API for listing and revoking active downstream grants, VK liveness checks on token refresh, and a background sweep worker to clean up revoked refresh tokens.

## Changes

- Added `GetOAuth2RefreshTokenByHashAny` to look up refresh tokens including revoked ones, enabling detection of token reuse attacks where a previously rotated token is re-presented
- When a revoked token is re-presented during refresh, all active tokens in the same family are now revoked per RFC 9700 §2.2.2 to limit damage from a potentially stolen token
- Added `RevokeOAuth2RefreshTokensByFamilyID` and `RevokeOAuth2RefreshTokensByMode` for bulk revocation of token families and mode-scoped grants respectively
- Added `SweepOAuth2RefreshTokens` to delete revoked tokens older than a configurable retention window (default 30 days), swept every 10 minutes via a new `oauth2SweepWorker`
- Added `ListOAuth2Sessions` which joins refresh token rows with client names and VK names for human-readable display in the Connected Clients UI
- Added `GetOAuth2SessionByID` and `RevokeOAuth2Session` for single-session lookup and revocation
- Added `OAuth2SessionsHandler` exposing `GET /api/oauth2/sessions` and `DELETE /api/oauth2/sessions/{id}`; user-mode sessions enforce that the caller's identity matches `bf_sub` before allowing revocation
- Added a VK liveness check during token refresh: if the virtual key referenced by a VK-mode token has been deleted or disabled, the refresh is rejected with `invalid_grant`
- The `oauth2SweepWorker` is started during server bootstrap and stopped cleanly on all error and shutdown paths alongside the existing temp-token sweep worker

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./...
```

1. Issue a refresh token and rotate it once. Re-present the original (now revoked) token to `POST /token` — expect `invalid_grant` and all sibling tokens in the family to be revoked.
2. Disable or delete a virtual key that has an active VK-mode refresh token. Attempt a token refresh — expect `invalid_grant` with "virtual key is no longer active".
3. `GET /api/oauth2/sessions` — expect a JSON list of active grants with client names and VK display names populated.
4. `DELETE /api/oauth2/sessions/{id}` with a user-mode session ID using a mismatched caller identity — expect `403 Forbidden`.
5. `DELETE /api/oauth2/sessions/{id}` with the correct caller identity — expect `204 No Content` and the session absent from subsequent list calls.
6. Wait for or trigger the sweep worker; confirm revoked tokens older than the retention window are removed from the database.

## Breaking changes

- [x] Yes
- [ ] No

The `ConfigStore` interface has new required methods. Any custom implementations of `ConfigStore` must add `GetOAuth2RefreshTokenByHashAny`, `RevokeOAuth2RefreshTokensByFamilyID`, `RevokeOAuth2RefreshTokensByMode`, `SweepOAuth2RefreshTokens`, `ListOAuth2Sessions`, `GetOAuth2SessionByID`, and `RevokeOAuth2Session`.

## Related issues

## Security considerations

- Implements RFC 9700 §2.2.2 refresh token family revocation: re-use of a revoked token triggers revocation of all active tokens in the family, limiting the blast radius of a stolen token.
- The session revoke endpoint enforces identity matching for user-mode grants, preventing one user from revoking another user's session even if they share visibility of the row.
- VK liveness checks prevent deleted or disabled virtual keys from silently continuing to obtain access tokens via refresh.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

Adds an OAuth2 consent page and supporting configuration to allow MCP clients (e.g. `claude mcp add`) to authenticate via a browser-based OAuth flow. Anonymous visitors arrive at `/oauth/consent?flow=<id>` with a short-lived temp token embedded in the URL fragment, choose how they want to identify themselves (signed-in user, virtual key, or anonymous session), and are redirected back to the MCP client with an authorization code.

## Changes

- **`/oauth/consent` route** — New standalone page (`layout.tsx` + `page.tsx`) that renders outside the dashboard chrome. It wraps itself with `ThemeProvider`, `ReduxProvider`, `NuqsAdapter`, and `Toaster` directly since it does not inherit them from `ClientLayout`. `TempTokenScope` extracts the `#t=…` fragment and attaches it as `X-Bifrost-Temp-Token` on every API call so the consent APIs can authenticate the anonymous visitor.
- **Consent UI** — `ConsentView` fetches the flow details and presents up to three authentication options: continuing as the signed-in user, entering a virtual key, or proceeding anonymously. It preserves the temp token across the login redirect via `sessionStorage` so users who choose to sign in are returned to the correct flow.
- **`oauth2ConsentApi`** — New RTK Query endpoints (`GET` and `PUT` `/oauth2/consent/flows/:flowId`) for fetching flow details and submitting the chosen identity mode.
- **MCP Server Auth Mode setting** — The MCP config view gains a `mcp_server_auth_mode` selector (`headers` / `both` / `oauth`) with contextual warnings: switching to `oauth` disables VK/header access; downgrading back to `headers` revokes all existing OAuth JWTs. OAuth2 server settings (issuer URL, auth code TTL, access token TTL) are revealed when the mode is `both` or `oauth`.
- **`CoreConfig` types** — Added `mcp_server_auth_mode` and `oauth2_server_config` (issuer URL, auth code TTL, access token TTL) to the config type and dirty-check logic.
- **`loginGoto`** — `/oauth/consent` is now treated as a valid post-login redirect destination.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

1. Start Bifrost with an MCP server configured.
2. Run `claude mcp add --transport sse <bifrost-url>/mcp` (or equivalent). The client should redirect to `/oauth/consent?flow=<id>#t=<token>`.
3. Verify the consent page loads without requiring a dashboard login.
4. Test each identity mode:
   - **User** — sign in via the login redirect and confirm you are returned to the consent page and redirected back to the MCP client.
   - **Virtual key** — enter a valid `sk-bf-…` key and confirm a successful redirect.
   - **Anonymous** — click "Continue without an identity" and confirm a successful redirect.
5. In the MCP config view, toggle `mcp_server_auth_mode` between `headers`, `both`, and `oauth` and verify the correct warnings appear and the OAuth2 server settings section shows/hides accordingly.

```sh
cd ui
pnpm i
pnpm build
```

## Breaking changes

- [x] Yes
- [ ] No

Setting `mcp_server_auth_mode` to `oauth` disables virtual key and header-based MCP authentication immediately. All existing MCP integrations using VK, api-key, or session headers will stop working until clients re-authenticate via the OAuth consent flow. Setting the mode back to `headers` from `both` or `oauth` invalidates all previously issued OAuth JWTs and refresh tokens.

## Security considerations

- The temp token (`#t=…`) is passed in the URL fragment and never sent to the server by the browser directly; it is extracted client-side and attached as a custom header (`X-Bifrost-Temp-Token`), limiting exposure in server logs.
- `setSuppressGlobal401` is called when restoring a temp token from `sessionStorage` after a login redirect to prevent the global 401 handler from clearing the session prematurely during the consent flow.
- The consent page is intentionally accessible without a dashboard session; all authorization is enforced server-side via the flow ID and temp token.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
TejasGhatte and others added 24 commits July 5, 2026 10:36
## Summary

Adds `cancelled` as a first-class terminal log status alongside `success` and `error`. Previously, cancelled requests were excluded from all aggregate queries, histograms, and materialized views. This change ensures cancelled requests are tracked, counted, and surfaced in charts and exports.

## Changes

- Introduced a `terminalLogStatuses` constant (`["success", "error", "cancelled"]`) to replace all scattered inline `[]string{"success", "error"}` slices across query filters in `rdb.go` and `matviews.go`.
- Added `cancelled_count` to the `mv_logs_hourly` materialized view definition and included `cancelled` in the `WHERE status IN (...)` clause so the view captures cancelled requests.
- Added `cancelled_count` to `mvLogsHourlyRequiredColumns` to enforce schema compatibility checks on startup.
- Added `canUseMatViewStatusFilter` to gate matview usage: non-terminal statuses (e.g. `processing`) force the raw query path, while terminal statuses (including `cancelled`) remain matview-eligible.
- Extended `HistogramBucket` and `ModelUsageStats` structs with a `Cancelled` field, and propagated it through all histogram query paths (raw DB and matview) for `GetHistogram`, `GetModelHistogram`, and their matview equivalents.
- Updated CSV export helpers (`overviewVolumeToCSV`, `overviewModelUsageToCSV`) to include cancelled counts.
- Updated UI chart components (`logVolumeChart.tsx`, `modelUsageChart.tsx`, `logsVolumeChart.tsx`, `overviewTab.tsx`) to render a stacked `cancelled` segment using `zinc-400` (`#a1a1aa`), display it in tooltips and legends, and handle missing values with a `?? 0` fallback.
- Added `cancelled` to the `CHART_COLORS` palette and updated the `HistogramBucket`, `ModelUsageStats`, and `MCPHistogramBucket` TypeScript interfaces.
- Added `TestCancelledStatusIncludedInLogAggregates` to verify that cancelled rows appear in `SearchLogs`, `GetStats`, and `GetHistogram` results while `processing` rows are excluded from terminal aggregates.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

```sh
# Core/Transports
go test ./framework/logstore/...

# UI
cd ui
pnpm i
pnpm build
```

The new test `TestCancelledStatusIncludedInLogAggregates` covers the following scenarios:
- A `cancelled` log appears in `SearchLogs` when filtering by `status=cancelled`.
- `GetStats` with no filter counts all 4 rows as total requests but only 3 (terminal) as the cache-hit denominator.
- `GetStats` filtered to `cancelled` returns correct latency and a zero success rate.
- `GetHistogram` filtered to `cancelled` returns a bucket with `Count=1`, `Success=0`, `Error=0`, `Cancelled=1`.

Because `mv_logs_hourly` gains a new `cancelled_count` column, the materialized view must be recreated on first startup. The existing schema version/migration logic handles this via `mvLogsHourlyRequiredColumns`.

## Breaking changes

- [x] Yes
- [ ] No

The `mv_logs_hourly` materialized view schema changes (new `cancelled_count` column). On startup the store will detect the missing column and recreate the view. No manual migration is required, but there will be a brief period during view recreation where matview-backed queries fall back to raw table scans.

API consumers reading `HistogramBucket` or `ModelUsageStats` JSON will now receive an additional `cancelled` field. This is additive and backwards-compatible for consumers that ignore unknown fields.

## Related issues

## Security considerations

None. No new auth surfaces, secrets, or PII handling introduced.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

Fixes two bugs in the Anthropic reverse-stream converter that caused incorrect `content_block` index numbering depending on the request path:

1. **Passthrough path (Claude Code):** The converter was unconditionally consuming (and discarding) a content-block index for dropped server-tool result blocks (`web_fetch`, zero-result `web_search`). This is correct only on the passthrough path, where verbatim raw upstream frames are interleaved and indices must stay in lockstep. On the all-normalized path, this caused a gap in indices (e.g. `[0, 1, 3]` instead of `[0, 1, 2]`), which strict Anthropic SDK clients reject as a missing block.

2. **Tool version recognition:** `convertAnthropicToolToBifrost` matched `web_search` and `web_fetch` tool types by exact version string. Any future-dated version (e.g. `web_fetch_20260318`) silently fell through to the client-function default instead of being recognized as a server tool.

## Changes

- Introduced a `passthrough bool` field on `anthropicToResponsesStreamState` and a `SetResponsesStreamPassthrough(ctx)` function. The transport calls this when `shouldUsePassthrough` is true; the converter reads it to decide whether to consume the discarded result-block index.
- The index-consuming `allocBlockIndex("")` calls for `web_fetch` and resultless `web_search` are now gated on `state.passthrough`, keeping indices contiguous on the all-normalized path.
- `convertAnthropicToolToBifrost` now matches `web_search_*` and `web_fetch_*` tool types by prefix (via `strings.HasPrefix`) in a separate `switch typeStr := string(*tool.Type); { case ... }` block before the exact-match switch, so any current or future dated version is correctly recognized.
- The passthrough test harness (`runAnthropicPassthrough`) now calls `SetResponsesStreamPassthrough` to mirror what the transport does, keeping existing passthrough tests accurate.
- Added `TestAnthropicConverterOnly_IndicesContiguous` to assert that the all-normalized path emits contiguous `content_block_start` indices `0, 1, 2, …` across `web_fetch`, zero-result `web_search`, and combinations thereof.
- Added `TestServerSearchTools_VersionRecognition` to assert that known and future-dated `web_search_*` / `web_fetch_*` tool types all map to the correct neutral server-tool type and never fall through to `ResponsesToolTypeFunction`.

## Type of change

- [x] Bug fix

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations

## How to test

```sh
go test ./core/providers/anthropic/... -run "TestAnthropicConverterOnly_IndicesContiguous|TestServerSearchTools_VersionRecognition|TestAnthropicPassthrough|TestAnthropicConverterOnlyStream"
go test ./transports/bifrost-http/...
go test ./...
```

`TestAnthropicConverterOnly_IndicesContiguous` will fail before the fix with output like:
```
non-contiguous content_block_start indices [0 1 3] (position 2 is index 3, not 2); native Anthropic never skips an index
```

`TestServerSearchTools_VersionRecognition` will fail before the fix for `web_search_20260318` and `web_fetch_20260318` with:
```
neutral tool type = "function", want "web_search" (must not fall through to a client function tool)
```

## Breaking changes

- [x] No

## Security considerations

None.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

Adds ClickHouse as a supported log store backend, enabling high-throughput, append-only OLAP storage for Bifrost request logs, MCP tool logs, and async jobs. This provides a scalable alternative to SQLite and Postgres for analytics-heavy deployments.

## Changes

- **New `ClickHouseLogStore`**: Implements the `LogStore` interface using `ReplacingMergeTree` tables with a connection-level `final = 1` setting so reads transparently see the latest version of each row. Inserts are plain (no `ON CONFLICT`); idempotency is handled by RMT dedup.
- **Read-modify-write (RMW) updates**: Since ClickHouse has no cheap `UPDATE`, mutations re-read the existing row, apply the patch, and re-insert with a higher `ver` (defaulted to `now64(9)`). Per-row shard locks prevent concurrent updaters on the same pod from silently dropping each other's patches.
- **Schema migrations**: `clickhousemigrate.go` derives column definitions from GORM-parsed struct schemas and runs idempotent `CREATE TABLE IF NOT EXISTS` + `ALTER TABLE ... ADD COLUMN IF NOT EXISTS` migrations. No migration ledger is needed since both DDL statements are inherently concurrency-safe. Cluster-mode DDL (`ON CLUSTER`) and `ReplicatedReplacingMergeTree` are supported via the optional `cluster` config field.
- **Dialect-aware SQL**: Extracted a `unixBucketExpr` helper in `dialectsql.go` that returns the correct unix-bucket expression per dialect (SQLite, MySQL, Postgres, ClickHouse), eliminating repeated per-dialect switch blocks across all histogram queries. ClickHouse-specific JSON functions (`isValidJSON`, `JSONExtractString`) and `quantile()` aggregates replace Postgres/SQLite equivalents in filter, stats, and latency histogram paths.
- **DSN builder**: `buildClickHouseDSN` supports native (port 9000/9440) and HTTP (port 8123/8443) protocols, TLS, credentials, custom dial timeout, and passes `final=1` and `mutations_sync=1` as connection-level settings.
- **`driver.Valuer` on custom string types**: `AsyncJobStatus` and `RequestType` now implement `driver.Valuer` so the clickhouse-go batch insert path can serialize them correctly.
- **Example configs**: Added `withclickhouselogstore` and `withclickhouselogstorehttp` example configs for native and HTTP protocol setups.
- **Docker Compose**: Added a `clickhouse` service (native on host port 9001, HTTP on 8123) to `framework/docker-compose.yml` for local development and integration tests.
- **Config schema**: `config.schema.json` updated to include `clickhouse` as a valid `logs_store.type` with full property documentation.
- **Makefile fix**: Framework test loop now runs each package in a subshell so a failing package no longer aborts the loop; a sentinel file tracks failures and exits non-zero after the summary is printed. Absolute paths via `$(CURDIR)` fix JUnit report paths when `cd` changes the working directory.
- **`.gitignore` consolidation**: Merged several scattered `.gitignore` files (UI, plugin, CLI, semantic cache) into the root `.gitignore`.
- **Integration tests**: `clickhousestore_test.go` covers create, idempotent insert, batch insert, map/struct updates, dedup key protection, concurrent RMW correctness, bulk cost backfill, search/stats, delete, TTL-based batch delete, MCP tool logs, async jobs, and all histogram types. Tests skip automatically when ClickHouse is unavailable.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Start the framework Docker Compose stack (includes the new ClickHouse service):

```sh
cd framework
docker compose up -d clickhouse
```

Run the ClickHouse integration tests:

```sh
cd framework
go test ./logstore/... -run TestClickHouse -v
```

Run the full framework test suite:

```sh
make test-framework
```

To test end-to-end with the example config:

```sh
# Native protocol (port 9001)
cp examples/configs/withclickhouselogstore/config.json /path/to/bifrost/config.json

# HTTP protocol (port 8123)
cp examples/configs/withclickhouselogstorehttp/config.json /path/to/bifrost/config.json
```

**New `logs_store` config fields for ClickHouse:**

| Field | Type | Default | Description |
|---|---|---|---|
| `host` | string | required | ClickHouse host |
| `port` | string | protocol default | 9000 (native), 8123 (http) |
| `database` | string | `default` | Database name |
| `username` | string | — | Username |
| `password` | string | — | Password |
| `protocol` | `native`\|`http` | `native` | Wire protocol |
| `secure` | bool | `false` | Enable TLS |
| `dial_timeout` | int (ms) | `10000` | Connection dial timeout |
| `cluster` | string | — | ON CLUSTER name for replicated deployments |

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

ClickHouse credentials are handled via `schemas.SecretVar` (consistent with existing Postgres/SQLite config patterns). The `cluster` field is identifier-escaped before interpolation into DDL to prevent injection via config. No new secrets are introduced beyond what operators supply in their config.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
## Summary

ClickHouse's `ReplacingMergeTree` engine keeps the row with the **highest `ver`** on merge, meaning a plain `INSERT` is not idempotent in the way `ON CONFLICT DO NOTHING` is for SQL stores. A retried "processing" insert arriving after a completion update (or after the hybrid store's `has_object` flip) would resurrect the stale row, silently dropping `status`, `cost`, and `has_object`. This PR fixes `CreateIfNotExists`, `BatchCreateIfNotExists`, and `BatchCreateMCPToolLogsIfNotExists` to check for existing rows before inserting, and fixes `DeleteLogsBatch` to return an accurate deleted count so the `LogsCleaner` pacing works correctly.

## Changes

- Added `chFilterMissing`, a generic helper that queries existing ids from a given table and returns only the entries not yet present. It deduplicates within the batch (first occurrence wins, matching `ON CONFLICT DO NOTHING` semantics) and must be called under the RMW shard locks to prevent concurrent `Update` re-inserts from interleaving.
- `CreateIfNotExists`, `BatchCreateIfNotExists`, and `BatchCreateMCPToolLogsIfNotExists` now acquire the appropriate RMW shard locks and call `chFilterMissing` before inserting, skipping any ids already present in the table.
- `DeleteLogsBatch` is overridden on `ClickHouseLogStore` to select ids first, then delete by id. The GORM ClickHouse driver rewrites `DELETE` into an `ALTER TABLE` mutation whose result always reports 0 rows affected; selecting ids first gives an accurate count so the `LogsCleaner` does not stop early.
- Added `TestClickHouseCreateIfNotExistsKeepsExistingRow` to verify that a retried insert does not overwrite a row that has already been updated with `status=success` and `has_object=true`, for both the single and batch variants.
- Added `TestClickHouseHybridHasObjectSurvivesDuplicateCreate` to exercise the full `HybridLogStore`-over-ClickHouse flow: create, async `has_object` flip, completion update, duplicate create retry — asserting that status, `has_object`, and payload hydration all survive.
- Updated `TestClickHouseDeleteLogsBatch` to assert the returned deleted count is accurate.

## Type of change

- [x] Bug fix

## Affected areas

- [x] Core (Go)

## How to test

```sh
go test ./framework/logstore/... -run TestClickHouseIdempotentCreate
go test ./framework/logstore/... -run TestClickHouseCreateIfNotExistsKeepsExistingRow
go test ./framework/logstore/... -run TestClickHouseHybridHasObjectSurvivesDuplicateCreate
go test ./framework/logstore/... -run TestClickHouseDeleteLogsBatch
go test ./framework/logstore/...
```

A running ClickHouse instance is required for the integration tests. Tests that cannot connect will be skipped automatically via `trySetupClickHouseStore`.

## Breaking changes

- [x] No

## Security considerations

None. The existence check uses parameterised `WHERE id IN ?` queries; no new user-controlled input surfaces are introduced.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

Briefly explain the purpose of this PR and the problem it solves.

## Changes

- What was changed and why
- Any notable design decisions or trade-offs

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Describe the steps to validate this change. Include commands and expected outcomes.

```sh
# Core/Transports
go version
go test ./...

# UI
cd ui
pnpm i || npm i
pnpm test || npm test
pnpm build || npm run build
```

If adding new configs or environment variables, document them here.

## Screenshots/Recordings

If UI changes, add before/after screenshots or short clips.

## Breaking changes

- [ ] Yes
- [ ] No

If yes, describe impact and migration instructions.

## Related issues

Link related issues and discussions. Example: Closes #123

## Security considerations

Note any security implications (auth, secrets, PII, sandboxing, etc.).

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…4935)

## Summary

The `Content-Type: application/json` header was previously only set when the request method was `POST` or when a body was present. This meant non-POST requests with no body (e.g., `GET` or `DELETE`) would not include the header, which could cause inconsistent behavior. The header is now always set regardless of method or body presence.

## Changes

- Removed the outer conditional that gated `Content-Type: application/json` on the method being `POST` or the body being non-empty
- The header is now unconditionally set on all requests in `executeResponsesLifecycleUnary`
- Body assignment logic remains unchanged: a provided body is used as-is, and an empty `POST` body defaults to `{}`

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/providers/openai/...
```

Verify that requests made via the OpenAI Responses lifecycle (including non-POST methods) include the `Content-Type: application/json` header, and that POST requests with no body still default to `{}`.

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

N/A

## Security considerations

No security implications. This change only affects request header construction.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…nses (#4936)

## Summary

Deprecated models are no longer silently removed from list-models API responses. Instead, they are retained in the response and annotated with `is_deprecated: true`. The UI consumes this flag to render deprecated models as non-selectable (dimmed, with a "Deprecated" badge) rather than hiding them entirely.

## Changes

- Removed `FilterDeprecatedModels` from `BifrostListModelsResponse` and all call sites that dropped deprecated entries from responses.
- Removed `DistinctActiveBaseModelNames` and `GetDistinctActiveBaseModelNames`; `GetDistinctBaseModelNames` is now used everywhere, including the base models endpoint.
- Renamed `enrichAndFilterListModelsResponse` → `enrichListModelsResponse` and `filterDeprecatedListModelsResponse` → `markDeprecatedListModelsResponse` to reflect the new behavior: models are annotated, not removed.
- The enrichment logic now sets `IsDeprecated = true` when either the provider or the catalog pricing entry marks the model as deprecated, instead of excluding it from the result set.
- The management models list endpoint no longer calls `isModelDeprecated` to delete entries before pagination.
- `ModelResponse` in the providers API type definition gains an `is_deprecated` field.
- `ModelMultiselect` maps `is_deprecated` to `isDisabled` on each option, preventing selection of deprecated models. Deprecated options render with reduced opacity, a `cursor-not-allowed` style, and a small "Deprecated" badge.
- Updated all affected tests to assert that deprecated models are present in responses with the correct flag value rather than asserting they are absent.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

```sh
# Core/Transports
go test ./core/schemas/... ./framework/modelcatalog/... ./transports/bifrost-http/...

# UI
cd ui
pnpm i
pnpm build
```

1. Call the list-models endpoint for a provider that has deprecated models in the pricing catalog. Verify all models are returned and deprecated ones carry `"is_deprecated": true`.
2. Call the base models endpoint and confirm deprecated base model names appear in the response.
3. Open the model multiselect in the UI, confirm deprecated models appear with the "Deprecated" badge and cannot be selected.

## Screenshots/Recordings

![image.png](https://app.graphite.com/user-attachments/assets/cd496e4e-7c6b-4559-9f33-224dc17f0815.png)



## Breaking changes

- [x] Yes
- [ ] No

Clients that relied on deprecated models being absent from list-models or list-base-models responses will now receive them. Clients should filter on `is_deprecated: true` if they wish to exclude deprecated entries.

## Related issues

## Security considerations

None.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

The `SetStreamIdleTimeoutIfEmpty` call was being made before invoking the shared `HandleGemini*Stream` functions, meaning the timeout was set in the provider-level methods rather than inside the shared handlers. This caused the Vertex provider — which delegates directly to those shared handlers — to never set the stream idle timeout at all. Moving the call into `HandleGeminiChatCompletionStream` and `HandleGeminiResponsesStream`, and passing `streamIdleTimeoutInSeconds` as an explicit parameter, ensures the timeout is consistently applied regardless of which provider invokes the handler.

## Changes

- Removed `SetStreamIdleTimeoutIfEmpty` calls from `GeminiProvider.ChatCompletionStream` and `GeminiProvider.ResponsesStream`.
- Added `streamIdleTimeoutInSeconds int` parameter to `HandleGeminiChatCompletionStream` and `HandleGeminiResponsesStream`.
- Moved `SetStreamIdleTimeoutIfEmpty` to the top of each shared handler so it is always executed.
- Updated `VertexProvider.ChatCompletionStream` and `VertexProvider.ResponsesStream` to pass `provider.networkConfig.StreamIdleTimeoutInSeconds` to the shared handlers, fixing the missing timeout for Vertex streaming calls.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Verify that streaming requests through the Vertex provider respect the configured `StreamIdleTimeoutInSeconds` by setting a short timeout and confirming the stream is terminated after the idle period elapses.

```sh
go test ./...
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No security implications.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…ecret values (#4946)

## Summary

Ensures that `SecretVar` instances parsed from plain-text values (i.e., JSON objects with a `value` field but no `type`, `ref`, or `from_env`) are explicitly tagged with `SecretTypePlainText` rather than left with an empty `SecretType`. Previously, these cases fell through without a type assignment, making it ambiguous whether a secret was intentionally plain text or simply uninitialized.

## Changes

- When parsing a JSON secret object that has no `type`, `ref`, or `from_env` field, `SecretType` is now explicitly set to `SecretTypePlainText`.
- When `parseSecretRef` returns a plain string value (no `env.` or `vault.` prefix), the returned `SecretVar` now carries `SecretTypePlainText`.
- During `UnmarshalJSON`, if `SecretType` remains empty after processing a recognized JSON structure, it is defaulted to `SecretTypePlainText`.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [ ] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/schemas/...
```

Verify that a `SecretVar` parsed from a plain string or a JSON object like `{"value": "my-secret"}` has `SecretType == SecretTypePlainText` rather than an empty string.

## Breaking changes

- [ ] Yes
- [x] No

## Security considerations

This change makes secret type classification more explicit. Code that previously relied on an empty `SecretType` to identify plain-text secrets should be reviewed to ensure it handles `SecretTypePlainText` correctly.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…through flags, Gemini raw-body gating, and schema sync exclusions (#4948)

## Summary

This PR fixes and clarifies several test correctness issues across the codebase, aligning tests with actual runtime behavior rather than incorrect assumptions about where and when certain transformations occur.

## Changes

- **`core/schemas/responses_test.go`**: Rewrote the `tool_search_call` round-trip test to verify that `tool_search_call` items decode without error and re-encode byte-identically (including object-form `arguments`), rather than checking that arguments were normalized to a string. The test now operates directly on item JSON and uses `MarshalSorted` to assert verbatim round-tripping.

- **`plugins/governance/resolver_test.go`**: Removed two tests (`DirectKeySatisfiesMandatoryAuth` and `HeaderWithoutContextDoesNotSatisfyMandatoryAuth`) that were testing behavior at the wrong layer. This auth enforcement is handled by the transport, not the governance plugin directly.

- **`transports/bifrost-http/integrations/bedrock_test.go`**: Corrected the assertion for the Bedrock rerank route converter — converters intentionally leave `Provider` empty; resolution happens later in the `modelcatalogresolver` `PreRequestHook`.

- **`transports/bifrost-http/integrations/genai_test.go`**: Added `x-model-provider: gemini` header to existing tests that require explicit Gemini provider identification for raw-body passthrough to engage. Added a new test (`TestExtractAndSetModelAndRequestTypeNoRawPassthroughWithoutExplicitGemini`) confirming that raw-body passthrough does not activate when no explicit provider is declared, since the model may resolve to Vertex or another provider downstream.

- **`transports/bifrost-http/integrations/router_test.go`**: Renamed and corrected `TestCreateHandler_AnthropicRouteClears_UseRawRequestBody_WhenCatalogSelectsBedrock` to `TestCreateHandler_AnthropicRouteSetsPassthroughFlags`. The router does not clear passthrough flags when a model resolves to Bedrock — that happens per-attempt in core via `clearAnthropicPassthroughForNonNativeProvider`. The test now asserts flags remain set at converter time and that the response is a 400 (not 500).

- **`transports/bifrost-http/lib/config_test.go`**: Added `circuit_breaker_config` to the enterprise schema paths and top-level sync allowlists, and added `roles` to the excluded schema fields for `governance` (enterprise RBAC role bootstrap not present in OSS config).

## Type of change

- [x] Bug fix
- [ ] Feature
- [x] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [x] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/schemas/...
go test ./plugins/governance/...
go test ./transports/bifrost-http/integrations/...
go test ./transports/bifrost-http/lib/...
```

All previously failing or incorrectly passing tests should now pass with accurate assertions.

## Breaking changes

- [x] No

## Security considerations

None. The removed governance tests were not covering a security gap — the auth enforcement they tested lives at the transport layer and is covered there.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

Adds end-to-end test coverage for a set of provider egress streaming and truncation correctness bugs. The new tests assert that Bedrock `converse-stream` properly closes content blocks before terminating, that Anthropic normalized and Claude Code passthrough streams emit contiguous `content_block_start` indices starting from 0, and that Bedrock Responses API truncated responses correctly signal `status=incomplete` with `reason=max_output_tokens` in both streaming and non-streaming modes.

## Changes

- Added a `contentBlockStop`-before-`messageStop` assertion to the existing Bedrock `converse-stream` basic test to catch #4923.
- Added a `content_block_start` index contiguity check to the existing Anthropic normalized streaming test to catch gaps introduced by server-tool rewrites (#4890 / #4932).
- Added a new **section 17 – Provider Egress Streaming/Truncation Guards** with four requests:
  - Bedrock forced-tool `converse-stream` verifies `toolUse`, `contentBlockStop`, and `messageStop` ordering (#4923).
  - Anthropic normalized `web_fetch` streaming verifies contiguous `content_block_start` indices (#4932).
  - Bedrock Responses non-streaming truncation verifies `status=incomplete` and `incomplete_details.reason=max_output_tokens` (#4680).
  - Bedrock Responses streaming truncation verifies `response.incomplete` is emitted and `response.completed` is absent (#4680).
- Added a new **section 18 – Claude Code Passthrough server-tool streaming index contiguity** with three requests covering `web_search` (normal results), `web_search` (zero results), and `web_fetch` via the `claude-cli` User-Agent passthrough path (#4890).

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Run the Postman/Newman collection against a live Bifrost instance:

```sh
newman run tests/e2e/api/collections/provider-harness.json \
  --env-var baseUrl=<BIFROST_URL> \
  --env-var bedrockModel=<BEDROCK_MODEL_ID> \
  --env-var anthropicKey=<ANTHROPIC_API_KEY>
```

All tests in sections 17 and 18 should pass. Specifically:
- Bedrock `converse-stream` responses must contain `contentBlockStop` before `messageStop`.
- All Anthropic streaming responses must have `content_block_start` indices `[0, 1, 2, …]` with no gaps.
- Bedrock Responses truncated (non-streaming) must return `status=incomplete` with `incomplete_details.reason=max_output_tokens`.
- Bedrock Responses truncated (streaming) must emit `response.incomplete` and must **not** emit `response.completed`.

## Breaking changes

- [x] No

## Related issues

Closes #4923, #4932, #4890, #4680

## Security considerations

None. These are read-only test assertions against existing API endpoints; no new credentials or secrets are introduced beyond those already required by the collection.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

The Anthropic `web_fetch_tool_result` block was previously discarded after conversion — only the request URL was preserved. This PR carries the full typed result payload (fetched document content, metadata, error codes) through the Bifrost neutral format so that Anthropic-compatible reverse conversion can faithfully reconstruct the `server_tool_use` + `web_fetch_tool_result` block pair on both the streaming and non-streaming paths.

## Changes

- Introduced `ResponsesWebFetchCall`, `ResponsesWebFetchDocument`, and `ResponsesWebFetchSource` schema types to represent the `web_fetch_tool_result` payload in the neutral format. Embedded `*ResponsesWebFetchCall` in `ResponsesToolMessage` alongside the existing advisor and code-interpreter payloads.
- Added `convertAnthropicWebFetchResultToBifrost` to map an `AnthropicContentBlock` web fetch result into the new neutral types, and `convertBifrostWebFetchCallToAnthropicBlocks` to rebuild the `server_tool_use` + `web_fetch_tool_result` block pair from a neutral `ResponsesMessage`. Both the streaming and non-streaming reverse converters now call these helpers instead of duplicating inline logic.
- On the forward (Anthropic → Bifrost) streaming path, `ToBifrostResponsesStream` now attaches the typed result payload to the emitted `web_fetch_call` item instead of dropping it.
- On the reverse (Bifrost → Anthropic) streaming path, `ToAnthropicResponsesStreamResponse` now emits the `web_fetch_tool_result` `content_block_start`/`content_block_stop` pair at `output_item.done` when the payload is present, removing the previous passthrough-only index-bump workaround for web fetch.
- `convertAnthropicContentBlocksToResponsesMessages` now calls `attachAnthropicWebFetchResult` on `web_fetch_tool_result` blocks instead of silently skipping them, attaching the result to the matching `web_fetch_call` message.
- `ConvertBifrostMessagesToAnthropicMessages` now delegates web fetch block construction to `convertBifrostWebFetchCallToAnthropicBlocks`, removing the duplicated inline assembly.
- Added `AnthropicToolTypeWebFetch20260318` and its `response_inclusion` field on `AnthropicToolWebFetch`. `convertBifrostToolToAnthropic` selects this type when `ResponseInclusion` is set. `UseCache` and `ResponseInclusion` are now round-tripped through `convertAnthropicToolToBifrost`.
- Deep-copy paths in `core/schemas/utils.go` and `framework/streaming/responses.go` updated to cover `ResponsesWebFetchCall` and its nested `Document`/`Source`/`Citations` fields, and to copy `Caller` on `ResponsesToolMessage`.
- Updated `TestAnthropicConverterOnly_IndicesContiguous` comments to reflect that `web_fetch` no longer collapses a result block on the all-normalized path. Added `TestAnthropicWebFetchResultRoundTrip` to verify the full Anthropic → Bifrost → Anthropic round-trip for a web fetch result with a nested document.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/providers/anthropic/... -run TestAnthropicWebFetchResultRoundTrip
go test ./core/providers/anthropic/... -run TestAnthropicConverterOnly_IndicesContiguous
go test ./core/providers/anthropic/... -run TestDoesWebSearchOrFetchAutoInjectCodeExecution
go test ./...
```

The new `TestAnthropicWebFetchResultRoundTrip` test exercises the full round-trip: an `AnthropicContentBlock` web fetch result is converted to the neutral `ResponsesWebFetchCall`, assembled into a `ResponsesMessage`, and then rebuilt back into `server_tool_use` + `web_fetch_tool_result` blocks with the document content intact.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No new auth, secrets, PII, or sandboxing surface introduced. Fetched document content that was previously discarded is now stored in memory within the request lifetime and forwarded to the caller — consistent with how other tool result payloads are handled.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
## Summary

`gemini-2.5-pro` does not support disabling thinking via `ThinkingBudget=0` / `IncludeThoughts=false`. When a budget of zero (or effort `"none"`) is requested for this model, the `ThinkingConfig` should be omitted entirely rather than set to a zero-budget value that the API rejects or mishandles.

## Changes

- Added `canDisableThinkingWithBudget(model string) bool` which returns `false` for `gemini-2.5-pro`, reflecting that the model cannot have thinking disabled via a zero budget.
- Added `setThinkingBudgetZeroIfSupported(config *GenerationConfig, model string)` which either sets `IncludeThoughts=false` + `ThinkingBudget=0` for models that support it, or sets `ThinkingConfig = nil` for `gemini-2.5-pro`.
- Replaced the two inline zero-budget assignments in both the chat (`convertParamsToGenerationConfig`) and responses (`convertParamsToGenerationConfigResponses`) paths with calls to `setThinkingBudgetZeroIfSupported`.
- Updated the `budget_zero_disables_thinking` test case to target `gemini-2.5-flash` (where the behavior is valid) and added a separate `pro_budget_zero_omits_thinking_config` case asserting that `ThinkingConfig` is `nil` for `gemini-2.5-pro`.
- Added `TestThinkingBudgetZeroUnsupportedForProResponses` to explicitly verify the responses path also omits `ThinkingConfig` when effort `"none"` is passed for `gemini-2.5-pro`.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/providers/gemini/... -run TestThinkingBudget
```

Expected: all `TestThinkingBudgetValidation_Chat`, `TestThinkingBudgetValidation_Responses`, and `TestThinkingBudgetZeroUnsupportedForProResponses` tests pass, with the `pro_budget_zero_omits_thinking_config` cases asserting a `nil` `ThinkingConfig`.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

Fixes a Gemini API rejection that occurs when `functionCallingConfig` is sent in a request that contains no `function_declarations` — for example, a web-search-only request that includes `GoogleSearch` but no custom function tools.

## Changes

- `ToolConfig` (i.e. `functionCallingConfig`) is now only set on the Gemini request when at least one tool in the request has `FunctionDeclarations`. Previously, `ToolConfig` was unconditionally applied whenever `ToolChoice` was present, causing Gemini to reject requests that only used built-in tools like `GoogleSearch`.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Send a Gemini request that includes `GoogleSearch` as the only tool alongside a `ToolChoice` parameter. The request should succeed without a rejection from the Gemini API.

```sh
go test ./...
```

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

None.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…tions surface (#4943)

* fix: round-trip anthropic redacted_thinking blocks on the chat completions surface

* chore: drop unreachable map guard, document the redacted_thinking strip exclusion

* refactor: handle stream content block starts with a type switch, document test cases

---------

Co-authored-by: Pratham Mishra <99235987+Pratham-Mishra04@users.noreply.github.com>
## Summary

Provider error responses were missing the `error.type` field on the nested `ErrorField` object. OpenAI-shaped consumers (and passthrough clients) read `error.type` to identify the exception category, so Bedrock, Cohere, Gemini, HuggingFace, and Vertex errors appeared with only `error.message` and no type — making it impossible to distinguish error classes programmatically.

## Changes

- **Bedrock**: Exception type (from `X-Amzn-Errortype` header or body `__type`) is now written to both the top-level `BifrostError.Type` and the nested `Error.Type`. Previously the nested field was never populated.
- **Cohere**: `errorResp.Type` is now forwarded to `Error.Type` when present.
- **Gemini**: `status` field (e.g. `RESOURCE_EXHAUSTED`, `INVALID_ARGUMENT`) from both single-object and array error bodies is now written to `Error.Type`. This also fixes the `ToGeminiError` round-trip, which reconstructs `status` from `error.type`.
- **HuggingFace**: `Error` struct is initialised before the type assignment, and `errorResp.Type` is now mirrored to `Error.Type` in addition to the top-level field.
- **Vertex**: `createError` now accepts a `status` string and writes it to `Error.Type`. The OpenAI-format path also attempts to recover the Vertex `status` field from the raw body when `openAIErr.Error.Type` is absent.
- Tests added for each provider covering the nested `error.type` population, header/body sourcing, and the Gemini round-trip regression.

## Type of change

- [x] Bug fix

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/providers/bedrock/... ./core/providers/cohere/... ./core/providers/gemini/... ./core/providers/huggingface/... ./core/providers/vertex/...
```

Each new test file exercises the specific fix:
- `TestParseBedrockHTTPError_PopulatesNestedErrorType` — type from header
- `TestParseBedrockHTTPError_NestedTypeFromBodyType` — type from body `__type`
- `TestParseCohereError_PopulatesNestedErrorType`
- `TestParseGeminiError_SingleObjectPopulatesStatusType` / `TestParseGeminiError_ArrayPopulatesStatusType`
- `TestParseGeminiError_RoundTripToGeminiError` — regression for `ToGeminiError` status preservation
- `TestParseHuggingFaceImageError_PopulatesNestedErrorType`
- `TestParseVertexError_PopulatesStatusType` / `TestParseVertexError_NoStatusNoType`

## Breaking changes

- [x] No

## Security considerations

None. Changes are limited to error response parsing; no auth, secrets, or PII are involved.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

When an OpenAI-compatible inbound request uses the legacy `max_tokens` field instead of `max_completion_tokens`, the value was previously ignored during conversion to Bifrost format. This PR ensures `max_tokens` is automatically promoted to `max_completion_tokens` when the latter is not explicitly set, preserving token limit intent across the conversion boundary.

## Changes

- In `ToBifrostChatRequest`, if `MaxCompletionTokens` is not set but `MaxTokens` is present, `MaxTokens` is copied into `MaxCompletionTokens` before constructing the Bifrost request.
- A local copy of `ChatParameters` is made before modification to avoid mutating the original request struct.
- A test assertion was added to verify that `max_tokens` is correctly mapped to `max_completion_tokens` after conversion.
- Added `claude-fable-5` with a 128k token limit to the static Anthropic max output token fallback map.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/providers/openai/...
```

Send an OpenAI-compatible chat request with `max_tokens` set but without `max_completion_tokens`. Verify the resulting Bifrost request has `MaxCompletionTokens` populated with the value from `max_tokens`.

## Breaking changes

- [x] No

## Security considerations

None.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
## Summary

All documentation code examples and a test comment have been updated to replace `schemas.NewEnvVar` with `schemas.NewSecretVar` across every supported provider. This aligns the docs with the renamed API and ensures users following the examples use the correct constructor.

## Changes

- Replaced all occurrences of `schemas.NewEnvVar(...)` with `schemas.NewSecretVar(...)` in provider documentation examples for Anthropic, Azure, Bedrock, Cerebras, Cohere, DeepSeek, ElevenLabs, Fireworks, Gemini, Groq, HuggingFace, Mistral, Nebius, Ollama, OpenAI, OpenCode, OpenRouter, Parasail, Perplexity, Replicate, Runware, Runway, SGL, Vertex, VLLM, and xAI.
- Updated a comment in `plugins_test.go` that referenced `NewEnvVar` to reference `NewSecretVar` instead.
- Added a blank line in `plugins_test.go` for formatting consistency.

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [x] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [x] Docs

## How to test

Verify that all documentation examples reference `schemas.NewSecretVar` and that no remaining references to `schemas.NewEnvVar` exist in the provider docs.

```sh
grep -r "NewEnvVar" docs/providers/
```

Expected outcome: no matches returned.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

N/A

## Security considerations

No security impact. This is a documentation-only rename that reflects the updated constructor name for handling secret/environment variable references.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…g of content type

 (#4956)

## Summary

This PR adds `content_type` support to Anthropic file uploads and enables `file_id` references in document/image content blocks via the Files API beta header. Previously, file uploads always used the browser-inferred content type from `CreateFormFile`, and there was no way to reference already-uploaded files by ID in message content.

## Changes

- Replaced `writer.CreateFormFile` with a manually constructed `textproto.MIMEHeader` in `FileUpload`, allowing the caller-supplied `ContentType` to be set on the multipart part rather than relying on the default `application/octet-stream`.
- Added `ContentType *string` to `AnthropicFileUploadRequest` and `BifrostFileUploadRequest` so the MIME type flows from the HTTP transport through to the provider.
- In the HTTP integration, `content_type` is now read from the multipart form field first, falling back to the `Content-Type` header on the file part itself.
- Added a `"file"` source case in `toBifrostResponsesDocumentBlock` to map Anthropic `file_id` references back into the Bifrost schema.
- Added `ConvertResponsesFileBlockToAnthropic` handling for `FileID`\-only blocks, setting `source.type = "file"` and populating `file_id` before returning early.
- Moved `FileID` from `ResponsesMessageContentBlock` to `ResponsesInputMessageContentBlockFile`, where it semantically belongs alongside `FileData`, `FileURL`, and `Filename`.
- Added automatic injection of the `files-api-2025-04-14` beta header when any message content block contains a `"file"` source type, consistent with how other beta headers are appended.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./...
```

1. Upload a file via the Anthropic files route with an explicit `content_type` form field (e.g., `application/pdf`) and verify the multipart part carries that content type.
2. Upload a file where only the file part's `Content-Type` header is set and confirm it is used as the fallback.
3. Send a Responses-path message with a document block referencing a `file_id` and confirm the `files-api-2025-04-14` beta header is automatically added to the outgoing request.
4. Confirm that a response containing a `"file"` source block is correctly mapped back to `ResponsesInputMessageContentBlockFile.FileID`.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

## Security considerations

No new auth surfaces or secrets handling introduced. The `ContentType` value is trimmed and validated before being set as a MIME header to avoid header injection.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
## Summary

Adds end-to-end test coverage for the Anthropic Files API, including file upload with an explicit `content_type` override, file upload with `content_type` inferred from the file part, using an uploaded file as a document source in `/v1/messages`, and using an uploaded file via `input_file` in `/v1/responses`. Cleanup requests delete both test files after the scenarios run.

## Changes

- Added response shape detection for Anthropic file objects (`type: "file"`) and deleted file objects (`type: "file_deleted"`) in the provider harness test script so the content-validation assertion correctly recognises Anthropic Files API responses.
- Added two upload scenarios: one that passes an explicit `content_type` field (`text/markdown`) alongside the file, and one that relies on the content type being inferred from the uploaded file part.
- Added a preview test for referencing an uploaded file as a `document` source with `file_id` in an Anthropic `/v1/messages` request (the proxy is expected to inject the `files-api-2025-04-14` beta header automatically).
- Added a preview test for referencing the same file via `input_file` / `file_id` through the native `/v1/responses` endpoint with an Anthropic model.
- Added DELETE cleanup requests for both uploaded files (`anthropicUploadFileIdA` and `anthropicUploadFileIdB`) to avoid leaving orphaned files after the test run.

## Type of change

- [ ] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [x] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

Run the Postman/Newman collection against a running instance with a valid `anthropicKey` and `baseUrl` set:

```sh
newman run tests/e2e/api/collections/provider-harness.json \
  --env-var "baseUrl=http://localhost:8080" \
  --env-var "anthropicKey=<your-key>"
```

Expected outcomes:

- Both upload requests return HTTP 200 with a non-empty `id` field.
- The document and `input_file` preview requests return a valid model response.
- Both DELETE cleanup requests succeed, removing the uploaded files.

## Breaking changes

- [x] No

## Related issues

## Security considerations

The `anthropicKey` is supplied via an environment variable and is not hardcoded in the collection. No PII or secrets are stored in the test fixtures.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
## Summary

This PR bumps core to v1.6.3, framework to v1.4.3, and all dependent plugins to their next patch versions, shipping a large batch of new features and bug fixes accumulated since v1.6.2.

## Changes

**New providers and integrations**
- DeepSeek added as a first-class provider with dedicated request handling and thinking-mode gating
- `bedrock_mantle` added as a first-class provider with SigV4 key config, native-Anthropic and OpenAI-compatible routing
- ClickHouse added as a log store backend with a hybrid store mode

**Auth and security**
- Full OAuth 2.1 authorization server for `/mcp`: discovery, dynamic client registration, PKCE authorize/token with refresh token rotation, consent page, JWT Bearer auth, session listing/revocation, sweep worker, and `mcp_server_auth_mode` config
- Virtual key expiry field with governance enforcement
- VK-mode OAuth2 grants revoked on VK deletion with user-liveness checks at refresh and request time
- Virtual key values now use `schemas.SecretVar` to support env-store references
- `virtualKeysByID` secondary index with cached signing key and VK lookups on the `/mcp` JWT auth path

**Provider and API fixes**
- Round-trip Anthropic `redacted_thinking` blocks so tool-use turns with extended thinking replay correctly
- Emit `contentBlockStop` events on Bedrock ConverseStream egress
- Clear per-attempt stream close claim so streaming retries and fallbacks survive SSE-embedded provider errors
- Report `cached_tokens` as reads only per the OpenAI spec so cache writes are not billed as reads
- Preserve Anthropic file ID document sources and forward file IDs and content type on the files integration
- Surface Gemini batch inline responses from the response field instead of dest
- Guard Gemini tool call config, fix 2.5-pro thinking budget value, OpenAI-through signature compatibility, and video reference field mapping
- Signal Bedrock `max_output_tokens` truncation on the Responses API
- Propagate `max_tokens` from the OpenAI integration and pass `chunking_strategy` as an extra param
- Preserve codex `tool_search_call` and `tool_search_output` input items on the Responses API streaming path
- Fixed Perplexity Responses API compatibility and Bedrock error type setting across all integrations

**Observability and logging**
- Error responses now carry latency information
- Cost recalculation streams progress via SSE with batch processing
- Sweep orphaned deferred spans in trace store TTL cleanup and complete deferred LLM spans on streaming goroutine exit
- Sanitize `ErrorDetailsParsed` so raw payloads honor `disable_content_logging`
- Fixed stats and log state for cancelled requests
- Fixed billing on failed Responses stream requests for Anthropic and Bedrock, and cost for image generation and edit streaming
- Tier costs now evaluated via input tokens instead of total tokens

**MCP**
- Per-MCP-server tool execution timeout configuration
- Deterministic MCP tool ordering for prompt cache stability
- Fixed MCP reconnect failure when `ListTools` fails during startup
- Skip background token refresh for disabled or unconfigured MCP clients
- Added connection_type, auth_type, state, virtual_key, and server/client_id filters with pagination to the MCP clients list

**Other features**
- IPv6 support added to the HTTP transport
- Models marked `is_deprecated` in pricing and catalog APIs instead of being filtered out
- Added user, team, customer, and business-unit name columns to the logs list
- Connectors can now attach multiple teams, customers, and business units
- Extended Bedrock vendor-prefix pricing fallback to OpenAI, Google, and xAI models
- Added stemming alongside exact keyword match and a no-signal fallback to the complexity analyzer
- Added missing OpenAI Responses lifecycle methods with explicit per-verb governance flags
- Refactored Anthropic request building into `BuildAnthropicChatRequestBody`, shared `completeRequest` across Anthropic, Azure, and Bedrock, lazy `BodySigner` SigV4 signing, and `BearerAuthHeader` helper

## Type of change

- [x] Bug fix
- [x] Feature
- [x] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [x] Providers/Integrations
- [x] Plugins
- [x] UI (React)
- [ ] Docs

## How to test

```sh
# Core/Transports
go version
go test ./...
```

- OAuth 2.1 MCP auth: configure `mcp_server_auth_mode`, register a client via DCR, complete the PKCE authorize/token flow, and verify JWT Bearer auth on `/mcp` requests.
- DeepSeek provider: configure a DeepSeek key and send a chat completion request; verify thinking mode is disabled when tool choice is required.
- `bedrock_mantle`: configure SigV4 credentials and verify both native-Anthropic and OpenAI-compatible routing paths.
- ClickHouse log store: configure the ClickHouse DSN and verify logs are written and queryable, including the hybrid mode token usage columns.
- Virtual key expiry: set an expiry on a VK and confirm requests are rejected after expiration.
- Streaming retries: trigger an SSE-embedded provider error and confirm the retry/fallback path completes successfully.
- Cache token accounting: issue a cached request and confirm `cached_tokens` in usage reflects reads only.

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

Closes #2347, #3106, #3121, #3139, #3357, #3951, #4262, #4314, #4402, #4446, #4679, #4689, #4720, #4721, #4756, #4777, #4788, #4816, #4851, #4863, #4868, #4872, #4942

## Security considerations

- The OAuth 2.1 authorization server introduces new token issuance, PKCE enforcement, refresh token rotation, and session revocation surfaces — these paths should be reviewed for token leakage, replay, and CSRF risks.
- Virtual key values stored as `schemas.SecretVar` may now reference env-store entries; ensure env-store access controls are correctly scoped.
- `disable_content_logging` bypass via `ErrorDetailsParsed` has been patched; verify no other raw-payload paths bypass this flag.
- SigV4 credentials for `bedrock_mantle` are handled via the new `BedrockMantleKeyConfig`; confirm sensitive fields are cleared before persistence.

## Checklist

- [x] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [x] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [x] I verified the CI pipeline passes locally if applicable
## Summary

The Bedrock provider's non-streaming executor path (`executeBedrockRequest`) had a bespoke inline error-handling block that bypassed `parseBedrockHTTPError`, causing the AWS exception type (e.g., `ResourceNotFoundException`) to be silently dropped from error responses. This was particularly visible with end-of-life models that return the exception type only via the `X-Amzn-Errortype` header. The fix routes all upstream HTTP errors through the shared `parseBedrockHTTPError` function and adds a regression test to guard this path.

## Changes

- Replaced the inline error-parsing block in `executeBedrockRequest` with a call to `parseBedrockHTTPError`, ensuring the AWS exception type is consistently surfaced on both the top-level `BifrostError.Type` and the nested `Error.Type` field.
- Added `TestExecuteBedrockRequest_SurfacesExceptionType`, an end-to-end test that spins up a local HTTP server mimicking a real AWS EOL model response (with a `:<url>` qualifier on the `X-Amzn-Errortype` header) and asserts the exception type is correctly propagated.

## Type of change

- [x] Bug fix
- [ ] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
go test ./core/providers/bedrock/...
```

The new test `TestExecuteBedrockRequest_SurfacesExceptionType` will fail on the previous code and pass with this fix. Verify that `ResourceNotFoundException` appears on both `bifrostErr.Type` and `bifrostErr.Error.Type` when a 404 response carries the `X-Amzn-Errortype` header.

## Screenshots/Recordings

N/A

## Breaking changes

- [ ] Yes
- [x] No

## Related issues

Regression for the bespoke inline error path in `executeBedrockRequest` that dropped the AWS exception type for retired/unsupported model responses.

## Security considerations

No security implications. This change only affects error message propagation.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [x] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [x] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
…antle` provider, virtual key `expires_at`, and per-client `toolExecutionTimeout` to Helm chart (#4960)

## Summary

Extends the Bifrost Helm chart with several new configuration capabilities: OAuth2/MCP server authentication modes, ClickHouse as a logs store backend, AWS Bedrock Mantle provider support, virtual key expiry, and per-client tool execution timeout overrides.

## Changes

- **MCP server auth modes**: Added `mcpServerAuthMode` (`headers`, `both`, `oauth`) to control how `/mcp` authenticates inbound clients. Added `oauth2ServerConfig` block supporting `issuerUrl`, `authCodeTtl`, `accessTokenTtl`, and `disableVkIdentity` for Bifrost-issued JWT flows.
- **ClickHouse logs store**: Added `clickhouse` as a valid `storage.logsStore.type`. Supports full connection configuration including host, port, database, credentials, protocol (`native`/`http`), TLS, dial timeout, and optional cluster name for replicated DDL.
- **Bedrock Mantle provider**: Added `bedrock_mantle_key_config` schema and example configuration supporting SigV4 credentials, AssumeRole via `role_arn`, `external_id`, and `session_name`. Added mutual-exclusivity constraints alongside existing provider key configs.
- **Virtual key expiry**: Added `expires_at` (RFC3339) field to virtual key definitions. Requests using a key past its expiry timestamp are rejected.
- **Per-client tool execution timeout**: Added `toolExecutionTimeout` to MCP client configuration, allowing per-server overrides of the global `toolManagerConfig.toolExecutionTimeout`. Accepts a Go duration string or bare integer (seconds); `0` falls back to the global default.

## Type of change

- [ ] Bug fix
- [x] Feature
- [ ] Refactor
- [ ] Documentation
- [ ] Chore/CI

## Affected areas

- [ ] Core (Go)
- [ ] Transports (HTTP)
- [x] Providers/Integrations
- [ ] Plugins
- [ ] UI (React)
- [ ] Docs

## How to test

```sh
# Render the chart and verify new fields appear in the generated config
helm template bifrost ./helm-charts/bifrost \
  --set bifrost.client.mcpServerAuthMode=both \
  --set bifrost.client.oauth2ServerConfig.issuerUrl=https://example.com \
  --set bifrost.client.oauth2ServerConfig.authCodeTtl=300 \
  --set bifrost.client.oauth2ServerConfig.accessTokenTtl=600 \
  --set storage.logsStore.type=clickhouse \
  --set storage.logsStore.clickhouse.host=clickhouse.default.svc.cluster.local

# Validate schema
helm lint ./helm-charts/bifrost

# Verify ClickHouse logs store block is rendered correctly
helm template bifrost ./helm-charts/bifrost \
  --set storage.logsStore.type=clickhouse \
  --set storage.logsStore.clickhouse.host=ch-host \
  --set storage.logsStore.clickhouse.protocol=native \
  --set storage.logsStore.clickhouse.secure=true

# Verify virtual key expiry field is included
helm template bifrost ./helm-charts/bifrost \
  --set 'bifrost.virtualKeys[0].name=test' \
  --set 'bifrost.virtualKeys[0].expires_at=2026-12-31T23:59:59Z'
```

New configuration fields:

| Field | Description |
|---|---|
| `bifrost.client.mcpServerAuthMode` | `headers` (default), `both`, or `oauth` |
| `bifrost.client.oauth2ServerConfig.*` | OAuth2 AS settings for `/mcp` |
| `storage.logsStore.type=clickhouse` | ClickHouse backend for logs |
| `storage.logsStore.clickhouse.*` | ClickHouse connection parameters |
| `virtualKeys[].expires_at` | RFC3339 expiry for virtual keys |
| `mcpClients[].toolExecutionTimeout` | Per-server tool execution timeout override |
| `providers.bedrock_mantle.*` | AWS Bedrock Mantle provider key config |

## Breaking changes

- [ ] Yes
- [x] No

## Security considerations

- `oauth2ServerConfig` introduces Bifrost as an OAuth2 authorization server issuing JWTs for MCP clients. The `issuerUrl` must be a stable, publicly reachable URL in multi-host deployments to ensure JWT `iss` claims and discovery documents are consistent.
- `disableVkIdentity` removes virtual-key identity from the OAuth consent flow; only valid in `oauth` mode.
- `bedrock_mantle_key_config` credentials support the `env.` prefix to avoid embedding secrets directly in values files.
- Virtual key `expires_at` enforcement happens server-side; expired keys are rejected at request time.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
@akshaydeo
akshaydeo requested a review from a team as a code owner July 6, 2026 18:30
@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Too many files changed for review. (503 files found, 100 file limit)

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
6 out of 10 committers have signed the CLA.

✅ citrocat
✅ surki
✅ nnNyx
✅ impoiler
✅ fus3r
✅ BearTS
❌ akshaydeo
❌ TejasGhatte
❌ roroghost17
❌ Pratham-Mishra04
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 489 files, which is 189 over the limit of 300.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c5f6e8f9-aa03-4b7b-9c44-d241c2cdede1

📥 Commits

Reviewing files that changed from the base of the PR and between 8f91911 and 4278bc7.

⛔ Files ignored due to path filters (14)
  • docs/media/provider-dashboard-deepseek.png is excluded by !**/*.png
  • docs/media/ui-load-balancing-dashboard.png is excluded by !**/*.png
  • docs/media/ui-load-balancing-metrics.png is excluded by !**/*.png
  • docs/media/ui-load-balancing-settings.png is excluded by !**/*.png
  • docs/media/ui-load-balancing.png is excluded by !**/*.png
  • docs/media/ui-mcp-server-auth-mode.png is excluded by !**/*.png
  • docs/media/ui-oauth-consent.png is excluded by !**/*.png
  • docs/media/ui-oauth-grants.png is excluded by !**/*.png
  • docs/media/ui-virtual-key-expiry.png is excluded by !**/*.png
  • examples/mcps/mcp-test-client/go.sum is excluded by !**/*.sum
  • framework/go.sum is excluded by !**/*.sum
  • plugins/governance/go.sum is excluded by !**/*.sum
  • tests/cmd/e2eseed/go.sum is excluded by !**/*.sum
  • tests/cmd/seed/go.sum is excluded by !**/*.sum
📒 Files selected for processing (489)
  • .claude/skills/review-pr/SKILL.md
  • .gitignore
  • Makefile
  • core/bifrost.go
  • core/changelog.md
  • core/internal/llmtests/account.go
  • core/internal/llmtests/responses_lifecycle.go
  • core/internal/llmtests/tests.go
  • core/internal/llmtests/validation_presets.go
  • core/internal/mcptests/connect_ping_listtools_test.go
  • core/internal/mcptests/per_server_timeout_test.go
  • core/mcp/clientmanager.go
  • core/mcp/credstore/per_user_headers.go
  • core/mcp/credstore/per_user_oauth.go
  • core/mcp/toolmanager.go
  • core/network/dialaddrhost_test.go
  • core/network/http.go
  • core/providers/anthropic/advisor_test.go
  • core/providers/anthropic/anthropic.go
  • core/providers/anthropic/cancelbilling_test.go
  • core/providers/anthropic/chat.go
  • core/providers/anthropic/chat_test.go
  • core/providers/anthropic/codeexecution_test.go
  • core/providers/anthropic/emptytoolresult_test.go
  • core/providers/anthropic/passthroughstream_test.go
  • core/providers/anthropic/redactedthinking_test.go
  • core/providers/anthropic/requestbuilder.go
  • core/providers/anthropic/requestbuilder_test.go
  • core/providers/anthropic/responses.go
  • core/providers/anthropic/types.go
  • core/providers/anthropic/utils.go
  • core/providers/anthropic/utils_test.go
  • core/providers/anthropic/websearch_test.go
  • core/providers/azure/azure.go
  • core/providers/azure/realtime.go
  • core/providers/azure/utils.go
  • core/providers/bedrock/bedrock.go
  • core/providers/bedrock/bedrock_test.go
  • core/providers/bedrock/cancelbilling_test.go
  • core/providers/bedrock/conversestreamstop_test.go
  • core/providers/bedrock/errors.go
  • core/providers/bedrock/errors_test.go
  • core/providers/bedrock/mantle.go
  • core/providers/bedrock/mantle_test.go
  • core/providers/bedrock/responses.go
  • core/providers/bedrock/streambuffering_test.go
  • core/providers/bedrock/transport_test.go
  • core/providers/bedrock/types.go
  • core/providers/bedrockmantle/bedrockmantle.go
  • core/providers/bedrockmantle/bedrockmantle_test.go
  • core/providers/bedrockmantle/utils.go
  • core/providers/cerebras/cerebras.go
  • core/providers/cohere/cohere.go
  • core/providers/cohere/errors.go
  • core/providers/cohere/errors_test.go
  • core/providers/deepseek/cachedcontents.go
  • core/providers/deepseek/deepseek.go
  • core/providers/deepseek/deepseek_test.go
  • core/providers/elevenlabs/elevenlabs.go
  • core/providers/fireworks/fireworks.go
  • core/providers/gemini/batch.go
  • core/providers/gemini/batchresults_test.go
  • core/providers/gemini/cachedcontents.go
  • core/providers/gemini/chat.go
  • core/providers/gemini/errors.go
  • core/providers/gemini/errors_test.go
  • core/providers/gemini/fileupload_test.go
  • core/providers/gemini/gemini.go
  • core/providers/gemini/gemini_test.go
  • core/providers/gemini/responses.go
  • core/providers/gemini/types.go
  • core/providers/gemini/utils.go
  • core/providers/gemini/videos.go
  • core/providers/groq/groq.go
  • core/providers/huggingface/errors.go
  • core/providers/huggingface/errors_test.go
  • core/providers/huggingface/huggingface.go
  • core/providers/mistral/mistral.go
  • core/providers/nebius/nebius.go
  • core/providers/ollama/ollama.go
  • core/providers/openai/chat.go
  • core/providers/openai/chat_test.go
  • core/providers/openai/large_payload.go
  • core/providers/openai/openai.go
  • core/providers/openai/openai_test.go
  • core/providers/openai/realtime.go
  • core/providers/openai/responses.go
  • core/providers/openai/responses_test.go
  • core/providers/openai/responseslifecycle.go
  • core/providers/openai/tool_search_roundtrip_test.go
  • core/providers/openai/transcription.go
  • core/providers/openai/transcription_test.go
  • core/providers/openai/utils.go
  • core/providers/opencode/opencode.go
  • core/providers/openrouter/openrouter.go
  • core/providers/parasail/parasail.go
  • core/providers/perplexity/perplexity.go
  • core/providers/perplexity/responses.go
  • core/providers/replicate/replicate.go
  • core/providers/replicate/utils.go
  • core/providers/runware/runware.go
  • core/providers/runway/runway.go
  • core/providers/sgl/sgl.go
  • core/providers/utils/bodysigner.go
  • core/providers/utils/modelparamscache.go
  • core/providers/utils/utils.go
  • core/providers/utils/utils_test.go
  • core/providers/vertex/cachedcontents.go
  • core/providers/vertex/errors.go
  • core/providers/vertex/errors_test.go
  • core/providers/vertex/types.go
  • core/providers/vertex/utils.go
  • core/providers/vertex/utils_test.go
  • core/providers/vertex/vertex.go
  • core/providers/vertex/vertex_test.go
  • core/providers/vllm/vllm.go
  • core/providers/xai/xai.go
  • core/schemas/account.go
  • core/schemas/account_test.go
  • core/schemas/async.go
  • core/schemas/bifrost.go
  • core/schemas/chatcompletions.go
  • core/schemas/images.go
  • core/schemas/mcp.go
  • core/schemas/mcp_json_test.go
  • core/schemas/models.go
  • core/schemas/provider.go
  • core/schemas/responses.go
  • core/schemas/responses_test.go
  • core/schemas/responsestooloutput_test.go
  • core/schemas/secretvar.go
  • core/schemas/serialization_test.go
  • core/schemas/trace.go
  • core/schemas/utils.go
  • core/schemas/utils_test.go
  • core/schemas/vault.go
  • core/streamfallback_test.go
  • core/utils.go
  • core/version
  • docs/benchmarking/run-your-own-benchmarks.mdx
  • docs/changelogs/helm-v2.1.26.mdx
  • docs/cli-agents/claude-code.mdx
  • docs/cli-agents/codex-cli.mdx
  • docs/cli-agents/cursor.mdx
  • docs/cli-agents/gemini-cli.mdx
  • docs/cli-agents/librechat.mdx
  • docs/cli-agents/open-webui.mdx
  • docs/cli-agents/opencode.mdx
  • docs/cli-agents/qwen-code.mdx
  • docs/cli-agents/roo-code.mdx
  • docs/cli-agents/zed-editor.mdx
  • docs/deployment-guides/config-json/providers.mdx
  • docs/deployment-guides/config-json/schema-reference.mdx
  • docs/deployment-guides/helm/providers.mdx
  • docs/deployment-guides/how-to/security-best-practices.mdx
  • docs/docs.json
  • docs/enterprise/adaptive-load-balancing.mdx
  • docs/features/governance/complexity-router.mdx
  • docs/features/governance/mcp-tools.mdx
  • docs/features/governance/virtual-keys.mdx
  • docs/mcp/auth/oauth.mdx
  • docs/mcp/auth/overview.mdx
  • docs/mcp/auth/per-user-headers.mdx
  • docs/mcp/auth/per-user-oauth.mdx
  • docs/mcp/gateway-auth.mdx
  • docs/mcp/gateway.mdx
  • docs/openapi/openapi.json
  • docs/openapi/paths/management/mcp.yaml
  • docs/openapi/schemas/inference/chat.yaml
  • docs/openapi/schemas/inference/common.yaml
  • docs/openapi/schemas/management/config.yaml
  • docs/openapi/schemas/management/governance.yaml
  • docs/openapi/schemas/management/mcp.yaml
  • docs/overview.mdx
  • docs/providers/provider-routing.mdx
  • docs/providers/supported-providers/anthropic.mdx
  • docs/providers/supported-providers/azure.mdx
  • docs/providers/supported-providers/bedrock-mantle.mdx
  • docs/providers/supported-providers/bedrock.mdx
  • docs/providers/supported-providers/cerebras.mdx
  • docs/providers/supported-providers/cohere.mdx
  • docs/providers/supported-providers/deepseek.mdx
  • docs/providers/supported-providers/elevenlabs.mdx
  • docs/providers/supported-providers/fireworks.mdx
  • docs/providers/supported-providers/gemini.mdx
  • docs/providers/supported-providers/groq.mdx
  • docs/providers/supported-providers/huggingface.mdx
  • docs/providers/supported-providers/mistral.mdx
  • docs/providers/supported-providers/nebius.mdx
  • docs/providers/supported-providers/ollama.mdx
  • docs/providers/supported-providers/openai.mdx
  • docs/providers/supported-providers/opencode.mdx
  • docs/providers/supported-providers/openrouter.mdx
  • docs/providers/supported-providers/overview.mdx
  • docs/providers/supported-providers/parasail.mdx
  • docs/providers/supported-providers/perplexity.mdx
  • docs/providers/supported-providers/replicate.mdx
  • docs/providers/supported-providers/runware.mdx
  • docs/providers/supported-providers/runway.mdx
  • docs/providers/supported-providers/sgl.mdx
  • docs/providers/supported-providers/vertex.mdx
  • docs/providers/supported-providers/vllm.mdx
  • docs/providers/supported-providers/xai.mdx
  • docs/quickstart/gateway/provider-configuration.mdx
  • docs/quickstart/go-sdk/provider-configuration.mdx
  • examples/configs/withclickhouselogstore/config.json
  • examples/configs/withclickhouselogstorehttp/config.json
  • examples/mcps/http-no-ping-server/main.go
  • examples/mcps/mcp-test-client/README.md
  • examples/mcps/mcp-test-client/go.mod
  • examples/mcps/mcp-test-client/main.go
  • examples/plugins/hello-world/.gitignore
  • framework/changelog.md
  • framework/configstore/clientconfig.go
  • framework/configstore/encryption_test.go
  • framework/configstore/migrations.go
  • framework/configstore/migrations_test.go
  • framework/configstore/rdb.go
  • framework/configstore/rdb_deadlock_postgres_test.go
  • framework/configstore/rdb_mcp_sessions_identity_test.go
  • framework/configstore/rdb_mcp_sessions_test.go
  • framework/configstore/rdb_oauth2_test.go
  • framework/configstore/rdb_test.go
  • framework/configstore/store.go
  • framework/configstore/tables/clientconfig.go
  • framework/configstore/tables/encryption_test.go
  • framework/configstore/tables/key.go
  • framework/configstore/tables/mcp.go
  • framework/configstore/tables/mcpheaders.go
  • framework/configstore/tables/mcplibrary.go
  • framework/configstore/tables/mcpoauth2.go
  • framework/configstore/tables/mcpoauth2issuance.go
  • framework/configstore/tables/mcpoauth2server.go
  • framework/configstore/tables/modelpricing.go
  • framework/configstore/tables/routingrules.go
  • framework/configstore/tables/temptokens.go
  • framework/configstore/tables/virtualkey.go
  • framework/configstore/vault_callbacks.go
  • framework/configstore/vault_callbacks_test.go
  • framework/docker-compose.yml
  • framework/go.mod
  • framework/logstore/asyncjob_test.go
  • framework/logstore/clickhouse.go
  • framework/logstore/clickhousemigrate.go
  • framework/logstore/clickhousestore.go
  • framework/logstore/clickhousestore_test.go
  • framework/logstore/config.go
  • framework/logstore/dialectsql.go
  • framework/logstore/logstoreparity_test.go
  • framework/logstore/matviews.go
  • framework/logstore/multi_team_filter_test.go
  • framework/logstore/rdb.go
  • framework/logstore/rdb_perf_test.go
  • framework/logstore/store.go
  • framework/logstore/tables.go
  • framework/mcp_headers/sweep.go
  • framework/modelcatalog/datasheet/capabilities_test.go
  • framework/modelcatalog/datasheet/cost.go
  • framework/modelcatalog/datasheet/cost_test.go
  • framework/modelcatalog/datasheet/overrides_test.go
  • framework/modelcatalog/datasheet/params.go
  • framework/modelcatalog/datasheet/store.go
  • framework/modelcatalog/datasheet/store_test.go
  • framework/modelcatalog/datasheet/types.go
  • framework/modelcatalog/main.go
  • framework/modelcatalog/models.go
  • framework/modelcatalog/pool_test.go
  • framework/oauth2/sync.go
  • framework/streaming/responses.go
  • framework/streaming/responses_test.go
  • framework/temptoken/scope.go
  • framework/temptoken/sweeper.go
  • framework/tracing/store.go
  • framework/tracing/store_test.go
  • framework/vectorstore/pinecone.go
  • framework/vectorstore/pineconehost_test.go
  • framework/version
  • helm-charts/bifrost/Chart.yaml
  • helm-charts/bifrost/README.md
  • helm-charts/bifrost/templates/_helpers.tpl
  • helm-charts/bifrost/values.schema.json
  • helm-charts/bifrost/values.yaml
  • helm-charts/index.yaml
  • plugins/compat/changelog.md
  • plugins/compat/conversion.go
  • plugins/compat/dropparams.go
  • plugins/compat/dropparams_test.go
  • plugins/compat/version
  • plugins/governance/changelog.md
  • plugins/governance/complexity/analyzer.go
  • plugins/governance/complexity/analyzer_test.go
  • plugins/governance/complexity/config.go
  • plugins/governance/complexity/keywords.go
  • plugins/governance/complexity/matcher.go
  • plugins/governance/complexity/matcher_test.go
  • plugins/governance/complexity/utils.go
  • plugins/governance/go.mod
  • plugins/governance/main.go
  • plugins/governance/ratelimitreset_test.go
  • plugins/governance/resolver.go
  • plugins/governance/resolver_test.go
  • plugins/governance/store.go
  • plugins/governance/store_test.go
  • plugins/governance/test_utils.go
  • plugins/governance/tracker.go
  • plugins/governance/utils.go
  • plugins/governance/version
  • plugins/jsonparser/changelog.md
  • plugins/jsonparser/version
  • plugins/logging/changelog.md
  • plugins/logging/go.mod
  • plugins/logging/main.go
  • plugins/logging/operations.go
  • plugins/logging/operations_test.go
  • plugins/logging/sanitize_test.go
  • plugins/logging/strip.go
  • plugins/logging/strip_test.go
  • plugins/logging/utils.go
  • plugins/logging/version
  • plugins/logging/writer.go
  • plugins/maxim/changelog.md
  • plugins/maxim/version
  • plugins/mocker/changelog.md
  • plugins/mocker/version
  • plugins/modelcatalogresolver/changelog.md
  • plugins/modelcatalogresolver/version
  • plugins/otel/changelog.md
  • plugins/otel/version
  • plugins/prompts/changelog.md
  • plugins/prompts/version
  • plugins/semanticcache/changelog.md
  • plugins/semanticcache/main.go
  • plugins/semanticcache/plugin_paths_test.go
  • plugins/semanticcache/search.go
  • plugins/semanticcache/version
  • plugins/telemetry/changelog.md
  • plugins/telemetry/version
  • scripts/bifrost-migration-cli/.gitignore
  • scripts/bifrost-migration-cli/model.go
  • tests/cmd/e2eseed/go.mod
  • tests/cmd/seed/go.mod
  • tests/cmd/seed/seed.go
  • tests/cmd/seedvks/go.mod
  • tests/cmd/seedvks/main.go
  • tests/e2e/api/README.md
  • tests/e2e/api/collections/bifrost-v1-mcp-auth.postman_collection.json
  • tests/e2e/api/collections/bifrost-v1-vk-expiry.postman_collection.json
  • tests/e2e/api/collections/provider-harness.json
  • tests/e2e/api/provider-capabilities.json
  • tests/e2e/api/provider_config/bifrost-v1-bedrock-mantle.postman_environment.json
  • tests/e2e/api/runners/filter-collection.mjs
  • tests/e2e/api/runners/harness-monitor.mjs
  • tests/e2e/api/runners/individual/run-newman-mcp-auth-tests.sh
  • tests/e2e/api/runners/individual/run-newman-vk-expiry-tests.sh
  • tests/e2e/clis/.gitignore
  • tests/e2e/clis/reports/.keep
  • tests/e2e/features/virtual-keys/pages/virtual-keys.page.ts
  • tests/semanticcache/.gitignore
  • transports/bifrost-http/handlers/config.go
  • transports/bifrost-http/handlers/featureflags.go
  • transports/bifrost-http/handlers/governance.go
  • transports/bifrost-http/handlers/governance_test.go
  • transports/bifrost-http/handlers/inference.go
  • transports/bifrost-http/handlers/list_models_vk_test.go
  • transports/bifrost-http/handlers/localhostcheck_test.go
  • transports/bifrost-http/handlers/logging.go
  • transports/bifrost-http/handlers/logging_test.go
  • transports/bifrost-http/handlers/mcp.go
  • transports/bifrost-http/handlers/mcpheaders.go
  • transports/bifrost-http/handlers/mcpinference.go
  • transports/bifrost-http/handlers/mcpoauth2.go
  • transports/bifrost-http/handlers/mcpoauth2consent.go
  • transports/bifrost-http/handlers/mcpoauth2consent_test.go
  • transports/bifrost-http/handlers/mcpoauth2discovery.go
  • transports/bifrost-http/handlers/mcpoauth2discovery_test.go
  • transports/bifrost-http/handlers/mcpoauth2issuance.go
  • transports/bifrost-http/handlers/mcpoauth2issuance_test.go
  • transports/bifrost-http/handlers/mcpoauth2jwt.go
  • transports/bifrost-http/handlers/mcpoauth2jwt_test.go
  • transports/bifrost-http/handlers/mcpoauth2sessions.go
  • transports/bifrost-http/handlers/mcpoauth2sessions_test.go
  • transports/bifrost-http/handlers/mcpoauth2utils.go
  • transports/bifrost-http/handlers/mcpoauth2utils_test.go
  • transports/bifrost-http/handlers/mcpserver.go
  • transports/bifrost-http/handlers/mcpserver_auth_test.go
  • transports/bifrost-http/handlers/mcpsessions.go
  • transports/bifrost-http/handlers/mcpsessions_test.go
  • transports/bifrost-http/handlers/middlewares.go
  • transports/bifrost-http/handlers/plugins_test.go
  • transports/bifrost-http/handlers/provider_keys.go
  • transports/bifrost-http/handlers/providers.go
  • transports/bifrost-http/handlers/providers_test.go
  • transports/bifrost-http/handlers/requestpayload_test.go
  • transports/bifrost-http/handlers/session.go
  • transports/bifrost-http/handlers/temptokens.go
  • transports/bifrost-http/handlers/utils.go
  • transports/bifrost-http/handlers/websocket.go
  • transports/bifrost-http/integrations/anthropic.go
  • transports/bifrost-http/integrations/anthropic_test.go
  • transports/bifrost-http/integrations/bedrock_test.go
  • transports/bifrost-http/integrations/genai_test.go
  • transports/bifrost-http/integrations/openai.go
  • transports/bifrost-http/integrations/router.go
  • transports/bifrost-http/integrations/router_test.go
  • transports/bifrost-http/lib/config.go
  • transports/bifrost-http/lib/config_test.go
  • transports/bifrost-http/server/oauth2.go
  • transports/bifrost-http/server/server.go
  • transports/changelog.md
  • transports/config.schema.json
  • transports/go.mod
  • transports/version
  • ui/.gitignore
  • ui/app/_fallbacks/enterprise/components/circuit-breaker/circuitBreakerView.tsx
  • ui/app/_fallbacks/enterprise/components/pii-redactor/piiRedactorProviderView.tsx
  • ui/app/_fallbacks/enterprise/components/pii-redactor/piiRedactorRulesView.tsx
  • ui/app/_fallbacks/enterprise/lib/contexts/rbacContext.tsx
  • ui/app/login/layout.tsx
  • ui/app/oauth/consent/layout.tsx
  • ui/app/oauth/consent/page.tsx
  • ui/app/workspace/audit-logs/page.tsx
  • ui/app/workspace/complexity-router/page.tsx
  • ui/app/workspace/config/views/mcpView.tsx
  • ui/app/workspace/dashboard/components/charts/logVolumeChart.tsx
  • ui/app/workspace/dashboard/components/charts/modelUsageChart.tsx
  • ui/app/workspace/dashboard/components/overviewTab.tsx
  • ui/app/workspace/dashboard/page.tsx
  • ui/app/workspace/dashboard/utils/chartUtils.ts
  • ui/app/workspace/dashboard/utils/exportUtils.ts
  • ui/app/workspace/logs/views/columns.tsx
  • ui/app/workspace/logs/views/logsHeaderView.tsx
  • ui/app/workspace/logs/views/logsVolumeChart.tsx
  • ui/app/workspace/mcp-registry/page.tsx
  • ui/app/workspace/mcp-registry/views/mcpClientSheet.tsx
  • ui/app/workspace/mcp-registry/views/mcpClientsFilterSidebar.tsx
  • ui/app/workspace/mcp-registry/views/mcpClientsTable.tsx
  • ui/app/workspace/mcp-sessions/page.tsx
  • ui/app/workspace/mcp-sessions/views/sessionsTable.tsx
  • ui/app/workspace/model-catalog/views/overviewTab.tsx
  • ui/app/workspace/model-limits/views/modelLimitsTable.tsx
  • ui/app/workspace/oauth-grants/layout.tsx
  • ui/app/workspace/oauth-grants/page.tsx
  • ui/app/workspace/oauth-grants/views/grantActions.tsx
  • ui/app/workspace/oauth-grants/views/grantsFilterBar.tsx
  • ui/app/workspace/oauth-grants/views/grantsTable.tsx
  • ui/app/workspace/oauth-grants/views/revokeGrantDialog.tsx
  • ui/app/workspace/pii-redactor/layout.tsx
  • ui/app/workspace/pii-redactor/page.tsx
  • ui/app/workspace/pii-redactor/providers/layout.tsx
  • ui/app/workspace/pii-redactor/providers/page.tsx
  • ui/app/workspace/pii-redactor/rules/layout.tsx
  • ui/app/workspace/pii-redactor/rules/page.tsx
  • ui/app/workspace/providers/dialogs/addNewCustomProviderSheet.tsx
  • ui/app/workspace/providers/dialogs/providerConfigSheet.tsx
  • ui/app/workspace/providers/fragments/allowedRequestsFields.tsx
  • ui/app/workspace/providers/fragments/apiKeysFormFragment.tsx
  • ui/app/workspace/providers/fragments/apiStructureFormFragment.tsx
  • ui/app/workspace/providers/fragments/betaHeadersFormFragment.tsx
  • ui/app/workspace/providers/fragments/governanceFormFragment.tsx
  • ui/app/workspace/providers/views/modelProviderKeysTableView.tsx
  • ui/app/workspace/providers/views/providerKeyForm.tsx
  • ui/app/workspace/virtual-keys/views/virtualKeyDetailsSheet.tsx
  • ui/app/workspace/virtual-keys/views/virtualKeySheet.tsx
  • ui/app/workspace/virtual-keys/views/virtualKeysTable.tsx
  • ui/components/sidebar.tsx
  • ui/components/ui/datePickerWithRange.tsx
  • ui/components/ui/modelMultiselect.tsx
  • ui/components/ui/tagInput.tsx
  • ui/components/ui/truncatedLabel.tsx
  • ui/lib/constants/config.ts
  • ui/lib/constants/icons.tsx
  • ui/lib/constants/logs.ts
  • ui/lib/schemas/providerForm.ts
  • ui/lib/store/apis/baseApi.ts
  • ui/lib/store/apis/governanceApi.ts
  • ui/lib/store/apis/index.ts
  • ui/lib/store/apis/mcpApi.ts
  • ui/lib/store/apis/mcpSessionsApi.ts
  • ui/lib/store/apis/oauth2ConsentApi.ts
  • ui/lib/store/apis/oauth2SessionsApi.ts
  • ui/lib/store/apis/providersApi.ts
  • ui/lib/types/config.ts
  • ui/lib/types/governance.ts
  • ui/lib/types/logs.ts
  • ui/lib/types/mcp.ts
  • ui/lib/types/mcpSessions.ts
  • ui/lib/types/schemas.ts
  • ui/lib/utils/loginGoto.ts
  • ui/lib/utils/validation.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

Comment @coderabbitai help to get the list of available commands.

@akshaydeo
akshaydeo merged commit 6fb0a3e into main Jul 6, 2026
14 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.