fix(harness): complete credential flow for non-Bedrock model providers#926
fix(harness): complete credential flow for non-Bedrock model providers#926aidandaly24 wants to merge 1 commit intopreviewfrom
Conversation
The harness API key flow had multiple gaps blocking end-to-end deploy+invoke for OpenAI and Gemini harnesses. This change fixes the credential lifecycle end-to-end: TUI/CLI collection, schema validation, deploy-time resolution, and the vended CDK's IAM policy wiring. Highlights - Vended CDK (src/assets/cdk/bin/cdk.ts) now resolves model.apiKeyCredential to the token-vault provider ARN from deployed state, so AgentCoreHarnessRole receives the required bedrock-agentcore:GetResourceApiKey policy. Without this, invokes always failed with AccessDeniedException. - Cross-provider credential dedup filters candidates by authorizerType and provider name so OpenAI and Gemini credentials never contaminate each other at deploy time. - Preflight validator rejects dangling apiKeyCredential references and OAuth credentials used as API key credentials, surfacing the error before CDK synth. - Pre-deploy identity escalates skipped credentials that harnesses reference and rolls back newly-created token-vault providers when the subsequent CDK deploy fails. - Harness primitive writes in rollback-safe order (.env.local then harness.json then agentcore.json as commit point) and rejects skip-on- API-key for non-Bedrock providers, both in the TUI wizard and primitive add(). - Schema now trims apiKeyArn, requires min length on apiKeyCredential, rejects Secrets Manager ARNs case-insensitively, and fails non-Bedrock providers that ship with neither credential field set. - CLI guards: agent-path rejects --api-key-arn; harness path rejects both --api-key and --api-key-arn; Secrets Manager ARN rejected at validate time with a clear remediation message. - Extracted computeCredentialName to credential-utils so schema-mapper and CredentialPrimitive share one implementation. Testing - 85 schema tests, 6 preflight validator tests, 4 cross-provider isolation tests, 5 HarnessPrimitive tests including dedup paths, 6 end-to-end integration tests against tmpdir with real ConfigIO, and a source-level wiring guard for useCreateFlow. - Verified end-to-end in AWS: OpenAI (us-east-1) and Gemini harnesses deploy, invoke, and return responses. M3/M4/M5/M7 error paths produce the expected messages.
| // Harness paths in agentcore.json are relative to the project root (parent of agentcore/). | ||
| const projectRoot = path.resolve(configRoot, '..'); | ||
| const harnessConfigs: { | ||
| interface HarnessRawConfig { |
There was a problem hiding this comment.
Why per-target credential resolution in cdk.ts
AgentCoreHarnessRole scopes its bedrock-agentcore:GetResourceApiKey policy to the specific ARN passed in via harness.apiKeyArn. Since our flow stores the credential by name (apiKeyCredential), we have to resolve that name → ARN from deployed state before CDK synth so the IAM statement is concrete.
That's why the harness config is built in two steps:
harnessRawConfigs— read once from eachharness.jsonon disk (pre-loop)harnessConfigs— resolved per-target inside thefor (const target of targets)loop, using that target'scredentialsmap from deployed state
Alternative we could have taken
AgentCoreRuntime skips per-credential resolution entirely and grants a wildcard policy via grantCredentialAccess():
actions: ['bedrock-agentcore:GetResourceApiKey', ...],
resources: [
`arn:aws:bedrock-agentcore:*:${account}:token-vault/*`,
`arn:aws:bedrock-agentcore:*:${account}:apikeycredentialprovider/*`,
],We could change AgentCoreHarnessRole to do the same. That would eliminate the resolution block in cdk.ts entirely — but it relaxes the harness role's IAM scoping from "this one credential" to "any credential in this account." Current approach preserves the tighter scoping the construct already had.
Description
The harness API key credential flow had multiple gaps blocking end-to-end deploy+invoke for OpenAI and Gemini harnesses. This PR fixes the credential lifecycle end-to-end: TUI/CLI collection, schema validation, deploy-time resolution, and the vended CDK's IAM policy wiring.
What was broken
apiKeyArntoAgentCoreHarnessRole, but our new flow stores the credential by name (apiKeyCredential). The role never got thebedrock-agentcore:GetResourceApiKeypolicy..env.localentries silently fell through to CDK synth; Secrets Manager ARNs inapiKeyArnwere rejected only at Zod time with no case/whitespace tolerance..env.localin an order that could leave a harness referencing a non-existent credential on partial failure.--api-key-arn; both--api-keyand--api-key-arncould be set at once with no error.What changed
src/assets/cdk/bin/cdk.ts) resolvesmodel.apiKeyCredentialto the token-vault provider ARN from deployed state per-target before passing toAgentCoreStack.CredentialPrimitive.resolveCredentialStrategyfilters reuse candidates byauthorizerType === 'ApiKeyCredentialProvider'ANDcred.name.endsWith(modelProvider), preventing cross-provider contamination.preflight.ts#validateHarnessCredentialReferences) rejects danglingapiKeyCredentialreferences and OAuth credentials used as API key credentials, beforepre-deploy-identityruns.HarnessPrimitive.add()rejects non-Bedrock provider without a credential source, rejects both--api-keyand--api-key-arnset together, and writes in rollback-safe order:.env.local→harness.json→agentcore.json(the commit point).apiKeyArn, enforces.min(1)onapiKeyCredential, rejects Secrets Manager ARNs case-insensitively, and fails non-Bedrock providers that ship with neither credential field.ApiKeySecretInputwhen rendered in the harness wizard; CLI guards surface--api-key-arnon the agent path and both-flags combinations atvalidatetime.computeCredentialNamefromschema-mapper.tsintocredential-utils.tsso the agent and harness flows share the same naming.Related Issue
Closes #
Type of Change
Testing
npm run test:unitandnpm run test:integnpm run typechecknpm run lintAutomated coverage
End-to-end AWS verification (account 603141041947)
Checklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.