- Expanded web search and fetching providers with robust parsing, authentication storage integration, and response validation.
- Added support for new configurations including SearXNG safesearch, Cloudflare AI Gateway endpoints, and dynamic Firecrawl base URLs.
- Implemented comprehensive test suites covering error handling, content filtering, and provider-specific response behaviors.
Decoded the extra JSON string layer returned by Z.AI MCP search and kept structured payloads out of answer text.
Added regression coverage for source extraction and plain prose preservation.
Fixes#8000
callDuckDuckGoHtml now parses params.query once when parsedQuery is absent and uses that structured view for both q and kl. Direct searchDuckDuckGo/DuckDuckGoProvider.search calls no longer strip lang: from q while defaulting kl to us-en.
Fixes#7110
Replaced unrestricted locale component swapping with DuckDuckGo's documented kl allowlist and explicit aliases for provider-specific codes such as jp-jp, kr-kr, tw-tzh, and uk-en. Unsupported locale combinations now fall back to the existing us-en default instead of sending invalid kl values.
Expanded regression coverage for Japanese, Korean, Traditional Chinese, and unsupported region-language combinations.
Fixes#7110
callDuckDuckGoHtml hardcoded kl=us-en and never read parsedQuery.lang, so
the shared lang: directive was silently discarded — unlike the Perplexity
and SearXNG providers, which map it. Distinct locales collapsed to the same
request.
Added localeToKl mapping the parsed language-region locale onto DDG's
region-language kl code (swapping components, gb->uk alias), falling back to
us-en for language-only, malformed, or absent locales.
Fixes#7110
GPT-5.6 Responses-Lite models receive tool_choice "auto" (the forced
hosted choice is invalid under the lite shape, #5771/#5772), so the model
may answer without invoking the hosted web_search tool. The codex search
parser accepted any non-empty answer, returning a stale completion with
zero sources as a successful search.
callCodexSearch now tracks response.web_search_call.* events (and
web_search_call output items) and throws CodexNoWebSearchError when none
occurred. The candidate chain treats that error as retryable, advancing
default lite models to a non-lite model that forces web_search, and
surfaces a clear failure when the model was explicitly configured.
Fixes#6988
(cherry picked from commit a276cd0b3df1d0d041faf0a63fabcbb884e36a91)
- Implemented a structured web-search query parsing module supporting directives, tokenization, date parsing, and syntax serialization.
- Updated search providers to map query directives and date bounds to native provider parameters and filters.
- Added lenient result constraint post-filtering and configuration settings for enhanced engine routing.
- Added comprehensive unit and integration tests covering query parsing, constraint filtering, and provider-specific request mapping.
- Replaced single-provider preferences with ordered priority lists for web search and image generation.
- Added a `MultiSelectSubmenu` component supporting toggle and reordering interactions in settings.
- Implemented migration logic to convert legacy single-provider preferences into ordered priority lists.
- Updated setup wizard scenes, image generation fallback logic, and search provider chains to use priority lists.
- Routed Codex web search through configured Responses base URLs, API keys, and headers while preserving the official OAuth backend.
- Refused OAuth leakage to custom endpoints and stopped explicitly selected providers from silently falling back.
- Added transport, safety, and fail-closed regression coverage.
Fixes#6001
The consumer ask endpoint (/rest/sse/perplexity_ask) intermittently closes
its socket before responding. getApiConfigs emitted the OAuth session JWT
(returned by getApiKey while OAuth is the active origin) as a direct
api.perplexity.ai api-key config, so a transient transport failure on the
ask endpoint fell through and sent the session token as a Bearer to the
direct API, whose 401 masked the real error.
- Suppress the direct api-key config when getCredentialOrigin reports the
active perplexity credential as oauth.
- Give the OAuth ask request one transport-only retry; HTTP responses
(including 401/429) are final and never retried.
- Add regression coverage for both legs.
Fixes#5315
- Added explicit existence checks for objects prior to property access across various test suites.
- Replaced optional chaining with non-null assertions to satisfy TypeScript strictness requirements in test assertions.
Restored OMP_PROFILE and PI_PROFILE after the search CLI provider-settings tests override the agent dir, then rebuilt the directory resolver from env.
Fixes#3793
Distinguished an absent provider (use configured preferred provider) from an explicit `--provider auto` (one-shot bypass that still respects exclusions) in executeSearch.
Fixes#3793
Initialized the Z.AI Streamable HTTP MCP session before calling web_search_prime and preserved the returned session id on subsequent requests.
Added regression coverage for the authenticated MCP request sequence.
Fixes#3619
PerplexityProvider.isAvailable() accepted authStorage.hasAuth("openrouter")
as a valid credential, so any user with an OpenRouter key configured (for
LLM access) had every webSearch: auto request silently routed through
OpenRouter's perplexity/sonar-pro endpoint. Since Perplexity sits first in
SEARCH_PROVIDER_ORDER, downstream providers like Gemini were never reached
and users saw unexpected charges on their OpenRouter billing.
Auto-chain admission now requires a direct Perplexity credential
(PERPLEXITY_COOKIES, Perplexity OAuth, or PERPLEXITY_API_KEY).
isExplicitlyAvailable still returns true, so users who want the
OpenRouter-backed perplexity/sonar-pro path can opt in by setting
webSearch: perplexity explicitly — the existing OpenRouter fallback in
getApiConfigs handles that case unchanged.
Fixes#3251
- Moved authentication logic to `perplexity-auth.ts` to share logic between search providers and CLI commands.
- Updated authentication priority to prefer browser cookies over OAuth tokens during search operations.
- Modified the `token` CLI command to display active OAuth tokens when both an OAuth token and an API key are configured.
- Added comprehensive unit tests in `perplexity.test.ts` to verify authentication priority and precedence.
- Integrated comprehensive loop guard support for DeepSeek and assistant prose patterns, including configurable stream checks.
- Implemented Moonshot Flavored JSON Schema (MFJS) normalization for improved tool compatibility and enum type inference.
- Added support for Ollama reasoning effort backfilling and Grok-specific service tier cost tracking across providers.
- Expanded model catalog with new entries and unified compatibility logic for improved OpenRouter API integration.
This change introduces a new `openrouter` API type and extensively refactors OpenAI-family streaming providers, centralizing shared logic and improving robustness.
Key changes include:
- **Unified OpenAI-family Logic:** Consolidated core utilities, compat resolution, request shaping, and stream processing into `openai-shared.ts`, reducing duplication across `openai-completions`, `openai-responses`, and `openai-codex-responses`.
- **OpenRouter API Type:** Introduced a dedicated `openrouter` API type with dual-surface compatibility, allowing it to dispatch requests as either OpenAI Chat Completions or Responses.
- **Enhanced Provider Integration:**
- Improved Perplexity search to leverage shared OpenAI streaming transports, including API-key fallback to OpenRouter and support for Perplexity's Responses API.
- Integrated xAI-specific logic directly into the shared `stream.ts` dispatch, removing the dedicated `xai-responses` provider.
- Refined credential parsing for Google Gemini CLI and handling of Azure deployment names.
- **Robustness & Consistency:** Improved error handling for Codex, standardized output token parameter resolution, and ensured consistent application of reasoning suppression across all Chat Completions dialects.
- **New Documentation:** Added `provider-endpoint-constraints.md` to detail endpoint-specific behaviors and quirks for various providers.
- **Telemetry & Debugging:** Extended telemetry propagation to advisor calls and overflow compaction tasks. Improved debugging for Codex WebSocket failures and stream error messages.
- **Tooling & Security:** Updated browser stealth scripts to prevent detection and added a new `ts-no-inline-cast-access` TTSR rule.
Treated SearXNG HTTP 200 responses with no usable sources and upstream engine failures as transient provider errors. Added a generic renderable-content guard so provider fallback continues instead of returning an invisible success.\n\nFixes #2571
- Added transcript and assistant block version tracking for finalized segments.
- Changed committed block reuse logic to require prior finalization and same version.
- Fixed rerendering of committed finalized blocks when version values changed.
Anthropic OAuth web search now uses resolveAnthropicMetadataUserId so metadata.user_id matches the main streaming path's {session_id, account_uuid?, device_id} JSON envelope. API-key paths keep forwarding the raw session id.
Exported resolveAnthropicMetadataUserId from pi-ai. Extended the regression test to cover the OAuth shape.
Refs #2295
Forwarded the active web search session id as Anthropic Messages metadata.user_id so enterprise gateways can attribute and rate-limit search calls consistently with the main streaming path.
Added a focused Anthropic web search request-shape regression test.
Fixes#2295
- Added optional FetchImpl fields to compaction, proxy, AI, coding-agent, and mnemopi options.
- Threaded injected fetch implementations through OAuth, discovery, and search/LLM request flows.
- Removed exported hookFetch utility and its package entrypoint from utils.
- Replaced global-fetch test monkeypatching with per-test FetchImpl mocks across test suites.
- Updated the initial render intent to carry a `clearScrollback` flag.
- Adjusted first-frame rendering logic to preserve existing scrollback by default and clear only when requested.
- Added `resolver` stubs to coding-agent and web-search test registries for interface compatibility.
- Forced authenticated ask requests to `experimental`, matching the anonymous fallback since the cookie session ignores pro upgrades.
- Kept TUI collapsed search answers full; capping now only applies in compact mode via `maxAnswerLines`.
- Preserved full multiline task pending preview instead of bounding it.
- Sent the OAuth token as `__Secure-next-auth.session-token` cookie since the ask endpoint ignores bearer headers and silently downgrades to `turbo`.
- Fell back to `result.title` when web results omit `name`.
- Renamed `callPerplexityOAuth` to `callPerplexityAsk` and removed a stray brace.
- Added tests covering OAuth, API-key, and anonymous request shapes.
- Stopped capping the synthesized answer at 12 lines while sources expanded in full.
- Rendered the answer through Markdown so headings, bold, lists, and code display formatted.
- Added regression tests for expanded/collapsed answer rendering.
- Raised default search results to 20 and context size to high.
- Added related questions parsing and return_related_questions request flag.
- Added API-key request-shape tests covering defaults and parsing.
Reviewer noted that an unconditional ExaProvider.isAvailable steered the auto chain into the public MCP fallback before any later-configured provider could run. Restored the credential-gated isAvailable, then split out isExplicitlyAvailable so resolveProviderChain still routes an explicit Exa selection through MCP without affecting other providers.\n\nRefs #1860
- Passed ANTHROPIC_SEARCH_BASE_URL through to Anthropic web search calls that use authStorage fallback credentials instead of only applying it with ANTHROPIC_SEARCH_API_KEY.
- Added regression coverage asserting fallback Anthropic credentials use the search-specific base URL.
- Updated the environment and web_search docs to reflect the actual credential and base URL resolution order.
Fixes#1694
- Centralized OAuth access lifecycle in `AuthStorage`, returning identity metadata and new access-result types.
- Added 60-second skew and strict expiry checks, returning undefined/throws for stale or expired OAuth credentials.
- Removed provider-local token refresh flows from Gemini, Gemini CLI, Antigravity, Kimi, and related OAuth helpers.
- Migrated web-search providers from `AgentStorage` to `AuthStorage` session-aware lookup with `authStorage`/`sessionId`/`signal` flow.
- Replaced `findAnthropicAuth`/DB auth lookup with `buildAnthropicAuthConfig` and explicit base-url override/env fallback ordering.
- Added OpenAI Codex and Gemini web search provider options with updated setup/auth descriptions.
- Updated Codex OAuth flow to refresh near-expiry tokens during web_search and persist the refreshed credentials.
- Plumbed AgentStorage through search orchestrator, scrapers, and fetch paths so providers share session credentials.
- Refactored web provider and credential helpers to accept caller-provided AgentStorage and resolve keys synchronously.
Bun's WinHTTP backend can ignore AbortSignal once a TCP/TLS connection
stalls (oven-sh/bun#15275, oven-sh/bun#18536), so Esc never reached the
in-flight `web_search` fetch on Windows and the session froze until
Ctrl+C. Only kimi shipped any timeout at all (server-side); every other
provider passed `signal` to `fetch` with no client-side bound.
Introduced `withHardTimeout(signal, ms=60_000)` in providers/utils and
wired it into every web-search provider's outbound fetch — anthropic,
brave, codex, exa, gemini, jina, kagi, kimi, parallel, perplexity
(api-key and oauth), searxng, synthetic, tavily, z.ai. 60s tolerates
legitimate slow LLM-mediated responses while still guaranteeing the
request settles within a minute when Bun's abort fails to propagate.
Independently, `executeSearch`'s provider-fallback loop swallowed every
`AbortError` as a regular provider error and returned
"All web search providers failed", masking cancellation on every
platform. The catch block now calls `throwIfAborted(signal)` first so a
caller-initiated cancel propagates as `ToolAbortError`.
Fixes#1221
Removed unconditional topic:news coupling in buildRequestBody that scoped
Tavily index to news publications whenever recency was set. Technical queries
with --recency now search the general index filtered by time only.
Tightened SearchParams.recency contract in base.ts: providers MUST interpret
recency as a pure time filter and MUST NOT change topic scope as a side effect.