Skip to main content
Tools for running agents safely: input and output guardrails check what goes into and comes out of any run (and its tool arguments), patch guardrails are fail-closed checks over a proposed change before you act on it, and sandboxed tools run inside an isolated container instead of the host process. For per-call human decisions see Approvals; for hooks that inspect or veto each tool call see HookRegistry in the API overview.

Input and output guardrails

createAgent({ guardrails }) (or ExecuteOptions.guardrails) checks a run at three points, each list in order: A guardrail is { name, check(ctx) }. ctx has kind ('input', 'output' or 'tool'), text, the run’s messages, toolName and args for a tool call, and the run’s signal. check returns (or resolves to) { ok: true } or { ok: false, reason, action?, replacement? }:
  • action: 'block' (the default) ends the run with finishReason: 'guardrail' and result.guardrail ({ name, kind, reason, toolName? }). result.text is '' and a blocked reply is not added to the transcript; a blocked tool call does not run (nor do the calls after it in that turn) and gets a “cancelled” result, so the transcript stays valid. stream() emits guardrail.tripped before run.done.
  • action: 'rewrite' replaces the text with replacement (a tool call’s arguments: replacement is the new arguments as JSON), the next guardrail sees the new text, and stream() emits guardrail.rewrote.
With onTripped: 'throw' a block rejects with GuardrailError (LOUSHO_GUARDRAIL_TRIPPED, with the same guardrail) instead. A check that throws fails the run. Sub-agents run their parent’s guardrails, then their own. Agent spec files name built-in guardrails in policy.guardrails (see Configuration).

Patch guardrails

runGuardrails(action, guardrails) runs every check concurrently over a proposed patch and rolls the results up into one verdict. The ops-pipeline example uses it to gate a fixer agent’s patch before a pull request is opened:
Fail-closed. A guardrail that throws, rejects, does not settle within its timeout (30 s by default), or resolves with anything but pass: true counts as failed: runGuardrailSafely(guardrail, action) wraps each one. A command guardrail kills its process when the timeout fires (on Windows, the whole process tree). Write your own as { name, check(action) } returning { pass, reason? }.

Sandboxed tools

A tool opts in with requiresSandbox: true and a sandboxExecute(args, sandbox) function. The executor then calls sandboxExecute with the run’s SandboxAdapter instead of calling execute:
  • NoopSandbox (the default) runs on the host. It is a stand-in, not isolation.
  • SubprocessSandbox runs each command in a new, network-isolated, auto-removed Docker container, with no host directory mounted except the cwd you pass. It needs a running Docker daemon and the optional peer dockerode (npm install dockerode@^5.0.1; loaded on first use). When the run is cancelled (run.abort(), signal) or the command times out, the container is killed and removed.
  • Implement SandboxAdapter (name, run(cmd, args, opts), writeFile(path, content)) for another backend.
For coding agents, SandboxShell puts the workspace shell tool behind the same adapter; see Workspace tools.