Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
e5823f2
feat: add gateway import command and unhide import from TUI
jesseturner21 Apr 14, 2026
02f7cd3
fix: expand ARN input to show full resource ARN and add gateway support
jesseturner21 Apr 14, 2026
d2671e8
refactor: remove --name and --yes flags from import gateway command
jesseturner21 Apr 14, 2026
916c10e
feat: add e2e tests for import gateway command
jesseturner21 Apr 14, 2026
631e235
chore: gitignore bugbash-resources.json and .omc/
jesseturner21 Apr 14, 2026
9a0a571
feat: preserve gateway executionRoleArn during import
jesseturner21 Apr 15, 2026
9bdc60a
Merge branch 'main' into feat/import-gateway
jesseturner21 Apr 15, 2026
b09a133
refactor: export internal gateway import functions for unit testing
jesseturner21 Apr 16, 2026
6b56208
test: add unit tests for mcpServer target mapping and credential reso…
jesseturner21 Apr 16, 2026
53599c5
test: add unit tests for apiGateway, openApiSchema, smithyModel, lamb…
jesseturner21 Apr 16, 2026
0f36496
test: add unit tests for toGatewaySpec gateway-level field mapping
jesseturner21 Apr 16, 2026
d95937c
test: add unit tests for handleImportGateway full flow validation
jesseturner21 Apr 16, 2026
435fa22
test: add unit tests for buildCredentialArnMap and CFN template matching
jesseturner21 Apr 16, 2026
f518b15
fix: exclude already-deployed logical IDs when building import resour…
jesseturner21 Apr 21, 2026
d314a72
fix(import): translate AccessDenied on GetGateway to a friendly not-f…
jesseturner21 Apr 21, 2026
4932cd9
fix(import): detect AWS_REGION / ARN region mismatch before import
jesseturner21 Apr 21, 2026
098b104
feat: add GovCloud multi-partition support (#908)
aidandaly24 Apr 23, 2026
c0d5b7b
feat: remove deployed/local from status legend (#936)
avi-alpert Apr 23, 2026
b49a06f
feat: upgrade agent inspector to 0.2.1 (#937)
avi-alpert Apr 23, 2026
1903f7d
fix(deploy): honor aws-targets.json region for all SDK and CDK calls …
aidandaly24 Apr 23, 2026
12275c3
chore: bump version to 0.10.0 (#944)
github-actions[bot] Apr 23, 2026
a365bf5
feat: add GitHub Action for automated PR review via AgentCore Harness…
jesseturner21 Apr 24, 2026
cb1e81a
fix: buffer streaming text to avoid per-token log lines in GitHub Act…
jesseturner21 Apr 24, 2026
3d2d671
fix: allow code-based evaluators in online eval configs (#947)
jariy17 Apr 24, 2026
c30ed54
fix: add TTY detection before TUI fallbacks to prevent agent/CI hangs…
jesseturner21 Apr 24, 2026
5271f55
fix: agentcore dev not working in windows (#951)
avi-alpert Apr 24, 2026
933bac8
fix: use pull_request_target for fork PR support (#958)
jesseturner21 Apr 24, 2026
8613657
fix: lower eventExpiryDuration minimum from 7 to 3 days (closes #744)…
notgitika Apr 24, 2026
51e4a8e
fix: display session ID after CLI invoke completes (#957)
notgitika Apr 24, 2026
7a4104d
test: add browser tests for agent inspector (#938)
avi-alpert Apr 24, 2026
7c37fa6
feat: add telemetry schemas and client (#941)
Hweinstock Apr 24, 2026
f8dc490
chore: bump version to 0.11.0 (#967)
github-actions[bot] Apr 24, 2026
343fedc
fix(invoke): auto-generate session ID for bearer-token invocations (#…
aidandaly24 Apr 27, 2026
a725d12
fix: show 'Computing diff changes...' step during deploy diff phase (…
aidandaly24 Apr 27, 2026
acbfb9e
test: split browser tests into its own job, fix logs path (#975)
avi-alpert Apr 27, 2026
f6a3e99
feat(invoke): add --prompt-file and stdin support for long prompts (#…
aidandaly24 Apr 27, 2026
fdd6631
fix(import): remove experimental warning from import command (#977)
jesseturner21 Apr 27, 2026
e7b85c1
fix: duplicate header flash and help menu truncation (closes #895, cl…
notgitika Apr 27, 2026
17b5727
test: configure git in browser tests workflow (#976)
avi-alpert Apr 27, 2026
b6bdb81
fix(import): allow re-import of resource after remove without CDK pip…
jesseturner21 Apr 27, 2026
dbcb231
Merge branch 'main' into feat/import-gateway
jesseturner21 Apr 27, 2026
343469b
style: fix prettier formatting for import-utils and ArnInputScreen
jesseturner21 Apr 27, 2026
7f2fe34
fix(import): address PR review blockers B4, B6, B7, H2, H5, H7, H8
jesseturner21 Apr 27, 2026
da80c72
fix(import): remove credential ARN from error messages to resolve Cod…
jesseturner21 Apr 28, 2026
78a5109
fix(import): only set resourceName when executionRoleArn is present
jesseturner21 Apr 28, 2026
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
13 changes: 13 additions & 0 deletions .github/scripts/prompts/review.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Review this GitHub PR: {pr_url}

You have tools to fetch the PR diff, read files, search the web, and post comments on the PR.

You have these repos cloned locally for context:
- /opt/workspace/agentcore-cli — aws/agentcore-cli
- /opt/workspace/agentcore-l3-cdk-constructs — aws/agentcore-l3-cdk-constructs

Before reviewing, read all existing comments on the PR to understand what has already been discussed. Do not repeat or re-post issues that have already been raised in existing comments.

Review the PR. If there are any serious issues that require code changes before merging, post a comment on the PR for each issue explaining the problem. If there are multiple ways to fix an issue, list the options so the author can choose. Skip style nits and minor suggestions — only flag things that actually need to change.

If all serious issues have already been raised in existing comments, or if you found no new issues, post a single comment on the PR saying it looks good to merge (or that all issues have already been flagged).
21 changes: 21 additions & 0 deletions .github/scripts/prompts/system.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# AgentCore CLI Development Workspace

This workspace contains two repos for developing and testing the AgentCore CLI.

## Repositories

### agentcore-cli/ (`aws/agentcore-cli`)

The terminal experience for creating, developing, and deploying AI agents to AgentCore. Node.js/TypeScript CLI built with Ink (React-based TUI).

### agentcore-l3-cdk-constructs/ (`aws/agentcore-l3-cdk-constructs`)

AWS CDK L3 constructs for declaring and deploying AgentCore infrastructure. Used by agentcore-cli to vend CDK projects when users run `agentcore create`.

## How they relate

`agentcore-cli` is the main product. It vends CDK projects using constructs from `agentcore-l3-cdk-constructs`.

## Testing with a bundled distribution

Run `npm run bundle` in `agentcore-cli/` to create a tar distribution that includes the packaged `agentcore-l3-cdk-constructs`. You can then install it globally with `npm install -g <path-to-tar>` to test the CLI end-to-end.
217 changes: 217 additions & 0 deletions .github/scripts/python/harness_review.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
"""Invoke Bedrock AgentCore Harness to review a GitHub PR.

Reads PR_URL from the environment. Streams harness output to stdout.
Uses raw HTTP with SigV4 signing — no custom service model needed.
"""

import json
import os
import sys
import time
import uuid

import boto3
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
from botocore.eventstream import EventStreamBuffer
from urllib.parse import quote
import urllib3

# ANSI color codes
CYAN = "\033[36m"
YELLOW = "\033[33m"
GREEN = "\033[32m"
RED = "\033[31m"
DIM = "\033[2m"
RESET = "\033[0m"

SCRIPTS_DIR = os.path.join(os.path.dirname(__file__), "..")


def read_prompt(filename):
"""Read a prompt template from the prompts directory."""
path = os.path.join(SCRIPTS_DIR, "prompts", filename)
with open(path) as f:
return f.read()


def invoke_harness(harness_arn, body, region):
"""Send a SigV4-signed request to the harness invoke endpoint. Returns a streaming response.

InvokeHarness is not in standard boto3, so we call the REST API directly.
boto3 is only used to resolve AWS credentials (from env vars, OIDC, etc.)
and sign the request with SigV4. The response is an AWS binary event stream.
"""
session = boto3.Session(region_name=region)
credentials = session.get_credentials().get_frozen_credentials()
url = f"https://bedrock-agentcore.{region}.amazonaws.com/harnesses/invoke?harnessArn={quote(harness_arn, safe='')}"
request = AWSRequest(method="POST", url=url, data=body, headers={
"Content-Type": "application/json",
"Accept": "application/vnd.amazon.eventstream",
})
SigV4Auth(credentials, "bedrock-agentcore", region).add_auth(request)
return urllib3.PoolManager().urlopen(
"POST", url, body=body,
headers=dict(request.headers),
preload_content=False,
timeout=urllib3.Timeout(connect=10, read=600),
)


def parse_events(http_response):
"""Yield (event_type, payload) tuples from the harness binary event stream.

The response arrives as raw bytes in AWS binary event stream format.
EventStreamBuffer reassembles complete events from the 4KB chunks,
and we decode each event's JSON payload before yielding it.
"""
event_buffer = EventStreamBuffer()
for chunk in http_response.stream(4096):
event_buffer.add_data(chunk)
for event in event_buffer:
if event.headers.get(":message-type") == "exception":
payload = json.loads(event.payload.decode("utf-8"))
print(f"\n{RED}ERROR: {payload}{RESET}", file=sys.stderr)
sys.exit(1)
event_type = event.headers.get(":event-type", "")
if event.payload:
yield event_type, json.loads(event.payload.decode("utf-8"))


def print_stream(http_response):
"""Display harness events with GitHub Actions log groups.

The harness streams events as the agent works:
contentBlockStart — a new block begins (text or tool call)
contentBlockDelta — incremental chunks of text or tool input JSON
contentBlockStop — block complete, we now have full tool input to display
messageStop — agent finished
internalServerException — server error

Tool calls are wrapped in ::group::/::endgroup:: for collapsible sections
in the GitHub Actions log UI. Agent reasoning text is printed inline in dim.
"""
start_time = time.time()
iteration = 0
tool_name = None
tool_input = ""
tool_start = 0.0
in_group = False
text_buffer = ""

def close_group():
nonlocal in_group
if in_group:
print("::endgroup::", flush=True)
in_group = False

def flush_text():
nonlocal text_buffer
if text_buffer:
for line in text_buffer.splitlines():
print(f"{DIM}{line}{RESET}", flush=True)
text_buffer = ""

for event_type, payload in parse_events(http_response):

if event_type == "contentBlockStart":
start = payload.get("start", {})
if "toolUse" in start:
tool_name = start["toolUse"].get("name", "unknown")
tool_input = ""
tool_start = time.time()
iteration += 1

elif event_type == "contentBlockDelta":
delta = payload.get("delta", {})
if "text" in delta:
close_group()
text_buffer += delta["text"]
if "toolUse" in delta:
tool_input += delta["toolUse"].get("input", "")

elif event_type == "contentBlockStop":
flush_text()
if tool_name:
elapsed = time.time() - tool_start
try:
parsed = json.loads(tool_input)
except (json.JSONDecodeError, TypeError):
parsed = tool_input

close_group()

cmd = parsed.get("command") if isinstance(parsed, dict) else None
header = f"{CYAN}[{iteration}]{RESET} {YELLOW}{tool_name}{RESET} {DIM}({elapsed:.1f}s){RESET}"
if cmd:
header += f": $ {cmd}"

print(f"::group::{header}", flush=True)
in_group = True

if isinstance(parsed, dict):
for k, v in parsed.items():
if k != "command":
print(f" {DIM}{k}:{RESET} {str(v)[:300]}", flush=True)

tool_name = None
tool_input = ""

elif event_type == "messageStop":
flush_text()
close_group()
if payload.get("stopReason") == "end_turn":
total = time.time() - start_time
print(f"\n\n{GREEN}{'=' * 50}", flush=True)
print(f" Done ({int(total // 60)}m {int(total % 60)}s)", flush=True)
print(f"{'=' * 50}{RESET}", flush=True)

elif event_type == "internalServerException":
close_group()
print(f"\n{RED}ERROR: {payload}{RESET}", file=sys.stderr)
sys.exit(1)

close_group()
total = time.time() - start_time
print(f"\n{GREEN}Review complete.{RESET} {DIM}({iteration} tool calls, {int(total)}s total){RESET}")


# --- Main ---

# All config comes from environment variables (set via GitHub secrets/workflow)
MODEL_ID = os.environ.get("HARNESS_MODEL_ID", "us.anthropic.claude-opus-4-7")
HARNESS_ARN = os.environ.get("HARNESS_ARN", "")
PR_URL = os.environ.get("PR_URL", "")

for name, val in [("HARNESS_ARN", HARNESS_ARN), ("PR_URL", PR_URL)]:
if not val:
print(f"{RED}ERROR: {name} environment variable is required{RESET}", file=sys.stderr)
sys.exit(1)

# Extract region from the ARN (arn:aws:bedrock-agentcore:{region}:{account}:harness/{id})
REGION = HARNESS_ARN.split(":")[3]
SESSION_ID = str(uuid.uuid4()).upper()

print(f"{CYAN}Session:{RESET} {SESSION_ID}")
print(f"{CYAN}PR:{RESET} {PR_URL}")
print(f"{CYAN}Harness:{RESET} {HARNESS_ARN}")
print()

SYSTEM_PROMPT = read_prompt("system.md")
REVIEW_PROMPT = read_prompt("review.md").format(pr_url=PR_URL)

request_body = json.dumps({
"runtimeSessionId": SESSION_ID,
"systemPrompt": [{"text": SYSTEM_PROMPT}],
"messages": [{"role": "user", "content": [{"text": REVIEW_PROMPT}]}],
"model": {"bedrockModelConfig": {"modelId": MODEL_ID}},
})

http_response = invoke_harness(HARNESS_ARN, request_body, REGION)

if http_response.status != 200:
error = http_response.read().decode("utf-8")
print(f"{RED}ERROR: HTTP {http_response.status}: {error}{RESET}", file=sys.stderr)
sys.exit(1)

print_stream(http_response)
45 changes: 45 additions & 0 deletions .github/workflows/e2e-tests-full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,48 @@ jobs:
GEMINI_API_KEY: ${{ env.E2E_GEMINI_API_KEY }}
CDK_TARBALL: ${{ env.CDK_TARBALL }}
run: npm run test:e2e
browser-tests:
runs-on: ubuntu-latest
environment: e2e-testing
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event_name == 'workflow_dispatch' && github.ref || 'main' }}
- uses: actions/setup-node@v6
with:
node-version: '20.x'
cache: 'npm'
- name: Configure git
run: |
git config --global user.email "ci@amazon.com"
git config --global user.name "CI"
- uses: astral-sh/setup-uv@v7
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v6
with:
role-to-assume: ${{ secrets.E2E_AWS_ROLE_ARN }}
aws-region: ${{ inputs.aws_region || 'us-east-1' }}
- name: Get AWS Account ID
id: aws
run: echo "account_id=$(aws sts get-caller-identity --query Account --output text)" >> "$GITHUB_OUTPUT"
- run: npm ci
- run: npm run build
- name: Install CLI globally
run: npm install -g "$(npm pack | tail -1)"
- name: Install Playwright browsers
run: npx playwright install chromium --with-deps
- name: Run browser tests
env:
AWS_ACCOUNT_ID: ${{ steps.aws.outputs.account_id }}
AWS_REGION: ${{ inputs.aws_region || 'us-east-1' }}
PLAYWRIGHT_TRACE: 'off'
run: npm run test:browser
- name: Print browser test debug info
if: failure()
run: |
echo "=== Dev server PTY output ==="
cat browser-tests/test-results/agentcore-dev-pty.log 2>/dev/null || echo "(no pty log)"
echo ""
echo "=== Error contexts ==="
find browser-tests/test-results -name 'error-context.md' -exec echo "--- {} ---" \; -exec cat {} \; 2>/dev/null || echo "(no error contexts)"
Loading
Loading