Lifecycle Hooks
Section titled “Lifecycle Hooks”Every agent execution flows through a deterministic 12-phase lifecycle. Hooks let you intercept any phase to add logging, metrics, validation, or custom behavior.
Quick Example
Section titled “Quick Example”import { ReactiveAgents } from "reactive-agents";
const agent = await ReactiveAgents.create() .withProvider("anthropic") .withReasoning() .withHook({ phase: "think", timing: "after", // Plain function — no Effect import needed. // Return nothing to observe, or return the (modified) context to change it. handler: (ctx) => { console.log(`Iteration ${ctx.metadata.stepsCount}`); }, }) .build();Available Phases
Section titled “Available Phases”| Phase | When It Runs | Common Hook Use Cases |
|---|---|---|
bootstrap | Before anything else | Load external config, validate preconditions |
guardrail | Input safety check | Log blocked inputs, custom filtering |
cost-route | Model tier selection | Override routing decisions |
strategy-select | Strategy selection | Log which strategy was chosen |
think | Each reasoning iteration | Progress logging, custom metrics |
act | Tool execution | Tool call tracking, audit logging |
observe | Process tool results | Result validation, caching |
verify | Output fact-checking | Custom verification logic |
memory-flush | Persist memories | Custom memory operations |
cost-track | Cost accounting | Budget alerts, cost telemetry |
audit | Decision audit trail | Rationale logging, compliance |
complete | Final result assembly | Post-processing, cleanup |
Hook Timing
Section titled “Hook Timing”Each phase supports three timing points:
before— Runs before the phase executes. Can modify theExecutionContext.after— Runs after the phase completes successfully. Receives the updated context.on-error— Runs when the phase throws an error. Can log or clean up, but cannot prevent the error from propagating.
Hook Handler Signature
Section titled “Hook Handler Signature”handler: (ctx: ExecutionContext) => | ExecutionContext | void | Promise<ExecutionContext | void> | Effect.Effect<ExecutionContext, ExecutionError>The handler receives the current ExecutionContext. Return the (possibly modified) context to change execution, or return nothing (void) to observe without side-effects. The Effect form is also accepted. Useful fields include:
metadata— step count, strategy, last response, reasoning results (engine-populated)toolResults— tool execution results accumulated this runmessages— conversation messages for the tasktaskId/agentId/sessionId— correlation identifiers
Agent-visible working memory is the recall meta-tool (Conductor’s Suite), not a field on this context.
Ordering
Section titled “Ordering”Hooks registered for the same phase and timing run sequentially in registration order. If a hook fails:
beforehook failure: the phase is skipped and theon-errorhook runsafterhook failure: logged but does not affect the phase resulton-errorhook failure: logged but does not mask the original error
Practical Patterns
Section titled “Practical Patterns”Progress Logging
Section titled “Progress Logging”// …then chain on your builder:.withHook({ phase: "think", timing: "before", handler: (ctx) => { const step = ctx.metadata.stepsCount + 1; const max = ctx.maxIterations ?? 10; console.log(`Step ${step}/${max}`); // Return nothing — just observing. },})Cost Alert
Section titled “Cost Alert”.withHook({ phase: "complete", timing: "after", handler: (ctx) => { if (ctx.cost > 0.10) { console.warn(`⚠ Execution cost $${ctx.cost.toFixed(3)} exceeded $0.10 threshold`); } // Return nothing — just observing. },})Audit Trail
Section titled “Audit Trail”.withHook({ phase: "act", timing: "after", handler: (ctx) => { const last = ctx.toolResults.at(-1) as { toolName?: string } | undefined; const toolName = last?.toolName ?? "unknown"; auditLog.append({ event: "tool_call", tool: toolName, taskId: ctx.taskId, timestamp: Date.now() }); // Return nothing — just observing. },})Error Handling
Section titled “Error Handling”.withHook({ phase: "think", timing: "on-error", handler: (ctx) => { console.error(`Think phase failed at step ${ctx.metadata.stepsCount}. Check your prompt or model.`); // Return nothing — just observing the error. },})