From 32c2b8e14c658a17f9c5d3311a503b7a1b447a60 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Thu, 29 Oct 2026 12:24:00 -0400 Subject: [PATCH 01/10] feat(aws-agents): update skills to reflect AgentCore CLI v1 --- .../aws-agents/skills/agents-build/SKILL.md | 30 +- .../skills/agents-build/references/browser.md | 4 +- .../agents-build/references/integrate.md | 34 +- .../references/local-vs-deployed.md | 2 +- .../skills/agents-build/references/memory.md | 25 +- .../skills/agents-build/references/migrate.md | 128 +--- .../agents-build/references/multi-agent.md | 65 +- .../agents-build/references/payments.md | 221 +------ .../references/request-headers.md | 4 +- .../agents-build/references/teardown.md | 165 +---- .../skills/agents-build/references/vpc.md | 26 +- .../scripts/process_payment_tool.py | 2 +- .../scripts/setup_payment_user.py | 52 +- .../scripts/test_setup_payment_user.py | 39 ++ .../aws-agents/skills/agents-connect/SKILL.md | 565 +++--------------- .../agents-connect/references/policy.md | 336 +---------- plugins/aws-agents/skills/agents-pay/SKILL.md | 46 +- .../agents-pay/references/openclaw-setup.md | 17 +- .../agents-pay/references/operator-guide.md | 9 +- .../agents-pay/references/security-model.md | 6 +- .../skills/agents-pay/references/setup.md | 102 ++-- .../agents-pay/references/troubleshooting.md | 2 +- .../agents-pay/scripts/agents_pay_admin.py | 79 ++- .../scripts/test_resource_resolution.py | 52 ++ .../aws-core/skills/amazon-bedrock/SKILL.md | 2 +- .../references/agentcore-harness.md | 31 +- .../cli.md | 36 +- .../deploy.md | 168 +++--- .../mapping.md | 12 +- ...ate-bedrock-agents-to-agentcore-harness.md | 4 +- skills/core-skills/amazon-bedrock/SKILL.md | 2 +- .../references/agentcore-harness.md | 31 +- .../cli.md | 36 +- .../deploy.md | 168 +++--- .../mapping.md | 12 +- ...ate-bedrock-agents-to-agentcore-harness.md | 4 +- 36 files changed, 726 insertions(+), 1791 deletions(-) create mode 100644 plugins/aws-agents/skills/agents-build/scripts/test_setup_payment_user.py diff --git a/plugins/aws-agents/skills/agents-build/SKILL.md b/plugins/aws-agents/skills/agents-build/SKILL.md index 27f4ce937..f19800b2e 100644 --- a/plugins/aws-agents/skills/agents-build/SKILL.md +++ b/plugins/aws-agents/skills/agents-build/SKILL.md @@ -10,7 +10,7 @@ description: > "A2A", "A2A auth", "orchestrator not delegating", "specialist not called", "migrate Bedrock Agent", "migration issue", "change model", "browser tool", "code - interpreter", "delete agent", "tear down", "agentcore remove", + interpreter", "delete agent", "agentcore remove", "cross-account memory", "add payments capability to my agent", "wire payments plugin", "integrate x402 payments with the agent I'm building", @@ -24,7 +24,7 @@ metadata: type: skill version: "1.0.0" author: aws-agentcore - requires-cli: ">=0.9.0" + requires-cli: ">=1.0.0" --- # build @@ -41,13 +41,13 @@ Add capabilities to your AgentCore agent project. - Adding the Browser tool so the agent can navigate websites - Adding the Code Interpreter so the agent can execute code in a sandbox - Adding AgentCore Payments so the agent can pay for x402- or MPP-protected APIs, tools, or content -- Removing resources from your project or tearing down a deployment +- Removing individual resources from your project Do NOT use for: - Connecting to external tools/APIs via Gateway (OpenAPI specs, Lambda, MCP servers, credentials, policies) → use `agents-connect` - Scaffolding a new project → use `agents-get-started` -- Deploying → use `agents-deploy` +- Deploying or tearing down a deployment → use `agents-deploy` ## Input @@ -61,13 +61,13 @@ Do NOT use for: ### Step 0: Verify CLI version -Run `agentcore --version`. This skill requires v0.9.0 or later. +Run `agentcore --version`. This skill requires v1.0.0 or later. -If older: "Run `agentcore update` to get the latest version." +If older, offer `npm install -g @aws/agentcore@latest` (or `agentcore update`), then verify the new version. For a v0 project (`"version": 1`), commit current work, run `agentcore migrate` to review its plan, and apply the reviewed plan with `agentcore migrate --yes` before following v1 instructions. ### Step 1: Read project context -Read `agentcore/agentcore.json` to understand the current project — framework, existing resources, agent configuration. +Read `agentcore/agentcore.json` to understand the current project — its `runtimes`, `harnesses`, `memories`, and other resources. For a Harness, read `app//harness.yaml` too. If `agentcore/agentcore.json` is not found: @@ -99,7 +99,8 @@ Based on the developer's prompt and `$ARGUMENTS`, load the appropriate reference | Browser tool, web navigation, form filling, scraping, Nova Act, Playwright, live view | [`references/browser.md`](references/browser.md) | | Code Interpreter, execute code, sandbox, run Python/JS/TS, data analysis in agent, pandas | [`references/code-interpreter.md`](references/code-interpreter.md) | | Payments, pay for x402 or MPP content, 402 Payment Required, Machine Payments Protocol, WWW-Authenticate: Payment, microtransactions, paid API/tool, payment manager/connector | [`references/payments.md`](references/payments.md) | -| Delete agent, remove resource, tear down, clean up, destroy, start fresh | [`references/teardown.md`](references/teardown.md) | +| Remove one resource from the project | [`references/teardown.md`](references/teardown.md) | +| Tear down a deployed target, clean up, destroy, start fresh | `agents-deploy` teardown workflow | | Change model, switch model, use Haiku/Sonnet/Nova, different model | Inline — see "Changing the model" below | If the developer asks about the difference between local dev and deployed (e.g., "why does my memory work after deploy but not locally?"), load [`references/local-vs-deployed.md`](references/local-vs-deployed.md) alongside the specific workflow reference. @@ -110,7 +111,7 @@ If the intent is ambiguous, ask the developer which capability they want to add. ### Changing the model -The model is configured in `app//model/load.py` (scaffolded by `agentcore create`). To change it: +For a Strands Runtime, the model is configured in `app//model/load.py` (the `agent-python-strands` template uses `app/agent/model/load.py`). To change it: 1. Open `app//model/load.py` 2. Change the `model_id` parameter in the `BedrockModel()` constructor @@ -135,18 +136,11 @@ After changing the model: - If using `agents-harden`, update the IAM policy to scope to the new model ARN - Run `agentcore dev` to test locally, then `agentcore deploy` to update the deployed agent -No `agentcore.json` change is needed — the model is configured in code, not in the project config. +For a Harness, edit `app//harness.yaml` instead, under `model.modelId`, then run `agentcore deploy`. A Runtime model is configured in code. ### Pre-flight: validate any `--name` before generating the CLI command -Whichever reference you load, most end up producing an `agentcore add --name ` command. The CLI fails **late** on invalid names — you'll see the error after walking through prompts, not before running the command. Validate up front: - -| Resource | Max chars | Allowed | Starts with | -|---|---|---|---| -| Agent (`add agent`) | 48 | alphanumeric + `_` | letter | -| Memory, gateway, gateway-target, credential, evaluator, online-eval, policy, policy-engine, payment-manager, payment-connector | 48 | alphanumeric + `_` | letter | - -Count the characters before constructing the command. If the name is over the limit or contains hyphens, dots, or spaces, push back: "`` is N characters / uses `-`, which the CLI rejects. How about ``?" Never run the command with an invalid name hoping the CLI message will be clear. +Check `agentcore add --help` and the v1 schema for the selected resource's name rules. Resource names differ from project names; do not apply one rule to every resource. Note: `agentcore create --name` (the project name) has a **stricter 23-char limit** and does not allow underscores. That's covered in `agents-get-started`; if you see the developer re-running create, flag the 23-char limit specifically. diff --git a/plugins/aws-agents/skills/agents-build/references/browser.md b/plugins/aws-agents/skills/agents-build/references/browser.md index b047f1a80..752307ecc 100644 --- a/plugins/aws-agents/skills/agents-build/references/browser.md +++ b/plugins/aws-agents/skills/agents-build/references/browser.md @@ -131,7 +131,7 @@ Install: `pip install bedrock-agentcore nova-act boto3` The `browser_session` context manager handles start/stop. Do not leak sessions — always use the context manager or wrap raw `BrowserClient` calls in try/finally. -**Credential handling:** the Nova Act API key is a secret. If this is running inside an AgentCore Runtime agent, register it as a credential (`agentcore add credential --name NovaAct --api-key ...`) and retrieve it with `@requires_api_key(provider_name="NovaAct")`. Do not put it in runtime env vars. See `agents-connect` Path D. +**Credential handling:** the Nova Act API key is a secret. If this is running inside an AgentCore Runtime agent, register it as a credential (`agentcore add credentials api-key --name NovaAct --api-key file://`) and retrieve it with `@requires_api_key(provider_name="NovaAct")`. Do not put it in runtime env vars. See `agents-connect` Path D. ## Path C — Playwright for scripted automation @@ -230,5 +230,5 @@ If your agent runs in VPC mode, the Browser tool can also run in VPC. See [`vpc. - Browser sessions are always wrapped in a context manager or try/finally — never leaked - IAM is scoped to `browser/*` in the account, not `Resource: "*"` -- Nova Act API keys and other secrets use `agentcore add credential` + `@requires_api_key`, not env vars +- Nova Act API keys and other secrets use `agentcore add credentials api-key` + `@requires_api_key`, not env vars - The code handles the case where the agent runs outside AgentCore Runtime (no `.env.local`, no credential provider) — typically by reading a local secret for development and the credential provider for production diff --git a/plugins/aws-agents/skills/agents-build/references/integrate.md b/plugins/aws-agents/skills/agents-build/references/integrate.md index 56a030080..9f2bf2abd 100644 --- a/plugins/aws-agents/skills/agents-build/references/integrate.md +++ b/plugins/aws-agents/skills/agents-build/references/integrate.md @@ -33,40 +33,24 @@ Do NOT use for: Read `agentcore/agentcore.json` to get the agent name. Then check if it's deployed: ```bash -agentcore status --type agent +agentcore status --json ``` **If not deployed:** "Your agent needs to be deployed before you can call it from an app. Run `agentcore deploy` first, or use the `agents-deploy` skill for guidance." Do not proceed until the agent is deployed. -### Step 2: Get the agent endpoint +### Step 2: Get the Runtime ARN and target ```bash -agentcore fetch access --name --type agent +agentcore status --json ``` -This returns: - -- **Agent Runtime ARN** — needed for SDK invocation -- **Endpoint URL** — for direct HTTPS calls -- **Auth configuration** — what auth method is configured - -Note the auth type from the output. It determines how the client app authenticates. +Select the deployed `runtime` entry by name in the `resources` array. Its `arn` is the identifier for SDK invocation. If the project has multiple deployment targets, pass `--target ` and use that target's region. The old `agentcore fetch access` command is absent in v1. `agentcore runtime get --id --json` reads further service details when needed. ### Step 3: Determine auth method -Read the agent's `authorizerType` field from `agentcore/agentcore.json` (it's a top-level field on the runtime entry; JWT details live in the separate `authorizerConfiguration` object on the same runtime). - -| Auth type | How the client authenticates | Best for | -|---|---|---| -| **None** (default) | IAM SigV4 signing on the request | Backend services with AWS credentials | -| **AWS_IAM** | IAM SigV4 signing on the request | Backend services, Lambda-to-agent calls | -| **CUSTOM_JWT** | Bearer token in Authorization header | Web/mobile apps with an identity provider | - -**If no authorizer is configured:** The agent uses IAM auth by default. The calling identity needs `bedrock-agentcore:InvokeAgentRuntime` permission. - -**If CUSTOM_JWT:** The client sends a JWT from the configured identity provider. The agent validates it against the discovery URL, allowed audience, and allowed clients configured during setup. +Read the Runtime entry's `authorizerType` and `authorizerConfiguration` in `agentcore/agentcore.json`. An unset authorizer uses IAM. AWS_IAM callers sign requests with SigV4 and need `bedrock-agentcore:InvokeAgentRuntime`; CUSTOM_JWT callers present a JWT from the configured issuer. Keep bearer tokens out of source code and logs. For a quick CLI verification, use `agentcore invoke --runtime --payload '{"prompt":"Hello"}' --target ` and add `--bearer-token` only for a JWT Runtime. ### Step 4: Generate client code @@ -88,7 +72,7 @@ try: payload=json.dumps({ "prompt": "Hello, what can you do?" }).encode(), - runtimeSessionId="session-123", # reuse for multi-turn conversations + runtimeSessionId="550e8400-e29b-41d4-a716-446655440000", # reuse for multi-turn conversations ) # Handle streaming response — response["response"] is a StreamingBody @@ -156,7 +140,7 @@ const response = await client.send( payload: new TextEncoder().encode( JSON.stringify({ prompt: "Hello, what can you do?" }) ), - runtimeSessionId: "session-123", + runtimeSessionId: "550e8400-e29b-41d4-a716-446655440000", }) ); @@ -305,7 +289,7 @@ response = agentcore.invoke_agent_runtime( agentRuntimeArn="arn:aws:bedrock-agentcore:::runtime/", qualifier="DEFAULT", payload=json.dumps({"prompt": "hello"}).encode(), - runtimeSessionId="session-123", + runtimeSessionId="550e8400-e29b-41d4-a716-446655440000", ) ``` @@ -342,7 +326,7 @@ client = boto3.client("bedrock-agentcore", region_name="") response = client.invoke_agent_runtime_command( agentRuntimeArn="", qualifier="DEFAULT", - runtimeSessionId="session-123", # must be an existing session + runtimeSessionId="550e8400-e29b-41d4-a716-446655440000", # must be an existing session command="ls -la /tmp && cat /tmp/status.json", ) diff --git a/plugins/aws-agents/skills/agents-build/references/local-vs-deployed.md b/plugins/aws-agents/skills/agents-build/references/local-vs-deployed.md index 645eedd64..209215ec2 100644 --- a/plugins/aws-agents/skills/agents-build/references/local-vs-deployed.md +++ b/plugins/aws-agents/skills/agents-build/references/local-vs-deployed.md @@ -16,7 +16,7 @@ AgentCore has a local dev server (`agentcore dev`) and a deployed runtime. They | Traces (X-Ray) | ✅ `agentcore dev` emits OTEL to CloudWatch by default; disable with `--no-traces` | ✅ auto-enabled | | CloudWatch logs | ✅ via ADOT / OTEL wiring (same path as traces) | ✅ if using `logging` module + OTEL | | **Evaluator *definition*** (`agentcore add evaluator`, writing the instructions/code) | ✅ — writes to `agentcore.json`; custom code is unit-testable locally | ✅ | -| **`agentcore run eval`** (on-demand eval over traces) | ✅ — operates on CloudWatch spans; local-dev spans land there if OTEL is on (default) | ✅ | +| **`agentcore eval ondemand`** (on-demand eval over traces) | ✅ — operates on CloudWatch spans; local-dev spans land there if OTEL is on (default) | ✅ | | **`Evaluate` API with hand-constructed spans** (boto3) | ✅ — no runtime needed at all; submit `SessionSpans` directly | ✅ | | **Dataset runner** (`OnDemandEvaluationDatasetRunner`) | ❌ invokes an AgentCore Runtime agent in its pipeline | ✅ | | **Online eval monitoring** (`agentcore add online-eval`) | ❌ ingests traces continuously from deployed runtime | ✅ | diff --git a/plugins/aws-agents/skills/agents-build/references/memory.md b/plugins/aws-agents/skills/agents-build/references/memory.md index 258be5edf..abf53fc6f 100644 --- a/plugins/aws-agents/skills/agents-build/references/memory.md +++ b/plugins/aws-agents/skills/agents-build/references/memory.md @@ -94,7 +94,7 @@ Which strategy (or combination) do you want? Run the CLI command to add memory to the project config: ```bash -agentcore add memory --name --strategies --expiry 30 +agentcore add memory --name MyMemory --strategies SEMANTIC,USER_PREFERENCE --event-expiry-duration 30 ``` This updates `agentcore/agentcore.json`. The memory resource is provisioned when you next run `agentcore deploy` — it takes 2–5 minutes to become active. @@ -104,12 +104,11 @@ The resulting config entry looks like: ```json { "memories": [{ - "type": "AgentCoreMemory", "name": "MyMemory", "eventExpiryDuration": 30, "strategies": [ - {"type": "SEMANTIC"}, - {"type": "USER_PREFERENCE"} + {"type": "SEMANTIC", "namespaceTemplates": ["/users/{actorId}/facts"]}, + {"type": "USER_PREFERENCE", "namespaceTemplates": ["/users/{actorId}/preferences"]} ] }] } @@ -458,7 +457,7 @@ Before shipping a memory integration for ADK or Claude SDK, validate the end-to- 2. Invoke the agent with facts to remember 3. Start a new session 4. Invoke again and verify the agent recalls those facts -5. Check `agentcore logs --runtime --query "memory" --since 1h --level error` for any memory errors +5. Check `agentcore logs runtime --name --query "memory" --since 1h --level error` for any memory errors If you build a working pattern, consider contributing it to [`awslabs/agentcore-samples`](https://github.com/awslabs/agentcore-samples) so the next developer doesn't have to figure it out. @@ -473,9 +472,9 @@ The MEMORY__ID env var is only injected after deploy. The code above handles this gracefully — it runs without memory when the env var isn't set. To test memory: - agentcore deploy -y - agentcore invoke "My name is Alex and I prefer concise answers" - agentcore invoke "What do you know about me?" + agentcore deploy + agentcore invoke --runtime --payload '{"prompt":"My name is Alex and I prefer concise answers"}' --session-id 550e8400-e29b-41d4-a716-446655440000 + agentcore invoke --runtime --payload '{"prompt":"What do you know about me?"}' --session-id 550e8400-e29b-41d4-a716-446655440000 If using long-term memory (SEMANTIC or USER_PREFERENCE), wait 5–30 seconds between the first and second invoke — extraction runs asynchronously after @@ -529,12 +528,12 @@ Most common cause: the memory resource is configured but the code isn't reading 1. Check that LTM strategies (SEMANTIC, USER_PREFERENCE) are configured — not just SUMMARIZATION 2. Wait 5–30 seconds after a session ends before starting a new one — extraction is async -3. Verify the memory resource is ACTIVE: `agentcore status --type memory` +3. Find the Memory ID in `agentcore status --json`, then verify its service status is ACTIVE with `agentcore memory get --id --json` 4. Use UUIDs (v4) for session IDs — the platform requires a minimum of 33 characters. Short IDs like "session-1" or "test" cause LTM to fail silently. `agentcore invoke` generates compliant IDs by default. **Memory not loading at session start:** -1. Verify `MEMORY__ID` env var is set: `agentcore status --type memory --json` +1. Find the Memory ID in `agentcore status --json`, then verify `MEMORY__ID` is set in the deployed Runtime environment 2. Check the actor_id is consistent across sessions — memory is scoped per actor 3. Confirm the namespace paths in retrieval_config match the namespaces used when writing — the retrieval namespace must exactly match the namespace the strategy extracts into 4. CLI defaults use paths without trailing slashes (e.g., `/users/{actorId}/facts`). If you customized namespace templates when creating the memory resource, use whatever pattern you chose — consistency between writer and reader is what matters. @@ -543,7 +542,7 @@ Most common cause: the memory resource is configured but the code isn't reading Memory takes 2–5 minutes to become ACTIVE after `agentcore deploy`. Check status: ```bash -agentcore status --type memory +agentcore memory get --id --json ``` ## S3 delivery / export buckets must be in the same account @@ -580,7 +579,7 @@ The supported geographies and inference-region mappings change as AgentCore expa ## Beyond the CLI: memory features that require the API -The CLI's `agentcore add memory` and `agentcore.json` cover strategy selection, expiry, and basic configuration. Some memory capabilities are API/SDK-only — the CLI doesn't expose them. When the developer needs one of these, the graduation path is: create the memory via CLI as usual, deploy, then apply the additional config via boto3 or AWS CLI. +The v1 `agentcore add memory` command covers strategy selection, event expiry, indexed keys, stream delivery resources, and a KMS key ARN. Use its help before reaching for a direct API call. Resource policies and specialized strategy overrides may still need the API or SDK. **Resource-based policies** (cross-account access, principal-level restrictions): @@ -588,7 +587,7 @@ The CLI's `agentcore add memory` and `agentcore.json` cover strategy selection, import boto3, json client = boto3.client("bedrock-agentcore-control") -memory_id = "" # from: agentcore status --type memory --json +memory_id = "" # from: agentcore status --json client.put_memory_resource_policy( memoryId=memory_id, diff --git a/plugins/aws-agents/skills/agents-build/references/migrate.md b/plugins/aws-agents/skills/agents-build/references/migrate.md index 8111121a6..fe3ccfd89 100644 --- a/plugins/aws-agents/skills/agents-build/references/migrate.md +++ b/plugins/aws-agents/skills/agents-build/references/migrate.md @@ -1,125 +1,47 @@ -# migrate +# Migrate a Bedrock Agent or a v0 CLI project -Move an existing Amazon Bedrock Agent to AgentCore Runtime. +These are different migrations. Identify the source before running a command. -## When to use +## AgentCore CLI v0 project -- You have an existing Bedrock Agent (created via the Bedrock console or API) and want to run it on AgentCore Runtime -- You want to add AgentCore capabilities (Memory, Gateway, Observability) to an existing agent -- You want to move from the declarative Bedrock Agents model to a code-first framework +If `agentcore/agentcore.json` has `"version": 1`, commit the project and run `agentcore migrate` from the project root. Read the plan it prints and resolve any concerns. Apply it with `agentcore migrate --yes` only after approving that plan. Use `--target ` when the project has several deployment targets. Recheck the v2 `agentcore/agentcore.json`, run `agentcore build`, and test the migrated project before deploying. This migration preserves deployed resources; it does not import a Bedrock Agent. -## Input +## Classic Bedrock Agent to AgentCore Runtime -`$ARGUMENTS` is optional: +Use this when the source is a classic Bedrock Agent with an agent ID and an alias pointing to a prepared version. The v1 CLI translates it into a code based Runtime; it does not create a Harness. For a Harness migration, use the canonical `amazon-bedrock` guide at `skills/core-skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md`. -``` -/migrate # interactive — walks through the migration -/migrate strands # migrate targeting Strands framework -/migrate langgraph # migrate targeting LangGraph framework -``` - -## What migration does - -The `agentcore create --type import` command reads your existing Bedrock Agent's configuration and generates an AgentCore project that reproduces its behavior in a code-first framework. Specifically: - -- **System prompt** → copied into the generated `main.py` -- **Action groups (Lambda-backed)** → converted to Gateway targets with `--type lambda-function-arn` -- **Knowledge bases** → referenced in the system prompt with a note to wire retrieval manually (AgentCore doesn't auto-import KB bindings) -- **Guardrails** → noted in comments but not auto-converted (AgentCore uses Cedar policies, not Bedrock Guardrails) -- **Agent alias / version** → the import targets a specific alias, not the draft - -What migration does **not** do: - -- It does not delete or modify the original Bedrock Agent — the source agent keeps running -- It does not migrate conversation history or session state -- It does not convert Bedrock Guardrails to Cedar policies (different authorization model) -- It does not auto-wire Knowledge Base retrieval — you'll need to add that as a tool or direct SDK call - -## Prerequisites +### Prerequisites -1. The Bedrock Agent must exist and have at least one alias -2. Your AWS credentials must have `bedrock:GetAgent`, `bedrock:GetAgentAlias`, and `bedrock:ListAgentActionGroups` permissions -3. You need the agent ID, alias ID, and region +- Obtain the source agent ID, alias ID, region, and a desired Strands or LangGraph implementation. +- Confirm the alias resolves to a prepared version rather than `DRAFT`. +- Use credentials that can read the source agent, its alias, action groups, and knowledge bases. +- Start from an existing v1 project, or create one with `agentcore create --name --template empty`. -## Process +### Translate and review -### Step 1: Run the import +From the v1 project root: ```bash -agentcore create \ - --type import \ +agentcore add runtime \ + --name ImportedAgent \ --agent-id \ --agent-alias-id \ - --region \ - --name \ - --framework Strands + --framework strands \ + --region ``` -The `--framework` flag determines which code-first framework the generated project uses. Strands is recommended for the closest mapping to Bedrock Agent behavior. +`--framework` accepts `strands` or `langgraph`. `--language` is not needed on this import path. The importer writes Runtime code and configuration into the project. Inspect the generated files and `agentcore/agentcore.json`, including the prompt, model, action groups, knowledge base references, auth, and IAM. Record anything the importer did not carry over. Source and target agents can have different prompt or tool behavior; compare actual responses before cutover. -**Project name rules apply:** max 23 characters, alphanumeric only, starts with a letter. - -### Step 2: Review the generated project +### Test and deploy ```bash -cd -cat app//main.py -cat agentcore/agentcore.json -``` - -Check: - -- The system prompt matches your original agent's instructions -- Action groups appear as Gateway targets in `agentcore.json` (under `agentCoreGateways`) -- The model ID is correct for your target region - -### Step 3: Fill in what migration doesn't cover - -**Knowledge Bases:** If your Bedrock Agent used Knowledge Bases, you have two options: - -1. **Keep using the KB via boto3** — call `bedrock-agent-runtime:RetrieveAndGenerate` or `Retrieve` directly from your agent code as a tool -2. **Replace with AgentCore Memory** — if the KB was used for user-specific context, AgentCore Memory with SEMANTIC strategy may be a better fit. See [memory.md](memory.md). - -**Guardrails → Cedar policies:** Bedrock Guardrails (content filters, denied topics, word filters) don't have a 1:1 mapping to Cedar policies. Cedar policies control *which tools the agent can call and with what parameters* — they're authorization rules, not content filters. If you need content filtering, keep the guardrail logic in your agent code (pre/post-processing) or use Bedrock Guardrails as a standalone API call. - -**Custom orchestration:** If your Bedrock Agent used custom orchestration (return-of-control, custom Lambda orchestrators), you'll need to rebuild that logic in the framework's native patterns — Strands tool chains, LangGraph graph nodes, etc. - -### Step 4: Test locally and deploy - -```bash -# Test locally (memory and gateway won't be available yet) +agentcore build agentcore dev - -# Deploy when ready -agentcore deploy -y - -# Verify -agentcore invoke "Hello, what can you do?" -agentcore status +agentcore deploy --region +agentcore status --json +agentcore invoke --runtime ImportedAgent --payload '{"prompt":"Hello, what can you do?"}' ``` -### Step 5: Cut over traffic - -Once the AgentCore agent is working correctly: - -1. Update your application to invoke the AgentCore Runtime instead of the Bedrock Agent -2. See [integrate.md](integrate.md) for the invocation patterns (SigV4, JWT, SDK) -3. Keep the original Bedrock Agent running as a fallback until you're confident -4. Delete the Bedrock Agent only after the AgentCore agent has been stable in production - -## Common migration issues - -**"Model not available in target region"** -The imported agent may reference a model ID that isn't available in your AgentCore deployment region. Edit `model/load.py` to use a cross-region inference profile or a model available in your region. - -**"Action group Lambda in a different region"** -Gateway targets can invoke Lambda functions cross-region, but latency increases. Consider deploying the Lambda in the same region as your AgentCore agent, or accept the latency trade-off. - -**"Agent behavior differs after migration"** -The most common cause is prompt format differences between Bedrock Agent's orchestration and the code-first framework. Bedrock Agent injects structured XML around tool results; Strands/LangGraph use different formats. Tune the system prompt to compensate. - -## Output +Set the first deployment region deliberately. `agentcore/aws-targets.json` starts empty and the first deploy creates the default target using the CLI's effective region. Use `--target` to select another target on later commands. -- A working AgentCore project that reproduces the Bedrock Agent's behavior -- A list of what was auto-converted and what needs manual work -- Guidance on cutting over traffic from the old agent to the new one +Keep the source Bedrock Agent running until application calls have been tested against the new Runtime. Its history and sessions are separate. For application invocation, load [integrate.md](integrate.md). diff --git a/plugins/aws-agents/skills/agents-build/references/multi-agent.md b/plugins/aws-agents/skills/agents-build/references/multi-agent.md index a3f17d99a..735908eb1 100644 --- a/plugins/aws-agents/skills/agents-build/references/multi-agent.md +++ b/plugins/aws-agents/skills/agents-build/references/multi-agent.md @@ -27,15 +27,16 @@ Build AgentCore systems where agents delegate work to other agents. The specialist is a standard AgentCore agent. Deploy it normally: ```bash -agentcore create --name SpecialistAgent --defaults -# ... add your specialist logic to app/SpecialistAgent/main.py ... -agentcore deploy -y +agentcore create --name SpecialistProject --template agent-python-strands +cd SpecialistProject +# ... add your specialist logic to app/agent/main.py ... +agentcore deploy ``` Get the specialist's runtime ARN after deploy: ```bash -agentcore status --runtime SpecialistAgent --json | jq -r '.runtimes[0].arn' +agentcore status --json | jq -r '.resources[] | select(.resourceType == "runtime" and .name == "agent") | .arn' ``` ### Step 2: Add the specialist as a tool in the orchestrator @@ -204,38 +205,9 @@ The orchestrator's execution role needs permission to invoke the specialist: Add this to `agentcore/agentcore.json` under the orchestrator agent's IAM config, or add it manually to the auto-created execution role after deploy. -### Step 4: Pass the specialist ARN at deploy time +### Step 4: Pass the specialist ARN to the orchestrator -Add the specialist ARN as an environment variable in the orchestrator's deployment: - -```bash -# After deploying the specialist, get its ARN: -SPECIALIST_ARN=$(agentcore status --runtime SpecialistAgent --json | jq -r '.runtimes[0].arn') - -# For local dev, write to .env.local: -echo "SPECIALIST_AGENT_ARN=$SPECIALIST_ARN" >> agentcore/.env.local -``` - -**For the deployed orchestrator**, the specialist ARN needs to be available as an environment variable. The recommended pattern is: - -1. **Edit `agentcore/agentcore.json`** — find the orchestrator agent's entry and add the env var to its configuration (the exact field name depends on your CLI version; run `agentcore validate` after editing). In current CLI versions, agent environment variables are typically managed through the deployment config. - -2. **Or use CDK overrides** — for teams using the CDK constructs directly, set the env var in the Runtime construct's environment property. - -3. **Or write the env var at deploy time** — some teams use a pre-deploy script that generates `agentcore/.env.local` and `agentcore/agentcore.json` updates together: - -```bash -# pre-deploy.sh — run before every orchestrator deploy -SPECIALIST_ARN=$(agentcore status --runtime SpecialistAgent --json | jq -r '.runtimes[0].arn') -echo "SPECIALIST_AGENT_ARN=$SPECIALIST_ARN" >> agentcore/.env.local - -# Then deploy -agentcore deploy -y -``` - -The CLI does not currently provide a dedicated `--env` flag on `agentcore add agent`. Check `agentcore add agent --help` for the current options in your CLI version. - ---- +Run `agentcore status --json` in the specialist project and select the deployed Runtime ARN from the `resources` array. For a different deployment target, pass `--target `. In the orchestrator project, set that ARN as the `SPECIALIST_AGENT_ARN` value in its Runtime entry's `environmentVariables` object in `agentcore/agentcore.json`. For local development, set the same variable in `agentcore/.env.local`. These are separate files: `.env.local` does not configure the deployed Runtime. Run `agentcore build`, then `agentcore deploy` in the orchestrator project. Do not grant its execution role `bedrock-agentcore:InvokeAgentRuntime` on `*`; scope the permission to the specialist Runtime ARN. ## Pattern 2: A2A protocol @@ -243,10 +215,10 @@ The specialist exposes the A2A standard — discoverable via an agent card, call ### Step 1: Build the A2A specialist -Use the `serve_a2a` helper from `bedrock-agentcore` — this matches what the CLI scaffolds via `agentcore create --protocol A2A`. +Use the `serve_a2a` helper from `bedrock-agentcore` — this matches what the CLI scaffolds via `agentcore create --template a2a-python-strands`. ```python -# app/SpecialistA2A/main.py +# app/agent/main.py from strands import Agent, tool from strands.multiagent.a2a.executor import StrandsA2AExecutor from bedrock_agentcore.runtime import serve_a2a @@ -281,15 +253,16 @@ bedrock-agentcore ### Step 2: Deploy the A2A specialist ```bash -agentcore create --name SpecialistA2A --protocol A2A -# The CLI scaffolds app/SpecialistA2A/main.py with the serve_a2a pattern shown above — customize it with your specialist logic -agentcore deploy -y +agentcore create --name SpecialistA2A --template a2a-python-strands +cd SpecialistA2A +# The CLI scaffolds app/agent/main.py with the serve_a2a pattern shown above — customize it with your specialist logic +agentcore deploy ``` After deploy, get the runtime URL: ```bash -agentcore fetch access --name SpecialistA2A --type agent +agentcore status --json ``` ### Step 3: Test locally @@ -324,12 +297,12 @@ The specialist URL is a non-secret identifier, so pass it via an env var in the ```bash # One-time: register the OAuth provider that issues tokens for the specialist. -# Omit --client-secret to get an interactive prompt (value goes straight into the credential provider). -agentcore add credential \ +# The v1 CLI accepts client secrets from file or stdin, never inline values. +agentcore add credentials oauth \ --name SpecialistA2A \ - --type oauth \ --discovery-url https:///.well-known/openid-configuration \ --client-id \ + --client-secret file:// \ --scopes a2a.invoke ``` @@ -448,12 +421,12 @@ turns = memory_client.get_last_k_turns( **Specialist not found:** - Verify `SPECIALIST_AGENT_ARN` env var is set correctly -- Check `agentcore status --runtime SpecialistAgent` shows `deployed` state +- Check `agentcore status --json` shows `deployed` state **A2A auth errors:** - A2A supports SigV4 and OAuth 2.0 — make sure you're using the right auth method -- Get the correct bearer token: `agentcore fetch access --name SpecialistA2A --type agent` +- Obtain a bearer token from the configured identity provider; `agentcore status --json` does not issue JWTs ## Output diff --git a/plugins/aws-agents/skills/agents-build/references/payments.md b/plugins/aws-agents/skills/agents-build/references/payments.md index 752721876..6b90e72f6 100644 --- a/plugins/aws-agents/skills/agents-build/references/payments.md +++ b/plugins/aws-agents/skills/agents-build/references/payments.md @@ -1,223 +1,46 @@ # payments -Add AgentCore Payments to your agent — the managed service that lets your agent pay for x402- and MPP-protected APIs, MCP tools, and web content via microtransactions (Coinbase CDP, Stripe Privy). - -AgentCore Payments is **protocol-agnostic**: it supports both **x402** (Coinbase/Cloudflare's HTTP-native stablecoin micropayment protocol) and **MPP** (the Machine Payments Protocol from Stripe and Tempo). Both are exercised through the same `ProcessPayment` API and the same manager/connector/instrument/session resources — the service detects which protocol a merchant speaks from its `402 Payment Required` response and mints the matching payment proof. You do not pick a protocol up front; you provision payments once and the agent can pay either kind of merchant. See **How x402 Payment Works** and **MPP (Machine Payments Protocol)** below for the two wire flows. - -The control-plane resources (payment manager, connector, credential provider) are provisioned with the AgentCore **CLI**. The per-user data-plane resources (instrument, session) are created with the AgentCore **SDK** (a provided script). Payments can be wired into the agent in two ways: (1) a **framework-native integration** for Strands (plugin) or LangGraph (middleware) that handles 402 detection, payment signing, and retry transparently — no custom tool code needed, or (2) a **framework-agnostic local tool** (`scripts/process_payment_tool.py`) for any other Python framework (OpenAI Agents SDK, CrewAI, etc.) or when you need full manual control. +Add AgentCore Payments to an existing agent. This reference covers the v1 CLI provisioning path and Runtime wiring. For the detailed human operated setup and IAM role split, use the bundled `agents-pay` [setup](../../agents-pay/references/setup.md) and [operator guide](../../agents-pay/references/operator-guide.md). The SDK based payment code below remains applicable after the resources are deployed. ## When to use -- You want your agent to autonomously pay for x402- or MPP-protected content (APIs, MCP tools, paywalled sites) -- A tool call returns `402 Payment Required` and you want it settled and retried automatically -- You have a payment manager and need to wire payments into your agent code -- You want budget controls on what the agent can spend -- Payment processing isn't working as expected - -Do NOT use this skill for: - -- Connecting to non-paid external tools/APIs via Gateway → use `agents-connect` -- Inbound auth (who can call your agent) → use `agents-harden` -- General agent scaffolding or project creation -- Non-payment related agent capabilities (memory, VPC, multi-agent) - -## Input - -`$ARGUMENTS` is optional: `/payments`, `/payments wire`, `/payments debug`, `/payments coinbase`, `/payments stripe`. +- Wire a Strands or LangGraph Runtime to x402 or MPP payments. +- Add an AgentCore payment manager and connector to a v1 project. +- Debug a 402 challenge after the payment resources exist. ## Process -**Execution model — minimize human stops.** Run the steps yourself, in order, without pausing between them. There are only **two** points that require the developer; pause at these and resume automatically once the developer confirms: - -- **Step 3b (connector credentials)** — for **Coinbase QuickCreate** (recommended) the developer authorizes through Coinbase in the browser — no secrets; for **Manual** (Coinbase or Stripe) the developer runs the connector command with their secrets. Present the path, then wait for them to confirm the connector is `READY`. -- **Step 7 (delegation + funding)** — the developer authorizes the wallet and funds it (browser + faucet). Surface the instructions, then wait. - -Everything else — Steps 0–3a, **4 (deploy), 5 (wire), 6 (instrument/session), 8 (set env + test)** — you run automatically. After the developer confirms 3b, ask them for the **user id** and **email** for the first wallet (Step 6 needs them), then immediately continue through 4 → 5 → 6 (and present Step 7) without asking permission for each. After they confirm 7, run Step 8. Do not stop after every step. - -### Step 0: Install / verify the AgentCore CLI - -The CLI is the **npm** package `@aws/agentcore` (Node.js 20+). It is NOT a pip package — do not `pip install` it. - -```bash -agentcore --version # need >= 0.20.0 (payment commands are preview, added in 0.20.x) -# if missing or older: -npm install -g @aws/agentcore -``` - -### Step 1: Have an AgentCore project (for CLI provisioning) - -The CLI provisions payment resources into a project (`agentcore/agentcore.json`). - -- **Project exists**: read `agentcore/agentcore.json` — check the `payments` array and the `runtimes` array (framework). -- **No project**: scaffold one (don't call `--help`; run it directly). Non-interactive: +### Step 0: Check the CLI and project - ```bash - agentcore create --project-name --name --framework Strands --defaults - ``` +Require `agentcore --version` at v1.0.0 or later. Read `agentcore/agentcore.json`; a v1 project has `"version": 2` and payment managers in `payments`. A v0 project must be committed and migrated first: review `agentcore migrate`, then apply the reviewed plan with `agentcore migrate --yes`. If there is no project and one is needed, use `agentcore create --name PaymentsProject --template empty`. A Strands Runtime project can instead start with `--template agent-python-strands`. - `--project-name` and `--name` are both required non-interactively (`--name` is the agent/resource name; without it the CLI drops to the interactive wizard). Project name: start with a letter, alphanumeric, ≤23 chars, no underscores. `--defaults` = Python + Bedrock, no memory; or run `agentcore create` for the interactive wizard. A project is only needed to provision the payment resources via the CLI — the local payment tool (Step 5) works in any agent, framework, or host. +Choose the Runtime framework by reading its code and `runtimes[]` entry. The CLI does not wire the payment plugin into the Runtime source; Step 5 is still required. -### Step 2: Determine the situation +### Step 1: Select provider and authenticate the operator -- **Case A — nothing configured**: proceed to Step 3. -- **Case B — manager/connector exist, needs wiring**: skip to Step 5. -- **Case C — wired, debugging**: ask what's failing, then use the Debugging section. -- **Case D — developer asking about payments without a project** (architecture, flow explanation): explain the end‑to‑end payment flow (see **How x402 Payment Works** for x402, and **MPP (Machine Payments Protocol)** for MPP), and ask whether they want to set up payments (→ proceed to Step 3) or need wiring help (→ Step 5). +For Coinbase, Quick Create is the smallest v1 path. The human completes any required Marketplace subscription and Coinbase authorization. For manual Coinbase or Stripe Privy, the human uses `agentcore add credentials payment` with secret values read from files or stdin. The v1 CLI rejects inline secrets and the v0 bare-command wizard no longer applies. Keep provider credentials outside the agent conversation. See the `agents-pay` setup reference for the manual provider flags and IAM role separation. -**Framework check**: If the project uses **Strands** or **LangGraph** (check `agentcore/agentcore.json` → `runtimes` array), offer the native integration path (Step 5a) which is simpler — no custom tool script needed. If the project uses another framework, or the developer wants manual control, use the generic tool path (Step 5b). - -**Provider & credential mode**: For **Coinbase**, prefer **QuickCreate** (Step 3b, recommended) — you authorize through Coinbase and the service provisions the credentials, so there are no secrets to gather or store. Choose **Manual** only if you already manage your own Coinbase keys. **Stripe (Privy) is manual-only.** - -### Step 3: Provision the payment manager and connector (CLI — control plane) - -**3a. Payment manager — no secrets, run it directly (non-interactive).** The agent can run this for the developer: +### Step 2: Add resources ```bash agentcore add payment-manager \ ---name \ ---network-preferences eip155:84532 -``` - -`eip155:84532` is Base Sepolia (testnet). Names: alphanumeric only, ≤48 chars, start with a letter. - -**Then tag the project as skill-onboarded — this step is mandatory, not optional.** The agent MUST edit `agentcore/agentcore.json` and add the `agentcore:onboarding-source` entry to the top-level `tags` object (keep the existing entries) before moving on to Step 3b. If the `tags` object does not exist yet, create it with the entries shown: - -```json -"tags": { - "agentcore:created-by": "agentcore-cli", - "agentcore:project-name": "", - "agentcore:onboarding-source": "agent-toolkit-skill" -} -``` - -Project tags are applied to the provisioned AWS resources at deploy. The `agentcore:onboarding-source` tag lets the AgentCore Payments service distinguish resources onboarded through this skill from resources provisioned with the CLI directly — set it exactly as shown. **This tag is required: never skip it, and do not proceed to `agentcore deploy` (Step 4) without it** — resources deployed without the tag are indistinguishable from direct-CLI provisioning and defeat the purpose of onboarding through this skill. - -**3b. Payment connector — choose a credential mode.** There are two ways to supply the connector's credentials: - -- **Coinbase — QuickCreate (recommended):** you authorize through Coinbase and AgentCore Payments provisions and stores the credentials for you — no keys to generate or paste. **Coinbase only.** -- **Manual (Coinbase CDP or Stripe Privy):** you generate the provider keys yourself and pass them to the connector. **This is the only path for Stripe (Privy).** - -**Coinbase prerequisite — AWS Marketplace subscription (required for BOTH QuickCreate and Manual).** Any Coinbase connector requires an active AWS Marketplace subscription to **"Coinbase Wallets for AgentCore Payments"**. With this subscription, your Coinbase wallet usage charges are consolidated into your monthly AWS bill as per Coinbase's public pricing. Until an active subscription exists in your AWS account, `CreatePaymentConnector` fails with a `SubscriptionRequiredException` (HTTP 403). - -Subscribe once per AWS account, before creating a Coinbase connector — [Coinbase Wallets for AgentCore Payments](https://aws.amazon.com/marketplace/pp/prodview-ia2zd5puqyi7g): - -1. **Prerequisite:** be logged in to your AWS Console. -2. Open the link above — it takes you to the **"Coinbase Wallets for AgentCore Payments"** product listing page. -3. Click **View purchase options**. -4. Review the listing details (terms and conditions, etc.), then click **Subscribe**. -5. You are now subscribed to Coinbase — proceed with Coinbase connector creation (QuickCreate or Manual, below). - -> A missing subscription 403s at `agentcore deploy` (when the connector is actually created). Because the CLI provisions the payment manager and connector in the **same CloudFormation stack**, that failure rolls the whole stack back and **deletes the PaymentManager too** — see **Recovery after a `SubscriptionRequiredException`** in Step 4. - -**Coinbase — QuickCreate (recommended). No secrets, so the agent can run this directly.** Prerequisite: the Coinbase AWS Marketplace subscription above. Because no credentials are entered, nothing sensitive lands in the command, shell history, or `agentcore/.env.local`, and you skip the "get your provider credentials" step below. - -```bash + --name MyPayments \ + --network-preferences eip155:84532 agentcore add payment-connector \ ---manager \ ---name \ ---provider CoinbaseCDP \ ---provision-mode QUICK_CREATE -``` - -This records a QuickCreate Coinbase connector locally with **no secrets**. When the connector is created at `agentcore deploy` (Step 4), it comes up in `PENDING_AUTHENTICATION` with an `authorizationUrl` the developer must open to authorize through Coinbase. **The AgentCore CLI does not open that URL for you today** — the agent retrieves it (via `GetPaymentConnector`) and opens it / hands it to the developer (see the handling bullets below). After the developer authorizes, the connector moves `PENDING_AUTHENTICATION` → `READY` — there is no API Key ID, API Key Secret, or Wallet Secret to obtain or store. Present the command (the agent may run it — no secrets are involved), drive the authorization as described below, and confirm the connector is `READY` before continuing. (Driving the API directly instead of the CLI: pass `provisionMode=QUICK_CREATE` with an empty `credentialProviderConfigurations` list — AWS CLI `--provision-mode QUICK_CREATE --credential-provider-configurations '[]'` — then open the returned `authorizationUrl` and poll `get-payment-connector` until `READY`.) - -**Handling the `authorizationUrl` (short-lived + single-use).** If the connector is created via the API/SDK — or the agent surfaces the URL to the developer instead of the CLI opening the browser itself — treat the `authorizationUrl` returned for the `PENDING_AUTHENTICATION` connector carefully: - -- **Valid for 10 minutes** after the connector is created, then it expires — opening a stale URL returns an "Invalid request"/expired error at Coinbase. Open it promptly. -- **Open it exactly once, as a real browser navigation — never *fetch* or *preview* it.** It carries a one-time OAuth consent session, so no `curl`/`HEAD`/`WebFetch` of the URL, and don't paste it anywhere that auto-previews or "unfurls" links (Slack, Teams, other chat tools). A preview fetch consumes the one-time session, so the developer's later click fails with "Invalid request". (Launching a browser to *navigate* to it is the intended use — that is different from fetching it.) -- **Opening it — the AgentCore CLI does NOT open it for you today, so the agent must drive this.** If you (the agent) are running on the developer's machine with a desktop browser, open it for them with the OS opener — `open ""` (macOS), `xdg-open ""` (Linux), `start "" ""` (Windows); these navigate, they do not pre-fetch. If you are headless / remote / in CI (no local GUI browser, or the developer isn't at this machine), print the URL as plain/code text and have the developer open it. Retrieve the URL from `GetPaymentConnector` (or the create response) — never fetch the URL itself. -- **Poll `GetPaymentConnector` until the status is terminal — do not reopen the URL to check.** After the developer authorizes, poll the connector's `status` until it reaches one of `READY`, `AUTHENTICATION_EXPIRED`, `AUTHENTICATION_FAILED`, or `AWS_MARKETPLACE_SUBSCRIPTION_REQUIRED` (space the calls out, e.g. every few seconds). `CREATING`, `PENDING_AUTHENTICATION` (waiting for consent), and `PROVISIONING` (consent succeeded, credentials being provisioned) are all **transient** — keep polling. -- **`READY`** — done. The credential provider is provisioned and the connector is ready to use; no further action. -- **`AUTHENTICATION_EXPIRED` / `AUTHENTICATION_FAILED`** — the OAuth consent lapsed (the 10-minute window passed) or failed. The connector cannot be recovered in place, so stop polling it and **ask the developer to replace it**: delete the expired/failed connector and recreate it by restarting QuickCreate (which mints a fresh `authorizationUrl`). -- **`AWS_MARKETPLACE_SUBSCRIPTION_REQUIRED`** — the AWS account is not subscribed to **"Coinbase Wallets for AgentCore Payments"** (see the Coinbase prerequisite above). Have the developer subscribe, then replace the connector. On the CLI deploy path the payment manager may have been rolled back too — see **Recovery after a `SubscriptionRequiredException`** in Step 4. - -```bash -# Poll status until terminal: READY / AUTHENTICATION_EXPIRED / AUTHENTICATION_FAILED / AWS_MARKETPLACE_SUBSCRIPTION_REQUIRED -# (CREATING / PENDING_AUTHENTICATION / PROVISIONING = keep polling; a still-valid authorizationUrl is returned while PENDING_AUTHENTICATION): -aws bedrock-agentcore-control get-payment-connector \ - --payment-manager-id "" \ - --payment-connector-id "" \ - --region -``` - -If the status is `AUTHENTICATION_EXPIRED` or `AUTHENTICATION_FAILED`, replace the connector — delete it, then restart QuickCreate: - -```bash -# Delete the expired/failed connector, then re-run the QuickCreate command above to mint a fresh authorizationUrl. -agentcore remove payment-connector --manager --name --yes -agentcore deploy -# then re-run: agentcore add payment-connector … --provider CoinbaseCDP --provision-mode QUICK_CREATE (and agentcore deploy) -``` - -**Manual (Coinbase CDP or Stripe Privy) — needs provider credentials. The DEVELOPER runs this, not the agent.** The agent presents the prerequisites and the command below, but must NOT execute it or handle the credentials. This single command creates the credential provider and the connector. The CLI writes the provider secrets in **plaintext to `agentcore/.env.local`** and records the credential locally; `agentcore deploy` (Step 4) then uploads them to **AgentCore Identity** (`agentcore.json` keeps only a reference). For Stripe Privy, three of these values are reused later — the delegation frontend in Step 7b reads them back out of `agentcore/.env.local`, so the developer is never asked for them twice. Note the CLI namespaces each key as `AGENTCORE_CREDENTIAL___STRIPE_PRIVY_`, using the manager and connector names chosen below; Step 7b matches on the `_STRIPE_PRIVY_` suffix for that reason. - -**Before running — get your provider credentials** (do this first; the connector command needs them). These match the exact locations in the [AgentCore Payments prerequisites](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments-prerequisites.html). - -- **Coinbase CDP** (): - 1. Create or log in to a Coinbase Developer Platform account and project. - 2. Generate an **API key** (or reuse one) at and note two values: - - **API Key ID** — the public identifier for your CDP project. - - **API Key Secret** — the private secret used to sign API requests to the CDP control plane. - 3. Under **Project > Wallets > Non-custodial Wallet > Security**, generate a **Wallet Secret** — used for cryptographic wallet operations such as deriving addresses and signing transactions. - 4. In the same place (**Project > Wallets > Non-custodial Wallet > Security**), **enable Delegated signing** (required). -- **Stripe Privy** (): - 1. Create a **dedicated** Privy app for AgentCore operations (do not reuse apps that serve other purposes). - 2. In **App settings > Basics > API Keys**, copy the **App ID** and **App Secret**. - 3. In **Wallet Infrastructure > Keys and quorums**, choose **New Key** to generate a P-256 key pair, and note two values: - - **Authorization ID (ID)** — the public key identifier from the generated pair. - - **Authorization Private Key (Private key)** — the private key from the generated pair, used for signing wallet operations. - -Recommended — interactive wizard. Run the command with **no flags** (the secrets never appear in the command, shell history, or process list; the CLI still writes them to `agentcore/.env.local` either way — see the security note below). Passing `--manager`/`--name`/`--provider` does NOT trigger the wizard — those flags switch the CLI to non-interactive mode and it then requires every secret flag too, failing with "Missing required options" otherwise: - -```bash -agentcore add payment-connector -# the wizard prompts for everything interactively — manager, connector name, provider, then the secrets: -# CoinbaseCDP : API Key ID, API Key Secret, Wallet Secret -# StripePrivy : App ID, App Secret, Authorization Private Key, Authorization ID -``` - -Non-interactive alternative (CI/scripted) — pass the secrets as flags. These land in shell history and the process list, so prefer the wizard for local setup: - -```bash -# Coinbase CDP (dummy values — replace with your own) -agentcore add payment-connector --manager --name --provider CoinbaseCDP \ ---api-key-id 11111111-2222-3333-4444-555555555555 \ ---api-key-secret cdp_sk_EXAMPLEexampleEXAMPLEexampleEXAMPLE0000 \ ---wallet-secret cdp_wallet_EXAMPLEexampleEXAMPLEexample1111 -# Stripe Privy (dummy values — replace with your own) -agentcore add payment-connector --manager --name --provider StripePrivy \ ---app-id clxxxxxxxxxxxxxxxxxxxxxxxx \ ---app-secret privy_sk_EXAMPLEexampleEXAMPLEexample2222 \ ---authorization-private-key MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwEXAMPLE... \ ---authorization-id ezzzzzzzzzzzzzzzzzzzzzzzz -``` - -> **Wizard vs flags:** The flags `--manager`, `--name`, and `--provider` are marked `[non-interactive]` — if you provide any of them, the CLI switches to **non-interactive mode** and expects **all required secrets as flags**. Running it with those three flags but omitting the secrets errors with missing-required-flags rather than dropping back to the wizard. For the interactive wizard, run the command with no flags: `agentcore add payment-connector`. Then wait for the developer to confirm it's done. - -Security: - -- **QuickCreate stores no secrets locally.** With Coinbase QuickCreate there are no provider keys to generate, paste, or store — AgentCore Payments provisions and holds the credential provider for you, so nothing lands in `agentcore/.env.local`. The bullets below apply to the **Manual** path. -- **`agentcore/.env.local` holds the provider secrets in plaintext.** The CLI writes it when the connector is added (wizard or flags) and uploads it to AgentCore Identity at `agentcore deploy`. Ensure it is gitignored — the Python scaffold's default `.gitignore` only lists `.env`, so add `.env.local` (or `.env.*`). The agent must not read `agentcore/.env.local` — where Step 7b needs values from it, the developer copies them across. -- The agent presents the command but never runs it or handles the credentials; never paste credentials into chat. - -### Step 4: Deploy (create the resources) — agent runs - -```bash -agentcore deploy -y + --manager MyPayments --name Coinbase --quick-create ``` -`agentcore deploy` provisions the project's resources to your AWS account: the payment manager/connector via the AgentCore control plane, and supporting IAM (the `PaymentProcessPaymentRole`) and any runtime via a CloudFormation stack (CDK). **Coinbase QuickCreate:** if you added the connector with `--provision-mode QUICK_CREATE`, deploy is when it is created — it comes up in `PENDING_AUTHENTICATION` with an `authorizationUrl`. **The CLI does not open that URL for you today**, so the agent must retrieve it (`GetPaymentConnector`) and open it / hand it to the developer (see Step 3b); after authorization the connector moves `PENDING_AUTHENTICATION` → `READY` (this is the developer-involved point from Step 3b). After deploy, the manager ARN, connector ID, and role ARN are written to `agentcore/.cli/deployed-state.json`. On CLI 0.20.x these live under `targets..resources.payments[]` (`managerArn`, `connectors[].connectorId`, `processPaymentRoleArn`); the Step 6 script reads this shape automatically. +For a manual connector, replace `--quick-create` with `--credential ` after adding that credential. Manager names are alphanumeric, start with a letter, and have at most 48 characters; connector names can also contain underscores. Add any intended onboarding tag to the project's top-level `tags` before deploying. -**Recovery after a `SubscriptionRequiredException` (Coinbase, CLI deploy).** If `agentcore deploy` fails while creating a Coinbase connector with `SubscriptionRequiredException` (HTTP 403), the account is not subscribed to **"Coinbase Wallets for AgentCore Payments"** — pause and have the developer subscribe (see the Coinbase prerequisite in Step 3b). **Important:** because the CLI provisions the payment manager and connector in the **same CloudFormation stack**, this deploy failure rolls the stack back and **deletes the PaymentManager that was being created alongside the connector**. So when the developer returns and confirms the subscription is active, do NOT just retry the connector — first recheck whether the manager still exists: +### Step 3: Build and deploy ```bash -aws bedrock-agentcore-control get-payment-manager \ - --payment-manager-id "" \ - --region +agentcore build +agentcore deploy --region +agentcore status --json ``` -- **Manager returned (still exists):** resume — create/deploy the connector (Step 3b → Step 4). -- **Not found (`ResourceNotFoundException`):** the rollback removed it — **restart the flow from Step 3a**: recreate the payment manager (and re-apply the mandatory `agentcore:onboarding-source` tag), then create the connector (Step 3b), then `agentcore deploy` again. +The first deploy creates the default target with the effective AWS account and region. Check the account and region. Coinbase Quick Create reports an authorization URL; the human completes that flow and waits for the connector to become ready. v1 payment identifiers are surfaced by `agentcore status --json`, not the `payments[]` branch of `agentcore/.cli/deployed-state.json`. If an admin script cannot discover them, supply its `--manager-arn` and `--connector-id` flags. ### Step 5: Wire the agent @@ -332,7 +155,7 @@ The agent calls `x402_fetch` instead of a generic HTTP tool; payment is handled ### Step 6: Provision the per-user instrument and session (SDK script — data plane) — agent runs -The instrument (per-user wallet) and session (budget-bounded spend window) are data-plane resources — there is no CLI command for them. First ask the developer for the **user id** and **email** to provision the wallet for (if not already collected after Step 3b). Then run the provided script [`scripts/setup_payment_user.py`](../scripts/setup_payment_user.py) once per user. It auto-reads the manager ARN/connector ID from `deployed-state.json` (or accepts `--manager-arn`/`--connector-id`): +The instrument (per-user wallet) and session (budget-bounded spend window) are data-plane resources — the v1 CLI can inspect them with `agentcore payment instrument` and `agentcore payment session`, while creation remains an SDK workflow. First ask the developer for the **user id** and **email** to provision the wallet for (if not already collected after Step 3b). Then run the provided script [`scripts/setup_payment_user.py`](../scripts/setup_payment_user.py) once per user. It reads the manager ARN/connector ID from `agentcore status --json` (or accepts `--manager-arn`/`--connector-id`): ```bash python scripts/setup_payment_user.py --user-id alice --email alice@example.com --budget 5 @@ -841,7 +664,7 @@ MPP is method-agnostic, but the one crypto network AgentCore adds *beyond* the x | Tempo Mainnet | `eip155:4217` | Mainnet | | Tempo Moderato | `eip155:42431` | Testnet | -- **Provider — StripePrivy only.** Tempo is not supported on Coinbase-managed instruments (`ValidationException("Tempo payments are not supported for Coinbase-managed payment instruments.")`). To pay Tempo/MPP endpoints, provision the connector with `--provider StripePrivy` (Step 3b) and create the instrument on it. +- **Provider — StripePrivy only.** Tempo is not supported on Coinbase-managed instruments (`ValidationException("Tempo payments are not supported for Coinbase-managed payment instruments.")`). To pay Tempo/MPP endpoints, add a Stripe Privy payment credential and use `--credential ` on the connector and create the instrument on it. - **No up-front network choice.** As with x402, you do not "target" Tempo when provisioning — AgentCore maps the MPP challenge to Tempo automatically (a `method="evm"` challenge carrying `chainId=42431` maps to Moderato). Provision once with a StripePrivy connector; the agent then pays x402 *and* Tempo/MPP merchants through the same manager/instrument/session. - **Asset — the network's canonical stablecoin.** AgentCore validates the charge against the network's canonical USD stablecoin (`MPP_EVM_NON_CANONICAL_USDC` otherwise). On **Moderato** that canonical token is **pathUSD**; fund the wallet with pathUSD from the Tempo faucet () — see Step 7. - **Access gate.** MPP (including Tempo) is behind a fail-closed account allowlist — a first call may return `AccessDeniedException(MPP_ACCESS_NOT_ALLOWLISTED)`. Request access via AWS Support (see Debugging). @@ -874,7 +697,7 @@ For testing, start with **Base Sepolia** (network: `ETHEREUM`, chain: `BASE_SEPO ## Quality criteria - CLI is installed via `npm install -g @aws/agentcore`, not pip -- Control plane (credential provider, manager, connector) is provisioned via the CLI; the manager non-interactively. For a Coinbase connector, **QuickCreate (`--provision-mode QUICK_CREATE`, no secrets) is offered first**; manual secret entry is the alternative and the only path for Stripe (Privy). Only the connector step involves the developer — QuickCreate: browser authorization; Manual: entering secrets +- Control plane (credential provider, manager, connector) is provisioned via the CLI; the manager non-interactively. For a Coinbase connector, **Quick Create (`--quick-create`, no secrets) is offered first**; manual secret entry is the alternative and the only path for Stripe (Privy). Only the connector step involves the developer — QuickCreate: browser authorization; Manual: entering secrets - The `agentcore:onboarding-source: agent-toolkit-skill` tag is added to `agentcore/agentcore.json` (Step 3a) before deploy — this is mandatory, so the provisioned resources are attributable to this skill - Data plane (instrument, session) is created via the SDK script, not hand-written code - If the project is Strands or LangGraph, the native integration (Step 5a) is offered first as the simpler path diff --git a/plugins/aws-agents/skills/agents-build/references/request-headers.md b/plugins/aws-agents/skills/agents-build/references/request-headers.md index ed6e18047..d035be46e 100644 --- a/plugins/aws-agents/skills/agents-build/references/request-headers.md +++ b/plugins/aws-agents/skills/agents-build/references/request-headers.md @@ -57,9 +57,9 @@ If the header names are fixed by a protocol or external system (A2A requires `A2 } ``` -Then `agentcore deploy`. The `$schema` URL at the top of the file (`https://schema.agentcore.aws.dev/v1/agentcore.json`) gives IDE autocomplete and validation for every field. +Then `agentcore deploy`. Run `agentcore build` to validate the edited v1 project before deployment. -**CLI shortcut** — `agentcore add agent --request-header-allowlist "X-Tenant-Id,A2A-Version"` writes the same array. **Important:** the CLI auto-prefixes entries with `X-Amzn-Bedrock-AgentCore-Runtime-Custom-` as they land in `agentcore.json`. If you're editing the JSON by hand, write the prefixed form directly. If you're using the CLI, pass the short name and let the CLI add the prefix. +**When creating a new Runtime**, `agentcore add runtime --name --language python --request-header-allowlist X-Tenant-Id A2A-Version` writes the allowlist. Edit the existing Runtime entry instead of adding a duplicate. **Important:** the CLI auto-prefixes entries with `X-Amzn-Bedrock-AgentCore-Runtime-Custom-` as they land in `agentcore.json`. If you're editing the JSON by hand, write the prefixed form directly. If you're using the CLI, pass the short name and let the CLI add the prefix. `Authorization` passes through by default and doesn't need to be in the allowlist. diff --git a/plugins/aws-agents/skills/agents-build/references/teardown.md b/plugins/aws-agents/skills/agents-build/references/teardown.md index f350e1900..f87c2d68e 100644 --- a/plugins/aws-agents/skills/agents-build/references/teardown.md +++ b/plugins/aws-agents/skills/agents-build/references/teardown.md @@ -1,166 +1,35 @@ -# teardown +# Remove a project resource -Remove individual resources from your project or tear down the entire deployment. +Use this reference for removing one resource from an AgentCore CLI v1 project. For tearing down an entire deployed target, use the `agents-deploy` teardown workflow, which previews and scopes deletion to a named target. -## When to use +## Before removing -- You want to remove a gateway, memory, credential, evaluator, or other resource from your project -- You want to delete a deployed agent and clean up all AWS resources -- You're iterating in a sandbox account and want to start fresh -- You need to remove a resource that's stuck or no longer needed +Read `agentcore/agentcore.json` and `agentcore/aws-targets.json`. Require CLI v1.0.0 or later and project config `"version": 2`. For a v0 project, commit, review `agentcore migrate`, and apply the reviewed plan with `agentcore migrate --yes` first. Identify the resource name, its parent if any, and each target where it is deployed. Confirm the intended deletion with the user when it will remove durable data such as Memory. -## Process +`agentcore remove` changes the local project declaration. `agentcore deploy --target ` applies the change in AWS for that target. `agentcore status --target --json` reports deployed project resources; it has no `--state` or `--type` filter in v1. -### Removing individual resources from your project - -Use `agentcore remove` to remove a resource from `agentcore.json`. This marks the resource for deletion — the actual AWS resource is removed on the next `agentcore deploy`. +## Remove a resource ```bash -# Remove a memory resource agentcore remove memory --name MyMemory - -# Remove a gateway target -agentcore remove gateway-target --name WeatherTools --gateway MyGateway - -# Remove a gateway (remove all its targets first) +agentcore remove gateway-target --gateway MyGateway --name WeatherTools agentcore remove gateway --name MyGateway - -# Remove a credential agentcore remove credential --name MyAPIKey - -# Remove an evaluator -agentcore remove evaluator --name ResponseQuality - -# Remove an online eval config -agentcore remove online-eval --name production_monitor - -# Remove a policy -agentcore remove policy --name SpendingLimit --engine MyPolicyEngine - -# Remove a policy engine (remove all its policies first) +agentcore remove runtime --name MyRuntime +agentcore remove harness --name MyHarness +agentcore remove policy --engine MyPolicyEngine --name SpendingLimit agentcore remove policy-engine --name MyPolicyEngine +agentcore remove payment-connector --manager MyPayments --name Coinbase +agentcore remove payment-manager --name MyPayments ``` -After removing, deploy to apply the changes: - -```bash -agentcore deploy -y -``` - -Check what's pending removal before deploying: - -```bash -agentcore status --state pending-removal -``` - -### Removing an agent from a multi-agent project - -If your project has multiple agents (runtimes), you can remove one: - -```bash -agentcore remove agent --name SecondAgent -agentcore deploy -y -``` - -This deletes the agent's runtime, endpoint, and associated resources from AWS. The agent's code in `app//` is not deleted — remove it manually if you no longer need it. - -### Tearing down the entire deployment - -To remove all deployed AWS resources for a project: - -```bash -# Preview what will be destroyed -agentcore deploy --diff - -# Destroy all resources -npx cdk destroy --app "npx ts-node agentcore/cdk/bin/cdk.ts" --force -``` - -Alternatively, delete the CloudFormation stack directly: +These are examples; use only the command for the resource the user selected. Remove child targets before their Gateway, policies before their engine, and connectors before their manager if project validation requires it. For an API key or OAuth provider, the resource type passed to `agentcore remove` is `credential`. Check `agentcore remove --help` for the resource types supported by the installed v1 CLI. -```bash -# Find the stack name -aws cloudformation list-stacks \ - --stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE \ - --query "StackSummaries[?contains(StackName, '')].StackName" - -# Delete it -aws cloudformation delete-stack --stack-name - -# Wait for deletion to complete -aws cloudformation wait stack-delete-complete --stack-name -``` - -### What gets deleted and what doesn't - -| Resource | Deleted by `cdk destroy` | Notes | -|---|---|---| -| AgentCore Runtime(s) | ✅ | Includes all endpoints and versions | -| Memory resource(s) | ✅ | Memory data is deleted permanently | -| Gateway(s) and targets | ✅ | | -| Credentials | ✅ | Secrets Manager entries are removed | -| Policy engine(s) and policies | ✅ | | -| Evaluator definitions | ✅ | | -| Online eval configs | ✅ | | -| IAM roles | ✅ | Created by CDK | -| CloudWatch log groups | ❌ | Persist after deletion — delete manually if needed | -| ECR images (Container builds) | ❌ | Persist — delete the repository manually | -| CDK bootstrap stack | ❌ | Shared across projects — don't delete unless you're done with CDK entirely | -| Local project files | ❌ | `agentcore/`, `app/` — delete manually | - -### Cleaning up CloudWatch log groups - -Log groups persist after stack deletion. To clean them up: - -```bash -# List AgentCore log groups -aws logs describe-log-groups \ - --log-group-name-prefix /aws/bedrock-agentcore/ \ - --query "logGroups[].logGroupName" - -# Delete a specific log group -aws logs delete-log-group --log-group-name /aws/bedrock-agentcore/runtimes/-DEFAULT -``` - -### Cleaning up ECR repositories (Container builds) - -```bash -# List AgentCore ECR repositories -aws ecr describe-repositories \ - --query "repositories[?contains(repositoryName, 'bedrock-agentcore')].repositoryName" - -# Delete a repository and all its images -aws ecr delete-repository --repository-name --force -``` - -### Handling stuck resources - -If a runtime is stuck in DELETING state for more than 30 minutes, see the "Runtime stuck in DELETING" section in `agents-debug`. The short version: don't keep retrying — open an AWS Support case with the runtime ARN and the original delete request ID from CloudTrail. - -## Common issues - -**"Can't remove gateway — targets still attached"** -Remove all gateway targets first, then remove the gateway: +Before deploying, inspect the changed `agentcore/agentcore.json` and preview the target stack with `agentcore build` followed by `cd agentcore/cdk && npx cdk diff` using the project's target context. See the `agents-deploy` diff procedure for the exact target setup. Then deploy each intended target explicitly: ```bash -agentcore remove gateway-target --name Target1 --gateway MyGateway -agentcore remove gateway-target --name Target2 --gateway MyGateway -agentcore remove gateway --name MyGateway +agentcore deploy --target +agentcore status --target --json ``` -**"Can't remove policy engine — policies still attached"** -Remove all policies first, then remove the engine: - -```bash -agentcore remove policy --name Policy1 --engine MyEngine -agentcore remove policy-engine --name MyEngine -``` - -**"Resource shows pending-removal but deploy doesn't delete it"** -Check `agentcore status --state pending-removal` and verify the resource is listed. If deploy completes without removing it, check the CDK output for errors — the deletion may have failed silently due to a dependency. - -## Output - -- CLI commands to remove the specific resource(s) -- Guidance on what persists after deletion and how to clean it up -- Warnings about irreversible data loss (memory data, credentials) +`--yes` on `agentcore deploy` is only the confirmation for deleting a target stack when the project declares nothing to deploy. It is not a general noninteractive deploy flag. Do not delete a CloudFormation stack by searching for a project name; a project can have several targets and other resources can survive stack deletion. diff --git a/plugins/aws-agents/skills/agents-build/references/vpc.md b/plugins/aws-agents/skills/agents-build/references/vpc.md index 59eb8d3cf..4304bb969 100644 --- a/plugins/aws-agents/skills/agents-build/references/vpc.md +++ b/plugins/aws-agents/skills/agents-build/references/vpc.md @@ -33,7 +33,7 @@ When you configure VPC mode, AgentCore creates **Elastic Network Interfaces (ENI ## Step 0: Verify CLI version -Run `agentcore --version`. This skill requires v0.9.0 or later. If the version is older, tell the developer to run `agentcore update` before proceeding. +Run `agentcore --version`. This skill requires v1.0.0 or later. If the version is older, tell the developer to run `agentcore update` before proceeding. --- @@ -118,22 +118,22 @@ Type: HTTP, Port: 80, Destination: API security group or CIDR (if needed) ### New project ```bash -agentcore create \ - --name MyAgent \ - --defaults \ - --network-mode VPC \ - --subnets subnet-abc123,subnet-def456 \ - --security-groups sg-agent123 +agentcore create --name MyProject --template agent-python-strands +cd MyProject ``` +The template already creates the `agent` Runtime. Set `networkMode` and `networkConfig` on that entry as shown below before deploying. + ### Existing project +For an existing Runtime, edit its entry in `agentcore/agentcore.json` as shown below. `agentcore add runtime` creates another Runtime; it does not update the existing one. If the project has no Runtime yet, create it: + ```bash -agentcore add agent \ - --name MyAgent \ +agentcore add runtime \ + --name PrivateAgent \ + --language python \ --network-mode VPC \ - --subnets subnet-abc123,subnet-def456 \ - --security-groups sg-agent123 + --network-config '{"subnets":["subnet-abc123","subnet-def456"],"securityGroups":["sg-agent123"]}' ``` Or edit `agentcore/agentcore.json` directly — add the `networkMode` and `networkConfig` fields to the runtime's entry: @@ -153,12 +153,12 @@ Or edit `agentcore/agentcore.json` directly — add the `networkMode` and `netwo } ``` -The `$schema` URL at the top of `agentcore.json` (`https://schema.agentcore.aws.dev/v1/agentcore.json`) gives IDE autocomplete and validation for every field — including the subnet/security-group ID patterns. +For a container Runtime, also set `networkConfig.vpcId` so the build can use the VPC. Run `agentcore build` to validate the project before deployment. ### Deploy ```bash -agentcore deploy -y +agentcore deploy ``` --- diff --git a/plugins/aws-agents/skills/agents-build/scripts/process_payment_tool.py b/plugins/aws-agents/skills/agents-build/scripts/process_payment_tool.py index aef6e8601..325b5f909 100644 --- a/plugins/aws-agents/skills/agents-build/scripts/process_payment_tool.py +++ b/plugins/aws-agents/skills/agents-build/scripts/process_payment_tool.py @@ -24,7 +24,7 @@ CLI; the per-user instrument/session are created by setup_payment_user.py. This tool only consumes them, via these environment variables: - PAYMENT_MANAGER_ARN payment manager ARN (from deployed-state.json) + PAYMENT_MANAGER_ARN payment manager ARN (from agentcore status --json) PAYMENT_INSTRUMENT_ID per-user wallet ID (from setup_payment_user.py) PAYMENT_SESSION_ID per-conversation session (from setup_payment_user.py) PAYMENT_USER_ID end-user identity (required) diff --git a/plugins/aws-agents/skills/agents-build/scripts/setup_payment_user.py b/plugins/aws-agents/skills/agents-build/scripts/setup_payment_user.py index 9a0114016..308ef70d4 100644 --- a/plugins/aws-agents/skills/agents-build/scripts/setup_payment_user.py +++ b/plugins/aws-agents/skills/agents-build/scripts/setup_payment_user.py @@ -10,23 +10,37 @@ python setup_payment_user.py --user-id alice --email alice@example.com [--budget 5] \ [--manager-arn ...] [--connector-id ...] [--region us-east-1] [--network ETHEREUM] -Manager ARN / connector ID are auto-read from agentcore/.cli/deployed-state.json if not passed. +Manager ARN / connector ID are read from `agentcore status --json` for v1 projects. """ import argparse import json import os +import subprocess import sys from pathlib import Path -from bedrock_agentcore.payments import PaymentManager - - def _from_deployed_state(): - """Best-effort: read manager ARN + connector ID from the CLI's deployed state. - - CLI 0.20.x writes targets..resources.payments[]; older shapes used a - top-level payments[]. Handle both. - """ + """Best-effort payment discovery from v1 status, with a v0 state fallback.""" + if Path("agentcore/agentcore.json").exists(): + try: + result = subprocess.run( + ["agentcore", "status", "--json"], + capture_output=True, + text=True, + timeout=30, + check=False, + ) + if result.returncode == 0: + for resource in json.loads(result.stdout).get("resources", []): + if resource.get("resourceType") != "payment-manager" or not resource.get("arn"): + continue + for child in resource.get("children", []): + if child.get("resourceType") == "payment-connector" and child.get("id"): + return resource["arn"], child["id"] + except (OSError, ValueError, subprocess.TimeoutExpired): + pass + + # Legacy v0 projects recorded payment outputs in deployed state. path = Path("agentcore/.cli/deployed-state.json") if not path.exists(): return None, None @@ -41,9 +55,12 @@ def _from_deployed_state(): payments = data.get("payments") # legacy/top-level fallback if not payments: return None, None - pay = payments[0] + pay = next(iter(payments.values()), {}) if isinstance(payments, dict) else payments[0] connectors = pay.get("connectors") or [] - return pay.get("managerArn"), (connectors[0].get("connectorId") if connectors else None) + connector = next(iter(connectors.values()), {}) if isinstance(connectors, dict) else ( + connectors[0] if connectors else {} + ) + return pay.get("managerArn"), connector.get("connectorId") except Exception: return None, None @@ -57,7 +74,7 @@ def main(): ap.add_argument("--network", default="ETHEREUM", help="Wallet network family: ETHEREUM or SOLANA") ap.add_argument("--manager-arn", default=os.environ.get("PAYMENT_MANAGER_ARN")) ap.add_argument("--connector-id", default=os.environ.get("PAYMENT_CONNECTOR_ID")) - ap.add_argument("--region", default=os.environ.get("AWS_REGION", "us-east-1")) + ap.add_argument("--region", default=os.environ.get("AWS_REGION")) args = ap.parse_args() manager_arn, connector_id = args.manager_arn, args.connector_id @@ -67,9 +84,12 @@ def main(): connector_id = connector_id or ds_conn if not manager_arn or not connector_id: sys.exit("Could not resolve manager ARN / connector ID. Pass --manager-arn and --connector-id, " - "or run from the project dir with agentcore/.cli/deployed-state.json present.") + "or run from a deployed v1 project directory and check `agentcore status --json`.") + + from bedrock_agentcore.payments import PaymentManager - manager = PaymentManager(payment_manager_arn=manager_arn, region_name=args.region) + region = args.region or manager_arn.split(":")[3] + manager = PaymentManager(payment_manager_arn=manager_arn, region_name=region) # Data plane: per-user instrument (wallet). Email -> linkedAccounts. instrument = manager.create_payment_instrument( @@ -100,14 +120,14 @@ def main(): print("Instrument ID :", instrument_id) print("Wallet address:", wallet_address) - print("Session ID :", session_id or "(none - use `agentcore invoke --auto-session`)") + print("Session ID :", session_id or "(none - create an approved session before spending)") print("\nExport these for the x402 tool (Step 8):") print(f" export PAYMENT_MANAGER_ARN={manager_arn}") print(f" export PAYMENT_INSTRUMENT_ID={instrument_id}") if session_id: print(f" export PAYMENT_SESSION_ID={session_id}") print(f" export PAYMENT_USER_ID={args.user_id}") - print(f" export AWS_REGION={args.region}") + print(f" export AWS_REGION={region}") print("\nOne-time per wallet:") if redirect_url: print(f" 1. Delegation (Coinbase): visit {redirect_url}, log in, grant access to {wallet_address}") diff --git a/plugins/aws-agents/skills/agents-build/scripts/test_setup_payment_user.py b/plugins/aws-agents/skills/agents-build/scripts/test_setup_payment_user.py new file mode 100644 index 000000000..2bd2e6d5f --- /dev/null +++ b/plugins/aws-agents/skills/agents-build/scripts/test_setup_payment_user.py @@ -0,0 +1,39 @@ +"""Verify payment resource discovery from a v1 project status.""" +import importlib.util +import json +import os +import tempfile +from pathlib import Path +from unittest.mock import patch + +script = Path(__file__).with_name("setup_payment_user.py") +spec = importlib.util.spec_from_file_location("setup_payment_user", script) +module = importlib.util.module_from_spec(spec) +spec.loader.exec_module(module) + +status = { + "resources": [{ + "resourceType": "payment-manager", + "arn": "arn:aws:bedrock-agentcore:us-east-1:111111111111:payment-manager/test", + "children": [{"resourceType": "payment-connector", "id": "connector-test"}], + }], +} + +with tempfile.TemporaryDirectory() as directory: + root = Path(directory) + (root / "agentcore").mkdir() + (root / "agentcore" / "agentcore.json").write_text('{"version":2}') + original_dir = Path.cwd() + try: + os.chdir(root) + with patch.object(module.subprocess, "run") as run: + run.return_value.returncode = 0 + run.return_value.stdout = json.dumps(status) + assert module._from_deployed_state() == ( + status["resources"][0]["arn"], "connector-test" + ) + run.assert_called_once() + finally: + os.chdir(original_dir) + +print("setup_payment_user v1 discovery: PASSED") diff --git a/plugins/aws-agents/skills/agents-connect/SKILL.md b/plugins/aws-agents/skills/agents-connect/SKILL.md index 0790cd874..480b665a5 100644 --- a/plugins/aws-agents/skills/agents-connect/SKILL.md +++ b/plugins/aws-agents/skills/agents-connect/SKILL.md @@ -1,556 +1,145 @@ --- name: agents-connect description: > - Use when connecting your agent to external APIs, tools, or services via - Gateway, or restricting tool access with Cedar policies. Handles gateway - setup, target types, outbound auth (OAuth, API key, IAM), credentials, - and Cedar policy authoring. Triggers on: "connect to API", "add gateway", - "connect to MCP server", "Lambda tools", "OpenAPI", "gateway target", - "Cedar policy", "restrict tools", "policy engine", "gateway auth error", - "store API key", "outbound credential", "env var API key", "API key None - after deploy", "credential not available after deploy", - "should this be a gateway target", "give my agent tools", - "add tools to agent". - Not for inbound auth (who can call your agent) — use agents-harden. - Not for debugging agent behavior — use agents-debug. - Not for VPC networking errors (agent can't reach APIs due to VPC) — use - agents-build. Not for creating or hosting a new MCP server project — use - agents-get-started. + Use when connecting an AgentCore Runtime or Harness to external APIs, MCP + servers, Lambda tools, or OpenAPI services through Gateway, or controlling + Gateway tools with Cedar policies. Covers Gateway targets, API keys, OAuth, + outbound credentials, and policy engines. For inbound Runtime auth use + agents-harden; for private VPC networking use agents-build. allowed-tools: Read Grep Glob Bash metadata: type: skill version: "1.0.0" author: aws-agentcore - requires-cli: ">=0.9.0" + requires-cli: ">=1.0.0" --- # connect -Give your AgentCore agent access to external APIs, tools, and services via the AgentCore Gateway — and control what it can access with Cedar policies. +Connect an agent to external tools through AgentCore Gateway and choose outbound authentication for each target. ## When to use -- You want your agent to call an external API or MCP server -- You want to expose Lambda functions as agent tools -- You have an OpenAPI spec you want to turn into agent tools -- Your agent needs credentials to call an external service -- You want to restrict which tools your agent can call (Cedar policies) -- You want role-based or amount-based access control on tool calls -- A gateway connection, tool call, or policy authorization is failing +- Connect an external MCP server, Lambda function, OpenAPI service, or another project Runtime. +- Add an API key or OAuth credential provider. +- Restrict tool calls using Cedar policies; load [references/policy.md](references/policy.md). -For adding Cedar policies to control tool access, load [`references/policy.md`](references/policy.md). - -## Input - -`$ARGUMENTS` is optional: - -``` -/connect # interactive — asks what you're connecting to -/connect mcp # MCP server setup -/connect lambda # Lambda function as tools -/connect openapi # OpenAPI schema as tools -/connect credential # Add a credential for outbound auth -``` +For an app calling your agent, use [agents-build integration](../agents-build/references/integrate.md). For a new MCP server project, use `agents-get-started`. ## Process -### Step 0: Verify CLI version - -Run `agentcore --version`. This skill requires v0.9.0 or later. If the version is older, tell the developer to run `agentcore update` before proceeding. - -### Step 1: Read the project - -Read `agentcore/agentcore.json` to understand: - -- What framework the project uses -- What gateways and targets are already configured (in the `agentCoreGateways` array) - -**If no project context:** Ask what they're trying to connect to and proceed with the appropriate pattern. - -### Step 2: Identify what they're connecting to - -Ask (or infer from `$ARGUMENTS`): - -> "What are you connecting your agent to? -> -> 1. An external MCP server (e.g., a third-party tool provider) -> 2. A Lambda function you've written -> 3. An API with an OpenAPI spec -> 4. An AWS API Gateway REST API -> 5. An external service with no OpenAPI spec, MCP server, or Lambda in front of it — and you can't add one" - -**Options 1–4 front the service as a Gateway target.** This is the default path: the gateway handles outbound auth via its credential providers (so the agent code never sees the secret), the tool becomes discoverable over MCP, and policy engines can authorize or deny calls at the edge. Pick the target type that matches the service. - -**Option 5 is Path D** — register a credential and call the API directly from agent code. This is the fallback when fronting isn't practical; the skill walks through when it's appropriate and when it isn't. - ---- - -## Default: prefer a Gateway target over direct API calls in code - -Before jumping into paths, set expectations. Most "my agent needs to call X" requests land on a Gateway target — not on `httpx` inside the entrypoint. - -**Why Gateway is the default:** - -- **Credential injection at the edge.** Gateway's credential providers (OAuth, API key, IAM) attach auth to the outbound request. The agent code calls `session.call_tool(...)` — it never touches the secret. Agent code that does `client = openai.OpenAI(api_key=...)` is one leaked prompt / log line / traceback away from exfiltrating the key. -- **Discoverable tool catalog.** Tools are listed by the MCP server; the framework (Strands, LangGraph, etc.) binds them automatically. Adding a tool is an `agentcore add gateway-target` + redeploy, not a code change. -- **Policy enforcement.** Cedar policies can authorize or deny tool calls per principal, per tool, per argument value. This is impossible when tool calls are buried in `httpx.post(...)` inside agent code. -- **Semantic search.** Once the catalog has 20+ tools, `x_amz_bedrock_agentcore_search` selects the relevant ones per turn. - -**When a direct API call in agent code is the right answer:** - -| Situation | Why Gateway isn't right | What to do | -|---|---|---| -| Streaming/bidirectional protocol (SSE with live output, WebSockets, WebRTC, long-polling) | Gateway's MCP transport doesn't front those yet | Direct call, Path D | -| Latency hot path where the MCP hop is measurable and the trade-off is accepted | Extra network hop | Direct call, Path D, with measurement to back the decision | -| Vendor proprietary protocol / binary SDK | No HTTP surface for Gateway to front | Use the vendor SDK directly, Path D for any secrets | -| Calling another agent via A2A | A2A is HTTP-by-design and has its own auth model | [`agents-build/references/multi-agent.md`](../agents-build/references/multi-agent.md), not a Gateway target | -| AWS service SDK (S3, DynamoDB, SQS, etc.) the runtime already has IAM for | No auth value in fronting — adds hops | Direct boto3 call with the runtime's execution role | - -For **every other case**, recommend a Gateway target. If the developer insists on a direct call, ask which of the five situations above applies. If none, steer them back to a Gateway target. - -**Triage heuristic:** - -- Service has an MCP server → Path A -- Service is a Lambda function you control → Path B -- Service has an OpenAPI spec (or you can generate one — FastAPI, ASP.NET, Spring, etc. generate OpenAPI automatically) → Path C -- Service is already fronted by API Gateway → Path C (`--type api-gateway`) -- None of the above and you can't add one → Path D - ---- - -## What Gateway is — and what it isn't - -Before choosing a target type, get the mental model right. Most Gateway confusion comes from having it flipped. +### 1. Check CLI and project -**Gateway hosts tools for your agent to call.** The direction is: +Run `agentcore --version`; require v1.0.0 or later. If the CLI is older, upgrade with `npm install -g @aws/agentcore@latest` and recheck. Read `agentcore/agentcore.json`. Version 2 is the v1 project format. If it is version 1, commit the project, review `agentcore migrate`, then apply the reviewed plan with `agentcore migrate --yes`. -``` -Your agent ───→ Gateway ───→ Lambda function / OpenAPI API / MCP server / Smithy model - (agent calls tool) -``` - -The agent is the client. The Gateway fronts a catalog of tools. Each tool is a Gateway target (Lambda, OpenAPI, MCP server, API Gateway, Smithy). - -**Gateway is not an inbound reverse proxy for your agent.** If you're building an app that needs to invoke your agent, the app does not go through a Gateway. The direction is: - -``` -Your app ───→ AgentCore Runtime (direct invoke_agent_runtime call) -``` - -The app signs the invocation with IAM SigV4 or presents a JWT. See [`agents-build/references/integrate.md`](../agents-build/references/integrate.md) for the app-side patterns. - -### When you're confused about which direction you need - -Ask: **who is calling whom?** - -- "My agent needs to look up weather data" → agent is calling a tool → **Gateway target** (this skill, Paths A/B/C) -- "My FastAPI app needs to call my agent" → app is calling the agent → **direct invocation** (not Gateway; use [`agents-build/references/integrate.md`](../agents-build/references/integrate.md)) -- "My agent needs to fetch data from my FastAPI app" → agent is calling the app as a tool → **Gateway target** with the app exposed as an OpenAPI or REST target (Path C with your FastAPI's `/openapi.json`) - -If you catch yourself configuring a Gateway target whose endpoint is `bedrock-agentcore..amazonaws.com` or pointing at your own runtime's URL, stop — you have the flow inverted. - -### What target type fits your tool - -| What the tool is | Target type | Notes | -|---|---|---| -| MCP server (third-party or your own) | `mcp-server` | Most common for MCP tool catalogs | -| AWS Lambda function you wrote | `lambda-function-arn` | Uses IAM auth automatically | -| HTTP API with an OpenAPI spec | `open-api-schema` | FastAPI's built-in `/openapi.json` works | -| AWS API Gateway REST API | `api-gateway` | For APIs already fronted by API Gateway | -| AWS service with a Smithy model | `smithy-model` | Direct AWS service integration | - -Your tool doesn't naturally have an OpenAPI spec and isn't an MCP server or Lambda? Either wrap it in a Lambda (simplest), generate an OpenAPI spec for it (FastAPI does this automatically), or front it with API Gateway. +Locate the project Gateway under `agentCoreGateways` and the Runtime or Harness that will call it. If there is no project, first create one using `agentcore create --name --template empty`, or work in the user's existing project. Do not create a second project without preserving their current work. ---- - -### Step 3: Navigate the auth matrix - -**This is the most common source of errors.** The auth options depend on the target type, and the CLI exposes only a subset of what the API/SDK support. - -| What you're connecting to | CLI `--type` | Outbound auth via CLI | Additional options via API/SDK | -|---|---|---|---| -| External MCP server | `mcp-server` | `none`, `oauth` (2LO only) | OAuth 3LO (`AUTHORIZATION_CODE`); IAM (SigV4) | -| Lambda function | `lambda-function-arn` | `none` (default — direct invoke via gateway role), `oauth` (2LO) for OAuth-protected downstreams | OAuth 3LO | -| OpenAPI spec | `open-api-schema` | `oauth` (2LO), `api-key` (required — no `none`) | OAuth 3LO | -| AWS API Gateway | `api-gateway` | `none`, `api-key` | IAM (`GATEWAY_IAM_ROLE`) | -| Smithy model | `smithy-model` | `oauth` (2LO) | IAM; OAuth 3LO | - -**Two OAuth grant types, not one.** The CLI's `--outbound-auth oauth` only configures **2-legged OAuth** (client credentials / M2M). If the service requires **3-legged OAuth** (`AUTHORIZATION_CODE` grant, user-delegated access), there is no CLI flag — you must configure the target via boto3 / the AWS SDK. See the [CreateGatewayTarget docs](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-building-adding-targets-authorization.html) for the `OAuthCredentialProvider` with `grantType: AUTHORIZATION_CODE` and `defaultReturnUrl`. 3LO applies to MCP, Lambda, OpenAPI, and Smithy targets. Call this out up front — developers who need 3LO will otherwise burn a round-trip trying CLI flags that don't exist. - -**IAM (SigV4) for MCP servers** is configured via the AWS SDK/API (`CreateGatewayTarget` with `GATEWAY_IAM_ROLE` credential provider + `iamCredentialProvider.service`), not the CLI. It requires the MCP server to be hosted behind an AWS service that natively verifies SigV4: AgentCore Runtime, AgentCore Gateway, Amazon API Gateway, or Lambda Function URLs. ALB or direct EC2 endpoints do not verify SigV4 — use OAuth there instead. ([MCP server target auth strategies](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-target-MCPservers.html#gateway-target-MCPservers-considerations)) - -**API key auth for MCP server targets is not supported at the API level** — not just a CLI gap. The [MCP server targets docs](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-target-MCPservers.html#gateway-target-MCPservers-considerations) list only "No authorization, OAuth, and IAM" as supported authorization strategies for MCP targets. If the MCP server uses an API key (a common pattern for third-party MCP providers), handle it in agent code via Path D. - -**Auth options change.** If the matrix above doesn't match what the CLI accepts, check the current CLI help (`agentcore add gateway-target --help`) and the AWS docs — auth support per target type evolves across releases. If the `awsknowledge` MCP server is available, search for "AgentCore CreateGatewayTarget" to get the current API parameters. +### 2. Choose a target -**CLI vs. API for gateway auth:** The CLI covers `none`, `oauth` (2LO), and `api-key`. For IAM (SigV4) and 3-legged OAuth, use boto3 directly — the examples are in the Path A section below. The general pattern: create the gateway and target via CLI, deploy, then apply the advanced auth config via boto3 if the CLI doesn't support it. +| Service | v1 approach | +|---|---| +| External MCP server | `agentcore add gateway-target --gateway --name --endpoint https://.../mcp` | +| Runtime in this project | `agentcore add gateway-target --gateway --name --runtime ` | +| Lambda function by ARN | `--target-configuration file://.json` with `targetType: "lambdaFunctionArn"` | +| OpenAPI service | `--target-configuration file://.json` with `targetType: "openApiSchema"` | +| Curated Knowledge Base or web search connector | `agentcore add gateway-connector --gateway --name --connector `; add `--knowledge-base ` for a Knowledge Base | +| AWS SDK call using the Runtime's role | Call the AWS SDK directly from agent code | -Tell the developer which auth option applies to their target type before generating any commands. +The v1 shortcut uses `--endpoint` or `--runtime`. The v0 `--type`, `--lambda-arn`, `--schema`, and `--tool-schema-file` flags are gone. For full target configuration, include `name`, `targetType`, and the type's required fields in the JSON file. Do not combine `--target-configuration` with shortcut `--name` or `--outbound-auth` flags; authentication goes inside the JSON object in that form. Run `agentcore add gateway-target --help` and `agentcore build` when adapting a target schema. -### When your gateway has many tools, let the model search for them +### 3. Create an authenticated Gateway -Once a gateway has more than a handful of tools — roughly 20+ — passing every tool definition to the model on every turn wastes tokens and degrades accuracy. The model does better when it sees only the tools relevant to the current request. +Gateway's default authorizer is `NONE`. Set inbound auth deliberately when creating it: -AgentCore Gateway has a built-in semantic search tool for exactly this. Your agent calls a single MCP tool named `x_amz_bedrock_agentcore_search` with a natural-language query, and the gateway returns the most relevant tools from its catalog. The agent then invokes the returned tools normally. - -If a developer is considering building their own tool-selection layer with Bedrock Knowledge Bases, a vector store, or custom embeddings — stop them. The gateway already does this, evaluated against curated relevance criteria, with no infrastructure to manage. - -Usage pattern (the agent calls this the same way it calls any other gateway tool): - -```python -# Via the MCP client, as a tool call -result = await session.call_tool( - "x_amz_bedrock_agentcore_search", - arguments={"query": "find tools related to processing refunds"} -) -# result.content lists the most relevant tools — the agent then invokes them +```bash +agentcore add gateway --name MyGateway --authorizer-type AWS_IAM ``` -The feature works with any target type (Lambda, OpenAPI, MCP, API Gateway, Smithy). Enable it per gateway — see the [Search for tools in your AgentCore gateway](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-using-mcp-semantic-search.html) docs for the exact API surface and framework-specific client code. +For JWT, pass `--authorizer-type CUSTOM_JWT --authorizer-configuration file://authorizer.json`; use the project's `authorizerConfiguration` shape in the v1 schema. The gateway authorizer controls who can call the Gateway. It does not authenticate the Gateway to a target. -Rule of thumb: if a gateway has more than 20 tools, recommend enabling semantic search. For smaller catalogs, passing all tools directly is still fine. +Enable `--enable-semantic-search` if the Gateway needs tool search. This must be configured on the Gateway; merely calling `x_amz_bedrock_agentcore_search` does not enable it. -### Passing custom headers from the caller to the agent +### 4. Configure outbound authentication -If the developer needs callers to send custom HTTP headers (tenant IDs, correlation IDs, protocol-specific headers like `A2A-Version`, tracing headers, idempotency keys), the runtime's default is to strip most headers before they reach agent code. Load [`agents-build/references/request-headers.md`](../agents-build/references/request-headers.md) for the allowlist configuration and prefix pattern. - -This is about inbound calls to your agent, not outbound calls to tools — but developers hit it often enough that it's worth mentioning here. - ---- - -## Path A: MCP server - -### Add a gateway (if none exists) - -> [!WARNING] -> Never deploy a gateway without inbound authentication to production. A gateway with -> no authorizer exposes all connected tools (Lambda, MCP, OpenAPI) to any caller who -> knows the URL — functionally equivalent to --authorizer-type NONE on the runtime. -> Always use --authorizer-type CUSTOM_JWT or AWS_IAM for production gateways. -> The no-auth form (agentcore add gateway --name X) is for local testing only. +**Public MCP server:** ```bash -# Development (no inbound auth — for testing only) -agentcore add gateway --name MyGateway - -# Production (JWT inbound auth) -agentcore add gateway \ - --name MyGateway \ - --authorizer-type CUSTOM_JWT \ - --discovery-url https://your-idp.example.com/.well-known/openid-configuration \ - --allowed-audience my-api \ - --allowed-clients my-client-id +agentcore add gateway-target \ + --gateway MyGateway --name WeatherTools \ + --endpoint https://mcp.example.com/mcp --outbound-auth none ``` -### Add the MCP server as a target +**OAuth MCP server:** register the client credential, then reference it by name: ```bash -# No outbound auth (public MCP server) +agentcore add credentials oauth \ + --name WeatherOAuth \ + --client-id \ + --discovery-url https://auth.example.com/.well-known/openid-configuration \ + --scopes read write \ + --client-secret file:// agentcore add gateway-target \ - --type mcp-server \ - --name WeatherTools \ + --gateway MyGateway --name WeatherTools \ --endpoint https://mcp.example.com/mcp \ - --gateway MyGateway - -# OAuth outbound auth (2-legged — client credentials / M2M) -agentcore add gateway-target \ - --type mcp-server \ - --name WeatherTools \ - --endpoint https://mcp.example.com/mcp \ - --gateway MyGateway \ - --outbound-auth oauth \ - --oauth-client-id your-client-id \ - --oauth-client-secret your-client-secret \ - --oauth-discovery-url https://auth.example.com/.well-known/openid-configuration \ - --oauth-scopes read,write + --outbound-auth oauth --credential-name WeatherOAuth \ + --scope read --scope write ``` -Note: The CLI `--outbound-auth` flag supports `oauth` (2LO / client credentials) or `none` for MCP servers. - -- **3-legged OAuth (`AUTHORIZATION_CODE` grant)** — user-delegated access — is supported by the API but has no CLI path. Configure via boto3 `create_gateway_target` with `OAuthCredentialProvider.grantType = "AUTHORIZATION_CODE"` and `defaultReturnUrl`. See [Connecting to an OAuth-protected MCP server using Authorization Code flow](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-target-MCPservers.html#gateway-target-MCPservers-auth-code-grant-flow). -- **IAM (SigV4)** for MCP servers hosted on AgentCore Runtime, another AgentCore Gateway, API Gateway, or Lambda Function URLs is configured via the AWS SDK/API (not the CLI) — use `CreateGatewayTarget` with `GATEWAY_IAM_ROLE` credential provider and an `iamCredentialProvider.service` value. -- **API key auth** is not supported for MCP server targets at the API level (the MCP target docs list only no-auth, OAuth, and IAM as strategies) — if the MCP server uses an API key, handle it in agent code directly (see Path D). +The CLI rejects inline `--client-secret` and `--api-key` values. Use a local secret file, `-` for stdin, or the matching `--client-secret-reference` / `--api-key-secret-reference` with an existing Secrets Manager secret. Keep local secret files out of git. Check `agentcore add credentials oauth --help` for vendored OAuth configuration. -### Deploy and get the gateway URL +**API key provider:** ```bash -agentcore deploy -y -agentcore fetch access --name MyGateway +agentcore add credentials api-key --name MyAPIKey --api-key file:// ``` -The gateway URL is injected as `AGENTCORE_GATEWAY__URL` after deploy. - -### Generate gateway client code - -**Framework-agnostic MCP client:** - -```python -import os -import asyncio -from mcp import ClientSession -from mcp.client.streamable_http import streamablehttp_client - -# Injected by AgentCore after deploy. Format: AGENTCORE_GATEWAY__URL -GATEWAY_URL = os.getenv("AGENTCORE_GATEWAY_MYGATEWAY_URL") - -async def get_gateway_tools(): - """Discover tools from the gateway. Returns empty list if not deployed.""" - if not GATEWAY_URL: - return [] - async with streamablehttp_client(GATEWAY_URL) as (read, write, _): - async with ClientSession(read, write) as session: - await session.initialize() - result = await session.list_tools() - return result.tools - -async def call_gateway_tool(tool_name: str, arguments: dict): - """Call a specific tool through the gateway.""" - if not GATEWAY_URL: - raise RuntimeError("Gateway not available in local dev — deploy first") - async with streamablehttp_client(GATEWAY_URL) as (read, write, _): - async with ClientSession(read, write) as session: - await session.initialize() - return await session.call_tool(tool_name, arguments) -``` - -**For Strands**, pass gateway tools directly to the agent: - -```python -from mcp.client.streamable_http import streamablehttp_client -from mcp import ClientSession -from strands import Agent -from bedrock_agentcore.runtime import BedrockAgentCoreApp -from model.load import load_model # scaffolded by `agentcore create` - -app = BedrockAgentCoreApp() -GATEWAY_URL = os.getenv("AGENTCORE_GATEWAY_MYGATEWAY_URL") - -@app.entrypoint -def invoke(payload, context): - if not GATEWAY_URL: - # Local dev — run without gateway tools - agent = Agent(model=load_model()) - return {"response": str(agent(payload.get("prompt", "")))} - - # Deployed — discover and use gateway tools - tools = asyncio.run(get_gateway_tools()) - agent = Agent( - model=load_model(), - tools=tools, - ) - return {"response": str(agent(payload.get("prompt", "")))} - -if __name__ == "__main__": - app.run() -``` - -**For LangGraph**, add gateway tools to the tool node: +For a complete OpenAPI target, put the provider name in `outboundAuth` in its JSON configuration: -```python -from langchain_mcp_adapters.client import MultiServerMCPClient - -@app.entrypoint -def agent_invocation(payload, context): - if not GATEWAY_URL: - tools = [] - else: - # Use LangChain MCP adapter to get tools as LangChain-compatible tools - client = MultiServerMCPClient({"gateway": {"url": GATEWAY_URL, "transport": "streamable_http"}}) - tools = asyncio.run(client.get_tools()) - - llm_with_tools = llm.bind_tools(tools) - # ... rest of your LangGraph graph ... +```json +{ + "name": "MyAPI", + "targetType": "openApiSchema", + "schemaSource": {"inline": {"path": "specs/api.json"}}, + "outboundAuth": {"type": "API_KEY", "credentialName": "MyAPIKey"} +} ``` ---- - -## Path B: Lambda function as tools - ```bash -agentcore add gateway-target \ - --type lambda-function-arn \ - --name MyTools \ - --lambda-arn arn:aws:lambda:us-east-1:123456789012:function:my-tools \ - --tool-schema-file tools.json \ - --gateway MyGateway +agentcore add gateway-target --gateway MyGateway --target-configuration file://my-api-target.json ``` -The `tools.json` defines the tool schemas: +For an existing Lambda, use the complete configuration form: ```json { - "inlinePayload": [ - { - "name": "get_weather", - "description": "Get current weather for a city", - "inputSchema": { - "type": "object", - "properties": { - "city": {"type": "string", "description": "City name"} - }, - "required": ["city"] - } - } - ] + "name": "MyTools", + "targetType": "lambdaFunctionArn", + "lambdaFunctionArn": { + "lambdaArn": "arn:aws:lambda:us-east-1:123456789012:function:my-tools", + "toolSchemaFile": "tools.json" + } } ``` -**Auth:** Lambda targets use IAM role auth automatically — no `--outbound-auth` flag. The gateway's execution role needs `lambda:InvokeFunction` on the Lambda ARN. +Do not add `outboundAuth` to a `lambdaFunctionArn` target: the gateway role invokes Lambda through IAM. Scope that role to the intended function. -Use the same MCP client code from Path A to call the tools. - ---- - -## Path C: OpenAPI spec as tools +### 5. Build, deploy, and wire the agent ```bash -# From a local file (api-key auth) -agentcore add credential --name MyAPIKey --api-key sk-... - -agentcore add gateway-target \ - --type open-api-schema \ - --name MyAPI \ - --schema specs/api.json \ - --gateway MyGateway \ - --outbound-auth api-key \ - --credential-name MyAPIKey +agentcore build +agentcore deploy --region +agentcore status --json ``` -**Auth is required** for OpenAPI targets — either `oauth` (client credentials or authorization code) or `api-key`. - -⚠️ **Security note:** `--api-key` appears in shell history. Two safer options: - -1. **Interactive prompt (recommended):** run `agentcore add credential --name MyAPIKey --type api-key` without `--api-key` — the CLI will prompt, and the value goes straight into the credential provider (Secrets Manager-backed) without hitting your shell history. -2. **Edit `agentcore.json` + `.env.local` for local dev only:** if you need the credential to work under `agentcore dev`, put the value in `agentcore/.env.local` (gitignored). This file is read by the local dev server only — it is **not** uploaded to runtime on deploy. The deployed runtime gets the value from the credential provider. - -Do **not** try to ship a credential to the deployed runtime via environment variables — AgentCore Runtime env vars are not vault-backed. Register the credential once with `agentcore add credential` and reference it by name in the gateway target or in code (Path D). - ---- - -## Path D: Credentials for use in agent code - -For calling APIs directly in agent code (not through a gateway target). - -### Before you reach for Path D, check if it's actually the right path - -Path D is the **fallback**, not the starting point. For most external services, a Gateway target (Paths A–C) is safer and less code. Before generating Path D code, confirm one of these applies: - -- The service uses a streaming/bidirectional protocol Gateway doesn't front (SSE with live output, WebSockets, WebRTC) -- It's a measurably latency-critical hot path and the team has accepted the trade-off -- The client is a vendor binary SDK with no HTTP surface -- It's an AWS service SDK where the runtime's execution role already has IAM permissions (in which case: use the SDK directly — no credential registration needed) -- The developer has a specific blocker (e.g., the service ships an OpenAI-shaped API the vendor's SDK wraps, and rebuilding the SDK call as a Gateway target would be a regression) - -If none of those applies, route back to Path A/B/C: - -> "Before we wire up a credential for direct use in agent code, can we front this as a Gateway target instead? Gateway injects the credential at the edge — your agent code never touches the secret — and the tool becomes policy-enforceable. If SERVICE has an OpenAPI spec, MCP server, or Lambda function in front of it, Path C / A / B is the better fit. Which one applies?" - -Only continue into the rest of Path D when the developer confirms a legitimate reason Gateway won't work. - -### Register the credential - -```bash -# API key -agentcore add credential --name OpenAI --api-key sk-... - -# OAuth (machine-to-machine) -agentcore add credential \ - --name MyOAuthProvider \ - --type oauth \ - --discovery-url https://idp.example.com/.well-known/openid-configuration \ - --client-id my-client-id \ - --client-secret my-client-secret \ - --scopes read,write -``` - -⚠️ **Security note:** `--api-key` and `--client-secret` appear in shell history. Run the command without those flags to get an interactive prompt — the value goes straight into the credential provider without touching your shell history. - -**For local dev only**, put the same value in `agentcore/.env.local` (gitignored) so `agentcore dev` can resolve the decorator locally. The deployed runtime ignores `.env.local` and fetches the secret from the credential provider at call time — **never** ship secrets as runtime environment variables. - -### Use credentials in agent code - -Use the `@requires_api_key` or `@requires_access_token` decorators — they handle token caching and refresh automatically. The decorators work with both sync and async functions: - -```python -from bedrock_agentcore.identity.auth import requires_api_key, requires_access_token - -# Sync function — decorator injects the fetched key via keyword arg -@requires_api_key(provider_name="OpenAI") -def call_openai(prompt: str, *, api_key: str) -> str: - import openai - client = openai.OpenAI(api_key=api_key) - response = client.chat.completions.create( - model="gpt-4o", - messages=[{"role": "user", "content": prompt}] - ) - return response.choices[0].message.content - -# Async function — same decorator, async def -@requires_access_token( - provider_name="MyOAuthProvider", - scopes=["read", "write"], - auth_flow="M2M", -) -async def call_my_api(data: dict, *, access_token: str) -> dict: - import httpx - async with httpx.AsyncClient() as client: - response = await client.post( - "https://api.example.com/endpoint", - headers={"Authorization": f"Bearer {access_token}"}, - json=data, - ) - return response.json() -``` - -The decorator itself handles the token lifecycle — you don't need to make the function async just to use it. Parameters are keyword-only (`*, api_key: str` or `*, access_token: str`) — the decorator injects them. - -**Local dev:** In `agentcore dev`, credentials are read from `agentcore/.env.local`. The decorator pattern works the same way locally and deployed. - ---- - -## Local dev gap - -> [!WARNING] -> Gateway URLs (AGENTCORE_GATEWAY_*_URL) are only available after deploy. -> In agentcore dev, these env vars are not set. Always check before using: -> -> ```python -> GATEWAY_URL = os.getenv("AGENTCORE_GATEWAY_MYGATEWAY_URL") -> if not GATEWAY_URL: -> # run without gateway tools in local dev -> ``` -> -> Never assume the gateway is available locally. - ---- - -## Troubleshooting - -**"mcp-server target doesn't support api-key auth"** -Correct — API key auth is not supported for MCP server targets at the API level ([MCP target auth strategies](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-target-MCPservers.html#gateway-target-MCPservers-considerations)). Options: OAuth (2LO or 3LO), IAM (for MCP servers hosted on AgentCore Runtime, API Gateway, or Lambda Function URLs), or Path D — manage the credential in agent code and call the MCP server directly. - -**"I need 3LO / authorization-code OAuth but `--outbound-auth oauth` doesn't ask for a return URL"** -The CLI only configures 2LO (client credentials). 3-legged OAuth requires boto3 — call `create_gateway_target` with `credentialProviderType: OAUTH`, `grantType: AUTHORIZATION_CODE`, and `defaultReturnUrl`. See [Connecting to an OAuth-protected MCP server using Authorization Code flow](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-target-MCPservers.html#gateway-target-MCPservers-auth-code-grant-flow). - -**"api-gateway target doesn't support oauth"** -Use `api-key` or `none` for API Gateway targets. - -**Gateway URL not set after deploy** -Run `agentcore fetch access --name MyGateway` to get the URL. Check `agentcore status --type gateway` to verify the gateway is deployed. - -**Tool calls failing with auth errors** -Check `agentcore logs --runtime MyAgent --since 1h --level error` for the specific error. Common causes: expired OAuth token, wrong credential name, IAM permission missing. +On the first deploy, the CLI creates the default target using the effective AWS account and region; subsequent deploys can select `--target `. Read the deployed Gateway URL from `agentcore status --json`. The deployed Runtime receives `AGENTCORE_GATEWAY__URL`; check the exact name in its generated README or environment. In local `agentcore dev`, this deployed URL is absent unless supplied explicitly. The agent framework still needs MCP client wiring to discover and call the Gateway's tools. -**"Adding gateway to existing agent" workaround** -The CLI recommends creating a throwaway agent to copy gateway client code. This skill generates the code directly — no workaround needed. +A Harness uses a Gateway tool entry in `app//harness.yaml`, referencing the deployed Gateway ARN. Its config and a Runtime's MCP client code are different paths; use the one matching the project. -**MCP clients (Claude Desktop, claude.ai) can't auto-connect to Gateway** -AgentCore Gateway does not currently implement the MCP OAuth spec endpoints (RFC 8414 OAuth Authorization Server Metadata, RFC 7591 Dynamic Client Registration). MCP clients that expect to auto-discover OAuth config and register themselves — like Claude Desktop and claude.ai — cannot connect without manual credential configuration. The workaround is to manually obtain the Cognito `client_id` and `client_secret` and enter them in the MCP client's advanced settings. This is a platform limitation, not a config error. +### 6. Verify and troubleshoot -## Output +- Inspect `agentcore status --json` and `agentcore gateway target list --gateway-id --json` for deployed resources. +- For a Runtime error, use `agentcore logs runtime --name --since 1h --level error`; for a Harness, use `agentcore logs harness --name --since 1h --level error`. +- Compare the target's `outboundAuth` type and credential name with its provider in `agentcore.json`. A Gateway URL alone does not tell you the outbound auth state. +- For Cedar authorization, load [references/policy.md](references/policy.md). -- A clear recommendation on **Gateway target vs direct API call**, grounded in the five cases where direct is legitimate -- CLI commands to set up the gateway and target (or to register the credential, if Path D is the right call) -- Framework-specific gateway client code -- Credential setup (avoiding shell history exposure, never stored in runtime env vars) -- Local dev gap handling +Direct API calls from agent code are suitable when the service has no Gateway target shape or the AWS SDK already uses the Runtime's IAM role. For API keys and OAuth in code, register providers with `agentcore add credentials api-key` or `oauth`, then use the AgentCore Identity SDK decorators. The deployed Runtime obtains providers through Identity; local development may need `agentcore/.env.local`. Do not place secrets in Runtime environment variables. diff --git a/plugins/aws-agents/skills/agents-connect/references/policy.md b/plugins/aws-agents/skills/agents-connect/references/policy.md index ad3e6d21a..cf163a45e 100644 --- a/plugins/aws-agents/skills/agents-connect/references/policy.md +++ b/plugins/aws-agents/skills/agents-connect/references/policy.md @@ -1,336 +1,62 @@ -# policy +# Gateway policies in AgentCore CLI v1 -Control what your AgentCore agent can do — restrict tool calls, enforce business rules, and protect sensitive operations. +Use Cedar policy engines to authorize Gateway tool calls. Policies act at the Gateway boundary; they do not replace Runtime or Harness inbound authentication, and Bedrock Guardrails are a different feature. -## When to use +## Inspect the project -- You want to restrict which tools your agent can call -- You want to enforce business rules (e.g., refunds only under $500) -- You want role-based access control on agent actions -- You want an emergency kill switch for specific tools -- A policy is denying requests you expect to allow (or allowing what you expect to deny) +Read `agentcore/agentcore.json` for `agentCoreGateways`, `policyEngines`, and each Gateway's `policyEngineConfiguration`. For a v0 project (`"version": 1`), commit, review `agentcore migrate`, then apply the reviewed plan with `agentcore migrate --yes` before continuing. Require CLI v1.0.0 or later. -## Input +Choose a Gateway and list the tool names and caller identities that should be allowed. When an attached engine enforces decisions, write explicit `permit` policies for desired calls. A matching `forbid` overrides a `permit`. -`$ARGUMENTS` is optional: +## Create and attach an engine -``` -/policy # interactive — asks what you want to restrict -/policy generate # generate Cedar from natural language -/policy debug # diagnose why a policy is allowing/denying -/policy emergency # generate an emergency shutdown policy -``` - -## How AgentCore policy works - -AgentCore Policy enforces Cedar-based authorization rules at the **gateway boundary** — before any tool call reaches its target. Every tool call is evaluated against your policies in real time. - -**Default behavior:** Without a policy engine attached to your gateway, all tool calls are allowed. Once you attach a policy engine, the default is **deny** — you must write explicit `permit` policies for everything you want to allow. - -**Key concepts:** - -- **Policy engine** — the container for your policies, attached to a gateway -- **Policy** — a Cedar rule that permits or forbids specific actions -- **`forbid` overrides `permit`** — if any forbid policy matches, the action is denied regardless of permit policies - ---- - -## Process - -### Step 1: Read the project - -Read `agentcore/agentcore.json` to understand: - -- What gateways exist (in the `agentCoreGateways` array) -- Whether a policy engine is already configured (in the `policyEngines` array) - -### Step 2: Understand the goal - -Ask (or infer from `$ARGUMENTS`): - -> "What do you want to control? -> -> 1. Restrict a tool based on input values (e.g., amount < $500) -> 2. Role-based access (only certain users can call certain tools) -> 3. Block a specific tool entirely -> 4. Emergency shutdown — disable all tools immediately -> 5. Debug why a policy is allowing or denying unexpectedly" - ---- - -## Path A: Set up a policy engine - -### Step A1: Create the policy engine +For a new Gateway and engine, add the Gateway with an explicit inbound authorizer, then add the engine and attach in log-only mode: ```bash -# Create and attach to an existing gateway +agentcore add gateway --name MyGateway --authorizer-type AWS_IAM agentcore add policy-engine \ --name MyPolicyEngine \ --attach-to-gateways MyGateway \ - --attach-mode LOG_ONLY -``` - -**Start with `LOG_ONLY` mode** — policies are evaluated and logged but not enforced. This lets you verify your policies work correctly before enabling enforcement. - -Switch to `ENFORCE` when ready: - -```bash -# Update an existing gateway -agentcore add gateway \ - --name MyGateway \ - --policy-engine MyPolicyEngine \ - --policy-engine-mode ENFORCE -``` - -(The same `--policy-engine` and `--policy-engine-mode` flags work at gateway creation time too.) - -### Step A2: Deploy to activate - -```bash -agentcore deploy -y + --attach-mode log-only +agentcore build +agentcore deploy --region +agentcore status --json ``` ---- - -## Path B: Write Cedar policies +`--attach-mode` accepts `log-only` or `enforce`; the v0 `LOG_ONLY` and `ENFORCE` flag values are not the v1 spelling. For an existing Gateway with no engine, `agentcore add policy-engine --attach-to-gateways MyGateway` can attach it. To change a Gateway's existing mode, edit its `policyEngineConfiguration` in `agentcore/agentcore.json`, run `agentcore build`, then deploy. `agentcore add gateway` creates a Gateway; it does not update an existing one. Use `--policy-engine-name` and `--policy-engine-mode` when creating a new Gateway around an engine already declared in the project. -> [!WARNING] -> Cedar policies that reference a specific gateway ARN in the `resource` field require -> the gateway to be deployed first. You cannot add a policy with a gateway ARN before -> the gateway exists in AWS. -> -> Two-phase deployment: -> -> 1. Deploy the gateway first: `agentcore deploy -y` -> 2. Get the gateway ARN: `agentcore status --type gateway --json` -> 3. Add the policy with the real ARN, then deploy again -> -> The `-g` / `--generate` flag also requires a deployed gateway — it calls an AWS API -> that needs the gateway ARN to convert natural language into Cedar. If you run -> `-g` before deploying the gateway, it will fail. +## Write a Cedar policy -### Option 1: Natural language generation (easiest) - -**Requires the gateway to be deployed first** — the CLI calls an API that needs the gateway ARN. +A policy referencing the deployed Gateway ARN requires the first deploy. Obtain that ARN from `agentcore status --json`. Save the Cedar statement to a file, review it, and add it to the project: ```bash -# Deploy the gateway first -agentcore deploy -y - -# Then generate the policy (--gateway tells the CLI which deployed gateway to use) agentcore add policy \ - --name refund_policy \ --engine MyPolicyEngine \ - -g "Allow users with the refund-agent role to process refunds when the amount is less than 500" \ - --gateway MyGateway -``` - -The CLI generates Cedar from your description, resolves the gateway ARN automatically, and validates the result. Review the generated policy before deploying. - -**Policy name rules:** letters, numbers, underscores only — **no hyphens**. `refund-policy` fails; `refund_policy` works. - -### Option 2: Write Cedar directly - -Save to a `.cedar` file and register. If the policy references a gateway ARN in the `resource` field, you need the ARN from a prior deploy: - -```bash -# Get the gateway ARN after deploying -agentcore status --type gateway --json | jq -r '.gateways[0].arn' - -# Update your .cedar file with the real ARN, then add the policy -agentcore add policy \ --name refund_policy \ - --engine MyPolicyEngine \ - --source policy.cedar -``` - -### Cedar syntax reference - -**Action name format:** `AgentCore::Action::"TargetName___tool_name"` — three underscores between target name and tool name. This is the most common Cedar mistake. - -```cedar -// TargetName is the gateway target name (from agentcore add gateway-target --name) -// tool_name is the tool name within that target -AgentCore::Action::"RefundTarget___process_refund" -// ^^^ -// three underscores -``` - -**Principal types:** - -- `AgentCore::OAuthUser` — authenticated user via OAuth/JWT -- `AgentCore::IamEntity` — IAM-authenticated caller (when gateway uses AWS_IAM auth). The `id` attribute contains the full IAM ARN. - -**Resource format:** - -```cedar -AgentCore::Gateway::"arn:aws:bedrock-agentcore:::gateway/" -``` - -Get your gateway ARN: `agentcore status --type gateway --json | jq -r '.gateways[0].arn'` - -### Common policy patterns - -**Amount-based restriction:** - -```cedar -permit( - principal is AgentCore::OAuthUser, - action == AgentCore::Action::"RefundTarget___process_refund", - resource == AgentCore::Gateway::"arn:aws:bedrock-agentcore:us-east-1:123456789012:gateway/my-gateway-id" -) -when { - principal.hasTag("role") && - principal.getTag("role") == "refund-agent" && - context.input.amount < 500 -}; -``` - -**Role-based access (OAuth user):** - -```cedar -permit( - principal is AgentCore::OAuthUser, - action == AgentCore::Action::"AdminTarget___delete_record", - resource == AgentCore::Gateway::"arn:..." -) -when { - principal.hasTag("role") && - ["admin", "superuser"].contains(principal.getTag("role")) -}; -``` - -**Account-based access (IAM entity):** - -```cedar -permit( - principal is AgentCore::IamEntity, - action == AgentCore::Action::"AdminTarget___delete_record", - resource == AgentCore::Gateway::"arn:..." -) -when { - principal.id like "arn:aws:iam::123456789012:*" -}; -``` - -**Block a specific tool entirely:** - -```cedar -forbid( - principal, - action == AgentCore::Action::"PaymentTarget___transfer_funds", - resource == AgentCore::Gateway::"arn:..." -); -``` - -**Emergency shutdown — disable all tools:** - -```cedar -forbid(principal, action, resource); -``` - -**Required field validation:** - -```cedar -forbid( - principal is AgentCore::OAuthUser, - action == AgentCore::Action::"InsuranceTarget___file_claim", - resource == AgentCore::Gateway::"arn:..." -) -unless { - context.input has description && - context.input has priority -}; -``` - -### Critical Cedar rules - -**Always use `hasTag()` before `getTag()`:** - -```cedar -// ❌ Wrong — throws error if tag doesn't exist -when { principal.getTag("role") == "admin" } - -// ✅ Correct — check existence first -when { - principal.hasTag("role") && - principal.getTag("role") == "admin" -} + --statement file://policy.cedar \ + --validation-mode fail-on-any-findings \ + --enforcement-mode log-only +agentcore build +agentcore deploy ``` -**Default deny:** Once a policy engine is attached in ENFORCE mode, everything is denied unless a `permit` policy matches. Write explicit permits for every action you want to allow. - -**`forbid` always wins:** A `forbid` policy overrides any `permit` policy. Use this for emergency shutdowns and hard blocks. - ---- - -## Path C: Test policies before enforcing - -### LOG_ONLY mode - -In LOG_ONLY mode, all requests are allowed but policy decisions are logged to CloudWatch. Use this to verify your policies before switching to ENFORCE. - -```bash -# Check policy decision logs -agentcore logs --runtime MyAgent --since 1h --query "policy" -``` - -Look for log entries showing `ALLOW` or `DENY` decisions for each tool call. - -### Validate policy syntax - -```bash -agentcore add policy \ - --name test_policy \ - --engine MyPolicyEngine \ - --source policy.cedar \ - --validation-mode FAIL_ON_ANY_FINDINGS -``` - -If the Cedar syntax is invalid, the CLI returns a validation error before creating the policy. - -### Switch to ENFORCE - -Once LOG_ONLY results look correct: - -```bash -# Update gateway to enforce mode -agentcore add gateway \ - --name MyGateway \ - --policy-engine MyPolicyEngine \ - --policy-engine-mode ENFORCE -agentcore deploy -y -``` - ---- - -## Path D: Debug policy failures - -**"Access denied" on a tool call you expect to allow:** +`--statement` accepts inline text, `file://`, or `-` for stdin. The v0 `--source` and `-g` flags are gone. If the policy checks an output, set `--authorization-phase return-output`; otherwise the CLI infers the phase from the statement. Tool action names use `AgentCore::Action::"TargetName___tool_name"` (three underscores). Confirm the name from the deployed Gateway target and test the intended principal, tool, and arguments. -1. Check that a `permit` policy exists for this action — remember, default is deny -2. Verify the action name format: `TargetName___tool_name` (three underscores) -3. Verify the resource ARN matches your gateway's actual ARN -4. Check that `hasTag()` is used before `getTag()` in conditions -5. Check LOG_ONLY logs to see what the policy engine is evaluating +For natural language generation, use the Gateway resource command with its deployed ID or ARN: ```bash -# Check recent policy decisions -agentcore logs --runtime MyAgent --since 1h --query "policy" -agentcore status --type policy-engine +agentcore gateway policy generate \ + --gateway-id \ + --policy-engine-id \ + --prompt "Allow the refund tool only for approved operators" ``` -**"Everything is being denied" after attaching a policy engine:** -You attached a policy engine but haven't written any `permit` policies yet. The default is deny. Write at least one `permit` policy for the actions you want to allow. +Review the generated Cedar and save the chosen statement to `policy.cedar`; add it with `agentcore add policy --statement file://policy.cedar`. Generation does not create the project policy for you. -**Policy name validation error:** -Policy names must match `^[A-Za-z][A-Za-z0-9_]*$` — letters, numbers, underscores only, starts with a letter. No hyphens. +## Test and enforce ---- +Start with the Gateway's engine in `log-only` and the policy in `log-only`. Exercise allowed and denied tool calls and inspect policy decisions in the service's policy decision logs. `agentcore logs runtime` and `agentcore logs harness` show agent application logs, not necessarily the Gateway policy decision stream. -## Output +After checking the expected decisions, set the Gateway's `policyEngineConfiguration` mode to `ENFORCE` and the desired policy's `enforcementMode` to `ACTIVE` in `agentcore/agentcore.json`, then run `agentcore build` and `agentcore deploy`. Confirm the actual enum values against the v1 project schema and CLI output before editing. Test an allowed call and a denied call against the deployed Gateway. -- CLI commands to create the policy engine and policies -- Cedar policy file for the requested use case -- LOG_ONLY testing workflow before enforcement -- Debugging guidance for policy failures +If a call is denied unexpectedly, check the policy's action name, Gateway ARN, principal type, authorization phase, and engine attachment. If every call is denied, check that at least one applicable `permit` is active. Do not solve a denial by changing the Gateway's inbound auth to `NONE`. diff --git a/plugins/aws-agents/skills/agents-pay/SKILL.md b/plugins/aws-agents/skills/agents-pay/SKILL.md index 39b4e8881..93717d7df 100644 --- a/plugins/aws-agents/skills/agents-pay/SKILL.md +++ b/plugins/aws-agents/skills/agents-pay/SKILL.md @@ -20,6 +20,7 @@ metadata: type: skill version: "1.0.0" author: aws-agentcore + requires-cli: ">=1.0.0" --- # pay @@ -130,7 +131,7 @@ path** (the agent). They share resource identifiers and nothing else. ``` ADMIN PATH — human only, holds credentials - agentcore add payment-manager / payment-connector (provider secrets via CLI wizard) + agentcore add payment-manager / credentials payment / payment-connector agents_pay_admin.py init-config -> ~/.agents-pay/config.json (0600) agents_pay_admin.py new-session -> budget-bounded session, typed approval | @@ -229,27 +230,30 @@ agentcore --version ### Step 1: Provision payment resources — human runs this outside the LLM loop -The agent must NOT run this step; it involves provider credentials. Tell the -user to open a separate terminal and complete the commands there. Do not ask -them to paste credentials, command output, deployed state, or generated IDs -back into chat. Wait only for the user to confirm that setup completed. +The agent must not handle provider secrets. Ask the human to use the v1 flow in +[references/setup.md](references/setup.md) from an AgentCore project root. The CLI +must be v1.0.0 or later (`agentcore --version`). The human may choose Coinbase +Quick Create without supplying secrets, or manual Coinbase/Stripe provisioning +with `agentcore add credentials payment` and file-based secret inputs. The v0 +no-flags payment wizard and inline secret flags are not v1 commands. + +The minimum Quick Create path is: ```bash -npm install -g @aws/agentcore -agentcore add payment-manager # NO FLAGS — interactive wizard -agentcore add payment-connector # NO FLAGS — interactive wizard -agentcore deploy # interactive deployment +agentcore add payment-manager --name MyPayments +agentcore add payment-connector --manager MyPayments --name Coinbase --quick-create +agentcore build +agentcore deploy --region +agentcore status --json ``` -Run both `agentcore add` commands with **no flags** to keep the complete setup -flow in the human's terminal. In particular, connector secret flags put values -in shell history and the process list. See -[`references/setup.md`](references/setup.md) for obtaining Coinbase CDP / -Stripe Privy credentials and for the split IAM policies. - -`agentcore/.env.local` holds provider secrets in plaintext until `deploy` -uploads them to AgentCore Identity. Ensure `.env.local` is gitignored. **The -agent must never read that file.** +If there is no project, first run `agentcore create --name PaymentsProject --template empty` +and enter it. For a v0 project, commit, review `agentcore migrate`, then apply the +reviewed plan with `agentcore migrate --yes`. The human completes Coinbase +Marketplace subscription and authorization outside the agent loop. For manual +providers, follow the credential steps in the setup reference. `agentcore/.env.local` +may hold provider secrets until deploy and must remain gitignored and unread by +the agent. ### Step 2: Write the payment policy — human runs this @@ -293,9 +297,7 @@ script, so a look-alike contract cannot be pasted in. python3 scripts/agents_pay_admin.py create-instrument --email you@example.com ``` -The manager ARN and connector ID are read from `agentcore/.cli/deployed-state.json` -(written by `agentcore deploy`), so nothing needs copying by hand — run it from the -project directory, or pass `--manager-arn` / `--connector-id`. +The manager ARN and connector ID are read from `agentcore status --json` for a v1 project. Run from the project directory, or pass `--manager-arn` / `--connector-id`. It prints the wallet address, the delegation URL, and the `export` lines for the runtime. Delegation and funding are then done by the **end user** — see @@ -450,7 +452,7 @@ decide whether its runtime surface is safe: | Ask | Safe answer | Why | |---|---|---| -| Does any tool take a wallet secret or provider key as a **parameter**? | No — credentials come from the environment or the `agentcore` wizard | A model-visible secret ends up in transcripts, traces, and logs | +| Does any tool take a wallet secret or provider key as a **parameter**? | No — credentials come from the environment or the `agentcore` credential flow | A model-visible secret ends up in transcripts, traces, and logs | | Can the model call something that **creates a payment session**? | No — session creation is human-only | Otherwise it mints fresh budget when one runs out, and per-session caps bound nothing | If either answer is wrong, do not use the plugin's tools for payment. Disable the diff --git a/plugins/aws-agents/skills/agents-pay/references/openclaw-setup.md b/plugins/aws-agents/skills/agents-pay/references/openclaw-setup.md index 545aa99a4..ef89b11a8 100644 --- a/plugins/aws-agents/skills/agents-pay/references/openclaw-setup.md +++ b/plugins/aws-agents/skills/agents-pay/references/openclaw-setup.md @@ -37,17 +37,17 @@ Create an AgentCore project before adding and deploying the payment resources: ```bash npm install -g @aws/agentcore -PROJECT_NAME=openclaw-payments -agentcore create --project-name "$PROJECT_NAME" --no-agent +PROJECT_NAME=OpenclawPayments +agentcore create --name "$PROJECT_NAME" --template empty cd "$PROJECT_NAME" -agentcore add payment-manager -agentcore add payment-connector -agentcore deploy +agentcore add payment-manager --name MyPayments --no-auto-payment +agentcore add payment-connector --manager MyPayments --name Coinbase --quick-create +agentcore build +agentcore deploy --region export AGENTCORE_PROJECT_DIR="$PWD" ``` -Run both `agentcore add` commands without flags so credentials remain in the -interactive terminal. Keep `AGENTCORE_PROJECT_DIR` set while running the setup +Quick Create needs no provider secrets. Complete the Coinbase authorization outside the OpenClaw conversation. For manual provider credentials, follow [setup.md](setup.md). Keep `AGENTCORE_PROJECT_DIR` set while running the setup wizard from the plugin directory. ## Quick Setup (Recommended) @@ -91,8 +91,7 @@ python scripts/agents_pay_admin.py new-session \ --expiry-minutes 60 ``` -The two `agentcore add` commands must run without flags so credentials remain in -the interactive terminal. Do not paste credentials, command output, deployed +The provisioning commands above use v1 flags. Keep any manual provider credentials in the human terminal and out of the OpenClaw conversation. Do not paste credentials, command output, deployed state, or generated identifiers into an LLM conversation. Complete delegation and testnet funding before creating the session. Then edit diff --git a/plugins/aws-agents/skills/agents-pay/references/operator-guide.md b/plugins/aws-agents/skills/agents-pay/references/operator-guide.md index 0fd3b2387..61ccd3ce2 100644 --- a/plugins/aws-agents/skills/agents-pay/references/operator-guide.md +++ b/plugins/aws-agents/skills/agents-pay/references/operator-guide.md @@ -89,9 +89,10 @@ python -m pip install -r requirements.txt npm install -g @aws/agentcore # 1. Provision (ControlPlaneRole; only step that touches provider secrets) -agentcore add payment-manager # NO FLAGS — interactive wizard -agentcore add payment-connector # NO FLAGS — interactive wizard -agentcore deploy +agentcore add payment-manager --name MyPayments --no-auto-payment +agentcore add payment-connector --manager MyPayments --name Coinbase --quick-create +agentcore build +agentcore deploy --region # 2. Write the config with at least one approved payee python scripts/agents_pay_admin.py init-config \ @@ -161,7 +162,7 @@ runtime path; enabling both would make policy enforcement path-dependent. Two things to confirm before you trust the OpenClaw runtime: 1. **No tool takes a wallet secret or provider key as a parameter.** Credentials - belong in the environment or the `agentcore` wizard, never in a model-visible + belong in the environment or the `agentcore` credential flow, never in a model-visible schema — otherwise they land in transcripts, traces, and logs. 2. **No model-callable tool creates a payment session.** If the model can mint budget, the per-session cap bounds nothing. diff --git a/plugins/aws-agents/skills/agents-pay/references/security-model.md b/plugins/aws-agents/skills/agents-pay/references/security-model.md index a07021454..d4a9c6004 100644 --- a/plugins/aws-agents/skills/agents-pay/references/security-model.md +++ b/plugins/aws-agents/skills/agents-pay/references/security-model.md @@ -61,7 +61,7 @@ explicit `payment_session_id` so budget still comes from a human. | Component | Trusted? | Holds credentials? | Who runs it | |---|---|---|---| -| Operator at a terminal | Yes — the root of authority | Yes (via the CLI wizard) | Human | +| Operator at a terminal | Yes — the root of authority | Yes (via the CLI credential flow) | Human | | `agents_pay_admin.py` | Yes | Only transiently, from the human | Human | | `~/.agents-pay/config.json` | Yes — the authorization record | No | Written by human, read by runtime | | `x402_policy.py` | Yes — the decision point | No | In-process, runtime | @@ -92,7 +92,7 @@ Two design rules apply throughout this skill: | `get_payment_session_status` | `payment_session_status()` — agent tool | Unchanged in spirit: read-only, cannot mint budget | | Browser payment | `prepare_browser_payment(url)` + `attach_browser_payment(handle, url)` | The model receives an opaque single-use handle; trusted glue redeems it | | Create a payment session | `agents_pay_admin.py new-session` — human at a TTY | A runtime that can mint sessions has no cumulative bound | -| Provision infrastructure | `agentcore` CLI wizard + `agents_pay_admin.py init-config` | Provider secrets never enter tool parameters, and setup does not exist at runtime | +| Provision infrastructure | `agentcore` CLI credential flow + `agents_pay_admin.py init-config` | Provider secrets never enter tool parameters, and setup does not exist at runtime | The browser flow is worth stating plainly, because it is the one case where a proof must reach a caller: the model receives a handle, never proof bytes. The @@ -106,7 +106,7 @@ for trusted glue, not for the model's tool set. | Control | Enforcement | |---|---| | Challenge validation | Strict schema, configured scheme and network, exact asset contract, explicit recipient mode, canonical positive amount under `max_per_payment_usd`, and resource/origin checks are enforced before signing. Conflicting `amount` and `maxAmountRequired` aliases are refused; the signer receives one version-canonical amount field. The normal mode requires `payTo` in `allowed_recipients`; the explicit `allow_any_recipient: true` mode delegates beneficiary choice to the publisher. | -| Secret handling | No script accepts a secret argument. Provider credentials go only to the `agentcore` CLI wizard; signing happens inside AgentCore Payments. `preflight` rejects credential-shaped environment variables. | +| Secret handling | No script accepts a secret argument. Provider credentials go only through `agentcore add credentials payment` with file or stdin secret inputs; signing happens inside AgentCore Payments. `preflight` rejects credential-shaped environment variables. | | Network protection | `assert_public_https_url()` and `assert_public_ip()` require HTTPS and reject loopback, RFC1918, link-local, metadata, multicast, reserved, unspecified, CGNAT, and v4-mapped forms. `_PinnedResolverTransport` connects to the vetted address; redirects are refused and bodies are capped. | | Content isolation | Paid bodies are withheld from model-visible output by default. The runtime returns status, content type, byte count, and SHA-256 hash. An operator may opt in to body return by setting `return_body: true` in the config file (OS-account-bound, 0600); when enabled, content is capped at 10 KiB and marked `untrusted: true`. Authorisation never reads content. The OpenClaw plugin (`get_paid_content`) mirrors this: an optional `returnBody: true` plugin-config field (default unset/false, same behaviour as today) opts a single installation in to the same capped, `untrusted`-marked body return — set per operator, not model-controllable. | | Idempotency | `derive_client_token()` hashes session, origin, path, network, asset, recipient, and amount. It excludes a publisher nonce so retries reuse the same authorisation. | diff --git a/plugins/aws-agents/skills/agents-pay/references/setup.md b/plugins/aws-agents/skills/agents-pay/references/setup.md index 23679d00e..6fdbf17ef 100644 --- a/plugins/aws-agents/skills/agents-pay/references/setup.md +++ b/plugins/aws-agents/skills/agents-pay/references/setup.md @@ -1,107 +1,73 @@ # agents-pay setup -Full provisioning walkthrough. **A human runs every step here** except the final -wiring. See the tool inventory in SKILL.md for what the agent can call. +A human provisions resources and authorizes payment sessions outside the agent Runtime. The agent can only spend an already approved session. Require AgentCore CLI v1.0.0 or later and a Python environment with `bedrock_agentcore.payments`. -## 0. Install +## 0. Install and locate the project ```bash python3 -m venv .venv source .venv/bin/activate python -m pip install -r requirements.txt -npm install -g @aws/agentcore +npm install -g @aws/agentcore@latest agentcore --version python -c "from bedrock_agentcore.payments import PaymentManager; print('payments OK')" ``` -If that import fails, install a `bedrock-agentcore` release with payments -support before continuing. Keep the virtual environment active for the Python -commands below. - -`requirements.txt` pins the complete Python runtime graph used by this skill. For -integrity pinning in your own release process, generate platform-specific hashes -from that exact graph: +Work from the root of an AgentCore CLI v1 project (`agentcore/agentcore.json` has `"version": 2`). If the project is v0, commit it, review `agentcore migrate`, and apply the reviewed plan using `agentcore migrate --yes`. For a project dedicated to payment resources: ```bash -python -m pip download -r requirements.txt -d wheels/ -python -m pip hash wheels/* +agentcore create --name PaymentsProject --template empty +cd PaymentsProject ``` -## 1. Obtain provider credentials - -Do this before running the connector command. **Never paste these into a chat, -an agent prompt, or a command flag.** - -**Coinbase CDP** — - -1. Create or open a CDP project -2. Create an API key — note the **API Key ID** and **API Key Secret** -3. Generate a **Wallet Secret** (used for signing operations) -4. Project → Wallet → Embedded Wallets → Policies → **enable Delegated signing** (required) - -**Stripe Privy** — - -1. Create a **dedicated** Privy app for AgentCore (do not reuse an existing app) -2. Copy the **App ID** and **App Secret** -3. Wallet Infrastructure → Authorization → New Key → generate a P-256 key pair -4. Strip the `wallet-auth:` prefix from the private key; keep the raw base64 (starts `MIGHAgEA...`) -5. Note the **Authorization ID** +## 1. Choose the connector -## 2. Create the payment manager +For Coinbase, Quick Create lets the service provision provider credentials. Complete any required AWS Marketplace subscription and Coinbase authorization outside the agent conversation. For manually managed Coinbase or Stripe Privy credentials, obtain them from the provider and store each secret in a local file kept outside git. The v1 CLI accepts secret flags only as `file://` or `-` for stdin; it rejects inline secrets. -Leave the LLM conversation and run this in a separate terminal. Do not paste -credentials, command output, deployed state, or generated IDs back into chat. -Use the bare command so the whole setup stays in the interactive terminal: +## 2. Add the payment manager ```bash -agentcore add payment-manager +agentcore add payment-manager \ + --name MyPayments \ + --network-preferences eip155:84532 \ + --no-auto-payment ``` -Names: start with a letter, alphanumeric plus underscores, ≤48 characters. +Names start with a letter and contain only alphanumeric characters, at most 48 characters. The v1 CLI requires `--name`; the v0 bare-command wizard is gone. If the project uses skill onboarding tags, put `"agentcore:onboarding-source": "agent-toolkit-skill"` in the project's top-level `tags` object and retain existing tags. +The CLI enables auto-payment by default. This skill uses `--no-auto-payment` so spending remains behind its operator-approved session and policy checks. -Optionally tag the project so the service can distinguish skill-onboarded -resources — add to the top-level `tags` object in `agentcore/agentcore.json`, -keeping existing entries: +## 3. Add the payment connector -```json -"tags": { "agentcore:onboarding-source": "agent-toolkit-skill" } -``` - -## 3. Create the payment connector - -Run it with **no flags**. The wizard prompts for each secret, so nothing lands in -shell history or the process list: +For Coinbase Quick Create: ```bash -agentcore add payment-connector +agentcore add payment-connector \ + --manager MyPayments --name Coinbase --quick-create ``` -Passing `--manager`, `--name`, or `--provider` switches the CLI to -non-interactive mode and then *requires every secret as a flag* — it will not -fall back to prompting. Use the bare command. - -The CLI writes provider secrets in **plaintext** to `agentcore/.env.local` and -uploads them to AgentCore Identity on deploy. Before deploying: +For manual Coinbase or Stripe, first add a v1 payment credential provider. Examples use file inputs for secrets: ```bash -grep -q '^\.env\.local$\|^\.env\.\*$' .gitignore || echo '.env.local' >> .gitignore -chmod 600 agentcore/.env.local +agentcore add credentials payment \ + --name CoinbaseCredentials --provider CoinbaseCDP \ + --api-key-id \ + --api-key-secret file:// \ + --wallet-secret file:// +agentcore add payment-connector \ + --manager MyPayments --name Coinbase --credential CoinbaseCredentials ``` -The Python scaffold's default `.gitignore` lists only `.env`, so `.env.local` -must be added. **The agent must never read this file.** +For Stripe Privy, the credential command uses `--provider StripePrivy`, `--app-id`, `--app-secret file://`, `--authorization-private-key file://`, and `--authorization-id`. Then add the connector with `--credential `. The provider secrets are stored in `agentcore/.env.local` until deploy sends them to AgentCore Identity. Check that `.env.local` is ignored by git and readable only by the human operator. Do not paste secret values or file contents into an agent conversation. -## 4. Deploy +## 4. Build, deploy, and discover ```bash -agentcore deploy +agentcore build +agentcore deploy --region +agentcore status --json ``` -Provisions the manager and connector, plus a -`PaymentProcessPaymentRole`. IDs land in -`agentcore/.cli/deployed-state.json` under -`targets..resources.payments[]` (`managerArn`, -`connectors[].connectorId`, `processPaymentRoleArn`). +The first deploy creates a `default` target in `agentcore/aws-targets.json` from the effective account and region. Check that it uses the intended account and region. For Quick Create, complete the Coinbase authorization URL reported at deploy and confirm the connector is ready. The v1 `agentcore/.cli/deployed-state.json` records the stack binding but does not store payment manager and connector IDs. Read them from `agentcore status --json`; `scripts/agents_pay_admin.py` now uses that output for automatic discovery. If discovery is unavailable, pass `--manager-arn` and `--connector-id` explicitly. ## 5. IAM: the four-role model @@ -266,7 +232,7 @@ invent or keep in sync: python3 scripts/agents_pay_admin.py create-instrument --email you@example.com ``` -The manager ARN and connector ID are read from `agentcore/.cli/deployed-state.json` +The manager ARN and connector ID are read from `agentcore status --json` automatically, so run this from your AgentCore project directory. Otherwise pass `--manager-arn` and `--connector-id` explicitly. diff --git a/plugins/aws-agents/skills/agents-pay/references/troubleshooting.md b/plugins/aws-agents/skills/agents-pay/references/troubleshooting.md index 004c3e7bf..e76613218 100644 --- a/plugins/aws-agents/skills/agents-pay/references/troubleshooting.md +++ b/plugins/aws-agents/skills/agents-pay/references/troubleshooting.md @@ -130,7 +130,7 @@ python3 scripts/agents_pay_admin.py preflight ``` Manager ARN and connector ID come from -`agentcore/.cli/deployed-state.json`; the session ID from `new-session`. +`agentcore status --json`; the session ID from `new-session`. ### "bedrock-agentcore with payments support is not installed" diff --git a/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py b/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py index 852c08142..ab0b8839c 100755 --- a/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py +++ b/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py @@ -27,9 +27,8 @@ new-session Create a budget-bounded session (ManagementRole, TTY approval) preflight Verify wiring and confirm no secrets are exposed -Secrets are NEVER accepted as arguments here either. Provider credentials go to -the AgentCore CLI's own interactive wizard (`agentcore add payment-connector`), -which keeps them out of shell history and out of this process. +Secrets are NEVER accepted as arguments here either. Provider credentials go +through `agentcore add credentials payment`, using file or stdin secret inputs. """ from __future__ import annotations @@ -39,6 +38,7 @@ import os import secrets import stat +import subprocess import sys import tempfile from decimal import Decimal, InvalidOperation @@ -112,7 +112,7 @@ def _find_deployed_state(project_dir: str | Path | None = None) -> Path | None: def discover_deployed(project_dir: str | Path | None = None) -> dict[str, str | None]: - """Best-effort read of manager ARN / connector ID from the CLI's deploy record. + """Best-effort read of manager ARN / connector ID from the CLI project. Returns a dict with possibly-None values; callers fall back to flags or env. Purely a convenience: nothing security-relevant is decided from this file, and @@ -121,14 +121,13 @@ def discover_deployed(project_dir: str | Path | None = None) -> dict[str, str | Searches several relative paths so the command works whether you run from the project root, inside the agentcore/ directory, or a subdirectory of it. - CLI 0.20.x writes targets..resources.payments[]; 0.26.x writes - payments as objects keyed by name. Older layouts used a top-level payments[]. - All three are handled. + CLI v1 reports payment identifiers through `agentcore status --json`. + The deployed state layouts from v0 are retained as a fallback. """ out: dict[str, str | None] = {"manager_arn": None, "connector_id": None, "role_arn": None} state_path = _find_deployed_state(project_dir) if state_path is None: - return out + return _discover_v1_status(project_dir, None) try: data = json.loads(state_path.read_text()) payments = None @@ -139,7 +138,7 @@ def discover_deployed(project_dir: str | Path | None = None) -> dict[str, str | if not payments: payments = data.get("payments") if not payments: - return out + return _discover_v1_status(project_dir, state_path) # 0.26.x: payments is a dict keyed by name if isinstance(payments, dict): pay = next(iter(payments.values()), {}) @@ -147,7 +146,7 @@ def discover_deployed(project_dir: str | Path | None = None) -> dict[str, str | elif isinstance(payments, list): pay = payments[0] if payments else {} else: - return out + return _discover_v1_status(project_dir, state_path) connectors = pay.get("connectors") or [] out["manager_arn"] = pay.get("managerArn") # 0.26.x: connectors may be a dict keyed by name @@ -160,6 +159,45 @@ def discover_deployed(project_dir: str | Path | None = None) -> dict[str, str | out["connector_id"] = first_connector.get("connectorId") if first_connector else None out["role_arn"] = pay.get("processPaymentRoleArn") except Exception: # noqa: BLE001 - convenience only; never fatal + return _discover_v1_status(project_dir, state_path) + return out + + +def _discover_v1_status( + project_dir: str | Path | None, state_path: Path | None +) -> dict[str, str | None]: + """Resolve v1 stack outputs via the CLI's project status JSON.""" + out: dict[str, str | None] = {"manager_arn": None, "connector_id": None, "role_arn": None} + start = state_path.resolve() if state_path else Path( + project_dir or os.environ.get("AGENTCORE_PROJECT_DIR") or Path.cwd() + ).expanduser().resolve() + root = next( + (path for path in (start, *start.parents) if (path / "agentcore" / "agentcore.json").exists()), + None, + ) + if root is None: + return out + try: + result = subprocess.run( + ["agentcore", "status", "--json"], + cwd=root, + capture_output=True, + text=True, + timeout=30, + check=False, + ) + if result.returncode != 0: + return out + status = json.loads(result.stdout) + for resource in status.get("resources", []): + if resource.get("resourceType") == "payment-manager" and resource.get("arn"): + out["manager_arn"] = resource["arn"] + for child in resource.get("children", []): + if child.get("resourceType") == "payment-connector" and child.get("id"): + out["connector_id"] = child["id"] + break + break + except (OSError, ValueError, subprocess.TimeoutExpired): pass return out @@ -446,8 +484,8 @@ def cmd_create_instrument(args: argparse.Namespace) -> int: if not manager_arn or not connector_id: checked = ", ".join(str(p) for p in deployed_state_candidates()) print( - f"Could not find deployed-state.json (checked: {checked}).\n\n" - "This file is created by `agentcore deploy`. To resolve:\n" + f"Could not discover payment resources from the current project (checked legacy state: {checked}).\n\n" + "Deploy the v1 project, then check `agentcore status --json`. To resolve:\n" " \u2022 Run this command from the directory that CONTAINS the agentcore/ folder, OR\n" " \u2022 Run from inside the agentcore/ directory itself, OR\n" " \u2022 Pass --manager-arn and --connector-id explicitly.", @@ -555,8 +593,8 @@ def cmd_new_session(args: argparse.Namespace) -> int: checked = ", ".join(str(p) for p in deployed_state_candidates()) print( f"Could not determine the payment manager ARN.\n\n" - f"Searched for deployed-state.json at: {checked}\n\n" - "This file is created by `agentcore deploy`. To resolve:\n" + f"Checked legacy state paths: {checked}\n\n" + "Deploy the v1 project, then check `agentcore status --json`. To resolve:\n" " \u2022 Run this command from the directory that CONTAINS the agentcore/ folder, OR\n" " \u2022 Run from inside the agentcore/ directory itself, OR\n" " \u2022 Pass --manager-arn explicitly, OR\n" @@ -842,7 +880,7 @@ def cmd_setup_openclaw(args: argparse.Namespace) -> int: if not manager_arn: checked = ", ".join(str(p) for p in deployed_state_candidates(project_dir)) print( - "\nCould not auto-discover payment manager ARN from deployed-state.json.\n" + "\nCould not auto-discover payment manager ARN from `agentcore status --json`.\n" f"Checked: {checked}" ) manager_arn = _prompt("Payment Manager ARN", "") @@ -985,18 +1023,15 @@ def cmd_preflight(args: argparse.Namespace) -> int: for var in required: print(f"[{'ok' if os.environ.get(var) else '--'}] {var}{'' if os.environ.get(var) else ' not set'}") - # If the manager ARN is missing but discoverable, hand the operator the exact - # line to run rather than making them dig it out of deployed-state.json. + # If the manager ARN is missing but discoverable, show the export command. if not os.environ.get("PAYMENT_MANAGER_ARN"): discovered = discover_deployed() if discovered["manager_arn"]: - state_path = _find_deployed_state() - print(f"\n Found in {state_path}: run this to fix the above ->") + print("\n Found in project status: run this to fix the above ->") print(f" export PAYMENT_MANAGER_ARN={discovered['manager_arn']}") else: print( - "\n deployed-state.json not found " - f"(checked: {', '.join(str(p) for p in deployed_state_candidates())})." + "\n Payment resources were not found in project status or legacy deploy state." ) print(" Run from the directory that CONTAINS the agentcore/ folder,") print(" from inside it, or set the variables by hand.") @@ -1093,7 +1128,7 @@ def main() -> int: "--project-dir", default=None, help=( - "AgentCore project directory used to locate deployed-state.json " + "AgentCore project directory used for `agentcore status --json` " "(or set AGENTCORE_PROJECT_DIR)" ), ) diff --git a/plugins/aws-agents/skills/agents-pay/scripts/test_resource_resolution.py b/plugins/aws-agents/skills/agents-pay/scripts/test_resource_resolution.py index 3b94ff24f..c370152e5 100644 --- a/plugins/aws-agents/skills/agents-pay/scripts/test_resource_resolution.py +++ b/plugins/aws-agents/skills/agents-pay/scripts/test_resource_resolution.py @@ -13,6 +13,7 @@ import tempfile import os from pathlib import Path +from unittest.mock import patch sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import agents_pay_admin as m @@ -97,4 +98,55 @@ def _write_config(name: str, resources: dict) -> Path: print("resolve_manager_arn: PASSED") +# --- CLI v1 project status ------------------------------------------------ + +v1_root = Path(d) / "v1-project" +(v1_root / "agentcore").mkdir(parents=True) +(v1_root / "agentcore" / "agentcore.json").write_text('{"name":"PayTest","version":2}') +v1_status = { + "projectName": "PayTest", + "target": "default", + "region": "us-east-1", + "resources": [ + { + "resourceType": "payment-manager", + "name": "Payments", + "deploymentState": "deployed", + "arn": "arn:aws:bedrock-agentcore:us-east-1:111111111111:payment-manager/manager-1", + "children": [ + { + "resourceType": "payment-connector", + "name": "Coinbase", + "deploymentState": "deployed", + "id": "connector-1", + } + ], + } + ], +} +with patch.object(m.subprocess, "run") as run: + run.return_value.returncode = 0 + run.return_value.stdout = json.dumps(v1_status) + found = m.discover_deployed(v1_root) + assert found["manager_arn"] == v1_status["resources"][0]["arn"] + assert found["connector_id"] == "connector-1" + run.assert_called_once() + assert run.call_args.kwargs["cwd"] == v1_root.resolve() + +print("discover_deployed v1: PASSED") + +# v1 deployed state exists, but has no payments branch. Discovery must still +# read status from the project root. +(v1_root / "agentcore" / ".cli").mkdir() +(v1_root / "agentcore" / ".cli" / "deployed-state.json").write_text('{"version":2}') +with patch.object(m.subprocess, "run") as run: + run.return_value.returncode = 0 + run.return_value.stdout = json.dumps(v1_status) + found = m.discover_deployed(v1_root) + assert found["manager_arn"] == v1_status["resources"][0]["arn"] + assert found["connector_id"] == "connector-1" + assert run.call_args.kwargs["cwd"] == v1_root.resolve() + +print("discover_deployed v1 with state: PASSED") + print("ALL RESOURCE RESOLUTION TESTS PASSED") diff --git a/plugins/aws-core/skills/amazon-bedrock/SKILL.md b/plugins/aws-core/skills/amazon-bedrock/SKILL.md index b8eab21a3..86ecf5a0c 100644 --- a/plugins/aws-core/skills/amazon-bedrock/SKILL.md +++ b/plugins/aws-core/skills/amazon-bedrock/SKILL.md @@ -240,7 +240,7 @@ You MUST read [guardrails reference](references/guardrails.md) before responding ### Deploy an agent to AgentCore -If the user wants a managed agent loop without writing orchestration code, route to **Harness** (config-based). Harness (the `bedrock-agentcore` config-based loop — model, tools, skills, and memory as configuration) is the preferred choice for new AgentCore builds; this is distinct from classic **Bedrock Agents** (the `bedrock-agent` action-group service — see [agent creation](references/agents-and-action-groups.md)). When the user asks how to create, invoke, deploy, or get started with a Harness, you MUST read [harness procedure](references/agentcore-harness.md) and follow its Deployment Workflow step by step before responding. Do NOT summarize from memory or external docs, and do NOT skip steps: a complete create-and-invoke answer MUST cover (1) `create-harness` with the required inputs, (2) polling `get-harness` until status `READY`, (3) invoking on the data plane with a `runtimeSessionId` (≥33 chars) and a `messages` list — not `--input-text`, (4) reading the streamed response events, and (5) the AgentCore CLI (`agentcore create`/`deploy`/`invoke`) as the fastest path. The reference is authoritative over any external documentation. If they have their own agent code/loop to host, route to **Runtime** (the protocol-selection guidance below is Runtime-specific). +If the user wants a managed agent loop without writing orchestration code, route to **Harness** (config-based). Harness is distinct from classic **Bedrock Agents** (the `bedrock-agent` action-group service — see [agent creation](references/agents-and-action-groups.md)). For a CLI Harness workflow, read the [Harness procedure](references/agentcore-harness.md) and use AgentCore CLI v1.0.0 or later: `agentcore create --name --template empty`, `agentcore add harness`, `agentcore build`, `agentcore deploy`, and `agentcore invoke --harness --prompt `. For direct SDK/API creation, cover `CreateHarness`, polling `GetHarness` until `READY`, then streaming `InvokeHarness` events from the data plane. If they have their own agent code or loop to host, route to **Runtime**. Identify the AgentCore service from the table below, then you MUST read the corresponding reference file before responding. Follow any procedures in the reference step by step. Do not summarize — execute. diff --git a/plugins/aws-core/skills/amazon-bedrock/references/agentcore-harness.md b/plugins/aws-core/skills/amazon-bedrock/references/agentcore-harness.md index 88286dfa4..9671e1e63 100644 --- a/plugins/aws-core/skills/amazon-bedrock/references/agentcore-harness.md +++ b/plugins/aws-core/skills/amazon-bedrock/references/agentcore-harness.md @@ -34,9 +34,9 @@ Both run inside AgentCore Runtime infrastructure, but they solve different probl | | Harness | Runtime | |---|---------|---------| -| **You provide** | Configuration (no code) | Agent code + ARM64 container | +| **You provide** | Configuration (no orchestration code) | Agent code, packaged as CodeZip or a container | | **Agent loop** | Managed (Strands) | You write it | -| **Change model / add a tool** | Config change, no redeploy | Code change + redeploy | +| **Change model / add a tool** | Edit Harness config, then deploy the project | Change code or config, then deploy the project | | **Framework choice** | Strands only | Any (LangGraph, CrewAI, custom) | | **Best for** | Fast setup, dynamic config | Custom loop control, non-loop patterns (graph/workflow), bidirectional streaming, hooks | @@ -70,19 +70,24 @@ Deployment Progress (all three stages are required — creation alone does not y - Skipping Step 2 — a harness is not invokable until `get-harness` reports `READY`. - Invoking with `--input-text` or `--harness-id` — there is no such parameter. Invocation is on the **data plane** (`bedrock-agentcore`), takes a `runtimeSessionId` (≥33 chars) and a `messages` list, and returns a **stream** of events you must iterate (see Streaming Response Format). Treating the response as a single string drops the agent's output. -**AgentCore CLI:** +**AgentCore CLI v1:** ```bash -npm install -g @aws/agentcore@preview -# Set the execution-limit guardrails (max iterations, max tokens, timeout) explicitly -# rather than relying on defaults — see Security Considerations. Use `agentcore create --help` -# for the current flag names, or set them on the underlying create-harness call below. -agentcore create --name myresearchagent --model-provider bedrock -agentcore deploy -agentcore invoke --harness myresearchagent --session-id "$(uuidgen)" "Hello, what can you do?" +npm install -g @aws/agentcore@latest +agentcore create --name ResearchProject --template empty +cd ResearchProject +agentcore add harness \ + --name ResearchHarness \ + --model '{"provider":"bedrock","modelId":"global.anthropic.claude-sonnet-5-5"}' \ + --authorizer-type AWS_IAM \ + --max-iterations 25 --max-tokens 4096 --timeout-seconds 300 +agentcore build +agentcore deploy --region +agentcore status --json +agentcore invoke --harness ResearchHarness --prompt "Hello, what can you do?" ``` -Useful CLI commands: `agentcore dev` (local dev server + inspector), `agentcore status`, `agentcore add harness`. +The editable Harness files are `app/ResearchHarness/harness.yaml` and `system-prompt.md`. The first deploy creates the default target in `agentcore/aws-targets.json` from the effective account and region. `agentcore dev` serves project Runtimes locally; it is not the Harness create step. **AWS CLI / SDK:** @@ -125,7 +130,7 @@ for event in response["stream"]: - `harnessName` must start with a letter and contain only letters, digits, and underscores, max 40 characters. - `runtimeSessionId` MUST be at least 33 characters — a standard UUID (36 chars, with hyphens) satisfies this. If your `uuidgen` strips hyphens (32 chars), it will be too short; append a suffix or concatenate two. Over the wire it maps to the `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header. Reuse the same session id across invocations to continue the conversation in the same environment. - When no model is configured the harness applies a default Bedrock model; check the [CreateHarness API reference](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateHarness.html) for the current default, and `aws bedrock list-foundation-models` for available model IDs. -- Install the AgentCore CLI from the `@aws/agentcore@preview` npm channel (`npm install -g @aws/agentcore@preview`). +- Install AgentCore CLI v1.0.0 or later with `npm install -g @aws/agentcore@latest`. - Refer to the latest AWS documentation for authoritative API parameters. ## Configuration Surface @@ -251,7 +256,7 @@ The imperative shell operation `InvokeAgentRuntimeCommand` (`POST /runtimes/ --allowed-clients `. (Do not use `oidcAuthorizerConfiguration` — that name appears in some examples but is not the API field.) +- OAuth JWT config is `authorizerConfiguration.customJWTAuthorizer` with `discoveryUrl` (required), `allowedAudience`, and `allowedClients`. With the v1 project CLI, use `agentcore add harness --authorizer-type CUSTOM_JWT --authorizer-configuration ''` or edit `harness.yaml` and run `agentcore build`. (Do not use `oidcAuthorizerConfiguration` — that is not the API field.) - Set `allowedAudience` and/or `allowedClients` to constrain which tokens are accepted: `allowedAudience` validates the JWT `aud` claim and `allowedClients` validates the client ID, so a token issued for a different service or client cannot be replayed against the harness. A JWT authorizer with neither constraint accepts any valid token from the issuer. **Network and container:** diff --git a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/cli.md b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/cli.md index 855e140bd..2cb52fa9a 100644 --- a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/cli.md +++ b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/cli.md @@ -1,36 +1,22 @@ -# AgentCore CLI +# AgentCore CLI v1 for the Harness migration -The migration tool is the AgentCore CLI, package **`@aws/agentcore`** — use the **latest** version. Its command/flag surface shifts between releases, so **verify it live** rather than trusting a hardcoded flag table. - -The AgentCore CLI (`@aws/agentcore`) is **not** the same as the `bedrock-agentcore-starter-toolkit`. The starter toolkit is deprecated and not recommended — do not use it or its commands for this migration. Everything here uses `@aws/agentcore`. - -## Authoritative, always-current sources - -- Installed surface: `agentcore --help`, then `agentcore --help` for each command about to be used. -- Per-project schema the CLI ships: `https://schema.agentcore.aws.dev/v1/agentcore.json` inside any scaffolded project (authoritative shape for `agentcore.json`, harness, gateway, target, tool-schema). Read before hand-editing config. -- Published / latest version: `npm view @aws/agentcore version`. -- Package page: https://www.npmjs.com/package/@aws/agentcore - -## Phase 0 checks +Use `@aws/agentcore` v1.0.0 or later. This is separate from the Bedrock AgentCore Starter Toolkit and from the AWS CLI. Check the installed commands before acting: ```bash agentcore --version -npm view @aws/agentcore version # newer release available? -python3 -c "import boto3; print(boto3.__version__)" # discovery path probe (see discovery.md) -``` - -Then probe the commands the migration actually calls and confirm the flags/values each needs are present: - -```bash agentcore create --help +agentcore add harness --help agentcore add gateway --help agentcore add gateway-target --help -agentcore add harness --help -agentcore add tool --help +agentcore add gateway-connector --help +agentcore build --help agentcore deploy --help +agentcore status --help +agentcore invoke --help ``` -If a required flag is **absent**, stop — don't generate commands against a surface that no longer exists. Update the CLI (`npm install -g @aws/agentcore@latest`) and re-probe, or update this skill if the references assume a flag the CLI renamed. +A v1 project has `"version": 2` in `agentcore/agentcore.json`. `agentcore/aws-targets.json` is empty until the first deploy creates the default target from the effective AWS account and region. The Harness's own editable config is `app//harness.yaml`, with its prompt in `system-prompt.md`. `agentcore add harness --model` takes a JSON object such as `{"provider":"bedrock","modelId":""}`; the written YAML uses `model.bedrockModelConfig.modelId`. For direct YAML edits, follow the scaffolded field names. `agentcore build` validates and packages the project. + +`agentcore add runtime --agent-id ... --agent-alias-id ...` translates a classic Bedrock Agent into a code Runtime. It is a separate route from this Harness migration. `agentcore migrate` converts an AgentCore CLI v0 project to v1; it does not import a classic Bedrock Agent. -## Never reverse-engineer the CLI bundle -Do **not** read the CLI's minified source (`node_modules/@aws/agentcore/dist/**`) — internal names and wizard-only code paths produce confident-but-wrong conclusions. If `--help`, `.llm-context`, the hosted schema, and these references don't answer it, stop and ask the user. +Do not infer flags from v0 guides. The v1 CLI has no `add tool`, `validate`, `fetch access`, or `project` command group. Check command help and the v1 source for any option this migration needs before issuing the command. diff --git a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md index 15aab0a18..07c268e22 100644 --- a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md +++ b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md @@ -1,116 +1,96 @@ -# Deploy +# Deploy a migrated Bedrock Agent as a v1 Harness -Deploy into the **source agent's region** (mirror). If any step fails, surface the error and stop — **fail loudly**, never silently work around a failure. +Use the approved discovery and mapping plan. Deploy in the source agent's region and preserve its inbound access posture. Do not change or delete the source Bedrock Agent as part of this migration. -## CRITICAL: set the deploy-target region right after scaffold (region trap) -`agentcore create` with no resolved region **silently writes a default region** into `agentcore/aws-targets.json` that is usually not the source agent's. Immediately after scaffold, before any `add`/`deploy`: **edit `agentcore/aws-targets.json` so the deploy-target region is the source agent's region**, and verify it before the first deploy. A wrong region is wrong on two counts: the shims invoke the **source** Lambdas / KB **by ARN**, so a harness elsewhere can't reach them; and a deploy can hit region-specific Harness CFN-type issues that surface as a stack rollback (read the CloudFormation failure; if region-specific, redeploy in the source region). - -If a deploy already landed in the wrong region: `aws cloudformation delete-stack` the wrong-region stack, reset `agentcore/.cli/deployed-state.json` to `{"targets":{}}`, fix `aws-targets.json`, then redeploy. - -## Source-side prerequisites — the builder grants these, never the migration (INV-1) - -The AG shim invokes the **original** source Lambda by ARN, so the source Lambda's resource policy must allow the shim's execution role to call it. **This is the builder's job, not the migration's** — the skill must never mutate source-side infrastructure to unblock itself. - -Expect the **first shim invocation to fail with `AccessDeniedException`** until the permission exists. When it does: **stop and hand the builder this command; do NOT run `aws lambda add-permission` on the source yourself** (that mutates the source and breaks INV-1): +## Scaffold and configure ```bash -aws lambda add-permission --function-name \ - --statement-id agentcore-shim-invoke --action lambda:InvokeFunction \ - --principal --source-arn +agentcore create --name --template empty +cd +agentcore add harness \ + --name \ + --model '{"provider":"bedrock","modelId":""}' \ + --authorizer-type AWS_IAM \ + --max-iterations 25 --max-tokens 4096 --timeout-seconds 300 +agentcore add gateway --name --authorizer-type AWS_IAM ``` -(`bedrock-agent-runtime:Retrieve` for the KB shim is granted on the shim role via its `iamPolicy`, so the KB path needs no source-side change — only the AG-shim → original-Lambda invoke does.) - -## Two-phase deploy - -A gateway tool can only attach once the gateway and its targets are *deployed* (`add tool --type agentcore_gateway` reads the gateway's deployed tool list). So deploy twice: - -1. **Scaffold:** `agentcore create` (plain — not the `--type import`/`agentcore import` path, which builds a *code* project, not a Harness). Then `cd` in and run **every** later `add`/`deploy`/`status` from that one directory — those commands resolve the project from the cwd, so a second project or a different dir yields "No agentcore project found". -2. **Add** infra that doesn't need a deployed gateway: - - `agentcore add gateway`, then hand-add one **`targetType: "lambda"` code target** to `agentcore.json` per action group / KB shim (see "How shims are deployed"). - - `agentcore add harness` (one command, all flags): `--model-id` (= source `foundationModel`), `--temperature`/`--top-p`/`--model-max-tokens` (only one of temperature/top-p when the model rejects both), `--system-prompt` (folded instruction + non-DEFAULT override intent, per [mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)). Managed memory stays on. `--additional-params` is `lite_llm`-provider only, so a bedrock harness rejects it — which is why the source guardrail is classified **cannot migrate** (verify the current CLI surface per [mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)'s guardrail section before relying on this). - - if the source had CodeInterpreter: `agentcore add tool --harness --type agentcore_code_interpreter --name `. -3. **First deploy:** `agentcore deploy` — creates gateway, targets, harness, memory. -4. **Attach the gateway tool** (gateway now deployed): `agentcore add tool --harness --type agentcore_gateway --name --gateway --outbound-auth awsIam`. `--gateway ` resolves the deployed ARN automatically. **Add it exactly once** — check `harness.json`/`agentcore status` first; a duplicate `agentcore_gateway` tool deploys fine but breaks at runtime (`Tool name '…' already exists`), silently disabling every gateway-backed tool. If already doubled, remove the extra from `harness.json` and redeploy. -5. **Second deploy:** `agentcore deploy` — applies the gateway tool. The migration isn't complete until this succeeds and `agentcore status` shows the harness at a new version with the tool. - -## Inbound auth — match the source's invocation posture, never loosen it (INV-2/INV-3) - -`--outbound-auth awsIam` above governs how the **harness calls the gateway**. It says nothing about **who may invoke the migrated harness/gateway** — that is *inbound* auth, and it is a separate, mandatory decision. The source Bedrock Agent is IAM-gated: only principals with `bedrock-agent-runtime:InvokeAgent` on that agent's ARN can invoke it. The migrated harness must be **no more reachable than that**. - -- **Discover the source posture** (Phase 2): which principals hold `bedrock-agent-runtime:InvokeAgent` on the source, and any resource-based policy on the agent. This is the bar to match. -- **Configure inbound auth explicitly** on `add harness`/`add gateway` — check `agentcore add harness --help` and `add gateway --help` for the inbound-auth flag (SigV4 with an allowed-role list, JWT with a verified issuer, etc.) and set it to match the discovered posture. Do **not** rely on the CLI default. -- **If the CLI's inbound default is unauthenticated or broader than the source** (or you cannot determine it), **hard-stop** and have the builder confirm the intended posture before deploying — a migrated agent invokable by parties who couldn't reach the source violates both secure-by-default and preserve-posture. - -## How shims are deployed — hand-author the code target, CLI builds it at deploy - -`agentcore add gateway-target` **cannot create a code target non-interactively** (`--type lambda` is rejected). So this is one of the guide's explicitly sanctioned hand-edits (see "Config edits — the rule"): add the target to `agentcore.json` yourself, then `agentcore deploy` builds the Lambda from your source. Per shim: +Adjust invocation limits to the approved plan. A Bedrock Agent's source `foundationModel` may need an inference profile in the target region. Read `app//harness.yaml`; it uses `model.bedrockModelConfig.modelId`, not the JSON input shape used by `--model`. Put the mapped instruction in `app//system-prompt.md`. If the source idle session TTL matters, set the `lifecycleConfig.idleRuntimeSessionTimeout` field in `harness.yaml` to the mapped value. Keep managed memory configured unless the assessment calls for a different mode. -1. Place rendered shim at `tools//handler.py` (from `{kb_shim,lambda_shim}.py.tmpl` in `assets/`) with a `pyproject.toml` beside it. -2. Add one entry to `agentCoreGateways[].targets[]` in `agentcore.json`: +For Code Interpreter, add one tool to the YAML `tools` list: - ```json - { - "name": "", - "targetType": "lambda", - "toolDefinitions": [ /* one per function/operation; mirror source schema exactly */ ], - "compute": { - "host": "Lambda", - "implementation": { "language": "Python", "path": "tools/", "handler": "handler.lambda_handler" }, - "pythonVersion": "", - "timeout": 30, - "iamPolicy": { /* full policy document, least-privilege — see below */ } - } - } - ``` - -3. Run `agentcore validate` (must print `Valid`), then `agentcore deploy`. - -**No `environment` key — all shim config is baked in at render time.** The `compute` schema is strict and has **no** environment-variable support; adding an `environment` key fails `agentcore validate`. Every `{{TOKEN}}` in the shim templates (`ORIGINAL_LAMBDA_ARN`, `SCHEMA_STYLE`, `OP_ROUTES`, `KB_ID`, …) is substituted directly into `tools//handler.py` as a literal. An unsubstituted token causes a `SyntaxError` or wrong behavior at runtime — the post-render grep in [mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md) is the gate. - -**Scope `iamPolicy` to a single resource ARN — never a wildcard.** It is a full policy document (`Version` + `Statement` array are required), granting exactly one action on exactly one resource: +```yaml +tools: + - type: agentcore_code_interpreter + name: code_interpreter + config: + agentCoreCodeInterpreter: {} +``` -- AG shim — invoke only the original Lambda: +The v1 CLI has no `add tool` command. Do not duplicate the YAML `tools` key. Run `agentcore build` after each configuration edit. + +## Add Gateway targets + +An action-group shim is a new Lambda backed Gateway target. Render the source action group into `tools//handler.py` using the templates in this skill's `assets/`, with one `toolDefinitions` entry per source function or OpenAPI operation. Store the complete target object in a JSON file and add it through the CLI: + +```json +{ + "name": "ActionGroupTools", + "targetType": "lambda", + "toolDefinitions": [ + {"name":"example_tool","description":"Describe the mapped operation","inputSchema":{"type":"object","properties":{}}} + ], + "compute": { + "host": "Lambda", + "implementation": {"language":"Python","path":"tools/action-group","handler":"handler.lambda_handler"}, + "pythonVersion": "PYTHON_3_12", + "timeout": 30, + "iamPolicy": {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"lambda:InvokeFunction","Resource":""}]} + } +} +``` - ```json - { "Version": "2012-10-17", "Statement": [ - { "Effect": "Allow", "Action": "lambda:InvokeFunction", - "Resource": "arn:aws:lambda:::function:" } ] } - ``` +```bash +agentcore add gateway-target --gateway --target-configuration file://action-group-target.json +``` -- KB shim — retrieve only from the source KB: +Use a scoped `bedrock-agent-runtime:Retrieve` policy on the source Knowledge Base ARN for a KB shim. For a Knowledge Base with default retrieval behavior that the managed connector supports, use `agentcore add gateway-connector --gateway --name --connector bedrock-knowledge-bases --knowledge-base ` instead. `lambdaFunctionArn` targets point at an existing Lambda; use `lambda` with `compute` for a shim the project builds. Do not put secret values or unsupported `environment` keys into target configuration. Run `agentcore build` to catch schema errors. - ```json - { "Version": "2012-10-17", "Statement": [ - { "Effect": "Allow", "Action": "bedrock-agent-runtime:Retrieve", - "Resource": "arn:aws:bedrock:::knowledge-base/" } ] } - ``` +## First deploy and Gateway ARN -**Logging & monitoring.** Give the execution role `logs:CreateLogGroup`, `logs:CreateLogStream`, `logs:PutLogEvents` scoped to the function's log group (`arn:aws:logs:::log-group:/aws/lambda/:*`); recommend a CloudWatch alarm on the shim's `Errors`/`Throttles` metrics; and confirm CloudTrail captures `lambda:Invoke` for audit. The shims handle tool arguments (AG shim) and retrieval text (KB shim), which may be sensitive: **encrypt the log group with a KMS key** (`aws logs associate-kms-key --log-group-name /aws/lambda/ --kms-key-id `) and do **not** log full request/response payloads. +```bash +agentcore build +agentcore deploy --region +agentcore status --json +``` -**Throttling & blast radius.** Set **reserved concurrency** on each shim Lambda (`aws lambda put-function-concurrency`) so a runaway caller can't exhaust account-wide Lambda concurrency, and enable request throttling on the Gateway if the CLI/service exposes it. +The first deploy creates the default target in `agentcore/aws-targets.json` using the effective account and the explicit source region. Confirm both before proceeding. Find the deployed Gateway ARN in `agentcore status --json`. Record it in the approved migration plan. If deployment fails, inspect the CloudFormation failure and fix the project configuration; do not reset all deployed state or delete a stack by guesswork. -**Non-negotiable details (these fail `agentcore validate` if wrong):** +## Attach the Gateway to the Harness -- The `compute` block is **strict** — unknown keys (e.g. `environment`) are rejected, and a Python Lambda **must** set `pythonVersion`. -- `pythonVersion` is an enum (e.g. `PYTHON_3_12`), **not** a bare `"3.12"` — check the project's `agentcore.json` schema (or `agentcore validate` feedback) for the currently valid values. Prefer the source Lambda's runtime. -- `implementation` requires all three of `language`, `path`, `handler` and nothing else. -- `targetType` is the literal string `"lambda"` here (valid in the JSON schema, even though `--type lambda` is rejected on the CLI). -- `path` is relative to the project root (parent of `agentcore/`). -- `handler` is `.` = `handler.lambda_handler`. +Edit the existing `tools` list in `app//harness.yaml` once: -Do **not** use `--type lambda-function-arn` — that wires a *pre-existing* Lambda by ARN, not a shim this skill builds. +```yaml +tools: + - type: agentcore_gateway + name: migrated_tools + config: + agentCoreGateway: + gatewayArn: arn:aws:bedrock-agentcore:::gateway/ + outboundAuth: + awsIam: {} +``` -## Config edits — the rule -Hand-edit a config file **only where this guide explicitly says to** — the region correction in `aws-targets.json`, the `targetType: "lambda"` code target in `agentcore.json`, the documented recovery edits (removing a duplicate `agentcore_gateway` tool from `harness.json`; resetting `deployed-state.json` for a wrong-region redeploy). Everything else goes through `agentcore` commands — harness and tools via `add harness`/`add tool`, gateways and other targets via `add gateway`/`add gateway-target`. Do not invent new hand-edits: when an `add` command fails, fix its flags (missing `--name`, wrong `--type`) rather than editing `harness.json`/`agentcore.json` to route around the failure. +The Gateway ARN exists only after the first deploy. The `outboundAuth` block governs the Harness to Gateway call. The Gateway's `authorizerType` and Harness's `authorizerType` govern inbound access. A classic Bedrock Agent is IAM gated; explicitly preserve at least that posture and confirm the caller roles have only the necessary new permissions. The v1 Gateway default is `NONE`, so do not omit `--authorizer-type AWS_IAM` during creation. The Harness tool name must be unique. -## One action group = one target, with all its functions -A Bedrock action group can expose several functions/operations (up to three), each with its own schema. The tool-schema file is a **`ToolDefinition[]` array**, so a single Lambda target carries every function in that action group — one array entry per function/operationId. Do not split an action group into multiple targets. [tool_schema.json.tmpl](assets/tool_schema.json.tmpl) is already an array; add one entry per function, mirroring the source schema exactly ([mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)). +```bash +agentcore build +agentcore deploy +agentcore status --json +``` -## Verification -The migration is done when `agentcore deploy` reports success. Before treating it as complete, **prompt the user** to confirm the deploy succeeded and they're satisfied — surface the deployed harness/gateway ARNs from `agentcore status`. Deeper parity (invoking the harness, comparing against the source) is out of scope unless the user asks. +Confirm the second deploy succeeds and the Harness and Gateway are deployed. An optional parity check uses `agentcore invoke --harness --prompt "Hello"`; compare the output with the source agent before traffic cutover. -## Rendering templates into CLI inputs +## Source permissions and verification -- KB shim / AG shim Lambda code: adapt the `*.py.tmpl` files in `assets/` (rendering rules in [mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)) and place the rendered handler at `tools//handler.py` — see "How shims are deployed" above. -- Tool-schema files: one array per target, one entry per source function/operation, mirroring the source schema exactly ([mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)). +The shim's role must be allowed to invoke the source Lambda ARN. If a source Lambda policy change is required, hand the exact proposed change to the source owner; keep source-side changes outside the migration automation. Give the shim role only the Lambda or KB permission it needs and verify its CloudWatch log access. Alarm on Lambda errors and throttles, and avoid logging full tool arguments or retrieval text. Do not claim successful migration until `agentcore build` and both deploys complete and the target status reports deployed resources. diff --git a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md index 2f5e53833..a05b013f5 100644 --- a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md +++ b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md @@ -12,7 +12,7 @@ A Bedrock action-group Lambda speaks the Bedrock event envelope; AgentCore Gatew **OpenAPI action groups: pass the route TEMPLATE verbatim.** The original Lambda dispatches by matching `apiPath` against the literal route template (`/customer/{customer_id}`), with path-param values delivered separately in `parameters`. Substituting a value into the path (`/customer/tkashina`) matches no template, so the original falls through to its unhandled-op branch. Render the shim's `_OP_ROUTES` table (mapping each operationId to its `{method, apiPath-template}`) from the source OpenAPI schema with placeholders intact, and keep values in `parameters`. -Each action group becomes one Gateway **`targetType: "lambda"` code target**, hand-added to `agentcore.json` (the CLI can't create it non-interactively), with `toolDefinitions` holding **one entry per function/operation**. `agentcore deploy` then builds the shim Lambda. Exact block + gotchas in [deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md); follow it. +Each action group becomes one Gateway **`targetType: "lambda"` code target**, added with `agentcore add gateway-target --target-configuration file://.json`, with `toolDefinitions` holding one entry per function or operation. `agentcore deploy` builds the shim Lambda. Follow [deploy.md](deploy.md). ### Tool schemas — mirror exactly Reproduce the source action group's schema faithfully into the tool-schema file ([tool_schema.json.tmpl](assets/tool_schema.json.tmpl)): same tool names, parameter names, types, and descriptions. Do **not** rewrite or "improve" them — fidelity to the source agent's behavior is the goal, and a renamed tool or tightened type changes how the model selects it. @@ -23,7 +23,7 @@ Reproduce the source action group's schema faithfully into the tool-schema file - **Any non-`MANAGED` type (`VECTOR`, `KENDRA`, `SQL`)** → **always the KB shim.** The connector cannot accept these at all, so type alone decides and retrieval config is irrelevant. Use the **KB shim** ([kb_shim.py.tmpl](assets/kb_shim.py.tmpl)): a hand-added **`targetType: "lambda"` code target** (see [deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md)) that calls `bedrock-agent-runtime:Retrieve` and returns MCP-shaped passages. - **`MANAGED`** → then (and only then) check the **retrieval config on the KB association** (`knowledgeBaseConfigurations[].retrievalConfiguration`: reranker, metadata filter, hybrid/search-type override, top-k): - - **default** retrieval config → native Gateway connector: `add gateway-target --type connector --connector bedrock-knowledge-bases --knowledge-base-id `. + - **default** retrieval config → native Gateway connector: `agentcore add gateway-connector --gateway --name --connector bedrock-knowledge-bases --knowledge-base `. - **non-default** retrieval config → the **KB shim** (the connector uses a fixed retrieval contract and would silently drop the custom config), reproducing that config from the manifest's KB association. Both paths reproduce the source agent's retrieval — the choice is wiring, not fidelity, so every reachable KB is still **clean**. (An *unreachable* KB is a hard-stop — see [eligibility.md](references/bedrock-agents-to-agentcore-harness/eligibility.md). Type picks the wiring; it is never itself the blocker.) @@ -34,7 +34,7 @@ A `customControl: RETURN_CONTROL` action group has no Lambda; the original appli ## Built-in action groups - **`AMAZON.UserInput`**: no tool. Add a clarification instruction to the system prompt; the harness asks clarifying questions naturally. -- **`AMAZON.CodeInterpreter`**: use the built-in tool, `agentcore add tool --harness --type agentcore_code_interpreter --name ` (`--harness` and `--name` required — see [deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md)). +- **`AMAZON.CodeInterpreter`**: add an `agentcore_code_interpreter` tool to `app//harness.yaml`; see [deploy.md](deploy.md). The v1 CLI has no `add tool` command. ## Mapping memory to managed memory (default) @@ -45,10 +45,10 @@ Use the harness's **managed memory**, which is on by default — the harness aut Reference: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-memory.html ## Mapping model parameters to the harness model config -Set model params via `agentcore add harness` flags: `--model-id`, `--model-max-tokens`, `--temperature`, `--top-p`, `--api-format`. Some models reject setting temperature and top-p together — check the target model's inference-parameter constraints (e.g. `aws bedrock get-foundation-model`, or a probe call) and set **only one** if so. +Set model parameters in the `--model` JSON when adding the Harness, or edit `model.bedrockModelConfig` in `harness.yaml`: `modelId`, `maxTokens`, `temperature`, `topP`, and `apiFormat`. Some models reject setting temperature and topP together; set only supported parameters. ## Mapping idle session TTL (preserve the source's session-lifetime bound) -The source's `idleSessionTTLInSeconds` bounds how long an idle session is retained — a security control (it caps the credential/context leakage and replay window). Read its actual value from the discovery manifest (the source agent's own setting, whatever it is) and preserve it: if `agentcore add harness` exposes an equivalent session-TTL flag, set it to the source value. If the CLI has no equivalent, **classify it degraded in Phase 4** with the concrete delta (e.g. "source set 300s; migrated defaults to Ns") so the builder decides — never silently extend the window. +The source's `idleSessionTTLInSeconds` bounds how long an idle session is retained. Read its actual value from the discovery manifest and set `lifecycleConfig.idleRuntimeSessionTimeout` in the Harness YAML. If the source value is outside the target service's accepted range, classify the difference in the assessment before deployment. ## Guardrail — cannot migrate; surface to the user Check whether the installed CLI can attach a guardrail to a **bedrock** harness before classifying: look in `agentcore add harness --help` for a guardrail field, and note that `--additional-params` (the pass-through) is `lite_llm`-provider only, so a bedrock harness rejects it. If no guardrail path exists in the CLI surface you're running, classify the guardrail **cannot** in the assessment and tell the user the migrated harness will **not** enforce it, recording the source `guardrailIdentifier` + version. Do **not** fake it with a system-prompt mention (that enforces nothing). Enforcing it means applying `ApplyGuardrail` outside the harness — out of scope. If the CLI surface you're running exposes a guardrail field, use it and reclassify as clean. @@ -59,4 +59,4 @@ Fold the agent instruction into the harness `--system-prompt`. If `AMAZON.UserIn Prompt overrides in **DEFAULT** mode carry nothing custom — skip them. But a **non-DEFAULT override** (especially `ORCHESTRATION` or `PRE_PROCESSING`) may hold real business logic — routing rules like "use tool X for billing questions, tool Y for refunds," or input-classification the app relied on. Don't discard that as boilerplate. Read each non-DEFAULT override, separate the Bedrock-Agents scaffolding (the orchestration loop mechanics the harness now owns) from the business intent, and fold the intent into the system prompt — a modern model handles tool-routing guidance cleanly in the prompt. When unsure whether an override is boilerplate or load-bearing, surface it to the user rather than dropping it. ## Mapping the model (mirror the source) -Default `--model-id` to the source agent's `foundationModel`. If the CLI's harness default differs, set it explicitly to match the source (parity). +Set the Harness `--model` JSON `modelId` to the source agent's `foundationModel` or an approved compatible inference profile. The CLI's default model may differ. diff --git a/plugins/aws-core/skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md b/plugins/aws-core/skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md index e002b2035..776984002 100644 --- a/plugins/aws-core/skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md +++ b/plugins/aws-core/skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md @@ -79,9 +79,9 @@ Completion criterion: user approved the written plan. ### Phase 6 — Implement & deploy -Drive the CLI per the approved plan, following [bedrock-agents-to-agentcore-harness/deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md) end to end: scaffold with plain `agentcore create`, deploy the shim Lambdas, `add gateway`/`gateway-target`/`harness`, then the **two-phase deploy**. On scaffolding specifically: use `agentcore create` with no flags. Do **not** use `agentcore create --import` (or `agentcore import`) — that flag *does exist*, but it imports the Bedrock Agent into a **code project**, not a Harness, so it is the wrong route for this migration. There is **no** `agentcore init` command. Set the deploy region before the first deploy (deploy.md's region trap). Generate shim code and tool schemas by **adapting** the `.tmpl` templates in `assets/` per [bedrock-agents-to-agentcore-harness/mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md). Configure the harness/gateway **inbound auth** to match the source's invocation posture discovered in Phase 2 (deploy.md "Inbound auth") — do not accept the CLI default if it is broader. If deploy fails, surface the error — **fail loudly**, never silently work around it. In particular, if a shim invocation fails with `AccessDenied`, **do not** run `aws lambda add-permission` on the source — that mutates source-side infra; stop and hand the builder the command (deploy.md "Source-side prerequisites"). +Drive the CLI per the approved plan, following [bedrock-agents-to-agentcore-harness/deploy.md](bedrock-agents-to-agentcore-harness/deploy.md) end to end: scaffold with `agentcore create --name --template empty`, add the Harness and Gateway, add Gateway targets from complete JSON configurations, then run the two deploys. `agentcore add runtime --agent-id ...` translates a Bedrock Agent into code and is a different route. Set the source region explicitly on the first deploy. Render shim code and tool schemas from the `.tmpl` templates in `assets/` per [mapping.md](bedrock-agents-to-agentcore-harness/mapping.md). Configure Harness and Gateway inbound auth to preserve the source's invocation posture. If deploy fails, surface the error and fix the project; if shim invocation fails with `AccessDenied`, hand the source Lambda permission change to its owner. -Completion criterion: `agentcore deploy` reports success. Verification is deploy-success-only; deeper parity is out of scope. +Completion criterion: `agentcore build` and both `agentcore deploy` runs succeed, and `agentcore status --json` shows the Harness and Gateway deployed. Deeper response parity should be tested before cutting over application traffic. ## Security considerations diff --git a/skills/core-skills/amazon-bedrock/SKILL.md b/skills/core-skills/amazon-bedrock/SKILL.md index b8eab21a3..86ecf5a0c 100644 --- a/skills/core-skills/amazon-bedrock/SKILL.md +++ b/skills/core-skills/amazon-bedrock/SKILL.md @@ -240,7 +240,7 @@ You MUST read [guardrails reference](references/guardrails.md) before responding ### Deploy an agent to AgentCore -If the user wants a managed agent loop without writing orchestration code, route to **Harness** (config-based). Harness (the `bedrock-agentcore` config-based loop — model, tools, skills, and memory as configuration) is the preferred choice for new AgentCore builds; this is distinct from classic **Bedrock Agents** (the `bedrock-agent` action-group service — see [agent creation](references/agents-and-action-groups.md)). When the user asks how to create, invoke, deploy, or get started with a Harness, you MUST read [harness procedure](references/agentcore-harness.md) and follow its Deployment Workflow step by step before responding. Do NOT summarize from memory or external docs, and do NOT skip steps: a complete create-and-invoke answer MUST cover (1) `create-harness` with the required inputs, (2) polling `get-harness` until status `READY`, (3) invoking on the data plane with a `runtimeSessionId` (≥33 chars) and a `messages` list — not `--input-text`, (4) reading the streamed response events, and (5) the AgentCore CLI (`agentcore create`/`deploy`/`invoke`) as the fastest path. The reference is authoritative over any external documentation. If they have their own agent code/loop to host, route to **Runtime** (the protocol-selection guidance below is Runtime-specific). +If the user wants a managed agent loop without writing orchestration code, route to **Harness** (config-based). Harness is distinct from classic **Bedrock Agents** (the `bedrock-agent` action-group service — see [agent creation](references/agents-and-action-groups.md)). For a CLI Harness workflow, read the [Harness procedure](references/agentcore-harness.md) and use AgentCore CLI v1.0.0 or later: `agentcore create --name --template empty`, `agentcore add harness`, `agentcore build`, `agentcore deploy`, and `agentcore invoke --harness --prompt `. For direct SDK/API creation, cover `CreateHarness`, polling `GetHarness` until `READY`, then streaming `InvokeHarness` events from the data plane. If they have their own agent code or loop to host, route to **Runtime**. Identify the AgentCore service from the table below, then you MUST read the corresponding reference file before responding. Follow any procedures in the reference step by step. Do not summarize — execute. diff --git a/skills/core-skills/amazon-bedrock/references/agentcore-harness.md b/skills/core-skills/amazon-bedrock/references/agentcore-harness.md index 88286dfa4..9671e1e63 100644 --- a/skills/core-skills/amazon-bedrock/references/agentcore-harness.md +++ b/skills/core-skills/amazon-bedrock/references/agentcore-harness.md @@ -34,9 +34,9 @@ Both run inside AgentCore Runtime infrastructure, but they solve different probl | | Harness | Runtime | |---|---------|---------| -| **You provide** | Configuration (no code) | Agent code + ARM64 container | +| **You provide** | Configuration (no orchestration code) | Agent code, packaged as CodeZip or a container | | **Agent loop** | Managed (Strands) | You write it | -| **Change model / add a tool** | Config change, no redeploy | Code change + redeploy | +| **Change model / add a tool** | Edit Harness config, then deploy the project | Change code or config, then deploy the project | | **Framework choice** | Strands only | Any (LangGraph, CrewAI, custom) | | **Best for** | Fast setup, dynamic config | Custom loop control, non-loop patterns (graph/workflow), bidirectional streaming, hooks | @@ -70,19 +70,24 @@ Deployment Progress (all three stages are required — creation alone does not y - Skipping Step 2 — a harness is not invokable until `get-harness` reports `READY`. - Invoking with `--input-text` or `--harness-id` — there is no such parameter. Invocation is on the **data plane** (`bedrock-agentcore`), takes a `runtimeSessionId` (≥33 chars) and a `messages` list, and returns a **stream** of events you must iterate (see Streaming Response Format). Treating the response as a single string drops the agent's output. -**AgentCore CLI:** +**AgentCore CLI v1:** ```bash -npm install -g @aws/agentcore@preview -# Set the execution-limit guardrails (max iterations, max tokens, timeout) explicitly -# rather than relying on defaults — see Security Considerations. Use `agentcore create --help` -# for the current flag names, or set them on the underlying create-harness call below. -agentcore create --name myresearchagent --model-provider bedrock -agentcore deploy -agentcore invoke --harness myresearchagent --session-id "$(uuidgen)" "Hello, what can you do?" +npm install -g @aws/agentcore@latest +agentcore create --name ResearchProject --template empty +cd ResearchProject +agentcore add harness \ + --name ResearchHarness \ + --model '{"provider":"bedrock","modelId":"global.anthropic.claude-sonnet-5-5"}' \ + --authorizer-type AWS_IAM \ + --max-iterations 25 --max-tokens 4096 --timeout-seconds 300 +agentcore build +agentcore deploy --region +agentcore status --json +agentcore invoke --harness ResearchHarness --prompt "Hello, what can you do?" ``` -Useful CLI commands: `agentcore dev` (local dev server + inspector), `agentcore status`, `agentcore add harness`. +The editable Harness files are `app/ResearchHarness/harness.yaml` and `system-prompt.md`. The first deploy creates the default target in `agentcore/aws-targets.json` from the effective account and region. `agentcore dev` serves project Runtimes locally; it is not the Harness create step. **AWS CLI / SDK:** @@ -125,7 +130,7 @@ for event in response["stream"]: - `harnessName` must start with a letter and contain only letters, digits, and underscores, max 40 characters. - `runtimeSessionId` MUST be at least 33 characters — a standard UUID (36 chars, with hyphens) satisfies this. If your `uuidgen` strips hyphens (32 chars), it will be too short; append a suffix or concatenate two. Over the wire it maps to the `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header. Reuse the same session id across invocations to continue the conversation in the same environment. - When no model is configured the harness applies a default Bedrock model; check the [CreateHarness API reference](https://docs.aws.amazon.com/bedrock-agentcore-control/latest/APIReference/API_CreateHarness.html) for the current default, and `aws bedrock list-foundation-models` for available model IDs. -- Install the AgentCore CLI from the `@aws/agentcore@preview` npm channel (`npm install -g @aws/agentcore@preview`). +- Install AgentCore CLI v1.0.0 or later with `npm install -g @aws/agentcore@latest`. - Refer to the latest AWS documentation for authoritative API parameters. ## Configuration Surface @@ -251,7 +256,7 @@ The imperative shell operation `InvokeAgentRuntimeCommand` (`POST /runtimes/ --allowed-clients `. (Do not use `oidcAuthorizerConfiguration` — that name appears in some examples but is not the API field.) +- OAuth JWT config is `authorizerConfiguration.customJWTAuthorizer` with `discoveryUrl` (required), `allowedAudience`, and `allowedClients`. With the v1 project CLI, use `agentcore add harness --authorizer-type CUSTOM_JWT --authorizer-configuration ''` or edit `harness.yaml` and run `agentcore build`. (Do not use `oidcAuthorizerConfiguration` — that is not the API field.) - Set `allowedAudience` and/or `allowedClients` to constrain which tokens are accepted: `allowedAudience` validates the JWT `aud` claim and `allowedClients` validates the client ID, so a token issued for a different service or client cannot be replayed against the harness. A JWT authorizer with neither constraint accepts any valid token from the issuer. **Network and container:** diff --git a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/cli.md b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/cli.md index 855e140bd..2cb52fa9a 100644 --- a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/cli.md +++ b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/cli.md @@ -1,36 +1,22 @@ -# AgentCore CLI +# AgentCore CLI v1 for the Harness migration -The migration tool is the AgentCore CLI, package **`@aws/agentcore`** — use the **latest** version. Its command/flag surface shifts between releases, so **verify it live** rather than trusting a hardcoded flag table. - -The AgentCore CLI (`@aws/agentcore`) is **not** the same as the `bedrock-agentcore-starter-toolkit`. The starter toolkit is deprecated and not recommended — do not use it or its commands for this migration. Everything here uses `@aws/agentcore`. - -## Authoritative, always-current sources - -- Installed surface: `agentcore --help`, then `agentcore --help` for each command about to be used. -- Per-project schema the CLI ships: `https://schema.agentcore.aws.dev/v1/agentcore.json` inside any scaffolded project (authoritative shape for `agentcore.json`, harness, gateway, target, tool-schema). Read before hand-editing config. -- Published / latest version: `npm view @aws/agentcore version`. -- Package page: https://www.npmjs.com/package/@aws/agentcore - -## Phase 0 checks +Use `@aws/agentcore` v1.0.0 or later. This is separate from the Bedrock AgentCore Starter Toolkit and from the AWS CLI. Check the installed commands before acting: ```bash agentcore --version -npm view @aws/agentcore version # newer release available? -python3 -c "import boto3; print(boto3.__version__)" # discovery path probe (see discovery.md) -``` - -Then probe the commands the migration actually calls and confirm the flags/values each needs are present: - -```bash agentcore create --help +agentcore add harness --help agentcore add gateway --help agentcore add gateway-target --help -agentcore add harness --help -agentcore add tool --help +agentcore add gateway-connector --help +agentcore build --help agentcore deploy --help +agentcore status --help +agentcore invoke --help ``` -If a required flag is **absent**, stop — don't generate commands against a surface that no longer exists. Update the CLI (`npm install -g @aws/agentcore@latest`) and re-probe, or update this skill if the references assume a flag the CLI renamed. +A v1 project has `"version": 2` in `agentcore/agentcore.json`. `agentcore/aws-targets.json` is empty until the first deploy creates the default target from the effective AWS account and region. The Harness's own editable config is `app//harness.yaml`, with its prompt in `system-prompt.md`. `agentcore add harness --model` takes a JSON object such as `{"provider":"bedrock","modelId":""}`; the written YAML uses `model.bedrockModelConfig.modelId`. For direct YAML edits, follow the scaffolded field names. `agentcore build` validates and packages the project. + +`agentcore add runtime --agent-id ... --agent-alias-id ...` translates a classic Bedrock Agent into a code Runtime. It is a separate route from this Harness migration. `agentcore migrate` converts an AgentCore CLI v0 project to v1; it does not import a classic Bedrock Agent. -## Never reverse-engineer the CLI bundle -Do **not** read the CLI's minified source (`node_modules/@aws/agentcore/dist/**`) — internal names and wizard-only code paths produce confident-but-wrong conclusions. If `--help`, `.llm-context`, the hosted schema, and these references don't answer it, stop and ask the user. +Do not infer flags from v0 guides. The v1 CLI has no `add tool`, `validate`, `fetch access`, or `project` command group. Check command help and the v1 source for any option this migration needs before issuing the command. diff --git a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md index 15aab0a18..07c268e22 100644 --- a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md +++ b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md @@ -1,116 +1,96 @@ -# Deploy +# Deploy a migrated Bedrock Agent as a v1 Harness -Deploy into the **source agent's region** (mirror). If any step fails, surface the error and stop — **fail loudly**, never silently work around a failure. +Use the approved discovery and mapping plan. Deploy in the source agent's region and preserve its inbound access posture. Do not change or delete the source Bedrock Agent as part of this migration. -## CRITICAL: set the deploy-target region right after scaffold (region trap) -`agentcore create` with no resolved region **silently writes a default region** into `agentcore/aws-targets.json` that is usually not the source agent's. Immediately after scaffold, before any `add`/`deploy`: **edit `agentcore/aws-targets.json` so the deploy-target region is the source agent's region**, and verify it before the first deploy. A wrong region is wrong on two counts: the shims invoke the **source** Lambdas / KB **by ARN**, so a harness elsewhere can't reach them; and a deploy can hit region-specific Harness CFN-type issues that surface as a stack rollback (read the CloudFormation failure; if region-specific, redeploy in the source region). - -If a deploy already landed in the wrong region: `aws cloudformation delete-stack` the wrong-region stack, reset `agentcore/.cli/deployed-state.json` to `{"targets":{}}`, fix `aws-targets.json`, then redeploy. - -## Source-side prerequisites — the builder grants these, never the migration (INV-1) - -The AG shim invokes the **original** source Lambda by ARN, so the source Lambda's resource policy must allow the shim's execution role to call it. **This is the builder's job, not the migration's** — the skill must never mutate source-side infrastructure to unblock itself. - -Expect the **first shim invocation to fail with `AccessDeniedException`** until the permission exists. When it does: **stop and hand the builder this command; do NOT run `aws lambda add-permission` on the source yourself** (that mutates the source and breaks INV-1): +## Scaffold and configure ```bash -aws lambda add-permission --function-name \ - --statement-id agentcore-shim-invoke --action lambda:InvokeFunction \ - --principal --source-arn +agentcore create --name --template empty +cd +agentcore add harness \ + --name \ + --model '{"provider":"bedrock","modelId":""}' \ + --authorizer-type AWS_IAM \ + --max-iterations 25 --max-tokens 4096 --timeout-seconds 300 +agentcore add gateway --name --authorizer-type AWS_IAM ``` -(`bedrock-agent-runtime:Retrieve` for the KB shim is granted on the shim role via its `iamPolicy`, so the KB path needs no source-side change — only the AG-shim → original-Lambda invoke does.) - -## Two-phase deploy - -A gateway tool can only attach once the gateway and its targets are *deployed* (`add tool --type agentcore_gateway` reads the gateway's deployed tool list). So deploy twice: - -1. **Scaffold:** `agentcore create` (plain — not the `--type import`/`agentcore import` path, which builds a *code* project, not a Harness). Then `cd` in and run **every** later `add`/`deploy`/`status` from that one directory — those commands resolve the project from the cwd, so a second project or a different dir yields "No agentcore project found". -2. **Add** infra that doesn't need a deployed gateway: - - `agentcore add gateway`, then hand-add one **`targetType: "lambda"` code target** to `agentcore.json` per action group / KB shim (see "How shims are deployed"). - - `agentcore add harness` (one command, all flags): `--model-id` (= source `foundationModel`), `--temperature`/`--top-p`/`--model-max-tokens` (only one of temperature/top-p when the model rejects both), `--system-prompt` (folded instruction + non-DEFAULT override intent, per [mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)). Managed memory stays on. `--additional-params` is `lite_llm`-provider only, so a bedrock harness rejects it — which is why the source guardrail is classified **cannot migrate** (verify the current CLI surface per [mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)'s guardrail section before relying on this). - - if the source had CodeInterpreter: `agentcore add tool --harness --type agentcore_code_interpreter --name `. -3. **First deploy:** `agentcore deploy` — creates gateway, targets, harness, memory. -4. **Attach the gateway tool** (gateway now deployed): `agentcore add tool --harness --type agentcore_gateway --name --gateway --outbound-auth awsIam`. `--gateway ` resolves the deployed ARN automatically. **Add it exactly once** — check `harness.json`/`agentcore status` first; a duplicate `agentcore_gateway` tool deploys fine but breaks at runtime (`Tool name '…' already exists`), silently disabling every gateway-backed tool. If already doubled, remove the extra from `harness.json` and redeploy. -5. **Second deploy:** `agentcore deploy` — applies the gateway tool. The migration isn't complete until this succeeds and `agentcore status` shows the harness at a new version with the tool. - -## Inbound auth — match the source's invocation posture, never loosen it (INV-2/INV-3) - -`--outbound-auth awsIam` above governs how the **harness calls the gateway**. It says nothing about **who may invoke the migrated harness/gateway** — that is *inbound* auth, and it is a separate, mandatory decision. The source Bedrock Agent is IAM-gated: only principals with `bedrock-agent-runtime:InvokeAgent` on that agent's ARN can invoke it. The migrated harness must be **no more reachable than that**. - -- **Discover the source posture** (Phase 2): which principals hold `bedrock-agent-runtime:InvokeAgent` on the source, and any resource-based policy on the agent. This is the bar to match. -- **Configure inbound auth explicitly** on `add harness`/`add gateway` — check `agentcore add harness --help` and `add gateway --help` for the inbound-auth flag (SigV4 with an allowed-role list, JWT with a verified issuer, etc.) and set it to match the discovered posture. Do **not** rely on the CLI default. -- **If the CLI's inbound default is unauthenticated or broader than the source** (or you cannot determine it), **hard-stop** and have the builder confirm the intended posture before deploying — a migrated agent invokable by parties who couldn't reach the source violates both secure-by-default and preserve-posture. - -## How shims are deployed — hand-author the code target, CLI builds it at deploy - -`agentcore add gateway-target` **cannot create a code target non-interactively** (`--type lambda` is rejected). So this is one of the guide's explicitly sanctioned hand-edits (see "Config edits — the rule"): add the target to `agentcore.json` yourself, then `agentcore deploy` builds the Lambda from your source. Per shim: +Adjust invocation limits to the approved plan. A Bedrock Agent's source `foundationModel` may need an inference profile in the target region. Read `app//harness.yaml`; it uses `model.bedrockModelConfig.modelId`, not the JSON input shape used by `--model`. Put the mapped instruction in `app//system-prompt.md`. If the source idle session TTL matters, set the `lifecycleConfig.idleRuntimeSessionTimeout` field in `harness.yaml` to the mapped value. Keep managed memory configured unless the assessment calls for a different mode. -1. Place rendered shim at `tools//handler.py` (from `{kb_shim,lambda_shim}.py.tmpl` in `assets/`) with a `pyproject.toml` beside it. -2. Add one entry to `agentCoreGateways[].targets[]` in `agentcore.json`: +For Code Interpreter, add one tool to the YAML `tools` list: - ```json - { - "name": "", - "targetType": "lambda", - "toolDefinitions": [ /* one per function/operation; mirror source schema exactly */ ], - "compute": { - "host": "Lambda", - "implementation": { "language": "Python", "path": "tools/", "handler": "handler.lambda_handler" }, - "pythonVersion": "", - "timeout": 30, - "iamPolicy": { /* full policy document, least-privilege — see below */ } - } - } - ``` - -3. Run `agentcore validate` (must print `Valid`), then `agentcore deploy`. - -**No `environment` key — all shim config is baked in at render time.** The `compute` schema is strict and has **no** environment-variable support; adding an `environment` key fails `agentcore validate`. Every `{{TOKEN}}` in the shim templates (`ORIGINAL_LAMBDA_ARN`, `SCHEMA_STYLE`, `OP_ROUTES`, `KB_ID`, …) is substituted directly into `tools//handler.py` as a literal. An unsubstituted token causes a `SyntaxError` or wrong behavior at runtime — the post-render grep in [mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md) is the gate. - -**Scope `iamPolicy` to a single resource ARN — never a wildcard.** It is a full policy document (`Version` + `Statement` array are required), granting exactly one action on exactly one resource: +```yaml +tools: + - type: agentcore_code_interpreter + name: code_interpreter + config: + agentCoreCodeInterpreter: {} +``` -- AG shim — invoke only the original Lambda: +The v1 CLI has no `add tool` command. Do not duplicate the YAML `tools` key. Run `agentcore build` after each configuration edit. + +## Add Gateway targets + +An action-group shim is a new Lambda backed Gateway target. Render the source action group into `tools//handler.py` using the templates in this skill's `assets/`, with one `toolDefinitions` entry per source function or OpenAPI operation. Store the complete target object in a JSON file and add it through the CLI: + +```json +{ + "name": "ActionGroupTools", + "targetType": "lambda", + "toolDefinitions": [ + {"name":"example_tool","description":"Describe the mapped operation","inputSchema":{"type":"object","properties":{}}} + ], + "compute": { + "host": "Lambda", + "implementation": {"language":"Python","path":"tools/action-group","handler":"handler.lambda_handler"}, + "pythonVersion": "PYTHON_3_12", + "timeout": 30, + "iamPolicy": {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"lambda:InvokeFunction","Resource":""}]} + } +} +``` - ```json - { "Version": "2012-10-17", "Statement": [ - { "Effect": "Allow", "Action": "lambda:InvokeFunction", - "Resource": "arn:aws:lambda:::function:" } ] } - ``` +```bash +agentcore add gateway-target --gateway --target-configuration file://action-group-target.json +``` -- KB shim — retrieve only from the source KB: +Use a scoped `bedrock-agent-runtime:Retrieve` policy on the source Knowledge Base ARN for a KB shim. For a Knowledge Base with default retrieval behavior that the managed connector supports, use `agentcore add gateway-connector --gateway --name --connector bedrock-knowledge-bases --knowledge-base ` instead. `lambdaFunctionArn` targets point at an existing Lambda; use `lambda` with `compute` for a shim the project builds. Do not put secret values or unsupported `environment` keys into target configuration. Run `agentcore build` to catch schema errors. - ```json - { "Version": "2012-10-17", "Statement": [ - { "Effect": "Allow", "Action": "bedrock-agent-runtime:Retrieve", - "Resource": "arn:aws:bedrock:::knowledge-base/" } ] } - ``` +## First deploy and Gateway ARN -**Logging & monitoring.** Give the execution role `logs:CreateLogGroup`, `logs:CreateLogStream`, `logs:PutLogEvents` scoped to the function's log group (`arn:aws:logs:::log-group:/aws/lambda/:*`); recommend a CloudWatch alarm on the shim's `Errors`/`Throttles` metrics; and confirm CloudTrail captures `lambda:Invoke` for audit. The shims handle tool arguments (AG shim) and retrieval text (KB shim), which may be sensitive: **encrypt the log group with a KMS key** (`aws logs associate-kms-key --log-group-name /aws/lambda/ --kms-key-id `) and do **not** log full request/response payloads. +```bash +agentcore build +agentcore deploy --region +agentcore status --json +``` -**Throttling & blast radius.** Set **reserved concurrency** on each shim Lambda (`aws lambda put-function-concurrency`) so a runaway caller can't exhaust account-wide Lambda concurrency, and enable request throttling on the Gateway if the CLI/service exposes it. +The first deploy creates the default target in `agentcore/aws-targets.json` using the effective account and the explicit source region. Confirm both before proceeding. Find the deployed Gateway ARN in `agentcore status --json`. Record it in the approved migration plan. If deployment fails, inspect the CloudFormation failure and fix the project configuration; do not reset all deployed state or delete a stack by guesswork. -**Non-negotiable details (these fail `agentcore validate` if wrong):** +## Attach the Gateway to the Harness -- The `compute` block is **strict** — unknown keys (e.g. `environment`) are rejected, and a Python Lambda **must** set `pythonVersion`. -- `pythonVersion` is an enum (e.g. `PYTHON_3_12`), **not** a bare `"3.12"` — check the project's `agentcore.json` schema (or `agentcore validate` feedback) for the currently valid values. Prefer the source Lambda's runtime. -- `implementation` requires all three of `language`, `path`, `handler` and nothing else. -- `targetType` is the literal string `"lambda"` here (valid in the JSON schema, even though `--type lambda` is rejected on the CLI). -- `path` is relative to the project root (parent of `agentcore/`). -- `handler` is `.` = `handler.lambda_handler`. +Edit the existing `tools` list in `app//harness.yaml` once: -Do **not** use `--type lambda-function-arn` — that wires a *pre-existing* Lambda by ARN, not a shim this skill builds. +```yaml +tools: + - type: agentcore_gateway + name: migrated_tools + config: + agentCoreGateway: + gatewayArn: arn:aws:bedrock-agentcore:::gateway/ + outboundAuth: + awsIam: {} +``` -## Config edits — the rule -Hand-edit a config file **only where this guide explicitly says to** — the region correction in `aws-targets.json`, the `targetType: "lambda"` code target in `agentcore.json`, the documented recovery edits (removing a duplicate `agentcore_gateway` tool from `harness.json`; resetting `deployed-state.json` for a wrong-region redeploy). Everything else goes through `agentcore` commands — harness and tools via `add harness`/`add tool`, gateways and other targets via `add gateway`/`add gateway-target`. Do not invent new hand-edits: when an `add` command fails, fix its flags (missing `--name`, wrong `--type`) rather than editing `harness.json`/`agentcore.json` to route around the failure. +The Gateway ARN exists only after the first deploy. The `outboundAuth` block governs the Harness to Gateway call. The Gateway's `authorizerType` and Harness's `authorizerType` govern inbound access. A classic Bedrock Agent is IAM gated; explicitly preserve at least that posture and confirm the caller roles have only the necessary new permissions. The v1 Gateway default is `NONE`, so do not omit `--authorizer-type AWS_IAM` during creation. The Harness tool name must be unique. -## One action group = one target, with all its functions -A Bedrock action group can expose several functions/operations (up to three), each with its own schema. The tool-schema file is a **`ToolDefinition[]` array**, so a single Lambda target carries every function in that action group — one array entry per function/operationId. Do not split an action group into multiple targets. [tool_schema.json.tmpl](assets/tool_schema.json.tmpl) is already an array; add one entry per function, mirroring the source schema exactly ([mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)). +```bash +agentcore build +agentcore deploy +agentcore status --json +``` -## Verification -The migration is done when `agentcore deploy` reports success. Before treating it as complete, **prompt the user** to confirm the deploy succeeded and they're satisfied — surface the deployed harness/gateway ARNs from `agentcore status`. Deeper parity (invoking the harness, comparing against the source) is out of scope unless the user asks. +Confirm the second deploy succeeds and the Harness and Gateway are deployed. An optional parity check uses `agentcore invoke --harness --prompt "Hello"`; compare the output with the source agent before traffic cutover. -## Rendering templates into CLI inputs +## Source permissions and verification -- KB shim / AG shim Lambda code: adapt the `*.py.tmpl` files in `assets/` (rendering rules in [mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)) and place the rendered handler at `tools//handler.py` — see "How shims are deployed" above. -- Tool-schema files: one array per target, one entry per source function/operation, mirroring the source schema exactly ([mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)). +The shim's role must be allowed to invoke the source Lambda ARN. If a source Lambda policy change is required, hand the exact proposed change to the source owner; keep source-side changes outside the migration automation. Give the shim role only the Lambda or KB permission it needs and verify its CloudWatch log access. Alarm on Lambda errors and throttles, and avoid logging full tool arguments or retrieval text. Do not claim successful migration until `agentcore build` and both deploys complete and the target status reports deployed resources. diff --git a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md index 2f5e53833..a05b013f5 100644 --- a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md +++ b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md @@ -12,7 +12,7 @@ A Bedrock action-group Lambda speaks the Bedrock event envelope; AgentCore Gatew **OpenAPI action groups: pass the route TEMPLATE verbatim.** The original Lambda dispatches by matching `apiPath` against the literal route template (`/customer/{customer_id}`), with path-param values delivered separately in `parameters`. Substituting a value into the path (`/customer/tkashina`) matches no template, so the original falls through to its unhandled-op branch. Render the shim's `_OP_ROUTES` table (mapping each operationId to its `{method, apiPath-template}`) from the source OpenAPI schema with placeholders intact, and keep values in `parameters`. -Each action group becomes one Gateway **`targetType: "lambda"` code target**, hand-added to `agentcore.json` (the CLI can't create it non-interactively), with `toolDefinitions` holding **one entry per function/operation**. `agentcore deploy` then builds the shim Lambda. Exact block + gotchas in [deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md); follow it. +Each action group becomes one Gateway **`targetType: "lambda"` code target**, added with `agentcore add gateway-target --target-configuration file://.json`, with `toolDefinitions` holding one entry per function or operation. `agentcore deploy` builds the shim Lambda. Follow [deploy.md](deploy.md). ### Tool schemas — mirror exactly Reproduce the source action group's schema faithfully into the tool-schema file ([tool_schema.json.tmpl](assets/tool_schema.json.tmpl)): same tool names, parameter names, types, and descriptions. Do **not** rewrite or "improve" them — fidelity to the source agent's behavior is the goal, and a renamed tool or tightened type changes how the model selects it. @@ -23,7 +23,7 @@ Reproduce the source action group's schema faithfully into the tool-schema file - **Any non-`MANAGED` type (`VECTOR`, `KENDRA`, `SQL`)** → **always the KB shim.** The connector cannot accept these at all, so type alone decides and retrieval config is irrelevant. Use the **KB shim** ([kb_shim.py.tmpl](assets/kb_shim.py.tmpl)): a hand-added **`targetType: "lambda"` code target** (see [deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md)) that calls `bedrock-agent-runtime:Retrieve` and returns MCP-shaped passages. - **`MANAGED`** → then (and only then) check the **retrieval config on the KB association** (`knowledgeBaseConfigurations[].retrievalConfiguration`: reranker, metadata filter, hybrid/search-type override, top-k): - - **default** retrieval config → native Gateway connector: `add gateway-target --type connector --connector bedrock-knowledge-bases --knowledge-base-id `. + - **default** retrieval config → native Gateway connector: `agentcore add gateway-connector --gateway --name --connector bedrock-knowledge-bases --knowledge-base `. - **non-default** retrieval config → the **KB shim** (the connector uses a fixed retrieval contract and would silently drop the custom config), reproducing that config from the manifest's KB association. Both paths reproduce the source agent's retrieval — the choice is wiring, not fidelity, so every reachable KB is still **clean**. (An *unreachable* KB is a hard-stop — see [eligibility.md](references/bedrock-agents-to-agentcore-harness/eligibility.md). Type picks the wiring; it is never itself the blocker.) @@ -34,7 +34,7 @@ A `customControl: RETURN_CONTROL` action group has no Lambda; the original appli ## Built-in action groups - **`AMAZON.UserInput`**: no tool. Add a clarification instruction to the system prompt; the harness asks clarifying questions naturally. -- **`AMAZON.CodeInterpreter`**: use the built-in tool, `agentcore add tool --harness --type agentcore_code_interpreter --name ` (`--harness` and `--name` required — see [deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md)). +- **`AMAZON.CodeInterpreter`**: add an `agentcore_code_interpreter` tool to `app//harness.yaml`; see [deploy.md](deploy.md). The v1 CLI has no `add tool` command. ## Mapping memory to managed memory (default) @@ -45,10 +45,10 @@ Use the harness's **managed memory**, which is on by default — the harness aut Reference: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-memory.html ## Mapping model parameters to the harness model config -Set model params via `agentcore add harness` flags: `--model-id`, `--model-max-tokens`, `--temperature`, `--top-p`, `--api-format`. Some models reject setting temperature and top-p together — check the target model's inference-parameter constraints (e.g. `aws bedrock get-foundation-model`, or a probe call) and set **only one** if so. +Set model parameters in the `--model` JSON when adding the Harness, or edit `model.bedrockModelConfig` in `harness.yaml`: `modelId`, `maxTokens`, `temperature`, `topP`, and `apiFormat`. Some models reject setting temperature and topP together; set only supported parameters. ## Mapping idle session TTL (preserve the source's session-lifetime bound) -The source's `idleSessionTTLInSeconds` bounds how long an idle session is retained — a security control (it caps the credential/context leakage and replay window). Read its actual value from the discovery manifest (the source agent's own setting, whatever it is) and preserve it: if `agentcore add harness` exposes an equivalent session-TTL flag, set it to the source value. If the CLI has no equivalent, **classify it degraded in Phase 4** with the concrete delta (e.g. "source set 300s; migrated defaults to Ns") so the builder decides — never silently extend the window. +The source's `idleSessionTTLInSeconds` bounds how long an idle session is retained. Read its actual value from the discovery manifest and set `lifecycleConfig.idleRuntimeSessionTimeout` in the Harness YAML. If the source value is outside the target service's accepted range, classify the difference in the assessment before deployment. ## Guardrail — cannot migrate; surface to the user Check whether the installed CLI can attach a guardrail to a **bedrock** harness before classifying: look in `agentcore add harness --help` for a guardrail field, and note that `--additional-params` (the pass-through) is `lite_llm`-provider only, so a bedrock harness rejects it. If no guardrail path exists in the CLI surface you're running, classify the guardrail **cannot** in the assessment and tell the user the migrated harness will **not** enforce it, recording the source `guardrailIdentifier` + version. Do **not** fake it with a system-prompt mention (that enforces nothing). Enforcing it means applying `ApplyGuardrail` outside the harness — out of scope. If the CLI surface you're running exposes a guardrail field, use it and reclassify as clean. @@ -59,4 +59,4 @@ Fold the agent instruction into the harness `--system-prompt`. If `AMAZON.UserIn Prompt overrides in **DEFAULT** mode carry nothing custom — skip them. But a **non-DEFAULT override** (especially `ORCHESTRATION` or `PRE_PROCESSING`) may hold real business logic — routing rules like "use tool X for billing questions, tool Y for refunds," or input-classification the app relied on. Don't discard that as boilerplate. Read each non-DEFAULT override, separate the Bedrock-Agents scaffolding (the orchestration loop mechanics the harness now owns) from the business intent, and fold the intent into the system prompt — a modern model handles tool-routing guidance cleanly in the prompt. When unsure whether an override is boilerplate or load-bearing, surface it to the user rather than dropping it. ## Mapping the model (mirror the source) -Default `--model-id` to the source agent's `foundationModel`. If the CLI's harness default differs, set it explicitly to match the source (parity). +Set the Harness `--model` JSON `modelId` to the source agent's `foundationModel` or an approved compatible inference profile. The CLI's default model may differ. diff --git a/skills/core-skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md b/skills/core-skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md index e002b2035..776984002 100644 --- a/skills/core-skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md +++ b/skills/core-skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md @@ -79,9 +79,9 @@ Completion criterion: user approved the written plan. ### Phase 6 — Implement & deploy -Drive the CLI per the approved plan, following [bedrock-agents-to-agentcore-harness/deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md) end to end: scaffold with plain `agentcore create`, deploy the shim Lambdas, `add gateway`/`gateway-target`/`harness`, then the **two-phase deploy**. On scaffolding specifically: use `agentcore create` with no flags. Do **not** use `agentcore create --import` (or `agentcore import`) — that flag *does exist*, but it imports the Bedrock Agent into a **code project**, not a Harness, so it is the wrong route for this migration. There is **no** `agentcore init` command. Set the deploy region before the first deploy (deploy.md's region trap). Generate shim code and tool schemas by **adapting** the `.tmpl` templates in `assets/` per [bedrock-agents-to-agentcore-harness/mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md). Configure the harness/gateway **inbound auth** to match the source's invocation posture discovered in Phase 2 (deploy.md "Inbound auth") — do not accept the CLI default if it is broader. If deploy fails, surface the error — **fail loudly**, never silently work around it. In particular, if a shim invocation fails with `AccessDenied`, **do not** run `aws lambda add-permission` on the source — that mutates source-side infra; stop and hand the builder the command (deploy.md "Source-side prerequisites"). +Drive the CLI per the approved plan, following [bedrock-agents-to-agentcore-harness/deploy.md](bedrock-agents-to-agentcore-harness/deploy.md) end to end: scaffold with `agentcore create --name --template empty`, add the Harness and Gateway, add Gateway targets from complete JSON configurations, then run the two deploys. `agentcore add runtime --agent-id ...` translates a Bedrock Agent into code and is a different route. Set the source region explicitly on the first deploy. Render shim code and tool schemas from the `.tmpl` templates in `assets/` per [mapping.md](bedrock-agents-to-agentcore-harness/mapping.md). Configure Harness and Gateway inbound auth to preserve the source's invocation posture. If deploy fails, surface the error and fix the project; if shim invocation fails with `AccessDenied`, hand the source Lambda permission change to its owner. -Completion criterion: `agentcore deploy` reports success. Verification is deploy-success-only; deeper parity is out of scope. +Completion criterion: `agentcore build` and both `agentcore deploy` runs succeed, and `agentcore status --json` shows the Harness and Gateway deployed. Deeper response parity should be tested before cutting over application traffic. ## Security considerations From e3de8bd5f5313d32229918839b315c881005194a Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Thu, 29 Oct 2026 12:48:03 -0400 Subject: [PATCH 02/10] fix(aws-agents): fix runtime and harness configuration guidance --- plugins/aws-agents/skills/agents-build/SKILL.md | 4 ++-- .../references/bedrock-agents-to-agentcore-harness/deploy.md | 2 +- .../references/bedrock-agents-to-agentcore-harness/mapping.md | 2 +- .../references/bedrock-agents-to-agentcore-harness/deploy.md | 2 +- .../references/bedrock-agents-to-agentcore-harness/mapping.md | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/aws-agents/skills/agents-build/SKILL.md b/plugins/aws-agents/skills/agents-build/SKILL.md index f19800b2e..82f9a9cfb 100644 --- a/plugins/aws-agents/skills/agents-build/SKILL.md +++ b/plugins/aws-agents/skills/agents-build/SKILL.md @@ -113,7 +113,7 @@ If the intent is ambiguous, ask the developer which capability they want to add. For a Strands Runtime, the model is configured in `app//model/load.py` (the `agent-python-strands` template uses `app/agent/model/load.py`). To change it: -1. Open `app//model/load.py` +1. Open `app//model/load.py` 2. Change the `model_id` parameter in the `BedrockModel()` constructor ```python @@ -136,7 +136,7 @@ After changing the model: - If using `agents-harden`, update the IAM policy to scope to the new model ARN - Run `agentcore dev` to test locally, then `agentcore deploy` to update the deployed agent -For a Harness, edit `app//harness.yaml` instead, under `model.modelId`, then run `agentcore deploy`. A Runtime model is configured in code. +For a Bedrock Harness, edit `app//harness.yaml` instead, under `model.bedrockModelConfig.modelId`, then run `agentcore build` and `agentcore deploy`. Other Harness model providers use their corresponding model configuration block. A Runtime model is configured in code. ### Pre-flight: validate any `--name` before generating the CLI command diff --git a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md index 07c268e22..1abdb4abc 100644 --- a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md +++ b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md @@ -15,7 +15,7 @@ agentcore add harness \ agentcore add gateway --name --authorizer-type AWS_IAM ``` -Adjust invocation limits to the approved plan. A Bedrock Agent's source `foundationModel` may need an inference profile in the target region. Read `app//harness.yaml`; it uses `model.bedrockModelConfig.modelId`, not the JSON input shape used by `--model`. Put the mapped instruction in `app//system-prompt.md`. If the source idle session TTL matters, set the `lifecycleConfig.idleRuntimeSessionTimeout` field in `harness.yaml` to the mapped value. Keep managed memory configured unless the assessment calls for a different mode. +Adjust invocation limits to the approved plan. A Bedrock Agent's source `foundationModel` may need an inference profile in the target region. Read `app//harness.yaml`; it uses `model.bedrockModelConfig.modelId`, not the JSON input shape used by `--model`. Put the mapped instruction in `app//system-prompt.md`. If the source idle session TTL matters, set `environment.agentCoreRuntimeEnvironment.lifecycleConfiguration.idleRuntimeSessionTimeout` in `harness.yaml` to the mapped value. Keep managed memory configured unless the assessment calls for a different mode. For Code Interpreter, add one tool to the YAML `tools` list: diff --git a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md index a05b013f5..4f8c204f5 100644 --- a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md +++ b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md @@ -48,7 +48,7 @@ Reference: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness Set model parameters in the `--model` JSON when adding the Harness, or edit `model.bedrockModelConfig` in `harness.yaml`: `modelId`, `maxTokens`, `temperature`, `topP`, and `apiFormat`. Some models reject setting temperature and topP together; set only supported parameters. ## Mapping idle session TTL (preserve the source's session-lifetime bound) -The source's `idleSessionTTLInSeconds` bounds how long an idle session is retained. Read its actual value from the discovery manifest and set `lifecycleConfig.idleRuntimeSessionTimeout` in the Harness YAML. If the source value is outside the target service's accepted range, classify the difference in the assessment before deployment. +The source's `idleSessionTTLInSeconds` bounds how long an idle session is retained. Read its actual value from the discovery manifest and set `environment.agentCoreRuntimeEnvironment.lifecycleConfiguration.idleRuntimeSessionTimeout` in the Harness YAML. If the source value is outside the target service's accepted range, classify the difference in the assessment before deployment. ## Guardrail — cannot migrate; surface to the user Check whether the installed CLI can attach a guardrail to a **bedrock** harness before classifying: look in `agentcore add harness --help` for a guardrail field, and note that `--additional-params` (the pass-through) is `lite_llm`-provider only, so a bedrock harness rejects it. If no guardrail path exists in the CLI surface you're running, classify the guardrail **cannot** in the assessment and tell the user the migrated harness will **not** enforce it, recording the source `guardrailIdentifier` + version. Do **not** fake it with a system-prompt mention (that enforces nothing). Enforcing it means applying `ApplyGuardrail` outside the harness — out of scope. If the CLI surface you're running exposes a guardrail field, use it and reclassify as clean. diff --git a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md index 07c268e22..1abdb4abc 100644 --- a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md +++ b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md @@ -15,7 +15,7 @@ agentcore add harness \ agentcore add gateway --name --authorizer-type AWS_IAM ``` -Adjust invocation limits to the approved plan. A Bedrock Agent's source `foundationModel` may need an inference profile in the target region. Read `app//harness.yaml`; it uses `model.bedrockModelConfig.modelId`, not the JSON input shape used by `--model`. Put the mapped instruction in `app//system-prompt.md`. If the source idle session TTL matters, set the `lifecycleConfig.idleRuntimeSessionTimeout` field in `harness.yaml` to the mapped value. Keep managed memory configured unless the assessment calls for a different mode. +Adjust invocation limits to the approved plan. A Bedrock Agent's source `foundationModel` may need an inference profile in the target region. Read `app//harness.yaml`; it uses `model.bedrockModelConfig.modelId`, not the JSON input shape used by `--model`. Put the mapped instruction in `app//system-prompt.md`. If the source idle session TTL matters, set `environment.agentCoreRuntimeEnvironment.lifecycleConfiguration.idleRuntimeSessionTimeout` in `harness.yaml` to the mapped value. Keep managed memory configured unless the assessment calls for a different mode. For Code Interpreter, add one tool to the YAML `tools` list: diff --git a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md index a05b013f5..4f8c204f5 100644 --- a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md +++ b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md @@ -48,7 +48,7 @@ Reference: https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness Set model parameters in the `--model` JSON when adding the Harness, or edit `model.bedrockModelConfig` in `harness.yaml`: `modelId`, `maxTokens`, `temperature`, `topP`, and `apiFormat`. Some models reject setting temperature and topP together; set only supported parameters. ## Mapping idle session TTL (preserve the source's session-lifetime bound) -The source's `idleSessionTTLInSeconds` bounds how long an idle session is retained. Read its actual value from the discovery manifest and set `lifecycleConfig.idleRuntimeSessionTimeout` in the Harness YAML. If the source value is outside the target service's accepted range, classify the difference in the assessment before deployment. +The source's `idleSessionTTLInSeconds` bounds how long an idle session is retained. Read its actual value from the discovery manifest and set `environment.agentCoreRuntimeEnvironment.lifecycleConfiguration.idleRuntimeSessionTimeout` in the Harness YAML. If the source value is outside the target service's accepted range, classify the difference in the assessment before deployment. ## Guardrail — cannot migrate; surface to the user Check whether the installed CLI can attach a guardrail to a **bedrock** harness before classifying: look in `agentcore add harness --help` for a guardrail field, and note that `--additional-params` (the pass-through) is `lite_llm`-provider only, so a bedrock harness rejects it. If no guardrail path exists in the CLI surface you're running, classify the guardrail **cannot** in the assessment and tell the user the migrated harness will **not** enforce it, recording the source `guardrailIdentifier` + version. Do **not** fake it with a system-prompt mention (that enforces nothing). Enforcing it means applying `ApplyGuardrail` outside the harness — out of scope. If the CLI surface you're running exposes a guardrail field, use it and reclassify as clean. From b3018b1e3c7e360904b50c69006dd1a5491a9d01 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Thu, 29 Oct 2026 12:48:20 -0400 Subject: [PATCH 03/10] fix(aws-agents): correct header allowlist example --- .../skills/agents-build/references/request-headers.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/aws-agents/skills/agents-build/references/request-headers.md b/plugins/aws-agents/skills/agents-build/references/request-headers.md index d035be46e..92b3eb5db 100644 --- a/plugins/aws-agents/skills/agents-build/references/request-headers.md +++ b/plugins/aws-agents/skills/agents-build/references/request-headers.md @@ -49,17 +49,17 @@ If the header names are fixed by a protocol or external system (A2A requires `A2 { "name": "MyAgent", "requestHeaderAllowlist": [ - "X-Amzn-Bedrock-AgentCore-Runtime-Custom-X-Tenant-Id", - "X-Amzn-Bedrock-AgentCore-Runtime-Custom-A2A-Version" + "X-Tenant-Id", + "A2A-Version" ] } ] } ``` -Then `agentcore deploy`. Run `agentcore build` to validate the edited v1 project before deployment. +Run `agentcore build` to validate the edited v1 project, then `agentcore deploy`. -**When creating a new Runtime**, `agentcore add runtime --name --language python --request-header-allowlist X-Tenant-Id A2A-Version` writes the allowlist. Edit the existing Runtime entry instead of adding a duplicate. **Important:** the CLI auto-prefixes entries with `X-Amzn-Bedrock-AgentCore-Runtime-Custom-` as they land in `agentcore.json`. If you're editing the JSON by hand, write the prefixed form directly. If you're using the CLI, pass the short name and let the CLI add the prefix. +**When creating a new Runtime**, `agentcore add runtime --name --language python --request-header-allowlist X-Tenant-Id A2A-Version` writes the allowlist. Edit the existing Runtime entry instead of adding a duplicate. The CLI stores the header names as passed; use the same names in the CLI flag, project JSON, and caller request. The reserved `X-Amzn-Bedrock-AgentCore-Runtime-Custom-` prefix is a separate pass-through option that does not need an allowlist entry. `Authorization` passes through by default and doesn't need to be in the allowlist. From a818c203b09d458cff0a65a4baa06f094d56c88d Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Thu, 29 Oct 2026 12:48:45 -0400 Subject: [PATCH 04/10] fix(aws-agents): ensure agent deployment workflows persist selected target --- .../skills/agents-build/references/integrate.md | 10 +++++----- .../agents-build/references/local-vs-deployed.md | 2 +- .../skills/agents-build/references/migrate.md | 6 +++--- .../skills/agents-build/references/teardown.md | 8 +++++++- .../aws-agents/skills/agents-build/references/vpc.md | 4 ++-- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/plugins/aws-agents/skills/agents-build/references/integrate.md b/plugins/aws-agents/skills/agents-build/references/integrate.md index 9f2bf2abd..d33253713 100644 --- a/plugins/aws-agents/skills/agents-build/references/integrate.md +++ b/plugins/aws-agents/skills/agents-build/references/integrate.md @@ -30,23 +30,23 @@ Do NOT use for: ### Step 1: Check deployment status -Read `agentcore/agentcore.json` to get the agent name. Then check if it's deployed: +Read `agentcore/agentcore.json` for the Runtime name and `agentcore/aws-targets.json` for the intended deployment target. Keep that target and its region for the SDK example below. Then check whether that Runtime is deployed there: ```bash -agentcore status --json +agentcore status --target --json ``` -**If not deployed:** "Your agent needs to be deployed before you can call it from an app. Run `agentcore deploy` first, or use the `agents-deploy` skill for guidance." +**If not deployed:** "Your agent needs to be deployed to `` before you can call it from an app. Run `agentcore deploy --target ` first, or use the `agents-deploy` skill for guidance." Do not proceed until the agent is deployed. ### Step 2: Get the Runtime ARN and target ```bash -agentcore status --json +agentcore status --target --json ``` -Select the deployed `runtime` entry by name in the `resources` array. Its `arn` is the identifier for SDK invocation. If the project has multiple deployment targets, pass `--target ` and use that target's region. The old `agentcore fetch access` command is absent in v1. `agentcore runtime get --id --json` reads further service details when needed. +Select the deployed `runtime` entry by name in the `resources` array. Its `arn` is the identifier for SDK invocation. Use the selected target's region, including for `agentcore runtime get --id --region --json` when further service details are needed. The old `agentcore fetch access` command is absent in v1. ### Step 3: Determine auth method diff --git a/plugins/aws-agents/skills/agents-build/references/local-vs-deployed.md b/plugins/aws-agents/skills/agents-build/references/local-vs-deployed.md index 209215ec2..93d80ca9f 100644 --- a/plugins/aws-agents/skills/agents-build/references/local-vs-deployed.md +++ b/plugins/aws-agents/skills/agents-build/references/local-vs-deployed.md @@ -57,7 +57,7 @@ else: Because memory, gateway, and policies don't work locally, the realistic test loop is: 1. **Local:** `agentcore dev` to verify the agent's code structure, framework wiring, system prompt, and any in-code logic -2. **Deploy to a staging target:** `agentcore deploy --target staging` to test with real memory, gateway, and policies +2. **Deploy to a staging target:** define `staging` with its account and region in `agentcore/aws-targets.json` first, then run `agentcore deploy --target staging` to test with real memory, gateway, and policies. Named targets are not created automatically. 3. **Production:** only after staging validation Don't expect `agentcore dev` to reproduce a production failure involving memory recall, gateway tool calls, or policy denials — those require a deployed environment. diff --git a/plugins/aws-agents/skills/agents-build/references/migrate.md b/plugins/aws-agents/skills/agents-build/references/migrate.md index fe3ccfd89..e85c9609c 100644 --- a/plugins/aws-agents/skills/agents-build/references/migrate.md +++ b/plugins/aws-agents/skills/agents-build/references/migrate.md @@ -38,10 +38,10 @@ agentcore add runtime \ agentcore build agentcore dev agentcore deploy --region -agentcore status --json -agentcore invoke --runtime ImportedAgent --payload '{"prompt":"Hello, what can you do?"}' +agentcore status --target default --json +agentcore invoke --target default --runtime ImportedAgent --payload '{"prompt":"Hello, what can you do?"}' ``` -Set the first deployment region deliberately. `agentcore/aws-targets.json` starts empty and the first deploy creates the default target using the CLI's effective region. Use `--target` to select another target on later commands. +These commands cover a new project with no deployment targets. Set its first deployment region deliberately: the first deploy creates `default` using the CLI's effective region. For an existing project, choose a target from `agentcore/aws-targets.json` and pass `--target ` to deploy, status, and invoke; `--region` does not change an existing target's region. Keep the source Bedrock Agent running until application calls have been tested against the new Runtime. Its history and sessions are separate. For application invocation, load [integrate.md](integrate.md). diff --git a/plugins/aws-agents/skills/agents-build/references/teardown.md b/plugins/aws-agents/skills/agents-build/references/teardown.md index f87c2d68e..38782c861 100644 --- a/plugins/aws-agents/skills/agents-build/references/teardown.md +++ b/plugins/aws-agents/skills/agents-build/references/teardown.md @@ -25,7 +25,13 @@ agentcore remove payment-manager --name MyPayments These are examples; use only the command for the resource the user selected. Remove child targets before their Gateway, policies before their engine, and connectors before their manager if project validation requires it. For an API key or OAuth provider, the resource type passed to `agentcore remove` is `credential`. Check `agentcore remove --help` for the resource types supported by the installed v1 CLI. -Before deploying, inspect the changed `agentcore/agentcore.json` and preview the target stack with `agentcore build` followed by `cd agentcore/cdk && npx cdk diff` using the project's target context. See the `agents-deploy` diff procedure for the exact target setup. Then deploy each intended target explicitly: +Before deploying, inspect the changed `agentcore/agentcore.json` and preview the target stack with `agentcore build` followed by the vended CDK CLI, using the selected target's stack name. `npx cdk` in this directory resolves to the project's CDK app, not the CDK CLI. See the `agents-deploy` diff procedure for the exact target setup. + +```bash +(cd agentcore/cdk && node node_modules/aws-cdk/bin/cdk diff --app cdk.out AgentCore--) +``` + +Then deploy each intended target explicitly: ```bash agentcore deploy --target diff --git a/plugins/aws-agents/skills/agents-build/references/vpc.md b/plugins/aws-agents/skills/agents-build/references/vpc.md index 4304bb969..2e2adbdd3 100644 --- a/plugins/aws-agents/skills/agents-build/references/vpc.md +++ b/plugins/aws-agents/skills/agents-build/references/vpc.md @@ -153,12 +153,12 @@ Or edit `agentcore/agentcore.json` directly — add the `networkMode` and `netwo } ``` -For a container Runtime, also set `networkConfig.vpcId` so the build can use the VPC. Run `agentcore build` to validate the project before deployment. +For a container Runtime, also set `networkConfig.vpcId` so the build can use the VPC. Run `agentcore build` to validate the project before deployment. Read `agentcore/aws-targets.json` and select the target whose account and region contain the configured VPC resources. ### Deploy ```bash -agentcore deploy +agentcore deploy --target ``` --- From a08d75b7866ada398193b3c0ee140cf3114c3116 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Thu, 29 Oct 2026 12:49:47 -0400 Subject: [PATCH 05/10] fix(aws-agents): fix Privy guidance in payments skill --- .../agents-build/references/payments.md | 105 ++++++------------ .../scripts/setup_payment_user.py | 92 +++++++++------ 2 files changed, 93 insertions(+), 104 deletions(-) diff --git a/plugins/aws-agents/skills/agents-build/references/payments.md b/plugins/aws-agents/skills/agents-build/references/payments.md index 6b90e72f6..70ba1f696 100644 --- a/plugins/aws-agents/skills/agents-build/references/payments.md +++ b/plugins/aws-agents/skills/agents-build/references/payments.md @@ -12,7 +12,7 @@ Add AgentCore Payments to an existing agent. This reference covers the v1 CLI pr ### Step 0: Check the CLI and project -Require `agentcore --version` at v1.0.0 or later. Read `agentcore/agentcore.json`; a v1 project has `"version": 2` and payment managers in `payments`. A v0 project must be committed and migrated first: review `agentcore migrate`, then apply the reviewed plan with `agentcore migrate --yes`. If there is no project and one is needed, use `agentcore create --name PaymentsProject --template empty`. A Strands Runtime project can instead start with `--template agent-python-strands`. +Require `agentcore --version` at v1.0 or later. Read `agentcore/agentcore.json`; a v1 project has `"version": 2` and payment managers in `payments`. Read `agentcore/aws-targets.json` and select the deployment target, account, and region for all later commands. A v0 project must be committed and migrated first: review `agentcore migrate`, then apply the reviewed plan with `agentcore migrate --yes`. If there is no project and one is needed, use `agentcore create --name PaymentsProject --template empty`. A Strands Runtime project can instead start with `--template agent-python-strands`. Choose the Runtime framework by reading its code and `runtimes[]` entry. The CLI does not wire the payment plugin into the Runtime source; Step 5 is still required. @@ -35,12 +35,13 @@ For a manual connector, replace `--quick-create` with `--credential -agentcore status --json +agentcore deploy --target +agentcore status --target --json ``` -The first deploy creates the default target with the effective AWS account and region. Check the account and region. Coinbase Quick Create reports an authorization URL; the human completes that flow and waits for the connector to become ready. v1 payment identifiers are surfaced by `agentcore status --json`, not the `payments[]` branch of `agentcore/.cli/deployed-state.json`. If an admin script cannot discover them, supply its `--manager-arn` and `--connector-id` flags. +Run `agentcore build` before deploying a Quick Create connector. For a manual connector referencing a new payment credential, the first build can fail because that credential has no deployed ARN; deploy provisions it before building the stack, then `agentcore build` can run successfully. + +If this is a new project with no targets, run `agentcore deploy --region ` to create `default`, then use `--target default`. For an existing target, use its account and region; `--region` does not change them. Coinbase Quick Create reports an authorization URL; the human completes that flow and waits for the connector to become ready. v1 payment identifiers are surfaced by `agentcore status --target --json`, not the `payments[]` branch of `agentcore/.cli/deployed-state.json`. If an admin script cannot discover them, supply its `--manager-arn` and `--connector-id` flags. ### Step 5: Wire the agent @@ -153,17 +154,18 @@ Payments are wired with a small local tool, not a framework-specific plugin — The agent calls `x402_fetch` instead of a generic HTTP tool; payment is handled inside the tool. (Tell the model, via the system prompt, to use `x402_fetch` for URLs that may require payment.) -### Step 6: Provision the per-user instrument and session (SDK script — data plane) — agent runs +### Step 6: Provision the per-user instrument (SDK script — data plane) — agent runs -The instrument (per-user wallet) and session (budget-bounded spend window) are data-plane resources — the v1 CLI can inspect them with `agentcore payment instrument` and `agentcore payment session`, while creation remains an SDK workflow. First ask the developer for the **user id** and **email** to provision the wallet for (if not already collected after Step 3b). Then run the provided script [`scripts/setup_payment_user.py`](../scripts/setup_payment_user.py) once per user. It reads the manager ARN/connector ID from `agentcore status --json` (or accepts `--manager-arn`/`--connector-id`): +The instrument (per-user wallet) and session (budget-bounded spend window) are data-plane resources — the v1 CLI can inspect them with `agentcore payment instrument` and `agentcore payment session`, while creation remains an SDK workflow. Ask the developer for the **user id** and **email** to provision the wallet. Run this skill's [`scripts/setup_payment_user.py`](../scripts/setup_payment_user.py) from the AgentCore project root, using its absolute path so the CLI can find the project. Select the same target used for deployment; the script discovers the manager ARN and connector ID from `agentcore status --target --json`: ```bash -python scripts/setup_payment_user.py --user-id alice --email alice@example.com --budget 5 +python /absolute/path/to/agents-build/scripts/setup_payment_user.py \ + --user-id alice --email alice@example.com --target ``` -It creates the instrument (with the email in `linkedAccounts`) and a budget-bounded session, then prints the `export` lines for `PAYMENT_INSTRUMENT_ID` / `PAYMENT_SESSION_ID` / `PAYMENT_USER_ID` (used in Step 8), plus the `wallet_address` and `redirect_url` (used in Step 7). The script is the canonical data-plane path — do not hand-write the SDK calls. +It creates the instrument (with the email in `linkedAccounts`) and prints its ID, wallet address, and delegation URL for Step 7. Create the time-limited session after delegation and funding in Step 8. -**LangGraph with `auto_session=True`**: If you used Step 5a with LangGraph and set `auto_session=True`, you only need the instrument from this step — skip the session creation. The middleware creates a session automatically on the first 402. You still need to run `setup_payment_user.py` for the instrument (do NOT use the --budget flag as that will create a session). +**LangGraph with `auto_session=True`**: Skip the manual session step below. The middleware creates a session on the first 402. The instrument is still required. ### Step 7: Delegation and funding (one-time per wallet) — developer does this @@ -171,7 +173,7 @@ Using the `wallet_address` / `redirect_url` the script printed: 1. **Delegation** — authorize the agent to spend from the wallet. - **Coinbase CDP**: the end user visits `redirect_url`, logs in, and grants permissions to `wallet_address`. - - **Stripe Privy**: Delegation requires a frontend app where the end user authenticates with Privy and approves the agent as a session signer on their wallets. Use the reference frontend at . The agent automates the mechanical parts (7a, 7c, 7f); the developer handles the two steps that touch secrets or the dashboard (7b, 7d) and the browser approval (7e). No credential is requested twice — 7b reuses what Step 3b already captured: + - **Stripe Privy**: Delegation requires a frontend app where the end user authenticates with Privy and approves the agent as a session signer on their wallets. Use the reference frontend at . The agent automates the mechanical parts (7a, 7c, 7f); the developer handles the two steps that touch secrets or the dashboard (7b, 7d) and the browser approval (7e). Configure the frontend for the same Privy app and authorization key used by the manual payment credential in Step 1: **7a. Clone and install the delegation frontend — agent runs:** @@ -181,56 +183,13 @@ Using the `wallet_address` / `redirect_url` the script printed: pnpm install ``` - **7b. Configure environment — reuse the Step 3b credentials. The DEVELOPER runs this, not the agent.** - - **Do not ask the developer to re-provide the Privy credentials.** All three were already captured when the payment connector was created in Step 3b, and the CLI wrote them to `agentcore/.env.local` in the agent project. - - **The connector's key names are namespaced — match on the suffix, not the full name.** `agentcore add payment-connector` does not write bare `STRIPE_PRIVY_*` keys. It writes one key per secret in the form: - - ```text - AGENTCORE_CREDENTIAL___STRIPE_PRIVY_ - ``` - - `` and `` are the names the developer chose in Step 3b, so the fully-qualified key names are different in every project. Never search for a hardcoded full name — match on the `_STRIPE_PRIVY_` **suffix**: - - | Frontend `.env.local` variable | Suffix to match in `agentcore/.env.local` | Visibility | - |----------|--------|------------| - | `NEXT_PUBLIC_PRIVY_APP_ID` | `*_STRIPE_PRIVY_APP_ID` | Public (client) | - | `PRIVY_APP_SECRET` | `*_STRIPE_PRIVY_APP_SECRET` | Server-only | - | `NEXT_PUBLIC_PRIVY_SIGNER_ID` | `*_STRIPE_PRIVY_AUTHORIZATION_ID` | Public (identifier only) | - | `NEXT_PUBLIC_NETWORK_MODE` | not a connector credential — set `testnet` for Base Sepolia / Solana Devnet, `mainnet` for production | Public | - - The frontend does **not** need the authorization private key — only the connector signs, so `*_STRIPE_PRIVY_AUTHORIZATION_PRIVATE_KEY` stays where it is. + **7b. Configure environment. The DEVELOPER runs this, not the agent.** - `PRIVY_APP_SECRET` is a real secret and `agentcore/.env.local` holds it in plaintext, so the **developer** runs the commands below — the agent must not read that file (same rule as Step 3b). + In the frontend project, copy `.env.example` to `.env.local`. Set `NEXT_PUBLIC_PRIVY_APP_ID` to the App ID used with `agentcore add credentials payment --provider StripePrivy`, `PRIVY_APP_SECRET` to that app's secret, and `NEXT_PUBLIC_PRIVY_SIGNER_ID` to the same authorization ID. Get the values from the original credential inputs or the Privy Dashboard under **Configuration > App settings** and **Wallet infrastructure > Authorization keys**. Set `NEXT_PUBLIC_NETWORK_MODE=testnet` for Base Sepolia or Solana Devnet, or `mainnet` for production. The frontend does not need the authorization private key. - First confirm the keys are there. This prints key **names** only, never a value — substitute the absolute path to the agent project: + Do not copy `agentcore/.env.local` into the frontend. The v1 CLI names entries after the **credential provider name** (`AGENTCORE_CREDENTIAL__`) and encrypts secret values there; a text copy would give the frontend unusable ciphertext. The developer should enter the values directly in the frontend's local configuration without posting them to the agent. - ```bash - grep -oE '^[^=]*_STRIPE_PRIVY_[A-Z_]+' /absolute/path/to/agent-project/agentcore/.env.local - ``` - - Expect four lines ending in `_APP_ID`, `_APP_SECRET`, `_AUTHORIZATION_PRIVATE_KEY`, and `_AUTHORIZATION_ID`. If it prints nothing, skip to the dashboard fallback below. - - Then write the frontend's `.env.local`. One command, absolute paths on both sides (relative paths and `cd` are what break here — the two projects are different directories), no values printed: - - ```bash - sed -nE 's/^[^=]*_STRIPE_PRIVY_APP_ID=/NEXT_PUBLIC_PRIVY_APP_ID=/p;s/^[^=]*_STRIPE_PRIVY_APP_SECRET=/PRIVY_APP_SECRET=/p;s/^[^=]*_STRIPE_PRIVY_AUTHORIZATION_ID=/NEXT_PUBLIC_PRIVY_SIGNER_ID=/p' /absolute/path/to/agent-project/agentcore/.env.local > /absolute/path/to/agentcore-privy-frontend/.env.local && printf 'NEXT_PUBLIC_NETWORK_MODE=testnet\n' >> /absolute/path/to/agentcore-privy-frontend/.env.local - ``` - - It writes the file outright, so there is no need to `cp .env.example .env.local` first and no duplicate keys to reason about. Verify by listing the key names — again no values: - - ```bash - cut -d= -f1 /absolute/path/to/agentcore-privy-frontend/.env.local - ``` - - All four of `NEXT_PUBLIC_PRIVY_APP_ID`, `PRIVY_APP_SECRET`, `NEXT_PUBLIC_PRIVY_SIGNER_ID`, `NEXT_PUBLIC_NETWORK_MODE` must be present. Fewer than four means a suffix didn't match — use the fallback rather than a partial file. - - **Dashboard fallback.** If `agentcore/.env.local` is missing or the suffixes don't match — the connector was created on another machine, or the CLI's key format changed — fill in the values by hand instead. `cp .env.example .env.local` in the frontend, then take the App ID and App Secret from the Privy Dashboard under **Configuration > App settings**, and the signer ID from **Wallet infrastructure > Authorization keys**. It must be the **same app and same authorization key** used in Step 3b. - - The `NEXT_PUBLIC_PRIVY_SIGNER_ID` is the Authorization Key ID (looks like `zr17anh9dpiqno1iaref9jpx`) — the same key whose private key went to the payment connector. It is safe to expose publicly (it's an identifier, not a secret). - - > **Important:** Taking these values straight out of `agentcore/.env.local` is what guarantees the frontend uses the same Privy app and authorization key as the connector. If they are set by hand and diverge, delegation succeeds but payments fail with "Wallet policy denied the transaction." + The `NEXT_PUBLIC_PRIVY_SIGNER_ID` is the Authorization Key ID for the same key whose private key went to the payment credential. If the app or key IDs differ, delegation can succeed while payments fail. **7c. Start the frontend — agent runs:** @@ -258,7 +217,7 @@ Using the `wallet_address` / `redirect_url` the script printed: > **Check what's already in the field first.** Privy's default is permissive — an app with an **empty** allowed-origins list accepts every origin, so delegation works without this step. Adding the first entry switches the app to allowlist-only: > - > - **Dedicated AgentCore app** (what Step 3b recommends): the list is normally empty and nothing else uses the App ID, so adding `http://localhost:3000` is safe. Remember to also add the deployed origin before going to production, or the hosted frontend will break. + > - **Dedicated AgentCore app**: the list is normally empty and nothing else uses the App ID, so adding `http://localhost:3000` is safe. Remember to also add the deployed origin before going to production, or the hosted frontend will break. > - **Shared app** with existing entries: append the localhost URL, don't replace the list. Remove it again once delegation testing is done. **7e. Complete delegation — developer does this in browser:** @@ -275,12 +234,12 @@ Using the `wallet_address` / `redirect_url` the script printed: **7f. Verify delegation — agent can validate:** - After the developer confirms delegation is complete, the agent can verify by calling the same check-signers endpoint the frontend uses: + After the developer confirms delegation is complete, the agent can verify by calling the same check-signers endpoint the frontend uses. Obtain the Privy wallet IDs from the authenticated frontend session; the payment instrument ID and wallet address printed in Step 6 are different identifiers: ```bash curl -s -X POST http://localhost:3000/api/check-signers \ -H "Content-Type: application/json" \ - -d '{"walletIds": [""]}' | python3 -m json.tool + -d '{"walletIds": [""]}' | python3 -m json.tool ``` Expected: `{"connected": true}`. If `false`, the developer needs to repeat step 7e. @@ -291,12 +250,22 @@ Using the `wallet_address` / `redirect_url` the script printed: - **x402 (Base Sepolia)**: send testnet USDC via the Circle faucet (). - **MPP on Tempo Moderato** (StripePrivy instruments only): send the canonical test stablecoin **pathUSD** via the Tempo faucet (). AgentCore accepts only the network's canonical token — the faucet's other stablecoins (AlphaUSD/BetaUSD/ThetaUSD) are rejected with `MPP_EVM_NON_CANONICAL_USDC`. For buyer-pays challenges (`buyerPaysGasFees: true`) the wallet also needs native Tempo gas. See [MPP networks, providers, and funding](#mpp-networks-providers-and-funding). -### Step 8: Set env vars and test — agent runs +### Step 8: Create a session, then set env vars and test -Set the tool's config from the `export` lines the Step 6 script printed — it emits all of them (`PAYMENT_MANAGER_ARN`, `PAYMENT_INSTRUMENT_ID`, `PAYMENT_SESSION_ID`, `PAYMENT_USER_ID`, `AWS_REGION`), so just copy them into the agent's environment: +After the end user completes delegation and funding, create a bounded session for the **same user ID** without creating another instrument. Use the instrument ID printed in Step 6: ```bash -export PAYMENT_MANAGER_ARN=... # all five printed by setup_payment_user.py +python /absolute/path/to/agents-build/scripts/setup_payment_user.py \ + --user-id alice --session-only --instrument-id \ + --budget 5 --target +``` + +The SDK script prints `PAYMENT_SESSION_ID` and the same instrument ID. With LangGraph `auto_session=True`, skip this command; the middleware creates the session on the first 402. + +Set the tool's config from the `export` lines the Step 6 and Step 8 script runs printed (`PAYMENT_MANAGER_ARN`, `PAYMENT_INSTRUMENT_ID`, `PAYMENT_SESSION_ID`, `PAYMENT_USER_ID`, `AWS_REGION`): + +```bash +export PAYMENT_MANAGER_ARN=... export PAYMENT_INSTRUMENT_ID=... export PAYMENT_SESSION_ID=... export PAYMENT_USER_ID=... @@ -348,7 +317,7 @@ For the generic `x402_fetch` tool (Step 5b), pass `permit2_allowance_limit="..." - The `authorizationUrl` is valid for only **10 minutes** and is **single-use** — this error means it expired, was already used, or was consumed by a link preview before you clicked it. - **Link unfurling is the most common cause**: pasting the URL into Slack/Teams/chat (or letting the agent fetch it) fires a preview request that spends the one-time consent session. Share the URL as plain text and open it directly in a browser, once, promptly. -- Check the connector with `GetPaymentConnector` (e.g. `aws bedrock-agentcore-control get-payment-connector --payment-manager-id --payment-connector-id --region `) and poll until the status is terminal: `READY` = it already succeeded (no action); `AUTHENTICATION_EXPIRED`/`AUTHENTICATION_FAILED` = the consent window lapsed or failed — delete the connector and recreate it via QuickCreate to get a fresh URL; `AWS_MARKETPLACE_SUBSCRIPTION_REQUIRED` = subscribe to "Coinbase Wallets for AgentCore Payments" then recreate (on the CLI deploy path the manager may have rolled back — see Step 4 recovery); `CREATING`/`PENDING_AUTHENTICATION`/`PROVISIONING` = still in progress, keep polling. +- Check the connector with `GetPaymentConnector` (e.g. `aws bedrock-agentcore-control get-payment-connector --payment-manager-id --payment-connector-id --region `) and poll until the status is terminal: `READY` = it already succeeded (no action); `AUTHENTICATION_EXPIRED`/`AUTHENTICATION_FAILED` = the consent window lapsed or failed — delete the connector and recreate it via QuickCreate to get a fresh URL; `AWS_MARKETPLACE_SUBSCRIPTION_REQUIRED` = subscribe to "Coinbase Wallets for AgentCore Payments" then recreate (on the CLI deploy path the manager may have rolled back — inspect the deployment failure first); `CREATING`/`PENDING_AUTHENTICATION`/`PROVISIONING` = still in progress, keep polling. **Agent sees 402 but does not pay:** @@ -420,7 +389,7 @@ For the generic `x402_fetch` tool (Step 5b), pass `permit2_allowance_limit="..." **MPP on Tempo: `ProcessPayment` fails with "Tempo payments are not supported for Coinbase-managed payment instruments":** -- Tempo requires a **StripePrivy** connector/instrument. Provision the connector with `--provider StripePrivy` (Step 3b) and create the instrument on it — Coinbase-managed instruments cannot pay Tempo at all. +- Tempo requires a **StripePrivy** connector/instrument. Add a StripePrivy payment credential in Step 1, then create the connector with `--credential ` in Step 2 and create the instrument on it — Coinbase-managed instruments cannot pay Tempo at all. **ProcessPayment fails with "Payment session not found":** @@ -526,9 +495,9 @@ For the generic `x402_fetch` tool (Step 5b), pass `permit2_allowance_limit="..." **Stripe Privy: Delegation frontend setup issues:** - **Login fails, the Privy modal won't open, or the browser console shows an origin/CORS or "not a valid origin for this app" error**: the local origin is not allowlisted. Add the dev server's exact URL under Privy Dashboard > Configuration > App settings > Domains > Allowed origins > Web & mobile web (Step 7d). Privy matches the browser **origin**, so `http://localhost:3000` works but `http://localhost:3000/` (trailing slash) and `http://localhost` (no port) do not. If the dev server moved off port 3000, the allowlisted port must move with it. -- **"Missing server configuration" from /api/check-signers**: One or more env vars (`NEXT_PUBLIC_PRIVY_APP_ID`, `PRIVY_APP_SECRET`, `NEXT_PUBLIC_PRIVY_SIGNER_ID`) are not set in the frontend's `.env.local`. Map them from the `*_STRIPE_PRIVY_APP_ID` / `*_STRIPE_PRIVY_APP_SECRET` / `*_STRIPE_PRIVY_AUTHORIZATION_ID` keys in `agentcore/.env.local` (Step 7b). Two things make a straight file copy fail: the frontend uses different key names, and the connector's keys are namespaced `AGENTCORE_CREDENTIAL___STRIPE_PRIVY_*` — so grep the suffix, not the bare name. `cut -d= -f1` on the frontend's `.env.local` shows which keys actually landed. +- **"Missing server configuration" from /api/check-signers**: One or more env vars (`NEXT_PUBLIC_PRIVY_APP_ID`, `PRIVY_APP_SECRET`, `NEXT_PUBLIC_PRIVY_SIGNER_ID`) are not set in the frontend's `.env.local`. Set them from the same Privy app and authorization key used for the payment credential in Step 1, as described in Step 7b. `cut -d= -f1` on the frontend's `.env.local` shows which keys are present without printing their values. - **Login fails or no wallets appear**: The Privy app may not have embedded wallets enabled. In Privy Dashboard > Wallet Infrastructure, ensure embedded wallets are configured for the relevant chains (Ethereum/Solana). -- **"Give access" succeeds but payments still fail with "Wallet policy denied"**: The `NEXT_PUBLIC_PRIVY_SIGNER_ID` in the frontend doesn't match the Authorization ID used in the payment connector (Step 3b). Re-derive it from the `*_STRIPE_PRIVY_AUTHORIZATION_ID` key in `agentcore/.env.local` rather than retyping it from the dashboard. +- **"Give access" succeeds but payments still fail with "Wallet policy denied"**: The `NEXT_PUBLIC_PRIVY_SIGNER_ID` in the frontend may not match the Authorization ID used by the StripePrivy payment credential. Have the developer compare the IDs against the original credential input or Privy Dashboard, without exposing the secret or authorization private key to the agent. - **User logged in with wrong email**: The email must match the one passed to `setup_payment_user.py --email`. If mismatched, the instrument points to a different Privy user's wallets. Log out, log back in with the correct email. - **Port conflict**: If the agent's own server is on port 3000, the frontend auto-selects another port. Check the terminal output for the actual URL — and allowlist that port (Step 7d), otherwise login fails. diff --git a/plugins/aws-agents/skills/agents-build/scripts/setup_payment_user.py b/plugins/aws-agents/skills/agents-build/scripts/setup_payment_user.py index 308ef70d4..9b2d08d42 100644 --- a/plugins/aws-agents/skills/agents-build/scripts/setup_payment_user.py +++ b/plugins/aws-agents/skills/agents-build/scripts/setup_payment_user.py @@ -7,10 +7,13 @@ - optionally one budget-bounded payment session Usage: - python setup_payment_user.py --user-id alice --email alice@example.com [--budget 5] \ - [--manager-arn ...] [--connector-id ...] [--region us-east-1] [--network ETHEREUM] + python setup_payment_user.py --user-id alice --email alice@example.com \ + [--manager-arn ...] [--connector-id ...] [--target staging] + python setup_payment_user.py --user-id alice --session-only \ + --instrument-id --budget 5 [--target staging] -Manager ARN / connector ID are read from `agentcore status --json` for v1 projects. +Manager ARN / connector ID are read from `agentcore status --target --json` +for v1 projects. The default target is "default". """ import argparse import json @@ -19,12 +22,12 @@ import sys from pathlib import Path -def _from_deployed_state(): +def _from_deployed_state(target="default"): """Best-effort payment discovery from v1 status, with a v0 state fallback.""" if Path("agentcore/agentcore.json").exists(): try: result = subprocess.run( - ["agentcore", "status", "--json"], + ["agentcore", "status", "--target", target, "--json"], capture_output=True, text=True, timeout=30, @@ -48,9 +51,9 @@ def _from_deployed_state(): data = json.loads(path.read_text()) payments = None targets = data.get("targets") or {} - target = targets.get("default") or (next(iter(targets.values()), {}) if targets else {}) - if isinstance(target, dict): - payments = (target.get("resources") or {}).get("payments") + legacy_target = targets.get(target) or {} + if isinstance(legacy_target, dict): + payments = (legacy_target.get("resources") or {}).get("payments") if not payments: payments = data.get("payments") # legacy/top-level fallback if not payments: @@ -68,45 +71,61 @@ def _from_deployed_state(): def main(): ap = argparse.ArgumentParser(description="Provision a per-user AgentCore Payments instrument") ap.add_argument("--user-id", required=True, help="Stable end-user identifier") - ap.add_argument("--email", required=True, help="End-user email (linked to the wallet; required for delegation)") + ap.add_argument("--email", help="End-user email (required when creating an instrument)") ap.add_argument("--budget", default=None, help="Optional session spend cap in USD, e.g. 5") + ap.add_argument("--session-only", action="store_true", + help="Create a session for an existing instrument after delegation and funding") + ap.add_argument("--instrument-id", help="Existing instrument ID (required with --session-only)") ap.add_argument("--expiry-minutes", type=int, default=60, help="Session expiry, 15-480") ap.add_argument("--network", default="ETHEREUM", help="Wallet network family: ETHEREUM or SOLANA") ap.add_argument("--manager-arn", default=os.environ.get("PAYMENT_MANAGER_ARN")) ap.add_argument("--connector-id", default=os.environ.get("PAYMENT_CONNECTOR_ID")) - ap.add_argument("--region", default=os.environ.get("AWS_REGION")) + ap.add_argument("--region", help="SDK region override; defaults to the payment manager ARN's region") + ap.add_argument("--target", default=os.environ.get("AGENTCORE_TARGET", "default"), + help="AgentCore deployment target used for status discovery") args = ap.parse_args() + if args.session_only: + if not args.instrument_id or not args.budget: + ap.error("--session-only requires --instrument-id and --budget") + elif not args.email: + ap.error("--email is required when creating an instrument") manager_arn, connector_id = args.manager_arn, args.connector_id if not manager_arn or not connector_id: - ds_arn, ds_conn = _from_deployed_state() + ds_arn, ds_conn = _from_deployed_state(args.target) manager_arn = manager_arn or ds_arn connector_id = connector_id or ds_conn - if not manager_arn or not connector_id: - sys.exit("Could not resolve manager ARN / connector ID. Pass --manager-arn and --connector-id, " - "or run from a deployed v1 project directory and check `agentcore status --json`.") + if not manager_arn or (not args.session_only and not connector_id): + sys.exit("Could not resolve the payment manager ARN or required connector ID. Pass " + "--manager-arn and, when creating an instrument, --connector-id; or check " + "`agentcore status --target --json` in the deployed project.") from bedrock_agentcore.payments import PaymentManager region = args.region or manager_arn.split(":")[3] manager = PaymentManager(payment_manager_arn=manager_arn, region_name=region) - # Data plane: per-user instrument (wallet). Email -> linkedAccounts. - instrument = manager.create_payment_instrument( - user_id=args.user_id, - payment_connector_id=connector_id, - payment_instrument_type="EMBEDDED_CRYPTO_WALLET", - payment_instrument_details={ - "embeddedCryptoWallet": { - "network": args.network, - "linkedAccounts": [{"email": {"emailAddress": args.email}}], - } - }, - ) - instrument_id = instrument["paymentInstrumentId"] - wallet = instrument.get("paymentInstrumentDetails", {}).get("embeddedCryptoWallet", {}) - wallet_address = wallet.get("walletAddress") - redirect_url = wallet.get("redirectUrl") # Coinbase delegation URL; None for Privy + wallet_address = None + redirect_url = None + if args.session_only: + instrument_id = args.instrument_id + else: + # Data plane: per-user instrument (wallet). Email -> linkedAccounts. + instrument = manager.create_payment_instrument( + user_id=args.user_id, + payment_connector_id=connector_id, + payment_instrument_type="EMBEDDED_CRYPTO_WALLET", + payment_instrument_details={ + "embeddedCryptoWallet": { + "network": args.network, + "linkedAccounts": [{"email": {"emailAddress": args.email}}], + } + }, + ) + instrument_id = instrument["paymentInstrumentId"] + wallet = instrument.get("paymentInstrumentDetails", {}).get("embeddedCryptoWallet", {}) + wallet_address = wallet.get("walletAddress") + redirect_url = wallet.get("redirectUrl") # Coinbase delegation URL; None for Privy # Data plane: optional budget-bounded session. NOTE: cap key is "value", not "amount". session_id = None @@ -128,12 +147,13 @@ def main(): print(f" export PAYMENT_SESSION_ID={session_id}") print(f" export PAYMENT_USER_ID={args.user_id}") print(f" export AWS_REGION={region}") - print("\nOne-time per wallet:") - if redirect_url: - print(f" 1. Delegation (Coinbase): visit {redirect_url}, log in, grant access to {wallet_address}") - else: - print(" 1. Delegation (Privy): approve delegation via the Privy frontend SDK") - print(f" 2. Funding: send testnet USDC to {wallet_address} via https://faucet.circle.com/ (Base Sepolia)") + if not args.session_only: + print("\nOne-time per wallet:") + if redirect_url: + print(f" 1. Delegation (Coinbase): visit {redirect_url}, log in, grant access to {wallet_address}") + else: + print(" 1. Delegation (Privy): approve delegation via the Privy frontend SDK") + print(f" 2. Funding: send testnet USDC to {wallet_address} via https://faucet.circle.com/ (Base Sepolia)") if __name__ == "__main__": From 9796051f074786059e5420d630b5064682d8e126 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Thu, 29 Oct 2026 12:50:07 -0400 Subject: [PATCH 06/10] fix(aws-agents): persist selected target on gateway and policy operations --- .../aws-agents/skills/agents-connect/SKILL.md | 20 ++++++++++++------- .../agents-connect/references/policy.md | 15 +++++++------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/plugins/aws-agents/skills/agents-connect/SKILL.md b/plugins/aws-agents/skills/agents-connect/SKILL.md index 480b665a5..9176a8125 100644 --- a/plugins/aws-agents/skills/agents-connect/SKILL.md +++ b/plugins/aws-agents/skills/agents-connect/SKILL.md @@ -32,7 +32,7 @@ For an app calling your agent, use [agents-build integration](../agents-build/re Run `agentcore --version`; require v1.0.0 or later. If the CLI is older, upgrade with `npm install -g @aws/agentcore@latest` and recheck. Read `agentcore/agentcore.json`. Version 2 is the v1 project format. If it is version 1, commit the project, review `agentcore migrate`, then apply the reviewed plan with `agentcore migrate --yes`. -Locate the project Gateway under `agentCoreGateways` and the Runtime or Harness that will call it. If there is no project, first create one using `agentcore create --name --template empty`, or work in the user's existing project. Do not create a second project without preserving their current work. +Locate the project Gateway under `agentCoreGateways` and the Runtime or Harness that will call it. Read `agentcore/aws-targets.json` and choose the deployment target before running status or deploy commands. Carry that target through every subsequent command. If there is no project, first create one using `agentcore create --name --template empty`, or work in the user's existing project. Do not create a second project without preserving their current work. ### 2. Choose a target @@ -45,7 +45,7 @@ Locate the project Gateway under `agentCoreGateways` and the Runtime or Harness | Curated Knowledge Base or web search connector | `agentcore add gateway-connector --gateway --name --connector `; add `--knowledge-base ` for a Knowledge Base | | AWS SDK call using the Runtime's role | Call the AWS SDK directly from agent code | -The v1 shortcut uses `--endpoint` or `--runtime`. The v0 `--type`, `--lambda-arn`, `--schema`, and `--tool-schema-file` flags are gone. For full target configuration, include `name`, `targetType`, and the type's required fields in the JSON file. Do not combine `--target-configuration` with shortcut `--name` or `--outbound-auth` flags; authentication goes inside the JSON object in that form. Run `agentcore add gateway-target --help` and `agentcore build` when adapting a target schema. +The v1 shortcut uses `--endpoint` or `--runtime`. The v0 `--type`, `--lambda-arn`, `--schema`, and `--tool-schema-file` flags are gone. For full target configuration, include `name`, `targetType`, and the type's required fields in the JSON file. Do not combine `--target-configuration` with shortcut `--name` or `--outbound-auth` flags; authentication goes inside the JSON object in that form. Run `agentcore add gateway-target --help` when adapting a target schema. `agentcore build` validates a target after its referenced credentials have been deployed. ### 3. Create an authenticated Gateway @@ -126,19 +126,25 @@ Do not add `outboundAuth` to a `lambdaFunctionArn` target: the gateway role invo ### 5. Build, deploy, and wire the agent ```bash -agentcore build +# First deployment when aws-targets.json has no entries: agentcore deploy --region -agentcore status --json +agentcore status --target default --json + +# For an existing target instead, use: +agentcore deploy --target +agentcore status --target --json ``` -On the first deploy, the CLI creates the default target using the effective AWS account and region; subsequent deploys can select `--target `. Read the deployed Gateway URL from `agentcore status --json`. The deployed Runtime receives `AGENTCORE_GATEWAY__URL`; check the exact name in its generated README or environment. In local `agentcore dev`, this deployed URL is absent unless supplied explicitly. The agent framework still needs MCP client wiring to discover and call the Gateway's tools. +If the project has no new credential providers, run `agentcore build` before deploying. With a new provider referenced by a Gateway target, the first `agentcore build` can fail because the provider has no deployed ARN yet; `agentcore deploy` provisions the credential before building the stack. Run `agentcore build` after that first deploy. + +On the first deploy, the CLI creates the default target using the effective AWS account and region. `--region` does not change an existing target's region. Read the deployed Gateway URL from `agentcore status --target --json`. The deployed Runtime receives `AGENTCORE_GATEWAY__URL`; check the exact name in its generated README or environment. In local `agentcore dev`, this deployed URL is absent unless supplied explicitly. The agent framework still needs MCP client wiring to discover and call the Gateway's tools. A Harness uses a Gateway tool entry in `app//harness.yaml`, referencing the deployed Gateway ARN. Its config and a Runtime's MCP client code are different paths; use the one matching the project. ### 6. Verify and troubleshoot -- Inspect `agentcore status --json` and `agentcore gateway target list --gateway-id --json` for deployed resources. -- For a Runtime error, use `agentcore logs runtime --name --since 1h --level error`; for a Harness, use `agentcore logs harness --name --since 1h --level error`. +- Inspect `agentcore status --target --json` and `agentcore gateway target list --gateway-id --region --json` for deployed resources. +- For a Runtime error, use `agentcore logs runtime --name --target --since 1h --level error`; for a Harness, use `agentcore logs harness --name --target --since 1h --level error`. - Compare the target's `outboundAuth` type and credential name with its provider in `agentcore.json`. A Gateway URL alone does not tell you the outbound auth state. - For Cedar authorization, load [references/policy.md](references/policy.md). diff --git a/plugins/aws-agents/skills/agents-connect/references/policy.md b/plugins/aws-agents/skills/agents-connect/references/policy.md index cf163a45e..3464d6f31 100644 --- a/plugins/aws-agents/skills/agents-connect/references/policy.md +++ b/plugins/aws-agents/skills/agents-connect/references/policy.md @@ -4,7 +4,7 @@ Use Cedar policy engines to authorize Gateway tool calls. Policies act at the Ga ## Inspect the project -Read `agentcore/agentcore.json` for `agentCoreGateways`, `policyEngines`, and each Gateway's `policyEngineConfiguration`. For a v0 project (`"version": 1`), commit, review `agentcore migrate`, then apply the reviewed plan with `agentcore migrate --yes` before continuing. Require CLI v1.0.0 or later. +Read `agentcore/agentcore.json` for `agentCoreGateways`, `policyEngines`, and each Gateway's `policyEngineConfiguration`. Read `agentcore/aws-targets.json`, select the intended deployment target, and use it for every deploy, status, and Gateway command below. For a v0 project (`"version": 1`), commit, review `agentcore migrate`, then apply the reviewed plan with `agentcore migrate --yes` before continuing. Require CLI v1.0.0 or later. Choose a Gateway and list the tool names and caller identities that should be allowed. When an attached engine enforces decisions, write explicit `permit` policies for desired calls. A matching `forbid` overrides a `permit`. @@ -19,15 +19,15 @@ agentcore add policy-engine \ --attach-to-gateways MyGateway \ --attach-mode log-only agentcore build -agentcore deploy --region -agentcore status --json +agentcore deploy --target +agentcore status --target --json ``` -`--attach-mode` accepts `log-only` or `enforce`; the v0 `LOG_ONLY` and `ENFORCE` flag values are not the v1 spelling. For an existing Gateway with no engine, `agentcore add policy-engine --attach-to-gateways MyGateway` can attach it. To change a Gateway's existing mode, edit its `policyEngineConfiguration` in `agentcore/agentcore.json`, run `agentcore build`, then deploy. `agentcore add gateway` creates a Gateway; it does not update an existing one. Use `--policy-engine-name` and `--policy-engine-mode` when creating a new Gateway around an engine already declared in the project. +If this is the project's first deployment and `aws-targets.json` is empty, use `agentcore deploy --region ` to create `default`, then use `--target default` on later commands. A named target must already be defined in `aws-targets.json`. `--attach-mode` accepts `log-only` or `enforce`; the v0 `LOG_ONLY` and `ENFORCE` flag values are not the v1 spelling. For an existing Gateway with no engine, `agentcore add policy-engine --attach-to-gateways MyGateway` can attach it. To change a Gateway's existing mode, edit its `policyEngineConfiguration` in `agentcore/agentcore.json`, run `agentcore build`, then deploy to the same target. `agentcore add gateway` creates a Gateway; it does not update an existing one. Use `--policy-engine-name` and `--policy-engine-mode` when creating a new Gateway around an engine already declared in the project. ## Write a Cedar policy -A policy referencing the deployed Gateway ARN requires the first deploy. Obtain that ARN from `agentcore status --json`. Save the Cedar statement to a file, review it, and add it to the project: +A policy referencing the deployed Gateway ARN requires the first deploy. Obtain that ARN from `agentcore status --target --json`. Save the Cedar statement to a file, review it, and add it to the project: ```bash agentcore add policy \ @@ -37,7 +37,7 @@ agentcore add policy \ --validation-mode fail-on-any-findings \ --enforcement-mode log-only agentcore build -agentcore deploy +agentcore deploy --target ``` `--statement` accepts inline text, `file://`, or `-` for stdin. The v0 `--source` and `-g` flags are gone. If the policy checks an output, set `--authorization-phase return-output`; otherwise the CLI infers the phase from the statement. Tool action names use `AgentCore::Action::"TargetName___tool_name"` (three underscores). Confirm the name from the deployed Gateway target and test the intended principal, tool, and arguments. @@ -48,6 +48,7 @@ For natural language generation, use the Gateway resource command with its deplo agentcore gateway policy generate \ --gateway-id \ --policy-engine-id \ + --region \ --prompt "Allow the refund tool only for approved operators" ``` @@ -57,6 +58,6 @@ Review the generated Cedar and save the chosen statement to `policy.cedar`; add Start with the Gateway's engine in `log-only` and the policy in `log-only`. Exercise allowed and denied tool calls and inspect policy decisions in the service's policy decision logs. `agentcore logs runtime` and `agentcore logs harness` show agent application logs, not necessarily the Gateway policy decision stream. -After checking the expected decisions, set the Gateway's `policyEngineConfiguration` mode to `ENFORCE` and the desired policy's `enforcementMode` to `ACTIVE` in `agentcore/agentcore.json`, then run `agentcore build` and `agentcore deploy`. Confirm the actual enum values against the v1 project schema and CLI output before editing. Test an allowed call and a denied call against the deployed Gateway. +After checking the expected decisions, set the Gateway's `policyEngineConfiguration` mode to `ENFORCE` and the desired policy's `enforcementMode` to `ACTIVE` in `agentcore/agentcore.json`, then run `agentcore build` and `agentcore deploy --target `. Confirm the actual enum values against the v1 project schema and CLI output before editing. Test an allowed call and a denied call against the deployed Gateway. If a call is denied unexpectedly, check the policy's action name, Gateway ARN, principal type, authorization phase, and engine attachment. If every call is denied, check that at least one applicable `permit` is active. Do not solve a denial by changing the Gateway's inbound auth to `NONE`. From fcb7e31ed8b3910d4da045448ee8037e27ec2c13 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Thu, 29 Oct 2026 12:50:28 -0400 Subject: [PATCH 07/10] fix(aws-agents): fix payment admin setup instructions --- plugins/aws-agents/skills/agents-pay/SKILL.md | 21 ++++++++++++++- .../agents-pay/references/openclaw-setup.md | 22 +++++++++++++--- .../agents-pay/references/operator-guide.md | 8 ++++++ .../skills/agents-pay/references/setup.md | 26 ++++++++++++++----- .../agents-pay/scripts/agents_pay_admin.py | 18 +++++++++++-- 5 files changed, 81 insertions(+), 14 deletions(-) diff --git a/plugins/aws-agents/skills/agents-pay/SKILL.md b/plugins/aws-agents/skills/agents-pay/SKILL.md index 93717d7df..c7b2be46b 100644 --- a/plugins/aws-agents/skills/agents-pay/SKILL.md +++ b/plugins/aws-agents/skills/agents-pay/SKILL.md @@ -218,6 +218,7 @@ files (`../other-skill/...`) can silently break. Everything needed is here. ### Step 0: Prerequisites ```bash +cd /absolute/path/to/agents-pay python3 --version # 3.9+ python3 -m venv .venv source .venv/bin/activate @@ -227,6 +228,8 @@ agentcore --version `bedrock_agentcore.payments` must be importable. Verify: `python -c "from bedrock_agentcore.payments import PaymentManager"`. +Run the next step from the AgentCore project root; create a project and enter it +first if needed. ### Step 1: Provision payment resources — human runs this outside the LLM loop @@ -255,6 +258,22 @@ providers, follow the credential steps in the setup reference. `agentcore/.env.l may hold provider secrets until deploy and must remain gitignored and unread by the agent. +For an existing project, choose a target from `agentcore/aws-targets.json` and +use `agentcore deploy --target ` and `agentcore status --target --json` +in place of the default-target commands above. Before the admin steps, the human +records the project root and selected target, then runs the bundled script from +its installed skill directory: + +```bash +export AGENTCORE_PROJECT_DIR="$PWD" +export AGENTCORE_TARGET= +export AWS_REGION= +cd /absolute/path/to/agents-pay +``` + +Use a separate admin config path for each target. The script is in the skill +directory, not in the AgentCore project. + ### Step 2: Write the payment policy — human runs this Until this file exists, every payment is refused. There is no permissive default. @@ -297,7 +316,7 @@ script, so a look-alike contract cannot be pasted in. python3 scripts/agents_pay_admin.py create-instrument --email you@example.com ``` -The manager ARN and connector ID are read from `agentcore status --json` for a v1 project. Run from the project directory, or pass `--manager-arn` / `--connector-id`. +The manager ARN and connector ID are read from `agentcore status --target --json` for a v1 project using `AGENTCORE_PROJECT_DIR` and `AGENTCORE_TARGET`. Otherwise pass `--manager-arn` / `--connector-id`. It prints the wallet address, the delegation URL, and the `export` lines for the runtime. Delegation and funding are then done by the **end user** — see diff --git a/plugins/aws-agents/skills/agents-pay/references/openclaw-setup.md b/plugins/aws-agents/skills/agents-pay/references/openclaw-setup.md index ef89b11a8..a7316500b 100644 --- a/plugins/aws-agents/skills/agents-pay/references/openclaw-setup.md +++ b/plugins/aws-agents/skills/agents-pay/references/openclaw-setup.md @@ -18,8 +18,11 @@ instrument. export PAYMENT_USER_ID="my-openclaw-agent" ``` -Pass `--user-id $PAYMENT_USER_ID` to every admin command, or run `init-config` -first (which generates and stores one in `~/.agents-pay/config.json` for you). +For manual setup, pass `--user-id "$PAYMENT_USER_ID"` to `init-config`; later +commands read the stored ID. For the interactive wizard, enter this ID at its +User ID prompt. Alternatively, let either flow generate an ID and use that +same stored ID in the OpenClaw configuration. Exporting `PAYMENT_USER_ID` alone +does not set the ID used by `init-config` or the wizard. ## Install @@ -45,6 +48,7 @@ agentcore add payment-connector --manager MyPayments --name Coinbase --quick-cre agentcore build agentcore deploy --region export AGENTCORE_PROJECT_DIR="$PWD" +export AGENTCORE_TARGET=default ``` Quick Create needs no provider secrets. Complete the Coinbase authorization outside the OpenClaw conversation. For manual provider credentials, follow [setup.md](setup.md). Keep `AGENTCORE_PROJECT_DIR` set while running the setup @@ -77,15 +81,26 @@ If payment infrastructure or a session does not exist, stop the LLM workflow. Open a separate terminal and run the human setup from the bundled skill: ```bash +export PAYMENT_USER_ID="my-openclaw-agent" +export AGENTCORE_PROJECT_DIR="/absolute/path/to/OpenclawPayments" +export AGENTCORE_TARGET=default +export AWS_REGION= cd ~/.openclaw/extensions/aws-agents-pay/skills/agents-pay python3.11 -m venv .venv source .venv/bin/activate python -m pip install -r requirements.txt python scripts/agents_pay_admin.py init-config \ + --user-id "$PAYMENT_USER_ID" \ --max-per-payment-usd 0.05 \ --network eip155:84532 \ --recipient 0xMerchantWalletAddress python scripts/agents_pay_admin.py create-instrument --email you@example.com +``` + +Complete delegation and testnet funding for the new instrument, then create a +short-lived session: + +```bash python scripts/agents_pay_admin.py new-session \ --budget 1.00 \ --expiry-minutes 60 @@ -94,8 +109,7 @@ python scripts/agents_pay_admin.py new-session \ The provisioning commands above use v1 flags. Keep any manual provider credentials in the human terminal and out of the OpenClaw conversation. Do not paste credentials, command output, deployed state, or generated identifiers into an LLM conversation. -Complete delegation and testnet funding before creating the session. Then edit -the OpenClaw config locally in the same terminal or a local editor. +Edit the OpenClaw config locally in the same terminal or a local editor. > **Important:** The `userId` below must match the identity used in > `create-instrument` and `new-session`. A mismatch means the session cannot diff --git a/plugins/aws-agents/skills/agents-pay/references/operator-guide.md b/plugins/aws-agents/skills/agents-pay/references/operator-guide.md index 61ccd3ce2..b16a5d4cf 100644 --- a/plugins/aws-agents/skills/agents-pay/references/operator-guide.md +++ b/plugins/aws-agents/skills/agents-pay/references/operator-guide.md @@ -83,17 +83,25 @@ and on why the split exists at all: ```bash # 0. Prerequisites +cd /absolute/path/to/agents-pay python3 -m venv .venv source .venv/bin/activate python -m pip install -r requirements.txt npm install -g @aws/agentcore # 1. Provision (ControlPlaneRole; only step that touches provider secrets) +cd /absolute/path/to/AgentCore/project agentcore add payment-manager --name MyPayments --no-auto-payment agentcore add payment-connector --manager MyPayments --name Coinbase --quick-create agentcore build agentcore deploy --region +# The admin script lives in the installed agents-pay skill directory. +export AGENTCORE_PROJECT_DIR="$PWD" +export AGENTCORE_TARGET=default +export AWS_REGION= +cd /absolute/path/to/agents-pay + # 2. Write the config with at least one approved payee python scripts/agents_pay_admin.py init-config \ --max-per-payment-usd 0.05 \ diff --git a/plugins/aws-agents/skills/agents-pay/references/setup.md b/plugins/aws-agents/skills/agents-pay/references/setup.md index 6fdbf17ef..13b5fa8e1 100644 --- a/plugins/aws-agents/skills/agents-pay/references/setup.md +++ b/plugins/aws-agents/skills/agents-pay/references/setup.md @@ -13,7 +13,7 @@ agentcore --version python -c "from bedrock_agentcore.payments import PaymentManager; print('payments OK')" ``` -Work from the root of an AgentCore CLI v1 project (`agentcore/agentcore.json` has `"version": 2`). If the project is v0, commit it, review `agentcore migrate`, and apply the reviewed plan using `agentcore migrate --yes`. For a project dedicated to payment resources: +Work from the root of an AgentCore CLI v1 project (`agentcore/agentcore.json` has `"version": 2`). Read `agentcore/aws-targets.json` and select the intended deployment target before provisioning. If the project is v0, commit it, review `agentcore migrate`, and apply the reviewed plan using `agentcore migrate --yes`. For a project dedicated to payment resources: ```bash agentcore create --name PaymentsProject --template empty @@ -62,12 +62,24 @@ For Stripe Privy, the credential command uses `--provider StripePrivy`, `--app-i ## 4. Build, deploy, and discover ```bash -agentcore build -agentcore deploy --region -agentcore status --json +agentcore deploy --target +agentcore status --target --json ``` -The first deploy creates a `default` target in `agentcore/aws-targets.json` from the effective account and region. Check that it uses the intended account and region. For Quick Create, complete the Coinbase authorization URL reported at deploy and confirm the connector is ready. The v1 `agentcore/.cli/deployed-state.json` records the stack binding but does not store payment manager and connector IDs. Read them from `agentcore status --json`; `scripts/agents_pay_admin.py` now uses that output for automatic discovery. If discovery is unavailable, pass `--manager-arn` and `--connector-id` explicitly. +Run `agentcore build` before deploying a Quick Create connector. For a manual connector referencing a new payment credential, the first build can fail because that credential has no deployed ARN; deploy provisions it before building the stack, then `agentcore build` can run successfully. + +For a new project with no targets, run `agentcore deploy --region ` to create `default`, then use `--target default`. For an existing target, use its account and region; `--region` does not change them. For Quick Create, complete the Coinbase authorization URL reported at deploy and confirm the connector is ready. The v1 `agentcore/.cli/deployed-state.json` records the stack binding but does not store payment manager and connector IDs. Read them from `agentcore status --target --json`. + +Before running the human admin script below, record the project root and selected target, then switch to the installed `agents-pay` skill directory, where `scripts/agents_pay_admin.py` lives: + +```bash +export AGENTCORE_PROJECT_DIR="$PWD" +export AGENTCORE_TARGET= +export AWS_REGION= +cd /absolute/path/to/agents-pay +``` + +Use a separate admin config path for each deployment target. The script uses `AGENTCORE_PROJECT_DIR` and `AGENTCORE_TARGET` for status discovery; if discovery is unavailable, pass `--manager-arn` and `--connector-id` explicitly. ## 5. IAM: the four-role model @@ -232,8 +244,8 @@ invent or keep in sync: python3 scripts/agents_pay_admin.py create-instrument --email you@example.com ``` -The manager ARN and connector ID are read from `agentcore status --json` -automatically, so run this from your AgentCore project directory. Otherwise pass +The manager ARN and connector ID are read from `agentcore status --target --json` +using `AGENTCORE_PROJECT_DIR` and `AGENTCORE_TARGET`. Otherwise pass `--manager-arn` and `--connector-id` explicitly. Add `--network-family SOLANA` for Solana; the default `ETHEREUM` covers Base and diff --git a/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py b/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py index ab0b8839c..5eca9d821 100755 --- a/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py +++ b/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py @@ -82,6 +82,11 @@ def _check_aws_credentials(region: str | None = None) -> bool: USDC_DECIMALS = 6 +def project_target() -> str: + """The CLI target whose payment resources the operator selected.""" + return os.environ.get("AGENTCORE_TARGET") or "default" + + def admin_config_path(explicit: str | None) -> Path: """Resolve an operator-selected path for administrative commands only.""" return Path(explicit or os.environ.get("AGENTS_PAY_CONFIG") or DEFAULT_CONFIG) @@ -132,7 +137,7 @@ def discover_deployed(project_dir: str | Path | None = None) -> dict[str, str | data = json.loads(state_path.read_text()) payments = None targets = data.get("targets") or {} - target = targets.get("default") or (next(iter(targets.values()), {}) if targets else {}) + target = targets.get(project_target()) or {} if isinstance(target, dict): payments = (target.get("resources") or {}).get("payments") if not payments: @@ -179,7 +184,7 @@ def _discover_v1_status( return out try: result = subprocess.run( - ["agentcore", "status", "--json"], + ["agentcore", "status", "--target", project_target(), "--json"], cwd=root, capture_output=True, text=True, @@ -354,6 +359,15 @@ def cmd_init_config(args: argparse.Namespace) -> int: config = load_raw_config(path) if args.force else {"resources": {}, "policy": {}} + prior_target = (config.get("resources") or {}).get("target") + if prior_target and prior_target != project_target(): + print( + f"Config at {path} belongs to target {prior_target!r}, not {project_target()!r}. " + "Use a separate --path for each deployment target.", + file=sys.stderr, + ) + return 1 + config["resources"]["target"] = project_target() config["policy"] = { "max_per_payment_usd": args.max_per_payment_usd, "allowed_networks": [network], From 2ee5368a83ba5a9d96ec29734b682fa32db3eeb1 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Thu, 29 Oct 2026 12:51:45 -0400 Subject: [PATCH 08/10] fix(aws-agents): update agent auth and cross-session memory examples --- .../skills/agents-build/references/memory.md | 20 ++++--- .../agents-build/references/multi-agent.md | 57 ++++++++++++------- 2 files changed, 48 insertions(+), 29 deletions(-) diff --git a/plugins/aws-agents/skills/agents-build/references/memory.md b/plugins/aws-agents/skills/agents-build/references/memory.md index abf53fc6f..d06cdfaf7 100644 --- a/plugins/aws-agents/skills/agents-build/references/memory.md +++ b/plugins/aws-agents/skills/agents-build/references/memory.md @@ -29,6 +29,7 @@ Read `agentcore/agentcore.json`. Look for: - The `memories` array — is memory already configured? - The `runtimes` array — what agents are in the project and what framework do they use? - The project `name` — needed for env var construction +- The intended target in `agentcore/aws-targets.json` — use the same target for deployment, status, logs, and invocation **If `agentcore/agentcore.json` does not exist**, check if there's any AgentCore project structure nearby (look for `agentcore/` directory). If none found, proceed with the most helpful answer possible based on what the developer asked — don't block on missing context. If the question is about strategy selection or code patterns, answer it directly. Only ask "which situation are you in?" if the answer genuinely depends on it (e.g., they need CLI commands that differ by setup type). @@ -457,7 +458,7 @@ Before shipping a memory integration for ADK or Claude SDK, validate the end-to- 2. Invoke the agent with facts to remember 3. Start a new session 4. Invoke again and verify the agent recalls those facts -5. Check `agentcore logs runtime --name --query "memory" --since 1h --level error` for any memory errors +5. Check `agentcore logs runtime --name --target --query "memory" --since 1h --level error` for any memory errors If you build a working pattern, consider contributing it to [`awslabs/agentcore-samples`](https://github.com/awslabs/agentcore-samples) so the next developer doesn't have to figure it out. @@ -471,10 +472,11 @@ Always include this note: The MEMORY__ID env var is only injected after deploy. The code above handles this gracefully — it runs without memory when the env var isn't set. -To test memory: - agentcore deploy - agentcore invoke --runtime --payload '{"prompt":"My name is Alex and I prefer concise answers"}' --session-id 550e8400-e29b-41d4-a716-446655440000 - agentcore invoke --runtime --payload '{"prompt":"What do you know about me?"}' --session-id 550e8400-e29b-41d4-a716-446655440000 +To test cross-session memory on the selected target, use two distinct session IDs +and the same userId (actor ID): + agentcore deploy --target + agentcore invoke --target --runtime --payload '{"prompt":"My name is Alex and I prefer concise answers","userId":"alex"}' --session-id 550e8400-e29b-41d4-a716-446655440000 + agentcore invoke --target --runtime --payload '{"prompt":"What do you know about me?","userId":"alex"}' --session-id 550e8400-e29b-41d4-a716-446655440001 If using long-term memory (SEMANTIC or USER_PREFERENCE), wait 5–30 seconds between the first and second invoke — extraction runs asynchronously after @@ -526,14 +528,14 @@ Most common cause: the memory resource is configured but the code isn't reading **Memory not persisting across sessions:** -1. Check that LTM strategies (SEMANTIC, USER_PREFERENCE) are configured — not just SUMMARIZATION +1. Check that the intended long-term strategy is configured and that the code retrieves from its namespace. SUMMARIZATION is also a long-term strategy, but its retrieval path differs from facts and preferences. 2. Wait 5–30 seconds after a session ends before starting a new one — extraction is async -3. Find the Memory ID in `agentcore status --json`, then verify its service status is ACTIVE with `agentcore memory get --id --json` +3. Find the Memory ID in `agentcore status --target --json`, then verify its service status is ACTIVE with `agentcore memory get --id --region --json` 4. Use UUIDs (v4) for session IDs — the platform requires a minimum of 33 characters. Short IDs like "session-1" or "test" cause LTM to fail silently. `agentcore invoke` generates compliant IDs by default. **Memory not loading at session start:** -1. Find the Memory ID in `agentcore status --json`, then verify `MEMORY__ID` is set in the deployed Runtime environment +1. Find the Memory ID in `agentcore status --target --json`, then verify `MEMORY__ID` is set in that target's deployed Runtime environment 2. Check the actor_id is consistent across sessions — memory is scoped per actor 3. Confirm the namespace paths in retrieval_config match the namespaces used when writing — the retrieval namespace must exactly match the namespace the strategy extracts into 4. CLI defaults use paths without trailing slashes (e.g., `/users/{actorId}/facts`). If you customized namespace templates when creating the memory resource, use whatever pattern you chose — consistency between writer and reader is what matters. @@ -542,7 +544,7 @@ Most common cause: the memory resource is configured but the code isn't reading Memory takes 2–5 minutes to become ACTIVE after `agentcore deploy`. Check status: ```bash -agentcore memory get --id --json +agentcore memory get --id --region --json ``` ## S3 delivery / export buckets must be in the same account diff --git a/plugins/aws-agents/skills/agents-build/references/multi-agent.md b/plugins/aws-agents/skills/agents-build/references/multi-agent.md index 735908eb1..d5325c376 100644 --- a/plugins/aws-agents/skills/agents-build/references/multi-agent.md +++ b/plugins/aws-agents/skills/agents-build/references/multi-agent.md @@ -53,7 +53,7 @@ app = BedrockAgentCoreApp() # Set this env var in your orchestrator's deployment config SPECIALIST_ARN = os.getenv("SPECIALIST_AGENT_ARN") -REGION = os.getenv("AWS_REGION", "us-east-1") +REGION = os.getenv("SPECIALIST_AGENT_REGION") def call_specialist(prompt: str, session_id: str = None) -> str: """ @@ -207,7 +207,7 @@ Add this to `agentcore/agentcore.json` under the orchestrator agent's IAM config ### Step 4: Pass the specialist ARN to the orchestrator -Run `agentcore status --json` in the specialist project and select the deployed Runtime ARN from the `resources` array. For a different deployment target, pass `--target `. In the orchestrator project, set that ARN as the `SPECIALIST_AGENT_ARN` value in its Runtime entry's `environmentVariables` object in `agentcore/agentcore.json`. For local development, set the same variable in `agentcore/.env.local`. These are separate files: `.env.local` does not configure the deployed Runtime. Run `agentcore build`, then `agentcore deploy` in the orchestrator project. Do not grant its execution role `bedrock-agentcore:InvokeAgentRuntime` on `*`; scope the permission to the specialist Runtime ARN. +Run `agentcore status --target --json` in the specialist project and select the deployed Runtime ARN and the top-level `region`. In the orchestrator project, add `{"name":"SPECIALIST_AGENT_ARN","value":""}` and `{"name":"SPECIALIST_AGENT_REGION","value":""}` to that Runtime entry's `envVars` array in `agentcore/agentcore.json`. For local development, set the same variables in `agentcore/.env.local`. These are separate files: `.env.local` does not configure the deployed Runtime. Run `agentcore build`, then `agentcore deploy --target ` in the orchestrator project. Do not grant its execution role `bedrock-agentcore:InvokeAgentRuntime` on `*`; scope the permission to the specialist Runtime ARN. ## Pattern 2: A2A protocol @@ -232,14 +232,15 @@ def analyze_data(dataset_name: str) -> str: return f"Analysis results for {dataset_name}..." -agent = Agent( - model=load_model(), - system_prompt="You are an analysis specialist. Use tools when appropriate.", - tools=[analyze_data], -) +def create_agent(context_id: str) -> Agent: + return Agent( + model=load_model(), + system_prompt="You are an analysis specialist. Use tools when appropriate.", + tools=[analyze_data], + ) if __name__ == "__main__": - serve_a2a(StrandsA2AExecutor(agent)) + serve_a2a(StrandsA2AExecutor(agent_factory=create_agent)) ``` ``` @@ -256,19 +257,31 @@ bedrock-agentcore agentcore create --name SpecialistA2A --template a2a-python-strands cd SpecialistA2A # The CLI scaffolds app/agent/main.py with the serve_a2a pattern shown above — customize it with your specialist logic +# Configure the specialist Runtime in agentcore/agentcore.json for CUSTOM_JWT +# with the same OIDC discovery URL and allowed client ID used by the +# orchestrator's OAuth M2M credential below. For example, set: +# "authorizerType": "CUSTOM_JWT", +# "authorizerConfiguration": {"customJwtAuthorizer": { +# "discoveryUrl": "https:///.well-known/openid-configuration", +# "allowedClients": [""]}} +# Also set "requestHeaderAllowlist": ["Authorization"] on this Runtime +# so the A2A server receives the bearer header. agentcore deploy ``` -After deploy, get the runtime URL: +After deploy, get the Runtime ARN and region: ```bash -agentcore status --json +agentcore status --json | jq '{region, runtime: [.resources[] | select(.resourceType == "runtime") | .arn]}' ``` +`status` does not return a URL. URL-encode the Runtime ARN (including its colons and slashes) and use `https://bedrock-agentcore..amazonaws.com/runtimes//invocations/` as `SPECIALIST_A2A_URL`. The same URL must be used for agent-card discovery and JSON-RPC calls. + ### Step 3: Test locally ```bash -# Start the A2A server locally (from your project dir) +# Start the A2A server locally (from your specialist project directory). +# Run the curl commands below in another terminal while this server is running. agentcore dev # Test the agent card (discovery) @@ -293,10 +306,11 @@ curl -X POST http://localhost:9000 \ ### Step 4: Call the A2A specialist from the orchestrator -The specialist URL is a non-secret identifier, so pass it via an env var in the orchestrator's deployment config. The bearer token **is** a secret — do **not** stash it in `os.getenv(...)` on the deployed runtime (runtime env vars are not vault-backed). Register an OAuth M2M provider once, then use `@requires_access_token` to fetch a fresh token at call time: +The specialist URL is a non-secret identifier, so pass it via the orchestrator Runtime's `envVars` array in its project. The bearer token **is** a secret — do **not** stash it in `os.getenv(...)` on the deployed runtime (runtime env vars are not vault-backed). Return to the **orchestrator project root** before registering the OAuth M2M provider; the specialist project cannot provision credentials for a different project's Runtime. Use `@requires_access_token` to fetch a fresh token at call time: ```bash -# One-time: register the OAuth provider that issues tokens for the specialist. +# From the orchestrator project root: register the OAuth provider that issues +# tokens for calls to the specialist. # The v1 CLI accepts client secrets from file or stdin, never inline values. agentcore add credentials oauth \ --name SpecialistA2A \ @@ -356,29 +370,32 @@ def invoke(payload, context): return {"response": result} ``` -The decorator handles caching and refresh. For local dev, put the OAuth values in `agentcore/.env.local` so `agentcore dev` can resolve the decorator — the deployed runtime reads them from the credential provider instead. +Add `{"name":"SPECIALIST_A2A_URL","value":""}` to the orchestrator Runtime's `envVars`, then run `agentcore deploy --target ` from that project. This first deploy provisions the new credential before building the Runtime; `agentcore build` may fail before that happens. The decorator handles caching and refresh. For local dev, put the OAuth values in the orchestrator project's `agentcore/.env.local` so `agentcore dev` can resolve the decorator — the deployed Runtime reads them from the credential provider instead. --- ## Shared memory across agents -Memory is a top-level resource — not nested under a single agent. Multiple agents can share it by reading the same env var. +Memory is a top-level resource — not nested under a single agent. Multiple agents can share the same Memory ID when their IAM roles can access it. ### Setup -1. Create one shared memory resource: +1. Create one shared Memory resource in an owner project and deploy it to the chosen target: ```bash agentcore add memory --name SharedMemory --strategies SEMANTIC,USER_PREFERENCE +agentcore deploy --target ``` -1. In each agent's code, read the same env var: +2. Get the Memory ID and ARN from `agentcore status --target --json`. The owner project's Runtime receives `MEMORY_SHAREDMEMORY_ID` automatically. For a Runtime in another project, add `{"name":"MEMORY_SHAREDMEMORY_ID","value":""}` to that Runtime's `envVars` in `agentcore/agentcore.json`, grant its execution role the required Memory actions on the deployed Memory ARN, and deploy that project to its intended target. Both Runtimes must use the Memory's region. + +3. In each agent's code, read the same env var: ```python MEMORY_ID = os.getenv("MEMORY_SHAREDMEMORY_ID") ``` -1. Use a consistent `actor_id` scheme — typically the end user's ID — so both agents read and write the same user's memory. +4. Use a consistent `actor_id` scheme — typically the end user's ID — so both agents read and write the same user's memory. ### Key consideration @@ -410,7 +427,7 @@ turns = memory_client.get_last_k_turns( - Verify it's running on port 9000 (not 8080) - Check the agent card endpoint returns: `curl http://localhost:9000/.well-known/agent-card.json` -- Verify your `main.py` uses `serve_a2a(StrandsA2AExecutor(agent))` — the older `A2AServer + FastAPI` pattern is deprecated in favor of this +- Verify your `main.py` uses `serve_a2a(StrandsA2AExecutor(agent_factory=create_agent))` **Direct invocation permission denied:** @@ -439,6 +456,6 @@ turns = memory_client.get_last_k_turns( - Pattern recommendation matches the developer's latency and interoperability needs - Generated code includes correct IAM permissions for agent-to-agent invocation -- A2A server runs on port 9000 (not 8080) using `serve_a2a(StrandsA2AExecutor(agent))` +- A2A server runs on port 9000 (not 8080) using `serve_a2a(StrandsA2AExecutor(agent_factory=create_agent))` - Agent card is at `/.well-known/agent-card.json` with correct capabilities - Shared memory uses consistent `actor_id` scheme across agents From 86841e4e438527e32e6d5d26d2d071ee39abbc62 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Thu, 29 Oct 2026 13:05:54 -0400 Subject: [PATCH 09/10] fix: address review findings for agentcore CLI v1 skill updates --- .../agents-build/references/integrate.md | 19 ++++++-- .../references/local-vs-deployed.md | 8 ++-- .../skills/agents-build/references/memory.md | 23 ++++----- .../agents-build/references/multi-agent.md | 18 +++---- .../agents-build/references/payments.md | 41 ++++++++-------- .../references/request-headers.md | 11 +++-- .../scripts/setup_payment_user.py | 45 ++++++++++++------ .../scripts/test_setup_payment_user.py | 25 +++++++++- .../aws-agents/skills/agents-connect/SKILL.md | 2 +- plugins/aws-agents/skills/agents-pay/SKILL.md | 2 +- .../skills/agents-pay/references/setup.md | 3 +- .../agents-pay/scripts/agents_pay_admin.py | 47 +++++++++---------- .../scripts/test_resource_resolution.py | 37 +++++++++++++++ .../references/agentcore-harness.md | 2 +- .../mapping.md | 8 ++-- ...ate-bedrock-agents-to-agentcore-harness.md | 30 ++++++------ .../references/agentcore-harness.md | 2 +- .../mapping.md | 8 ++-- ...ate-bedrock-agents-to-agentcore-harness.md | 30 ++++++------ 19 files changed, 223 insertions(+), 138 deletions(-) diff --git a/plugins/aws-agents/skills/agents-build/references/integrate.md b/plugins/aws-agents/skills/agents-build/references/integrate.md index d33253713..bdd4469b6 100644 --- a/plugins/aws-agents/skills/agents-build/references/integrate.md +++ b/plugins/aws-agents/skills/agents-build/references/integrate.md @@ -105,15 +105,22 @@ except ClientError as e: ```python import requests +from urllib.parse import quote -AGENT_URL = "" +AGENT_URL = ( + "https://bedrock-agentcore..amazonaws.com/runtimes/" + + quote("", safe="") + + "/invocations" +) JWT_TOKEN = "" +session_id = "550e8400-e29b-41d4-a716-446655440000" # reuse for later turns response = requests.post( AGENT_URL, headers={ "Authorization": f"Bearer {JWT_TOKEN}", "Content-Type": "application/json", + "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": session_id, }, json={"prompt": "Hello, what can you do?"}, stream=True, @@ -154,14 +161,16 @@ for await (const chunk of response.response) { #### JavaScript/TypeScript (fetch) — JWT auth ```typescript -const AGENT_URL = ""; +const AGENT_URL = `https://bedrock-agentcore..amazonaws.com/runtimes/${encodeURIComponent("")}/invocations`; const JWT_TOKEN = ""; +const sessionId = "550e8400-e29b-41d4-a716-446655440000"; // reuse for later turns const response = await fetch(AGENT_URL, { method: "POST", headers: { Authorization: `Bearer ${JWT_TOKEN}`, "Content-Type": "application/json", + "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id": sessionId, }, body: JSON.stringify({ prompt: "Hello, what can you do?" }), }); @@ -179,10 +188,10 @@ while (true) { Explain how sessions work: -- **`runtimeSessionId`** — pass the same value across multiple calls to maintain conversation context +- **`runtimeSessionId`** — pass the same value across calls so the Runtime can route them to the same session - Generate a unique session ID per user conversation (e.g., UUID) -- Sessions are server-side — the agent remembers the conversation history for that session ID -- If you omit the session ID, each call is stateless (no conversation memory) +- The session ID alone does not save conversation history. Check that the deployed agent keeps history in its process or uses a session or memory manager. The CLI's Strands template caches agents by session ID and wires a memory manager when configured; custom Runtime code must implement its own history handling. +- If you omit the session ID, the service assigns one for that invocation, so a later call cannot deliberately resume it. ```python import uuid diff --git a/plugins/aws-agents/skills/agents-build/references/local-vs-deployed.md b/plugins/aws-agents/skills/agents-build/references/local-vs-deployed.md index 93d80ca9f..31d548840 100644 --- a/plugins/aws-agents/skills/agents-build/references/local-vs-deployed.md +++ b/plugins/aws-agents/skills/agents-build/references/local-vs-deployed.md @@ -10,8 +10,8 @@ AgentCore has a local dev server (`agentcore dev`) and a deployed runtime. They | Framework model calls | ✅ if Bedrock creds are available | ✅ | | Python/JS function tools (framework-native) | ✅ | ✅ | | Credentials (`@requires_api_key`, `@requires_access_token`) | ✅ from `agentcore/.env.local` | ✅ from Secrets Manager | -| Memory | ❌ env var not set locally | ✅ `MEMORY__ID` injected | -| Gateway | ❌ env var not set locally | ✅ `AGENTCORE_GATEWAY__URL` injected | +| Memory | Not injected locally; can use a deployed Memory if its ID and AWS access are supplied | ✅ `MEMORY__ID` injected | +| Gateway | Not injected locally; can call a deployed Gateway if its URL and auth are supplied | ✅ `AGENTCORE_GATEWAY__URL` injected | | Cedar policy evaluation | ❌ policies only enforced at gateway | ✅ | | Traces (X-Ray) | ✅ `agentcore dev` emits OTEL to CloudWatch by default; disable with `--no-traces` | ✅ auto-enabled | | CloudWatch logs | ✅ via ADOT / OTEL wiring (same path as traces) | ✅ if using `logging` module + OTEL | @@ -54,13 +54,13 @@ else: ## Testing workflow -Because memory, gateway, and policies don't work locally, the realistic test loop is: +To test memory, Gateway, and policies with the deployed agent's identity, use this loop: 1. **Local:** `agentcore dev` to verify the agent's code structure, framework wiring, system prompt, and any in-code logic 2. **Deploy to a staging target:** define `staging` with its account and region in `agentcore/aws-targets.json` first, then run `agentcore deploy --target staging` to test with real memory, gateway, and policies. Named targets are not created automatically. 3. **Production:** only after staging validation -Don't expect `agentcore dev` to reproduce a production failure involving memory recall, gateway tool calls, or policy denials — those require a deployed environment. +`agentcore dev` can call deployed Memory or Gateway resources when you configure them explicitly, but it runs under local credentials and does not reproduce the deployed Runtime's IAM identity. ## Common "works locally, fails deployed" causes diff --git a/plugins/aws-agents/skills/agents-build/references/memory.md b/plugins/aws-agents/skills/agents-build/references/memory.md index d06cdfaf7..4907c37a8 100644 --- a/plugins/aws-agents/skills/agents-build/references/memory.md +++ b/plugins/aws-agents/skills/agents-build/references/memory.md @@ -9,7 +9,7 @@ Add, configure, and debug AgentCore Memory — the managed service that lets you - Memory recall isn't working as expected - You want to share memory across multiple agents -Do NOT use this skill for within-session conversation history. That's handled automatically by the runtime — no configuration needed. +For within-session conversation history, first check how the agent code retains state. A Runtime session ID routes calls to a session, but custom agent code must retain messages or use a session manager. The CLI's Strands template caches the agent by session ID; the sample below constructs a fresh agent on every call and therefore needs a session manager to retain history. ## Input @@ -132,9 +132,9 @@ Read `app//main.py` (or the equivalent entrypoint) to detect the fram | Google ADK / Claude Agent SDK | BYO — use `MemoryClient` directly | Validate end-to-end before shipping | > [!WARNING] -> Always check for the MEMORY_ID env var before initializing memory. Memory is NOT -> available during `agentcore dev` — the env var is only set after deploy. Code that -> assumes memory is always available will fail silently in local development. +> Always check for the MEMORY_ID env var before initializing memory. `agentcore dev` +> does not inject the deployed Memory ID. Local development can use a deployed Memory +> if you supply its ID and have AWS access; otherwise run without Memory. #### Strands — Session Manager pattern (recommended for new projects) @@ -467,10 +467,11 @@ If you build a working pattern, consider contributing it to [`awslabs/agentcore- Always include this note: ``` -⚠️ Memory is not available during local development (agentcore dev). +⚠️ agentcore dev does not inject the deployed Memory ID automatically. -The MEMORY__ID env var is only injected after deploy. The code above -handles this gracefully — it runs without memory when the env var isn't set. +The deployed Runtime receives MEMORY__ID automatically. The code above +runs without Memory when the env var is absent. For a local integration test, +provide the deployed Memory ID and use local AWS credentials with access to it. To test cross-session memory on the selected target, use two distinct session IDs and the same userId (actor ID): @@ -524,18 +525,18 @@ Then use the same wiring code from Step 5, reading `MEMORY_ID` from the environm If memory was working and stopped, or never worked: **Agent keeps forgetting things even with memory set up:** -Most common cause: the memory resource is configured but the code isn't reading from it at session start. Check that your entrypoint calls `get_last_k_turns` (or uses the session manager) before creating the agent, not after. Also verify the `MEMORY__ID` env var is set — it's only injected after deploy, not during `agentcore dev`. +Most common cause: the memory resource is configured but the code isn't reading from it at session start. Check that your entrypoint calls `get_last_k_turns` (or uses the session manager) before creating the agent, not after. Also verify the `MEMORY__ID` env var is set. The deployed Runtime receives it automatically; `agentcore dev` needs it supplied explicitly. **Memory not persisting across sessions:** 1. Check that the intended long-term strategy is configured and that the code retrieves from its namespace. SUMMARIZATION is also a long-term strategy, but its retrieval path differs from facts and preferences. 2. Wait 5–30 seconds after a session ends before starting a new one — extraction is async -3. Find the Memory ID in `agentcore status --target --json`, then verify its service status is ACTIVE with `agentcore memory get --id --region --json` +3. Find the Memory ARN in `agentcore status --target --json`, take the ID after `memory/`, then verify its service status is ACTIVE with `agentcore memory get --id --region --json` 4. Use UUIDs (v4) for session IDs — the platform requires a minimum of 33 characters. Short IDs like "session-1" or "test" cause LTM to fail silently. `agentcore invoke` generates compliant IDs by default. **Memory not loading at session start:** -1. Find the Memory ID in `agentcore status --target --json`, then verify `MEMORY__ID` is set in that target's deployed Runtime environment +1. Find the Memory ARN in `agentcore status --target --json` and take the ID after `memory/`, then verify `MEMORY__ID` is set in that target's deployed Runtime environment 2. Check the actor_id is consistent across sessions — memory is scoped per actor 3. Confirm the namespace paths in retrieval_config match the namespaces used when writing — the retrieval namespace must exactly match the namespace the strategy extracts into 4. CLI defaults use paths without trailing slashes (e.g., `/users/{actorId}/facts`). If you customized namespace templates when creating the memory resource, use whatever pattern you chose — consistency between writer and reader is what matters. @@ -589,7 +590,7 @@ The v1 `agentcore add memory` command covers strategy selection, event expiry, i import boto3, json client = boto3.client("bedrock-agentcore-control") -memory_id = "" # from: agentcore status --json +memory_id = "" # ID after memory/ in the ARN from agentcore status --json client.put_memory_resource_policy( memoryId=memory_id, diff --git a/plugins/aws-agents/skills/agents-build/references/multi-agent.md b/plugins/aws-agents/skills/agents-build/references/multi-agent.md index d5325c376..e9a683142 100644 --- a/plugins/aws-agents/skills/agents-build/references/multi-agent.md +++ b/plugins/aws-agents/skills/agents-build/references/multi-agent.md @@ -193,17 +193,20 @@ For a validated ADK multi-agent pattern, use A2A instead of direct invocation ### Step 3: Grant IAM permission -The orchestrator's execution role needs permission to invoke the specialist: +The orchestrator's execution role needs permission to invoke the specialist. After deploying the specialist, take its exact Runtime ARN from `agentcore status --target --json` and save this policy as `app/agent/specialist-invoke-policy.json` in the orchestrator project (or under that Runtime's actual `codeLocation`): ```json { - "Effect": "Allow", - "Action": "bedrock-agentcore:InvokeAgentRuntime", - "Resource": "arn:aws:bedrock-agentcore:::runtime/SpecialistAgent-*" + "Version": "2012-10-17", + "Statement": [{ + "Effect": "Allow", + "Action": "bedrock-agentcore:InvokeAgentRuntime", + "Resource": "" + }] } ``` -Add this to `agentcore/agentcore.json` under the orchestrator agent's IAM config, or add it manually to the auto-created execution role after deploy. +Add `"specialist-invoke-policy.json"` to the orchestrator Runtime's `additionalPolicies` array in `agentcore/agentcore.json`. The v1 CLI resolves a `.json` policy path relative to that Runtime's `codeLocation` and attaches it to a CLI-created execution role during deployment. If the Runtime uses an imported `executionRoleArn`, attach the same policy to that role separately. Run `agentcore build` to check the path before deployment. ### Step 4: Pass the specialist ARN to the orchestrator @@ -264,8 +267,7 @@ cd SpecialistA2A # "authorizerConfiguration": {"customJwtAuthorizer": { # "discoveryUrl": "https:///.well-known/openid-configuration", # "allowedClients": [""]}} -# Also set "requestHeaderAllowlist": ["Authorization"] on this Runtime -# so the A2A server receives the bearer header. +# Authorization is passed through by default; no allowlist entry is needed. agentcore deploy ``` @@ -387,7 +389,7 @@ agentcore add memory --name SharedMemory --strategies SEMANTIC,USER_PREFERENCE agentcore deploy --target ``` -2. Get the Memory ID and ARN from `agentcore status --target --json`. The owner project's Runtime receives `MEMORY_SHAREDMEMORY_ID` automatically. For a Runtime in another project, add `{"name":"MEMORY_SHAREDMEMORY_ID","value":""}` to that Runtime's `envVars` in `agentcore/agentcore.json`, grant its execution role the required Memory actions on the deployed Memory ARN, and deploy that project to its intended target. Both Runtimes must use the Memory's region. +2. Get the Memory ARN from `agentcore status --target --json` and take its ID after `memory/`. The owner project's Runtime receives `MEMORY_SHAREDMEMORY_ID` automatically. For a Runtime in another project, add `{"name":"MEMORY_SHAREDMEMORY_ID","value":""}` to that Runtime's `envVars` in `agentcore/agentcore.json`, grant its execution role the required Memory actions on the deployed Memory ARN using an `additionalPolicies` policy file (or update its imported role), and deploy that project to its intended target. Both Runtimes must use the Memory's region. 3. In each agent's code, read the same env var: diff --git a/plugins/aws-agents/skills/agents-build/references/payments.md b/plugins/aws-agents/skills/agents-build/references/payments.md index 70ba1f696..d3214ecbd 100644 --- a/plugins/aws-agents/skills/agents-build/references/payments.md +++ b/plugins/aws-agents/skills/agents-build/references/payments.md @@ -25,7 +25,8 @@ For Coinbase, Quick Create is the smallest v1 path. The human completes any requ ```bash agentcore add payment-manager \ --name MyPayments \ - --network-preferences eip155:84532 + --network-preferences eip155:84532 \ + --no-auto-payment agentcore add payment-connector \ --manager MyPayments --name Coinbase --quick-create ``` @@ -52,6 +53,7 @@ If the project uses Strands or LangGraph, use the framework's native payments in **Strands:** ```python +import os from strands import Agent from strands_tools import http_request from bedrock_agentcore.payments.integrations.config import AgentCorePaymentsPluginConfig @@ -77,37 +79,31 @@ The plugin intercepts 402 responses from ANY tool, signs payment, and retries au **LangGraph:** ```python +import os from langchain.agents import create_agent from bedrock_agentcore.payments.integrations.langgraph import ( AgentCorePaymentsConfig, AgentCorePaymentsMiddleware, ) -# Choose ONE of the following configurations - -# Option A: explicit session (production) - config = AgentCorePaymentsConfig( - ... - payment_session_id=os.environ["PAYMENT_SESSION_ID"], - ) - - # Option B: auto-session (dev/test convenience) - config = AgentCorePaymentsConfig( - ... - auto_session=True, - auto_session_budget="5.00", - auto_session_expiry_minutes=60, - ) - +config = AgentCorePaymentsConfig( + payment_manager_arn=os.environ["PAYMENT_MANAGER_ARN"], + user_id=os.environ["PAYMENT_USER_ID"], + payment_instrument_id=os.environ["PAYMENT_INSTRUMENT_ID"], + payment_session_id=os.environ["PAYMENT_SESSION_ID"], + region=os.environ["AWS_REGION"], +) payments = AgentCorePaymentsMiddleware(config) agent = create_agent( - model=model, + model=model, # the LangGraph model already configured in this project tools=[], # middleware auto-registers http_request + payment query tools middleware=[payments], ) ``` +For a dev/test Runtime that may create sessions, omit `payment_session_id` and set `auto_session=True`, `auto_session_budget="5.00"`, and `auto_session_expiry_minutes=60` on the same config. Its execution role then needs `CreatePaymentSession`; use the explicit session above with the separated production roles. + The middleware wraps ALL tool calls, detects 402 from any response format (no `PAYMENT_REQUIRED:` marker needed), signs payment, and retries. It also auto-registers an `http_request` tool and payment query tools. **LangGraph simplifications vs the generic tool path:** @@ -154,9 +150,9 @@ Payments are wired with a small local tool, not a framework-specific plugin — The agent calls `x402_fetch` instead of a generic HTTP tool; payment is handled inside the tool. (Tell the model, via the system prompt, to use `x402_fetch` for URLs that may require payment.) -### Step 6: Provision the per-user instrument (SDK script — data plane) — agent runs +### Step 6: Provision the per-user instrument (SDK script — data plane) — human operator runs -The instrument (per-user wallet) and session (budget-bounded spend window) are data-plane resources — the v1 CLI can inspect them with `agentcore payment instrument` and `agentcore payment session`, while creation remains an SDK workflow. Ask the developer for the **user id** and **email** to provision the wallet. Run this skill's [`scripts/setup_payment_user.py`](../scripts/setup_payment_user.py) from the AgentCore project root, using its absolute path so the CLI can find the project. Select the same target used for deployment; the script discovers the manager ARN and connector ID from `agentcore status --target --json`: +The instrument (per-user wallet) and session (budget-bounded spend window) are data-plane resources — the v1 CLI can inspect them with `agentcore payment instrument` and `agentcore payment session`, while creation remains an SDK workflow. The human operator obtains the **user id** and **email** and runs this skill's [`scripts/setup_payment_user.py`](../scripts/setup_payment_user.py) from the AgentCore project root, using its absolute path so the CLI can find the project. Use the separate ManagementRole described in the `agents-pay` operator guide; do not give its session-creation permission to the payment-capable Runtime. Select the same target used for deployment; the script discovers the manager ARN and connector ID from `agentcore status --target --json`: ```bash python /absolute/path/to/agents-build/scripts/setup_payment_user.py \ @@ -164,6 +160,7 @@ python /absolute/path/to/agents-build/scripts/setup_payment_user.py \ ``` It creates the instrument (with the email in `linkedAccounts`) and prints its ID, wallet address, and delegation URL for Step 7. Create the time-limited session after delegation and funding in Step 8. +If the target contains multiple managers or the chosen manager has multiple connectors, pass the intended `--manager-arn` and `--connector-id` explicitly. The script refuses ambiguous discovery. **LangGraph with `auto_session=True`**: Skip the manual session step below. The middleware creates a session on the first 402. The instrument is still required. @@ -250,9 +247,9 @@ Using the `wallet_address` / `redirect_url` the script printed: - **x402 (Base Sepolia)**: send testnet USDC via the Circle faucet (). - **MPP on Tempo Moderato** (StripePrivy instruments only): send the canonical test stablecoin **pathUSD** via the Tempo faucet (). AgentCore accepts only the network's canonical token — the faucet's other stablecoins (AlphaUSD/BetaUSD/ThetaUSD) are rejected with `MPP_EVM_NON_CANONICAL_USDC`. For buyer-pays challenges (`buyerPaysGasFees: true`) the wallet also needs native Tempo gas. See [MPP networks, providers, and funding](#mpp-networks-providers-and-funding). -### Step 8: Create a session, then set env vars and test +### Step 8: Have the human operator create a session, then set env vars and test -After the end user completes delegation and funding, create a bounded session for the **same user ID** without creating another instrument. Use the instrument ID printed in Step 6: +After the end user completes delegation and funding, the human operator creates a bounded session for the **same user ID** without creating another instrument. Use the instrument ID printed in Step 6: ```bash python /absolute/path/to/agents-build/scripts/setup_payment_user.py \ diff --git a/plugins/aws-agents/skills/agents-build/references/request-headers.md b/plugins/aws-agents/skills/agents-build/references/request-headers.md index 92b3eb5db..a127140c8 100644 --- a/plugins/aws-agents/skills/agents-build/references/request-headers.md +++ b/plugins/aws-agents/skills/agents-build/references/request-headers.md @@ -68,7 +68,7 @@ Run `agentcore build` to validate the edited v1 project, then `agentcore deploy` - **Maximum 20 headers** in the allowlist (including `Authorization` if you include it explicitly) - **Header name length:** up to 256 characters - **Header value size:** up to 4 KB per header -- **Names are case-sensitive** — list them exactly as they'll be sent +- **Header names are matched case-insensitively.** Keep one consistent spelling in the project and caller examples for readability. - **Changes take effect after the next deploy** of the runtime If you hit the 20-header cap, combine related data into one JSON-encoded header rather than using many separate ones. @@ -120,12 +120,13 @@ Headers arrive in the runtime's `context` object passed to your invocation handl ```python @app.entrypoint def invoke(payload, context): - tenant = context.headers.get("X-Tenant-Id") - correlation_id = context.headers.get("X-Correlation-Id") + headers = {name.lower(): value for name, value in (context.request_headers or {}).items()} + tenant = headers.get("x-tenant-id") + correlation_id = headers.get("x-correlation-id") # ... use as needed ``` -Headers that weren't in the allowlist will be absent (not empty string) from the context. +Headers that weren't in the allowlist will be absent (not empty string) from `context.request_headers`. ## What won't work @@ -136,7 +137,7 @@ Headers that weren't in the allowlist will be absent (not empty string) from the ## Troubleshooting **"My agent doesn't see the header I'm sending"** -Check (in order): (1) Is the header in the allowlist? (2) Is the spelling an exact match including case? (3) Did you redeploy the runtime after updating the allowlist? (4) Is the caller actually sending the header — `curl -v` or equivalent network inspection. +Check (in order): (1) Is the header in the allowlist? (2) Does the name match, ignoring case? (3) Did you redeploy the runtime after updating the allowlist? (4) Is the caller actually sending the header — `curl -v` or equivalent network inspection. **"I hit the 20-header limit"** Consolidate related data into a single JSON-encoded header. For example, instead of `X-Region`, `X-Environment`, `X-Service-Name` as three separate headers, use `X-Context: {"region":"us-west-2","env":"prod","service":"billing"}`. diff --git a/plugins/aws-agents/skills/agents-build/scripts/setup_payment_user.py b/plugins/aws-agents/skills/agents-build/scripts/setup_payment_user.py index 9b2d08d42..8814da77e 100644 --- a/plugins/aws-agents/skills/agents-build/scripts/setup_payment_user.py +++ b/plugins/aws-agents/skills/agents-build/scripts/setup_payment_user.py @@ -22,8 +22,8 @@ import sys from pathlib import Path -def _from_deployed_state(target="default"): - """Best-effort payment discovery from v1 status, with a v0 state fallback.""" +def _from_deployed_state(target="default", manager_arn=None): + """Discover only an unambiguous manager/connector pair for the selected target.""" if Path("agentcore/agentcore.json").exists(): try: result = subprocess.run( @@ -34,16 +34,28 @@ def _from_deployed_state(target="default"): check=False, ) if result.returncode == 0: - for resource in json.loads(result.stdout).get("resources", []): - if resource.get("resourceType") != "payment-manager" or not resource.get("arn"): - continue - for child in resource.get("children", []): - if child.get("resourceType") == "payment-connector" and child.get("id"): - return resource["arn"], child["id"] + managers = [ + resource for resource in json.loads(result.stdout).get("resources", []) + if resource.get("resourceType") == "payment-manager" + and resource.get("arn") + and (manager_arn is None or resource["arn"] == manager_arn) + ] + if len(managers) == 1: + connectors = [ + child for child in managers[0].get("children", []) + if child.get("resourceType") == "payment-connector" and child.get("id") + ] + return managers[0]["arn"], connectors[0]["id"] if len(connectors) == 1 else None + return None, None except (OSError, ValueError, subprocess.TimeoutExpired): pass # Legacy v0 projects recorded payment outputs in deployed state. + try: + if json.loads(Path("agentcore/agentcore.json").read_text()).get("version") == 2: + return None, None + except (OSError, ValueError): + pass path = Path("agentcore/.cli/deployed-state.json") if not path.exists(): return None, None @@ -58,12 +70,15 @@ def _from_deployed_state(target="default"): payments = data.get("payments") # legacy/top-level fallback if not payments: return None, None - pay = next(iter(payments.values()), {}) if isinstance(payments, dict) else payments[0] + managers = list(payments.values()) if isinstance(payments, dict) else payments + managers = [pay for pay in managers if manager_arn is None or pay.get("managerArn") == manager_arn] + if len(managers) != 1: + return None, None + pay = managers[0] connectors = pay.get("connectors") or [] - connector = next(iter(connectors.values()), {}) if isinstance(connectors, dict) else ( - connectors[0] if connectors else {} - ) - return pay.get("managerArn"), connector.get("connectorId") + connectors = list(connectors.values()) if isinstance(connectors, dict) else connectors + connectors = [connector for connector in connectors if connector.get("connectorId")] + return pay.get("managerArn"), connectors[0]["connectorId"] if len(connectors) == 1 else None except Exception: return None, None @@ -92,11 +107,11 @@ def main(): manager_arn, connector_id = args.manager_arn, args.connector_id if not manager_arn or not connector_id: - ds_arn, ds_conn = _from_deployed_state(args.target) + ds_arn, ds_conn = _from_deployed_state(args.target, manager_arn) manager_arn = manager_arn or ds_arn connector_id = connector_id or ds_conn if not manager_arn or (not args.session_only and not connector_id): - sys.exit("Could not resolve the payment manager ARN or required connector ID. Pass " + sys.exit("Could not resolve a unique payment manager and connector for this target. Pass " "--manager-arn and, when creating an instrument, --connector-id; or check " "`agentcore status --target --json` in the deployed project.") diff --git a/plugins/aws-agents/skills/agents-build/scripts/test_setup_payment_user.py b/plugins/aws-agents/skills/agents-build/scripts/test_setup_payment_user.py index 2bd2e6d5f..9b6422d91 100644 --- a/plugins/aws-agents/skills/agents-build/scripts/test_setup_payment_user.py +++ b/plugins/aws-agents/skills/agents-build/scripts/test_setup_payment_user.py @@ -32,7 +32,30 @@ assert module._from_deployed_state() == ( status["resources"][0]["arn"], "connector-test" ) - run.assert_called_once() + assert module._from_deployed_state(manager_arn="arn:other") == (None, None) + second_manager = { + "resourceType": "payment-manager", + "arn": "arn:aws:bedrock-agentcore:us-east-1:111111111111:payment-manager/other", + "children": [{"resourceType": "payment-connector", "id": "other-connector"}], + } + run.return_value.stdout = json.dumps({ + "resources": [*status["resources"], second_manager], + }) + assert module._from_deployed_state() == (None, None) + assert module._from_deployed_state(manager_arn=second_manager["arn"]) == ( + second_manager["arn"], "other-connector" + ) + run.return_value.stdout = json.dumps({ + "resources": [{ + **status["resources"][0], + "children": [ + *status["resources"][0]["children"], + {"resourceType": "payment-connector", "id": "connector-2"}, + ], + }], + }) + assert module._from_deployed_state() == (status["resources"][0]["arn"], None) + assert run.call_count == 5 finally: os.chdir(original_dir) diff --git a/plugins/aws-agents/skills/agents-connect/SKILL.md b/plugins/aws-agents/skills/agents-connect/SKILL.md index 9176a8125..cc3a6420e 100644 --- a/plugins/aws-agents/skills/agents-connect/SKILL.md +++ b/plugins/aws-agents/skills/agents-connect/SKILL.md @@ -137,7 +137,7 @@ agentcore status --target --json If the project has no new credential providers, run `agentcore build` before deploying. With a new provider referenced by a Gateway target, the first `agentcore build` can fail because the provider has no deployed ARN yet; `agentcore deploy` provisions the credential before building the stack. Run `agentcore build` after that first deploy. -On the first deploy, the CLI creates the default target using the effective AWS account and region. `--region` does not change an existing target's region. Read the deployed Gateway URL from `agentcore status --target --json`. The deployed Runtime receives `AGENTCORE_GATEWAY__URL`; check the exact name in its generated README or environment. In local `agentcore dev`, this deployed URL is absent unless supplied explicitly. The agent framework still needs MCP client wiring to discover and call the Gateway's tools. +On the first deploy, the CLI creates the default target using the effective AWS account and region. `--region` does not change an existing target's region. `agentcore status --target --json` reports the deployed Gateway ARN, but not its URL. Take the Gateway ID after `gateway/` in that ARN and run `agentcore gateway get --id --region --json`; its `gatewayUrl` is the MCP URL. The deployed Runtime receives `AGENTCORE_GATEWAY__URL`; check the exact name in its generated README or environment. In local `agentcore dev`, this deployed URL is absent unless supplied explicitly. The agent framework still needs MCP client wiring to discover and call the Gateway's tools. A Harness uses a Gateway tool entry in `app//harness.yaml`, referencing the deployed Gateway ARN. Its config and a Runtime's MCP client code are different paths; use the one matching the project. diff --git a/plugins/aws-agents/skills/agents-pay/SKILL.md b/plugins/aws-agents/skills/agents-pay/SKILL.md index c7b2be46b..fc9636f0b 100644 --- a/plugins/aws-agents/skills/agents-pay/SKILL.md +++ b/plugins/aws-agents/skills/agents-pay/SKILL.md @@ -243,7 +243,7 @@ no-flags payment wizard and inline secret flags are not v1 commands. The minimum Quick Create path is: ```bash -agentcore add payment-manager --name MyPayments +agentcore add payment-manager --name MyPayments --no-auto-payment agentcore add payment-connector --manager MyPayments --name Coinbase --quick-create agentcore build agentcore deploy --region diff --git a/plugins/aws-agents/skills/agents-pay/references/setup.md b/plugins/aws-agents/skills/agents-pay/references/setup.md index 13b5fa8e1..218e03d18 100644 --- a/plugins/aws-agents/skills/agents-pay/references/setup.md +++ b/plugins/aws-agents/skills/agents-pay/references/setup.md @@ -5,6 +5,7 @@ A human provisions resources and authorizes payment sessions outside the agent R ## 0. Install and locate the project ```bash +cd /absolute/path/to/agents-pay python3 -m venv .venv source .venv/bin/activate python -m pip install -r requirements.txt @@ -79,7 +80,7 @@ export AWS_REGION= cd /absolute/path/to/agents-pay ``` -Use a separate admin config path for each deployment target. The script uses `AGENTCORE_PROJECT_DIR` and `AGENTCORE_TARGET` for status discovery; if discovery is unavailable, pass `--manager-arn` and `--connector-id` explicitly. +Use a separate admin config path for each deployment target. The script uses `AGENTCORE_PROJECT_DIR` and `AGENTCORE_TARGET` for status discovery; if discovery is unavailable or the target has multiple managers or connectors, pass the intended `--manager-arn` and `--connector-id` explicitly. Keep these two identifiers from the same manager. ## 5. IAM: the four-role model diff --git a/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py b/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py index 5eca9d821..af6558353 100755 --- a/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py +++ b/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py @@ -144,24 +144,17 @@ def discover_deployed(project_dir: str | Path | None = None) -> dict[str, str | payments = data.get("payments") if not payments: return _discover_v1_status(project_dir, state_path) - # 0.26.x: payments is a dict keyed by name - if isinstance(payments, dict): - pay = next(iter(payments.values()), {}) - # 0.20.x and older: payments is a list - elif isinstance(payments, list): - pay = payments[0] if payments else {} - else: + if not isinstance(payments, (dict, list)): return _discover_v1_status(project_dir, state_path) + managers = list(payments.values()) if isinstance(payments, dict) else payments + if len(managers) != 1: + return out + pay = managers[0] connectors = pay.get("connectors") or [] out["manager_arn"] = pay.get("managerArn") - # 0.26.x: connectors may be a dict keyed by name - if isinstance(connectors, dict): - first_connector = next(iter(connectors.values()), {}) - elif isinstance(connectors, list): - first_connector = connectors[0] if connectors else {} - else: - first_connector = {} - out["connector_id"] = first_connector.get("connectorId") if first_connector else None + connectors = list(connectors.values()) if isinstance(connectors, dict) else connectors + if len(connectors) == 1: + out["connector_id"] = connectors[0].get("connectorId") out["role_arn"] = pay.get("processPaymentRoleArn") except Exception: # noqa: BLE001 - convenience only; never fatal return _discover_v1_status(project_dir, state_path) @@ -194,14 +187,18 @@ def _discover_v1_status( if result.returncode != 0: return out status = json.loads(result.stdout) - for resource in status.get("resources", []): - if resource.get("resourceType") == "payment-manager" and resource.get("arn"): - out["manager_arn"] = resource["arn"] - for child in resource.get("children", []): - if child.get("resourceType") == "payment-connector" and child.get("id"): - out["connector_id"] = child["id"] - break - break + managers = [ + resource for resource in status.get("resources", []) + if resource.get("resourceType") == "payment-manager" and resource.get("arn") + ] + if len(managers) == 1: + out["manager_arn"] = managers[0]["arn"] + connectors = [ + child for child in managers[0].get("children", []) + if child.get("resourceType") == "payment-connector" and child.get("id") + ] + if len(connectors) == 1: + out["connector_id"] = connectors[0]["id"] except (OSError, ValueError, subprocess.TimeoutExpired): pass return out @@ -493,7 +490,9 @@ def cmd_create_instrument(args: argparse.Namespace) -> int: discovered = discover_deployed() manager_arn = resolve_manager_arn(args.manager_arn, config_path) - connector_id = args.connector_id or os.environ.get("PAYMENT_CONNECTOR_ID") or discovered["connector_id"] + connector_id = args.connector_id or os.environ.get("PAYMENT_CONNECTOR_ID") + if not connector_id and manager_arn == discovered["manager_arn"]: + connector_id = discovered["connector_id"] if not manager_arn or not connector_id: checked = ", ".join(str(p) for p in deployed_state_candidates()) diff --git a/plugins/aws-agents/skills/agents-pay/scripts/test_resource_resolution.py b/plugins/aws-agents/skills/agents-pay/scripts/test_resource_resolution.py index c370152e5..e2cd21966 100644 --- a/plugins/aws-agents/skills/agents-pay/scripts/test_resource_resolution.py +++ b/plugins/aws-agents/skills/agents-pay/scripts/test_resource_resolution.py @@ -149,4 +149,41 @@ def _write_config(name: str, resources: dict) -> Path: print("discover_deployed v1 with state: PASSED") +# Ambiguous project resources must require explicit selection by the operator. +with patch.object(m.subprocess, "run") as run: + run.return_value.returncode = 0 + run.return_value.stdout = json.dumps({ + **v1_status, + "resources": [ + *v1_status["resources"], + { + "resourceType": "payment-manager", + "name": "OtherPayments", + "arn": "arn:aws:bedrock-agentcore:us-east-1:111111111111:payment-manager/manager-2", + "children": [{"resourceType": "payment-connector", "id": "connector-2"}], + }, + ], + }) + found = m.discover_deployed(v1_root) + assert found["manager_arn"] is None + assert found["connector_id"] is None + +with patch.object(m.subprocess, "run") as run: + run.return_value.returncode = 0 + run.return_value.stdout = json.dumps({ + **v1_status, + "resources": [{ + **v1_status["resources"][0], + "children": [ + *v1_status["resources"][0]["children"], + {"resourceType": "payment-connector", "id": "connector-2"}, + ], + }], + }) + found = m.discover_deployed(v1_root) + assert found["manager_arn"] == v1_status["resources"][0]["arn"] + assert found["connector_id"] is None + +print("discover_deployed ambiguity: PASSED") + print("ALL RESOURCE RESOLUTION TESTS PASSED") diff --git a/plugins/aws-core/skills/amazon-bedrock/references/agentcore-harness.md b/plugins/aws-core/skills/amazon-bedrock/references/agentcore-harness.md index 9671e1e63..b40ee4d04 100644 --- a/plugins/aws-core/skills/amazon-bedrock/references/agentcore-harness.md +++ b/plugins/aws-core/skills/amazon-bedrock/references/agentcore-harness.md @@ -87,7 +87,7 @@ agentcore status --json agentcore invoke --harness ResearchHarness --prompt "Hello, what can you do?" ``` -The editable Harness files are `app/ResearchHarness/harness.yaml` and `system-prompt.md`. The first deploy creates the default target in `agentcore/aws-targets.json` from the effective account and region. `agentcore dev` serves project Runtimes locally; it is not the Harness create step. +The editable Harness files are `app/ResearchHarness/harness.yaml` and `system-prompt.md`. The first deploy creates the default target in `agentcore/aws-targets.json` from the effective account and region. `agentcore dev` can run a Runtime locally and can deploy or use a Harness in AWS; it does not run a Harness locally. **AWS CLI / SDK:** diff --git a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md index 4f8c204f5..8b2d76c6b 100644 --- a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md +++ b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md @@ -8,25 +8,25 @@ The `.tmpl` files are not for any template engine; they are guidance for you (th A Bedrock action-group Lambda speaks the Bedrock event envelope; AgentCore Gateway invokes Lambda targets with a different shape, so the original won't work behind Gateway unchanged. -**Default: proxy-by-ARN.** Create a *new* shim Lambda ([lambda_shim.py.tmpl](assets/lambda_shim.py.tmpl) — its docstring documents both envelopes) that translates the Gateway event into the Bedrock event, invokes the original by ARN, and unwraps the response. This leaves the original **untouched** — editing it in place can change its response shape and break the source agent. The original's ARN is rendered into the shim source as a literal (no env vars — see [deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md)). +**Default: proxy-by-ARN.** Create a *new* shim Lambda ([lambda_shim.py.tmpl](../../assets/lambda_shim.py.tmpl) — its docstring documents both envelopes) that translates the Gateway event into the Bedrock event, invokes the original by ARN, and unwraps the response. This leaves the original **untouched** — editing it in place can change its response shape and break the source agent. The original's ARN is rendered into the shim source as a literal (no env vars — see [deploy.md](deploy.md)). **OpenAPI action groups: pass the route TEMPLATE verbatim.** The original Lambda dispatches by matching `apiPath` against the literal route template (`/customer/{customer_id}`), with path-param values delivered separately in `parameters`. Substituting a value into the path (`/customer/tkashina`) matches no template, so the original falls through to its unhandled-op branch. Render the shim's `_OP_ROUTES` table (mapping each operationId to its `{method, apiPath-template}`) from the source OpenAPI schema with placeholders intact, and keep values in `parameters`. Each action group becomes one Gateway **`targetType: "lambda"` code target**, added with `agentcore add gateway-target --target-configuration file://.json`, with `toolDefinitions` holding one entry per function or operation. `agentcore deploy` builds the shim Lambda. Follow [deploy.md](deploy.md). ### Tool schemas — mirror exactly -Reproduce the source action group's schema faithfully into the tool-schema file ([tool_schema.json.tmpl](assets/tool_schema.json.tmpl)): same tool names, parameter names, types, and descriptions. Do **not** rewrite or "improve" them — fidelity to the source agent's behavior is the goal, and a renamed tool or tightened type changes how the model selects it. +Reproduce the source action group's schema faithfully into the tool-schema file ([tool_schema.json.tmpl](../../assets/tool_schema.json.tmpl)): same tool names, parameter names, types, and descriptions. Do **not** rewrite or "improve" them — fidelity to the source agent's behavior is the goal, and a renamed tool or tightened type changes how the model selects it. ## Mapping the knowledge base (connector or KB shim, by type) **Decide by `knowledgeBaseConfiguration.type` FIRST — the native Gateway connector accepts ONLY a `MANAGED` Bedrock KB.** -- **Any non-`MANAGED` type (`VECTOR`, `KENDRA`, `SQL`)** → **always the KB shim.** The connector cannot accept these at all, so type alone decides and retrieval config is irrelevant. Use the **KB shim** ([kb_shim.py.tmpl](assets/kb_shim.py.tmpl)): a hand-added **`targetType: "lambda"` code target** (see [deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md)) that calls `bedrock-agent-runtime:Retrieve` and returns MCP-shaped passages. +- **Any non-`MANAGED` type (`VECTOR`, `KENDRA`, `SQL`)** → **always the KB shim.** The connector cannot accept these at all, so type alone decides and retrieval config is irrelevant. Use the **KB shim** ([kb_shim.py.tmpl](../../assets/kb_shim.py.tmpl)): a hand-added **`targetType: "lambda"` code target** (see [deploy.md](deploy.md)) that calls `bedrock-agent-runtime:Retrieve` and returns MCP-shaped passages. - **`MANAGED`** → then (and only then) check the **retrieval config on the KB association** (`knowledgeBaseConfigurations[].retrievalConfiguration`: reranker, metadata filter, hybrid/search-type override, top-k): - **default** retrieval config → native Gateway connector: `agentcore add gateway-connector --gateway --name --connector bedrock-knowledge-bases --knowledge-base `. - **non-default** retrieval config → the **KB shim** (the connector uses a fixed retrieval contract and would silently drop the custom config), reproducing that config from the manifest's KB association. -Both paths reproduce the source agent's retrieval — the choice is wiring, not fidelity, so every reachable KB is still **clean**. (An *unreachable* KB is a hard-stop — see [eligibility.md](references/bedrock-agents-to-agentcore-harness/eligibility.md). Type picks the wiring; it is never itself the blocker.) +Both paths reproduce the source agent's retrieval — the choice is wiring, not fidelity, so every reachable KB is still **clean**. (An *unreachable* KB is a hard-stop — see [eligibility.md](eligibility.md). Type picks the wiring; it is never itself the blocker.) ### Return-of-Control action groups — cannot migrate A `customControl: RETURN_CONTROL` action group has no Lambda; the original application handled execution outside Bedrock. There is no automatic harness equivalent. **Do not silently drop it.** Classify it **cannot** in the migration assessment and confirm with the user before continuing — the migrated agent will lack that capability unless the user supplies a backend for it. diff --git a/plugins/aws-core/skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md b/plugins/aws-core/skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md index 776984002..3f848f9cf 100644 --- a/plugins/aws-core/skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md +++ b/plugins/aws-core/skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md @@ -2,14 +2,14 @@ Migrate a Bedrock Agent into an AgentCore **Harness** (the managed agent loop) using the **AgentCore CLI**. The skill drives the CLI to scaffold and deploy; it never hand-rolls boto3 infrastructure the CLI owns. Remeber that this skill can be used for performing the migration AND helping the user answer any migration related questions even if they don't want to perform the migration. -Reference files, scripts and templates are available in [references](references/bedrock-agents-to-agentcore-harness/) +Reference files, scripts and templates are available in [references](bedrock-agents-to-agentcore-harness/) -**Inline agents.** This guidance also migrates a Bedrock **inline** agent (one invoked via `InvokeInlineAgent` with its configuration supplied per-request rather than persisted as a stored agent). An inline agent has no `agentId`, so it takes a modified entry path: **its `InvokeInlineAgent` request payload *is* the source manifest.** Concretely — **skip Phase 0's agent-region confirm and all of Phase 1 (identity resolution) and Phase 2's fetch (`fetch_bedrock_agent.py` needs an `--agent-id` an inline agent doesn't have)**; instead, take the user-supplied `InvokeInlineAgent` payload, extract its components (`foundationModel`, `instruction`, `actionGroups`, `knowledgeBases`, `guardrailConfiguration`, prompt overrides), write them into `./out/source-agent.json` in the manifest shape ([bedrock-agents-to-agentcore-harness/discovery.md](references/bedrock-agents-to-agentcore-harness/discovery.md)), then **enter at Phase 3**. Every phase from Phase 3 on — eligibility gates, component mapping, deploy — applies unchanged, since they operate on the manifest, not on how it was obtained. +**Inline agents.** This guidance also migrates a Bedrock **inline** agent (one invoked via `InvokeInlineAgent` with its configuration supplied per-request rather than persisted as a stored agent). An inline agent has no `agentId`, so it takes a modified entry path: **its `InvokeInlineAgent` request payload *is* the source manifest.** Concretely — **skip Phase 0's agent-region confirm and all of Phase 1 (identity resolution) and Phase 2's fetch (`fetch_bedrock_agent.py` needs an `--agent-id` an inline agent doesn't have)**; instead, take the user-supplied `InvokeInlineAgent` payload, extract its components (`foundationModel`, `instruction`, `actionGroups`, `knowledgeBases`, `guardrailConfiguration`, prompt overrides), write them into `./out/source-agent.json` in the manifest shape ([bedrock-agents-to-agentcore-harness/discovery.md](bedrock-agents-to-agentcore-harness/discovery.md)), then **enter at Phase 3**. Every phase from Phase 3 on — eligibility gates, component mapping, deploy — applies unchanged, since they operate on the manifest, not on how it was obtained. Two ideas run through every phase: - **gate** — a hard checkpoint the migration must pass before continuing. Some gates are eligibility (a source feature with no validated harness path); others are user approval (account, region, cost). A failed gate stops the migration and is reported, never worked around silently. -- **mirror** — preserve user-visible *behavior*, not Bedrock-Agents *structure*: keep what the agent does, drop only what the harness now does for you (see [bedrock-agents-to-agentcore-harness/mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)). +- **mirror** — preserve user-visible *behavior*, not Bedrock-Agents *structure*: keep what the agent does, drop only what the harness now does for you (see [bedrock-agents-to-agentcore-harness/mapping.md](bedrock-agents-to-agentcore-harness/mapping.md)). This skill makes AWS API calls throughout (STS, Bedrock Agent reads, deploys). The **AWS MCP server is recommended** for streamlined access, but is not required — every step also works with the AWS CLI and boto3 as described here. @@ -17,7 +17,7 @@ This skill makes AWS API calls throughout (STS, Bedrock Agent reads, deploys). T The triggering request *is* the input. Extract whatever the user gave — agent **id/name/ARN**, **region**, **profile/account** — and **confirm** it in the phases below rather than re-asking. Identity resolution (Phase 1) runs *after* preflight, since listing agents to disambiguate a name needs a confirmed account + region first. -**Fail fast.** If the request *already* names a hard-stop disqualifier (multi-agent, custom orchestration, multimodal), say so and stop before preflight — always with that condition's specific alternative from [bedrock-agents-to-agentcore-harness/eligibility.md](references/bedrock-agents-to-agentcore-harness/eligibility.md), never a bare "not supported." Phase 3 still runs the full gate for everything that clears this. +**Fail fast.** If the request *already* names a hard-stop disqualifier (multi-agent, custom orchestration, multimodal), say so and stop before preflight — always with that condition's specific alternative from [bedrock-agents-to-agentcore-harness/eligibility.md](bedrock-agents-to-agentcore-harness/eligibility.md), never a bare "not supported." Phase 3 still runs the full gate for everything that clears this. ## Phases @@ -27,7 +27,7 @@ Finish each phase and summarize before the next. Establish the migration runs against the account, region, and CLI the user intends — before touching the source agent. -1. Confirm the **CLI** is present and current per [bedrock-agents-to-agentcore-harness/cli.md](references/bedrock-agents-to-agentcore-harness/cli.md). Stop if it is missing or a required command/flag is absent. +1. Confirm the **CLI** is present and current per [bedrock-agents-to-agentcore-harness/cli.md](bedrock-agents-to-agentcore-harness/cli.md). Stop if it is missing or a required command/flag is absent. 2. Resolve AWS credentials and run `aws sts get-caller-identity`. **Echo the account id, caller ARN, and resolved region back to the user and ask them to confirm or correct** before proceeding. Never assume the default profile is the right one. 3. Confirm the **source agent's region** here (if the user supplied one, confirm rather than re-ask). The harness must deploy there — but the CLI defaults elsewhere, so you set it explicitly before the first deploy (Phase 6; see deploy.md's region trap). @@ -37,23 +37,23 @@ Completion criterion: the user has confirmed `(account, region)` and the CLI pas **Inline agents skip this phase** (they have no `agentId`; see "Inline agents" above — take the payload and enter at Phase 3). -Resolve a concrete `(agentId, agentVersion, aliasId)` from whatever the user gave (id, name, ARN, or nothing). If the user gave a name fragment, gave nothing, or **asks to see what's available**, list the agents in the confirmed region (`bedrock-agent:ListAgents`) and present them for the user to pick from. Default to the **production alias's** numbered version, not DRAFT — unless the user has only DRAFT or asks for it explicitly. See [bedrock-agents-to-agentcore-harness/discovery.md](references/bedrock-agents-to-agentcore-harness/discovery.md) for resolution rules and the DRAFT-only case. +Resolve a concrete `(agentId, agentVersion, aliasId)` from whatever the user gave (id, name, ARN, or nothing). If the user gave a name fragment, gave nothing, or **asks to see what's available**, list the agents in the confirmed region (`bedrock-agent:ListAgents`) and present them for the user to pick from. Default to the **production alias's** numbered version, not DRAFT — unless the user has only DRAFT or asks for it explicitly. See [bedrock-agents-to-agentcore-harness/discovery.md](bedrock-agents-to-agentcore-harness/discovery.md) for resolution rules and the DRAFT-only case. Completion criterion: the user has confirmed the exact `(account, region, agentId, agentVersion, aliasId)` tuple. ### Phase 2 — Discovery -Snapshot the agent into one manifest (`./out/source-agent.json`) — via the bundled `scripts/fetch_bedrock_agent.py` (needs python3 + boto3), or the `aws bedrock-agent` fallback when boto3 is absent. Both paths and the manifest shape are in [bedrock-agents-to-agentcore-harness/discovery.md](references/bedrock-agents-to-agentcore-harness/discovery.md). **For an inline agent** the fetch script does not apply (no `agentId`) — build the manifest from the `InvokeInlineAgent` payload instead, per "Inline agents" above and discovery.md's inline note. +Snapshot the agent into one manifest (`./out/source-agent.json`) — via the bundled `scripts/fetch_bedrock_agent.py` (needs python3 + boto3), or the `aws bedrock-agent` fallback when boto3 is absent. Both paths and the manifest shape are in [bedrock-agents-to-agentcore-harness/discovery.md](bedrock-agents-to-agentcore-harness/discovery.md). **For an inline agent** the fetch script does not apply (no `agentId`) — build the manifest from the `InvokeInlineAgent` payload instead, per "Inline agents" above and discovery.md's inline note. Read the manifest and present a **concise, human-readable inventory** — a scannable list or small table (model; action groups by name + type; KBs + type; guardrail; memory; collaboration), not a prose paragraph. -Also capture the source's **security posture** so the migration can preserve it (see [bedrock-agents-to-agentcore-harness/deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md)): its **inbound invocation posture** (which principals hold `bedrock-agent-runtime:InvokeAgent`, plus any resource-based policy on the agent) and its `idleSessionTTLInSeconds`. These set the bar Phase 6 must match, not loosen. +Also capture the source's **security posture** so the migration can preserve it (see [bedrock-agents-to-agentcore-harness/deploy.md](bedrock-agents-to-agentcore-harness/deploy.md)): its **inbound invocation posture** (which principals hold `bedrock-agent-runtime:InvokeAgent`, plus any resource-based policy on the agent) and its `idleSessionTTLInSeconds`. These set the bar Phase 6 must match, not loosen. Completion criterion: manifest written and inventory presented. ### Phase 3 — Eligibility gate -Check the manifest against the eligibility rubric in [bedrock-agents-to-agentcore-harness/eligibility.md](references/bedrock-agents-to-agentcore-harness/eligibility.md). Any **hard-stop** condition (multimodal input, multi-agent collaboration, unreachable KB, custom orchestration) ends the migration here. +Check the manifest against the eligibility rubric in [bedrock-agents-to-agentcore-harness/eligibility.md](bedrock-agents-to-agentcore-harness/eligibility.md). Any **hard-stop** condition (multimodal input, multi-agent collaboration, unreachable KB, custom orchestration) ends the migration here. If a gate fails: **stop**, tell the user exactly which condition failed and why, and suggest manual alternatives. @@ -63,17 +63,17 @@ Completion criterion: every hard-stop condition checked and explicitly cleared, The agent is eligible, but not every component migrates with full fidelity. Before planning, show the user a **per-component ledger** classifying each discovered component three ways: -- **clean** — behavior preserved. Most components land here: action groups via shim, *any* KB (MANAGED-with-default-config via connector, everything else via KB shim — both reproduce retrieval, see [bedrock-agents-to-agentcore-harness/mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)), CodeInterpreter built-in, model + inference params, and managed memory (the standard harness memory, not a downgrade). +- **clean** — behavior preserved. Most components land here: action groups via shim, *any* KB (MANAGED-with-default-config via connector, everything else via KB shim — both reproduce retrieval, see [bedrock-agents-to-agentcore-harness/mapping.md](bedrock-agents-to-agentcore-harness/mapping.md)), CodeInterpreter built-in, model + inference params, and managed memory (the standard harness memory, not a downgrade). - **degraded** — a genuine fidelity change the user should weigh, e.g. a non-DEFAULT orchestration/pre-processing prompt whose business logic can't be cleanly folded into the system prompt, or any behavior the migration can only approximate. -- **cannot** — no harness equivalent, capability lost: Return-of-Control action groups and the **guardrail** (see [bedrock-agents-to-agentcore-harness/mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)). Surface both to the user. +- **cannot** — no harness equivalent, capability lost: Return-of-Control action groups and the **guardrail** (see [bedrock-agents-to-agentcore-harness/mapping.md](bedrock-agents-to-agentcore-harness/mapping.md)). Surface both to the user. -Use the mapping in [bedrock-agents-to-agentcore-harness/mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md) to classify. Present the ledger and **pause for explicit acknowledgement** — the user must accept the *degraded* and *cannot* items before planning. Nothing irreversible has happened yet; this is informed consent on fidelity loss. +Use the mapping in [bedrock-agents-to-agentcore-harness/mapping.md](bedrock-agents-to-agentcore-harness/mapping.md) to classify. Present the ledger and **pause for explicit acknowledgement** — the user must accept the *degraded* and *cannot* items before planning. Nothing irreversible has happened yet; this is informed consent on fidelity loss. Completion criterion: user has seen the full ledger and acknowledged the degraded/cannot items. ### Phase 5 — Plan -Map each acknowledged component to its harness target using [bedrock-agents-to-agentcore-harness/mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md). Produce a written **migration plan** and **pause for approval** (gate). Surface costs and that the source agent is never modified. +Map each acknowledged component to its harness target using [bedrock-agents-to-agentcore-harness/mapping.md](bedrock-agents-to-agentcore-harness/mapping.md). Produce a written **migration plan** and **pause for approval** (gate). Surface costs and that the source agent is never modified. Completion criterion: user approved the written plan. @@ -85,8 +85,8 @@ Completion criterion: `agentcore build` and both `agentcore deploy` runs succeed ## Security considerations -- **Least-privilege shim roles** — one action on one resource ARN, no wildcards; exact policies in [bedrock-agents-to-agentcore-harness/deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md). -- **The manifest holds sensitive data** — handling rules in [bedrock-agents-to-agentcore-harness/discovery.md](references/bedrock-agents-to-agentcore-harness/discovery.md). +- **Least-privilege shim roles** — one action on one resource ARN, no wildcards; exact policies in [bedrock-agents-to-agentcore-harness/deploy.md](bedrock-agents-to-agentcore-harness/deploy.md). +- **The manifest holds sensitive data** — handling rules in [bedrock-agents-to-agentcore-harness/discovery.md](bedrock-agents-to-agentcore-harness/discovery.md). - **Use ephemeral credentials** (IAM roles, SSO, `assume-role`) for both discovery and deploy — never long-lived IAM user access keys. ## What this migration can not do diff --git a/skills/core-skills/amazon-bedrock/references/agentcore-harness.md b/skills/core-skills/amazon-bedrock/references/agentcore-harness.md index 9671e1e63..b40ee4d04 100644 --- a/skills/core-skills/amazon-bedrock/references/agentcore-harness.md +++ b/skills/core-skills/amazon-bedrock/references/agentcore-harness.md @@ -87,7 +87,7 @@ agentcore status --json agentcore invoke --harness ResearchHarness --prompt "Hello, what can you do?" ``` -The editable Harness files are `app/ResearchHarness/harness.yaml` and `system-prompt.md`. The first deploy creates the default target in `agentcore/aws-targets.json` from the effective account and region. `agentcore dev` serves project Runtimes locally; it is not the Harness create step. +The editable Harness files are `app/ResearchHarness/harness.yaml` and `system-prompt.md`. The first deploy creates the default target in `agentcore/aws-targets.json` from the effective account and region. `agentcore dev` can run a Runtime locally and can deploy or use a Harness in AWS; it does not run a Harness locally. **AWS CLI / SDK:** diff --git a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md index 4f8c204f5..8b2d76c6b 100644 --- a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md +++ b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/mapping.md @@ -8,25 +8,25 @@ The `.tmpl` files are not for any template engine; they are guidance for you (th A Bedrock action-group Lambda speaks the Bedrock event envelope; AgentCore Gateway invokes Lambda targets with a different shape, so the original won't work behind Gateway unchanged. -**Default: proxy-by-ARN.** Create a *new* shim Lambda ([lambda_shim.py.tmpl](assets/lambda_shim.py.tmpl) — its docstring documents both envelopes) that translates the Gateway event into the Bedrock event, invokes the original by ARN, and unwraps the response. This leaves the original **untouched** — editing it in place can change its response shape and break the source agent. The original's ARN is rendered into the shim source as a literal (no env vars — see [deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md)). +**Default: proxy-by-ARN.** Create a *new* shim Lambda ([lambda_shim.py.tmpl](../../assets/lambda_shim.py.tmpl) — its docstring documents both envelopes) that translates the Gateway event into the Bedrock event, invokes the original by ARN, and unwraps the response. This leaves the original **untouched** — editing it in place can change its response shape and break the source agent. The original's ARN is rendered into the shim source as a literal (no env vars — see [deploy.md](deploy.md)). **OpenAPI action groups: pass the route TEMPLATE verbatim.** The original Lambda dispatches by matching `apiPath` against the literal route template (`/customer/{customer_id}`), with path-param values delivered separately in `parameters`. Substituting a value into the path (`/customer/tkashina`) matches no template, so the original falls through to its unhandled-op branch. Render the shim's `_OP_ROUTES` table (mapping each operationId to its `{method, apiPath-template}`) from the source OpenAPI schema with placeholders intact, and keep values in `parameters`. Each action group becomes one Gateway **`targetType: "lambda"` code target**, added with `agentcore add gateway-target --target-configuration file://.json`, with `toolDefinitions` holding one entry per function or operation. `agentcore deploy` builds the shim Lambda. Follow [deploy.md](deploy.md). ### Tool schemas — mirror exactly -Reproduce the source action group's schema faithfully into the tool-schema file ([tool_schema.json.tmpl](assets/tool_schema.json.tmpl)): same tool names, parameter names, types, and descriptions. Do **not** rewrite or "improve" them — fidelity to the source agent's behavior is the goal, and a renamed tool or tightened type changes how the model selects it. +Reproduce the source action group's schema faithfully into the tool-schema file ([tool_schema.json.tmpl](../../assets/tool_schema.json.tmpl)): same tool names, parameter names, types, and descriptions. Do **not** rewrite or "improve" them — fidelity to the source agent's behavior is the goal, and a renamed tool or tightened type changes how the model selects it. ## Mapping the knowledge base (connector or KB shim, by type) **Decide by `knowledgeBaseConfiguration.type` FIRST — the native Gateway connector accepts ONLY a `MANAGED` Bedrock KB.** -- **Any non-`MANAGED` type (`VECTOR`, `KENDRA`, `SQL`)** → **always the KB shim.** The connector cannot accept these at all, so type alone decides and retrieval config is irrelevant. Use the **KB shim** ([kb_shim.py.tmpl](assets/kb_shim.py.tmpl)): a hand-added **`targetType: "lambda"` code target** (see [deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md)) that calls `bedrock-agent-runtime:Retrieve` and returns MCP-shaped passages. +- **Any non-`MANAGED` type (`VECTOR`, `KENDRA`, `SQL`)** → **always the KB shim.** The connector cannot accept these at all, so type alone decides and retrieval config is irrelevant. Use the **KB shim** ([kb_shim.py.tmpl](../../assets/kb_shim.py.tmpl)): a hand-added **`targetType: "lambda"` code target** (see [deploy.md](deploy.md)) that calls `bedrock-agent-runtime:Retrieve` and returns MCP-shaped passages. - **`MANAGED`** → then (and only then) check the **retrieval config on the KB association** (`knowledgeBaseConfigurations[].retrievalConfiguration`: reranker, metadata filter, hybrid/search-type override, top-k): - **default** retrieval config → native Gateway connector: `agentcore add gateway-connector --gateway --name --connector bedrock-knowledge-bases --knowledge-base `. - **non-default** retrieval config → the **KB shim** (the connector uses a fixed retrieval contract and would silently drop the custom config), reproducing that config from the manifest's KB association. -Both paths reproduce the source agent's retrieval — the choice is wiring, not fidelity, so every reachable KB is still **clean**. (An *unreachable* KB is a hard-stop — see [eligibility.md](references/bedrock-agents-to-agentcore-harness/eligibility.md). Type picks the wiring; it is never itself the blocker.) +Both paths reproduce the source agent's retrieval — the choice is wiring, not fidelity, so every reachable KB is still **clean**. (An *unreachable* KB is a hard-stop — see [eligibility.md](eligibility.md). Type picks the wiring; it is never itself the blocker.) ### Return-of-Control action groups — cannot migrate A `customControl: RETURN_CONTROL` action group has no Lambda; the original application handled execution outside Bedrock. There is no automatic harness equivalent. **Do not silently drop it.** Classify it **cannot** in the migration assessment and confirm with the user before continuing — the migrated agent will lack that capability unless the user supplies a backend for it. diff --git a/skills/core-skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md b/skills/core-skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md index 776984002..3f848f9cf 100644 --- a/skills/core-skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md +++ b/skills/core-skills/amazon-bedrock/references/migrate-bedrock-agents-to-agentcore-harness.md @@ -2,14 +2,14 @@ Migrate a Bedrock Agent into an AgentCore **Harness** (the managed agent loop) using the **AgentCore CLI**. The skill drives the CLI to scaffold and deploy; it never hand-rolls boto3 infrastructure the CLI owns. Remeber that this skill can be used for performing the migration AND helping the user answer any migration related questions even if they don't want to perform the migration. -Reference files, scripts and templates are available in [references](references/bedrock-agents-to-agentcore-harness/) +Reference files, scripts and templates are available in [references](bedrock-agents-to-agentcore-harness/) -**Inline agents.** This guidance also migrates a Bedrock **inline** agent (one invoked via `InvokeInlineAgent` with its configuration supplied per-request rather than persisted as a stored agent). An inline agent has no `agentId`, so it takes a modified entry path: **its `InvokeInlineAgent` request payload *is* the source manifest.** Concretely — **skip Phase 0's agent-region confirm and all of Phase 1 (identity resolution) and Phase 2's fetch (`fetch_bedrock_agent.py` needs an `--agent-id` an inline agent doesn't have)**; instead, take the user-supplied `InvokeInlineAgent` payload, extract its components (`foundationModel`, `instruction`, `actionGroups`, `knowledgeBases`, `guardrailConfiguration`, prompt overrides), write them into `./out/source-agent.json` in the manifest shape ([bedrock-agents-to-agentcore-harness/discovery.md](references/bedrock-agents-to-agentcore-harness/discovery.md)), then **enter at Phase 3**. Every phase from Phase 3 on — eligibility gates, component mapping, deploy — applies unchanged, since they operate on the manifest, not on how it was obtained. +**Inline agents.** This guidance also migrates a Bedrock **inline** agent (one invoked via `InvokeInlineAgent` with its configuration supplied per-request rather than persisted as a stored agent). An inline agent has no `agentId`, so it takes a modified entry path: **its `InvokeInlineAgent` request payload *is* the source manifest.** Concretely — **skip Phase 0's agent-region confirm and all of Phase 1 (identity resolution) and Phase 2's fetch (`fetch_bedrock_agent.py` needs an `--agent-id` an inline agent doesn't have)**; instead, take the user-supplied `InvokeInlineAgent` payload, extract its components (`foundationModel`, `instruction`, `actionGroups`, `knowledgeBases`, `guardrailConfiguration`, prompt overrides), write them into `./out/source-agent.json` in the manifest shape ([bedrock-agents-to-agentcore-harness/discovery.md](bedrock-agents-to-agentcore-harness/discovery.md)), then **enter at Phase 3**. Every phase from Phase 3 on — eligibility gates, component mapping, deploy — applies unchanged, since they operate on the manifest, not on how it was obtained. Two ideas run through every phase: - **gate** — a hard checkpoint the migration must pass before continuing. Some gates are eligibility (a source feature with no validated harness path); others are user approval (account, region, cost). A failed gate stops the migration and is reported, never worked around silently. -- **mirror** — preserve user-visible *behavior*, not Bedrock-Agents *structure*: keep what the agent does, drop only what the harness now does for you (see [bedrock-agents-to-agentcore-harness/mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)). +- **mirror** — preserve user-visible *behavior*, not Bedrock-Agents *structure*: keep what the agent does, drop only what the harness now does for you (see [bedrock-agents-to-agentcore-harness/mapping.md](bedrock-agents-to-agentcore-harness/mapping.md)). This skill makes AWS API calls throughout (STS, Bedrock Agent reads, deploys). The **AWS MCP server is recommended** for streamlined access, but is not required — every step also works with the AWS CLI and boto3 as described here. @@ -17,7 +17,7 @@ This skill makes AWS API calls throughout (STS, Bedrock Agent reads, deploys). T The triggering request *is* the input. Extract whatever the user gave — agent **id/name/ARN**, **region**, **profile/account** — and **confirm** it in the phases below rather than re-asking. Identity resolution (Phase 1) runs *after* preflight, since listing agents to disambiguate a name needs a confirmed account + region first. -**Fail fast.** If the request *already* names a hard-stop disqualifier (multi-agent, custom orchestration, multimodal), say so and stop before preflight — always with that condition's specific alternative from [bedrock-agents-to-agentcore-harness/eligibility.md](references/bedrock-agents-to-agentcore-harness/eligibility.md), never a bare "not supported." Phase 3 still runs the full gate for everything that clears this. +**Fail fast.** If the request *already* names a hard-stop disqualifier (multi-agent, custom orchestration, multimodal), say so and stop before preflight — always with that condition's specific alternative from [bedrock-agents-to-agentcore-harness/eligibility.md](bedrock-agents-to-agentcore-harness/eligibility.md), never a bare "not supported." Phase 3 still runs the full gate for everything that clears this. ## Phases @@ -27,7 +27,7 @@ Finish each phase and summarize before the next. Establish the migration runs against the account, region, and CLI the user intends — before touching the source agent. -1. Confirm the **CLI** is present and current per [bedrock-agents-to-agentcore-harness/cli.md](references/bedrock-agents-to-agentcore-harness/cli.md). Stop if it is missing or a required command/flag is absent. +1. Confirm the **CLI** is present and current per [bedrock-agents-to-agentcore-harness/cli.md](bedrock-agents-to-agentcore-harness/cli.md). Stop if it is missing or a required command/flag is absent. 2. Resolve AWS credentials and run `aws sts get-caller-identity`. **Echo the account id, caller ARN, and resolved region back to the user and ask them to confirm or correct** before proceeding. Never assume the default profile is the right one. 3. Confirm the **source agent's region** here (if the user supplied one, confirm rather than re-ask). The harness must deploy there — but the CLI defaults elsewhere, so you set it explicitly before the first deploy (Phase 6; see deploy.md's region trap). @@ -37,23 +37,23 @@ Completion criterion: the user has confirmed `(account, region)` and the CLI pas **Inline agents skip this phase** (they have no `agentId`; see "Inline agents" above — take the payload and enter at Phase 3). -Resolve a concrete `(agentId, agentVersion, aliasId)` from whatever the user gave (id, name, ARN, or nothing). If the user gave a name fragment, gave nothing, or **asks to see what's available**, list the agents in the confirmed region (`bedrock-agent:ListAgents`) and present them for the user to pick from. Default to the **production alias's** numbered version, not DRAFT — unless the user has only DRAFT or asks for it explicitly. See [bedrock-agents-to-agentcore-harness/discovery.md](references/bedrock-agents-to-agentcore-harness/discovery.md) for resolution rules and the DRAFT-only case. +Resolve a concrete `(agentId, agentVersion, aliasId)` from whatever the user gave (id, name, ARN, or nothing). If the user gave a name fragment, gave nothing, or **asks to see what's available**, list the agents in the confirmed region (`bedrock-agent:ListAgents`) and present them for the user to pick from. Default to the **production alias's** numbered version, not DRAFT — unless the user has only DRAFT or asks for it explicitly. See [bedrock-agents-to-agentcore-harness/discovery.md](bedrock-agents-to-agentcore-harness/discovery.md) for resolution rules and the DRAFT-only case. Completion criterion: the user has confirmed the exact `(account, region, agentId, agentVersion, aliasId)` tuple. ### Phase 2 — Discovery -Snapshot the agent into one manifest (`./out/source-agent.json`) — via the bundled `scripts/fetch_bedrock_agent.py` (needs python3 + boto3), or the `aws bedrock-agent` fallback when boto3 is absent. Both paths and the manifest shape are in [bedrock-agents-to-agentcore-harness/discovery.md](references/bedrock-agents-to-agentcore-harness/discovery.md). **For an inline agent** the fetch script does not apply (no `agentId`) — build the manifest from the `InvokeInlineAgent` payload instead, per "Inline agents" above and discovery.md's inline note. +Snapshot the agent into one manifest (`./out/source-agent.json`) — via the bundled `scripts/fetch_bedrock_agent.py` (needs python3 + boto3), or the `aws bedrock-agent` fallback when boto3 is absent. Both paths and the manifest shape are in [bedrock-agents-to-agentcore-harness/discovery.md](bedrock-agents-to-agentcore-harness/discovery.md). **For an inline agent** the fetch script does not apply (no `agentId`) — build the manifest from the `InvokeInlineAgent` payload instead, per "Inline agents" above and discovery.md's inline note. Read the manifest and present a **concise, human-readable inventory** — a scannable list or small table (model; action groups by name + type; KBs + type; guardrail; memory; collaboration), not a prose paragraph. -Also capture the source's **security posture** so the migration can preserve it (see [bedrock-agents-to-agentcore-harness/deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md)): its **inbound invocation posture** (which principals hold `bedrock-agent-runtime:InvokeAgent`, plus any resource-based policy on the agent) and its `idleSessionTTLInSeconds`. These set the bar Phase 6 must match, not loosen. +Also capture the source's **security posture** so the migration can preserve it (see [bedrock-agents-to-agentcore-harness/deploy.md](bedrock-agents-to-agentcore-harness/deploy.md)): its **inbound invocation posture** (which principals hold `bedrock-agent-runtime:InvokeAgent`, plus any resource-based policy on the agent) and its `idleSessionTTLInSeconds`. These set the bar Phase 6 must match, not loosen. Completion criterion: manifest written and inventory presented. ### Phase 3 — Eligibility gate -Check the manifest against the eligibility rubric in [bedrock-agents-to-agentcore-harness/eligibility.md](references/bedrock-agents-to-agentcore-harness/eligibility.md). Any **hard-stop** condition (multimodal input, multi-agent collaboration, unreachable KB, custom orchestration) ends the migration here. +Check the manifest against the eligibility rubric in [bedrock-agents-to-agentcore-harness/eligibility.md](bedrock-agents-to-agentcore-harness/eligibility.md). Any **hard-stop** condition (multimodal input, multi-agent collaboration, unreachable KB, custom orchestration) ends the migration here. If a gate fails: **stop**, tell the user exactly which condition failed and why, and suggest manual alternatives. @@ -63,17 +63,17 @@ Completion criterion: every hard-stop condition checked and explicitly cleared, The agent is eligible, but not every component migrates with full fidelity. Before planning, show the user a **per-component ledger** classifying each discovered component three ways: -- **clean** — behavior preserved. Most components land here: action groups via shim, *any* KB (MANAGED-with-default-config via connector, everything else via KB shim — both reproduce retrieval, see [bedrock-agents-to-agentcore-harness/mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)), CodeInterpreter built-in, model + inference params, and managed memory (the standard harness memory, not a downgrade). +- **clean** — behavior preserved. Most components land here: action groups via shim, *any* KB (MANAGED-with-default-config via connector, everything else via KB shim — both reproduce retrieval, see [bedrock-agents-to-agentcore-harness/mapping.md](bedrock-agents-to-agentcore-harness/mapping.md)), CodeInterpreter built-in, model + inference params, and managed memory (the standard harness memory, not a downgrade). - **degraded** — a genuine fidelity change the user should weigh, e.g. a non-DEFAULT orchestration/pre-processing prompt whose business logic can't be cleanly folded into the system prompt, or any behavior the migration can only approximate. -- **cannot** — no harness equivalent, capability lost: Return-of-Control action groups and the **guardrail** (see [bedrock-agents-to-agentcore-harness/mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md)). Surface both to the user. +- **cannot** — no harness equivalent, capability lost: Return-of-Control action groups and the **guardrail** (see [bedrock-agents-to-agentcore-harness/mapping.md](bedrock-agents-to-agentcore-harness/mapping.md)). Surface both to the user. -Use the mapping in [bedrock-agents-to-agentcore-harness/mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md) to classify. Present the ledger and **pause for explicit acknowledgement** — the user must accept the *degraded* and *cannot* items before planning. Nothing irreversible has happened yet; this is informed consent on fidelity loss. +Use the mapping in [bedrock-agents-to-agentcore-harness/mapping.md](bedrock-agents-to-agentcore-harness/mapping.md) to classify. Present the ledger and **pause for explicit acknowledgement** — the user must accept the *degraded* and *cannot* items before planning. Nothing irreversible has happened yet; this is informed consent on fidelity loss. Completion criterion: user has seen the full ledger and acknowledged the degraded/cannot items. ### Phase 5 — Plan -Map each acknowledged component to its harness target using [bedrock-agents-to-agentcore-harness/mapping.md](references/bedrock-agents-to-agentcore-harness/mapping.md). Produce a written **migration plan** and **pause for approval** (gate). Surface costs and that the source agent is never modified. +Map each acknowledged component to its harness target using [bedrock-agents-to-agentcore-harness/mapping.md](bedrock-agents-to-agentcore-harness/mapping.md). Produce a written **migration plan** and **pause for approval** (gate). Surface costs and that the source agent is never modified. Completion criterion: user approved the written plan. @@ -85,8 +85,8 @@ Completion criterion: `agentcore build` and both `agentcore deploy` runs succeed ## Security considerations -- **Least-privilege shim roles** — one action on one resource ARN, no wildcards; exact policies in [bedrock-agents-to-agentcore-harness/deploy.md](references/bedrock-agents-to-agentcore-harness/deploy.md). -- **The manifest holds sensitive data** — handling rules in [bedrock-agents-to-agentcore-harness/discovery.md](references/bedrock-agents-to-agentcore-harness/discovery.md). +- **Least-privilege shim roles** — one action on one resource ARN, no wildcards; exact policies in [bedrock-agents-to-agentcore-harness/deploy.md](bedrock-agents-to-agentcore-harness/deploy.md). +- **The manifest holds sensitive data** — handling rules in [bedrock-agents-to-agentcore-harness/discovery.md](bedrock-agents-to-agentcore-harness/discovery.md). - **Use ephemeral credentials** (IAM roles, SSO, `assume-role`) for both discovery and deploy — never long-lived IAM user access keys. ## What this migration can not do From bebe3e12f12d6ddd5059ba7dd27633364e6936f6 Mon Sep 17 00:00:00 2001 From: Nicolas Borges Date: Thu, 29 Oct 2026 15:58:04 -0400 Subject: [PATCH 10/10] fix(aws-agents): require shim manifests and prefer v1 payment status --- .../agents-pay/scripts/agents_pay_admin.py | 32 ++++++++--- .../scripts/test_resource_resolution.py | 54 +++++++++++++++++++ .../deploy.md | 13 ++++- .../deploy.md | 13 ++++- 4 files changed, 102 insertions(+), 10 deletions(-) diff --git a/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py b/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py index af6558353..690f9f16f 100755 --- a/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py +++ b/plugins/aws-agents/skills/agents-pay/scripts/agents_pay_admin.py @@ -116,6 +116,19 @@ def _find_deployed_state(project_dir: str | Path | None = None) -> Path | None: return None +def _find_project_root( + project_dir: str | Path | None, state_path: Path | None +) -> Path | None: + """Locate the project config from an explicit directory, state file, or cwd.""" + start = Path( + project_dir or os.environ.get("AGENTCORE_PROJECT_DIR") or state_path or Path.cwd() + ).expanduser().resolve() + return next( + (path for path in (start, *start.parents) if (path / "agentcore" / "agentcore.json").is_file()), + None, + ) + + def discover_deployed(project_dir: str | Path | None = None) -> dict[str, str | None]: """Best-effort read of manager ARN / connector ID from the CLI project. @@ -127,10 +140,19 @@ def discover_deployed(project_dir: str | Path | None = None) -> dict[str, str | project root, inside the agentcore/ directory, or a subdirectory of it. CLI v1 reports payment identifiers through `agentcore status --json`. - The deployed state layouts from v0 are retained as a fallback. + Legacy deployed state is used only for v0 projects; migration can leave + stale payment identifiers in that file. """ out: dict[str, str | None] = {"manager_arn": None, "connector_id": None, "role_arn": None} state_path = _find_deployed_state(project_dir) + root = _find_project_root(project_dir, state_path) + if root is not None: + try: + version = json.loads((root / "agentcore" / "agentcore.json").read_text()).get("version") + except (OSError, ValueError): + version = None + if version == 2: + return _discover_v1_status(root, state_path) if state_path is None: return _discover_v1_status(project_dir, None) try: @@ -166,13 +188,7 @@ def _discover_v1_status( ) -> dict[str, str | None]: """Resolve v1 stack outputs via the CLI's project status JSON.""" out: dict[str, str | None] = {"manager_arn": None, "connector_id": None, "role_arn": None} - start = state_path.resolve() if state_path else Path( - project_dir or os.environ.get("AGENTCORE_PROJECT_DIR") or Path.cwd() - ).expanduser().resolve() - root = next( - (path for path in (start, *start.parents) if (path / "agentcore" / "agentcore.json").exists()), - None, - ) + root = _find_project_root(project_dir, state_path) if root is None: return out try: diff --git a/plugins/aws-agents/skills/agents-pay/scripts/test_resource_resolution.py b/plugins/aws-agents/skills/agents-pay/scripts/test_resource_resolution.py index e2cd21966..bb6a35ded 100644 --- a/plugins/aws-agents/skills/agents-pay/scripts/test_resource_resolution.py +++ b/plugins/aws-agents/skills/agents-pay/scripts/test_resource_resolution.py @@ -149,6 +149,60 @@ def _write_config(name: str, resources: dict) -> Path: print("discover_deployed v1 with state: PASSED") +# Migrated v0 state can retain an outdated payments branch after the v1 project +# replaces its resources. The v1 status result must take precedence over it. +(v1_root / "agentcore" / ".cli" / "deployed-state.json").write_text(json.dumps({ + "targets": { + "default": { + "resources": { + "payments": { + "OldPayments": { + "managerArn": "arn:aws:bedrock-agentcore:us-east-1:111111111111:payment-manager/old", + "connectors": {"OldConnector": {"connectorId": "old-connector"}}, + } + } + } + } + } +})) +with patch.object(m.subprocess, "run") as run: + run.return_value.returncode = 0 + run.return_value.stdout = json.dumps(v1_status) + found = m.discover_deployed(v1_root) + assert found["manager_arn"] == v1_status["resources"][0]["arn"] + assert found["connector_id"] == "connector-1" + run.assert_called_once() + assert run.call_args.kwargs["cwd"] == v1_root.resolve() + +with patch.object(m.subprocess, "run") as run: + run.return_value.returncode = 1 + found = m.discover_deployed(v1_root) + assert found["manager_arn"] is None + assert found["connector_id"] is None + run.assert_called_once() + +with patch.dict(os.environ, {"AGENTCORE_PROJECT_DIR": str(v1_root), "AGENTCORE_TARGET": "default"}): + with patch.object(m.subprocess, "run") as run: + run.return_value.returncode = 0 + run.return_value.stdout = json.dumps(v1_status) + found = m.discover_deployed() + assert found["manager_arn"] == v1_status["resources"][0]["arn"] + assert found["connector_id"] == "connector-1" + run.assert_called_once() + +print("discover_deployed migrated v1 with stale state: PASSED") + +# Legacy v0 projects still use their payment entries without calling v1 status. +(v1_root / "agentcore" / "agentcore.json").write_text('{"name":"PayTest","version":1}') +with patch.object(m.subprocess, "run") as run: + found = m.discover_deployed(v1_root) + assert found["manager_arn"].endswith("payment-manager/old") + assert found["connector_id"] == "old-connector" + run.assert_not_called() +(v1_root / "agentcore" / "agentcore.json").write_text('{"name":"PayTest","version":2}') + +print("discover_deployed v0 fallback: PASSED") + # Ambiguous project resources must require explicit selection by the operator. with patch.object(m.subprocess, "run") as run: run.return_value.returncode = 0 diff --git a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md index 1abdb4abc..dc313e13e 100644 --- a/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md +++ b/plugins/aws-core/skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md @@ -31,7 +31,18 @@ The v1 CLI has no `add tool` command. Do not duplicate the YAML `tools` key. Run ## Add Gateway targets -An action-group shim is a new Lambda backed Gateway target. Render the source action group into `tools//handler.py` using the templates in this skill's `assets/`, with one `toolDefinitions` entry per source function or OpenAPI operation. Store the complete target object in a JSON file and add it through the CLI: +An action-group shim is a new Lambda backed Gateway target. Render the source action group into `tools//handler.py` using the templates in this skill's `assets/`, with one `toolDefinitions` entry per source function or OpenAPI operation. The v1 CDK Python packager also requires a `pyproject.toml` in each shim's `compute.implementation.path`; the empty project and Harness scaffold do not create one under `tools/`. Place this minimal manifest beside each rendered `handler.py`, including KB shims: + +```toml +# tools//pyproject.toml +[project] +name = "agentcore-migration-shim" +version = "0.1.0" +requires-python = ">=3.12,<3.13" +dependencies = ["boto3"] +``` + +This Python requirement matches the `PYTHON_3_12` Lambda configuration below. Store the complete target object in a JSON file and add it through the CLI: ```json { diff --git a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md index 1abdb4abc..dc313e13e 100644 --- a/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md +++ b/skills/core-skills/amazon-bedrock/references/bedrock-agents-to-agentcore-harness/deploy.md @@ -31,7 +31,18 @@ The v1 CLI has no `add tool` command. Do not duplicate the YAML `tools` key. Run ## Add Gateway targets -An action-group shim is a new Lambda backed Gateway target. Render the source action group into `tools//handler.py` using the templates in this skill's `assets/`, with one `toolDefinitions` entry per source function or OpenAPI operation. Store the complete target object in a JSON file and add it through the CLI: +An action-group shim is a new Lambda backed Gateway target. Render the source action group into `tools//handler.py` using the templates in this skill's `assets/`, with one `toolDefinitions` entry per source function or OpenAPI operation. The v1 CDK Python packager also requires a `pyproject.toml` in each shim's `compute.implementation.path`; the empty project and Harness scaffold do not create one under `tools/`. Place this minimal manifest beside each rendered `handler.py`, including KB shims: + +```toml +# tools//pyproject.toml +[project] +name = "agentcore-migration-shim" +version = "0.1.0" +requires-python = ">=3.12,<3.13" +dependencies = ["boto3"] +``` + +This Python requirement matches the `PYTHON_3_12` Lambda configuration below. Store the complete target object in a JSON file and add it through the CLI: ```json {