Skip to content
Merged
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
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ These options are available on all commands:

- `create` - Create new AgentCore project
- `add` - Add resources (agent, memory, credential, evaluator, online-eval, gateway, gateway-target, policy-engine,
policy, payment-manager, payment-connector)
policy, payment-manager, payment-connector, capacity-provider)
- `remove` - Remove resources (agent, memory, credential, evaluator, online-eval, gateway, gateway-target,
policy-engine, policy, payment-manager, payment-connector, all)
policy-engine, policy, payment-manager, payment-connector, capacity-provider, all)
- `deploy` - Deploy infrastructure to AWS
- `status` - Check deployment status
- `dev` - Local development server (CodeZip: uvicorn with hot-reload; Container: Docker build + run with volume mount)
Expand Down Expand Up @@ -90,6 +90,7 @@ Current primitives:
- `PolicyPrimitive` — Cedar policy creation/removal within policy engines
- `PaymentManagerPrimitive` — payment manager creation/removal with agent code wiring
- `PaymentConnectorPrimitive` — payment connector creation/removal with credential management
- `CapacityProviderPrimitive` — capacity provider creation/removal (customer-managed EC2 compute pool for runtimes)

Singletons are created in `registry.ts` and wired into CLI commands via `cli.ts`. See `src/cli/AGENTS.md` for details on
adding new primitives.
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ agentcore invoke

### Resource Management

| Command | Description |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `add` | Add harnesses, agents, memory, credentials, gateways and gateway-targets, evaluators, online evals, online insights, knowledge bases, config bundles, datasets, policy engines and policies, payment managers and payment connectors, runtime endpoints |
| `remove` | Remove any of the above resources from the project |
| Command | Description |
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `add` | Add harnesses, agents, memory, credentials, gateways and gateway-targets, evaluators, online evals, online insights, knowledge bases, config bundles, datasets, policy engines and policies, payment managers and payment connectors, capacity providers, runtime endpoints |
| `remove` | Remove any of the above resources from the project |

> **Note**: Run `agentcore deploy` after `add` or `remove` to update resources in AWS.

Expand Down Expand Up @@ -264,8 +264,8 @@ my-project/
Projects use JSON schema files in the `agentcore/` directory:

- `agentcore.json` - Project resources (agents, memory, credentials, gateways, evaluators, online evals/insights,
knowledge bases, harnesses, policy engines and policies, payment managers and connectors, config bundles, datasets,
runtime endpoints)
knowledge bases, harnesses, policy engines and policies, payment managers and connectors, capacity providers, config
bundles, datasets, runtime endpoints)
- `deployed-state.json` - Runtime state in agentcore/.cli/ (auto-managed)
- `aws-targets.json` - Deployment targets (account, region)

Expand Down
52 changes: 52 additions & 0 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,57 @@ agentcore add config-bundle \
| `--commit-message <text>` | Commit message for this version |
| `--json` | JSON output |

### add capacity-provider

Add a capacity provider — a customer-managed pool of AWS-managed EC2 compute that agent runtimes can run on instead of
the default managed fleet. Everything except the description and tags is immutable after creation.

**Operator role.** AgentCore assumes an IAM _operator role_ to create and manage the EC2 compute on your behalf. Omit
`--operator-role-arn` and the CLI provisions one for you at deploy time — a role that trusts
`bedrock-agentcore.amazonaws.com` (scoped to your account and region) and carries the AWS managed policy
`BedrockAgentCoreRuntimeInstancesOperatorRolePolicy` (EC2/Auto Scaling/fleet management plus the
`agentcore-lifecycle-events-*` EventBridge permissions the service needs). Pass `--operator-role-arn` only when you want
to bring your own role; it must grant those same permissions, or capacity provider creation fails asynchronously
(surfaced by CloudFormation as `NotStabilized`).

