- Change status from 'question' to 'questions' (plural) - Add QuestionItem with id field for answer matching - Update PendingQuestion to PendingQuestions with questions array - Update AgentWaitingEvent payload to questions array - Update ClaudeAgentManager and MockAgentManager adapters - Update TestHarness and all test files
114 lines
2.9 KiB
TypeScript
114 lines
2.9 KiB
TypeScript
/**
|
|
* 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(),
|
|
});
|
|
|
|
/**
|
|
* Individual question item with unique ID for answer matching
|
|
*/
|
|
const questionItemSchema = z.object({
|
|
id: z.string(),
|
|
question: z.string(),
|
|
options: z.array(optionSchema).optional(),
|
|
multiSelect: z.boolean().optional(),
|
|
});
|
|
|
|
/**
|
|
* Discriminated union for agent output.
|
|
*
|
|
* Agent must return one of:
|
|
* - done: Task completed successfully
|
|
* - questions: Agent needs user input to continue (supports multiple questions)
|
|
* - 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 (one or more questions)
|
|
z.object({
|
|
status: z.literal('questions'),
|
|
questions: z.array(questionItemSchema),
|
|
}),
|
|
|
|
// 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: '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'],
|
|
},
|
|
},
|
|
multiSelect: { type: 'boolean' },
|
|
},
|
|
required: ['id', 'question'],
|
|
},
|
|
},
|
|
},
|
|
required: ['status', 'questions'],
|
|
},
|
|
{
|
|
properties: {
|
|
status: { const: 'unrecoverable_error' },
|
|
error: { type: 'string' },
|
|
attempted: { type: 'string' },
|
|
},
|
|
required: ['status', 'error'],
|
|
},
|
|
],
|
|
};
|