@aws/agentcore 1.0.0-preview.12 → 1.0.0-preview.13

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 (48) hide show
  1. package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +414 -14
  2. package/dist/assets/cdk/bin/cdk.ts +49 -5
  3. package/dist/assets/cdk/lib/cdk-stack.ts +162 -1
  4. package/dist/assets/cdk/test/cdk.test.ts +2 -0
  5. package/dist/assets/python/a2a/langchain_langgraph/base/pyproject.toml +1 -1
  6. package/dist/assets/python/agui/googleadk/base/pyproject.toml +1 -1
  7. package/dist/assets/python/agui/langchain_langgraph/base/pyproject.toml +1 -1
  8. package/dist/assets/python/http/autogen/base/pyproject.toml +1 -1
  9. package/dist/assets/python/http/langchain_langgraph/base/pyproject.toml +1 -1
  10. package/dist/assets/python/http/openaiagents/base/pyproject.toml +1 -1
  11. package/dist/assets/python/http/strands/base/main.py +39 -2
  12. package/dist/assets/python/http/strands/capabilities/payments/__init__.py +4 -0
  13. package/dist/assets/python/http/strands/capabilities/payments/payments.py +142 -0
  14. package/dist/cli/index.mjs +571 -505
  15. package/dist/lib/packaging/helpers.d.ts.map +1 -1
  16. package/dist/lib/packaging/helpers.js +50 -10
  17. package/dist/lib/packaging/helpers.js.map +1 -1
  18. package/dist/lib/utils/env.d.ts +4 -0
  19. package/dist/lib/utils/env.d.ts.map +1 -1
  20. package/dist/lib/utils/env.js +18 -0
  21. package/dist/lib/utils/env.js.map +1 -1
  22. package/dist/lib/utils/index.d.ts +1 -1
  23. package/dist/lib/utils/index.d.ts.map +1 -1
  24. package/dist/lib/utils/index.js +2 -1
  25. package/dist/lib/utils/index.js.map +1 -1
  26. package/dist/schema/schemas/agentcore-project.d.ts +57 -2
  27. package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
  28. package/dist/schema/schemas/agentcore-project.js +61 -5
  29. package/dist/schema/schemas/agentcore-project.js.map +1 -1
  30. package/dist/schema/schemas/deployed-state.d.ts +97 -0
  31. package/dist/schema/schemas/deployed-state.d.ts.map +1 -1
  32. package/dist/schema/schemas/deployed-state.js +24 -4
  33. package/dist/schema/schemas/deployed-state.js.map +1 -1
  34. package/dist/schema/schemas/primitives/harness.d.ts +33 -0
  35. package/dist/schema/schemas/primitives/harness.d.ts.map +1 -1
  36. package/dist/schema/schemas/primitives/harness.js +42 -1
  37. package/dist/schema/schemas/primitives/harness.js.map +1 -1
  38. package/dist/schema/schemas/primitives/index.d.ts +4 -2
  39. package/dist/schema/schemas/primitives/index.d.ts.map +1 -1
  40. package/dist/schema/schemas/primitives/index.js +14 -2
  41. package/dist/schema/schemas/primitives/index.js.map +1 -1
  42. package/dist/schema/schemas/primitives/payment.d.ts +54 -0
  43. package/dist/schema/schemas/primitives/payment.d.ts.map +1 -0
  44. package/dist/schema/schemas/primitives/payment.js +79 -0
  45. package/dist/schema/schemas/primitives/payment.js.map +1 -0
  46. package/npm-shrinkwrap.json +748 -342
  47. package/package.json +2 -1
  48. package/scripts/run-e2e-local.sh +9 -3
