Permissions and consent
For each effect of each plugin, you decide whether the agent may act directly, must ask first, or may not act at all. Scheduled runs only read.
Once you accept the terms during first-run setup, the agent may use your enabled plugins on your behalf.
You can still control in detail what the agent may do. Each plugin has a separate setting, called a mode, for each effect. For example, you can let the agent check your campus-card balance directly but ask you before it recharges the card. You change modes in Settings → Plugins → plugin → AI permissions.
The three modes
The mode decides what happens when the agent calls a tool with that effect.
| Mode | Result |
|---|---|
auto | The tool runs directly |
ask | An approval prompt appears first |
off | The call is refused |
The approval prompt shows the plugin, the tool, the effect and the inputs. It offers three choices: Allow, Deny and Always allow.
When the mode is off, the call is refused and the agent is told that the tool is blocked.
Default modes
Each kind of plugin has its own defaults.
| Plugin kind | read | write | money | public |
|---|---|---|---|---|
| Bundled plugin | auto | auto | auto | auto |
| Official plugin document | auto | auto | auto | auto |
| Community plugin document | auto | ask | ask | ask |
| Remote plugin | auto | ask | ask | ask |
A plugin can change its own defaults with defaultModes.
- For bundled plugins and official plugin documents,
defaultModesapplies as declared, for example{ money: 'ask' }. Effects that it doesn't mention stayauto. - Community plugin documents can also set
defaultModes, but only settings stricter than the table above take effect.
If users would want to confirm an action first, declare a stricter default for it:
definePlugin({
// …
defaultModes: { money: 'ask', public: 'ask' },
});Who is asked, and when
Whether a call runs is decided in one place only, the host's invoke path. The host checks in this order.
- The tool must exist, and its plugin must be available (its capabilities are met) and enabled.
- A
userOnlytool is refused unlessactor === 'user'. - The input is validated against the tool's zod schema.
- If the mode is
off, the call is refused. - If
actor === 'scheduler'and the effect is notread, the call is refused. Unattended runs can only read. - If the mode is
askandactor === 'scheduler', the call is declined, because there is nobody to ask. - If the mode is
askandactor === 'agent', an approval prompt appears. If you deny it, the agent receives a "declined" error. - If
actor === 'user', there is never a prompt, because tapping a button already shows what you want.
Tools that always ask
Two kinds of tool ask on every agent call, whatever the mode says. auto never skips the prompt,
off still blocks the call, and scheduled calls are refused.
- A
granttool gives the agent access it doesn't have yet, for examplecopilot.requestApp, which adds an app to Copilot's scope. Its prompt offers Allow once, Always allow and Don't allow. The answer reaches the tool asctx.grant('once'or'always') and never changes the plugin's modes. - An
alwaysAsktool is for actions the host can't judge from an observed target. Its prompt has no "always allow" option.
When a tool's resolveEffect names a target (for example the button Copilot is about to tap), the
prompt shows it as the headline and doesn't offer "always allow" either.
Consent beyond tools
Besides tool calls, a few other things need your consent separately.
Campus services. When you sign in to your campus account, you allow the app to reach campus
services. A plugin declares the sites and services it uses (scopes) and
requires: ['campus-session']. These are shown on the plugin's page.
PKU harness Cloud. Each plugin has its own Allow cloud switch. Next to the switch, the app
lists the plugin's disclosure lines, which say in plain words what the plugin stores online. A
cloud call made without consent throws CloudError('consent_required'). See
Cloud.
Files. Only you can choose files. A plugin may call services.files.pick from a screen, and
never from a tool that the agent calls. Documents you attach in chat are read into text on the phone, and the agent
pages through and searches them with the core files plugin's tools. Those tools return third-party
content, so calling one marks the agent's turn as tainted.
Notifications and exact alarms. These are granted through the system's permission prompts.
A plugin shows the prompt by calling services.notify.fix(kind) when you tap.
Memory. If the agent writes a memory after it has read untrusted content, the memory is saved as pending. It takes effect only after you approve it on the memory screen.
Settings, including the modes themselves. The agent can change settings through the core
settings plugin: the app's appearance, language and time zone, the model, each plugin's switch,
each plugin's modes and each plugin's preferences. The change tool has the write effect, so it
follows the settings plugin's own mode, which is auto by default like any bundled plugin. A user
who wants to confirm every settings change sets that mode to ask. Secrets and steps that need the
user's hands (API keys, the campus account, system permissions) are uiOnly: the agent can only
open their screen. See Architecture → The agent operates everything.
Rules for plugin authors
- You must declare each tool's effect honestly. The
moneyandpubliceffects exist so that users can keep those actions behind a confirmation. - You must not hide a write inside a
readtool, for example "read and mark as seen". - You should set
defaultModestoaskformoneyandpublic, unless the action is easy to undo. - You should split a tool that both reads and writes into two tools.
- You must use
userOnlyfor actions that the agent must not start itself, for example approving a memory the agent wrote.
Tools and effects
Tools are the only way a plugin offers features. Each tool has an id, a title, a description for the model, a zod input schema and exactly one effect (read, write, money or public).
Capabilities
Plugins declare in `requires` what they need from the phone. If the phone can't provide it, the harness hides the plugin and tells the user why.