File size: 1,743 Bytes
4e23b01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
 * `sessionQuestionService` — ask-user request broker. Mirrors
 * `agent-core-v2/agent/interaction/question.ts` (the in-process camelCase
 * representation; the snake_case protocol shape is adapted at the edge).
 */

import { z } from 'zod';

import { noResult } from '../helpers.js';
import type { ServiceContract } from '../types.js';

export const questionOptionSchema = z.object({
  label: z.string(),
  description: z.string().optional(),
});

export const questionItemSchema = z.object({
  question: z.string(),
  header: z.string().optional(),
  body: z.string().optional(),
  options: z.array(questionOptionSchema),
  multiSelect: z.boolean().optional(),
  otherLabel: z.string().optional(),
  otherDescription: z.string().optional(),
});

/** `QuestionAnswers = Record<string, string | true>`. */
export const questionAnswersSchema = z.record(
  z.string(),
  z.union([z.string(), z.literal(true)]),
);

export const questionResponseSchema = z.object({
  answers: questionAnswersSchema,
  method: z.enum(['enter', 'space', 'number_key']).optional(),
});

/** `QuestionResult = null | QuestionAnswers | QuestionResponse`. */
export const questionResultSchema = z.union([
  z.null(),
  questionAnswersSchema,
  questionResponseSchema,
]);

export const questionRequestSchema = z.object({
  id: z.string().optional(),
  turnId: z.number().optional(),
  toolCallId: z.string().optional(),
  questions: z.array(questionItemSchema),
});

export const sessionQuestionContract = {
  listPending: { input: z.tuple([]), output: z.array(questionRequestSchema) },
  answer: { input: z.tuple([z.string(), questionResultSchema]), output: noResult },
  dismiss: { input: z.tuple([z.string()]), output: noResult },
} satisfies ServiceContract;