// Wire protocol mirrors backend/service/app.py. // Keep these names + shapes in lockstep with the Python side. export type UiHintField = { /** Canonical btsynonym this field will set when submitted. */ slot: string; /** Human-readable label rendered next to the input. */ label: string; /** Input control type. */ kind: "text" | "number" | "date" | "dropdown"; /** For kind=dropdown: the available options. */ options?: string[]; /** Optional placeholder text. */ placeholder?: string; }; export type UiHint = { kind: "text" | "dropdown" | "multi_dropdown" | "date" | "confirm" | "form" | "multi_slot_form"; slot?: string; label?: string; options?: string[]; prompt?: string; prefill?: Record; /** For kind=multi_slot_form: the fields to render in the form. */ fields?: UiHintField[]; /** Optional descriptive title shown above the form. */ title?: string; }; export type TraceEvent = { ts: number; turn_index: number; kind: string; level: "info" | "warn" | "error" | string; summary: string; data: Record; }; export type AgentStateSnapshot = { session_id: string; submodule: string; current_journey: string | null; slots: Record; variant_locked: boolean; locked_at_turn: number | null; turn_count: number; suspended_count: number; fired_apis: unknown[]; terminated: boolean; termination_reason: string | null; }; // Server -> Client export type ServerMessage = | { type: "session"; session_id: string; resumed: boolean; state: AgentStateSnapshot; available_journeys: string[]; } | { type: "turn_start"; user_message: string } | { type: "trace"; event: TraceEvent } | { type: "turn_complete"; reply: string; ui_hint: UiHint; state: AgentStateSnapshot; execution: unknown | null; terminated: boolean; } | { type: "pong" } | { type: "error"; message: string }; // Client -> Server export type ClientMessage = | { type: "user_message"; text: string } | { type: "ping" } | { type: "reset" }; // Internal message-thread model export type ChatMessage = | { id: string; role: "user"; text: string; ts: number } | { id: string; role: "assistant"; text: string; ts: number; ui_hint?: UiHint; // candidate_journeys surfaced from the resolver when ambiguous candidates?: string[]; // attached state at the moment this message was produced terminated?: boolean; } | { id: string; role: "system"; text: string; ts: number; level?: "info" | "warn" | "error" }; export type Scenario = { id: string; title: string; description: string; prompt: string; /** Example follow-up messages — what to type next once the initial prompt is sent. */ followups?: string[]; category: "happy_path" | "ambiguity" | "pivot" | "edge_case" | "off_topic"; }; export type ConnectionStatus = "connecting" | "open" | "closed" | "error";