@epam/ai-dial-chat-hooks 1.2.0-dev.164 → 1.2.0-dev.170

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.
@@ -1,195 +1,227 @@
1
1
  import { safeDecodeURI as e } from "../../shared/string-utils.js";
2
- import { DEFAULT_GENERATION_CONFLICT_MESSAGE as t, DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE as n, GenerationConflictError as r, GenerationPersistenceError as i, StreamUpstreamError as a } from "../create-chat-stream-api.js";
3
- import { getConversationPath as o } from "./conversation-path.js";
4
- import { applyChunkToMessages as s } from "./apply-chunk.js";
5
- import { restoreBufferedMessage as c } from "./buffered-generation.js";
6
- import { createResumeIfAwaitingGeneration as l, hasGeneratedPayload as u, isAwaitingGenerationResume as d } from "./generation-resume.js";
7
- import { generateUUID as f } from "@epam/ai-dial-chat-shared";
8
- import { useCallback as p, useEffect as m, useMemo as h, useRef as g, useState as _ } from "react";
9
- import { SendCompletionDtoModeEnum as v } from "@epam/ai-dial-chat-api-client";
2
+ import { DEFAULT_GENERATION_CONFLICT_MESSAGE as t, DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE as n, GenerationConflictError as r, GenerationPersistenceError as i, StreamInterruptedError as a, StreamUpstreamError as o } from "../create-chat-stream-api.js";
3
+ import { getConversationPath as s } from "./conversation-path.js";
4
+ import { applyChunkToMessages as c } from "./apply-chunk.js";
5
+ import { restoreBufferedMessage as l } from "./buffered-generation.js";
6
+ import { createResumeIfAwaitingGeneration as u, fetchConversationForRecovery as d, hasGeneratedPayload as f, isAwaitingGenerationResume as p } from "./generation-resume.js";
7
+ import { MessageRole as m, generateUUID as h } from "@epam/ai-dial-chat-shared";
8
+ import { useCallback as g, useEffect as _, useMemo as v, useRef as y, useState as b } from "react";
9
+ import { SendCompletionDtoModeEnum as x } from "@epam/ai-dial-chat-api-client";
10
10
  //#region src/conversation/useConversationStream/useConversationStream.ts
