# https://github.com/strands-agents/harness-sdk Project Manual

Generated at: 2026-06-03 17:17:16 UTC

## Table of Contents

- [Monorepo Architecture & SDKs Overview](#page-1)
- [Agent Loop, Tools & Hooks](#page-2)
- [Model Providers & Multi-Agent Patterns](#page-3)
- [Plugins, Bidirectional Streaming & WASM Interop](#page-4)

<a id='page-1'></a>

## Monorepo Architecture & SDKs Overview

### Related Pages

Related topics: [Agent Loop, Tools & Hooks](#page-2), [Plugins, Bidirectional Streaming & WASM Interop](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/strands-agents/harness-sdk/blob/main/README.md)
- [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)
- [team/README.md](https://github.com/strands-agents/harness-sdk/blob/main/team/README.md)
- [designs/README.md](https://github.com/strands-agents/harness-sdk/blob/main/designs/README.md)
- [site/package.json](https://github.com/strands-agents/harness-sdk/blob/main/site/package.json)
- [strands-py-wasm/src/strands/_generated/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/_generated/__init__.py)
- [strands-py-wasm/src/strands/types.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/types.py)
- [strands-py/src/strands/models/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/__init__.py)
- [strands-py/src/strands/models/openai.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai.py)
- [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/mistral.py)
- [strands-py/src/strands/models/openai_responses.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai_responses.py)
- [strands-ts/src/models/model.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/model.ts)
- [strands-ts/src/models/streaming.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/streaming.ts)
- [strands-ts/src/models/bedrock.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/bedrock.ts)
</details>

# Monorepo Architecture & SDKs Overview

## Purpose and Scope

The Strands Agents harness-sdk repository is a **multi-package monorepo** that hosts the Strands agent framework across several language targets, build systems, and supporting tooling. Its primary purpose is to provide a single source of truth from which Python-native, TypeScript-native, and WebAssembly-bridged SDKs are derived, along with the documentation, design proposals, and team policies that govern them.

The repository coordinates three first-class surfaces:

1. A **pure Python SDK** (`strands-py`) — the original Strands implementation and the canonical reference for the agent loop, model providers, and tool ecosystem.
2. A **TypeScript SDK** (`strands-ts`) — written in TypeScript and published to the npm ecosystem.
3. A **WASM-bridged package** (`strands-py-wasm` + `strands-wasm`) — a WebAssembly component built from the TypeScript SDK that is loaded and driven by Python through `wasmtime-py`, enabling Python users to consume the TypeScript agent implementation.

Around these core SDKs, the monorepo also houses the documentation site (`site/`), the design-proposal process (`designs/`), the team's internal doctrine (`team/`), and developer tooling such as the `strandly` toolchain. Source: [README.md](https://github.com/strands-agents/harness-sdk/blob/main/README.md).

> **Community alignment.** Multiple open feature requests target the breadth of language targets and interop surface: a TypeScript/Node SDK ([#156](https://github.com/strands-agents/harness-sdk/issues/156)), a Go SDK ([#616](https://github.com/strands-agents/harness-sdk/issues/616)), a Java SDK ([#240](https://github.com/strands-agents/harness-sdk/issues/240)), and AG-UI protocol support ([#140](https://github.com/strands-agents/harness-sdk/issues/140)). The monorepo structure already accommodates the TS direction; Go and Java remain future work.

## High-Level Repository Layout

The repository is organized into discrete top-level packages, each with a focused responsibility. The table below summarizes the layout that can be derived from the README and package contents.

| Path | Role | Notes |
|------|------|-------|
| `strands-py/` | Pure Python SDK | The original, canonical Strands implementation; `pip install strands-agents`. |
| `strands-ts/` | TypeScript SDK | Published to npm; defines streaming, models, and tools. |
| `strands-wasm/` | WASM build tooling | Monorepo developer guide; describes WIT contracts and the build pipeline. |
| `strands-py-wasm/` | Python wrapper around the WASM component | Installed as `strands-py-wasm`; exposes generated Python bindings. |
| `strandly/` | Toolchain CLI | Linked to `PATH` by the bootstrap script. |
| `site/` | Documentation site (Astro + Starlight) | Builds API references for both Python and TypeScript SDKs. |
| `designs/` | RFC-style design proposals | Captures context, decision, and consequences for large features. |
| `team/` | Internal team doctrine | Tenets, decisions, API bar, agent guidelines. |

Source: [README.md](https://github.com/strands-agents/harness-sdk/blob/main/README.md), [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md), [team/README.md](https://github.com/strands-agents/harness-sdk/blob/main/team/README.md).

## Architecture Overview

The monorepo is intentionally layered. The diagram below illustrates the relationship between language targets, the WASM bridge, the documentation site, and the governance process.

```mermaid
graph TD
    Repo[harness-sdk Monorepo]
    Py[strands-py: Python SDK]
    TS[strands-ts: TypeScript SDK]
    WASMTool[strands-wasm: Build Tooling]
    WASMPy[strands-py-wasm: Python + WASM]
    Site[site: Astro/Starlight Docs]
    Designs[designs: RFCs]
    Team[team: Tenets & Decisions]
    Strandly[strandly: CLI Toolchain]

    Repo --> Py
    Repo --> TS
    Repo --> WASMTool
    Repo --> WASMPy
    Repo --> Site
    Repo --> Designs
    Repo --> Team
    Repo --> Strandly

    TS -->|compiled to| WASMTool
    WASMTool -->|produces .wasm| WASMPy
    WASMPy -->|loaded by| Py
    Site -->|clones & generates| Py
    Site -->|clones & generates| TS
    Designs -->|feeds| Repo
    Team -->|guides| Repo
    Strandly -->|builds & links| WASMTool
```

Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md), [site/package.json](https://github.com/strands-agents/harness-sdk/blob/main/site/package.json).

### Component Responsibilities

- **`strands-py`** is the public Python package. It exposes the `Agent` class, tool decorators, and a large family of model providers. The README's "Quick Start" example uses `from strands import Agent` and `pip install strands-agents strands-agents-tools`. Source: [README.md](https://github.com/strands-agents/harness-sdk/blob/main/README.md).
- **`strands-ts`** is the TypeScript counterpart. It defines streaming event types (e.g. `ToolUseInputDelta`, `ReasoningContentDelta`, `CitationsDelta`, `Usage`) and base model configuration (`BaseModelConfig`, `StreamOptions`). Source: [strands-ts/src/models/streaming.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/streaming.ts), [strands-ts/src/models/model.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/model.ts).
- **`strands-wasm`** is the build-time tooling and developer guide that explains how the TS SDK is compiled into a WebAssembly component (`strands-agent.wasm`) and how Python loads it via `wasmtime-py`. Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md).
- **`strands-py-wasm`** is the Python distribution that re-exports a generated `_generated` module and a curated `types` surface, allowing Python code to call into the WASM guest. Source: [strands-py-wasm/src/strands/_generated/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/_generated/__init__.py), [strands-py-wasm/src/strands/types.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/types.py).
- **`site/`** is the documentation site built with Astro and Starlight. Its `package.json` defines scripts to clone both SDKs and regenerate API references for each. Source: [site/package.json](https://github.com/strands-agents/harness-sdk/blob/main/site/package.json).
- **`designs/`** formalizes the design-proposal process. It documents the template, lifecycle, and "no designs have been accepted yet" current state. Source: [designs/README.md](https://github.com/strands-agents/harness-sdk/blob/main/designs/README.md).
- **`team/`** contains the project's doctrine: `TENETS.md`, `DECISIONS.md`, `API_BAR_RAISING.md`, and `AGENT_GUIDELINES.md`. Source: [team/README.md](https://github.com/strands-agents/harness-sdk/blob/main/team/README.md).
- **`strandly/`** provides the CLI toolchain that the bootstrap workflow links into `PATH`. Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md).

## The WASM Bridge

The most architecturally distinctive piece of the monorepo is the bridge that lets Python host a TypeScript agent. The TS SDK is compiled to a WebAssembly component; Python loads this component via `wasmtime-py` and drives it. Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md).

### WIT Contract

The contract between the WASM "guest" (TypeScript) and the Python "host" is defined in a single WIT file. It separates responsibilities cleanly:

- **Exports** (TS implements, Python calls): The `api` interface — agent construction, streaming, conversation management. All model provider HTTP calls (Bedrock, Anthropic, OpenAI, Gemini) happen inside the WASM guest.
- **Imports** (Python implements, TS calls back into): `tool-provider` for executing Python-defined tools, and `host-log` for routing log entries to Python's logging framework.

When the TS agent loop decides a tool needs to run, it calls the `tool-provider` import which crosses the WASM boundary back to Python where the actual tool function lives. Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md).

```mermaid
graph LR
    PyAgent[Python host: Agent loop & tools]
    WIT[wit/agent.wit contract]
    TSAgent[TypeScript guest: agent loop]
    Models[Model providers in WASM]
    Tools[Python-defined tools]
    Logs[Python logging]

    PyAgent -->|drives| TSAgent
    WIT --> PyAgent
    WIT --> TSAgent
    TSAgent --> Models
    TSAgent -->|tool-provider import| Tools
    PyAgent -->|exports api| WIT
    TSAgent -->|host-log import| Logs
```

Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md).

### Bootstrap and Build

The first-time setup is a single command. From the repository root:

```bash
git clone https://github.com/strands-agents/sdk-python.git
cd sdk-python
npm install
npm run dev -- bootstrap
```

`bootstrap` installs toolchains, links `strandly` to `PATH`, generates type bindings, builds all layers, installs `strands-py-wasm`, and runs all tests. Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md).

### Generated Python Bindings

`strands-py-wasm` exposes a large generated surface from `_generated/`. The list of re-exported names includes content block types (`TextBlock`, `ImageBlock`, `VideoBlock`, `DocumentBlock`, `CitationsBlock`), tool types (`ToolUseBlock`, `ToolResultBlock`, `ToolSpec`), model types (`BedrockModel`, `AnthropicModel`, `OpenaiModel`, `GoogleModel`, `CustomModel`), multi-agent types (`SwarmConfig`, `GraphConfig`, `HandoffEvent`), retry types (`ExponentialBackoff`, `ConstantBackoff`), session types (`SessionManager`, `SlidingWindowConversationManager`, `SummarizingConversationManager`), and many more. Source: [strands-py-wasm/src/strands/_generated/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/_generated/__init__.py).

The curated public surface is narrowed in `types.py`. Some stream-oriented types are intentionally dropped from the public re-export because they are not part of the host API:

| Dropped symbol | Reason / category |
|----------------|-------------------|
| `Datetime`, `Duration`, `Instant` | Time primitives not surfaced at the boundary. |
| `Error` | Underlying error primitive; users import the typed exceptions instead. |
| `InputStream`, `OutputStream`, `Pollable` | Async I/O primitives used internally by the WASM layer. |

The curated surface then re-exports the remaining symbols and adds convenience union types such as `ModelInput`, `ConversationManagerInput`, `VendedToolInput`, and `VendedPluginInput`. Source: [strands-py-wasm/src/strands/types.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/types.py).

## Model Provider Surface

Both language targets expose a family of model providers behind a consistent abstract base. The Python side uses lazy loading so that optional provider dependencies are not imported until the user actually instantiates that provider. Source: [strands-py/src/strands/models/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/__init__.py).

### Python Provider Catalog

| Provider class | Module | Lazy loaded |
|----------------|--------|-------------|
| `BedrockModel` | `bedrock.py` | Eager |
| `AnthropicModel` | `anthropic.py` | Yes (via `__getattr__`) |
| `GeminiModel` | `gemini.py` | Yes |
| `LiteLLMModel` | `litellm.py` | Yes |
| `LlamaAPIModel` | `llamaapi.py` | Yes |
| `LlamaCppModel` | `llamacpp.py` | Yes |
| `MistralModel` | `mistral.py` | Yes |
| `OllamaModel` | `ollama.py` | Yes |
| `OpenAIModel` | `openai.py` | Yes |

`Model` and `BaseModelConfig` are exported eagerly. Source: [strands-py/src/strands/models/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/__init__.py).

### TypeScript Provider Catalog

The TS SDK mirrors the same model-provider philosophy. The Bedrock provider, for example, imports `@aws-sdk/client-bedrock-runtime` and reuses shared streaming types such as `CitationsDelta`, `ReasoningContentDelta`, and `Usage`. It also re-exports the common `BaseModelConfig` and `StreamOptions` from a single source of truth. Source: [strands-ts/src/models/bedrock.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/bedrock.ts), [strands-ts/src/models/model.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/model.ts).

### Streaming Event Vocabulary

The TS SDK defines a stream event vocabulary that the Python WASM package mirrors through its generated bindings. Key event shapes include:

| Type | Purpose |
|------|---------|
| `ToolUseInputDelta` | Incremental tool input being generated. |
| `ReasoningContentDelta` | Incremental reasoning/thinking content. |
| `CitationsDelta` | Citations attached to generated content. |
| `Usage` | Input, output, total, and cache token counts. |

Source: [strands-ts/src/models/streaming.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/streaming.ts).

### Provider Implementation Patterns

Each provider implements the same loop, with provider-specific request formatting and error handling. The OpenAI provider formats an OpenAI-compatible chat streaming request, the OpenAI Responses provider reuses the native `input_tokens.count` endpoint when `use_native_token_count` is enabled, and the Mistral provider supports both streaming and non-streaming code paths. Source: [strands-py/src/strands/models/openai.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai.py), [strands-py/src/strands/models/openai_responses.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai_responses.py), [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/mistral.py).

## Documentation Site

The `site/` package is the user-facing documentation hub. It is built with Astro 6 and Starlight 0.38, uses TypeDoc 0.28 for TypeScript reference generation, and uses pydoc-markdown for the Python reference. The package script surface shows how the docs are tied to both SDKs:

| Script | Effect |
|--------|--------|
| `build` | `astro build` |
| `build:all` | `sdk:clone` + `sdk:generate` + `build` |
| `sdk:clone` | Clones both SDK repositories locally. |
| `sdk:generate:py` | Runs pydoc-markdown (via `uv` or `pip`). |
| `sdk:generate:ts` | Runs TypeDoc on the TypeScript SDK. |
| `sdk:sync` | `sdk:clone` + `sdk:generate` + `npm install`. |
| `routes:update` | Updates the known-routes index used by the site. |
| `typecheck` / `test` / `format` | Standard maintenance scripts. |

Source: [site/package.json](https://github.com/strands-agents/harness-sdk/blob/main/site/package.json).

## Governance and Design Process

Two governance documents shape the monorepo's evolution.

### Design Proposals (`designs/`)

A design document is required for new major features affecting multiple parts of the SDK, breaking changes to existing APIs, architectural changes requiring design discussion, large contributions, and features that introduce new concepts. Bug fixes, small improvements, documentation updates, and new extensions in a contributor's own repository are explicitly excluded. The submission flow is:

1. Check the roadmap.
2. Create a branch.
3. Add `designs/NNNN-feature-name.md` using the provided template.
4. Submit a pull request.
5. Iterate on feedback.
6. Get approval and merge.
7. Reference the design from the implementation PR.

The template captures Status, Date, Issue, Context, Decision, Developer Experience, Alternatives Considered, Consequences, and Willingness to Implement. At the time of the source files reviewed, "No designs have been accepted yet." Source: [designs/README.md](https://github.com/strands-agents/harness-sdk/blob/main/designs/README.md).

### Team Doctrine (`team/`)

The `team/` directory contains the long-lived principles that apply to all SDK work:

| Document | Purpose |
|----------|---------|
| `TENETS.md` | Core principles guiding SDK design. |
| `DECISIONS.md` | Record of design and API decisions with rationale. |
| `API_BAR_RAISING.md` | Process for reviewing and approving API changes. |
| `AGENT_GUIDELINES.md` | Conventions for AI agents operating on the repos. |

When a contribution results in a new decision that could guide future work, the contribution should add it to `DECISIONS.md`. Source: [team/README.md](https://github.com/strands-agents/harness-sdk/blob/main/team/README.md).

## End-to-End Developer Workflow

The first-time developer experience flows through the monorepo's `npm run dev -- bootstrap` command, which is intentionally exhaustive so that "if this command doesn't enable development out of the box, file an issue." Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md).

```mermaid
graph TD
    A[Clone repository] --> B[npm install]
    B --> C[npm run dev -- bootstrap]
    C --> D[Toolchains installed]
    C --> E[strandly linked to PATH]
    C --> F[Type bindings generated]
    C --> G[All layers built]
    C --> H[strands-py-wasm installed]
    C --> I[All tests run]
    D --> J[Ready to develop]
    E --> J
    F --> J
    G --> J
    H --> J
    I --> J
```

Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md).

Prerequisites are intentionally minimal: Node.js 20+ and Python 3.10+. Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md).

## Configuration and Customization Points

The monorepo is configurable in several places, each owned by a different package.

| Layer | Configuration mechanism | Source |
|-------|------------------------|--------|
| TypeScript model providers | `BaseModelConfig` (`modelId`, `maxTokens`, `temperature`, `topP`, `contextWindowLimit`). `contextWindowLimit` is auto-resolved from a built-in lookup table when not explicitly set, and re-resolved when `modelId` changes via `updateConfig()`. | [strands-ts/src/models/model.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/model.ts) |
| Python provider selection | Lazy import via `__getattr__` so unused providers do not impose dependency costs. | [strands-py/src/strands/models/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/__init__.py) |
| WASM Python public surface | `types.py` drops stream-only primitives (`Datetime`, `Duration`, `Error`, `InputStream`, `OutputStream`, `Pollable`) and adds curated union types (`ModelInput`, `ConversationManagerInput`, `VendedToolInput`, `VendedPluginInput`). | [strands-py-wasm/src/strands/types.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/types.py) |
| OpenAI Responses token counting | `use_native_token_count` in provider config toggles between native `input_tokens.count` and fallback estimation. | [strands-py/src/strands/models/openai_responses.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai_responses.py) |
| Mistral tool choice | `tool_choice` is accepted but currently ignored for that provider (with a runtime warning). | [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/mistral.py) |
| Documentation site | `site/package.json` scripts (`sdk:clone`, `sdk:generate:py`, `sdk:generate:ts`, `routes:update`) drive which SDKs are referenced and how. | [site/package.json](https://github.com/strands-agents/harness-sdk/blob/main/site/package.json) |

## Common Failure Modes and Pitfalls

These are the failure modes that can be observed from the source files alone; they are not invented, only described from evidence in the code.

- **Optional provider dependencies.** The Python `models/__init__.py` lazy-loads every provider except `BedrockModel`. If a user instantiates, e.g., `AnthropicModel` without that provider's package installed, the lazy import will fail at first use. Source: [strands-py/src/strands/models/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/__init__.py).
- **Native token counting fallback.** The OpenAI Responses provider attempts a native count when `use_native_token_count` is true, but on any exception it logs a debug message and falls back to estimation. Callers must not assume native counts are always returned. Source: [strands-py/src/strands/models/openai_responses.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai_responses.py).
- **Ignored `tool_choice`.** The Mistral provider accepts `tool_choice` for interface consistency but explicitly does not honor it. Relying on it for Mistral will silently misbehave. Source: [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/mistral.py).
- **Auto-populated `contextWindowLimit`.** The TS base config notes that explicitly set `contextWindowLimit` is preserved when `modelId` changes, while auto-populated values are re-resolved. Code that mutates the model ID dynamically should be aware of this. Source: [strands-ts/src/models/model.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/model.ts).
- **Design gating.** New major features require an accepted design document. A pull request that adds cross-cutting functionality without a corresponding accepted design is likely to be sent back to the proposal stage. Source: [designs/README.md](https://github.com/strands-agents/harness-sdk/blob/main/designs/README.md).
- **Bootstrap dependency.** The `bootstrap` script is the single source of truth for first-time setup. If it does not yield a working developer environment, the project's own documentation asks contributors to file an issue rather than improvise. Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md).

## Community-Reported Gaps and Roadmap Signals

Several community requests are worth calling out here because they directly map to monorepo decisions:

- **TypeScript/Node SDK ([#156](https://github.com/strands-agents/harness-sdk/issues/156))** — Already addressed by `strands-ts`, though users continue to discuss frontend, backend, and IaC (CDK) TypeScript workflows.
- **Go SDK ([#616](https://github.com/strands-agents/harness-sdk/issues/616))** and **Java SDK ([#240](https://github.com/strands-agents/harness-sdk/issues/240))** — Both are open requests. Neither has a corresponding package in the monorepo today; both would require new top-level directories analogous to `strands-py` and `strands-ts`.
- **Agent Skills ([#1181](https://github.com/strands-agents/harness-sdk/issues/1181))** — The generated `strands-py-wasm` symbols already include `SkillSource`, and the `VendedPluginInput` union includes `AgentSkills`, indicating the WASM-bridged SDK has begun to expose a skills surface.
- **AG-UI Protocol ([#140](https://github.com/strands-agents/harness-sdk/issues/140))** — Community proposal to map Strands events to the AG-UI protocol. The streaming event vocabulary in `strands-ts/src/models/streaming.ts` is the natural integration point.
- **Latest release signal** — "Python WASM v0.0.1 / Initial release of strands-agents-wasm" indicates the WASM bridge is the most recent forward step in the monorepo.

Source: `community_context` provided in the query; cross-references via [strands-py-wasm/src/strands/types.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/types.py) and [strands-py-wasm/src/strands/_generated/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/_generated/__init__.py).

## See Also

- [strands-py/src/strands/models/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/__init__.py) — Python model provider registry and lazy loading.
- [strands-ts/src/models/model.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/model.ts) — TypeScript base model configuration and streaming options.
- [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md) — WASM build pipeline and WIT contract reference.
- [strands-py-wasm/src/strands/types.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/types.py) — Curated Python re-exports of the WASM-bridged surface.
- [designs/README.md](https://github.com/strands-agents/harness-sdk/blob/main/designs/README.md) — Design proposal process and template.
- [team/README.md](https://github.com/strands-agents/harness-sdk/blob/main/team/README.md) — Team doctrine, tenets, and decisions log.

---

<a id='page-2'></a>

## Agent Loop, Tools & Hooks

### Related Pages

Related topics: [Monorepo Architecture & SDKs Overview](#page-1), [Model Providers & Multi-Agent Patterns](#page-3)

<details>
<summary>Related Source Files</summary>

The following source files from the repository were used to generate this page:

- [README.md](https://github.com/strands-agents/harness-sdk/blob/main/README.md)
- [strands-py/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/README.md)
- [strands-ts/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/README.md)
- [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)
- [designs/README.md](https://github.com/strands-agents/harness-sdk/blob/main/designs/README.md)
- [team/README.md](https://github.com/strands-agents/harness-sdk/blob/main/team/README.md)
- [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/mistral.py)
- [strands-py/src/strands/models/openai.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai.py)
- [strands-py/src/strands/models/litellm.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/litellm.py)
- [strands-py/src/strands/models/openai_responses.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai_responses.py)
- [strands-py/src/strands/models/writer.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py)
- [strands-ts/src/models/streaming.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/streaming.ts)
- [strands-ts/src/vended-tools/notebook/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/vended-tools/notebook/README.md)
- [strands-ts/examples/browser-agent/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/examples/browser-agent/README.md)
- [strands-py-wasm/src/strands/_generated/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/_generated/__init__.py)
- [site/package.json](https://github.com/strands-agents/harness-sdk/blob/main/site/package.json)
</details>

# Agent Loop, Tools & Hooks

## Overview

The **Agent Loop, Tools & Hooks** is the core execution model that makes Strands Agents a model-driven SDK for building and running AI agents. Rather than prescribing a rigid graph or state machine, Strands keeps the agent loop lightweight, hands the model control over tool selection, and exposes lifecycle hooks so developers can observe, customize, and integrate around each turn of the loop.

Strands Agents is delivered as a **monorepo** containing multiple SDKs that all converge on the same conceptual model:

| Package | Path | Purpose |
|---------|------|---------|
| Python SDK | `strands-py/` | Agent loop, model providers, tools (PyPI: `strands-agents`) |
| TypeScript SDK | `strands-ts/` | Agent loop, model providers, tools (npm: `@strands/agents`) |
| WebAssembly bindings | `strands-wasm/` + `strands-py-wasm/` | Run the TypeScript agent from Python via WASM |
| Developer CLI | `strandly/` | Local builds, codegen, workspace tooling |
| Documentation site | `site/` | Astro/Starlight site at strandsagents.com |
| Design proposals | `designs/` | RFC-style documents for significant changes |
| Team docs | `team/` | Tenets, decisions, API review process |

Source: [README.md](https://github.com/strands-agents/harness-sdk/blob/main/README.md)

The high-level tagline — "simple agent loop that just works and is fully customizable" — is the design intent behind this subsystem. Source: [strands-py/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/README.md)

---

## High-Level Architecture

The agent loop sits between three collaborators: a **model provider**, a set of **tools**, and a **hooks registry**. On each invocation, the agent sends messages and tool specs to the model, consumes the streaming response, executes any tool calls the model returns, appends tool results, and repeats until the model produces a final answer.

```mermaid
graph TD
    User[User / Caller] -->|prompt| Agent[Agent Loop]
    Agent -->|messages + tool_specs| Model[Model Provider]
    Model -->|streamed chunks| Agent
    Agent -->|tool_use| ToolRegistry[Tool Registry]
    ToolRegistry -->|executes| Tool1[Local Tool]
    ToolRegistry -->|executes| Tool2[MCP Tool]
    ToolRegistry -->|executes| Tool3[Vended Tool]
    Tool1 -->|tool_result| Agent
    Tool2 -->|tool_result| Agent
    Tool3 -->|tool_result| Agent
    Agent -->|events| Hooks[Hooks Registry]
    Hooks -->|observers| Observer1[Logging]
    Hooks -->|observers| Observer2[Metrics]
    Hooks -->|observers| Observer3[Tracing]
    Agent -->|final response| User
```

The TypeScript and Python SDKs are not independent forks of the same idea — they interoperate. The TypeScript SDK is compiled into a WebAssembly component (`strands-agent.wasm`) and loaded by Python via `wasmtime-py`. A WIT contract (`wit/agent.wit`) defines what crosses the boundary: the TS agent exposes an `api` interface (construction, streaming, conversation management), while Python implements the `tool-provider` and `host-log` imports. When the TS agent loop decides a tool needs to run, it calls the `tool-provider` import which crosses back to Python where the actual tool function lives. Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)

```mermaid
graph LR
    subgraph Python[Python Host]
        PyAgent[Python Agent / Caller]
        PyTool[Python-defined Tool]
        PyLog[Python logging]
    end
    subgraph WASM[WASM Guest - strands-agent.wasm]
        TsLoop[TypeScript Agent Loop]
        TsModel[Model Provider HTTP]
    end
    PyAgent -->|invoke| TsLoop
    TsLoop -->|HTTP| TsModel
    TsLoop -->|tool-provider import| PyTool
    PyTool -->|result| TsLoop
    TsLoop -->|host-log import| PyLog
    TsLoop -->|streamed events| PyAgent
```

---

## Agent Loop

### Quick Start — Python

The Python SDK exposes a single `Agent` class. Tools are passed as a list to the constructor, and a prompt is sent by calling the agent directly. Source: [strands-py/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/README.md)

```python
from strands import Agent
from strands_tools import calculator

agent = Agent(tools=[calculator])
agent("What is the square root of 1764")
```

By default the agent uses Amazon Bedrock and expects AWS credentials plus Claude 4 Sonnet model access in `us-west-2`. Other model providers are configurable. Source: [strands-py/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/README.md)

### Quick Start — TypeScript

The TypeScript SDK mirrors the same model. An `Agent` is constructed with a `model` and a list of `tools`, and prompts are sent with `agent.invoke(...)`. Source: [strands-ts/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/README.md) and [strands-ts/src/vended-tools/notebook/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/vended-tools/notebook/README.md)

```typescript
import { Agent, BedrockModel } from '@strands-agents/sdk'
import { notebook } from '@strands-agents/sdk/vended-tools/notebook'

const agent = new Agent({
  model: new BedrockModel({ region: 'us-east-1' }),
  tools: [notebook],
})

await agent.invoke('Create a notebook called "ideas" with the title "# Project Ideas"')
await agent.invoke('Add "- Build a web scraper" to the ideas notebook')
```

TypeScript requires **Node.js 20+**. Source: [strands-ts/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/README.md)

### Streaming

Both SDKs stream model responses in real time. The TypeScript SDK exposes a rich set of streaming event types defined in `strands-ts/src/models/streaming.ts`. These types form the contract that tools, hooks, and downstream UIs consume. Source: [strands-ts/src/models/streaming.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/streaming.ts)

| Event type | Purpose |
|------------|---------|
| `ToolUseInputDelta` | Incremental JSON of a tool's input parameters as the model generates them |
| `ReasoningContentDelta` | Incremental reasoning / chain-of-thought text, signature, or redacted content |
| `CitationsDelta` | Citations linking generated content back to source locations |
| `Usage` | Token accounting — `inputTokens`, `outputTokens`, `totalTokens`, plus cache metrics |

On the Python side, the model providers expose an async generator `stream(...)` that yields standardized `StreamEvent` chunks. The streaming implementation is responsible for formatting provider-specific events into the canonical `messageStart`, `contentBlockStart`, `contentBlockDelta`, `contentBlockStop`, `messageStop` lifecycle. Source: [strands-py/src/strands/models/writer.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py)

```python
async with mistralai.Mistral(**self.client_args) as client:
    stream_response = await client.chat.stream_async(**request)
    yield self.format_chunk({"chunk_type": "message_start"})
    async for chunk in stream_response:
        ...
        yield self.format_chunk(...)
```

Source: [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/mistral.py)

### Structured Output

Both SDKs support extracting structured data from model responses. The TypeScript SDK does this with **Zod schemas** and automatic retry on validation errors. Source: [strands-ts/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/README.md)

The Python SDK does the same via Pydantic-style output models. Providers that support a native `response_format` use it directly; others fall back to a tool-call-based extraction strategy. Source: [strands-py/src/strands/models/litellm.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/litellm.py)

```python
if supports_response_schema(self.get_config()["model_id"]):
    result = await self._structured_output_using_response_schema(output_model, prompt, system_prompt)
else:
    result = await self._structured_output_using_tool(output_model, prompt, system_prompt)
yield {"output": result}
```

Source: [strands-py/src/strands/models/litellm.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/litellm.py)

### Multi-Agent Orchestration (TypeScript)

The TypeScript SDK ships with first-class multi-agent patterns: **Graph** and **Swarm**. These let developers coordinate multiple agents without leaving the same agent-loop mental model. Source: [strands-ts/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/README.md)

---

## Tools

Tools are the primary extension point of the agent loop. The model receives a list of tool specifications and decides — turn by turn — which tools to invoke.

### Defining Tools in Python

Python tools are authored with a `@tool` decorator. The decorator introspects the function signature and produces a `ToolSpec` the model can call. Source: [strands-py/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/README.md)

```python
from strands import Agent, tool

@tool
def word_count(text: str) -> int:
    return len(text.split())
```

### Defining Tools in TypeScript

TypeScript tools are defined with **Zod schemas**, which give both runtime validation and compile-time type inference. Source: [strands-ts/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/README.md)

```typescript
import { tool } from '@strands-agents/sdk'
import { z } from 'zod'

const wordCount = tool({
  name: 'word_count',
  description: 'Count words in a string',
  input: z.object({ text: z.string() }),
  execute: async ({ text }) => text.split(/\s+/).length,
})
```

### Built-in Tool Packages

Python ships `strands-agents-tools` with a curated set of tools, including `calculator`, used in the quick start. The TypeScript SDK organizes built-in tools under `@strands-agents/sdk/vended-tools/*` (for example `notebook`). Source: [strands-py/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/README.md), [strands-ts/src/vended-tools/notebook/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/vended-tools/notebook/README.md)

### MCP (Model Context Protocol) Support

Both SDKs have **native support for MCP servers and clients**, giving agents access to thousands of pre-built tools without leaving the agent loop. Source: [strands-py/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/README.md), [strands-ts/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/README.md)

### Vended Tools & Session State

The TypeScript vended-tool pattern bundles a tool with the per-session state it manages. For example, the `notebook` tool keeps notebooks in `agent.appState`, which persists across `invoke` calls in the same session. Source: [strands-ts/src/vended-tools/notebook/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/vended-tools/notebook/README.md)

```typescript
// State is accessible via the agent
console.log(agent.appState.get('notebooks'))
// Output: { todo: '# Tasks\n- [ ] Review code\n- [ ] Write tests' }
```

This is one of the patterns the **AG-UI community proposal (#140)** is asking to standardize. The proposal describes a thin wrapper that maps Strands events to the AG-UI protocol so front-end applications can render agent state, tool calls, and streamed deltas uniformly. Source: community issue #140 in the conversation context.

### WASM Tool Bridge

The most distinctive tool integration is the **WASM tool provider**. When a TypeScript agent compiled to WASM needs to run a tool, it calls the `tool-provider` import across the WASM boundary back into Python. This means a single Python-defined tool can be invoked by an agent whose loop, model, and orchestration are all running in WASM. Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)

```mermaid
sequenceDiagram
    participant U as User
    participant Py as Python Caller
    participant W as WASM Agent (TS)
    participant M as Model Provider
    participant T as Python Tool

    U->>Py: invoke(prompt)
    Py->>W: api.invoke(prompt)
    W->>M: HTTP stream(messages, tool_specs)
    M-->>W: tool_use chunk
    W->>Py: tool-provider.run(name, args)
    Py->>T: execute(args)
    T-->>Py: result
    Py-->>W: tool_result
    W->>M: stream(tool_result)
    M-->>W: final answer
    W-->>Py: stream(final answer)
    Py-->>U: response
```

---

## Hooks

Hooks let developers observe and customize the agent loop without rewriting it. The TypeScript SDK explicitly advertises **"Extensible Hooks: Lifecycle hooks for monitoring and customizing agent behavior."** Source: [strands-ts/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/README.md)

The hook surface is event-driven. A hook can subscribe to lifecycle moments such as:

| Lifecycle moment | Typical use |
|------------------|-------------|
| Message start | Inject context, begin a span |
| Tool call dispatched | Validate arguments, redact secrets |
| Tool result received | Log outcomes, record metrics |
| Message stop | Update token usage, emit final telemetry |
| Reasoning delta | Stream intermediate thoughts to a UI |

The streaming event vocabulary is the contract hooks consume. For example, `ToolUseInputDelta` lets a hook reconstruct the partial JSON of an in-flight tool call, while `Usage` reports token consumption after a turn. Source: [strands-ts/src/models/streaming.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/streaming.ts)

```typescript
export interface Usage {
  inputTokens: number
  outputTokens: number
  totalTokens: number
  /* cache-related metrics */
}
```

Source: [strands-ts/src/models/streaming.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/streaming.ts)

The Python SDK exposes a similar hook concept through its model-provider layer: every provider's `stream(...)` is an async generator that yields standardized `StreamEvent` chunks, which is the natural integration point for any observer that wants to see exactly what the model is producing without re-implementing a provider. Source: [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/mistral.py)

---

## Model Provider Surface

While not strictly part of the agent loop, every model provider implements the same surface, which is what makes the loop model-agnostic. Looking across providers, the common shape is: Source: [strands-py/src/strands/models/openai.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai.py), [strands-py/src/strands/models/litellm.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/litellm.py), [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/mistral.py), [strands-py/src/strands/models/writer.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py)

| Method | Purpose |
|--------|---------|
| `format_request(messages, tool_specs, system_prompt)` | Convert canonical messages + tool specs into a provider-native request body |
| `format_chunk(event)` | Convert a provider-native event into a canonical `StreamEvent` |
| `stream(messages, tool_specs, system_prompt, ...)` | Async generator yielding canonical `StreamEvent` chunks |
| `structured_output(output_model, ...)` | Extract a typed object from the model response |

OpenAI Responses additionally supports **native token counting** via `openai.AsyncOpenAI.responses.input_tokens.count` when `use_native_token_count` is enabled, and falls back to estimation if the call fails. Source: [strands-py/src/strands/models/openai_responses.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai_responses.py)

The Writer provider shows a small but important detail: it renames `model_id` to `model` for the underlying API and skips the `tools` field when no tools are supplied, because the Writer API rejects empty tool arrays. Source: [strands-py/src/strands/models/writer.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py)

```python
try:
    request["model"] = request.pop("model_id")
except KeyError as e:
    raise KeyError("Please specify a model ID. Use 'model_id' keyword argument.") from e

if tool_specs:
    request["tools"] = [...]
```

Source: [strands-py/src/strands/models/writer.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py)

This per-provider behavior is exactly what the agent loop abstracts away: the loop always speaks the canonical `format_request` / `stream` / `structured_output` protocol, and each provider translates to its own wire format.

---

## Configuration & Feature Matrix

| Feature | Python SDK | TypeScript SDK |
|---------|------------|----------------|
| Install | `pip install strands-agents strands-agents-tools` | `npm install @strands-agents/sdk` |
| Runtime | Python 3.10+ | Node.js 20+ |
| Tool definition | `@tool` decorator | Zod schemas (`z.object(...)`) |
| Model providers | Bedrock, Anthropic, Gemini, LiteLLM, Llama, Ollama, OpenAI, Writer, Mistral, custom | Bedrock, OpenAI, Anthropic, custom |
| Streaming | `async for chunk in agent.stream(...)` | `await agent.invoke(...)` (streaming) |
| Structured output | Pydantic-style output models | Zod schemas with auto-retry |
| MCP | Built-in client | Built-in client |
| Hooks | Via provider `stream()` events | First-class lifecycle hooks |
| Multi-agent | Multi-agent systems | Graph, Swarm |
| WASM interop | `strands-py-wasm` host | Compiled to `strands-agent.wasm` |

Source: [README.md](https://github.com/strands-agents/harness-sdk/blob/main/README.md), [strands-py/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/README.md), [strands-ts/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/README.md)

---

## Common Failure Modes & Gotchas

The following items are not bugs, but are common points of confusion when first working with the agent loop:

1. **Default model expects Bedrock credentials.** The Python quick start uses Bedrock + Claude 4 Sonnet in `us-west-2`. Running it on a machine without AWS credentials or model access enabled will fail before the agent loop even starts. Source: [strands-py/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/README.md)

2. **Writer provider requires `model_id`.** Forgetting the `model_id` keyword argument raises a `KeyError` from the Writer provider's `format_request`. Source: [strands-py/src/strands/models/writer.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py)

3. **OpenAI Responses token counting can fall back to estimation.** The native count is opt-in (`use_native_token_count=True`). When unset or when the API errors, the provider falls back to an estimation routine. Source: [strands-py/src/strands/models/openai_responses.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai_responses.py)

4. **Mistral `tool_choice` is currently a no-op.** The provider accepts `tool_choice` for interface consistency but warns and ignores it. Source: [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/mistral.py)

5. **The browser agent example is not production-safe.** It executes LLM-generated HTML, CSS, and JavaScript in a sandboxed iframe. The README explicitly warns it should not be used in production. Source: [strands-ts/examples/browser-agent/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/examples/browser-agent/README.md)

6. **Bootstrap your WASM dev environment with `npm run dev -- bootstrap`.** If this command doesn't enable development out of the box, the WASM README asks you to file an issue. Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)

7. **WASM tool calls round-trip through Python.** If you call the WASM agent and it dispatches a tool, the call crosses the WASM boundary back to the Python host. Latency-sensitive workloads should be aware of the boundary cost. Source: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)

---

## Community & Roadmap Signals

The community context surfaces several recurring themes that intersect directly with the agent loop, tools, and hooks:

- **Cross-language SDK parity** (issues #156, #240, #616): users are asking for TypeScript, Go, and Java SDKs. The TypeScript SDK already exists in this monorepo, and it is also the engine that powers the WASM bindings, so Python users effectively already get a TS-quality agent loop through `strands-py-wasm`. Source: [README.md](https://github.com/strands-agents/harness-sdk/blob/main/README.md), [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)

- **Skills support** (issue #1181): the community wants a way to load relevant knowledge files depending on the task, to use the context window more efficiently. This is a hooks/tools concern: it requires a tool that can list, load, and inject skill content, plus hook integration to fire at the right lifecycle moment. Source: community issue #1181 in the conversation context.

- **AG-UI protocol** (issue #140): a proposed standard for connecting agents to front-end applications. The proposal maps Strands events to AG-UI, which means adopting the existing `StreamEvent` vocabulary (including `ToolUseInputDelta`, `ReasoningContentDelta`, `Usage`) is the natural foundation. Source: community issue #140 in the conversation context.

- **Design proposals live under `designs/`** and follow an RFC-style template. The team has not yet accepted any designs, so significant agent-loop changes (such as Skills) would land there first. Source: [designs/README.md](https://github.com/strands-agents/harness-sdk/blob/main/designs/README.md)

---

## See Also

- [Agent Loop concept guide (strandsagents.com)](https://strandsagents.com/docs/user-guide/concepts/agents/agent-loop/) — official narrative walkthrough
- [Examples gallery (strandsagents.com)](https://strandsagents.com/docs/examples/) — runnable patterns
- [Python API reference (strandsagents.com)](https://strandsagents.com/docs/api/python/strands.agent.agent/)
- [Production & Deployment Guide (strandsagents.com)](https://strandsagents.com/docs/user-guide/deploy/operating-agents-in-production/)
- [team/TENETS.md](https://github.com/strands-agents/harness-sdk/blob/main/team/TENETS.md) — guiding principles for SDK design
- [team/DECISIONS.md](https://github.com/strands-agents/harness-sdk/blob/main/team/DECISIONS.md) — past design decisions
- [team/API_BAR_RAISING.md](https://github.com/strands-agents/harness-sdk/blob/main/team/API_BAR_RAISING.md) — process for API changes

---

<a id='page-3'></a>

## Model Providers & Multi-Agent Patterns

### Related Pages

Related topics: [Agent Loop, Tools & Hooks](#page-2), [Plugins, Bidirectional Streaming & WASM Interop](#page-4)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [strands-py/src/strands/models/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/__init__.py)
- [strands-py/src/strands/models/bedrock.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/bedrock.py)
- [strands-py/src/strands/models/anthropic.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/anthropic.py)
- [strands-py/src/strands/models/gemini.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/gemini.py)
- [strands-py/src/strands/models/openai.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai.py)
- [strands-py/src/strands/models/openai_responses.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/openai_responses.py)
- [strands-py/src/strands/models/ollama.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/ollama.py)
- [strands-py/src/strands/models/litellm.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/litellm.py)
- [strands-py/src/strands/models/llamaapi.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/llamaapi.py)
- [strands-py/src/strands/models/llamacpp.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/llamacpp.py)
- [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/mistral.py)
- [strands-py/src/strands/models/writer.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py)
- [strands-py/src/strands/models/sagemaker.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/sagemaker.py)
- [strands-ts/src/models/model.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/model.ts)
- [strands-ts/src/models/streaming.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/streaming.ts)
- [strands-py-wasm/src/strands/types.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/types.py)
- [strands-py-wasm/src/strands/_generated/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/_generated/__init__.py)
- [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)
- [strands-py/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/README.md)
- [strands-ts/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/README.md)
- [designs/README.md](https://github.com/strands-agents/harness-sdk/blob/main/designs/README.md)
- [team/README.md](https://github.com/strands-agents/harness-sdk/blob/main/team/README.md)
</details>

# Model Providers & Multi-Agent Patterns

## Overview

Strands Agents is a model-driven SDK for building and running AI agents. Its two most consequential architectural commitments are: (1) a model-agnostic provider layer that lets the same agent loop drive Bedrock, Anthropic, OpenAI, Gemini, Mistral, Writer, LiteLLM, LlamaAPI, LlamaCpp, Ollama, and SageMaker, and (2) a multi-agent orchestration layer that composes those agents into Graph and Swarm topologies. This page documents both layers, the streaming event contract that ties them together, and the multi-language surface (Python, TypeScript, and the WASM bridge) that exposes them.

The provider layer is declared in `strands-py/src/strands/models/__init__.py` as a package of "an abstract base Model class along with concrete implementations for specific providers," and it uses lazy import via `__getattr__` so optional provider SDKs are only loaded when actually requested. The TypeScript counterpart is declared in `strands-ts/src/models/model.ts` through the `BaseModelConfig` interface and the `StreamOptions` shape, ensuring provider parity across languages.

The multi-agent capabilities (Graph and Swarm) are advertised as "Advanced Capabilities" in the top-level README and as "Multi-Agent Orchestration" in the TypeScript README, and are exposed across the Python, TypeScript, and WASM boundaries through the type set in `strands-py-wasm/src/strands/_generated/__init__.py` (e.g. `SwarmConfig`).

## The Model-Provider Layer

### Provider Package Layout

`strands-py/src/strands/models/__init__.py` lists the model module's public surface and implements lazy loading for every non-default provider:

```python
# strands-py/src/strands/models/__init__.py
from . import bedrock, model
from .bedrock import BedrockModel
from .model import BaseModelConfig, CacheConfig, CacheToolsConfig, Model

__all__ = [
    "bedrock",
    "model",
    "BaseModelConfig",
    "BedrockModel",
    "CacheConfig",
    "CacheToolsConfig",
    "Model",
]

def __getattr__(name: str) -> Any:
    if name == "AnthropicModel":
        from .anthropic import AnthropicModel
        return AnthropicModel
    # ... GeminiModel, LiteLLMModel, LlamaAPIModel, LlamaCppModel,
    #     MistralModel, OllamaModel, OpenAIModel, ... lazily loaded
```

Source: [strands-py/src/strands/models/__init__.py]()

This pattern is important operationally: a project that only needs Bedrock never imports the Anthropic, Gemini, or Mistral SDKs, keeping cold-start times and dependency graphs lean. Each provider module follows a uniform contract (`format_request`, `format_chunk`, `stream`, `_handle_non_streaming_response`) so the agent loop can be provider-blind.

### Provider Inventory

The table below summarizes the providers shipped in the Python SDK, the module that implements them, and notable configuration behaviors visible in the source.

| Provider | Module | Stream Toggle | Tool Choice | Notable Behavior |
|----------|--------|---------------|-------------|------------------|
| Amazon Bedrock | `strands-py/src/strands/models/bedrock.py` | `config["stream"]` (default `True`) | Honored | Default provider, requires `model_id` in `us-west-2` per `strands-py/README.md` |
| Anthropic | `strands-py/src/strands/models/anthropic.py` | Honored | Honored | Direct Anthropic API |
| OpenAI (Chat Completions) | `strands-py/src/strands/models/openai.py` | Honored | Honored | `_format_system_messages` and `_format_regular_messages` produce OpenAI-compatible arrays |
| OpenAI (Responses) | `strands-py/src/strands/models/openai_responses.py` | Honored | Honored | Separate module for the new Responses API |
| Gemini | `strands-py/src/strands/models/gemini.py` | Honored | Honored | Google GenAI |
| Mistral | `strands-py/src/strands/models/mistral.py` | `config.get("stream", True)` | Ignored with `warn_on_tool_choice_not_supported` | Raises `ModelThrottledException` on rate limits |
| Writer | `strands-py/src/strands/models/writer.py` | Always streaming in `_stream` | Ignored with warning | Renames `model_id` → `model`; raises `KeyError` if not set; `tools` key omitted when empty |
| LiteLLM | `strands-py/src/strands/models/litellm.py` | Honored | Honored | Routes through LiteLLM to 100+ providers |
| LlamaAPI | `strands-py/src/strands/models/llamaapi.py` | Honored | Honored | Hosted Llama API |
| LlamaCpp | `strands-py/src/strands/models/llamacpp.py` | Honored | Honored | Local in-process inference |
| Ollama | `strands-py/src/strands/models/ollama.py` | Honored | Honored | Local Ollama daemon |
| SageMaker | `strands-py/src/strands/models/sagemaker.py` | Honored | Honored | Self-hosted SageMaker endpoints |

Source: [strands-py/src/strands/models/mistral.py](), [strands-py/src/strands/models/writer.py](), [strands-py/src/strands/models/openai.py]()

### Common Configuration Surface

`strands-py/src/strands/models/model.py` defines `BaseModelConfig`, which every provider extends. The TypeScript equivalent lives in `strands-ts/src/models/model.ts` as the `BaseModelConfig` interface:

```typescript
// strands-ts/src/models/model.ts
export interface BaseModelConfig {
  modelId?: string
  maxTokens?: number
  temperature?: number
  topP?: number
  contextWindowLimit?: number
}
```

Source: [strands-ts/src/models/model.ts]()

`contextWindowLimit` has special semantics in the TypeScript layer: when not provided, it is resolved from a built-in lookup table keyed by `modelId`, and is re-resolved automatically after `updateConfig({ modelId })` if the value was initially auto-populated. This auto-resolution behavior is implemented to keep cache- and truncation-related code free of provider-specific branching.

### Streaming Event Contract

Every provider funnels into the same chunk vocabulary. `strands-ts/src/models/streaming.ts` documents the wire-level types that both the Python and TypeScript layers must emit:

| Chunk Type | Purpose |
|------------|---------|
| `messageStart` | Begins an assistant turn; sets `role: "assistant"` |
| `contentBlockStart` | Opens a content block (text or tool use) |
| `contentBlockDelta` | Incremental text or tool input |
| `contentBlockStop` | Closes a content block |
| `messageStop` | Ends an assistant turn |
| `toolUseStart` / `toolUseInputDelta` / `toolUseStop` | Tool-use lifecycle |
| `reasoningContentDelta` | Reasoning / thinking tokens (text, signature, redacted) |
| `citationsDelta` | Citations linking generated content to source locations |
| `usage` | Input/output/cache token accounting |

Source: [strands-ts/src/models/streaming.ts]()

The Python providers emit matching chunk types via `format_chunk`. For example, the Mistral provider emits `{"chunk_type": "message_start"}` and then translates streamed choices into `content_block_delta` events, accumulating text in `accumulated_text` and tool calls in a `dict[str, list[Any]]` keyed by tool id. Source: [strands-py/src/strands/models/mistral.py]()

The Writer provider follows the same shape: `format_chunk({"chunk_type": "message_start"})` then `format_chunk({"chunk_type": "content_block_start", "data_type": "text"})` followed by per-token `content_block_delta` events for `choice.delta.content` and tool-call deltas for `choice.delta.tool_calls`. Source: [strands-py/src/strands/models/writer.py]()

### Provider Streaming Flow

```mermaid
graph TD
    A[Agent Loop] --> B[Model.stream]
    B --> C[format_request]
    C --> D{stream flag}
    D -->|True| E[Provider stream API]
    D -->|False| F[Non-streaming API + _handle_non_streaming_response]
    E --> G[format_chunk per event]
    F --> G
    G --> H[Standardized StreamEvent]
    H --> I[Agent consumes via async iterator]
    I --> J[Tool dispatch, if toolUse present]
```

Source: [strands-py/src/strands/models/mistral.py](), [strands-py/src/strands/models/writer.py]()

### Provider Quirks and Failure Modes

Two cross-provider quirks are worth flagging because they trip up new users:

1. **`tool_choice` is silently ignored on some providers.** Mistral and Writer accept the parameter for interface consistency but pass it through `warn_on_tool_choice_not_supported(tool_choice)` and discard the value. Source: [strands-py/src/strands/models/mistral.py](), [strands-py/src/strands/models/writer.py]()

2. **Throttling is normalized to `ModelThrottledException`.** Both Mistral and Writer translate provider-specific rate-limit errors (`mistralai.RateLimitError`, `writerai.RateLimitError`) into a single SDK exception so retry/circuit-breaker code in the agent loop can be provider-agnostic. Source: [strands-py/src/strands/models/mistral.py](), [strands-py/src/strands/models/writer.py]()

3. **Empty `tools` lists are dropped.** Writer raises a `KeyError` if `model_id` is not set, and explicitly omits the `tools` key from the request when `tool_specs` is empty, because the Writer API rejects empty tools arrays. Source: [strands-py/src/strands/models/writer.py]()

## TypeScript Provider Parity

The TypeScript SDK follows the same model-agnostic philosophy. The default `BedrockModel` is the canonical first-class provider, with OpenAI listed as supported and an extension point for custom providers (advertised in `strands-ts/README.md`):

```typescript
import { Agent, BedrockModel } from '@strands-agents/sdk'

const agent = new Agent({
  model: new BedrockModel({ region: 'us-east-1' }),
  tools: [notebook],
})
```

Source: [strands-ts/README.md](), [strands-ts/src/vended-tools/notebook/README.md]()

The TypeScript `BaseModelConfig` includes an explicit `contextWindowLimit` field whose default behavior (auto-resolved from a lookup table, re-resolved on `modelId` change unless explicitly set) is documented in the JSDoc and designed to let conversation managers pick a strategy without per-provider branching. Source: [strands-ts/src/models/model.ts]()

## Multi-Agent Patterns

### Pattern Catalog

The Strands SDK ships two multi-agent orchestration patterns, both surfaced across Python, TypeScript, and the WASM surface:

| Pattern | Best For | Surface Name |
|---------|----------|--------------|
| **Graph** | Explicit, deterministic routing between agents with typed edges | Exposed via Python, TypeScript, and `strands-py-wasm` |
| **Swarm** | Autonomous, emergent delegation between peer agents | Exposed as `SwarmConfig` in the WASM type set |

Source: [strands-py/README.md](), [strands-ts/README.md](), [strands-py-wasm/src/strands/_generated/__init__.py]()

The `SwarmConfig` symbol appears in the WASM public types generated for the Python↔TypeScript bridge, indicating that Swarm is treated as a first-class orchestration primitive that must round-trip through the WIT contract. Source: [strands-py-wasm/src/strands/_generated/__init__.py]()

### How the Multi-Agent Loop Reuses the Provider Layer

Every agent in a Graph or Swarm is itself a Strands `Agent`, so it can target any provider in the table above. This means a single multi-agent system can mix providers — for example, a routing agent on Bedrock, a research agent on OpenAI, and a code-generation agent on a local LlamaCpp instance — without any change to the orchestration code. The provider-agnosticism of the `Model` base class is what makes that composition possible. Source: [strands-py/src/strands/models/__init__.py]()

```mermaid
graph TD
    User[User Request] --> Router{Router Agent}
    Router -->|research| Research[Research Agent<br/>OpenAI]
    Router -->|code| Code[Code Agent<br/>LlamaCpp local]
    Router -->|summarize| Summary[Summary Agent<br/>Bedrock]
    Research --> Synth[Synthesizer Agent<br/>Bedrock]
    Code --> Synth
    Summary --> Synth
    Synth --> User
```

The diagram is a representative Graph topology; concrete edge conditions are authored by the application, not the SDK. Source: [strands-py/README.md](), [strands-ts/README.md]()

### Tool and Plugin Reuse in Multi-Agent Setups

Multi-agent setups benefit from the same plugin/tool system as single agents. Vended tools (`BashTool`, `FileEditorTool`, `HttpRequestTool`, `NotebookTool`) and vended plugins (`AgentSkills`, `ContextOffloader`) are re-exported by the WASM surface via type unions in `strands-py-wasm/src/strands/types.py`:

```python
VendedToolInput = VendedTool | BashTool | FileEditorTool | HttpRequestTool | NotebookTool
VendedPluginInput = VendedPlugin | AgentSkills | ContextOffloader
```

Source: [strands-py-wasm/src/strands/types.py]()

A community request, [FEATURE] Add support for skills (#1181), asks for first-class `Agent Skills` support to load relevant knowledge files into context per task. The `AgentSkills` plugin already exists in the WASM type surface, so the request maps onto plugin wiring rather than a new primitive. Source: [strands-py-wasm/src/strands/types.py]()

## Streaming and Conversation Management

Both single-agent and multi-agent invocations share the same streaming event contract documented in `strands-ts/src/models/streaming.ts`. Three event categories matter most for multi-agent systems:

1. **Reasoning deltas** — `reasoningContentDelta` carries incremental text, signature, and redacted-reasoning bytes. This is the type multi-agent routers surface to expose an agent's intermediate deliberation to the parent.
2. **Tool-use deltas** — `toolUseInputDelta` streams partial JSON for tool inputs, which is critical when an agent invokes a long-running tool that another agent should be able to observe.
3. **Usage deltas** — `Usage` reports `inputTokens`, `outputTokens`, `totalTokens`, plus cache-related metrics, enabling fair cost accounting when one parent agent orchestrates many children.

Source: [strands-ts/src/models/streaming.ts]()

On the Python side, conversation managers are exposed as the union `SlidingWindowConversationManager | SummarizingConversationManager`, and the model-agnostic input is `ModelConfig | BedrockModel | AnthropicModel | OpenaiModel | GoogleModel | CustomModel`. These unions make the orchestration surface fully provider-agnostic. Source: [strands-py-wasm/src/strands/types.py]()

## The WASM Bridge and Cross-Language Providers

The Python WASM build (`strands-py-wasm`) is a WebAssembly packaging of the TypeScript SDK. The contract is defined by `wit/agent.wit` and the type set in `strands-py-wasm/src/strands/_generated/__init__.py`:

- **Exports** (TypeScript implements, Python calls): the `api` interface, including agent construction, streaming, and conversation management. All model provider HTTP calls — Bedrock, Anthropic, OpenAI, Gemini — happen inside the WASM guest.
- **Imports** (Python implements, TypeScript calls back into): `tool-provider` for executing Python-defined tools, and `host-log` for routing log entries to Python's logging framework.

Source: [strands-wasm/README.md]()

This means that when running `strands-py-wasm`, the model-provider HTTP calls are made by the TypeScript code compiled into `strands-agent.wasm`, and Python only sees streamed events through the `api` export. For a multi-agent Python application that imports the WASM package, every child agent's provider is effectively a TypeScript-backed provider, but the Python orchestration code is unaware of that boundary. Source: [strands-wasm/README.md]()

The latest release of `strands-agents-wasm` is `v0.0.1` (initial release), per the community context.

## Community-Driven Direction

Several community issues are directly relevant to the provider and multi-agent story:

- **#156 Add TypeScript/JavaScript/NodeJS SDK** — Resolved by the existence of the `strands-ts` package and the `strands-py-wasm` bridge, both of which expose the same provider and multi-agent surfaces.
- **#616 Go SDK** — Not yet on the roadmap. The WASM bridge demonstrates that the provider contract is portable enough to retarget, so a future Go SDK is plausible but unannounced.
- **#240 Java SDK** — Same status as Go; no announced work, but the type set in `strands-py-wasm/src/strands/_generated/__init__.py` is a candidate seed for cross-language codegen.
- **#1181 Agent Skills** — The `AgentSkills` plugin already appears in the WASM type surface; further work is integration rather than core infrastructure.
- **#140 AG-UI protocol** — A proposal to map Strands stream events to the AG-UI protocol. The standardized event vocabulary in `strands-ts/src/models/streaming.ts` (and mirrored by Python `format_chunk`) is the natural mapping source.

Source: [strands-py-wasm/src/strands/_generated/__init__.py](), [strands-ts/src/models/streaming.ts]()

## Design and Contribution Workflow

Significant changes to the provider or multi-agent layers are expected to follow the design process described in `designs/README.md`. The repository uses an RFC-style template with `Status`, `Context`, `Decision`, `Developer Experience`, `Alternatives Considered`, `Consequences`, and `Willingness to Implement` sections. As of the most recent README snapshot, no designs have been accepted yet, so all multi-agent and provider work is currently shaped in code rather than in merged design documents. Source: [designs/README.md]()

The `team/` directory houses internal guidance: `TENETS.md` (core SDK principles), `DECISIONS.md` (rationale records), `API_BAR_RAISING.md` (the API review process), and `AGENT_GUIDELINES.md` (conventions for AI agents operating on the repo). Source: [team/README.md]()

## See Also

- Quickstart and overview: `strands-py/README.md`, `strands-ts/README.md`
- Provider internals: `strands-py/src/strands/models/*.py`, `strands-ts/src/models/model.ts`
- Streaming contract: `strands-ts/src/models/streaming.ts`
- WASM type surface: `strands-py-wasm/src/strands/_generated/__init__.py`, `strands-py-wasm/src/strands/types.py`
- WASM architecture: `strands-wasm/README.md`
- Design process: `designs/README.md`, `team/README.md`
- Vended tools used by multi-agent setups: `strands-ts/src/vended-tools/notebook/README.md`

---

<a id='page-4'></a>

## Plugins, Bidirectional Streaming & WASM Interop

### Related Pages

Related topics: [Monorepo Architecture & SDKs Overview](#page-1), [Model Providers & Multi-Agent Patterns](#page-3)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [strands-py/src/strands/vended_plugins/skills/agent_skills.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/vended_plugins/skills/agent_skills.py)
- [strands-py/src/strands/vended_plugins/skills/skill.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/vended_plugins/skills/skill.py)
- [strands-py/src/strands/vended_plugins/context_offloader/plugin.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/vended_plugins/context_offloader/plugin.py)
- [strands-py/src/strands/vended_plugins/context_offloader/storage.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/vended_plugins/context_offloader/storage.py)
- [strands-py/src/strands/vended_plugins/steering/core/handler.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/vended_plugins/steering/core/handler.py)
- [strands-py/src/strands/experimental/bidi/agent/agent.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/experimental/bidi/agent/agent.py)
- [strands-py/src/strands/experimental/bidi/models/nova_sonic.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/experimental/bidi/models/nova_sonic.py)
- [strands-py/src/strands/experimental/bidi/models/gemini_live.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/experimental/bidi/models/gemini_live.py)
- [strands-py/src/strands/experimental/bidi/models/openai_realtime.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/experimental/bidi/models/openai_realtime.py)
- [strands-py/src/strands/interrupt.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/interrupt.py)
- [strands-py/src/strands/plugins/plugin.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/plugins/plugin.py)
- [strands-py/src/strands/plugins/registry.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/plugins/registry.py)
</details>

# Plugins, Bidirectional Streaming & WASM Interop

The Strands Agents SDK exposes three closely related extensibility surfaces:

1. A **vended plugin system** (Skills, Context Offloader, Steering) that wraps reusable agent behaviors into a single lifecycle API.
2. A **bidirectional streaming (bidi) subsystem** under `strands.experimental.bidi` for realtime, multimodal model sessions.
3. A **WebAssembly (WASM) interop layer** (`strands-wasm`, `strands-py-wasm`, `strands-ts`) that compiles the TypeScript runtime into a WASM component embedded inside the Python host.

This page describes the public shape of those subsystems, how they fit together, and how the WIT contract defines the boundary between the WASM guest and the Python host.

---

## 1. High-Level Architecture

The SDK is structured as a multi-language monorepo where the TypeScript runtime can be executed in two ways: standalone (Node.js) or compiled to a WASM component that the Python host drives through `wasmtime-py`.

```mermaid
graph TD
    subgraph "Python host (strands-py)"
        Py[Agent / @tool decorator]
        Wrap[strands-py-wasm wrapper]
        ModelsPy[Model providers<br/>Bedrock, Anthropic, OpenAI,<br/>Gemini, Writer, Mistral, Ollama]
        Bidi[experimental.bidi<br/>nova_sonic, gemini_live,<br/>openai_realtime]
        Plugins[vended_plugins<br/>Skills, ContextOffloader, Steering]
    end

    subgraph "WASM boundary (wit/agent.wit)"
        WIT[(WIT contract)]
    end

    subgraph "WASM guest (strands-ts compiled)"
        Loop[Agent event loop]
        ModelsTs[Model providers<br/>Vercel AI SDK v3]
        Hooks[Hooks / streaming]
    end

    Py --> Wrap
    Wrap -->|loads via wasmtime-py| WIT
    WIT -->|exports api| Loop
    Loop --> ModelsTs
    Loop -->|imports tool-provider| WIT
    WIT -->|dispatches to Python @tool| Py
    Loop -->|imports host-log| WIT
    WIT -->|routes to Python logging| Py

    Py -.direct path.-> ModelsPy
    Py -.experimental.-> Bidi
    Py -.vended plugins.-> Plugins
```

The diagram above reflects the architecture described in [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md). The Python path that goes directly to `ModelsPy` is the classical Strands Agents path; the path through the WASM guest is the new `strands-py-wasm` runtime.

---

## 2. The WASM Interop Layer

### 2.1 Build and Runtime Topology

The TypeScript SDK is compiled into a single WebAssembly component named `strands-agent.wasm`. Python loads this component via `wasmtime-py` and drives it. Source: [strands-wasm/README.md:9-21](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)

The WIT contract at `wit/agent.wit` defines the boundary. The WASM component is the "guest" and Python is the "host":

| Direction            | WIT side          | Implementer | Purpose                                                                 |
|----------------------|-------------------|-------------|-------------------------------------------------------------------------|
| Export `api`         | `wit/agent.wit`   | TypeScript  | Agent construction, streaming, conversation management                  |
| Import `tool-provider` | `wit/agent.wit`  | Python      | Execute Python-defined `@tool` functions called by the TS agent loop    |
| Import `host-log`    | `wit/agent.wit`   | Python      | Route log entries produced inside WASM back to the Python logger        |

All model provider HTTP calls (Bedrock, Anthropic, OpenAI, Gemini) happen inside the WASM guest when running on the WASM path. When the TS agent loop decides a tool must run, it crosses the boundary back to Python where the actual `@tool` function lives. Source: [strands-wasm/README.md:15-21](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)

### 2.2 Monorepo Layout

The monorepo is split into layers, each with a single language and a single concern. Source: [strands-wasm/README.md:33-50](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)

| Package             | Language   | Role                                                                  |
|---------------------|------------|-----------------------------------------------------------------------|
| `wit/`              | WIT        | Interface contract between the WASM guest and host                    |
| `strands-ts/`       | TypeScript | Agent runtime: event loop, model providers, tools, hooks, streaming   |
| `strands-wasm/`     | TypeScript | Bridges the TS SDK to WIT exports, compiles to a WASM component        |
| `strands-py-wasm/`  | Python     | Python wrapper: `Agent` class, `@tool` decorator, direct WASM host    |
| `strandly/`         | TypeScript | Dev CLI that orchestrates build, test, lint, and CI                   |
| `dev-docs/`         | Markdown   | Design proposals and team decisions                                   |

### 2.3 Generated Bindings

`strandly generate` produces type bindings from `wit/agent.wit` into three locations:

- `strands-ts/generated/` (gitignored)
- `strands-wasm/generated/` (gitignored)
- `strands-py-wasm/src/strands/_generated/` (committed)

Generated files must never be edited by hand. CI runs `strandly generate --check` and fails the build if they are stale. Source: [strands-wasm/README.md:54-60](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)

The exported Python symbols include the vended plugin and tool surface — for example `VendedPlugin`, `VendedTool`, `AgentSkills`, `ContextOffloader`, `BashTool`, `FileEditorTool`, `HttpRequestTool`, `NotebookTool`, plus all of the conversation managers, model configs, and streaming primitives. Source: [strands-py-wasm/src/strands/_generated/__init__.py:1-180](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/_generated/__init__.py)

A small set of WIT-level symbols is intentionally dropped at the Python public surface because they have no meaningful Python equivalent (such as the `Pollable` future type and the `InputStream`/`OutputStream` handles). Source: [strands-py-wasm/src/strands/types.py:1-10](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/types.py)

```python
_DROPPED: set[str] = {
    "Datetime",
    "Duration",
    "Error",
    "InputStream",
    "Instant",
    "OutputStream",
    "Pollable",
}
```

### 2.4 Bootstrap and Test Layers

| Layer          | Framework | Location                                                          |
|----------------|-----------|-------------------------------------------------------------------|
| TypeScript SDK | vitest    | `strands-ts/src/**/__tests__/` (unit), `strands-ts/test/` (integ) |
| Python wrapper | pytest    | `strands-py-wasm/tests/`                                          |

A first-time setup runs `npm run dev -- bootstrap`, which installs toolchains, links `strandly` to the user's `PATH`, generates type bindings, builds all layers, installs `strands-py-wasm`, and runs every test. Source: [strands-wasm/README.md:29-40](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)

---

## 3. The Plugin System

### 3.1 Two Plugin Surfaces

The repository distinguishes two surfaces:

| Surface     | Import path                                      | Audience                       | Examples                                          |
|-------------|--------------------------------------------------|--------------------------------|---------------------------------------------------|
| Core plugins| `strands.plugins`                                | SDK authors and integrators    | Custom agent lifecycle hooks                      |
| Vended plugins | `strands.vended_plugins`                      | End users                      | `AgentSkills`, `ContextOffloader`, Steering       |

Vended plugins are pre-built, supported plugins shipped with the SDK. They appear in the generated Python surface as `VendedPlugin`, `AgentSkills`, and `ContextOffloader`. Source: [strands-py-wasm/src/strands/_generated/__init__.py:1-180](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/_generated/__init__.py)

The Python public type union re-exports the vended plugin types so that `VendedPluginInput` accepts either a raw `VendedPlugin` or one of the concrete vended implementations (`AgentSkills`, `ContextOffloader`). Source: [strands-py-wasm/src/strands/types.py:15-22](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/types.py)

```python
VendedToolInput = VendedTool | BashTool | FileEditorTool | HttpRequestTool | NotebookTool
VendedPluginInput = VendedPlugin | AgentSkills | ContextOffloader
```

### 3.2 Plugin Lifecycle

A plugin is a stateful object that participates in the agent event loop. The registry at `strands.plugins.registry` provides discovery and lookup, while `strands.plugins.plugin` defines the base protocol.

Common responsibilities implemented by the vended plugins include:

- Loading skill manifests on demand (the `AgentSkills` plugin in `strands/vended_plugins/skills/agent_skills.py`).
- Managing skill definitions and metadata (the `Skill` dataclass in `strands/vended_plugins/skills/skill.py`).
- Offloading long context to durable storage (the `ContextOffloader` plugin in `strands/vended_plugins/context_offloader/plugin.py`).
- Persisting and retrieving offloaded context (the storage backends in `strands/vended_plugins/context_offloader/storage.py`).
- Steering agent behavior mid-conversation (the `Steering` core handler in `strands/vended_plugins/steering/core/handler.py`).

The Skills plugin in particular addresses a recurring community request: loading relevant knowledge files for the active task so that the model can use the context window more efficiently. Source: [community issue #1181](https://github.com/strands-agents/harness-sdk/issues/1181)

### 3.3 Bidirectional Interrupts

`strands.interrupt` provides the cooperative cancellation / interrupt primitive that plugins (and user code) call to ask the agent loop to stop cleanly. The vended plugins and the bidi subsystem both rely on it to coordinate shutdown.

---

## 4. Bidirectional Streaming (Bidi)

### 4.1 Scope

`strands.experimental.bidi` is the realtime, multimodal model session API. It targets providers that expose persistent duplex audio/video/text streams rather than the request/response streaming of `strands.models`.

The current experimental surface is implemented per provider:

| Provider module                      | Model family                          | Source                                                                                              |
|--------------------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------|
| `strands.experimental.bidi.models.nova_sonic`  | Amazon Nova Sonic         | [strands-py/src/strands/experimental/bidi/models/nova_sonic.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/experimental/bidi/models/nova_sonic.py)     |
| `strands.experimental.bidi.models.gemini_live` | Google Gemini Live        | [strands-py/src/strands/experimental/bidi/models/gemini_live.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/experimental/bidi/models/gemini_live.py)   |
| `strands.experimental.bidi.models.openai_realtime` | OpenAI Realtime API  | [strands-py/src/strands/experimental/bidi/models/openai_realtime.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/experimental/bidi/models/openai_realtime.py) |

A single `BidiAgent` facade in `strands.experimental.bidi.agent.agent` is the user-facing entry point and dispatches to the right provider.

### 4.2 Relationship to the Standard Streaming Pipeline

The standard Strands streaming pipeline yields typed events such as `messageStart`, `contentBlockStart`, `contentBlockDelta`, `contentBlockStop`, and `messageStop`, with separate delta shapes for text, tool use input, reasoning, and citations. Source: [strands-ts/src/models/streaming.ts:1-90](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/streaming.ts)

```ts
export interface ToolUseInputDelta {
  type: 'toolUseInputDelta'
  input: string
}

export interface ReasoningContentDelta {
  type: 'reasoningContentDelta'
  text?: string
  signature?: string
  redactedContent?: Uint8Array
}

export interface CitationsDelta {
  type: 'citationsDelta'
  citations: Citation[]
  content: CitationGeneratedContent[]
}
```

The bidi subsystem reuses the same `Usage` and `Metrics` shapes but extends the event stream with realtime audio/video frames. Usage is accumulated across chunks by `accumulateUsage(target, source)`, which mutates `target` in place and folds the optional cache read/write token fields when they are present. Source: [strands-ts/src/models/streaming.ts:90-130](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/streaming.ts)

### 4.3 Experimental Status

The `experimental.bidi` namespace signals that the API may change. The community has asked for standardized agent-front-end protocols such as AG-UI that would benefit from a stable bidi surface. Source: [community issue #140 (AG-UI)](https://github.com/strands-agents/harness-sdk/issues/140)

---

## 5. Streaming Primitives and Model Provider Integration

### 5.1 Base Model Configuration

All model providers share a base configuration with a model ID, generation limits, sampling controls, and a context window size. Source: [strands-ts/src/models/model.ts:1-50](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/model.ts)

```ts
export interface BaseModelConfig {
  modelId?: string
  maxTokens?: number
  temperature?: number
  topP?: number
  contextWindowLimit?: number
}
```

`contextWindowLimit` is auto-resolved from a built-in lookup table based on `modelId` when not set, and is automatically re-resolved if `modelId` is changed via `updateConfig()` — as long as it was initially auto-populated. Explicit values are preserved.

### 5.2 Provider Streaming Pattern

Each Python provider follows the same shape: a synchronous `format_request()` that produces a provider-native request body, followed by an async `stream()` (or `chat()`) generator that yields standardized `StreamEvent` chunks.

The Writer provider formats a streaming request like this, then maps the provider's choice/delta stream back to the standard event vocabulary and raises `ModelThrottledException` on `writerai.RateLimitError`. Source: [strands-py/src/strands/models/writer.py:1-80](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py)

```python
request = {
    **{k: v for k, v in self.config.items()},
    "messages": self._format_request_messages(messages, system_prompt),
    "stream": True,
}
request["model"] = request.pop("model_id")  # Writer uses 'model' not 'model_id'
```

The Anthropic provider emits native Anthropic request payloads (with `max_tokens`, `model`, `tools[*].input_schema`, and a `tool_choice` mapped from the SDK's `ToolChoice` union). Source: [strands-py/src/strands/models/anthropic.py:1-50](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/anthropic.py)

```python
return {
    "max_tokens": self.config["max_tokens"],
    "messages": self._format_request_messages(messages),
    "model": self.config["model_id"],
    "tools": [
        {
            "name": tool_spec["name"],
            "description": tool_spec["description"],
            "input_schema": tool_spec["inputSchema"]["json"],
        }
        for tool_spec in tool_specs or []
    ],
    **(self._format_tool_choice(tool_choice)),
    **({"system": system_prompt} if system_prompt else {}),
    **(self.config.get("params") or {}),
}
```

Mistral supports both streaming and non-streaming modes; the non-streaming branch is selected by `config["stream"] == False` and uses `client.chat.complete_async`. Source: [strands-py/src/strands/models/mistral.py:1-50](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/mistral.py)

Ollama uses `ollama.AsyncClient` and emits `content_start`, `content_delta`, and `content_stop` chunks with `data_type="tool"` whenever `event.message.tool_calls` is present. Source: [strands-py/src/strands/models/ollama.py:1-50](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/ollama.py)

### 5.3 Vercel AI SDK v3 Adapter

The TypeScript SDK includes a Vercel provider that adapts the SDK's `Model` and `StreamOptions` interfaces to the Vercel AI SDK v3 `LanguageModelV3` contract. This makes any Vercel v3-compatible model provider (Anthropic, OpenAI, Google, etc. via the Vercel ecosystem) usable from the TS runtime, and therefore from the WASM guest. Source: [strands-ts/src/models/vercel.ts:1-40](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/vercel.ts)

The community has expressed interest in a first-class TypeScript / Node.js SDK and a Go SDK, both of which would naturally reuse this adapter. Source: [community issue #156 (Node SDK)](https://github.com/strands-agents/harness-sdk/issues/156), [community issue #616 (Go SDK)](https://github.com/strands-agents/harness-sdk/issues/616)

### 5.4 Tool Choice Handling

When a provider does not support `tool_choice`, the standard `warn_on_tool_choice_not_supported(tool_choice)` helper is invoked to log a warning and proceed. This keeps the per-provider `stream()` signatures uniform across the model layer. Source: [strands-py/src/strands/models/writer.py:60-90](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py)

---

## 6. WASM Data Flow End-to-End

```mermaid
graph TD
    User[User code<br/>agent = Agent]
    PyHost[Python host<br/>strands-py-wasm]
    Guest[strands-agent.wasm<br/>TypeScript compiled]
    TSLoop[TS agent event loop]
    ModelProv[Model provider<br/>inside WASM]
    TP[WIT: tool-provider import]
    HL[WIT: host-log import]
    PyTool[Python @tool function]
    PyLog[Python logging]

    User -->|invoke| PyHost
    PyHost -->|load via wasmtime-py| Guest
    Guest --> TSLoop
    TSLoop -->|HTTP call| ModelProv
    ModelProv -->|stream chunks| TSLoop
    TSLoop -->|needs tool?| TP
    TP -->|call across boundary| PyHost
    PyHost --> PyTool
    PyTool -->|result| PyHost
    PyHost -->|return| TP
    TSLoop -->|logs| HL
    HL --> PyLog
    TSLoop -->|streamed events| PyHost
    PyHost --> User
```

The flow above mirrors the architecture described in [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md). Two boundary crossings are notable: tools (`tool-provider` import) and logs (`host-log` import). Both are Python-defined so that user code never has to be re-implemented in TypeScript to participate in the WASM path.

---

## 7. Configuration and Convention Reference

### 7.1 Generated Symbols Available to Python

The generated `__init__` exposes a broad surface so that Python code can drive every WIT-defined concept. A representative subset relevant to plugins, streaming, and WASM interop:

| Symbol                        | Category              | Notes                                                    |
|-------------------------------|-----------------------|----------------------------------------------------------|
| `VendedPlugin`                | Plugin                | Base type for vended plugins                             |
| `VendedTool`                  | Tool                  | Base type for vended tools                               |
| `AgentSkills`                 | Vended plugin         | Skill-loading plugin                                     |
| `ContextOffloader`            | Vended plugin         | Context offloading plugin                                |
| `BashTool`                    | Vended tool           | Shell execution                                          |
| `FileEditorTool`              | Vended tool           | File editing                                             |
| `HttpRequestTool`             | Vended tool           | HTTP client                                              |
| `NotebookTool`                | Vended tool           | Notebook editing                                         |
| `BedrockModel` / `AnthropicModel` / `OpenaiModel` / `GoogleModel` / `CustomModel` | Model providers | Per-provider model configs |
| `ModelInput`                  | Type alias            | `ModelConfig | <provider>Model`                           |
| `VendedPluginInput`           | Type alias            | `VendedPlugin | AgentSkills | ContextOffloader`            |
| `VendedToolInput`             | Type alias            | `VendedTool | <concrete vended tool>`                   |
| `StreamEvent` / `Message` / `TextBlock` / `ToolUseBlock` / `ToolResultBlock` | Streaming | Standard event vocabulary         |
| `Usage` / `Metrics`           | Streaming             | Token and latency accounting                             |

Source: [strands-py-wasm/src/strands/_generated/__init__.py:1-180](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/_generated/__init__.py), [strands-py-wasm/src/strands/types.py:15-22](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/types.py)

### 7.2 Dropped WIT Symbols

These WIT-level types are intentionally not re-exported to Python because they model asynchronous / handle-based concepts that are not meaningful at the Python surface: `Datetime`, `Duration`, `Error`, `InputStream`, `Instant`, `OutputStream`, `Pollable`. Source: [strands-py-wasm/src/strands/types.py:1-10](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/types.py)

### 7.3 Test Placement

| Layer          | Framework | Location                                                          |
|----------------|-----------|-------------------------------------------------------------------|
| TypeScript SDK | vitest    | `strands-ts/src/**/__tests__/` (unit), `strands-ts/test/` (integ) |
| Python wrapper | pytest    | `strands-py-wasm/tests/`                                          |

Bug fixes should include a test that reproduces the original issue. Source: [strands-wasm/README.md:62-70](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)

---

## 8. Common Failure Modes and Best Practices

### 8.1 Stale Generated Bindings

CI runs `strandly generate --check`. If a developer changes `wit/agent.wit` or the TS sources that drive it without re-running `strandly generate`, the build fails. The fix is to run `npm run dev -- bootstrap` (or `strandly generate` directly). Source: [strands-wasm/README.md:54-60](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)

### 8.2 Model ID vs Model Key

The Writer provider expects the request key `model` while the rest of the SDK uses `model_id`. The provider handles the rename internally but raises `KeyError("Please specify a model ID. Use 'model_id' keyword argument.")` if no `model_id` is provided. Source: [strands-py/src/strands/models/writer.py:15-25](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py)

### 8.3 Unsupported `tool_choice`

For providers that do not honor `tool_choice` (Writer, Mistral, Ollama), `warn_on_tool_choice_not_supported` is called and the parameter is otherwise ignored. Code that depends on forced tool calls should pick a provider that supports it. Source: [strands-py/src/strands/models/writer.py:60-90](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py)

### 8.4 Throttling

Each provider maps its native throttling exception to `ModelThrottledException` so that the agent loop can apply a single retry policy across providers. The Writer provider's path is documented in [strands-py/src/strands/models/writer.py:60-100](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py).

### 8.5 Bidi API Stability

The `experimental.bidi` surface is subject to change. Code targeting it should pin a version and be ready to migrate when providers stabilize. The community is also exploring a separate protocol — AG-UI — to standardize agent-to-UI events, which would likely build on top of the bidi subsystem. Source: [community issue #140 (AG-UI)](https://github.com/strands-agents/harness-sdk/issues/140)

---

## 9. See Also

- Repository root: [README.md](https://github.com/strands-agents/harness-sdk/blob/main/README.md)
- WASM developer guide: [strands-wasm/README.md](https://github.com/strands-agents/harness-sdk/blob/main/strands-wasm/README.md)
- Team process and decisions: [team/README.md](https://github.com/strands-agents/harness-sdk/blob/main/team/README.md)
- Design proposal process: [designs/README.md](https://github.com/strands-agents/harness-sdk/blob/main/designs/README.md)
- Generated Python symbols: [strands-py-wasm/src/strands/_generated/__init__.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py-wasm/src/strands/_generated/__init__.py)
- TypeScript streaming vocabulary: [strands-ts/src/models/streaming.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/streaming.ts)
- TypeScript base model config: [strands-ts/src/models/model.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/model.ts)
- Vercel AI SDK v3 adapter: [strands-ts/src/models/vercel.ts](https://github.com/strands-agents/harness-sdk/blob/main/strands-ts/src/models/vercel.ts)
- Python Anthropic provider: [strands-py/src/strands/models/anthropic.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/anthropic.py)
- Python Writer provider: [strands-py/src/strands/models/writer.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/writer.py)
- Python Mistral provider: [strands-py/src/strands/models/mistral.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/mistral.py)
- Python Ollama provider: [strands-py/src/strands/models/ollama.py](https://github.com/strands-agents/harness-sdk/blob/main/strands-py/src/strands/models/ollama.py)
- Community: Skills support request #1181, AG-UI request #140, Node SDK request #156, Go SDK request #616, Java SDK request #240.

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: strands-agents/harness-sdk

Summary: Found 30 structured pitfall item(s), including 3 high/blocking item(s). Top priority: Installation risk - Installation risk requires verification.

## 1. Installation risk - Installation risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/strands-agents/harness-sdk/issues/2748

## 2. Security or permission risk - Security or permission risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Developers should check this security_permissions risk before relying on the project: [BUG] CacheConfig(strategy="auto") does not detect Claude models when using ARN-based inference profiles
- User impact: Developers may expose sensitive permissions or credentials: [BUG] CacheConfig(strategy="auto") does not detect Claude models when using ARN-based inference profiles
- Evidence: failure_mode_cluster:github_issue | https://github.com/strands-agents/harness-sdk/issues/2601

## 3. Security or permission risk - Security or permission risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/strands-agents/harness-sdk/issues/482

## 4. Identity risk - Identity risk requires verification

- Severity: medium
- Evidence strength: runtime_trace
- Finding: Project evidence flags a identity risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Repro command: `pip install strands-agents`
- Evidence: identity.distribution | https://github.com/strands-agents/harness-sdk

## 5. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: [BUG] Gemini rejects tool results with multiple same-format images: duplicate displayName in function_response.parts (400 INVALID_ARGUMENT)
- User impact: Developers may fail before the first successful local run: [BUG] Gemini rejects tool results with multiple same-format images: duplicate displayName in function_response.parts (400 INVALID_ARGUMENT)
- Evidence: failure_mode_cluster:github_issue | https://github.com/strands-agents/harness-sdk/issues/2748

## 6. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: [BUG] OpenAIModel tool message content sent as array instead of string breaks OpenAI-compatible endpoints (e.g., Kimi K2.5)
- User impact: Developers may fail before the first successful local run: [BUG] OpenAIModel tool message content sent as array instead of string breaks OpenAI-compatible endpoints (e.g., Kimi K2.5)
- Evidence: failure_mode_cluster:github_issue | https://github.com/strands-agents/harness-sdk/issues/1696

## 7. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: python/v1.41.0
- User impact: Upgrade or migration may change expected behavior: python/v1.41.0
- Evidence: failure_mode_cluster:github_release | https://github.com/strands-agents/harness-sdk/releases/tag/python/v1.41.0

## 8. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: v1.41.0
- User impact: Upgrade or migration may change expected behavior: v1.41.0
- Evidence: failure_mode_cluster:github_release | https://github.com/strands-agents/harness-sdk/releases/tag/v1.41.0

## 9. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/strands-agents/harness-sdk/issues/1696

## 10. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/strands-agents/harness-sdk/issues/2614

## 11. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/strands-agents/harness-sdk/issues/2421

## 12. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: [FEATURE] Support Managed Bedrock Knowledge Bases for Memory Manager
- User impact: Developers may misconfigure credentials, environment, or host setup: [FEATURE] Support Managed Bedrock Knowledge Bases for Memory Manager
- Evidence: failure_mode_cluster:github_issue | https://github.com/strands-agents/harness-sdk/issues/2880

## 13. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: [FEATURE] Support for multiple MCP servers (and loading from config file)
- User impact: Developers may misconfigure credentials, environment, or host setup: [FEATURE] Support for multiple MCP servers (and loading from config file)
- Evidence: failure_mode_cluster:github_issue | https://github.com/strands-agents/harness-sdk/issues/482

## 14. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: [WASM] MCP tools
- User impact: Developers may misconfigure credentials, environment, or host setup: [WASM] MCP tools
- Evidence: failure_mode_cluster:github_issue | https://github.com/strands-agents/harness-sdk/issues/2456

## 15. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: [WASM] Python SDK - Gaps and Limitations
- User impact: Developers may misconfigure credentials, environment, or host setup: [WASM] Python SDK - Gaps and Limitations
- Evidence: failure_mode_cluster:github_issue | https://github.com/strands-agents/harness-sdk/issues/2421

## 16. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: python/v1.42.0
- User impact: Upgrade or migration may change expected behavior: python/v1.42.0
- Evidence: failure_mode_cluster:github_release | https://github.com/strands-agents/harness-sdk/releases/tag/python/v1.42.0

## 17. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: python/v1.44.0
- User impact: Upgrade or migration may change expected behavior: python/v1.44.0
- Evidence: failure_mode_cluster:github_release | https://github.com/strands-agents/harness-sdk/releases/tag/python/v1.44.0

## 18. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: v1.35.0
- User impact: Upgrade or migration may change expected behavior: v1.35.0
- Evidence: failure_mode_cluster:github_release | https://github.com/strands-agents/harness-sdk/releases/tag/v1.35.0

## 19. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: v1.36.0
- User impact: Upgrade or migration may change expected behavior: v1.36.0
- Evidence: failure_mode_cluster:github_release | https://github.com/strands-agents/harness-sdk/releases/tag/v1.36.0

## 20. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: v1.37.0
- User impact: Upgrade or migration may change expected behavior: v1.37.0
- Evidence: failure_mode_cluster:github_release | https://github.com/strands-agents/harness-sdk/releases/tag/v1.37.0

## 21. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: v1.38.0
- User impact: Upgrade or migration may change expected behavior: v1.38.0
- Evidence: failure_mode_cluster:github_release | https://github.com/strands-agents/harness-sdk/releases/tag/v1.38.0

## 22. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://github.com/strands-agents/harness-sdk

## 23. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this migration risk before relying on the project: typescript/v1.6.0
- User impact: Upgrade or migration may change expected behavior: typescript/v1.6.0
- Evidence: failure_mode_cluster:github_release | https://github.com/strands-agents/harness-sdk/releases/tag/typescript/v1.6.0

## 24. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/strands-agents/harness-sdk

## 25. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://github.com/strands-agents/harness-sdk

## 26. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://github.com/strands-agents/harness-sdk

## 27. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/strands-agents/harness-sdk/issues/2601

## 28. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/strands-agents/harness-sdk/issues/2456

## 29. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/strands-agents/harness-sdk

## 30. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/strands-agents/harness-sdk

<!-- canonical_name: strands-agents/harness-sdk; human_manual_source: deepwiki_human_wiki -->
