From b0aada1f91eeb0e9edfffb5244b402948ff96273 Mon Sep 17 00:00:00 2001 From: Brian Strauch Date: Thu, 28 May 2026 09:43:09 -0700 Subject: [PATCH 1/6] Added Strands Agents sample Co-Authored-By: Claude Opus 4.7 (1M context) --- strands-agents/.eslintignore | 3 + strands-agents/.eslintrc.js | 43 +++++++++ strands-agents/README.md | 80 ++++++++++++++++ strands-agents/package.json | 64 +++++++++++++ strands-agents/src/activities.ts | 3 + .../src/activities/activity-interrupt.ts | 31 +++++++ strands-agents/src/activities/hooks.ts | 6 ++ strands-agents/src/activities/tools.ts | 7 ++ strands-agents/src/activity-interrupt.ts | 39 ++++++++ strands-agents/src/continue-as-new.ts | 28 ++++++ strands-agents/src/hello-world.ts | 21 +++++ strands-agents/src/hooks.ts | 21 +++++ strands-agents/src/human-in-the-loop.ts | 32 +++++++ strands-agents/src/mcp-server.ts | 24 +++++ strands-agents/src/mcp.ts | 21 +++++ .../src/mocha/activity-interrupt.test.ts | 54 +++++++++++ .../src/mocha/continue-as-new.test.ts | 53 +++++++++++ strands-agents/src/mocha/hello-world.test.ts | 40 ++++++++ strands-agents/src/mocha/hooks.test.ts | 49 ++++++++++ .../src/mocha/human-in-the-loop.test.ts | 52 +++++++++++ strands-agents/src/mocha/mcp.test.ts | 91 +++++++++++++++++++ strands-agents/src/mocha/streaming.test.ts | 57 ++++++++++++ .../src/mocha/structured-output.test.ts | 47 ++++++++++ strands-agents/src/mocha/stub-model.ts | 53 +++++++++++ strands-agents/src/mocha/tools.test.ts | 53 +++++++++++ strands-agents/src/streaming.ts | 47 ++++++++++ strands-agents/src/structured-output.ts | 22 +++++ strands-agents/src/tools.ts | 27 ++++++ strands-agents/src/worker.ts | 44 +++++++++ strands-agents/src/workflows.ts | 17 ++++ .../src/workflows/activity-interrupt.ts | 47 ++++++++++ .../src/workflows/continue-as-new.ts | 60 ++++++++++++ strands-agents/src/workflows/hello-world.ts | 9 ++ strands-agents/src/workflows/hooks.ts | 36 ++++++++ .../src/workflows/human-in-the-loop.ts | 62 +++++++++++++ strands-agents/src/workflows/mcp.ts | 14 +++ strands-agents/src/workflows/streaming.ts | 15 +++ .../src/workflows/structured-output.ts | 19 ++++ strands-agents/src/workflows/tools.ts | 33 +++++++ strands-agents/tsconfig.json | 13 +++ 40 files changed, 1437 insertions(+) create mode 100644 strands-agents/.eslintignore create mode 100644 strands-agents/.eslintrc.js create mode 100644 strands-agents/README.md create mode 100644 strands-agents/package.json create mode 100644 strands-agents/src/activities.ts create mode 100644 strands-agents/src/activities/activity-interrupt.ts create mode 100644 strands-agents/src/activities/hooks.ts create mode 100644 strands-agents/src/activities/tools.ts create mode 100644 strands-agents/src/activity-interrupt.ts create mode 100644 strands-agents/src/continue-as-new.ts create mode 100644 strands-agents/src/hello-world.ts create mode 100644 strands-agents/src/hooks.ts create mode 100644 strands-agents/src/human-in-the-loop.ts create mode 100644 strands-agents/src/mcp-server.ts create mode 100644 strands-agents/src/mcp.ts create mode 100644 strands-agents/src/mocha/activity-interrupt.test.ts create mode 100644 strands-agents/src/mocha/continue-as-new.test.ts create mode 100644 strands-agents/src/mocha/hello-world.test.ts create mode 100644 strands-agents/src/mocha/hooks.test.ts create mode 100644 strands-agents/src/mocha/human-in-the-loop.test.ts create mode 100644 strands-agents/src/mocha/mcp.test.ts create mode 100644 strands-agents/src/mocha/streaming.test.ts create mode 100644 strands-agents/src/mocha/structured-output.test.ts create mode 100644 strands-agents/src/mocha/stub-model.ts create mode 100644 strands-agents/src/mocha/tools.test.ts create mode 100644 strands-agents/src/streaming.ts create mode 100644 strands-agents/src/structured-output.ts create mode 100644 strands-agents/src/tools.ts create mode 100644 strands-agents/src/worker.ts create mode 100644 strands-agents/src/workflows.ts create mode 100644 strands-agents/src/workflows/activity-interrupt.ts create mode 100644 strands-agents/src/workflows/continue-as-new.ts create mode 100644 strands-agents/src/workflows/hello-world.ts create mode 100644 strands-agents/src/workflows/hooks.ts create mode 100644 strands-agents/src/workflows/human-in-the-loop.ts create mode 100644 strands-agents/src/workflows/mcp.ts create mode 100644 strands-agents/src/workflows/streaming.ts create mode 100644 strands-agents/src/workflows/structured-output.ts create mode 100644 strands-agents/src/workflows/tools.ts create mode 100644 strands-agents/tsconfig.json diff --git a/strands-agents/.eslintignore b/strands-agents/.eslintignore new file mode 100644 index 000000000..699794d04 --- /dev/null +++ b/strands-agents/.eslintignore @@ -0,0 +1,3 @@ +node_modules +lib +.eslintrc.js diff --git a/strands-agents/.eslintrc.js b/strands-agents/.eslintrc.js new file mode 100644 index 000000000..32d63d64b --- /dev/null +++ b/strands-agents/.eslintrc.js @@ -0,0 +1,43 @@ +const { builtinModules } = require('module'); + +const ALLOWED_NODE_BUILTINS = new Set(['assert']); + +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + project: './tsconfig.json', + tsconfigRootDir: __dirname, + }, + plugins: ['@typescript-eslint', 'deprecation'], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'prettier', + ], + rules: { + '@typescript-eslint/no-floating-promises': 'error', + 'deprecation/deprecation': 'warn', + 'object-shorthand': ['error', 'always'], + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + '@typescript-eslint/no-explicit-any': 'off', + }, + overrides: [ + { + files: ['src/workflows.ts', 'src/workflows-*.ts', 'src/workflows/*.ts'], + rules: { + 'no-restricted-imports': [ + 'error', + ...builtinModules.filter((m) => !ALLOWED_NODE_BUILTINS.has(m)).flatMap((m) => [m, `node:${m}`]), + ], + }, + }, + ], +}; diff --git a/strands-agents/README.md b/strands-agents/README.md new file mode 100644 index 000000000..1eb28edc1 --- /dev/null +++ b/strands-agents/README.md @@ -0,0 +1,80 @@ +# Strands Agents + +A complete set of samples for the Temporal TypeScript SDK's Strands integration, mirroring the Python samples in [`samples-python/strands_plugin`](https://github.com/temporalio/samples-python/tree/strands-plugin-samples/strands_plugin). + +One worker registers all activities, all workflows, and an MCP server. Each feature has its own client script in `src/`: + +| Feature | Client | What it demonstrates | +| --- | --- | --- | +| Hello World | `npm run workflow:hello-world` | Minimal `TemporalAgent` invocation. | +| Tools | `npm run workflow:tools` | Pure Strands `tool()` + `workflow.activityAsTool` for I/O. | +| Human in the Loop | `npm run workflow:human-in-the-loop` | Gate a tool call on human approval with a `BeforeToolCallEvent` hook + `event.interrupt()`. | +| Activity Interrupt | `npm run workflow:activity-interrupt` | Activity-thrown interrupt routed through `StrandsFailureConverter`. | +| Hooks | `npm run workflow:hooks` | `AfterToolCallEvent` with in-workflow and activity-dispatched callbacks. | +| MCP | `npm run workflow:mcp` | Connect to a stdio MCP server via `TemporalMCPClient`. | +| Structured Output | `npm run workflow:structured-output` | Constrain output with a Zod schema. | +| Streaming | `npm run workflow:streaming` | Forward model chunks to subscribers via `WorkflowStream`. | +| Continue as New | `npm run workflow:continue-as-new` | Long-lived chat with history-aware continue-as-new. | + +### Directory layout + +``` +src/ +├── worker.ts # one worker, registers everything +├── workflows.ts # re-exports every workflow in workflows/ +├── activities.ts # re-exports every activity in activities/ +├── mcp-server.ts # stdio MCP server (used by the mcp feature) +├── .ts # one client script per feature +├── workflows/ +│ └── .ts # workflow definition for that feature +├── activities/ +│ └── .ts # activities for that feature (only where needed) +└── mocha/ + ├── stub-model.ts # shared StubModel for tests + └── .test.ts # one test per feature +``` + +### Prerequisites + +`@temporalio/strands-agents` and its dependency `@temporalio/workflow-streams` live in [`temporalio/sdk-typescript`](https://github.com/temporalio/sdk-typescript) on the `strands` branch and are not yet published to npm. `package.json` references both as `file:` tarballs, assuming `sdk-typescript` is checked out next to `samples-typescript`: + +``` +/ +├── samples-typescript/ # this repo +└── sdk-typescript/ # the SDK repo on branch `strands` +``` + +Before installing, build the contrib packages and produce the tarballs (the `workspace:*` deps inside them need to be substituted with concrete versions, which `pnpm pack` does automatically): + +```bash +cd /path/to/sdk-typescript +git checkout strands +pnpm install +pnpm build + +cd contrib/strands && pnpm pack && cd ../.. +cd contrib/workflow-streams && pnpm pack && cd ../.. +# `@temporalio/common` needs to be packed too because the streaming feature +# relies on the `INTERNAL_HANDLER_NAME_ALLOWLIST` added on the `strands` branch +# but not yet published to npm. The sample's `package.json` overrides every +# transitive `@temporalio/common` with this tarball. +cd packages/common && pnpm pack && cd ../.. +``` + +### Running + +1. `npm install` in this directory. +1. Configure AWS credentials so the default `BedrockModel` can reach Amazon Bedrock (`AWS_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`). The default model is `global.anthropic.claude-sonnet-4-6`, which requires submitting Anthropic's use-case details form — pick the model in the [Bedrock model catalog](https://console.aws.amazon.com/bedrock/home#/model-catalog) and fill out the form before invoking. To use a different provider — or pin a model that doesn't require the form — swap `BedrockModel` in `src/worker.ts` for `AnthropicModel`, `OpenAIModel`, etc., from `@strands-agents/sdk`, or pass a `modelId` to `BedrockModel`. +1. `temporal server start-dev`. +1. `npm run start.watch` in one shell to start the worker. +1. `npm run workflow:` in another shell — see the table above. + +### Tests + +`src/mocha/*.test.ts` exercises each workflow with a `StubModel` and `TestWorkflowEnvironment`, so the suite runs without AWS credentials: + +```bash +npm test +``` + +> The `mocha@8` version pinned across `samples-typescript` is incompatible with Node 24+ (its bundled `yargs` is loaded as ESM and breaks at startup). Use Node 22 to run tests, matching the rest of `samples-typescript`. diff --git a/strands-agents/package.json b/strands-agents/package.json new file mode 100644 index 000000000..1f9e1d65f --- /dev/null +++ b/strands-agents/package.json @@ -0,0 +1,64 @@ +{ + "name": "temporal-strands-agents", + "version": "0.1.0", + "private": true, + "scripts": { + "build": "tsc --build", + "build.watch": "tsc --build --watch", + "format": "prettier --write .", + "format:check": "prettier --check .", + "lint": "eslint .", + "start": "ts-node src/worker.ts", + "start.watch": "nodemon src/worker.ts", + "workflow:hello-world": "ts-node src/hello-world.ts", + "workflow:tools": "ts-node src/tools.ts", + "workflow:human-in-the-loop": "ts-node src/human-in-the-loop.ts", + "workflow:activity-interrupt": "ts-node src/activity-interrupt.ts", + "workflow:hooks": "ts-node src/hooks.ts", + "workflow:mcp": "ts-node src/mcp.ts", + "workflow:structured-output": "ts-node src/structured-output.ts", + "workflow:streaming": "ts-node src/streaming.ts", + "workflow:continue-as-new": "ts-node src/continue-as-new.ts", + "test": "mocha --exit --require ts-node/register --require source-map-support/register src/mocha/*.test.ts" + }, + "nodemonConfig": { + "execMap": { + "ts": "ts-node" + }, + "ext": "ts", + "watch": [ + "src" + ] + }, + "overrides": { + "@temporalio/common": "file:../../sdk-typescript/packages/common/temporalio-common-1.17.2.tgz" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.25.2", + "@strands-agents/sdk": "^1.3.0", + "@temporalio/activity": "^1.17.2", + "@temporalio/client": "^1.17.2", + "@temporalio/envconfig": "^1.17.2", + "@temporalio/strands-agents": "file:../../sdk-typescript/contrib/strands/temporalio-strands-agents-1.17.2.tgz", + "@temporalio/worker": "^1.17.2", + "@temporalio/workflow": "^1.17.2", + "@temporalio/workflow-streams": "file:../../sdk-typescript/contrib/workflow-streams/temporalio-workflow-streams-1.17.0.tgz", + "zod": "^4.1.12" + }, + "devDependencies": { + "@temporalio/testing": "^1.17.2", + "@tsconfig/node22": "^22.0.0", + "@types/mocha": "8.x", + "@types/node": "^22.9.1", + "@typescript-eslint/eslint-plugin": "^8.18.0", + "@typescript-eslint/parser": "^8.18.0", + "eslint": "^8.57.1", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-deprecation": "^3.0.0", + "mocha": "8.x", + "nodemon": "^3.1.7", + "prettier": "^3.4.2", + "ts-node": "^10.9.2", + "typescript": "^5.6.3" + } +} diff --git a/strands-agents/src/activities.ts b/strands-agents/src/activities.ts new file mode 100644 index 000000000..9cacefc6c --- /dev/null +++ b/strands-agents/src/activities.ts @@ -0,0 +1,3 @@ +export { fetchWeather } from './activities/tools'; +export { deleteThing } from './activities/activity-interrupt'; +export { persistToolCall } from './activities/hooks'; diff --git a/strands-agents/src/activities/activity-interrupt.ts b/strands-agents/src/activities/activity-interrupt.ts new file mode 100644 index 000000000..d8cc73f5d --- /dev/null +++ b/strands-agents/src/activities/activity-interrupt.ts @@ -0,0 +1,31 @@ +import { ApplicationFailure } from '@temporalio/common'; +import { STRANDS_INTERRUPT_TYPE } from '@temporalio/strands-agents'; + +const APPROVED = new Set(); + +export async function deleteThing(input: { name: string }): Promise { + if (!APPROVED.has(input.name)) { + // First attempt: mark the name as approved on the way out (simulating the + // human flipping a flag during the interrupt pause) and stop the agent by + // raising an interrupt-shaped failure. The plugin's `StrandsFailureConverter` + // would also recognize a thrown `{ interrupts: [{ toJSON: () => ... }] }`, + // but throwing `ApplicationFailure` directly avoids any chance of the + // converter being skipped (and keeps `nonRetryable: true` so the workflow + // sees the interrupt instead of a retry-then-success). + APPROVED.add(input.name); + throw ApplicationFailure.create({ + message: 'interrupt:approval', + type: STRANDS_INTERRUPT_TYPE, + nonRetryable: true, + details: [ + { + id: `delete:${input.name}`, + name: 'approval', + reason: `approve delete of protected resource '${input.name}'?`, + source: 'tool', + }, + ], + }); + } + return `deleted ${input.name}`; +} diff --git a/strands-agents/src/activities/hooks.ts b/strands-agents/src/activities/hooks.ts new file mode 100644 index 000000000..27d81b2ba --- /dev/null +++ b/strands-agents/src/activities/hooks.ts @@ -0,0 +1,6 @@ +import { log } from '@temporalio/activity'; + +export async function persistToolCall(toolName: string): Promise { + // In production, write to a database / S3 / your audit pipeline. + log.info(`audit: tool ${toolName} completed`); +} diff --git a/strands-agents/src/activities/tools.ts b/strands-agents/src/activities/tools.ts new file mode 100644 index 000000000..db205b158 --- /dev/null +++ b/strands-agents/src/activities/tools.ts @@ -0,0 +1,7 @@ +export async function fetchWeather(input: { city: string }): Promise<{ city: string; temperatureF: number; conditions: string }> { + return { + city: input.city, + temperatureF: 72, + conditions: 'sunny', + }; +} diff --git a/strands-agents/src/activity-interrupt.ts b/strands-agents/src/activity-interrupt.ts new file mode 100644 index 000000000..16ffc1d28 --- /dev/null +++ b/strands-agents/src/activity-interrupt.ts @@ -0,0 +1,39 @@ +import { Client, Connection } from '@temporalio/client'; +import { StrandsPlugin } from '@temporalio/strands-agents'; +import { + activityInterrupt, + activityInterruptApproveSignal, + activityInterruptPendingApprovalQuery, +} from './workflows'; + +async function run() { + const connection = await Connection.connect({ + address: process.env.TEMPORAL_ADDRESS ?? 'localhost:7233', + }); + // The starter goes through the plugin's failure converter so an Interrupt + // payload deserializes cleanly if it propagates to the client. + const client = new Client({ connection, plugins: [new StrandsPlugin()] }); + + const handle = await client.workflow.start(activityInterrupt, { + args: ["Please delete the 'system' user."], + taskQueue: 'strands-agents', + workflowId: 'strands-activity-interrupt', + }); + + let reason: string | null = null; + while (reason === null) { + await new Promise((resolve) => setTimeout(resolve, 500)); + reason = await handle.query(activityInterruptPendingApprovalQuery); + } + console.log(`Approval requested: ${reason}`); + + await handle.signal(activityInterruptApproveSignal, 'approve'); + + const result = await handle.result(); + console.log(`Result: ${result}`); +} + +run().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/strands-agents/src/continue-as-new.ts b/strands-agents/src/continue-as-new.ts new file mode 100644 index 000000000..d658a5f40 --- /dev/null +++ b/strands-agents/src/continue-as-new.ts @@ -0,0 +1,28 @@ +import { Client, Connection } from '@temporalio/client'; +import { chatEnd, chatTurn, chatWorkflow } from './workflows'; + +async function run() { + const connection = await Connection.connect({ + address: process.env.TEMPORAL_ADDRESS ?? 'localhost:7233', + }); + const client = new Client({ connection }); + + const handle = await client.workflow.start(chatWorkflow, { + taskQueue: 'strands-agents', + workflowId: 'strands-continue-as-new', + }); + + for (const prompt of ['Hi! What is durable execution?', 'Give me a one-sentence summary.']) { + const reply = await handle.executeUpdate(chatTurn, { args: [prompt] }); + console.log(`user: ${prompt}`); + console.log(`assistant: ${reply}\n`); + } + + await handle.signal(chatEnd); + await handle.result(); +} + +run().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/strands-agents/src/hello-world.ts b/strands-agents/src/hello-world.ts new file mode 100644 index 000000000..12d5d7eb3 --- /dev/null +++ b/strands-agents/src/hello-world.ts @@ -0,0 +1,21 @@ +import { Client, Connection } from '@temporalio/client'; +import { helloWorld } from './workflows'; + +async function run() { + const connection = await Connection.connect({ + address: process.env.TEMPORAL_ADDRESS ?? 'localhost:7233', + }); + const client = new Client({ connection }); + + const result = await client.workflow.execute(helloWorld, { + args: ['Write a haiku about durable execution.'], + taskQueue: 'strands-agents', + workflowId: 'strands-hello-world', + }); + console.log(`Result: ${result}`); +} + +run().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/strands-agents/src/hooks.ts b/strands-agents/src/hooks.ts new file mode 100644 index 000000000..8e2688f4b --- /dev/null +++ b/strands-agents/src/hooks.ts @@ -0,0 +1,21 @@ +import { Client, Connection } from '@temporalio/client'; +import { hooksWorkflow } from './workflows'; + +async function run() { + const connection = await Connection.connect({ + address: process.env.TEMPORAL_ADDRESS ?? 'localhost:7233', + }); + const client = new Client({ connection }); + + const fired = await client.workflow.execute(hooksWorkflow, { + args: ["Echo 'hello' once."], + taskQueue: 'strands-agents', + workflowId: 'strands-hooks', + }); + console.log(`Tools that fired AfterToolCallEvent: ${JSON.stringify(fired)}`); +} + +run().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/strands-agents/src/human-in-the-loop.ts b/strands-agents/src/human-in-the-loop.ts new file mode 100644 index 000000000..689834195 --- /dev/null +++ b/strands-agents/src/human-in-the-loop.ts @@ -0,0 +1,32 @@ +import { Client, Connection } from '@temporalio/client'; +import { hitlApproveSignal, hitlPendingApprovalQuery, humanInTheLoop } from './workflows'; + +async function run() { + const connection = await Connection.connect({ + address: process.env.TEMPORAL_ADDRESS ?? 'localhost:7233', + }); + const client = new Client({ connection }); + + const handle = await client.workflow.start(humanInTheLoop, { + args: ['Please delete /tmp/sensitive.txt'], + taskQueue: 'strands-agents', + workflowId: 'strands-human-in-the-loop', + }); + + let reason: string | null = null; + while (reason === null) { + await new Promise((resolve) => setTimeout(resolve, 500)); + reason = await handle.query(hitlPendingApprovalQuery); + } + console.log(`Approval requested: ${reason}`); + + await handle.signal(hitlApproveSignal, 'approve'); + + const result = await handle.result(); + console.log(`Result: ${result}`); +} + +run().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/strands-agents/src/mcp-server.ts b/strands-agents/src/mcp-server.ts new file mode 100644 index 000000000..a07075050 --- /dev/null +++ b/strands-agents/src/mcp-server.ts @@ -0,0 +1,24 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { z } from 'zod'; + +const server = new McpServer({ name: 'echo-server', version: '0.1.0' }); + +server.registerTool( + 'echo', + { + description: 'Return the input message unchanged.', + inputSchema: { message: z.string() }, + }, + async ({ message }) => ({ content: [{ type: 'text', text: message }] }) +); + +async function main() { + const transport = new StdioServerTransport(); + await server.connect(transport); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/strands-agents/src/mcp.ts b/strands-agents/src/mcp.ts new file mode 100644 index 000000000..0e52a8358 --- /dev/null +++ b/strands-agents/src/mcp.ts @@ -0,0 +1,21 @@ +import { Client, Connection } from '@temporalio/client'; +import { mcpWorkflow } from './workflows'; + +async function run() { + const connection = await Connection.connect({ + address: process.env.TEMPORAL_ADDRESS ?? 'localhost:7233', + }); + const client = new Client({ connection }); + + const result = await client.workflow.execute(mcpWorkflow, { + args: ["Use the echo tool to echo the message 'hello from MCP'."], + taskQueue: 'strands-agents', + workflowId: 'strands-mcp', + }); + console.log(`Result: ${result}`); +} + +run().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/strands-agents/src/mocha/activity-interrupt.test.ts b/strands-agents/src/mocha/activity-interrupt.test.ts new file mode 100644 index 000000000..559d47140 --- /dev/null +++ b/strands-agents/src/mocha/activity-interrupt.test.ts @@ -0,0 +1,54 @@ +import assert from 'assert'; +import { after, before, describe, it } from 'mocha'; +import { StrandsPlugin } from '@temporalio/strands-agents'; +import { TestWorkflowEnvironment } from '@temporalio/testing'; +import { Worker } from '@temporalio/worker'; +import { deleteThing } from '../activities/activity-interrupt'; +import { activityInterrupt, activityInterruptApproveSignal } from '../workflows'; +import { StubModel, textTurn, toolCallTurn } from './stub-model'; + +describe('activityInterrupt workflow', () => { + let testEnv: TestWorkflowEnvironment; + + before(async () => { + testEnv = await TestWorkflowEnvironment.createLocal(); + }); + + after(async () => { + await testEnv?.teardown(); + }); + + it('round-trips an interrupt thrown from an activity through the failure converter', async () => { + const { client, nativeConnection } = testEnv; + const taskQueue = 'test'; + + const worker = await Worker.create({ + connection: nativeConnection, + taskQueue, + workflowsPath: require.resolve('../workflows'), + activities: { deleteThing }, + plugins: [ + new StrandsPlugin({ + models: { + bedrock: () => + new StubModel([ + toolCallTurn('deleteThing', 'call_1', { name: 'system' }), + textTurn('deleted system'), + ]), + }, + }), + ], + }); + + const result = await worker.runUntil(async () => { + const handle = await client.workflow.start(activityInterrupt, { + args: ['delete system'], + workflowId: 'test-activity-interrupt', + taskQueue, + }); + await handle.signal(activityInterruptApproveSignal, 'approve'); + return handle.result(); + }); + assert.equal(result, 'deleted system'); + }); +}); diff --git a/strands-agents/src/mocha/continue-as-new.test.ts b/strands-agents/src/mocha/continue-as-new.test.ts new file mode 100644 index 000000000..aefd0e863 --- /dev/null +++ b/strands-agents/src/mocha/continue-as-new.test.ts @@ -0,0 +1,53 @@ +import assert from 'assert'; +import { after, before, describe, it } from 'mocha'; +import { StrandsPlugin } from '@temporalio/strands-agents'; +import { TestWorkflowEnvironment } from '@temporalio/testing'; +import { Worker } from '@temporalio/worker'; +import { chatEnd, chatTurn, chatWorkflow } from '../workflows'; +import { StubModel, textTurn } from './stub-model'; + +describe('chatWorkflow', () => { + let testEnv: TestWorkflowEnvironment; + + before(async () => { + testEnv = await TestWorkflowEnvironment.createLocal(); + }); + + after(async () => { + await testEnv?.teardown(); + }); + + it('runs multiple turns and exits on endChat', async () => { + const { client, nativeConnection } = testEnv; + const taskQueue = 'test'; + + const worker = await Worker.create({ + connection: nativeConnection, + taskQueue, + workflowsPath: require.resolve('../workflows'), + plugins: [ + new StrandsPlugin({ + models: { + bedrock: () => new StubModel([textTurn('one'), textTurn('two')]), + }, + }), + ], + }); + + await worker.runUntil(async () => { + const handle = await client.workflow.start(chatWorkflow, { + workflowId: 'test-chat', + taskQueue, + }); + + const replies: string[] = []; + replies.push(await handle.executeUpdate(chatTurn, { args: ['first'] })); + replies.push(await handle.executeUpdate(chatTurn, { args: ['second'] })); + + await handle.signal(chatEnd); + await handle.result(); + + assert.deepEqual(replies, ['one', 'two']); + }); + }); +}); diff --git a/strands-agents/src/mocha/hello-world.test.ts b/strands-agents/src/mocha/hello-world.test.ts new file mode 100644 index 000000000..d5b94c3e3 --- /dev/null +++ b/strands-agents/src/mocha/hello-world.test.ts @@ -0,0 +1,40 @@ +import assert from 'assert'; +import { after, before, describe, it } from 'mocha'; +import { StrandsPlugin } from '@temporalio/strands-agents'; +import { TestWorkflowEnvironment } from '@temporalio/testing'; +import { Worker } from '@temporalio/worker'; +import { helloWorld } from '../workflows'; +import { StubModel, textTurn } from './stub-model'; + +describe('helloWorld workflow', () => { + let testEnv: TestWorkflowEnvironment; + + before(async () => { + testEnv = await TestWorkflowEnvironment.createLocal(); + }); + + after(async () => { + await testEnv?.teardown(); + }); + + it('returns the model reply', async () => { + const { client, nativeConnection } = testEnv; + const taskQueue = 'test'; + + const worker = await Worker.create({ + connection: nativeConnection, + taskQueue, + workflowsPath: require.resolve('../workflows'), + plugins: [new StrandsPlugin({ models: { bedrock: () => new StubModel([textTurn('hello from stub')]) } })], + }); + + const result = await worker.runUntil( + client.workflow.execute(helloWorld, { + args: ['say hi'], + workflowId: 'test-hello', + taskQueue, + }) + ); + assert.equal(result, 'hello from stub'); + }); +}); diff --git a/strands-agents/src/mocha/hooks.test.ts b/strands-agents/src/mocha/hooks.test.ts new file mode 100644 index 000000000..49431ddc5 --- /dev/null +++ b/strands-agents/src/mocha/hooks.test.ts @@ -0,0 +1,49 @@ +import assert from 'assert'; +import { after, before, describe, it } from 'mocha'; +import { StrandsPlugin } from '@temporalio/strands-agents'; +import { TestWorkflowEnvironment } from '@temporalio/testing'; +import { Worker } from '@temporalio/worker'; +import { persistToolCall } from '../activities/hooks'; +import { hooksWorkflow } from '../workflows'; +import { StubModel, textTurn, toolCallTurn } from './stub-model'; + +describe('hooksWorkflow', () => { + let testEnv: TestWorkflowEnvironment; + + before(async () => { + testEnv = await TestWorkflowEnvironment.createLocal(); + }); + + after(async () => { + await testEnv?.teardown(); + }); + + it('fires both the in-workflow callback and the activity-as-hook on AfterToolCallEvent', async () => { + const { client, nativeConnection } = testEnv; + const taskQueue = 'test'; + + const worker = await Worker.create({ + connection: nativeConnection, + taskQueue, + workflowsPath: require.resolve('../workflows'), + activities: { persistToolCall }, + plugins: [ + new StrandsPlugin({ + models: { + bedrock: () => + new StubModel([toolCallTurn('echo', 'call_1', { text: 'hello' }), textTurn('done')]), + }, + }), + ], + }); + + const fired = await worker.runUntil( + client.workflow.execute(hooksWorkflow, { + args: ['say hello'], + workflowId: 'test-hooks', + taskQueue, + }) + ); + assert.deepEqual(fired, ['echo']); + }); +}); diff --git a/strands-agents/src/mocha/human-in-the-loop.test.ts b/strands-agents/src/mocha/human-in-the-loop.test.ts new file mode 100644 index 000000000..0d43546c0 --- /dev/null +++ b/strands-agents/src/mocha/human-in-the-loop.test.ts @@ -0,0 +1,52 @@ +import assert from 'assert'; +import { after, before, describe, it } from 'mocha'; +import { StrandsPlugin } from '@temporalio/strands-agents'; +import { TestWorkflowEnvironment } from '@temporalio/testing'; +import { Worker } from '@temporalio/worker'; +import { hitlApproveSignal, humanInTheLoop } from '../workflows'; +import { StubModel, textTurn, toolCallTurn } from './stub-model'; + +describe('humanInTheLoop workflow', () => { + let testEnv: TestWorkflowEnvironment; + + before(async () => { + testEnv = await TestWorkflowEnvironment.createLocal(); + }); + + after(async () => { + await testEnv?.teardown(); + }); + + it('pauses on BeforeToolCallEvent.interrupt and resumes after a signal', async () => { + const { client, nativeConnection } = testEnv; + const taskQueue = 'test'; + + const worker = await Worker.create({ + connection: nativeConnection, + taskQueue, + workflowsPath: require.resolve('../workflows'), + plugins: [ + new StrandsPlugin({ + models: { + bedrock: () => + new StubModel([ + toolCallTurn('deleteFile', 'call_1', { path: '/tmp/x' }), + textTurn('deleted /tmp/x'), + ]), + }, + }), + ], + }); + + const result = await worker.runUntil(async () => { + const handle = await client.workflow.start(humanInTheLoop, { + args: ['delete /tmp/x'], + workflowId: 'test-hitl', + taskQueue, + }); + await handle.signal(hitlApproveSignal, 'approve'); + return handle.result(); + }); + assert.equal(result, 'deleted /tmp/x'); + }); +}); diff --git a/strands-agents/src/mocha/mcp.test.ts b/strands-agents/src/mocha/mcp.test.ts new file mode 100644 index 000000000..0c706eca8 --- /dev/null +++ b/strands-agents/src/mocha/mcp.test.ts @@ -0,0 +1,91 @@ +import assert from 'assert'; +import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; +import { McpClient } from '@strands-agents/sdk'; +import type { JSONValue, Tool } from '@strands-agents/sdk'; +import { after, before, describe, it } from 'mocha'; +import { StrandsPlugin } from '@temporalio/strands-agents'; +import { TestWorkflowEnvironment } from '@temporalio/testing'; +import { Worker } from '@temporalio/worker'; +import { mcpWorkflow } from '../workflows'; +import { StubModel, textTurn, toolCallTurn } from './stub-model'; + +const STUB_TRANSPORT: Transport = { + async start() {}, + async send() {}, + async close() {}, +}; + +class StubMcpClient extends McpClient { + constructor(private readonly toolList: Array<{ name: string; description: string; inputSchema: unknown }>) { + super({ transport: STUB_TRANSPORT }); + } + + override async connect(): Promise {} + override async disconnect(): Promise {} + override async listTools(): Promise { + return this.toolList.map( + (t) => + ({ + name: t.name, + description: t.description, + toolSpec: { name: t.name, description: t.description, inputSchema: t.inputSchema }, + }) as unknown as Tool + ) as never; + } + override async callTool(tool: { name: string }, args: JSONValue): Promise { + return { + content: [{ type: 'text', text: `mcp:${tool.name}(${JSON.stringify(args)})` }], + isError: false, + } as never; + } +} + +describe('mcpWorkflow', () => { + let testEnv: TestWorkflowEnvironment; + + before(async () => { + testEnv = await TestWorkflowEnvironment.createLocal(); + }); + + after(async () => { + await testEnv?.teardown(); + }); + + it('discovers and calls MCP tools through per-server activities', async () => { + const { client, nativeConnection } = testEnv; + const taskQueue = 'test'; + + const echoFactory = (): McpClient => + new StubMcpClient([ + { + name: 'echo', + description: 'Echo the input', + inputSchema: { type: 'object', properties: { message: { type: 'string' } } }, + }, + ]); + + const worker = await Worker.create({ + connection: nativeConnection, + taskQueue, + workflowsPath: require.resolve('../workflows'), + plugins: [ + new StrandsPlugin({ + models: { + bedrock: () => + new StubModel([toolCallTurn('echo', 'call_1', { message: 'hi' }), textTurn('echoed')]), + }, + mcpClients: { echo: echoFactory }, + }), + ], + }); + + const result = await worker.runUntil( + client.workflow.execute(mcpWorkflow, { + args: ['echo hi'], + workflowId: 'test-mcp', + taskQueue, + }) + ); + assert.equal(result, 'echoed'); + }); +}); diff --git a/strands-agents/src/mocha/streaming.test.ts b/strands-agents/src/mocha/streaming.test.ts new file mode 100644 index 000000000..2390c25b0 --- /dev/null +++ b/strands-agents/src/mocha/streaming.test.ts @@ -0,0 +1,57 @@ +import assert from 'assert'; +import { after, before, describe, it } from 'mocha'; +import { StrandsPlugin } from '@temporalio/strands-agents'; +import { TestWorkflowEnvironment } from '@temporalio/testing'; +import { Worker } from '@temporalio/worker'; +import { WorkflowStreamClient } from '@temporalio/workflow-streams/client'; +import { streamingWorkflow } from '../workflows'; +import { StubModel, textTurn } from './stub-model'; + +describe('streamingWorkflow', () => { + let testEnv: TestWorkflowEnvironment; + + before(async () => { + testEnv = await TestWorkflowEnvironment.createLocal(); + }); + + after(async () => { + await testEnv?.teardown(); + }); + + it('publishes model events to the WorkflowStream subscriber', async () => { + const { client, nativeConnection } = testEnv; + const taskQueue = 'test'; + + const worker = await Worker.create({ + connection: nativeConnection, + taskQueue, + workflowsPath: require.resolve('../workflows'), + plugins: [new StrandsPlugin({ models: { bedrock: () => new StubModel([textTurn('streamed')]) } })], + }); + + await worker.runUntil(async () => { + const handle = await client.workflow.start(streamingWorkflow, { + args: ['say something'], + workflowId: 'test-streaming', + taskQueue, + }); + + const stream = WorkflowStreamClient.create(client, handle.workflowId); + const events: Array<{ type?: string }> = []; + const subscriber = (async () => { + for await (const item of stream.subscribe<{ type?: string }>(['events'], 0, { + pollCooldown: '50 milliseconds', + resultType: true, + })) { + events.push(item.data); + if (item.data.type === 'modelMessageStopEvent') break; + } + })(); + + assert.equal(await handle.result(), 'streamed'); + await subscriber; + assert.ok(events.some((e) => e.type === 'modelMessageStartEvent')); + assert.ok(events.some((e) => e.type === 'modelMessageStopEvent')); + }); + }); +}); diff --git a/strands-agents/src/mocha/structured-output.test.ts b/strands-agents/src/mocha/structured-output.test.ts new file mode 100644 index 000000000..d1f9d208a --- /dev/null +++ b/strands-agents/src/mocha/structured-output.test.ts @@ -0,0 +1,47 @@ +import assert from 'assert'; +import { after, before, describe, it } from 'mocha'; +import { StrandsPlugin } from '@temporalio/strands-agents'; +import { TestWorkflowEnvironment } from '@temporalio/testing'; +import { Worker } from '@temporalio/worker'; +import { structuredOutputWorkflow } from '../workflows'; +import { StubModel, toolCallTurn } from './stub-model'; + +describe('structuredOutputWorkflow', () => { + let testEnv: TestWorkflowEnvironment; + + before(async () => { + testEnv = await TestWorkflowEnvironment.createLocal(); + }); + + after(async () => { + await testEnv?.teardown(); + }); + + it('returns the validated structured output', async () => { + const { client, nativeConnection } = testEnv; + const taskQueue = 'test'; + const person = { name: 'John Smith', age: 30, occupation: 'software engineer' }; + + const worker = await Worker.create({ + connection: nativeConnection, + taskQueue, + workflowsPath: require.resolve('../workflows'), + plugins: [ + new StrandsPlugin({ + models: { + bedrock: () => new StubModel([toolCallTurn('strands_structured_output', 'call_1', person)]), + }, + }), + ], + }); + + const result = await worker.runUntil( + client.workflow.execute(structuredOutputWorkflow, { + args: ['describe John'], + workflowId: 'test-structured', + taskQueue, + }) + ); + assert.deepEqual(result, person); + }); +}); diff --git a/strands-agents/src/mocha/stub-model.ts b/strands-agents/src/mocha/stub-model.ts new file mode 100644 index 000000000..34774cdf7 --- /dev/null +++ b/strands-agents/src/mocha/stub-model.ts @@ -0,0 +1,53 @@ +import { Model } from '@strands-agents/sdk'; +import type { BaseModelConfig, Message, ModelStreamEvent, StreamOptions } from '@strands-agents/sdk'; + +export class StubModel extends Model { + private readonly turns: ModelStreamEvent[][]; + private turnIndex = 0; + + constructor(turns: ModelStreamEvent[][]) { + super(); + this.turns = turns; + } + + override updateConfig(_: BaseModelConfig): void {} + override getConfig(): BaseModelConfig { + return {}; + } + + override async *stream(_messages: Message[], _options?: StreamOptions): AsyncIterable { + if (this.turnIndex >= this.turns.length) { + throw new Error('StubModel exhausted: no more turns'); + } + const events = this.turns[this.turnIndex++]!; + for (const event of events) { + yield event; + } + } +} + +export function textTurn(text: string): ModelStreamEvent[] { + return [ + { type: 'modelMessageStartEvent', role: 'assistant' }, + { type: 'modelContentBlockStartEvent' }, + { type: 'modelContentBlockDeltaEvent', delta: { type: 'textDelta', text } }, + { type: 'modelContentBlockStopEvent' }, + { type: 'modelMessageStopEvent', stopReason: 'endTurn' }, + ]; +} + +export function toolCallTurn(toolName: string, toolUseId: string, input: object): ModelStreamEvent[] { + return [ + { type: 'modelMessageStartEvent', role: 'assistant' }, + { + type: 'modelContentBlockStartEvent', + start: { type: 'toolUseStart', name: toolName, toolUseId }, + }, + { + type: 'modelContentBlockDeltaEvent', + delta: { type: 'toolUseInputDelta', input: JSON.stringify(input) }, + }, + { type: 'modelContentBlockStopEvent' }, + { type: 'modelMessageStopEvent', stopReason: 'toolUse' }, + ]; +} diff --git a/strands-agents/src/mocha/tools.test.ts b/strands-agents/src/mocha/tools.test.ts new file mode 100644 index 000000000..631565e1b --- /dev/null +++ b/strands-agents/src/mocha/tools.test.ts @@ -0,0 +1,53 @@ +import assert from 'assert'; +import { after, before, describe, it } from 'mocha'; +import { StrandsPlugin } from '@temporalio/strands-agents'; +import { TestWorkflowEnvironment } from '@temporalio/testing'; +import { Worker } from '@temporalio/worker'; +import { fetchWeather } from '../activities/tools'; +import { toolsWorkflow } from '../workflows'; +import { StubModel, textTurn, toolCallTurn } from './stub-model'; + +describe('toolsWorkflow', () => { + let testEnv: TestWorkflowEnvironment; + + before(async () => { + testEnv = await TestWorkflowEnvironment.createLocal(); + }); + + after(async () => { + await testEnv?.teardown(); + }); + + it('dispatches the in-workflow tool and the activity-as-tool', async () => { + const { client, nativeConnection } = testEnv; + const taskQueue = 'test'; + + const worker = await Worker.create({ + connection: nativeConnection, + taskQueue, + workflowsPath: require.resolve('../workflows'), + activities: { fetchWeather }, + plugins: [ + new StrandsPlugin({ + models: { + bedrock: () => + new StubModel([ + toolCallTurn('letterCounter', 'call_1', { word: 'strawberry', letter: 'r' }), + toolCallTurn('fetchWeather', 'call_2', { city: 'San Francisco' }), + textTurn('there are 3 Rs and it is sunny'), + ]), + }, + }), + ], + }); + + const result = await worker.runUntil( + client.workflow.execute(toolsWorkflow, { + args: ['count r in strawberry and check the weather'], + workflowId: 'test-tools', + taskQueue, + }) + ); + assert.equal(result, 'there are 3 Rs and it is sunny'); + }); +}); diff --git a/strands-agents/src/streaming.ts b/strands-agents/src/streaming.ts new file mode 100644 index 000000000..c4e844988 --- /dev/null +++ b/strands-agents/src/streaming.ts @@ -0,0 +1,47 @@ +import { Client, Connection } from '@temporalio/client'; +import { WorkflowStreamClient } from '@temporalio/workflow-streams/client'; +import { streamingWorkflow } from './workflows'; + +interface StreamEvent { + type?: string; + delta?: { type?: string; text?: string }; +} + +async function run() { + const connection = await Connection.connect({ + address: process.env.TEMPORAL_ADDRESS ?? 'localhost:7233', + }); + const client = new Client({ connection }); + const workflowId = 'strands-streaming'; + + const handle = await client.workflow.start(streamingWorkflow, { + args: ['Count from 1 to 5, one number per sentence.'], + taskQueue: 'strands-agents', + workflowId, + }); + + const stream = WorkflowStreamClient.create(client, workflowId); + const consume = (async () => { + for await (const item of stream.subscribe(['events'], 0, { + pollCooldown: '50 milliseconds', + resultType: true, + })) { + const event = item.data; + if (event.type === 'modelContentBlockDeltaEvent' && event.delta?.type === 'textDelta' && event.delta.text) { + process.stdout.write(event.delta.text); + } else if (event.type === 'modelMessageStopEvent') { + process.stdout.write('\n'); + return; + } + } + })(); + + const result = await handle.result(); + await consume; + console.log(`Final result: ${result}`); +} + +run().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/strands-agents/src/structured-output.ts b/strands-agents/src/structured-output.ts new file mode 100644 index 000000000..2cc3386a8 --- /dev/null +++ b/strands-agents/src/structured-output.ts @@ -0,0 +1,22 @@ +import { Client, Connection } from '@temporalio/client'; +import { StrandsPlugin } from '@temporalio/strands-agents'; +import { structuredOutputWorkflow } from './workflows'; + +async function run() { + const connection = await Connection.connect({ + address: process.env.TEMPORAL_ADDRESS ?? 'localhost:7233', + }); + const client = new Client({ connection, plugins: [new StrandsPlugin()] }); + + const person = await client.workflow.execute(structuredOutputWorkflow, { + args: ['John Smith is a 30 year-old software engineer.'], + taskQueue: 'strands-agents', + workflowId: 'strands-structured-output', + }); + console.log(`name=${person.name} age=${person.age} occupation=${person.occupation}`); +} + +run().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/strands-agents/src/tools.ts b/strands-agents/src/tools.ts new file mode 100644 index 000000000..038236095 --- /dev/null +++ b/strands-agents/src/tools.ts @@ -0,0 +1,27 @@ +import { Client, Connection } from '@temporalio/client'; +import { toolsWorkflow } from './workflows'; + +async function run() { + const connection = await Connection.connect({ + address: process.env.TEMPORAL_ADDRESS ?? 'localhost:7233', + }); + const client = new Client({ connection }); + + const result = await client.workflow.execute(toolsWorkflow, { + args: [ + [ + 'Please do two things:', + "1. Count the letter R's in 'strawberry'.", + '2. Fetch the weather in San Francisco.', + ].join('\n'), + ], + taskQueue: 'strands-agents', + workflowId: 'strands-tools', + }); + console.log(`Result: ${result}`); +} + +run().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/strands-agents/src/worker.ts b/strands-agents/src/worker.ts new file mode 100644 index 000000000..01aac19b8 --- /dev/null +++ b/strands-agents/src/worker.ts @@ -0,0 +1,44 @@ +import path from 'node:path'; +import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; +import { McpClient } from '@strands-agents/sdk'; +import { StrandsPlugin } from '@temporalio/strands-agents'; +import { NativeConnection, Worker } from '@temporalio/worker'; +import * as activities from './activities'; + +const ECHO_SERVER = path.join(__dirname, 'mcp-server.ts'); + +function makeEchoClient(): McpClient { + return new McpClient({ + transport: new StdioClientTransport({ + command: 'npx', + args: ['ts-node', ECHO_SERVER], + }), + }); +} + +async function run() { + const connection = await NativeConnection.connect({ + address: process.env.TEMPORAL_ADDRESS ?? 'localhost:7233', + }); + try { + const worker = await Worker.create({ + connection, + taskQueue: 'strands-agents', + workflowsPath: require.resolve('./workflows'), + activities, + // Omit `models:` so the plugin registers its default `BedrockModel` under + // the name `bedrock`. To use a different provider or pin a model ID, + // pass e.g. `models: { bedrock: () => new BedrockModel({ modelId: '...' }) }`. + plugins: [new StrandsPlugin({ mcpClients: { echo: makeEchoClient } })], + }); + console.log('Worker started. Ctrl+C to exit.'); + await worker.run(); + } finally { + await connection.close(); + } +} + +run().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/strands-agents/src/workflows.ts b/strands-agents/src/workflows.ts new file mode 100644 index 000000000..4aee1a442 --- /dev/null +++ b/strands-agents/src/workflows.ts @@ -0,0 +1,17 @@ +export { helloWorld } from './workflows/hello-world'; +export { toolsWorkflow } from './workflows/tools'; +export { + humanInTheLoop, + hitlApproveSignal, + hitlPendingApprovalQuery, +} from './workflows/human-in-the-loop'; +export { + activityInterrupt, + activityInterruptApproveSignal, + activityInterruptPendingApprovalQuery, +} from './workflows/activity-interrupt'; +export { hooksWorkflow } from './workflows/hooks'; +export { mcpWorkflow } from './workflows/mcp'; +export { structuredOutputWorkflow, PersonInfo } from './workflows/structured-output'; +export { streamingWorkflow } from './workflows/streaming'; +export { chatWorkflow, chatTurn, chatEnd, chatMessages, type ChatInput } from './workflows/continue-as-new'; diff --git a/strands-agents/src/workflows/activity-interrupt.ts b/strands-agents/src/workflows/activity-interrupt.ts new file mode 100644 index 000000000..72fc9d5d8 --- /dev/null +++ b/strands-agents/src/workflows/activity-interrupt.ts @@ -0,0 +1,47 @@ +import type { InterruptResponseContent, InterruptResponseContentData } from '@strands-agents/sdk'; +import { TemporalAgent, workflow as strandsWorkflow } from '@temporalio/strands-agents'; +import { condition, defineQuery, defineSignal, setHandler } from '@temporalio/workflow'; + +export const activityInterruptApproveSignal = defineSignal<[string]>('activityInterruptApprove'); +export const activityInterruptPendingApprovalQuery = defineQuery('activityInterruptPendingApproval'); + +export async function activityInterrupt(prompt: string): Promise { + let approval: string | null = null; + let pendingReason: string | null = null; + + setHandler(activityInterruptApproveSignal, (response) => { + approval = response; + }); + setHandler(activityInterruptPendingApprovalQuery, () => pendingReason); + + const agent = new TemporalAgent({ + activityOptions: { startToCloseTimeout: '60 seconds', retry: { maximumAttempts: 3 } }, + tools: [ + strandsWorkflow.activityAsTool('deleteThing', { + description: 'Delete a thing by name.', + inputSchema: { + type: 'object', + properties: { name: { type: 'string' } }, + required: ['name'], + }, + activityOptions: { startToCloseTimeout: '30 seconds', retry: { maximumAttempts: 3 } }, + }), + ], + }); + + let result = await agent.invoke(prompt); + while (result.stopReason === 'interrupt') { + const interrupts = result.interrupts ?? []; + pendingReason = (interrupts[0]?.reason as string | undefined) ?? null; + await condition(() => approval !== null); + const response = approval!; + approval = null; + pendingReason = null; + const responses: InterruptResponseContentData[] = interrupts.map((i) => ({ + type: 'interruptResponse', + interruptResponse: { interruptId: i.id, response }, + })); + result = await agent.invoke(responses as InterruptResponseContent[]); + } + return result.toString(); +} diff --git a/strands-agents/src/workflows/continue-as-new.ts b/strands-agents/src/workflows/continue-as-new.ts new file mode 100644 index 000000000..def7e8e46 --- /dev/null +++ b/strands-agents/src/workflows/continue-as-new.ts @@ -0,0 +1,60 @@ +import type { Message } from '@strands-agents/sdk'; +import { TemporalAgent } from '@temporalio/strands-agents'; +import { + allHandlersFinished, + condition, + continueAsNew, + defineQuery, + defineSignal, + defineUpdate, + setHandler, + workflowInfo, +} from '@temporalio/workflow'; + +export interface ChatInput { + messages?: Message[]; +} + +export const chatTurn = defineUpdate('turn'); +export const chatEnd = defineSignal('endChat'); +export const chatMessages = defineQuery('messages'); + +export async function chatWorkflow(input: ChatInput = {}): Promise { + let done = false; + let agent: TemporalAgent | null = null; + // Serialize concurrent `turn` updates so they can't interleave on `agent.messages`. + let pending: Promise = Promise.resolve(); + + setHandler(chatTurn, async (prompt) => { + await condition(() => agent !== null); + const prev = pending; + let release!: () => void; + pending = new Promise((resolve) => { + release = resolve; + }); + try { + await prev; + const result = await agent!.invoke(prompt); + return result.toString().trim(); + } finally { + release(); + } + }); + setHandler(chatEnd, () => { + done = true; + }); + setHandler(chatMessages, () => (agent ? [...agent.messages] : [])); + + agent = new TemporalAgent({ + activityOptions: { startToCloseTimeout: '60 seconds', retry: { maximumAttempts: 3 } }, + messages: input.messages ?? [], + }); + + await condition(() => done || workflowInfo().continueAsNewSuggested); + // Drain in-flight `turn` updates before exiting or handing off. + await condition(allHandlersFinished); + + if (!done) { + await continueAsNew({ messages: agent.messages }); + } +} diff --git a/strands-agents/src/workflows/hello-world.ts b/strands-agents/src/workflows/hello-world.ts new file mode 100644 index 000000000..37a52f290 --- /dev/null +++ b/strands-agents/src/workflows/hello-world.ts @@ -0,0 +1,9 @@ +import { TemporalAgent } from '@temporalio/strands-agents'; + +export async function helloWorld(prompt: string): Promise { + const agent = new TemporalAgent({ + activityOptions: { startToCloseTimeout: '60 seconds', retry: { maximumAttempts: 3 } }, + }); + const result = await agent.invoke(prompt); + return result.toString(); +} diff --git a/strands-agents/src/workflows/hooks.ts b/strands-agents/src/workflows/hooks.ts new file mode 100644 index 000000000..1d7e7c0b2 --- /dev/null +++ b/strands-agents/src/workflows/hooks.ts @@ -0,0 +1,36 @@ +import { AfterToolCallEvent, tool } from '@strands-agents/sdk'; +import { TemporalAgent, workflow as strandsWorkflow } from '@temporalio/strands-agents'; +import { z } from 'zod'; + +const echo = tool({ + name: 'echo', + description: 'Echo back the input text.', + inputSchema: z.object({ text: z.string() }), + callback: ({ text }) => text, +}); + +export async function hooksWorkflow(prompt: string): Promise { + const fired: string[] = []; + + const agent = new TemporalAgent({ + activityOptions: { startToCloseTimeout: '60 seconds', retry: { maximumAttempts: 3 } }, + tools: [echo], + }); + + // Callback 1: in-workflow, deterministic state mutation. + agent.addHook(AfterToolCallEvent, (event) => { + fired.push(event.toolUse.name); + }); + + // Callback 2: dispatch to a Temporal activity for I/O. + agent.addHook( + AfterToolCallEvent, + strandsWorkflow.activityAsHook('persistToolCall', { + activityInput: (event) => event.toolUse.name, + activityOptions: { startToCloseTimeout: '15 seconds', retry: { maximumAttempts: 3 } }, + }) + ); + + await agent.invoke(prompt); + return fired; +} diff --git a/strands-agents/src/workflows/human-in-the-loop.ts b/strands-agents/src/workflows/human-in-the-loop.ts new file mode 100644 index 000000000..71606bf61 --- /dev/null +++ b/strands-agents/src/workflows/human-in-the-loop.ts @@ -0,0 +1,62 @@ +import { + BeforeToolCallEvent, + tool, + type InterruptResponseContent, + type InterruptResponseContentData, +} from '@strands-agents/sdk'; +import { TemporalAgent } from '@temporalio/strands-agents'; +import { condition, defineQuery, defineSignal, setHandler } from '@temporalio/workflow'; +import { z } from 'zod'; + +export const hitlApproveSignal = defineSignal<[string]>('hitlApprove'); +export const hitlPendingApprovalQuery = defineQuery('hitlPendingApproval'); + +const deleteFile = tool({ + name: 'deleteFile', + description: 'Delete a file at the given path.', + inputSchema: z.object({ path: z.string() }), + callback: ({ path }) => `deleted ${path}`, +}); + +export async function humanInTheLoop(prompt: string): Promise { + let approval: string | null = null; + let pendingReason: string | null = null; + + setHandler(hitlApproveSignal, (response) => { + approval = response; + }); + setHandler(hitlPendingApprovalQuery, () => pendingReason); + + const agent = new TemporalAgent({ + activityOptions: { startToCloseTimeout: '60 seconds', retry: { maximumAttempts: 3 } }, + tools: [deleteFile], + }); + + agent.addHook(BeforeToolCallEvent, (event) => { + if (event.toolUse.name !== 'deleteFile') return; + const path = (event.toolUse.input as { path?: string }).path; + const response = event.interrupt({ + name: 'approval', + reason: `approve delete of ${path}?`, + }); + if (response !== 'approve') { + event.cancel = 'denied'; + } + }); + + let result = await agent.invoke(prompt); + while (result.stopReason === 'interrupt') { + const interrupts = result.interrupts ?? []; + pendingReason = (interrupts[0]?.reason as string | undefined) ?? null; + await condition(() => approval !== null); + const response = approval!; + approval = null; + pendingReason = null; + const responses: InterruptResponseContentData[] = interrupts.map((i) => ({ + type: 'interruptResponse', + interruptResponse: { interruptId: i.id, response }, + })); + result = await agent.invoke(responses as InterruptResponseContent[]); + } + return result.toString(); +} diff --git a/strands-agents/src/workflows/mcp.ts b/strands-agents/src/workflows/mcp.ts new file mode 100644 index 000000000..34640916b --- /dev/null +++ b/strands-agents/src/workflows/mcp.ts @@ -0,0 +1,14 @@ +import { TemporalAgent, TemporalMCPClient } from '@temporalio/strands-agents'; + +export async function mcpWorkflow(prompt: string): Promise { + const echo = new TemporalMCPClient({ + server: 'echo', + activityOptions: { startToCloseTimeout: '30 seconds', retry: { maximumAttempts: 3 } }, + }); + const agent = new TemporalAgent({ + activityOptions: { startToCloseTimeout: '60 seconds', retry: { maximumAttempts: 3 } }, + tools: [echo], + }); + const result = await agent.invoke(prompt); + return result.toString(); +} diff --git a/strands-agents/src/workflows/streaming.ts b/strands-agents/src/workflows/streaming.ts new file mode 100644 index 000000000..bbfee83c8 --- /dev/null +++ b/strands-agents/src/workflows/streaming.ts @@ -0,0 +1,15 @@ +import { TemporalAgent } from '@temporalio/strands-agents'; +import { WorkflowStream } from '@temporalio/workflow-streams/workflow'; + +export async function streamingWorkflow(prompt: string): Promise { + // Constructing the stream installs the publish/poll handlers that + // WorkflowStreamClient calls. Nothing in the workflow body reads from it. + void new WorkflowStream(); + + const agent = new TemporalAgent({ + activityOptions: { startToCloseTimeout: '60 seconds', retry: { maximumAttempts: 3 } }, + streamingTopic: 'events', + }); + const result = await agent.invoke(prompt); + return result.toString(); +} diff --git a/strands-agents/src/workflows/structured-output.ts b/strands-agents/src/workflows/structured-output.ts new file mode 100644 index 000000000..a2ed03c4f --- /dev/null +++ b/strands-agents/src/workflows/structured-output.ts @@ -0,0 +1,19 @@ +import { TemporalAgent } from '@temporalio/strands-agents'; +import { z } from 'zod'; + +export const PersonInfo = z.object({ + name: z.string().describe('Name of the person'), + age: z.number().describe('Age of the person'), + occupation: z.string().describe('Occupation of the person'), +}); + +export type PersonInfo = z.infer; + +export async function structuredOutputWorkflow(prompt: string): Promise { + const agent = new TemporalAgent({ + activityOptions: { startToCloseTimeout: '60 seconds', retry: { maximumAttempts: 3 } }, + structuredOutputSchema: PersonInfo, + }); + const result = await agent.invoke(prompt); + return result.structuredOutput as PersonInfo; +} diff --git a/strands-agents/src/workflows/tools.ts b/strands-agents/src/workflows/tools.ts new file mode 100644 index 000000000..2b5c57fc2 --- /dev/null +++ b/strands-agents/src/workflows/tools.ts @@ -0,0 +1,33 @@ +import { tool } from '@strands-agents/sdk'; +import { TemporalAgent, workflow as strandsWorkflow } from '@temporalio/strands-agents'; +import { z } from 'zod'; + +const letterCounter = tool({ + name: 'letterCounter', + description: 'Count how many times `letter` appears in `word` (case-insensitive).', + inputSchema: z.object({ + word: z.string(), + letter: z.string(), + }), + callback: ({ word, letter }) => word.toLowerCase().split(letter.toLowerCase()).length - 1, +}); + +export async function toolsWorkflow(prompt: string): Promise { + const agent = new TemporalAgent({ + activityOptions: { startToCloseTimeout: '60 seconds', retry: { maximumAttempts: 3 } }, + tools: [ + letterCounter, + strandsWorkflow.activityAsTool('fetchWeather', { + description: 'Fetch the current weather for a city.', + inputSchema: { + type: 'object', + properties: { city: { type: 'string' } }, + required: ['city'], + }, + activityOptions: { startToCloseTimeout: '30 seconds', retry: { maximumAttempts: 3 } }, + }), + ], + }); + const result = await agent.invoke(prompt); + return result.toString(); +} diff --git a/strands-agents/tsconfig.json b/strands-agents/tsconfig.json new file mode 100644 index 000000000..488f2c62a --- /dev/null +++ b/strands-agents/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "@tsconfig/node22/tsconfig.json", + "version": "5.6.3", + "compilerOptions": { + "lib": ["es2021"], + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "rootDir": "./src", + "outDir": "./lib" + }, + "include": ["src/**/*.ts"] +} From 05091f6888fc90cbd044a02afe6cddb0577c15a8 Mon Sep 17 00:00:00 2001 From: Brian Strauch Date: Tue, 2 Jun 2026 13:54:23 -0700 Subject: [PATCH 2/6] Add snipsync markers to Strands Agents samples Wraps each Strands Agents sample with @@@SNIPSTART/@@@SNIPEND markers (typescript-strands-* labels) so the TypeScript integration guide in the documentation repo can pull snippets via snipsync. Co-Authored-By: Claude Opus 4.8 (1M context) --- strands-agents/src/activities/activity-interrupt.ts | 2 ++ strands-agents/src/activities/hooks.ts | 2 ++ strands-agents/src/activities/tools.ts | 2 ++ strands-agents/src/hello-world.ts | 2 ++ strands-agents/src/streaming.ts | 2 ++ strands-agents/src/worker.ts | 2 ++ strands-agents/src/workflows/activity-interrupt.ts | 2 ++ strands-agents/src/workflows/continue-as-new.ts | 2 ++ strands-agents/src/workflows/hello-world.ts | 2 ++ strands-agents/src/workflows/hooks.ts | 2 ++ strands-agents/src/workflows/human-in-the-loop.ts | 2 ++ strands-agents/src/workflows/mcp.ts | 2 ++ strands-agents/src/workflows/streaming.ts | 2 ++ strands-agents/src/workflows/structured-output.ts | 2 ++ strands-agents/src/workflows/tools.ts | 2 ++ 15 files changed, 30 insertions(+) diff --git a/strands-agents/src/activities/activity-interrupt.ts b/strands-agents/src/activities/activity-interrupt.ts index d8cc73f5d..cc6031e4e 100644 --- a/strands-agents/src/activities/activity-interrupt.ts +++ b/strands-agents/src/activities/activity-interrupt.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-activity-interrupt-activity import { ApplicationFailure } from '@temporalio/common'; import { STRANDS_INTERRUPT_TYPE } from '@temporalio/strands-agents'; @@ -29,3 +30,4 @@ export async function deleteThing(input: { name: string }): Promise { } return `deleted ${input.name}`; } +// @@@SNIPEND diff --git a/strands-agents/src/activities/hooks.ts b/strands-agents/src/activities/hooks.ts index 27d81b2ba..b2d120fda 100644 --- a/strands-agents/src/activities/hooks.ts +++ b/strands-agents/src/activities/hooks.ts @@ -1,6 +1,8 @@ +// @@@SNIPSTART typescript-strands-hooks-activity import { log } from '@temporalio/activity'; export async function persistToolCall(toolName: string): Promise { // In production, write to a database / S3 / your audit pipeline. log.info(`audit: tool ${toolName} completed`); } +// @@@SNIPEND diff --git a/strands-agents/src/activities/tools.ts b/strands-agents/src/activities/tools.ts index db205b158..57d032432 100644 --- a/strands-agents/src/activities/tools.ts +++ b/strands-agents/src/activities/tools.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-tools-activity export async function fetchWeather(input: { city: string }): Promise<{ city: string; temperatureF: number; conditions: string }> { return { city: input.city, @@ -5,3 +6,4 @@ export async function fetchWeather(input: { city: string }): Promise<{ city: str conditions: 'sunny', }; } +// @@@SNIPEND diff --git a/strands-agents/src/hello-world.ts b/strands-agents/src/hello-world.ts index 12d5d7eb3..9f5a18d67 100644 --- a/strands-agents/src/hello-world.ts +++ b/strands-agents/src/hello-world.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-hello-world-client import { Client, Connection } from '@temporalio/client'; import { helloWorld } from './workflows'; @@ -19,3 +20,4 @@ run().catch((err) => { console.error(err); process.exit(1); }); +// @@@SNIPEND diff --git a/strands-agents/src/streaming.ts b/strands-agents/src/streaming.ts index c4e844988..5b7e45ccb 100644 --- a/strands-agents/src/streaming.ts +++ b/strands-agents/src/streaming.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-streaming-client import { Client, Connection } from '@temporalio/client'; import { WorkflowStreamClient } from '@temporalio/workflow-streams/client'; import { streamingWorkflow } from './workflows'; @@ -45,3 +46,4 @@ run().catch((err) => { console.error(err); process.exit(1); }); +// @@@SNIPEND diff --git a/strands-agents/src/worker.ts b/strands-agents/src/worker.ts index 01aac19b8..af0602b93 100644 --- a/strands-agents/src/worker.ts +++ b/strands-agents/src/worker.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-worker import path from 'node:path'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; import { McpClient } from '@strands-agents/sdk'; @@ -42,3 +43,4 @@ run().catch((err) => { console.error(err); process.exit(1); }); +// @@@SNIPEND diff --git a/strands-agents/src/workflows/activity-interrupt.ts b/strands-agents/src/workflows/activity-interrupt.ts index 72fc9d5d8..dbf9d0c8a 100644 --- a/strands-agents/src/workflows/activity-interrupt.ts +++ b/strands-agents/src/workflows/activity-interrupt.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-activity-interrupt-workflow import type { InterruptResponseContent, InterruptResponseContentData } from '@strands-agents/sdk'; import { TemporalAgent, workflow as strandsWorkflow } from '@temporalio/strands-agents'; import { condition, defineQuery, defineSignal, setHandler } from '@temporalio/workflow'; @@ -45,3 +46,4 @@ export async function activityInterrupt(prompt: string): Promise { } return result.toString(); } +// @@@SNIPEND diff --git a/strands-agents/src/workflows/continue-as-new.ts b/strands-agents/src/workflows/continue-as-new.ts index def7e8e46..a93772d80 100644 --- a/strands-agents/src/workflows/continue-as-new.ts +++ b/strands-agents/src/workflows/continue-as-new.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-continue-as-new-workflow import type { Message } from '@strands-agents/sdk'; import { TemporalAgent } from '@temporalio/strands-agents'; import { @@ -58,3 +59,4 @@ export async function chatWorkflow(input: ChatInput = {}): Promise { await continueAsNew({ messages: agent.messages }); } } +// @@@SNIPEND diff --git a/strands-agents/src/workflows/hello-world.ts b/strands-agents/src/workflows/hello-world.ts index 37a52f290..2cec15ca3 100644 --- a/strands-agents/src/workflows/hello-world.ts +++ b/strands-agents/src/workflows/hello-world.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-hello-world-workflow import { TemporalAgent } from '@temporalio/strands-agents'; export async function helloWorld(prompt: string): Promise { @@ -7,3 +8,4 @@ export async function helloWorld(prompt: string): Promise { const result = await agent.invoke(prompt); return result.toString(); } +// @@@SNIPEND diff --git a/strands-agents/src/workflows/hooks.ts b/strands-agents/src/workflows/hooks.ts index 1d7e7c0b2..f240f03d0 100644 --- a/strands-agents/src/workflows/hooks.ts +++ b/strands-agents/src/workflows/hooks.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-hooks-workflow import { AfterToolCallEvent, tool } from '@strands-agents/sdk'; import { TemporalAgent, workflow as strandsWorkflow } from '@temporalio/strands-agents'; import { z } from 'zod'; @@ -34,3 +35,4 @@ export async function hooksWorkflow(prompt: string): Promise { await agent.invoke(prompt); return fired; } +// @@@SNIPEND diff --git a/strands-agents/src/workflows/human-in-the-loop.ts b/strands-agents/src/workflows/human-in-the-loop.ts index 71606bf61..bd6ec0d35 100644 --- a/strands-agents/src/workflows/human-in-the-loop.ts +++ b/strands-agents/src/workflows/human-in-the-loop.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-human-in-the-loop-workflow import { BeforeToolCallEvent, tool, @@ -60,3 +61,4 @@ export async function humanInTheLoop(prompt: string): Promise { } return result.toString(); } +// @@@SNIPEND diff --git a/strands-agents/src/workflows/mcp.ts b/strands-agents/src/workflows/mcp.ts index 34640916b..99969d6fd 100644 --- a/strands-agents/src/workflows/mcp.ts +++ b/strands-agents/src/workflows/mcp.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-mcp-workflow import { TemporalAgent, TemporalMCPClient } from '@temporalio/strands-agents'; export async function mcpWorkflow(prompt: string): Promise { @@ -12,3 +13,4 @@ export async function mcpWorkflow(prompt: string): Promise { const result = await agent.invoke(prompt); return result.toString(); } +// @@@SNIPEND diff --git a/strands-agents/src/workflows/streaming.ts b/strands-agents/src/workflows/streaming.ts index bbfee83c8..51c6fa055 100644 --- a/strands-agents/src/workflows/streaming.ts +++ b/strands-agents/src/workflows/streaming.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-streaming-workflow import { TemporalAgent } from '@temporalio/strands-agents'; import { WorkflowStream } from '@temporalio/workflow-streams/workflow'; @@ -13,3 +14,4 @@ export async function streamingWorkflow(prompt: string): Promise { const result = await agent.invoke(prompt); return result.toString(); } +// @@@SNIPEND diff --git a/strands-agents/src/workflows/structured-output.ts b/strands-agents/src/workflows/structured-output.ts index a2ed03c4f..2e3e8354f 100644 --- a/strands-agents/src/workflows/structured-output.ts +++ b/strands-agents/src/workflows/structured-output.ts @@ -1,3 +1,4 @@ +// @@@SNIPSTART typescript-strands-structured-output-workflow import { TemporalAgent } from '@temporalio/strands-agents'; import { z } from 'zod'; @@ -17,3 +18,4 @@ export async function structuredOutputWorkflow(prompt: string): Promise { const result = await agent.invoke(prompt); return result.toString(); } +// @@@SNIPEND From c90ba3e5f3bb1bd0b01a88a033b0a5ba905601ea Mon Sep 17 00:00:00 2001 From: Brian Strauch Date: Fri, 24 Jul 2026 09:42:19 -0700 Subject: [PATCH 3/6] chore: bump strands-agents sample to 1.21.1 Switch @temporalio/strands-agents and @temporalio/workflow-streams from local tgz references to published npm packages, and bump all @temporalio/* deps from 1.17.2 to 1.21.1. Co-Authored-By: Claude Opus 4.8 (1M context) --- strands-agents/package.json | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/strands-agents/package.json b/strands-agents/package.json index 1f9e1d65f..07c117222 100644 --- a/strands-agents/package.json +++ b/strands-agents/package.json @@ -30,23 +30,20 @@ "src" ] }, - "overrides": { - "@temporalio/common": "file:../../sdk-typescript/packages/common/temporalio-common-1.17.2.tgz" - }, "dependencies": { "@modelcontextprotocol/sdk": "^1.25.2", "@strands-agents/sdk": "^1.3.0", - "@temporalio/activity": "^1.17.2", - "@temporalio/client": "^1.17.2", - "@temporalio/envconfig": "^1.17.2", - "@temporalio/strands-agents": "file:../../sdk-typescript/contrib/strands/temporalio-strands-agents-1.17.2.tgz", - "@temporalio/worker": "^1.17.2", - "@temporalio/workflow": "^1.17.2", - "@temporalio/workflow-streams": "file:../../sdk-typescript/contrib/workflow-streams/temporalio-workflow-streams-1.17.0.tgz", + "@temporalio/activity": "^1.21.1", + "@temporalio/client": "^1.21.1", + "@temporalio/envconfig": "^1.21.1", + "@temporalio/strands-agents": "^1.21.1", + "@temporalio/worker": "^1.21.1", + "@temporalio/workflow": "^1.21.1", + "@temporalio/workflow-streams": "^1.21.1", "zod": "^4.1.12" }, "devDependencies": { - "@temporalio/testing": "^1.17.2", + "@temporalio/testing": "^1.21.1", "@tsconfig/node22": "^22.0.0", "@types/mocha": "8.x", "@types/node": "^22.9.1", From 9b1ffd2123635cf9aab722ecefd5c5884b816365 Mon Sep 17 00:00:00 2001 From: Brian Strauch Date: Fri, 24 Jul 2026 09:44:00 -0700 Subject: [PATCH 4/6] chore: register strands-agents in list-of-samples Co-Authored-By: Claude Opus 4.8 (1M context) --- .scripts/list-of-samples.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.scripts/list-of-samples.json b/.scripts/list-of-samples.json index 32628e13f..56d850fe2 100644 --- a/.scripts/list-of-samples.json +++ b/.scripts/list-of-samples.json @@ -49,6 +49,7 @@ "snippets", "standalone-activity", "state", + "strands-agents", "timer-examples", "timer-progress", "vscode-debugger", From 5b381c5bf43a6ea0dec6376e2fc1cda4daeb542a Mon Sep 17 00:00:00 2001 From: Brian Strauch Date: Fri, 24 Jul 2026 09:52:45 -0700 Subject: [PATCH 5/6] chore: fix CI for strands-agents (lockfile, shared configs, formatting) - Regenerate pnpm-lock.yaml (was stale after the 1.21.1 dep bump, which failed CI's frozen-lockfile install) - Add missing shared config files (.gitignore, .npmrc, .nvmrc, .post-create, .prettierignore, .prettierrc) that the sample was created without - Run prettier over the sample source to satisfy format:check Co-Authored-By: Claude Opus 4.8 (1M context) --- pnpm-lock.yaml | 740 +++++++++++++++++- strands-agents/.gitignore | 2 + strands-agents/.npmrc | 1 + strands-agents/.nvmrc | 1 + strands-agents/.post-create | 18 + strands-agents/.prettierignore | 1 + strands-agents/.prettierrc | 2 + strands-agents/README.md | 22 +- strands-agents/src/activities/tools.ts | 4 +- strands-agents/src/activity-interrupt.ts | 6 +- strands-agents/src/mcp-server.ts | 2 +- .../src/mocha/activity-interrupt.test.ts | 5 +- strands-agents/src/mocha/hello-world.test.ts | 2 +- strands-agents/src/mocha/hooks.test.ts | 5 +- .../src/mocha/human-in-the-loop.test.ts | 5 +- strands-agents/src/mocha/mcp.test.ts | 7 +- .../src/mocha/structured-output.test.ts | 2 +- strands-agents/src/mocha/tools.test.ts | 2 +- strands-agents/src/workflows.ts | 6 +- strands-agents/src/workflows/hooks.ts | 2 +- 20 files changed, 764 insertions(+), 71 deletions(-) create mode 100644 strands-agents/.gitignore create mode 100644 strands-agents/.npmrc create mode 100644 strands-agents/.nvmrc create mode 100644 strands-agents/.post-create create mode 100644 strands-agents/.prettierignore create mode 100644 strands-agents/.prettierrc diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f5e797757..23a44fcb6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1795,7 +1795,7 @@ importers: version: 1.21.0 '@temporalio/interceptors-opentelemetry-v2': specifier: ^1.21.0 - version: 1.21.0(@temporalio/common@1.21.0)(@temporalio/workflow@1.21.0) + version: 1.21.0(@temporalio/common@1.21.1)(@temporalio/workflow@1.21.0) '@temporalio/worker': specifier: ^1.21.0 version: 1.21.0(@swc/helpers@0.5.15) @@ -1865,10 +1865,10 @@ importers: version: 1.21.0 '@temporalio/interceptors-opentelemetry': specifier: ^1.21.0 - version: 1.21.0(@temporalio/common@1.21.0)(@temporalio/workflow@1.21.0) + version: 1.21.0(@temporalio/common@1.21.1)(@temporalio/workflow@1.21.0) '@temporalio/lambda-worker': specifier: ^1.21.0 - version: 1.21.0(@aws-lambda-powertools/logger@2.33.0)(@opentelemetry/exporter-trace-otlp-grpc@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@swc/helpers@0.5.15)(@temporalio/interceptors-opentelemetry@1.21.0(@temporalio/common@1.21.0)(@temporalio/workflow@1.21.0))(@types/aws-lambda@8.10.161) + version: 1.21.0(@aws-lambda-powertools/logger@2.33.0)(@opentelemetry/exporter-trace-otlp-grpc@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@swc/helpers@0.5.15)(@temporalio/interceptors-opentelemetry@1.21.0(@temporalio/common@1.21.1)(@temporalio/workflow@1.21.0))(@types/aws-lambda@8.10.161) '@temporalio/worker': specifier: ^1.21.0 version: 1.21.0(@swc/helpers@0.5.15) @@ -1920,7 +1920,7 @@ importers: version: 1.21.0 '@temporalio/langsmith': specifier: ^1.21.0 - version: 1.21.0(@swc/helpers@0.5.15)(langsmith@0.7.17(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.220.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(openai@6.45.0(ws@8.18.0)(zod@4.4.3))(ws@8.18.0)) + version: 1.21.0(@swc/helpers@0.5.15)(langsmith@0.7.17(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.220.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(ws@8.18.0)) '@temporalio/worker': specifier: ^1.21.0 version: 1.21.0(@swc/helpers@0.5.15) @@ -1929,7 +1929,7 @@ importers: version: 1.21.0 langsmith: specifier: ^0.7.9 - version: 0.7.17(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.220.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(openai@6.45.0(ws@8.18.0)(zod@4.4.3))(ws@8.18.0) + version: 0.7.17(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.220.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(ws@8.18.0) nanoid: specifier: 3.x version: 3.3.12 @@ -2866,10 +2866,10 @@ importers: version: 1.29.0(zod@4.4.3) '@openai/agents-core': specifier: ^0.11.6 - version: 0.11.8(ws@8.18.0)(zod@4.4.3) + version: 0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) '@openai/agents-openai': specifier: ^0.11.6 - version: 0.11.8(ws@8.18.0)(zod@4.4.3) + version: 0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) '@opentelemetry/api': specifier: ^1.9.0 version: 1.9.0 @@ -2884,7 +2884,7 @@ importers: version: 1.21.0 '@temporalio/openai-agents': specifier: ^1.21.0 - version: 1.21.0(@openai/agents-core@0.11.8(ws@8.18.0)(zod@4.4.3))(@openai/agents-openai@0.11.8(ws@8.18.0)(zod@4.4.3))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@swc/helpers@0.5.15)(openai@6.45.0(ws@8.18.0)(zod@4.4.3)) + version: 1.21.0(@openai/agents-core@0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(@openai/agents-openai@0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@swc/helpers@0.5.15)(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3)) '@temporalio/worker': specifier: ^1.21.0 version: 1.21.0(@swc/helpers@0.5.15) @@ -2899,7 +2899,7 @@ importers: version: 0.0.2 openai: specifier: ^6.0.0 - version: 6.45.0(ws@8.18.0)(zod@4.4.3) + version: 6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) zod: specifier: ^4.0.0 version: 4.4.3 @@ -3847,6 +3847,82 @@ importers: specifier: ^5.6.3 version: 5.7.3 + strands-agents: + dependencies: + '@modelcontextprotocol/sdk': + specifier: ^1.25.2 + version: 1.29.0(zod@4.4.3) + '@strands-agents/sdk': + specifier: ^1.3.0 + version: 1.11.0(@ai-sdk/provider@3.0.14)(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@opentelemetry/api@1.9.0)(@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-node@2.9.0(@opentelemetry/api@1.9.0))(@smithy/types@4.16.1)(express@5.2.1)(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(zod@4.4.3) + '@temporalio/activity': + specifier: ^1.21.1 + version: 1.21.1 + '@temporalio/client': + specifier: ^1.21.1 + version: 1.21.1 + '@temporalio/envconfig': + specifier: ^1.21.1 + version: 1.21.1 + '@temporalio/strands-agents': + specifier: ^1.21.1 + version: 1.21.1(@strands-agents/sdk@1.11.0(@ai-sdk/provider@3.0.14)(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@opentelemetry/api@1.9.0)(@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-node@2.9.0(@opentelemetry/api@1.9.0))(@smithy/types@4.16.1)(express@5.2.1)(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(zod@4.4.3)) + '@temporalio/worker': + specifier: ^1.21.1 + version: 1.21.1(@swc/helpers@0.5.15) + '@temporalio/workflow': + specifier: ^1.21.1 + version: 1.21.1 + '@temporalio/workflow-streams': + specifier: ^1.21.1 + version: 1.21.1 + zod: + specifier: ^4.1.12 + version: 4.4.3 + devDependencies: + '@temporalio/testing': + specifier: ^1.21.1 + version: 1.21.1(@swc/helpers@0.5.15) + '@tsconfig/node22': + specifier: ^22.0.0 + version: 22.0.5 + '@types/mocha': + specifier: 8.x + version: 8.2.3 + '@types/node': + specifier: ^22.9.1 + version: 22.12.0 + '@typescript-eslint/eslint-plugin': + specifier: ^8.18.0 + version: 8.22.0(@typescript-eslint/parser@8.22.0(eslint@8.57.1)(typescript@5.7.3))(eslint@8.57.1)(typescript@5.7.3) + '@typescript-eslint/parser': + specifier: ^8.18.0 + version: 8.22.0(eslint@8.57.1)(typescript@5.7.3) + eslint: + specifier: ^8.57.1 + version: 8.57.1 + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@8.57.1) + eslint-plugin-deprecation: + specifier: ^3.0.0 + version: 3.0.0(eslint@8.57.1)(typescript@5.7.3) + mocha: + specifier: 8.x + version: 8.4.0(ts-node@10.9.2(@swc/core@1.10.11(@swc/helpers@0.5.15))(@types/node@22.12.0)(typescript@5.7.3)) + nodemon: + specifier: ^3.1.7 + version: 3.1.9 + prettier: + specifier: ^3.4.2 + version: 3.4.2 + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@swc/core@1.10.11(@swc/helpers@0.5.15))(@types/node@22.12.0)(typescript@5.7.3) + typescript: + specifier: ^5.6.3 + version: 5.7.3 + timer-examples: dependencies: '@temporalio/activity': @@ -4301,10 +4377,90 @@ packages: '@middy/core': optional: true + '@aws-sdk/client-bedrock-runtime@3.1094.0': + resolution: {integrity: sha512-zvSgV2cG95ig6M2jGh21jZlCwoy/pVXRzwjhuAlHTJgP/a5BQ7OKZ/TymiosHxFdEZ0p+Qou08sj8P6IAy7P1Q==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/core@3.976.0': + resolution: {integrity: sha512-0cjRaEdlVoOrsNb9pP5q1Syyc8pXw5xSj2Np2ryReRTr9FppIIRVSdZK4lbnfmc2Hvgux/xBOUU6baB7z8//uA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-env@3.972.60': + resolution: {integrity: sha512-BAkxdoe7tpDDqCghGpuOeHQRbm/2znVvOQm0AvpQbA2tbfMN46doN4zx65fv85ImP3KADwc2zQPmbrlI9MPfMg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-http@3.972.62': + resolution: {integrity: sha512-g/0fGqKTb9xpKdd9AtpmV5Eo3DFKbnkpA2+w0peISSlu7NfAoWOuYBFxsu+yWBtxU89ka55ezoZBCbFaS8pjYQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-ini@3.973.5': + resolution: {integrity: sha512-ylubazcRfq2TVus/qXucSXeC42Qdjp5HQxTu68K/BsdMiZlcSLD1zkpoCgApXZX1Y6YJhtGGs7ZHhO/GuIgBlw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-login@3.972.67': + resolution: {integrity: sha512-CCygIKJ9YbI3n84OClSaSppkgKKHVj2TGT33c6FRORZrYNZQ1POmD+ip0FLYokiJAK7sSdc3YVkOsBm90oxWMQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-node@3.972.71': + resolution: {integrity: sha512-HIg7Q2osBzajQwL+1Vkyh2E7Gim3eTNb9RHIsOxDGjW0eZg4oEKtRs5sioCnc73ilhaOm4gX2lHVF8J7+nt2rg==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-process@3.972.60': + resolution: {integrity: sha512-YIo3f99hM43QdYG8hDzwGemnR/pU95b0kramqSJUTleCqaB7+HwKf7YZFHqvOgTqZTPx/mRmNIqoDRr3U0Z3Tw==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-sso@3.973.4': + resolution: {integrity: sha512-BPdmL8sSBOCv4ngZ+3LHxyc3CNqDCEK37CHioCk7zGrTMY5sUtkH8q+o6qA80nn6w3/fyBPGNE7OIRlmoOxRQA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.972.66': + resolution: {integrity: sha512-kSAziJboOmZmsR9/MTbiNjowl2BPes1bQuJpne4qAZ62ubi8fjfr/aupJSQje6udBoYxXTQbsL0e0kby2la3ng==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/eventstream-handler-node@3.972.29': + resolution: {integrity: sha512-t3tKQRTVXsI2QNPE3CaNjHl0wRO9Xi3acZkAyti2RQsiFmZ9Gi0kArX2ighlRJ1BtDVuul413gThAgzyTfgmWA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-eventstream@3.972.24': + resolution: {integrity: sha512-oykin4mDWxNOuYQ7SF1cHzgYeuFEkF4cdRwgvjFFbIklkx09qIFBiOgsORafG9sXZFO3TayMmQuAQYgADXhI8w==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/middleware-websocket@3.972.42': + resolution: {integrity: sha512-dw+GP8DC7QC2C8tUoK7DI8BnrNAjz8tb+uBHSrD2qJvxkCf58kTtFr98pljSrk+umU4n4HDW4eU2k7C2dWMzsg==} + engines: {node: '>= 14.0.0'} + + '@aws-sdk/nested-clients@3.997.34': + resolution: {integrity: sha512-Y9REVrSwmLM+Qy6sZJ7ofMC2S3Hr3tPP/4CzL5U1olPP7OGoF+6+Px0E49cVQBtSxJtyeLJMf0UaBErfeSahAA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/signature-v4-multi-region@3.996.41': + resolution: {integrity: sha512-QMUytg+FQMGouc8gHS00KoYih3+N6cqmVI/pQGOIo7Nr7OpQaiXjSYOuL+vsPZ1tymY4LAQ8MYcHJmws5LRxng==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1092.0': + resolution: {integrity: sha512-hBYUAr6iBLNFcsiWTgtBb0stdSw39VOUq4Sp4A5caCNf66BAZplWN4FleKrVpJx5li2YgdnK2DqoFSMWC642FQ==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/token-providers@3.1094.0': + resolution: {integrity: sha512-eroYjVPX8/20Ah7r0FqADfgBTQyujHUxpOBYsbHM/DYuqMidtO9reM3xN9qXk51oO0TuowEci/SpBbpzGtwV1A==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/types@3.974.2': + resolution: {integrity: sha512-3W6IUtSxFbH6X7Wb7DzGCV5QiFQsd0g8bOfntpmDxQlzBoKWUMBu/JPQR0DwkE+Hpnxd6db1tXbOwdeHddG6cA==} + engines: {node: '>=20.0.0'} + + '@aws-sdk/xml-builder@3.972.36': + resolution: {integrity: sha512-RdGmS1GLrtaTOLE1ElSluMldNrpk9Emq6uYs8SS8iHlu5xTAmM9rRkM91o48+rIRryBtyO9t+uLYCoMG6jVMVA==} + engines: {node: '>=20.0.0'} + '@aws/lambda-invoke-store@0.2.4': resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} engines: {node: '>=18.0.0'} + '@aws/lambda-invoke-store@0.3.0': + resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -7801,9 +7957,99 @@ packages: Deprecated: no longer maintained and no longer used by Sinon packages. See https://github.com/sinonjs/nise/issues/243 for replacement details. + '@smithy/core@3.29.8': + resolution: {integrity: sha512-rpCbCV+TimOBi3VLNBMmtTvgfOWcFIEAru3+TFlG87SL2F+te4jOnnNR+cf3uR4eJ5Qf4LnT80fqnBKgPRS6zA==} + engines: {node: '>=18.0.0'} + + '@smithy/credential-provider-imds@4.4.13': + resolution: {integrity: sha512-X+2HNZhWi5i3rJsCas0LPf6fTQUaKyJ40zd8aTO/bwpRfpU3biYaqLr7C1WMibL7PVKJalpi1PyybjGPNoHC8Q==} + engines: {node: '>=18.0.0'} + + '@smithy/fetch-http-handler@5.6.10': + resolution: {integrity: sha512-5/Yj9mS2JjTsB3B8ZX7euh77mrY9aXW23ag1yAmFykSRmA6vldqBrgqmSeQ50EjY+5SB8+aE4w14B6LKbBVEhQ==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.9.10': + resolution: {integrity: sha512-ETQz9v/Z+nTQc6fRWTXxUpxJqwpmzB3Tn3WKAdHwWkeT+m+HE5czs6GNG8vW+4vyxXSls65RVcvOZwk7Q/PS/Q==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.6.9': + resolution: {integrity: sha512-g5rnEii/mkT0mjVJmlsaOfyNBtHNTecD9Lo4NP8D5HzMUEnZNpz7/FbvBCjNcV4vteHFAxOGiLUYNxPkDZZAPw==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.16.1': + resolution: {integrity: sha512-0JFs3V2y2M9tKW5na/qxe69Zv+uxLMO7QBbhxF/FHu/Gp2NFZAAL9tWl9PU02xxo07pb3G9FTyjNc6D5uZrJIg==} + engines: {node: '>=18.0.0'} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@strands-agents/sdk@1.11.0': + resolution: {integrity: sha512-k2a9UtDd8vcmpaSpSHDbhSEe3zt5DnjzI83OGV5sFlStH5XtOMwmAc7EoiidhCxKUYOoIUUJQjLKsUFRJ3ac7w==} + engines: {node: '>=20.0.0'} + peerDependencies: + '@a2a-js/sdk': ^0.3.10 + '@ai-sdk/provider': ^3.0.0 + '@anthropic-ai/sdk': ^0.109.1 + '@aws-sdk/client-bedrock-agent': ^3.1078.0 + '@aws-sdk/client-bedrock-agent-runtime': ^3.1078.0 + '@aws-sdk/client-s3': ^3.1078.0 + '@aws/bedrock-token-generator': ^1.1.0 + '@cedar-policy/cedar-wasm': ^4.0.0 + '@cedar-policy/mcp-schema-generator-wasm': ^0.6.0 + '@google/genai': ^2.6.0 + '@modelcontextprotocol/sdk': ^1.25.2 + '@opentelemetry/api': ^1.9.0 + '@opentelemetry/exporter-metrics-otlp-http': ^0.219.0 + '@opentelemetry/exporter-trace-otlp-http': ^0.219.0 + '@opentelemetry/resources': ^2.8.0 + '@opentelemetry/sdk-metrics': ^2.6.1 + '@opentelemetry/sdk-trace-base': ^2.6.1 + '@opentelemetry/sdk-trace-node': ^2.6.1 + '@smithy/types': ^4.15.1 + express: ^5.1.0 + openai: ^6.45.0 + zod: ^4.1.12 + peerDependenciesMeta: + '@a2a-js/sdk': + optional: true + '@ai-sdk/provider': + optional: true + '@anthropic-ai/sdk': + optional: true + '@aws-sdk/client-bedrock-agent': + optional: true + '@aws-sdk/client-bedrock-agent-runtime': + optional: true + '@aws-sdk/client-s3': + optional: true + '@aws/bedrock-token-generator': + optional: true + '@cedar-policy/cedar-wasm': + optional: true + '@cedar-policy/mcp-schema-generator-wasm': + optional: true + '@google/genai': + optional: true + '@opentelemetry/exporter-metrics-otlp-http': + optional: true + '@opentelemetry/exporter-trace-otlp-http': + optional: true + '@opentelemetry/resources': + optional: true + '@opentelemetry/sdk-metrics': + optional: true + '@opentelemetry/sdk-trace-base': + optional: true + '@opentelemetry/sdk-trace-node': + optional: true + '@smithy/types': + optional: true + express: + optional: true + openai: + optional: true + '@surma/rollup-plugin-off-main-thread@2.2.3': resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} @@ -7987,6 +8233,10 @@ packages: resolution: {integrity: sha512-v9Tf3CNi0Iy5VylB5GrzWz9KpuhrWDhdDuyfDHO1BX71Exwq1aXRnak0WQY0umjNI/oK+mPHKQ8/RAmmAFOL/w==} engines: {node: '>= 20.3.0'} + '@temporalio/activity@1.21.1': + resolution: {integrity: sha512-UjA1d4ugL3pRXElwnggtVAMe3lppUPzYpBPmpDLTXfOtxEXPTK1x+8OC2jrZY7qmvpHkOdoo86S+UYFzJkMk/A==} + engines: {node: '>= 20.3.0'} + '@temporalio/ai-sdk@1.20.3': resolution: {integrity: sha512-j3XUiu/3ckePcd9gBzDcwuN/phyvtAQot2RFNeKoLgAeWGgDhhfbrT9q2sg1i8VgTd4z4uprQpRJacFgGW88bA==} engines: {node: '>= 20.3.0'} @@ -8003,6 +8253,10 @@ packages: resolution: {integrity: sha512-ujkdvIOswOi4AbISWbH5/w4vCOhgb+0oj/o/4AImArBLyxVqlqhW+AIrY+7zxOkFpF2CN9dlDJBzDdCGbBeHYA==} engines: {node: '>= 20.3.0'} + '@temporalio/client@1.21.1': + resolution: {integrity: sha512-rdZAh20wzI5i/SyS46Nv9mE6t2KkS9DTrB57HEMLsq6eqm58Nc6la0WQKJGsUNkmN98KKirByidZ5D/ik3kiQw==} + engines: {node: '>= 20.3.0'} + '@temporalio/common@1.20.3': resolution: {integrity: sha512-VM9GyUAa7tl413nM24mcBhUfUeqcGazmDkx/GfDA0c+KuEVusclMg9eqa63EDv/rguC/YhWaXu6q5Kl8+99GSw==} engines: {node: '>= 20.3.0'} @@ -8011,6 +8265,10 @@ packages: resolution: {integrity: sha512-cGPkW47dRo2MPT5BzTUSuRUrtxNdYUaUSlvwBA2fX45egrlza2oBvsev52c7l0h0tReflDuAAEoh1JGS2qot6Q==} engines: {node: '>= 20.3.0'} + '@temporalio/common@1.21.1': + resolution: {integrity: sha512-8Pis59xYLrGu6GfkkWvrYWkSPEvo8lBBILsR6gBHGbymNlEc0ynylRXqPmRjwuLfYS7jHzxqjjO7cvXJQ/z2Fg==} + engines: {node: '>= 20.3.0'} + '@temporalio/core-bridge@1.20.3': resolution: {integrity: sha512-i7hu6lg6bm2esyYYDwHh0XmGkOWSL5iicLjC7DNRU4b4As/dKcRxYMxNY6dXtKzbM94xhmX6Fh4j5/fxen/4kw==} engines: {node: '>= 20.3.0'} @@ -8019,6 +8277,10 @@ packages: resolution: {integrity: sha512-EBSLy4fip2e5jPMznGi32bcYZK3MztNvMfDY3Z96gJYk6N1R5EkR+lINoxLv1wkLVUoFBj+I+xQ1Xi8FF4iZbw==} engines: {node: '>= 20.3.0'} + '@temporalio/core-bridge@1.21.1': + resolution: {integrity: sha512-gCy/6TFhcFAjFPRN1DeHSwAnXU380Jn/y6yG2dkSV+rYK0JRl66SE2NvdcAjzhoPO/twHEaHklbppojH0cUpUw==} + engines: {node: '>= 20.3.0'} + '@temporalio/envconfig@1.20.3': resolution: {integrity: sha512-5CgTXb/UsvmwZc8lW5aLkplEpCRGwJkV+bWNcxblFjfayit0VYCxn+S7iMbW01Hj7dQWJ+RX+Xg+E+iFHf07JA==} engines: {node: '>= 20.3.0'} @@ -8027,6 +8289,10 @@ packages: resolution: {integrity: sha512-d2MWXGvgsqXUP+Cw6oVyF0Yz68XuR4yns5fuCZgu5BUwlKGdjQ56f+AX2cSyl0/P6yugG0sLJ6iQe9UbQCBdeQ==} engines: {node: '>= 20.3.0'} + '@temporalio/envconfig@1.21.1': + resolution: {integrity: sha512-VwhGteeXTIMMsWlYF6zxYcxZWWuWvPIfCmcTJSk86iGKF2O0umhPPR+DE4Uc7Y6mG+wJ7GJVe7YmY2KZu2xOCg==} + engines: {node: '>= 20.3.0'} + '@temporalio/interceptors-opentelemetry-v2@1.21.0': resolution: {integrity: sha512-E+uSPMXr62emNJihqAhwus2tLtI/XV93Zta40JCXDzRVLbtVAwCuTfVH9J9plW00tWSxRy92qKOTX7/4KMriBg==} engines: {node: '>= 20.0.0'} @@ -8085,6 +8351,10 @@ packages: resolution: {integrity: sha512-xuOmjJnBwcLf7fXOGWJUyOvGRqF0JTVlSgJPOOQpgWrGUSXYRvPKSW0p3tUE1rzfssBDYZp9905uK2OJE+3LpA==} engines: {node: '>= 20.3.0'} + '@temporalio/nexus@1.21.1': + resolution: {integrity: sha512-CIAoTt/WpSE0bn1mE9q5O6hU76q97W349e0FSrUuwQAYDXBy7jzFM6ZzYmm7S5Uk2tC1xrRCAjUGg1lMnHQppw==} + engines: {node: '>= 20.3.0'} + '@temporalio/nyc-test-coverage@1.21.0': resolution: {integrity: sha512-RReHRMe5CnWENZAwJcPDu8wYeoFVgOI7IrbWqgFgSEwJR07kc1YZnaLggswqGErnCCed/8AAz7Coex7mEIjugQ==} engines: {node: '>= 20.3.0'} @@ -8114,6 +8384,10 @@ packages: resolution: {integrity: sha512-NmKGG27iYQzR0YaU6EWUzBCbiWS+KhmOF1qBOkwdVysLQIT7w2QjFLAmBw80wrrWxb/1l7xlLYHlNYSWJ2OqIg==} engines: {node: '>= 20.3.0'} + '@temporalio/plugin@1.21.1': + resolution: {integrity: sha512-OabF/4y0SXn8ifakRGbAAqBUuIPl4dj1peqEbxgOvRQO9Ak6ZWmUpPvR72oh1QUTXYn38svudsL8MeP8+6DHFg==} + engines: {node: '>= 20.3.0'} + '@temporalio/proto@1.20.3': resolution: {integrity: sha512-RdbHC3zH+N0QqG1Q38h2LilFIQ3lbGLVmLYCyNcV+PPfzrtrZ8pwBACpSGwEuF7WWFziLAhJ2oMVXTnmXY5ocQ==} engines: {node: '>= 20.3.0'} @@ -8122,6 +8396,16 @@ packages: resolution: {integrity: sha512-i2rNFdMZOWPDiO9YTl3knksR80IX36DpklJftpJndlAFfPAXv/iHf2UXxiGyr4N4beF8TNyNWOb30Vjk663ROg==} engines: {node: '>= 20.3.0'} + '@temporalio/proto@1.21.1': + resolution: {integrity: sha512-eSHGrZ6CxbtjrAzxiMgKrWeDiBlWk6/JkIqsB1hrkPB6TQXC67Az8v0BL0Fj3ur8ktQZbaacON/xCDjTsvJyGw==} + engines: {node: '>= 20.3.0'} + + '@temporalio/strands-agents@1.21.1': + resolution: {integrity: sha512-4ZLPLf9mj2x232RKHEo3bez9RjJZRRvfvQkX7xp8EmpDKN7EhlJmZk/RBz+eivBN1PIDeYF3EVYZkBEhp0+ccw==} + engines: {node: '>= 20.0.0'} + peerDependencies: + '@strands-agents/sdk': ^1.3.0 + '@temporalio/testing@1.20.3': resolution: {integrity: sha512-YptZtr/HPKov+9GxOEY9Onpuzy/WppBVOdqWaNbZasD5x3vo3aVOyFA1eVcGXSLnKMmQC+JGr+LLLJP/9Dn36A==} engines: {node: '>= 20.3.0'} @@ -8130,6 +8414,10 @@ packages: resolution: {integrity: sha512-YjCT3v2CUTpGZkY5/epX4QL8KGg2ZR0pTKV/SttGQ8PiBr4HofY6zJxY0bRVxo7sRDboOogOYjbEV+ElGVJHpQ==} engines: {node: '>= 20.3.0'} + '@temporalio/testing@1.21.1': + resolution: {integrity: sha512-vKYQZsa8EFE4Rvlh0v1Ry2B0+vnnpgfHarSadue+3ZUWf3cx28HGCseZEgHcaG299ChsjbADntKO6YjwlMv1Jw==} + engines: {node: '>= 20.3.0'} + '@temporalio/worker@1.20.3': resolution: {integrity: sha512-29W39KxGoz8QfiELT5al1jr+nnZEnpWCjr5gYT2a6ZlXr2WKxbJw4rRBy+OyGwHs5kGT0pPYayYvwcDTvWRpQQ==} engines: {node: '>= 20.3.0'} @@ -8138,6 +8426,10 @@ packages: resolution: {integrity: sha512-8StC83emDAlTg48PyPuqmNhdNRimf2OfMcJbQbMypq7jJg6QULmbLlbXmaZlDc/8qGxyYwOc6b2VwzQhLNGpZw==} engines: {node: '>= 20.3.0'} + '@temporalio/worker@1.21.1': + resolution: {integrity: sha512-ccXus6+w317tL+NsJXEYWpHFxcy0VDPfstmBzej0yZrkzWNvAkaWVGQbguKoLToDM8+DMaqklx1dX4gJnknR1g==} + engines: {node: '>= 20.3.0'} + '@temporalio/workflow-streams@1.20.3': resolution: {integrity: sha512-YIoQTFDhA4yroUOxjj3EDjOk/QHCLXNyz3TGrSFbbVobEykyjXWhkYlLyu1SkVu9bee5Nz+lH4T34B4vvEMzgg==} engines: {node: '>= 20.3.0'} @@ -8146,6 +8438,10 @@ packages: resolution: {integrity: sha512-ymMFOo6MHfx3HAPs1nAENB4T1EiifBR+73jxRJXpz0cst4SoA8QcmxoGK/eSuA7qC5FmtmeoKX9rmagcUNRUDA==} engines: {node: '>= 20.3.0'} + '@temporalio/workflow-streams@1.21.1': + resolution: {integrity: sha512-x4reh2EaiH6ageejhlFslWOAQ5cODYhWWD44XjMCSKj7xelrVgyjAzWmSvbmLOK/z71HhzGYIPCd6W4bY0Nlww==} + engines: {node: '>= 20.3.0'} + '@temporalio/workflow@1.20.3': resolution: {integrity: sha512-6LRfnWp3HcvqvKFwbOhs5iDQkHNIM5Rxtxhsi2E5ViPDvJMBqlQ0sQ4oRo5SEgr5Bri650i7GfEQQJMVl7xoKg==} engines: {node: '>= 20.3.0'} @@ -8154,6 +8450,10 @@ packages: resolution: {integrity: sha512-TlVrRTkcqbC/LFaWsJDr/43wupXhex3LZnxX/deba6badmRMhnQxzKdAXC2/NDOUrs7k6jG7j3QwOMGvW2rBYw==} engines: {node: '>= 20.3.0'} + '@temporalio/workflow@1.21.1': + resolution: {integrity: sha512-Tsoe9RnB0mL75DGVo3wJJrgTl+QnHYUysPjqRkzQdzgeKravN8RxE0HCfS3cYRZej3eEVwoDftgbo7/KR0NXWQ==} + engines: {node: '>= 20.3.0'} + '@testing-library/dom@7.31.2': resolution: {integrity: sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ==} engines: {node: '>=10'} @@ -9390,6 +9690,9 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -16256,6 +16559,10 @@ packages: resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} hasBin: true + uuid@14.0.1: + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} + hasBin: true + uuid@8.3.2: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true @@ -16902,8 +17209,190 @@ snapshots: '@aws-lambda-powertools/commons': 2.33.0 '@aws/lambda-invoke-store': 0.2.4 + '@aws-sdk/client-bedrock-runtime@3.1094.0': + dependencies: + '@aws-sdk/core': 3.976.0 + '@aws-sdk/credential-provider-node': 3.972.71 + '@aws-sdk/eventstream-handler-node': 3.972.29 + '@aws-sdk/middleware-eventstream': 3.972.24 + '@aws-sdk/middleware-websocket': 3.972.42 + '@aws-sdk/token-providers': 3.1094.0 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/fetch-http-handler': 5.6.10 + '@smithy/node-http-handler': 4.9.10 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/core@3.976.0': + dependencies: + '@aws-sdk/types': 3.974.2 + '@aws-sdk/xml-builder': 3.972.36 + '@aws/lambda-invoke-store': 0.3.0 + '@smithy/core': 3.29.8 + '@smithy/signature-v4': 5.6.9 + '@smithy/types': 4.16.1 + bowser: 2.14.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.60': + dependencies: + '@aws-sdk/core': 3.976.0 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.62': + dependencies: + '@aws-sdk/core': 3.976.0 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/fetch-http-handler': 5.6.10 + '@smithy/node-http-handler': 4.9.10 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.973.5': + dependencies: + '@aws-sdk/core': 3.976.0 + '@aws-sdk/credential-provider-env': 3.972.60 + '@aws-sdk/credential-provider-http': 3.972.62 + '@aws-sdk/credential-provider-login': 3.972.67 + '@aws-sdk/credential-provider-process': 3.972.60 + '@aws-sdk/credential-provider-sso': 3.973.4 + '@aws-sdk/credential-provider-web-identity': 3.972.66 + '@aws-sdk/nested-clients': 3.997.34 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/credential-provider-imds': 4.4.13 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-login@3.972.67': + dependencies: + '@aws-sdk/core': 3.976.0 + '@aws-sdk/nested-clients': 3.997.34 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-node@3.972.71': + dependencies: + '@aws-sdk/credential-provider-env': 3.972.60 + '@aws-sdk/credential-provider-http': 3.972.62 + '@aws-sdk/credential-provider-ini': 3.973.5 + '@aws-sdk/credential-provider-process': 3.972.60 + '@aws-sdk/credential-provider-sso': 3.973.4 + '@aws-sdk/credential-provider-web-identity': 3.972.66 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/credential-provider-imds': 4.4.13 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-process@3.972.60': + dependencies: + '@aws-sdk/core': 3.976.0 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.973.4': + dependencies: + '@aws-sdk/core': 3.976.0 + '@aws-sdk/nested-clients': 3.997.34 + '@aws-sdk/token-providers': 3.1092.0 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-web-identity@3.972.66': + dependencies: + '@aws-sdk/core': 3.976.0 + '@aws-sdk/nested-clients': 3.997.34 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/eventstream-handler-node@3.972.29': + dependencies: + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-eventstream@3.972.24': + dependencies: + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/middleware-websocket@3.972.42': + dependencies: + '@aws-sdk/core': 3.976.0 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/fetch-http-handler': 5.6.10 + '@smithy/signature-v4': 5.6.9 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.997.34': + dependencies: + '@aws-sdk/core': 3.976.0 + '@aws-sdk/signature-v4-multi-region': 3.996.41 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/fetch-http-handler': 5.6.10 + '@smithy/node-http-handler': 4.9.10 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/signature-v4-multi-region@3.996.41': + dependencies: + '@aws-sdk/types': 3.974.2 + '@smithy/signature-v4': 5.6.9 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1092.0': + dependencies: + '@aws-sdk/core': 3.976.0 + '@aws-sdk/nested-clients': 3.997.34 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.1094.0': + dependencies: + '@aws-sdk/core': 3.976.0 + '@aws-sdk/nested-clients': 3.997.34 + '@aws-sdk/types': 3.974.2 + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/types@3.974.2': + dependencies: + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@aws-sdk/xml-builder@3.972.36': + dependencies: + '@smithy/types': 4.16.1 + tslib: 2.8.1 + '@aws/lambda-invoke-store@0.2.4': {} + '@aws/lambda-invoke-store@0.3.0': {} + '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 @@ -20021,10 +20510,10 @@ snapshots: transitivePeerDependencies: - encoding - '@openai/agents-core@0.11.8(ws@8.18.0)(zod@4.4.3)': + '@openai/agents-core@0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3)': dependencies: debug: 4.4.3(supports-color@5.5.0) - openai: 6.45.0(ws@8.18.0)(zod@4.4.3) + openai: 6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) optionalDependencies: '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) zod: 4.4.3 @@ -20036,11 +20525,11 @@ snapshots: - supports-color - ws - '@openai/agents-openai@0.11.8(ws@8.18.0)(zod@4.4.3)': + '@openai/agents-openai@0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3)': dependencies: - '@openai/agents-core': 0.11.8(ws@8.18.0)(zod@4.4.3) + '@openai/agents-core': 0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) debug: 4.4.3(supports-color@5.5.0) - openai: 6.45.0(ws@8.18.0)(zod@4.4.3) + openai: 6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) zod: 4.4.3 transitivePeerDependencies: - '@aws-sdk/credential-provider-node' @@ -21068,8 +21557,60 @@ snapshots: '@sinonjs/text-encoding@0.7.3': {} + '@smithy/core@3.29.8': + dependencies: + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@smithy/credential-provider-imds@4.4.13': + dependencies: + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.6.10': + dependencies: + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.9.10': + dependencies: + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@smithy/signature-v4@5.6.9': + dependencies: + '@smithy/core': 3.29.8 + '@smithy/types': 4.16.1 + tslib: 2.8.1 + + '@smithy/types@4.16.1': + dependencies: + tslib: 2.8.1 + '@standard-schema/spec@1.1.0': {} + '@strands-agents/sdk@1.11.0(@ai-sdk/provider@3.0.14)(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@opentelemetry/api@1.9.0)(@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-node@2.9.0(@opentelemetry/api@1.9.0))(@smithy/types@4.16.1)(express@5.2.1)(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(zod@4.4.3)': + dependencies: + '@aws-sdk/client-bedrock-runtime': 3.1094.0 + '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) + '@opentelemetry/api': 1.9.0 + '@types/json-schema': 7.0.15 + uuid: 14.0.1 + yaml: 2.9.0 + zod: 4.4.3 + optionalDependencies: + '@ai-sdk/provider': 3.0.14 + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.9.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': 2.9.0(@opentelemetry/api@1.9.0) + '@smithy/types': 4.16.1 + express: 5.2.1 + openai: 6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) + '@surma/rollup-plugin-off-main-thread@2.2.3': dependencies: ejs: 3.1.10 @@ -21242,6 +21783,11 @@ snapshots: '@temporalio/client': 1.21.0 '@temporalio/common': 1.21.0 + '@temporalio/activity@1.21.1': + dependencies: + '@temporalio/client': 1.21.1 + '@temporalio/common': 1.21.1 + '@temporalio/ai-sdk@1.20.3(@ai-sdk/mcp@1.0.64(zod@3.25.76))(@ai-sdk/provider@3.0.14)(ai@6.0.234(zod@3.25.76))': dependencies: '@ai-sdk/mcp': 1.0.64(zod@3.25.76) @@ -21277,6 +21823,16 @@ snapshots: nexus-rpc: 0.0.2 uuid: 11.1.0 + '@temporalio/client@1.21.1': + dependencies: + '@grpc/grpc-js': 1.14.4 + '@temporalio/common': 1.21.1 + '@temporalio/proto': 1.21.1 + abort-controller: 3.0.0 + long: 5.3.2 + nexus-rpc: 0.0.2 + uuid: 11.1.0 + '@temporalio/common@1.20.3': dependencies: '@temporalio/proto': 1.20.3 @@ -21293,6 +21849,14 @@ snapshots: nexus-rpc: 0.0.2 proto3-json-serializer: 2.0.2 + '@temporalio/common@1.21.1': + dependencies: + '@temporalio/proto': 1.21.1 + long: 5.3.2 + ms: 3.0.0-canary.1 + nexus-rpc: 0.0.2 + proto3-json-serializer: 2.0.2 + '@temporalio/core-bridge@1.20.3': dependencies: '@grpc/grpc-js': 1.14.4 @@ -21303,6 +21867,11 @@ snapshots: '@grpc/grpc-js': 1.14.4 '@temporalio/common': 1.21.0 + '@temporalio/core-bridge@1.21.1': + dependencies: + '@grpc/grpc-js': 1.14.4 + '@temporalio/common': 1.21.1 + '@temporalio/envconfig@1.20.3': dependencies: '@temporalio/common': 1.20.3 @@ -21313,29 +21882,34 @@ snapshots: '@temporalio/common': 1.21.0 smol-toml: 1.6.1 - '@temporalio/interceptors-opentelemetry-v2@1.21.0(@temporalio/common@1.21.0)(@temporalio/workflow@1.21.0)': + '@temporalio/envconfig@1.21.1': + dependencies: + '@temporalio/common': 1.21.1 + smol-toml: 1.6.1 + + '@temporalio/interceptors-opentelemetry-v2@1.21.0(@temporalio/common@1.21.1)(@temporalio/workflow@1.21.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.0) - '@temporalio/common': 1.21.0 + '@temporalio/common': 1.21.1 '@temporalio/plugin': 1.21.0 optionalDependencies: '@temporalio/workflow': 1.21.0 - '@temporalio/interceptors-opentelemetry@1.21.0(@temporalio/common@1.21.0)(@temporalio/workflow@1.21.0)': + '@temporalio/interceptors-opentelemetry@1.21.0(@temporalio/common@1.21.1)(@temporalio/workflow@1.21.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) - '@temporalio/common': 1.21.0 + '@temporalio/common': 1.21.1 '@temporalio/plugin': 1.21.0 optionalDependencies: '@temporalio/workflow': 1.21.0 - '@temporalio/lambda-worker@1.21.0(@aws-lambda-powertools/logger@2.33.0)(@opentelemetry/exporter-trace-otlp-grpc@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@swc/helpers@0.5.15)(@temporalio/interceptors-opentelemetry@1.21.0(@temporalio/common@1.21.0)(@temporalio/workflow@1.21.0))(@types/aws-lambda@8.10.161)': + '@temporalio/lambda-worker@1.21.0(@aws-lambda-powertools/logger@2.33.0)(@opentelemetry/exporter-trace-otlp-grpc@0.52.1(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@swc/helpers@0.5.15)(@temporalio/interceptors-opentelemetry@1.21.0(@temporalio/common@1.21.1)(@temporalio/workflow@1.21.0))(@types/aws-lambda@8.10.161)': dependencies: '@temporalio/common': 1.21.0 '@temporalio/envconfig': 1.21.0 @@ -21345,7 +21919,7 @@ snapshots: '@opentelemetry/exporter-trace-otlp-grpc': 0.52.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) - '@temporalio/interceptors-opentelemetry': 1.21.0(@temporalio/common@1.21.0)(@temporalio/workflow@1.21.0) + '@temporalio/interceptors-opentelemetry': 1.21.0(@temporalio/common@1.21.1)(@temporalio/workflow@1.21.0) '@types/aws-lambda': 8.10.161 transitivePeerDependencies: - '@minify-html/node' @@ -21362,7 +21936,7 @@ snapshots: - uglify-js - webpack-cli - '@temporalio/langsmith@1.21.0(@swc/helpers@0.5.15)(langsmith@0.7.17(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.220.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(openai@6.45.0(ws@8.18.0)(zod@4.4.3))(ws@8.18.0))': + '@temporalio/langsmith@1.21.0(@swc/helpers@0.5.15)(langsmith@0.7.17(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.220.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(ws@8.18.0))': dependencies: '@temporalio/activity': 1.21.0 '@temporalio/client': 1.21.0 @@ -21370,7 +21944,7 @@ snapshots: '@temporalio/plugin': 1.21.0 '@temporalio/worker': 1.21.0(@swc/helpers@0.5.15) '@temporalio/workflow': 1.21.0 - langsmith: 0.7.17(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.220.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(openai@6.45.0(ws@8.18.0)(zod@4.4.3))(ws@8.18.0) + langsmith: 0.7.17(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.220.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(ws@8.18.0) transitivePeerDependencies: - '@minify-html/node' - '@swc/css' @@ -21402,6 +21976,14 @@ snapshots: long: 5.3.2 nexus-rpc: 0.0.2 + '@temporalio/nexus@1.21.1': + dependencies: + '@temporalio/client': 1.21.1 + '@temporalio/common': 1.21.1 + '@temporalio/proto': 1.21.1 + long: 5.3.2 + nexus-rpc: 0.0.2 + '@temporalio/nyc-test-coverage@1.21.0(@temporalio/common@1.21.0)(@temporalio/worker@1.21.0(@swc/helpers@0.5.15))(@temporalio/workflow@1.21.0)(typescript@5.7.3)(webpack@5.108.3(@swc/core@1.10.11(@swc/helpers@0.5.15)))': dependencies: '@temporalio/common': 1.21.0 @@ -21416,10 +21998,10 @@ snapshots: - supports-color - typescript - '@temporalio/openai-agents@1.21.0(@openai/agents-core@0.11.8(ws@8.18.0)(zod@4.4.3))(@openai/agents-openai@0.11.8(ws@8.18.0)(zod@4.4.3))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@swc/helpers@0.5.15)(openai@6.45.0(ws@8.18.0)(zod@4.4.3))': + '@temporalio/openai-agents@1.21.0(@openai/agents-core@0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(@openai/agents-openai@0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@swc/helpers@0.5.15)(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))': dependencies: - '@openai/agents-core': 0.11.8(ws@8.18.0)(zod@4.4.3) - '@openai/agents-openai': 0.11.8(ws@8.18.0)(zod@4.4.3) + '@openai/agents-core': 0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) + '@openai/agents-openai': 0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) '@opentelemetry/api': 1.9.0 '@temporalio/activity': 1.21.0 '@temporalio/common': 1.21.0 @@ -21429,7 +22011,7 @@ snapshots: '@temporalio/workflow-streams': 1.21.0 '@ungap/structured-clone': 1.3.2 headers-polyfill: 4.0.3 - openai: 6.45.0(ws@8.18.0)(zod@4.4.3) + openai: 6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) web-streams-polyfill: 4.2.0 optionalDependencies: '@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0) @@ -21452,6 +22034,8 @@ snapshots: '@temporalio/plugin@1.21.0': {} + '@temporalio/plugin@1.21.1': {} + '@temporalio/proto@1.20.3': dependencies: long: 5.3.2 @@ -21462,6 +22046,23 @@ snapshots: long: 5.3.2 protobufjs: 7.6.5 + '@temporalio/proto@1.21.1': + dependencies: + long: 5.3.2 + protobufjs: 7.6.5 + + '@temporalio/strands-agents@1.21.1(@strands-agents/sdk@1.11.0(@ai-sdk/provider@3.0.14)(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@opentelemetry/api@1.9.0)(@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-node@2.9.0(@opentelemetry/api@1.9.0))(@smithy/types@4.16.1)(express@5.2.1)(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(zod@4.4.3))': + dependencies: + '@strands-agents/sdk': 1.11.0(@ai-sdk/provider@3.0.14)(@modelcontextprotocol/sdk@1.29.0(zod@4.4.3))(@opentelemetry/api@1.9.0)(@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-node@2.9.0(@opentelemetry/api@1.9.0))(@smithy/types@4.16.1)(express@5.2.1)(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(zod@4.4.3) + '@temporalio/activity': 1.21.1 + '@temporalio/common': 1.21.1 + '@temporalio/plugin': 1.21.1 + '@temporalio/workflow': 1.21.1 + '@temporalio/workflow-streams': 1.21.1 + '@ungap/structured-clone': 1.3.2 + headers-polyfill: 4.0.3 + web-streams-polyfill: 4.2.0 + '@temporalio/testing@1.20.3(@swc/helpers@0.5.15)': dependencies: '@temporalio/activity': 1.20.3 @@ -21510,6 +22111,30 @@ snapshots: - uglify-js - webpack-cli + '@temporalio/testing@1.21.1(@swc/helpers@0.5.15)': + dependencies: + '@temporalio/activity': 1.21.1 + '@temporalio/client': 1.21.1 + '@temporalio/common': 1.21.1 + '@temporalio/core-bridge': 1.21.1 + '@temporalio/proto': 1.21.1 + '@temporalio/worker': 1.21.1(@swc/helpers@0.5.15) + '@temporalio/workflow': 1.21.1 + transitivePeerDependencies: + - '@minify-html/node' + - '@swc/css' + - '@swc/helpers' + - '@swc/html' + - clean-css + - cssnano + - csso + - esbuild + - html-minifier-terser + - lightningcss + - postcss + - uglify-js + - webpack-cli + '@temporalio/worker@1.20.3(@swc/helpers@0.5.15)': dependencies: '@grpc/grpc-js': 1.14.4 @@ -21621,6 +22246,43 @@ snapshots: - uglify-js - webpack-cli + '@temporalio/worker@1.21.1(@swc/helpers@0.5.15)': + dependencies: + '@grpc/grpc-js': 1.14.4 + '@swc/core': 1.10.11(@swc/helpers@0.5.15) + '@temporalio/activity': 1.21.1 + '@temporalio/client': 1.21.1 + '@temporalio/common': 1.21.1 + '@temporalio/core-bridge': 1.21.1 + '@temporalio/nexus': 1.21.1 + '@temporalio/proto': 1.21.1 + '@temporalio/workflow': 1.21.1 + heap-js: 2.6.0 + memfs: 4.17.0 + nexus-rpc: 0.0.2 + protobufjs: 7.6.5 + rxjs: 7.8.2 + source-map: 0.7.6 + source-map-loader: 5.0.0(webpack@5.108.4(@swc/core@1.10.11(@swc/helpers@0.5.15))) + supports-color: 8.1.1 + swc-loader: 0.2.6(@swc/core@1.10.11(@swc/helpers@0.5.15))(webpack@5.108.4(@swc/core@1.10.11(@swc/helpers@0.5.15))) + unionfs: 4.5.4 + webpack: 5.108.4(@swc/core@1.10.11(@swc/helpers@0.5.15)) + transitivePeerDependencies: + - '@minify-html/node' + - '@swc/css' + - '@swc/helpers' + - '@swc/html' + - clean-css + - cssnano + - csso + - esbuild + - html-minifier-terser + - lightningcss + - postcss + - uglify-js + - webpack-cli + '@temporalio/workflow-streams@1.20.3': dependencies: '@temporalio/activity': 1.20.3 @@ -21637,6 +22299,14 @@ snapshots: '@temporalio/proto': 1.21.0 '@temporalio/workflow': 1.21.0 + '@temporalio/workflow-streams@1.21.1': + dependencies: + '@temporalio/activity': 1.21.1 + '@temporalio/client': 1.21.1 + '@temporalio/common': 1.21.1 + '@temporalio/proto': 1.21.1 + '@temporalio/workflow': 1.21.1 + '@temporalio/workflow@1.20.3': dependencies: '@temporalio/common': 1.20.3 @@ -21649,6 +22319,12 @@ snapshots: '@temporalio/proto': 1.21.0 nexus-rpc: 0.0.2 + '@temporalio/workflow@1.21.1': + dependencies: + '@temporalio/common': 1.21.1 + '@temporalio/proto': 1.21.1 + nexus-rpc: 0.0.2 + '@testing-library/dom@7.31.2': dependencies: '@babel/code-frame': 7.26.2 @@ -23430,6 +24106,8 @@ snapshots: boolbase@1.0.0: {} + bowser@2.14.1: {} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -27822,14 +28500,14 @@ snapshots: kuler@2.0.0: {} - langsmith@0.7.17(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.220.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(openai@6.45.0(ws@8.18.0)(zod@4.4.3))(ws@8.18.0): + langsmith@0.7.17(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.220.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.0))(openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3))(ws@8.18.0): dependencies: p-queue: 6.6.2 optionalDependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/exporter-trace-otlp-proto': 0.220.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.0) - openai: 6.45.0(ws@8.18.0)(zod@4.4.3) + openai: 6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) ws: 8.18.0 language-subtag-registry@0.3.23: {} @@ -29095,8 +29773,10 @@ snapshots: transitivePeerDependencies: - encoding - openai@6.45.0(ws@8.18.0)(zod@4.4.3): + openai@6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3): optionalDependencies: + '@aws-sdk/credential-provider-node': 3.972.71 + '@smithy/signature-v4': 5.6.9 ws: 8.18.0 zod: 4.4.3 @@ -32246,6 +32926,8 @@ snapshots: uuid@11.1.0: {} + uuid@14.0.1: {} + uuid@8.3.2: {} uuid@9.0.1: {} diff --git a/strands-agents/.gitignore b/strands-agents/.gitignore new file mode 100644 index 000000000..a9f4ed545 --- /dev/null +++ b/strands-agents/.gitignore @@ -0,0 +1,2 @@ +lib +node_modules \ No newline at end of file diff --git a/strands-agents/.npmrc b/strands-agents/.npmrc new file mode 100644 index 000000000..9cf949503 --- /dev/null +++ b/strands-agents/.npmrc @@ -0,0 +1 @@ +package-lock=false \ No newline at end of file diff --git a/strands-agents/.nvmrc b/strands-agents/.nvmrc new file mode 100644 index 000000000..2bd5a0a98 --- /dev/null +++ b/strands-agents/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/strands-agents/.post-create b/strands-agents/.post-create new file mode 100644 index 000000000..055c11e9e --- /dev/null +++ b/strands-agents/.post-create @@ -0,0 +1,18 @@ +To begin development, install the Temporal CLI: + +Mac: {cyan brew install temporal} +Other: Download and extract the latest release from https://github.com/temporalio/cli/releases/latest + +Start Temporal Server: + +{cyan temporal server start-dev} + +Use Node version 18+ (v22.x is recommended): + +Mac: {cyan brew install node@22} +Other: https://nodejs.org/en/download/ + +Then, in the project directory, using two other shells, run these commands: + +{cyan npm run start.watch} +{cyan npm run workflow} diff --git a/strands-agents/.prettierignore b/strands-agents/.prettierignore new file mode 100644 index 000000000..7951405f8 --- /dev/null +++ b/strands-agents/.prettierignore @@ -0,0 +1 @@ +lib \ No newline at end of file diff --git a/strands-agents/.prettierrc b/strands-agents/.prettierrc new file mode 100644 index 000000000..965d50bff --- /dev/null +++ b/strands-agents/.prettierrc @@ -0,0 +1,2 @@ +printWidth: 120 +singleQuote: true diff --git a/strands-agents/README.md b/strands-agents/README.md index 1eb28edc1..ae638e387 100644 --- a/strands-agents/README.md +++ b/strands-agents/README.md @@ -4,17 +4,17 @@ A complete set of samples for the Temporal TypeScript SDK's Strands integration, One worker registers all activities, all workflows, and an MCP server. Each feature has its own client script in `src/`: -| Feature | Client | What it demonstrates | -| --- | --- | --- | -| Hello World | `npm run workflow:hello-world` | Minimal `TemporalAgent` invocation. | -| Tools | `npm run workflow:tools` | Pure Strands `tool()` + `workflow.activityAsTool` for I/O. | -| Human in the Loop | `npm run workflow:human-in-the-loop` | Gate a tool call on human approval with a `BeforeToolCallEvent` hook + `event.interrupt()`. | -| Activity Interrupt | `npm run workflow:activity-interrupt` | Activity-thrown interrupt routed through `StrandsFailureConverter`. | -| Hooks | `npm run workflow:hooks` | `AfterToolCallEvent` with in-workflow and activity-dispatched callbacks. | -| MCP | `npm run workflow:mcp` | Connect to a stdio MCP server via `TemporalMCPClient`. | -| Structured Output | `npm run workflow:structured-output` | Constrain output with a Zod schema. | -| Streaming | `npm run workflow:streaming` | Forward model chunks to subscribers via `WorkflowStream`. | -| Continue as New | `npm run workflow:continue-as-new` | Long-lived chat with history-aware continue-as-new. | +| Feature | Client | What it demonstrates | +| ------------------ | ------------------------------------- | ------------------------------------------------------------------------------------------- | +| Hello World | `npm run workflow:hello-world` | Minimal `TemporalAgent` invocation. | +| Tools | `npm run workflow:tools` | Pure Strands `tool()` + `workflow.activityAsTool` for I/O. | +| Human in the Loop | `npm run workflow:human-in-the-loop` | Gate a tool call on human approval with a `BeforeToolCallEvent` hook + `event.interrupt()`. | +| Activity Interrupt | `npm run workflow:activity-interrupt` | Activity-thrown interrupt routed through `StrandsFailureConverter`. | +| Hooks | `npm run workflow:hooks` | `AfterToolCallEvent` with in-workflow and activity-dispatched callbacks. | +| MCP | `npm run workflow:mcp` | Connect to a stdio MCP server via `TemporalMCPClient`. | +| Structured Output | `npm run workflow:structured-output` | Constrain output with a Zod schema. | +| Streaming | `npm run workflow:streaming` | Forward model chunks to subscribers via `WorkflowStream`. | +| Continue as New | `npm run workflow:continue-as-new` | Long-lived chat with history-aware continue-as-new. | ### Directory layout diff --git a/strands-agents/src/activities/tools.ts b/strands-agents/src/activities/tools.ts index 57d032432..c1b4f0544 100644 --- a/strands-agents/src/activities/tools.ts +++ b/strands-agents/src/activities/tools.ts @@ -1,5 +1,7 @@ // @@@SNIPSTART typescript-strands-tools-activity -export async function fetchWeather(input: { city: string }): Promise<{ city: string; temperatureF: number; conditions: string }> { +export async function fetchWeather(input: { + city: string; +}): Promise<{ city: string; temperatureF: number; conditions: string }> { return { city: input.city, temperatureF: 72, diff --git a/strands-agents/src/activity-interrupt.ts b/strands-agents/src/activity-interrupt.ts index 16ffc1d28..dc639fdeb 100644 --- a/strands-agents/src/activity-interrupt.ts +++ b/strands-agents/src/activity-interrupt.ts @@ -1,10 +1,6 @@ import { Client, Connection } from '@temporalio/client'; import { StrandsPlugin } from '@temporalio/strands-agents'; -import { - activityInterrupt, - activityInterruptApproveSignal, - activityInterruptPendingApprovalQuery, -} from './workflows'; +import { activityInterrupt, activityInterruptApproveSignal, activityInterruptPendingApprovalQuery } from './workflows'; async function run() { const connection = await Connection.connect({ diff --git a/strands-agents/src/mcp-server.ts b/strands-agents/src/mcp-server.ts index a07075050..1231322c4 100644 --- a/strands-agents/src/mcp-server.ts +++ b/strands-agents/src/mcp-server.ts @@ -10,7 +10,7 @@ server.registerTool( description: 'Return the input message unchanged.', inputSchema: { message: z.string() }, }, - async ({ message }) => ({ content: [{ type: 'text', text: message }] }) + async ({ message }) => ({ content: [{ type: 'text', text: message }] }), ); async function main() { diff --git a/strands-agents/src/mocha/activity-interrupt.test.ts b/strands-agents/src/mocha/activity-interrupt.test.ts index 559d47140..38d7b78c7 100644 --- a/strands-agents/src/mocha/activity-interrupt.test.ts +++ b/strands-agents/src/mocha/activity-interrupt.test.ts @@ -31,10 +31,7 @@ describe('activityInterrupt workflow', () => { new StrandsPlugin({ models: { bedrock: () => - new StubModel([ - toolCallTurn('deleteThing', 'call_1', { name: 'system' }), - textTurn('deleted system'), - ]), + new StubModel([toolCallTurn('deleteThing', 'call_1', { name: 'system' }), textTurn('deleted system')]), }, }), ], diff --git a/strands-agents/src/mocha/hello-world.test.ts b/strands-agents/src/mocha/hello-world.test.ts index d5b94c3e3..ad9e3d67e 100644 --- a/strands-agents/src/mocha/hello-world.test.ts +++ b/strands-agents/src/mocha/hello-world.test.ts @@ -33,7 +33,7 @@ describe('helloWorld workflow', () => { args: ['say hi'], workflowId: 'test-hello', taskQueue, - }) + }), ); assert.equal(result, 'hello from stub'); }); diff --git a/strands-agents/src/mocha/hooks.test.ts b/strands-agents/src/mocha/hooks.test.ts index 49431ddc5..3d02a1a7f 100644 --- a/strands-agents/src/mocha/hooks.test.ts +++ b/strands-agents/src/mocha/hooks.test.ts @@ -30,8 +30,7 @@ describe('hooksWorkflow', () => { plugins: [ new StrandsPlugin({ models: { - bedrock: () => - new StubModel([toolCallTurn('echo', 'call_1', { text: 'hello' }), textTurn('done')]), + bedrock: () => new StubModel([toolCallTurn('echo', 'call_1', { text: 'hello' }), textTurn('done')]), }, }), ], @@ -42,7 +41,7 @@ describe('hooksWorkflow', () => { args: ['say hello'], workflowId: 'test-hooks', taskQueue, - }) + }), ); assert.deepEqual(fired, ['echo']); }); diff --git a/strands-agents/src/mocha/human-in-the-loop.test.ts b/strands-agents/src/mocha/human-in-the-loop.test.ts index 0d43546c0..d921f9b77 100644 --- a/strands-agents/src/mocha/human-in-the-loop.test.ts +++ b/strands-agents/src/mocha/human-in-the-loop.test.ts @@ -29,10 +29,7 @@ describe('humanInTheLoop workflow', () => { new StrandsPlugin({ models: { bedrock: () => - new StubModel([ - toolCallTurn('deleteFile', 'call_1', { path: '/tmp/x' }), - textTurn('deleted /tmp/x'), - ]), + new StubModel([toolCallTurn('deleteFile', 'call_1', { path: '/tmp/x' }), textTurn('deleted /tmp/x')]), }, }), ], diff --git a/strands-agents/src/mocha/mcp.test.ts b/strands-agents/src/mocha/mcp.test.ts index 0c706eca8..2968e97b0 100644 --- a/strands-agents/src/mocha/mcp.test.ts +++ b/strands-agents/src/mocha/mcp.test.ts @@ -29,7 +29,7 @@ class StubMcpClient extends McpClient { name: t.name, description: t.description, toolSpec: { name: t.name, description: t.description, inputSchema: t.inputSchema }, - }) as unknown as Tool + }) as unknown as Tool, ) as never; } override async callTool(tool: { name: string }, args: JSONValue): Promise { @@ -71,8 +71,7 @@ describe('mcpWorkflow', () => { plugins: [ new StrandsPlugin({ models: { - bedrock: () => - new StubModel([toolCallTurn('echo', 'call_1', { message: 'hi' }), textTurn('echoed')]), + bedrock: () => new StubModel([toolCallTurn('echo', 'call_1', { message: 'hi' }), textTurn('echoed')]), }, mcpClients: { echo: echoFactory }, }), @@ -84,7 +83,7 @@ describe('mcpWorkflow', () => { args: ['echo hi'], workflowId: 'test-mcp', taskQueue, - }) + }), ); assert.equal(result, 'echoed'); }); diff --git a/strands-agents/src/mocha/structured-output.test.ts b/strands-agents/src/mocha/structured-output.test.ts index d1f9d208a..b1a5e221a 100644 --- a/strands-agents/src/mocha/structured-output.test.ts +++ b/strands-agents/src/mocha/structured-output.test.ts @@ -40,7 +40,7 @@ describe('structuredOutputWorkflow', () => { args: ['describe John'], workflowId: 'test-structured', taskQueue, - }) + }), ); assert.deepEqual(result, person); }); diff --git a/strands-agents/src/mocha/tools.test.ts b/strands-agents/src/mocha/tools.test.ts index 631565e1b..954fdc964 100644 --- a/strands-agents/src/mocha/tools.test.ts +++ b/strands-agents/src/mocha/tools.test.ts @@ -46,7 +46,7 @@ describe('toolsWorkflow', () => { args: ['count r in strawberry and check the weather'], workflowId: 'test-tools', taskQueue, - }) + }), ); assert.equal(result, 'there are 3 Rs and it is sunny'); }); diff --git a/strands-agents/src/workflows.ts b/strands-agents/src/workflows.ts index 4aee1a442..9ec230d5f 100644 --- a/strands-agents/src/workflows.ts +++ b/strands-agents/src/workflows.ts @@ -1,10 +1,6 @@ export { helloWorld } from './workflows/hello-world'; export { toolsWorkflow } from './workflows/tools'; -export { - humanInTheLoop, - hitlApproveSignal, - hitlPendingApprovalQuery, -} from './workflows/human-in-the-loop'; +export { humanInTheLoop, hitlApproveSignal, hitlPendingApprovalQuery } from './workflows/human-in-the-loop'; export { activityInterrupt, activityInterruptApproveSignal, diff --git a/strands-agents/src/workflows/hooks.ts b/strands-agents/src/workflows/hooks.ts index f240f03d0..a5925e2e0 100644 --- a/strands-agents/src/workflows/hooks.ts +++ b/strands-agents/src/workflows/hooks.ts @@ -29,7 +29,7 @@ export async function hooksWorkflow(prompt: string): Promise { strandsWorkflow.activityAsHook('persistToolCall', { activityInput: (event) => event.toolUse.name, activityOptions: { startToCloseTimeout: '15 seconds', retry: { maximumAttempts: 3 } }, - }) + }), ); await agent.invoke(prompt); From 0ea356747dd46a55de5337601167dc649a9da434 Mon Sep 17 00:00:00 2001 From: Brian Strauch Date: Fri, 24 Jul 2026 10:23:54 -0700 Subject: [PATCH 6/6] fix: convert strands-agents sample to ESM so CI passes @strands-agents/sdk is ESM-only (every published version sets "type": "module"), which the repo's nodenext tsconfig cannot consume from a CommonJS sample. Under npm's flat node_modules this was masked, but pnpm's strict layout (used in CI) surfaces it as a build failure, and CommonJS also can't require() the SDK on Node 20. Convert the sample to ESM, following the fetch-esm template: - package.json: "type": "module"; run scripts via tsx (drop ts-node/nodemon); mocha runs under tsx; add @temporalio/common (was imported but undeclared) - tsconfig.json: module esnext / moduleResolution node / noEmit - worker.ts + tests: replace require.resolve/__dirname with import.meta.url (new src/mocha/workflows-path.ts helper for the test suite) - rename .eslintrc.js -> .eslintrc.cjs (CommonJS config under type: module) - exclude strands-agents from the shared tsconfig/eslintrc copies Also patch @temporalio/strands-agents@1.21.1 via pnpm.packageExtensions: it calls zod 4's z.toJSONSchema at runtime but only declares zod as a devDependency, so pnpm's strict layout leaves it unresolvable. Declaring the dependency links the correct zod v4 for that package only. Co-Authored-By: Claude Opus 4.8 (1M context) --- .scripts/copy-shared-files.mjs | 2 + package.json | 5 + pnpm-lock.yaml | 169 ++++++++++++------ .../{.eslintrc.js => .eslintrc.cjs} | 0 strands-agents/package.json | 42 ++--- .../src/mocha/activity-interrupt.test.ts | 3 +- .../src/mocha/continue-as-new.test.ts | 3 +- strands-agents/src/mocha/hello-world.test.ts | 3 +- strands-agents/src/mocha/hooks.test.ts | 3 +- .../src/mocha/human-in-the-loop.test.ts | 3 +- strands-agents/src/mocha/mcp.test.ts | 3 +- strands-agents/src/mocha/streaming.test.ts | 3 +- .../src/mocha/structured-output.test.ts | 3 +- strands-agents/src/mocha/tools.test.ts | 3 +- strands-agents/src/mocha/workflows-path.ts | 5 + strands-agents/src/worker.ts | 8 +- strands-agents/tsconfig.json | 6 +- 17 files changed, 176 insertions(+), 88 deletions(-) rename strands-agents/{.eslintrc.js => .eslintrc.cjs} (100%) create mode 100644 strands-agents/src/mocha/workflows-path.ts diff --git a/.scripts/copy-shared-files.mjs b/.scripts/copy-shared-files.mjs index 00d0a3e5d..c8b09de4f 100644 --- a/.scripts/copy-shared-files.mjs +++ b/.scripts/copy-shared-files.mjs @@ -18,6 +18,7 @@ const TSCONFIG_EXCLUDE = [ 'nextjs-ecommerce-oneclick', 'monorepo-folders', 'fetch-esm', + 'strands-agents', 'production', 'hello-world-js', 'food-delivery', @@ -41,6 +42,7 @@ const ESLINTRC_EXCLUDE = [ 'nextjs-ecommerce-oneclick', 'monorepo-folders', 'fetch-esm', + 'strands-agents', 'hello-world-js', 'protobufs', 'food-delivery', diff --git a/package.json b/package.json index 71080a353..3753cfb25 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,11 @@ "peerDependencies": { "ts-node": "*" } + }, + "@temporalio/strands-agents": { + "dependencies": { + "zod": "^4.0.0" + } } } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23a44fcb6..d097e58d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,7 +4,7 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -packageExtensionsChecksum: sha256-RSsb6d/Rd8pXhdhSBzrPSOhVUv6Qo/SaIv70LvTVxoA= +packageExtensionsChecksum: sha256-UuLKW9BCv/VaZT6E7tclNpwm12M1QVyh2vqxmcC/zL8= importers: @@ -3861,6 +3861,9 @@ importers: '@temporalio/client': specifier: ^1.21.1 version: 1.21.1 + '@temporalio/common': + specifier: ^1.21.1 + version: 1.21.1 '@temporalio/envconfig': specifier: ^1.21.1 version: 1.21.1 @@ -3887,8 +3890,8 @@ importers: specifier: ^22.0.0 version: 22.0.5 '@types/mocha': - specifier: 8.x - version: 8.2.3 + specifier: ^10.0.10 + version: 10.0.10 '@types/node': specifier: ^22.9.1 version: 22.12.0 @@ -3908,17 +3911,14 @@ importers: specifier: ^3.0.0 version: 3.0.0(eslint@8.57.1)(typescript@5.7.3) mocha: - specifier: 8.x - version: 8.4.0(ts-node@10.9.2(@swc/core@1.10.11(@swc/helpers@0.5.15))(@types/node@22.12.0)(typescript@5.7.3)) - nodemon: - specifier: ^3.1.7 - version: 3.1.9 + specifier: ^11.7.4 + version: 11.7.6(ts-node@10.9.2(@swc/core@1.10.11(@swc/helpers@0.5.15))(@types/node@22.12.0)(typescript@5.7.3)) prettier: specifier: ^3.4.2 version: 3.4.2 - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@swc/core@1.10.11(@swc/helpers@0.5.15))(@types/node@22.12.0)(typescript@5.7.3) + tsx: + specifier: ^4.20.6 + version: 4.20.6 typescript: specifier: ^5.6.3 version: 5.7.3 @@ -8714,6 +8714,9 @@ packages: '@types/mocha@10.0.1': resolution: {integrity: sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==} + '@types/mocha@10.0.10': + resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} + '@types/mocha@8.2.3': resolution: {integrity: sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==} @@ -9890,6 +9893,10 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -10610,6 +10617,10 @@ packages: resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} engines: {node: '>=0.3.1'} + diff@7.0.0: + resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} + engines: {node: '>=0.3.1'} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -13608,6 +13619,13 @@ packages: peerDependencies: ts-node: '*' + mocha@11.7.6: + resolution: {integrity: sha512-nS9xOGbw2I3cjCpxwZAEJ9xK9lmJ08vEkQvLtz4du9ZrF9UrjRpeJGiIgl2Z+Qs++pmB4ecDe48Fwsh+j+j7xA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + ts-node: '*' + mocha@8.4.0: resolution: {integrity: sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==} engines: {node: '>= 10.12.0'} @@ -15075,6 +15093,10 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + recast@0.21.5: resolution: {integrity: sha512-hjMmLaUXAm1hIuTqOdeYObMslq/q+Xff6QE3Y2P+uoHAg2nmVlLBps2hzh1UJDdMtDTMXOFewK6ky51JQIeECg==} engines: {node: '>= 4'} @@ -16933,6 +16955,9 @@ packages: workerpool@6.2.1: resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} + workerpool@9.3.4: + resolution: {integrity: sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==} + wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} @@ -17442,7 +17467,7 @@ snapshots: '@babel/types': 7.29.7 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -17549,7 +17574,7 @@ snapshots: '@babel/core': 7.26.7 '@babel/helper-compilation-targets': 7.26.5 '@babel/helper-plugin-utils': 7.26.5 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.10 transitivePeerDependencies: @@ -17560,7 +17585,7 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.12 transitivePeerDependencies: @@ -19059,7 +19084,7 @@ snapshots: '@babel/parser': 7.29.7 '@babel/template': 7.29.7 '@babel/types': 7.29.7 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -19419,7 +19444,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) espree: 9.6.1 globals: 13.24.0 ignore: 5.3.2 @@ -19484,7 +19509,7 @@ snapshots: '@humanwhocodes/config-array@0.13.0': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -20512,7 +20537,7 @@ snapshots: '@openai/agents-core@0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3)': dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) openai: 6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) optionalDependencies: '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.3) @@ -20528,7 +20553,7 @@ snapshots: '@openai/agents-openai@0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3)': dependencies: '@openai/agents-core': 0.11.8(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) openai: 6.45.0(@aws-sdk/credential-provider-node@3.972.71)(@smithy/signature-v4@5.6.9)(ws@8.18.0)(zod@4.4.3) zod: 4.4.3 transitivePeerDependencies: @@ -22062,6 +22087,7 @@ snapshots: '@ungap/structured-clone': 1.3.2 headers-polyfill: 4.0.3 web-streams-polyfill: 4.2.0 + zod: 4.4.3 '@temporalio/testing@1.20.3(@swc/helpers@0.5.15)': dependencies: @@ -22376,7 +22402,7 @@ snapshots: '@textlint/markdown-to-ast@12.6.1': dependencies: '@textlint/ast-node-types': 12.6.1 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) mdast-util-gfm-autolink-literal: 0.1.3 remark-footnotes: 3.0.0 remark-frontmatter: 3.0.0 @@ -22652,6 +22678,8 @@ snapshots: '@types/mocha@10.0.1': {} + '@types/mocha@10.0.10': {} + '@types/mocha@8.2.3': {} '@types/mocha@9.1.1': {} @@ -22815,7 +22843,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/type-utils': 5.62.0(eslint@8.57.1)(typescript@5.7.3) '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.7.3) - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) eslint: 8.57.1 graphemer: 1.4.0 ignore: 5.3.2 @@ -22857,7 +22885,7 @@ snapshots: '@typescript-eslint/scope-manager': 5.62.0 '@typescript-eslint/types': 5.62.0 '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.7.3) - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) eslint: 8.57.1 optionalDependencies: typescript: 5.7.3 @@ -22895,7 +22923,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.7.3) '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.7.3) - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) eslint: 8.57.1 tsutils: 3.21.0(typescript@5.7.3) optionalDependencies: @@ -22907,7 +22935,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 8.22.0(typescript@5.7.3) '@typescript-eslint/utils': 8.22.0(eslint@8.57.1)(typescript@5.7.3) - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) eslint: 8.57.1 ts-api-utils: 2.0.0(typescript@5.7.3) typescript: 5.7.3 @@ -22924,7 +22952,7 @@ snapshots: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.3 @@ -22938,7 +22966,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.9 @@ -22953,7 +22981,7 @@ snapshots: dependencies: '@typescript-eslint/types': 8.22.0 '@typescript-eslint/visitor-keys': 8.22.0 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.9 @@ -23431,7 +23459,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -24089,7 +24117,7 @@ snapshots: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) http-errors: 2.0.1 iconv-lite: 0.7.2 on-finished: 2.4.1 @@ -24344,6 +24372,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + chownr@1.1.4: {} chownr@2.0.0: {} @@ -24863,6 +24895,12 @@ snapshots: optionalDependencies: supports-color: 5.5.0 + debug@4.4.3(supports-color@8.1.1): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 + decamelize@1.2.0: {} decamelize@4.0.0: {} @@ -25014,6 +25052,8 @@ snapshots: diff@5.2.2: {} + diff@7.0.0: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -25574,7 +25614,7 @@ snapshots: eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) enhanced-resolve: 5.18.0 eslint: 8.57.1 fast-glob: 3.3.3 @@ -25821,7 +25861,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -26057,7 +26097,7 @@ snapshots: content-type: 1.0.5 cookie: 0.7.1 cookie-signature: 1.2.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 @@ -26209,7 +26249,7 @@ snapshots: finalhandler@2.1.1: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 on-finished: 2.4.1 @@ -26484,7 +26524,7 @@ snapshots: dependencies: '@tootallnate/once': 1.1.2 data-uri-to-buffer: 3.0.1 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) file-uri-to-path: 2.0.0 fs-extra: 8.1.0 ftp: 0.3.10 @@ -26803,7 +26843,7 @@ snapshots: dependencies: '@tootallnate/once': 1.1.2 agent-base: 6.0.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -26835,14 +26875,14 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.3 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -27288,7 +27328,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -29166,7 +29206,7 @@ snapshots: micromark@2.11.4: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) parse-entities: 2.0.0 transitivePeerDependencies: - supports-color @@ -29174,7 +29214,7 @@ snapshots: micromark@3.2.0: dependencies: '@types/debug': 4.1.13 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) decode-named-character-reference: 1.3.0 micromark-core-commonmark: 1.1.0 micromark-factory-space: 1.1.0 @@ -29362,6 +29402,31 @@ snapshots: yargs-parser: 20.2.4 yargs-unparser: 2.0.0 + mocha@11.7.6(ts-node@10.9.2(@swc/core@1.10.11(@swc/helpers@0.5.15))(@types/node@22.12.0)(typescript@5.7.3)): + dependencies: + browser-stdout: 1.3.1 + chokidar: 4.0.3 + debug: 4.4.3(supports-color@8.1.1) + diff: 7.0.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 10.4.5 + he: 1.2.0 + is-path-inside: 3.0.3 + js-yaml: 4.2.0 + log-symbols: 4.1.0 + minimatch: 9.0.9 + ms: 2.1.3 + picocolors: 1.1.1 + serialize-javascript: 6.0.2 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + ts-node: 10.9.2(@swc/core@1.10.11(@swc/helpers@0.5.15))(@types/node@22.12.0)(typescript@5.7.3) + workerpool: 9.3.4 + yargs: 17.7.2 + yargs-parser: 21.1.1 + yargs-unparser: 2.0.0 + mocha@8.4.0(ts-node@10.9.2(@swc/core@1.10.11(@swc/helpers@0.5.15))(@types/node@22.12.0)(typescript@5.7.3)): dependencies: '@ungap/promise-all-settled': 1.1.2 @@ -29874,7 +29939,7 @@ snapshots: dependencies: '@tootallnate/once': 1.1.2 agent-base: 6.0.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) get-uri: 3.0.2 http-proxy-agent: 4.0.1 https-proxy-agent: 5.0.1 @@ -30748,7 +30813,7 @@ snapshots: proxy-agent@5.0.0: dependencies: agent-base: 6.0.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) http-proxy-agent: 4.0.1 https-proxy-agent: 5.0.1 lru-cache: 5.1.1 @@ -31071,6 +31136,8 @@ snapshots: dependencies: picomatch: 2.3.1 + readdirp@4.1.2: {} + recast@0.21.5: dependencies: ast-types: 0.15.2 @@ -31246,7 +31313,7 @@ snapshots: require-in-the-middle@8.0.1: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) module-details-from-path: 1.0.3 transitivePeerDependencies: - supports-color @@ -31374,7 +31441,7 @@ snapshots: router@2.2.0: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 @@ -31548,7 +31615,7 @@ snapshots: send@1.2.1: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 @@ -31809,7 +31876,7 @@ snapshots: socks-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) socks: 2.8.9 transitivePeerDependencies: - supports-color @@ -31904,7 +31971,7 @@ snapshots: spdy-transport@3.0.0: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) detect-node: 2.1.0 hpack.js: 2.1.6 obuf: 1.1.2 @@ -31915,7 +31982,7 @@ snapshots: spdy@4.0.2: dependencies: - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) handle-thing: 2.0.1 http-deceiver: 1.2.7 select-hose: 2.0.0 @@ -32143,7 +32210,7 @@ snapshots: dependencies: component-emitter: 1.3.1 cookiejar: 2.1.4 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) fast-safe-stringify: 2.1.1 form-data: 4.0.1 formidable: 2.1.2 @@ -33019,7 +33086,7 @@ snapshots: vite-node@1.6.1(@types/node@22.12.0)(terser@5.37.0): dependencies: cac: 6.7.14 - debug: 4.4.3(supports-color@5.5.0) + debug: 4.4.3(supports-color@8.1.1) pathe: 1.1.2 picocolors: 1.1.1 vite: 5.4.21(@types/node@22.12.0)(terser@5.37.0) @@ -33623,6 +33690,8 @@ snapshots: workerpool@6.2.1: {} + workerpool@9.3.4: {} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 diff --git a/strands-agents/.eslintrc.js b/strands-agents/.eslintrc.cjs similarity index 100% rename from strands-agents/.eslintrc.js rename to strands-agents/.eslintrc.cjs diff --git a/strands-agents/package.json b/strands-agents/package.json index 07c117222..9178b8e08 100644 --- a/strands-agents/package.json +++ b/strands-agents/package.json @@ -2,39 +2,32 @@ "name": "temporal-strands-agents", "version": "0.1.0", "private": true, + "type": "module", "scripts": { "build": "tsc --build", "build.watch": "tsc --build --watch", "format": "prettier --write .", "format:check": "prettier --check .", "lint": "eslint .", - "start": "ts-node src/worker.ts", - "start.watch": "nodemon src/worker.ts", - "workflow:hello-world": "ts-node src/hello-world.ts", - "workflow:tools": "ts-node src/tools.ts", - "workflow:human-in-the-loop": "ts-node src/human-in-the-loop.ts", - "workflow:activity-interrupt": "ts-node src/activity-interrupt.ts", - "workflow:hooks": "ts-node src/hooks.ts", - "workflow:mcp": "ts-node src/mcp.ts", - "workflow:structured-output": "ts-node src/structured-output.ts", - "workflow:streaming": "ts-node src/streaming.ts", - "workflow:continue-as-new": "ts-node src/continue-as-new.ts", - "test": "mocha --exit --require ts-node/register --require source-map-support/register src/mocha/*.test.ts" - }, - "nodemonConfig": { - "execMap": { - "ts": "ts-node" - }, - "ext": "ts", - "watch": [ - "src" - ] + "start": "tsx src/worker.ts", + "start.watch": "tsx watch src/worker.ts", + "workflow:hello-world": "tsx src/hello-world.ts", + "workflow:tools": "tsx src/tools.ts", + "workflow:human-in-the-loop": "tsx src/human-in-the-loop.ts", + "workflow:activity-interrupt": "tsx src/activity-interrupt.ts", + "workflow:hooks": "tsx src/hooks.ts", + "workflow:mcp": "tsx src/mcp.ts", + "workflow:structured-output": "tsx src/structured-output.ts", + "workflow:streaming": "tsx src/streaming.ts", + "workflow:continue-as-new": "tsx src/continue-as-new.ts", + "test": "tsx ./node_modules/mocha/bin/mocha.js --exit \"src/mocha/*.test.ts\"" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.25.2", "@strands-agents/sdk": "^1.3.0", "@temporalio/activity": "^1.21.1", "@temporalio/client": "^1.21.1", + "@temporalio/common": "^1.21.1", "@temporalio/envconfig": "^1.21.1", "@temporalio/strands-agents": "^1.21.1", "@temporalio/worker": "^1.21.1", @@ -45,17 +38,16 @@ "devDependencies": { "@temporalio/testing": "^1.21.1", "@tsconfig/node22": "^22.0.0", - "@types/mocha": "8.x", + "@types/mocha": "^10.0.10", "@types/node": "^22.9.1", "@typescript-eslint/eslint-plugin": "^8.18.0", "@typescript-eslint/parser": "^8.18.0", "eslint": "^8.57.1", "eslint-config-prettier": "^9.1.0", "eslint-plugin-deprecation": "^3.0.0", - "mocha": "8.x", - "nodemon": "^3.1.7", + "mocha": "^11.7.4", "prettier": "^3.4.2", - "ts-node": "^10.9.2", + "tsx": "^4.20.6", "typescript": "^5.6.3" } } diff --git a/strands-agents/src/mocha/activity-interrupt.test.ts b/strands-agents/src/mocha/activity-interrupt.test.ts index 38d7b78c7..3ce6e480f 100644 --- a/strands-agents/src/mocha/activity-interrupt.test.ts +++ b/strands-agents/src/mocha/activity-interrupt.test.ts @@ -1,5 +1,6 @@ import assert from 'assert'; import { after, before, describe, it } from 'mocha'; +import { workflowsPath } from './workflows-path'; import { StrandsPlugin } from '@temporalio/strands-agents'; import { TestWorkflowEnvironment } from '@temporalio/testing'; import { Worker } from '@temporalio/worker'; @@ -25,7 +26,7 @@ describe('activityInterrupt workflow', () => { const worker = await Worker.create({ connection: nativeConnection, taskQueue, - workflowsPath: require.resolve('../workflows'), + workflowsPath, activities: { deleteThing }, plugins: [ new StrandsPlugin({ diff --git a/strands-agents/src/mocha/continue-as-new.test.ts b/strands-agents/src/mocha/continue-as-new.test.ts index aefd0e863..547c1504c 100644 --- a/strands-agents/src/mocha/continue-as-new.test.ts +++ b/strands-agents/src/mocha/continue-as-new.test.ts @@ -1,5 +1,6 @@ import assert from 'assert'; import { after, before, describe, it } from 'mocha'; +import { workflowsPath } from './workflows-path'; import { StrandsPlugin } from '@temporalio/strands-agents'; import { TestWorkflowEnvironment } from '@temporalio/testing'; import { Worker } from '@temporalio/worker'; @@ -24,7 +25,7 @@ describe('chatWorkflow', () => { const worker = await Worker.create({ connection: nativeConnection, taskQueue, - workflowsPath: require.resolve('../workflows'), + workflowsPath, plugins: [ new StrandsPlugin({ models: { diff --git a/strands-agents/src/mocha/hello-world.test.ts b/strands-agents/src/mocha/hello-world.test.ts index ad9e3d67e..7e4b7f2c9 100644 --- a/strands-agents/src/mocha/hello-world.test.ts +++ b/strands-agents/src/mocha/hello-world.test.ts @@ -1,5 +1,6 @@ import assert from 'assert'; import { after, before, describe, it } from 'mocha'; +import { workflowsPath } from './workflows-path'; import { StrandsPlugin } from '@temporalio/strands-agents'; import { TestWorkflowEnvironment } from '@temporalio/testing'; import { Worker } from '@temporalio/worker'; @@ -24,7 +25,7 @@ describe('helloWorld workflow', () => { const worker = await Worker.create({ connection: nativeConnection, taskQueue, - workflowsPath: require.resolve('../workflows'), + workflowsPath, plugins: [new StrandsPlugin({ models: { bedrock: () => new StubModel([textTurn('hello from stub')]) } })], }); diff --git a/strands-agents/src/mocha/hooks.test.ts b/strands-agents/src/mocha/hooks.test.ts index 3d02a1a7f..c3d7b67da 100644 --- a/strands-agents/src/mocha/hooks.test.ts +++ b/strands-agents/src/mocha/hooks.test.ts @@ -1,5 +1,6 @@ import assert from 'assert'; import { after, before, describe, it } from 'mocha'; +import { workflowsPath } from './workflows-path'; import { StrandsPlugin } from '@temporalio/strands-agents'; import { TestWorkflowEnvironment } from '@temporalio/testing'; import { Worker } from '@temporalio/worker'; @@ -25,7 +26,7 @@ describe('hooksWorkflow', () => { const worker = await Worker.create({ connection: nativeConnection, taskQueue, - workflowsPath: require.resolve('../workflows'), + workflowsPath, activities: { persistToolCall }, plugins: [ new StrandsPlugin({ diff --git a/strands-agents/src/mocha/human-in-the-loop.test.ts b/strands-agents/src/mocha/human-in-the-loop.test.ts index d921f9b77..739dcab43 100644 --- a/strands-agents/src/mocha/human-in-the-loop.test.ts +++ b/strands-agents/src/mocha/human-in-the-loop.test.ts @@ -1,5 +1,6 @@ import assert from 'assert'; import { after, before, describe, it } from 'mocha'; +import { workflowsPath } from './workflows-path'; import { StrandsPlugin } from '@temporalio/strands-agents'; import { TestWorkflowEnvironment } from '@temporalio/testing'; import { Worker } from '@temporalio/worker'; @@ -24,7 +25,7 @@ describe('humanInTheLoop workflow', () => { const worker = await Worker.create({ connection: nativeConnection, taskQueue, - workflowsPath: require.resolve('../workflows'), + workflowsPath, plugins: [ new StrandsPlugin({ models: { diff --git a/strands-agents/src/mocha/mcp.test.ts b/strands-agents/src/mocha/mcp.test.ts index 2968e97b0..ea1cc7d9a 100644 --- a/strands-agents/src/mocha/mcp.test.ts +++ b/strands-agents/src/mocha/mcp.test.ts @@ -3,6 +3,7 @@ import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; import { McpClient } from '@strands-agents/sdk'; import type { JSONValue, Tool } from '@strands-agents/sdk'; import { after, before, describe, it } from 'mocha'; +import { workflowsPath } from './workflows-path'; import { StrandsPlugin } from '@temporalio/strands-agents'; import { TestWorkflowEnvironment } from '@temporalio/testing'; import { Worker } from '@temporalio/worker'; @@ -67,7 +68,7 @@ describe('mcpWorkflow', () => { const worker = await Worker.create({ connection: nativeConnection, taskQueue, - workflowsPath: require.resolve('../workflows'), + workflowsPath, plugins: [ new StrandsPlugin({ models: { diff --git a/strands-agents/src/mocha/streaming.test.ts b/strands-agents/src/mocha/streaming.test.ts index 2390c25b0..929f0c40a 100644 --- a/strands-agents/src/mocha/streaming.test.ts +++ b/strands-agents/src/mocha/streaming.test.ts @@ -1,5 +1,6 @@ import assert from 'assert'; import { after, before, describe, it } from 'mocha'; +import { workflowsPath } from './workflows-path'; import { StrandsPlugin } from '@temporalio/strands-agents'; import { TestWorkflowEnvironment } from '@temporalio/testing'; import { Worker } from '@temporalio/worker'; @@ -25,7 +26,7 @@ describe('streamingWorkflow', () => { const worker = await Worker.create({ connection: nativeConnection, taskQueue, - workflowsPath: require.resolve('../workflows'), + workflowsPath, plugins: [new StrandsPlugin({ models: { bedrock: () => new StubModel([textTurn('streamed')]) } })], }); diff --git a/strands-agents/src/mocha/structured-output.test.ts b/strands-agents/src/mocha/structured-output.test.ts index b1a5e221a..5312e439c 100644 --- a/strands-agents/src/mocha/structured-output.test.ts +++ b/strands-agents/src/mocha/structured-output.test.ts @@ -1,5 +1,6 @@ import assert from 'assert'; import { after, before, describe, it } from 'mocha'; +import { workflowsPath } from './workflows-path'; import { StrandsPlugin } from '@temporalio/strands-agents'; import { TestWorkflowEnvironment } from '@temporalio/testing'; import { Worker } from '@temporalio/worker'; @@ -25,7 +26,7 @@ describe('structuredOutputWorkflow', () => { const worker = await Worker.create({ connection: nativeConnection, taskQueue, - workflowsPath: require.resolve('../workflows'), + workflowsPath, plugins: [ new StrandsPlugin({ models: { diff --git a/strands-agents/src/mocha/tools.test.ts b/strands-agents/src/mocha/tools.test.ts index 954fdc964..bf90e6190 100644 --- a/strands-agents/src/mocha/tools.test.ts +++ b/strands-agents/src/mocha/tools.test.ts @@ -1,5 +1,6 @@ import assert from 'assert'; import { after, before, describe, it } from 'mocha'; +import { workflowsPath } from './workflows-path'; import { StrandsPlugin } from '@temporalio/strands-agents'; import { TestWorkflowEnvironment } from '@temporalio/testing'; import { Worker } from '@temporalio/worker'; @@ -25,7 +26,7 @@ describe('toolsWorkflow', () => { const worker = await Worker.create({ connection: nativeConnection, taskQueue, - workflowsPath: require.resolve('../workflows'), + workflowsPath, activities: { fetchWeather }, plugins: [ new StrandsPlugin({ diff --git a/strands-agents/src/mocha/workflows-path.ts b/strands-agents/src/mocha/workflows-path.ts new file mode 100644 index 000000000..eeb575c7a --- /dev/null +++ b/strands-agents/src/mocha/workflows-path.ts @@ -0,0 +1,5 @@ +import { fileURLToPath } from 'node:url'; + +// Absolute path to the workflows entrypoint, resolved for the ESM runtime +// (tests run under tsx, so the `.ts` source is always the target). +export const workflowsPath = fileURLToPath(new URL('../workflows.ts', import.meta.url)); diff --git a/strands-agents/src/worker.ts b/strands-agents/src/worker.ts index af0602b93..6589b8c25 100644 --- a/strands-agents/src/worker.ts +++ b/strands-agents/src/worker.ts @@ -1,18 +1,20 @@ // @@@SNIPSTART typescript-strands-worker import path from 'node:path'; +import { fileURLToPath } from 'node:url'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; import { McpClient } from '@strands-agents/sdk'; import { StrandsPlugin } from '@temporalio/strands-agents'; import { NativeConnection, Worker } from '@temporalio/worker'; import * as activities from './activities'; -const ECHO_SERVER = path.join(__dirname, 'mcp-server.ts'); +const ext = path.extname(fileURLToPath(import.meta.url)); +const ECHO_SERVER = fileURLToPath(new URL(`./mcp-server${ext}`, import.meta.url)); function makeEchoClient(): McpClient { return new McpClient({ transport: new StdioClientTransport({ command: 'npx', - args: ['ts-node', ECHO_SERVER], + args: ['tsx', ECHO_SERVER], }), }); } @@ -25,7 +27,7 @@ async function run() { const worker = await Worker.create({ connection, taskQueue: 'strands-agents', - workflowsPath: require.resolve('./workflows'), + workflowsPath: fileURLToPath(new URL(`./workflows${ext}`, import.meta.url)), activities, // Omit `models:` so the plugin registers its default `BedrockModel` under // the name `bedrock`. To use a different provider or pin a model ID, diff --git a/strands-agents/tsconfig.json b/strands-agents/tsconfig.json index 488f2c62a..e42e44e52 100644 --- a/strands-agents/tsconfig.json +++ b/strands-agents/tsconfig.json @@ -3,11 +3,15 @@ "version": "5.6.3", "compilerOptions": { "lib": ["es2021"], + "module": "esnext", + "moduleResolution": "node", "declaration": true, "declarationMap": true, "sourceMap": true, "rootDir": "./src", - "outDir": "./lib" + "outDir": "./lib", + "allowImportingTsExtensions": true, + "noEmit": true }, "include": ["src/**/*.ts"] }