Releases: strands-agents/sdk-python
v1.33.0
Pins litellm<=1.82.6 to supply chain attack - Supply Chain Attack in litellm 1.82.8 on PyPI
What's Changed
- fix: summarization conversation manager sometimes returns empty response by @Unshure in #1947
- fix: remove agent from swarm test to get more consistency out of it by @Unshure in #1946
- fix: CRITICAL: Hard pin
litellm<=1.82.6to mitigate supply chain attack by @udaymehta in #1961
New Contributors
- @udaymehta made their first contribution in #1961
Full Changelog: v1.32.0...v1.33.0
v1.32.0
What's Changed
- fix(event-loop): ensure all cycle metrics include end time and duration by @stephentreacy in #1903
- fix: pin upper bound for mistralai dependency by @mkmeral in #1935
- fix: override end_turn stop reason when streaming response contains toolUse blocks by @atian8179 in #1827
New Contributors
- @stephentreacy made their first contribution in #1903
- @atian8179 made their first contribution in #1827
Full Changelog: v1.31.0...v1.32.0
v1.31.0
What's Changed
- feat: pass A2A request context metadata as invocation state by @mkmeral in #1854
- fix: s3session manager bug by @mehtarac in #1915
- fix(graph): only evaluate outbound edges from completed nodes by @giulio-leone in #1846
- fix(openai): always use string content for tool messages by @giulio-leone in #1878
- feat: widen openai dependency to support 2.x for litellm compatibility by @BV-Venky in #1793
- fix: typeError when serializing multimodal prompts with binary content in Graph/Swarm session persistence by @JackYPCOnline in #1870
- fix: lowercase the python language in code snippet by @zastrowm in #1929
- fix: openai repsonses api error handling by @Unshure in #1931
New Contributors
Full Changelog: v1.30.0...v1.31.0
v1.30.0
What's Changed
- feat: add "anthropic" cache strategy to bypass model ID check by @kevmyung in #1808
- feat: serialize tool results as JSON when possible by @clareliguori in #1752
- fix: summary manager using structured output by @pgrayy in #1805
- feat(mcp): expose server instructions from InitializeResult on MCPClient by @ShotaroKataoka in #1814
- fix: added LANGFUSE_BASE_URL check for additinoal attribute by @poshinchen in #1826
- feat(session): add dirty flag to skip unnecessary agent state persistence by @Unshure in #1803
- feat: add public tool_spec setter by @mkmeral in #1822
- feat: add CancellationToken for graceful agent execution cancellation by @jgoyani1 in #1772
- feat(session): optimize session manager initialization by @Unshure in #1829
- fix(mistral): report usage metrics in streaming mode by @jackatorcflo in #1697
- fix(openai_responses): use output_text for assistant messages in multi-turn conversations by @giulio-leone in #1851
- feat(hooks): add resume flag to AfterInvocationEvent by @mkmeral in #1767
- fix: place cache point on last user message instead of assistant by @kevmyung in #1821
- feat(skills): add agent skills as a plugin by @mkmeral in #1755
- feat(steering): move steering from experimental to production by @dbschmigelski in #1853
- fix: break circular references so Agent cleanup doesn't hang with MCPClient by @dbschmigelski in #1830
- fix: Set is_new_session = False at the end of each initialize* method by @mehtarac in #1859
New Contributors
- @ShotaroKataoka made their first contribution in #1814
- @jgoyani1 made their first contribution in #1772
- @jackatorcflo made their first contribution in #1697
- @giulio-leone made their first contribution in #1851
Full Changelog: v1.29.0...v1.30.0
v1.29.0
What's Changed
- test: pin virtualenv to <21 for hatch bug by @clareliguori in #1771
- fix(telemetry): added latest semantic conventions as span attributes for langfuse by @poshinchen in #1768
- fix: preserve guardrail_latest_message wrapping after tool execution by @austinmw in #1658
- feat(conversation-manager): improve tool result truncation strategy by @kevmyung in #1756
- feat(plugins): improve plugin creation devex with @hook and @tool decorators by @Unshure in #1740
- ci: bump actions/upload-artifact from 6 to 7 by @dependabot[bot] in #1777
- ci: bump actions/download-artifact from 7 to 8 by @dependabot[bot] in #1776
- fix: throw exceptions from ConcurrentToolExecutor (#1796) by @charles-dyfis-net in #1797
- feat: add OpenAI Responses API model implementation by @notgitika in #975
New Contributors
Full Changelog: v1.28.0...v1.29.0
v1.28.0
What's Changed
- fix: update region for agentcore in our new account by @afarntrog in #1715
- fix: remove test that fails for python 3.14 by @Unshure in #1717
- feat(hooks): support union types and list of types for add_hook by @Unshure in #1719
- feat: make pyaudio an optional dependency by lazy loading by @mehtarac in #1731
- feat(hooks): add Plugin Protocol for agent extensibility by @Unshure in #1733
- feat: add plugins parameter to Agent by @Unshure in #1734
- refactor(plugins): convert Plugin from Protocol to ABC by @Unshure in #1741
- feat(steering): migrate SteeringHandler from HookProvider to Plugin by @Unshure in #1738
- chore: switch to Sonnet 4.6 for Anthropic provider integ tests by @clareliguori in #1754
- fix: rename init_plugin to init_agent by @Unshure in #1765
Full Changelog: v1.27.0...v1.28.0
v1.27.0
What's Changed
- feat: Propagate exceptions to AfterToolCallEvent for decorated tools (#1565) by @charles-dyfis-net in #1566
- feat(workflows): add conventional commit workflow in PR by @mkmeral in #1645
- fix: the A2AAgent returns empty AgentResult content by @afarntrog in #1675
- auto run review workflow on maintainer PR by @mehtarac in #1673
- fix: correct output reference for approval-env in integration test by @afarntrog in #1685
- fix: update approval env var for strands agent workflows by @Unshure in #1701
- fix: update allowed roles to include maintainer by @afarntrog in #1704
- fix: propagate reasoningSignature on Gemini tool use by @afarntrog in #1703
- ci: bump actions/github-script from 7 to 8 by @dependabot[bot] in #1699
- ci: bump amannn/action-semantic-pull-request from 5 to 6 by @dependabot[bot] in #1684
- fix: handle OpenAI model responses with tool calls and no other assistant content by @clareliguori in #1562
- fix: Update finalize condition for workflow execution by @Unshure in #1708
- fix: upgrade mcp minimum dependency to 1.23.0 for Tasks support by @clareliguori in #1674
- feat(agent): add concurrent_invocation_mode parameter by @zastrowm in #1707
- test: coverage for python 3.14 by @awsarron in #1178
- feat(agent): add add_hook convenience method for hook callback registration by @Unshure in #1706
Full Changelog: v1.26.0...v1.27.0
v1.26.0
What's Changed
- ci: bump aws-actions/configure-aws-credentials from 5 to 6 by @dependabot[bot] in #1632
- docs: add guidance on using Protocol instead of Callable for extensible interfaces by @dbschmigelski in #1637
- feat(mcp): Implement basic support for Tasks by @LucaButBoring in #1475
- fix(multiagent): set empty text part data in
partsforArtifactby @punkyoon in #1643 - fix(summarizing_conversation_manager): use model stream to generate summary by @mkmeral in #1653
- fix(bedrock): add 'prompt is too long' to context window overflow mes… by @eladb3 in #1663
- fix: fix mcp tests by @afarntrog in #1664
New Contributors
- @LucaButBoring made their first contribution in #1475
- @punkyoon made their first contribution in #1643
- @eladb3 made their first contribution in #1663
Full Changelog: v1.25.0...v1.26.0
v1.25.0
Major Features
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.
from strands.agent.a2a_agent import A2AAgent
# Connect to a remote A2A agent
a2a_agent = A2AAgent(endpoint="http://localhost:9000")
# Invoke it like any other agent
result = a2a_agent("Show me 10 ^ 6")
print(result.message)
# Or stream events asynchronously
async for event in a2a_agent.stream_async("Summarize this report"):
if event.get("type") == "a2a_stream":
print(f"A2A event: {event['event']}")
elif "result" in event:
print(f"Final: {event['result'].message}")A2AAgent Support in Graph Workflows - PR#1615
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.
from strands import Agent
from strands.agent.a2a_agent import A2AAgent
from strands.multiagent.graph import GraphBuilder
local_agent = Agent(name="summarizer", system_prompt="Summarize input concisely.")
remote_agent = A2AAgent(endpoint="http://remote-agent:9000")
builder = GraphBuilder()
builder.add_node(remote_agent, "research")
builder.add_node(local_agent, "summarize")
builder.add_edge("research", "summarize")
graph = builder.build()
result = graph("Analyze recent AI trends")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
from strands.types.tools import ToolContext
@tool(context=True)
def approval_tool(tool_context: ToolContext) -> str:
return tool_context.interrupt("approval", reason="Needs human approval")
agent = Agent(name="reviewer", tools=[approval_tool])
inner_graph = GraphBuilder()
inner_graph.add_node(agent, "reviewer")
outer_graph = GraphBuilder()
outer_graph.add_node(inner_graph.build(), "review_pipeline")
graph = outer_graph.build()
result = graph("Review this document")
while result.status == Status.INTERRUPTED:
responses = [
{"interruptResponse": {"interruptId": i.id, "response": "Approved"}}
for i in result.interrupts
]
result = graph(responses)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
}
}
}
}
]
}])Configurable Structured Output Prompt - PR#1627
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.
from strands import Agent
from pydantic import BaseModel
class UserInfo(BaseModel):
name: str
age: int
# Custom prompt avoids triggering Bedrock Guardrails prompt attack filter
agent = Agent(
structured_output_model=UserInfo,
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@toolparameter was typed asT | Nonewithout a default value, the schema cleaning logic was strippingnullfrom theanyOfarray, making the field required with no way to express null. This caused LLMs to fall back to passing"null"as a string. TheanyOfsimplification is now skipped for fields in therequiredarray, 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"asAPIErrorinstead ofBadRequestErrorwith codecontext_length_exceeded. These errors are now detected and converted toContextWindowOverflowException, allowing conversation managers likeSummarizingConversationManagerto trigger properly. -
retry_strategy=NoneNow Disables Retries - PR#1630
Passingretry_strategy=Noneto the Agent constructor previously applied the default retry strategy instead of disabling retries. NowNoneexplicitly disables all SDK retries (equivalent toModelRetryStrategy(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 passedNone. -
A2A Server Agent Card URL Updated on Host/Port Override - PR#1626
When overridinghostandportinA2AServer.serve(), the agent card at/.well-known/agent-card.jsonstill 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 explicithttp_urlwas provided in the constructor.
Minor Changes
- Increase pytest timeout to 45 seconds - PR#1586
- Publish integ test results to CloudWatch - PR#1587
- Clone main metrics upload script for integ tests - PR#1600
- Skip S3 location for non-Bedrock model providers - PR#1602
- Add conditional execution for finalize step - PR#1605
- Fix various test warnings - PR#1613
- Fix Bedrock file warnings - PR#1603
- Increase test timeout - PR#1623
- Fix OpenAI test - PR#1624
- Remove broken MCP transport timeout test - PR#1635
- Bump
actions/setup-pythonfrom 4 to 6 - PR#1548 - Bump
aws-actions/configure-aws-credentialsfrom 4 to 5 - PR#1547 - Bump
actions/download-artifactfrom 4 to 7 - PR#1609 - Bump
actions/upload-artifactfrom 4 to 6 - PR#1608
New Contributors
- @charles-dyfis-net made their first contribution in PR#1584
- @dinindu...
v1.24.0
What's Changed
- test: fix flaky openai structured output test by adding Field guidance by @dbschmigelski in #1534
- interrupts - multiagent - do not emit AfterNodeCallEvent on interrupt by @pgrayy in #1539
- ci: add workflow for lambda layer publish by @dbschmigelski in #870
- fix: Populate tool_args correctly for steering by @clareliguori in #1531
- interrupts - graph - agent based by @pgrayy in #1533
- chore: refactor use_span to be closed automatically by @poshinchen in #1293
- ci: limit permission scope on lambda layer github action by @dbschmigelski in #1555
- chore: Enable Auto-close labels on Pull requests as well. by @yonib05 in #1552
- Use devtools actions by @Unshure in #1554
- feat(bedrock): add automatic prompt caching support by @kevmyung in #1438
- feat(hooks): add retry mechanism for tool calls by @dbschmigelski in #1556
- feat(tools): move ToolProvider out of experimental namespace by @Unshure in #1567
- [FIX] models - gemini - start and stop reasoningContent by @JackYPCOnline in #1557
- feat(agent): update AgentResult str priority order by @afarntrog in #1553
- callback handler - fix reporting of tool when missing delta by @pgrayy in #1573
- feat(hooks): Add invocation state by @mkmeral in #1550
- test(steering): Fix failing integ tests by @mkmeral in #1580
New Contributors
Full Changelog: v1.23.0...v1.24.0