@personaai/react 0.5.2 → 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 +420 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -27
- package/dist/index.d.ts +34 -27
- package/dist/index.js +422 -66
- 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,8 +588,27 @@ 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 = "";
|
|
@@ -431,7 +619,11 @@ function useChat(options = {}) {
|
|
|
431
619
|
const patchAssistant = (patch) => {
|
|
432
620
|
setMessages(
|
|
433
621
|
(prev) => prev.map(
|
|
434
|
-
(msg) => msg.id === assistantMessageId ? {
|
|
622
|
+
(msg) => msg.id === assistantMessageId ? {
|
|
623
|
+
...msg,
|
|
624
|
+
toolCalls: Array.from(toolCallsMap.values()),
|
|
625
|
+
...patch
|
|
626
|
+
} : msg
|
|
435
627
|
)
|
|
436
628
|
);
|
|
437
629
|
};
|
|
@@ -465,11 +657,19 @@ function useChat(options = {}) {
|
|
|
465
657
|
if (raw === "[DONE]") break;
|
|
466
658
|
try {
|
|
467
659
|
const event = JSON.parse(raw);
|
|
660
|
+
chatLogger.trace("stream event", { type: event.type, event });
|
|
468
661
|
options.onEvent?.(event);
|
|
469
662
|
if (event.type === "TEXT_MESSAGE_CHUNK" && event.delta) {
|
|
663
|
+
chatLogger.debug("text chunk", {
|
|
664
|
+
deltaLength: event.delta.length
|
|
665
|
+
});
|
|
470
666
|
accumulatedText += event.delta;
|
|
471
667
|
patchAssistant({ content: accumulatedText, isStreaming: true });
|
|
472
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
|
+
});
|
|
473
673
|
const existing = toolCallsMap.get(event.toolCallId);
|
|
474
674
|
if (existing) {
|
|
475
675
|
existing.args = (existing.args || "") + (event.delta || "");
|
|
@@ -483,20 +683,37 @@ function useChat(options = {}) {
|
|
|
483
683
|
}
|
|
484
684
|
patchAssistant({});
|
|
485
685
|
} else if (event.type === "TOOL_CALL_RESULT") {
|
|
686
|
+
chatLogger.debug("tool call result", {
|
|
687
|
+
toolCallId: event.toolCallId
|
|
688
|
+
});
|
|
486
689
|
const existing = toolCallsMap.get(event.toolCallId);
|
|
487
690
|
if (existing) {
|
|
488
691
|
existing.result = event.content;
|
|
489
692
|
existing.isError = isErrorToolContent(event.content);
|
|
693
|
+
if (existing.isError)
|
|
694
|
+
chatLogger.warn("tool call error", {
|
|
695
|
+
toolCallId: event.toolCallId
|
|
696
|
+
});
|
|
490
697
|
if (existing.toolName === "present_file" && !existing.isError) {
|
|
491
698
|
const presented = parsePresentedFile(event.content);
|
|
492
|
-
if (presented)
|
|
699
|
+
if (presented) {
|
|
700
|
+
chatLogger.info("present_file", { path: presented.path });
|
|
701
|
+
setPresentedFile(presented);
|
|
702
|
+
}
|
|
493
703
|
}
|
|
494
704
|
patchAssistant({});
|
|
495
705
|
}
|
|
496
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
|
+
});
|
|
497
711
|
setFiles(normalizeWorkspaceFiles(event.snapshot.files));
|
|
498
712
|
setTodos(event.snapshot.todos);
|
|
499
713
|
} else if (event.type === "REASONING_MESSAGE_START" && event.messageId) {
|
|
714
|
+
chatLogger.debug("reasoning start", {
|
|
715
|
+
messageId: event.messageId
|
|
716
|
+
});
|
|
500
717
|
activeReasoningId = event.messageId;
|
|
501
718
|
reasoningById.set(event.messageId, { content: "" });
|
|
502
719
|
insertReasoningMessage(event.messageId, streamSeq++);
|
|
@@ -507,25 +724,39 @@ function useChat(options = {}) {
|
|
|
507
724
|
activeReasoningId = rid;
|
|
508
725
|
reasoningById.set(rid, { content: "" });
|
|
509
726
|
insertReasoningMessage(rid, streamSeq++);
|
|
727
|
+
chatLogger.debug("reasoning lazy start", { messageId: rid });
|
|
510
728
|
}
|
|
511
729
|
const entry = reasoningById.get(rid);
|
|
512
730
|
entry.content += event.delta;
|
|
731
|
+
chatLogger.trace("reasoning content", {
|
|
732
|
+
messageId: rid,
|
|
733
|
+
deltaLength: event.delta?.length ?? 0
|
|
734
|
+
});
|
|
513
735
|
setMessages(
|
|
514
736
|
(prev) => prev.map(
|
|
515
737
|
(m) => m.id === rid ? { ...m, content: entry.content, isStreaming: true } : m
|
|
516
738
|
)
|
|
517
739
|
);
|
|
518
740
|
} else if (event.type === "REASONING_END") {
|
|
741
|
+
chatLogger.debug("reasoning end", {
|
|
742
|
+
messageId: activeReasoningId
|
|
743
|
+
});
|
|
519
744
|
const rid = activeReasoningId;
|
|
520
745
|
activeReasoningId = null;
|
|
521
746
|
if (rid) {
|
|
522
747
|
setMessages(
|
|
523
|
-
(prev) => prev.map(
|
|
748
|
+
(prev) => prev.map(
|
|
749
|
+
(m) => m.id === rid ? { ...m, isStreaming: false } : m
|
|
750
|
+
)
|
|
524
751
|
);
|
|
525
752
|
}
|
|
526
753
|
} else if (event.type === "CUSTOM") {
|
|
754
|
+
chatLogger.debug("custom event", { name: event.name });
|
|
527
755
|
if (event.name === "hitl_request") {
|
|
528
756
|
const value2 = event.value;
|
|
757
|
+
chatLogger.info("hitl interrupt", {
|
|
758
|
+
actionCount: value2.actionRequests?.length ?? 0
|
|
759
|
+
});
|
|
529
760
|
setInterrupt({
|
|
530
761
|
kind: "hitl",
|
|
531
762
|
actionRequests: value2.actionRequests,
|
|
@@ -533,22 +764,49 @@ function useChat(options = {}) {
|
|
|
533
764
|
});
|
|
534
765
|
} else if (event.name === "clarification_request") {
|
|
535
766
|
const value2 = event.value;
|
|
536
|
-
|
|
767
|
+
chatLogger.info("clarification interrupt", {
|
|
768
|
+
questionCount: value2.questions?.length ?? 0
|
|
769
|
+
});
|
|
770
|
+
setInterrupt({
|
|
771
|
+
kind: "clarification",
|
|
772
|
+
questions: value2.questions
|
|
773
|
+
});
|
|
537
774
|
} else if (event.name === "subagent_activity") {
|
|
538
775
|
const { toolCallId, ...entry } = event.value;
|
|
776
|
+
chatLogger.trace("subagent activity", {
|
|
777
|
+
toolCallId,
|
|
778
|
+
kind: entry.kind
|
|
779
|
+
});
|
|
539
780
|
const existing = toolCallsMap.get(toolCallId);
|
|
540
781
|
if (existing) {
|
|
541
|
-
existing.subagentActivity = [
|
|
782
|
+
existing.subagentActivity = [
|
|
783
|
+
...existing.subagentActivity || [],
|
|
784
|
+
entry
|
|
785
|
+
];
|
|
542
786
|
patchAssistant({});
|
|
543
787
|
}
|
|
544
788
|
}
|
|
545
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
|
+
});
|
|
546
798
|
throw new Error(event.message || "Stream error from agent");
|
|
799
|
+
} else {
|
|
800
|
+
chatLogger.trace("unhandled event", { type: event.type });
|
|
547
801
|
}
|
|
548
802
|
} catch (e) {
|
|
549
803
|
if (e instanceof Error && e.message.startsWith("Stream error")) {
|
|
550
804
|
throw e;
|
|
551
805
|
}
|
|
806
|
+
chatLogger.warn("event parse error", {
|
|
807
|
+
raw: raw.slice(0, 200),
|
|
808
|
+
error: e instanceof Error ? e.message : String(e)
|
|
809
|
+
});
|
|
552
810
|
}
|
|
553
811
|
}
|
|
554
812
|
}
|
|
@@ -561,11 +819,25 @@ function useChat(options = {}) {
|
|
|
561
819
|
toolCalls: Array.from(toolCallsMap.values())
|
|
562
820
|
};
|
|
563
821
|
setMessages(
|
|
564
|
-
(prev) => prev.map(
|
|
822
|
+
(prev) => prev.map(
|
|
823
|
+
(msg) => msg.id === assistantMessageId ? finalMessage : msg
|
|
824
|
+
)
|
|
565
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
|
+
});
|
|
566
836
|
options.onFinish?.(finalMessage);
|
|
567
837
|
} catch (err) {
|
|
568
838
|
if (controller.signal.aborted) {
|
|
839
|
+
chatLogger.info("sendMessage aborted", { agentId: targetAgentId });
|
|
840
|
+
chatLogger.debug("stream aborted", { agentId: targetAgentId });
|
|
569
841
|
setMessages(
|
|
570
842
|
(prev) => prev.map(
|
|
571
843
|
(msg) => msg.id === assistantMessageId ? { ...msg, isStreaming: false } : msg
|
|
@@ -574,6 +846,14 @@ function useChat(options = {}) {
|
|
|
574
846
|
return;
|
|
575
847
|
}
|
|
576
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
|
+
});
|
|
577
857
|
setError(errorObj);
|
|
578
858
|
options.onError?.(errorObj);
|
|
579
859
|
setMessages(
|
|
@@ -589,22 +869,48 @@ function useChat(options = {}) {
|
|
|
589
869
|
setIsStreaming(false);
|
|
590
870
|
abortControllerRef.current = null;
|
|
591
871
|
finalizeReasoning();
|
|
872
|
+
chatLogger.debug("sendMessage finally", {
|
|
873
|
+
agentId: targetAgentId,
|
|
874
|
+
isStreaming: false
|
|
875
|
+
});
|
|
876
|
+
chatLogger.trace("sendMessage end", { agentId: targetAgentId });
|
|
592
877
|
}
|
|
593
878
|
},
|
|
594
|
-
[
|
|
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]
|
|
595
897
|
);
|
|
596
|
-
const handleInputChange = useCallback((e) => {
|
|
597
|
-
setInput(e.target.value);
|
|
598
|
-
}, []);
|
|
599
898
|
const handleSubmit = useCallback(
|
|
600
899
|
(e) => {
|
|
601
900
|
if (e) e.preventDefault();
|
|
901
|
+
chatLogger.debug("handleSubmit", {});
|
|
602
902
|
void sendMessage();
|
|
603
903
|
},
|
|
604
|
-
[sendMessage]
|
|
904
|
+
[sendMessage, chatLogger]
|
|
605
905
|
);
|
|
606
906
|
const reload = useCallback(() => {
|
|
607
|
-
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
|
+
}
|
|
608
914
|
let lastUserIndex = -1;
|
|
609
915
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
610
916
|
if (messages[i].role === "user") {
|
|
@@ -612,19 +918,42 @@ function useChat(options = {}) {
|
|
|
612
918
|
break;
|
|
613
919
|
}
|
|
614
920
|
}
|
|
615
|
-
if (lastUserIndex === -1)
|
|
921
|
+
if (lastUserIndex === -1) {
|
|
922
|
+
chatLogger.warn("reload no user message", {});
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
616
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
|
+
});
|
|
617
930
|
setMessages(messages.slice(0, lastUserIndex));
|
|
618
931
|
void sendMessage(lastUserMessage.content);
|
|
619
|
-
}, [isStreaming, messages, sendMessage]);
|
|
932
|
+
}, [isStreaming, messages, sendMessage, chatLogger]);
|
|
620
933
|
const resumeInterrupt = useCallback(
|
|
621
|
-
(resume, displayContent) =>
|
|
622
|
-
|
|
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]
|
|
623
942
|
);
|
|
624
|
-
const dismissPresentedFile = useCallback(() =>
|
|
943
|
+
const dismissPresentedFile = useCallback(() => {
|
|
944
|
+
chatLogger.debug("dismissPresentedFile", {});
|
|
945
|
+
setPresentedFile(null);
|
|
946
|
+
}, [chatLogger]);
|
|
625
947
|
const openWorkspaceFile = useCallback(
|
|
626
|
-
(path) =>
|
|
627
|
-
|
|
948
|
+
(path) => {
|
|
949
|
+
chatLogger.debug("openWorkspaceFile", { path });
|
|
950
|
+
setPresentedFile({
|
|
951
|
+
path,
|
|
952
|
+
title: path.split("/").pop() || path,
|
|
953
|
+
description: ""
|
|
954
|
+
});
|
|
955
|
+
},
|
|
956
|
+
[chatLogger]
|
|
628
957
|
);
|
|
629
958
|
return {
|
|
630
959
|
messages,
|
|
@@ -656,7 +985,10 @@ function useChat(options = {}) {
|
|
|
656
985
|
import { useCallback as useCallback2, useEffect as useEffect2, useState as useState2 } from "react";
|
|
657
986
|
function useMemory(autoFetch = true) {
|
|
658
987
|
const { fetchWithAuth } = usePersonaContext();
|
|
659
|
-
const [memory, setMemory] = useState2({
|
|
988
|
+
const [memory, setMemory] = useState2({
|
|
989
|
+
userFiles: [],
|
|
990
|
+
agentMemories: []
|
|
991
|
+
});
|
|
660
992
|
const [isLoading, setIsLoading] = useState2(false);
|
|
661
993
|
const [error, setError] = useState2(null);
|
|
662
994
|
const fetchMemory = useCallback2(async () => {
|
|
@@ -684,7 +1016,8 @@ function useMemory(autoFetch = true) {
|
|
|
684
1016
|
if (params.scope) query.set("scope", params.scope);
|
|
685
1017
|
if (params.agentId) query.set("agentId", params.agentId);
|
|
686
1018
|
const res = await fetchWithAuth(`/memory/file?${query.toString()}`);
|
|
687
|
-
if (!res.ok)
|
|
1019
|
+
if (!res.ok)
|
|
1020
|
+
throw new Error(`Failed to get memory file: ${res.statusText}`);
|
|
688
1021
|
return await res.json();
|
|
689
1022
|
},
|
|
690
1023
|
[fetchWithAuth]
|
|
@@ -696,7 +1029,8 @@ function useMemory(autoFetch = true) {
|
|
|
696
1029
|
headers: { "Content-Type": "application/json" },
|
|
697
1030
|
body: JSON.stringify(params)
|
|
698
1031
|
});
|
|
699
|
-
if (!res.ok)
|
|
1032
|
+
if (!res.ok)
|
|
1033
|
+
throw new Error(`Failed to write memory file: ${res.statusText}`);
|
|
700
1034
|
const updated = await res.json();
|
|
701
1035
|
void fetchMemory();
|
|
702
1036
|
return updated;
|
|
@@ -711,7 +1045,8 @@ function useMemory(autoFetch = true) {
|
|
|
711
1045
|
const res = await fetchWithAuth(`/memory/file?${query.toString()}`, {
|
|
712
1046
|
method: "DELETE"
|
|
713
1047
|
});
|
|
714
|
-
if (!res.ok)
|
|
1048
|
+
if (!res.ok)
|
|
1049
|
+
throw new Error(`Failed to delete memory file: ${res.statusText}`);
|
|
715
1050
|
void fetchMemory();
|
|
716
1051
|
},
|
|
717
1052
|
[fetchWithAuth, fetchMemory]
|
|
@@ -765,7 +1100,8 @@ function useThreads(autoFetch = true) {
|
|
|
765
1100
|
headers: { "Content-Type": "application/json" },
|
|
766
1101
|
body: JSON.stringify({ agentId: targetAgentId })
|
|
767
1102
|
});
|
|
768
|
-
if (!res.ok)
|
|
1103
|
+
if (!res.ok)
|
|
1104
|
+
throw new Error(`Failed to create thread: ${res.statusText}`);
|
|
769
1105
|
const data = await res.json();
|
|
770
1106
|
const created = data?.data ?? data;
|
|
771
1107
|
void fetchThreads();
|
|
@@ -775,8 +1111,11 @@ function useThreads(autoFetch = true) {
|
|
|
775
1111
|
);
|
|
776
1112
|
const deleteThread = useCallback3(
|
|
777
1113
|
async (threadId) => {
|
|
778
|
-
const res = await fetchWithAuth(`/threads/${threadId}`, {
|
|
779
|
-
|
|
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}`);
|
|
780
1119
|
setThreads((prev) => prev.filter((t) => t._id !== threadId));
|
|
781
1120
|
},
|
|
782
1121
|
[fetchWithAuth]
|
|
@@ -788,7 +1127,8 @@ function useThreads(autoFetch = true) {
|
|
|
788
1127
|
headers: { "Content-Type": "application/json" },
|
|
789
1128
|
body: JSON.stringify({ ids: threadIds })
|
|
790
1129
|
});
|
|
791
|
-
if (!res.ok)
|
|
1130
|
+
if (!res.ok)
|
|
1131
|
+
throw new Error(`Failed to bulk-delete threads: ${res.statusText}`);
|
|
792
1132
|
const data = await res.json();
|
|
793
1133
|
const result = data?.data ?? data;
|
|
794
1134
|
const deletedSet = new Set(result.deleted);
|
|
@@ -810,10 +1150,13 @@ function useThreads(autoFetch = true) {
|
|
|
810
1150
|
headers: { "Content-Type": "application/json" },
|
|
811
1151
|
body: JSON.stringify(input)
|
|
812
1152
|
});
|
|
813
|
-
if (!res.ok)
|
|
1153
|
+
if (!res.ok)
|
|
1154
|
+
throw new Error(`Failed to update thread: ${res.statusText}`);
|
|
814
1155
|
const data = await res.json();
|
|
815
1156
|
const updated = data?.data ?? data;
|
|
816
|
-
setThreads(
|
|
1157
|
+
setThreads(
|
|
1158
|
+
(prev) => prev.map((t) => t._id === threadId ? { ...t, ...updated } : t)
|
|
1159
|
+
);
|
|
817
1160
|
return updated;
|
|
818
1161
|
},
|
|
819
1162
|
[fetchWithAuth]
|
|
@@ -925,19 +1268,17 @@ function useFiles(autoFetch = true) {
|
|
|
925
1268
|
headers: { "Content-Type": "application/json" },
|
|
926
1269
|
body: JSON.stringify({ ids: fileIds })
|
|
927
1270
|
});
|
|
928
|
-
if (!res.ok)
|
|
1271
|
+
if (!res.ok)
|
|
1272
|
+
throw new Error(`Failed to bulk-delete files: ${res.statusText}`);
|
|
929
1273
|
const idSet = new Set(fileIds);
|
|
930
1274
|
setFiles((prev) => prev.filter((f) => !idSet.has(f.id)));
|
|
931
1275
|
return await res.json();
|
|
932
1276
|
},
|
|
933
1277
|
[fetchWithAuth]
|
|
934
1278
|
);
|
|
935
|
-
const getDownloadUrl = useCallback4(
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
},
|
|
939
|
-
[]
|
|
940
|
-
);
|
|
1279
|
+
const getDownloadUrl = useCallback4((fileId) => {
|
|
1280
|
+
return `/files/${fileId}`;
|
|
1281
|
+
}, []);
|
|
941
1282
|
useEffect4(() => {
|
|
942
1283
|
if (autoFetch) {
|
|
943
1284
|
void fetchFiles();
|
|
@@ -1049,8 +1390,11 @@ function useMcpConnections(options = {}) {
|
|
|
1049
1390
|
try {
|
|
1050
1391
|
const returnTo = options.returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
|
|
1051
1392
|
const query = returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : "";
|
|
1052
|
-
const res = await fetchWithAuth(
|
|
1053
|
-
|
|
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}`);
|
|
1054
1398
|
const data = await res.json();
|
|
1055
1399
|
const items = Array.isArray(data) ? data : data?.items || [];
|
|
1056
1400
|
setConnections(items);
|
|
@@ -1077,11 +1421,23 @@ function useMcpConnections(options = {}) {
|
|
|
1077
1421
|
}
|
|
1078
1422
|
|
|
1079
1423
|
// src/index.ts
|
|
1080
|
-
|
|
1424
|
+
import {
|
|
1425
|
+
createLogger as createLogger2,
|
|
1426
|
+
createNoopLogger,
|
|
1427
|
+
setLogLevel,
|
|
1428
|
+
getLogLevel,
|
|
1429
|
+
isLevelEnabled
|
|
1430
|
+
} from "@personaai/logger";
|
|
1431
|
+
var VERSION = "0.5.4";
|
|
1081
1432
|
export {
|
|
1082
1433
|
PersonaProvider,
|
|
1083
1434
|
VERSION,
|
|
1435
|
+
createLogger2 as createLogger,
|
|
1436
|
+
createNoopLogger,
|
|
1437
|
+
getLogLevel,
|
|
1438
|
+
isLevelEnabled,
|
|
1084
1439
|
openSSEStream,
|
|
1440
|
+
setLogLevel,
|
|
1085
1441
|
supportsStreamingFetch,
|
|
1086
1442
|
useAgents,
|
|
1087
1443
|
useChat,
|