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

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. 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 extracts the preset field from the request payload. It calls getPresetFromTools() (defined in 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, 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, 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:

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 and restricts the session accordingly.

Switching Presets on Active Sessions

Change tool permissions dynamically using the setTools command:

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

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 implement filtering logic using the preset definitions:

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 to determine visibility.

Overriding via System Commands

Modify the preset mid-conversation using system messages:

{
  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.

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 and on the client through 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 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. 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →