@aws/agentcore 0.9.1 → 1.0.0-preview.2

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 (89) hide show
  1. package/dist/agent-inspector/index.css +1 -1
  2. package/dist/agent-inspector/index.js +90 -72
  3. package/dist/assets/README.md +56 -31
  4. package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +1781 -259
  5. package/dist/assets/__tests__/__snapshots__/dockerfile-render.test.ts.snap +77 -0
  6. package/dist/assets/__tests__/dockerfile-render.test.ts +24 -0
  7. package/dist/assets/agents/AGENTS.md +84 -55
  8. package/dist/assets/cdk/bin/cdk.ts +40 -0
  9. package/dist/assets/cdk/cdk.json +1 -1
  10. package/dist/assets/cdk/lib/cdk-stack.ts +59 -2
  11. package/dist/assets/container/python/Dockerfile +4 -0
  12. package/dist/assets/evaluators/python-lambda/execution-role-policy.json +1 -1
  13. package/dist/assets/harness/invoke.py.template +74 -0
  14. package/dist/assets/python/a2a/googleadk/base/main.py +65 -3
  15. package/dist/assets/python/a2a/langchain_langgraph/base/main.py +64 -1
  16. package/dist/assets/python/a2a/strands/base/main.py +65 -2
  17. package/dist/assets/python/agui/googleadk/base/README.md +30 -0
  18. package/dist/assets/python/agui/googleadk/base/gitignore.template +41 -0
  19. package/dist/assets/python/agui/googleadk/base/main.py +31 -0
  20. package/dist/assets/python/agui/googleadk/base/model/__init__.py +1 -0
  21. package/dist/assets/python/agui/googleadk/base/model/load.py +41 -0
  22. package/dist/assets/python/agui/googleadk/base/pyproject.toml +24 -0
  23. package/dist/assets/python/agui/langchain_langgraph/base/README.md +22 -0
  24. package/dist/assets/python/agui/langchain_langgraph/base/gitignore.template +41 -0
  25. package/dist/assets/python/agui/langchain_langgraph/base/main.py +74 -0
  26. package/dist/assets/python/agui/langchain_langgraph/base/model/__init__.py +1 -0
  27. package/dist/assets/python/agui/langchain_langgraph/base/model/load.py +123 -0
  28. package/dist/assets/python/agui/langchain_langgraph/base/pyproject.toml +30 -0
  29. package/dist/assets/python/agui/strands/base/README.md +22 -0
  30. package/dist/assets/python/agui/strands/base/gitignore.template +41 -0
  31. package/dist/assets/python/agui/strands/base/main.py +43 -0
  32. package/dist/assets/python/agui/strands/base/model/__init__.py +1 -0
  33. package/dist/assets/python/agui/strands/base/model/load.py +123 -0
  34. package/dist/assets/python/agui/strands/base/pyproject.toml +27 -0
  35. package/dist/assets/python/agui/strands/capabilities/memory/__init__.py +1 -0
  36. package/dist/assets/python/agui/strands/capabilities/memory/session.py +38 -0
  37. package/dist/assets/python/http/autogen/base/main.py +61 -1
  38. package/dist/assets/python/http/googleadk/base/main.py +62 -2
  39. package/dist/assets/python/http/langchain_langgraph/base/main.py +61 -1
  40. package/dist/assets/python/http/openaiagents/base/main.py +70 -4
  41. package/dist/assets/python/http/strands/base/main.py +64 -6
  42. package/dist/cli/index.mjs +415 -377
  43. package/dist/lib/constants.d.ts +1 -0
  44. package/dist/lib/constants.d.ts.map +1 -1
  45. package/dist/lib/constants.js +3 -1
  46. package/dist/lib/constants.js.map +1 -1
  47. package/dist/lib/errors/config.d.ts.map +1 -1
  48. package/dist/lib/errors/config.js +5 -2
  49. package/dist/lib/errors/config.js.map +1 -1
  50. package/dist/lib/schemas/io/config-io.d.ts +9 -1
  51. package/dist/lib/schemas/io/config-io.d.ts.map +1 -1
  52. package/dist/lib/schemas/io/config-io.js +14 -0
  53. package/dist/lib/schemas/io/config-io.js.map +1 -1
  54. package/dist/lib/schemas/io/path-resolver.d.ts +12 -0
  55. package/dist/lib/schemas/io/path-resolver.d.ts.map +1 -1
  56. package/dist/lib/schemas/io/path-resolver.js +18 -0
  57. package/dist/lib/schemas/io/path-resolver.js.map +1 -1
  58. package/dist/schema/constants.d.ts +1 -0
  59. package/dist/schema/constants.d.ts.map +1 -1
  60. package/dist/schema/constants.js +8 -1
  61. package/dist/schema/constants.js.map +1 -1
  62. package/dist/schema/schemas/agent-env.d.ts +20 -0
  63. package/dist/schema/schemas/agent-env.d.ts.map +1 -1
  64. package/dist/schema/schemas/agent-env.js +17 -2
  65. package/dist/schema/schemas/agent-env.js.map +1 -1
  66. package/dist/schema/schemas/agentcore-project.d.ts +17 -0
  67. package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
  68. package/dist/schema/schemas/agentcore-project.js +18 -1
  69. package/dist/schema/schemas/agentcore-project.js.map +1 -1
  70. package/dist/schema/schemas/aws-targets.d.ts +3 -0
  71. package/dist/schema/schemas/aws-targets.d.ts.map +1 -1
  72. package/dist/schema/schemas/aws-targets.js +1 -0
  73. package/dist/schema/schemas/aws-targets.js.map +1 -1
  74. package/dist/schema/schemas/deployed-state.d.ts +50 -0
  75. package/dist/schema/schemas/deployed-state.d.ts.map +1 -1
  76. package/dist/schema/schemas/deployed-state.js +15 -1
  77. package/dist/schema/schemas/deployed-state.js.map +1 -1
  78. package/dist/schema/schemas/primitives/harness.d.ts +287 -0
  79. package/dist/schema/schemas/primitives/harness.d.ts.map +1 -0
  80. package/dist/schema/schemas/primitives/harness.js +237 -0
  81. package/dist/schema/schemas/primitives/harness.js.map +1 -0
  82. package/dist/schema/schemas/primitives/index.d.ts +2 -0
  83. package/dist/schema/schemas/primitives/index.d.ts.map +1 -1
  84. package/dist/schema/schemas/primitives/index.js +14 -1
  85. package/dist/schema/schemas/primitives/index.js.map +1 -1
  86. package/package.json +2 -2
  87. package/scripts/bump-version.ts +7 -80
  88. package/scripts/bundle.mjs +57 -3
  89. package/dist/agent-inspector/index.js.map +0 -1
@@ -99,6 +99,45 @@ async function main() {
99
99
  throw new Error('No deployment targets configured. Please define targets in agentcore/aws-targets.json');
100
100
  }
101
101
 
