/**
 * Session Manager Service
 *
 * This service manages agent sessions with provider session ID support for
 * resumable sessions and cross-restart persistence.
 *
 * Key features:
 * - Track active sessions with process metadata
 * - Persist session state to database via Agent entity metadata
 * - Start, resume, stop, and suspend sessions
 * - Message running sessions via agent channels
 * - Query session history per agent
 *
 * @module
 */
import { EventEmitter } from 'node:events';
import type { EntityId, Timestamp, MessageId, DocumentId } from '@stoneforge/core';
import type { QuarryAPI } from '@stoneforge/quarry';
import type { AgentRole, WorkerMode } from '../types/agent.js';
import type { SpawnerService, SessionStatus, UWPTaskInfo } from './spawner.js';
import type { AgentRegistry } from '../services/agent-registry.js';
import type { SettingsService } from '../services/settings-service.js';
import type { OperationLogService } from '../services/operation-log-service.js';
/**
 * Session record with persistence metadata
 */
export interface SessionRecord {
    /** Internal session ID (unique per spawn) */
    readonly id: string;
    /** Provider session ID (for resume) */
    readonly providerSessionId?: string;
    /** Agent entity ID this session belongs to */
    readonly agentId: EntityId;
    /** Agent role */
    readonly agentRole: AgentRole;
    /** Worker mode (for workers) */
    readonly workerMode?: WorkerMode;
    /** Spawn mode: headless or interactive */
    readonly mode: 'headless' | 'interactive';
    /** Process ID (if running) */
    readonly pid?: number;
    /** Current status */
    readonly status: SessionStatus;
    /** Working directory */
    readonly workingDirectory: string;
    /** Git worktree path (for workers) */
    readonly worktree?: string;
    /** Session created timestamp */
    readonly createdAt: Timestamp;
    /** Session started timestamp (when running state entered) */
    readonly startedAt?: Timestamp;
    /** Last activity timestamp */
    readonly lastActivityAt: Timestamp;
    /** Session ended timestamp (when terminated/suspended) */
    readonly endedAt?: Timestamp;
    /** Reason for termination (if applicable) */
    readonly terminationReason?: string;
}
/**
 * Options for starting a new session
 */
export interface StartSessionOptions {
    /** Working directory for the session */
    readonly workingDirectory?: string;
    /** Git worktree path */
    readonly worktree?: string;
    /** Initial prompt to send to the agent */
    readonly initialPrompt?: string;
    /** Additional environment variables */
    readonly environmentVariables?: Record<string, string>;
    /** Whether to use interactive mode (PTY) */
    readonly interactive?: boolean;
    /** Terminal columns (for interactive mode) */
    readonly cols?: number;
    /** Terminal rows (for interactive mode) */
    readonly rows?: number;
    /** LLM model to use for this session (overrides agent metadata) */
    readonly model?: string;
    /** Executable path override for rate limit fallback (takes highest priority in path resolution) */
    readonly executablePathOverride?: string;
}
/**
 * Options for resuming a session
 */
export interface ResumeSessionOptions {
    /** The provider session ID to resume */
    readonly providerSessionId: string;
    /** Working directory for the session */
    readonly workingDirectory?: string;
    /** Git worktree path */
    readonly worktree?: string;
    /** Additional prompt to send after resume */
    readonly resumePrompt?: string;
    /**
     * Whether to check the ready queue before resuming (UWP compliance).
     * If true (default), the session will check for assigned tasks before
     * continuing with the previous context.
     */
    readonly checkReadyQueue?: boolean;
    /**
     * Callback to get ready tasks - allows integration without circular deps.
     * If not provided and checkReadyQueue is true, UWP check will be skipped.
     */
    readonly getReadyTasks?: (agentId: EntityId, limit: number) => Promise<UWPTaskInfo[]>;
}
/**
 * Options for stopping a session
 */
export interface StopSessionOptions {
    /** Whether to attempt graceful shutdown (default: true) */
    readonly graceful?: boolean;
    /** Reason for stopping */
    readonly reason?: string;
}
/**
 * Options for sending a message to a session
 */
export interface MessageSessionOptions {
    /** Document ID containing the message content */
    readonly contentRef?: DocumentId;
    /** Message content (if contentRef not provided) */
    readonly content?: string;
    /** Entity ID of the sender */
    readonly senderId?: EntityId;
    /** Additional metadata for the message */
    readonly metadata?: Record<string, unknown>;
}
/**
 * Result of sending a message to a session
 */
