@epam/ai-dial-chat-hooks 1.2.0-dev.253 → 1.2.0-dev.258

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.
@@ -48,7 +48,7 @@ var c = "Production", l = 50, u = (e, t, n) => {
48
48
  return b((e) => e.includes(n) ? e : [...e, n]), Promise.resolve();
49
49
  }, []), T = e((e) => {
50
50
  let t = s(e), n = Array.isArray(d) ? d : [];
51
- !t || n.includes(t) || f?.([t, ...n].slice(0, l));
51
+ !t || n[0] === t || f?.([t, ...n.filter((e) => e !== t)].slice(0, l));
52
52
  }, [d, f]), E = e((e) => !e.includes(c), []);
53
53
  return {
54
54
  folderItems: n(() => {
package/catalog.d.ts CHANGED
@@ -1227,9 +1227,9 @@ export declare interface UsePublishFoldersResult {
1227
1227
  */
1228
1228
  onCreatePublishFolder: (parentPath: string[], name: string) => Promise<void>;
1229
1229
  /**
1230
- * Records `folderPath` as a publish destination the user has actually used,
1231
- * so it keeps appearing in the tree on later publishes. Call it after a
1232
- * successful publish.
1230
+ * Records `folderPath` as the most recent publish destination (moving it to
1231
+ * the front if already remembered), so it keeps appearing in the tree on
1232
+ * later publishes. Call it after a successful publish.
1233
1233
  */
1234
1234
  rememberPublishFolder: (folderPath: string[]) => void;
1235
1235
  /** Heuristic write-access check for a folder path (see the TODO above). */
@@ -3,37 +3,38 @@ import { DEFAULT_GENERATION_PERSISTENCE_ERROR_MESSAGE as t, GenerationPersistenc
3
3
  import { getConversationPath as r } from "./conversation-path.js";
4
4
  import { applyChunkToMessages as i } from "./apply-chunk.js";
5
5
  import { restoreBufferedMessage as a } from "./buffered-generation.js";
6
- import { MessageRole as o } from "@epam/ai-dial-chat-shared";
6
+ import { BackgroundGenerationStatus as o, MessageRole as s } from "@epam/ai-dial-chat-shared";
7
7
  //#region src/conversation/useConversationStream/generation-resume.ts
8
- var s = (e) => {
8
+ var c = (e) => {
9
9
  let t = e.custom_content;
10
10
  return !!e.content || e.responseId != null || !!t?.attachments?.length || !!t?.stages?.length || !!t?.annotations?.length || !!t?.form_schema || t?.state != null;
11
- }, c = (e) => {
11
+ }, l = (e) => e.messages.findIndex((e) => e.backgroundGeneration?.status === o.Pending), u = (e) => {
12
+ if (l(e) !== -1) return !0;
12
13
  let t = e.messages[e.messages.length - 1];
13
- return !!t && t.role === o.Assistant && !s(t) && t.streamErrorMessage == null && !t.wasStoppedByUser;
14
- }, l = 300 * 1e3, u = "awaiting-resume", d = [
14
+ return !!t && t.role === s.Assistant && !c(t) && t.streamErrorMessage == null && !t.wasStoppedByUser;
15
+ }, d = 300 * 1e3, f = "awaiting-resume", p = [
15
16
  1e3,
16
17
  2e3,
17
18
  4e3,
18
19
  8e3,
19
20
  16e3
20
- ], f = (e) => new Promise((t) => {
21
+ ], m = (e) => new Promise((t) => {
21
22
  let n = typeof window < "u", r = () => {
22
23
  clearTimeout(i), n && window.removeEventListener("online", r), t();
23
24
  }, i = setTimeout(r, e);
24
25
  n && window.addEventListener("online", r);
25
- }), p = async (e, t) => {
26
- for (let n = 0; n <= d.length; n++) {
26
+ }), h = async (e, t) => {
27
+ for (let n = 0; n <= p.length; n++) {
27
28
  if (t()) return null;
28
29
  try {
29
30
  return await e();
30
31
  } catch {
31
- if (n === d.length) return null;
32
- await f(d[n]);
32
+ if (n === p.length) return null;
33
+ await m(p[n]);
33
34
  }
34
35
  }
35
36
  return null;
36
- }, m = async (e, t) => {
37
+ }, g = async (e, t) => {
37
38
  let n = e.getReader(), r = new TextDecoder(), i = "";
38
39
  try {
39
40
  for (;;) {
@@ -62,112 +63,112 @@ var s = (e) => {
62
63
  } catch {} finally {
63
64
  n.releaseLock();
64
65
  }
65
- }, h = ({ transport: o, setConversation: d, conversationRef: f, resumingPathsRef: p, bufferedGenerationsRef: h, addStreamingPath: g, removeStreamingPath: _, isPathDisplayed: v, generationPersistenceErrorMessage: y = t, frameScheduler: b }) => (t, x, S = {}) => {
66
- if (!c(x)) {
67
- S.onSettled?.();
66
+ }, _ = ({ transport: o, setConversation: s, conversationRef: p, resumingPathsRef: m, bufferedGenerationsRef: h, addStreamingPath: _, removeStreamingPath: v, isPathDisplayed: y, generationPersistenceErrorMessage: b = t, frameScheduler: x, stoppedGenerationIdsRef: S }) => (t, C, w = {}) => {
67
+ if (!u(C)) {
68
+ w.onSettled?.();
68
69
  return;
69
70
  }
70
- let C = r(t);
71
- if (!S.skipDedupe && p.current.has(C)) return;
72
- p.current.add(C), g(C);
73
- let w = x.messages.length - 1, T = {
74
- generationId: u,
75
- messageIndex: w,
76
- message: S.seedMessage ?? x.messages[w]
71
+ let T = r(t);
72
+ if (!w.skipDedupe && m.current.has(T)) return;
73
+ m.current.add(T), _(T);
74
+ let E = l(C), D = E !== -1, O = D ? E : C.messages.length - 1, k = {
75
+ generationId: f,
76
+ messageIndex: O,
77
+ message: w.seedMessage ?? C.messages[O]
77
78
  };
78
- h.current.set(C, T);
79
- let E = () => h.current.get(C) === T, D = (e, t = !1) => {
80
- if (b?.flush(C), !E()) return;
81
- p.current.delete(C), _(C);
82
- let n = e && e.messages.length - 1 === w && c(e) && s(T.message);
79
+ h.current.set(T, k);
80
+ let A = () => h.current.get(T) === k, j = D ? C.messages[O].backgroundGeneration?.generationId : void 0, M = () => j != null && (S?.current.has(j) ?? !1), N = (e, t = !1) => {
81
+ if (x?.flush(T), j != null && S?.current.delete(j), !A()) return;
82
+ m.current.delete(T), v(T);
83
+ let n = !D && e && e.messages.length - 1 === O && u(e) && c(k.message);
83
84
  if (t || n) {
84
- T.message = {
85
- ...T.message,
86
- streamErrorMessage: y
87
- }, v(C) && d((e) => {
88
- if (!e || !E() || !v(C)) return e;
89
- let t = a(e, T);
90
- return f.current = t, t;
85
+ k.message = {
86
+ ...k.message,
87
+ streamErrorMessage: b
88
+ }, y(T) && s((e) => {
89
+ if (!e || !A() || !y(T)) return e;
90
+ let t = a(e, k);
91
+ return p.current = t, t;
91
92
  });
92
93
  return;
93
94
  }
94
- h.current.delete(C), e && v(C) && (d(e), f.current = e);
95
- }, O = async () => {
95
+ h.current.delete(T), e && y(T) && (s(e), p.current = e);
96
+ }, P = async () => {
96
97
  try {
97
- D(await o.getConversation(e(t)));
98
+ N(await o.getConversation(e(t)));
98
99
  } catch {
99
- D(void 0, s(T.message));
100
+ N(void 0, c(k.message));
100
101
  }
101
- }, k = (e) => {
102
- b?.cancel(C), E() && (T.message = e, v(C) && d((e) => {
103
- if (!e || !E() || !v(C)) return e;
104
- let t = a(e, T);
105
- return f.current = t, t;
102
+ }, F = (e) => {
103
+ x?.cancel(T), A() && (k.message = e, y(T) && s((e) => {
104
+ if (!e || !A() || !y(T)) return e;
105
+ let t = a(e, k);
106
+ return p.current = t, t;
106
107
  }));
107
- }, A = () => d((e) => {
108
- if (!e || !E() || !v(C)) return e;
109
- let t = a(e, T);
110
- return f.current = t, t;
111
- }), j = (e) => {
112
- if (!E()) return;
113
- let t = i([T.message], 0, e);
114
- if (t && (T.message = t[0]), v(C)) {
115
- if (b) {
116
- b.schedule(C, A);
108
+ }, I = () => s((e) => {
109
+ if (!e || !A() || !y(T)) return e;
110
+ let t = a(e, k);
111
+ return p.current = t, t;
112
+ }), L = (e) => {
113
+ if (!A() || M()) return;
114
+ let t = i([k.message], 0, e);
115
+ if (t && (k.message = t[0]), y(T)) {
116
+ if (x) {
117
+ x.schedule(T, I);
117
118
  return;
118
119
  }
119
- A();
120
+ I();
120
121
  }
121
- }, M = async () => {
122
+ }, R = async () => {
122
123
  let n = new AbortController(), r;
123
124
  try {
124
- r = await o.watchConversation(C, n.signal);
125
+ r = await o.watchConversation(T, n.signal);
125
126
  } catch {
126
- await O();
127
+ await P();
127
128
  return;
128
129
  }
129
130
  let i = !1, a = window.setTimeout(() => {
130
131
  n.abort();
131
- }, l);
132
+ }, d);
132
133
  try {
133
- await m(r, async (n) => {
134
+ await g(r, async (n) => {
134
135
  if (n?.action !== "UPDATE") return !1;
135
136
  try {
136
137
  let n = await o.getConversation(e(t));
137
- if (!c(n)) return D(n), i = !0, !0;
138
+ if (!u(n)) return N(n), i = !0, !0;
138
139
  } catch {}
139
140
  return !1;
140
141
  });
141
142
  } finally {
142
143
  clearTimeout(a);
143
144
  }
144
- i || await O();
145
- }, N = async () => {
145
+ i || await P();
146
+ }, z = async () => {
146
147
  let e = new AbortController(), t;
147
148
  try {
148
- t = await o.attachToGeneration(C, e.signal);
149
+ t = await o.attachToGeneration(T, e.signal);
149
150
  } catch {
150
151
  return !1;
151
152
  }
152
153
  let r = !1, i = !1;
153
- return await m(t, (e) => {
154
- if (!E()) return !0;
154
+ return await g(t, (e) => {
155
+ if (!A()) return !0;
155
156
  switch (e.type) {
156
- case "snapshot": return k(e.message), !1;
157
- case "chunk": return j(e.chunk), !1;
157
+ case "snapshot": return F(e.message), !1;
158
+ case "chunk": return L(e.chunk), !1;
158
159
  case "done":
159
160
  case "error":
160
161
  case "stopped": return i = e.type === "error" && e.errorType === n.type, r = !0, !0;
161
162
  }
162
- }), r ? (i ? D(void 0, !0) : await O(), !0) : !E();
163
+ }), r ? (i ? N(void 0, !0) : await P(), !0) : !A();
163
164
  };
164
165
  (async () => {
165
166
  try {
166
- await N() || await M();
167
+ await z() || await R();
167
168
  } finally {
168
- S.onSettled?.();
169
+ w.onSettled?.();
169
170
  }
170
171
  })();
171
172
  };
172
173
  //#endregion
173
- export { h as createResumeIfAwaitingGeneration, p as fetchConversationForRecovery, s as hasGeneratedPayload, c as isAwaitingGenerationResume };
174
+ export { _ as createResumeIfAwaitingGeneration, h as fetchConversationForRecovery, l as findPendingBackgroundMessageIndex, c as hasGeneratedPayload, u as isAwaitingGenerationResume };
@@ -3,36 +3,40 @@ import { DEFAULT_GENERATION_CONFLICT_MESSAGE as t, DEFAULT_GENERATION_PERSISTENC
3
3
  import { getConversationPath as s } from "./conversation-path.js";
4
4
  import { applyChunkToMessages as c } from "./apply-chunk.js";
5
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 { createFrameScheduler as m } from "./frame-scheduler.js";
8
- import { MessageRole as ee, generateUUID as h } from "@epam/ai-dial-chat-shared";
9
- import { useCallback as g, useEffect as _, useMemo as v, useRef as y, useState as b } from "react";
10
- import { SendCompletionDtoModeEnum as x } from "@epam/ai-dial-chat-api-client";
6
+ import { createResumeIfAwaitingGeneration as u, fetchConversationForRecovery as ee, findPendingBackgroundMessageIndex as d, hasGeneratedPayload as te, isAwaitingGenerationResume as f } from "./generation-resume.js";
7
+ import { createFrameScheduler as p } from "./frame-scheduler.js";
8
+ import { MessageRole as m, generateUUID as ne } from "@epam/ai-dial-chat-shared";
9
+ import { useCallback as h, useEffect as g, useMemo as _, useRef as v, useState as y } from "react";
10
+ import { SendCompletionDtoModeEnum as b } from "@epam/ai-dial-chat-api-client";
11
11
  //#region src/conversation/useConversationStream/useConversationStream.ts
12
- var te = 2e4, ne = (e, t, n) => e instanceof i ? n : e instanceof r ? t : e instanceof o ? e.message : "", S = ({ conversationId: r, state: { setConversation: o, conversationRef: S }, transport: C, generation: { startGeneration: w, completeGeneration: T }, channel: E, overlay: D, onStopError: O, generationConflictMessage: k = t, generationPersistenceErrorMessage: A = n, onStreamError: j, batchChunksPerFrame: M = !1 }) => {
13
- let N = y(j);
14
- _(() => {
12
+ var re = 2e4, ie = (e, t, n) => e instanceof i ? n : e instanceof r ? t : e instanceof o ? e.message : "", x = (e) => {
13
+ if (!e) return;
14
+ let t = d(e);
15
+ return t === -1 ? void 0 : e.messages[t].backgroundGeneration?.generationId;
16
+ }, S = ({ conversationId: r, state: { setConversation: o, conversationRef: S }, transport: C, generation: { startGeneration: w, completeGeneration: T }, channel: E, overlay: D, onStopError: O, generationConflictMessage: k = t, generationPersistenceErrorMessage: A = n, onStreamError: j, batchChunksPerFrame: M = !1 }) => {
17
+ let N = v(j);
18
+ g(() => {
15
19
  N.current = j;
16
20
  }, [j]);
17
- let P = y(M);
18
- _(() => {
21
+ let P = v(M);
22
+ g(() => {
19
23
  P.current = M;
20
24
  }, [M]);
21
- let [F] = b(m), [I, L] = b(() => /* @__PURE__ */ new Set()), [R, z] = b(null), B = y(null), V = y(null), H = y(/* @__PURE__ */ new Set()), U = y(/* @__PURE__ */ new Map()), W = y(/* @__PURE__ */ new Set()), G = y(/* @__PURE__ */ new Map()), K = y(r);
22
- _(() => (K.current = r, () => F.cancelAll()), [r, F]);
23
- let q = g((e) => s(K.current ?? "") === e, []), J = g((e) => {
25
+ let [F] = y(p), [I, L] = y(() => /* @__PURE__ */ new Set()), [R, z] = y(null), B = v(null), V = v(null), H = v(/* @__PURE__ */ new Set()), U = v(/* @__PURE__ */ new Map()), W = v(/* @__PURE__ */ new Set()), G = v(/* @__PURE__ */ new Map()), K = v(r);
26
+ g(() => (K.current = r, () => F.cancelAll()), [r, F]);
27
+ let q = h((e) => s(K.current ?? "") === e, []), J = h((e) => {
24
28
  L((t) => {
25
29
  if (t.has(e)) return t;
26
30
  let n = new Set(t);
27
31
  return n.add(e), n;
28
32
  });
29
- }, []), Y = g((e) => {
33
+ }, []), Y = h((e) => {
30
34
  L((t) => {
31
35
  if (!t.has(e)) return t;
32
36
  let n = new Set(t);
33
37
  return n.delete(e), n;
34
38
  });
35
- }, []), X = v(() => u({
39
+ }, []), X = _(() => u({
36
40
  transport: C,
37
41
  setConversation: o,
38
42
  conversationRef: S,
@@ -42,7 +46,8 @@ var te = 2e4, ne = (e, t, n) => e instanceof i ? n : e instanceof r ? t : e inst
42
46
  removeStreamingPath: Y,
43
47
  isPathDisplayed: q,
44
48
  generationPersistenceErrorMessage: A,
45
- frameScheduler: M ? F : void 0
49
+ frameScheduler: M ? F : void 0,
50
+ stoppedGenerationIdsRef: W
46
51
  }), [
47
52
  J,
48
53
  Y,
@@ -51,44 +56,44 @@ var te = 2e4, ne = (e, t, n) => e instanceof i ? n : e instanceof r ? t : e inst
51
56
  A,
52
57
  M,
53
58
  F
54
- ]), Z = g((t, n, r, u, m, g, _ = x.Append) => {
55
- let v = g ?? h(), y = s(t);
56
- F.flush(y), H.current.delete(y), B.current = v, V.current = y, G.current.set(y, v), z(y);
57
- let b;
58
- _ === x.Regenerate ? b = r : _ === x.Edit && (b = r - 1);
59
- let O = () => G.current.get(y) !== v, j = w(y, v), M = S.current?.messages[r];
60
- M ? U.current.set(y, {
61
- generationId: v,
59
+ ]), Z = h((t, n, r, u, p, h, g = b.Append) => {
60
+ let _ = h ?? ne(), v = s(t);
61
+ F.flush(v), H.current.delete(v), B.current = _, V.current = v, G.current.set(v, _), z(v);
62
+ let y;
63
+ g === b.Regenerate ? y = r : g === b.Edit && (y = r - 1);
64
+ let x = () => G.current.get(v) !== _, O = w(v, _), j = S.current?.messages[r];
65
+ j ? U.current.set(v, {
66
+ generationId: _,
62
67
  messageIndex: r,
63
- message: M
64
- }) : U.current.delete(y), J(y), D?.notifyGenerationStart?.(), E?.ensureConnected();
65
- let I = () => {
66
- O() || Y(y), B.current === v && (B.current = null, V.current = null, z(null)), T(y, v), E?.notifyGenerationSettled?.();
67
- }, L = () => {
68
- if (O()) return;
69
- let e = U.current.get(y);
70
- !e || e.generationId !== v || (e.message = {
68
+ message: j
69
+ }) : U.current.delete(v), J(v), D?.notifyGenerationStart?.(), E?.ensureConnected();
70
+ let M = () => {
71
+ x() || Y(v), B.current === _ && (B.current = null, V.current = null, z(null)), T(v, _), E?.notifyGenerationSettled?.();
72
+ }, I = () => {
73
+ if (x()) return;
74
+ let e = U.current.get(v);
75
+ !e || e.generationId !== _ || (e.message = {
71
76
  ...e.message,
72
77
  streamErrorMessage: A
73
- }, N.current?.(new i()), q(y) && o((t) => {
74
- if (!t || O() || !q(y)) return t;
78
+ }, N.current?.(new i()), q(v) && o((t) => {
79
+ if (!t || x() || !q(v)) return t;
75
80
  let n = l(t, e);
76
81
  return S.current = n, n;
77
82
  }));
78
- }, R = {
79
- signal: j.signal,
83
+ }, L = {
84
+ signal: O.signal,
80
85
  onChunk: (e) => {
81
- if (B.current !== v) return;
82
- let t = U.current.get(y);
83
- if (t?.generationId === v) {
86
+ if (B.current !== _) return;
87
+ let t = U.current.get(v);
88
+ if (t?.generationId === _) {
84
89
  let n = c([t.message], 0, e);
85
90
  n && (t.message = n[0]);
86
91
  }
87
- if (q(y)) {
88
- if (P.current && t?.generationId === v) {
89
- F.schedule(y, () => {
90
- let e = U.current.get(y);
91
- if (B.current !== v || !q(y) || e?.generationId !== v) return;
92
+ if (q(v)) {
93
+ if (P.current && t?.generationId === _) {
94
+ F.schedule(v, () => {
95
+ let e = U.current.get(v);
96
+ if (B.current !== _ || !q(v) || e?.generationId !== _) return;
92
97
  let t = { ...e };
93
98
  o((e) => {
94
99
  if (!e) return e;
@@ -100,8 +105,8 @@ var te = 2e4, ne = (e, t, n) => e instanceof i ? n : e instanceof r ? t : e inst
100
105
  }
101
106
  o((t) => {
102
107
  if (!t) return t;
103
- let n = U.current.get(y), i;
104
- if (n?.generationId === v) i = l(t, n);
108
+ let n = U.current.get(v), i;
109
+ if (n?.generationId === _) i = l(t, n);
105
110
  else {
106
111
  let n = c(t.messages, r, e);
107
112
  if (!n) return t;
@@ -115,47 +120,51 @@ var te = 2e4, ne = (e, t, n) => e instanceof i ? n : e instanceof r ? t : e inst
115
120
  }
116
121
  },
117
122
  onComplete: async () => {
118
- F.flush(y);
119
- let n = U.current.get(y), r = n?.generationId === v ? n : void 0;
120
- if (I(), W.current.has(v) ? W.current.delete(v) : O() || D?.notifyGenerationEnd?.(), !O()) {
121
- if (!q(y)) {
122
- r && U.current.delete(y);
123
+ F.flush(v);
124
+ let n = U.current.get(v), r = n?.generationId === _ ? n : void 0;
125
+ if (M(), W.current.has(_) ? W.current.delete(_) : x() || D?.notifyGenerationEnd?.(), !x()) {
126
+ if (!q(v)) {
127
+ r && U.current.delete(v);
123
128
  return;
124
129
  }
125
130
  try {
126
131
  let n = await C.getConversation(e(t));
127
- if (O()) return;
128
- if (!q(y)) {
129
- r && U.current.delete(y);
132
+ if (x()) return;
133
+ if (!q(v)) {
134
+ r && U.current.delete(v);
130
135
  return;
131
136
  }
132
- if (r && r.messageIndex === n.messages.length - 1 && p(n) && f(r.message)) {
133
- L();
137
+ if (d(n) !== -1) {
138
+ X(t, n, { seedMessage: r?.message });
134
139
  return;
135
140
  }
136
- r && U.current.delete(y), o(n), S.current = n;
141
+ if (r && r.messageIndex === n.messages.length - 1 && f(n) && te(r.message)) {
142
+ I();
143
+ return;
144
+ }
145
+ r && U.current.delete(v), o(n), S.current = n;
137
146
  } catch {
138
- O() || L();
147
+ x() || I();
139
148
  }
140
149
  }
141
150
  },
142
151
  onError: (e) => {
143
- if (F.flush(y), N.current?.(e), e instanceof a && !O()) {
144
- Q(e);
152
+ if (F.flush(v), N.current?.(e), e instanceof a && !x()) {
153
+ Z(e);
145
154
  return;
146
155
  }
147
- K(e);
156
+ R(e);
148
157
  }
149
- }, K = (e) => {
150
- let t = U.current.get(y), n = t?.generationId === v ? t : void 0;
158
+ }, R = (e) => {
159
+ let t = U.current.get(v), n = t?.generationId === _ ? t : void 0;
151
160
  if (n && (e instanceof i ? n.message = {
152
161
  ...n.message,
153
162
  streamErrorMessage: A
154
- } : U.current.delete(y)), I(), O() || !q(y)) return;
155
- let a = ne(e, k, A);
163
+ } : U.current.delete(v)), M(), x() || !q(v)) return;
164
+ let a = ie(e, k, A);
156
165
  o((e) => {
157
- if (!e || O() || !q(y)) return e;
158
- let t = n?.generationId === v ? l(e, n) : e, i = {
166
+ if (!e || x() || !q(v)) return e;
167
+ let t = n?.generationId === _ ? l(e, n) : e, i = {
159
168
  ...t,
160
169
  messages: t.messages.map((e, t) => t === r ? {
161
170
  ...e,
@@ -164,41 +173,41 @@ var te = 2e4, ne = (e, t, n) => e instanceof i ? n : e instanceof r ? t : e inst
164
173
  };
165
174
  return S.current = i, i;
166
175
  });
167
- }, Z = () => {
168
- I(), W.current.has(v) ? W.current.delete(v) : O() || D?.notifyGenerationEnd?.();
169
- }, Q = async (n) => {
170
- H.current.add(y);
171
- let i = await d(() => C.getConversation(e(t)), O);
172
- if (O()) {
173
- I();
176
+ }, K = () => {
177
+ M(), W.current.has(_) ? W.current.delete(_) : x() || D?.notifyGenerationEnd?.();
178
+ }, Z = async (n) => {
179
+ H.current.add(v);
180
+ let i = await ee(() => C.getConversation(e(t)), x);
181
+ if (x()) {
182
+ M();
174
183
  return;
175
184
  }
176
- let a = i?.messages[i.messages.length - 1], s = i != null && i.messages.length - 1 === r && a?.role === ee.Assistant;
185
+ let a = i?.messages[i.messages.length - 1], s = i != null && i.messages.length - 1 === r && a?.role === m.Assistant;
177
186
  if (!i || !s) {
178
- H.current.delete(y), K(n);
187
+ H.current.delete(v), R(n);
179
188
  return;
180
189
  }
181
- let c = U.current.get(y), l = c?.generationId === v ? c : void 0;
182
- if (p(i)) {
190
+ let c = U.current.get(v), l = c?.generationId === _ ? c : void 0;
191
+ if (f(i)) {
183
192
  X(t, i, {
184
193
  seedMessage: l?.message,
185
194
  skipDedupe: !0,
186
- onSettled: Z
195
+ onSettled: K
187
196
  });
188
197
  return;
189
198
  }
190
- H.current.delete(y), l && U.current.delete(y), q(y) && (o(i), S.current = i), Z();
199
+ H.current.delete(v), l && U.current.delete(v), q(v) && (o(i), S.current = i), K();
191
200
  };
192
201
  (async () => {
193
202
  try {
194
- let e = E?.channelId ?? await E?.waitForChannel(te) ?? void 0, t = W.current.has(v);
195
- if (j.signal.aborted || O() || t) {
196
- U.current.get(y)?.generationId === v && U.current.delete(y), O() || Y(y), B.current === v && (B.current = null, V.current = null, z(null)), T(y, v), E?.notifyGenerationSettled?.(), t ? W.current.delete(v) : O() || D?.notifyGenerationEnd?.();
203
+ let e = E?.channelId ?? await E?.waitForChannel(re) ?? void 0, t = W.current.has(_);
204
+ if (O.signal.aborted || x() || t) {
205
+ U.current.get(v)?.generationId === _ && U.current.delete(v), x() || Y(v), B.current === _ && (B.current = null, V.current = null, z(null)), T(v, _), E?.notifyGenerationSettled?.(), t ? W.current.delete(_) : x() || D?.notifyGenerationEnd?.();
197
206
  return;
198
207
  }
199
- C.streamCompletion(y, n, u, R, m, v, _, b, e);
208
+ C.streamCompletion(v, n, u, L, p, _, g, y, e);
200
209
  } catch (e) {
201
- R.onError(e instanceof Error ? e : Error(String(e)));
210
+ L.onError(e instanceof Error ? e : Error(String(e)));
202
211
  }
203
212
  })();
204
213
  }, [
@@ -217,34 +226,38 @@ var te = 2e4, ne = (e, t, n) => e instanceof i ? n : e instanceof r ? t : e inst
217
226
  k,
218
227
  A,
219
228
  F
220
- ]), Q = g((e, t) => {
229
+ ]), ae = h((e, t) => {
221
230
  let n = s(e), r = U.current.get(n);
222
231
  return r ? l(t, r) : t;
223
- }, []), re = g(() => {
224
- let e = B.current;
225
- if (!e || !r) return;
226
- let t = s(r);
227
- V.current === t && (W.current.add(e), F.flush(t), D?.notifyStopGenerating?.(), C.stopCompletion({
228
- generationId: e,
229
- path: t
232
+ }, []), oe = h(() => {
233
+ if (!r) return;
234
+ let e = s(r), t = (V.current === e ? B.current : null) ?? x(S.current);
235
+ if (!t) return;
236
+ W.current.add(t), F.flush(e), D?.notifyStopGenerating?.();
237
+ let n = U.current.get(e)?.message.content;
238
+ C.stopCompletion({
239
+ generationId: t,
240
+ path: e,
241
+ content: n
230
242
  }).catch((e) => {
231
243
  let t = e instanceof Error ? e : Error(String(e));
232
244
  O?.(t);
233
- }));
245
+ });
234
246
  }, [
235
247
  r,
248
+ S,
236
249
  F,
237
250
  O,
238
251
  D,
239
252
  C
240
- ]), ie = g((e, t) => X(e, t), [X]), ae = r != null && I.has(s(r)), $ = r == null ? null : s(r);
253
+ ]), se = h((e, t) => X(e, t), [X]), Q = r != null && I.has(s(r)), $ = r == null ? null : s(r);
241
254
  return {
242
255
  startStream: Z,
243
- handleStop: re,
244
- resumeIfAwaitingGeneration: ie,
245
- restoreBufferedGeneration: Q,
246
- isStreaming: ae,
247
- canStopStreaming: $ != null && R === $
256
+ handleStop: oe,
257
+ resumeIfAwaitingGeneration: se,
258
+ restoreBufferedGeneration: ae,
259
+ isStreaming: Q,
260
+ canStopStreaming: $ != null && (R === $ || Q && x(S.current) != null)
248
261
  };
249
262
  };
250
263
  //#endregion
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
 
@@ -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>>;
@@ -596,11 +601,12 @@ export declare const hasStructuredAnnouncement: ({ title, description, }: Announ
596
601
  export declare const isAnswerIncomplete: (answer: Message | undefined) => boolean;
597
602
 
598
603
  /**
599
- * True when the conversation's last message is an unresolved assistant
600
- * placeholder: the backend only persists a conversation at generation start
601
- * (empty placeholder) and at generation end (final content, or a partial
602
- * flagged `streamErrorMessage`/`wasStoppedByUser`). This can mean generation
603
- * is still active elsewhere, or that its terminal save failed.
604
+ * True when the conversation has a pending background message, or its last
605
+ * message is an unresolved assistant placeholder: the backend only persists a
606
+ * conversation at generation start (empty placeholder) and at generation end
607
+ * (final content, or a partial flagged `streamErrorMessage`/`wasStoppedByUser`).
608
+ * This can mean generation is still active elsewhere, or that its terminal save
609
+ * failed.
604
610
  */
605
611
  export declare const isAwaitingGenerationResume: (conversation: Conversation) => boolean;
606
612
 
@@ -40,6 +40,8 @@ export declare interface AttachmentCanvasUrlResolvers {
40
40
  resolveDialUrl: (attachment: DisplayAttachment) => string | undefined;
41
41
  /** Resolves a DIAL Core file id to a fetchable metadata URL (used for cache-freshness validation). */
42
42
  resolveDialFileMetadataUrl: (fileId: string) => string | undefined;
43
+ /** URL of a host-served bootstrap document that renders HTML with no download URL (inline `data`) under its own response CSP instead of the embedding page's — forwarded as `HtmlCanvasContent.srcdocHostUrl`. When omitted, that HTML renders via plain `srcdoc`. */
44
+ htmlSrcdocHostUrl?: string;
43
45
  }
44
46
 
45
47
  /** The type of content the canvas can display. */
@@ -138,6 +140,8 @@ declare interface HtmlCanvasContent {
138
140
  isSameOriginUrl?: boolean;
139
141
  /** Lazily fetches the full HTML source text for the "View source" toggle. Used instead of an eagerly-populated `srcdoc` when `url` is set; absent when `srcdoc` is already populated. */
140
142
  resolveSourceText?: () => Promise<string>;
143
+ /** Host-served bootstrap document used to render `srcdoc` without inheriting the embedding page's CSP. When set alongside `srcdoc`, the iframe loads this URL via `src` (still `sandbox="allow-scripts"`) and posts `{ type: HTML_PREVIEW_FRAME_RENDER_MESSAGE, html: srcdoc }` to it once loaded, so the HTML renders under that response's own policy. Ignored when `srcdoc` is absent or `isSameOriginUrl` selects `url`. */
144
+ srcdocHostUrl?: string;
141
145
  }
142
146
 
143
147
  /** Content payload for image attachments. */
@@ -343,8 +347,10 @@ export declare const resolveGroupedVisualizerCanvasContent: (attachments: Displa
343
347
  * front; `resolveSourceText` fetches it lazily, only when the "View source"
344
348
  * toggle is used, and rejects if that fetch fails. `isSameOriginUrl` reflects
345
349
  * a real comparison against the embedding document's own origin.
346
- * With no download URL (local file or inline data), falls back to `srcdoc`
347
- * only, rejecting payloads larger than 1 MiB to prevent browser truncation.
350
+ * With no download URL (local file or inline data), falls back to `srcdoc`,
351
+ * rejecting payloads larger than 1 MiB to prevent browser truncation, and
352
+ * forwards `resolvers.htmlSrcdocHostUrl` so that HTML can still render under
353
+ * its own response-level CSP.
348
354
  * Returns `null` if no source is available, or an `ErrorCanvasContent` when
349
355
  * that fallback fetch fails.
350
356
  */
@@ -145,7 +145,8 @@ var g = (e, t) => URL.createObjectURL(s(e, t)), _ = (e) => {
145
145
  let i = await k(e, n);
146
146
  return i == null ? null : typeof i == "string" ? i.length > P ? null : {
147
147
  type: t.Html,
148
- srcdoc: i
148
+ srcdoc: i,
149
+ srcdocHostUrl: n.htmlSrcdocHostUrl
149
150
  } : i;
150
151
  }, R = (e, t) => ({
151
152
  id: e,
package/index.d.ts CHANGED
@@ -359,6 +359,8 @@ export declare interface AttachmentCanvasUrlResolvers {
359
359
  resolveDialUrl: (attachment: DisplayAttachment) => string | undefined;
360
360
  /** Resolves a DIAL Core file id to a fetchable metadata URL (used for cache-freshness validation). */
361
361
  resolveDialFileMetadataUrl: (fileId: string) => string | undefined;
362
+ /** URL of a host-served bootstrap document that renders HTML with no download URL (inline `data`) under its own response CSP instead of the embedding page's — forwarded as `HtmlCanvasContent.srcdocHostUrl`. When omitted, that HTML renders via plain `srcdoc`. */
363
+ htmlSrcdocHostUrl?: string;
362
364
  }
363
365
 
364
366
  /** The type of content the canvas can display. */
@@ -779,6 +781,7 @@ export declare interface ChatStreamApi {
779
781
  stopCompletion: (dto: {
780
782
  generationId: string;
781
783
  path: string;
784
+ content?: string;
782
785
  }) => Promise<void>;
783
786
  }
784
787
 
@@ -934,10 +937,14 @@ export declare type ConversationStreamStarter = (conversationPath: string, userC
934
937
  export declare interface ConversationStreamTransport {
935
938
  /** Starts a completion; delivers chunks/completion/error through `options`, aborted via `options.signal`. */
936
939
  streamCompletion(path: string, message: string | undefined, model: string, options: StreamCompletionOptions, customContent?: MessageCustomContent, generationId?: string, mode?: SendCompletionDtoModeEnum, messageIndex?: number, clientChannelId?: string): void;
937
- /** Requests the backend stop an active generation. */
940
+ /**
941
+ * Requests the backend stop an active generation. `content` is the answer text shown
942
+ * so far; the backend saves it for a generation whose text it does not hold.
943
+ */
938
944
  stopCompletion(params: {
939
945
  generationId: string;
940
946
  path: string;
947
+ content?: string;
941
948
  }): Promise<void>;
942
949
  /** Opens a stream of resource-update events for `path`, until aborted via `signal`. */
943
950
  watchConversation(path: string, signal: AbortSignal): Promise<ReadableStream<Uint8Array>>;
@@ -2101,6 +2108,8 @@ declare interface HtmlCanvasContent {
2101
2108
  isSameOriginUrl?: boolean;
2102
2109
  /** Lazily fetches the full HTML source text for the "View source" toggle. Used instead of an eagerly-populated `srcdoc` when `url` is set; absent when `srcdoc` is already populated. */
2103
2110
  resolveSourceText?: () => Promise<string>;
2111
+ /** Host-served bootstrap document used to render `srcdoc` without inheriting the embedding page's CSP. When set alongside `srcdoc`, the iframe loads this URL via `src` (still `sandbox="allow-scripts"`) and posts `{ type: HTML_PREVIEW_FRAME_RENDER_MESSAGE, html: srcdoc }` to it once loaded, so the HTML renders under that response's own policy. Ignored when `srcdoc` is absent or `isSameOriginUrl` selects `url`. */
2112
+ srcdocHostUrl?: string;
2104
2113
  }
2105
2114
 
2106
2115
  /** Content payload for image attachments. */
@@ -2134,11 +2143,12 @@ export declare const initiateOAuthLogin: (auth: ToolsetOAuthSettings, toolsetId:
2134
2143
  export declare const isAnswerIncomplete: (answer: Message | undefined) => boolean;
2135
2144
 
2136
2145
  /**
2137
- * True when the conversation's last message is an unresolved assistant
2138
- * placeholder: the backend only persists a conversation at generation start
2139
- * (empty placeholder) and at generation end (final content, or a partial
2140
- * flagged `streamErrorMessage`/`wasStoppedByUser`). This can mean generation
2141
- * is still active elsewhere, or that its terminal save failed.
2146
+ * True when the conversation has a pending background message, or its last
2147
+ * message is an unresolved assistant placeholder: the backend only persists a
2148
+ * conversation at generation start (empty placeholder) and at generation end
2149
+ * (final content, or a partial flagged `streamErrorMessage`/`wasStoppedByUser`).
2150
+ * This can mean generation is still active elsewhere, or that its terminal save
2151
+ * failed.
2142
2152
  */
2143
2153
  export declare const isAwaitingGenerationResume: (conversation: Conversation) => boolean;
2144
2154
 
@@ -3446,8 +3456,10 @@ export declare const resolveGroupedVisualizerCanvasContent: (attachments: Displa
3446
3456
  * front; `resolveSourceText` fetches it lazily, only when the "View source"
3447
3457
  * toggle is used, and rejects if that fetch fails. `isSameOriginUrl` reflects
3448
3458
  * a real comparison against the embedding document's own origin.
3449
- * With no download URL (local file or inline data), falls back to `srcdoc`
3450
- * only, rejecting payloads larger than 1 MiB to prevent browser truncation.
3459
+ * With no download URL (local file or inline data), falls back to `srcdoc`,
3460
+ * rejecting payloads larger than 1 MiB to prevent browser truncation, and
3461
+ * forwards `resolvers.htmlSrcdocHostUrl` so that HTML can still render under
3462
+ * its own response-level CSP.
3451
3463
  * Returns `null` if no source is available, or an `ErrorCanvasContent` when
3452
3464
  * that fallback fetch fails.
3453
3465
  */
@@ -6150,9 +6162,9 @@ export declare interface UsePublishFoldersResult {
6150
6162
  */
6151
6163
  onCreatePublishFolder: (parentPath: string[], name: string) => Promise<void>;
6152
6164
  /**
6153
- * Records `folderPath` as a publish destination the user has actually used,
6154
- * so it keeps appearing in the tree on later publishes. Call it after a
6155
- * successful publish.
6165
+ * Records `folderPath` as the most recent publish destination (moving it to
6166
+ * the front if already remembered), so it keeps appearing in the tree on
6167
+ * later publishes. Call it after a successful publish.
6156
6168
  */
6157
6169
  rememberPublishFolder: (folderPath: string[]) => void;
6158
6170
  /** Heuristic write-access check for a folder path (see the TODO above). */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@epam/ai-dial-chat-hooks",
3
3
  "description": "Framework-level React hooks extracted from AI DIAL Chat for building custom chat interfaces",
4
- "version": "1.2.0-dev.253",
4
+ "version": "1.2.0-dev.258",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "main": "./index.js",
@@ -111,7 +111,7 @@
111
111
  }
112
112
  },
113
113
  "dependencies": {
114
- "@epam/ai-dial-chat-api-client": "1.2.0-dev.253",
114
+ "@epam/ai-dial-chat-api-client": "1.2.0-dev.258",
115
115
  "dompurify": "3.4.16",
116
116
  "fflate": "^0.8.3",
117
117
  "lru-cache": "^10.4.3",
@@ -120,21 +120,21 @@
120
120
  },
121
121
  "peerDependencies": {
122
122
  "react": "^19.2.8",
123
- "@epam/ai-dial-attachment-canvas": "1.2.0-dev.253",
124
- "@epam/ai-dial-attachment-input": "1.2.0-dev.253",
125
- "@epam/ai-dial-catalog": "1.2.0-dev.253",
126
- "@epam/ai-dial-chat-overlay": "1.2.0-dev.253",
127
- "@epam/ai-dial-chat-shared": "1.2.0-dev.253",
128
- "@epam/ai-dial-mcp-apps": "1.2.0-dev.253",
129
- "@epam/ai-dial-publish-panel": "1.2.0-dev.253",
130
- "@epam/ai-dial-quotations": "1.2.0-dev.253",
131
- "@epam/ai-dial-react-file-manager": "^0.3.0-dev.20",
132
- "@epam/ai-dial-scheduled-tasks": "1.2.0-dev.253",
133
- "@epam/ai-dial-share": "1.2.0-dev.253",
134
- "@epam/ai-dial-skill-editor": "1.2.0-dev.253",
135
- "@epam/ai-dial-source-panel": "1.2.0-dev.253",
136
- "@epam/ai-dial-ui-kit": "^0.15.0-dev.35",
137
- "@epam/ai-dial-usage-dashboard": "1.2.0-dev.253",
123
+ "@epam/ai-dial-attachment-canvas": "1.2.0-dev.258",
124
+ "@epam/ai-dial-attachment-input": "1.2.0-dev.258",
125
+ "@epam/ai-dial-catalog": "1.2.0-dev.258",
126
+ "@epam/ai-dial-chat-overlay": "1.2.0-dev.258",
127
+ "@epam/ai-dial-chat-shared": "1.2.0-dev.258",
128
+ "@epam/ai-dial-mcp-apps": "1.2.0-dev.258",
129
+ "@epam/ai-dial-publish-panel": "1.2.0-dev.258",
130
+ "@epam/ai-dial-quotations": "1.2.0-dev.258",
131
+ "@epam/ai-dial-react-file-manager": "^0.3.0-dev.22",
132
+ "@epam/ai-dial-scheduled-tasks": "1.2.0-dev.258",
133
+ "@epam/ai-dial-share": "1.2.0-dev.258",
134
+ "@epam/ai-dial-skill-editor": "1.2.0-dev.258",
135
+ "@epam/ai-dial-source-panel": "1.2.0-dev.258",
136
+ "@epam/ai-dial-ui-kit": "^0.15.0-dev.36",
137
+ "@epam/ai-dial-usage-dashboard": "1.2.0-dev.258",
138
138
  "@modelcontextprotocol/sdk": "^1.29.0",
139
139
  "@epam/pdf-highlighter-kit": "^0.0.19",
140
140
  "@mcp-ui/client": "^7.1.1"