102
+ // Read harness configs for role creation.
103
+ // Harness fields may not yet be on the AgentCoreProjectSpec type from @aws/agentcore-cdk,
104
+ // so we read them dynamically via specAny (same pattern as gateways above).
105
+ // Harness paths in agentcore.json are relative to the project root (parent of agentcore/).
106
+ const projectRoot = path.resolve(configRoot, '..');
107
+ const harnessConfigs: {
108
+ name: string;
109
+ executionRoleArn?: string;
110
+ memoryName?: string;
111
+ containerUri?: string;
112
+ hasDockerfile?: boolean;
113
+ dockerfileName?: string;
114
+ harnessDir?: string;
115
+ tools?: { type: string; name: string }[];
116
+ apiKeyArn?: string;
117
+ }[] = [];
118
+ for (const entry of specAny.harnesses ?? []) {
119
+ const harnessDir = path.resolve(projectRoot, entry.path);
120
+ const harnessPath = path.resolve(harnessDir, 'harness.json');
121
+ try {
122
+ const harnessSpec = JSON.parse(fs.readFileSync(harnessPath, 'utf-8'));
123
+ harnessConfigs.push({
124
+ name: entry.name,
125
+ executionRoleArn: harnessSpec.executionRoleArn,
126
+ memoryName: harnessSpec.memory?.name,
127
+ containerUri: harnessSpec.containerUri,
128
+ hasDockerfile: !!harnessSpec.dockerfile,
129
+ dockerfileName: harnessSpec.dockerfile,
130
+ harnessDir,
131
+ tools: harnessSpec.tools,
132
+ apiKeyArn: harnessSpec.model?.apiKeyArn,
133
+ });
134
+ } catch (err) {
135
+ throw new Error(
136
+ \`Could not read harness.json for "\${entry.name}" at \${harnessPath}: \${err instanceof Error ? err.message : err}\`
137
+ );
138
+ }
139
+ }
140
+
102
141
  const app = new App();
103
142
 
104
143
  for (const target of targets) {
@@ -118,6 +157,7 @@ async function main() {
118
157
  spec,
119
158
  mcpSpec,
120
159
  credentials,
160
+ harnesses: harnessConfigs.length > 0 ? harnessConfigs : undefined,
121
161
  env,
122
162
  description: \`AgentCore stack for \${spec.name} deployed to \${target.name} (\${target.region})\`,
123
163
  tags: {
@@ -149,7 +189,7 @@ exports[`Assets Directory Snapshots > CDK assets > cdk/cdk/cdk.json should match
149
189
  "@aws-cdk/aws-ecs-patterns:secGroupsDisablesImplicitOpenListener": true,
150
190
  "@aws-cdk/aws-lambda:recognizeLayerVersion": true,
151
191
  "@aws-cdk/core:checkSecretUsage": true,
152
- "@aws-cdk/core:target-partitions": ["aws", "aws-cn"],
192
+ "@aws-cdk/core:target-partitions": ["aws", "aws-cn", "aws-us-gov"],
153
193
  "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
154
194
  "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
155
195
  "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
@@ -261,10 +301,26 @@ exports[`Assets Directory Snapshots > CDK assets > cdk/cdk/lib/cdk-stack.ts shou
261
301
  AgentCoreMcp,
262
302
  type AgentCoreProjectSpec,
263
303
  type AgentCoreMcpSpec,
304
+ ContainerSourceAssetFromPath,
305
+ AgentEcrRepository,
306
+ ContainerBuildProject,
307
+ ContainerImageBuilder,
264
308
  } from '@aws/agentcore-cdk';
265
309
  import { CfnOutput, Stack, type StackProps } from 'aws-cdk-lib';
266
310
  import { Construct } from 'constructs';
267
311
 
312
+ export interface HarnessConfig {
313
+ name: string;
314
+ executionRoleArn?: string;
315
+ memoryName?: string;
316
+ containerUri?: string;
317
+ hasDockerfile?: boolean;
318
+ dockerfileName?: string;
319
+ harnessDir?: string;
320
+ tools?: { type: string; name: string }[];
321
+ apiKeyArn?: string;
322
+ }
323
+
268
324
  export interface AgentCoreStackProps extends StackProps {
269
325
  /**
270
326
  * The AgentCore project specification containing agents, memories, and credentials.
@@ -278,6 +334,10 @@ export interface AgentCoreStackProps extends StackProps {
278
334
  * Credential provider ARNs from deployed state, keyed by credential name.
279
335
  */
280
336
  credentials?: Record<string, { credentialProviderArn: string; clientSecretArn?: string }>;
337
+ /**
338
+ * Harness role configurations. Each entry creates an IAM execution role for a harness.
339
+ */
340
+ harnesses?: HarnessConfig[];
281
341
  }
282
342
 
283
343
  /**
@@ -293,11 +353,48 @@ export class AgentCoreStack extends Stack {
293
353
  constructor(scope: Construct, id: string, props: AgentCoreStackProps) {
294
354
  super(scope, id, props);
295
355
 
296
- const { spec, mcpSpec, credentials } = props;
356
+ const { spec, mcpSpec, credentials, harnesses } = props;
357
+
358
+ // Build container images for harnesses that specify a dockerfile (no containerUri).
359
+ // Produces CDK outputs consumed by the imperative harness deployer.
360
+ const harnessesForCdk = harnesses ? [...harnesses] : [];
361
+ if (harnesses) {
362
+ for (let i = 0; i < harnesses.length; i++) {
363
+ const h = harnesses[i]!;
364
+ if (h.hasDockerfile && !h.containerUri && h.harnessDir) {
365
+ const pascalName = h.name.replace(/(^|_)([a-z])/g, (_: string, __: string, c: string) => c.toUpperCase());
366
+ const sourceAsset = new ContainerSourceAssetFromPath(this, \`Harness\${pascalName}SourceAsset\`, {
367
+ sourcePath: h.harnessDir,
368
+ });
369
+ const ecrRepo = new AgentEcrRepository(this, \`Harness\${pascalName}EcrRepo\`, {
370
+ projectName: spec.name,
371
+ agentName: \`harness-\${h.name}\`,
372
+ });
373
+ const buildProject = ContainerBuildProject.getOrCreate(this);
374
+ buildProject.grantPushTo(ecrRepo.repository);
375
+ sourceAsset.asset.grantRead(buildProject.role);
376
+
377
+ const builder = new ContainerImageBuilder(this, \`Harness\${pascalName}ContainerBuild\`, {
378
+ buildProject,
379
+ sourceAsset,
380
+ repository: ecrRepo,
381
+ dockerfile: h.dockerfileName ?? 'Dockerfile',
382
+ });
383
+
384
+ new CfnOutput(this, \`Harness\${pascalName}ContainerUriOutput\`, {
385
+ value: builder.containerUri,
386
+ });
387
+
388
+ // Pass the built containerUri to the harness role construct so it gets ECR pull permissions
389
+ harnessesForCdk[i] = { ...h, containerUri: builder.containerUri };
390
+ }
391
+ }
392
+ }
297
393
 
298
- // Create AgentCoreApplication with all agents
394
+ // Create AgentCoreApplication with all agents and harness roles
299
395
  this.application = new AgentCoreApplication(this, 'Application', {
300
396
  spec,
397
+ harnesses: harnessesForCdk.length > 0 ? harnessesForCdk : undefined,
301
398
  });
302
399
 
303
400
  // Create AgentCoreMcp if there are gateways configured
@@ -449,6 +546,7 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
449
546
  "evaluators/python-lambda/execution-role-policy.json",
450
547
  "evaluators/python-lambda/lambda_function.py",
451
548
  "evaluators/python-lambda/pyproject.toml",
549
+ "harness/invoke.py.template",
452
550
  "mcp/python-lambda/README.md",
453
551
  "mcp/python-lambda/handler.py",
454
552
  "mcp/python-lambda/pyproject.toml",
@@ -475,6 +573,26 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
475
573
  "python/a2a/strands/base/pyproject.toml",
476
574
  "python/a2a/strands/capabilities/memory/__init__.py",
477
575
  "python/a2a/strands/capabilities/memory/session.py",
576
+ "python/agui/googleadk/base/README.md",
577
+ "python/agui/googleadk/base/gitignore.template",
578
+ "python/agui/googleadk/base/main.py",
579
+ "python/agui/googleadk/base/model/__init__.py",
580
+ "python/agui/googleadk/base/model/load.py",
581
+ "python/agui/googleadk/base/pyproject.toml",
582
+ "python/agui/langchain_langgraph/base/README.md",
583
+ "python/agui/langchain_langgraph/base/gitignore.template",
584
+ "python/agui/langchain_langgraph/base/main.py",
585
+ "python/agui/langchain_langgraph/base/model/__init__.py",
586
+ "python/agui/langchain_langgraph/base/model/load.py",
587
+ "python/agui/langchain_langgraph/base/pyproject.toml",
588
+ "python/agui/strands/base/README.md",
589
+ "python/agui/strands/base/gitignore.template",
590
+ "python/agui/strands/base/main.py",
591
+ "python/agui/strands/base/model/__init__.py",
592
+ "python/agui/strands/base/model/load.py",
593
+ "python/agui/strands/base/pyproject.toml",
594
+ "python/agui/strands/capabilities/memory/__init__.py",
595
+ "python/agui/strands/capabilities/memory/session.py",
478
596
  "python/http/autogen/base/README.md",
479
597
  "python/http/autogen/base/gitignore.template",
480
598
  "python/http/autogen/base/main.py",
@@ -907,12 +1025,1033 @@ This agent implements the A2A protocol using Google's Agent Development Kit, ena
907
1025
 
908
1026
  ## Local Development
909
1027
 
910
- \`\`\`bash
911
- uv sync
912
- uv run python main.py
1028
+ \`\`\`bash
1029
+ uv sync
1030
+ uv run python main.py
1031
+ \`\`\`
1032
+
1033
+ The agent starts on port 9000.
1034
+
1035
+ ## Deploy
1036
+
1037
+ \`\`\`bash
1038
+ agentcore deploy
1039
+ \`\`\`
1040
+ "
1041
+ `;
1042
+
1043
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/gitignore.template should match snapshot 1`] = `
1044
+ "# Environment variables
1045
+ .env
1046
+
1047
+ # Python
1048
+ __pycache__/
1049
+ *.py[cod]
1050
+ *$py.class
1051
+ *.so
1052
+ .Python
1053
+ build/
1054
+ develop-eggs/
1055
+ dist/
1056
+ downloads/
1057
+ eggs/
1058
+ .eggs/
1059
+ lib/
1060
+ lib64/
1061
+ parts/
1062
+ sdist/
1063
+ var/
1064
+ wheels/
1065
+ *.egg-info/
1066
+ .installed.cfg
1067
+ *.egg
1068
+
1069
+ # Virtual environments
1070
+ .venv/
1071
+ venv/
1072
+ ENV/
1073
+ env/
1074
+
1075
+ # IDE
1076
+ .vscode/
1077
+ .idea/
1078
+ *.swp
1079
+ *.swo
1080
+ *~
1081
+
1082
+ # OS
1083
+ .DS_Store
1084
+ Thumbs.db
1085
+ "
1086
+ `;
1087
+
1088
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/main.py should match snapshot 1`] = `
1089
+ "import os
1090
+ from google.adk.agents import Agent
1091
+ from google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor
1092
+ from google.adk.runners import Runner
1093
+ from google.adk.sessions import InMemorySessionService
1094
+ from a2a.types import AgentCapabilities, AgentCard, AgentSkill
1095
+ from bedrock_agentcore.runtime import serve_a2a
1096
+ from model.load import load_model
1097
+
1098
+ load_model() # Sets GOOGLE_API_KEY env var (returns None)
1099
+
1100
+
1101
+ def add_numbers(a: int, b: int) -> int:
1102
+ """Return the sum of two numbers."""
1103
+ return a + b
1104
+
1105
+
1106
+ tools = [add_numbers]
1107
+
1108
+ {{#if sessionStorageMountPath}}
1109
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
1110
+
1111
+ def _safe_resolve(path: str) -> str:
1112
+ """Resolve path safely within the storage boundary."""
1113
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
1114
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
1115
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
1116
+ return resolved
1117
+
1118
+ def file_read(path: str) -> str:
1119
+ """Read a file from persistent storage. The path is relative to the storage root."""
1120
+ try:
1121
+ full_path = _safe_resolve(path)
1122
+ with open(full_path) as f:
1123
+ return f.read()
1124
+ except ValueError as e:
1125
+ return str(e)
1126
+ except OSError as e:
1127
+ return f"Error reading '{path}': {e.strerror}"
1128
+
1129
+ def file_write(path: str, content: str) -> str:
1130
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
1131
+ try:
1132
+ full_path = _safe_resolve(path)
1133
+ parent = os.path.dirname(full_path)
1134
+ if parent:
1135
+ os.makedirs(parent, exist_ok=True)
1136
+ with open(full_path, "w") as f:
1137
+ f.write(content)
1138
+ return f"Written to {path}"
1139
+ except ValueError as e:
1140
+ return str(e)
1141
+ except OSError as e:
1142
+ return f"Error writing '{path}': {e.strerror}"
1143
+
1144
+ def list_files(directory: str = "") -> str:
1145
+ """List files in persistent storage. The directory is relative to the storage root."""
1146
+ try:
1147
+ target = _safe_resolve(directory)
1148
+ entries = os.listdir(target)
1149
+ return "\\n".join(entries) if entries else "(empty directory)"
1150
+ except ValueError as e:
1151
+ return str(e)
1152
+ except OSError as e:
1153
+ return f"Error listing '{directory}': {e.strerror}"
1154
+
1155
+ tools.extend([file_read, file_write, list_files])
1156
+ {{/if}}
1157
+
1158
+ AGENT_INSTRUCTION = """
1159
+ You are a helpful assistant. Use tools when appropriate.
1160
+ {{#if sessionStorageMountPath}}
1161
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
1162
+ {{/if}}
1163
+ """
1164
+
1165
+ agent = Agent(
1166
+ model="gemini-2.5-flash",
1167
+ name="{{ name }}",
1168
+ description="A helpful assistant that can use tools.",
1169
+ instruction=AGENT_INSTRUCTION,
1170
+ tools=tools,
1171
+ )
1172
+
1173
+ runner = Runner(
1174
+ app_name=agent.name,
1175
+ agent=agent,
1176
+ session_service=InMemorySessionService(),
1177
+ )
1178
+
1179
+ card = AgentCard(
1180
+ name=agent.name,
1181
+ description=agent.description,
1182
+ url="http://localhost:9000/",
1183
+ version="0.1.0",
1184
+ capabilities=AgentCapabilities(streaming=True),
1185
+ skills=[
1186
+ AgentSkill(
1187
+ id="tools",
1188
+ name="tools",
1189
+ description="Use tools to help answer questions",
1190
+ tags=["tools"],
1191
+ )
1192
+ ],
1193
+ default_input_modes=["text"],
1194
+ default_output_modes=["text"],
1195
+ )
1196
+
1197
+ if __name__ == "__main__":
1198
+ serve_a2a(A2aAgentExecutor(runner=runner), card)
1199
+ "
1200
+ `;
1201
+
1202
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/model/__init__.py should match snapshot 1`] = `
1203
+ "# Package marker
1204
+ "
1205
+ `;
1206
+
1207
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/model/load.py should match snapshot 1`] = `
1208
+ "import os
1209
+ from bedrock_agentcore.identity.auth import requires_api_key
1210
+
1211
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1212
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1213
+
1214
+
1215
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1216
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1217
+ """Fetch API key from AgentCore Identity."""
1218
+ return api_key
1219
+
1220
+
1221
+ def _get_api_key() -> str:
1222
+ """
1223
+ Uses AgentCore Identity for API key management in deployed environments.
1224
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1225
+ """
1226
+ if os.getenv("LOCAL_DEV") == "1":
1227
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1228
+ if not api_key:
1229
+ raise RuntimeError(
1230
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1231
+ )
1232
+ return api_key
1233
+ return _agentcore_identity_api_key_provider()
1234
+
1235
+
1236
+ def load_model() -> None:
1237
+ """
1238
+ Set up Gemini API key authentication.
1239
+ Uses AgentCore Identity for API key management in deployed environments,
1240
+ and falls back to .env file for local development.
1241
+ Sets the GOOGLE_API_KEY environment variable for the Google ADK.
1242
+ """
1243
+ api_key = _get_api_key()
1244
+ # Use Google AI Studios API Key Authentication.
1245
+ # https://google.github.io/adk-docs/agents/models/#google-ai-studio
1246
+ os.environ["GOOGLE_API_KEY"] = api_key
1247
+ # Set to TRUE is using Google Vertex AI, Set to FALSE for Google AI Studio
1248
+ os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "FALSE"
1249
+ "
1250
+ `;
1251
+
1252
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/pyproject.toml should match snapshot 1`] = `
1253
+ "[build-system]
1254
+ requires = ["hatchling"]
1255
+ build-backend = "hatchling.build"
1256
+
1257
+ [project]
1258
+ name = "{{ name }}"
1259
+ version = "0.1.0"
1260
+ description = "AgentCore A2A Agent using Google ADK"
1261
+ readme = "README.md"
1262
+ requires-python = ">=3.10"
1263
+ dependencies = [
1264
+ "a2a-sdk >= 0.2.0",
1265
+ "aws-opentelemetry-distro",
1266
+ "bedrock-agentcore[a2a] >= 1.0.3",
1267
+ "google-adk >= 1.0.0",
1268
+ "google-genai >= 1.0.0",
1269
+ ]
1270
+
1271
+ [tool.hatch.build.targets.wheel]
1272
+ packages = ["."]
1273
+ "
1274
+ `;
1275
+
1276
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/README.md should match snapshot 1`] = `
1277
+ "# {{ name }}
1278
+
1279
+ An A2A (Agent-to-Agent) agent deployed on Amazon Bedrock AgentCore using LangChain + LangGraph.
1280
+
1281
+ ## Overview
1282
+
1283
+ This agent implements the A2A protocol using LangGraph, enabling agent-to-agent communication.
1284
+
1285
+ ## Local Development
1286
+
1287
+ \`\`\`bash
1288
+ uv sync
1289
+ uv run python main.py
1290
+ \`\`\`
1291
+
1292
+ The agent starts on port 9000.
1293
+
1294
+ ## Deploy
1295
+
1296
+ \`\`\`bash
1297
+ agentcore deploy
1298
+ \`\`\`
1299
+ "
1300
+ `;
1301
+
1302
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/gitignore.template should match snapshot 1`] = `
1303
+ "# Environment variables
1304
+ .env
1305
+
1306
+ # Python
1307
+ __pycache__/
1308
+ *.py[cod]
1309
+ *$py.class
1310
+ *.so
1311
+ .Python
1312
+ build/
1313
+ develop-eggs/
1314
+ dist/
1315
+ downloads/
1316
+ eggs/
1317
+ .eggs/
1318
+ lib/
1319
+ lib64/
1320
+ parts/
1321
+ sdist/
1322
+ var/
1323
+ wheels/
1324
+ *.egg-info/
1325
+ .installed.cfg
1326
+ *.egg
1327
+
1328
+ # Virtual environments
1329
+ .venv/
1330
+ venv/
1331
+ ENV/
1332
+ env/
1333
+
1334
+ # IDE
1335
+ .vscode/
1336
+ .idea/
1337
+ *.swp
1338
+ *.swo
1339
+ *~
1340
+
1341
+ # OS
1342
+ .DS_Store
1343
+ Thumbs.db
1344
+ "
1345
+ `;
1346
+
1347
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/main.py should match snapshot 1`] = `
1348
+ "import os
1349
+ from langchain_core.tools import tool
1350
+ from langgraph.prebuilt import create_react_agent
1351
+ from opentelemetry.instrumentation.langchain import LangchainInstrumentor
1352
+ from a2a.server.agent_execution import AgentExecutor, RequestContext
1353
+ from a2a.server.events import EventQueue
1354
+ from a2a.server.tasks import TaskUpdater
1355
+ from a2a.types import AgentCapabilities, AgentCard, AgentSkill, Part, TextPart
1356
+ from a2a.utils import new_task
1357
+ from bedrock_agentcore.runtime import serve_a2a
1358
+ from model.load import load_model
1359
+
1360
+ LangchainInstrumentor().instrument()
1361
+
1362
+
1363
+ @tool
1364
+ def add_numbers(a: int, b: int) -> int:
1365
+ """Return the sum of two numbers."""
1366
+ return a + b
1367
+
1368
+
1369
+ tools = [add_numbers]
1370
+
1371
+ {{#if sessionStorageMountPath}}
1372
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
1373
+
1374
+ def _safe_resolve(path: str) -> str:
1375
+ """Resolve path safely within the storage boundary."""
1376
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
1377
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
1378
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
1379
+ return resolved
1380
+
1381
+ @tool
1382
+ def file_read(path: str) -> str:
1383
+ """Read a file from persistent storage. The path is relative to the storage root."""
1384
+ try:
1385
+ full_path = _safe_resolve(path)
1386
+ with open(full_path) as f:
1387
+ return f.read()
1388
+ except ValueError as e:
1389
+ return str(e)
1390
+ except OSError as e:
1391
+ return f"Error reading '{path}': {e.strerror}"
1392
+
1393
+ @tool
1394
+ def file_write(path: str, content: str) -> str:
1395
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
1396
+ try:
1397
+ full_path = _safe_resolve(path)
1398
+ parent = os.path.dirname(full_path)
1399
+ if parent:
1400
+ os.makedirs(parent, exist_ok=True)
1401
+ with open(full_path, "w") as f:
1402
+ f.write(content)
1403
+ return f"Written to {path}"
1404
+ except ValueError as e:
1405
+ return str(e)
1406
+ except OSError as e:
1407
+ return f"Error writing '{path}': {e.strerror}"
1408
+
1409
+ @tool
1410
+ def list_files(directory: str = "") -> str:
1411
+ """List files in persistent storage. The directory is relative to the storage root."""
1412
+ try:
1413
+ target = _safe_resolve(directory)
1414
+ entries = os.listdir(target)
1415
+ return "\\n".join(entries) if entries else "(empty directory)"
1416
+ except ValueError as e:
1417
+ return str(e)
1418
+ except OSError as e:
1419
+ return f"Error listing '{directory}': {e.strerror}"
1420
+
1421
+ tools.extend([file_read, file_write, list_files])
1422
+ {{/if}}
1423
+
1424
+ SYSTEM_PROMPT = """
1425
+ You are a helpful assistant. Use tools when appropriate.
1426
+ {{#if sessionStorageMountPath}}
1427
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
1428
+ {{/if}}
1429
+ """
1430
+
1431
+ model = load_model()
1432
+ graph = create_react_agent(model, tools=tools, prompt=SYSTEM_PROMPT)
1433
+
1434
+
1435
+ class LangGraphA2AExecutor(AgentExecutor):
1436
+ """Wraps a LangGraph CompiledGraph as an a2a-sdk AgentExecutor."""
1437
+
1438
+ def __init__(self, graph):
1439
+ self.graph = graph
1440
+
1441
+ async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
1442
+ task = context.current_task or new_task(context.message)
1443
+ if not context.current_task:
1444
+ await event_queue.enqueue_event(task)
1445
+ updater = TaskUpdater(event_queue, task.id, task.context_id)
1446
+
1447
+ user_text = context.get_user_input()
1448
+ result = await self.graph.ainvoke({"messages": [("user", user_text)]})
1449
+ response = result["messages"][-1].content
1450
+
1451
+ await updater.add_artifact([Part(root=TextPart(text=response))])
1452
+ await updater.complete()
1453
+
1454
+ async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
1455
+ pass
1456
+
1457
+
1458
+ card = AgentCard(
1459
+ name="{{ name }}",
1460
+ description="A LangGraph agent on Bedrock AgentCore",
1461
+ url="http://localhost:9000/",
1462
+ version="0.1.0",
1463
+ capabilities=AgentCapabilities(streaming=True),
1464
+ skills=[
1465
+ AgentSkill(
1466
+ id="tools",
1467
+ name="tools",
1468
+ description="Use tools to help answer questions",
1469
+ tags=["tools"],
1470
+ )
1471
+ ],
1472
+ default_input_modes=["text"],
1473
+ default_output_modes=["text"],
1474
+ )
1475
+
1476
+ if __name__ == "__main__":
1477
+ serve_a2a(LangGraphA2AExecutor(graph), card)
1478
+ "
1479
+ `;
1480
+
1481
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/model/__init__.py should match snapshot 1`] = `
1482
+ "# Package marker
1483
+ "
1484
+ `;
1485
+
1486
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/model/load.py should match snapshot 1`] = `
1487
+ "{{#if (eq modelProvider "Bedrock")}}
1488
+ from langchain_aws import ChatBedrock
1489
+
1490
+ # Uses global inference profile for Claude Sonnet 4.5
1491
+ # https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html
1492
+ MODEL_ID = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
1493
+
1494
+
1495
+ def load_model() -> ChatBedrock:
1496
+ """Get Bedrock model client using IAM credentials."""
1497
+ return ChatBedrock(model_id=MODEL_ID)
1498
+ {{/if}}
1499
+ {{#if (eq modelProvider "Anthropic")}}
1500
+ import os
1501
+ from langchain_anthropic import ChatAnthropic
1502
+ from bedrock_agentcore.identity.auth import requires_api_key
1503
+
1504
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1505
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1506
+
1507
+
1508
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1509
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1510
+ """Fetch API key from AgentCore Identity."""
1511
+ return api_key
1512
+
1513
+
1514
+ def _get_api_key() -> str:
1515
+ """
1516
+ Uses AgentCore Identity for API key management in deployed environments.
1517
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1518
+ """
1519
+ if os.getenv("LOCAL_DEV") == "1":
1520
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1521
+ if not api_key:
1522
+ raise RuntimeError(
1523
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1524
+ )
1525
+ return api_key
1526
+ return _agentcore_identity_api_key_provider()
1527
+
1528
+
1529
+ def load_model() -> ChatAnthropic:
1530
+ """Get authenticated Anthropic model client."""
1531
+ return ChatAnthropic(
1532
+ model="claude-sonnet-4-5-20250929",
1533
+ api_key=_get_api_key()
1534
+ )
1535
+ {{/if}}
1536
+ {{#if (eq modelProvider "OpenAI")}}
1537
+ import os
1538
+ from langchain_openai import ChatOpenAI
1539
+ from bedrock_agentcore.identity.auth import requires_api_key
1540
+
1541
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1542
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1543
+
1544
+
1545
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1546
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1547
+ """Fetch API key from AgentCore Identity."""
1548
+ return api_key
1549
+
1550
+
1551
+ def _get_api_key() -> str:
1552
+ """
1553
+ Uses AgentCore Identity for API key management in deployed environments.
1554
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1555
+ """
1556
+ if os.getenv("LOCAL_DEV") == "1":
1557
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1558
+ if not api_key:
1559
+ raise RuntimeError(
1560
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1561
+ )
1562
+ return api_key
1563
+ return _agentcore_identity_api_key_provider()
1564
+
1565
+
1566
+ def load_model() -> ChatOpenAI:
1567
+ """Get authenticated OpenAI model client."""
1568
+ return ChatOpenAI(
1569
+ model="gpt-4.1",
1570
+ api_key=_get_api_key()
1571
+ )
1572
+ {{/if}}
1573
+ {{#if (eq modelProvider "Gemini")}}
1574
+ import os
1575
+ from langchain_google_genai import ChatGoogleGenerativeAI
1576
+ from bedrock_agentcore.identity.auth import requires_api_key
1577
+
1578
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1579
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1580
+
1581
+
1582
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1583
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1584
+ """Fetch API key from AgentCore Identity."""
1585
+ return api_key
1586
+
1587
+
1588
+ def _get_api_key() -> str:
1589
+ """
1590
+ Uses AgentCore Identity for API key management in deployed environments.
1591
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1592
+ """
1593
+ if os.getenv("LOCAL_DEV") == "1":
1594
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1595
+ if not api_key:
1596
+ raise RuntimeError(
1597
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1598
+ )
1599
+ return api_key
1600
+ return _agentcore_identity_api_key_provider()
1601
+
1602
+
1603
+ def load_model() -> ChatGoogleGenerativeAI:
1604
+ """Get authenticated Gemini model client."""
1605
+ return ChatGoogleGenerativeAI(
1606
+ model="gemini-2.5-flash",
1607
+ api_key=_get_api_key()
1608
+ )
1609
+ {{/if}}
1610
+ "
1611
+ `;
1612
+
1613
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/pyproject.toml should match snapshot 1`] = `
1614
+ "[build-system]
1615
+ requires = ["hatchling"]
1616
+ build-backend = "hatchling.build"
1617
+
1618
+ [project]
1619
+ name = "{{ name }}"
1620
+ version = "0.1.0"
1621
+ description = "AgentCore A2A Agent using LangChain + LangGraph"
1622
+ readme = "README.md"
1623
+ requires-python = ">=3.10"
1624
+ dependencies = [
1625
+ "a2a-sdk >= 0.2.0",
1626
+ {{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
1627
+ {{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
1628
+ {{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
1629
+ {{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
1630
+ {{/if}}"aws-opentelemetry-distro",
1631
+ "opentelemetry-instrumentation-langchain >= 0.59.0",
1632
+ "bedrock-agentcore[a2a] >= 1.0.3",
1633
+ "botocore[crt] >= 1.35.0",
1634
+ "langgraph >= 0.2.0",
1635
+ ]
1636
+
1637
+ [tool.hatch.build.targets.wheel]
1638
+ packages = ["."]
1639
+ "
1640
+ `;
1641
+
1642
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/README.md should match snapshot 1`] = `
1643
+ "# {{ name }}
1644
+
1645
+ An A2A (Agent-to-Agent) agent deployed on Amazon Bedrock AgentCore using Strands SDK.
1646
+
1647
+ ## Overview
1648
+
1649
+ This agent implements the A2A protocol, enabling agent-to-agent communication. Other agents can discover and interact with this agent via the \`/.well-known/agent-card.json\` endpoint.
1650
+
1651
+ ## Local Development
1652
+
1653
+ \`\`\`bash
1654
+ uv sync
1655
+ uv run python main.py
1656
+ \`\`\`
1657
+
1658
+ The agent starts on port 9000.
1659
+
1660
+ ## Deploy
1661
+
1662
+ \`\`\`bash
1663
+ agentcore deploy
1664
+ \`\`\`
1665
+ "
1666
+ `;
1667
+
1668
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/gitignore.template should match snapshot 1`] = `
1669
+ "# Environment variables
1670
+ .env
1671
+
1672
+ # Python
1673
+ __pycache__/
1674
+ *.py[cod]
1675
+ *$py.class
1676
+ *.so
1677
+ .Python
1678
+ build/
1679
+ develop-eggs/
1680
+ dist/
1681
+ downloads/
1682
+ eggs/
1683
+ .eggs/
1684
+ lib/
1685
+ lib64/
1686
+ parts/
1687
+ sdist/
1688
+ var/
1689
+ wheels/
1690
+ *.egg-info/
1691
+ .installed.cfg
1692
+ *.egg
1693
+
1694
+ # Virtual environments
1695
+ .venv/
1696
+ venv/
1697
+ ENV/
1698
+ env/
1699
+
1700
+ # IDE
1701
+ .vscode/
1702
+ .idea/
1703
+ *.swp
1704
+ *.swo
1705
+ *~
1706
+
1707
+ # OS
1708
+ .DS_Store
1709
+ Thumbs.db
1710
+ "
1711
+ `;
1712
+
1713
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/main.py should match snapshot 1`] = `
1714
+ "from strands import Agent, tool
1715
+ from strands.multiagent.a2a.executor import StrandsA2AExecutor
1716
+ from bedrock_agentcore.runtime import serve_a2a
1717
+ from model.load import load_model
1718
+ {{#if hasMemory}}
1719
+ from memory.session import get_memory_session_manager
1720
+ {{/if}}
1721
+ {{#if sessionStorageMountPath}}
1722
+ import os
1723
+ {{/if}}
1724
+
1725
+
1726
+ @tool
1727
+ def add_numbers(a: int, b: int) -> int:
1728
+ """Return the sum of two numbers."""
1729
+ return a + b
1730
+
1731
+
1732
+ tools = [add_numbers]
1733
+
1734
+ {{#if sessionStorageMountPath}}
1735
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
1736
+
1737
+ def _safe_resolve(path: str) -> str:
1738
+ """Resolve path safely within the storage boundary."""
1739
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
1740
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
1741
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
1742
+ return resolved
1743
+
1744
+ @tool
1745
+ def file_read(path: str) -> str:
1746
+ """Read a file from persistent storage. The path is relative to the storage root."""
1747
+ try:
1748
+ full_path = _safe_resolve(path)
1749
+ with open(full_path) as f:
1750
+ return f.read()
1751
+ except ValueError as e:
1752
+ return str(e)
1753
+ except OSError as e:
1754
+ return f"Error reading '{path}': {e.strerror}"
1755
+
1756
+ @tool
1757
+ def file_write(path: str, content: str) -> str:
1758
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
1759
+ try:
1760
+ full_path = _safe_resolve(path)
1761
+ parent = os.path.dirname(full_path)
1762
+ if parent:
1763
+ os.makedirs(parent, exist_ok=True)
1764
+ with open(full_path, "w") as f:
1765
+ f.write(content)
1766
+ return f"Written to {path}"
1767
+ except ValueError as e:
1768
+ return str(e)
1769
+ except OSError as e:
1770
+ return f"Error writing '{path}': {e.strerror}"
1771
+
1772
+ @tool
1773
+ def list_files(directory: str = "") -> str:
1774
+ """List files in persistent storage. The directory is relative to the storage root."""
1775
+ try:
1776
+ target = _safe_resolve(directory)
1777
+ entries = os.listdir(target)
1778
+ return "\\n".join(entries) if entries else "(empty directory)"
1779
+ except ValueError as e:
1780
+ return str(e)
1781
+ except OSError as e:
1782
+ return f"Error listing '{directory}': {e.strerror}"
1783
+
1784
+ tools.extend([file_read, file_write, list_files])
1785
+ {{/if}}
1786
+
1787
+ SYSTEM_PROMPT = """
1788
+ You are a helpful assistant. Use tools when appropriate.
1789
+ {{#if sessionStorageMountPath}}
1790
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
1791
+ {{/if}}
1792
+ """
1793
+
1794
+ {{#if hasMemory}}
1795
+ def agent_factory():
1796
+ cache = {}
1797
+ def get_or_create_agent(session_id, user_id):
1798
+ key = f"{session_id}/{user_id}"
1799
+ if key not in cache:
1800
+ cache[key] = Agent(
1801
+ model=load_model(),
1802
+ session_manager=get_memory_session_manager(session_id, user_id),
1803
+ system_prompt=SYSTEM_PROMPT,
1804
+ tools=tools,
1805
+ )
1806
+ return cache[key]
1807
+ return get_or_create_agent
1808
+
1809
+ get_or_create_agent = agent_factory()
1810
+ agent = get_or_create_agent("default-session", "default-user")
1811
+ {{else}}
1812
+ agent = Agent(
1813
+ model=load_model(),
1814
+ system_prompt=SYSTEM_PROMPT,
1815
+ tools=tools,
1816
+ )
1817
+ {{/if}}
1818
+
1819
+ if __name__ == "__main__":
1820
+ serve_a2a(StrandsA2AExecutor(agent))
1821
+ "
1822
+ `;
1823
+
1824
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/model/__init__.py should match snapshot 1`] = `
1825
+ "# Package marker
1826
+ "
1827
+ `;
1828
+
1829
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/model/load.py should match snapshot 1`] = `
1830
+ "{{#if (eq modelProvider "Bedrock")}}
1831
+ from strands.models.bedrock import BedrockModel
1832
+
1833
+
1834
+ def load_model() -> BedrockModel:
1835
+ """Get Bedrock model client using IAM credentials."""
1836
+ return BedrockModel(model_id="global.anthropic.claude-sonnet-4-5-20250929-v1:0")
1837
+ {{/if}}
1838
+ {{#if (eq modelProvider "Anthropic")}}
1839
+ import os
1840
+
1841
+ from strands.models.anthropic import AnthropicModel
1842
+ from bedrock_agentcore.identity.auth import requires_api_key
1843
+
1844
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1845
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1846
+
1847
+
1848
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1849
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1850
+ """Fetch API key from AgentCore Identity."""
1851
+ return api_key
1852
+
1853
+
1854
+ def _get_api_key() -> str:
1855
+ """
1856
+ Uses AgentCore Identity for API key management in deployed environments.
1857
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1858
+ """
1859
+ if os.getenv("LOCAL_DEV") == "1":
1860
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1861
+ if not api_key:
1862
+ raise RuntimeError(
1863
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1864
+ )
1865
+ return api_key
1866
+ return _agentcore_identity_api_key_provider()
1867
+
1868
+
1869
+ def load_model() -> AnthropicModel:
1870
+ """Get authenticated Anthropic model client."""
1871
+ return AnthropicModel(
1872
+ client_args={"api_key": _get_api_key()},
1873
+ model_id="claude-sonnet-4-5-20250929",
1874
+ max_tokens=5000,
1875
+ )
1876
+ {{/if}}
1877
+ {{#if (eq modelProvider "OpenAI")}}
1878
+ import os
1879
+
1880
+ from strands.models.openai import OpenAIModel
1881
+ from bedrock_agentcore.identity.auth import requires_api_key
1882
+
1883
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1884
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1885
+
1886
+
1887
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1888
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1889
+ """Fetch API key from AgentCore Identity."""
1890
+ return api_key
1891
+
1892
+
1893
+ def _get_api_key() -> str:
1894
+ """
1895
+ Uses AgentCore Identity for API key management in deployed environments.
1896
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1897
+ """
1898
+ if os.getenv("LOCAL_DEV") == "1":
1899
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1900
+ if not api_key:
1901
+ raise RuntimeError(
1902
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1903
+ )
1904
+ return api_key
1905
+ return _agentcore_identity_api_key_provider()
1906
+
1907
+
1908
+ def load_model() -> OpenAIModel:
1909
+ """Get authenticated OpenAI model client."""
1910
+ return OpenAIModel(
1911
+ client_args={"api_key": _get_api_key()},
1912
+ model_id="gpt-4.1",
1913
+ )
1914
+ {{/if}}
1915
+ {{#if (eq modelProvider "Gemini")}}
1916
+ import os
1917
+
1918
+ from strands.models.gemini import GeminiModel
1919
+ from bedrock_agentcore.identity.auth import requires_api_key
1920
+
1921
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1922
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1923
+
1924
+
1925
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1926
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1927
+ """Fetch API key from AgentCore Identity."""
1928
+ return api_key
1929
+
1930
+
1931
+ def _get_api_key() -> str:
1932
+ """
1933
+ Uses AgentCore Identity for API key management in deployed environments.
1934
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1935
+ """
1936
+ if os.getenv("LOCAL_DEV") == "1":
1937
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1938
+ if not api_key:
1939
+ raise RuntimeError(
1940
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1941
+ )
1942
+ return api_key
1943
+ return _agentcore_identity_api_key_provider()
1944
+
1945
+
1946
+ def load_model() -> GeminiModel:
1947
+ """Get authenticated Gemini model client."""
1948
+ return GeminiModel(
1949
+ client_args={"api_key": _get_api_key()},
1950
+ model_id="gemini-2.5-flash",
1951
+ )
1952
+ {{/if}}
1953
+ "
1954
+ `;
1955
+
1956
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/pyproject.toml should match snapshot 1`] = `
1957
+ "[build-system]
1958
+ requires = ["hatchling"]
1959
+ build-backend = "hatchling.build"
1960
+
1961
+ [project]
1962
+ name = "{{ name }}"
1963
+ version = "0.1.0"
1964
+ description = "AgentCore A2A Agent using Strands SDK"
1965
+ readme = "README.md"
1966
+ requires-python = ">=3.10"
1967
+ dependencies = [
1968
+ {{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
1969
+ {{/if}}"a2a-sdk[all] >= 0.2.0",
1970
+ "aws-opentelemetry-distro",
1971
+ "bedrock-agentcore[a2a] >= 1.0.3",
1972
+ "botocore[crt] >= 1.35.0",
1973
+ {{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
1974
+ {{/if}}{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
1975
+ {{/if}}"strands-agents >= 1.13.0",
1976
+ ]
1977
+
1978
+ [tool.hatch.build.targets.wheel]
1979
+ packages = ["."]
1980
+ "
1981
+ `;
1982
+
1983
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/__init__.py should match snapshot 1`] = `
1984
+ "# Package marker
1985
+ "
1986
+ `;
1987
+
1988
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/session.py should match snapshot 1`] = `
1989
+ "import os
1990
+ from typing import Optional
1991
+
1992
+ from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig{{#if memoryProviders.[0].strategies.length}}, RetrievalConfig{{/if}}
1993
+ from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
1994
+
1995
+ MEMORY_ID = os.getenv("{{memoryProviders.[0].envVarName}}")
1996
+ REGION = os.getenv("AWS_REGION")
1997
+
1998
+ def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
1999
+ if not MEMORY_ID:
2000
+ return None
2001
+
2002
+ {{#if memoryProviders.[0].strategies.length}}
2003
+ retrieval_config = {
2004
+ {{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}
2005
+ f"/users/{actor_id}/facts": RetrievalConfig(top_k=3, relevance_score=0.5),
2006
+ {{/if}}
2007
+ {{#if (includes memoryProviders.[0].strategies "USER_PREFERENCE")}}
2008
+ f"/users/{actor_id}/preferences": RetrievalConfig(top_k=3, relevance_score=0.5),
2009
+ {{/if}}
2010
+ {{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
2011
+ f"/summaries/{actor_id}/{session_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
2012
+ {{/if}}
2013
+ }
2014
+ {{/if}}
2015
+
2016
+ return AgentCoreMemorySessionManager(
2017
+ AgentCoreMemoryConfig(
2018
+ memory_id=MEMORY_ID,
2019
+ session_id=session_id,
2020
+ actor_id=actor_id,
2021
+ {{#if memoryProviders.[0].strategies.length}}
2022
+ retrieval_config=retrieval_config,
2023
+ {{/if}}
2024
+ ),
2025
+ REGION
2026
+ )
2027
+ "
2028
+ `;
2029
+
2030
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/README.md should match snapshot 1`] = `
2031
+ "# {{ name }}
2032
+
2033
+ An AG-UI agent deployed on Amazon Bedrock AgentCore using Google ADK.
2034
+
2035
+ ## Overview
2036
+
2037
+ This agent implements the AG-UI protocol using Google's Agent Development Kit, enabling rich agent-user interaction via the AG-UI event stream.
2038
+
2039
+ ## Local Development
2040
+
2041
+ \`\`\`bash
2042
+ uv sync
2043
+ uv run python main.py
2044
+ \`\`\`
2045
+
2046
+ The agent starts on port 8080 and serves requests at \`/invocations\`.
2047
+
2048
+ ## Health Check
2049
+
2050
+ \`\`\`
2051
+ GET /ping
913
2052
  \`\`\`
914
2053
 
915
- The agent starts on port 9000.
2054
+ Returns \`{"status": "healthy"}\`.
916
2055
 
917
2056
  ## Deploy
918
2057
 
@@ -922,7 +2061,7 @@ agentcore deploy
922
2061
  "
923
2062
  `;
924
2063
 
925
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/gitignore.template should match snapshot 1`] = `
2064
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/gitignore.template should match snapshot 1`] = `
926
2065
  "# Environment variables
927
2066
  .env
928
2067
 
@@ -967,64 +2106,47 @@ Thumbs.db
967
2106
  "
968
2107
  `;
969
2108
 
970
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/main.py should match snapshot 1`] = `
971
- "from google.adk.agents import Agent
972
- from google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor
973
- from google.adk.runners import Runner
974
- from google.adk.sessions import InMemorySessionService
975
- from a2a.types import AgentCapabilities, AgentCard, AgentSkill
976
- from bedrock_agentcore.runtime import serve_a2a
2109
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/main.py should match snapshot 1`] = `
2110
+ "import os
2111
+ import uvicorn
2112
+ from google.adk.agents import LlmAgent
2113
+ from ag_ui_adk import ADKAgent, AGUIToolset, create_adk_app
977
2114
  from model.load import load_model
978
2115
 
2116
+ load_model()
979
2117
 
980
- def add_numbers(a: int, b: int) -> int:
981
- """Return the sum of two numbers."""
982
- return a + b
983
-
984
-
985
- agent = Agent(
986
- model=load_model(),
2118
+ agent = LlmAgent(
987
2119
  name="{{ name }}",
988
- description="A helpful assistant that can use tools.",
989
- instruction="You are a helpful assistant. Use tools when appropriate.",
990
- tools=[add_numbers],
2120
+ model="gemini-2.5-flash",
2121
+ instruction="You are a helpful assistant.",
2122
+ tools=[AGUIToolset()],
991
2123
  )
992
2124
 
993
- runner = Runner(
994
- app_name=agent.name,
995
- agent=agent,
996
- session_service=InMemorySessionService(),
2125
+ adk_agent = ADKAgent(
2126
+ adk_agent=agent,
2127
+ app_name="{{ name }}",
2128
+ use_in_memory_services=True,
997
2129
  )
998
2130
 
999
- card = AgentCard(
1000
- name=agent.name,
1001
- description=agent.description,
1002
- url="http://localhost:9000/",
1003
- version="0.1.0",
1004
- capabilities=AgentCapabilities(streaming=True),
1005
- skills=[
1006
- AgentSkill(
1007
- id="tools",
1008
- name="tools",
1009
- description="Use tools to help answer questions",
1010
- tags=["tools"],
1011
- )
1012
- ],
1013
- default_input_modes=["text"],
1014
- default_output_modes=["text"],
1015
- )
2131
+ app = create_adk_app(adk_agent, path="/invocations")
2132
+
2133
+
2134
+ @app.get("/ping")
2135
+ async def ping():
2136
+ return {"status": "healthy"}
2137
+
1016
2138
 
1017
2139
  if __name__ == "__main__":
1018
- serve_a2a(A2aAgentExecutor(runner=runner), card)
2140
+ uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))
1019
2141
  "
1020
2142
  `;
1021
2143
 
1022
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/model/__init__.py should match snapshot 1`] = `
2144
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/model/__init__.py should match snapshot 1`] = `
1023
2145
  "# Package marker
1024
2146
  "
1025
2147
  `;
1026
2148
 
1027
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/model/load.py should match snapshot 1`] = `
2149
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/model/load.py should match snapshot 1`] = `
1028
2150
  "import os
1029
2151
  from bedrock_agentcore.identity.auth import requires_api_key
1030
2152
 
@@ -1069,7 +2191,7 @@ def load_model() -> None:
1069
2191
  "
1070
2192
  `;
1071
2193
 
1072
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/pyproject.toml should match snapshot 1`] = `
2194
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/pyproject.toml should match snapshot 1`] = `
1073
2195
  "[build-system]
1074
2196
  requires = ["hatchling"]
1075
2197
  build-backend = "hatchling.build"
@@ -1077,15 +2199,19 @@ build-backend = "hatchling.build"
1077
2199
  [project]
1078
2200
  name = "{{ name }}"
1079
2201
  version = "0.1.0"
1080
- description = "AgentCore A2A Agent using Google ADK"
2202
+ description = "AgentCore AG-UI Agent using Google ADK"
1081
2203
  readme = "README.md"
1082
2204
  requires-python = ">=3.10"
1083
2205
  dependencies = [
1084
- "a2a-sdk >= 0.2.0",
1085
- "aws-opentelemetry-distro",
1086
- "bedrock-agentcore[a2a] >= 1.0.3",
1087
- "google-adk >= 1.0.0",
2206
+ "ag-ui-adk >= 0.6.0",
2207
+ "ag-ui-protocol >= 0.1.10",
2208
+ "bedrock-agentcore >= 1.0.3",
2209
+ "fastapi >= 0.115.12",
2210
+ "google-adk >= 1.16.0",
1088
2211
  "google-genai >= 1.0.0",
2212
+ "opentelemetry-distro",
2213
+ "opentelemetry-exporter-otlp",
2214
+ "uvicorn >= 0.34.3",
1089
2215
  ]
1090
2216
 
1091
2217
  [tool.hatch.build.targets.wheel]
@@ -1093,14 +2219,14 @@ packages = ["."]
1093
2219
  "
1094
2220
  `;
1095
2221
 
1096
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/README.md should match snapshot 1`] = `
2222
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/README.md should match snapshot 1`] = `
1097
2223
  "# {{ name }}
1098
2224
 
1099
- An A2A (Agent-to-Agent) agent deployed on Amazon Bedrock AgentCore using LangChain + LangGraph.
2225
+ An AG-UI agent deployed on Amazon Bedrock AgentCore using LangChain + LangGraph.
1100
2226
 
1101
2227
  ## Overview
1102
2228
 
1103
- This agent implements the A2A protocol using LangGraph, enabling agent-to-agent communication.
2229
+ This agent implements the AG-UI protocol using LangGraph, enabling seamless frontend-to-agent communication with support for streaming, tool calls, and frontend-injected tools.
1104
2230
 
1105
2231
  ## Local Development
1106
2232
 
@@ -1109,7 +2235,7 @@ uv sync
1109
2235
  uv run python main.py
1110
2236
  \`\`\`
1111
2237
 
1112
- The agent starts on port 9000.
2238
+ The agent starts on port 8080.
1113
2239
 
1114
2240
  ## Deploy
1115
2241
 
@@ -1119,7 +2245,7 @@ agentcore deploy
1119
2245
  "
1120
2246
  `;
1121
2247
 
1122
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/gitignore.template should match snapshot 1`] = `
2248
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/gitignore.template should match snapshot 1`] = `
1123
2249
  "# Environment variables
1124
2250
  .env
1125
2251
 
@@ -1164,16 +2290,22 @@ Thumbs.db
1164
2290
  "
1165
2291
  `;
1166
2292
 
1167
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/main.py should match snapshot 1`] = `
1168
- "from langchain_core.tools import tool
1169
- from langgraph.prebuilt import create_react_agent
2293
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/main.py should match snapshot 1`] = `
2294
+ "import os
2295
+
2296
+ os.environ["LANGGRAPH_FAST_API"] = "true"
2297
+
2298
+ import uvicorn
2299
+ from typing import Any, List
2300
+ from fastapi import FastAPI
2301
+ from fastapi.middleware.cors import CORSMiddleware
2302
+ from langgraph.graph import StateGraph, START
2303
+ from langgraph.graph.message import MessagesState
2304
+ from langgraph.checkpoint.memory import MemorySaver
2305
+ from langgraph.prebuilt import ToolNode, tools_condition
2306
+ from langchain_core.tools import tool
1170
2307
  from opentelemetry.instrumentation.langchain import LangchainInstrumentor
1171
- from a2a.server.agent_execution import AgentExecutor, RequestContext
1172
- from a2a.server.events import EventQueue
1173
- from a2a.server.tasks import TaskUpdater
1174
- from a2a.types import AgentCapabilities, AgentCard, AgentSkill, Part, TextPart
1175
- from a2a.utils import new_task
1176
- from bedrock_agentcore.runtime import serve_a2a
2308
+ from ag_ui_langgraph import LangGraphAgent, add_langgraph_fastapi_endpoint
1177
2309
  from model.load import load_model
1178
2310
 
1179
2311
  LangchainInstrumentor().instrument()
@@ -1185,62 +2317,63 @@ def add_numbers(a: int, b: int) -> int:
1185
2317
  return a + b
1186
2318
 
1187
2319
 
2320
+ backend_tools = [add_numbers]
1188
2321
  model = load_model()
1189
- graph = create_react_agent(model, tools=[add_numbers])
1190
2322
 
1191
2323
 
1192
- class LangGraphA2AExecutor(AgentExecutor):
1193
- """Wraps a LangGraph CompiledGraph as an a2a-sdk AgentExecutor."""
1194
-
1195
- def __init__(self, graph):
1196
- self.graph = graph
1197
-
1198
- async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
1199
- task = context.current_task or new_task(context.message)
1200
- if not context.current_task:
1201
- await event_queue.enqueue_event(task)
1202
- updater = TaskUpdater(event_queue, task.id, task.context_id)
2324
+ class AgentState(MessagesState):
2325
+ tools: List[Any]
1203
2326
 
1204
- user_text = context.get_user_input()
1205
- result = await self.graph.ainvoke({"messages": [("user", user_text)]})
1206
- response = result["messages"][-1].content
1207
2327
 
1208
- await updater.add_artifact([Part(root=TextPart(text=response))])
1209
- await updater.complete()
2328
+ def chat_node(state: AgentState):
2329
+ bound_model = model.bind_tools(
2330
+ [*state.get("tools", []), *backend_tools],
2331
+ )
2332
+ response = bound_model.invoke(state["messages"])
2333
+ return {"messages": [response]}
1210
2334
 
1211
- async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
1212
- pass
1213
2335
 
2336
+ builder = StateGraph(AgentState)
2337
+ builder.add_node("chat", chat_node)
2338
+ builder.add_node("tools", ToolNode(tools=backend_tools))
2339
+ builder.add_edge(START, "chat")
2340
+ builder.add_conditional_edges("chat", tools_condition)
2341
+ builder.add_edge("tools", "chat")
2342
+ graph = builder.compile(checkpointer=MemorySaver())
1214
2343
 
1215
- card = AgentCard(
2344
+ agent = LangGraphAgent(
1216
2345
  name="{{ name }}",
1217
- description="A LangGraph agent on Bedrock AgentCore",
1218
- url="http://localhost:9000/",
1219
- version="0.1.0",
1220
- capabilities=AgentCapabilities(streaming=True),
1221
- skills=[
1222
- AgentSkill(
1223
- id="tools",
1224
- name="tools",
1225
- description="Use tools to help answer questions",
1226
- tags=["tools"],
1227
- )
1228
- ],
1229
- default_input_modes=["text"],
1230
- default_output_modes=["text"],
2346
+ graph=graph,
2347
+ description="A helpful assistant",
2348
+ )
2349
+
2350
+ app = FastAPI()
2351
+ app.add_middleware(
2352
+ CORSMiddleware,
2353
+ allow_origins=["*"],
2354
+ allow_methods=["*"],
2355
+ allow_headers=["*"],
1231
2356
  )
1232
2357
 
2358
+ add_langgraph_fastapi_endpoint(app=app, agent=agent, path="/invocations")
2359
+
2360
+
2361
+ @app.get("/ping")
2362
+ async def ping():
2363
+ return {"status": "healthy"}
2364
+
2365
+
1233
2366
  if __name__ == "__main__":
1234
- serve_a2a(LangGraphA2AExecutor(graph), card)
2367
+ uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))
1235
2368
  "
1236
2369
  `;
1237
2370
 
1238
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/model/__init__.py should match snapshot 1`] = `
2371
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/model/__init__.py should match snapshot 1`] = `
1239
2372
  "# Package marker
1240
2373
  "
1241
2374
  `;
1242
2375
 
1243
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/model/load.py should match snapshot 1`] = `
2376
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/model/load.py should match snapshot 1`] = `
1244
2377
  "{{#if (eq modelProvider "Bedrock")}}
1245
2378
  from langchain_aws import ChatBedrock
1246
2379
 
@@ -1367,7 +2500,7 @@ def load_model() -> ChatGoogleGenerativeAI:
1367
2500
  "
1368
2501
  `;
1369
2502
 
1370
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/pyproject.toml should match snapshot 1`] = `
2503
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/pyproject.toml should match snapshot 1`] = `
1371
2504
  "[build-system]
1372
2505
  requires = ["hatchling"]
1373
2506
  build-backend = "hatchling.build"
@@ -1375,20 +2508,25 @@ build-backend = "hatchling.build"
1375
2508
  [project]
1376
2509
  name = "{{ name }}"
1377
2510
  version = "0.1.0"
1378
- description = "AgentCore A2A Agent using LangChain + LangGraph"
2511
+ description = "AgentCore AG-UI Agent using LangChain + LangGraph"
1379
2512
  readme = "README.md"
1380
2513
  requires-python = ">=3.10"
1381
2514
  dependencies = [
1382
- "a2a-sdk >= 0.2.0",
2515
+ "ag-ui-langgraph >= 0.0.31",
2516
+ "ag-ui-protocol >= 0.1.10",
1383
2517
  {{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
1384
2518
  {{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
1385
2519
  {{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
1386
2520
  {{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
1387
2521
  {{/if}}"aws-opentelemetry-distro",
1388
2522
  "opentelemetry-instrumentation-langchain >= 0.59.0",
1389
- "bedrock-agentcore[a2a] >= 1.0.3",
2523
+ "bedrock-agentcore >= 1.0.3",
1390
2524
  "botocore[crt] >= 1.35.0",
1391
- "langgraph >= 0.2.0",
2525
+ "langgraph >= 0.3.25",
2526
+ "langchain >= 0.3.0",
2527
+ "langchain-core >= 0.3.0",
2528
+ "fastapi >= 0.115.12",
2529
+ "uvicorn >= 0.34.3",
1392
2530
  ]
1393
2531
 
1394
2532
  [tool.hatch.build.targets.wheel]
@@ -1396,14 +2534,14 @@ packages = ["."]
1396
2534
  "
1397
2535
  `;
1398
2536
 
1399
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/README.md should match snapshot 1`] = `
2537
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/README.md should match snapshot 1`] = `
1400
2538
  "# {{ name }}
1401
2539
 
1402
- An A2A (Agent-to-Agent) agent deployed on Amazon Bedrock AgentCore using Strands SDK.
2540
+ An AG-UI agent deployed on Amazon Bedrock AgentCore using Strands SDK.
1403
2541
 
1404
2542
  ## Overview
1405
2543
 
1406
- This agent implements the A2A protocol, enabling agent-to-agent communication. Other agents can discover and interact with this agent via the \`/.well-known/agent-card.json\` endpoint.
2544
+ This agent implements the AG-UI protocol, enabling streaming agent-to-UI communication. The agent exposes an \`/invocations\` endpoint that accepts AG-UI protocol requests and streams responses back to the client.
1407
2545
 
1408
2546
  ## Local Development
1409
2547
 
@@ -1412,7 +2550,7 @@ uv sync
1412
2550
  uv run python main.py
1413
2551
  \`\`\`
1414
2552
 
1415
- The agent starts on port 9000.
2553
+ The agent starts on port 8080.
1416
2554
 
1417
2555
  ## Deploy
1418
2556
 
@@ -1422,7 +2560,7 @@ agentcore deploy
1422
2560
  "
1423
2561
  `;
1424
2562
 
1425
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/gitignore.template should match snapshot 1`] = `
2563
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/gitignore.template should match snapshot 1`] = `
1426
2564
  "# Environment variables
1427
2565
  .env
1428
2566
 
@@ -1467,10 +2605,16 @@ Thumbs.db
1467
2605
  "
1468
2606
  `;
1469
2607
 
1470
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/main.py should match snapshot 1`] = `
1471
- "from strands import Agent, tool
1472
- from strands.multiagent.a2a.executor import StrandsA2AExecutor
1473
- from bedrock_agentcore.runtime import serve_a2a
2608
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/main.py should match snapshot 1`] = `
2609
+ "import os
2610
+
2611
+ # Suppress OpenTelemetry warnings during local development; remove for production
2612
+ if os.getenv("LOCAL_DEV") == "1":
2613
+ os.environ["OTEL_SDK_DISABLED"] = "true"
2614
+
2615
+ import uvicorn
2616
+ from strands import Agent, tool
2617
+ from ag_ui_strands import StrandsAgent, StrandsAgentConfig, create_strands_app
1474
2618
  from model.load import load_model
1475
2619
  {{#if hasMemory}}
1476
2620
  from memory.session import get_memory_session_manager
@@ -1485,42 +2629,35 @@ def add_numbers(a: int, b: int) -> int:
1485
2629
 
1486
2630
  tools = [add_numbers]
1487
2631
 
1488
- {{#if hasMemory}}
1489
- def agent_factory():
1490
- cache = {}
1491
- def get_or_create_agent(session_id, user_id):
1492
- key = f"{session_id}/{user_id}"
1493
- if key not in cache:
1494
- cache[key] = Agent(
1495
- model=load_model(),
1496
- session_manager=get_memory_session_manager(session_id, user_id),
1497
- system_prompt="You are a helpful assistant. Use tools when appropriate.",
1498
- tools=tools,
1499
- )
1500
- return cache[key]
1501
- return get_or_create_agent
1502
-
1503
- get_or_create_agent = agent_factory()
1504
- agent = get_or_create_agent("default-session", "default-user")
1505
- {{else}}
1506
2632
  agent = Agent(
1507
2633
  model=load_model(),
1508
2634
  system_prompt="You are a helpful assistant. Use tools when appropriate.",
1509
2635
  tools=tools,
1510
2636
  )
2637
+
2638
+ {{#if hasMemory}}
2639
+ def session_manager_provider(input_data):
2640
+ return get_memory_session_manager(input_data.thread_id, "default-user")
2641
+
2642
+ config = StrandsAgentConfig(session_manager_provider=session_manager_provider)
2643
+ {{else}}
2644
+ config = StrandsAgentConfig()
1511
2645
  {{/if}}
1512
2646
 
2647
+ agui_agent = StrandsAgent(agent=agent, name="{{ name }}", description="A helpful assistant", config=config)
2648
+ app = create_strands_app(agui_agent, path="/invocations", ping_path="/ping")
2649
+
1513
2650
  if __name__ == "__main__":
1514
- serve_a2a(StrandsA2AExecutor(agent))
2651
+ uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))
1515
2652
  "
1516
2653
  `;
1517
2654
 
1518
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/model/__init__.py should match snapshot 1`] = `
2655
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/model/__init__.py should match snapshot 1`] = `
1519
2656
  "# Package marker
1520
2657
  "
1521
2658
  `;
1522
2659
 
1523
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/model/load.py should match snapshot 1`] = `
2660
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/model/load.py should match snapshot 1`] = `
1524
2661
  "{{#if (eq modelProvider "Bedrock")}}
1525
2662
  from strands.models.bedrock import BedrockModel
1526
2663
 
@@ -1647,7 +2784,7 @@ def load_model() -> GeminiModel:
1647
2784
  "
1648
2785
  `;
1649
2786
 
1650
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/pyproject.toml should match snapshot 1`] = `
2787
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/pyproject.toml should match snapshot 1`] = `
1651
2788
  "[build-system]
1652
2789
  requires = ["hatchling"]
1653
2790
  build-backend = "hatchling.build"
@@ -1655,18 +2792,22 @@ build-backend = "hatchling.build"
1655
2792
  [project]
1656
2793
  name = "{{ name }}"
1657
2794
  version = "0.1.0"
1658
- description = "AgentCore A2A Agent using Strands SDK"
2795
+ description = "AgentCore AG-UI Agent using Strands SDK"
1659
2796
  readme = "README.md"
1660
- requires-python = ">=3.10"
2797
+ requires-python = ">=3.12"
1661
2798
  dependencies = [
1662
2799
  {{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
1663
- {{/if}}"a2a-sdk[all] >= 0.2.0",
2800
+ {{/if}}"ag-ui-strands >= 0.1.7",
2801
+ "ag-ui-protocol >= 0.1.10",
1664
2802
  "aws-opentelemetry-distro",
1665
- "bedrock-agentcore[a2a] >= 1.0.3",
2803
+ "bedrock-agentcore >= 1.0.3",
1666
2804
  "botocore[crt] >= 1.35.0",
2805
+ "fastapi >= 0.115.12",
1667
2806
  {{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
1668
2807
  {{/if}}{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
1669
- {{/if}}"strands-agents >= 1.13.0",
2808
+ {{/if}}"strands-agents >= 1.15.0",
2809
+ "strands-agents-tools >= 0.2.14",
2810
+ "uvicorn >= 0.34.3",
1670
2811
  ]
1671
2812
 
1672
2813
  [tool.hatch.build.targets.wheel]
@@ -1674,12 +2815,12 @@ packages = ["."]
1674
2815
  "
1675
2816
  `;
1676
2817
 
1677
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/__init__.py should match snapshot 1`] = `
2818
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/capabilities/memory/__init__.py should match snapshot 1`] = `
1678
2819
  "# Package marker
1679
2820
  "
1680
2821
  `;
1681
2822
 
1682
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/session.py should match snapshot 1`] = `
2823
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/capabilities/memory/session.py should match snapshot 1`] = `
1683
2824
  "import os
1684
2825
  from typing import Optional
1685
2826
 
@@ -1834,6 +2975,66 @@ add_numbers_tool = FunctionTool(
1834
2975
  # Define a collection of tools used by the model
1835
2976
  tools = [add_numbers_tool]
1836
2977
 
2978
+ {{#if sessionStorageMountPath}}
2979
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
2980
+
2981
+ def _safe_resolve(path: str) -> str:
2982
+ """Resolve path safely within the storage boundary."""
2983
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
2984
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
2985
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
2986
+ return resolved
2987
+
2988
+ def file_read(path: str) -> str:
2989
+ """Read a file from persistent storage. The path is relative to the storage root."""
2990
+ try:
2991
+ full_path = _safe_resolve(path)
2992
+ with open(full_path) as f:
2993
+ return f.read()
2994
+ except ValueError as e:
2995
+ return str(e)
2996
+ except OSError as e:
2997
+ return f"Error reading '{path}': {e.strerror}"
2998
+
2999
+ def file_write(path: str, content: str) -> str:
3000
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
3001
+ try:
3002
+ full_path = _safe_resolve(path)
3003
+ parent = os.path.dirname(full_path)
3004
+ if parent:
3005
+ os.makedirs(parent, exist_ok=True)
3006
+ with open(full_path, "w") as f:
3007
+ f.write(content)
3008
+ return f"Written to {path}"
3009
+ except ValueError as e:
3010
+ return str(e)
3011
+ except OSError as e:
3012
+ return f"Error writing '{path}': {e.strerror}"
3013
+
3014
+ def list_files(directory: str = "") -> str:
3015
+ """List files in persistent storage. The directory is relative to the storage root."""
3016
+ try:
3017
+ target = _safe_resolve(directory)
3018
+ entries = os.listdir(target)
3019
+ return "\\n".join(entries) if entries else "(empty directory)"
3020
+ except ValueError as e:
3021
+ return str(e)
3022
+ except OSError as e:
3023
+ return f"Error listing '{directory}': {e.strerror}"
3024
+
3025
+ tools.extend([
3026
+ FunctionTool(file_read, description="Read a file from persistent storage. The path is relative to the storage root."),
3027
+ FunctionTool(file_write, description="Write content to a file in persistent storage. The path is relative to the storage root."),
3028
+ FunctionTool(list_files, description="List files in persistent storage. The directory is relative to the storage root."),
3029
+ ])
3030
+ {{/if}}
3031
+
3032
+ SYSTEM_MESSAGE = """
3033
+ You are a helpful assistant. Use tools when appropriate.
3034
+ {{#if sessionStorageMountPath}}
3035
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
3036
+ {{/if}}
3037
+ """
1837
3038
 
1838
3039
  @app.entrypoint
1839
3040
  async def invoke(payload, context):
@@ -1847,7 +3048,7 @@ async def invoke(payload, context):
1847
3048
  name="{{ name }}",
1848
3049
  model_client=load_model(),
1849
3050
  tools=tools + mcp_tools,
1850
- system_message="You are a helpful assistant. Use tools when appropriate.",
3051
+ system_message=SYSTEM_MESSAGE,
1851
3052
  )
1852
3053
 
1853
3054
  # Process the user prompt
@@ -2202,6 +3403,66 @@ def add_numbers(a: int, b: int) -> int:
2202
3403
  return a + b
2203
3404
 
2204
3405
 
3406
+ # Define a collection of tools used by the model
3407
+ tools = [add_numbers]
3408
+
3409
+ {{#if sessionStorageMountPath}}
3410
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
3411
+
3412
+ def _safe_resolve(path: str) -> str:
3413
+ """Resolve path safely within the storage boundary."""
3414
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
3415
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
3416
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
3417
+ return resolved
3418
+
3419
+ def file_read(path: str) -> str:
3420
+ """Read a file from persistent storage. The path is relative to the storage root."""
3421
+ try:
3422
+ full_path = _safe_resolve(path)
3423
+ with open(full_path) as f:
3424
+ return f.read()
3425
+ except ValueError as e:
3426
+ return str(e)
3427
+ except OSError as e:
3428
+ return f"Error reading '{path}': {e.strerror}"
3429
+
3430
+ def file_write(path: str, content: str) -> str:
3431
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
3432
+ try:
3433
+ full_path = _safe_resolve(path)
3434
+ parent = os.path.dirname(full_path)
3435
+ if parent:
3436
+ os.makedirs(parent, exist_ok=True)
3437
+ with open(full_path, "w") as f:
3438
+ f.write(content)
3439
+ return f"Written to {path}"
3440
+ except ValueError as e:
3441
+ return str(e)
3442
+ except OSError as e:
3443
+ return f"Error writing '{path}': {e.strerror}"
3444
+
3445
+ def list_files(directory: str = "") -> str:
3446
+ """List files in persistent storage. The directory is relative to the storage root."""
3447
+ try:
3448
+ target = _safe_resolve(directory)
3449
+ entries = os.listdir(target)
3450
+ return "\\n".join(entries) if entries else "(empty directory)"
3451
+ except ValueError as e:
3452
+ return str(e)
3453
+ except OSError as e:
3454
+ return f"Error listing '{directory}': {e.strerror}"
3455
+
3456
+ tools.extend([file_read, file_write, list_files])
3457
+ {{/if}}
3458
+
3459
+ AGENT_INSTRUCTION = """
3460
+ I can answer your questions using the knowledge I have!
3461
+ {{#if sessionStorageMountPath}}
3462
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
3463
+ {{/if}}
3464
+ """
3465
+
2205
3466
  # Get MCP Toolset
2206
3467
  {{#if hasGateway}}
2207
3468
  mcp_toolset = get_all_gateway_mcp_toolsets()
@@ -2224,8 +3485,8 @@ agent = Agent(
2224
3485
  model=MODEL_ID,
2225
3486
  name="{{ name }}",
2226
3487
  description="Agent to answer questions",
2227
- instruction="I can answer your questions using the knowledge I have!",
2228
- tools=mcp_toolset + [add_numbers],
3488
+ instruction=AGENT_INSTRUCTION,
3489
+ tools=mcp_toolset + tools,
2229
3490
  )
2230
3491
 
2231
3492
 
@@ -2563,6 +3824,66 @@ def add_numbers(a: int, b: int) -> int:
2563
3824
  # Define a collection of tools used by the model
2564
3825
  tools = [add_numbers]
2565
3826
 
3827
+ {{#if sessionStorageMountPath}}
3828
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
3829
+
3830
+ def _safe_resolve(path: str) -> str:
3831
+ """Resolve path safely within the storage boundary."""
3832
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
3833
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
3834
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
3835
+ return resolved
3836
+
3837
+ @tool
3838
+ def file_read(path: str) -> str:
3839
+ """Read a file from persistent storage. The path is relative to the storage root."""
3840
+ try:
3841
+ full_path = _safe_resolve(path)
3842
+ with open(full_path) as f:
3843
+ return f.read()
3844
+ except ValueError as e:
3845
+ return str(e)
3846
+ except OSError as e:
3847
+ return f"Error reading '{path}': {e.strerror}"
3848
+
3849
+ @tool
3850
+ def file_write(path: str, content: str) -> str:
3851
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
3852
+ try:
3853
+ full_path = _safe_resolve(path)
3854
+ parent = os.path.dirname(full_path)
3855
+ if parent:
3856
+ os.makedirs(parent, exist_ok=True)
3857
+ with open(full_path, "w") as f:
3858
+ f.write(content)
3859
+ return f"Written to {path}"
3860
+ except ValueError as e:
3861
+ return str(e)
3862
+ except OSError as e:
3863
+ return f"Error writing '{path}': {e.strerror}"
3864
+
3865
+ @tool
3866
+ def list_files(directory: str = "") -> str:
3867
+ """List files in persistent storage. The directory is relative to the storage root."""
3868
+ try:
3869
+ target = _safe_resolve(directory)
3870
+ entries = os.listdir(target)
3871
+ return "\\n".join(entries) if entries else "(empty directory)"
3872
+ except ValueError as e:
3873
+ return str(e)
3874
+ except OSError as e:
3875
+ return f"Error listing '{directory}': {e.strerror}"
3876
+
3877
+ tools.extend([file_read, file_write, list_files])
3878
+ {{/if}}
3879
+
3880
+ SYSTEM_PROMPT = """
3881
+ You are a helpful assistant. Use tools when appropriate.
3882
+ {{#if sessionStorageMountPath}}
3883
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
3884
+ {{/if}}
3885
+ """
3886
+
2566
3887
 
2567
3888
  @app.entrypoint
2568
3889
  async def invoke(payload, context):
@@ -2581,7 +3902,7 @@ async def invoke(payload, context):
2581
3902
  mcp_tools = await mcp_client.get_tools()
2582
3903
 
2583
3904
  # Define the agent using create_react_agent
2584
- graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools)
3905
+ graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools, prompt=SYSTEM_PROMPT)
2585
3906
 
2586
3907
  # Process the user prompt
2587
3908
  prompt = payload.get("prompt", "What can you help me with?")
@@ -2986,6 +4307,68 @@ def add_numbers(a: int, b: int) -> int:
2986
4307
  return a + b
2987
4308
 
2988
4309
 
4310
+ tools = [add_numbers]
4311
+
4312
+ {{#if sessionStorageMountPath}}
4313
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
4314
+
4315
+ def _safe_resolve(path: str) -> str:
4316
+ """Resolve path safely within the storage boundary."""
4317
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
4318
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
4319
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
4320
+ return resolved
4321
+
4322
+ @function_tool
4323
+ def file_read(path: str) -> str:
4324
+ """Read a file from persistent storage. The path is relative to the storage root."""
4325
+ try:
4326
+ full_path = _safe_resolve(path)
4327
+ with open(full_path) as f:
4328
+ return f.read()
4329
+ except ValueError as e:
4330
+ return str(e)
4331
+ except OSError as e:
4332
+ return f"Error reading '{path}': {e.strerror}"
4333
+
4334
+ @function_tool
4335
+ def file_write(path: str, content: str) -> str:
4336
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
4337
+ try:
4338
+ full_path = _safe_resolve(path)
4339
+ parent = os.path.dirname(full_path)
4340
+ if parent:
4341
+ os.makedirs(parent, exist_ok=True)
4342
+ with open(full_path, "w") as f:
4343
+ f.write(content)
4344
+ return f"Written to {path}"
4345
+ except ValueError as e:
4346
+ return str(e)
4347
+ except OSError as e:
4348
+ return f"Error writing '{path}': {e.strerror}"
4349
+
4350
+ @function_tool
4351
+ def list_files(directory: str = "") -> str:
4352
+ """List files in persistent storage. The directory is relative to the storage root."""
4353
+ try:
4354
+ target = _safe_resolve(directory)
4355
+ entries = os.listdir(target)
4356
+ return "\\n".join(entries) if entries else "(empty directory)"
4357
+ except ValueError as e:
4358
+ return str(e)
4359
+ except OSError as e:
4360
+ return f"Error listing '{directory}': {e.strerror}"
4361
+
4362
+ tools.extend([file_read, file_write, list_files])
4363
+ {{/if}}
4364
+
4365
+ INSTRUCTIONS = """
4366
+ You are a helpful assistant. Use tools when appropriate.
4367
+ {{#if sessionStorageMountPath}}
4368
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
4369
+ {{/if}}
4370
+ """
4371
+
2989
4372
  # Define the agent execution
2990
4373
  async def main(query):
2991
4374
  ensure_credentials_loaded()
@@ -2995,8 +4378,9 @@ async def main(query):
2995
4378
  agent = Agent(
2996
4379
  name="{{ name }}",
2997
4380
  model="gpt-4.1",
4381
+ instructions=INSTRUCTIONS,
2998
4382
  mcp_servers=mcp_servers,
2999
- tools=[add_numbers]
4383
+ tools=tools
3000
4384
  )
3001
4385
  result = await Runner.run(agent, query)
3002
4386
  return result
@@ -3004,8 +4388,9 @@ async def main(query):
3004
4388
  agent = Agent(
3005
4389
  name="{{ name }}",
3006
4390
  model="gpt-4.1",
4391
+ instructions=INSTRUCTIONS,
3007
4392
  mcp_servers=[],
3008
- tools=[add_numbers]
4393
+ tools=tools
3009
4394
  )
3010
4395
  result = await Runner.run(agent, query)
3011
4396
  return result
@@ -3016,8 +4401,9 @@ async def main(query):
3016
4401
  agent = Agent(
3017
4402
  name="{{ name }}",
3018
4403
  model="gpt-4.1",
4404
+ instructions=INSTRUCTIONS,
3019
4405
  mcp_servers=active_servers,
3020
- tools=[add_numbers]
4406
+ tools=tools
3021
4407
  )
3022
4408
  result = await Runner.run(agent, query)
3023
4409
  return result
@@ -3025,8 +4411,9 @@ async def main(query):
3025
4411
  agent = Agent(
3026
4412
  name="{{ name }}",
3027
4413
  model="gpt-4.1",
4414
+ instructions=INSTRUCTIONS,
3028
4415
  mcp_servers=[],
3029
- tools=[add_numbers]
4416
+ tools=tools
3030
4417
  )
3031
4418
  result = await Runner.run(agent, query)
3032
4419
  return result
@@ -3308,6 +4695,9 @@ from mcp_client.client import get_streamable_http_mcp_client
3308
4695
  {{#if hasMemory}}
3309
4696
  from memory.session import get_memory_session_manager
3310
4697
  {{/if}}
4698
+ {{#if sessionStorageMountPath}}
4699
+ import os
4700
+ {{/if}}
3311
4701
 
3312
4702
  app = BedrockAgentCoreApp()
3313
4703
  log = app.logger
@@ -3329,11 +4719,70 @@ def add_numbers(a: int, b: int) -> int:
3329
4719
  return a+b
3330
4720
  tools.append(add_numbers)
3331
4721
 
4722
+ {{#if sessionStorageMountPath}}
4723
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
4724
+
4725
+ def _safe_resolve(path: str) -> str:
4726
+ """Resolve path safely within the storage boundary."""
4727
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
4728
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
4729
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
4730
+ return resolved
4731
+
4732
+ @tool
4733
+ def file_read(path: str) -> str:
4734
+ """Read a file from persistent storage. The path is relative to the storage root."""
4735
+ try:
4736
+ full_path = _safe_resolve(path)
4737
+ with open(full_path) as f:
4738
+ return f.read()
4739
+ except ValueError as e:
4740
+ return str(e)
4741
+ except OSError as e:
4742
+ return f"Error reading '{path}': {e.strerror}"
4743
+
4744
+ @tool
4745
+ def file_write(path: str, content: str) -> str:
4746
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
4747
+ try:
4748
+ full_path = _safe_resolve(path)
4749
+ parent = os.path.dirname(full_path)
4750
+ if parent:
4751
+ os.makedirs(parent, exist_ok=True)
4752
+ with open(full_path, "w") as f:
4753
+ f.write(content)
4754
+ return f"Written to {path}"
4755
+ except ValueError as e:
4756
+ return str(e)
4757
+ except OSError as e:
4758
+ return f"Error writing '{path}': {e.strerror}"
4759
+
4760
+ @tool
4761
+ def list_files(directory: str = "") -> str:
4762
+ """List files in persistent storage. The directory is relative to the storage root."""
4763
+ try:
4764
+ target = _safe_resolve(directory)
4765
+ entries = os.listdir(target)
4766
+ return "\\n".join(entries) if entries else "(empty directory)"
4767
+ except ValueError as e:
4768
+ return str(e)
4769
+ except OSError as e:
4770
+ return f"Error listing '{directory}': {e.strerror}"
4771
+
4772
+ tools.extend([file_read, file_write, list_files])
4773
+ {{/if}}
4774
+
3332
4775
  # Add MCP client to tools if available
3333
4776
  for mcp_client in mcp_clients:
3334
4777
  if mcp_client:
3335
4778
  tools.append(mcp_client)
3336
4779
 
4780
+ SYSTEM_PROMPT = """
4781
+ You are a helpful assistant. Use tools when appropriate.
4782
+ {{#if sessionStorageMountPath}}
4783
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
4784
+ {{/if}}
4785
+ """
3337
4786
 
3338
4787
  {{#if hasMemory}}
3339
4788
  def agent_factory():
@@ -3345,9 +4794,7 @@ def agent_factory():
3345
4794
  cache[key] = Agent(
3346
4795
  model=load_model(),
3347
4796
  session_manager=get_memory_session_manager(session_id, user_id),
3348
- system_prompt="""
3349
- You are a helpful assistant. Use tools when appropriate.
3350
- """,
4797
+ system_prompt=SYSTEM_PROMPT,
3351
4798
  tools=tools
3352
4799
  )
3353
4800
  return cache[key]
@@ -3361,9 +4808,7 @@ def get_or_create_agent():
3361
4808
  if _agent is None:
3362
4809
  _agent = Agent(
3363
4810
  model=load_model(),
3364
- system_prompt="""
3365
- You are a helpful assistant. Use tools when appropriate.
3366
- """,
4811
+ system_prompt=SYSTEM_PROMPT,
3367
4812
  tools=tools
3368
4813
  )
3369
4814
  return _agent
@@ -3826,27 +5271,47 @@ exports[`Assets Directory Snapshots > Root-level assets > AGENTS.md should match
3826
5271
 
3827
5272
  This directory stores:
3828
5273
 
3829
- - Template assets for agents written in different Languages, SDKs and having different configurations
5274
+ - Template assets for agents written in different languages, SDKs, and configurations
3830
5275
  - Container templates (\`container/python/\`) with \`Dockerfile\` and \`.dockerignore\` for Container build agents
5276
+ - Vended documentation (\`README.md\`, \`agents/AGENTS.md\`) copied into user projects at create time
5277
+ - CDK project template (\`cdk/\`) using \`@aws/agentcore-cdk\` L3 constructs
5278
+ - Evaluator templates (\`evaluators/\`) for code-based evaluators
5279
+ - MCP tool templates (\`mcp/\`) for Lambda and AgentCoreRuntime compute
3831
5280
 
3832
5281
  ### Directory Layout
3833
5282
 
3834
5283
  \`\`\`
3835
5284
  assets/
3836
- ├── python/ # Framework templates (one per SDK)
3837
- ├── strands/
3838
- ├── langchain_langgraph/
3839
- ├── googleadk/
3840
- ├── openaiagents/
3841
- └── autogen/
5285
+ ├── README.md # Vended to project root as project README
5286
+ ├── AGENTS.md # This file — internal dev context
5287
+ ├── agents/
5288
+ └── AGENTS.md # Vended to project root for AI coding assistants
5289
+ ├── python/ # Framework templates (one per SDK per protocol)
5290
+ ├── http/ # HTTP protocol agents
5291
+ │ │ ├── strands/
5292
+ │ │ ├── langchain_langgraph/
5293
+ │ │ ├── googleadk/
5294
+ │ │ ├── openaiagents/
5295
+ │ │ └── autogen/
5296
+ │ ├── mcp/ # MCP protocol agents
5297
+ │ │ └── standalone/
5298
+ │ └── a2a/ # A2A protocol agents
5299
+ │ ├── strands/
5300
+ │ ├── langchain_langgraph/
5301
+ │ └── googleadk/
5302
+ ├── typescript/ # TypeScript agent templates
3842
5303
  ├── container/ # Container build templates
3843
5304
  │ └── python/
3844
5305
  │ ├── Dockerfile
3845
5306
  │ └── dockerignore.template
3846
- └── agents/ # AGENTS.md vended to user projects
5307
+ ├── cdk/ # CDK project template (@aws/agentcore-cdk)
5308
+ ├── evaluators/ # Code-based evaluator templates
5309
+ └── mcp/ # MCP tool templates (Lambda + AgentCoreRuntime)
5310
+ ├── python/
5311
+ └── python-lambda/
3847
5312
  \`\`\`
3848
5313
 
3849
- The rendering logic is rooted in the \`AgentEnvSpec\` and must ALWAYS respect the configuration in the Spec.
5314
+ The rendering logic is rooted in the \`AgentEnvSpec\` and must ALWAYS respect the configuration in the spec.
3850
5315
 
3851
5316
  For Container builds, \`BaseRenderer.render()\` automatically copies the \`container/<language>/\` templates (Dockerfile,
3852
5317
  .dockerignore) into the agent directory when \`buildType === 'Container'\`.
@@ -3855,10 +5320,13 @@ For Container builds, \`BaseRenderer.render()\` automatically copies the \`conta
3855
5320
 
3856
5321
  - Always make sure the templates are as close to working code as possible
3857
5322
  - AVOID as much as possible using any conditionals within the templates
5323
+ - Test template rendering with \`agentcore add agent\` for each framework/protocol combination
3858
5324
 
3859
5325
  ## How to use the assets in this directory
3860
5326
 
3861
- - These assets are rendered by the CLI's template renderer in \`src/cli/templates/\`.
5327
+ - These assets are rendered by the CLI's template renderer in \`src/cli/templates/\`
5328
+ - The \`README.md\` and \`agents/AGENTS.md\` are copied verbatim (no template rendering) during project creation
5329
+ - The \`.llm-context/\` files are sourced from \`src/schema/llm-compacted/\` and written during init
3862
5330
  "
3863
5331
  `;
3864
5332
 
@@ -3870,14 +5338,19 @@ This project was created with the [AgentCore CLI](https://github.com/aws/agentco
3870
5338
  ## Project Structure
3871
5339
 
3872
5340
  \`\`\`
3873
- .
3874
5341
  my-project/
5342
+ ├── AGENTS.md # AI coding assistant context
3875
5343
  ├── agentcore/
3876
- │ ├── .env.local # API keys (gitignored)
3877
- │ ├── agentcore.json # Resource specifications
3878
- │ ├── aws-targets.json # Deployment targets
3879
- └── cdk/ # CDK infrastructure
3880
- ├── app/ # Application code
5344
+ │ ├── agentcore.json # Project config (agents, memories, credentials, gateways, evaluators)
5345
+ │ ├── aws-targets.json # Deployment targets (account + region)
5346
+ │ ├── .env.local # Secrets — API keys (gitignored)
5347
+ ├── .llm-context/ # TypeScript type definitions for AI assistants
5348
+ │ │ ├── agentcore.ts # AgentCoreProjectSpec types
5349
+ │ │ ├── aws-targets.ts # Deployment target types
5350
+ │ │ └── mcp.ts # Gateway and MCP tool types
5351
+ │ └── cdk/ # CDK infrastructure (@aws/agentcore-cdk)
5352
+ ├── app/ # Agent application code
5353
+ └── evaluators/ # Custom evaluator code (if any)
3881
5354
  \`\`\`
3882
5355
 
3883
5356
  ## Getting Started
@@ -3885,7 +5358,9 @@ my-project/
3885
5358
  ### Prerequisites
3886
5359
 
3887
5360
  - **Node.js** 20.x or later
3888
- - **uv** for Python agents ([install](https://docs.astral.sh/uv/getting-started/installation/))
5361
+ - **Python 3.10+** and **uv** for Python agents ([install uv](https://docs.astral.sh/uv/getting-started/installation/))
5362
+ - **AWS credentials** configured (\`aws configure\` or environment variables)
5363
+ - **Docker** (only for Container build agents)
3889
5364
 
3890
5365
  ### Development
3891
5366
 
@@ -3903,44 +5378,62 @@ Deploy to AWS:
3903
5378
  agentcore deploy
3904
5379
  \`\`\`
3905
5380
 
3906
- Or use CDK directly:
5381
+ ## Commands
3907
5382
 
3908
- \`\`\`bash
3909
- cd agentcore/cdk
3910
- npx cdk deploy
3911
- \`\`\`
5383
+ | Command | Description |
5384
+ | --- | --- |
5385
+ | \`agentcore create\` | Create a new AgentCore project |
5386
+ | \`agentcore add\` | Add resources (agent, memory, credential, gateway, evaluator, policy) |
5387
+ | \`agentcore remove\` | Remove resources |
5388
+ | \`agentcore dev\` | Run agent locally with hot-reload |
5389
+ | \`agentcore deploy\` | Deploy to AWS via CDK |
5390
+ | \`agentcore status\` | Show deployment status |
5391
+ | \`agentcore invoke\` | Invoke agent (local or deployed) |
5392
+ | \`agentcore logs\` | View agent logs |
5393
+ | \`agentcore traces\` | View agent traces |
5394
+ | \`agentcore eval\` | Run evaluations |
5395
+ | \`agentcore package\` | Package agent artifacts |
5396
+ | \`agentcore validate\` | Validate configuration |
5397
+ | \`agentcore pause\` | Pause a deployed agent |
5398
+ | \`agentcore resume\` | Resume a paused agent |
5399
+ | \`agentcore fetch\` | Fetch remote resource definitions |
5400
+ | \`agentcore import\` | Import existing resources |
5401
+ | \`agentcore update\` | Check for CLI updates |
3912
5402
 
3913
5403
  ## Configuration
3914
5404
 
3915
- Edit the JSON files in \`agentcore/\` to configure your agents, memory, and credentials. See \`agentcore/.llm-context/\` for
3916
- type definitions and validation constraints.
5405
+ Edit the JSON files in \`agentcore/\` to configure your project. See \`agentcore/.llm-context/\` for type definitions and validation constraints.
3917
5406
 
3918
- The project uses a **flat resource model** where agents, memories, and credentials are top-level arrays in
3919
- \`agentcore.json\`.
5407
+ The project uses a **flat resource model** agents, memories, credentials, gateways, evaluators, and policies are top-level arrays in \`agentcore.json\`. Resources are independent; agents discover memories and credentials at runtime via environment variables or SDK calls.
3920
5408
 
3921
- ## Commands
5409
+ ## Resources
3922
5410
 
3923
- | Command | Description |
3924
- | -------------------- | ----------------------------------------------- |
3925
- | \`agentcore create\` | Create a new AgentCore project |
3926
- | \`agentcore add\` | Add resources (agent, memory, credential, target) |
3927
- | \`agentcore remove\` | Remove resources |
3928
- | \`agentcore dev\` | Run agent locally |
3929
- | \`agentcore deploy\` | Deploy to AWS |
3930
- | \`agentcore status\` | Show deployment status |
3931
- | \`agentcore invoke\` | Invoke agent (local or deployed) |
3932
- | \`agentcore package\` | Package agent artifacts |
3933
- | \`agentcore validate\` | Validate configuration |
3934
- | \`agentcore update\` | Check for CLI updates |
5411
+ | Resource | Purpose |
5412
+ | --- | --- |
5413
+ | Agent (runtime) | HTTP, MCP, or A2A agent deployed to AgentCore Runtime |
5414
+ | Memory | Persistent context storage with configurable strategies |
5415
+ | Credential | API key or OAuth credential providers |
5416
+ | Gateway | MCP gateway that routes tool calls to targets |
5417
+ | Gateway Target | Tool implementation (Lambda, MCP server, OpenAPI, Smithy, API Gateway) |
5418
+ | Evaluator | Custom LLM-as-a-Judge or code-based evaluation |
5419
+ | Online Eval Config | Continuous evaluation pipeline for deployed agents |
5420
+ | Policy | Cedar authorization policies for gateway tools |
3935
5421
 
3936
5422
  ### Agent Types
3937
5423
 
3938
- - **Template agents**: Created from framework templates (Strands, LangChain_LangGraph, CrewAI, GoogleADK, OpenAIAgents)
5424
+ - **Template agents**: Created from framework templates (Strands, LangChain/LangGraph, GoogleADK, OpenAI Agents, Autogen)
3939
5425
  - **BYO agents**: Bring your own code with \`agentcore add agent --type byo\`
5426
+ - **Import agents**: Import existing Bedrock agents with \`agentcore import\`
5427
+
5428
+ ### Build Types
5429
+
5430
+ - **CodeZip**: Python source packaged as a zip and deployed directly to AgentCore Runtime
5431
+ - **Container**: Docker image built via CodeBuild (ARM64), pushed to ECR, and deployed to AgentCore Runtime
3940
5432
 
3941
5433
  ## Documentation
3942
5434
 
3943
- - [AgentCore CLI Documentation](https://github.com/aws/agentcore-cli)
5435
+ - [AgentCore CLI](https://github.com/aws/agentcore-cli)
5436
+ - [AgentCore CDK Constructs](https://github.com/aws/agentcore-l3-cdk-constructs)
3944
5437
  - [Amazon Bedrock AgentCore](https://aws.amazon.com/bedrock/agentcore/)
3945
5438
  "
3946
5439
  `;
@@ -3950,103 +5443,114 @@ exports[`Assets Directory Snapshots > Root-level assets > agents/AGENTS.md shoul
3950
5443
 
3951
5444
  This project contains configuration and infrastructure for an Amazon Bedrock AgentCore application.
3952
5445
 
3953
- The \`agentcore/\` directory serves as a declarative model of an AgentCore project along with a concrete implementation
3954
- through the \`agentcore/cdk/\` project which is modeled to take the configs as input. The project uses a **flat resource
3955
- model** where agents, memories, and credentials are top-level arrays.
5446
+ The \`agentcore/\` directory is a declarative model of the project. The \`agentcore/cdk/\` subdirectory uses the
5447
+ \`@aws/agentcore-cdk\` L3 constructs to deploy the configuration to AWS.
3956
5448
 
3957
5449
  ## Mental Model
3958
5450
 
3959
- The project uses a **flat resource model**. Agents, memories, and credentials are independent top-level arrays in
3960
- \`agentcore.json\`. There is no binding or attachment between resources in the schema — each resource is provisioned
3961
- independently. To use a memory or credential from an agent, the application code discovers the resource at runtime
3962
- (e.g., via environment variables or SDK calls). Tags defined in \`agentcore.json\` flow through to deployed CloudFormation resources.
5451
+ The project uses a **flat resource model**. Agents, memories, credentials, gateways, evaluators, and policies are
5452
+ independent top-level arrays in \`agentcore.json\`. There is no binding between resources in the schema — each resource is
5453
+ provisioned independently. Agents discover memories and credentials at runtime via environment variables or SDK calls.
5454
+ Tags defined in \`agentcore.json\` flow through to deployed CloudFormation resources.
3963
5455
 
3964
5456
  ## Critical Invariants
3965
5457
 
3966
- 1. **Schema-First Authority:** The \`.json\` files are the absolute source of truth. Do not attempt to modify agent
3967
- behavior by editing the generated CDK code in \`cdk/\`.
3968
- 2. **Resource Identity:** The \`name\` field in the schema determines the CloudFormation Logical ID.
3969
- - **Renaming** an agent or target will **destroy and recreate** that resource.
3970
- - **Modifying** other fields (descriptions, config) will update the resource **in-place**.
3971
- 3. **1:1 Validation:** The schema maps directly to valid CloudFormation. If your JSON conforms to the types in
3972
- \`.llm-context/\`, it will deploy successfully.
3973
- 4. **Resource Removal:** To remove all resources, use \`agentcore remove all\`. To tear down deployed infrastructure, run
3974
- \`agentcore deploy\` after removal — it will detect the empty state and offer a teardown flow.
5458
+ 1. **Schema-First Authority:** The \`.json\` files are the source of truth. Do not modify agent behavior by editing
5459
+ generated CDK code in \`cdk/\`.
5460
+ 2. **Resource Identity:** The \`name\` field determines the CloudFormation Logical ID.
5461
+ - **Renaming** a resource will **destroy and recreate** it.
5462
+ - **Modifying** other fields will update the resource **in-place**.
5463
+ 3. **Schema Validation:** If your JSON conforms to the types in \`.llm-context/\`, it will deploy successfully. Run
5464
+ \`agentcore validate\` to check.
5465
+ 4. **Resource Removal:** Use \`agentcore remove\` to remove resources. Run \`agentcore deploy\` after removal to tear down
5466
+ deployed infrastructure.
3975
5467
 
3976
5468
  ## Directory Structure
3977
5469
 
3978
5470
  \`\`\`
3979
- myNewProject/
3980
- ├── AGENTS.md # This file - AI coding assistant context
3981
- ├── agentcore/ # AgentCore configuration directory
5471
+ myProject/
5472
+ ├── AGENTS.md # This file AI coding assistant context
5473
+ ├── agentcore/
3982
5474
  │ ├── agentcore.json # Main project config (AgentCoreProjectSpec)
3983
- │ ├── aws-targets.json # Deployment targets
3984
- │ ├── .llm-context/ # TypeScript type definitions for AI coding assistants
3985
- ├── README.md # Guide to using the schema files
5475
+ │ ├── aws-targets.json # Deployment targets (account + region)
5476
+ │ ├── .env.local # Secrets API keys (gitignored)
5477
+ │ ├── .llm-context/ # TypeScript type definitions for AI assistants
5478
+ │ │ ├── README.md # Guide to using schema files
3986
5479
  │ │ ├── agentcore.ts # AgentCoreProjectSpec types
3987
- │ │ └── aws-targets.ts # AWS deployment target types
3988
- │ └── cdk/ # AWS CDK project for deployment
3989
- └── app/ # Application code (if agents were created)
5480
+ │ │ ├── aws-targets.ts # AWS deployment target types
5481
+ └── mcp.ts # Gateway and MCP tool types
5482
+ └── cdk/ # AWS CDK project (@aws/agentcore-cdk L3 constructs)
5483
+ ├── app/ # Agent application code
5484
+ └── evaluators/ # Custom evaluator code (if any)
3990
5485
  \`\`\`
3991
5486
 
3992
5487
  ## Schema Reference
3993
5488
 
3994
5489
  The \`agentcore/.llm-context/\` directory contains TypeScript type definitions optimized for AI coding assistants. Each
3995
- file maps to a JSON config file and includes validation constraints as comments.
5490
+ file maps to a JSON config file and includes validation constraints as comments (\`@regex\`, \`@min\`, \`@max\`).
3996
5491
 
3997
- | JSON Config | Schema File | Root Type |
3998
- | ---------------------------- | --------------------------------------- | ----------------------- |
3999
- | \`agentcore/agentcore.json\` | \`agentcore/.llm-context/agentcore.ts\` | \`AgentCoreProjectSpec\` |
4000
- | \`agentcore/aws-targets.json\` | \`agentcore/.llm-context/aws-targets.ts\` | \`AWSDeploymentTarget[]\` |
5492
+ | JSON Config | Schema File | Root Type |
5493
+ | --- | --- | --- |
5494
+ | \`agentcore/agentcore.json\` | \`agentcore/.llm-context/agentcore.ts\` | \`AgentCoreProjectSpec\` |
5495
+ | \`agentcore/agentcore.json\` (gateways) | \`agentcore/.llm-context/mcp.ts\` | \`AgentCoreMcpSpec\` |
5496
+ | \`agentcore/aws-targets.json\` | \`agentcore/.llm-context/aws-targets.ts\` | \`AwsDeploymentTarget[]\` |
4001
5497
 
4002
5498
  ### Key Types
4003
5499
 
4004
- - **AgentCoreProjectSpec**: Root project configuration with \`agents\`, \`memories\`, \`credentials\` arrays
4005
- - **AgentEnvSpec**: Agent configuration (runtime, entrypoint, code location)
4006
- - **Memory**: Memory resource with strategies and expiry
4007
- - **Credential**: API key credential provider
5500
+ - **AgentCoreProjectSpec**: Root config with \`runtimes\`, \`memories\`, \`credentials\`, \`agentCoreGateways\`, \`evaluators\`, \`onlineEvalConfigs\`, \`policyEngines\` arrays
5501
+ - **AgentEnvSpec**: Agent configuration (build type, entrypoint, code location, runtime version, network mode)
5502
+ - **Memory**: Memory resource with strategies (SEMANTIC, SUMMARIZATION, USER_PREFERENCE, EPISODIC) and expiry
5503
+ - **Credential**: API key or OAuth credential provider
5504
+ - **AgentCoreGateway**: MCP gateway with targets (Lambda, MCP server, OpenAPI, Smithy, API Gateway)
5505
+ - **Evaluator**: LLM-as-a-Judge or code-based evaluator
5506
+ - **OnlineEvalConfig**: Continuous evaluation pipeline bound to an agent
4008
5507
 
4009
5508
  ### Common Enum Values
4010
5509
 
4011
5510
  - **BuildType**: \`'CodeZip'\` | \`'Container'\`
4012
- - **NetworkMode**: \`'PUBLIC'\`
4013
- - **RuntimeVersion**: \`'PYTHON_3_10'\` | \`'PYTHON_3_11'\` | \`'PYTHON_3_12'\` | \`'PYTHON_3_13'\` | \`'PYTHON_3_14'\`
5511
+ - **NetworkMode**: \`'PUBLIC'\` | \`'VPC'\`
5512
+ - **RuntimeVersion**: \`'PYTHON_3_10'\` | \`'PYTHON_3_11'\` | \`'PYTHON_3_12'\` | \`'PYTHON_3_13'\` | \`'PYTHON_3_14'\` | \`'NODE_18'\` | \`'NODE_20'\` | \`'NODE_22'\`
4014
5513
  - **MemoryStrategyType**: \`'SEMANTIC'\` | \`'SUMMARIZATION'\` | \`'USER_PREFERENCE'\` | \`'EPISODIC'\`
5514
+ - **GatewayTargetType**: \`'lambda'\` | \`'mcpServer'\` | \`'openApiSchema'\` | \`'smithyModel'\` | \`'apiGateway'\` | \`'lambdaFunctionArn'\`
5515
+ - **ModelProvider**: \`'Bedrock'\` | \`'Gemini'\` | \`'OpenAI'\` | \`'Anthropic'\`
4015
5516
 
4016
5517
  ### Build Types
4017
5518
 
4018
- - **CodeZip**: Python source is packaged as a zip artifact and deployed directly to AgentCore Runtime.
4019
- - **Container**: Agent code is built as a Docker container image. Requires a \`Dockerfile\` in the agent's \`codeLocation\`
4020
- directory. At deploy time, the source is uploaded to S3, built in CodeBuild (ARM64), pushed to a per-agent ECR
4021
- repository, and the container URI is provided to the AgentCore Runtime. For local development (\`agentcore dev\`), the
4022
- container is built and run locally with volume-mounted hot-reload.
5519
+ - **CodeZip**: Python source packaged as a zip and deployed directly to AgentCore Runtime.
5520
+ - **Container**: Docker image built in CodeBuild (ARM64), pushed to a per-agent ECR repository. Requires a \`Dockerfile\`
5521
+ in the agent's \`codeLocation\` directory. For local development (\`agentcore dev\`), the container is built and run
5522
+ locally with volume-mounted hot-reload.
4023
5523
 
4024
5524
  ### Supported Frameworks (for template agents)
4025
5525
 
4026
- - **Strands** - Works with Bedrock, Anthropic, OpenAI, Gemini
4027
- - **LangChain_LangGraph** - Works with Bedrock, Anthropic, OpenAI, Gemini
4028
- - **GoogleADK** - Gemini only
4029
- - **OpenAIAgents** - OpenAI only
5526
+ - **Strands** Bedrock, Anthropic, OpenAI, Gemini
5527
+ - **LangChain/LangGraph** Bedrock, Anthropic, OpenAI, Gemini
5528
+ - **GoogleADK** Gemini
5529
+ - **OpenAI Agents** OpenAI
5530
+ - **Autogen** — Bedrock, Anthropic, OpenAI, Gemini
4030
5531
 
5532
+ ### Protocols
4031
5533
 
4032
- ### Specific Context
4033
-
4034
- Directory pathing to local projects is required for runtimes. Both CodeZip (Python zip) and Container (Docker image)
4035
- deployment options are available.
5534
+ - **HTTP** — Standard HTTP agent endpoint
5535
+ - **MCP** — Model Context Protocol server
5536
+ - **A2A** — Agent-to-Agent protocol (Google A2A)
4036
5537
 
4037
5538
  ## Deployment
4038
5539
 
4039
- The \`agentcore/cdk/\` subdirectory contains an AWS CDK node project.
5540
+ Deployments are orchestrated through the CLI:
4040
5541
 
4041
- Deployments of this project are primarily intended to be orchestrated through the \`agentcore deploy\` command in the CLI.
5542
+ \`\`\`bash
5543
+ agentcore deploy # Synthesizes CDK and deploys to AWS
5544
+ agentcore status # Shows deployment status
5545
+ \`\`\`
4042
5546
 
4043
- Alternatively, the project can be deployed directly as a traditional CDK project:
5547
+ Alternatively, deploy directly via CDK:
4044
5548
 
4045
5549
  \`\`\`bash
4046
5550
  cd agentcore/cdk
4047
5551
  npm install
4048
- npx cdk synth # Preview CloudFormation template
4049
- npx cdk deploy # Deploy to AWS
5552
+ npx cdk synth
5553
+ npx cdk deploy
4050
5554
  \`\`\`
4051
5555
 
4052
5556
  ## Editing Schemas
@@ -4057,7 +5561,25 @@ When modifying JSON config files:
4057
5561
  2. Check validation constraint comments (\`@regex\`, \`@min\`, \`@max\`)
4058
5562
  3. Use exact enum values as string literals
4059
5563
  4. Use CloudFormation-safe names (alphanumeric, start with letter)
4060
- 5. Run \`agentcore validate\` command to verify changes.
5564
+ 5. Run \`agentcore validate\` to verify changes
5565
+
5566
+ ## CLI Commands
5567
+
5568
+ | Command | Description |
5569
+ | --- | --- |
5570
+ | \`agentcore create\` | Create a new project |
5571
+ | \`agentcore add <resource>\` | Add agent, memory, credential, gateway, evaluator, policy |
5572
+ | \`agentcore remove <resource>\` | Remove a resource |
5573
+ | \`agentcore dev\` | Run agent locally with hot-reload |
5574
+ | \`agentcore deploy\` | Deploy to AWS |
5575
+ | \`agentcore status\` | Show deployment status |
5576
+ | \`agentcore invoke\` | Invoke agent (local or deployed) |
5577
+ | \`agentcore logs\` | View agent logs |
5578
+ | \`agentcore traces\` | View agent traces |
5579
+ | \`agentcore eval\` | Run evaluations against an agent |
5580
+ | \`agentcore package\` | Package agent artifacts |
5581
+ | \`agentcore validate\` | Validate configuration |
5582
+ | \`agentcore pause\` / \`resume\` | Pause or resume a deployed agent |
4061
5583
  "
4062
5584
  `;
4063
5585