Skip to content

refactor(appkit): forward eager plugin instance through preparePlugins#297

Closed
MarioCadenas wants to merge 1 commit intoagent/5d-relocate-shared-agent-filesfrom
agent/5e-prepare-plugins-forward-instance
Closed

refactor(appkit): forward eager plugin instance through preparePlugins#297
MarioCadenas wants to merge 1 commit intoagent/5d-relocate-shared-agent-filesfrom
agent/5e-prepare-plugins-forward-instance

Conversation

@MarioCadenas
Copy link
Copy Markdown
Collaborator

@MarioCadenas MarioCadenas commented Apr 21, 2026

Summary

Stepping-stone PR unblocking fromPlugin (PR #298).

At this point in the stack, toPluginWithInstance eagerly constructs a plugin instance (Instance A) at factory-call time and attaches it to the returned PluginData as an .instance field. Previously, createApp's preparePlugins stripped that field before handing control to createAndRegisterPlugin, which then fell through to new Plugin(baseConfig) and constructed a second instance (Instance B). Dispatch used B; the handle the user held at module scope was A.

This PR:

  • Widens OptionalConfigPluginDef in packages/shared/src/plugin.ts with an optional instance?: BasePlugin field.
  • Forwards pluginData.instance through preparePlugins in packages/appkit/src/core/appkit.ts. The existing preBuilt ?? new Plugin(baseConfig) branch handles reuse — if instance is present, reuse it; otherwise construct fresh.

Effect: for plugins built via toPluginWithInstance (analytics, files, genie, lakebase), the handle a user holds at module scope is now the same object that answers runtime requests. Plugins built via plain toPlugin have no instance field and continue to construct fresh — no behavior change.

Why this is its own PR

The instance-forwarding fix is small, mechanical, and reviewable in isolation. PR #298's fromPlugin builds on top of it.

Note for reviewers: this plumbing is reverted later in the stack (PR #300) once toPluginWithInstance itself is retired in favor of fromPlugin. At that point no factory produces an instance field, so the forwarding logic becomes dead code and gets removed. We kept this PR in the stack rather than squashing because it was genuinely useful while it existed — it let #298 land incrementally on top of a working baseline, and it makes the fromPlugin design rationale legible in review.

PR Stack

  1. Shared types + Adapters — feat(appkit): add shared agent types and LLM adapter implementations #282
  2. Tool types + MCP client — feat(appkit): add FunctionTool, HostedTool types and MCP client #283
  3. Agent plugin core + ToolProvider implementations — feat(appkit): add agent plugin core and ToolProvider implementations #284
  4. PluginContext mediator — feat(appkit): add PluginContext mediator for inter-plugin communication #285
  5. createAgent + agent-app + docs (v1) — feat(appkit): add createAgent wrapper, agent-app, and API docs #286
  6. Plugin context-binding separation — refactor(appkit): separate plugin context binding from plugin construction #293
  7. agents() plugin + createAgent(def) + .toolkit()feat(appkit): add agents() plugin, createAgent() factory, and .toolkit() #294
  8. agent-app + docs migrated to agents()feat(appkit): migrate agent-app and docs to the new agents() plugin #295
  9. Relocate shared agent utilities — refactor(appkit): relocate shared agent utilities into plugins/agents #296
  10. preparePlugins forwards eager instance (this PR)
  11. fromPlugin() API — feat(appkit): add fromPlugin() for referencing plugin tools in code-defined agents #298
  12. Retire deprecated agent() + createAgentAppchore(appkit): remove deprecated agent() plugin and createAgentApp shortcut #299
  13. Retire toPluginWithInstance + bug fixes — refactor(appkit): retire toPluginWithInstance; consolidate on fromPlugin + fix schema/routing bugs #300

Test plan

  • New test packages/appkit/src/core/tests/appkit.test.ts asserting instance identity for toPluginWithInstance factories and unchanged behavior for plain toPlugin
  • Full vitest suite passing
  • Typecheck clean

`toPluginWithInstance` eagerly constructs a plugin instance at factory-call
time and exposes it via `PluginData.instance`. Previously, `preparePlugins`
stripped this field before handing control to `createAndRegisterPlugin`,
which then fell through to `new Plugin(baseConfig)` and constructed a
second instance. The first instance (the handle the user holds at module
scope) was discarded; dispatch used the second.

Widen `InputPluginMap`'s entry type with an optional `instance?: BasePlugin`
field and forward it through `preparePlugins`. The existing
`preBuilt ?? new Plugin(baseConfig)` branch handles reuse. Plugins built
via plain `toPlugin` have no `instance` field and continue to construct
fresh inside `createAndRegisterPlugin`.

This hardens an invariant that agents/agents.ts and other tooling now
rely on: the handle returned from `analytics()` and friends is the same
object that answers runtime requests.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
@MarioCadenas
Copy link
Copy Markdown
Collaborator Author

Superseded by the v2 6-PR stack:

  1. Shared agent types + LLM adapters — feat(appkit): shared agent types and LLM adapter implementations #301
  2. Tool primitives + ToolProvider surfaces — feat(appkit): tool primitives and ToolProvider surfaces on core plugins #302
  3. Plugin infrastructure (attachContext + PluginContext) — feat(appkit): plugin infrastructure — attachContext + PluginContext mediator #303
  4. agents() plugin + createAgent(def) + markdown-driven agents — feat(appkit): agents() plugin, createAgent(def), and markdown-driven agents #304
  5. fromPlugin() DX + runAgent plugins arg + toolkit-resolver — feat(appkit): fromPlugin() DX, runAgent plugins arg, shared toolkit-resolver #305
  6. Reference app + dev-playground + docs — feat(appkit): reference agent-app, dev-playground chat UI, docs, and template #306

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant