Skip to content

[Bug]: StrandsAgent silently drops plugins from per-thread agents #1933

Description

@ben-gineer

Pre-flight Checklist

  • I have searched existing issues and this hasn't been reported yet.
  • I am using the latest version AG-UI.

Describe the Bug

Summary

When a StrandsAgent is constructed from a template Agent that has plugins (e.g. AgentSkills), those plugins are silently dropped from every per-thread StrandsAgentCore instance. The per-thread agents behave as if no plugins were configured.

Root cause

StrandsAgent.__init__ (in src/ag_ui_strands/agent.py) copies the template agent's configuration using _extract_agent_kwargs, which introspects Agent.__init__'s constructor signature and reads matching attributes off the agent instance. The problem is that Strands does not retain the plugins list as an attribute after Agent.__init__ completes — plugins are consumed during initialisation (hooks registered into HookRegistry, tools registered into ToolRegistry) and the original list is discarded. As a result, _extract_agent_kwargs finds nothing to copy, and the per-thread agents are created without any plugins.

The existing hooks= parameter on StrandsAgent was added to work around exactly this problem for hook callbacks — but it is an undiscoverable escape hatch that requires callers to reach into plugin internals, and it only works for plugins that register hooks. Plugins that only register tools have no workaround at all.

Steps to Reproduce

Steps to reproduce

from strands import Agent, AgentSkills
from ag_ui_strands import StrandsAgent

plugin = AgentSkills(skills=["./skills"])
agent = Agent(model=model, tools=[...], plugins=[plugin])

# Plugin is registered on the template agent correctly.

agui_agent = StrandsAgent(agent=agent, name="my-agent")

# Per-thread StrandsAgentCore instances have no AgentSkills plugin.
# The <available_skills> XML is never injected into the system prompt.
# The `skills` tool is absent.
# No warning or error is raised.

### Expected Behavior

Plugins attached to the template agent are reproduced faithfully in every per-thread StrandsAgentCore. The behaviour of a per-thread agent matches the template agent.

### Actual behaviour
Plugins are silently dropped. When using AgentSkills, the <available_skills> XML is never injected into the system prompt and the skills tool is not registered. No warning or error is raised.

### Environment

```text
ag_ui_strands-0.1.9
strands-agents-1.42.0

Screenshots

No response

Logs & Errors

Additional Context

Current workaround

Pass the plugin's internal hook callbacks via StrandsAgent(hooks=[...]) and explicitly include the plugin's tools in the template agent's tools list:

plugin = AgentSkills(skills=["./skills"])
agent = Agent(model=model, tools=[*my_tools, *plugin.tools], plugins=[plugin])

agui_agent = StrandsAgent(
    agent=agent,
    name="my-agent",
    hooks=plugin.hooks,  # must reach into plugin internals
)

This is undiscoverable, requires knowledge of plugin internals, and only works for plugins that register hooks.

Suggested fix

Add a plugins parameter to StrandsAgent.init (alongside the existing hooks parameter). When provided, call agent._plugin_registry.add_and_init(plugin) on each per-thread StrandsAgentCore immediately after it is created in run().

In src/ag_ui_strands/agent.py:

__init__ — accept and store plugins:

def __init__(
    self,
    agent: StrandsAgentCore,
    name: str,
    description: str = "",
    config: "StrandsAgentConfig | None" = None,
    hooks: "list | None" = None,
    plugins: "list | None" = None,   # <-- add this
):
    ...
    self._hooks = list(hooks) if hooks else []
    self._plugins = list(plugins) if plugins else []   # <-- add this

run() — initialise plugins on each new per-thread agent (after the existing StrandsAgentCore(...) call, around line 406):

self._agents_by_thread[thread_id] = StrandsAgentCore(
    model=self._model,
    system_prompt=self._system_prompt,
    tools=self._tools,
    session_manager=session_manager,
    **core_kwargs,
)
# Initialise plugins on the fresh per-thread agent so their hooks
# and tools are registered exactly as they would be on the template.
for plugin in self._plugins:
    self._agents_by_thread[thread_id]._plugin_registry.add_and_init(plugin)

This mirrors what Agent.__init__ does internally and requires no changes to Strands itself.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions