Skip to content

Commit 711d21b

Browse files
authored
Merge branch 'main' into caller-principal-gate
2 parents 1d8b615 + 377306e commit 711d21b

448 files changed

Lines changed: 36294 additions & 4669 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/skills/adk-agent-builder/references/function-nodes.md‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ worker = node(some_func, parallel_worker=True)
152152
```
153153

154154
Accepted keywords: `name`, `rerun_on_resume`, `retry_config`, `timeout`,
155-
`parallel_worker`, `max_parallel_workers`, `auth_config`, `parameter_binding`.
155+
`parallel_worker`, `max_parallel_workers`, `auth_config`,
156+
`require_confirmation`, `parameter_binding`.
156157

157158
## Routing and state from a node
158159

@@ -166,6 +167,25 @@ def update_counter(node_input: str):
166167
return Event(output=node_input, state={'last_input': node_input})
167168
```
168169

170+
## Requiring confirmation before a node runs
171+
172+
`require_confirmation` pauses the workflow with an `adk_request_input`
173+
interrupt (`wf_tool_confirmation:{node_path}`) before the function runs. It
174+
accepts a `bool` or a sync/async predicate callable receiving the function's
175+
bound arguments (plus `ctx` or `node_input` if declared). `@node` automatically
176+
defaults `rerun_on_resume=True` when `require_confirmation` is set.
177+
178+
```python
179+
@node(require_confirmation=True)
180+
def delete_file(path: str) -> str:
181+
return f'Deleted {path}'
182+
183+
184+
@node(require_confirmation=lambda amount: amount > 1000)
185+
def transfer_funds(amount: int) -> str:
186+
return f'Transferred {amount}'
187+
```
188+
169189
## Requiring authentication before a node runs
170190

171191
`auth_config` makes the framework request user credentials before the node's

‎.agents/skills/adk-architecture/references/api-principles.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ The public API surface of ADK includes:
1818
- ADK adheres to Semantic Versioning 2.0.0.
1919
- Any change that forces a developer to alter their existing code to upgrade is a **breaking change** and necessitates a MAJOR version bump.
2020
- Avoid breaking changes whenever possible by using optional parameters and deprecation cycles.
21+
- **Session, Artifact, and Memory Service Changes**:
22+
Adding any new field to `Session`, `ArtifactVersion`, `MemoryEntry`, or to their associated service base classes (`BaseSessionService`, `BaseArtifactService`, `BaseMemoryService`) and database schemas is strictly considered a **breaking change** (even if the new field is optional or provides a default value).
23+
- *Rationale*: There are numerous downstream implementations and custom subclasses across diverse storage backends (e.g. relational databases, key-value stores, document databases, external cloud backends). Downstream storage implementations do not automatically persist, deserialize, or round-trip fields they were not compiled or migrated to handle, leading to silent state loss or deserialization errors when sessions or memories are reloaded.
2124

2225
### 2. Self-Containment
2326
- Each package should be as self-contained as possible to reduce coupling.

‎.agents/skills/adk-review/SKILL.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ Confirm the changed files pass `pre-commit run --files {paths}`.
7474
`src/google/adk/`? A breaking change needs a deprecation cycle first, because
7575
the package is released under Semantic Versioning and users pin minor
7676
versions.
77+
- **Session, Artifact, and Memory service changes**: adding any new field to
78+
`Session`, `ArtifactVersion`, `MemoryEntry`, or to `BaseSessionService`,
79+
`BaseArtifactService`, `BaseMemoryService` and their schemas is a **breaking
80+
change** (even if optional or defaulted), because existing downstream storage
81+
subclasses and external backends will not persist or deserialize unknown
82+
fields, causing silent state loss upon reload.
7783
- **Execution and resumption**: changes to workflows, nodes, or state must stay
7884
compatible with the event execution lifecycle and with session resumption
7985
(human-in-the-loop steps and checkpoints). See the `adk-architecture` skill.

‎.agents/skills/adk-setup/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ every dependency extra, pre-commit hooks, and a green unit-test run.
2020

2121
## Prerequisites
2222

