@aws/agentcore 0.8.2 → 1.0.0-preview.1

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 (90) hide show
  1. package/dist/agent-inspector/favicon.svg +60 -0
  2. package/dist/agent-inspector/index.css +1 -0
  3. package/dist/agent-inspector/index.html +14 -0
  4. package/dist/agent-inspector/index.js +275 -0
  5. package/dist/agent-inspector/index.js.map +1 -0
  6. package/dist/assets/README.md +56 -31
  7. package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +1732 -259
  8. package/dist/assets/__tests__/__snapshots__/dockerfile-render.test.ts.snap +77 -0
  9. package/dist/assets/__tests__/dockerfile-render.test.ts +24 -0
  10. package/dist/assets/agents/AGENTS.md +84 -55
  11. package/dist/assets/cdk/bin/cdk.ts +35 -0
  12. package/dist/assets/cdk/lib/cdk-stack.ts +15 -2
  13. package/dist/assets/cdk/package.json +1 -1
  14. package/dist/assets/container/python/Dockerfile +4 -0
  15. package/dist/assets/harness/invoke.py.template +74 -0
  16. package/dist/assets/python/a2a/googleadk/base/main.py +65 -3
  17. package/dist/assets/python/a2a/langchain_langgraph/base/main.py +64 -1
  18. package/dist/assets/python/a2a/strands/base/main.py +65 -2
  19. package/dist/assets/python/agui/googleadk/base/README.md +30 -0
  20. package/dist/assets/python/agui/googleadk/base/gitignore.template +41 -0
  21. package/dist/assets/python/agui/googleadk/base/main.py +31 -0
  22. package/dist/assets/python/agui/googleadk/base/model/__init__.py +1 -0
  23. package/dist/assets/python/agui/googleadk/base/model/load.py +41 -0
  24. package/dist/assets/python/agui/googleadk/base/pyproject.toml +24 -0
  25. package/dist/assets/python/agui/langchain_langgraph/base/README.md +22 -0
  26. package/dist/assets/python/agui/langchain_langgraph/base/gitignore.template +41 -0
  27. package/dist/assets/python/agui/langchain_langgraph/base/main.py +74 -0
  28. package/dist/assets/python/agui/langchain_langgraph/base/model/__init__.py +1 -0
  29. package/dist/assets/python/agui/langchain_langgraph/base/model/load.py +123 -0
  30. package/dist/assets/python/agui/langchain_langgraph/base/pyproject.toml +30 -0
  31. package/dist/assets/python/agui/strands/base/README.md +22 -0
  32. package/dist/assets/python/agui/strands/base/gitignore.template +41 -0
  33. package/dist/assets/python/agui/strands/base/main.py +43 -0
  34. package/dist/assets/python/agui/strands/base/model/__init__.py +1 -0
  35. package/dist/assets/python/agui/strands/base/model/load.py +123 -0
  36. package/dist/assets/python/agui/strands/base/pyproject.toml +27 -0
  37. package/dist/assets/python/agui/strands/capabilities/memory/__init__.py +1 -0
  38. package/dist/assets/python/agui/strands/capabilities/memory/session.py +38 -0
  39. package/dist/assets/python/http/autogen/base/main.py +61 -1
  40. package/dist/assets/python/http/googleadk/base/main.py +62 -2
  41. package/dist/assets/python/http/langchain_langgraph/base/main.py +61 -1
  42. package/dist/assets/python/http/openaiagents/base/main.py +70 -4
  43. package/dist/assets/python/http/strands/base/main.py +64 -6
  44. package/dist/cli/index.mjs +517 -466
  45. package/dist/lib/constants.d.ts +1 -0
  46. package/dist/lib/constants.d.ts.map +1 -1
  47. package/dist/lib/constants.js +3 -1
  48. package/dist/lib/constants.js.map +1 -1
  49. package/dist/lib/errors/config.d.ts.map +1 -1
  50. package/dist/lib/errors/config.js +5 -2
  51. package/dist/lib/errors/config.js.map +1 -1
  52. package/dist/lib/schemas/io/config-io.d.ts +9 -1
  53. package/dist/lib/schemas/io/config-io.d.ts.map +1 -1
  54. package/dist/lib/schemas/io/config-io.js +14 -0
  55. package/dist/lib/schemas/io/config-io.js.map +1 -1
  56. package/dist/lib/schemas/io/path-resolver.d.ts +12 -0
  57. package/dist/lib/schemas/io/path-resolver.d.ts.map +1 -1
  58. package/dist/lib/schemas/io/path-resolver.js +18 -0
  59. package/dist/lib/schemas/io/path-resolver.js.map +1 -1
  60. package/dist/schema/constants.d.ts +1 -0
  61. package/dist/schema/constants.d.ts.map +1 -1
  62. package/dist/schema/constants.js +8 -1
  63. package/dist/schema/constants.js.map +1 -1
  64. package/dist/schema/schemas/agent-env.d.ts +20 -0
  65. package/dist/schema/schemas/agent-env.d.ts.map +1 -1
  66. package/dist/schema/schemas/agent-env.js +17 -2
  67. package/dist/schema/schemas/agent-env.js.map +1 -1
  68. package/dist/schema/schemas/agentcore-project.d.ts +17 -0
  69. package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
  70. package/dist/schema/schemas/agentcore-project.js +18 -1
  71. package/dist/schema/schemas/agentcore-project.js.map +1 -1
  72. package/dist/schema/schemas/deployed-state.d.ts +50 -0
  73. package/dist/schema/schemas/deployed-state.d.ts.map +1 -1
  74. package/dist/schema/schemas/deployed-state.js +15 -1
  75. package/dist/schema/schemas/deployed-state.js.map +1 -1
  76. package/dist/schema/schemas/primitives/evaluator.d.ts.map +1 -1
  77. package/dist/schema/schemas/primitives/evaluator.js +0 -1
  78. package/dist/schema/schemas/primitives/evaluator.js.map +1 -1
  79. package/dist/schema/schemas/primitives/harness.d.ts +287 -0
  80. package/dist/schema/schemas/primitives/harness.d.ts.map +1 -0
  81. package/dist/schema/schemas/primitives/harness.js +237 -0
  82. package/dist/schema/schemas/primitives/harness.js.map +1 -0
  83. package/dist/schema/schemas/primitives/index.d.ts +2 -0
  84. package/dist/schema/schemas/primitives/index.d.ts.map +1 -1
  85. package/dist/schema/schemas/primitives/index.js +14 -1
  86. package/dist/schema/schemas/primitives/index.js.map +1 -1
  87. package/package.json +4 -1
  88. package/scripts/bump-version.ts +7 -80
  89. package/scripts/bundle.mjs +57 -3
  90. package/scripts/copy-assets.mjs +14 -0
