diff --git a/.github/harness/README.md b/.github/harness/README.md deleted file mode 100644 index d9ba15c61..000000000 --- a/.github/harness/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Harness Resources - -Container and scripts for AI-powered automation via -[AgentCore Harness](https://docs.aws.amazon.com/bedrock/latest/userguide/agentcore.html). - -## Structure - -``` -harness/ -├── Dockerfile # Container image for the harness runtime -├── harness_review.py # Invokes the harness to review PRs (SigV4 + event stream) -└── prompts/ - ├── system.md # System prompt (workspace context) - └── review.md # PR review task prompt -``` - -## Current: PR Reviewer - -Reviews pull requests on open/reopen via `.github/workflows/pr-ai-review.yml`. - -### Dual-token setup - -The Dockerfile takes two build args: - -- **`CLONE_TOKEN`** — baked into git config for cloning private repos -- **`GITHUB_TOKEN`** — baked into `gh` CLI auth for posting PR comments - -### Building the container - -```bash -finch build \ - --build-arg CLONE_TOKEN= \ - --build-arg GITHUB_TOKEN= \ - -t pr-reviewer .github/harness/ -``` - -## Future: Tester - -This directory will also house a harness-based test runner. diff --git a/.github/workflows/pr-ai-review.yml b/.github/workflows/pr-ai-review.yml index 71014d915..8446de393 100644 --- a/.github/workflows/pr-ai-review.yml +++ b/.github/workflows/pr-ai-review.yml @@ -135,7 +135,7 @@ jobs: env: PR_URL: ${{ steps.pr-url.outputs.url }} HARNESS_ARN: ${{ env.HARNESS_ARN }} - run: python .github/harness/harness_review.py + run: python examples/AgentCoreCliReviewer/app/PRReviewer/harness_review.py - name: Remove agentcore-harness-reviewing label if: always() diff --git a/.gitignore b/.gitignore index e544ccddc..d317db9c5 100644 --- a/.gitignore +++ b/.gitignore @@ -36,8 +36,7 @@ report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Finder (MacOS) folder config .DS_Store -# Testing Directory -examples +# Testing artifacts __pycache__/ # Python build artifacts diff --git a/.prettierignore b/.prettierignore index 717fb2363..2dfebb2d6 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,3 +5,4 @@ src/assets/**/*.md src/assets/**/*.ts src/assets/**/*.json src/assets/**/*.template +**/cdk.out/ diff --git a/eslint.config.mjs b/eslint.config.mjs index f689048b5..189c302d7 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -167,6 +167,7 @@ export default tseslint.config( '.github', 'src/assets', 'src/schema/llm-compacted', + 'examples', '.agentcore', '**/.agentcore/**', '.venv', diff --git a/examples/AgentCoreCliReviewer/AGENTS.md b/examples/AgentCoreCliReviewer/AGENTS.md new file mode 100644 index 000000000..9e19c55d5 --- /dev/null +++ b/examples/AgentCoreCliReviewer/AGENTS.md @@ -0,0 +1,210 @@ +# AgentCore Project + +This project contains configuration and infrastructure for an Amazon Bedrock AgentCore application. + +The `agentcore/` directory is a declarative model of the project. The `agentcore/cdk/` subdirectory uses the +`@aws/agentcore-cdk` L3 constructs to deploy the configuration to AWS. + +## Mental Model + +The project uses a **flat resource model**. Agents, memories, credentials, gateways, evaluators, and policies are +independent top-level arrays in `agentcore.json`. There is no binding between resources in the schema — each resource is +provisioned independently. Agents discover memories and credentials at runtime via environment variables or SDK calls. +Tags defined in `agentcore.json` flow through to deployed CloudFormation resources. + +## Critical Invariants + +1. **Schema-First Authority:** The `.json` files are the source of truth. Do not modify agent behavior by editing + generated CDK code in `cdk/`. +2. **Resource Identity:** The `name` field determines the CloudFormation Logical ID. + - **Renaming** a resource will **destroy and recreate** it. + - **Modifying** other fields will update the resource **in-place**. +3. **Schema Validation:** Run `agentcore validate` before deploying configuration changes. +4. **Resource Removal:** Use `agentcore remove` to remove resources. Run `agentcore deploy` after removal to tear down + deployed infrastructure. +5. **Invocation Input:** Validate runtime payloads and require text prompts to be strings. If a Strands app accepts a + caller-supplied message history, normalize the history tail with `strip_trailing_tool_use()` before invocation. + +## Directory Structure + +``` +myProject/ +├── AGENTS.md # This file — AI coding assistant context +├── agentcore/ +│ ├── agentcore.json # Main project config (AgentCoreProjectSpec) +│ ├── aws-targets.json # Deployment targets (account + region) +│ ├── .env.local # Secrets — API keys (gitignored) +│ └── cdk/ # AWS CDK project (@aws/agentcore-cdk L3 constructs) +├── app/ # Agent application code +└── evaluators/ # Custom evaluator code (if any) +``` + +## Configuration Reference + +- **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 +- **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 +- **AgentCoreGateway**: MCP gateway with targets (Lambda, MCP server, OpenAPI, Smithy, API Gateway, web-search, + knowledge-base) +- **Evaluator**: LLM-as-a-Judge or code-based evaluator +- **OnlineEvalConfig**: Continuous evaluation pipeline bound to an agent +- **OnlineInsightsConfig** _[preview]_: Continuous failure-pattern analysis bound to an agent +- **KnowledgeBase**: Managed Bedrock Knowledge Base auto-wired to a gateway +- **Harness**: Declarative agent — runtime + tools + skills + memory + observability without writing agent code +- **PolicyEngine** + **Policy**: Cedar policy engine with form-based guardrails (Bedrock content filters, prompt-attack, + sensitive-info) or raw Cedar policies +- **PaymentManager** + **PaymentConnector**: x402-protocol payment orchestration with provider credentials (CoinbaseCDP, + StripePrivy) +- **ConfigBundle**: Versioned runtime configuration as a separately-deployable resource +- **Dataset**: Curated session dataset for batch evaluation and recommendation runs +- **RuntimeEndpoint**: Named endpoint (e.g. `PROMPT_V1`) targeting a specific runtime version + +### Common Enum Values + +- **BuildType**: `'CodeZip'` | `'Container'` +- **NetworkMode**: `'PUBLIC'` | `'VPC'` +- **RuntimeVersion**: `'PYTHON_3_10'` | `'PYTHON_3_11'` | `'PYTHON_3_12'` | `'PYTHON_3_13'` | `'PYTHON_3_14'` | + `'NODE_18'` | `'NODE_20'` | `'NODE_22'` +- **MemoryStrategyType**: `'SEMANTIC'` | `'SUMMARIZATION'` | `'USER_PREFERENCE'` | `'EPISODIC'` +- **GatewayTargetType**: `'lambda'` | `'mcpServer'` | `'openApiSchema'` | `'smithyModel'` | `'apiGateway'` | + `'lambdaFunctionArn'` | `'connector'` (web-search, bedrock-knowledge-bases) +- **ModelProvider**: `'Bedrock'` | `'Gemini'` | `'OpenAI'` | `'Anthropic'` +- **PaymentProvider**: `'CoinbaseCDP'` | `'StripePrivy'` +- **PolicyEnforcementMode**: `'ACTIVE'` | `'PASSIVE'` +- **GuardrailContentFilter**: `'VIOLENCE'` | `'HATE'` | `'SEXUAL'` | `'MISCONDUCT'` | `'INSULTS'` + +### Build Types + +- **CodeZip**: Python source packaged as a zip and deployed directly to AgentCore Runtime. +- **Container**: Docker image built in CodeBuild (ARM64), pushed to a per-agent ECR repository. Requires a `Dockerfile` + in the agent's `codeLocation` directory. For local development (`agentcore dev`), the container is built and run + locally with volume-mounted hot-reload. + +### Supported Frameworks (for template agents) + +- **Strands** — Bedrock, Anthropic, OpenAI, Gemini +- **LangChain/LangGraph** — Bedrock, Anthropic, OpenAI, Gemini +- **GoogleADK** — Gemini +- **OpenAI Agents** — OpenAI +- **Autogen** — Bedrock, Anthropic, OpenAI, Gemini + +### Protocols + +- **HTTP** — Standard HTTP agent endpoint +- **MCP** — Model Context Protocol server +- **A2A** — Agent-to-Agent protocol (Google A2A) + +## Deployment + +Deployments are orchestrated through the CLI: + +```bash +agentcore deploy # Synthesizes CDK and deploys to AWS +agentcore status # Shows deployment status +``` + +Alternatively, deploy directly via CDK: + +```bash +cd agentcore/cdk +npm install +npx cdk synth +npx cdk deploy +``` + +## Editing Schemas + +When modifying JSON config files: + +1. Use exact enum values as string literals +2. Use CloudFormation-safe names (alphanumeric, start with letter) +3. Run `agentcore validate` to verify changes + +## Harness Export + +`agentcore export harness` converts a harness configuration into a deployable Strands Python agent under +`app//`. + +**After every export, you MUST read `app//EXPORT_NOTES.md` before proceeding.** + +This file lists any manual follow-up items required before the agent will deploy or run correctly — missing files to +create, IAM policies to add, or configuration steps the exporter could not automate. A clean export produces "No manual +steps required." Complete every item in the file before running `agentcore deploy`. + +```bash +agentcore export harness --name # generates app//EXPORT_NOTES.md +cat app//EXPORT_NOTES.md # read this before touching anything else +``` + +## CLI Commands + +Run `agentcore --help` or `agentcore --help` for full flags. Commonly used: + +**Project lifecycle** + +| Command | Description | +| -------------------- | ----------------------------------------------------------------- | +| `agentcore create` | Create a new project | +| `agentcore dev` | Run agent locally with hot-reload | +| `agentcore deploy` | Deploy to AWS | +| `agentcore invoke` | Invoke agent (local or deployed) | +| `agentcore status` | Show deployment status | +| `agentcore validate` | Validate configuration | +| `agentcore package` | Package agent artifacts | +| `agentcore import` | Import resources from a Bedrock AgentCore Starter Toolkit project | + +**Resources** + +| Command | Description | +| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `agentcore add ` | 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 remove ` | Remove any resource | +| `agentcore export harness` | Export a harness to a Strands runtime agent under `app//` | + +**Jobs (run, view, archive, lifecycle)** + +| Command | Description | +| ---------------------------------------------------- | ------------------------------------------------------------------------------ | +| `agentcore run eval` | Run on-demand evaluation against agent traces | +| `agentcore run batch-evaluation` | Run evaluators across all sessions at scale | +| `agentcore run recommendation` | Optimize prompts or tool descriptions from real traces | +| `agentcore run insights` _[preview]_ | Run failure-pattern analysis across sessions | +| `agentcore run ab-test` | Start an A/B test (config-bundle or target-based) | +| `agentcore run ingest` | Start a fresh ingestion job for every data source on a deployed knowledge base | +| `agentcore view ` | List or view jobs (recommendation, batch-evaluation, ab-test, insights) | +| `agentcore archive ` | Delete a job on the service + clear local history | +| `agentcore stop ` | Stop a running batch-evaluation or ab-test | +| `agentcore promote ab-test` | Apply the winning variant to `agentcore.json` | +| `agentcore pause ` / `agentcore resume ` | Pause/resume a deployed online-eval, online-insights, or ab-test | + +**Config bundles & datasets** + +| Command | Description | +| -------------------------------------------------------- | ----------------------------------------- | +| `agentcore config-bundle versions` (alias `cb versions`) | List version history for a bundle | +| `agentcore config-bundle diff` | Diff two versions of a bundle | +| `agentcore config-bundle create-branch` | Create a new branch on an existing bundle | +| `agentcore dataset download` | Download a dataset version locally | +| `agentcore dataset publish-version` | Publish a new dataset version | +| `agentcore dataset remove-version` | Remove a dataset version | + +**Observability & history** + +| Command | Description | +| ------------------------------------------------ | ------------------------------------------ | +| `agentcore logs` | Stream/search agent runtime logs | +| `agentcore logs evals` | Stream/search online-eval logs | +| `agentcore traces list` / `agentcore traces get` | List recent traces or download one to JSON | +| `agentcore evals history` | View past on-demand eval results | + +**Utilities** + +| Command | Description | +| ------------------------ | -------------------------------------------------------------- | +| `agentcore fetch access` | Fetch access info for deployed gateway or agent | +| `agentcore feedback` | Send feedback (with optional screenshot) to the AgentCore team | +| `agentcore update` | Check for and install CLI updates | +| `agentcore telemetry` | View or change telemetry preferences | diff --git a/examples/AgentCoreCliReviewer/README.md b/examples/AgentCoreCliReviewer/README.md new file mode 100644 index 000000000..ab8ee2ad2 --- /dev/null +++ b/examples/AgentCoreCliReviewer/README.md @@ -0,0 +1,48 @@ +# AgentCore CLI PR Reviewer + +AgentCore CLI project for the automated pull-request reviewer used by `.github/workflows/pr-ai-review.yml`. + +This project was generated with AgentCore CLI 0.27.0, using the legacy `.github/harness/Dockerfile` before the harness +assets were moved here: + +```bash +agentcore create \ + --name PRReviewer \ + --project-name AgentCoreCliReviewer \ + --model-provider bedrock \ + --model-id us.anthropic.claude-opus-4-7 \ + --container .github/harness/Dockerfile \ + --no-harness-memory +``` + +## Structure + +```text +AgentCoreCliReviewer/ +├── agentcore/ # AgentCore and CDK deployment configuration +└── app/PRReviewer/ + ├── Dockerfile # Review workspace image + ├── harness.json # Harness model and runtime configuration + ├── harness_review.py # GitHub Actions invocation client + ├── prompts/review.md # Pull-request review task + └── system-prompt.md # AgentCore CLI workspace context +``` + +## Deploy + +The default target is account `631957124172` in `us-east-1`. + +```bash +AWS_PROFILE=deploy agentcore validate +AWS_PROFILE=deploy agentcore deploy --yes +``` + +The GitHub Actions invocation role is `arn:aws:iam::631957124172:role/GitHubActions-AgentCoreCliHarnessReview`. After +the authenticated image described below is deployed, update these AWS Secrets Manager values to cut the workflow over: + +- `aws/agentcore-cli/HARNESS_ARN`: the Harness ARN returned by `agentcore status` +- `aws/agentcore-cli/HARNESS_AWS_ROLE_ARN`: the GitHub Actions invocation role ARN above + +The Dockerfile expects `CLONE_TOKEN` and `GITHUB_TOKEN` build arguments. AgentCore CLI's Harness Dockerfile build does +not currently expose custom build arguments, so a production deployment must use a prebuilt private ECR image with those +arguments or migrate authentication to a runtime-supported secret mechanism. diff --git a/examples/AgentCoreCliReviewer/agentcore/.gitignore b/examples/AgentCoreCliReviewer/agentcore/.gitignore new file mode 100644 index 000000000..df5400cb1 --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/.gitignore @@ -0,0 +1,12 @@ +# Secrets (local environment files are never committed) +.env.local + +# CDK Build Artifacts +cdk/cdk.out/ +cdk/node_modules/ + +# CLI Internals +.cli/* + +# Ephemeral Staging +.cache/* diff --git a/examples/AgentCoreCliReviewer/agentcore/agentcore.json b/examples/AgentCoreCliReviewer/agentcore/agentcore.json new file mode 100644 index 000000000..01cd78559 --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/agentcore.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://schema.agentcore.aws.dev/v1/agentcore.json", + "name": "AgentCoreCliReviewer", + "version": 1, + "managedBy": "CDK", + "tags": { + "agentcore:created-by": "agentcore-cli", + "agentcore:project-name": "AgentCoreCliReviewer" + }, + "runtimes": [], + "memories": [], + "knowledgeBases": [], + "credentials": [], + "evaluators": [], + "onlineEvalConfigs": [], + "agentCoreGateways": [], + "policyEngines": [], + "configBundles": [], + "abTests": [], + "harnesses": [ + { + "name": "PRReviewer", + "path": "app/PRReviewer" + } + ], + "datasets": [], + "payments": [] +} diff --git a/examples/AgentCoreCliReviewer/agentcore/aws-targets.json b/examples/AgentCoreCliReviewer/agentcore/aws-targets.json new file mode 100644 index 000000000..cc6c97df1 --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/aws-targets.json @@ -0,0 +1,8 @@ +[ + { + "name": "default", + "description": "PR reviewer harness", + "account": "631957124172", + "region": "us-east-1" + } +] diff --git a/examples/AgentCoreCliReviewer/agentcore/cdk/.gitignore b/examples/AgentCoreCliReviewer/agentcore/cdk/.gitignore new file mode 100644 index 000000000..964b4d89c --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/cdk/.gitignore @@ -0,0 +1,9 @@ +# Build output +dist/ + +# Dependencies +node_modules/ + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/examples/AgentCoreCliReviewer/agentcore/cdk/.npmignore b/examples/AgentCoreCliReviewer/agentcore/cdk/.npmignore new file mode 100644 index 000000000..c1d6d45dc --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/cdk/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/examples/AgentCoreCliReviewer/agentcore/cdk/.prettierrc b/examples/AgentCoreCliReviewer/agentcore/cdk/.prettierrc new file mode 100644 index 000000000..5563802ee --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/cdk/.prettierrc @@ -0,0 +1,8 @@ +{ + "trailingComma": "es5", + "printWidth": 120, + "tabWidth": 2, + "semi": true, + "singleQuote": true, + "arrowParens": "avoid" +} diff --git a/examples/AgentCoreCliReviewer/agentcore/cdk/README.md b/examples/AgentCoreCliReviewer/agentcore/cdk/README.md new file mode 100644 index 000000000..5fa522fc6 --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/cdk/README.md @@ -0,0 +1,26 @@ +# AgentCore CDK Project + +This CDK project is managed by the AgentCore CLI. It deploys your agent infrastructure into AWS using the `@aws/agentcore-cdk` L3 constructs. + +## Structure + +- `bin/cdk.ts` — Entry point. Reads project configuration from `agentcore/` and creates a stack per deployment target. +- `lib/cdk-stack.ts` — Defines `AgentCoreStack`, which wraps the `AgentCoreApplication` L3 construct. +- `test/cdk.test.ts` — Unit tests for stack synthesis. + +## Useful commands + +- `npm run build` compile TypeScript to JavaScript +- `npm run test` run unit tests +- `npx cdk synth` emit the synthesized CloudFormation template +- `npx cdk deploy` deploy this stack to your default AWS account/region +- `npx cdk diff` compare deployed stack with current state + +## Usage + +You typically don't need to interact with this directory directly. The AgentCore CLI handles synthesis and deployment: + +```bash +agentcore deploy # synthesizes and deploys via CDK +agentcore status # checks deployment status +``` diff --git a/examples/AgentCoreCliReviewer/agentcore/cdk/bin/cdk.ts b/examples/AgentCoreCliReviewer/agentcore/cdk/bin/cdk.ts new file mode 100644 index 000000000..b15b3281d --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/cdk/bin/cdk.ts @@ -0,0 +1,192 @@ +#!/usr/bin/env node +import { AgentCoreStack, type HarnessConfig } from '../lib/cdk-stack'; +import { ConfigIO, HarnessSpecSchema, type AwsDeploymentTarget } from '@aws/agentcore-cdk'; +import { App, type Environment } from 'aws-cdk-lib'; +import * as path from 'path'; +import * as fs from 'fs'; + +function toEnvironment(target: AwsDeploymentTarget): Environment { + return { + account: target.account, + region: target.region, + }; +} + +function sanitize(name: string): string { + return name.replace(/_/g, '-'); +} + +function toStackName(projectName: string, targetName: string): string { + return `AgentCore-${sanitize(projectName)}-${sanitize(targetName)}`; +} + +async function main() { + // Config root is parent of cdk/ directory. The CLI sets process.cwd() to agentcore/cdk/. + const configRoot = path.resolve(process.cwd(), '..'); + const configIO = new ConfigIO({ baseDir: configRoot }); + + const spec = await configIO.readProjectSpec(); + const targets = await configIO.readAWSDeploymentTargets(); + + // The vended CDK project compiles against the published @aws/agentcore-cdk + // schema type, which may lag the CLI's own AgentCoreProjectSpec (e.g. payments, + // harnesses, gateway fields). Cast once so those fields are reachable. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const specAny = spec as any; + + // Extract MCP configuration from project spec. + // Gateway fields are stored in agentcore.json but may not yet be on the + const mcpSpec = specAny.agentCoreGateways?.length + ? { + agentCoreGateways: specAny.agentCoreGateways, + mcpRuntimeTools: specAny.mcpRuntimeTools, + unassignedTargets: specAny.unassignedTargets, + } + : undefined; + + // Read deployed state for credential ARNs (populated by pre-deploy identity setup) + let deployedState: Record | undefined; + try { + deployedState = JSON.parse(fs.readFileSync(path.join(configRoot, '.cli', 'deployed-state.json'), 'utf8')); + } catch { + // Deployed state may not exist on first deploy + } + + if (targets.length === 0) { + throw new Error('No deployment targets configured. Please define targets in agentcore/aws-targets.json'); + } + + // Read harness configs: the full validated spec drives the CFN resource; the + // role-scoped fields drive the IAM role + container build. + const projectRoot = path.resolve(configRoot, '..'); + + // Read non-S3 KB connector-config files and pass their parsed contents to the + // L3 verbatim. The L3 does not read files; it expects the parsed + // connectorParameters keyed by the data source's connectorConfigFile path. + const connectorParametersByFile: Record> = {}; + for (const kb of specAny.knowledgeBases ?? []) { + for (const ds of kb.dataSources ?? []) { + if (ds.type !== 'S3' && ds.connectorConfigFile) { + const abs = path.resolve(projectRoot, ds.connectorConfigFile); + try { + connectorParametersByFile[ds.connectorConfigFile] = JSON.parse(fs.readFileSync(abs, 'utf-8')); + } catch (err) { + throw new Error( + `Could not read connector config '${ds.connectorConfigFile}' for knowledge base '${kb.name}' at ${abs}: ${err instanceof Error ? err.message : err}` + ); + } + } + } + } + + // Synthesize an AWS::BedrockAgentCore::Harness resource for each harness entry in the spec. + const harnessConfigs: HarnessConfig[] = []; + for (const entry of specAny.harnesses ?? []) { + const harnessDir = path.resolve(projectRoot, entry.path); + const harnessPath = path.resolve(harnessDir, 'harness.json'); + try { + const harnessSpec = HarnessSpecSchema.parse(JSON.parse(fs.readFileSync(harnessPath, 'utf-8'))); + harnessConfigs.push({ + name: entry.name, + executionRoleArn: harnessSpec.executionRoleArn, + // Only an `existing` memory ref carries a name to wire IAM against; managed memory is + // owned by the harness (no sibling) and disabled has none — both resolve to undefined. + memoryName: harnessSpec.memory?.mode === 'existing' ? harnessSpec.memory.name : undefined, + containerUri: harnessSpec.containerUri, + hasDockerfile: !!harnessSpec.dockerfile, + dockerfile: harnessSpec.dockerfile, + codeLocation: harnessSpec.dockerfile ? harnessDir : undefined, + tools: harnessSpec.tools, + skills: harnessSpec.skills, + apiKeyArn: harnessSpec.model?.apiKeyArn, + efsAccessPoints: harnessSpec.efsAccessPoints, + s3AccessPoints: harnessSpec.s3AccessPoints, + apiFormat: harnessSpec.model?.apiFormat, + // Full spec + dir drive the AWS::BedrockAgentCore::Harness CFN resource. + spec: harnessSpec, + harnessDir, + }); + } catch (err) { + throw new Error( + `Could not read harness.json for "${entry.name}" at ${harnessPath}: ${err instanceof Error ? err.message : err}` + ); + } + } + + const app = new App(); + + for (const target of targets) { + const env = toEnvironment(target); + const stackName = toStackName(spec.name, target.name); + + // Extract credentials from deployed state for this target + const targetState = (deployedState as Record)?.targets as + | Record> + | undefined; + const targetResources = targetState?.[target.name]?.resources as Record | undefined; + const credentials = targetResources?.credentials as + | Record + | undefined; + + // Payment credential provider ARNs live in the same credentials map as identity credentials + const paymentCredentials = credentials; + + const paymentSpec = specAny.payments?.length + ? specAny.payments.map( + (p: { + name: string; + description?: string; + authorizerType: 'AWS_IAM' | 'CUSTOM_JWT'; + authorizerConfiguration?: unknown; + autoPayment?: boolean; + paymentToolAllowlist?: string[]; + networkPreferences?: string[]; + connectors: { name: string; provider?: string; credentialName: string }[]; + }) => ({ + name: p.name, + description: p.description, + authorizerType: p.authorizerType, + authorizerConfiguration: p.authorizerConfiguration, + autoPayment: p.autoPayment, + paymentToolAllowlist: p.paymentToolAllowlist, + networkPreferences: p.networkPreferences, + connectors: p.connectors.map(c => { + const credentialProviderArn = paymentCredentials?.[c.credentialName]?.credentialProviderArn; + if (!credentialProviderArn) { + // Fail fast with an actionable message rather than passing an empty + // ARN that fails opaquely server-side at CreatePaymentConnector. + throw new Error( + `Payment connector "${c.name}" on manager "${p.name}" references credential ` + + `"${c.credentialName}", but no deployed credential provider was found for it. ` + + `Run \`agentcore deploy\` so the credential provider is created first.` + ); + } + return { name: c.name, provider: c.provider, credentialProviderArn }; + }), + }) + ) + : undefined; + + new AgentCoreStack(app, stackName, { + spec, + mcpSpec, + credentials, + connectorParametersByFile, + harnesses: harnessConfigs.length > 0 ? harnessConfigs : undefined, + paymentSpec, + env, + description: `AgentCore stack for ${spec.name} deployed to ${target.name} (${target.region})`, + tags: { + 'agentcore:project-name': spec.name, + 'agentcore:target-name': target.name, + }, + }); + } + + app.synth(); +} + +main().catch((error: unknown) => { + console.error('AgentCore CDK synthesis failed:', error instanceof Error ? error.message : error); + process.exit(1); +}); diff --git a/examples/AgentCoreCliReviewer/agentcore/cdk/cdk.json b/examples/AgentCoreCliReviewer/agentcore/cdk/cdk.json new file mode 100644 index 000000000..19e6983ab --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/cdk/cdk.json @@ -0,0 +1,88 @@ +{ + "app": "node dist/bin/cdk.js", + "watch": { + "include": ["**"], + "exclude": ["README.md", "cdk*.json", "tsconfig.json", "package*.json", "yarn.lock", "node_modules", "dist", "test"] + }, + "context": { + "@aws-cdk/aws-signer:signingProfileNamePassedToCfn": true, + "@aws-cdk/aws-ecs-patterns:secGroupsDisablesImplicitOpenListener": true, + "@aws-cdk/aws-lambda:recognizeLayerVersion": true, + "@aws-cdk/core:checkSecretUsage": true, + "@aws-cdk/core:target-partitions": ["aws", "aws-cn", "aws-us-gov"], + "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, + "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, + "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, + "@aws-cdk/aws-iam:minimizePolicies": true, + "@aws-cdk/core:validateSnapshotRemovalPolicy": true, + "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, + "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, + "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, + "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, + "@aws-cdk/core:enablePartitionLiterals": true, + "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, + "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, + "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, + "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, + "@aws-cdk/aws-route53-patters:useCertificate": true, + "@aws-cdk/customresources:installLatestAwsSdkDefault": false, + "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, + "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, + "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, + "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, + "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, + "@aws-cdk/aws-redshift:columnId": true, + "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, + "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, + "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, + "@aws-cdk/aws-kms:aliasNameRef": true, + "@aws-cdk/aws-kms:applyImportedAliasPermissionsToPrincipal": true, + "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, + "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, + "@aws-cdk/aws-efs:denyAnonymousAccess": true, + "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, + "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, + "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, + "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, + "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, + "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, + "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true, + "@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true, + "@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true, + "@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true, + "@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true, + "@aws-cdk/aws-eks:nodegroupNameAttribute": true, + "@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true, + "@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true, + "@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false, + "@aws-cdk/aws-s3:keepNotificationInImportedBucket": false, + "@aws-cdk/core:explicitStackTags": true, + "@aws-cdk/aws-ecs:enableImdsBlockingDeprecatedFeature": false, + "@aws-cdk/aws-ecs:disableEcsImdsBlocking": true, + "@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true, + "@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true, + "@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true, + "@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true, + "@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true, + "@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true, + "@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true, + "@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true, + "@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true, + "@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true, + "@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true, + "@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": true, + "@aws-cdk/core:enableAdditionalMetadataCollection": true, + "@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": false, + "@aws-cdk/aws-s3:setUniqueReplicationRoleName": true, + "@aws-cdk/aws-events:requireEventBusPolicySid": true, + "@aws-cdk/core:aspectPrioritiesMutating": true, + "@aws-cdk/aws-dynamodb:retainTableReplica": true, + "@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true, + "@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true, + "@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true, + "@aws-cdk/aws-s3:publicAccessBlockedByDefault": true, + "@aws-cdk/aws-lambda:useCdkManagedLogGroup": true, + "@aws-cdk/aws-elasticloadbalancingv2:networkLoadBalancerWithSecurityGroupByDefault": true, + "@aws-cdk/aws-ecs-patterns:uniqueTargetGroupId": true + } +} diff --git a/examples/AgentCoreCliReviewer/agentcore/cdk/jest.config.js b/examples/AgentCoreCliReviewer/agentcore/cdk/jest.config.js new file mode 100644 index 000000000..0077a6547 --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/cdk/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest', + }, + setupFilesAfterEnv: ['aws-cdk-lib/testhelpers/jest-autoclean'], +}; diff --git a/examples/AgentCoreCliReviewer/agentcore/cdk/lib/cdk-stack.ts b/examples/AgentCoreCliReviewer/agentcore/cdk/lib/cdk-stack.ts new file mode 100644 index 000000000..3dac0669d --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/cdk/lib/cdk-stack.ts @@ -0,0 +1,249 @@ +import { + AgentCoreApplication, + AgentCoreMcp, + AgentCorePaymentManager, + AgentCorePaymentConnector, + type AgentCoreProjectSpec, + type AgentCoreMcpSpec, + type CustomJWTAuthorizerConfig, + type HarnessDeploymentConfig, +} from '@aws/agentcore-cdk'; +import { CfnOutput, Stack, type StackProps } from 'aws-cdk-lib'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import { Construct } from 'constructs'; + +/** + * Harness deployment config: role-scoped fields (for IAM role + container build) + * plus the full validated spec + its config directory so the L3 construct can + * synthesize the AWS::BedrockAgentCore::Harness resource. + */ +export type HarnessConfig = HarnessDeploymentConfig; + +export interface PaymentConnectorSpec { + name: string; + provider: 'CoinbaseCDP' | 'StripePrivy'; + credentialProviderArn: string; +} + +export interface PaymentSpec { + name: string; + description?: string; + authorizerType: 'AWS_IAM' | 'CUSTOM_JWT'; + authorizerConfiguration?: { customJWTAuthorizer: CustomJWTAuthorizerConfig }; + autoPayment?: boolean; + paymentToolAllowlist?: string[]; + networkPreferences?: string[]; + connectors: PaymentConnectorSpec[]; +} + +export interface AgentCoreStackProps extends StackProps { + /** + * The AgentCore project specification containing agents, memories, and credentials. + */ + spec: AgentCoreProjectSpec; + /** + * The MCP specification containing gateways and servers. + */ + mcpSpec?: AgentCoreMcpSpec; + /** + * Credential provider ARNs from deployed state, keyed by credential name. + */ + credentials?: Record; + /** + * Harness role configurations. + */ + harnesses?: HarnessConfig[]; + /** + * Parsed connectorParameters for non-S3 KB data sources, keyed by + * connectorConfigFile path. Forwarded to AgentCoreApplication. + */ + connectorParametersByFile?: Record>; + /** + * Payment specifications with resolved credential provider ARNs. + */ + paymentSpec?: PaymentSpec[]; +} + +function toCdkId(name: string): string { + return name.replace(/_/g, ''); +} + +/** + * Decide whether a deployed runtime should receive payment env vars + IAM grants. + * Payments today only ships a runtime shim for Python HTTP runtimes; injecting + * AGENTCORE_PAYMENT_* env vars into TypeScript / MCP / A2A / AGUI runtimes + * would surface env vars they cannot consume and would dilute least-privilege + * IAM grants for runtimes that never call ProcessPayment. + */ +function isPaymentEligibleAgent(agent: { entrypoint?: string; protocol?: string }): boolean { + if (agent.protocol && agent.protocol !== 'HTTP') { + return false; + } + const entrypoint = typeof agent.entrypoint === 'string' ? agent.entrypoint : ''; + const entrypointFile = entrypoint.split(':')[0] ?? ''; + return entrypointFile.endsWith('.py'); +} + +/** + * CDK Stack that deploys AgentCore infrastructure. + * + * This is a thin wrapper that instantiates L3 constructs. + * All resource logic and outputs are contained within the L3 constructs. + */ +export class AgentCoreStack extends Stack { + /** The AgentCore application containing all agent environments */ + public readonly application: AgentCoreApplication; + + constructor(scope: Construct, id: string, props: AgentCoreStackProps) { + super(scope, id, props); + + const { spec, mcpSpec, credentials, harnesses, connectorParametersByFile, paymentSpec } = props; + + // Create AgentCoreApplication with all agents and harness roles + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const appProps: Record = { spec }; + if (harnesses?.length) { + appProps.harnesses = harnesses; + } + if (connectorParametersByFile && Object.keys(connectorParametersByFile).length > 0) { + appProps.connectorParametersByFile = connectorParametersByFile; + } + if (credentials) { + appProps.credentials = credentials; + } + this.application = new AgentCoreApplication(this, 'Application', appProps as any); + + // Create AgentCoreMcp if there are gateways configured + if (mcpSpec?.agentCoreGateways && mcpSpec.agentCoreGateways.length > 0) { + new AgentCoreMcp(this, 'Mcp', { + projectName: spec.name, + mcpSpec, + agentCoreApplication: this.application, + credentials, + projectTags: spec.tags, + }); + } + + // Create payment infrastructure via CFN constructs + if (paymentSpec && paymentSpec.length > 0) { + for (const payment of paymentSpec) { + const mgrId = toCdkId(payment.name); + const manager = new AgentCorePaymentManager(this, `Payment${mgrId}`, { + projectName: spec.name, + name: payment.name, + authorizerType: payment.authorizerType, + description: payment.description, + authorizerConfiguration: payment.authorizerConfiguration, + tags: spec.tags, + }); + + const prefix = `AGENTCORE_PAYMENT_${payment.name.toUpperCase().replace(/-/g, '_')}`; + + // Wire env vars from construct output tokens into eligible agent environments only. + // See isPaymentEligibleAgent — non-Python or non-HTTP runtimes have no shim that + // can consume these env vars, and giving them sts:AssumeRole on the + // ProcessPaymentRole would broaden the privilege surface unnecessarily. + for (const env of this.application.environments.values()) { + if (!isPaymentEligibleAgent(env.agent)) { + continue; + } + env.runtime.addEnvironmentVariable(`${prefix}_MANAGER_ARN`, manager.paymentManagerArn); + env.runtime.addEnvironmentVariable(`${prefix}_PROCESS_PAYMENT_ROLE_ARN`, manager.processPaymentRoleArn); + + // Grant runtime execution role permission to assume the ProcessPaymentRole. + // The ProcessPaymentRole's trust policy allows AccountRootPrincipal, but the + // caller still needs sts:AssumeRole on its own role to perform the assumption. + env.runtime.role.addToPrincipalPolicy( + new iam.PolicyStatement({ + actions: ['sts:AssumeRole'], + resources: [manager.processPaymentRoleArn], + }) + ); + + // Grant payment data-plane actions directly to the runtime role. + // + // NOTE: This deviates from the canonical role model in the AgentCore Payments + // beta guide, which assigns Get/List/Create instrument+session actions to a + // separate ManagementRole and limits the agent's role to ProcessPayment only. + // The current SDK plugin (AgentCorePaymentsPlugin.generate_payment_header) + // calls GetPaymentInstrument internally during the 402 auto-pay path, so the + // runtime role needs read access. CreatePaymentSession is included so + // `agentcore invoke --auto-session` works without a separate ManagementRole + // call. Tighten this if the SDK is updated to accept pre-fetched instrument + // details and split create-session into a backend-only flow. + env.runtime.role.addToPrincipalPolicy( + new iam.PolicyStatement({ + actions: [ + 'bedrock-agentcore:GetPaymentInstrument', + 'bedrock-agentcore:ListPaymentInstruments', + 'bedrock-agentcore:GetPaymentInstrumentBalance', + 'bedrock-agentcore:GetPaymentSession', + 'bedrock-agentcore:ListPaymentSessions', + 'bedrock-agentcore:CreatePaymentSession', + 'bedrock-agentcore:ProcessPayment', + ], + resources: [manager.paymentManagerArn, `${manager.paymentManagerArn}/*`], + }) + ); + + if (payment.autoPayment !== undefined) { + env.runtime.addEnvironmentVariable(`${prefix}_AUTO_PAYMENT`, String(payment.autoPayment)); + } + if (payment.paymentToolAllowlist) { + env.runtime.addEnvironmentVariable(`${prefix}_TOOL_ALLOWLIST`, payment.paymentToolAllowlist.join(',')); + } + if (payment.networkPreferences) { + env.runtime.addEnvironmentVariable(`${prefix}_NETWORK_PREFERENCES`, payment.networkPreferences.join(',')); + } + if (payment.authorizerType === 'CUSTOM_JWT') { + env.runtime.addEnvironmentVariable(`${prefix}_AUTH_MODE`, 'bearer'); + } + } + + // Create connectors for this manager + for (const connector of payment.connectors) { + const connId = toCdkId(connector.name); + const conn = new AgentCorePaymentConnector(this, `Payment${mgrId}${connId}`, { + projectName: spec.name, + paymentManager: manager, + connectorName: connector.name, + connectorType: connector.provider, + credentialProviderArn: connector.credentialProviderArn, + }); + + // Wire first connector's ID as env var (eligible agents only) + if (connector === payment.connectors[0]) { + for (const env of this.application.environments.values()) { + if (!isPaymentEligibleAgent(env.agent)) continue; + env.runtime.addEnvironmentVariable(`${prefix}_CONNECTOR_ID`, conn.paymentConnectorId); + } + } + + new CfnOutput(this, `Payment${mgrId}${connId}ConnectorId`, { + value: conn.paymentConnectorId, + }); + } + + // CFN Outputs for post-deploy state parsing + new CfnOutput(this, `Payment${mgrId}ManagerArn`, { + value: manager.paymentManagerArn, + }); + new CfnOutput(this, `Payment${mgrId}ManagerId`, { + value: manager.paymentManagerId, + }); + new CfnOutput(this, `Payment${mgrId}ProcessPaymentRoleArn`, { + value: manager.processPaymentRoleArn, + }); + new CfnOutput(this, `Payment${mgrId}ResourceRetrievalRoleArn`, { + value: manager.resourceRetrievalRoleArn, + }); + } + } + + // Stack-level output + new CfnOutput(this, 'StackNameOutput', { + description: 'Name of the CloudFormation Stack', + value: this.stackName, + }); + } +} diff --git a/examples/AgentCoreCliReviewer/agentcore/cdk/package.json b/examples/AgentCoreCliReviewer/agentcore/cdk/package.json new file mode 100644 index 000000000..550a52797 --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/cdk/package.json @@ -0,0 +1,30 @@ +{ + "name": "agentcore-cdk-app", + "version": "0.1.0", + "bin": { + "cdk": "dist/bin/cdk.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest", + "cdk": "npm run build && cdk", + "clean": "rm -rf dist", + "format": "prettier --write .", + "format:check": "prettier --check ." + }, + "devDependencies": { + "@types/jest": "~29.5.14", + "@types/node": "~24.13.3", + "jest": "~29.7.0", + "ts-jest": "~29.4.11", + "aws-cdk": "~2.1126.0", + "prettier": "~3.9.5", + "typescript": "~5.9.3" + }, + "dependencies": { + "@aws/agentcore-cdk": "0.1.0-alpha.45", + "aws-cdk-lib": "~2.261.0", + "constructs": "~10.7.0" + } +} diff --git a/examples/AgentCoreCliReviewer/agentcore/cdk/test/cdk.test.ts b/examples/AgentCoreCliReviewer/agentcore/cdk/test/cdk.test.ts new file mode 100644 index 000000000..8db318ada --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/cdk/test/cdk.test.ts @@ -0,0 +1,31 @@ +import * as cdk from 'aws-cdk-lib'; +import { Template } from 'aws-cdk-lib/assertions'; +import { AgentCoreStack } from '../lib/cdk-stack'; + +test('AgentCoreStack synthesizes with empty spec', () => { + const app = new cdk.App(); + const stack = new AgentCoreStack(app, 'TestStack', { + spec: { + name: 'testproject', + version: 1, + managedBy: 'CDK' as const, + runtimes: [], + memories: [], + credentials: [], + evaluators: [], + onlineEvalConfigs: [], + configBundles: [], + policyEngines: [], + payments: [], + agentCoreGateways: [], + mcpRuntimeTools: [], + unassignedTargets: [], + datasets: [], + knowledgeBases: [], + }, + }); + const template = Template.fromStack(stack); + template.hasOutput('StackNameOutput', { + Description: 'Name of the CloudFormation Stack', + }); +}); diff --git a/examples/AgentCoreCliReviewer/agentcore/cdk/tsconfig.json b/examples/AgentCoreCliReviewer/agentcore/cdk/tsconfig.json new file mode 100644 index 000000000..c70b0d444 --- /dev/null +++ b/examples/AgentCoreCliReviewer/agentcore/cdk/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "CommonJS", + "moduleResolution": "Node", + "lib": ["es2022"], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": true, + "skipLibCheck": true, + "typeRoots": ["./node_modules/@types"], + "rootDir": ".", + "outDir": "dist" + }, + "include": ["bin/**/*", "lib/**/*", "test/**/*"], + "exclude": ["node_modules", "cdk.out", "dist"] +} diff --git a/.github/harness/Dockerfile b/examples/AgentCoreCliReviewer/app/PRReviewer/Dockerfile similarity index 100% rename from .github/harness/Dockerfile rename to examples/AgentCoreCliReviewer/app/PRReviewer/Dockerfile diff --git a/examples/AgentCoreCliReviewer/app/PRReviewer/harness.json b/examples/AgentCoreCliReviewer/app/PRReviewer/harness.json new file mode 100644 index 000000000..861df899d --- /dev/null +++ b/examples/AgentCoreCliReviewer/app/PRReviewer/harness.json @@ -0,0 +1,13 @@ +{ + "name": "PRReviewer", + "model": { + "provider": "bedrock", + "modelId": "us.anthropic.claude-opus-4-7" + }, + "tools": [], + "skills": [], + "memory": { + "mode": "disabled" + }, + "dockerfile": "Dockerfile" +} diff --git a/.github/harness/harness_review.py b/examples/AgentCoreCliReviewer/app/PRReviewer/harness_review.py similarity index 100% rename from .github/harness/harness_review.py rename to examples/AgentCoreCliReviewer/app/PRReviewer/harness_review.py diff --git a/.github/harness/prompts/review.md b/examples/AgentCoreCliReviewer/app/PRReviewer/prompts/review.md similarity index 100% rename from .github/harness/prompts/review.md rename to examples/AgentCoreCliReviewer/app/PRReviewer/prompts/review.md diff --git a/.github/harness/prompts/system.md b/examples/AgentCoreCliReviewer/app/PRReviewer/system-prompt.md similarity index 100% rename from .github/harness/prompts/system.md rename to examples/AgentCoreCliReviewer/app/PRReviewer/system-prompt.md