export interface MessageSessionResult {
    /** Whether the message was sent successfully */
    readonly success: boolean;
    /** The message ID if sent via channel */
    readonly messageId?: MessageId;
    /** Error message if failed */
    readonly error?: string;
}
/**
 * Filter options for listing sessions
 */
export interface SessionFilter {
    /** Filter by agent ID */
    readonly agentId?: EntityId;
    /** Filter by agent role */
    readonly role?: AgentRole;
    /** Filter by status */
    readonly status?: SessionStatus | SessionStatus[];
    /** Filter by sessions started after this time */
    readonly startedAfter?: Timestamp;
    /** Filter by sessions started before this time */
    readonly startedBefore?: Timestamp;
    /** Include only sessions with provider session IDs (resumable) */
    readonly resumable?: boolean;
}
/**
 * Session history entry stored in database
 */
export interface SessionHistoryEntry {
    /** Internal session ID */
    readonly id: string;
    /** Provider session ID */
    readonly providerSessionId?: string;
    /** Session status when entry was created */
    readonly status: SessionStatus;
    /** Working directory */
    readonly workingDirectory: string;
    /** Worktree path */
    readonly worktree?: string;
    /** Session started timestamp */
    readonly startedAt?: Timestamp;
    /** Session ended timestamp */
    readonly endedAt?: Timestamp;
    /** Termination reason */
    readonly terminationReason?: string;
}
/**
 * Role-based session history entry - includes agent context
 */
export interface RoleSessionHistoryEntry extends SessionHistoryEntry {
    /** Agent role for this session */
    readonly role: AgentRole;
    /** Agent entity ID */
    readonly agentId: EntityId;
    /** Agent name at time of session */
    readonly agentName?: string;
}
/**
 * Result of the UWP check performed during session resume
 */
export interface ResumeUWPCheckResult {
    /** Whether a ready task was found during the UWP check */
    readonly hasReadyTask: boolean;
    /** The task ID if a ready task was found */
    readonly taskId?: string;
    /** The task title if a ready task was found */
    readonly taskTitle?: string;
    /** The task priority if a ready task was found */
    readonly taskPriority?: number;
    /**
     * Whether the resumed session should process this task first.
     * If true, the resume prompt includes instructions to process the task.
     */
    readonly shouldProcessFirst: boolean;
}
/**
 * Session Manager interface for agent session lifecycle management.
 *
 * The manager provides methods for:
 * - Starting new agent sessions
 * - Resuming previous sessions with context preservation
 * - Stopping and suspending sessions
 * - Messaging running sessions
 * - Querying session history
 */
