feat(appkit): add fromPlugin() for referencing plugin tools in code-defined agents#298
Closed
MarioCadenas wants to merge 1 commit intoagent/5e-prepare-plugins-forward-instancefrom
Closed
Conversation
…efined agents
Introduces `fromPlugin(factory, opts?)` — a spread-friendly symbol-keyed
marker that references a plugin's tools inside an `AgentDefinition.tools`
record without needing an intermediate `const analyticsP = analytics()`
variable or a `.toolkit()` call.
```ts
const support = createAgent({
instructions: "…",
tools: {
...fromPlugin(analytics),
...fromPlugin(files, { only: ["uploads.read"] }),
get_weather: tool({ … }),
},
});
await createApp({
plugins: [server(), analytics(), files(), agents({ agents: { support } })],
});
```
At setup time `AgentsPlugin.buildToolIndex` walks
`Object.getOwnPropertySymbols(def.tools)`, resolves each marker against
`PluginContext.getToolProviders()`, and calls the provider's `.toolkit(opts)`
method. For providers without `.toolkit()` (third-party ToolProviders or
plugins built with plain `toPlugin`), the resolver falls back to walking
`getAgentTools()` and synthesizing namespaced keys
(`\${pluginName}.\${localName}`), respecting `only` / `except` / `rename` /
`prefix` the same way. Missing plugins throw at setup with an
`Available: …` listing so wiring errors surface on boot, not mid-request.
`runAgent` gains an optional `plugins?: PluginData[]` argument so
`fromPlugin` markers work in standalone mode too (plugin tools dispatch
as the service principal since there is no HTTP request).
Factory identity is carried via a new `pluginName` field stamped onto
the returned function inside `toPlugin` / `toPluginWithInstance`, which
`fromPlugin` reads synchronously — no throwaway instance construction.
`.toolkit()` is not deprecated — it remains the power-user path for
renaming or combining tools in ways `fromPlugin`'s options can't express.
Docs lead with `fromPlugin` as the primary shape.
Also:
- Widens `AgentDefinition.tools` to
`{ [key: string]: AgentTool } & { [key: symbol]: FromPluginMarker }`
so spread compiles under strict TS while preserving string-key
autocomplete.
- `hasExplicitTools` now includes symbol keys, so a `tools: { ...fromPlugin(x) }`
record correctly disables auto-inherit on code-defined agents.
- Shared `resolveToolkitFromProvider` helper between auto-inherit and
fromPlugin resolution paths.
- Migrates apps/agent-app/server.ts to spread `fromPlugin` instead of
hand-writing the support agent's tool list.
- Adds a new code-defined `helper` agent to apps/dev-playground/server/index.ts
showing the API alongside the markdown-driven `autocomplete` agent.
- Docs updated: new "Scoping tools in code" section in agents.md; new
fromPlugin section in the migration guide with before/after.
Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
This was referenced Apr 21, 2026
Collaborator
Author
|
Superseded by the v2 6-PR stack:
The v2 stack reorganizes the same work so no PR ships API that a later PR deletes. Start at #301 for the new entry point. Branches from this older stack are preserved unchanged. |
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.
Summary
Introduces
fromPlugin(factory, opts?)— a spread-friendly, symbol-keyed marker for pulling a plugin's tools into a code-defined agent without needing an intermediate variable or an eager.toolkit()call.Before
After
How it works
fromPlugin(factory)readsfactory.pluginName(stamped onto everytoPlugin-produced factory) and returns a single symbol-keyed entry:{ [Symbol()]: FromPluginMarker }. Each call generates a freshSymbol(), so multiple spreads of the same plugin coexist safely.AgentsPlugin.buildToolIndexwalksObject.getOwnPropertySymbols(def.tools), resolves each marker againstcontext.getToolProviders(), and calls the plugin's.toolkit(opts)method. Missing plugins throw at setup with anAvailable: ...listing — wiring errors surface on boot, not mid-request.ToolProviders without a.toolkit()method (e.g. third-party plugins built with plaintoPlugin), the resolver falls back to walkinggetAgentTools()and synthesizing namespaced keys (${pluginName}.${localName}) while still honoringonly/except/rename/prefix.Type plumbing
NamedPluginFactorytype +pluginNamestamp on the factory returned bytoPlugin/toPluginWithInstance.AgentDefinition.toolswidened to{ [key: string]: AgentTool } & { [key: symbol]: FromPluginMarker }so spread compiles under strict TS while preserving string-key autocomplete.hasExplicitToolsinbuildToolIndexnow counts symbol keys, so atools: { ...fromPlugin(x) }record correctly disables auto-inherit on code-defined agents.runAgentsupportRunAgentInputgains an optionalplugins?: PluginData[]argument sofromPluginmarkers work in standalone mode too. Plugin tool dispatch in standalone mode runs as the service principal (no OBO, no HTTP request). If a def contains markers and the argument is absent,runAgentthrows with guidance.Migration
apps/agent-app/server.ts): rewritten to spreadfromPlugin(analytics)/fromPlugin(files, ...)instead of the.toolkit()dance.apps/dev-playground/server/index.ts): gains a new code-definedhelperagent demonstrating the API alongside the markdown-drivenautocompleteagent.agents.md; newfromPluginsection in the migration guide with before/after.PR Stack
agents()plugin +createAgent(def)+.toolkit()— feat(appkit): add agents() plugin, createAgent() factory, and .toolkit() #294agents()— feat(appkit): migrate agent-app and docs to the new agents() plugin #295preparePluginsforwards eager instance — refactor(appkit): forward eager plugin instance through preparePlugins #297fromPlugin()API (this PR)agent()+createAgentApp— chore(appkit): remove deprecated agent() plugin and createAgentApp shortcut #299toPluginWithInstance+ bug fixes — refactor(appkit): retire toPluginWithInstance; consolidate on fromPlugin + fix schema/routing bugs #300Test plan
packages/appkit/src/plugins/agents/tests/from-plugin.test.ts(marker shape, symbol uniqueness, type guard, factory without pluginName)agents-plugin.test.tswith: marker resolution, mixed inline + marker, structural error assertions, symbol-only disables auto-inherit, fallback-to-getAgentToolsrun-agent.test.tswith: standalone marker resolution viapluginsarg + guidance error when missing