Repository navigation
chore(deps): update rust crate adk-rust to v2.3.0 - #469
Open
renovate[bot] wants to merge 1 commit into
Open
renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
2.2.0→2.3.0Release Notes
zavora-ai/adk-rust (adk-rust)
v2.3.0Compare Source
Breaking
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 the
adk-acpAPI is unchanged.AP2 amount conversion is fallible (
adk-payments,ap2):PaymentCurrencyAmount::to_moneyandAp2McpReceiptStatus::from_receiptreturn
Result<_, Ap2Error>. Malformed, over-precise, or overflowing amountsreturn
Ap2Error::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, sostruct-literal construction needs
..Default::default().Terminal streaming events carry the complete response (
adk-agent,adk-server): thelast event of a streamed model response holds the whole response, marked
provider_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 thecontent rendered for an event
idwhencontent_completeis set, or pass each eventthrough
adk_core::EventTextDeltas. Tool-result events arrive in completion order ratherthan call order.
Security
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 v1
messageIddeduplicates per caller; and a v1INPUT_REQUIREDtaskresumes only for its owner. The session user was derived from the
client-supplied
contextId, so an authenticated caller who knew anothercaller's
contextIdor task ID could read that caller's history, poll itstask, or cancel it. Deployments without authentication keep the
contextId-derived session user.Debug event lookup checks trace ownership (
adk-server):GET /api/apps/{app}/users/{user}/sessions/{session}/events/{event}answers 403when spans recorded for the session belong to a different user, as
GET /api/debug/trace/session/{session_id}does, instead of reading the traceby session ID alone.
Background and cron routes mount behind authentication (
adk-server,feature
background):ServerBuilder::with_background_runsandServerBuilder::with_cron_jobsserve/api/runsand/api/cronbehind theserver'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,feature
a2a-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 ofblocked IPv4 addresses.
HttpPushNotificationSender::new()does not followredirects 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 theliteral and granted full network access and writes to
/. Paths that are notvalid UTF-8 are rejected with
PolicyViolation.The Seatbelt profile is deny-by-default (
adk-sandbox,sandbox-macos):(allow default)followed(deny default), so every read and mach lookup wasallowed —
~/.sshand other host secrets were readable from inside thesandbox. 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 setof startup mach services, and the policy's paths.
ProcessBackendnow reportsfilesystem_read_isolationon macOS. Programs installed elsewhere, such as arustup toolchain or Xcode, need
allow_read.Linux
allow_process_spawn = falseis enforced (adk-sandbox,sandbox-linux): it previously added only--new-session, which does notblock
forkorexec. 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 onthe wrapped command.
LocalUnixSession::exec_commandis hardened (adk-sandbox,workspace):commands run with a cleared environment (only
PATH,HOME,USER,LOGNAME,LANG,LC_ALL,LC_CTYPE,TZ,TMPDIR, andTERMpassthrough), stdin from
/dev/null, output read while the command runs and cappedat 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 only
sh.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 than
none.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 shellcommands into the container. Branch names starting with
-are rejected.Redis memory
delete_usererased other users' memories (adk-memory): theindex-key
SCANpattern wasmem_idx:{app}:{user}*, with no delimiter after theuser id and no glob escaping, so deleting
alicealso 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 werejoined with
:, which identity validation allows, so appa:bwith usercshared keys with app
aand 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 into
id IN ('…')without escaping, so an id containing'failedthe delete and the id
x') OR ('1'='1deleted every row. Ids are now typedstring 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 with
session.not_found.createno longer upserts: an ID already taken in the appfails with
session.already_exists, so a session and its events cannot betaken 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):sealed into one envelope per event;
llm_requestand eventprovider_metadataare not persisted.createand from eventstate_deltaare encrypted pervalue, so
app:anduser:values stay in their shared tiers and laterwrites are encrypted even when the initial state is empty.
key or event ID; data moved to another session fails with
session.encryption.decrypt_failed.listreturns decrypted state and events instead of ciphertext.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.session.encryption.unencrypted_data;EncryptedSession::with_legacy_migration(true)reads and re-encrypts it.inner()andinto_inner()expose the wrapped service.RedisSessionConfigredacts credentials inDebug(adk-session,feature
redis): the URL password and*password*query parameters print as[REDACTED].UI sign-in warning (
adk-cli): the account name no longer reachesstdout 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 on
u32underflow 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 passesvalidation. Rejected attempts, including every attempt of a run that exhausts
output_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 of
RunnerConfig::cancellation_tokeninstead ofspawning two watcher tasks that never completed. Cancelling the global token
still stops every in-flight run, and
Runner::interruptcancels only its target.Lost wakeup in
SharedState::wait_for_key(adk-core): a waiter registersfor notification before it reads the key, so a
set_sharedthat lands betweenthe read and the wait wakes it instead of leaving it to time out.
adk-devtools: file tools refuse dangling symlinks, and
globstays inside theworkspace.
Workspace::resolvestepped past a path component that failed tocanonicalize, so a dangling symlink passed the containment check and
write_filefollowed it, creating its target outside the root — a repository containing
link -> ~/.zshenvletwrite_file("link", ..)create~/.zshenv. Dangling andlooping symlinks are now refused wherever they point, and
write_file/edit_filere-check the canonical parent before the open and open with
O_NOFOLLOWon Unix.globjoined the pattern onto the root unchecked, so../*and symlinkeddirectories listed host paths; it now refuses
..and absolute patterns, escapesthe root path, and drops every match that resolves outside the root.
adk-devtools:
bashoutput is capped as it is read, and a cancelled call killsthe command. Output was collected in full and cut afterwards with
String::truncate, which bounded only the report and panicked when the cap fellinside a multi-byte character. Each stream now keeps at most
max_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 itselfread-only. The tool returned
is_read_only() == truebut executed any SQL, soparallel dispatch could run a
DROP TABLEalongside other calls. In read-only mode,now the default, it accepts a single
SELECTorWITHstatement (comments andstring contents are ignored, multiple statements are refused), and a BigQuery dry
run must classify the query as
SELECTbefore it runs.is_read_only()followsthe configuration.
adk-tool: BigQuery and Spanner service-account keys stay in memory. Keys
resolved through
from_secretwere written to the shared temp directory withdefault 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 nowaccept only
httpandhttps(DEFAULT_ALLOWED_SCHEMES) unlessBrowserToolset::with_allowed_schemeswidens the list, and refuse other schemesbefore the browser is touched.
browser_evaluate_jswas part of the defaulttoolset and the
FullandScrapingprofiles; it is now excluded from all of them.adk-code: Rust executors no longer expose the host environment.
RustExecutorandRustSandboxExecutorranrustcwith the inheritedenvironment, so
env!("OPENAI_API_KEY")oroption_env!in model-writtencode embedded host credentials in the binary, and
RustSandboxExecutorran thebinary with the full host environment.
rustcnow starts from a clearedenvironment plus the
ProcessBackend::toolchain_envallowlist, withkill_on_dropand the request timeout.RustSandboxExecutorruns the binarywith only the variables named by the request's
EnvironmentPolicyand nowreports
enforce_environment_policy: true.RustExecutorcompiles insidethe
SandboxBackendwhen 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 cannotenforce.
EnvironmentPolicy::Nonepassed validation on backends reportingenforce_environment_policy: false, although those backends pass the hostenvironment through. Such backends now reject every request with
ExecutionError::UnsupportedPolicy.adk-code:
EmbeddedJsExecutorenforces its timeout. The timeout waschecked only after evaluation returned, so
while (true) {}never returnedand pinned a blocking-pool thread. Evaluation runs on a dedicated thread,
executereturnsExecutionStatus::Timeoutat the deadline, and Boa runtimelimits (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 from
DockerConfigalone while the backendreported network, filesystem, and environment enforcement. Each request is now
checked with
DockerConfig::validate_sandbox_policy, and a request stricterthan the container — for example
SandboxPolicy::strict_rust()against awith_network()container — fails withExecutionError::UnsupportedPolicy.Variables named by
EnvironmentPolicy::AllowListare passed from the host tothe 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 ignorethe 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 with
Ap2Error::AuthorizationVerifierNotConfigureduntilwith_merchant_authorization_verifierandwith_user_authorization_verifierare set.
allow_unverified_authorizations()restores the presence-only checksfor 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 previousidempotency-only profile is
permissive().with_idempotency_mode(IdempotencyMode::RequireForPost).Amounts follow the currency minor unit (
adk-payments): AP2 amounts and ACPdelegated-payment allowances use the ISO 4217 minor-unit scale (
JPY0,KWD3) instead of a fixed scale of 2.
AmountThresholdGuardrail::newthresholds areminor units at that scale;
with_currencybinds them to one currency anddenies the others.
AP2 human-not-present authority (
adk-payments,ap2): an intent needsmerchant or SKU constraints;
requires_refundabilityalone no longerauthorizes autonomous payment.
AP2 authorizations are verified, not just present (
adk-payments,ap2):the default verifiers accepted any non-empty
merchant_authorizationoruser_authorization, so a forged value reachedexecute_payment. The adapternow rejects every authorization until cryptographic verifiers are configured.
A merchant verifier that sets
VERIFIED_MERCHANT_NAME_CLAIMmakes the adapterreject carts whose
merchant_namediffers from the verified signer.AP2 payment mandates are single-use (
adk-payments,ap2): the adapterconsumes each mandate in a
PaymentMandateLedgerbefore execution and recordsit on the transaction. A resubmitted mandate returns
Ap2Error::PaymentMandateReplayed, a second mandate for a paid transactionreturns
Ap2Error::TransactionAlreadyPaid, andap2_payment_mandate_idisnever rebound by later receipts.
with_payment_mandate_ledgerplugs in sharedstorage for multi-instance deployments.
ACP signature enforcement without a verifier (
adk-payments,acp):require_signature(true)withoutwith_signature_verifieronly checked thatthe header existed. Router
build()now fails for that profile, and therequest 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 whoseconfirmation the invocation context requires — a team
Delegaterelationshipwith
RelationshipApprovalPolicy::Required— no longer executes when the staticor handler decision for the call is
Deny. The tool executor applies the unionof the agent's
ToolConfirmationPolicyandInvocationContext::requires_tool_confirmation, so a denied delegate callreturns 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 projectsegment. Identifiers containing neither character keep their existing keys.
Data stored under an identifier containing
:or%must be renamed to theencoded key, for example memory entries of app
a:b, userc, sessionsmovefrom
mem:a:b:c:stomem:a%3Ab:c:s. Inadk-session, an app namedapp_state,user_state,sessions_idxorsession_lookupalso has its firstbyte 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; call
delete_sessionfirst to avoid it.Anthropic API keys in
Debugoutput (adk-anthropic):Anthropic,ManagedAgentsClient, andFilesClientredact the API key inDebugoutput, and the
x-api-keyand bearerAuthorizationheader values aremarked 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 without
api_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 theOpenAI-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 through
HTTP_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 aserver's MRTR sampling requests with the configured sampling handler. Every toolset on
AdkClientHandler, including one passed toMcpToolset::new, now applies ADK's inputpolicy, 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.
Added
Gemini Live spoken language (
adk-realtime):RealtimeConfig::with_languagesends a BCP-47 tag as
generationConfig.speechConfig.languageCodefor AI Studioand 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-backedcoordinator delegates over ACP stdio to an OpenAI-backed ADK agent served by
AcpServer, 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 eachmodel request through the new
Toolset::process_llm_requesthook. Guidanceis 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 — thenewest 1,000 for at most one hour by default — configured with
ServerBuilder::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 thatinvoke
rustcthemselves apply the same minimal toolchain allowlist afterenv_clear().DockerConfig::validate_sandbox_policy(adk-code): checks whether acontainer created from a
DockerConfigsatisfies aSandboxPolicy.Plain-HTTP base URLs for internal gateways (
adk-anthropic,adk-model):Anthropic::allow_insecure_http()andAnthropicConfig::allow_insecure_http()acceptan
http://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 URLfrom
ANTHROPIC_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 call
BrowserSession::start(), which now keeps a livesession and replaces one that no longer responds;
chrome_optionpasses Chrome optionssuch as
binaryandprefs.EventTextDeltas(adk-core): converts complete response snapshots back into text andthinking deltas for append-only output such as terminals and chat streams.
McpToolset::cancel_pending_tasks(adk-tool): cancels remote MCP tasks whosetool-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 thegrace period when it stops a server.
OpenCode Go and Zen (
adk-model): theopencodefeature addsOpenCodeClient, whichroutes documented models through Chat Completions, Responses, Anthropic Messages or Gemini
generateContentand sends the application'sUser-Agentandx-opencode-sessionheaderswith every request. Unknown models take an explicit
OpenCodeApi.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.Changed
wasmtime 48 (
adk-sandbox, featurewasm):wasmtimeandwasmtime-wasimove from 46 to 48.0.3 together. No
adk-sandboxAPI changes; wasmtime typesare not part of its public API.
Supported versions (
SECURITY.md): security fixes target the 2.2.x lineand 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; see
adk_graph::functional::task_call_id. The wrapperalso uses the context parameter's real name instead of assuming
ctx.Dynamic pauses dropped completed siblings' successors (
adk-graph): adynamic or tool-confirmation pause saved only the paused node, so the successors
and
gototargets of nodes that completed in the same super-step never ran afterthe resume. The saved frontier is now those successors plus the paused node, in
invokeand in every stream mode.Fan-in arrivals lost across a pause or crash (
adk-graph): a join'srecorded 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 keyloads with no arrivals, as before.
CompiledGraph::update_statenow keeps thearrivals, the answered gate, the retry budget, and the child ledger.
GraphAgentanswered every later turn with the first (adk-graph): with acheckpointer, 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): ahit'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-linkescapes; HTTP nodes reject URLs outside an
HttpActionPolicy(defaulthttpsand
http), re-check every redirect, and log URLs without query strings orcredentials.
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, since
action-db,action-email, andaction-codecompile placeholders only.
Failed retry-budget checkpoint was silent (
adk-graph): when a super-stepfails 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 workflowresumed 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 thecurrent working directory as their only root;
ActionNodeExecutor::with_file_rootsand
action::file::execute_file_inname others. Reported paths are the resolvedabsolute 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 storebelongs 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 previousbehavior with
BigQueryToolset::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 out
browser_evaluate_js(Fullnow has 45 tools,Scraping13); thescroll, hover, and alert helpers stay. Opt in with
BrowserToolset::new(browser).with_evaluate_js(true);.with_js(true)alsoenables 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): thepin was five minor versions behind. Two changes in the dependency's 0.7.0 release reach this
crate's
a2a-v1surface.JsonRpcRequest.idis now the three-stateJsonRpcRequestId(
Absent/Null/Value), so an explicit"id": nullis treated as the call JSON-RPC 2.0says it is rather than collapsing into a notification; responses are unchanged, because
to_response_id()maps bothAbsentandNullto theNonethe previousOption<Value>produced.
TaskPushNotificationConfig::task_idandAuthenticationInfo.credentialsare nowOption<String>, matching the canonical schema that previously rejected valid cross-SDKpayloads 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 the
Authorizationheader rather than sending an empty or placeholderbearer token.
Anthropic streams must end with
message_stop(adk-model): a stream that closeswithout
message_stop, or whose message has no stop reason, now fails instead ofending 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-streamingcall, and accepts
with_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 itsproxy, timeout and default-header settings into every request. The redirect policy is
always replaced with
Policy::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 in
provider_metadata(webSearchQueries,groundingChunks,groundingSupports) for applications that display them.Model-facing MCP tools forward MRTR input to a custom client handler (
adk-tool):on
McpToolset::newwith a handler other thanAdkClientHandler, an MRTR inputrequest from a tool call returned an error. It now reaches the handler, whose default
create_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 through
scripts/cargo-pinned.sh, which resolves therust-toolchain.tomlpin via rustup and refusesto 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 OpenAIResponses streaming with commentary, emit a final response carrying the full message with
provider_metadata.content_completeset totrue. Code that consumesLlmstreamsdirectly 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 theprovider's raw usage.
adk-eval'sCostTrackerpricesprompt_token_countat the fullinput rate, so cached Anthropic input is counted at that rate, and Claude through Bedrock
still reports
prompt_token_countwithout cache tokens, so the two paths differ for thesame request.
Vertex requests are no longer replayed over REST (
adk-gemini,adk-model): a gRPCtransport failure on a non-streaming Vertex
generateContentcall is returned instead ofbeing re-sent over REST, because the first request may already have reached the model.
adk-modelmaps those failures toErrorCategory::Unavailable, soRetryConfigdecideswhether to retry.
Fixed
acp_full_protocolpermission tests (examples/acp_full_protocol): theexample reads
tool_confirmation_decisionsby function-call ID, matching thekey the ACP server populates, so approval and denial resume correctly.
OpenAI reasoning history (
adk-model):Part::Thinkingis excludedfrom 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 with
with_reasoning_replay(true)to sendreasoning_content, oruse
with_reasoning_replay_field(ReasoningReplayField::Reasoning)to sendreasoning. Replay emits only the selected field; standard OpenAI andAzure requests omit both fields by default.
Open Responses argument identity (
adk-model): conflicting item or callidentities 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 requestsretain 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 retainmodel 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): ChatCompletions streaming now preserves structured JSON
function.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 nowserializes as
web_search, matching the OpenAI Responses API. The explicitpreview variant remains
web_search_preview_2025_03_11. URL annotations fromresponse text are preserved as ADK citation metadata. Open Responses mode now
also accepts compatible endpoints that omit output-message IDs, statuses, or
empty
output_text.annotations, and reconstructs final function argumentsfrom their streaming events when the completed response omits them.
OpenAI-compatible usage metadata (
adk-model): Chat Completionsstreaming and non-streaming responses now use one normalization path,
preserve the complete provider-native
usageobject inprovider_usage,and project reported cache-write tokens alongside cache reads.
OpenAI tool request stability (
adk-model): Chat Completions andResponses requests now serialize function tools in deterministic name order,
preserving identical cacheable prefixes across repeated executions.
DeepSeek streaming emitted every response twice (
adk-model): theterminal 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 — sothey saw the whole response twice. With
output_schemaset that is{...}{...}, which fails JSON parsing ("Response is not valid JSON: trailingcharacters") and burns every schema-validation retry, so structured output was
unusable on DeepSeek. The terminal chunk now contributes only its own
delta,plus reasoning that was buffered but never streamed (thinking disabled), and a
delta.contentarriving in the same chunk asfinish_reasonis no longerdropped.
Rust sandbox and container stdin delivery (
adk-code):RustSandboxExecutorandContainerCommandExecutorwrite stdin concurrentlywith 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 bycount and age instead of accumulating for the life of the process. A
message/sendthat reuses thetaskIdof a running task, or of anothercaller's task, is rejected with JSON-RPC error
-32602instead of replacing therunning task's registration and its abort handle. A task is registered before
it is spawned, so a fast failure cannot leave a permanent
workingentry. Afailed 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 pausedinstead of claiming them one per scheduler tick. The
Queuepolicy holds atmost
DEFAULT_MAX_QUEUE_DEPTH(100) runs per job, configurable withCronState::with_max_queue_depth, and skips an occurrence with a warning whenfull.
executionCountreports the runs the scheduler started.A2aServerBuilder::bind_addrrustdoc (adk-server): states the actualdefault,
127.0.0.1:8080.AP2 amount parsing (
adk-payments,ap2): the sign comes from the decimalstring, so
-0.50is-50minor units rather than+50, and exponentnotation parses exactly. Every amount in a cart shares one scale, so
mixed-precision items no longer produce a spurious
ap2_unallocated_delta.Cart sums use checked arithmetic; overflow, more than 18 fraction digits, and
mixed currencies return
Ap2Error::InvalidAmountinstead of panicking orwrapping.
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 profile
sandbox-execrefused to parse and nothing ran. Theyare now ignored with a
tracing::warnand all network access is blocked, ason Linux. The
NetworkRuledocumentation no longer claims macOS enforces them.ProcessBackendstdin cannot hang an execution (adk-sandbox): stdin iswritten 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,
so
nohup … &descendants no longer outlive the call or hold its pipes open.ProcessBackendkeeps enforcer configuration (adk-sandbox): thewrapped program is resolved before
configure_commandruns. Rebuilding thecommand 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 thesource 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 over64 KiB deadlocked the command, which was then reported as
timed_out.SandboxToolclampstimeout_secs(adk-sandbox): values outside theschema'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 the
memory: truecapability. Previously no limit applied.DockerSessionreads binary files intact and kills timed-out commands(
adk-sandbox,workspace-docker):read_filereturns raw bytes instead oflossily 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 globaluniqueness constraint but was
{session_id}_{i}orentry_{millis}, so a secondadd_sessionfor a session id failed, as did the same session id under anothertenant, and two
add_entry_to_projectcalls in one millisecond collided. Ids arenow scoped by app, user, project and session, direct entries use a UUID, and
add_sessionmerges on the id, so re-ingesting a session updates it.Neo4j vector search returned nothing for most tenants (
adk-memory): searchtook the global top
limitfrom the vector index, then filtered by app, userand project. It now requests
limit × 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 vectorresults.
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 aquoted phrase, bare
AND/OR/NOT/NEARare dropped, and an entry matcheswhen it contains every word, as with
plainto_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 untilthe process ran out. Every chunk now advances at least one character. The new
try_newconstructors rejectchunk_size == 0andchunk_overlap >= chunk_sizewith
RagError::ConfigError;newnormalises such pairs with a warning insteadof 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 nowmerge-inserts on
id, keeping the last duplicate in a batch, and an embeddingwhose length differs from the collection's dimensions returns
RagError::VectorStoreErrorinstead of panicking.RAG ingest dropped chunks without embeddings (
adk-rag):RagPipeline::ingestzipped chunks with embeddings, so a provider returningfewer vectors than chunks silently stored fewer chunks. A count mismatch now
fails with
RagError::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 providerclient the same status mapping and
Retry-Afterhandling.Long Anthropic streams (
adk-anthropic): streaming requests run on an HTTPclient without a total request timeout, so a generation longer than 60 seconds
is no longer cut off mid-body.
with_timeoutstill bounds non-streamingrequests and the wait for streaming response headers.
Anthropic stream inactivity timeout (
adk-anthropic): the 30-second timeoutnow 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 one
Errinstead ofrepeating the error.
Mid-stream Anthropic
errorevents (adk-anthropic):AccumulatingStreamyields an SSE
errorevent (for exampleoverloaded_error) asErrand sendsthe error, not the partial message, through its channel.
From<ApiError> for Errormapsoverloaded_errorandrate_limit_errortothe retryable
ServiceUnavailableandRateLimitvariants.Unknown Anthropic SSE event types (
adk-anthropic): an unrecognized eventtype is logged at
debugand surfaced asMessageStreamEvent::Pinginsteadof 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 MBlimit 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 keptthe empty
{}input fromcontent_block_start.Text tool-call false positives (
adk-model): text-encoded tool calls areparsed only when the request declares the named tool, so a
```jsonexamplewith a
"name"key stays text. The DeepSeek fence format requires anargumentsorparametersobject instead of defaulting to{}, and text afterthe 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, and504 from OpenAI-compatible, Azure OpenAI, Azure AI, Anthropic, DeepSeek, and
Groq map to
ErrorCategory::Unavailablewith the upstream status, soexecute_with_retryretries them. ARetry-Afterheader becomes the error'sretry hint for every one of these providers.
Retry-Aftercapped atmax_delay(adk-model): a server-provided retrydelay no longer exceeds
RetryConfig::max_delay, soRetry-After: 3600cannotstall an agent run for an hour.
Stable tool order for cached providers (
adk-model): Anthropic and Bedrocksend 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 with
model.<provider>.invalid_tool_argumentsinstead of calling the tool with
{}ornull. A zero-argument Bedrock callreceives
{}.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 at
debug.Firestore app-state path (
adk-session, featurefirestore): app statemoves 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 thedocument paths the backend writes.
Undecodable stored events are logged (
adk-session): the SQLite,PostgreSQL, Redis, and Firestore backends emit a
warn!with the session andevent 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): preservecitations 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-taskinput 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 names
McpToolset::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):McpToolsetmeasureseach tool's input and output schema against
McpSchemaLimits(256 KiB and 10 000JSON 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 log
schema.bytesandschema.nodesatdebuginstead of the full schema.
McpToolset::with_schema_limitsandMcpServerManager::with_schema_limitsconfigure the limits, and managed toolsetsare named after their server ID.
Native provider fidelity (
adk-model,adk-anthropic,adk-gemini): retainstreamed 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 thelink 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; andglobwalksthe workspace instead of expanding a canonical root whose
\\?\prefix the glob crateread as a wildcard.
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.