import { z } from "zod"; export const recordSchema = z.record(z.string(), z.unknown()); export type Profile = z.infer; export const configSchema = z.object({ search: z.boolean().default(false), lb: z.boolean().default(false), allowDirect: z.boolean().default(true), s2sUrl: z.string().default(""), primaryPrompt: z.string().default(""), reasoningType: z.enum(["PROXY", "OPENAI", "NODE_RED"]).catch("PROXY"), auth: z.boolean().default(false), injectProfile: z.boolean().default(false), debugPrompt: z.boolean().default(false), startSessionMode: z.string().default(""), theme: z.string().default(""), apipilot: z.boolean().default(false), }); export type ServerConfig = z.infer; export const defaultConfig = configSchema.parse({}); export const reportsSchema = z.object({ count: z.number().default(0), hints: z.array(z.string()).default([]), entries: z .array( z.object({ sourceScheduleLabel: z.string().nullish(), date: z.union([z.string(), z.number()]).nullish(), }), ) .default([]), reason: z.string().optional(), truncated: z.boolean().optional(), note: z.string().optional(), }); export type Reports = z.infer; export const accountSchema = z.object({ enabled: z.boolean(), loggedIn: z.boolean().default(false), auth: z.boolean().default(false), tier: z.string().default("anon"), username: z.string().nullish(), avatar: z.string().nullish(), loginUrl: z.string().nullish(), logoutUrl: z.string().nullish(), remainingSec: z.number().nullish(), limitSec: z.number().nullish(), }); export type AccountInfo = z.infer; export const toolSchema = z.object({ name: z.string(), description: z.string().default(""), parameters: recordSchema.default({}), requires_auth: z.boolean().default(false), }); export type DynamicTool = z.infer; export interface FunctionTool { type: "function"; name: string; description: string; parameters: Record; } export const toolsEnvelopeSchema = z.object({ success: z.boolean(), data: z.object({ tools: z.array(toolSchema), count: z.number().optional() }).nullish(), error: z.unknown().optional(), }); export const grantSchema = z.object({ session_id: z.string(), connect_url: z.string(), pending_timeout_s: z.number().default(60), tier: z.string().default("anon"), limited: z.boolean().default(false), heartbeatSec: z.number().default(5), remainingSec: z.number().nullish(), }); export type SessionGrant = z.infer; export const queueSchema = z.object({ state: z.literal("queued"), queue_id: z.string(), position: z.number().default(0), poll_interval_s: z.number().default(2), }); export const sessionSchema = z.union([queueSchema, grantSchema]); export type ConnectionStatus = | "idle" | "connecting" | "queued" | "your-turn" | "listening" | "user-speaking" | "processing" | "ai-speaking" | "closed" | "error"; export interface Transcript { role: "user" | "assistant"; text: string; partial: boolean; id: string; } export interface FinishedResponse { responseId: string; status: string; transcript: string; audible: boolean; } export interface ToolCall { name: string; arguments: string; callId: string; } export interface NoiseGate { enabled: boolean; thresholdDb: number; } const contentSchema = z.object({ type: z.string().optional(), text: z.string().optional(), transcript: z.string().optional(), }); // The wire protocol is additive; unknown event fields are ignored, while consumed fields are validated. export const realtimeEventSchema = z.object({ type: z.string(), response_id: z.string().optional(), item_id: z.string().optional(), delta: z.string().optional(), transcript: z.string().optional(), text: z.string().optional(), name: z.string().optional(), arguments: z.string().optional(), call_id: z.string().optional(), response: z .object({ id: z.string().optional(), status: z.string().optional(), output: z.array(z.object({ content: z.array(contentSchema).optional() })).optional(), }) .optional(), part: contentSchema.optional(), error: z .object({ type: z.string().optional(), code: z.string().optional(), message: z.string().optional() }) .optional(), }); export type RealtimeEvent = z.infer;