# How Tool Presets Work in Pi-Web: PRESET_NONE, READ_ONLY, DEFAULT, and FULL Explained

> Discover how tool presets like PRESET_NONE, READ_ONLY, DEFAULT, and FULL control AgentSession operations in pi-web. Understand server and client-side enforcement for secure tool access.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: deep-dive
- Published: 2026-08-17

---

**Tool presets in pi-web are static allow-lists that restrict which operations an AgentSession can invoke, ranging from no tools (PRESET_NONE) to full internal access (PRESET_FULL), with enforcement on both server and client sides.**

The **agegr/pi-web** repository implements a permission system that controls tool availability through four distinct presets. These presets determine whether an AI assistant can execute write operations, read-only queries, or internal management commands during a session.

## Understanding the Four Tool Presets

The preset system defines four permission levels in [`lib/tool-presets.ts`](https://github.com/agegr/pi-web/blob/main/lib/tool-presets.ts). Each preset maps to a specific string value sent during session initialization and translates to a filtered set of available tools.

### PRESET_NONE: Complete Tool Restriction

When a session uses `PRESET_NONE`, the assistant cannot invoke any tools. The server sends `preset: "none"` during session creation and injects a system message stating *"no tools for user"*. This mode is useful for pure conversational interactions where file system or code execution access is prohibited.

### PRESET_READ_ONLY: Query-Only Access

`PRESET_READ_ONLY` permits tools whose names do not end with `.write`. This allows read operations like `file.read` and `git.log` while blocking modifications. The preset string is `"readOnly"`, and both the server allow-list and client-side UI filter exclude any tool ending in the `.write` suffix.

### PRESET_DEFAULT: Standard Public Tools

As the default configuration for new sessions, `PRESET_DEFAULT` enables every public tool listed in the Pi-Web SDK. Sent as `preset: "default"`, this preset imposes no additional UI filtering and provides balanced access for general development tasks without exposing internal-only functions.

### PRESET_FULL: Internal and Public Access

`PRESET_FULL` grants access to all public tools plus internal-only operations such as `agent.fork` and `agent.setModel`. Used primarily for "daisy-chain" internal workflows, this preset sends `preset: "full"` and should be reserved for trusted automation scenarios where the UI itself manages agent lifecycle operations.

## How Tool Presets Are Applied in the Codebase

The preset logic flows through three critical layers during the session lifecycle.

### Server-Side Enforcement in rpc-manager.ts

When creating a session via `POST /api/agent/new`, [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) extracts the `preset` field from the request payload. It calls `getPresetFromTools()` (defined in [`lib/tool-presets.ts`](https://github.com/agegr/pi-web/blob/main/lib/tool-presets.ts)) to build the allow-list and constructs the appropriate system message based on the preset type.

### Client-Side Filtering in useAgentSession.ts

The client receives the active preset through the `agent-client` API via `session.getTools()`. In [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts), the UI filters the tool menu using `presetAllows()`, which implements the suffix-checking logic (e.g., rejecting `.write` tools for `READ_ONLY`).

### Persistence with tool-preset-preference.ts

User-selected presets persist across sessions through [`lib/tool-preset-preference.ts`](https://github.com/agegr/pi-web/blob/main/lib/tool-preset-preference.ts), which stores the chosen value in `localStorage`. When launching new sessions, the application reapplies the last-used preset automatically.

## Practical Examples for Working with Tool Presets

### Creating a Session with a Specific Preset

To launch a read-only session, send the preset string in the initialization payload:

```json
POST /api/agent/new
{
  "cwd": "/home/user/project",
  "message": "Explain the repository",
  "preset": "readOnly"
}

```

The server validates the preset against the enum in [`tool-presets.ts`](https://github.com/agegr/pi-web/blob/main/tool-presets.ts) and restricts the session accordingly.

### Switching Presets on Active Sessions

Change tool permissions dynamically using the `setTools` command:

```ts
await fetch(`/api/agent/${sessionId}`, {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ cmd: "setTools", preset: "full" })
});

```

[`agent-client.ts`](https://github.com/agegr/pi-web/blob/main/agent-client.ts) forwards this command to `AgentSession.setTools(preset)`, which rebuilds the allow-list without terminating the session.

### Filtering Tools in the UI

Components like [`ChatInput.tsx`](https://github.com/agegr/pi-web/blob/main/ChatInput.tsx) implement filtering logic using the preset definitions:

```tsx
const allowedTools = tools.filter(t => presetAllows(t.name, currentPreset));
return (
  <Menu>
    {allowedTools.map(t => (
      <MenuItem key={t.name}>{t.displayName}</MenuItem>
    ))}
  </Menu>
);

```

The `presetAllows` function references the mapping in [`lib/tool-presets.ts`](https://github.com/agegr/pi-web/blob/main/lib/tool-presets.ts) to determine visibility.

### Overriding via System Commands

Modify the preset mid-conversation using system messages:

```ts
{
  role: "system",
  content: [
    { type: "put", key: "preset", value: "none" }
  ]
}

```

The `system[:put]` handler updates the session header and immediately re-applies the new allow-list, as documented in [`AGENTS.md`](https://github.com/agegr/pi-web/blob/main/AGENTS.md).

## Summary

- **Four permission levels**: `PRESET_NONE`, `PRESET_READ_ONLY`, `PRESET_DEFAULT`, and `PRESET_FULL` provide granular control from complete restriction to full internal access.
- **Dual enforcement**: Presets apply on the server via [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) and on the client through [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts) to ensure consistent security boundaries.
- **String mapping**: Each enum value maps to specific preset strings (`"none"`, `"readOnly"`, `"default"`, `"full"`) used in API payloads.
- **Persistence**: User preferences store in `localStorage` via [`lib/tool-preset-preference.ts`](https://github.com/agegr/pi-web/blob/main/lib/tool-preset-preference.ts) for seamless session continuity.
- **Dynamic updates**: The `setTools` command and system message overrides allow runtime preset changes without session restarts.

## Frequently Asked Questions

### How do I restrict an assistant from modifying files in pi-web?

Apply `PRESET_READ_ONLY` when creating the session by sending `"preset": "readOnly"` in the `/api/agent/new` request. This filters out all tools ending with `.write` both on the server and in the UI, preventing file modifications while allowing read operations like `file.read` and `git.log`.

### Can I change the tool preset after a session has started?

Yes. Use the `setTools` command with a POST request to `/api/agent/${sessionId}` containing the new preset value. The backend updates the allow-list immediately via `AgentSession.setTools()`, and the client refreshes the available tools without requiring a new session.

### Where does pi-web store the user's last selected tool preset?

The application persists the chosen preset in the browser's `localStorage` through [`lib/tool-preset-preference.ts`](https://github.com/agegr/pi-web/blob/main/lib/tool-preset-preference.ts). When users create new sessions, the system automatically applies their previously selected preset mode.

### What is the difference between PRESET_DEFAULT and PRESET_FULL?

`PRESET_DEFAULT` enables all public tools available in the SDK, suitable for standard development tasks. `PRESET_FULL` additionally exposes internal-only tools like `agent.fork` and `agent.setModel` that manage agent lifecycle and model configuration, reserved for internal workflows and advanced automation.