Commit 2142c77
feat: add agentcore import command for starter toolkit migration (#620)
* feat: add agentcore import command for starter toolkit migration
Implements a 3-phase import flow that migrates Bedrock AgentCore Starter
Toolkit projects to the agentcore-cli:
- Phase 1: Creates companion CloudFormation resources (IAM roles/policies)
- Phase 2: Imports existing AWS resources via CFN IMPORT change set
- Phase 3: User runs `agentcore deploy` to reconcile the stack
Parses .bedrock_agentcore.yaml, scaffolds CLI project structure, copies
agent source code, publishes CDK assets, and adopts pre-existing runtimes
and memories into the CLI's CloudFormation stack.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: require existing project for import, fix memory support
- Remove scaffold.ts; import now requires `agentcore create` first
- Resolve target from project's aws-targets.json (auto-select single,
error on multiple without --target, create default from YAML if empty)
- Replace dangling Fn::GetAtt/Ref to removed primary resources with "*"
in Phase 1 companion template (fixes IAM policy ARN validation)
- Fix memoryArn placeholder in deployed state (construct ARN from ID)
- Make --target optional (no default value)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: set up Python venv after import and fix setuptools auto-discovery
Starter toolkit projects have multiple top-level directories (model/,
mcp_client/) which causes setuptools to fail with "Multiple top-level
packages discovered". Fix by appending [tool.setuptools] py-modules = []
to pyproject.toml after copy. Also run uv venv + uv sync so agentcore
dev works immediately after import.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: import credential providers from starter toolkit YAML
Parse OAuth and API key credential providers from .bedrock_agentcore.yaml
identity config and add them to project.json during import. Fix YAML
parser to handle list items with nested key-value pairs (e.g.,
credential_providers). Fix eslint no-base-to-string errors by adding
explicit type casts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: guard memory mode check with typeof string to prevent empty YAML mode from creating invalid memory
The simple YAML parser turns bare "mode:" (no value) into an empty object {},
which is truthy and passes the existing `!== 'NO_MEMORY' && memoryConfig.mode`
check. This caused a memory with mode={} to be created, which is invalid.
Added typeof check to ensure mode is actually a string before proceeding.
Also adds comprehensive unit tests for import memory handling (35 tests).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use Array.isArray guard for VPC networkConfig subnets/securityGroups
The YAML parser creates empty objects {} for keys with no list items
(e.g., `subnets:` with nothing underneath). The previous code used
`(networkModeConfig.subnets as string[]) ?? []` which doesn't protect
against this since {} is truthy. This resulted in subnets being {} instead
of [] when the YAML had empty subnet/security_group keys under VPC mode.
Now uses Array.isArray() to ensure we always get a proper array, falling
back to [] when the parser returns a non-array value.
Also adds comprehensive VPC import tests (18 tests covering parsing,
edge cases, mixed agents, quoted values, and starter toolkit format).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: make import idempotent by only importing newly-added resources
Previously, running `agentcore import` twice with the same YAML would
attempt to re-import already-managed CloudFormation resources in Phase 2,
causing errors like "resource already exists in stack". The bug was that
agentsToImport and memoriesToImport were filtered from all parsed YAML
agents (regardless of whether they were skipped during merge), rather
than from only the newly-added ones.
Fix: track which agents/memories are actually added during the merge
step (newlyAddedAgentNames / newlyAddedMemoryNames) and filter
agentsToImport / memoriesToImport to only include those newly-added
resources.
Adds 17 unit tests covering first import, second import idempotency,
partial overlap, credential idempotency, and edge cases.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: defer target resolution for undeployed starter toolkit imports
When importing a starter toolkit YAML with no physical IDs (agent_id and
memory_id are null), the import command would fail unnecessarily if no
deployment targets existed and the YAML had null account/region. This is
incorrect because the no-deploy path only needs config merge and source
copy -- no CloudFormation operations.
The fix computes hasPhysicalIds early and only performs strict target
resolution (requiring account/region) when physical IDs are present.
When no physical IDs exist, target resolution is lenient -- it uses an
existing target if available, or falls back to 'default' for stackName.
Also adds 22 unit tests covering the no-deploy path: YAML parsing of
null IDs, early return behavior, config merge, Python setup, absence of
CloudFormation operations, and target resolution edge cases.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add Test Group 1 unit tests for no-memory agent import path
63 tests covering YAML parsing, toAgentEnvSpec conversion, merge logic,
source copy, Phase 1/Phase 2 template operations, findLogicalId helpers,
sanitize/toStackName, and integration for a single agent with no memory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add 30 unit tests for multi-agent import scenarios
Tests cover:
- YAML parsing with 2, 3, and similar-named agents
- Memory deduplication across shared agents
- Credential extraction from identity.credential_providers
- findLogicalIdByProperty with multiple runtimes
- findLogicalIdByProperty Fn::Sub false substring match regression test
- filterCompanionOnlyTemplate with multiple primary resources
- buildImportTemplate with multiple import targets
- Stack name sanitization and source code directory structure
- Partial import (deployed + undeployed agents mixed)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Revert "test: add 30 unit tests for multi-agent import scenarios"
This reverts commit 25a1458125d9ecdae1dd3e8c2f6e17336fc7498f.
* test: add merge-logic unit tests for CLI-native create then import scenario
16 tests covering agent/memory/credential deduplication, Set-based name
matching, toMemorySpec clamping, source copy skip logic, append-only merge
behavior, and edge cases for projects with existing agents and memories.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add merge-logic unit tests for CLI-native create then import scenario
16 tests covering agent/memory/credential deduplication, Set-based name
matching, toMemorySpec clamping, source copy skip logic, append-only merge
behavior, and edge cases for projects with existing agents and memories.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix import for container builds and multi-agent stacks
- Publish CDK assets to S3 before Phase 1 (CodeBuild needs source zip)
- Handle directory assets in publishCdkAssets (zip before upload)
- Export publishCdkAssets for use in actions.ts
- Copy Dockerfile from starter toolkit config for Container builds
- Generate fallback Dockerfile if toolkit config not found
- Preserve previously imported resources during Phase 1 UPDATE
by merging deployed template back into companion template
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(import): map OAuth providers correctly and fix region mismatch
- Change toCredentialSpec() to map OAuth providers to OAuthCredentialProvider
instead of ApiKeyCredentialProvider, so the CLI wires CLIENT_ID/CLIENT_SECRET
env vars correctly (not API_KEY)
- Make discoveryUrl optional in OAuthCredentialSchema for imported providers
that already exist in Identity service
- Skip Identity service create/update for OAuth providers without discoveryUrl
during deploy (provider already exists)
- Set AWS_REGION/AWS_DEFAULT_REGION to target.region before import operations
to prevent region mismatch when env var differs from aws-targets.json
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(import): handle symlinks and excluded dirs in source copy, warn on authorizer config
BUG-001: copyDirRecursive now skips symlinks and common excluded directories
(.venv, .git, __pycache__, node_modules, etc.) preventing EISDIR crash when
importing projects with virtualenvs containing symlinks like .venv/lib64 -> lib.
BUG-004: Import now warns when an agent has authorizer_configuration set in the
starter toolkit YAML, since custom JWT authorizer config is not automatically
imported and must be manually recreated via `agentcore add gateway`.
Constraint: CLI gateway model is separate from agent config, so full authorizer import requires design work
Rejected: Auto-create gateway during import | gateway-agent linking logic is complex and warrants its own feature
Confidence: high
Scope-risk: narrow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(import): fix multi-agent memory import and skip Python setup for containers
Two fixes:
1. Memory name prefix mismatch: CDK prefixes memory names with project
name (e.g. "myproject_Agent_mem") but import searched for unprefixed
YAML name. Added fallback lookup with project name prefix.
2. Container agents no longer trigger unnecessary Python venv setup
during import, since dependencies are installed inside the Docker image.
Constraint: CDK BasePrimitiveConstruct generates physicalName as ${projectName}_${name}
Rejected: Stripping prefix from CDK names | would break other CDK conventions
Confidence: high
Scope-risk: narrow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(build): add jsx: automatic to esbuild config to fix TUI crash
esbuild does not read tsconfig.json, so it defaults to classic JSX
transform (React.createElement) while tsconfig specifies react-jsx
(automatic). This produces 432 bare React.createElement calls in the
bundle that reference a nonexistent React global, crashing all TUI
commands (status, deploy, etc.) with "React is not defined".
Constraint: esbuild ignores tsconfig jsx settings by default
Rejected: Adding React as external | would require React in node_modules at runtime
Confidence: high
Scope-risk: narrow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(import): warn about memory env var mismatch after import
The Starter Toolkit injects memory ID via BEDROCK_AGENTCORE_MEMORY_ID,
but CDK constructs use MEMORY_{NAME}_ID pattern. After import, agent
code still references the old env var, causing memory to silently fail.
Add a yellow warning during import telling users the correct env var
name to update in their agent code.
Constraint: CDK env var naming is controlled by AgentCoreMemory.getEnvVarName()
Rejected: Auto-rewrite agent source code | too fragile across frameworks
Confidence: high
Scope-risk: narrow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(import): warn about memory env var mismatch with diff-style hint
The Starter Toolkit injects memory ID via BEDROCK_AGENTCORE_MEMORY_ID,
but CDK constructs use MEMORY_{NAME}_ID pattern. After import, agent
code still references the old env var, causing memory to silently fail.
Show a git-diff-style warning during import with red/green highlighting
so users see exactly what to change in their agent code.
Constraint: CDK env var naming is controlled by AgentCoreMemory.getEnvVarName()
Rejected: Auto-rewrite agent source code | too fragile across frameworks
Confidence: high
Scope-risk: narrow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tui): hide import command from interactive TUI menu
Import is a CLI-only command that requires --source flag and doesn't
have a TUI screen. Remove it from the interactive command picker.
Confidence: high
Scope-risk: narrow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(import): auto-bootstrap CDK environment before asset publishing
Import fails with "The specified bucket does not exist" when the AWS
account hasn't been CDK-bootstrapped. The deploy command handles this
via useCdkPreflight, but import bypasses that since it's CLI-only.
Check bootstrap status after CDK synth and auto-bootstrap if needed,
before disposing the toolkit wrapper.
Tested on two unbootstrapped accounts (509471412906, 126432121770).
Constraint: Must bootstrap before disposing toolkitWrapper since bootstrap requires it
Rejected: Prompt user to manually run cdk bootstrap | poor UX for import flow
Confidence: high
Scope-risk: narrow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(import): resolve lint and format issues in import files
- Remove unused waitUntilChangeSetCreateComplete import
- Prefix unused assetHash with underscore
- Add eslint-disable for necessary any cast in STS credentials
- Remove unnecessary type assertion in test
- Fix prettier formatting in multi-agent test
Confidence: high
Scope-risk: narrow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tests): update tests for optional OAuth discoveryUrl schema change
OAuthCredentialProvider.discoveryUrl was made optional to support imported
providers that already exist in Identity service. Update tests to match:
- Schema test: expect success when discoveryUrl is omitted
- Pre-deploy identity tests: add discoveryUrl to fixtures that test
update and error paths (without it, the code now skips the provider)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tests): hoist bootstrap mocks in idempotency test to survive clearAllMocks
checkBootstrapNeeded and bootstrapEnvironment were created as inline
vi.fn() inside the vi.mock factory. vi.clearAllMocks() in beforeEach
wipes their mockResolvedValue, causing handleImport to fail in CI
where test execution order differs from local runs.
Hoist the mocks and configure them in setupCommonMocks like all other
mock functions in the file.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* logs
* fix(import): rollback config on CDK/CloudFormation phase failure
When import fails during CDK build/synth or CloudFormation phases, the
merged config (agentcore.json) was already written to disk. On retry,
the import would detect existing agents and report false success without
completing the CloudFormation import. This snapshots the original config
before merging and restores it if any later phase fails.
Constraint: Rollback only triggers after config is written (configWritten flag)
Constraint: Snapshot must be a deep clone since projectSpec is mutated in-place
Rejected: Deferred config write until after all phases | too invasive, changes control flow for all paths
Confidence: high
Scope-risk: narrow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(import): map STM_ONLY memory to zero strategies
STM_ONLY memories incorrectly received a SEMANTIC strategy in the import
flow, while the CLI create flow and starter toolkit both deploy STM_ONLY
with zero strategies. This mismatch caused drift/errors during reconciliation.
Constraint: Must match CLI create flow which maps shortTerm to strategies: []
Rejected: Keep SEMANTIC for STM_ONLY | causes reconciliation drift with deployed resources
Confidence: high
Scope-risk: narrow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(import): extract custom JWT authorizer config from starter toolkit YAML
Instead of warning users to manually recreate the gateway, the import
pipeline now reads authorizer_configuration.customJWTAuthorizer from
the starter toolkit YAML and writes authorizerType/authorizerConfiguration
to agentcore.json so CDK handles it automatically.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add yaml@2 and regenerate package-lock.json for CI compatibility
Resolves npm ci failure caused by missing yaml@2.8.3 transitive
dependency required by vitest/vite and lint-staged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>1 parent 0b743db commit 2142c77
37 files changed
Lines changed: 10251 additions & 2153 deletions
File tree
- src
- cli
- commands/import
- __tests__
- fixtures
- operations/deploy
- __tests__
- tui/utils
- schema/schemas
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
| 51 | + | |
51 | 52 | | |
52 | 53 | | |
53 | 54 | | |
| |||
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
| 98 | + | |
98 | 99 | | |
99 | 100 | | |
100 | 101 | | |
101 | 102 | | |
102 | 103 | | |
103 | 104 | | |
104 | 105 | | |
105 | | - | |
| 106 | + | |
106 | 107 | | |
107 | 108 | | |
108 | 109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
| |||
138 | 139 | | |
139 | 140 | | |
140 | 141 | | |
| 142 | + | |
141 | 143 | | |
142 | 144 | | |
143 | 145 | | |
| |||
Lines changed: 268 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
Lines changed: 39 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
Lines changed: 28 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
0 commit comments