3876 Commits

Author SHA1 Message Date
Shangjie Chen e753651b7d fix: prevent duplicate synthetic user event on single-turn agent resumption
Continuous Integration / Pre-commit Linter (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.10) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.11) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.12) (push) Waiting to run
Continuous Integration / Mypy Check (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.10) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.11) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.12) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.13) (push) Waiting to run
Continuous Integration / Unit Tests (Python 3.14) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Waiting to run
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Waiting to run
Copybara PR Handler / close-imported-pr (push) Waiting to run
When resuming a single-turn agent node from an HITL tool confirmation pause,
prepare_llm_agent_input was unconditionally appending a synthetic user event,
shadowing the user's FunctionResponse and causing an infinite confirmation loop.
This change skips synthetic user input injection when resume_inputs are present.

Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 969178203
2026-08-22 17:44:30 -07:00
Kathy Wu d9f4d3d288 fix: stop the session liveness probe from crashing when the SDK moves its streams
Copybara PR Handler / close-imported-pr (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / Pre-commit Linter (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.10) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.11) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.12) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Has been cancelled
`_is_session_disconnected` reads `session._read_stream._closed` and
`session._write_stream._closed`. Four attribute reads, all four private to the
MCP SDK, none of them promised.

The probe is not the only liveness signal. `create_session` pairs it with
`SessionContext._is_task_alive`, which ADK owns and which catches strictly
more: a crashed transport can leave both streams open while the task behind
them is already dead. So a missing attribute has a sensible answer -- treat
the session as connected and let the task check decide -- and no reason to
take down the call with an `AttributeError`.

Read the four defensively and say in the docstring where liveness actually
comes from. No behaviour change while the SDK keeps the streams: a closed
stream still reports disconnected, and either stream counts.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 968726270
2026-08-21 15:33:58 -07:00
Aarav Mittal e577c301d5 feat(cli): Support Cloud Build worker pools for Agent Engine deploy
Merge https://github.com/google/adk-python/pull/6701

## Summary

