fix(exec): support harness deployments (#1724)#1728
Conversation
agentcore exec reported "No deployed runtimes found" for harness
deployments because loadExecContext only inspected
resources.runtimes, while status and invoke also inspect
resources.harnesses. A harness carries its underlying runtime ARN in
agentRuntimeArn, which is exactly what exec needs to open a shell.
loadExecContext and the ExecScreen picker are now harness-aware: they
consider both buckets, add a --harness flag, auto-select a single
runtime or (failing that) a single harness, and require an explicit
flag when both kinds are deployed.
Constraint: ExecContext only carries { region, runtimeArn }; a harness
resolves to harnessState.agentRuntimeArn (a full ARN).
Rejected: Reuse resolveHarness from operations/resolve-agent.ts |
it returns runtimeId, not the full ARN exec needs, and adds a
getHarness API call exec does not require.
Confidence: high
Scope-risk: narrow
Directive: Keep the runtimes-only auto-select path unchanged so
existing single-agent exec behavior does not regress.
Not-tested: Live harness with agentRuntimeArn absent from local state
E2E testing against a real harness deployment revealed the initial fix
resolved a harness to its underlying agentRuntimeArn, which the data
plane rejects: HarnessLinkedRuntimeValidator blocks ExecuteCommand and
the WebSocket shell against harness-linked runtimes ("managed by a
harness and cannot be invoked directly"). The service instead routes a
harness ARN on the /runtimes/{arn}/... path through the harness exec
path (ExecuteCommandActivity.invokeHarnessExecuteCommand delegates to
LoopyDP). loadExecContext and ExecScreen now resolve harnesses to
harnessState.harnessArn.
Verified E2E in us-east-1: `agentcore exec` (auto-select), `exec
--harness <name>`, and `exec --json` all run inside the harness
container; passing agentRuntimeArn is rejected by the service.
Constraint: Data plane only accepts a harness ARN (not its linked
runtime ARN) on the exec/shell path.
Rejected: Resolve harness -> agentRuntimeArn | service returns
ValidationException, exec never connects.
Confidence: high
Scope-risk: narrow
Directive: Do not "simplify" harness exec to use agentRuntimeArn — the
service will reject it. The harness ARN is required.
Package TarballHow to installgh release download pr-1728-tarball --repo aws/agentcore-cli --pattern "*.tgz" --dir /tmp/pr-tarball
npm install -g /tmp/pr-tarball/aws-agentcore-0.23.0.tgz |
|
Claude Security Review: no high-confidence findings. (run) |
agentcore-cli-automation
left a comment
There was a problem hiding this comment.
Nice fix — the harness discovery path is well-structured and the tests cover the important branches. Found two related issues around --harness handling in the command wiring that should be addressed before merging. Both are behavioral (not style), and neither is exercised by the current tests.
Coverage Report
|
|
Nit / simplification the tail of The new no-flag path handles "0 deployed", "both kinds exist", "N harnesses", and "N runtimes" as four separate states, plus the // No flag: exec needs exactly one target.
const candidates = [
...Object.values(runtimes).map(r => r.runtimeArn),
...Object.values(harnesses).map(h => h.harnessArn),
].filter(Boolean);
if (candidates.length === 0) {
throw new Error(`No deployed runtimes or harnesses found in target '${targetName}'.`);
}
if (candidates.length > 1) {
throw new Error(
`Target '${targetName}' has multiple deploy targets. Specify one with --runtime <name> or --harness <name>: ` +
[...Object.keys(runtimes), ...Object.keys(harnesses)].join(', ')
);
}
return { region, runtimeArn: candidates[0]! };Same behavior, ~30 lines → ~18. The one thing you give up is kind-specific ambiguity wording ("Multiple harnesses..." vs "Multiple agents..."); the merged message lists all names and both flags, so it's always correct regardless of mix. If the distinct wording is intentional UX, ignore this. The explicit |
…RN paths Address PR review feedback (#1728): - Interactive mode: `exec --it --harness <name>` fell through to the agent picker because the direct-PTY guard only checked `runtimeArn`. It now takes the direct path for either `runtimeArn` or `harnessName`. - Mutual exclusion: `--runtime <arn> --harness <name>` silently ignored `--harness` because both ARN short-circuits returned before the mutex check. Hoisted the mutex to the top of loadExecContext so it covers every path. - Simplification: collapsed the no-flag tail from four kind-specific branches to a single candidate-count check; the merged ambiguity message lists all names and both flags, so it is correct regardless of the runtime/harness mix. Tests: added regressions for `--it --harness` skipping the picker, ARN-form `--runtime` + `--harness` mutex (with and without --region), and updated the ambiguity-wording assertions for the merged message. Constraint: data plane only accepts a harness ARN (not its linked runtime ARN) on the exec path Rejected: enforce mutex at the CLI layer in command.tsx | loadExecContext is the single chokepoint every caller (incl. TUI exit path) routes through Confidence: high Scope-risk: narrow Directive: the no-flag ambiguity message intentionally merges runtime+harness kinds — do not re-split without restoring the kind-specific wording tests
The AgentCore data plane (InvokeAgentRuntimeCommandShell) explicitly rejects harness-linked runtimes: interactive shell is not supported for harness deployments yet. Previously `agentcore exec --it` against a harness surfaced an opaque 403 reconnect loop with a misleading "check IAM permission" message. loadExecContext now detects an interactive session resolving to a harness ARN and throws up front with actionable guidance to use one-shot exec, which IS supported. The guard lives in loadExecContext (the single resolution chokepoint) so it covers --harness, a harness ARN via --runtime, and the auto-select / picker paths uniformly. It keys off the resolved ARN (…:harness/…), and only fires when options.interactive is set — one-shot exec against a harness is unaffected. Verified E2E against a real us-east-1 harness under a PTY: `exec --it` now prints the guidance and makes no network call; one-shot exec still runs in the harness container. Constraint: Service blocks InvokeAgentRuntimeCommandShell for harnesses (YggDP HarnessLinkedRuntimeValidator / CommandShellActivity guard). Rejected: Duplicate the check in command.tsx before requireTTY() | would not cover auto-select/picker paths; loadExecContext is the one chokepoint that sees the resolved ARN in every case. Confidence: high Scope-risk: narrow Directive: Remove this guard only once the service supports the shell API for harness-linked runtimes (the error message says "yet").
|
Claude Security Review: no high-confidence findings. (run) |
--harness now accepts a full harness ARN in addition to a name, matching --runtime <name|arn>. A name is resolved to the harness ARN from deployed state; an `arn:` value is used directly (with an `arn:` + --region short-circuit that skips the config read, mirroring --runtime). A runtime ARN passed to --harness is rejected with guidance to use --runtime, and a full-ARN --harness skips the project requirement like --runtime does. Also corrects the stale ExecOptions.harnessName doc comment (it resolves to the harness ARN, not the underlying agentRuntimeArn). Confidence: high Scope-risk: narrow
|
Claude Security Review: no high-confidence findings. (run) |
Problem
Fixes #1724.
agentcore execreported "No deployed runtimes found" for harness deployments, whileagentcore statusandagentcore invokeworked. Deployed resources live in two buckets —resources.runtimesandresources.harnesses— andexeconly inspectedruntimes.Root cause
Two layers:
loadExecContext(and theExecScreenpicker) only enumeratedresources.runtimes, so a harness-only project always hit the "No deployed runtimes found" throw.agentRuntimeArn. The Yggdrasill data plane'sHarnessLinkedRuntimeValidatorblocksExecuteCommand/InvokeAgentRuntimeCommandShellagainst a harness-linked runtime ARN ("managed by a harness and cannot be invoked directly"), but routes a harness ARN on the/runtimes/{arn}/...path through the harness exec path (ExecuteCommandActivity.invokeHarnessExecuteCommand→ LoopyDP). The first attempt resolved toagentRuntimeArnand got rejected by the service; the corrected fix targetsharnessState.harnessArn.Changes
loadExecContext(src/cli/commands/exec/action.ts): harness-aware. Considers both buckets; adds--harness <name>; auto-selects a single runtime, else a single harness; errors when both kinds are deployed and no flag is given; resolves harnesses to the harness ARN.ExecScreen(src/cli/tui/screens/exec/ExecScreen.tsx): picker lists harnesses (via harness ARN); empty-state message updated.--harnessflag registered onagentcore exec(command.tsx,types.ts); mutual-exclusion with--runtime.has_harnessto exec metrics (command-run.tsschema + all 3 call sites).Tests
loadExecContext with harnessesblock (regression foragentcore execreports 'No deployed runtimes found' for harness deployments #1724) — auto-select single harness,--harness, not-found, missing harness ARN, multiple-harness ambiguity, runtime+harness ambiguity,--runtime/--harnessmutual exclusion. Existing runtime-only tests unchanged.End-to-end validation (real AWS)
Built the tarball (
npm run bundle), installed it into a scratch project,agentcore create(harness is the default) +agentcore deployto a real harness inus-east-1, then exercisedexecagainst the deployed harness. Verbatim output:The deployed CloudFormation stack was torn down after validation.
Constraint: Data plane only accepts a harness ARN (not its linked runtime ARN) on the exec/shell path.
Confidence: high
Scope-risk: narrow