First TypeScript release cut from the unified harness-sdk monorepo. The
itemized change list was omitted during the repository merge — see the
release on GitHub
for the full compare. Earlier TypeScript history continues below from the
original sdk-typescript repository.
Amazon Bedrock now offers service tiers (Priority, Standard, Flex) that let you control the trade-off between latency and cost on a per-request basis. BedrockModel accepts a new service_tier configuration field, consistent with how other Bedrock-specific features like guardrails are exposed. When not set, the field is omitted and Bedrock uses its default behavior.
from strands import Agent
from strands.models.bedrock import BedrockModel
# Use "flex" tier for cost-optimized batch processing
Valid values are "default", "priority", and "flex". If a model or region does not support the specified tier, Bedrock returns a ValidationException.
Bug Fixes
Sliding window conversation manager user-first enforcement — PR#2087: The sliding window could produce a trimmed conversation starting with an assistant message, causing ValidationException on providers that require user-first ordering (including Bedrock Nova). The trim-point validation now ensures the first remaining message always has role == "user". Also fixed a short-circuit logic bug in the toolUse guard that let orphaned tool-use blocks slip through at window boundaries.
MCP _meta forwarding — PR#1918, PR#2081: Custom metadata per the MCP spec was silently dropped because MCPClient never forwarded the _meta field to ClientSession.call_tool(). Additionally, the OTEL instrumentation used model_dump() instead of model_dump(by_alias=True), serializing the field as "meta" instead of "_meta" and corrupting the payload. Both the direct call_tool and task-augmented execution paths now correctly forward meta.
Tool exception propagation to OpenTelemetry spans — PR#2046: When a tool raised an exception, the original exception was dropped before reaching end_tool_call_span, causing all tool spans to get StatusCode.OK even on errors. Tool errors now correctly propagate with StatusCode.ERROR, preserving the original exception type and traceback for observability backends like Langfuse.
Anthropic premature stream termination — PR#2047: The Anthropic provider crashed with AttributeError when the stream terminated before the final message_stop event, because it accessed event.message.usage on event types that lack a .message attribute. Now uses the Anthropic SDK’s stream.get_final_message() to read accumulated usage from all received events, gracefully handling premature termination and empty streams.
The SDK now supports cooperative cancellation of running agent invocations. Callers can stop an agent mid-execution via agent.cancel(), AbortSignal.timeout(), or any external AbortSignal (e.g., from an HTTP framework). Cancellation is checked between model stream events and before each tool execution, and running tools are never forcibly interrupted — they complete unless the tool itself checks the cancellation signal.
Agents can now be used as tools for other agents via agent.asTool() or by passing agents directly in the tools array (auto-wrapped). This enables hierarchical multi-agent architectures where a parent agent delegates subtasks to specialized child agents. The preserveContext option controls whether the sub-agent retains conversation history across invocations.
constresearcher=newAgent({
name: 'researcher',
description: 'Finds information on a topic',
tools: [searchTool],
printer: false,
});
// Pass agents directly in tools array (auto-wrapped)
constwriter=newAgent({
tools: [researcher],
});
await writer.invoke('Write an article about quantum computing');
SessionManager now implements MultiAgentPlugin, enabling Graph and Swarm orchestrators to automatically save and restore execution state. After each orchestrator invocation, the multi-agent state (node statuses, results, steps, app state) is persisted. On the next invocation, the snapshot is restored before the first node executes.
// First invocation — snapshot saved automatically
await graph.invoke('Write about AI');
// Second invocation — state restored from snapshot
await graph.invoke('Continue');
Scope isolation between agent and multi-agent snapshots is maintained. A shared SessionManager across multiple orchestrators restores each one independently.
Adds internal snapshot take/load support for multi-agent orchestrators (Graph and Swarm). takeSnapshot() captures orchestrator ID and serialized execution state; loadSnapshot() validates scope, schema version, and orchestrator ID before restoring state. Also adds scope validation to agent loadSnapshot — passing a multi-agent snapshot to the agent loader now throws instead of silently no-oping.
Bug Fixes
Invocation lock leak on stream break — PR#796: When a consumer broke out of for-await-of on agent.stream(), the runtime called .return() on the generator. The finally block tried to drain remaining events via yield, but with no consumer to resume the generator, it suspended permanently. The lock disposable never ran, leaving _isInvoking stuck at true and causing ConcurrentInvocationError on any subsequent call. Drain events are now only yielded when the consumer may still be iterating.
Bedrock thinking + forced tool_choice conflict — PR#798: When using structuredOutputSchema with extended thinking enabled, the agent hit a Bedrock API error: “Thinking may not be enabled when tool_choice forces tool use.” The SDK now strips the thinking key from additionalRequestFields when toolChoice forces tool use (any or tool variants), preserving thinking for auto and unset tool choice.
Bedrock context window overflow detection — PR#782: Synced BEDROCK_CONTEXT_WINDOW_OVERFLOW_MESSAGES with the Python SDK to include the 'prompt is too long' error pattern, ensuring consistent overflow detection across both SDKs.
Ground Truth Assertion Support for Goal Success Rate Evaluator — PR#180
The GoalSuccessRateEvaluator now supports a second evaluation mode: assertion-based evaluation. When expected_assertion is provided on the evaluation case, the judge LLM evaluates whether the agent’s behavior satisfies explicit success assertions rather than inferring goals from the conversation. This enables precise, reproducible evaluation with human-authored success criteria.
from strands_evals import Case
from strands_evals.evaluators import GoalSuccessRateEvaluator
# Basic mode (existing) — judge infers goals from conversation
case_basic = Case(
session_id="session-1",
goal="Help user book a flight",
)
# Assertion mode (new) — judge evaluates against explicit criteria
case_assertion = Case(
session_id="session-2",
goal="Help user book a flight",
expected_assertion="Agent confirmed departure city, destination, and date before searching for flights",
TraceProvider now exposes an as_task() method that returns a task callable, eliminating the need to write wrapper functions when running evaluations against traced sessions.
A2AAgent: First-Class Client for Remote A2A Agents - PR#1441
The new A2AAgent class makes it simple to connect to and invoke remote agents that implement the Agent-to-Agent (A2A) protocol. A2AAgent implements the AgentBase protocol, so it can be called synchronously, asynchronously, or used in streaming mode just like a local Agent. It automatically discovers the remote agent’s card to populate its name and description, and manages HTTP client lifecycle for you.
Graph nodes now accept any AgentBase implementation as an executor, not just the concrete Agent class. This means A2AAgent instances and other custom AgentBase implementations can participate in graph-based multi-agent workflows alongside local agents. The Agent class also now explicitly extends AgentBase for compile-time protocol verification.
Interrupt Support for MultiAgent Graph Nodes - PR#1606
Interrupts now propagate correctly through nested multi-agent graph nodes. When an agent inside a nested Graph, Swarm, or any custom MultiAgentBase node raises an interrupt, the outer graph pauses execution and surfaces the interrupt to the caller. After the caller provides a response, the outer graph resumes execution from where it left off. This builds on the interrupt support for single-agent graph nodes added in v1.24.0.
from strands import Agent, tool
from strands.interrupt import Interrupt
from strands.multiagent import GraphBuilder, Status
S3 Location Support for Documents, Images, and Videos - PR#1572
Media content types now support S3 locations as a source, allowing you to reference documents, images, and videos stored in Amazon S3 directly without base64 encoding. The new S3Location type includes a required uri field and an optional bucketOwner field for cross-account access. On Bedrock, S3 locations are passed through to the API natively.
from strands import Agent
agent = Agent()
response = agent([{
"role": "user",
"content": [
{"text": "Summarize this document:"},
{
"document": {
"format": "pdf",
"name": "report",
"source": {
"location": {
"type": "s3",
"uri": "s3://my-bucket/documents/report.pdf",
"bucketOwner": "123456789012"# optional, for cross-account
The prompt message the agent uses to request structured output formatting is now configurable via the structured_output_prompt parameter. Previously, the hardcoded message "You must format the previous response as structured output." could trigger Bedrock Guardrails prompt-attack filters. You can now customize this message at the agent level or per invocation to work around guardrail rules or to better suit your use case.
structured_output_prompt="Please use the output tool now."
)
# Or override per invocation
result = agent(
"Extract user info from: John is 30 years old",
structured_output_prompt="Format the response using the output tool."
)
Major Bug Fixes
Nullable Semantics Preserved for Required Union[T, None] Tool Parameters - PR#1584
When a @tool parameter was typed as T | None without a default value, the schema cleaning logic was stripping null from the anyOf array, making the field required with no way to express null. This caused LLMs to fall back to passing "null" as a string. The anyOf simplification is now skipped for fields in the required array, preserving proper nullable semantics.
LedgerProvider Now Handles Parallel Tool Calls Correctly - PR#1559
When agents proposed multiple tool calls in a single model response, the ledger provider only updated the last tool in the batch (ledger['tool_calls'][-1]), leaving earlier pending tools without proper status updates. The provider now correctly tracks and updates each individual tool call.
Context Overflow Detection for OpenAI-Compatible Bedrock Endpoints - PR#1529
OpenAI-compatible endpoints that wrap Bedrock models (e.g., Databricks Model Serving) return Bedrock-style error messages like "Input is too long for requested model" as APIError instead of BadRequestError with code context_length_exceeded. These errors are now detected and converted to ContextWindowOverflowException, allowing conversation managers like SummarizingConversationManager to trigger properly.
retry_strategy=None Now Disables Retries - PR#1630
Passing retry_strategy=None to the Agent constructor previously applied the default retry strategy instead of disabling retries. Now None explicitly disables all SDK retries (equivalent to ModelRetryStrategy(max_attempts=1)), while omitting the parameter entirely still applies the default strategy. Note: This is a minor breaking change in behavior for code that explicitly passed None.
A2A Server Agent Card URL Updated on Host/Port Override - PR#1626
When overriding host and port in A2AServer.serve(), the agent card at /.well-known/agent-card.json still returned the original URL from constructor defaults, causing A2A clients to connect to the wrong address. The server now updates the agent card URL to match the overridden host/port, unless an explicit http_url was provided in the constructor.
A new GeminiModel provider brings native Google Gemini support to the TypeScript SDK. It integrates with the @google/genai SDK and supports streaming text responses with the standard ModelStreamEvent interface, configurable model parameters, and API key authentication via constructor option or the GEMINI_API_KEY environment variable. You can also pass a pre-configured GoogleGenAI client instance. The default model is gemini-2.5-flash. This initial release supports text content streaming, with tool calling planned for a follow-up.
Hooks can now request tool execution retries through a new retry property on AfterToolCallEvent, bringing feature parity with the Python SDK. When a hook sets event.retry = true, the agent discards the current result from conversation history and re-executes the tool, emitting a fresh BeforeToolCallEvent on each attempt. Retries work both for error recovery and for success-based re-evaluation when the result doesn’t meet criteria. Breaking change:AfterModelCallEvent.retryModelCall has been renamed to AfterModelCallEvent.retry for consistency.
Migration note: Rename afterModelCallEvent.retryModelCall = true to afterModelCallEvent.retry = true.
Bug Fixes
Add @google/genai to devDependencies - PR#502
Fixed CI build failures by adding the @google/genai package to devDependencies, ensuring TypeScript compilation succeeds for the new Gemini model provider.
The new ResponseRelevanceEvaluator measures how well an agent’s response addresses the user’s question. It uses a 5-level LLM-as-judge scoring system — Not At All (0.0), Not Generally (0.25), Neutral/Mixed (0.5), Generally Yes (0.75), and Completely Yes (1.0) — with a pass threshold at ≥0.5. Like other trace-level evaluators, it requires an actual_trajectory session and supports both sync and async evaluation.
from strands_evals.evaluators import ResponseRelevanceEvaluator
evaluator = ResponseRelevanceEvaluator()
results = evaluator.evaluate(evaluation_data)
# results[0].score -> 1.0 (for COMPLETELY_YES)
# results[0].test_pass -> True (score >= 0.5)
# results[0].reason -> "The response directly answers the question."
The new ConcisenessEvaluator assesses whether an agent’s response is appropriately concise. It uses a 3-level scoring system: Perfectly Concise (1.0) for responses that deliver exactly what was asked, Partially Concise (0.5) for minor extra wording, and Not Concise (0.0) for verbose or repetitive content. The pass threshold is ≥0.5. Both evaluators accept an optional custom model and system_prompt for the LLM judge.
from strands_evals.evaluators import ConcisenessEvaluator
Automatic Retry with Exponential Backoff for Throttled Evaluations - PR#107
Experiment.run_evaluations() and run_evaluations_async() now automatically retry on throttling errors using tenacity. Both task execution and evaluator execution are wrapped with exponential backoff (up to 6 attempts, 4s → 240s). Throttling is detected for ModelThrottledException, EventLoopException, and botocore ClientError with codes like ThrottlingException and TooManyRequestsException. Non-throttling errors are raised immediately without retrying. Additionally, one evaluator failing no longer prevents other evaluators from running on the same case.
from strands_evals import Case, Experiment
from strands_evals.evaluators import OutputEvaluator, ConcisenessEvaluator
experiment = Experiment(
cases=[Case(name="test", input="What is 2+2?", expected_output="4")],
evaluators=[
OutputEvaluator(rubric="Is the answer correct?"),
ConcisenessEvaluator(),
],
)
# Throttling errors are retried automatically with exponential backoff.
# If one evaluator fails, the other still runs.
reports = experiment.run_evaluations(my_agent)
Bug Fixes
Replace Deprecated Structured Output Methods - PR#67
Updated OutputEvaluator to call the agent with the structured_output_model parameter instead of the removed structured_output() and structured_output_async() methods. Without this fix, OutputEvaluator would fail on recent Strands SDK versions.
Strands Evaluation is a powerful framework for evaluating AI agents and LLM applications. From simple output validation to complex multi-agent interaction analysis, trajectory evaluation, and automated experiment generation, Strands Evaluation provides comprehensive tools to measure and improve your AI systems.
LLM-as-a-Judge: Built-in evaluators using language models for sophisticated assessment with structured scoring
Trace-based Evaluation: Analyze agent behavior through OpenTelemetry execution traces
Automated Experiment Generation: Generate comprehensive test suites from context descriptions
Custom Evaluators: Extensible framework for domain-specific evaluation logic
Experiment Management: Save, load, and version your evaluation experiments with JSON serialization
Built-in Scoring Tools: Helper functions for exact, in-order, and any-order trajectory matching
Simulators: Enable multi-turn evaluation of conversational agents by generating realistic interaction patterns that adapt based on agent responses to create authentic evaluation scenarios