23-
1. **Python.** ADK supports 3.11 through 3.14 (`requires-python = ">=3.11"` in
23+
1. **Python.** ADK supports 3.11 through 3.15 (`requires-python = ">=3.11"` in
2424
`pyproject.toml`). These steps use 3.11, the version the repo's own tooling
2525
defaults to.
2626

‎.agents/skills/adk-style/references/pydantic.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,20 @@ and defaults `model_dump_json()` to `by_alias=True`, so Python stays
7171
snake_case while the wire format stays camelCase without every call site
7272
remembering to pass `by_alias`.
7373

74+
### Never Pollute Persisted Models with Transient Flags
75+
76+
Do not add transient control or bookkeeping fields (such as `NodeInfo.flush`
77+
or temporary routing flags) as serialized fields on persisted models (`Event`,
78+
`NodeInfo`, `Session`, `Content`).
79+
80+
- For in-memory state on a model instance, use `PrivateAttr()`.
81+
- For ADK-internal event metadata that must survive storage across a
82+
pause/resume boundary without being writable by external callers or exposed
83+
in public responses, use `google.adk.events._internal_metadata`
84+
(`INTERNAL_METADATA_PREFIX = '__adk_internal_'`,
85+
`without_internal_metadata()`, `public_metadata()`, `internal_metadata()`,
86+
and `public_event()`).
87+
7488
## `field_validator` — Single-Field Validation
7589

7690
Use `@field_validator` when a constraint needs logic that `Field()` cannot

‎.agents/skills/adk-style/references/testing.md‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,14 @@ tests/unittests/models/test_llm_request.py # LlmRequest messages
232232
tests/unittests/test_runners.py # Runner messages
233233
```
234234

235+
### 11. Parameterize variations instead of copy-pasting tests
236+
237+
When verifying the same contract across a matrix of modes or configurations
238+
(such as `AgentMode.SINGLE_TURN` vs `AgentMode.CHAT`, `include_contents='none'`
239+
vs `'default'`, or resumable vs non-resumable apps), use
240+
`@pytest.mark.parametrize` on a single focused test rather than duplicating the
241+
test body across multiple functions.
242+
235243
### Test Structure Template
236244

237245
```python
@@ -262,16 +270,20 @@ def test_<behavior_description>():
262270

263271
## ADK Test Fakes
264272

265-
Use the fakes in `tests/unittests/testing_utils.py` instead of patching ADK
266-
internals:
273+
Use the fakes in `tests/unittests/testing_utils.py` (or
274+
`tests/unittests/workflow/testing_utils.py` for workflow tests) instead of
275+
patching ADK internals or constructing a bare `google.adk.runners.Runner`:
267276

268277
- `testing_utils.MockModel.create(responses=[...])` builds a model that
269278
replies with canned responses in order and records each request in
270279
`.requests`.
271-
- `testing_utils.InMemoryRunner(root_agent=agent)` runs an agent on in-memory
272-
services; `await runner.run_async('hi')` returns the events, and
273-
`testing_utils.simplify_events(events)` reduces them to `(author, content)`
274-
pairs.
280+
- `testing_utils.InMemoryRunner(root_agent=agent)` (or `node=wf` in
281+
`workflow/testing_utils.py`) and `testing_utils.TestInMemoryRunner` run an
282+
agent or workflow on in-memory services and automatically attach
283+
`InvariantPlugin` (`check_invariants=True`) to validate event-stream
284+
invariants on every turn. `await runner.run_async('hi')` returns the events,
285+
and `testing_utils.simplify_events(events)` reduces them to
286+
`(author, content)` pairs.
275287

276288
Request the `env_variables` fixture from `conftest.py` to run a test against
277289
both the Gemini API and Vertex AI.

‎.agents/skills/adk-style/references/visibility.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ Since Python lacks true package-private access, we simulate it by:
3232
- **Only public names** (symbols intended for use outside the package) should be imported into `__init__.py` and listed in `__all__`.
3333
- Users should be able to import public symbols directly from the package level, rather than digging into internal modules.
3434

35+
### 5. Implicit Workflow Wrapper Nodes Stay Private
36+
37+
- A node class whose only job is to wrap an object the user already holds (such as `_ToolNode` for a `BaseTool`, `_ToolsetNode` for a `toolset['name']` descriptor, or `_LlmAgentWrapper` for a `BaseAgent`) must stay private (`_`-prefixed) and must **not** be exported from `google.adk.workflow`.
38+
- Users pass `NodeLike` targets directly into `Workflow(edges=...)` or `ctx.run_node(...)` (which normalize them via `build_node`), and configure per-node overrides (`name`, `rerun_on_resume`, `retry_config`, `timeout`) through the public `node()` helper (e.g., `node(toolset['read_file'], name='reader', timeout=30)`).
39+
3540
## Examples
3641

3742
### Exposing a Public Interface
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "2.11.0"
2+
".": "2.12.0"
33
}

‎.github/release-please-config.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,5 +57,5 @@
5757
]
5858
}
5959
},
60-
"last-release-sha": "731ea30ecb63ea54ea1b51a81635b3016e9c8f55"
60+
"last-release-sha": "e9b74d36140f2594b24c481fb7a3825b2cb01e96"
6161
}

‎.github/workflows/copybara-pr-handler.yml‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,55 @@ jobs:
158158
}
159159
160160
console.log(`Marked PR #${prNumber} as merged (was ${pr.data.state})`);
161+
162+
// Auto-close issues linked via closing keywords in PR body
163+
const prBody = pr.data.body || '';
164+
const escapedOwner = context.repo.owner.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
165+
const escapedRepo = context.repo.repo.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
166+
const issueClosingRegex = new RegExp(
167+
`\\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?)\\s*:?\\s*(?:https:\\/\\/github\\.com\\/${escapedOwner}\\/${escapedRepo}\\/issues\\/|(?:${escapedOwner}\\/${escapedRepo})?#)(\\d+)`,
168+
'gi'
169+
);
170+
const linkedIssues = new Set();
171+
let match;
172+
while ((match = issueClosingRegex.exec(prBody)) !== null) {
173+
linkedIssues.add(parseInt(match[1], 10));
174+
}
175+
176+
for (const issueNumber of linkedIssues) {
177+
try {
178+
const issue = await github.rest.issues.get({
179+
owner: context.repo.owner,
180+
repo: context.repo.repo,
181+
issue_number: issueNumber
182+
});
183+
184+
if (issue.data.pull_request) {
185+
continue;
186+
}
187+
188+
if (issue.data.state === 'open') {
189+
await github.rest.issues.createComment({
190+
owner: context.repo.owner,
191+
repo: context.repo.repo,
192+
issue_number: issueNumber,
193+
body: `Resolved by #${prNumber} (imported and merged in commit ${commitSha}). Closing issue.`
194+
});
195+
196+
await github.rest.issues.update({
197+
owner: context.repo.owner,
198+
repo: context.repo.repo,
199+
issue_number: issueNumber,
200+
state: 'closed',
201+
state_reason: 'completed'
202+
});
203+
204+
console.log(`Automatically closed linked issue #${issueNumber}`);
205+
}
206+
} catch (issueError) {
207+
console.log(`Could not process linked issue #${issueNumber}:`, issueError.message);
208+
}
209+
}
161210
} catch (error) {
162211
console.log(`Error marking PR #${prNumber} as merged:`, error.message);
163212
}

0 commit comments

Comments
 (0)