Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,28 @@ LLM_MAX_TOKENS=4096
# MANAGER_LLM_MODEL=bedrock/us.anthropic.claude-opus-4-8
# Note: Opus 4.7+ Bedrock IDs are undated (us.anthropic.claude-opus-4-8); older models keep the dated -v1:0 profile form. See https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-opus-4-8.html

# Provider: AWS Bedrock via the Anthropic Messages endpoint (RECOMMENDED for
# Bedrock). Routes Claude through CrewAI's native Anthropic provider against
# Bedrock's /anthropic route, so the Converse toolUse/toolResult sanitizer
# (patches/crewai_bedrock_fix.py) is NOT needed. Only current-generation Claude
# tiers are served on the Messages API (Sonnet 5, Opus 4.7/4.8, Haiku 4.5);
# Sonnet 4.5 is Converse-only. Model ids are the Bedrock model / inference-
# profile ids (no provider prefix).
# ANTHROPIC_COMPATIBLE_LLM_API_BASE_URL=https://bedrock-runtime.us-west-2.amazonaws.com/anthropic
# ANTHROPIC_COMPATIBLE_LLM_API_KEY=your-bedrock-api-key
# DEFAULT_LLM_MODEL=us.anthropic.claude-sonnet-5
# MANAGER_LLM_MODEL=us.anthropic.claude-sonnet-5
# MEMORY_LLM_MODEL=us.anthropic.claude-haiku-4-5-20251001-v1:0
#
# AWS credentials for the deploy CLI (dev account). deploy.sh sources this .env,
# so either an AWS_PROFILE that is logged in, or explicit keys, work:
# AWS_PROFILE=genai
# -- or --
# AWS_ACCESS_KEY_ID=...
# AWS_SECRET_ACCESS_KEY=...
# AWS_SESSION_TOKEN=...
# AWS_REGION=us-west-2

# Provider: Custom OpenAI-compatible endpoint (alternative)
# For endpoints with no CrewAI native provider prefix of their own — NVIDIA
# NIM, Ollama, HuggingFace TGI, vLLM, LM Studio, etc. Set DEFAULT_LLM_MODEL to
Expand Down
193 changes: 193 additions & 0 deletions infra/aws/agentcore/auth-agentcore.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
AWSTemplateFormatVersion: "2010-09-09"
Description: >
Auth stack for the AAMP buyer AgentCore deployment. Houses the IAM
execution role the buyer runtime assumes, including a SCOPED grant to
invoke seller AgentCore runtimes for buyer->seller (agent-to-agent) calls.
This "auth" stack is the home for auth-related infra (IAM today; Cognito +
gateway authorizer for the cross-org path live alongside it — see NOTE).

# =============================================================================
# NOTE — SECURITY MODEL / WHEN TO USE THIS
# =============================================================================
# The SellerInvoke permission below lets the buyer runtime call the seller's
# AgentCore runtimes directly via bedrock-agentcore:InvokeAgentRuntime, gated
# by AWS IAM. That is appropriate ONLY for:
#
# * Same-account testing — buyer and seller deployed in one AWS account
# (the demo / integration-test setup), OR
# * Customers whose security standards explicitly ALLOW cross-account IAM
# access between the buyer's and seller's accounts (you would then extend
# the resource ARNs / add a cross-account role trust accordingly).
#
# It is NOT the recommended pattern for CROSS-ORGANIZATION buyer<->seller
# interactions (different companies, no shared IAM trust). For that, front the
# seller runtimes with an **AgentCore Gateway + CUSTOM_JWT authorizer backed by
# Cognito** (or the customer's OIDC IdP): the external buyer authenticates with
# a short-lived OAuth2 JWT bearer token at the gateway edge — no IAM in the
# seller's account required. See the `enterprise-auth-gateway` spec.
#
# In short: IAM (this stack) = same-account / trusted cross-account.
# Gateway + CUSTOM_JWT/Cognito = cross-org, zero shared IAM.
# =============================================================================

# =============================================================================
# Parameters
# =============================================================================
Parameters:
Environment:
Type: String
Default: staging
AllowedValues: [staging, production]
SellerRuntimeNamePrefix:
Type: String
Default: "a4a_aamp_seller_"
Description: >
Name prefix of the seller AgentCore runtimes this buyer may invoke. The
SellerInvoke policy wildcards on runtime/<prefix>* so the buyer can reach
the seller's http/mcp/a2a runtimes without hardcoding the CLI-assigned
ARN suffixes (which differ per deploy). Keep this as tight as your naming
allows — it is scoped to this account + region + the seller name prefix,
NOT a blanket bedrock-agentcore:* on all runtimes. Match your seller
deploy --name base (e.g. a4a_aamp_seller_ for a4a_aamp_seller_*_mcp).
SellerAccountId:
Type: String
Default: ""
Description: >
Optional. AWS account id that hosts the seller runtimes. Defaults to THIS
account (same-account model). Set this ONLY for a trusted cross-account
setup your security standards permit; the SellerInvoke resource ARNs then
point at that account. (Cross-ORG should use the gateway path instead.)

