Skill: killerapp/aws-agentcore-langgraph (version 1.0.2)

--- name: aws-agentcore-langgraph description: Deploy production LangGraph agents on AWS Bedrock AgentCore. Use for (1) multi-agent systems with orchestrator and specialist agent patterns, (2) building stateful agents with persistent cross-session memory, (3) connecting external tools via AgentCore Gateway (MCP, Lambda, APIs), (4) managing shared context across distributed agents, or (5) deploying complex agent ecosystems via CLI with production observability and scaling. --- # AWS AgentCore + LangGraph Multi-agent systems on AWS Bedrock AgentCore with LangGraph orchestration. Source: https://github.com/aws/bedrock-agentcore-starter-toolkit ## Install ```bash pip install bedrock-agentcore bedrock-agentcore-starter-toolkit langgraph uv tool install bedrock-agentcore-starter-toolkit # installs agentcore CLI ``` ## Quick Start ```python from langgraph.graph import StateGraph, START from langgraph.graph.message import add_messages from langgraph.prebuilt import ToolNode, tools_condition # routing + tool execution from bedrock_agentcore.runtime import BedrockAgentCoreApp from typing import Annotated from typing_extensions import TypedDict class State(TypedDict): messages: Annotated[list, add_messages] builder = StateGraph(State) builder.add_node("agent", agent_node) builder.add_node("tools", ToolNode(tools)) # prebuilt tool executor builder.add_conditional_edges("agent", tools_condition) # routes to tools or END builder.add_edge(START, "agent") graph = builder.compile() app = BedrockAgentCoreApp() # Wraps as HTTP service on port 8080 (/invocations, /ping) @app.entrypoint def invoke(payload, context): result = graph.invoke({"messages": [("user", payload.get("prompt", ""))]}) return {"result": result["messages"][-1].content} app.run() ``` ## CLI Commands | Command | Purpose | |---------|---------| | `agentcore configure -e agent.py --region us-east-1` | Setup | | `agentcore configure -e agent.py --region us-east-1 --name my_agent --non-interactive` | Scripted setup | | `agentcore launch --deployment-type container` | Deploy (container mode) | | `agentcore launch --disable-memory` | Deploy without memory subsystem | | `agentcore dev` | Hot-reload local dev server | | `agentcore invoke '{"prompt": "Hello"}'` | Test | | `agentcore destroy` | Cleanup | ## Core Patterns ### Multi-Agent Orchestration - Orchestrator delegates to specialists (customer service, e-commerce, healthcare, financial, etc.) - Specialists: inline functions or separate deployed agents; all share `session_id` for context ### Memory (STM/LTM) ```python from bedrock_agentcore.memory import MemoryClient memory = MemoryClient() memory.create_event(session_id, actor_id, event_type, payload) # Store events = memory.list_events(session_id) # Retrieve (returns list) ``` - **STM**: Turn-by-turn within session | **LTM**: Facts/decisions across sessions/agents - ~10s eventual consistency after writes ### Gateway Tools ```bash python -m bedrock_agentcore.gateway.deploy --stack-name my-agents --region us-east-1 ``` ```python from bedrock_agentcore.gateway import GatewayToolClient gateway = GatewayToolClient() result = gateway.call("tool_name", param1=value1, param2=value2) ``` - Transport: Fallback Mock (local), Local MCP servers, Production Gateway (Lambda/REST/MCP) - Auto-configures `BEDROCK_AGENTCORE_GATEWAY_URL` after deploy ## Decision Tree ``` Multiple agents coordinating? → Orchestrator + specialists pattern Persistent cross-session memory? → AgentCore Memory (not LangGraph checkpoints) External APIs/Lambda? → AgentCore Gateway Single agent, simple? → Quick Start above Complex multi-step logic? → StateGraph + tools_condition + ToolNode ``` ## Key Concepts - **AgentCore Runtime**: HTTP service on port 8080 (handles `/invocations`, `/ping`) - **AgentCore Memory**: Managed cross-session/cross-agent memory - **LangGraph Routing**: `tools_condition` for agent→tool routing, `ToolNode` for execution - **AgentCore Gateway**: Transforms APIs/Lambda into MCP tools with auth ## Naming Rules - Start with letter, only letters/numbers/underscores, 1-48 chars: `my_agent` not `my-agent` ## Troubleshooting | Issue | Fix | |-------|-----| | `on-demand throughput isn't supported` | Use `us.anthropic.claude-*` inference profiles | | `Model use case details not submitted` | Fill Anthropic form in Bedrock Console | | `Invalid agent name` | Use underscores not hyphens | | Memory empty after write | Wait ~10s (eventual consistency) | | Container not reading .env | Set ENV in Dockerfile, not .env | | Memory not working after deploy | Check logs for "Memory enabled/disabled" | | `list_events` returns empty | Check actor_id/session_id match; `event['payload']` is a list | | Gateway "Unknown tool" | Lambda must strip `___` prefix from `bedrockAgentCoreToolName` | | Platform mismatch warning | Normal - CodeBuild handles ARM64 cross-platform builds | ## References - [agentcore-cli.md](references/agentcore-cli.md) - CLI commands, deployment, lifecycle - [agentcore-runtime.md](references/agentcore-runtime.md) - Streaming, async, observability - [agentcore-memory.md](references/agentcore-memory.md) - STM/LTM patterns, API reference - [agentcore-gateway.md](references/agentcore-gateway.md) - Tool integration, MCP, Lambda - [langgraph-patterns.md](references/langgraph-patterns.md) - StateGraph design, routing - [reference-architecture-advertising-agents-use-case.pdf](references/reference-architecture-advertising-agents-use-case.pdf) - Example multi-agent architecture

