How Pi Web Integrates with the Pi Coding Agent and Shares Session Files
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, which delegates to RpcSession.start() defined in lib/rpc-manager.ts.
How RpcSession.start() Builds a Session
The start() method performs three critical operations:
- Instantiates
AgentSessionLikeviacreateAgentSessionFromServices, injecting the configured tools, model provider, and thinking level. - Wraps the session in
AgentSessionWrapper, which exposes a cleaner interface and tracks metadata. - Registers global caches through
cacheSessionPath, mapping the session ID to its JSONL file path in__piSessionPathCacheand__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.
// 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 POSTs structured commands to app/api/agent/[id]/route.ts. This route:
- Resolves the session ID to its
AgentSessionWrappervia the global cache - Forwards the command to the underlying
AgentSessionLike - Returns typed results or normalized errors through
AgentCommandError
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 processingagent_end— agent finished or erroredcompaction_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 implements listAllSessions(), which:
- Calls
SessionManager.listAll()to discover files in the sessions directory - Builds
SessionInfoobjects with metadata (ID, name, modification time) - Populates the bidirectional caches for fast ID↔path lookups
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.
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 |
RpcSession.start(), AgentSessionWrapper, global cache registration |
lib/session-reader.ts |
Directory scanning, SessionInfo construction, path resolution caching |
lib/agent-client.ts |
Browser-side sendAgentCommand(), error normalization |
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, which delegates toRpcSession.start()and registers the session in hot-reload-surviving global caches. -
Command execution flows from
sendAgentCommand()in the browser throughapp/api/agent/[id]/route.tsto the underlying agent session. -
Real-time synchronization uses Server-Sent Events from
app/api/agent/[id]/events/route.ts, streaming typedAgentEventobjects without polling overhead. -
Session file sharing relies on JSONL files in
~/.pi/agent/sessions/, withlib/session-reader.tsproviding 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 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.
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 →