@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.
- package/dist/assets/__tests__/__snapshots__/assets.snapshot.test.ts.snap +414 -14
- package/dist/assets/cdk/bin/cdk.ts +49 -5
- package/dist/assets/cdk/lib/cdk-stack.ts +162 -1
- package/dist/assets/cdk/test/cdk.test.ts +2 -0
- package/dist/assets/python/a2a/langchain_langgraph/base/pyproject.toml +1 -1
- package/dist/assets/python/agui/googleadk/base/pyproject.toml +1 -1
- package/dist/assets/python/agui/langchain_langgraph/base/pyproject.toml +1 -1
- package/dist/assets/python/http/autogen/base/pyproject.toml +1 -1
- package/dist/assets/python/http/langchain_langgraph/base/pyproject.toml +1 -1
- package/dist/assets/python/http/openaiagents/base/pyproject.toml +1 -1
- package/dist/assets/python/http/strands/base/main.py +39 -2
- package/dist/assets/python/http/strands/capabilities/payments/__init__.py +4 -0
- package/dist/assets/python/http/strands/capabilities/payments/payments.py +142 -0
- package/dist/cli/index.mjs +571 -505
- package/dist/lib/packaging/helpers.d.ts.map +1 -1
- package/dist/lib/packaging/helpers.js +50 -10
- package/dist/lib/packaging/helpers.js.map +1 -1
- package/dist/lib/utils/env.d.ts +4 -0
- package/dist/lib/utils/env.d.ts.map +1 -1
- package/dist/lib/utils/env.js +18 -0
- package/dist/lib/utils/env.js.map +1 -1
- package/dist/lib/utils/index.d.ts +1 -1
- package/dist/lib/utils/index.d.ts.map +1 -1
- package/dist/lib/utils/index.js +2 -1
- package/dist/lib/utils/index.js.map +1 -1
- package/dist/schema/schemas/agentcore-project.d.ts +57 -2
- package/dist/schema/schemas/agentcore-project.d.ts.map +1 -1
- package/dist/schema/schemas/agentcore-project.js +61 -5
- package/dist/schema/schemas/agentcore-project.js.map +1 -1
- package/dist/schema/schemas/deployed-state.d.ts +97 -0
- package/dist/schema/schemas/deployed-state.d.ts.map +1 -1
- package/dist/schema/schemas/deployed-state.js +24 -4
- package/dist/schema/schemas/deployed-state.js.map +1 -1
- package/dist/schema/schemas/primitives/harness.d.ts +33 -0
- package/dist/schema/schemas/primitives/harness.d.ts.map +1 -1
- package/dist/schema/schemas/primitives/harness.js +42 -1
- package/dist/schema/schemas/primitives/harness.js.map +1 -1
- package/dist/schema/schemas/primitives/index.d.ts +4 -2
- package/dist/schema/schemas/primitives/index.d.ts.map +1 -1
- package/dist/schema/schemas/primitives/index.js +14 -2
- package/dist/schema/schemas/primitives/index.js.map +1 -1
- package/dist/schema/schemas/primitives/payment.d.ts +54 -0
- package/dist/schema/schemas/primitives/payment.d.ts.map +1 -0
- package/dist/schema/schemas/primitives/payment.js +79 -0
- package/dist/schema/schemas/primitives/payment.js.map +1 -0
- package/npm-shrinkwrap.json +748 -342
- package/package.json +2 -1
- package/scripts/run-e2e-local.sh +9 -3
|
@@ -28,12 +28,14 @@ async function main() {
|
|
|
28
28
|
const spec = await configIO.readProjectSpec();
|
|
29
29
|
const targets = await configIO.readAWSDeploymentTargets();
|
|
30
30
|
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
// dynamically and cast the resulting object.
|
|
31
|
+
// The vended CDK project compiles against the published @aws/agentcore-cdk
|
|
32
|
+
// schema type, which may lag the CLI's own AgentCoreProjectSpec (e.g. payments,
|
|
33
|
+
// harnesses, gateway fields). Cast once so those fields are reachable.
|
|
35
34
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
35
|
const specAny = spec as any;
|
|
36
|
+
|
|
37
|
+
// Extract MCP configuration from project spec.
|
|
38
|
+
// Gateway fields are stored in agentcore.json but may not yet be on the
|
|
37
39
|
const mcpSpec = specAny.agentCoreGateways?.length
|
|
38
40
|
? {
|
|
39
41
|
agentCoreGateways: specAny.agentCoreGateways,
|
|
@@ -68,6 +70,7 @@ async function main() {
|
|
|
68
70
|
apiKeyArn?: string;
|
|
69
71
|
efsAccessPoints?: { accessPointArn: string; mountPath: string }[];
|
|
70
72
|
s3AccessPoints?: { accessPointArn: string; mountPath: string }[];
|
|
73
|
+
apiFormat?: 'converse_stream' | 'responses' | 'chat_completions';
|
|
71
74
|
}[] = [];
|
|
72
75
|
for (const entry of specAny.harnesses ?? []) {
|
|
73
76
|
const harnessDir = path.resolve(projectRoot, entry.path);
|
|
@@ -86,6 +89,7 @@ async function main() {
|
|
|
86
89
|
apiKeyArn: harnessSpec.model?.apiKeyArn,
|
|
87
90
|
efsAccessPoints: harnessSpec.efsAccessPoints,
|
|
88
91
|
s3AccessPoints: harnessSpec.s3AccessPoints,
|
|
92
|
+
apiFormat: harnessSpec.model?.apiFormat,
|
|
89
93
|
});
|
|
90
94
|
} catch (err) {
|
|
91
95
|
throw new Error(
|
|
@@ -109,11 +113,51 @@ async function main() {
|
|
|
109
113
|
| Record<string, { credentialProviderArn: string; clientSecretArn?: string }>
|
|
110
114
|
| undefined;
|
|
111
115
|
|
|
116
|
+
// Payment credential provider ARNs live in the same credentials map as identity credentials
|
|
117
|
+
const paymentCredentials = credentials;
|
|
118
|
+
|
|
119
|
+
const paymentSpec = specAny.payments?.length
|
|
120
|
+
? specAny.payments.map(
|
|
121
|
+
(p: {
|
|
122
|
+
name: string;
|
|
123
|
+
description?: string;
|
|
124
|
+
authorizerType: 'AWS_IAM' | 'CUSTOM_JWT';
|
|
125
|
+
authorizerConfiguration?: unknown;
|
|
126
|
+
autoPayment?: boolean;
|
|
127
|
+
paymentToolAllowlist?: string[];
|
|
128
|
+
networkPreferences?: string[];
|
|
129
|
+
connectors: { name: string; provider?: string; credentialName: string }[];
|
|
130
|
+
}) => ({
|
|
131
|
+
name: p.name,
|
|
132
|
+
description: p.description,
|
|
133
|
+
authorizerType: p.authorizerType,
|
|
134
|
+
authorizerConfiguration: p.authorizerConfiguration,
|
|
135
|
+
autoPayment: p.autoPayment,
|
|
136
|
+
paymentToolAllowlist: p.paymentToolAllowlist,
|
|
137
|
+
networkPreferences: p.networkPreferences,
|
|
138
|
+
connectors: p.connectors.map(c => {
|
|
139
|
+
const credentialProviderArn = paymentCredentials?.[c.credentialName]?.credentialProviderArn;
|
|
140
|
+
if (!credentialProviderArn) {
|
|
141
|
+
// Fail fast with an actionable message rather than passing an empty
|
|
142
|
+
// ARN that fails opaquely server-side at CreatePaymentConnector.
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Payment connector "${c.name}" on manager "${p.name}" references credential ` +
|
|
145
|
+
`"${c.credentialName}", but no deployed credential provider was found for it. ` +
|
|
146
|
+
`Run \`agentcore deploy\` so the credential provider is created first.`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return { name: c.name, provider: c.provider, credentialProviderArn };
|
|
150
|
+
}),
|
|
151
|
+
})
|
|
152
|
+
)
|
|
153
|
+
: undefined;
|
|
154
|
+
|
|
112
155
|
new AgentCoreStack(app, stackName, {
|
|
113
156
|
spec,
|
|
114
157
|
mcpSpec,
|
|
115
158
|
credentials,
|
|
116
159
|
harnesses: harnessConfigs.length > 0 ? harnessConfigs : undefined,
|
|
160
|
+
paymentSpec,
|
|
117
161
|
env,
|
|
118
162
|
description: `AgentCore stack for ${spec.name} deployed to ${target.name} (${target.region})`,
|
|
119
163
|
tags: {
|
|
@@ -128,5 +172,5 @@ async function main() {
|
|
|
128
172
|
|
|
129
173
|
main().catch((error: unknown) => {
|
|
130
174
|
console.error('AgentCore CDK synthesis failed:', error instanceof Error ? error.message : error);
|
|
131
|
-
process.
|
|
175
|
+
process.exit(1);
|
|
132
176
|
});
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AgentCoreApplication,
|
|
3
3
|
AgentCoreMcp,
|
|
4
|
+
AgentCorePaymentManager,
|
|
5
|
+
AgentCorePaymentConnector,
|
|
4
6
|
type AgentCoreProjectSpec,
|
|
5
7
|
type AgentCoreMcpSpec,
|
|
8
|
+
type CustomJWTAuthorizerConfig,
|
|
6
9
|
} from '@aws/agentcore-cdk';
|
|
7
10
|
import { CfnOutput, Stack, type StackProps } from 'aws-cdk-lib';
|
|
11
|
+
import * as iam from 'aws-cdk-lib/aws-iam';
|
|
8
12
|
import { Construct } from 'constructs';
|
|
9
13
|
|
|
10
14
|
export interface HarnessConfig {
|
|
@@ -21,6 +25,23 @@ export interface HarnessConfig {
|
|
|
21
25
|
s3AccessPoints?: { accessPointArn: string; mountPath: string }[];
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
export interface PaymentConnectorSpec {
|
|
29
|
+
name: string;
|
|
30
|
+
provider: 'CoinbaseCDP' | 'StripePrivy';
|
|
31
|
+
credentialProviderArn: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface PaymentSpec {
|
|
35
|
+
name: string;
|
|
36
|
+
description?: string;
|
|
37
|
+
authorizerType: 'AWS_IAM' | 'CUSTOM_JWT';
|
|
38
|
+
authorizerConfiguration?: { customJWTAuthorizer: CustomJWTAuthorizerConfig };
|
|
39
|
+
autoPayment?: boolean;
|
|
40
|
+
paymentToolAllowlist?: string[];
|
|
41
|
+
networkPreferences?: string[];
|
|
42
|
+
connectors: PaymentConnectorSpec[];
|
|
43
|
+
}
|
|
44
|
+
|
|
24
45
|
export interface AgentCoreStackProps extends StackProps {
|
|
25
46
|
/**
|
|
26
47
|
* The AgentCore project specification containing agents, memories, and credentials.
|
|
@@ -38,6 +59,30 @@ export interface AgentCoreStackProps extends StackProps {
|
|
|
38
59
|
* Harness role configurations.
|
|
39
60
|
*/
|
|
40
61
|
harnesses?: HarnessConfig[];
|
|
62
|
+
/**
|
|
63
|
+
* Payment specifications with resolved credential provider ARNs.
|
|
64
|
+
*/
|
|
65
|
+
paymentSpec?: PaymentSpec[];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function toCdkId(name: string): string {
|
|
69
|
+
return name.replace(/_/g, '');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Decide whether a deployed runtime should receive payment env vars + IAM grants.
|
|
74
|
+
* Payments today only ships a runtime shim for Python HTTP runtimes; injecting
|
|
75
|
+
* AGENTCORE_PAYMENT_* env vars into TypeScript / MCP / A2A / AGUI runtimes
|
|
76
|
+
* would surface env vars they cannot consume and would dilute least-privilege
|
|
77
|
+
* IAM grants for runtimes that never call ProcessPayment.
|
|
78
|
+
*/
|
|
79
|
+
function isPaymentEligibleAgent(agent: { entrypoint?: string; protocol?: string }): boolean {
|
|
80
|
+
if (agent.protocol && agent.protocol !== 'HTTP') {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const entrypoint = typeof agent.entrypoint === 'string' ? agent.entrypoint : '';
|
|
84
|
+
const entrypointFile = entrypoint.split(':')[0] ?? '';
|
|
85
|
+
return entrypointFile.endsWith('.py');
|
|
41
86
|
}
|
|
42
87
|
|
|
43
88
|
/**
|
|
@@ -53,7 +98,7 @@ export class AgentCoreStack extends Stack {
|
|
|
53
98
|
constructor(scope: Construct, id: string, props: AgentCoreStackProps) {
|
|
54
99
|
super(scope, id, props);
|
|
55
100
|
|
|
56
|
-
const { spec, mcpSpec, credentials, harnesses } = props;
|
|
101
|
+
const { spec, mcpSpec, credentials, harnesses, paymentSpec } = props;
|
|
57
102
|
|
|
58
103
|
// Create AgentCoreApplication with all agents and harness roles
|
|
59
104
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -74,6 +119,122 @@ export class AgentCoreStack extends Stack {
|
|
|
74
119
|
});
|
|
75
120
|
}
|
|
76
121
|
|
|
122
|
+
// Create payment infrastructure via CFN constructs
|
|
123
|
+
if (paymentSpec && paymentSpec.length > 0) {
|
|
124
|
+
for (const payment of paymentSpec) {
|
|
125
|
+
const mgrId = toCdkId(payment.name);
|
|
126
|
+
const manager = new AgentCorePaymentManager(this, `Payment${mgrId}`, {
|
|
127
|
+
projectName: spec.name,
|
|
128
|
+
name: payment.name,
|
|
129
|
+
authorizerType: payment.authorizerType,
|
|
130
|
+
description: payment.description,
|
|
131
|
+
authorizerConfiguration: payment.authorizerConfiguration,
|
|
132
|
+
tags: spec.tags,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const prefix = `AGENTCORE_PAYMENT_${payment.name.toUpperCase().replace(/-/g, '_')}`;
|
|
136
|
+
|
|
137
|
+
// Wire env vars from construct output tokens into eligible agent environments only.
|
|
138
|
+
// See isPaymentEligibleAgent — non-Python or non-HTTP runtimes have no shim that
|
|
139
|
+
// can consume these env vars, and giving them sts:AssumeRole on the
|
|
140
|
+
// ProcessPaymentRole would broaden the privilege surface unnecessarily.
|
|
141
|
+
for (const env of this.application.environments.values()) {
|
|
142
|
+
if (!isPaymentEligibleAgent(env.agent)) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
env.runtime.addEnvironmentVariable(`${prefix}_MANAGER_ARN`, manager.paymentManagerArn);
|
|
146
|
+
env.runtime.addEnvironmentVariable(`${prefix}_PROCESS_PAYMENT_ROLE_ARN`, manager.processPaymentRoleArn);
|
|
147
|
+
|
|
148
|
+
// Grant runtime execution role permission to assume the ProcessPaymentRole.
|
|
149
|
+
// The ProcessPaymentRole's trust policy allows AccountRootPrincipal, but the
|
|
150
|
+
// caller still needs sts:AssumeRole on its own role to perform the assumption.
|
|
151
|
+
env.runtime.role.addToPrincipalPolicy(
|
|
152
|
+
new iam.PolicyStatement({
|
|
153
|
+
actions: ['sts:AssumeRole'],
|
|
154
|
+
resources: [manager.processPaymentRoleArn],
|
|
155
|
+
})
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
// Grant payment data-plane actions directly to the runtime role.
|
|
159
|
+
//
|
|
160
|
+
// NOTE: This deviates from the canonical role model in the AgentCore Payments
|
|
161
|
+
// beta guide, which assigns Get/List/Create instrument+session actions to a
|
|
162
|
+
// separate ManagementRole and limits the agent's role to ProcessPayment only.
|
|
163
|
+
// The current SDK plugin (AgentCorePaymentsPlugin.generate_payment_header)
|
|
164
|
+
// calls GetPaymentInstrument internally during the 402 auto-pay path, so the
|
|
165
|
+
// runtime role needs read access. CreatePaymentSession is included so
|
|
166
|
+
// `agentcore invoke --auto-session` works without a separate ManagementRole
|
|
167
|
+
// call. Tighten this if the SDK is updated to accept pre-fetched instrument
|
|
168
|
+
// details and split create-session into a backend-only flow.
|
|
169
|
+
env.runtime.role.addToPrincipalPolicy(
|
|
170
|
+
new iam.PolicyStatement({
|
|
171
|
+
actions: [
|
|
172
|
+
'bedrock-agentcore:GetPaymentInstrument',
|
|
173
|
+
'bedrock-agentcore:ListPaymentInstruments',
|
|
174
|
+
'bedrock-agentcore:GetPaymentInstrumentBalance',
|
|
175
|
+
'bedrock-agentcore:GetPaymentSession',
|
|
176
|
+
'bedrock-agentcore:ListPaymentSessions',
|
|
177
|
+
'bedrock-agentcore:CreatePaymentSession',
|
|
178
|
+
'bedrock-agentcore:ProcessPayment',
|
|
179
|
+
],
|
|
180
|
+
resources: [manager.paymentManagerArn, `${manager.paymentManagerArn}/*`],
|
|
181
|
+
})
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
if (payment.autoPayment !== undefined) {
|
|
185
|
+
env.runtime.addEnvironmentVariable(`${prefix}_AUTO_PAYMENT`, String(payment.autoPayment));
|
|
186
|
+
}
|
|
187
|
+
if (payment.paymentToolAllowlist) {
|
|
188
|
+
env.runtime.addEnvironmentVariable(`${prefix}_TOOL_ALLOWLIST`, payment.paymentToolAllowlist.join(','));
|
|
189
|
+
}
|
|
190
|
+
if (payment.networkPreferences) {
|
|
191
|
+
env.runtime.addEnvironmentVariable(`${prefix}_NETWORK_PREFERENCES`, payment.networkPreferences.join(','));
|
|
192
|
+
}
|
|
193
|
+
if (payment.authorizerType === 'CUSTOM_JWT') {
|
|
194
|
+
env.runtime.addEnvironmentVariable(`${prefix}_AUTH_MODE`, 'bearer');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Create connectors for this manager
|
|
199
|
+
for (const connector of payment.connectors) {
|
|
200
|
+
const connId = toCdkId(connector.name);
|
|
201
|
+
const conn = new AgentCorePaymentConnector(this, `Payment${mgrId}${connId}`, {
|
|
202
|
+
projectName: spec.name,
|
|
203
|
+
paymentManager: manager,
|
|
204
|
+
connectorName: connector.name,
|
|
205
|
+
connectorType: connector.provider,
|
|
206
|
+
credentialProviderArn: connector.credentialProviderArn,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Wire first connector's ID as env var (eligible agents only)
|
|
210
|
+
if (connector === payment.connectors[0]) {
|
|
211
|
+
for (const env of this.application.environments.values()) {
|
|
212
|
+
if (!isPaymentEligibleAgent(env.agent)) continue;
|
|
213
|
+
env.runtime.addEnvironmentVariable(`${prefix}_CONNECTOR_ID`, conn.paymentConnectorId);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
new CfnOutput(this, `Payment${mgrId}${connId}ConnectorId`, {
|
|
218
|
+
value: conn.paymentConnectorId,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// CFN Outputs for post-deploy state parsing
|
|
223
|
+
new CfnOutput(this, `Payment${mgrId}ManagerArn`, {
|
|
224
|
+
value: manager.paymentManagerArn,
|
|
225
|
+
});
|
|
226
|
+
new CfnOutput(this, `Payment${mgrId}ManagerId`, {
|
|
227
|
+
value: manager.paymentManagerId,
|
|
228
|
+
});
|
|
229
|
+
new CfnOutput(this, `Payment${mgrId}ProcessPaymentRoleArn`, {
|
|
230
|
+
value: manager.processPaymentRoleArn,
|
|
231
|
+
});
|
|
232
|
+
new CfnOutput(this, `Payment${mgrId}ResourceRetrievalRoleArn`, {
|
|
233
|
+
value: manager.resourceRetrievalRoleArn,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
77
238
|
// Stack-level output
|
|
78
239
|
new CfnOutput(this, 'StackNameOutput', {
|
|
79
240
|
description: 'Name of the CloudFormation Stack',
|
|
@@ -17,7 +17,7 @@ dependencies = [
|
|
|
17
17
|
{{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
|
|
18
18
|
{{/if}}"aws-opentelemetry-distro",
|
|
19
19
|
"opentelemetry-instrumentation-langchain >= 0.59.0",
|
|
20
|
-
"bedrock-agentcore[a2a] >= 1.0
|
|
20
|
+
"bedrock-agentcore[a2a] >= 1.8.0",
|
|
21
21
|
"botocore[crt] >= 1.35.0",
|
|
22
22
|
"langgraph >= 0.2.0",
|
|
23
23
|
]
|
|
@@ -11,7 +11,7 @@ requires-python = ">=3.10"
|
|
|
11
11
|
dependencies = [
|
|
12
12
|
"ag-ui-adk >= 0.6.0",
|
|
13
13
|
"ag-ui-protocol >= 0.1.10",
|
|
14
|
-
"bedrock-agentcore >= 1.0
|
|
14
|
+
"bedrock-agentcore >= 1.8.0",
|
|
15
15
|
"fastapi >= 0.115.12",
|
|
16
16
|
"google-adk >= 1.16.0, < 2.0.0",
|
|
17
17
|
"google-genai >= 1.0.0, < 2.0.0",
|
|
@@ -18,7 +18,7 @@ dependencies = [
|
|
|
18
18
|
{{/if}}{{#if (eq modelProvider "OpenAI")}}"langchain-openai >= 0.2.0",
|
|
19
19
|
{{/if}}"aws-opentelemetry-distro",
|
|
20
20
|
"opentelemetry-instrumentation-langchain >= 0.59.0",
|
|
21
|
-
"bedrock-agentcore >= 1.0
|
|
21
|
+
"bedrock-agentcore >= 1.8.0",
|
|
22
22
|
"botocore[crt] >= 1.35.0",
|
|
23
23
|
"langgraph >= 0.3.25",
|
|
24
24
|
"langchain >= 0.3.0",
|
|
@@ -13,7 +13,7 @@ dependencies = [
|
|
|
13
13
|
"autogen-ext[mcp] >= 0.7.5",
|
|
14
14
|
"opentelemetry-distro",
|
|
15
15
|
"opentelemetry-exporter-otlp",
|
|
16
|
-
"bedrock-agentcore >= 1.0
|
|
16
|
+
"bedrock-agentcore >= 1.8.0",
|
|
17
17
|
"botocore[crt] >= 1.35.0",
|
|
18
18
|
"tiktoken",
|
|
19
19
|
{{#if (eq modelProvider "Bedrock")}}
|
|
@@ -15,7 +15,7 @@ dependencies = [
|
|
|
15
15
|
"mcp >= 1.19.0",
|
|
16
16
|
"langchain-mcp-adapters >= 0.1.11",
|
|
17
17
|
"langchain >= 1.0.3",
|
|
18
|
-
"bedrock-agentcore >= 1.0
|
|
18
|
+
"bedrock-agentcore >= 1.8.0",
|
|
19
19
|
"botocore[crt] >= 1.35.0",
|
|
20
20
|
{{#if (eq modelProvider "Bedrock")}}
|
|
21
21
|
"langchain-aws >= 1.0.0",
|
|
@@ -11,7 +11,7 @@ requires-python = ">=3.10"
|
|
|
11
11
|
dependencies = [
|
|
12
12
|
"aws-opentelemetry-distro",
|
|
13
13
|
"openai-agents >= 0.4.2",
|
|
14
|
-
"bedrock-agentcore >= 1.0
|
|
14
|
+
"bedrock-agentcore >= 1.8.0",
|
|
15
15
|
"botocore[crt] >= 1.35.0",
|
|
16
16
|
{{#if hasGateway}}{{#if (includes gatewayAuthTypes "AWS_IAM")}}"mcp-proxy-for-aws >= 1.1.0",
|
|
17
17
|
{{/if}}{{/if}}
|
|
@@ -18,6 +18,9 @@ from memory.session import get_memory_session_manager
|
|
|
18
18
|
{{#if needsOs}}
|
|
19
19
|
import os
|
|
20
20
|
{{/if}}
|
|
21
|
+
{{#if hasPayment}}
|
|
22
|
+
from capabilities.payments.payments import create_payments_plugin, PAYMENT_SYSTEM_PROMPT
|
|
23
|
+
{{/if}}
|
|
21
24
|
|
|
22
25
|
app = BedrockAgentCoreApp()
|
|
23
26
|
log = app.logger
|
|
@@ -153,12 +156,12 @@ class ConfigBundleHook(HookProvider):
|
|
|
153
156
|
{{/if}}
|
|
154
157
|
|
|
155
158
|
{{#if hasMemory}}
|
|
159
|
+
{{#unless hasPayment}}
|
|
156
160
|
def agent_factory():
|
|
157
161
|
cache = {}
|
|
158
162
|
def get_or_create_agent(session_id, user_id):
|
|
159
163
|
key = f"{session_id}/{user_id}"
|
|
160
164
|
if key not in cache:
|
|
161
|
-
# Create an agent for the given session_id and user_id
|
|
162
165
|
cache[key] = Agent(
|
|
163
166
|
model=load_model(),
|
|
164
167
|
session_manager=get_memory_session_manager(session_id, user_id),
|
|
@@ -169,6 +172,7 @@ def agent_factory():
|
|
|
169
172
|
return cache[key]
|
|
170
173
|
return get_or_create_agent
|
|
171
174
|
get_or_create_agent = agent_factory()
|
|
175
|
+
{{/unless}}
|
|
172
176
|
{{else}}
|
|
173
177
|
{{#if hasConfigBundle}}
|
|
174
178
|
def create_agent():
|
|
@@ -179,6 +183,7 @@ def create_agent():
|
|
|
179
183
|
hooks=[ConfigBundleHook()],
|
|
180
184
|
)
|
|
181
185
|
{{else}}
|
|
186
|
+
{{#unless hasPayment}}
|
|
182
187
|
_agent = None
|
|
183
188
|
|
|
184
189
|
def get_or_create_agent():
|
|
@@ -187,9 +192,10 @@ def get_or_create_agent():
|
|
|
187
192
|
_agent = Agent(
|
|
188
193
|
model=load_model(),
|
|
189
194
|
system_prompt=DEFAULT_SYSTEM_PROMPT,
|
|
190
|
-
tools=tools
|
|
195
|
+
tools=tools,
|
|
191
196
|
)
|
|
192
197
|
return _agent
|
|
198
|
+
{{/unless}}
|
|
193
199
|
{{/if}}
|
|
194
200
|
{{/if}}
|
|
195
201
|
|
|
@@ -198,16 +204,47 @@ def get_or_create_agent():
|
|
|
198
204
|
async def invoke(payload, context):
|
|
199
205
|
log.info("Invoking Agent.....")
|
|
200
206
|
|
|
207
|
+
{{#if hasPayment}}
|
|
208
|
+
user_id = payload.get("user_id") or getattr(context, "user_id", "default-user")
|
|
209
|
+
instrument_id = payload.get("payment_instrument_id")
|
|
210
|
+
session_id = payload.get("payment_session_id")
|
|
211
|
+
payments_plugin = create_payments_plugin(user_id, instrument_id, session_id)
|
|
212
|
+
plugins = [payments_plugin] if payments_plugin else []
|
|
213
|
+
{{/if}}
|
|
214
|
+
|
|
201
215
|
{{#if hasMemory}}
|
|
216
|
+
{{#if hasPayment}}
|
|
217
|
+
mem_session_id = getattr(context, 'session_id', 'default-session')
|
|
218
|
+
mem_user_id = getattr(context, 'user_id', 'default-user')
|
|
219
|
+
agent = Agent(
|
|
220
|
+
model=load_model(),
|
|
221
|
+
session_manager=get_memory_session_manager(mem_session_id, mem_user_id),
|
|
222
|
+
system_prompt=DEFAULT_SYSTEM_PROMPT + PAYMENT_SYSTEM_PROMPT,
|
|
223
|
+
tools=tools,
|
|
224
|
+
plugins=plugins,{{#if hasConfigBundle}}
|
|
225
|
+
hooks=[ConfigBundleHook()],{{/if}}
|
|
226
|
+
)
|
|
227
|
+
{{else}}
|
|
202
228
|
session_id = getattr(context, 'session_id', 'default-session')
|
|
203
229
|
user_id = getattr(context, 'user_id', 'default-user')
|
|
204
230
|
agent = get_or_create_agent(session_id, user_id)
|
|
231
|
+
{{/if}}
|
|
232
|
+
{{else}}
|
|
233
|
+
{{#if hasPayment}}
|
|
234
|
+
agent = Agent(
|
|
235
|
+
model=load_model(),
|
|
236
|
+
system_prompt=DEFAULT_SYSTEM_PROMPT + PAYMENT_SYSTEM_PROMPT,
|
|
237
|
+
tools=tools,
|
|
238
|
+
plugins=plugins,{{#if hasConfigBundle}}
|
|
239
|
+
hooks=[ConfigBundleHook()],{{/if}}
|
|
240
|
+
)
|
|
205
241
|
{{else}}
|
|
206
242
|
{{#if hasConfigBundle}}
|
|
207
243
|
agent = create_agent()
|
|
208
244
|
{{else}}
|
|
209
245
|
agent = get_or_create_agent()
|
|
210
246
|
{{/if}}
|
|
247
|
+
{{/if}}
|
|
211
248
|
{{/if}}
|
|
212
249
|
|
|
213
250
|
# Execute and format response
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Payment capability -- auto-generated by agentcore CLI.
|
|
2
|
+
|
|
3
|
+
Configures AgentCorePaymentsPlugin for Strands agents.
|
|
4
|
+
Manager config is auto-discovered from AGENTCORE_PAYMENT_*
|
|
5
|
+
environment variables set at deploy time.
|
|
6
|
+
|
|
7
|
+
Uses a per-invocation factory pattern so each request gets its own
|
|
8
|
+
plugin instance with the correct user_id, instrument_id, and session_id.
|
|
9
|
+
This prevents concurrency bugs where one user's payment context
|
|
10
|
+
could leak to another user's request.
|
|
11
|
+
|
|
12
|
+
Uses the official SDK plugin which handles:
|
|
13
|
+
- x402 v1 (body-based) and v2 (header-based) payment detection
|
|
14
|
+
- Automatic 402 response interception and payment processing
|
|
15
|
+
- Retry limiting (max 3 payment retries per tool use)
|
|
16
|
+
- Error management and logging
|
|
17
|
+
"""
|
|
18
|
+
import os
|
|
19
|
+
import logging
|
|
20
|
+
|
|
21
|
+
import boto3
|
|
22
|
+
from bedrock_agentcore.payments.integrations.strands import AgentCorePaymentsPlugin
|
|
23
|
+
from bedrock_agentcore.payments.integrations.config import AgentCorePaymentsPluginConfig
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
PAYMENT_SYSTEM_PROMPT = """
|
|
28
|
+
You have payment capabilities via the x402 protocol:
|
|
29
|
+
- Use http_request to call HTTP endpoints. 402 Payment Required responses are settled automatically by the plugin and the call is retried.
|
|
30
|
+
- Use get_payment_session to check your remaining budget before expensive operations
|
|
31
|
+
- Use get_payment_instrument_balance to check wallet USDC balance
|
|
32
|
+
- Use list_payment_instruments to see available payment instruments
|
|
33
|
+
- If budget is low, inform the user before proceeding with paid requests
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
_manager_arn = None
|
|
37
|
+
_connector_id = None
|
|
38
|
+
_process_payment_role_arn = None
|
|
39
|
+
_name_segment = None
|
|
40
|
+
_region = None
|
|
41
|
+
_auth_mode = None
|
|
42
|
+
_manager_count = 0
|
|
43
|
+
for key, value in os.environ.items():
|
|
44
|
+
if key.startswith("AGENTCORE_PAYMENT_") and key.endswith("_MANAGER_ARN"):
|
|
45
|
+
if _manager_arn is None:
|
|
46
|
+
_manager_arn = value
|
|
47
|
+
_name_segment = key[len("AGENTCORE_PAYMENT_"):-len("_MANAGER_ARN")]
|
|
48
|
+
_manager_count += 1
|
|
49
|
+
if _manager_count > 1:
|
|
50
|
+
logger.warning(
|
|
51
|
+
"Multiple payment managers detected in environment. Using the first one found. "
|
|
52
|
+
"Remove extra AGENTCORE_PAYMENT_*_MANAGER_ARN env vars to eliminate ambiguity."
|
|
53
|
+
)
|
|
54
|
+
_region = os.getenv("AWS_REGION")
|
|
55
|
+
|
|
56
|
+
_prefix = f"AGENTCORE_PAYMENT_{_name_segment}_" if _name_segment else "AGENTCORE_PAYMENT_"
|
|
57
|
+
_auth_mode = os.getenv(f"{_prefix}AUTH_MODE", "sigv4")
|
|
58
|
+
_connector_id = os.getenv(f"{_prefix}CONNECTOR_ID")
|
|
59
|
+
_process_payment_role_arn = os.getenv(f"{_prefix}PROCESS_PAYMENT_ROLE_ARN")
|
|
60
|
+
_auto_payment = os.getenv(f"{_prefix}AUTO_PAYMENT", "true").lower() == "true"
|
|
61
|
+
_allowlist_raw = os.getenv(f"{_prefix}TOOL_ALLOWLIST")
|
|
62
|
+
_allowlist = _allowlist_raw.split(",") if _allowlist_raw else None
|
|
63
|
+
_network_prefs_raw = os.getenv(f"{_prefix}NETWORK_PREFERENCES")
|
|
64
|
+
_network_prefs = _network_prefs_raw.split(",") if _network_prefs_raw else None
|
|
65
|
+
|
|
66
|
+
if not _manager_arn:
|
|
67
|
+
logger.warning("No payment manager config found in environment")
|
|
68
|
+
if not _connector_id:
|
|
69
|
+
logger.warning("No payment connector config found in environment")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _assume_role_session(role_arn):
|
|
73
|
+
"""Assume an IAM role and return a boto3 session with temporary credentials."""
|
|
74
|
+
sts = boto3.client("sts", region_name=_region)
|
|
75
|
+
creds = sts.assume_role(
|
|
76
|
+
RoleArn=role_arn,
|
|
77
|
+
RoleSessionName="agentcore-payment-plugin",
|
|
78
|
+
)["Credentials"]
|
|
79
|
+
return boto3.Session(
|
|
80
|
+
aws_access_key_id=creds["AccessKeyId"],
|
|
81
|
+
aws_secret_access_key=creds["SecretAccessKey"],
|
|
82
|
+
aws_session_token=creds["SessionToken"],
|
|
83
|
+
region_name=_region,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def create_payments_plugin(user_id, instrument_id=None, session_id=None):
|
|
88
|
+
"""Create a fresh plugin instance per invocation.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
user_id: From invocation context (required for SigV4, derived from JWT for bearer)
|
|
92
|
+
instrument_id: From invocation payload (created by app backend per user)
|
|
93
|
+
session_id: From invocation payload (created by app backend per conversation)
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
AgentCorePaymentsPlugin instance, or None if no manager is configured.
|
|
97
|
+
"""
|
|
98
|
+
if not _manager_arn:
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
config_kwargs = {
|
|
102
|
+
"payment_manager_arn": _manager_arn,
|
|
103
|
+
"region": _region,
|
|
104
|
+
"payment_instrument_id": instrument_id,
|
|
105
|
+
"payment_session_id": session_id,
|
|
106
|
+
"payment_connector_id": _connector_id,
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
config_kwargs["auto_payment"] = _auto_payment
|
|
110
|
+
if _allowlist:
|
|
111
|
+
config_kwargs["payment_tool_allowlist"] = _allowlist
|
|
112
|
+
if _network_prefs:
|
|
113
|
+
config_kwargs["network_preferences_config"] = _network_prefs
|
|
114
|
+
|
|
115
|
+
if _process_payment_role_arn:
|
|
116
|
+
# Only pass boto3_session if SDK supports it (added in bedrock-agentcore >= 1.11).
|
|
117
|
+
# Older SDKs use the runtime role's default credentials and can still call ProcessPayment
|
|
118
|
+
# if the runtime role has been granted permission directly.
|
|
119
|
+
import inspect
|
|
120
|
+
if "boto3_session" in inspect.signature(AgentCorePaymentsPluginConfig).parameters:
|
|
121
|
+
config_kwargs["boto3_session"] = _assume_role_session(_process_payment_role_arn)
|
|
122
|
+
else:
|
|
123
|
+
logger.warning(
|
|
124
|
+
"PROCESS_PAYMENT_ROLE_ARN set but bedrock-agentcore SDK does not support boto3_session. "
|
|
125
|
+
"Upgrade to bedrock-agentcore>=1.11 to enable cross-role payment processing."
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
if _auth_mode == "bearer":
|
|
129
|
+
bearer_token = os.getenv("AGENTCORE_BEARER_TOKEN")
|
|
130
|
+
if bearer_token:
|
|
131
|
+
config_kwargs["bearer_token"] = bearer_token
|
|
132
|
+
else:
|
|
133
|
+
logger.warning(
|
|
134
|
+
"Bearer auth mode configured but AGENTCORE_BEARER_TOKEN not set. "
|
|
135
|
+
"Falling back to SigV4. Set AGENTCORE_BEARER_TOKEN or pass bearer_token in invoke context."
|
|
136
|
+
)
|
|
137
|
+
config_kwargs["user_id"] = user_id or "default-user"
|
|
138
|
+
else:
|
|
139
|
+
config_kwargs["user_id"] = user_id or "default-user"
|
|
140
|
+
|
|
141
|
+
config = AgentCorePaymentsPluginConfig(**config_kwargs)
|
|
142
|
+
return AgentCorePaymentsPlugin(config=config)
|