path: references/agentcore-cli.md
content: # AgentCore CLI Reference

## Primitives Overview

| Primitive | Purpose | agentcore CLI | AWS CLI |
|-----------|---------|---------------|---------|
| Runtime | Deploy/manage agents | `status/deploy/invoke` | `list-agent-runtimes` |
| Memory | STM/LTM persistence | `memory` | `list-memories` |
| Gateway | API to MCP tools | `gateway` | `list-gateways` |
| Identity | OAuth/credentials | `identity` | `list-workload-identities` |
| Code Interpreter | Sandboxed code | - | `list-code-interpreters` |
| Browser | Web browsing | - | `list-browsers` |
| Observability | Traces/logs | `obs` | CloudWatch |

## agentcore CLI Commands

| Command | Purpose |
|---------|---------|
| `agentcore configure` | Interactive setup (creates `.bedrock_agentcore.yaml` - add to .gitignore) |
| `agentcore status` | Current agent status |
| `agentcore deploy` | Deploy via CodeBuild (recommended) |
| `agentcore deploy --local` | Local dev server |
| `agentcore invoke '{"prompt":"Hi"}'` | Test invocation |
| `agentcore destroy` | Cleanup all resources |
| `agentcore dev` | Hot-reload dev server |

### Memory Commands
- `agentcore memory list` / `create NAME` / `get --memory-id ID` / `delete --memory-id ID`

### Gateway Commands
- `agentcore gateway list` / `create NAME` / `target list`

### Identity Commands
- `agentcore identity list` / `create NAME`

### Observability Commands
- `agentcore obs traces` / `logs` / `spans`

## AWS CLI (bedrock-agentcore-control)

| Operation | Command |
|-----------|---------|
| List runtimes | `aws bedrock-agentcore-control list-agent-runtimes --region us-east-1` |
| List memories | `aws bedrock-agentcore-control list-memories --region us-east-1` |
| List gateways | `aws bedrock-agentcore-control list-gateways --region us-east-1` |
| Get runtime | `aws bedrock-agentcore-control get-agent-runtime --agent-runtime-id ID` |
| Get memory | `aws bedrock-agentcore-control get-memory --memory-id ID` |

### CloudWatch Logs
```bash
# Runtime logs
aws logs tail /aws/bedrock-agentcore/runtimes/AGENT_ID-DEFAULT \
  --log-stream-name-prefix "$(date +%Y/%m/%d)/[runtime-logs]" --since 1h
```

## Environment Variables

Auto-injected by toolkit during `agentcore launch`:
- `BEDROCK_AGENTCORE_MEMORY_ID` - Memory resource ID
- `BEDROCK_AGENTCORE_MEMORY_NAME` - Memory name
- `AWS_REGION` - Deployment region

Custom: `agentcore launch --env GUARDRAIL_ID="xyz" --env KB_ID="abc"`

## Documentation

| Resource | URL |
|----------|-----|
| CLI Reference | https://aws.github.io/bedrock-agentcore-starter-toolkit/api-reference/cli.html |
| AWS CLI | https://awscli.amazonaws.com/v2/documentation/api/latest/reference/bedrock-agentcore-control/index.html |


