feat(l1): frontend api/types for next-node, intake outcome, categories

Add IntakeOutcome/IntakeResult/NearMiss, TreeNode union, NextNodeRequest/Result,
L1Categories types; add ai_build to SessionKind; retype intake() to IntakeResult and
add nextNode/escalations/getCategories/setCategories methods. nextNode body carries
node_text (backend advance_ai_build stores it). tsc -b clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-30 20:06:43 -04:00
parent 7c25b42fb0
commit 03e87488b0
2 changed files with 69 additions and 8 deletions

View File

@@ -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<IntakeResponse>('/l1/intake', body).then(r => r.data),
apiClient.post<IntakeResult>('/l1/intake', body).then(r => r.data),
nextNode: (sessionId: string, body: NextNodeRequest) =>
apiClient
.post<NextNodeResult>(`/l1/sessions/${sessionId}/next-node`, body)
.then(r => r.data),
escalations: () =>
apiClient.get<WalkSession[]>('/l1/escalations').then(r => r.data),
getCategories: () =>
apiClient.get<L1Categories>('/accounts/me/l1-categories').then(r => r.data),
setCategories: (enabled: string[]) =>
apiClient
.patch<L1Categories>('/accounts/me/l1-categories', { enabled })
.then(r => r.data),
queue: (statusFilter?: string) =>
apiClient.get<QueueRow[]>('/l1/queue', {

View File

@@ -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[]
}