@arbidocs/react 0.4.5 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +12 -0
  2. package/dist/artifacts/index.cjs +59 -23
  3. package/dist/artifacts/index.cjs.map +1 -1
  4. package/dist/artifacts/index.d.cts +18 -2
  5. package/dist/artifacts/index.d.ts +18 -2
  6. package/dist/artifacts/index.js +56 -21
  7. package/dist/artifacts/index.js.map +1 -1
  8. package/dist/{chunk-DVRJFLE7.js → chunk-2ACHNDO6.js} +5 -5
  9. package/dist/chunk-2ACHNDO6.js.map +1 -0
  10. package/dist/{chunk-2DXS7L7M.cjs → chunk-CBJ3WOGD.cjs} +18 -3
  11. package/dist/chunk-CBJ3WOGD.cjs.map +1 -0
  12. package/dist/{chunk-ZY53R6I4.js → chunk-KWCHIAQ5.js} +18 -4
  13. package/dist/chunk-KWCHIAQ5.js.map +1 -0
  14. package/dist/{chunk-7V7B7OEM.cjs → chunk-OI55JRKT.cjs} +44 -28
  15. package/dist/chunk-OI55JRKT.cjs.map +1 -0
  16. package/dist/{chunk-H7ZZQ4ZV.js → chunk-RIHMIEXM.js} +44 -29
  17. package/dist/chunk-RIHMIEXM.js.map +1 -0
  18. package/dist/{chunk-7BDGLRQX.cjs → chunk-Z5YCRJY3.cjs} +6 -6
  19. package/dist/chunk-Z5YCRJY3.cjs.map +1 -0
  20. package/dist/index-CC6CvXEs.d.cts +1366 -0
  21. package/dist/index-CC6CvXEs.d.ts +1366 -0
  22. package/dist/index.cjs +952 -322
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +588 -28
  25. package/dist/index.d.ts +588 -28
  26. package/dist/index.js +642 -42
  27. package/dist/index.js.map +1 -1
  28. package/dist/ui/index.cjs +131 -127
  29. package/dist/ui/index.d.cts +24 -1324
  30. package/dist/ui/index.d.ts +24 -1324
  31. package/dist/ui/index.js +2 -2
  32. package/package.json +3 -3
  33. package/dist/chunk-2DXS7L7M.cjs.map +0 -1
  34. package/dist/chunk-7BDGLRQX.cjs.map +0 -1
  35. package/dist/chunk-7V7B7OEM.cjs.map +0 -1
  36. package/dist/chunk-DVRJFLE7.js.map +0 -1
  37. package/dist/chunk-H7ZZQ4ZV.js.map +0 -1
  38. package/dist/chunk-ZY53R6I4.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import { useDocuments, useAssistantQuery, arbiQueryKeys, useWorkspaces, useParsedDocument, useTags, useActivity, useThumbnails } from './chunk-ZY53R6I4.js';
2
- export { DEFAULT_USD_PER_CREDIT, arbiQueryKeys, creditsToUsd, formatCredits, isUsageMetered, useActivity, useAgents, useAssistantQuery, useConfigs, useConversations, useDeleteAgents, useDocuments, useFacts, useHealth, useParsedDocument, useProjectInvoices, useProjectUsage, useProjects, useProjectsUsage, useSessions, useTags, useTasks, useThumbnails, useUserUsageToday, useWorkspaces } from './chunk-ZY53R6I4.js';
1
+ import { useDocuments, useConversations, useAssistantQuery, useConversationThreads, arbiQueryKeys, useWorkspaces, useParsedDocument, useTags, useActivity, useThumbnails } from './chunk-KWCHIAQ5.js';
2
+ export { DEFAULT_USD_PER_CREDIT, arbiQueryKeys, creditsToUsd, formatCredits, isUsageMetered, useActivity, useAgents, useAssistantQuery, useConfigs, useConversationThreads, useConversations, useDeleteAgents, useDocuments, useFacts, useHealth, useParsedDocument, useProjectInvoices, useProjectUsage, useProjects, useProjectsUsage, useSessions, useTags, useTasks, useThumbnails, useUserUsageToday, useWorkspaces } from './chunk-KWCHIAQ5.js';
3
3
  import { formatRelativeTime } from './chunk-FSMIN5VX.js';
4
4
  export { DEFAULT_NOTIFY_TONE, NotificationBell, Toaster, formatRelativeTime, notify, seedNotifications, selectUnread, toast, useNotify } from './chunk-FSMIN5VX.js';
