None for no opinion. Pass policies to the workspace, or add and
remove them later:
preCommand, ctx.sessionId).
Shell hooks
These fire when an agent runs a line:ws.shell, session.shell, the
shell, ls and grep tools, mirage shell, HTTP /shell, RPC
shell and SSH.
Every command on a line passes
pre_command before any of them runs, so
a line refused whole never runs halfway.
pre_command
git push --force origin main gets git: Permission denied, exit 126.
pre_execute
python3 /jobs/train.py runs on the workspace’s docker runtime. See
Route policy for runtimes.
post_execute
output truncated at limit (100 lines); ....
pre_session
export AWS_SECRET=x gets AWS_SECRET: permission denied, exit 1.
VFS hooks
These fire on every VFS call:session.vfs, the read, write, edit
and glob tools, mirage vfs, HTTP /vfs/<call>, RPC vfs/<call>,
FUSE, FSKit and SFTP. pre_vfs also fires for every file a shell command
reads or writes.
pre_vfs fires thousands of times under one grep -r, so keep it cheap.
An Ask from it reaches a host only from session.vfs or a file tool;
inside a line it refuses.
pre_vfs
session.vfs.write("/reports/q3.md", ...) raises EACCES.
post_vfs
session.vfs.read returns at most the first 1 MB. post_vfs does not
see the reads inside a command, so cat is not cut; bound a line with
post_execute.
Answers
TypeScript returns
{ kind: 'deny', reason }, with
scope: 'operand', path for an operand, { kind: 'ask', reason },
{ kind: 'route', runtime } and new Limit({ maxLines }). A hook that
raises refuses too.
The reason never goes to stderr. It is on the result’s refusal:
policy denied: <reason>, or
requires approval: <reason> (ask <id>).
When policies disagree
- Any Deny beats any Ask, so answering an ask never reopens a refusal.
- The first Deny speaks. Built-ins come first, then the session’s
profile, then
policies=, thenadd(). - Limits merge to the tightest value of each field.
- Two Routes to different runtimes refuse the line.
Which door enforces what
A line fires every hook; a VFS call fires only the VFS hooks. So a profile rule that names a command holds only on a line, and a rule that names only paths holds on both.session.tools.names() lists only the tools the session’s profile leaves
it, and MCP, RPC and the agent adapters offer exactly those.
Examples
policy.py and policy.ts run a coded policy beside a profile’spolicy: script. Their output is
pinned in CI.