@cntyclub/agent-react 0.16.0 → 0.18.0
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/dist/api-client.d.ts +67 -9
- package/dist/api-client.d.ts.map +1 -1
- package/dist/components/agent-panel.d.ts.map +1 -1
- package/dist/components/message-item.d.ts +5 -1
- package/dist/components/message-item.d.ts.map +1 -1
- package/dist/components/thinking-indicator.d.ts.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +582 -133
- package/dist/index.js.map +1 -1
- package/dist/realtime-client.d.ts +41 -0
- package/dist/realtime-client.d.ts.map +1 -0
- package/dist/types.d.ts +183 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/use-agent-chat.d.ts +24 -1
- package/dist/use-agent-chat.d.ts.map +1 -1
- package/dist/use-smooth-text.d.ts +17 -0
- package/dist/use-smooth-text.d.ts.map +1 -0
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
|
-
import { cn, Button, ThinkingOrb, Tooltip, TooltipTrigger, TooltipContent, ChatMessage, ChatMessageBubble, Markdown, AuroraText, Spinner, Checkbox, Collapsible, CollapsibleTrigger, ChatToolChip, CollapsiblePanel, ChatToolCard, ChatToolCardHeader, ChatToolCardActions, Group, Menu, MenuTrigger, MenuPopup, MenuItem, ChatHeader, ChatHeaderAvatar, ChatHeaderTitle, ChatHeaderActions, Chat, ChatEmptyState, ChatEmptyStateIcon, ChatEmptyStateTitle, ChatEmptyStateDescription, ChatSuggestions, ChatSuggestion, useMediaQuery, TooltipProvider, DataTablePaged } from '@cntyclub/ui-react';
|
|
1
|
+
import { cn, Button, ThinkingOrb, Tooltip, TooltipTrigger, TooltipContent, ChatMessage, ChatMessageBubble, Markdown, AuroraText, Spinner, Checkbox, Collapsible, CollapsibleTrigger, ChatToolChip, CollapsiblePanel, ChatToolCard, ChatToolCardHeader, ChatToolCardActions, Group, Menu, MenuTrigger, MenuPopup, MenuItem, ChatHeader, ChatHeaderAvatar, ChatHeaderTitle, ChatHeaderActions, ContextMeter, Chat, ChatEmptyState, ChatEmptyStateIcon, ChatEmptyStateTitle, ChatEmptyStateDescription, ChatSuggestions, ChatSuggestion, useMediaQuery, TooltipProvider, DataTablePaged } from '@cntyclub/ui-react';
|
|
2
2
|
import { XIcon, Trash2Icon, SquareIcon, PauseIcon, PlayIcon, CheckIcon, FileTextIcon, PaperclipIcon, MicIcon, ArrowUpIcon, CopyIcon, Maximize2Icon, ListChecksIcon, ChevronDownIcon, ShieldAlertIcon, CheckCheckIcon, UploadCloudIcon, HistoryIcon, SquarePenIcon, Minimize2Icon, ChevronsRightIcon, SparklesIcon, DownloadIcon, CodeIcon } from 'lucide-react';
|
|
3
|
-
import * as
|
|
3
|
+
import * as React12 from 'react';
|
|
4
4
|
import { AnimatePresence, motion } from 'motion/react';
|
|
5
5
|
import { createPortal } from 'react-dom';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
7
|
|
|
8
8
|
// src/api-client.ts
|
|
9
|
+
function collabBody(options) {
|
|
10
|
+
if (!options) return {};
|
|
11
|
+
const body = {};
|
|
12
|
+
if (options.idempotencyKey) body.idempotency_key = options.idempotencyKey;
|
|
13
|
+
if (options.replyToId) body.reply_to_id = options.replyToId;
|
|
14
|
+
if (options.threadRootId) body.thread_root_id = options.threadRootId;
|
|
15
|
+
if (options.mentionUserIds?.length) body.mention_user_ids = options.mentionUserIds;
|
|
16
|
+
return body;
|
|
17
|
+
}
|
|
18
|
+
function appendCollab(form, options) {
|
|
19
|
+
if (!options) return;
|
|
20
|
+
if (options.idempotencyKey) form.append("idempotency_key", options.idempotencyKey);
|
|
21
|
+
if (options.replyToId) form.append("reply_to_id", options.replyToId);
|
|
22
|
+
if (options.threadRootId) form.append("thread_root_id", options.threadRootId);
|
|
23
|
+
for (const id of options.mentionUserIds ?? []) form.append("mention_user_ids", id);
|
|
24
|
+
}
|
|
9
25
|
var AgentApiError = class extends Error {
|
|
10
26
|
status;
|
|
11
27
|
constructor(message, status) {
|
|
@@ -65,20 +81,22 @@ var AgentApiClient = class {
|
|
|
65
81
|
method: "POST"
|
|
66
82
|
});
|
|
67
83
|
}
|
|
68
|
-
sendMessage(message, conversationId, files) {
|
|
84
|
+
sendMessage(message, conversationId, files, options) {
|
|
69
85
|
if (files && files.length > 0) {
|
|
70
86
|
const form = new FormData();
|
|
71
87
|
form.append("client_id", this.config.clientId);
|
|
72
88
|
form.append("message", message);
|
|
73
89
|
if (conversationId) form.append("conversation_id", conversationId);
|
|
74
90
|
for (const file of files) form.append("files", file, file.name);
|
|
91
|
+
appendCollab(form, options);
|
|
75
92
|
return this.request("/agent-mode/chat/", { body: form, method: "POST" });
|
|
76
93
|
}
|
|
77
94
|
return this.request("/agent-mode/chat/", {
|
|
78
95
|
body: {
|
|
79
96
|
client_id: this.config.clientId,
|
|
80
97
|
conversation_id: conversationId ?? null,
|
|
81
|
-
message
|
|
98
|
+
message,
|
|
99
|
+
...collabBody(options)
|
|
82
100
|
},
|
|
83
101
|
method: "POST"
|
|
84
102
|
});
|
|
@@ -91,7 +109,7 @@ var AgentApiClient = class {
|
|
|
91
109
|
}
|
|
92
110
|
// ------------------------------------------------------------- streaming
|
|
93
111
|
/** POST a chat turn and yield parsed SSE events as they stream in. */
|
|
94
|
-
async streamMessage(message, conversationId, files, onEvent, signal) {
|
|
112
|
+
async streamMessage(message, conversationId, files, onEvent, signal, options) {
|
|
95
113
|
let body;
|
|
96
114
|
const headers = await this.streamHeaders();
|
|
97
115
|
if (files && files.length > 0) {
|
|
@@ -100,13 +118,15 @@ var AgentApiClient = class {
|
|
|
100
118
|
form.append("message", message);
|
|
101
119
|
if (conversationId) form.append("conversation_id", conversationId);
|
|
102
120
|
for (const file of files) form.append("files", file, file.name);
|
|
121
|
+
appendCollab(form, options);
|
|
103
122
|
body = form;
|
|
104
123
|
} else {
|
|
105
124
|
headers["Content-Type"] = "application/json";
|
|
106
125
|
body = JSON.stringify({
|
|
107
126
|
client_id: this.config.clientId,
|
|
108
127
|
conversation_id: conversationId ?? null,
|
|
109
|
-
message
|
|
128
|
+
message,
|
|
129
|
+
...collabBody(options)
|
|
110
130
|
});
|
|
111
131
|
}
|
|
112
132
|
await this.consumeStream("/agent-mode/chat/stream/", { body, headers, signal }, onEvent);
|
|
@@ -184,14 +204,219 @@ var AgentApiClient = class {
|
|
|
184
204
|
listConversations() {
|
|
185
205
|
return this.request("/agent-mode/conversations/");
|
|
186
206
|
}
|
|
187
|
-
getConversation(conversationId) {
|
|
188
|
-
|
|
207
|
+
getConversation(conversationId, options) {
|
|
208
|
+
const qs = new URLSearchParams();
|
|
209
|
+
if (options?.beforeSeq != null) qs.set("before_seq", String(options.beforeSeq));
|
|
210
|
+
if (options?.limit != null) qs.set("limit", String(options.limit));
|
|
211
|
+
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
212
|
+
return this.request(`/agent-mode/conversations/${conversationId}/${suffix}`);
|
|
189
213
|
}
|
|
214
|
+
/** Soft-delete (creator/admin) or leave (member). Never a hard delete. */
|
|
190
215
|
deleteConversation(conversationId) {
|
|
191
216
|
return this.request(`/agent-mode/conversations/${conversationId}/`, {
|
|
192
217
|
method: "DELETE"
|
|
193
218
|
});
|
|
194
219
|
}
|
|
220
|
+
// ------------------------------------------------------------- collaboration
|
|
221
|
+
setVisibility(conversationId, visibility, scope) {
|
|
222
|
+
return this.request(`/agent-mode/conversations/${conversationId}/visibility/`, {
|
|
223
|
+
method: "POST",
|
|
224
|
+
body: {
|
|
225
|
+
client_id: this.config.clientId,
|
|
226
|
+
visibility,
|
|
227
|
+
scope_type: scope?.scopeType ?? "none",
|
|
228
|
+
scope_event_id: scope?.scopeEventId
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
listParticipants(conversationId) {
|
|
233
|
+
return this.request(`/agent-mode/conversations/${conversationId}/participants/`);
|
|
234
|
+
}
|
|
235
|
+
markRead(conversationId, seq) {
|
|
236
|
+
return this.request(`/agent-mode/conversations/${conversationId}/read/`, {
|
|
237
|
+
method: "POST",
|
|
238
|
+
body: { client_id: this.config.clientId, seq }
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
saveDraft(conversationId, text) {
|
|
242
|
+
return this.request(`/agent-mode/conversations/${conversationId}/draft/`, {
|
|
243
|
+
method: "POST",
|
|
244
|
+
body: { client_id: this.config.clientId, text }
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
setFlags(conversationId, flags) {
|
|
248
|
+
return this.request(`/agent-mode/conversations/${conversationId}/flags/`, {
|
|
249
|
+
method: "POST",
|
|
250
|
+
body: { client_id: this.config.clientId, ...flags }
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
listFiles(conversationId) {
|
|
254
|
+
return this.request(`/agent-mode/conversations/${conversationId}/files/`);
|
|
255
|
+
}
|
|
256
|
+
summarize(conversationId) {
|
|
257
|
+
return this.request(`/agent-mode/conversations/${conversationId}/summarize/`, {
|
|
258
|
+
method: "POST",
|
|
259
|
+
body: { client_id: this.config.clientId }
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
listContext(conversationId) {
|
|
263
|
+
return this.request(`/agent-mode/conversations/${conversationId}/context/`);
|
|
264
|
+
}
|
|
265
|
+
pinContext(conversationId, item) {
|
|
266
|
+
return this.request(`/agent-mode/conversations/${conversationId}/context/`, {
|
|
267
|
+
method: "POST",
|
|
268
|
+
body: {
|
|
269
|
+
client_id: this.config.clientId,
|
|
270
|
+
kind: item.kind,
|
|
271
|
+
label: item.label,
|
|
272
|
+
ref_type: item.refType,
|
|
273
|
+
ref_id: item.refId
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
editMessage(conversationId, messageId, message) {
|
|
278
|
+
return this.request(`/agent-mode/conversations/${conversationId}/messages/${messageId}/`, {
|
|
279
|
+
method: "PATCH",
|
|
280
|
+
body: { client_id: this.config.clientId, message }
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
deleteMessage(conversationId, messageId) {
|
|
284
|
+
return this.request(`/agent-mode/conversations/${conversationId}/messages/${messageId}/`, {
|
|
285
|
+
method: "DELETE"
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
search(query, limit = 20) {
|
|
289
|
+
const qs = new URLSearchParams({ query, limit: String(limit) });
|
|
290
|
+
return this.request(`/agent-mode/search/?${qs.toString()}`);
|
|
291
|
+
}
|
|
292
|
+
getToolTrust() {
|
|
293
|
+
return this.request("/agent-mode/tool-trust/");
|
|
294
|
+
}
|
|
295
|
+
setToolTrust(toolName, trusted) {
|
|
296
|
+
return this.request("/agent-mode/tool-trust/", {
|
|
297
|
+
method: "POST",
|
|
298
|
+
body: { client_id: this.config.clientId, tool_name: toolName, trusted }
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// src/realtime-client.ts
|
|
304
|
+
var AgentRealtimeClient = class {
|
|
305
|
+
config;
|
|
306
|
+
conversationId;
|
|
307
|
+
handlers;
|
|
308
|
+
socket = null;
|
|
309
|
+
lastSeq = 0;
|
|
310
|
+
closed = false;
|
|
311
|
+
retries = 0;
|
|
312
|
+
reconnectTimer = null;
|
|
313
|
+
constructor(config, conversationId, handlers, lastSeq = 0) {
|
|
314
|
+
this.config = config;
|
|
315
|
+
this.conversationId = conversationId;
|
|
316
|
+
this.handlers = handlers;
|
|
317
|
+
this.lastSeq = lastSeq;
|
|
318
|
+
}
|
|
319
|
+
async connect() {
|
|
320
|
+
if (this.closed || typeof WebSocket === "undefined") return;
|
|
321
|
+
const url = await this.buildUrl();
|
|
322
|
+
if (!url) return;
|
|
323
|
+
this.handlers.onStatus?.("connecting");
|
|
324
|
+
let socket;
|
|
325
|
+
try {
|
|
326
|
+
socket = new WebSocket(url);
|
|
327
|
+
} catch {
|
|
328
|
+
this.scheduleReconnect();
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
this.socket = socket;
|
|
332
|
+
socket.onopen = () => {
|
|
333
|
+
this.retries = 0;
|
|
334
|
+
this.handlers.onStatus?.("open");
|
|
335
|
+
this.send({ action: "sync", last_seq: this.lastSeq });
|
|
336
|
+
};
|
|
337
|
+
socket.onmessage = (raw) => {
|
|
338
|
+
let event;
|
|
339
|
+
try {
|
|
340
|
+
event = JSON.parse(raw.data);
|
|
341
|
+
} catch {
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
this.trackSeq(event);
|
|
345
|
+
this.handlers.onEvent(event);
|
|
346
|
+
};
|
|
347
|
+
socket.onclose = () => {
|
|
348
|
+
this.handlers.onStatus?.("closed");
|
|
349
|
+
if (!this.closed) this.scheduleReconnect();
|
|
350
|
+
};
|
|
351
|
+
socket.onerror = () => {
|
|
352
|
+
try {
|
|
353
|
+
socket.close();
|
|
354
|
+
} catch {
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
/** Advance the local high-water mark so a reconnect replays only the true gap. */
|
|
359
|
+
trackSeq(event) {
|
|
360
|
+
if (event.type === "sync") {
|
|
361
|
+
if (typeof event.last_seq === "number") this.lastSeq = Math.max(this.lastSeq, event.last_seq);
|
|
362
|
+
} else if (event.type === "message" || event.type === "message_edited") {
|
|
363
|
+
const seq = event.message?.seq;
|
|
364
|
+
if (typeof seq === "number") this.lastSeq = Math.max(this.lastSeq, seq);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
setLastSeq(seq) {
|
|
368
|
+
if (seq > this.lastSeq) this.lastSeq = seq;
|
|
369
|
+
}
|
|
370
|
+
sendTyping(isTyping) {
|
|
371
|
+
this.send({ action: "typing", is_typing: isTyping });
|
|
372
|
+
}
|
|
373
|
+
markRead(seq) {
|
|
374
|
+
this.setLastSeq(seq);
|
|
375
|
+
this.send({ action: "read", seq });
|
|
376
|
+
}
|
|
377
|
+
requestSync() {
|
|
378
|
+
this.send({ action: "sync", last_seq: this.lastSeq });
|
|
379
|
+
}
|
|
380
|
+
close() {
|
|
381
|
+
this.closed = true;
|
|
382
|
+
if (this.reconnectTimer) clearTimeout(this.reconnectTimer);
|
|
383
|
+
this.reconnectTimer = null;
|
|
384
|
+
if (this.socket) {
|
|
385
|
+
try {
|
|
386
|
+
this.socket.close();
|
|
387
|
+
} catch {
|
|
388
|
+
}
|
|
389
|
+
this.socket = null;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
send(payload) {
|
|
393
|
+
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
|
394
|
+
try {
|
|
395
|
+
this.socket.send(JSON.stringify(payload));
|
|
396
|
+
} catch {
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
scheduleReconnect() {
|
|
401
|
+
if (this.closed || this.reconnectTimer) return;
|
|
402
|
+
this.retries += 1;
|
|
403
|
+
const delay = Math.min(3e4, 1e3 * 2 ** Math.min(this.retries, 5));
|
|
404
|
+
this.reconnectTimer = setTimeout(() => {
|
|
405
|
+
this.reconnectTimer = null;
|
|
406
|
+
void this.connect();
|
|
407
|
+
}, delay);
|
|
408
|
+
}
|
|
409
|
+
async buildUrl() {
|
|
410
|
+
const token = await this.config.getAccessToken();
|
|
411
|
+
if (!token) return null;
|
|
412
|
+
const base = (this.config.realtime?.baseUrl ?? this.config.apiBaseUrl).replace(/\/$/, "").replace(/^http/, "ws");
|
|
413
|
+
const params = new URLSearchParams({
|
|
414
|
+
client_id: this.config.clientId,
|
|
415
|
+
token,
|
|
416
|
+
last_seq: String(this.lastSeq)
|
|
417
|
+
});
|
|
418
|
+
return `${base}/ws/agent-mode/conversations/${this.conversationId}/?${params.toString()}`;
|
|
419
|
+
}
|
|
195
420
|
};
|
|
196
421
|
|
|
197
422
|
// src/file-support.ts
|
|
@@ -259,7 +484,7 @@ function formatBytes(bytes) {
|
|
|
259
484
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
260
485
|
}
|
|
261
486
|
function AgentImageLightbox({ src, alt, onClose }) {
|
|
262
|
-
|
|
487
|
+
React12.useEffect(() => {
|
|
263
488
|
if (!src) return;
|
|
264
489
|
const onKey = (event) => {
|
|
265
490
|
if (event.key === "Escape") onClose();
|
|
@@ -329,24 +554,24 @@ function pickMimeType() {
|
|
|
329
554
|
return "";
|
|
330
555
|
}
|
|
331
556
|
function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError }) {
|
|
332
|
-
const [phase, setPhase] =
|
|
333
|
-
const [elapsed, setElapsed] =
|
|
334
|
-
const [playing, setPlaying] =
|
|
335
|
-
const recorderRef =
|
|
336
|
-
const streamRef =
|
|
337
|
-
const chunksRef =
|
|
338
|
-
const blobRef =
|
|
339
|
-
const audioUrlRef =
|
|
340
|
-
const audioRef =
|
|
341
|
-
const timerRef =
|
|
342
|
-
const canvasRef =
|
|
343
|
-
const audioContextRef =
|
|
344
|
-
const analyserRef =
|
|
345
|
-
const rafRef =
|
|
346
|
-
const levelsRef =
|
|
347
|
-
const onErrorRef =
|
|
557
|
+
const [phase, setPhase] = React12.useState("recording");
|
|
558
|
+
const [elapsed, setElapsed] = React12.useState(0);
|
|
559
|
+
const [playing, setPlaying] = React12.useState(false);
|
|
560
|
+
const recorderRef = React12.useRef(null);
|
|
561
|
+
const streamRef = React12.useRef(null);
|
|
562
|
+
const chunksRef = React12.useRef([]);
|
|
563
|
+
const blobRef = React12.useRef(null);
|
|
564
|
+
const audioUrlRef = React12.useRef(null);
|
|
565
|
+
const audioRef = React12.useRef(null);
|
|
566
|
+
const timerRef = React12.useRef(null);
|
|
567
|
+
const canvasRef = React12.useRef(null);
|
|
568
|
+
const audioContextRef = React12.useRef(null);
|
|
569
|
+
const analyserRef = React12.useRef(null);
|
|
570
|
+
const rafRef = React12.useRef(null);
|
|
571
|
+
const levelsRef = React12.useRef(new Array(MAX_BARS).fill(0));
|
|
572
|
+
const onErrorRef = React12.useRef(onError);
|
|
348
573
|
onErrorRef.current = onError;
|
|
349
|
-
const stopMeter =
|
|
574
|
+
const stopMeter = React12.useCallback(() => {
|
|
350
575
|
if (rafRef.current != null) {
|
|
351
576
|
cancelAnimationFrame(rafRef.current);
|
|
352
577
|
rafRef.current = null;
|
|
@@ -357,7 +582,7 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
|
|
|
357
582
|
if (ctx && ctx.state !== "closed") void ctx.close().catch(() => {
|
|
358
583
|
});
|
|
359
584
|
}, []);
|
|
360
|
-
const stopTracks =
|
|
585
|
+
const stopTracks = React12.useCallback(() => {
|
|
361
586
|
streamRef.current?.getTracks().forEach((track) => track.stop());
|
|
362
587
|
streamRef.current = null;
|
|
363
588
|
if (timerRef.current) {
|
|
@@ -366,7 +591,7 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
|
|
|
366
591
|
}
|
|
367
592
|
stopMeter();
|
|
368
593
|
}, [stopMeter]);
|
|
369
|
-
const startMeter =
|
|
594
|
+
const startMeter = React12.useCallback((stream) => {
|
|
370
595
|
const Ctor = typeof window === "undefined" ? void 0 : window.AudioContext ?? window.webkitAudioContext;
|
|
371
596
|
if (!Ctor) return;
|
|
372
597
|
let context;
|
|
@@ -436,7 +661,7 @@ function AgentVoiceRecorder({ maxSeconds, transcribe, onDone, onCancel, onError
|
|
|
436
661
|
};
|
|
437
662
|
rafRef.current = requestAnimationFrame(draw);
|
|
438
663
|
}, []);
|
|
439
|
-
|
|
664
|
+
React12.useEffect(() => {
|
|
440
665
|
let cancelled = false;
|
|
441
666
|
void (async () => {
|
|
442
667
|
if (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia) {
|
|
@@ -589,40 +814,40 @@ function AgentComposer({
|
|
|
589
814
|
voiceEnabled,
|
|
590
815
|
voiceMaxSeconds = 600
|
|
591
816
|
}) {
|
|
592
|
-
const textareaRef =
|
|
593
|
-
const inputRef =
|
|
594
|
-
const [recording, setRecording] =
|
|
595
|
-
const [lightboxSrc, setLightboxSrc] =
|
|
817
|
+
const textareaRef = React12.useRef(null);
|
|
818
|
+
const inputRef = React12.useRef(null);
|
|
819
|
+
const [recording, setRecording] = React12.useState(false);
|
|
820
|
+
const [lightboxSrc, setLightboxSrc] = React12.useState(null);
|
|
596
821
|
const canSubmit = !disabled && !busy && (value.trim().length > 0 || files.length > 0);
|
|
597
822
|
const voiceAvailable = Boolean(voiceEnabled && onTranscribe);
|
|
598
|
-
const submit =
|
|
823
|
+
const submit = React12.useCallback(() => {
|
|
599
824
|
if (!canSubmit) return;
|
|
600
825
|
onSubmit(value.trim());
|
|
601
826
|
textareaRef.current?.focus();
|
|
602
827
|
}, [canSubmit, onSubmit, value]);
|
|
603
|
-
|
|
828
|
+
React12.useEffect(() => {
|
|
604
829
|
if (!disabled) textareaRef.current?.focus();
|
|
605
830
|
}, [disabled]);
|
|
606
|
-
|
|
831
|
+
React12.useEffect(() => {
|
|
607
832
|
const textarea = textareaRef.current;
|
|
608
833
|
if (!textarea) return;
|
|
609
834
|
textarea.style.height = "auto";
|
|
610
835
|
textarea.style.height = `${Math.min(textarea.scrollHeight, 144)}px`;
|
|
611
836
|
}, [value]);
|
|
612
|
-
const previews =
|
|
837
|
+
const previews = React12.useMemo(
|
|
613
838
|
() => files.map((file, originalIndex) => {
|
|
614
839
|
const image = isImageFile(file);
|
|
615
840
|
return { file, image, originalIndex, url: image ? URL.createObjectURL(file) : null };
|
|
616
841
|
}),
|
|
617
842
|
[files]
|
|
618
843
|
);
|
|
619
|
-
|
|
844
|
+
React12.useEffect(
|
|
620
845
|
() => () => {
|
|
621
846
|
previews.forEach((preview) => preview.url && URL.revokeObjectURL(preview.url));
|
|
622
847
|
},
|
|
623
848
|
[previews]
|
|
624
849
|
);
|
|
625
|
-
const ordered =
|
|
850
|
+
const ordered = React12.useMemo(
|
|
626
851
|
() => [...previews].sort((a, b) => Number(b.image) - Number(a.image)),
|
|
627
852
|
[previews]
|
|
628
853
|
);
|
|
@@ -867,15 +1092,15 @@ function groupMessages(messages) {
|
|
|
867
1092
|
return items;
|
|
868
1093
|
}
|
|
869
1094
|
function ChatScroller({ children, className, ...props }) {
|
|
870
|
-
const viewportRef =
|
|
871
|
-
const pinnedRef =
|
|
872
|
-
const handleScroll =
|
|
1095
|
+
const viewportRef = React12.useRef(null);
|
|
1096
|
+
const pinnedRef = React12.useRef(true);
|
|
1097
|
+
const handleScroll = React12.useCallback(() => {
|
|
873
1098
|
const viewport = viewportRef.current;
|
|
874
1099
|
if (!viewport) return;
|
|
875
1100
|
const distanceFromBottom = viewport.scrollHeight - viewport.scrollTop - viewport.clientHeight;
|
|
876
1101
|
pinnedRef.current = distanceFromBottom < 64;
|
|
877
1102
|
}, []);
|
|
878
|
-
|
|
1103
|
+
React12.useEffect(() => {
|
|
879
1104
|
const viewport = viewportRef.current;
|
|
880
1105
|
if (!viewport || !pinnedRef.current) return;
|
|
881
1106
|
viewport.scrollTop = viewport.scrollHeight;
|
|
@@ -892,15 +1117,66 @@ function ChatScroller({ children, className, ...props }) {
|
|
|
892
1117
|
}
|
|
893
1118
|
);
|
|
894
1119
|
}
|
|
1120
|
+
var MIN_CHARS_PER_FRAME = 2;
|
|
1121
|
+
var CATCH_UP = 0.22;
|
|
1122
|
+
function usePrefersReducedMotion() {
|
|
1123
|
+
const [reduced, setReduced] = React12.useState(
|
|
1124
|
+
() => typeof matchMedia !== "undefined" && matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
1125
|
+
);
|
|
1126
|
+
React12.useEffect(() => {
|
|
1127
|
+
if (typeof matchMedia === "undefined") return;
|
|
1128
|
+
const mq = matchMedia("(prefers-reduced-motion: reduce)");
|
|
1129
|
+
const onChange = (event) => setReduced(event.matches);
|
|
1130
|
+
mq.addEventListener("change", onChange);
|
|
1131
|
+
return () => mq.removeEventListener("change", onChange);
|
|
1132
|
+
}, []);
|
|
1133
|
+
return reduced;
|
|
1134
|
+
}
|
|
1135
|
+
function useSmoothText(target, animate) {
|
|
1136
|
+
const prefersReduced = usePrefersReducedMotion();
|
|
1137
|
+
const enabled = animate && !prefersReduced;
|
|
1138
|
+
const [count, setCount] = React12.useState(() => enabled ? 0 : target.length);
|
|
1139
|
+
const countRef = React12.useRef(count);
|
|
1140
|
+
countRef.current = count;
|
|
1141
|
+
const everEnabled = React12.useRef(enabled);
|
|
1142
|
+
if (enabled) everEnabled.current = true;
|
|
1143
|
+
React12.useEffect(() => {
|
|
1144
|
+
if (!everEnabled.current) {
|
|
1145
|
+
if (countRef.current !== target.length) setCount(target.length);
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
if (countRef.current > target.length) {
|
|
1149
|
+
setCount(target.length);
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
if (countRef.current >= target.length) return;
|
|
1153
|
+
let raf = 0;
|
|
1154
|
+
let last = performance.now();
|
|
1155
|
+
const tick = (now) => {
|
|
1156
|
+
const dt = Math.min(64, now - last);
|
|
1157
|
+
last = now;
|
|
1158
|
+
const frames = dt / (1e3 / 60);
|
|
1159
|
+
const gap = target.length - countRef.current;
|
|
1160
|
+
const step = Math.max(MIN_CHARS_PER_FRAME * frames, gap * CATCH_UP * frames);
|
|
1161
|
+
const next = Math.min(target.length, countRef.current + step);
|
|
1162
|
+
countRef.current = next;
|
|
1163
|
+
setCount(next);
|
|
1164
|
+
if (next < target.length) raf = requestAnimationFrame(tick);
|
|
1165
|
+
};
|
|
1166
|
+
raf = requestAnimationFrame(tick);
|
|
1167
|
+
return () => cancelAnimationFrame(raf);
|
|
1168
|
+
}, [target, enabled]);
|
|
1169
|
+
return everEnabled.current ? target.slice(0, Math.floor(count)) : target;
|
|
1170
|
+
}
|
|
895
1171
|
function CopyButton({ className, text }) {
|
|
896
|
-
const [copied, setCopied] =
|
|
897
|
-
const timerRef =
|
|
898
|
-
|
|
1172
|
+
const [copied, setCopied] = React12.useState(false);
|
|
1173
|
+
const timerRef = React12.useRef(null);
|
|
1174
|
+
React12.useEffect(() => {
|
|
899
1175
|
return () => {
|
|
900
1176
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
901
1177
|
};
|
|
902
1178
|
}, []);
|
|
903
|
-
const copy =
|
|
1179
|
+
const copy = React12.useCallback(async () => {
|
|
904
1180
|
try {
|
|
905
1181
|
await navigator.clipboard.writeText(text);
|
|
906
1182
|
} catch {
|
|
@@ -932,8 +1208,9 @@ function CopyButton({ className, text }) {
|
|
|
932
1208
|
] });
|
|
933
1209
|
}
|
|
934
1210
|
var isImageAttachment = (format) => IMAGE_ACCEPT.includes(`.${format.toLowerCase()}`);
|
|
935
|
-
function MessageItem({ message }) {
|
|
936
|
-
const [lightboxSrc, setLightboxSrc] =
|
|
1211
|
+
function MessageItem({ animate = false, message }) {
|
|
1212
|
+
const [lightboxSrc, setLightboxSrc] = React12.useState(null);
|
|
1213
|
+
const displayed = useSmoothText(message.content ?? "", animate && message.role === "assistant");
|
|
937
1214
|
if (message.role === "user") {
|
|
938
1215
|
const attachments = message.attachments ?? [];
|
|
939
1216
|
const images = attachments.filter((a) => isImageAttachment(a.format) && a.url);
|
|
@@ -989,7 +1266,7 @@ function MessageItem({ message }) {
|
|
|
989
1266
|
if (src) setLightboxSrc(src);
|
|
990
1267
|
},
|
|
991
1268
|
role: "presentation",
|
|
992
|
-
children: /* @__PURE__ */ jsx(Markdown, { children:
|
|
1269
|
+
children: /* @__PURE__ */ jsx(Markdown, { children: displayed })
|
|
993
1270
|
}
|
|
994
1271
|
) }),
|
|
995
1272
|
/* @__PURE__ */ jsx(
|
|
@@ -1017,8 +1294,8 @@ var DEFAULT_PHRASES = [
|
|
|
1017
1294
|
];
|
|
1018
1295
|
function ThinkingIndicator({ className, intervalMs = 1800, phrases }) {
|
|
1019
1296
|
const words = phrases && phrases.length ? phrases : DEFAULT_PHRASES;
|
|
1020
|
-
const [index, setIndex] =
|
|
1021
|
-
|
|
1297
|
+
const [index, setIndex] = React12.useState(0);
|
|
1298
|
+
React12.useEffect(() => {
|
|
1022
1299
|
setIndex(0);
|
|
1023
1300
|
const timer = setInterval(() => {
|
|
1024
1301
|
setIndex((current) => (current + 1) % words.length);
|
|
@@ -1037,14 +1314,7 @@ function ThinkingIndicator({ className, intervalMs = 1800, phrases }) {
|
|
|
1037
1314
|
role: "status",
|
|
1038
1315
|
children: [
|
|
1039
1316
|
/* @__PURE__ */ jsx(ThinkingOrb, { className: "shrink-0", size: 20, speed: 0.7, state: "solving" }),
|
|
1040
|
-
/* @__PURE__ */ jsx(
|
|
1041
|
-
"span",
|
|
1042
|
-
{
|
|
1043
|
-
className: "fade-in-0 animate-in text-xs font-medium duration-300",
|
|
1044
|
-
children: /* @__PURE__ */ jsx(AuroraText, { children: words[index] })
|
|
1045
|
-
},
|
|
1046
|
-
index
|
|
1047
|
-
)
|
|
1317
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium", children: /* @__PURE__ */ jsx(AuroraText, { speed: 2.2, children: /* @__PURE__ */ jsx("span", { className: "fade-in-0 inline-block animate-in duration-300", children: words[index] }, index) }) })
|
|
1048
1318
|
]
|
|
1049
1319
|
}
|
|
1050
1320
|
);
|
|
@@ -1273,7 +1543,7 @@ function DownloadButton({ block, size = "sm" }) {
|
|
|
1273
1543
|
);
|
|
1274
1544
|
}
|
|
1275
1545
|
function DocumentViewer({ block, onClose }) {
|
|
1276
|
-
|
|
1546
|
+
React12.useEffect(() => {
|
|
1277
1547
|
const onKey = (event) => event.key === "Escape" && onClose();
|
|
1278
1548
|
window.addEventListener("keydown", onKey);
|
|
1279
1549
|
return () => window.removeEventListener("keydown", onKey);
|
|
@@ -1298,7 +1568,7 @@ function DocumentViewer({ block, onClose }) {
|
|
|
1298
1568
|
] });
|
|
1299
1569
|
}
|
|
1300
1570
|
function DocumentCard({ block }) {
|
|
1301
|
-
const [open, setOpen] =
|
|
1571
|
+
const [open, setOpen] = React12.useState(false);
|
|
1302
1572
|
const previewLines = (block.preview || "").split("\n").slice(0, 6).join("\n");
|
|
1303
1573
|
return /* @__PURE__ */ jsxs("div", { className: "w-full max-w-full overflow-hidden rounded-xl border border-border bg-card shadow-xs", children: [
|
|
1304
1574
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-border/72 border-b px-3 py-2", children: [
|
|
@@ -1420,7 +1690,7 @@ function StepDetail({
|
|
|
1420
1690
|
] });
|
|
1421
1691
|
}
|
|
1422
1692
|
function ToolActivityGroup({ describeToolCall, steps }) {
|
|
1423
|
-
const [open, setOpen] =
|
|
1693
|
+
const [open, setOpen] = React12.useState(false);
|
|
1424
1694
|
if (steps.length === 0) return null;
|
|
1425
1695
|
const last = steps[steps.length - 1];
|
|
1426
1696
|
const anyRunning = steps.some((step) => step.status === "running");
|
|
@@ -1513,6 +1783,12 @@ function ToolApprovalCard({
|
|
|
1513
1783
|
] })
|
|
1514
1784
|
] });
|
|
1515
1785
|
}
|
|
1786
|
+
function contextMeterLabel(meter) {
|
|
1787
|
+
const pct = Math.round(meter.pct * 100);
|
|
1788
|
+
if (meter.state === "full") return `Context full (${pct}%) \u2014 start a new chat to continue`;
|
|
1789
|
+
if (meter.state === "summarizing") return `Context ${pct}% \u2014 summarizing older messages`;
|
|
1790
|
+
return `Context ${pct}% used`;
|
|
1791
|
+
}
|
|
1516
1792
|
function AgentPanel({
|
|
1517
1793
|
agentMode,
|
|
1518
1794
|
canToggleAgentMode,
|
|
@@ -1525,12 +1801,12 @@ function AgentPanel({
|
|
|
1525
1801
|
onClose,
|
|
1526
1802
|
onToggleAgentMode
|
|
1527
1803
|
}) {
|
|
1528
|
-
const [draft, setDraft] =
|
|
1529
|
-
const [view, setView] =
|
|
1530
|
-
const [files, setFiles] =
|
|
1531
|
-
const [uploadError, setUploadError] =
|
|
1532
|
-
const [dragging, setDragging] =
|
|
1533
|
-
const dragDepth =
|
|
1804
|
+
const [draft, setDraft] = React12.useState("");
|
|
1805
|
+
const [view, setView] = React12.useState("chat");
|
|
1806
|
+
const [files, setFiles] = React12.useState([]);
|
|
1807
|
+
const [uploadError, setUploadError] = React12.useState(null);
|
|
1808
|
+
const [dragging, setDragging] = React12.useState(false);
|
|
1809
|
+
const dragDepth = React12.useRef(0);
|
|
1534
1810
|
const agentName = config.agentName ?? chat.agentInfo?.name ?? "Assistant";
|
|
1535
1811
|
const caps = {
|
|
1536
1812
|
visionEnabled: chat.agentInfo?.vision_enabled,
|
|
@@ -1541,7 +1817,7 @@ function AgentPanel({
|
|
|
1541
1817
|
"What can you help me with?",
|
|
1542
1818
|
"Give me a quick summary of my data."
|
|
1543
1819
|
];
|
|
1544
|
-
const addFiles =
|
|
1820
|
+
const addFiles = React12.useCallback(
|
|
1545
1821
|
(incoming) => {
|
|
1546
1822
|
const accepted = [];
|
|
1547
1823
|
let rejection = null;
|
|
@@ -1558,10 +1834,10 @@ function AgentPanel({
|
|
|
1558
1834
|
},
|
|
1559
1835
|
[config, caps.visionEnabled, caps.visionMaxMb]
|
|
1560
1836
|
);
|
|
1561
|
-
const removeFile =
|
|
1837
|
+
const removeFile = React12.useCallback((index) => {
|
|
1562
1838
|
setFiles((current) => current.filter((_, i) => i !== index));
|
|
1563
1839
|
}, []);
|
|
1564
|
-
const handleSubmit =
|
|
1840
|
+
const handleSubmit = React12.useCallback(
|
|
1565
1841
|
(text) => {
|
|
1566
1842
|
const staged = files;
|
|
1567
1843
|
setDraft("");
|
|
@@ -1571,7 +1847,7 @@ function AgentPanel({
|
|
|
1571
1847
|
},
|
|
1572
1848
|
[chat, files]
|
|
1573
1849
|
);
|
|
1574
|
-
const onDragEnter =
|
|
1850
|
+
const onDragEnter = React12.useCallback(
|
|
1575
1851
|
(event) => {
|
|
1576
1852
|
if (!uploadEnabled || !event.dataTransfer?.types?.includes("Files")) return;
|
|
1577
1853
|
dragDepth.current += 1;
|
|
@@ -1579,12 +1855,12 @@ function AgentPanel({
|
|
|
1579
1855
|
},
|
|
1580
1856
|
[uploadEnabled]
|
|
1581
1857
|
);
|
|
1582
|
-
const onDragLeave =
|
|
1858
|
+
const onDragLeave = React12.useCallback((event) => {
|
|
1583
1859
|
if (!event.dataTransfer?.types?.includes("Files")) return;
|
|
1584
1860
|
dragDepth.current = Math.max(0, dragDepth.current - 1);
|
|
1585
1861
|
if (dragDepth.current === 0) setDragging(false);
|
|
1586
1862
|
}, []);
|
|
1587
|
-
const onDrop =
|
|
1863
|
+
const onDrop = React12.useCallback(
|
|
1588
1864
|
(event) => {
|
|
1589
1865
|
if (!uploadEnabled) return;
|
|
1590
1866
|
event.preventDefault();
|
|
@@ -1595,7 +1871,7 @@ function AgentPanel({
|
|
|
1595
1871
|
},
|
|
1596
1872
|
[addFiles, uploadEnabled]
|
|
1597
1873
|
);
|
|
1598
|
-
const openHistory =
|
|
1874
|
+
const openHistory = React12.useCallback(() => {
|
|
1599
1875
|
setView("history");
|
|
1600
1876
|
void chat.refreshConversations();
|
|
1601
1877
|
}, [chat]);
|
|
@@ -1630,6 +1906,33 @@ function AgentPanel({
|
|
|
1630
1906
|
] })
|
|
1631
1907
|
] }),
|
|
1632
1908
|
/* @__PURE__ */ jsxs(ChatHeaderActions, { children: [
|
|
1909
|
+
chat.contextMeter && chat.contextMeter.pct > 0 ? /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
1910
|
+
/* @__PURE__ */ jsx(
|
|
1911
|
+
TooltipTrigger,
|
|
1912
|
+
{
|
|
1913
|
+
render: /* @__PURE__ */ jsx(
|
|
1914
|
+
"button",
|
|
1915
|
+
{
|
|
1916
|
+
"aria-label": contextMeterLabel(chat.contextMeter),
|
|
1917
|
+
className: "mr-0.5 inline-flex cursor-default items-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
1918
|
+
type: "button",
|
|
1919
|
+
children: /* @__PURE__ */ jsx(
|
|
1920
|
+
ContextMeter,
|
|
1921
|
+
{
|
|
1922
|
+
lockAt: chat.contextMeter.lock_at,
|
|
1923
|
+
pct: chat.contextMeter.pct,
|
|
1924
|
+
size: 18,
|
|
1925
|
+
state: chat.contextMeter.state,
|
|
1926
|
+
summarizeAt: chat.contextMeter.summarize_at,
|
|
1927
|
+
warnAt: chat.contextMeter.warn_at
|
|
1928
|
+
}
|
|
1929
|
+
)
|
|
1930
|
+
}
|
|
1931
|
+
)
|
|
1932
|
+
}
|
|
1933
|
+
),
|
|
1934
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: contextMeterLabel(chat.contextMeter) })
|
|
1935
|
+
] }) : null,
|
|
1633
1936
|
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
1634
1937
|
/* @__PURE__ */ jsx(
|
|
1635
1938
|
TooltipTrigger,
|
|
@@ -1759,7 +2062,16 @@ function AgentPanel({
|
|
|
1759
2062
|
const anchorKey = newMessageAnchorId ? grouped.find(
|
|
1760
2063
|
(it) => it.kind !== "tool-activity" && it.message.id === newMessageAnchorId
|
|
1761
2064
|
)?.key : void 0;
|
|
1762
|
-
|
|
2065
|
+
let liveAssistantKey = null;
|
|
2066
|
+
if (chat.sending) {
|
|
2067
|
+
for (let i = grouped.length - 1; i >= 0; i -= 1) {
|
|
2068
|
+
if (grouped[i].kind === "assistant-text") {
|
|
2069
|
+
liveAssistantKey = grouped[i].key;
|
|
2070
|
+
break;
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
}
|
|
2074
|
+
return grouped.map((item) => /* @__PURE__ */ jsxs(React12.Fragment, { children: [
|
|
1763
2075
|
item.key === anchorKey ? /* @__PURE__ */ jsx(NewMessagesDivider, {}) : null,
|
|
1764
2076
|
/* @__PURE__ */ jsx(
|
|
1765
2077
|
motion.div,
|
|
@@ -1773,7 +2085,7 @@ function AgentPanel({
|
|
|
1773
2085
|
describeToolCall: config.describeToolCall,
|
|
1774
2086
|
steps: item.steps
|
|
1775
2087
|
}
|
|
1776
|
-
) }) : /* @__PURE__ */ jsx(MessageItem, { message: item.message })
|
|
2088
|
+
) }) : /* @__PURE__ */ jsx(MessageItem, { animate: item.key === liveAssistantKey, message: item.message })
|
|
1777
2089
|
}
|
|
1778
2090
|
)
|
|
1779
2091
|
] }, item.key));
|
|
@@ -1842,6 +2154,43 @@ var OPTIMISTIC_ID_PREFIX = "optimistic-";
|
|
|
1842
2154
|
var STREAMING_ID_PREFIX = "streaming-";
|
|
1843
2155
|
var optimisticCounter = 0;
|
|
1844
2156
|
var streamingCounter = 0;
|
|
2157
|
+
function newIdempotencyKey() {
|
|
2158
|
+
try {
|
|
2159
|
+
if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
|
|
2160
|
+
} catch {
|
|
2161
|
+
}
|
|
2162
|
+
return `idem-${Date.now()}-${Math.floor(Math.random() * 1e9)}`;
|
|
2163
|
+
}
|
|
2164
|
+
function mergeMessages(current, incoming) {
|
|
2165
|
+
if (!incoming?.length) return current;
|
|
2166
|
+
const settledIds = new Set(
|
|
2167
|
+
current.filter((m) => !m.id.startsWith(OPTIMISTIC_ID_PREFIX)).map((m) => m.id)
|
|
2168
|
+
);
|
|
2169
|
+
const additions = incoming.filter((m) => m.id && !settledIds.has(m.id));
|
|
2170
|
+
if (!additions.length) return current;
|
|
2171
|
+
let base = current;
|
|
2172
|
+
const optimistic = current.filter((m) => m.id.startsWith(OPTIMISTIC_ID_PREFIX));
|
|
2173
|
+
const placeholder = optimistic[optimistic.length - 1];
|
|
2174
|
+
if (placeholder) {
|
|
2175
|
+
const echoIndex = additions.findIndex(
|
|
2176
|
+
(m) => m.role === "user" && (m.content ?? "") === (placeholder.content ?? "")
|
|
2177
|
+
);
|
|
2178
|
+
if (echoIndex !== -1) {
|
|
2179
|
+
base = current.filter((m) => m.id !== placeholder.id);
|
|
2180
|
+
additions[echoIndex] = {
|
|
2181
|
+
...additions[echoIndex],
|
|
2182
|
+
client_key: placeholder.client_key ?? placeholder.id
|
|
2183
|
+
};
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
const merged = [...base, ...additions];
|
|
2187
|
+
merged.sort((a, b) => {
|
|
2188
|
+
const sa = typeof a.seq === "number" ? a.seq : Number.MAX_SAFE_INTEGER;
|
|
2189
|
+
const sb = typeof b.seq === "number" ? b.seq : Number.MAX_SAFE_INTEGER;
|
|
2190
|
+
return sa - sb;
|
|
2191
|
+
});
|
|
2192
|
+
return merged;
|
|
2193
|
+
}
|
|
1845
2194
|
function buildOptimisticUserMessage(text, files) {
|
|
1846
2195
|
optimisticCounter += 1;
|
|
1847
2196
|
const attachments = (files ?? []).map((file, index) => ({
|
|
@@ -1859,17 +2208,25 @@ function buildOptimisticUserMessage(text, files) {
|
|
|
1859
2208
|
};
|
|
1860
2209
|
}
|
|
1861
2210
|
function useAgentChat(config) {
|
|
1862
|
-
const client =
|
|
1863
|
-
const
|
|
1864
|
-
|
|
1865
|
-
const [
|
|
1866
|
-
const [
|
|
1867
|
-
const [
|
|
1868
|
-
const [
|
|
1869
|
-
const [
|
|
1870
|
-
const [
|
|
1871
|
-
const [
|
|
1872
|
-
|
|
2211
|
+
const client = React12.useMemo(() => new AgentApiClient(config), [config]);
|
|
2212
|
+
const configRef = React12.useRef(config);
|
|
2213
|
+
configRef.current = config;
|
|
2214
|
+
const [agentInfo, setAgentInfo] = React12.useState(null);
|
|
2215
|
+
const [conversationId, setConversationId] = React12.useState(null);
|
|
2216
|
+
const [conversationEpoch, setConversationEpoch] = React12.useState(0);
|
|
2217
|
+
const [conversations, setConversations] = React12.useState([]);
|
|
2218
|
+
const [messages, setMessages] = React12.useState([]);
|
|
2219
|
+
const [pendingApprovals, setPendingApprovals] = React12.useState([]);
|
|
2220
|
+
const [sending, setSending] = React12.useState(false);
|
|
2221
|
+
const [resolvingApprovalId, setResolvingApprovalId] = React12.useState(null);
|
|
2222
|
+
const [error, setError] = React12.useState(null);
|
|
2223
|
+
const [currentConversation, setCurrentConversation] = React12.useState(null);
|
|
2224
|
+
const [contextMeter, setContextMeter] = React12.useState(null);
|
|
2225
|
+
const [participants, setParticipants] = React12.useState([]);
|
|
2226
|
+
const [myState, setMyState] = React12.useState(null);
|
|
2227
|
+
const [typingUsers, setTypingUsers] = React12.useState({});
|
|
2228
|
+
const [realtimeStatus, setRealtimeStatus] = React12.useState("idle");
|
|
2229
|
+
React12.useEffect(() => {
|
|
1873
2230
|
let cancelled = false;
|
|
1874
2231
|
client.getConfig().then((info) => {
|
|
1875
2232
|
if (!cancelled) setAgentInfo(info);
|
|
@@ -1879,11 +2236,11 @@ function useAgentChat(config) {
|
|
|
1879
2236
|
cancelled = true;
|
|
1880
2237
|
};
|
|
1881
2238
|
}, [client]);
|
|
1882
|
-
const handleFailure =
|
|
2239
|
+
const handleFailure = React12.useCallback((err) => {
|
|
1883
2240
|
const message = err instanceof AgentApiError ? err.message : "Something went wrong. Please try again.";
|
|
1884
2241
|
setError(message);
|
|
1885
2242
|
}, []);
|
|
1886
|
-
const applyResponse =
|
|
2243
|
+
const applyResponse = React12.useCallback(
|
|
1887
2244
|
(response) => {
|
|
1888
2245
|
if (response.status === "error" && !response.messages?.length) {
|
|
1889
2246
|
setError(response.message ?? "The assistant could not answer.");
|
|
@@ -1913,7 +2270,7 @@ function useAgentChat(config) {
|
|
|
1913
2270
|
},
|
|
1914
2271
|
[]
|
|
1915
2272
|
);
|
|
1916
|
-
const handleStreamEvent =
|
|
2273
|
+
const handleStreamEvent = React12.useCallback(
|
|
1917
2274
|
(event, ctx) => {
|
|
1918
2275
|
switch (event.type) {
|
|
1919
2276
|
case "conversation":
|
|
@@ -1991,29 +2348,93 @@ function useAgentChat(config) {
|
|
|
1991
2348
|
},
|
|
1992
2349
|
[]
|
|
1993
2350
|
);
|
|
2351
|
+
const applyRealtime = React12.useCallback((event) => {
|
|
2352
|
+
switch (event.type) {
|
|
2353
|
+
case "sync":
|
|
2354
|
+
setMessages((current) => mergeMessages(current, event.messages ?? []));
|
|
2355
|
+
break;
|
|
2356
|
+
case "message":
|
|
2357
|
+
setMessages((current) => mergeMessages(current, [event.message]));
|
|
2358
|
+
break;
|
|
2359
|
+
case "message_edited":
|
|
2360
|
+
setMessages(
|
|
2361
|
+
(current) => current.map(
|
|
2362
|
+
(message) => message.id === event.message.id ? { ...event.message, client_key: message.client_key ?? message.id } : message
|
|
2363
|
+
)
|
|
2364
|
+
);
|
|
2365
|
+
break;
|
|
2366
|
+
case "message_deleted":
|
|
2367
|
+
setMessages(
|
|
2368
|
+
(current) => current.map(
|
|
2369
|
+
(message) => message.id === event.message_id ? { ...message, deleted: true, content: "" } : message
|
|
2370
|
+
)
|
|
2371
|
+
);
|
|
2372
|
+
break;
|
|
2373
|
+
case "typing":
|
|
2374
|
+
setTypingUsers((current) => {
|
|
2375
|
+
const next = { ...current };
|
|
2376
|
+
if (event.is_typing) next[event.user_id] = event.name;
|
|
2377
|
+
else delete next[event.user_id];
|
|
2378
|
+
return next;
|
|
2379
|
+
});
|
|
2380
|
+
break;
|
|
2381
|
+
case "visibility_changed":
|
|
2382
|
+
setCurrentConversation(
|
|
2383
|
+
(current) => current ? { ...current, visibility: event.visibility } : current
|
|
2384
|
+
);
|
|
2385
|
+
break;
|
|
2386
|
+
}
|
|
2387
|
+
}, []);
|
|
2388
|
+
const realtimeEnabled = config.realtime?.enabled !== false;
|
|
2389
|
+
const isPublic = currentConversation?.visibility === "public";
|
|
2390
|
+
const baselineSeq = currentConversation?.last_seq ?? 0;
|
|
2391
|
+
React12.useEffect(() => {
|
|
2392
|
+
if (!realtimeEnabled || !conversationId || !isPublic) {
|
|
2393
|
+
setRealtimeStatus("idle");
|
|
2394
|
+
return;
|
|
2395
|
+
}
|
|
2396
|
+
const rt = new AgentRealtimeClient(
|
|
2397
|
+
configRef.current,
|
|
2398
|
+
conversationId,
|
|
2399
|
+
{ onEvent: applyRealtime, onStatus: setRealtimeStatus },
|
|
2400
|
+
baselineSeq
|
|
2401
|
+
);
|
|
2402
|
+
void rt.connect();
|
|
2403
|
+
return () => {
|
|
2404
|
+
rt.close();
|
|
2405
|
+
};
|
|
2406
|
+
}, [applyRealtime, baselineSeq, conversationId, isPublic, realtimeEnabled]);
|
|
1994
2407
|
const streamingEnabled = config.streaming !== false && typeof ReadableStream !== "undefined";
|
|
1995
|
-
const send =
|
|
2408
|
+
const send = React12.useCallback(
|
|
1996
2409
|
async (text, files) => {
|
|
1997
2410
|
if (sending) return;
|
|
1998
2411
|
if (!text.trim() && !(files && files.length)) return;
|
|
1999
2412
|
setSending(true);
|
|
2000
2413
|
setError(null);
|
|
2001
2414
|
setMessages((current) => [...current, buildOptimisticUserMessage(text, files)]);
|
|
2415
|
+
const idempotencyKey = newIdempotencyKey();
|
|
2002
2416
|
try {
|
|
2003
2417
|
if (streamingEnabled) {
|
|
2004
2418
|
const ctx = { streamingId: null };
|
|
2005
2419
|
let started = false;
|
|
2006
2420
|
try {
|
|
2007
|
-
await client.streamMessage(
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2421
|
+
await client.streamMessage(
|
|
2422
|
+
text,
|
|
2423
|
+
conversationId,
|
|
2424
|
+
files,
|
|
2425
|
+
(event) => {
|
|
2426
|
+
started = true;
|
|
2427
|
+
handleStreamEvent(event, ctx);
|
|
2428
|
+
},
|
|
2429
|
+
void 0,
|
|
2430
|
+
{ idempotencyKey }
|
|
2431
|
+
);
|
|
2011
2432
|
return;
|
|
2012
2433
|
} catch (err) {
|
|
2013
2434
|
if (started) throw err;
|
|
2014
2435
|
}
|
|
2015
2436
|
}
|
|
2016
|
-
const response = await client.sendMessage(text, conversationId, files);
|
|
2437
|
+
const response = await client.sendMessage(text, conversationId, files, { idempotencyKey });
|
|
2017
2438
|
applyResponse(response);
|
|
2018
2439
|
} catch (err) {
|
|
2019
2440
|
handleFailure(err);
|
|
@@ -2023,14 +2444,14 @@ function useAgentChat(config) {
|
|
|
2023
2444
|
},
|
|
2024
2445
|
[applyResponse, client, conversationId, handleFailure, handleStreamEvent, sending, streamingEnabled]
|
|
2025
2446
|
);
|
|
2026
|
-
const transcribe =
|
|
2447
|
+
const transcribe = React12.useCallback(
|
|
2027
2448
|
async (audio) => {
|
|
2028
2449
|
const { text } = await client.transcribe(audio);
|
|
2029
2450
|
return text;
|
|
2030
2451
|
},
|
|
2031
2452
|
[client]
|
|
2032
2453
|
);
|
|
2033
|
-
const resolveApproval =
|
|
2454
|
+
const resolveApproval = React12.useCallback(
|
|
2034
2455
|
async (callId, approve) => {
|
|
2035
2456
|
if (resolvingApprovalId) return;
|
|
2036
2457
|
setResolvingApprovalId(callId);
|
|
@@ -2059,14 +2480,19 @@ function useAgentChat(config) {
|
|
|
2059
2480
|
},
|
|
2060
2481
|
[applyResponse, client, handleFailure, handleStreamEvent, resolvingApprovalId, streamingEnabled]
|
|
2061
2482
|
);
|
|
2062
|
-
const newChat =
|
|
2483
|
+
const newChat = React12.useCallback(() => {
|
|
2063
2484
|
setConversationId(null);
|
|
2064
2485
|
setMessages([]);
|
|
2065
2486
|
setPendingApprovals([]);
|
|
2066
2487
|
setError(null);
|
|
2488
|
+
setCurrentConversation(null);
|
|
2489
|
+
setContextMeter(null);
|
|
2490
|
+
setParticipants([]);
|
|
2491
|
+
setMyState(null);
|
|
2492
|
+
setTypingUsers({});
|
|
2067
2493
|
setConversationEpoch((e) => e + 1);
|
|
2068
2494
|
}, []);
|
|
2069
|
-
const refreshConversations =
|
|
2495
|
+
const refreshConversations = React12.useCallback(async () => {
|
|
2070
2496
|
try {
|
|
2071
2497
|
const { conversations: list } = await client.listConversations();
|
|
2072
2498
|
setConversations(list);
|
|
@@ -2076,7 +2502,7 @@ function useAgentChat(config) {
|
|
|
2076
2502
|
return [];
|
|
2077
2503
|
}
|
|
2078
2504
|
}, [client, handleFailure]);
|
|
2079
|
-
const openConversation =
|
|
2505
|
+
const openConversation = React12.useCallback(
|
|
2080
2506
|
async (id) => {
|
|
2081
2507
|
setError(null);
|
|
2082
2508
|
try {
|
|
@@ -2084,6 +2510,23 @@ function useAgentChat(config) {
|
|
|
2084
2510
|
setConversationId(id);
|
|
2085
2511
|
setMessages(payload.messages ?? []);
|
|
2086
2512
|
setPendingApprovals(payload.pending_approvals ?? []);
|
|
2513
|
+
const convo = payload.conversation;
|
|
2514
|
+
setCurrentConversation(
|
|
2515
|
+
convo ? {
|
|
2516
|
+
id: convo.id,
|
|
2517
|
+
title: convo.title,
|
|
2518
|
+
visibility: convo.visibility,
|
|
2519
|
+
scope_type: convo.scope_type,
|
|
2520
|
+
last_seq: convo.last_seq,
|
|
2521
|
+
is_creator: convo.is_creator,
|
|
2522
|
+
can_toggle_visibility: convo.can_toggle_visibility,
|
|
2523
|
+
can_go_private: convo.can_go_private
|
|
2524
|
+
} : null
|
|
2525
|
+
);
|
|
2526
|
+
setContextMeter(payload.context_meter ?? null);
|
|
2527
|
+
setParticipants(payload.participants ?? []);
|
|
2528
|
+
setMyState(payload.my_state ?? null);
|
|
2529
|
+
setTypingUsers({});
|
|
2087
2530
|
setConversationEpoch((e) => e + 1);
|
|
2088
2531
|
} catch (err) {
|
|
2089
2532
|
handleFailure(err);
|
|
@@ -2091,7 +2534,7 @@ function useAgentChat(config) {
|
|
|
2091
2534
|
},
|
|
2092
2535
|
[client, handleFailure]
|
|
2093
2536
|
);
|
|
2094
|
-
const deleteConversation =
|
|
2537
|
+
const deleteConversation = React12.useCallback(
|
|
2095
2538
|
async (id) => {
|
|
2096
2539
|
try {
|
|
2097
2540
|
await client.deleteConversation(id);
|
|
@@ -2103,25 +2546,31 @@ function useAgentChat(config) {
|
|
|
2103
2546
|
},
|
|
2104
2547
|
[client, conversationId, handleFailure, newChat]
|
|
2105
2548
|
);
|
|
2106
|
-
const clearError =
|
|
2549
|
+
const clearError = React12.useCallback(() => setError(null), []);
|
|
2107
2550
|
return {
|
|
2108
2551
|
agentInfo,
|
|
2109
2552
|
clearError,
|
|
2553
|
+
contextMeter,
|
|
2110
2554
|
conversationId,
|
|
2111
2555
|
conversationEpoch,
|
|
2112
2556
|
conversations,
|
|
2557
|
+
currentConversation,
|
|
2113
2558
|
deleteConversation,
|
|
2114
2559
|
error,
|
|
2115
2560
|
messages,
|
|
2561
|
+
myState,
|
|
2116
2562
|
newChat,
|
|
2117
2563
|
openConversation,
|
|
2564
|
+
participants,
|
|
2118
2565
|
pendingApprovals,
|
|
2566
|
+
realtimeStatus,
|
|
2119
2567
|
refreshConversations,
|
|
2120
2568
|
resolveApproval,
|
|
2121
2569
|
resolvingApprovalId,
|
|
2122
2570
|
send,
|
|
2123
2571
|
sending,
|
|
2124
|
-
transcribe
|
|
2572
|
+
transcribe,
|
|
2573
|
+
typingUsers
|
|
2125
2574
|
};
|
|
2126
2575
|
}
|
|
2127
2576
|
var STORAGE_PREFIX = "cc-agent-always-approve:";
|
|
@@ -2140,11 +2589,11 @@ function readStored(clientId) {
|
|
|
2140
2589
|
}
|
|
2141
2590
|
}
|
|
2142
2591
|
function useAlwaysApprove(clientId) {
|
|
2143
|
-
const [approved, setApproved] =
|
|
2144
|
-
|
|
2592
|
+
const [approved, setApproved] = React12.useState(() => /* @__PURE__ */ new Set());
|
|
2593
|
+
React12.useEffect(() => {
|
|
2145
2594
|
setApproved(readStored(clientId));
|
|
2146
2595
|
}, [clientId]);
|
|
2147
|
-
const persist =
|
|
2596
|
+
const persist = React12.useCallback(
|
|
2148
2597
|
(next) => {
|
|
2149
2598
|
setApproved(next);
|
|
2150
2599
|
if (typeof window === "undefined") return;
|
|
@@ -2155,8 +2604,8 @@ function useAlwaysApprove(clientId) {
|
|
|
2155
2604
|
},
|
|
2156
2605
|
[clientId]
|
|
2157
2606
|
);
|
|
2158
|
-
const isAlwaysApproved =
|
|
2159
|
-
const setAlwaysApprove =
|
|
2607
|
+
const isAlwaysApproved = React12.useCallback((toolName) => approved.has(toolName), [approved]);
|
|
2608
|
+
const setAlwaysApprove = React12.useCallback(
|
|
2160
2609
|
(toolName) => {
|
|
2161
2610
|
if (approved.has(toolName)) return;
|
|
2162
2611
|
const next = new Set(approved);
|
|
@@ -2165,7 +2614,7 @@ function useAlwaysApprove(clientId) {
|
|
|
2165
2614
|
},
|
|
2166
2615
|
[approved, persist]
|
|
2167
2616
|
);
|
|
2168
|
-
const clearAlwaysApprove =
|
|
2617
|
+
const clearAlwaysApprove = React12.useCallback(
|
|
2169
2618
|
(toolName) => {
|
|
2170
2619
|
if (!approved.has(toolName)) return;
|
|
2171
2620
|
const next = new Set(approved);
|
|
@@ -2191,10 +2640,10 @@ function AgentWidget({
|
|
|
2191
2640
|
const isControlled = controlledOpen !== void 0;
|
|
2192
2641
|
const isDocked = presentation === "docked";
|
|
2193
2642
|
const storageKey2 = `cc-agent-open:${config.clientId}`;
|
|
2194
|
-
const [uncontrolledOpen, setUncontrolledOpen] =
|
|
2643
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React12.useState(false);
|
|
2195
2644
|
const open = controlledOpen ?? uncontrolledOpen;
|
|
2196
|
-
const [bootstrapDone, setBootstrapDone] =
|
|
2197
|
-
const persist =
|
|
2645
|
+
const [bootstrapDone, setBootstrapDone] = React12.useState(isControlled);
|
|
2646
|
+
const persist = React12.useCallback(
|
|
2198
2647
|
(next) => {
|
|
2199
2648
|
if (isControlled || !behavior.persistOpenState) return;
|
|
2200
2649
|
try {
|
|
@@ -2204,7 +2653,7 @@ function AgentWidget({
|
|
|
2204
2653
|
},
|
|
2205
2654
|
[behavior.persistOpenState, isControlled, storageKey2]
|
|
2206
2655
|
);
|
|
2207
|
-
const setOpen =
|
|
2656
|
+
const setOpen = React12.useCallback(
|
|
2208
2657
|
(next) => {
|
|
2209
2658
|
setUncontrolledOpen(next);
|
|
2210
2659
|
persist(next);
|
|
@@ -2212,14 +2661,14 @@ function AgentWidget({
|
|
|
2212
2661
|
},
|
|
2213
2662
|
[onOpenChange, persist]
|
|
2214
2663
|
);
|
|
2215
|
-
const [agentMode, setAgentMode] =
|
|
2664
|
+
const [agentMode, setAgentMode] = React12.useState(false);
|
|
2216
2665
|
const isDesktop = useMediaQuery("(min-width: 640px)", { defaultSSRValue: true, ssr: true });
|
|
2217
2666
|
const fullscreen = !isDesktop || agentMode;
|
|
2218
2667
|
const chat = useAgentChat(config);
|
|
2219
2668
|
const alwaysApprove = useAlwaysApprove(config.clientId);
|
|
2220
2669
|
const { messages, pendingApprovals, resolveApproval, resolvingApprovalId, sending } = chat;
|
|
2221
|
-
const bootRef =
|
|
2222
|
-
|
|
2670
|
+
const bootRef = React12.useRef(false);
|
|
2671
|
+
React12.useEffect(() => {
|
|
2223
2672
|
if (bootRef.current) return;
|
|
2224
2673
|
if (isControlled && !isDocked) return;
|
|
2225
2674
|
bootRef.current = true;
|
|
@@ -2251,15 +2700,15 @@ function AgentWidget({
|
|
|
2251
2700
|
cancelled = true;
|
|
2252
2701
|
};
|
|
2253
2702
|
}, []);
|
|
2254
|
-
const seenCountRef =
|
|
2255
|
-
const [baselined, setBaselined] =
|
|
2256
|
-
const [anchorId, setAnchorId] =
|
|
2257
|
-
|
|
2703
|
+
const seenCountRef = React12.useRef(0);
|
|
2704
|
+
const [baselined, setBaselined] = React12.useState(false);
|
|
2705
|
+
const [anchorId, setAnchorId] = React12.useState(null);
|
|
2706
|
+
React12.useEffect(() => {
|
|
2258
2707
|
if (baselined || !bootstrapDone) return;
|
|
2259
2708
|
seenCountRef.current = messages.length;
|
|
2260
2709
|
setBaselined(true);
|
|
2261
2710
|
}, [baselined, bootstrapDone, messages.length]);
|
|
2262
|
-
|
|
2711
|
+
React12.useEffect(() => {
|
|
2263
2712
|
if (open) {
|
|
2264
2713
|
if (messages.length > seenCountRef.current) {
|
|
2265
2714
|
const firstUnread = messages[seenCountRef.current];
|
|
@@ -2269,7 +2718,7 @@ function AgentWidget({
|
|
|
2269
2718
|
}
|
|
2270
2719
|
}, [open, messages]);
|
|
2271
2720
|
const unreadCount = open || !baselined ? 0 : messages.slice(seenCountRef.current).filter(isUnreadable).length;
|
|
2272
|
-
const workLabel =
|
|
2721
|
+
const workLabel = React12.useMemo(() => {
|
|
2273
2722
|
if (!sending) return null;
|
|
2274
2723
|
const last = messages[messages.length - 1];
|
|
2275
2724
|
if (last?.role === "tool") return "Calling tools\u2026";
|
|
@@ -2278,14 +2727,14 @@ function AgentWidget({
|
|
|
2278
2727
|
}, [sending, messages]);
|
|
2279
2728
|
const approvalWaiting = pendingApprovals.length > 0;
|
|
2280
2729
|
const { isAlwaysApproved } = alwaysApprove;
|
|
2281
|
-
|
|
2730
|
+
React12.useEffect(() => {
|
|
2282
2731
|
if (resolvingApprovalId) return;
|
|
2283
2732
|
const target = pendingApprovals.find((approval) => isAlwaysApproved(approval.tool_name));
|
|
2284
2733
|
if (target) void resolveApproval(target.id, true);
|
|
2285
2734
|
}, [pendingApprovals, resolvingApprovalId, isAlwaysApproved, resolveApproval]);
|
|
2286
|
-
const firedWriteIds =
|
|
2735
|
+
const firedWriteIds = React12.useRef(/* @__PURE__ */ new Set());
|
|
2287
2736
|
const { onWriteSuccess } = config;
|
|
2288
|
-
|
|
2737
|
+
React12.useEffect(() => {
|
|
2289
2738
|
if (!onWriteSuccess) return;
|
|
2290
2739
|
for (const message of messages) {
|
|
2291
2740
|
if (message.role !== "tool" || !message.is_write || message.tool_status !== "success") continue;
|
|
@@ -2295,9 +2744,9 @@ function AgentWidget({
|
|
|
2295
2744
|
onWriteSuccess({ arguments: args, toolName: message.tool_name ?? "" });
|
|
2296
2745
|
}
|
|
2297
2746
|
}, [messages, onWriteSuccess]);
|
|
2298
|
-
const lastNavigatedMessageId =
|
|
2299
|
-
const navEpochRef =
|
|
2300
|
-
|
|
2747
|
+
const lastNavigatedMessageId = React12.useRef(null);
|
|
2748
|
+
const navEpochRef = React12.useRef(-1);
|
|
2749
|
+
React12.useEffect(() => {
|
|
2301
2750
|
if (!open || fullscreen || !config.navigate || !config.pages?.length) return;
|
|
2302
2751
|
const toolMessages = messages.filter((message) => message.role === "tool");
|
|
2303
2752
|
const latest = toolMessages[toolMessages.length - 1];
|
|
@@ -2316,7 +2765,7 @@ function AgentWidget({
|
|
|
2316
2765
|
const scrollTo = typeof mapping.scrollTo === "function" ? mapping.scrollTo(args) : mapping.scrollTo;
|
|
2317
2766
|
config.navigate(path, { scrollTo: scrollTo ?? null });
|
|
2318
2767
|
}, [chat.conversationEpoch, config, fullscreen, messages, open]);
|
|
2319
|
-
const closePanel =
|
|
2768
|
+
const closePanel = React12.useCallback(() => {
|
|
2320
2769
|
setOpen(false);
|
|
2321
2770
|
setAgentMode(false);
|
|
2322
2771
|
}, [setOpen]);
|
|
@@ -2489,6 +2938,6 @@ function defineAgentConfig(config) {
|
|
|
2489
2938
|
return config;
|
|
2490
2939
|
}
|
|
2491
2940
|
|
|
2492
|
-
export { AgentApiClient, AgentApiError, AgentComposer, AgentImageLightbox, AgentPanel, AgentVoiceRecorder, AgentWidget, ChatScroller, CopyButton, DEFAULT_ACCEPT, DocumentCard, IMAGE_ACCEPT, MessageItem, TaskChecklist, ThinkingIndicator, ToolActivityGroup, ToolApprovalCard, acceptAttribute, acceptedExtensions, attachmentsEnabled, defineAgentConfig, deriveDetails, fileUploadEnabled, formatBytes, groupMessages, humanizeToolName, isImageFile, resolveToolPresentation, textExtensions, useAgentChat, useAlwaysApprove, validateFile };
|
|
2941
|
+
export { AgentApiClient, AgentApiError, AgentComposer, AgentImageLightbox, AgentPanel, AgentRealtimeClient, AgentVoiceRecorder, AgentWidget, ChatScroller, CopyButton, DEFAULT_ACCEPT, DocumentCard, IMAGE_ACCEPT, MessageItem, TaskChecklist, ThinkingIndicator, ToolActivityGroup, ToolApprovalCard, acceptAttribute, acceptedExtensions, attachmentsEnabled, defineAgentConfig, deriveDetails, fileUploadEnabled, formatBytes, groupMessages, humanizeToolName, isImageFile, resolveToolPresentation, textExtensions, useAgentChat, useAlwaysApprove, useSmoothText, validateFile };
|
|
2493
2942
|
//# sourceMappingURL=index.js.map
|
|
2494
2943
|
//# sourceMappingURL=index.js.map
|