import { z } from "zod"; import { ISSUE_PRIORITIES, ROUTINE_CATCH_UP_POLICIES, ROUTINE_CONCURRENCY_POLICIES, ROUTINE_STATUSES, ROUTINE_TRIGGER_SIGNING_MODES, ROUTINE_VARIABLE_TYPES, } from "../constants.js"; import { ISSUE_EXECUTION_WORKSPACE_PREFERENCES, issueExecutionWorkspaceSettingsSchema, } from "./issue.js"; const routineVariableValueSchema = z.union([z.string(), z.number().finite(), z.boolean()]); export const routineVariableSchema = z.object({ name: z.string().trim().regex(/^[A-Za-z][A-Za-z0-9_]*$/), label: z.string().trim().max(120).optional().nullable(), type: z.enum(ROUTINE_VARIABLE_TYPES).optional().default("text"), defaultValue: routineVariableValueSchema.optional().nullable(), required: z.boolean().optional().default(true), options: z.array(z.string().trim().min(1).max(120)).max(50).optional().default([]), }).superRefine((value, ctx) => { if (value.type === "select" && value.options.length === 0) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["options"], message: "Select variables require at least one option", }); } if (value.type !== "select" && value.options.length > 0) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["options"], message: "Only select variables can define options", }); } if (value.type === "select" && value.defaultValue != null) { if (typeof value.defaultValue !== "string" || !value.options.includes(value.defaultValue)) { ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["defaultValue"], message: "Select variable defaults must match one of the allowed options", }); } } }); export const createRoutineSchema = z.object({ projectId: z.string().uuid(), goalId: z.string().uuid().optional().nullable(), parentIssueId: z.string().uuid().optional().nullable(), title: z.string().trim().min(1).max(200), description: z.string().optional().nullable(), assigneeAgentId: z.string().uuid(), priority: z.enum(ISSUE_PRIORITIES).optional().default("medium"), status: z.enum(ROUTINE_STATUSES).optional().default("active"), concurrencyPolicy: z.enum(ROUTINE_CONCURRENCY_POLICIES).optional().default("coalesce_if_active"), catchUpPolicy: z.enum(ROUTINE_CATCH_UP_POLICIES).optional().default("skip_missed"), variables: z.array(routineVariableSchema).optional().default([]), }); export type CreateRoutine = z.infer; export const updateRoutineSchema = createRoutineSchema.partial(); export type UpdateRoutine = z.infer; const baseTriggerSchema = z.object({ label: z.string().trim().max(120).optional().nullable(), enabled: z.boolean().optional().default(true), }); export const createRoutineTriggerSchema = z.discriminatedUnion("kind", [ baseTriggerSchema.extend({ kind: z.literal("schedule"), cronExpression: z.string().trim().min(1), timezone: z.string().trim().min(1).default("UTC"), }), baseTriggerSchema.extend({ kind: z.literal("webhook"), signingMode: z.enum(ROUTINE_TRIGGER_SIGNING_MODES).optional().default("bearer"), replayWindowSec: z.number().int().min(30).max(86_400).optional().default(300), }), baseTriggerSchema.extend({ kind: z.literal("api"), }), ]); export type CreateRoutineTrigger = z.infer; export const updateRoutineTriggerSchema = z.object({ label: z.string().trim().max(120).optional().nullable(), enabled: z.boolean().optional(), cronExpression: z.string().trim().min(1).optional().nullable(), timezone: z.string().trim().min(1).optional().nullable(), signingMode: z.enum(ROUTINE_TRIGGER_SIGNING_MODES).optional().nullable(), replayWindowSec: z.number().int().min(30).max(86_400).optional().nullable(), }); export type UpdateRoutineTrigger = z.infer; export const runRoutineSchema = z.object({ triggerId: z.string().uuid().optional().nullable(), payload: z.record(z.unknown()).optional().nullable(), variables: z.record(routineVariableValueSchema).optional().nullable(), idempotencyKey: z.string().trim().max(255).optional().nullable(), source: z.enum(["manual", "api"]).optional().default("manual"), executionWorkspaceId: z.string().uuid().optional().nullable(), executionWorkspacePreference: z.enum(ISSUE_EXECUTION_WORKSPACE_PREFERENCES).optional().nullable(), executionWorkspaceSettings: issueExecutionWorkspaceSettingsSchema.optional().nullable(), }); export type RunRoutine = z.infer; export const rotateRoutineTriggerSecretSchema = z.object({}); export type RotateRoutineTriggerSecret = z.infer;