@icanbwell/baileyai-chat-ui 1.0.16 → 1.0.18

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
@@ -189,13 +229,104 @@ export declare interface BaileyChatTransport {
189
229
  signal: AbortSignal;
190
230
  onChunk: (text: string) => void;
191
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;
192
240
  }): Promise<{
193
241
  status?: number;
194
242
  text: string;
195
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;
249
+ /** Optional. Why a request that never got a response failed. Set by `createBaileyClient`
250
+ * together with `status: undefined` when `fetch` rejects or the host's `getHeaders`
251
+ * throws (`'network'`); the type is a subset of `BaileyApiErrorKind`. `'timeout'` is reserved
252
+ * for a transport that resolves instead of throwing on a timeout: `createBaileyClient`
253
+ * still rethrows its idle-timeout `TimeoutError`. `errorMessage` stays a fixed string.
254
+ * Hosts that ignore this field behave exactly as before. */
255
+ errorKind?: Extract<BaileyApiErrorKind, 'network' | 'timeout'>;
256
+ /** Optional. The error `fetch` threw, set only with `errorKind` when the failure came
257
+ * from `fetch`; never set when `getHeaders` threw (it can carry auth detail) and never
258
+ * a request or response body. Browsers do not distinguish CORS, DNS or offline: all
259
+ * surface as a generic `TypeError`. Do not show `cause.message` to users without review,
260
+ * and do not log the request URL (it can carry identifiers). */
261
+ errorCause?: unknown;
196
262
  }>;
197
263
  }
198
264
 