path: references/agentcore-gateway.md
content: # AgentCore Gateway Integration

Gateway transforms APIs, Lambda, and MCP servers into unified MCP-compatible tools.

## Quick Start

```bash
agentcore gateway create-mcp-gateway --name MyGateway --region us-east-1
agentcore gateway create-mcp-gateway-target --gateway-arn ARN --gateway-url URL \
  --role-arn ROLE --name OrderLookup --target-type lambda
```

## Target Types

| Type | CLI `--target-type` | Use Case |
|------|---------------------|----------|
| Lambda | `lambda` | Custom code, DB queries |
| OpenAPI | `openApiSchema` | REST APIs with spec |
| MCP Server | `mcpServer` | Existing MCP servers |
| Smithy | `smithyModel` | AWS Smithy models |
| API Gateway | SDK only | API Gateway stages |

## CLI Commands

| Command | Purpose |
|---------|---------|
| `agentcore gateway create-mcp-gateway --name NAME` | Create gateway |
| `agentcore gateway list-mcp-gateways` | List gateways |
| `agentcore gateway create-mcp-gateway-target ...` | Add target |
| `agentcore gateway list-mcp-gateway-targets --gateway-arn ARN` | List targets |

## Lambda Handler Requirements

**Event format**: Flat dict of input schema properties (`{"order_id": "ORD-123"}`).

**Tool name prefix**: Names include target prefix `{target_name}___{tool_name}`. Strip it:
```python
def lambda_handler(event, context):
    full_name = context.client_context.custom.get('bedrockAgentCoreToolName', '')
    tool_name = full_name.split("___")[-1]  # Strip prefix
    if tool_name == "lookup_order":
        return {"status": "found", "order_id": event["order_id"]}
```

## Credential Providers

| Type | Use Case |
|------|----------|
| `GATEWAY_IAM_ROLE` | Lambda, internal AWS |
| `API_KEY` | Third-party APIs |
| `OAUTH` | OAuth2 APIs |

## LangGraph Integration

```python
from langchain_core.tools import StructuredTool
import boto3

data_client = boto3.client('bedrock-agentcore', region_name='us-east-1')

def call_gateway(tool_name: str, args: dict) -> dict:
    return data_client.invoke_mcp_tool(
        gatewayIdentifier="MyGateway", toolName=tool_name, arguments=args
    ).get("result", {})

tools = [StructuredTool.from_function(
    func=lambda order_id: call_gateway("lookup_order", {"order_id": order_id}),
    name="lookup_order", description="Look up order by ID"
)]
```

## Pre-built Integrations

Console one-click: Salesforce, Slack, Jira, Asana, Zendesk

## Troubleshooting

| Issue | Solution |
|-------|----------|
| "Unknown tool" in Lambda | Strip `___` prefix from tool name |
| Lambda timeout | Increase timeout |
| Auth errors | Check Cognito token, `allowedClients` |
| Tools not discoverable | Verify target status ACTIVE |

## Documentation

| Resource | URL |
|----------|-----|
| Gateway Overview | https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway.html |
| Lambda Targets | https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-add-target-lambda.html |
| MCP Server Targets | https://aws.amazon.com/blogs/machine-learning/transform-your-mcp-architecture-unite-mcp-servers-through-agentcore-gateway/ |


path: references/agentcore-memory.md
content: # AgentCore Memory Integration

## Memory Types

| Type | Scope | Use Case |
|------|-------|----------|
| Short-Term (STM) | Within session | Turn-by-turn conversation |
| Long-Term (LTM) | Across sessions | User preferences, facts |
| LangGraph Checkpointing | Per thread | Graph state persistence |

## Short-Term Memory API

```python
from bedrock_agentcore.memory import MemoryClient
client = MemoryClient(region_name="us-east-1")

# Store event
client.create_event(
    memory_id=memory_id, actor_id="user-123", session_id="session-abc",
    messages=[("Question?", "USER"), ("Answer.", "ASSISTANT")]
)

# Retrieve - returns LIST directly (not dict)
events = client.list_events(memory_id=memory_id, actor_id="user-123", session_id="session-abc")
```

**Event structure**: `events` is a list; each event's `payload` is also a LIST of messages.

**CRITICAL**: ~10 second eventual consistency delay after `create_event`.

