import { z } from "zod/v4"; import { toolEffectSchema } from "./effect"; /** * Background jobs: durable work that outlives the request that started it. * * A job is enqueued from any app context (`coline.jobs.enqueue`) and runs * on Coline's job workers with the principal that enqueued it — the * invoking user, or the app itself for schedules, events, and endpoints. * Attempts retry with exponential backoff; work longer than one attempt's * timeout continues by checkpointing state or returning `continueWith`. * The declared `effect` is the job's runtime capability ceiling, exactly * like a tool's. */ export const jobNameSchema = z .string() .trim() .regex(/^[a-z][a-z0-9_-]*$/, "Job names are lowercase identifiers.") .max(80); export const MAX_JOB_TIMEOUT_SECONDS = 900; export const appJobDefinitionSchema = z.strictObject({ name: jobNameSchema, description: z.string().trim().max(500).optional(), effect: toolEffectSchema.default("write"), /** Wall-clock budget for one attempt. */ timeoutSeconds: z.number().int().min(1).max(MAX_JOB_TIMEOUT_SECONDS).default(300), /** Attempts before the run is dead-lettered. */ maxAttempts: z.number().int().min(1).max(20).default(5), /** Concurrent runs of this job per install. */ concurrency: z.number().int().min(1).max(20).default(4), }); export type AppJobDefinition = z.infer; export type AppJobDefinitionInput = z.input; export const appJobsSchema = z .array(appJobDefinitionSchema) .max(50) .default([]) .refine( (jobs) => new Set(jobs.map((job) => job.name)).size === jobs.length, "Job names must be unique.", );