@agno-hq/chat-react 0.3.2 → 0.3.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.
Files changed (33) hide show
  1. package/README.md +81 -5
  2. package/dist/chat/index.cjs +99 -83
  3. package/dist/chat/index.d.cts +80 -16
  4. package/dist/chat/index.d.ts +80 -16
  5. package/dist/chat/index.js +2 -2
  6. package/dist/{chunk-RE7ZT73K.js → chunk-5QKU7OPE.js} +2 -2
  7. package/dist/{chunk-RE7ZT73K.js.map → chunk-5QKU7OPE.js.map} +1 -1
  8. package/dist/{chunk-6V2YIPHF.js → chunk-BCVKAMSO.js} +22 -4
  9. package/dist/chunk-BCVKAMSO.js.map +1 -0
  10. package/dist/{chunk-Q73XHL35.cjs → chunk-ESV52ERB.cjs} +771 -444
  11. package/dist/chunk-ESV52ERB.cjs.map +1 -0
  12. package/dist/{chunk-2WM3CCFE.cjs → chunk-HBSF434L.cjs} +2 -2
  13. package/dist/{chunk-2WM3CCFE.cjs.map → chunk-HBSF434L.cjs.map} +1 -1
  14. package/dist/{chunk-XTP3TMEZ.js → chunk-OGDPK4Z3.js} +602 -279
  15. package/dist/chunk-OGDPK4Z3.js.map +1 -0
  16. package/dist/{chunk-55HQJGLP.cjs → chunk-Y34YRC5T.cjs} +22 -3
  17. package/dist/chunk-Y34YRC5T.cjs.map +1 -0
  18. package/dist/{client-DUyVqxU9.d.cts → client-Dwk6ThnW.d.cts} +24 -12
  19. package/dist/{client-DUyVqxU9.d.ts → client-Dwk6ThnW.d.ts} +24 -12
  20. package/dist/core/index.cjs +23 -23
  21. package/dist/core/index.d.cts +2 -2
  22. package/dist/core/index.d.ts +2 -2
  23. package/dist/core/index.js +2 -2
  24. package/dist/index.cjs +122 -106
  25. package/dist/index.d.cts +2 -2
  26. package/dist/index.d.ts +2 -2
  27. package/dist/index.js +3 -3
  28. package/dist/styles.css +79 -34
  29. package/package.json +1 -1
  30. package/dist/chunk-55HQJGLP.cjs.map +0 -1
  31. package/dist/chunk-6V2YIPHF.js.map +0 -1
  32. package/dist/chunk-Q73XHL35.cjs.map +0 -1
  33. package/dist/chunk-XTP3TMEZ.js.map +0 -1
@@ -1,10 +1,176 @@
1
- import { AgnoClient, activityLabel, isSubRunEvent, applySubRunEvent, isStepEvent, applyStepEvent, isStartedEvent, isToolEvent, toolsFromEvent, mergeTool, isContentEvent, applyContentEvent, isReasoningStepEvent, isReasoningCompletedEvent, isFollowupsCompletedEvent, isPausedEvent, isCompletedEvent, isCancelledEvent, isErrorEvent, sessionRunsToMessages, isToolCompletedEvent } from './chunk-6V2YIPHF.js';
2
- import React7, { createContext, useMemo, useState, useRef, useEffect, useCallback, useContext, useId, isValidElement, cloneElement, useLayoutEffect, useReducer } from 'react';
1
+ import { AgnoClient, activityLabel, isSubRunEvent, applySubRunEvent, isStepEvent, applyStepEvent, isStartedEvent, isToolEvent, toolsFromEvent, mergeTool, isContentEvent, applyContentEvent, isReasoningStepEvent, reasoningStepsFromEvent, isReasoningCompletedEvent, isFollowupsCompletedEvent, isPausedEvent, isCompletedEvent, isCancelledEvent, isErrorEvent, sessionRunsToMessages, isToolCompletedEvent } from './chunk-BCVKAMSO.js';
2
+ import React8, { createContext, useMemo, useState, useRef, useEffect, useCallback, useContext, useId, isValidElement, cloneElement, useLayoutEffect, useReducer } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { createPortal } from 'react-dom';
5
- import { ChevronDown as ChevronDown$1, ArrowUp as ArrowUp$1, Paperclip as Paperclip$1, File, FileType2, FileAudio as FileAudio$1, FileVideo as FileVideo$1, Wrench as Wrench$1, BookOpen as BookOpen$1, Globe as Globe$1, Link, Brain as Brain$1, Box as Box$1, Plus as Plus$1, Check as Check$1, Copy as Copy$1, X, MemoryStick, RefreshCcw, Activity, Trash2, MessageSquare, LoaderCircle, Square } from 'lucide-react';
5
+ import { ChevronDown as ChevronDown$1, ArrowUp as ArrowUp$1, Paperclip as Paperclip$1, File, FileType2, FileAudio as FileAudio$1, FileVideo as FileVideo$1, Wrench as Wrench$1, BookOpen as BookOpen$1, Globe as Globe$1, Link as Link$1, Brain as Brain$1, Box as Box$1, Plus as Plus$1, Check as Check$1, Copy as Copy$1, X, MemoryStick, RefreshCcw, Activity, Trash2, MessageSquare, LoaderCircle, Square } from 'lucide-react';
6
6
  import { Streamdown } from 'streamdown';
7
7
 
8
+ function useRunView(initialMessages = []) {
9
+ const [view, setView] = useState({
10
+ messages: initialMessages,
11
+ events: [],
12
+ status: "idle",
13
+ activity: null,
14
+ error: null
15
+ });
16
+ const viewRef = useRef(view);
17
+ const update = useCallback((key, action) => {
18
+ const value = typeof action === "function" ? action(viewRef.current[key]) : action;
19
+ if (Object.is(value, viewRef.current[key])) return;
20
+ viewRef.current = { ...viewRef.current, [key]: value };
21
+ setView(viewRef.current);
22
+ }, []);
23
+ const setters = useMemo(() => ({
24
+ setMessages: (action) => update("messages", action),
25
+ setEvents: (action) => update("events", action),
26
+ setStatus: (action) => update("status", action),
27
+ setActivity: (action) => update("activity", action),
28
+ setError: (action) => update("error", action)
29
+ }), [update]);
30
+ return { ...view, ...setters, viewRef };
31
+ }
32
+
33
+ // src/chat/streaming/bufferTextEvents.ts
34
+ var DEFAULT_STREAMING_OPTIONS = Object.freeze({
35
+ enabled: true,
36
+ flushIntervalMs: 150,
37
+ maxBufferEvents: 100,
38
+ maxBufferChars: 16384
39
+ });
40
+ var MAX_TIMEOUT_MS = 2147483647;
41
+ var RICH_FIELDS = [
42
+ "tool",
43
+ "tools",
44
+ "response_audio",
45
+ "images",
46
+ "videos",
47
+ "audio",
48
+ "image",
49
+ "references",
50
+ "citations",
51
+ "extra_data"
52
+ ];
53
+ function isPlainText(event) {
54
+ return (event.event === "RunContent" || event.event === "TeamRunContent") && typeof event.content === "string" && event.content.length > 0 && RICH_FIELDS.every((key) => event[key] == null);
55
+ }
56
+ function isContinuationOf(previous, next) {
57
+ const keys = /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(next)]);
58
+ for (const key of keys) {
59
+ if (key === "content") continue;
60
+ const before = previous[key];
61
+ const after = next[key];
62
+ if (key === "event_index" && typeof before === "number" && typeof after === "number") {
63
+ if (after <= before) return false;
64
+ continue;
65
+ }
66
+ if (!Object.is(before, after)) return false;
67
+ }
68
+ return true;
69
+ }
70
+ function boundedNumber(value, fallback, min) {
71
+ return value !== void 0 && Number.isFinite(value) && value >= min ? Math.min(Math.floor(value), MAX_TIMEOUT_MS) : fallback;
72
+ }
73
+ function bufferTextEvents(deliver, options = {}) {
74
+ const defaults = DEFAULT_STREAMING_OPTIONS;
75
+ const interval = boundedNumber(
76
+ options.flushIntervalMs,
77
+ defaults.flushIntervalMs,
78
+ 0
79
+ );
80
+ const maxEvents = boundedNumber(
81
+ options.maxBufferEvents,
82
+ defaults.maxBufferEvents,
83
+ 1
84
+ );
85
+ const maxChars = boundedNumber(
86
+ options.maxBufferChars,
87
+ defaults.maxBufferChars,
88
+ 1
89
+ );
90
+ const enabled = (options.enabled ?? defaults.enabled) && interval > 0;
91
+ let events = [];
92
+ let chunks = [];
93
+ let chars = 0;
94
+ let hasText = false;
95
+ let disposed = false;
96
+ let timer;
97
+ const isHidden = () => typeof document !== "undefined" && document.visibilityState === "hidden";
98
+ const onVisibilityChange = () => {
99
+ if (isHidden()) flush();
100
+ };
101
+ const stopObserving = () => {
102
+ if (typeof document !== "undefined")
103
+ document.removeEventListener("visibilitychange", onVisibilityChange);
104
+ };
105
+ const cancelScheduled = () => {
106
+ stopObserving();
107
+ clearTimeout(timer);
108
+ timer = void 0;
109
+ };
110
+ const flush = () => {
111
+ cancelScheduled();
112
+ const batch = { events, chunks };
113
+ events = [];
114
+ chunks = [];
115
+ chars = 0;
116
+ if (!disposed && (batch.events.length || batch.chunks.length))
117
+ deliver(batch);
118
+ };
119
+ const schedule = () => {
120
+ if (timer !== void 0 || disposed) return;
121
+ if (isHidden()) return flush();
122
+ if (typeof document !== "undefined")
123
+ document.addEventListener("visibilitychange", onVisibilityChange);
124
+ timer = setTimeout(() => {
125
+ timer = void 0;
126
+ flush();
127
+ }, interval);
128
+ };
129
+ const deliverNow = (event) => {
130
+ if (!disposed) deliver({ events: [event], chunks: [event] });
131
+ };
132
+ const append = (event) => {
133
+ const last = chunks[chunks.length - 1];
134
+ if (last && isContinuationOf(last, event)) {
135
+ chunks[chunks.length - 1] = {
136
+ ...event,
137
+ content: String(last.content) + event.content
138
+ };
139
+ } else {
140
+ chunks.push(event);
141
+ }
142
+ events.push(event);
143
+ chars += String(event.content).length;
144
+ };
145
+ return {
146
+ push(event) {
147
+ if (disposed) return;
148
+ if (!enabled || !isPlainText(event)) {
149
+ flush();
150
+ hasText = false;
151
+ deliverNow(event);
152
+ return;
153
+ }
154
+ if (!hasText) {
155
+ hasText = true;
156
+ deliverNow(event);
157
+ return;
158
+ }
159
+ append(event);
160
+ if (events.length >= maxEvents || chars >= maxChars) flush();
161
+ else schedule();
162
+ },
163
+ flush,
164
+ dispose() {
165
+ disposed = true;
166
+ cancelScheduled();
167
+ events = [];
168
+ chunks = [];
169
+ }
170
+ };
171
+ }
172
+
173
+ // src/chat/useAgnoChat.ts
8
174
  var messageCounter = 0;
