Files
HarborForge.OpenclawPlugin/plugin/calendar/scheduler.d.ts
zhi 3b0ea0ad12 PLG-CAL-004: Implement ScheduledGatewayRestart handling in plugin
- Add state persistence (persistState/restoreState) for recovery after restart
- Add handleScheduledGatewayRestart method that:
  - Persists current scheduler state to disk
  - Sends final heartbeat to backend before shutdown
  - Stops the calendar scheduler (pauses scheduled tasks)
- Add isRestartPending flag to prevent new slot processing during restart
- Add isScheduledGatewayRestart helper to detect restart events
- Update scheduler to detect and handle ScheduledGatewayRestart events
- Add new tools: harborforge_restart_status, harborforge_calendar_pause/resume
- Export isRestartPending and getStateFilePath methods
- Bump plugin version to 0.3.1
2026-04-01 09:41:02 +00:00

235 lines
7.1 KiB
TypeScript

/**
* 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<AgentStatusValue | null>;
/** Function to wake/spawn agent with task context */
wakeAgent: (context: AgentWakeContext) => Promise<boolean>;
/** 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<typeof setInterval> | null;
/** Set of slot IDs that have been deferred in current session */
deferredSlotIds: Set<string>;
/** 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<void>;
/**
* 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<void>;
/**
* Abort the current slot execution.
* Call this when the agent cannot complete the task.
*/
abortCurrentSlot(reason?: string): Promise<void>;
/**
* Pause the current slot execution.
* Call this when the agent needs to temporarily pause.
*/
pauseCurrentSlot(): Promise<void>;
/**
* Resume a paused slot.
*/
resumeCurrentSlot(): Promise<void>;
/**
* 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<SchedulerState>;
/**
* 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