@epam/ai-dial-chat-hooks 1.2.0-dev.2 → 1.2.0-dev.201
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api-error/api-error.js +5 -4
- package/attachment/useAttachmentValidation/useAttachmentValidation.js +26 -19
- package/attachments.d.ts +10 -4
- package/catalog/catalog-derivations.js +1 -1
- package/catalog/deployment-id.js +8 -2
- package/catalog/map-deployment-limits-to-input.js +81 -9
- package/catalog/map-deployment-to-catalog-item.js +34 -34
- package/catalog/map-entity-details-to-catalog.js +7 -0
- package/catalog/map-skill-to-catalog-item.js +30 -27
- package/catalog/publish.js +6 -5
- package/catalog/useApplicationCredentials/useApplicationCredentials.js +39 -0
- package/catalog/useSkillDetailsPanelData/useSkillDetailsPanelData.js +39 -39
- package/catalog/useSkillItemDetails.js +19 -17
- package/catalog.d.ts +134 -20
- package/catalog.js +23 -22
- package/conversation/announcement-message.js +16 -5
- package/conversation/conversation-transfer/export-conversation.js +24 -16
- package/conversation/conversation-transfer/import-conversation.js +80 -31
- package/conversation/conversation-transfer/queue.js +8 -5
- package/conversation/create-chat-stream-api.js +85 -49
- package/conversation/message-utils.js +44 -2
- package/conversation/stage.js +23 -0
- package/conversation/useAttachmentUpload/useAttachmentUpload.js +12 -5
- package/conversation/useConversationExport/useConversationExport.js +9 -9
- package/conversation/useConversationHandlers/useConversationHandlers.js +13 -17
- package/conversation/useConversationImport/useConversationImport.js +123 -121
- package/conversation/useConversationPanelItems/useConversationPanelItems.js +2 -3
- package/conversation/useConversationScroll/useConversationScroll.js +68 -64
- package/conversation/useConversationStream/apply-chunk.js +1 -1
- package/conversation/useConversationStream/generation-resume.js +104 -63
- package/conversation/useConversationStream/useConversationStream.js +183 -108
- package/conversation/useMessageSelectionReply/useMessageSelectionReply.js +189 -0
- package/conversation/useTranscribeAudio/transcription-retry.js +5 -4
- package/conversation-overlay.d.ts +7 -0
- package/conversation-overlay.js +2 -0
- package/conversation-sources/useConversationSources/useConversationSources.js +31 -24
- package/conversation-sources.d.ts +1 -1
- package/conversation-transfer.d.ts +2 -1
- package/conversation.d.ts +274 -17
- package/conversation.js +32 -30
- package/file-manager-canvas.d.ts +398 -0
- package/file-manager-canvas.js +2 -0
- package/file-manager.d.ts +153 -348
- package/file-manager.js +25 -24
- package/files/attachment-canvas.js +51 -13
- package/files/attachment-types.js +14 -13
- package/files/dial-file-manager-mapping.util.js +2 -1
- package/files/dial-file-manager-path.util.js +23 -1
- package/files/dial-file-manager.model.js +23 -19
- package/files/dial-file-manager.types.js +3 -3
- package/files/useDialFileListing/useDialFileListing.js +157 -145
- package/files/useDialFileManager/useDialFileManager.js +132 -128
- package/files/useDialFileManagerSections/useDialFileManagerSections.js +155 -0
- package/files/useDialFileManagerTabConfig/useDialFileManagerTabConfig.js +20 -19
- package/files/useDialFileMutations/useDialFileMutations.js +33 -32
- package/files/useDialFileUploadBatch/useDialFileUploadBatch.js +117 -110
- package/files/useFileAttachmentPicker/useFileAttachmentPicker.js +43 -0
- package/index.d.ts +1124 -90
- package/index.js +136 -122
- package/mcp-apps/useMcpAppHostAdapter/useMcpAppHostAdapter.js +7 -5
- package/mcp-apps/useMcpAppHostContext/useMcpAppHostContext.js +1 -1
- package/mcp-apps/useMcpAppTools/useMcpAppTools.js +1 -1
- package/mcp-apps/useOpenMcpAppCanvas/useOpenMcpAppCanvas.js +25 -23
- package/mcp-apps.d.ts +8 -6
- package/oauth/toolset-id.js +5 -3
- package/oauth/useToolsetLogin/useToolsetLogin.js +8 -8
- package/oauth.d.ts +32 -0
- package/oauth.js +9 -9
- package/package.json +36 -21
- package/scheduled-task/scheduled-task-description.js +77 -0
- package/scheduled-task/scheduled-task-preparation.js +24 -0
- package/scheduled-task/scheduled-task-trigger.js +7 -2
- package/scheduled-task/scheduled-tasks-api-client.js +19 -0
- package/scheduled-task/use-scheduled-task-runs.js +194 -0
- package/scheduled-task/use-scheduled-tasks.js +93 -0
- package/scheduled-tasks.d.ts +195 -3
- package/scheduled-tasks.js +7 -2
- package/skill/skill-archive.js +19 -0
- package/skill/skill-types.js +8 -7
- package/skill/skill.js +42 -27
- package/skill/useSkillArchiveImport/useSkillArchiveImport.js +75 -0
- package/skill/useSkillEditorLoad.js +29 -28
- package/skill/useSkillEditorSubmit.js +108 -84
- package/skill/useSkillFileActions.js +70 -58
- package/skill-editor.d.ts +131 -3
- package/skill-editor.js +9 -8
- package/usage/map-usage-data-to-dashboard.js +66 -0
- package/usage/map-user-usage-to-model-limits.js +202 -0
- package/usage.d.ts +125 -0
- package/usage.js +3 -0
- package/usePageFileDrag/usePageFileDrag.js +3 -3
- package/utils.d.ts +8 -6
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { useCallback as e, useEffect as t, useLayoutEffect as n, useMemo as r, useRef as i, useState as a } from "react";
|
|
2
|
+
//#region src/conversation/useMessageSelectionReply/useMessageSelectionReply.ts
|
|
3
|
+
var o = "button,input,textarea,select,iframe,[role=\"button\"],[contenteditable=\"true\"],[hidden],[inert],[aria-hidden=\"true\"]", s = [], c = (e, t) => e?.startContainer === t.startContainer && e.startOffset === t.startOffset && e.endContainer === t.endContainer && e.endOffset === t.endOffset, l = (e, t) => {
|
|
4
|
+
let n = window.visualViewport, r = n?.offsetLeft ?? 0, i = n?.offsetTop ?? 0, a = r + (n?.width ?? document.documentElement.clientWidth), o = i + (n?.height ?? document.documentElement.clientHeight);
|
|
5
|
+
for (let e = t; e; e = e.parentElement) {
|
|
6
|
+
let t = getComputedStyle(e);
|
|
7
|
+
if (t.display === "none" || t.visibility === "hidden") return;
|
|
8
|
+
let n = e.getBoundingClientRect();
|
|
9
|
+
/(hidden|clip|auto|scroll)/.test(t.overflowX) && (r = Math.max(r, n.left), a = Math.min(a, n.right)), /(hidden|clip|auto|scroll)/.test(t.overflowY) && (i = Math.max(i, n.top), o = Math.min(o, n.bottom));
|
|
10
|
+
}
|
|
11
|
+
return Array.from(e.getClientRects()).find((e) => e.width > 0 && e.height > 0 && e.left < a && e.right > r && e.top < o && e.bottom > i);
|
|
12
|
+
}, u = ({ conversationId: u, enabled: d, droppedFiles: f, onDroppedFilesConsumed: p }) => {
|
|
13
|
+
let m = i(/* @__PURE__ */ new Set()), h = i(null), g = i(null), _ = i(null), [v, y] = a(null), [b, x] = a({
|
|
14
|
+
conversationId: u,
|
|
15
|
+
files: []
|
|
16
|
+
}), [S, C] = a(), [w, T] = a(0), E = i(/* @__PURE__ */ new WeakSet()), D = i({
|
|
17
|
+
conversationId: u,
|
|
18
|
+
enabled: d
|
|
19
|
+
});
|
|
20
|
+
n(() => {
|
|
21
|
+
D.current = {
|
|
22
|
+
conversationId: u,
|
|
23
|
+
enabled: d
|
|
24
|
+
};
|
|
25
|
+
}, [u, d]);
|
|
26
|
+
let O = e(() => {
|
|
27
|
+
_.current = g.current?.range.cloneRange() ?? null, g.current = null, y(null);
|
|
28
|
+
}, []), k = e((e) => {
|
|
29
|
+
if (e) return m.current.add(e), () => {
|
|
30
|
+
m.current.delete(e), g.current?.body === e && O();
|
|
31
|
+
};
|
|
32
|
+
}, [O]);
|
|
33
|
+
t(() => {
|
|
34
|
+
O(), _.current = null, E.current = /* @__PURE__ */ new WeakSet(), x({
|
|
35
|
+
conversationId: u,
|
|
36
|
+
files: []
|
|
37
|
+
}), T(0), C(void 0);
|
|
38
|
+
}, [u, O]), t(() => {
|
|
39
|
+
if (!d) {
|
|
40
|
+
O(), x({
|
|
41
|
+
conversationId: u,
|
|
42
|
+
files: []
|
|
43
|
+
});
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
let e = 0, t = !1, n = !1, r = !1, i = () => {
|
|
47
|
+
if (e = 0, t || n || r || h.current?.contains(document.activeElement)) return;
|
|
48
|
+
let i = window.getSelection();
|
|
49
|
+
if (!i || i.rangeCount !== 1 || i.isCollapsed || !i.toString().trim()) {
|
|
50
|
+
O();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
let a = i.getRangeAt(0);
|
|
54
|
+
if (c(_.current, a)) return;
|
|
55
|
+
let s = [...m.current].find((e) => e.contains(a.startContainer) && e.contains(a.endContainer));
|
|
56
|
+
if (!s || !s.isConnected) {
|
|
57
|
+
O();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
let d = a.cloneContents();
|
|
61
|
+
d.querySelectorAll(o).forEach((e) => e.remove());
|
|
62
|
+
let f = a.commonAncestorContainer;
|
|
63
|
+
if ((f instanceof Element ? f : f.parentElement)?.closest(o) || !d.textContent?.trim()) {
|
|
64
|
+
O();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
let p = l(a, s);
|
|
68
|
+
if (!p) {
|
|
69
|
+
O();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
let v = {
|
|
73
|
+
conversationId: u,
|
|
74
|
+
body: s,
|
|
75
|
+
range: a.cloneRange(),
|
|
76
|
+
text: i.toString(),
|
|
77
|
+
rect: p
|
|
78
|
+
};
|
|
79
|
+
g.current = v, y(v);
|
|
80
|
+
}, a = () => {
|
|
81
|
+
e && cancelAnimationFrame(e), e = requestAnimationFrame(i);
|
|
82
|
+
}, s = () => {
|
|
83
|
+
e && cancelAnimationFrame(e), e = requestAnimationFrame(() => {
|
|
84
|
+
e = 0;
|
|
85
|
+
let t = g.current;
|
|
86
|
+
if (!t) return;
|
|
87
|
+
let n = l(t.range, t.body);
|
|
88
|
+
if (!n) O();
|
|
89
|
+
else {
|
|
90
|
+
let e = {
|
|
91
|
+
...t,
|
|
92
|
+
rect: n
|
|
93
|
+
};
|
|
94
|
+
g.current = e, y(e);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}, f = (e) => {
|
|
98
|
+
t = h.current?.contains(e.target) ?? !1, n = !t, t || (O(), [...m.current].some((t) => t.contains(e.target)) && (_.current = null));
|
|
99
|
+
}, p = () => {
|
|
100
|
+
n = !1, t || a(), t = !1;
|
|
101
|
+
}, v = () => {
|
|
102
|
+
n = !1, r = !1, t = !1, O();
|
|
103
|
+
let e = window.getSelection();
|
|
104
|
+
_.current = e?.rangeCount ? e.getRangeAt(0).cloneRange() : null;
|
|
105
|
+
}, b = (e) => {
|
|
106
|
+
if (!h.current?.contains(document.activeElement) && (e.shiftKey && [
|
|
107
|
+
"ArrowLeft",
|
|
108
|
+
"ArrowRight",
|
|
109
|
+
"ArrowUp",
|
|
110
|
+
"ArrowDown",
|
|
111
|
+
"Home",
|
|
112
|
+
"End",
|
|
113
|
+
"PageUp",
|
|
114
|
+
"PageDown"
|
|
115
|
+
].includes(e.key) || (e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "a")) {
|
|
116
|
+
r = !0, O(), _.current = null;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (g.current) {
|
|
120
|
+
if (e.key === "Escape") {
|
|
121
|
+
O();
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (e.key === "Tab" && !e.shiftKey && !h.current?.contains(document.activeElement)) {
|
|
125
|
+
let t = h.current?.querySelector("button");
|
|
126
|
+
t && (e.preventDefault(), t.focus());
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}, S = (e) => {
|
|
130
|
+
r && !e.shiftKey && (r = !1), e.key !== "Escape" && e.key !== "Tab" && a();
|
|
131
|
+
}, C = new MutationObserver((e) => {
|
|
132
|
+
let t = g.current;
|
|
133
|
+
t && (!t.body.isConnected || e.some((e) => t.body.contains(e.target))) && O();
|
|
134
|
+
});
|
|
135
|
+
return C.observe(document.body, {
|
|
136
|
+
subtree: !0,
|
|
137
|
+
childList: !0,
|
|
138
|
+
characterData: !0,
|
|
139
|
+
attributes: !0,
|
|
140
|
+
attributeFilter: [
|
|
141
|
+
"class",
|
|
142
|
+
"style",
|
|
143
|
+
"hidden",
|
|
144
|
+
"inert"
|
|
145
|
+
]
|
|
146
|
+
}), document.addEventListener("selectionchange", a), document.addEventListener("pointerdown", f, !0), document.addEventListener("pointerup", p, !0), document.addEventListener("pointercancel", v, !0), window.addEventListener("blur", v), document.addEventListener("keydown", b), document.addEventListener("keyup", S), document.addEventListener("scroll", s, !0), window.addEventListener("resize", s), window.visualViewport?.addEventListener("resize", s), window.visualViewport?.addEventListener("scroll", s), () => {
|
|
147
|
+
cancelAnimationFrame(e), C.disconnect(), document.removeEventListener("selectionchange", a), document.removeEventListener("pointerdown", f, !0), document.removeEventListener("pointerup", p, !0), document.removeEventListener("pointercancel", v, !0), window.removeEventListener("blur", v), document.removeEventListener("keydown", b), document.removeEventListener("keyup", S), document.removeEventListener("scroll", s, !0), window.removeEventListener("resize", s), window.visualViewport?.removeEventListener("resize", s), window.visualViewport?.removeEventListener("scroll", s);
|
|
148
|
+
};
|
|
149
|
+
}, [
|
|
150
|
+
u,
|
|
151
|
+
O,
|
|
152
|
+
d
|
|
153
|
+
]);
|
|
154
|
+
let A = e(() => {
|
|
155
|
+
let e = g.current;
|
|
156
|
+
if (!D.current.enabled || !e || e.conversationId !== D.current.conversationId || !e.body.isConnected || !l(e.range, e.body)) return;
|
|
157
|
+
let t = new File([e.text], `reply-${crypto.randomUUID()}.txt`, { type: "text/plain" });
|
|
158
|
+
E.current.add(t), T(0), x((n) => ({
|
|
159
|
+
conversationId: e.conversationId,
|
|
160
|
+
files: [...n.conversationId === e.conversationId ? n.files : [], t]
|
|
161
|
+
})), C((e) => (e ?? 0) + 1), O(), window.getSelection()?.removeAllRanges();
|
|
162
|
+
}, [O]), j = d && b.conversationId === u ? b.files : s, M = r(() => [...f, ...j], [f, j]), N = e(() => {
|
|
163
|
+
f.length && p(), x((e) => ({
|
|
164
|
+
...e,
|
|
165
|
+
files: e.files.filter((e) => !j.includes(e))
|
|
166
|
+
}));
|
|
167
|
+
}, [
|
|
168
|
+
f,
|
|
169
|
+
p,
|
|
170
|
+
j
|
|
171
|
+
]), P = e((e) => {
|
|
172
|
+
let t = e.filter((e) => e.file && E.current.has(e.file));
|
|
173
|
+
t.length && (t.forEach((e) => E.current.delete(e.file)), T((e) => e + 1));
|
|
174
|
+
}, []);
|
|
175
|
+
return {
|
|
176
|
+
contentRef: k,
|
|
177
|
+
actionRef: h,
|
|
178
|
+
selection: d && v?.conversationId === u ? v : null,
|
|
179
|
+
onReply: A,
|
|
180
|
+
dismiss: O,
|
|
181
|
+
pendingFiles: M,
|
|
182
|
+
onFilesConsumed: N,
|
|
183
|
+
focusRequestId: S,
|
|
184
|
+
onAttachmentsChange: P,
|
|
185
|
+
addedRevision: w
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
//#endregion
|
|
189
|
+
export { u as useMessageSelectionReply };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AudioTranscriptionError as e, AudioTranscriptionErrorReason as t } from "./audio-transcription-error.js";
|
|
2
2
|
//#region src/conversation/useTranscribeAudio/transcription-retry.ts
|
|
3
|
-
var n = 2, r =
|
|
3
|
+
var n = 2, r = 6e3, i = (e, t) => (t.throwIfAborted(), new Promise((n, r) => {
|
|
4
4
|
let i = () => {
|
|
5
5
|
clearTimeout(a), r(t.reason);
|
|
6
6
|
}, a = setTimeout(() => {
|
|
@@ -28,9 +28,10 @@ var n = 2, r = 9e4, i = (e, t) => (t.throwIfAborted(), new Promise((n, r) => {
|
|
|
28
28
|
503,
|
|
29
29
|
504
|
|
30
30
|
].includes(u.status)) throw o;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
c
|
|
31
|
+
if ([429, 503].includes(u.status)) throw new e(t.Busy);
|
|
32
|
+
let d = Math.max(1e3, a(u.headers.get("retry-after")) ?? 2e3 * 2 ** l);
|
|
33
|
+
if (l >= n || c + d > r) throw new e(t.Busy);
|
|
34
|
+
c += d, await i(d, s);
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Message } from '@epam/ai-dial-chat-shared';
|
|
2
|
+
import { OverlayChatMessage } from '@epam/ai-dial-chat-overlay';
|
|
3
|
+
|
|
4
|
+
/** Maps chat messages to the DIAL Chat Overlay protocol's message shape. */
|
|
5
|
+
export declare const toOverlayMessages: (messages: Message[]) => OverlayChatMessage[];
|
|
6
|
+
|
|
7
|
+
export { }
|
|
@@ -1,35 +1,42 @@
|
|
|
1
1
|
import { MessageRole as e, messageAttachmentToDisplayAttachment as t } from "@epam/ai-dial-chat-shared";
|
|
2
|
-
import {
|
|
3
|
-
import { useMemo as
|
|
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
|
|
6
|
-
let
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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 (
|
|
14
|
-
let 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,
|
|
17
|
-
|
|
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
|
|
20
|
-
if (!
|
|
24
|
+
for (let e of n ?? []) {
|
|
25
|
+
if (!r(e)) continue;
|
|
21
26
|
let t = e.reference_url;
|
|
22
|
-
|
|
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
|
|
34
|
+
for (let e of a(p)) {
|
|
30
35
|
let t = e?.body?.source?.attachment, n = t?.url;
|
|
31
|
-
|
|
32
|
-
|
|
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:
|
|
42
|
-
generated:
|
|
43
|
-
sources:
|
|
48
|
+
uploaded: o,
|
|
49
|
+
generated: s,
|
|
50
|
+
sources: l
|
|
44
51
|
};
|
|
45
|
-
}, [
|
|
52
|
+
}, [n, i]);
|
|
46
53
|
//#endregion
|
|
47
|
-
export {
|
|
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),
|
|
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.
|