---
title: "What an agent combines"
description: "An agent that reads private data, takes in outside input and can send without anyone asked is flagged on the install screen, the Configure tab and its own page, with the functions that would close it. A flag never refuses an install."
canonical_url: "https://docs.oz0.ai/build/parts/what-an-agent-combines"
---
# What an agent combines

> An agent that reads private data, takes in outside input and can send without anyone asked is flagged on the install screen, the Configure tab and its own page, with the functions that would close it. A flag never refuses an install.

For: plugin authors.

Review an agent's access as a combination. Private data, outside input and functions that send information can expose data when the same agent has all three:

- private data: something the tenant would not publish;
- outside input: text from people or systems outside the tenant, which can carry instructions to the model;
- sending out: a function that writes, posts or sends.

An agent that holds all three can be steered by text from outside into sending what it read somewhere it should not
go, in one job. The platform works out what each agent combines and flags an agent that holds all three while at
least one of its sending functions runs without anyone asked.

Flags show combinations worth a second look. No flag does not mean an agent is safe.

## The three ingredients

The check reads the agent's functions: its own plugin's, and other plugins' that its plugin is granted under `uses:`.
Once the plugin is installed, another plugin's function that its grant does not reach (never granted, or revoked
since) shows as Refuse, since its agents' calls of it are refused. It also reads what starts the agent, from any
installed plugin: a flow of another plugin that runs the agent counts as much as one of its own.

<table>
<thead>
  <tr>
    <th>
      Ingredient
    </th>
    
    <th>
      What counts
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Private data
    </td>
    
    <td>
      A function whose tool reaches a system through one of its plugin's <a href="https://docs.oz0.ai/admin/connections">
        connections
      </a>
      
      . A function whose tool declares a label source (<code>
        labels.source
      </code>
      
      ), when the tenant's default level ranks above its lowest one, as the built-in levels do.
    </td>
  </tr>
  
  <tr>
    <td>
      Outside input
    </td>
    
    <td>
      A function whose results come from outside the tenant: one that does not say <code>
        openWorldHint: false
      </code>
      
      , or a remote server's, as <a href="https://docs.oz0.ai/govern/taint-and-labels">
        taint
      </a>
      
       reads them. A tool that says its results hold outside text (<code>
        labels.untrusted
      </code>
      
      , such as a mailbox). <code>
        oz.wait_for_event
      </code>
      
      . A start by a routine's webhook or event trigger, a flow's webhook or event trigger that runs the agent in a step, or a <a href="https://docs.oz0.ai/build/apps/chat-channels">
        chat channel
      </a>
      
       binding that names it.
    </td>
  </tr>
  
  <tr>
    <td>
      Sending out
    </td>
    
    <td>
      A function of type Write, Sends out, Destructive or Unknown.
    </td>
  </tr>
</tbody>
</table>

A function the tenant vouches for (`oz0 tenant labels <tenant> --trusted`) is not outside input, as it does not taint a job. A
schedule is not outside input: it starts the agent with the routine's own task.

The tenant's routines, its channel bindings and other plugins' flows are named only to someone who may read the
tenant (`tenant.read`). Anyone else, such as a role that may read plugins' settings and nothing more, sees the start
counted as outside input under the name "not visible to you".

A tool result can carry a confidentiality level of its own, and every result the platform has no level for gets the
tenant's default level. That default level does not count as private data: every agent would have it. Only a tool that
declares where its data comes from does.

### Why Unknown counts as sending out

A function that does not say what it does (no annotations, or not read-only and silent on `destructiveHint`) may do
any of the things the other types say. The check is only as good as the functions' annotations, so it takes the worst
case. An Unknown function asks a person by default, so it closes the flag until someone allows it.

## When an agent is flagged

The agent is flagged when it holds all three and a sending function's default is Allow. The default is the one the
install screen shows: the tenant's rule that covers the function, then the plugin's suggested policy, then what the
function declares; a function under the plugin's `requires_approval` always asks.

Any default but Allow
closes it: Ask, Refuse, and a plugin's suggested Judge, which is currently written as Ask and
so closes it.

This flag is about what an agent combines; the house rule
[OZ-S018](https://docs.oz0.ai/build/plugins/structure/lint-rules#oz-s018), which checks that a verified plugin's writes ask, counts only Ask and
Refuse, not a suggested Judge.

The flag names:

- the functions or starts behind each ingredient, and why each one counts;
- the sending functions that run without asking: switching each to Ask closes the flag.

The check looks at each function's own default. A job that read outside input also asks before tools that can send
data out, through the [taint default](https://docs.oz0.ai/govern/taint-and-labels), while that default is on. The flag still shows: it
covers a write that is not outbound, a tenant that turned the default off, and private data that taint does not track.

## Where it shows

- The install screen: a line per flagged agent in the May do block, with one action that switches the
closing functions to Ask on the screen before you install. The flag clears as soon as none is left on Allow.
`oz0 plugin install`, `oz0 plugin update` and `oz0 plugin preview` print the same, with the command that closes
each function. See [What the install shows first](https://docs.oz0.ai/build/plugins/install/review#what-the-install-shows-first).
- The Configure tab: above the Tools tab's groups, with the same action on the tab's switches, which you then
save as usual. See [Configure an installed plugin](https://docs.oz0.ai/build/plugins/configure#what-its-agents-combine).
- The agent's page: beside its capability card on the Permissions tab, with a way to its plugin's Configure
tab. An agent that is not flagged says what it holds of the three.
- `oz0 catalog`: a table of the flagged agents and the functions that send without asking.
- `plugin lint`: house rule [OZ-S020](https://docs.oz0.ai/build/plugins/structure/lint-rules#oz-s020) runs the same check on the plugin alone.

## A flag never refuses

A flag never refuses an install, an update or a job. Whether an agent may hold all three is the admin's decision: a
support agent may well need to read a ticket from a customer, look up the customer's record and post a reply. The
flag makes the combination visible, so the decision is made on purpose.

## Duty pairs beside it

The same places also show the pairs of the tenant's own [duty rules](https://docs.oz0.ai/govern/tool-policy/duties#duties) an agent holds: two
duties, each a set of functions, that the tenant does not want one agent to hold together, such as paying and
approving payments. They are worked out from the same functions as the three ingredients. A pair the tenant marks
refuse is the tenant's rule, not this check's: it refuses the install, the grant or the policy change that would
create it. The combination itself still only flags.

## How to close it

Pick one:

- Switch the sending functions to Ask with the flag's action, on the Configure tab, or with
`oz0 plugin policy <plugin> --set <function>=ask --reason "..."`. For another plugin's function, the switch is for
your plugin's agents only: `oz0 plugin policy <owner> --set <function>=ask --caller <plugin>`.
- Take an ingredient away: give the outside input to another agent that cannot send, or leave a function the
agent does not need out of its `tools:`.
- As a plugin's author, ask about the sending functions in the plugin's
[suggested policy](https://docs.oz0.ai/build/plugins/structure#a-suggested-policy). A verified capability must:
[OZ-S020](https://docs.oz0.ai/build/plugins/structure/lint-rules#oz-s020) fails lint at the verified level otherwise.

## What it does not see

- What a function does with a given call: a Write that only touches the tenant's own records counts as sending out.
- Delegation: an agent that hands work to another agent is checked on its own functions, and the other agent on its
own.
- A harness session's own tools (a shell, a web fetch) beyond the functions it is granted.
- A level a tool result declares in its own `_meta`, which is known only once a call returns.


## Sitemap

See the full [sitemap](https://docs.oz0.ai/sitemap.md) for all pages.
