Pi Web Architecture: Understanding the Three-Layer Browser-Based AI Coding System
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– Provides the overall application layout and navigation structurecomponents/ChatWindow.tsx– Renders the conversational interface and message historyhooks/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 requestsapp/api/sessions/route.ts– Lists available sessions by reading from the filesystemapp/api/files/[...path]/route.ts– Provides secure file viewing capabilitiesapp/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 (which enforces allow-listed filesystem roots) and 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.tsvia thecreateAgentSessionFromServicesfunction - Lifecycle management is handled by
AgentSessionWrapper, which wraps raw agent instances - Command processing happens through
session.prompt(), which emits events likeagent_start,tool_call, andagent_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 documentation. When a user sends a message, the request travels through a carefully orchestrated pipeline:
-
UI to Server: The browser issues a
POST /api/agent/[id]request containing the user message. The route handler inapp/api/agent/[id]/route.tscallsstartRpcSession()fromlib/rpc-manager.tsto locate or instantiate anAgentSessionWrapper. Implementation details are found at lines 119-130 ofrpc-manager.ts. -
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. -
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. -
Server to Browser: The React hook
useAgentSession.tsconsumes the SSE stream, parsing JSON events and updating the component state in real time. This creates the appearance of a live, typing assistant. -
File Operations: For non-chat features like the file explorer, the browser calls dedicated routes (
/api/files,/api/worktrees) that delegate tolib/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:
// 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, 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:
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:
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.tsxandChatWindow.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.tsand session routing throughlib/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.jsonlfiles. - 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 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 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 and 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →