Package
Plugin Migration Plan
How to move from the old capability-oriented design to service-owned plugin points
Plugin Migration Plan
This page is a practical migration guide for future code changes.
Migration Goal
Move from the old model:
- plugins expose capabilities
- services call capabilities
- explicit calls, hooks, and providers are mixed together
to the new model:
- services define their own extension points
- runtime provides
pipeline / guard / effect / resolve - plugins only implement those points
- assets only handle dependencies
Decision Rules
What should be a service
Prefer a service if any of these are true:
- it has lifecycle
- it actively participates in the agent execution cycle
- it is tightly coupled to current agent state
- it owns a primary workflow
What should be a plugin
Prefer a plugin if these are true:
- it only inserts logic at a workflow point
- it has no standalone lifecycle
- it does not run on its own
- it behaves more like middleware, validation, or a provider
What should be an asset
- models
- CLI dependencies
- external binaries
- low-level installation and configuration resources
How to Choose the Four Semantics
pipeline
Use for serial transformation.
Good for:
- inbound augmentation
- reply rewriting
- metadata enrichment
guard
Use for serial validation that interrupts on error.
Good for:
- authorization
- input validation
- pre-execution interception
effect
Use for side effects only.
Good for:
- observation
- metrics
- audit logs
resolve
Use for single-owner value resolution.
Good for:
- role resolution
- transcription
- any unique provider
Standard Migration Steps
- decide whether the logic should remain in a service
- if it is not a primary workflow, choose the matching plugin semantic
- define a stable point name in the service
- move the old plugin logic into
hooksorresolves - replace old capability calls with
runtime.plugins.* - push dependency install and availability checks into assets
- update
/docsor/devdocs
Pseudocode Template
export const TASK_PLUGIN_POINTS = {
beforeRun: "task.beforeRun",
afterRun: "task.afterRun",
resolveExecutor: "task.resolveExecutor",
} as const;
await runtime.plugins.guard(TASK_PLUGIN_POINTS.beforeRun, input);
const executor = await runtime.plugins.resolve(
TASK_PLUGIN_POINTS.resolveExecutor,
input,
);
await runtime.plugins.effect(TASK_PLUGIN_POINTS.afterRun, result);
export const somePlugin: Plugin = {
name: "some",
hooks: {
guard: {
[TASK_PLUGIN_POINTS.beforeRun]: [async ({ value }) => {
// ...
}],
},
effect: {
[TASK_PLUGIN_POINTS.afterRun]: [async ({ value }) => {
// ...
}],
},
},
resolves: {
[TASK_PLUGIN_POINTS.resolveExecutor]: async ({ value }) => {
return {};
},
},
};Existing Migrated Reference Cases
chat.observePrincipal: authchat.authorizeIncoming: authchat.resolveUserRole: authchat.augmentInbound: voice
What to Check Next
- whether a service still imports concrete plugin module paths directly
- whether a plugin is trying to own workflow instead of implementing service points
- whether dependency installation still leaks outside assets
- whether docs still describe capability as the core abstraction