feat(payments): add AgentCore Payments as first-class CLI resource#1261
Open
aidandaly24 wants to merge 34 commits into
Open
feat(payments): add AgentCore Payments as first-class CLI resource#1261aidandaly24 wants to merge 34 commits into
aidandaly24 wants to merge 34 commits into
Conversation
Contributor
Package TarballHow to installgh release download pr-1261-tarball --repo aws/agentcore-cli --pattern "*.tgz" --dir /tmp/pr-tarball
npm install -g /tmp/pr-tarball/aws-agentcore-0.14.2.tgz |
31df340 to
5425a52
Compare
6bb9d46 to
59d48ee
Compare
| if (result.success) { | ||
| console.log(JSON.stringify({ success: true })); | ||
| } else { | ||
| console.log(JSON.stringify({ success: false, error: result.error.message })); |
Contributor
|
Claude Security Review: no high-confidence findings. (run) |
…ain.py template
The hasMemory/hasPayment/hasConfigBundle nesting was missing a closing
{{/if}} for the outer hasMemory block, causing Handlebars parse errors
during agentcore create.
…ullish coalescing - Use serviceEndpoint() instead of hardcoded .amazonaws.com - Type JSON.parse results as Record<string, unknown> to avoid unsafe any - Use ?? instead of || for userId fallback
From 15-agent parallel bugbash:
- H1: Use serializeResult() in PaymentManagerPrimitive add handler (JSON {} fix)
- H2: Service principal uses dnsSuffix(region) for multi-partition
- H3: wirePaymentCapability handles BYO agent pattern (Agent() fallback)
- H4: Protocol check moved above auto-session block, autoSession in predicate
- H5: Vended CDK test fixture updated (remove configBundles, add payments)
- M1: remove-all preserves $schema and tags fields
- M2: Template derives connector/role from scoped prefix (not independent scan)
- L1: removeEnvVars writes empty string (not bare newline) when no keys remain
- L2: payment-env.ts guards processPaymentRoleArn before injection
…schema The CDK's AgentCoreProjectSpecSchema now includes $schema, configBundles, abTests, and httpGateways fields (matching what actually exists in agentcore.json). This eliminates all `as any` casts in the vended CDK template and CLI remove-all handler. Also updates L3 CDK constructs package with these schema additions.
… to migrate - Delete payment-iam.ts entirely (addPaymentDenyToExecutionRole was unnecessary defense-in-depth that surprises customers with unexpected IAM mutations) - Delete migratePaymentResources, shouldMigratePayments, cleanupImperativePayment (migration path for users that don't exist — feature hasn't shipped) - Delete ensureProcessPaymentRole, ensureResourceRetrievalRole, deletePaymentRoles (CDK constructs handle all IAM role creation) - Simplify cleanupPaymentCredentialProviders to only handle credential providers - Remove migration detection block from deploy actions
…d params - Delete PaymentConnectorResult, PaymentManagerResult, PaymentDeployResult (zero consumers) - Remove roleCreatedByCli from PaymentDeployedState (CDK manages roles, field is meaningless) - Remove unused accountId param from SetupPaymentResourcesOptions - Remove dead migration comment from actions.ts - Move cleanupPaymentCredentialProviders to static import (AGENTS.md: no inline imports)
… stale code CRITICAL: - TUI teardown now cleans up payment credential providers before stack destroy HIGH: - Remove dead imperative API exports from barrel (createPaymentManager, etc.) - Fix "backward compatibility" comment (unreleased feature has no backward compat) MEDIUM: - Remove console.error in getOrCreatePaymentSession (silent fallthrough to create) - Fix stale variable name processor → manager in useCreatePayment - Fix autoPayment schema to just .optional() (no confusing default+optional combo) - Fix connector description to mention both providers - Fix unused catch variables (prefix with _)
…OM_JWT
Fix 1 — Delete dead code:
- Remove createPaymentManager, listPaymentManagers, deletePaymentManager,
createPaymentConnector, deletePaymentConnector, listPaymentConnectors,
generateClientToken and ~14 associated type interfaces from agentcore-payments.ts
- These had zero call sites (CDK constructs handle all resource creation)
- Removed ~270 lines of dead code
Fix 2 — Inject AGENTCORE_PAYMENT_{NAME}_AUTH_MODE:
- cdk-stack.ts: inject AUTH_MODE='bearer' when authorizerType is CUSTOM_JWT
- deployed-state.ts: add authorizerType to PaymentDeployedStateSchema
- outputs.ts: pass authorizerType through from spec in parsePaymentOutputs
- actions.ts + useDeployFlow.ts: include authorizerType in paymentSpecs
- payment-env.ts: read authorizerType from project spec for dev mode
- payments.py: read from prefixed env var (${_prefix}AUTH_MODE)
Without this fix, CUSTOM_JWT users always get SigV4 auth mode at runtime.
120 new tests across 7 files (6 new + 1 extended): - parsePaymentOutputs (23): output key mapping, missing fields, multi-manager - PaymentManagerPrimitive (20): add/remove/cascade/getRemovable/previewRemove - PaymentConnectorPrimitive (18): add/remove/composite-key/previewRemove - validate action.ts (9): all payment error paths in handleValidate - payment-env (7): dev-mode env var injection + AUTH_MODE - pre-deploy-payments (15): credential provider create/update/cleanup - wirePaymentCapability (17): template/BYO patching, idempotency Total suite: 4036 tests passing.
…alignment
- Fix TUI deploy bug: runPaymentPreDeploy now calls setAllCredentials so
useDeployFlow.persistDeployedState has correct connector ARNs
- Remove export from 9 dead type interfaces in agentcore-payments.ts
- Rename PaymentCredentialProviderResult → PaymentCredentialProviderApiResult
to resolve name collision with payment-types.ts
- Fix defaultSpendLimit schema mismatch: CDK now uses z.string().optional()
matching CLI (was z.object({amount,currency}) — incompatible)
- Remove dead PaymentCredentialProviderResult re-export from barrel
Payment credential providers use the same /identities/ endpoint as API key and OAuth providers. Move setupPaymentCredentialProviders, hasPaymentCredentialProviders, and cleanupPaymentCredentialProviders into pre-deploy-identity.ts alongside the other credential provider operations. - Delete pre-deploy-payments.ts (merged into pre-deploy-identity.ts) - Delete payment-types.ts (types inlined in pre-deploy-identity.ts) - Rename: setupPaymentResources → setupPaymentCredentialProviders - Rename: hasPaymentManagers → hasPaymentCredentialProviders - Update all import paths and barrel exports - Update test imports
- H2: remove abTests/httpGateways from vended cdk.test.ts (not in CDK schema) - H3: fix double-blank-lines in wirePaymentCapability (regex captured newlines) - H4: import PAYMENT_SYSTEM_PROMPT and use it in wired Agent constructor - M1: respect --dry-run flag in `remove all` CLI path (was destructive) - M2: sanitize underscores from CDK logical IDs (toCdkId helper) - M4: reject invalid --auto-payment values instead of coercing to true - M5: require --provider explicitly (no silent CoinbaseCDP default) - H5: add --json flag to validate command - L9/L10: validate payment flags early (mutual exclusion, empty strings)
- M6: AUTH_MODE reads from deployed state directly (not project spec) - M7: remove dead CREDENTIAL_PROVIDER_NAME and per-connector env var injection - M8: use process.exit(1) instead of process.exitCode = 1 in CDK bin
…l types Move the payment-specific upfront missing-vars list into a shared assertEnvFileExists() helper that runs once at deploy start and lists every required env var across ApiKey, OAuth2, and payment connectors. Users populating credentials see the full list at once instead of discovering them piecemeal across separate setup steps.
- Update asset snapshots after mainline updates to template files - Make getAllCredentials tolerant of projectSpec without payments field (some test fixtures don't set it)
The earlier regex used a lookahead that stopped at the wrong newline boundary, leaving the function body of get_or_create_agent() orphaned at module scope (a Python SyntaxError on import). Replace the lookahead with a hard anchor on 'return _agent' since that's the unambiguous end of the function body.
Found in end-to-end deploy + invoke testing: 1. Vended cdk-stack.ts: grant runtime execution role sts:AssumeRole on the ProcessPaymentRole. ProcessPaymentRole's trust policy allows AccountRootPrincipal, but the caller still needs sts:AssumeRole on its own role. Without this, every invoke that touches payments fails with AccessDenied. 2. Vended payments.py: detect whether the installed bedrock-agentcore SDK supports the boto3_session field via inspect.signature() before passing it. The field was added in 1.11; older published versions (1.10 and below) reject the kwarg. Falls back to the runtime role's default credentials with a warning when the SDK is too old.
AWS docs (https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments-prerequisites.html) ship the StripePrivy authorization-private-key with a 'wallet-auth:' prefix. The CLI's base64 validator rejected this with a misleading error message. Strip the prefix transparently in both CLI and TUI so users can paste the key straight from the docs.
wirePaymentCapability used to drop a Strands-shaped payments.py and regex-rewrite main.py for every runtime, regardless of framework. On LangGraph, GoogleADK, OpenAIAgents, and AutoGen templates the regex either no-ops or corrupts the agent's Agent() constructor (none of them accept plugins=). Detect the framework by import signature on main.py and skip non-Strands runtimes cleanly. Surface a warning on the add success path listing the skipped runtime names so the user knows payments must be wired manually for those frameworks. - PaymentManagerPrimitive.add: collect skippedRuntimes, return in result. - wirePaymentCapability: read main.py first; bail before any fs writes if "from strands import" is absent. - CLI add: warn when skippedRuntimes is non-empty. - Tests: 6 new cases for non-Strands gating + missing main.py; existing fixtures updated to include the strands import where the intent was the Strands wiring path.
The validate JSON output logs result.error.message, which CodeQL flags because the upstream validation builds error strings from env-var NAMES (e.g. AGENTCORE_CREDENTIAL_FOO_API_KEY_SECRET — the name of the env var, not its value). The names are deterministic public strings derived from the credential name; they never contain credential values. Suppress the alert with a justification.
…, regex, recovery - F-01-1: gate payment env-var injection + IAM grants by language (Python) and protocol (HTTP). New helper isPaymentEligibleRuntime is used in PaymentManagerPrimitive.add, the vended cdk-stack.ts payment loop, and dev/payment-env.ts so non-Python or non-HTTP runtimes never get env vars they cannot consume. - F-01-2: emit hooks=[ConfigBundleHook()] alongside plugins= in both the template and the regex-emitted Agent block. Prevents existing config-bundle customers from silently losing system-prompt injection when adding payments. - R-13-1: insert the payment import at the top of main.py (after the file docstring and any from __future__ imports). Removes a regex that could splice the new import inside a parenthesised multi-line from x import (...) block and produce a SyntaxError. - R-13-2: tighten the agent-replacement regexes — allow trailing # type: ignore comments, allow typed-annotation _agent: Agent | None = None form, and abort cleanly when the call site is replaced but the singleton has an unrecognised shape rather than shipping corrupted code. - S-02-1: re-add after remove now correctly patches main.py. The previous early-return short-circuited the whole flow when remove() left payments.py behind. - D-12-2: when CFN succeeds but the post-deploy state-write fails, surface the stack name + region + recovery commands so the user can fix the local I/O issue or manually delete the orphan stack. - C-05-3: exclude .env / .env.local / .env.* files from the deploy zip at any depth. Closes a footgun for BYO projects with --code-location . where agentcore/.env.local could otherwise be packaged into S3. Tests: 8 new cases covering paren-aware imports, docstring + future, type-ignore comments, typed annotations, abort path, re-add cycle, and .env exclusion at any depth.
Contributor
|
Claude Security Review: no high-confidence findings. (run) |
…prompt update The deployed agent runtime role was missing every Get/List/Create payment instrument+session action and ProcessPayment. The L3 stack only granted sts:AssumeRole on the ProcessPaymentRole, which carries only ProcessPayment — and the SDK plugin's auto-pay path calls GetPaymentInstrument on the runtime's own credentials before any role assumption. Without this grant the plugin fails with AccessDeniedException on the very first 402 it tries to settle. Add the seven required actions to the runtime role's inline policy in the vended CDK stack template (src/assets/cdk/lib/cdk-stack.ts). Scoped to the manager ARN. Also update PAYMENT_SYSTEM_PROMPT in the Strands payments capability to mention the http_request tool, which the AgentCorePaymentsPlugin now provides automatically (see SDK PR aws/bedrock-agentcore-sdk-python#493). The other prompt lines for get_payment_session / get_payment_instrument_balance / list_payment_instruments are unchanged — those tools were already provided by the plugin. Snapshot updated to reflect the cdk-stack.ts addition.
Contributor
|
Claude Security Review: no high-confidence findings. (run) |
`getOrCreatePaymentSession` was reading `result.paymentSessionId`
directly off the CreatePaymentSession API response. That field doesn't
exist at top level — the service wraps the session in
`paymentSession.paymentSessionId` per the public model
(`bedrock-agentcore` Data Plane). The TypeScript `as` cast doesn't
validate at runtime, so the function silently returned `undefined`,
the auto-session reassignment in handleInvoke set
`options.paymentSessionId = undefined`, and the runtime received an
invoke body without payment_session_id. Plugin then errored with
"payment_session_id is required for x402 payments".
Confirmed against the live API model: CreatePaymentSession output
shape is {paymentSession: {paymentSessionId, paymentManagerArn,
userId, expiryTimeInMinutes, ...}} (note: ListPaymentSessions returns
paymentSessions[] at top level — only Create wraps).
Verified end-to-end on Base Sepolia: with this fix, `agentcore invoke
--auto-session` creates the session, forwards it to the runtime, the
plugin signs via ProcessPayment, and the merchant settles on chain.
Existing unit tests passed because the mocked client returns a stub
shaped however the test wants — they don't enforce the real API
response shape.
Contributor
|
Claude Security Review: no high-confidence findings. (run) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds AgentCore Payments as a first-class resource type in the CLI. Includes:
agentcore add payment-manager/payment-connectorcommands (CLI + TUI wizard)agentcore remove payment-manager/payment-connectorwith cascading deleteAgentCorePaymentManager+AgentCorePaymentConnectorL3 constructs--payment-instrument-id,--payment-session-id,--auto-sessionRelated Issue
Closes #
Documentation PR
Type of Change
Testing
How have you tested the change?
npm run test:unitandnpm run test:integnpm run typechecknpm run lintsrc/assets/, I rannpm run test:update-snapshotsand committed the updated snapshots120 new payment-specific unit tests added covering:
Checklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the
terms of your choice.
Update — runtime IAM + system prompt for x402 auto-pay (commit 1f6a2f2)
End-to-end testing surfaced a missing IAM grant and a stale system prompt. Both blockers are fixed in this commit; the rest of the PR is unchanged.
What broke without the fix
The agent runtime role only had
sts:AssumeRoleon theProcessPaymentRole(which carries onlyProcessPayment). ButAgentCorePaymentsPlugin.generate_payment_headercallsGetPaymentInstrumenton the runtime's own credentials before any role assumption. The plugin failed withAccessDeniedExceptionon the very first 402 it tried to settle:Fix
Grant the runtime role the seven payment data-plane actions, scoped to the manager ARN, in the vended CDK stack template:
bedrock-agentcore:GetPaymentInstrumentbedrock-agentcore:ListPaymentInstrumentsbedrock-agentcore:GetPaymentInstrumentBalancebedrock-agentcore:GetPaymentSessionbedrock-agentcore:ListPaymentSessionsbedrock-agentcore:CreatePaymentSession(so--auto-sessionworks without a separate ManagementRole call)bedrock-agentcore:ProcessPaymentA code comment notes this deviates from the canonical 4-role split in the AgentCore Payments beta guide. The deviation is required by the current SDK plugin, which calls
GetPaymentInstrumentfrom insidegenerate_payment_header. If the SDK is later updated to accept a pre-fetched instrument and to split create-session into a backend-only flow, this grant can be tightened.System-prompt update
PAYMENT_SYSTEM_PROMPTincapabilities/payments/payments.pynow mentions thehttp_requesttool that the SDK plugin auto-registers:The other prompt lines (for
get_payment_session,get_payment_instrument_balance,list_payment_instruments) are unchanged — those tools were already provided by the plugin.SDK dependency
This commit's system-prompt change is forward-looking; the
http_requesttool ships in bedrock-agentcore SDK PR aws/bedrock-agentcore-sdk-python#493. Once that merges and a new SDK release ships, the template'spyproject.toml.hbsshould pinbedrock-agentcore >= <released-version>. Pin bump intentionally NOT included in this commit — it should land alongside the published version number.End-to-end verification (after IAM fix + SDK PR)
Tested against
https://x402.bitcoinsapi.com/weatheron Base Sepolia from a fresh project. Plugin path runs cleanly:Detected 402 Payment Required response from tool: http_requestSuccessfully retrieved instrument(← fails without this commit's IAM grant)Successfully processed payment for user default-user(← fails without SDK PR feat: addagentcore tracescommand and trace link in invoke TUI #493)Added payment header to tool input headers: ['PAYMENT-SIGNATURE'](Final on-chain settle currently fails inside the seller-side facilitator simulation due to two unrelated issues in CDP's signing service —
validAfterset to current timestamp instead ofnow-N, and an ECRecover failure on the returnedvbyte. Those are tracked separately as service-side bugs and are out of scope for this PR.)