@aws/agentcore 1.0.0-preview.23 → 1.0.0-preview.25

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 (42) hide show
  1. package/dist/assets/README.md +5 -0
  2. package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +146 -28
  3. package/dist/assets/__tests__/input-validation.test.ts +44 -0
  4. package/dist/assets/agents/AGENTS.md +2 -0
  5. package/dist/assets/evaluators/autoevals-lambda/execution-role-policy.json +15 -0
  6. package/dist/assets/evaluators/autoevals-lambda/lambda_function.py +37 -0
  7. package/dist/assets/evaluators/autoevals-lambda/pyproject.toml +22 -0
  8. package/dist/assets/evaluators/deepeval-lambda/execution-role-policy.json +15 -0
  9. package/dist/assets/evaluators/deepeval-lambda/lambda_function.py +29 -0
  10. package/dist/assets/evaluators/deepeval-lambda/pyproject.toml +19 -0
  11. package/dist/assets/mcp/python/pyproject.toml +1 -1
  12. package/dist/assets/python/a2a/googleadk/base/main.py +8 -2
  13. package/dist/assets/python/a2a/googleadk/base/pyproject.toml +4 -4
  14. package/dist/assets/python/a2a/langchain_langgraph/base/main.py +11 -5
  15. package/dist/assets/python/a2a/langchain_langgraph/base/pyproject.toml +2 -2
  16. package/dist/assets/python/a2a/strands/base/pyproject.toml +1 -1
  17. package/dist/assets/python/http/autogen/base/README.md +5 -0
  18. package/dist/assets/python/http/autogen/base/main.py +2 -0
  19. package/dist/assets/python/http/googleadk/base/README.md +5 -0
  20. package/dist/assets/python/http/googleadk/base/main.py +2 -0
  21. package/dist/assets/python/http/langchain_langgraph/base/README.md +5 -0
  22. package/dist/assets/python/http/langchain_langgraph/base/main.py +4 -0
  23. package/dist/assets/python/http/langchain_langgraph/base/pyproject.toml +2 -2
  24. package/dist/assets/python/http/openaiagents/base/README.md +5 -0
  25. package/dist/assets/python/http/openaiagents/base/main.py +2 -0
  26. package/dist/assets/python/http/strands/base/README.md +6 -0
  27. package/dist/assets/python/http/strands/base/main.py +40 -4
  28. package/dist/assets/python/http/strands/base/pyproject.toml +1 -1
  29. package/dist/assets/python/mcp/standalone/base/pyproject.toml +1 -1
  30. package/dist/assets/typescript/http/strands/base/README.md +5 -0
  31. package/dist/assets/typescript/http/strands/base/main.ts +8 -3
  32. package/dist/assets/typescript/http/vercelai/base/README.md +5 -0
  33. package/dist/assets/typescript/http/vercelai/base/main.ts +8 -2
  34. package/dist/cli/index.mjs +507 -502
  35. package/npm-shrinkwrap.json +234 -376
  36. package/package.json +4 -1
  37. package/scripts/extract-cli-model.mjs +47 -6
  38. package/scripts/extract-cli-model.test.mjs +43 -0
  39. package/scripts/render_adoc.py +81 -5
  40. package/scripts/safe-junit-reporter.ts +7 -0
  41. package/scripts/sanitize-test-artifacts.ts +65 -0
  42. package/scripts/test_render_adoc.py +38 -0
@@ -5,7 +5,7 @@ from google.adk.agents import Agent
5
5
  from google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor
6
6
  from google.adk.runners import Runner
7
7
  from google.adk.sessions import InMemorySessionService
8
- from a2a.types import AgentCapabilities, AgentCard, AgentSkill
8
+ from a2a.types import AgentCapabilities, AgentCard, AgentInterface, AgentSkill
9
9
  from bedrock_agentcore.runtime import serve_a2a
10
10
  from model.load import load_model
11
11
 
