@blade-hq/agent-react 2610.0.0-beta.17 → 2610.0.0-beta.19
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/README.md +9 -1
- package/dist/components/AgentLoopBlock.d.ts +1 -4
- package/dist/components/AssistantTurnBlock.d.ts +12 -4
- package/dist/components/display-utils.d.ts +1 -0
- package/dist/hooks/use-message-pin.d.ts +22 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1015 -260
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/style.full.css +2 -2
- package/package.json +2 -2
- package/public-api.md +24 -1
package/dist/index.js
CHANGED
|
@@ -245,6 +245,8 @@ function useLlmChat(options) {
|
|
|
245
245
|
const [error, setError] = useState3(null);
|
|
246
246
|
const [isStreaming, setIsStreaming] = useState3(false);
|
|
247
247
|
const abortRef = useRef2(null);
|
|
248
|
+
const activeStartedAtRef = useRef2(null);
|
|
249
|
+
const assistantTimingsRef = useRef2(/* @__PURE__ */ new WeakMap());
|
|
248
250
|
const generationRef = useRef2(0);
|
|
249
251
|
const historyRef = useRef2([]);
|
|
250
252
|
const optionsRef = useRef2(options);
|
|
@@ -263,16 +265,31 @@ function useLlmChat(options) {
|
|
|
263
265
|
setFailedToolIds([]);
|
|
264
266
|
setError(null);
|
|
265
267
|
setIsStreaming(false);
|
|
268
|
+
activeStartedAtRef.current = null;
|
|
269
|
+
assistantTimingsRef.current = /* @__PURE__ */ new WeakMap();
|
|
266
270
|
}, [stop]);
|
|
267
271
|
const send = useCallback2(async (text) => {
|
|
268
272
|
const content = text.trim();
|
|
269
273
|
if (!content || abortRef.current) return false;
|
|
270
274
|
const opts = optionsRef.current;
|
|
271
275
|
const maxRounds = opts.maxToolRounds ?? DEFAULT_MAX_TOOL_ROUNDS;
|
|
276
|
+
const startedAt = Date.now();
|
|
277
|
+
activeStartedAtRef.current = startedAt;
|
|
272
278
|
const commit = (message) => {
|
|
273
279
|
historyRef.current = [...historyRef.current, message];
|
|
274
280
|
setHistory(historyRef.current);
|
|
275
281
|
};
|
|
282
|
+
const latestAssistantTiming = { current: null };
|
|
283
|
+
const commitAssistant = (message) => {
|
|
284
|
+
const timing = { startedAt };
|
|
285
|
+
assistantTimingsRef.current.set(message, timing);
|
|
286
|
+
latestAssistantTiming.current = timing;
|
|
287
|
+
commit(message);
|
|
288
|
+
return timing;
|
|
289
|
+
};
|
|
290
|
+
const finishTiming = (timing) => {
|
|
291
|
+
timing.durationMs = Math.max(0, Date.now() - timing.startedAt);
|
|
292
|
+
};
|
|
276
293
|
setError(null);
|
|
277
294
|
setIsStreaming(true);
|
|
278
295
|
setStreamingText("");
|
|
@@ -298,8 +315,11 @@ function useLlmChat(options) {
|
|
|
298
315
|
};
|
|
299
316
|
setStreamingText(null);
|
|
300
317
|
setStreamingCalls([]);
|
|
301
|
-
|
|
302
|
-
if (!result.toolCalls.length)
|
|
318
|
+
const assistantTiming = commitAssistant(assistant);
|
|
319
|
+
if (!result.toolCalls.length) {
|
|
320
|
+
finishTiming(assistantTiming);
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
303
323
|
const bail = (reason) => {
|
|
304
324
|
for (const call of result.toolCalls) {
|
|
305
325
|
commit({ role: "tool", tool_call_id: call.id, content: JSON.stringify({ error: reason }) });
|
|
@@ -308,11 +328,13 @@ function useLlmChat(options) {
|
|
|
308
328
|
};
|
|
309
329
|
if (!opts.onToolCall) {
|
|
310
330
|
bail("\u8FD9\u4E2A\u5E94\u7528\u6CA1\u6709\u63D0\u4F9B\u5DE5\u5177\u6267\u884C\u5165\u53E3");
|
|
331
|
+
finishTiming(assistantTiming);
|
|
311
332
|
return true;
|
|
312
333
|
}
|
|
313
334
|
if (round >= maxRounds) {
|
|
314
335
|
bail(`\u5DE5\u5177\u8C03\u7528\u5DF2\u8FBE\u4E0A\u9650 ${maxRounds} \u8F6E\uFF0C\u6CA1\u6709\u6267\u884C`);
|
|
315
336
|
setError(`\u5DE5\u5177\u8C03\u7528\u8D85\u8FC7 ${maxRounds} \u8F6E\u4ECD\u672A\u7ED9\u51FA\u7ED3\u8BBA\uFF0C\u5DF2\u505C\u4E0B\u3002`);
|
|
337
|
+
finishTiming(assistantTiming);
|
|
316
338
|
return false;
|
|
317
339
|
}
|
|
318
340
|
for (const call of result.toolCalls) {
|
|
@@ -337,21 +359,39 @@ function useLlmChat(options) {
|
|
|
337
359
|
setStreamingCalls([]);
|
|
338
360
|
if (controller.signal.aborted) {
|
|
339
361
|
if (generation !== generationRef.current) return false;
|
|
340
|
-
|
|
362
|
+
const timing = commitAssistant({
|
|
363
|
+
role: "assistant",
|
|
364
|
+
content: partial ? `${partial}\uFF08\u5DF2\u505C\u6B62\uFF09` : "\uFF08\u5DF2\u505C\u6B62\uFF09"
|
|
365
|
+
});
|
|
366
|
+
timing.status = "interrupted";
|
|
367
|
+
finishTiming(timing);
|
|
341
368
|
return false;
|
|
342
369
|
}
|
|
343
370
|
if (partial && generation === generationRef.current) {
|
|
344
|
-
|
|
371
|
+
const timing = commitAssistant({ role: "assistant", content: partial });
|
|
372
|
+
timing.status = "failed";
|
|
373
|
+
finishTiming(timing);
|
|
374
|
+
} else if (latestAssistantTiming.current) {
|
|
375
|
+
latestAssistantTiming.current.status = "failed";
|
|
376
|
+
finishTiming(latestAssistantTiming.current);
|
|
345
377
|
}
|
|
346
378
|
setError(err instanceof Error && err.message ? err.message : "\u5BF9\u8BDD\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
347
379
|
return false;
|
|
348
380
|
} finally {
|
|
349
381
|
if (abortRef.current === controller) abortRef.current = null;
|
|
382
|
+
activeStartedAtRef.current = null;
|
|
350
383
|
setIsStreaming(false);
|
|
351
384
|
}
|
|
352
385
|
}, []);
|
|
353
386
|
const messages = useMemo2(
|
|
354
|
-
() => toChatMessages(
|
|
387
|
+
() => toChatMessages(
|
|
388
|
+
history,
|
|
389
|
+
streamingText,
|
|
390
|
+
streamingCalls,
|
|
391
|
+
failedToolIds,
|
|
392
|
+
assistantTimingsRef.current,
|
|
393
|
+
activeStartedAtRef.current
|
|
394
|
+
),
|
|
355
395
|
[history, streamingText, streamingCalls, failedToolIds]
|
|
356
396
|
);
|
|
357
397
|
return { messages, isStreaming, error, send, stop, reset };
|
|
@@ -503,7 +543,7 @@ function parseSse(raw) {
|
|
|
503
543
|
if (!delta) return null;
|
|
504
544
|
return { text: delta.content, toolCallDeltas: delta.tool_calls };
|
|
505
545
|
}
|
|
506
|
-
function toChatMessages(history, streamingText, streamingCalls, failedToolIds) {
|
|
546
|
+
function toChatMessages(history, streamingText, streamingCalls, failedToolIds, assistantTimings, activeStartedAt) {
|
|
507
547
|
const results = /* @__PURE__ */ new Map();
|
|
508
548
|
for (const msg of history) {
|
|
509
549
|
if (msg.role === "tool") results.set(msg.tool_call_id, msg.content);
|
|
@@ -515,10 +555,13 @@ function toChatMessages(history, streamingText, streamingCalls, failedToolIds) {
|
|
|
515
555
|
messages.push({ role: "user", content: msg.content, status: "completed" });
|
|
516
556
|
continue;
|
|
517
557
|
}
|
|
558
|
+
const timing = assistantTimings.get(msg);
|
|
518
559
|
messages.push({
|
|
519
560
|
role: "assistant",
|
|
520
561
|
content: msg.content,
|
|
521
|
-
status: "completed",
|
|
562
|
+
status: timing?.status ?? "completed",
|
|
563
|
+
...timing ? { timestamp: new Date(timing.startedAt).toISOString() } : {},
|
|
564
|
+
...timing?.durationMs === void 0 ? {} : { duration_ms: timing.durationMs },
|
|
522
565
|
...msg.tool_calls?.length ? { tool_calls: msg.tool_calls.map((call) => toToolCallInfo(call, results, failedToolIds)) } : {}
|
|
523
566
|
});
|
|
524
567
|
}
|
|
@@ -527,6 +570,7 @@ function toChatMessages(history, streamingText, streamingCalls, failedToolIds) {
|
|
|
527
570
|
role: "assistant",
|
|
528
571
|
content: streamingText,
|
|
529
572
|
status: "streaming",
|
|
573
|
+
...activeStartedAt === null ? {} : { timestamp: new Date(activeStartedAt).toISOString() },
|
|
530
574
|
...streamingCalls.length ? { tool_calls: streamingCalls.map((call) => toToolCallInfo(call, results, failedToolIds)) } : {}
|
|
531
575
|
});
|
|
532
576
|
}
|
|
@@ -544,6 +588,184 @@ function toToolCallInfo(call, results, failedToolIds) {
|
|
|
544
588
|
};
|
|
545
589
|
}
|
|
546
590
|
|
|
591
|
+
// src/hooks/use-message-pin.ts
|
|
592
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
593
|
+
var DEFAULT_MARGIN_PX = 24;
|
|
594
|
+
var MANUAL_SCROLL_TOLERANCE_PX = 2;
|
|
595
|
+
function useMessagePin({
|
|
596
|
+
targetKey,
|
|
597
|
+
pinTarget,
|
|
598
|
+
layoutKey,
|
|
599
|
+
getScrollElement,
|
|
600
|
+
getContentElement,
|
|
601
|
+
getTargetElement,
|
|
602
|
+
getTargetScrollTop,
|
|
603
|
+
getSpacerHeight,
|
|
604
|
+
setSpacerHeight,
|
|
605
|
+
stopAutoScroll,
|
|
606
|
+
scrollToBottom,
|
|
607
|
+
margin = DEFAULT_MARGIN_PX
|
|
608
|
+
}) {
|
|
609
|
+
const pinActiveRef = useRef3(false);
|
|
610
|
+
const frameRef = useRef3(null);
|
|
611
|
+
const retryTimeoutRef = useRef3(null);
|
|
612
|
+
const manualScrollCheckTimeoutRef = useRef3(null);
|
|
613
|
+
const repositionPendingRef = useRef3(false);
|
|
614
|
+
const pinnedTargetScrollTopRef = useRef3(null);
|
|
615
|
+
const appliedTargetKeyRef = useRef3(null);
|
|
616
|
+
const initialObservedTargetKeyRef = useRef3(pinTarget ? null : targetKey);
|
|
617
|
+
const observedTargetKeyRef = useRef3(initialObservedTargetKeyRef.current);
|
|
618
|
+
const setSpacerHeightRef = useRef3(setSpacerHeight);
|
|
619
|
+
setSpacerHeightRef.current = setSpacerHeight;
|
|
620
|
+
const release = useCallback3(() => {
|
|
621
|
+
if (!pinActiveRef.current) return;
|
|
622
|
+
pinActiveRef.current = false;
|
|
623
|
+
repositionPendingRef.current = false;
|
|
624
|
+
pinnedTargetScrollTopRef.current = null;
|
|
625
|
+
if (frameRef.current != null) cancelAnimationFrame(frameRef.current);
|
|
626
|
+
frameRef.current = null;
|
|
627
|
+
if (manualScrollCheckTimeoutRef.current != null) {
|
|
628
|
+
clearTimeout(manualScrollCheckTimeoutRef.current);
|
|
629
|
+
manualScrollCheckTimeoutRef.current = null;
|
|
630
|
+
}
|
|
631
|
+
setSpacerHeightRef.current(0);
|
|
632
|
+
}, []);
|
|
633
|
+
const reposition = useCallback3(
|
|
634
|
+
() => {
|
|
635
|
+
const scroll = getScrollElement();
|
|
636
|
+
const target = getTargetElement();
|
|
637
|
+
if (!scroll || !pinActiveRef.current) return;
|
|
638
|
+
const targetScrollTop = target ? Math.max(
|
|
639
|
+
0,
|
|
640
|
+
scroll.scrollTop + target.getBoundingClientRect().top - scroll.getBoundingClientRect().top - margin
|
|
641
|
+
) : getTargetScrollTop?.(scroll);
|
|
642
|
+
if (targetScrollTop == null) return;
|
|
643
|
+
const previousSpacerHeight = getSpacerHeight();
|
|
644
|
+
const baseScrollHeight = scroll.scrollHeight - previousSpacerHeight;
|
|
645
|
+
const nextSpacerHeight = Math.max(
|
|
646
|
+
0,
|
|
647
|
+
targetScrollTop + scroll.clientHeight - baseScrollHeight
|
|
648
|
+
);
|
|
649
|
+
setSpacerHeightRef.current(nextSpacerHeight);
|
|
650
|
+
pinnedTargetScrollTopRef.current = targetScrollTop;
|
|
651
|
+
stopAutoScroll();
|
|
652
|
+
scroll.scrollTop = targetScrollTop;
|
|
653
|
+
if (nextSpacerHeight === 0 && previousSpacerHeight > 0) {
|
|
654
|
+
pinActiveRef.current = false;
|
|
655
|
+
scrollToBottom();
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
[
|
|
659
|
+
getScrollElement,
|
|
660
|
+
getSpacerHeight,
|
|
661
|
+
getTargetElement,
|
|
662
|
+
getTargetScrollTop,
|
|
663
|
+
margin,
|
|
664
|
+
scrollToBottom,
|
|
665
|
+
stopAutoScroll
|
|
666
|
+
]
|
|
667
|
+
);
|
|
668
|
+
const scheduleReposition = useCallback3(
|
|
669
|
+
() => {
|
|
670
|
+
if (!pinActiveRef.current) return;
|
|
671
|
+
repositionPendingRef.current = true;
|
|
672
|
+
if (frameRef.current != null) return;
|
|
673
|
+
frameRef.current = requestAnimationFrame(() => {
|
|
674
|
+
frameRef.current = null;
|
|
675
|
+
const shouldReposition = repositionPendingRef.current;
|
|
676
|
+
repositionPendingRef.current = false;
|
|
677
|
+
if (shouldReposition) reposition();
|
|
678
|
+
});
|
|
679
|
+
},
|
|
680
|
+
[reposition]
|
|
681
|
+
);
|
|
682
|
+
useEffect3(() => {
|
|
683
|
+
if (!targetKey) {
|
|
684
|
+
release();
|
|
685
|
+
appliedTargetKeyRef.current = null;
|
|
686
|
+
observedTargetKeyRef.current = null;
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
if (!pinTarget) {
|
|
690
|
+
if (pinActiveRef.current && appliedTargetKeyRef.current != null && appliedTargetKeyRef.current !== targetKey) {
|
|
691
|
+
release();
|
|
692
|
+
}
|
|
693
|
+
observedTargetKeyRef.current = targetKey;
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (appliedTargetKeyRef.current === targetKey) {
|
|
697
|
+
observedTargetKeyRef.current = targetKey;
|
|
698
|
+
return;
|
|
699
|
+
}
|
|
700
|
+
if (observedTargetKeyRef.current === targetKey) return;
|
|
701
|
+
appliedTargetKeyRef.current = targetKey;
|
|
702
|
+
observedTargetKeyRef.current = targetKey;
|
|
703
|
+
pinActiveRef.current = true;
|
|
704
|
+
stopAutoScroll();
|
|
705
|
+
scheduleReposition();
|
|
706
|
+
if (retryTimeoutRef.current != null) clearTimeout(retryTimeoutRef.current);
|
|
707
|
+
retryTimeoutRef.current = window.setTimeout(() => {
|
|
708
|
+
retryTimeoutRef.current = null;
|
|
709
|
+
scheduleReposition();
|
|
710
|
+
}, 80);
|
|
711
|
+
}, [pinTarget, release, scheduleReposition, stopAutoScroll, targetKey]);
|
|
712
|
+
useEffect3(() => {
|
|
713
|
+
if (layoutKey !== void 0) scheduleReposition();
|
|
714
|
+
}, [layoutKey, scheduleReposition]);
|
|
715
|
+
useEffect3(() => {
|
|
716
|
+
const scroll = getScrollElement();
|
|
717
|
+
if (!scroll) return;
|
|
718
|
+
const content = getContentElement?.();
|
|
719
|
+
const observer = new ResizeObserver(scheduleReposition);
|
|
720
|
+
observer.observe(scroll);
|
|
721
|
+
if (content && content !== scroll) observer.observe(content);
|
|
722
|
+
window.addEventListener("resize", scheduleReposition);
|
|
723
|
+
window.visualViewport?.addEventListener("resize", scheduleReposition);
|
|
724
|
+
const handleScroll = () => {
|
|
725
|
+
if (!pinActiveRef.current) return;
|
|
726
|
+
if (manualScrollCheckTimeoutRef.current != null) {
|
|
727
|
+
clearTimeout(manualScrollCheckTimeoutRef.current);
|
|
728
|
+
}
|
|
729
|
+
manualScrollCheckTimeoutRef.current = window.setTimeout(() => {
|
|
730
|
+
manualScrollCheckTimeoutRef.current = null;
|
|
731
|
+
if (!pinActiveRef.current || repositionPendingRef.current) return;
|
|
732
|
+
const targetScrollTop = pinnedTargetScrollTopRef.current;
|
|
733
|
+
if (targetScrollTop != null && Math.abs(scroll.scrollTop - targetScrollTop) > MANUAL_SCROLL_TOLERANCE_PX) {
|
|
734
|
+
release();
|
|
735
|
+
}
|
|
736
|
+
}, 0);
|
|
737
|
+
};
|
|
738
|
+
scroll.addEventListener("scroll", handleScroll, { passive: true });
|
|
739
|
+
return () => {
|
|
740
|
+
observer.disconnect();
|
|
741
|
+
window.removeEventListener("resize", scheduleReposition);
|
|
742
|
+
window.visualViewport?.removeEventListener("resize", scheduleReposition);
|
|
743
|
+
scroll.removeEventListener("scroll", handleScroll);
|
|
744
|
+
};
|
|
745
|
+
}, [getContentElement, getScrollElement, release, scheduleReposition]);
|
|
746
|
+
useEffect3(
|
|
747
|
+
() => () => {
|
|
748
|
+
if (frameRef.current != null) cancelAnimationFrame(frameRef.current);
|
|
749
|
+
frameRef.current = null;
|
|
750
|
+
if (retryTimeoutRef.current != null) clearTimeout(retryTimeoutRef.current);
|
|
751
|
+
retryTimeoutRef.current = null;
|
|
752
|
+
if (manualScrollCheckTimeoutRef.current != null) {
|
|
753
|
+
clearTimeout(manualScrollCheckTimeoutRef.current);
|
|
754
|
+
}
|
|
755
|
+
manualScrollCheckTimeoutRef.current = null;
|
|
756
|
+
repositionPendingRef.current = false;
|
|
757
|
+
pinnedTargetScrollTopRef.current = null;
|
|
758
|
+
appliedTargetKeyRef.current = null;
|
|
759
|
+
observedTargetKeyRef.current = initialObservedTargetKeyRef.current;
|
|
760
|
+
pinActiveRef.current = false;
|
|
761
|
+
setSpacerHeightRef.current(0);
|
|
762
|
+
},
|
|
763
|
+
[]
|
|
764
|
+
);
|
|
765
|
+
const isActive = useCallback3(() => pinActiveRef.current, []);
|
|
766
|
+
return { release, isActive };
|
|
767
|
+
}
|
|
768
|
+
|
|
547
769
|
// src/components/AgentChat.tsx
|
|
548
770
|
import { BladeApiError, latestPostChatFollowup } from "@blade-hq/agent-client";
|
|
549
771
|
|
|
@@ -636,6 +858,18 @@ var ArrowUp = createLucideIcon("ArrowUp", [
|
|
|
636
858
|
["path", { d: "M12 19V5", key: "x0mq9r" }]
|
|
637
859
|
]);
|
|
638
860
|
|
|
861
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/book-open.js
|
|
862
|
+
var BookOpen = createLucideIcon("BookOpen", [
|
|
863
|
+
["path", { d: "M12 7v14", key: "1akyts" }],
|
|
864
|
+
[
|
|
865
|
+
"path",
|
|
866
|
+
{
|
|
867
|
+
d: "M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z",
|
|
868
|
+
key: "ruj8y"
|
|
869
|
+
}
|
|
870
|
+
]
|
|
871
|
+
]);
|
|
872
|
+
|
|
639
873
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/bot.js
|
|
640
874
|
var Bot = createLucideIcon("Bot", [
|
|
641
875
|
["path", { d: "M12 8V4H8", key: "hb8ula" }],
|
|
@@ -646,31 +880,6 @@ var Bot = createLucideIcon("Bot", [
|
|
|
646
880
|
["path", { d: "M9 13v2", key: "rq6x2g" }]
|
|
647
881
|
]);
|
|
648
882
|
|
|
649
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/brain.js
|
|
650
|
-
var Brain = createLucideIcon("Brain", [
|
|
651
|
-
[
|
|
652
|
-
"path",
|
|
653
|
-
{
|
|
654
|
-
d: "M12 5a3 3 0 1 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 1 0 12 18Z",
|
|
655
|
-
key: "l5xja"
|
|
656
|
-
}
|
|
657
|
-
],
|
|
658
|
-
[
|
|
659
|
-
"path",
|
|
660
|
-
{
|
|
661
|
-
d: "M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 1 1 12 18Z",
|
|
662
|
-
key: "ep3f8r"
|
|
663
|
-
}
|
|
664
|
-
],
|
|
665
|
-
["path", { d: "M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4", key: "1p4c4q" }],
|
|
666
|
-
["path", { d: "M17.599 6.5a3 3 0 0 0 .399-1.375", key: "tmeiqw" }],
|
|
667
|
-
["path", { d: "M6.003 5.125A3 3 0 0 0 6.401 6.5", key: "105sqy" }],
|
|
668
|
-
["path", { d: "M3.477 10.896a4 4 0 0 1 .585-.396", key: "ql3yin" }],
|
|
669
|
-
["path", { d: "M19.938 10.5a4 4 0 0 1 .585.396", key: "1qfode" }],
|
|
670
|
-
["path", { d: "M6 18a4 4 0 0 1-1.967-.516", key: "2e4loj" }],
|
|
671
|
-
["path", { d: "M19.967 17.484A4 4 0 0 1 18 18", key: "159ez6" }]
|
|
672
|
-
]);
|
|
673
|
-
|
|
674
883
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/check.js
|
|
675
884
|
var Check = createLucideIcon("Check", [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]]);
|
|
676
885
|
|
|
@@ -697,6 +906,39 @@ var Copy = createLucideIcon("Copy", [
|
|
|
697
906
|
["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
|
|
698
907
|
]);
|
|
699
908
|
|
|
909
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/earth.js
|
|
910
|
+
var Earth = createLucideIcon("Earth", [
|
|
911
|
+
["path", { d: "M21.54 15H17a2 2 0 0 0-2 2v4.54", key: "1djwo0" }],
|
|
912
|
+
[
|
|
913
|
+
"path",
|
|
914
|
+
{
|
|
915
|
+
d: "M7 3.34V5a3 3 0 0 0 3 3a2 2 0 0 1 2 2c0 1.1.9 2 2 2a2 2 0 0 0 2-2c0-1.1.9-2 2-2h3.17",
|
|
916
|
+
key: "1tzkfa"
|
|
917
|
+
}
|
|
918
|
+
],
|
|
919
|
+
["path", { d: "M11 21.95V18a2 2 0 0 0-2-2a2 2 0 0 1-2-2v-1a2 2 0 0 0-2-2H2.05", key: "14pb5j" }],
|
|
920
|
+
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]
|
|
921
|
+
]);
|
|
922
|
+
|
|
923
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-pen-line.js
|
|
924
|
+
var FilePenLine = createLucideIcon("FilePenLine", [
|
|
925
|
+
[
|
|
926
|
+
"path",
|
|
927
|
+
{
|
|
928
|
+
d: "m18 5-2.414-2.414A2 2 0 0 0 14.172 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2",
|
|
929
|
+
key: "142zxg"
|
|
930
|
+
}
|
|
931
|
+
],
|
|
932
|
+
[
|
|
933
|
+
"path",
|
|
934
|
+
{
|
|
935
|
+
d: "M21.378 12.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z",
|
|
936
|
+
key: "2t3380"
|
|
937
|
+
}
|
|
938
|
+
],
|
|
939
|
+
["path", { d: "M8 18h1", key: "13wk12" }]
|
|
940
|
+
]);
|
|
941
|
+
|
|
700
942
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-text.js
|
|
701
943
|
var FileText = createLucideIcon("FileText", [
|
|
702
944
|
["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
|
|
@@ -781,6 +1023,12 @@ var Play = createLucideIcon("Play", [
|
|
|
781
1023
|
["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]
|
|
782
1024
|
]);
|
|
783
1025
|
|
|
1026
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/search.js
|
|
1027
|
+
var Search = createLucideIcon("Search", [
|
|
1028
|
+
["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }],
|
|
1029
|
+
["path", { d: "m21 21-4.3-4.3", key: "1qie3q" }]
|
|
1030
|
+
]);
|
|
1031
|
+
|
|
784
1032
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/settings-2.js
|
|
785
1033
|
var Settings2 = createLucideIcon("Settings2", [
|
|
786
1034
|
["path", { d: "M20 7h-9", key: "3s1dr2" }],
|
|
@@ -809,6 +1057,12 @@ var Square = createLucideIcon("Square", [
|
|
|
809
1057
|
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
|
|
810
1058
|
]);
|
|
811
1059
|
|
|
1060
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/terminal.js
|
|
1061
|
+
var Terminal = createLucideIcon("Terminal", [
|
|
1062
|
+
["polyline", { points: "4 17 10 11 4 5", key: "akl6gq" }],
|
|
1063
|
+
["line", { x1: "12", x2: "20", y1: "19", y2: "19", key: "q2wloq" }]
|
|
1064
|
+
]);
|
|
1065
|
+
|
|
812
1066
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/triangle-alert.js
|
|
813
1067
|
var TriangleAlert = createLucideIcon("TriangleAlert", [
|
|
814
1068
|
[
|
|
@@ -822,6 +1076,17 @@ var TriangleAlert = createLucideIcon("TriangleAlert", [
|
|
|
822
1076
|
["path", { d: "M12 17h.01", key: "p32p05" }]
|
|
823
1077
|
]);
|
|
824
1078
|
|
|
1079
|
+
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/wrench.js
|
|
1080
|
+
var Wrench = createLucideIcon("Wrench", [
|
|
1081
|
+
[
|
|
1082
|
+
"path",
|
|
1083
|
+
{
|
|
1084
|
+
d: "M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z",
|
|
1085
|
+
key: "cbrjhi"
|
|
1086
|
+
}
|
|
1087
|
+
]
|
|
1088
|
+
]);
|
|
1089
|
+
|
|
825
1090
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/x.js
|
|
826
1091
|
var X = createLucideIcon("X", [
|
|
827
1092
|
["path", { d: "M18 6 6 18", key: "1bl5f8" }],
|
|
@@ -829,7 +1094,7 @@ var X = createLucideIcon("X", [
|
|
|
829
1094
|
]);
|
|
830
1095
|
|
|
831
1096
|
// src/components/AgentChat.tsx
|
|
832
|
-
import { useCallback as
|
|
1097
|
+
import { useCallback as useCallback8, useEffect as useEffect11, useMemo as useMemo8, useState as useState13 } from "react";
|
|
833
1098
|
|
|
834
1099
|
// src/lib/utils.ts
|
|
835
1100
|
function cn(...inputs) {
|
|
@@ -958,6 +1223,12 @@ function ReplayMismatchPrompt({ mismatch, className }) {
|
|
|
958
1223
|
|
|
959
1224
|
// src/components/ChatInput.tsx
|
|
960
1225
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1226
|
+
function isImeCompositionKey(event) {
|
|
1227
|
+
return event.isComposing || event.keyCode === 229;
|
|
1228
|
+
}
|
|
1229
|
+
function shouldSubmitChatInput(event, menuOpen) {
|
|
1230
|
+
return event.key === "Enter" && !event.shiftKey && !menuOpen && !isImeCompositionKey(event);
|
|
1231
|
+
}
|
|
961
1232
|
function ChatInput({
|
|
962
1233
|
value,
|
|
963
1234
|
onValueChange,
|
|
@@ -977,7 +1248,12 @@ function ChatInput({
|
|
|
977
1248
|
onValueChange("");
|
|
978
1249
|
};
|
|
979
1250
|
const handleKeyDown = (event) => {
|
|
980
|
-
if (
|
|
1251
|
+
if (shouldSubmitChatInput({
|
|
1252
|
+
key: event.key,
|
|
1253
|
+
shiftKey: event.shiftKey,
|
|
1254
|
+
isComposing: event.nativeEvent.isComposing,
|
|
1255
|
+
keyCode: event.nativeEvent.keyCode
|
|
1256
|
+
}, false)) {
|
|
981
1257
|
event.preventDefault();
|
|
982
1258
|
void handleSend();
|
|
983
1259
|
}
|
|
@@ -1027,16 +1303,16 @@ function ChatInput({
|
|
|
1027
1303
|
}
|
|
1028
1304
|
|
|
1029
1305
|
// src/components/ConnectionBanner.tsx
|
|
1030
|
-
import { useEffect as
|
|
1306
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
|
|
1031
1307
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1032
1308
|
var CONNECTION_NOTICE_DELAY_MS = 3e3;
|
|
1033
1309
|
var CONNECTION_ERROR_DELAY_MS = 15e3;
|
|
1034
1310
|
function useConnectionNoticePhase(connected) {
|
|
1035
1311
|
const [phase, setPhase] = useState4("hidden");
|
|
1036
|
-
const connectedRef =
|
|
1037
|
-
const timersRef =
|
|
1312
|
+
const connectedRef = useRef4(connected);
|
|
1313
|
+
const timersRef = useRef4([]);
|
|
1038
1314
|
connectedRef.current = connected;
|
|
1039
|
-
|
|
1315
|
+
useEffect4(() => {
|
|
1040
1316
|
const clearTimers = () => {
|
|
1041
1317
|
for (const timer of timersRef.current) clearTimeout(timer);
|
|
1042
1318
|
timersRef.current = [];
|
|
@@ -1076,7 +1352,7 @@ function useConnectionNoticePhase(connected) {
|
|
|
1076
1352
|
return phase;
|
|
1077
1353
|
}
|
|
1078
1354
|
function ConnectionBanner({ connection, className }) {
|
|
1079
|
-
const hasConnectedRef =
|
|
1355
|
+
const hasConnectedRef = useRef4(connection === "connected" || connection === "reconnecting");
|
|
1080
1356
|
if (connection === "connected") hasConnectedRef.current = true;
|
|
1081
1357
|
const connected = connection === "connected";
|
|
1082
1358
|
const phase = useConnectionNoticePhase(connected);
|
|
@@ -1103,10 +1379,10 @@ function ConnectionBanner({ connection, className }) {
|
|
|
1103
1379
|
|
|
1104
1380
|
// src/components/MessageList.tsx
|
|
1105
1381
|
import { isHiddenInternalMessage } from "@blade-hq/agent-client";
|
|
1106
|
-
import { useCallback as
|
|
1382
|
+
import { useCallback as useCallback7, useEffect as useEffect10, useMemo as useMemo7, useRef as useRef11, useState as useState12 } from "react";
|
|
1107
1383
|
|
|
1108
1384
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
|
|
1109
|
-
import { useCallback as
|
|
1385
|
+
import { useCallback as useCallback4, useMemo as useMemo3, useRef as useRef5, useState as useState5 } from "react";
|
|
1110
1386
|
var DEFAULT_SPRING_ANIMATION = {
|
|
1111
1387
|
/**
|
|
1112
1388
|
* A value from 0 to 1, on how much to damp the animation.
|
|
@@ -1146,9 +1422,9 @@ var useStickToBottom = (options = {}) => {
|
|
|
1146
1422
|
const [escapedFromLock, updateEscapedFromLock] = useState5(false);
|
|
1147
1423
|
const [isAtBottom, updateIsAtBottom] = useState5(options.initial !== false);
|
|
1148
1424
|
const [isNearBottom, setIsNearBottom] = useState5(false);
|
|
1149
|
-
const optionsRef =
|
|
1425
|
+
const optionsRef = useRef5(null);
|
|
1150
1426
|
optionsRef.current = options;
|
|
1151
|
-
const isSelecting =
|
|
1427
|
+
const isSelecting = useCallback4(() => {
|
|
1152
1428
|
if (!mouseDown) {
|
|
1153
1429
|
return false;
|
|
1154
1430
|
}
|
|
@@ -1159,11 +1435,11 @@ var useStickToBottom = (options = {}) => {
|
|
|
1159
1435
|
const range = selection.getRangeAt(0);
|
|
1160
1436
|
return range.commonAncestorContainer.contains(scrollRef.current) || scrollRef.current?.contains(range.commonAncestorContainer);
|
|
1161
1437
|
}, []);
|
|
1162
|
-
const setIsAtBottom =
|
|
1438
|
+
const setIsAtBottom = useCallback4((isAtBottom2) => {
|
|
1163
1439
|
state.isAtBottom = isAtBottom2;
|
|
1164
1440
|
updateIsAtBottom(isAtBottom2);
|
|
1165
1441
|
}, []);
|
|
1166
|
-
const setEscapedFromLock =
|
|
1442
|
+
const setEscapedFromLock = useCallback4((escapedFromLock2) => {
|
|
1167
1443
|
state.escapedFromLock = escapedFromLock2;
|
|
1168
1444
|
updateEscapedFromLock(escapedFromLock2);
|
|
1169
1445
|
}, []);
|
|
@@ -1220,7 +1496,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
1220
1496
|
}
|
|
1221
1497
|
};
|
|
1222
1498
|
}, []);
|
|
1223
|
-
const scrollToBottom =
|
|
1499
|
+
const scrollToBottom = useCallback4((scrollOptions = {}) => {
|
|
1224
1500
|
if (typeof scrollOptions === "string") {
|
|
1225
1501
|
scrollOptions = { animation: scrollOptions };
|
|
1226
1502
|
}
|
|
@@ -1305,11 +1581,11 @@ var useStickToBottom = (options = {}) => {
|
|
|
1305
1581
|
}
|
|
1306
1582
|
return next();
|
|
1307
1583
|
}, [setIsAtBottom, isSelecting, state]);
|
|
1308
|
-
const stopScroll =
|
|
1584
|
+
const stopScroll = useCallback4(() => {
|
|
1309
1585
|
setEscapedFromLock(true);
|
|
1310
1586
|
setIsAtBottom(false);
|
|
1311
1587
|
}, [setEscapedFromLock, setIsAtBottom]);
|
|
1312
|
-
const handleScroll =
|
|
1588
|
+
const handleScroll = useCallback4(({ target }) => {
|
|
1313
1589
|
if (target !== scrollRef.current) {
|
|
1314
1590
|
return;
|
|
1315
1591
|
}
|
|
@@ -1348,7 +1624,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
1348
1624
|
}
|
|
1349
1625
|
}, 1);
|
|
1350
1626
|
}, [setEscapedFromLock, setIsAtBottom, isSelecting, state]);
|
|
1351
|
-
const handleWheel =
|
|
1627
|
+
const handleWheel = useCallback4(({ target, deltaY }) => {
|
|
1352
1628
|
let element = target;
|
|
1353
1629
|
while (!["scroll", "auto"].includes(getComputedStyle(element).overflow)) {
|
|
1354
1630
|
if (!element.parentElement) {
|
|
@@ -1418,7 +1694,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
1418
1694
|
};
|
|
1419
1695
|
};
|
|
1420
1696
|
function useRefCallback(callback, deps) {
|
|
1421
|
-
const result =
|
|
1697
|
+
const result = useCallback4((ref) => {
|
|
1422
1698
|
result.current = ref;
|
|
1423
1699
|
return callback(ref);
|
|
1424
1700
|
}, deps);
|
|
@@ -1450,11 +1726,11 @@ function mergeAnimations(...animations) {
|
|
|
1450
1726
|
|
|
1451
1727
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
|
|
1452
1728
|
import * as React from "react";
|
|
1453
|
-
import { createContext as createContext2, useContext as useContext2, useEffect as
|
|
1729
|
+
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef6 } from "react";
|
|
1454
1730
|
var StickToBottomContext = createContext2(null);
|
|
1455
|
-
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect :
|
|
1731
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect5;
|
|
1456
1732
|
function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
|
|
1457
|
-
const customTargetScrollTop =
|
|
1733
|
+
const customTargetScrollTop = useRef6(null);
|
|
1458
1734
|
const targetScrollTop = React.useCallback((target, elements) => {
|
|
1459
1735
|
const get = context?.targetScrollTop ?? currentTargetScrollTop;
|
|
1460
1736
|
return get?.(target, elements) ?? target;
|
|
@@ -1530,8 +1806,13 @@ function useStickToBottomContext() {
|
|
|
1530
1806
|
}
|
|
1531
1807
|
|
|
1532
1808
|
// src/components/AssistantTurnBlock.tsx
|
|
1533
|
-
import {
|
|
1534
|
-
|
|
1809
|
+
import {
|
|
1810
|
+
getFileParts,
|
|
1811
|
+
getImageParts,
|
|
1812
|
+
getTextContent,
|
|
1813
|
+
normalizeMessageContent
|
|
1814
|
+
} from "@blade-hq/agent-client";
|
|
1815
|
+
import { useEffect as useEffect8, useRef as useRef9, useState as useState10 } from "react";
|
|
1535
1816
|
|
|
1536
1817
|
// src/components/AgentLoopBlock.tsx
|
|
1537
1818
|
import { useState as useState6 } from "react";
|
|
@@ -1551,6 +1832,7 @@ var TOOL_NAME_ALIASES = {
|
|
|
1551
1832
|
glob: "Glob",
|
|
1552
1833
|
grep: "Grep",
|
|
1553
1834
|
ls: "Ls",
|
|
1835
|
+
multi_edit: "MultiEdit",
|
|
1554
1836
|
read: "Read",
|
|
1555
1837
|
read_skill: "ReadSkill",
|
|
1556
1838
|
web_fetch: "WebFetch",
|
|
@@ -1563,6 +1845,7 @@ var TOOL_DISPLAY_LABELS = {
|
|
|
1563
1845
|
Read: "\u8BFB\u53D6\u6587\u4EF6",
|
|
1564
1846
|
Write: "\u5199\u5165\u6587\u4EF6",
|
|
1565
1847
|
Edit: "\u7F16\u8F91\u6587\u4EF6",
|
|
1848
|
+
MultiEdit: "\u7F16\u8F91\u6587\u4EF6",
|
|
1566
1849
|
Ls: "\u5217\u51FA\u76EE\u5F55",
|
|
1567
1850
|
Glob: "\u5339\u914D\u6587\u4EF6",
|
|
1568
1851
|
Grep: "\u641C\u7D22\u6587\u672C",
|
|
@@ -1576,6 +1859,16 @@ var TOOL_DISPLAY_LABELS = {
|
|
|
1576
1859
|
ListSessions: "\u5217\u51FA\u5386\u53F2\u4F1A\u8BDD",
|
|
1577
1860
|
GetSessionHistory: "\u8BFB\u53D6\u4F1A\u8BDD\u5386\u53F2"
|
|
1578
1861
|
};
|
|
1862
|
+
var PRIORITY_FILE_KEYS = [
|
|
1863
|
+
"file_path",
|
|
1864
|
+
"output_file",
|
|
1865
|
+
"output_path",
|
|
1866
|
+
"path",
|
|
1867
|
+
"filepath",
|
|
1868
|
+
"target_file",
|
|
1869
|
+
"destination",
|
|
1870
|
+
"filename"
|
|
1871
|
+
];
|
|
1579
1872
|
function safeParseJson(value) {
|
|
1580
1873
|
if (!value) return null;
|
|
1581
1874
|
try {
|
|
@@ -1584,6 +1877,38 @@ function safeParseJson(value) {
|
|
|
1584
1877
|
return null;
|
|
1585
1878
|
}
|
|
1586
1879
|
}
|
|
1880
|
+
function isPlainObject(value) {
|
|
1881
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1882
|
+
}
|
|
1883
|
+
function findFileLikeValue(value, depth = 0, allowPlainString = false) {
|
|
1884
|
+
if (depth > 3) return null;
|
|
1885
|
+
if (typeof value === "string") {
|
|
1886
|
+
const trimmed = value.trim();
|
|
1887
|
+
if (!trimmed) return null;
|
|
1888
|
+
if (allowPlainString || trimmed.includes("/") || /\.[a-z0-9]{1,8}$/i.test(trimmed)) {
|
|
1889
|
+
return trimmed;
|
|
1890
|
+
}
|
|
1891
|
+
return null;
|
|
1892
|
+
}
|
|
1893
|
+
if (Array.isArray(value)) {
|
|
1894
|
+
for (const item of value) {
|
|
1895
|
+
const found = findFileLikeValue(item, depth + 1, allowPlainString);
|
|
1896
|
+
if (found) return found;
|
|
1897
|
+
}
|
|
1898
|
+
return null;
|
|
1899
|
+
}
|
|
1900
|
+
if (isPlainObject(value)) {
|
|
1901
|
+
for (const key of PRIORITY_FILE_KEYS) {
|
|
1902
|
+
const direct = findFileLikeValue(value[key], depth + 1, true);
|
|
1903
|
+
if (direct) return direct;
|
|
1904
|
+
}
|
|
1905
|
+
for (const nested of Object.values(value)) {
|
|
1906
|
+
const found = findFileLikeValue(nested, depth + 1, allowPlainString);
|
|
1907
|
+
if (found) return found;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
return null;
|
|
1911
|
+
}
|
|
1587
1912
|
function getStringArgValue(args, key) {
|
|
1588
1913
|
const value = args?.[key];
|
|
1589
1914
|
return typeof value === "string" ? value.trim() : "";
|
|
@@ -1606,6 +1931,15 @@ function formatToolName(name) {
|
|
|
1606
1931
|
const normalized = stripped.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
1607
1932
|
return TOOL_NAME_ALIASES[normalized] ?? stripped;
|
|
1608
1933
|
}
|
|
1934
|
+
function extractToolFilePath(toolCall) {
|
|
1935
|
+
const formattedName = formatToolName(toolCall.name);
|
|
1936
|
+
if (formattedName !== "Read" && formattedName !== "Write" && formattedName !== "Edit" && formattedName !== "MultiEdit") {
|
|
1937
|
+
return null;
|
|
1938
|
+
}
|
|
1939
|
+
const filePath = findFileLikeValue(safeParseJson(toolCall.arguments));
|
|
1940
|
+
if (!filePath || filePath.split("/").pop()?.toLowerCase() === "phase.json") return null;
|
|
1941
|
+
return filePath;
|
|
1942
|
+
}
|
|
1609
1943
|
function getToolDisplayLabel(toolCall) {
|
|
1610
1944
|
const normalized = formatToolName(toolCall.name);
|
|
1611
1945
|
const args = safeParseJson(toolCall.arguments);
|
|
@@ -1687,79 +2021,64 @@ function AgentLoopBlock({ toolCall }) {
|
|
|
1687
2021
|
const description = parseAgentDescription(toolCall.arguments);
|
|
1688
2022
|
const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
|
|
1689
2023
|
const failed = toolCall.status === "error" || toolCall.status === "cancelled";
|
|
1690
|
-
|
|
2024
|
+
const hasResult = toolCall.result != null;
|
|
2025
|
+
const iconClass = cn(
|
|
2026
|
+
"size-3.5 shrink-0",
|
|
2027
|
+
failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
|
|
2028
|
+
);
|
|
2029
|
+
return /* @__PURE__ */ jsxs5("div", { className: "blade-chat-agent-loop text-xs leading-[22px]", children: [
|
|
1691
2030
|
/* @__PURE__ */ jsxs5(
|
|
1692
|
-
"
|
|
2031
|
+
"button",
|
|
1693
2032
|
{
|
|
2033
|
+
type: "button",
|
|
2034
|
+
onClick: () => hasResult && setExpanded(!expanded),
|
|
2035
|
+
disabled: !hasResult,
|
|
2036
|
+
"aria-expanded": hasResult ? expanded : void 0,
|
|
2037
|
+
"data-testid": "execution-tool-intent",
|
|
1694
2038
|
className: cn(
|
|
1695
|
-
"
|
|
1696
|
-
|
|
2039
|
+
"flex min-w-0 items-center gap-1 py-1.5 text-left",
|
|
2040
|
+
hasResult && "cursor-pointer hover:text-[hsl(var(--foreground))]",
|
|
2041
|
+
failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
|
|
1697
2042
|
),
|
|
2043
|
+
title: `\u5B50\u4EFB\u52A1\uFF1A${description}`,
|
|
1698
2044
|
children: [
|
|
1699
|
-
/* @__PURE__ */
|
|
1700
|
-
|
|
2045
|
+
running ? /* @__PURE__ */ jsx6(LoaderCircle, { className: cn(iconClass, "animate-spin"), "aria-hidden": "true" }) : toolCall.status === "error" ? /* @__PURE__ */ jsx6(CircleAlert, { className: iconClass, "aria-hidden": "true" }) : toolCall.status === "cancelled" ? /* @__PURE__ */ jsx6(X, { className: iconClass, "aria-hidden": "true" }) : /* @__PURE__ */ jsx6(Bot, { className: iconClass, "aria-hidden": "true" }),
|
|
2046
|
+
/* @__PURE__ */ jsxs5("span", { className: "min-w-0 truncate", children: [
|
|
2047
|
+
"\u5B50\u4EFB\u52A1\uFF1A",
|
|
2048
|
+
description
|
|
2049
|
+
] }),
|
|
2050
|
+
hasResult ? /* @__PURE__ */ jsx6(
|
|
2051
|
+
ChevronRight,
|
|
1701
2052
|
{
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
ChevronRight,
|
|
1709
|
-
{
|
|
1710
|
-
size: 11,
|
|
1711
|
-
className: cn(
|
|
1712
|
-
"shrink-0 text-[hsl(var(--muted-foreground))] transition-transform",
|
|
1713
|
-
expanded && "rotate-90"
|
|
1714
|
-
)
|
|
1715
|
-
}
|
|
1716
|
-
),
|
|
1717
|
-
/* @__PURE__ */ jsx6(Bot, { size: 12, className: "shrink-0 text-[hsl(var(--muted-foreground))]" }),
|
|
1718
|
-
/* @__PURE__ */ jsxs5(
|
|
1719
|
-
"span",
|
|
1720
|
-
{
|
|
1721
|
-
className: cn(
|
|
1722
|
-
"flex shrink-0 items-center gap-1 text-[10px]",
|
|
1723
|
-
failed ? "text-[hsl(var(--muted-foreground))]" : running ? "text-blue-300" : "text-[hsl(var(--primary))]"
|
|
1724
|
-
),
|
|
1725
|
-
children: [
|
|
1726
|
-
running ? /* @__PURE__ */ jsx6(LoaderCircle, { size: 11, className: "animate-spin" }) : failed ? /* @__PURE__ */ jsx6(X, { size: 11 }) : /* @__PURE__ */ jsx6(Check, { size: 11 }),
|
|
1727
|
-
/* @__PURE__ */ jsx6("span", { children: running ? "\u6267\u884C\u4E2D" : failed ? "\u5DF2\u7EC8\u6B62" : "\u5B8C\u6210" })
|
|
1728
|
-
]
|
|
1729
|
-
}
|
|
1730
|
-
),
|
|
1731
|
-
/* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: [
|
|
1732
|
-
"\u5B50\u667A\u80FD\u4F53\uFF1A",
|
|
1733
|
-
description
|
|
1734
|
-
] })
|
|
1735
|
-
]
|
|
2053
|
+
size: 14,
|
|
2054
|
+
className: cn(
|
|
2055
|
+
"shrink-0 transition-transform duration-300",
|
|
2056
|
+
expanded && "rotate-90"
|
|
2057
|
+
),
|
|
2058
|
+
"aria-hidden": "true"
|
|
1736
2059
|
}
|
|
1737
|
-
)
|
|
1738
|
-
typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 && /* @__PURE__ */ jsx6("span", { className: "shrink-0 font-mono text-[10px] text-[hsl(var(--muted-foreground))]", children: formatToolDuration(toolCall.duration_ms) })
|
|
2060
|
+
) : null
|
|
1739
2061
|
]
|
|
1740
2062
|
}
|
|
1741
2063
|
),
|
|
1742
|
-
expanded &&
|
|
1743
|
-
/* @__PURE__ */ jsx6("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
|
|
1744
|
-
/* @__PURE__ */ jsx6("pre", { className: "max-h-[400px] overflow-auto whitespace-pre-wrap rounded-md bg-[hsl(var(--muted))] p-2 font-mono text-[11px] text-[hsl(var(--foreground))]", children: formatToolResult(toolCall.result) })
|
|
1745
|
-
] })
|
|
2064
|
+
expanded && hasResult ? /* @__PURE__ */ jsx6("div", { className: "ml-[18px] mt-1.5 max-h-[400px] overflow-auto whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]", children: formatToolResult(toolCall.result) }) : null
|
|
1746
2065
|
] });
|
|
1747
2066
|
}
|
|
1748
2067
|
|
|
1749
2068
|
// src/components/MarkdownContent.tsx
|
|
1750
2069
|
import {
|
|
1751
|
-
useEffect as
|
|
2070
|
+
useEffect as useEffect6,
|
|
1752
2071
|
useMemo as useMemo5,
|
|
1753
|
-
useRef as
|
|
2072
|
+
useRef as useRef7,
|
|
1754
2073
|
useState as useState7
|
|
1755
2074
|
} from "react";
|
|
1756
2075
|
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1757
2076
|
var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
|
|
1758
2077
|
function CodeBlockPre({ children, node: _node, ...props }) {
|
|
1759
|
-
const preRef =
|
|
2078
|
+
const preRef = useRef7(null);
|
|
1760
2079
|
const [copied, setCopied] = useState7(false);
|
|
1761
2080
|
const [language, setLanguage] = useState7("");
|
|
1762
|
-
|
|
2081
|
+
useEffect6(() => {
|
|
1763
2082
|
const codeEl = preRef.current?.querySelector("code");
|
|
1764
2083
|
setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
|
|
1765
2084
|
}, []);
|
|
@@ -1833,7 +2152,7 @@ function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
|
|
|
1833
2152
|
import { useState as useState9 } from "react";
|
|
1834
2153
|
|
|
1835
2154
|
// src/components/AskUserQuestionBlock.tsx
|
|
1836
|
-
import { useEffect as
|
|
2155
|
+
import { useEffect as useEffect7, useMemo as useMemo6, useRef as useRef8, useState as useState8 } from "react";
|
|
1837
2156
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1838
2157
|
var CUSTOM_TEXTAREA_MAX_HEIGHT = 160;
|
|
1839
2158
|
function resizeCustomTextarea(textarea) {
|
|
@@ -1842,12 +2161,12 @@ function resizeCustomTextarea(textarea) {
|
|
|
1842
2161
|
textarea.style.overflowY = textarea.scrollHeight > CUSTOM_TEXTAREA_MAX_HEIGHT ? "auto" : "hidden";
|
|
1843
2162
|
}
|
|
1844
2163
|
function useAutoResizeTextarea(value) {
|
|
1845
|
-
const textareaRef =
|
|
1846
|
-
|
|
2164
|
+
const textareaRef = useRef8(null);
|
|
2165
|
+
useEffect7(() => {
|
|
1847
2166
|
const textarea = textareaRef.current;
|
|
1848
2167
|
if (textarea?.value === value) resizeCustomTextarea(textarea);
|
|
1849
2168
|
}, [value]);
|
|
1850
|
-
|
|
2169
|
+
useEffect7(() => {
|
|
1851
2170
|
const textarea = textareaRef.current;
|
|
1852
2171
|
if (!textarea || typeof ResizeObserver === "undefined") return;
|
|
1853
2172
|
let previousWidth = textarea.clientWidth;
|
|
@@ -1877,7 +2196,7 @@ function AskUserQuestionBlock({
|
|
|
1877
2196
|
const [usingCustom, setUsingCustom] = useState8(/* @__PURE__ */ new Set());
|
|
1878
2197
|
const [note, setNote] = useState8("");
|
|
1879
2198
|
const [submitted, setSubmitted] = useState8(false);
|
|
1880
|
-
|
|
2199
|
+
useEffect7(() => {
|
|
1881
2200
|
if (sessionStatus === "failed" || sessionStatus === "interrupted") {
|
|
1882
2201
|
setSubmitted(false);
|
|
1883
2202
|
}
|
|
@@ -2369,44 +2688,239 @@ function buildAskUserPayload(argumentsJson) {
|
|
|
2369
2688
|
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2370
2689
|
function ThinkingBlock({ reasoning, isStreaming }) {
|
|
2371
2690
|
const [open, setOpen] = useState10(false);
|
|
2372
|
-
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-thinking
|
|
2691
|
+
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-thinking text-xs", children: [
|
|
2373
2692
|
/* @__PURE__ */ jsxs9(
|
|
2374
2693
|
"button",
|
|
2375
2694
|
{
|
|
2376
2695
|
type: "button",
|
|
2377
2696
|
onClick: () => setOpen(!open),
|
|
2378
2697
|
"aria-expanded": open,
|
|
2379
|
-
className: "inline-flex items-center gap-1
|
|
2698
|
+
className: "group/thinking inline-flex items-center gap-1 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
|
|
2380
2699
|
children: [
|
|
2381
|
-
/* @__PURE__ */ jsx11(
|
|
2382
|
-
isStreaming ? /* @__PURE__ */ jsx11(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }) : /* @__PURE__ */ jsx11("span", { children: "\u601D\u8003\u8FC7\u7A0B" }),
|
|
2383
|
-
/* @__PURE__ */ jsxs9("span", { className: "text-[hsl(var(--muted-foreground))]/70", children: [
|
|
2384
|
-
"\xB7 ",
|
|
2385
|
-
new Intl.NumberFormat("zh-CN").format(reasoning.length),
|
|
2386
|
-
" \u5B57"
|
|
2387
|
-
] }),
|
|
2700
|
+
isStreaming ? /* @__PURE__ */ jsx11(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }) : /* @__PURE__ */ jsx11("span", { children: "\u5DF2\u601D\u8003" }),
|
|
2388
2701
|
/* @__PURE__ */ jsx11(
|
|
2389
|
-
|
|
2702
|
+
ChevronRight,
|
|
2390
2703
|
{
|
|
2391
|
-
size:
|
|
2392
|
-
className: cn(
|
|
2704
|
+
size: 14,
|
|
2705
|
+
className: cn(
|
|
2706
|
+
"shrink-0 opacity-0 transition-[opacity,transform] group-hover/thinking:opacity-100",
|
|
2707
|
+
open && "rotate-90 opacity-100"
|
|
2708
|
+
)
|
|
2393
2709
|
}
|
|
2394
2710
|
)
|
|
2395
2711
|
]
|
|
2396
2712
|
}
|
|
2397
2713
|
),
|
|
2398
|
-
open && /* @__PURE__ */ jsx11("div", { className: "mt-1.5 whitespace-pre-wrap
|
|
2714
|
+
open && /* @__PURE__ */ jsx11("div", { className: "mt-1.5 whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]", children: reasoning })
|
|
2399
2715
|
] });
|
|
2400
2716
|
}
|
|
2401
2717
|
function getMessageText(message) {
|
|
2402
2718
|
return getTextContent(normalizeMessageContent(message.content)).trim();
|
|
2403
2719
|
}
|
|
2720
|
+
function hasRenderableMessageContent(message) {
|
|
2721
|
+
return Boolean(getMessageText(message)) || getImageParts(message.content).length > 0 || getFileParts(message.content).length > 0;
|
|
2722
|
+
}
|
|
2723
|
+
function getLastContentMessage(messages) {
|
|
2724
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
2725
|
+
if (hasRenderableMessageContent(messages[index])) return messages[index];
|
|
2726
|
+
}
|
|
2727
|
+
return null;
|
|
2728
|
+
}
|
|
2404
2729
|
function findLatestReasoningMessageIndex(messages) {
|
|
2405
2730
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
2406
2731
|
if (messages[index].reasoning) return index;
|
|
2407
2732
|
}
|
|
2408
2733
|
return -1;
|
|
2409
2734
|
}
|
|
2735
|
+
function resolveTurnDisplayMode({
|
|
2736
|
+
isStreaming: _isStreaming,
|
|
2737
|
+
displayMode
|
|
2738
|
+
}) {
|
|
2739
|
+
return displayMode;
|
|
2740
|
+
}
|
|
2741
|
+
function formatExecutionDuration(durationMs) {
|
|
2742
|
+
const totalSeconds = Math.max(0, Math.round(durationMs / 1e3));
|
|
2743
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
2744
|
+
const seconds = totalSeconds % 60;
|
|
2745
|
+
return minutes > 0 ? `${minutes}\u5206${seconds}\u79D2` : `${seconds}\u79D2`;
|
|
2746
|
+
}
|
|
2747
|
+
function getExecutionDurationMs({
|
|
2748
|
+
messages,
|
|
2749
|
+
isStreaming,
|
|
2750
|
+
now = Date.now()
|
|
2751
|
+
}) {
|
|
2752
|
+
const knownDuration = messages.reduce(
|
|
2753
|
+
(total, message) => {
|
|
2754
|
+
if (typeof message.duration_ms === "number" && message.duration_ms > 0) {
|
|
2755
|
+
return total + message.duration_ms;
|
|
2756
|
+
}
|
|
2757
|
+
return total + (message.tool_calls ?? []).reduce(
|
|
2758
|
+
(toolTotal, toolCall) => toolTotal + (typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 ? toolCall.duration_ms : 0),
|
|
2759
|
+
0
|
|
2760
|
+
);
|
|
2761
|
+
},
|
|
2762
|
+
0
|
|
2763
|
+
);
|
|
2764
|
+
if (!isStreaming) return knownDuration;
|
|
2765
|
+
const startedAt = messages.map((message) => message.timestamp ? Date.parse(message.timestamp) : Number.NaN).filter((value) => Number.isFinite(value)).sort((a, b) => a - b)[0];
|
|
2766
|
+
if (startedAt === void 0) return knownDuration;
|
|
2767
|
+
return Math.max(knownDuration, now - startedAt);
|
|
2768
|
+
}
|
|
2769
|
+
function findLastExceptionalEvent(messages) {
|
|
2770
|
+
for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex -= 1) {
|
|
2771
|
+
const messageStatus = messages[messageIndex].status;
|
|
2772
|
+
if (messageStatus === "failed") return { messageIndex, status: "error" };
|
|
2773
|
+
if (messageStatus === "interrupted") return { messageIndex, status: "cancelled" };
|
|
2774
|
+
const toolCalls = messages[messageIndex].tool_calls ?? [];
|
|
2775
|
+
for (let toolIndex = toolCalls.length - 1; toolIndex >= 0; toolIndex -= 1) {
|
|
2776
|
+
const status = toolCalls[toolIndex].status;
|
|
2777
|
+
if (status === "error" || status === "cancelled") {
|
|
2778
|
+
return { messageIndex, status };
|
|
2779
|
+
}
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
return null;
|
|
2783
|
+
}
|
|
2784
|
+
function executionSummaryLabel({
|
|
2785
|
+
messages,
|
|
2786
|
+
isStreaming,
|
|
2787
|
+
durationMs,
|
|
2788
|
+
sessionStatus,
|
|
2789
|
+
askAnswers
|
|
2790
|
+
}) {
|
|
2791
|
+
if (isStreaming) {
|
|
2792
|
+
return durationMs > 0 ? `\u6B63\u5728\u6267\u884C ${formatExecutionDuration(durationMs)}` : "\u6B63\u5728\u6267\u884C";
|
|
2793
|
+
}
|
|
2794
|
+
if (sessionStatus === "waiting_for_input" && messages.some(
|
|
2795
|
+
(message) => (message.tool_calls ?? []).some(
|
|
2796
|
+
(toolCall) => formatToolName(toolCall.name) === "AskUserQuestion" && toolCall.status === "awaiting_answer" && !askAnswers?.[toolCall.id]
|
|
2797
|
+
)
|
|
2798
|
+
)) {
|
|
2799
|
+
return "\u7B49\u5F85\u8F93\u5165";
|
|
2800
|
+
}
|
|
2801
|
+
const completedLabel = durationMs > 0 ? `\u6267\u884C\u5B8C\u6210 ${formatExecutionDuration(durationMs)}` : "\u6267\u884C\u5B8C\u6210";
|
|
2802
|
+
const lastExceptionalEvent = findLastExceptionalEvent(messages);
|
|
2803
|
+
if (lastExceptionalEvent) {
|
|
2804
|
+
const recovered = messages.slice(lastExceptionalEvent.messageIndex + 1).some(hasRenderableMessageContent);
|
|
2805
|
+
if (lastExceptionalEvent.status === "error") {
|
|
2806
|
+
return recovered ? `${completedLabel} \xB7 \u90E8\u5206\u6B65\u9AA4\u5931\u8D25` : "\u6267\u884C\u5931\u8D25";
|
|
2807
|
+
}
|
|
2808
|
+
return recovered ? `${completedLabel} \xB7 \u90E8\u5206\u6B65\u9AA4\u672A\u5B8C\u6210` : "\u6267\u884C\u5DF2\u4E2D\u65AD";
|
|
2809
|
+
}
|
|
2810
|
+
return completedLabel;
|
|
2811
|
+
}
|
|
2812
|
+
function executionToolTypeLabel(toolCall) {
|
|
2813
|
+
switch (formatToolName(toolCall.name)) {
|
|
2814
|
+
case "WebSearch":
|
|
2815
|
+
case "WebFetch":
|
|
2816
|
+
return "\u7F51\u7EDC\u68C0\u7D22";
|
|
2817
|
+
case "Bash":
|
|
2818
|
+
case "BgBash":
|
|
2819
|
+
return "\u547D\u4EE4\u6267\u884C";
|
|
2820
|
+
case "Read":
|
|
2821
|
+
case "ReadSkill":
|
|
2822
|
+
return "\u5185\u5BB9\u8BFB\u53D6";
|
|
2823
|
+
case "Write":
|
|
2824
|
+
case "Edit":
|
|
2825
|
+
case "MultiEdit":
|
|
2826
|
+
return "\u6587\u4EF6\u5904\u7406";
|
|
2827
|
+
case "Grep":
|
|
2828
|
+
case "Glob":
|
|
2829
|
+
return "\u5185\u5BB9\u641C\u7D22";
|
|
2830
|
+
case "Agent":
|
|
2831
|
+
return "\u5B50\u4EFB\u52A1";
|
|
2832
|
+
case "search_skills":
|
|
2833
|
+
return "\u6280\u80FD\u68C0\u7D22";
|
|
2834
|
+
case "get_skill_content":
|
|
2835
|
+
return "\u8BFB\u53D6\u6280\u80FD";
|
|
2836
|
+
case "run_skill_tool":
|
|
2837
|
+
return "\u6267\u884C\u6280\u80FD";
|
|
2838
|
+
default:
|
|
2839
|
+
return toolCall.display_name?.trim() || "\u6267\u884C\u6B65\u9AA4";
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
function executionToolIntent(toolCall) {
|
|
2843
|
+
const normalizedName = formatToolName(toolCall.name);
|
|
2844
|
+
let args = null;
|
|
2845
|
+
try {
|
|
2846
|
+
const parsed = JSON.parse(toolCall.arguments);
|
|
2847
|
+
args = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
2848
|
+
} catch {
|
|
2849
|
+
args = null;
|
|
2850
|
+
}
|
|
2851
|
+
const getString = (key) => {
|
|
2852
|
+
const value = args?.[key];
|
|
2853
|
+
return typeof value === "string" ? value.trim() : "";
|
|
2854
|
+
};
|
|
2855
|
+
const explicitIntent = getString("description") || getString("_meta_display_name") || getString("display_name") || toolCall.display_name?.trim() || "";
|
|
2856
|
+
if (explicitIntent) return explicitIntent;
|
|
2857
|
+
if (normalizedName === "search_skills") return getString("query");
|
|
2858
|
+
if (normalizedName === "get_skill_content" || normalizedName === "ReadSkill") {
|
|
2859
|
+
return getString("skill_name") || getString("skill");
|
|
2860
|
+
}
|
|
2861
|
+
if (normalizedName === "FinishTask") return getString("title");
|
|
2862
|
+
return "";
|
|
2863
|
+
}
|
|
2864
|
+
function ExecutionToolRow({ toolCall }) {
|
|
2865
|
+
const normalizedName = formatToolName(toolCall.name);
|
|
2866
|
+
const typeLabel = executionToolTypeLabel(toolCall);
|
|
2867
|
+
const intent = executionToolIntent(toolCall);
|
|
2868
|
+
const label = intent ? `${typeLabel}\uFF1A${intent}` : typeLabel;
|
|
2869
|
+
const failed = toolCall.status === "error" || toolCall.status === "cancelled";
|
|
2870
|
+
const filePath = toolCall.status === "done" && (normalizedName === "Write" || normalizedName === "Edit" || normalizedName === "MultiEdit") ? extractToolFilePath(toolCall) : null;
|
|
2871
|
+
const [fileOpen, setFileOpen] = useState10(false);
|
|
2872
|
+
const iconClass = cn(
|
|
2873
|
+
"size-3.5 shrink-0",
|
|
2874
|
+
failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
|
|
2875
|
+
);
|
|
2876
|
+
const icon = toolCall.status === "pending" ? /* @__PURE__ */ jsx11(LoaderCircle, { className: cn(iconClass, "animate-spin"), "aria-hidden": "true" }) : toolCall.status === "error" ? /* @__PURE__ */ jsx11(CircleAlert, { className: iconClass, "aria-hidden": "true" }) : toolCall.status === "cancelled" ? /* @__PURE__ */ jsx11(X, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "WebSearch" || normalizedName === "WebFetch" ? /* @__PURE__ */ jsx11(Earth, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Bash" || normalizedName === "BgBash" ? /* @__PURE__ */ jsx11(Terminal, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Write" || normalizedName === "Edit" || normalizedName === "MultiEdit" ? /* @__PURE__ */ jsx11(FilePenLine, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Read" || normalizedName === "ReadSkill" ? /* @__PURE__ */ jsx11(BookOpen, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Grep" || normalizedName === "Glob" || normalizedName === "search_skills" ? /* @__PURE__ */ jsx11(Search, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "get_skill_content" ? /* @__PURE__ */ jsx11(BookOpen, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Agent" ? /* @__PURE__ */ jsx11(Bot, { className: iconClass, "aria-hidden": "true" }) : /* @__PURE__ */ jsx11(Wrench, { className: iconClass, "aria-hidden": "true" });
|
|
2877
|
+
const rowClassName = cn(
|
|
2878
|
+
"flex min-w-0 items-center gap-1 py-1.5 text-xs leading-[22px]",
|
|
2879
|
+
failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
|
|
2880
|
+
);
|
|
2881
|
+
if (!filePath) {
|
|
2882
|
+
return /* @__PURE__ */ jsxs9("div", { "data-testid": "execution-tool-intent", className: rowClassName, title: label, children: [
|
|
2883
|
+
icon,
|
|
2884
|
+
/* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: label })
|
|
2885
|
+
] });
|
|
2886
|
+
}
|
|
2887
|
+
return /* @__PURE__ */ jsxs9("div", { className: "min-w-0", children: [
|
|
2888
|
+
/* @__PURE__ */ jsxs9(
|
|
2889
|
+
"button",
|
|
2890
|
+
{
|
|
2891
|
+
type: "button",
|
|
2892
|
+
"data-testid": "execution-tool-intent",
|
|
2893
|
+
className: cn(rowClassName, "w-full text-left"),
|
|
2894
|
+
title: label,
|
|
2895
|
+
"aria-expanded": fileOpen,
|
|
2896
|
+
onClick: () => setFileOpen((open) => !open),
|
|
2897
|
+
children: [
|
|
2898
|
+
icon,
|
|
2899
|
+
/* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: label }),
|
|
2900
|
+
/* @__PURE__ */ jsx11(
|
|
2901
|
+
ChevronRight,
|
|
2902
|
+
{
|
|
2903
|
+
size: 14,
|
|
2904
|
+
className: cn("shrink-0 transition-transform", fileOpen && "rotate-90"),
|
|
2905
|
+
"aria-hidden": "true"
|
|
2906
|
+
}
|
|
2907
|
+
)
|
|
2908
|
+
]
|
|
2909
|
+
}
|
|
2910
|
+
),
|
|
2911
|
+
fileOpen ? /* @__PURE__ */ jsxs9(
|
|
2912
|
+
"div",
|
|
2913
|
+
{
|
|
2914
|
+
className: "ml-[18px] truncate text-xs leading-[22px] text-[hsl(var(--muted-foreground))]",
|
|
2915
|
+
title: filePath,
|
|
2916
|
+
children: [
|
|
2917
|
+
"\u6587\u4EF6\uFF1A",
|
|
2918
|
+
filePath
|
|
2919
|
+
]
|
|
2920
|
+
}
|
|
2921
|
+
) : null
|
|
2922
|
+
] });
|
|
2923
|
+
}
|
|
2410
2924
|
function AssistantTurnBlock({
|
|
2411
2925
|
messages,
|
|
2412
2926
|
isStreaming = false,
|
|
@@ -2417,53 +2931,235 @@ function AssistantTurnBlock({
|
|
|
2417
2931
|
sessionId
|
|
2418
2932
|
}) {
|
|
2419
2933
|
const hasInterrupted = messages.some((message) => message.status === "interrupted");
|
|
2420
|
-
const
|
|
2421
|
-
(message) =>
|
|
2934
|
+
const hasFailedWithoutContent = messages.some(
|
|
2935
|
+
(message) => message.status === "failed" && !hasRenderableMessageContent(message)
|
|
2936
|
+
);
|
|
2937
|
+
const finalMessage = getLastContentMessage(messages);
|
|
2938
|
+
const hasExecutionProcess = messages.some(
|
|
2939
|
+
(message) => message.reasoning || (message.tool_calls?.length ?? 0) > 0
|
|
2422
2940
|
);
|
|
2423
2941
|
const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
|
|
2942
|
+
const hasActionableToolCall = messages.some(
|
|
2943
|
+
(message) => message.status === "failed" || message.status === "interrupted" || (message.tool_calls ?? []).some(
|
|
2944
|
+
(toolCall) => toolCall.status === "error" || toolCall.status === "cancelled"
|
|
2945
|
+
)
|
|
2946
|
+
);
|
|
2947
|
+
const questionToolCalls = messages.flatMap(
|
|
2948
|
+
(message) => (message.tool_calls ?? []).filter(
|
|
2949
|
+
(toolCall) => formatToolName(toolCall.name) === "AskUserQuestion"
|
|
2950
|
+
)
|
|
2951
|
+
);
|
|
2952
|
+
const [displayMode, setDisplayMode] = useState10(
|
|
2953
|
+
() => isStreaming || hasActionableToolCall ? "detail" : "compact"
|
|
2954
|
+
);
|
|
2955
|
+
const userSelectedDisplayModeRef = useRef9(false);
|
|
2956
|
+
const wasStreamingRef = useRef9(isStreaming);
|
|
2957
|
+
useEffect8(() => {
|
|
2958
|
+
if (wasStreamingRef.current && !isStreaming && !userSelectedDisplayModeRef.current) {
|
|
2959
|
+
setDisplayMode(hasActionableToolCall ? "detail" : "compact");
|
|
2960
|
+
}
|
|
2961
|
+
wasStreamingRef.current = isStreaming;
|
|
2962
|
+
}, [hasActionableToolCall, isStreaming]);
|
|
2963
|
+
const effectiveMode = resolveTurnDisplayMode({ isStreaming, displayMode });
|
|
2964
|
+
const executionDurationMs = getExecutionDurationMs({ messages, isStreaming });
|
|
2965
|
+
const [clock, setClock] = useState10(() => Date.now());
|
|
2966
|
+
const hasLiveStartTime = messages.some(
|
|
2967
|
+
(message) => message.timestamp != null && Number.isFinite(Date.parse(message.timestamp))
|
|
2968
|
+
);
|
|
2969
|
+
useEffect8(() => {
|
|
2970
|
+
if (!isStreaming || !hasLiveStartTime) return;
|
|
2971
|
+
const timer = window.setInterval(() => setClock(Date.now()), 1e3);
|
|
2972
|
+
return () => window.clearInterval(timer);
|
|
2973
|
+
}, [hasLiveStartTime, isStreaming]);
|
|
2974
|
+
const liveExecutionDurationMs = isStreaming ? getExecutionDurationMs({ messages, isStreaming, now: clock }) : executionDurationMs;
|
|
2975
|
+
if (!hasExecutionProcess) {
|
|
2976
|
+
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-assistant-turn flex flex-col gap-3", children: [
|
|
2977
|
+
hasInterrupted && /* @__PURE__ */ jsx11("div", { className: "ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }),
|
|
2978
|
+
hasFailedWithoutContent && /* @__PURE__ */ jsx11("div", { className: "ml-4 w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400", children: "\u751F\u6210\u5931\u8D25" }),
|
|
2979
|
+
messages.map((message, index) => {
|
|
2980
|
+
return hasRenderableMessageContent(message) ? /* @__PURE__ */ jsx11(
|
|
2981
|
+
"div",
|
|
2982
|
+
{
|
|
2983
|
+
className: "flex flex-col gap-3",
|
|
2984
|
+
children: /* @__PURE__ */ jsx11(
|
|
2985
|
+
AssistantMessageContent,
|
|
2986
|
+
{
|
|
2987
|
+
message,
|
|
2988
|
+
sessionId,
|
|
2989
|
+
streaming: isStreaming && index === messages.length - 1
|
|
2990
|
+
}
|
|
2991
|
+
)
|
|
2992
|
+
},
|
|
2993
|
+
message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
|
|
2994
|
+
) : null;
|
|
2995
|
+
}),
|
|
2996
|
+
isStreaming && !finalMessage ? /* @__PURE__ */ jsx11(Shimmer, { className: "ml-4", children: "\u6B63\u5728\u751F\u6210..." }) : null
|
|
2997
|
+
] });
|
|
2998
|
+
}
|
|
2424
2999
|
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-assistant-turn flex flex-col gap-3", children: [
|
|
2425
3000
|
hasInterrupted && /* @__PURE__ */ jsx11("div", { className: "ml-4 w-fit rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }),
|
|
2426
|
-
|
|
3001
|
+
hasFailedWithoutContent && /* @__PURE__ */ jsx11("div", { className: "ml-4 w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400", children: "\u751F\u6210\u5931\u8D25" }),
|
|
3002
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex w-full items-start gap-2.5", children: [
|
|
3003
|
+
/* @__PURE__ */ jsx11(
|
|
3004
|
+
"span",
|
|
3005
|
+
{
|
|
3006
|
+
className: "grid size-[30px] shrink-0 place-items-center rounded-full bg-[hsl(var(--muted)/0.55)] text-[hsl(var(--foreground))]",
|
|
3007
|
+
"aria-hidden": "true",
|
|
3008
|
+
children: /* @__PURE__ */ jsx11(Bot, { size: 16 })
|
|
3009
|
+
}
|
|
3010
|
+
),
|
|
3011
|
+
/* @__PURE__ */ jsxs9("div", { className: "min-w-0 flex-1 pt-0.5", children: [
|
|
3012
|
+
/* @__PURE__ */ jsxs9(
|
|
3013
|
+
"button",
|
|
3014
|
+
{
|
|
3015
|
+
type: "button",
|
|
3016
|
+
onClick: () => {
|
|
3017
|
+
userSelectedDisplayModeRef.current = true;
|
|
3018
|
+
setDisplayMode(displayMode === "detail" ? "compact" : "detail");
|
|
3019
|
+
},
|
|
3020
|
+
"aria-expanded": effectiveMode === "detail",
|
|
3021
|
+
"aria-label": effectiveMode === "detail" ? "\u6536\u8D77\u6267\u884C\u8FC7\u7A0B" : "\u5C55\u5F00\u6267\u884C\u8FC7\u7A0B",
|
|
3022
|
+
"data-testid": "assistant-execution-summary",
|
|
3023
|
+
className: "inline-flex min-w-0 max-w-full select-none items-center gap-1 bg-transparent p-0 text-left text-xs leading-[22px] text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))] focus-visible:ring-1 focus-visible:ring-[hsl(var(--ring))] focus:outline-none",
|
|
3024
|
+
children: [
|
|
3025
|
+
/* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: executionSummaryLabel({
|
|
3026
|
+
messages,
|
|
3027
|
+
isStreaming,
|
|
3028
|
+
durationMs: liveExecutionDurationMs,
|
|
3029
|
+
sessionStatus,
|
|
3030
|
+
askAnswers
|
|
3031
|
+
}) }),
|
|
3032
|
+
/* @__PURE__ */ jsx11(
|
|
3033
|
+
ChevronRight,
|
|
3034
|
+
{
|
|
3035
|
+
size: 14,
|
|
3036
|
+
className: cn(
|
|
3037
|
+
"shrink-0 transition-transform duration-300",
|
|
3038
|
+
effectiveMode === "detail" && "rotate-90"
|
|
3039
|
+
),
|
|
3040
|
+
"aria-hidden": "true"
|
|
3041
|
+
}
|
|
3042
|
+
)
|
|
3043
|
+
]
|
|
3044
|
+
}
|
|
3045
|
+
),
|
|
3046
|
+
/* @__PURE__ */ jsx11("div", { className: "mt-3 h-px w-full bg-[hsl(var(--border)/0.75)]" })
|
|
3047
|
+
] })
|
|
3048
|
+
] }),
|
|
3049
|
+
effectiveMode === "detail" ? /* @__PURE__ */ jsx11("div", { className: "ml-10 flex flex-col gap-3 pt-1", children: messages.map((message, index) => {
|
|
2427
3050
|
const isLast = index === messages.length - 1;
|
|
2428
3051
|
const streamingThis = isStreaming && isLast;
|
|
2429
3052
|
const text = getMessageText(message);
|
|
2430
|
-
const toolCalls = message.tool_calls ?? []
|
|
3053
|
+
const toolCalls = (message.tool_calls ?? []).filter(
|
|
3054
|
+
(toolCall) => formatToolName(toolCall.name) !== "AskUserQuestion"
|
|
3055
|
+
);
|
|
2431
3056
|
const showReasoning = !!message.reasoning && (!isStreaming || index === latestReasoningIndex);
|
|
2432
3057
|
return /* @__PURE__ */ jsxs9(
|
|
2433
3058
|
"div",
|
|
2434
3059
|
{
|
|
2435
3060
|
className: "flex flex-col gap-3",
|
|
2436
3061
|
children: [
|
|
2437
|
-
showReasoning && message.reasoning
|
|
2438
|
-
|
|
2439
|
-
|
|
3062
|
+
showReasoning && message.reasoning ? /* @__PURE__ */ jsx11(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }) : null,
|
|
3063
|
+
hasRenderableMessageContent(message) && message !== finalMessage ? /* @__PURE__ */ jsx11(
|
|
3064
|
+
AssistantMessageContent,
|
|
2440
3065
|
{
|
|
2441
|
-
|
|
2442
|
-
className: "blade-chat-prose",
|
|
3066
|
+
message,
|
|
2443
3067
|
sessionId,
|
|
2444
|
-
|
|
3068
|
+
streaming: streamingThis,
|
|
3069
|
+
compact: true
|
|
2445
3070
|
}
|
|
2446
|
-
)
|
|
2447
|
-
toolCalls.length > 0
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
toolCall,
|
|
2452
|
-
answerData: askAnswers?.[toolCall.id],
|
|
2453
|
-
onAnswer,
|
|
2454
|
-
answered: sessionStatus !== "waiting_for_input",
|
|
2455
|
-
sessionStatus,
|
|
2456
|
-
renderer: toolCallRenderer
|
|
2457
|
-
},
|
|
2458
|
-
toolCall.id
|
|
2459
|
-
)
|
|
2460
|
-
) })
|
|
3071
|
+
) : null,
|
|
3072
|
+
toolCalls.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-0.5", children: toolCalls.map((toolCall) => {
|
|
3073
|
+
const custom = toolCallRenderer?.(toolCall);
|
|
3074
|
+
return custom !== null && custom !== void 0 ? /* @__PURE__ */ jsx11("div", { children: custom }, toolCall.id) : formatToolName(toolCall.name) === "Agent" ? /* @__PURE__ */ jsx11(AgentLoopBlock, { toolCall }, toolCall.id) : /* @__PURE__ */ jsx11(ExecutionToolRow, { toolCall }, toolCall.id);
|
|
3075
|
+
}) }) : null
|
|
2461
3076
|
]
|
|
2462
3077
|
},
|
|
2463
3078
|
message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
|
|
2464
3079
|
);
|
|
2465
|
-
}),
|
|
2466
|
-
|
|
3080
|
+
}) }) : null,
|
|
3081
|
+
finalMessage ? /* @__PURE__ */ jsx11("div", { className: "ml-10", children: /* @__PURE__ */ jsx11(
|
|
3082
|
+
AssistantMessageContent,
|
|
3083
|
+
{
|
|
3084
|
+
message: finalMessage,
|
|
3085
|
+
sessionId,
|
|
3086
|
+
streaming: isStreaming && finalMessage === messages[messages.length - 1]
|
|
3087
|
+
}
|
|
3088
|
+
) }) : null,
|
|
3089
|
+
questionToolCalls.map((toolCall) => /* @__PURE__ */ jsx11(
|
|
3090
|
+
ToolCallBlock,
|
|
3091
|
+
{
|
|
3092
|
+
toolCall,
|
|
3093
|
+
answerData: askAnswers?.[toolCall.id],
|
|
3094
|
+
onAnswer,
|
|
3095
|
+
answered: sessionStatus !== "waiting_for_input",
|
|
3096
|
+
sessionStatus,
|
|
3097
|
+
renderer: toolCallRenderer
|
|
3098
|
+
},
|
|
3099
|
+
toolCall.id
|
|
3100
|
+
))
|
|
3101
|
+
] });
|
|
3102
|
+
}
|
|
3103
|
+
function AssistantMessageContent({
|
|
3104
|
+
message,
|
|
3105
|
+
sessionId,
|
|
3106
|
+
streaming,
|
|
3107
|
+
compact = false
|
|
3108
|
+
}) {
|
|
3109
|
+
const text = getMessageText(message);
|
|
3110
|
+
const imageParts = getImageParts(message.content);
|
|
3111
|
+
const fileParts = getFileParts(message.content);
|
|
3112
|
+
const failed = message.status === "failed";
|
|
3113
|
+
const failedBadge = failed ? /* @__PURE__ */ jsx11("div", { className: "w-fit rounded-full border border-red-500/30 bg-red-500/10 px-2.5 py-1 text-[10px] font-medium text-red-400", children: "\u751F\u6210\u5931\u8D25" }) : null;
|
|
3114
|
+
const textContent = text ? /* @__PURE__ */ jsx11(
|
|
3115
|
+
"div",
|
|
3116
|
+
{
|
|
3117
|
+
className: cn(
|
|
3118
|
+
"blade-chat-assistant-text",
|
|
3119
|
+
compact ? "text-xs leading-[22px] text-[hsl(var(--foreground))]" : "text-[15px] leading-8 text-[hsl(var(--foreground))]"
|
|
3120
|
+
),
|
|
3121
|
+
children: /* @__PURE__ */ jsx11(
|
|
3122
|
+
MarkdownContent,
|
|
3123
|
+
{
|
|
3124
|
+
mode: streaming ? "streaming" : "static",
|
|
3125
|
+
className: "blade-chat-prose",
|
|
3126
|
+
sessionId,
|
|
3127
|
+
children: text
|
|
3128
|
+
}
|
|
3129
|
+
)
|
|
3130
|
+
}
|
|
3131
|
+
) : null;
|
|
3132
|
+
if (imageParts.length === 0 && fileParts.length === 0) {
|
|
3133
|
+
if (!failed) return textContent;
|
|
3134
|
+
return failedBadge || textContent ? /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-2", children: [
|
|
3135
|
+
failedBadge,
|
|
3136
|
+
textContent
|
|
3137
|
+
] }) : null;
|
|
3138
|
+
}
|
|
3139
|
+
return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3", children: [
|
|
3140
|
+
failedBadge,
|
|
3141
|
+
imageParts.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx11(
|
|
3142
|
+
"img",
|
|
3143
|
+
{
|
|
3144
|
+
src: part.image_url.url,
|
|
3145
|
+
alt: "\u6D88\u606F\u9644\u4EF6",
|
|
3146
|
+
className: "max-h-72 rounded-xl border border-[hsl(var(--border))] object-cover"
|
|
3147
|
+
},
|
|
3148
|
+
part.image_url.url
|
|
3149
|
+
)) }) : null,
|
|
3150
|
+
fileParts.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "flex flex-wrap gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs9(
|
|
3151
|
+
"div",
|
|
3152
|
+
{
|
|
3153
|
+
className: "flex min-w-0 items-center gap-1.5 rounded-lg border border-[hsl(var(--border))] bg-[hsl(var(--muted)/0.3)] px-2.5 py-1.5 text-xs text-[hsl(var(--muted-foreground))]",
|
|
3154
|
+
title: part.name,
|
|
3155
|
+
children: [
|
|
3156
|
+
/* @__PURE__ */ jsx11(FileText, { size: 12, className: "shrink-0" }),
|
|
3157
|
+
/* @__PURE__ */ jsx11("span", { className: "max-w-56 truncate", children: part.name })
|
|
3158
|
+
]
|
|
3159
|
+
},
|
|
3160
|
+
`${part.name}-${part.data.slice(0, 32)}`
|
|
3161
|
+
)) }) : null,
|
|
3162
|
+
textContent
|
|
2467
3163
|
] });
|
|
2468
3164
|
}
|
|
2469
3165
|
|
|
@@ -2560,7 +3256,7 @@ var RenderErrorBoundary = class extends Component {
|
|
|
2560
3256
|
};
|
|
2561
3257
|
|
|
2562
3258
|
// src/components/PostChatFollowupBlock.tsx
|
|
2563
|
-
import { useCallback as
|
|
3259
|
+
import { useCallback as useCallback6, useEffect as useEffect9, useRef as useRef10, useState as useState11 } from "react";
|
|
2564
3260
|
import { Fragment as Fragment2, jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2565
3261
|
function emitInteraction(callback, event) {
|
|
2566
3262
|
try {
|
|
@@ -2691,10 +3387,10 @@ function ResultFeedback({
|
|
|
2691
3387
|
const [reason, setReason] = useState11(savedFeedback?.reason ?? null);
|
|
2692
3388
|
const [saving, setSaving] = useState11(false);
|
|
2693
3389
|
const [saveError, setSaveError] = useState11(false);
|
|
2694
|
-
const reportedShown =
|
|
2695
|
-
const latestChoice =
|
|
3390
|
+
const reportedShown = useRef10(false);
|
|
3391
|
+
const latestChoice = useRef10(null);
|
|
2696
3392
|
const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer;
|
|
2697
|
-
|
|
3393
|
+
useEffect9(() => {
|
|
2698
3394
|
if (!eligible || reportedShown.current) return;
|
|
2699
3395
|
reportedShown.current = true;
|
|
2700
3396
|
emitInteraction(onInteraction, {
|
|
@@ -2703,13 +3399,13 @@ function ResultFeedback({
|
|
|
2703
3399
|
assistantEntryId: followup.assistant_entry_id
|
|
2704
3400
|
});
|
|
2705
3401
|
}, [eligible, followup.assistant_entry_id, onInteraction, sessionId]);
|
|
2706
|
-
|
|
3402
|
+
useEffect9(() => {
|
|
2707
3403
|
if (!savedFeedback || latestChoice.current) return;
|
|
2708
3404
|
setSaved(savedFeedback);
|
|
2709
3405
|
setHelpful(savedFeedback.helpful);
|
|
2710
3406
|
setReason(savedFeedback.reason);
|
|
2711
3407
|
}, [savedFeedback]);
|
|
2712
|
-
const submit =
|
|
3408
|
+
const submit = useCallback6(
|
|
2713
3409
|
async (nextHelpful, nextReason) => {
|
|
2714
3410
|
if (!sessionId) return;
|
|
2715
3411
|
const choice = { helpful: nextHelpful, reason: nextReason };
|
|
@@ -2816,13 +3512,13 @@ function PostChatFollowupBlock({
|
|
|
2816
3512
|
onFeedbackSaved
|
|
2817
3513
|
}) {
|
|
2818
3514
|
const [expanded, setExpanded] = useState11(false);
|
|
2819
|
-
const adopted =
|
|
2820
|
-
const reportedSuggestions =
|
|
2821
|
-
const reportedArtifacts =
|
|
2822
|
-
const openedArtifacts =
|
|
3515
|
+
const adopted = useRef10(/* @__PURE__ */ new Set());
|
|
3516
|
+
const reportedSuggestions = useRef10(false);
|
|
3517
|
+
const reportedArtifacts = useRef10(/* @__PURE__ */ new Set());
|
|
3518
|
+
const openedArtifacts = useRef10(/* @__PURE__ */ new Set());
|
|
2823
3519
|
const artifacts = followup.final_artifacts ?? [];
|
|
2824
3520
|
const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3);
|
|
2825
|
-
|
|
3521
|
+
useEffect9(() => {
|
|
2826
3522
|
if (!reportedSuggestions.current && followup.suggestions.length > 0) {
|
|
2827
3523
|
reportedSuggestions.current = true;
|
|
2828
3524
|
emitInteraction(onInteraction, {
|
|
@@ -2852,7 +3548,7 @@ function PostChatFollowupBlock({
|
|
|
2852
3548
|
sessionId,
|
|
2853
3549
|
visibleArtifacts
|
|
2854
3550
|
]);
|
|
2855
|
-
const reportArtifactOpened =
|
|
3551
|
+
const reportArtifactOpened = useCallback6(
|
|
2856
3552
|
(artifactIndex, artifactKind) => {
|
|
2857
3553
|
if (openedArtifacts.current.has(artifactIndex)) return;
|
|
2858
3554
|
openedArtifacts.current.add(artifactIndex);
|
|
@@ -2946,7 +3642,7 @@ function PostChatFollowupBlock({
|
|
|
2946
3642
|
}
|
|
2947
3643
|
|
|
2948
3644
|
// src/components/UserMessageBubble.tsx
|
|
2949
|
-
import { getFileParts, getImageParts, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
|
|
3645
|
+
import { getFileParts as getFileParts2, getImageParts as getImageParts2, getTextContent as getTextContent2 } from "@blade-hq/agent-client";
|
|
2950
3646
|
import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2951
3647
|
function isUserMessage(message) {
|
|
2952
3648
|
return message.role === "user";
|
|
@@ -2957,8 +3653,8 @@ function isErrorMessage(message) {
|
|
|
2957
3653
|
var isSending = (message) => message.status === "streaming";
|
|
2958
3654
|
function UserMessageBubble({ message, className }) {
|
|
2959
3655
|
const text = getTextContent2(message.content).trim();
|
|
2960
|
-
const fileParts =
|
|
2961
|
-
const imageParts =
|
|
3656
|
+
const fileParts = getFileParts2(message.content);
|
|
3657
|
+
const imageParts = getImageParts2(message.content);
|
|
2962
3658
|
return /* @__PURE__ */ jsx15("div", { className: cn("blade-chat-user-row flex justify-end", className), children: /* @__PURE__ */ jsxs13("div", { className: "blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3", children: [
|
|
2963
3659
|
imageParts.length > 0 && /* @__PURE__ */ jsx15("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx15(
|
|
2964
3660
|
"img",
|
|
@@ -3045,6 +3741,9 @@ function MessageList({
|
|
|
3045
3741
|
resultFeedbackByEntry = /* @__PURE__ */ new Map(),
|
|
3046
3742
|
onResultFeedbackSaved
|
|
3047
3743
|
}) {
|
|
3744
|
+
const userMessages = messages.filter((message) => isUserMessage(message));
|
|
3745
|
+
const latestUserMessage = userMessages.at(-1);
|
|
3746
|
+
const shouldPinLatestUser = latestUserMessage != null && (latestUserMessage.entry_id == null || latestUserMessage.entry_id.startsWith("local-user-"));
|
|
3048
3747
|
const renderBlocks = useMemo7(() => {
|
|
3049
3748
|
const visible = messages.filter((message) => {
|
|
3050
3749
|
if ((message.loop_name ?? "root") !== "root") return false;
|
|
@@ -3128,101 +3827,151 @@ function MessageList({
|
|
|
3128
3827
|
}
|
|
3129
3828
|
return blocks;
|
|
3130
3829
|
}, [messages, isStreaming]);
|
|
3131
|
-
return /* @__PURE__ */ jsx16("div", { className: cn("blade-chat-messages relative min-h-0 flex-1", className), children: /* @__PURE__ */ jsxs14(
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
postChatFollowup && block.messages.some(
|
|
3147
|
-
(message) => message.entry_id === postChatFollowup.assistant_entry_id
|
|
3148
|
-
)
|
|
3149
|
-
);
|
|
3150
|
-
return /* @__PURE__ */ jsx16("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs14(
|
|
3151
|
-
RenderErrorBoundary,
|
|
3152
|
-
{
|
|
3153
|
-
label: "\u52A9\u624B\u6D88\u606F",
|
|
3154
|
-
details: block.key,
|
|
3155
|
-
resetKey: getMessageResetSignature(block.messages),
|
|
3156
|
-
children: [
|
|
3157
|
-
/* @__PURE__ */ jsx16(
|
|
3158
|
-
AssistantTurnBlock,
|
|
3159
|
-
{
|
|
3160
|
-
messages: block.messages,
|
|
3161
|
-
isStreaming: block.isStreaming,
|
|
3162
|
-
askAnswers,
|
|
3163
|
-
onAnswer,
|
|
3164
|
-
sessionStatus,
|
|
3165
|
-
toolCallRenderer,
|
|
3166
|
-
sessionId
|
|
3167
|
-
}
|
|
3168
|
-
),
|
|
3169
|
-
blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx16(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
|
|
3170
|
-
hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx16(
|
|
3171
|
-
PostChatFollowupBlock,
|
|
3172
|
-
{
|
|
3173
|
-
followup: postChatFollowup,
|
|
3174
|
-
sessionId,
|
|
3175
|
-
onSuggestion,
|
|
3176
|
-
isViewer,
|
|
3177
|
-
onInteraction: onFollowupInteraction,
|
|
3178
|
-
savedFeedback: blockFeedback,
|
|
3179
|
-
onFeedbackSaved: onResultFeedbackSaved
|
|
3180
|
-
}
|
|
3181
|
-
) : null
|
|
3182
|
-
]
|
|
3830
|
+
return /* @__PURE__ */ jsx16("div", { className: cn("blade-chat-messages relative min-h-0 flex-1", className), children: /* @__PURE__ */ jsxs14(
|
|
3831
|
+
StickToBottom,
|
|
3832
|
+
{
|
|
3833
|
+
className: "h-full overflow-y-hidden",
|
|
3834
|
+
initial: "instant",
|
|
3835
|
+
resize: "instant",
|
|
3836
|
+
children: [
|
|
3837
|
+
/* @__PURE__ */ jsx16(StickToBottom.Content, { className: "blade-chat-messages-scroll", children: /* @__PURE__ */ jsx16("div", { className: "blade-chat-messages-content mx-auto max-w-[748px]", children: /* @__PURE__ */ jsxs14("div", { className: "flex min-w-0 flex-col", children: [
|
|
3838
|
+
renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs14("div", { className: "blade-chat-empty", children: [
|
|
3839
|
+
/* @__PURE__ */ jsx16(MessageSquare, { size: 40, strokeWidth: 1.5 }),
|
|
3840
|
+
/* @__PURE__ */ jsx16("span", { className: "text-base font-medium", children: "\u5F00\u59CB\u5BF9\u8BDD" }),
|
|
3841
|
+
/* @__PURE__ */ jsx16("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
|
|
3842
|
+
] }) : renderBlocks.map((block) => {
|
|
3843
|
+
if (block.type === "message") {
|
|
3844
|
+
return /* @__PURE__ */ jsx16("div", { "data-entry-id": block.message.entry_id, children: isUserMessage(block.message) ? /* @__PURE__ */ jsx16(UserMessageBubble, { message: block.message }) : isErrorMessage(block.message) ? /* @__PURE__ */ jsx16(ErrorMessageBlock, { message: block.message }) : null }, block.key);
|
|
3183
3845
|
}
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3846
|
+
if (block.type === "assistant_turn") {
|
|
3847
|
+
const blockFeedback = block.messages.map(
|
|
3848
|
+
(message) => message.entry_id ? resultFeedbackByEntry.get(message.entry_id) : void 0
|
|
3849
|
+
).find((feedback) => feedback != null);
|
|
3850
|
+
const hasActiveFollowup = Boolean(
|
|
3851
|
+
postChatFollowup && block.messages.some(
|
|
3852
|
+
(message) => message.entry_id === postChatFollowup.assistant_entry_id
|
|
3853
|
+
)
|
|
3854
|
+
);
|
|
3855
|
+
return /* @__PURE__ */ jsx16("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs14(
|
|
3856
|
+
RenderErrorBoundary,
|
|
3857
|
+
{
|
|
3858
|
+
label: "\u52A9\u624B\u6D88\u606F",
|
|
3859
|
+
details: block.key,
|
|
3860
|
+
resetKey: getMessageResetSignature(block.messages),
|
|
3861
|
+
children: [
|
|
3862
|
+
/* @__PURE__ */ jsx16(
|
|
3863
|
+
AssistantTurnBlock,
|
|
3864
|
+
{
|
|
3865
|
+
messages: block.messages,
|
|
3866
|
+
isStreaming: block.isStreaming,
|
|
3867
|
+
askAnswers,
|
|
3868
|
+
onAnswer,
|
|
3869
|
+
sessionStatus,
|
|
3870
|
+
toolCallRenderer,
|
|
3871
|
+
sessionId
|
|
3872
|
+
}
|
|
3873
|
+
),
|
|
3874
|
+
blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx16(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
|
|
3875
|
+
hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx16(
|
|
3876
|
+
PostChatFollowupBlock,
|
|
3877
|
+
{
|
|
3878
|
+
followup: postChatFollowup,
|
|
3879
|
+
sessionId,
|
|
3880
|
+
onSuggestion,
|
|
3881
|
+
isViewer,
|
|
3882
|
+
onInteraction: onFollowupInteraction,
|
|
3883
|
+
savedFeedback: blockFeedback,
|
|
3884
|
+
onFeedbackSaved: onResultFeedbackSaved
|
|
3885
|
+
}
|
|
3886
|
+
) : null
|
|
3887
|
+
]
|
|
3888
|
+
}
|
|
3889
|
+
) }, block.key);
|
|
3890
|
+
}
|
|
3891
|
+
if (block.type === "context") {
|
|
3892
|
+
return /* @__PURE__ */ jsx16("div", { "data-entry-id": block.message.entry_id, children: /* @__PURE__ */ jsx16(ContextCard, { context: block.message.context }) }, block.key);
|
|
3893
|
+
}
|
|
3894
|
+
if (block.type === "compaction") {
|
|
3895
|
+
return /* @__PURE__ */ jsxs14(
|
|
3896
|
+
"div",
|
|
3897
|
+
{
|
|
3898
|
+
className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]",
|
|
3899
|
+
children: [
|
|
3900
|
+
/* @__PURE__ */ jsx16(Layers, { size: 12 }),
|
|
3901
|
+
/* @__PURE__ */ jsx16("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
|
|
3902
|
+
]
|
|
3903
|
+
},
|
|
3904
|
+
block.key
|
|
3905
|
+
);
|
|
3906
|
+
}
|
|
3907
|
+
return /* @__PURE__ */ jsx16(PlanningDivider, { kind: block.kind }, block.key);
|
|
3908
|
+
}),
|
|
3909
|
+
sessionStatus === "interrupted" && !isStreaming ? /* @__PURE__ */ jsx16("div", { className: "flex", children: /* @__PURE__ */ jsx16("div", { className: "rounded-full border border-amber-500/30 bg-amber-500/10 px-2.5 py-1 text-[10px] font-medium uppercase tracking-[0.12em] text-amber-300", children: "\u5DF2\u4E2D\u65AD" }) }) : null
|
|
3910
|
+
] }) }) }),
|
|
3911
|
+
/* @__PURE__ */ jsx16(
|
|
3912
|
+
PinLatestUserMessage,
|
|
3913
|
+
{
|
|
3914
|
+
userMessageCount: userMessages.length,
|
|
3915
|
+
shouldPinLatestUser,
|
|
3916
|
+
targetKey: latestUserMessage?.render_id ?? latestUserMessage?.entry_id ?? (latestUserMessage ? `user:${userMessages.length}` : null)
|
|
3917
|
+
},
|
|
3918
|
+
sessionId ?? "no-session"
|
|
3919
|
+
),
|
|
3920
|
+
/* @__PURE__ */ jsx16(ScrollToBottomButton, {})
|
|
3921
|
+
]
|
|
3922
|
+
},
|
|
3923
|
+
sessionId ?? "no-session"
|
|
3924
|
+
) });
|
|
3209
3925
|
}
|
|
3210
|
-
function
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3926
|
+
function PinLatestUserMessage({
|
|
3927
|
+
userMessageCount,
|
|
3928
|
+
shouldPinLatestUser,
|
|
3929
|
+
targetKey
|
|
3930
|
+
}) {
|
|
3931
|
+
const { contentRef, scrollRef, scrollToBottom, stopScroll } = useStickToBottomContext();
|
|
3932
|
+
const previousCountRef = useRef11(userMessageCount);
|
|
3933
|
+
const spacerHeightRef = useRef11(0);
|
|
3934
|
+
const getScrollElement = useCallback7(() => scrollRef.current, [scrollRef]);
|
|
3935
|
+
const getContentElement = useCallback7(() => contentRef.current, [contentRef]);
|
|
3936
|
+
const getTargetElement = useCallback7(() => {
|
|
3937
|
+
const rows = contentRef.current?.querySelectorAll(".blade-chat-user-row");
|
|
3938
|
+
return rows?.item((rows?.length ?? 0) - 1) ?? null;
|
|
3939
|
+
}, [contentRef]);
|
|
3940
|
+
const getSpacerHeight = useCallback7(() => spacerHeightRef.current, []);
|
|
3941
|
+
const setSpacerHeight = useCallback7(
|
|
3942
|
+
(height) => {
|
|
3943
|
+
spacerHeightRef.current = height;
|
|
3944
|
+
const content = contentRef.current;
|
|
3945
|
+
if (!content) return;
|
|
3946
|
+
if (height > 0) content.style.setProperty("--blade-chat-pin-spacer", `${height}px`);
|
|
3947
|
+
else content.style.removeProperty("--blade-chat-pin-spacer");
|
|
3948
|
+
},
|
|
3949
|
+
[contentRef]
|
|
3950
|
+
);
|
|
3951
|
+
useMessagePin({
|
|
3952
|
+
targetKey,
|
|
3953
|
+
pinTarget: shouldPinLatestUser,
|
|
3954
|
+
getScrollElement,
|
|
3955
|
+
getContentElement,
|
|
3956
|
+
getTargetElement,
|
|
3957
|
+
getSpacerHeight,
|
|
3958
|
+
setSpacerHeight,
|
|
3959
|
+
stopAutoScroll: stopScroll,
|
|
3960
|
+
scrollToBottom
|
|
3961
|
+
});
|
|
3962
|
+
useEffect10(() => {
|
|
3963
|
+
if (userMessageCount > previousCountRef.current && !shouldPinLatestUser) {
|
|
3215
3964
|
scrollToBottom("instant");
|
|
3216
3965
|
}
|
|
3217
3966
|
previousCountRef.current = userMessageCount;
|
|
3218
|
-
}, [
|
|
3967
|
+
}, [scrollToBottom, shouldPinLatestUser, userMessageCount]);
|
|
3219
3968
|
return null;
|
|
3220
3969
|
}
|
|
3221
3970
|
function ScrollToBottomButton() {
|
|
3222
3971
|
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
3223
3972
|
const [visible, setVisible] = useState12(false);
|
|
3224
|
-
const hideTimerRef =
|
|
3225
|
-
|
|
3973
|
+
const hideTimerRef = useRef11(null);
|
|
3974
|
+
useEffect10(() => {
|
|
3226
3975
|
if (isAtBottom) {
|
|
3227
3976
|
if (!hideTimerRef.current) {
|
|
3228
3977
|
hideTimerRef.current = setTimeout(() => {
|
|
@@ -3244,7 +3993,7 @@ function ScrollToBottomButton() {
|
|
|
3244
3993
|
}
|
|
3245
3994
|
};
|
|
3246
3995
|
}, [isAtBottom]);
|
|
3247
|
-
const handleClick =
|
|
3996
|
+
const handleClick = useCallback7(() => {
|
|
3248
3997
|
if (hideTimerRef.current) {
|
|
3249
3998
|
clearTimeout(hideTimerRef.current);
|
|
3250
3999
|
hideTimerRef.current = null;
|
|
@@ -3454,7 +4203,7 @@ function ChatSessionView({
|
|
|
3454
4203
|
const [resultFeedback, setResultFeedback] = useState13([]);
|
|
3455
4204
|
const resolvedSessionId = session?.sessionId;
|
|
3456
4205
|
const isViewer = state?.viewerRole === "viewer";
|
|
3457
|
-
|
|
4206
|
+
useEffect11(() => {
|
|
3458
4207
|
setResultFeedback([]);
|
|
3459
4208
|
if (!resolvedSessionId || isViewer) return;
|
|
3460
4209
|
let cancelled = false;
|
|
@@ -3483,18 +4232,18 @@ function ChatSessionView({
|
|
|
3483
4232
|
() => new Map(resultFeedback.map((item) => [item.assistant_entry_id, item])),
|
|
3484
4233
|
[resultFeedback]
|
|
3485
4234
|
);
|
|
3486
|
-
const handleResultFeedbackSaved =
|
|
4235
|
+
const handleResultFeedbackSaved = useCallback8((saved) => {
|
|
3487
4236
|
setResultFeedback((current) => [
|
|
3488
4237
|
...current.filter((item) => item.assistant_entry_id !== saved.assistant_entry_id),
|
|
3489
4238
|
saved
|
|
3490
4239
|
]);
|
|
3491
4240
|
}, []);
|
|
3492
|
-
|
|
4241
|
+
useEffect11(() => {
|
|
3493
4242
|
if (session) {
|
|
3494
4243
|
onSessionReady?.(session);
|
|
3495
4244
|
}
|
|
3496
4245
|
}, [session, onSessionReady]);
|
|
3497
|
-
|
|
4246
|
+
useEffect11(() => {
|
|
3498
4247
|
if (!session) return;
|
|
3499
4248
|
const offAttach = session.on("attachRequested", ({ label, content }) => {
|
|
3500
4249
|
setInputText((prev) => `${prev ? `${prev}
|
|
@@ -3510,12 +4259,12 @@ ${content}`);
|
|
|
3510
4259
|
offInsert();
|
|
3511
4260
|
};
|
|
3512
4261
|
}, [session]);
|
|
3513
|
-
|
|
4262
|
+
useEffect11(() => {
|
|
3514
4263
|
if (isUnauthorizedError(error)) {
|
|
3515
4264
|
onUnauthorized();
|
|
3516
4265
|
}
|
|
3517
4266
|
}, [error, onUnauthorized]);
|
|
3518
|
-
|
|
4267
|
+
useEffect11(() => {
|
|
3519
4268
|
if (!session || !commands) return;
|
|
3520
4269
|
const unsubscribes = Object.entries(commands).map(
|
|
3521
4270
|
([action, handler]) => session.onCommand(action, (payload) => handler(payload))
|
|
@@ -3586,7 +4335,7 @@ ${content}`);
|
|
|
3586
4335
|
}
|
|
3587
4336
|
|
|
3588
4337
|
// src/components/LlmChat.tsx
|
|
3589
|
-
import { useEffect as
|
|
4338
|
+
import { useEffect as useEffect12, useMemo as useMemo9, useState as useState15 } from "react";
|
|
3590
4339
|
|
|
3591
4340
|
// src/components/LlmAdvancedSettings.tsx
|
|
3592
4341
|
import { useState as useState14 } from "react";
|
|
@@ -3734,7 +4483,7 @@ ${text}` : text),
|
|
|
3734
4483
|
}),
|
|
3735
4484
|
[send, reset]
|
|
3736
4485
|
);
|
|
3737
|
-
|
|
4486
|
+
useEffect12(() => {
|
|
3738
4487
|
onReady?.(handle);
|
|
3739
4488
|
}, [handle, onReady]);
|
|
3740
4489
|
return /* @__PURE__ */ jsx20(
|
|
@@ -3817,6 +4566,7 @@ export {
|
|
|
3817
4566
|
useAgentSession,
|
|
3818
4567
|
useBladeClient,
|
|
3819
4568
|
useLlmChat,
|
|
4569
|
+
useMessagePin,
|
|
3820
4570
|
useReplay
|
|
3821
4571
|
};
|
|
3822
4572
|
/*! Bundled license information:
|
|
@@ -3828,13 +4578,15 @@ lucide-react/dist/esm/createLucideIcon.js:
|
|
|
3828
4578
|
lucide-react/dist/esm/icons/arrow-right.js:
|
|
3829
4579
|
lucide-react/dist/esm/icons/arrow-up-right.js:
|
|
3830
4580
|
lucide-react/dist/esm/icons/arrow-up.js:
|
|
4581
|
+
lucide-react/dist/esm/icons/book-open.js:
|
|
3831
4582
|
lucide-react/dist/esm/icons/bot.js:
|
|
3832
|
-
lucide-react/dist/esm/icons/brain.js:
|
|
3833
4583
|
lucide-react/dist/esm/icons/check.js:
|
|
3834
4584
|
lucide-react/dist/esm/icons/chevron-down.js:
|
|
3835
4585
|
lucide-react/dist/esm/icons/chevron-right.js:
|
|
3836
4586
|
lucide-react/dist/esm/icons/circle-alert.js:
|
|
3837
4587
|
lucide-react/dist/esm/icons/copy.js:
|
|
4588
|
+
lucide-react/dist/esm/icons/earth.js:
|
|
4589
|
+
lucide-react/dist/esm/icons/file-pen-line.js:
|
|
3838
4590
|
lucide-react/dist/esm/icons/file-text.js:
|
|
3839
4591
|
lucide-react/dist/esm/icons/globe.js:
|
|
3840
4592
|
lucide-react/dist/esm/icons/layers.js:
|
|
@@ -3844,10 +4596,13 @@ lucide-react/dist/esm/icons/lock-keyhole.js:
|
|
|
3844
4596
|
lucide-react/dist/esm/icons/message-square-more.js:
|
|
3845
4597
|
lucide-react/dist/esm/icons/message-square.js:
|
|
3846
4598
|
lucide-react/dist/esm/icons/play.js:
|
|
4599
|
+
lucide-react/dist/esm/icons/search.js:
|
|
3847
4600
|
lucide-react/dist/esm/icons/settings-2.js:
|
|
3848
4601
|
lucide-react/dist/esm/icons/sparkles.js:
|
|
3849
4602
|
lucide-react/dist/esm/icons/square.js:
|
|
4603
|
+
lucide-react/dist/esm/icons/terminal.js:
|
|
3850
4604
|
lucide-react/dist/esm/icons/triangle-alert.js:
|
|
4605
|
+
lucide-react/dist/esm/icons/wrench.js:
|
|
3851
4606
|
lucide-react/dist/esm/icons/x.js:
|
|
3852
4607
|
lucide-react/dist/esm/lucide-react.js:
|
|
3853
4608
|
(**
|