5
- import { MarkMenu, AiMarkdown, Card, Button, Badge, TagEntity, Input, FILE_TYPE_COLORS, getEntityIcon, UserEntity, getEntityColorStyles, AgentBadge, Textarea, Label, Slider, Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DocumentEntity, DocumentCard, useAudioRecorder, MicWaveformIndicator, IconToggle, classifyMediaError, formatElapsed, PassageCard } from './chunk-DVRJFLE7.js';
6
- export { AgentBadge, AiMarkdown, ArbiBotHeadIcon, AudIcon, Avatar, AvatarFallback, AvatarImage, BASE_COLORS, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DictateProvider, DocumentCard, DocumentEntity, ENTITY_MAPPINGS, EmlIcon, EntityIcon, EntityPill, FILE_TYPE_COLORS, HoverCard, HoverCardContent, HoverCardTrigger, IconToggle, ImgIcon, InlineMicButton, Input, Label, MarkMenu, MicRecordingOverlay, MicWaveformIndicator, NEUTRAL_GRAY, PLACEHOLDER_COLOR, PassageCard, PdfIcon, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PresenceLabel, Progress, ProjectEntity, RadioGroup, RadioGroupItem, RagDot, RelevanceDot, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Slider, StatusPill, Switch, TAG_TYPE_ICONS, TAG_TYPE_LABELS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TagEntity, Textarea, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserEntity, UserMenu, VISIBILITY_ICONS, VidIcon, VisibilityIcon, badgeVariants, buttonVariants, cardVariants, configureEntityRegistry, configureRagLabels, configureStatusRegistry, formatElapsed, getDocIcon, getEntityColor, getEntityColorStyles, getEntityIcon, getEntityLabel, getStatusVariant, getTagTypeIcon, getTagTypeLabel, getWorkspaceColor, toggleVariants, useAudioRecorder } from './chunk-DVRJFLE7.js';
7
- import { normalizeCitationMarkup, MarkdownRenderer } from './chunk-H7ZZQ4ZV.js';
8
- export { MarkdownRenderer, MarkdownStreamingContext, markdownBaseComponents, normalizeCitationMarkup, splitMarkdownBlocks } from './chunk-H7ZZQ4ZV.js';
5
+ import { MarkMenu, AiMarkdown, Card, Button, Badge, TagEntity, Input, FILE_TYPE_COLORS, getEntityIcon, UserEntity, getEntityColorStyles, AgentBadge, Textarea, Label, Slider, Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DocumentEntity, DocumentCard, useAudioRecorder, MicWaveformIndicator, IconToggle, classifyMediaError, formatElapsed, PassageCard } from './chunk-2ACHNDO6.js';
6
+ export { AgentBadge, AiMarkdown, ArbiBotHeadIcon, AudIcon, Avatar, AvatarFallback, AvatarImage, BASE_COLORS, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DictateProvider, DocumentCard, DocumentEntity, ENTITY_MAPPINGS, EmlIcon, EntityIcon, EntityPill, FILE_TYPE_COLORS, HoverCard, HoverCardContent, HoverCardTrigger, IconToggle, ImgIcon, InlineMicButton, Input, Label, MarkMenu, MicRecordingOverlay, MicWaveformIndicator, NEUTRAL_GRAY, PLACEHOLDER_COLOR, PassageCard, PdfIcon, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PresenceLabel, Progress, ProjectEntity, RadioGroup, RadioGroupItem, RagDot, RelevanceDot, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Slider, StatusPill, Switch, TAG_TYPE_ICONS, TAG_TYPE_LABELS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, TagEntity, Textarea, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserEntity, UserMenu, VISIBILITY_ICONS, VidIcon, VisibilityIcon, badgeVariants, buttonVariants, cardVariants, configureEntityRegistry, configureRagLabels, configureStatusRegistry, formatElapsed, getDocIcon, getEntityColor, getEntityColorStyles, getEntityIcon, getEntityLabel, getStatusVariant, getTagTypeIcon, getTagTypeLabel, getWorkspaceColor, toggleVariants, useAudioRecorder } from './chunk-2ACHNDO6.js';
7
+ import { normalizeCitationMarkup, MarkdownRenderer } from './chunk-RIHMIEXM.js';
8
+ export { MarkdownRenderer, MarkdownStreamingContext, markdownBaseComponents, normalizeCitationMarkup, splitMarkdownBlocks, trimOpenCitation } from './chunk-RIHMIEXM.js';
9
9
  import { ScrollArea, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from './chunk-N5SVBDLC.js';
10
10
  export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ScrollArea, ScrollBar } from './chunk-N5SVBDLC.js';
11
11
  import { cn } from './chunk-SN5YV6FF.js';
@@ -14,11 +14,11 @@ import { AudioViewer, ImageViewer, VideoViewer } from './chunk-HDCRPPBW.js';
14
14
  import { useArbi } from './chunk-J4MX2W4H.js';
15
15
  export { ArbiProvider, useArbi } from './chunk-J4MX2W4H.js';
16
16
  import React, { memo, useState, createContext, useMemo, useCallback, useEffect, useRef, useLayoutEffect, useContext, useId, Fragment as Fragment$1 } from 'react';
17
+ import { resolveCitations, summarizeCitations, connectWithReconnect } from '@arbidocs/sdk/browser';
18
+ export { Arbi, ArbiError } from '@arbidocs/sdk/browser';
17
19
  import { Link, Trash2, CheckCheck, Check, Loader2, AlertTriangle, ShieldCheck, Copy, Sparkles, FileText, GitCompareArrows, Download, Image as Image$1, Search, CornerDownLeft, X, ArrowLeft, Send, Tags, Crown, Info, Activity, Gauge, Gift, PartyPopper, UserCheck, MessageCircleMore, FileX2, FileCheck2, Pencil, FolderPlus, SquareArrowOutUpRight, MessagesSquare, ArrowRight, MoreHorizontal, SquarePen, Mic, Pause, Play, Square, RotateCcw, Camera, Circle } from 'lucide-react';
18
20
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
19
- import { connectWithReconnect } from '@arbidocs/sdk/browser';
20
- export { Arbi, ArbiError } from '@arbidocs/sdk/browser';
21
- import { isMessageType } from '@arbidocs/client';
21
+ import { isMessageType, passwordChangeErrorMessage } from '@arbidocs/client';
22
22
  export { isMessageType } from '@arbidocs/client';
23
23
  import { create } from 'zustand';
24
24
  import { useQueryClient, useMutation, useQuery } from '@tanstack/react-query';
@@ -30,14 +30,67 @@ import Cropper from 'react-easy-crop';
30
30
  import { AgGridReact } from 'ag-grid-react';
31
31
  import { ModuleRegistry, AllCommunityModule } from 'ag-grid-community';
32
32
 
