/** * HarborForge Calendar Scheduler * * PLG-CAL-002: Plugin-side handling for pending slot execution. * PLG-CAL-004: ScheduledGatewayRestart event handling with state persistence. * * Responsibilities: * - Run calendar heartbeat every minute * - Detect when agent is Idle and slots are pending * - Wake agent with task context * - Handle slot status transitions (attended, ongoing, deferred) * - Manage agent status transitions (idle → busy/on_call) * - Persist state on ScheduledGatewayRestart and restore on startup * - Send final heartbeat before graceful shutdown * * Design reference: NEXT_WAVE_DEV_DIRECTION.md §6 (Agent wakeup mechanism) */ import { CalendarBridgeClient } from './calendar-bridge'; import { CalendarSlotResponse, AgentStatusValue } from './types'; export interface CalendarSchedulerConfig { /** Calendar bridge client for backend communication */ bridge: CalendarBridgeClient; /** Function to get current agent status from backend */ getAgentStatus: () => Promise; /** Function to wake/spawn agent with task context */ wakeAgent: (context: AgentWakeContext) => Promise; /** Logger instance */ logger: { info: (...args: any[]) => void; error: (...args: any[]) => void; debug: (...args: any[]) => void; warn: (...args: any[]) => void; }; /** Heartbeat interval in milliseconds (default: 60000) */ heartbeatIntervalMs?: number; /** Enable verbose debug logging */ debug?: boolean; /** Directory for state persistence (default: plugin data dir) */ stateDir?: string; } /** * Context passed to agent when waking for slot execution. * This is the payload the agent receives to understand what to do. */ export interface AgentWakeContext { /** The slot to execute */ slot: CalendarSlotResponse; /** Human-readable task description */ taskDescription: string; /** Prompt/instructions for the agent */ prompt: string; /** Whether this is a virtual slot (needs materialization) */ isVirtual: boolean; } /** * Current execution state tracked by the scheduler. */ interface SchedulerState { /** Whether scheduler is currently running */ isRunning: boolean; /** Currently executing slot (null if idle) */ currentSlot: CalendarSlotResponse | null; /** Last heartbeat timestamp */ lastHeartbeatAt: Date | null; /** Interval handle for cleanup */ intervalHandle: ReturnType | null; /** Set of slot IDs that have been deferred in current session */ deferredSlotIds: Set; /** Whether agent is currently processing a slot */ isProcessing: boolean; /** Whether a gateway restart is scheduled/pending */ isRestartPending: boolean; } /** * CalendarScheduler manages the periodic heartbeat and slot execution lifecycle. */ export declare class CalendarScheduler { private config; private state; private stateFilePath; constructor(config: CalendarSchedulerConfig); /** * Get default state directory (plugin data directory or temp fallback). */ private getDefaultStateDir; /** * Persist current state to disk for recovery after restart. */ private persistState; /** * Restore state from disk if available. */ private restoreState; /** * Clear persisted state file after successful restore. */ private clearPersistedState; /** * Send a final heartbeat to the backend before shutdown. */ private sendFinalHeartbeat; /** * Handle ScheduledGatewayRestart event. * PLG-CAL-004: Persist state, send final heartbeat, pause scheduled tasks. */ private handleScheduledGatewayRestart; /** * Start the calendar scheduler. * Begins periodic heartbeat to check for pending slots. */ start(): void; /** * Stop the calendar scheduler. * Cleans up intervals and resets state. */ stop(): void; /** * Execute a single heartbeat cycle. * Fetches pending slots and handles execution logic. */ runHeartbeat(): Promise; /** * Handle slots when agent is not idle. * Defer all pending slots with priority boost. */ private handleNonIdleAgent; /** * Handle slots when agent is idle. * Select highest priority slot and wake agent. */ private handleIdleAgent; /** * Check if a slot is a ScheduledGatewayRestart system event. */ private isScheduledGatewayRestart; /** * Execute a slot by waking the agent. */ private executeSlot; /** * Build the wake context for an agent based on slot details. */ private buildWakeContext; /** * Build prompt for job-type slots. */ private buildJobPrompt; /** * Build prompt for system event slots. */ private buildSystemPrompt; /** * Build prompt for entertainment slots. */ private buildEntertainmentPrompt; /** * Build generic prompt for slots without specific event data. */ private buildGenericPrompt; /** * Mark a slot as deferred with priority boost. */ private deferSlot; /** * Revert a slot to not_started status after failed execution attempt. */ private revertSlot; /** * Complete the current slot execution. * Call this when the agent finishes the task. */ completeCurrentSlot(actualDurationMinutes: number): Promise; /** * Abort the current slot execution. * Call this when the agent cannot complete the task. */ abortCurrentSlot(reason?: string): Promise; /** * Pause the current slot execution. * Call this when the agent needs to temporarily pause. */ pauseCurrentSlot(): Promise; /** * Resume a paused slot. */ resumeCurrentSlot(): Promise; /** * Trigger an immediate replanning pass after the current slot lifecycle ends. * This lets previously deferred/not-started slots compete again as soon as * the agent becomes idle. */ private triggerReplan; /** * Get a stable ID for a slot (real or virtual). */ private getSlotId; /** * Format a Date as ISO time string (HH:MM:SS). */ private formatTime; /** * Debug logging helper. */ private logDebug; /** * Get current scheduler state (for introspection). */ getState(): Readonly; /** * Check if scheduler is running. */ isRunning(): boolean; /** * Check if currently processing a slot. */ isProcessing(): boolean; /** * Get the current slot being executed (if any). */ getCurrentSlot(): CalendarSlotResponse | null; /** * Check if a gateway restart is pending. */ isRestartPending(): boolean; /** * Get the path to the state file. */ getStateFilePath(): string; } /** * Factory function to create a CalendarScheduler from plugin context. */ export declare function createCalendarScheduler(config: CalendarSchedulerConfig): CalendarScheduler; export {}; //# sourceMappingURL=scheduler.d.ts.map