265
+ /**
266
+ * A best-effort cleanup step (e.g. cancelling an unread response body) failed. Reported through
267
+ * the client's `onCleanupError` option and never thrown to the caller. The message is fixed and
268
+ * carries no response data.
269
+ */
270
+ export declare class BaileyCleanupError extends Error {
271
+ readonly operation: 'cancel-response-body' | 'cancel-stream-reader';
272
+ constructor(params: {
273
+ operation: BaileyCleanupError['operation'];
274
+ });
275
+ }
276
+
277
+ export declare interface BaileyClient extends BaileyChatTransport {
278
+ listConversations(params?: {
279
+ limit?: number;
280
+ cursor?: string;
281
+ signal?: AbortSignal;
282
+ }): Promise<BaileyConversationList>;
283
+ getConversation(params: {
284
+ conversationId: string;
285
+ limit?: number;
286
+ beforeAbsoluteIndex?: number;
287
+ signal?: AbortSignal;
288
+ }): Promise<BaileyConversationPage>;
289
+ deleteConversation(params: {
290
+ conversationId: string;
291
+ signal?: AbortSignal;
292
+ }): Promise<void>;
293
+ submitFeedback(params: {
294
+ conversationId: string;
295
+ messageId: string;
296
+ rating: 'up' | 'down';
297
+ reasonCodes?: string[];
298
+ comment?: string;
299
+ signal?: AbortSignal;
300
+ }): Promise<void>;
301
+ clearFeedback(params: {
302
+ conversationId: string;
303
+ messageId: string;
304
+ signal?: AbortSignal;
305
+ }): Promise<void>;
306
+ }
307
+
308
+ export declare interface BaileyClientOptions {
309
+ /** Bailey API root. Expected to include the `/bailey/v1` prefix; a trailing slash is tolerated. */
310
+ baseUrl: string;
311
+ /** Called before every request. The library never reads or stores tokens itself. */
312
+ getHeaders: () => Promise<Record<string, string>>;
313
+ fetch?: typeof fetch;
314
+ /** Fired once on a 401/403 response, before the call rejects (or the stream call returns). */
315
+ onAuthError?: (error: BaileyApiError) => void;
316
+ /** Called when a best-effort cleanup step fails (e.g. cancelling an unread response body).
317
+ * Never thrown to the caller; an exception thrown by this callback is also contained. */
318
+ onCleanupError?: (error: BaileyCleanupError) => void;
319
+ /** Timeout for non-stream calls. Default 30_000. */
320
+ requestTimeoutMs?: number;
321
+ /** Max gap between stream chunks before the stream is aborted. Default 155_000 (a backstop just above `useBaileyChat`'s own 150s idle timeout). */
322
+ idleTimeoutMs?: number;
323
+ /** How `streamChat` sends a held conversation id: the legacy `x-chat-id` request header
324
+ * (default) or the `conversation` field in the JSON body. Never both. Keep `'header'` until
325
+ * the backend release that accepts the body field is deployed wherever the host runs;
326
+ * an older backend rejects the unknown field with HTTP 400. */
327
+ conversationVia?: 'header' | 'body';
328
+ }
329
+
199
330
  export declare type BaileyContentPart = {
200
331
  type: 'text';
201
332
  text: string;
@@ -214,6 +345,178 @@ export declare type BaileyContentPart = {
214
345
  displayMode?: string;
215
346
  };
216
347
 
348
+ export declare interface BaileyContextLossInfo {
349
+ /** The id the turn sent. */
350
+ sentId: string;
351
+ /** The id the server reported instead; undefined when it reported none. For
352
+ * `reason: 'history'` it is the id the server reported, normally equal to `sentId`. */
353
+ reportedId?: string;
354
+ /** `'id'`: the server reported no id or a different one. `'history'`: the server reported the
355
+ * same id but said it loaded no history (`conversation.history_status` of `'failed'` or
356
+ * `'empty'`). Only newer backends send the status; older ones can only produce `'id'`. The
357
+ * accompanying `history_loaded` count is not used by the library. Optional in the type so hosts that
358
+ * construct this object keep compiling; the library always sets it on what it passes to
359
+ * `onContextLoss`. */
360
+ reason?: 'id' | 'history';
361
+ }
362
+
363
+ export declare interface BaileyConversationList {
364
+ conversations: BaileyConversationSummary[];
365
+ nextCursor: string | null;
366
+ }
367
+
368
+ export declare interface BaileyConversationPage {
369
+ conversation: BaileyConversationSummary;
370
+ messages: BaileyServerMessage[];
371
+ hasMoreBefore: boolean;
372
+ nextBeforeAbsoluteIndex: number | null;
373
+ }
374
+
375
+ /** Pluggable persistence for the held conversation id. `useBaileyChat` guards every call: an
376
+ * implementation that throws is reported through `onStorageError` and the id stays in memory. */
377
+ export declare interface BaileyConversationStorage {
378
+ get(key: string): string | null;
379
+ set(key: string, value: string): void;
380
+ remove(key: string): void;
381
+ }
382
+
383
+ export declare interface BaileyConversationSummary {
384
+ conversationId: string;
385
+ title: string | null;
386
+ status: string;
387
+ messageCount: number;
388
+ createdAt: string;
389
+ updatedAt: string;
390
+ }
391
+
392
+ export declare function BaileyFeedbackButtons(props: BaileyFeedbackButtonsProps): JSX_2.Element | null;
393
+
394
+ export declare interface BaileyFeedbackButtonsProps {
395
+ /** The backend's assistant message ID (`BaileyMessage.serverMessageId`). Renders nothing when empty. */
396
+ messageId: string;
397
+ /** Create or replace the rating. Any rejection (including a 404) means failure: the selection
398
+ * reverts and an error is shown. Nothing is retried automatically; the user retries by hand. */
399
+ onSubmit: (submission: BaileyFeedbackSubmission) => Promise<void>;
400
+ /** Remove the rating and comment. Same rejection contract as `onSubmit`. */
401
+ onClear: (params: {
402
+ messageId: string;
403
+ }) => Promise<void>;
404
+ /** A previously submitted rating to show as selected. */
405
+ initialRating?: BaileyFeedbackRating;
406
+ /** Subset (and order) of the backend reason codes to offer. Defaults to all five. */
407
+ reasonOptions?: BaileyFeedbackReasonCode[];
408
+ /** Comment length limit; defaults to 2000 and can only be lowered. */
409
+ maxCommentLength?: number;
410
+ labels?: Partial<BaileyFeedbackLabels>;
411
+ className?: string;
412
+ }
413
+
414
+ export declare interface BaileyFeedbackHandlers {
415
+ onSubmit: (submission: BaileyFeedbackSubmission) => Promise<void>;
416
+ onClear: (params: {
417
+ messageId: string;
418
+ }) => Promise<void>;
419
+ }
420
+
421
+ export declare interface BaileyFeedbackLabels {
422
+ feedbackGroupLabel: string;
423
+ feedbackThumbsUpAriaLabel: string;
424
+ feedbackThumbsDownAriaLabel: string;
425
+ feedbackReasonGroupLabel: string;
426
+ feedbackCommentLabel: string;
427
+ feedbackCommentPlaceholder: string;
428
+ feedbackSubmitText: string;
429
+ feedbackCancelText: string;
430
+ feedbackThanksText: string;
431
+ feedbackUndoText: string;
432
+ feedbackEditText: string;
433
+ feedbackAddCommentText: string;
434
+ feedbackSavingText: string;
435
+ feedbackErrorText: string;
436
+ feedbackPrivacyNote: string;
437
+ feedbackReasonInaccurate: string;
438
+ feedbackReasonUnhelpful: string;
439
+ feedbackReasonUnsafe: string;
440
+ feedbackReasonTooLong: string;
441
+ feedbackReasonOther: string;
442
+ }
443
+
444
+ export declare type BaileyFeedbackRating = 'up' | 'down';
445
+
446
+ /** The backend's reason-code enum. A host may select a subset, never add a code: the backend
447
+ * rejects unknown codes with 422, which would fail the whole submit. */
448
+ export declare type BaileyFeedbackReasonCode = 'inaccurate' | 'unhelpful' | 'unsafe' | 'too_long' | 'other';
449
+
450
+ export declare interface BaileyFeedbackSubmission {
451
+ messageId: string;
452
+ rating: BaileyFeedbackRating;
453
+ /** Omitted when no reason is selected. */
454
+ reasonCodes?: BaileyFeedbackReasonCode[];
455
+ /** Omitted when blank. PHI-capable: only ever passed to `onSubmit`. */
456
+ comment?: string;
457
+ }
458
+
459
+ /** Owns the conversations list (so nothing is fetched until the drawer is mounted) and wires the
460
+ * chat actions into the presentational view. */
461
+ export declare function BaileyHistoryDrawer({ client, pageSize, chat, labels, locale, onClose }: BaileyHistoryDrawerProps): JSX_2.Element;
462
+
463
+ export declare interface BaileyHistoryDrawerProps {
464
+ client: BaileyClient;
465
+ pageSize?: number;
466
+ chat: Pick<UseBaileyChatResult, 'conversationId' | 'loadConversation' | 'newChat' | 'historyStatus'>;
467
+ /** Already merged with the defaults by the panel. */
468
+ labels: BaileyHistoryLabels;
469
+ locale?: string;
470
+ onClose: (params: {
471
+ restoreFocusTo: BaileyHistoryFocusTarget;
472
+ }) => void;
473
+ }
474
+
475
+ declare type BaileyHistoryFocusTarget = 'menu' | 'input';
476
+
477
+ export declare interface BaileyHistoryLabels {
478
+ /** Reused from the panel's menu: also the drawer's "New chat" control. */
479
+ newChatButtonText: string;
480
+ /** Reused from the panel's error banner: the drawer's Retry controls. */
481
+ retryButtonText: string;
482
+ historyTitle: string;
483
+ historyBackAriaLabel: string;
484
+ historyLoadingText: string;
485
+ historyLoadingMoreText: string;
486
+ historyLoadMoreText: string;
487
+ historyLoadMoreErrorText: string;
488
+ /** `{count}` is the number of conversations shown. */
489
+ historyCountTextOne: string;
490
+ historyCountTextMany: string;
491
+ historyEmptyTitle: string;
492
+ historyEmptyDescription: string;
493
+ historyEmptyCtaText: string;
494
+ historyErrorTitle: string;
495
+ historyErrorDescription: string;
496
+ /** Shown instead of the generic error when the list call fails with a 401/403. The English
497
+ * wording is a default for copy review. */
498
+ historySessionExpiredTitle: string;
499
+ historySessionExpiredDescription: string;
500
+ /** Shown when opening the chosen conversation failed. */
501
+ historySelectErrorText: string;
502
+ historyUntitledText: string;
503
+ historyCurrentText: string;
504
+ historyMessageCountOne: string;
505
+ historyMessageCountMany: string;
506
+ /** `{title}` is the conversation title (user-facing only; never logged). */
507
+ historyDeleteAriaLabel: string;
508
+ historyDeleteConfirmQuestion: string;
509
+ historyDeleteConfirmText: string;
510
+ historyDeleteCancelText: string;
511
+ historyDeletingText: string;
512
+ historyDeletedText: string;
513
+ historyDeleteFailedText: string;
514
+ historyDeleteRetryText: string;
515
+ historyDeleteDismissText: string;
516
+ /** Only used when `Intl.RelativeTimeFormat` is unavailable. */
517
+ historyJustNowText: string;
518
+ }
519
+
217
520
  export declare interface BaileyLastRequest {
218
521
  model: string;
219
522
  systemPrompt: string;
@@ -270,6 +573,19 @@ export declare interface BaileyMessage {
270
573
  * never reads or interprets it — it exists purely as a pass-through so a transport/backend
271
574
  * can use it as a conversation-title fallback for a turn whose only content is hidden. */
272
575
  titleHint?: string;
576
+ /** The backend's own ID for this assistant message, from `response.completed.message_id` (or
577
+ * the transport's `onMessageId`). Distinct from `id`, which is minted client-side. A host
578
+ * needs it to address the message in server APIs such as feedback; undefined on user
579
+ * messages and when the backend doesn't report one (older backends). */
580
+ serverMessageId?: string;
581
+ /** The rating the user previously gave this assistant message, when the backend reports one
582
+ * for a loaded conversation. Feeds `BaileyFeedbackButtons`' `initialRating`. PHI-capable
583
+ * (`comment`): never logged or placed in trace events. */
584
+ feedback?: {
585
+ rating: BaileyFeedbackRating;
586
+ reasonCodes?: BaileyFeedbackReasonCode[];
587
+ comment?: string;
588
+ };
273
589
  }
274
590
 
275
591
  export declare interface BaileyOutputItem {
@@ -300,6 +616,32 @@ export declare interface BaileyPrivacyNote {
300
616
  alwaysVisible?: boolean;
301
617
  }
302
618
 
619
+ export declare interface BaileyServerFeedback {
620
+ rating: string;
621
+ reasonCodes: string[];
622
+ comment?: string | null;
623
+ updatedAt?: string;
624
+ }
625
+
626
+ export declare interface BaileyServerMessage {
627
+ messageId: string;
628
+ role: string;
629
+ content: string;
630
+ hidden: boolean;
631
+ createdAt: string;
632
+ feedback?: BaileyServerFeedback | null;
633
+ }
634
+
635
+ export declare type BaileyStorageOperation = 'read' | 'write' | 'remove' | 'key';
636
+
637
+ /** The `conversation` object on `response.created` / `response.completed`. Every member is optional
638
+ * and `unknown`: older backends omit them and the library ignores values it does not recognise. */
639
+ export declare interface BaileyStreamConversation {
640
+ id?: unknown;
641
+ history_status?: unknown;
642
+ history_loaded?: unknown;
643
+ }
644
+
303
645
  export declare type BaileyStreamEvent = {
304
646
  type: 'response.output_text.delta';
305
647
  delta: string;
@@ -317,8 +659,15 @@ export declare type BaileyStreamEvent = {
317
659
  message?: string;
318
660
  } | {
319
661
  type: 'response.created';
662
+ response?: {
663
+ conversation?: BaileyStreamConversation | null;
664
+ };
320
665
  } | {
321
666
  type: 'response.completed';
667
+ message_id?: string;
668
+ response?: {
669
+ conversation?: BaileyStreamConversation | null;
670
+ };
322
671
  } | {
323
672
  type: 'error';
324
673
  message: string;
@@ -441,8 +790,38 @@ export declare interface CopyMarkdownButtonProps {
441
790
  tooltipCopiedText?: string;
442
791
  }
443
792
 
793
+ /** Creates the Bailey HTTP client: streaming chat, conversation list/read/delete and feedback.
794
+ * The host supplies `baseUrl` and `getHeaders`; the client never reads or stores tokens and never
795
+ * logs request or response bodies. Failures reject with `BaileyApiError` (fixed message, no body). */
796
+ export declare function createBaileyClient(options: BaileyClientOptions): BaileyClient;
797
+
798
+ /**
799
+ * Props for `BaileyFeedbackButtons` backed by the Bailey client. Both handlers reject (the
800
+ * buttons then revert and show their error state; nothing is retried automatically) with
801
+ * `Error('no conversation id')` when no id is held, without calling the client, and otherwise
802
+ * propagate the client's `BaileyApiError`. The comment is only ever sent in the request body: it
803
+ * is never logged and never placed in an error.
804
+ */
805
+ export declare function createBaileyFeedbackHandlers(params: {
806
+ client: BaileyClient;
807
+ /** Read at call time, so it can be `() => chat.conversationId` from a ref or latest value. */
808
+ getConversationId: () => string | undefined;
809
+ }): BaileyFeedbackHandlers;
810
+
444
811
  export declare function createBaileyMarkdownComponents({ renderTable, renderChart }?: BaileyMarkdownRenderers): Components;
445
812
 
813
+ /** In-memory storage, one instance per call; useful for tests and for hosts that want no browser storage. */
814
+ export declare function createMemoryConversationStorage(): BaileyConversationStorage;
815
+
816
+ /**
817
+ * sessionStorage-backed storage. Failures (Safari private mode, blocked cookies, quota, or
818
+ * `window.sessionStorage` itself throwing) propagate to the caller on purpose: `useBaileyChat`
819
+ * catches them and surfaces them as `persistence: 'degraded'` and `onStorageError`.
820
+ */
821
+ export declare function createSessionConversationStorage(): BaileyConversationStorage;
822
+
823
+ export declare const DEFAULT_CONVERSATION_STORAGE_KEY = "baileyai-chat-ui:conversation";
824
+
446
825
  export declare function extractPseudoToolCalls(text: string, options?: ExtractPseudoToolCallsOptions): ExtractPseudoToolCallsResult;
447
826
 
448
827
  declare interface ExtractPseudoToolCallsOptions {
@@ -466,6 +845,18 @@ export declare const extractTableData: (node: HastNode) => {
466
845
  rows: BaileyTableCell[][];
467
846
  } | null;
468
847
 
848
+ /**
849
+ * Relative time such as "2 hours ago" via Intl (no dependency). A future time (clock skew) is
850
+ * clamped to "now"; past 30 days it becomes a medium date. Returns '' for an unparseable time.
851
+ * `justNowText` is only used when Intl.RelativeTimeFormat does not exist.
852
+ */
853
+ export declare function formatRelativeTime(params: {
854
+ iso: string;
855
+ locale?: string;
856
+ now?: number;
857
+ justNowText?: string;
858
+ }): string;
859
+
469
860
  export declare function formatTraceGap(ms: number): string;
470
861
 
471
862
  export declare function formatTraceTime(at: number): string;
@@ -486,10 +877,34 @@ export declare function HeartIcon({ className }: {
486
877
  className?: string;
487
878
  }): JSX_2.Element;
488
879
 
880
+ /**
881
+ * True when `conversationId` carries the `<sub>:` prefix the server requires of the caller's ids
882
+ * (it silently replaces any other id), with a non-empty remainder. A blank `sub` never matches.
883
+ * This only validates; the library does not create conversation ids.
884
+ */
885
+ export declare function isConversationIdForSub(params: {
886
+ conversationId: string;
887
+ sub: string;
888
+ }): boolean;
889
+
489
890
  export declare const isSafeMarkdownUrl: (href: string) => boolean;
490
891
 
491
892
  export declare function joinInstructions(...parts: Array<string | undefined | null | false>): string;
492
893
 
894
+ /**
895
+ * Turns a server transcript page into chat messages, oldest-first as given. Drops rows the user
896
+ * never saw (`hidden`) and any role other than user/assistant. Never logs.
897
+ */
898
+ export declare function mapTranscript(params: {
899
+ messages: BaileyServerMessage[];
900
+ }): BaileyMessage[];
901
+
902
+ /** Prepends an earlier page to the current messages, dropping earlier rows already present. */
903
+ export declare function mergeEarlierPage(params: {
904
+ current: BaileyMessage[];
905
+ earlier: BaileyMessage[];
906
+ }): BaileyMessage[];
907
+
493
908
  export declare const parseBaileyChartSpec: (raw: string) => BaileyChartSpec | null;
494
909
 
495
910
  export declare interface ParsedSseFrames {
@@ -520,6 +935,16 @@ export declare function resolveToolCall(name: string, argsJson: string | undefin
520
935
 
521
936
  export declare const shouldUseGrid: (rows: BaileyTableCell[][]) => boolean;
522
937
 
938
+ export declare function ThumbDownIcon({ className, filled }: {
939
+ className?: string;
940
+ filled?: boolean;
941
+ }): JSX_2.Element;
942
+
943
+ export declare function ThumbUpIcon({ className, filled }: {
944
+ className?: string;
945
+ filled?: boolean;
946
+ }): JSX_2.Element;
947
+
523
948
  export declare function toActivityLines(events: BaileyTraceEvent[], turnSentAt: number | undefined): ActivityLine[];
524
949
 
525
950
  export declare interface ToolStep {
@@ -593,7 +1018,7 @@ export declare type TraceSegment = {
593
1018
 
594
1019
  export declare function traceSummaryLine(events: BaileyTraceEvent[]): string;
595
1020
 
596
- export declare const useBaileyChat: ({ transport, model, instructions, tools, toolChoice, firstUserMessagePrefix, onAssistantMessageComplete, idleTimeoutMs, }: UseBaileyChatConfig) => UseBaileyChatResult;
1021
+ 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;
597
1022
 
598
1023
  export declare interface UseBaileyChatConfig {
599
1024
  transport: BaileyChatTransport;
@@ -617,9 +1042,64 @@ export declare interface UseBaileyChatConfig {
617
1042
  * hang the UI forever. Resets on every chunk. Defaults to 150s, matching Bailey's own
618
1043
  * upstream idle timeout so a well-behaved backend error has a chance to arrive first. */
619
1044
  idleTimeoutMs?: number;
1045
+ /** Opt-in. Enables `loadConversation` / `loadEarlier` (history loading through the Bailey
1046
+ * client). Streaming still goes through `transport`; pass the same client for both. */
1047
+ client?: BaileyClient;
1048
+ /** Opt-in. Seeds the held conversation id (e.g. to resume a conversation). Takes precedence
1049
+ * over an id restored from `storage`. With `sub`, a seed that does not start with `<sub>:`
1050
+ * is ignored and never sent. Once an id is held it is sent to the transport as
1051
+ * `conversationId` on every turn. */
1052
+ conversationId?: string;
1053
+ /** Opt-in. The signed-in user's subject, used ONLY to key and validate the stored id: the
1054
+ * stored value lives under `${storageKey}:${sub}` and is rejected on read (and removed) unless
1055
+ * it starts with `<sub>:`, so a different user in the same browser session never inherits the
1056
+ * previous user's id. A seed and any id reported by the transport are validated the same way.
1057
+ * The library never mints an id and never decodes a token. A blank `sub` is treated as absent.
1058
+ * Hosts should also clear the stored id on logout through the same `storage`. */
1059
+ sub?: string;
1060
+ /** Persistence for the held conversation id. On by default: sessionStorage (per tab), so a
1061
+ * reload resumes the conversation. `null` disables it. Without `sub` the stored id is held
1062
+ * unvalidated (the server rejects an id that is not the caller's and the library then adopts
1063
+ * the server-reported one); with `sub` it is keyed and validated, see `sub`. Pass your own
1064
+ * `BaileyConversationStorage` to override. A storage that throws is treated as unavailable.
1065
+ * Hosts should clear it on logout. */
1066
+ storage?: BaileyConversationStorage | false;
1067
+ /** Storage key base for the held id. Hosts with several chat surfaces append their own surface
1068
+ * name so surfaces do not share a conversation. With `sub` the key used is
1069
+ * `${storageKey}:${sub}`. Defaults to DEFAULT_CONVERSATION_STORAGE_KEY. */
1070
+ storageKey?: string;
1071
+ /** By default, once a conversation id is held only the newest message is sent in `input`
1072
+ * (the server prepends the stored history and does not deduplicate); with no id held the
1073
+ * full transcript is sent. Set to `true` to always send the full transcript and never the id
1074
+ * (stateless requests; an escape hatch, e.g. for an environment where the server does not
1075
+ * persist). The id is withheld because with one the server would prepend its stored history to
1076
+ * the full transcript. The held id is still tracked, stored and used by `loadConversation`. */
1077
+ sendFullTranscript?: boolean;
1078
+ /** Called when a storage call fails (read, write or remove on `storage`) or when `sub` is
1079
+ * provided but blank (operation `'key'`). Chat keeps working with the id in memory only and
1080
+ * `persistence` becomes `'degraded'`. Receives the underlying error; nothing is logged. */
1081
+ onStorageError?: (error: unknown, operation: BaileyStorageOperation) => void;
1082
+ /** Called when a turn that sent a held id came back with no id or a different one
1083
+ * (`reason: 'id'`), or with the same id but `conversation.history_status` of `'failed'` or
1084
+ * `'empty'` (`reason: 'history'`, newer backends only): the server did not continue that
1085
+ * conversation (persistence off, unknown or foreign id, or history that could not be loaded).
1086
+ * The held id and its stored value are dropped, so the next turn sends
1087
+ * the full transcript. */
1088
+ onContextLoss?: (info: BaileyContextLossInfo) => void;
620
1089
  }
621
1090
 
622
1091
  export declare interface UseBaileyChatResult {
1092
+ /** `'ok'`: the held id is persisted. `'off'`: the host passed `storage: false`. `'degraded'`: a
1093
+ * storage call failed or `sub` was blank, so the id lives in memory only. Optional in the type
1094
+ * so hosts that construct their own result objects still compile. */
1095
+ persistence?: 'ok' | 'degraded' | 'off';
1096
+ /** True from a turn whose reported id was missing or different from the one sent, or whose
1097
+ * history load failed or was empty (see `onContextLoss`) until the next turn starts or the conversation is replaced. */
1098
+ contextLost?: boolean;
1099
+ /** Read-only. The held conversation id (seeded, restored from `storage`, or reported by the
1100
+ * transport via `onConversationId`); `undefined` until one exists and after `newChat()`.
1101
+ * Optional in the type so hosts that construct their own result objects still compile. */
1102
+ conversationId?: string;
623
1103
  messages: BaileyMessage[];
624
1104
  traceEvents: BaileyTraceEvent[];
625
1105
  lastRequest: BaileyLastRequest | null;
@@ -670,6 +1150,45 @@ export declare interface UseBaileyChatResult {
670
1150
  * whose `role` isn't `'user'` or `'assistant'` is dropped rather than trusted, since a
671
1151
  * restored transcript crosses a service boundary this hook doesn't control. */
672
1152
  loadMessages: (messages: BaileyMessage[]) => void;
1153
+ /** Loads the newest page of a stored conversation (requires `client`), replaces the messages,
1154
+ * and holds `conversationId` (so later turns send only the newest message). With `sub`, an id
1155
+ * without the `<sub>:` prefix rejects with `Error('invalid conversation id')` and no request
1156
+ * is made. Rejects with the client's `BaileyApiError` on failure (messages are left intact and
1157
+ * `historyStatus` is `'error'`). A response that arrives after `newChat()`, `loadMessages` or
1158
+ * another `loadConversation` is discarded. Optional in the type so hosts that construct their
1159
+ * own result objects still compile; the hook always provides it. */
1160
+ loadConversation?: (params: {
1161
+ conversationId: string;
1162
+ }) => Promise<void>;
1163
+ /** Loads the next-older page of the held conversation and prepends it (deduplicated). No-op
1164
+ * when `hasEarlier` is false. Rejects like `loadConversation`. */
1165
+ loadEarlier?: () => Promise<void>;
1166
+ /** True while an older page exists for the held conversation. */
1167
+ hasEarlier?: boolean;
1168
+ /** State of the most recent history load. */
1169
+ historyStatus?: 'idle' | 'loading' | 'error';
1170
+ }
1171
+
1172
+ /** Lists the caller's conversations through `client` (loading, error, refresh, load more, delete with
1173
+ * rollback on failure). Nothing is fetched while `enabled` is false. Titles are PHI-capable and are
1174
+ * never logged. */
1175
+ export declare function useBaileyConversations(params: {
1176
+ client: BaileyClient;
1177
+ pageSize?: number;
1178
+ enabled?: boolean;
1179
+ }): UseBaileyConversationsResult;
1180
+
1181
+ export declare interface UseBaileyConversationsResult {
1182
+ conversations: BaileyConversationSummary[];
1183
+ status: 'idle' | 'loading' | 'error';
1184
+ error: BaileyApiError | null;
1185
+ hasMore: boolean;
1186
+ refresh: () => Promise<void>;
1187
+ loadMore: () => Promise<void>;
1188
+ /** Optimistic remove; restores the row and rethrows if the delete fails. */
1189
+ remove: (params: {
1190
+ conversationId: string;
1191
+ }) => Promise<void>;
673
1192
  }
674
1193
 
675
1194
  export { }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@icanbwell/baileyai-chat-ui",
3
- "version": "1.0.16",
3
+ "version": "1.0.18",
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": {