```bash
# Minimal — operator role is created automatically
agentcore add capacity-provider \
--name MyCapacityProvider \
--subnets subnet-0123456789abcdef0 \
--security-groups sg-0123456789abcdef0 \
--instance-types c6a.large

# With a named EBS volume, lifecycle limits, and ARM64 (and a bring-your-own operator role)
agentcore add capacity-provider \
--name MyCapacityProvider \
--operator-role-arn arn:aws:iam::123456789012:role/MyOperatorRole \
--subnets subnet-0123456789abcdef0,subnet-0fedcba9876543210 \
--security-groups sg-0123456789abcdef0 \
--os LINUX_ARM64 \
--instance-types c7g.large,c7g.xlarge \
--volume data:20 --volume-encrypted \
--idle-instance-timeout 3600 \
--max-lifetime 28800
```

| Flag | Description |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `--name <name>` | Capacity provider name (required); immutable after creation |
| `--operator-role-arn <arn>` | IAM role ARN AgentCore assumes to manage the capacity provider (optional — auto-created if omitted); immutable |
| `--description <desc>` | Description (the only mutable field besides tags) |
| `--subnets <subnets>` | Comma-separated subnet IDs, 1–16 (required) |
| `--security-groups <groups>` | Comma-separated security group IDs, 1–16 (required) |
| `--os <os>` | `LINUX_X86_64` (default) or `LINUX_ARM64` |
| `--instance-types <types>` | Comma-separated allowed EC2 instance types, 1–30 (required) |
| `--volume <name:sizeGiB>` | Named EBS volume as `name:sizeGiB` (repeatable, max 5) |
| `--volume-encrypted` | Encrypt EBS volumes |
| `--volume-kms-key <arn>` | KMS key ARN for EBS volume encryption |
| `--instance-profile-arn <arn>` | IAM instance profile ARN for launched instances |
| `--idle-instance-timeout <secs>` | Idle instance timeout in seconds (60–1209600) |
| `--max-lifetime <secs>` | Maximum instance lifetime in seconds (60–1209600) |
| `--json` | JSON output |

### remove

Remove resources from project.
Expand All @@ -797,6 +848,7 @@ agentcore remove dataset --name MyDataset
agentcore remove config-bundle --name MyBundle
agentcore remove payment-manager --name MyManager -y
agentcore remove payment-connector --name MyCDPConnector --manager MyManager -y
agentcore remove capacity-provider --name MyCapacityProvider -y

# Reset everything
agentcore remove all -y
Expand Down
210 changes: 210 additions & 0 deletions integ-tests/add-remove-capacity-provider.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import { createTestProject, readProjectConfig, runCLI } from '../src/test-utils/index.js';
import type { TestProject } from '../src/test-utils/index.js';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';

const OPERATOR_ROLE_ARN = 'arn:aws:iam::123456789012:role/MyOperatorRole';