@@ -99,6 +99,40 @@ 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
+ tools?: { type: string; name: string }[];
114
+ apiKeyArn?: string;
115
+ }[] = [];
116
+ for (const entry of specAny.harnesses ?? []) {
117
+ const harnessPath = path.resolve(projectRoot, entry.path, 'harness.json');
118
+ try {
119
+ const harnessSpec = JSON.parse(fs.readFileSync(harnessPath, 'utf-8'));
120
+ harnessConfigs.push({
121
+ name: entry.name,
122
+ executionRoleArn: harnessSpec.executionRoleArn,
123
+ memoryName: harnessSpec.memory?.name,
124
+ containerUri: harnessSpec.containerUri,
125
+ hasDockerfile: !!harnessSpec.dockerfile,
126
+ tools: harnessSpec.tools,
127
+ apiKeyArn: harnessSpec.model?.apiKeyArn,
128
+ });
129
+ } catch (err) {
130
+ throw new Error(
131
+ \`Could not read harness.json for "\${entry.name}" at \${harnessPath}: \${err instanceof Error ? err.message : err}\`
132
+ );
133
+ }
134
+ }
135
+
102
136
  const app = new App();
103
137
 
104
138
  for (const target of targets) {
@@ -118,6 +152,7 @@ async function main() {
118
152
  spec,
119
153
  mcpSpec,
120
154
  credentials,
155
+ harnesses: harnessConfigs.length > 0 ? harnessConfigs : undefined,
121
156
  env,
122
157
  description: \`AgentCore stack for \${spec.name} deployed to \${target.name} (\${target.region})\`,
123
158
  tags: {
@@ -278,6 +313,18 @@ export interface AgentCoreStackProps extends StackProps {
278
313
  * Credential provider ARNs from deployed state, keyed by credential name.
279
314
  */
280
315
  credentials?: Record<string, { credentialProviderArn: string; clientSecretArn?: string }>;
316
+ /**
317
+ * Harness role configurations. Each entry creates an IAM execution role for a harness.
318
+ */
319
+ harnesses?: {
320
+ name: string;
321
+ executionRoleArn?: string;
322
+ memoryName?: string;
323
+ containerUri?: string;
324
+ hasDockerfile?: boolean;
325
+ tools?: { type: string; name: string }[];
326
+ apiKeyArn?: string;
327
+ }[];
281
328
  }
282
329
 
283
330
  /**
@@ -293,11 +340,12 @@ export class AgentCoreStack extends Stack {
293
340
  constructor(scope: Construct, id: string, props: AgentCoreStackProps) {
294
341
  super(scope, id, props);
295
342
 
296
- const { spec, mcpSpec, credentials } = props;
343
+ const { spec, mcpSpec, credentials, harnesses } = props;
297
344
 
298
- // Create AgentCoreApplication with all agents
345
+ // Create AgentCoreApplication with all agents and harness roles
299
346
  this.application = new AgentCoreApplication(this, 'Application', {
300
347
  spec,
348
+ harnesses,
301
349
  });
302
350
 
303
351
  // Create AgentCoreMcp if there are gateways configured
@@ -357,7 +405,7 @@ exports[`Assets Directory Snapshots > CDK assets > cdk/cdk/package.json should m
357
405
  "typescript": "~5.9.3"
358
406
  },
359
407
  "dependencies": {
360
- "@aws/agentcore-cdk": "0.1.0-alpha.19",
408
+ "@aws/agentcore-cdk": "^0.1.0-alpha.19",
361
409
  "aws-cdk-lib": "^2.248.0",
362
410
  "constructs": "^10.0.0"
363
411
  }
@@ -449,6 +497,7 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
449
497
  "evaluators/python-lambda/execution-role-policy.json",
450
498
  "evaluators/python-lambda/lambda_function.py",
451
499
  "evaluators/python-lambda/pyproject.toml",
500
+ "harness/invoke.py.template",
452
501
  "mcp/python-lambda/README.md",
453
502
  "mcp/python-lambda/handler.py",
454
503
  "mcp/python-lambda/pyproject.toml",
@@ -475,6 +524,26 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
475
524
  "python/a2a/strands/base/pyproject.toml",
476
525
  "python/a2a/strands/capabilities/memory/__init__.py",
477
526
  "python/a2a/strands/capabilities/memory/session.py",
527
+ "python/agui/googleadk/base/README.md",
528
+ "python/agui/googleadk/base/gitignore.template",
529
+ "python/agui/googleadk/base/main.py",
530
+ "python/agui/googleadk/base/model/__init__.py",
531
+ "python/agui/googleadk/base/model/load.py",
532
+ "python/agui/googleadk/base/pyproject.toml",
533
+ "python/agui/langchain_langgraph/base/README.md",
534
+ "python/agui/langchain_langgraph/base/gitignore.template",
535
+ "python/agui/langchain_langgraph/base/main.py",
536
+ "python/agui/langchain_langgraph/base/model/__init__.py",
537
+ "python/agui/langchain_langgraph/base/model/load.py",
538
+ "python/agui/langchain_langgraph/base/pyproject.toml",
539
+ "python/agui/strands/base/README.md",
540
+ "python/agui/strands/base/gitignore.template",
541
+ "python/agui/strands/base/main.py",
542
+ "python/agui/strands/base/model/__init__.py",
543
+ "python/agui/strands/base/model/load.py",
544
+ "python/agui/strands/base/pyproject.toml",
545
+ "python/agui/strands/capabilities/memory/__init__.py",
546
+ "python/agui/strands/capabilities/memory/session.py",
478
547
  "python/http/autogen/base/README.md",
479
548
  "python/http/autogen/base/gitignore.template",
480
549
  "python/http/autogen/base/main.py",
@@ -907,12 +976,1033 @@ This agent implements the A2A protocol using Google's Agent Development Kit, ena
907
976
 
908
977
  ## Local Development
909
978
 
910
- \`\`\`bash
911
- uv sync
912
- uv run python main.py
979
+ \`\`\`bash
980
+ uv sync
981
+ uv run python main.py
982
+ \`\`\`
983
+
984
+ The agent starts on port 9000.
985
+
986
+ ## Deploy
987
+
988
+ \`\`\`bash
989
+ agentcore deploy
990
+ \`\`\`
991
+ "
992
+ `;
993
+
994
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/gitignore.template should match snapshot 1`] = `
995
+ "# Environment variables
996
+ .env
997
+
998
+ # Python
999
+ __pycache__/
1000
+ *.py[cod]
1001
+ *$py.class
1002
+ *.so
1003
+ .Python
1004
+ build/
1005
+ develop-eggs/
1006
+ dist/
1007
+ downloads/
1008
+ eggs/
1009
+ .eggs/
1010
+ lib/
1011
+ lib64/
1012
+ parts/
1013
+ sdist/
1014
+ var/
1015
+ wheels/
1016
+ *.egg-info/
1017
+ .installed.cfg
1018
+ *.egg
1019
+
1020
+ # Virtual environments
1021
+ .venv/
1022
+ venv/
1023
+ ENV/
1024
+ env/
1025
+
1026
+ # IDE
1027
+ .vscode/
1028
+ .idea/
1029
+ *.swp
1030
+ *.swo
1031
+ *~
1032
+
1033
+ # OS
1034
+ .DS_Store
1035
+ Thumbs.db
1036
+ "
1037
+ `;
1038
+
1039
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/main.py should match snapshot 1`] = `
1040
+ "import os
1041
+ from google.adk.agents import Agent
1042
+ from google.adk.a2a.executor.a2a_agent_executor import A2aAgentExecutor
1043
+ from google.adk.runners import Runner
1044
+ from google.adk.sessions import InMemorySessionService
1045
+ from a2a.types import AgentCapabilities, AgentCard, AgentSkill
1046
+ from bedrock_agentcore.runtime import serve_a2a
1047
+ from model.load import load_model
1048
+
1049
+ load_model() # Sets GOOGLE_API_KEY env var (returns None)
1050
+
1051
+
1052
+ def add_numbers(a: int, b: int) -> int:
1053
+ """Return the sum of two numbers."""
1054
+ return a + b
1055
+
1056
+
1057
+ tools = [add_numbers]
1058
+
1059
+ {{#if sessionStorageMountPath}}
1060
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
1061
+
1062
+ def _safe_resolve(path: str) -> str:
1063
+ """Resolve path safely within the storage boundary."""
1064
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
1065
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
1066
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
1067
+ return resolved
1068
+
1069
+ def file_read(path: str) -> str:
1070
+ """Read a file from persistent storage. The path is relative to the storage root."""
1071
+ try:
1072
+ full_path = _safe_resolve(path)
1073
+ with open(full_path) as f:
1074
+ return f.read()
1075
+ except ValueError as e:
1076
+ return str(e)
1077
+ except OSError as e:
1078
+ return f"Error reading '{path}': {e.strerror}"
1079
+
1080
+ def file_write(path: str, content: str) -> str:
1081
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
1082
+ try:
1083
+ full_path = _safe_resolve(path)
1084
+ parent = os.path.dirname(full_path)
1085
+ if parent:
1086
+ os.makedirs(parent, exist_ok=True)
1087
+ with open(full_path, "w") as f:
1088
+ f.write(content)
1089
+ return f"Written to {path}"
1090
+ except ValueError as e:
1091
+ return str(e)
1092
+ except OSError as e:
1093
+ return f"Error writing '{path}': {e.strerror}"
1094
+
1095
+ def list_files(directory: str = "") -> str:
1096
+ """List files in persistent storage. The directory is relative to the storage root."""
1097
+ try:
1098
+ target = _safe_resolve(directory)
1099
+ entries = os.listdir(target)
1100
+ return "\\n".join(entries) if entries else "(empty directory)"
1101
+ except ValueError as e:
1102
+ return str(e)
1103
+ except OSError as e:
1104
+ return f"Error listing '{directory}': {e.strerror}"
1105
+
1106
+ tools.extend([file_read, file_write, list_files])
1107
+ {{/if}}
1108
+
1109
+ AGENT_INSTRUCTION = """
1110
+ You are a helpful assistant. Use tools when appropriate.
1111
+ {{#if sessionStorageMountPath}}
1112
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
1113
+ {{/if}}
1114
+ """
1115
+
1116
+ agent = Agent(
1117
+ model="gemini-2.5-flash",
1118
+ name="{{ name }}",
1119
+ description="A helpful assistant that can use tools.",
1120
+ instruction=AGENT_INSTRUCTION,
1121
+ tools=tools,
1122
+ )
1123
+
1124
+ runner = Runner(
1125
+ app_name=agent.name,
1126
+ agent=agent,
1127
+ session_service=InMemorySessionService(),
1128
+ )
1129
+
1130
+ card = AgentCard(
1131
+ name=agent.name,
1132
+ description=agent.description,
1133
+ url="http://localhost:9000/",
1134
+ version="0.1.0",
1135
+ capabilities=AgentCapabilities(streaming=True),
1136
+ skills=[
1137
+ AgentSkill(
1138
+ id="tools",
1139
+ name="tools",
1140
+ description="Use tools to help answer questions",
1141
+ tags=["tools"],
1142
+ )
1143
+ ],
1144
+ default_input_modes=["text"],
1145
+ default_output_modes=["text"],
1146
+ )
1147
+
1148
+ if __name__ == "__main__":
1149
+ serve_a2a(A2aAgentExecutor(runner=runner), card)
1150
+ "
1151
+ `;
1152
+
1153
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/model/__init__.py should match snapshot 1`] = `
1154
+ "# Package marker
1155
+ "
1156
+ `;
1157
+
1158
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/model/load.py should match snapshot 1`] = `
1159
+ "import os
1160
+ from bedrock_agentcore.identity.auth import requires_api_key
1161
+
1162
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1163
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1164
+
1165
+
1166
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1167
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1168
+ """Fetch API key from AgentCore Identity."""
1169
+ return api_key
1170
+
1171
+
1172
+ def _get_api_key() -> str:
1173
+ """
1174
+ Uses AgentCore Identity for API key management in deployed environments.
1175
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1176
+ """
1177
+ if os.getenv("LOCAL_DEV") == "1":
1178
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1179
+ if not api_key:
1180
+ raise RuntimeError(
1181
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1182
+ )
1183
+ return api_key
1184
+ return _agentcore_identity_api_key_provider()
1185
+
1186
+
1187
+ def load_model() -> None:
1188
+ """
1189
+ Set up Gemini API key authentication.
1190
+ Uses AgentCore Identity for API key management in deployed environments,
1191
+ and falls back to .env file for local development.
1192
+ Sets the GOOGLE_API_KEY environment variable for the Google ADK.
1193
+ """
1194
+ api_key = _get_api_key()
1195
+ # Use Google AI Studios API Key Authentication.
1196
+ # https://google.github.io/adk-docs/agents/models/#google-ai-studio
1197
+ os.environ["GOOGLE_API_KEY"] = api_key
1198
+ # Set to TRUE is using Google Vertex AI, Set to FALSE for Google AI Studio
1199
+ os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "FALSE"
1200
+ "
1201
+ `;
1202
+
1203
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/pyproject.toml should match snapshot 1`] = `
1204
+ "[build-system]
1205
+ requires = ["hatchling"]
1206
+ build-backend = "hatchling.build"
1207
+
1208
+ [project]
1209
+ name = "{{ name }}"
1210
+ version = "0.1.0"
1211
+ description = "AgentCore A2A Agent using Google ADK"
1212
+ readme = "README.md"
1213
+ requires-python = ">=3.10"
1214
+ dependencies = [
1215
+ "a2a-sdk >= 0.2.0",
1216
+ "aws-opentelemetry-distro",
1217
+ "bedrock-agentcore[a2a] >= 1.0.3",
1218
+ "google-adk >= 1.0.0",
1219
+ "google-genai >= 1.0.0",
1220
+ ]
1221
+
1222
+ [tool.hatch.build.targets.wheel]
1223
+ packages = ["."]
1224
+ "
1225
+ `;
1226
+
1227
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/README.md should match snapshot 1`] = `
1228
+ "# {{ name }}
1229
+
1230
+ An A2A (Agent-to-Agent) agent deployed on Amazon Bedrock AgentCore using LangChain + LangGraph.
1231
+
1232
+ ## Overview
1233
+
1234
+ This agent implements the A2A protocol using LangGraph, enabling agent-to-agent communication.
1235
+
1236
+ ## Local Development
1237
+
1238
+ \`\`\`bash
1239
+ uv sync
1240
+ uv run python main.py
1241
+ \`\`\`
1242
+
1243
+ The agent starts on port 9000.
1244
+
1245
+ ## Deploy
1246
+
1247
+ \`\`\`bash
1248
+ agentcore deploy
1249
+ \`\`\`
1250
+ "
1251
+ `;
1252
+
1253
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/gitignore.template should match snapshot 1`] = `
1254
+ "# Environment variables
1255
+ .env
1256
+
1257
+ # Python
1258
+ __pycache__/
1259
+ *.py[cod]
1260
+ *$py.class
1261
+ *.so
1262
+ .Python
1263
+ build/
1264
+ develop-eggs/
1265
+ dist/
1266
+ downloads/
1267
+ eggs/
1268
+ .eggs/
1269
+ lib/
1270
+ lib64/
1271
+ parts/
1272
+ sdist/
1273
+ var/
1274
+ wheels/
1275
+ *.egg-info/
1276
+ .installed.cfg
1277
+ *.egg
1278
+
1279
+ # Virtual environments
1280
+ .venv/
1281
+ venv/
1282
+ ENV/
1283
+ env/
1284
+
1285
+ # IDE
1286
+ .vscode/
1287
+ .idea/
1288
+ *.swp
1289
+ *.swo
1290
+ *~
1291
+
1292
+ # OS
1293
+ .DS_Store
1294
+ Thumbs.db
1295
+ "
1296
+ `;
1297
+
1298
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/main.py should match snapshot 1`] = `
1299
+ "import os
1300
+ from langchain_core.tools import tool
1301
+ from langgraph.prebuilt import create_react_agent
1302
+ from opentelemetry.instrumentation.langchain import LangchainInstrumentor
1303
+ from a2a.server.agent_execution import AgentExecutor, RequestContext
1304
+ from a2a.server.events import EventQueue
1305
+ from a2a.server.tasks import TaskUpdater
1306
+ from a2a.types import AgentCapabilities, AgentCard, AgentSkill, Part, TextPart
1307
+ from a2a.utils import new_task
1308
+ from bedrock_agentcore.runtime import serve_a2a
1309
+ from model.load import load_model
1310
+
1311
+ LangchainInstrumentor().instrument()
1312
+
1313
+
1314
+ @tool
1315
+ def add_numbers(a: int, b: int) -> int:
1316
+ """Return the sum of two numbers."""
1317
+ return a + b
1318
+
1319
+
1320
+ tools = [add_numbers]
1321
+
1322
+ {{#if sessionStorageMountPath}}
1323
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
1324
+
1325
+ def _safe_resolve(path: str) -> str:
1326
+ """Resolve path safely within the storage boundary."""
1327
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
1328
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
1329
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
1330
+ return resolved
1331
+
1332
+ @tool
1333
+ def file_read(path: str) -> str:
1334
+ """Read a file from persistent storage. The path is relative to the storage root."""
1335
+ try:
1336
+ full_path = _safe_resolve(path)
1337
+ with open(full_path) as f:
1338
+ return f.read()
1339
+ except ValueError as e:
1340
+ return str(e)
1341
+ except OSError as e:
1342
+ return f"Error reading '{path}': {e.strerror}"
1343
+
1344
+ @tool
1345
+ def file_write(path: str, content: str) -> str:
1346
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
1347
+ try:
1348
+ full_path = _safe_resolve(path)
1349
+ parent = os.path.dirname(full_path)
1350
+ if parent:
1351
+ os.makedirs(parent, exist_ok=True)
1352
+ with open(full_path, "w") as f:
1353
+ f.write(content)
1354
+ return f"Written to {path}"
1355
+ except ValueError as e:
1356
+ return str(e)
1357
+ except OSError as e:
1358
+ return f"Error writing '{path}': {e.strerror}"
1359
+
1360
+ @tool
1361
+ def list_files(directory: str = "") -> str:
1362
+ """List files in persistent storage. The directory is relative to the storage root."""
1363
+ try:
1364
+ target = _safe_resolve(directory)
1365
+ entries = os.listdir(target)
1366
+ return "\\n".join(entries) if entries else "(empty directory)"
1367
+ except ValueError as e:
1368
+ return str(e)
1369
+ except OSError as e:
1370
+ return f"Error listing '{directory}': {e.strerror}"
1371
+
1372
+ tools.extend([file_read, file_write, list_files])
1373
+ {{/if}}
1374
+
1375
+ SYSTEM_PROMPT = """
1376
+ You are a helpful assistant. Use tools when appropriate.
1377
+ {{#if sessionStorageMountPath}}
1378
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
1379
+ {{/if}}
1380
+ """
1381
+
1382
+ model = load_model()
1383
+ graph = create_react_agent(model, tools=tools, prompt=SYSTEM_PROMPT)
1384
+
1385
+
1386
+ class LangGraphA2AExecutor(AgentExecutor):
1387
+ """Wraps a LangGraph CompiledGraph as an a2a-sdk AgentExecutor."""
1388
+
1389
+ def __init__(self, graph):
1390
+ self.graph = graph
1391
+
1392
+ async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
1393
+ task = context.current_task or new_task(context.message)
1394
+ if not context.current_task:
1395
+ await event_queue.enqueue_event(task)
1396
+ updater = TaskUpdater(event_queue, task.id, task.context_id)
1397
+
1398
+ user_text = context.get_user_input()
1399
+ result = await self.graph.ainvoke({"messages": [("user", user_text)]})
1400
+ response = result["messages"][-1].content
1401
+
1402
+ await updater.add_artifact([Part(root=TextPart(text=response))])
1403
+ await updater.complete()
1404
+
1405
+ async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
1406
+ pass
1407
+
1408
+
1409
+ card = AgentCard(
1410
+ name="{{ name }}",
1411
+ description="A LangGraph agent on Bedrock AgentCore",
1412
+ url="http://localhost:9000/",
1413
+ version="0.1.0",
1414
+ capabilities=AgentCapabilities(streaming=True),
1415
+ skills=[
1416
+ AgentSkill(
1417
+ id="tools",
1418
+ name="tools",
1419
+ description="Use tools to help answer questions",
1420
+ tags=["tools"],
1421
+ )
1422
+ ],
1423
+ default_input_modes=["text"],
1424
+ default_output_modes=["text"],
1425
+ )
1426
+
1427
+ if __name__ == "__main__":
1428
+ serve_a2a(LangGraphA2AExecutor(graph), card)
1429
+ "
1430
+ `;
1431
+
1432
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/model/__init__.py should match snapshot 1`] = `
1433
+ "# Package marker
1434
+ "
1435
+ `;
1436
+
1437
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/model/load.py should match snapshot 1`] = `
1438
+ "{{#if (eq modelProvider "Bedrock")}}
1439
+ from langchain_aws import ChatBedrock
1440
+
1441
+ # Uses global inference profile for Claude Sonnet 4.5
1442
+ # https://docs.aws.amazon.com/bedrock/latest/userguide/inference-profiles-support.html
1443
+ MODEL_ID = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
1444
+
1445
+
1446
+ def load_model() -> ChatBedrock:
1447
+ """Get Bedrock model client using IAM credentials."""
1448
+ return ChatBedrock(model_id=MODEL_ID)
1449
+ {{/if}}
1450
+ {{#if (eq modelProvider "Anthropic")}}
1451
+ import os
1452
+ from langchain_anthropic import ChatAnthropic
1453
+ from bedrock_agentcore.identity.auth import requires_api_key
1454
+
1455
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1456
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1457
+
1458
+
1459
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1460
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1461
+ """Fetch API key from AgentCore Identity."""
1462
+ return api_key
1463
+
1464
+
1465
+ def _get_api_key() -> str:
1466
+ """
1467
+ Uses AgentCore Identity for API key management in deployed environments.
1468
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1469
+ """
1470
+ if os.getenv("LOCAL_DEV") == "1":
1471
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1472
+ if not api_key:
1473
+ raise RuntimeError(
1474
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1475
+ )
1476
+ return api_key
1477
+ return _agentcore_identity_api_key_provider()
1478
+
1479
+
1480
+ def load_model() -> ChatAnthropic:
1481
+ """Get authenticated Anthropic model client."""
1482
+ return ChatAnthropic(
1483
+ model="claude-sonnet-4-5-20250929",
1484
+ api_key=_get_api_key()
1485
+ )
1486
+ {{/if}}
1487
+ {{#if (eq modelProvider "OpenAI")}}
1488
+ import os
1489
+ from langchain_openai import ChatOpenAI
1490
+ from bedrock_agentcore.identity.auth import requires_api_key
1491
+
1492
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1493
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1494
+
1495
+
1496
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1497
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1498
+ """Fetch API key from AgentCore Identity."""
1499
+ return api_key
1500
+
1501
+
1502
+ def _get_api_key() -> str:
1503
+ """
1504
+ Uses AgentCore Identity for API key management in deployed environments.
1505
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1506
+ """
1507
+ if os.getenv("LOCAL_DEV") == "1":
1508
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1509
+ if not api_key:
1510
+ raise RuntimeError(
1511
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1512
+ )
1513
+ return api_key
1514
+ return _agentcore_identity_api_key_provider()
1515
+
1516
+
1517
+ def load_model() -> ChatOpenAI:
1518
+ """Get authenticated OpenAI model client."""
1519
+ return ChatOpenAI(
1520
+ model="gpt-4.1",
1521
+ api_key=_get_api_key()
1522
+ )
1523
+ {{/if}}
1524
+ {{#if (eq modelProvider "Gemini")}}
1525
+ import os
1526
+ from langchain_google_genai import ChatGoogleGenerativeAI
1527
+ from bedrock_agentcore.identity.auth import requires_api_key
1528
+
1529
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1530
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1531
+
1532
+
1533
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1534
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1535
+ """Fetch API key from AgentCore Identity."""
1536
+ return api_key
1537
+
1538
+
1539
+ def _get_api_key() -> str:
1540
+ """
1541
+ Uses AgentCore Identity for API key management in deployed environments.
1542
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1543
+ """
1544
+ if os.getenv("LOCAL_DEV") == "1":
1545
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1546
+ if not api_key:
1547
+ raise RuntimeError(
1548
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1549
+ )
1550
+ return api_key
1551
+ return _agentcore_identity_api_key_provider()
1552
+
1553
+
1554
+ def load_model() -> ChatGoogleGenerativeAI:
1555
+ """Get authenticated Gemini model client."""
1556
+ return ChatGoogleGenerativeAI(
1557
+ model="gemini-2.5-flash",
1558
+ api_key=_get_api_key()
1559
+ )
1560
+ {{/if}}
1561
+ "
1562
+ `;
1563
+
1564
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/pyproject.toml should match snapshot 1`] = `
1565
+ "[build-system]
1566
+ requires = ["hatchling"]
1567
+ build-backend = "hatchling.build"
1568
+
1569
+ [project]
1570
+ name = "{{ name }}"
1571
+ version = "0.1.0"
1572
+ description = "AgentCore A2A Agent using LangChain + LangGraph"
1573
+ readme = "README.md"
1574
+ requires-python = ">=3.10"
1575
+ dependencies = [
1576
+ "a2a-sdk >= 0.2.0",
1577
+ {{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
1578
+ {{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
1579
+ {{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
1580
+ {{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
1581
+ {{/if}}"aws-opentelemetry-distro",
1582
+ "opentelemetry-instrumentation-langchain >= 0.59.0",
1583
+ "bedrock-agentcore[a2a] >= 1.0.3",
1584
+ "botocore[crt] >= 1.35.0",
1585
+ "langgraph >= 0.2.0",
1586
+ ]
1587
+
1588
+ [tool.hatch.build.targets.wheel]
1589
+ packages = ["."]
1590
+ "
1591
+ `;
1592
+
1593
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/README.md should match snapshot 1`] = `
1594
+ "# {{ name }}
1595
+
1596
+ An A2A (Agent-to-Agent) agent deployed on Amazon Bedrock AgentCore using Strands SDK.
1597
+
1598
+ ## Overview
1599
+
1600
+ 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.
1601
+
1602
+ ## Local Development
1603
+
1604
+ \`\`\`bash
1605
+ uv sync
1606
+ uv run python main.py
1607
+ \`\`\`
1608
+
1609
+ The agent starts on port 9000.
1610
+
1611
+ ## Deploy
1612
+
1613
+ \`\`\`bash
1614
+ agentcore deploy
1615
+ \`\`\`
1616
+ "
1617
+ `;
1618
+
1619
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/gitignore.template should match snapshot 1`] = `
1620
+ "# Environment variables
1621
+ .env
1622
+
1623
+ # Python
1624
+ __pycache__/
1625
+ *.py[cod]
1626
+ *$py.class
1627
+ *.so
1628
+ .Python
1629
+ build/
1630
+ develop-eggs/
1631
+ dist/
1632
+ downloads/
1633
+ eggs/
1634
+ .eggs/
1635
+ lib/
1636
+ lib64/
1637
+ parts/
1638
+ sdist/
1639
+ var/
1640
+ wheels/
1641
+ *.egg-info/
1642
+ .installed.cfg
1643
+ *.egg
1644
+
1645
+ # Virtual environments
1646
+ .venv/
1647
+ venv/
1648
+ ENV/
1649
+ env/
1650
+
1651
+ # IDE
1652
+ .vscode/
1653
+ .idea/
1654
+ *.swp
1655
+ *.swo
1656
+ *~
1657
+
1658
+ # OS
1659
+ .DS_Store
1660
+ Thumbs.db
1661
+ "
1662
+ `;
1663
+
1664
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/main.py should match snapshot 1`] = `
1665
+ "from strands import Agent, tool
1666
+ from strands.multiagent.a2a.executor import StrandsA2AExecutor
1667
+ from bedrock_agentcore.runtime import serve_a2a
1668
+ from model.load import load_model
1669
+ {{#if hasMemory}}
1670
+ from memory.session import get_memory_session_manager
1671
+ {{/if}}
1672
+ {{#if sessionStorageMountPath}}
1673
+ import os
1674
+ {{/if}}
1675
+
1676
+
1677
+ @tool
1678
+ def add_numbers(a: int, b: int) -> int:
1679
+ """Return the sum of two numbers."""
1680
+ return a + b
1681
+
1682
+
1683
+ tools = [add_numbers]
1684
+
1685
+ {{#if sessionStorageMountPath}}
1686
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
1687
+
1688
+ def _safe_resolve(path: str) -> str:
1689
+ """Resolve path safely within the storage boundary."""
1690
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
1691
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
1692
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
1693
+ return resolved
1694
+
1695
+ @tool
1696
+ def file_read(path: str) -> str:
1697
+ """Read a file from persistent storage. The path is relative to the storage root."""
1698
+ try:
1699
+ full_path = _safe_resolve(path)
1700
+ with open(full_path) as f:
1701
+ return f.read()
1702
+ except ValueError as e:
1703
+ return str(e)
1704
+ except OSError as e:
1705
+ return f"Error reading '{path}': {e.strerror}"
1706
+
1707
+ @tool
1708
+ def file_write(path: str, content: str) -> str:
1709
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
1710
+ try:
1711
+ full_path = _safe_resolve(path)
1712
+ parent = os.path.dirname(full_path)
1713
+ if parent:
1714
+ os.makedirs(parent, exist_ok=True)
1715
+ with open(full_path, "w") as f:
1716
+ f.write(content)
1717
+ return f"Written to {path}"
1718
+ except ValueError as e:
1719
+ return str(e)
1720
+ except OSError as e:
1721
+ return f"Error writing '{path}': {e.strerror}"
1722
+
1723
+ @tool
1724
+ def list_files(directory: str = "") -> str:
1725
+ """List files in persistent storage. The directory is relative to the storage root."""
1726
+ try:
1727
+ target = _safe_resolve(directory)
1728
+ entries = os.listdir(target)
1729
+ return "\\n".join(entries) if entries else "(empty directory)"
1730
+ except ValueError as e:
1731
+ return str(e)
1732
+ except OSError as e:
1733
+ return f"Error listing '{directory}': {e.strerror}"
1734
+
1735
+ tools.extend([file_read, file_write, list_files])
1736
+ {{/if}}
1737
+
1738
+ SYSTEM_PROMPT = """
1739
+ You are a helpful assistant. Use tools when appropriate.
1740
+ {{#if sessionStorageMountPath}}
1741
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
1742
+ {{/if}}
1743
+ """
1744
+
1745
+ {{#if hasMemory}}
1746
+ def agent_factory():
1747
+ cache = {}
1748
+ def get_or_create_agent(session_id, user_id):
1749
+ key = f"{session_id}/{user_id}"
1750
+ if key not in cache:
1751
+ cache[key] = Agent(
1752
+ model=load_model(),
1753
+ session_manager=get_memory_session_manager(session_id, user_id),
1754
+ system_prompt=SYSTEM_PROMPT,
1755
+ tools=tools,
1756
+ )
1757
+ return cache[key]
1758
+ return get_or_create_agent
1759
+
1760
+ get_or_create_agent = agent_factory()
1761
+ agent = get_or_create_agent("default-session", "default-user")
1762
+ {{else}}
1763
+ agent = Agent(
1764
+ model=load_model(),
1765
+ system_prompt=SYSTEM_PROMPT,
1766
+ tools=tools,
1767
+ )
1768
+ {{/if}}
1769
+
1770
+ if __name__ == "__main__":
1771
+ serve_a2a(StrandsA2AExecutor(agent))
1772
+ "
1773
+ `;
1774
+
1775
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/model/__init__.py should match snapshot 1`] = `
1776
+ "# Package marker
1777
+ "
1778
+ `;
1779
+
1780
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/model/load.py should match snapshot 1`] = `
1781
+ "{{#if (eq modelProvider "Bedrock")}}
1782
+ from strands.models.bedrock import BedrockModel
1783
+
1784
+
1785
+ def load_model() -> BedrockModel:
1786
+ """Get Bedrock model client using IAM credentials."""
1787
+ return BedrockModel(model_id="global.anthropic.claude-sonnet-4-5-20250929-v1:0")
1788
+ {{/if}}
1789
+ {{#if (eq modelProvider "Anthropic")}}
1790
+ import os
1791
+
1792
+ from strands.models.anthropic import AnthropicModel
1793
+ from bedrock_agentcore.identity.auth import requires_api_key
1794
+
1795
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1796
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1797
+
1798
+
1799
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1800
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1801
+ """Fetch API key from AgentCore Identity."""
1802
+ return api_key
1803
+
1804
+
1805
+ def _get_api_key() -> str:
1806
+ """
1807
+ Uses AgentCore Identity for API key management in deployed environments.
1808
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1809
+ """
1810
+ if os.getenv("LOCAL_DEV") == "1":
1811
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1812
+ if not api_key:
1813
+ raise RuntimeError(
1814
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1815
+ )
1816
+ return api_key
1817
+ return _agentcore_identity_api_key_provider()
1818
+
1819
+
1820
+ def load_model() -> AnthropicModel:
1821
+ """Get authenticated Anthropic model client."""
1822
+ return AnthropicModel(
1823
+ client_args={"api_key": _get_api_key()},
1824
+ model_id="claude-sonnet-4-5-20250929",
1825
+ max_tokens=5000,
1826
+ )
1827
+ {{/if}}
1828
+ {{#if (eq modelProvider "OpenAI")}}
1829
+ import os
1830
+
1831
+ from strands.models.openai import OpenAIModel
1832
+ from bedrock_agentcore.identity.auth import requires_api_key
1833
+
1834
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1835
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1836
+
1837
+
1838
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1839
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1840
+ """Fetch API key from AgentCore Identity."""
1841
+ return api_key
1842
+
1843
+
1844
+ def _get_api_key() -> str:
1845
+ """
1846
+ Uses AgentCore Identity for API key management in deployed environments.
1847
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1848
+ """
1849
+ if os.getenv("LOCAL_DEV") == "1":
1850
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1851
+ if not api_key:
1852
+ raise RuntimeError(
1853
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1854
+ )
1855
+ return api_key
1856
+ return _agentcore_identity_api_key_provider()
1857
+
1858
+
1859
+ def load_model() -> OpenAIModel:
1860
+ """Get authenticated OpenAI model client."""
1861
+ return OpenAIModel(
1862
+ client_args={"api_key": _get_api_key()},
1863
+ model_id="gpt-4.1",
1864
+ )
1865
+ {{/if}}
1866
+ {{#if (eq modelProvider "Gemini")}}
1867
+ import os
1868
+
1869
+ from strands.models.gemini import GeminiModel
1870
+ from bedrock_agentcore.identity.auth import requires_api_key
1871
+
1872
+ IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
1873
+ IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
1874
+
1875
+
1876
+ @requires_api_key(provider_name=IDENTITY_PROVIDER_NAME)
1877
+ def _agentcore_identity_api_key_provider(api_key: str) -> str:
1878
+ """Fetch API key from AgentCore Identity."""
1879
+ return api_key
1880
+
1881
+
1882
+ def _get_api_key() -> str:
1883
+ """
1884
+ Uses AgentCore Identity for API key management in deployed environments.
1885
+ For local development, run via 'agentcore dev' which loads agentcore/.env.
1886
+ """
1887
+ if os.getenv("LOCAL_DEV") == "1":
1888
+ api_key = os.getenv(IDENTITY_ENV_VAR)
1889
+ if not api_key:
1890
+ raise RuntimeError(
1891
+ f"{IDENTITY_ENV_VAR} not found. Add {IDENTITY_ENV_VAR}=your-key to .env.local"
1892
+ )
1893
+ return api_key
1894
+ return _agentcore_identity_api_key_provider()
1895
+
1896
+
1897
+ def load_model() -> GeminiModel:
1898
+ """Get authenticated Gemini model client."""
1899
+ return GeminiModel(
1900
+ client_args={"api_key": _get_api_key()},
1901
+ model_id="gemini-2.5-flash",
1902
+ )
1903
+ {{/if}}
1904
+ "
1905
+ `;
1906
+
1907
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/pyproject.toml should match snapshot 1`] = `
1908
+ "[build-system]
1909
+ requires = ["hatchling"]
1910
+ build-backend = "hatchling.build"
1911
+
1912
+ [project]
1913
+ name = "{{ name }}"
1914
+ version = "0.1.0"
1915
+ description = "AgentCore A2A Agent using Strands SDK"
1916
+ readme = "README.md"
1917
+ requires-python = ">=3.10"
1918
+ dependencies = [
1919
+ {{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
1920
+ {{/if}}"a2a-sdk[all] >= 0.2.0",
1921
+ "aws-opentelemetry-distro",
1922
+ "bedrock-agentcore[a2a] >= 1.0.3",
1923
+ "botocore[crt] >= 1.35.0",
1924
+ {{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
1925
+ {{/if}}{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
1926
+ {{/if}}"strands-agents >= 1.13.0",
1927
+ ]
1928
+
1929
+ [tool.hatch.build.targets.wheel]
1930
+ packages = ["."]
1931
+ "
1932
+ `;
1933
+
1934
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/__init__.py should match snapshot 1`] = `
1935
+ "# Package marker
1936
+ "
1937
+ `;
1938
+
1939
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/session.py should match snapshot 1`] = `
1940
+ "import os
1941
+ from typing import Optional
1942
+
1943
+ from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig{{#if memoryProviders.[0].strategies.length}}, RetrievalConfig{{/if}}
1944
+ from bedrock_agentcore.memory.integrations.strands.session_manager import AgentCoreMemorySessionManager
1945
+
1946
+ MEMORY_ID = os.getenv("{{memoryProviders.[0].envVarName}}")
1947
+ REGION = os.getenv("AWS_REGION")
1948
+
1949
+ def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
1950
+ if not MEMORY_ID:
1951
+ return None
1952
+
1953
+ {{#if memoryProviders.[0].strategies.length}}
1954
+ retrieval_config = {
1955
+ {{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}
1956
+ f"/users/{actor_id}/facts": RetrievalConfig(top_k=3, relevance_score=0.5),
1957
+ {{/if}}
1958
+ {{#if (includes memoryProviders.[0].strategies "USER_PREFERENCE")}}
1959
+ f"/users/{actor_id}/preferences": RetrievalConfig(top_k=3, relevance_score=0.5),
1960
+ {{/if}}
1961
+ {{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
1962
+ f"/summaries/{actor_id}/{session_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
1963
+ {{/if}}
1964
+ }
1965
+ {{/if}}
1966
+
1967
+ return AgentCoreMemorySessionManager(
1968
+ AgentCoreMemoryConfig(
1969
+ memory_id=MEMORY_ID,
1970
+ session_id=session_id,
1971
+ actor_id=actor_id,
1972
+ {{#if memoryProviders.[0].strategies.length}}
1973
+ retrieval_config=retrieval_config,
1974
+ {{/if}}
1975
+ ),
1976
+ REGION
1977
+ )
1978
+ "
1979
+ `;
1980
+
1981
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/README.md should match snapshot 1`] = `
1982
+ "# {{ name }}
1983
+
1984
+ An AG-UI agent deployed on Amazon Bedrock AgentCore using Google ADK.
1985
+
1986
+ ## Overview
1987
+
1988
+ This agent implements the AG-UI protocol using Google's Agent Development Kit, enabling rich agent-user interaction via the AG-UI event stream.
1989
+
1990
+ ## Local Development
1991
+
1992
+ \`\`\`bash
1993
+ uv sync
1994
+ uv run python main.py
1995
+ \`\`\`
1996
+
1997
+ The agent starts on port 8080 and serves requests at \`/invocations\`.
1998
+
1999
+ ## Health Check
2000
+
2001
+ \`\`\`
2002
+ GET /ping
913
2003
  \`\`\`
914
2004
 
915
- The agent starts on port 9000.
2005
+ Returns \`{"status": "healthy"}\`.
916
2006
 
917
2007
  ## Deploy
918
2008
 
@@ -922,7 +2012,7 @@ agentcore deploy
922
2012
  "
923
2013
  `;
924
2014
 
925
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/gitignore.template should match snapshot 1`] = `
2015
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/gitignore.template should match snapshot 1`] = `
926
2016
  "# Environment variables
927
2017
  .env
928
2018
 
@@ -967,64 +2057,47 @@ Thumbs.db
967
2057
  "
968
2058
  `;
969
2059
 
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
2060
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/main.py should match snapshot 1`] = `
2061
+ "import os
2062
+ import uvicorn
2063
+ from google.adk.agents import LlmAgent
2064
+ from ag_ui_adk import ADKAgent, AGUIToolset, create_adk_app
977
2065
  from model.load import load_model
978
2066
 
2067
+ load_model()
979
2068
 
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(),
2069
+ agent = LlmAgent(
987
2070
  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],
2071
+ model="gemini-2.5-flash",
2072
+ instruction="You are a helpful assistant.",
2073
+ tools=[AGUIToolset()],
991
2074
  )
992
2075
 
993
- runner = Runner(
994
- app_name=agent.name,
995
- agent=agent,
996
- session_service=InMemorySessionService(),
2076
+ adk_agent = ADKAgent(
2077
+ adk_agent=agent,
2078
+ app_name="{{ name }}",
2079
+ use_in_memory_services=True,
997
2080
  )
998
2081
 
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
- )
2082
+ app = create_adk_app(adk_agent, path="/invocations")
2083
+
2084
+
2085
+ @app.get("/ping")
2086
+ async def ping():
2087
+ return {"status": "healthy"}
2088
+
1016
2089
 
1017
2090
  if __name__ == "__main__":
1018
- serve_a2a(A2aAgentExecutor(runner=runner), card)
2091
+ uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))
1019
2092
  "
1020
2093
  `;
1021
2094
 
1022
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/model/__init__.py should match snapshot 1`] = `
2095
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/model/__init__.py should match snapshot 1`] = `
1023
2096
  "# Package marker
1024
2097
  "
1025
2098
  `;
1026
2099
 
1027
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/model/load.py should match snapshot 1`] = `
2100
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/model/load.py should match snapshot 1`] = `
1028
2101
  "import os
1029
2102
  from bedrock_agentcore.identity.auth import requires_api_key
1030
2103
 
@@ -1069,7 +2142,7 @@ def load_model() -> None:
1069
2142
  "
1070
2143
  `;
1071
2144
 
1072
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/googleadk/base/pyproject.toml should match snapshot 1`] = `
2145
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/googleadk/base/pyproject.toml should match snapshot 1`] = `
1073
2146
  "[build-system]
1074
2147
  requires = ["hatchling"]
1075
2148
  build-backend = "hatchling.build"
@@ -1077,15 +2150,19 @@ build-backend = "hatchling.build"
1077
2150
  [project]
1078
2151
  name = "{{ name }}"
1079
2152
  version = "0.1.0"
1080
- description = "AgentCore A2A Agent using Google ADK"
2153
+ description = "AgentCore AG-UI Agent using Google ADK"
1081
2154
  readme = "README.md"
1082
2155
  requires-python = ">=3.10"
1083
2156
  dependencies = [
1084
- "a2a-sdk >= 0.2.0",
1085
- "aws-opentelemetry-distro",
1086
- "bedrock-agentcore[a2a] >= 1.0.3",
1087
- "google-adk >= 1.0.0",
2157
+ "ag-ui-adk >= 0.6.0",
2158
+ "ag-ui-protocol >= 0.1.10",
2159
+ "bedrock-agentcore >= 1.0.3",
2160
+ "fastapi >= 0.115.12",
2161
+ "google-adk >= 1.16.0",
1088
2162
  "google-genai >= 1.0.0",
2163
+ "opentelemetry-distro",
2164
+ "opentelemetry-exporter-otlp",
2165
+ "uvicorn >= 0.34.3",
1089
2166
  ]
1090
2167
 
1091
2168
  [tool.hatch.build.targets.wheel]
@@ -1093,14 +2170,14 @@ packages = ["."]
1093
2170
  "
1094
2171
  `;
1095
2172
 
1096
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/README.md should match snapshot 1`] = `
2173
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/README.md should match snapshot 1`] = `
1097
2174
  "# {{ name }}
1098
2175
 
1099
- An A2A (Agent-to-Agent) agent deployed on Amazon Bedrock AgentCore using LangChain + LangGraph.
2176
+ An AG-UI agent deployed on Amazon Bedrock AgentCore using LangChain + LangGraph.
1100
2177
 
1101
2178
  ## Overview
1102
2179
 
1103
- This agent implements the A2A protocol using LangGraph, enabling agent-to-agent communication.
2180
+ 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
2181
 
1105
2182
  ## Local Development
1106
2183
 
@@ -1109,7 +2186,7 @@ uv sync
1109
2186
  uv run python main.py
1110
2187
  \`\`\`
1111
2188
 
1112
- The agent starts on port 9000.
2189
+ The agent starts on port 8080.
1113
2190
 
1114
2191
  ## Deploy
1115
2192
 
@@ -1119,7 +2196,7 @@ agentcore deploy
1119
2196
  "
1120
2197
  `;
1121
2198
 
1122
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/gitignore.template should match snapshot 1`] = `
2199
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/gitignore.template should match snapshot 1`] = `
1123
2200
  "# Environment variables
1124
2201
  .env
1125
2202
 
@@ -1164,16 +2241,22 @@ Thumbs.db
1164
2241
  "
1165
2242
  `;
1166
2243
 
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
2244
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/main.py should match snapshot 1`] = `
2245
+ "import os
2246
+
2247
+ os.environ["LANGGRAPH_FAST_API"] = "true"
2248
+
2249
+ import uvicorn
2250
+ from typing import Any, List
2251
+ from fastapi import FastAPI
2252
+ from fastapi.middleware.cors import CORSMiddleware
2253
+ from langgraph.graph import StateGraph, START
2254
+ from langgraph.graph.message import MessagesState
2255
+ from langgraph.checkpoint.memory import MemorySaver
2256
+ from langgraph.prebuilt import ToolNode, tools_condition
2257
+ from langchain_core.tools import tool
1170
2258
  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
2259
+ from ag_ui_langgraph import LangGraphAgent, add_langgraph_fastapi_endpoint
1177
2260
  from model.load import load_model
1178
2261
 
1179
2262
  LangchainInstrumentor().instrument()
@@ -1185,62 +2268,63 @@ def add_numbers(a: int, b: int) -> int:
1185
2268
  return a + b
1186
2269
 
1187
2270
 
2271
+ backend_tools = [add_numbers]
1188
2272
  model = load_model()
1189
- graph = create_react_agent(model, tools=[add_numbers])
1190
2273
 
1191
2274
 
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)
2275
+ class AgentState(MessagesState):
2276
+ tools: List[Any]
1203
2277
 
1204
- user_text = context.get_user_input()
1205
- result = await self.graph.ainvoke({"messages": [("user", user_text)]})
1206
- response = result["messages"][-1].content
1207
2278
 
1208
- await updater.add_artifact([Part(root=TextPart(text=response))])
1209
- await updater.complete()
2279
+ def chat_node(state: AgentState):
2280
+ bound_model = model.bind_tools(
2281
+ [*state.get("tools", []), *backend_tools],
2282
+ )
2283
+ response = bound_model.invoke(state["messages"])
2284
+ return {"messages": [response]}
1210
2285
 
1211
- async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
1212
- pass
1213
2286
 
2287
+ builder = StateGraph(AgentState)
2288
+ builder.add_node("chat", chat_node)
2289
+ builder.add_node("tools", ToolNode(tools=backend_tools))
2290
+ builder.add_edge(START, "chat")
2291
+ builder.add_conditional_edges("chat", tools_condition)
2292
+ builder.add_edge("tools", "chat")
2293
+ graph = builder.compile(checkpointer=MemorySaver())
1214
2294
 
1215
- card = AgentCard(
2295
+ agent = LangGraphAgent(
1216
2296
  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"],
2297
+ graph=graph,
2298
+ description="A helpful assistant",
2299
+ )
2300
+
2301
+ app = FastAPI()
2302
+ app.add_middleware(
2303
+ CORSMiddleware,
2304
+ allow_origins=["*"],
2305
+ allow_methods=["*"],
2306
+ allow_headers=["*"],
1231
2307
  )
1232
2308
 
2309
+ add_langgraph_fastapi_endpoint(app=app, agent=agent, path="/invocations")
2310
+
2311
+
2312
+ @app.get("/ping")
2313
+ async def ping():
2314
+ return {"status": "healthy"}
2315
+
2316
+
1233
2317
  if __name__ == "__main__":
1234
- serve_a2a(LangGraphA2AExecutor(graph), card)
2318
+ uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))
1235
2319
  "
1236
2320
  `;
1237
2321
 
1238
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/model/__init__.py should match snapshot 1`] = `
2322
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/model/__init__.py should match snapshot 1`] = `
1239
2323
  "# Package marker
1240
2324
  "
1241
2325
  `;
1242
2326
 
1243
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/model/load.py should match snapshot 1`] = `
2327
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/model/load.py should match snapshot 1`] = `
1244
2328
  "{{#if (eq modelProvider "Bedrock")}}
1245
2329
  from langchain_aws import ChatBedrock
1246
2330
 
@@ -1367,7 +2451,7 @@ def load_model() -> ChatGoogleGenerativeAI:
1367
2451
  "
1368
2452
  `;
1369
2453
 
1370
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/langchain_langgraph/base/pyproject.toml should match snapshot 1`] = `
2454
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/langchain_langgraph/base/pyproject.toml should match snapshot 1`] = `
1371
2455
  "[build-system]
1372
2456
  requires = ["hatchling"]
1373
2457
  build-backend = "hatchling.build"
@@ -1375,20 +2459,25 @@ build-backend = "hatchling.build"
1375
2459
  [project]
1376
2460
  name = "{{ name }}"
1377
2461
  version = "0.1.0"
1378
- description = "AgentCore A2A Agent using LangChain + LangGraph"
2462
+ description = "AgentCore AG-UI Agent using LangChain + LangGraph"
1379
2463
  readme = "README.md"
1380
2464
  requires-python = ">=3.10"
1381
2465
  dependencies = [
1382
- "a2a-sdk >= 0.2.0",
2466
+ "ag-ui-langgraph >= 0.0.31",
2467
+ "ag-ui-protocol >= 0.1.10",
1383
2468
  {{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
1384
2469
  {{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
1385
2470
  {{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
1386
2471
  {{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
1387
2472
  {{/if}}"aws-opentelemetry-distro",
1388
2473
  "opentelemetry-instrumentation-langchain >= 0.59.0",
1389
- "bedrock-agentcore[a2a] >= 1.0.3",
2474
+ "bedrock-agentcore >= 1.0.3",
1390
2475
  "botocore[crt] >= 1.35.0",
1391
- "langgraph >= 0.2.0",
2476
+ "langgraph >= 0.3.25",
2477
+ "langchain >= 0.3.0",
2478
+ "langchain-core >= 0.3.0",
2479
+ "fastapi >= 0.115.12",
2480
+ "uvicorn >= 0.34.3",
1392
2481
  ]
1393
2482
 
1394
2483
  [tool.hatch.build.targets.wheel]
@@ -1396,14 +2485,14 @@ packages = ["."]
1396
2485
  "
1397
2486
  `;
1398
2487
 
1399
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/README.md should match snapshot 1`] = `
2488
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/README.md should match snapshot 1`] = `
1400
2489
  "# {{ name }}
1401
2490
 
1402
- An A2A (Agent-to-Agent) agent deployed on Amazon Bedrock AgentCore using Strands SDK.
2491
+ An AG-UI agent deployed on Amazon Bedrock AgentCore using Strands SDK.
1403
2492
 
1404
2493
  ## Overview
1405
2494
 
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.
2495
+ 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
2496
 
1408
2497
  ## Local Development
1409
2498
 
@@ -1412,7 +2501,7 @@ uv sync
1412
2501
  uv run python main.py
1413
2502
  \`\`\`
1414
2503
 
1415
- The agent starts on port 9000.
2504
+ The agent starts on port 8080.
1416
2505
 
1417
2506
  ## Deploy
1418
2507
 
@@ -1422,7 +2511,7 @@ agentcore deploy
1422
2511
  "
1423
2512
  `;
1424
2513
 
1425
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/gitignore.template should match snapshot 1`] = `
2514
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/gitignore.template should match snapshot 1`] = `
1426
2515
  "# Environment variables
1427
2516
  .env
1428
2517
 
@@ -1467,10 +2556,16 @@ Thumbs.db
1467
2556
  "
1468
2557
  `;
1469
2558
 
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
2559
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/main.py should match snapshot 1`] = `
2560
+ "import os
2561
+
2562
+ # Suppress OpenTelemetry warnings during local development; remove for production
2563
+ if os.getenv("LOCAL_DEV") == "1":
2564
+ os.environ["OTEL_SDK_DISABLED"] = "true"
2565
+
2566
+ import uvicorn
2567
+ from strands import Agent, tool
2568
+ from ag_ui_strands import StrandsAgent, StrandsAgentConfig, create_strands_app
1474
2569
  from model.load import load_model
1475
2570
  {{#if hasMemory}}
1476
2571
  from memory.session import get_memory_session_manager
@@ -1485,42 +2580,35 @@ def add_numbers(a: int, b: int) -> int:
1485
2580
 
1486
2581
  tools = [add_numbers]
1487
2582
 
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
2583
  agent = Agent(
1507
2584
  model=load_model(),
1508
2585
  system_prompt="You are a helpful assistant. Use tools when appropriate.",
1509
2586
  tools=tools,
1510
2587
  )
2588
+
2589
+ {{#if hasMemory}}
2590
+ def session_manager_provider(input_data):
2591
+ return get_memory_session_manager(input_data.thread_id, "default-user")
2592
+
2593
+ config = StrandsAgentConfig(session_manager_provider=session_manager_provider)
2594
+ {{else}}
2595
+ config = StrandsAgentConfig()
1511
2596
  {{/if}}
1512
2597
 
2598
+ agui_agent = StrandsAgent(agent=agent, name="{{ name }}", description="A helpful assistant", config=config)
2599
+ app = create_strands_app(agui_agent, path="/invocations", ping_path="/ping")
2600
+
1513
2601
  if __name__ == "__main__":
1514
- serve_a2a(StrandsA2AExecutor(agent))
2602
+ uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT", "8080")))
1515
2603
  "
1516
2604
  `;
1517
2605
 
1518
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/model/__init__.py should match snapshot 1`] = `
2606
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/model/__init__.py should match snapshot 1`] = `
1519
2607
  "# Package marker
1520
2608
  "
1521
2609
  `;
1522
2610
 
1523
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/model/load.py should match snapshot 1`] = `
2611
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/model/load.py should match snapshot 1`] = `
1524
2612
  "{{#if (eq modelProvider "Bedrock")}}
1525
2613
  from strands.models.bedrock import BedrockModel
1526
2614
 
@@ -1647,7 +2735,7 @@ def load_model() -> GeminiModel:
1647
2735
  "
1648
2736
  `;
1649
2737
 
1650
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/base/pyproject.toml should match snapshot 1`] = `
2738
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/base/pyproject.toml should match snapshot 1`] = `
1651
2739
  "[build-system]
1652
2740
  requires = ["hatchling"]
1653
2741
  build-backend = "hatchling.build"
@@ -1655,18 +2743,22 @@ build-backend = "hatchling.build"
1655
2743
  [project]
1656
2744
  name = "{{ name }}"
1657
2745
  version = "0.1.0"
1658
- description = "AgentCore A2A Agent using Strands SDK"
2746
+ description = "AgentCore AG-UI Agent using Strands SDK"
1659
2747
  readme = "README.md"
1660
- requires-python = ">=3.10"
2748
+ requires-python = ">=3.12"
1661
2749
  dependencies = [
1662
2750
  {{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
1663
- {{/if}}"a2a-sdk[all] >= 0.2.0",
2751
+ {{/if}}"ag-ui-strands >= 0.1.7",
2752
+ "ag-ui-protocol >= 0.1.10",
1664
2753
  "aws-opentelemetry-distro",
1665
- "bedrock-agentcore[a2a] >= 1.0.3",
2754
+ "bedrock-agentcore >= 1.0.3",
1666
2755
  "botocore[crt] >= 1.35.0",
2756
+ "fastapi >= 0.115.12",
1667
2757
  {{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
1668
2758
  {{/if}}{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
1669
- {{/if}}"strands-agents >= 1.13.0",
2759
+ {{/if}}"strands-agents >= 1.15.0",
2760
+ "strands-agents-tools >= 0.2.14",
2761
+ "uvicorn >= 0.34.3",
1670
2762
  ]
1671
2763
 
1672
2764
  [tool.hatch.build.targets.wheel]
@@ -1674,12 +2766,12 @@ packages = ["."]
1674
2766
  "
1675
2767
  `;
1676
2768
 
1677
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/__init__.py should match snapshot 1`] = `
2769
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/capabilities/memory/__init__.py should match snapshot 1`] = `
1678
2770
  "# Package marker
1679
2771
  "
1680
2772
  `;
1681
2773
 
1682
- exports[`Assets Directory Snapshots > Python framework assets > python/python/a2a/strands/capabilities/memory/session.py should match snapshot 1`] = `
2774
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/agui/strands/capabilities/memory/session.py should match snapshot 1`] = `
1683
2775
  "import os
1684
2776
  from typing import Optional
1685
2777
 
@@ -1834,6 +2926,66 @@ add_numbers_tool = FunctionTool(
1834
2926
  # Define a collection of tools used by the model
1835
2927
  tools = [add_numbers_tool]
1836
2928
 
2929
+ {{#if sessionStorageMountPath}}
2930
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
2931
+
2932
+ def _safe_resolve(path: str) -> str:
2933
+ """Resolve path safely within the storage boundary."""
2934
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
2935
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
2936
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
2937
+ return resolved
2938
+
2939
+ def file_read(path: str) -> str:
2940
+ """Read a file from persistent storage. The path is relative to the storage root."""
2941
+ try:
2942
+ full_path = _safe_resolve(path)
2943
+ with open(full_path) as f:
2944
+ return f.read()
2945
+ except ValueError as e:
2946
+ return str(e)
2947
+ except OSError as e:
2948
+ return f"Error reading '{path}': {e.strerror}"
2949
+
2950
+ def file_write(path: str, content: str) -> str:
2951
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
2952
+ try:
2953
+ full_path = _safe_resolve(path)
2954
+ parent = os.path.dirname(full_path)
2955
+ if parent:
2956
+ os.makedirs(parent, exist_ok=True)
2957
+ with open(full_path, "w") as f:
2958
+ f.write(content)
2959
+ return f"Written to {path}"
2960
+ except ValueError as e:
2961
+ return str(e)
2962
+ except OSError as e:
2963
+ return f"Error writing '{path}': {e.strerror}"
2964
+
2965
+ def list_files(directory: str = "") -> str:
2966
+ """List files in persistent storage. The directory is relative to the storage root."""
2967
+ try:
2968
+ target = _safe_resolve(directory)
2969
+ entries = os.listdir(target)
2970
+ return "\\n".join(entries) if entries else "(empty directory)"
2971
+ except ValueError as e:
2972
+ return str(e)
2973
+ except OSError as e:
2974
+ return f"Error listing '{directory}': {e.strerror}"
2975
+
2976
+ tools.extend([
2977
+ FunctionTool(file_read, description="Read a file from persistent storage. The path is relative to the storage root."),
2978
+ FunctionTool(file_write, description="Write content to a file in persistent storage. The path is relative to the storage root."),
2979
+ FunctionTool(list_files, description="List files in persistent storage. The directory is relative to the storage root."),
2980
+ ])
2981
+ {{/if}}
2982
+
2983
+ SYSTEM_MESSAGE = """
2984
+ You are a helpful assistant. Use tools when appropriate.
2985
+ {{#if sessionStorageMountPath}}
2986
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
2987
+ {{/if}}
2988
+ """
1837
2989
 
1838
2990
  @app.entrypoint
1839
2991
  async def invoke(payload, context):
@@ -1847,7 +2999,7 @@ async def invoke(payload, context):
1847
2999
  name="{{ name }}",
1848
3000
  model_client=load_model(),
1849
3001
  tools=tools + mcp_tools,
1850
- system_message="You are a helpful assistant. Use tools when appropriate.",
3002
+ system_message=SYSTEM_MESSAGE,
1851
3003
  )
1852
3004
 
1853
3005
  # Process the user prompt
@@ -2202,6 +3354,66 @@ def add_numbers(a: int, b: int) -> int:
2202
3354
  return a + b
2203
3355
 
2204
3356
 
3357
+ # Define a collection of tools used by the model
3358
+ tools = [add_numbers]
3359
+
3360
+ {{#if sessionStorageMountPath}}
3361
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
3362
+
3363
+ def _safe_resolve(path: str) -> str:
3364
+ """Resolve path safely within the storage boundary."""
3365
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
3366
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
3367
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
3368
+ return resolved
3369
+
3370
+ def file_read(path: str) -> str:
3371
+ """Read a file from persistent storage. The path is relative to the storage root."""
3372
+ try:
3373
+ full_path = _safe_resolve(path)
3374
+ with open(full_path) as f:
3375
+ return f.read()
3376
+ except ValueError as e:
3377
+ return str(e)
3378
+ except OSError as e:
3379
+ return f"Error reading '{path}': {e.strerror}"
3380
+
3381
+ def file_write(path: str, content: str) -> str:
3382
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
3383
+ try:
3384
+ full_path = _safe_resolve(path)
3385
+ parent = os.path.dirname(full_path)
3386
+ if parent:
3387
+ os.makedirs(parent, exist_ok=True)
3388
+ with open(full_path, "w") as f:
3389
+ f.write(content)
3390
+ return f"Written to {path}"
3391
+ except ValueError as e:
3392
+ return str(e)
3393
+ except OSError as e:
3394
+ return f"Error writing '{path}': {e.strerror}"
3395
+
3396
+ def list_files(directory: str = "") -> str:
3397
+ """List files in persistent storage. The directory is relative to the storage root."""
3398
+ try:
3399
+ target = _safe_resolve(directory)
3400
+ entries = os.listdir(target)
3401
+ return "\\n".join(entries) if entries else "(empty directory)"
3402
+ except ValueError as e:
3403
+ return str(e)
3404
+ except OSError as e:
3405
+ return f"Error listing '{directory}': {e.strerror}"
3406
+
3407
+ tools.extend([file_read, file_write, list_files])
3408
+ {{/if}}
3409
+
3410
+ AGENT_INSTRUCTION = """
3411
+ I can answer your questions using the knowledge I have!
3412
+ {{#if sessionStorageMountPath}}
3413
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
3414
+ {{/if}}
3415
+ """
3416
+
2205
3417
  # Get MCP Toolset
2206
3418
  {{#if hasGateway}}
2207
3419
  mcp_toolset = get_all_gateway_mcp_toolsets()
@@ -2224,8 +3436,8 @@ agent = Agent(
2224
3436
  model=MODEL_ID,
2225
3437
  name="{{ name }}",
2226
3438
  description="Agent to answer questions",
2227
- instruction="I can answer your questions using the knowledge I have!",
2228
- tools=mcp_toolset + [add_numbers],
3439
+ instruction=AGENT_INSTRUCTION,
3440
+ tools=mcp_toolset + tools,
2229
3441
  )
2230
3442
 
2231
3443
 
@@ -2563,6 +3775,66 @@ def add_numbers(a: int, b: int) -> int:
2563
3775
  # Define a collection of tools used by the model
2564
3776
  tools = [add_numbers]
2565
3777
 
3778
+ {{#if sessionStorageMountPath}}
3779
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
3780
+
3781
+ def _safe_resolve(path: str) -> str:
3782
+ """Resolve path safely within the storage boundary."""
3783
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
3784
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
3785
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
3786
+ return resolved
3787
+
3788
+ @tool
3789
+ def file_read(path: str) -> str:
3790
+ """Read a file from persistent storage. The path is relative to the storage root."""
3791
+ try:
3792
+ full_path = _safe_resolve(path)
3793
+ with open(full_path) as f:
3794
+ return f.read()
3795
+ except ValueError as e:
3796
+ return str(e)
3797
+ except OSError as e:
3798
+ return f"Error reading '{path}': {e.strerror}"
3799
+
3800
+ @tool
3801
+ def file_write(path: str, content: str) -> str:
3802
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
3803
+ try:
3804
+ full_path = _safe_resolve(path)
3805
+ parent = os.path.dirname(full_path)
3806
+ if parent:
3807
+ os.makedirs(parent, exist_ok=True)
3808
+ with open(full_path, "w") as f:
3809
+ f.write(content)
3810
+ return f"Written to {path}"
3811
+ except ValueError as e:
3812
+ return str(e)
3813
+ except OSError as e:
3814
+ return f"Error writing '{path}': {e.strerror}"
3815
+
3816
+ @tool
3817
+ def list_files(directory: str = "") -> str:
3818
+ """List files in persistent storage. The directory is relative to the storage root."""
3819
+ try:
3820
+ target = _safe_resolve(directory)
3821
+ entries = os.listdir(target)
3822
+ return "\\n".join(entries) if entries else "(empty directory)"
3823
+ except ValueError as e:
3824
+ return str(e)
3825
+ except OSError as e:
3826
+ return f"Error listing '{directory}': {e.strerror}"
3827
+
3828
+ tools.extend([file_read, file_write, list_files])
3829
+ {{/if}}
3830
+
3831
+ SYSTEM_PROMPT = """
3832
+ You are a helpful assistant. Use tools when appropriate.
3833
+ {{#if sessionStorageMountPath}}
3834
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
3835
+ {{/if}}
3836
+ """
3837
+
2566
3838
 
2567
3839
  @app.entrypoint
2568
3840
  async def invoke(payload, context):
@@ -2581,7 +3853,7 @@ async def invoke(payload, context):
2581
3853
  mcp_tools = await mcp_client.get_tools()
2582
3854
 
2583
3855
  # Define the agent using create_react_agent
2584
- graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools)
3856
+ graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools, prompt=SYSTEM_PROMPT)
2585
3857
 
2586
3858
  # Process the user prompt
2587
3859
  prompt = payload.get("prompt", "What can you help me with?")
@@ -2986,6 +4258,68 @@ def add_numbers(a: int, b: int) -> int:
2986
4258
  return a + b
2987
4259
 
2988
4260
 
4261
+ tools = [add_numbers]
4262
+
4263
+ {{#if sessionStorageMountPath}}
4264
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
4265
+
4266
+ def _safe_resolve(path: str) -> str:
4267
+ """Resolve path safely within the storage boundary."""
4268
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
4269
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
4270
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
4271
+ return resolved
4272
+
4273
+ @function_tool
4274
+ def file_read(path: str) -> str:
4275
+ """Read a file from persistent storage. The path is relative to the storage root."""
4276
+ try:
4277
+ full_path = _safe_resolve(path)
4278
+ with open(full_path) as f:
4279
+ return f.read()
4280
+ except ValueError as e:
4281
+ return str(e)
4282
+ except OSError as e:
4283
+ return f"Error reading '{path}': {e.strerror}"
4284
+
4285
+ @function_tool
4286
+ def file_write(path: str, content: str) -> str:
4287
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
4288
+ try:
4289
+ full_path = _safe_resolve(path)
4290
+ parent = os.path.dirname(full_path)
4291
+ if parent:
4292
+ os.makedirs(parent, exist_ok=True)
4293
+ with open(full_path, "w") as f:
4294
+ f.write(content)
4295
+ return f"Written to {path}"
4296
+ except ValueError as e:
4297
+ return str(e)
4298
+ except OSError as e:
4299
+ return f"Error writing '{path}': {e.strerror}"
4300
+
4301
+ @function_tool
4302
+ def list_files(directory: str = "") -> str:
4303
+ """List files in persistent storage. The directory is relative to the storage root."""
4304
+ try:
4305
+ target = _safe_resolve(directory)
4306
+ entries = os.listdir(target)
4307
+ return "\\n".join(entries) if entries else "(empty directory)"
4308
+ except ValueError as e:
4309
+ return str(e)
4310
+ except OSError as e:
4311
+ return f"Error listing '{directory}': {e.strerror}"
4312
+
4313
+ tools.extend([file_read, file_write, list_files])
4314
+ {{/if}}
4315
+
4316
+ INSTRUCTIONS = """
4317
+ You are a helpful assistant. Use tools when appropriate.
4318
+ {{#if sessionStorageMountPath}}
4319
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
4320
+ {{/if}}
4321
+ """
4322
+
2989
4323
  # Define the agent execution
2990
4324
  async def main(query):
2991
4325
  ensure_credentials_loaded()
@@ -2995,8 +4329,9 @@ async def main(query):
2995
4329
  agent = Agent(
2996
4330
  name="{{ name }}",
2997
4331
  model="gpt-4.1",
4332
+ instructions=INSTRUCTIONS,
2998
4333
  mcp_servers=mcp_servers,
2999
- tools=[add_numbers]
4334
+ tools=tools
3000
4335
  )
3001
4336
  result = await Runner.run(agent, query)
3002
4337
  return result
@@ -3004,8 +4339,9 @@ async def main(query):
3004
4339
  agent = Agent(
3005
4340
  name="{{ name }}",
3006
4341
  model="gpt-4.1",
4342
+ instructions=INSTRUCTIONS,
3007
4343
  mcp_servers=[],
3008
- tools=[add_numbers]
4344
+ tools=tools
3009
4345
  )
3010
4346
  result = await Runner.run(agent, query)
3011
4347
  return result
@@ -3016,8 +4352,9 @@ async def main(query):
3016
4352
  agent = Agent(
3017
4353
  name="{{ name }}",
3018
4354
  model="gpt-4.1",
4355
+ instructions=INSTRUCTIONS,
3019
4356
  mcp_servers=active_servers,
3020
- tools=[add_numbers]
4357
+ tools=tools
3021
4358
  )
3022
4359
  result = await Runner.run(agent, query)
3023
4360
  return result
@@ -3025,8 +4362,9 @@ async def main(query):
3025
4362
  agent = Agent(
3026
4363
  name="{{ name }}",
3027
4364
  model="gpt-4.1",
4365
+ instructions=INSTRUCTIONS,
3028
4366
  mcp_servers=[],
3029
- tools=[add_numbers]
4367
+ tools=tools
3030
4368
  )
3031
4369
  result = await Runner.run(agent, query)
3032
4370
  return result
@@ -3308,6 +4646,9 @@ from mcp_client.client import get_streamable_http_mcp_client
3308
4646
  {{#if hasMemory}}
3309
4647
  from memory.session import get_memory_session_manager
3310
4648
  {{/if}}
4649
+ {{#if sessionStorageMountPath}}
4650
+ import os
4651
+ {{/if}}
3311
4652
 
3312
4653
  app = BedrockAgentCoreApp()
3313
4654
  log = app.logger
@@ -3329,11 +4670,70 @@ def add_numbers(a: int, b: int) -> int:
3329
4670
  return a+b
3330
4671
  tools.append(add_numbers)
3331
4672
 
4673
+ {{#if sessionStorageMountPath}}
4674
+ SESSION_STORAGE_PATH = "{{sessionStorageMountPath}}"
4675
+
4676
+ def _safe_resolve(path: str) -> str:
4677
+ """Resolve path safely within the storage boundary."""
4678
+ resolved = os.path.realpath(os.path.join(SESSION_STORAGE_PATH, path.lstrip("/")))
4679
+ if not resolved.startswith(os.path.realpath(SESSION_STORAGE_PATH)):
4680
+ raise ValueError(f"Path '{path}' is outside the storage boundary")
4681
+ return resolved
4682
+
4683
+ @tool
4684
+ def file_read(path: str) -> str:
4685
+ """Read a file from persistent storage. The path is relative to the storage root."""
4686
+ try:
4687
+ full_path = _safe_resolve(path)
4688
+ with open(full_path) as f:
4689
+ return f.read()
4690
+ except ValueError as e:
4691
+ return str(e)
4692
+ except OSError as e:
4693
+ return f"Error reading '{path}': {e.strerror}"
4694
+
4695
+ @tool
4696
+ def file_write(path: str, content: str) -> str:
4697
+ """Write content to a file in persistent storage. The path is relative to the storage root."""
4698
+ try:
4699
+ full_path = _safe_resolve(path)
4700
+ parent = os.path.dirname(full_path)
4701
+ if parent:
4702
+ os.makedirs(parent, exist_ok=True)
4703
+ with open(full_path, "w") as f:
4704
+ f.write(content)
4705
+ return f"Written to {path}"
4706
+ except ValueError as e:
4707
+ return str(e)
4708
+ except OSError as e:
4709
+ return f"Error writing '{path}': {e.strerror}"
4710
+
4711
+ @tool
4712
+ def list_files(directory: str = "") -> str:
4713
+ """List files in persistent storage. The directory is relative to the storage root."""
4714
+ try:
4715
+ target = _safe_resolve(directory)
4716
+ entries = os.listdir(target)
4717
+ return "\\n".join(entries) if entries else "(empty directory)"
4718
+ except ValueError as e:
4719
+ return str(e)
4720
+ except OSError as e:
4721
+ return f"Error listing '{directory}': {e.strerror}"
4722
+
4723
+ tools.extend([file_read, file_write, list_files])
4724
+ {{/if}}
4725
+
3332
4726
  # Add MCP client to tools if available
3333
4727
  for mcp_client in mcp_clients:
3334
4728
  if mcp_client:
3335
4729
  tools.append(mcp_client)
3336
4730
 
4731
+ SYSTEM_PROMPT = """
4732
+ You are a helpful assistant. Use tools when appropriate.
4733
+ {{#if sessionStorageMountPath}}
4734
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
4735
+ {{/if}}
4736
+ """
3337
4737
 
3338
4738
  {{#if hasMemory}}
3339
4739
  def agent_factory():
@@ -3345,9 +4745,7 @@ def agent_factory():
3345
4745
  cache[key] = Agent(
3346
4746
  model=load_model(),
3347
4747
  session_manager=get_memory_session_manager(session_id, user_id),
3348
- system_prompt="""
3349
- You are a helpful assistant. Use tools when appropriate.
3350
- """,
4748
+ system_prompt=SYSTEM_PROMPT,
3351
4749
  tools=tools
3352
4750
  )
3353
4751
  return cache[key]
@@ -3361,9 +4759,7 @@ def get_or_create_agent():
3361
4759
  if _agent is None:
3362
4760
  _agent = Agent(
3363
4761
  model=load_model(),
3364
- system_prompt="""
3365
- You are a helpful assistant. Use tools when appropriate.
3366
- """,
4762
+ system_prompt=SYSTEM_PROMPT,
3367
4763
  tools=tools
3368
4764
  )
3369
4765
  return _agent
@@ -3826,27 +5222,47 @@ exports[`Assets Directory Snapshots > Root-level assets > AGENTS.md should match
3826
5222
 
3827
5223
  This directory stores:
3828
5224
 
3829
- - Template assets for agents written in different Languages, SDKs and having different configurations
5225
+ - Template assets for agents written in different languages, SDKs, and configurations
3830
5226
  - Container templates (\`container/python/\`) with \`Dockerfile\` and \`.dockerignore\` for Container build agents
5227
+ - Vended documentation (\`README.md\`, \`agents/AGENTS.md\`) copied into user projects at create time
5228
+ - CDK project template (\`cdk/\`) using \`@aws/agentcore-cdk\` L3 constructs
5229
+ - Evaluator templates (\`evaluators/\`) for code-based evaluators
5230
+ - MCP tool templates (\`mcp/\`) for Lambda and AgentCoreRuntime compute
3831
5231
 
3832
5232
  ### Directory Layout
3833
5233
 
3834
5234
  \`\`\`
3835
5235
  assets/
3836
- ├── python/ # Framework templates (one per SDK)
3837
- ├── strands/
3838
- ├── langchain_langgraph/
3839
- ├── googleadk/
3840
- ├── openaiagents/
3841
- └── autogen/
5236
+ ├── README.md # Vended to project root as project README
5237
+ ├── AGENTS.md # This file — internal dev context
5238
+ ├── agents/
5239
+ └── AGENTS.md # Vended to project root for AI coding assistants
5240
+ ├── python/ # Framework templates (one per SDK per protocol)
5241
+ ├── http/ # HTTP protocol agents
5242
+ │ │ ├── strands/
5243
+ │ │ ├── langchain_langgraph/
5244
+ │ │ ├── googleadk/
5245
+ │ │ ├── openaiagents/
5246
+ │ │ └── autogen/
5247
+ │ ├── mcp/ # MCP protocol agents
5248
+ │ │ └── standalone/
5249
+ │ └── a2a/ # A2A protocol agents
5250
+ │ ├── strands/
5251
+ │ ├── langchain_langgraph/
5252
+ │ └── googleadk/
5253
+ ├── typescript/ # TypeScript agent templates
3842
5254
  ├── container/ # Container build templates
3843
5255
  │ └── python/
3844
5256
  │ ├── Dockerfile
3845
5257
  │ └── dockerignore.template
3846
- └── agents/ # AGENTS.md vended to user projects
5258
+ ├── cdk/ # CDK project template (@aws/agentcore-cdk)
5259
+ ├── evaluators/ # Code-based evaluator templates
5260
+ └── mcp/ # MCP tool templates (Lambda + AgentCoreRuntime)
5261
+ ├── python/
5262
+ └── python-lambda/
3847
5263
  \`\`\`
3848
5264
 
3849
- The rendering logic is rooted in the \`AgentEnvSpec\` and must ALWAYS respect the configuration in the Spec.
5265
+ The rendering logic is rooted in the \`AgentEnvSpec\` and must ALWAYS respect the configuration in the spec.
3850
5266
 
3851
5267
  For Container builds, \`BaseRenderer.render()\` automatically copies the \`container/<language>/\` templates (Dockerfile,
3852
5268
  .dockerignore) into the agent directory when \`buildType === 'Container'\`.
@@ -3855,10 +5271,13 @@ For Container builds, \`BaseRenderer.render()\` automatically copies the \`conta
3855
5271
 
3856
5272
  - Always make sure the templates are as close to working code as possible
3857
5273
  - AVOID as much as possible using any conditionals within the templates
5274
+ - Test template rendering with \`agentcore add agent\` for each framework/protocol combination
3858
5275
 
3859
5276
  ## How to use the assets in this directory
3860
5277
 
3861
- - These assets are rendered by the CLI's template renderer in \`src/cli/templates/\`.
5278
+ - These assets are rendered by the CLI's template renderer in \`src/cli/templates/\`
5279
+ - The \`README.md\` and \`agents/AGENTS.md\` are copied verbatim (no template rendering) during project creation
5280
+ - The \`.llm-context/\` files are sourced from \`src/schema/llm-compacted/\` and written during init
3862
5281
  "
3863
5282
  `;
3864
5283
 
@@ -3870,14 +5289,19 @@ This project was created with the [AgentCore CLI](https://github.com/aws/agentco
3870
5289
  ## Project Structure
3871
5290
 
3872
5291
  \`\`\`
3873
- .
3874
5292
  my-project/
5293
+ ├── AGENTS.md # AI coding assistant context
3875
5294
  ├── 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
5295
+ │ ├── agentcore.json # Project config (agents, memories, credentials, gateways, evaluators)
5296
+ │ ├── aws-targets.json # Deployment targets (account + region)
5297
+ │ ├── .env.local # Secrets — API keys (gitignored)
5298
+ ├── .llm-context/ # TypeScript type definitions for AI assistants
5299
+ │ │ ├── agentcore.ts # AgentCoreProjectSpec types
5300
+ │ │ ├── aws-targets.ts # Deployment target types
5301
+ │ │ └── mcp.ts # Gateway and MCP tool types
5302
+ │ └── cdk/ # CDK infrastructure (@aws/agentcore-cdk)
5303
+ ├── app/ # Agent application code
5304
+ └── evaluators/ # Custom evaluator code (if any)
3881
5305
  \`\`\`
3882
5306
 
3883
5307
  ## Getting Started
@@ -3885,7 +5309,9 @@ my-project/
3885
5309
  ### Prerequisites
3886
5310
 
3887
5311
  - **Node.js** 20.x or later
3888
- - **uv** for Python agents ([install](https://docs.astral.sh/uv/getting-started/installation/))
5312
+ - **Python 3.10+** and **uv** for Python agents ([install uv](https://docs.astral.sh/uv/getting-started/installation/))
5313
+ - **AWS credentials** configured (\`aws configure\` or environment variables)
5314
+ - **Docker** (only for Container build agents)
3889
5315
 
3890
5316
  ### Development
3891
5317
 
@@ -3903,44 +5329,62 @@ Deploy to AWS:
3903
5329
  agentcore deploy
3904
5330
  \`\`\`
3905
5331
 
3906
- Or use CDK directly:
5332
+ ## Commands
3907
5333
 
3908
- \`\`\`bash
3909
- cd agentcore/cdk
3910
- npx cdk deploy
3911
- \`\`\`
5334
+ | Command | Description |
5335
+ | --- | --- |
5336
+ | \`agentcore create\` | Create a new AgentCore project |
5337
+ | \`agentcore add\` | Add resources (agent, memory, credential, gateway, evaluator, policy) |
5338
+ | \`agentcore remove\` | Remove resources |
5339
+ | \`agentcore dev\` | Run agent locally with hot-reload |
5340
+ | \`agentcore deploy\` | Deploy to AWS via CDK |
5341
+ | \`agentcore status\` | Show deployment status |
5342
+ | \`agentcore invoke\` | Invoke agent (local or deployed) |
5343
+ | \`agentcore logs\` | View agent logs |
5344
+ | \`agentcore traces\` | View agent traces |
5345
+ | \`agentcore eval\` | Run evaluations |
5346
+ | \`agentcore package\` | Package agent artifacts |
5347
+ | \`agentcore validate\` | Validate configuration |
5348
+ | \`agentcore pause\` | Pause a deployed agent |
5349
+ | \`agentcore resume\` | Resume a paused agent |
5350
+ | \`agentcore fetch\` | Fetch remote resource definitions |
5351
+ | \`agentcore import\` | Import existing resources |
5352
+ | \`agentcore update\` | Check for CLI updates |
3912
5353
 
3913
5354
  ## Configuration
3914
5355
 
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.
5356
+ Edit the JSON files in \`agentcore/\` to configure your project. See \`agentcore/.llm-context/\` for type definitions and validation constraints.
3917
5357
 
3918
- The project uses a **flat resource model** where agents, memories, and credentials are top-level arrays in
3919
- \`agentcore.json\`.
5358
+ 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
5359
 
3921
- ## Commands
5360
+ ## Resources
3922
5361
 
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 |
5362
+ | Resource | Purpose |
5363
+ | --- | --- |
5364
+ | Agent (runtime) | HTTP, MCP, or A2A agent deployed to AgentCore Runtime |
5365
+ | Memory | Persistent context storage with configurable strategies |
5366
+ | Credential | API key or OAuth credential providers |
5367
+ | Gateway | MCP gateway that routes tool calls to targets |
5368
+ | Gateway Target | Tool implementation (Lambda, MCP server, OpenAPI, Smithy, API Gateway) |
5369
+ | Evaluator | Custom LLM-as-a-Judge or code-based evaluation |
5370
+ | Online Eval Config | Continuous evaluation pipeline for deployed agents |
5371
+ | Policy | Cedar authorization policies for gateway tools |
3935
5372
 
3936
5373
  ### Agent Types
3937
5374
 
3938
- - **Template agents**: Created from framework templates (Strands, LangChain_LangGraph, CrewAI, GoogleADK, OpenAIAgents)
5375
+ - **Template agents**: Created from framework templates (Strands, LangChain/LangGraph, GoogleADK, OpenAI Agents, Autogen)
3939
5376
  - **BYO agents**: Bring your own code with \`agentcore add agent --type byo\`
5377
+ - **Import agents**: Import existing Bedrock agents with \`agentcore import\`
5378
+
5379
+ ### Build Types
5380
+
5381
+ - **CodeZip**: Python source packaged as a zip and deployed directly to AgentCore Runtime
5382
+ - **Container**: Docker image built via CodeBuild (ARM64), pushed to ECR, and deployed to AgentCore Runtime
3940
5383
 
3941
5384
  ## Documentation
3942
5385
 
3943
- - [AgentCore CLI Documentation](https://github.com/aws/agentcore-cli)
5386
+ - [AgentCore CLI](https://github.com/aws/agentcore-cli)
5387
+ - [AgentCore CDK Constructs](https://github.com/aws/agentcore-l3-cdk-constructs)
3944
5388
  - [Amazon Bedrock AgentCore](https://aws.amazon.com/bedrock/agentcore/)
3945
5389
  "
3946
5390
  `;
@@ -3950,103 +5394,114 @@ exports[`Assets Directory Snapshots > Root-level assets > agents/AGENTS.md shoul
3950
5394
 
3951
5395
  This project contains configuration and infrastructure for an Amazon Bedrock AgentCore application.
3952
5396
 
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.
5397
+ The \`agentcore/\` directory is a declarative model of the project. The \`agentcore/cdk/\` subdirectory uses the
5398
+ \`@aws/agentcore-cdk\` L3 constructs to deploy the configuration to AWS.
3956
5399
 
3957
5400
  ## Mental Model
3958
5401
 
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.
5402
+ The project uses a **flat resource model**. Agents, memories, credentials, gateways, evaluators, and policies are
5403
+ independent top-level arrays in \`agentcore.json\`. There is no binding between resources in the schema — each resource is
5404
+ provisioned independently. Agents discover memories and credentials at runtime via environment variables or SDK calls.
5405
+ Tags defined in \`agentcore.json\` flow through to deployed CloudFormation resources.
3963
5406
 
3964
5407
  ## Critical Invariants
3965
5408
 
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.
5409
+ 1. **Schema-First Authority:** The \`.json\` files are the source of truth. Do not modify agent behavior by editing
5410
+ generated CDK code in \`cdk/\`.
5411
+ 2. **Resource Identity:** The \`name\` field determines the CloudFormation Logical ID.
5412
+ - **Renaming** a resource will **destroy and recreate** it.
5413
+ - **Modifying** other fields will update the resource **in-place**.
5414
+ 3. **Schema Validation:** If your JSON conforms to the types in \`.llm-context/\`, it will deploy successfully. Run
5415
+ \`agentcore validate\` to check.
5416
+ 4. **Resource Removal:** Use \`agentcore remove\` to remove resources. Run \`agentcore deploy\` after removal to tear down
5417
+ deployed infrastructure.
3975
5418
 
3976
5419
  ## Directory Structure
3977
5420
 
3978
5421
  \`\`\`
3979
- myNewProject/
3980
- ├── AGENTS.md # This file - AI coding assistant context
3981
- ├── agentcore/ # AgentCore configuration directory
5422
+ myProject/
5423
+ ├── AGENTS.md # This file AI coding assistant context
5424
+ ├── agentcore/
3982
5425
  │ ├── 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
5426
+ │ ├── aws-targets.json # Deployment targets (account + region)
5427
+ │ ├── .env.local # Secrets API keys (gitignored)
5428
+ │ ├── .llm-context/ # TypeScript type definitions for AI assistants
5429
+ │ │ ├── README.md # Guide to using schema files
3986
5430
  │ │ ├── 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)
5431
+ │ │ ├── aws-targets.ts # AWS deployment target types
5432
+ └── mcp.ts # Gateway and MCP tool types
5433
+ └── cdk/ # AWS CDK project (@aws/agentcore-cdk L3 constructs)
5434
+ ├── app/ # Agent application code
5435
+ └── evaluators/ # Custom evaluator code (if any)
3990
5436
  \`\`\`
3991
5437
 
3992
5438
  ## Schema Reference
3993
5439
 
3994
5440
  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.
5441
+ file maps to a JSON config file and includes validation constraints as comments (\`@regex\`, \`@min\`, \`@max\`).
3996
5442
 
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[]\` |
5443
+ | JSON Config | Schema File | Root Type |
5444
+ | --- | --- | --- |
5445
+ | \`agentcore/agentcore.json\` | \`agentcore/.llm-context/agentcore.ts\` | \`AgentCoreProjectSpec\` |
5446
+ | \`agentcore/agentcore.json\` (gateways) | \`agentcore/.llm-context/mcp.ts\` | \`AgentCoreMcpSpec\` |
5447
+ | \`agentcore/aws-targets.json\` | \`agentcore/.llm-context/aws-targets.ts\` | \`AwsDeploymentTarget[]\` |
4001
5448
 
4002
5449
  ### Key Types
4003
5450
 
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
5451
+ - **AgentCoreProjectSpec**: Root config with \`runtimes\`, \`memories\`, \`credentials\`, \`agentCoreGateways\`, \`evaluators\`, \`onlineEvalConfigs\`, \`policyEngines\` arrays
5452
+ - **AgentEnvSpec**: Agent configuration (build type, entrypoint, code location, runtime version, network mode)
5453
+ - **Memory**: Memory resource with strategies (SEMANTIC, SUMMARIZATION, USER_PREFERENCE, EPISODIC) and expiry
5454
+ - **Credential**: API key or OAuth credential provider
5455
+ - **AgentCoreGateway**: MCP gateway with targets (Lambda, MCP server, OpenAPI, Smithy, API Gateway)
5456
+ - **Evaluator**: LLM-as-a-Judge or code-based evaluator
5457
+ - **OnlineEvalConfig**: Continuous evaluation pipeline bound to an agent
4008
5458
 
4009
5459
  ### Common Enum Values
4010
5460
 
4011
5461
  - **BuildType**: \`'CodeZip'\` | \`'Container'\`
4012
- - **NetworkMode**: \`'PUBLIC'\`
4013
- - **RuntimeVersion**: \`'PYTHON_3_10'\` | \`'PYTHON_3_11'\` | \`'PYTHON_3_12'\` | \`'PYTHON_3_13'\` | \`'PYTHON_3_14'\`
5462
+ - **NetworkMode**: \`'PUBLIC'\` | \`'VPC'\`
5463
+ - **RuntimeVersion**: \`'PYTHON_3_10'\` | \`'PYTHON_3_11'\` | \`'PYTHON_3_12'\` | \`'PYTHON_3_13'\` | \`'PYTHON_3_14'\` | \`'NODE_18'\` | \`'NODE_20'\` | \`'NODE_22'\`
4014
5464
  - **MemoryStrategyType**: \`'SEMANTIC'\` | \`'SUMMARIZATION'\` | \`'USER_PREFERENCE'\` | \`'EPISODIC'\`
5465
+ - **GatewayTargetType**: \`'lambda'\` | \`'mcpServer'\` | \`'openApiSchema'\` | \`'smithyModel'\` | \`'apiGateway'\` | \`'lambdaFunctionArn'\`
5466
+ - **ModelProvider**: \`'Bedrock'\` | \`'Gemini'\` | \`'OpenAI'\` | \`'Anthropic'\`
4015
5467
 
4016
5468
  ### Build Types
4017
5469
 
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.
5470
+ - **CodeZip**: Python source packaged as a zip and deployed directly to AgentCore Runtime.
5471
+ - **Container**: Docker image built in CodeBuild (ARM64), pushed to a per-agent ECR repository. Requires a \`Dockerfile\`
5472
+ in the agent's \`codeLocation\` directory. For local development (\`agentcore dev\`), the container is built and run
5473
+ locally with volume-mounted hot-reload.
4023
5474
 
4024
5475
  ### Supported Frameworks (for template agents)
4025
5476
 
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
5477
+ - **Strands** Bedrock, Anthropic, OpenAI, Gemini
5478
+ - **LangChain/LangGraph** Bedrock, Anthropic, OpenAI, Gemini
5479
+ - **GoogleADK** Gemini
5480
+ - **OpenAI Agents** OpenAI
5481
+ - **Autogen** — Bedrock, Anthropic, OpenAI, Gemini
4030
5482
 
5483
+ ### Protocols
4031
5484
 
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.
5485
+ - **HTTP** — Standard HTTP agent endpoint
5486
+ - **MCP** — Model Context Protocol server
5487
+ - **A2A** — Agent-to-Agent protocol (Google A2A)
4036
5488
 
4037
5489
  ## Deployment
4038
5490
 
4039
- The \`agentcore/cdk/\` subdirectory contains an AWS CDK node project.
5491
+ Deployments are orchestrated through the CLI:
4040
5492
 
4041
- Deployments of this project are primarily intended to be orchestrated through the \`agentcore deploy\` command in the CLI.
5493
+ \`\`\`bash
5494
+ agentcore deploy # Synthesizes CDK and deploys to AWS
5495
+ agentcore status # Shows deployment status
5496
+ \`\`\`
4042
5497
 
4043
- Alternatively, the project can be deployed directly as a traditional CDK project:
5498
+ Alternatively, deploy directly via CDK:
4044
5499
 
4045
5500
  \`\`\`bash
4046
5501
  cd agentcore/cdk
4047
5502
  npm install
4048
- npx cdk synth # Preview CloudFormation template
4049
- npx cdk deploy # Deploy to AWS
5503
+ npx cdk synth
5504
+ npx cdk deploy
4050
5505
  \`\`\`
4051
5506
 
4052
5507
  ## Editing Schemas
@@ -4057,7 +5512,25 @@ When modifying JSON config files:
4057
5512
  2. Check validation constraint comments (\`@regex\`, \`@min\`, \`@max\`)
4058
5513
  3. Use exact enum values as string literals
4059
5514
  4. Use CloudFormation-safe names (alphanumeric, start with letter)
4060
- 5. Run \`agentcore validate\` command to verify changes.
5515
+ 5. Run \`agentcore validate\` to verify changes
5516
+
5517
+ ## CLI Commands
5518
+
5519
+ | Command | Description |
5520
+ | --- | --- |
5521
+ | \`agentcore create\` | Create a new project |
5522
+ | \`agentcore add <resource>\` | Add agent, memory, credential, gateway, evaluator, policy |
5523
+ | \`agentcore remove <resource>\` | Remove a resource |
5524
+ | \`agentcore dev\` | Run agent locally with hot-reload |
5525
+ | \`agentcore deploy\` | Deploy to AWS |
5526
+ | \`agentcore status\` | Show deployment status |
5527
+ | \`agentcore invoke\` | Invoke agent (local or deployed) |
5528
+ | \`agentcore logs\` | View agent logs |
5529
+ | \`agentcore traces\` | View agent traces |
5530
+ | \`agentcore eval\` | Run evaluations against an agent |
5531
+ | \`agentcore package\` | Package agent artifacts |
5532
+ | \`agentcore validate\` | Validate configuration |
5533
+ | \`agentcore pause\` / \`resume\` | Pause or resume a deployed agent |
4061
5534
  "
4062
5535
  `;
4063
5536