feat(plugins): add declarative agent plugin support#75
Open
galuszkm wants to merge 2 commits into
Open
Conversation
- add `PluginDef` schema and `plugins:` field on agents (inline object + string shorthand) - resolve entries to live `strands.plugins.Plugin` instances via class or factory, passed to `Agent(plugins=[...])` - rewrite plugin `type:` import specs relative to the config file; leave `params` opaque - remove `plugins` from `agent_kwargs` now that it's first-class - require `strands-agents>=1.48.0` across all extras for the plugins API - document in Chapter 19 + full reference and add runnable `examples/15_plugins` - cover class, factory, non-Plugin, and bad-spec paths in tests with a `FakePlugin` fake
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds first-class support for strands plugins as a declarative field on agents.
Agents can now attach reusable behaviour packages — skills, context injection, steering, goal loops, or any custom
strands.plugins.Plugin— straight from YAML, the same wayhooks:already works.Each
plugins:entry is resolved to a livestrands.plugins.Plugininstance and passed through tostrands.Agent(plugins=[...]). strands-compose stays a thin translator: it resolves the import spec, calls it withparams, validates the result is aPlugin, and otherwise gets out of the way.What's included:
PluginDef(type+params) and aplugins: list[PluginDef | str]field onAgentDef. Accepts an inline object (type:+ optionalparams:) or a bare string shorthand, mirroringhooks:.config/resolvers/plugins.pywithresolve_plugin/resolve_plugin_entry. Supports bothmodule.path:Name(installed package) and./file.py:Name(local file) import specs, and resolves either aPluginsubclass or a factory callable returning one.type:is rewritten relative to the config file (like tools/hooks);paramsare treated as opaque kwargs and forwarded verbatim.build_agent_from_defresolves the plugin list and passes it to both the standard and custom-factoryAgent(...)construction paths.pluginsremoved from the documentedagent_kwargspassthrough now that it's a first-class field (prevents a duplicate-keyword error).docs/README.md, and a runnableexamples/15_plugins/demoingAgentSkills, aContextInjectorfactory, andGoalLoop, including a sampleconventional-commitsskill.strands-agentsto>=1.48.0,<2.0.0across all extras, the minimum exposing the plugins API used here.Related Issues
N/A
Type of Change
YAML / API Impact
Yes — this extends both the config schema and the public API, backwards-compatible:
agents.<name>.plugins:list. Defaults to empty, so all existing configs are unaffected.PluginDefmodel andresolve_plugin/resolve_plugin_entryresolvers. Nothing existing is renamed or removed.pluginsis no longer accepted viaagent_kwargs— it's now a dedicated field. Passing it both ways raises a duplicate-keywordTypeErrorfromAgent(), which is the intended guard rather than a silent regression.strands-agents>=1.48.0.Testing
How have you tested the change?
uv run just check(lint + type check) — clean, no issuesuv run just testfor overall testing — 232 passed, 76% coverageexamples/still workNew/updated tests:
tests/resolve/test_plugins.py— covers class resolution withparams, factory resolution, the bare-string shorthand, missing:intype(ValueError), and a non-Pluginresult (TypeError).tests/resolve/test_agents.py— asserts a declared plugin's@toolreachesagent.tool_names, proving the resolved list is wired intoAgent(plugins=[...]).tests/fakes/— adds aFakePluginandfake_plugin_factorycovering both the class and callable resolution paths.Checklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.