{"_id":"@aws-blocks/bb-agent","_rev":"12-e1309b3578f0ffd8178a8612bb53264a","name":"@aws-blocks/bb-agent","dist-tags":{"latest":"0.4.1"},"versions":{"0.1.0":{"name":"@aws-blocks/bb-agent","version":"0.1.0","author":{"name":"Amazon Web Services"},"license":"Apache-2.0","_id":"@aws-blocks/bb-agent@0.1.0","maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"dist":{"shasum":"4fbf01ea07e5b92f0582c87035ddff29112e35a0","tarball":"https://registry.npmjs.org/@aws-blocks/bb-agent/-/bb-agent-0.1.0.tgz","fileCount":75,"integrity":"sha512-9u7J+aQm02niOic/OV9VgadOPhJc9hE9KYzs07/PSZ6z643+AtN6Ve2YZm8AOfZKj9OLY4bVao38ogQGZUDU4g==","signatures":[{"sig":"MEUCIAbHfHXkmhIavbWdUZh2pcxoFCR2ELNkj9saP9lsixs/AiEAr2708QK1mi9mWaQjk2kD2MMooAYDUFR4t+HI4rBhzgg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":360874},"type":"module","exports":{".":{"cdk":{"types":"./dist/index.cdk.d.ts","default":"./dist/index.cdk.js"},"types":"./dist/index.mock.d.ts","browser":"./dist/index.browser.js","default":"./dist/index.mock.js","aws-runtime":"./dist/index.aws.js"},"./client":{"types":"./dist/index.hooks.d.ts","default":"./dist/index.hooks.js"}},"gitHead":"639f6fad510d473abd72286351b3294a646d9895","scripts":{"test":"node --test dist/index.test.js","build":"tsc --build","prebuild":"node ../../scripts/generate-version.mjs Agent"},"_npmUser":{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"},"_npmVersion":"10.9.8","description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","directories":{},"_nodeVersion":"22.22.3","dependencies":{"zod":"^4.1.12","ulid":"^2.3.0","openai":"^6.7.0","@aws-blocks/core":"^0.1.0","@strands-agents/sdk":"~1.3.0","@aws-blocks/bb-logger":"^0.1.0","@aws-blocks/bb-realtime":"^0.1.0","@aws-sdk/client-bedrock":"^3.700.0","@aws-blocks/bb-async-job":"^0.1.0","@aws-blocks/bb-file-bucket":"^0.1.0","@aws-blocks/bb-distributed-table":"^0.1.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^20.0.0","@opentelemetry/api":"^1.9.0","@modelcontextprotocol/sdk":"^1.12.1","@aws-sdk/client-bedrock-runtime":"^3.700.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.257.0"},"_npmOperationalInternal":{"tmp":"tmp/bb-agent_0.1.0_1781566780175_0.9993856850528231","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aws-blocks/bb-agent","version":"0.1.1","author":{"name":"Amazon Web Services"},"license":"Apache-2.0","_id":"@aws-blocks/bb-agent@0.1.1","maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"dist":{"shasum":"4a825d9b6d195a4c750236dee95c7d62b06cc9be","tarball":"https://registry.npmjs.org/@aws-blocks/bb-agent/-/bb-agent-0.1.1.tgz","fileCount":75,"integrity":"sha512-VSOdwP/EMEEC51PT/AL51l1O+g4SWagg72ewe3vAs5oaiGlu3rb5NgBPdqD3BQkczoGl6udn5ssCTywUtCYsGg==","signatures":[{"sig":"MEUCIAMz5jzuqlaFLz9S01CGDXrOQn+okB700rds4Adb2+GCAiEAw4MrLb/+CJK6Mcw0Vm/UQaAd/z4y359QLjkeroL7Vxc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":360874},"type":"module","exports":{".":{"cdk":{"types":"./dist/index.cdk.d.ts","default":"./dist/index.cdk.js"},"types":"./dist/index.mock.d.ts","browser":"./dist/index.browser.js","default":"./dist/index.mock.js","aws-runtime":"./dist/index.aws.js"},"./client":{"types":"./dist/index.hooks.d.ts","default":"./dist/index.hooks.js"}},"gitHead":"ed3e0ad5724d28c2a2fb0ea9207dfc9e941257f9","scripts":{"test":"node --test dist/index.test.js","build":"tsc --build","prebuild":"node ../../scripts/generate-version.mjs Agent"},"_npmUser":{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"},"_npmVersion":"10.9.8","description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","directories":{},"_nodeVersion":"22.22.3","dependencies":{"zod":"^4.1.12","ulid":"^2.3.0","openai":"^6.7.0","@aws-blocks/core":"^0.1.1","@strands-agents/sdk":"~1.3.0","@aws-blocks/bb-logger":"^0.1.1","@aws-blocks/bb-realtime":"^0.1.1","@aws-sdk/client-bedrock":"^3.700.0","@aws-blocks/bb-async-job":"^0.1.1","@aws-blocks/bb-file-bucket":"^0.1.1","@aws-blocks/bb-distributed-table":"^0.1.1"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^20.0.0","@opentelemetry/api":"^1.9.0","@modelcontextprotocol/sdk":"^1.12.1","@aws-sdk/client-bedrock-runtime":"^3.700.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.257.0"},"_npmOperationalInternal":{"tmp":"tmp/bb-agent_0.1.1_1781633785557_0.5230768844363647","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@aws-blocks/bb-agent","version":"0.1.2","author":{"name":"Amazon Web Services"},"license":"Apache-2.0","_id":"@aws-blocks/bb-agent@0.1.2","maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"dist":{"shasum":"97804f0923a3ae7bcdd6b9bac18b9627ec8f3755","tarball":"https://registry.npmjs.org/@aws-blocks/bb-agent/-/bb-agent-0.1.2.tgz","fileCount":75,"integrity":"sha512-Zvg0kDzzD3i89TXeQMT3z8Ho8TVMKTzmpi6nKLfzjdLn4t8i9TECHsr5qfvvYHvnIQgvW4GbD3O6zhSdsW50Uw==","signatures":[{"sig":"MEQCIGOl7GGkU/aejUVGxWhhXe3WFcuQYhHDy1B5NLz8lwMxAiAFQl/lH4oqLYpzV3wei+CiGWnV0o/bjU8Gdi4umknP0w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":359904},"type":"module","exports":{".":{"cdk":{"types":"./dist/index.cdk.d.ts","default":"./dist/index.cdk.js"},"types":"./dist/index.mock.d.ts","browser":"./dist/index.browser.js","default":"./dist/index.mock.js","aws-runtime":"./dist/index.aws.js"},"./client":{"types":"./dist/index.hooks.d.ts","default":"./dist/index.hooks.js"}},"gitHead":"dda9c244684538292fdebd51821ae3c2ebd593e1","scripts":{"test":"node --test dist/index.test.js","build":"tsc --build","prebuild":"node ../../scripts/generate-version.mjs Agent"},"_npmUser":{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"},"_npmVersion":"10.9.8","description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","directories":{},"_nodeVersion":"22.22.3","dependencies":{"zod":"^4.1.12","ulid":"^2.3.0","openai":"^6.7.0","@aws-blocks/core":"^0.1.1","@strands-agents/sdk":"~1.3.0","@aws-blocks/bb-logger":"^0.1.1","@aws-blocks/bb-realtime":"^0.1.1","@aws-sdk/client-bedrock":"^3.700.0","@aws-blocks/bb-async-job":"^0.1.1","@aws-blocks/bb-file-bucket":"^0.1.1","@aws-blocks/bb-distributed-table":"^0.1.1"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^20.0.0","@opentelemetry/api":"^1.9.0","@modelcontextprotocol/sdk":"^1.12.1","@aws-sdk/client-bedrock-runtime":"^3.700.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.257.0"},"_npmOperationalInternal":{"tmp":"tmp/bb-agent_0.1.2_1781803590456_0.8026637898934836","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@aws-blocks/bb-agent","version":"0.1.3","author":{"name":"Amazon Web Services"},"license":"Apache-2.0","_id":"@aws-blocks/bb-agent@0.1.3","maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"dist":{"shasum":"7d2d1e6b38985c44a05d0c3189d6fd1091b635c2","tarball":"https://registry.npmjs.org/@aws-blocks/bb-agent/-/bb-agent-0.1.3.tgz","fileCount":76,"integrity":"sha512-nGUgSqb7rExUdRr4pAf9nOiy65Kl2KfhimaZZ+lssVTiSh87u8ts6eupRkeKkx/IFgjyP7SsHgIj58CDDEIj0g==","signatures":[{"sig":"MEQCIC2h65swu+mtuKHWeUvVnx4VPkcJZVlF7zhpJd9blPIKAiBrEi06S2fEa1Kf95cWGOHi9WGUiy08dtLnqa0bwYBQIA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":363242},"type":"module","exports":{".":{"cdk":{"types":"./dist/index.cdk.d.ts","default":"./dist/index.cdk.js"},"types":"./dist/index.mock.d.ts","browser":"./dist/index.browser.js","default":"./dist/index.mock.js","aws-runtime":"./dist/index.aws.js"},"./client":{"types":"./dist/index.hooks.d.ts","default":"./dist/index.hooks.js"}},"gitHead":"3328f36992d39dc20b0c0d760e4a38c866ddfbbd","scripts":{"test":"node --test dist/index.test.js","build":"tsc --build","prebuild":"node ../../scripts/generate-version.mjs Agent"},"_npmUser":{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"},"_npmVersion":"10.9.8","description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","directories":{},"_nodeVersion":"22.22.3","dependencies":{"zod":"^4.1.12","ulid":"^2.3.0","openai":"^6.7.0","@aws-blocks/core":"^0.1.1","@strands-agents/sdk":"~1.3.0","@aws-blocks/bb-logger":"^0.1.2","@aws-blocks/bb-realtime":"^0.1.2","@aws-sdk/client-bedrock":"^3.700.0","@aws-blocks/bb-async-job":"^0.1.2","@aws-blocks/bb-file-bucket":"^0.1.2","@aws-blocks/bb-distributed-table":"^0.1.3"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^20.0.0","@opentelemetry/api":"^1.9.0","@modelcontextprotocol/sdk":"^1.12.1","@aws-sdk/client-bedrock-runtime":"^3.700.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.257.0"},"_npmOperationalInternal":{"tmp":"tmp/bb-agent_0.1.3_1782192243553_0.7087572969800644","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@aws-blocks/bb-agent","version":"0.3.0","author":{"name":"Amazon Web Services"},"license":"Apache-2.0","_id":"@aws-blocks/bb-agent@0.3.0","maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"dist":{"shasum":"f5af96339c9881e46986a3b55d253b02830a37e6","tarball":"https://registry.npmjs.org/@aws-blocks/bb-agent/-/bb-agent-0.3.0.tgz","fileCount":76,"integrity":"sha512-XueNFepEnjPFbTLJ9TkXkzf4PS5PZDxndkt53PhSztB1YefQSytwjBnGyeJcXf5KIbrCFNtZQ2NTVb46JO8yNA==","signatures":[{"sig":"MEUCIQDBVWXFmM5/cp17n5h/jjYNwuF02reJ35ktzR4zT6d5agIgdfMTvISMIFvjyT9XKmkhiRGmV/8u2xRuNVB/XyWJ670=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":372027},"type":"module","exports":{".":{"cdk":{"types":"./dist/index.cdk.d.ts","default":"./dist/index.cdk.js"},"types":"./dist/index.mock.d.ts","browser":"./dist/index.browser.js","default":"./dist/index.mock.js","aws-runtime":"./dist/index.aws.js"},"./client":{"types":"./dist/index.hooks.d.ts","default":"./dist/index.hooks.js"}},"gitHead":"e8f38546b59a85fb707e0b751887f0cdeebf4fb0","scripts":{"test":"node --test dist/index.test.js","build":"tsc --build","prebuild":"node ../../scripts/generate-version.mjs Agent"},"_npmUser":{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"},"_npmVersion":"10.9.8","description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","directories":{},"_nodeVersion":"22.23.1","dependencies":{"zod":"^4.1.12","ulid":"^2.3.0","openai":"^6.7.0","@aws-blocks/core":"^0.1.10","@strands-agents/sdk":"~1.3.0","@aws-blocks/bb-logger":"^0.1.2","@aws-blocks/bb-realtime":"^0.1.2","@aws-sdk/client-bedrock":"^3.700.0","@aws-blocks/bb-async-job":"^0.1.2","@aws-blocks/bb-file-bucket":"^0.1.2","@aws-blocks/bb-distributed-table":"^0.1.3"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^20.0.0","@opentelemetry/api":"^1.9.0","@modelcontextprotocol/sdk":"^1.12.1","@aws-sdk/client-bedrock-runtime":"^3.700.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.257.0"},"_npmOperationalInternal":{"tmp":"tmp/bb-agent_0.3.0_1783033089165_0.7534879000232688","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"name":"@aws-blocks/bb-agent","version":"0.3.1","author":{"name":"Amazon Web Services"},"license":"Apache-2.0","_id":"@aws-blocks/bb-agent@0.3.1","maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"dist":{"shasum":"8d48782a0cb883032892d12bde37b35983adf765","tarball":"https://registry.npmjs.org/@aws-blocks/bb-agent/-/bb-agent-0.3.1.tgz","fileCount":76,"integrity":"sha512-PqEbCSH+3lRXUH+OW8uXdLfPJ8AujMbywv5issGHx8d5mErvQuiZz/bb9rpPWpvJtXukJN8Vc55dsrEoOe+/4w==","signatures":[{"sig":"MEYCIQCofPOyj7C+9Bs/a4mH8AaCz74bU/iambZQGxGpOFAvjwIhAM1jH6KfLFiBHOJqAsGS84nRcenlVJIds7OrQ5XMa5U0","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":380758},"type":"module","exports":{".":{"cdk":{"types":"./dist/index.cdk.d.ts","default":"./dist/index.cdk.js"},"types":"./dist/index.mock.d.ts","browser":"./dist/index.browser.js","default":"./dist/index.mock.js","aws-runtime":"./dist/index.aws.js"},"./client":{"types":"./dist/index.hooks.d.ts","default":"./dist/index.hooks.js"}},"gitHead":"8de709192c04c4bb9088402cd45473716ef42cdf","scripts":{"test":"node --test dist/index.test.js","build":"tsc --build","prebuild":"node ../../scripts/generate-version.mjs Agent"},"_npmUser":{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"},"_npmVersion":"10.9.8","description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","directories":{},"_nodeVersion":"22.23.1","dependencies":{"zod":"^4.1.12","ulid":"^2.3.0","openai":"^6.7.0","@aws-blocks/core":"^0.1.10","@strands-agents/sdk":"~1.3.0","@aws-blocks/bb-logger":"^0.1.2","@aws-blocks/bb-realtime":"^0.1.2","@aws-sdk/client-bedrock":"^3.700.0","@aws-blocks/bb-async-job":"^0.1.2","@aws-blocks/bb-file-bucket":"^0.1.2","@aws-blocks/bb-distributed-table":"^0.1.3"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^20.0.0","@opentelemetry/api":"^1.9.0","@modelcontextprotocol/sdk":"^1.12.1","@aws-sdk/client-bedrock-runtime":"^3.700.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.257.0"},"_npmOperationalInternal":{"tmp":"tmp/bb-agent_0.3.1_1783719871888_0.062125703221457496","host":"s3://npm-registry-packages-npm-production"}},"0.3.2":{"name":"@aws-blocks/bb-agent","version":"0.3.2","author":{"name":"Amazon Web Services"},"license":"Apache-2.0","_id":"@aws-blocks/bb-agent@0.3.2","maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"dist":{"shasum":"a7911f2a6fb3d18705a26852d2dcd11c6539ef6d","tarball":"https://registry.npmjs.org/@aws-blocks/bb-agent/-/bb-agent-0.3.2.tgz","fileCount":76,"integrity":"sha512-2EuW2aHpFB2pFx0GXLbZWVHxo3HtxJY+cXdJ5+fSMnGitu16W3lA8GeE3ChMUNgidXxyDehr+PfhR87Ka0jtJg==","signatures":[{"sig":"MEYCIQDwyYwKYou9u3+uyHnIWTgTVcSGMoAAvvBZ2hI5kgr5PgIhAK5hzx/jSqRcongHm1VjV5B1By9ZduZm3mJkBF48L5ml","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":383283},"type":"module","exports":{".":{"cdk":{"types":"./dist/index.cdk.d.ts","default":"./dist/index.cdk.js"},"types":"./dist/index.mock.d.ts","browser":"./dist/index.browser.js","default":"./dist/index.mock.js","aws-runtime":"./dist/index.aws.js"},"./client":{"types":"./dist/index.hooks.d.ts","default":"./dist/index.hooks.js"}},"gitHead":"090ce6109a2a78a7f9c5c9d1650a5822b964d3f0","scripts":{"test":"node --test dist/index.test.js","build":"tsc --build","prebuild":"node ../../scripts/generate-version.mjs Agent"},"_npmUser":{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"},"_npmVersion":"10.9.8","description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","directories":{},"_nodeVersion":"22.23.1","dependencies":{"zod":"^4.1.12","ulid":"^2.3.0","openai":"^6.7.0","@aws-blocks/core":"^0.1.10","@opentelemetry/api":"^1.9.0","@strands-agents/sdk":"~1.3.0","@aws-blocks/bb-logger":"^0.1.2","@aws-blocks/bb-realtime":"^0.1.2","@aws-sdk/client-bedrock":"^3.700.0","@aws-blocks/bb-async-job":"^0.1.2","@aws-blocks/bb-file-bucket":"^0.1.2","@aws-blocks/bb-distributed-table":"^0.1.3"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^20.0.0","@modelcontextprotocol/sdk":"^1.12.1","@aws-sdk/client-bedrock-runtime":"^3.700.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.257.0"},"_npmOperationalInternal":{"tmp":"tmp/bb-agent_0.3.2_1784238104183_0.18108955618891232","host":"s3://npm-registry-packages-npm-production"}},"0.3.3":{"name":"@aws-blocks/bb-agent","version":"0.3.3","author":{"name":"Amazon Web Services"},"license":"Apache-2.0","_id":"@aws-blocks/bb-agent@0.3.3","maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"homepage":"https://github.com/aws-devtools-labs/aws-blocks/tree/main/packages/bb-agent#readme","bugs":{"url":"https://github.com/aws-devtools-labs/aws-blocks/issues"},"dist":{"shasum":"5b81e051f6735064b1b6a50b93161c88c3cf5993","tarball":"https://registry.npmjs.org/@aws-blocks/bb-agent/-/bb-agent-0.3.3.tgz","fileCount":76,"integrity":"sha512-TgoW1jws5fPm3DNc1bwJb87KpfhUvHrC6YxGuejm2fUexsBpdHsHKKnq4TlC7AFlAYtFKEPM80wEDOB39KDSyQ==","signatures":[{"sig":"MEUCIBVm/B0L9cEFi8bJyKii5jTt/BzsaPUlAMiGnp+n9ngAAiEApfHnrCGqiAOuyafrgl/rhp9cL+95hwVL2pfm5DsN28Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aws-blocks%2fbb-agent@0.3.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":383617},"type":"module","exports":{".":{"cdk":{"types":"./dist/index.cdk.d.ts","default":"./dist/index.cdk.js"},"types":"./dist/index.mock.d.ts","browser":"./dist/index.browser.js","default":"./dist/index.mock.js","aws-runtime":"./dist/index.aws.js"},"./client":{"types":"./dist/index.hooks.d.ts","default":"./dist/index.hooks.js"}},"gitHead":"f6b9cad7b543663cc9ea73a4476ca5511255d779","scripts":{"test":"node --test dist/index.test.js","build":"tsc --build","prebuild":"node ../../scripts/generate-version.mjs Agent"},"_npmUser":{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"},"repository":{"url":"git+https://github.com/aws-devtools-labs/aws-blocks.git","type":"git","directory":"packages/bb-agent"},"_npmVersion":"10.9.8","description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","directories":{},"_nodeVersion":"22.23.1","dependencies":{"zod":"^4.1.12","ulid":"^2.3.0","openai":"^6.7.0","@aws-blocks/core":"^0.1.17","@opentelemetry/api":"^1.9.0","@strands-agents/sdk":"~1.3.0","@aws-blocks/bb-logger":"^0.1.3","@aws-blocks/bb-realtime":"^0.1.2","@aws-sdk/client-bedrock":"^3.700.0","@aws-blocks/bb-async-job":"^0.1.3","@aws-blocks/bb-file-bucket":"^0.1.3","@aws-blocks/bb-distributed-table":"^0.1.4"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^20.0.0","@modelcontextprotocol/sdk":"^1.12.1","@aws-sdk/client-bedrock-runtime":"^3.700.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.257.0"},"_npmOperationalInternal":{"tmp":"tmp/bb-agent_0.3.3_1786135407788_0.5680330897143653","host":"s3://npm-registry-packages-npm-production"}},"0.3.4":{"name":"@aws-blocks/bb-agent","version":"0.3.4","author":{"name":"Amazon Web Services"},"license":"Apache-2.0","_id":"@aws-blocks/bb-agent@0.3.4","maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"homepage":"https://github.com/aws-devtools-labs/aws-blocks/tree/main/packages/bb-agent#readme","bugs":{"url":"https://github.com/aws-devtools-labs/aws-blocks/issues"},"dist":{"shasum":"174f52c47313b6a440e4d21e2c4124898fe3584d","tarball":"https://registry.npmjs.org/@aws-blocks/bb-agent/-/bb-agent-0.3.4.tgz","fileCount":76,"integrity":"sha512-ZVg5pd+jEzk9JCLq7+CMzzxFTX88cI1qMGZe8bHpWc9LOkysgSKNangDwPWqzns+fV7aD8bBa/EUzhGOQO4lOg==","signatures":[{"sig":"MEUCICjKktMPuPFAHTa7PXJjneqZR15owH+BHmymh2jvm5NdAiEA7/Rk7kQdiktlQuE1Um55cKu8a8k0RRycgTH1T81lv9Q=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aws-blocks%2fbb-agent@0.3.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":383616},"type":"module","exports":{".":{"cdk":{"types":"./dist/index.cdk.d.ts","default":"./dist/index.cdk.js"},"types":"./dist/index.mock.d.ts","browser":"./dist/index.browser.js","default":"./dist/index.mock.js","aws-runtime":"./dist/index.aws.js"},"./client":{"types":"./dist/index.hooks.d.ts","default":"./dist/index.hooks.js"}},"gitHead":"164b106810e2b4f7801426158feae82a4ccbe3d4","scripts":{"test":"node --test dist/index.test.js","build":"tsc --build","prebuild":"node ../../scripts/generate-version.mjs Agent"},"_npmUser":{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"},"repository":{"url":"git+https://github.com/aws-devtools-labs/aws-blocks.git","type":"git","directory":"packages/bb-agent"},"_npmVersion":"10.9.8","description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","directories":{},"_nodeVersion":"22.23.2","dependencies":{"zod":"^4.1.12","ulid":"^2.3.0","openai":"^6.7.0","@aws-blocks/core":"^0.2.0","@opentelemetry/api":"^1.9.0","@strands-agents/sdk":"~1.3.0","@aws-blocks/bb-logger":"^0.1.4","@aws-blocks/bb-realtime":"^0.1.4","@aws-sdk/client-bedrock":"^3.700.0","@aws-blocks/bb-async-job":"^0.1.4","@aws-blocks/bb-file-bucket":"^0.1.4","@aws-blocks/bb-distributed-table":"^0.1.5"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^20.0.0","@modelcontextprotocol/sdk":"^1.12.1","@aws-sdk/client-bedrock-runtime":"^3.700.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.257.0"},"_npmOperationalInternal":{"tmp":"tmp/bb-agent_0.3.4_1787230478168_0.2962689077044731","host":"s3://npm-registry-packages-npm-production"}},"0.3.5":{"name":"@aws-blocks/bb-agent","version":"0.3.5","author":{"name":"Amazon Web Services"},"license":"Apache-2.0","_id":"@aws-blocks/bb-agent@0.3.5","maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"homepage":"https://github.com/aws-devtools-labs/aws-blocks/tree/main/packages/bb-agent#readme","bugs":{"url":"https://github.com/aws-devtools-labs/aws-blocks/issues"},"dist":{"shasum":"3425c719c88f3c4b53593a76312c25bf997253ab","tarball":"https://registry.npmjs.org/@aws-blocks/bb-agent/-/bb-agent-0.3.5.tgz","fileCount":84,"integrity":"sha512-T9Tmg5fhqBTVCsYCCfnxIAnyEpW/lFPMRXFkyYrLywM7ilmxNVpHJ0cTrVGSh8sfDXVnnc+cNKBqTL54vtugKA==","signatures":[{"sig":"MEYCIQCwq1aaeLJLJnuN8Qba+sQn6fFU4V9f/DjmQ2EWzszpsgIhAIzEnyMo1iPnc61nsjx0fHlhg0zL+1Y26b5kuyZtnijw","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aws-blocks%2fbb-agent@0.3.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":394021},"type":"module","exports":{".":{"cdk":{"types":"./dist/index.cdk.d.ts","default":"./dist/index.cdk.js"},"types":"./dist/index.mock.d.ts","browser":"./dist/index.browser.js","default":"./dist/index.mock.js","aws-runtime":"./dist/index.aws.js"},"./client":{"types":"./dist/index.hooks.d.ts","default":"./dist/index.hooks.js"}},"gitHead":"04d4b21b1cae9600c44a2fc63c0020ca21894316","scripts":{"test":"node --test dist/index.test.js && node --conditions=cdk --test dist/index.cdk.test.js","build":"tsc --build","prebuild":"node ../../scripts/generate-version.mjs Agent"},"_npmUser":{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"},"repository":{"url":"git+https://github.com/aws-devtools-labs/aws-blocks.git","type":"git","directory":"packages/bb-agent"},"_npmVersion":"10.9.8","description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","directories":{},"_nodeVersion":"22.23.2","dependencies":{"zod":"^4.1.12","ulid":"^2.3.0","openai":"^6.7.0","@aws-blocks/core":"^0.3.0","@opentelemetry/api":"^1.9.0","@strands-agents/sdk":"~1.3.0","@aws-blocks/bb-logger":"^0.1.5","@aws-blocks/bb-realtime":"^0.1.5","@aws-sdk/client-bedrock":"^3.700.0","@aws-blocks/bb-async-job":"^0.1.5","@aws-blocks/bb-file-bucket":"^0.1.5","@aws-blocks/bb-distributed-table":"^0.1.6"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^20.0.0","@modelcontextprotocol/sdk":"^1.12.1","@aws-sdk/client-bedrock-runtime":"^3.700.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.257.0"},"_npmOperationalInternal":{"tmp":"tmp/bb-agent_0.3.5_1788264356214_0.5538004335828788","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@aws-blocks/bb-agent","version":"0.4.0","author":{"name":"Amazon Web Services"},"license":"Apache-2.0","_id":"@aws-blocks/bb-agent@0.4.0","maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"homepage":"https://github.com/aws-devtools-labs/aws-blocks/tree/main/packages/bb-agent#readme","bugs":{"url":"https://github.com/aws-devtools-labs/aws-blocks/issues"},"dist":{"shasum":"aac77ac82d142da2a279505c3eb9232eee525f32","tarball":"https://registry.npmjs.org/@aws-blocks/bb-agent/-/bb-agent-0.4.0.tgz","fileCount":96,"integrity":"sha512-QNhd7prxsj/khM/RKsd2q7xJPGSBUnbYRDr6YTFEt42QPQkKlr1ihEHrHyzQ34zbTYoJBIQXlcd4etKmdFm0TA==","signatures":[{"sig":"MEUCIQDejLX6yhi4VsTEn2dZDPyrSikXJs7i5IHRAzlbDo8FXgIgB85LfXeULjFrIBoZxq07ID3BDvlkPDw76gfMgiqb5Pc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aws-blocks%2fbb-agent@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":585840},"type":"module","exports":{".":{"cdk":{"types":"./dist/index.cdk.d.ts","default":"./dist/index.cdk.js"},"types":"./dist/index.mock.d.ts","browser":"./dist/index.browser.js","default":"./dist/index.mock.js","aws-runtime":"./dist/index.aws.js"},"./client":{"types":"./dist/index.hooks.d.ts","default":"./dist/index.hooks.js"},"./agentcore":{"types":"./dist/agentcore-entry.d.ts","default":"./dist/agentcore-entry.js"}},"gitHead":"ce01923c591828486e07d3d3f2b6b9d585e39057","scripts":{"test":"node --test dist/index.test.js && node --conditions=cdk --test dist/index.cdk.test.js && node --test dist/agentcore-bundle.test.js","build":"tsc --build","prebuild":"node ../../scripts/generate-version.mjs Agent"},"_npmUser":{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"},"repository":{"url":"git+https://github.com/aws-devtools-labs/aws-blocks.git","type":"git","directory":"packages/bb-agent"},"_npmVersion":"10.9.8","description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","directories":{},"_nodeVersion":"22.23.2","dependencies":{"zod":"^4.1.12","ulid":"^2.3.0","openai":"^6.7.0","esbuild":"^0.27.0","@aws-blocks/core":"^0.4.0","bedrock-agentcore":"^0.4.0","@opentelemetry/api":"^1.9.0","@strands-agents/sdk":"^1.7.0","@aws-blocks/bb-logger":"^0.1.6","@aws-blocks/bb-realtime":"^0.2.0","@aws-sdk/client-bedrock":"^3.700.0","@aws-blocks/bb-file-bucket":"^0.2.0","@aws-blocks/bb-distributed-table":"^0.1.7","@aws-sdk/client-bedrock-agentcore":"^3.700.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^20.0.0","@modelcontextprotocol/sdk":"^1.12.1","@aws-blocks/bb-lambda-compute":"^0.4.0","@aws-sdk/client-bedrock-runtime":"^3.700.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.257.0"},"_npmOperationalInternal":{"tmp":"tmp/bb-agent_0.4.0_1788867510431_0.10557781325714721","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"_id":"@aws-blocks/bb-agent@0.4.1","bugs":{"url":"https://github.com/aws-devtools-labs/aws-blocks/issues"},"dist":{"shasum":"66fa185729e75fd2b46456c0aa92d9895290c89e","tarball":"https://registry.npmjs.org/@aws-blocks/bb-agent/-/bb-agent-0.4.1.tgz","fileCount":96,"integrity":"sha512-eHGGElXPsnw9IYQaX1pp247AjYpKuQ1AbQSVuvU8D/rCYgSt4pHcFTc2I9pzrq7yLTCVh+mTkorwmENlGCzk/w==","signatures":[{"sig":"MEUCIQDU/AzlC2eNSQMFfmNWT7ajpSI685eSYMQHF0/WZvE4TwIgSjYXOzhO8dG22rxK1m+50oEk2N3AbFancGjDKyAEFNE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCpS1s1pwfbZHthrBO+3odOI0i9lEMLzFSZNGN8B9IzdQIhAIZUKXMts2V010zJUZ4rrllQQuMOMYIbdLH9LJIqx7de"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aws-blocks%2fbb-agent@0.4.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":593929},"name":"@aws-blocks/bb-agent","type":"module","author":{"name":"Amazon Web Services"},"exports":{".":{"cdk":{"types":"./dist/index.cdk.d.ts","default":"./dist/index.cdk.js"},"types":"./dist/index.mock.d.ts","browser":"./dist/index.browser.js","default":"./dist/index.mock.js","aws-runtime":"./dist/index.aws.js"},"./client":{"types":"./dist/index.hooks.d.ts","default":"./dist/index.hooks.js"},"./agentcore":{"types":"./dist/agentcore-entry.d.ts","default":"./dist/agentcore-entry.js"}},"gitHead":"51eeb265ab5efdbd853e989cf8bd6d30951c29c7","license":"Apache-2.0","scripts":{"test":"node --test dist/index.test.js && node --conditions=cdk --test dist/index.cdk.test.js && node --test dist/agentcore-bundle.test.js","build":"tsc --build","prebuild":"node ../../scripts/generate-version.mjs Agent"},"version":"0.4.1","_npmUser":{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"},"homepage":"https://github.com/aws-devtools-labs/aws-blocks/tree/main/packages/bb-agent#readme","keywords":["aws-blocks","ai","agent","llm","bedrock","chatbot"],"repository":{"url":"git+https://github.com/aws-devtools-labs/aws-blocks.git","type":"git","directory":"packages/bb-agent"},"_npmVersion":"10.9.8","description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","directories":{},"maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"_nodeVersion":"22.23.2","dependencies":{"zod":"^4.1.12","ulid":"^2.3.0","openai":"^6.7.0","esbuild":"^0.27.0","@aws-blocks/core":"^0.5.0","bedrock-agentcore":"^0.4.0","@opentelemetry/api":"^1.9.0","@strands-agents/sdk":"^1.7.0","@aws-blocks/bb-logger":"^0.2.0","@aws-blocks/bb-realtime":"^0.2.1","@aws-sdk/client-bedrock":"^3.700.0","@aws-blocks/bb-file-bucket":"^0.2.1","@aws-blocks/bb-distributed-table":"^0.2.0","@aws-sdk/client-bedrock-agentcore":"^3.700.0"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.0","@types/node":"^20.0.0","@modelcontextprotocol/sdk":"^1.12.1","@aws-blocks/bb-lambda-compute":"^0.5.0","@aws-sdk/client-bedrock-runtime":"^3.700.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.257.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bb-agent_0.4.1_1789678884600_0.8050269972309843"}}},"time":{"created":"2026-06-15T23:39:40.044Z","modified":"2026-09-17T21:01:25.178Z","0.1.0":"2026-06-15T23:39:40.348Z","0.1.1":"2026-06-16T18:16:25.746Z","0.1.2":"2026-06-18T17:26:30.597Z","0.1.3":"2026-06-23T05:24:03.785Z","0.3.0":"2026-07-02T22:58:09.351Z","0.3.1":"2026-07-10T21:44:32.022Z","0.3.2":"2026-07-16T21:41:44.329Z","0.3.3":"2026-08-07T20:43:27.973Z","0.3.4":"2026-08-20T12:54:38.319Z","0.3.5":"2026-09-01T12:05:56.364Z","0.4.0":"2026-09-08T11:38:30.588Z","0.4.1":"2026-09-17T21:01:24.793Z"},"bugs":{"url":"https://github.com/aws-devtools-labs/aws-blocks/issues"},"author":{"name":"Amazon Web Services"},"license":"Apache-2.0","homepage":"https://github.com/aws-devtools-labs/aws-blocks/tree/main/packages/bb-agent#readme","repository":{"url":"git+https://github.com/aws-devtools-labs/aws-blocks.git","type":"git","directory":"packages/bb-agent"},"description":"AI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).","maintainers":[{"name":"aws-blocks-npm-ops","email":"aws-blocks@amazon.com"}],"readme":"# @aws-blocks/bb-agent\n\nAI agent with streaming, tool calling, and conversation persistence. Powered by [Strands Agents SDK](https://strandsagents.com/).\n\n**When to use:** Conversational AI experiences — chatbots, copilots, data extraction, or any LLM-powered feature. Supports multi-turn conversations, tool calling with Zod schemas, and multiple model providers.\n\n**Requires:** `zod` ^4.0.0 as a peer dependency. Tool parameters use Zod schemas for validation. If you see `ZodType missing properties` errors, check your zod version.\n\n> Design & mock parity details: [DESIGN.md](./DESIGN.md)\n\n## Quick Start\n\n```typescript\nimport { Scope } from '@aws-blocks/core';\nimport { Agent } from '@aws-blocks/bb-agent';\n\nconst scope = new Scope('my-app');\n\nconst agent = new Agent(scope, 'support-agent', {\n  systemPrompt: 'You are a helpful support agent.',\n});\n\n// Create a conversation and stream a response\nconst conversationId = await agent.createConversationId('user-123');\nconst channel = await agent.getChannel(conversationId);\nconst sub = channel.subscribe((chunk) => { /* handle chunk */ });\nawait sub.established;\nconst result = await agent.stream('Until when are you open tomorrow?', { conversationId, userId: 'user-123' });\nconst done = await result.complete();\nconsole.log(done.text); // \"We're open until 6pm tomorrow.\"\n```\nUses [`BedrockModels.BALANCED`](#bedrock-presets) (Claude Sonnet 4.6) by default. See [Model Configuration](#model-configuration) for other presets, [Tools](#tools) for adding capabilities, and [Local Development](#local-development) for running without AWS Bedrock.\n\n## API\n\n```typescript\nconst agent = new Agent(scope, id, config)\n```\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `stream(message, options?)` | `Promise<AgentStreamResult>` | Submit a message. Returns immediately with `{ channelId, channel, complete }`. |\n| `resume(channelId, responses, options?)` | `Promise<void>` | Resume an interrupted agent with user responses. Chunks publish to the same channel. |\n| `createConversationId(userId)` | `Promise<string>` | Generate a new conversation ID (UUID). |\n| `getConversation(id, options?)` | `Promise<Message[]>` | Get messages in a conversation. Pass `{ limit }` for most recent N. |\n| `listConversations(userId)` | `Promise<Conversation[]>` | List all conversations for a user. |\n| `deleteConversation(id, userId)` | `Promise<void>` | Delete a conversation and its session data. |\n| `getPendingInterrupts(conversationId)` | `Promise<Array<...>>` | Get unanswered interrupts (for reload support). |\n| `getChannel(channelId)` | `Promise<RealtimeChannel>` | Get a Realtime channel for subscribing to chunks. |\n\n`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.\n\n**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:\n\n```typescript\n// Correct: subscribe first, await established, then send\nconst channel = await agent.getChannel(conversationId);\nconst sub = channel.subscribe((chunk) => { /* handle chunk */ });\nawait sub.established;\nawait agent.stream(message, { conversationId, userId });\n\n// Wrong: send first, subscribe after — early chunks lost\nawait agent.stream(message, { conversationId, userId });\nconst channel = await agent.getChannel(conversationId); // too late!\n```\n\nThe `useChat` hook (see [Client Hook](#client-hook--usechat)) handles this ordering automatically. Use it instead of hand-rolling stream logic.\n\n### Authorization (caller responsibility)\n\nThe Agent BB scopes data by `conversationId`, which is an unguessable UUID, but it does **not** authorize the caller against a conversation on read paths. `getConversation(id)` and `getPendingInterrupts(conversationId)` take only an id, so any caller that supplies a valid conversation ID gets the messages back.\n\nYour API handler owns authorization: derive `userId` from the authenticated session and verify the conversation belongs to that user before reading it. `listConversations(userId)` returns only the conversations a user owns, so it's the safe way to resolve which conversation IDs a caller may access:\n\n```typescript\nexport const api = new ApiNamespace(scope, 'api', (context) => ({\n  async getMessages(conversationId: string) {\n    const user = await auth.getCurrentUser(context);\n    const owned = await agent.listConversations(user.userId);\n    if (!owned.some(c => c.conversationId === conversationId)) {\n      throw new Error('Not found');\n    }\n    return agent.getConversation(conversationId);\n  },\n}));\n```\n\n`deleteConversation(id, userId)` is owner-scoped internally — it verifies the conversation belongs to `userId` before deleting anything, so a non-owner call is a no-op.\n\n### AgentStreamResult\n\nReturned by `stream()`. Provides the Realtime channel and convenience methods:\n\n| Property/Method | Type | Description |\n|--------|------|-------------|\n| `channelId` | `string` | Realtime channel where chunks are published. |\n| `channel` | `Promise<RealtimeChannel>` | Realtime channel handle — `await` it, then call `.subscribe(handler)`. |\n| `complete()` | `Promise<AgentStreamChunk>` | Wait for the done chunk (full text + token usage). |\n\n### AgentStreamChunk\n\nEach chunk published to the Realtime channel has a `type` and type-specific fields:\n\n| Type | Fields | Description |\n|------|--------|-------------|\n| `text-delta` | `text: string` | Incremental text token (in `'token'` streaming mode) or full block (in `'block'` mode). |\n| `tool-call` | `toolName: string`, `input: JSONValue` | Agent is calling a tool. |\n| `tool-result` | `toolName: string`, `text: string` | Tool returned a result. |\n| `done` | `text: string`, `usage: TokenUsage` | Agent finished. `text` contains the full response. `usage` has `{ inputTokens, outputTokens, totalTokens }`. |\n| `error` | `error: string` | Agent encountered an error. |\n| `interrupt` | `interrupts: Array<{ id, name, reason }>` | Agent paused for approval. See [Tool Approval](#tool-approval-human-in-the-loop). |\n\n### Message Roles\n\nMessages stored in conversation history use these roles:\n\n| Role | Description |\n|------|-------------|\n| `user` | User message. |\n| `assistant` | Agent response text. |\n| `tool-call` | Record of a tool invocation (stored for audit). |\n| `tool-result` | Record of a tool's return value. |\n| `approval` | User's approval/denial response to an interrupt. |\n| `interrupt` | Agent paused — snapshot of pending interrupts. |\n\nThe `useChat` hook only surfaces `user`, `assistant`, and `approval` messages to the UI. Use `agent.getConversation()` directly to access the full history including tool-call/tool-result records.\n\n### AgentConfig\n\n| Option | Type | Description |\n|--------|------|----------------------------------------------------------------------|\n| `model` | `{ deployed, local? }` | Model configuration (see below). |\n| `systemPrompt` | `string` | System prompt for the agent. |\n| `tools` | `(tool) => Record<string, AgentTool>` | Tools the agent can call during reasoning. |\n| `toolContextSchema` | `z.ZodType` | Optional schema for per-call tool context. When set, `context` is required and typed. |\n| `inferenceOnly` | `boolean` | Skip persistence infra. Default: `false`. |\n| `conversation` | `ConversationManagerConfig` | How the agent trims message history (sliding-window or summarizing). |\n| `streamingMode` | `'token' \\| 'block'` | How text chunks are published to the client. Default: `'block'`. |\n| `maxLlmCalls` | `number \\| false` | Max model invocations per turn before the turn is stopped; `false` disables. Default: `20`. See [Limiting runaway cost](#limiting-runaway-cost). |\n| `maxToolIterations` | `number \\| false` | Max tool calls per turn before the turn is stopped; `false` disables. Default: `20`. See [Limiting runaway cost](#limiting-runaway-cost). |\n\n### Model Configuration\n\nModel configuration is optional. When omitted, the agent defaults to `BedrockModels.BALANCED` (Claude Sonnet 4.6) for deployment. Local development works out of the box — the canned provider (keyword-based mock) is used automatically when no local model is specified.\n\n| Option | Type | Description |\n|--------|------|-------------|\n| `provider` | `'bedrock' \\| 'openai-api' \\| 'canned'` | Model provider. |\n| `modelId` | `string` | Model ID. Required for bedrock and openai-api. |\n| `endpoint` | `string` | API endpoint. For openai-api (defaults to api.openai.com). |\n| `apiKey` | `string \\| () => Promise<string>` | API key for openai-api. Accepts a string or async resolver. Falls back to `OPENAI_API_KEY` env var. |\n| `inferenceConfig` | `{ temperature?, topP?, maxTokens?, stopSequences? }` | Optional inference parameters. |\n\n```typescript\nimport { Agent } from '@aws-blocks/bb-agent';\n\n// Minimal — just deployed model, canned provider used locally automatically\nconst agent = new Agent(scope, 'agent', {\n  model: {\n    deployed: { provider: 'bedrock', modelId: '...' },\n  },\n  systemPrompt: '...',\n});\n```\n\nSpecify a model for local development to use instead of the canned provider:\n\n```typescript\nconst agent = new Agent(scope, 'agent', {\n  model: {\n    deployed: { provider: 'bedrock', modelId: '...' },\n    local: { provider: 'openai-api', modelId: 'llama3.1:8b', endpoint: 'http://localhost:11434/v1', apiKey: 'ollama' },\n  },\n  systemPrompt: '...',\n});\n```\n\nFor fallback support, provide an array of candidates. They are tried in order — the first available model wins. Health checks verify each candidate before selecting it (see [Health Checks](#health-checks)):\n\n```typescript\nmodel: {\n  deployed: [\n    { provider: 'bedrock', modelId: '...' },\n    { provider: 'bedrock', modelId: '...' },\n    { provider: 'canned' },  // canned can be used in deployed as a last resort\n  ],\n  local: [\n    { provider: 'openai-api', modelId: 'llama3.2:3b', endpoint: 'http://localhost:11434/v1' },\n    // canned is always appended implicitly as last fallback for local\n  ],\n}\n```\n\n#### Bedrock Presets\n\nPre-configured model presets for quick setup. Names are capability-based so the underlying model can be upgraded without breaking your code. These use [global inference profiles](https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html) — requests may be routed to any supported AWS region for optimal throughput. If your workload has data residency requirements, specify a region-scoped inference profile explicitly instead of using a preset.\n\n```typescript\nimport { Agent, BedrockModels} from '@aws-blocks/bb-agent';\n\nconst agent = new Agent(scope, 'agent', {\n  model: {\n    deployed: BedrockModels.BALANCED,\n  },\n  systemPrompt: '...',\n});\n```\n\n| Preset | Current Model | Notes |\n|--------|---------------|-------|\n| `BedrockModels.BALANCED` | `global.anthropic.claude-sonnet-4-6` | Great tool use, balanced cost. Recommended default for most workloads. |\n| `BedrockModels.SMART` | `global.anthropic.claude-opus-4-8` | Highest capability for the hardest tasks. |\n| `BedrockModels.FAST` | `global.anthropic.claude-haiku-4-5-20251001-v1:0` | Lowest latency, still strong capabilities. |\n\n> **Migrating?** `DEFAULT` → `BALANCED` (or `SMART` for highest capability). `BUDGET`/`MICRO` → `FAST`. The old presets are still available but deprecated. Please consider upgrading!\n\nOverride inference settings with spread:\n```typescript\nmodel: { deployed: { ...BedrockModels.BALANCED, inferenceConfig: { temperature: 0.9, maxTokens: 8192 } } }\n```\n\n#### Ollama Presets\n\nConvenience shortcuts for local development using [Ollama](https://ollama.com/). Requires Ollama installed and running (`ollama serve`), model pulled (`ollama pull <model-id>`). Uses the default endpoint `http://localhost:11434/v1`.\n\n```typescript\nimport { Agent, BedrockModels, OllamaModels} from '@aws-blocks/bb-agent';\n\nconst agent = new Agent(scope, 'agent', {\n  model: {\n    deployed: BedrockModels.BALANCED, \n    local: OllamaModels.SMALL,\n  },\n  systemPrompt: '...',\n});\n```\n\n| Preset | Current Model | Size | Recommended VRAM |\n|--------|---------------|------|------------------|\n| `OllamaModels.XSMALL` | `llama3.2:3b` | 2 GB | 4 GB |\n| `OllamaModels.SMALL` | `llama3.1:8b` | 4.7 GB | 8 GB |\n| `OllamaModels.MEDIUM` | `deepseek-r1:14b` | 9 GB | 16 GB |\n| `OllamaModels.LARGE` | `llama3.3:70b` | 43 GB | 48 GB+ |\n| `OllamaModels.XLARGE` | `llama4:16x17b` | 67 GB | 80 GB+ |\n\nCustom endpoint or specific model? Use `openai-api` directly:\n```typescript\nmodel: { local: { provider: 'openai-api', modelId: 'llama3.1:8b', endpoint: 'http://custom-host:11434/v1', apiKey: 'ollama' } }\n```\nSee [Ollama Presets](#ollama-presets) and [Local Development](#local-development) for more options.\n\n#### Health Checks\n\nBefore selecting a model, the agent verifies its availability:\n\n- **Bedrock:** Verifies model availability via `@aws-sdk/client-bedrock` (free, no inference cost).\n- **OpenAI-compatible:** Pings `GET /v1/models` and checks if the specified model ID is in the response.\n- **Canned:** Always available (no external dependency).\n\nHealth checks verify the model *exists* but cannot guarantee invoke access (e.g., EULA not accepted, quota limits). If all candidates fail, the agent throws `AgentErrors.ModelUnavailable`. Check logs for details.\n\nTo see detailed health check logs, pass a logger with `info` level:\n\n```typescript\nimport { Logger } from '@aws-blocks/bb-logger';\n\nconst agent = new Agent(scope, 'agent', {\n  model: { deployed: BedrockModels.BALANCED },\n  systemPrompt: '...',\n  logger: new Logger(scope, 'agent-log', { level: 'info' }),\n});\n```\n\n#### API Key Management\n\n```typescript\n// Recommended: AppSetting with secret (encrypted via SSM SecureString)\nconst openaiKey = new AppSetting(scope, 'openai-key', {\n  name: '/myapp/openai-api-key',\n  secret: true,\n});\n\nconst agent = new Agent(scope, 'agent', {\n  model: {\n    deployed: {\n      provider: 'openai-api',\n      modelId: 'gpt-4',\n      apiKey: () => openaiKey.get(),\n    },\n  },\n});\n\n// Alternative: environment variable (local dev)\n// Set OPENAI_API_KEY — no apiKey needed in config\n\n// Alternative: plain string (discouraged — leaks in source control)\n// apiKey: 'sk-...'\n```\n\n#### AWS Credentials (Bedrock)\n\nThe `bedrock` provider uses your configured AWS credentials. See [Strands quickstart](https://strandsagents.com/docs/user-guide/quickstart/typescript/#configuring-credentials) for setup instructions.\n\n#### Bedrock via Mantle\n\nAmazon Bedrock exposes an OpenAI-compatible endpoint via [Bedrock Mantle](https://docs.aws.amazon.com/bedrock/latest/userguide/bedrock-mantle.html). Use it with `provider: 'openai-api'` and set the endpoint to `https://bedrock-mantle.<region>.api.aws/v1`.\n\n### Error Handling\n\n```typescript\nimport { isBlocksError } from '@aws-blocks/core';\nimport { AgentErrors } from '@aws-blocks/bb-agent';\n\ntry {\n  await agent.getConversation(id);\n} catch (e: unknown) {\n  if (isBlocksError(e, AgentErrors.PersistenceRequired)) {\n    // agent is in inferenceOnly mode\n  }\n}\n```\n\n| Error | When |\n|-------|------|\n| `AgentErrors.PersistenceRequired` | Conversation CRUD called on an inferenceOnly agent. |\n| `AgentErrors.InvalidModelConfig` | Missing modelId, apiKey, unknown provider, or `needsApproval` + `interrupt` both specified. |\n| `AgentErrors.ModelUnavailable` | All model candidates failed health checks. Check logs for details. |\n| `AgentErrors.StreamFailed` | Agent encountered an error during execution. |\n| `AgentErrors.InterruptRequired` | Agent paused for approval. Use `InterruptError` for typed access to pending interrupts. |\n| `AgentErrors.BrowserNotSupported` | Agent instantiated in the browser (server-side only). |\n\n### Streaming Mode\n\nControls how text is published to the client:\n\n- **`'block'` (default)** — buffers text and publishes when a full content block completes.\n- **`'token'`** — publishes every text delta immediately as it arrives. Use for typewriter-style UIs.\n\n```typescript\nconst agent = new Agent(scope, 'support', {\n  streamingMode: 'token',\n  ...\n});\n```\n\n### Conversation Management\n\nControls how the agent trims message history when the context window fills up:\n\n```typescript\n// Sliding window — keep last 20 messages\nconst agent = new Agent(scope, 'support', {\n  conversation: { strategy: 'sliding-window', windowSize: 20 },\n  ...\n});\n\n// Summarizing — summarizes older messages, preserves 5 most recent\nconst agent = new Agent(scope, 'support', {\n  conversation: { strategy: 'summarizing', preserveRecentMessages: 5 },\n  ...\n});\n```\n\n### Limiting runaway cost\n\nAn 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.\n\n> **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.\n\nTwo per-turn safety caps bound this, and **both default to `20`**:\n\n- **`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.\n- **`maxToolIterations`** — the maximum number of tool calls in a single turn (parallel tool batches count each call).\n\nWhen 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.\n\n```typescript\nconst agent = new Agent(scope, 'support', {\n  systemPrompt: '...',\n  maxLlmCalls: 40,          // agent legitimately reasons over many steps\n  maxToolIterations: 60,    // ...and chains many tools per turn\n});\n\n// Or disable a cap entirely with `false`:\nconst unbounded = new Agent(scope, 'batch', {\n  systemPrompt: '...',\n  maxLlmCalls: false,       // no per-turn model-call limit\n  maxToolIterations: false,\n});\n```\n\nRaise 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.\n\nWhen 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.\n\n## Tools\n\nTools 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.\n\n### Adding Tools\n\nAdd tools to let the agent take actions. Each tool has a description, Zod schema for parameters, and a handler. The handler receives `{ input, context, interrupt }`:\n\n```typescript\nimport { z } from 'zod';\n\nconst agent = new Agent(scope, 'support', {\n  model: { deployed: { provider: 'bedrock', modelId: '...' } },\n  systemPrompt: 'You are a customer support agent. Look up orders when asked.',\n  tools: (tool) => ({\n    getOrderStatus: tool({\n      description: 'Get the status of a customer order by ID',\n      parameters: z.object({ orderId: z.string() }),\n      handler: async ({ input }) => {\n        const order = await db.getOrder(input.orderId);\n        return { orderId: input.orderId, status: order.status, total: order.total };\n      },\n    }),\n  }),\n});\n```\n\n### Declaring tools (the `tools` callback)\n\n`tools` is a callback that receives a `tool()` factory and returns a Record keyed by tool name:\n\n```typescript\ntools: (tool) => ({\n  getOrderStatus: tool({ /* ... */ }),\n})\n```\n\nThe callback form lets TypeScript infer each tool's `input` from its `parameters`. The Record key is the tool's name.\n\n### Tool Context — Scoping Tools to the Caller\n\nTools often need request-scoped information (e.g. the authenticated `userId`). Pass a `context` object on each `stream()`/`resume()` call; it's forwarded to every tool invocation:\n\n```typescript\nconst agent = new Agent(scope, 'support', {\n  model: { deployed: { provider: 'bedrock', modelId: '...' } },\n  systemPrompt: 'You are a support agent.',\n  tools: (tool) => ({\n    listMyOrders: tool({\n      description: \"List the current user's orders\",\n      parameters: z.object({}),\n      handler: async ({ context }) => {\n        return db.listOrders({ userId: context.userId });\n      },\n    }),\n  }),\n});\n\nconst user = await auth.getCurrentUser(requestContext);\nawait agent.stream(message, { conversationId, userId: user.userId, context: { userId: user.userId } });\n```\n\nTo make context required and type-safe, declare a `toolContextSchema`:\n\n```typescript\nconst agent = new Agent(scope, 'support', {\n  model: { deployed: { provider: 'bedrock', modelId: '...' } },\n  systemPrompt: '...',\n  toolContextSchema: z.object({ userId: z.string(), tenantId: z.string() }),\n  tools: (tool) => ({\n    listMyOrders: tool({\n      description: \"List the current user's orders\",\n      parameters: z.object({}),\n      handler: async ({ context }) => {\n        // context.userId and context.tenantId are typed as string\n        return db.listOrders({ userId: context.userId, tenantId: context.tenantId });\n      },\n    }),\n  }),\n});\n\n// context is now required and validated — omitting it throws InvalidModelConfig\nawait agent.stream(message, { conversationId, userId, context: { userId, tenantId } });\n```\n\n### Using KnowledgeBase with the Agent\n\nThe `KnowledgeBase` BB can be used as an agent tool, giving the agent the ability to search documents on demand:\n\n```typescript\nimport { Agent } from '@aws-blocks/bb-agent';\nimport { KnowledgeBase } from '@aws-blocks/bb-knowledge-base';\nimport { z } from 'zod';\n\nconst kb = new KnowledgeBase(scope, 'docs', {\n  source: './knowledge',\n  description: 'Product documentation and FAQs',\n});\n\nconst agent = new Agent(scope, 'assistant', {\n  model: { deployed: { provider: 'bedrock', modelId: '...' } },\n  systemPrompt: 'You are a helpful assistant. Search the knowledge base when the user asks about our product.',\n  tools: (tool) => ({\n    searchDocs: tool({\n      description: 'Search product documentation for relevant information',\n      parameters: z.object({\n        query: z.string().describe('The search query'),\n        maxResults: z.number().optional().describe('Max results to return (default: 5)'),\n      }),\n      handler: async ({ input }) => kb.retrieve(input.query, { maxResults: input.maxResults ?? 5 }),\n    }),\n  }),\n});\n```\n\n### Tool Approval (Human-in-the-Loop)\n\nBy default, tools run autonomously. Set `needsApproval: true` on tools that should pause for user approval — the agent publishes an interrupt chunk, the client shows a confirmation UI, the user responds, and the agent resumes.\n\n| Configuration | Behavior |\n|---------------|----------|\n| `needsApproval: false` (default) | Tool runs autonomously |\n| `needsApproval: true` | Pauses for approval every time — user sees Yes / No |\n| `needsApproval: true, trustable: true` | Pauses for approval — user sees Yes / No / Trust. \"Trust\" auto-approves for the rest of the conversation |\n\nTools that modify state should require user approval. Set `needsApproval: true`:\n\n```typescript\ntools: (tool) => ({\n  getOrderStatus: tool({\n    description: 'Look up an order',\n    parameters: z.object({ orderId: z.string() }),\n    needsApproval: false,  // read-only — safe to run\n    handler: async ({ input }) => db.getOrder(input.orderId),\n  }),\n  cancelOrder: tool({\n    description: 'Cancel a customer order',\n    parameters: z.object({ orderId: z.string(), reason: z.string() }),\n    needsApproval: true,   // destructive — ask first\n    trustable: true,        // user can say \"trust\" to stop being asked\n    handler: async ({ input }) => db.cancelOrder(input.orderId, input.reason),\n  }),\n})\n```\nWhen a tool is interrupted, the client receives an `interrupt` chunk. Resume with `agent.resume()`:\n\n```typescript\n// Client receives: { type: 'interrupt', interrupts: [{ id, name, reason }] }\n// User approves → resume the agent:\nawait agent.resume(channelId, [{ interruptId: interrupt.id, approved: true }], { conversationId, userId });\n```\n\n**Interrupt chunk format:** `name` is `approve:${toolName}:${toolUseId}` and `reason` contains `{ tool: string, input: any, trustable: boolean }`. Use `reason.tool` for display and `reason.trustable` to decide whether to show a Trust button.\n\n### Custom Interrupts\n\nFor tools that need input-level approval decisions or runtime-conditional pausing, use the `interrupt` field or call `interrupt()` inside the handler:\n\n```typescript\ntools: (tool) => ({\n  transferMoney: tool({\n    description: 'Transfer money between accounts',\n    parameters: z.object({ from: z.string(), to: z.string(), amount: z.number() }),\n    interrupt: ({ input, interrupt }) => {\n      if (input.amount > 100) {\n        interrupt({ name: 'confirm-transfer', reason: { message: `Transfer $${input.amount}?` } });\n      }\n    },\n    handler: async ({ input }) => ({ status: 'completed', amount: input.amount }),\n  }),\n})\n```\n\n## Headless Usage (No UI)\n\nThe Agent BB works without a frontend — for scripts, background jobs, or server-to-server flows. Use `complete()` to wait for the full response. For UI-based flows, see [Client Hook — useChat](#client-hook--usechat).\n\n### Without tool approval\n\n```typescript\nimport { Agent, BedrockModels } from '@aws-blocks/bb-agent';\n\nconst agent = new Agent(scope, 'summarizer', {\n  model: { deployed: BedrockModels.BALANCED },\n  systemPrompt: 'Summarize the input concisely.',\n});\n\nconst conversationId = await agent.createConversationId('system');\nconst result = await agent.stream('Summarize this quarter earnings report...', { conversationId, userId: 'system' });\nconst done = await result.complete();\nconsole.log(done.text);\n```\n\n### With tool approval\n\nWhen tools have `needsApproval: true`, `complete()` throws an `InterruptError`. Handle it programmatically:\n\n```typescript\nimport { Agent, BedrockModels, InterruptError } from '@aws-blocks/bb-agent';\nimport { z } from 'zod';\n\nconst refundBot = new Agent(scope, 'refunds', {\n  model: { deployed: BedrockModels.BALANCED },\n  systemPrompt: 'You process customer refund requests.',\n  tools: (tool) => ({\n    issueRefund: tool({\n      description: 'Issue a refund to a customer',\n      parameters: z.object({ orderId: z.string(), amount: z.number() }),\n      needsApproval: true,\n      handler: async ({ input }) => {\n        await payments.refund(input.orderId, input.amount);\n        return { refunded: true, amount: input.amount };\n      },\n    }),\n  }),\n});\n\nconst conversationId = await refundBot.createConversationId('system');\nconst result = await refundBot.stream('Refund order #456, item was damaged. Total was $75.', { conversationId, userId: 'system' });\n\nwhile (true) {\n  try {\n    const done = await result.complete();\n    console.log(done.text);\n    break;\n  } catch (err) {\n    if (!(err instanceof InterruptError)) throw err;\n    // Auto-approve refunds under $100, reject larger ones\n    const responses = err.interrupts.map(i => ({\n      interruptId: i.id,\n      approved: i.reason?.input?.amount < 100,\n    }));\n    await refundBot.resume(result.channelId, responses, { conversationId, userId: 'system' });\n  }\n}\n```\n\n## Inference-Only (No Persistence)\n\nSet `inferenceOnly: true` for stateless tasks that don't need conversation history — classification, extraction, summarization. No DynamoDB tables or session storage are created.\n\n```typescript\nconst classifier = new Agent(scope, 'classifier', {\n  inferenceOnly: true,\n  model: { deployed: { provider: 'bedrock', modelId: '...' } },\n  systemPrompt: 'Classify the sentiment of the input as positive, negative, or neutral.',\n});\n\nconst result = await classifier.stream('I love this product!');\nconst done = await result.complete();\nconsole.log(done.text); // \"positive\"\n```\n## Local Development\n\nThe Agent BB works locally without any external dependencies. No AWS credentials, no API keys, no running services — just `npm run dev`.\nBy default the agent uses the **CannedProvider** — a keyword-based mock that responds instantly without calling any real model. For real LLM calls locally, set `model.local` to an `openai-api` config (Ollama, vLLM, etc.) or use the Ollama presets. See [Model Configuration](#model-configuration) for details.\n\n### Use Local LLM\n\nFor real model responses during development, use a fallback chain with your company's shared vLLM server and a local Ollama instance. The agent tries each in order — on the company network it uses the shared server, at home it falls through to your local Ollama:\n\n```typescript\nconst agent = new Agent(scope, 'support', {\n  model: {\n    deployed: { provider: 'bedrock', modelId: '...' },\n    local: [\n      { provider: 'openai-api', modelId: 'llama3.1:70b', endpoint: 'http://vllm.internal.company.com/v1' },\n      { provider: 'openai-api', modelId: 'llama3.1:8b', endpoint: 'http://localhost:11434/v1', apiKey: 'ollama' },\n      // canned is appended implicitly — if nothing is available, agent still works\n    ],\n  },\n  systemPrompt: '...',\n});\n```\n\n### Canned Provider\n\nThe CannedProvider is a custom Strands model provider that requires no network or API keys:\n\n- Returns simple mock responses\n- Triggers tool calls when the prompt mentions a tool name (e.g., \"get order\" triggers `getOrderStatus`)\n- Generates valid tool inputs from Zod schemas, respecting schema `default` values (from `.default()`) before falling back to type-based placeholders (`'sample'`, `1`, `true`, `[]`)\n- Streams responses word by word, matching the same protocol as real providers\n\n#### Canned Hints — `cannedExamples` and `cannedTriggers`\n\nTwo 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:\n\n| Field | Type | Effect (canned provider only) |\n| --- | --- | --- |\n| `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. |\n| `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. |\n\nBuilding 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:\n\n```typescript\ntools: (tool) => ({\n  searchDocs: tool({\n    description: 'Search product documentation for relevant information',\n    parameters: z.object({\n      query: z.string().describe('The search query'),\n      maxResults: z.number().optional().describe('Max results to return (default: 5)'),\n    }),\n    handler: async ({ input }) => kb.retrieve(input.query, { maxResults: input.maxResults ?? 5 }),\n\n    // Canned provider hints (ignored by real models):\n    // Without this the mock would search for the literal 'sample' and match nothing —\n    // use a query that hits YOUR documents so local runs return meaningful results.\n    cannedExamples: { query: 'how do I reset my password' },\n    // The name already matches \"search\"/\"docs\"/\"searchDocs\"; these add phrasings that don't\n    // contain the name, so \"help me find the manual\" or \"look up the guide\" also fire the tool.\n    cannedTriggers: ['find', 'look up'],\n  }),\n}),\n```\n\n\n## Client Hook — `useChat`\n\nImport from `@aws-blocks/bb-agent/client`. Manages conversation state, streaming subscriptions, and interrupt handling. Handles the subscribe-before-send ordering automatically.\n\n```typescript\nimport { useChat } from '@aws-blocks/bb-agent/client';\n\nconst chat = useChat({\n  api: {\n    sendMessage: (convId, msg, chId) => api.sendMessage(convId, msg, chId),\n    createConversation: () => api.createConversation(userId),\n    getConversation: (id) => api.getConversation(id),\n    resume: (chId, responses, convId) => api.resume(chId, responses, convId),\n  },\n  subscribe: async (channelId, handler) => {\n    const channel = await api.getChannel(channelId);\n    return channel.subscribe(handler);\n  },\n  onMessagesChange: (msgs) => renderMessages(msgs),\n  onLoadingChange: (loading) => updateSpinner(loading),\n  onInterrupt: (interrupts) => showApprovalUI(interrupts),\n});\n\nawait chat.sendMessage('Hello!');\nawait chat.respondToInterrupt([{ interruptId: 'x', approved: true }]);\n```\n\n**Note:** `useChat` is a factory function, not a React hook. Call it **once** (e.g., outside a component or in a ref) — not on every render. It returns a mutable singleton. Message history only includes `user`, `assistant`, and `approval` messages — tool-call/tool-result internals are filtered for UI clarity. Use `getConversation()` directly if you need the full history.\n\n## Full Examples\n\n### 1. End-to-End: Backend + Frontend with `useChat`\n\nComplete wiring showing the backend API and frontend `useChat` connected together.\n\n**Backend** (`aws-blocks/index.ts`):\n\n```typescript\nimport { Scope, ApiNamespace } from '@aws-blocks/core';\nimport { Agent, BedrockModels } from '@aws-blocks/bb-agent';\n\nconst scope = new Scope('my-app');\n\nconst agent = new Agent(scope, 'chat', {\n  model: { deployed: BedrockModels.BALANCED },\n  systemPrompt: 'You are a helpful assistant.',\n});\n\nexport const api = new ApiNamespace(scope, 'api', (context) => ({\n  async createConversation(userId: string) {\n    return { conversationId: await agent.createConversationId(userId) };\n  },\n  async sendMessage(conversationId: string, message: string, channelId: string, userId: string) {\n    const result = await agent.stream(message, { conversationId, channelId, userId });\n    return { channelId: result.channelId };\n  },\n  async getConversation(conversationId: string) {\n    const messages = await agent.getConversation(conversationId);\n    return { messages };\n  },\n  async getChannel(channelId: string) {\n    return agent.getChannel(channelId);\n  },\n}));\n```\n\n**Frontend** (`app.ts`):\n\n```typescript\nimport { useChat } from '@aws-blocks/bb-agent/client';\n\nconst userId = getCurrentUserId();\n\nconst chat = useChat({\n  api: {\n    sendMessage: (convId, msg, chId) => api.sendMessage(convId, msg, chId, userId),\n    createConversation: () => api.createConversation(userId),\n    getConversation: (id) => api.getConversation(id),\n  },\n  subscribe: async (channelId, handler) => {\n    const channel = await api.getChannel(channelId);\n    return channel.subscribe(handler);\n  },\n  onMessagesChange: (msgs) => renderMessages(msgs),\n  onLoadingChange: (loading) => updateSpinner(loading),\n});\n\n// Send a message — useChat handles subscribe-before-send automatically\nawait chat.sendMessage('Hello!');\n\n// Load an existing conversation (subscribes first, then backfills history)\nawait chat.loadConversation('conv-123');\n```\n\nThe 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.\n\n### 2. React: hold the instance once, drive `useState` from the callbacks\n\n`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).\n\n```tsx\n'use client'; // Next.js only — see the note below. Plain React (Vite/CRA) can omit this.\n\nimport { useRef, useState, useEffect } from 'react';\nimport { useChat, type ChatMessage } from '@aws-blocks/bb-agent/client';\nimport { api } from './api'; // your generated aws-blocks API client\n\nexport function Chat() {\n  const [messages, setMessages] = useState<ChatMessage[]>([]);\n  const [isLoading, setIsLoading] = useState(false);\n  const [input, setInput] = useState('');\n\n  // Create the instance exactly once. The ref survives every re-render,\n  // so the subscription and conversation state are never torn down.\n  // Type the ref as `| undefined` and initialize with `undefined` — @types/react 19\n  // tightened the useRef overloads, so a bare useRef<T>() no longer compiles.\n  const chatRef = useRef<ReturnType<typeof useChat> | undefined>(undefined);\n  if (!chatRef.current) {\n    // 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.\n    chatRef.current = useChat({\n      api: {\n        sendMessage: (convId, msg, chId) => api.sendMessage(convId, msg, chId),\n        createConversation: () => api.createConversation(),\n        getConversation: (id) => api.getConversation(id),\n      },\n      subscribe: async (channelId, handler) => {\n        const channel = await api.getChannel(channelId);\n        return channel.subscribe(handler);\n      },\n      // Bridge the mutable instance into React state — these fire on every change.\n      onMessagesChange: setMessages,\n      onLoadingChange: setIsLoading,\n    });\n  }\n  const chat = chatRef.current!; // guaranteed set by the block above\n\n  // Tear down the WebSocket subscription when the component unmounts.\n  useEffect(() => () => chat.destroy(), [chat]);\n\n  async function handleSend(e: React.FormEvent) {\n    e.preventDefault();\n    const text = input.trim();\n    if (!text || isLoading) return;\n    setInput('');\n    await chat.sendMessage(text);\n  }\n\n  return (\n    <div>\n      <ul>\n        {messages.map((m) => (\n          <li key={m.id} data-role={m.role}>\n            <strong>{m.role}:</strong> {m.content}\n          </li>\n        ))}\n      </ul>\n      <form onSubmit={handleSend}>\n        <input value={input} onChange={(e) => setInput(e.target.value)} disabled={isLoading} />\n        <button type=\"submit\" disabled={isLoading}>Send</button>\n      </form>\n    </div>\n  );\n}\n```\n\nKey points:\n\n- **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.\n- **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.\n- **Clean up on unmount** with `chat.destroy()` in a `useEffect` cleanup, so the Realtime subscription is closed.\n- **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 }])`.\n\n**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.\n\n### 3. Support Agent with Tools\n\nAgent with tools that can look up orders and search documentation. Uses tool context to scope queries to the authenticated user.\n\n```typescript\nimport { Scope, ApiNamespace } from '@aws-blocks/core';\nimport { Agent, BedrockModels } from '@aws-blocks/bb-agent';\nimport { KnowledgeBase } from '@aws-blocks/bb-knowledge-base';\nimport { z } from 'zod';\n\nconst scope = new Scope('my-app');\n\nconst kb = new KnowledgeBase(scope, 'docs', { source: './knowledge' });\n\nconst agent = new Agent(scope, 'support', {\n  model: { deployed: BedrockModels.BALANCED },\n  systemPrompt: 'You are a customer support agent. Look up orders and search documentation to help the user.',\n  toolContextSchema: z.object({ userId: z.string() }),\n  tools: (tool) => ({\n    getOrder: tool({\n      description: 'Get order details by ID',\n      parameters: z.object({ orderId: z.string() }),\n      handler: async ({ input, context }) => {\n        return db.getOrder(input.orderId, { userId: context.userId });\n      },\n    }),\n    searchDocs: tool({\n      description: 'Search product documentation',\n      parameters: z.object({ query: z.string() }),\n      handler: async ({ input }) => kb.retrieve(input.query, { maxResults: 5 }),\n    }),\n  }),\n});\n\nexport const api = new ApiNamespace(scope, 'api', (context) => ({\n  async chat(message: string, conversationId: string) {\n    const user = await auth.getCurrentUser(context);\n    return await agent.stream(message, {\n      conversationId,\n      userId: user.userId,\n      context: { userId: user.userId },\n    });\n  },\n}));\n```\n\n\n## Best Practices\n\n- Keep system prompts focused — one agent per task, not one agent for everything\n- Define tools with descriptive names and descriptions — the model uses these to decide when to call them\n- Set `model.local` to an array of fallback candidates for flexible local dev\n- Set logging to `info` during development to surface health check and model resolution details\n\n## What It Provisions\n\nThe Agent BB composes several internal Building Blocks automatically:\n\n| BB | AWS Resource | Purpose |\n|----|-------------|---------|\n| `FileBucket` | S3 | Session snapshot storage (Strands agent state between turns) |\n| `DistributedTable` × 2 | DynamoDB | Conversations table + messages table |\n| `Realtime` | API Gateway WebSocket | Streaming chunks to connected clients |\n\nThe 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.\n\nWhen `inferenceOnly: true`, the two DistributedTables are skipped (no conversation persistence).\n\n## Scaling & Cost (AWS)\n\n- **Model:** Bedrock pay-per-token pricing. See [Bedrock pricing](https://aws.amazon.com/bedrock/pricing/).\n- **Persistence:** DynamoDB (DistributedTable) — PAY_PER_REQUEST, single-digit ms latency.\n- **Session storage:** S3 (FileBucket) — ~$0.023 per GB/month.\n- **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/).\n- **Streaming:** API Gateway WebSocket (Realtime) — per-message + per-connection-minute pricing.\n\n## Troubleshooting\n\n**\"Access denied / Legacy model\"** — Some older model IDs may be marked as legacy. Switch to a cross-region inference profile.\n\n**\"ValidationException\"** — Model ID not recognized. Use `aws bedrock list-foundation-models --query \"modelSummaries[].modelId\"` to see available models.\n\n**Health check passes but invocation fails** — The health check verifies the model exists but cannot check EULA acceptance or account-level access.\n\n## See Also\n\n- [Strands Agents SDK](https://strandsagents.com/)\n- [Bedrock supported models](https://docs.aws.amazon.com/bedrock/latest/userguide/models-supported.html)\n- [Cross-region inference profiles](https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html)\n- [Bedrock pricing](https://aws.amazon.com/bedrock/pricing/)\n- [Bedrock AgentCore Runtime pricing](https://aws.amazon.com/bedrock/agentcore/pricing/)\n- [Ollama model library](https://ollama.com/library)\n","readmeFilename":"README.md","keywords":["aws-blocks","ai","agent","llm","bedrock","chatbot"]}