# How the 10-Minute Idle Timeout Works for AgentSession in pi-web

> Understand the 10-minute idle timeout for AgentSession in pi-web. Learn how it resets timers and calls shutdown() to manage inactive sessions efficiently.

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

---

**The 10-minute idle timeout automatically shuts down inactive AgentSession instances by resetting a timer on every activity event and calling `shutdown()` when no activity occurs for 600,000 milliseconds.**

Pi Web wraps the Pi SDK's native `AgentSession` with an `AgentSessionWrapper` that enforces automatic cleanup of idle sessions. This mechanism prevents resource leaks in long-running server environments while ensuring active sessions remain available. According to the `agegr/pi-web` source code, the timeout logic resides in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts) and tracks multiple activity signals to determine when a session is truly idle.

## Where the Idle Timer Is Registered

The `AgentSessionWrapper` class initializes and manages the idle timer throughout a session's lifecycle. Three distinct events trigger `resetIdleTimer()`:

- **Wrapper startup** — `this.resetIdleTimer()` is called immediately when `start()` runs【[rpc-manager.ts L50-L61](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts#L50-L61)】
- **Incoming RPC commands** — every `send` operation resets the timer【[rpc-manager.ts L96-L99](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts#L96-L99)】
- **Prompt completion** — `finishPrompt` triggers another reset【[rpc-manager.ts L38-L41](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts#L38-L41)】

This comprehensive coverage ensures any meaningful interaction keeps the session alive.

## The resetIdleTimer Implementation

The core timeout logic appears in a private method that clears existing timers and schedules fresh ones:

```typescript
private resetIdleTimer(): void {
  if (this.idleTimer) clearTimeout(this.idleTimer);
  this.idleTimer = setTimeout(() => {
    // If a run is still active, postpone the shutdown.
    if (this.isRunning()) {
      this.resetIdleTimer();
      return;
    }
    // No activity → gracefully shut down the session.
    void this.shutdown().catch(err => {
      console.error("[pi-web] failed to shut down idle session:", err);
    });
  }, 10 * 60 * 1000);   // 10 minutes
}

```

The **10-minute duration** is hardcoded as `10 * 60 * 1000` milliseconds. When the timer fires, the wrapper performs one final safety check before terminating.

## How isRunning() Determines Session Activity

The `isRunning()` method prevents premature shutdown by checking four active state conditions:

| Condition | Source Property | Description |
|-----------|---------------|-------------|
| Pending prompts | `pendingPromptCount > 0` | User input awaiting response |
| Active streaming | `inner.isStreaming` | SDK transmitting response chunks |
| Compaction in progress | `inner.isCompacting` | Background context compression |
| Bash command running | `inner.isBashRunning` | Subprocess executing shell command |

If **any** condition evaluates to `true`, `resetIdleTimer()` reschedules itself. Only when all conditions are `false` does `shutdown()` execute, closing the SDK session and removing the wrapper from the global registry.

## Practical Code Examples

### Manually Trigger Idle Shutdown for Testing

```typescript
import { startRpcSession } from "@/lib/rpc-manager";

// Create a temporary session.
const wrapper = await startRpcSession({ cwd: "/tmp", toolNames: [] });

// Immediately clear the idle timer and fire it manually.
wrapper['idleTimer'] && clearTimeout(wrapper['idleTimer']);
wrapper['idleTimer'] = setTimeout(() => wrapper['shutdown'](), 0);

```

This pattern bypasses the 10-minute wait during development or automated tests.

### Observing Timeout Behavior in the UI

```tsx
import { useAgentSession } from "@/hooks/useAgentSession";

export default function IdleDemo({ sessionId }: { sessionId: string }) {
  const { state, send } = useAgentSession(sessionId);

  // After a prompt finishes, the UI will automatically show the session as
  // "offline" roughly 10 minutes later if no further activity occurs.
  useEffect(() => {
    if (!state.isRunning) console.log("Session is idle – will shut down soon");
  }, [state.isRunning]);
}

```

The `useAgentSession` hook consumes wrapper state and reflects shutdown status in the client interface.

## Key Files in the Idle Timeout Flow

- **[`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts)** — Implements `AgentSessionWrapper`, idle timer registration, and shutdown execution
- **[`lib/session-timing.ts`](https://github.com/agegr/pi-web/blob/main/lib/session-timing.ts)** — Utility functions for measuring and displaying session activity duration
- **[`hooks/useAgentSession.ts`](https://github.com/agegr/pi-web/blob/main/hooks/useAgentSession.ts)** — Client-side state management that propagates idle/shutdown status to the UI

## Summary

- **Activity resets** occur on wrapper start, every RPC `send`, and each `finishPrompt` event
- **10-minute threshold** is enforced via `setTimeout` with `10 * 60 * 1000` ms duration
- **Safety check** via `isRunning()` prevents shutdown during streaming, compaction, or bash execution
- **Graceful cleanup** calls `shutdown()` to release SDK resources and unregister the wrapper
- **Cross-stack visibility** connects server-side timeout logic to UI state through `useAgentSession`

## Frequently Asked Questions

### Can the 10-minute idle timeout duration be configured?

No. The duration is hardcoded as `10 * 60 * 1000` milliseconds in [`lib/rpc-manager.ts`](https://github.com/agegr/pi-web/blob/main/lib/rpc-manager.ts). The `resetIdleTimer()` method contains no configurable parameter for the timeout value.

### What happens if a session is actively streaming when the timer fires?

The `isRunning()` check detects `inner.isStreaming` and calls `resetIdleTimer()` to postpone shutdown. The session continues until streaming completes and no other activity indicators remain active.

### How can I verify a session shut down due to idle timeout?

Check server logs for the `[pi-web] failed to shut down idle session` error message on failure, or monitor the `state.isRunning` property via `useAgentSession` on the client. A transition from `true` to `false` without explicit user action indicates idle timeout.

### Does the idle timeout conflict with manual session closure?

No. The `shutdown()` method is idempotent. Calling it manually clears the idle timer and closes the session immediately. Subsequent timer callbacks detect the closed state and exit silently.