@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
@@ -70,16 +70,14 @@ class InMemoryArtifactService {
70
70
  userId,
71
71
  sessionId
72
72
  }) {
73
- const sessionPrefix = `${appName}/${userId}/${sessionId}/`;
74
- const usernamespacePrefix = `${appName}/${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 `${appName}/${userId}/user/${filename}`;
151
+ return `${artifactPrefix("user", appName, userId)}${encodeURIComponent(filename)}`;
154
152
  }
155
- return `${appName}/${userId}/${sessionId}/${filename}`;
153
+ return `${artifactPrefix("session", appName, userId, sessionId)}${encodeURIComponent(filename)}`;
154
+ }
155
+ function artifactPrefix(scope, ...parts) {
156
+ return `${[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${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,
@@ -5,9 +5,9 @@ const require = topLevelCreateRequire(import.meta.url);
5
5
  * Copyright 2026 Google LLC
6
6
  * SPDX-License-Identifier: Apache-2.0
7
7
  */
8
- import { isCompactedEvent } from "../events/compacted_event.js";
9
8
  import { createEvent } from "../events/event.js";
10
9
  import { ContextCompactionTrigger } from "../plugins/base_plugin.js";
10
+ import { getActiveEvents } from "./compaction_utils.js";
11
11
  class AgentControlledContextCompactor {
12
12
  constructor(options) {
13
13
  this.trigger = ContextCompactionTrigger.AgentControlled;
@@ -18,7 +18,7 @@ class AgentControlledContextCompactor {
18
18
  }
19
19
  async compact(invocationContext) {
20
20
  const events = invocationContext.session.events;
21
- const activeEvents = this.getActiveEvents(events);
21
+ const activeEvents = getActiveEvents(events);
22
22
  const consolidateToolCallIndex = activeEvents.reduce(
23
23
  (acc, e, idx) => {
24
24
  var _a, _b;
@@ -66,15 +66,6 @@ class AgentControlledContextCompactor {
66
66
  delete invocationContext.session.state["temp:consolidate_context"];
67
67
  delete invocationContext.session.state["temp:consolidate_context_detail"];
68
68
  }
69
- getActiveEvents(events) {
70
- const latest = events.filter(isCompactedEvent).pop();
71
- return latest ? [
72
- latest,
73
- ...events.filter(
74
- (e) => !isCompactedEvent(e) && e.timestamp > latest.endTime
75
- )
76
- ] : events;
77
- }
78
69
  }
79
70
  export {
80
71
  AgentControlledContextCompactor
@@ -5,10 +5,20 @@ const require = topLevelCreateRequire(import.meta.url);
5
5
  * Copyright 2026 Google LLC
6
6
  * SPDX-License-Identifier: Apache-2.0
7
7
  */
8
+ import { isCompactedEvent } from "../events/compacted_event.js";
8
9
  import {
9
10
  getFunctionCalls,
10
11
  getFunctionResponses
11
12
  } from "../events/event.js";
13
+ function getActiveEvents(events) {
14
+ const latest = events.filter(isCompactedEvent).pop();
15
+ return latest ? [
16
+ latest,
17
+ ...events.filter(
18
+ (e) => !isCompactedEvent(e) && e.timestamp > latest.endTime
19
+ )
20
+ ] : events;
21
+ }
12
22
  function calculateRetainStartIndex(rawEvents, eventRetentionSize) {
13
23
  let retainStartIndex = Math.max(0, rawEvents.length - eventRetentionSize);
14
24
  while (retainStartIndex > 0) {
@@ -23,5 +33,6 @@ function calculateRetainStartIndex(rawEvents, eventRetentionSize) {
23
33
  return retainStartIndex;
24
34
  }
25
35
  export {
26
- calculateRetainStartIndex
36
+ calculateRetainStartIndex,
37
+ getActiveEvents
27
38
  };
@@ -7,7 +7,10 @@ const require = topLevelCreateRequire(import.meta.url);
7
7
  */
8
8
  import { getContents } from "../agents/processors/content_processor_utils.js";
9
9
  import { isCompactedEvent } from "../events/compacted_event.js";
10
- import { calculateRetainStartIndex } from "./compaction_utils.js";
10
+ import {
11
+ calculateRetainStartIndex,
12
+ getActiveEvents
13
+ } from "./compaction_utils.js";
11
14
  const CHARS_PER_TOKEN = 4;
12
15
  class TokenBasedContextCompactor {
13
16
  constructor(options) {
@@ -15,27 +18,9 @@ class TokenBasedContextCompactor {
15
18
  this.eventRetentionSize = options.eventRetentionSize;
16
19
  this.summarizer = options.summarizer;
17
20
  }
18
- getActiveEvents(events) {
19
- let latestCompactedEvent = void 0;
20
- for (let i = events.length - 1; i >= 0; i--) {
21
- const e = events[i];
22
- if (isCompactedEvent(e)) {
23
- if (!latestCompactedEvent || e.endTime > latestCompactedEvent.endTime) {
24
- latestCompactedEvent = e;
25
- }
26
- }
27
- }
28
- if (!latestCompactedEvent) {
29
- return events;
30
- }
31
- const activeRawEvents = events.filter(
32
- (e) => !isCompactedEvent(e) && e.timestamp > latestCompactedEvent.endTime
33
- );
34
- return [latestCompactedEvent, ...activeRawEvents];
35
- }
36
21
  shouldCompact(invocationContext) {
37
22
  const events = invocationContext.session.events;
38
- const activeEvents = this.getActiveEvents(events);
23
+ const activeEvents = getActiveEvents(events);
39
24
  const rawEvents = activeEvents.filter((e) => !isCompactedEvent(e));
40
25
  if (rawEvents.length <= this.eventRetentionSize) {
41
26
  return false;
@@ -58,7 +43,7 @@ class TokenBasedContextCompactor {
58
43
  }
59
44
  async compact(invocationContext) {
60
45
  const events = invocationContext.session.events;
61
- const activeEvents = this.getActiveEvents(events);
46
+ const activeEvents = getActiveEvents(events);
62
47
  const rawEvents = activeEvents.filter((e) => !isCompactedEvent(e));
63
48
  if (rawEvents.length <= this.eventRetentionSize) {
64
49
  return;
package/dist/esm/index.js CHANGED
@@ -9,6 +9,7 @@ import { AGENT_CARD_PATH, RemoteA2AAgent } from "./a2a/a2a_remote_agent.js";
9
9
  import { getA2AAgentCard } from "./a2a/agent_card.js";
10
10
  import { A2AAgentExecutor } from "./a2a/agent_executor.js";
11
11
  import { toA2a } from "./a2a/agent_to_a2a.js";
12
+ import { bearerTokenUserBuilder } from "./a2a/auth.js";
12
13
  import { InvocationContext } from "./agents/invocation_context.js";
13
14
  import { FileArtifactService } from "./artifacts/file_artifact_service.js";
14
15
  import { GcsArtifactService } from "./artifacts/gcs_artifact_service.js";
@@ -56,6 +57,7 @@ export {
56
57
  RunSkillScriptTool,
57
58
  UnsafeLocalCodeExecutor,
58
59
  VertexAiSessionService,
60
+ bearerTokenUserBuilder,
59
61
  getA2AAgentCard,
60
62
  getArtifactServiceFromUri,
61
63
  getSessionServiceFromUri,
@@ -8,12 +8,23 @@ const require = topLevelCreateRequire(import.meta.url);
8
8
  class InMemoryMemoryService {
9
9
  constructor() {
10
10
  this.memories = [];
11
- this.sessionEvents = {};
11
+ /**
12
+ * A map from user key to a map from session ID to events.
13
+ *
14
+ * The inner map is keyed by `session.id`, which arrives off the request path
15
+ * and contains no `/`, so it can be exactly `__proto__`. On a plain object
16
+ * literal that key would reach the inherited `__proto__` setter and
17
+ * re-parent the map instead of creating an own property, silently dropping
18
+ * the session from the `Object.values` scan in `searchMemory`. The outer key
19
+ * always contains a `/` (see `getUserKey`) and cannot collide, but is
20
+ * created the same way for consistency.
21
+ */
22
+ this.sessionEvents = /* @__PURE__ */ Object.create(null);
12
23
  }
13
24
  async addSessionToMemory(session) {
14
25
  const userKey = getUserKey(session.appName, session.userId);
15
26
  if (!this.sessionEvents[userKey]) {
16
- this.sessionEvents[userKey] = {};
27
+ this.sessionEvents[userKey] = /* @__PURE__ */ Object.create(null);
17
28
  }
18
29
  this.sessionEvents[userKey][session.id] = session.events.filter(
19
30
  (event) => {
@@ -8,7 +8,10 @@ const require = topLevelCreateRequire(import.meta.url);
8
8
  import { Client } from "@google-cloud/vertexai";
9
9
  import { createUserContent } from "@google/genai";
10
10
  import { logger } from "../utils/logger.js";
11
- import { getExpressModeApiKey } from "../utils/vertex_ai_utils.js";
11
+ import {
12
+ EXPRESS_MODE_UNSUPPORTED_MESSAGE,
13
+ getExpressModeApiKey
14
+ } from "../utils/vertex_ai_utils.js";
12
15
  const GENERATE_MEMORIES_KNOWN_FIELDS = [
13
16
  "disableConsolidation",
14
17
  "waitForCompletion",
@@ -89,6 +92,9 @@ class VertexAiMemoryBankService {
89
92
  if (options.client) {
90
93
  this.memories = options.client.agentEnginesInternal.memories;
91
94
  } else {
95
+ if (this.expressModeApiKey && (!this.projectId || !this.location)) {
96
+ throw new Error(EXPRESS_MODE_UNSUPPORTED_MESSAGE);
97
+ }
92
98
  const client = new Client({
93
99
  project: this.projectId,
94
100
  location: this.location
@@ -73,8 +73,10 @@ class BasePlugin {
73
73
  * @param params.invocationContext The context for the entire invocation.
74
74
  * @param params.event The event raised by the runner.
75
75
  * @returns An optional value. A non-`undefined` return may be used by the
76
- * framework to modify or replace the response. Returning `undefined`
77
- * allows the original response to be used.
76
+ * framework to modify or replace the response. Copy `params.event` when
77
+ * constructing a replacement to preserve fields that are not being
78
+ * modified, such as event actions. Returning `undefined` allows the
79
+ * original response to be used.
78
80
  */
79
81
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
80
82
  async onEventCallback(params) {
@@ -120,7 +120,7 @@ class Runner {
120
120
  ctx,
121
121
  this,
122
122
  async function* () {
123
- var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
123
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
124
124
  const session = await this.sessionService.getSession({
125
125
  appName: this.appName,
126
126
  userId,
@@ -237,24 +237,31 @@ class Runner {
237
237
  if ((_h = params.abortSignal) == null ? void 0 : _h.aborted) {
238
238
  return;
239
239
  }
240
- if (!event.partial) {
241
- await this.sessionService.appendEvent({ session, event });
242
- }
243
240
  const modifiedEvent = await this.pluginManager.runOnEventCallback({
244
241
  invocationContext,
245
242
  event
246
243
  });
247
- if ((_i = params.abortSignal) == null ? void 0 : _i.aborted) {
248
- return;
244
+ const outputEvent = modifiedEvent ? {
245
+ ...modifiedEvent,
246
+ id: event.id,
247
+ invocationId: event.invocationId,
248
+ timestamp: event.timestamp,
249
+ author: modifiedEvent.author || event.author,
250
+ branch: (_i = modifiedEvent.branch) != null ? _i : event.branch
251
+ } : event;
252
+ if (!event.partial) {
253
+ await this.sessionService.appendEvent({
254
+ session,
255
+ event: outputEvent
256
+ });
249
257
  }
250
- if (modifiedEvent) {
251
- yield modifiedEvent;
252
- } else {
253
- yield event;
258
+ if ((_j = params.abortSignal) == null ? void 0 : _j.aborted) {
259
+ return;
254
260
  }
261
+ yield outputEvent;
255
262
  }
256
263
  await this.pluginManager.runAfterRunCallback({ invocationContext });
257
- if ((_j = params.abortSignal) == null ? void 0 : _j.aborted) {
264
+ if ((_k = params.abortSignal) == null ? void 0 : _k.aborted) {
258
265
  return;
259
266
  }
260
267
  }
@@ -61,7 +61,12 @@ class BaseSessionService {
61
61
  if (key.startsWith(State.TEMP_PREFIX)) {
62
62
  continue;
63
63
  }
64
- session.state[key] = value;
64
+ Object.defineProperty(session.state, key, {
65
+ value,
66
+ writable: true,
67
+ enumerable: true,
68
+ configurable: true
69
+ });
65
70
  }
66
71
  }
67
72
  }
@@ -70,7 +75,7 @@ function trimTempDeltaState(event) {
70
75
  return event;
71
76
  }
72
77
  const stateDelta = event.actions.stateDelta;
73
- const filteredStateDelta = {};
78
+ const filteredStateDelta = /* @__PURE__ */ Object.create(null);
74
79
  for (const [key, value] of Object.entries(stateDelta)) {
75
80
  if (!key.startsWith(State.TEMP_PREFIX)) {
76
81
  filteredStateDelta[key] = value;
@@ -80,7 +85,7 @@ function trimTempDeltaState(event) {
80
85
  return event;
81
86
  }
82
87
  function trimTempState(state) {
83
- const filteredState = {};
88
+ const filteredState = /* @__PURE__ */ Object.create(null);
84
89
  for (const [key, value] of Object.entries(state)) {
85
90
  if (!key.startsWith(State.TEMP_PREFIX)) {
86
91
  filteredState[key] = value;
@@ -25,15 +25,15 @@ class InMemorySessionService extends BaseSessionService {
25
25
  * A map from app name to a map from user ID to a map from session ID to
26
26
  * session.
27
27
  */
28
- this.sessions = {};
28
+ this.sessions = /* @__PURE__ */ Object.create(null);
29
29
  /**
30
30
  * A map from app name to a map from user ID to a map from key to the value.
31
31
  */
32
- this.userState = {};
32
+ this.userState = /* @__PURE__ */ Object.create(null);
33
33
  /**
34
34
  * A map from app name to a map from key to the value.
35
35
  */
36
- this.appState = {};
36
+ this.appState = /* @__PURE__ */ Object.create(null);
37
37
  }
38
38
  async createSession({
39
39
  appName,
@@ -52,10 +52,10 @@ class InMemorySessionService extends BaseSessionService {
52
52
  lastUpdateTime: Date.now()
53
53
  });
54
54
  if (!this.sessions[appName]) {
55
- this.sessions[appName] = {};
55
+ this.sessions[appName] = /* @__PURE__ */ Object.create(null);
56
56
  }
57
57
  if (!this.sessions[appName][userId]) {
58
- this.sessions[appName][userId] = {};
58
+ this.sessions[appName][userId] = /* @__PURE__ */ Object.create(null);
59
59
  }
60
60
  this.sessions[appName][userId][session.id] = session;
61
61
  const copiedSession = cloneDeep(session);
@@ -220,12 +220,12 @@ class InMemorySessionService extends BaseSessionService {
220
220
  if (event.actions && event.actions.stateDelta) {
221
221
  for (const key of Object.keys(event.actions.stateDelta)) {
222
222
  if (key.startsWith(State.APP_PREFIX)) {
223
- this.appState[appName] = this.appState[appName] || {};
223
+ this.appState[appName] = this.appState[appName] || /* @__PURE__ */ Object.create(null);
224
224
  this.appState[appName][key.replace(State.APP_PREFIX, "")] = event.actions.stateDelta[key];
225
225
  }
226
226
  if (key.startsWith(State.USER_PREFIX)) {
227
- this.userState[appName] = this.userState[appName] || {};
228
- this.userState[appName][userId] = this.userState[appName][userId] || {};
227
+ this.userState[appName] = this.userState[appName] || /* @__PURE__ */ Object.create(null);
228
+ this.userState[appName][userId] = this.userState[appName][userId] || /* @__PURE__ */ Object.create(null);
229
229
  this.userState[appName][userId][key.replace(State.USER_PREFIX, "")] = event.actions.stateDelta[key];
230
230
  }
231
231
  }
@@ -19,7 +19,10 @@ import { Client } from "@google-cloud/vertexai/build/src/genai/client.js";
19
19
  import { isCompactedEvent } from "../events/compacted_event.js";
20
20
  import { experimental } from "../utils/experimental.js";
21
21
  import { logger } from "../utils/logger.js";
22
- import { getExpressModeApiKey } from "../utils/vertex_ai_utils.js";
22
+ import {
23
+ EXPRESS_MODE_UNSUPPORTED_MESSAGE,
24
+ getExpressModeApiKey
25
+ } from "../utils/vertex_ai_utils.js";
23
26
  import { partialCopy } from "../utils/partial_copy.js";
24
27
  import {
25
28
  BaseSessionService,
@@ -47,16 +50,14 @@ let VertexAiSessionService = class extends BaseSessionService {
47
50
  this.location,
48
51
  options.expressModeApiKey
49
52
  );
50
- if (!options.sessions) {
51
- if (!this.expressModeApiKey && (!this.projectId || !this.location)) {
52
- throw new Error(
53
- "Either (Project ID and Location) or an expressModeApiKey is required."
54
- );
55
- }
56
- }
57
53
  if (options.sessions) {
58
54
  this.sessions = options.sessions;
59
55
  } else {
56
+ if (!this.projectId || !this.location) {
57
+ throw new Error(
58
+ this.expressModeApiKey ? EXPRESS_MODE_UNSUPPORTED_MESSAGE : "Project ID and Location are required."
59
+ );
60
+ }
60
61
  const client = new Client({
61
62
  project: this.projectId,
62
63
  location: this.location
@@ -80,13 +81,25 @@ let VertexAiSessionService = class extends BaseSessionService {
80
81
  }
81
82
  return match[3];
82
83
  }
84
+ /**
85
+ * Creates a session on Vertex AI Agent Engine.
86
+ *
87
+ * @throws if both `ttl` and `expireTime` are specified.
88
+ */
83
89
  async createSession({
84
90
  appName,
85
91
  userId,
86
92
  state,
87
- sessionId
93
+ sessionId,
94
+ ttl,
95
+ expireTime
88
96
  }) {
89
97
  var _a;
98
+ if (ttl != null && expireTime != null) {
99
+ throw new Error(
100
+ "Cannot specify both 'ttl' and 'expireTime' simultaneously."
101
+ );
102
+ }
90
103
  const reasoningEngineId = this.getReasoningEngineId(appName);
91
104
  const filteredState = state ? trimTempState(state) : void 0;
92
105
  let apiResponse = await this.sessions.createInternal({
@@ -94,7 +107,9 @@ let VertexAiSessionService = class extends BaseSessionService {
94
107
  userId,
95
108
  config: {
96
109
  ...filteredState ? { sessionState: filteredState } : {},
97
- ...sessionId ? { sessionId } : {}
110
+ ...sessionId ? { sessionId } : {},
111
+ ...ttl != null ? { ttl } : {},
112
+ ...expireTime != null ? { expireTime } : {}
98
113
  }
99
114
  });
100
115
  const operationName = apiResponse.name;
@@ -180,7 +195,7 @@ let VertexAiSessionService = class extends BaseSessionService {
180
195
  return session;
181
196
  } catch (error) {
182
197
  const err = error;
183
- if (err.code === GRPC_NOT_FOUND || err.code === HTTP_NOT_FOUND) {
198
+ if (err.code === GRPC_NOT_FOUND || err.code === HTTP_NOT_FOUND || err.status === HTTP_NOT_FOUND) {
184
199
  return void 0;
185
200
  }
186
201
  logger.error(`Error getting session from Vertex AI: ${err.message}`);
@@ -18,6 +18,9 @@ const ALLOWED_FRONTMATTER_KEYS = /* @__PURE__ */ new Set([
18
18
  "description",
19
19
  "license",
20
20
  "allowed-tools",
21
+ // Camel-case alias: FrontmatterSchema's preprocessor derives `allowedTools`
22
+ // from `allowed-tools`, and `.loose()` passes either spelling through.
23
+ "allowedTools",
21
24
  "metadata",
22
25
  "compatibility"
23
26
  ]);
@@ -80,7 +83,7 @@ async function loadDir(directoryPath) {
80
83
  }
81
84
  return files;
82
85
  }
83
- function parseSkillMdContent(content) {
86
+ function parseFrontmatterYaml(content) {
84
87
  if (!content.startsWith("---")) {
85
88
  throw new Error("SKILL.md must start with YAML frontmatter (---)");
86
89
  }
@@ -92,15 +95,34 @@ function parseSkillMdContent(content) {
92
95
  const body = parts.filter((_, i) => i >= 2).join("---").trim();
93
96
  try {
94
97
  const parsed = yaml.load(frontmatterStr);
95
- if (typeof parsed !== "object" || parsed === null) {
98
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
96
99
  throw new Error("SKILL.md frontmatter must be a YAML mapping");
97
100
  }
98
- const frontmatter = FrontmatterSchema.parse(parsed);
99
- return { frontmatter, body };
101
+ return { raw: parsed, body };
100
102
  } catch (e) {
101
103
  throw new Error(`Invalid YAML in frontmatter: ${e.message}`);
102
104
  }
103
105
  }
106
+ function validateFrontmatter(raw) {
107
+ try {
108
+ return FrontmatterSchema.parse(raw);
109
+ } catch (e) {
110
+ throw new Error(`Invalid YAML in frontmatter: ${e.message}`);
111
+ }
112
+ }
113
+ function parseSkillMdContent(content) {
114
+ const { raw, body } = parseFrontmatterYaml(content);
115
+ return { frontmatter: validateFrontmatter(raw), body };
116
+ }
117
+ function isDangerousZipEntryName(entryName) {
118
+ if (path.posix.isAbsolute(entryName) || path.win32.isAbsolute(entryName)) {
119
+ return true;
120
+ }
121
+ return entryName.split(/[/\\]/).some((segment) => segment === "..");
122
+ }
123
+ function isBareSkillName(name) {
124
+ return name !== "." && name !== ".." && path.basename(name) === name;
125
+ }
104
126
  async function loadSkillFromDir(skillDir) {
105
127
  const resolvedDir = path.resolve(skillDir);
106
128
  const skill = await loadSkillFile(skillDir);
@@ -240,6 +262,11 @@ async function loadAllSkillsInDir(skillsBasePath) {
240
262
  function loadSkillFromZipBuffer(zipBuffer) {
241
263
  const zip = new AdmZip(zipBuffer);
242
264
  const entries = zip.getEntries();
265
+ for (const entry of entries) {
266
+ if (isDangerousZipEntryName(entry.entryName)) {
267
+ throw new Error(`Dangerous zip entry ignored: ${entry.entryName}`);
268
+ }
269
+ }
243
270
  let skillMdContent = "";
244
271
  for (const entry of entries) {
245
272
  if (entry.isDirectory) continue;
@@ -251,8 +278,15 @@ function loadSkillFromZipBuffer(zipBuffer) {
251
278
  if (!skillMdContent) {
252
279
  throw new Error("SKILL.md not found in zipped filesystem.");
253
280
  }
254
- const { frontmatter: parsed, body } = parseSkillMdContent(skillMdContent);
255
- const frontmatter = FrontmatterSchema.parse(parsed);
281
+ const { raw, body } = parseFrontmatterYaml(skillMdContent);
282
+ const skillName = raw["name"];
283
+ if (!skillName) {
284
+ throw new Error("SKILL.md frontmatter must contain 'name'");
285
+ }
286
+ if (typeof skillName !== "string" || !isBareSkillName(skillName)) {
287
+ throw new Error(`Invalid skill name in SKILL.md: ${String(skillName)}`);
288
+ }
289
+ const frontmatter = validateFrontmatter(raw);
256
290
  const references = {};
257
291
  const assets = {};
258
292
  const scripts = {};