Skip to content

Production Deployment Checklist

Last updated 3 days ago · 80337d2

Updated 3 days ago

"fix(debt): Wave 1 — stop the lies (public API + published claims)" · 80337d2 · 2026-07-19

  • ### Tool Approval Gates (human-in-the-loop)

This checklist covers the builder methods and configuration options you should evaluate before deploying a Reactive Agents application to production. Each section is independent — enable the layers that match your threat model and reliability requirements.

Guardrails screen every prompt and response for prompt injection, PII leakage, and toxic content. Enable with .withGuardrails(). Each detector can be toggled independently; pass a customBlocklist to reject additional phrases.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withGuardrails({
injection: true,
pii: true,
toxicity: true,
})
.build();

A GuardrailViolationDetected event is emitted on the EventBus whenever a check fires, so violations surface in your observability pipeline automatically.

Behavioral contracts constrain what the agent is allowed to do at runtime. Use a tool deny list to block dangerous tools, cap iterations, and restrict output patterns.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withBehavioralContracts({
deniedTools: ["shell-execute"],
maxIterations: 20,
maxToolCalls: 40,
})
.build();

Gate high-risk tool calls behind human approval. Name the tools in .withApprovalPolicy() (with .withDurableRuns()): a gated call pauses the run, persists awaiting-approval, and run() returns status: "awaiting-approval" + pendingApproval. A human approves or denies — from this process or any other — and the run resumes from its checkpoint.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withTools({ tools: [databaseWriteTool] })
.withDurableRuns()
.withApprovalPolicy({ tools: ["database-write"], mode: "detach" })
.build();
const result = await agent.run("purge stale records");
if (result.status === "awaiting-approval") {
await agent.approveRun(result.pendingApproval.runId); // or denyRun(runId, reason)
}
// Or, in one call: agent.run(task, { onApproval: ({ toolName }) => confirm(toolName) })

See Durable Human-in-the-Loop for the full flow.

The per-tool requiresApproval: true flag is metadata only — it does not by itself pause a run. Use .withApprovalPolicy({ tools: [...] }) to gate.

Restrict the agent to a fixed set of tools. Any tool not in the list is invisible to the LLM and cannot be called, regardless of what the model requests.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withTools({ allowedTools: ["web-search", "file-read"] })
.build();

The kill switch enables programmatic lifecycle control. Call agent.stop() for a graceful exit that completes the current step, or agent.terminate() for an immediate halt.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withKillSwitch()
.build();
// Graceful stop from a signal handler or timeout
process.on("SIGTERM", () => agent.stop());

The default iteration cap is 10. Increase it for complex multi-step tasks, or lower it for latency-sensitive paths.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withMaxIterations(20)
.build();

Set a wall-clock timeout in milliseconds. The agent throws a TimeoutError if the run does not complete within the limit.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withTimeout(60_000) // 60 seconds
.build();

Configure automatic retries on transient failures (network errors, rate limits). Exponential backoff is applied between attempts.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withRetryPolicy({ maxRetries: 3, backoffMs: 1000 })
.build();

Set per-request and daily token budgets. The agent performs a pre-flight budget check before each run and a per-iteration check during the ReAct loop. A BudgetExceededError is thrown on overspend.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withCostTracking({
perRequest: 0.10, // USD
daily: 5.00, // USD
})
.build();
try {
const result = await agent.run("Analyze the Q4 sales report");
} catch (e) {
if (e instanceof BudgetExceededError) {
console.error("Budget exceeded:", e.message);
// escalate, alert, or degrade gracefully
}
}

With complexity routing enabled, simple queries are automatically routed to a cheaper model tier, reserving your primary model for tasks that need it.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withModelRouting()
.build();

Enable the metrics dashboard to get a structured execution summary after every run: phase timing, tool call counts, token usage, estimated cost, and smart alerts.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withObservability({ verbosity: "normal", live: true })
.build();

The dashboard is driven entirely by the EventBus. No manual instrumentation is required — MetricsCollector auto-subscribes to ToolCallCompleted and phase lifecycle events.

Call agent.exportMetrics() to retrieve metrics programmatically for forwarding to an external monitoring system (Prometheus, Datadog, etc.).

const result = await agent.run("Process batch job");
const metrics = await agent.exportMetrics();
// Forward to your monitoring pipeline
await metricsClient.record({
agentId: "prod-agent",
duration: metrics.totalDurationMs,
tokens: metrics.totalTokens,
cost: metrics.estimatedCostUsd,
steps: metrics.stepCount,
});

Register a handler to capture all agent errors in one place. The handler receives the error and a context object with task metadata for structured logging.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withErrorHandler((error, ctx) => {
logger.error(error.message, {
taskId: ctx.taskId,
agentId: ctx.agentId,
iteration: ctx.iteration,
});
metrics.increment("agent.error", { type: error.constructor.name });
})
.build();

RuntimeErrors is the exhaustive union of all errors the agent can throw. Use it for type-safe catch blocks.

import { RuntimeErrors } from "@reactive-agents/runtime";
try {
const result = await agent.run(prompt);
} catch (e) {
const error = e as RuntimeErrors;
switch (error._tag) {
case "BudgetExceededError":
// degrade gracefully or queue for later
break;
case "GuardrailViolation":
// return a safe fallback response
break;
case "MaxIterationsError":
// return partial result if available
break;
default:
throw e;
}
}

When running Effect-based code directly, use unwrapError() to extract a clean message from an Effect FiberFailure, and errorContext() to retrieve actionable remediation hints.

import { unwrapError, errorContext } from "@reactive-agents/runtime";
try {
const result = await agent.run(prompt);
} catch (raw) {
const error = unwrapError(raw);
const ctx = errorContext(raw);
console.error(error.message);
if (ctx?.suggestion) {
console.info("Suggestion:", ctx.suggestion);
}
}

The "enhanced" memory tier activates semantic search, episodic recall, and procedural memory in addition to working memory. It requires embedding support — set EMBEDDING_PROVIDER and EMBEDDING_MODEL in your environment.

Terminal window
EMBEDDING_PROVIDER=openai
EMBEDDING_MODEL=text-embedding-3-small
const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withMemory({ tier: "enhanced" })
.build();

Background consolidation merges and compacts memory entries over time, preventing unbounded growth and keeping retrieval quality high.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withMemory({ tier: "enhanced" })
.withMemoryConsolidation()
.build();

Cross-run experience learning stores task outcomes in the episodic layer and surfaces relevant prior experiences at the start of each new run, improving performance on repeated task types.

const agent = await ReactiveAgents.create()
.withProvider("anthropic")
.withMemory({ tier: "enhanced" })
.withExperienceLearning()
.build();

ConcernBuilder MethodDefaultProduction Recommendation
Prompt injection.withGuardrails()offEnable
Cost limits.withCostTracking({ budget })offSet per-request budget
Iteration limit.withMaxIterations(N)1020–50 for complex tasks
Min iterations.withMinIterations(N)none2–3 for research tasks
Output quality.withOutputValidator(fn)noneValidate structure for critical outputs
Answer verification.withVerificationStep()noneEnable for high-stakes decisions — on a REVISE verdict, reflect mode re-runs once with the verification feedback so the verdict shapes the final answer
Timeout.withTimeout(ms)none60_000–300_000
Retry.withRetryPolicy()none{ maxAttempts: 3 }
Observability.withObservability()offEnable with verbosity: "normal"
Error handler.withErrorHandler()noneSet for logging/alerting
Kill switch.withKillSwitch()offEnable for long-running agents