describe('integration: add and remove capacity providers', () => {
let project: TestProject;

beforeAll(async () => {
project = await createTestProject({ noAgent: true });
});

afterAll(async () => {
await project.cleanup();
});

describe('capacity provider lifecycle', () => {
const cpName = `IntegCp${Date.now().toString().slice(-6)}`;

it('adds a capacity provider', async () => {
const result = await runCLI(
[
'add',
'capacity-provider',
'--name',
cpName,
'--operator-role-arn',
OPERATOR_ROLE_ARN,
'--subnets',
'subnet-0123456789abcdef0',
'--security-groups',
'sg-0123456789abcdef0',
'--os',
'LINUX_X86_64',
'--instance-types',
'c6a.large',
'--json',
],
project.projectPath
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);
expect(json.capacityProviderName).toBe(cpName);

const config = await readProjectConfig(project.projectPath);
const cp = config.capacityProviders?.find((c: Record<string, unknown>) => c.name === cpName);
expect(cp, `Capacity provider "${cpName}" should be in config`).toBeTruthy();
expect(cp!.operatorRoleArn).toBe(OPERATOR_ROLE_ARN);
const ec2 = (cp as any).computeConfiguration.ec2Configuration;
expect(ec2.launchTemplateSource.launchParameters.operatingSystem).toBe('LINUX_X86_64');
expect(ec2.launchTemplateSource.launchParameters.instanceRequirements.allowedInstanceTypes).toEqual([
'c6a.large',
]);
expect(ec2.vpcConfiguration.subnets).toEqual(['subnet-0123456789abcdef0']);
expect(ec2.vpcConfiguration.securityGroups).toEqual(['sg-0123456789abcdef0']);
});

it('adds a capacity provider with volumes, lifecycle, and description', async () => {
const richName = `${cpName}Rich`;
const result = await runCLI(
[
'add',
'capacity-provider',
'--name',
richName,
'--operator-role-arn',
OPERATOR_ROLE_ARN,
'--description',
'my rich capacity provider',
'--subnets',
'subnet-0123456789abcdef0,subnet-0fedcba9876543210',
'--security-groups',
'sg-0123456789abcdef0',
'--os',
'LINUX_ARM64',
'--instance-types',
'c7g.large,c7g.xlarge',
'--volume',
'data:20',
'--volume-encrypted',
'--idle-instance-timeout',
'3600',
'--max-lifetime',
'28800',
'--json',
],
project.projectPath
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
expect(JSON.parse(result.stdout).success).toBe(true);

const config = await readProjectConfig(project.projectPath);
const cp = config.capacityProviders?.find((c: Record<string, unknown>) => c.name === richName);
expect(cp).toBeTruthy();
expect(cp!.description).toBe('my rich capacity provider');
const ec2 = (cp as any).computeConfiguration.ec2Configuration;
expect(ec2.launchTemplateSource.launchParameters.operatingSystem).toBe('LINUX_ARM64');
expect(ec2.vpcConfiguration.subnets).toHaveLength(2);
expect(ec2.volumes).toEqual([{ ebsConfiguration: { name: 'data', sizeGiB: 20, encrypted: true } }]);
expect(ec2.lifecycleConfiguration).toEqual({ idleInstanceTimeout: 3600, maxLifetime: 28800 });

await runCLI(['remove', 'capacity-provider', '--name', richName, '--yes'], project.projectPath);
});

it('rejects a duplicate capacity provider name', async () => {
const result = await runCLI(
[
'add',
'capacity-provider',
'--name',
cpName,
'--operator-role-arn',
OPERATOR_ROLE_ARN,
'--subnets',
'subnet-0123456789abcdef0',
'--security-groups',
'sg-0123456789abcdef0',
'--instance-types',
'c6a.large',
'--json',
],
project.projectPath
);

expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
expect(json.error).toContain('already exists');
});

it('removes the capacity provider', async () => {
const result = await runCLI(
['remove', 'capacity-provider', '--name', cpName, '--yes', '--json'],
project.projectPath
);

expect(result.exitCode, `stdout: ${result.stdout}, stderr: ${result.stderr}`).toBe(0);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(true);

const config = await readProjectConfig(project.projectPath);
const found = config.capacityProviders?.some((c: Record<string, unknown>) => c.name === cpName);
expect(found, `Capacity provider "${cpName}" should be removed`).toBeFalsy();
});
});

describe('validation', () => {
it('rejects a missing required option', async () => {
const result = await runCLI(['add', 'capacity-provider', '--name', 'noRole', '--json'], project.projectPath);
expect(result.exitCode).toBe(1);
const json = JSON.parse(result.stdout);
expect(json.success).toBe(false);
});

it('rejects an unsupported operating system', async () => {
const result = await runCLI(
[
'add',
'capacity-provider',
'--name',
'badOs',
'--operator-role-arn',
OPERATOR_ROLE_ARN,
'--subnets',
'subnet-0123456789abcdef0',
'--security-groups',
'sg-0123456789abcdef0',
'--os',
'WINDOWS_X86_64',
'--instance-types',
'c6a.large',
'--json',
],
project.projectPath
);
expect(result.exitCode).toBe(1);
});

it('rejects a malformed operator role ARN', async () => {
const result = await runCLI(
[
'add',
'capacity-provider',
'--name',
'badArn',
'--operator-role-arn',
'not-an-arn',
'--subnets',
'subnet-0123456789abcdef0',
'--security-groups',
'sg-0123456789abcdef0',
'--instance-types',
'c6a.large',
'--json',
],
project.projectPath
);
expect(result.exitCode).toBe(1);
});

it('passes agentcore validate after add/remove lifecycle', async () => {
const result = await runCLI(['validate'], project.projectPath);
expect(result.exitCode).toBe(0);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7511,7 +7511,7 @@ file maps to a JSON config file and includes validation constraints as comments

### Key Types

- **AgentCoreProjectSpec**: Root config with runtimes, memories, knowledge bases, credentials, evaluators, online evals and insights, gateways, policy engines, config bundles, A/B tests, harness registrations, datasets, and payment managers
- **AgentCoreProjectSpec**: Root config with runtimes, memories, knowledge bases, credentials, evaluators, online evals and insights, gateways, policy engines, config bundles, A/B tests, harness registrations, datasets, payment managers, and capacity providers
- **AgentEnvSpec**: Agent configuration (build type, entrypoint, code location, runtime version, network mode)
- **Memory**: Memory resource with strategies (SEMANTIC, SUMMARIZATION, USER_PREFERENCE, EPISODIC) and expiry
- **Credential**: API key or OAuth credential provider
Expand Down Expand Up @@ -7622,7 +7622,7 @@ Run \`agentcore --help\` or \`agentcore <command> --help\` for full flags. Commo

| Command | Description |
| --- | --- |
| \`agentcore add <resource>\` | Add agent, memory, credential, gateway, gateway-target, evaluator, online-eval, online-insights, knowledge-base, harness, policy-engine, policy, payment-manager, payment-connector, config-bundle, dataset, runtime-endpoint |
| \`agentcore add <resource>\` | Add agent, memory, credential, gateway, gateway-target, evaluator, online-eval, online-insights, knowledge-base, harness, policy-engine, policy, payment-manager, payment-connector, capacity-provider, config-bundle, dataset, runtime-endpoint |
| \`agentcore remove <resource>\` | Remove any resource |
| \`agentcore export harness\` | Export a harness to a Strands runtime agent under \`app/<agentName>/\` |

Expand Down
4 changes: 2 additions & 2 deletions src/assets/agents/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ file maps to a JSON config file and includes validation constraints as comments

### Key Types

- **AgentCoreProjectSpec**: Root config with runtimes, memories, knowledge bases, credentials, evaluators, online evals and insights, gateways, policy engines, config bundles, A/B tests, harness registrations, datasets, and payment managers
- **AgentCoreProjectSpec**: Root config with runtimes, memories, knowledge bases, credentials, evaluators, online evals and insights, gateways, policy engines, config bundles, A/B tests, harness registrations, datasets, payment managers, and capacity providers
- **AgentEnvSpec**: Agent configuration (build type, entrypoint, code location, runtime version, network mode)
- **Memory**: Memory resource with strategies (SEMANTIC, SUMMARIZATION, USER_PREFERENCE, EPISODIC) and expiry
- **Credential**: API key or OAuth credential provider
Expand Down Expand Up @@ -167,7 +167,7 @@ Run `agentcore --help` or `agentcore <command> --help` for full flags. Commonly

| Command | Description |
| --- | --- |
| `agentcore add <resource>` | Add agent, memory, credential, gateway, gateway-target, evaluator, online-eval, online-insights, knowledge-base, harness, policy-engine, policy, payment-manager, payment-connector, config-bundle, dataset, runtime-endpoint |
| `agentcore add <resource>` | Add agent, memory, credential, gateway, gateway-target, evaluator, online-eval, online-insights, knowledge-base, harness, policy-engine, policy, payment-manager, payment-connector, capacity-provider, config-bundle, dataset, runtime-endpoint |
| `agentcore remove <resource>` | Remove any resource |
| `agentcore export harness` | Export a harness to a Strands runtime agent under `app/<agentName>/` |

Expand Down
Loading
Loading