feat: add business hours scheduling to heartbeat

- Configurable start/end hours (0-23)
- Timezone support (default: UTC)
- Silent skip outside business hours (no log spam)
- Backward compatible (no schedule = always run)
This commit is contained in:
2026-02-16 11:27:27 +00:00
parent 9a77b433c9
commit b26ed1eb53

View File

@@ -31,6 +31,13 @@ export type HeartbeatConfig = {
enabled: boolean; enabled: boolean;
intervalSeconds: number; intervalSeconds: number;
maxPickupsPerTick: number; maxPickupsPerTick: number;
schedule?: {
enabled_hours?: {
start: number; // 0-23
end: number; // 0-23
timezone?: string; // IANA timezone (default: UTC)
};
};
}; };
type Agent = { type Agent = {
@@ -165,6 +172,26 @@ function hasProjects(workspace: string): boolean {
); );
} }
/**
* Check if current time is within enabled hours.
*/
function isWithinBusinessHours(config: HeartbeatConfig): boolean {
if (!config.schedule?.enabled_hours) return true;
const { start, end, timezone = "UTC" } = config.schedule.enabled_hours;
// Get current hour in the configured timezone
const now = new Date();
const formatter = new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
hour12: false,
});
const currentHour = parseInt(formatter.format(now), 10);
return currentHour >= start && currentHour < end;
}
/** /**
* Run one heartbeat tick for all agents. * Run one heartbeat tick for all agents.
*/ */
@@ -174,6 +201,11 @@ async function runHeartbeatTick(
pluginConfig: Record<string, unknown> | undefined, pluginConfig: Record<string, unknown> | undefined,
logger: ServiceContext["logger"], logger: ServiceContext["logger"],
): Promise<void> { ): Promise<void> {
// Skip tick if outside business hours
if (!isWithinBusinessHours(config)) {
return; // Silent skip - no logging spam
}
try { try {
const result = await processAllAgents(agents, config, pluginConfig, logger); const result = await processAllAgents(agents, config, pluginConfig, logger);
logTickResult(result, logger); logTickResult(result, logger);