@aws-blocks/bb-agent 0.3.5 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +65 -17
- package/README.md +156 -8
- package/dist/agent.aws.d.ts +15 -1
- package/dist/agent.aws.d.ts.map +1 -1
- package/dist/agent.aws.js +49 -0
- package/dist/agent.d.ts +82 -14
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +238 -57
- package/dist/agentcore-bundle.d.ts +12 -0
- package/dist/agentcore-bundle.d.ts.map +1 -0
- package/dist/agentcore-bundle.js +150 -0
- package/dist/agentcore-bundle.test.d.ts +2 -0
- package/dist/agentcore-bundle.test.d.ts.map +1 -0
- package/dist/agentcore-bundle.test.js +46 -0
- package/dist/agentcore-entry.d.ts +21 -0
- package/dist/agentcore-entry.d.ts.map +1 -0
- package/dist/agentcore-entry.js +120 -0
- package/dist/agentcore-runtime.cdk.d.ts +27 -0
- package/dist/agentcore-runtime.cdk.d.ts.map +1 -0
- package/dist/agentcore-runtime.cdk.js +168 -0
- package/dist/index.aws.d.ts +1 -0
- package/dist/index.aws.d.ts.map +1 -1
- package/dist/index.cdk.d.ts +12 -6
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +31 -31
- package/dist/index.cdk.test.js +128 -51
- package/dist/index.hooks.d.ts +2 -2
- package/dist/index.hooks.d.ts.map +1 -1
- package/dist/index.mock.d.ts +1 -0
- package/dist/index.mock.d.ts.map +1 -1
- package/dist/index.test.js +426 -1
- package/dist/model-factory.d.ts +2 -2
- package/dist/model-factory.d.ts.map +1 -1
- package/dist/model-factory.js +2 -2
- package/dist/providers/canned.d.ts +8 -1
- package/dist/providers/canned.d.ts.map +1 -1
- package/dist/providers/canned.js +127 -42
- package/dist/types.d.ts +63 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +24 -9
- package/src/agent.aws.ts +58 -1
- package/src/agent.ts +269 -56
- package/src/agentcore-bundle.test.ts +52 -0
- package/src/agentcore-bundle.ts +162 -0
- package/src/agentcore-entry.ts +134 -0
- package/src/agentcore-runtime.cdk.ts +203 -0
- package/src/index.aws.ts +3 -0
- package/src/index.cdk.test.ts +145 -53
- package/src/index.cdk.ts +33 -34
- package/src/index.hooks.ts +2 -2
- package/src/index.mock.ts +3 -0
- package/src/index.test.ts +473 -1
- package/src/model-factory.ts +3 -3
- package/src/providers/canned.ts +131 -36
- package/src/types.ts +64 -1
- package/src/version.ts +1 -1
- package/dist/job-event-source.d.ts +0 -19
- package/dist/job-event-source.d.ts.map +0 -1
- package/dist/job-event-source.js +0 -20
- package/src/job-event-source.ts +0 -21
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serve a registered Agent on the AgentCore harness.
|
|
3
|
+
*
|
|
4
|
+
* The developer's backend must already have been imported in THIS process (so the Agent
|
|
5
|
+
* registered itself in the shared registry) — either by `main()` below (standalone launch
|
|
6
|
+
* via `BB_AGENT_BACKEND_MODULE`) or by the co-bundle (agentcore-bundle.ts) that imports the
|
|
7
|
+
* backend and this `serve` from the same bb-agent module instance. Co-bundling is required
|
|
8
|
+
* because the registry is a module singleton — a split would put the Agent in one map and
|
|
9
|
+
* the lookup in another.
|
|
10
|
+
*
|
|
11
|
+
* @param agentId - fullId of the target Agent (defaults to process.env.BB_AGENT_ID)
|
|
12
|
+
*/
|
|
13
|
+
export declare function serve(agentId?: string | undefined): void;
|
|
14
|
+
/**
|
|
15
|
+
* Standalone launch: load config, import the developer backend by path, then serve.
|
|
16
|
+
* Used when the artifact runs this file directly with BB_AGENT_BACKEND_MODULE pointing at the
|
|
17
|
+
* backend. When an app co-bundles the backend with `serve` (agentcore-bundle.ts), it calls
|
|
18
|
+
* `serve()` directly instead and this `main()` is not the entry.
|
|
19
|
+
*/
|
|
20
|
+
export declare function main(): Promise<void>;
|
|
21
|
+
//# sourceMappingURL=agentcore-entry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentcore-entry.d.ts","sourceRoot":"","sources":["../src/agentcore-entry.ts"],"names":[],"mappings":"AAkDA;;;;;;;;;;;GAWG;AACH,wBAAgB,KAAK,CAAC,OAAO,qBAA0B,GAAG,IAAI,CAyC7D;AAED;;;;;GAKG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAc1C"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* AgentCore Runtime entrypoint for the Agent BB.
|
|
5
|
+
*
|
|
6
|
+
* Hosts the developer's real Agent — the same instance the Lambda handler would build — on
|
|
7
|
+
* the `BedrockAgentCoreApp` harness (implements the `/invocations` + `/ping` contract on port
|
|
8
|
+
* 8080), and runs the agent loop as a **background async task** that streams chunks to the
|
|
9
|
+
* browser over the Realtime BB (exactly as the loop does on Lambda today).
|
|
10
|
+
*
|
|
11
|
+
* Why background + Realtime (not the harness's SSE response): the browser never holds a
|
|
12
|
+
* connection to AgentCore — it subscribes to a Realtime channel by `channelId`. So the
|
|
13
|
+
* invocation must return immediately while the loop keeps running server-side. The harness
|
|
14
|
+
* keeps the microVM alive (up to the 8h session lifetime) while an async task is in flight —
|
|
15
|
+
* `/ping` reports `HealthyBusy` — via `addAsyncTask()`/`completeAsyncTask()`. `runAgent()`
|
|
16
|
+
* publishes every chunk to Realtime under the runtime's execution role.
|
|
17
|
+
*
|
|
18
|
+
* How the developer's agent definition reaches this process:
|
|
19
|
+
* The `tools` callback in AgentConfig is a JS closure and can't be serialized across a
|
|
20
|
+
* process boundary. So instead of shipping data, we ship code: this entrypoint imports
|
|
21
|
+
* the SAME developer backend module the Lambda handler imports (co-bundled with
|
|
22
|
+
* `--conditions=aws-runtime`, so `new Agent()` resolves to the AWS runtime class). That
|
|
23
|
+
* construction registers the live Agent in the instance registry (see agent.ts); we look
|
|
24
|
+
* it up by the `BB_AGENT_ID` the CDK Runtime construct set, and drive its loop.
|
|
25
|
+
*
|
|
26
|
+
* Launched by the CodeZip artifact as: ['main.js'] (the co-bundle from agentcore-bundle.ts).
|
|
27
|
+
*/
|
|
28
|
+
import { loadConfigToProcessEnv } from '@aws-blocks/core';
|
|
29
|
+
import { BedrockAgentCoreApp } from 'bedrock-agentcore/runtime';
|
|
30
|
+
import { z } from 'zod';
|
|
31
|
+
import { getAgentInstance } from './agent.js';
|
|
32
|
+
/** Request contract — mirrors the Lambda jobPayloadSchema, minus transport-only fields. */
|
|
33
|
+
const requestSchema = z.object({
|
|
34
|
+
/** User prompt. Empty on resume (interruptResponses drive the turn instead). */
|
|
35
|
+
prompt: z.string().default(''),
|
|
36
|
+
/** Realtime channel the client subscribes to for this turn's chunks. */
|
|
37
|
+
channelId: z.string(),
|
|
38
|
+
/** Conversation to persist to / restore the session from. Falls back to the AgentCore session id. */
|
|
39
|
+
conversationId: z.string().optional(),
|
|
40
|
+
/** Owner of the conversation. Required when persistence is enabled (not inferenceOnly). */
|
|
41
|
+
userId: z.string().optional(),
|
|
42
|
+
/** HITL resume: approval responses to apply instead of a new prompt. */
|
|
43
|
+
interruptResponses: z.array(z.object({ interruptId: z.string(), response: z.string() })).optional(),
|
|
44
|
+
/** Per-call tool context, threaded through to tool handlers. Must be JSON-serializable. */
|
|
45
|
+
context: z.unknown().optional(),
|
|
46
|
+
});
|
|
47
|
+
/**
|
|
48
|
+
* Serve a registered Agent on the AgentCore harness.
|
|
49
|
+
*
|
|
50
|
+
* The developer's backend must already have been imported in THIS process (so the Agent
|
|
51
|
+
* registered itself in the shared registry) — either by `main()` below (standalone launch
|
|
52
|
+
* via `BB_AGENT_BACKEND_MODULE`) or by the co-bundle (agentcore-bundle.ts) that imports the
|
|
53
|
+
* backend and this `serve` from the same bb-agent module instance. Co-bundling is required
|
|
54
|
+
* because the registry is a module singleton — a split would put the Agent in one map and
|
|
55
|
+
* the lookup in another.
|
|
56
|
+
*
|
|
57
|
+
* @param agentId - fullId of the target Agent (defaults to process.env.BB_AGENT_ID)
|
|
58
|
+
*/
|
|
59
|
+
export function serve(agentId = process.env.BB_AGENT_ID) {
|
|
60
|
+
if (!agentId)
|
|
61
|
+
throw new Error('BB_AGENT_ID is required (fullId of the target Agent).');
|
|
62
|
+
const agent = getAgentInstance(agentId);
|
|
63
|
+
if (!agent) {
|
|
64
|
+
throw new Error(`No Agent registered with id '${agentId}'. Ensure the backend module constructs it at import time.`);
|
|
65
|
+
}
|
|
66
|
+
const app = new BedrockAgentCoreApp({
|
|
67
|
+
invocationHandler: {
|
|
68
|
+
requestSchema,
|
|
69
|
+
process: (request, context) => {
|
|
70
|
+
// AgentCore routes every invocation for a session to the same warm microVM.
|
|
71
|
+
// runtimeSessionId maps to the Agent BB's conversationId (session state key).
|
|
72
|
+
const conversationId = request.conversationId ?? context.sessionId;
|
|
73
|
+
// Run the turn as a background async task so this invocation returns immediately.
|
|
74
|
+
// The harness reports `/ping` = HealthyBusy while the task is in flight, keeping
|
|
75
|
+
// the microVM alive (up to the 8h session lifetime) until the loop completes.
|
|
76
|
+
// Chunks are delivered out-of-band via Realtime — this HTTP response is just an ack.
|
|
77
|
+
const taskId = app.addAsyncTask('agent-turn');
|
|
78
|
+
void agent
|
|
79
|
+
.invokeTurn({
|
|
80
|
+
message: request.prompt,
|
|
81
|
+
conversationId,
|
|
82
|
+
channelId: request.channelId,
|
|
83
|
+
userId: request.userId ?? 'anonymous',
|
|
84
|
+
interruptResponses: request.interruptResponses,
|
|
85
|
+
context: request.context,
|
|
86
|
+
})
|
|
87
|
+
.finally(() => app.completeAsyncTask(taskId));
|
|
88
|
+
// The client already has channelId (from the RPC that invoked us) and subscribes
|
|
89
|
+
// to Realtime; it does not consume this response body.
|
|
90
|
+
return { channelId: request.channelId, status: 'accepted' };
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
app.run();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Standalone launch: load config, import the developer backend by path, then serve.
|
|
98
|
+
* Used when the artifact runs this file directly with BB_AGENT_BACKEND_MODULE pointing at the
|
|
99
|
+
* backend. When an app co-bundles the backend with `serve` (agentcore-bundle.ts), it calls
|
|
100
|
+
* `serve()` directly instead and this `main()` is not the entry.
|
|
101
|
+
*/
|
|
102
|
+
export async function main() {
|
|
103
|
+
// Same cold-start contract as the Lambda handler: pull BB resource identifiers
|
|
104
|
+
// (table names, bucket names, Realtime callback URL) into process.env before importing
|
|
105
|
+
// the backend, so BB constructors can resolve them.
|
|
106
|
+
await loadConfigToProcessEnv();
|
|
107
|
+
const backendModule = process.env.BB_AGENT_BACKEND_MODULE;
|
|
108
|
+
if (!backendModule)
|
|
109
|
+
throw new Error('BB_AGENT_BACKEND_MODULE env var is required (path to the developer backend module).');
|
|
110
|
+
// Import the developer backend — constructing the real Agent, which registers itself.
|
|
111
|
+
await import(backendModule);
|
|
112
|
+
serve();
|
|
113
|
+
}
|
|
114
|
+
// Auto-run the standalone launch path ONLY when a backend module path is provided.
|
|
115
|
+
// The co-bundle path (agentcore-bundle.ts) imports `serve` and invokes it directly after
|
|
116
|
+
// importing the backend inline, and does NOT set BB_AGENT_BACKEND_MODULE — so `main()` must
|
|
117
|
+
// not fire there (it would double-serve and throw on the missing env var).
|
|
118
|
+
if (process.env.BB_AGENT_BACKEND_MODULE) {
|
|
119
|
+
void main();
|
|
120
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Runtime } from 'aws-cdk-lib/aws-bedrockagentcore';
|
|
2
|
+
import { Scope } from '@aws-blocks/core/cdk';
|
|
3
|
+
import type { ScopeParent } from '@aws-blocks/core';
|
|
4
|
+
/** References the agent loop needs, handed in by the Agent CDK constructor. */
|
|
5
|
+
export interface AgentCoreRuntimeProps {
|
|
6
|
+
/** fullId of the owning Agent — the container looks the Agent up by this (`BB_AGENT_ID`). */
|
|
7
|
+
agentFullId: string;
|
|
8
|
+
/**
|
|
9
|
+
* Pre-built asset dir to use instead of co-bundling at synth. Set by unit tests and apps
|
|
10
|
+
* that pre-bundle; when omitted, the backend module is co-bundled from the BlocksStack.
|
|
11
|
+
*/
|
|
12
|
+
agentcoreAssetPath?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare class AgentCoreRuntime extends Scope {
|
|
15
|
+
/** The provisioned runtime, or undefined when no backend asset could be resolved (isolated tests). */
|
|
16
|
+
readonly runtime?: Runtime;
|
|
17
|
+
/** The runtime ARN (empty string when not provisioned). */
|
|
18
|
+
readonly runtimeArn: string;
|
|
19
|
+
constructor(scope: ScopeParent, id: string, props: AgentCoreRuntimeProps);
|
|
20
|
+
/**
|
|
21
|
+
* Co-bundle the app backend + `serve()` into an AgentCore code-asset dir. Returns undefined
|
|
22
|
+
* when the backend module path can't be discovered (isolated unit tests) — the caller then
|
|
23
|
+
* skips provisioning rather than failing synth.
|
|
24
|
+
*/
|
|
25
|
+
private buildAsset;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=agentcore-runtime.cdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentcore-runtime.cdk.d.ts","sourceRoot":"","sources":["../src/agentcore-runtime.cdk.ts"],"names":[],"mappings":"AA4BA,OAAO,EAAqE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAE9H,OAAO,EAAqC,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAChF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,+EAA+E;AAC/E,MAAM,WAAW,qBAAqB;IACrC,6FAA6F;IAC7F,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,qBAAa,gBAAiB,SAAQ,KAAK;IAC1C,sGAAsG;IACtG,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAEhB,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB;IAuIxE;;;;OAIG;IACH,OAAO,CAAC,UAAU;CAWlB"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Self-contained CDK provisioning for the Agent BB's AgentCore Runtime.
|
|
5
|
+
*
|
|
6
|
+
* This class owns EVERYTHING AgentCore-specific: the synth-time co-bundle of the app backend,
|
|
7
|
+
* the `Runtime` construct (via `fromCodeAsset` — Node 22 CodeZip, no Docker), the shared role's
|
|
8
|
+
* AgentCore trust + grants, the container env injection, and the grant that lets the app's RPC
|
|
9
|
+
* handler invoke the runtime. It is deliberately kept in one place, with no AgentCore details
|
|
10
|
+
* leaking into the `Agent` CDK constructor or into core, so it can later fold into a per-BB
|
|
11
|
+
* compute abstraction (should one land) without touching call sites — the Agent just constructs
|
|
12
|
+
* it and hands over references to the BBs the loop uses.
|
|
13
|
+
*
|
|
14
|
+
* The loop runs INSIDE this runtime AS the shared Blocks execution role (the same role the Lambda
|
|
15
|
+
* handler runs as), so it inherits every Building Block's grants — including the Realtime publish
|
|
16
|
+
* permissions already granted to the handler, so it streams chunks to the browser via the Realtime
|
|
17
|
+
* BB with no extra grant. The container loads the full app config (via the injected config-bucket
|
|
18
|
+
* location) exactly as the handler does, so it discovers the Realtime callback URL and every other
|
|
19
|
+
* registerConfig() value a tool's BB may need. This class adds to that shared role only what's
|
|
20
|
+
* AgentCore-specific: the `bedrock-agentcore` assume-role trust, Bedrock model access, and the
|
|
21
|
+
* handler's `InvokeAgentRuntime` permission. Inbound auth is IAM (SigV4): the RPC handler
|
|
22
|
+
* invokes with its own credentials; the browser never talks to the runtime directly.
|
|
23
|
+
*/
|
|
24
|
+
import { mkdirSync } from 'node:fs';
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
import * as cdk from 'aws-cdk-lib';
|
|
27
|
+
import { AgentCoreRuntime as AgentCoreRuntimeVersion, AgentRuntimeArtifact, Runtime } from 'aws-cdk-lib/aws-bedrockagentcore';
|
|
28
|
+
import { Effect, PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
|
|
29
|
+
import { getConfigLocation, registerConfig, Scope } from '@aws-blocks/core/cdk';
|
|
30
|
+
import { bundleAgentCoreAsset } from './agentcore-bundle.js';
|
|
31
|
+
export class AgentCoreRuntime extends Scope {
|
|
32
|
+
/** The provisioned runtime, or undefined when no backend asset could be resolved (isolated tests). */
|
|
33
|
+
runtime;
|
|
34
|
+
/** The runtime ARN (empty string when not provisioned). */
|
|
35
|
+
runtimeArn;
|
|
36
|
+
constructor(scope, id, props) {
|
|
37
|
+
super(id, { parent: scope });
|
|
38
|
+
const assetPath = props.agentcoreAssetPath ?? this.buildAsset();
|
|
39
|
+
if (!assetPath) {
|
|
40
|
+
// No backend module discoverable (e.g. an isolated unit test that constructs the
|
|
41
|
+
// Agent without a BlocksStack) — skip provisioning rather than failing synth.
|
|
42
|
+
this.runtimeArn = '';
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const stack = cdk.Stack.of(this);
|
|
46
|
+
// Run the loop AS the shared Blocks execution role (`role: this.executionRole`) — the same
|
|
47
|
+
// role the Lambda handler runs as. Because every Building Block grants its runtime permissions
|
|
48
|
+
// to this role (a tool that uses KVStore/tables/etc. grants there too), the AgentCore
|
|
49
|
+
// container inherits them all automatically — no bespoke, under-granted role, and no need to
|
|
50
|
+
// mirror each BB's grants. One shared role that any compute can assume keeps this a drop-in
|
|
51
|
+
// for a future per-BB compute abstraction.
|
|
52
|
+
const role = this.executionRole;
|
|
53
|
+
// Add the agent's shared-role trust + grants ONCE per stack. Every agent instance would
|
|
54
|
+
// otherwise add the SAME trust / Bedrock / InvokeAgentRuntime statements to the ONE shared
|
|
55
|
+
// role; with several agents in an app that piles up duplicates and overflows the IAM inline-
|
|
56
|
+
// policy size limit (CDK then spills into `OverflowPolicy` managed policies and the role
|
|
57
|
+
// misbehaves). These are identical for every agent — Bedrock models and the runtime-ARN
|
|
58
|
+
// wildcard are stack-scoped — so doing it once covers every agent's container. (Realtime
|
|
59
|
+
// publish, the session bucket, and the conversation/message tables are already granted to the
|
|
60
|
+
// shared role by the Realtime BB's handler wiring and the Agent's FileBucket/DistributedTable
|
|
61
|
+
// children, so they're not repeated here — the loop inherits them by running as the role.)
|
|
62
|
+
const SHARED_GRANTS_KEY = Symbol.for('BLOCKS_AGENT_RUNTIME_SHARED_ROLE_GRANTS');
|
|
63
|
+
const stackAny = stack;
|
|
64
|
+
if (!stackAny[SHARED_GRANTS_KEY]) {
|
|
65
|
+
// The container publishes to Realtime AS this shared role, which already holds the publish
|
|
66
|
+
// grants (the Realtime BB grants postToConnection + the connections table to the handler on
|
|
67
|
+
// the same role) — so no Realtime IAM grant is needed here; it's inherited by running as the role.
|
|
68
|
+
// Trust: let the AgentCore Runtime assume this shared role (it runs AS the role). Added here
|
|
69
|
+
// rather than in core, so the role only trusts `bedrock-agentcore` when an Agent exists.
|
|
70
|
+
// Scope it to this account/region with aws:SourceAccount + aws:SourceArn — AWS's recommended
|
|
71
|
+
// AgentCore Runtime trust policy — so only AgentCore runtimes in THIS account can assume the
|
|
72
|
+
// role (tightens the confused-deputy surface) without breaking assumption. `assumeRolePolicy`
|
|
73
|
+
// exists only on the concrete `Role`; core always creates BlocksRole concretely, so narrow
|
|
74
|
+
// and fail loud if that ever changes.
|
|
75
|
+
if (!(role instanceof Role)) {
|
|
76
|
+
throw new Error('AgentCore Runtime requires the shared Blocks execution role to be a concrete iam.Role to add its assume-role trust');
|
|
77
|
+
}
|
|
78
|
+
role.assumeRolePolicy?.addStatements(new PolicyStatement({
|
|
79
|
+
effect: Effect.ALLOW,
|
|
80
|
+
principals: [new ServicePrincipal('bedrock-agentcore.amazonaws.com')],
|
|
81
|
+
actions: ['sts:AssumeRole'],
|
|
82
|
+
conditions: {
|
|
83
|
+
StringEquals: { 'aws:SourceAccount': stack.account },
|
|
84
|
+
ArnLike: { 'aws:SourceArn': `arn:${stack.partition}:bedrock-agentcore:${stack.region}:${stack.account}:*` },
|
|
85
|
+
},
|
|
86
|
+
}));
|
|
87
|
+
// Bedrock model access for the loop (no Building Block grants this, and the loop no longer
|
|
88
|
+
// runs on the handler).
|
|
89
|
+
role.addToPrincipalPolicy(new PolicyStatement({
|
|
90
|
+
actions: [
|
|
91
|
+
'bedrock:InvokeModel',
|
|
92
|
+
'bedrock:InvokeModelWithResponseStream',
|
|
93
|
+
'bedrock:GetFoundationModel',
|
|
94
|
+
'bedrock:ListFoundationModels',
|
|
95
|
+
'bedrock:GetInferenceProfile',
|
|
96
|
+
],
|
|
97
|
+
resources: [`arn:${stack.partition}:bedrock:*::foundation-model/*`, `arn:${stack.partition}:bedrock:*:*:inference-profile/*`],
|
|
98
|
+
}));
|
|
99
|
+
// Let the app's RPC handler (also the shared role) invoke the runtimes — stream()/resume()
|
|
100
|
+
// call InvokeAgentRuntime. Scope to a wildcard runtime ARN rather than a specific runtime's
|
|
101
|
+
// ARN: the runtime uses this same shared role as its executionRole, so referencing its ARN
|
|
102
|
+
// here would create a Role→Runtime→Role dependency cycle. (Same wildcard style as Bedrock.)
|
|
103
|
+
role.addToPrincipalPolicy(new PolicyStatement({
|
|
104
|
+
actions: ['bedrock-agentcore:InvokeAgentRuntime'],
|
|
105
|
+
resources: [
|
|
106
|
+
`arn:${stack.partition}:bedrock-agentcore:${stack.region}:${stack.account}:runtime/*`,
|
|
107
|
+
`arn:${stack.partition}:bedrock-agentcore:${stack.region}:${stack.account}:runtime/*/*`,
|
|
108
|
+
],
|
|
109
|
+
}));
|
|
110
|
+
stackAny[SHARED_GRANTS_KEY] = true;
|
|
111
|
+
}
|
|
112
|
+
// Inject the config location so the container loads the FULL app config via
|
|
113
|
+
// loadConfigToProcessEnv() — the same config the Lambda handler loads. That's how it gets
|
|
114
|
+
// BLOCKS_RT_CALLBACK_URL (registered by the Realtime BB) plus any other config-backed BB value
|
|
115
|
+
// an agent's tools touch; without it the container runs with empty config and those BBs fail.
|
|
116
|
+
// Idempotent (one config bucket per stack); IAM to read it is inherited via the shared role.
|
|
117
|
+
const { bucketName: configBucketName, key: configKey } = getConfigLocation(this);
|
|
118
|
+
const runtime = new Runtime(this, 'AgentRuntime', {
|
|
119
|
+
agentRuntimeArtifact: AgentRuntimeArtifact.fromCodeAsset({
|
|
120
|
+
path: assetPath,
|
|
121
|
+
runtime: AgentCoreRuntimeVersion.NODE_22,
|
|
122
|
+
// Launch command, NOT a Lambda file.export handler. Single element = the .js file;
|
|
123
|
+
// the NODE_22 runtime invokes `node` itself. (A leading 'node' element is rejected.)
|
|
124
|
+
entrypoint: ['main.js'],
|
|
125
|
+
}),
|
|
126
|
+
executionRole: role,
|
|
127
|
+
// Inbound auth defaults to IAM (SigV4): the RPC handler invokes with its own creds.
|
|
128
|
+
// The browser never invokes the runtime directly (it subscribes to Realtime).
|
|
129
|
+
environmentVariables: {
|
|
130
|
+
// The Agent's fullId so the container's getAgentInstance(BB_AGENT_ID) matches the
|
|
131
|
+
// Agent the co-bundled backend registers at import.
|
|
132
|
+
BB_AGENT_ID: props.agentFullId,
|
|
133
|
+
// The namespace the container rebuilds fullId (and every derived resource name) from.
|
|
134
|
+
// MUST be the owning stack/backend's canonical root id — the SAME value the Lambda
|
|
135
|
+
// handler and the Lambda compute use (`backendStackName`) — not the raw CFN stack name.
|
|
136
|
+
// They coincide for a top-level BlocksStack, but for a BlocksBackend embedded in a
|
|
137
|
+
// customer stack the handler uses the backend fullId while cdk.Stack.of(this).stackName
|
|
138
|
+
// is the customer stack name; using the latter would make the container derive names from
|
|
139
|
+
// the wrong namespace and miss its own tables/bucket/config.
|
|
140
|
+
BLOCKS_STACK_NAME: this.backendStackName,
|
|
141
|
+
// Config location so the container's loadConfigToProcessEnv() loads the full app config
|
|
142
|
+
// (same as the handler) — this delivers BLOCKS_RT_CALLBACK_URL and every other
|
|
143
|
+
// registerConfig() value a tool's BB may read. IAM to read it is inherited (shared role).
|
|
144
|
+
BLOCKS_CONFIG_BUCKET: configBucketName,
|
|
145
|
+
BLOCKS_CONFIG_KEY: configKey,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
this.runtime = runtime;
|
|
149
|
+
this.runtimeArn = runtime.agentRuntimeArn;
|
|
150
|
+
// Expose THIS agent's runtime ARN to the Lambda runtime path so stream() can resolve it at
|
|
151
|
+
// call time. Per-agent (each agent has its own runtime), so it stays outside the shared guard.
|
|
152
|
+
registerConfig(this, `BB_AGENT_${props.agentFullId}_RUNTIME_ARN`, runtime.agentRuntimeArn);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Co-bundle the app backend + `serve()` into an AgentCore code-asset dir. Returns undefined
|
|
156
|
+
* when the backend module path can't be discovered (isolated unit tests) — the caller then
|
|
157
|
+
* skips provisioning rather than failing synth.
|
|
158
|
+
*/
|
|
159
|
+
buildAsset() {
|
|
160
|
+
const stack = globalThis.CURRENT_BLOCKS_STACK;
|
|
161
|
+
const backendModulePath = stack?.backendModulePath;
|
|
162
|
+
if (!backendModulePath)
|
|
163
|
+
return undefined;
|
|
164
|
+
const outDir = join(cdk.App.of(this)?.outdir ?? cdk.Stack.of(this).node.tryGetContext('cdk.out') ?? '.cdk-agentcore', `agentcore-${this.fullId}`);
|
|
165
|
+
mkdirSync(outDir, { recursive: true });
|
|
166
|
+
return bundleAgentCoreAsset(backendModulePath, outDir);
|
|
167
|
+
}
|
|
168
|
+
}
|
package/dist/index.aws.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { Agent } from './agent.aws.js';
|
|
2
|
+
export type { AgentTurnPayload } from './agent.js';
|
|
2
3
|
export { AgentErrors, InterruptError } from './errors.js';
|
|
3
4
|
export { BedrockModels, OllamaModels } from './models.js';
|
|
4
5
|
export type { AgentConfig, AgentResult, AgentStreamChunk, AgentStreamResult, ToolDefinition, AgentTool, ToolFactory, ToolsConfig, ToolHandlerArgs, DefaultToolContext, InterruptResponse, ToolCallRecord, ModelConfig, StreamOptions, Message, Conversation, JSONValue, TokenUsage } from './types.js';
|
package/dist/index.aws.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.aws.d.ts","sourceRoot":"","sources":["../src/index.aws.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.aws.d.ts","sourceRoot":"","sources":["../src/index.aws.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvC,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,cAAc,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.cdk.d.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { BuildingBlockScope } from '@aws-blocks/core/cdk';
|
|
2
2
|
import type { ScopeParent } from '@aws-blocks/core';
|
|
3
|
+
import type { AgentConfig } from './types.js';
|
|
3
4
|
export { AgentErrors } from './errors.js';
|
|
4
5
|
export { BedrockModels, OllamaModels } from './models.js';
|
|
5
|
-
export declare class Agent extends
|
|
6
|
+
export declare class Agent extends BuildingBlockScope {
|
|
6
7
|
/**
|
|
7
8
|
* CDK layer for the Agent BB.
|
|
8
|
-
* Mirrors the runtime's BB creation so CDK discovers and provisions all resources.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
10
|
+
* Provisions the session FileBucket, the conversation + message DistributedTables, the
|
|
11
|
+
* Realtime BB used to stream chunks to the browser, and the AgentCore Runtime that hosts
|
|
12
|
+
* the streaming agent loop. All AgentCore-specific provisioning (co-bundle, runtime role
|
|
13
|
+
* and its grants, container env, and the handler's invoke permission) lives in the
|
|
14
|
+
* self-contained {@link AgentCoreRuntime} so it can later fold into a per-BB compute abstraction.
|
|
15
|
+
*
|
|
16
|
+
* The loop runs inside the AgentCore Runtime (not the shared handler Lambda), so the shared
|
|
17
|
+
* handler no longer needs Bedrock access — the runtime's own role gets it (see AgentCoreRuntime).
|
|
12
18
|
*/
|
|
13
|
-
constructor(scope: ScopeParent, id: string, config?:
|
|
19
|
+
constructor(scope: ScopeParent, id: string, config?: AgentConfig);
|
|
14
20
|
}
|
|
15
21
|
//# sourceMappingURL=index.cdk.d.ts.map
|
package/dist/index.cdk.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAOpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE1D,qBAAa,KAAM,SAAQ,kBAAkB;IAC5C;;;;;;;;;;;OAWG;gBACS,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW;CAyChE"}
|
package/dist/index.cdk.js
CHANGED
|
@@ -1,43 +1,38 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
import {
|
|
4
|
-
import { Scope } from '@aws-blocks/core/cdk';
|
|
3
|
+
import { BuildingBlockScope } from '@aws-blocks/core/cdk';
|
|
5
4
|
import { DistributedTable } from '@aws-blocks/bb-distributed-table';
|
|
6
5
|
import { Realtime } from '@aws-blocks/bb-realtime';
|
|
7
|
-
import { AsyncJob } from '@aws-blocks/bb-async-job';
|
|
8
6
|
import { FileBucket } from '@aws-blocks/bb-file-bucket';
|
|
7
|
+
import * as ec2 from 'aws-cdk-lib/aws-ec2';
|
|
8
|
+
import { AgentCoreRuntime } from './agentcore-runtime.cdk.js';
|
|
9
9
|
import { messageSchema, conversationSchema, agentStreamChunkSchema } from './schemas.js';
|
|
10
|
-
import { INTERACTIVE_JOB_EVENT_SOURCE } from './job-event-source.js';
|
|
11
|
-
import { z } from 'zod';
|
|
12
10
|
export { AgentErrors } from './errors.js';
|
|
13
11
|
export { BedrockModels, OllamaModels } from './models.js';
|
|
14
|
-
|
|
15
|
-
message: z.string(),
|
|
16
|
-
conversationId: z.string().optional(),
|
|
17
|
-
});
|
|
18
|
-
export class Agent extends Scope {
|
|
12
|
+
export class Agent extends BuildingBlockScope {
|
|
19
13
|
/**
|
|
20
14
|
* CDK layer for the Agent BB.
|
|
21
|
-
* Mirrors the runtime's BB creation so CDK discovers and provisions all resources.
|
|
22
15
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
16
|
+
* Provisions the session FileBucket, the conversation + message DistributedTables, the
|
|
17
|
+
* Realtime BB used to stream chunks to the browser, and the AgentCore Runtime that hosts
|
|
18
|
+
* the streaming agent loop. All AgentCore-specific provisioning (co-bundle, runtime role
|
|
19
|
+
* and its grants, container env, and the handler's invoke permission) lives in the
|
|
20
|
+
* self-contained {@link AgentCoreRuntime} so it can later fold into a per-BB compute abstraction.
|
|
21
|
+
*
|
|
22
|
+
* The loop runs inside the AgentCore Runtime (not the shared handler Lambda), so the shared
|
|
23
|
+
* handler no longer needs Bedrock access — the runtime's own role gets it (see AgentCoreRuntime).
|
|
25
24
|
*/
|
|
26
25
|
constructor(scope, id, config) {
|
|
27
|
-
super(id, { parent: scope });
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}));
|
|
35
|
-
// Propagate `removalPolicy` to the sessions bucket so customers can
|
|
36
|
-
// opt sandbox stacks into clean teardown. Without it, CDK's RETAIN
|
|
37
|
-
// default applies (production-safe) and `cdk destroy` will fail on
|
|
38
|
-
// a non-empty bucket — same pattern as FileBucket / KnowledgeBase.
|
|
39
|
-
// ID shortened to keep S3 bucket names within the 63-char limit
|
|
26
|
+
super(id, { parent: scope, vpc: { interfaceEndpoints: [ec2.InterfaceVpcEndpointAwsService.BEDROCK_RUNTIME] } });
|
|
27
|
+
// Session-snapshot bucket. Provisioned here (and granted to the shared execution role that the
|
|
28
|
+
// AgentCore Runtime runs as); the deployed loop re-derives its name from this bucket's `fullId`
|
|
29
|
+
// in-process — the same `'sn'` id → same fullId → same physical bucket — so no name needs to be
|
|
30
|
+
// injected into the container. Propagate `removalPolicy` so customers can opt sandbox stacks into
|
|
31
|
+
// clean teardown (without it, CDK's RETAIN default applies). ID shortened to keep the S3 bucket
|
|
32
|
+
// name within the 63-char limit.
|
|
40
33
|
new FileBucket(this, 'sn', { removalPolicy: config?.removalPolicy });
|
|
34
|
+
// Conversation metadata + message history. These grant read/write to the shared execution
|
|
35
|
+
// role, which the AgentCore Runtime then runs as — so the loop can persist history.
|
|
41
36
|
if (!config?.inferenceOnly) {
|
|
42
37
|
new DistributedTable(this, 'convos', {
|
|
43
38
|
schema: conversationSchema,
|
|
@@ -50,13 +45,18 @@ export class Agent extends Scope {
|
|
|
50
45
|
}
|
|
51
46
|
new Realtime(this, 'rt', {
|
|
52
47
|
namespaces: {
|
|
53
|
-
chunks:
|
|
48
|
+
chunks: Realtime.namespace(agentStreamChunkSchema),
|
|
54
49
|
},
|
|
55
50
|
});
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
// The agent loop runs on the AgentCore Runtime (as the shared Blocks execution role) and
|
|
52
|
+
// streams to the browser over Realtime. AgentCoreRuntime co-bundles the app backend,
|
|
53
|
+
// provisions the runtime, adds the bedrock-agentcore trust + Bedrock to the shared role, and
|
|
54
|
+
// injects the config location so the container loads the same app config as the handler
|
|
55
|
+
// (the session bucket/tables and Realtime publish are already granted to that role). Kept
|
|
56
|
+
// self-contained so it can later fold into a per-BB compute abstraction.
|
|
57
|
+
new AgentCoreRuntime(this, 'runtime', {
|
|
58
|
+
agentFullId: this.fullId,
|
|
59
|
+
agentcoreAssetPath: config?.agentcoreAssetPath,
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
}
|