@google/adk 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/a2a/a2a_remote_agent.js +12 -2
- package/dist/cjs/a2a/agent_to_a2a.js +0 -1
- package/dist/cjs/a2a/auth.js +65 -0
- package/dist/cjs/a2a/event_converter_utils.js +40 -15
- package/dist/cjs/agents/functions.js +20 -7
- package/dist/cjs/agents/llm_agent.js +2 -1
- package/dist/cjs/agents/processors/basic_llm_request_processor.js +3 -2
- package/dist/cjs/agents/processors/content_request_processor.js +2 -21
- package/dist/cjs/agents/processors/instructions_llm_request_processor.js +3 -1
- package/dist/cjs/artifacts/in_memory_artifact_service.js +10 -9
- package/dist/cjs/auth/credential_service/in_memory_credential_service.js +14 -3
- package/dist/cjs/code_executors/unsafe_local_code_executor.js +23 -12
- package/dist/cjs/common.js +6 -0
- package/dist/cjs/context/agent_controlled_context_compactor.js +2 -11
- package/dist/cjs/context/compaction_utils.js +14 -2
- package/dist/cjs/context/token_based_context_compactor.js +2 -20
- package/dist/cjs/index.js +3 -0
- package/dist/cjs/memory/in_memory_memory_service.js +13 -2
- package/dist/cjs/memory/vertex_ai_memory_bank_service.js +3 -0
- package/dist/cjs/plugins/base_plugin.js +4 -2
- package/dist/cjs/runner/runner.js +18 -11
- package/dist/cjs/sessions/base_session_service.js +8 -3
- package/dist/cjs/sessions/in_memory_session_service.js +8 -8
- package/dist/cjs/sessions/vertex_ai_session_service.js +22 -10
- package/dist/cjs/skills/loader.js +40 -6
- package/dist/cjs/tools/get_user_choice_tool.js +53 -0
- package/dist/cjs/tools/mcp/mcp_session_manager.js +35 -21
- package/dist/cjs/tools/request_input_tool.js +54 -0
- package/dist/cjs/utils/crypto_shim.js +43 -0
- package/dist/cjs/utils/env_aware_utils.js +15 -13
- package/dist/cjs/utils/error_utils.js +120 -0
- package/dist/cjs/utils/file_utils.js +5 -2
- package/dist/cjs/utils/output_schema_utils.js +43 -0
- package/dist/cjs/utils/vertex_ai_utils.js +3 -0
- package/dist/cjs/version.js +1 -1
- package/dist/esm/a2a/a2a_remote_agent.js +12 -2
- package/dist/esm/a2a/agent_to_a2a.js +0 -1
- package/dist/esm/a2a/auth.js +37 -0
- package/dist/esm/a2a/event_converter_utils.js +40 -15
- package/dist/esm/agents/functions.js +20 -7
- package/dist/esm/agents/llm_agent.js +2 -1
- package/dist/esm/agents/processors/basic_llm_request_processor.js +3 -2
- package/dist/esm/agents/processors/content_request_processor.js +2 -21
- package/dist/esm/agents/processors/instructions_llm_request_processor.js +3 -1
- package/dist/esm/artifacts/in_memory_artifact_service.js +10 -9
- package/dist/esm/auth/credential_service/in_memory_credential_service.js +14 -3
- package/dist/esm/code_executors/unsafe_local_code_executor.js +22 -11
- package/dist/esm/common.js +4 -0
- package/dist/esm/context/agent_controlled_context_compactor.js +2 -11
- package/dist/esm/context/compaction_utils.js +12 -1
- package/dist/esm/context/token_based_context_compactor.js +6 -21
- package/dist/esm/index.js +2 -0
- package/dist/esm/memory/in_memory_memory_service.js +13 -2
- package/dist/esm/memory/vertex_ai_memory_bank_service.js +7 -1
- package/dist/esm/plugins/base_plugin.js +4 -2
- package/dist/esm/runner/runner.js +18 -11
- package/dist/esm/sessions/base_session_service.js +8 -3
- package/dist/esm/sessions/in_memory_session_service.js +8 -8
- package/dist/esm/sessions/vertex_ai_session_service.js +26 -11
- package/dist/esm/skills/loader.js +40 -6
- package/dist/esm/tools/get_user_choice_tool.js +25 -0
- package/dist/esm/tools/mcp/mcp_session_manager.js +35 -21
- package/dist/esm/tools/request_input_tool.js +26 -0
- package/dist/esm/utils/crypto_shim.js +15 -0
- package/dist/esm/utils/env_aware_utils.js +15 -13
- package/dist/esm/utils/error_utils.js +92 -0
- package/dist/esm/utils/file_utils.js +5 -2
- package/dist/esm/utils/output_schema_utils.js +15 -0
- package/dist/esm/utils/vertex_ai_utils.js +2 -0
- package/dist/esm/version.js +1 -1
- package/dist/types/a2a/auth.d.ts +30 -0
- package/dist/types/a2a/event_converter_utils.d.ts +4 -1
- package/dist/types/auth/credential_service/in_memory_credential_service.d.ts +11 -0
- package/dist/types/code_executors/unsafe_local_code_executor.d.ts +5 -1
- package/dist/types/common.d.ts +2 -0
- package/dist/types/context/agent_controlled_context_compactor.d.ts +0 -1
- package/dist/types/context/compaction_utils.d.ts +8 -0
- package/dist/types/context/token_based_context_compactor.d.ts +0 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/memory/in_memory_memory_service.d.ts +11 -0
- package/dist/types/plugins/base_plugin.d.ts +4 -2
- package/dist/types/sessions/base_session_service.d.ts +8 -0
- package/dist/types/sessions/in_memory_session_service.d.ts +9 -0
- package/dist/types/sessions/vertex_ai_session_service.d.ts +19 -1
- package/dist/types/skills/loader.d.ts +5 -0
- package/dist/types/tools/get_user_choice_tool.d.ts +14 -0
- package/dist/types/tools/request_input_tool.d.ts +15 -0
- package/dist/types/utils/crypto_shim.d.ts +16 -0
- package/dist/types/utils/env_aware_utils.d.ts +23 -0
- package/dist/types/utils/error_utils.d.ts +18 -0
- package/dist/types/utils/output_schema_utils.d.ts +18 -0
- package/dist/types/utils/vertex_ai_utils.d.ts +1 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/web/a2a/a2a_remote_agent.js +12 -2
- package/dist/web/a2a/agent_to_a2a.js +0 -1
- package/dist/web/a2a/auth.js +37 -0
- package/dist/web/a2a/event_converter_utils.js +40 -15
- package/dist/web/agents/functions.js +20 -7
- package/dist/web/agents/llm_agent.js +2 -1
- package/dist/web/agents/processors/basic_llm_request_processor.js +3 -2
- package/dist/web/agents/processors/content_request_processor.js +2 -21
- package/dist/web/agents/processors/instructions_llm_request_processor.js +3 -1
- package/dist/web/artifacts/in_memory_artifact_service.js +10 -9
- package/dist/web/auth/credential_service/in_memory_credential_service.js +14 -3
- package/dist/web/code_executors/unsafe_local_code_executor.js +22 -11
- package/dist/web/common.js +4 -0
- package/dist/web/context/agent_controlled_context_compactor.js +2 -11
- package/dist/web/context/compaction_utils.js +12 -1
- package/dist/web/context/token_based_context_compactor.js +6 -21
- package/dist/web/index.js +2 -0
- package/dist/web/memory/in_memory_memory_service.js +13 -2
- package/dist/web/memory/vertex_ai_memory_bank_service.js +7 -1
- package/dist/web/plugins/base_plugin.js +4 -2
- package/dist/web/runner/runner.js +17 -11
- package/dist/web/sessions/base_session_service.js +8 -3
- package/dist/web/sessions/in_memory_session_service.js +8 -8
- package/dist/web/sessions/vertex_ai_session_service.js +24 -11
- package/dist/web/skills/loader.js +40 -6
- package/dist/web/tools/get_user_choice_tool.js +25 -0
- package/dist/web/tools/mcp/mcp_session_manager.js +35 -21
- package/dist/web/tools/request_input_tool.js +26 -0
- package/dist/web/utils/crypto_shim.js +15 -0
- package/dist/web/utils/env_aware_utils.js +15 -13
- package/dist/web/utils/error_utils.js +92 -0
- package/dist/web/utils/file_utils.js +5 -2
- package/dist/web/utils/output_schema_utils.js +15 -0
- package/dist/web/utils/vertex_ai_utils.js +2 -0
- package/dist/web/version.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Browser stand-in for the `node:crypto` builtin, wired up by the alias in
|
|
8
|
+
* `build.js` so that the Node fallback in `env_aware_utils.ts` does not pull a
|
|
9
|
+
* Node builtin into the web bundle.
|
|
10
|
+
*
|
|
11
|
+
* `randomUUID` is reached here only after both `globalThis.crypto` branches in
|
|
12
|
+
* `env_aware_utils.ts` have been ruled out. In a browser that means the Web
|
|
13
|
+
* Crypto API is genuinely absent, so there is no secure source left to fall
|
|
14
|
+
* back to and the only correct move is to fail rather than degrade.
|
|
15
|
+
*/
|
|
16
|
+
export declare function randomUUID(): string;
|
|
@@ -7,6 +7,29 @@
|
|
|
7
7
|
* Returns true if the environment is a browser.
|
|
8
8
|
*/
|
|
9
9
|
export declare function isBrowser(): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Generates a random UUID from a cryptographically secure source.
|
|
12
|
+
*
|
|
13
|
+
* `crypto.randomUUID()` is only exposed in secure contexts, so it is absent on
|
|
14
|
+
* plain-HTTP origins even though `crypto` itself is present.
|
|
15
|
+
* `crypto.getRandomValues()` carries no such restriction, so it is used as the
|
|
16
|
+
* fallback rather than `Math.random()`.
|
|
17
|
+
*
|
|
18
|
+
* In Node the `globalThis.crypto` global was added in v17.4.0 and stayed behind
|
|
19
|
+
* `--experimental-global-webcrypto` until v19.0.0, so neither of those branches
|
|
20
|
+
* matches on a default Node 18 or earlier. `node:crypto` carries no such gate —
|
|
21
|
+
* its `randomUUID` has existed since v14.17.0 — so it is the last resort. In
|
|
22
|
+
* the bundled web build the import is aliased to `crypto_shim.ts`, which
|
|
23
|
+
* throws, because a browser without the Web Crypto API has no secure source
|
|
24
|
+
* left. The non-bundle `dist/web` output that `package.json#browser` points
|
|
25
|
+
* at keeps the import verbatim, as it already does for `node:async_hooks`
|
|
26
|
+
* and `node:path`.
|
|
27
|
+
*
|
|
28
|
+
* Some callers use this value to make security decisions — the OAuth2 `state`
|
|
29
|
+
* parameter in `AuthHandler` and the session identifiers minted by the session
|
|
30
|
+
* services — so this function must not silently degrade to a non-cryptographic
|
|
31
|
+
* generator.
|
|
32
|
+
*/
|
|
10
33
|
export declare function randomUUID(): string;
|
|
11
34
|
/**
|
|
12
35
|
* Encodes the given string or Uint8Array to base64.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Formats an arbitrary thrown value into a readable, root-cause message.
|
|
8
|
+
*
|
|
9
|
+
* Recursively flattens `AggregateError.errors` (joining leaves with ` | `) and
|
|
10
|
+
* unwraps the `Error.cause` chain, and — when HTTP details are synchronously
|
|
11
|
+
* available — appends the status code and a response-body snippet truncated to
|
|
12
|
+
* 1000 characters with a `... [truncated]` marker. It never throws and is safe
|
|
13
|
+
* on `null`/`undefined` and cyclic error graphs.
|
|
14
|
+
*
|
|
15
|
+
* @param err The thrown or rejected value to format.
|
|
16
|
+
* @return A single human-readable message describing the root cause(s).
|
|
17
|
+
*/
|
|
18
|
+
export declare function formatError(err: unknown): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Returns whether the model can natively accept an output schema at the same
|
|
8
|
+
* time as tools, which is strictly more reliable than the prompt-based
|
|
9
|
+
* `set_model_response` workaround.
|
|
10
|
+
*
|
|
11
|
+
* Early Access Program model names encode no numeric version, so
|
|
12
|
+
* `isGemini2OrAbove` rejects them even on Vertex AI. The Python
|
|
13
|
+
* implementation accepts them; that gap lives in the shared predicate.
|
|
14
|
+
*
|
|
15
|
+
* @param modelString A simple or path-based model name.
|
|
16
|
+
* @return True if the model supports an output schema alongside tools.
|
|
17
|
+
*/
|
|
18
|
+
export declare function canUseOutputSchemaWithTools(modelString: string): boolean;
|
package/dist/types/version.d.ts
CHANGED
|
@@ -127,7 +127,12 @@ class RemoteA2AAgent extends BaseAgent {
|
|
|
127
127
|
yield new __await(callback(context, chunk));
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
-
const adkEvent = toAdkEvent(
|
|
130
|
+
const adkEvent = toAdkEvent(
|
|
131
|
+
chunk,
|
|
132
|
+
context.invocationId,
|
|
133
|
+
this.name,
|
|
134
|
+
context.branch
|
|
135
|
+
);
|
|
131
136
|
if (!adkEvent) {
|
|
132
137
|
continue;
|
|
133
138
|
}
|
|
@@ -158,7 +163,12 @@ class RemoteA2AAgent extends BaseAgent {
|
|
|
158
163
|
yield new __await(callback(context, result));
|
|
159
164
|
}
|
|
160
165
|
}
|
|
161
|
-
const adkEvent = toAdkEvent(
|
|
166
|
+
const adkEvent = toAdkEvent(
|
|
167
|
+
result,
|
|
168
|
+
context.invocationId,
|
|
169
|
+
this.name,
|
|
170
|
+
context.branch
|
|
171
|
+
);
|
|
162
172
|
if (adkEvent) {
|
|
163
173
|
processor.updateCustomMetadata(adkEvent, result);
|
|
164
174
|
yield adkEvent;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {createRequire as topLevelCreateRequire} from 'module';
|
|
2
|
+
const require = topLevelCreateRequire(import.meta.url);
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright 2026 Google LLC
|
|
6
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
7
|
+
*/
|
|
8
|
+
import { timingSafeEqual } from "node:crypto";
|
|
9
|
+
const AUTHENTICATED_USER_NAME = "a2a-bearer-token";
|
|
10
|
+
const BEARER_SCHEME_PREFIX = "bearer ";
|
|
11
|
+
function timingSafeEqualStrings(a, b) {
|
|
12
|
+
const left = Buffer.from(a, "utf8");
|
|
13
|
+
const right = Buffer.from(b, "utf8");
|
|
14
|
+
return left.length === right.length && timingSafeEqual(left, right);
|
|
15
|
+
}
|
|
16
|
+
function bearerTokenUserBuilder(token) {
|
|
17
|
+
const expected = token.trim();
|
|
18
|
+
if (!expected) {
|
|
19
|
+
throw new Error(
|
|
20
|
+
"bearerTokenUserBuilder: an empty A2A bearer token is not a valid authenticator. Supply a real shared secret, or configure no token at all to run the A2A surface unauthenticated."
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return async (req) => {
|
|
24
|
+
var _a;
|
|
25
|
+
const header = (_a = req.headers.authorization) != null ? _a : "";
|
|
26
|
+
const credential = header.toLowerCase().startsWith(BEARER_SCHEME_PREFIX) ? header.slice(BEARER_SCHEME_PREFIX.length) : "";
|
|
27
|
+
if (!timingSafeEqualStrings(credential, expected)) {
|
|
28
|
+
throw new Error(
|
|
29
|
+
"A2A request rejected: missing or invalid `Authorization: Bearer` credential."
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
return { isAuthenticated: true, userName: AUTHENTICATED_USER_NAME };
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
bearerTokenUserBuilder
|
|
37
|
+
};
|
|
@@ -62,33 +62,34 @@ function toA2AMessage(event, {
|
|
|
62
62
|
metadata: getA2AEventMetadata(event, { appName, userId, sessionId })
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
-
function toAdkEvent(event, invocationId, agentName) {
|
|
65
|
+
function toAdkEvent(event, invocationId, agentName, branch) {
|
|
66
66
|
if (isMessage(event)) {
|
|
67
|
-
return messageToAdkEvent(event, invocationId, agentName);
|
|
67
|
+
return messageToAdkEvent(event, invocationId, agentName, branch);
|
|
68
68
|
}
|
|
69
69
|
if (isTask(event)) {
|
|
70
|
-
return taskToAdkEvent(event, invocationId, agentName);
|
|
70
|
+
return taskToAdkEvent(event, invocationId, agentName, branch);
|
|
71
71
|
}
|
|
72
72
|
if (isTaskArtifactUpdateEvent(event)) {
|
|
73
|
-
return artifactUpdateToAdkEvent(event, invocationId, agentName);
|
|
73
|
+
return artifactUpdateToAdkEvent(event, invocationId, agentName, branch);
|
|
74
74
|
}
|
|
75
75
|
if (isTaskStatusUpdateEvent(event)) {
|
|
76
|
-
return event.final ? finalTaskStatusUpdateToAdkEvent(event, invocationId, agentName) : taskStatusUpdateToAdkEvent(event, invocationId, agentName);
|
|
76
|
+
return event.final ? finalTaskStatusUpdateToAdkEvent(event, invocationId, agentName, branch) : taskStatusUpdateToAdkEvent(event, invocationId, agentName, branch);
|
|
77
77
|
}
|
|
78
78
|
return void 0;
|
|
79
79
|
}
|
|
80
|
-
function messageToAdkEvent(msg, invocationId, agentName) {
|
|
80
|
+
function messageToAdkEvent(msg, invocationId, agentName, branch) {
|
|
81
81
|
const parts = toGenAIParts(msg.parts);
|
|
82
82
|
const content = parts.length === 0 ? void 0 : msg.role === MessageRole.USER ? createUserContent(parts) : createModelContent(parts);
|
|
83
83
|
return __spreadProps(__spreadValues({}, createAdkEventFromMetadata(msg)), {
|
|
84
84
|
invocationId,
|
|
85
85
|
author: msg.role === MessageRole.USER ? MessageRole.USER : agentName,
|
|
86
|
+
branch,
|
|
86
87
|
content,
|
|
87
88
|
turnComplete: true,
|
|
88
89
|
partial: false
|
|
89
90
|
});
|
|
90
91
|
}
|
|
91
|
-
function artifactUpdateToAdkEvent(a2aEvent, invocationId, agentName) {
|
|
92
|
+
function artifactUpdateToAdkEvent(a2aEvent, invocationId, agentName, branch) {
|
|
92
93
|
var _a;
|
|
93
94
|
const partsToConvert = ((_a = a2aEvent.artifact) == null ? void 0 : _a.parts) || [];
|
|
94
95
|
if (partsToConvert.length === 0) {
|
|
@@ -98,12 +99,13 @@ function artifactUpdateToAdkEvent(a2aEvent, invocationId, agentName) {
|
|
|
98
99
|
return __spreadProps(__spreadValues({}, createAdkEventFromMetadata(a2aEvent)), {
|
|
99
100
|
invocationId,
|
|
100
101
|
author: agentName,
|
|
102
|
+
branch,
|
|
101
103
|
content: createModelContent(toGenAIParts(partsToConvert)),
|
|
102
104
|
longRunningToolIds: getLongRunningToolIDs(partsToConvert),
|
|
103
105
|
partial
|
|
104
106
|
});
|
|
105
107
|
}
|
|
106
|
-
function finalTaskStatusUpdateToAdkEvent(a2aEvent, invocationId, agentName) {
|
|
108
|
+
function finalTaskStatusUpdateToAdkEvent(a2aEvent, invocationId, agentName, branch) {
|
|
107
109
|
var _a;
|
|
108
110
|
const partsToConvert = ((_a = a2aEvent.status.message) == null ? void 0 : _a.parts) || [];
|
|
109
111
|
if (partsToConvert.length === 0) {
|
|
@@ -115,13 +117,14 @@ function finalTaskStatusUpdateToAdkEvent(a2aEvent, invocationId, agentName) {
|
|
|
115
117
|
return __spreadProps(__spreadValues({}, createAdkEventFromMetadata(a2aEvent)), {
|
|
116
118
|
invocationId,
|
|
117
119
|
author: agentName,
|
|
120
|
+
branch,
|
|
118
121
|
errorMessage: isFailedTask ? getFailedTaskStatusUpdateEventError(a2aEvent) : void 0,
|
|
119
122
|
content: hasContent ? createModelContent(parts) : void 0,
|
|
120
123
|
longRunningToolIds: getLongRunningToolIDs(partsToConvert),
|
|
121
124
|
turnComplete: true
|
|
122
125
|
});
|
|
123
126
|
}
|
|
124
|
-
function taskStatusUpdateToAdkEvent(a2aEvent, invocationId, agentName) {
|
|
127
|
+
function taskStatusUpdateToAdkEvent(a2aEvent, invocationId, agentName, branch) {
|
|
125
128
|
const msg = a2aEvent.status.message;
|
|
126
129
|
if (!msg) {
|
|
127
130
|
return void 0;
|
|
@@ -133,12 +136,13 @@ function taskStatusUpdateToAdkEvent(a2aEvent, invocationId, agentName) {
|
|
|
133
136
|
return __spreadProps(__spreadValues({}, createAdkEventFromMetadata(a2aEvent)), {
|
|
134
137
|
invocationId,
|
|
135
138
|
author: agentName,
|
|
139
|
+
branch,
|
|
136
140
|
content: createModelContent(parts),
|
|
137
141
|
turnComplete: false,
|
|
138
142
|
partial: true
|
|
139
143
|
});
|
|
140
144
|
}
|
|
141
|
-
function taskToAdkEvent(a2aTask, invocationId, agentName) {
|
|
145
|
+
function taskToAdkEvent(a2aTask, invocationId, agentName, branch) {
|
|
142
146
|
var _a, _b;
|
|
143
147
|
const parts = [];
|
|
144
148
|
const longRunningToolIds = [];
|
|
@@ -165,16 +169,26 @@ function taskToAdkEvent(a2aTask, invocationId, agentName) {
|
|
|
165
169
|
return __spreadProps(__spreadValues({}, createAdkEventFromMetadata(a2aTask)), {
|
|
166
170
|
invocationId,
|
|
167
171
|
author: agentName,
|
|
172
|
+
branch,
|
|
168
173
|
content: isFailed ? void 0 : createModelContent(parts),
|
|
169
174
|
errorMessage: isFailed ? getFailedTaskStatusUpdateEventError(a2aTask) : void 0,
|
|
170
175
|
longRunningToolIds,
|
|
171
176
|
turnComplete: isTerminal
|
|
172
177
|
});
|
|
173
178
|
}
|
|
179
|
+
const PEER_SETTABLE_ACTION_FIELDS = /* @__PURE__ */ new Set(["escalate"]);
|
|
174
180
|
function createAdkEventFromMetadata(a2aEvent) {
|
|
175
181
|
const metadata = a2aEvent.metadata || {};
|
|
176
182
|
return createEvent({
|
|
177
|
-
branch
|
|
183
|
+
// `branch` is intentionally NOT restored from peer metadata here (unlike
|
|
184
|
+
// the other fields below): it is the mechanism getContents() (see
|
|
185
|
+
// content_processor_utils.ts) uses to keep sibling sub-agent branches'
|
|
186
|
+
// conversation contexts isolated from each other. A remote A2A peer that
|
|
187
|
+
// controls its own outgoing metadata could otherwise forge `adk_branch`
|
|
188
|
+
// (set it to a shared ancestor branch, or omit it) to leak its content
|
|
189
|
+
// into an unrelated sibling agent's LLM context. Every caller of the
|
|
190
|
+
// `*ToAdkEvent` functions in this file force-sets `branch` from its own
|
|
191
|
+
// local `InvocationContext` instead, the same way `author` is handled.
|
|
178
192
|
author: metadata[A2AMetadataKeys.AUTHOR],
|
|
179
193
|
partial: metadata[A2AMetadataKeys.PARTIAL],
|
|
180
194
|
errorCode: metadata[A2AMetadataKeys.ERROR_CODE],
|
|
@@ -183,10 +197,21 @@ function createAdkEventFromMetadata(a2aEvent) {
|
|
|
183
197
|
groundingMetadata: metadata[A2AMetadataKeys.GROUNDING_METADATA],
|
|
184
198
|
usageMetadata: metadata[A2AMetadataKeys.USAGE_METADATA],
|
|
185
199
|
customMetadata: metadata[A2AMetadataKeys.CUSTOM_METADATA],
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
200
|
+
// Only fields in PEER_SETTABLE_ACTION_FIELDS may be restored from
|
|
201
|
+
// metadata a remote A2A peer controls. Every other action field either
|
|
202
|
+
// mutates the caller's own session or drives the caller's own control
|
|
203
|
+
// flow (e.g. `transferToAgent`, see llm_agent.ts), so it must never be
|
|
204
|
+
// rebuilt from peer-supplied data. Filtering through an allowlist here
|
|
205
|
+
// (rather than just omitting the unsafe field) means a future action
|
|
206
|
+
// field is unsafe-by-default: adding it to `candidateActions` alone
|
|
207
|
+
// does nothing until it's also added to the allowlist.
|
|
208
|
+
actions: createEventActions(
|
|
209
|
+
Object.fromEntries(
|
|
210
|
+
Object.entries({
|
|
211
|
+
escalate: !!metadata[A2AMetadataKeys.ESCALATE]
|
|
212
|
+
}).filter(([key]) => PEER_SETTABLE_ACTION_FIELDS.has(key))
|
|
213
|
+
)
|
|
214
|
+
)
|
|
190
215
|
});
|
|
191
216
|
}
|
|
192
217
|
function getLongRunningToolIDs(parts) {
|
|
@@ -185,6 +185,18 @@ async function handleFunctionCallsAsync({
|
|
|
185
185
|
toolConfirmationDict
|
|
186
186
|
});
|
|
187
187
|
}
|
|
188
|
+
function normalizeCallbackResponse(response) {
|
|
189
|
+
if (response == null) {
|
|
190
|
+
return void 0;
|
|
191
|
+
}
|
|
192
|
+
if (typeof response !== "object") {
|
|
193
|
+
return { result: response };
|
|
194
|
+
}
|
|
195
|
+
if (Array.isArray(response)) {
|
|
196
|
+
return { results: response };
|
|
197
|
+
}
|
|
198
|
+
return response;
|
|
199
|
+
}
|
|
188
200
|
async function handleFunctionCallList({
|
|
189
201
|
invocationContext,
|
|
190
202
|
functionCalls,
|
|
@@ -231,6 +243,7 @@ async function handleFunctionCallList({
|
|
|
231
243
|
}
|
|
232
244
|
}
|
|
233
245
|
}
|
|
246
|
+
functionResponse = normalizeCallbackResponse(functionResponse);
|
|
234
247
|
if (functionResponse == null) {
|
|
235
248
|
try {
|
|
236
249
|
functionResponse = await callToolAsync(tool, functionArgs, toolContext);
|
|
@@ -242,8 +255,8 @@ async function handleFunctionCallList({
|
|
|
242
255
|
toolContext,
|
|
243
256
|
error: e
|
|
244
257
|
});
|
|
245
|
-
if (onToolErrorResponse) {
|
|
246
|
-
functionResponse = onToolErrorResponse;
|
|
258
|
+
if (onToolErrorResponse != null) {
|
|
259
|
+
functionResponse = normalizeCallbackResponse(onToolErrorResponse);
|
|
247
260
|
} else {
|
|
248
261
|
functionResponseError = e.message;
|
|
249
262
|
}
|
|
@@ -272,17 +285,17 @@ async function handleFunctionCallList({
|
|
|
272
285
|
}
|
|
273
286
|
}
|
|
274
287
|
if (alteredFunctionResponse != null) {
|
|
275
|
-
functionResponse = alteredFunctionResponse;
|
|
288
|
+
functionResponse = normalizeCallbackResponse(alteredFunctionResponse);
|
|
276
289
|
}
|
|
277
|
-
if (tool.isLongRunning &&
|
|
290
|
+
if (tool.isLongRunning && functionResponse == null) {
|
|
278
291
|
continue;
|
|
279
292
|
}
|
|
280
293
|
if (functionResponseError) {
|
|
281
294
|
functionResponse = { error: functionResponseError };
|
|
282
|
-
} else if (
|
|
295
|
+
} else if (functionResponse == null) {
|
|
283
296
|
functionResponse = { result: functionResponse };
|
|
284
|
-
} else
|
|
285
|
-
functionResponse =
|
|
297
|
+
} else {
|
|
298
|
+
functionResponse = normalizeCallbackResponse(functionResponse);
|
|
286
299
|
}
|
|
287
300
|
const functionResponseEvent = createEvent({
|
|
288
301
|
invocationId: invocationContext.invocationId,
|
|
@@ -84,6 +84,7 @@ import { isBaseLlm } from "../models/base_llm.js";
|
|
|
84
84
|
import { LLMRegistry } from "../models/registry.js";
|
|
85
85
|
import { isBaseTool } from "../tools/base_tool.js";
|
|
86
86
|
import { logger } from "../utils/logger.js";
|
|
87
|
+
import { canUseOutputSchemaWithTools } from "../utils/output_schema_utils.js";
|
|
87
88
|
import { Context } from "./context.js";
|
|
88
89
|
import {
|
|
89
90
|
runAsyncGeneratorWithOtelContext,
|
|
@@ -499,7 +500,7 @@ class LlmAgent extends (_b = BaseAgent, _a = LLM_AGENT_SIGNATURE_SYMBOL, _b) {
|
|
|
499
500
|
}
|
|
500
501
|
}
|
|
501
502
|
const allTools = [...this.tools];
|
|
502
|
-
if (this.outputSchema && allTools.length > 0) {
|
|
503
|
+
if (this.outputSchema && allTools.length > 0 && !canUseOutputSchemaWithTools(this.canonicalModel.model)) {
|
|
503
504
|
const setModelResponseTool = new FunctionTool({
|
|
504
505
|
name: "set_model_response",
|
|
505
506
|
description: "Call this tool to submit your final response conforming to the output schema. Use this tool only when you have collected all the information and are ready to return the final answer.",
|
|
@@ -38,6 +38,7 @@ var __asyncGenerator = (__this, __arguments, generator) => {
|
|
|
38
38
|
* SPDX-License-Identifier: Apache-2.0
|
|
39
39
|
*/
|
|
40
40
|
import { setOutputSchema } from "../../models/llm_request.js";
|
|
41
|
+
import { canUseOutputSchemaWithTools } from "../../utils/output_schema_utils.js";
|
|
41
42
|
import { isLlmAgent } from "../llm_agent.js";
|
|
42
43
|
import { BaseLlmRequestProcessor } from "./base_llm_processor.js";
|
|
43
44
|
class BasicLlmRequestProcessor extends BaseLlmRequestProcessor {
|
|
@@ -51,14 +52,14 @@ class BasicLlmRequestProcessor extends BaseLlmRequestProcessor {
|
|
|
51
52
|
// eslint-disable-next-line require-yield
|
|
52
53
|
runAsync(invocationContext, llmRequest) {
|
|
53
54
|
return __asyncGenerator(this, null, function* () {
|
|
54
|
-
var _a;
|
|
55
|
+
var _a, _b;
|
|
55
56
|
const agent = invocationContext.agent;
|
|
56
57
|
if (!isLlmAgent(agent)) {
|
|
57
58
|
return;
|
|
58
59
|
}
|
|
59
60
|
llmRequest.model = agent.canonicalModel.model;
|
|
60
61
|
llmRequest.config = __spreadValues({}, (_a = agent.generateContentConfig) != null ? _a : {});
|
|
61
|
-
if (agent.outputSchema && (!agent.tools || agent.
|
|
62
|
+
if (agent.outputSchema && (!((_b = agent.tools) == null ? void 0 : _b.length) || canUseOutputSchemaWithTools(agent.canonicalModel.model))) {
|
|
62
63
|
setOutputSchema(llmRequest, agent.outputSchema);
|
|
63
64
|
}
|
|
64
65
|
if (invocationContext.runConfig) {
|
|
@@ -21,7 +21,7 @@ var __asyncGenerator = (__this, __arguments, generator) => {
|
|
|
21
21
|
* Copyright 2025 Google LLC
|
|
22
22
|
* SPDX-License-Identifier: Apache-2.0
|
|
23
23
|
*/
|
|
24
|
-
import {
|
|
24
|
+
import { getActiveEvents } from "../../context/compaction_utils.js";
|
|
25
25
|
import { isLlmAgent } from "../llm_agent.js";
|
|
26
26
|
import {
|
|
27
27
|
getContents,
|
|
@@ -42,26 +42,7 @@ class ContentRequestProcessor {
|
|
|
42
42
|
if (!agent || !isLlmAgent(agent)) {
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
const compactedEvents = events.filter(isCompactedEvent);
|
|
47
|
-
const latestCompactedEvent = compactedEvents.length > 0 ? compactedEvents.reduce(
|
|
48
|
-
(latest, current) => current.endTime > latest.endTime ? current : latest
|
|
49
|
-
) : void 0;
|
|
50
|
-
if (latestCompactedEvent) {
|
|
51
|
-
const remainingEvents = events.filter((event) => {
|
|
52
|
-
if (event === latestCompactedEvent) {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
if (isCompactedEvent(event)) {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
if (event.timestamp <= latestCompactedEvent.endTime) {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
return true;
|
|
62
|
-
});
|
|
63
|
-
events = [latestCompactedEvent, ...remainingEvents];
|
|
64
|
-
}
|
|
45
|
+
const events = getActiveEvents(invocationContext.session.events);
|
|
65
46
|
if (agent.includeContents === "default") {
|
|
66
47
|
llmRequest.contents = getContents(
|
|
67
48
|
events,
|
|
@@ -22,6 +22,7 @@ var __asyncGenerator = (__this, __arguments, generator) => {
|
|
|
22
22
|
* SPDX-License-Identifier: Apache-2.0
|
|
23
23
|
*/
|
|
24
24
|
import { appendInstructions } from "../../models/llm_request.js";
|
|
25
|
+
import { canUseOutputSchemaWithTools } from "../../utils/output_schema_utils.js";
|
|
25
26
|
import { injectSessionState } from "../instructions.js";
|
|
26
27
|
import { isLlmAgent } from "../llm_agent.js";
|
|
27
28
|
import { ReadonlyContext } from "../readonly_context.js";
|
|
@@ -33,6 +34,7 @@ class InstructionsLlmRequestProcessor extends BaseLlmRequestProcessor {
|
|
|
33
34
|
// eslint-disable-next-line require-yield
|
|
34
35
|
runAsync(invocationContext, llmRequest) {
|
|
35
36
|
return __asyncGenerator(this, null, function* () {
|
|
37
|
+
var _a;
|
|
36
38
|
const agent = invocationContext.agent;
|
|
37
39
|
if (!isLlmAgent(agent)) {
|
|
38
40
|
return;
|
|
@@ -64,7 +66,7 @@ class InstructionsLlmRequestProcessor extends BaseLlmRequestProcessor {
|
|
|
64
66
|
}
|
|
65
67
|
appendInstructions(llmRequest, [instructionWithState]);
|
|
66
68
|
}
|
|
67
|
-
if (agent.outputSchema && agent.tools && agent.
|
|
69
|
+
if (agent.outputSchema && ((_a = agent.tools) == null ? void 0 : _a.length) && !canUseOutputSchemaWithTools(agent.canonicalModel.model)) {
|
|
68
70
|
appendInstructions(llmRequest, [
|
|
69
71
|
'To output the final result, you must call the "set_model_response" function with the appropriate values. Do not output anything else.'
|
|
70
72
|
]);
|
|
@@ -70,16 +70,14 @@ class InMemoryArtifactService {
|
|
|
70
70
|
userId,
|
|
71
71
|
sessionId
|
|
72
72
|
}) {
|
|
73
|
-
const sessionPrefix = ""
|
|
74
|
-
const
|
|
73
|
+
const sessionPrefix = artifactPrefix("session", appName, userId, sessionId);
|
|
74
|
+
const userPrefix = artifactPrefix("user", appName, userId);
|
|
75
75
|
const filenames = [];
|
|
76
76
|
for (const path in this.artifacts) {
|
|
77
77
|
if (path.startsWith(sessionPrefix)) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const filename = path.replace(usernamespacePrefix, "");
|
|
82
|
-
filenames.push(filename);
|
|
78
|
+
filenames.push(decodeURIComponent(path.slice(sessionPrefix.length)));
|
|
79
|
+
} else if (path.startsWith(userPrefix)) {
|
|
80
|
+
filenames.push(decodeURIComponent(path.slice(userPrefix.length)));
|
|
83
81
|
}
|
|
84
82
|
}
|
|
85
83
|
return Promise.resolve(filenames.sort());
|
|
@@ -150,9 +148,12 @@ class InMemoryArtifactService {
|
|
|
150
148
|
}
|
|
151
149
|
function artifactPath(appName, userId, sessionId, filename) {
|
|
152
150
|
if (fileHasUserNamespace(filename)) {
|
|
153
|
-
return "".concat(appName,
|
|
151
|
+
return "".concat(artifactPrefix("user", appName, userId)).concat(encodeURIComponent(filename));
|
|
154
152
|
}
|
|
155
|
-
return "".concat(appName,
|
|
153
|
+
return "".concat(artifactPrefix("session", appName, userId, sessionId)).concat(encodeURIComponent(filename));
|
|
154
|
+
}
|
|
155
|
+
function artifactPrefix(scope, ...parts) {
|
|
156
|
+
return "".concat([scope, ...parts].map(encodeURIComponent).join("/"), "/");
|
|
156
157
|
}
|
|
157
158
|
function fileHasUserNamespace(filename) {
|
|
158
159
|
return filename.startsWith("user:");
|
|
@@ -7,7 +7,18 @@ const require = topLevelCreateRequire(import.meta.url);
|
|
|
7
7
|
*/
|
|
8
8
|
class InMemoryCredentialService {
|
|
9
9
|
constructor() {
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* A map from app name to a map from user ID to a map from credential key to
|
|
12
|
+
* credential.
|
|
13
|
+
*
|
|
14
|
+
* `appName`, `userId` and `credentialKey` are all attacker-influenced, so
|
|
15
|
+
* every level is created with `Object.create(null)`. On an ordinary `{}`
|
|
16
|
+
* literal a key of `__proto__` resolves to the inherited `__proto__`
|
|
17
|
+
* accessor rather than creating an own property, so nested assignment
|
|
18
|
+
* pollutes every object in the process, and a lookup of an inherited key
|
|
19
|
+
* such as `toString` returns a function instead of `undefined`.
|
|
20
|
+
*/
|
|
21
|
+
this.credentials = /* @__PURE__ */ Object.create(null);
|
|
11
22
|
}
|
|
12
23
|
loadCredential(authConfig, toolContext) {
|
|
13
24
|
const credentialBucket = this.getBucketForCurrentContext(toolContext);
|
|
@@ -22,10 +33,10 @@ class InMemoryCredentialService {
|
|
|
22
33
|
getBucketForCurrentContext(toolContext) {
|
|
23
34
|
const { appName, userId } = toolContext.invocationContext.session;
|
|
24
35
|
if (!this.credentials[appName]) {
|
|
25
|
-
this.credentials[appName] =
|
|
36
|
+
this.credentials[appName] = /* @__PURE__ */ Object.create(null);
|
|
26
37
|
}
|
|
27
38
|
if (!this.credentials[appName][userId]) {
|
|
28
|
-
this.credentials[appName][userId] =
|
|
39
|
+
this.credentials[appName][userId] = /* @__PURE__ */ Object.create(null);
|
|
29
40
|
}
|
|
30
41
|
return this.credentials[appName][userId];
|
|
31
42
|
}
|
|
@@ -5,7 +5,7 @@ const require = topLevelCreateRequire(import.meta.url);
|
|
|
5
5
|
* Copyright 2026 Google LLC
|
|
6
6
|
* SPDX-License-Identifier: Apache-2.0
|
|
7
7
|
*/
|
|
8
|
-
import { spawn } from "child_process";
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
9
|
import * as fs from "node:fs/promises";
|
|
10
10
|
import * as os from "node:os";
|
|
11
11
|
import * as path from "node:path";
|
|
@@ -17,13 +17,21 @@ import {
|
|
|
17
17
|
CodeExecutionLanguage
|
|
18
18
|
} from "./code_execution_utils.js";
|
|
19
19
|
const IS_WINDOWS = os.platform() === "win32";
|
|
20
|
+
const POWERSHELL_BASE_ARGS = [
|
|
21
|
+
"-NoLogo",
|
|
22
|
+
"-NoProfile",
|
|
23
|
+
"-ExecutionPolicy",
|
|
24
|
+
"Bypass",
|
|
25
|
+
"-File"
|
|
26
|
+
];
|
|
27
|
+
const CMD_BASE_ARGS = ["/D", "/c"];
|
|
28
|
+
function isPowerShellCommand(commandPath) {
|
|
29
|
+
return /^(powershell|pwsh)(\.exe)?$/i.test(path.win32.basename(commandPath));
|
|
30
|
+
}
|
|
20
31
|
async function createTempScriptFile(code, language, shellCommandPath) {
|
|
21
|
-
const tempDir =
|
|
22
|
-
os.tmpdir(),
|
|
23
|
-
"adk_js_unsafe_code_executor",
|
|
24
|
-
Date.now().toString() + "_" + Math.random().toString(36).slice(2)
|
|
32
|
+
const tempDir = await fs.mkdtemp(
|
|
33
|
+
path.join(os.tmpdir(), "adk_js_unsafe_code_executor_")
|
|
25
34
|
);
|
|
26
|
-
await fs.mkdir(tempDir, { recursive: true });
|
|
27
35
|
const ext = getExtensionForLanguage(language, shellCommandPath) || ".js";
|
|
28
36
|
const filePath = path.join(tempDir, "script".concat(ext));
|
|
29
37
|
await fs.writeFile(filePath, code);
|
|
@@ -43,6 +51,9 @@ function getExtensionForLanguage(language, shellCommandPath) {
|
|
|
43
51
|
return ".bat";
|
|
44
52
|
}
|
|
45
53
|
if (language === CodeExecutionLanguage.SHELL) {
|
|
54
|
+
if (shellCommandPath && isPowerShellCommand(shellCommandPath)) {
|
|
55
|
+
return ".ps1";
|
|
56
|
+
}
|
|
46
57
|
if (IS_WINDOWS) {
|
|
47
58
|
if (shellCommandPath && shellCommandPath.toLowerCase().includes("cmd")) {
|
|
48
59
|
return ".bat";
|
|
@@ -101,17 +112,17 @@ class UnsafeLocalCodeExecutor extends BaseCodeExecutor {
|
|
|
101
112
|
command = this.pythonCommandPath;
|
|
102
113
|
} else if (language === CodeExecutionLanguage.SHELL) {
|
|
103
114
|
command = this.shellCommandPath;
|
|
104
|
-
if (this.shellCommandPath
|
|
105
|
-
args = [
|
|
115
|
+
if (isPowerShellCommand(this.shellCommandPath)) {
|
|
116
|
+
args = [...POWERSHELL_BASE_ARGS, filePath];
|
|
106
117
|
} else if (this.shellCommandPath.toLowerCase().includes("cmd")) {
|
|
107
|
-
args = [
|
|
118
|
+
args = [...CMD_BASE_ARGS, filePath];
|
|
108
119
|
}
|
|
109
120
|
} else if (language === CodeExecutionLanguage.POWERSHELL) {
|
|
110
121
|
command = IS_WINDOWS ? "powershell" : "pwsh";
|
|
111
|
-
args = [
|
|
122
|
+
args = [...POWERSHELL_BASE_ARGS, filePath];
|
|
112
123
|
} else if (language === CodeExecutionLanguage.WINDOWS_CMD) {
|
|
113
124
|
command = "cmd.exe";
|
|
114
|
-
args = [
|
|
125
|
+
args = [...CMD_BASE_ARGS, filePath];
|
|
115
126
|
}
|
|
116
127
|
if (params.codeExecutionInput.args) {
|
|
117
128
|
if (Array.isArray(params.codeExecutionInput.args)) {
|
package/dist/web/common.js
CHANGED
|
@@ -121,6 +121,7 @@ import {
|
|
|
121
121
|
import { ExampleTool } from "./tools/example_tool.js";
|
|
122
122
|
import { EXIT_LOOP, ExitLoopTool } from "./tools/exit_loop_tool.js";
|
|
123
123
|
import { FunctionTool, isFunctionTool } from "./tools/function_tool.js";
|
|
124
|
+
import { getUserChoiceTool } from "./tools/get_user_choice_tool.js";
|
|
124
125
|
import {
|
|
125
126
|
GOOGLE_MAPS_GROUNDING,
|
|
126
127
|
GoogleMapsGroundingTool
|
|
@@ -137,6 +138,7 @@ import {
|
|
|
137
138
|
PRELOAD_MEMORY,
|
|
138
139
|
PreloadMemoryTool
|
|
139
140
|
} from "./tools/preload_memory_tool.js";
|
|
141
|
+
import { requestInputTool } from "./tools/request_input_tool.js";
|
|
140
142
|
import { ToolConfirmation } from "./tools/tool_confirmation.js";
|
|
141
143
|
import { URL_CONTEXT, UrlContextTool } from "./tools/url_context_tool.js";
|
|
142
144
|
import { VertexAiSearchTool } from "./tools/vertex_ai_search_tool.js";
|
|
@@ -305,6 +307,7 @@ export {
|
|
|
305
307
|
getFunctionCalls,
|
|
306
308
|
getFunctionResponses,
|
|
307
309
|
getLogger,
|
|
310
|
+
getUserChoiceTool,
|
|
308
311
|
hasThoughts,
|
|
309
312
|
hasTrailingCodeExecutionResult,
|
|
310
313
|
isAgentTool,
|
|
@@ -332,6 +335,7 @@ export {
|
|
|
332
335
|
loadWebPage,
|
|
333
336
|
populateClientFunctionCallId,
|
|
334
337
|
pruneThoughts,
|
|
338
|
+
requestInputTool,
|
|
335
339
|
runWithClientLabel,
|
|
336
340
|
setLogLevel,
|
|
337
341
|
setLogger,
|