import type { IsoTime } from './common.js'; /** Opaque, stable, URL-safe ticket id: lowercase SHA-256 hex. */ export type InboxTicketIdDTO = string; export interface TicketSourceDTO { sessionName?: string; askedBy?: string; blockedSince?: IsoTime; emittedAt?: IsoTime; profileName?: string; nodeId?: string; } export interface ReviewTicketSummaryDTO { /** Request identity for direct request/review routes. */ request_id: string; /** Daemon-owned projection directory. */ request_dir: string; ticket_id: InboxTicketIdDTO; kind: 'review'; title: string; subtitle: string; blocked_since: IsoTime; source: TicketSourceDTO; } /** One question's recommended option, flat on the wire like every other summary field. */ export interface RecommendedOptionDTO { slot_id: string; option_id: string; label: string; } /** * What one keypress on an inbox row publishes. `responses` is a complete final response * map this daemon already accepts for the ticket, so a client publishes it untouched * rather than assembling one from the summary. */ export interface PageFastActionDTO { kind: 'answer' | 'acknowledge'; responses: PageResponsesDTO; } export interface PageTicketSummaryDTO { /** Request identity for direct request routes. */ request_id: string; /** Daemon-owned projection directory. */ request_dir: string; ticket_id: InboxTicketIdDTO; kind: 'page'; title: string; subtitle?: string; placement: 'inline' | 'panel'; dialect: 'jsx' | 'html'; inbox: boolean; steps: number; emitted_at: IsoTime; source: TicketSourceDTO; slot_kinds: string[]; /** The ticket is waiting on the person: it asks something, or an agent bridge is parked on its reply. Not a writability gate — any pending, non-passive ticket accepts a response. */ awaits_response: boolean; /** Omitted when no question recommends an option. */ recommended_options?: RecommendedOptionDTO[]; /** Present only on a pending ticket that a row can settle without opening it. */ fast_action?: PageFastActionDTO; state: 'pending' | 'resolved' | 'canceled' | 'passive'; /** Present only on resolved history entries; the stored one-line answer digest. */ answer_digest?: string; } export type InboxTicketSummaryDTO = PageTicketSummaryDTO | ReviewTicketSummaryDTO; export interface InboxListDTO { tickets: InboxTicketSummaryDTO[]; } export interface PageSlotDTO { id?: string; kind: string; step: number; config: Record; unvalidated?: true; display?: true; } /** The nested protocol object is deliberately camelCase on the wire. */ export interface PageManifestDTO { schema: 'crtr.page/v2'; dialect: 'jsx' | 'html'; title: string; subtitle?: string; delivery: { placement: 'inline' | 'panel'; inbox: boolean; reply: boolean; }; document: 'page.tsx' | 'page.html'; steps: number; slots: PageSlotDTO[]; source?: TicketSourceDTO; } /** Answer-annotation anchors: option on UserQuestion, row on UserTable. * Feedback on other page content — including the question text — is a * ticket-level feedback comment, never a response anchor. */ export interface PageCommentAnchorDTO { kind: 'option' | 'row'; optionId?: string; rowId?: string; } export interface PageCommentDTO { id: string; anchor: PageCommentAnchorDTO; text: string; } export interface PageOptionsResponseDTO { selectedOptionIds: string[]; comments: PageCommentDTO[]; freetext?: string; } export interface PageTextResponseDTO { text: string; edited: boolean; } export interface PageTableResponseDTO { selectedRowIds: string[]; selectedColumnIds: string[]; comments: PageCommentDTO[]; } export interface PageCardsResponseDTO { selectedCardIds: string[]; } export type SlotResponseDTO = PageOptionsResponseDTO | PageTextResponseDTO | PageTableResponseDTO | PageCardsResponseDTO | Record; export type PageResponsesDTO = Record; export interface InboxPageDTO { ticket_id: InboxTicketIdDTO; kind: 'page'; state: 'pending' | 'resolved' | 'canceled' | 'passive'; /** Opaque SQL-selected immutable presentation revision. */ revision: string; page: PageManifestDTO; /** JSX is compiled for the host; HTML is the authored document verbatim. */ document: string; /** Authored JSX or HTML, distinct from the compiled JSX host document. */ source_document: string; document_media_type: 'text/jsx' | 'text/html'; progress: { responses: PageResponsesDTO; } | null; /** Canonical stored terminal result, or null while the ticket remains open. */ result: PageTicketResultDTO | CanceledTicketResultDTO | null; /** Ticket-attached feedback comments and the companion conversation they * deliver to. Comments are immutable once sent — open until the companion * resolves them. */ feedback: PageFeedbackDTO; } export interface PageFeedbackCommentDTO { id: string; /** The highlighted text the note is about; for a chart, its title. */ quote: string; /** Surrounding text that locates the quote on the page. */ context?: string; note: string; /** Open until the companion has dealt with it; resolution is terminal. */ status: 'open' | 'resolved'; created_at: IsoTime; resolved_at?: IsoTime; } export interface PageFeedbackDTO { /** The ticket-bound companion conversation comments deliver to; null until * the first comment realizes it. */ companion_node_id: string | null; /** How many comments remain open. Zero once feedback exists is the ticket's * authoritative signal that the companion has dealt with all of it. */ open_count: number; comments: PageFeedbackCommentDTO[]; } /** `POST /v1/human/inbox/:ticket_id/feedback-comments` body. */ export interface CreatePageFeedbackCommentRequest { quote: string; context?: string; note: string; } export interface PageFeedbackDeliveryDTO { target_node_id: string; status: 'delivered' | 'failed'; via?: 'engine' | 'inbox'; woke: boolean; reason?: 'capacity_frozen'; error?: string; } /** Create result: the new comment, the ticket's full feedback state after it, * and the delivery outcome in the companion conversation. */ export interface PageFeedbackMutationDTO { comment: PageFeedbackCommentDTO; feedback: PageFeedbackDTO; delivery: PageFeedbackDeliveryDTO; } /** `POST .../feedback-comments/:comment_id/resolve` request. Resolution is * companion-only: `node_id` names the calling node and must equal the * ticket's bound companion. */ export interface ResolvePageFeedbackCommentRequest { node_id: string; } /** `POST .../feedback-comments/:comment_id/resolve` result. Resolution appends * no chat turn; the companion's own conversational report covers it. */ export interface PageFeedbackResolutionDTO { comment: PageFeedbackCommentDTO; feedback: PageFeedbackDTO; } export interface InboxPageHistoryDTO { tickets: PageTicketSummaryDTO[]; } export interface RespondInboxPageRequest { responses: PageResponsesDTO; } export interface InboxPageProgressDTO { responses: PageResponsesDTO; } export interface PageTicketResultDTO { schema: 'crtr.page-response/v1'; kind: 'page'; responses: PageResponsesDTO; summary: string; completedAt: IsoTime; } export interface PageAnswerGroupDTO { group: 'option' | 'card' | 'row' | 'column'; mode: 'single' | 'multi'; total: number; picked: { id: string; label: string; }[]; } export interface PageAnswerCommentDTO { /** Legacy stored responses may still carry the retired `whole` anchor. */ anchor: PageCommentAnchorDTO | { kind: 'whole'; }; anchorLabel: string; text: string; } export interface PageAnswerSlotDTO { id: string; kind: string; label?: string; state: 'answered' | 'unanswered'; blank: boolean; groups: PageAnswerGroupDTO[]; freetext?: string; text?: { value: string; edited: boolean; singleLine?: boolean; }; comments: PageAnswerCommentDTO[]; raw?: Record; } /** The camelCase protocol object shipped only by GET .../response. */ export interface PageAnswerDTO { title: string; subtitle?: string; completedAt: IsoTime; kind: 'answer' | 'acknowledgement'; digest: string; slots: PageAnswerSlotDTO[]; } export interface InboxPageResponseDTO extends PageTicketResultDTO { answer: PageAnswerDTO; } /** `POST /v1/human/inbox/:ticket_id/cancel` body. */ export interface CancelInboxTicketRequest { reason?: string; } /** `POST /v1/human/inbox/:ticket_id/cancel` result. */ export interface CanceledTicketResultDTO { schema: 'humanloop.cancel/v1'; kind: 'canceled'; canceledAt: IsoTime; reason?: string; actor?: string; }