@epam/ai-dial-chat-hooks 1.2.0-dev.99 → 1.3.0-dev.5

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.
Files changed (84) hide show
  1. package/api-error/api-error.js +7 -4
  2. package/catalog/catalog-derivations.js +1 -1
  3. package/catalog/deployment-id.js +8 -2
  4. package/catalog/map-deployment-limits-to-catalog.js +16 -24
  5. package/catalog/map-deployment-limits-to-input.js +81 -9
  6. package/catalog/map-deployment-to-catalog-item.js +34 -34
  7. package/catalog/map-entity-details-to-catalog.js +237 -192
  8. package/catalog/map-skill-to-catalog-item.js +61 -54
  9. package/catalog/publish.js +4 -4
  10. package/catalog/useCatalogEditNavigation/useCatalogEditNavigation.js +122 -101
  11. package/catalog/useCatalogItemDetails.js +9 -8
  12. package/catalog/usePublishFolders/usePublishFolders.js +1 -1
  13. package/catalog.d.ts +171 -34
  14. package/catalog.js +23 -23
  15. package/conversation/conversation-transfer/export-conversation.js +24 -16
  16. package/conversation/conversation-transfer/import-conversation.js +80 -31
  17. package/conversation/create-chat-stream-api.js +89 -49
  18. package/conversation/get-model-id-from-conversation-id.js +15 -9
  19. package/conversation/message-utils.js +44 -2
  20. package/conversation/stage.js +6 -2
  21. package/conversation/useActiveConversationSync/useActiveConversationSync.js +7 -8
  22. package/conversation/useConversationExport/useConversationExport.js +9 -9
  23. package/conversation/useConversationHandlers/message-utils.js +8 -5
  24. package/conversation/useConversationHandlers/useConversationHandlers.js +181 -167
  25. package/conversation/useConversationImport/useConversationImport.js +123 -121
  26. package/conversation/useConversationPanelItems/useConversationPanelItems.js +49 -31
  27. package/conversation/useConversationStream/frame-scheduler.js +39 -0
  28. package/conversation/useConversationStream/generation-resume.js +122 -64
  29. package/conversation/useConversationStream/useConversationStream.js +298 -118
  30. package/conversation/useMessageSelectionReply/useMessageSelectionReply.js +189 -0
  31. package/conversation-sources/useConversationSources/useConversationSources.js +31 -24
  32. package/conversation-sources.d.ts +1 -1
  33. package/conversation-transfer.d.ts +2 -1
  34. package/conversation.d.ts +218 -22
  35. package/conversation.js +32 -31
  36. package/file-manager-canvas.d.ts +27 -7
  37. package/file-manager.d.ts +87 -32
  38. package/file-manager.js +25 -24
  39. package/files/attachment-canvas.js +67 -44
  40. package/files/dial-file-manager-copy-move.util.js +9 -8
  41. package/files/dial-file-manager-mapping.util.js +2 -1
  42. package/files/dial-file-manager-path.util.js +24 -2
  43. package/files/dial-file-manager.model.js +23 -19
  44. package/files/dial-file-manager.types.js +3 -3
  45. package/files/file-manager-variant.js +1 -1
  46. package/files/resolve-dial-file-api-path.js +11 -12
  47. package/files/useDialFileListing/useDialFileListing.js +140 -127
  48. package/files/useDialFileManager/useDialFileManager.js +139 -135
  49. package/files/useDialFileManagerSections/useDialFileManagerSections.js +155 -0
  50. package/files/useDialFileManagerTabConfig/useDialFileManagerTabConfig.js +20 -15
  51. package/files/useDialFileMutations/useDialFileMutations.js +65 -60
  52. package/files/useDialFileUploadBatch/useDialFileUploadBatch.js +117 -110
  53. package/files/useFileAttachmentPicker/useFileAttachmentPicker.js +28 -23
  54. package/index.d.ts +643 -128
  55. package/index.js +136 -134
  56. package/mcp-apps/useMcpAppTools/useMcpAppTools.js +1 -1
  57. package/oauth/authorize-url.js +3 -3
  58. package/oauth/toolset-id.js +5 -3
  59. package/oauth/useToolsetLogin/useToolsetLogin.js +8 -8
  60. package/oauth.d.ts +34 -2
  61. package/oauth.js +9 -9
  62. package/package.json +19 -23
  63. package/scheduled-task/scheduled-task-description.js +43 -39
  64. package/scheduled-task/scheduled-task-trigger.js +74 -71
  65. package/scheduled-task/use-scheduled-task-runs.js +162 -50
  66. package/scheduled-task/use-scheduled-tasks.js +52 -48
  67. package/scheduled-tasks.d.ts +27 -6
  68. package/scheduled-tasks.js +2 -2
  69. package/shared/application-schema.js +5 -2
  70. package/shared/cron-day-of-month.js +13 -0
  71. package/skill/skill-archive.js +19 -0
  72. package/skill/skill-types.js +8 -7
  73. package/skill/skill.js +52 -29
  74. package/skill/useSkillArchiveImport/useSkillArchiveImport.js +38 -34
  75. package/skill/useSkillEditorLoad.js +65 -59
  76. package/skill/useSkillEditorSubmit.js +105 -93
  77. package/skill/useSkillFileActions.js +70 -58
  78. package/skill-editor.d.ts +62 -13
  79. package/skill-editor.js +9 -9
  80. package/usage/map-user-usage-to-model-limits.js +137 -95
  81. package/usage.d.ts +8 -3
  82. package/usePageFileDrag/usePageFileDrag.js +3 -3
  83. package/utils.d.ts +26 -6
  84. package/utils.js +10 -10
@@ -1,35 +1,42 @@
1
1
  import { MessageRole as e, messageAttachmentToDisplayAttachment as t } from "@epam/ai-dial-chat-shared";
