# What Happens to the AgentSessionWrapper When AgentSession.fork() Is Called

> Learn what happens to AgentSessionWrapper when AgentSession.fork() is called. Discover how it's destroyed to prevent registry corruption and ensure clean session reloads.

- Repository: [Alex Yang/pi-web](https://github.com/agegr/pi-web)
- Tags: internals
- Published: 2026-07-23

---

**When `AgentSession.fork()` is called, the `AgentSessionWrapper` is immediately destroyed via `this.destroy()` after capturing the new session ID, preventing registry corruption and ensuring the original session reloads cleanly on the next request.**

The `agegr/pi-web` repository implements a session management system where calling `AgentSession.fork()` triggers a critical lifecycle event for the `AgentSessionWrapper`. According to the architecture notes in [`AGENTS.md`](https://github.com/agegr/pi-web/blob/main/AGENTS.md) and the implementation details in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts), the fork operation mutates the wrapper's internal state in-place, requiring immediate destruction to maintain a valid `parentSession` chain.

## Why Forking Requires Immediate Wrapper Destruction

When `AgentSession.fork()` executes, it **mutates** the wrapper’s inner state directly. Specifically, the `inner.sessionId` property is overwritten with the ID of the newly created session rather than the original one.

If the wrapper remained registered in the global session registry under the **old** session ID, subsequent requests targeting that original ID would retrieve the already-forked state. This corruption leads to an invalid `parentSession` chain and causes subsequent fork operations to produce inconsistent or broken session hierarchies.

## The Destruction Sequence in rpc-manager.ts

The implementation in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) handles this by following a strict sequence:

1. **Capture the new session ID**: The `send("fork")` method first extracts the `newSessionId` from the fork result.
2. **Destroy the wrapper**: Before returning, it invokes `this.destroy()` on the current `AgentSessionWrapper` instance.
3. **Return the result**: The method returns the `newSessionId` to the caller.
4. **Reload on next access**: The next request for the original session ID triggers a fresh load of a clean `AgentSession` from the original session file, as the old wrapper no longer exists in the registry.

This ensures that the forked session operates independently without leaking its state back into the original session's context.

## Practical Implementation Examples

### Triggering a Fork via the HTTP API

You can trigger this behavior by sending a fork command to the agent API endpoint:

```javascript
// Trigger fork on current session
const response = await fetch(`/api/agent/${sessionId}`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ cmd: 'fork' })
});

const { newSessionId } = await response.json();
console.log('New forked session:', newSessionId);
// The original wrapper has been destroyed; newSessionId references the fresh session

```

### Using the agent-client.ts Helper

For client-side applications, the repository provides a helper in [`lib/agent-client.ts`](https://github.com/agegr/pi-web/blob/main/lib/agent-client.ts) that abstracts the fork operation:

```javascript
import { agentClient } from '@/lib/agent-client';

async function forkCurrentSession(currentSessionId) {
  const result = await agentClient(currentSessionId).post({ cmd: 'fork' });
  
  // result.newSessionId contains the forked session reference
  // The original session wrapper was automatically destroyed by the server
  return result.newSessionId;
}

```

## Summary

- `AgentSession.fork()` overwrites `inner.sessionId` with the **new** session ID, mutating the wrapper in-place.
- The wrapper must be destroyed **immediately** via `this.destroy()` to prevent corrupted `parentSession` chains.
- The implementation captures `newSessionId` before destruction to return a valid reference to the caller.
- Subsequent requests for the original session trigger a clean reload from the session file, as the stale wrapper no longer exists in the registry.

## Frequently Asked Questions

### What is the AgentSessionWrapper?

The `AgentSessionWrapper` is a container class defined in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) that manages the lifecycle and state of an `AgentSession` within the global registry. It holds the `inner` session object—including the `sessionId`—and provides methods like `destroy()` for cleanup.

### Why can't the wrapper remain in the registry after forking?

If the wrapper remained under the original session ID, its mutated state (specifically the updated `inner.sessionId`) would be returned to subsequent requests for that original ID. This causes a logical error where requests believe they are accessing the original session but are actually receiving the forked session state, breaking the `parentSession` relationship chain.

### How does the system reload the original session after the wrapper is destroyed?

Once `this.destroy()` removes the wrapper from the global registry (`globalThis.__piSessions`), the next incoming request for that session ID finds no active entry. This triggers the session loader to create a fresh `AgentSessionWrapper` instance from the original session file stored on disk, restoring the original state without the fork mutation.

### Is the forked session immediately ready when `AgentSession.fork()` returns?

Yes. The method captures the `newSessionId` before destroying the original wrapper, ensuring the forked session is fully initialized and its ID returned to the caller. The destruction of the original wrapper happens only after the new session reference is safely captured, guaranteeing continuity of operations while maintaining state isolation.