9
175
  var nextId = () => `m${Date.now().toString(36)}-${(messageCounter++).toString(36)}`;
10
176
  var nowSeconds = () => Math.floor(Date.now() / 1e3);
@@ -14,12 +180,20 @@ function useAgnoChat(options) {
14
180
  if (options.client) return options.client;
15
181
  return new AgnoClient({ baseUrl: options.baseUrl ?? "", headers: options.headers });
16
182
  }, [options.client, options.baseUrl, JSON.stringify(options.headers)]);
17
- const [messages, setMessages] = useState(options.initialMessages ?? []);
18
- const [events, setEvents] = useState([]);
19
- const [currentEvent, setCurrentEvent] = useState(null);
20
- const [status, setStatus] = useState("idle");
21
- const [activity, setActivity] = useState(null);
22
- const [error, setError] = useState(null);
183
+ const {
184
+ messages,
185
+ events,
186
+ status,
187
+ activity,
188
+ error,
189
+ viewRef,
190
+ setMessages,
191
+ setEvents,
192
+ setStatus,
193
+ setActivity,
194
+ setError
195
+ } = useRunView(options.initialMessages);
196
+ const currentEvent = events[events.length - 1] ?? null;
23
197
  const [sessionId, setSessionId] = useState(options.sessionId);
24
198
  const [sessions, setSessions] = useState([]);
25
199
  const [sessionsLoading, setSessionsLoading] = useState(false);
@@ -29,10 +203,14 @@ function useAgnoChat(options) {
29
203
  const backgroundRef = useRef(/* @__PURE__ */ new Map());
30
204
  const activeMsgIdRef = useRef(null);
31
205
  const runIdRef = useRef(void 0);
206
+ const streamingRef = useRef(options.streaming);
32
207
  const entityRef = useRef(entity);
33
208
  const sessionIdRef = useRef(options.sessionId);
34
209
  const pendingSessionNameRef = useRef(void 0);
35
210
  const bumpedSessionRef = useRef(void 0);
211
+ useEffect(() => {
212
+ streamingRef.current = options.streaming;
213
+ }, [options.streaming]);
36
214
  useEffect(() => {
37
215
  entityRef.current = entity;
38
216
  }, [entity]);
@@ -52,44 +230,37 @@ function useAgnoChat(options) {
52
230
  return [entry, ...rest];
53
231
  });
54
232
  }, []);
55
- const viewRef = useRef({ messages, events, status, activity, error });
56
- useEffect(() => {
57
- viewRef.current = { messages, events, status, activity, error };
58
- });
59
233
  const writeMessages = useCallback(
60
234
  (chan, fn) => {
61
235
  const park = chan?.park;
62
236
  if (park) park.messages = fn(park.messages);
63
237
  else setMessages(fn);
64
238
  },
65
- []
239
+ [setMessages]
66
240
  );
