v2.3.0 — Caller-bound A2A, OpenCode routing, complete streaming
-
adk-acpmoves toagent-client-protocol2.2.adk_acp::agent_client_protocolre-exports the SDK, so code that uses it directly compiles against the 2.x API. The wire protocol stays stable ACP v1, and theadk-acpAPI is unchanged. -
AP2 amount conversion is fallible (
adk-payments,ap2):PaymentCurrencyAmount::to_moneyandAp2McpReceiptStatus::from_receiptreturnResult<_, Ap2Error>. Malformed, over-precise, or overflowing amounts returnAp2Error::InvalidAmountinstead of a zero or wrapped value. -
Chrome starts with its sandbox on (
adk-browser):BrowserSessionno longer adds--no-sandboxto every Chrome session. Containers that run Chrome as root opt in withBrowserConfig::add_arg("--no-sandbox");chrome_optionscannot replace the argument list.BrowserConfiggains the public fieldsrequire_explicit_startandchrome_options, so struct-literal construction needs..Default::default(). -
Terminal streaming events carry the complete response (
adk-agent,adk-server): the last event of a streamed model response holds the whole response, markedprovider_metadata.content_complete = true, instead of only the final chunk. Raw/api/run_sseclients that append every event's text show the reply twice; replace the content rendered for an eventidwhencontent_completeis set, or pass each event throughadk_core::EventTextDeltas. Tool-result events arrive in completion order rather than call order.
-
A2A sessions and tasks bind to the authenticated caller (
adk-server): the/a2aand/a2a/streamroutes, and the v1jsonrpc_handlerandrest_router, use the authenticated principal as the session user and record it as the owner of every task they create.tasks/get,tasks/cancel, and the v1 task, list, subscribe, and push-config operations answer "not found" for another caller's task; a v1messageIddeduplicates per caller; and a v1INPUT_REQUIREDtask resumes only for its owner. The session user was derived from the client-suppliedcontextId, so an authenticated caller who knew another caller'scontextIdor task ID could read that caller's history, poll its task, or cancel it. Deployments without authentication keep thecontextId-derived session user. -
Debug event lookup checks trace ownership (
adk-server):GET /api/apps/{app}/users/{user}/sessions/{session}/events/{event}answers 403 when spans recorded for the session belong to a different user, asGET /api/debug/trace/session/{session_id}does, instead of reading the trace by session ID alone. -
Background and cron routes mount behind authentication (
adk-server, featurebackground):ServerBuilder::with_background_runsandServerBuilder::with_cron_jobsserve/api/runsand/api/cronbehind the server's auth middleware. The standalone routers carry no authentication; their rustdoc and the functional API guide state that a standalone mount must sit behind an authentication layer. -
Push webhook validation covers metadata and internal ranges (
adk-server, featurea2a-v1):validate_webhook_urlalso rejects0.0.0.0/8,169.254.0.0/16,100.64.0.0/10,fc00::/7,fe80::/10, multicast, broadcast,*.localhost, and IPv4-mapped, IPv4-compatible, and NAT64 forms of blocked IPv4 addresses.HttpPushNotificationSender::new()does not follow redirects and bounds each delivery attempt at 10 seconds. -
Seatbelt profiles escape every interpolated path (
adk-sandbox,sandbox-macos): allowed paths are written as SBPL string literals with\,", and control characters escaped. A directory namedw"))(allow network*)(allow file-write* (subpath "/previously closed the literal and granted full network access and writes to/. Paths that are not valid UTF-8 are rejected withPolicyViolation. -
The Seatbelt profile is deny-by-default (
adk-sandbox,sandbox-macos):(allow default)followed(deny default), so every read and mach lookup was allowed —~/.sshand other host secrets were readable from inside the sandbox. The profile now grants only the system runtime (/bin,/sbin,/usr,/System,/Library/Apple,/Library/Frameworks,/opt/homebrew,/private/etcminusmaster.passwdandsudoers, the dyld cache), a fixed set of startup mach services, and the policy's paths.ProcessBackendnow reportsfilesystem_read_isolationon macOS. Programs installed elsewhere, such as a rustup toolchain or Xcode, needallow_read. -
Linux
allow_process_spawn = falseis enforced (adk-sandbox,sandbox-linux): it previously added only--new-session, which does not blockforkorexec. bubblewrap now loads a seccomp program that failsfork,vfork, and non-threadclonewithEPERMandclone3withENOSYSon x86-64 and AArch64; other architectures returnEnforcerFailed.SandboxEnforcer::configure_commandinstalls the program and must be called on the wrapped command. -
LocalUnixSession::exec_commandis hardened (adk-sandbox,workspace): commands run with a cleared environment (onlyPATH,HOME,USER,LOGNAME,LANG,LC_ALL,LC_CTYPE,TZ,TMPDIR, andTERMpass through), stdin from/dev/null, output read while the command runs and capped at 1 MiB per stream, and their own process group, killed on timeout and on exit. Host credentials in the environment previously reached every command, and a timeout killed onlysh. -
DockerClientcontainers drop privileges and network (adk-sandbox,workspace-docker): containers start withcap_drop: ALL,no-new-privileges, a 512-process limit, and network modenone.with_network_modeandwith_pids_limitchange the defaults;GitRepomanifest entries need a network mode other thannone. -
DockerClientno longer builds shell commands from paths (adk-sandbox,workspace-docker): file writes,git clone, andgit checkoutpass paths, URLs, and branch names as arguments. A'in a path previously injected shell commands into the container. Branch names starting with-are rejected. -
Redis memory
delete_usererased other users' memories (adk-memory): the index-keySCANpattern wasmem_idx:{app}:{user}*, with no delimiter after the user id and no glob escaping, so deletingalicealso deleted the index sets ofalice2andalice-admin, and auser_idof*matched every user. Bothdelete_userpatterns now end the user segment with:and escape*,?,[,]and\. -
Redis key delimiter collisions (
adk-memory,adk-session): identifiers were joined with:, which identity validation allows, so appa:bwith usercshared keys with appaand userb:c, and a session id ending in:eventsaliased another session's event set. Key segments are now percent-encoded; the key-format change is listed under Changed. -
LanceDB delete predicate injection (
adk-rag):LanceDBVectorStore::deletespliced ids intoid IN ('…')without escaping, so an id containing'failed the delete and the idx') OR ('1'='1deleted every row. Ids are now typed string literals in a DataFusion expression. -
Firestore tenant isolation (
adk-session, featurefirestore):deleteand every append re-read the session document inside their transaction and apply only when its app, user, and session ID match the request. Deleting another user's session is a no-op, and appending to it fails withsession.not_found.createno longer upserts: an ID already taken in the app fails withsession.already_exists, so a session and its events cannot be taken over, and a new session never inherits events left under its path. App names, user IDs, and session IDs containing/are rejected. -
EncryptedSessionencrypts events and binds ciphertext to the session (adk-session, featureencrypted-session):- Event content, LLM response metadata, actions, and long-running tool IDs are sealed into one envelope per event;
llm_requestand eventprovider_metadataare not persisted. - State values from
createand from eventstate_deltaare encrypted per value, soapp:anduser:values stay in their shared tiers and later writes are encrypted even when the initial state is empty. - Associated data binds each ciphertext to its app, user, session, and state key or event ID; data moved to another session fails with
session.encryption.decrypt_failed. listreturns decrypted state and events instead of ciphertext.- Key rotation re-encrypts state through a state-only event appended with
append_event_for_identityand returns failures, instead of callingcreateover the existing session and discarding the result. append_event(session_id, ..)returnssession.encryption.identity_required; useappend_event_for_identity.- Data in the previous format is rejected with
session.encryption.unencrypted_data;EncryptedSession::with_legacy_migration(true)reads and re-encrypts it.inner()andinto_inner()expose the wrapped service.
- Event content, LLM response metadata, actions, and long-running tool IDs are sealed into one envelope per event;
-
RedisSessionConfigredacts credentials inDebug(adk-session, featureredis): the URL password and*password*query parameters print as[REDACTED]. -
UI sign-in warning (
adk-cli): the account name no longer reaches stdout when the launcher warns about UI sign-in. -
LoopAgentwith zero iterations (adk-agent):with_max_iterations(0)runs no iterations and completes after its callbacks, instead of panicking onu32underflow in debug builds and loopingu32::MAXtimes in release builds. -
output_keywritten before schema validation (adk-agent): withoutput_schemaset,state[output_key]receives only a response that passes validation. Rejected attempts, including every attempt of a run that exhaustsoutput_max_retries, are no longer persisted. -
After-tool callbacks without shared state (
adk-agent):CallbackContext::shared_state()in after-tool callbacks returns theParallelAgentshared state, matching before-tool callbacks. -
Two tasks leaked per run with a global cancellation token (
adk-runner): each run derives a child ofRunnerConfig::cancellation_tokeninstead of spawning two watcher tasks that never completed. Cancelling the global token still stops every in-flight run, andRunner::interruptcancels only its target. -
Lost wakeup in
SharedState::wait_for_key(adk-core): a waiter registers for notification before it reads the key, so aset_sharedthat lands between the read and the wait wakes it instead of leaving it to time out. -
adk-devtools: file tools refuse dangling symlinks, and
globstays inside the workspace.Workspace::resolvestepped past a path component that failed to canonicalize, so a dangling symlink passed the containment check andwrite_filefollowed it, creating its target outside the root — a repository containinglink -> ~/.zshenvletwrite_file("link", ..)create~/.zshenv. Dangling and looping symlinks are now refused wherever they point, andwrite_file/edit_filere-check the canonical parent before the open and open withO_NOFOLLOWon Unix.globjoined the pattern onto the root unchecked, so../*and symlinked directories listed host paths; it now refuses..and absolute patterns, escapes the root path, and drops every match that resolves outside the root. -
adk-devtools:
bashoutput is capped as it is read, and a cancelled call kills the command. Output was collected in full and cut afterwards withString::truncate, which bounded only the report and panicked when the cap fell inside a multi-byte character. Each stream now keeps at mostmax_output_byteswhile reading, drains and discards the excess so the command still completes, and cuts on a character boundary. Dropping the tool call left the command and its descendants running; the process group is now killed on drop, as on timeout. -
adk-tool:
bigquery_execute_sqlno longer runs writes while reporting itself read-only. The tool returnedis_read_only() == truebut executed any SQL, so parallel dispatch could run aDROP TABLEalongside other calls. In read-only mode, now the default, it accepts a singleSELECTorWITHstatement (comments and string contents are ignored, multiple statements are refused), and a BigQuery dry run must classify the query asSELECTbefore it runs.is_read_only()follows the configuration. -
adk-tool: BigQuery and Spanner service-account keys stay in memory. Keys resolved through
from_secretwere written to the shared temp directory with default permissions and removed only on normal completion, so a cancelled call left the key on disk. They are now parsed in memory and never written. A malformed key secret is no longer quoted back in the tool error. -
adk-browser: navigation accepts only web URLs, and arbitrary JavaScript is opt-in.
browser_navigateonly checked that the URL parsed, so a model could openfile:///etc/passwd,javascript:anddata:URLs, orchrome://pages;browser_new_tabandbrowser_new_windowdid not check at all. All three now accept onlyhttpandhttps(DEFAULT_ALLOWED_SCHEMES) unlessBrowserToolset::with_allowed_schemeswidens the list, and refuse other schemes before the browser is touched.browser_evaluate_jswas part of the default toolset and theFullandScrapingprofiles; it is now excluded from all of them. -
adk-code: Rust executors no longer expose the host environment.
RustExecutorandRustSandboxExecutorranrustcwith the inherited environment, soenv!("OPENAI_API_KEY")oroption_env!in model-written code embedded host credentials in the binary, andRustSandboxExecutorran the binary with the full host environment.rustcnow starts from a cleared environment plus theProcessBackend::toolchain_envallowlist, withkill_on_dropand the request timeout.RustSandboxExecutorruns the binary with only the variables named by the request'sEnvironmentPolicyand now reportsenforce_environment_policy: true.RustExecutorcompiles inside theSandboxBackendwhen the backend confines filesystem reads, soinclude_str!is confined like the binary; otherwise it compiles on the host. -
adk-code:
validate_policyrejects environment policies the backend cannot enforce.EnvironmentPolicy::Nonepassed validation on backends reportingenforce_environment_policy: false, although those backends pass the host environment through. Such backends now reject every request withExecutionError::UnsupportedPolicy. -
adk-code:
EmbeddedJsExecutorenforces its timeout. The timeout was checked only after evaluation returned, sowhile (true) {}never returned and pinned a blocking-pool thread. Evaluation runs on a dedicated thread,executereturnsExecutionStatus::Timeoutat the deadline, and Boa runtime limits (10,000,000 loop iterations per call frame, call depth 512, VM stack 10,240 values) stop the interpreter with an uncatchable error. -
adk-code:
DockerExecutorhonours the request's sandbox policy. Network, bind mounts, and environment came fromDockerConfigalone while the backend reported network, filesystem, and environment enforcement. Each request is now checked withDockerConfig::validate_sandbox_policy, and a request stricter than the container — for exampleSandboxPolicy::strict_rust()against awith_network()container — fails withExecutionError::UnsupportedPolicy. Variables named byEnvironmentPolicy::AllowListare passed from the host to the exec. -
Caller-aware A2A handlers (
adk-server): the built-in routers mounthandle_jsonrpc_for_callerandhandle_jsonrpc_stream_for_caller.handle_jsonrpcandhandle_jsonrpc_streamare deprecated because they ignore the authenticated caller.tasks/cancelfor an unknown task answersTask not found(-32603), astasks/getdoes, instead of a canceled status. -
v1
jsonrpc_handlertakes anAuthenticatedCaller(adk-server, featurea2a-v1): a direct call passes the extractor as the second argument;post(jsonrpc_handler)mounts are unchanged. -
CronJobStore::enqueue_runreturnsbool(adk-server, featurebackground):falsewhen the job's queue is full or the job does not exist. -
AP2 adapter requires verifiers (
adk-payments,ap2):Ap2Adapter::newconfigures no authorization verifier, so merchant, intent, and payment authorizations fail withAp2Error::AuthorizationVerifierNotConfigureduntilwith_merchant_authorization_verifierandwith_user_authorization_verifierare set.allow_unverified_authorizations()restores the presence-only checks for local development and logs a warning. -
AcpVerificationConfig::strict()enforces signatures and timestamps (adk-payments,acp): the strict profile requires a verifiedSignature, aTimestampwithin five minutes, and anIdempotency-Keyon POST, and routerbuild()fails untilwith_signature_verifieris set. The previous idempotency-only profile ispermissive().with_idempotency_mode(IdempotencyMode::RequireForPost). -
Amounts follow the currency minor unit (
adk-payments): AP2 amounts and ACP delegated-payment allowances use the ISO 4217 minor-unit scale (JPY0,KWD3) instead of a fixed scale of 2.AmountThresholdGuardrail::newthresholds are minor units at that scale;with_currencybinds them to one currency and denies the others. -
AP2 human-not-present authority (
adk-payments,ap2): an intent needs merchant or SKU constraints;requires_refundabilityalone no longer authorizes autonomous payment. -
AP2 authorizations are verified, not just present (
adk-payments,ap2): the default verifiers accepted any non-emptymerchant_authorizationoruser_authorization, so a forged value reachedexecute_payment. The adapter now rejects every authorization until cryptographic verifiers are configured. A merchant verifier that setsVERIFIED_MERCHANT_NAME_CLAIMmakes the adapter reject carts whosemerchant_namediffers from the verified signer. -
AP2 payment mandates are single-use (
adk-payments,ap2): the adapter consumes each mandate in aPaymentMandateLedgerbefore execution and records it on the transaction. A resubmitted mandate returnsAp2Error::PaymentMandateReplayed, a second mandate for a paid transaction returnsAp2Error::TransactionAlreadyPaid, andap2_payment_mandate_idis never rebound by later receipts.with_payment_mandate_ledgerplugs in shared storage for multi-instance deployments. -
ACP signature enforcement without a verifier (
adk-payments,acp):require_signature(true)withoutwith_signature_verifieronly checked that the header existed. Routerbuild()now fails for that profile, and the request verifier rejects every request if reached. -
Card data in delegated-payment selections (
adk-payments,acp):PaymentMethodSelection.extensionscarried the full card, including PAN, CVC, and cryptogram. It now keeps only type, funding type, brand, last four digits, wallet type, and the virtual flag. -
Team relationship approval honours
Deny(adk-agent): a tool whose confirmation the invocation context requires — a teamDelegaterelationship withRelationshipApprovalPolicy::Required— no longer executes when the static or handler decision for the call isDeny. The tool executor applies the union of the agent'sToolConfirmationPolicyandInvocationContext::requires_tool_confirmation, so a denied delegate call returns the same denied function response as the agent-policy path. -
Redis key segments are percent-encoded (
adk-memory,adk-session):%becomes%25and:becomes%3Ain every app, user, session and project segment. Identifiers containing neither character keep their existing keys. Data stored under an identifier containing:or%must be renamed to the encoded key, for example memory entries of appa:b, userc, sessionsmove frommem:a:b:c:stomem:a%3Ab:c:s. Inadk-session, an app namedapp_state,user_state,sessions_idxorsession_lookupalso has its first byte encoded in session and event keys (user_state:app:alice→%75ser_state:app:alice). -
Neo4j memory entry ids (
adk-memory): new entries use the scoped id formats:{app}:{user}:{scope}:{session}:{index}ord:{app}:{user}:{scope}:{uuid}. Entries written earlier keep their ids, so re-ingesting a session stored before this release adds a second copy; calldelete_sessionfirst to avoid it. -
Anthropic API keys in
Debugoutput (adk-anthropic):Anthropic,ManagedAgentsClient, andFilesClientredact the API key inDebugoutput, and thex-api-keyand bearerAuthorizationheader values are marked sensitive. -
Provider API keys in
DebugandSerializeoutput (adk-model):OpenAIConfig,AzureConfig,OpenAIResponsesConfig,OpenAICompatibleConfig,AnthropicConfig,GroqConfig,DeepSeekConfig,OpenRouterConfig, andAzureAIConfigredactapi_keyinDebugoutput and omit it when serialized. A config deserialized withoutapi_keygets an empty key. -
Model clients no longer follow HTTP redirects (
adk-anthropic,adk-gemini,adk-model): the Anthropic client, the Gemini Studio and Vertex backends, and the OpenAI-compatible, Azure OpenAI, OpenAI Responses (generation), Azure AI Inference and DeepSeek clients return a 3xx response as an error instead of re-sending the request and its credentials to another host. Proxies configured throughHTTP_PROXY/HTTPS_PROXYare unaffected. The Groq, OpenRouter and Conversations clients still follow redirects. -
Direct MCP tool calls follow ADK's MRTR input policy (
adk-tool):McpToolset::call_tool_valuewent through rmcp'scall_tool, which answered a server's MRTR sampling requests with the configured sampling handler. Every toolset onAdkClientHandler, including one passed toMcpToolset::new, now applies ADK's input policy, which rejects MRTR sampling and roots. On any other client handler, MRTR sampling and roots requests reach the handler only when it declared those capabilities. -
rustls upgraded to 0.23.45 (workspace and every standalone example): resolves RUSTSEC-2026-0285. No API change; the lockfiles move together so
cargo check --lockedkeeps passing for each example.
-
Gemini Live spoken language (
adk-realtime):RealtimeConfig::with_languagesends a BCP-47 tag asgenerationConfig.speechConfig.languageCodefor AI Studio and Vertex AI Live, so transcription does not switch languages on short or accented turns. Native-audio output models ignore it. -
Live ACP end-to-end example (
examples/acp_openai_e2e): an OpenAI-backed coordinator delegates over ACP stdio to an OpenAI-backed ADK agent served byAcpServer, and asserts environment passing, one-shot and persistent sessions, and the permission bridge. -
Automatic progressive-skill guidance (
adk-skill,adk-agent):SkillToolsetnow adds guidance for its currently available tools to each model request through the newToolset::process_llm_requesthook. Guidance is also applied to invocation-scoped toolsets. -
AuthenticatedCallerextractor (adk-server): reads the request'sRequestContextfrom the extensions the auth layer sets, and yieldsNonewhen no authentication ran. -
A2aTaskRetention(adk-server): bounds finished A2A task records — the newest 1,000 for at most one hour by default — configured withServerBuilder::with_a2a_task_retentionorA2aController::with_task_retention. -
RequestHandler::for_caller(adk-server, featurea2a-v1): returns aCallerScopethat runs every v1 operation for one caller. -
ProcessBackend::toolchain_envis public (adk-sandbox): callers that invokerustcthemselves apply the same minimal toolchain allowlist afterenv_clear(). -
DockerConfig::validate_sandbox_policy(adk-code): checks whether a container created from aDockerConfigsatisfies aSandboxPolicy. -
Plain-HTTP base URLs for internal gateways (
adk-anthropic,adk-model):Anthropic::allow_insecure_http()andAnthropicConfig::allow_insecure_http()accept anhttp://base URL on a non-loopback host and log a warning naming the host. OnAnthropic, call it beforewith_base_url, which validates when called. A base URL fromANTHROPIC_BASE_URLis acknowledged byANTHROPIC_ALLOW_INSECURE_HTTP=1instead. Without the opt-in, the validation error now names it (#700). -
Host-managed browser sessions (
adk-browser):BrowserConfig::require_explicit_startmakes tools wait for the host to callBrowserSession::start(), which now keeps a live session and replaces one that no longer responds;chrome_optionpasses Chrome options such asbinaryandprefs. -
EventTextDeltas(adk-core): converts complete response snapshots back into text and thinking deltas for append-only output such as terminals and chat streams. -
McpToolset::cancel_pending_tasks(adk-tool): cancels remote MCP tasks whose tool-call future was dropped, and in-flight ones, and releases each task once the server reports it terminal or no longer knows it.McpServerManagerruns it within the grace period when it stops a server. -
OpenCode Go and Zen (
adk-model): theopencodefeature addsOpenCodeClient, which routes documented models through Chat Completions, Responses, Anthropic Messages or GeminigenerateContentand sends the application'sUser-Agentandx-opencode-sessionheaders with every request. Unknown models take an explicitOpenCodeApi. -
Default HTTP headers on model clients (
adk-anthropic,adk-model):Anthropic::with_default_headers,AnthropicClient::with_default_headers,OpenAICompatible::with_default_headersandOpenAIResponsesClient::with_default_headersadd headers to every request. Authentication headers and disabled redirects are kept, and the Responses client keeps its request rewriting and adapter in either call order. -
OrcaRouter preset (
adk-model):OpenAICompatibleConfig::orcarouter()targetshttps://api.orcarouter.ai/v1through the shared OpenAI-compatible client and needs only theopenaifeature.
-
wasmtime 48 (
adk-sandbox, featurewasm):wasmtimeandwasmtime-wasimove from 46 to 48.0.3 together. Noadk-sandboxAPI changes; wasmtime types are not part of its public API. -
Supported versions (
SECURITY.md): security fixes target the 2.2.x line and 2.1.x; 2.0.x and 1.x no longer receive updates. -
Model::Gemini3ProPreviewis deprecated (adk-gemini): thegemini-3-pro-previewmodel is retired. UseModel::Gemini31ProPreview. -
#[task]calls shared one cached result (adk-rust-macros,adk-graph): the execution-log key was the task's name alone and the log is consulted within the run, so a task called in a loop returned the first call's result for every later call. Each call is now keyed by its ordinal in the run and a hash of its serializable arguments; seeadk_graph::functional::task_call_id. The wrapper also uses the context parameter's real name instead of assumingctx. -
Dynamic pauses dropped completed siblings' successors (
adk-graph): a dynamic or tool-confirmation pause saved only the paused node, so the successors andgototargets of nodes that completed in the same super-step never ran after the resume. The saved frontier is now those successors plus the paused node, ininvokeand in every stream mode. -
Fan-in arrivals lost across a pause or crash (
adk-graph): a join's recorded arrivals lived only in the executor, so a join whose first predecessor finished before a pause never ran. Arrivals and wait start times are stored in checkpoint metadata (adk.graph.fanIn, versioned); a checkpoint without the key loads with no arrivals, as before.CompiledGraph::update_statenow keeps the arrivals, the answered gate, the retry budget, and the child ledger. -
GraphAgentanswered every later turn with the first (adk-graph): with a checkpointer, the session's finished thread was reloaded with an empty frontier, so no node ran and the old output came back. A turn on a finished thread now runs the graph again from its entry nodes, starting from the previous turn's state; a paused thread still resumes. The recursion limit counts per turn. -
Node-cache hits broke super-step isolation (
adk-graph,node-cache): a hit's updates were applied before uncached siblings read the state, and a miss in the same step stored its result under a key computed from that changed state, which no lookup matched. Hits now join the step's update batch and every key is computed from the pre-step state. -
Action nodes trusted interpolated paths and URLs (
adk-graph,action): file nodes are confined to allowed roots, rejecting..and symbolic-link escapes; HTTP nodes reject URLs outside anHttpActionPolicy(defaulthttpsandhttp), re-check every redirect, and log URLs without query strings or credentials. -
Unimplemented action nodes pointed at feature flags (
adk-graph,action): database, email, and JavaScript/TypeScript code nodes now report that the node type is not implemented, sinceaction-db,action-email, andaction-codecompile placeholders only. -
Failed retry-budget checkpoint was silent (
adk-graph): when a super-step fails and its checkpoint cannot be saved, the executor logs a warning with the error instead of discarding it. -
#[task]execution-log keys (adk-rust-macros): keys change from{task}to{task}#{ordinal}or{task}#{ordinal}:{arg_hash}, so a workflow resumed from a log written before this release runs its tasks again. Every#[task]parameter must be a plain identifier. -
File action confinement (
adk-graph,action): file nodes default to the current working directory as their only root;ActionNodeExecutor::with_file_rootsandaction::file::execute_file_inname others. Reported paths are the resolved absolute paths, and listing skips symbolic links. -
HTTP action policy (
adk-graph,action-http): newHttpActionPolicy,ActionNodeExecutor::with_http_policy, andaction::http::execute_http_with_policy. Redirects are limited to 5 by default (previously reqwest's 10). -
Node-cache store lifetime (
adk-graph,node-cache): the in-memory store belongs to the compiled graph and is shared by every run, so an identical later run hits; previously each run started with an empty store. -
BigQueryToolsetis read-only by default (adk-tool, featurebigquery):bigquery_execute_sqlrefuses DML, DDL, and scripts. Restore the previous behavior withBigQueryToolset::with_project("my-project").with_read_only(false); the tool then reports itself as mutating. -
browser_evaluate_jsis opt-in, and navigation is limited tohttp/https(adk-browser):BrowserToolset::new,with_pool, and everyBrowserProfileleave outbrowser_evaluate_js(Fullnow has 45 tools,Scraping13); the scroll, hover, and alert helpers stay. Opt in withBrowserToolset::new(browser).with_evaluate_js(true);.with_js(true)also enables it. Allow other URL schemes with.with_allowed_schemes(["https", "file"]). -
a2a-protocol-typesupgraded from 0.5 to 0.12.1 (adk-server, featurea2a-v1): the pin was five minor versions behind. Two changes in the dependency's 0.7.0 release reach this crate'sa2a-v1surface.JsonRpcRequest.idis now the three-stateJsonRpcRequestId(Absent/Null/Value), so an explicit"id": nullis treated as the call JSON-RPC 2.0 says it is rather than collapsing into a notification; responses are unchanged, becauseto_response_id()maps bothAbsentandNullto theNonethe previousOption<Value>produced.TaskPushNotificationConfig::task_idandAuthenticationInfo.credentialsare nowOption<String>, matching the canonical schema that previously rejected valid cross-SDK payloads at parse time; a standalone push-config create with no task id is now refused with a structured invalid-params error, and a push notification whose config carries no credentials now omits theAuthorizationheader rather than sending an empty or placeholder bearer token. -
Anthropic streams must end with
message_stop(adk-model): a stream that closes withoutmessage_stop, or whose message has no stop reason, now fails instead of ending quietly. Anthropic-compatible proxies that drop the final event are affected. -
Azure OpenAI streams on the shared Chat Completions transport (
adk-model):AzureOpenAIClientnow streams when asked instead of always making a non-streaming call, and acceptswith_reasoning_effort. Error codes staymodel.azure_openai.*, including the status-specific ones. -
GeminiBuilder::with_http_clientapplies to AI Studio (adk-gemini): the configuredreqwest::ClientBuilderwas previously ignored on the AI Studio path and now carries its proxy, timeout and default-header settings into every request. The redirect policy is always replaced withPolicy::none(), and a client build failure returnsError::PerformRequestNewinstead of panicking. -
Gemini grounding is no longer appended to the answer (
adk-model): the "Searched" and "Sources" text that followed Google Search grounded responses is gone. The queries, sources and supports remain inprovider_metadata(webSearchQueries,groundingChunks,groundingSupports) for applications that display them. -
Model-facing MCP tools forward MRTR input to a custom client handler (
adk-tool): onMcpToolset::newwith a handler other thanAdkClientHandler, an MRTR input request from a tool call returned an error. It now reaches the handler, whose defaultcreate_elicitationdeclines. -
Local quality gates run on the pinned toolchain (
lefthook.yml,scripts/cargo-pinned.sh): the pre-commit and pre-push hooks and the examples gate route throughscripts/cargo-pinned.sh, which resolves therust-toolchain.tomlpin via rustup and refuses to run on any other cargo, so a shadowing toolchain cannot lint with the wrong compiler.scripts/setup-dev.shreports the cargo the gates resolve and installs the ALSA headersexamples/desktop_audioneeds on Linux. -
Streams end with a complete snapshot (
adk-model): Anthropic streaming, and OpenAI Responses streaming with commentary, emit a final response carrying the full message withprovider_metadata.content_completeset totrue. Code that consumesLlmstreams directly replaces the earlier deltas with that response instead of appending it. -
Token usage counts cache and thought tokens (
adk-model): Anthropicprompt_token_countnow includes cache-read and cache-creation input tokens, and Geminicandidates_token_countnow includes thought tokens;provider_usagecarries the provider's raw usage.adk-eval'sCostTrackerpricesprompt_token_countat the full input rate, so cached Anthropic input is counted at that rate, and Claude through Bedrock still reportsprompt_token_countwithout cache tokens, so the two paths differ for the same request. -
Vertex requests are no longer replayed over REST (
adk-gemini,adk-model): a gRPC transport failure on a non-streaming VertexgenerateContentcall is returned instead of being re-sent over REST, because the first request may already have reached the model.adk-modelmaps those failures toErrorCategory::Unavailable, soRetryConfigdecides whether to retry.
-
acp_full_protocolpermission tests (examples/acp_full_protocol): the example readstool_confirmation_decisionsby function-call ID, matching the key the ACP server populates, so approval and denial resume correctly. -
OpenAI reasoning history (
adk-model):Part::Thinkingis excluded from visible user, assistant, and system message content. By default, subsequent model requests omit Thinking entirely instead of replaying it as visible content; stored history is unchanged. Compatible backends can opt in withwith_reasoning_replay(true)to sendreasoning_content, or usewith_reasoning_replay_field(ReasoningReplayField::Reasoning)to sendreasoning. Replay emits only the selected field; standard OpenAI and Azure requests omit both fields by default. -
Open Responses argument identity (
adk-model): conflicting item or call identities terminate compatible streams before cached arguments can cross tool calls. Blank completed snapshots restore matching streamed arguments without replacing explicit nonblank values. -
Overlapping and interrupted tool history (
adk-runner): model requests retain actual tool results from overlapping turns and omit unresolved calls from earlier turns without fabricating interruption results. Persisted events remain unchanged. -
OpenAI Responses stream completion (
adk-model): completion events retain model errors and usage metadata. Open Responses mode ignores empty SSE data frames, restores missing arguments by tool identity or output index, and keeps streamed arguments when a terminal snapshot is blank. -
OpenAI-compatible structured tool arguments (
adk-model): Chat Completions streaming now preserves structured JSONfunction.argumentsemitted by compatible intermediaries, including repeated and empty snapshot chunks, instead of converting the tool call to empty arguments. -
OpenAI Responses web search (
adk-tool): the stable built-in tool now serializes asweb_search, matching the OpenAI Responses API. The explicit preview variant remainsweb_search_preview_2025_03_11. URL annotations from response text are preserved as ADK citation metadata. Open Responses mode now also accepts compatible endpoints that omit output-message IDs, statuses, or emptyoutput_text.annotations, and reconstructs final function arguments from their streaming events when the completed response omits them. -
OpenAI-compatible usage metadata (
adk-model): Chat Completions streaming and non-streaming responses now use one normalization path, preserve the complete provider-nativeusageobject inprovider_usage, and project reported cache-write tokens alongside cache reads. -
OpenAI tool request stability (
adk-model): Chat Completions and Responses requests now serialize function tools in deterministic name order, preserving identical cacheable prefixes across repeated executions. -
DeepSeek streaming emitted every response twice (
adk-model): the terminal SSE chunk replayed the accumulated text and reasoning buffers on top of the deltas it had already yielded as partial chunks. Consumers accumulate the parts of every chunk —LlmAgentdoes, to build conversation history — so they saw the whole response twice. Withoutput_schemaset that is{...}{...}, which fails JSON parsing ("Response is not valid JSON: trailing characters") and burns every schema-validation retry, so structured output was unusable on DeepSeek. The terminal chunk now contributes only its owndelta, plus reasoning that was buffered but never streamed (thinking disabled), and adelta.contentarriving in the same chunk asfinish_reasonis no longer dropped. -
Rust sandbox and container stdin delivery (
adk-code):RustSandboxExecutorandContainerCommandExecutorwrite stdin concurrently with draining stdout and stderr, inside the execution timeout. A program that stopped reading stdin, or filled an output pipe first, previously blocked the caller indefinitely. Captured output is capped while it is read. -
A2A task store hygiene (
adk-server): finished task records are evicted by count and age instead of accumulating for the life of the process. Amessage/sendthat reuses thetaskIdof a running task, or of another caller's task, is rejected with JSON-RPC error-32602instead of replacing the running task's registration and its abort handle. A task is registered before it is spawned, so a fast failure cannot leave a permanentworkingentry. A failed task's stored status message carries the same sanitized text as the streaming error. -
Cron pause, queue depth, and execution count (
adk-server, featurebackground): resuming a paused job skips the occurrences missed while paused instead of claiming them one per scheduler tick. TheQueuepolicy holds at mostDEFAULT_MAX_QUEUE_DEPTH(100) runs per job, configurable withCronState::with_max_queue_depth, and skips an occurrence with a warning when full.executionCountreports the runs the scheduler started. -
A2aServerBuilder::bind_addrrustdoc (adk-server): states the actual default,127.0.0.1:8080. -
AP2 amount parsing (
adk-payments,ap2): the sign comes from the decimal string, so-0.50is-50minor units rather than+50, and exponent notation parses exactly. Every amount in a cart shares one scale, so mixed-precision items no longer produce a spuriousap2_unallocated_delta. Cart sums use checked arithmetic; overflow, more than 18 fraction digits, and mixed currencies returnAp2Error::InvalidAmountinstead of panicking or wrapping. -
Scale-aware amount guardrail (
adk-payments):AmountThresholdGuardrailcompares totals with thresholds by value across scales and denies when the two cannot be aligned. -
Seatbelt domain rules fail closed (
adk-sandbox,sandbox-macos): Seatbelt accepts only*orlocalhostas a network host, soallow_domainrules produced a profilesandbox-execrefused to parse and nothing ran. They are now ignored with atracing::warnand all network access is blocked, as on Linux. TheNetworkRuledocumentation no longer claims macOS enforces them. -
ProcessBackendstdin cannot hang an execution (adk-sandbox): stdin is written concurrently with the output reads and inside the timeout. Writing it first deadlocked any child that filled its output pipe before reading stdin, and a child that never read stdin blocked the backend past the timeout. -
ProcessBackendbackground jobs end with the execution (adk-sandbox): the process group is killed when the main process exits, not only on timeout, sonohup … &descendants no longer outlive the call or hold its pipes open. -
ProcessBackendkeeps enforcer configuration (adk-sandbox): the wrapped program is resolved beforeconfigure_commandruns. Rebuilding the command afterwards discarded what the enforcer attached to it. -
Sandboxed
ProcessBackendexecutions run in their own scratch directory (adk-sandbox): under an enforcer, the per-execution directory holding the source file and compiler output is the working directory and is granted read-write, so Python, JavaScript, and Rust run under deny-default profiles. -
LocalUnixSessionlarge output (adk-sandbox,workspace): output over 64 KiB deadlocked the command, which was then reported astimed_out. -
SandboxToolclampstimeout_secs(adk-sandbox): values outside the schema's 1–300 second range are clamped instead of passed to the backend, and negative or fractional values no longer fall back to the 30-second default. -
WasmBackendenforces memory without a request limit (adk-sandbox,wasm):memory_limit_mb: NoneappliesWasmBackend::DEFAULT_MEMORY_LIMIT_MB(256 MiB) and modules are held to one linear memory, matching thememory: truecapability. Previously no limit applied. -
DockerSessionreads binary files intact and kills timed-out commands (adk-sandbox,workspace-docker):read_filereturns raw bytes instead of lossily decoded UTF-8, and a command that exceeds its timeout is killed inside the container with its descendants instead of running on. -
Neo4j memory entry ids (
adk-memory):MemoryEntry.idcarries a global uniqueness constraint but was{session_id}_{i}orentry_{millis}, so a secondadd_sessionfor a session id failed, as did the same session id under another tenant, and twoadd_entry_to_projectcalls in one millisecond collided. Ids are now scoped by app, user, project and session, direct entries use a UUID, andadd_sessionmerges on the id, so re-ingesting a session updates it. -
Neo4j vector search returned nothing for most tenants (
adk-memory): search took the global toplimitfrom the vector index, then filtered by app, user and project. It now requestslimit × 10candidates, capped atDEFAULT_VECTOR_CANDIDATE_CAP(1000) and configurable withNeo4jMemoryService::with_vector_candidate_cap, filters them, then applieslimit. A tenant whose entries all rank below the cap still gets no vector results. -
SQLite memory search failed on ordinary text (
adk-memory):search,delete_entriesanddelete_entries_in_projectbound the raw query to FTS5MATCH, sowhat's my name?raised an FTS5 syntax error. Each word is now a quoted phrase, bareAND/OR/NOT/NEARare dropped, and an entry matches when it contains every word, as withplainto_tsqueryin the Postgres backend. -
Chunkers looped forever on multibyte text (
adk-rag): whenchunk_size - chunk_overlapwas narrower than a character,FixedSizeChunker,RecursiveChunkerandMarkdownChunkerstopped advancing and grew memory until the process ran out. Every chunk now advances at least one character. The newtry_newconstructors rejectchunk_size == 0andchunk_overlap >= chunk_sizewithRagError::ConfigError;newnormalises such pairs with a warning instead of stopping after the first chunk. Docs now state that sizes are bytes of UTF-8. -
LanceDB
upsertappended duplicates (adk-rag):upsertcalledtable.add, so re-ingesting a document duplicated its chunks. It now merge-inserts onid, keeping the last duplicate in a batch, and an embedding whose length differs from the collection's dimensions returnsRagError::VectorStoreErrorinstead of panicking. -
RAG ingest dropped chunks without embeddings (
adk-rag):RagPipeline::ingestzipped chunks with embeddings, so a provider returning fewer vectors than chunks silently stored fewer chunks. A count mismatch now fails withRagError::PipelineError. -
Opt-in total stream bound (
adk-anthropic):Anthropic::with_stream_timeoutbounds the whole duration of a streaming request. Streams have no total bound by default. -
Shared HTTP error helpers (
adk-model):retry::category_for_status_code,retry::parse_retry_after, andretry::with_retry_aftergive every provider client the same status mapping andRetry-Afterhandling. -
Long Anthropic streams (
adk-anthropic): streaming requests run on an HTTP client without a total request timeout, so a generation longer than 60 seconds is no longer cut off mid-body.with_timeoutstill bounds non-streaming requests and the wait for streaming response headers. -
Anthropic stream inactivity timeout (
adk-anthropic): the 30-second timeout now measures only the wait on the server, so a stalled server fails the stream and a consumer that pauses between polls does not. A timeout, a transport error, or a buffer overflow ends the stream after oneErrinstead of repeating the error. -
Mid-stream Anthropic
errorevents (adk-anthropic):AccumulatingStreamyields an SSEerrorevent (for exampleoverloaded_error) asErrand sends the error, not the partial message, through its channel.From<ApiError> for Errormapsoverloaded_errorandrate_limit_errorto the retryableServiceUnavailableandRateLimitvariants. -
Unknown Anthropic SSE event types (
adk-anthropic): an unrecognized event type is logged atdebugand surfaced asMessageStreamEvent::Pinginstead of failing the stream. -
Anthropic request size validation (
adk-anthropic):MessageCreateParams::validatemeasures text bytes in place instead ofDebug-formatting the payload. Base64 images are held to the API's 5 MB limit and PDFs to 32 MB, with 32 MB of text plus base64 media per request, so an 800 KB JPEG or a typical PDF is no longer rejected by the 1 MB text cap. -
Anthropic server tool input (
adk-anthropic):AccumulatingStreamappliesinput_json_deltafragments toserver_tool_useblocks, which previously kept the empty{}input fromcontent_block_start. -
Text tool-call false positives (
adk-model): text-encoded tool calls are parsed only when the request declares the named tool, so a```jsonexample with a"name"key stays text. The DeepSeek fence format requires anargumentsorparametersobject instead of defaulting to{}, and text after the closing fence is kept.parse_declared_tool_callsandToolCallBuffer::for_declared_toolsexpose the filtered parse. -
Transient upstream errors are retried (
adk-model): HTTP 500, 502, 503, and 504 from OpenAI-compatible, Azure OpenAI, Azure AI, Anthropic, DeepSeek, and Groq map toErrorCategory::Unavailablewith the upstream status, soexecute_with_retryretries them. ARetry-Afterheader becomes the error's retry hint for every one of these providers. -
Retry-Aftercapped atmax_delay(adk-model): a server-provided retry delay no longer exceedsRetryConfig::max_delay, soRetry-After: 3600cannot stall an agent run for an hour. -
Stable tool order for cached providers (
adk-model): Anthropic and Bedrock send tool definitions sorted by name, so the prompt-cache prefix matches across requests. -
Malformed streamed tool arguments (
adk-model): Anthropic, Bedrock, DeepSeek, Groq, and Azure AI fail withmodel.<provider>.invalid_tool_argumentsinstead of calling the tool with{}ornull. A zero-argument Bedrock call receives{}. -
Mid-stream Anthropic errors in
AnthropicClient(adk-model): an SSEerrorevent fails the stream with the category of the equivalent HTTP error (overloaded_error→Unavailable,rate_limit_error→RateLimited) instead of being logged atdebug. -
Firestore app-state path (
adk-session, featurefirestore): app state moves to{root}/{app_name}/app_state/current. The previous{root}/{app_name}/app_statepath is a collection path, so everycreateandappend_eventfailed.app_state_pathanduser_state_pathnow return the document paths the backend writes. -
Undecodable stored events are logged (
adk-session): the SQLite, PostgreSQL, Redis, and Firestore backends emit awarn!with the session and event ID for each stored event that fails to deserialize, and still return the remaining events. -
Agent event fidelity (
adk-agent,adk-runner,adk-cli,adk-graph): preserve citations and complete terminal content, continue provider-native paused turns, and emit individual tool results while sibling approvals are pending. An approval failure ends the turn with an error once running tools finish, approvals are requested one at a time, and tool-start events no longer end responses. Built-in streaming consumers render complete snapshots once, and later agents in the same invocation see each streamed response once. -
Direct MCP tool calls keep the task lifecycle (
adk-tool):McpToolset::call_tool_valuepolls server-materialized tasks, answers MRTR and in-task input through the connection's handler, and restores resource subscriptions after a reconnect, as model-facing calls do. A task returned while task support is disabled is cancelled, and the error namesMcpToolset::with_task_support(McpTaskConfig::enabled()). Each input round is capped at 64 requests, a pending elicitation no longer holds the connection lock, and an error result without a text block reports its structured content. -
MCP tool schemas are bounded at discovery (
adk-tool):McpToolsetmeasures each tool's input and output schema againstMcpSchemaLimits(256 KiB and 10 000 JSON values by default) before copying or logging it. A tool over a limit is skipped with a warning naming the toolset and the tool, and discovery continues with the remaining tools. Accepted tools logschema.bytesandschema.nodesatdebuginstead of the full schema.McpToolset::with_schema_limitsandMcpServerManager::with_schema_limitsconfigure the limits, and managed toolsets are named after their server ID. -
Native provider fidelity (
adk-model,adk-anthropic,adk-gemini): retain streamed Unicode, tool images, PDF inputs, native search/reasoning blocks and usage; preserve commentary for application rendering and honor explicit request/retry configuration. -
Windows: the Rust sandbox links again, Docker policy accepts rooted mounts, and the devtools
globtool finds files (adk-code,adk-devtools):rustcis pinned torust-lldas inadk-sandbox, so Git's GNUlink.exeon PATH no longer intercepts the link step; bind-mount containment checks for a rooted rather than an absolute path, so Linux-container mounts such as/srv/datavalidate on a Windows host; andglobwalks the workspace instead of expanding a canonical root whose\\?\prefix the glob crate read as a wildcard.
Full changelog: https://github.com/zavora-ai/adk-rust/compare/v2.2.0...v2.3.0
v2.2.0 — Agent Platform, completed
API-compatible minor release. All 43 crates are on crates.io.
adk-rust = "2.2.0"
Wave 4 platform services complete the Gemini Enterprise Agent Platform consumption path. Every one is opt-in, composable with any preset, and appended to gemini-agent-platform.
| Feature | Crate | What it gives you |
|---|---|---|
vertex-eval |
adk-eval | Gen AI Evaluation Service bridge — evaluateInstances and a service-backed judge |
vertex-rag |
adk-rag | Vertex AI RAG Engine retrieval, plus grounding through Tool::Retrieval |
agent-retrieval |
adk-rag | Agent Retrieval / Vector Search 2.0 VectorStore with hybrid RRF |
vertex-agent-registry |
adk-tool, adk-cli | Agent Registry registration, discovery, and AgentSearchTool |
vertex-skill-registry |
adk-skill, adk-cli | Skill Registry consumption and remote skill loading |
vertex-remote-engine |
adk-server | RemoteReasoningEngineAgent — call deployed Agent Engine agents as sub-agents |
skills-progressive-disclosure |
adk-skill | SkillToolset — discover a skill index first, load bodies and resources on demand |
Graph workflows gain native tool confirmation pauses.
- One invocation now exports as one trace.
LlmAgentheld acall_llmspan guard across its generator's suspension points, andRunnerinstrumented only the construction of the agent stream rather than the draining of it, so spans detached from their parents. Both now instrument the individual futures. Names, attributes, and durations were always correct — only parentage was wrong. adk-realtime --features livekitcompiles again —livekit-data-streampinned to=0.1.1, since 0.1.2 breaks livekit 0.8.1's own source.adk-audioONNX features compile again —ortpinned to=2.0.0-rc.11, since rc.13 moved the CUDA and CoreML execution providers.
No API changes from 2.1.0. Coming from 1.x, see the migration guide.
Full detail in the CHANGELOG.
ADK-Rust v2.1.0 — Portable Teams, Agent Platform, and Runtime UI
ADK-Rust 2.1.0 is an API-compatible minor release adding portable team orchestration, the owned runtime UI, Gemini Enterprise Agent Platform integrations, hardened ambient scheduling and tool guardrails, expanded Anthropic capabilities, refreshed provider model catalogs, and security dependency updates.
All 43 workspace crates are published on crates.io. See the 2.1.0 changelog for the detailed compatibility and behavior notes.
- feat(server): add plain-JSON POST /api/run runtime endpoint - PR 0.2 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/569
- feat(server): bridge adk-skill SkillIndex into A2A agent-card skills - PR 0.3 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/570
- feat(session): complete VertexAiSessionService for managed deployments - PR 0.4 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/571
- feat(model): drive Vertex backend selection from GOOGLE_GENAI_USE_* env - PR 0.5 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/572
- feat(telemetry): export traces and JSON logs to Google Cloud - PR 0.6 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/573
- feat(gemini): implement cached-content operations on VertexBackend - PR 0.7 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/574
- feat(artifact): add GCS artifact backend with adk-python blob parity - PR 0.8 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/575
- test(rag): add shared VectorStore contract and property test suite - PR 0.9 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/576
- fix(rag): reorder FLEXIBLE after TYPE for surrealdb 3.2, add feature-… by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/578
- Add native MCP 2026 lifecycle, Tasks, and MRTR by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/577
- feat(server): add Agent Engine dispatch core - envelope, operations, router - PR 1.1 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/579
- docs: state what v2.0.0 adds, and fix stale README claims by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/582
- feat(server): add turnkey serve_agent_engine entrypoint and integration - PR 1.2 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/583
- feat(memory): add Vertex AI Memory Bank backend - PR 2.1 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/584
- feat(tool): add Example Store client and few-shot provider - PR 2.2 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/585
- feat(cargo-adk): register a docker addon, fixing README drift - PR 2.3 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/586
- feat(adk-graph): add SqliteCheckpointer::from_pool for existing sqlx pools by @1111mp in https://github.com/zavora-ai/adk-rust/pull/589
- feat(cargo-adk): add agent-engine template with BYOC terraform - PR 2.4 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/590
- feat(deploy): add Agent Engine BYOC deployment client - PR 2.5 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/591
- test(adk-graph): cover SqliteCheckpointer::from_pool by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/596
- feat(cli): add adk-rust deploy agent-engine subcommand - PR 2.6 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/597
- feat(gcp): add adk-gcp shared ADC/LRO plumbing crate - PR 3.1 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/598
- refactor(session): migrate Vertex backend to adk-gcp plumbing - PR 3.2 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/599
- feat(agent): add portable team orchestration by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/606
- feat(runtime): harden ambient scheduling and tool guardrails by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/608
- refactor(memory): migrate Vertex backend to adk-gcp plumbing - PR 3.3 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/600
- ci: prepare merge queue and simplify docs setup by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/609
- refactor(tool): migrate example store client to adk-gcp - PR 3.4 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/601
- refactor(deploy): migrate gcp client to adk-gcp plumbing - PR 3.5 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/602
- refactor(artifact): migrate GCS auth to adk-gcp plumbing - PR 3.5b by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/603
- feat(code): add Vertex sandbox code-execution client and tool - PR 3.6 by @joseph-wortmann in https://github.com/zavora-ai/adk-rust/pull/604
- feat(anthropic): compatible request customization, refusal fallback, and v2.1.0 by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/610
- docs: align v2.1 overview and remove live audio tests by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/612
- feat(server): modernize embedded runtime UI and telemetry by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/614
- chore: ignore TypeScript incremental build metadata by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/617
- fix(model): correct provider pricing and refresh the model catalog by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/616
- feat(model): align current provider contracts, defaults, and docs by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/618
- chore(release): prepare 2.1.0 and resolve security alerts by @jkmaina in https://github.com/zavora-ai/adk-rust/pull/619
- @1111mp made their first contribution in https://github.com/zavora-ai/adk-rust/pull/589
Full Changelog: https://github.com/zavora-ai/adk-rust/compare/v2.0.0...v2.1.0
Thank you to @joseph-wortmann, @1111mp, and @jkmaina for the merged code, tests, examples, and documentation in this release. The pull-request list above preserves attribution for every merged change; @1111mp is a first-time code contributor in v2.1.0.
Thank you to the people whose reports and proposals shaped the shipped work:
- @VikashLoomba proposed Anthropic per-request header customization (#592) and typed server-side safety-refusal fallback (#593).
- @1111mp requested existing SQLx pool support for
SqliteCheckpointer(#588) and contributed the implementation. - @joseph-wortmann reported and fixed the SurrealDB 3.2 collection-schema regression (#568).
- @jkmaina authored the design and hardening issues for portable teams (#605), ambient scheduling and tool guardrails (#607), the embedded runtime UI (#613), and provider pricing corrections (#615).
v2.0.0 — Agents That Act
A major release for agents that run on their own and finish what they start. 42 crates. 4,300+ tests. 104 runnable examples. 568 μs agent-loop overhead.
🎬 Rust & Beyond, Episode 3 — Agents That Act · 40 min 50 sec
- Subgraphs — a compiled graph as a node, nested, each with its own checkpoint thread
- Routing without edges —
with_gotolets a node choose its successor;with_goto_parenthands control back to a parent graph - Imperative invocation —
run_node_withfor work whose size comes from state - Durable resume — SQLite checkpointers and delta checkpoints; a run resumes in a fresh process that shares only the database file
- Built to run unattended — per-node retry with capped backoff, node timeouts, a concurrency bound, and checkpoint retention
- Governed computer use —
adk-computer-use, with approval interrupts bound to a digest - Coding-agent tools —
adk-devtools: read, write, edit, glob, grep and bash scoped to a sandboxed workspace - MCP on rmcp 3.1 — tools, resources, prompts, elicitation and tasks
Six APIs changed shape, and the fan-in default changed behaviour without an API change. Read the migration guide before upgrading.
| Change | Action |
|---|---|
DeferredNodeConfig gained a public min_predecessors field |
add min_predecessors: None to struct literals |
CompiledGraph::get_next_nodes returns Result<Vec<String>> |
handle the error instead of unwrapping a Vec |
CompiledGraph::time_travel returns Result<TimeTravelHandle> |
it no longer panics without a checkpointer |
| Task execution needs the extension declared at handshake time | declare it when you build the server |
| A tool no longer declares its own task contract | move the contract to the server |
rmcp::model::SamplingMessageContent → SamplingMessageContentBlock |
rename at the call site |
An unknown route key now fails the run instead of ending it and reporting success. Fan-in runs once per super-step rather than once per arriving branch.
cargo add adk-rust
# or scaffold a full project:
cargo install cargo-adk
cargo adk new my-agent
cargo run
Coming from 1.x? Start with the migration guide.
- 📚 Documentation
- 📦 crates.io
- 🗺️ Roadmap
- 📋 CHANGELOG — the full v2.0.0 entry
- 🔀 Migration guide
- 🎬 Episode 3 — Agents That Act
Many thanks to everyone who made v2.0.0 possible:
@joseph-wortmann @mikefaille @caibirdme @NikiKrutan @mscharley @hiratara @chathaway-codes @nullsauce @morlay
And to everyone who filed an issue or a reproduction this cycle. 🙏
v1.0.0 — The Stable Foundation
The first stable release of the Rust Agent Development Kit. 39 crates. 130K+ downloads in 6 months. Semver stability guarantee.
- Semver stable — pin to
1.xand your build won't break - All former Beta crates promoted to Stable
- 4.6× faster cold start than Python ADK (measured by
adk-bench) - 7 model providers — Gemini, OpenAI, Anthropic, DeepSeek, Groq, Ollama, Bedrock
- 6 session backends — SQLite, PostgreSQL, Redis, MongoDB, Firestore, Neo4j
- A2A v1.0.0 protocol — Agent-to-Agent communication
- Real-time voice — OpenAI, Gemini Live, LiveKit
- Graph workflows — Durable, resumable, with HITL interrupts
- MCP integration — rmcp 1.2, server lifecycle, elicitation, resources
adk-bench— Cross-framework benchmarking (vs Python ADK, LangChain, CrewAI)adk-managed— Managed Agent Runtime with durable execution and checkpointingadk-enterprise— Enterprise client SDK (HTTP/SSE, zero runtime deps)- Gemini Interactions API (Beta) — server-side stateful history
- Functional Graph API — write workflows as async functions
- Background Runs & Cron Scheduling for
adk-server - Lock poison recovery across session, runner, and tool layers
- Security hardening —
.cargo/audit.toml, dependency advisories documented - MSRV enforcement —
rust-toolchain.tomlpins Rust 1.94.0
cargo add adk-rust
# or scaffold a full project:
cargo install cargo-adk
cargo adk new my-agent
cargo run
Or try the ADK Playground — 120+ agents, no install.
- 📚 Documentation
- 🎮 Playground
- 📦 crates.io
- 🗺️ Roadmap
- 📋 CHANGELOG
- 🎬 Launch Video (Episode 2)
Many thanks to everyone who made v1.0.0 possible:
@mikefaille @rohan-panickar @dhruv-pant @tomtom215 @danielsan @CodingFlow @ctylx @poborin @chillin-capybara @baotao2006
And dozens more in discussions, issues, and pre-release testing. 🙏
v0.9.0
- Composable Template System — 8 base templates, 9 addons, 5 enterprise patterns via
cargo adk new --addon - Cargo Adk Build — Compile-without-deploy subcommand for pre-deployment verification
- A2A Simple Scaffolding —
A2aServer::quick_start,A2aServer::builder,cargo adk new --template a2a
- Version bump from 0.8.5 to 0.9.0
- hickory-proto 0.26.1 (moderate — DNS cache poisoning)
- openssl 0.10.80 (moderate — certificate chain validation)
- rubato 3.0 (low — integer overflow in sample rate conversion)
- similar 3 (low — DoS via quadratic diff complexity)
See CHANGELOG.md for full details.
v0.8.4
- Gemini schema: array types require
items: v0.8.3 stripped tuple-styleitemsentirely, causing Gemini to reject arrays with "missing field" errors. Now tupleitems([{type:number},{type:number}]) are converted to a single schema using the first element ({type:number}). Arrays without anyitemsfield also get a default{type:string}added.
adk-gemini = "0.8.4"
Full Changelog: https://github.com/zavora-ai/adk-rust/compare/v0.8.3...v0.8.4
v0.8.3
-
Gemini schema
itemstuple validation error: Fixed 400 errors from Gemini API when MCP tools declareditemsusing JSON array syntax (tuple validation). Gemini's Schema proto only supportsitemsas a single schema object. -
Comprehensive unsupported keyword stripping: Expanded the Gemini schema adapter's unsupported keywords list from 7 to 32 entries based on official Gemini API documentation. The Schema proto only supports
type,description,enum,items,properties,required,nullable, andformat. All other JSON Schema keywords are now stripped including numeric constraints (minimum,maximum,multipleOf), string constraints (minLength,maxLength,pattern), array constraints (minItems,maxItems,uniqueItems,contains,prefixItems), object constraints (minProperties,maxProperties,dependentRequired,dependentSchemas), annotations (title,default,deprecated,examples,readOnly,writeOnly), content keywords (contentMediaType,contentEncoding), and meta ($id).
adk-rust = "0.8.3"
# or individual crates:
adk-gemini = "0.8.3"
Full Changelog: https://github.com/zavora-ai/adk-rust/compare/v0.8.2...v0.8.3
v0.8.2 — Enhanced Plugin System, Retry & Reflect, Memory Tools, OTel GenAI Semconv, ACP Server
Fine-grained tool-call and model-call interception with pipeline execution, shared state, and short-circuit capabilities. Enables composable cross-cutting concerns without modifying agent code.
New standalone plugin that intercepts tool failures and injects structured reflection prompts for agent self-correction. Configurable backoff, per-tool limits, and circuit-breaker patterns.
Self-callable agent tools (LoadMemoryTool, PreloadMemoryTool) enabling agents to autonomously search their own long-term memory during reasoning.
Full compliance with OpenTelemetry GenAI Semantic Conventions v1.41.0. All gen_ai.* attributes, span naming, finish reason mapping, and opt-in content events.
Expose any ADK-Rust agent as an ACP-compatible agent for IDE connections (Kiro, VS Code, Claude Desktop). Stdio and HTTP transports, permission bridge, session management.
MCP tool schemas normalized per-provider at request time. SchemaAdapter trait, per-provider transforms, schema caching.
Environment variable interpolation, plugin references, session/memory backend configuration, round-trip serialization.
See CHANGELOG.md for full details.
v0.8.1 — ACP Integration
Agent Client Protocol (ACP) support — connect ADK agents to Claude Code, Codex, Kiro CLI, and any ACP-compatible agent as tools.
use adk_acp::AcpAgentTool;
let kiro = AcpAgentTool::new("kiro-cli acp --trust-all-tools")
.description("Delegate coding tasks to Kiro CLI");
let agent = LlmAgentBuilder::new("orchestrator")
.tool(Arc::new(kiro))
.build()?;
AcpAgentTool— wraps any ACP agent as an ADK ToolAcpToolset— multiple ACP agents as a Toolsetprompt_agent()— low-level prompt/response- Auto-approve mode for permission requests
- Feature:
adk-rust = { version = "0.8.1", features = ["acp"] }
Push agents to ADK Platform:
cargo adk deploy # build + push
cargo adk deploy --dry-run # validate without pushing
- MCP schema sanitization for Gemini: Strips
exclusiveMinimum/exclusiveMaximum, collapses"type": ["string", "null"]→"type": "string", removesitemson non-array types. Fixes Gemini rejections with computer-use and playwright MCP tools.
- @chillin-capybara — ACP integration, adk-acp crate
Full Changelog: https://github.com/zavora-ai/adk-rust/compare/v0.8.0...v0.8.1