Scheduled and loop agents get workspaces; schedules UI speaks the user's timezone (#1909)
* fix(app): schedules UI speaks the user's timezone * refactor(server): require explicit agent placement and bind create-agent command * fix(server): scheduled agents get a workspace and show in the sidebar * fix(server): loop agents get a workspace and show in the sidebar * fix(server): preserve scheduled and loop run semantics through the create command * fix(server): serialize all schedule record writes through the per-schedule queue * fix(server): tighten create-agent dependency contracts * fix(server): handle schedule restamps and loop cancellations * fix(server): preserve scheduled and loop create semantics * refactor(server): schedule store owns atomic mutations * fix(server): keep schedule stamp reuse deterministic * fix(server): handle loop workspace stamp rejection * refactor(server): schedule store owns typed identity upsert * fix(server): keep schedule workspace stamps server-owned * fix(server): preserve scheduled run semantics * refactor: required nullable workspaceId replaces placement union; pure cadence policy * fix(server): tighten scheduled and loop agent edges * fix(server): fail canceled scheduled runs * fix(server): close schedule and loop cwd edges * fix(server): refresh schedule workspace stamps * fix(server): preserve schedule timezone on replace * fix(server): close loop stop and cadence reset edges
This commit is contained in:
@@ -2,6 +2,7 @@ import type { ScheduleCadence, ScheduleSummary } from "@getpaseo/protocol/schedu
|
||||
import { validateCronExpression } from "@getpaseo/protocol/schedule/cron-expression";
|
||||
|
||||
export type IntervalUnit = "minutes" | "hours" | "days";
|
||||
type CronCadence = Extract<ScheduleCadence, { type: "cron" }>;
|
||||
|
||||
const MS_PER_MINUTE = 60_000;
|
||||
const MS_PER_HOUR = MS_PER_MINUTE * 60;
|
||||
@@ -82,7 +83,7 @@ export function formatCadence(cadence: ScheduleCadence): string {
|
||||
if (cadence.type === "every") {
|
||||
return formatEvery(cadence.everyMs);
|
||||
}
|
||||
return describeCron(cadence.expression) ?? cadence.expression;
|
||||
return describeCron(cadence) ?? cadence.expression;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,8 +91,8 @@ export function formatCadence(cadence: ScheduleCadence): string {
|
||||
* expression is valid but not one of the recognized patterns (callers fall
|
||||
* back to showing the raw expression).
|
||||
*/
|
||||
export function describeCron(expr: string): string | null {
|
||||
const trimmed = expr.trim();
|
||||
export function describeCron(cadence: CronCadence): string | null {
|
||||
const trimmed = cadence.expression.trim();
|
||||
if (validateCron(trimmed) !== null) {
|
||||
return null;
|
||||
}
|
||||
@@ -121,20 +122,21 @@ export function describeCron(expr: string): string | null {
|
||||
return null;
|
||||
}
|
||||
const time = `${pad2(Number.parseInt(hour, 10))}:${pad2(minuteNum)}`;
|
||||
const timezone = cadence.timezone ?? "UTC";
|
||||
|
||||
if (dayOfWeek === "*") {
|
||||
return `Daily at ${time} UTC`;
|
||||
return `Daily at ${time} ${timezone}`;
|
||||
}
|
||||
if (dayOfWeek === "1-5") {
|
||||
return `Weekdays at ${time} UTC`;
|
||||
return `Weekdays at ${time} ${timezone}`;
|
||||
}
|
||||
if (dayOfWeek === "0,6" || dayOfWeek === "6,0") {
|
||||
return `Weekends at ${time} UTC`;
|
||||
return `Weekends at ${time} ${timezone}`;
|
||||
}
|
||||
if (/^\d$/.test(dayOfWeek)) {
|
||||
const day = DAY_NAMES[Number.parseInt(dayOfWeek, 10)];
|
||||
if (day) {
|
||||
return `${day}s at ${time} UTC`;
|
||||
return `${day}s at ${time} ${timezone}`;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user