feat: add heartbeat active hours

This commit is contained in:
Peter Steinberger
2026-01-21 20:30:29 +00:00
parent 717fb9e413
commit 31943dcecb
6 changed files with 166 additions and 1 deletions

View File

@@ -162,6 +162,15 @@ export type AgentDefaultsConfig = {
heartbeat?: {
/** Heartbeat interval (duration string, default unit: minutes; default: 30m). */
every?: string;
/** Optional active-hours window (local time); heartbeats run only inside this window. */
activeHours?: {
/** Start time (24h, HH:MM). Inclusive. */
start?: string;
/** End time (24h, HH:MM). Exclusive. Use "24:00" for end-of-day. */
end?: string;
/** Timezone for the window ("user", "local", or IANA TZ id). Default: "user". */
timezone?: string;
};
/** Heartbeat model override (provider/model). */
model?: string;
/** Delivery target (last|whatsapp|telegram|discord|slack|msteams|signal|imessage|none). */

View File

@@ -11,6 +11,14 @@ import {
export const HeartbeatSchema = z
.object({
every: z.string().optional(),
activeHours: z
.object({
start: z.string().optional(),
end: z.string().optional(),
timezone: z.string().optional(),
})
.strict()
.optional(),
model: z.string().optional(),
includeReasoning: z.boolean().optional(),
target: z
@@ -42,6 +50,42 @@ export const HeartbeatSchema = z
message: "invalid duration (use ms, s, m, h)",
});
}
const active = val.activeHours;
if (!active) return;
const timePattern = /^([01]\d|2[0-3]|24):([0-5]\d)$/;
const validateTime = (raw: string | undefined, opts: { allow24: boolean }, path: string) => {
if (!raw) return;
if (!timePattern.test(raw)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["activeHours", path],
message: 'invalid time (use "HH:MM" 24h format)',
});
return;
}
const [hourStr, minuteStr] = raw.split(":");
const hour = Number(hourStr);
const minute = Number(minuteStr);
if (hour === 24 && minute !== 0) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["activeHours", path],
message: "invalid time (24:00 is the only allowed 24:xx value)",
});
return;
}
if (hour === 24 && !opts.allow24) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ["activeHours", path],
message: "invalid time (start cannot be 24:00)",
});
}
};
validateTime(active.start, { allow24: false }, "start");
validateTime(active.end, { allow24: true }, "end");
})
.optional();