Skip to content

refactor(appkit): separate plugin context binding from plugin construction#293

Closed
MarioCadenas wants to merge 1 commit intoagent/4-createagent-appsfrom
agent/5a-plugin-base-refactor
Closed

refactor(appkit): separate plugin context binding from plugin construction#293
MarioCadenas wants to merge 1 commit intoagent/4-createagent-appsfrom
agent/5a-plugin-base-refactor

Conversation

@MarioCadenas
Copy link
Copy Markdown
Collaborator

@MarioCadenas MarioCadenas commented Apr 21, 2026

Summary

Splits the Plugin base-class lifecycle into two phases:

  • Construction (now pure): new Plugin(config) does nothing side-effectful. Factories like toPlugin can return a PluginData tuple at module scope without pulling in CacheManager, TelemetryManager, or PluginContext.
  • attachContext({ context, telemetryConfig }): runs inside createApp after all plugins have been constructed. Binds telemetry, cache, and the PluginContext mediator onto each instance, then fires setup().

This unblocks the new agents() plugin (PR #294) which needs to construct agent definitions at module scope and resolve their plugin references lazily at setup() time, and it's a prerequisite for any factory that wants to hand back a typed, reusable plugin handle before createApp runs.

Notes

  • ServerPlugin.setup() now defers its HTTP start to the setup:complete lifecycle hook so that routes registered by deferred-phase plugins (like agents()) are visible when the server binds. Without this, extendRoutes() would run before AgentsPlugin.injectRoutes() had a chance to add its handlers.
  • Adds attachContext to BasePlugin in packages/shared/src/plugin.ts.

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 (this PR)
  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 — refactor(appkit): forward eager plugin instance through preparePlugins #297
  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

  • Full vitest suite (unchanged test count, all passing)
  • Typecheck clean across all 8 workspace packages

Move the telemetry/cache/context wiring out of the Plugin constructor into a
new attachContext(deps) method called by AppKit after construction. Lets
plugin factories eagerly construct instances at module top-level without
depending on TelemetryManager/CacheManager being initialized yet.

- Plugin.constructor stays pure by default but opportunistically binds
  telemetry+cache if the core services are already initialized (backward
  compat with existing tests that new PluginX(config) directly).
- Plugin.attachContext(deps) is idempotent and does the full binding,
  including context. AppKit._createApp calls it after construction.
- ServerPlugin overrides attachContext to register HTTP/Express
  instrumentations and become the route target (previously in its
  constructor).
- BasePlugin.attachContext added to the shared interface as optional.

Also defer ServerPlugin's start() to the 'setup:complete' lifecycle hook
when running through AppKit. Previously start() ran inside setup() and
extendRoutes() iterated context.getPlugins() synchronously — so any
deferred-phase plugin declared after server() in the plugin array was
invisible to the route-mount loop and its /api/<name>/* routes silently
dropped. The fix also keeps the pre-AppKit path (direct new
ServerPlugin(...) construction) working by falling back to immediate
start() when this.context is undefined.

No behavior change when used through createApp. Enables upcoming
.toolkit() support on plugin factories where the factory eagerly
constructs the plugin instance at call time.

Signed-off-by: MarioCadenas <MarioCadenas@users.noreply.github.com>
This was referenced Apr 21, 2026
@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