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 withfinishReason: 'guardrail'andresult.guardrail({ name, kind, reason, toolName? }).result.textis''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()emitsguardrail.trippedbeforerun.done.action: 'rewrite'replaces the text withreplacement(a tool call’s arguments:replacementis the new arguments as JSON), the next guardrail sees the new text, andstream()emitsguardrail.rewrote.
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 withrequiresSandbox: 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.SubprocessSandboxruns each command in a new, network-isolated, auto-removed Docker container, with no host directory mounted except thecwdyou pass. It needs a running Docker daemon and the optional peerdockerode(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.
SandboxShell puts the workspace shell tool behind the same
adapter; see Workspace tools.