feat(08.1-01): define agent output schema with Zod
- Discriminated union with done/question/unrecoverable_error status - Options schema for structured question choices - JSON schema export for Claude CLI --json-schema flag - Type export for runtime validation
This commit is contained in:
95
src/agent/schema.ts
Normal file
95
src/agent/schema.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Agent Output Schema
|
||||
*
|
||||
* Defines structured output schema for Claude agents using discriminated unions.
|
||||
* Replaces broken AskUserQuestion detection with explicit agent status signaling.
|
||||
*/
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* Option for questions - allows agent to present choices to user
|
||||
*/
|
||||
const optionSchema = z.object({
|
||||
label: z.string(),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
/**
|
||||
* Discriminated union for agent output.
|
||||
*
|
||||
* Agent must return one of:
|
||||
* - done: Task completed successfully
|
||||
* - question: Agent needs user input to continue
|
||||
* - unrecoverable_error: Agent hit an error it cannot recover from
|
||||
*/
|
||||
export const agentOutputSchema = z.discriminatedUnion('status', [
|
||||
// Agent completed successfully
|
||||
z.object({
|
||||
status: z.literal('done'),
|
||||
result: z.string(),
|
||||
filesModified: z.array(z.string()).optional(),
|
||||
}),
|
||||
|
||||
// Agent needs user input to continue
|
||||
z.object({
|
||||
status: z.literal('question'),
|
||||
question: z.string(),
|
||||
options: z.array(optionSchema).optional(),
|
||||
multiSelect: z.boolean().optional(),
|
||||
}),
|
||||
|
||||
// Agent hit unrecoverable error
|
||||
z.object({
|
||||
status: z.literal('unrecoverable_error'),
|
||||
error: z.string(),
|
||||
attempted: z.string().optional(),
|
||||
}),
|
||||
]);
|
||||
|
||||
export type AgentOutput = z.infer<typeof agentOutputSchema>;
|
||||
|
||||
/**
|
||||
* JSON Schema for --json-schema flag (convert Zod to JSON Schema).
|
||||
* This is passed to Claude CLI to enforce structured output.
|
||||
*/
|
||||
export const agentOutputJsonSchema = {
|
||||
type: 'object',
|
||||
oneOf: [
|
||||
{
|
||||
properties: {
|
||||
status: { const: 'done' },
|
||||
result: { type: 'string' },
|
||||
filesModified: { type: 'array', items: { type: 'string' } },
|
||||
},
|
||||
required: ['status', 'result'],
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
status: { const: 'question' },
|
||||
question: { type: 'string' },
|
||||
options: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
label: { type: 'string' },
|
||||
description: { type: 'string' },
|
||||
},
|
||||
required: ['label'],
|
||||
},
|
||||
},
|
||||
multiSelect: { type: 'boolean' },
|
||||
},
|
||||
required: ['status', 'question'],
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
status: { const: 'unrecoverable_error' },
|
||||
error: { type: 'string' },
|
||||
attempted: { type: 'string' },
|
||||
},
|
||||
required: ['status', 'error'],
|
||||
},
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user