Skip to content

feat(invoke): overload invoke for project and existing Runtimes, harnesses, and Gateways - #2399

Merged
tejaskash merged 7 commits into
refactorfrom
feat/invoke-overload
Sep 25, 2026
Merged

tejaskash merged 7 commits into
refactorfrom
feat/invoke-overload

Conversation

@tejaskash

@tejaskash tejaskash commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

What

agentcore invoke is now one command that invokes a Runtime, harness, or Gateway. It replaces the invoke runtime and invoke harness subcommands.

  • --runtime, --harness, and --gateway each take a project name, an ID, or an ARN. resolveResource in src/handlers/utils.tsx returns { id, region, credentials }.
    • A project name resolves through resolveDeployedResource, which now covers Gateways. It returns the target's region and account-verified credential provider.
    • An ID or ARN resolves locally, with no lookup.
  • invoke then runs the existing runtime invoke, harness invoke, or gateway invoke handler with that ID, region, and credentials on the context. There is one copy of each request path, and the existing TUIs and routes are unchanged.
  • Without a resource flag, an interactive invoke opens the picker, which now lists Gateways too. A headless call needs exactly one of --runtime, --harness, or --gateway.
  • IDs and ARNs use your default credentials. An ARN uses its own region.
  • --local stays project Runtime only.

The standalone runtime|harness|gateway invoke commands are unchanged and stay behind imperative-commands.

Why

