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:

  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.

// 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 AgentSessionWrapper via 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 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 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
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 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 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 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:

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 →