diff --git a/src/agent/schema.ts b/src/agent/schema.ts index 0a29776..38d20c8 100644 --- a/src/agent/schema.ts +++ b/src/agent/schema.ts @@ -8,6 +8,7 @@ * - execute: Standard task execution (done/questions/error) * - discuss: Gather context through questions, output decisions * - breakdown: Decompose initiative into phases + * - decompose: Decompose phase into individual tasks */ import { z } from 'zod'; @@ -61,6 +62,30 @@ const phaseBreakdownSchema = z.object({ export type PhaseBreakdown = z.infer; +/** + * Task type enum - mirrors database task.type column. + */ +const taskTypeSchema = z.enum([ + 'auto', + 'checkpoint:human-verify', + 'checkpoint:decision', + 'checkpoint:human-action', +]); + +/** + * A task from decompose output. + * Prompt instructs: { "number": 1, "name": "...", "description": "...", "type": "auto", "dependencies": [0] } + */ +const taskBreakdownSchema = z.object({ + number: z.number().int().positive(), + name: z.string().min(1), + description: z.string(), + type: taskTypeSchema.default('auto'), + dependencies: z.array(z.number().int()).optional().default([]), +}); + +export type TaskBreakdown = z.infer; + // ============================================================================= // EXECUTE MODE SCHEMA (default) // ============================================================================= @@ -346,3 +371,105 @@ export const breakdownOutputJsonSchema = { }, ], }; + +// ============================================================================= +// DECOMPOSE MODE SCHEMA +// ============================================================================= + +/** + * Decompose mode output schema. + * Agent asks questions OR completes with tasks. + * + * Prompt tells agent: + * - Output "questions" status when needing clarification + * - Output "decompose_complete" status with tasks array when done + */ +export const decomposeOutputSchema = z.discriminatedUnion('status', [ + // Agent needs clarification + z.object({ + status: z.literal('questions'), + questions: z.array(questionItemSchema), + }), + // Agent has decomposed phase into tasks + z.object({ + status: z.literal('decompose_complete'), + tasks: z.array(taskBreakdownSchema), + }), + // Unrecoverable error + z.object({ + status: z.literal('unrecoverable_error'), + error: z.string(), + }), +]); + +export type DecomposeOutput = z.infer; + +/** + * JSON Schema for decompose mode (passed to Claude CLI --json-schema) + */ +export const decomposeOutputJsonSchema = { + type: 'object', + oneOf: [ + { + properties: { + status: { const: 'questions' }, + questions: { + type: 'array', + items: { + type: 'object', + properties: { + id: { type: 'string' }, + question: { type: 'string' }, + options: { + type: 'array', + items: { + type: 'object', + properties: { + label: { type: 'string' }, + description: { type: 'string' }, + }, + required: ['label'], + }, + }, + }, + required: ['id', 'question'], + }, + }, + }, + required: ['status', 'questions'], + }, + { + properties: { + status: { const: 'decompose_complete' }, + tasks: { + type: 'array', + items: { + type: 'object', + properties: { + number: { type: 'integer', minimum: 1 }, + name: { type: 'string', minLength: 1 }, + description: { type: 'string' }, + type: { + type: 'string', + enum: ['auto', 'checkpoint:human-verify', 'checkpoint:decision', 'checkpoint:human-action'], + }, + dependencies: { + type: 'array', + items: { type: 'integer' }, + }, + }, + required: ['number', 'name', 'description'], + }, + }, + }, + required: ['status', 'tasks'], + }, + { + properties: { + status: { const: 'unrecoverable_error' }, + error: { type: 'string' }, + }, + required: ['status', 'error'], + }, + ], +};