## Parsing Events

```python
for event in events:
    for msg in event.get("payload", []):
        if "conversational" in msg:
            role = msg["conversational"].get("role", "").lower()
            content = msg["conversational"].get("content", {}).get("text", "")
```

## Memory ID Injection

Toolkit auto-injects `BEDROCK_AGENTCORE_MEMORY_ID` via `agentcore launch`. Access with:
```python
memory_id = os.getenv("BEDROCK_AGENTCORE_MEMORY_ID")
```

## CLI Commands

| Command | Purpose |
|---------|---------|
| `agentcore memory create --name NAME --strategy STM` | Create memory |
| `agentcore memory list` | List memories |
| `agentcore memory get --memory-id ID` | Get details |
| `agentcore memory delete --memory-id ID` | Delete |

## Production Checkpointers

```python
# PostgreSQL (recommended)
from langgraph.checkpoint.postgres import AsyncPostgresSaver
async with AsyncPostgresSaver.from_conn_string(DATABASE_URL) as checkpointer:
    graph = builder.compile(checkpointer=checkpointer)

# SQLite (local dev)
from langgraph.checkpoint.sqlite import SqliteSaver
checkpointer = SqliteSaver.from_conn_string("checkpoints.db")
```

Install: `pip install langgraph-checkpoint-postgres` (or `-sqlite`, `-redis`)

## Environment Variables

| Variable | Source |
|----------|--------|
| `BEDROCK_AGENTCORE_MEMORY_ID` | Auto-injected |
| `BEDROCK_AGENTCORE_MEMORY_NAME` | Auto-injected |

Custom: `agentcore launch --env GUARDRAIL_ID="xyz"`

## Episodic Memory (Dec 2025)

Agents learn from experiences across sessions. Auto-enabled with LTM. Available: US/EU/APAC regions.

## Documentation

| Resource | URL |
|----------|-----|
| Memory Overview | https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/memory.html |
| Memory Strategies | https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/memory-strategies.html |


path: references/agentcore-runtime.md
content: # AgentCore Runtime Patterns

## Basic Entrypoint

```python
from bedrock_agentcore.runtime import BedrockAgentCoreApp
app = BedrockAgentCoreApp()

@app.entrypoint
def invoke(payload, context):
    result = graph.invoke({"messages": [("user", payload.get("prompt", ""))]})
    return {"result": result["messages"][-1].content}

app.run()  # HTTP server on port 8080
```

## Async Streaming

```python
@app.entrypoint
async def invoke(payload, context):
    async for chunk in graph.astream({"messages": [("user", payload["prompt"])]}, stream_mode="updates"):
        for node_name, output in chunk.items():
            if "messages" in output:
                yield output["messages"][-1].content
```

Stream modes: `"values"` (full state), `"updates"` (changed keys, recommended), `"messages"` (messages only)

## CLI Reference

| Command | Purpose |
|---------|---------|
| `agentcore configure -e agent.py --region us-east-1` | Interactive setup |
| `agentcore configure --non-interactive --name my_agent` | Scripted setup |
| `agentcore launch` | Deploy via CodeBuild (ARM64) |
| `agentcore launch --local` | Local Docker testing |
| `agentcore invoke '{"prompt":"Hi"}'` | Test invocation |
| `agentcore status --verbose` | Detailed status |
| `agentcore destroy --dry-run` | Preview cleanup |

**Agent naming**: Letters/numbers/underscores only, 1-48 chars. Use `my_agent` not `my-agent`.

**Platform note**: ARM64 containers required. CodeBuild handles cross-platform builds automatically.

## Bedrock Models

| Model | ID | Notes |
|-------|-----|-------|
| Claude 3 Haiku | `anthropic.claude-3-haiku-20240307-v1:0` | Fast, on-demand |
| Claude 3.5 Haiku | `anthropic.claude-3-5-haiku-20241022-v1:0` | Better quality |
| Claude 3.5 Sonnet | `anthropic.claude-3-5-sonnet-20241022-v2:0` | High quality |
| Claude 4.5 Haiku | `us.anthropic.claude-haiku-4-5-20251001-v1:0` | Requires inference profile |

**CRITICAL**: Claude 4.x requires `us.` or `eu.` prefix. Using `anthropic.claude-haiku-4-5-*` causes `ValidationException`.