2
- import { isReferenceOnlyAttachment as n, resolveMessageAnnotations as r } from "@epam/ai-dial-quotations";
3
- import { useMemo as i } from "react";
2
+ import { getAnnotationPdfPage as n, isReferenceOnlyAttachment as r, parsePdfPageReference as i, resolveMessageAnnotations as a } from "@epam/ai-dial-quotations";
3
+ import { useMemo as o } from "react";
4
4
  //#region src/conversation-sources/useConversationSources/useConversationSources.ts
5
- var a = {}, o = (o, s = a) => i(() => {
6
- let i = [], a = [], c = [], l = /* @__PURE__ */ new Set(), u = /* @__PURE__ */ new Set(), d = /* @__PURE__ */ new Set();
7
- for (let f of o) {
8
- let o = f.custom_content?.attachments;
9
- if (f.role === e.User) for (let e of o ?? []) {
10
- let n = t(e, s);
11
- u.has(n.id) || (u.add(n.id), i.push(n));
5
+ var s = {}, c = (e, t) => {
6
+ let r = n(e);
7
+ if (r == null) return t;
8
+ let a = i(t);
9
+ return a == null || a.page != null ? t : `${t}#page=${r}`;
10
+ }, l = (n, i = s) => o(() => {
11
+ let o = [], s = [], l = [], u = /* @__PURE__ */ new Set(), d = /* @__PURE__ */ new Set(), f = /* @__PURE__ */ new Set();
12
+ for (let p of n) {
13
+ let n = p.custom_content?.attachments;
14
+ if (p.role === e.User) for (let e of n ?? []) {
15
+ let n = t(e, i);
16
+ d.has(n.id) || (d.add(n.id), o.push(n));
12
17
  }
13
- else if (f.role === e.Assistant) {
14
- let e = o?.filter((e) => !n(e));
18
+ else if (p.role === e.Assistant) {
19
+ let e = n?.filter((e) => !r(e));
15
20
  for (let n of e ?? []) {
16
- let e = t(n, s);
17
- d.has(e.id) || (d.add(e.id), a.push(e));
21
+ let e = t(n, i);
22
+ f.has(e.id) || (f.add(e.id), s.push(e));
18
23
  }
19
- for (let e of o ?? []) {
20
- if (!n(e)) continue;
24
+ for (let e of n ?? []) {
25
+ if (!r(e)) continue;
21
26
  let t = e.reference_url;
22
- l.has(t) || (l.add(t), c.push({
27
+ u.has(t) || (u.add(t), l.push({
23
28
  url: t,
24
29
  title: e.title ?? t,
25
30
  contentType: e.reference_type ?? e.type ?? "",
26
31
  quote: e.data
27
32
  }));
28
33
  }
29
- for (let e of r(f)) {
34
+ for (let e of a(p)) {
30
35
  let t = e?.body?.source?.attachment, n = t?.url;
31
- !n || l.has(n) || (l.add(n), c.push({
32
- url: n,
36
+ if (!n) continue;
37
+ let r = c(e, n);
38
+ u.has(r) || (u.add(r), l.push({
39
+ url: r,
33
40
  title: t?.title ?? n.split("/").pop() ?? n,
34
41
  contentType: t?.type ?? "",
35
42
  quote: e.body?.quote
@@ -38,10 +45,10 @@ var a = {}, o = (o, s = a) => i(() => {
38
45
  }
39
46
  }
40
47
  return {
41
- uploaded: i,
42
- generated: a,
43
- sources: c
48
+ uploaded: o,
49
+ generated: s,
50
+ sources: l
44
51
  };
45
- }, [o, s]);
52
+ }, [n, i]);
46
53
  //#endregion
47
- export { o as useConversationSources };
54
+ export { l as useConversationSources };
@@ -18,7 +18,7 @@ export declare interface UseConversationSourcesResult {
18
18
  uploaded: DisplayAttachment[];
19
19
  /** Deduplicated attachments the assistant generated across the conversation. */
20
20
  generated: DisplayAttachment[];
21
- /** Quotation sources referenced by assistant messages, deduplicated by URL. */
21
+ /** Quotation sources referenced by assistant messages, deduplicated by URL; PDF sources are qualified with `#page=N`. */
22
22
  sources: QuotationSource[];
23
23
  }
24
24
 
@@ -139,7 +139,8 @@ export declare interface UseConversationExportResult {
139
139
  * `.dial`/`.zip`, v5 envelope, old- or new-chat origin) once per file, then
140
140
  * re-uploads any archive attachments to `uploads/<YYYY-MM>/` —
141
141
  * disambiguating a name collision with a ` (n)` suffix instead of rejecting
142
- * it — rewrites attachment references (and renamed titles), regenerates each
142
+ * it — rewrites attachment references (and renamed titles), drops any
143
+ * reference still pointing into another user's bucket, regenerates each
143
144
  * conversation's id/path with a fresh UUID (collision-free save, no replace
144
145
  * dialog), and persists every conversation through the injected
145
146
  * `conversationsApi`. One job per imported file, not per conversation.
package/conversation.d.ts CHANGED
@@ -215,6 +215,7 @@ export declare interface ChatStreamApi {
215
215
  stopCompletion: (dto: {
216
216
  generationId: string;
217
217
  path: string;
218
+ content?: string;
218
219
  }) => Promise<void>;
219
220
  }
220
221
 
@@ -297,7 +298,7 @@ export declare interface ConversationStreamOverlayNotifier {
297
298
  }
298
299
 
299
300
  /** The exact `startStream` shape `useConversationStream` returns. */
300
- export declare type ConversationStreamStarter = (conversationPath: string, userContent: string, messageIndex: number, model: string, customContent?: MessageCustomContent, generationId?: string, mode?: SendCompletionDtoModeEnum) => void;
301
+ export declare type ConversationStreamStarter = (conversationPath: string, userContent: string, messageIndex: number, model: string, customContent?: MessageCustomContent, generationId?: string, mode?: SendCompletionDtoModeEnum, options?: StartStreamOptions) => void;
301
302
 
302
303
  /**
303
304
  * Host-owned transport for the DIAL Core completion protocol. Implemented at
@@ -307,10 +308,14 @@ export declare type ConversationStreamStarter = (conversationPath: string, userC
307
308
  export declare interface ConversationStreamTransport {
308
309
  /** Starts a completion; delivers chunks/completion/error through `options`, aborted via `options.signal`. */
309
310
  streamCompletion(path: string, message: string | undefined, model: string, options: StreamCompletionOptions, customContent?: MessageCustomContent, generationId?: string, mode?: SendCompletionDtoModeEnum, messageIndex?: number, clientChannelId?: string): void;
310
- /** Requests the backend stop an active generation. */
311
+ /**
312
+ * Requests the backend stop an active generation. `content` is the answer text shown
313
+ * so far; the backend saves it for a generation whose text it does not hold.
314
+ */
311
315
  stopCompletion(params: {
312
316
  generationId: string;
313
317
  path: string;
318
+ content?: string;
314
319
  }): Promise<void>;
315
320
  /** Opens a stream of resource-update events for `path`, until aborted via `signal`. */
316
321
  watchConversation(path: string, signal: AbortSignal): Promise<ReadableStream<Uint8Array>>;
@@ -325,6 +330,13 @@ export declare interface ConversationStreamTransport {
325
330
  attachToGeneration(path: string, signal: AbortSignal): Promise<ReadableStream<Uint8Array>>;
326
331
  /** Reloads the persisted conversation by its full (bucket-qualified) id. */
327
332
  getConversation(conversationId: string, signal?: AbortSignal): Promise<Conversation>;
333
+ /**
334
+ * Saves `conversation` at the bucket-stripped `path`, resolving with the saved
335
+ * conversation. Used only for the single recovery save after the backend's
336
+ * terminal save was rejected with `401`; without it that failure shows the
337
+ * persistence warning immediately.
338
+ */
339
+ saveConversation?(path: string, conversation: Conversation): Promise<Conversation>;
328
340
  }
329
341
 
330
342
  /**
@@ -347,6 +359,12 @@ export declare interface CreateChatStreamApiDeps {
347
359
  getTimezone?: () => string | undefined;
348
360
  /** `fetch` implementation to issue requests through. Defaults to the global `fetch`. */
349
361
  fetchImpl?: typeof fetch;
362
+ /**
363
+ * How long an open completion stream may go without receiving a byte
364
+ * (keepalive comments included) before it is reported as a
365
+ * {@link StreamInterruptedError}. Defaults to {@link DEFAULT_STREAM_IDLE_TIMEOUT_MS}.
366
+ */
367
+ idleTimeoutMs?: number;
350
368
  }
351
369
 
352
370
  /**
@@ -361,6 +379,11 @@ export declare const createMessagePair: (content: string, customContent?: Messag
361
379
  /** Fallback text of a {@link GenerationConflictError} when no message is supplied. */
362
380
  export declare const DEFAULT_GENERATION_CONFLICT_MESSAGE = "A response is already being generated in this conversation. Wait for it to finish or stop it before sending another message.";
363
381
 
382
+ /** Safe fallback for hosts that do not supply a translated persistence warning. */
383
+ export declare const DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE = "The response could not be saved. It is still shown here, but may be lost if you reload or leave this page. Copy it before continuing.";
384
+
385
+ export declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 45000;
386
+
364
387
  /**
365
388
  * Derives the row-action visibility state for a single conversation item.
366
389
  *
@@ -376,6 +399,12 @@ export declare const DEFAULT_GENERATION_CONFLICT_MESSAGE = "A response is alread
376
399
  */
377
400
  export declare const deriveConversationRowActionState: (item: Pick<ConversationListItemDto, "sharedWithMe" | "publishedWithMe" | "isReadonly">, publishHistory: PublishHistoryEntry[] | undefined, recipients: RecipientsCountEntry) => ConversationRowActionState;
378
401
 
402
+ /**
403
+ * Returns the deployment a conversation belongs to, resolved from its id
404
+ * alone, or `undefined` when no candidate matches `deployments`.
405
+ */
406
+ export declare const findDeploymentForConversationId: (deployments: DeploymentItemDto[], conversationId: string) => DeploymentItemDto | undefined;
407
+
379
408
  /**
380
409
  * Normalises a version string for display. The server falls back to a bare
381
410
  * `package.json` version (`0.45.0`), while a CI-supplied `CHAT_VERSION` is
@@ -395,6 +424,18 @@ export declare class GenerationConflictError extends Error {
395
424
  constructor(message?: string);
396
425
  }
397
426
 
427
+ /** Identifies a failed terminal save independently of the model's generation outcome. */
428
+ export declare class GenerationPersistenceError extends Error {
429
+ static readonly type = "conversation_save_failed";
430
+ /** HTTP status of the rejected terminal write, when the backend reported one. */
431
+ readonly status?: number;
432
+ /**
433
+ * Creates the error with the default persistence message.
434
+ * @param status - HTTP status of the rejected terminal write, if known
435
+ */
436
+ constructor(status?: number);
437
+ }
438
+
398
439
  /**
399
440
  * Strips the leading bucket segment from a conversation id and decodes any
400
441
  * already-percent-encoded remainder back to raw, so callers that re-encode
@@ -407,6 +448,21 @@ export declare const getConversationPath: (conversationId: string) => string;
407
448
  */
408
449
  export declare const getConversationSource: (item: Pick<ConversationListItemDto, "sharedWithMe" | "publishedWithMe">) => FilterTab;
409
450
 
451
+ /**
452
+ * Returns the tool values the conversation's last message sets through its
453
+ * `form_schema`, restricted to `toolIds`, plus a key identifying that exact
454
+ * application. `undefined` when the last message is not an assistant message
455
+ * or sets no known tool.
456
+ *
457
+ * Streamed chunks and reloads re-create an identical `form_schema`; comparing
458
+ * keys rather than object identity applies an app's value once and leaves a
459
+ * later user toggle alone until the app sends a different value.
460
+ */
461
+ export declare const getFormSchemaToolSyncKey: (conversationId: string, messages: Message[], toolIds: readonly string[]) => {
462
+ key: string;
463
+ values: Record<string, boolean>;
464
+ } | undefined;
465
+
410
466
  /**
411
467
  * Returns the deployment the conversation was last running on: scanning
412
468
  * backwards, the `new_deployment_id` of a `model_changed` status message or
@@ -417,7 +473,7 @@ export declare const getConversationSource: (item: Pick<ConversationListItemDto,
417
473
  * an edit truncates past one: the marker is appended at the end of the
418
474
  * timeline, so truncating at an earlier message drops it, and the model the
419
475
  * user picked reverted on the next page load even though the answer had been
420
- * regenerated on it (issue #8712). The assistant message the backend writes
476
+ * regenerated on it ([#8712](https://github.com/epam/ai-dial-chat/issues/8712)). The assistant message the backend writes
421
477
  * carries the deployment that produced it, which survives that truncation
422
478
  * because it *is* the regenerated message.
423
479
  */
@@ -430,6 +486,21 @@ export declare const getLastDeploymentId: (messages: Message[]) => string | null
430
486
  */
431
487
  export declare const getLastUserMessageToolConfiguration: (messages: Message[]) => Record<string, unknown> | undefined;
432
488
 
489
+ /**
490
+ * Returns the tool configuration a (re-)loaded conversation should show: the
491
+ * last user message's `configuration_value`, overlaid in order by the
492
+ * `form_schema` values of the assistant messages after it. An app that turned
493
+ * a toggle off in its final response must not see it re-armed on reload just
494
+ * because the user message that started the run had it on.
495
+ */
496
+ export declare const getLatestToolConfiguration: (messages: Message[]) => Record<string, unknown> | undefined;
497
+
498
+ /**
499
+ * Returns every deployment id a conversation id may belong to, longest first,
500
+ * or an empty array when the format is unrecognised.
501
+ */
502
+ export declare const getModelIdCandidatesFromConversationId: (id: string) => string[];
503
+
433
504
  /**
434
505
  * Extracts the deployment/model ID from a DIAL Core conversation ID.
435
506
  *
@@ -513,6 +584,15 @@ export declare const getStarterSubmitText: (starter: StarterOption, description?
513
584
  */
514
585
  export declare const getTimeOfDayGreeting: (hour: number, translations: GreetingTranslations, firstName?: string) => string;
515
586
 
587
+ /**
588
+ * Returns the boolean values an assistant message's `form_schema` sets, keyed
589
+ * by property name, or `undefined` when no property resolves to a boolean.
590
+ * Lets a DIAL app switch a tools-menu toggle (e.g. `deep_research`) per
591
+ * message — on while a run is in progress, off once its result is delivered.
592
+ * Not filtered by tool id: `restoreToolConfiguration` ignores unknown ids.
593
+ */
594
+ export declare const getToolConfigurationFromFormSchema: (formSchema: DeploymentConfigurationSchema | undefined) => Record<string, boolean> | undefined;
595
+
516
596
  /** Pre-translated greeting phrase strings for each time-of-day period. */
517
597
  export declare interface GreetingTranslations {
518
598
  morningWithName: string;
@@ -546,11 +626,12 @@ export declare const hasStructuredAnnouncement: ({ title, description, }: Announ
546
626
  export declare const isAnswerIncomplete: (answer: Message | undefined) => boolean;
547
627
 
548
628
  /**
549
- * True when the conversation's last message is an unresolved assistant
550
- * placeholder: the backend only persists a conversation at generation start
551
- * (empty placeholder) and at generation end (final content, or a partial
552
- * flagged `streamErrorMessage`/`wasStoppedByUser`), so this shape means a
553
- * generation was still active elsewhere when the conversation was loaded.
629
+ * True when the conversation has a pending background message, or its last
630
+ * message is an unresolved assistant placeholder: the backend only persists a
631
+ * conversation at generation start (empty placeholder) and at generation end
632
+ * (final content, or a partial flagged `streamErrorMessage`/`wasStoppedByUser`).
633
+ * This can mean generation is still active elsewhere, or that its terminal save
634
+ * failed.
554
635
  */
555
636
  export declare const isAwaitingGenerationResume: (conversation: Conversation) => boolean;
556
637
 
@@ -593,9 +674,12 @@ declare interface LocalModeValues {
593
674
  }
594
675
 
595
676
  /**
596
- * Returns the normalized stages of a raw stage array or a message-like
597
- * payload (reading `custom_content.stages`, falling back to
598
- * `customContent.stages`), or `undefined` when there are none.
677
+ * Returns the normalized stages of a complete raw stage array or a
678
+ * message-like payload (reading `custom_content.stages`, falling back to
679
+ * `customContent.stages`), or `undefined` when there are none. An entry
680
+ * without an `index` takes its array position — the identity a complete
681
+ * non-streaming array's `parent_stage_index` refers to — while explicit
682
+ * (possibly sparse) indexes are kept. Not for partial stream deltas.
599
683
  */
600
684
  export declare const mapStages: (source: RawStage[] | RawStageSource | null | undefined) => Stage[] | undefined;
601
685
 
@@ -624,6 +708,20 @@ export declare interface MessagePair {
624
708
  assistantMessage: Message;
625
709
  }
626
710
 
711
+ /** A stable snapshot of one eligible native text selection. */
712
+ export declare interface MessageSelectionReplySnapshot {
713
+ /** Scope that owned the selection when it was captured. */
714
+ conversationId: string;
715
+ /** Message text root containing the captured range. */
716
+ body: HTMLDivElement;
717
+ /** Captured native range. */
718
+ range: Range;
719
+ /** Native selected text preserved for the generated file. */
720
+ text: string;
721
+ /** Visible fragment of the selected range. */
722
+ rect: DOMRect;
723
+ }
724
+
627
725
  /**
628
726
  * Normalises a stored response-format string to the current enum. Legacy data
629
727
  * may contain `'Markdown'` or `'PlainText'` (capital-first) instead of the
@@ -667,6 +765,11 @@ export declare interface RawStage {
667
765
  tag?: string | null;
668
766
  /** Files produced or referenced by this stage. */
669
767
  attachments?: RawStageAttachment[] | null;
768
+ /**
769
+ * `index` of the parent stage (its array position in a complete unindexed
770
+ * array). Nullish means top level; `0` is a valid parent.
771
+ */
772
+ parent_stage_index?: number | null;
670
773
  }
671
774
 
672
775
  /**
@@ -753,6 +856,18 @@ export declare const shouldRerunGenerationOnEdit: (messages: Message[], messageI
753
856
  /** True when the first user/assistant exchange is complete and LLM naming may still run. */
754
857
  export declare const shouldWatchForDisplayNameUpdate: (conversation: Conversation) => boolean;
755
858
 
859
+ /** Per-start options of {@link UseConversationStreamResult.startStream}. */
860
+ export declare interface StartStreamOptions {
861
+ /**
862
+ * Treats a `GenerationConflictError` as "this turn is already being
863
+ * generated" — e.g. a page reloaded before that generation saved its start
864
+ * state — and joins the running generation instead of showing the conflict
865
+ * message, which remains the fallback. Only for a start that sends no new
866
+ * user text; a send the user typed must keep reporting the conflict.
867
+ */
868
+ resumeOnConflict?: boolean;
869
+ }
870
+
756
871
  /** Options accepted by {@link ConversationStreamTransport.streamCompletion}. */
757
872
  export declare interface StreamCompletionOptions {
758
873
  onChunk: (chunk: StreamChunk) => void;
@@ -761,10 +876,34 @@ export declare interface StreamCompletionOptions {
761
876
  signal: AbortSignal;
762
877
  }
763
878
 
879
+ /**
880
+ * Reported through `onError` when the network connection carrying a
881
+ * completion stream is lost (a rejected `fetch`, or a read that fails after a
882
+ * 2xx response) or goes silent past the idle timeout — what a laptop sleep or
883
+ * a phone lock mid-generation produces. The backend-owned generation may
884
+ * still be running, so the caller can recover it rather than treat the
885
+ * response as failed. `cause` holds the original error, when there is one.
886
+ */
887
+ export declare class StreamInterruptedError extends Error {
888
+ constructor(cause?: unknown);
889
+ }
890
+
891
+ /**
892
+ * Reported through `onError` when DIAL Core itself signals a failure with an
893
+ * in-band `{ error: { message } }` SSE chunk. Its `message` is upstream text
894
+ * intended for the user, unlike a transport failure (failed `fetch`, non-OK
895
+ * status, broken stream), whose message is technical detail. A custom
896
+ * transport raises this for any user-facing upstream error text.
897
+ */
898
+ export declare class StreamUpstreamError extends Error {
899
+ constructor(message: string);
900
+ }
901
+
764
902
  /**
765
903
  * Returns a renderable `Stage` for one raw payload: `index` defaults to `0`,
766
904
  * `name` to `''`, an unrecognized `status` to `null` (still running), and
767
- * `content`/`tag`/`attachments` are passed through when present.
905
+ * `content`/`tag`/`attachments`/`parent_stage_index` are passed through when
906
+ * present (a parent of `0` included).
768
907
  */
769
908
  export declare const toStage: (stage: RawStage) => Stage;
770
909
 
@@ -784,7 +923,8 @@ export declare interface TranscribeWithDeploymentParams {
784
923
  * 1. If the active conversation is not present in the list, requests a
785
924
  * refresh. The list and refresh callback are intentionally excluded from
786
925
  * the dependency array to avoid a refresh-loop on every list update.
787
- * 2. Marks the active conversation as viewed whenever it or the list changes.
926
+ * 2. Marks the active conversation as viewed when its matching identity becomes
927
+ * available. List refreshes and persistence rollback do not retry the write.
788
928
  */
789
929
  export declare const useActiveConversationSync: ({ activeConversationId, items, refreshConversations, markConversationViewed, conversationIdsMatch, toPanelConversationId, }: UseActiveConversationSyncParams) => string | undefined;
790
930
 
@@ -985,10 +1125,14 @@ export declare interface UseConversationLookupMapsParams {
985
1125
  * Returns a memoised array whose shape is compatible with the conversation
986
1126
  * panel's item contract. All app-specific concerns (icon URL resolution,
987
1127
  * route generation, id normalisation) are injected via resolver callbacks.
1128
+ *
1129
+ * While every input except `items` is unchanged, a DTO that is referentially
1130
+ * the same as in the previous render maps to the same item object, so a list
1131
+ * change (or a navigation that re-derives the list) re-renders only the rows
1132
+ * whose DTO actually changed.
988
1133
  */
989
- export declare const useConversationPanelItems: ({ items, deployments, isDeploymentsLoading, toPanelConversationId, resolveIconUrl, resolveIconTooltip, resolveHref, resolveTaskBadge, }: UseConversationPanelItemsParams) => {
990
- showTaskBadge?: boolean | undefined;
991
- taskBadgeLabel?: string | undefined;
1134
+ export declare const useConversationPanelItems: ({ items, deployments, isDeploymentsLoading, toPanelConversationId, resolveIconUrl, resolveIconTooltip, resolveHref, resolveTaskPresentation, }: UseConversationPanelItemsParams) => {
1135
+ leadingIcon?: ReactNode;
992
1136
  isUnread?: boolean | undefined;
993
1137
  id: string;
994
1138
  title: string;
@@ -1024,11 +1168,12 @@ export declare interface UseConversationPanelItemsResolvers {
1024
1168
  /** Resolves the href to navigate to when the conversation row is clicked. */
1025
1169
  resolveHref: (panelConversationId: string) => string;
1026
1170
  /**
1027
- * Returns badge metadata for a scheduled-task conversation, or `undefined`
1028
- * when the item should not show a task badge.
1171
+ * Returns the row presentation for a scheduled-task conversation — a
1172
+ * host-rendered leading icon that replaces the deployment avatar, and the
1173
+ * unread flag — or `undefined` for an ordinary conversation.
1029
1174
  */
1030
- resolveTaskBadge?: (item: ConversationListItemDto) => {
1031
- label: string;
1175
+ resolveTaskPresentation?: (item: ConversationListItemDto) => {
1176
+ leadingIcon?: ReactNode;
1032
1177
  isUnread: boolean;
1033
1178
  } | undefined;
1034
1179
  }
@@ -1041,7 +1186,7 @@ export declare interface UseConversationPanelItemsResolvers {
1041
1186
  * `transport`/`generation`/`channel`/`overlay` capabilities rather than any
1042
1187
  * app context or `server-api` import.
1043
1188
  */
1044
- export declare const useConversationStream: ({ conversationId, state: { setConversation, conversationRef }, transport, generation: { startGeneration, completeGeneration }, channel, overlay, onStopError, generationConflictMessage, }: UseConversationStreamParams) => UseConversationStreamResult;
1189
+ export declare const useConversationStream: ({ conversationId, state: { setConversation, conversationRef }, transport, generation: { startGeneration, completeGeneration }, channel, overlay, onStopError, generationConflictMessage, generationPersistenceErrorMessage, onStreamError, batchChunksPerFrame, }: UseConversationStreamParams) => UseConversationStreamResult;
1045
1190
 
1046
1191
  /** Parameters for {@link useConversationStream}. */
1047
1192
  export declare interface UseConversationStreamParams {
@@ -1059,17 +1204,39 @@ export declare interface UseConversationStreamParams {
1059
1204
  * {@link DEFAULT_GENERATION_CONFLICT_MESSAGE}.
1060
1205
  */
1061
1206
  generationConflictMessage?: string;
1207
+ /** Host-translated warning shown when the received answer could not be saved. */
1208
+ generationPersistenceErrorMessage?: string;
1209
+ /**
1210
+ * Receives the original error of every failed stream. The hook shows only
1211
+ * host-supplied or upstream-supplied text on the message bubble and hides
1212
+ * transport detail (e.g. `Failed to fetch`), so this is where the host can
1213
+ * still log or report the raw error.
1214
+ */
1215
+ onStreamError?: (error: Error) => void;
1216
+ /**
1217
+ * Coalesces the displayed chunk updates into one `setConversation` per
1218
+ * animation frame. Chunks still reach the per-path buffer immediately, and
1219
+ * any pending update is flushed before completion, error, stop, or a
1220
+ * superseding generation. Defaults to `false` (one update per chunk).
1221
+ */
1222
+ batchChunksPerFrame?: boolean;
1062
1223
  }
1063
1224
 
1064
1225
  /** Return value of {@link useConversationStream}. */
1065
1226
  export declare interface UseConversationStreamResult {
1066
- startStream: (conversationId: string, userContent: string, messageIndex: number, model: string, customContent?: MessageCustomContent, generationId?: string, mode?: SendCompletionDtoModeEnum) => void;
1227
+ startStream: (conversationId: string, userContent: string, messageIndex: number, model: string, customContent?: MessageCustomContent, generationId?: string, mode?: SendCompletionDtoModeEnum, options?: StartStreamOptions) => void;
1067
1228
  handleStop: () => void;
1068
1229
  resumeIfAwaitingGeneration: (currentConversationId: string, conversation: Conversation) => void;
1069
1230
  /** Restores the accumulated live assistant message when its conversation is loaded again before completion. */
1070
1231
  restoreBufferedGeneration: (currentConversationId: string, conversation: Conversation) => Conversation;
1071
1232
  isStreaming: boolean;
1072
1233
  canStopStreaming: boolean;
1234
+ /** Whether the displayed conversation's terminal read failed. */
1235
+ hasConversationReloadError: boolean;
1236
+ /** Whether a retry of that read is in flight. */
1237
+ isReloadingConversation: boolean;
1238
+ /** Retries the failed read without saving or regenerating the answer. */
1239
+ retryConversationReload: () => Promise<void>;
1073
1240
  }
1074
1241
 
1075
1242
  /**
@@ -1105,6 +1272,35 @@ export declare interface UseImportFilePickerResult {
1105
1272
  handleFileChange: (event: ChangeEvent<HTMLInputElement>) => void;
1106
1273
  }
1107
1274
 
1275
+ /**
1276
+ * Captures eligible message text and creates Reply files for an existing composer
1277
+ * attachment flow without acquiring host deployment, upload or localization knowledge.
1278
+ */
1279
+ export declare const useMessageSelectionReply: ({ conversationId, enabled, droppedFiles, onDroppedFilesConsumed, }: UseMessageSelectionReplyParams) => {
1280
+ contentRef: (node: HTMLDivElement | null) => (() => void) | undefined;
1281
+ actionRef: RefObject<HTMLDivElement | null>;
1282
+ selection: MessageSelectionReplySnapshot | null;
1283
+ onReply: () => void;
1284
+ dismiss: () => void;
1285
+ pendingFiles: File[];
1286
+ onFilesConsumed: () => void;
1287
+ focusRequestId: number | undefined;
1288
+ onAttachmentsChange: (attachments: Attachment[]) => void;
1289
+ addedRevision: number;
1290
+ };
1291
+
1292
+ /** Parameters accepted by {@link useMessageSelectionReply}. */
1293
+ export declare interface UseMessageSelectionReplyParams {
1294
+ /** Identifier used to discard selection and queued files after a scope change. */
1295
+ conversationId: string;
1296
+ /** Whether the host currently permits a Reply attachment. */
1297
+ enabled: boolean;
1298
+ /** Files from the host's ordinary page-drop queue. */
1299
+ droppedFiles: File[];
1300
+ /** Acknowledges the captured page-drop batch after the composer accepts it. */
1301
+ onDroppedFilesConsumed: () => void;
1302
+ }
1303
+
1108
1304
  /**
1109
1305
  * Derives the tools submenu from the active deployment's configuration schema:
1110
1306
  * every boolean property becomes a toggle. Manages toggle state locally, resets
package/conversation.js CHANGED
@@ -1,32 +1,33 @@
1
1
  import { buildAnnouncementSignature as e, hasAnnouncementContent as t, hasStructuredAnnouncement as n, sanitizeAnnouncementHtml as r, sanitizeAnnouncementMessageHtml as i } from "./conversation/announcement-message.js";
2
- import { DEFAULT_GENERATION_CONFLICT_MESSAGE as a, GenerationConflictError as o, createChatStreamApi as s } from "./conversation/create-chat-stream-api.js";
3
- import { shouldWatchForDisplayNameUpdate as c } from "./conversation/display-name-watch.js";
4
- import { formatAppVersion as l, sanitizeFooterHtml as u } from "./conversation/footer-message.js";
5
- import { getModelIdFromConversationId as d } from "./conversation/get-model-id-from-conversation-id.js";
6
- import { getTimeOfDayGreeting as f } from "./conversation/greeting.js";
7
- import { createDeploymentChangedMessage as p } from "./conversation/message-factory.js";
8
- import { getLastDeploymentId as m, getLastUserMessageToolConfiguration as h, isMessageStreaming as g, messageHasStages as _, normalizeResponseFormat as v } from "./conversation/message-utils.js";
9
- import { getQuickAppConversationStarters as y } from "./conversation/quick-app-conversation-starters.js";
10
- import { mapStages as b, toStage as x } from "./conversation/stage.js";
11
- import { getStarterPopulateText as S, getStartersFromSchema as C } from "./conversation/starter-option.js";
12
- import { deriveConversationRowActionState as w } from "./conversation/deriveConversationRowActionState/deriveConversationRowActionState.js";
13
- import { useActiveConversationSync as T } from "./conversation/useActiveConversationSync/useActiveConversationSync.js";
14
- import { useAsyncConfirmDialog as E } from "./conversation/useAsyncConfirmDialog/useAsyncConfirmDialog.js";
15
- import { useConversationLookupMaps as D } from "./conversation/useConversationLookupMaps/useConversationLookupMaps.js";
16
- import { getConversationSource as O, useConversationPanelItems as k } from "./conversation/useConversationPanelItems/useConversationPanelItems.js";
17
- import { useImportFilePicker as A } from "./conversation/useImportFilePicker/useImportFilePicker.js";
18
- import { useAttachmentUpload as j } from "./conversation/useAttachmentUpload/useAttachmentUpload.js";
19
- import { AudioTranscriptionError as M, AudioTranscriptionErrorReason as N } from "./conversation/useTranscribeAudio/audio-transcription-error.js";
20
- import { useTranscribeAudio as P } from "./conversation/useTranscribeAudio/useTranscribeAudio.js";
21
- import { attachmentToDto as F, attachmentsToDtos as I } from "./conversation/useConversationHandlers/attachment-to-dto.js";
22
- import { createMessagePair as L } from "./conversation/useConversationHandlers/message-factory.js";
23
- import { hasActiveToolConfig as R, isAnswerIncomplete as z, isMessageChanged as B, shouldRerunGenerationOnEdit as V } from "./conversation/useConversationHandlers/message-utils.js";
24
- import { getStarterConversationText as H, getStarterDisplayText as U, getStarterSubmitText as W } from "./conversation/useConversationHandlers/starter-option.js";
25
- import { getConversationPath as G } from "./conversation/useConversationStream/conversation-path.js";
26
- import { useConversationHandlers as K } from "./conversation/useConversationHandlers/useConversationHandlers.js";
27
- import { applyChunkToMessages as q, mergeStages as J } from "./conversation/useConversationStream/apply-chunk.js";
28
- import { isAwaitingGenerationResume as Y } from "./conversation/useConversationStream/generation-resume.js";
29
- import { useConversationStream as X } from "./conversation/useConversationStream/useConversationStream.js";
30
- import { useChatSettingsFormConfig as Z } from "./useChatSettingsFormConfig/useChatSettingsFormConfig.js";
31
- import { useToolsMenu as Q } from "./useToolsMenu/useToolsMenu.js";
32
- export { M as AudioTranscriptionError, N as AudioTranscriptionErrorReason, a as DEFAULT_GENERATION_CONFLICT_MESSAGE, o as GenerationConflictError, q as applyChunkToMessages, F as attachmentToDto, I as attachmentsToDtos, e as buildAnnouncementSignature, s as createChatStreamApi, p as createDeploymentChangedMessage, L as createMessagePair, w as deriveConversationRowActionState, l as formatAppVersion, G as getConversationPath, O as getConversationSource, m as getLastDeploymentId, h as getLastUserMessageToolConfiguration, d as getModelIdFromConversationId, y as getQuickAppConversationStarters, H as getStarterConversationText, U as getStarterDisplayText, S as getStarterPopulateText, W as getStarterSubmitText, C as getStartersFromSchema, f as getTimeOfDayGreeting, R as hasActiveToolConfig, t as hasAnnouncementContent, n as hasStructuredAnnouncement, z as isAnswerIncomplete, Y as isAwaitingGenerationResume, B as isMessageChanged, g as isMessageStreaming, b as mapStages, J as mergeStages, _ as messageHasStages, v as normalizeResponseFormat, r as sanitizeAnnouncementHtml, i as sanitizeAnnouncementMessageHtml, u as sanitizeFooterHtml, V as shouldRerunGenerationOnEdit, c as shouldWatchForDisplayNameUpdate, x as toStage, T as useActiveConversationSync, E as useAsyncConfirmDialog, j as useAttachmentUpload, Z as useChatSettingsFormConfig, K as useConversationHandlers, D as useConversationLookupMaps, k as useConversationPanelItems, X as useConversationStream, A as useImportFilePicker, Q as useToolsMenu, P as useTranscribeAudio };
2
+ import { DEFAULT_GENERATION_CONFLICT_MESSAGE as a, DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE as o, DEFAULT_STREAM_IDLE_TIMEOUT_MS as s, GenerationConflictError as c, GenerationPersistenceError as l, StreamInterruptedError as u, StreamUpstreamError as d, createChatStreamApi as f } from "./conversation/create-chat-stream-api.js";
3
+ import { shouldWatchForDisplayNameUpdate as p } from "./conversation/display-name-watch.js";
4
+ import { formatAppVersion as m, sanitizeFooterHtml as h } from "./conversation/footer-message.js";
5
+ import { findDeploymentForConversationId as g, getModelIdCandidatesFromConversationId as _, getModelIdFromConversationId as v } from "./conversation/get-model-id-from-conversation-id.js";
6
+ import { getTimeOfDayGreeting as y } from "./conversation/greeting.js";
7
+ import { createDeploymentChangedMessage as b } from "./conversation/message-factory.js";
8
+ import { getFormSchemaToolSyncKey as x, getLastDeploymentId as S, getLastUserMessageToolConfiguration as C, getLatestToolConfiguration as w, getToolConfigurationFromFormSchema as T, isMessageStreaming as E, messageHasStages as D, normalizeResponseFormat as O } from "./conversation/message-utils.js";
9
+ import { getQuickAppConversationStarters as k } from "./conversation/quick-app-conversation-starters.js";
10
+ import { mapStages as A, toStage as j } from "./conversation/stage.js";
11
+ import { getStarterPopulateText as M, getStartersFromSchema as N } from "./conversation/starter-option.js";
12
+ import { deriveConversationRowActionState as P } from "./conversation/deriveConversationRowActionState/deriveConversationRowActionState.js";
13
+ import { useActiveConversationSync as F } from "./conversation/useActiveConversationSync/useActiveConversationSync.js";
14
+ import { useAsyncConfirmDialog as I } from "./conversation/useAsyncConfirmDialog/useAsyncConfirmDialog.js";
15
+ import { useConversationLookupMaps as L } from "./conversation/useConversationLookupMaps/useConversationLookupMaps.js";
16
+ import { getConversationSource as R, useConversationPanelItems as z } from "./conversation/useConversationPanelItems/useConversationPanelItems.js";
17
+ import { useImportFilePicker as B } from "./conversation/useImportFilePicker/useImportFilePicker.js";
18
+ import { useMessageSelectionReply as V } from "./conversation/useMessageSelectionReply/useMessageSelectionReply.js";
19
+ import { useAttachmentUpload as H } from "./conversation/useAttachmentUpload/useAttachmentUpload.js";
20
+ import { AudioTranscriptionError as U, AudioTranscriptionErrorReason as W } from "./conversation/useTranscribeAudio/audio-transcription-error.js";
21
+ import { useTranscribeAudio as G } from "./conversation/useTranscribeAudio/useTranscribeAudio.js";
22
+ import { attachmentToDto as K, attachmentsToDtos as q } from "./conversation/useConversationHandlers/attachment-to-dto.js";
23
+ import { createMessagePair as J } from "./conversation/useConversationHandlers/message-factory.js";
24
+ import { hasActiveToolConfig as Y, isAnswerIncomplete as X, isMessageChanged as Z, shouldRerunGenerationOnEdit as Q } from "./conversation/useConversationHandlers/message-utils.js";
25
+ import { getStarterConversationText as $, getStarterDisplayText as ee, getStarterSubmitText as te } from "./conversation/useConversationHandlers/starter-option.js";
26
+ import { getConversationPath as ne } from "./conversation/useConversationStream/conversation-path.js";
27
+ import { useConversationHandlers as re } from "./conversation/useConversationHandlers/useConversationHandlers.js";
28
+ import { applyChunkToMessages as ie, mergeStages as ae } from "./conversation/useConversationStream/apply-chunk.js";
29
+ import { isAwaitingGenerationResume as oe } from "./conversation/useConversationStream/generation-resume.js";
30
+ import { useConversationStream as se } from "./conversation/useConversationStream/useConversationStream.js";
31
+ import { useChatSettingsFormConfig as ce } from "./useChatSettingsFormConfig/useChatSettingsFormConfig.js";
32
+ import { useToolsMenu as le } from "./useToolsMenu/useToolsMenu.js";
33
+ export { U as AudioTranscriptionError, W as AudioTranscriptionErrorReason, a as DEFAULT_GENERATION_CONFLICT_MESSAGE, o as DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE, s as DEFAULT_STREAM_IDLE_TIMEOUT_MS, c as GenerationConflictError, l as GenerationPersistenceError, u as StreamInterruptedError, d as StreamUpstreamError, ie as applyChunkToMessages, K as attachmentToDto, q as attachmentsToDtos, e as buildAnnouncementSignature, f as createChatStreamApi, b as createDeploymentChangedMessage, J as createMessagePair, P as deriveConversationRowActionState, g as findDeploymentForConversationId, m as formatAppVersion, ne as getConversationPath, R as getConversationSource, x as getFormSchemaToolSyncKey, S as getLastDeploymentId, C as getLastUserMessageToolConfiguration, w as getLatestToolConfiguration, _ as getModelIdCandidatesFromConversationId, v as getModelIdFromConversationId, k as getQuickAppConversationStarters, $ as getStarterConversationText, ee as getStarterDisplayText, M as getStarterPopulateText, te as getStarterSubmitText, N as getStartersFromSchema, y as getTimeOfDayGreeting, T as getToolConfigurationFromFormSchema, Y as hasActiveToolConfig, t as hasAnnouncementContent, n as hasStructuredAnnouncement, X as isAnswerIncomplete, oe as isAwaitingGenerationResume, Z as isMessageChanged, E as isMessageStreaming, A as mapStages, ae as mergeStages, D as messageHasStages, O as normalizeResponseFormat, r as sanitizeAnnouncementHtml, i as sanitizeAnnouncementMessageHtml, h as sanitizeFooterHtml, Q as shouldRerunGenerationOnEdit, p as shouldWatchForDisplayNameUpdate, j as toStage, F as useActiveConversationSync, I as useAsyncConfirmDialog, H as useAttachmentUpload, ce as useChatSettingsFormConfig, re as useConversationHandlers, L as useConversationLookupMaps, z as useConversationPanelItems, se as useConversationStream, B as useImportFilePicker, V as useMessageSelectionReply, le as useToolsMenu, G as useTranscribeAudio };