How to Use the Pi Web Global Session Registry (`globalThis.__piSessions`)

Access active Agent sessions in Pi Web via globalThis.__piSessions, a Map-backed global registry that stores session wrappers by ID, or use the helper functions in lib/rpc-manager.ts for type-safe interactions.

The agegr/pi-web repository maintains a global registry attached to globalThis called __piSessions to track every active Agent session across the server lifecycle. This registry stores AgentSessionWrapper instances in a Map keyed by session ID, enabling centralized session management and graceful shutdown handling.

Understanding the Global Registry Structure

globalThis.__piSessions is a Map<string, AgentSessionWrapper> where each entry maps a session UUID to its controlling wrapper object. According to the source code in lib/rpc-manager.ts lines 57-66, the registry is created lazily the first time it is accessed, ensuring zero overhead until an Agent session actually starts.

The registry integrates with the Node process lifecycle. A cleanup function registered at initialization automatically destroys all active sessions when the server exits, preventing orphaned Pi SDK processes and ensuring graceful shutdowns.

Helper Functions for Type-Safe Access

While direct access to globalThis.__piSessions works, the exported helpers in lib/rpc-manager.ts handle lazy-initialization, TypeScript typing, and cleanup logic. They also insulate your code from the exact storage mechanism, making future refactors safer.

getRpcSession(sessionId)

Returns the AgentSessionWrapper for a given ID, or undefined if the session does not exist.

  • Source: lib/rpc-manager.ts lines 98-100
  • Use case: Fetching a specific session to send commands or check health

getRunningRpcSessionIds()

Returns an array of session IDs whose isRunning() flag is currently true.

  • Source: lib/rpc-manager.ts lines 19-24
  • Use case: Debugging dashboards, admin pages, or status endpoints

hasBusyRpcSessionForCwd(cwd)

Checks whether any session for the supplied working directory is currently starting or running.

  • Source: lib/rpc-manager.ts lines 2-7
  • Use case: Preventing duplicate session launches for the same project directory

destroyRpcSessionsForCwd(cwd)

Gracefully shuts down all sessions belonging to a specific current working directory.

  • Source: lib/rpc-manager.ts lines 10-16
  • Use case: Cleanup routines when a project is closed or deleted

subscribeRunningSessions(listener)

Registers a callback that fires whenever the set of running session IDs changes. The listener receives the new ID list as its argument. Pi Web stores these listeners in globalThis.__piRunningListeners and triggers them via notifyRunningChange() whenever a session’s running state flips.

  • Source: lib/rpc-manager.ts lines 41-46
  • Use case: Real-time UI updates via Server-Sent Events (SSE) or WebSockets

Practical Implementation Examples

Send Commands to a Specific Session

Use getRpcSession to retrieve a wrapper and access the underlying Pi SDK AgentSession via the inner property:

import { getRpcSession } from '@/lib/rpc-manager';

const sessionId = 'c1d2e3f4-5678-90ab-cdef-1234567890ab';
const wrapper = getRpcSession(sessionId);

if (wrapper && wrapper.isAlive()) {
  await wrapper.inner.send('some_command', { foo: 'bar' });
}

Monitor Active Sessions

Fetch all currently running session IDs for logging or administrative interfaces:

import { getRunningRpcSessionIds } from '@/lib/rpc-manager';

const runningIds = getRunningRpcSessionIds();
console.log('Currently running sessions:', runningIds);

Clean Up Sessions by Project Directory

Shut down every session associated with a specific path, useful for project-switching workflows:

import { destroyRpcSessionsForCwd } from '@/lib/rpc-manager';
import { resolve } from 'path';

const cwd = resolve('/home/user/my-project');
const count = await destroyRpcSessionsForCwd(cwd);
console.log(`Destroyed ${count} session(s) for ${cwd}`);

React to Session State Changes

Subscribe to registry changes to broadcast updates to connected clients:

import { subscribeRunningSessions, getRunningRpcSessionIds } from '@/lib/rpc-manager';

const unsubscribe = subscribeRunningSessions((ids) => {
  console.log('Running sessions changed:', ids);
});

console.log('Initial running sessions:', getRunningRpcSessionIds());

// Cleanup when shutting down your component or route:
unsubscribe();

Direct Registry Access (Advanced)

For low-level enumeration or debugging, access the Map directly. Note that this bypasses the lazy-initialization guard:

const registry = globalThis.__piSessions;
if (registry) {
  for (const [id, wrapper] of registry.entries()) {
    console.log(`Session ${id} alive: ${wrapper.isAlive()}`);
  }
}

Key Source Files

  • lib/rpc-manager.ts: Implements the global registry, helper APIs, and lifecycle hooks (lines 57-66, 98-100, and throughout).
  • AGENTS.md: High-level design documentation referencing globalThis.__piSessions.
  • app/api/agent/[id]/route.ts: Example server-side usage of getRpcSession to forward client commands.
  • app/api/sessions/[id]/route.ts: Demonstrates session lookup and shutdown via the registry.

Summary

  • globalThis.__piSessions is a Map<string, AgentSessionWrapper> storing all active Pi Web Agent sessions by ID.
  • The registry initializes lazily on first access and automatically cleans up on process exit.
  • Prefer helper functions (getRpcSession, getRunningRpcSessionIds, etc.) over direct Map access to ensure type safety and proper initialization.
  • Use subscribeRunningSessions to react to session lifecycle changes in real-time applications.
  • Use destroyRpcSessionsForCwd for bulk cleanup of sessions tied to specific project directories.

Frequently Asked Questions

What data type is globalThis.__piSessions?

globalThis.__piSessions is a standard JavaScript Map where keys are session ID strings and values are AgentSessionWrapper instances. The wrapper objects encapsulate the raw Pi SDK AgentSession and provide methods like isAlive(), isRunning(), and shutdown().

When is the global registry initialized?

The registry initializes lazily the first time any helper function or direct access triggers its creation. According to lib/rpc-manager.ts lines 57-66, the initialization checks if (!globalThis.__piSessions) and assigns a new Map() only when needed.

How do I safely check if a session exists without manually touching globalThis?

Import and use getRpcSession(sessionId) from lib/rpc-manager.ts. It returns the AgentSessionWrapper if present, or undefined if the ID is not in the registry. This approach guarantees the registry is initialized before lookup and provides full TypeScript type inference.

Can I listen for new session creation events specifically?

While there is no hook exclusively for "creation," you can use subscribeRunningSessions to detect when a session transitions to a running state. This callback fires whenever the set of running session IDs changes, effectively notifying you of both new sessions starting and existing sessions stopping. The callback receives the complete updated array of running IDs.

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 →