Octopus
Plugin Ordering
Plugins are registered in the order they are added with AddPlugin<T>(). Ordering controls DI service precedence, startup sequencing, and shutdown sequencing. Getting this wrong causes startup failures or silent misbehaviour.
Why Ordering Matters
Plugin ordering affects three distinct behaviours:
| Behaviour | Effect of Order |
|---|---|
| DI registration | When two plugins register the same interface, the last registration wins. This is how you override built-in services. |
| OnStartAsync sequence | Plugins start in registration order. A plugin that depends on another plugin's services must be registered after it. |
| OnStopAsync sequence | Plugins stop in reverse registration order. Last registered, first stopped — ensuring dependents clean up before dependencies. |
Recommended Registration Order
builder.Services.AddOctopus(config =>
{
// ── Tier 1: Core storage (everything else depends on these) ──────────
config.AddPlugin<SqlServerPlugin>(); // Must be first
// Registers: OctopusDbContext, IEpisodicMemoryStore,
// IProceduralMemoryStore, IAgentStore, IAIFunctionStore
// Runs EF Core migrations in OnStartAsync
// ── Tier 2: AI / Semantic layer ──────────────────────────────────────
config.AddPlugin<SemanticKernelPlugin>(); // Requires IAgentStore from SqlServerPlugin
// Registers: ISemanticMemoryStore, IEmbeddingProvider
// Ensures vector collections exist in OnStartAsync
// ── Tier 3: UI and integration features ──────────────────────────────
config.AddPlugin<ChatbotUIPlugin>(); // Requires SqlServerPlugin (session storage)
config.AddPlugin<ProcessPlugin>(); // Requires SqlServerPlugin + ChatbotUIPlugin (SSE)
config.AddPlugin<WebDriverPlugin>(); // No dependency on other built-ins
// ── Tier 4: Your custom plugins (last) ───────────────────────────────
config.AddPlugin<MyHRPlugin>(); // Uses IAgentStore, MCPToolRegistry
config.AddPlugin<MyInventoryPlugin>(); // Depends on MyHRPlugin? Register after it.
});
Dependency Rules
| If Your Plugin Uses... | Register After... |
|---|---|
OctopusDbContext, IAgentStore, IEpisodicMemoryStore | SqlServerPlugin |
ISemanticMemoryStore, IEmbeddingProvider | SemanticKernelPlugin |
ISSEResponseWriter, IChatUIRenderer | ChatbotUIPlugin |
IWorkflowTriggerService, IOctopusHILActor | ProcessPlugin |
IBrowserSession, IPlaywrightManager | WebDriverPlugin |
| Another custom plugin's services | That custom plugin |
Service Override Pattern
Because the DI container uses the last registration for an interface, you can override any built-in service by registering after the plugin that provides it:
// Override IEpisodicMemoryStore with a custom Redis-backed implementation
config.AddPlugin<SqlServerPlugin>(); // Registers default IEpisodicMemoryStore
// Your plugin's OnRegister runs after SqlServerPlugin's:
public void OnRegister(IServiceCollection services, OctopusConfig config)
{
// This supersedes the SqlServerPlugin registration
services.AddScoped<IEpisodicMemoryStore, RedisEpisodicMemoryStore>();
}
// ✓ RedisEpisodicMemoryStore will be resolved for IEpisodicMemoryStore everywhere
Override responsibility. When you override a service, your implementation must satisfy the full contract of the interface. Check what methods the Octopus core calls on the interface and ensure all are implemented correctly.
What Happens with Wrong Order
| Mistake | Symptom | Fix |
|---|---|---|
Custom plugin registered before SqlServerPlugin | InvalidOperationException: IAgentStore is not registered | Move SqlServerPlugin first |
SemanticKernelPlugin before SqlServerPlugin | IAgentStore not found during OnStartAsync vector collection setup | Always register SqlServerPlugin first |
| Override plugin registered before the plugin it overrides | Built-in implementation is used instead of your override (last wins) | Register your plugin after the one you override |
| Plugin A depends on Plugin B's service, registered before B | OnStartAsync resolves null or throws because B has not run yet | Register B before A |
Startup and Shutdown Order Summary
1
OnRegister: SqlServerPlugin → SemanticKernelPlugin → ChatbotUIPlugin → ProcessPlugin → WebDriverPlugin → Custom
DI services registered in this order. Last registration wins for duplicate interfaces.
2
OnStartAsync: same order (SqlServerPlugin first)
SqlServerPlugin runs migrations, creates Octopus tables. SemanticKernelPlugin creates vector collections. Custom plugins register MCP tools.
3
OnStopAsync: reverse order (Custom first → SqlServerPlugin last)
Custom plugins clean up first. Core storage plugins stop last to ensure any in-flight persistence completes before the database context closes.