@aws/agentcore 1.0.0-preview.1 → 1.0.0-preview.10

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 (185) hide show
  1. package/README.md +56 -23
  2. package/dist/agent-inspector/index.css +1 -1
  3. package/dist/agent-inspector/index.js +87 -83
  4. package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +803 -51
  5. package/dist/assets/__tests__/__snapshots__/dockerfile-render.test.ts.snap +6 -2
  6. package/dist/assets/__tests__/assets.snapshot.test.ts +10 -24
  7. package/dist/assets/cdk/bin/cdk.ts +6 -4
  8. package/dist/assets/cdk/cdk.json +1 -1
  9. package/dist/assets/cdk/lib/cdk-stack.ts +20 -14
  10. package/dist/assets/cdk/test/cdk.test.ts +1 -0
  11. package/dist/assets/container/python/Dockerfile +3 -1
  12. package/dist/assets/container/typescript/Dockerfile +25 -0
  13. package/dist/assets/container/typescript/dockerignore.template +24 -0
  14. package/dist/assets/datasets/predefined-v1.jsonl +3 -0
  15. package/dist/assets/datasets/simulated-v1.jsonl +3 -0
  16. package/dist/assets/evaluators/python-lambda/execution-role-policy.json +1 -1
  17. package/dist/assets/python/a2a/googleadk/base/pyproject.toml +2 -2
  18. package/dist/assets/python/a2a/langchain_langgraph/base/pyproject.toml +1 -1
  19. package/dist/assets/python/a2a/strands/base/pyproject.toml +2 -2
  20. package/dist/assets/python/a2a/strands/capabilities/memory/session.py +7 -2
  21. package/dist/assets/python/agui/googleadk/base/pyproject.toml +1 -1
  22. package/dist/assets/python/agui/strands/base/pyproject.toml +1 -1
  23. package/dist/assets/python/agui/strands/capabilities/memory/session.py +7 -2
  24. package/dist/assets/python/http/googleadk/base/pyproject.toml +1 -1
  25. package/dist/assets/python/http/langchain_langgraph/base/main.py +52 -8
  26. package/dist/assets/python/http/strands/base/main.py +71 -8
  27. package/dist/assets/python/http/strands/base/pyproject.toml +1 -1
  28. package/dist/assets/python/http/strands/capabilities/memory/session.py +7 -2
  29. package/dist/assets/typescript/http/strands/base/README.md +37 -0
  30. package/dist/assets/typescript/http/strands/base/gitignore.template +22 -0
  31. package/dist/assets/typescript/http/strands/base/main.ts +66 -0
  32. package/dist/assets/typescript/http/strands/base/mcp_client/client.ts +11 -0
  33. package/dist/assets/typescript/http/strands/base/model/load.ts +102 -0
  34. package/dist/assets/typescript/http/strands/base/package.json +37 -0
  35. package/dist/assets/typescript/http/strands/base/tsconfig.json +19 -0
  36. package/dist/assets/typescript/http/vercelai/base/README.md +37 -0
  37. package/dist/assets/typescript/http/vercelai/base/gitignore.template +22 -0
  38. package/dist/assets/typescript/http/vercelai/base/main.ts +24 -0
  39. package/dist/assets/typescript/http/vercelai/base/model/load.ts +121 -0
  40. package/dist/assets/typescript/http/vercelai/base/package.json +35 -0
  41. package/dist/assets/typescript/http/vercelai/base/tsconfig.json +19 -0
  42. package/dist/cli/index.mjs +1284 -726
  43. package/dist/lib/errors/index.d.ts +1 -1
  44. package/dist/lib/errors/index.d.ts.map +1 -1
  45. package/dist/lib/errors/index.js +1 -1
  46. package/dist/lib/errors/index.js.map +1 -1
  47. package/dist/lib/errors/types.d.ts +183 -0
  48. package/dist/lib/errors/types.d.ts.map +1 -0
  49. package/dist/lib/errors/types.js +284 -0
  50. package/dist/lib/errors/types.js.map +1 -0
  51. package/dist/lib/errors/zod.d.ts +8 -0
  52. package/dist/lib/errors/zod.d.ts.map +1 -0
  53. package/dist/lib/errors/{config.js → zod.js} +7 -80
  54. package/dist/lib/errors/zod.js.map +1 -0
  55. package/dist/lib/index.d.ts +3 -0
  56. package/dist/lib/index.d.ts.map +1 -1
  57. package/dist/lib/index.js +4 -1
  58. package/dist/lib/index.js.map +1 -1
  59. package/dist/lib/packaging/build-args.js +2 -2
  60. package/dist/lib/packaging/build-args.js.map +1 -1
  61. package/dist/lib/packaging/container.js +5 -5
  62. package/dist/lib/packaging/container.js.map +1 -1
  63. package/dist/lib/packaging/helpers.d.ts +16 -0
  64. package/dist/lib/packaging/helpers.d.ts.map +1 -1
  65. package/dist/lib/packaging/helpers.js +71 -9
  66. package/dist/lib/packaging/helpers.js.map +1 -1
  67. package/dist/lib/packaging/index.d.ts +0 -1
  68. package/dist/lib/packaging/index.d.ts.map +1 -1
  69. package/dist/lib/packaging/index.js +4 -19
  70. package/dist/lib/packaging/index.js.map +1 -1
  71. package/dist/lib/packaging/node.d.ts +2 -2
  72. package/dist/lib/packaging/node.d.ts.map +1 -1
  73. package/dist/lib/packaging/node.js +83 -38
  74. package/dist/lib/packaging/node.js.map +1 -1
  75. package/dist/lib/packaging/python.js +12 -12
  76. package/dist/lib/packaging/python.js.map +1 -1
  77. package/dist/lib/result.d.ts +29 -0
  78. package/dist/lib/result.d.ts.map +1 -0
  79. package/dist/lib/result.js +16 -0
  80. package/dist/lib/result.js.map +1 -0
  81. package/dist/lib/schemas/io/config-io.d.ts.map +1 -1
  82. package/dist/lib/schemas/io/config-io.js +11 -3
  83. package/dist/lib/schemas/io/config-io.js.map +1 -1
  84. package/dist/lib/schemas/io/global-config.d.ts +33 -0
  85. package/dist/lib/schemas/io/global-config.d.ts.map +1 -0
  86. package/dist/lib/schemas/io/global-config.js +89 -0
  87. package/dist/lib/schemas/io/global-config.js.map +1 -0
  88. package/dist/lib/schemas/io/index.d.ts +1 -2
  89. package/dist/lib/schemas/io/index.d.ts.map +1 -1
  90. package/dist/lib/schemas/io/index.js +1 -4
  91. package/dist/lib/schemas/io/index.js.map +1 -1
  92. package/dist/lib/schemas/io/path-resolver.d.ts +0 -6
  93. package/dist/lib/schemas/io/path-resolver.d.ts.map +1 -1
  94. package/dist/lib/schemas/io/path-resolver.js +3 -13
  95. package/dist/lib/schemas/io/path-resolver.js.map +1 -1
  96. package/dist/lib/time-constants.d.ts +5 -0
  97. package/dist/lib/time-constants.d.ts.map +1 -0
  98. package/dist/lib/time-constants.js +8 -0
  99. package/dist/lib/time-constants.js.map +1 -0
  100. package/dist/lib/utils/index.d.ts +1 -0
  101. package/dist/lib/utils/index.d.ts.map +1 -1
  102. package/dist/lib/utils/index.js +4 -1
  103. package/dist/lib/utils/index.js.map +1 -1
  104. package/dist/lib/utils/polling.d.ts +31 -0
  105. package/dist/lib/utils/polling.d.ts.map +1 -0
  106. package/dist/lib/utils/polling.js +61 -0
  107. package/dist/lib/utils/polling.js.map +1 -0
  108. package/dist/schema/constants.d.ts +7 -0
  109. package/dist/schema/constants.d.ts.map +1 -1
  110. package/dist/schema/constants.js +18 -3
  111. package/dist/schema/constants.js.map +1 -1
  112. package/dist/schema/schemas/agent-env.d.ts +25 -1
  113. package/dist/schema/schemas/agent-env.d.ts.map +1 -1
  114. package/dist/schema/schemas/agent-env.js +51 -5
  115. package/dist/schema/schemas/agent-env.js.map +1 -1
  116. package/dist/schema/schemas/agentcore-project.d.ts +161 -5
  117. package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
  118. package/dist/schema/schemas/agentcore-project.js +159 -13
  119. package/dist/schema/schemas/agentcore-project.js.map +1 -1
  120. package/dist/schema/schemas/aws-targets.d.ts +3 -0
  121. package/dist/schema/schemas/aws-targets.d.ts.map +1 -1
  122. package/dist/schema/schemas/aws-targets.js +1 -0
  123. package/dist/schema/schemas/aws-targets.js.map +1 -1
  124. package/dist/schema/schemas/deployed-state.d.ts +165 -0
  125. package/dist/schema/schemas/deployed-state.d.ts.map +1 -1
  126. package/dist/schema/schemas/deployed-state.js +59 -1
  127. package/dist/schema/schemas/deployed-state.js.map +1 -1
  128. package/dist/schema/schemas/mcp.d.ts +2 -0
  129. package/dist/schema/schemas/mcp.d.ts.map +1 -1
  130. package/dist/schema/schemas/mcp.js +10 -2
  131. package/dist/schema/schemas/mcp.js.map +1 -1
  132. package/dist/schema/schemas/primitives/ab-test.d.ts +141 -0
  133. package/dist/schema/schemas/primitives/ab-test.d.ts.map +1 -0
  134. package/dist/schema/schemas/primitives/ab-test.js +97 -0
  135. package/dist/schema/schemas/primitives/ab-test.js.map +1 -0
  136. package/dist/schema/schemas/primitives/config-bundle.d.ts +31 -0
  137. package/dist/schema/schemas/primitives/config-bundle.d.ts.map +1 -0
  138. package/dist/schema/schemas/primitives/config-bundle.js +38 -0
  139. package/dist/schema/schemas/primitives/config-bundle.js.map +1 -0
  140. package/dist/schema/schemas/primitives/dataset.d.ts +48 -0
  141. package/dist/schema/schemas/primitives/dataset.d.ts.map +1 -0
  142. package/dist/schema/schemas/primitives/dataset.js +58 -0
  143. package/dist/schema/schemas/primitives/dataset.js.map +1 -0
  144. package/dist/schema/schemas/primitives/evaluator.d.ts +8 -0
  145. package/dist/schema/schemas/primitives/evaluator.d.ts.map +1 -1
  146. package/dist/schema/schemas/primitives/evaluator.js +17 -1
  147. package/dist/schema/schemas/primitives/evaluator.js.map +1 -1
  148. package/dist/schema/schemas/primitives/harness.d.ts +84 -8
  149. package/dist/schema/schemas/primitives/harness.d.ts.map +1 -1
  150. package/dist/schema/schemas/primitives/harness.js +29 -5
  151. package/dist/schema/schemas/primitives/harness.js.map +1 -1
  152. package/dist/schema/schemas/primitives/http-gateway.d.ts +21 -0
  153. package/dist/schema/schemas/primitives/http-gateway.d.ts.map +1 -0
  154. package/dist/schema/schemas/primitives/http-gateway.js +34 -0
  155. package/dist/schema/schemas/primitives/http-gateway.js.map +1 -0
  156. package/dist/schema/schemas/primitives/index.d.ts +9 -3
  157. package/dist/schema/schemas/primitives/index.d.ts.map +1 -1
  158. package/dist/schema/schemas/primitives/index.js +24 -1
  159. package/dist/schema/schemas/primitives/index.js.map +1 -1
  160. package/dist/schema/schemas/primitives/memory.d.ts +22 -4
  161. package/dist/schema/schemas/primitives/memory.d.ts.map +1 -1
  162. package/dist/schema/schemas/primitives/memory.js +57 -16
  163. package/dist/schema/schemas/primitives/memory.js.map +1 -1
  164. package/dist/schema/schemas/primitives/online-eval-config.d.ts +1 -0
  165. package/dist/schema/schemas/primitives/online-eval-config.d.ts.map +1 -1
  166. package/dist/schema/schemas/primitives/online-eval-config.js +2 -0
  167. package/dist/schema/schemas/primitives/online-eval-config.js.map +1 -1
  168. package/npm-shrinkwrap.json +16604 -0
  169. package/package.json +27 -15
  170. package/scripts/bump-version.ts +3 -3
  171. package/scripts/bundle.mjs +92 -66
  172. package/scripts/run-e2e-dev.sh +56 -0
  173. package/scripts/run-e2e-local.sh +112 -0
  174. package/dist/agent-inspector/index.js.map +0 -1
  175. package/dist/lib/errors/config.d.ts +0 -49
  176. package/dist/lib/errors/config.d.ts.map +0 -1
  177. package/dist/lib/errors/config.js.map +0 -1
  178. package/dist/lib/packaging/errors.d.ts +0 -16
  179. package/dist/lib/packaging/errors.d.ts.map +0 -1
  180. package/dist/lib/packaging/errors.js +0 -36
  181. package/dist/lib/packaging/errors.js.map +0 -1
  182. package/dist/lib/schemas/io/cli-config.d.ts +0 -12
  183. package/dist/lib/schemas/io/cli-config.d.ts.map +0 -1
  184. package/dist/lib/schemas/io/cli-config.js +0 -35
  185. package/dist/lib/schemas/io/cli-config.js.map +0 -1
