@personaai/react 0.5.2 → 0.6.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/index.cjs CHANGED
@@ -22,7 +22,12 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  PersonaProvider: () => PersonaProvider,
24
24
  VERSION: () => VERSION,
25
+ createLogger: () => import_logger2.createLogger,
26
+ createNoopLogger: () => import_logger2.createNoopLogger,
27
+ getLogLevel: () => import_logger2.getLogLevel,
28
+ isLevelEnabled: () => import_logger2.isLevelEnabled,
25
29
  openSSEStream: () => openSSEStream,
30
+ setLogLevel: () => import_logger2.setLogLevel,
26
31
  supportsStreamingFetch: () => supportsStreamingFetch,
27
32
  useAgents: () => useAgents,
28
33
  useChat: () => useChat,
@@ -37,42 +42,106 @@ module.exports = __toCommonJS(index_exports);
37
42
 
38
43
  // src/context/PersonaContext.tsx
39
44
  var import_react = require("react");
45
+ var import_logger = require("@personaai/logger");
40
46
  var import_jsx_runtime = require("react/jsx-runtime");
41
47
  var PersonaContext = (0, import_react.createContext)(null);
42
48
  function PersonaProvider({
43
49
  baseUrl,
44
50
  getAuthToken,
45
51
  defaultAgentId,
52
+ logLevel,
53
+ logger: loggerProp,
46
54
  children
47
55
  }) {
48
- const normalizedBaseUrl = (0, import_react.useMemo)(() => baseUrl.replace(/\/+$/, ""), [baseUrl]);
56
+ const normalizedBaseUrl = (0, import_react.useMemo)(
57
+ () => baseUrl.replace(/\/+$/, ""),
58
+ [baseUrl]
59
+ );
60
+ const logger = (0, import_react.useMemo)(() => {
61
+ const l = loggerProp ?? (0, import_logger.createLogger)(
62
+ "react",
63
+ logLevel !== void 0 ? { level: logLevel } : void 0
64
+ );
65
+ l.debug("PersonaProvider created", {
66
+ baseUrl: normalizedBaseUrl,
67
+ hasDefaultAgentId: !!defaultAgentId
68
+ });
69
+ l.info("PersonaProvider mounted", { baseUrl: normalizedBaseUrl });
70
+ l.trace("PersonaProvider config", {
71
+ baseUrl: normalizedBaseUrl,
72
+ hasGetAuthToken: !!getAuthToken
73
+ });
74
+ return l;
75
+ }, [loggerProp, logLevel, normalizedBaseUrl, defaultAgentId, getAuthToken]);
49
76
  const value = (0, import_react.useMemo)(() => {
50
77
  async function fetchWithAuth(path, init = {}) {
51
78
  const token = getAuthToken ? await getAuthToken() : null;
52
79
  const headers = new Headers(init.headers || {});
53
80
  if (token) {
54
81
  headers.set("Authorization", `Bearer ${token}`);
82
+ logger.trace("fetchWithAuth with token", { path });
83
+ } else {
84
+ logger.trace("fetchWithAuth without token", { path });
85
+ logger.debug("fetchWithAuth no token", { path });
55
86
  }
56
87
  const cleanPath = path.startsWith("/") ? path : `/${path}`;
57
88
  const url = `${normalizedBaseUrl}${cleanPath}`;
58
- return fetch(url, {
59
- ...init,
60
- headers
89
+ logger.debug("fetchWithAuth request", {
90
+ path: cleanPath,
91
+ url,
92
+ hasToken: !!token
93
+ });
94
+ logger.trace("fetchWithAuth details", {
95
+ url,
96
+ path: cleanPath,
97
+ hasToken: !!token
61
98
  });
99
+ try {
100
+ const res = await fetch(url, {
101
+ ...init,
102
+ headers
103
+ });
104
+ logger.debug("fetchWithAuth response", {
105
+ path: cleanPath,
106
+ status: res.status,
107
+ ok: res.ok
108
+ });
109
+ if (!res.ok) {
110
+ logger.warn("fetchWithAuth non-ok", {
111
+ path: cleanPath,
112
+ status: res.status
113
+ });
114
+ } else {
115
+ logger.info("fetchWithAuth succeeded", {
116
+ path: cleanPath,
117
+ status: res.status
118
+ });
119
+ }
120
+ return res;
121
+ } catch (err) {
122
+ logger.error("fetchWithAuth failed", {
123
+ path: cleanPath,
124
+ error: err instanceof Error ? err.message : String(err)
125
+ });
126
+ throw err;
127
+ }
62
128
  }
63
129
  return {
64
130
  baseUrl: normalizedBaseUrl,
65
131
  getAuthToken,
66
132
  defaultAgentId,
67
- fetchWithAuth
133
+ fetchWithAuth,
134
+ logger
68
135
  };
69
- }, [normalizedBaseUrl, getAuthToken, defaultAgentId]);
136
+ }, [normalizedBaseUrl, getAuthToken, defaultAgentId, logger]);
70
137
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PersonaContext.Provider, { value, children });
71
138
  }
