@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.
Files changed (129) hide show
  1. package/dist/cjs/a2a/a2a_remote_agent.js +12 -2
  2. package/dist/cjs/a2a/agent_to_a2a.js +0 -1
  3. package/dist/cjs/a2a/auth.js +65 -0
  4. package/dist/cjs/a2a/event_converter_utils.js +40 -15
  5. package/dist/cjs/agents/functions.js +20 -7
  6. package/dist/cjs/agents/llm_agent.js +2 -1
  7. package/dist/cjs/agents/processors/basic_llm_request_processor.js +3 -2
  8. package/dist/cjs/agents/processors/content_request_processor.js +2 -21
  9. package/dist/cjs/agents/processors/instructions_llm_request_processor.js +3 -1
  10. package/dist/cjs/artifacts/in_memory_artifact_service.js +10 -9
  11. package/dist/cjs/auth/credential_service/in_memory_credential_service.js +14 -3
  12. package/dist/cjs/code_executors/unsafe_local_code_executor.js +23 -12
  13. package/dist/cjs/common.js +6 -0
  14. package/dist/cjs/context/agent_controlled_context_compactor.js +2 -11
  15. package/dist/cjs/context/compaction_utils.js +14 -2
  16. package/dist/cjs/context/token_based_context_compactor.js +2 -20
  17. package/dist/cjs/index.js +3 -0
  18. package/dist/cjs/memory/in_memory_memory_service.js +13 -2
  19. package/dist/cjs/memory/vertex_ai_memory_bank_service.js +3 -0
  20. package/dist/cjs/plugins/base_plugin.js +4 -2
  21. package/dist/cjs/runner/runner.js +18 -11
  22. package/dist/cjs/sessions/base_session_service.js +8 -3
  23. package/dist/cjs/sessions/in_memory_session_service.js +8 -8
  24. package/dist/cjs/sessions/vertex_ai_session_service.js +22 -10
  25. package/dist/cjs/skills/loader.js +40 -6
  26. package/dist/cjs/tools/get_user_choice_tool.js +53 -0
  27. package/dist/cjs/tools/mcp/mcp_session_manager.js +35 -21
  28. package/dist/cjs/tools/request_input_tool.js +54 -0
  29. package/dist/cjs/utils/crypto_shim.js +43 -0
  30. package/dist/cjs/utils/env_aware_utils.js +15 -13
  31. package/dist/cjs/utils/error_utils.js +120 -0
  32. package/dist/cjs/utils/file_utils.js +5 -2
  33. package/dist/cjs/utils/output_schema_utils.js +43 -0
  34. package/dist/cjs/utils/vertex_ai_utils.js +3 -0
  35. package/dist/cjs/version.js +1 -1
  36. package/dist/esm/a2a/a2a_remote_agent.js +12 -2
  37. package/dist/esm/a2a/agent_to_a2a.js +0 -1
  38. package/dist/esm/a2a/auth.js +37 -0
  39. package/dist/esm/a2a/event_converter_utils.js +40 -15
  40. package/dist/esm/agents/functions.js +20 -7
  41. package/dist/esm/agents/llm_agent.js +2 -1
  42. package/dist/esm/agents/processors/basic_llm_request_processor.js +3 -2
  43. package/dist/esm/agents/processors/content_request_processor.js +2 -21
  44. package/dist/esm/agents/processors/instructions_llm_request_processor.js +3 -1
  45. package/dist/esm/artifacts/in_memory_artifact_service.js +10 -9
  46. package/dist/esm/auth/credential_service/in_memory_credential_service.js +14 -3
  47. package/dist/esm/code_executors/unsafe_local_code_executor.js +22 -11
  48. package/dist/esm/common.js +4 -0
  49. package/dist/esm/context/agent_controlled_context_compactor.js +2 -11
  50. package/dist/esm/context/compaction_utils.js +12 -1
  51. package/dist/esm/context/token_based_context_compactor.js +6 -21
  52. package/dist/esm/index.js +2 -0
  53. package/dist/esm/memory/in_memory_memory_service.js +13 -2
  54. package/dist/esm/memory/vertex_ai_memory_bank_service.js +7 -1
  55. package/dist/esm/plugins/base_plugin.js +4 -2
  56. package/dist/esm/runner/runner.js +18 -11
  57. package/dist/esm/sessions/base_session_service.js +8 -3
  58. package/dist/esm/sessions/in_memory_session_service.js +8 -8
  59. package/dist/esm/sessions/vertex_ai_session_service.js +26 -11
  60. package/dist/esm/skills/loader.js +40 -6
  61. package/dist/esm/tools/get_user_choice_tool.js +25 -0
  62. package/dist/esm/tools/mcp/mcp_session_manager.js +35 -21
  63. package/dist/esm/tools/request_input_tool.js +26 -0
  64. package/dist/esm/utils/crypto_shim.js +15 -0
  65. package/dist/esm/utils/env_aware_utils.js +15 -13
  66. package/dist/esm/utils/error_utils.js +92 -0
  67. package/dist/esm/utils/file_utils.js +5 -2
  68. package/dist/esm/utils/output_schema_utils.js +15 -0
  69. package/dist/esm/utils/vertex_ai_utils.js +2 -0
  70. package/dist/esm/version.js +1 -1
  71. package/dist/types/a2a/auth.d.ts +30 -0
  72. package/dist/types/a2a/event_converter_utils.d.ts +4 -1
  73. package/dist/types/auth/credential_service/in_memory_credential_service.d.ts +11 -0
  74. package/dist/types/code_executors/unsafe_local_code_executor.d.ts +5 -1
  75. package/dist/types/common.d.ts +2 -0
  76. package/dist/types/context/agent_controlled_context_compactor.d.ts +0 -1
  77. package/dist/types/context/compaction_utils.d.ts +8 -0
  78. package/dist/types/context/token_based_context_compactor.d.ts +0 -1
  79. package/dist/types/index.d.ts +2 -1
  80. package/dist/types/memory/in_memory_memory_service.d.ts +11 -0
  81. package/dist/types/plugins/base_plugin.d.ts +4 -2
  82. package/dist/types/sessions/base_session_service.d.ts +8 -0
  83. package/dist/types/sessions/in_memory_session_service.d.ts +9 -0
  84. package/dist/types/sessions/vertex_ai_session_service.d.ts +19 -1
  85. package/dist/types/skills/loader.d.ts +5 -0
  86. package/dist/types/tools/get_user_choice_tool.d.ts +14 -0
  87. package/dist/types/tools/request_input_tool.d.ts +15 -0
  88. package/dist/types/utils/crypto_shim.d.ts +16 -0
  89. package/dist/types/utils/env_aware_utils.d.ts +23 -0
  90. package/dist/types/utils/error_utils.d.ts +18 -0
  91. package/dist/types/utils/output_schema_utils.d.ts +18 -0
  92. package/dist/types/utils/vertex_ai_utils.d.ts +1 -0
  93. package/dist/types/version.d.ts +1 -1
  94. package/dist/web/a2a/a2a_remote_agent.js +12 -2
  95. package/dist/web/a2a/agent_to_a2a.js +0 -1
  96. package/dist/web/a2a/auth.js +37 -0
  97. package/dist/web/a2a/event_converter_utils.js +40 -15
  98. package/dist/web/agents/functions.js +20 -7
  99. package/dist/web/agents/llm_agent.js +2 -1
  100. package/dist/web/agents/processors/basic_llm_request_processor.js +3 -2
  101. package/dist/web/agents/processors/content_request_processor.js +2 -21
  102. package/dist/web/agents/processors/instructions_llm_request_processor.js +3 -1
  103. package/dist/web/artifacts/in_memory_artifact_service.js +10 -9
  104. package/dist/web/auth/credential_service/in_memory_credential_service.js +14 -3
  105. package/dist/web/code_executors/unsafe_local_code_executor.js +22 -11
  106. package/dist/web/common.js +4 -0
  107. package/dist/web/context/agent_controlled_context_compactor.js +2 -11
  108. package/dist/web/context/compaction_utils.js +12 -1
  109. package/dist/web/context/token_based_context_compactor.js +6 -21
  110. package/dist/web/index.js +2 -0
  111. package/dist/web/memory/in_memory_memory_service.js +13 -2
  112. package/dist/web/memory/vertex_ai_memory_bank_service.js +7 -1
  113. package/dist/web/plugins/base_plugin.js +4 -2
  114. package/dist/web/runner/runner.js +17 -11
  115. package/dist/web/sessions/base_session_service.js +8 -3
  116. package/dist/web/sessions/in_memory_session_service.js +8 -8
  117. package/dist/web/sessions/vertex_ai_session_service.js +24 -11
  118. package/dist/web/skills/loader.js +40 -6
  119. package/dist/web/tools/get_user_choice_tool.js +25 -0
  120. package/dist/web/tools/mcp/mcp_session_manager.js +35 -21
  121. package/dist/web/tools/request_input_tool.js +26 -0
  122. package/dist/web/utils/crypto_shim.js +15 -0
  123. package/dist/web/utils/env_aware_utils.js +15 -13
  124. package/dist/web/utils/error_utils.js +92 -0
  125. package/dist/web/utils/file_utils.js +5 -2
  126. package/dist/web/utils/output_schema_utils.js +15 -0
  127. package/dist/web/utils/vertex_ai_utils.js +2 -0
  128. package/dist/web/version.js +1 -1
  129. 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;
