feat: add managed tool catalog and build environments - #397
Conversation
# Conflicts: # lambda/v2-orchestrator/index.js
| environmentId: environment.environmentId, | ||
| name: environment.name, | ||
| revisionId: revision.revisionId, | ||
| imageDigest: revision.imageDigest, | ||
| runtimeVersion: revision.runtimeVersion ?? null, | ||
| runtimeArn: revision.runtimeArn, | ||
| runtimeEndpoint: revision.runtimeEndpoint ?? null, | ||
| compatibilityVersion, | ||
| verification: revision.verification ?? null, |
There was a problem hiding this comment.
| environmentId: environment.environmentId, | |
| name: environment.name, | |
| revisionId: revision.revisionId, | |
| imageDigest: revision.imageDigest, | |
| runtimeVersion: revision.runtimeVersion ?? null, | |
| runtimeArn: revision.runtimeArn, | |
| runtimeEndpoint: revision.runtimeEndpoint ?? null, | |
| compatibilityVersion, | |
| verification: revision.verification ?? null, | |
| environmentId: environment.environmentId, | |
| name: environment.name, | |
| revisionId: revision.revisionId, | |
| imageDigest: revision.imageDigest, | |
| runtimeVersion: revision.runtimeVersion ?? null, | |
| runtimeArn: revision.runtimeArn, | |
| runtimeEndpoint: revision.runtimeEndpoint ?? null, | |
| compatibilityVersion, | |
| verification: revision.verification ?? null, | |
| tools: revision.flattenedRecipe?.resolvedTools ?? [], |
as documented in https://github.com/aws-samples/sample-collaborative-ai-dlc/pull/397/changes#diff-51840c6001bebb21d56b029ca1ff58b642117adcdf734564433f7c6e8051cdabR86, we might want to also have the tools. Maybe add a test asserting those snapshots survive into the intent and audit output.
| Create a `.NET SDK` tool using an official Linux ARM64 SDK archive and the | ||
| `.NET` preset. Confirm source inspection, normalization, scanning, `dotnet | ||
| --version`, and a real console build succeed, then publish it. | ||
|
|
||
| Create catalog-backed environments based on Standard: | ||
|
|
||
| 1. Select Go and publish the resulting environment. | ||
| 2. Select Maven and confirm the recommended Java version is added | ||
| automatically. | ||
| 3. Select `.NET SDK` and publish the resulting environment. | ||
| 4. Confirm every generated Dockerfile copies tools from exact OCI digests and | ||
| retains the protected base entrypoint, command, user, port, and health | ||
| behavior. | ||
| 5. Confirm the projected and actual compressed image sizes stay below the | ||
| configured AgentCore image limit. |
There was a problem hiding this comment.
not clear, we mix languages, .NET seems important, I don't understand
There was a problem hiding this comment.
Quite verbose, without diagram, a bit painful to read. Can we imagine a TL;DR or the main instructions/options first and "going further" separated. Or the actionable options first and FYI later. Something to make it more digest.
| @@ -1,3 +1,3 @@ | |||
| # Projects and Settings | |||
|
|
|||
| A project is the workspace where intents run. It represents a product, service, or feature area and groups together: | |||
There was a problem hiding this comment.
By reading this, I see we still speak about "projects", not "spaces", created this issue for follow up: #409
| verification: fallback.verification ?? { status: 'PASSED', source: 'legacy-runtime' }, | ||
| }); | ||
|
|
||
| const supportsCompatibilityVersion = (candidate, current) => { |
There was a problem hiding this comment.
Duplication of isSupportedCompatibilityVersion in recipe.js:701
| throw new Error('deterministic runtime validation failed'); | ||
| } | ||
| } finally { | ||
| await runtimeClient.send( |
There was a problem hiding this comment.
No try catch of the StopRuntimeSessionCommand. A stop failure masks the real validation error; because the replacement error is retryable, a broken revision returns {pending:true} and is retried forever instead of being marked FAILED.
| imageBuild: 'PASSED', | ||
| baseDigest: 'PASSED', | ||
| architecture: 'arm64', | ||
| nonRoot: deterministic.nonRoot === true, |
There was a problem hiding this comment.
The deterministic check at lines 517–523 only fails verification when ok, nonce, or compatibilityVersion are wrong — it never looks at nonRoot. Yet at line 544 verification is hard-coded to status: 'PASSED', and line 549 records nonRoot: deterministic.nonRoot === true. Because ok is defined as workspaceWritable && protectedRuntime (in managed-runtime-check.js) and excludes nonRoot, a runtime running as root returns nonRoot: false but still passes the check at 517–523 → the revision goes READY with status: 'PASSED' and nonRoot: false recorded, which contradicts itself. Please either add nonRoot === true to the condition at 517–523, or stop recording a field verification doesn't gate on. (Low priority — non-root is already enforced at build time in recipe.js.)
| return response(405, { error: 'Method not allowed' }); | ||
| } catch { | ||
| console.error('intents handler error'); | ||
| return response(500, { error: 'Internal server error' }); |
There was a problem hiding this comment.
resolveEnvironmentSnapshot can throw different errors (ENVIRONMENT_NOT_PUBLISHED, ENVIRONMENT_COMPATIBILITY_UNSUPPORTED, ...) but the catch simply return an opaque 500, loosing the interesting information. Please bind the error and map these codes to a 409, the way this file already does at line 1283 for SOURCE_CONTROL_NOT_READY.
| } | ||
| } | ||
| })().finally(() => { | ||
| initialization = null; |
There was a problem hiding this comment.
initialize (lines 328...) memoizes its promise with ??=, but nulls it in .finally, which runs on success too. So the next request re-runs the whole block — seedSystemTools(), listVersionsByStatus('DRAFT'), listVersionsByStatus('FAILED') on every call; the cache only dedupes concurrent calls, never across requests. Compare lambda/environments/index.js:352–359, which nulls the cache in .catch (error only) so a successful init persists. Please switch to the .catch pattern here.
| sk: `REV#${revisionId}`, | ||
| }); | ||
|
|
||
| const readPublishedEnvironment = async (environmentId) => { |
There was a problem hiding this comment.
Two code paths gate the same project environment_id differently:
readPublishedEnvironment(here), used by the assignment PUT handler, accepts on status only — line 1002:['PUBLISHED', 'SUPERSEDED'].includes(revision.status). It never checks compatibility.resolveEnvironmentSnapshot(environment-snapshot.js:32–95), used at intent-create, additionally requiressupportsCompatibilityVersion(...)at lines 73–81.
So: publish a revision at compatibility version 1 → assign it to a project (passes) → platform advances RUNTIME_COMPATIBILITY_VERSION to 3 → intent-create now throws ENVIRONMENT_COMPATIBILITY_UNSUPPORTED. An environment that was accepted at assignment is rejected at run time. Please apply the same acceptancrules (ideally one shared resolver) in both places.
| new GetCommand({ | ||
| TableName: processTable, | ||
| Key: { pk: `EXEC#${intentId}`, sk: 'META' }, | ||
| ConsistentRead: true, |
There was a problem hiding this comment.
We might not need the ConsistentRead here. The execution snapshot is written once at setup and is immutable per execution, so eventual consistency is safe here.
There was a problem hiding this comment.
Why having 2 recipe.js (v1 and v2) ? Both are introduced in this PR so using v1 and v2 is a bit misleading and introduces some idea of legacy while there is not: 2 schemas, 2 validators, 2 Dockerfile/verification/build-context generators, 2 test suites...
If you want to distinguish system fixed allow-list from catalog-driven, maybe be more explicit in the naming than v1 and v2.
jeromevdl
left a comment
There was a problem hiding this comment.
This feature is quite complex and there is legitimately complexity in the PR to handle this, but the PR adds 20-30% incidental complexity on top of it because of duplicated subsystems:
- recipe v1 vs v2
- 3 different ways to evaluate if environment is ready:
readPublishedEnvironment,resolveEnvironmentSnapshot,publishedBase - one stack for environments, one for tools very close to the environment (createStore, createHandler, startBuild, createStatusHandler, ... in index/status/store vs tools-index/tools-status/tools-store, both modeled as: a versioned entity with DRAFT/BUILDING/READY/PUBLISHED-style revisions, built via CodeBuild, whose status is reconciled by a polling handler, fronted by a CRUD API handler backed by a DynamoDB store. That shared lifecycle is implemented twice. I've compared the shapes and signatures here, not diffed the two stacks line by line, so I can't put a precise "X% identical" number on it.
Can you check what could be merged (schema v1/v2 ? duplications of env and tools ? one environment resolution function ?) and maybe others to simplify / reduce the amount of code?
| @@ -129,11 +130,24 @@ const discussionSessionIdFor = (intentId, discussionId) => | |||
| `aidlc-discuss-${intentId}-${discussionId}`.padEnd(33, '0'); | |||
|
|
|||
| export const invokeDiscussionAssist = async ({ intentId, payload }) => { | |||
There was a problem hiding this comment.
This PR adds V2_PROCESS_TABLE to the discussions lambda, so invokeDiscussionAssist now does a DynamoDB Get where it previously had zero DB dependency — and if (!meta) throw. For a normal intent the META row always exists (created at DRAFT), so it's unreachable in practice, but a throttled or IAM-denied Get now throws where before it couldn't. New blast radius on a hot interactive path
|
Also, agents/index.js:79-80,107-108 (fetchRuntimeCapabilities, verifyMcpServers) still hit the core AGENTCORE_RUNTIME_ARN, never a project's managed environment. |
Summary
Tool ingestion
Publisher verifiedevidence fromPlatform pinnedimportsCompatibility
Verification
npm run format:checknpm run lintnpm run secretlintnpm run sdk:checknpm --prefix frontend run typechecknpm --prefix frontend run buildterraform fmt -recursive -check terraformTerraform was not planned or applied against AWS. The deployed-stack workflow and .NET catalog example are documented in
docs/development/testing.md.Closes #390