```python
# Claude 3.5 (on-demand)
llm = init_chat_model("anthropic.claude-3-5-haiku-20241022-v1:0", model_provider="bedrock_converse")

# Claude 4.5 (inference profile)
llm = init_chat_model("us.anthropic.claude-haiku-4-5-20251001-v1:0", model_provider="bedrock_converse")
```

## Model Access Errors

`ResourceNotFoundException`: Go to Bedrock Console > Model access > Request Anthropic access > Wait ~15 min.

## Local Testing

```bash
python agent.py
curl -X POST http://localhost:8080/invocations -H "Content-Type: application/json" -d '{"prompt":"Test"}'
curl http://localhost:8080/ping  # Health check
```

## Documentation

| Resource | URL |
|----------|-----|
| Runtime Overview | https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime.html |
| Model IDs | https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html |


path: references/langgraph-patterns.md
content: # LangGraph 1.0 Patterns

## State Definition

```python
from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages

class State(TypedDict):
    messages: Annotated[list, add_messages]  # Accumulates
    current_step: str                         # Overwrites
```

Reducers: `add_messages` (built-in), `operator.add` (concat lists), `lambda a, b: a + b` (sum)

## Graph Building

```python
from langgraph.graph import StateGraph, START, END

builder = StateGraph(State)
builder.add_node("agent", agent_node)
builder.add_node("tools", ToolNode(tools))
builder.add_edge(START, "agent")
builder.add_edge("tools", "agent")
builder.add_conditional_edges("agent", tools_condition)  # Routes to "tools" or END
graph = builder.compile()
```

## Tools Pattern

```python
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_core.tools import tool

@tool
def search(query: str) -> str:
    """Search the web."""
    return f"Results for: {query}"

tools = [search]
llm_with_tools = llm.bind_tools(tools)

def agent(state):
    return {"messages": [llm_with_tools.invoke(state["messages"])]}
```

## Command Pattern (State Updates from Tools)

```python
from langgraph.types import Command

@tool
def lookup_user(user_id: str) -> Command:
    return Command(update={"user_info": db.get(user_id), "messages": [...]})
```

## Checkpointing

```python
# In-memory (testing)
from langgraph.checkpoint.memory import InMemorySaver
graph = builder.compile(checkpointer=InMemorySaver())

# PostgreSQL (production)
from langgraph.checkpoint.postgres import AsyncPostgresSaver
async with AsyncPostgresSaver.from_conn_string(URL) as cp:
    graph = builder.compile(checkpointer=cp)

config = {"configurable": {"thread_id": "user-123"}}
result = graph.invoke(inputs, config)
```

State ops: `graph.get_state(config)`, `graph.get_state_history(config)`, `graph.update_state(config, {...})`

## Streaming

```python
# Updates only (recommended)
async for chunk in graph.astream(inputs, config, stream_mode="updates"):
    for node, output in chunk.items():
        print(f"{node}: {output}")
```

Modes: `"updates"` (changed keys), `"values"` (full state), `"messages"` (messages only)

## Migration Notes (1.0)

| Change | Notes |
|--------|-------|
| `create_react_agent` | Deprecated, use `langchain.agents.create_agent` |
| `prompt=` | Changed to `system_prompt=` |
| Python 3.9 | Requires 3.10+ |
| `ToolNode`, `tools_condition` | Still supported |

## Best Practices

- **Compile once** at startup, not per-request
- **Use async** for production (`ainvoke`, `astream`)
- **Pure node functions** - return updates, don't mutate
- **Thread IDs** for multi-tenancy

## Documentation

| Resource | URL |
|----------|-----|
| LangGraph | https://langchain-ai.github.io/langgraph/ |
| Concepts | https://langchain-ai.github.io/langgraph/concepts/ |
| Checkpointing | https://langchain-ai.github.io/langgraph/concepts/persistence/ |


path: scripts/agent-details.sh
content: #!/bin/bash
# Get detailed info for an agent runtime
# Usage: ./agent-details.sh <agent-id> [region] [profile]

AGENT_ID="${1:?Usage: ./agent-details.sh <agent-id> [region] [profile]}"
REGION="${2:-us-east-1}"
PROFILE="${3:-}"

if [ -n "$PROFILE" ]; then
  export AWS_PROFILE="$PROFILE"
fi
export AWS_REGION="$REGION"

echo "=== Agent: $AGENT_ID ==="