33
+ function previewText(content) {
34
+ return content.replace(/\s+/g, " ").trim().replace(/^(?:(?:[-*•>]|#{1,6})\s+)+/, "");
35
+ }
36
+ function toChatCitations(source) {
37
+ if (!source?.tools) return [];
38
+ const resolved = resolveCitations(source);
39
+ const summaries = summarizeCitations(resolved);
40
+ return resolved.map((r, i) => {
41
+ const summary = summaries[i];
42
+ return {
43
+ num: r.citationNum,
44
+ statement: summary.statement,
45
+ documentTitle: summary.docTitle,
46
+ page: summary.pageNumber,
47
+ passages: r.chunks.map((chunk) => ({
48
+ id: chunk.metadata.chunk_ext_id,
49
+ documentId: chunk.metadata.doc_ext_id ?? null,
50
+ documentTitle: chunk.metadata.doc_title ?? summary.docTitle,
51
+ page: chunk.metadata.page_number ?? null,
52
+ text: previewText(chunk.content)
53
+ }))
54
+ };
55
+ });
56
+ }
57
+
58
+ // src/chat/useArbiChat.ts
33
59
  var counter = 0;
34
60
  var nextId = (prefix) => `${prefix}-${Date.now().toString(36)}-${(counter++).toString(36)}`;
35
61
  var emptyThread = () => ({
36
- id: nextId("thread"),
62
+ key: nextId("thread"),
63
+ conversationId: null,
64
+ title: "",
65
+ messages: [],
66
+ responseId: null,
67
+ isLoaded: true
68
+ });
69
+ var storedThread = (conversationId) => ({
70
+ key: conversationId,
71
+ conversationId,
37
72
  title: "",
38
73
  messages: [],
39
- responseId: null
74
+ responseId: null,
75
+ isLoaded: false
40
76
  });
77
+ var publicId = (t) => t.conversationId ?? t.key;
78
+ function messagesFromHistory(history) {
79
+ return history.filter((m) => m.role !== "system" && m.content.trim() !== "").map((m) => {
80
+ if (m.role === "user") {
81
+ return { id: m.external_id, role: "user", content: m.content, status: "complete" };
82
+ }
83
+ const citations = toChatCitations({ tools: m.tools });
84
+ return {
85
+ id: m.external_id,
86
+ role: "assistant",
87
+ content: m.content,
88
+ status: m.status === "failed" ? "error" : "complete",
89
+ ...m.status === "failed" ? { error: "The assistant request failed." } : {},
90
+ ...citations.length > 0 ? { citations } : {}
91
+ };
92
+ });
93
+ }
41
94
  function titleFromQuestion(question, maxLength = 48) {
42
95
  const flat = question.replace(/\s+/g, " ").trim();
43
96
  if (flat.length <= maxLength) return flat;
@@ -47,29 +100,88 @@ function titleFromQuestion(question, maxLength = 48) {
47
100
  return `${words.replace(/[\s,;:.\-–—]+$/, "")}\u2026`;
48
101
  }
49
102
  var messageOf = (e, fallback) => e instanceof Error ? e.message : fallback;
103
+ var isAbort = (e) => e instanceof DOMException && e.name === "AbortError";
104
+ var TITLE_REFRESH_DELAYS_MS = [2500, 6e3, 12e3];
50
105
  function useArbiChat(options = {}) {
51
106
  const { workspaceId, model, titleLength = 48 } = options;
52
107
  const arbi = useArbi();
53
108
  const { data: docs = [], isLoading: isLoadingDocuments, refetch } = useDocuments(workspaceId);
109
+ const {
110
+ data: stored = [],
111
+ isLoading: isLoadingThreads,
112
+ refetch: refetchConversations
113
+ } = useConversations(workspaceId);
54
114
  const query = useAssistantQuery();
55
115
  const { mutateAsync } = query;
56
116
  const [threads, setThreads] = useState(() => [emptyThread()]);
57
- const [activeId, setActiveId] = useState(() => threads[0].id);
117
+ const [activeKey, setActiveKey] = useState(() => threads[0].key);
58
118
  const [deselected, setDeselected] = useState(/* @__PURE__ */ new Set());
59
119
  const [isRunning, setIsRunning] = useState(false);
60
120
  const [isUploading, setIsUploading] = useState(false);
61
121
  const [uploadError, setUploadError] = useState(null);
122
+ const [historyError, setHistoryError] = useState(null);
123
+ const abortRef = useRef(null);
124
+ const timersRef = useRef([]);
125
+ useEffect(() => {
126
+ const timers = timersRef.current;
127
+ return () => timers.forEach(clearTimeout);
128
+ }, []);
62
129
  const scopeRef = useRef(workspaceId);
63
130
  useEffect(() => {
64
131
  if (scopeRef.current === workspaceId) return;
65
132
  scopeRef.current = workspaceId;
66
133
  const fresh = emptyThread();
67
134
  setThreads([fresh]);
68
- setActiveId(fresh.id);
135
+ setActiveKey(fresh.key);
69
136
  setDeselected(/* @__PURE__ */ new Set());
70
137
  setUploadError(null);
138
+ setHistoryError(null);
71
139
  }, [workspaceId]);
72
- const active = threads.find((t) => t.id === activeId) ?? threads[0];
140
+ const active = threads.find((t) => t.key === activeKey) ?? threads[0];
141
+ const threadEntries = useMemo(() => {
142
+ const storedIds = new Set(stored.map((c) => c.external_id));
143
+ const byConversation = new Map(
144
+ threads.filter((t) => t.conversationId).map((t) => [t.conversationId, t])
145
+ );
146
+ const fresh = threads.filter((t) => !t.conversationId);
147
+ const justSaved = threads.filter((t) => t.conversationId && !storedIds.has(t.conversationId));
148
+ const persisted = [...stored].filter((c) => c.message_count > 0 || byConversation.has(c.external_id)).sort((a, b) => b.updated_at.localeCompare(a.updated_at)).map((c) => {
149
+ const local = byConversation.get(c.external_id);
150
+ return {
151
+ key: local?.key ?? c.external_id,
152
+ id: c.external_id,
153
+ title: c.title || local?.title || ""
154
+ };
155
+ });
156
+ return [
157
+ ...fresh.map((t) => ({ key: t.key, id: publicId(t), title: t.title })),
158
+ ...justSaved.map((t) => ({ key: t.key, id: publicId(t), title: t.title })),
159
+ ...persisted
160
+ ];
161
+ }, [threads, stored]);
162
+ const needsLoad = !!active.conversationId && !active.isLoaded;
163
+ const loaded = useConversationThreads(workspaceId, active.conversationId ?? void 0, {
164
+ enabled: needsLoad
165
+ });
166
+ useEffect(() => {
167
+ const data = loaded.data;
168
+ if (!needsLoad || !data || data.conversation_ext_id !== active.conversationId) return;
169
+ const primary = [...data.threads].sort((a, b) => b.history.length - a.history.length)[0];
170
+ setThreads(
171
+ (all) => all.map(
172
+ (t) => t.key === active.key ? {
173
+ ...t,
174
+ isLoaded: true,
175
+ messages: messagesFromHistory(primary?.history ?? []),
176
+ responseId: primary?.leaf_message_ext_id ?? null
177
+ } : t
178
+ )
179
+ );
180
+ }, [loaded.data, needsLoad, active.conversationId, active.key]);
181
+ useEffect(() => {
182
+ if (loaded.error)
183
+ setHistoryError(messageOf(loaded.error, "The conversation could not be loaded."));
184
+ }, [loaded.error]);
73
185
  const selectedIds = useMemo(
74
186
  () => docs.map((d) => d.external_id).filter((id) => !!id && !deselected.has(id)),
75
187
  [docs, deselected]
@@ -83,16 +195,23 @@ function useArbiChat(options = {}) {
83
195
  [docs, deselected]
84
196
  );
85
197
  const patchThread = useCallback(
86
- (id, patch) => setThreads((all) => all.map((t) => t.id === id ? patch(t) : t)),
198
+ (key, patch) => setThreads((all) => all.map((t) => t.key === key ? patch(t) : t)),
87
199
  []
88
200
  );
89
201
  const patchMessage = useCallback(
90
- (threadId, messageId, patch) => patchThread(threadId, (t) => ({
202
+ (threadKey, messageId, patch) => patchThread(threadKey, (t) => ({
91
203
  ...t,
92
204
  messages: t.messages.map((m) => m.id === messageId ? { ...m, ...patch } : m)
93
205
  })),
94
206
  [patchThread]
95
207
  );
208
+ const refreshTitles = useCallback(() => {
209
+ void refetchConversations();
210
+ timersRef.current.forEach(clearTimeout);
211
+ timersRef.current = TITLE_REFRESH_DELAYS_MS.map(
212
+ (delay) => setTimeout(() => void refetchConversations(), delay)
213
+ );
214
+ }, [refetchConversations]);
96
215
  const ask = useCallback(
97
216
  async (thread, text) => {
98
217
  const userMessage = {
@@ -107,11 +226,13 @@ function useArbiChat(options = {}) {
107
226
  content: "",
108
227
  status: "streaming"
109
228
  };
110
- patchThread(thread.id, (t) => ({
229
+ patchThread(thread.key, (t) => ({
111
230
  ...t,
112
231
  title: t.title || titleFromQuestion(text, titleLength),
113
232
  messages: [...t.messages, userMessage, answer]
114
233
  }));
234
+ const controller = new AbortController();
235
+ abortRef.current = controller;
115
236
  setIsRunning(true);
116
237
  let buffer = "";
117
238
  let steps = 0;
@@ -122,15 +243,34 @@ function useArbiChat(options = {}) {
122
243
  docIds: selectedIds,
123
244
  previousResponseId: thread.responseId,
124
245
  model,
246
+ signal: controller.signal,
247
+ onStreamStart: (data) => {
248
+ if (data.assistant_message_ext_id) {
249
+ patchThread(thread.key, (t) => ({
250
+ ...t,
251
+ responseId: data.assistant_message_ext_id ?? t.responseId
252
+ }));
253
+ }
254
+ },
255
+ onUserMessage: (ev) => {
256
+ patchThread(thread.key, (t) => ({
257
+ ...t,
258
+ conversationId: t.conversationId ?? ev.conversation_ext_id
259
+ }));
260
+ },
261
+ onMetadata: (meta) => {
262
+ const citations = toChatCitations(meta);
263
+ if (citations.length > 0) patchMessage(thread.key, answer.id, { citations });
264
+ },
125
265
  onToken: (token) => {
126
266
  buffer += token;
127
- patchMessage(thread.id, answer.id, { content: buffer });
267
+ patchMessage(thread.key, answer.id, { content: buffer });
128
268
  },
129
269
  onOutputItemDone: (ev) => {
130
270
  const type = ev.item.type;
131
271
  if (type === "function_call" || type === "reasoning") {
132
272
  steps += 1;
133
- patchMessage(thread.id, answer.id, { stepCount: steps });
273
+ patchMessage(thread.key, answer.id, { stepCount: steps });
134
274
  }
135
275
  },
136
276
  onError: (message) => {
@@ -140,37 +280,50 @@ function useArbiChat(options = {}) {
140
280
  const finalText = result.text || buffer;
141
281
  const failure = streamError || result.errors[0] || null;
142
282
  if (failure && !finalText) {
143
- patchMessage(thread.id, answer.id, { status: "error", error: failure });
283
+ patchMessage(thread.key, answer.id, { status: "error", error: failure });
144
284
  } else {
145
- patchMessage(thread.id, answer.id, {
285
+ const citations = toChatCitations(result.metadata);
286
+ patchMessage(thread.key, answer.id, {
146
287
  status: "complete",
147
288
  content: finalText,
148
- stepCount: result.agentSteps.length || steps || void 0
289
+ stepCount: result.agentSteps.length || steps || void 0,
290
+ ...citations.length > 0 ? { citations } : {}
149
291
  });
150
- patchThread(thread.id, (t) => ({
292
+ patchThread(thread.key, (t) => ({
151
293
  ...t,
152
294
  responseId: result.assistantMessageExtId ?? t.responseId
153
295
  }));
154
296
  }
155
297
  } catch (e) {
156
- patchMessage(thread.id, answer.id, {
157
- status: "error",
158
- error: messageOf(e, "The assistant request failed.")
159
- });
298
+ if (controller.signal.aborted || isAbort(e)) {
299
+ patchMessage(thread.key, answer.id, {
300
+ status: "complete",
301
+ content: buffer,
302
+ stopped: true
303
+ });
304
+ } else {
305
+ patchMessage(thread.key, answer.id, {
306
+ status: "error",
307
+ error: messageOf(e, "The assistant request failed.")
308
+ });
309
+ }
160
310
  } finally {
311
+ if (abortRef.current === controller) abortRef.current = null;
161
312
  setIsRunning(false);
313
+ refreshTitles();
162
314
  }
163
315
  },
164
- [mutateAsync, patchMessage, patchThread, selectedIds, model, titleLength]
316
+ [mutateAsync, patchMessage, patchThread, refreshTitles, selectedIds, model, titleLength]
165
317
  );
166
318
  const send = useCallback(
167
319
  async (text) => {
168
320
  const trimmed = text.trim();
169
- if (!trimmed || isRunning) return;
321
+ if (!trimmed || isRunning || !active.isLoaded) return;
170
322
  await ask(active, trimmed);
171
323
  },
172
324
  [active, ask, isRunning]
173
325
  );
326
+ const stop = useCallback(() => abortRef.current?.abort(), []);
174
327
  const lastMessage = active.messages[active.messages.length - 1];
175
328
  const error = lastMessage?.status === "error" ? lastMessage.error ?? null : null;
176
329
  const retry = useCallback(async () => {
@@ -180,23 +333,54 @@ function useArbiChat(options = {}) {
180
333
  const question = messages[messages.length - 2];
181
334
  if (failed?.status !== "error" || question?.role !== "user") return;
182
335
  const trimmed = { ...active, messages: messages.slice(0, -2) };
183
- patchThread(active.id, () => trimmed);
336
+ patchThread(active.key, () => trimmed);
184
337
  await ask(trimmed, question.content);
185
338
  }, [active, ask, isRunning, patchThread]);
186
339
  const newThread = useCallback(() => {
187
- if (active.messages.length === 0) return;
340
+ setHistoryError(null);
341
+ const blank = threads.find((t) => !t.conversationId && t.messages.length === 0);
342
+ if (blank) {
343
+ setActiveKey(blank.key);
344
+ return;
345
+ }
188
346
  const fresh = emptyThread();
189
347
  setThreads((all) => [fresh, ...all]);
190
- setActiveId(fresh.id);
191
- }, [active.messages.length]);
348
+ setActiveKey(fresh.key);
349
+ }, [threads]);
350
+ const selectThread = useCallback(
351
+ (id) => {
352
+ const entry = threadEntries.find((e) => e.id === id);
353
+ if (!entry) return;
354
+ setHistoryError(null);
355
+ setThreads((all) => all.some((t) => t.key === entry.key) ? all : [...all, storedThread(id)]);
356
+ setActiveKey(entry.key);
357
+ },
358
+ [threadEntries]
359
+ );
192
360
  const deleteThread = useCallback(
193
361
  (id) => {
194
- const rest = threads.filter((t) => t.id !== id);
195
- const next = rest.length > 0 ? rest : [emptyThread()];
196
- setThreads(next);
197
- if (id === activeId) setActiveId(next[0].id);
362
+ const entry = threadEntries.find((e) => e.id === id);
363
+ if (!entry) return;
364
+ const remove = () => {
365
+ const rest = threads.filter((t) => t.key !== entry.key);
366
+ const next = rest.length > 0 ? rest : [emptyThread()];
367
+ setThreads(next);
368
+ if (entry.key === active.key) setActiveKey(next[0].key);
369
+ };
370
+ const local = threads.find((t) => t.key === entry.key);
371
+ const conversationId = local ? local.conversationId : id;
372
+ if (!conversationId) return remove();
373
+ void (async () => {
374
+ try {
375
+ await arbi.conversations.delete(conversationId);
376
+ remove();
377
+ await refetchConversations();
378
+ } catch (e) {
379
+ setHistoryError(messageOf(e, "The conversation could not be deleted."));
380
+ }
381
+ })();
198
382
  },
199
- [threads, activeId]
383
+ [threadEntries, threads, active.key, arbi, refetchConversations]
200
384
  );
201
385
  const toggleDocument = useCallback((id) => {
202
386
  setDeselected((prev) => {
@@ -224,18 +408,22 @@ function useArbiChat(options = {}) {
224
408
  [arbi, refetch]
225
409
  );
226
410
  return {
227
- threads: threads.map(({ id, title }) => ({ id, title })),
228
- activeThreadId: active.id,
411
+ threads: threadEntries.map(({ id, title }) => ({ id, title })),
412
+ activeThreadId: publicId(active),
229
413
  messages: active.messages,
230
414
  isRunning,
231
415
  error,
416
+ isLoadingThread: needsLoad && !historyError,
417
+ isLoadingThreads,
418
+ historyError,
232
419
  documents,
233
420
  isLoadingDocuments,
234
421
  isUploading,
235
422
  uploadError,
236
423
  send,
237
424
  retry,
238
- selectThread: setActiveId,
425
+ stop,
426
+ selectThread,
239
427
  newThread,
240
428
  deleteThread,
241
429
  toggleDocument,
@@ -842,6 +1030,418 @@ function useRemoveContacts() {
842
1030
  onSuccess: () => qc.invalidateQueries({ queryKey: arbiQueryKeys.contacts() })
843
1031
  });
844
1032
  }
1033
+ function toProfileView(profile) {
1034
+ return {
1035
+ ...profile,
1036
+ fullName: [profile.givenName, profile.familyName].map((part) => part?.trim()).filter(Boolean).join(" ")
1037
+ };
1038
+ }
1039
+ function useProfile(options) {
1040
+ const arbi = useArbi();
1041
+ return useQuery({
1042
+ queryKey: arbiQueryKeys.profile(),
1043
+ queryFn: () => {
1044
+ const profile = arbi.user.profile();
1045
+ return profile ? toProfileView(profile) : null;
1046
+ },
1047
+ enabled: (options?.enabled ?? true) && arbi.isLoggedIn,
1048
+ staleTime: Infinity
1049
+ });
1050
+ }
1051
+ function useUpdateProfile() {
1052
+ const arbi = useArbi();
1053
+ const qc = useQueryClient();
1054
+ return useMutation({
1055
+ mutationFn: async (vars) => {
1056
+ await arbi.profile.update({
1057
+ ...vars.givenName !== void 0 && { given_name: vars.givenName.trim() },
1058
+ ...vars.familyName !== void 0 && { family_name: vars.familyName.trim() },
1059
+ ...vars.picture !== void 0 && { picture: vars.picture }
1060
+ });
1061
+ const profile = arbi.user.profile();
1062
+ return profile ? toProfileView(profile) : null;
1063
+ },
1064
+ onSuccess: (profile) => {
1065
+ qc.setQueryData(arbiQueryKeys.profile(), profile);
1066
+ return qc.invalidateQueries({ queryKey: arbiQueryKeys.workspaces() });
1067
+ }
1068
+ });
1069
+ }
1070
+ function useNameEditor({ givenName, familyName, save }) {
1071
+ const [isEditing, setIsEditing] = useState(false);
1072
+ const [given, setGiven] = useState("");
1073
+ const [family, setFamily] = useState("");
1074
+ const [isSaving, setIsSaving] = useState(false);
1075
+ const [error, setError] = useState(null);
1076
+ const onStartEdit = useCallback(() => {
1077
+ setGiven(givenName ?? "");
1078
+ setFamily(familyName ?? "");
1079
+ setError(null);
1080
+ setIsEditing(true);
1081
+ }, [givenName, familyName]);
1082
+ const onSave = useCallback(() => {
1083
+ const trimmedGiven = given.trim();
1084
+ if (!trimmedGiven) return;
1085
+ setIsSaving(true);
1086
+ setError(null);
1087
+ void Promise.resolve().then(() => save({ givenName: trimmedGiven, familyName: family.trim() })).then(() => setIsEditing(false)).catch((e) => setError(e)).finally(() => setIsSaving(false));
1088
+ }, [given, family, save]);
1089
+ return {
1090
+ isEditing,
1091
+ givenName: given,
1092
+ familyName: family,
1093
+ onGivenNameChange: setGiven,
1094
+ onFamilyNameChange: setFamily,
1095
+ onStartEdit,
1096
+ onSave,
1097
+ onCancel: () => setIsEditing(false),
1098
+ isSaving,
1099
+ error
1100
+ };
1101
+ }
1102
+ var MAX_AVATAR_FILE_BYTES = 5 * 1024 * 1024;
1103
+ function useAvatarCrop({
1104
+ upload,
1105
+ onProblem,
1106
+ maxBytes = MAX_AVATAR_FILE_BYTES
1107
+ }) {
1108
+ const [pendingImage, setPendingImage] = useState(null);
1109
+ const [isUploading, setIsUploading] = useState(false);
1110
+ const onFileChosen = useCallback(
1111
+ (file) => {
1112
+ if (!file.type.startsWith("image/")) return onProblem?.("not-image");
1113
+ if (file.size > maxBytes) return onProblem?.("too-large");
1114
+ const reader = new FileReader();
1115
+ reader.onload = () => setPendingImage(reader.result);
1116
+ reader.onerror = () => onProblem?.("unreadable");
1117
+ reader.readAsDataURL(file);
1118
+ },
1119
+ [maxBytes, onProblem]
1120
+ );
1121
+ const onCropComplete = useCallback(
1122
+ (result) => {
1123
+ setPendingImage(null);
1124
+ setIsUploading(true);
1125
+ void Promise.resolve().then(() => upload(result.dataUrl)).catch(() => {
1126
+ }).finally(() => setIsUploading(false));
1127
+ },
1128
+ [upload]
1129
+ );
1130
+ return {
1131
+ onFileChosen,
1132
+ pendingImage,
1133
+ onCropComplete,
1134
+ onCropCancel: () => setPendingImage(null),
1135
+ isUploading
1136
+ };
1137
+ }
1138
+ function useRequestEmailChange() {
1139
+ const arbi = useArbi();
1140
+ return useMutation({
1141
+ mutationFn: (newEmail) => arbi.account.requestEmailChange(newEmail)
1142
+ });
1143
+ }
1144
+ function useConfirmEmailChange() {
1145
+ const arbi = useArbi();
1146
+ const qc = useQueryClient();
1147
+ return useMutation({
1148
+ mutationFn: ({ newEmail, code }) => arbi.account.confirmEmailChange(newEmail, code),
1149
+ onSuccess: () => {
1150
+ const profile = arbi.user.profile();
1151
+ qc.setQueryData(arbiQueryKeys.profile(), profile ? toProfileView(profile) : null);
1152
+ }
1153
+ });
1154
+ }
1155
+ function useEmailChange({
1156
+ requestCode,
1157
+ confirm,
1158
+ cooldownSeconds: cooldownLength = 30
1159
+ }) {
1160
+ const [isOpen, setIsOpen] = useState(false);
1161
+ const [step, setStep] = useState("enter");
1162
+ const [newEmail, setNewEmail] = useState("");
1163
+ const [code, setCode] = useState("");
1164
+ const [cooldown, setCooldown] = useState(0);
1165
+ const [isSending, setIsSending] = useState(false);
1166
+ const [isConfirming, setIsConfirming] = useState(false);
1167
+ const [error, setError] = useState(null);
1168
+ useEffect(() => {
1169
+ if (cooldown <= 0) return;
1170
+ const timer = setTimeout(() => setCooldown((c) => c - 1), 1e3);
1171
+ return () => clearTimeout(timer);
1172
+ }, [cooldown]);
1173
+ const reset = useCallback(() => {
1174
+ setIsOpen(false);
1175
+ setStep("enter");
1176
+ setNewEmail("");
1177
+ setCode("");
1178
+ setError(null);
1179
+ }, []);
1180
+ const onSendCode = useCallback(() => {
1181
+ const email = newEmail.trim();
1182
+ if (!email) return;
1183
+ setIsSending(true);
1184
+ setError(null);
1185
+ void Promise.resolve().then(() => requestCode(email)).then(() => {
1186
+ setStep("verify");
1187
+ setCooldown(cooldownLength);
1188
+ }).catch((e) => setError(e)).finally(() => setIsSending(false));
1189
+ }, [newEmail, requestCode, cooldownLength]);
1190
+ const onConfirm = useCallback(() => {
1191
+ const email = newEmail.trim();
1192
+ const entered = code.trim();
1193
+ if (!email || !entered) return;
1194
+ setIsConfirming(true);
1195
+ setError(null);
1196
+ void Promise.resolve().then(() => confirm({ newEmail: email, code: entered })).then(reset).catch((e) => setError(e)).finally(() => setIsConfirming(false));
1197
+ }, [newEmail, code, confirm, reset]);
1198
+ return {
1199
+ isOpen,
1200
+ onStart: () => setIsOpen(true),
1201
+ step,
1202
+ newEmail,
1203
+ onNewEmailChange: setNewEmail,
1204
+ onSendCode,
1205
+ isSending,
1206
+ code,
1207
+ onCodeChange: setCode,
1208
+ onConfirm,
1209
+ isConfirming,
1210
+ cooldownSeconds: cooldown,
1211
+ onCancel: reset,
1212
+ error
1213
+ };
1214
+ }
1215
+ function usePasswordChange() {
1216
+ const arbi = useArbi();
1217
+ return useMutation({
1218
+ mutationFn: (vars) => arbi.account.changePassword(vars)
1219
+ });
1220
+ }
1221
+ var DEFAULT_PASSWORD_CHANGE_FORM_MESSAGES = {
1222
+ required: "All fields are required",
1223
+ mismatch: "New passwords do not match",
1224
+ sameAsCurrent: "New password must be different from current password",
1225
+ wrongCurrent: "Current password is incorrect.",
1226
+ wrongRecoveryKey: "Recovery key did not match this account. Try a different recovery file."
1227
+ };
1228
+ function usePasswordChangeForm({
1229
+ recoveryMode = false,
1230
+ getUnmetRequirement,
1231
+ submit,
1232
+ onClose,
1233
+ onRecoveryComplete,
1234
+ messages
1235
+ }) {
1236
+ const m = useMemo(() => ({ ...DEFAULT_PASSWORD_CHANGE_FORM_MESSAGES, ...messages }), [messages]);
1237
+ const [currentPassword, setCurrentPassword] = useState("");
1238
+ const [newPassword, setNewPassword] = useState("");
1239
+ const [confirmPassword, setConfirmPassword] = useState("");
1240
+ const [error, setError] = useState(null);
1241
+ const [isProcessing, setIsProcessing] = useState(false);
1242
+ const [result, setResult] = useState(null);
1243
+ const clear = useCallback(() => {
1244
+ setCurrentPassword("");
1245
+ setNewPassword("");
1246
+ setConfirmPassword("");
1247
+ }, []);
1248
+ const isSubmitDisabled = isProcessing || !recoveryMode && !currentPassword || !newPassword || !confirmPassword || getUnmetRequirement(newPassword) !== null || newPassword !== confirmPassword;
1249
+ const onSubmit = useCallback(() => {
1250
+ setError(null);
1251
+ if (!recoveryMode && !currentPassword || !newPassword || !confirmPassword) {
1252
+ return setError(m.required);
1253
+ }
1254
+ if (newPassword !== confirmPassword) return setError(m.mismatch);
1255
+ const unmet = getUnmetRequirement(newPassword);
1256
+ if (unmet !== null) return setError(unmet);
1257
+ if (!recoveryMode && currentPassword === newPassword) return setError(m.sameAsCurrent);
1258
+ setIsProcessing(true);
1259
+ void submit({ newPassword, currentPassword: recoveryMode ? void 0 : currentPassword }).then((outcome) => {
1260
+ clear();
1261
+ if (recoveryMode) {
1262
+ onClose();
1263
+ onRecoveryComplete?.();
1264
+ } else {
1265
+ setResult(outcome);
1266
+ }
1267
+ }).catch((e) => {
1268
+ const message = passwordChangeErrorMessage(e);
1269
+ const wrongCurrent = /invalid signature|current password|401/i.test(message);
1270
+ setError(wrongCurrent ? recoveryMode ? m.wrongRecoveryKey : m.wrongCurrent : message);
1271
+ }).finally(() => setIsProcessing(false));
1272
+ }, [
1273
+ recoveryMode,
1274
+ currentPassword,
1275
+ newPassword,
1276
+ confirmPassword,
1277
+ getUnmetRequirement,
1278
+ submit,
1279
+ clear,
1280
+ onClose,
1281
+ onRecoveryComplete,
1282
+ m
1283
+ ]);
1284
+ return {
1285
+ currentPassword,
1286
+ onCurrentPasswordChange: setCurrentPassword,
1287
+ newPassword,
1288
+ onNewPasswordChange: setNewPassword,
1289
+ confirmPassword,
1290
+ onConfirmPasswordChange: setConfirmPassword,
1291
+ error,
1292
+ isProcessing,
1293
+ isSubmitDisabled,
1294
+ onSubmit,
1295
+ result,
1296
+ onCancel: () => {
1297
+ clear();
1298
+ setError(null);
1299
+ setResult(null);
1300
+ onClose();
1301
+ },
1302
+ onDone: () => {
1303
+ setResult(null);
1304
+ onClose();
1305
+ }
1306
+ };
1307
+ }
1308
+ function toNotificationPreferences(stored, keys) {
1309
+ const checked = Object.fromEntries(keys.map((key) => [key, stored?.[key] ?? true]));
1310
+ return {
1311
+ masterChecked: keys.some((key) => checked[key]),
1312
+ checked,
1313
+ masterPatch: (value) => Object.fromEntries(keys.map((key) => [key, value])),
1314
+ categoryPatch: (key, value) => ({ [key]: value })
1315
+ };
1316
+ }
1317
+ function useUserSettings(options) {
1318
+ const arbi = useArbi();
1319
+ return useQuery({
1320
+ queryKey: arbiQueryKeys.userSettings(),
1321
+ queryFn: () => arbi.settings.get(),
1322
+ enabled: (options?.enabled ?? true) && arbi.isLoggedIn
1323
+ });
1324
+ }
1325
+ function useNotificationPreferences(keys, options) {
1326
+ const arbi = useArbi();
1327
+ const qc = useQueryClient();
1328
+ const settings = useUserSettings(options);
1329
+ const stored = settings.data?.email_notifications;
1330
+ const save = useMutation({
1331
+ mutationFn: (patch) => arbi.settings.update({ email_notifications: patch }),
1332
+ onMutate: async (patch) => {
1333
+ await qc.cancelQueries({ queryKey: arbiQueryKeys.userSettings() });
1334
+ const previous = qc.getQueryData(arbiQueryKeys.userSettings());
1335
+ qc.setQueryData(
1336
+ arbiQueryKeys.userSettings(),
1337
+ (old) => old ? { ...old, email_notifications: { ...old.email_notifications, ...patch } } : old
1338
+ );
1339
+ return { previous };
1340
+ },
1341
+ onError: (_err, _patch, context) => {
1342
+ qc.setQueryData(arbiQueryKeys.userSettings(), context?.previous);
1343
+ },
1344
+ onSettled: () => qc.invalidateQueries({ queryKey: arbiQueryKeys.userSettings() })
1345
+ });
1346
+ const model = useMemo(() => toNotificationPreferences(stored, keys), [stored, keys]);
1347
+ const setAll = useCallback(
1348
+ (value) => save.mutate(model.masterPatch(value)),
1349
+ [save, model]
1350
+ );
1351
+ const setCategory = useCallback(
1352
+ (key, value) => save.mutate(model.categoryPatch(key, value)),
1353
+ [save, model]
1354
+ );
1355
+ return {
1356
+ ...model,
1357
+ isLoading: settings.isLoading,
1358
+ isSaving: save.isPending,
1359
+ /** The master switch handler: every category on or off. */
1360
+ setAll,
1361
+ /** One category's handler. */
1362
+ setCategory
1363
+ };
1364
+ }
1365
+ function useTerminateSession() {
1366
+ const arbi = useArbi();
1367
+ const qc = useQueryClient();
1368
+ return useMutation({
1369
+ mutationFn: (sessionId) => arbi.sessions.terminate(sessionId),
1370
+ onSuccess: () => qc.invalidateQueries({ queryKey: arbiQueryKeys.sessions() })
1371
+ });
1372
+ }
1373
+ function useLogoutAllSessions() {
1374
+ const arbi = useArbi();
1375
+ const qc = useQueryClient();
1376
+ return useMutation({
1377
+ mutationFn: () => arbi.sessions.logoutAll(),
1378
+ onSuccess: () => qc.clear()
1379
+ });
1380
+ }
1381
+ function useCurrentSessionId() {
1382
+ return useArbi().sessions.currentId();
1383
+ }
1384
+ function parseDeleteAccountBlockers(error) {
1385
+ const body = error?.apiError ?? error;
1386
+ const detail = body?.detail;
1387
+ if (detail === "active_subscription") return [{ type: "active_subscription" }];
1388
+ if (detail && typeof detail === "object") {
1389
+ const d = detail;
1390
+ if (d.reason === "workspace_members" && Array.isArray(d.workspaces)) {
1391
+ return [{ type: "workspace_members", workspaces: d.workspaces.map(String) }];
1392
+ }
1393
+ }
1394
+ return [];
1395
+ }
1396
+ function useDeleteAccount() {
1397
+ const arbi = useArbi();
1398
+ return useMutation({ mutationFn: () => arbi.account.requestDeletion() });
1399
+ }
1400
+ function useDeleteAccountFlow({
1401
+ userEmail,
1402
+ remove,
1403
+ onClose,
1404
+ onDeleted,
1405
+ onError
1406
+ }) {
1407
+ const [step, setStep] = useState("warning");
1408
+ const [confirmEmail, setConfirmEmail] = useState("");
1409
+ const [blockers, setBlockers] = useState([]);
1410
+ const [isDeleting, setIsDeleting] = useState(false);
1411
+ const close = useCallback(() => {
1412
+ setStep("warning");
1413
+ setConfirmEmail("");
1414
+ setBlockers([]);
1415
+ onClose();
1416
+ }, [onClose]);
1417
+ const onSubmit = useCallback(() => {
1418
+ if (confirmEmail.toLowerCase() !== userEmail.toLowerCase()) return;
1419
+ setIsDeleting(true);
1420
+ void Promise.resolve().then(remove).then(() => {
1421
+ close();
1422
+ onDeleted();
1423
+ }).catch((error) => {
1424
+ const found = parseDeleteAccountBlockers(error);
1425
+ if (found.length > 0) {
1426
+ setBlockers(found);
1427
+ setStep("warning");
1428
+ } else {
1429
+ onError?.(error);
1430
+ }
1431
+ }).finally(() => setIsDeleting(false));
1432
+ }, [confirmEmail, userEmail, remove, close, onDeleted, onError]);
1433
+ return {
1434
+ step,
1435
+ blockers,
1436
+ confirmEmail,
1437
+ onConfirmEmailChange: setConfirmEmail,
1438
+ onContinue: () => setStep("confirm"),
1439
+ onBack: () => setStep("warning"),
1440
+ isDeleting,
1441
+ onSubmit,
1442
+ onClose: close
1443
+ };
1444
+ }
845
1445
  function useWorkspaceDocs(workspaceId) {
846
1446
  const { data: docs = [], isLoading, isError } = useDocuments(workspaceId);
847
1447
  const docIds = useMemo(
@@ -5067,6 +5667,6 @@ function hasVideoTrack(blob) {
5067
5667
  });
5068
5668
  }
5069
5669
 
5070
- export { ActivityFeed, AiActionCard, AiAnswer, AppleIcon, ArbiBotIcon, ArbiIcon, ArbiWebSocketProvider, ArtifactCanvas, ChatBubble, CitationRenderer, ClaudeIcon, CommandPalette, CommandPaletteProvider, ConversationList, DEFAULT_ASPECT_OPTIONS, DEFAULT_START_BY_EMAIL_LABELS, DateSeparator, DirectMessages, DmAvatar, DmComposer, DocIcon, DocQaPanel, DocSearch, DocumentCardMenu, DocumentGallery, DocumentReview, DocumentTable, DraftTitleControls, FileIcon, FireworksIcon, GeminiIcon, GitHubIcon, GoogleIcon, HtmlIcon, ImageCropModal, ImageCropper, LinkedInIcon, MessageArtifactCard, MessageBubble, MessageThread, OpenAIIcon, PhotoCapture, ProviderIcon, RUN_WINDOW_MS, SemanticSearch, StartByEmail, TxtIcon, TypingIndicator, VideoCapture, VoiceNoteRecorder, XlsIcon, annotateThread, captureFrameAsBlob, cropImage, dayKey, describeActivity, getNotificationTypeMeta, getWorkspaceActionText, getWorkspaceKind, getWorkspaceSentPhrase, hasVideoTrack, isWorkspaceNotificationType, parseActivityContent, pickRecentWorkspaces, redlineStats, resolveActiveWorkspace, textToArtifact, titleFromQuestion, toRedlineMarkdown, toShellWorkspace, useAddContacts, useAgentConfig, useAgentModels, useAgentWorkspaces, useAiTask, useArbiChat, useArbiConnected, useArbiDocumentSync, useArbiShellWorkspaces, useArbiUiStore, useArbiWebSocket, useArbiWsContext, useArbiWsMessage, useArbiWsStatus, useArbiWsStore, useArbiWsTask, useArbiWsTasks, useCameraStream, useCommandPalette, useContacts, useCreateAgent, useCreateTask, useDeleteTask, useDirectMessages, useGrantAgentAccess, usePendingContacts, useRemoveContacts, useRevokeAgentAccess, useRunTask, useSaveAgentConfig, useSemanticSearch, useShellWorkspaces, useSkills, useTaskRuns, useUpdateTask, useVideoRecorder, useWorkspaceDocs, userDisplayName, userInitials };
5670
+ export { ActivityFeed, AiActionCard, AiAnswer, AppleIcon, ArbiBotIcon, ArbiIcon, ArbiWebSocketProvider, ArtifactCanvas, ChatBubble, CitationRenderer, ClaudeIcon, CommandPalette, CommandPaletteProvider, ConversationList, DEFAULT_ASPECT_OPTIONS, DEFAULT_PASSWORD_CHANGE_FORM_MESSAGES, DEFAULT_START_BY_EMAIL_LABELS, DateSeparator, DirectMessages, DmAvatar, DmComposer, DocIcon, DocQaPanel, DocSearch, DocumentCardMenu, DocumentGallery, DocumentReview, DocumentTable, DraftTitleControls, FileIcon, FireworksIcon, GeminiIcon, GitHubIcon, GoogleIcon, HtmlIcon, ImageCropModal, ImageCropper, LinkedInIcon, MAX_AVATAR_FILE_BYTES, MessageArtifactCard, MessageBubble, MessageThread, OpenAIIcon, PhotoCapture, ProviderIcon, RUN_WINDOW_MS, SemanticSearch, StartByEmail, TxtIcon, TypingIndicator, VideoCapture, VoiceNoteRecorder, XlsIcon, annotateThread, captureFrameAsBlob, cropImage, dayKey, describeActivity, getNotificationTypeMeta, getWorkspaceActionText, getWorkspaceKind, getWorkspaceSentPhrase, hasVideoTrack, isWorkspaceNotificationType, parseActivityContent, parseDeleteAccountBlockers, pickRecentWorkspaces, redlineStats, resolveActiveWorkspace, textToArtifact, titleFromQuestion, toChatCitations, toNotificationPreferences, toProfileView, toRedlineMarkdown, toShellWorkspace, useAddContacts, useAgentConfig, useAgentModels, useAgentWorkspaces, useAiTask, useArbiChat, useArbiConnected, useArbiDocumentSync, useArbiShellWorkspaces, useArbiUiStore, useArbiWebSocket, useArbiWsContext, useArbiWsMessage, useArbiWsStatus, useArbiWsStore, useArbiWsTask, useArbiWsTasks, useAvatarCrop, useCameraStream, useCommandPalette, useConfirmEmailChange, useContacts, useCreateAgent, useCreateTask, useCurrentSessionId, useDeleteAccount, useDeleteAccountFlow, useDeleteTask, useDirectMessages, useEmailChange, useGrantAgentAccess, useLogoutAllSessions, useNameEditor, useNotificationPreferences, usePasswordChange, usePasswordChangeForm, usePendingContacts, useProfile, useRemoveContacts, useRequestEmailChange, useRevokeAgentAccess, useRunTask, useSaveAgentConfig, useSemanticSearch, useShellWorkspaces, useSkills, useTaskRuns, useTerminateSession, useUpdateProfile, useUpdateTask, useUserSettings, useVideoRecorder, useWorkspaceDocs, userDisplayName, userInitials };
5071
5671
  //# sourceMappingURL=index.js.map
5072
5672
  //# sourceMappingURL=index.js.map