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