import { z } from 'zod'; /** * Configuration for a loop-type DAG node. * * The loop repeatedly invokes the model with the given prompt until the * `until` condition is satisfied or `max_iterations` is reached. * When `interactive` is true, a human gate must approve each iteration. */ export const loopNodeConfigSchema = z .object({ /** The prompt sent to the model on each iteration. */ prompt: z.string().min(1, 'loop prompt must not be empty'), /** Natural-language condition that must be true for the loop to exit. */ until: z.string().min(1, 'loop until condition must not be empty'), /** Maximum iterations before the loop is force-stopped. */ max_iterations: z .number() .int() .positive('max_iterations must be a positive integer'), /** Whether each iteration starts with a fresh context window. */ fresh_context: z.boolean().default(false), /** Optional bash command whose exit code is used as the until-check. */ until_bash: z.string().optional(), /** When true, a human must approve each iteration before it proceeds. */ interactive: z.boolean().optional(), /** Message shown to the human gate when interactive is true. */ gate_message: z.string().optional(), }) .superRefine((config, ctx) => { if (config.interactive && !config.gate_message) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'gate_message is required when interactive is true', path: ['gate_message'], }); } }); export type LoopNodeConfig = z.infer;