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.
|
|
||
| const options: ExecOptions = { | ||
| runtimeArn: cliOptions.runtime, | ||
| harnessName: cliOptions.harness, |
There was a problem hiding this comment.
Interactive mode with --harness still opens the picker.
This branch only guards on options.runtimeArn, so agentcore exec --it --harness foo falls through to runExecLoop(options) on line 165 and shows the picker instead of connecting straight to the harness. loadExecContext already understands harnessName — the direct-PTY path just isn't taking it into account.
Suggested fix:
// With --runtime or --harness: skip agent picker, go straight to PTY
if (options.runtimeArn || options.harnessName) {
requireTTY();
await runInteractiveShell(options);
process.exit(0);
}Worth adding a test asserting that --harness <name> in --it mode goes through runInteractiveShell (or at least does not invoke pickAgent).
|
|
||
| // Mutual exclusion: a name-form --runtime and --harness cannot both be set. | ||
| if (options.runtimeArn && options.harnessName) { | ||
| throw new Error('Cannot specify both --runtime and --harness.'); |
There was a problem hiding this comment.
Mutual exclusion is bypassed when --runtime is passed as an ARN.
The --runtime <arn> short-circuit on line 53 returns before the mutex check on line 58, so agentcore exec --runtime arn:... --harness foo silently ignores --harness instead of erroring. The existing test only exercises the name form of --runtime.
Two ways to fix:
- Move the mutual-exclusion check above the ARN short-circuit (and above the earlier
runtimeArn + regionshort-circuit on line 24 as well):if (options.runtimeArn && options.harnessName) { throw new Error('Cannot specify both --runtime and --harness.'); } // ...then the ARN short-circuits and name-form resolution
- Enforce mutual exclusion at the CLI layer in
command.tsxbefore constructingExecOptions, soloadExecContextnever has to consider the combination.
Either way, please add a regression test for the ARN + --harness case.
Coverage Report
|
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