Skip to content

guard

const guard: object

approve: (config) => Middleware = guardApprove

Human-in-the-loop tool approval.

Creates a guard.approve() middleware for human-in-the-loop tool approval.

Intercepts tool calls before execution for tools with requireApproval set. Delegates to the developer-supplied approval function which can approve, deny, or modify the tool call.

ApproveConfig

Approval configuration with the handler function

Middleware

Middleware with a tool hook

import { approve, deny, modify } from "agent-express"
agent.use(guard.approve({
approve: async (toolName, args) => {
if (toolName === "delete_all") return deny("Blocked")
return approve()
},
}))

budget: (config) => Middleware = budgetGuard

USD cost cap per session.

Creates a guard.budget() middleware that enforces a per-session USD cost limit.

Tracks accumulated cost across all model calls using token counts from LLM responses multiplied by per-model pricing (USD per 1M tokens).

BudgetConfig

Budget configuration with USD limit and optional pricing overrides

Middleware

Middleware that enforces cost limits

// Default: throws BudgetExceededError
agent.use(guard.budget({ limit: 0.50 }))
// Graceful stop: turn ends with empty text
agent.use(guard.budget({ limit: 0.50, onLimit: "stop" }))
// Custom handler:
agent.use(guard.budget({
limit: 1.00,
onLimit: (ctx, cost) => "Sorry, I've reached my budget limit.",
}))

input: (validator) => Middleware = inputGuard

Validate input before each LLM call.

Creates a guard.input() middleware that validates input before each LLM call.

Runs in the model hook before next(). If the validator returns { ok: false }, throws InputGuardrailError. If it returns modified messages, those replace the originals for this model call.

InputValidator

Async or sync validation function receiving ModelContext

Middleware

Middleware that validates input before each LLM call

agent.use(guard.input(async (ctx) => {
if (ctx.messages.some(m => typeof m.content === "string" && m.content.includes("ignore previous"))) {
return { ok: false, reason: "Potential prompt injection" }
}
return { ok: true }
}))

maxIterations: (max) => Middleware = guardMaxIterations

Limit model→tool→model iterations per turn.

Creates a guard.maxIterations() middleware that limits the number of model calls per turn.

Prevents runaway agent loops where the model repeatedly calls tools without converging. Uses a closure-based counter (not session state) that resets at the start of each turn.

When the limit is reached, the middleware strips tool calls from the last response so no unnecessary tool executions happen. If the model produced no text, the turn completes with an empty string.

number = 25

Maximum model calls allowed per turn. Default: 25.

Middleware

Middleware that enforces per-turn iteration limits

agent.use(guard.maxIterations()) // default: 25
agent.use(guard.maxIterations(10)) // custom limit

output: (validatorOrConfig) => Middleware = outputGuard

Validate output after each LLM response.

Creates a guard.output() middleware that validates each model response BEFORE tool calls are executed.

Accepts either a validator function (shorthand) or a config object (full control).

OutputGuardConfig | OutputValidator

Middleware

// Shorthand — blocked responses are replaced by default
agent.use(guard.output(async (response, ctx) => {
if (response.toolCalls?.some(tc => tc.toolName === "delete_all")) {
return { ok: false, reason: "Dangerous tool call blocked" }
}
return { ok: true }
}))
// Full config — throw on block
agent.use(guard.output({
validate: myValidator,
onBlock: "error",
}))

timeout: (config) => Middleware = guardTimeout

Turn and model call timeouts.

Creates a guard.timeout() middleware that enforces time limits on turns and individual model calls.

Throws TurnTimeoutError when a limit is exceeded. Timeouts are cleaned up via try/finally to prevent resource leaks.

TimeoutConfig = {}

Timeout configuration. Defaults: turn 120s, model 60s.

Middleware

Middleware that enforces time limits

agent.use(guard.timeout()) // defaults: turn 2min, model 1min
agent.use(guard.timeout({ turn: 30_000 })) // custom turn, default model
agent.use(guard.timeout({ turn: 30_000, model: 10_000 })) // both custom