Skip to content
Closed
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
50 changes: 50 additions & 0 deletions .github/workflows/canary.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: canary
on:
schedule:
- cron: "0 * * * *"
workflow_dispatch:

concurrency:
group: canary
cancel-in-progress: false

permissions:
id-token: write
contents: read

jobs:
# Resolve the commit hash for the latest published @rc version.
resolve_release:
runs-on: ubuntu-latest
outputs:
ref: ${{ steps.resolve.outputs.ref }}
steps:
- uses: actions/setup-node@v4
with:
node-version: "24"
- name: Resolve latest RC release commit
id: resolve
run: |
version="$(npm view @aws/agentcore@rc version)"
tag_ref="refs/tags/v${version}"
# Resolve annotated tags to their commit hash when needed.
ref="$(git ls-remote https://github.com/aws/agentcore-cli.git "${tag_ref}" "${tag_ref}^{}" | awk -v tag_ref="${tag_ref}" '
$2 == tag_ref "^{}" { print $1; found = 1; exit }
$2 == tag_ref { tag_sha = $1 }
END { if (!found && tag_sha != "") print tag_sha }
')"
if [[ -z "${ref}" ]]; then
echo "::error::No upstream tag found for @aws/agentcore@rc version ${version}"
exit 1
fi
echo "ref=${ref}" >> "$GITHUB_OUTPUT"

e2e:
needs: resolve_release
uses: ./.github/workflows/e2e-test.yml
with:
ref: ${{ needs.resolve_release.outputs.ref }}
tags: canary
linuxOnly: true
secrets:
WORKFLOW_SECRETS_READER_ROLE_ARN: ${{ secrets.WORKFLOW_SECRETS_READER_ROLE_ARN }}
35 changes: 20 additions & 15 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ on:
required: false
type: string
default: ""
linuxOnly:
description: Run E2E tests only on Linux
required: false
type: boolean
default: false
secrets:
WORKFLOW_SECRETS_READER_ROLE_ARN:
required: true
Expand All @@ -25,9 +30,13 @@ on:
description: Vitest tag expression, for example runtime || canary
type: string
default: ""
linuxOnly:
description: Run E2E tests only on Linux
type: boolean
default: false

concurrency:
group: e2e-test-${{ inputs.ref || github.ref }}-${{ inputs.tags || 'all' }}
group: e2e-test-${{ inputs.ref || github.ref }}-${{ inputs.tags || 'all' }}-${{ inputs.linuxOnly && 'linux-only' || 'all-os' }}
cancel-in-progress: false

env:
Expand All @@ -40,7 +49,7 @@ jobs:
id-token: write
contents: read
outputs:
is-authorized: ${{ steps.check.outputs.is_authorized }}
is-authorized: ${{ github.workflow == 'canary' || steps.check.outputs.is_authorized == 'true' }}
steps:
- name: Fetch secrets from Secrets Manager
uses: aws/agentcore-devx-devtools/.github/actions/fetch-secrets@75989f65f7f193deaf83c237c36572d1a8f800b2
Expand Down Expand Up @@ -71,25 +80,21 @@ jobs:
name: test (${{ matrix.name }})
needs: authorize
if: needs.authorize.outputs.is-authorized == 'true'
runs-on: ${{ fromJSON(matrix.runner) }}
runs-on: >-
${{ fromJSON(
matrix.name == 'Linux' &&
format('["codebuild-agentcore-e2e-{0}-{1}","e2e-linux"]', github.run_id, github.run_attempt)
|| matrix.name == 'Windows' &&
format('["codebuild-agentcore-e2e-{0}-{1}","image:windows-1.0","e2e-windows"]', github.run_id, github.run_attempt)
|| matrix.name == 'macOS' && '["macos-latest"]'
) }}
permissions:
id-token: write
contents: read
strategy:
fail-fast: false
matrix:
include:
# Per-job labels stop GitHub from routing a job to the runner CodeBuild created for another job.
# https://docs.aws.amazon.com/codebuild/latest/userguide/sample-github-action-runners-update-labels.html
- name: Linux
runner: '["codebuild-agentcore-e2e-${{ github.run_id }}-${{ github.run_attempt }}", "e2e-linux"]'
- name: Windows
runner: '["codebuild-agentcore-e2e-${{ github.run_id }}-${{ github.run_attempt }}", "image:windows-1.0", "e2e-windows"]'