# =============================================================================
# Conditions
# =============================================================================
Conditions:
# Use this account for the seller ARNs unless a (trusted) cross-account id is given.
UseThisAccountForSeller: !Equals [!Ref SellerAccountId, ""]

# =============================================================================
# Resources
# =============================================================================
Resources:

# ---------------------------------------------------------------------------
# Buyer AgentCore runtime execution role
#
# Provisioned here (declarative, least-privilege) instead of letting the
# agentcore CLI auto-create a role, so the seller-invoke grant is owned by
# IaC. Pass this role to `agentcore configure --execution-role <arn>`.
# ---------------------------------------------------------------------------
BuyerRuntimeExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "aamp-buyer-${Environment}-runtime-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: bedrock-agentcore.amazonaws.com
Action: sts:AssumeRole
Policies:
# -- Standard AgentCore runtime execution permissions --
# Mirrors the policy the `agentcore` CLI auto-creates for a runtime
# (ECR image pull, CloudWatch Logs + log-delivery, X-Ray tracing,
# CloudWatch metrics, and AgentCore memory/workload access), so a
# CFN-owned role is a drop-in for the CLI-created one.
- PolicyName: AgentCoreRuntimeExecution
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: EcrImagePull
Effect: Allow
Action:
- ecr:BatchGetImage
- ecr:GetDownloadUrlForLayer
- ecr:BatchCheckLayerAvailability
Resource: !Sub "arn:aws:ecr:${AWS::Region}:${AWS::AccountId}:repository/bedrock-agentcore-*"
- Sid: EcrAuth
Effect: Allow
Action: ecr:GetAuthorizationToken
Resource: "*"
- Sid: Logs
Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- logs:DescribeLogStreams
- logs:DescribeLogGroups
Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/bedrock-agentcore/runtimes/*"
- Sid: XRayTracing
Effect: Allow
Action:
- xray:PutTraceSegments
- xray:PutTelemetryRecords
- xray:GetSamplingRules
- xray:GetSamplingTargets
Resource: "*"
- Sid: CloudWatchMetrics
Effect: Allow
Action: cloudwatch:PutMetricData
Resource: "*"
- Sid: AgentCoreMemory
Effect: Allow
Action:
- bedrock-agentcore:CreateEvent
- bedrock-agentcore:GetEvent
- bedrock-agentcore:GetMemory
- bedrock-agentcore:GetMemoryRecord
- bedrock-agentcore:ListActors
- bedrock-agentcore:ListEvents
- bedrock-agentcore:ListMemoryRecords
- bedrock-agentcore:ListSessions
Resource: "*"
# -- Bedrock model access (Claude via the Anthropic Messages endpoint) --
- PolicyName: BedrockInvoke
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- bedrock:InvokeModel
- bedrock:InvokeModelWithResponseStream
Resource: "*"
# -- SCOPED seller invoke (see NOTE at top of this file) --
- PolicyName: SellerInvoke
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: InvokeSellerRuntimes
Effect: Allow
Action: bedrock-agentcore:InvokeAgentRuntime
# Wildcard on the seller name prefix + its endpoint children,
# scoped to a single account + region. Not bedrock-agentcore:*.
Resource:
- !If
- UseThisAccountForSeller
- !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:runtime/${SellerRuntimeNamePrefix}*"
- !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${SellerAccountId}:runtime/${SellerRuntimeNamePrefix}*"
- !If
- UseThisAccountForSeller
- !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${AWS::AccountId}:runtime/${SellerRuntimeNamePrefix}*/*"
- !Sub "arn:aws:bedrock-agentcore:${AWS::Region}:${SellerAccountId}:runtime/${SellerRuntimeNamePrefix}*/*"
Tags:
- Key: Name
Value: !Sub "aamp-buyer-${Environment}-runtime-role"
- Key: Project
Value: ad-buyer-system
- Key: Environment
Value: !Ref Environment