67
- const writeEvents = useCallback((chan, e) => {
68
- const park = chan?.park;
69
- if (park) park.events = [...park.events, e];
70
- else {
71
- setEvents((prev) => [...prev, e]);
72
- setCurrentEvent(e);
73
- }
74
- }, []);
241
+ const writeEvents = useCallback((chan, incoming) => {
242
+ const park = chan.park;
243
+ if (park) park.events = [...park.events, ...incoming];
244
+ else setEvents((previous) => [...previous, ...incoming]);
245
+ }, [setEvents]);
75
246
  const writeStatus = useCallback((chan, status2) => {
76
247
  const park = chan?.park;
77
248
  if (park) park.status = status2;
78
249
  else setStatus(status2);
79
- }, []);
250
+ }, [setStatus]);
80
251
  const writeActivity = useCallback((chan, activity2) => {
81
252
  const park = chan?.park;
82
253
  if (park) park.activity = activity2;
83
254
  else setActivity(activity2);
84
- }, []);
255
+ }, [setActivity]);
85
256
  const writeError = useCallback((chan, message) => {
86
257
  const park = chan?.park;
87
258
  if (park) park.error = message;
88
259
  else setError(message);
89
- }, []);
260
+ }, [setError]);
90
261
  const patchActive = useCallback(
91
262
  (chan, patch) => {
92
- const id = chan?.park ? chan.park.activeMsgId : activeMsgIdRef.current;
263
+ const id = chan ? chan.messageId : activeMsgIdRef.current;
93
264
  if (!id) return;
94
265
  writeMessages(chan, (prev) => prev.map((m) => m.id === id ? patch(m) : m));
95
266
  },
@@ -97,9 +268,6 @@ function useAgnoChat(options) {
97
268
  );
98
269
  const applyEvent = useCallback(
99
270
  (chan, e) => {
100
- writeEvents(chan, e);
101
- patchActive(chan, (m) => ({ ...m, events: [...m.events ?? [], e] }));
102
- onEvent?.(e);
103
271
  const label2 = activityLabel(e);
104
272
  if (label2) writeActivity(chan, label2);
105
273
  if (e.session_id && !chan?.park && e.session_id !== sessionIdRef.current) {
@@ -118,7 +286,7 @@ function useAgnoChat(options) {
118
286
  else runIdRef.current = e.run_id;
119
287
  }
120
288
  if (isSubRunEvent(e)) {
121
- const kind = entityRef.current?.type === "workflow" ? "executor" : "member";
289
+ const kind = (chan?.entityType ?? entityRef.current?.type) === "workflow" ? "executor" : "member";
122
290
  patchActive(chan, (m) => applySubRunEvent(m, e, kind));
123
291
  return;
124
292
  }
@@ -151,7 +319,7 @@ function useAgnoChat(options) {
151
319
  return;
152
320
  }
153
321
  if (isReasoningStepEvent(e)) {
154
- const incoming = e.reasoning_steps ?? e.extra_data?.reasoning_steps ?? [];
322
+ const incoming = reasoningStepsFromEvent(e);
155
323
  if (incoming.length) {
156
324
  patchActive(chan, (m) => ({
157
325
  ...m,
@@ -161,8 +329,8 @@ function useAgnoChat(options) {
161
329
  return;
162
330
  }
163
331
  if (isReasoningCompletedEvent(e)) {
164
- const steps = e.reasoning_steps ?? e.extra_data?.reasoning_steps;
165
- if (steps?.length) patchActive(chan, (m) => ({ ...m, reasoning_steps: steps }));
332
+ const steps = reasoningStepsFromEvent(e);
333
+ if (steps.length) patchActive(chan, (m) => ({ ...m, reasoning_steps: steps }));
166
334
  return;
167
335
  }
168
336
  if (isFollowupsCompletedEvent(e)) {
@@ -178,7 +346,7 @@ function useAgnoChat(options) {
178
346
  patchActive(chan, (m) => {
179
347
  let tool_calls = m.tool_calls ?? [];
180
348
  for (const t of pausedTools2) tool_calls = mergeTool(tool_calls, t);
181
- return { ...m, status: "paused", streaming: false, requirements, tool_calls };
349
+ return { ...m, status: "paused", streaming: false, requirements, step_requirements: e.step_requirements ?? m.step_requirements, tool_calls };
182
350
  });
183
351
  return;
184
352
  }
@@ -217,13 +385,11 @@ function useAgnoChat(options) {
217
385
  }
218
386
  },
219
387
  [
220
- onEvent,
221
388
  onSessionId,
222
389
  patchActive,
223
390
  upsertSession,
224
391
  writeActivity,
225
392
  writeError,
226
- writeEvents,
227
393
  writeStatus
228
394
  ]
229
395
  );
@@ -261,12 +427,11 @@ function useAgnoChat(options) {
261
427
  bumpedSessionRef.current = park.bumped;
262
428
  setMessages(park.messages);
263
429
  setEvents(park.events);
264
- setCurrentEvent(park.events[park.events.length - 1] ?? null);
265
430
  setStatus(park.status);
266
431
  setActivity(park.activity);
267
432
  setError(park.error);
268
433
  return true;
269
- }, []);
434
+ }, [setMessages, setEvents, setStatus, setActivity, setError]);
270
435
  const releaseCurrentRun = useCallback(() => {
271
436
  const chan = channelRef.current;
272
437
  if (!chan || chan.park) return;
@@ -282,29 +447,64 @@ function useAgnoChat(options) {
282
447
  const drive = useCallback(
283
448
  async (starter) => {
284
449
  const controller = new AbortController();
285
- const chan = { controller, park: null };
450
+ let finished = false;
451
+ const isCurrent = () => !controller.signal.aborted && (channelRef.current === chan || Boolean(chan.park && backgroundRef.current.get(chan.park.sessionId) === chan));
452
+ const chan = {
453
+ controller,
454
+ park: null,
455
+ messageId: activeMsgIdRef.current,
456
+ entityType: entityRef.current?.type,
457
+ buffer: bufferTextEvents(({ events: incoming, chunks }) => {
458
+ if (!isCurrent()) return;
459
+ if (incoming.length) {
460
+ writeEvents(chan, incoming);
461
+ patchActive(chan, (m) => ({ ...m, events: [...m.events ?? [], ...incoming] }));
462
+ }
463
+ for (const chunk of chunks) applyEvent(chan, chunk);
464
+ }, streamingRef.current)
465
+ };
466
+ controller.signal.addEventListener("abort", () => chan.buffer.dispose(), { once: true });
286
467
  channelRef.current = chan;
287
468
  abortRef.current = controller;
288
- setError(null);
289
- setStatus("streaming");
290
- await starter({
291
- signal: controller.signal,
292
- // `chan.park` is read at call time, so events follow the run when the
293
- // user navigates away mid-stream.
294
- onEvent: (e) => applyEvent(chan, e),
295
- onError: (err) => {
296
- patchActive(chan, (m) => ({ ...m, streaming: false, status: "error", error: err.message }));
297
- writeError(chan, err.message);
298
- writeStatus(chan, "error");
299
- },
300
- onComplete: () => {
301
- writeActivity(chan, null);
302
- patchActive(chan, (m) => m.streaming ? { ...m, streaming: false } : m);
303
- const park = chan.park;
304
- if (park) park.status = park.status === "streaming" ? "completed" : park.status;
305
- else setStatus((s) => s === "streaming" ? "completed" : s);
306
- }
307
- });
469
+ writeError(chan, null);
470
+ writeStatus(chan, "streaming");
471
+ const complete = () => {
472
+ if (finished || !isCurrent()) return;
473
+ chan.buffer.flush();
474
+ finished = true;
475
+ chan.buffer.dispose();
476
+ writeActivity(chan, null);
477
+ patchActive(chan, (m) => m.streaming ? { ...m, streaming: false, status: "completed" } : m);
478
+ const status2 = chan.park?.status ?? viewRef.current.status;
479
+ if (status2 === "streaming") writeStatus(chan, "completed");
480
+ };
481
+ const fail = (err) => {
482
+ if (finished || !isCurrent()) return;
483
+ chan.buffer.flush();
484
+ finished = true;
485
+ chan.buffer.dispose();
486
+ patchActive(chan, (m) => ({ ...m, streaming: false, status: "error", error: err.message }));
487
+ writeActivity(chan, null);
488
+ writeError(chan, err.message);
489
+ writeStatus(chan, "error");
490
+ };
491
+ try {
492
+ await starter({
493
+ signal: controller.signal,
494
+ onEvent: (e) => {
495
+ if (finished || !isCurrent()) return;
496
+ onEvent?.(e);
497
+ if (isCurrent()) chan.buffer.push(e);
498
+ },
499
+ onError: fail,
500
+ onComplete: complete
501
+ });
502
+ complete();
503
+ } catch (error2) {
504
+ fail(error2 instanceof Error ? error2 : new Error(String(error2)));
505
+ } finally {
506
+ chan.buffer.dispose();
507
+ }
308
508
  if (chan.park) {
309
509
  const { sessionId: sessionId2 } = chan.park;
310
510
  setBackgroundRunning((prev) => prev.filter((s) => s !== sessionId2));
@@ -313,7 +513,7 @@ function useAgnoChat(options) {
313
513
  abortRef.current = null;
314
514
  }
315
515
  },
316
- [applyEvent, patchActive, writeActivity, writeError, writeStatus]
516
+ [applyEvent, onEvent, patchActive, writeActivity, writeError, writeEvents, writeStatus]
317
517
  );
318
518
  const sendMessage = useCallback(
319
519
  async (message, sendOptions) => {
@@ -342,7 +542,6 @@ function useAgnoChat(options) {
342
542
  };
343
543
  activeMsgIdRef.current = agentMsg.id;
344
544
  setEvents([]);
345
- setCurrentEvent(null);
346
545
  setMessages((prev) => [...prev, userMsg, agentMsg]);
347
546
  await drive(
348
547
  (cb) => client.run({
@@ -356,7 +555,7 @@ function useAgnoChat(options) {
356
555
  })
357
556
  );
358
557
  },
359
- [client, drive, userId]
558
+ [client, drive, setEvents, setMessages, userId]
360
559
  );
361
560
  const continueRun = useCallback(
362
561
  async (resolution) => {
@@ -366,7 +565,7 @@ function useAgnoChat(options) {
366
565
  setError("No paused run to continue.");
367
566
  return;
368
567
  }
369
- patchActive(null, (m) => ({ ...m, streaming: true, status: "streaming", requirements: void 0 }));
568
+ patchActive(null, (m) => ({ ...m, streaming: true, status: "streaming", requirements: void 0, step_requirements: void 0 }));
370
569
  await drive(
371
570
  (cb) => client.continueRun({
372
571
  type: ent.type,
@@ -375,6 +574,7 @@ function useAgnoChat(options) {
375
574
  sessionId: sessionIdRef.current,
376
575
  userId,
377
576
  tools: resolution.tools,
577
+ requirements: resolution.requirements,
378
578
  stepRequirements: resolution.stepRequirements,
379
579
  ...cb
380
580
  })
@@ -393,8 +593,12 @@ function useAgnoChat(options) {
393
593
  const respondToConfirmation = useCallback(
394
594
  async (approve) => {
395
595
  const ent = entityRef.current;
596
+ if (ent?.type === "team" && activeMessage?.requirements?.length) {
597
+ await continueRun({ requirements: activeMessage.requirements.map((r) => ({ ...r, confirmation: approve })) });
598
+ return;
599
+ }
396
600
  if (ent?.type === "workflow") {
397
- const reqs = (activeMessage?.requirements ?? []).map((r) => ({ ...r, confirmation: approve }));
601
+ const reqs = activeMessage?.step_requirements?.length ? activeMessage.step_requirements.map((r) => ({ ...r, confirmed: approve })) : (activeMessage?.requirements ?? []).map((r) => ({ ...r, confirmation: approve }));
398
602
  await continueRun({ stepRequirements: reqs });
399
603
  return;
400
604
  }
@@ -406,6 +610,17 @@ function useAgnoChat(options) {
406
610
  const submitUserInput = useCallback(
407
611
  async (values) => {
408
612
  const ent = entityRef.current;
613
+ if (ent?.type === "team" && activeMessage?.requirements?.length) {
614
+ const requirements = activeMessage.requirements.map((r) => ({
615
+ ...r,
616
+ user_input_schema: (r.user_input_schema ?? r.tool_execution?.user_input_schema ?? []).map((f) => ({
617
+ ...f,
618
+ value: f.name in values ? values[f.name] : f.value
619
+ }))
620
+ }));
621
+ await continueRun({ requirements });
622
+ return;
623
+ }
409
624
  if (ent?.type === "workflow") {
410
625
  const reqs = (activeMessage?.requirements ?? []).map((r) => ({
411
626
  ...r,
@@ -430,15 +645,16 @@ function useAgnoChat(options) {
430
645
  [activeMessage, continueRun, pausedTools]
431
646
  );
432
647
  const cancel = useCallback(async () => {
648
+ channelRef.current?.buffer.flush();
433
649
  abortRef.current?.abort();
434
650
  channelRef.current = null;
435
651
  const ent = entityRef.current;
436
652
  const runId = runIdRef.current;
437
- setStatus("cancelled");
438
- setActivity(null);
653
+ writeStatus(null, "cancelled");
654
+ writeActivity(null, null);
439
655
  patchActive(null, (m) => m.streaming ? { ...m, streaming: false, status: "cancelled" } : m);
440
656
  if (ent && runId) await client.cancelRun(ent.type, ent.id, runId, sessionIdRef.current);
441
- }, [client, patchActive]);
657
+ }, [client, patchActive, writeActivity, writeStatus]);
442
658
  const refreshSessions = useCallback(async () => {
443
659
  const ent = entityRef.current;
444
660
  if (!ent) {
@@ -458,6 +674,14 @@ function useAgnoChat(options) {
458
674
  setSessionsLoading(false);
459
675
  }
460
676
  }, [client]);
677
+ const clearRunView = useCallback(() => {
678
+ activeMsgIdRef.current = null;
679
+ runIdRef.current = void 0;
680
+ setEvents([]);
681
+ setStatus("idle");
682
+ setActivity(null);
683
+ setError(null);
684
+ }, [setEvents, setStatus, setActivity, setError]);
461
685
  const loadSession = useCallback(
462
686
  async (id) => {
463
687
  const ent = entityRef.current;
@@ -468,13 +692,7 @@ function useAgnoChat(options) {
468
692
  setSessionId(id);
469
693
  onSessionId?.(id);
470
694
  if (resumeRun(id)) return;
471
- activeMsgIdRef.current = null;
472
- runIdRef.current = void 0;
473
- setEvents([]);
474
- setCurrentEvent(null);
475
- setStatus("idle");
476
- setActivity(null);
477
- setError(null);
695
+ clearRunView();
478
696
  try {
479
697
  const runs = await client.getSessionRuns(ent.type, id, ent.db_id);
480
698
  setMessages(sessionRunsToMessages(runs));
@@ -482,7 +700,7 @@ function useAgnoChat(options) {
482
700
  setError(err instanceof Error ? err.message : String(err));
483
701
  }
484
702
  },
485
- [client, onSessionId, releaseCurrentRun, resumeRun]
703
+ [client, clearRunView, onSessionId, releaseCurrentRun, resumeRun, setMessages]
486
704
  );
487
705
  const deleteSession = useCallback(
488
706
  async (id) => {
@@ -497,29 +715,27 @@ function useAgnoChat(options) {
497
715
  }
498
716
  setSessions((prev) => prev.filter((s) => s.session_id !== id));
499
717
  if (sessionIdRef.current === id) {
718
+ channelRef.current?.controller.abort();
719
+ channelRef.current = null;
720
+ abortRef.current = null;
721
+ clearRunView();
500
722
  sessionIdRef.current = void 0;
501
723
  setSessionId(void 0);
502
724
  setMessages([]);
503
725
  }
504
726
  }
505
727
  },
506
- [client]
728
+ [client, clearRunView, setMessages]
507
729
  );
508
730
  const reset = useCallback(() => {
509
731
  releaseCurrentRun();
510
- activeMsgIdRef.current = null;
511
- runIdRef.current = void 0;
732
+ clearRunView();
512
733
  sessionIdRef.current = void 0;
513
734
  pendingSessionNameRef.current = void 0;
514
735
  bumpedSessionRef.current = void 0;
515
736
  setMessages([]);
516
- setEvents([]);
517
- setCurrentEvent(null);
518
- setStatus("idle");
519
- setActivity(null);
520
- setError(null);
521
737
  setSessionId(void 0);
522
- }, [releaseCurrentRun]);
738
+ }, [clearRunView, releaseCurrentRun, setMessages]);
523
739
  useEffect(() => {
524
740
  const background = backgroundRef.current;
525
741
  return () => {
@@ -643,7 +859,7 @@ var FileVideo = icon(FileVideo$1);
643
859
  var Wrench = icon(Wrench$1);
644
860
  var BookOpen = icon(BookOpen$1);
645
861
  var Globe = icon(Globe$1);
646
- var LinkIcon = icon(Link);
862
+ var LinkIcon = icon(Link$1);
647
863
  var Brain = icon(Brain$1);
648
864
  var Box = icon(Box$1);
649
865
  var Plus = icon(Plus$1);
@@ -1220,6 +1436,16 @@ function Followups({
1220
1436
  }
1221
1437
  var isBoolField = (f) => f.field_type === "bool" || f.field_type === "boolean";
1222
1438
  function buildAsks(message, entityType) {
1439
+ if (entityType === "workflow" && message.step_requirements?.length) {
1440
+ return message.step_requirements.map((r) => ({
1441
+ id: r.step_id,
1442
+ name: r.step_name,
1443
+ needsConfirmation: Boolean(r.requires_confirmation) && r.confirmed == null,
1444
+ fields: [],
1445
+ stepRequirement: r,
1446
+ unsupported: !r.requires_confirmation || Boolean(r.requires_user_input || r.requires_route_selection || r.requires_output_review)
1447
+ }));
1448
+ }
1223
1449
  const fromTools = (message.tool_calls ?? []).filter((t) => t.requires_confirmation || t.requires_user_input).map((t) => ({
1224
1450
  id: t.tool_call_id ?? `${t.tool_name}`,
1225
1451
  name: t.tool_name,
@@ -1232,11 +1458,11 @@ function buildAsks(message, entityType) {
1232
1458
  id: r.id,
1233
1459
  name: r.tool_execution?.tool_name,
1234
1460
  args: r.tool_execution?.tool_args,
1235
- needsConfirmation: Boolean(r.tool_execution?.requires_confirmation) || r.confirmation == null,
1461
+ needsConfirmation: entityType === "workflow" ? Boolean(r.tool_execution?.requires_confirmation) || r.confirmation == null : Boolean(r.tool_execution?.requires_confirmation) && r.confirmation == null,
1236
1462
  fields: r.user_input_schema ?? r.tool_execution?.user_input_schema ?? [],
1237
1463
  requirement: r
1238
1464
  }));
1239
- if (entityType === "workflow") return fromReqs.length ? fromReqs : fromTools;
1465
+ if (entityType === "workflow" || entityType === "team") return fromReqs.length ? fromReqs : fromTools;
1240
1466
  return fromTools.length ? fromTools : fromReqs;
1241
1467
  }
1242
1468
  function HumanInput({
@@ -1255,11 +1481,26 @@ function HumanInput({
1255
1481
  const setValue = (id, name, value) => setValues((v) => ({ ...v, [id]: { ...v[id] ?? {}, [name]: value } }));
1256
1482
  const valueFor = (ask, field) => values[ask.id]?.[field.name] ?? field.value ?? (isBoolField(field) ? false : "");
1257
1483
  const fillFields = (ask) => ask.fields.map((f) => ({ ...f, value: valueFor(ask, f) }));
1258
- const ready = asks.every((a) => !a.needsConfirmation || choices[a.id]);
1484
+ const ready = asks.every((a) => !a.unsupported && (!a.needsConfirmation || choices[a.id]));
1259
1485
  const submit = () => {
1486
+ if (entityType === "team" && asks.every((a) => a.requirement)) {
1487
+ const requirements = asks.map((a) => ({
1488
+ ...a.requirement,
1489
+ ...a.needsConfirmation ? { confirmation: choices[a.id] === "confirm" } : {},
1490
+ ...a.fields.length ? { user_input_schema: fillFields(a) } : {},
1491
+ ...reasons[a.id] ? { confirmation_note: reasons[a.id] } : {}
1492
+ }));
1493
+ onResolve({ requirements });
1494
+ return;
1495
+ }
1260
1496
  if (entityType === "workflow") {
1261
1497
  const stepRequirements = asks.map((a) => {
1262
1498
  const approved = a.needsConfirmation ? choices[a.id] === "confirm" : true;
1499
+ if (a.stepRequirement) return {
1500
+ ...a.stepRequirement,
1501
+ confirmed: approved,
1502
+ ...reasons[a.id] ? { rejection_feedback: reasons[a.id] } : {}
1503
+ };
1263
1504
  return {
1264
1505
  id: a.id,
1265
1506
  confirmation: approved,
@@ -1272,7 +1513,7 @@ function HumanInput({
1272
1513
  return;
1273
1514
  }
1274
1515
  const tools = asks.map((a) => {
1275
- const base = a.tool ?? { tool_call_id: a.id, tool_name: a.name };
1516
+ const base = a.tool ?? a.requirement?.tool_execution ?? { tool_call_id: a.id, tool_name: a.name };
1276
1517
  const approved = a.needsConfirmation ? choices[a.id] === "confirm" : true;
1277
1518
  return {
1278
1519
  ...base,
@@ -1285,6 +1526,7 @@ function HumanInput({
1285
1526
  };
1286
1527
  return /* @__PURE__ */ jsxs("div", { className: cx("agno-hitl", className), children: [
1287
1528
  /* @__PURE__ */ jsx("div", { className: "agno-hitl__title", children: "Your input is needed" }),
1529
+ asks.some((a) => a.unsupported) && /* @__PURE__ */ jsx("div", { className: "agno-hitl__notice", role: "status", children: "This workflow needs an input type this chat does not support yet." }),
1288
1530
  asks.map((ask) => {
1289
1531
  const choice = choices[ask.id];
1290
1532
  const hasArgs = ask.args && Object.keys(ask.args).length > 0;
@@ -1393,11 +1635,14 @@ function sourceHost(url) {
1393
1635
  }
1394
1636
  }
1395
1637
  function faviconUrl(url) {
1396
- if (!url) return void 0;
1638
+ return faviconFallbacks(url)[0];
1639
+ }
1640
+ function faviconFallbacks(url) {
1641
+ if (!url) return [];
1397
1642
  try {
1398
- return new URL("/favicon.ico", url).href;
1643
+ return ["/favicon.ico", "/favicon.svg"].map((path) => new URL(path, url).href);
1399
1644
  } catch {
1400
- return void 0;
1645
+ return [];
1401
1646
  }
1402
1647
  }
1403
1648
  function displayUrl(url) {
@@ -1526,73 +1771,6 @@ var SourcesProvider = SourcesContext.Provider;
1526
1771
  function useSources() {
1527
1772
  return useContext(SourcesContext);
1528
1773
  }
1529
- var THEMES = { light: "github-light", dark: "monokai" };
1530
- var highlighter;
1531
- var booting;
1532
- var loading = /* @__PURE__ */ new Map();
1533
- var loaded = /* @__PURE__ */ new Set();
1534
- var unsupported = /* @__PURE__ */ new Set();
1535
- async function boot() {
1536
- if (highlighter) return highlighter;
1537
- booting ?? (booting = (async () => {
1538
- const [{ createHighlighterCore }, { createJavaScriptRegexEngine }, { bundledThemes }] = await Promise.all([import('shiki/core'), import('shiki/engine/javascript'), import('shiki/themes')]);
1539
- highlighter = await createHighlighterCore({
1540
- themes: [bundledThemes[THEMES.light], bundledThemes[THEMES.dark]],
1541
- langs: [],
1542
- // No WASM: the JS engine keeps this a plain import. `forgiving` skips
1543
- // the odd grammar rule it can't translate rather than failing the block.
1544
- engine: createJavaScriptRegexEngine({ forgiving: true })
1545
- });
1546
- return highlighter;
1547
- })());
1548
- return booting;
1549
- }
1550
- function ensureLanguage(lang) {
1551
- let pending = loading.get(lang);
1552
- if (!pending) {
1553
- pending = (async () => {
1554
- const [core, { bundledLanguages }] = await Promise.all([boot(), import('shiki/langs')]);
1555
- const grammar = bundledLanguages[lang];
1556
- if (!grammar) {
1557
- unsupported.add(lang);
1558
- return;
1559
- }
1560
- await core.loadLanguage(grammar);
1561
- loaded.add(lang);
1562
- })().catch(() => {
1563
- unsupported.add(lang);
1564
- });
1565
- loading.set(lang, pending);
1566
- }
1567
- return pending;
1568
- }
1569
- function normalizeLanguage(language) {
1570
- const lang = language?.trim().toLowerCase();
1571
- return lang || void 0;
1572
- }
1573
- function useHighlight(code, language) {
1574
- const [, rerender] = useReducer((n) => n + 1, 0);
1575
- const lang = normalizeLanguage(language);
1576
- const ready = Boolean(lang && loaded.has(lang));
1577
- useEffect(() => {
1578
- if (!lang || ready || unsupported.has(lang)) return;
1579
- let cancelled = false;
1580
- void ensureLanguage(lang).then(() => {
1581
- if (!cancelled) rerender();
1582
- });
1583
- return () => {
1584
- cancelled = true;
1585
- };
1586
- }, [lang, ready]);
1587
- return useMemo(() => {
1588
- if (!lang || !ready || !highlighter) return null;
1589
- try {
1590
- return highlighter.codeToTokens(code, { lang, themes: THEMES, defaultColor: false }).tokens;
1591
- } catch {
1592
- return null;
1593
- }
1594
- }, [code, lang, ready]);
1595
- }
1596
1774
  var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
1597
1775
  var caches = /* @__PURE__ */ new WeakMap();
1598
1776
  var inflight = /* @__PURE__ */ new WeakMap();
@@ -1649,10 +1827,11 @@ function Favicon({
1649
1827
  size = 14,
1650
1828
  className
1651
1829
  }) {
1652
- const src = faviconUrl(url);
1653
- const [failed, setFailed] = useState(false);
1654
- useEffect(() => setFailed(false), [src]);
1655
- if (kind === "document" || !src || failed) {
1830
+ const candidates = useMemo(() => faviconFallbacks(url), [url]);
1831
+ const [attempt, setAttempt] = useState(0);
1832
+ const src = candidates[attempt];
1833
+ useEffect(() => setAttempt(0), [url]);
1834
+ if (kind === "document" || !src) {
1656
1835
  const Glyph2 = kind === "document" ? BookOpen : Globe;
1657
1836
  return /* @__PURE__ */ jsx("span", { className: cx("agno-favicon", "agno-favicon--glyph", className), "aria-hidden": true, children: /* @__PURE__ */ jsx(Glyph2, { size }) });
1658
1837
  }
@@ -1666,7 +1845,7 @@ function Favicon({
1666
1845
  height: size,
1667
1846
  loading: "lazy",
1668
1847
  decoding: "async",
1669
- onError: () => setFailed(true),
1848
+ onError: () => setAttempt((n) => n + 1),
1670
1849
  "aria-hidden": true
1671
1850
  }
1672
1851
  );
@@ -1797,36 +1976,24 @@ function HoverPreview({
1797
1976
  }
1798
1977
  );
1799
1978
  }
1800
- function el(tag, className) {
1801
- return function Element({ node: _node, className: _theirs, ...rest }) {
1802
- return React7.createElement(tag, { className, ...rest });
1803
- };
1804
- }
1805
1979
  function sourceForUrl(url, sources) {
1806
- return url ? sources.find((s) => s.url === url) : void 0;
1980
+ return url ? sources.find((source) => source.url === url) : void 0;
1807
1981
  }
1808
1982
  function citationForLink(label2, href, ctx) {
1809
1983
  if (!/^\d+$/.test(label2.trim())) return void 0;
1810
- return sourceForUrl(href, ctx.sources) ?? ctx.sources.find((s) => s.index === Number(label2.trim()));
1811
- }
1812
- function labelOf(children) {
1813
- if (typeof children === "string") return children;
1814
- if (Array.isArray(children) && children.length === 1 && typeof children[0] === "string") {
1815
- return children[0];
1816
- }
1817
- return "";
1984
+ return sourceForUrl(href, ctx.sources) ?? ctx.sources.find((source) => source.index === Number(label2.trim()));
1818
1985
  }
1819
1986
  function CitationMarker({
1820
1987
  source,
1821
1988
  className,
1822
- Link
1989
+ Link: Link2
1823
1990
  }) {
1824
1991
  const props = {
1825
1992
  className: cx("agno-cite", className),
1826
1993
  href: source.url ?? `#${source.anchorId}`,
1827
1994
  "aria-label": `Source ${source.index}: ${source.title}`
1828
1995
  };
1829
- return /* @__PURE__ */ jsx(HoverPreview, { source, className: "agno-cite__wrap", children: Link && source.url ? /* @__PURE__ */ jsx(Link, { ...props, children: source.index }) : /* @__PURE__ */ jsx(
1996
+ return /* @__PURE__ */ jsx(HoverPreview, { source, className: "agno-cite__wrap", children: Link2 && source.url ? /* @__PURE__ */ jsx(Link2, { ...props, children: source.index }) : /* @__PURE__ */ jsx(
1830
1997
  "a",
1831
1998
  {
1832
1999
  ...props,
@@ -1836,6 +2003,73 @@ function CitationMarker({
1836
2003
  }
1837
2004
  ) });
1838
2005
  }
2006
+ var THEMES = { light: "github-light", dark: "monokai" };
2007
+ var highlighter;
2008
+ var booting;
2009
+ var loading = /* @__PURE__ */ new Map();
2010
+ var loaded = /* @__PURE__ */ new Set();
2011
+ var unsupported = /* @__PURE__ */ new Set();
2012
+ async function boot() {
2013
+ if (highlighter) return highlighter;
2014
+ booting ?? (booting = (async () => {
2015
+ const [{ createHighlighterCore }, { createJavaScriptRegexEngine }, { bundledThemes }] = await Promise.all([import('shiki/core'), import('shiki/engine/javascript'), import('shiki/themes')]);
2016
+ highlighter = await createHighlighterCore({
2017
+ themes: [bundledThemes[THEMES.light], bundledThemes[THEMES.dark]],
2018
+ langs: [],
2019
+ // No WASM: the JS engine keeps this a plain import. `forgiving` skips
2020
+ // the odd grammar rule it can't translate rather than failing the block.
2021
+ engine: createJavaScriptRegexEngine({ forgiving: true })
2022
+ });
2023
+ return highlighter;
2024
+ })());
2025
+ return booting;
2026
+ }
2027
+ function ensureLanguage(lang) {
2028
+ let pending = loading.get(lang);
2029
+ if (!pending) {
2030
+ pending = (async () => {
2031
+ const [core, { bundledLanguages }] = await Promise.all([boot(), import('shiki/langs')]);
2032
+ const grammar = bundledLanguages[lang];
2033
+ if (!grammar) {
2034
+ unsupported.add(lang);
2035
+ return;
2036
+ }
2037
+ await core.loadLanguage(grammar);
2038
+ loaded.add(lang);
2039
+ })().catch(() => {
2040
+ unsupported.add(lang);
2041
+ });
2042
+ loading.set(lang, pending);
2043
+ }
2044
+ return pending;
2045
+ }
2046
+ function normalizeLanguage(language) {
2047
+ const lang = language?.trim().toLowerCase();
2048
+ return lang || void 0;
2049
+ }
2050
+ function useHighlight(code, language) {
2051
+ const [, rerender] = useReducer((n) => n + 1, 0);
2052
+ const lang = normalizeLanguage(language);
2053
+ const ready = Boolean(lang && loaded.has(lang));
2054
+ useEffect(() => {
2055
+ if (!lang || ready || unsupported.has(lang)) return;
2056
+ let cancelled = false;
2057
+ void ensureLanguage(lang).then(() => {
2058
+ if (!cancelled) rerender();
2059
+ });
2060
+ return () => {
2061
+ cancelled = true;
2062
+ };
2063
+ }, [lang, ready]);
2064
+ return useMemo(() => {
2065
+ if (!lang || !ready || !highlighter) return null;
2066
+ try {
2067
+ return highlighter.codeToTokens(code, { lang, themes: THEMES, defaultColor: false }).tokens;
2068
+ } catch {
2069
+ return null;
2070
+ }
2071
+ }, [code, lang, ready]);
2072
+ }
1839
2073
  var COPIED_FOR = 1600;
1840
2074
  var CLIPBOARD_TIMEOUT = 400;
1841
2075
  async function writeClipboard(text) {
@@ -1861,7 +2095,7 @@ async function writeClipboard(text) {
1861
2095
  return false;
1862
2096
  }
1863
2097
  }
1864
- function CopyButton({ text, className }) {
2098
+ function CopyButton({ text, label: label2 = "Copy", size = 14, className }) {
1865
2099
  const [copied, setCopied] = useState(false);
1866
2100
  useEffect(() => {
1867
2101
  if (!copied) return;
@@ -1869,17 +2103,18 @@ function CopyButton({ text, className }) {
1869
2103
  return () => clearTimeout(timer);
1870
2104
  }, [copied]);
1871
2105
  const copy = async () => {
1872
- if (await writeClipboard(text())) setCopied(true);
2106
+ if (await writeClipboard(typeof text === "function" ? text() : text)) setCopied(true);
1873
2107
  };
2108
+ const name = copied ? "Copied" : label2;
1874
2109
  return /* @__PURE__ */ jsx(
1875
2110
  "button",
1876
2111
  {
1877
2112
  type: "button",
1878
- className: cx("agno-md-copy", copied && "is-copied", className),
2113
+ className: cx("agno-copy", copied && "is-copied", className),
1879
2114
  onClick: copy,
1880
- "aria-label": copied ? "Copied" : "Copy code",
1881
- title: copied ? "Copied" : "Copy code",
1882
- children: copied ? /* @__PURE__ */ jsx(Check, { size: 13 }) : /* @__PURE__ */ jsx(Copy, { size: 13 })
2115
+ "aria-label": name,
2116
+ title: name,
2117
+ children: copied ? /* @__PURE__ */ jsx(Check, { size }) : /* @__PURE__ */ jsx(Copy, { size })
1883
2118
  }
1884
2119
  );
1885
2120
  }
@@ -1887,15 +2122,20 @@ function textOf(children) {
1887
2122
  if (typeof children === "string") return children;
1888
2123
  if (typeof children === "number") return String(children);
1889
2124
  if (Array.isArray(children)) return children.map(textOf).join("");
1890
- if (React7.isValidElement(children)) return textOf(children.props.children);
2125
+ if (React8.isValidElement(children))
2126
+ return textOf(children.props.children);
1891
2127
  return "";
1892
2128
  }
1893
2129
  function fencedCode(children) {
1894
2130
  const child = Array.isArray(children) ? children[0] : children;
1895
- if (!React7.isValidElement(child)) {
2131
+ if (!React8.isValidElement(
2132
+ child
2133
+ )) {
1896
2134
  return { code: textOf(children) };
1897
2135
  }
1898
- const language = /(?:^|\s)language-([^\s]+)/.exec(child.props.className ?? "")?.[1];
2136
+ const language = /(?:^|\s)language-([^\s]+)/.exec(
2137
+ child.props.className ?? ""
2138
+ )?.[1];
1899
2139
  return { language, code: textOf(child.props.children).replace(/\n$/, "") };
1900
2140
  }
1901
2141
  function plainLines(code) {
@@ -1904,93 +2144,159 @@ function plainLines(code) {
1904
2144
  function CodeBlock({
1905
2145
  children,
1906
2146
  copyClass,
1907
- codeCopy
2147
+ codeCopy,
2148
+ streaming
1908
2149
  }) {
1909
2150
  const { language, code } = useMemo(() => fencedCode(children), [children]);
1910
- const highlighted = useHighlight(code, language);
1911
- const lines = highlighted ?? plainLines(code);
2151
+ const highlighted = useHighlight(streaming ? "" : code, language);
2152
+ const lines = streaming ? plainLines(code) : highlighted ?? plainLines(code);
1912
2153
  const label2 = normalizeLanguage(language) ?? "text";
1913
2154
  return /* @__PURE__ */ jsxs("div", { className: "agno-md-code-block", "data-language": label2, children: [
1914
2155
  /* @__PURE__ */ jsxs("div", { className: "agno-md-code-head", children: [
1915
2156
  /* @__PURE__ */ jsx("span", { className: "agno-md-code-lang", children: label2 }),
1916
- codeCopy && /* @__PURE__ */ jsx(CopyButton, { className: copyClass, text: () => code })
2157
+ codeCopy && /* @__PURE__ */ jsx(
2158
+ CopyButton,
2159
+ {
2160
+ className: cx("agno-md-copy", copyClass),
2161
+ text: () => code,
2162
+ label: "Copy code",
2163
+ size: 13
2164
+ }
2165
+ )
1917
2166
  ] }),
1918
- /* @__PURE__ */ jsx("pre", { className: "agno-md-pre", style: { "--agno-code-gutter": `${String(lines.length).length}ch` }, children: /* @__PURE__ */ jsx("code", { className: language ? `language-${language}` : void 0, children: lines.map((tokens, i) => /* @__PURE__ */ jsxs(React7.Fragment, { children: [
1919
- /* @__PURE__ */ jsx("span", { className: "agno-md-line", children: tokens.map((token, j) => /* @__PURE__ */ jsx("span", { style: token.htmlStyle, children: token.content }, j)) }),
1920
- i < lines.length - 1 && "\n"
1921
- ] }, i)) }) })
2167
+ /* @__PURE__ */ jsx(
2168
+ "pre",
2169
+ {
2170
+ className: "agno-md-pre",
2171
+ style: {
2172
+ "--agno-code-gutter": `${String(lines.length).length}ch`
2173
+ },
2174
+ children: /* @__PURE__ */ jsx("code", { className: language ? `language-${language}` : void 0, children: lines.map((tokens, i) => /* @__PURE__ */ jsxs(React8.Fragment, { children: [
2175
+ /* @__PURE__ */ jsx("span", { className: "agno-md-line", children: tokens.map((token, j) => /* @__PURE__ */ jsx(
2176
+ "span",
2177
+ {
2178
+ style: token.htmlStyle,
2179
+ children: token.content
2180
+ },
2181
+ j
2182
+ )) }),
2183
+ i < lines.length - 1 && "\n"
2184
+ ] }, i)) })
2185
+ }
2186
+ )
1922
2187
  ] });
1923
2188
  }
1924
- function elementsFor(ctx) {
1925
- const Link = function Link2({
2189
+ var InlineRenderContext = createContext({
2190
+ sources: [],
2191
+ canResolve: false,
2192
+ codeCopy: true
2193
+ });
2194
+ function el(tag, className) {
2195
+ return function Element({
1926
2196
  node: _node,
1927
2197
  className: _theirs,
1928
- href,
1929
- children,
1930
2198
  ...rest
1931
2199
  }) {
1932
- const cited = citationForLink(labelOf(children), href, ctx);
1933
- if (cited) return /* @__PURE__ */ jsx(CitationMarker, { source: cited, className: ctx.markerClass, Link: ctx.Link });
1934
- const link = ctx.Link && href ? /* @__PURE__ */ jsx(ctx.Link, { href, ...rest, children }) : /* @__PURE__ */ jsx("a", { href, target: "_blank", rel: "noreferrer noopener", ...rest, children });
1935
- const source = sourceForUrl(href, ctx.sources);
1936
- if (!href || !/^https?:\/\//i.test(href) || !source && !ctx.canResolve) return link;
2200
+ return React8.createElement(tag, { className, ...rest });
2201
+ };
2202
+ }
2203
+ function Link({
2204
+ node: _node,
2205
+ className: _theirs,
2206
+ href,
2207
+ children,
2208
+ ...rest
2209
+ }) {
2210
+ const ctx = useContext(InlineRenderContext);
2211
+ const cited = citationForLink(textOf(children), href, ctx);
2212
+ if (cited)
1937
2213
  return /* @__PURE__ */ jsx(
1938
- HoverPreview,
2214
+ CitationMarker,
1939
2215
  {
1940
- source: source ?? {
1941
- index: 0,
1942
- anchorId: "",
1943
- kind: "url",
1944
- url: href,
1945
- title: labelOf(children) || sourceHost(href) || href
1946
- },
1947
- children: link
2216
+ source: cited,
2217
+ className: ctx.markerClass,
2218
+ Link: ctx.Link
1948
2219
  }
1949
2220
  );
1950
- };
1951
- const Code = function Code2({ node: _node, className, ...rest }) {
1952
- const fenced = typeof className === "string" && className.includes("language-");
1953
- return /* @__PURE__ */ jsx("code", { className: fenced ? className : "agno-md-code", ...rest });
1954
- };
1955
- return {
1956
- a: Link,
1957
- code: Code,
1958
- p: el("p", "agno-md-p"),
1959
- // Streamdown renders `**bold**` as a Tailwind-classed <span>, which is not
1960
- // bold anywhere Tailwind isn't. These stay real elements, styled by the
1961
- // browser and read correctly by a screen reader.
1962
- strong: el("strong"),
1963
- em: el("em"),
1964
- del: el("del"),
1965
- sub: el("sub"),
1966
- sup: el("sup"),
1967
- h1: el("h2", "agno-md-heading"),
1968
- h2: el("h3", "agno-md-heading"),
1969
- h3: el("h4", "agno-md-heading"),
1970
- h4: el("h5", "agno-md-heading"),
1971
- h5: el("h6", "agno-md-heading"),
1972
- h6: el("h6", "agno-md-heading"),
1973
- ul: el("ul", "agno-md-list"),
1974
- ol: el("ol", "agno-md-list"),
1975
- li: el("li"),
1976
- pre: function Pre({ children }) {
1977
- return /* @__PURE__ */ jsx(CodeBlock, { copyClass: ctx.copyClass, codeCopy: ctx.codeCopy, children });
1978
- },
1979
- blockquote: el("blockquote", "agno-md-quote"),
1980
- hr: el("hr", "agno-md-rule"),
1981
- table: function Table({ node: _node, className: _theirs, ...rest }) {
1982
- return /* @__PURE__ */ jsx("div", { className: "agno-md-table-wrap", children: /* @__PURE__ */ jsx("table", { className: "agno-md-table", ...rest }) });
1983
- },
1984
- thead: el("thead"),
1985
- tbody: el("tbody"),
1986
- tr: el("tr"),
1987
- th: el("th"),
1988
- td: el("td"),
1989
- img: function Image({ node: _node, className: _theirs, ...rest }) {
1990
- return /* @__PURE__ */ jsx("img", { className: "agno-md-img", ...rest });
2221
+ const link = ctx.Link && href ? /* @__PURE__ */ jsx(ctx.Link, { href, ...rest, children }) : /* @__PURE__ */ jsx("a", { href, target: "_blank", rel: "noreferrer noopener", ...rest, children });
2222
+ const source = sourceForUrl(href, ctx.sources);
2223
+ if (!href || !/^https?:\/\//i.test(href) || !source && !ctx.canResolve)
2224
+ return link;
2225
+ return /* @__PURE__ */ jsx(
2226
+ HoverPreview,
2227
+ {
2228
+ source: source ?? {
2229
+ index: 0,
2230
+ anchorId: "",
2231
+ kind: "url",
2232
+ url: href,
2233
+ title: textOf(children) || sourceHost(href) || href
2234
+ },
2235
+ children: link
1991
2236
  }
1992
- };
2237
+ );
2238
+ }
2239
+ function Code({ node: _node, className, ...rest }) {
2240
+ const fenced = typeof className === "string" && className.includes("language-");
2241
+ return /* @__PURE__ */ jsx("code", { className: fenced ? className : "agno-md-code", ...rest });
1993
2242
  }
2243
+ function Pre({ children }) {
2244
+ const ctx = useContext(InlineRenderContext);
2245
+ return /* @__PURE__ */ jsx(
2246
+ CodeBlock,
2247
+ {
2248
+ copyClass: ctx.copyClass,
2249
+ codeCopy: ctx.codeCopy,
2250
+ streaming: ctx.streaming,
2251
+ children
2252
+ }
2253
+ );
2254
+ }
2255
+ function Table({ node: _node, className: _theirs, ...rest }) {
2256
+ return /* @__PURE__ */ jsx("div", { className: "agno-md-table-wrap", children: /* @__PURE__ */ jsx("table", { className: "agno-md-table", ...rest }) });
2257
+ }
2258
+ function Image({ node: _node, className: _theirs, ...rest }) {
2259
+ return /* @__PURE__ */ jsx("img", { className: "agno-md-img", ...rest });
2260
+ }
2261
+ var MARKDOWN_COMPONENTS = {
2262
+ a: Link,
2263
+ code: Code,
2264
+ p: el("p", "agno-md-p"),
2265
+ // Streamdown renders `**bold**` as a Tailwind-classed <span>, which is not
2266
+ // bold anywhere Tailwind isn't. These stay real elements, styled by the
2267
+ // browser and read correctly by a screen reader.
2268
+ strong: el("strong"),
2269
+ em: el("em"),
2270
+ del: el("del"),
2271
+ sub: el("sub"),
2272
+ sup: el("sup"),
2273
+ h1: el("h2", "agno-md-heading"),
2274
+ h2: el("h3", "agno-md-heading"),
2275
+ h3: el("h4", "agno-md-heading"),
2276
+ h4: el("h5", "agno-md-heading"),
2277
+ h5: el("h6", "agno-md-heading"),
2278
+ h6: el("h6", "agno-md-heading"),
2279
+ ul: el("ul", "agno-md-list"),
2280
+ ol: el("ol", "agno-md-list"),
2281
+ li: el("li"),
2282
+ pre: Pre,
2283
+ blockquote: el("blockquote", "agno-md-quote"),
2284
+ hr: el("hr", "agno-md-rule"),
2285
+ table: Table,
2286
+ thead: el("thead"),
2287
+ tbody: el("tbody"),
2288
+ tr: el("tr"),
2289
+ th: el("th"),
2290
+ td: el("td"),
2291
+ img: Image
2292
+ };
2293
+ var STREAMING_MOTION = {
2294
+ animation: "fadeIn",
2295
+ duration: 400,
2296
+ easing: "ease-out",
2297
+ sep: "word",
2298
+ stagger: 0
2299
+ };
1994
2300
  function Markdown({
1995
2301
  content,
1996
2302
  className,
@@ -2007,30 +2313,33 @@ function Markdown({
2007
2313
  const providerLink = useLinkComponent();
2008
2314
  const cited = sources ?? fromMessage;
2009
2315
  const copy = codeCopy ?? fromProvider ?? true;
2010
- const Link = linkComponent ?? providerLink;
2011
- const components = useMemo(
2012
- () => elementsFor({
2316
+ const Link2 = linkComponent ?? providerLink;
2317
+ const inlineContext = useMemo(
2318
+ () => ({
2013
2319
  sources: cited,
2014
2320
  canResolve,
2015
2321
  markerClass: cn.citationMarker,
2016
2322
  copyClass: cn.copyButton,
2017
2323
  codeCopy: copy,
2018
- Link
2324
+ Link: Link2,
2325
+ streaming
2019
2326
  }),
2020
- [cited, canResolve, cn.citationMarker, cn.copyButton, copy, Link]
2327
+ [cited, canResolve, cn.citationMarker, cn.copyButton, copy, Link2, streaming]
2021
2328
  );
2022
2329
  const text = useMemo(() => linkCitations(content, cited), [content, cited]);
2023
- return /* @__PURE__ */ jsx(
2330
+ return /* @__PURE__ */ jsx(InlineRenderContext.Provider, { value: inlineContext, children: /* @__PURE__ */ jsx(
2024
2331
  Streamdown,
2025
2332
  {
2026
2333
  className: cx("agno-md", className),
2027
- components,
2334
+ components: MARKDOWN_COMPONENTS,
2028
2335
  mode: streaming ? "streaming" : "static",
2029
2336
  parseIncompleteMarkdown: streaming !== false,
2337
+ animated: streaming ? STREAMING_MOTION : false,
2338
+ isAnimating: Boolean(streaming),
2030
2339
  ...options,
2031
2340
  children: text
2032
2341
  }
2033
- );
2342
+ ) });
2034
2343
  }
2035
2344
  function statusOf(tool) {
2036
2345
  if (tool.tool_call_error) return { label: "error", cls: "error" };
@@ -2152,7 +2461,7 @@ function stepsFromEvents(message, { hideReasoning, hideTools }) {
2152
2461
  const key = `reasoning:${e.run_id ?? "run"}`;
2153
2462
  const at = indexOf.get(key);
2154
2463
  const previous = at != null ? steps[at].steps : [];
2155
- const incoming = e.reasoning_steps ?? e.extra_data?.reasoning_steps ?? [];
2464
+ const incoming = reasoningStepsFromEvent(e);
2156
2465
  const completed = isReasoningCompletedEvent(e);
2157
2466
  put(key, {
2158
2467
  kind: "reasoning",
@@ -2428,7 +2737,7 @@ function SourceCard({
2428
2737
  className
2429
2738
  }) {
2430
2739
  const cn = useResolvedClassNames();
2431
- const Link = useLinkComponent();
2740
+ const Link2 = useLinkComponent();
2432
2741
  const { preview } = useLinkPreview(source.url, false);
2433
2742
  const body = /* @__PURE__ */ jsxs(Fragment, { children: [
2434
2743
  /* @__PURE__ */ jsx(
@@ -2444,8 +2753,8 @@ function SourceCard({
2444
2753
  source.url && /* @__PURE__ */ jsx(LinkIcon, { className: "agno-source__link" })
2445
2754
  ] });
2446
2755
  const classes = cx("agno-source", `agno-source--${source.kind}`, cn.sourceCard, className);
2447
- if (source.url && Link) {
2448
- return /* @__PURE__ */ jsx(Link, { id: source.anchorId, className: classes, href: source.url, title: source.url, children: body });
2756
+ if (source.url && Link2) {
2757
+ return /* @__PURE__ */ jsx(Link2, { id: source.anchorId, className: classes, href: source.url, title: source.url, children: body });
2449
2758
  }
2450
2759
  return source.url ? /* @__PURE__ */ jsx(
2451
2760
  "a",
@@ -2575,6 +2884,20 @@ function Citations({
2575
2884
  ] })
2576
2885
  ] });
2577
2886
  }
2887
+ function MessageActions({
2888
+ message,
2889
+ hideCopy,
2890
+ children,
2891
+ className,
2892
+ classNames
2893
+ }) {
2894
+ const cn = useResolvedClassNames(classNames);
2895
+ if (hideCopy && !children) return null;
2896
+ return /* @__PURE__ */ jsxs("div", { className: cx("agno-msg__actions", cn.messageActions, className), role: "toolbar", "aria-label": "Message actions", children: [
2897
+ !hideCopy && /* @__PURE__ */ jsx(CopyButton, { className: cx("agno-msg__action", cn.messageCopyButton), text: message.content, label: "Copy message" }),
2898
+ children
2899
+ ] });
2900
+ }
2578
2901
  function audioSrc(a) {
2579
2902
  if (a.url) return a.url;
2580
2903
  if (a.base64_audio) return `data:${a.mime_type ?? "audio/mpeg"};base64,${a.base64_audio}`;
@@ -2615,6 +2938,8 @@ function Message({
2615
2938
  hideReasoning,
2616
2939
  hideTools,
2617
2940
  hideSources,
2941
+ hideActions,
2942
+ actions,
2618
2943
  onFollowup
2619
2944
  }) {
2620
2945
  const cn = useResolvedClassNames(classNames);
@@ -2662,10 +2987,7 @@ function Message({
2662
2987
  classNames: cn
2663
2988
  }
2664
2989
  ),
2665
- content ? /* @__PURE__ */ jsxs("div", { className: cx("agno-msg__content", cn.messageContent), children: [
2666
- renderMarkdown ? renderMarkdown(content) : /* @__PURE__ */ jsx(Markdown, { content, className: cn.markdown, streaming: message.streaming }),
2667
- message.streaming && /* @__PURE__ */ jsx("span", { className: "agno-msg__caret", "aria-hidden": true })
2668
- ] }) : isAgent && message.streaming && hasActivity && /* @__PURE__ */ jsxs("div", { className: "agno-msg__activity", role: "status", "aria-live": "polite", children: [
2990
+ content ? /* @__PURE__ */ jsx("div", { className: cx("agno-msg__content", cn.messageContent), children: renderMarkdown ? renderMarkdown(content) : /* @__PURE__ */ jsx(Markdown, { content, className: cn.markdown, streaming: message.streaming }) }) : isAgent && message.streaming && hasActivity && /* @__PURE__ */ jsxs("div", { className: "agno-msg__activity", role: "status", "aria-live": "polite", children: [
2669
2991
  /* @__PURE__ */ jsx(GridLoader, {}),
2670
2992
  /* @__PURE__ */ jsx("span", { className: "agno-msg__activity-label", children: activity ?? "Working..." })
2671
2993
  ] }),
@@ -2681,7 +3003,8 @@ function Message({
2681
3003
  showSources && /* @__PURE__ */ jsx(Citations, { sources, classNames: cn }),
2682
3004
  isAgent && onFollowup && !message.streaming && /* @__PURE__ */ jsx(Followups, { items: message.followups, onSelect: onFollowup, classNames: cn }),
2683
3005
  message.error && /* @__PURE__ */ jsx("div", { className: "agno-msg__error", children: message.error }),
2684
- children
3006
+ children,
3007
+ isAgent && !hideActions && !message.streaming && message.content && /* @__PURE__ */ jsx(MessageActions, { message, classNames: cn, children: actions })
2685
3008
  ] }) })
2686
3009
  ]
2687
3010
  }
@@ -2741,7 +3064,7 @@ function MessageList({
2741
3064
  items.length === 0 ? /* @__PURE__ */ jsx("div", { className: cx("agno-list__empty", cn.empty), children: emptyState ?? "Start the conversation." }) : /* @__PURE__ */ jsxs("div", { className: "agno-list__inner", children: [
2742
3065
  items.map((m) => {
2743
3066
  const live = m.streaming && runStatus === "streaming" ? runActivity : void 0;
2744
- return renderMessage ? /* @__PURE__ */ jsx(React7.Fragment, { children: renderMessage(m, live) }, m.id) : /* @__PURE__ */ jsx(
3067
+ return renderMessage ? /* @__PURE__ */ jsx(React8.Fragment, { children: renderMessage(m, live) }, m.id) : /* @__PURE__ */ jsx(
2745
3068
  Message,
2746
3069
  {
2747
3070
  message: m,
@@ -3186,7 +3509,7 @@ function AgnoChat(props) {
3186
3509
  };
3187
3510
  }, [client, props.entity, props.entities, props.showEntityPicker]);
3188
3511
  const entity = props.entity ?? selected;
3189
- const chat = useAgnoChat({ client, entity, userId: props.userId });
3512
+ const chat = useAgnoChat({ client, entity, userId: props.userId, streaming: props.streaming });
3190
3513
  const { refreshSessions } = chat;
3191
3514
  useEffect(() => {
3192
3515
  if (props.showSessions && entity) refreshSessions();
@@ -3498,6 +3821,6 @@ function EventLog({
3498
3821
  ] });
3499
3822
  }
3500
3823
 
3501
- export { AgnoChat, AgnoMark, ArrowUp, BehindTheScenes, BookOpen, Box, Brain, ChatBubble, ChatInput, ChatLauncher, ChatProvider, ChatWindow, Check, ChevronDown, Citations, Close, Copy, EntityBadge, EntitySelector, EventLog, Favicon, FileAudio, FileIcon, FilePreview, FileType, FileVideo, Followups, Globe, GridLoader, HoverPreview, HumanInput, LinkIcon, LinkPreviewCard, Markdown, MemberResponses, Memory, Message, MessageList, Multimedia, Paperclip, Plus, Pulse, QuickPrompts, Reasoning, Rerun, SessionList, SourceCard, SourcesProvider, Spinner, StatusIndicator, Stop, TeamGlyph, ToolCalls, Trash, WorkflowSteps, Wrench, admitFiles, behindTheScenesItems, behindTheScenesLabel, collectSources, cx, displayUrl, faviconUrl, fileAccepted, getProviderIcon, hasBehindTheScenes, linkCitations, normalizeFollowups, quickPromptLabel, quickPromptText, sourceHost, sourcesFromMarkdown, useAgnoChat, useChatContext, useLinkComponent, useLinkPreview, useOptionalChatContext, useResolvedChat, useResolvedClassNames, useSources, withoutSourcesLine, writeClipboard };
3502
- //# sourceMappingURL=chunk-XTP3TMEZ.js.map
3503
- //# sourceMappingURL=chunk-XTP3TMEZ.js.map
3824
+ export { AgnoChat, AgnoMark, ArrowUp, BehindTheScenes, BookOpen, Box, Brain, ChatBubble, ChatInput, ChatLauncher, ChatProvider, ChatWindow, Check, ChevronDown, Citations, Close, Copy, CopyButton, DEFAULT_STREAMING_OPTIONS, EntityBadge, EntitySelector, EventLog, Favicon, FileAudio, FileIcon, FilePreview, FileType, FileVideo, Followups, Globe, GridLoader, HoverPreview, HumanInput, LinkIcon, LinkPreviewCard, Markdown, MemberResponses, Memory, Message, MessageActions, MessageList, Multimedia, Paperclip, Plus, Pulse, QuickPrompts, Reasoning, Rerun, SessionList, SourceCard, SourcesProvider, Spinner, StatusIndicator, Stop, TeamGlyph, ToolCalls, Trash, WorkflowSteps, Wrench, admitFiles, behindTheScenesItems, behindTheScenesLabel, collectSources, cx, displayUrl, faviconFallbacks, faviconUrl, fileAccepted, getProviderIcon, hasBehindTheScenes, linkCitations, normalizeFollowups, quickPromptLabel, quickPromptText, sourceHost, sourcesFromMarkdown, useAgnoChat, useChatContext, useLinkComponent, useLinkPreview, useOptionalChatContext, useResolvedChat, useResolvedClassNames, useSources, withoutSourcesLine, writeClipboard };
3825
+ //# sourceMappingURL=chunk-OGDPK4Z3.js.map
3826
+ //# sourceMappingURL=chunk-OGDPK4Z3.js.map