export interface SessionManager {
    /**
     * Starts a new session for an agent.
     *
     * @param agentId - The agent entity ID
     * @param options - Start options
     * @returns The session record and event emitter
     */
    startSession(agentId: EntityId, options?: StartSessionOptions): Promise<{
        session: SessionRecord;
        events: EventEmitter;
    }>;
    /**
     * Resumes a previous session using its provider session ID.
     *
     * When `checkReadyQueue` is true (default), implements the Universal Work Principle (UWP):
     * - Before continuing with the previous context, checks if any tasks were assigned during suspension
     * - If tasks are found, the session will be instructed to process them first
     *
     * @param agentId - The agent entity ID
     * @param options - Resume options including the provider session ID
     * @returns The resumed session record, event emitter, and UWP check result
     */
    resumeSession(agentId: EntityId, options: ResumeSessionOptions): Promise<{
        session: SessionRecord;
        events: EventEmitter;
        uwpCheck?: ResumeUWPCheckResult;
    }>;
    /**
     * Stops a running session.
     *
     * @param sessionId - The internal session ID
     * @param options - Stop options
     */
    stopSession(sessionId: string, options?: StopSessionOptions): Promise<void>;
    /**
     * Suspends a session (marks it for later resume).
     * The session can be resumed later using its provider session ID.
     *
     * @param sessionId - The internal session ID
     * @param reason - Optional reason for suspension
     */
    suspendSession(sessionId: string, reason?: string): Promise<void>;
    /**
     * Interrupts a running session (sends interrupt signal to stop current operation).
     * This is like pressing Escape in the Claude Code CLI - it stops the current
     * operation but keeps the session running.
     *
     * @param sessionId - The internal session ID
     */
    interruptSession(sessionId: string): Promise<void>;
    /**
     * Gets a session by internal ID.
     *
     * @param sessionId - The internal session ID
     * @returns The session record or undefined if not found
     */
    getSession(sessionId: string): SessionRecord | undefined;
    /**
     * Gets the active session for an agent.
     *
     * @param agentId - The agent entity ID
     * @returns The active session or undefined if none
     */
    getActiveSession(agentId: EntityId): SessionRecord | undefined;
    /**
     * Lists sessions matching the filter.
     *
     * @param filter - Filter options
     * @returns Array of matching session records
     */
    listSessions(filter?: SessionFilter): SessionRecord[];
    /**
     * Gets the most recent session for an agent that can be resumed.
     *
     * @param agentId - The agent entity ID
     * @returns The most recent resumable session or undefined
     */
    getMostRecentResumableSession(agentId: EntityId): SessionRecord | undefined;
    /**
     * Gets the session history for an agent from the database.
     *
     * @param agentId - The agent entity ID
     * @param limit - Maximum number of entries to return
     * @returns Array of session history entries
     */
    getSessionHistory(agentId: EntityId, limit?: number): Promise<SessionHistoryEntry[]>;
    /**
     * Gets the session history for a role across all agents.
     *
     * This aggregates session history from all agents with the specified role,
     * sorted by most recent first.
     *
     * @param role - The agent role to filter by
     * @param limit - Maximum number of entries to return (default 10)
     * @returns Array of role-based session history entries
     */
    getSessionHistoryByRole(role: AgentRole, limit?: number): Promise<RoleSessionHistoryEntry[]>;
    /**
     * Gets the most recent previous session for a role.
     *
     * Returns the most recently ended (suspended or terminated) session for
     * agents with the specified role. This is useful for predecessor queries
     * where a new agent needs to consult the previous session holder for context.
     *
     * @param role - The agent role to find previous session for
     * @returns The most recent previous session, or undefined if none found
     */
    getPreviousSession(role: AgentRole): Promise<RoleSessionHistoryEntry | undefined>;
    /**
     * Sends a message to a running session via its agent channel.
     *
     * For headless sessions, messages are queued for the agent to process.
     * For interactive sessions, messages appear in the agent's inbox.
     *
     * @param sessionId - The internal session ID
     * @param options - Message options
     * @returns Result of the send operation
     */
    messageSession(sessionId: string, options: MessageSessionOptions): Promise<MessageSessionResult>;
    /**
     * Gets the event emitter for a session.
     *
     * Events emitted:
     * - 'event' (SpawnedSessionEvent) - Parsed stream-json event
     * - 'status' (SessionStatus) - Session status change
     * - 'error' (Error) - Session error
     * - 'exit' (code: number | null, signal: string | null) - Process exit
     *
     * @param sessionId - The internal session ID
     * @returns The event emitter or undefined if session not found
     */
    getEventEmitter(sessionId: string): EventEmitter | undefined;
    /**
     * Records that the human user typed into a session's PTY.
     * Used for idle-based debouncing of message forwarding.
     *
     * @param sessionId - The internal session ID
     */
    recordUserInput(sessionId: string): void;
    /**
     * Gets how long (ms) the user has been idle in the agent's active session.
     * Returns undefined if the agent has no active session or no input has been recorded.
     *
     * @param agentId - The agent entity ID
     * @returns Milliseconds since last user input, or undefined
     */
    getSessionUserIdleMs(agentId: EntityId): number | undefined;
    /**
     * Persists the current session state to the database.
     * Called automatically on state changes but can be called manually.
     *
     * @param sessionId - The internal session ID
     */
    persistSession(sessionId: string): Promise<void>;
    /**
     * Loads session state from the database for an agent.
     * Used to recover sessions after restart.
     *
     * @param agentId - The agent entity ID
     */
    loadSessionState(agentId: EntityId): Promise<void>;
    /**
     * Reconciles in-memory state with database on startup.
     * Finds agents marked as 'running' whose processes are no longer alive
     * and resets them to 'idle'.
     *
     * @returns Summary of reconciliation results
     */
    reconcileOnStartup(): Promise<{
        reconciled: number;
        errors: string[];
    }>;
    /**
     * Sets the operation log service for persistent event logging.
     * Called after construction to avoid circular dependency issues.
     */
    setOperationLog(log: OperationLogService): void;
}
/**
 * Implementation of the Session Manager.
 */