72
139
  function usePersonaContext() {
73
140
  const context = (0, import_react.useContext)(PersonaContext);
74
141
  if (!context) {
75
- throw new Error("usePersonaContext must be used within a <PersonaProvider>");
142
+ throw new Error(
143
+ "usePersonaContext must be used within a <PersonaProvider>"
144
+ );
76
145
  }
77
146
  return context;
78
147
  }
@@ -107,7 +176,8 @@ function xhrStream(opts) {
107
176
  return new Promise((resolveStream, rejectStream) => {
108
177
  const xhr = new XMLHttpRequest();
109
178
  xhr.open("POST", opts.url);
110
- for (const [key, val] of Object.entries(opts.headers)) xhr.setRequestHeader(key, val);
179
+ for (const [key, val] of Object.entries(opts.headers))
180
+ xhr.setRequestHeader(key, val);
111
181
  let consumed = 0;
112
182
  let finished = false;
113
183
  let failure = null;
@@ -229,7 +299,10 @@ async function openSSEStream(opts) {
229
299
  ok: false,
230
300
  errorText,
231
301
  getHeader: (name) => response.headers.get(name),
232
- reader: { read: async () => ({ done: true }), cancel: () => controller.abort() }
302
+ reader: {
303
+ read: async () => ({ done: true }),
304
+ cancel: () => controller.abort()
305
+ }
233
306
  };
234
307
  }
235
308
  if (!response.body) {
@@ -255,7 +328,8 @@ async function openSSEStream(opts) {
255
328
 
256
329
  // src/hooks/useChat.ts
257
330
  function isErrorToolContent(content) {
258
- if (typeof content !== "string" || !content.trim().startsWith("{")) return false;
331
+ if (typeof content !== "string" || !content.trim().startsWith("{"))
332
+ return false;
259
333
  try {
260
334
  return JSON.parse(content)?.status === "error";
261
335
  } catch {
@@ -268,9 +342,17 @@ function persistedTraceToActivityEntries(items) {
268
342
  if (item.type === "text") {
269
343
  if (item.text) entries.push({ kind: "text", delta: item.text });
270
344
  } else {
271
- entries.push({ kind: "tool_start", toolName: item.name, args: item.argsText });
345
+ entries.push({
346
+ kind: "tool_start",
347
+ toolName: item.name,
348
+ args: item.argsText
349
+ });
272
350
  if (item.status === "completed") {
273
- entries.push({ kind: "tool_result", toolName: item.name, result: item.resultText });
351
+ entries.push({
352
+ kind: "tool_result",
353
+ toolName: item.name,
354
+ result: item.resultText
355
+ });
274
356
  }
275
357
  }
276
358
  }
@@ -279,7 +361,8 @@ function persistedTraceToActivityEntries(items) {
279
361
  function parsePresentedFile(content) {
280
362
  try {
281
363
  const parsed = JSON.parse(content);
282
- if (parsed?.status !== "success" || typeof parsed.filePath !== "string") return null;
364
+ if (parsed?.status !== "success" || typeof parsed.filePath !== "string")
365
+ return null;
283
366
  return {
284
367
  path: parsed.filePath,
285
368
  title: typeof parsed.title === "string" ? parsed.title : parsed.filePath,
@@ -320,10 +403,27 @@ function normalizePendingInterrupt(pending) {
320
403
  return null;
321
404
  }
322
405
  function useChat(options = {}) {
323
- const { defaultAgentId, fetchWithAuth, baseUrl, getAuthToken } = usePersonaContext();
406
+ const { defaultAgentId, fetchWithAuth, baseUrl, getAuthToken, logger } = usePersonaContext();
407
+ const chatLogger = (0, import_react2.useMemo)(() => logger.child("chat"), [logger]);
324
408
  const agentId = options.agentId || defaultAgentId;
325
409
  const threadId = options.threadId;
326
- const [messages, setMessages] = (0, import_react2.useState)(options.initialMessages || []);
410
+ const didLogInitRef = (0, import_react2.useRef)(false);
411
+ if (!didLogInitRef.current) {
412
+ didLogInitRef.current = true;
413
+ chatLogger.debug("useChat init", {
414
+ agentId,
415
+ threadId,
416
+ hasInitialMessages: !!options.initialMessages?.length
417
+ });
418
+ chatLogger.trace("useChat options", {
419
+ agentId,
420
+ threadId,
421
+ initialMessageCount: options.initialMessages?.length ?? 0
422
+ });
423
+ }
424
+ const [messages, setMessages] = (0, import_react2.useState)(
425
+ options.initialMessages || []
426
+ );
327
427
  const [input, setInput] = (0, import_react2.useState)("");
328
428
  const [isStreaming, setIsStreaming] = (0, import_react2.useState)(false);
329
429
  const [isLoadingHistory, setIsLoadingHistory] = (0, import_react2.useState)(false);
@@ -336,12 +436,21 @@ function useChat(options = {}) {
336
436
  const loadedThreadIdRef = (0, import_react2.useRef)(void 0);
337
437
  const stop = (0, import_react2.useCallback)(() => {
338
438
  if (abortControllerRef.current) {
439
+ chatLogger.info("stop streaming", {});
440
+ chatLogger.debug("abort controller", {
441
+ hasController: !!abortControllerRef.current
442
+ });
339
443
  abortControllerRef.current.abort();
340
444
  abortControllerRef.current = null;
341
445
  setIsStreaming(false);
446
+ chatLogger.debug("streaming stopped");
447
+ } else {
448
+ chatLogger.trace("stop called \u2014 no active stream");
342
449
  }
343
- }, []);
450
+ }, [chatLogger]);
344
451
  const clear = (0, import_react2.useCallback)(() => {
452
+ chatLogger.info("clear chat", { messageCount: messages.length });
453
+ chatLogger.debug("clear", { messageCount: messages.length });
345
454
  stop();
346
455
  setMessages([]);
347
456
  setError(null);
@@ -349,14 +458,17 @@ function useChat(options = {}) {
349
458
  setFiles({});
350
459
  setTodos([]);
351
460
  setPresentedFile(null);
352
- }, [stop]);
461
+ }, [stop, chatLogger, messages.length]);
353
462
  const loadThreadMessages = (0, import_react2.useCallback)(
354
463
  async (id) => {
464
+ chatLogger.debug("loadThreadMessages start", { threadId: id });
465
+ chatLogger.trace("loadThreadMessages", { threadId: id });
355
466
  setIsLoadingHistory(true);
356
467
  setError(null);
357
468
  try {
358
469
  const res = await fetchWithAuth(`/threads/${id}/messages`);
359
- if (!res.ok) throw new Error(`Failed to load thread history: ${res.statusText}`);
470
+ if (!res.ok)
471
+ throw new Error(`Failed to load thread history: ${res.statusText}`);
360
472
  const body = await res.json();
361
473
  const data = body?.data ?? body;
362
474
  const raw = data?.messages ?? [];
@@ -368,42 +480,98 @@ function useChat(options = {}) {
368
480
  createdAt: /* @__PURE__ */ new Date(),
369
481
  toolCalls: m.toolCalls?.map((tc) => {
370
482
  const trace = subagentTraces[tc.toolCallId];
371
- return Array.isArray(trace) && trace.length > 0 ? { ...tc, subagentActivity: persistedTraceToActivityEntries(trace) } : tc;
483
+ return Array.isArray(trace) && trace.length > 0 ? {
484
+ ...tc,
485
+ subagentActivity: persistedTraceToActivityEntries(trace)
486
+ } : tc;
372
487
  })
373
488
  }));
374
489
  setMessages(loaded);
375
490
  setInterrupt(normalizePendingInterrupt(data?.pendingInterrupt));
376
491
  setFiles(normalizeWorkspaceFiles(data?.state?.files ?? {}));
377
492
  setTodos(data?.state?.todos ?? []);
493
+ chatLogger.info("loadThreadMessages succeeded", {
494
+ threadId: id,
495
+ messageCount: loaded.length
496
+ });
497
+ chatLogger.debug("loadThreadMessages completed", {
498
+ threadId: id,
499
+ messageCount: loaded.length
500
+ });
378
501
  return loaded;
379
502
  } catch (err) {
380
503
  const errorObj = err instanceof Error ? err : new Error(String(err));
504
+ chatLogger.warn("loadThreadMessages failed", {
505
+ threadId: id,
506
+ error: errorObj.message
507
+ });
508
+ chatLogger.error("loadThreadMessages error", {
509
+ threadId: id,
510
+ error: errorObj.message
511
+ });
381
512
  setError(errorObj);
382
513
  return [];
383
514
  } finally {
384
515
  setIsLoadingHistory(false);
516
+ chatLogger.trace("loadThreadMessages end", { threadId: id });
385
517
  }
386
518
  },
387
- [fetchWithAuth]
519
+ [fetchWithAuth, chatLogger]
388
520
  );
389
521
  (0, import_react2.useEffect)(() => {
390
- if (!threadId || isStreaming) return;
391
- if (loadedThreadIdRef.current === threadId) return;
392
- if (messages.length > 0) return;
522
+ if (!threadId || isStreaming) {
523
+ chatLogger.trace("auto-load skipped", { threadId, isStreaming });
524
+ return;
525
+ }
526
+ if (loadedThreadIdRef.current === threadId) {
527
+ chatLogger.trace("auto-load already loaded", { threadId });
528
+ return;
529
+ }
530
+ if (messages.length > 0) {
531
+ chatLogger.trace("auto-load has messages", {
532
+ threadId,
533
+ count: messages.length
534
+ });
535
+ return;
536
+ }
393
537
  loadedThreadIdRef.current = threadId;
538
+ chatLogger.info("auto-load thread history", { threadId });
539
+ chatLogger.debug("loadThreadMessages trigger", { threadId });
394
540
  void loadThreadMessages(threadId);
395
- }, [threadId, isStreaming, messages.length, loadThreadMessages]);
541
+ }, [threadId, isStreaming, messages.length, loadThreadMessages, chatLogger]);
396
542
  const sendMessage = (0, import_react2.useCallback)(
397
543
  async (contentToSend, overrideOptions) => {
398
544
  const prompt = (contentToSend ?? input).trim();
399
- if (!prompt || isStreaming) return;
545
+ if (!prompt || isStreaming) {
546
+ chatLogger.trace("sendMessage skipped", {
547
+ hasPrompt: !!prompt,
548
+ isStreaming
549
+ });
550
+ return;
551
+ }
400
552
  const targetAgentId = overrideOptions?.agentId || agentId;
401
553
  if (!targetAgentId) {
402
554
  const err = new Error("No Agent ID specified for useChat.");
555
+ chatLogger.warn("sendMessage no agentId", {});
556
+ chatLogger.error("sendMessage failed \u2014 no agent", {
557
+ error: err.message
558
+ });
403
559
  setError(err);
404
560
  options.onError?.(err);
405
561
  return;
406
562
  }
563
+ chatLogger.debug("sendMessage start", {
564
+ promptPreview: prompt.slice(0, 100),
565
+ agentId: targetAgentId,
566
+ threadId: threadId ?? overrideOptions?.threadId,
567
+ hasResume: !!overrideOptions?.resume,
568
+ messageCount: messages.length
569
+ });
570
+ chatLogger.trace("sendMessage details", {
571
+ agentId: targetAgentId,
572
+ promptPreview: prompt.slice(0, 200),
573
+ hasResume: !!overrideOptions?.resume
574
+ });
407
575
  const userMessage = {
408
576
  id: `user-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
409
577
  role: "user",
@@ -440,7 +608,13 @@ function useChat(options = {}) {
440
608
  content: m.content
441
609
  }));
442
610
  const resolvedThreadId = await (overrideOptions?.threadId ?? options.threadId);
611
+ chatLogger.trace("resolved threadId", { threadId: resolvedThreadId });
443
612
  const token = getAuthToken ? await getAuthToken() : null;
613
+ chatLogger.debug("opening SSE stream", {
614
+ agentId: targetAgentId,
615
+ hasToken: !!token,
616
+ hasThreadId: !!resolvedThreadId
617
+ });
444
618
  const stream = await openSSEStream({
445
619
  url: `${baseUrl}/chat`,
446
620
  headers: {
@@ -456,8 +630,27 @@ function useChat(options = {}) {
456
630
  signal: controller.signal
457
631
  });
458
632
  if (!stream.ok) {
459
- throw new Error(`Chat error (${stream.status}): ${stream.errorText ?? "Stream failed"}`);
633
+ chatLogger.warn("SSE stream not ok", {
634
+ status: stream.status,
635
+ errorText: stream.errorText
636
+ });
637
+ chatLogger.error("chat stream failed", {
638
+ agentId: targetAgentId,
639
+ status: stream.status,
640
+ error: stream.errorText
641
+ });
642
+ throw new Error(
643
+ `Chat error (${stream.status}): ${stream.errorText ?? "Stream failed"}`
644
+ );
460
645
  }
646
+ chatLogger.debug("SSE stream opened", {
647
+ agentId: targetAgentId,
648
+ status: stream.status
649
+ });
650
+ chatLogger.info("chat stream started", {
651
+ agentId: targetAgentId,
652
+ threadId: resolvedThreadId
653
+ });
461
654
  const reader = stream.reader;
462
655
  let buffer = "";
463
656
  let accumulatedText = "";
@@ -468,7 +661,11 @@ function useChat(options = {}) {
468
661
  const patchAssistant = (patch) => {
469
662
  setMessages(
470
663
  (prev) => prev.map(
471
- (msg) => msg.id === assistantMessageId ? { ...msg, toolCalls: Array.from(toolCallsMap.values()), ...patch } : msg
664
+ (msg) => msg.id === assistantMessageId ? {
665
+ ...msg,
666
+ toolCalls: Array.from(toolCallsMap.values()),
667
+ ...patch
668
+ } : msg
472
669
  )
473
670
  );
474
671
  };
@@ -502,11 +699,19 @@ function useChat(options = {}) {
502
699
  if (raw === "[DONE]") break;
503
700
  try {
504
701
  const event = JSON.parse(raw);
702
+ chatLogger.trace("stream event", { type: event.type, event });
505
703
  options.onEvent?.(event);
506
704
  if (event.type === "TEXT_MESSAGE_CHUNK" && event.delta) {
705
+ chatLogger.debug("text chunk", {
706
+ deltaLength: event.delta.length
707
+ });
507
708
  accumulatedText += event.delta;
508
709
  patchAssistant({ content: accumulatedText, isStreaming: true });
509
710
  } else if (event.type === "TOOL_CALL_CHUNK" && event.toolCallId) {
711
+ chatLogger.debug("tool call chunk", {
712
+ toolCallId: event.toolCallId,
713
+ toolCallName: event.toolCallName
714
+ });
510
715
  const existing = toolCallsMap.get(event.toolCallId);
511
716
  if (existing) {
512
717
  existing.args = (existing.args || "") + (event.delta || "");
@@ -520,20 +725,37 @@ function useChat(options = {}) {
520
725
  }
521
726
  patchAssistant({});
522
727
  } else if (event.type === "TOOL_CALL_RESULT") {
728
+ chatLogger.debug("tool call result", {
729
+ toolCallId: event.toolCallId
730
+ });
523
731
  const existing = toolCallsMap.get(event.toolCallId);
524
732
  if (existing) {
525
733
  existing.result = event.content;
526
734
  existing.isError = isErrorToolContent(event.content);
735
+ if (existing.isError)
736
+ chatLogger.warn("tool call error", {
737
+ toolCallId: event.toolCallId
738
+ });
527
739
  if (existing.toolName === "present_file" && !existing.isError) {
528
740
  const presented = parsePresentedFile(event.content);
529
- if (presented) setPresentedFile(presented);
741
+ if (presented) {
742
+ chatLogger.info("present_file", { path: presented.path });
743
+ setPresentedFile(presented);
744
+ }
530
745
  }
531
746
  patchAssistant({});
532
747
  }
533
748
  } else if (event.type === "STATE_SNAPSHOT") {
749
+ chatLogger.debug("state snapshot", {
750
+ fileCount: Object.keys(event.snapshot.files ?? {}).length,
751
+ todoCount: event.snapshot.todos?.length ?? 0
752
+ });
534
753
  setFiles(normalizeWorkspaceFiles(event.snapshot.files));
535
754
  setTodos(event.snapshot.todos);
536
755
  } else if (event.type === "REASONING_MESSAGE_START" && event.messageId) {
756
+ chatLogger.debug("reasoning start", {
757
+ messageId: event.messageId
758
+ });
537
759
  activeReasoningId = event.messageId;
538
760
  reasoningById.set(event.messageId, { content: "" });
539
761
  insertReasoningMessage(event.messageId, streamSeq++);
@@ -544,25 +766,39 @@ function useChat(options = {}) {
544
766
  activeReasoningId = rid;
545
767
  reasoningById.set(rid, { content: "" });
546
768
  insertReasoningMessage(rid, streamSeq++);
769
+ chatLogger.debug("reasoning lazy start", { messageId: rid });
547
770
  }
548
771
  const entry = reasoningById.get(rid);
549
772
  entry.content += event.delta;
773
+ chatLogger.trace("reasoning content", {
774
+ messageId: rid,
775
+ deltaLength: event.delta?.length ?? 0
776
+ });
550
777
  setMessages(
551
778
  (prev) => prev.map(
552
779
  (m) => m.id === rid ? { ...m, content: entry.content, isStreaming: true } : m
553
780
  )
554
781
  );
555
782
  } else if (event.type === "REASONING_END") {
783
+ chatLogger.debug("reasoning end", {
784
+ messageId: activeReasoningId
785
+ });
556
786
  const rid = activeReasoningId;
557
787
  activeReasoningId = null;
558
788
  if (rid) {
559
789
  setMessages(
560
- (prev) => prev.map((m) => m.id === rid ? { ...m, isStreaming: false } : m)
790
+ (prev) => prev.map(
791
+ (m) => m.id === rid ? { ...m, isStreaming: false } : m
792
+ )
561
793
  );
562
794
  }
563
795
  } else if (event.type === "CUSTOM") {
796
+ chatLogger.debug("custom event", { name: event.name });
564
797
  if (event.name === "hitl_request") {
565
798
  const value2 = event.value;
799
+ chatLogger.info("hitl interrupt", {
800
+ actionCount: value2.actionRequests?.length ?? 0
801
+ });
566
802
  setInterrupt({
567
803
  kind: "hitl",
568
804
  actionRequests: value2.actionRequests,
@@ -570,22 +806,49 @@ function useChat(options = {}) {
570
806
  });
571
807
  } else if (event.name === "clarification_request") {
572
808
  const value2 = event.value;
573
- setInterrupt({ kind: "clarification", questions: value2.questions });
809
+ chatLogger.info("clarification interrupt", {
810
+ questionCount: value2.questions?.length ?? 0
811
+ });
812
+ setInterrupt({
813
+ kind: "clarification",
814
+ questions: value2.questions
815
+ });
574
816
  } else if (event.name === "subagent_activity") {
575
817
  const { toolCallId, ...entry } = event.value;
818
+ chatLogger.trace("subagent activity", {
819
+ toolCallId,
820
+ kind: entry.kind
821
+ });
576
822
  const existing = toolCallsMap.get(toolCallId);
577
823
  if (existing) {
578
- existing.subagentActivity = [...existing.subagentActivity || [], entry];
824
+ existing.subagentActivity = [
825
+ ...existing.subagentActivity || [],
826
+ entry
827
+ ];
579
828
  patchAssistant({});
580
829
  }
581
830
  }
582
831
  } else if (event.type === "RUN_ERROR") {
832
+ chatLogger.warn("run error", {
833
+ message: event.message,
834
+ code: event.code
835
+ });
836
+ chatLogger.error("stream run error", {
837
+ code: event.code,
838
+ message: event.message
839
+ });
583
840
  throw new Error(event.message || "Stream error from agent");
841
+ } else {
842
+ chatLogger.trace("unhandled event", { type: event.type });
584
843
  }
585
844
  } catch (e) {
586
845
  if (e instanceof Error && e.message.startsWith("Stream error")) {
587
846
  throw e;
588
847
  }
848
+ chatLogger.warn("event parse error", {
849
+ raw: raw.slice(0, 200),
850
+ error: e instanceof Error ? e.message : String(e)
851
+ });
589
852
  }
590
853
  }
591
854
  }
@@ -598,11 +861,25 @@ function useChat(options = {}) {
598
861
  toolCalls: Array.from(toolCallsMap.values())
599
862
  };
600
863
  setMessages(
601
- (prev) => prev.map((msg) => msg.id === assistantMessageId ? finalMessage : msg)
864
+ (prev) => prev.map(
865
+ (msg) => msg.id === assistantMessageId ? finalMessage : msg
866
+ )
602
867
  );
868
+ chatLogger.info("sendMessage succeeded", {
869
+ agentId: targetAgentId,
870
+ textLength: accumulatedText.length,
871
+ toolCallCount: toolCallsMap.size
872
+ });
873
+ chatLogger.debug("sendMessage completed", {
874
+ agentId: targetAgentId,
875
+ textLength: accumulatedText.length,
876
+ eventCount: toolCallsMap.size + 1
877
+ });
603
878
  options.onFinish?.(finalMessage);
604
879
  } catch (err) {
605
880
  if (controller.signal.aborted) {
881
+ chatLogger.info("sendMessage aborted", { agentId: targetAgentId });
882
+ chatLogger.debug("stream aborted", { agentId: targetAgentId });
606
883
  setMessages(
607
884
  (prev) => prev.map(
608
885
  (msg) => msg.id === assistantMessageId ? { ...msg, isStreaming: false } : msg
@@ -611,6 +888,14 @@ function useChat(options = {}) {
611
888
  return;
612
889
  }
613
890
  const errorObj = err instanceof Error ? err : new Error(String(err));
891
+ chatLogger.warn("sendMessage failed", {
892
+ agentId: targetAgentId,
893
+ error: errorObj.message
894
+ });
895
+ chatLogger.error("sendMessage error", {
896
+ agentId: targetAgentId,
897
+ error: errorObj.message
898
+ });
614
899
  setError(errorObj);
615
900
  options.onError?.(errorObj);
616
901
  setMessages(
@@ -626,22 +911,48 @@ function useChat(options = {}) {
626
911
  setIsStreaming(false);
627
912
  abortControllerRef.current = null;
628
913
  finalizeReasoning();
914
+ chatLogger.debug("sendMessage finally", {
915
+ agentId: targetAgentId,
916
+ isStreaming: false
917
+ });
918
+ chatLogger.trace("sendMessage end", { agentId: targetAgentId });
629
919
  }
630
920
  },
631
- [agentId, baseUrl, getAuthToken, input, isStreaming, messages, options]
921
+ [
922
+ agentId,
923
+ baseUrl,
924
+ getAuthToken,
925
+ input,
926
+ isStreaming,
927
+ messages,
928
+ options,
929
+ chatLogger,
930
+ threadId
931
+ ]
932
+ );
933
+ const handleInputChange = (0, import_react2.useCallback)(
934
+ (e) => {
935
+ chatLogger.trace("handleInputChange", { length: e.target.value.length });
936
+ setInput(e.target.value);
937
+ },
938
+ [chatLogger]
632
939
  );
633
- const handleInputChange = (0, import_react2.useCallback)((e) => {
634
- setInput(e.target.value);
635
- }, []);
636
940
  const handleSubmit = (0, import_react2.useCallback)(
637
941
  (e) => {
638
942
  if (e) e.preventDefault();
943
+ chatLogger.debug("handleSubmit", {});
639
944
  void sendMessage();
640
945
  },
641
- [sendMessage]
946
+ [sendMessage, chatLogger]
642
947
  );
643
948
  const reload = (0, import_react2.useCallback)(() => {
644
- if (messages.length === 0 || isStreaming) return;
949
+ if (messages.length === 0 || isStreaming) {
950
+ chatLogger.trace("reload skipped", {
951
+ messageCount: messages.length,
952
+ isStreaming
953
+ });
954
+ return;
955
+ }
645
956
  let lastUserIndex = -1;
646
957
  for (let i = messages.length - 1; i >= 0; i--) {
647
958
  if (messages[i].role === "user") {
@@ -649,19 +960,42 @@ function useChat(options = {}) {
649
960
  break;
650
961
  }
651
962
  }
652
- if (lastUserIndex === -1) return;
963
+ if (lastUserIndex === -1) {
964
+ chatLogger.warn("reload no user message", {});
965
+ return;
966
+ }
653
967
  const lastUserMessage = messages[lastUserIndex];
968
+ chatLogger.info("reload", { messageId: lastUserMessage.id });
969
+ chatLogger.debug("reload last user", {
970
+ preview: lastUserMessage.content.slice(0, 100)
971
+ });
654
972
  setMessages(messages.slice(0, lastUserIndex));
655
973
  void sendMessage(lastUserMessage.content);
656
- }, [isStreaming, messages, sendMessage]);
974
+ }, [isStreaming, messages, sendMessage, chatLogger]);
657
975
  const resumeInterrupt = (0, import_react2.useCallback)(
658
- (resume, displayContent) => sendMessage(displayContent, { resume }),
659
- [sendMessage]
976
+ (resume, displayContent) => {
977
+ chatLogger.info("resumeInterrupt", {
978
+ kind: resume.decisions ? "hitl" : "clarification",
979
+ preview: displayContent.slice(0, 50)
980
+ });
981
+ return sendMessage(displayContent, { resume });
982
+ },
983
+ [sendMessage, chatLogger]
660
984
  );
661
- const dismissPresentedFile = (0, import_react2.useCallback)(() => setPresentedFile(null), []);
985
+ const dismissPresentedFile = (0, import_react2.useCallback)(() => {
986
+ chatLogger.debug("dismissPresentedFile", {});
987
+ setPresentedFile(null);
988
+ }, [chatLogger]);
662
989
  const openWorkspaceFile = (0, import_react2.useCallback)(
663
- (path) => setPresentedFile({ path, title: path.split("/").pop() || path, description: "" }),
664
- []
990
+ (path) => {
991
+ chatLogger.debug("openWorkspaceFile", { path });
992
+ setPresentedFile({
993
+ path,
994
+ title: path.split("/").pop() || path,
995
+ description: ""
996
+ });
997
+ },
998
+ [chatLogger]
665
999
  );
666
1000
  return {
667
1001
  messages,
@@ -693,7 +1027,10 @@ function useChat(options = {}) {
693
1027
  var import_react3 = require("react");
694
1028
  function useMemory(autoFetch = true) {
695
1029
  const { fetchWithAuth } = usePersonaContext();
696
- const [memory, setMemory] = (0, import_react3.useState)({ userFiles: [], agentMemories: [] });
1030
+ const [memory, setMemory] = (0, import_react3.useState)({
1031
+ userFiles: [],
1032
+ agentMemories: []
1033
+ });
697
1034
  const [isLoading, setIsLoading] = (0, import_react3.useState)(false);
698
1035
  const [error, setError] = (0, import_react3.useState)(null);
699
1036
  const fetchMemory = (0, import_react3.useCallback)(async () => {
@@ -721,7 +1058,8 @@ function useMemory(autoFetch = true) {
721
1058
  if (params.scope) query.set("scope", params.scope);
722
1059
  if (params.agentId) query.set("agentId", params.agentId);
723
1060
  const res = await fetchWithAuth(`/memory/file?${query.toString()}`);
724
- if (!res.ok) throw new Error(`Failed to get memory file: ${res.statusText}`);
1061
+ if (!res.ok)
1062
+ throw new Error(`Failed to get memory file: ${res.statusText}`);
725
1063
  return await res.json();
726
1064
  },
727
1065
  [fetchWithAuth]
@@ -733,7 +1071,8 @@ function useMemory(autoFetch = true) {
733
1071
  headers: { "Content-Type": "application/json" },
734
1072
  body: JSON.stringify(params)
735
1073
  });
736
- if (!res.ok) throw new Error(`Failed to write memory file: ${res.statusText}`);
1074
+ if (!res.ok)
1075
+ throw new Error(`Failed to write memory file: ${res.statusText}`);
737
1076
  const updated = await res.json();
738
1077
  void fetchMemory();
739
1078
  return updated;
@@ -748,7 +1087,8 @@ function useMemory(autoFetch = true) {
748
1087
  const res = await fetchWithAuth(`/memory/file?${query.toString()}`, {
749
1088
  method: "DELETE"
750
1089
  });
751
- if (!res.ok) throw new Error(`Failed to delete memory file: ${res.statusText}`);
1090
+ if (!res.ok)
1091
+ throw new Error(`Failed to delete memory file: ${res.statusText}`);
752
1092
  void fetchMemory();
753
1093
  },
754
1094
  [fetchWithAuth, fetchMemory]
@@ -802,7 +1142,8 @@ function useThreads(autoFetch = true) {
802
1142
  headers: { "Content-Type": "application/json" },
803
1143
  body: JSON.stringify({ agentId: targetAgentId })
804
1144
  });
805
- if (!res.ok) throw new Error(`Failed to create thread: ${res.statusText}`);
1145
+ if (!res.ok)
1146
+ throw new Error(`Failed to create thread: ${res.statusText}`);
806
1147
  const data = await res.json();
807
1148
  const created = data?.data ?? data;
808
1149
  void fetchThreads();
@@ -812,8 +1153,11 @@ function useThreads(autoFetch = true) {
812
1153
  );
813
1154
  const deleteThread = (0, import_react4.useCallback)(
814
1155
  async (threadId) => {
815
- const res = await fetchWithAuth(`/threads/${threadId}`, { method: "DELETE" });
816
- if (!res.ok) throw new Error(`Failed to delete thread: ${res.statusText}`);
1156
+ const res = await fetchWithAuth(`/threads/${threadId}`, {
1157
+ method: "DELETE"
1158
+ });
1159
+ if (!res.ok)
1160
+ throw new Error(`Failed to delete thread: ${res.statusText}`);
817
1161
  setThreads((prev) => prev.filter((t) => t._id !== threadId));
818
1162
  },
819
1163
  [fetchWithAuth]
@@ -825,7 +1169,8 @@ function useThreads(autoFetch = true) {
825
1169
  headers: { "Content-Type": "application/json" },
826
1170
  body: JSON.stringify({ ids: threadIds })
827
1171
  });
828
- if (!res.ok) throw new Error(`Failed to bulk-delete threads: ${res.statusText}`);
1172
+ if (!res.ok)
1173
+ throw new Error(`Failed to bulk-delete threads: ${res.statusText}`);
829
1174
  const data = await res.json();
830
1175
  const result = data?.data ?? data;
831
1176
  const deletedSet = new Set(result.deleted);
@@ -847,10 +1192,13 @@ function useThreads(autoFetch = true) {
847
1192
  headers: { "Content-Type": "application/json" },
848
1193
  body: JSON.stringify(input)
849
1194
  });
850
- if (!res.ok) throw new Error(`Failed to update thread: ${res.statusText}`);
1195
+ if (!res.ok)
1196
+ throw new Error(`Failed to update thread: ${res.statusText}`);
851
1197
  const data = await res.json();
852
1198
  const updated = data?.data ?? data;
853
- setThreads((prev) => prev.map((t) => t._id === threadId ? { ...t, ...updated } : t));
1199
+ setThreads(
1200
+ (prev) => prev.map((t) => t._id === threadId ? { ...t, ...updated } : t)
1201
+ );
854
1202
  return updated;
855
1203
  },
856
1204
  [fetchWithAuth]
@@ -859,6 +1207,21 @@ function useThreads(autoFetch = true) {
859
1207
  (threadId, title) => updateThread(threadId, { title }),
860
1208
  [updateThread]
861
1209
  );
1210
+ const resetThread = (0, import_react4.useCallback)(
1211
+ async (threadId) => {
1212
+ const res = await fetchWithAuth(`/threads/${threadId}/reset`, {
1213
+ method: "POST"
1214
+ });
1215
+ if (!res.ok) throw new Error(`Failed to reset thread: ${res.statusText}`);
1216
+ const data = await res.json();
1217
+ const reset = data?.data ?? data;
1218
+ setThreads(
1219
+ (prev) => prev.map((t) => t._id === threadId ? { ...t, ...reset } : t)
1220
+ );
1221
+ return reset;
1222
+ },
1223
+ [fetchWithAuth]
1224
+ );
862
1225
  const getThread = (0, import_react4.useCallback)(
863
1226
  async (threadId) => {
864
1227
  const res = await fetchWithAuth(`/threads/${threadId}`);
@@ -884,6 +1247,7 @@ function useThreads(autoFetch = true) {
884
1247
  deleteAllThreads,
885
1248
  updateThread,
886
1249
  renameThread,
1250
+ resetThread,
887
1251
  getThread
888
1252
  };
889
1253
  }
@@ -962,19 +1326,17 @@ function useFiles(autoFetch = true) {
962
1326
  headers: { "Content-Type": "application/json" },
963
1327
  body: JSON.stringify({ ids: fileIds })
964
1328
  });
965
- if (!res.ok) throw new Error(`Failed to bulk-delete files: ${res.statusText}`);
1329
+ if (!res.ok)
1330
+ throw new Error(`Failed to bulk-delete files: ${res.statusText}`);
966
1331
  const idSet = new Set(fileIds);
967
1332
  setFiles((prev) => prev.filter((f) => !idSet.has(f.id)));
968
1333
  return await res.json();
969
1334
  },
970
1335
  [fetchWithAuth]
971
1336
  );
972
- const getDownloadUrl = (0, import_react5.useCallback)(
973
- (fileId) => {
974
- return `/files/${fileId}`;
975
- },
976
- []
977
- );
1337
+ const getDownloadUrl = (0, import_react5.useCallback)((fileId) => {
1338
+ return `/files/${fileId}`;
1339
+ }, []);
978
1340
  (0, import_react5.useEffect)(() => {
979
1341
  if (autoFetch) {
980
1342
  void fetchFiles();
@@ -1086,8 +1448,11 @@ function useMcpConnections(options = {}) {
1086
1448
  try {
1087
1449
  const returnTo = options.returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
1088
1450
  const query = returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : "";
1089
- const res = await fetchWithAuth(`/agents/${agentId}/mcp-connections${query}`);
1090
- if (!res.ok) throw new Error(`Failed to load MCP connections: ${res.statusText}`);
1451
+ const res = await fetchWithAuth(
1452
+ `/agents/${agentId}/mcp-connections${query}`
1453
+ );
1454
+ if (!res.ok)
1455
+ throw new Error(`Failed to load MCP connections: ${res.statusText}`);
1091
1456
  const data = await res.json();
1092
1457
  const items = Array.isArray(data) ? data : data?.items || [];
1093
1458
  setConnections(items);
@@ -1114,12 +1479,18 @@ function useMcpConnections(options = {}) {
1114
1479
  }
1115
1480
 
1116
1481
  // src/index.ts
1117
- var VERSION = "0.4.0";
1482
+ var import_logger2 = require("@personaai/logger");
1483
+ var VERSION = "0.6.0";
1118
1484
  // Annotate the CommonJS export names for ESM import in node:
1119
1485
  0 && (module.exports = {
1120
1486
  PersonaProvider,
1121
1487
  VERSION,
1488
+ createLogger,
1489
+ createNoopLogger,
1490
+ getLogLevel,
1491
+ isLevelEnabled,
1122
1492
  openSSEStream,
1493
+ setLogLevel,
1123
1494
  supportsStreamingFetch,
1124
1495
  useAgents,
1125
1496
  useChat,