How the 10-Minute Idle Timeout Works for AgentSession in pi-web
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 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 whenstart()runs【rpc-manager.ts L50-L61】 - Incoming RPC commands — every
sendoperation resets the timer【rpc-manager.ts L96-L99】 - Prompt completion —
finishPrompttriggers another reset【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:
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
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
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— ImplementsAgentSessionWrapper, idle timer registration, and shutdown executionlib/session-timing.ts— Utility functions for measuring and displaying session activity durationhooks/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 eachfinishPromptevent - 10-minute threshold is enforced via
setTimeoutwith10 * 60 * 1000ms 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. 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →