Compare commits
1 Commits
zhi-2026-0
...
ec09578de3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec09578de3 |
@@ -19,6 +19,7 @@
|
||||
import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'fs';
|
||||
import { join, dirname } from 'path';
|
||||
import { CalendarBridgeClient } from './calendar-bridge';
|
||||
import { ScheduleCache } from './schedule-cache';
|
||||
import {
|
||||
CalendarSlotResponse,
|
||||
SlotStatus,
|
||||
@@ -44,6 +45,8 @@ export interface CalendarSchedulerConfig {
|
||||
};
|
||||
/** Heartbeat interval in milliseconds (default: 60000) */
|
||||
heartbeatIntervalMs?: number;
|
||||
/** Schedule sync interval in milliseconds (default: 300000 = 5 min) */
|
||||
syncIntervalMs?: number;
|
||||
/** Enable verbose debug logging */
|
||||
debug?: boolean;
|
||||
/** Directory for state persistence (default: plugin data dir) */
|
||||
@@ -95,8 +98,10 @@ interface SchedulerState {
|
||||
currentSlot: CalendarSlotResponse | null;
|
||||
/** Last heartbeat timestamp */
|
||||
lastHeartbeatAt: Date | null;
|
||||
/** Interval handle for cleanup */
|
||||
/** Heartbeat interval handle */
|
||||
intervalHandle: ReturnType<typeof setInterval> | null;
|
||||
/** Schedule sync interval handle */
|
||||
syncIntervalHandle: 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 */
|
||||
@@ -117,10 +122,13 @@ export class CalendarScheduler {
|
||||
private config: Required<CalendarSchedulerConfig>;
|
||||
private state: SchedulerState;
|
||||
private stateFilePath: string;
|
||||
/** Local cache of today's full schedule, synced periodically from backend */
|
||||
private scheduleCache: ScheduleCache = new ScheduleCache();
|
||||
|
||||
constructor(config: CalendarSchedulerConfig) {
|
||||
this.config = {
|
||||
heartbeatIntervalMs: 60000, // 1 minute default
|
||||
syncIntervalMs: 300_000, // 5 minutes default
|
||||
debug: false,
|
||||
stateDir: this.getDefaultStateDir(),
|
||||
...config,
|
||||
@@ -133,6 +141,7 @@ export class CalendarScheduler {
|
||||
currentSlot: null,
|
||||
lastHeartbeatAt: null,
|
||||
intervalHandle: null,
|
||||
syncIntervalHandle: null,
|
||||
deferredSlotIds: new Set(),
|
||||
isProcessing: false,
|
||||
isRestartPending: false,
|
||||
@@ -327,14 +336,21 @@ export class CalendarScheduler {
|
||||
this.state.isRestartPending = false;
|
||||
this.config.logger.info('Calendar scheduler started');
|
||||
|
||||
// Run initial heartbeat immediately
|
||||
// Run initial sync + heartbeat immediately
|
||||
this.runSync();
|
||||
this.runHeartbeat();
|
||||
|
||||
// Schedule periodic heartbeats
|
||||
// Schedule periodic heartbeats (slot execution checks)
|
||||
this.state.intervalHandle = setInterval(
|
||||
() => this.runHeartbeat(),
|
||||
this.config.heartbeatIntervalMs
|
||||
);
|
||||
|
||||
// Schedule periodic schedule sync (full day schedule refresh)
|
||||
this.state.syncIntervalHandle = setInterval(
|
||||
() => this.runSync(),
|
||||
this.config.syncIntervalMs
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,10 +364,41 @@ export class CalendarScheduler {
|
||||
clearInterval(this.state.intervalHandle);
|
||||
this.state.intervalHandle = null;
|
||||
}
|
||||
if (this.state.syncIntervalHandle) {
|
||||
clearInterval(this.state.syncIntervalHandle);
|
||||
this.state.syncIntervalHandle = null;
|
||||
}
|
||||
|
||||
this.config.logger.info('Calendar scheduler stopped');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync today's full schedule from backend into local cache.
|
||||
* Runs every syncIntervalMs (default: 5 min).
|
||||
* Catches new slots assigned by other agents or the manager.
|
||||
*/
|
||||
async runSync(): Promise<void> {
|
||||
if (!this.state.isRunning || this.state.isRestartPending) return;
|
||||
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
try {
|
||||
const slots = await this.config.bridge.getDaySchedule(today);
|
||||
if (slots) {
|
||||
this.scheduleCache.sync(today, slots);
|
||||
this.logDebug(`Schedule synced: ${slots.length} slots for ${today}`);
|
||||
}
|
||||
} catch (err) {
|
||||
this.config.logger.warn(`Schedule sync failed: ${String(err)}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the local schedule cache (for status reporting / tools).
|
||||
*/
|
||||
getScheduleCache(): ScheduleCache {
|
||||
return this.scheduleCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a single heartbeat cycle.
|
||||
* Fetches pending slots and handles execution logic.
|
||||
@@ -611,13 +658,11 @@ Task Code: ${code}
|
||||
Estimated Duration: ${duration} minutes
|
||||
Slot Type: ${slot.slot_type}
|
||||
Priority: ${slot.priority}
|
||||
Working Sessions: ${jobData.working_sessions?.join(', ') || 'none recorded'}
|
||||
|
||||
Please focus on this task for the allocated time. When you finish or need to pause,
|
||||
report your progress back to the calendar system.
|
||||
|
||||
Working sessions: ${jobData.working_sessions?.join(', ') || 'none recorded'}
|
||||
|
||||
Start working on ${code} now.`;
|
||||
Follow the daily-routine skill's task-handson workflow to execute this task.
|
||||
Use harborforge_calendar_complete when finished, or harborforge_calendar_pause to pause.
|
||||
Before going idle, check for overdue slots as described in the slot-complete workflow.`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -630,19 +675,15 @@ Start working on ${code} now.`;
|
||||
switch (sysData.event) {
|
||||
case 'ScheduleToday':
|
||||
return `System Event: Schedule Today
|
||||
|
||||
Please review today's calendar and schedule any pending tasks or planning activities.
|
||||
Estimated time: ${slot.estimated_duration} minutes.
|
||||
|
||||
Check your calendar and plan the day's work.`;
|
||||
Follow the daily-routine skill's plan-schedule workflow to plan today's work.`;
|
||||
|
||||
case 'SummaryToday':
|
||||
return `System Event: Daily Summary
|
||||
|
||||
Please provide a summary of today's activities and progress.
|
||||
Estimated time: ${slot.estimated_duration} minutes.
|
||||
|
||||
Review what was accomplished and prepare end-of-day notes.`;
|
||||
Review today's completed, deferred, and abandoned slots. Write a summary to your daily note (memory/YYYY-MM-DD.md).`;
|
||||
|
||||
case 'ScheduledGatewayRestart':
|
||||
return `System Event: Scheduled Gateway Restart
|
||||
|
||||
Reference in New Issue
Block a user