diff --git a/frontend/src/api/l1.ts b/frontend/src/api/l1.ts index 512cc0f1..482d089a 100644 --- a/frontend/src/api/l1.ts +++ b/frontend/src/api/l1.ts @@ -1,7 +1,10 @@ import { apiClient } from './client' import type { IntakeRequest, - IntakeResponse, + IntakeResult, + L1Categories, + NextNodeRequest, + NextNodeResult, QueueRow, WalkSession, AdhocNote, @@ -9,7 +12,23 @@ import type { export const l1Api = { intake: (body: IntakeRequest) => - apiClient.post('/l1/intake', body).then(r => r.data), + apiClient.post('/l1/intake', body).then(r => r.data), + + nextNode: (sessionId: string, body: NextNodeRequest) => + apiClient + .post(`/l1/sessions/${sessionId}/next-node`, body) + .then(r => r.data), + + escalations: () => + apiClient.get('/l1/escalations').then(r => r.data), + + getCategories: () => + apiClient.get('/accounts/me/l1-categories').then(r => r.data), + + setCategories: (enabled: string[]) => + apiClient + .patch('/accounts/me/l1-categories', { enabled }) + .then(r => r.data), queue: (statusFilter?: string) => apiClient.get('/l1/queue', { diff --git a/frontend/src/types/l1.ts b/frontend/src/types/l1.ts index 95c499b5..ea74a577 100644 --- a/frontend/src/types/l1.ts +++ b/frontend/src/types/l1.ts @@ -1,4 +1,4 @@ -export type SessionKind = 'flow' | 'proposal' | 'adhoc' +export type SessionKind = 'flow' | 'proposal' | 'adhoc' | 'ai_build' export type SessionStatus = 'active' | 'resolved' | 'escalated' | 'abandoned' export type TicketKind = 'psa' | 'internal' @@ -42,11 +42,53 @@ export interface IntakeRequest { customer_name?: string customer_contact?: string flow_id?: string + force_build?: boolean } -export interface IntakeResponse { - session_id: string - session_kind: SessionKind - ticket_id: string - ticket_kind: TicketKind +export type IntakeOutcome = 'matched' | 'suggest' | 'out_of_scope' | 'build' + +export interface NearMiss { + flow_id: string + flow_name: string + score: number +} + +/** Phase 2A intake response — `outcome` drives the frontend dispatch. + * Session fields are present only for `matched` / `build`. */ +export interface IntakeResult { + outcome: IntakeOutcome + session_id?: string + session_kind?: SessionKind + ticket_id?: string + ticket_kind?: TicketKind + flow_id?: string // for 'matched' + near_miss?: NearMiss // for 'suggest' + category?: string // for 'out_of_scope' +} + +/** A single node of an AI-built decision tree, returned by /next-node. */ +export type TreeNode = + | { node_type: 'question'; id: string; text: string } + | { node_type: 'instruction'; id: string; text: string } + | { node_type: 'resolved'; id: string; text: string } + | { node_type: 'escalate'; id: string; reason_category?: string; text: string } + | { node_type: 'needs_review'; id: string; text: string } + +export interface NextNodeRequest { + node_id?: string + node_text?: string // rendered text of the node being answered + answer?: 'yes' | 'no' + acknowledged?: boolean + note?: string +} + +export interface NextNodeResult { + node: TreeNode + session_status: string +} + +export interface L1Categories { + enabled: string[] + available: string[] + hard_floor: string[] }