@personaai/react 0.5.1 → 0.5.4
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/index.cjs +428 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -27
- package/dist/index.d.ts +47 -27
- package/dist/index.js +430 -71
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -1,47 +1,111 @@
|
|
|
1
1
|
// src/context/PersonaContext.tsx
|
|
2
2
|
import { createContext, useContext, useMemo } from "react";
|
|
3
|
+
import { createLogger } from "@personaai/logger";
|
|
3
4
|
import { jsx } from "react/jsx-runtime";
|
|
4
5
|
var PersonaContext = createContext(null);
|
|
5
6
|
function PersonaProvider({
|
|
6
7
|
baseUrl,
|
|
7
8
|
getAuthToken,
|
|
8
9
|
defaultAgentId,
|
|
10
|
+
logLevel,
|
|
11
|
+
logger: loggerProp,
|
|
9
12
|
children
|
|
10
13
|
}) {
|
|
11
|
-
const normalizedBaseUrl = useMemo(
|
|
14
|
+
const normalizedBaseUrl = useMemo(
|
|
15
|
+
() => baseUrl.replace(/\/+$/, ""),
|
|
16
|
+
[baseUrl]
|
|
17
|
+
);
|
|
18
|
+
const logger = useMemo(() => {
|
|
19
|
+
const l = loggerProp ?? createLogger(
|
|
20
|
+
"react",
|
|
21
|
+
logLevel !== void 0 ? { level: logLevel } : void 0
|
|
22
|
+
);
|
|
23
|
+
l.debug("PersonaProvider created", {
|
|
24
|
+
baseUrl: normalizedBaseUrl,
|
|
25
|
+
hasDefaultAgentId: !!defaultAgentId
|
|
26
|
+
});
|
|
27
|
+
l.info("PersonaProvider mounted", { baseUrl: normalizedBaseUrl });
|
|
28
|
+
l.trace("PersonaProvider config", {
|
|
29
|
+
baseUrl: normalizedBaseUrl,
|
|
30
|
+
hasGetAuthToken: !!getAuthToken
|
|
31
|
+
});
|
|
32
|
+
return l;
|
|
33
|
+
}, [loggerProp, logLevel, normalizedBaseUrl, defaultAgentId, getAuthToken]);
|
|
12
34
|
const value = useMemo(() => {
|
|
13
35
|
async function fetchWithAuth(path, init = {}) {
|
|
14
36
|
const token = getAuthToken ? await getAuthToken() : null;
|
|
15
37
|
const headers = new Headers(init.headers || {});
|
|
16
38
|
if (token) {
|
|
17
39
|
headers.set("Authorization", `Bearer ${token}`);
|
|
40
|
+
logger.trace("fetchWithAuth with token", { path });
|
|
41
|
+
} else {
|
|
42
|
+
logger.trace("fetchWithAuth without token", { path });
|
|
43
|
+
logger.debug("fetchWithAuth no token", { path });
|
|
18
44
|
}
|
|
19
45
|
const cleanPath = path.startsWith("/") ? path : `/${path}`;
|
|
20
46
|
const url = `${normalizedBaseUrl}${cleanPath}`;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
47
|
+
logger.debug("fetchWithAuth request", {
|
|
48
|
+
path: cleanPath,
|
|
49
|
+
url,
|
|
50
|
+
hasToken: !!token
|
|
51
|
+
});
|
|
52
|
+
logger.trace("fetchWithAuth details", {
|
|
53
|
+
url,
|
|
54
|
+
path: cleanPath,
|
|
55
|
+
hasToken: !!token
|
|
24
56
|
});
|
|
57
|
+
try {
|
|
58
|
+
const res = await fetch(url, {
|
|
59
|
+
...init,
|
|
60
|
+
headers
|
|
61
|
+
});
|
|
62
|
+
logger.debug("fetchWithAuth response", {
|
|
63
|
+
path: cleanPath,
|
|
64
|
+
status: res.status,
|
|
65
|
+
ok: res.ok
|
|
66
|
+
});
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
logger.warn("fetchWithAuth non-ok", {
|
|
69
|
+
path: cleanPath,
|
|
70
|
+
status: res.status
|
|
71
|
+
});
|
|
72
|
+
} else {
|
|
73
|
+
logger.info("fetchWithAuth succeeded", {
|
|
74
|
+
path: cleanPath,
|
|
75
|
+
status: res.status
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return res;
|
|
79
|
+
} catch (err) {
|
|
80
|
+
logger.error("fetchWithAuth failed", {
|
|
81
|
+
path: cleanPath,
|
|
82
|
+
error: err instanceof Error ? err.message : String(err)
|
|
83
|
+
});
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
25
86
|
}
|
|
26
87
|
return {
|
|
27
88
|
baseUrl: normalizedBaseUrl,
|
|
28
89
|
getAuthToken,
|
|
29
90
|
defaultAgentId,
|
|
30
|
-
fetchWithAuth
|
|
91
|
+
fetchWithAuth,
|
|
92
|
+
logger
|
|
31
93
|
};
|
|
32
|
-
}, [normalizedBaseUrl, getAuthToken, defaultAgentId]);
|
|
94
|
+
}, [normalizedBaseUrl, getAuthToken, defaultAgentId, logger]);
|
|
33
95
|
return /* @__PURE__ */ jsx(PersonaContext.Provider, { value, children });
|
|
34
96
|
}
|
|
35
97
|
function usePersonaContext() {
|
|
36
98
|
const context = useContext(PersonaContext);
|
|
37
99
|
if (!context) {
|
|
38
|
-
throw new Error(
|
|
100
|
+
throw new Error(
|
|
101
|
+
"usePersonaContext must be used within a <PersonaProvider>"
|
|
102
|
+
);
|
|
39
103
|
}
|
|
40
104
|
return context;
|
|
41
105
|
}
|
|
42
106
|
|
|
43
107
|
// src/hooks/useChat.ts
|
|
44
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
108
|
+
import { useCallback, useEffect, useMemo as useMemo2, useRef, useState } from "react";
|
|
45
109
|
|
|
46
110
|
// src/streaming.ts
|
|
47
111
|
function supportsStreamingFetch() {
|
|
@@ -70,7 +134,8 @@ function xhrStream(opts) {
|
|
|
70
134
|
return new Promise((resolveStream, rejectStream) => {
|
|
71
135
|
const xhr = new XMLHttpRequest();
|
|
72
136
|
xhr.open("POST", opts.url);
|
|
73
|
-
for (const [key, val] of Object.entries(opts.headers))
|
|
137
|
+
for (const [key, val] of Object.entries(opts.headers))
|
|
138
|
+
xhr.setRequestHeader(key, val);
|
|
74
139
|
let consumed = 0;
|
|
75
140
|
let finished = false;
|
|
76
141
|
let failure = null;
|
|
@@ -192,7 +257,10 @@ async function openSSEStream(opts) {
|
|
|
192
257
|
ok: false,
|
|
193
258
|
errorText,
|
|
194
259
|
getHeader: (name) => response.headers.get(name),
|
|
195
|
-
reader: {
|
|
260
|
+
reader: {
|
|
261
|
+
read: async () => ({ done: true }),
|
|
262
|
+
cancel: () => controller.abort()
|
|
263
|
+
}
|
|
196
264
|
};
|
|
197
265
|
}
|
|
198
266
|
if (!response.body) {
|
|
@@ -218,7 +286,8 @@ async function openSSEStream(opts) {
|
|
|
218
286
|
|
|
219
287
|
// src/hooks/useChat.ts
|
|
220
288
|
function isErrorToolContent(content) {
|
|
221
|
-
if (typeof content !== "string" || !content.trim().startsWith("{"))
|
|
289
|
+
if (typeof content !== "string" || !content.trim().startsWith("{"))
|
|
290
|
+
return false;
|
|
222
291
|
try {
|
|
223
292
|
return JSON.parse(content)?.status === "error";
|
|
224
293
|
} catch {
|
|
@@ -231,9 +300,17 @@ function persistedTraceToActivityEntries(items) {
|
|
|
231
300
|
if (item.type === "text") {
|
|
232
301
|
if (item.text) entries.push({ kind: "text", delta: item.text });
|
|
233
302
|
} else {
|
|
234
|
-
entries.push({
|
|
303
|
+
entries.push({
|
|
304
|
+
kind: "tool_start",
|
|
305
|
+
toolName: item.name,
|
|
306
|
+
args: item.argsText
|
|
307
|
+
});
|
|
235
308
|
if (item.status === "completed") {
|
|
236
|
-
entries.push({
|
|
309
|
+
entries.push({
|
|
310
|
+
kind: "tool_result",
|
|
311
|
+
toolName: item.name,
|
|
312
|
+
result: item.resultText
|
|
313
|
+
});
|
|
237
314
|
}
|
|
238
315
|
}
|
|
239
316
|
}
|
|
@@ -242,7 +319,8 @@ function persistedTraceToActivityEntries(items) {
|
|
|
242
319
|
function parsePresentedFile(content) {
|
|
243
320
|
try {
|
|
244
321
|
const parsed = JSON.parse(content);
|
|
245
|
-
if (parsed?.status !== "success" || typeof parsed.filePath !== "string")
|
|
322
|
+
if (parsed?.status !== "success" || typeof parsed.filePath !== "string")
|
|
323
|
+
return null;
|
|
246
324
|
return {
|
|
247
325
|
path: parsed.filePath,
|
|
248
326
|
title: typeof parsed.title === "string" ? parsed.title : parsed.filePath,
|
|
@@ -283,10 +361,27 @@ function normalizePendingInterrupt(pending) {
|
|
|
283
361
|
return null;
|
|
284
362
|
}
|
|
285
363
|
function useChat(options = {}) {
|
|
286
|
-
const { defaultAgentId, fetchWithAuth, baseUrl, getAuthToken } = usePersonaContext();
|
|
364
|
+
const { defaultAgentId, fetchWithAuth, baseUrl, getAuthToken, logger } = usePersonaContext();
|
|
365
|
+
const chatLogger = useMemo2(() => logger.child("chat"), [logger]);
|
|
287
366
|
const agentId = options.agentId || defaultAgentId;
|
|
288
367
|
const threadId = options.threadId;
|
|
289
|
-
const
|
|
368
|
+
const didLogInitRef = useRef(false);
|
|
369
|
+
if (!didLogInitRef.current) {
|
|
370
|
+
didLogInitRef.current = true;
|
|
371
|
+
chatLogger.debug("useChat init", {
|
|
372
|
+
agentId,
|
|
373
|
+
threadId,
|
|
374
|
+
hasInitialMessages: !!options.initialMessages?.length
|
|
375
|
+
});
|
|
376
|
+
chatLogger.trace("useChat options", {
|
|
377
|
+
agentId,
|
|
378
|
+
threadId,
|
|
379
|
+
initialMessageCount: options.initialMessages?.length ?? 0
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
const [messages, setMessages] = useState(
|
|
383
|
+
options.initialMessages || []
|
|
384
|
+
);
|
|
290
385
|
const [input, setInput] = useState("");
|
|
291
386
|
const [isStreaming, setIsStreaming] = useState(false);
|
|
292
387
|
const [isLoadingHistory, setIsLoadingHistory] = useState(false);
|
|
@@ -299,12 +394,21 @@ function useChat(options = {}) {
|
|
|
299
394
|
const loadedThreadIdRef = useRef(void 0);
|
|
300
395
|
const stop = useCallback(() => {
|
|
301
396
|
if (abortControllerRef.current) {
|
|
397
|
+
chatLogger.info("stop streaming", {});
|
|
398
|
+
chatLogger.debug("abort controller", {
|
|
399
|
+
hasController: !!abortControllerRef.current
|
|
400
|
+
});
|
|
302
401
|
abortControllerRef.current.abort();
|
|
303
402
|
abortControllerRef.current = null;
|
|
304
403
|
setIsStreaming(false);
|
|
404
|
+
chatLogger.debug("streaming stopped");
|
|
405
|
+
} else {
|
|
406
|
+
chatLogger.trace("stop called \u2014 no active stream");
|
|
305
407
|
}
|
|
306
|
-
}, []);
|
|
408
|
+
}, [chatLogger]);
|
|
307
409
|
const clear = useCallback(() => {
|
|
410
|
+
chatLogger.info("clear chat", { messageCount: messages.length });
|
|
411
|
+
chatLogger.debug("clear", { messageCount: messages.length });
|
|
308
412
|
stop();
|
|
309
413
|
setMessages([]);
|
|
310
414
|
setError(null);
|
|
@@ -312,14 +416,17 @@ function useChat(options = {}) {
|
|
|
312
416
|
setFiles({});
|
|
313
417
|
setTodos([]);
|
|
314
418
|
setPresentedFile(null);
|
|
315
|
-
}, [stop]);
|
|
419
|
+
}, [stop, chatLogger, messages.length]);
|
|
316
420
|
const loadThreadMessages = useCallback(
|
|
317
421
|
async (id) => {
|
|
422
|
+
chatLogger.debug("loadThreadMessages start", { threadId: id });
|
|
423
|
+
chatLogger.trace("loadThreadMessages", { threadId: id });
|
|
318
424
|
setIsLoadingHistory(true);
|
|
319
425
|
setError(null);
|
|
320
426
|
try {
|
|
321
427
|
const res = await fetchWithAuth(`/threads/${id}/messages`);
|
|
322
|
-
if (!res.ok)
|
|
428
|
+
if (!res.ok)
|
|
429
|
+
throw new Error(`Failed to load thread history: ${res.statusText}`);
|
|
323
430
|
const body = await res.json();
|
|
324
431
|
const data = body?.data ?? body;
|
|
325
432
|
const raw = data?.messages ?? [];
|
|
@@ -331,42 +438,98 @@ function useChat(options = {}) {
|
|
|
331
438
|
createdAt: /* @__PURE__ */ new Date(),
|
|
332
439
|
toolCalls: m.toolCalls?.map((tc) => {
|
|
333
440
|
const trace = subagentTraces[tc.toolCallId];
|
|
334
|
-
return Array.isArray(trace) && trace.length > 0 ? {
|
|
441
|
+
return Array.isArray(trace) && trace.length > 0 ? {
|
|
442
|
+
...tc,
|
|
443
|
+
subagentActivity: persistedTraceToActivityEntries(trace)
|
|
444
|
+
} : tc;
|
|
335
445
|
})
|
|
336
446
|
}));
|
|
337
447
|
setMessages(loaded);
|
|
338
448
|
setInterrupt(normalizePendingInterrupt(data?.pendingInterrupt));
|
|
339
449
|
setFiles(normalizeWorkspaceFiles(data?.state?.files ?? {}));
|
|
340
450
|
setTodos(data?.state?.todos ?? []);
|
|
451
|
+
chatLogger.info("loadThreadMessages succeeded", {
|
|
452
|
+
threadId: id,
|
|
453
|
+
messageCount: loaded.length
|
|
454
|
+
});
|
|
455
|
+
chatLogger.debug("loadThreadMessages completed", {
|
|
456
|
+
threadId: id,
|
|
457
|
+
messageCount: loaded.length
|
|
458
|
+
});
|
|
341
459
|
return loaded;
|
|
342
460
|
} catch (err) {
|
|
343
461
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
462
|
+
chatLogger.warn("loadThreadMessages failed", {
|
|
463
|
+
threadId: id,
|
|
464
|
+
error: errorObj.message
|
|
465
|
+
});
|
|
466
|
+
chatLogger.error("loadThreadMessages error", {
|
|
467
|
+
threadId: id,
|
|
468
|
+
error: errorObj.message
|
|
469
|
+
});
|
|
344
470
|
setError(errorObj);
|
|
345
471
|
return [];
|
|
346
472
|
} finally {
|
|
347
473
|
setIsLoadingHistory(false);
|
|
474
|
+
chatLogger.trace("loadThreadMessages end", { threadId: id });
|
|
348
475
|
}
|
|
349
476
|
},
|
|
350
|
-
[fetchWithAuth]
|
|
477
|
+
[fetchWithAuth, chatLogger]
|
|
351
478
|
);
|
|
352
479
|
useEffect(() => {
|
|
353
|
-
if (!threadId || isStreaming)
|
|
354
|
-
|
|
355
|
-
|
|
480
|
+
if (!threadId || isStreaming) {
|
|
481
|
+
chatLogger.trace("auto-load skipped", { threadId, isStreaming });
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
if (loadedThreadIdRef.current === threadId) {
|
|
485
|
+
chatLogger.trace("auto-load already loaded", { threadId });
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (messages.length > 0) {
|
|
489
|
+
chatLogger.trace("auto-load has messages", {
|
|
490
|
+
threadId,
|
|
491
|
+
count: messages.length
|
|
492
|
+
});
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
356
495
|
loadedThreadIdRef.current = threadId;
|
|
496
|
+
chatLogger.info("auto-load thread history", { threadId });
|
|
497
|
+
chatLogger.debug("loadThreadMessages trigger", { threadId });
|
|
357
498
|
void loadThreadMessages(threadId);
|
|
358
|
-
}, [threadId, isStreaming, messages.length, loadThreadMessages]);
|
|
499
|
+
}, [threadId, isStreaming, messages.length, loadThreadMessages, chatLogger]);
|
|
359
500
|
const sendMessage = useCallback(
|
|
360
501
|
async (contentToSend, overrideOptions) => {
|
|
361
502
|
const prompt = (contentToSend ?? input).trim();
|
|
362
|
-
if (!prompt || isStreaming)
|
|
503
|
+
if (!prompt || isStreaming) {
|
|
504
|
+
chatLogger.trace("sendMessage skipped", {
|
|
505
|
+
hasPrompt: !!prompt,
|
|
506
|
+
isStreaming
|
|
507
|
+
});
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
363
510
|
const targetAgentId = overrideOptions?.agentId || agentId;
|
|
364
511
|
if (!targetAgentId) {
|
|
365
512
|
const err = new Error("No Agent ID specified for useChat.");
|
|
513
|
+
chatLogger.warn("sendMessage no agentId", {});
|
|
514
|
+
chatLogger.error("sendMessage failed \u2014 no agent", {
|
|
515
|
+
error: err.message
|
|
516
|
+
});
|
|
366
517
|
setError(err);
|
|
367
518
|
options.onError?.(err);
|
|
368
519
|
return;
|
|
369
520
|
}
|
|
521
|
+
chatLogger.debug("sendMessage start", {
|
|
522
|
+
promptPreview: prompt.slice(0, 100),
|
|
523
|
+
agentId: targetAgentId,
|
|
524
|
+
threadId: threadId ?? overrideOptions?.threadId,
|
|
525
|
+
hasResume: !!overrideOptions?.resume,
|
|
526
|
+
messageCount: messages.length
|
|
527
|
+
});
|
|
528
|
+
chatLogger.trace("sendMessage details", {
|
|
529
|
+
agentId: targetAgentId,
|
|
530
|
+
promptPreview: prompt.slice(0, 200),
|
|
531
|
+
hasResume: !!overrideOptions?.resume
|
|
532
|
+
});
|
|
370
533
|
const userMessage = {
|
|
371
534
|
id: `user-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
|
|
372
535
|
role: "user",
|
|
@@ -403,7 +566,13 @@ function useChat(options = {}) {
|
|
|
403
566
|
content: m.content
|
|
404
567
|
}));
|
|
405
568
|
const resolvedThreadId = await (overrideOptions?.threadId ?? options.threadId);
|
|
569
|
+
chatLogger.trace("resolved threadId", { threadId: resolvedThreadId });
|
|
406
570
|
const token = getAuthToken ? await getAuthToken() : null;
|
|
571
|
+
chatLogger.debug("opening SSE stream", {
|
|
572
|
+
agentId: targetAgentId,
|
|
573
|
+
hasToken: !!token,
|
|
574
|
+
hasThreadId: !!resolvedThreadId
|
|
575
|
+
});
|
|
407
576
|
const stream = await openSSEStream({
|
|
408
577
|
url: `${baseUrl}/chat`,
|
|
409
578
|
headers: {
|
|
@@ -419,28 +588,53 @@ function useChat(options = {}) {
|
|
|
419
588
|
signal: controller.signal
|
|
420
589
|
});
|
|
421
590
|
if (!stream.ok) {
|
|
422
|
-
|
|
591
|
+
chatLogger.warn("SSE stream not ok", {
|
|
592
|
+
status: stream.status,
|
|
593
|
+
errorText: stream.errorText
|
|
594
|
+
});
|
|
595
|
+
chatLogger.error("chat stream failed", {
|
|
596
|
+
agentId: targetAgentId,
|
|
597
|
+
status: stream.status,
|
|
598
|
+
error: stream.errorText
|
|
599
|
+
});
|
|
600
|
+
throw new Error(
|
|
601
|
+
`Chat error (${stream.status}): ${stream.errorText ?? "Stream failed"}`
|
|
602
|
+
);
|
|
423
603
|
}
|
|
604
|
+
chatLogger.debug("SSE stream opened", {
|
|
605
|
+
agentId: targetAgentId,
|
|
606
|
+
status: stream.status
|
|
607
|
+
});
|
|
608
|
+
chatLogger.info("chat stream started", {
|
|
609
|
+
agentId: targetAgentId,
|
|
610
|
+
threadId: resolvedThreadId
|
|
611
|
+
});
|
|
424
612
|
const reader = stream.reader;
|
|
425
613
|
let buffer = "";
|
|
426
614
|
let accumulatedText = "";
|
|
427
615
|
const toolCallsMap = /* @__PURE__ */ new Map();
|
|
616
|
+
let streamSeq = 0;
|
|
428
617
|
let activeReasoningId = null;
|
|
429
618
|
const reasoningById = /* @__PURE__ */ new Map();
|
|
430
619
|
const patchAssistant = (patch) => {
|
|
431
620
|
setMessages(
|
|
432
621
|
(prev) => prev.map(
|
|
433
|
-
(msg) => msg.id === assistantMessageId ? {
|
|
622
|
+
(msg) => msg.id === assistantMessageId ? {
|
|
623
|
+
...msg,
|
|
624
|
+
toolCalls: Array.from(toolCallsMap.values()),
|
|
625
|
+
...patch
|
|
626
|
+
} : msg
|
|
434
627
|
)
|
|
435
628
|
);
|
|
436
629
|
};
|
|
437
|
-
const insertReasoningMessage = (id) => {
|
|
630
|
+
const insertReasoningMessage = (id, seq) => {
|
|
438
631
|
const msg = {
|
|
439
632
|
id,
|
|
440
633
|
role: "reasoning",
|
|
441
634
|
content: "",
|
|
442
635
|
createdAt: /* @__PURE__ */ new Date(),
|
|
443
|
-
isStreaming: true
|
|
636
|
+
isStreaming: true,
|
|
637
|
+
seq
|
|
444
638
|
};
|
|
445
639
|
setMessages((prev) => {
|
|
446
640
|
const idx = prev.findIndex((m) => m.id === assistantMessageId);
|
|
@@ -463,11 +657,19 @@ function useChat(options = {}) {
|
|
|
463
657
|
if (raw === "[DONE]") break;
|
|
464
658
|
try {
|
|
465
659
|
const event = JSON.parse(raw);
|
|
660
|
+
chatLogger.trace("stream event", { type: event.type, event });
|
|
466
661
|
options.onEvent?.(event);
|
|
467
662
|
if (event.type === "TEXT_MESSAGE_CHUNK" && event.delta) {
|
|
663
|
+
chatLogger.debug("text chunk", {
|
|
664
|
+
deltaLength: event.delta.length
|
|
665
|
+
});
|
|
468
666
|
accumulatedText += event.delta;
|
|
469
667
|
patchAssistant({ content: accumulatedText, isStreaming: true });
|
|
470
668
|
} else if (event.type === "TOOL_CALL_CHUNK" && event.toolCallId) {
|
|
669
|
+
chatLogger.debug("tool call chunk", {
|
|
670
|
+
toolCallId: event.toolCallId,
|
|
671
|
+
toolCallName: event.toolCallName
|
|
672
|
+
});
|
|
471
673
|
const existing = toolCallsMap.get(event.toolCallId);
|
|
472
674
|
if (existing) {
|
|
473
675
|
existing.args = (existing.args || "") + (event.delta || "");
|
|
@@ -475,54 +677,86 @@ function useChat(options = {}) {
|
|
|
475
677
|
toolCallsMap.set(event.toolCallId, {
|
|
476
678
|
toolCallId: event.toolCallId,
|
|
477
679
|
toolName: event.toolCallName || "",
|
|
478
|
-
args: event.delta || ""
|
|
680
|
+
args: event.delta || "",
|
|
681
|
+
seq: streamSeq++
|
|
479
682
|
});
|
|
480
683
|
}
|
|
481
684
|
patchAssistant({});
|
|
482
685
|
} else if (event.type === "TOOL_CALL_RESULT") {
|
|
686
|
+
chatLogger.debug("tool call result", {
|
|
687
|
+
toolCallId: event.toolCallId
|
|
688
|
+
});
|
|
483
689
|
const existing = toolCallsMap.get(event.toolCallId);
|
|
484
690
|
if (existing) {
|
|
485
691
|
existing.result = event.content;
|
|
486
692
|
existing.isError = isErrorToolContent(event.content);
|
|
693
|
+
if (existing.isError)
|
|
694
|
+
chatLogger.warn("tool call error", {
|
|
695
|
+
toolCallId: event.toolCallId
|
|
696
|
+
});
|
|
487
697
|
if (existing.toolName === "present_file" && !existing.isError) {
|
|
488
698
|
const presented = parsePresentedFile(event.content);
|
|
489
|
-
if (presented)
|
|
699
|
+
if (presented) {
|
|
700
|
+
chatLogger.info("present_file", { path: presented.path });
|
|
701
|
+
setPresentedFile(presented);
|
|
702
|
+
}
|
|
490
703
|
}
|
|
491
704
|
patchAssistant({});
|
|
492
705
|
}
|
|
493
706
|
} else if (event.type === "STATE_SNAPSHOT") {
|
|
707
|
+
chatLogger.debug("state snapshot", {
|
|
708
|
+
fileCount: Object.keys(event.snapshot.files ?? {}).length,
|
|
709
|
+
todoCount: event.snapshot.todos?.length ?? 0
|
|
710
|
+
});
|
|
494
711
|
setFiles(normalizeWorkspaceFiles(event.snapshot.files));
|
|
495
712
|
setTodos(event.snapshot.todos);
|
|
496
713
|
} else if (event.type === "REASONING_MESSAGE_START" && event.messageId) {
|
|
714
|
+
chatLogger.debug("reasoning start", {
|
|
715
|
+
messageId: event.messageId
|
|
716
|
+
});
|
|
497
717
|
activeReasoningId = event.messageId;
|
|
498
718
|
reasoningById.set(event.messageId, { content: "" });
|
|
499
|
-
insertReasoningMessage(event.messageId);
|
|
719
|
+
insertReasoningMessage(event.messageId, streamSeq++);
|
|
500
720
|
} else if (event.type === "REASONING_MESSAGE_CONTENT") {
|
|
501
721
|
let rid = event.messageId || activeReasoningId || "";
|
|
502
722
|
if (!rid || !reasoningById.has(rid)) {
|
|
503
723
|
rid = rid || `reasoning-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
504
724
|
activeReasoningId = rid;
|
|
505
725
|
reasoningById.set(rid, { content: "" });
|
|
506
|
-
insertReasoningMessage(rid);
|
|
726
|
+
insertReasoningMessage(rid, streamSeq++);
|
|
727
|
+
chatLogger.debug("reasoning lazy start", { messageId: rid });
|
|
507
728
|
}
|
|
508
729
|
const entry = reasoningById.get(rid);
|
|
509
730
|
entry.content += event.delta;
|
|
731
|
+
chatLogger.trace("reasoning content", {
|
|
732
|
+
messageId: rid,
|
|
733
|
+
deltaLength: event.delta?.length ?? 0
|
|
734
|
+
});
|
|
510
735
|
setMessages(
|
|
511
736
|
(prev) => prev.map(
|
|
512
737
|
(m) => m.id === rid ? { ...m, content: entry.content, isStreaming: true } : m
|
|
513
738
|
)
|
|
514
739
|
);
|
|
515
740
|
} else if (event.type === "REASONING_END") {
|
|
741
|
+
chatLogger.debug("reasoning end", {
|
|
742
|
+
messageId: activeReasoningId
|
|
743
|
+
});
|
|
516
744
|
const rid = activeReasoningId;
|
|
517
745
|
activeReasoningId = null;
|
|
518
746
|
if (rid) {
|
|
519
747
|
setMessages(
|
|
520
|
-
(prev) => prev.map(
|
|
748
|
+
(prev) => prev.map(
|
|
749
|
+
(m) => m.id === rid ? { ...m, isStreaming: false } : m
|
|
750
|
+
)
|
|
521
751
|
);
|
|
522
752
|
}
|
|
523
753
|
} else if (event.type === "CUSTOM") {
|
|
754
|
+
chatLogger.debug("custom event", { name: event.name });
|
|
524
755
|
if (event.name === "hitl_request") {
|
|
525
756
|
const value2 = event.value;
|
|
757
|
+
chatLogger.info("hitl interrupt", {
|
|
758
|
+
actionCount: value2.actionRequests?.length ?? 0
|
|
759
|
+
});
|
|
526
760
|
setInterrupt({
|
|
527
761
|
kind: "hitl",
|
|
528
762
|
actionRequests: value2.actionRequests,
|
|
@@ -530,22 +764,49 @@ function useChat(options = {}) {
|
|
|
530
764
|
});
|
|
531
765
|
} else if (event.name === "clarification_request") {
|
|
532
766
|
const value2 = event.value;
|
|
533
|
-
|
|
767
|
+
chatLogger.info("clarification interrupt", {
|
|
768
|
+
questionCount: value2.questions?.length ?? 0
|
|
769
|
+
});
|
|
770
|
+
setInterrupt({
|
|
771
|
+
kind: "clarification",
|
|
772
|
+
questions: value2.questions
|
|
773
|
+
});
|
|
534
774
|
} else if (event.name === "subagent_activity") {
|
|
535
775
|
const { toolCallId, ...entry } = event.value;
|
|
776
|
+
chatLogger.trace("subagent activity", {
|
|
777
|
+
toolCallId,
|
|
778
|
+
kind: entry.kind
|
|
779
|
+
});
|
|
536
780
|
const existing = toolCallsMap.get(toolCallId);
|
|
537
781
|
if (existing) {
|
|
538
|
-
existing.subagentActivity = [
|
|
782
|
+
existing.subagentActivity = [
|
|
783
|
+
...existing.subagentActivity || [],
|
|
784
|
+
entry
|
|
785
|
+
];
|
|
539
786
|
patchAssistant({});
|
|
540
787
|
}
|
|
541
788
|
}
|
|
542
789
|
} else if (event.type === "RUN_ERROR") {
|
|
790
|
+
chatLogger.warn("run error", {
|
|
791
|
+
message: event.message,
|
|
792
|
+
code: event.code
|
|
793
|
+
});
|
|
794
|
+
chatLogger.error("stream run error", {
|
|
795
|
+
code: event.code,
|
|
796
|
+
message: event.message
|
|
797
|
+
});
|
|
543
798
|
throw new Error(event.message || "Stream error from agent");
|
|
799
|
+
} else {
|
|
800
|
+
chatLogger.trace("unhandled event", { type: event.type });
|
|
544
801
|
}
|
|
545
802
|
} catch (e) {
|
|
546
803
|
if (e instanceof Error && e.message.startsWith("Stream error")) {
|
|
547
804
|
throw e;
|
|
548
805
|
}
|
|
806
|
+
chatLogger.warn("event parse error", {
|
|
807
|
+
raw: raw.slice(0, 200),
|
|
808
|
+
error: e instanceof Error ? e.message : String(e)
|
|
809
|
+
});
|
|
549
810
|
}
|
|
550
811
|
}
|
|
551
812
|
}
|
|
@@ -558,11 +819,25 @@ function useChat(options = {}) {
|
|
|
558
819
|
toolCalls: Array.from(toolCallsMap.values())
|
|
559
820
|
};
|
|
560
821
|
setMessages(
|
|
561
|
-
(prev) => prev.map(
|
|
822
|
+
(prev) => prev.map(
|
|
823
|
+
(msg) => msg.id === assistantMessageId ? finalMessage : msg
|
|
824
|
+
)
|
|
562
825
|
);
|
|
826
|
+
chatLogger.info("sendMessage succeeded", {
|
|
827
|
+
agentId: targetAgentId,
|
|
828
|
+
textLength: accumulatedText.length,
|
|
829
|
+
toolCallCount: toolCallsMap.size
|
|
830
|
+
});
|
|
831
|
+
chatLogger.debug("sendMessage completed", {
|
|
832
|
+
agentId: targetAgentId,
|
|
833
|
+
textLength: accumulatedText.length,
|
|
834
|
+
eventCount: toolCallsMap.size + 1
|
|
835
|
+
});
|
|
563
836
|
options.onFinish?.(finalMessage);
|
|
564
837
|
} catch (err) {
|
|
565
838
|
if (controller.signal.aborted) {
|
|
839
|
+
chatLogger.info("sendMessage aborted", { agentId: targetAgentId });
|
|
840
|
+
chatLogger.debug("stream aborted", { agentId: targetAgentId });
|
|
566
841
|
setMessages(
|
|
567
842
|
(prev) => prev.map(
|
|
568
843
|
(msg) => msg.id === assistantMessageId ? { ...msg, isStreaming: false } : msg
|
|
@@ -571,6 +846,14 @@ function useChat(options = {}) {
|
|
|
571
846
|
return;
|
|
572
847
|
}
|
|
573
848
|
const errorObj = err instanceof Error ? err : new Error(String(err));
|
|
849
|
+
chatLogger.warn("sendMessage failed", {
|
|
850
|
+
agentId: targetAgentId,
|
|
851
|
+
error: errorObj.message
|
|
852
|
+
});
|
|
853
|
+
chatLogger.error("sendMessage error", {
|
|
854
|
+
agentId: targetAgentId,
|
|
855
|
+
error: errorObj.message
|
|
856
|
+
});
|
|
574
857
|
setError(errorObj);
|
|
575
858
|
options.onError?.(errorObj);
|
|
576
859
|
setMessages(
|
|
@@ -586,22 +869,48 @@ function useChat(options = {}) {
|
|
|
586
869
|
setIsStreaming(false);
|
|
587
870
|
abortControllerRef.current = null;
|
|
588
871
|
finalizeReasoning();
|
|
872
|
+
chatLogger.debug("sendMessage finally", {
|
|
873
|
+
agentId: targetAgentId,
|
|
874
|
+
isStreaming: false
|
|
875
|
+
});
|
|
876
|
+
chatLogger.trace("sendMessage end", { agentId: targetAgentId });
|
|
589
877
|
}
|
|
590
878
|
},
|
|
591
|
-
[
|
|
879
|
+
[
|
|
880
|
+
agentId,
|
|
881
|
+
baseUrl,
|
|
882
|
+
getAuthToken,
|
|
883
|
+
input,
|
|
884
|
+
isStreaming,
|
|
885
|
+
messages,
|
|
886
|
+
options,
|
|
887
|
+
chatLogger,
|
|
888
|
+
threadId
|
|
889
|
+
]
|
|
890
|
+
);
|
|
891
|
+
const handleInputChange = useCallback(
|
|
892
|
+
(e) => {
|
|
893
|
+
chatLogger.trace("handleInputChange", { length: e.target.value.length });
|
|
894
|
+
setInput(e.target.value);
|
|
895
|
+
},
|
|
896
|
+
[chatLogger]
|
|
592
897
|
);
|
|
593
|
-
const handleInputChange = useCallback((e) => {
|
|
594
|
-
setInput(e.target.value);
|
|
595
|
-
}, []);
|
|
596
898
|
const handleSubmit = useCallback(
|
|
597
899
|
(e) => {
|
|
598
900
|
if (e) e.preventDefault();
|
|
901
|
+
chatLogger.debug("handleSubmit", {});
|
|
599
902
|
void sendMessage();
|
|
600
903
|
},
|
|
601
|
-
[sendMessage]
|
|
904
|
+
[sendMessage, chatLogger]
|
|
602
905
|
);
|
|
603
906
|
const reload = useCallback(() => {
|
|
604
|
-
if (messages.length === 0 || isStreaming)
|
|
907
|
+
if (messages.length === 0 || isStreaming) {
|
|
908
|
+
chatLogger.trace("reload skipped", {
|
|
909
|
+
messageCount: messages.length,
|
|
910
|
+
isStreaming
|
|
911
|
+
});
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
605
914
|
let lastUserIndex = -1;
|
|
606
915
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
607
916
|
if (messages[i].role === "user") {
|
|
@@ -609,19 +918,42 @@ function useChat(options = {}) {
|
|
|
609
918
|
break;
|
|
610
919
|
}
|
|
611
920
|
}
|
|
612
|
-
if (lastUserIndex === -1)
|
|
921
|
+
if (lastUserIndex === -1) {
|
|
922
|
+
chatLogger.warn("reload no user message", {});
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
613
925
|
const lastUserMessage = messages[lastUserIndex];
|
|
926
|
+
chatLogger.info("reload", { messageId: lastUserMessage.id });
|
|
927
|
+
chatLogger.debug("reload last user", {
|
|
928
|
+
preview: lastUserMessage.content.slice(0, 100)
|
|
929
|
+
});
|
|
614
930
|
setMessages(messages.slice(0, lastUserIndex));
|
|
615
931
|
void sendMessage(lastUserMessage.content);
|
|
616
|
-
}, [isStreaming, messages, sendMessage]);
|
|
932
|
+
}, [isStreaming, messages, sendMessage, chatLogger]);
|
|
617
933
|
const resumeInterrupt = useCallback(
|
|
618
|
-
(resume, displayContent) =>
|
|
619
|
-
|
|
934
|
+
(resume, displayContent) => {
|
|
935
|
+
chatLogger.info("resumeInterrupt", {
|
|
936
|
+
kind: resume.decisions ? "hitl" : "clarification",
|
|
937
|
+
preview: displayContent.slice(0, 50)
|
|
938
|
+
});
|
|
939
|
+
return sendMessage(displayContent, { resume });
|
|
940
|
+
},
|
|
941
|
+
[sendMessage, chatLogger]
|
|
620
942
|
);
|
|
621
|
-
const dismissPresentedFile = useCallback(() =>
|
|
943
|
+
const dismissPresentedFile = useCallback(() => {
|
|
944
|
+
chatLogger.debug("dismissPresentedFile", {});
|
|
945
|
+
setPresentedFile(null);
|
|
946
|
+
}, [chatLogger]);
|
|
622
947
|
const openWorkspaceFile = useCallback(
|
|
623
|
-
(path) =>
|
|
624
|
-
|
|
948
|
+
(path) => {
|
|
949
|
+
chatLogger.debug("openWorkspaceFile", { path });
|
|
950
|
+
setPresentedFile({
|
|
951
|
+
path,
|
|
952
|
+
title: path.split("/").pop() || path,
|
|
953
|
+
description: ""
|
|
954
|
+
});
|
|
955
|
+
},
|
|
956
|
+
[chatLogger]
|
|
625
957
|
);
|
|
626
958
|
return {
|
|
627
959
|
messages,
|
|
@@ -653,7 +985,10 @@ function useChat(options = {}) {
|
|
|
653
985
|
import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
|
|
654
986
|
function useMemory(autoFetch = true) {
|
|
655
987
|
const { fetchWithAuth } = usePersonaContext();
|
|
656
|
-
const [memory, setMemory] = useState2({
|
|
988
|
+
const [memory, setMemory] = useState2({
|
|
989
|
+
userFiles: [],
|
|
990
|
+
agentMemories: []
|
|
991
|
+
});
|
|
657
992
|
const [isLoading, setIsLoading] = useState2(false);
|
|
658
993
|
const [error, setError] = useState2(null);
|
|
659
994
|
const fetchMemory = useCallback2(async () => {
|
|
@@ -681,7 +1016,8 @@ function useMemory(autoFetch = true) {
|
|
|
681
1016
|
if (params.scope) query.set("scope", params.scope);
|
|
682
1017
|
if (params.agentId) query.set("agentId", params.agentId);
|
|
683
1018
|
const res = await fetchWithAuth(`/memory/file?${query.toString()}`);
|
|
684
|
-
if (!res.ok)
|
|
1019
|
+
if (!res.ok)
|
|
1020
|
+
throw new Error(`Failed to get memory file: ${res.statusText}`);
|
|
685
1021
|
return await res.json();
|
|
686
1022
|
},
|
|
687
1023
|
[fetchWithAuth]
|
|
@@ -693,7 +1029,8 @@ function useMemory(autoFetch = true) {
|
|
|
693
1029
|
headers: { "Content-Type": "application/json" },
|
|
694
1030
|
body: JSON.stringify(params)
|
|
695
1031
|
});
|
|
696
|
-
if (!res.ok)
|
|
1032
|
+
if (!res.ok)
|
|
1033
|
+
throw new Error(`Failed to write memory file: ${res.statusText}`);
|
|
697
1034
|
const updated = await res.json();
|
|
698
1035
|
void fetchMemory();
|
|
699
1036
|
return updated;
|
|
@@ -708,7 +1045,8 @@ function useMemory(autoFetch = true) {
|
|
|
708
1045
|
const res = await fetchWithAuth(`/memory/file?${query.toString()}`, {
|
|
709
1046
|
method: "DELETE"
|
|
710
1047
|
});
|
|
711
|
-
if (!res.ok)
|
|
1048
|
+
if (!res.ok)
|
|
1049
|
+
throw new Error(`Failed to delete memory file: ${res.statusText}`);
|
|
712
1050
|
void fetchMemory();
|
|
713
1051
|
},
|
|
714
1052
|
[fetchWithAuth, fetchMemory]
|
|
@@ -762,7 +1100,8 @@ function useThreads(autoFetch = true) {
|
|
|
762
1100
|
headers: { "Content-Type": "application/json" },
|
|
763
1101
|
body: JSON.stringify({ agentId: targetAgentId })
|
|
764
1102
|
});
|
|
765
|
-
if (!res.ok)
|
|
1103
|
+
if (!res.ok)
|
|
1104
|
+
throw new Error(`Failed to create thread: ${res.statusText}`);
|
|
766
1105
|
const data = await res.json();
|
|
767
1106
|
const created = data?.data ?? data;
|
|
768
1107
|
void fetchThreads();
|
|
@@ -772,8 +1111,11 @@ function useThreads(autoFetch = true) {
|
|
|
772
1111
|
);
|
|
773
1112
|
const deleteThread = useCallback3(
|
|
774
1113
|
async (threadId) => {
|
|
775
|
-
const res = await fetchWithAuth(`/threads/${threadId}`, {
|
|
776
|
-
|
|
1114
|
+
const res = await fetchWithAuth(`/threads/${threadId}`, {
|
|
1115
|
+
method: "DELETE"
|
|
1116
|
+
});
|
|
1117
|
+
if (!res.ok)
|
|
1118
|
+
throw new Error(`Failed to delete thread: ${res.statusText}`);
|
|
777
1119
|
setThreads((prev) => prev.filter((t) => t._id !== threadId));
|
|
778
1120
|
},
|
|
779
1121
|
[fetchWithAuth]
|
|
@@ -785,7 +1127,8 @@ function useThreads(autoFetch = true) {
|
|
|
785
1127
|
headers: { "Content-Type": "application/json" },
|
|
786
1128
|
body: JSON.stringify({ ids: threadIds })
|
|
787
1129
|
});
|
|
788
|
-
if (!res.ok)
|
|
1130
|
+
if (!res.ok)
|
|
1131
|
+
throw new Error(`Failed to bulk-delete threads: ${res.statusText}`);
|
|
789
1132
|
const data = await res.json();
|
|
790
1133
|
const result = data?.data ?? data;
|
|
791
1134
|
const deletedSet = new Set(result.deleted);
|
|
@@ -807,10 +1150,13 @@ function useThreads(autoFetch = true) {
|
|
|
807
1150
|
headers: { "Content-Type": "application/json" },
|
|
808
1151
|
body: JSON.stringify(input)
|
|
809
1152
|
});
|
|
810
|
-
if (!res.ok)
|
|
1153
|
+
if (!res.ok)
|
|
1154
|
+
throw new Error(`Failed to update thread: ${res.statusText}`);
|
|
811
1155
|
const data = await res.json();
|
|
812
1156
|
const updated = data?.data ?? data;
|
|
813
|
-
setThreads(
|
|
1157
|
+
setThreads(
|
|
1158
|
+
(prev) => prev.map((t) => t._id === threadId ? { ...t, ...updated } : t)
|
|
1159
|
+
);
|
|
814
1160
|
return updated;
|
|
815
1161
|
},
|
|
816
1162
|
[fetchWithAuth]
|
|
@@ -922,19 +1268,17 @@ function useFiles(autoFetch = true) {
|
|
|
922
1268
|
headers: { "Content-Type": "application/json" },
|
|
923
1269
|
body: JSON.stringify({ ids: fileIds })
|
|
924
1270
|
});
|
|
925
|
-
if (!res.ok)
|
|
1271
|
+
if (!res.ok)
|
|
1272
|
+
throw new Error(`Failed to bulk-delete files: ${res.statusText}`);
|
|
926
1273
|
const idSet = new Set(fileIds);
|
|
927
1274
|
setFiles((prev) => prev.filter((f) => !idSet.has(f.id)));
|
|
928
1275
|
return await res.json();
|
|
929
1276
|
},
|
|
930
1277
|
[fetchWithAuth]
|
|
931
1278
|
);
|
|
932
|
-
const getDownloadUrl = useCallback4(
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
},
|
|
936
|
-
[]
|
|
937
|
-
);
|
|
1279
|
+
const getDownloadUrl = useCallback4((fileId) => {
|
|
1280
|
+
return `/files/${fileId}`;
|
|
1281
|
+
}, []);
|
|
938
1282
|
useEffect4(() => {
|
|
939
1283
|
if (autoFetch) {
|
|
940
1284
|
void fetchFiles();
|
|
@@ -1046,8 +1390,11 @@ function useMcpConnections(options = {}) {
|
|
|
1046
1390
|
try {
|
|
1047
1391
|
const returnTo = options.returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
|
|
1048
1392
|
const query = returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : "";
|
|
1049
|
-
const res = await fetchWithAuth(
|
|
1050
|
-
|
|
1393
|
+
const res = await fetchWithAuth(
|
|
1394
|
+
`/agents/${agentId}/mcp-connections${query}`
|
|
1395
|
+
);
|
|
1396
|
+
if (!res.ok)
|
|
1397
|
+
throw new Error(`Failed to load MCP connections: ${res.statusText}`);
|
|
1051
1398
|
const data = await res.json();
|
|
1052
1399
|
const items = Array.isArray(data) ? data : data?.items || [];
|
|
1053
1400
|
setConnections(items);
|
|
@@ -1074,11 +1421,23 @@ function useMcpConnections(options = {}) {
|
|
|
1074
1421
|
}
|
|
1075
1422
|
|
|
1076
1423
|
// src/index.ts
|
|
1077
|
-
|
|
1424
|
+
import {
|
|
1425
|
+
createLogger as createLogger2,
|
|
1426
|
+
createNoopLogger,
|
|
1427
|
+
setLogLevel,
|
|
1428
|
+
getLogLevel,
|
|
1429
|
+
isLevelEnabled
|
|
1430
|
+
} from "@personaai/logger";
|
|
1431
|
+
var VERSION = "0.5.4";
|
|
1078
1432
|
export {
|
|
1079
1433
|
PersonaProvider,
|
|
1080
1434
|
VERSION,
|
|
1435
|
+
createLogger2 as createLogger,
|
|
1436
|
+
createNoopLogger,
|
|
1437
|
+
getLogLevel,
|
|
1438
|
+
isLevelEnabled,
|
|
1081
1439
|
openSSEStream,
|
|
1440
|
+
setLogLevel,
|
|
1082
1441
|
supportsStreamingFetch,
|
|
1083
1442
|
useAgents,
|
|
1084
1443
|
useChat,
|