All notable changes to Agentic API are documented here.
- Added SearXNG as a selectable backend for the gateway-owned
web_searchtool (#326, part of #291). Select it withAGENTIC_WEB_SEARCH_PROVIDER=searxngor[web_search] provider = "searxng"and pointAGENTIC_WEB_SEARCH_BASE_URLor[web_search] base_urlat a self-hosted instance; the endpoint is mandatory (an absolutehttp(s)URL without a query or fragment, sub-path mounts allowed) and the server refuses to start without it. No API key is needed;SEARXNG_API_KEY(or the variable named byapi_key_env) is sent as aBearertoken only when set. Web and news results come from oneformat=json&categories=general,newsrequest per query, split by category. The gateway adapts the shared tool contract:allowed_domains/blocked_domainsand the model'sinclude_domains/exclude_domainsare enforced client-side on a label boundary,countis applied client-side after filtering,freshnessmaps totime_range(date ranges are ignored),languageis normalized to SearXNG'sxx/xx-YYform,safesearchmaps to0/1/2, andcountryplus the You.com-specific arguments are ignored. A403is reported as the JSON format being disabled, and a429explains SearXNG's bot-detection limiter, which rejects the gateway'sAccept-Encoding-free requests unless its address is onpass_ip; neither is retried. The SearXNG provider uses a dedicated client with redirects disabled: a "bang" query (!!g,!ddg, ...) makes SearXNG redirect to the named external engine before it looks atformat=json, and the gateway reports that 3xx as a failedweb_search_callrather than following it out of the configured instance. Each SearXNGmetadata[]entry carries"provider": "searxng". Concurrency inheritsmax_concurrent_gateway_calls. - Added Claude's native
web_fetch_20250910server tool as a gateway-executed tool on/v1/messagesand/v1/messages/count_tokens(#408). The declaration is rewritten into an ordinaryweb_fetchfunction tool for the upstream, the gateway fetches the page the model names and feeds the text back as a hiddentool_result, and the call never reaches the client, like native web search.max_usesis a request-wide budget of fetches charged for every admitted call,allowed_domains/blocked_domainsmatch on the host and apply to every redirect hop, andmax_content_tokenscuts the text at an approximate four bytes per token below the 1 MiB tool output cap. Only a URL that already appeared in a user message or a tool result can be fetched; non-public addresses (loopback, private, link-local, cloud metadata, carrier-grade NAT) are refused directly, through DNS, and through redirects, with connections pinned to the checked addresses and made directly, without environment proxies, under the default policy; every fetch is bounded in time, size, and redirects, and at mostmax_concurrent_gateway_callsfetches run at once. Failures reach the model as the documentedweb_fetch_tool_result_errorcodes.citationsenabled, the later-versionuse_cacheandresponse_inclusionsettings, and otherweb_fetch_*versions are rejected with HTTP 400; a plain function namedweb_fetchstays client-owned. Operators tune or disable the fetcher with[web_fetch]inconfig.tomlorAGENTIC_WEB_FETCH_ENABLED,AGENTIC_WEB_FETCH_ALLOW_PRIVATE_NETWORKS,AGENTIC_WEB_FETCH_MAX_RESPONSE_BYTES, andAGENTIC_WEB_FETCH_TIMEOUT_SECS.
- The domain lists of
web_searchandweb_fetchdeclarations are one shared type,DomainFilters(formerlyWebSearchFilters, the same two fields), matched by one shared policy module that also validates aweb_fetchentry as a host name (web_searchkeeps its non-empty-entry rule in the Messages adapter); the request and response wire shapes are unchanged, and the OpenAPI component is namedDomainFilters. agentic_core:ToolOutputcarries an explicit success/failure status set by the handler, which the Messages loop reports asis_error; the request-scoped tool registry and the declaration helpers take one concreteToolDeclaration, which each API converts to at its adapter boundary (responses_declarationsfor the Responses wire tools,registry_toolsfor Messages), soResponsesToolis a pure wire enum without the Messages-onlyweb_fetchvariant and without declaration behaviour; discovered MCP tools reach a Responses request throughrecord_discovered_mcp_tools.WebSearchProviderKindgains aSearxngvariant ("searxng") with no default endpoint,SEARXNG_API_KEYas its conventional key variable, and no provider concurrency ceiling;WebSearchProviderKind::ALLlists it afterTavily, so the operator-facing "expected one of" message is nowyou, brave, tavily, searxng.agentic_core::tool::SEARXNG_BASE_URL_HINTandvalidate_searxng_base_urlcarry the operator-facing rules for the mandatory endpoint (absolutehttp(s)URL with a host and no query or fragment), shared by the startup check and the provider.
- Added an OpenAI-compatible Conversations API with conversation and item CRUD, metadata updates, cursor pagination, and atomic initial-item creation. Stored Responses items can be managed through conversations (#351, #370).
- Added
GET /v1/responses/{response_id}for stored response payloads, preserving output, status, usage, and IDs without another upstream request. History-only and legacy records return 409 because they lack a retrievable snapshot (#354). - Added opt-in multi-agent HTTP Responses execution for stored requests with
multi_agent.enabled: true. The gateway can coordinate subagents, stream their work as one response, and resume their stored tree throughprevious_response_id; it validates the stored tree and enforces a shared response budget (#372, #373). - Added opt-in gateway-executed
code_interpreterusing isolated Linux workers and acode_interpreter_calloutput lifecycle. Building withembedded-code-interpreteralso requires the documented runtime setup and operator enablement; default builds do not register the tool (#368). - Added Tavily as a selectable
web_searchprovider withAGENTIC_WEB_SEARCH_PROVIDER=tavilyandTAVILY_API_KEY(#329). - Passed
/v1/chat/completionsand/v1/completionsthrough to the configured upstream, including streaming responses and upstream status codes (#369). - Added execution-level OpenTelemetry spans for Responses and Messages, including inference rounds, gateway tool calls, compaction, persistence, delivery, and upstream trace-context propagation (#349).
- Rust API migration:
InputItemandOutputIteminclude multi-agent item variants, and Responses streaming event types include agent attribution and collaboration events. Downstream exhaustive matches must handle the new variants.WebSearchProviderKind::ALLis now a&'static [Self]slice (#329, #372). - Preserved
prompt_cache_keyacross typed Responses execution, gateway tool rounds, and internal compaction; callers must supply it again on a later request if they want it on that continuation (#347). - Disabled Nagle's algorithm on accepted gateway connections to avoid delayed small SSE and WebSocket frames (#380).
- Rejected incomplete or malformed upstream Messages streams before built-in tool dispatch or successful completion, including missing starts or stops, invalid block indexes, duplicate events, and malformed SSE data (#345, #396).
- Counted every search in a batched Messages
web_searchcall againstmax_uses; a call that would exceed the remaining budget is refused in full (#391). - Hid unexecuted gateway-executed tool calls from terminal Messages results (#346).
- Prevented
store: falseHTTP continuations of stored Responses from writing a durable child response (#387).
- Added opt-in OpenTelemetry traces and HTTP metrics with OTLP HTTP/protobuf export, optional gzip compression,
incoming W3C trace context, trace-correlated local logs, and deadline-bounded shutdown. Telemetry is disabled
by default; enable signals with
OTEL_TRACES_EXPORTER=otlpand/orOTEL_METRICS_EXPORTER=otlp. Request spans remain open through response-body completion or disconnect, and cancellation is distinguished from handler panics. See the observability guide for configuration (#341). - Verified image preservation through the Responses gateway end to end (#253): integration coverage for mixed
text/image ordering, multiple images per turn, client-executed
view_imagetool output,previous_response_idcontinuation,conversation_idrehydration, statelessstore: falseproxying, and compaction of retained image-bearing user messages, over both the HTTP and WebSocket transports. - Recorded paired image cassettes — client → OpenAI as the reference and client → gateway → vLLM serving
Qwen/Qwen2.5-VL-3B-Instruct— for a text-and-image message, two interleaved images, aprevious_response_idfollow-up, and a client-executed tool returning an image through a structuredfunction_call_output, each streaming and non-streaming. Replay coverage compares request shape, completed-response structure, the streaming event lifecycle, and the history the gateway forwards on continuation; model wording is never compared (#253). The cassette recorder accepts--input-filefor the first of several turns and sends a tool handler's list of content parts as a structured output array. - Added automated Docker Hub release and nightly container publishing, with optional 30-day nightly tag retention
when
DOCKERHUB_CLEANUP_ENABLED=trueand the credential has tag-read and tag-delete permissions (#322, #337). - Added typed per-model input-modality overrides to
config.toml([models."<served-model-id>"] input_modalities = ["text", "image"]), validated at startup: unknown modality names, empty lists, duplicates, and image-only lists are rejected with the offending file and line (#252). - Added Brave Search as a selectable backend for the gateway-executed built-in
web_searchtool (#294, Phase 2 of #291). Select it withAGENTIC_WEB_SEARCH_PROVIDER=braveor[web_search] provider = "brave"and supplyBRAVE_API_KEY; the endpoint defaults tohttps://api.search.brave.comand can be overridden withAGENTIC_WEB_SEARCH_BASE_URLor[web_search] base_url. Web and news results come from one request per query. The gateway adapts the shared tool contract:allowed_domains/blocked_domainsand the model'sinclude_domains/exclude_domainsare enforced client-side on a label boundary,countis clamped to Brave's maximum of 20,freshnessis rendered in Brave syntax,languagemaps tosearch_lang, and the You.com-specificlivecrawl,livecrawl_formats,crawl_timeout, andboost_domainsarguments are ignored. Rejected credentials and HTTP 429 responses fail theweb_search_callwithout an automatic retry, naming the key variable or the upstreamRetry-Aftervalue and never echoing the secret. Each Bravemetadata[]entry carries"provider": "brave". - Added
[web_search] max_concurrent_queriesandAGENTIC_WEB_SEARCH_MAX_CONCURRENT_QUERIESto cap concurrent provider requests inside one batched search. Brave defaults to1for its free-plan rate limit; You.com keeps inheritingmax_concurrent_gateway_calls. The effective ceiling is the smallest of the gateway limit, this override, and the provider's own ceiling.
- Bounded Tokio runtime shutdown to 1 second after request draining, followed by a separate 3-second telemetry shutdown deadline. In-flight blocking work no longer keeps process exit waiting indefinitely (#341).
- Modeled
refusalas an assistant-history content part so OpenAI-style history replays through the typed Responses executor instead of being rejected as unmodeled (#253). - Changed Rust input-content APIs (#263):
InputTextContent,InputImageContent, andInputFileContentnow retain unmodeled fields inextra. UseInputTextContent::new(text)or supplyextra: Default::default()when migrating struct literals.InputContentgainsRefusal(RefusalContent)and replaces the unitUnknownvariant withUnknown(String); update exhaustive matches and constructors.Unknowncannot be serialized and typed execution rejects it with the original content type in the error. Existing content-type re-export paths are preserved. - Rust
agentic_core::config::Configstruct literals must now provideresponses: ResponsesConfig::default()(or validated custom limits).ExecutionContext::newkeeps its signature and defaults; useExecutionContext::with_responses_configto override them.ExecuteRequest::with_max_stream_event_bytesandGatewayStreamAccumulator::with_max_stream_event_bytesadd explicit delivery limits; existing constructors andcall_inferenceremain available, withinference::call_inference_limitedexposing a custom SSE-line limit. - Response-size failures now use
ExecutorError::ResourceLimitExceeded { limit, max_bytes };ResourceLimitis re-exported fromagentic_core::executor. Callers classifying size failures should handle this typed variant instead of inspecting error messages (#304). - Modeled the Codex model catalog and the upstream model listing as typed Rust structs instead of
untyped JSON, and reported an undecodable upstream
/v1/modelspayload as502rather than serving it as an empty catalog (#252). agentic run codexandagentic harness codexnow resolve the model and its input modalities from a single gateway catalog snapshot before writing an isolated Codex home, retrying a warming gateway and failing with an actionable error when the catalog cannot be fetched or does not list the selected model. A gateway behind OIDC now requires--api-keyforagentic harness codex.agentic_harness::prepare_codex_homerequires the resolved modalities and is no longer public (#252).WebSearchProviderConfigis now#[non_exhaustive]and gainsproviderandmax_concurrent_queriesfields; construct it withWebSearchProviderConfig::new(api_key, base_url)plus thewith_providerandwith_max_concurrent_queriesbuilders. Downstream crates that built it with a struct literal must switch to the constructor; field reads andDefaultare unchanged.WebSearchProviderKindgains aBravevariant,FromStr(case-insensitive),default_base_url,default_max_concurrent_queries, andconfig_name;WebSearchHandler::from_configbuilds the handler for the selected provider andGatewayExecutors::from_configuses it. Withproviderunset, You.com behavior, configuration, and model-facing output are unchanged; a generatedconfig.tomlnow recordsprovider = "you"and leavesapi_key_envunset so provider switches select the matching default credential variable.
- Reported accumulated token usage across all hidden gateway-executed Messages tool rounds instead of reporting only the final inference round, for both JSON responses and streaming responses (#325).
- Fixed Python release setup, fixture readiness, and portable wheel builds; validate Linux x86_64, macOS Intel, and macOS Apple Silicon wheels in CI. Included the package description and documentation links in PyPI metadata (#305, #307, #309).
- Built public container dependencies before Docker Hub authentication so repository-scoped credentials can publish releases, and skipped unchanged nightly publications using recorded successful commit status (#337).
- Rejected message content the typed Responses executor cannot convey — unmodeled part types and empty part arrays,
alongside the existing
input_filerejection — with a400naming the offending part, instead of forwarding a synthetic{"type": "unknown"}part or silently dropping it. Modeled parts keep their unmodeled extension fields through the typed path, so a message is never mutated in transit, never means something different on the typed path than on the rawstore: falsepath, and is never persisted with content the client did not send (#253). - Counted an image referenced by
file_idas retained context during compaction, matching inline images (#253). - Followed MCP
tools/listpagination to discover tools beyond the first page, including opaque empty cursors; reject repeated cursors and bounded-pagination failures instead of exposing partial discovery (#311). - Accounted for unrestricted output role/type/status strings, empty web-search query entries, pending or late-bound item identities, and terminal error details in response limits. Kept reasoning-part and shell-command completion accounting linear for sequential multipart streams (#304).
- Charged the Responses retained-byte budget for logical output (text, arguments, annotations, nested JSON, and one structural charge per retained entry, including empty JSON values) instead of raw upstream SSE line bytes, so fine-grained chunking, coarse chunking, and non-streaming JSON consume identical budget, and empty or done-only parts are charged as they arrive (#288, #304).
- Replaced the fixed 1 MiB Responses WebSocket event ceiling with the configured
max_stream_event_bytes; the executor now validates the terminalresponse.completedevent against the WebSocket transport limit, includingstream_idrouting metadata, before persisting the response or publishing a session checkpoint (#304). - Added independent, validated
[responses]limits for upstream JSON bodies, upstream SSE lines, retained output, and client stream events, with a typedResourceLimitExceedederror that maps upstream overflows to HTTP 502 (#288). - Resolved Codex image capabilities consistently: the HTTP model catalog and both launcher modes
now advertise the same resolved
input_modalities, so a vision-capable model no longer has image content stripped client-side because an isolated catalog hardcoded["text"]. Existing persistent Codex session homes must be regenerated to pick this up (#252).
- Extended the pinned Codex 0.149.1 smoke with actual PNG attachments through both launcher modes, exact upstream image-byte assertions, and a text-only negative control. The smoke replays the committed vision recording without live API credentials (#261).
- Added client-executed shell tools with typed
shell_callandshell_call_outputitems, incremental command streaming, explicit tool selection, and stored-history continuation (#264). - Added a configurable serialized request size limit for HTTP bodies and WebSocket messages through
--max-request-body-size-bytes,AGENTIC_MAX_REQUEST_BODY_SIZE_BYTES, or[server] max_request_body_size_bytes, retaining the 10 MiB default (#260). - Added the Agentic API website, versioned documentation navigation, contributor profiles, and automatic website deployment after crate releases (#272, #285, #287).
-
Refactored
web_searchinto a typed provider contract and module split (tool/web_search/{mod,args,you}) as the extension seam for further providers (#291): provider responses now normalize intoWebSearchResult/WebSearchProviderMetadatainstead of forwarding raw You.com JSON, and the provider trait exposes amax_concurrent_requestsceiling that bounds query fan-out. The model-facing tool output keeps You.com's field names and the publicweb_search_call.action.sourceslist is unchanged, but the normalization contract is now explicit: cosmeticthumbnail_url/original_thumbnail_url/favicon_urland unknown fields are dropped, keys follow the typed struct order,nulland empty fields are omitted, and each query has a metadata object even when the provider omits metadata or returnsnull. Itsqueryfalls back to the submitted query; absentsearch_uuidandlatencyremain omitted. This intentionally changes the model-facing output frommetadata: [null]tometadata: [{"query": "..."}]in that case. An invalidfreshnessfails fast with a tool config error instead of a provider round trip. The You.com response body and aggregate tool-output size limits are unchanged.WebSearchProviderConfigkeeps its existing public shape, gains anewconstructor, and redacts the API key inDebugoutput; provider selection and per-provider concurrency configuration are deferred to the first additional provider. -
Unified Responses JSON and SSE processing under
AgentPipeline, sharing synchronous ingestion, typed output-item assembly, tool-call translation, lifecycle validation, and ordered client delivery (#274). -
Introduced
MessagesRequestContextfor the Messages tool loop, preserving unmodeled upstream fields while centralizing request mutation and web-search budgets (#249). -
Changed Rust integration APIs: Messages loops now accept
MessagesRequestContext, the publicfunction_ssemodule was removed, and gateway configuration usesGatewayOptions. Downstream crate consumers must adapt affected integrations (#249, #274, #260).
- Made stream delivery cancellation-safe, committing lifecycle and sequence state only after successful delivery and bounding deferred events by count and bytes (#302).
- Finished Messages inference rounds at
message_stop, and allowed Messages to answer after forced tool use (#290, #296). - Preserved incomplete upstream terminal status when an SSE completion event carries an incomplete response (#277).
- Honored Responses WebSocket storage settings with bounded connection-local sessions (#257).
- Applied the configured streaming chunk timeout to stalled upstream error-body reads in Responses streaming and Messages tool-loop requests (#286).
- Added opt-in Python wheel publishing through PyPI Trusted Publishing, using the declared Cargo workspace version, and rejected duplicate PyPI and crates.io releases before building (#301).
- Expanded shell-tool replay and continuation coverage, shared-ingestion lifecycle checks, WebSocket session and storage tests, request-size boundary tests, and stalled upstream error-body regressions.
- Added the build-only
agentic-apiPython distribution withserve,doctor, and version commands, packaged Rust gateway binaries, local or remote vLLM launch modes, wheel validation, and Linux and macOS release artifacts (#201). - Added end-to-end parallel tool calling for typed Responses requests, including forwarding the model-generation preference, bounded concurrent execution for gateway-executed built-in tools, batched web searches, stable output ordering, and per-call failure isolation (#181, #214).
- Added attached Claude Code and Codex workflows with isolated model and provider configuration and recorded CLI coverage (#210).
- Added configurable streaming chunk timeouts for Responses and Messages streams, with a ten-minute default (#221, #227).
- Added an
agentic-llm-dsplit-execution backend with authenticated hydrate and persist endpoints for the llm-d coordinator (#216). - Added deployment guides and replay coverage for NVIDIA Dynamo and llm-d Kubernetes upstreams, including persistent PostgreSQL storage in the kind guide (#184, #207, #212).
- Added a benchmark suite comparing WebSocket, HTTP/SSE, and HTTP/JSON Agentic API flows with direct vLLM across tool loops, function selection, and stateful conversation workloads (#185).
- Added a repository-local pull request review skill with explicit wire-format and replay-cassette checks (#228).
- Added client tool search support with typed tool discovery, deferred tool materialization, stateful continuation, and recorded streaming, non-streaming, and WebSocket coverage (#186).
- Added concurrent Responses WebSocket multiplexing with per-request
stream_idrouting, FIFO ordering within each stream, and bounded concurrency across streams (#240). - Added compile-time OpenAPI 3.1 schema generation and checked-in schema validation for the HTTP API (#229).
- Added pinned SGLang conformance recordings, replay coverage, and launch and recording guidance (#267).
- Forwarded typed Responses reasoning configuration upstream and preserved complete streamed reasoning content, summaries, and opaque state (#219, #225).
- Replayed persisted plaintext reasoning safely during continuation while rejecting opaque-only state that vLLM cannot consume (#222).
- Preserved MCP list-tools records in item history for discovery lifecycle decisions while excluding them from model input, preventing repeated public discovery items on later turns (#214).
- Improved Rust and container CI caching, test setup, and path filtering to shorten release validation (#205).
- Clarified client-executed and gateway-executed tool roles in Codex integration documentation (#230).
- Documented executor streaming ownership and validation boundaries, with a repository review skill for enforcing the architecture (#246).
- Updated the execution architecture documentation to match the current scheduler and llm-d backend (#270).
- Preserved the typed
ignore_eosextension when forwarding Responses requests to vLLM (#268).
- Rejected continuations that omit required function call outputs instead of proceeding with unresolved call IDs (#214).
- Preserved MCP and web-search public item types during mixed built-in tool rounds (#214).
- Removed connection-nominated hop-by-hop headers from proxied requests and responses as required by HTTP semantics (#217).
- Required a healthy packaged gateway before
agentic-api doctor --mode localreports success (#223). - Rebuilt workspace crates after
cargo-chefdependency cooking so container binaries carry current source and package metadata (#208, #209). - Hardened split execution with atomic duplicate persistence, strict relayed-response validation, independent secret validation, bounded hydrate and persist payloads, stable error envelopes, and graceful shutdown error propagation (#235).
- Rejected relayed responses with missing, reused, or unstable tool call IDs before persistence, while preserving the reserved response ID for corrected retries (#237).
- Aligned relayed SSE validation with provider-compatible event shapes while continuing to reject inconsistent lifecycles and terminal items (#236).
- Forwarded Responses
textgeneration settings through typed execution paths while preserving provider-specific stateless proxy payloads and JSON Schema property order (#231, #234). - Bounded WebSocket queues, response data, gateway tool results, and MCP discovery and transport payloads so concurrent response streams cannot grow memory without limit (#240).
- Enforced CLI readiness deadlines across probes and retry sleeps, including stalled and late-success cases (#265).
- Cleaned up model subprocesses when startup is interrupted or fails during readiness and database initialization (#266).
- Preserved upstream error headers and content types on non-streaming Responses errors (#250, #262).
- Accepted upstream SSE
data:fields with or without an optional separating space (#269). - Rejected unsupported message file content on typed Responses paths instead of silently dropping it (#258).
- Excluded image bytes from compaction token estimates while continuing to count surrounding text (#255, #259).
- Treated negative upstream
sequence_numbersentinels as unspecified while preserving otherwise valid streaming events (#267). - Made web-search action construction fallible so empty query lists return a typed error instead of panicking (#230).
- Added matched OpenAI and gateway cassettes for reasoning and parallel tool calling, replay tests for Dynamo, a generic cassette validator, Python package and wheel test suites, and dedicated CI jobs for the new release paths.
- Strengthened multi-round cassette assertions for public stream ordering and stabilized Python readiness retry coverage across supported interpreter versions (#242, #247).
- Added regression coverage for structured
input_textitems that omit an explicit message type (#150, #248).
- Preserved Claude Code Messages transport fidelity across the gateway.
- Updated You.com web search integration to use GET query parameters.
- Aligned deployment and harness documentation with the 0.4.0 release.
- Fixed web search test hangs in CI.
- Added the Agentic API harness CLI for running Codex and Claude Code against Agentic API.
- Added home-based configuration and typed tool settings for standalone deployments.
- Added support for Codex CLI remote compaction V2.
- Added Kubernetes deployment guidance and architecture documentation.
- Improved handling of Codex and Claude harness upstream configuration and compatible reasoning effort values.
- Preserved unsupported parallel tool calls through serialized upstream requests.
- Hardened MCP configuration and startup behavior.
- Improved Kubernetes health and readiness behavior for read-only container roots.
- Added native Codex and Claude harness coverage and expanded compatibility tests.
Initial documented release.