Implements **Cloud Build private worker pool** support for `adk deploy agent_engine` ([#2141](https://github.com/google/adk-python/issues/2141)).

### Why this is needed
Enterprise / VPC-SC Agent Engine deploys often **cannot use the default public Cloud Build pool**. Without a way to point the build at a private worker pool, `adk deploy agent_engine` fails for teams that require private networking, org build policies, or connectivity to private resources.

The Vertex Agent Engine SDK already accepts this via `config.build_config.worker_pool` → `spec.build_spec.worker_pool`, but ADK never exposed it on the CLI or documented a first-class config key.

### What we did
- Added `--worker_pool` to `adk deploy agent_engine`
- Accepted the same value from `.agent_engine_config.json` as either:
  - top-level `"worker_pool": "projects/.../workerPools/..."` (convenience), or
  - `"build_config": {"worker_pool": "..."}` (native SDK shape)
- Nested the value into `agent_config["build_config"]["worker_pool"]` before `client.agent_engines.update(...)`
- Validated the Cloud Build resource name format early with a clear error
- Preserved other `build_config` fields (e.g. build `service_account`)
- CLI flag overrides config-file values (same pattern as `display_name`)

### How it fits
```
adk deploy agent_engine --worker_pool=...
        │
        ▼
to_agent_engine(... worker_pool=...)
        │
        ▼
agent_config["build_config"]["worker_pool"] = <resource name>
        │
        ▼
vertexai.Client().agent_engines.update(config=agent_config)
        │
        ▼
Cloud Build runs on the private worker pool
```

### Usage
```bash
adk deploy agent_engine \
  --project=my-project \
  --region=us-central1 \
  --worker_pool=projects/my-project/locations/us-central1/workerPools/my-private-pool \
  my_agent
```

Or in `.agent_engine_config.json`:
```json
{
  "worker_pool": "projects/my-project/locations/us-central1/workerPools/my-private-pool"
}
```

### Verification
- Confirmed `worker_pool` was **not** previously implemented in ADK (`rg` / deploy path audit)
- Confirmed Vertex SDK mapping in `vertexai/_genai/agent_engines.py` (`build_config.worker_pool` → `spec.build_spec.worker_pool`)
- Added unit tests for validation, config nesting, CLI passthrough, and deploy config forwarding
- `uv run pytest tests/unittests/cli/utils/test_cli_deploy.py` → **60 passed**

Fixes #2141

## Test plan
- [x] Unit tests for `_validate_worker_pool` (valid + malformed)
- [x] Unit tests for `_apply_worker_pool_to_agent_config` (CLI, config top-level, override, preserve other build_config fields)
- [x] `to_agent_engine` forwards `build_config.worker_pool` on `agent_engines.update`
- [x] CLI `--worker_pool` reaches `to_agent_engine`
- [ ] Maintainer review of CLI naming / config-file shape
- [ ] Optional: end-to-end deploy against a real private worker pool in a VPC-SC project

---

cc @klateefa @yeesian @wuliang229 @Jacksunwei @hangfei @llalitkumarrr @GWeale

I claimed this on [#2141](https://github.com/google/adk-python/issues/2141#issuecomment-5274468133) (self-assign needs triage permissions — please assign me `@a2105z` if that helps routing). Ready for review whenever you have a moment.

Co-authored-by: Yifan Wang <wanyif@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/6701 from a2105z:feat/agent-engine-worker-pool c59661ae1c2e244037e85f5b0a577b9cff70197b
PiperOrigin-RevId: 968673077
2026-08-21 13:52:52 -07:00
George Weale e3ae4ac2b4 fix(sessions): stop dropping event actions on v0 PostgreSQL migration
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 968671770
2026-08-21 13:51:31 -07:00
George Weale c0614d6580 feat: record context cache state on the LLM call span
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 968671743
2026-08-21 13:50:38 -07:00
jun weijia 26110c7559 fix: reject negative recent-event limits
Merge https://github.com/google/adk-python/pull/6687

Unsupported negative limits should fail at configuration time, before a session backend handles them.

PiperOrigin-RevId: 968610704
2026-08-21 11:46:22 -07:00
Kathy Wu d18df2fa1c fix: detect MCP tool errors under either field spelling
`_detect_error_in_response` reads `isError` off the dumped `CallToolResult`.
MCP SDK 1.x names that field `isError`, so the lookup works. 2.x renames it to
`is_error`, so `model_dump` emits the snake_case key and the lookup returns
`None` for every result. Tool errors would stop being reported to telemetry,
with nothing in the logs to say so.

`_get_declaration` has the same problem one step earlier: it reads
`inputSchema` and `outputSchema`, which 2.x renames and removes. That one at
least fails loudly with an `AttributeError`.

Read both spellings. `_read_field` returns the first attribute a model
defines, so it picks up the camelCase name on 1.x and the snake_case name on
2.x, and raises a named error if a future release renames the field again.

`model_dump(by_alias=True)` would have been a smaller change, but it is not
equivalent: every MCP result model aliases `meta` to `_meta`, so dumping by
alias would rename that key in the payload ADK returns to the caller. The
casing of the returned dict is left alone here and belongs with the SDK
upgrade itself.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 968586089
2026-08-21 10:59:07 -07:00
Kathy Wu 574da43d65 refactor: build the MCP read timeout in one place
`SessionContext._start` builds `ClientSession`'s `read_timeout_seconds` twice,
once for stdio and once for SSE and streamable HTTP, each with its own inline
`timedelta(...) if ... is not None else None`.

Move that to `_read_timeout`. ADK carries every timeout as float seconds and
now converts once, at the boundary where the SDK is called.

This is a plain de-duplication today. It also isolates a difference between
MCP SDK versions: 1.x types `read_timeout_seconds` as a `timedelta` and 2.x
types it as a float, so the conversion is the only line that has to change.

The explicit `is None` check is deliberate. A zero timeout is a real value, not
a missing one, and a truthiness check would silently turn it into "no timeout".

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 968580764
2026-08-21 10:49:05 -07:00
Liang Wu 98896eb2aa feat(live): let a live streaming tool send messages to the user directly
A streaming tool used to only talk to the model: every value it yielded came
back as a FunctionResponse. Therefore it costs model context and could derail the model's reasoning.

An Event with message yielded by a streaming tool is now addressed to the user instead. It is enqueued on the invocation's event queue, so the runner appends it to the session and streams it to the client, and it is never sent over the live model
connection. Plain values are still sent to the model as FunctionResponse. A tool can mix and match any number of each, in any order.

Runner's run_live also now initializes the invocation's event queue and merges it with the live agent's own event stream. Without a queue, anything running under
the live agent that enqueues an event -- a streaming tool, or a node -- fails
with "_event_queue is not set".

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 968533249
2026-08-21 09:15:04 -07:00
chelsealong 775c1bd36e feat: support opt-in session retention in MultimodalToolResultsPlugin
Merge https://github.com/google/adk-python/pull/6698

Adds a `retention` parameter to `MultimodalToolResultsPlugin` to allow
tool-returned parts to persist across turns in a session.

Fixes #6695

PiperOrigin-RevId: 968187977
2026-08-20 18:57:27 -07:00
Google Team Member 77cfe4349d refactor: simplify default max LLM calls configuration
PiperOrigin-RevId: 968166903
2026-08-20 18:03:21 -07:00
Kathy Wu 3c977bc2ef fix(skills): skip search results that fail frontmatter validation
`GCPSkillRegistry.search_skills` built a `Frontmatter` for every hit with no
per-item error handling. `Frontmatter.name` must be kebab-case (or snake_case
behind the feature flag), but the catalog holds names outside that set -- the
first-party entry `cloud.google.com-agent-platform-eval-flywheel` has dots. The
first such hit raised a pydantic `ValidationError` and took down the whole
call, so search returned nothing at all against any catalog that holds one
non-conforming entry. An entry with an empty description did the same.

Skip the entry and log a warning instead. The caller does not control what the
catalog holds, so one entry it never asked about must not break discovery for
everything else.

A name that is not a string gets the same treatment. `.split` on it raises
before validation is reached, which would sink the call the same way.

Fixes #6838

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 968161389
2026-08-20 17:49:58 -07:00
garyzava dac18699b9 fix: honor before_run_callback early-exit for Workflow runs
Merge https://github.com/google/adk-python/pull/6032

In the Workflow run path, capture the before_run_callback result and early exit if it is Content, matching the non-workflow path behavior.

Fixes #6013

PiperOrigin-RevId: 968159218
2026-08-20 17:45:20 -07:00
brucearctor a01d516a6b feat: support injecting custom LLM clients into Gemini and AnthropicLlm
Merge https://github.com/google/adk-python/pull/5035

Enable injecting pre-configured LLM clients into Gemini and AnthropicLlm models to support multi-agent systems with distinct configurations.

Fixes #5027

PiperOrigin-RevId: 968151995
2026-08-20 17:29:45 -07:00
Google Team Member 75679db3fa feat: add ADK_MAX_LLM_CALLS environment variable to configure max LLM calls limit
Continuous Integration / Pre-commit Linter (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Has been cancelled
Copybara PR Handler / close-imported-pr (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.10) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.11) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.12) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.13) (push) Has been cancelled
PiperOrigin-RevId: 968115816
2026-08-20 16:14:11 -07:00
Xuan Yang 9a32eba1e2 fix: revert the A2A guard that broke every HITL tool confirmation
Reverts #6462. An agent served over A2A could no longer satisfy a
human-in-the-loop tool confirmation at all: the request converter stamped
`a2a_metadata` on every inbound invocation unconditionally, and the
confirmation processor returned early whenever that key was present, dropping
the operator's legitimate approval along with a forged one. Where A2A is the
only channel reaching the human operator, every tool requiring confirmation was
permanently blocked.

The guard also did not close the vector it targeted: #6461 notes `/run` and
`/run_sse` are unauthenticated by default, so the same forged approval still
lands over plain HTTP. Gating on the transport rather than on the identity of
the approving principal is at once too broad and too narrow.

Reopens #6461.

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 968106938
2026-08-20 15:58:14 -07:00
Shangjie Chen a7bf3d7f49 chore: Consolidate other agent presentation and fencing helpers into _fencing.py
Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 968098131
2026-08-20 15:42:00 -07:00
Shangjie Chen 1d89e0ff8d docs(guides): add developer unit guide for Runner and Runner Live Streaming
Add granular developer guide for BaseRunner and InMemoryRunner covering session
lifecycle resolution, RunConfig, and event streaming under
docs/guides/runners/runner/index.md, and real-time bidirectional streaming with
run_live and LiveRequestQueue under docs/guides/runners/runner/live.md.

Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 968085075
2026-08-20 15:18:44 -07:00
Kathy Wu d6954946df feat: resolve auth for RemoteA2aAgent in AgentRegistry
`AgentRegistry.get_mcp_toolset` already derives an `auth_scheme`/`auth_credential`
pair from a registered resource's auth provider binding, but
`get_remote_a2a_agent` ignored bindings entirely. A caller who registered an A2A
agent behind an auth provider had no way to reach it: the returned
`RemoteA2aAgent` sent unauthenticated requests, and the caller could not supply
a credential either.

Accept `auth_scheme`, `auth_credential` and `continue_uri` on
`get_remote_a2a_agent` and forward the pair to `RemoteA2aAgent`. When no scheme
is passed, resolve the agent's `authProviderBinding` into a
`GcpAuthProviderScheme`. The signature and the resolution path mirror
`get_mcp_toolset`, so both registered resource types authenticate the same way.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 968076388
2026-08-20 15:04:17 -07:00
Google Team Member 393ec0858b feat: add location parameter to list_accessible_data_agents in data_agent toolset
Allow callers to explicitly specify the Google Cloud location when listing data agents, following standard three-level precedence (explicit argument, toolset config location, falling back to global).

PiperOrigin-RevId: 968067492
2026-08-20 14:48:51 -07:00
Kathy Wu d42c634bd6 feat: support auth_scheme and auth_credential in RemoteA2aAgent
`RemoteA2aAgent` could not authenticate its calls; a caller had to bake a static
token into a custom `httpx_client`. Every other ADK component with a remote
endpoint takes an `auth_scheme`/`auth_credential` pair.

Accept that pair and an optional `credential_key`. `CredentialManager` resolves
the credential once per invocation, and the headers go on the card fetch and the
message send; with nothing to send, the agent emits `adk_request_credential`.
The interceptors and the derived key are per agent, so one agent's token cannot
reach another agent's host.

`build_auth_headers` also stops sending `Bearer None` for a tokenless OAuth2
credential. The interactive round trip needs an `LlmAgent` parent; the
`AgentRegistry` path resolves server-side.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 968025847
2026-08-20 13:38:19 -07:00
Shangjie Chen af653512ce refactor(llmflows): simplify contents processor by extracting compaction
**Problem:**
contents.py was over 1,400 lines and mixed high-level LLM request content assembly with low-level event compaction filtering and missing function-call recovery logic.

**Solution:**
Extract _process_compaction_events and _recover_compacted_function_calls into a private helper module _content_compaction.py, preserving re-exports in contents.py for backward compatibility, and move corresponding unit tests into test__content_compaction.py.

Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 967989388
2026-08-20 12:38:13 -07:00
ftnext dc735bd953 fix(cli): Preserve non-ASCII text in adk test --rebuild, Web UI test saving, and CLI JSONL
Merge https://github.com/google/adk-python/pull/6550

### Link to Issue or Description of Change

**1. Link to an existing issue (if applicable):**

N/A

**2. Or, if no issue exists, describe the change:**

**Problem:**

`adk test --rebuild`, `dev_server.py` (ADK Web test creation), and `cli.py` (JSONL stream output) write/output JSON using `json.dump` / `json.dumps` with the default `ensure_ascii=True` and without explicit UTF-8 encoding.
As a result, Japanese and other non-ASCII event text is converted to `\uXXXX` escape sequences, making test fixtures and CLI outputs difficult to read and review.

**Solution:**

- Write rebuilt and saved test fixtures as UTF-8 with `ensure_ascii=False`.
- Output CLI JSONL events and schemas with `ensure_ascii=False`.
- Add regression unit tests verifying non-ASCII text preservation in rebuilt tests, web server test creation, and CLI event printing.

### Testing Plan

**Unit Tests:**

- [x] I have added or updated unit tests for my change.
- [x] All relevant unit tests pass locally.

### Checklist

- [x] I have read the [CONTRIBUTING.md](https://github.com/google/adk-python/blob/main/CONTRIBUTING.md) document.
- [x] I have performed a self-review of my own code.
- [x] I have commented my code, particularly in hard-to-understand areas.
- [x] I have added tests that prove my fix is effective or that my feature works.
- [x] New and existing relevant unit tests pass locally with my changes.
- [ ] I have manually tested my changes end-to-end.
- [x] Any dependent changes have been merged and published in downstream modules. (N/A: no dependent changes.)

### Additional context

No public APIs or fixture schemas are changed. Rebuilt files remain JSON-compatible; only the textual representation of non-ASCII characters changes.

Co-authored-by: Yi Liu <yiliuly@google.com>
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/6550 from ftnext:preserve-unicode-in-rebuilt-tests c9a1197c7c5ded4afed86279acfcc2025135fd9c
PiperOrigin-RevId: 967917608
2026-08-20 10:43:26 -07:00
Haran Rajkumar 4599a52659 feat(antigravity): capture client-side tool outcomes for a function_response
A client-side tool's outcome never reaches the trajectory, so an ADK caller
saw the function_call and never the function_response. This adds the buffer
that captures the outcome from the SDK's post-tool-call and on-tool-error
hooks, and the converter that drains it into a matching function_response
event. Nothing calls it yet.

Co-authored-by: Haran Rajkumar <haranrk@google.com>
PiperOrigin-RevId: 967595598
2026-08-19 22:43:05 -07:00
chelsealong e4ba7040fb fix: make SqliteSessionService state merges use dict.update() semantics
Merge https://github.com/google/adk-python/pull/6729

Fixes #6728

PiperOrigin-RevId: 967540198
2026-08-19 19:58:44 -07:00
Shangjie Chen c986ff0fce fix: prevent duplicate function execution when support_cfc is enabled
When support_cfc=True is used in run_async, delegate to run_live directly to prevent duplicate tool execution.

Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 967506529
2026-08-19 18:04:44 -07:00
George Weale c244a9c833 perf: run local code execution in a plain child interpreter
UnsafeLocalCodeExecutor ran each program as a multiprocessing spawn child,
which had to import this package before it could run a single line, costing
about 2.3 seconds per execution. It now runs the program in a plain child
interpreter, the shape ContainerCodeExecutor already uses, which brings a
trivial program down to about 35 milliseconds. The result now comes from the
child's exit status and pipes rather than a queue the child has to write to,
so a program that dies without reporting anything no longer leaves the agent
waiting forever, and the traceback the model is shown no longer opens with a
frame from inside this package.

One behavior change follows from taking the result from the exit status: a
program calling sys.exit(0) is now reported as having succeeded. It was
previously reported as a failure, because the spawn child raised SystemExit
before it could write to the result queue.

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967496975
2026-08-19 17:39:16 -07:00
Ishaan ac8dad2580 fix: strip internal planning tags from PlanReActPlanner output
Merge https://github.com/google/adk-python/pull/6709

References #3378

PiperOrigin-RevId: 967495179
2026-08-19 17:34:13 -07:00
MarkHe1222 ffe518aff0 docs: fix typos and grammar in README
Merge https://github.com/google/adk-python/pull/6724

PiperOrigin-RevId: 967480759
2026-08-19 16:56:37 -07:00
Google Team Member 3f2d399cd9 fix: resolve server disconnect logs
PiperOrigin-RevId: 967479906
2026-08-19 16:55:28 -07:00
Dani Zamora 17cb2657ce docs: add audio_stream_end documentation for realtime input
Merge https://github.com/google/adk-python/pull/6768

PiperOrigin-RevId: 967479785
2026-08-19 16:54:13 -07:00
George Weale deee6d2c47 fix(flows): pair a function response with the call it answers
Close #6761

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967478519
2026-08-19 16:51:19 -07:00
Kathy Wu 2aea8595fb fix: stop RemoteA2aAgent forwarding credential requests to the remote peer
`_construct_message_parts_from_session` rebuilds the outgoing message from
the session history. It drops `function_response` parts that carry
credential material, but not `function_call` parts. An
`adk_request_credential` call carries a serialized `AuthConfig` in its
arguments, including `raw_auth_credential` (an OAuth2 client secret or a
service account key), and `BaseLlmFlow` appends such an event to the session
whenever a toolset asks the client for a credential. A `RemoteA2aAgent` in
that session then replays the secret to the remote peer.

Drop credential-bearing `function_call` parts too. The scrub runs before
`_present_other_agent_message`, which renders a `function_call` as text with
its arguments inlined, so a later scrub would be too late.

A call counts as credential-bearing by name, or by shape when the name is not
one we know. Only `adk_request_credential` counts by name; the mock auth call
is left alone, because its args hold the peer's own prompt and it stands in for
the peer's last text part. The shape check reads the AuthConfig out of the
`authConfig` field of the AuthToolArguments envelope. A response carries the
AuthConfig flat, so the response-side check reads the top level. Reading the
top level of a request would match nothing, and would drop any ordinary call
that takes an `auth_scheme` argument.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 967478046
2026-08-19 16:50:09 -07:00
George Weale b4a9acbcb9 test(sessions): run the shared contract tests against more backends
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967478023
2026-08-19 16:49:07 -07:00
Kathy Wu 4e68bad199 refactor: key the MCP agent-server session map on the connection
`to_mcp_server` keeps one ADK session per MCP connection, so successive tool
calls on that connection form a single conversation. It keyed that map on
`ctx.session`.

That key is correct on MCP SDK 1.x, where the server builds one session object
per connection. It is wrong on 2.x: the server builds a fresh `ServerSession`
for every inbound request and holds the connection on the session's private
`_connection`. The key would change on every call, so every tool call would
start a new conversation. Nothing raises. The agent just forgets.

Route the key through `_connection_key`, which reads `_connection` when the SDK
provides it and falls back to the session when it does not. That gives one key
per connection on both versions, and leaves 1.x behaviour unchanged.

The fallback degrades to one session per request on purpose. It must not fall
back to an object shared by all connections, because separate clients would
then share one conversation.

The private attribute is a stopgap. SDK 2.x already defines a public
`mcp.server.context.Context.connection`, but the server does not hand that
class to tool functions yet, so a tool's `Context` has no public route to its
connection.

Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 967462559
2026-08-19 16:14:37 -07:00
Kathy Wu 3819b4e16c chore(integrations): send the ADK client label on registry API requests
Co-authored-by: Kathy Wu <wukathy@google.com>
PiperOrigin-RevId: 967455221
2026-08-19 15:59:59 -07:00
Google Team Member 66908e4c61 fix: count tool call and response chars in compaction
PiperOrigin-RevId: 967437247
2026-08-19 15:20:52 -07:00
George Weale 69a3ca5e11 fix(mcp): evict idle sessions from the MCP session pool
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967426735
2026-08-19 14:58:49 -07:00
Shangjie Chen b370fc00d1 docs(guides): add developer unit guide for BaseCodeExecutor
Add granular developer guide for BaseCodeExecutor covering architecture,
execution flow, backend environments (BuiltIn, UnsafeLocal, Container,
GKE gVisor, Vertex AI, Agent Engine), delimiter extraction, and error
retry loops under docs/guides/code_executors/code_executor/index.md.

Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 967423934
2026-08-19 14:54:33 -07:00
Shangjie Chen f3250bd965 fix: Fix built-in search tools in agent hierarchy when transfer_to_agent is present
Gemini models reject requests that combine built-in search tools (such as
GoogleSearchTool or VertexAiSearchTool) with function declarations (such as
transfer_to_agent) with "400 INVALID_ARGUMENT: Tool use with function calling
is unsupported".

This change addresses this in two ways:
1. When built-in search tools (GoogleSearchTool / VertexAiSearchTool) have
   `bypass_multi_tools_limit=True` in an agent hierarchy, `multiple_tools`
   now accounts for transfer targets so the tool is converted to its
   function-tool equivalent (GoogleSearchAgentTool / DiscoveryEngineSearchTool),
   allowing it to cleanly coexist with `transfer_to_agent`.
2. When built-in search tools are used without bypass in an agent hierarchy,
   `_AgentTransferLlmRequestProcessor` skips injecting `transfer_to_agent`
   and transfer instructions, ensuring only the built-in search tool is sent
   to the model without conflict.

Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 967423502
2026-08-19 14:52:55 -07:00
George Weale 2aa2b469b0 perf: run the Google credential refresh off the event loop
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967420389
2026-08-19 14:46:52 -07:00
George Weale 5b59139e0e fix(workflow): resume node auth from the node's own auth config
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967419219
2026-08-19 14:44:21 -07:00
George Weale 3f9e6bec37 fix: sanitize state deltas before writing them to JSON state columns
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967414738
2026-08-19 14:37:13 -07:00
George Weale f3b59fd62e fix(workflow): declare abc.ABC on BaseNode so abstract subclasses type-check
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967414592
2026-08-19 14:36:39 -07:00
George Weale 51231cd4ac perf: key the Pub/Sub publisher cache on its options' value
Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967414460
2026-08-19 14:35:16 -07:00
George Weale 94475c9a76 fix(artifacts): Publish file artifact versions atomically
FileArtifactService chose the next version by listing the versions directory
and then created that directory as a separate step. Two saves racing on the
same artifact read the same version list, and the loser's mkdir raised
FileExistsError. An interrupted save was worse: the version directory existed
but held no payload, and because version discovery counts any integer-named
directory, that empty directory became the latest version and load_artifact
returned None instead of the previous good content.

A save now reserves a version by creating a hidden `.{version}.pending`
directory, which mkdir makes atomic and mutually exclusive, writes the payload
and metadata inside it, and publishes the result with a single os.replace onto
`versions/{version}`. Version discovery ignores the staging directories, so a
version becomes visible only once it is complete, and a save that raises
removes its own staging directory. A save whose version list went stale
re-checks before publishing and takes the next free version instead, so
os.replace always lands on a name that does not exist. That last part matters
on Windows, where os.replace maps to MoveFileEx and reports an error when the
destination names an existing directory.

The guarantee is scoped to process-level faults. An exception or a signal never
publishes a partial version, but nothing is fsynced, so a power loss can still
make the rename durable ahead of the file contents and expose an empty
`versions/{version}`. Full durability would need explicit fsyncs and is not
addressed here, and version directories that earlier releases already left
empty are not repaired.

Behavior change: published version numbers are no longer guaranteed to be
contiguous. A reservation abandoned by a signal or a host failure leaves its
`.{version}.pending` directory behind; that directory is never read and never
published, but it holds its version number permanently. There is deliberately
no in-process sweep, because nothing distinguishes an abandoned reservation
from a slow concurrent write, so reclaiming those directories is left to the
operator. The class-level layout comment records the staging directory and the
non-contiguity.

The public artifact-service API and the completed on-disk version layout are
unchanged.

Co-authored-by: George Weale <gweale@google.com>
PiperOrigin-RevId: 967413806
2026-08-19 14:33:45 -07:00
Liang Wu 7616c78a1e fix: trigger Gemini 3.x Live response after sending conversation history
Gemini 3.x Live does not begin generating from `send_client_content` alone; it
starts only once it receives realtime input. As a result, seeding a new live
connection with prior conversation history left the model silent until the user
spoke again. This is most visible right after an agent transfer, where the
sub-agent is expected to reply immediately.

`GeminiLlmConnection.send_history` now follows the replayed history with a
minimal placeholder realtime input for Gemini 3.x Live models, gated on the
history ending with a user turn (the same condition that sets `turn_complete`).
Histories that end on a model turn still leave the model waiting for new user
input, preserving the documented `send_history` behavior. Other model families
are unaffected.

Co-authored-by: Liang Wu <wuliang@google.com>
PiperOrigin-RevId: 967411170
2026-08-19 14:28:55 -07:00
Shangjie Chen ea23a89d73 docs(guides): add developer unit guide for BasePlanner
Add granular developer guide for BasePlanner covering planning system
instructions, ThinkingConfig integration, PlanReActPlanner structured
reasoning tags, and thought partitioning under
docs/guides/planners/planner/index.md.

Co-authored-by: Shangjie Chen <deanchen@google.com>
PiperOrigin-RevId: 967402627
2026-08-19 14:12:50 -07:00
Chris Kinzel bb86bdd737 fix: prevent contextvars leak across async generators
Merge https://github.com/google/adk-python/pull/5725

Fixes #5722

PiperOrigin-RevId: 967359464
2026-08-19 12:53:29 -07:00
Ashutosh0x 8d2f2779e6 fix: validate SQL identifiers in Spanner search tool
Merge https://github.com/google/adk-python/pull/5952

Fixes #5913

PiperOrigin-RevId: 967358801
2026-08-19 12:52:20 -07:00