11
- var y = 2e4, b = (e, t, n) => e instanceof i ? n : e instanceof r ? t : e instanceof a ? e.message : "", x = ({ conversationId: r, state: { setConversation: a, conversationRef: x }, transport: S, generation: { startGeneration: C, completeGeneration: w }, channel: T, overlay: E, onStopError: D, generationConflictMessage: O = t, generationPersistenceErrorMessage: k = n, onStreamError: A }) => {
12
- let j = g(A);
13
- m(() => {
14
- j.current = A;
15
- }, [A]);
16
- let [M, N] = _(() => /* @__PURE__ */ new Set()), [P, F] = _(null), I = g(null), L = g(null), R = g(/* @__PURE__ */ new Set()), z = g(/* @__PURE__ */ new Map()), B = g(/* @__PURE__ */ new Set()), V = g(/* @__PURE__ */ new Map()), H = g(r);
17
- m(() => {
18
- H.current = r;
11
+ var S = 2e4, ee = (e, t, n) => e instanceof i ? n : e instanceof r ? t : e instanceof o ? e.message : "", C = ({ conversationId: r, state: { setConversation: o, conversationRef: C }, transport: w, generation: { startGeneration: T, completeGeneration: E }, channel: D, overlay: O, onStopError: k, generationConflictMessage: A = t, generationPersistenceErrorMessage: j = n, onStreamError: M }) => {
12
+ let N = y(M);
13
+ _(() => {
14
+ N.current = M;
15
+ }, [M]);
16
+ let [P, F] = b(() => /* @__PURE__ */ new Set()), [I, L] = b(null), R = y(null), z = y(null), B = y(/* @__PURE__ */ new Set()), V = y(/* @__PURE__ */ new Map()), H = y(/* @__PURE__ */ new Set()), U = y(/* @__PURE__ */ new Map()), W = y(r);
17
+ _(() => {
18
+ W.current = r;
19
19
  }, [r]);
20
- let U = p((e) => o(H.current ?? "") === e, []), W = p((e) => {
21
- N((t) => {
20
+ let G = g((e) => s(W.current ?? "") === e, []), K = g((e) => {
21
+ F((t) => {
22
22
  if (t.has(e)) return t;
23
23
  let n = new Set(t);
24
24
  return n.add(e), n;
25
25
  });
26
- }, []), G = p((e) => {
27
- N((t) => {
26
+ }, []), q = g((e) => {
27
+ F((t) => {
28
28
  if (!t.has(e)) return t;
29
29
  let n = new Set(t);
30
30
  return n.delete(e), n;
31
31
  });
32
- }, []), K = p((t, n, r, l, p, m, h = v.Append) => {
33
- let g = m ?? f(), _ = o(t);
34
- R.current.delete(_), I.current = g, L.current = _, V.current.set(_, g), F(_);
35
- let D;
36
- h === v.Regenerate ? D = r : h === v.Edit && (D = r - 1);
37
- let A = () => V.current.get(_) !== g, M = C(_, g), N = x.current?.messages[r];
38
- N ? z.current.set(_, {
39
- generationId: g,
32
+ }, []), J = v(() => u({
33
+ transport: w,
34
+ setConversation: o,
35
+ conversationRef: C,
36
+ resumingPathsRef: B,
37
+ bufferedGenerationsRef: V,
38
+ addStreamingPath: K,
39
+ removeStreamingPath: q,
40
+ isPathDisplayed: G,
41
+ generationPersistenceErrorMessage: j
42
+ }), [
43
+ K,
44
+ q,
45
+ G,
46
+ w,
47
+ j
48
+ ]), Y = g((t, n, r, u, g, _, v = x.Append) => {
49
+ let y = _ ?? h(), b = s(t);
50
+ B.current.delete(b), R.current = y, z.current = b, U.current.set(b, y), L(b);
51
+ let k;
52
+ v === x.Regenerate ? k = r : v === x.Edit && (k = r - 1);
53
+ let M = () => U.current.get(b) !== y, P = T(b, y), F = C.current?.messages[r];
54
+ F ? V.current.set(b, {
55
+ generationId: y,
40
56
  messageIndex: r,
41
- message: N
42
- }) : z.current.delete(_), W(_), E?.notifyGenerationStart?.(), T?.ensureConnected();
43
- let P = () => {
44
- if (A()) return;
45
- let e = z.current.get(_);
46
- !e || e.generationId !== g || (e.message = {
57
+ message: F
58
+ }) : V.current.delete(b), K(b), O?.notifyGenerationStart?.(), D?.ensureConnected();
59
+ let I = () => {
60
+ M() || q(b), R.current === y && (R.current = null, z.current = null, L(null)), E(b, y), D?.notifyGenerationSettled?.();
61
+ }, W = () => {
62
+ if (M()) return;
63
+ let e = V.current.get(b);
64
+ !e || e.generationId !== y || (e.message = {
47
65
  ...e.message,
48
- streamErrorMessage: k
49
- }, j.current?.(new i()), U(_) && a((t) => {
50
- if (!t || A() || !U(_)) return t;
51
- let n = c(t, e);
52
- return x.current = n, n;
66
+ streamErrorMessage: j
67
+ }, N.current?.(new i()), G(b) && o((t) => {
68
+ if (!t || M() || !G(b)) return t;
69
+ let n = l(t, e);
70
+ return C.current = n, n;
53
71
  }));
54
- }, H = {
55
- signal: M.signal,
72
+ }, Y = {
73
+ signal: P.signal,
56
74
  onChunk: (e) => {
57
- if (I.current !== g) return;
58
- let t = z.current.get(_);
59
- if (t?.generationId === g) {
60
- let n = s([t.message], 0, e);
75
+ if (R.current !== y) return;
76
+ let t = V.current.get(b);
77
+ if (t?.generationId === y) {
78
+ let n = c([t.message], 0, e);
61
79
  n && (t.message = n[0]);
62
80
  }
63
- U(_) && a((t) => {
81
+ G(b) && o((t) => {
64
82
  if (!t) return t;
65
- let n = z.current.get(_), i;
66
- if (n?.generationId === g) i = c(t, n);
83
+ let n = V.current.get(b), i;
84
+ if (n?.generationId === y) i = l(t, n);
67
85
  else {
68
- let n = s(t.messages, r, e);
86
+ let n = c(t.messages, r, e);
69
87
  if (!n) return t;
70
88
  i = {
71
89
  ...t,
72
90
  messages: n
73
91
  };
74
92
  }
75
- return x.current = i, i;
93
+ return C.current = i, i;
76
94
  });
77
95
  },
78
96
  onComplete: async () => {
79
- let n = z.current.get(_), r = n?.generationId === g ? n : void 0;
80
- if (A() || G(_), I.current === g && (I.current = null, L.current = null, F(null)), w(_, g), T?.notifyGenerationSettled?.(), B.current.has(g) ? B.current.delete(g) : A() || E?.notifyGenerationEnd?.(), !A()) {
81
- if (!U(_)) {
82
- r && z.current.delete(_);
97
+ let n = V.current.get(b), r = n?.generationId === y ? n : void 0;
98
+ if (I(), H.current.has(y) ? H.current.delete(y) : M() || O?.notifyGenerationEnd?.(), !M()) {
99
+ if (!G(b)) {
100
+ r && V.current.delete(b);
83
101
  return;
84
102
  }
85
103
  try {
86
- let n = await S.getConversation(e(t));
87
- if (A()) return;
88
- if (!U(_)) {
89
- r && z.current.delete(_);
104
+ let n = await w.getConversation(e(t));
105
+ if (M()) return;
106
+ if (!G(b)) {
107
+ r && V.current.delete(b);
90
108
  return;
91
109
  }
92
- if (r && r.messageIndex === n.messages.length - 1 && d(n) && u(r.message)) {
93
- P();
110
+ if (r && r.messageIndex === n.messages.length - 1 && p(n) && f(r.message)) {
111
+ W();
94
112
  return;
95
113
  }
96
- r && z.current.delete(_), a(n), x.current = n;
114
+ r && V.current.delete(b), o(n), C.current = n;
97
115
  } catch {
98
- A() || P();
116
+ M() || W();
99
117
  }
100
118
  }
101
119
  },
102
120
  onError: (e) => {
103
- j.current?.(e);
104
- let t = z.current.get(_), n = t?.generationId === g ? t : void 0;
105
- if (n && (e instanceof i ? n.message = {
106
- ...n.message,
107
- streamErrorMessage: k
108
- } : z.current.delete(_)), A() || G(_), I.current === g && (I.current = null, L.current = null, F(null)), w(_, g), T?.notifyGenerationSettled?.(), A() || !U(_)) return;
109
- let o = b(e, O, k);
110
- a((e) => {
111
- if (!e || A() || !U(_)) return e;
112
- let t = n?.generationId === g ? c(e, n) : e, i = {
113
- ...t,
114
- messages: t.messages.map((e, t) => t === r ? {
115
- ...e,
116
- streamErrorMessage: o
117
- } : e)
118
- };
119
- return x.current = i, i;
121
+ if (N.current?.(e), e instanceof a && !M()) {
122
+ Q(e);
123
+ return;
124
+ }
125
+ X(e);
126
+ }
127
+ }, X = (e) => {
128
+ let t = V.current.get(b), n = t?.generationId === y ? t : void 0;
129
+ if (n && (e instanceof i ? n.message = {
130
+ ...n.message,
131
+ streamErrorMessage: j
132
+ } : V.current.delete(b)), I(), M() || !G(b)) return;
133
+ let a = ee(e, A, j);
134
+ o((e) => {
135
+ if (!e || M() || !G(b)) return e;
136
+ let t = n?.generationId === y ? l(e, n) : e, i = {
137
+ ...t,
138
+ messages: t.messages.map((e, t) => t === r ? {
139
+ ...e,
140
+ streamErrorMessage: a
141
+ } : e)
142
+ };
143
+ return C.current = i, i;
144
+ });
145
+ }, Z = () => {
146
+ I(), H.current.has(y) ? H.current.delete(y) : M() || O?.notifyGenerationEnd?.();
147
+ }, Q = async (n) => {
148
+ B.current.add(b);
149
+ let i = await d(() => w.getConversation(e(t)), M);
150
+ if (M()) {
151
+ I();
152
+ return;
153
+ }
154
+ let a = i?.messages[i.messages.length - 1], s = i != null && i.messages.length - 1 === r && a?.role === m.Assistant;
155
+ if (!i || !s) {
156
+ B.current.delete(b), X(n);
157
+ return;
158
+ }
159
+ let c = V.current.get(b), l = c?.generationId === y ? c : void 0;
160
+ if (p(i)) {
161
+ J(t, i, {
162
+ seedMessage: l?.message,
163
+ skipDedupe: !0,
164
+ onSettled: Z
120
165
  });
166
+ return;
121
167
  }
168
+ B.current.delete(b), l && V.current.delete(b), G(b) && (o(i), C.current = i), Z();
122
169
  };
123
170
  (async () => {
124
171
  try {
125
- let e = T?.channelId ?? await T?.waitForChannel(y) ?? void 0, t = B.current.has(g);
126
- if (M.signal.aborted || A() || t) {
127
- z.current.get(_)?.generationId === g && z.current.delete(_), A() || G(_), I.current === g && (I.current = null, L.current = null, F(null)), w(_, g), T?.notifyGenerationSettled?.(), t ? B.current.delete(g) : A() || E?.notifyGenerationEnd?.();
172
+ let e = D?.channelId ?? await D?.waitForChannel(S) ?? void 0, t = H.current.has(y);
173
+ if (P.signal.aborted || M() || t) {
174
+ V.current.get(b)?.generationId === y && V.current.delete(b), M() || q(b), R.current === y && (R.current = null, z.current = null, L(null)), E(b, y), D?.notifyGenerationSettled?.(), t ? H.current.delete(y) : M() || O?.notifyGenerationEnd?.();
128
175
  return;
129
176
  }
130
- S.streamCompletion(_, n, l, H, p, g, h, D, e);
177
+ w.streamCompletion(b, n, u, Y, g, y, v, k, e);
131
178
  } catch (e) {
132
- H.onError(e instanceof Error ? e : Error(String(e)));
179
+ Y.onError(e instanceof Error ? e : Error(String(e)));
133
180
  }
134
181
  })();
135
182
  }, [
136
- C,
137
- w,
138
- W,
139
- G,
140
- U,
141
- T?.channelId,
142
- T?.ensureConnected,
143
- T?.waitForChannel,
144
- T?.notifyGenerationSettled,
183
+ T,
145
184
  E,
146
- S,
185
+ K,
186
+ q,
187
+ G,
188
+ D?.channelId,
189
+ D?.ensureConnected,
190
+ D?.waitForChannel,
191
+ D?.notifyGenerationSettled,
147
192
  O,
148
- k
149
- ]), q = p((e, t) => {
150
- let n = o(e), r = z.current.get(n);
151
- return r ? c(t, r) : t;
152
- }, []), J = p(() => {
153
- let e = I.current;
193
+ w,
194
+ J,
195
+ A,
196
+ j
197
+ ]), X = g((e, t) => {
198
+ let n = s(e), r = V.current.get(n);
199
+ return r ? l(t, r) : t;
200
+ }, []), Z = g(() => {
201
+ let e = R.current;
154
202
  if (!e || !r) return;
155
- let t = o(r);
156
- L.current === t && (B.current.add(e), E?.notifyStopGenerating?.(), S.stopCompletion({
203
+ let t = s(r);
204
+ z.current === t && (H.current.add(e), O?.notifyStopGenerating?.(), w.stopCompletion({
157
205
  generationId: e,
158
206
  path: t
159
207
  }).catch((e) => {
160
208
  let t = e instanceof Error ? e : Error(String(e));
161
- D?.(t);
209
+ k?.(t);
162
210
  }));
163
211
  }, [
164
212
  r,
165
- D,
166
- E,
167
- S
168
- ]), Y = h(() => l({
169
- transport: S,
170
- setConversation: a,
171
- conversationRef: x,
172
- resumingPathsRef: R,
173
- bufferedGenerationsRef: z,
174
- addStreamingPath: W,
175
- removeStreamingPath: G,
176
- isPathDisplayed: U,
177
- generationPersistenceErrorMessage: k
178
- }), [
179
- W,
180
- G,
181
- U,
182
- S,
183
- k
184
- ]), X = r != null && M.has(o(r)), Z = r == null ? null : o(r);
213
+ k,
214
+ O,
215
+ w
216
+ ]), Q = g((e, t) => J(e, t), [J]), te = r != null && P.has(s(r)), $ = r == null ? null : s(r);
185
217
  return {
186
- startStream: K,
187
- handleStop: J,
188
- resumeIfAwaitingGeneration: Y,
189
- restoreBufferedGeneration: q,
190
- isStreaming: X,
191
- canStopStreaming: Z != null && P === Z
218
+ startStream: Y,
219
+ handleStop: Z,
220
+ resumeIfAwaitingGeneration: Q,
221
+ restoreBufferedGeneration: X,
222
+ isStreaming: te,
223
+ canStopStreaming: $ != null && I === $
192
224
  };
193
225
  };
194
226
  //#endregion
195
- export { x as useConversationStream };
227
+ export { C as useConversationStream };
package/conversation.d.ts CHANGED
@@ -347,6 +347,12 @@ export declare interface CreateChatStreamApiDeps {
347
347
  getTimezone?: () => string | undefined;
348
348
  /** `fetch` implementation to issue requests through. Defaults to the global `fetch`. */
349
349
  fetchImpl?: typeof fetch;
350
+ /**
351
+ * How long an open completion stream may go without receiving a byte
352
+ * (keepalive comments included) before it is reported as a
353
+ * {@link StreamInterruptedError}. Defaults to {@link DEFAULT_STREAM_IDLE_TIMEOUT_MS}.
354
+ */
355
+ idleTimeoutMs?: number;
350
356
  }
351
357
 
352
358
  /**
@@ -364,6 +370,8 @@ export declare const DEFAULT_GENERATION_CONFLICT_MESSAGE = "A response is alread
364
370
  /** Safe fallback for hosts that do not supply a translated persistence warning. */
365
371
  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.";
366
372
 
373
+ export declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 45000;
374
+
367
375
  /**
368
376
  * Derives the row-action visibility state for a single conversation item.
369
377
  *
@@ -784,6 +792,18 @@ export declare interface StreamCompletionOptions {
784
792
  signal: AbortSignal;
785
793
  }
786
794
 
795
+ /**
796
+ * Reported through `onError` when the network connection carrying a
797
+ * completion stream is lost (a rejected `fetch`, or a read that fails after a
798
+ * 2xx response) or goes silent past the idle timeout — what a laptop sleep or
799
+ * a phone lock mid-generation produces. The backend-owned generation may
800
+ * still be running, so the caller can recover it rather than treat the
801
+ * response as failed. `cause` holds the original error, when there is one.
802
+ */
803
+ export declare class StreamInterruptedError extends Error {
804
+ constructor(cause?: unknown);
805
+ }
806
+
787
807
  /**
788
808
  * Reported through `onError` when DIAL Core itself signals a failure with an
789
809
  * in-band `{ error: { message } }` SSE chunk. Its `message` is upstream text
package/conversation.js CHANGED
@@ -1,33 +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, DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE as o, GenerationConflictError as s, GenerationPersistenceError as c, StreamUpstreamError as l, createChatStreamApi as u } from "./conversation/create-chat-stream-api.js";
3
- import { shouldWatchForDisplayNameUpdate as d } from "./conversation/display-name-watch.js";
4
- import { formatAppVersion as f, sanitizeFooterHtml as p } from "./conversation/footer-message.js";
5
- import { getModelIdFromConversationId as m } from "./conversation/get-model-id-from-conversation-id.js";
6
- import { getTimeOfDayGreeting as h } from "./conversation/greeting.js";
7
- import { createDeploymentChangedMessage as g } from "./conversation/message-factory.js";
8
- import { getLastDeploymentId as _, getLastUserMessageToolConfiguration as v, isMessageStreaming as y, messageHasStages as b, normalizeResponseFormat as x } from "./conversation/message-utils.js";
9
- import { getQuickAppConversationStarters as S } from "./conversation/quick-app-conversation-starters.js";
10
- import { mapStages as C, toStage as w } from "./conversation/stage.js";
11
- import { getStarterPopulateText as T, getStartersFromSchema as E } from "./conversation/starter-option.js";
12
- import { deriveConversationRowActionState as D } from "./conversation/deriveConversationRowActionState/deriveConversationRowActionState.js";
13
- import { useActiveConversationSync as O } from "./conversation/useActiveConversationSync/useActiveConversationSync.js";
14
- import { useAsyncConfirmDialog as k } from "./conversation/useAsyncConfirmDialog/useAsyncConfirmDialog.js";
15
- import { useConversationLookupMaps as A } from "./conversation/useConversationLookupMaps/useConversationLookupMaps.js";
16
- import { getConversationSource as j, useConversationPanelItems as M } from "./conversation/useConversationPanelItems/useConversationPanelItems.js";
17
- import { useImportFilePicker as N } from "./conversation/useImportFilePicker/useImportFilePicker.js";
18
- import { useMessageSelectionReply as P } from "./conversation/useMessageSelectionReply/useMessageSelectionReply.js";
19
- import { useAttachmentUpload as F } from "./conversation/useAttachmentUpload/useAttachmentUpload.js";
20
- import { AudioTranscriptionError as I, AudioTranscriptionErrorReason as L } from "./conversation/useTranscribeAudio/audio-transcription-error.js";
21
- import { useTranscribeAudio as R } from "./conversation/useTranscribeAudio/useTranscribeAudio.js";
22
- import { attachmentToDto as z, attachmentsToDtos as B } from "./conversation/useConversationHandlers/attachment-to-dto.js";
23
- import { createMessagePair as V } from "./conversation/useConversationHandlers/message-factory.js";
24
- import { hasActiveToolConfig as H, isAnswerIncomplete as U, isMessageChanged as W, shouldRerunGenerationOnEdit as G } from "./conversation/useConversationHandlers/message-utils.js";
25
- import { getStarterConversationText as K, getStarterDisplayText as q, getStarterSubmitText as J } from "./conversation/useConversationHandlers/starter-option.js";
26
- import { getConversationPath as Y } from "./conversation/useConversationStream/conversation-path.js";
27
- import { useConversationHandlers as X } from "./conversation/useConversationHandlers/useConversationHandlers.js";
28
- import { applyChunkToMessages as Z, mergeStages as Q } from "./conversation/useConversationStream/apply-chunk.js";
29
- import { isAwaitingGenerationResume as $ } from "./conversation/useConversationStream/generation-resume.js";
30
- import { useConversationStream as ee } from "./conversation/useConversationStream/useConversationStream.js";
31
- import { useChatSettingsFormConfig as te } from "./useChatSettingsFormConfig/useChatSettingsFormConfig.js";
32
- import { useToolsMenu as ne } from "./useToolsMenu/useToolsMenu.js";
33
- export { I as AudioTranscriptionError, L as AudioTranscriptionErrorReason, a as DEFAULT_GENERATION_CONFLICT_MESSAGE, o as DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE, s as GenerationConflictError, c as GenerationPersistenceError, l as StreamUpstreamError, Z as applyChunkToMessages, z as attachmentToDto, B as attachmentsToDtos, e as buildAnnouncementSignature, u as createChatStreamApi, g as createDeploymentChangedMessage, V as createMessagePair, D as deriveConversationRowActionState, f as formatAppVersion, Y as getConversationPath, j as getConversationSource, _ as getLastDeploymentId, v as getLastUserMessageToolConfiguration, m as getModelIdFromConversationId, S as getQuickAppConversationStarters, K as getStarterConversationText, q as getStarterDisplayText, T as getStarterPopulateText, J as getStarterSubmitText, E as getStartersFromSchema, h as getTimeOfDayGreeting, H as hasActiveToolConfig, t as hasAnnouncementContent, n as hasStructuredAnnouncement, U as isAnswerIncomplete, $ as isAwaitingGenerationResume, W as isMessageChanged, y as isMessageStreaming, C as mapStages, Q as mergeStages, b as messageHasStages, x as normalizeResponseFormat, r as sanitizeAnnouncementHtml, i as sanitizeAnnouncementMessageHtml, p as sanitizeFooterHtml, G as shouldRerunGenerationOnEdit, d as shouldWatchForDisplayNameUpdate, w as toStage, O as useActiveConversationSync, k as useAsyncConfirmDialog, F as useAttachmentUpload, te as useChatSettingsFormConfig, X as useConversationHandlers, A as useConversationLookupMaps, M as useConversationPanelItems, ee as useConversationStream, N as useImportFilePicker, P as useMessageSelectionReply, ne as useToolsMenu, R 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 { getModelIdFromConversationId as g } from "./conversation/get-model-id-from-conversation-id.js";
6
+ import { getTimeOfDayGreeting as _ } from "./conversation/greeting.js";
7
+ import { createDeploymentChangedMessage as v } from "./conversation/message-factory.js";
8
+ import { getLastDeploymentId as y, getLastUserMessageToolConfiguration as b, isMessageStreaming as x, messageHasStages as S, normalizeResponseFormat as C } from "./conversation/message-utils.js";
9
+ import { getQuickAppConversationStarters as w } from "./conversation/quick-app-conversation-starters.js";
10
+ import { mapStages as T, toStage as E } from "./conversation/stage.js";
11
+ import { getStarterPopulateText as D, getStartersFromSchema as O } from "./conversation/starter-option.js";
12
+ import { deriveConversationRowActionState as k } from "./conversation/deriveConversationRowActionState/deriveConversationRowActionState.js";
13
+ import { useActiveConversationSync as A } from "./conversation/useActiveConversationSync/useActiveConversationSync.js";
14
+ import { useAsyncConfirmDialog as j } from "./conversation/useAsyncConfirmDialog/useAsyncConfirmDialog.js";
15
+ import { useConversationLookupMaps as M } from "./conversation/useConversationLookupMaps/useConversationLookupMaps.js";
16
+ import { getConversationSource as N, useConversationPanelItems as P } from "./conversation/useConversationPanelItems/useConversationPanelItems.js";
17
+ import { useImportFilePicker as F } from "./conversation/useImportFilePicker/useImportFilePicker.js";
18
+ import { useMessageSelectionReply as I } from "./conversation/useMessageSelectionReply/useMessageSelectionReply.js";
19
+ import { useAttachmentUpload as L } from "./conversation/useAttachmentUpload/useAttachmentUpload.js";
20
+ import { AudioTranscriptionError as R, AudioTranscriptionErrorReason as z } from "./conversation/useTranscribeAudio/audio-transcription-error.js";
21
+ import { useTranscribeAudio as B } from "./conversation/useTranscribeAudio/useTranscribeAudio.js";
22
+ import { attachmentToDto as V, attachmentsToDtos as H } from "./conversation/useConversationHandlers/attachment-to-dto.js";
23
+ import { createMessagePair as U } from "./conversation/useConversationHandlers/message-factory.js";
24
+ import { hasActiveToolConfig as W, isAnswerIncomplete as G, isMessageChanged as K, shouldRerunGenerationOnEdit as q } from "./conversation/useConversationHandlers/message-utils.js";
25
+ import { getStarterConversationText as J, getStarterDisplayText as Y, getStarterSubmitText as X } from "./conversation/useConversationHandlers/starter-option.js";
26
+ import { getConversationPath as Z } from "./conversation/useConversationStream/conversation-path.js";
27
+ import { useConversationHandlers as Q } from "./conversation/useConversationHandlers/useConversationHandlers.js";
28
+ import { applyChunkToMessages as $, mergeStages as ee } from "./conversation/useConversationStream/apply-chunk.js";
29
+ import { isAwaitingGenerationResume as te } from "./conversation/useConversationStream/generation-resume.js";
30
+ import { useConversationStream as ne } from "./conversation/useConversationStream/useConversationStream.js";
31
+ import { useChatSettingsFormConfig as re } from "./useChatSettingsFormConfig/useChatSettingsFormConfig.js";
32
+ import { useToolsMenu as ie } from "./useToolsMenu/useToolsMenu.js";
33
+ export { R as AudioTranscriptionError, z 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, $ as applyChunkToMessages, V as attachmentToDto, H as attachmentsToDtos, e as buildAnnouncementSignature, f as createChatStreamApi, v as createDeploymentChangedMessage, U as createMessagePair, k as deriveConversationRowActionState, m as formatAppVersion, Z as getConversationPath, N as getConversationSource, y as getLastDeploymentId, b as getLastUserMessageToolConfiguration, g as getModelIdFromConversationId, w as getQuickAppConversationStarters, J as getStarterConversationText, Y as getStarterDisplayText, D as getStarterPopulateText, X as getStarterSubmitText, O as getStartersFromSchema, _ as getTimeOfDayGreeting, W as hasActiveToolConfig, t as hasAnnouncementContent, n as hasStructuredAnnouncement, G as isAnswerIncomplete, te as isAwaitingGenerationResume, K as isMessageChanged, x as isMessageStreaming, T as mapStages, ee as mergeStages, S as messageHasStages, C as normalizeResponseFormat, r as sanitizeAnnouncementHtml, i as sanitizeAnnouncementMessageHtml, h as sanitizeFooterHtml, q as shouldRerunGenerationOnEdit, p as shouldWatchForDisplayNameUpdate, E as toStage, A as useActiveConversationSync, j as useAsyncConfirmDialog, L as useAttachmentUpload, re as useChatSettingsFormConfig, Q as useConversationHandlers, M as useConversationLookupMaps, P as useConversationPanelItems, ne as useConversationStream, F as useImportFilePicker, I as useMessageSelectionReply, ie as useToolsMenu, B as useTranscribeAudio };
package/index.d.ts CHANGED
@@ -141,6 +141,7 @@ import { SkillEditorFileActions } from '@epam/ai-dial-skill-editor';
141
141
  import { SkillEditorValues } from '@epam/ai-dial-skill-editor';
142
142
  import { SkillFileBatchError } from '@epam/ai-dial-skill-editor';
143
143
  import { SkillFileListResponseDto } from '@epam/ai-dial-chat-api-client';
144
+ import { SkillFileSourceEntry } from '@epam/ai-dial-skill-editor';
144
145
  import { SkillFileTreeNode } from '@epam/ai-dial-skill-editor';
145
146
  import { SkillFileUploadCandidate } from '@epam/ai-dial-skill-editor';
146
147
  import { SkillFileValidationResult } from '@epam/ai-dial-skill-editor';
@@ -991,6 +992,12 @@ export declare interface CreateChatStreamApiDeps {
991
992
  getTimezone?: () => string | undefined;
992
993
  /** `fetch` implementation to issue requests through. Defaults to the global `fetch`. */
993
994
  fetchImpl?: typeof fetch;
995
+ /**
996
+ * How long an open completion stream may go without receiving a byte
997
+ * (keepalive comments included) before it is reported as a
998
+ * {@link StreamInterruptedError}. Defaults to {@link DEFAULT_STREAM_IDLE_TIMEOUT_MS}.
999
+ */
1000
+ idleTimeoutMs?: number;
994
1001
  }
995
1002
 
996
1003
  /**
@@ -1145,6 +1152,8 @@ export declare const DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE = "The respons
1145
1152
  */
1146
1153
  export declare const DEFAULT_MAX_ARCHIVE_BYTES: number;
1147
1154
 
1155
+ export declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 45000;
1156
+
1148
1157
  /** Translated folder-path labels for the Personal/Shared/Public deployment groupings. */
1149
1158
  export declare interface DeploymentFolderLabels {
1150
1159
  personal: string;
@@ -1859,6 +1868,9 @@ export declare const getQuickAppConversationStarters: (conversationStarters: unk
1859
1868
  */
1860
1869
  export declare const getRemainingCharacters: (value: string, maxLength: number) => number | null;
1861
1870
 
1871
+ /** Returns the limit each over-long skill field exceeds, keyed by field; fields within their limit are omitted. */
1872
+ export declare const getSkillFieldLengthViolations: (values: Record<SkillTextField, string>) => Partial<Record<SkillTextField, number>>;
1873
+
1862
1874
  /**
1863
1875
  * Returns the text to start/populate a conversation from a selected starter button.
1864
1876
  * `description` (the schema property's shared intro/question text) is used only
@@ -2521,6 +2533,15 @@ export declare enum McpResourceKind {
2521
2533
 
2522
2534
  export declare const mergeCreatedFolderIntoCache: (cache: Map<string, ListFilesItemDto[]>, parentApiPath: string, created: CreateFolderResponseDto, inheritedPermissions?: string[]) => Map<string, ListFilesItemDto[]>;
2523
2535
 
2536
+ /**
2537
+ * Merges a freshly-fetched page 0 into the currently-loaded run list: an
2538
+ * already-known id is replaced in place at its existing array position, an
2539
+ * unseen id is prepended (upstream order is `created_at desc`, so it is the
2540
+ * newest), and an id absent from `incoming` is left untouched — a background
2541
+ * refresh never removes a loaded run.
2542
+ */
2543
+ export declare const mergeRunsById: (current: ScheduledTaskRunDto[], incoming: ScheduledTaskRunDto[]) => ScheduledTaskRunDto[];
2544
+
2524
2545
  /**
2525
2546
  * Merges incoming stage deltas into an existing stage list, keyed by `index`.
2526
2547
  *
@@ -3581,6 +3602,9 @@ export declare const SKILL_MANIFEST_FILE = "SKILL.md";
3581
3602
  */
3582
3603
  export declare const SKILL_MANIFEST_MAX_BYTES: number;
3583
3604
 
3605
+ /** Length limit of each skill editor text field. */
3606
+ export declare const SKILL_TEXT_FIELD_MAX_LENGTHS: Record<SkillTextField, number>;
3607
+
3584
3608
  /**
3585
3609
  * Client-side mirror of the backend's default `SKILL_UPLOAD_MAX_FILES`
3586
3610
  * (`apps/chat-api/src/config/environment.config.ts`), used only for
@@ -3689,6 +3713,8 @@ export declare interface SkillEditorSubmitClient {
3689
3713
  export declare interface SkillEditorSubmitMessages {
3690
3714
  /** Shown when a required field is empty. */
3691
3715
  required: string;
3716
+ /** Shown when a name, description or instructions value exceeds its length limit, given that limit. */
3717
+ tooLong: (maxLength: number) => string;
3692
3718
  /**
3693
3719
  * Shown when the Instructions value opens with its own YAML frontmatter
3694
3720
  * block — pasting a whole `SKILL.md` in there would otherwise produce a
@@ -3895,6 +3921,9 @@ export declare enum SkillSource {
3895
3921
  Public = "public"
3896
3922
  }
3897
3923
 
3924
+ /** Skill editor text fields with a length limit. */
3925
+ export declare type SkillTextField = 'name' | 'description' | 'instructions';
3926
+
3898
3927
  /** Splits a file name into its base and extension, using only the last dot. */
3899
3928
  export declare const splitFileNameExtension: (fileName: string) => {
3900
3929
  base: string;
@@ -3931,6 +3960,18 @@ export declare interface StreamCompletionOptions {
3931
3960
  signal: AbortSignal;
3932
3961
  }
3933
3962
 
3963
+ /**
3964
+ * Reported through `onError` when the network connection carrying a
3965
+ * completion stream is lost (a rejected `fetch`, or a read that fails after a
3966
+ * 2xx response) or goes silent past the idle timeout — what a laptop sleep or
3967
+ * a phone lock mid-generation produces. The backend-owned generation may
3968
+ * still be running, so the caller can recover it rather than treat the
3969
+ * response as failed. `cause` holds the original error, when there is one.
3970
+ */
3971
+ export declare class StreamInterruptedError extends Error {
3972
+ constructor(cause?: unknown);
3973
+ }
3974
+
3934
3975
  /**
3935
3976
  * Reported through `onError` when DIAL Core itself signals a failure with an
3936
3977
  * in-band `{ error: { message } }` SSE chunk. Its `message` is upstream text
@@ -5902,8 +5943,8 @@ export declare interface UsePublishFoldersResult {
5902
5943
  hasPublishWriteAccess: (folderPath: string[]) => boolean;
5903
5944
  }
5904
5945
 
5905
- /** Shared, cancellation-safe pagination for one schedule's run history. */
5906
- export declare const useScheduledTaskRuns: (client: ScheduledTasksApiClient, { scheduleId, enabled, pageSize }: UseScheduledTaskRunsOptions) => {
5946
+ /** Shared, cancellation-safe pagination and background refresh for one schedule's run history. */
5947
+ export declare const useScheduledTaskRuns: (client: ScheduledTasksApiClient, { scheduleId, enabled, pageSize, nextRunTime, }: UseScheduledTaskRunsOptions) => {
5907
5948
  items: ScheduledTaskRunDto[];
5908
5949
  isLoading: boolean;
5909
5950
  isLoadingMore: boolean;
@@ -5919,6 +5960,12 @@ export declare interface UseScheduledTaskRunsOptions {
5919
5960
  scheduleId: string;
5920
5961
  enabled?: boolean;
5921
5962
  pageSize?: number;
5963
+ /**
5964
+ * The task's next scheduled run time (ISO string), when known. Drives the
5965
+ * one-shot background refresh described on {@link useScheduledTaskRuns}.
5966
+ * The caller already holds the task DTO — this hook never fetches it.
5967
+ */
5968
+ nextRunTime?: string | null;
5922
5969
  }
5923
5970
 
5924
5971
  /** Shared scheduler-list lifecycle with generation-scoped stale-response guards. */
@@ -6192,10 +6239,11 @@ export declare interface UseSkillEditorSubmitResult {
6192
6239
  /**
6193
6240
  * Owns the Skill Editor's batch file upload workflow: validating a staged
6194
6241
  * batch, committing it atomically (supporting files plus an optional
6195
- * `SKILL.md` manifest import, with a confirmation gate), and removing
6242
+ * `SKILL.md` manifest import, with a confirmation gate), creating empty
6243
+ * folders, expanding `.zip` archives for staging, and removing
6196
6244
  * already-committed nodes.
6197
6245
  */
6198
- export declare const useSkillFileActions: ({ files, setFiles, filesContentRef, frontmatterRef, loadedValues, setLoadedValues, isEditMode, isDirty, setSelectedPath, messages, }: UseSkillFileActionsParams) => UseSkillFileActionsResult;
6246
+ export declare const useSkillFileActions: ({ files, setFiles, filesContentRef, frontmatterRef, loadedValues, setLoadedValues, isEditMode, isDirty, setSelectedPath, messages, pickFromFileSystem, }: UseSkillFileActionsParams) => UseSkillFileActionsResult;
6199
6247
 
6200
6248
  /** Parameters accepted by {@link useSkillFileActions}. */
6201
6249
  export declare interface UseSkillFileActionsParams {
@@ -6219,6 +6267,12 @@ export declare interface UseSkillFileActionsParams {
6219
6267
  setSelectedPath: Dispatch<SetStateAction<string>>;
6220
6268
  /** Localized messages, resolved by the host. */
6221
6269
  messages: SkillFileActionsMessages;
6270
+ /**
6271
+ * Opens the host's file-system picker; passed through unchanged as
6272
+ * `fileActions.pickFromFileSystem`. Omit to hide the editor's "Open DIAL
6273
+ * file system" entry.
6274
+ */
6275
+ pickFromFileSystem?: () => Promise<SkillFileSourceEntry[] | undefined>;
6222
6276
  }
6223
6277
 
6224
6278
  /** Return value of {@link useSkillFileActions}. */