# How Pi Web Integrates with the Pi Coding Agent and Shares Session Files

> Discover how Pi Web integrates with the Pi Coding Agent using REST SSE endpoints and Next.js API routes. Learn how session files are managed and persisted as JSONL for hot reloads.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: how-to-guide
- Published: 2026-08-14

---

**Pi Web connects to the pi coding agent through REST/SSE endpoints in Next.js API routes, wrapping the `@earendil-works/pi-coding-agent` library in a session management layer that persists all data as JSONL files with global caches surviving hot reloads.**

The **pi coding agent** integration in Pi Web follows a three-layer architecture: session lifecycle management in server-side RPC handlers, command and event streaming through typed API routes, and file-based persistence with path resolution caching. This design lets the web UI start, monitor, and resume agent sessions while the underlying JSONL files remain the single source of truth.

## Session Creation and Lifecycle Management

New sessions begin in [`app/api/agent/new/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/agent/new/route.ts), which delegates to `RpcSession.start()` defined in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts).

### How RpcSession.start() Builds a Session

The `start()` method performs three critical operations:

1. **Instantiates `AgentSessionLike`** via `createAgentSessionFromServices`, injecting the configured tools, model provider, and thinking level.
2. **Wraps the session** in `AgentSessionWrapper`, which exposes a cleaner interface and tracks metadata.
3. **Registers global caches** through `cacheSessionPath`, mapping the session ID to its JSONL file path in `__piSessionPathCache` and `__piPathToSessionIdCache`.

These caches live on `globalThis`, so they persist across Next.js hot reloads. A session started before a code change remains resolvable after the server restarts.

```typescript
// lib/rpc-manager.ts — RpcSession.start() orchestration
const wrapper = new AgentSessionWrapper(session);
const sessionFile = wrapper.inner.sessionFile;
cacheSessionPath(sessionId, sessionFile); // globalThis.__piSessionPathCache

```

## Command Handling and Event Streaming

Once a session exists, the UI communicates through two complementary channels.

### Sending Commands via REST

The client helper `sendAgentCommand()` in [`lib/agent-client.ts`](https://github.com/agegr/pi-web/blob/main/lib/agent-client.ts) POSTs structured commands to `app/api/agent/[id]/route.ts`. This route:

- Resolves the session ID to its `AgentSessionWrapper` via the global cache
- Forwards the command to the underlying `AgentSessionLike`
- Returns typed results or normalized errors through `AgentCommandError`

```typescript
import { sendAgentCommand } from '@/lib/agent-client';

// Execute code in an existing session
const result = await sendAgentCommand<{ answer: string }>('a1b2c3', {
  cmd: 'run',
  input: { code: 'console.log("hello")' },
});

```

### Real-Time Updates via SSE

For live feedback, `app/api/agent/[id]/events/route.ts` establishes a **Server-Sent Events** connection. The endpoint streams `AgentEvent` objects as the agent emits them:

- `agent_start` — agent began processing
- `agent_end` — agent finished or errored
- `compaction_start` — context window compaction in progress

The `AgentSessionWrapper` mediates between the raw agent events and the SSE format, ensuring the UI state stays synchronized without polling.

## Session File Sharing and Path Resolution

All session data lives in **JSONL files** under `~/.pi/agent/sessions/`. Pi Web treats these files as the authoritative store, with the UI layer operating as a read-through cache.

### Scanning and Listing Sessions

[`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts) implements `listAllSessions()`, which:

1. Calls `SessionManager.listAll()` to discover files in the sessions directory
2. Builds `SessionInfo` objects with metadata (ID, name, modification time)
3. Populates the bidirectional caches for fast ID↔path lookups

```typescript
import { listAllSessions } from '@/lib/session-reader';

const sessions = await listAllSessions();
// Returns: [{ id: 'abc123', name: 'session-2024-01-15', modified: Date, ... }]

```

### Resolving Session Paths

When the UI needs to access a specific session's data, `resolveSessionPath()` checks `__piSessionPathCache` first, falling back to a directory scan only on cache miss. This ensures sub-millisecond path resolution for active sessions.

```typescript
import { resolveSessionPath } from '@/lib/session-reader';

const filePath = await resolveSessionPath('a1b2c3');
// ~/.pi/agent/sessions/2024-01/session-a1b2c3.jsonl

```

## Key Integration Files

| File | Responsibility |
|------|---------------|
| [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) | `RpcSession.start()`, `AgentSessionWrapper`, global cache registration |
| [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts) | Directory scanning, `SessionInfo` construction, path resolution caching |
| [`lib/agent-client.ts`](https://github.com/agegr/pi-web/blob/main/lib/agent-client.ts) | Browser-side `sendAgentCommand()`, error normalization |
| [`app/api/agent/new/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/agent/new/route.ts) | Public POST endpoint for session creation |
| `app/api/agent/[id]/route.ts` | Command routing to active sessions |
| `app/api/agent/[id]/events/route.ts` | SSE streaming of agent lifecycle events |

## Summary

- **Pi Web integrates with the pi coding agent** through a thin server-side wrapper (`RpcSession`/`AgentSessionWrapper`) that exposes the agent's capabilities as REST and SSE endpoints.

- **Session creation** happens in [`app/api/agent/new/route.ts`](https://github.com/agegr/pi-web/blob/main/app/api/agent/new/route.ts), which delegates to `RpcSession.start()` and registers the session in hot-reload-surviving global caches.

- **Command execution** flows from `sendAgentCommand()` in the browser through `app/api/agent/[id]/route.ts` to the underlying agent session.

- **Real-time synchronization** uses Server-Sent Events from `app/api/agent/[id]/events/route.ts`, streaming typed `AgentEvent` objects without polling overhead.

- **Session file sharing** relies on JSONL files in `~/.pi/agent/sessions/`, with [`lib/session-reader.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-reader.ts) providing cached path resolution and metadata listing.

## Frequently Asked Questions

### How does Pi Web maintain session state across server restarts?

Pi Web stores session ID-to-path mappings in `globalThis.__piSessionPathCache` and `globalThis.__piPathToSessionIdCache`. Because these caches attach to the global object rather than module scope, they survive Next.js hot reloads. The actual session data persists as JSONL files on disk, so a fresh server process can reconstruct the cache by scanning `~/.pi/agent/sessions/` via `SessionManager.listAll()`.

### What format are session files stored in?

Session files use **JSONL (JSON Lines)**, with one JSON object per line representing an event or state snapshot. This append-only format supports efficient streaming and compaction. The file path is exposed through `inner.sessionFile` on the `AgentSessionWrapper` and tracked in the global caches.

### Can multiple browser clients connect to the same agent session?

Yes. The `AgentSessionWrapper` in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) maintains a single underlying `AgentSessionLike` per session ID. Multiple SSE connections to `app/api/agent/[id]/events/route.ts` will all receive the same event stream, and commands from any client POSTing to `app/api/agent/[id]/route.ts` are serialized through the same wrapper instance.