# CodeBuild does not support macOS.
# https://docs.aws.amazon.com/codebuild/latest/userguide/action-runner-questions.html#action-runner-platform
- name: macOS
runner: '["macos-latest"]'
name: ${{ fromJSON(inputs.linuxOnly && '["Linux"]' || '["Linux","Windows","macOS"]') }}
steps:
- uses: actions/checkout@v7
with:
Expand Down
2 changes: 1 addition & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion command.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# AgentCore CLI command reference

This reference was generated from `agentcore --help` for version `1.0.0-rc.4`.
This reference was generated from `agentcore --help` for version `1.0.0-rc.5`.

## Table of contents

Expand Down
1 change: 1 addition & 0 deletions e2eTest/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const E2E_PREFIX = "e2e";

export const TAGS = {
CANARY: "canary",
HARNESS: "harness",
RUNTIME: "runtime",
} as const;
3 changes: 2 additions & 1 deletion e2eTest/project/harness.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ const HarnessInvokeResponseSchema = z.object({
transcript: z.array(TranscriptItemSchema).min(2),
});

describe("add, deploy, and invoke harnesses", { sequential: true, tags: [TAGS.HARNESS] }, () => {
const harnessTags = [TAGS.HARNESS, TAGS.CANARY];
describe("add, deploy, and invoke harnesses", { sequential: true, tags: harnessTags }, () => {
const cli = new CliRunner();
const projectName = `${E2E_PREFIX}${Date.now().toString(36)}`;
let projectDir: string;
Expand Down
2 changes: 1 addition & 1 deletion e2eTest/project/templates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const A2aResponseSchema = z.object({

describe(
"add, dev, deploy, invoke for runtime templates",
{ sequential: true, tags: [TAGS.RUNTIME] },
{ sequential: true, tags: [TAGS.RUNTIME, TAGS.CANARY] },
() => {
const cli = new CliRunner();
const projectName = `${E2E_PREFIX}${Date.now().toString(36)}`;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@aws/agentcore",
"version": "1.0.0-rc.4",
"version": "1.0.0-rc.5",
"repository": {
"type": "git",
"url": "https://github.com/aws/agentcore-cli.git"
Expand Down
4 changes: 2 additions & 2 deletions src/assets/templates/a2a-python-strands/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ requires-python = ">=3.10"
dependencies = [
"a2a-sdk[all] ~= 0.3.0",
"aws-opentelemetry-distro ~= 0.18.0",
"bedrock-agentcore[a2a] ~= 1.9.1",
"bedrock-agentcore[a2a,strands-agents] >= 1.18.1, < 2.0.0",
"botocore[crt] ~= 1.43.0",
"strands-agents ~= 1.15.0",
"strands-agents >= 1.15.0, < 2.0.0",
]

[tool.hatch.build.targets.wheel]
Expand Down
2 changes: 1 addition & 1 deletion src/assets/templates/agent-python-minimal/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"aws-opentelemetry-distro ~= 0.18.0",
"bedrock-agentcore ~= 1.9.1",
"bedrock-agentcore >= 1.18.1, < 2.0.0",
"botocore[crt] ~= 1.43.0",
]

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
FROM public.ecr.aws/docker/library/python:3.12-slim-trixie

RUN apt-get update && apt-get upgrade -y && rm -rf /var/lib/apt/lists/*

RUN pip install --no-cache-dir uv

ARG UV_DEFAULT_INDEX
Expand All @@ -8,6 +10,7 @@ ARG UV_INDEX
WORKDIR /app

ENV UV_SYSTEM_PYTHON=1 \
UV_NO_CACHE=1 \
UV_COMPILE_BYTECODE=1 \
UV_NO_PROGRESS=1 \
PYTHONUNBUFFERED=1 \
Expand All @@ -22,7 +25,7 @@ COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project

COPY --chown=bedrock_agentcore:bedrock_agentcore . .
RUN uv sync --frozen --no-dev
RUN uv sync --frozen --no-dev && /usr/local/bin/python -m pip uninstall -y uv

USER bedrock_agentcore

Expand Down
12 changes: 6 additions & 6 deletions src/assets/templates/agent-python-strands/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"aws-opentelemetry-distro ~= 0.18.0",
"bedrock-agentcore ~= 1.9.1",
"bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0",
"botocore[crt] ~= 1.43.0",
{{#if (eq modelProvider "Anthropic")}}"strands-agents[anthropic] ~= 1.15.0",
{{else}}{{#if (eq modelProvider "OpenAI")}}"strands-agents[openai] ~= 1.15.0",
{{else}}{{#if (eq modelProvider "Gemini")}}"strands-agents[gemini] ~= 1.15.0",
{{else}}{{#if (eq modelProvider "LiteLLM")}}"strands-agents[litellm] ~= 1.15.0",
{{else}}"strands-agents ~= 1.15.0",
{{#if (eq modelProvider "Anthropic")}}"strands-agents[anthropic] >= 1.15.0, < 2.0.0",
{{else}}{{#if (eq modelProvider "OpenAI")}}"strands-agents[openai] >= 1.15.0, < 2.0.0",
{{else}}{{#if (eq modelProvider "Gemini")}}"strands-agents[gemini] >= 1.15.0, < 2.0.0",
{{else}}{{#if (eq modelProvider "LiteLLM")}}"strands-agents[litellm] >= 1.15.0, < 2.0.0",
{{else}}"strands-agents >= 1.15.0, < 2.0.0",
{{/if}}{{/if}}{{/if}}{{/if}}
]

Expand Down
4 changes: 2 additions & 2 deletions src/assets/templates/agui-python-strands/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ dependencies = [
"ag-ui-strands >= 0.3.0, < 0.4.0",
"ag-ui-protocol >= 0.1.19, < 0.2.0",
"aws-opentelemetry-distro ~= 0.18.0",
"bedrock-agentcore ~= 1.9.1",
"bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0",
"botocore[crt] ~= 1.43.0",
"strands-agents ~= 1.15.0",
"strands-agents >= 1.15.0, < 2.0.0",
"uvicorn >= 0.34.3, < 1.0.0",
]

Expand Down
6 changes: 3 additions & 3 deletions src/assets/templates/export-harness-python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"aws-opentelemetry-distro ~= 0.18.0",
"bedrock-agentcore ~= 1.9.1",
"bedrock-agentcore[strands-agents] >= 1.18.1, < 2.0.0",
"botocore[crt] ~= 1.43.0",
"mcp >= 1.23.0, < 2.0.0",
"mcp >= 1.28.1, < 2.0.0",
{{#if bedrockMantle}}"aws-bedrock-token-generator >= 1.1.0, < 2.0.0",
{{/if}}"strands-agents{{#if strandsExtras}}[{{strandsExtras}}]{{/if}} ~= 1.54.0",
{{/if}}"strands-agents{{#if strandsExtras}}[{{strandsExtras}}]{{/if}} >= 1.54.0, < 2.0.0",

]

Expand Down
2 changes: 1 addition & 1 deletion src/assets/templates/mcp-python-fastmcp/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"aws-opentelemetry-distro ~= 0.18.0",
"mcp ~= 1.24.0",
"mcp >= 1.28.1, < 2.0.0",
]

[tool.hatch.build.targets.wheel]
Expand Down
19 changes: 11 additions & 8 deletions src/components/CliOnlyScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function cliOnlyCommands(

const CLI_ONLY = cliOnlyCommands();

describe("menus list command-line-only subcommands below a divider", () => {
describe("menus separate mixed command groups with a divider", () => {
test("the root menu", async () => {
const r = renderScreen("/agentcore", { withContext: inProjectContext });

Expand Down Expand Up @@ -77,13 +77,14 @@ describe("menus list command-line-only subcommands below a divider", () => {
r.unmount();
});

test("a menu whose every subcommand is command line only", async () => {
test("a menu whose every subcommand is command line only has no redundant divider", async () => {
const r = renderScreen("/agentcore/eval/ondemand");

await waitFor(() => hasCliDivider(r.lastFrame()!));
await waitForText(r.lastFrame, "❯ evaluate");
expect(hasCliDivider(r.lastFrame()!)).toBe(false);
expect(menuEntries(r.lastFrame()!)).toEqual({
screens: [],
cliOnly: ["evaluate", "simulate"],
screens: ["evaluate", "simulate"],
cliOnly: [],
});
r.unmount();
});
Expand Down Expand Up @@ -133,10 +134,12 @@ describe("every command-line-only command opens on screen", () => {
const parent = command.parent!;

if (command.commands.length > 0) {
// A group opens its own menu, with every child under the divider.
// A CLI-only group opens its own menu without a redundant separator.
await waitForText(r.lastFrame, path.join(" → "));
await waitFor(() => hasCliDivider(r.lastFrame()!));
expect(menuEntries(r.lastFrame()!).screens).toEqual([]);
expect(hasCliDivider(r.lastFrame()!)).toBe(false);
expect(menuEntries(r.lastFrame()!).screens).toEqual(
command.commands.map((child) => child.name()),
);
} else {
await waitForText(r.lastFrame, "this command runs from the command line");
const help = command.createHelp();
Expand Down
49 changes: 49 additions & 0 deletions src/components/RouterScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,28 @@ function menuGroups(frame: string): { title: string | undefined; names: string[]
// rendered frames — behavior a user would see, not internal state.

describe("menu rendering", () => {
test.each([
"/agentcore/log",
"/agentcore/traces",
"/agentcore/harness/traces",
"/agentcore/runtime/traces",
"/agentcore/payment",
"/agentcore/payment/connector",
"/agentcore/payment/manager",
"/agentcore/payment/session",
"/agentcore/payment/instrument",
])("%s has no extra divider above a CLI-only menu", async (path) => {
const r = renderScreen(path, { withContext: inProjectContext });
await waitForText(r.lastFrame, "type to choose a command");

const frame = r.lastFrame()!;
const lines = frame.split("\n");
const filterRow = lines.findIndex((line) => /^\s*\/ /.test(line));
expect(lines[filterRow + 2]).toMatch(/^\s*❯ /);
expect(hasCliDivider(frame)).toBe(false);
r.unmount();
});

test("CLI-only command names use the same white and focused colors as other commands", () => {
// A separate process keeps ANSI enabled without changing the plain-text screen tests.
const result = spawnSync(
Expand Down Expand Up @@ -316,6 +338,21 @@ describe("narrow terminals", () => {
});

describe("filtering", () => {
test("removes the CLI-only divider when filtering leaves only that group and restores it", async () => {
const r = renderScreen("/agentcore");
await waitForText(r.lastFrame, "❯ create");
expect(hasCliDivider(r.lastFrame()!)).toBe(true);

await r.write("feedback");
await waitForText(r.lastFrame, "❯ feedback");
expect(hasCliDivider(r.lastFrame()!)).toBe(false);

for (const _ of "feedback") await r.press("backspace");
await waitForText(r.lastFrame, "❯ create");
expect(hasCliDivider(r.lastFrame()!)).toBe(true);
r.unmount();
});

test("typing narrows the options to matches", async () => {
const r = renderScreen("/agentcore/harness");
await waitForText(r.lastFrame, "list");
Expand Down Expand Up @@ -433,6 +470,18 @@ describe("navigation", () => {
describe("short terminals", () => {
const ROWS = 15;

test("does not reserve a row for a redundant CLI-only divider", async () => {
const r = renderScreen("/agentcore/log", { withContext: inProjectContext });
await waitForText(r.lastFrame, "❯ runtime");
await r.resize(100, 8);

expect(r.lastFrame()).toContain("❯ runtime");
expect(r.lastFrame()).toContain("harness");
expect(r.lastFrame()).not.toContain("more");
expect(hasCliDivider(r.lastFrame()!)).toBe(false);
r.unmount();
});

// fullMenu renders the root menu at the default height, where every option fits.
async function fullMenu() {
const r = renderScreen("/agentcore", { withContext: inProjectContext });
Expand Down
Loading
Loading