@@ -1,7 +1,9 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`Dockerfile enableOtel rendering > renders opentelemetry-instrument CMD when enableOtel is true > Dockerfile-enableOtel-true 1`] = `
4
- "FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
4
+ "FROM public.ecr.aws/docker/library/python:3.12-slim-bookworm
5
+
6
+ RUN pip install --no-cache-dir uv
5
7
 
6
8
  ARG UV_DEFAULT_INDEX
7
9
  ARG UV_INDEX
@@ -39,7 +41,9 @@ CMD ["opentelemetry-instrument", "python", "-m", "main"]
39
41
  `;
40
42
 
41
43
  exports[`Dockerfile enableOtel rendering > renders plain python CMD when enableOtel is false > Dockerfile-enableOtel-false 1`] = `
42
- "FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
44
+ "FROM public.ecr.aws/docker/library/python:3.12-slim-bookworm
45
+
46
+ RUN pip install --no-cache-dir uv
43
47
 
44
48
  ARG UV_DEFAULT_INDEX
45
49
  ARG UV_INDEX
@@ -87,36 +87,22 @@ describe('Assets Directory Snapshots', () => {
87
87
  });
88
88
  });
89
89
 
90
- describe('Static assets', () => {
90
+ describe.skipIf(assetFiles.filter(f => f.startsWith('static/')).length === 0)('Static assets', () => {
91
91
  const staticFiles = assetFiles.filter(f => f.startsWith('static/'));
92
92
 
93
- if (staticFiles.length > 0) {
94
- it.each(staticFiles)('static/%s should match snapshot', file => {
95
- const content = readFileContent(path.join(ASSETS_DIR, file));
96
- expect(content).toMatchSnapshot();
97
- });
98
- } else {
99
- it('static directory is empty or does not exist', () => {
100
- // Static assets may not exist
101
- expect(true).toBe(true);
102
- });
103
- }
93
+ it.each(staticFiles)('static/%s should match snapshot', file => {
94
+ const content = readFileContent(path.join(ASSETS_DIR, file));
95
+ expect(content).toMatchSnapshot();
96
+ });
104
97
  });
105
98
 
106
- describe('TypeScript assets', () => {
99
+ describe.skipIf(assetFiles.filter(f => f.startsWith('typescript/')).length === 0)('TypeScript assets', () => {
107
100
  const tsFiles = assetFiles.filter(f => f.startsWith('typescript/'));
108
101
 
109
- if (tsFiles.length > 0) {
110
- it.each(tsFiles)('typescript/%s should match snapshot', file => {
111
- const content = readFileContent(path.join(ASSETS_DIR, file));
112
- expect(content).toMatchSnapshot();
113
- });
114
- } else {
115
- it('typescript directory is empty or contains only placeholder files', () => {
116
- // TypeScript assets may not exist yet
117
- expect(true).toBe(true);
118
- });
119
- }
102
+ it.each(tsFiles)('typescript/%s should match snapshot', file => {
103
+ const content = readFileContent(path.join(ASSETS_DIR, file));
104
+ expect(content).toMatchSnapshot();
105
+ });
120
106
  });
121
107
 
122
108
  describe('Root-level assets', () => {
@@ -55,9 +55,6 @@ async function main() {
55
55
  }
56
56
 
57
57
  // Read harness configs for role creation.
58
- // Harness fields may not yet be on the AgentCoreProjectSpec type from @aws/agentcore-cdk,
59
- // so we read them dynamically via specAny (same pattern as gateways above).
60
- // Harness paths in agentcore.json are relative to the project root (parent of agentcore/).
61
58
  const projectRoot = path.resolve(configRoot, '..');
62
59
  const harnessConfigs: {
63
60
  name: string;
@@ -65,11 +62,14 @@ async function main() {
65
62
  memoryName?: string;
66
63
  containerUri?: string;
67
64
  hasDockerfile?: boolean;
65
+ dockerfile?: string;
66
+ codeLocation?: string;
68
67
  tools?: { type: string; name: string }[];
69
68
  apiKeyArn?: string;
70
69
  }[] = [];
71
70
  for (const entry of specAny.harnesses ?? []) {
72
- const harnessPath = path.resolve(projectRoot, entry.path, 'harness.json');
71
+ const harnessDir = path.resolve(projectRoot, entry.path);
72
+ const harnessPath = path.resolve(harnessDir, 'harness.json');
73
73
  try {
74
74
  const harnessSpec = JSON.parse(fs.readFileSync(harnessPath, 'utf-8'));
75
75
  harnessConfigs.push({
@@ -78,6 +78,8 @@ async function main() {
78
78
  memoryName: harnessSpec.memory?.name,
79
79
  containerUri: harnessSpec.containerUri,
80
80
  hasDockerfile: !!harnessSpec.dockerfile,
81
+ dockerfile: harnessSpec.dockerfile,
82
+ codeLocation: harnessSpec.dockerfile ? harnessDir : undefined,
81
83
  tools: harnessSpec.tools,
82
84
  apiKeyArn: harnessSpec.model?.apiKeyArn,
83
85
  });
@@ -9,7 +9,7 @@
9
9
  "@aws-cdk/aws-ecs-patterns:secGroupsDisablesImplicitOpenListener": true,
10
10
  "@aws-cdk/aws-lambda:recognizeLayerVersion": true,
11
11
  "@aws-cdk/core:checkSecretUsage": true,
12
- "@aws-cdk/core:target-partitions": ["aws", "aws-cn"],
12
+ "@aws-cdk/core:target-partitions": ["aws", "aws-cn", "aws-us-gov"],
13
13
  "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
14
14
  "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
15
15
  "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
@@ -7,6 +7,18 @@ import {
7
7
  import { CfnOutput, Stack, type StackProps } from 'aws-cdk-lib';
8
8
  import { Construct } from 'constructs';
9
9
 
10
+ export interface HarnessConfig {
11
+ name: string;
12
+ executionRoleArn?: string;
13
+ memoryName?: string;
14
+ containerUri?: string;
15
+ hasDockerfile?: boolean;
16
+ dockerfile?: string;
17
+ codeLocation?: string;
18
+ tools?: { type: string; name: string }[];
19
+ apiKeyArn?: string;
20
+ }
21
+
10
22
  export interface AgentCoreStackProps extends StackProps {
11
23
  /**
12
24
  * The AgentCore project specification containing agents, memories, and credentials.
@@ -21,17 +33,9 @@ export interface AgentCoreStackProps extends StackProps {
21
33
  */
22
34
  credentials?: Record<string, { credentialProviderArn: string; clientSecretArn?: string }>;
23
35
  /**
24
- * Harness role configurations. Each entry creates an IAM execution role for a harness.
36
+ * Harness role configurations.
25
37
  */
26
- harnesses?: {
27
- name: string;
28
- executionRoleArn?: string;
29
- memoryName?: string;
30
- containerUri?: string;
31
- hasDockerfile?: boolean;
32
- tools?: { type: string; name: string }[];
33
- apiKeyArn?: string;
34
- }[];
38
+ harnesses?: HarnessConfig[];
35
39
  }
36
40
 
37
41
  /**
@@ -50,10 +54,12 @@ export class AgentCoreStack extends Stack {
50
54
  const { spec, mcpSpec, credentials, harnesses } = props;
51
55
 
52
56
  // Create AgentCoreApplication with all agents and harness roles
53
- this.application = new AgentCoreApplication(this, 'Application', {
54
- spec,
55
- harnesses,
56
- });
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ const appProps: Record<string, unknown> = { spec };
59
+ if (harnesses?.length) {
60
+ appProps.harnesses = harnesses;
61
+ }
62
+ this.application = new AgentCoreApplication(this, 'Application', appProps as any);
57
63
 
58
64
  // Create AgentCoreMcp if there are gateways configured
59
65
  if (mcpSpec?.agentCoreGateways && mcpSpec.agentCoreGateways.length > 0) {
@@ -18,6 +18,7 @@ test('AgentCoreStack synthesizes with empty spec', () => {
18
18
  agentCoreGateways: [],
19
19
  mcpRuntimeTools: [],
20
20
  unassignedTargets: [],
21
+ datasets: [],
21
22
  },
22
23
  });
23
24
  const template = Template.fromStack(stack);
@@ -1,4 +1,6 @@
1
- FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
1
+ FROM public.ecr.aws/docker/library/python:3.12-slim-bookworm
2
+
3
+ RUN pip install --no-cache-dir uv
2
4
 
3
5
  ARG UV_DEFAULT_INDEX
4
6
  ARG UV_INDEX
@@ -0,0 +1,25 @@
1
+ FROM public.ecr.aws/docker/library/node:22-slim
2
+
3
+ WORKDIR /app
4
+
5
+ ENV NODE_ENV=production \
6
+ DOCKER_CONTAINER=1
7
+
8
+ RUN userdel -r node 2>/dev/null || true
9
+ RUN useradd -m -u 1000 bedrock_agentcore
10
+
11
+ COPY package.json package-lock.json* ./
12
+ RUN npm ci --omit=dev || npm install --omit=dev
13
+
14
+ COPY --chown=bedrock_agentcore:bedrock_agentcore . .
15
+
16
+ USER bedrock_agentcore
17
+
18
+ # AgentCore Runtime service contract ports
19
+ # https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-service-contract.html
20
+ # 8080: HTTP Mode
21
+ # 8000: MCP Mode
22
+ # 9000: A2A Mode
23
+ EXPOSE 8080 8000 9000
24
+
25
+ CMD ["npx", "tsx", "main.ts"]
@@ -0,0 +1,24 @@
1
+ # Node
2
+ node_modules/
3
+ dist/
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+
8
+ # IDE
9
+ .vscode/
10
+ .idea/
11
+
12
+ # Testing
13
+ coverage/
14
+
15
+ # Secrets and environment files
16
+ .env
17
+ .env.*
18
+
19
+ # Version control
20
+ .git/
21
+
22
+ # AgentCore build artifacts
23
+ .agentcore/artifacts/
24
+ *.zip
@@ -0,0 +1,3 @@
1
+ {"scenario_id": "refund-policy", "turns": [{"input": "What is your refund policy?", "expectedResponse": "We offer full refunds within 30 days of purchase. After 30 days, we can provide store credit."}, {"input": "What if I lost my receipt?", "expectedResponse": "No problem! We can look up your purchase using your email address or payment method."}], "assertions": ["Agent should clearly state the 30-day refund window", "Agent should offer alternatives for lost receipts"], "expected_trajectory": ["lookup_policy", "check_eligibility"]}
2
+ {"scenario_id": "order-tracking", "turns": [{"input": "Where is my order #12345?", "expectedResponse": "Let me look up order #12345 for you. I can see it shipped on Monday and is expected to arrive by Thursday."}], "assertions": ["Agent should reference the specific order number", "Agent should provide estimated delivery date"], "expected_trajectory": ["lookup_order", "get_shipping_status"]}
3
+ {"scenario_id": "account-locked", "turns": [{"input": "I can't log into my account", "expectedResponse": "I'm sorry you're having trouble logging in. Let me help you regain access. Can you provide the email address associated with your account?"}, {"input": "It's john@example.com", "expectedResponse": "I've sent a password reset link to john@example.com. Please check your inbox and spam folder. The link expires in 24 hours."}], "assertions": ["Agent should ask for identifying information", "Agent should explain the reset process clearly"], "expected_trajectory": ["verify_identity", "send_reset_link"]}
@@ -0,0 +1,3 @@
1
+ {"scenario_id": "frustrated-refund-customer", "input": "I want a refund for my cancelled flight BK-98765", "actor_profile": {"traits": {"personality": "impatient and frustrated", "communication_style": "direct and demanding"}, "context": "Has been waiting 3 days for a refund with no response. Previously had a bad experience with customer service.", "goal": "Get a full cash refund for cancelled flight BK-98765, not a voucher or credit"}, "max_turns": 10, "assertions": ["Agent should acknowledge the frustration", "Agent should not offer only vouchers when cash refund is requested", "Agent should provide a timeline for the refund"]}
2
+ {"scenario_id": "confused-new-user", "input": "How do I set up my new account?", "actor_profile": {"traits": {"personality": "polite but confused", "communication_style": "asks many follow-up questions", "technical_level": "beginner"}, "context": "First time using the service. Not familiar with technical terminology. English is a second language.", "goal": "Successfully create and configure a new account with basic settings"}, "max_turns": 15, "assertions": ["Agent should use simple non-technical language", "Agent should break instructions into small steps", "Agent should confirm understanding at each step"]}
3
+ {"scenario_id": "edge-case-multi-issue", "input": "I need to change my flight AND get a refund for the hotel that was bundled with it", "actor_profile": {"traits": {"personality": "methodical and detail-oriented", "communication_style": "provides lots of context upfront"}, "context": "Has a bundled flight+hotel booking. Flight was changed by the airline, making the hotel dates wrong. Wants partial refund for hotel and rebooking for flight.", "goal": "Get the flight rebooked to new dates AND get a refund for the hotel nights that no longer align"}, "max_turns": 12, "assertions": ["Agent should handle both issues without losing track", "Agent should clarify which parts are refundable", "Agent should not close the conversation until both issues are resolved"]}
@@ -4,7 +4,7 @@
4
4
  {
5
5
  "Effect": "Allow",
6
6
  "Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
7
- "Resource": "arn:aws:logs:*:*:log-group:/aws/lambda/*"
7
+ "Resource": "arn:*:logs:*:*:log-group:/aws/lambda/*"
8
8
  }
9
9
  ]
10
10
  }
@@ -9,10 +9,10 @@ description = "AgentCore A2A Agent using Google ADK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
- "a2a-sdk >= 0.2.0",
12
+ "a2a-sdk >= 0.2.0, < 1.0.0",
13
13
  "aws-opentelemetry-distro",
14
14
  "bedrock-agentcore[a2a] >= 1.0.3",
15
- "google-adk >= 1.0.0",
15
+ "google-adk >= 1.0.0, < 2.0.0",
16
16
  "google-genai >= 1.0.0",
17
17
  ]
18
18
 
@@ -9,7 +9,7 @@ description = "AgentCore A2A Agent using LangChain + LangGraph"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
- "a2a-sdk >= 0.2.0",
12
+ "a2a-sdk >= 0.2.0, < 1.0.0",
13
13
  {{#if (eq modelProvider "Anthropic")}}"langchain-anthropic >= 0.3.0",
14
14
  {{/if}}{{#if (eq modelProvider "Bedrock")}}"langchain-aws >= 0.2.0",
15
15
  {{/if}}{{#if (eq modelProvider "Gemini")}}"langchain-google-genai >= 2.0.0",
@@ -10,9 +10,9 @@ readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
12
  {{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
13
- {{/if}}"a2a-sdk[all] >= 0.2.0",
13
+ {{/if}}"a2a-sdk[all] >= 0.2.0, < 1.0.0",
14
14
  "aws-opentelemetry-distro",
15
- "bedrock-agentcore[a2a] >= 1.0.3",
15
+ "bedrock-agentcore[a2a] >= 1.9.1",
16
16
  "botocore[crt] >= 1.35.0",
17
17
  {{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
18
18
  {{/if}}{{#if (eq modelProvider "OpenAI")}}"openai >= 1.0.0",
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import uuid
2
3
  from typing import Optional
3
4
 
4
5
  from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig{{#if memoryProviders.[0].strategies.length}}, RetrievalConfig{{/if}}
@@ -7,10 +8,14 @@ from bedrock_agentcore.memory.integrations.strands.session_manager import AgentC
7
8
  MEMORY_ID = os.getenv("{{memoryProviders.[0].envVarName}}")
8
9
  REGION = os.getenv("AWS_REGION")
9
10
 
10
- def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
11
+ def get_memory_session_manager(session_id: Optional[str], actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
11
12
  if not MEMORY_ID:
12
13
  return None
13
14
 
15
+ # AgentCoreMemoryConfig rejects None; OAuth/CUSTOM_JWT callers can reach us
16
+ # without a runtime session header, so synthesize one when absent.
17
+ session_id = session_id or uuid.uuid4().hex
18
+
14
19
  {{#if memoryProviders.[0].strategies.length}}
15
20
  retrieval_config = {
16
21
  {{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}
@@ -20,7 +25,7 @@ def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[Agent
20
25
  f"/users/{actor_id}/preferences": RetrievalConfig(top_k=3, relevance_score=0.5),
21
26
  {{/if}}
22
27
  {{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
23
- f"/summaries/{actor_id}/{session_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
28
+ f"/summaries/{actor_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
24
29
  {{/if}}
25
30
  }
26
31
  {{/if}}
@@ -13,7 +13,7 @@ dependencies = [
13
13
  "ag-ui-protocol >= 0.1.10",
14
14
  "bedrock-agentcore >= 1.0.3",
15
15
  "fastapi >= 0.115.12",
16
- "google-adk >= 1.16.0",
16
+ "google-adk >= 1.16.0, < 2.0.0",
17
17
  "google-genai >= 1.0.0",
18
18
  "opentelemetry-distro",
19
19
  "opentelemetry-exporter-otlp",
@@ -13,7 +13,7 @@ dependencies = [
13
13
  {{/if}}"ag-ui-strands >= 0.1.7",
14
14
  "ag-ui-protocol >= 0.1.10",
15
15
  "aws-opentelemetry-distro",
16
- "bedrock-agentcore >= 1.0.3",
16
+ "bedrock-agentcore >= 1.9.1",
17
17
  "botocore[crt] >= 1.35.0",
18
18
  "fastapi >= 0.115.12",
19
19
  {{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
@@ -1,4 +1,5 @@
1
1
  import os
2
+ import uuid
2
3
  from typing import Optional
3
4
 
4
5
  from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig{{#if memoryProviders.[0].strategies.length}}, RetrievalConfig{{/if}}
@@ -7,10 +8,14 @@ from bedrock_agentcore.memory.integrations.strands.session_manager import AgentC
7
8
  MEMORY_ID = os.getenv("{{memoryProviders.[0].envVarName}}")
8
9
  REGION = os.getenv("AWS_REGION")
9
10
 
10
- def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
11
+ def get_memory_session_manager(session_id: Optional[str], actor_id: str) -> Optional[AgentCoreMemorySessionManager]:
11
12
  if not MEMORY_ID:
12
13
  return None
13
14
 
15
+ # AgentCoreMemoryConfig rejects None; OAuth/CUSTOM_JWT callers can reach us
16
+ # without a runtime session header, so synthesize one when absent.
17
+ session_id = session_id or uuid.uuid4().hex
18
+
14
19
  {{#if memoryProviders.[0].strategies.length}}
15
20
  retrieval_config = {
16
21
  {{#if (includes memoryProviders.[0].strategies "SEMANTIC")}}
@@ -20,7 +25,7 @@ def get_memory_session_manager(session_id: str, actor_id: str) -> Optional[Agent
20
25
  f"/users/{actor_id}/preferences": RetrievalConfig(top_k=3, relevance_score=0.5),
21
26
  {{/if}}
22
27
  {{#if (includes memoryProviders.[0].strategies "SUMMARIZATION")}}
23
- f"/summaries/{actor_id}/{session_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
28
+ f"/summaries/{actor_id}": RetrievalConfig(top_k=3, relevance_score=0.5),
24
29
  {{/if}}
25
30
  }
26
31
  {{/if}}
@@ -11,7 +11,7 @@ requires-python = ">=3.10"
11
11
  dependencies = [
12
12
  "opentelemetry-distro",
13
13
  "opentelemetry-exporter-otlp",
14
- "google-adk >= 1.17.0",
14
+ "google-adk >= 1.17.0, < 2.0.0",
15
15
  "bedrock-agentcore >= 1.0.3",
16
16
  "botocore[crt] >= 1.35.0",
17
17
  {{#if hasGateway}}{{#if (includes gatewayAuthTypes "AWS_IAM")}}"mcp-proxy-for-aws >= 1.1.0",
@@ -1,7 +1,13 @@
1
1
  import os
2
- from langchain_core.messages import HumanMessage
2
+ from typing import Any
3
+
4
+ from langchain_core.messages import HumanMessage{{#if hasConfigBundle}}, SystemMessage{{/if}}
3
5
  from langgraph.prebuilt import create_react_agent
4
6
  from langchain.tools import tool
7
+ {{#if hasConfigBundle}}
8
+ from langchain_core.callbacks import BaseCallbackHandler
9
+ from bedrock_agentcore.runtime.context import BedrockAgentCoreContext
10
+ {{/if}}
5
11
  from opentelemetry.instrumentation.langchain import LangchainInstrumentor
6
12
  from bedrock_agentcore.runtime import BedrockAgentCoreApp
7
13
  from model.load import load_model
@@ -25,6 +31,14 @@ def get_or_create_model():
25
31
  return _llm
26
32
 
27
33
 
34
+ DEFAULT_SYSTEM_PROMPT = """
35
+ You are a helpful assistant. Use tools when appropriate.
36
+ {{#if sessionStorageMountPath}}
37
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
38
+ {{/if}}
39
+ """
40
+
41
+
28
42
  # Define a simple function tool
29
43
  @tool
30
44
  def add_numbers(a: int, b: int) -> int:
@@ -88,13 +102,28 @@ def list_files(directory: str = "") -> str:
88
102
  tools.extend([file_read, file_write, list_files])
89
103
  {{/if}}
90
104
 
91
- SYSTEM_PROMPT = """
92
- You are a helpful assistant. Use tools when appropriate.
93
- {{#if sessionStorageMountPath}}
94
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
95
- {{/if}}
96
- """
105
+ {{#if hasConfigBundle}}
106
+
107
+ class ConfigBundleCallback(BaseCallbackHandler):
108
+ """Injects config bundle values into LangGraph agent at runtime.
109
+
110
+ BedrockAgentCoreContext.get_config_bundle() fetches the component configuration
111
+ for the current runtime ARN from the config bundle service. The SDK caches the
112
+ result and refreshes on bundle version changes.
113
+ """
114
+
115
+ def on_chain_start(self, serialized: dict, inputs: dict, **kwargs: Any) -> None:
116
+ config = BedrockAgentCoreContext.get_config_bundle()
117
+ prompt = config.get("systemPrompt", DEFAULT_SYSTEM_PROMPT)
97
118
 
119
+ messages = inputs.get("messages", [])
120
+ if messages and isinstance(messages[0], SystemMessage):
121
+ messages[0] = SystemMessage(content=prompt)
122
+ else:
123
+ messages.insert(0, SystemMessage(content=prompt))
124
+ inputs["messages"] = messages
125
+
126
+ {{/if}}
98
127
 
99
128
  @app.entrypoint
100
129
  async def invoke(payload, context):
@@ -113,7 +142,21 @@ async def invoke(payload, context):
113
142
  mcp_tools = await mcp_client.get_tools()
114
143
 
115
144
  # Define the agent using create_react_agent
116
- graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools, prompt=SYSTEM_PROMPT)
145
+ {{#if hasConfigBundle}}
146
+ graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools, prompt=DEFAULT_SYSTEM_PROMPT)
147
+ callback = ConfigBundleCallback()
148
+
149
+ # Process the user prompt
150
+ prompt = payload.get("prompt", "What can you help me with?")
151
+ log.info(f"Agent input: {prompt}")
152
+
153
+ # Run the agent with config bundle callback
154
+ result = await graph.ainvoke(
155
+ {"messages": [HumanMessage(content=prompt)]},
156
+ config={"callbacks": [callback]},
157
+ )
158
+ {{else}}
159
+ graph = create_react_agent(get_or_create_model(), tools=mcp_tools + tools, prompt=DEFAULT_SYSTEM_PROMPT)
117
160
 
118
161
  # Process the user prompt
119
162
  prompt = payload.get("prompt", "What can you help me with?")
@@ -121,6 +164,7 @@ async def invoke(payload, context):
121
164
 
122
165
  # Run the agent
123
166
  result = await graph.ainvoke({"messages": [HumanMessage(content=prompt)]})
167
+ {{/if}}
124
168
 
125
169
  # Return result
126
170
  output = result["messages"][-1].content
@@ -1,4 +1,10 @@
1
+ from typing import Any
2
+
1
3
  from strands import Agent, tool
4
+ {{#if hasConfigBundle}}
5
+ from strands.hooks import HookProvider, HookRegistry, BeforeInvocationEvent, BeforeToolCallEvent
6
+ from bedrock_agentcore.runtime.context import BedrockAgentCoreContext
7
+ {{/if}}
2
8
  from bedrock_agentcore.runtime import BedrockAgentCoreApp
3
9
  from model.load import load_model
4
10
  {{#if hasGateway}}
@@ -23,11 +29,26 @@ mcp_clients = get_all_gateway_mcp_clients()
23
29
  mcp_clients = [get_streamable_http_mcp_client()]
24
30
  {{/if}}
25
31
 
32
+ DEFAULT_SYSTEM_PROMPT = """
33
+ You are a helpful assistant. Use tools when appropriate.
34
+ {{#if sessionStorageMountPath}}
35
+ You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
36
+ {{/if}}
37
+ """
38
+
39
+ {{#if hasConfigBundle}}
40
+ DEFAULT_TOOL_DESC = "Return the sum of two numbers"
41
+ {{/if}}
42
+
26
43
  # Define a collection of tools used by the model
27
44
  tools = []
28
45
 
29
46
  # Define a simple function tool
47
+ {{#if hasConfigBundle}}
48
+ @tool(description=DEFAULT_TOOL_DESC)
49
+ {{else}}
30
50
  @tool
51
+ {{/if}}
31
52
  def add_numbers(a: int, b: int) -> int:
32
53
  """Return the sum of two numbers"""
33
54
  return a+b
@@ -91,12 +112,39 @@ for mcp_client in mcp_clients:
91
112
  if mcp_client:
92
113
  tools.append(mcp_client)
93
114
 
94
- SYSTEM_PROMPT = """
95
- You are a helpful assistant. Use tools when appropriate.
96
- {{#if sessionStorageMountPath}}
97
- You have persistent storage at {{sessionStorageMountPath}}. Use file tools to read and write files. Data persists across sessions.
115
+ {{#if hasConfigBundle}}
116
+
117
+ class ConfigBundleHook(HookProvider):
118
+ """Injects config bundle values (system prompt, tool descriptions) before each invocation.
119
+
120
+ BedrockAgentCoreContext.get_config_bundle() fetches the component configuration
121
+ for the current runtime ARN from the config bundle service. The SDK caches the
122
+ result and refreshes on bundle version changes.
123
+ """
124
+
125
+ def register_hooks(self, registry: HookRegistry, **kwargs: Any) -> None:
126
+ registry.add_callback(BeforeInvocationEvent, self._inject_system_prompt)
127
+ registry.add_callback(BeforeToolCallEvent, self._override_tool_desc)
128
+
129
+ def _inject_system_prompt(self, event: BeforeInvocationEvent) -> None:
130
+ config = BedrockAgentCoreContext.get_config_bundle()
131
+ prompt = config.get("systemPrompt", DEFAULT_SYSTEM_PROMPT)
132
+
133
+ if prompt != event.agent.system_prompt:
134
+ event.agent.system_prompt = prompt
135
+
136
+ def _override_tool_desc(self, event: BeforeToolCallEvent) -> None:
137
+ config = BedrockAgentCoreContext.get_config_bundle()
138
+ tool_descs = config.get("toolDescriptions", {})
139
+
140
+ tool_name = event.tool_use["name"]
141
+ override = tool_descs.get(tool_name)
142
+ if override and event.selected_tool:
143
+ spec = event.selected_tool.tool_spec
144
+ if spec and "description" in spec:
145
+ spec["description"] = override
146
+
98
147
  {{/if}}
99
- """
100
148
 
101
149
  {{#if hasMemory}}
102
150
  def agent_factory():
@@ -108,13 +156,23 @@ def agent_factory():
108
156
  cache[key] = Agent(
109
157
  model=load_model(),
110
158
  session_manager=get_memory_session_manager(session_id, user_id),
111
- system_prompt=SYSTEM_PROMPT,
112
- tools=tools
159
+ system_prompt=DEFAULT_SYSTEM_PROMPT,
160
+ tools=tools{{#if hasConfigBundle}},
161
+ hooks=[ConfigBundleHook()]{{/if}}
113
162
  )
114
163
  return cache[key]
115
164
  return get_or_create_agent
116
165
  get_or_create_agent = agent_factory()
117
166
  {{else}}
167
+ {{#if hasConfigBundle}}
168
+ def create_agent():
169
+ return Agent(
170
+ model=load_model(),
171
+ system_prompt=DEFAULT_SYSTEM_PROMPT,
172
+ tools=tools,
173
+ hooks=[ConfigBundleHook()],
174
+ )
175
+ {{else}}
118
176
  _agent = None
119
177
 
120
178
  def get_or_create_agent():
@@ -122,11 +180,12 @@ def get_or_create_agent():
122
180
  if _agent is None:
123
181
  _agent = Agent(
124
182
  model=load_model(),
125
- system_prompt=SYSTEM_PROMPT,
183
+ system_prompt=DEFAULT_SYSTEM_PROMPT,
126
184
  tools=tools
127
185
  )
128
186
  return _agent
129
187
  {{/if}}
188
+ {{/if}}
130
189
 
131
190
 
132
191
  @app.entrypoint
@@ -137,8 +196,12 @@ async def invoke(payload, context):
137
196
  session_id = getattr(context, 'session_id', 'default-session')
138
197
  user_id = getattr(context, 'user_id', 'default-user')
139
198
  agent = get_or_create_agent(session_id, user_id)
199
+ {{else}}
200
+ {{#if hasConfigBundle}}
201
+ agent = create_agent()
140
202
  {{else}}
141
203
  agent = get_or_create_agent()
204
+ {{/if}}
142
205
  {{/if}}
143
206
 
144
207
  # Execute and format response
@@ -11,7 +11,7 @@ requires-python = ">=3.10"
11
11
  dependencies = [
12
12
  {{#if (eq modelProvider "Anthropic")}}"anthropic >= 0.30.0",
13
13
  {{/if}}"aws-opentelemetry-distro",
14
- "bedrock-agentcore >= 1.0.3",
14
+ "bedrock-agentcore >= 1.9.1",
15
15
  "botocore[crt] >= 1.35.0",
16
16
  {{#if (eq modelProvider "Gemini")}}"google-genai >= 1.0.0",
17
17
  {{/if}}"mcp >= 1.19.0",