Raced queued Exa throttle waits against the caller abort signal so requests cancelled behind an earlier throttle wait reject immediately without breaking the serialized throttle chain.
Added regression coverage for cancelling a third Exa request queued behind another delayed request.
Fixes#3271
Made Exa request pacing observe cancellation during the configured delay instead of waiting for the full delay before checking the signal.
Added regression coverage for a queued Exa request cancelled while throttled.
Fixes#3271
Added configurable Exa search request pacing via exa.searchDelayMs so repeated web_search calls no longer burst directly into Exa rate limits.
Covered the provider contract with a focused Exa test and recorded the back-to-back request repro.
Fixes#3271
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
- Centralized Parallel API utilities and parsing logic into a single module.
- Exported constants and helper functions from `parallel.ts` to replace duplicated definitions in the search provider.
- Updated the search provider to leverage the unified `parseParallelSearchPayload` function with metadata parsing toggled off.
- 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.
- Replaced inconsistent list truncation messages with a standardized `[...N items elided...]` format across all scrapers.
- Updated documentation reference to use consistent variable naming convention.
- Updated various truncation and overflow messages across `pi-shell` and `coding-agent` to use the consistent `[...N units elided...]` format.
- Improved clarity of elided output by explicitly stating the count and type of omitted information.
- 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.
- Derived the `SEARCH_PROVIDER_ORDER`, `SEARCH_PROVIDER_PREFERENCES`, and `SEARCH_PROVIDER_LABELS` metadata dynamically from a single `SEARCH_PROVIDER_OPTIONS` source of truth.
- Reprioritized the default search provider sequence, shifting higher-order choices like Perplexity, Gemini, and Anthropic ahead of Tavily and Brave.
- Updated documentation to reflect the new search provider evaluation order.
- Migrated all wire protocol, schema definitions, and tools validation from Zod to ArkType across multiple packages.
- Updated extension runtimes, custom tools loader, and TypeBox compatibility shim to expose and use ArkType instances.
- Added a comprehensive ArkType migration guide, validation parity tests, and helper utilities.
- Removed redundant PDF asset routing and parsing implementations from the read tool.
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.
- Removed ANTIGRAVITY_NO_PREAMBLE_INSTRUCTION from the shared Gemini header constants.
- Updated Gemini CLI and web search request builders to prepend only ANTIGRAVITY_SYSTEM_INSTRUCTION before existing system parts.
- Adjusted the Gemini CLI alignment test to stop asserting the removed anti-preamble prompt lines.
- Updated `executeSearch` to read `providers.antigravityEndpoint` once and catch uninitialized-settings errors, so web search fallback no longer aborts before any provider runs.
- Added missing `Vision` and `Git` entries to `TAB_GROUPS` so those new settings now render in their intended setting-section groups.
- Adjusted context and idle-compaction tests to wait for prompt-in-flight transitions and provide `getContextUsage` for deterministic snapshots during pending turns.
- Updated getApiKey signatures to accept a Model and return ApiKey or ApiKeyResolver.
- Updated stream key handling to resolve credentials per model and use seedApiKeyResolver for retries.
- Added antigravityEndpointMode setting with auto/production/sandbox endpoint selection.
- Added 429/5xx endpoint failover for Gemini stream, usage, search, and image calls.
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
- Extended GitHub URL parsing to recognize commit paths and extract commit refs.
- Implemented a commit renderer that fetches commit metadata, stats, and file patches from the GitHub API.
- Added a handler branch to render commit URLs to markdown with `github-commit` results metadata.
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
Move bundled models, model cache/manager, thinking metadata, effort helpers,
provider descriptors/discovery, wire constants, and model identity utilities
into the new @oh-my-pi/pi-catalog package.
Update pi-ai to keep provider runtime/auth concerns, move catalog provider
metadata into CATALOG_PROVIDERS, and migrate coding-agent, agent, stats, docs,
and tests to import catalog values from pi-catalog.
Split coding-agent model registry helpers into discovery, roles, and models
config modules while preserving registry orchestration.
BREAKING CHANGE: @oh-my-pi/pi-ai no longer exports catalog subpaths such as
/models, /model-cache, /model-manager, /model-thinking, /effort,
/provider-models*, discovery helpers, and provider wire constants; use the
matching @oh-my-pi/pi-catalog subpaths instead.
- Introduced memoized dynamic import loaders for Babel parser, mnemopi modules, puppeteer, and HTML-related packages.
- Refactored eval import-rewrite helpers and runtime call sites to use asynchronous wrapping and parsing flows.
- Shifted fetch and web-scraper linkedom usage to on-demand imports so heavy modules load only when needed.
- Exported and applied wrapFetchForCch only for OAuth Anthropic web-search calls.
- Mapped model_context_window_exceeded to "length" and mapped unknown stop reasons to "stop".
- Adjusted header and param generation to preserve caller User-Agent and gate Claude Code betas.
- Updated stream and strict-tool retry handling to clear terminal errors and prevent regressions.
Completes the injectable-fetch transport wiring (15.10.8) that the feature
left half-done, fixing the deterministic CI test failures:
- compaction.compact() rebuilt summaryOptions field-by-field but dropped
`fetch`, so the injected transport never reached
requestOpenAiRemoteCompaction / generateSummary's remote path. Thread it.
- Read-tool URL pipeline had no fetch seam: renderHtmlToText gained a
fetchOverride param but renderUrl/ToolSession never carried one, so the
jina/parallel reader backends always used global fetch. Add
ToolSession.fetch -> renderUrl -> renderHtmlToText (defaults to global).
- searchWithParallel mirrored extractWithParallel but missed the fetch
option; add it.
- Repair tests whose deleted hookFetch interceptors were never replaced
with a FetchImpl seam (fetch-kagi-toggle, web-search-parallel,
issue-970 discovery).
- Update issue-1746 POSIX case to the #2154 preserved-scrollback contract:
unknown-viewport streaming deferral is now platform-independent.
- 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.
- Derived descriptors, default-model map, env keys, login list, and refresh dispatch from one ProviderDefinition per provider.
- Disabled OpenAI Codex stream obfuscation and interrupted whitespace-only tool-call argument deltas.
- Derived auth-broker callback ports and paste-code login set from the registry.
- Added status.done and tool.* symbols to theme mappings and presets.
- Replaced generic success glyphs with contextual +/-, tool icons, and warnings.
- Mapped tool/task/job completions to status.done or status.enabled with icon overrides.
- Triggered runtime provider refresh after extension registration and warned on failure.
- Adjusted `LspTool` to retry only zero/decl-only references with two 250ms waits for project-aware servers.
- Removed raw-mode GitHub repo README API fallback in `read`, so `:raw` now renders the page directly.
- Replaced regex heuristics in `isImagePlaceholderAnswer` with a fixed normalized placeholder set.
- Expanded Codex placeholder detection to match common image-reference phrases and punctuation.
- Raised `codex` provider failure when final and streamed text are placeholders and no sources exist.
- Dropped placeholder prose from returned answers while preserving citation sources.
- Routed image-gen, inspect-image, and web search providers through `withAuth`.
- Used `reuseInitialApiKey`/`createAuthStorageResolver` for force-refresh and rotate retries.
- Attached HTTP status to thrown errors so the retry classifier detects retryable failures.
- Replaced `providers.parallelFetch` with a new `providers.fetch` enum in settings and added migration cleanup for the legacy key.
- Updated `renderHtmlToText` to follow configured reader preference with ordered fallback attempts and remote-reader timeout handling before local conversion.
- Updated YouTube and fetch tests to use `providers.fetch` and cover Jina-first stall fallback behavior.
- Parsed /actions/runs URLs into run and job render handlers.
- Rendered run metadata with per-job breakdown, showing steps for failed jobs.
- Fetched job logs via API token, stripping ISO timestamp prefixes.
- 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.
- Added anonymous Perplexity authentication mode for unauthenticated web searches.
- Switched web-search setup checks to use `isExplicitlyAvailable` and removed key enforcement in doctor.
- Updated Perplexity OAuth flow to reuse auth handling for all non-key searches and anonymous responses.
- Updated CLI and provider option help text to mark the Perplexity key optional with fallback.
- Stopped prepending system_prompt to the consumer ask endpoint, which lacks a system slot and refused the meta-instruction.
- Kept system_prompt as a proper system message on the API-key path.
- Showed answer text in full in the TUI; kept the `omp q` compact cap.
- Rendered each source as a single title/domain/age line with the URL linked on the title.
- Collapsed the metadata block to one Provider line plus Usage.
- Rendered search errors as a framed panel matching the success layout.
- 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
Restored Exa's unauthenticated MCP fallback when neither auth storage nor EXA_API_KEY provides credentials. Preserved API-key search ordering and bounded the MCP request with the web-search hard timeout. Updated provider copy and regression coverage for the no-key path.\n\nFixes #1860