@@ -3,6 +3,7 @@
3
3
  * Copyright 2026 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
+ export declare const EXPRESS_MODE_UNSUPPORTED_MESSAGE: string;
6
7
  /**
7
8
  * Validates and returns the API key for Express Mode.
8
9
  *
@@ -3,4 +3,4 @@
3
3
  * Copyright 2025 Google LLC
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
- export declare const version = "1.5.0";
6
+ export declare const version = "1.6.0";
@@ -127,7 +127,12 @@ class RemoteA2AAgent extends BaseAgent {
127
127
  yield new __await(callback(context, chunk));
128
128
  }
129
129
  }
130
- const adkEvent = toAdkEvent(chunk, context.invocationId, this.name);
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(result, context.invocationId, this.name);
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;
@@ -70,7 +70,6 @@ async function toA2a(agent, options = {}) {
70
70
  );
71
71
  const app = (_d = options.app) != null ? _d : express();
72
72
  if (!options.app) {
73
- app.use(express.urlencoded({ limit: "50mb", extended: true }));
74
73
  app.use(express.json({ limit: "50mb" }));
75
74
  }
76
75
  app.use(
@@ -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: metadata[A2AMetadataKeys.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
- actions: createEventActions({
187
- escalate: !!metadata[A2AMetadataKeys.ESCALATE],
188
- transferToAgent: metadata[A2AMetadataKeys.TRANSFER_TO_AGENT]
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 && !functionResponse) {
290
+ if (tool.isLongRunning && functionResponse == null) {
278
291
  continue;
279
292
  }
280
293
  if (functionResponseError) {
281
294
  functionResponse = { error: functionResponseError };
282
- } else if (typeof functionResponse !== "object" || functionResponse == null) {
295
+ } else if (functionResponse == null) {
283
296
  functionResponse = { result: functionResponse };
284
- } else if (Array.isArray(functionResponse)) {
285
- functionResponse = { results: 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.tools.length === 0)) {
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 { isCompactedEvent } from "../../events/compacted_event.js";
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
- let events = invocationContext.session.events;
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.tools.length > 0) {
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 = "".concat(appName, "/").concat(userId, "/").concat(sessionId, "/");
74
- const usernamespacePrefix = "".concat(appName, "/").concat(userId, "/user/");
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
- const filename = path.replace(sessionPrefix, "");
79
- filenames.push(filename);
80
- } else if (path.startsWith(usernamespacePrefix)) {
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, "/").concat(userId, "/user/").concat(filename);
151
+ return "".concat(artifactPrefix("user", appName, userId)).concat(encodeURIComponent(filename));
154
152
  }
155
- return "".concat(appName, "/").concat(userId, "/").concat(sessionId, "/").concat(filename);
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
- this.credentials = {};
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 = path.join(
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.toLowerCase().includes("powershell")) {
105
- args = ["-NoLogo", "-ExecutionPolicy", "Bypass", "-File", filePath];
115
+ if (isPowerShellCommand(this.shellCommandPath)) {
116
+ args = [...POWERSHELL_BASE_ARGS, filePath];
106
117
  } else if (this.shellCommandPath.toLowerCase().includes("cmd")) {
107
- args = ["/c", filePath];
118
+ args = [...CMD_BASE_ARGS, filePath];
108
119
  }
109
120
  } else if (language === CodeExecutionLanguage.POWERSHELL) {
110
121
  command = IS_WINDOWS ? "powershell" : "pwsh";
111
- args = ["-NoLogo", "-ExecutionPolicy", "Bypass", "-File", filePath];
122
+ args = [...POWERSHELL_BASE_ARGS, filePath];
112
123
  } else if (language === CodeExecutionLanguage.WINDOWS_CMD) {
113
124
  command = "cmd.exe";
114
- args = ["/c", filePath];
125
+ args = [...CMD_BASE_ARGS, filePath];
115
126
  }
116
127
  if (params.codeExecutionInput.args) {
117
128
  if (Array.isArray(params.codeExecutionInput.args)) {
@@ -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,