Skip to content

feat: hierarchical agent credentials and per-intent CLI selection - #405

Open
JWThewes wants to merge 4 commits into
mainfrom
feature/issue-404-hierarchical-agent-credentials
Open

feat: hierarchical agent credentials and per-intent CLI selection#405
JWThewes wants to merge 4 commits into
mainfrom
feature/issue-404-hierarchical-agent-credentials

Conversation

@JWThewes

Copy link
Copy Markdown
Contributor

Summary

  • add write-only Bedrock and Kiro credential settings at platform, space, and user scopes with user > space > platform precedence
  • require an explicit CLI selection for Compose with AI and intent start, then pin the selected credential source for the intent lifetime
  • resolve credentials per AgentCore invocation and add scoped APIs, IAM permissions, settings UI, capability indicators, and invalid-credential handling

Testing

  • commit hooks: formatting, lint, secret scanning, dependency audit, frontend typecheck, and 878 tests
  • AgentCore suite: 773 tests
  • intents suite: 203 tests
  • agents suite: 10 tests
  • frontend suite: 464 tests
  • frontend, agents, and intents production builds
  • Terraform validation for the changed API and AgentCore modules

Closes #404

@JWThewes
JWThewes marked this pull request as ready for review August 14, 2026 09:03
}
if (binding) bindings = [binding];
} else {
const requestedCli = payload.requestedCli || meta?.agentCli || null;

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.

for discussion-assist-start on a DRAFT intent, payload does not contain requestedCli and meta.agentCli is still null. Therefore, no credentials are resolved, availableClis is empty, and the assistant fails. Could we pass and resolve the user’s selected CLI and credential binding for this command, and add a test for this scenario?

@jeromevdl

Copy link
Copy Markdown
Contributor

Please also update documentation (at least docs/using-the-platform/platform-settings.md) which still says credentials are platform-only.

/>
}
description="Which AI agent CLI runs this space's work — only CLIs installed in the deployment are selectable."
description="Highlighted when a user chooses a CLI for an intent; each user still makes an explicit selection."

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.

strange description, looks super generic and not targeting the user, more like a comment

import { SecretField } from '@/components/settings/SecretField';
import { SaveStatusButton, type SaveResult } from '@/components/settings/SaveStatusButton';

type Scope = 'platform' | 'space' | 'personal';

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.

Wondering why we don't have the intent scope. As of now the intent binds to the selected scope, but if we want to create a dedicated api key for one intent ?

const ATTACHMENT_INGEST_POLL_MS = 500;
const ATTACHMENT_INGEST_TIMEOUT_MS = 30_000;
const AGENT_CLIS: AgentCli[] = ['kiro', 'claude', 'opencode', 'codex'];
const AGENT_CLI_LABELS: Record<AgentCli, 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.

We have this config at different places in the frontend:
AgentTab.tsx, DefaultModelsCard.tsx, TierModelsSection.tsx and now also IntentView.tsx plus this one.

Could we centralize this in one place ?

// Backward-compatible helper retained for focused tests and local tooling. It
// resolves the legacy platform paths into the supplied object, but production
// AgentCore no longer calls it or mutates process.env.
export const resolveAgentAuth = async ({ env = {}, getParam } = {}) => {

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 function is not used anywhere apart from the tests.


// Only commands that can spawn an agent CLI need hierarchical credential
// resolution. Engine-only commands must remain available during an SSM outage.
const AGENT_AUTH_COMMANDS = new Set([

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.

We could probably simplify / merge this with the handler definition line 110
There are also some if in the resolveInvocationAgentAuth() function that could benefit from this refactor, as we have several place where we check those strings.

const COMMANDS = {
    'init-ws': {
      handler: 'initWs',
      agentAuth: false,
    },
    'run-stage': {
      handler: 'runStage',
      agentAuth: true,
    },
    // ...

platform: 'Platform',
} as const;

const credentialFailureMessage = (failureReason: string | null) => {

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 bit fragile, if the wording changes in the backend, it will silently fail. Maybe the API should return a proper structure (code +message)

Comment thread lambda/agents/index.js
// A session id >= 33 chars is required by InvokeAgentRuntime; the capabilities
// command is stateless so any stable id works.
const CAPABILITIES_SESSION_ID = 'aidlc-capabilities-probe-00000001';
const PLATFORM_CREDENTIAL_BINDINGS = {

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.

in shared/agent-credentials.js we already define a set of constants that could be reused here to avoid creating a copy of the mapping

Comment on lines +375 to +376
"arn:${local.partition}:ssm:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:parameter/${var.project_name}/${var.environment}/users/*/agent-credentials/*",
"arn:${local.partition}:ssm:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:parameter/${var.project_name}/${var.environment}/projects/*/agent-credentials/*",

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 resource is correctly limited to the current application and environment, but the wildcards still allow the shared AgentCore role to decrypt every user and space credential in that deployment. AgentCore also has permission to scan execution metadata containing credential bindings, so these paths are discoverable. Could credential lookup be moved behind the existing broker pattern, so AgentCore can request only the binding authorized for the current invocation? (AgentCore -> agent-credential broker -> SSM)

@JWThewes

Copy link
Copy Markdown
Contributor Author

@jeromevdl addressed your comments

@JWThewes
JWThewes requested a review from jeromevdl August 14, 2026 12:41
missingCredentialBindings.push(credentialBinding);
continue;
}
invocationEnv[credentialEnvName(binding.provider)] = value;

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 PR necessarily passes credentials through the CLI environment. Output redaction should be handled separately and consistently for both agent credentials and MCP environment variables. I’ll track application-level redaction and CloudWatch data-protection policies in a follow-up issue: #406

@jeromevdl

Copy link
Copy Markdown
Contributor

@jeromevdl addressed your comments

Not sure everything was addressed... Please mark resolved the comments you have fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Hierarchical agent credentials and per-intent CLI selection

2 participants