@@ -73,12 +73,14 @@ async function main() {
73
73
  const spec = await configIO.readProjectSpec();
74
74
  const targets = await configIO.readAWSDeploymentTargets();
75
75
 
76
- // Extract MCP configuration from project spec.
77
- // Gateway fields are stored in agentcore.json but may not yet be on the
78
- // AgentCoreProjectSpec type from @aws/agentcore-cdk, so we read them
79
- // dynamically and cast the resulting object.
76
+ // The vended CDK project compiles against the published @aws/agentcore-cdk
77
+ // schema type, which may lag the CLI's own AgentCoreProjectSpec (e.g. payments,
78
+ // harnesses, gateway fields). Cast once so those fields are reachable.
80
79
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
80
  const specAny = spec as any;
81
+
82
+ // Extract MCP configuration from project spec.
83
+ // Gateway fields are stored in agentcore.json but may not yet be on the
82
84
  const mcpSpec = specAny.agentCoreGateways?.length
83
85
  ? {
84
86
  agentCoreGateways: specAny.agentCoreGateways,
@@ -113,6 +115,7 @@ async function main() {
113
115
  apiKeyArn?: string;
114
116
  efsAccessPoints?: { accessPointArn: string; mountPath: string }[];
115
117
  s3AccessPoints?: { accessPointArn: string; mountPath: string }[];
118
+ apiFormat?: 'converse_stream' | 'responses' | 'chat_completions';
116
119
  }[] = [];
117
120
  for (const entry of specAny.harnesses ?? []) {
118
121
  const harnessDir = path.resolve(projectRoot, entry.path);
@@ -131,6 +134,7 @@ async function main() {
131
134
  apiKeyArn: harnessSpec.model?.apiKeyArn,
132
135
  efsAccessPoints: harnessSpec.efsAccessPoints,
133
136
  s3AccessPoints: harnessSpec.s3AccessPoints,
137
+ apiFormat: harnessSpec.model?.apiFormat,
134
138
  });
135
139
  } catch (err) {
136
140
  throw new Error(
@@ -154,11 +158,51 @@ async function main() {
154
158
  | Record<string, { credentialProviderArn: string; clientSecretArn?: string }>
155
159
  | undefined;
156
160
 
161
+ // Payment credential provider ARNs live in the same credentials map as identity credentials
162
+ const paymentCredentials = credentials;
163
+
164
+ const paymentSpec = specAny.payments?.length
165
+ ? specAny.payments.map(
166
+ (p: {
167
+ name: string;
168
+ description?: string;
169
+ authorizerType: 'AWS_IAM' | 'CUSTOM_JWT';
170
+ authorizerConfiguration?: unknown;
171
+ autoPayment?: boolean;
172
+ paymentToolAllowlist?: string[];
173
+ networkPreferences?: string[];
174
+ connectors: { name: string; provider?: string; credentialName: string }[];
175
+ }) => ({
176
+ name: p.name,
177
+ description: p.description,
178
+ authorizerType: p.authorizerType,
179
+ authorizerConfiguration: p.authorizerConfiguration,
180
+ autoPayment: p.autoPayment,
181
+ paymentToolAllowlist: p.paymentToolAllowlist,
182
+ networkPreferences: p.networkPreferences,
183
+ connectors: p.connectors.map(c => {
184
+ const credentialProviderArn = paymentCredentials?.[c.credentialName]?.credentialProviderArn;
185
+ if (!credentialProviderArn) {
186
+ // Fail fast with an actionable message rather than passing an empty
187
+ // ARN that fails opaquely server-side at CreatePaymentConnector.
188
+ throw new Error(
189
+ \`Payment connector "\${c.name}" on manager "\${p.name}" references credential \` +
190
+ \`"\${c.credentialName}", but no deployed credential provider was found for it. \` +
191
+ \`Run \\\`agentcore deploy\\\` so the credential provider is created first.\`
192
+ );
193
+ }
194
+ return { name: c.name, provider: c.provider, credentialProviderArn };
195
+ }),
196
+ })
197
+ )
198
+ : undefined;
199
+
157
200
  new AgentCoreStack(app, stackName, {
158
201
  spec,
159
202
  mcpSpec,
160
203
  credentials,
161
204
  harnesses: harnessConfigs.length > 0 ? harnessConfigs : undefined,
205
+ paymentSpec,
162
206
  env,
163
207
  description: \`AgentCore stack for \${spec.name} deployed to \${target.name} (\${target.region})\`,
164
208
  tags: {
@@ -173,7 +217,7 @@ async function main() {
173
217
 
174
218
  main().catch((error: unknown) => {
175
219
  console.error('AgentCore CDK synthesis failed:', error instanceof Error ? error.message : error);
176
- process.exitCode = 1;
220
+ process.exit(1);
177
221
  });
178
222
  "
179
223
  `;
@@ -300,10 +344,14 @@ exports[`Assets Directory Snapshots > CDK assets > cdk/cdk/lib/cdk-stack.ts shou
300
344
  "import {
301
345
  AgentCoreApplication,
302
346
  AgentCoreMcp,
347
+ AgentCorePaymentManager,
348
+ AgentCorePaymentConnector,
303
349
  type AgentCoreProjectSpec,
304
350
  type AgentCoreMcpSpec,
351
+ type CustomJWTAuthorizerConfig,
305
352
  } from '@aws/agentcore-cdk';
306
353
  import { CfnOutput, Stack, type StackProps } from 'aws-cdk-lib';
354
+ import * as iam from 'aws-cdk-lib/aws-iam';
307
355
  import { Construct } from 'constructs';
308
356
 
309
357
  export interface HarnessConfig {
@@ -320,6 +368,23 @@ export interface HarnessConfig {
320
368
  s3AccessPoints?: { accessPointArn: string; mountPath: string }[];
321
369
  }
322
370
 
371
+ export interface PaymentConnectorSpec {
372
+ name: string;
373
+ provider: 'CoinbaseCDP' | 'StripePrivy';
374
+ credentialProviderArn: string;
375
+ }
376
+
377
+ export interface PaymentSpec {
378
+ name: string;
379
+ description?: string;
380
+ authorizerType: 'AWS_IAM' | 'CUSTOM_JWT';
381
+ authorizerConfiguration?: { customJWTAuthorizer: CustomJWTAuthorizerConfig };
382
+ autoPayment?: boolean;
383
+ paymentToolAllowlist?: string[];
384
+ networkPreferences?: string[];
385
+ connectors: PaymentConnectorSpec[];
386
+ }
387
+
323
388
  export interface AgentCoreStackProps extends StackProps {
324
389
  /**
325
390
  * The AgentCore project specification containing agents, memories, and credentials.
@@ -337,6 +402,30 @@ export interface AgentCoreStackProps extends StackProps {
337
402
  * Harness role configurations.
338
403
  */
339
404
  harnesses?: HarnessConfig[];
405
+ /**
406
+ * Payment specifications with resolved credential provider ARNs.
407
+ */
408
+ paymentSpec?: PaymentSpec[];
409
+ }
410
+
411
+ function toCdkId(name: string): string {
412
+ return name.replace(/_/g, '');
413
+ }
414
+
415
+ /**
416
+ * Decide whether a deployed runtime should receive payment env vars + IAM grants.
417
+ * Payments today only ships a runtime shim for Python HTTP runtimes; injecting
418
+ * AGENTCORE_PAYMENT_* env vars into TypeScript / MCP / A2A / AGUI runtimes
419
+ * would surface env vars they cannot consume and would dilute least-privilege
420
+ * IAM grants for runtimes that never call ProcessPayment.
421
+ */
422
+ function isPaymentEligibleAgent(agent: { entrypoint?: string; protocol?: string }): boolean {
423
+ if (agent.protocol && agent.protocol !== 'HTTP') {
424
+ return false;
425
+ }
426
+ const entrypoint = typeof agent.entrypoint === 'string' ? agent.entrypoint : '';
427
+ const entrypointFile = entrypoint.split(':')[0] ?? '';
428
+ return entrypointFile.endsWith('.py');
340
429
  }
341
430
 
342
431
  /**
@@ -352,7 +441,7 @@ export class AgentCoreStack extends Stack {
352
441
  constructor(scope: Construct, id: string, props: AgentCoreStackProps) {
353
442
  super(scope, id, props);
354
443
 
355
- const { spec, mcpSpec, credentials, harnesses } = props;
444
+ const { spec, mcpSpec, credentials, harnesses, paymentSpec } = props;
356
445
 
357
446
  // Create AgentCoreApplication with all agents and harness roles
358
447
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -373,6 +462,122 @@ export class AgentCoreStack extends Stack {
373
462
  });
374
463
  }
375
464
 
465
+ // Create payment infrastructure via CFN constructs
466
+ if (paymentSpec && paymentSpec.length > 0) {
467
+ for (const payment of paymentSpec) {
468
+ const mgrId = toCdkId(payment.name);
469
+ const manager = new AgentCorePaymentManager(this, \`Payment\${mgrId}\`, {
470
+ projectName: spec.name,
471
+ name: payment.name,
472
+ authorizerType: payment.authorizerType,
473
+ description: payment.description,
474
+ authorizerConfiguration: payment.authorizerConfiguration,
475
+ tags: spec.tags,
476
+ });
477
+
478
+ const prefix = \`AGENTCORE_PAYMENT_\${payment.name.toUpperCase().replace(/-/g, '_')}\`;
479
+
480
+ // Wire env vars from construct output tokens into eligible agent environments only.
481
+ // See isPaymentEligibleAgent — non-Python or non-HTTP runtimes have no shim that
482
+ // can consume these env vars, and giving them sts:AssumeRole on the
483
+ // ProcessPaymentRole would broaden the privilege surface unnecessarily.
484
+ for (const env of this.application.environments.values()) {
485
+ if (!isPaymentEligibleAgent(env.agent)) {
486
+ continue;
487
+ }
488
+ env.runtime.addEnvironmentVariable(\`\${prefix}_MANAGER_ARN\`, manager.paymentManagerArn);
489
+ env.runtime.addEnvironmentVariable(\`\${prefix}_PROCESS_PAYMENT_ROLE_ARN\`, manager.processPaymentRoleArn);
490
+
491
+ // Grant runtime execution role permission to assume the ProcessPaymentRole.
492
+ // The ProcessPaymentRole's trust policy allows AccountRootPrincipal, but the
493
+ // caller still needs sts:AssumeRole on its own role to perform the assumption.
494
+ env.runtime.role.addToPrincipalPolicy(
495
+ new iam.PolicyStatement({
496
+ actions: ['sts:AssumeRole'],
497
+ resources: [manager.processPaymentRoleArn],
498
+ })
499
+ );
500
+
501
+ // Grant payment data-plane actions directly to the runtime role.
502
+ //
503
+ // NOTE: This deviates from the canonical role model in the AgentCore Payments
504
+ // beta guide, which assigns Get/List/Create instrument+session actions to a
505
+ // separate ManagementRole and limits the agent's role to ProcessPayment only.
506
+ // The current SDK plugin (AgentCorePaymentsPlugin.generate_payment_header)
507
+ // calls GetPaymentInstrument internally during the 402 auto-pay path, so the
508
+ // runtime role needs read access. CreatePaymentSession is included so
509
+ // \`agentcore invoke --auto-session\` works without a separate ManagementRole
510
+ // call. Tighten this if the SDK is updated to accept pre-fetched instrument
511
+ // details and split create-session into a backend-only flow.
512
+ env.runtime.role.addToPrincipalPolicy(
513
+ new iam.PolicyStatement({
514
+ actions: [
515
+ 'bedrock-agentcore:GetPaymentInstrument',
516
+ 'bedrock-agentcore:ListPaymentInstruments',
517
+ 'bedrock-agentcore:GetPaymentInstrumentBalance',
518
+ 'bedrock-agentcore:GetPaymentSession',
519
+ 'bedrock-agentcore:ListPaymentSessions',
520
+ 'bedrock-agentcore:CreatePaymentSession',
521
+ 'bedrock-agentcore:ProcessPayment',
522
+ ],
523
+ resources: [manager.paymentManagerArn, \`\${manager.paymentManagerArn}/*\`],
524
+ })
525
+ );
526
+
527
+ if (payment.autoPayment !== undefined) {
528
+ env.runtime.addEnvironmentVariable(\`\${prefix}_AUTO_PAYMENT\`, String(payment.autoPayment));
529
+ }
530
+ if (payment.paymentToolAllowlist) {
531
+ env.runtime.addEnvironmentVariable(\`\${prefix}_TOOL_ALLOWLIST\`, payment.paymentToolAllowlist.join(','));
532
+ }
533
+ if (payment.networkPreferences) {
534
+ env.runtime.addEnvironmentVariable(\`\${prefix}_NETWORK_PREFERENCES\`, payment.networkPreferences.join(','));
535
+ }
536
+ if (payment.authorizerType === 'CUSTOM_JWT') {
537
+ env.runtime.addEnvironmentVariable(\`\${prefix}_AUTH_MODE\`, 'bearer');
538
+ }
539
+ }
540
+
541
+ // Create connectors for this manager
542
+ for (const connector of payment.connectors) {
543
+ const connId = toCdkId(connector.name);
544
+ const conn = new AgentCorePaymentConnector(this, \`Payment\${mgrId}\${connId}\`, {
545
+ projectName: spec.name,
546
+ paymentManager: manager,
547
+ connectorName: connector.name,
548
+ connectorType: connector.provider,
549
+ credentialProviderArn: connector.credentialProviderArn,
550
+ });
551
+
552
+ // Wire first connector's ID as env var (eligible agents only)
553
+ if (connector === payment.connectors[0]) {
554
+ for (const env of this.application.environments.values()) {
555
+ if (!isPaymentEligibleAgent(env.agent)) continue;
556
+ env.runtime.addEnvironmentVariable(\`\${prefix}_CONNECTOR_ID\`, conn.paymentConnectorId);
557
+ }
558
+ }
559
+
560
+ new CfnOutput(this, \`Payment\${mgrId}\${connId}ConnectorId\`, {
561
+ value: conn.paymentConnectorId,
562
+ });
563
+ }
564
+
565
+ // CFN Outputs for post-deploy state parsing
566
+ new CfnOutput(this, \`Payment\${mgrId}ManagerArn\`, {
567
+ value: manager.paymentManagerArn,
568
+ });
569
+ new CfnOutput(this, \`Payment\${mgrId}ManagerId\`, {
570
+ value: manager.paymentManagerId,
571
+ });
572
+ new CfnOutput(this, \`Payment\${mgrId}ProcessPaymentRoleArn\`, {
573
+ value: manager.processPaymentRoleArn,
574
+ });
575
+ new CfnOutput(this, \`Payment\${mgrId}ResourceRetrievalRoleArn\`, {
576
+ value: manager.resourceRetrievalRoleArn,
577
+ });
578
+ }
579
+ }
580
+
376
581
  // Stack-level output
377
582
  new CfnOutput(this, 'StackNameOutput', {
378
583
  description: 'Name of the CloudFormation Stack',
@@ -445,6 +650,8 @@ test('AgentCoreStack synthesizes with empty spec', () => {
445
650
  evaluators: [],
446
651
  onlineEvalConfigs: [],
447
652
  policyEngines: [],
653
+ payments: [],
654
+ configBundles: [],
448
655
  agentCoreGateways: [],
449
656
  mcpRuntimeTools: [],
450
657
  unassignedTargets: [],
@@ -605,6 +812,8 @@ exports[`Assets Directory Snapshots > File listing > should match the expected f
605
812
  "python/http/strands/base/pyproject.toml",
606
813
  "python/http/strands/capabilities/memory/__init__.py",
607
814
  "python/http/strands/capabilities/memory/session.py",
815
+ "python/http/strands/capabilities/payments/__init__.py",
816
+ "python/http/strands/capabilities/payments/payments.py",
608
817
  "python/mcp/standalone/base/README.md",
609
818
  "python/mcp/standalone/base/gitignore.template",
610
819
  "python/mcp/standalone/base/main.py",
@@ -1629,7 +1838,7 @@ dependencies = [
1629
1838
  {{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
1630
1839
  {{/if}}"aws-opentelemetry-distro",
1631
1840
  "opentelemetry-instrumentation-langchain >= 0.59.0",
1632
- "bedrock-agentcore[a2a] >= 1.0.3",
1841
+ "bedrock-agentcore[a2a] >= 1.8.0",
1633
1842
  "botocore[crt] >= 1.35.0",
1634
1843
  "langgraph >= 0.2.0",
1635
1844
  ]
@@ -2276,7 +2485,7 @@ requires-python = ">=3.10"
2276
2485
  dependencies = [
2277
2486
  "ag-ui-adk >= 0.6.0",
2278
2487
  "ag-ui-protocol >= 0.1.10",
2279
- "bedrock-agentcore >= 1.0.3",
2488
+ "bedrock-agentcore >= 1.8.0",
2280
2489
  "fastapi >= 0.115.12",
2281
2490
  "google-adk >= 1.16.0, < 2.0.0",
2282
2491
  "google-genai >= 1.0.0, < 2.0.0",
@@ -2648,7 +2857,7 @@ dependencies = [
2648
2857
  {{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
2649
2858
  {{/if}}"aws-opentelemetry-distro",
2650
2859
  "opentelemetry-instrumentation-langchain >= 0.59.0",
2651
- "bedrock-agentcore >= 1.0.3",
2860
+ "bedrock-agentcore >= 1.8.0",
2652
2861
  "botocore[crt] >= 1.35.0",
2653
2862
  "langgraph >= 0.3.25",
2654
2863
  "langchain >= 0.3.0",
@@ -3467,7 +3676,7 @@ dependencies = [
3467
3676
  "autogen-ext[mcp] >= 0.7.5",
3468
3677
  "opentelemetry-distro",
3469
3678
  "opentelemetry-exporter-otlp",
3470
- "bedrock-agentcore >= 1.0.3",
3679
+ "bedrock-agentcore >= 1.8.0",
3471
3680
  "botocore[crt] >= 1.35.0",
3472
3681
  "tiktoken",
3473
3682
  {{#if (eq modelProvider "Bedrock")}}
@@ -4422,7 +4631,7 @@ dependencies = [
4422
4631
  "mcp >= 1.19.0",
4423
4632
  "langchain-mcp-adapters >= 0.1.11",
4424
4633
  "langchain >= 1.0.3",
4425
- "bedrock-agentcore >= 1.0.3",
4634
+ "bedrock-agentcore >= 1.8.0",
4426
4635
  "botocore[crt] >= 1.35.0",
4427
4636
  {{#if (eq modelProvider "Bedrock")}}
4428
4637
  "langchain-aws >= 1.0.0",
@@ -4858,7 +5067,7 @@ requires-python = ">=3.10"
4858
5067
  dependencies = [
4859
5068
  "aws-opentelemetry-distro",
4860
5069
  "openai-agents >= 0.4.2",
4861
- "bedrock-agentcore >= 1.0.3",
5070
+ "bedrock-agentcore >= 1.8.0",
4862
5071
  "botocore[crt] >= 1.35.0",
4863
5072
  {{#if hasGateway}}{{#if (includes gatewayAuthTypes "AWS_IAM")}}"mcp-proxy-for-aws >= 1.1.0",
4864
5073
  {{/if}}{{/if}}
@@ -4977,6 +5186,9 @@ from memory.session import get_memory_session_manager
4977
5186
  {{#if needsOs}}
4978
5187
  import os
4979
5188
  {{/if}}
5189
+ {{#if hasPayment}}
5190
+ from capabilities.payments.payments import create_payments_plugin, PAYMENT_SYSTEM_PROMPT
5191
+ {{/if}}
4980
5192
 
4981
5193
  app = BedrockAgentCoreApp()
4982
5194
  log = app.logger
@@ -5112,12 +5324,12 @@ class ConfigBundleHook(HookProvider):
5112
5324
  {{/if}}
5113
5325
 
5114
5326
  {{#if hasMemory}}
5327
+ {{#unless hasPayment}}
5115
5328
  def agent_factory():
5116
5329
  cache = {}
5117
5330
  def get_or_create_agent(session_id, user_id):
5118
5331
  key = f"{session_id}/{user_id}"
5119
5332
  if key not in cache:
5120
- # Create an agent for the given session_id and user_id
5121
5333
  cache[key] = Agent(
5122
5334
  model=load_model(),
5123
5335
  session_manager=get_memory_session_manager(session_id, user_id),
@@ -5128,6 +5340,7 @@ def agent_factory():
5128
5340
  return cache[key]
5129
5341
  return get_or_create_agent
5130
5342
  get_or_create_agent = agent_factory()
5343
+ {{/unless}}
5131
5344
  {{else}}
5132
5345
  {{#if hasConfigBundle}}
5133
5346
  def create_agent():
@@ -5138,6 +5351,7 @@ def create_agent():
5138
5351
  hooks=[ConfigBundleHook()],
5139
5352
  )
5140
5353
  {{else}}
5354
+ {{#unless hasPayment}}
5141
5355
  _agent = None
5142
5356
 
5143
5357
  def get_or_create_agent():
@@ -5146,9 +5360,10 @@ def get_or_create_agent():
5146
5360
  _agent = Agent(
5147
5361
  model=load_model(),
5148
5362
  system_prompt=DEFAULT_SYSTEM_PROMPT,
5149
- tools=tools
5363
+ tools=tools,
5150
5364
  )
5151
5365
  return _agent
5366
+ {{/unless}}
5152
5367
  {{/if}}
5153
5368
  {{/if}}
5154
5369
 
@@ -5157,16 +5372,47 @@ def get_or_create_agent():
5157
5372
  async def invoke(payload, context):
5158
5373
  log.info("Invoking Agent.....")
5159
5374
 
5375
+ {{#if hasPayment}}
5376
+ user_id = payload.get("user_id") or getattr(context, "user_id", "default-user")
5377
+ instrument_id = payload.get("payment_instrument_id")
5378
+ session_id = payload.get("payment_session_id")
5379
+ payments_plugin = create_payments_plugin(user_id, instrument_id, session_id)
5380
+ plugins = [payments_plugin] if payments_plugin else []
5381
+ {{/if}}
5382
+
5160
5383
  {{#if hasMemory}}
5384
+ {{#if hasPayment}}
5385
+ mem_session_id = getattr(context, 'session_id', 'default-session')
5386
+ mem_user_id = getattr(context, 'user_id', 'default-user')
5387
+ agent = Agent(
5388
+ model=load_model(),
5389
+ session_manager=get_memory_session_manager(mem_session_id, mem_user_id),
5390
+ system_prompt=DEFAULT_SYSTEM_PROMPT + PAYMENT_SYSTEM_PROMPT,
5391
+ tools=tools,
5392
+ plugins=plugins,{{#if hasConfigBundle}}
5393
+ hooks=[ConfigBundleHook()],{{/if}}
5394
+ )
5395
+ {{else}}
5161
5396
  session_id = getattr(context, 'session_id', 'default-session')
5162
5397
  user_id = getattr(context, 'user_id', 'default-user')
5163
5398
  agent = get_or_create_agent(session_id, user_id)
5399
+ {{/if}}
5400
+ {{else}}
5401
+ {{#if hasPayment}}
5402
+ agent = Agent(
5403
+ model=load_model(),
5404
+ system_prompt=DEFAULT_SYSTEM_PROMPT + PAYMENT_SYSTEM_PROMPT,
5405
+ tools=tools,
5406
+ plugins=plugins,{{#if hasConfigBundle}}
5407
+ hooks=[ConfigBundleHook()],{{/if}}
5408
+ )
5164
5409
  {{else}}
5165
5410
  {{#if hasConfigBundle}}
5166
5411
  agent = create_agent()
5167
5412
  {{else}}
5168
5413
  agent = get_or_create_agent()
5169
5414
  {{/if}}
5415
+ {{/if}}
5170
5416
  {{/if}}
5171
5417
 
5172
5418
  # Execute and format response
@@ -5479,6 +5725,160 @@ def get_memory_session_manager(session_id: Optional[str], actor_id: str) -> Opti
5479
5725
  "
5480
5726
  `;
5481
5727
 
5728
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/http/strands/capabilities/payments/__init__.py should match snapshot 1`] = `
5729
+ """"Payment capabilities for Strands agents."""
5730
+ from .payments import create_payments_plugin
5731
+
5732
+ __all__ = ["create_payments_plugin"]
5733
+ "
5734
+ `;
5735
+
5736
+ exports[`Assets Directory Snapshots > Python framework assets > python/python/http/strands/capabilities/payments/payments.py should match snapshot 1`] = `
5737
+ """"Payment capability -- auto-generated by agentcore CLI.
5738
+
5739
+ Configures AgentCorePaymentsPlugin for Strands agents.
5740
+ Manager config is auto-discovered from AGENTCORE_PAYMENT_*
5741
+ environment variables set at deploy time.
5742
+
5743
+ Uses a per-invocation factory pattern so each request gets its own
5744
+ plugin instance with the correct user_id, instrument_id, and session_id.
5745
+ This prevents concurrency bugs where one user's payment context
5746
+ could leak to another user's request.
5747
+
5748
+ Uses the official SDK plugin which handles:
5749
+ - x402 v1 (body-based) and v2 (header-based) payment detection
5750
+ - Automatic 402 response interception and payment processing
5751
+ - Retry limiting (max 3 payment retries per tool use)
5752
+ - Error management and logging
5753
+ """
5754
+ import os
5755
+ import logging
5756
+
5757
+ import boto3
5758
+ from bedrock_agentcore.payments.integrations.strands import AgentCorePaymentsPlugin
5759
+ from bedrock_agentcore.payments.integrations.config import AgentCorePaymentsPluginConfig
5760
+
5761
+ logger = logging.getLogger(__name__)
5762
+
5763
+ PAYMENT_SYSTEM_PROMPT = """
5764
+ You have payment capabilities via the x402 protocol:
5765
+ - Use http_request to call HTTP endpoints. 402 Payment Required responses are settled automatically by the plugin and the call is retried.
5766
+ - Use get_payment_session to check your remaining budget before expensive operations
5767
+ - Use get_payment_instrument_balance to check wallet USDC balance
5768
+ - Use list_payment_instruments to see available payment instruments
5769
+ - If budget is low, inform the user before proceeding with paid requests
5770
+ """
5771
+
5772
+ _manager_arn = None
5773
+ _connector_id = None
5774
+ _process_payment_role_arn = None
5775
+ _name_segment = None
5776
+ _region = None
5777
+ _auth_mode = None
5778
+ _manager_count = 0
5779
+ for key, value in os.environ.items():
5780
+ if key.startswith("AGENTCORE_PAYMENT_") and key.endswith("_MANAGER_ARN"):
5781
+ if _manager_arn is None:
5782
+ _manager_arn = value
5783
+ _name_segment = key[len("AGENTCORE_PAYMENT_"):-len("_MANAGER_ARN")]
5784
+ _manager_count += 1
5785
+ if _manager_count > 1:
5786
+ logger.warning(
5787
+ "Multiple payment managers detected in environment. Using the first one found. "
5788
+ "Remove extra AGENTCORE_PAYMENT_*_MANAGER_ARN env vars to eliminate ambiguity."
5789
+ )
5790
+ _region = os.getenv("AWS_REGION")
5791
+
5792
+ _prefix = f"AGENTCORE_PAYMENT_{_name_segment}_" if _name_segment else "AGENTCORE_PAYMENT_"
5793
+ _auth_mode = os.getenv(f"{_prefix}AUTH_MODE", "sigv4")
5794
+ _connector_id = os.getenv(f"{_prefix}CONNECTOR_ID")
5795
+ _process_payment_role_arn = os.getenv(f"{_prefix}PROCESS_PAYMENT_ROLE_ARN")
5796
+ _auto_payment = os.getenv(f"{_prefix}AUTO_PAYMENT", "true").lower() == "true"
5797
+ _allowlist_raw = os.getenv(f"{_prefix}TOOL_ALLOWLIST")
5798
+ _allowlist = _allowlist_raw.split(",") if _allowlist_raw else None
5799
+ _network_prefs_raw = os.getenv(f"{_prefix}NETWORK_PREFERENCES")
5800
+ _network_prefs = _network_prefs_raw.split(",") if _network_prefs_raw else None
5801
+
5802
+ if not _manager_arn:
5803
+ logger.warning("No payment manager config found in environment")
5804
+ if not _connector_id:
5805
+ logger.warning("No payment connector config found in environment")
5806
+
5807
+
5808
+ def _assume_role_session(role_arn):
5809
+ """Assume an IAM role and return a boto3 session with temporary credentials."""
5810
+ sts = boto3.client("sts", region_name=_region)
5811
+ creds = sts.assume_role(
5812
+ RoleArn=role_arn,
5813
+ RoleSessionName="agentcore-payment-plugin",
5814
+ )["Credentials"]
5815
+ return boto3.Session(
5816
+ aws_access_key_id=creds["AccessKeyId"],
5817
+ aws_secret_access_key=creds["SecretAccessKey"],
5818
+ aws_session_token=creds["SessionToken"],
5819
+ region_name=_region,
5820
+ )
5821
+
5822
+
5823
+ def create_payments_plugin(user_id, instrument_id=None, session_id=None):
5824
+ """Create a fresh plugin instance per invocation.
5825
+
5826
+ Args:
5827
+ user_id: From invocation context (required for SigV4, derived from JWT for bearer)
5828
+ instrument_id: From invocation payload (created by app backend per user)
5829
+ session_id: From invocation payload (created by app backend per conversation)
5830
+
5831
+ Returns:
5832
+ AgentCorePaymentsPlugin instance, or None if no manager is configured.
5833
+ """
5834
+ if not _manager_arn:
5835
+ return None
5836
+
5837
+ config_kwargs = {
5838
+ "payment_manager_arn": _manager_arn,
5839
+ "region": _region,
5840
+ "payment_instrument_id": instrument_id,
5841
+ "payment_session_id": session_id,
5842
+ "payment_connector_id": _connector_id,
5843
+ }
5844
+
5845
+ config_kwargs["auto_payment"] = _auto_payment
5846
+ if _allowlist:
5847
+ config_kwargs["payment_tool_allowlist"] = _allowlist
5848
+ if _network_prefs:
5849
+ config_kwargs["network_preferences_config"] = _network_prefs
5850
+
5851
+ if _process_payment_role_arn:
5852
+ # Only pass boto3_session if SDK supports it (added in bedrock-agentcore >= 1.11).
5853
+ # Older SDKs use the runtime role's default credentials and can still call ProcessPayment
5854
+ # if the runtime role has been granted permission directly.
5855
+ import inspect
5856
+ if "boto3_session" in inspect.signature(AgentCorePaymentsPluginConfig).parameters:
5857
+ config_kwargs["boto3_session"] = _assume_role_session(_process_payment_role_arn)
5858
+ else:
5859
+ logger.warning(
5860
+ "PROCESS_PAYMENT_ROLE_ARN set but bedrock-agentcore SDK does not support boto3_session. "
5861
+ "Upgrade to bedrock-agentcore>=1.11 to enable cross-role payment processing."
5862
+ )
5863
+
5864
+ if _auth_mode == "bearer":
5865
+ bearer_token = os.getenv("AGENTCORE_BEARER_TOKEN")
5866
+ if bearer_token:
5867
+ config_kwargs["bearer_token"] = bearer_token
5868
+ else:
5869
+ logger.warning(
5870
+ "Bearer auth mode configured but AGENTCORE_BEARER_TOKEN not set. "
5871
+ "Falling back to SigV4. Set AGENTCORE_BEARER_TOKEN or pass bearer_token in invoke context."
5872
+ )
5873
+ config_kwargs["user_id"] = user_id or "default-user"
5874
+ else:
5875
+ config_kwargs["user_id"] = user_id or "default-user"
5876
+
5877
+ config = AgentCorePaymentsPluginConfig(**config_kwargs)
5878
+ return AgentCorePaymentsPlugin(config=config)
5879
+ "
5880
+ `;
5881
+
5482
5882
  exports[`Assets Directory Snapshots > Python framework assets > python/python/mcp/standalone/base/README.md should match snapshot 1`] = `
5483
5883
  "# {{ name }}
5484
5884