aws bedrock-agentcore-control get-agent-runtime \
  --agent-runtime-id "$AGENT_ID" \
  --output json 2>/dev/null | \
  jq '{
    id: .agentRuntimeId,
    name: .agentRuntimeName,
    status: .status,
    version: .agentRuntimeVersion,
    image: .containerConfiguration?.containerUri,
    memory: .memoryId,
    network: .networkConfiguration?.networkMode
  }'


path: scripts/list-all.sh
content: #!/bin/bash
# List all AgentCore resources in region
# Usage: ./list-all.sh [region] [profile]

REGION="${1:-us-east-1}"
PROFILE="${2:-}"

if [ -n "$PROFILE" ]; then
  export AWS_PROFILE="$PROFILE"
fi
export AWS_REGION="$REGION"

echo "=== AgentCore Resources in $REGION ==="

echo -e "\n## Runtimes (Agents)"
aws bedrock-agentcore-control list-agent-runtimes --output json 2>/dev/null | \
  jq -r '.agentRuntimes[]? | [.agentRuntimeId, .status, .agentRuntimeName] | @tsv' | \
  column -t || echo "  (none)"

echo -e "\n## Memories"
aws bedrock-agentcore-control list-memories --output json 2>/dev/null | \
  jq -r '.memories[]? | [.id, .status] | @tsv' | \
  column -t || echo "  (none)"

echo -e "\n## Gateways"
aws bedrock-agentcore-control list-gateways --output json 2>/dev/null | \
  jq -r '.gateways[]? | [.gatewayId, .status, .name] | @tsv' | \
  column -t || echo "  (none)"

echo -e "\n## Workload Identities"
aws bedrock-agentcore-control list-workload-identities --output json 2>/dev/null | \
  jq -r '.workloadIdentities[]? | [.workloadIdentityId, .name] | @tsv' | \
  column -t || echo "  (none)"

echo -e "\n## Policies"
aws bedrock-agentcore-control list-policies --output json 2>/dev/null | \
  jq -r '.policies[]? | [.policyId, .status] | @tsv' | \
  column -t || echo "  (none)"

echo -e "\n## Code Interpreters"
aws bedrock-agentcore-control list-code-interpreters --output json 2>/dev/null | \
  jq -r '.codeInterpreters[]? | [.codeInterpreterId, .status] | @tsv' | \
  column -t || echo "  (none)"

echo -e "\n## Browsers"
aws bedrock-agentcore-control list-browsers --output json 2>/dev/null | \
  jq -r '.browsers[]? | [.browserId, .status] | @tsv' | \
  column -t || echo "  (none)"


path: scripts/memory-details.sh
content: #!/bin/bash
# Get detailed info for a memory resource
# Usage: ./memory-details.sh <memory-id> [region] [profile]

MEMORY_ID="${1:?Usage: ./memory-details.sh <memory-id> [region] [profile]}"
REGION="${2:-us-east-1}"
PROFILE="${3:-}"

if [ -n "$PROFILE" ]; then
  export AWS_PROFILE="$PROFILE"
fi
export AWS_REGION="$REGION"

echo "=== Memory: $MEMORY_ID ==="

aws bedrock-agentcore-control get-memory \
  --memory-id "$MEMORY_ID" \
  --output json 2>/dev/null | \
  jq '{
    id: .id,
    status: .status,
    strategies: [.strategies[]?.name // empty],
    eventExpiry: .eventExpiryDuration,
    created: .createdAt
  }'


path: scripts/tail-logs.sh
content: #!/bin/bash
# Tail runtime logs for an agent
# Usage: ./tail-logs.sh <agent-id> [since] [region] [profile]

AGENT_ID="${1:?Usage: ./tail-logs.sh <agent-id> [since] [region] [profile]}"
SINCE="${2:-5m}"
REGION="${3:-us-east-1}"
PROFILE="${4:-}"

if [ -n "$PROFILE" ]; then
  export AWS_PROFILE="$PROFILE"
fi
export AWS_REGION="$REGION"

LOG_GROUP="/aws/bedrock-agentcore/runtimes/${AGENT_ID}-DEFAULT"
DATE_PREFIX="$(date +%Y/%m/%d)"

echo "=== Logs for $AGENT_ID (since $SINCE) ==="
aws logs tail "$LOG_GROUP" \
  --log-stream-name-prefix "${DATE_PREFIX}/[runtime-logs]" \
  --since "$SINCE" \
  --format short


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)