@@ -99,7 +99,6 @@ runner = Runner(
99
99
  card = AgentCard(
100
100
  name=agent.name,
101
101
  description=agent.description,
102
- url="http://localhost:9000/",
103
102
  version="0.1.0",
104
103
  capabilities=AgentCapabilities(streaming=True),
105
104
  skills=[
@@ -112,6 +111,13 @@ card = AgentCard(
112
111
  ],
113
112
  default_input_modes=["text"],
114
113
  default_output_modes=["text"],
114
+ supported_interfaces=[
115
+ AgentInterface(
116
+ protocol_binding="JSONRPC",
117
+ protocol_version="1.0",
118
+ url="http://localhost:9000/",
119
+ )
120
+ ],
115
121
  )
116
122
 
117
123
  if __name__ == "__main__":
@@ -9,11 +9,11 @@ description = "AgentCore A2A Agent using Google ADK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
- "a2a-sdk >= 0.2.0, < 1.0.0",
12
+ "a2a-sdk[http-server] >= 1.0.1, < 2.0.0",
13
13
  "aws-opentelemetry-distro",
14
- "bedrock-agentcore[a2a] >= 1.0.3",
15
- "google-adk >= 1.0.0, < 2.0.0",
16
- "google-genai >= 1.0.0, < 2.0.0",
14
+ "bedrock-agentcore[a2a-v1] >= 1.19.0",
15
+ "google-adk[a2a] >= 2.5.0, < 3.0.0",
16
+ "google-genai >= 2.9.0, < 3.0.0",
17
17
  # 1.13.0 was yanked for broken imports.
18
18
  "opentelemetry-resourcedetector-gcp >= 1.9.0a0, < 2.0.0, != 1.13.0",
19
19
  ]
@@ -4,11 +4,11 @@ import os
4
4
  from langchain_core.tools import tool
5
5
  from langgraph.prebuilt import create_react_agent
6
6
  from opentelemetry.instrumentation.langchain import LangchainInstrumentor
7
+ from a2a.helpers import new_task_from_user_message
7
8
  from a2a.server.agent_execution import AgentExecutor, RequestContext
8
9
  from a2a.server.events import EventQueue
9
10
  from a2a.server.tasks import TaskUpdater
10
- from a2a.types import AgentCapabilities, AgentCard, AgentSkill, Part, TextPart
11
- from a2a.utils import new_task
11
+ from a2a.types import AgentCapabilities, AgentCard, AgentInterface, AgentSkill, Part
12
12
  from bedrock_agentcore.runtime import serve_a2a
13
13
  from model.load import load_model
14
14
 
@@ -100,7 +100,7 @@ class LangGraphA2AExecutor(AgentExecutor):
100
100
  self.graph = graph
101
101
 
102
102
  async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
103
- task = context.current_task or new_task(context.message)
103
+ task = context.current_task or new_task_from_user_message(context.message)
104
104
  if not context.current_task:
105
105
  await event_queue.enqueue_event(task)
106
106
  updater = TaskUpdater(event_queue, task.id, task.context_id)
@@ -109,7 +109,7 @@ class LangGraphA2AExecutor(AgentExecutor):
109
109
  result = await self.graph.ainvoke({"messages": [("user", user_text)]})
110
110
  response = result["messages"][-1].content
111
111
 
112
- await updater.add_artifact([Part(root=TextPart(text=response))])
112
+ await updater.add_artifact([Part(text=response)])
113
113
  await updater.complete()
114
114
 
115
115
  async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
@@ -119,7 +119,6 @@ class LangGraphA2AExecutor(AgentExecutor):
119
119
  card = AgentCard(
120
120
  name="{{ name }}",
121
121
  description="A LangGraph agent on Bedrock AgentCore",
122
- url="http://localhost:9000/",
123
122
  version="0.1.0",
124
123
  capabilities=AgentCapabilities(streaming=True),
125
124
  skills=[
@@ -132,6 +131,13 @@ card = AgentCard(
132
131
  ],
133
132
  default_input_modes=["text"],
134
133
  default_output_modes=["text"],
134
+ supported_interfaces=[
135
+ AgentInterface(
136
+ protocol_binding="JSONRPC",
137
+ protocol_version="1.0",
138
+ url="http://localhost:9000/",
139
+ )
140
+ ],
135
141
  )
136
142
 
137
143
  if __name__ == "__main__":
@@ -9,7 +9,7 @@ description = "AgentCore A2A Agent using LangChain + LangGraph"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
- "a2a-sdk >= 0.2.0, < 1.0.0",
12
+ "a2a-sdk[http-server] >= 1.0.1, < 2.0.0",
13
13
  {{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
14
14
  {{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
15
15
  {{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
@@ -17,7 +17,7 @@ dependencies = [
17
17
  {{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
18
18
  {{/if}}"aws-opentelemetry-distro",
19
19
  "opentelemetry-instrumentation-langchain >= 0.59.0",
20
- "bedrock-agentcore[a2a] >= 1.8.0",
20
+ "bedrock-agentcore[a2a-v1] >= 1.19.0",
21
21
  "botocore[crt] >= 1.35.0",
22
22
  "langgraph >= 0.2.0",
23
23
  ]
@@ -10,7 +10,7 @@ readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
12
  {{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
13
- {{/if}}"a2a-sdk[all] >= 0.2.0, < 1.0.0",
13
+ {{/if}}"a2a-sdk[all] >= 0.3.0, < 0.4.0",
14
14
  "aws-opentelemetry-distro",
15
15
  "bedrock-agentcore[a2a] >= 1.9.1",
16
16
  "botocore[crt] >= 1.35.0",
@@ -13,6 +13,11 @@ file defines a Starlette ASGI app with the AutoGen framework running within.
13
13
 
14
14
  `model/load.py` instantiates your chosen model provider.
15
15
 
16
+ ## Input Validation
17
+
18
+ Validate invocation input before forwarding it to the agent framework. Keep plain prompts typed as strings and pass
19
+ only prompt text to the agent.
20
+
16
21
  ## Environment Variables
17
22
 
18
23
  | Variable | Required | Description |
@@ -127,6 +127,8 @@ async def invoke(payload, context):
127
127
 
128
128
  # Process the user prompt
129
129
  prompt = payload.get("prompt", "What can you help me with?")
130
+ if not isinstance(prompt, str):
131
+ raise ValueError("prompt must be a string")
130
132
  session_id = getattr(context, "session_id", "default-session")
131
133
 
132
134
  # Reuse the per-session agent (preserves conversation history)
@@ -13,6 +13,11 @@ file defines a Starlette ASGI app with the Google ADK framework running within.
13
13
 
14
14
  `model/load.py` instantiates your chosen model provider (Gemini).
15
15
 
16
+ ## Input Validation
17
+
18
+ Validate invocation input before forwarding it to the agent framework. Keep plain prompts typed as strings and pass
19
+ only prompt text to the agent.
20
+
16
21
  ## Environment Variables
17
22
 
18
23
  | Variable | Required | Description |
@@ -191,6 +191,8 @@ async def invoke(payload, context):
191
191
 
192
192
  # Process the user prompt
193
193
  prompt = payload.get("prompt", "What can you help me with?")
194
+ if not isinstance(prompt, str):
195
+ raise ValueError("prompt must be a string")
194
196
  session_id = getattr(context, "session_id", "default_session")
195
197
  user_id = payload.get("user_id", "default_user")
196
198
 
@@ -13,6 +13,11 @@ file defines a Starlette ASGI app with the LangChain/LangGraph framework running
13
13
 
14
14
  `model/load.py` instantiates your chosen model provider.
15
15
 
16
+ ## Input Validation
17
+
18
+ Validate invocation input before forwarding it to the agent framework. Keep plain prompts typed as strings and pass
19
+ only prompt text to the agent.
20
+
16
21
  ## Environment Variables
17
22
 
18
23
  | Variable | Required | Description |
@@ -183,6 +183,8 @@ async def invoke(payload, context):
183
183
 
184
184
  # Process the user prompt
185
185
  prompt = payload.get("prompt", "What can you help me with?")
186
+ if not isinstance(prompt, str):
187
+ raise ValueError("prompt must be a string")
186
188
  session_id = getattr(context, "session_id", "default-session")
187
189
  touch_thread(session_id)
188
190
  log.info(f"Agent input: {prompt}")
@@ -202,6 +204,8 @@ async def invoke(payload, context):
202
204
 
203
205
  # Process the user prompt
204
206
  prompt = payload.get("prompt", "What can you help me with?")
207
+ if not isinstance(prompt, str):
208
+ raise ValueError("prompt must be a string")
205
209
  session_id = getattr(context, "session_id", "default-session")
206
210
  touch_thread(session_id)
207
211
  log.info(f"Agent input: {prompt}")
@@ -12,8 +12,8 @@ dependencies = [
12
12
  "aws-opentelemetry-distro",
13
13
  "opentelemetry-instrumentation-langchain >= 0.59.0",
14
14
  "langgraph >= 1.0.2",
15
- "mcp >= 1.19.0",
16
- "langchain-mcp-adapters >= 0.2.0",
15
+ "mcp ~= 1.24.0",
16
+ "langchain-mcp-adapters >= 0.3.1",
17
17
  "langchain >= 1.0.3",
18
18
  "bedrock-agentcore >= 1.8.0",
19
19
  "botocore[crt] >= 1.35.0",
@@ -13,6 +13,11 @@ file defines a Starlette ASGI app with the OpenAI Agents SDK framework running w
13
13
 
14
14
  `model/load.py` instantiates your chosen model provider (OpenAI).
15
15
 
16
+ ## Input Validation
17
+
18
+ Validate invocation input before forwarding it to the agent framework. Keep plain prompts typed as strings and pass
19
+ only prompt text to the agent.
20
+
16
21
  ## Environment Variables
17
22
 
18
23
  | Variable | Required | Description |
@@ -184,6 +184,8 @@ async def invoke(payload, context):
184
184
 
185
185
  # Process the user prompt
186
186
  prompt = payload.get("prompt", "What can you help me with?")
187
+ if not isinstance(prompt, str):
188
+ raise ValueError("prompt must be a string")
187
189
  session_id = getattr(context, "session_id", "default-session")
188
190
  session = get_session(session_id)
189
191
 
@@ -13,6 +13,12 @@ file defines a Starlette ASGI app with the chosen Agent framework SDK running wi
13
13
 
14
14
  `model/load.py` instantiates your chosen model provider.
15
15
 
16
+ ## Input Validation
17
+
18
+ Validate invocation input before forwarding it to Strands. Keep plain prompts typed as strings. If the app accepts a
19
+ caller-supplied message history, retain `strip_trailing_tool_use()`, which normalizes the history tail before
20
+ invoking the agent.
21
+
16
22
  ## Environment Variables
17
23
 
18
24
  | Variable | Required | Description |
@@ -481,17 +481,53 @@ get_or_create_agent = agent_factory()
481
481
  {{/if}}
482
482
 
483
483
 
484
+ def strip_trailing_tool_use(messages: Any) -> list[dict]:
485
+ """Strip toolUse blocks from the tail until the last message has none."""
486
+ if not isinstance(messages, list):
487
+ raise ValueError("messages must be a list")
488
+
489
+ messages = list(messages)
490
+ while messages:
491
+ last = messages[-1]
492
+ if not isinstance(last, dict):
493
+ raise ValueError("each message must be an object")
494
+ original_content = last.get("content", [])
495
+ if not isinstance(original_content, list) or not all(isinstance(block, dict) for block in original_content):
496
+ raise ValueError("each message content value must be a list of content blocks")
497
+
498
+ content = [block for block in original_content if "toolUse" not in block]
499
+ if len(content) == len(original_content):
500
+ break
501
+ if content:
502
+ messages[-1] = {**last, "content": content}
503
+ break
504
+ messages.pop()
505
+
506
+ return messages
507
+
508
+
484
509
  def _extract_prompt(payload: dict):
485
- """Accept harness-style messages[], tool_results[], or plain prompt string payloads."""
510
+ """Accept validated harness messages, tool results, or a plain prompt string."""
511
+ if not isinstance(payload, dict):
512
+ raise ValueError("payload must be a JSON object")
486
513
  if "messages" in payload:
487
- return payload["messages"]
514
+ return strip_trailing_tool_use(payload["messages"])
488
515
  if "tool_results" in payload:
516
+ tool_results = payload["tool_results"]
517
+ if not isinstance(tool_results, list) or not all(
518
+ isinstance(tool_result, dict) and isinstance(tool_result.get("toolUseId"), str)
519
+ for tool_result in tool_results
520
+ ):
521
+ raise ValueError("tool_results must contain objects with a toolUseId string")
489
522
  return [{"role": "user", "content": [{"toolResult": {
490
523
  "toolUseId": tr["toolUseId"],
491
524
  "status": tr.get("status", "success"),
492
525
  "content": tr.get("content", []),
493
- }} for tr in payload["tool_results"]]}]
494
- return payload.get("prompt", "")
526
+ }} for tr in tool_results]}]
527
+ prompt = payload.get("prompt", "")
528
+ if not isinstance(prompt, str):
529
+ raise ValueError("prompt must be a string")
530
+ return prompt
495
531
 
496
532
 
497
533
  def _has_inline_function_call(messages) -> bool:
@@ -14,7 +14,7 @@ dependencies = [
14
14
  "bedrock-agentcore >= 1.9.1",
15
15
  "botocore[crt] >= 1.35.0",
16
16
  {{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
17
- {{/if}}"mcp >= 1.19.0",
17
+ {{/if}}"mcp ~= 1.24.0",
18
18
  {{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
19
19
  {{/if}}{{#if (eq modelProvider "LiteLLM")}}"litellm >= 1.0.0",
20
20
  {{/if}}{{#if bedrockMantle}}"openai >= 1.0.0",
@@ -9,7 +9,7 @@ description = "AgentCore MCP Server"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
- "mcp >= 1.19.0",
12
+ "mcp ~= 1.24.0",
13
13
  ]
14
14
 
15
15
  [tool.hatch.build.targets.wheel]
@@ -13,6 +13,11 @@ defines an HTTP server that streams tokens from your chosen Agent framework SDK.
13
13
 
14
14
  `model/load.ts` instantiates your chosen model provider.
15
15
 
16
+ ## Input Validation
17
+
18
+ The generated Zod request schema keeps plain prompts typed as strings before forwarding them to Strands. Retain this
19
+ validation when extending the request shape, and pass only prompt text to the agent.
20
+
16
21
  ## Environment Variables
17
22
 
18
23
  | Variable | Required | Description |
@@ -34,6 +34,10 @@ const SYSTEM_PROMPT = `
34
34
  You are a helpful assistant. Use tools when appropriate.
35
35
  `;
36
36
 
37
+ const requestSchema = z.object({
38
+ prompt: z.string().default(''),
39
+ });
40
+
37
41
  {{#if hasMemory}}
38
42
  const agentCache = new Map<string, Agent>();
39
43
 
@@ -87,7 +91,8 @@ async function getOrCreateAgent(sessionId: string): Promise<Agent> {
87
91
 
88
92
  const app = new BedrockAgentCoreApp({
89
93
  invocationHandler: {
90
- async *process(payload: any, context: any) {
94
+ requestSchema,
95
+ async *process(payload, context) {
91
96
  {{#if hasMemory}}
92
97
  const sessionId = context?.sessionId ?? 'default-session';
93
98
  const actorId = getActorId(payload, context);
@@ -99,7 +104,7 @@ const app = new BedrockAgentCoreApp({
99
104
 
100
105
  {{#if hasMemory}}
101
106
  try {
102
- for await (const event of agent.stream(payload.prompt ?? '')) {
107
+ for await (const event of agent.stream(payload.prompt)) {
103
108
  if (
104
109
  event.type === 'modelStreamUpdateEvent' &&
105
110
  event.event?.type === 'modelContentBlockDeltaEvent' &&
@@ -123,7 +128,7 @@ const app = new BedrockAgentCoreApp({
123
128
  // e.g. Anthropic). Restoring on error keeps the session reusable.
124
129
  const snapshot = agent.takeSnapshot({ include: ['messages'] });
125
130
  try {
126
- for await (const event of agent.stream(payload.prompt ?? '')) {
131
+ for await (const event of agent.stream(payload.prompt)) {
127
132
  if (
128
133
  event.type === 'modelStreamUpdateEvent' &&
129
134
  event.event?.type === 'modelContentBlockDeltaEvent' &&
@@ -13,6 +13,11 @@ defines an HTTP app that streams tokens using the Vercel AI SDK's `streamText` A
13
13
 
14
14
  `model/load.ts` instantiates your chosen model provider.
15
15
 
16
+ ## Input Validation
17
+
18
+ The generated Zod request schema keeps plain prompts typed as strings before forwarding them to the agent framework.
19
+ Retain this validation when extending the request shape, and pass only prompt text to the agent.
20
+
16
21
  ## Environment Variables
17
22
 
18
23
  | Variable | Required | Description |
@@ -1,9 +1,14 @@
1
1
  import { BedrockAgentCoreApp } from 'bedrock-agentcore/runtime';
2
2
  import { streamText, type ModelMessage } from 'ai';
3
+ import { z } from 'zod';
3
4
  import { loadModel } from './model/load.js';
4
5
 
5
6
  const SYSTEM_PROMPT = `You are a helpful assistant.`;
6
7
 
8
+ const requestSchema = z.object({
9
+ prompt: z.string().default(''),
10
+ });
11
+
7
12
  const HISTORY_LIMIT = 128;
8
13
 
9
14
  // Keeps one message history per sessionId so each session remembers its own
@@ -32,10 +37,11 @@ function getHistory(sessionId: string): ModelMessage[] {
32
37
 
33
38
  const app = new BedrockAgentCoreApp({
34
39
  invocationHandler: {
35
- async *process(payload: any, context: any) {
40
+ requestSchema,
41
+ async *process(payload, context) {
36
42
  const sessionId = context?.sessionId ?? 'default-session';
37
43
  const history = getHistory(sessionId);
38
- const userMessage: ModelMessage = { role: 'user', content: payload.prompt ?? '' };
44
+ const userMessage: ModelMessage = { role: 'user', content: payload.prompt };
39
45
 
40
46
  const model = await loadModel();
41
47
  const result = streamText({