AgentSessionWrapper Idle Timeout Behavior in Pi Web: 10-Minute Session Management
The AgentSessionWrapper in Pi Web automatically destroys inactive sessions after 10 minutes of idle time, resetting the timer whenever a session starts or receives an RPC command.
The agegr/pi-web repository implements automatic resource cleanup through the AgentSessionWrapper class to prevent orphaned sessions from consuming server resources. This idle timeout mechanism ensures that abandoned sessions terminate automatically while keeping active workflows alive through intelligent activity detection.
How the 10-Minute Idle Timeout Works
The idle timeout logic is hard-coded within the AgentSessionWrapper class in lib/rpc-manager.ts. When the wrapper detects 10 minutes of inactivity and no running operations, it triggers an automatic cleanup sequence.
The resetIdleTimer() Implementation
The core timeout logic resides in the private resetIdleTimer() method, which creates a 10-minute timer (600,000 milliseconds) that checks session status before destruction:
// lib/rpc-manager.ts
private resetIdleTimer(): void {
if (this.idleTimer) clearTimeout(this.idleTimer);
this.idleTimer = setTimeout(() => {
// If something is still busy, keep the timer alive.
if (this.isRunning()) {
this.resetIdleTimer();
return;
}
// No activity → clean up the session.
this.destroy();
}, 10 * 60 * 1000); // ← 10‑minute idle timeout
}
According to the source code at lines 61-70, this method first clears any existing timer to prevent duplicate timeouts, then schedules a new check. If the session is busy when the timer fires, the method recursively calls itself to extend the timeout period.
Activity Events That Reset the Timer
Two primary actions refresh the idle deadline:
start()– Initializes the timer when a session begins (lines 44-46)send()– Resets the timer on every incoming RPC command including prompts, abort requests, and Bash executions (lines 105-107)
Any interaction with the session through these methods extends the lifecycle by another 10 minutes from the moment of activity.
Session Lifecycle Integration
The idle timeout integrates seamlessly with the wrapper's lifecycle methods to provide transparent resource management.
Automatic Timer Initialization
When you create a new session via startRpcSession() in lib/rpc-manager.ts (lines 34-45), the wrapper automatically invokes start(), which calls resetIdleTimer() to begin the inactivity countdown immediately:
import { startRpcSession } from "./lib/rpc-manager";
const { session } = await startRpcSession(
"my-session-id", // requested ID (may be generated)
"", // no pre‑existing session file → new session
"/home/user/project",
[] // optional tool list
);
// `session.start()` is called inside `startRpcSession`, which
// starts the 10‑minute idle timer.
Continuous Refresh on RPC Commands
Every command sent through the session extends its lifetime. When you call send() with any RPC payload, the wrapper executes resetIdleTimer() before processing the request:
await session.send({ type: "prompt", message: "Explain the code." });
// `resetIdleTimer()` runs, extending the idle deadline by another 10 min.
This design ensures that long-running conversations or batch operations maintain session persistence as long as commands arrive within the 10-minute window.
The isRunning() Safety Mechanism
Before destroying a session, the timeout handler verifies that no operations are active through the isRunning() helper (lines 40-42). This method returns true if the session is processing a prompt, streaming a response, running compaction, or executing a Bash command. When isRunning() returns true, resetIdleTimer() recurses instead of calling destroy(), preventing interruption of active workloads.
Practical Implementation Examples
Creating a Session with Auto-Cleanup
Sessions begin their idle countdown immediately upon creation:
import { startRpcSession } from "./lib/rpc-manager";
const { session } = await startRpcSession(
"production-agent-001",
"",
"/var/app",
["file-editor", "bash"]
);
// Idle timer starts automatically. If no commands sent for 10 minutes,
// session.destroy() runs automatically.
Maintaining Session Activity
Keep sessions alive by sending periodic commands or heartbeats through the send() method:
// This resets the 10-minute timer
await session.send({ type: "prompt", message: "Status check" });
// Timer is now reset to 10 minutes from this moment
Manual Cleanup Before Timeout
You can bypass the idle timeout entirely by explicitly destroying the session:
// Immediate teardown, bypassing the idle timer
session.destroy();
Calling destroy() marks the wrapper as dead, removes event listeners, aborts any running Bash processes, and notifies the UI immediately.
Summary
- 10-minute hardcoded limit: The idle timeout is fixed at
10 * 60 * 1000milliseconds inlib/rpc-manager.tsand cannot be configured at runtime - Activity resets timer: Any call to
start()orsend()invokesresetIdleTimer(), extending the session deadline by another 10 minutes - Busy-state protection: The
isRunning()method prevents destruction during active prompts, streaming, compaction, or Bash operations by recursively resetting the timer - Automatic resource reclamation: When the timeout fires on an idle session,
destroy()cleans up listeners, aborts subprocesses, and notifies the UI without requiring manual intervention
Frequently Asked Questions
Can I configure the idle timeout duration?
No, the timeout is hard-coded to 10 minutes in the resetIdleTimer() method at line 69 of lib/rpc-manager.ts. The value 10 * 60 * 1000 is not exposed as a configuration parameter, so modifying the timeout requires changing the source code and rebuilding the application.
What happens if a command is running when the idle timer fires?
The session remains alive. When the timer callback executes, it checks isRunning() to detect active prompts, streaming responses, compaction tasks, or Bash processes. If any operation is active, the method calls resetIdleTimer() recursively instead of destroy(), effectively extending the timeout until the operation completes and the next idle period begins.
How can I manually terminate a session before the idle timeout?
Call the destroy() method directly on the session instance. This immediately triggers cleanup logic including listener removal, Bash process abortion, and UI notification, bypassing the 10-minute idle wait. This is useful when you know a session is complete and want to free resources immediately.
Which specific activities prevent the session from being destroyed?
The isRunning() method (lines 40-42) returns true and prevents destruction when the session has an active prompt request, is streaming a response back to the client, is running compaction operations, or has an executing Bash command. Only when none of these states are active will the idle timeout successfully call destroy().
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 →