# Pi Web Architecture: Understanding the Three-Layer Browser-Based AI Coding System

> Explore Pi Web architecture and its three-layer system: Browser UI, Next.js Server API, and AgentSession. Understand this innovative browser-based AI coding environment.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: architecture
- Published: 2026-07-30

---

**Pi Web is built on three distinct logical layers—a React-based Browser UI, a Next.js Server API layer, and an in-process AgentSession—that together deliver a responsive, file-aware AI coding environment directly in the browser.**

The `agegr/pi-web` repository implements a unique **Pi Web architecture** that bridges frontend interactivity with local AI processing. Unlike traditional client-server models that rely on external services, Pi Web hosts the core coding agent directly inside the Node.js process while exposing it through modern web APIs. This design enables real-time streaming responses, persistent session management, and secure filesystem access while maintaining a seamless browser-based user experience.

## The Three Core Layers of Pi Web

### Browser Layer: React UI and Event Handling

The **Browser layer** serves as the presentation tier, built with React components that capture user input and render streaming AI responses. It communicates exclusively through Next.js API routes using standard HTTP requests and Server-Sent Events (SSE).

Key entry points in this layer include:
- [`components/AppShell.tsx`](https://github.com/agegr/pi-web/blob/main/components/AppShell.tsx) – Provides the overall application layout and navigation structure
- [`components/ChatWindow.tsx`](https://github.com/agegr/pi-web/blob/main/components/ChatWindow.tsx) – Renders the conversational interface and message history
- [`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts) – Manages SSE connections, handles reconnection logic, and exposes session state to React components

This layer remains strictly concerned with UI concerns, delegating all AI processing and file system operations to the server tier.

### Next.js Server Layer: API Gateway and Session Management

The **Next.js Server layer** acts as the middleware tier, implementing REST-style endpoints that bridge the browser and the AI engine. According to the source code in `agegr/pi-web`, this layer handles session lifecycle, file browsing, worktree management, and model discovery while enforcing security policies.

Primary API routes include:
- `app/api/agent/[id]/route.ts` – Manages session state and accepts commands via POST requests
- [`app/api/sessions/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/sessions/route.ts) – Lists available sessions by reading from the filesystem
- `app/api/files/[...path]/route.ts` – Provides secure file viewing capabilities
- `app/api/agent/[id]/events/route.ts` – Establishes SSE streams for real-time agent events

The server layer delegates command execution to the in-process AgentSession while handling auxiliary tasks through helpers like [`lib/file-access.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-access.ts) (which enforces allow-listed filesystem roots) and [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts) (which manages Git worktree resolution).

### AgentSession Layer: In-Process AI Engine

The **AgentSession layer** constitutes the core intelligence of Pi Web, hosting a live instance of the Pi coding agent within the same Node.js process as the Next.js server. This layer processes prompts, executes tools, streams events, and persists session data to `.jsonl` files stored under `~/.pi/agent/sessions/`.

Critical implementation details:
- Session creation occurs through [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) via the `createAgentSessionFromServices` function
- Lifecycle management is handled by `AgentSessionWrapper`, which wraps raw agent instances
- Command processing happens through `session.prompt()`, which emits events like `agent_start`, `tool_call`, and `agent_end`
- Persistence uses JSON Lines format for append-only session history

This architecture eliminates network latency between the API layer and the AI engine, enabling sub-millisecond communication for tool execution and response streaming.

## How the Layers Interact

The data flow between layers follows a specific pattern defined in the project's [`AGENTS.md`](https://github.com/agegr/pi-web/blob/main/AGENTS.md) documentation. When a user sends a message, the request travels through a carefully orchestrated pipeline:

1. **UI to Server**: The browser issues a `POST /api/agent/[id]` request containing the user message. The route handler in `app/api/agent/[id]/route.ts` calls `startRpcSession()` from [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) to locate or instantiate an `AgentSessionWrapper`. Implementation details are found at lines 119-130 of [`rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/rpc-manager.ts).

2. **Server to AgentSession**: The wrapper forwards the command to `session.prompt()` inside the live AgentSession. The agent processes the request asynchronously, generating a stream of events that represent thinking steps, tool invocations, and final responses.

3. **AgentSession to Server (SSE)**: Events are captured by the wrapper and rebroadcast over an SSE connection established at `GET /api/agent/[id]/events`. The server maintains this persistent connection, pushing updates as the agent generates them.

4. **Server to Browser**: The React hook [`useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/useAgentSession.ts) consumes the SSE stream, parsing JSON events and updating the component state in real time. This creates the appearance of a live, typing assistant.

5. **File Operations**: For non-chat features like the file explorer, the browser calls dedicated routes (`/api/files`, `/api/worktrees`) that delegate to [`lib/file-access.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-access.ts). These helpers validate paths against an allow-list before touching the filesystem, ensuring the browser cannot access arbitrary system files.

## Code Examples

### Loading Sessions from the UI

To retrieve the list of available coding sessions, the React frontend queries the sessions endpoint:

```typescript
// Inside a React component
import { useEffect, useState } from "react";

function SessionList() {
  const [sessions, setSessions] = useState<SessionInfo[]>([]);

  useEffect(() => {
    fetch("/api/sessions")
      .then((r) => r.json())
      .then(setSessions);
  }, []);

  return (
    <ul>
      {sessions.map((s) => (
        <li key={s.id}>{s.name ?? "(unnamed)"}</li>
      ))}
    </ul>
  );
}

```

This endpoint is implemented in [`app/api/sessions/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/sessions/route.ts), which reads the `~/.pi/agent/sessions/` directory and returns metadata about each `.jsonl` file found there.

### Sending Messages to the Agent

User input travels from the browser to the in-process agent through the Next.js API layer:

```typescript
async function sendMessage(sessionId: string, text: string) {
  await fetch(`/api/agent/${sessionId}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ role: "user", content: text }),
  });
}

```

The POST handler in `app/api/agent/[id]/route.ts` validates the request body and forwards it to `rpc-manager.startRpcSession()`, which either retrieves an existing session wrapper or initializes a new AgentSession instance.

### Subscribing to Real-Time Events

The browser maintains a persistent connection to receive streaming updates via Server-Sent Events:

```typescript
function useAgentEvents(sessionId: string, onEvent: (e: AgentEvent) => void) {
  useEffect(() => {
    const es = new EventSource(`/api/agent/${sessionId}/events`);
    es.addEventListener("message", (e) => onEvent(JSON.parse(e.data)));
    return () => es.close();
  }, [sessionId, onEvent]);
}

```

The SSE source is created by `app/api/agent/[id]/events/route.ts`, which subscribes to the session's event emitter and forwards each event to the connected browser client until the session terminates or the client disconnects.

## Summary

- **Pi Web architecture** consists of three distinct layers: the Browser (React UI), the Next.js Server (API gateway), and the AgentSession (in-process AI engine).
- The **Browser layer** handles all presentation concerns through components like [`AppShell.tsx`](https://github.com/agegr/pi-web/blob/main/AppShell.tsx) and [`ChatWindow.tsx`](https://github.com/agegr/pi-web/blob/main/ChatWindow.tsx), communicating via standard HTTP and SSE.
- The **Next.js Server layer** provides REST endpoints and security enforcement, managing file access through [`lib/file-access.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-access.ts) and session routing through [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts).
- The **AgentSession layer** runs the core AI logic directly in the Node.js process, streaming events to the browser while persisting history to `~/.pi/agent/sessions/` as `.jsonl` files.
- This three-tier design enables real-time, low-latency AI assistance within a browser environment while maintaining strict filesystem security boundaries.

## Frequently Asked Questions

### What is the role of the AgentSession layer in Pi Web?

The **AgentSession layer** hosts the actual Pi coding agent inside the Node.js process. It processes natural language prompts, executes development tools, manages conversation state, and emits streaming events. Unlike cloud-based AI services, this layer runs locally according to the `agegr/pi-web` source code, enabling offline operation and eliminating network latency between the API and the AI engine. Sessions are persisted automatically to JSON Lines files in the user's home directory.

### How does Pi Web maintain real-time communication between the server and browser?

Pi Web uses **Server-Sent Events (SSE)** to push updates from the AgentSession to the browser. When a user initiates a chat, the React hook [`useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/useAgentSession.ts) in the browser layer establishes an `EventSource` connection to `/api/agent/[id]/events`. The Next.js server maintains this connection, forwarding events emitted by the in-process agent as they occur. This creates a unidirectional stream that supports real-time typing indicators, tool execution updates, and final response delivery without polling overhead.

### Where does Pi Web store session data and conversation history?

Session data is stored in `.jsonl` (JSON Lines) files located under `~/.pi/agent/sessions/` on the local filesystem. The **AgentSession layer** appends each event (user messages, agent responses, tool calls) to this file for durability. The **Next.js Server layer** reads these files through [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts) when listing sessions or loading historical context, while the browser layer never accesses the filesystem directly. This design ensures conversation persistence across server restarts while maintaining security boundaries.

### How does Pi Web prevent the browser from accessing arbitrary files on the system?

Security is enforced at the **Next.js Server layer** through helper modules like [`lib/file-access.ts`](https://github.com/agegr/pi-web/blob/main/lib/file-access.ts) and [`lib/worktree.ts`](https://github.com/agegr/pi-web/blob/main/lib/worktree.ts). These modules implement an allow-list mechanism that restricts filesystem access to specific Git worktrees and approved directories. When the browser requests file contents via `/api/files/[...path]`, the server validates the requested path against this allow-list before reading from disk. This architecture ensures that even if the browser layer were compromised, it cannot traverse outside the designated project roots.