# =============================================================================
# Outputs
# =============================================================================
Outputs:
BuyerRuntimeExecutionRoleArn:
Description: >
Execution role ARN for the buyer AgentCore runtime. Pass to
`agentcore configure --execution-role <arn>` so the runtime can invoke
the seller runtimes (same-account / trusted cross-account only).
Value: !GetAtt BuyerRuntimeExecutionRole.Arn
Export:
Name: !Sub "${AWS::StackName}-BuyerRuntimeExecutionRoleArn"
57 changes: 55 additions & 2 deletions infra/aws/agentcore/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,34 @@

set -euo pipefail

# Load a gitignored .env at repo root if present, so local AWS credentials
# (AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKEN or AWS_PROFILE)
# and the Bedrock endpoint config below can be supplied without exporting them
# by hand. .env is listed in .gitignore and must never be committed.
_SCRIPT_DIR_EARLY="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
_REPO_ROOT_EARLY="$(cd "${_SCRIPT_DIR_EARLY}/../../.." && pwd)"
if [[ -f "${_REPO_ROOT_EARLY}/.env" ]]; then
echo ">>> Loading environment from ${_REPO_ROOT_EARLY}/.env"
set -a
# shellcheck disable=SC1091
source "${_REPO_ROOT_EARLY}/.env"
set +a
fi

REGION="${AWS_REGION:-us-west-2}"
AGENT_NAME="${AGENT_NAME:-}"
AWS_PROFILE="${AWS_PROFILE:-}"
LLM_MODEL="${DEFAULT_LLM_MODEL:-bedrock/us.amazon.nova-pro-v1:0}"
# Default model: current-generation Claude on Bedrock via the Anthropic
# Messages endpoint (see ANTHROPIC_COMPATIBLE_LLM_API_BASE_URL below). This
# replaces the retired Amazon Nova Pro default. The model id is the Bedrock
# model / inference-profile id passed to the Anthropic provider.
LLM_MODEL="${DEFAULT_LLM_MODEL:-us.anthropic.claude-sonnet-5}"
MEMORY_MODEL="${MEMORY_LLM_MODEL:-us.anthropic.claude-haiku-4-5-20251001-v1:0}"
# Bedrock's Anthropic-compatible (Messages API) base URL + API key. Setting
# these routes Claude through CrewAI's native Anthropic provider against
# Bedrock, so the Converse toolUse/toolResult sanitizer is not applied.
ANTHROPIC_BASE_URL="${ANTHROPIC_COMPATIBLE_LLM_API_BASE_URL:-https://bedrock-runtime.${REGION}.amazonaws.com/anthropic}"
BEDROCK_API_KEY="${ANTHROPIC_COMPATIBLE_LLM_API_KEY:-${AWS_BEARER_TOKEN_BEDROCK:-}}"
SELLER_AGENT_URL="${SELLER_AGENT_URL:-}"
DEPLOY_MODE="http"
DO_TEST=false
Expand Down Expand Up @@ -143,6 +167,31 @@ if [[ "${TEST_ONLY}" == "false" ]]; then
rm -f "${_GENERATED_DOCKERFILE}"
fi

# ── Auth stack (opt-in): provision the buyer runtime execution role with a
# scoped grant to invoke the seller runtimes, and pass it to agentcore
# configure. Only when SELLER_AGENT_URL is an AgentCore ARN (buyer->seller
# over InvokeAgentRuntime). For a plain HTTP seller URL or no seller, the CLI
# auto-creates the default role as before. See auth-agentcore.yaml for the
# security model (same-account / trusted cross-account only; cross-org uses
# the gateway + CUSTOM_JWT path).
EXECUTION_ROLE_ARN=""
if [[ "${SELLER_AGENT_URL}" == arn:* ]]; then
AUTH_STACK="aamp-buyer-auth-${ENVIRONMENT:-staging}"
echo ""
echo ">>> Deploying auth stack (buyer runtime role + seller-invoke): ${AUTH_STACK}"
aws cloudformation deploy \
--stack-name "${AUTH_STACK}" \
--template-file infra/aws/agentcore/auth-agentcore.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides "Environment=${ENVIRONMENT:-staging}" \
--region "${REGION}" ${AWS_PROFILE:+--profile "${AWS_PROFILE}"}
EXECUTION_ROLE_ARN=$(aws cloudformation describe-stacks \
--stack-name "${AUTH_STACK}" --region "${REGION}" ${AWS_PROFILE:+--profile "${AWS_PROFILE}"} \
--query "Stacks[0].Outputs[?OutputKey=='BuyerRuntimeExecutionRoleArn'].OutputValue" \
--output text 2>/dev/null || true)
echo " execution role: ${EXECUTION_ROLE_ARN:-<none>}"
fi

