@icanbwell/baileyai-chat-ui 1.0.15 → 1.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -22,6 +22,30 @@ export declare const BAILEY_TABLE_GRID_ROW_THRESHOLD = 5;
22
22
 
23
23
  export declare const BAILEYAI_CHAT_UI_VERSION: string;
24
24
 
25
+ /**
26
+ * Typed failure from the Bailey client. The message is a fixed string per kind and never
27
+ * includes a response body (bodies can carry PHI) or the text of `cause`.
28
+ *
29
+ * For kinds `network` and `timeout`, `cause` is the original error the runtime threw (the failed
30
+ * fetch's error, or the abort reason). It is never set for `http`, `invalid-response` or `aborted`.
31
+ * Browsers do not say whether a failed fetch was CORS, DNS or offline: all surface as a generic
32
+ * `TypeError`, so `kind` plus `cause` is all a host can show or log. Do not display `cause.message`
33
+ * to users without review, and do not log the request URL (it can carry identifiers).
34
+ */
35
+ export declare class BaileyApiError extends Error {
36
+ /** HTTP status; 0 when no response was received (network, timeout, aborted). */
37
+ readonly status: number;
38
+ readonly kind: BaileyApiErrorKind;
39
+ constructor(params: {
40
+ status: number;
41
+ kind: BaileyApiErrorKind;
42
+ message?: string;
43
+ cause?: unknown;
44
+ });
45
+ }
46
+
47
+ export declare type BaileyApiErrorKind = 'http' | 'network' | 'timeout' | 'aborted' | 'invalid-response';
48
+
25
49
  export declare interface BaileyChartDataset {
26
50
  label: string;
27
51
  data: number[] | Array<{
@@ -53,9 +77,9 @@ export declare interface BaileyChatInputMessage {
53
77
  titleHint?: string;
54
78
  }
55
79
 
56
- export declare function BaileyChatPanel({ chat, placeholder, replyPlaceholder, emptyPlaceholder, suggestions, onHistoryClick, privacyNote, resolveActivityIcon, className, height, width, hideDeveloperDetails, renderMessageActions, renderTable, renderChart, renderMcpApp, labels: labelsProp, formatMessageTime: formatMessageTimeProp, }: BaileyChatPanelProps): JSX_2.Element;
80
+ export declare function BaileyChatPanel({ chat, placeholder, replyPlaceholder, emptyPlaceholder, suggestions, onHistoryClick, history, locale, privacyNote, resolveActivityIcon, className, height, width, hideDeveloperDetails, renderMessageActions, renderTable, renderChart, renderMcpApp, labels: labelsProp, formatMessageTime: formatMessageTimeProp, }: BaileyChatPanelProps): JSX_2.Element;
57
81
 
58
- export declare interface BaileyChatPanelLabels extends ToolStepLabels {
82
+ export declare interface BaileyChatPanelLabels extends ToolStepLabels, Partial<BaileyHistoryLabels> {
59
83
  /** Visible text of the "new chat" item in the "..." menu. */
60
84
  newChatButtonText: string;
61
85
  /** @deprecated No longer applied -- the "New chat" menu item is named by its visible text
@@ -102,6 +126,11 @@ export declare interface BaileyChatPanelLabels extends ToolStepLabels {
102
126
  * McpAppFrame renderer — a host-supplied `renderMcpApp` (BaileyChatPanelProps) owns its
103
127
  * own accessible naming instead. */
104
128
  mcpAppDefaultTitle: string;
129
+ /** Visible text of the control at the top of the transcript that loads older messages of a
130
+ * loaded conversation (only rendered while `chat.hasEarlier`). */
131
+ loadEarlierButtonText?: string;
132
+ /** Shown beside that control when the last history load failed. Fixed copy: never an error message. */
133
+ loadEarlierErrorText?: string;
105
134
  }
106
135
 
107
136
  export declare interface BaileyChatPanelProps extends BaileyMarkdownRenderers {
@@ -140,6 +169,17 @@ export declare interface BaileyChatPanelProps extends BaileyMarkdownRenderers {
140
169
  * stay reachable even before the first message. Omit entirely to keep the menu to just
141
170
  * "New chat" (and no menu at all while the conversation is empty). */
142
171
  onHistoryClick?: () => void;
172
+ /** Opt-in default conversation-history drawer. Passing a Bailey client adds the "Chat history"
173
+ * menu item and opens a drawer inside the panel (nothing is fetched until it opens). Selecting
174
+ * a row calls `chat.loadConversation`, so `chat` must have been created with the same
175
+ * `client`. `onHistoryClick`, when also supplied, wins: the host's own UI opens and the drawer
176
+ * never mounts. Omit to keep today's behavior. */
177
+ history?: {
178
+ client: BaileyClient;
179
+ pageSize?: number;
180
+ };
181
+ /** Locale for the drawer's relative timestamps. Defaults to `navigator.language`. */
182
+ locale?: string;
143
183
  /** A short privacy/compliance note rendered below the input row, as the panel's bottom-most
144
184
  * element — empty state only by default, or every state if `privacyNote.alwaysVisible` is
145
185
  * set. No default — the exact copy/link is host-specific, so nothing is shown unless a host
@@ -147,7 +187,8 @@ export declare interface BaileyChatPanelProps extends BaileyMarkdownRenderers {
147
187
  privacyNote?: BaileyPrivacyNote;
148
188
  /** Customizes the icon shown next to each tool step — the live "Checking your vitals…" line
149
189
  * while a tool runs, and each entry in the expanded "Used N tools" summary — based on the
150
- * tool name. No icon is shown for a step unless this returns one. */
190
+ * tool name. No icon is shown for a step unless this returns one: `null`, `undefined`,
191
+ * `false`, `true` and `''` render nothing, and `0` renders as "0". */
151
192
  resolveActivityIcon?: (toolName: string | null) => ReactNode;
152
193
  className?: string;
153
194
  /** Explicit panel height (number is treated as px, e.g. 600 or '600px' or '100vh'). Applied
@@ -188,13 +229,91 @@ export declare interface BaileyChatTransport {
188
229
  signal: AbortSignal;
189
230
  onChunk: (text: string) => void;
190
231
  onRequestId?: (requestId: string) => void;
232
+ /** Optional. When present, a transport that supports it sends it as the `x-chat-id`
233
+ * request header so the backend loads and persists this conversation. Absent means the
234
+ * request carries no conversation identity (the behavior of every existing host). */
235
+ conversationId?: string;
236
+ /** Optional. Fired with the conversation id the backend reports in the
237
+ * `X-Conversation-Id` response header, if the transport can read it. */
238
+ onConversationId?: (conversationId: string) => void;
239
+ onMessageId?: (messageId: string) => void;
191
240
  }): Promise<{
192
241
  status?: number;
193
242
  text: string;
194
243
  errorMessage?: string;
244
+ /** Optional. `true` when the connection dropped after the response began, so what was
245
+ * delivered through `onChunk` is incomplete. `useBaileyChat` then ends the turn in the
246
+ * error state (the partial text stays visible) instead of treating it as a finished
247
+ * answer. A transport that never sets it behaves exactly as before. */
248
+ interrupted?: boolean;
195
249
  }>;
196
250
  }
197
251
 
252
+ /**
253
+ * A best-effort cleanup step (e.g. cancelling an unread response body) failed. Reported through
254
+ * the client's `onCleanupError` option and never thrown to the caller. The message is fixed and
255
+ * carries no response data.
256
+ */
257
+ export declare class BaileyCleanupError extends Error {
258
+ readonly operation: 'cancel-response-body' | 'cancel-stream-reader';
259
+ constructor(params: {
260
+ operation: BaileyCleanupError['operation'];
261
+ });
262
+ }
263
+
264
+ export declare interface BaileyClient extends BaileyChatTransport {
265
+ listConversations(params?: {
266
+ limit?: number;
267
+ cursor?: string;
268
+ signal?: AbortSignal;
269
+ }): Promise<BaileyConversationList>;
270
+ getConversation(params: {
271
+ conversationId: string;
272
+ limit?: number;
273
+ beforeAbsoluteIndex?: number;
274
+ signal?: AbortSignal;
275
+ }): Promise<BaileyConversationPage>;
276
+ deleteConversation(params: {
277
+ conversationId: string;
278
+ signal?: AbortSignal;
279
+ }): Promise<void>;
280
+ submitFeedback(params: {
281
+ conversationId: string;
282
+ messageId: string;
283
+ rating: 'up' | 'down';
284
+ reasonCodes?: string[];
285
+ comment?: string;
286
+ signal?: AbortSignal;
287
+ }): Promise<void>;
288
+ clearFeedback(params: {
289
+ conversationId: string;
290
+ messageId: string;
291
+ signal?: AbortSignal;
292
+ }): Promise<void>;
293
+ }
294
+
295
+ export declare interface BaileyClientOptions {
296
+ /** Bailey API root. Expected to include the `/bailey/v1` prefix; a trailing slash is tolerated. */
297
+ baseUrl: string;
298
+ /** Called before every request. The library never reads or stores tokens itself. */
299
+ getHeaders: () => Promise<Record<string, string>>;
300
+ fetch?: typeof fetch;
301
+ /** Fired once on a 401/403 response, before the call rejects (or the stream call returns). */
302
+ onAuthError?: (error: BaileyApiError) => void;
303
+ /** Called when a best-effort cleanup step fails (e.g. cancelling an unread response body).
304
+ * Never thrown to the caller; an exception thrown by this callback is also contained. */
305
+ onCleanupError?: (error: BaileyCleanupError) => void;
306
+ /** Timeout for non-stream calls. Default 30_000. */
307
+ requestTimeoutMs?: number;
308
+ /** Max gap between stream chunks before the stream is aborted. Default 155_000 (a backstop just above `useBaileyChat`'s own 150s idle timeout). */
309
+ idleTimeoutMs?: number;
310
+ /** How `streamChat` sends a held conversation id: the legacy `x-chat-id` request header
311
+ * (default) or the `conversation` field in the JSON body. Never both. Keep `'header'` until
312
+ * the backend release that accepts the body field is deployed wherever the host runs;
313
+ * an older backend rejects the unknown field with HTTP 400. */
314
+ conversationVia?: 'header' | 'body';
315
+ }
316
+
198
317
  export declare type BaileyContentPart = {
199
318
  type: 'text';
200
319
  text: string;
@@ -213,6 +332,170 @@ export declare type BaileyContentPart = {
213
332
  displayMode?: string;
214
333
  };
215
334
 
335
+ export declare interface BaileyContextLossInfo {
336
+ /** The id the turn sent. */
337
+ sentId: string;
338
+ /** The id the server reported instead; undefined when it reported none. */
339
+ reportedId?: string;
340
+ }
341
+
342
+ export declare interface BaileyConversationList {
343
+ conversations: BaileyConversationSummary[];
344
+ nextCursor: string | null;
345
+ }
346
+
347
+ export declare interface BaileyConversationPage {
348
+ conversation: BaileyConversationSummary;
349
+ messages: BaileyServerMessage[];
350
+ hasMoreBefore: boolean;
351
+ nextBeforeAbsoluteIndex: number | null;
352
+ }
353
+
354
+ /** Pluggable persistence for the held conversation id. `useBaileyChat` guards every call: an
355
+ * implementation that throws is reported through `onStorageError` and the id stays in memory. */
356
+ export declare interface BaileyConversationStorage {
357
+ get(key: string): string | null;
358
+ set(key: string, value: string): void;
359
+ remove(key: string): void;
360
+ }
361
+
362
+ export declare interface BaileyConversationSummary {
363
+ conversationId: string;
364
+ title: string | null;
365
+ status: string;
366
+ messageCount: number;
367
+ createdAt: string;
368
+ updatedAt: string;
369
+ }
370
+
371
+ export declare function BaileyFeedbackButtons(props: BaileyFeedbackButtonsProps): JSX_2.Element | null;
372
+
373
+ export declare interface BaileyFeedbackButtonsProps {
374
+ /** The backend's assistant message ID (`BaileyMessage.serverMessageId`). Renders nothing when empty. */
375
+ messageId: string;
376
+ /** Create or replace the rating. Any rejection (including a 404) means failure: the selection
377
+ * reverts and an error is shown. Nothing is retried automatically; the user retries by hand. */
378
+ onSubmit: (submission: BaileyFeedbackSubmission) => Promise<void>;
379
+ /** Remove the rating and comment. Same rejection contract as `onSubmit`. */
380
+ onClear: (params: {
381
+ messageId: string;
382
+ }) => Promise<void>;
383
+ /** A previously submitted rating to show as selected. */
384
+ initialRating?: BaileyFeedbackRating;
385
+ /** Subset (and order) of the backend reason codes to offer. Defaults to all five. */
386
+ reasonOptions?: BaileyFeedbackReasonCode[];
387
+ /** Comment length limit; defaults to 2000 and can only be lowered. */
388
+ maxCommentLength?: number;
389
+ labels?: Partial<BaileyFeedbackLabels>;
390
+ className?: string;
391
+ }
392
+
393
+ export declare interface BaileyFeedbackHandlers {
394
+ onSubmit: (submission: BaileyFeedbackSubmission) => Promise<void>;
395
+ onClear: (params: {
396
+ messageId: string;
397
+ }) => Promise<void>;
398
+ }
399
+
400
+ export declare interface BaileyFeedbackLabels {
401
+ feedbackGroupLabel: string;
402
+ feedbackThumbsUpAriaLabel: string;
403
+ feedbackThumbsDownAriaLabel: string;
404
+ feedbackReasonGroupLabel: string;
405
+ feedbackCommentLabel: string;
406
+ feedbackCommentPlaceholder: string;
407
+ feedbackSubmitText: string;
408
+ feedbackCancelText: string;
409
+ feedbackThanksText: string;
410
+ feedbackUndoText: string;
411
+ feedbackEditText: string;
412
+ feedbackAddCommentText: string;
413
+ feedbackSavingText: string;
414
+ feedbackErrorText: string;
415
+ feedbackPrivacyNote: string;
416
+ feedbackReasonInaccurate: string;
417
+ feedbackReasonUnhelpful: string;
418
+ feedbackReasonUnsafe: string;
419
+ feedbackReasonTooLong: string;
420
+ feedbackReasonOther: string;
421
+ }
422
+
423
+ export declare type BaileyFeedbackRating = 'up' | 'down';
424
+
425
+ /** The backend's reason-code enum. A host may select a subset, never add a code: the backend
426
+ * rejects unknown codes with 422, which would fail the whole submit. */
427
+ export declare type BaileyFeedbackReasonCode = 'inaccurate' | 'unhelpful' | 'unsafe' | 'too_long' | 'other';
428
+
429
+ export declare interface BaileyFeedbackSubmission {
430
+ messageId: string;
431
+ rating: BaileyFeedbackRating;
432
+ /** Omitted when no reason is selected. */
433
+ reasonCodes?: BaileyFeedbackReasonCode[];
434
+ /** Omitted when blank. PHI-capable: only ever passed to `onSubmit`. */
435
+ comment?: string;
436
+ }
437
+
438
+ /** Owns the conversations list (so nothing is fetched until the drawer is mounted) and wires the
439
+ * chat actions into the presentational view. */
440
+ export declare function BaileyHistoryDrawer({ client, pageSize, chat, labels, locale, onClose }: BaileyHistoryDrawerProps): JSX_2.Element;
441
+
442
+ export declare interface BaileyHistoryDrawerProps {
443
+ client: BaileyClient;
444
+ pageSize?: number;
445
+ chat: Pick<UseBaileyChatResult, 'conversationId' | 'loadConversation' | 'newChat' | 'historyStatus'>;
446
+ /** Already merged with the defaults by the panel. */
447
+ labels: BaileyHistoryLabels;
448
+ locale?: string;
449
+ onClose: (params: {
450
+ restoreFocusTo: BaileyHistoryFocusTarget;
451
+ }) => void;
452
+ }
453
+
454
+ declare type BaileyHistoryFocusTarget = 'menu' | 'input';
455
+
456
+ export declare interface BaileyHistoryLabels {
457
+ /** Reused from the panel's menu: also the drawer's "New chat" control. */
458
+ newChatButtonText: string;
459
+ /** Reused from the panel's error banner: the drawer's Retry controls. */
460
+ retryButtonText: string;
461
+ historyTitle: string;
462
+ historyBackAriaLabel: string;
463
+ historyLoadingText: string;
464
+ historyLoadingMoreText: string;
465
+ historyLoadMoreText: string;
466
+ historyLoadMoreErrorText: string;
467
+ /** `{count}` is the number of conversations shown. */
468
+ historyCountTextOne: string;
469
+ historyCountTextMany: string;
470
+ historyEmptyTitle: string;
471
+ historyEmptyDescription: string;
472
+ historyEmptyCtaText: string;
473
+ historyErrorTitle: string;
474
+ historyErrorDescription: string;
475
+ /** Shown instead of the generic error when the list call fails with a 401/403. The English
476
+ * wording is a default for copy review. */
477
+ historySessionExpiredTitle: string;
478
+ historySessionExpiredDescription: string;
479
+ /** Shown when opening the chosen conversation failed. */
480
+ historySelectErrorText: string;
481
+ historyUntitledText: string;
482
+ historyCurrentText: string;
483
+ historyMessageCountOne: string;
484
+ historyMessageCountMany: string;
485
+ /** `{title}` is the conversation title (user-facing only; never logged). */
486
+ historyDeleteAriaLabel: string;
487
+ historyDeleteConfirmQuestion: string;
488
+ historyDeleteConfirmText: string;
489
+ historyDeleteCancelText: string;
490
+ historyDeletingText: string;
491
+ historyDeletedText: string;
492
+ historyDeleteFailedText: string;
493
+ historyDeleteRetryText: string;
494
+ historyDeleteDismissText: string;
495
+ /** Only used when `Intl.RelativeTimeFormat` is unavailable. */
496
+ historyJustNowText: string;
497
+ }
498
+
216
499
  export declare interface BaileyLastRequest {
217
500
  model: string;
218
501
  systemPrompt: string;
@@ -269,6 +552,19 @@ export declare interface BaileyMessage {
269
552
  * never reads or interprets it — it exists purely as a pass-through so a transport/backend
270
553
  * can use it as a conversation-title fallback for a turn whose only content is hidden. */
271
554
  titleHint?: string;
555
+ /** The backend's own ID for this assistant message, from `response.completed.message_id` (or
556
+ * the transport's `onMessageId`). Distinct from `id`, which is minted client-side. A host
557
+ * needs it to address the message in server APIs such as feedback; undefined on user
558
+ * messages and when the backend doesn't report one (older backends). */
559
+ serverMessageId?: string;
560
+ /** The rating the user previously gave this assistant message, when the backend reports one
561
+ * for a loaded conversation. Feeds `BaileyFeedbackButtons`' `initialRating`. PHI-capable
562
+ * (`comment`): never logged or placed in trace events. */
563
+ feedback?: {
564
+ rating: BaileyFeedbackRating;
565
+ reasonCodes?: BaileyFeedbackReasonCode[];
566
+ comment?: string;
567
+ };
272
568
  }
273
569
 
274
570
  export declare interface BaileyOutputItem {
@@ -299,6 +595,24 @@ export declare interface BaileyPrivacyNote {
299
595
  alwaysVisible?: boolean;
300
596
  }
301
597
 
598
+ export declare interface BaileyServerFeedback {
599
+ rating: string;
600
+ reasonCodes: string[];
601
+ comment?: string | null;
602
+ updatedAt?: string;
603
+ }
604
+
605
+ export declare interface BaileyServerMessage {
606
+ messageId: string;
607
+ role: string;
608
+ content: string;
609
+ hidden: boolean;
610
+ createdAt: string;
611
+ feedback?: BaileyServerFeedback | null;
612
+ }
613
+
614
+ export declare type BaileyStorageOperation = 'read' | 'write' | 'remove' | 'key';
615
+
302
616
  export declare type BaileyStreamEvent = {
303
617
  type: 'response.output_text.delta';
304
618
  delta: string;
@@ -316,8 +630,19 @@ export declare type BaileyStreamEvent = {
316
630
  message?: string;
317
631
  } | {
318
632
  type: 'response.created';
633
+ response?: {
634
+ conversation?: {
635
+ id?: unknown;
636
+ } | null;
637
+ };
319
638
  } | {
320
639
  type: 'response.completed';
640
+ message_id?: string;
641
+ response?: {
642
+ conversation?: {
643
+ id?: unknown;
644
+ } | null;
645
+ };
321
646
  } | {
322
647
  type: 'error';
323
648
  message: string;
@@ -440,8 +765,38 @@ export declare interface CopyMarkdownButtonProps {
440
765
  tooltipCopiedText?: string;
441
766
  }
442
767
 
768
+ /** Creates the Bailey HTTP client: streaming chat, conversation list/read/delete and feedback.
769
+ * The host supplies `baseUrl` and `getHeaders`; the client never reads or stores tokens and never
770
+ * logs request or response bodies. Failures reject with `BaileyApiError` (fixed message, no body). */
771
+ export declare function createBaileyClient(options: BaileyClientOptions): BaileyClient;
772
+
773
+ /**
774
+ * Props for `BaileyFeedbackButtons` backed by the Bailey client. Both handlers reject (the
775
+ * buttons then revert and show their error state; nothing is retried automatically) with
776
+ * `Error('no conversation id')` when no id is held, without calling the client, and otherwise
777
+ * propagate the client's `BaileyApiError`. The comment is only ever sent in the request body: it
778
+ * is never logged and never placed in an error.
779
+ */
780
+ export declare function createBaileyFeedbackHandlers(params: {
781
+ client: BaileyClient;
782
+ /** Read at call time, so it can be `() => chat.conversationId` from a ref or latest value. */
783
+ getConversationId: () => string | undefined;
784
+ }): BaileyFeedbackHandlers;
785
+
443
786
  export declare function createBaileyMarkdownComponents({ renderTable, renderChart }?: BaileyMarkdownRenderers): Components;
444
787
 
788
+ /** In-memory storage, one instance per call; useful for tests and for hosts that want no browser storage. */
789
+ export declare function createMemoryConversationStorage(): BaileyConversationStorage;
790
+
791
+ /**
792
+ * sessionStorage-backed storage. Failures (Safari private mode, blocked cookies, quota, or
793
+ * `window.sessionStorage` itself throwing) propagate to the caller on purpose: `useBaileyChat`
794
+ * catches them and surfaces them as `persistence: 'degraded'` and `onStorageError`.
795
+ */
796
+ export declare function createSessionConversationStorage(): BaileyConversationStorage;
797
+
798
+ export declare const DEFAULT_CONVERSATION_STORAGE_KEY = "baileyai-chat-ui:conversation";
799
+
445
800
  export declare function extractPseudoToolCalls(text: string, options?: ExtractPseudoToolCallsOptions): ExtractPseudoToolCallsResult;
446
801
 
447
802
  declare interface ExtractPseudoToolCallsOptions {
@@ -465,6 +820,18 @@ export declare const extractTableData: (node: HastNode) => {
465
820
  rows: BaileyTableCell[][];
466
821
  } | null;
467
822
 
823
+ /**
824
+ * Relative time such as "2 hours ago" via Intl (no dependency). A future time (clock skew) is
825
+ * clamped to "now"; past 30 days it becomes a medium date. Returns '' for an unparseable time.
826
+ * `justNowText` is only used when Intl.RelativeTimeFormat does not exist.
827
+ */
828
+ export declare function formatRelativeTime(params: {
829
+ iso: string;
830
+ locale?: string;
831
+ now?: number;
832
+ justNowText?: string;
833
+ }): string;
834
+
468
835
  export declare function formatTraceGap(ms: number): string;
469
836
 
470
837
  export declare function formatTraceTime(at: number): string;
@@ -485,10 +852,34 @@ export declare function HeartIcon({ className }: {
485
852
  className?: string;
486
853
  }): JSX_2.Element;
487
854
 
855
+ /**
856
+ * True when `conversationId` carries the `<sub>:` prefix the server requires of the caller's ids
857
+ * (it silently replaces any other id), with a non-empty remainder. A blank `sub` never matches.
858
+ * This only validates; the library does not create conversation ids.
859
+ */
860
+ export declare function isConversationIdForSub(params: {
861
+ conversationId: string;
862
+ sub: string;
863
+ }): boolean;
864
+
488
865
  export declare const isSafeMarkdownUrl: (href: string) => boolean;
489
866
 
490
867
  export declare function joinInstructions(...parts: Array<string | undefined | null | false>): string;
491
868
 
869
+ /**
870
+ * Turns a server transcript page into chat messages, oldest-first as given. Drops rows the user
871
+ * never saw (`hidden`) and any role other than user/assistant. Never logs.
872
+ */
873
+ export declare function mapTranscript(params: {
874
+ messages: BaileyServerMessage[];
875
+ }): BaileyMessage[];
876
+
877
+ /** Prepends an earlier page to the current messages, dropping earlier rows already present. */
878
+ export declare function mergeEarlierPage(params: {
879
+ current: BaileyMessage[];
880
+ earlier: BaileyMessage[];
881
+ }): BaileyMessage[];
882
+
492
883
  export declare const parseBaileyChartSpec: (raw: string) => BaileyChartSpec | null;
493
884
 
494
885
  export declare interface ParsedSseFrames {
@@ -519,6 +910,16 @@ export declare function resolveToolCall(name: string, argsJson: string | undefin
519
910
 
520
911
  export declare const shouldUseGrid: (rows: BaileyTableCell[][]) => boolean;
521
912
 
913
+ export declare function ThumbDownIcon({ className, filled }: {
914
+ className?: string;
915
+ filled?: boolean;
916
+ }): JSX_2.Element;
917
+
918
+ export declare function ThumbUpIcon({ className, filled }: {
919
+ className?: string;
920
+ filled?: boolean;
921
+ }): JSX_2.Element;
922
+
522
923
  export declare function toActivityLines(events: BaileyTraceEvent[], turnSentAt: number | undefined): ActivityLine[];
523
924
 
524
925
  export declare interface ToolStep {
@@ -592,7 +993,7 @@ export declare type TraceSegment = {
592
993
 
593
994
  export declare function traceSummaryLine(events: BaileyTraceEvent[]): string;
594
995
 
595
- export declare const useBaileyChat: ({ transport, model, instructions, tools, toolChoice, firstUserMessagePrefix, onAssistantMessageComplete, idleTimeoutMs, }: UseBaileyChatConfig) => UseBaileyChatResult;
996
+ export declare const useBaileyChat: ({ transport, model, instructions, tools, toolChoice, firstUserMessagePrefix, onAssistantMessageComplete, idleTimeoutMs, client, conversationId: seedConversationId, sub: rawSub, storage: storageOption, storageKey, sendFullTranscript, onStorageError, onContextLoss, }: UseBaileyChatConfig) => UseBaileyChatResult;
596
997
 
597
998
  export declare interface UseBaileyChatConfig {
598
999
  transport: BaileyChatTransport;
@@ -616,9 +1017,62 @@ export declare interface UseBaileyChatConfig {
616
1017
  * hang the UI forever. Resets on every chunk. Defaults to 150s, matching Bailey's own
617
1018
  * upstream idle timeout so a well-behaved backend error has a chance to arrive first. */
618
1019
  idleTimeoutMs?: number;
1020
+ /** Opt-in. Enables `loadConversation` / `loadEarlier` (history loading through the Bailey
1021
+ * client). Streaming still goes through `transport`; pass the same client for both. */
1022
+ client?: BaileyClient;
1023
+ /** Opt-in. Seeds the held conversation id (e.g. to resume a conversation). Takes precedence
1024
+ * over an id restored from `storage`. With `sub`, a seed that does not start with `<sub>:`
1025
+ * is ignored and never sent. Once an id is held it is sent to the transport as
1026
+ * `conversationId` on every turn. */
1027
+ conversationId?: string;
1028
+ /** Opt-in. The signed-in user's subject, used ONLY to key and validate the stored id: the
1029
+ * stored value lives under `${storageKey}:${sub}` and is rejected on read (and removed) unless
1030
+ * it starts with `<sub>:`, so a different user in the same browser session never inherits the
1031
+ * previous user's id. A seed and any id reported by the transport are validated the same way.
1032
+ * The library never mints an id and never decodes a token. A blank `sub` is treated as absent.
1033
+ * Hosts should also clear the stored id on logout through the same `storage`. */
1034
+ sub?: string;
1035
+ /** Persistence for the held conversation id. On by default: sessionStorage (per tab), so a
1036
+ * reload resumes the conversation. `null` disables it. Without `sub` the stored id is held
1037
+ * unvalidated (the server rejects an id that is not the caller's and the library then adopts
1038
+ * the server-reported one); with `sub` it is keyed and validated, see `sub`. Pass your own
1039
+ * `BaileyConversationStorage` to override. A storage that throws is treated as unavailable.
1040
+ * Hosts should clear it on logout. */
1041
+ storage?: BaileyConversationStorage | false;
1042
+ /** Storage key base for the held id. Hosts with several chat surfaces append their own surface
1043
+ * name so surfaces do not share a conversation. With `sub` the key used is
1044
+ * `${storageKey}:${sub}`. Defaults to DEFAULT_CONVERSATION_STORAGE_KEY. */
1045
+ storageKey?: string;
1046
+ /** By default, once a conversation id is held only the newest message is sent in `input`
1047
+ * (the server prepends the stored history and does not deduplicate); with no id held the
1048
+ * full transcript is sent. Set to `true` to always send the full transcript and never the id
1049
+ * (stateless requests; an escape hatch, e.g. for an environment where the server does not
1050
+ * persist). The id is withheld because with one the server would prepend its stored history to
1051
+ * the full transcript. The held id is still tracked, stored and used by `loadConversation`. */
1052
+ sendFullTranscript?: boolean;
1053
+ /** Called when a storage call fails (read, write or remove on `storage`) or when `sub` is
1054
+ * provided but blank (operation `'key'`). Chat keeps working with the id in memory only and
1055
+ * `persistence` becomes `'degraded'`. Receives the underlying error; nothing is logged. */
1056
+ onStorageError?: (error: unknown, operation: BaileyStorageOperation) => void;
1057
+ /** Called when a turn that sent a held id came back with no id or a different one: the server
1058
+ * did not continue that conversation (persistence off, unknown or foreign id, or history that
1059
+ * could not be loaded). The held id and its stored value are dropped, so the next turn sends
1060
+ * the full transcript. */
1061
+ onContextLoss?: (info: BaileyContextLossInfo) => void;
619
1062
  }
620
1063
 
621
1064
  export declare interface UseBaileyChatResult {
1065
+ /** `'ok'`: the held id is persisted. `'off'`: the host passed `storage: false`. `'degraded'`: a
1066
+ * storage call failed or `sub` was blank, so the id lives in memory only. Optional in the type
1067
+ * so hosts that construct their own result objects still compile. */
1068
+ persistence?: 'ok' | 'degraded' | 'off';
1069
+ /** True from a turn whose reported id was missing or different from the one sent (see
1070
+ * `onContextLoss`) until the next turn starts or the conversation is replaced. */
1071
+ contextLost?: boolean;
1072
+ /** Read-only. The held conversation id (seeded, restored from `storage`, or reported by the
1073
+ * transport via `onConversationId`); `undefined` until one exists and after `newChat()`.
1074
+ * Optional in the type so hosts that construct their own result objects still compile. */
1075
+ conversationId?: string;
622
1076
  messages: BaileyMessage[];
623
1077
  traceEvents: BaileyTraceEvent[];
624
1078
  lastRequest: BaileyLastRequest | null;
@@ -669,6 +1123,45 @@ export declare interface UseBaileyChatResult {
669
1123
  * whose `role` isn't `'user'` or `'assistant'` is dropped rather than trusted, since a
670
1124
  * restored transcript crosses a service boundary this hook doesn't control. */
671
1125
  loadMessages: (messages: BaileyMessage[]) => void;
1126
+ /** Loads the newest page of a stored conversation (requires `client`), replaces the messages,
1127
+ * and holds `conversationId` (so later turns send only the newest message). With `sub`, an id
1128
+ * without the `<sub>:` prefix rejects with `Error('invalid conversation id')` and no request
1129
+ * is made. Rejects with the client's `BaileyApiError` on failure (messages are left intact and
1130
+ * `historyStatus` is `'error'`). A response that arrives after `newChat()`, `loadMessages` or
1131
+ * another `loadConversation` is discarded. Optional in the type so hosts that construct their
1132
+ * own result objects still compile; the hook always provides it. */
1133
+ loadConversation?: (params: {
1134
+ conversationId: string;
1135
+ }) => Promise<void>;
1136
+ /** Loads the next-older page of the held conversation and prepends it (deduplicated). No-op
1137
+ * when `hasEarlier` is false. Rejects like `loadConversation`. */
1138
+ loadEarlier?: () => Promise<void>;
1139
+ /** True while an older page exists for the held conversation. */
1140
+ hasEarlier?: boolean;
1141
+ /** State of the most recent history load. */
1142
+ historyStatus?: 'idle' | 'loading' | 'error';
1143
+ }
1144
+
1145
+ /** Lists the caller's conversations through `client` (loading, error, refresh, load more, delete with
1146
+ * rollback on failure). Nothing is fetched while `enabled` is false. Titles are PHI-capable and are
1147
+ * never logged. */
1148
+ export declare function useBaileyConversations(params: {
1149
+ client: BaileyClient;
1150
+ pageSize?: number;
1151
+ enabled?: boolean;
1152
+ }): UseBaileyConversationsResult;
1153
+
1154
+ export declare interface UseBaileyConversationsResult {
1155
+ conversations: BaileyConversationSummary[];
1156
+ status: 'idle' | 'loading' | 'error';
1157
+ error: BaileyApiError | null;
1158
+ hasMore: boolean;
1159
+ refresh: () => Promise<void>;
1160
+ loadMore: () => Promise<void>;
1161
+ /** Optimistic remove; restores the row and rethrows if the delete fails. */
1162
+ remove: (params: {
1163
+ conversationId: string;
1164
+ }) => Promise<void>;
672
1165
  }
673
1166
 
674
1167
  export { }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icanbwell/baileyai-chat-ui",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "Shared React chat UI and streaming logic for Bailey, the icanbwell AI assistant",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {