> ## Documentation Index
> Fetch the complete documentation index at: https://docs.plungeai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# One Agent policy and review

> How a one-agent tool call becomes allow, ask or deny, and the safety layers around a run (posture, review, the denial breaker, secret redaction, untrusted-content screening).


Every tool call in a loop is classified, then decided. One function makes the decision, so every door (Studio, REST, MCP, the CLI, the scheduler) gets the same answer, and the rung that decided is written to the run's audit row.

## Classes and presets

A call has one of five classes: `read`, `write`, `send`, `delete` or `pay` (how each tool gets its class is on [Tools](/one-agent/tools#permission-classes)). The `permissions` map on a task, and the stored presets on a saved bot, give a class or a tool name one preset:

| Preset | Effect |
|---|---|
| `allow` | The call runs. |
| `ask` | The run pauses for approval. |
| `deny` | The call does not run; the model gets an error that names the reason. |
| `hand_off` | A `deny` that does not retry. The call never runs, and the run's result carries a `[handed off to you — not run: …]` line so the owner knows it is theirs to do. A hand-off does not count toward the denial breaker. |

A preset this build does not recognise is treated as `ask`, never `allow`. `hand_off` is understood only by One Agent; for any other agent the engine rewrites it to `deny`.

## The decision order

The first rung that applies decides:

1. **Posture `read_only`:** `write`, `send`, `delete` and `pay` are denied. So are `delegate`, `invoke_workflow` and every MCP tool, and any tool with an unknown class (fail closed).
2. **Explicit `deny`:** on the tool's name or on its class. Posture adds a class `deny` on every mutating class.
3. **`hand_off`.**
4. **The money floor:** an operation in the always-gated set (`buy`, `purchase`, `pay`, `transfer`, `send`, `send_payment`, `withdraw`, `fetch_paid`, `shop`) is always `ask`. No `allow` lowers it, and it pauses the run even when unattended.
5. **The preset:** the tool's own entry, then its class entry. Anything other than `allow` asks.
6. **An admin lock:** a locked class lifts `allow` to `ask`. It never lifts a `deny`.
7. **Otherwise `allow`.**

The presets on the task and the presets stored on the saved bot are merged before the run starts: the task's own entries win per class, the bot's locks replace what the YAML carried, and the lower per-run budget wins. See [Permissions and budget](/bots/permissions-and-budget).

## Posture

`posture: read_only` makes a run safe to point at the open web. A bot's own `permissions` cannot lift it, and an admin lock never turns a deny into an allow. The `browser` tool still reads pages under it. Any value other than `read_only` is forced to `read_only`: a typo never widens a run.

## Review

`review: auto` adds one model call that reviews each `write`, `send`, `pay` or `delete` call the permissions allowed. It answers allow, ask the owner (the normal approval pause) or deny (a tool error that names the reason). A reviewer error asks and never allows. Three denials in a row stop the run with `stop_reason: review`. It costs one model call per gated action. Any other value is forced to `auto`.

## The denial breaker

Three denied calls in a row, with no executed call between them, stop an unattended run with `stop_reason: denied`. An interactive run pauses and asks the person how to proceed. Any executed call resets the count.

## Secrets stay out of results

Tool results, spilled files, the outcome of an approval and the saved result pass through a redactor first. JWTs, bearer tokens and `sk-` or `ozk_` keys become `[REDACTED:<type>]`. Card numbers, IBANs and email addresses pass, because they are the owner's own data.

## Untrusted content

Text someone else wrote is data, not instructions. The results of `web_search`, `web_fetch`, `browser`, `message_bot`, every `mcp__*` tool and `call_agent` calls to search, MCP or scraping agents are scanned for instruction patterns. A result that matches is wrapped in an `<untrusted-content>` marker listing the patterns it carries; a clean result is returned as it is. Either way the run is marked tainted. Today the taint is recorded in the audit trail only: a later `write` or `send` call writes a row saying it would have been asked, and nothing is blocked because of it. `code` results are not treated as untrusted.

`web_fetch` refuses private and link-local addresses.

## Other run limits

| Limit | Key | Stops with |
|---|---|---|
| Spend per run | `budget_usd_run` | `stop_reason: budget` |
| Wall-clock time | `max_runtime_s` | `stop_reason: timeout` |
| Turns | `max_iterations` | the turn cap |

Cancelling a run also cancels the child runs it started with `delegate` or `message_bot wait: true`, and the OceanCode sessions it started with `code`.
