@aws-blocks/bb-agent 0.3.4 → 0.4.0
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 -16
- package/README.md +73 -6
- 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 -55
- 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 +10 -4
- package/dist/index.cdk.d.ts.map +1 -1
- package/dist/index.cdk.js +27 -26
- package/dist/index.cdk.test.d.ts +2 -0
- package/dist/index.cdk.test.d.ts.map +1 -0
- package/dist/index.cdk.test.js +143 -0
- package/dist/index.mock.d.ts +1 -0
- package/dist/index.mock.d.ts.map +1 -1
- package/dist/index.test.js +404 -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 +16 -9
- package/src/agent.aws.ts +58 -1
- package/src/agent.ts +269 -54
- 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 +167 -0
- package/src/index.cdk.ts +29 -29
- package/src/index.mock.ts +3 -0
- package/src/index.test.ts +449 -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/DESIGN.md
CHANGED
|
@@ -3,47 +3,95 @@
|
|
|
3
3
|
Design document for the Agent Building Block. For usage, see [README.md](./README.md).
|
|
4
4
|
|
|
5
5
|
**Package:** `@aws-blocks/bb-agent`
|
|
6
|
-
**Type:** Composite (uses DistributedTable, Realtime,
|
|
7
|
-
**AWS Services:** Bedrock, DynamoDB, S3,
|
|
6
|
+
**Type:** Composite (uses DistributedTable, Realtime, FileBucket internally) + an AgentCore Runtime
|
|
7
|
+
**AWS Services:** Bedrock AgentCore Runtime, Bedrock, DynamoDB, S3, API Gateway (WebSocket)
|
|
8
8
|
**Agent Framework:** [Strands Agents SDK](https://strandsagents.com/)
|
|
9
9
|
|
|
10
10
|
## Architecture
|
|
11
11
|
|
|
12
|
-
The
|
|
12
|
+
The streaming agent loop runs on a **Bedrock AgentCore Runtime** (sessions up to 8h, warm,
|
|
13
|
+
managed) — not the shared Blocks request handler — and streams chunks to the browser over the
|
|
14
|
+
**Realtime** BB. The Agent BB composes these internal BBs plus the runtime:
|
|
13
15
|
|
|
14
|
-
| Internal BB | Purpose | Created when |
|
|
16
|
+
| Internal BB / resource | Purpose | Created when |
|
|
15
17
|
|-------------|---------|-------------|
|
|
16
18
|
| **FileBucket** | Session persistence (Strands SessionManager) | Always |
|
|
17
19
|
| **DistributedTable** | Frontend message history | `inferenceOnly: false` |
|
|
18
20
|
| **Realtime** | Streaming chunks to caller | Always |
|
|
19
|
-
| **
|
|
21
|
+
| **AgentCore Runtime** | Hosts the streaming loop (co-bundled backend + `serve()`) | Always (AWS) |
|
|
20
22
|
|
|
21
23
|
```
|
|
22
|
-
stream()
|
|
24
|
+
stream()/resume() → InvokeAgentRuntime (returns immediately)
|
|
23
25
|
↓
|
|
24
|
-
|
|
26
|
+
AgentCore Runtime container (agentcore-entry.ts): starts the turn as a BACKGROUND
|
|
27
|
+
async task and returns an ack immediately
|
|
25
28
|
↓
|
|
26
|
-
runAgent() → Strands agent loop → publishes chunks to Realtime
|
|
29
|
+
runAgent() → Strands agent loop → publishes chunks to Realtime (as the shared execution role)
|
|
27
30
|
→ persists messages to DistributedTable
|
|
28
31
|
→ SessionManager saves state to FileBucket
|
|
29
32
|
```
|
|
30
33
|
|
|
34
|
+
The RPC handler (`stream`/`resume`) only kicks off the turn; it does not hold the connection —
|
|
35
|
+
running the loop as a background task lets `InvokeAgentRuntime` return in seconds. The microVM then
|
|
36
|
+
stays alive on AgentCore's own terms: AgentCore polls the container's health endpoint, and the SDK
|
|
37
|
+
reports `HealthyBusy` while a background task is in flight, so AgentCore keeps the runtime running
|
|
38
|
+
(up to the 8h max session) and returns it to `Healthy` — eligible for reclaim — once the task
|
|
39
|
+
completes. The browser subscribes to the Realtime channel by `channelId` and receives chunks as the
|
|
40
|
+
loop runs, so a turn is bounded by the AgentCore session (8h), not by the request handler's
|
|
41
|
+
per-invocation limit or API Gateway's ~29s cap.
|
|
42
|
+
|
|
43
|
+
**Compute model.** All AgentCore provisioning is kept self-contained in `AgentCoreRuntime`
|
|
44
|
+
(`agentcore-runtime.cdk.ts`) — the co-bundle, the `Runtime`, the shared role's AgentCore trust +
|
|
45
|
+
grants, the container env, and the handler's invoke permission — so it can later fold into a per-BB
|
|
46
|
+
compute abstraction (should one land) without touching call sites.
|
|
47
|
+
|
|
31
48
|
## Session Persistence
|
|
32
49
|
|
|
33
50
|
Two storage backends, same FileBucket BB:
|
|
34
51
|
- **AWS:** Strands' native `S3Storage` → FileBucket-provisioned S3 bucket
|
|
35
52
|
- **Local:** Custom `FileBucketSnapshotStorage` → FileBucket mock (mirrors S3Storage key layout exactly)
|
|
36
53
|
|
|
54
|
+
## Runaway-protection caps
|
|
55
|
+
|
|
56
|
+
`AgentConfig.maxLlmCalls` / `maxToolIterations` (default 20, `false` disables) bound runaway cost. They're enforced in `runAgent` by counting Strands' `BeforeModelCallEvent` / `BeforeToolCallEvent` hooks and calling `agent.cancel()` once a cap is exceeded; cancellation ends the stream normally (`stopReason: 'cancelled'`), which `runAgent` surfaces as an `error` chunk and then skips the final persist + `done`.
|
|
57
|
+
|
|
58
|
+
**Scope is the whole logical turn, including across HITL resumes.** The counters are stored in the Strands agent's `appState` (keys `__bbAgentModelCallCount` / `__bbAgentToolCallCount`), which the `SessionManager` persists with the session snapshot — the same mechanism the `trusted:<tool>` flags use. A turn that pauses on an interrupt and continues via `resume()` therefore keeps counting on its existing budget. Locals in `runAgent` would reset on every resume, letting an auto-approving or trusted-tool resume loop re-enter itself indefinitely — exactly the runaway these caps exist to stop.
|
|
59
|
+
|
|
60
|
+
**The reset is lazy, and it has to be.** The `SessionManager` restores the snapshot's `appState` *during* `stream()`, i.e. after `runAgent` has already set up its hooks — so zeroing the counters up front is silently overwritten by the previous turn's values and the budget leaks from turn to turn (a second message on the same conversation would start at the first turn's count and trip immediately). Instead, a fresh turn mints a `turnId` and the first cap hook to fire notices the stored `__bbAgentCapTurnId` is stale, zeroes the counters, and claims the turn; a resume mints no id, so it continues on the restored counts. Strands has no per-turn identifier to reuse here — `invocationState` is a caller-supplied bag, and a per-invocation id would change on every `resume()`, which is the opposite of what's needed.
|
|
61
|
+
|
|
62
|
+
A tool call cancelled by the tool cap still gets an `AfterToolCallEvent` (Strands reports the cancellation as the call's result), so the `tool-call` row already written to the message table keeps its `tool-result` partner — no dangling `tool_use` is left for the next turn to replay. `runAgent` additionally writes an `assistant` row carrying the stop reason in `metadata.error`, so a reloaded conversation explains why it ended.
|
|
63
|
+
|
|
37
64
|
## Infrastructure (CDK)
|
|
38
65
|
|
|
39
|
-
The CDK class
|
|
40
|
-
- **
|
|
41
|
-
- **
|
|
42
|
-
- **DistributedTable:** `${id}-messages` — conversation history (only when `inferenceOnly: false`)
|
|
66
|
+
The CDK class provisions:
|
|
67
|
+
- **FileBucket:** `${id}-sn` — session snapshot storage
|
|
68
|
+
- **DistributedTable:** `${id}-convos` / `${id}-messages` — conversation metadata + history (only when `inferenceOnly: false`)
|
|
43
69
|
- **Realtime:** `${id}-rt` — streaming namespace `chunks`
|
|
44
|
-
- **
|
|
45
|
-
|
|
46
|
-
|
|
70
|
+
- **AgentCore Runtime** (`${id}-runtime`, via `AgentCoreRuntime`) — the co-bundled backend + `serve()`
|
|
71
|
+
harness. It runs **as the shared Blocks execution role** (`Scope.executionRole` / `BlocksRole`) —
|
|
72
|
+
the same role the Lambda handler runs as — so it **inherits every Building Block's grants**, including
|
|
73
|
+
**Realtime publish** (`execute-api:ManageConnections` + the connections table, granted to the handler
|
|
74
|
+
by the `${id}-rt` Realtime child), other BBs an agent's *tools* touch (KVStore, tables, etc.), and
|
|
75
|
+
this agent's own session bucket (S3) and conversation/message tables (DynamoDB). `AgentCoreRuntime`
|
|
76
|
+
adds to that shared role only what's AgentCore-specific and not already carried:
|
|
77
|
+
- **Trust:** the `bedrock-agentcore.amazonaws.com` assume-role statement, so the runtime can assume
|
|
78
|
+
the shared role. Scoped by `aws:SourceAccount` + `aws:SourceArn` (AWS's recommended AgentCore trust
|
|
79
|
+
policy). Added here — not in core — so a Realtime-only app never trusts AgentCore.
|
|
80
|
+
- **Bedrock:** `InvokeModel` + `InvokeModelWithResponseStream` on all foundation models and inference profiles
|
|
81
|
+
- **Handler grant:** the shared role is granted `bedrock-agentcore:InvokeAgentRuntime` (wildcard runtime
|
|
82
|
+
ARN, to avoid a role↔runtime dependency cycle) so the RPC handler can start the loop.
|
|
83
|
+
|
|
84
|
+
The container gets four environment variables: `BB_AGENT_ID`, `BLOCKS_STACK_NAME`, and the config
|
|
85
|
+
location `BLOCKS_CONFIG_BUCKET` / `BLOCKS_CONFIG_KEY`. `BB_AGENT_ID` + `BLOCKS_STACK_NAME` let the
|
|
86
|
+
co-bundled backend re-derive its resource names in-process (session bucket, conversation/message
|
|
87
|
+
tables) via the SDK-identifier registry — the same derivation the handler uses — so those names aren't
|
|
88
|
+
injected. `BLOCKS_CONFIG_BUCKET`/`BLOCKS_CONFIG_KEY` point the container at the shared config blob
|
|
89
|
+
(from core's `getConfigLocation()`); `loadConfigToProcessEnv()` loads the **same full app config the
|
|
90
|
+
handler does**, which is how the loop gets `BLOCKS_RT_CALLBACK_URL` (registered by the Realtime BB) and
|
|
91
|
+
every other `registerConfig()` value a tool's Building Block may read. IAM to read the blob is inherited
|
|
92
|
+
from the shared execution role.
|
|
93
|
+
|
|
94
|
+
> **Note:** The runtime (`agent.ts`) and CDK (`index.cdk.ts`) layers both create the internal BBs on the Agent scope (`this`) with the **same child ids** (`sn`, `convos`, `messages`, `rt`). Same id → same `fullId` → same derived physical name, so the deployed loop resolves the exact resources CDK provisioned.
|
|
47
95
|
|
|
48
96
|
## Model Providers
|
|
49
97
|
|
|
@@ -61,6 +109,7 @@ Custom Strands model provider for local development. No network, no API keys, no
|
|
|
61
109
|
|
|
62
110
|
- Returns instant keyword-based responses (e.g., prompt contains "weather" → weather response, otherwise a default canned response)
|
|
63
111
|
- Streams word by word, matching the same `ModelStreamEvent` protocol as Bedrock/OpenAI
|
|
64
|
-
- Triggers tool calls when the prompt mentions a tool name — splits camelCase names into words (e.g., "weather" matches `getWeather`) and emits Strands `toolUse` events
|
|
112
|
+
- Triggers tool calls when the prompt mentions a tool name (or a `cannedTriggers` keyword) — splits camelCase names into words (e.g., "weather" matches `getWeather`) and emits Strands `toolUse` events. Matching is on word boundaries, not substrings, so "category" does not fire `getCat`.
|
|
113
|
+
- Derives tool input from, in order of preference: the tool's `cannedExamples`, the schema `default` (from Zod `.default()`), the first `enum` value (for enum fields), then a generic placeholder by type (`'sample'` / `1` / `true` / `[]`)
|
|
65
114
|
- After Strands executes the tool and sends the result back, returns a fixed acknowledgment (`"I called the tool and got a result."`)
|
|
66
115
|
- Token usage reports zeros (no real model call)
|
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ const agent = new Agent(scope, id, config)
|
|
|
48
48
|
| `getPendingInterrupts(conversationId)` | `Promise<Array<...>>` | Get unanswered interrupts (for reload support). |
|
|
49
49
|
| `getChannel(channelId)` | `Promise<RealtimeChannel>` | Get a Realtime channel for subscribing to chunks. |
|
|
50
50
|
|
|
51
|
-
`stream()`
|
|
51
|
+
`stream()` invokes the AgentCore Runtime (`InvokeAgentRuntime`) and returns immediately — no API Gateway timeout risk. The loop runs on the runtime (sessions up to 8h) and publishes chunks to Realtime as it goes. The channel ID is resolved as `options.channelId || options.conversationId || crypto.randomUUID()` — empty strings are treated as unset and fall through to the next value.
|
|
52
52
|
|
|
53
53
|
**Important: Subscribe before sending.** The agent starts emitting chunks immediately after `stream()` is called. If you subscribe to the channel after calling `stream()`, early chunks may be dropped. Always subscribe first, await `established`, then send:
|
|
54
54
|
|
|
@@ -136,6 +136,8 @@ The `useChat` hook only surfaces `user`, `assistant`, and `approval` messages to
|
|
|
136
136
|
| `inferenceOnly` | `boolean` | Skip persistence infra. Default: `false`. |
|
|
137
137
|
| `conversation` | `ConversationManagerConfig` | How the agent trims message history (sliding-window or summarizing). |
|
|
138
138
|
| `streamingMode` | `'token' \| 'block'` | How text chunks are published to the client. Default: `'block'`. |
|
|
139
|
+
| `maxLlmCalls` | `number \| false` | Max model invocations per turn before the turn is stopped; `false` disables. Default: `20`. See [Limiting runaway cost](#limiting-runaway-cost). |
|
|
140
|
+
| `maxToolIterations` | `number \| false` | Max tool calls per turn before the turn is stopped; `false` disables. Default: `20`. See [Limiting runaway cost](#limiting-runaway-cost). |
|
|
139
141
|
|
|
140
142
|
### Model Configuration
|
|
141
143
|
|
|
@@ -359,6 +361,38 @@ const agent = new Agent(scope, 'support', {
|
|
|
359
361
|
});
|
|
360
362
|
```
|
|
361
363
|
|
|
364
|
+
### Limiting runaway cost
|
|
365
|
+
|
|
366
|
+
An agent runs a reason→act loop: each iteration is one **model call**, optionally followed by tool calls, and a model call that requests no tools ends the turn. A misbehaving agent — or a prompt that induces one — can loop this cycle far longer than intended; an unbounded loop can run up unexpected cost.
|
|
367
|
+
|
|
368
|
+
> **These caps are a safety backstop, not a way to guide the agent.** The defaults exist only to stop a runaway from racking up cost — they are *not* tuned for your agent and should not be used to shape its behavior. An agent that legitimately needs more steps or tools will be cut off mid-task at the default. **Set these values deliberately for your own agent** based on how many steps and tool calls a healthy turn takes, so a normal turn always completes and only genuine runaways are stopped.
|
|
369
|
+
|
|
370
|
+
Two per-turn safety caps bound this, and **both default to `20`**:
|
|
371
|
+
|
|
372
|
+
- **`maxLlmCalls`** — the maximum number of model invocations in a single turn. This is the most direct spend guard (model calls are the billing unit), and because every tool round needs a model call it transitively bounds tool loops too.
|
|
373
|
+
- **`maxToolIterations`** — the maximum number of tool calls in a single turn (parallel tool batches count each call).
|
|
374
|
+
|
|
375
|
+
When either cap is hit, the turn is stopped and the client receives an `error` chunk (so `complete()` rejects) instead of `done`. The counts cover the whole turn, including across a [tool-approval interrupt](#tool-approval-human-in-the-loop): they are kept in the agent's session state, so a turn that pauses for approval and continues via `resume()` keeps its existing budget instead of starting a fresh one. Only a new message starts a new budget.
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
const agent = new Agent(scope, 'support', {
|
|
379
|
+
systemPrompt: '...',
|
|
380
|
+
maxLlmCalls: 40, // agent legitimately reasons over many steps
|
|
381
|
+
maxToolIterations: 60, // ...and chains many tools per turn
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Or disable a cap entirely with `false`:
|
|
385
|
+
const unbounded = new Agent(scope, 'batch', {
|
|
386
|
+
systemPrompt: '...',
|
|
387
|
+
maxLlmCalls: false, // no per-turn model-call limit
|
|
388
|
+
maxToolIterations: false,
|
|
389
|
+
});
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
Raise the caps for agents that legitimately take many steps so they aren't cut off mid-task, or set a cap to `false` to disable it — tuning these to your agent is part of delivering a good agentic experience, not just a cost lever. The caps bound call *count*, not tokens or wall-clock — for real cost protection, also configure a [billing alarm](https://docs.aws.amazon.com/cost-management/latest/userguide/monitor-charges.html) or a CloudWatch alarm on Bedrock spend.
|
|
393
|
+
|
|
394
|
+
When sizing the caps for an agent that uses [tool approval](#tool-approval-human-in-the-loop), remember that approved and trusted tool calls both count: a `trustable` tool that's been trusted runs without interrupting, and a tool approved through `resume()` continues on the same budget, so a long approve-and-continue turn can still reach the cap.
|
|
395
|
+
|
|
362
396
|
## Tools
|
|
363
397
|
|
|
364
398
|
Tools let the agent take actions during its reasoning — query a database, call an API, send an email. The model decides *when* to call a tool based on the user's message and the tool's description. You define the tool's schema and handler; the framework handles the rest.
|
|
@@ -640,9 +674,41 @@ The CannedProvider is a custom Strands model provider that requires no network o
|
|
|
640
674
|
|
|
641
675
|
- Returns simple mock responses
|
|
642
676
|
- Triggers tool calls when the prompt mentions a tool name (e.g., "get order" triggers `getOrderStatus`)
|
|
643
|
-
- Generates valid tool inputs from Zod schemas
|
|
677
|
+
- Generates valid tool inputs from Zod schemas, respecting schema `default` values (from `.default()`) before falling back to type-based placeholders (`'sample'`, `1`, `true`, `[]`)
|
|
644
678
|
- Streams responses word by word, matching the same protocol as real providers
|
|
645
679
|
|
|
680
|
+
#### Canned Hints — `cannedExamples` and `cannedTriggers`
|
|
681
|
+
|
|
682
|
+
Two optional tool fields make the canned provider more useful for local prototyping. Both are **ignored by the real bedrock/openai providers**, so they're safe to leave on production tools:
|
|
683
|
+
|
|
684
|
+
| Field | Type | Effect (canned provider only) |
|
|
685
|
+
| --- | --- | --- |
|
|
686
|
+
| `cannedExamples` | `Record<string, JSONValue>` | Realistic tool input, shallow-merged over the generated placeholder — your fields win, unspecified fields fall back to schema defaults / placeholders. The merge is one level deep: a nested-object example replaces that whole generated sub-object rather than deep-merging into it. |
|
|
687
|
+
| `cannedTriggers` | `string[]` | Extra keyword phrases that make the provider select this tool, beyond its name and camelCase words. Single and multi-word phrases match on word boundaries (so `'log in'` won't fire on `"backlog in"`); internal whitespace is flexible. |
|
|
688
|
+
|
|
689
|
+
Building on the [KnowledgeBase tool](#using-knowledgebase-with-the-agent) above: without hints the mock calls `searchDocs` with `{ query: 'sample' }`, which matches nothing in your documents, so local testing returns empty results. A `cannedExamples` query that actually appears in *your* docs makes the mock return real hits, and `cannedTriggers` lets natural phrasings fire the tool:
|
|
690
|
+
|
|
691
|
+
```typescript
|
|
692
|
+
tools: (tool) => ({
|
|
693
|
+
searchDocs: tool({
|
|
694
|
+
description: 'Search product documentation for relevant information',
|
|
695
|
+
parameters: z.object({
|
|
696
|
+
query: z.string().describe('The search query'),
|
|
697
|
+
maxResults: z.number().optional().describe('Max results to return (default: 5)'),
|
|
698
|
+
}),
|
|
699
|
+
handler: async ({ input }) => kb.retrieve(input.query, { maxResults: input.maxResults ?? 5 }),
|
|
700
|
+
|
|
701
|
+
// Canned provider hints (ignored by real models):
|
|
702
|
+
// Without this the mock would search for the literal 'sample' and match nothing —
|
|
703
|
+
// use a query that hits YOUR documents so local runs return meaningful results.
|
|
704
|
+
cannedExamples: { query: 'how do I reset my password' },
|
|
705
|
+
// The name already matches "search"/"docs"/"searchDocs"; these add phrasings that don't
|
|
706
|
+
// contain the name, so "help me find the manual" or "look up the guide" also fire the tool.
|
|
707
|
+
cannedTriggers: ['find', 'look up'],
|
|
708
|
+
}),
|
|
709
|
+
}),
|
|
710
|
+
```
|
|
711
|
+
|
|
646
712
|
|
|
647
713
|
## Client Hook — `useChat`
|
|
648
714
|
|
|
@@ -800,7 +866,8 @@ The Agent BB composes several internal Building Blocks automatically:
|
|
|
800
866
|
| `FileBucket` | S3 | Session snapshot storage (Strands agent state between turns) |
|
|
801
867
|
| `DistributedTable` × 2 | DynamoDB | Conversations table + messages table |
|
|
802
868
|
| `Realtime` | API Gateway WebSocket | Streaming chunks to connected clients |
|
|
803
|
-
|
|
869
|
+
|
|
870
|
+
The streaming loop itself runs on a **Bedrock AgentCore Runtime** (provisioned by `AgentCoreRuntime` — not a composed BB). It's invoked via `InvokeAgentRuntime`, runs the loop for the length of the session (up to 8h), and publishes chunks over the Realtime BB above.
|
|
804
871
|
|
|
805
872
|
When `inferenceOnly: true`, the two DistributedTables are skipped (no conversation persistence).
|
|
806
873
|
|
|
@@ -809,9 +876,8 @@ When `inferenceOnly: true`, the two DistributedTables are skipped (no conversati
|
|
|
809
876
|
- **Model:** Bedrock pay-per-token pricing. See [Bedrock pricing](https://aws.amazon.com/bedrock/pricing/).
|
|
810
877
|
- **Persistence:** DynamoDB (DistributedTable) — PAY_PER_REQUEST, single-digit ms latency.
|
|
811
878
|
- **Session storage:** S3 (FileBucket) — ~$0.023 per GB/month.
|
|
812
|
-
- **
|
|
813
|
-
- **Streaming:**
|
|
814
|
-
- **No timeout limit:** Agent runs in AsyncJob consumer Lambda (up to 15 min), not behind API Gateway.
|
|
879
|
+
- **Loop compute:** Bedrock AgentCore Runtime — consumption-based (vCPU + memory while a session is active). See [AgentCore Runtime pricing](https://aws.amazon.com/bedrock/agentcore/pricing/).
|
|
880
|
+
- **Streaming:** API Gateway WebSocket (Realtime) — per-message + per-connection-minute pricing.
|
|
815
881
|
|
|
816
882
|
## Troubleshooting
|
|
817
883
|
|
|
@@ -827,4 +893,5 @@ When `inferenceOnly: true`, the two DistributedTables are skipped (no conversati
|
|
|
827
893
|
- [Bedrock supported models](https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html)
|
|
828
894
|
- [Cross-region inference profiles](https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html)
|
|
829
895
|
- [Bedrock pricing](https://aws.amazon.com/bedrock/pricing/)
|
|
896
|
+
- [Bedrock AgentCore Runtime pricing](https://aws.amazon.com/bedrock/agentcore/pricing/)
|
|
830
897
|
- [Ollama model library](https://ollama.com/library)
|
package/dist/agent.aws.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { ScopeParent } from '@aws-blocks/core';
|
|
|
2
2
|
import type { FileBucket } from '@aws-blocks/bb-file-bucket';
|
|
3
3
|
import type { SnapshotStorage } from '@strands-agents/sdk';
|
|
4
4
|
import type { S3StorageConfig } from '@strands-agents/sdk/session/s3-storage';
|
|
5
|
-
import { AgentBase } from './agent.js';
|
|
5
|
+
import { AgentBase, type AgentTurnPayload } from './agent.js';
|
|
6
6
|
import type { AgentConfig, DefaultToolContext } from './types.js';
|
|
7
7
|
/**
|
|
8
8
|
* Builds the deployed Agent's snapshot storage, pinning S3Storage to the Lambda
|
|
@@ -12,6 +12,20 @@ import type { AgentConfig, DefaultToolContext } from './types.js';
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function createDeployedSnapshotStorage(bucket: FileBucket, S3StorageImpl?: new (config: S3StorageConfig) => SnapshotStorage): SnapshotStorage;
|
|
14
14
|
export declare class Agent<TContext = DefaultToolContext> extends AgentBase<TContext> {
|
|
15
|
+
private _agentCore?;
|
|
15
16
|
constructor(scope: ScopeParent, id: string, config: AgentConfig<TContext>);
|
|
17
|
+
/**
|
|
18
|
+
* Run the turn on the AgentCore Runtime that hosts this agent's loop.
|
|
19
|
+
*
|
|
20
|
+
* Returns as soon as the runtime has ACCEPTED the turn: `agentcore-entry` starts `runAgent()`
|
|
21
|
+
* as a background async task (which streams chunks to Realtime under the runtime's own role)
|
|
22
|
+
* and responds immediately, so this `InvokeAgentRuntime` call does NOT hold the connection for
|
|
23
|
+
* the turn's duration — the loop keeps running server-side for up to the 8h session lifetime.
|
|
24
|
+
* `runtimeSessionId` is keyed by conversationId so a conversation's turns/resumes reuse one
|
|
25
|
+
* warm microVM.
|
|
26
|
+
*
|
|
27
|
+
* @internal Internal compute seam; not customer API.
|
|
28
|
+
*/
|
|
29
|
+
protected dispatchTurn(payload: AgentTurnPayload<TContext>): Promise<void>;
|
|
16
30
|
}
|
|
17
31
|
//# sourceMappingURL=agent.aws.d.ts.map
|
package/dist/agent.aws.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.aws.d.ts","sourceRoot":"","sources":["../src/agent.aws.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent.aws.d.ts","sourceRoot":"","sources":["../src/agent.aws.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9D,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAalE;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC5C,MAAM,EAAE,UAAU,EAClB,aAAa,GAAE,KAAK,MAAM,EAAE,eAAe,KAAK,eAA2B,GACzE,eAAe,CAEjB;AAED,qBAAa,KAAK,CAAC,QAAQ,GAAG,kBAAkB,CAAE,SAAQ,SAAS,CAAC,QAAQ,CAAC;IAC5E,OAAO,CAAC,UAAU,CAAC,CAAyB;gBAEhC,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC;IAIzE;;;;;;;;;;;OAWG;cACsB,YAAY,CAAC,OAAO,EAAE,gBAAgB,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CA4BzF"}
|
package/dist/agent.aws.js
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { createHash } from 'node:crypto';
|
|
4
|
+
import { getConfig } from '@aws-blocks/core';
|
|
5
|
+
import { BedrockAgentCoreClient, InvokeAgentRuntimeCommand } from '@aws-sdk/client-bedrock-agentcore';
|
|
3
6
|
import { S3Storage } from '@strands-agents/sdk/session/s3-storage';
|
|
4
7
|
import { AgentBase } from './agent.js';
|
|
8
|
+
import { AgentErrors, blocksAgentError } from './errors.js';
|
|
5
9
|
import { BedrockModels } from './models.js';
|
|
10
|
+
/**
|
|
11
|
+
* AgentCore requires a `runtimeSessionId` of at least 33 characters. Conversation/channel ids
|
|
12
|
+
* are UUIDs (36 chars) in the normal path and pass through unchanged; anything shorter is hashed
|
|
13
|
+
* to a stable 64-char hex id — stable per input, so a conversation keeps routing to one warm
|
|
14
|
+
* microVM across turns/resumes.
|
|
15
|
+
*/
|
|
16
|
+
function toRuntimeSessionId(base) {
|
|
17
|
+
return base.length >= 33 ? base : createHash('sha256').update(base).digest('hex');
|
|
18
|
+
}
|
|
6
19
|
/**
|
|
7
20
|
* Builds the deployed Agent's snapshot storage, pinning S3Storage to the Lambda
|
|
8
21
|
* execution region (`AWS_REGION`) so non-us-east-1 deploys use the correct regional
|
|
@@ -13,7 +26,43 @@ export function createDeployedSnapshotStorage(bucket, S3StorageImpl = S3Storage)
|
|
|
13
26
|
return new S3StorageImpl({ bucket: bucket.fullId, region: process.env.AWS_REGION });
|
|
14
27
|
}
|
|
15
28
|
export class Agent extends AgentBase {
|
|
29
|
+
_agentCore;
|
|
16
30
|
constructor(scope, id, config) {
|
|
17
31
|
super(scope, id, config, config.model?.deployed ?? BedrockModels.BALANCED, createDeployedSnapshotStorage);
|
|
18
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Run the turn on the AgentCore Runtime that hosts this agent's loop.
|
|
35
|
+
*
|
|
36
|
+
* Returns as soon as the runtime has ACCEPTED the turn: `agentcore-entry` starts `runAgent()`
|
|
37
|
+
* as a background async task (which streams chunks to Realtime under the runtime's own role)
|
|
38
|
+
* and responds immediately, so this `InvokeAgentRuntime` call does NOT hold the connection for
|
|
39
|
+
* the turn's duration — the loop keeps running server-side for up to the 8h session lifetime.
|
|
40
|
+
* `runtimeSessionId` is keyed by conversationId so a conversation's turns/resumes reuse one
|
|
41
|
+
* warm microVM.
|
|
42
|
+
*
|
|
43
|
+
* @internal Internal compute seam; not customer API.
|
|
44
|
+
*/
|
|
45
|
+
async dispatchTurn(payload) {
|
|
46
|
+
const runtimeArnKey = `BB_AGENT_${this.fullId}_RUNTIME_ARN`;
|
|
47
|
+
const runtimeArn = await getConfig(runtimeArnKey);
|
|
48
|
+
if (!runtimeArn) {
|
|
49
|
+
throw blocksAgentError(AgentErrors.StreamFailed, `AgentCore Runtime ARN not found (config key ${runtimeArnKey}). Ensure the app build produced the AgentCore asset and the stack deployed the Runtime.`);
|
|
50
|
+
}
|
|
51
|
+
this._agentCore ??= new BedrockAgentCoreClient({});
|
|
52
|
+
const body = {
|
|
53
|
+
prompt: payload.message,
|
|
54
|
+
channelId: payload.channelId,
|
|
55
|
+
conversationId: payload.conversationId,
|
|
56
|
+
userId: payload.userId,
|
|
57
|
+
interruptResponses: payload.interruptResponses,
|
|
58
|
+
context: payload.context,
|
|
59
|
+
};
|
|
60
|
+
await this._agentCore.send(new InvokeAgentRuntimeCommand({
|
|
61
|
+
agentRuntimeArn: runtimeArn,
|
|
62
|
+
runtimeSessionId: toRuntimeSessionId(payload.conversationId ?? payload.channelId),
|
|
63
|
+
contentType: 'application/json',
|
|
64
|
+
accept: 'application/json',
|
|
65
|
+
payload: new TextEncoder().encode(JSON.stringify(body)),
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
19
68
|
}
|
package/dist/agent.d.ts
CHANGED
|
@@ -4,15 +4,46 @@ import { FileBucket } from '@aws-blocks/bb-file-bucket';
|
|
|
4
4
|
import type { ChildLogger } from '@aws-blocks/bb-logger';
|
|
5
5
|
import type { SnapshotStorage } from '@strands-agents/sdk';
|
|
6
6
|
import type { AgentConfig, AgentStreamResult, StreamOptions, Message, Conversation, ModelConfig, InterruptResponse, DefaultToolContext } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* A single agent turn to dispatch (initial message or HITL resume). Passed to
|
|
9
|
+
* {@link AgentBase.dispatchTurn} — run in-process locally, or shipped to the AgentCore Runtime
|
|
10
|
+
* on AWS as the `InvokeAgentRuntime` payload.
|
|
11
|
+
*
|
|
12
|
+
* @internal Internal turn-dispatch seam — not part of the public API. Customers use
|
|
13
|
+
* `stream()` / `resume()`; this is the shape those hand to the compute layer.
|
|
14
|
+
*/
|
|
15
|
+
export interface AgentTurnPayload<TContext = DefaultToolContext> {
|
|
16
|
+
/** User prompt. Empty on resume (interruptResponses drive the turn instead). */
|
|
17
|
+
message: string;
|
|
18
|
+
/** Conversation to persist to / restore the session from (undefined for inferenceOnly). */
|
|
19
|
+
conversationId?: string;
|
|
20
|
+
/** Realtime channel the client subscribes to for this turn's chunks. */
|
|
21
|
+
channelId: string;
|
|
22
|
+
/** Conversation owner. */
|
|
23
|
+
userId: string;
|
|
24
|
+
/** HITL resume: approval responses to apply instead of a new prompt. */
|
|
25
|
+
interruptResponses?: Array<{
|
|
26
|
+
interruptId: string;
|
|
27
|
+
response: string;
|
|
28
|
+
}>;
|
|
29
|
+
/** Per-call tool context, forwarded to tool invocations. JSON-serializable. */
|
|
30
|
+
context?: TContext;
|
|
31
|
+
}
|
|
32
|
+
/** @internal Register a live Agent instance so the AgentCore entrypoint can find it by fullId. */
|
|
33
|
+
export declare function registerAgentInstance(fullId: string, agent: AgentBase<any>): void;
|
|
34
|
+
/** @internal Look up a registered Agent instance by fullId (used by the AgentCore entrypoint). */
|
|
35
|
+
export declare function getAgentInstance(fullId: string): AgentBase<any> | undefined;
|
|
7
36
|
/**
|
|
8
37
|
* Base class for the Agent BB. Extended by agent.mock.ts (model.local) and agent.aws.ts (model.deployed).
|
|
9
38
|
*
|
|
10
|
-
* Creates
|
|
39
|
+
* Creates internal BBs depending on mode:
|
|
11
40
|
* - FileBucket: session snapshot storage for Strands SessionManager (always)
|
|
12
41
|
* - DistributedTable: frontend message history (when inferenceOnly = false)
|
|
13
|
-
* - Realtime: streaming chunks to browser
|
|
14
|
-
*
|
|
15
|
-
* -
|
|
42
|
+
* - Realtime: streaming chunks to browser (always)
|
|
43
|
+
*
|
|
44
|
+
* The agent loop runs via {@link dispatchTurn}: in-process locally (mock), and on the
|
|
45
|
+
* AgentCore Runtime on AWS (the deployed subclass overrides dispatchTurn to invoke it). The
|
|
46
|
+
* AgentCore Runtime itself is provisioned by the CDK layer (index.cdk.ts → AgentCoreRuntime).
|
|
16
47
|
*/
|
|
17
48
|
export declare class AgentBase<TContext = DefaultToolContext> extends Scope {
|
|
18
49
|
/** Developer-facing agent configuration. */
|
|
@@ -25,8 +56,6 @@ export declare class AgentBase<TContext = DefaultToolContext> extends Scope {
|
|
|
25
56
|
private messages?;
|
|
26
57
|
/** Realtime pub/sub — streams chunks to browser. */
|
|
27
58
|
private rt;
|
|
28
|
-
/** Internal async job — runs the Strands agent in a separate execution context. */
|
|
29
|
-
private job;
|
|
30
59
|
/** Which model provider to use. */
|
|
31
60
|
private modelConfig;
|
|
32
61
|
/** Where to persist Strands agent state (snapshots). */
|
|
@@ -43,31 +72,70 @@ export declare class AgentBase<TContext = DefaultToolContext> extends Scope {
|
|
|
43
72
|
* @param createSnapshotStorage - factory that receives the internal FileBucket and returns the appropriate SnapshotStorage
|
|
44
73
|
*/
|
|
45
74
|
constructor(scope: ScopeParent, id: string, config: AgentConfig<TContext>, modelConfig: ModelConfig | ModelConfig[] | undefined, createSnapshotStorage: (bucket: FileBucket) => SnapshotStorage);
|
|
75
|
+
/**
|
|
76
|
+
* Run one agent turn (initial message or HITL resume) and publish its chunks to Realtime.
|
|
77
|
+
*
|
|
78
|
+
* This is the single execution entry the compute layer invokes: the AgentCore Runtime
|
|
79
|
+
* entrypoint (agentcore-entry.ts) calls it as a background async task on AWS, and locally
|
|
80
|
+
* {@link dispatchTurn} calls it in-process. Errors are caught and published as an `error`
|
|
81
|
+
* chunk (not re-thrown) so the client never hangs and a non-idempotent turn isn't retried.
|
|
82
|
+
*
|
|
83
|
+
* @param payload - the turn to run (see {@link AgentTurnPayload}).
|
|
84
|
+
* @internal Invoked by the compute layer (agentcore-entry / dispatchTurn), not customer API.
|
|
85
|
+
*/
|
|
86
|
+
invokeTurn(payload: AgentTurnPayload<TContext>): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Dispatch a turn to wherever the agent loop runs, returning promptly so `stream()`/`resume()`
|
|
89
|
+
* hand the client a `channelId` without waiting for the turn to finish.
|
|
90
|
+
*
|
|
91
|
+
* Base (local/mock): run the loop IN-PROCESS, fire-and-forget — chunks flow to the mock
|
|
92
|
+
* Realtime as the turn progresses. The deployed (AWS) subclass overrides this to invoke the
|
|
93
|
+
* AgentCore Runtime, which runs the loop as a background task and publishes to Realtime.
|
|
94
|
+
*
|
|
95
|
+
* @internal Internal compute seam (overridden by the AWS subclass); not customer API.
|
|
96
|
+
*/
|
|
97
|
+
protected dispatchTurn(payload: AgentTurnPayload<TContext>): Promise<void>;
|
|
46
98
|
/**
|
|
47
99
|
* Executes the Strands agent, publishes chunks to Realtime, persists messages to DynamoDB.
|
|
48
100
|
*
|
|
49
|
-
* Called by
|
|
50
|
-
*
|
|
101
|
+
* Called by {@link invokeTurn} (wherever the loop runs — locally in-process, or inside the
|
|
102
|
+
* AgentCore Runtime container on AWS). Publishes each event to the Realtime `chunks` channel.
|
|
103
|
+
*
|
|
104
|
+
* Flow: invokeTurn() → runAgent() → Strands agent.stream() → publishes chunks to Realtime BB
|
|
51
105
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
106
|
+
* @param message - the user message that starts the turn (ignored on the resume path)
|
|
107
|
+
* @param conversationId - conversation to load/persist history for; undefined means no persistence
|
|
108
|
+
* @param channelId - Realtime channel the stream chunks are published to
|
|
109
|
+
* @param userId - owner of the conversation, stored on every persisted message
|
|
110
|
+
* @param interruptResponses - approval responses when resuming a turn paused on a HITL interrupt
|
|
111
|
+
* @param context - per-call tool context, threaded to tool handlers via Strands invocationState
|
|
54
112
|
*/
|
|
55
113
|
private runAgent;
|
|
56
114
|
private createStrandsAgent;
|
|
57
115
|
/**
|
|
58
116
|
* Submit a message to the agent. Returns immediately with a channelId.
|
|
59
117
|
*
|
|
60
|
-
* Flow: stream() →
|
|
61
|
-
*
|
|
62
|
-
*
|
|
118
|
+
* Flow: stream() → dispatchTurn() → returns { channelId }
|
|
119
|
+
* dispatchTurn runs the loop where the compute lives (in-process locally; on the AgentCore
|
|
120
|
+
* Runtime on AWS) and publishes chunks to Realtime on the returned channelId.
|
|
63
121
|
*
|
|
64
122
|
* Subscribe to chunks via result.channel, or await result.complete() for the final response.
|
|
123
|
+
*
|
|
124
|
+
* Errors surface on two paths. Once the turn is dispatched, loop failures arrive as an `error`
|
|
125
|
+
* chunk on the channel (and reject `complete()`). A failure to *dispatch* the turn — e.g. on AWS
|
|
126
|
+
* when the AgentCore Runtime can't be invoked (unresolved runtime ARN, or an `InvokeAgentRuntime`
|
|
127
|
+
* error) — rejects this `stream()` call itself rather than reaching the channel. Always `await`
|
|
128
|
+
* `stream()` so a dispatch failure isn't lost.
|
|
65
129
|
*/
|
|
66
130
|
stream(message: string, options?: StreamOptions<TContext>): Promise<AgentStreamResult>;
|
|
67
131
|
/**
|
|
68
132
|
* Resume an interrupted agent with user's responses.
|
|
69
|
-
*
|
|
133
|
+
* Dispatches a new turn that loads the session and continues from the interrupt point.
|
|
70
134
|
* Chunks are published to the same channelId — use the existing subscription or call complete() again to wait for the result.
|
|
135
|
+
*
|
|
136
|
+
* Like `stream()`, errors surface on two paths: loop failures arrive as an `error` chunk on the
|
|
137
|
+
* channel once the turn is dispatched, while a failure to *dispatch* (e.g. on AWS when the AgentCore
|
|
138
|
+
* Runtime can't be invoked) rejects this `resume()` call itself. Always `await` it.
|
|
71
139
|
*/
|
|
72
140
|
resume(channelId: string, responses: Array<InterruptResponse>, options?: {
|
|
73
141
|
conversationId?: string;
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAA6C,MAAM,kBAAkB,CAAC;AACpF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|