PluginsExamples
Plugin and Service Composition
Use a combined scenario to show the common split where plugins own extension points and services own long-lived state
Plugin and Service Composition
Scenario
You are building a "message review" capability:
- a plugin inserts guard and effect logic into an existing chain
- a service owns review cache state and statistics over time
The cleanest design often becomes:
- the plugin owns extension points
- the service owns long-lived runtime state
A common split
ReviewCacheService
- owns recent review results
- provides long-lived state actions such as
statusandclear
ReviewPolicyPlugin
- checks a guard point before the message enters the main chain
- records an effect when the event was observed
Why not make it all a plugin
Because a plugin should not automatically become your long-lived state owner for:
- worker state
- session pools
- instance-lifetime data
Why not make it all a service
Because a service is not the most expressive place for:
- pipeline
- guard
- effect
- resolve
A stable design takeaway
If one capability needs both:
- runtime-chain extension
- long-lived state
then plugin plus service collaboration is often the right split.
A minimal implementation skeleton
import { Agent, BaseService, type Plugin } from "@downcity/agent";
class ReviewCacheService extends BaseService {
readonly name = "review_cache";
}
const reviewPolicyPlugin: Plugin = {
name: "review_policy",
title: "Review Policy",
description: "Adds guard and effect logic to the review flow.",
hooks: {
guard: {
"review.require_checked": [
async ({ value }) => {
const body = value as { reviewed?: unknown };
if (body.reviewed !== true) {
throw new Error("review is required before continuing");
}
},
],
},
},
};
const agent = new Agent({
id: "repo-helper",
path: "/path/to/project",
tools: {},
services: [new ReviewCacheService()],
plugins: [reviewPolicyPlugin],
});