The CLI is now project first (#2397, #2396). With the standalone families hidden by default, top-level invoke has to reach both project resources and existing resources.

How tested

  • Unit and screen tests. bun test passes, and typecheck and lint are clean.
  • Live in us-west-2: a project created and deployed through the CLI with a Runtime, a harness, and an MCP Gateway.
    • Each type was invoked by project name, by ID, and by ARN from inside the project.
    • Each type was invoked by ID and by ARN from outside the project, including an ARN with a different --region.
    • The bare invoke picker was used to open and use all three consoles.
    • By project name with environment credentials set alongside an AWS_PROFILE for a missing profile. This uses the target's verified credentials.
    • With imperative-commands on, runtime invoke, harness invoke, and gateway invoke were run headless and in the TUI. With it off, they stay hidden.

…nesses, and Gateways

BREAKING CHANGE: the invoke runtime, invoke harness, runtime invoke, harness invoke, and gateway invoke commands are removed. Use agentcore invoke with --runtime, --harness, or --gateway, which accept a project name, ID, or ARN.
@github-actions github-actions Bot added the size/xl PR size: XL label Sep 24, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added agentcore-harness-reviewing AgentCore Harness review in progress claude-security-reviewing Claude Code /security-review in progress labels Sep 24, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed claude-security-reviewing Claude Code /security-review in progress agentcore-harness-reviewing AgentCore Harness review in progress labels Sep 24, 2026

@agentcore-devx-automation agentcore-devx-automation Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AgentCore Harness Review

Verdict: Looks good

Nice refactor. The unified invoke command reads well, the resource-selection logic (selectResource + REQUEST_FLAGS) cleanly enforces per-resource flag scoping, and the route/deep-link updates are consistent across screens (Root.tsx, runtime/invoke/screen.tsx, gateway/invoke/screen.tsx, harness/get/screen.tsx, etc.). Tests are thorough and stay on the right side of the mocking line (real TestCoreClient, real temp directories, renderTuiAt only spied at the TUI boundary; the new toResourceArn suite in utils.test.tsx is a good addition).

A couple of small things worth being aware of — not blockers:

  • In src/handlers/project/invoke/index.tsx, headless is set whenever any flag is present (Object.values(flags).some(isSet)). Something benign like agentcore invoke --target prod inside a multi-resource project will therefore fall through the picker and error with "Choose a resource to invoke: …" instead of opening the TUI on that target. Similarly --target isn't threaded into ProjectInvokePickerScreen when the TUI does open, so it's silently ignored. Probably fine given the error message points users at the right flags, but worth confirming that's the intended UX.
  • Collapsing invoke runtime|harness|gateway into a single command path means the auto-emitted command_path telemetry attribute no longer distinguishes the three resource kinds. If invoke-by-resource-type is a metric you care about, consider setting a resource_type attribute on CommandRunMetricEventKey from the handler.

The runtime, harness, and gateway invoke commands stay behind the imperative-commands setting with their routes unchanged. agentcore invoke resolves the resource ARN, then runs the matching standalone handler with the service ID and the ARN's region on the context.
@tejaskash tejaskash changed the title feat(invoke)!: overload invoke for project and existing Runtimes, harnesses, and Gateways feat(invoke): overload invoke for project and existing Runtimes, harnesses, and Gateways Sep 24, 2026
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Sep 24, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Sep 24, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Sep 24, 2026
@codecov-commenter

codecov-commenter commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.75000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.28%. Comparing base (7d8f74d) to head (43c89e9).
⚠️ Report is 5 commits behind head on refactor.

Files with missing lines Patch % Lines
src/handlers/project/invoke/runtime.tsx 98.80% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           refactor    #2399      +/-   ##
============================================
+ Coverage     97.25%   97.28%   +0.03%     
============================================
  Files           612      611       -1     
  Lines         41019    40987      -32     
============================================
- Hits          39893    39875      -18     
+ Misses         1126     1112      -14     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Merge the runtime, harness, and gateway invoke flag lists instead of copying them. An interactive bare invoke always opens the picker so the deployment target is chosen there. Simplify picker rows and update a stale comment.
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Sep 24, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Sep 24, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Sep 24, 2026
…kups

Project names resolve through resolveDeployedResource, now covering Gateways, so the delegated handler gets the target's region and account-verified credential provider. IDs and ARNs resolve locally and the existing handler does its single lookup. The deployment target is passed explicitly instead of through a router context key, and the picker keeps the target credentials and region.
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Sep 24, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Sep 24, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Sep 24, 2026
Comment thread src/handlers/project/invoke/index.tsx Outdated
const resourceFlags = [
flag(
"runtime",
"the Runtime to invoke: a project name, ID, or ARN",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a little confusing I would do : resource id in project or ARN

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, it read a bit oddly. Changed it to "its name in this project, its ID, or its ARN". It's the project name that gets matched, not an ID, so I kept that part.

return name.replace(/_/g, "");
}

function findDeployedResourceId(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OOS, but doesn't this function fail if resourceType isn't a gateway, harness or runtime?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's typed to runtime, harness, and gateway only, so anything else won't compile. Those are the three types this function handles.

Comment thread src/handlers/project/selection.ts Outdated
import { InputValidationError, ResourceNotFoundError } from "../../errors";
import type { Project, ProjectInvokableResource } from "./types";

export const RESOURCE_LABELS: Record<ProjectInvokableResource, string> = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: i feel like we have this same constant / mapping defined in a few places. Wondering if we should consolidate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, there were two copies. Now there's one RESOURCE_LABELS in handlers/project/types.ts, and ProjectManager uses it too.

Comment thread src/router/flags.tsx Outdated
return `\nParameter details:\n\n${sections.join("\n\n")}\n`;
}

export function formatExamples(examples: HelpExample[]): string | undefined {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how is this related to the lifting of the command?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It isn't really. I pulled it out into its own branch (feat/help-examples) and I'll open that PR once this one lands.

Comment thread src/router/handler.tsx Outdated
children(): Handler[];
}

export interface HelpExample {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feels like a tangential change to the goal of this PR unless I'm missing a connection.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, moved it to feat/help-examples.

Comment thread src/handlers/project/invoke/index.tsx Outdated
const given = RESOURCE_TYPES.find((resourceType) => flags[resourceType] !== undefined);
if (given) return [given, flags[given]!];

if (!project) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not fully following the logic here.

Its saying that when selecting the resource to invoke:

  • If i'm not in a project and in headless, error with no project found.
  • if I'm not in a project and i'm in a tui, return undefined.

Whats the intuition for this? I would have expected selectResource to be independent of TUI, and not to throw a project not found error?

@tejaskash tejaskash Sep 25, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair, it was doing two jobs at once: picking the resource, and deciding whether the TUI opens. The function returning undefined meant the picker would handle selecting what to do.

I split it so neither function knows about the TUI:

  • flaggedResource returns whatever --runtime, --harness, or --gateway named, or nothing.
  • soleProjectResource returns the only resource in the project, or throws saying why (no project, no resources, or several to pick from).

The handler makes the TUI call:

const selection =
  flaggedResource(flags) ?? (headless ? soleProjectResource(project) : undefined);
if (!selection) // open the picker

So "no project found" only comes up on the headless path, where there's no picker to show it. In the TUI, the picker already has its own no-project screen.

@Hweinstock Hweinstock Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

discussed in person, we can simplify by opening the TUI on missing selector rather than trying to be smart and detect if there is a single resource first.

Comment thread src/handlers/project/invoke/index.tsx Outdated
gateway: invokeGatewayFlags,
} as const;

type RequestFlag = Exclude<(typeof INVOKE_FLAGS)[ProjectInvokableResource][number], { name: "id" }>;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whats the intuition behind a request flag? what makes it different from a normal flag?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad name on my part. They're the flags that belong to runtime/harness/gateway invoke, merged by name and passed through to whichever one runs. Renamed them to handlerFlags. The ones invoke owns itself (--runtime, --harness, --gateway, --target, --local, --port) are selectionFlags now.

Comment thread src/handlers/project/invoke/index.tsx Outdated
if (resolved.credentials) {
invokeCtx = invokeCtx.withValue(AwsCredentialProviderKey, resolved.credentials);
}
await invoker.handle(invokeCtx, request, {});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of curiosity, whats the advantage of calling the handlers v.s. calling the underlying functionality directly?

@tejaskash tejaskash Sep 25, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly so there's one copy of each request path. The handlers already do the flag checks, stdin payloads, output, progress, cancel on Ctrl-C, and the TUI deep link. My first pass called the operations directly and ended up copying about 150 lines per type. This way invoke and runtime/harness/gateway invoke can't drift apart.

Comment thread src/handlers/project/invoke/index.tsx Outdated
...(resourceType === "runtime" ? ["local", "port"] : []),
...invoker.flags().map(({ name }) => name),
];
const misplaced = Object.entries(flags).find(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: are there better names for these variables? invoker, misplaced, selected, etc. all feel a little ambiguous?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Renamed invoker to handler, selected to selection, misplaced to unsupported, allowed to acceptedFlags, and request to parsedHandlerFlags.

Split resource selection so it no longer depends on the TUI, rename the forwarded flags to handlerFlags, keep one RESOURCE_LABELS map in the project types, reword the resource flag help, and move the help examples feature to its own PR.
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Sep 25, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Sep 25, 2026
@tejaskash

Copy link
Copy Markdown
Contributor Author

On the automated review: --target on its own taking the headless path is on purpose. It lists the choices so you can pick one. Passing --target into the picker would need DeploymentTargetPicker to take a starting target, so I'd leave that and a resource_type telemetry attribute for later.

@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Sep 25, 2026
A bare interactive invoke always opens the picker, and a headless invoke needs exactly one of --runtime, --harness, or --gateway. Docs and template READMEs name the resource explicitly, and the minimal template now renders its README.
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Sep 25, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Sep 25, 2026
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Sep 25, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Sep 25, 2026
@tejaskash
tejaskash merged commit a08ce71 into refactor Sep 25, 2026
22 checks passed
@tejaskash
tejaskash deleted the feat/invoke-overload branch September 25, 2026 20:13
@tejaskash
tejaskash restored the feat/invoke-overload branch September 25, 2026 20:17
@tejaskash
tejaskash deleted the feat/invoke-overload branch September 25, 2026 20:17
@tejaskash
tejaskash restored the feat/invoke-overload branch September 25, 2026 20:20
@tejaskash
tejaskash deleted the feat/invoke-overload branch September 25, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants