@aws-blocks/bb-agent 0.4.0 → 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/README.md CHANGED
@@ -763,7 +763,8 @@ export const api = new ApiNamespace(scope, 'api', (context) => ({
763
763
  return { conversationId: await agent.createConversationId(userId) };
764
764
  },
765
765
  async sendMessage(conversationId: string, message: string, channelId: string, userId: string) {
766
- await agent.stream(message, { conversationId, channelId, userId });
766
+ const result = await agent.stream(message, { conversationId, channelId, userId });
767
+ return { channelId: result.channelId };
767
768
  },
768
769
  async getConversation(conversationId: string) {
769
770
  const messages = await agent.getConversation(conversationId);
@@ -803,7 +804,87 @@ await chat.sendMessage('Hello!');
803
804
  await chat.loadConversation('conv-123');
804
805
  ```
805
806
 
806
- ### 2. Support Agent with Tools
807
+ The example above is framework-agnostic on purpose — `useChat` has no React import and works with any UI layer. The two examples below show how to bridge it into a specific framework's reactivity.
808
+
809
+ ### 2. React: hold the instance once, drive `useState` from the callbacks
810
+
811
+ `useChat` is a factory, not a React hook, so it must **not** run on every render — recreating it drops the WebSocket subscription and conversation state each time. Hold the single instance in a `useRef` (created lazily so it survives re-renders), and turn the `onMessagesChange` / `onLoadingChange` / `onInterrupt` callbacks into `setState` calls so React re-renders when the mutable instance changes. This example keeps the `api` wiring minimal — it omits the `userId` that the End-to-End example (#1) threads through `createConversation` / `sendMessage`; thread it the same way here when your API needs it (or resolve the user server-side).
812
+
813
+ ```tsx
814
+ 'use client'; // Next.js only — see the note below. Plain React (Vite/CRA) can omit this.
815
+
816
+ import { useRef, useState, useEffect } from 'react';
817
+ import { useChat, type ChatMessage } from '@aws-blocks/bb-agent/client';
818
+ import { api } from './api'; // your generated aws-blocks API client
819
+
820
+ export function Chat() {
821
+ const [messages, setMessages] = useState<ChatMessage[]>([]);
822
+ const [isLoading, setIsLoading] = useState(false);
823
+ const [input, setInput] = useState('');
824
+
825
+ // Create the instance exactly once. The ref survives every re-render,
826
+ // so the subscription and conversation state are never torn down.
827
+ // Type the ref as `| undefined` and initialize with `undefined` — @types/react 19
828
+ // tightened the useRef overloads, so a bare useRef<T>() no longer compiles.
829
+ const chatRef = useRef<ReturnType<typeof useChat> | undefined>(undefined);
830
+ if (!chatRef.current) {
831
+ // eslint-disable-next-line react-hooks/rules-of-hooks -- useChat is a factory, not a hook; the use-prefix trips the linter's hook heuristic.
832
+ chatRef.current = useChat({
833
+ api: {
834
+ sendMessage: (convId, msg, chId) => api.sendMessage(convId, msg, chId),
835
+ createConversation: () => api.createConversation(),
836
+ getConversation: (id) => api.getConversation(id),
837
+ },
838
+ subscribe: async (channelId, handler) => {
839
+ const channel = await api.getChannel(channelId);
840
+ return channel.subscribe(handler);
841
+ },
842
+ // Bridge the mutable instance into React state — these fire on every change.
843
+ onMessagesChange: setMessages,
844
+ onLoadingChange: setIsLoading,
845
+ });
846
+ }
847
+ const chat = chatRef.current!; // guaranteed set by the block above
848
+
849
+ // Tear down the WebSocket subscription when the component unmounts.
850
+ useEffect(() => () => chat.destroy(), [chat]);
851
+
852
+ async function handleSend(e: React.FormEvent) {
853
+ e.preventDefault();
854
+ const text = input.trim();
855
+ if (!text || isLoading) return;
856
+ setInput('');
857
+ await chat.sendMessage(text);
858
+ }
859
+
860
+ return (
861
+ <div>
862
+ <ul>
863
+ {messages.map((m) => (
864
+ <li key={m.id} data-role={m.role}>
865
+ <strong>{m.role}:</strong> {m.content}
866
+ </li>
867
+ ))}
868
+ </ul>
869
+ <form onSubmit={handleSend}>
870
+ <input value={input} onChange={(e) => setInput(e.target.value)} disabled={isLoading} />
871
+ <button type="submit" disabled={isLoading}>Send</button>
872
+ </form>
873
+ </div>
874
+ );
875
+ }
876
+ ```
877
+
878
+ Key points:
879
+
880
+ - **One instance, held in a ref.** `useRef` + the lazy `if (!chatRef.current)` guard is the React idiom for "construct once." Because the identifier is `use`-prefixed, `eslint-plugin-react-hooks` (bundled in the default Next.js and CRA configs) flags the guarded call as a conditional hook (`react-hooks/rules-of-hooks`). `useChat` is a factory, not a hook, so this is a false positive — the inline `eslint-disable-next-line` above the call silences it. What you must **not** do is call `useChat(...)` unguarded on every render: that recreates the instance each time and is the footgun the factory note warns about.
881
+ - **Callbacks are your reactivity bridge.** `useChat` mutates its own message list in place; `onMessagesChange` / `onLoadingChange` hand you the new value so you can `setState` and trigger a render. Passing `setMessages` / `setIsLoading` directly is enough.
882
+ - **Clean up on unmount** with `chat.destroy()` in a `useEffect` cleanup, so the Realtime subscription is closed.
883
+ - **Approvals:** wire `resume: (chId, responses, convId) => api.resume(chId, responses, convId)` into the `api` object above (mirroring your backend's resume method — `respondToInterrupt` throws if it is absent), add `onInterrupt: setInterrupts` (with `const [interrupts, setInterrupts] = useState<Array<{ id: string; name: string; reason?: unknown }>>([])` — a bare `useState([])` infers `never[]` and rejects the payload) to render an approval UI, then call `chat.respondToInterrupt([{ interruptId, approved: true }])`.
884
+
885
+ **Next.js:** this is the same component — just keep the `'use client'` directive at the top of the file. `useChat` opens a browser WebSocket and holds client state, so it must run in a Client Component, never a Server Component. No other changes are needed.
886
+
887
+ ### 3. Support Agent with Tools
807
888
 
808
889
  Agent with tools that can look up orders and search documentation. Uses tool context to scope queries to the authenticated user.
809
890
 
@@ -1,9 +1,9 @@
1
- import { Scope } from '@aws-blocks/core/cdk';
1
+ import { BuildingBlockScope } from '@aws-blocks/core/cdk';
2
2
  import type { ScopeParent } from '@aws-blocks/core';
3
3
  import type { AgentConfig } from './types.js';
4
4
  export { AgentErrors } from './errors.js';
5
5
  export { BedrockModels, OllamaModels } from './models.js';
6
- export declare class Agent extends Scope {
6
+ export declare class Agent extends BuildingBlockScope {
7
7
  /**
8
8
  * CDK layer for the Agent BB.
9
9
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.cdk.d.ts","sourceRoot":"","sources":["../src/index.cdk.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAMpD,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,KAAK;IAC/B;;;;;;;;;;;OAWG;gBACS,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW;CAyChE"}
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,14 +1,15 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
- import { Scope } from '@aws-blocks/core/cdk';
3
+ import { BuildingBlockScope } from '@aws-blocks/core/cdk';
4
4
  import { DistributedTable } from '@aws-blocks/bb-distributed-table';
5
5
  import { Realtime } from '@aws-blocks/bb-realtime';
6
6
  import { FileBucket } from '@aws-blocks/bb-file-bucket';
7
+ import * as ec2 from 'aws-cdk-lib/aws-ec2';
7
8
  import { AgentCoreRuntime } from './agentcore-runtime.cdk.js';
8
9
  import { messageSchema, conversationSchema, agentStreamChunkSchema } from './schemas.js';
9
10
  export { AgentErrors } from './errors.js';
10
11
  export { BedrockModels, OllamaModels } from './models.js';
11
- export class Agent extends Scope {
12
+ export class Agent extends BuildingBlockScope {
12
13
  /**
13
14
  * CDK layer for the Agent BB.
14
15
  *
@@ -22,7 +23,7 @@ export class Agent extends Scope {
22
23
  * handler no longer needs Bedrock access — the runtime's own role gets it (see AgentCoreRuntime).
23
24
  */
24
25
  constructor(scope, id, config) {
25
- super(id, { parent: scope });
26
+ super(id, { parent: scope, vpc: { interfaceEndpoints: [ec2.InterfaceVpcEndpointAwsService.BEDROCK_RUNTIME] } });
26
27
  // Session-snapshot bucket. Provisioned here (and granted to the shared execution role that the
27
28
  // AgentCore Runtime runs as); the deployed loop re-derives its name from this bucket's `fullId`
28
29
  // in-process — the same `'sn'` id → same fullId → same physical bucket — so no name needs to be
@@ -22,7 +22,7 @@ export interface ChatMessage {
22
22
  /** Options for creating a chat instance. */
23
23
  export interface UseChatOptions {
24
24
  api: {
25
- sendMessage(conversationId: string, message: string, channelId: string): Promise<void>;
25
+ sendMessage(conversationId: string, message: string, channelId: string): Promise<unknown>;
26
26
  createConversation(): Promise<{
27
27
  conversationId: string;
28
28
  }>;
@@ -39,7 +39,7 @@ export interface UseChatOptions {
39
39
  trust?: boolean;
40
40
  toolName?: string;
41
41
  input?: any;
42
- }>, conversationId?: string): Promise<void>;
42
+ }>, conversationId?: string): Promise<unknown>;
43
43
  getPendingInterrupts?(conversationId: string): Promise<{
44
44
  interrupts: Array<{
45
45
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.hooks.d.ts","sourceRoot":"","sources":["../src/index.hooks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.hooks.d.ts","sourceRoot":"","sources":["../src/index.hooks.ts"],"names":[],"mappings":"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"}
@@ -1029,6 +1029,28 @@ describe('model-factory', () => {
1029
1029
  // ── useChat ──────────────────────────────────────────────────────────────────
1030
1030
  import { useChat } from './index.hooks.js';
1031
1031
  describe('useChat', () => {
1032
+ // Type-only regression guard for the api return-type contract (PR that widened
1033
+ // sendMessage/resume from Promise<void> to Promise<unknown>). This is compiled by
1034
+ // `tsc --build` before the runtime tests execute, so narrowing either member back
1035
+ // to Promise<void> fails CI here — the durable proof the manual PR check could not
1036
+ // commit. `unknown` must accept BOTH a natural object-returning backend and a
1037
+ // void-returning one; both assignments below must type-check.
1038
+ test('api sendMessage/resume accept object- and void-returning backends (type-only)', () => {
1039
+ const objectBackend = {
1040
+ sendMessage: async () => ({ channelId: 'c' }),
1041
+ createConversation: async () => ({ conversationId: 'c' }),
1042
+ getConversation: async () => ({ messages: [] }),
1043
+ resume: async () => ({ ok: true }),
1044
+ };
1045
+ const voidBackend = {
1046
+ sendMessage: async () => { },
1047
+ createConversation: async () => ({ conversationId: 'c' }),
1048
+ getConversation: async () => ({ messages: [] }),
1049
+ resume: async () => { },
1050
+ };
1051
+ assert.ok(objectBackend.sendMessage);
1052
+ assert.ok(voidBackend.sendMessage);
1053
+ });
1032
1054
  test('onError is called when error chunk arrives', async () => {
1033
1055
  let chunkHandler;
1034
1056
  let errorReceived;
package/dist/version.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export declare const BB_NAME = "Agent";
2
- export declare const BB_VERSION = "0.4.0";
2
+ export declare const BB_VERSION = "0.4.1";
3
3
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by scripts/generate-version.mjs — do not edit manually
2
2
  export const BB_NAME = 'Agent';
3
- export const BB_VERSION = '0.4.0';
3
+ export const BB_VERSION = '0.4.1';
package/package.json CHANGED
@@ -1,6 +1,14 @@
1
1
  {
2
2
  "name": "@aws-blocks/bb-agent",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
+ "keywords": [
5
+ "aws-blocks",
6
+ "ai",
7
+ "agent",
8
+ "llm",
9
+ "bedrock",
10
+ "chatbot"
11
+ ],
4
12
  "repository": {
5
13
  "type": "git",
6
14
  "url": "git+https://github.com/aws-devtools-labs/aws-blocks.git",
@@ -47,11 +55,11 @@
47
55
  "test": "node --test dist/index.test.js && node --conditions=cdk --test dist/index.cdk.test.js && node --test dist/agentcore-bundle.test.js"
48
56
  },
49
57
  "dependencies": {
50
- "@aws-blocks/bb-distributed-table": "^0.1.7",
51
- "@aws-blocks/bb-file-bucket": "^0.2.0",
52
- "@aws-blocks/bb-logger": "^0.1.6",
53
- "@aws-blocks/bb-realtime": "^0.2.0",
54
- "@aws-blocks/core": "^0.4.0",
58
+ "@aws-blocks/bb-distributed-table": "^0.2.0",
59
+ "@aws-blocks/bb-file-bucket": "^0.2.1",
60
+ "@aws-blocks/bb-logger": "^0.2.0",
61
+ "@aws-blocks/bb-realtime": "^0.2.1",
62
+ "@aws-blocks/core": "^0.5.0",
55
63
  "@aws-sdk/client-bedrock": "^3.700.0",
56
64
  "@aws-sdk/client-bedrock-agentcore": "^3.700.0",
57
65
  "@opentelemetry/api": "^1.9.0",
@@ -63,7 +71,7 @@
63
71
  "zod": "^4.1.12"
64
72
  },
65
73
  "devDependencies": {
66
- "@aws-blocks/bb-lambda-compute": "^0.4.0",
74
+ "@aws-blocks/bb-lambda-compute": "^0.5.0",
67
75
  "@aws-sdk/client-bedrock-runtime": "^3.700.0",
68
76
  "@modelcontextprotocol/sdk": "^1.12.1",
69
77
  "@types/node": "^20.0.0",
package/src/index.cdk.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
- import { Scope } from '@aws-blocks/core/cdk';
4
+ import { BuildingBlockScope } from '@aws-blocks/core/cdk';
5
5
  import type { ScopeParent } from '@aws-blocks/core';
6
6
  import { DistributedTable } from '@aws-blocks/bb-distributed-table';
7
7
  import { Realtime } from '@aws-blocks/bb-realtime';
8
8
  import { FileBucket } from '@aws-blocks/bb-file-bucket';
9
+ import * as ec2 from 'aws-cdk-lib/aws-ec2';
9
10
  import { AgentCoreRuntime } from './agentcore-runtime.cdk.js';
10
11
  import { messageSchema, conversationSchema, agentStreamChunkSchema } from './schemas.js';
11
12
  import type { AgentConfig } from './types.js';
@@ -13,7 +14,7 @@ import type { AgentConfig } from './types.js';
13
14
  export { AgentErrors } from './errors.js';
14
15
  export { BedrockModels, OllamaModels } from './models.js';
15
16
 
16
- export class Agent extends Scope {
17
+ export class Agent extends BuildingBlockScope {
17
18
  /**
18
19
  * CDK layer for the Agent BB.
19
20
  *
@@ -27,7 +28,7 @@ export class Agent extends Scope {
27
28
  * handler no longer needs Bedrock access — the runtime's own role gets it (see AgentCoreRuntime).
28
29
  */
29
30
  constructor(scope: ScopeParent, id: string, config?: AgentConfig) {
30
- super(id, { parent: scope });
31
+ super(id, { parent: scope, vpc: { interfaceEndpoints: [ec2.InterfaceVpcEndpointAwsService.BEDROCK_RUNTIME] } });
31
32
 
32
33
  // Session-snapshot bucket. Provisioned here (and granted to the shared execution role that the
33
34
  // AgentCore Runtime runs as); the deployed loop re-derives its name from this bucket's `fullId`
@@ -29,10 +29,10 @@ export interface ChatMessage {
29
29
  /** Options for creating a chat instance. */
30
30
  export interface UseChatOptions {
31
31
  api: {
32
- sendMessage(conversationId: string, message: string, channelId: string): Promise<void>;
32
+ sendMessage(conversationId: string, message: string, channelId: string): Promise<unknown>;
33
33
  createConversation(): Promise<{ conversationId: string }>;
34
34
  getConversation(id: string): Promise<{ messages: { role: string; content: string; metadata?: Record<string, any> }[] }>;
35
- resume?(channelId: string, responses: Array<{ interruptId: string; approved: boolean; trust?: boolean; toolName?: string; input?: any }>, conversationId?: string): Promise<void>;
35
+ resume?(channelId: string, responses: Array<{ interruptId: string; approved: boolean; trust?: boolean; toolName?: string; input?: any }>, conversationId?: string): Promise<unknown>;
36
36
  getPendingInterrupts?(conversationId: string): Promise<{ interrupts: Array<{ id: string; name: string; reason?: any }> }>;
37
37
  };
38
38
  /**
package/src/index.test.ts CHANGED
@@ -1185,8 +1185,32 @@ describe('model-factory', () => {
1185
1185
  // ── useChat ──────────────────────────────────────────────────────────────────
1186
1186
 
1187
1187
  import { useChat } from './index.hooks.js';
1188
+ import type { UseChatOptions } from './index.hooks.js';
1188
1189
 
1189
1190
  describe('useChat', () => {
1191
+ // Type-only regression guard for the api return-type contract (PR that widened
1192
+ // sendMessage/resume from Promise<void> to Promise<unknown>). This is compiled by
1193
+ // `tsc --build` before the runtime tests execute, so narrowing either member back
1194
+ // to Promise<void> fails CI here — the durable proof the manual PR check could not
1195
+ // commit. `unknown` must accept BOTH a natural object-returning backend and a
1196
+ // void-returning one; both assignments below must type-check.
1197
+ test('api sendMessage/resume accept object- and void-returning backends (type-only)', () => {
1198
+ const objectBackend: UseChatOptions['api'] = {
1199
+ sendMessage: async () => ({ channelId: 'c' }),
1200
+ createConversation: async () => ({ conversationId: 'c' }),
1201
+ getConversation: async () => ({ messages: [] }),
1202
+ resume: async () => ({ ok: true }),
1203
+ };
1204
+ const voidBackend: UseChatOptions['api'] = {
1205
+ sendMessage: async () => {},
1206
+ createConversation: async () => ({ conversationId: 'c' }),
1207
+ getConversation: async () => ({ messages: [] }),
1208
+ resume: async () => {},
1209
+ };
1210
+ assert.ok(objectBackend.sendMessage);
1211
+ assert.ok(voidBackend.sendMessage);
1212
+ });
1213
+
1190
1214
  test('onError is called when error chunk arrives', async () => {
1191
1215
  let chunkHandler: (chunk: any) => void;
1192
1216
  let errorReceived: string | undefined;
package/src/version.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // Auto-generated by scripts/generate-version.mjs — do not edit manually
2
2
  export const BB_NAME = 'Agent';
3
- export const BB_VERSION = '0.4.0';
3
+ export const BB_VERSION = '0.4.1';