export declare class SessionManagerImpl implements SessionManager {
    private readonly spawner;
    private readonly api;
    private readonly registry;
    private readonly settingsService?;
    private readonly sessions;
    private readonly agentSessions;
    private readonly sessionHistory;
    private readonly sessionCleanupFns;
    private operationLog;
    constructor(spawner: SpawnerService, api: QuarryAPI, // Used for message operations in messageSession
    registry: AgentRegistry, settingsService?: SettingsService | undefined);
    /**
     * Gets the API instance for direct operations
     */
    getApi(): QuarryAPI;
    /**
     * Sets the operation log service for persistent event logging.
     * Called after construction to avoid circular dependency issues.
     */
    setOperationLog(log: OperationLogService): void;
    startSession(agentId: EntityId, options?: StartSessionOptions): Promise<{
        session: SessionRecord;
        events: EventEmitter;
    }>;
    resumeSession(agentId: EntityId, options: ResumeSessionOptions): Promise<{
        session: SessionRecord;
        events: EventEmitter;
        uwpCheck?: ResumeUWPCheckResult;
    }>;
    stopSession(sessionId: string, options?: StopSessionOptions): Promise<void>;
    interruptSession(sessionId: string): Promise<void>;
    suspendSession(sessionId: string, reason?: string): Promise<void>;
    getSession(sessionId: string): SessionRecord | undefined;
    getActiveSession(agentId: EntityId): SessionRecord | undefined;
    listSessions(filter?: SessionFilter): SessionRecord[];
    getMostRecentResumableSession(agentId: EntityId): SessionRecord | undefined;
    getSessionHistory(agentId: EntityId, limit?: number): Promise<SessionHistoryEntry[]>;
    getSessionHistoryByRole(role: AgentRole, limit?: number): Promise<RoleSessionHistoryEntry[]>;
    getPreviousSession(role: AgentRole): Promise<RoleSessionHistoryEntry | undefined>;
    messageSession(sessionId: string, options: MessageSessionOptions): Promise<MessageSessionResult>;
    getEventEmitter(sessionId: string): EventEmitter | undefined;
    recordUserInput(sessionId: string): void;
    getSessionUserIdleMs(agentId: EntityId): number | undefined;
    persistSession(sessionId: string): Promise<void>;
    loadSessionState(agentId: EntityId): Promise<void>;
    reconcileOnStartup(): Promise<{
        reconciled: number;
        errors: string[];
    }>;
    /**
     * Resolves the executable path for a provider using the priority chain:
     * 1. Agent-specific executablePath from metadata
     * 2. Workspace-wide defaultExecutablePaths[providerName] from settings service
     * 3. undefined (provider will use its built-in default)
     */
    private resolveExecutablePath;
    /**
     * Creates a fresh provider instance with a custom executable path.
     * Returns undefined if no custom path is needed (use registry singleton instead).
     */
    private createProviderWithPath;
    /**
     * Resolves the provider for a session, creating a custom instance if a non-default
     * executable path is configured (agent-specific or workspace-wide).
     * Falls back to the registry singleton when no custom path is needed.
     */
    private resolveProvider;
    private scheduleTerminatedSessionCleanup;
    /**
     * Checks whether a process with the given PID is still alive.
     * Uses signal 0 which doesn't kill — just checks existence.
     */
    private isProcessAlive;
    /**
     * Cleans up a session whose process is no longer alive.
     * Transitions it to 'terminated' and schedules memory cleanup.
     */
    private cleanupDeadSession;
    private setupSpawnerEventHandlers;
    private setupSessionEventForwarding;
    /**
     * Cleans up event listeners for a session.
     * Called when a session exits to prevent listener leaks.
     */
    private cleanupSessionEventListeners;
    private createSessionState;
    private toPublicSession;
    private addToHistory;
    private getPersistedHistory;
    private mergeHistory;
    private getTimestampMs;
    /**
     * Builds the UWP task prompt to instruct the agent to process the assigned task first.
     * This ensures compliance with the Universal Work Principle: "If there is work on your anchor, YOU MUST RUN IT"
     */
    private buildUWPTaskPrompt;
}
/**
 * Creates a SessionManager instance
 */
export declare function createSessionManager(spawner: SpawnerService, api: QuarryAPI, registry: AgentRegistry, settingsService?: SettingsService): SessionManager;
//# sourceMappingURL=session-manager.d.ts.map