import * as Schema from "effect/Schema"; import { CommandId, IsoDateTime, ProjectId, ScheduledTaskId, ThreadId, TrimmedNonEmptyString, } from "./baseSchemas.ts"; import { ModelSelection } from "./modelSelection.ts"; import { OrchestrationV2Actor, OrchestrationV2CreationSource, OrchestrationV2ThreadLaunchWorkspaceStrategy, } from "./orchestrationV2.ts"; import { ProviderInteractionMode, RuntimeMode } from "./providerPolicy.ts"; /** 24-hour "HH:MM" wall-clock time. Mirrors `parseTimeOfDay` on the server. */ const TimeOfDay = TrimmedNonEmptyString.check( Schema.isPattern(/^([01]?\d|2[0-3]):([0-5]\d)$/), ).annotate({ description: "Local wall-clock time in 24-hour HH:MM form, such as 09:30." }); export const MIN_SCHEDULED_TASK_INTERVAL_MS = 60_000; const ScheduledTaskIntervalMs = Schema.Int.check(Schema.isGreaterThan(0)).annotate({ description: "Positive interval in milliseconds.", }); const ScheduledTaskIntervalSchedule = Schema.Struct({ type: Schema.Literal("interval").annotate({ description: "Select interval scheduling.", }), everyMs: ScheduledTaskIntervalMs, }).annotate({ description: "Run repeatedly after a fixed number of milliseconds.", }); const ScheduledTaskFixedTimeSchedule = Schema.Struct({ type: Schema.Literal("fixed_time").annotate({ description: "Select a fixed local wall-clock time.", }), timeOfDay: TimeOfDay, weekdays: Schema.optional( Schema.Array( Schema.Int.check(Schema.isBetween({ minimum: 0, maximum: 6 })).annotate({ description: "Weekday number where 0 is Sunday and 6 is Saturday.", }), ).annotate({ description: "Optional weekdays; omit to run every day.", }), ), }).annotate({ description: "Run at a fixed local wall-clock time on selected weekdays.", }); /** * Read model for persisted schedules. Keep accepting legacy sub-minute rows so * users can list, disable, edit, or delete them after the write minimum changes. */ export const ScheduledTaskSchedule = Schema.Union([ ScheduledTaskIntervalSchedule, ScheduledTaskFixedTimeSchedule, ]).annotate({ description: "Structured recurring schedule. Pass an object with type 'interval' or 'fixed_time'.", }); export type ScheduledTaskSchedule = typeof ScheduledTaskSchedule.Type; /** Mutation model: newly created or updated interval schedules run at most once per minute. */ export const ScheduledTaskUpsertSchedule = Schema.Union([ Schema.Struct({ type: Schema.Literal("interval").annotate({ description: "Select interval scheduling.", }), everyMs: ScheduledTaskIntervalMs.check( Schema.isGreaterThanOrEqualTo(MIN_SCHEDULED_TASK_INTERVAL_MS), ).annotate({ description: "Interval in milliseconds, with a minimum of 60000 (one minute).", }), }).annotate({ description: "Run repeatedly after a fixed number of milliseconds.", }), ScheduledTaskFixedTimeSchedule, ]).annotate({ description: "Writable recurring schedule. Pass an object with type 'interval' or 'fixed_time'.", }); export type ScheduledTaskUpsertSchedule = typeof ScheduledTaskUpsertSchedule.Type; export const ScheduledTaskRunStatus = Schema.Literals(["never", "running", "succeeded", "failed"]); export type ScheduledTaskRunStatus = typeof ScheduledTaskRunStatus.Type; export const ScheduledTask = Schema.Struct({ id: ScheduledTaskId, title: TrimmedNonEmptyString, prompt: TrimmedNonEmptyString, enabled: Schema.Boolean, schedule: ScheduledTaskSchedule, projectId: ProjectId, threadId: Schema.NullOr(ThreadId), workspaceStrategy: OrchestrationV2ThreadLaunchWorkspaceStrategy, modelSelection: ModelSelection, runtimeMode: RuntimeMode, interactionMode: ProviderInteractionMode, createdBy: OrchestrationV2Actor, creationSource: OrchestrationV2CreationSource, createdAt: IsoDateTime, updatedAt: IsoDateTime, nextRunAt: Schema.NullOr(IsoDateTime), lastRunAt: Schema.NullOr(IsoDateTime), lastRunStatus: ScheduledTaskRunStatus, lastRunError: Schema.NullOr(Schema.String), runCount: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)), }); export type ScheduledTask = typeof ScheduledTask.Type; export const ScheduledTaskListInput = Schema.Struct({}); export type ScheduledTaskListInput = typeof ScheduledTaskListInput.Type; export const ScheduledTaskListResult = Schema.Struct({ tasks: Schema.Array(ScheduledTask), }); export type ScheduledTaskListResult = typeof ScheduledTaskListResult.Type; export const ScheduledTaskUpsertInput = Schema.Struct({ id: Schema.optional(ScheduledTaskId), commandId: Schema.optional(CommandId), title: TrimmedNonEmptyString, prompt: TrimmedNonEmptyString, enabled: Schema.Boolean, schedule: ScheduledTaskUpsertSchedule, projectId: ProjectId, threadId: Schema.optional(Schema.NullOr(ThreadId)), workspaceStrategy: OrchestrationV2ThreadLaunchWorkspaceStrategy, modelSelection: ModelSelection, runtimeMode: RuntimeMode, interactionMode: ProviderInteractionMode, createdBy: Schema.optional(OrchestrationV2Actor), creationSource: Schema.optional(OrchestrationV2CreationSource), }); export type ScheduledTaskUpsertInput = typeof ScheduledTaskUpsertInput.Type; /** Partial update that flips only the enabled flag — never overwrites other fields. */ export const ScheduledTaskSetEnabledInput = Schema.Struct({ id: ScheduledTaskId, enabled: Schema.Boolean, }); export type ScheduledTaskSetEnabledInput = typeof ScheduledTaskSetEnabledInput.Type; export const ScheduledTaskDeleteInput = Schema.Struct({ id: ScheduledTaskId, }); export type ScheduledTaskDeleteInput = typeof ScheduledTaskDeleteInput.Type; export const ScheduledTaskRunNowInput = Schema.Struct({ id: ScheduledTaskId, }); export type ScheduledTaskRunNowInput = typeof ScheduledTaskRunNowInput.Type; export const ScheduledTaskMutationResult = Schema.Struct({ task: ScheduledTask, }); export type ScheduledTaskMutationResult = typeof ScheduledTaskMutationResult.Type; export const ScheduledTaskDeleteResult = Schema.Struct({ id: ScheduledTaskId, }); export type ScheduledTaskDeleteResult = typeof ScheduledTaskDeleteResult.Type; export const ScheduledTaskRunNowResult = Schema.Struct({ task: ScheduledTask, }); export type ScheduledTaskRunNowResult = typeof ScheduledTaskRunNowResult.Type; export class ScheduledTaskError extends Schema.TaggedErrorClass()( "ScheduledTaskError", { message: Schema.String, taskId: Schema.optional(ScheduledTaskId), cause: Schema.optional(Schema.Defect()), }, ) {}