# Configure
echo ""
echo ">>> Configuring agent..."
Expand All @@ -152,6 +201,7 @@ if [[ "${TEST_ONLY}" == "false" ]]; then
-rf infra/aws/agentcore/requirements.txt \
-p HTTP \
-r "${REGION}" \
${EXECUTION_ROLE_ARN:+--execution-role "${EXECUTION_ROLE_ARN}"} \
--non-interactive \
--deployment-type container

Expand All @@ -161,14 +211,17 @@ if [[ "${TEST_ONLY}" == "false" ]]; then
agentcore deploy \
--env "DEFAULT_LLM_MODEL=${LLM_MODEL}" \
--env "MANAGER_LLM_MODEL=${LLM_MODEL}" \
--env "PYTHONPATH=/app/src" \
--env "ANTHROPIC_COMPATIBLE_LLM_API_BASE_URL=${ANTHROPIC_BASE_URL}" \
--env "ANTHROPIC_COMPATIBLE_LLM_API_KEY=${BEDROCK_API_KEY}" \
--env "STORAGE_TYPE=sqlite" \
--env "DATABASE_URL=sqlite:///:memory:" \
--env "ANTHROPIC_API_KEY=not-used-with-bedrock" \
--env "SELLER_AGENT_URL=${SELLER_AGENT_URL}" \
--env "AWS_REGION=${REGION}" \
--env "AWS_DEFAULT_REGION=${REGION}" \
--env "CREW_MEMORY_ENABLED=true" \
--env "MEMORY_LLM_MODEL=bedrock/us.amazon.nova-lite-v1:0" \
--env "MEMORY_LLM_MODEL=${MEMORY_MODEL}" \
--auto-update-on-conflict

echo ""
Expand Down
24 changes: 22 additions & 2 deletions infra/aws/agentcore/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
# Requirements for AgentCore CLI deployment path.
# Used by: agentcore configure --requirements-file requirements-agentcore.txt
# Used by: agentcore configure --requirements-file infra/aws/agentcore/requirements.txt
#
# Kept in sync with pyproject.toml [project.dependencies]. The AgentCore
# toolkit-generated Dockerfile installs THIS file (it does not `pip install`
# the ad_buyer package), so every runtime import dependency must be listed
# here — including the git-based shared contract library — or the container
# fails at import with ModuleNotFoundError.
bedrock-agentcore
crewai[tools,anthropic,bedrock]==1.10.1
# Shared IAB agentic contract catalog. The crew path, deals_client, and the
# AgentCoreSellerProxy import iab_agentic_primitives, so it must be in the
# AgentCore image. Git-free GitHub archive tarball (the CodeBuild image has no
# git). Keep in sync with the seller agent's pin.
iab-agentic-primitives[client] @ https://github.com/IABTechLab/iab-agentic-primitives/archive/refs/tags/v0.5.1.tar.gz
# Pin crewai to match pyproject.toml (>=1.14.4). The Bedrock fix patch
# (patches/crewai_bedrock_fix.py) targets 1.14.x and DealBookingFlow relies on
# CrewAI >=1.14 Flow/state behaviors, so an older pin (the previous ==1.10.1)
# would make the AgentCore image diverge from the ECS/source runtime.
crewai[tools,anthropic,bedrock]>=1.14.4,<2.0.0
anthropic>=0.40.0
httpx>=0.27.0
pydantic>=2.0.0
Expand All @@ -15,3 +30,8 @@ sqlalchemy>=2.0.0
aiosqlite>=0.20.0
pyyaml>=6.0.0
nest-asyncio>=1.6.0
# NOTE: the AgentCore build image (astral uv slim) has no `git` binary, so a
# `git+https://` source fails with "Git executable not found". Use the GitHub
# archive tarball instead — a plain HTTPS download that needs no git. Keep this
# pinned to the same tag as pyproject.toml's git ref (v0.5.1).
iab-agentic-primitives[client] @ https://github.com/IABTechLab/iab-agentic-primitives/archive/refs/tags/v0.5.1.tar.gz
4 changes: 3 additions & 1 deletion patches/crewai_agentcore_memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ def _patched_memory_init(self, *args, **kwargs):
if "llm" not in kwargs or kwargs.get("llm") == "gpt-4o-mini":
bedrock_model = os.environ.get(
"MEMORY_LLM_MODEL",
os.environ.get("DEFAULT_LLM_MODEL", "bedrock/us.amazon.nova-lite-v1:0"),
os.environ.get(
"DEFAULT_LLM_MODEL", "bedrock/us.anthropic.claude-haiku-4-5-v1:0"
),
)
kwargs["llm"] = bedrock_model
try:
Expand Down
Loading
Loading