Skip to content

Lifecycle Hooks

Last updated 11 days ago · 2f8f0ef

Updated 11 days ago

"docs: propagate createAgent dual API + examples-compile anti-rot gate (#57)" · 2f8f0ef · 2026-07-11

  • ## Hook Handler Signature
  • ### Progress Logging
  • ### Cost Alert
  • ### Audit Trail
  • ### Error Handling

Every agent execution flows through a deterministic 12-phase lifecycle. Hooks let you intercept any phase to add logging, metrics, validation, or custom behavior.

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();
PhaseWhen It RunsCommon Hook Use Cases
bootstrapBefore anything elseLoad external config, validate preconditions
guardrailInput safety checkLog blocked inputs, custom filtering
cost-routeModel tier selectionOverride routing decisions
strategy-selectStrategy selectionLog which strategy was chosen
thinkEach reasoning iterationProgress logging, custom metrics
actTool executionTool call tracking, audit logging
observeProcess tool resultsResult validation, caching
verifyOutput fact-checkingCustom verification logic
memory-flushPersist memoriesCustom memory operations
cost-trackCost accountingBudget alerts, cost telemetry
auditDecision audit trailRationale logging, compliance
completeFinal result assemblyPost-processing, cleanup

Each phase supports three timing points:

  • before — Runs before the phase executes. Can modify the ExecutionContext.
  • 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.
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 run
  • messages — conversation messages for the task
  • taskId / agentId / sessionId — correlation identifiers

Agent-visible working memory is the recall meta-tool (Conductor’s Suite), not a field on this context.

Hooks registered for the same phase and timing run sequentially in registration order. If a hook fails:

  • before hook failure: the phase is skipped and the on-error hook runs
  • after hook failure: logged but does not affect the phase result
  • on-error hook failure: logged but does not mask the original error
// …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.
},
})
.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.
},
})
.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.
},
})
.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.
},
})