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

> Learn to use globalThis.__piSessions in Pi Web to access active agent sessions. Explore the Map-backed registry and helper functions for type-safe interactions.

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

---

**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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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:

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts)**: Implements the global registry, helper APIs, and lifecycle hooks (lines 57-66, 98-100, and throughout).
- **[`AGENTS.md`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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`](https://github.com/agegr/pi-web/blob/main/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.