pi-app-server 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +195 -0
- package/dist/command-classification.d.ts +59 -0
- package/dist/command-classification.d.ts.map +1 -0
- package/dist/command-classification.js +78 -0
- package/dist/command-classification.js.map +7 -0
- package/dist/command-execution-engine.d.ts +118 -0
- package/dist/command-execution-engine.d.ts.map +1 -0
- package/dist/command-execution-engine.js +259 -0
- package/dist/command-execution-engine.js.map +7 -0
- package/dist/command-replay-store.d.ts +241 -0
- package/dist/command-replay-store.d.ts.map +1 -0
- package/dist/command-replay-store.js +306 -0
- package/dist/command-replay-store.js.map +7 -0
- package/dist/command-router.d.ts +25 -0
- package/dist/command-router.d.ts.map +1 -0
- package/dist/command-router.js +353 -0
- package/dist/command-router.js.map +7 -0
- package/dist/extension-ui.d.ts +139 -0
- package/dist/extension-ui.d.ts.map +1 -0
- package/dist/extension-ui.js +189 -0
- package/dist/extension-ui.js.map +7 -0
- package/dist/resource-governor.d.ts +254 -0
- package/dist/resource-governor.d.ts.map +1 -0
- package/dist/resource-governor.js +603 -0
- package/dist/resource-governor.js.map +7 -0
- package/dist/server-command-handlers.d.ts +120 -0
- package/dist/server-command-handlers.d.ts.map +1 -0
- package/dist/server-command-handlers.js +234 -0
- package/dist/server-command-handlers.js.map +7 -0
- package/dist/server-ui-context.d.ts +22 -0
- package/dist/server-ui-context.d.ts.map +1 -0
- package/dist/server-ui-context.js +221 -0
- package/dist/server-ui-context.js.map +7 -0
- package/dist/server.d.ts +82 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +561 -0
- package/dist/server.js.map +7 -0
- package/dist/session-lock-manager.d.ts +100 -0
- package/dist/session-lock-manager.d.ts.map +1 -0
- package/dist/session-lock-manager.js +199 -0
- package/dist/session-lock-manager.js.map +7 -0
- package/dist/session-manager.d.ts +196 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/session-manager.js +1010 -0
- package/dist/session-manager.js.map +7 -0
- package/dist/session-store.d.ts +190 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/session-store.js +446 -0
- package/dist/session-store.js.map +7 -0
- package/dist/session-version-store.d.ts +83 -0
- package/dist/session-version-store.d.ts.map +1 -0
- package/dist/session-version-store.js +117 -0
- package/dist/session-version-store.js.map +7 -0
- package/dist/type-guards.d.ts +59 -0
- package/dist/type-guards.d.ts.map +1 -0
- package/dist/type-guards.js +40 -0
- package/dist/type-guards.js.map +7 -0
- package/dist/types.d.ts +621 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +23 -0
- package/dist/types.js.map +7 -0
- package/dist/validation.d.ts +22 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +323 -0
- package/dist/validation.js.map +7 -0
- package/package.json +135 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/session-manager.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Session Manager - owns session lifecycle, command execution, and subscriber maps.\n *\n * RESPONSIBILITIES (per AGENTS.md):\n * - Orchestration: coordinates stores, engines, sessions\n * - Session lifecycle (create, delete, list, load)\n * - Subscriber and event broadcast management\n * - Command execution pipeline (tracking, rate limiting, replay)\n *\n * DOES NOT:\n * - Handle server commands directly (delegates to server-command-handlers.ts)\n * - Handle session commands directly (delegates to command-router.ts)\n * - Mutate state directly (delegates to stores)\n */\n\nimport {\n type AgentSession,\n createAgentSession,\n type AgentSessionEvent,\n} from \"@mariozechner/pi-coding-agent\";\nimport type {\n CommandLifecycleEvent,\n RpcCommand,\n RpcEvent,\n RpcResponse,\n SessionInfo,\n SessionResolver,\n Subscriber,\n} from \"./types.js\";\nimport {\n getCommandDependsOn,\n getCommandId,\n getCommandIdempotencyKey,\n getCommandIfSessionVersion,\n getCommandType,\n getSessionId,\n} from \"./types.js\";\nimport { routeSessionCommand } from \"./command-router.js\";\nimport {\n routeServerCommand,\n executeLLMCommand,\n executeBashCommand,\n type ServerCommandContext,\n} from \"./server-command-handlers.js\";\nimport { ExtensionUIManager } from \"./extension-ui.js\";\nimport { createServerUIContext } from \"./server-ui-context.js\";\nimport { validateCommand, formatValidationErrors } from \"./validation.js\";\nimport { ResourceGovernor, DEFAULT_CONFIG } from \"./resource-governor.js\";\nimport {\n CommandReplayStore,\n type InFlightCommandRecord,\n SYNTHETIC_ID_PREFIX,\n} from \"./command-replay-store.js\";\nimport { SessionVersionStore } from \"./session-version-store.js\";\nimport { CommandExecutionEngine } from \"./command-execution-engine.js\";\nimport { SessionLockManager } from \"./session-lock-manager.js\";\nimport { SessionStore, type StoredSessionInfo } from \"./session-store.js\";\nimport { CircuitBreakerManager, type CircuitBreakerConfig } from \"./circuit-breaker.js\";\nimport { BashCircuitBreaker, type BashCircuitBreakerConfig } from \"./bash-circuit-breaker.js\";\n\n/** Default timeout for session commands (5 minutes for LLM operations) */\nconst DEFAULT_COMMAND_TIMEOUT_MS = 5 * 60 * 1000;\n\n/** Default graceful shutdown timeout (30 seconds) */\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 30 * 1000;\n\n/** Short command timeout (30 seconds) */\nconst SHORT_COMMAND_TIMEOUT_MS = 30 * 1000;\n\n/** Max time to wait for a dependency command to complete. */\nconst DEPENDENCY_WAIT_TIMEOUT_MS = 30 * 1000;\n\n/**\n * npm env keys inherited from npm scripts that can hijack global installs.\n * When present (e.g. npm_config_prefix=<project>), createAgentSession's\n * package manager may install \"global\" packages into the project directory.\n */\nconst SANITIZED_NPM_ENV_KEYS = [\"npm_config_prefix\", \"NPM_CONFIG_PREFIX\"] as const;\n\nexport interface SessionManagerRuntimeOptions {\n defaultCommandTimeoutMs?: number;\n shortCommandTimeoutMs?: number;\n dependencyWaitTimeoutMs?: number;\n idempotencyTtlMs?: number;\n /** Server version for session metadata tracking */\n serverVersion?: string;\n /** Circuit breaker configuration (optional, uses defaults if not provided) */\n circuitBreakerConfig?: Partial<Omit<CircuitBreakerConfig, \"providerName\">>;\n /** Bash circuit breaker configuration (optional, uses defaults if not provided) */\n bashCircuitBreakerConfig?: Partial<BashCircuitBreakerConfig>;\n}\n\nexport class PiSessionManager implements SessionResolver {\n private sessions = new Map<string, AgentSession>();\n private sessionCreatedAt = new Map<string, Date>();\n private subscribers = new Set<Subscriber>();\n private unsubscribers = new Map<string, () => void>();\n private governor: ResourceGovernor;\n\n /** Command replay and idempotency store. */\n private replayStore: CommandReplayStore;\n /** Session version store. */\n private versionStore: SessionVersionStore;\n /** Command execution engine. */\n private executionEngine: CommandExecutionEngine;\n /** Session ID lock manager for preventing create/delete races. */\n private lockManager: SessionLockManager;\n /** Session metadata store for persistence across restarts (ADR-0007). */\n private sessionStore: SessionStore;\n /** Circuit breaker for LLM providers (ADR-0010). */\n private circuitBreakers: CircuitBreakerManager;\n /** Circuit breaker for bash commands. */\n private bashCircuitBreaker: BashCircuitBreaker;\n\n // Shutdown state (single source of truth - server.ts delegates to this)\n private isShuttingDown = false;\n private inFlightCommands = new Set<Promise<unknown>>();\n\n // Periodic cleanup timers\n private sessionExpirationTimer: NodeJS.Timeout | null = null;\n\n private readonly defaultCommandTimeoutMs: number;\n private readonly shortCommandTimeoutMs: number;\n private readonly dependencyWaitTimeoutMs: number;\n\n // Extension UI request tracking\n private extensionUI = new ExtensionUIManager((sessionId: string, event: AgentSessionEvent) =>\n this.broadcastEvent(sessionId, event)\n );\n\n /** Optional memory metrics provider (set by server for ADR-0016) */\n private memoryMetricsProvider: (() => Record<string, unknown> | undefined) | null = null;\n\n constructor(governor?: ResourceGovernor, options: SessionManagerRuntimeOptions = {}) {\n this.governor = governor ?? new ResourceGovernor(DEFAULT_CONFIG);\n this.defaultCommandTimeoutMs =\n typeof options.defaultCommandTimeoutMs === \"number\" && options.defaultCommandTimeoutMs > 0\n ? options.defaultCommandTimeoutMs\n : DEFAULT_COMMAND_TIMEOUT_MS;\n this.shortCommandTimeoutMs =\n typeof options.shortCommandTimeoutMs === \"number\" && options.shortCommandTimeoutMs > 0\n ? options.shortCommandTimeoutMs\n : SHORT_COMMAND_TIMEOUT_MS;\n this.dependencyWaitTimeoutMs =\n typeof options.dependencyWaitTimeoutMs === \"number\" && options.dependencyWaitTimeoutMs > 0\n ? options.dependencyWaitTimeoutMs\n : DEPENDENCY_WAIT_TIMEOUT_MS;\n\n this.replayStore = new CommandReplayStore({\n idempotencyTtlMs: options.idempotencyTtlMs,\n });\n this.versionStore = new SessionVersionStore();\n this.executionEngine = new CommandExecutionEngine(\n this.replayStore,\n this.versionStore,\n this, // SessionResolver - the NEXUS seam\n {\n defaultCommandTimeoutMs: this.defaultCommandTimeoutMs,\n shortCommandTimeoutMs: this.shortCommandTimeoutMs,\n dependencyWaitTimeoutMs: this.dependencyWaitTimeoutMs,\n }\n );\n this.lockManager = new SessionLockManager();\n this.sessionStore = new SessionStore({\n serverVersion: options.serverVersion,\n });\n this.circuitBreakers = new CircuitBreakerManager(options.circuitBreakerConfig);\n this.bashCircuitBreaker = new BashCircuitBreaker(options.bashCircuitBreakerConfig);\n }\n\n /**\n * Get the resource governor for external checks (e.g., message size).\n */\n getGovernor(): ResourceGovernor {\n return this.governor;\n }\n\n /**\n * Get the circuit breaker manager for external access (e.g., admin operations).\n */\n getCircuitBreakers(): CircuitBreakerManager {\n return this.circuitBreakers;\n }\n\n /**\n * Get the bash circuit breaker for external access.\n */\n getBashCircuitBreaker(): BashCircuitBreaker {\n return this.bashCircuitBreaker;\n }\n\n // ==========================================================================\n // SHUTDOWN MANAGEMENT\n // ==========================================================================\n\n /**\n * Check if the server is shutting down.\n */\n isInShutdown(): boolean {\n return this.isShuttingDown;\n }\n\n /**\n * Set the memory metrics provider for ADR-0016 metrics system.\n * Called by PiServer to provide access to MemorySink metrics.\n */\n setMemoryMetricsProvider(provider: () => Record<string, unknown> | undefined): void {\n this.memoryMetricsProvider = provider;\n }\n\n /**\n * Initiate graceful shutdown.\n * - Stops accepting new commands\n * - Broadcasts shutdown notification to all clients\n * - Returns promise that resolves when all in-flight commands complete or timeout\n *\n * Idempotent: calling multiple times returns the same result.\n */\n async initiateShutdown(\n timeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS\n ): Promise<{ drained: number; timedOut: boolean }> {\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n timeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS;\n }\n\n // Idempotent check - only initiate once\n if (this.isShuttingDown) {\n // Return current state - how many commands are still in flight\n const remaining = this.inFlightCommands.size;\n return { drained: 0, timedOut: remaining > 0 };\n }\n\n this.isShuttingDown = true;\n\n // Broadcast shutdown notification\n const shutdownEvent = {\n type: \"server_shutdown\",\n data: { reason: \"graceful_shutdown\", timeoutMs },\n };\n this.broadcast(JSON.stringify(shutdownEvent));\n\n // Wait for in-flight commands with timeout\n const inFlightCount = this.inFlightCommands.size;\n\n if (inFlightCount === 0) {\n return { drained: 0, timedOut: false };\n }\n\n // Snapshot the current in-flight commands\n const snapshot = [...this.inFlightCommands];\n\n const drainPromise = Promise.allSettled(snapshot);\n\n const timeoutPromise = new Promise<{ drained: number; timedOut: boolean }>((resolve) => {\n setTimeout(() => {\n // Count how many from the original snapshot are still pending\n const stillPending = snapshot.filter((p) => this.inFlightCommands.has(p)).length;\n const drained = inFlightCount - stillPending;\n resolve({ drained, timedOut: true });\n }, timeoutMs);\n });\n\n const drainResult = new Promise<{ drained: number; timedOut: boolean }>((resolve) => {\n drainPromise.then(() => {\n resolve({ drained: inFlightCount, timedOut: false });\n });\n });\n\n return Promise.race([drainResult, timeoutPromise]);\n }\n\n /**\n * Dispose all sessions. Call after shutdown drain completes.\n */\n disposeAllSessions(): { disposed: number; failed: number } {\n let disposed = 0;\n let failed = 0;\n\n // Snapshot session IDs\n const sessionIds = [...this.sessions.keys()];\n\n for (const sessionId of sessionIds) {\n try {\n // Get session before removing\n const session = this.sessions.get(sessionId);\n\n // Remove from maps first\n this.sessions.delete(sessionId);\n this.sessionCreatedAt.delete(sessionId);\n\n // Unsubscribe\n const unsubscribe = this.unsubscribers.get(sessionId);\n if (unsubscribe) {\n this.unsubscribers.delete(sessionId);\n try {\n unsubscribe();\n } catch {\n // Ignore unsubscribe errors during disposal\n }\n }\n\n // Dispose session\n if (session) {\n try {\n session.dispose();\n disposed++;\n } catch {\n failed++;\n }\n }\n } catch {\n failed++;\n }\n }\n\n // Clear runtime registries\n this.versionStore.clear();\n this.executionEngine.clear();\n this.replayStore.clear();\n this.lockManager.clear();\n\n // Clear governor state\n this.governor.cleanupStaleData(new Set());\n\n return { disposed, failed };\n }\n\n /**\n * Get count of in-flight commands.\n */\n getInFlightCount(): number {\n return this.inFlightCommands.size;\n }\n\n /**\n * Register an in-flight command promise for shutdown draining.\n */\n private registerInFlightCommand<T>(promise: Promise<T>): void {\n this.inFlightCommands.add(promise);\n\n const cleanup = () => {\n this.inFlightCommands.delete(promise);\n };\n\n promise.then(cleanup, cleanup);\n }\n\n private broadcastCommandLifecycle(\n phase: CommandLifecycleEvent[\"type\"],\n data: CommandLifecycleEvent[\"data\"]\n ): void {\n const event: CommandLifecycleEvent = {\n type: phase,\n data,\n };\n this.broadcast(JSON.stringify(event));\n }\n\n // ==========================================================================\n // SESSION LIFECYCLE\n // ==========================================================================\n\n async createSession(sessionId: string, cwd?: string): Promise<SessionInfo> {\n // Validate session ID (validation doesn't need lock)\n const sessionIdError = this.governor.validateSessionId(sessionId);\n if (sessionIdError) {\n throw new Error(sessionIdError);\n }\n\n // Validate cwd if provided\n if (cwd) {\n const cwdError = this.governor.validateCwd(cwd);\n if (cwdError) {\n throw new Error(cwdError);\n }\n }\n\n // Acquire lock for this session ID to prevent concurrent create/delete races\n const lock = await this.lockManager.acquire(sessionId, \"createSession\");\n\n try {\n // Check for duplicate UNDER LOCK - prevents race condition\n if (this.sessions.has(sessionId)) {\n throw new Error(`Session ${sessionId} already exists`);\n }\n\n // Atomically reserve a session slot (prevents resource exhaustion)\n if (!this.governor.tryReserveSessionSlot()) {\n throw new Error(\n `Session limit reached (${this.governor.getConfig().maxSessions} sessions)`\n );\n }\n\n try {\n const { session } = await this.createAgentSessionWithSanitizedNpmEnv({\n cwd: cwd ?? process.cwd(),\n });\n\n // Wire extension UI - this is the nexus intervention!\n // Without this, extension UI requests (select, confirm, input, etc.) hang.\n await session.bindExtensions({\n uiContext: createServerUIContext(sessionId, this.extensionUI, (sid, event) =>\n this.broadcastEvent(sid, event)\n ),\n });\n\n // Final check still under lock - handles edge case of session creation side effects\n if (this.sessions.has(sessionId)) {\n session.dispose();\n throw new Error(`Session ${sessionId} already exists`);\n }\n\n this.sessions.set(sessionId, session);\n this.sessionCreatedAt.set(sessionId, new Date());\n this.versionStore.initialize(sessionId);\n // Record heartbeat (session count already incremented by tryReserveSessionSlot)\n this.governor.recordHeartbeat(sessionId);\n\n // Subscribe to all events from this session\n const unsubscribe = session.subscribe((event: AgentSessionEvent) => {\n this.broadcastEvent(sessionId, event);\n });\n this.unsubscribers.set(sessionId, unsubscribe);\n\n // ADR-0007: Persist session metadata\n const sessionInfo = this.getSessionInfo(sessionId)!;\n if (!session.sessionFile) {\n throw new Error(\"Session created without session file - cannot persist\");\n }\n await this.sessionStore.save({\n sessionId,\n sessionFile: session.sessionFile,\n cwd: cwd ?? process.cwd(),\n createdAt: sessionInfo.createdAt,\n modelId: session.model?.id,\n });\n\n return sessionInfo;\n } catch (error) {\n // Release the slot if session creation failed\n this.governor.releaseSessionSlot();\n throw error;\n }\n } finally {\n lock.release();\n }\n }\n\n async deleteSession(sessionId: string): Promise<void> {\n // Acquire lock for this session ID to prevent concurrent create/delete races\n const lock = await this.lockManager.acquire(sessionId, \"deleteSession\");\n\n try {\n const session = this.sessions.get(sessionId);\n if (!session) {\n throw new Error(`Session ${sessionId} not found`);\n }\n\n // Cancel any pending extension UI requests for this session\n this.extensionUI.cancelSessionRequests(sessionId);\n\n // Remove from maps first to prevent new operations\n this.sessions.delete(sessionId);\n this.sessionCreatedAt.delete(sessionId);\n this.versionStore.delete(sessionId);\n this.governor.unregisterSession(sessionId);\n\n // Clean up stale governor data for this session\n this.governor.cleanupStaleData(new Set(this.sessions.keys()));\n\n // Unsubscribe from events\n const unsubscribe = this.unsubscribers.get(sessionId);\n if (unsubscribe) {\n this.unsubscribers.delete(sessionId);\n try {\n unsubscribe();\n } catch (error) {\n console.error(`[deleteSession] Failed to unsubscribe:`, error);\n }\n }\n\n // Dispose the session\n try {\n session.dispose();\n } catch (error) {\n console.error(`[deleteSession] Failed to dispose session:`, error);\n }\n\n // Remove this session from all subscriber subscriptions\n for (const subscriber of this.subscribers) {\n subscriber.subscribedSessions.delete(sessionId);\n }\n\n // ADR-0007: Remove session metadata\n await this.sessionStore.delete(sessionId);\n } finally {\n lock.release();\n }\n }\n\n getSession(sessionId: string): AgentSession | undefined {\n return this.sessions.get(sessionId);\n }\n\n getSessionInfo(sessionId: string): SessionInfo | undefined {\n const session = this.sessions.get(sessionId);\n if (!session) return undefined;\n\n const createdAt = this.sessionCreatedAt.get(sessionId);\n if (!createdAt) {\n // Invariant violation: session exists but createdAt is missing\n console.error(`[getSessionInfo] Missing createdAt for session ${sessionId}`);\n return undefined;\n }\n\n return {\n sessionId,\n sessionName: session.sessionName,\n sessionFile: session.sessionFile,\n model: session.model,\n thinkingLevel: session.thinkingLevel,\n isStreaming: session.isStreaming,\n messageCount: session.messages.length,\n createdAt: createdAt.toISOString(),\n };\n }\n\n listSessions(): SessionInfo[] {\n const infos: SessionInfo[] = [];\n for (const sessionId of this.sessions.keys()) {\n const info = this.getSessionInfo(sessionId);\n if (info) infos.push(info);\n }\n return infos;\n }\n\n // ==========================================================================\n // SESSION PERSISTENCE (ADR-0007)\n // ==========================================================================\n\n /**\n * List stored sessions that can be loaded.\n * These are sessions that existed in previous server runs OR discovered on disk.\n */\n async listStoredSessions(): Promise<StoredSessionInfo[]> {\n return this.sessionStore.listAllSessions();\n }\n\n /**\n * Load a session from a stored session file.\n * Creates a new in-memory session that reads from the existing session file.\n */\n async loadSession(sessionId: string, sessionPath: string): Promise<SessionInfo> {\n // Validate session ID\n const sessionIdError = this.governor.validateSessionId(sessionId);\n if (sessionIdError) {\n throw new Error(sessionIdError);\n }\n\n // Acquire lock for this session ID\n const lock = await this.lockManager.acquire(sessionId, \"loadSession\");\n\n try {\n // Check for duplicate UNDER LOCK\n if (this.sessions.has(sessionId)) {\n throw new Error(`Session ${sessionId} already exists`);\n }\n\n // Atomically reserve a session slot\n if (!this.governor.tryReserveSessionSlot()) {\n throw new Error(\n `Session limit reached (${this.governor.getConfig().maxSessions} sessions)`\n );\n }\n\n try {\n // Create session and switch to the specified file\n const { session } = await this.createAgentSessionWithSanitizedNpmEnv({\n cwd: process.cwd(),\n });\n\n // Switch to the specified session file\n const switched = await session.switchSession(sessionPath);\n if (!switched) {\n session.dispose();\n throw new Error(`Failed to load session from ${sessionPath}`);\n }\n\n // Wire extension UI\n await session.bindExtensions({\n uiContext: createServerUIContext(sessionId, this.extensionUI, (sid, event) =>\n this.broadcastEvent(sid, event)\n ),\n });\n\n // Final check still under lock\n if (this.sessions.has(sessionId)) {\n session.dispose();\n throw new Error(`Session ${sessionId} already exists`);\n }\n\n this.sessions.set(sessionId, session);\n this.sessionCreatedAt.set(sessionId, new Date());\n this.versionStore.initialize(sessionId);\n this.governor.recordHeartbeat(sessionId);\n\n // Subscribe to events\n const unsubscribe = session.subscribe((event: AgentSessionEvent) => {\n this.broadcastEvent(sessionId, event);\n });\n this.unsubscribers.set(sessionId, unsubscribe);\n\n // Update session metadata\n const sessionInfo = this.getSessionInfo(sessionId)!;\n if (!session.sessionFile) {\n throw new Error(\"Session loaded without session file - cannot persist metadata\");\n }\n await this.sessionStore.save({\n sessionId,\n sessionFile: session.sessionFile,\n cwd: process.cwd(),\n createdAt: sessionInfo.createdAt,\n modelId: session.model?.id,\n });\n\n return sessionInfo;\n } catch (error) {\n this.governor.releaseSessionSlot();\n throw error;\n }\n } finally {\n lock.release();\n }\n }\n\n /**\n * Get the session store for direct access (e.g., cleanup).\n */\n getSessionStore(): SessionStore {\n return this.sessionStore;\n }\n\n /**\n * Start periodic cleanup of orphaned session metadata and expired sessions.\n * @param intervalMs Cleanup interval in milliseconds (default: 1 hour)\n */\n startSessionCleanup(intervalMs?: number): void {\n this.sessionStore.startPeriodicCleanup(intervalMs);\n this.startSessionExpirationCheck(intervalMs);\n }\n\n /**\n * Stop periodic cleanup.\n */\n stopSessionCleanup(): void {\n this.sessionStore.stopPeriodicCleanup();\n this.stopSessionExpirationCheck();\n }\n\n /**\n * Run a one-time cleanup of orphaned session metadata and expired sessions.\n */\n async cleanupSessions(): Promise<{ removed: number; kept: number }> {\n // Clean up expired sessions first\n await this.cleanupExpiredSessions();\n // Then clean up orphaned metadata\n return this.sessionStore.cleanup();\n }\n\n /**\n * Start periodic check for expired sessions (maxSessionLifetimeMs).\n */\n private startSessionExpirationCheck(intervalMs = 3600000): void {\n if (this.sessionExpirationTimer) {\n return; // Already running\n }\n\n this.sessionExpirationTimer = setInterval(() => {\n this.cleanupExpiredSessions().catch((error) => {\n console.error(\"[SessionManager] Session expiration cleanup failed:\", error);\n });\n }, intervalMs);\n\n // Don't prevent process exit\n if (this.sessionExpirationTimer.unref) {\n this.sessionExpirationTimer.unref();\n }\n }\n\n /**\n * Stop periodic session expiration check.\n */\n private stopSessionExpirationCheck(): void {\n if (this.sessionExpirationTimer) {\n clearInterval(this.sessionExpirationTimer);\n this.sessionExpirationTimer = null;\n }\n }\n\n /**\n * Clean up sessions that have exceeded maxSessionLifetimeMs.\n * Also cleans up stale circuit breakers for unused providers.\n */\n private async cleanupExpiredSessions(): Promise<void> {\n const expiredIds = this.governor.getExpiredSessions();\n\n for (const sessionId of expiredIds) {\n try {\n console.error(`[SessionManager] Deleting expired session: ${sessionId}`);\n await this.deleteSession(sessionId);\n } catch (error) {\n // Session may have been deleted already or deletion failed\n console.error(`[SessionManager] Failed to delete expired session ${sessionId}:`, error);\n }\n }\n\n // Clean up stale circuit breakers (ADR-0011)\n const staleBreakersRemoved = this.circuitBreakers.cleanupStaleBreakers();\n if (staleBreakersRemoved > 0) {\n console.error(`[SessionManager] Cleaned up ${staleBreakersRemoved} stale circuit breakers`);\n }\n\n // Clean up stale bash circuit breakers\n const staleBashBreakersRemoved = this.bashCircuitBreaker.cleanupStale();\n if (staleBashBreakersRemoved > 0) {\n console.error(\n `[SessionManager] Cleaned up ${staleBashBreakersRemoved} stale bash circuit breakers`\n );\n }\n }\n\n // ==========================================================================\n // SUBSCRIBER MANAGEMENT\n // ==========================================================================\n\n addSubscriber(subscriber: Subscriber): void {\n this.subscribers.add(subscriber);\n }\n\n removeSubscriber(subscriber: Subscriber): void {\n this.subscribers.delete(subscriber);\n }\n\n subscribeToSession(subscriber: Subscriber, sessionId: string): void {\n if (!this.sessions.has(sessionId)) {\n throw new Error(`Session ${sessionId} not found`);\n }\n subscriber.subscribedSessions.add(sessionId);\n }\n\n unsubscribeFromSession(subscriber: Subscriber, sessionId: string): void {\n subscriber.subscribedSessions.delete(sessionId);\n }\n\n // ==========================================================================\n // EVENT BROADCAST\n // ==========================================================================\n\n private broadcastEvent(sessionId: string, event: AgentSessionEvent): void {\n // Note: extension_ui_request events are NOT AgentSessionEvents.\n // They come through ExtensionUIContext (wired via bindExtensions)\n // and are broadcast via createServerUIContext -> ExtensionUIManager.broadcastUIRequest.\n\n const rpcEvent: RpcEvent = {\n type: \"event\",\n sessionId,\n event,\n };\n\n let data: string;\n try {\n data = JSON.stringify(rpcEvent);\n } catch (error) {\n // Log serialization errors but don't crash\n console.error(`[broadcastEvent] JSON serialization failed:`, error);\n return;\n }\n\n // Snapshot subscribers to prevent mutation during iteration\n const snapshot = [...this.subscribers];\n for (const subscriber of snapshot) {\n if (subscriber.subscribedSessions.has(sessionId)) {\n try {\n subscriber.send(data);\n } catch (error) {\n // Log failed sends for observability\n console.error(`[broadcastEvent] Failed to send to subscriber:`, error);\n // Subscriber will be cleaned up by close handler\n }\n }\n }\n }\n\n broadcast(data: string): void {\n // Snapshot subscribers to prevent mutation during iteration\n const snapshot = [...this.subscribers];\n for (const subscriber of snapshot) {\n try {\n subscriber.send(data);\n } catch (error) {\n // Log failed sends for observability\n console.error(`[broadcast] Failed to send to subscriber:`, error);\n }\n }\n }\n\n // ==========================================================================\n // COMMAND EXECUTION CONTEXT\n // ==========================================================================\n\n /**\n * Create the command execution context for server command handlers.\n * This is the NEXUS seam - provides everything handlers need without\n * direct coupling to SessionManager internals.\n */\n private createCommandContext(): ServerCommandContext {\n return {\n getSession: (sessionId: string) => this.sessions.get(sessionId),\n getSessionInfo: (sessionId: string) => this.getSessionInfo(sessionId),\n listSessions: () => this.listSessions(),\n createSession: (sessionId: string, cwd?: string) => this.createSession(sessionId, cwd),\n deleteSession: (sessionId: string) => this.deleteSession(sessionId),\n loadSession: (sessionId: string, sessionPath: string) =>\n this.loadSession(sessionId, sessionPath),\n listStoredSessions: () => this.listStoredSessions(),\n getMetrics: () => this.buildMetricsResponse(),\n getMemoryMetrics: () => this.memoryMetricsProvider?.(),\n getHealth: () => this.buildHealthResponse(),\n handleUIResponse: (command) =>\n this.extensionUI.handleUIResponse({\n id: command.id,\n sessionId: command.sessionId,\n type: \"extension_ui_response\",\n requestId: command.requestId,\n response: command.response,\n }),\n routeSessionCommand: (session, command, getSessionInfo) =>\n routeSessionCommand(session, command, getSessionInfo),\n generateSessionId: () => this.generateSessionId(),\n recordHeartbeat: (sessionId: string) => this.governor.recordHeartbeat(sessionId),\n getCircuitBreakers: () => ({\n hasOpenCircuit: () => this.circuitBreakers.hasOpenCircuit(),\n getBreaker: (provider: string) => {\n const breaker = this.circuitBreakers.getBreaker(provider);\n return {\n canExecute: () => breaker.canExecute(),\n recordSuccess: (elapsedMs: number) => breaker.recordSuccess(elapsedMs),\n recordFailure: (type: \"timeout\" | \"error\") => breaker.recordFailure(type),\n };\n },\n }),\n getBashCircuitBreaker: () => ({\n canExecute: (sessionId: string) => this.bashCircuitBreaker.canExecute(sessionId),\n recordSuccess: (sessionId: string) => this.bashCircuitBreaker.recordSuccess(sessionId),\n recordTimeout: (sessionId: string) => this.bashCircuitBreaker.recordTimeout(sessionId),\n recordSpawnError: (sessionId: string) =>\n this.bashCircuitBreaker.recordSpawnError(sessionId),\n hasOpenCircuit: () => this.bashCircuitBreaker.hasOpenCircuit(),\n getMetrics: () => this.bashCircuitBreaker.getMetrics(),\n }),\n getDefaultCommandTimeoutMs: () => this.defaultCommandTimeoutMs,\n };\n }\n\n /**\n * Build the metrics response (extracted for handler use).\n */\n private buildMetricsResponse(): RpcResponse {\n const governorMetrics = this.governor.getMetrics();\n const replayStats = this.replayStore.getStats();\n const versionStats = this.versionStore.getStats();\n const executionStats = this.executionEngine.getStats();\n const lockStats = this.lockManager.getStats();\n const extensionUIStats = this.extensionUI.getStats();\n const circuitBreakerMetrics = this.circuitBreakers.getAllMetrics();\n const bashCircuitBreakerMetrics = this.bashCircuitBreaker.getMetrics();\n const sessionStoreStats = {\n metadataResetCount: this.sessionStore.getMetadataResetCount(),\n };\n return {\n type: \"response\",\n command: \"get_metrics\",\n success: true,\n data: {\n ...governorMetrics,\n stores: {\n replay: replayStats,\n version: versionStats,\n execution: executionStats,\n lock: lockStats,\n extensionUI: extensionUIStats,\n sessionStore: sessionStoreStats,\n },\n circuitBreakers: circuitBreakerMetrics,\n bashCircuitBreaker: bashCircuitBreakerMetrics,\n },\n };\n }\n\n /**\n * Build the health check response (extracted for handler use).\n */\n private buildHealthResponse(): RpcResponse {\n const health = this.governor.isHealthy();\n const hasOpenCircuit = this.circuitBreakers.hasOpenCircuit();\n const hasOpenBashCircuit = this.bashCircuitBreaker.hasOpenCircuit();\n\n const issues = [...health.issues];\n if (hasOpenCircuit) {\n issues.push(\"One or more LLM provider circuits are open\");\n }\n if (hasOpenBashCircuit) {\n issues.push(\"Bash command circuit breaker is open\");\n }\n\n return {\n type: \"response\",\n command: \"health_check\",\n success: true,\n data: {\n ...health,\n hasOpenCircuit,\n hasOpenBashCircuit,\n issues,\n },\n };\n }\n\n // ==========================================================================\n // COMMAND EXECUTION\n // ==========================================================================\n\n async executeCommand(command: RpcCommand): Promise<RpcResponse> {\n const id = getCommandId(command);\n const commandType = getCommandType(command);\n const sessionId = getSessionId(command);\n const commandId = this.replayStore.getOrCreateCommandId(command);\n const dependsOn = getCommandDependsOn(command) ?? [];\n const ifSessionVersion = getCommandIfSessionVersion(command);\n const idempotencyKey = getCommandIdempotencyKey(command);\n const laneKey = this.executionEngine.getLaneKey(command);\n const fingerprint = this.replayStore.getCommandFingerprint(command);\n\n // Check for shutdown - reject new commands during shutdown\n if (this.isShuttingDown) {\n return {\n id,\n type: \"response\",\n command: commandType ?? \"unknown\",\n success: false,\n error: \"Server is shutting down\",\n };\n }\n\n // Input validation FIRST (don't rate-limit invalid commands)\n const validationErrors = validateCommand(command);\n if (validationErrors.length > 0) {\n return {\n id,\n type: \"response\",\n command: commandType ?? \"unknown\",\n success: false,\n error: `Validation failed: ${formatValidationErrors(validationErrors)}`,\n };\n }\n\n this.replayStore.cleanupIdempotencyCache();\n\n this.broadcastCommandLifecycle(\"command_accepted\", {\n commandId,\n commandType,\n sessionId,\n dependsOn,\n ifSessionVersion,\n idempotencyKey,\n });\n\n const finalizeResponse = (response: RpcResponse): RpcResponse => {\n this.broadcastCommandLifecycle(\"command_finished\", {\n commandId,\n commandType,\n sessionId,\n dependsOn,\n ifSessionVersion,\n idempotencyKey,\n success: response.success,\n error: response.success ? undefined : response.error,\n sessionVersion: response.sessionVersion,\n replayed: response.replayed,\n });\n return response;\n };\n\n // Check for replay opportunities or conflicts (ADR-0001: Free replay)\n // Replay is O(1) lookup - no execution cost, should not consume rate limit\n const replayCheck = this.replayStore.checkReplay(command, fingerprint);\n\n if (replayCheck.kind === \"conflict\") {\n return finalizeResponse(replayCheck.response);\n }\n\n if (replayCheck.kind === \"replay_cached\") {\n return finalizeResponse(replayCheck.response);\n }\n\n if (replayCheck.kind === \"replay_inflight\") {\n const replayed = await replayCheck.promise;\n return finalizeResponse(replayed);\n }\n\n // ADR-0001: Rate limiting only for NEW executions (replay is free)\n const rateLimitKey = sessionId ?? \"_server_\";\n const rateLimitResult = this.governor.canExecuteCommand(rateLimitKey);\n if (!rateLimitResult.allowed) {\n return finalizeResponse({\n id,\n type: \"response\",\n command: commandType,\n success: false,\n error: rateLimitResult.reason,\n });\n }\n\n // Additional rate limiting for extension_ui_response (prevents spam)\n if (commandType === \"extension_ui_response\" && sessionId) {\n const extRateLimitResult = this.governor.canExecuteExtensionUIResponse(sessionId);\n if (!extRateLimitResult.allowed) {\n return finalizeResponse({\n id,\n type: \"response\",\n command: commandType,\n success: false,\n error: extRateLimitResult.reason,\n });\n }\n }\n\n // Reject before starting execution if we cannot track this explicit command ID.\n // This prevents side effects from commands that are rejected as \"server busy\".\n const isExplicitId = typeof id === \"string\" && !id.startsWith(SYNTHETIC_ID_PREFIX);\n if (isExplicitId && !this.replayStore.canRegisterInFlight(id)) {\n return finalizeResponse({\n id,\n type: \"response\",\n command: commandType,\n success: false,\n error: \"Server busy - too many concurrent commands. Please retry.\",\n });\n }\n\n // Proceed with normal execution\n const commandExecution = this.executionEngine.runOnLane<RpcResponse>(\n laneKey,\n async (): Promise<RpcResponse> => {\n this.broadcastCommandLifecycle(\"command_started\", {\n commandId,\n commandType,\n sessionId,\n dependsOn,\n ifSessionVersion,\n idempotencyKey,\n });\n\n if (dependsOn.includes(commandId)) {\n return {\n id,\n type: \"response\",\n command: commandType,\n success: false,\n error: `Command '${commandId}' cannot depend on itself`,\n };\n }\n\n if (dependsOn.length > 0) {\n const dependencyResult = await this.executionEngine.awaitDependencies(dependsOn, laneKey);\n if (!dependencyResult.ok) {\n return {\n id,\n type: \"response\",\n command: commandType,\n success: false,\n error: dependencyResult.error,\n };\n }\n }\n\n if (sessionId !== undefined && ifSessionVersion !== undefined) {\n const versionError = this.executionEngine.checkSessionVersion(\n sessionId,\n ifSessionVersion,\n commandType\n );\n if (versionError) {\n return {\n id,\n type: \"response\" as const,\n command: commandType,\n success: false,\n error: versionError.error,\n };\n }\n }\n\n const rawResponse = await this.executeCommandInternal(command, id, commandType);\n return this.versionStore.applyVersion(command, rawResponse);\n }\n );\n\n // Track in-flight if we have an explicit ID\n let inFlightRecord: InFlightCommandRecord | undefined;\n if (id) {\n inFlightRecord = {\n commandType,\n laneKey,\n fingerprint,\n promise: commandExecution,\n };\n\n // ADR-0001: Reject if in-flight limit reached (don't evict - breaks dependencies)\n const registered = this.replayStore.registerInFlight(id, inFlightRecord);\n if (!registered) {\n return finalizeResponse({\n id,\n type: \"response\",\n command: commandType,\n success: false,\n error: \"Server busy - too many concurrent commands. Please retry.\",\n });\n }\n }\n\n this.registerInFlightCommand(commandExecution);\n\n let response: RpcResponse;\n\n try {\n response = await this.executionEngine.executeWithTimeout(\n commandType,\n commandExecution,\n command\n );\n } catch (error) {\n // ADR-0001: Create timeout response and store it BEFORE returning\n response = {\n id,\n type: \"response\",\n command: commandType,\n success: false,\n error: error instanceof Error ? error.message : String(error),\n timedOut: true, // Mark as timeout for debugging\n };\n }\n\n // ADR-0001: ATOMIC OUTCOME STORAGE\n // Store outcome BEFORE returning (not in async callback)\n // This ensures same command ID always returns same response\n //\n // Only store outcomes for EXPLICIT client IDs (not synthetic IDs).\n // Synthetic IDs (anon:timestamp:seq) are server-generated for anonymous\n // commands and should not be stored to prevent unbounded memory growth.\n // Clients must provide explicit IDs if they want replay semantics.\n if (isExplicitId) {\n try {\n this.replayStore.storeCommandOutcome({\n commandId: id,\n commandType,\n laneKey,\n fingerprint,\n success: response.success,\n error: response.success ? undefined : response.error,\n response,\n sessionVersion: response.sessionVersion,\n finishedAt: Date.now(),\n });\n } catch (outcomeError) {\n console.error(`[executeCommand] Failed to store command outcome for ${id}:`, outcomeError);\n }\n\n // Unregister in-flight after storing outcome\n if (this.replayStore.getInFlight(id) === inFlightRecord) {\n this.replayStore.unregisterInFlight(id, inFlightRecord!);\n }\n } else if (id && this.replayStore.getInFlight(id) === inFlightRecord) {\n // Synthetic ID: still need to unregister in-flight\n this.replayStore.unregisterInFlight(id, inFlightRecord!);\n }\n\n // Cache terminal idempotency outcome (including timeout responses)\n if (idempotencyKey) {\n this.replayStore.cacheIdempotencyResult({\n command,\n idempotencyKey,\n commandType,\n fingerprint,\n response,\n });\n }\n\n return finalizeResponse(response);\n }\n\n /**\n * Internal command execution (called after tracking and rate limiting).\n * Routes to server command handlers or session command handlers.\n */\n private async executeCommandInternal(\n command: RpcCommand,\n id: string | undefined,\n commandType: string\n ): Promise<RpcResponse> {\n const failResponse = (error: string, responseCommand = commandType): RpcResponse => {\n return {\n id,\n type: \"response\",\n command: responseCommand,\n success: false,\n error,\n };\n };\n\n try {\n const context = this.createCommandContext();\n\n // Try server command handlers first\n const serverResponse = routeServerCommand(command, context);\n if (serverResponse !== undefined) {\n const resolved = await Promise.resolve(serverResponse);\n return { ...resolved, id };\n }\n\n // Session commands - get the session first\n const cmdSessionId = getSessionId(command);\n const session = this.sessions.get(cmdSessionId!);\n if (!session) {\n return failResponse(`Session ${cmdSessionId} not found`);\n }\n\n // Record heartbeat for valid session activity\n this.governor.recordHeartbeat(cmdSessionId!);\n\n // ADR-0010: Circuit breaker for LLM commands\n const llmResponse = await executeLLMCommand(command, session, context);\n if (llmResponse !== undefined) {\n if (!llmResponse.success) {\n return { ...llmResponse, id };\n }\n return llmResponse;\n }\n\n // Bash circuit breaker protection\n const bashResponse = await executeBashCommand(command, session, context);\n if (bashResponse !== undefined) {\n if (!bashResponse.success) {\n return { ...bashResponse, id };\n }\n return bashResponse;\n }\n\n // Other session commands: route without circuit breaker\n const routed = routeSessionCommand(session, command, (sid) => this.getSessionInfo(sid));\n if (routed === undefined) {\n return failResponse(`Unknown command type: ${commandType}`);\n }\n\n const response = await Promise.resolve(routed);\n if (!response.success) {\n return failResponse(response.error ?? \"Unknown error\", response.command);\n }\n return response;\n } catch (error) {\n return failResponse(error instanceof Error ? error.message : String(error));\n }\n }\n\n // ==========================================================================\n // UTILITIES\n // ==========================================================================\n\n /**\n * Create an AgentSession while sanitizing npm prefix env leakage from npm scripts.\n *\n * npm sets npm_config_prefix for child processes. If inherited here,\n * pi-coding-agent's global package installation can be redirected into the\n * current project (e.g. ./lib/node_modules), causing flaky session creation.\n */\n private async createAgentSessionWithSanitizedNpmEnv(\n options: Parameters<typeof createAgentSession>[0]\n ): Promise<Awaited<ReturnType<typeof createAgentSession>>> {\n const snapshots = SANITIZED_NPM_ENV_KEYS.map((key) => ({\n key,\n had: Object.hasOwn(process.env, key),\n value: process.env[key],\n }));\n\n for (const snapshot of snapshots) {\n if (snapshot.had) {\n delete process.env[snapshot.key];\n }\n }\n\n try {\n return await createAgentSession(options);\n } finally {\n for (const snapshot of snapshots) {\n if (!snapshot.had) continue;\n if (snapshot.value === undefined) {\n delete process.env[snapshot.key];\n } else {\n process.env[snapshot.key] = snapshot.value;\n }\n }\n }\n }\n\n private generateSessionId(): string {\n // Use crypto for collision-safe ID generation\n const timestamp = Date.now().toString(36);\n const random = crypto.randomUUID().split(\"-\")[0];\n return `session-${timestamp}-${random}`;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["response"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session Store - persists session metadata across server restarts.
|
|
3
|
+
*
|
|
4
|
+
* The actual session content (messages, etc.) is managed by pi-coding-agent
|
|
5
|
+
* and stored in session files (~/.pi/agent/sessions/*.json).
|
|
6
|
+
*
|
|
7
|
+
* This store tracks:
|
|
8
|
+
* - Which sessions existed (sessionId -> sessionFile mapping)
|
|
9
|
+
* - Session metadata (createdAt, cwd, etc.)
|
|
10
|
+
* - Enables recovery after server restart
|
|
11
|
+
*
|
|
12
|
+
* ADR-0007: Session Persistence
|
|
13
|
+
*/
|
|
14
|
+
import type { SessionInfo } from "./types.js";
|
|
15
|
+
/** Metadata persisted for each session. */
|
|
16
|
+
export interface StoredSessionMetadata {
|
|
17
|
+
/** Unique session identifier */
|
|
18
|
+
sessionId: string;
|
|
19
|
+
/** Path to the session file managed by pi-coding-agent */
|
|
20
|
+
sessionFile: string;
|
|
21
|
+
/** Working directory when session was created */
|
|
22
|
+
cwd: string;
|
|
23
|
+
/** ISO timestamp when session was created */
|
|
24
|
+
createdAt: string;
|
|
25
|
+
/** Optional user-defined session name */
|
|
26
|
+
sessionName?: string;
|
|
27
|
+
/** Last known model (may be stale if session was modified externally) */
|
|
28
|
+
modelId?: string;
|
|
29
|
+
/** Server version that created this record (for migrations) */
|
|
30
|
+
serverVersion: string;
|
|
31
|
+
}
|
|
32
|
+
/** Input for saving session metadata (serverVersion added automatically). */
|
|
33
|
+
export type SaveSessionInput = Omit<StoredSessionMetadata, "serverVersion">;
|
|
34
|
+
/** Session with resolved metadata (combines stored + file system info). */
|
|
35
|
+
export interface StoredSessionInfo extends SessionInfo {
|
|
36
|
+
/** Path to the session file */
|
|
37
|
+
sessionFile: string;
|
|
38
|
+
/** Working directory */
|
|
39
|
+
cwd: string;
|
|
40
|
+
/** Whether the session file still exists on disk */
|
|
41
|
+
fileExists: boolean;
|
|
42
|
+
}
|
|
43
|
+
/** A group of sessions organized by working directory. */
|
|
44
|
+
export interface SessionGroup {
|
|
45
|
+
/** Full working directory path */
|
|
46
|
+
cwd: string;
|
|
47
|
+
/** Display-friendly path (shortened) */
|
|
48
|
+
displayPath: string;
|
|
49
|
+
/** Number of sessions in this group */
|
|
50
|
+
sessionCount: number;
|
|
51
|
+
/** Sessions in this group, sorted by date (newest first) */
|
|
52
|
+
sessions: StoredSessionInfo[];
|
|
53
|
+
}
|
|
54
|
+
/** Configuration for SessionStore */
|
|
55
|
+
export interface SessionStoreConfig {
|
|
56
|
+
/** Directory to store session metadata (default: ~/.pi/agent/server/) */
|
|
57
|
+
dataDir?: string;
|
|
58
|
+
/** Directory where pi-coding-agent stores sessions (default: ~/.pi/agent/sessions/) */
|
|
59
|
+
sessionsDir?: string;
|
|
60
|
+
/** Server version for migration tracking */
|
|
61
|
+
serverVersion?: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Session metadata store.
|
|
65
|
+
*
|
|
66
|
+
* Thread-safety: All operations are atomic via file locking.
|
|
67
|
+
* Callers should use SessionLockManager for in-memory coordination.
|
|
68
|
+
*/
|
|
69
|
+
export declare class SessionStore {
|
|
70
|
+
private readonly dataDir;
|
|
71
|
+
private readonly sessionsDir;
|
|
72
|
+
private readonly serverVersion;
|
|
73
|
+
private readonly metadataPath;
|
|
74
|
+
private metadataCache;
|
|
75
|
+
private lastLoadTime;
|
|
76
|
+
/** Cache TTL in ms (5 seconds) */
|
|
77
|
+
private readonly cacheTtl;
|
|
78
|
+
/** Count of metadata resets due to oversized/corrupt files */
|
|
79
|
+
private metadataResetCount;
|
|
80
|
+
constructor(config?: SessionStoreConfig);
|
|
81
|
+
/**
|
|
82
|
+
* Ensure the data directory exists.
|
|
83
|
+
*/
|
|
84
|
+
private ensureDataDir;
|
|
85
|
+
/**
|
|
86
|
+
* Load metadata from disk (with caching).
|
|
87
|
+
*/
|
|
88
|
+
private loadMetadata;
|
|
89
|
+
/**
|
|
90
|
+
* Validate metadata structure.
|
|
91
|
+
*/
|
|
92
|
+
private isValidMetadata;
|
|
93
|
+
/**
|
|
94
|
+
* Save metadata to disk.
|
|
95
|
+
*/
|
|
96
|
+
private saveMetadata;
|
|
97
|
+
/**
|
|
98
|
+
* Invalidate the cache (force reload on next access).
|
|
99
|
+
*/
|
|
100
|
+
invalidateCache(): void;
|
|
101
|
+
/**
|
|
102
|
+
* Save session metadata.
|
|
103
|
+
*/
|
|
104
|
+
save(meta: SaveSessionInput): Promise<void>;
|
|
105
|
+
/**
|
|
106
|
+
* Load session metadata by ID.
|
|
107
|
+
*/
|
|
108
|
+
load(sessionId: string): Promise<StoredSessionMetadata | null>;
|
|
109
|
+
/**
|
|
110
|
+
* Delete session metadata.
|
|
111
|
+
*/
|
|
112
|
+
delete(sessionId: string): Promise<boolean>;
|
|
113
|
+
/**
|
|
114
|
+
* List all stored session metadata.
|
|
115
|
+
*/
|
|
116
|
+
list(): Promise<StoredSessionMetadata[]>;
|
|
117
|
+
/**
|
|
118
|
+
* List stored sessions with resolved info (includes file existence check).
|
|
119
|
+
*/
|
|
120
|
+
listWithInfo(): Promise<StoredSessionInfo[]>;
|
|
121
|
+
/**
|
|
122
|
+
* Discover all session files in the sessions directory.
|
|
123
|
+
* This scans ~/.pi/agent/sessions/ for .jsonl files.
|
|
124
|
+
* Reads the first line of each file to get the correct cwd.
|
|
125
|
+
*/
|
|
126
|
+
discoverSessions(): Promise<StoredSessionInfo[]>;
|
|
127
|
+
/**
|
|
128
|
+
* Read the first line of a session file to get metadata.
|
|
129
|
+
* Uses readline to properly handle UTF-8 and avoid truncation issues.
|
|
130
|
+
*/
|
|
131
|
+
private readSessionFileMetadata;
|
|
132
|
+
/**
|
|
133
|
+
* Extract timestamp from session filename.
|
|
134
|
+
* 2026-02-22T16-09-11-130Z_6f572984.jsonl → 2026-02-22T16:09:11.130Z
|
|
135
|
+
*/
|
|
136
|
+
private extractTimestampFromFilename;
|
|
137
|
+
/**
|
|
138
|
+
* List all sessions (stored + discovered), merged.
|
|
139
|
+
* Stored sessions take precedence (they have more metadata).
|
|
140
|
+
*/
|
|
141
|
+
listAllSessions(): Promise<StoredSessionInfo[]>;
|
|
142
|
+
/**
|
|
143
|
+
* List all sessions grouped by working directory.
|
|
144
|
+
* Groups are sorted by most recent session (newest first).
|
|
145
|
+
*/
|
|
146
|
+
listSessionsGrouped(): Promise<SessionGroup[]>;
|
|
147
|
+
/**
|
|
148
|
+
* Format a full path for display.
|
|
149
|
+
* /home/tryinget/programming/pi-server → pi-server
|
|
150
|
+
* /home/tryinget → ~
|
|
151
|
+
*/
|
|
152
|
+
private formatDisplayPath;
|
|
153
|
+
/**
|
|
154
|
+
* Update session name in metadata.
|
|
155
|
+
*/
|
|
156
|
+
updateName(sessionId: string, name: string): Promise<boolean>;
|
|
157
|
+
/**
|
|
158
|
+
* Clean up metadata entries for sessions whose files no longer exist.
|
|
159
|
+
*/
|
|
160
|
+
cleanup(): Promise<{
|
|
161
|
+
removed: number;
|
|
162
|
+
kept: number;
|
|
163
|
+
}>;
|
|
164
|
+
/**
|
|
165
|
+
* Get store statistics.
|
|
166
|
+
*/
|
|
167
|
+
getStats(): Promise<{
|
|
168
|
+
sessionCount: number;
|
|
169
|
+
dataDir: string;
|
|
170
|
+
metadataPath: string;
|
|
171
|
+
metadataResetCount: number;
|
|
172
|
+
}>;
|
|
173
|
+
/**
|
|
174
|
+
* Get metadata reset count (synchronous, for metrics).
|
|
175
|
+
* This tracks how many times the metadata file was reset due to being
|
|
176
|
+
* oversized or corrupt, indicating potential disk/filesystem issues.
|
|
177
|
+
*/
|
|
178
|
+
getMetadataResetCount(): number;
|
|
179
|
+
private cleanupInterval;
|
|
180
|
+
/**
|
|
181
|
+
* Start periodic cleanup of orphaned metadata entries.
|
|
182
|
+
* @param intervalMs Cleanup interval in milliseconds (default: 1 hour)
|
|
183
|
+
*/
|
|
184
|
+
startPeriodicCleanup(intervalMs?: number): void;
|
|
185
|
+
/**
|
|
186
|
+
* Stop periodic cleanup.
|
|
187
|
+
*/
|
|
188
|
+
stopPeriodicCleanup(): void;
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=session-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../src/session-store.ts"],"names":[],"mappings":"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"}
|