Plugin System Lifecycle hooks for metrics, audit trails, tracing, and custom processing.
Cortex uses an opt-in plugin system where extensions subscribe to lifecycle events by implementing specific interfaces. The base interface requires only a Name() method — all hooks are optional.
type Extension interface {
Name () string
}
There are 16 hook interfaces organized by category. Extensions implement only the hooks they need.
Interface Method When it fires RunStartedOnRunStarted(ctx, agentID, runID, input)Agent run begins RunCompletedOnRunCompleted(ctx, agentID, runID, output, elapsed)Run finishes successfully RunFailedOnRunFailed(ctx, agentID, runID, err)Run fails with error
Interface Method When it fires StepStartedOnStepStarted(ctx, runID, stepIndex)Reasoning step begins StepCompletedOnStepCompleted(ctx, runID, stepIndex, elapsed)Reasoning step finishes
Interface Method When it fires ToolCalledOnToolCalled(ctx, runID, toolName, args)Tool invocation begins ToolCompletedOnToolCompleted(ctx, runID, toolName, result, elapsed)Tool succeeds ToolFailedOnToolFailed(ctx, runID, toolName, err)Tool fails
Interface Method When it fires PersonaResolvedOnPersonaResolved(ctx, agentID, personaName)Persona loaded for run BehaviorTriggeredOnBehaviorTriggered(ctx, runID, behaviorName)Behavior fires CognitivePhaseChangedOnCognitivePhaseChanged(ctx, runID, from, to)Phase transition
Interface Method When it fires CheckpointCreatedOnCheckpointCreated(ctx, cpID, runID, reason)Checkpoint created CheckpointResolvedOnCheckpointResolved(ctx, cpID, decision)Checkpoint resolved
Interface Method When it fires OrchestrationStartedOnOrchestrationStarted(ctx, orchID, strategy)Multi-agent orchestration begins OrchestrationCompletedOnOrchestrationCompleted(ctx, orchID, elapsed)Orchestration finishes AgentHandoffOnAgentHandoff(ctx, orchID, from, to, payload)Agent-to-agent handoff
Interface Method When it fires ShutdownOnShutdown(ctx)Graceful shutdown
The plugin.Registry type-caches extensions at registration time:
registry := plugin. NewRegistry (logger)
registry. Register (metricsExt)
registry. Register (auditExt)
When events are emitted, only extensions implementing the relevant hook are called:
registry. EmitRunStarted (ctx, agentID, runID, input)
// Only calls extensions that implement RunStarted
Hook errors are logged but never propagated . Hooks must not block the agent pipeline:
// Inside registry.EmitRunStarted:
if err := hook. OnRunStarted (ctx, agentID, runID, input); err != nil {
r.logger. Warn ( "extension hook error" , "hook" , "OnRunStarted" , "error" , err)
}
observability.MetricsExtension — Counters for all lifecycle events
audithook.Extension — Bridges events to an audit trail backend
type SlackNotifier struct {
webhookURL string
}
func ( s * SlackNotifier ) Name () string { return "slack-notifier" }
func ( s * SlackNotifier ) OnRunFailed ( ctx context . Context , agentID id . AgentID , runID id . AgentRunID , err error ) error {
// Send Slack notification
return postToSlack (s.webhookURL, fmt. Sprintf ( "Agent %s run %s failed: %v " , agentID, runID, err))
}
// Register:
eng, _ := engine. New (
engine. WithExtension ( & SlackNotifier {webhookURL: "https://hooks.slack.com/..." }),
)