Uh oh!
There was an error while loading. Please reload this page.
feat: close the eight essential CLI feature gaps - #2146
Open
AlexanderRichey wants to merge 12 commits into
Open
Conversation
`project deploy` now synthesizes the `default` target when aws-targets.json is missing, empty, or lacks a `default` entry: the account comes from STS GetCallerIdentity and the region from the CLI's already-resolved effective region, validated against AgentCoreRegionSchema before anything is written. Existing entries are preserved byte-for-byte, the synthesized entry is reported on stderr, and the deploy proceeds in the same invocation. Named targets still require explicit configuration, and unsupported regions or unresolvable credentials fail with actionable errors before the file is touched. Also restores the vended CDK app (bin/cdk.ts, lib/cdk-stack.ts, test/cdk.test.ts, package.json) to its last publishable state: it referenced an AgentCorePayments L3 that no released @aws/agentcore-cdk exports, so every fresh scaffold failed `cdk synth` at tsc before reaching AWS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
…eck gate Leftover e2e scaffolds under out/ (and stale dist/ build output) were being swept up by bun test and tsc, failing the gate on files that are not part of the source tree. Scope both to the repo source, and fix the two react(set-state-in-effect) findings oxlint 1.80 raises in usePagedList and DataTable by deriving/adjusting state during render instead of in effects. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
Bare `project create --name X` (and --defaults) now creates a harness project — a managed agent configured by spec — matching the original CLI's default quick start. The harness-only flags (--model-id, --api-key-arn, --api-base, --additional-params, --no-harness-memory, --max-iterations, --max-tokens, --timeout, --truncation-strategy, --container) flow into the harness spec, which is validated up front against the same schema `project add harness` uses and scaffolded through the same addResource path, so the two entry points cannot drift. Runtime scaffolding is selected by --template or the runtime flags exactly as before; mixing the two flag families is a validation error, mirroring the original's dispatch. Tests that relied on the previous hello-world default now scaffold it explicitly via --template. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
`project create --type import --agent-id <id> --agent-alias-id <id>` (and the same on `project add runtime`) wraps an existing Amazon Bedrock Agent as an AgentCore runtime: the agent and alias are described through @aws-sdk/client-bedrock-agent to validate they exist (with the global --region naming the agent's region, validated against the supported list), and a proxy runtime is scaffolded from a new bedrock-agent-proxy-python template that forwards prompts via InvokeAgent and streams the reply. The vended bedrock-agent-policy.json grants the execution role bedrock:InvokeAgent on the alias through the runtime's additionalPolicies, so the project deploys and invokes like any other. The original CLI translated the Bedrock Agent's definition into native framework code; this ships the proxy shape instead — the agent stays the brain, the runtime is the AgentCore front door. bun.lock: adds the @aws-sdk/client-bedrock-agent entry; the surrounding churn is bun regenerating the lock format on install. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
project remove now accepts credential, config-bundle, online-eval, online-insight, and memory; removing a credential also deletes the .env.local keys it reserved (EnvLocalFile gains removeKeys with snapshot/rollback). Removing a resource that does not exist now throws ResourceNotFoundError instead of warn-and-rewrite. New `project remove all` empties every resource collection in agentcore.json (spec-level; app/ code and aws-targets.json are kept) behind a y/N prompt with --yes for non-interactive use. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
Bare `agentcore --version` (and -V) prints the package version embedded at build time and exits 0, in dev, bundled, and compiled forms. Handled as a pre-parse intercept on the root router rather than a Commander version option: a root-level --version option would shadow subcommands that declare their own `--version <value>` flag (harness version get, runtime version get, dataset --version, …). Root-only, matching the original CLI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
`agentcore project export harness` converts a harness into an editable Python Strands runtime agent: it renders the strands-http-python template under app/<target-agent-name>/ with a context mapped from the harness spec (model incl. Bedrock Mantle/OpenAI/Gemini/LiteLLM, system prompt, remote MCP + inline-function tools, path/s3/git skills, in-project memory, truncation, and execution limits via a vendored hooks/execution_limits.py), registers the runtime in agentcore.json (the harness entry stays), installs its deps with uv sync, and writes an EXPORT_NOTES.md in the agent directory listing precise manual follow-ups for everything that cannot be mapped mechanically (gateway/browser/code-interpreter tools, external/managed memory, aws skills, custom Dockerfiles, ...). The harness comes from `--name` (in-project files) or `--arn` (fetched from the service via GetHarness, using the region embedded in the ARN); exactly one is required, and the project is resolved and validated by withProject before any service fetch. `--target-agent-name` defaults to <harnessName>Agent, `--build` overrides the CodeZip default (containerUri / dockerfile harnesses auto-select Container), and `--json` emits a machine-readable summary on stdout while progress and notes stay on stderr. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
Move the runtime log-group derivation, the Insights query-value sanitizer, and the paginating CloudWatch Logs Insights query runner out of the private scope of src/core/eval.tsx into src/core/observability.ts so the upcoming runtime logs/traces commands can share them. The runner's row-ceiling error becomes an injectable policy (InsightsRowLimit); eval passes its existing message, so behavior is unchanged. Also adds parseTimeString, porting the old CLI's time-parser semantics (now / relative 5m-1h-2d / epoch ms / ISO 8601) with a typed InputValidationError on invalid input. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
Adds `agentcore runtime logs`: by default it live-tails the runtime's CloudWatch log group via StartLiveTail (reconnecting transparently over the ~3h session cap; Ctrl+C exits 130), and with --since/--until it switches to a bounded FilterLogEvents search, paginated to completion oldest-to-newest. --level/--query compose into a server-side filter pattern (ported from the old CLI's filter-pattern.ts), --limit caps search results, and --json emits JSON Lines. A missing log group maps to "has the runtime been invoked yet?" guidance. Addressing follows `runtime invoke --id`; without --id inside a project the deployed runtime is resolved live from the target stack's CloudFormation outputs (default target, one runtime auto-selects, several list candidates), through a new ObservabilityClient wired into CoreClient and the Core contract. The runtime router's TUI dispatch is limited to its existing children so a bare `runtime logs` streams instead of opening the TUI. The gitignore's blanket `logs` entry is scoped to the repo root so the handler directory is trackable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
Adds `agentcore runtime traces list` and `agentcore runtime traces get <traceId>`. list aggregates the runtime's telemetry with a Logs Insights `stats ... by traceId` query (newest first, default 12h window, --limit default 20) and renders a traceId/timestamp/sessionId table, or a single JSON document with --json; an empty result prints a stderr notice that traces take 2-3 minutes to appear. get validates the trace-id format, downloads every log record of the trace (@message JSON-parsed when possible), writes them to --output or agentcore/.cli/traces/ <runtime>-<traceId>.json inside a project (./<traceId>.json outside), and prints the written path on stdout. Both leaves share `runtime logs`' addressing: --id anywhere, or automatic resolution of the project's deployed runtime from its stack outputs. The queries run through the shared Insights runner extracted in the earlier observability refactor. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
A bare `agentcore project create` in a terminal now opens an interactive wizard: name (validated live against ProjectNameSchema, schema messages inline) → project type → harness model id or agent template (with the strands memory choice; hello-world skips it) → confirmation summary → live ProjectManager progress → success screen with next steps. The wizard builds the same CreateProjectInput as the flag-driven handler — resolveScaffoldHarnessInput (now exported) for the harness path, resolveRuntimeTemplateShortcut for templates — and iterates core.projectManager.create in the cwd, npm install and git init included. A create() failure renders and tears the TUI down via useApp().exit(error), so the process exits nonzero. Dispatch: only a bare, flagless invocation on a TTY opens the wizard (withTuiOnEmptyFlagsAndArgs, TTY-gated at registration). Any user-supplied flag, --json, or a non-TTY session stays headless. To make the middleware reachable, --name is optional at the Commander layer and enforced inside handle with the same "required option '--name <name>' not specified" wording — so `project create --defaults` and bare non-TTY invocations fail exactly as before. The other six project subcommands keep their not-implemented stubs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
Ink delivers rapidly typed keystrokes and every terminal paste as one
multi-character useInput event whose key.return is false even when the
chunk ends in "\r" — so TextInput appended the control byte into the
value as an invisible character. In the create wizard's name step that
left a visually valid name failing ProjectNameSchema with a message the
user couldn't act on ("must ... contain only alphanumeric characters")
and enter refusing to advance. Found by driving the wizard end-to-end
in a real pty during the final live smoke.
A single-line input must never store control bytes: strip C0 controls
and DEL from appended text (the official ink-text-input strips \r/\n
for the same reason). A stripped chunk's newline is deliberately not a
submit — matching readline and browser single-line paste semantics.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8PContributor
Claude Security Review: no high-confidence findings. (run) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the eight essential feature gaps between the original CLI and the refactor, one commit per gap, each verified live against AWS account 501930284170 (us-east-1) where the gap has a deployable surface.
Gaps closed
1. Harness-first project creation —
feat(create): harness-first project creationBare
project create --name X(and--defaults) now creates a harness project, matching the original's default quick start. Harness-only flags (--model-id,--api-key-arn,--api-base,--additional-params,--no-harness-memory,--max-iterations,--max-tokens,--timeout,--truncation-strategy,--container) flow into the harness spec; the create-time harness scaffolds through the sameaddResourcepath asproject add harness; mixing harness and runtime-scaffolding flags is a validation error mirroring the original's dispatch.Live e2e:
project create --name GapE2eHarness0830 --model-id us.amazon.nova-lite-v1:0→ zero-configproject deploy→ stackAgentCore-GapE2eHarness0830-defaultCREATE_COMPLETE (us-east-1) →harness invokereturned a real Nova Lite response. Stack destroyed after (DELETE_COMPLETE).2. Bedrock Agent import —
feat(create): import a Bedrock Agent as a runtimeproject create --type import --agent-id --agent-alias-id(andproject add runtime --type import) describes the agent/alias via@aws-sdk/client-bedrock-agent(new dependency), validates the region against the supported list, and scaffolds a proxy runtime from the newbedrock-agent-proxy-pythontemplate (InvokeAgent + streamed reply; vendedbedrock-agent-policy.jsongrantsbedrock:InvokeAgentviaadditionalPolicies). Deliberate shape difference: the original translated the agent definition to framework code; this ships the proxy form per the parity spec.Live e2e: created throwaway Bedrock Agent
6AHPCYGV7S(Nova Lite) + aliasJJUMUT2PCQ+ service role; imported; deployedAgentCore-GapE2eImport0830-defaultCREATE_COMPLETE;runtime invoke→data: "The capital of France is Paris."; then destroyed the stack and deleted the agent, alias, and role.3. Export harness → Strands runtime —
feat(project): export a harness to a Strands runtime agentNew
project export harness(--namefor an in-project harness,--arnfor any service harness — region-aware from the ARN;--target-agent-name,--build CodeZip|Container,--json). Renders editable Strands code underapp/<target>/through the stock strands template's export context (model config for all four harness providers, system prompt, remote-MCP tools with credential env wiring, inline functions, skills, in-project memory, truncation, execution limits via a vendoredhooks/execution_limits.py), registers the runtime inagentcore.json(the harness entry is kept, mirroring the original), writesEXPORT_NOTES.mdfor anything unmappable, refuses overwrites, and validates the project before any service fetch (the 0.28.1 fix). 73 new unit tests.Live e2e:
GapE2eExport0830— deploy #1 CREATE_COMPLETE with harnessGapE2eExport0830_exportme-ZFg6mVuUYnREADY → export →project build→ deploy #2 UPDATE_COMPLETE →runtime invokeon the exported runtime streamed a live Nova Lite reply ("Exported agent reporting for duty."); the--arnpath was exercised against the deployed harness and built. Stack destroyed (DELETE_COMPLETE).4. Complete
project remove+remove all—feat(remove): cover all addable resources and add remove allThe remove enum now covers every addable type (adds credential, config-bundle, online-eval, online-insight, memory) with
.env.localcleanup for credentials; removing a nonexistent resource raisesResourceNotFoundErrorinstead of warn-and-rewrite;project remove allresets every spec collection behind a y/N confirmation with--yes.Live e2e:
GapE2eRemove0830— added all five newly-removable types, removed them (spec +.env.localverified), deployed CREATE_COMPLETE,remove all --yes, thendeploy --yesteardown path → DELETE_COMPLETE.5.
runtime logs—feat(runtime): logs streaming and search(+refactor(core): extract shared observability helpers)The precursor commit extracts the runtime log-group derivation and the multi-log-group, fully-paginated Logs Insights runner out of
core/evalintosrc/core/observability.ts(zero behavior change; eval re-pointed) and adds the time-string parser (now,30s/5m/1h/2d, epoch ms, ISO 8601).runtime logsthen ships follow mode by default (StartLiveTail with auto-reconnect over the 3-hour session cap; Ctrl-C exits 130 cleanly) and search mode on--since/--until(FilterLogEvents, paginated, oldest→newest), with--level,--query(composed into a CloudWatch filter pattern),--limit, and--jsonJSON-Lines.--idworks anywhere; inside a project the runtime id resolves live from the target stack's outputs via DescribeStacks (single runtime auto-selects; several → error listing candidates), with the deployment target's region taking precedence.Live e2e:
GapE2eObs0830deployed CREATE_COMPLETE; 4 invokes; search returned real events;--level/--query/--limit/--jsonverified; follow mode captured a mid-stream invocation marker and exited 130 on SIGINT; auto-resolve without--id(even with a wrong ambient--region) found the events. Stack destroyed after.6.
runtime traces—feat(runtime): traces list and getruntime traces list(--id/project auto-resolve,--limitdefault 20,--sincedefault 12h,--until,--json) aggregates recent traceIds with timestamps and session ids via the shared Insights runner (filtering empty-traceId infra records before aggregation — fixes an off-by-one the original had).runtime traces get <traceId>validates the id format and downloads every record to--outputoragentcore/.cli/traces/<runtime>-<traceId>.json. No hardcoded console URLs (deep link deliberately skipped).Live e2e: all four invocations' traces listed with matching session ids ~3 minutes after invoking;
traces getwrote 10 records to the default project path; invalid/missing trace ids and missing log group produce guided errors.7.
--version—feat(cli): add --versionBare
agentcore --version/-Vprints the build-time version (verified underbun runand in the bundleddist/index.js). Implemented as a root-only pre-parse intercept so leaf--version <value>flags (harness version get, etc.) are not shadowed.8.
project createTUI wizard —feat(tui): project create wizardBare
agentcore project createin a terminal now opens a wizard instead of exiting withNotImplementedError: name (validated live againstProjectNameSchema, schema messages shown inline) → project type (harness — the default, per gap 1 — or scaffolded agent) → harness model id or agent template with the strands memory choice (hello-world skips the memory question) → confirmation summary → liveProjectManagerprogress → success screen withcd/deploy next steps. No forked creation logic: the wizard builds the sameCreateProjectInputas the flag-driven handler (resolveScaffoldHarnessInput, now exported, for the harness path;resolveRuntimeTemplateShortcutfor templates) and iteratescore.projectManager.createin the cwd, npm install and git init included; acreate()failure renders and exits nonzero viauseApp().exit(error). Dispatch is conservative: only a bare, flagless invocation on a TTY opens the wizard (withTuiOnEmptyFlagsAndArgs, TTY-gated at registration) — any user-supplied flag,--json, or a non-TTY session behaves exactly as before, enforced by making--nameoptional at the Commander layer and re-validated in the handler with the identical error wording. The other six project subcommands keep their not-implemented stubs. 16 ink-testing-library tests cover the flows, validation, esc navigation, error teardown, and all four dispatch cases.Live check: wizard driven end-to-end in a real pty — name typed, harness type and default model accepted, review confirmed, creation ran (npm install + git init), success screen, exit 0 — producing a project spec-identical to the flag path's. Doing so surfaced a pre-existing UI-kit bug: Ink hands coalesced keystrokes (and every terminal paste) to
useInputas one chunk whosekey.returnis false, soTextInputstored a trailing\ras an invisible character that failed name validation with a message the user couldn't act on. Fixed infix(ui): strip control bytes from coalesced text-input chunks(C0/DEL stripped from appended text, matchingink-text-input's\r/\nhandling; stripped newlines deliberately don't submit), with a paste regression test.Verification
bun test(2513 pass, 0 fail, 184 files),bun run typecheck,bun run lint:check.project create --name GapE2eSmoke0831(bare → harness project) → zero-configproject deploy(target auto-provisioned: account 501930284170, us-east-1) → stackAgentCore-GapE2eSmoke0831-defaultCREATE_COMPLETE (verified via DescribeStacks) with harness READY →harness invokereturned the requested reply verbatim ("smoke test ok") →runtime logs --id harness_GapE2eSmoke0831_GapE2eSmoke0831-H1UyMN5OLm --since 15mreturned real CloudWatch events from the runtime log group; bareruntime logswithout--idin this harness-only project fails with the intended actionable error ("stack exports no runtime ids … pass --id" — harness stacks export an AgentRuntimeArn but no RuntimeId output, and the auto-resolver's contract is runtime outputs). Stack destroyed after (confirmed gone via DescribeStacks).CDKToolkitbootstrap stacks remain in us-east-1, us-west-2, eu-west-1 (pre-existing).Also included: a leading
chorecommit that scopesbun test/tscto the source tree (generatedout/scaffolds and staledist/output were failing the gate) and fixes tworeact(set-state-in-effect)findings newer oxlint raises.Stacked on #2145 (
feat(deploy): auto-provision the default deployment target from STS): its commit is the base of this branch because every live e2e here rides the zero-config deploy. The diff reduces to just this work once #2145 merges.🤖 Generated with Claude Code
https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P