@blade-hq/agent-react 2610.0.0-beta.3 → 2610.0.0-beta.30
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 +38 -2
- package/dist/components/AgentLoopBlock.d.ts +1 -4
- package/dist/components/AskUserQuestionBlock.d.ts +9 -0
- package/dist/components/AssistantTurnBlock.d.ts +17 -5
- package/dist/components/ConnectionBanner.d.ts +1 -1
- package/dist/components/ContextCard.d.ts +7 -0
- package/dist/components/ToolCallBlock.d.ts +4 -2
- package/dist/components/display-utils.d.ts +8 -0
- package/dist/embed/entry.d.ts +12 -0
- package/dist/hooks/use-message-pin.d.ts +22 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1420 -392
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-computer-command.d.ts +34 -0
- package/dist/style.css +77 -1
- package/dist/style.full.css +78 -2
- package/package.json +2 -2
- package/public-api.md +265 -4
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,11 +906,37 @@ 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
|
|
|
700
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/
|
|
701
|
-
var
|
|
702
|
-
["path", { d: "M21
|
|
703
|
-
[
|
|
704
|
-
|
|
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" }]
|
|
705
940
|
]);
|
|
706
941
|
|
|
707
942
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file-text.js
|
|
@@ -713,24 +948,6 @@ var FileText = createLucideIcon("FileText", [
|
|
|
713
948
|
["path", { d: "M16 17H8", key: "z1uh3a" }]
|
|
714
949
|
]);
|
|
715
950
|
|
|
716
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/file.js
|
|
717
|
-
var File = createLucideIcon("File", [
|
|
718
|
-
["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
|
|
719
|
-
["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }]
|
|
720
|
-
]);
|
|
721
|
-
|
|
722
|
-
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/film.js
|
|
723
|
-
var Film = createLucideIcon("Film", [
|
|
724
|
-
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
|
|
725
|
-
["path", { d: "M7 3v18", key: "bbkbws" }],
|
|
726
|
-
["path", { d: "M3 7.5h4", key: "zfgn84" }],
|
|
727
|
-
["path", { d: "M3 12h18", key: "1i2n21" }],
|
|
728
|
-
["path", { d: "M3 16.5h4", key: "1230mu" }],
|
|
729
|
-
["path", { d: "M17 3v18", key: "in4fa5" }],
|
|
730
|
-
["path", { d: "M17 7.5h4", key: "myr1c1" }],
|
|
731
|
-
["path", { d: "M17 16.5h4", key: "go4c1d" }]
|
|
732
|
-
]);
|
|
733
|
-
|
|
734
951
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/globe.js
|
|
735
952
|
var Globe = createLucideIcon("Globe", [
|
|
736
953
|
["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
|
|
@@ -806,6 +1023,12 @@ var Play = createLucideIcon("Play", [
|
|
|
806
1023
|
["polygon", { points: "6 3 20 12 6 21 6 3", key: "1oa8hb" }]
|
|
807
1024
|
]);
|
|
808
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
|
+
|
|
809
1032
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/settings-2.js
|
|
810
1033
|
var Settings2 = createLucideIcon("Settings2", [
|
|
811
1034
|
["path", { d: "M20 7h-9", key: "3s1dr2" }],
|
|
@@ -834,6 +1057,12 @@ var Square = createLucideIcon("Square", [
|
|
|
834
1057
|
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }]
|
|
835
1058
|
]);
|
|
836
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
|
+
|
|
837
1066
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/triangle-alert.js
|
|
838
1067
|
var TriangleAlert = createLucideIcon("TriangleAlert", [
|
|
839
1068
|
[
|
|
@@ -847,6 +1076,17 @@ var TriangleAlert = createLucideIcon("TriangleAlert", [
|
|
|
847
1076
|
["path", { d: "M12 17h.01", key: "p32p05" }]
|
|
848
1077
|
]);
|
|
849
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
|
+
|
|
850
1090
|
// ../../node_modules/.pnpm/lucide-react@0.468.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/x.js
|
|
851
1091
|
var X = createLucideIcon("X", [
|
|
852
1092
|
["path", { d: "M18 6 6 18", key: "1bl5f8" }],
|
|
@@ -854,7 +1094,7 @@ var X = createLucideIcon("X", [
|
|
|
854
1094
|
]);
|
|
855
1095
|
|
|
856
1096
|
// src/components/AgentChat.tsx
|
|
857
|
-
import { useCallback as
|
|
1097
|
+
import { useCallback as useCallback8, useEffect as useEffect11, useMemo as useMemo8, useState as useState13 } from "react";
|
|
858
1098
|
|
|
859
1099
|
// src/lib/utils.ts
|
|
860
1100
|
function cn(...inputs) {
|
|
@@ -981,8 +1221,17 @@ function ReplayMismatchPrompt({ mismatch, className }) {
|
|
|
981
1221
|
);
|
|
982
1222
|
}
|
|
983
1223
|
|
|
1224
|
+
// src/components/ChatSurface.tsx
|
|
1225
|
+
import { chatErrorForDisplay as chatErrorForDisplay2 } from "@blade-hq/agent-client";
|
|
1226
|
+
|
|
984
1227
|
// src/components/ChatInput.tsx
|
|
985
1228
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1229
|
+
function isImeCompositionKey(event) {
|
|
1230
|
+
return event.isComposing || event.keyCode === 229;
|
|
1231
|
+
}
|
|
1232
|
+
function shouldSubmitChatInput(event, menuOpen) {
|
|
1233
|
+
return event.key === "Enter" && !event.shiftKey && !menuOpen && !isImeCompositionKey(event);
|
|
1234
|
+
}
|
|
986
1235
|
function ChatInput({
|
|
987
1236
|
value,
|
|
988
1237
|
onValueChange,
|
|
@@ -1002,7 +1251,12 @@ function ChatInput({
|
|
|
1002
1251
|
onValueChange("");
|
|
1003
1252
|
};
|
|
1004
1253
|
const handleKeyDown = (event) => {
|
|
1005
|
-
if (
|
|
1254
|
+
if (shouldSubmitChatInput({
|
|
1255
|
+
key: event.key,
|
|
1256
|
+
shiftKey: event.shiftKey,
|
|
1257
|
+
isComposing: event.nativeEvent.isComposing,
|
|
1258
|
+
keyCode: event.nativeEvent.keyCode
|
|
1259
|
+
}, false)) {
|
|
1006
1260
|
event.preventDefault();
|
|
1007
1261
|
void handleSend();
|
|
1008
1262
|
}
|
|
@@ -1052,24 +1306,74 @@ function ChatInput({
|
|
|
1052
1306
|
}
|
|
1053
1307
|
|
|
1054
1308
|
// src/components/ConnectionBanner.tsx
|
|
1309
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "react";
|
|
1055
1310
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1311
|
+
var CONNECTION_NOTICE_DELAY_MS = 3e3;
|
|
1312
|
+
var CONNECTION_ERROR_DELAY_MS = 15e3;
|
|
1313
|
+
function useConnectionNoticePhase(connected) {
|
|
1314
|
+
const [phase, setPhase] = useState4("hidden");
|
|
1315
|
+
const connectedRef = useRef4(connected);
|
|
1316
|
+
const timersRef = useRef4([]);
|
|
1317
|
+
connectedRef.current = connected;
|
|
1318
|
+
useEffect4(() => {
|
|
1319
|
+
const clearTimers = () => {
|
|
1320
|
+
for (const timer of timersRef.current) clearTimeout(timer);
|
|
1321
|
+
timersRef.current = [];
|
|
1322
|
+
};
|
|
1323
|
+
const startGracePeriod = () => {
|
|
1324
|
+
clearTimers();
|
|
1325
|
+
setPhase("hidden");
|
|
1326
|
+
timersRef.current = [
|
|
1327
|
+
setTimeout(() => setPhase("recovering"), CONNECTION_NOTICE_DELAY_MS),
|
|
1328
|
+
setTimeout(() => setPhase("failed"), CONNECTION_ERROR_DELAY_MS)
|
|
1329
|
+
];
|
|
1330
|
+
};
|
|
1331
|
+
if (connected) {
|
|
1332
|
+
clearTimers();
|
|
1333
|
+
setPhase("hidden");
|
|
1334
|
+
} else {
|
|
1335
|
+
startGracePeriod();
|
|
1336
|
+
}
|
|
1337
|
+
const handleForeground = () => {
|
|
1338
|
+
if (!connectedRef.current) startGracePeriod();
|
|
1339
|
+
};
|
|
1340
|
+
const handleVisibilityChange = () => {
|
|
1341
|
+
if (document.visibilityState === "visible") handleForeground();
|
|
1342
|
+
};
|
|
1343
|
+
window.addEventListener("blade:app-active", handleForeground);
|
|
1344
|
+
window.addEventListener("focus", handleForeground);
|
|
1345
|
+
window.addEventListener("pageshow", handleForeground);
|
|
1346
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
1347
|
+
return () => {
|
|
1348
|
+
clearTimers();
|
|
1349
|
+
window.removeEventListener("blade:app-active", handleForeground);
|
|
1350
|
+
window.removeEventListener("focus", handleForeground);
|
|
1351
|
+
window.removeEventListener("pageshow", handleForeground);
|
|
1352
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
1353
|
+
};
|
|
1354
|
+
}, [connected]);
|
|
1355
|
+
return phase;
|
|
1356
|
+
}
|
|
1056
1357
|
function ConnectionBanner({ connection, className }) {
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
const
|
|
1358
|
+
const hasConnectedRef = useRef4(connection === "connected" || connection === "reconnecting");
|
|
1359
|
+
if (connection === "connected") hasConnectedRef.current = true;
|
|
1360
|
+
const connected = connection === "connected";
|
|
1361
|
+
const phase = useConnectionNoticePhase(connected);
|
|
1362
|
+
if (connected || phase === "hidden") return null;
|
|
1363
|
+
const recovering = phase === "recovering";
|
|
1364
|
+
const firstConnection = !hasConnectedRef.current;
|
|
1061
1365
|
return /* @__PURE__ */ jsx5("div", { className: cn("blade-chat-banner bg-[hsl(var(--background))] px-5 pt-3", className), children: /* @__PURE__ */ jsxs4(
|
|
1062
1366
|
"div",
|
|
1063
1367
|
{
|
|
1064
1368
|
className: cn(
|
|
1065
1369
|
"mx-auto flex max-w-3xl items-start gap-3 rounded-2xl border px-4 py-3",
|
|
1066
|
-
|
|
1370
|
+
recovering ? "border-amber-500/25 bg-amber-500/10 text-amber-100" : "border-rose-500/25 bg-rose-500/10 text-rose-100"
|
|
1067
1371
|
),
|
|
1068
1372
|
children: [
|
|
1069
|
-
/* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", children:
|
|
1373
|
+
/* @__PURE__ */ jsx5("span", { className: "mt-0.5 shrink-0", children: recovering ? /* @__PURE__ */ jsx5(LoaderCircle, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ jsx5(TriangleAlert, { size: 14 }) }),
|
|
1070
1374
|
/* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
|
|
1071
|
-
/* @__PURE__ */ jsx5("div", { className: "text-sm font-medium", children:
|
|
1072
|
-
/* @__PURE__ */ jsx5("div", { className: "text-xs opacity-80", children: "\
|
|
1375
|
+
/* @__PURE__ */ jsx5("div", { className: "text-sm font-medium", children: recovering ? firstConnection ? "\u6B63\u5728\u8FDE\u63A5\u2026" : "\u6B63\u5728\u6062\u590D\u8FDE\u63A5\u2026" : "\u6682\u65F6\u65E0\u6CD5\u8FDE\u63A5" }),
|
|
1376
|
+
/* @__PURE__ */ jsx5("div", { className: "text-xs opacity-80", children: recovering ? "\u6062\u590D\u540E\u4F1A\u81EA\u52A8\u540C\u6B65\u6700\u65B0\u6D88\u606F\uFF0C\u8BF7\u7A0D\u5019" : "\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u670D\u52A1\u72B6\u6001\uFF0C\u7CFB\u7EDF\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5" })
|
|
1073
1377
|
] })
|
|
1074
1378
|
]
|
|
1075
1379
|
}
|
|
@@ -1078,10 +1382,10 @@ function ConnectionBanner({ connection, className }) {
|
|
|
1078
1382
|
|
|
1079
1383
|
// src/components/MessageList.tsx
|
|
1080
1384
|
import { isHiddenInternalMessage } from "@blade-hq/agent-client";
|
|
1081
|
-
import { useCallback as
|
|
1385
|
+
import { useCallback as useCallback7, useEffect as useEffect10, useMemo as useMemo7, useRef as useRef11, useState as useState12 } from "react";
|
|
1082
1386
|
|
|
1083
1387
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/useStickToBottom.js
|
|
1084
|
-
import { useCallback as
|
|
1388
|
+
import { useCallback as useCallback4, useMemo as useMemo3, useRef as useRef5, useState as useState5 } from "react";
|
|
1085
1389
|
var DEFAULT_SPRING_ANIMATION = {
|
|
1086
1390
|
/**
|
|
1087
1391
|
* A value from 0 to 1, on how much to damp the animation.
|
|
@@ -1118,12 +1422,12 @@ globalThis.document?.addEventListener("click", () => {
|
|
|
1118
1422
|
mouseDown = false;
|
|
1119
1423
|
});
|
|
1120
1424
|
var useStickToBottom = (options = {}) => {
|
|
1121
|
-
const [escapedFromLock, updateEscapedFromLock] =
|
|
1122
|
-
const [isAtBottom, updateIsAtBottom] =
|
|
1123
|
-
const [isNearBottom, setIsNearBottom] =
|
|
1124
|
-
const optionsRef =
|
|
1425
|
+
const [escapedFromLock, updateEscapedFromLock] = useState5(false);
|
|
1426
|
+
const [isAtBottom, updateIsAtBottom] = useState5(options.initial !== false);
|
|
1427
|
+
const [isNearBottom, setIsNearBottom] = useState5(false);
|
|
1428
|
+
const optionsRef = useRef5(null);
|
|
1125
1429
|
optionsRef.current = options;
|
|
1126
|
-
const isSelecting =
|
|
1430
|
+
const isSelecting = useCallback4(() => {
|
|
1127
1431
|
if (!mouseDown) {
|
|
1128
1432
|
return false;
|
|
1129
1433
|
}
|
|
@@ -1134,11 +1438,11 @@ var useStickToBottom = (options = {}) => {
|
|
|
1134
1438
|
const range = selection.getRangeAt(0);
|
|
1135
1439
|
return range.commonAncestorContainer.contains(scrollRef.current) || scrollRef.current?.contains(range.commonAncestorContainer);
|
|
1136
1440
|
}, []);
|
|
1137
|
-
const setIsAtBottom =
|
|
1441
|
+
const setIsAtBottom = useCallback4((isAtBottom2) => {
|
|
1138
1442
|
state.isAtBottom = isAtBottom2;
|
|
1139
1443
|
updateIsAtBottom(isAtBottom2);
|
|
1140
1444
|
}, []);
|
|
1141
|
-
const setEscapedFromLock =
|
|
1445
|
+
const setEscapedFromLock = useCallback4((escapedFromLock2) => {
|
|
1142
1446
|
state.escapedFromLock = escapedFromLock2;
|
|
1143
1447
|
updateEscapedFromLock(escapedFromLock2);
|
|
1144
1448
|
}, []);
|
|
@@ -1195,7 +1499,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
1195
1499
|
}
|
|
1196
1500
|
};
|
|
1197
1501
|
}, []);
|
|
1198
|
-
const scrollToBottom =
|
|
1502
|
+
const scrollToBottom = useCallback4((scrollOptions = {}) => {
|
|
1199
1503
|
if (typeof scrollOptions === "string") {
|
|
1200
1504
|
scrollOptions = { animation: scrollOptions };
|
|
1201
1505
|
}
|
|
@@ -1280,11 +1584,11 @@ var useStickToBottom = (options = {}) => {
|
|
|
1280
1584
|
}
|
|
1281
1585
|
return next();
|
|
1282
1586
|
}, [setIsAtBottom, isSelecting, state]);
|
|
1283
|
-
const stopScroll =
|
|
1587
|
+
const stopScroll = useCallback4(() => {
|
|
1284
1588
|
setEscapedFromLock(true);
|
|
1285
1589
|
setIsAtBottom(false);
|
|
1286
1590
|
}, [setEscapedFromLock, setIsAtBottom]);
|
|
1287
|
-
const handleScroll =
|
|
1591
|
+
const handleScroll = useCallback4(({ target }) => {
|
|
1288
1592
|
if (target !== scrollRef.current) {
|
|
1289
1593
|
return;
|
|
1290
1594
|
}
|
|
@@ -1323,7 +1627,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
1323
1627
|
}
|
|
1324
1628
|
}, 1);
|
|
1325
1629
|
}, [setEscapedFromLock, setIsAtBottom, isSelecting, state]);
|
|
1326
|
-
const handleWheel =
|
|
1630
|
+
const handleWheel = useCallback4(({ target, deltaY }) => {
|
|
1327
1631
|
let element = target;
|
|
1328
1632
|
while (!["scroll", "auto"].includes(getComputedStyle(element).overflow)) {
|
|
1329
1633
|
if (!element.parentElement) {
|
|
@@ -1393,7 +1697,7 @@ var useStickToBottom = (options = {}) => {
|
|
|
1393
1697
|
};
|
|
1394
1698
|
};
|
|
1395
1699
|
function useRefCallback(callback, deps) {
|
|
1396
|
-
const result =
|
|
1700
|
+
const result = useCallback4((ref) => {
|
|
1397
1701
|
result.current = ref;
|
|
1398
1702
|
return callback(ref);
|
|
1399
1703
|
}, deps);
|
|
@@ -1425,11 +1729,11 @@ function mergeAnimations(...animations) {
|
|
|
1425
1729
|
|
|
1426
1730
|
// ../../node_modules/.pnpm/use-stick-to-bottom@1.1.3_react@19.2.4/node_modules/use-stick-to-bottom/dist/StickToBottom.js
|
|
1427
1731
|
import * as React from "react";
|
|
1428
|
-
import { createContext as createContext2, useContext as useContext2, useEffect as
|
|
1732
|
+
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect5, useImperativeHandle, useLayoutEffect, useMemo as useMemo4, useRef as useRef6 } from "react";
|
|
1429
1733
|
var StickToBottomContext = createContext2(null);
|
|
1430
|
-
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect :
|
|
1734
|
+
var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect5;
|
|
1431
1735
|
function StickToBottom({ instance, children, resize, initial, mass, damping, stiffness, targetScrollTop: currentTargetScrollTop, contextRef, ...props }) {
|
|
1432
|
-
const customTargetScrollTop =
|
|
1736
|
+
const customTargetScrollTop = useRef6(null);
|
|
1433
1737
|
const targetScrollTop = React.useCallback((target, elements) => {
|
|
1434
1738
|
const get = context?.targetScrollTop ?? currentTargetScrollTop;
|
|
1435
1739
|
return get?.(target, elements) ?? target;
|
|
@@ -1505,11 +1809,16 @@ function useStickToBottomContext() {
|
|
|
1505
1809
|
}
|
|
1506
1810
|
|
|
1507
1811
|
// src/components/AssistantTurnBlock.tsx
|
|
1508
|
-
import {
|
|
1509
|
-
|
|
1812
|
+
import {
|
|
1813
|
+
getFileParts,
|
|
1814
|
+
getImageParts,
|
|
1815
|
+
getTextContent,
|
|
1816
|
+
normalizeMessageContent
|
|
1817
|
+
} from "@blade-hq/agent-client";
|
|
1818
|
+
import { useEffect as useEffect8, useRef as useRef9, useState as useState10 } from "react";
|
|
1510
1819
|
|
|
1511
1820
|
// src/components/AgentLoopBlock.tsx
|
|
1512
|
-
import { useState as
|
|
1821
|
+
import { useState as useState6 } from "react";
|
|
1513
1822
|
|
|
1514
1823
|
// src/components/display-utils.ts
|
|
1515
1824
|
var TOOL_NAME_ALIASES = {
|
|
@@ -1525,7 +1834,9 @@ var TOOL_NAME_ALIASES = {
|
|
|
1525
1834
|
finish_task: "FinishTask",
|
|
1526
1835
|
glob: "Glob",
|
|
1527
1836
|
grep: "Grep",
|
|
1837
|
+
kb_search: "KbSearch",
|
|
1528
1838
|
ls: "Ls",
|
|
1839
|
+
multi_edit: "MultiEdit",
|
|
1529
1840
|
read: "Read",
|
|
1530
1841
|
read_skill: "ReadSkill",
|
|
1531
1842
|
web_fetch: "WebFetch",
|
|
@@ -1538,9 +1849,11 @@ var TOOL_DISPLAY_LABELS = {
|
|
|
1538
1849
|
Read: "\u8BFB\u53D6\u6587\u4EF6",
|
|
1539
1850
|
Write: "\u5199\u5165\u6587\u4EF6",
|
|
1540
1851
|
Edit: "\u7F16\u8F91\u6587\u4EF6",
|
|
1852
|
+
MultiEdit: "\u7F16\u8F91\u6587\u4EF6",
|
|
1541
1853
|
Ls: "\u5217\u51FA\u76EE\u5F55",
|
|
1542
1854
|
Glob: "\u5339\u914D\u6587\u4EF6",
|
|
1543
1855
|
Grep: "\u641C\u7D22\u6587\u672C",
|
|
1856
|
+
KbSearch: "\u68C0\u7D22\u77E5\u8BC6\u5E93",
|
|
1544
1857
|
WebSearch: "\u641C\u7D22\u7F51\u9875",
|
|
1545
1858
|
WebFetch: "\u6574\u7406\u7F51\u9875\u5185\u5BB9",
|
|
1546
1859
|
Agent: "\u6D3E\u751F\u5B50\u667A\u80FD\u4F53",
|
|
@@ -1563,6 +1876,17 @@ function getStringArgValue(args, key) {
|
|
|
1563
1876
|
const value = args?.[key];
|
|
1564
1877
|
return typeof value === "string" ? value.trim() : "";
|
|
1565
1878
|
}
|
|
1879
|
+
var SKILL_ENTRY_FILE_NAMES = /* @__PURE__ */ new Set(["skill.md", "command.md"]);
|
|
1880
|
+
var NON_SKILL_DIR_NAMES = /* @__PURE__ */ new Set([".", "..", ".agent", ".agents", ".claude", "skill_data", "skills"]);
|
|
1881
|
+
function getSkillNameFromFilePath(filePath) {
|
|
1882
|
+
if (!filePath) return null;
|
|
1883
|
+
const segments = filePath.split(/[\\/]+/).filter(Boolean);
|
|
1884
|
+
const fileName = segments.pop();
|
|
1885
|
+
if (!fileName || !SKILL_ENTRY_FILE_NAMES.has(fileName.toLowerCase())) return null;
|
|
1886
|
+
const dirName = segments.pop();
|
|
1887
|
+
if (!dirName || NON_SKILL_DIR_NAMES.has(dirName.toLowerCase())) return null;
|
|
1888
|
+
return dirName;
|
|
1889
|
+
}
|
|
1566
1890
|
function formatToolName(name) {
|
|
1567
1891
|
const trimmed = name.trim();
|
|
1568
1892
|
if (!trimmed) return name;
|
|
@@ -1587,6 +1911,12 @@ function getToolDisplayLabel(toolCall) {
|
|
|
1587
1911
|
const skillName = getStringArgValue(args, "skill") || getStringArgValue(args, "skill_name");
|
|
1588
1912
|
return skillName ? `${baseLabel}\u300C${skillName}\u300D` : baseLabel;
|
|
1589
1913
|
}
|
|
1914
|
+
if (normalized === "Read") {
|
|
1915
|
+
const skillName = getSkillNameFromFilePath(
|
|
1916
|
+
getStringArgValue(args, "file_path") || getStringArgValue(args, "path")
|
|
1917
|
+
);
|
|
1918
|
+
if (skillName) return `\u8BFB\u53D6\u6280\u80FD\u300C${skillName}\u300D`;
|
|
1919
|
+
}
|
|
1590
1920
|
if (normalized === "FinishTask") {
|
|
1591
1921
|
const title = getStringArgValue(args, "title");
|
|
1592
1922
|
return title ? `${baseLabel}\uFF1A${title}` : baseLabel;
|
|
@@ -1641,83 +1971,68 @@ function parseAgentDescription(argumentsJson) {
|
|
|
1641
1971
|
}
|
|
1642
1972
|
}
|
|
1643
1973
|
function AgentLoopBlock({ toolCall }) {
|
|
1644
|
-
const [expanded, setExpanded] =
|
|
1974
|
+
const [expanded, setExpanded] = useState6(false);
|
|
1645
1975
|
const description = parseAgentDescription(toolCall.arguments);
|
|
1646
1976
|
const running = toolCall.status === "pending" || toolCall.status === "awaiting_answer";
|
|
1647
1977
|
const failed = toolCall.status === "error" || toolCall.status === "cancelled";
|
|
1648
|
-
|
|
1978
|
+
const hasResult = toolCall.result != null;
|
|
1979
|
+
const iconClass = cn(
|
|
1980
|
+
"size-3.5 shrink-0",
|
|
1981
|
+
failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
|
|
1982
|
+
);
|
|
1983
|
+
return /* @__PURE__ */ jsxs5("div", { className: "blade-chat-agent-loop text-xs leading-[22px]", children: [
|
|
1649
1984
|
/* @__PURE__ */ jsxs5(
|
|
1650
|
-
"
|
|
1985
|
+
"button",
|
|
1651
1986
|
{
|
|
1987
|
+
type: "button",
|
|
1988
|
+
onClick: () => hasResult && setExpanded(!expanded),
|
|
1989
|
+
disabled: !hasResult,
|
|
1990
|
+
"aria-expanded": hasResult ? expanded : void 0,
|
|
1991
|
+
"data-testid": "execution-tool-intent",
|
|
1652
1992
|
className: cn(
|
|
1653
|
-
"
|
|
1654
|
-
|
|
1993
|
+
"flex min-w-0 items-center gap-1 py-1.5 text-left",
|
|
1994
|
+
hasResult && "cursor-pointer hover:text-[hsl(var(--foreground))]",
|
|
1995
|
+
failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
|
|
1655
1996
|
),
|
|
1997
|
+
title: `\u5B50\u4EFB\u52A1\uFF1A${description}`,
|
|
1656
1998
|
children: [
|
|
1657
|
-
/* @__PURE__ */
|
|
1658
|
-
|
|
1999
|
+
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" }),
|
|
2000
|
+
/* @__PURE__ */ jsxs5("span", { className: "min-w-0 truncate", children: [
|
|
2001
|
+
"\u5B50\u4EFB\u52A1\uFF1A",
|
|
2002
|
+
description
|
|
2003
|
+
] }),
|
|
2004
|
+
hasResult ? /* @__PURE__ */ jsx6(
|
|
2005
|
+
ChevronRight,
|
|
1659
2006
|
{
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
ChevronRight,
|
|
1667
|
-
{
|
|
1668
|
-
size: 11,
|
|
1669
|
-
className: cn(
|
|
1670
|
-
"shrink-0 text-[hsl(var(--muted-foreground))] transition-transform",
|
|
1671
|
-
expanded && "rotate-90"
|
|
1672
|
-
)
|
|
1673
|
-
}
|
|
1674
|
-
),
|
|
1675
|
-
/* @__PURE__ */ jsx6(Bot, { size: 12, className: "shrink-0 text-[hsl(var(--muted-foreground))]" }),
|
|
1676
|
-
/* @__PURE__ */ jsxs5(
|
|
1677
|
-
"span",
|
|
1678
|
-
{
|
|
1679
|
-
className: cn(
|
|
1680
|
-
"flex shrink-0 items-center gap-1 text-[10px]",
|
|
1681
|
-
failed ? "text-[hsl(var(--muted-foreground))]" : running ? "text-blue-300" : "text-[hsl(var(--primary))]"
|
|
1682
|
-
),
|
|
1683
|
-
children: [
|
|
1684
|
-
running ? /* @__PURE__ */ jsx6(LoaderCircle, { size: 11, className: "animate-spin" }) : failed ? /* @__PURE__ */ jsx6(X, { size: 11 }) : /* @__PURE__ */ jsx6(Check, { size: 11 }),
|
|
1685
|
-
/* @__PURE__ */ jsx6("span", { children: running ? "\u6267\u884C\u4E2D" : failed ? "\u5DF2\u7EC8\u6B62" : "\u5B8C\u6210" })
|
|
1686
|
-
]
|
|
1687
|
-
}
|
|
1688
|
-
),
|
|
1689
|
-
/* @__PURE__ */ jsxs5("span", { className: "min-w-0 flex-1 truncate font-medium text-[hsl(var(--foreground))]", children: [
|
|
1690
|
-
"\u5B50\u667A\u80FD\u4F53\uFF1A",
|
|
1691
|
-
description
|
|
1692
|
-
] })
|
|
1693
|
-
]
|
|
2007
|
+
size: 14,
|
|
2008
|
+
className: cn(
|
|
2009
|
+
"shrink-0 transition-transform duration-300",
|
|
2010
|
+
expanded && "rotate-90"
|
|
2011
|
+
),
|
|
2012
|
+
"aria-hidden": "true"
|
|
1694
2013
|
}
|
|
1695
|
-
)
|
|
1696
|
-
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) })
|
|
2014
|
+
) : null
|
|
1697
2015
|
]
|
|
1698
2016
|
}
|
|
1699
2017
|
),
|
|
1700
|
-
expanded &&
|
|
1701
|
-
/* @__PURE__ */ jsx6("div", { className: "mb-1 text-[10px] uppercase tracking-wider text-[hsl(var(--muted-foreground))]", children: "\u7ED3\u679C" }),
|
|
1702
|
-
/* @__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) })
|
|
1703
|
-
] })
|
|
2018
|
+
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
|
|
1704
2019
|
] });
|
|
1705
2020
|
}
|
|
1706
2021
|
|
|
1707
2022
|
// src/components/MarkdownContent.tsx
|
|
1708
2023
|
import {
|
|
1709
|
-
useEffect as
|
|
2024
|
+
useEffect as useEffect6,
|
|
1710
2025
|
useMemo as useMemo5,
|
|
1711
|
-
useRef as
|
|
1712
|
-
useState as
|
|
2026
|
+
useRef as useRef7,
|
|
2027
|
+
useState as useState7
|
|
1713
2028
|
} from "react";
|
|
1714
2029
|
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1715
2030
|
var SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/gi;
|
|
1716
2031
|
function CodeBlockPre({ children, node: _node, ...props }) {
|
|
1717
|
-
const preRef =
|
|
1718
|
-
const [copied, setCopied] =
|
|
1719
|
-
const [language, setLanguage] =
|
|
1720
|
-
|
|
2032
|
+
const preRef = useRef7(null);
|
|
2033
|
+
const [copied, setCopied] = useState7(false);
|
|
2034
|
+
const [language, setLanguage] = useState7("");
|
|
2035
|
+
useEffect6(() => {
|
|
1721
2036
|
const codeEl = preRef.current?.querySelector("code");
|
|
1722
2037
|
setLanguage(codeEl?.className.match(/language-(\S+)/)?.[1] ?? "");
|
|
1723
2038
|
}, []);
|
|
@@ -1788,11 +2103,40 @@ function Shimmer({ children = "\u6B63\u5728\u601D\u8003...", className }) {
|
|
|
1788
2103
|
}
|
|
1789
2104
|
|
|
1790
2105
|
// src/components/ToolCallBlock.tsx
|
|
1791
|
-
import { useState as
|
|
2106
|
+
import { useState as useState9 } from "react";
|
|
1792
2107
|
|
|
1793
2108
|
// src/components/AskUserQuestionBlock.tsx
|
|
1794
|
-
import { useEffect as
|
|
2109
|
+
import { useEffect as useEffect7, useMemo as useMemo6, useRef as useRef8, useState as useState8 } from "react";
|
|
1795
2110
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2111
|
+
var CUSTOM_TEXTAREA_MAX_HEIGHT = 160;
|
|
2112
|
+
function resizeCustomTextarea(textarea) {
|
|
2113
|
+
textarea.style.height = "auto";
|
|
2114
|
+
textarea.style.height = `${Math.min(textarea.scrollHeight, CUSTOM_TEXTAREA_MAX_HEIGHT)}px`;
|
|
2115
|
+
textarea.style.overflowY = textarea.scrollHeight > CUSTOM_TEXTAREA_MAX_HEIGHT ? "auto" : "hidden";
|
|
2116
|
+
}
|
|
2117
|
+
function useAutoResizeTextarea(value) {
|
|
2118
|
+
const textareaRef = useRef8(null);
|
|
2119
|
+
useEffect7(() => {
|
|
2120
|
+
const textarea = textareaRef.current;
|
|
2121
|
+
if (textarea?.value === value) resizeCustomTextarea(textarea);
|
|
2122
|
+
}, [value]);
|
|
2123
|
+
useEffect7(() => {
|
|
2124
|
+
const textarea = textareaRef.current;
|
|
2125
|
+
if (!textarea || typeof ResizeObserver === "undefined") return;
|
|
2126
|
+
let previousWidth = textarea.clientWidth;
|
|
2127
|
+
const observer = new ResizeObserver(([entry]) => {
|
|
2128
|
+
if (!entry || entry.contentRect.width === previousWidth) return;
|
|
2129
|
+
previousWidth = entry.contentRect.width;
|
|
2130
|
+
resizeCustomTextarea(textarea);
|
|
2131
|
+
});
|
|
2132
|
+
observer.observe(textarea);
|
|
2133
|
+
return () => observer.disconnect();
|
|
2134
|
+
}, []);
|
|
2135
|
+
return textareaRef;
|
|
2136
|
+
}
|
|
2137
|
+
function indentAnswerContinuationLines(answer) {
|
|
2138
|
+
return answer.replaceAll("\n", "\n ");
|
|
2139
|
+
}
|
|
1796
2140
|
function AskUserQuestionBlock({
|
|
1797
2141
|
data,
|
|
1798
2142
|
answered,
|
|
@@ -1801,18 +2145,19 @@ function AskUserQuestionBlock({
|
|
|
1801
2145
|
answerData,
|
|
1802
2146
|
onAnswer
|
|
1803
2147
|
}) {
|
|
1804
|
-
const [selections, setSelections] =
|
|
1805
|
-
const [customTexts, setCustomTexts] =
|
|
1806
|
-
const [usingCustom, setUsingCustom] =
|
|
1807
|
-
const [
|
|
1808
|
-
|
|
2148
|
+
const [selections, setSelections] = useState8(/* @__PURE__ */ new Map());
|
|
2149
|
+
const [customTexts, setCustomTexts] = useState8(/* @__PURE__ */ new Map());
|
|
2150
|
+
const [usingCustom, setUsingCustom] = useState8(/* @__PURE__ */ new Set());
|
|
2151
|
+
const [note, setNote] = useState8("");
|
|
2152
|
+
const [submitted, setSubmitted] = useState8(false);
|
|
2153
|
+
useEffect7(() => {
|
|
1809
2154
|
if (sessionStatus === "failed" || sessionStatus === "interrupted") {
|
|
1810
2155
|
setSubmitted(false);
|
|
1811
2156
|
}
|
|
1812
2157
|
}, [sessionStatus]);
|
|
1813
2158
|
const displayAnswerState = useMemo6(() => {
|
|
1814
2159
|
if (!(answered && answerData)) {
|
|
1815
|
-
return { selections, customTexts, usingCustom };
|
|
2160
|
+
return { selections, customTexts, usingCustom, note };
|
|
1816
2161
|
}
|
|
1817
2162
|
const nextSelections = /* @__PURE__ */ new Map();
|
|
1818
2163
|
const nextCustomTexts = /* @__PURE__ */ new Map();
|
|
@@ -1828,9 +2173,10 @@ function AskUserQuestionBlock({
|
|
|
1828
2173
|
return {
|
|
1829
2174
|
selections: nextSelections,
|
|
1830
2175
|
customTexts: nextCustomTexts,
|
|
1831
|
-
usingCustom: nextUsingCustom
|
|
2176
|
+
usingCustom: nextUsingCustom,
|
|
2177
|
+
note: answerData.note ?? ""
|
|
1832
2178
|
};
|
|
1833
|
-
}, [answerData, answered, customTexts, selections, usingCustom]);
|
|
2179
|
+
}, [answerData, answered, customTexts, note, selections, usingCustom]);
|
|
1834
2180
|
const toggleOption = (qIdx, optIdx, multi) => {
|
|
1835
2181
|
if (answered || submitted) return;
|
|
1836
2182
|
setSelections((prev) => {
|
|
@@ -1878,6 +2224,7 @@ function AskUserQuestionBlock({
|
|
|
1878
2224
|
const allAnswered = data.questions.every((_, i) => getAnswer(i) !== null);
|
|
1879
2225
|
const handleSubmit = () => {
|
|
1880
2226
|
if (answered || submitted || !allAnswered || !onAnswer) return;
|
|
2227
|
+
const trimmedNote = note.trim();
|
|
1881
2228
|
const nextAnswerData = {
|
|
1882
2229
|
selections: Object.fromEntries(
|
|
1883
2230
|
Array.from(selections.entries()).map(([qIdx, optionIndexes]) => [
|
|
@@ -1887,11 +2234,17 @@ function AskUserQuestionBlock({
|
|
|
1887
2234
|
),
|
|
1888
2235
|
custom: Object.fromEntries(
|
|
1889
2236
|
Array.from(usingCustom).map((qIdx) => [qIdx, (customTexts.get(qIdx) ?? "").trim()]).filter(([, text2]) => text2.length > 0)
|
|
1890
|
-
)
|
|
2237
|
+
),
|
|
2238
|
+
...trimmedNote ? { note: trimmedNote } : {}
|
|
1891
2239
|
};
|
|
1892
|
-
const parts = data.questions.map(
|
|
1893
|
-
|
|
1894
|
-
|
|
2240
|
+
const parts = data.questions.map(
|
|
2241
|
+
(q, i) => `- ${q.question} -> ${indentAnswerContinuationLines(getAnswer(i) ?? "")}`
|
|
2242
|
+
);
|
|
2243
|
+
const text = [
|
|
2244
|
+
`\u5173\u4E8E\u9700\u8981\u786E\u8BA4\u7684\u95EE\u9898\uFF0C\u7528\u6237\u7684\u56DE\u7B54\u5982\u4E0B\uFF1A
|
|
2245
|
+
${parts.join("\n")}`,
|
|
2246
|
+
trimmedNote ? `\u8865\u5145\u8BF4\u660E\uFF1A${indentAnswerContinuationLines(trimmedNote)}` : ""
|
|
2247
|
+
].filter(Boolean).join("\n");
|
|
1895
2248
|
setSubmitted(true);
|
|
1896
2249
|
onAnswer(text, toolCallId, nextAnswerData);
|
|
1897
2250
|
};
|
|
@@ -1923,6 +2276,15 @@ ${parts.join("\n")}`;
|
|
|
1923
2276
|
},
|
|
1924
2277
|
q.question
|
|
1925
2278
|
)),
|
|
2279
|
+
/* @__PURE__ */ jsx9(
|
|
2280
|
+
NoteField,
|
|
2281
|
+
{
|
|
2282
|
+
answered,
|
|
2283
|
+
submitted,
|
|
2284
|
+
note: displayAnswerState.note,
|
|
2285
|
+
onChange: setNote
|
|
2286
|
+
}
|
|
2287
|
+
),
|
|
1926
2288
|
!answered && !submitted && onAnswer && /* @__PURE__ */ jsx9(
|
|
1927
2289
|
"button",
|
|
1928
2290
|
{
|
|
@@ -1961,6 +2323,7 @@ function QuestionCard({
|
|
|
1961
2323
|
onCustomChange
|
|
1962
2324
|
}) {
|
|
1963
2325
|
const multi = question.multiSelect ?? false;
|
|
2326
|
+
const customTextareaRef = useAutoResizeTextarea(customText);
|
|
1964
2327
|
return /* @__PURE__ */ jsxs7("div", { children: [
|
|
1965
2328
|
/* @__PURE__ */ jsxs7("div", { className: cn("flex items-start gap-2", answered ? "mb-2" : "mb-3"), children: [
|
|
1966
2329
|
/* @__PURE__ */ jsx9(
|
|
@@ -2037,25 +2400,26 @@ function QuestionCard({
|
|
|
2037
2400
|
"div",
|
|
2038
2401
|
{
|
|
2039
2402
|
className: cn(
|
|
2040
|
-
"flex items-
|
|
2403
|
+
"flex items-start gap-2 rounded-lg border transition-all focus-within:ring-2 focus-within:ring-[hsl(var(--ring)/0.35)]",
|
|
2041
2404
|
answered ? "px-2.5 py-1.5" : "px-3 py-2.5",
|
|
2042
2405
|
isCustom ? "border-[hsl(var(--ring)/0.6)] bg-[hsl(var(--accent))]" : "border-[hsl(var(--border))] hover:border-[hsl(var(--ring)/0.3)] hover:bg-[hsl(var(--accent))]",
|
|
2043
2406
|
answered && "cursor-default opacity-70"
|
|
2044
2407
|
),
|
|
2045
2408
|
children: [
|
|
2046
|
-
/* @__PURE__ */ jsx9("span", { className: "shrink-0 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
|
|
2409
|
+
/* @__PURE__ */ jsx9("span", { className: "shrink-0 pt-1 text-xs text-[hsl(var(--muted-foreground))]", children: "\u5176\u4ED6\uFF1A" }),
|
|
2047
2410
|
/* @__PURE__ */ jsx9(
|
|
2048
|
-
"
|
|
2411
|
+
"textarea",
|
|
2049
2412
|
{
|
|
2050
|
-
|
|
2413
|
+
ref: customTextareaRef,
|
|
2414
|
+
rows: 2,
|
|
2051
2415
|
value: customText,
|
|
2052
|
-
|
|
2416
|
+
readOnly: answered,
|
|
2053
2417
|
onChange: (e) => onCustomChange(qIdx, e.target.value),
|
|
2054
2418
|
onFocus: () => onCustomFocus(qIdx),
|
|
2055
2419
|
"aria-label": "\u81EA\u5B9A\u4E49\u56DE\u7B54",
|
|
2056
2420
|
placeholder: "\u8F93\u5165\u4F60\u7684\u7B54\u6848...",
|
|
2057
2421
|
className: cn(
|
|
2058
|
-
"min-w-0 flex-1 bg-transparent text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]",
|
|
2422
|
+
"min-h-10 min-w-0 flex-1 resize-none bg-transparent leading-5 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]",
|
|
2059
2423
|
answered ? "text-xs" : "text-sm"
|
|
2060
2424
|
)
|
|
2061
2425
|
}
|
|
@@ -2066,6 +2430,49 @@ function QuestionCard({
|
|
|
2066
2430
|
] })
|
|
2067
2431
|
] });
|
|
2068
2432
|
}
|
|
2433
|
+
function NoteField({
|
|
2434
|
+
answered,
|
|
2435
|
+
submitted,
|
|
2436
|
+
note,
|
|
2437
|
+
onChange
|
|
2438
|
+
}) {
|
|
2439
|
+
const textareaRef = useAutoResizeTextarea(note);
|
|
2440
|
+
const readOnly = answered || submitted;
|
|
2441
|
+
if (answered && !note.trim()) return null;
|
|
2442
|
+
return /* @__PURE__ */ jsxs7(
|
|
2443
|
+
"label",
|
|
2444
|
+
{
|
|
2445
|
+
className: cn(
|
|
2446
|
+
"block rounded-lg border transition-all focus-within:ring-2 focus-within:ring-[hsl(var(--ring)/0.35)]",
|
|
2447
|
+
answered ? "px-2.5 py-1.5" : "px-3 py-2.5",
|
|
2448
|
+
note.trim() ? "border-[hsl(var(--ring)/0.6)] bg-[hsl(var(--accent))]" : "border-[hsl(var(--border))] hover:border-[hsl(var(--ring)/0.3)] hover:bg-[hsl(var(--accent))]",
|
|
2449
|
+
readOnly && "cursor-default opacity-70"
|
|
2450
|
+
),
|
|
2451
|
+
children: [
|
|
2452
|
+
/* @__PURE__ */ jsx9("span", { className: "mb-1.5 block text-xs text-[hsl(var(--muted-foreground))]", children: "\u8865\u5145\u8BF4\u660E\uFF08\u53EF\u9009\uFF09" }),
|
|
2453
|
+
/* @__PURE__ */ jsx9(
|
|
2454
|
+
"textarea",
|
|
2455
|
+
{
|
|
2456
|
+
ref: textareaRef,
|
|
2457
|
+
rows: 2,
|
|
2458
|
+
value: note,
|
|
2459
|
+
readOnly,
|
|
2460
|
+
onChange: (event) => {
|
|
2461
|
+
if (readOnly) return;
|
|
2462
|
+
onChange(event.target.value);
|
|
2463
|
+
},
|
|
2464
|
+
"aria-label": "\u8865\u5145\u8BF4\u660E",
|
|
2465
|
+
placeholder: "\u9009\u5B8C\u8FD8\u53EF\u4EE5\u518D\u8BB2\u4E24\u53E5\uFF0C\u7A7A\u7740\u5C31\u5F53\u6CA1\u6709",
|
|
2466
|
+
className: cn(
|
|
2467
|
+
"min-h-10 w-full resize-none bg-transparent leading-5 text-[hsl(var(--foreground))] outline-none placeholder:text-[hsl(var(--muted-foreground)/0.5)]",
|
|
2468
|
+
answered ? "text-xs" : "text-sm"
|
|
2469
|
+
)
|
|
2470
|
+
}
|
|
2471
|
+
)
|
|
2472
|
+
]
|
|
2473
|
+
}
|
|
2474
|
+
);
|
|
2475
|
+
}
|
|
2069
2476
|
function parseAskUserQuestion(toolResult) {
|
|
2070
2477
|
if (!toolResult) return null;
|
|
2071
2478
|
try {
|
|
@@ -2088,6 +2495,26 @@ function parseAskUserQuestion(toolResult) {
|
|
|
2088
2495
|
}
|
|
2089
2496
|
return null;
|
|
2090
2497
|
}
|
|
2498
|
+
function parseAskUserQuestionError(toolResult) {
|
|
2499
|
+
if (!toolResult) return null;
|
|
2500
|
+
try {
|
|
2501
|
+
const parsed = JSON.parse(toolResult);
|
|
2502
|
+
let detail = null;
|
|
2503
|
+
if (typeof parsed.error === "string") detail = parsed.error;
|
|
2504
|
+
if (parsed.error && typeof parsed.error === "object") {
|
|
2505
|
+
const message = parsed.error.message;
|
|
2506
|
+
if (typeof message === "string") detail = message;
|
|
2507
|
+
}
|
|
2508
|
+
if (!detail && typeof parsed.message === "string") detail = parsed.message;
|
|
2509
|
+
if (!detail) return null;
|
|
2510
|
+
return {
|
|
2511
|
+
message: parsed.error_code === "invalid_questions" ? "\u63D0\u95EE\u5185\u5BB9\u4E0D\u5B8C\u6574\uFF0C\u5F53\u524D\u6CA1\u6709\u7B49\u5F85\u4F60\u56DE\u7B54\u3002\u8BF7\u8BA9\u667A\u80FD\u4F53\u91CD\u65B0\u63D0\u95EE\u3002" : "\u8FD9\u6B21\u63D0\u95EE\u6CA1\u6709\u6210\u529F\uFF0C\u5F53\u524D\u6CA1\u6709\u7B49\u5F85\u4F60\u56DE\u7B54\u3002\u8BF7\u8BA9\u667A\u80FD\u4F53\u91CD\u65B0\u63D0\u95EE\u3002",
|
|
2512
|
+
detail
|
|
2513
|
+
};
|
|
2514
|
+
} catch {
|
|
2515
|
+
return null;
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2091
2518
|
function normalizeQuestionItem(value) {
|
|
2092
2519
|
if (!value || typeof value !== "object") return null;
|
|
2093
2520
|
const item = value;
|
|
@@ -2116,9 +2543,10 @@ import { Fragment, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
|
2116
2543
|
function resolveAskQuestionState({
|
|
2117
2544
|
toolStatus,
|
|
2118
2545
|
hasAnswerData,
|
|
2119
|
-
fallbackAnswered
|
|
2546
|
+
fallbackAnswered,
|
|
2547
|
+
fallbackAwaiting
|
|
2120
2548
|
}) {
|
|
2121
|
-
const awaitingAnswer = !hasAnswerData && toolStatus === "awaiting_answer";
|
|
2549
|
+
const awaitingAnswer = !hasAnswerData && (toolStatus === "awaiting_answer" || toolStatus === "pending" && fallbackAwaiting === true);
|
|
2122
2550
|
return {
|
|
2123
2551
|
awaitingAnswer,
|
|
2124
2552
|
answered: hasAnswerData || !awaitingAnswer && (Boolean(fallbackAnswered) || toolStatus === "done" || toolStatus === "cancelled" || toolStatus === "error")
|
|
@@ -2130,9 +2558,10 @@ function ToolCallBlock({
|
|
|
2130
2558
|
answered,
|
|
2131
2559
|
answerData,
|
|
2132
2560
|
sessionStatus,
|
|
2561
|
+
isActiveQuestion,
|
|
2133
2562
|
renderer
|
|
2134
2563
|
}) {
|
|
2135
|
-
const [expanded, setExpanded] =
|
|
2564
|
+
const [expanded, setExpanded] = useState9(false);
|
|
2136
2565
|
const normalizedName = formatToolName(toolCall.name);
|
|
2137
2566
|
if (renderer) {
|
|
2138
2567
|
const custom = renderer(toolCall);
|
|
@@ -2145,7 +2574,8 @@ function ToolCallBlock({
|
|
|
2145
2574
|
const questionState = resolveAskQuestionState({
|
|
2146
2575
|
toolStatus: toolCall.status,
|
|
2147
2576
|
hasAnswerData: Boolean(answerData),
|
|
2148
|
-
fallbackAnswered: answered
|
|
2577
|
+
fallbackAnswered: answered,
|
|
2578
|
+
fallbackAwaiting: isActiveQuestion === true && (sessionStatus === "paused" || sessionStatus === "waiting_for_input")
|
|
2149
2579
|
});
|
|
2150
2580
|
const canAnswer = questionState.awaitingAnswer && Boolean(onAnswer);
|
|
2151
2581
|
if (askData) {
|
|
@@ -2167,9 +2597,16 @@ function ToolCallBlock({
|
|
|
2167
2597
|
/* @__PURE__ */ jsx10("span", { children: "\u6B63\u5728\u51C6\u5907\u95EE\u9898\u2026" })
|
|
2168
2598
|
] });
|
|
2169
2599
|
}
|
|
2600
|
+
const errorDetail = parseAskUserQuestionError(
|
|
2601
|
+
typeof toolCall.result === "string" ? toolCall.result : null
|
|
2602
|
+
);
|
|
2170
2603
|
return /* @__PURE__ */ jsxs8("div", { className: "ml-4 max-w-lg rounded-xl border border-amber-500/35 bg-amber-500/10 p-4 text-sm text-[hsl(var(--foreground))]", children: [
|
|
2171
2604
|
/* @__PURE__ */ jsx10("div", { className: "font-semibold", children: "\u9009\u62E9\u9898\u5185\u5BB9\u6682\u65F6\u65E0\u6CD5\u663E\u793A" }),
|
|
2172
|
-
/* @__PURE__ */ jsx10("div", { className: "mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: "\u6536\u5230\u7684\u4EA4\u4E92\u6570\u636E\u4E0D\u5B8C\u6574\u3002\u8BF7\u8BA9\u667A\u80FD\u4F53\u91CD\u65B0\u63D0\u95EE\u3002" })
|
|
2605
|
+
/* @__PURE__ */ jsx10("div", { className: "mt-1 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: errorDetail?.message ?? "\u6536\u5230\u7684\u4EA4\u4E92\u6570\u636E\u4E0D\u5B8C\u6574\u3002\u8BF7\u8BA9\u667A\u80FD\u4F53\u91CD\u65B0\u63D0\u95EE\u3002" }),
|
|
2606
|
+
errorDetail?.detail ? /* @__PURE__ */ jsxs8("details", { className: "mt-2 text-xs text-[hsl(var(--muted-foreground))]", children: [
|
|
2607
|
+
/* @__PURE__ */ jsx10("summary", { className: "cursor-pointer", children: "\u67E5\u770B\u5177\u4F53\u539F\u56E0" }),
|
|
2608
|
+
/* @__PURE__ */ jsx10("div", { className: "mt-1 break-words font-mono", children: errorDetail.detail })
|
|
2609
|
+
] }) : null
|
|
2173
2610
|
] });
|
|
2174
2611
|
}
|
|
2175
2612
|
const tone = getToolTone(toolCall.status);
|
|
@@ -2234,45 +2671,233 @@ function buildAskUserPayload(argumentsJson) {
|
|
|
2234
2671
|
// src/components/AssistantTurnBlock.tsx
|
|
2235
2672
|
import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2236
2673
|
function ThinkingBlock({ reasoning, isStreaming }) {
|
|
2237
|
-
const [open, setOpen] =
|
|
2238
|
-
|
|
2674
|
+
const [open, setOpen] = useState10(false);
|
|
2675
|
+
if (!isStreaming) return null;
|
|
2676
|
+
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-thinking text-xs", children: [
|
|
2239
2677
|
/* @__PURE__ */ jsxs9(
|
|
2240
2678
|
"button",
|
|
2241
2679
|
{
|
|
2242
2680
|
type: "button",
|
|
2243
2681
|
onClick: () => setOpen(!open),
|
|
2244
2682
|
"aria-expanded": open,
|
|
2245
|
-
className: "inline-flex items-center gap-1
|
|
2683
|
+
className: "group/thinking inline-flex items-center gap-1 text-[hsl(var(--muted-foreground))] transition-colors hover:text-[hsl(var(--foreground))]",
|
|
2246
2684
|
children: [
|
|
2247
|
-
/* @__PURE__ */ jsx11(
|
|
2248
|
-
isStreaming ? /* @__PURE__ */ jsx11(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }) : /* @__PURE__ */ jsx11("span", { children: "\u601D\u8003\u8FC7\u7A0B" }),
|
|
2249
|
-
/* @__PURE__ */ jsxs9("span", { className: "text-[hsl(var(--muted-foreground))]/70", children: [
|
|
2250
|
-
"\xB7 ",
|
|
2251
|
-
new Intl.NumberFormat("zh-CN").format(reasoning.length),
|
|
2252
|
-
" \u5B57"
|
|
2253
|
-
] }),
|
|
2685
|
+
/* @__PURE__ */ jsx11(Shimmer, { className: "text-xs", children: "\u6B63\u5728\u601D\u8003" }),
|
|
2254
2686
|
/* @__PURE__ */ jsx11(
|
|
2255
|
-
|
|
2687
|
+
ChevronRight,
|
|
2256
2688
|
{
|
|
2257
|
-
size:
|
|
2258
|
-
className: cn(
|
|
2689
|
+
size: 14,
|
|
2690
|
+
className: cn(
|
|
2691
|
+
"shrink-0 opacity-0 transition-[opacity,transform] group-hover/thinking:opacity-100",
|
|
2692
|
+
open && "rotate-90 opacity-100"
|
|
2693
|
+
)
|
|
2259
2694
|
}
|
|
2260
2695
|
)
|
|
2261
2696
|
]
|
|
2262
2697
|
}
|
|
2263
2698
|
),
|
|
2264
|
-
open
|
|
2699
|
+
open ? /* @__PURE__ */ jsx11("div", { className: "mt-1.5 whitespace-pre-wrap text-xs leading-[22px] text-[hsl(var(--muted-foreground))]", children: reasoning }) : null
|
|
2265
2700
|
] });
|
|
2266
2701
|
}
|
|
2267
2702
|
function getMessageText(message) {
|
|
2268
2703
|
return getTextContent(normalizeMessageContent(message.content)).trim();
|
|
2269
2704
|
}
|
|
2705
|
+
function hasRenderableMessageContent(message) {
|
|
2706
|
+
return Boolean(getMessageText(message)) || getImageParts(message.content).length > 0 || getFileParts(message.content).length > 0;
|
|
2707
|
+
}
|
|
2708
|
+
function getLastContentMessage(messages) {
|
|
2709
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
2710
|
+
if (hasRenderableMessageContent(messages[index])) return messages[index];
|
|
2711
|
+
}
|
|
2712
|
+
return null;
|
|
2713
|
+
}
|
|
2714
|
+
function getOrderedMessageParts(message, toolCalls) {
|
|
2715
|
+
const blocks = message.blocks ?? [];
|
|
2716
|
+
if (!blocks.some((block) => block.type === "text") || !blocks.some((block) => block.type === "tool_use")) return [];
|
|
2717
|
+
const toolsById = new Map(toolCalls.map((toolCall) => [toolCall.id, toolCall]));
|
|
2718
|
+
const seenToolIds = /* @__PURE__ */ new Set();
|
|
2719
|
+
const parts = [];
|
|
2720
|
+
for (const [index, block] of blocks.entries()) {
|
|
2721
|
+
if (block.type === "text" && block.content != null && block.content !== "") {
|
|
2722
|
+
const content = Array.isArray(block.content) ? block.content : String(block.content);
|
|
2723
|
+
parts.push({ type: "text", key: `text-${index}`, content });
|
|
2724
|
+
}
|
|
2725
|
+
if (block.type !== "tool_use" || !block.tool_call_id) continue;
|
|
2726
|
+
const toolCall = toolsById.get(block.tool_call_id);
|
|
2727
|
+
if (!toolCall) continue;
|
|
2728
|
+
seenToolIds.add(toolCall.id);
|
|
2729
|
+
const previous = parts[parts.length - 1];
|
|
2730
|
+
if (previous?.type === "tools") previous.toolCalls.push(toolCall);
|
|
2731
|
+
else parts.push({ type: "tools", key: `tools-${index}`, toolCalls: [toolCall] });
|
|
2732
|
+
}
|
|
2733
|
+
const missingTools = toolCalls.filter((toolCall) => !seenToolIds.has(toolCall.id));
|
|
2734
|
+
if (seenToolIds.size === 0) return [];
|
|
2735
|
+
if (missingTools.length > 0) {
|
|
2736
|
+
parts.push({ type: "tools", key: "tools-missing", toolCalls: missingTools });
|
|
2737
|
+
}
|
|
2738
|
+
return parts;
|
|
2739
|
+
}
|
|
2270
2740
|
function findLatestReasoningMessageIndex(messages) {
|
|
2271
2741
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
2272
2742
|
if (messages[index].reasoning) return index;
|
|
2273
2743
|
}
|
|
2274
2744
|
return -1;
|
|
2275
2745
|
}
|
|
2746
|
+
function resolveTurnDisplayMode({
|
|
2747
|
+
isStreaming: _isStreaming,
|
|
2748
|
+
displayMode
|
|
2749
|
+
}) {
|
|
2750
|
+
return displayMode;
|
|
2751
|
+
}
|
|
2752
|
+
function formatExecutionDuration(durationMs) {
|
|
2753
|
+
const totalSeconds = Math.max(0, Math.round(durationMs / 1e3));
|
|
2754
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
2755
|
+
const seconds = totalSeconds % 60;
|
|
2756
|
+
return minutes > 0 ? `${minutes}\u5206${seconds}\u79D2` : `${seconds}\u79D2`;
|
|
2757
|
+
}
|
|
2758
|
+
function getExecutionDurationMs({
|
|
2759
|
+
messages,
|
|
2760
|
+
isStreaming,
|
|
2761
|
+
now = Date.now()
|
|
2762
|
+
}) {
|
|
2763
|
+
const knownDuration = messages.reduce(
|
|
2764
|
+
(total, message) => {
|
|
2765
|
+
if (typeof message.duration_ms === "number" && message.duration_ms > 0) {
|
|
2766
|
+
return total + message.duration_ms;
|
|
2767
|
+
}
|
|
2768
|
+
return total + (message.tool_calls ?? []).reduce(
|
|
2769
|
+
(toolTotal, toolCall) => toolTotal + (typeof toolCall.duration_ms === "number" && toolCall.duration_ms > 0 ? toolCall.duration_ms : 0),
|
|
2770
|
+
0
|
|
2771
|
+
);
|
|
2772
|
+
},
|
|
2773
|
+
0
|
|
2774
|
+
);
|
|
2775
|
+
if (!isStreaming) return knownDuration;
|
|
2776
|
+
const startedAt = messages.map((message) => message.timestamp ? Date.parse(message.timestamp) : Number.NaN).filter((value) => Number.isFinite(value)).sort((a, b) => a - b)[0];
|
|
2777
|
+
if (startedAt === void 0) return knownDuration;
|
|
2778
|
+
return Math.max(knownDuration, now - startedAt);
|
|
2779
|
+
}
|
|
2780
|
+
function findLastExceptionalEvent(messages) {
|
|
2781
|
+
for (let messageIndex = messages.length - 1; messageIndex >= 0; messageIndex -= 1) {
|
|
2782
|
+
const messageStatus = messages[messageIndex].status;
|
|
2783
|
+
if (messageStatus === "failed") return { messageIndex, status: "error" };
|
|
2784
|
+
if (messageStatus === "interrupted") return { messageIndex, status: "cancelled" };
|
|
2785
|
+
const toolCalls = messages[messageIndex].tool_calls ?? [];
|
|
2786
|
+
for (let toolIndex = toolCalls.length - 1; toolIndex >= 0; toolIndex -= 1) {
|
|
2787
|
+
const status = toolCalls[toolIndex].status;
|
|
2788
|
+
if (status === "error" || status === "cancelled") {
|
|
2789
|
+
return { messageIndex, status };
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
return null;
|
|
2794
|
+
}
|
|
2795
|
+
function executionSummaryLabel({
|
|
2796
|
+
messages,
|
|
2797
|
+
isStreaming,
|
|
2798
|
+
durationMs,
|
|
2799
|
+
sessionStatus,
|
|
2800
|
+
askAnswers
|
|
2801
|
+
}) {
|
|
2802
|
+
if (isStreaming) {
|
|
2803
|
+
return durationMs > 0 ? `\u6B63\u5728\u6267\u884C ${formatExecutionDuration(durationMs)}` : "\u6B63\u5728\u6267\u884C";
|
|
2804
|
+
}
|
|
2805
|
+
if (sessionStatus === "waiting_for_input" && messages.some(
|
|
2806
|
+
(message) => (message.tool_calls ?? []).some(
|
|
2807
|
+
(toolCall) => formatToolName(toolCall.name) === "AskUserQuestion" && toolCall.status === "awaiting_answer" && !askAnswers?.[toolCall.id]
|
|
2808
|
+
)
|
|
2809
|
+
)) {
|
|
2810
|
+
return "\u7B49\u5F85\u8F93\u5165";
|
|
2811
|
+
}
|
|
2812
|
+
const completedLabel = durationMs > 0 ? `\u6267\u884C\u5B8C\u6210 ${formatExecutionDuration(durationMs)}` : "\u6267\u884C\u5B8C\u6210";
|
|
2813
|
+
const lastExceptionalEvent = findLastExceptionalEvent(messages);
|
|
2814
|
+
if (lastExceptionalEvent) {
|
|
2815
|
+
const recovered = messages.slice(lastExceptionalEvent.messageIndex + 1).some(hasRenderableMessageContent);
|
|
2816
|
+
if (lastExceptionalEvent.status === "error") {
|
|
2817
|
+
return recovered ? `${completedLabel} \xB7 \u90E8\u5206\u6B65\u9AA4\u5931\u8D25` : "\u6267\u884C\u5931\u8D25";
|
|
2818
|
+
}
|
|
2819
|
+
return recovered ? `${completedLabel} \xB7 \u90E8\u5206\u6B65\u9AA4\u672A\u5B8C\u6210` : "\u6267\u884C\u5DF2\u4E2D\u65AD";
|
|
2820
|
+
}
|
|
2821
|
+
return completedLabel;
|
|
2822
|
+
}
|
|
2823
|
+
function businessToolDisplayName(toolCall) {
|
|
2824
|
+
const displayName = toolCall.display_name?.trim() ?? "";
|
|
2825
|
+
if (!displayName) return "";
|
|
2826
|
+
const rawName = toolCall.name.trim();
|
|
2827
|
+
return displayName !== rawName && formatToolName(displayName) !== formatToolName(rawName) ? displayName : "";
|
|
2828
|
+
}
|
|
2829
|
+
function executionToolTypeLabel(toolCall) {
|
|
2830
|
+
switch (formatToolName(toolCall.name)) {
|
|
2831
|
+
case "WebSearch":
|
|
2832
|
+
case "WebFetch":
|
|
2833
|
+
return "\u7F51\u7EDC\u68C0\u7D22";
|
|
2834
|
+
case "Bash":
|
|
2835
|
+
case "BgBash":
|
|
2836
|
+
return "\u547D\u4EE4\u6267\u884C";
|
|
2837
|
+
case "Read":
|
|
2838
|
+
case "ReadSkill":
|
|
2839
|
+
return "\u5185\u5BB9\u8BFB\u53D6";
|
|
2840
|
+
case "Write":
|
|
2841
|
+
case "Edit":
|
|
2842
|
+
case "MultiEdit":
|
|
2843
|
+
return "\u6587\u4EF6\u5904\u7406";
|
|
2844
|
+
case "Grep":
|
|
2845
|
+
case "Glob":
|
|
2846
|
+
return "\u5185\u5BB9\u641C\u7D22";
|
|
2847
|
+
case "Agent":
|
|
2848
|
+
return "\u5B50\u4EFB\u52A1";
|
|
2849
|
+
case "search_skills":
|
|
2850
|
+
return "\u6280\u80FD\u68C0\u7D22";
|
|
2851
|
+
case "get_skill_content":
|
|
2852
|
+
return "\u8BFB\u53D6\u6280\u80FD";
|
|
2853
|
+
case "run_skill_tool":
|
|
2854
|
+
return "\u6267\u884C\u6280\u80FD";
|
|
2855
|
+
default:
|
|
2856
|
+
return businessToolDisplayName(toolCall) || "\u6267\u884C\u6B65\u9AA4";
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
function executionToolIntent(toolCall) {
|
|
2860
|
+
const normalizedName = formatToolName(toolCall.name);
|
|
2861
|
+
let args = null;
|
|
2862
|
+
try {
|
|
2863
|
+
const parsed = JSON.parse(toolCall.arguments);
|
|
2864
|
+
args = parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
2865
|
+
} catch {
|
|
2866
|
+
args = null;
|
|
2867
|
+
}
|
|
2868
|
+
const getString = (key) => {
|
|
2869
|
+
const value = args?.[key];
|
|
2870
|
+
return typeof value === "string" ? value.trim() : "";
|
|
2871
|
+
};
|
|
2872
|
+
const explicitIntent = getString("description") || getString("_meta_display_name") || getString("display_name") || "";
|
|
2873
|
+
if (explicitIntent) return explicitIntent;
|
|
2874
|
+
if (normalizedName === "search_skills") return getString("query");
|
|
2875
|
+
if (normalizedName === "get_skill_content" || normalizedName === "ReadSkill") {
|
|
2876
|
+
return getString("skill_name") || getString("skill");
|
|
2877
|
+
}
|
|
2878
|
+
if (normalizedName === "FinishTask") return getString("title");
|
|
2879
|
+
return "";
|
|
2880
|
+
}
|
|
2881
|
+
function ExecutionToolRow({ toolCall }) {
|
|
2882
|
+
const normalizedName = formatToolName(toolCall.name);
|
|
2883
|
+
const typeLabel = executionToolTypeLabel(toolCall);
|
|
2884
|
+
const intent = executionToolIntent(toolCall);
|
|
2885
|
+
const label = intent ? `${typeLabel}\uFF1A${intent}` : typeLabel;
|
|
2886
|
+
const failed = toolCall.status === "error" || toolCall.status === "cancelled";
|
|
2887
|
+
const iconClass = cn(
|
|
2888
|
+
"size-3.5 shrink-0",
|
|
2889
|
+
failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
|
|
2890
|
+
);
|
|
2891
|
+
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" || normalizedName === "get_skill_content" ? /* @__PURE__ */ jsx11(BookOpen, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Grep" || normalizedName === "Glob" || normalizedName === "search_skills" ? /* @__PURE__ */ jsx11(Search, { className: iconClass, "aria-hidden": "true" }) : normalizedName === "Agent" ? /* @__PURE__ */ jsx11(Bot, { className: iconClass, "aria-hidden": "true" }) : /* @__PURE__ */ jsx11(Wrench, { className: iconClass, "aria-hidden": "true" });
|
|
2892
|
+
const rowClassName = cn(
|
|
2893
|
+
"flex min-w-0 items-center gap-1 py-1.5 text-xs leading-[22px]",
|
|
2894
|
+
failed ? "text-red-500" : "text-[hsl(var(--muted-foreground))]"
|
|
2895
|
+
);
|
|
2896
|
+
return /* @__PURE__ */ jsxs9("div", { "data-testid": "execution-tool-intent", className: rowClassName, title: label, children: [
|
|
2897
|
+
icon,
|
|
2898
|
+
/* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: label })
|
|
2899
|
+
] });
|
|
2900
|
+
}
|
|
2276
2901
|
function AssistantTurnBlock({
|
|
2277
2902
|
messages,
|
|
2278
2903
|
isStreaming = false,
|
|
@@ -2283,53 +2908,302 @@ function AssistantTurnBlock({
|
|
|
2283
2908
|
sessionId
|
|
2284
2909
|
}) {
|
|
2285
2910
|
const hasInterrupted = messages.some((message) => message.status === "interrupted");
|
|
2286
|
-
const
|
|
2287
|
-
(message) =>
|
|
2911
|
+
const hasFailedWithoutContent = messages.some(
|
|
2912
|
+
(message) => message.status === "failed" && !hasRenderableMessageContent(message)
|
|
2913
|
+
);
|
|
2914
|
+
const finalMessage = getLastContentMessage(messages);
|
|
2915
|
+
const turnToolCalls = messages.flatMap((message) => message.tool_calls ?? []);
|
|
2916
|
+
const finalOrderedParts = finalMessage ? getOrderedMessageParts(
|
|
2917
|
+
finalMessage,
|
|
2918
|
+
(finalMessage.tool_calls ?? []).filter(
|
|
2919
|
+
(toolCall) => formatToolName(toolCall.name) !== "AskUserQuestion"
|
|
2920
|
+
)
|
|
2921
|
+
) : [];
|
|
2922
|
+
const hasExecutionProcess = messages.some(
|
|
2923
|
+
(message) => message.reasoning || (message.tool_calls?.length ?? 0) > 0
|
|
2288
2924
|
);
|
|
2289
2925
|
const latestReasoningIndex = isStreaming ? findLatestReasoningMessageIndex(messages) : -1;
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2926
|
+
const hasActionableToolCall = messages.some(
|
|
2927
|
+
(message) => message.status === "failed" || message.status === "interrupted" || (message.tool_calls ?? []).some(
|
|
2928
|
+
(toolCall) => toolCall.status === "error" || toolCall.status === "cancelled"
|
|
2929
|
+
)
|
|
2930
|
+
);
|
|
2931
|
+
const questionToolCalls = messages.flatMap(
|
|
2932
|
+
(message) => (message.tool_calls ?? []).filter(
|
|
2933
|
+
(toolCall) => formatToolName(toolCall.name) === "AskUserQuestion"
|
|
2934
|
+
)
|
|
2935
|
+
);
|
|
2936
|
+
const activeQuestionId = questionToolCalls.filter((toolCall) => toolCall.status === "pending").at(-1)?.id;
|
|
2937
|
+
const [displayMode, setDisplayMode] = useState10(
|
|
2938
|
+
() => isStreaming || hasActionableToolCall ? "detail" : "compact"
|
|
2939
|
+
);
|
|
2940
|
+
const userSelectedDisplayModeRef = useRef9(false);
|
|
2941
|
+
const wasStreamingRef = useRef9(isStreaming);
|
|
2942
|
+
useEffect8(() => {
|
|
2943
|
+
if (wasStreamingRef.current && !isStreaming && !userSelectedDisplayModeRef.current) {
|
|
2944
|
+
setDisplayMode(hasActionableToolCall ? "detail" : "compact");
|
|
2945
|
+
}
|
|
2946
|
+
wasStreamingRef.current = isStreaming;
|
|
2947
|
+
}, [hasActionableToolCall, isStreaming]);
|
|
2948
|
+
const effectiveMode = resolveTurnDisplayMode({ isStreaming, displayMode });
|
|
2949
|
+
const executionDurationMs = getExecutionDurationMs({ messages, isStreaming });
|
|
2950
|
+
const [clock, setClock] = useState10(() => Date.now());
|
|
2951
|
+
const hasLiveStartTime = messages.some(
|
|
2952
|
+
(message) => message.timestamp != null && Number.isFinite(Date.parse(message.timestamp))
|
|
2953
|
+
);
|
|
2954
|
+
useEffect8(() => {
|
|
2955
|
+
if (!isStreaming || !hasLiveStartTime) return;
|
|
2956
|
+
const timer = window.setInterval(() => setClock(Date.now()), 1e3);
|
|
2957
|
+
return () => window.clearInterval(timer);
|
|
2958
|
+
}, [hasLiveStartTime, isStreaming]);
|
|
2959
|
+
const liveExecutionDurationMs = isStreaming ? getExecutionDurationMs({ messages, isStreaming, now: clock }) : executionDurationMs;
|
|
2960
|
+
const memoryRefs = collectMemoryRefs(messages);
|
|
2961
|
+
if (!hasExecutionProcess) {
|
|
2962
|
+
return /* @__PURE__ */ jsxs9(
|
|
2963
|
+
"div",
|
|
2964
|
+
{
|
|
2965
|
+
"aria-busy": isStreaming || void 0,
|
|
2966
|
+
className: "blade-chat-assistant-turn flex flex-col gap-3",
|
|
2967
|
+
children: [
|
|
2968
|
+
memoryRefs.length > 0 ? /* @__PURE__ */ jsx11(MemoryRefsHint, { refs: memoryRefs }) : null,
|
|
2969
|
+
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" }),
|
|
2970
|
+
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" }),
|
|
2971
|
+
messages.map((message, index) => {
|
|
2972
|
+
return hasRenderableMessageContent(message) ? /* @__PURE__ */ jsx11(
|
|
2973
|
+
"div",
|
|
2306
2974
|
{
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2975
|
+
className: "flex flex-col gap-3",
|
|
2976
|
+
children: /* @__PURE__ */ jsx11(
|
|
2977
|
+
AssistantMessageContent,
|
|
2978
|
+
{
|
|
2979
|
+
message,
|
|
2980
|
+
sessionId,
|
|
2981
|
+
streaming: isStreaming && index === messages.length - 1
|
|
2982
|
+
}
|
|
2983
|
+
)
|
|
2984
|
+
},
|
|
2985
|
+
message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
|
|
2986
|
+
) : null;
|
|
2987
|
+
})
|
|
2988
|
+
]
|
|
2989
|
+
}
|
|
2990
|
+
);
|
|
2991
|
+
}
|
|
2992
|
+
return /* @__PURE__ */ jsxs9(
|
|
2993
|
+
"div",
|
|
2994
|
+
{
|
|
2995
|
+
"aria-busy": isStreaming || void 0,
|
|
2996
|
+
className: "blade-chat-assistant-turn flex flex-col gap-3",
|
|
2997
|
+
children: [
|
|
2998
|
+
memoryRefs.length > 0 ? /* @__PURE__ */ jsx11(MemoryRefsHint, { refs: memoryRefs }) : null,
|
|
2999
|
+
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" }),
|
|
3000
|
+
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" }),
|
|
3001
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex w-full items-start gap-2.5", children: [
|
|
3002
|
+
/* @__PURE__ */ jsx11(
|
|
3003
|
+
"span",
|
|
3004
|
+
{
|
|
3005
|
+
className: "grid size-[30px] shrink-0 place-items-center rounded-full bg-[hsl(var(--muted)/0.55)] text-[hsl(var(--foreground))]",
|
|
3006
|
+
"aria-hidden": "true",
|
|
3007
|
+
children: /* @__PURE__ */ jsx11(Bot, { size: 16 })
|
|
3008
|
+
}
|
|
3009
|
+
),
|
|
3010
|
+
/* @__PURE__ */ jsxs9("div", { className: "min-w-0 flex-1 pt-0.5", children: [
|
|
3011
|
+
/* @__PURE__ */ jsxs9(
|
|
3012
|
+
"button",
|
|
3013
|
+
{
|
|
3014
|
+
type: "button",
|
|
3015
|
+
onClick: () => {
|
|
3016
|
+
userSelectedDisplayModeRef.current = true;
|
|
3017
|
+
setDisplayMode(displayMode === "detail" ? "compact" : "detail");
|
|
2323
3018
|
},
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
3019
|
+
"aria-expanded": effectiveMode === "detail",
|
|
3020
|
+
"aria-label": effectiveMode === "detail" ? "\u6536\u8D77\u6267\u884C\u8FC7\u7A0B" : "\u5C55\u5F00\u6267\u884C\u8FC7\u7A0B",
|
|
3021
|
+
"data-testid": "assistant-execution-summary",
|
|
3022
|
+
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",
|
|
3023
|
+
children: [
|
|
3024
|
+
/* @__PURE__ */ jsx11("span", { className: "min-w-0 truncate", children: executionSummaryLabel({
|
|
3025
|
+
messages,
|
|
3026
|
+
isStreaming,
|
|
3027
|
+
durationMs: liveExecutionDurationMs,
|
|
3028
|
+
sessionStatus,
|
|
3029
|
+
askAnswers
|
|
3030
|
+
}) }),
|
|
3031
|
+
/* @__PURE__ */ jsx11(
|
|
3032
|
+
ChevronRight,
|
|
3033
|
+
{
|
|
3034
|
+
size: 14,
|
|
3035
|
+
className: cn(
|
|
3036
|
+
"shrink-0 transition-transform duration-300",
|
|
3037
|
+
effectiveMode === "detail" && "rotate-90"
|
|
3038
|
+
),
|
|
3039
|
+
"aria-hidden": "true"
|
|
3040
|
+
}
|
|
3041
|
+
)
|
|
3042
|
+
]
|
|
3043
|
+
}
|
|
3044
|
+
),
|
|
3045
|
+
/* @__PURE__ */ jsx11("div", { className: "mt-3 h-px w-full bg-[hsl(var(--border)/0.75)]" })
|
|
3046
|
+
] })
|
|
3047
|
+
] }),
|
|
3048
|
+
effectiveMode === "detail" ? /* @__PURE__ */ jsx11("div", { className: "ml-10 flex flex-col gap-3 pt-1", children: messages.map((message, index) => {
|
|
3049
|
+
const isLast = index === messages.length - 1;
|
|
3050
|
+
const streamingThis = isStreaming && isLast;
|
|
3051
|
+
const text = getMessageText(message);
|
|
3052
|
+
const toolCalls = (message.tool_calls ?? []).filter(
|
|
3053
|
+
(toolCall) => formatToolName(toolCall.name) !== "AskUserQuestion"
|
|
3054
|
+
);
|
|
3055
|
+
const orderedParts = getOrderedMessageParts(message, toolCalls);
|
|
3056
|
+
const showReasoning = !!message.reasoning && isStreaming && index === latestReasoningIndex;
|
|
3057
|
+
return /* @__PURE__ */ jsxs9(
|
|
3058
|
+
"div",
|
|
3059
|
+
{
|
|
3060
|
+
className: "flex flex-col gap-3",
|
|
3061
|
+
children: [
|
|
3062
|
+
showReasoning && message.reasoning ? /* @__PURE__ */ jsx11(ThinkingBlock, { reasoning: message.reasoning, isStreaming: streamingThis && !text }) : null,
|
|
3063
|
+
orderedParts.length > 0 ? orderedParts.map(
|
|
3064
|
+
(part) => part.type === "text" ? /* @__PURE__ */ jsx11(
|
|
3065
|
+
AssistantMessageContent,
|
|
3066
|
+
{
|
|
3067
|
+
message: { ...message, content: part.content, tool_calls: turnToolCalls },
|
|
3068
|
+
sessionId,
|
|
3069
|
+
streaming: streamingThis,
|
|
3070
|
+
compact: true
|
|
3071
|
+
},
|
|
3072
|
+
part.key
|
|
3073
|
+
) : /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-0.5", children: part.toolCalls.map((toolCall) => {
|
|
3074
|
+
const custom = toolCallRenderer?.(toolCall);
|
|
3075
|
+
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);
|
|
3076
|
+
}) }, part.key)
|
|
3077
|
+
) : hasRenderableMessageContent(message) && message !== finalMessage ? /* @__PURE__ */ jsx11(
|
|
3078
|
+
AssistantMessageContent,
|
|
3079
|
+
{
|
|
3080
|
+
message,
|
|
3081
|
+
sessionId,
|
|
3082
|
+
streaming: streamingThis,
|
|
3083
|
+
compact: true
|
|
3084
|
+
}
|
|
3085
|
+
) : null,
|
|
3086
|
+
orderedParts.length === 0 && toolCalls.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "flex flex-col gap-0.5", children: toolCalls.map((toolCall) => {
|
|
3087
|
+
const custom = toolCallRenderer?.(toolCall);
|
|
3088
|
+
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);
|
|
3089
|
+
}) }) : null
|
|
3090
|
+
]
|
|
3091
|
+
},
|
|
3092
|
+
message.entry_id ?? `${message.timestamp ?? "assistant"}-${index}`
|
|
3093
|
+
);
|
|
3094
|
+
}) }) : null,
|
|
3095
|
+
finalMessage && (effectiveMode === "compact" || finalOrderedParts.length === 0) ? /* @__PURE__ */ jsx11("div", { className: "ml-10", children: /* @__PURE__ */ jsx11(
|
|
3096
|
+
AssistantMessageContent,
|
|
3097
|
+
{
|
|
3098
|
+
message: finalMessage,
|
|
3099
|
+
sessionId,
|
|
3100
|
+
streaming: isStreaming && finalMessage === messages[messages.length - 1]
|
|
3101
|
+
}
|
|
3102
|
+
) }) : null,
|
|
3103
|
+
questionToolCalls.map((toolCall) => /* @__PURE__ */ jsx11(
|
|
3104
|
+
ToolCallBlock,
|
|
3105
|
+
{
|
|
3106
|
+
toolCall,
|
|
3107
|
+
answerData: askAnswers?.[toolCall.id],
|
|
3108
|
+
onAnswer,
|
|
3109
|
+
answered: sessionStatus !== "waiting_for_input",
|
|
3110
|
+
sessionStatus,
|
|
3111
|
+
isActiveQuestion: toolCall.id === activeQuestionId,
|
|
3112
|
+
renderer: toolCallRenderer
|
|
3113
|
+
},
|
|
3114
|
+
toolCall.id
|
|
3115
|
+
))
|
|
3116
|
+
]
|
|
3117
|
+
}
|
|
3118
|
+
);
|
|
3119
|
+
}
|
|
3120
|
+
function collectMemoryRefs(messages) {
|
|
3121
|
+
const refs = /* @__PURE__ */ new Map();
|
|
3122
|
+
for (const message of messages) {
|
|
3123
|
+
for (const ref of message.memory_refs ?? []) if (!refs.has(ref.id)) refs.set(ref.id, ref);
|
|
3124
|
+
}
|
|
3125
|
+
return [...refs.values()];
|
|
3126
|
+
}
|
|
3127
|
+
function MemoryRefsHint({ refs }) {
|
|
3128
|
+
const [expanded, setExpanded] = useState10(false);
|
|
3129
|
+
const label = refs.some((ref) => ref.skill_name) ? "\u53C2\u8003\u4E86\u8BE5\u6280\u80FD\u7684\u5386\u53F2\u7ECF\u9A8C" : "\u53C2\u8003\u4E86\u5386\u53F2\u7ECF\u9A8C";
|
|
3130
|
+
return /* @__PURE__ */ jsxs9("div", { className: "blade-chat-memory-refs ml-1 w-full max-w-[680px]", children: [
|
|
3131
|
+
/* @__PURE__ */ jsxs9("button", { type: "button", onClick: () => setExpanded((value) => !value), className: "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[hsl(var(--primary)/0.22)] bg-[hsl(var(--primary)/0.07)] px-3 text-xs font-medium text-[hsl(var(--primary))]", children: [
|
|
3132
|
+
/* @__PURE__ */ jsx11(BookOpen, { size: 12 }),
|
|
3133
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
3134
|
+
label,
|
|
3135
|
+
"\uFF08",
|
|
3136
|
+
refs.length,
|
|
3137
|
+
"\uFF09"
|
|
3138
|
+
] }),
|
|
3139
|
+
/* @__PURE__ */ jsx11(ChevronRight, { size: 10, className: cn("transition-transform", expanded && "rotate-90") })
|
|
3140
|
+
] }),
|
|
3141
|
+
expanded ? /* @__PURE__ */ jsx11("div", { className: "mt-2 flex flex-col gap-2 rounded-xl border border-[hsl(var(--border)/0.8)] bg-[hsl(var(--muted)/0.28)] p-2.5", children: refs.map((ref) => /* @__PURE__ */ jsxs9("div", { className: "rounded-lg border border-[hsl(var(--border)/0.55)] bg-[hsl(var(--background)/0.72)] px-3 py-2.5 text-xs", children: [
|
|
3142
|
+
/* @__PURE__ */ jsx11("p", { className: "line-clamp-2 break-words leading-5", children: ref.content_preview }),
|
|
3143
|
+
ref.skill_name ? /* @__PURE__ */ jsx11("span", { className: "mt-1 inline-flex text-[10px] text-[hsl(var(--primary))]", children: ref.skill_name }) : null
|
|
3144
|
+
] }, ref.id)) }) : null
|
|
3145
|
+
] });
|
|
3146
|
+
}
|
|
3147
|
+
function AssistantMessageContent({
|
|
3148
|
+
message,
|
|
3149
|
+
sessionId,
|
|
3150
|
+
streaming,
|
|
3151
|
+
compact = false
|
|
3152
|
+
}) {
|
|
3153
|
+
const text = getMessageText(message);
|
|
3154
|
+
const imageParts = getImageParts(message.content);
|
|
3155
|
+
const fileParts = getFileParts(message.content);
|
|
3156
|
+
const failed = message.status === "failed";
|
|
3157
|
+
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;
|
|
3158
|
+
const textContent = text ? /* @__PURE__ */ jsx11(
|
|
3159
|
+
"div",
|
|
3160
|
+
{
|
|
3161
|
+
className: cn(
|
|
3162
|
+
"blade-chat-assistant-text",
|
|
3163
|
+
compact ? "text-xs leading-[22px] text-[hsl(var(--foreground))]" : "text-[15px] leading-8 text-[hsl(var(--foreground))]"
|
|
3164
|
+
),
|
|
3165
|
+
children: /* @__PURE__ */ jsx11(
|
|
3166
|
+
MarkdownContent,
|
|
3167
|
+
{
|
|
3168
|
+
mode: streaming ? "streaming" : "static",
|
|
3169
|
+
className: "blade-chat-prose",
|
|
3170
|
+
sessionId,
|
|
3171
|
+
children: text
|
|
3172
|
+
}
|
|
3173
|
+
)
|
|
3174
|
+
}
|
|
3175
|
+
) : null;
|
|
3176
|
+
if (imageParts.length === 0 && fileParts.length === 0) {
|
|
3177
|
+
if (!failed) return textContent;
|
|
3178
|
+
return failedBadge || textContent ? /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-2", children: [
|
|
3179
|
+
failedBadge,
|
|
3180
|
+
textContent
|
|
3181
|
+
] }) : null;
|
|
3182
|
+
}
|
|
3183
|
+
return /* @__PURE__ */ jsxs9("div", { className: "flex flex-col gap-3", children: [
|
|
3184
|
+
failedBadge,
|
|
3185
|
+
imageParts.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx11(
|
|
3186
|
+
"img",
|
|
3187
|
+
{
|
|
3188
|
+
src: part.image_url.url,
|
|
3189
|
+
alt: "\u6D88\u606F\u9644\u4EF6",
|
|
3190
|
+
className: "max-h-72 rounded-xl border border-[hsl(var(--border))] object-cover"
|
|
3191
|
+
},
|
|
3192
|
+
part.image_url.url
|
|
3193
|
+
)) }) : null,
|
|
3194
|
+
fileParts.length > 0 ? /* @__PURE__ */ jsx11("div", { className: "flex flex-wrap gap-1.5", children: fileParts.map((part) => /* @__PURE__ */ jsxs9(
|
|
3195
|
+
"div",
|
|
3196
|
+
{
|
|
3197
|
+
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))]",
|
|
3198
|
+
title: part.name,
|
|
3199
|
+
children: [
|
|
3200
|
+
/* @__PURE__ */ jsx11(FileText, { size: 12, className: "shrink-0" }),
|
|
3201
|
+
/* @__PURE__ */ jsx11("span", { className: "max-w-56 truncate", children: part.name })
|
|
3202
|
+
]
|
|
3203
|
+
},
|
|
3204
|
+
`${part.name}-${part.data.slice(0, 32)}`
|
|
3205
|
+
)) }) : null,
|
|
3206
|
+
textContent
|
|
2333
3207
|
] });
|
|
2334
3208
|
}
|
|
2335
3209
|
|
|
@@ -2386,7 +3260,7 @@ var RenderErrorBoundary = class extends Component {
|
|
|
2386
3260
|
};
|
|
2387
3261
|
|
|
2388
3262
|
// src/components/PostChatFollowupBlock.tsx
|
|
2389
|
-
import { useCallback as
|
|
3263
|
+
import { useCallback as useCallback6, useEffect as useEffect9, useRef as useRef10, useState as useState11 } from "react";
|
|
2390
3264
|
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2391
3265
|
function emitInteraction(callback, event) {
|
|
2392
3266
|
try {
|
|
@@ -2397,9 +3271,6 @@ function emitInteraction(callback, event) {
|
|
|
2397
3271
|
function basename(path) {
|
|
2398
3272
|
return path.split(/[\\/]/).filter(Boolean).pop() || path;
|
|
2399
3273
|
}
|
|
2400
|
-
function isVideo(path) {
|
|
2401
|
-
return /\.(?:mp4|mov|webm|mkv|avi|m4v)$/i.test(path);
|
|
2402
|
-
}
|
|
2403
3274
|
function ArtifactCard({
|
|
2404
3275
|
artifact,
|
|
2405
3276
|
sessionId,
|
|
@@ -2409,7 +3280,7 @@ function ArtifactCard({
|
|
|
2409
3280
|
onArtifactOpened
|
|
2410
3281
|
}) {
|
|
2411
3282
|
const client = useBladeClient();
|
|
2412
|
-
const [downloading, setDownloading] =
|
|
3283
|
+
const [downloading, setDownloading] = useState11(false);
|
|
2413
3284
|
const name = artifact.label || basename(artifact.target);
|
|
2414
3285
|
if (artifact.kind === "link") {
|
|
2415
3286
|
return /* @__PURE__ */ jsxs11(
|
|
@@ -2430,44 +3301,48 @@ ${artifact.target}`,
|
|
|
2430
3301
|
}
|
|
2431
3302
|
);
|
|
2432
3303
|
}
|
|
2433
|
-
const
|
|
2434
|
-
|
|
2435
|
-
|
|
3304
|
+
const fileName = basename(artifact.target);
|
|
3305
|
+
const downloadUrl = sessionId ? client.buildAuthedUrl(
|
|
3306
|
+
`/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(artifact.target)}`
|
|
3307
|
+
) : void 0;
|
|
3308
|
+
const handleDownload = async (event) => {
|
|
3309
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
|
|
3310
|
+
event.preventDefault();
|
|
3311
|
+
if (!sessionId || downloading) return;
|
|
3312
|
+
setDownloading(true);
|
|
3313
|
+
emitInteraction(onInteraction, {
|
|
3314
|
+
type: "artifact_download_started",
|
|
3315
|
+
sessionId,
|
|
3316
|
+
assistantEntryId,
|
|
3317
|
+
artifactIndex,
|
|
3318
|
+
artifactKind: "file"
|
|
3319
|
+
});
|
|
3320
|
+
try {
|
|
3321
|
+
await client.sessions.downloadFile(sessionId, artifact.target, fileName);
|
|
3322
|
+
emitInteraction(onInteraction, {
|
|
3323
|
+
type: "artifact_download_succeeded",
|
|
3324
|
+
sessionId,
|
|
3325
|
+
assistantEntryId,
|
|
3326
|
+
artifactIndex,
|
|
3327
|
+
artifactKind: "file"
|
|
3328
|
+
});
|
|
3329
|
+
} catch {
|
|
3330
|
+
} finally {
|
|
3331
|
+
setDownloading(false);
|
|
3332
|
+
}
|
|
3333
|
+
};
|
|
3334
|
+
return /* @__PURE__ */ jsx13(
|
|
3335
|
+
"a",
|
|
2436
3336
|
{
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
onClick:
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
artifactIndex,
|
|
2447
|
-
artifactKind: "file"
|
|
2448
|
-
});
|
|
2449
|
-
try {
|
|
2450
|
-
await client.sessions.downloadFile(sessionId, artifact.target, basename(artifact.target));
|
|
2451
|
-
emitInteraction(onInteraction, {
|
|
2452
|
-
type: "artifact_download_succeeded",
|
|
2453
|
-
sessionId,
|
|
2454
|
-
assistantEntryId,
|
|
2455
|
-
artifactIndex,
|
|
2456
|
-
artifactKind: "file"
|
|
2457
|
-
});
|
|
2458
|
-
} catch {
|
|
2459
|
-
} finally {
|
|
2460
|
-
setDownloading(false);
|
|
2461
|
-
}
|
|
2462
|
-
},
|
|
2463
|
-
title: name,
|
|
2464
|
-
"aria-label": `\u4E0B\u8F7D ${name}`,
|
|
2465
|
-
className: "group relative flex min-w-0 items-center gap-1.5 rounded-md border border-[hsl(var(--border))] bg-[hsl(var(--card))] px-2 py-1.5 text-left text-xs text-[hsl(var(--card-foreground))] hover:bg-[hsl(var(--accent))] disabled:opacity-60",
|
|
2466
|
-
children: [
|
|
2467
|
-
/* @__PURE__ */ jsx13(Icon2, { size: 15, className: "shrink-0 text-[hsl(var(--primary))]" }),
|
|
2468
|
-
/* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1 truncate font-medium", children: name }),
|
|
2469
|
-
/* @__PURE__ */ jsx13(Download, { size: 13, className: "absolute right-2 opacity-0 group-hover:opacity-100 group-focus-visible:opacity-100" })
|
|
2470
|
-
]
|
|
3337
|
+
href: downloadUrl,
|
|
3338
|
+
download: fileName,
|
|
3339
|
+
onClick: handleDownload,
|
|
3340
|
+
title: fileName,
|
|
3341
|
+
"aria-label": `\u4E0B\u8F7D\u6587\u4EF6\uFF1A${fileName}`,
|
|
3342
|
+
"aria-disabled": !sessionId || void 0,
|
|
3343
|
+
"aria-busy": downloading || void 0,
|
|
3344
|
+
className: "min-w-0 cursor-pointer break-all text-xs text-[hsl(var(--primary))] underline aria-disabled:cursor-not-allowed aria-disabled:opacity-60 aria-busy:cursor-wait",
|
|
3345
|
+
children: fileName
|
|
2471
3346
|
}
|
|
2472
3347
|
);
|
|
2473
3348
|
}
|
|
@@ -2511,15 +3386,15 @@ function ResultFeedback({
|
|
|
2511
3386
|
onFeedbackSaved
|
|
2512
3387
|
}) {
|
|
2513
3388
|
const client = useBladeClient();
|
|
2514
|
-
const [saved, setSaved] =
|
|
2515
|
-
const [helpful, setHelpful] =
|
|
2516
|
-
const [reason, setReason] =
|
|
2517
|
-
const [saving, setSaving] =
|
|
2518
|
-
const [saveError, setSaveError] =
|
|
2519
|
-
const reportedShown =
|
|
2520
|
-
const latestChoice =
|
|
3389
|
+
const [saved, setSaved] = useState11(savedFeedback ?? null);
|
|
3390
|
+
const [helpful, setHelpful] = useState11(savedFeedback?.helpful ?? null);
|
|
3391
|
+
const [reason, setReason] = useState11(savedFeedback?.reason ?? null);
|
|
3392
|
+
const [saving, setSaving] = useState11(false);
|
|
3393
|
+
const [saveError, setSaveError] = useState11(false);
|
|
3394
|
+
const reportedShown = useRef10(false);
|
|
3395
|
+
const latestChoice = useRef10(null);
|
|
2521
3396
|
const eligible = followup.feedback_eligible === true && Boolean(sessionId) && !isViewer;
|
|
2522
|
-
|
|
3397
|
+
useEffect9(() => {
|
|
2523
3398
|
if (!eligible || reportedShown.current) return;
|
|
2524
3399
|
reportedShown.current = true;
|
|
2525
3400
|
emitInteraction(onInteraction, {
|
|
@@ -2528,13 +3403,13 @@ function ResultFeedback({
|
|
|
2528
3403
|
assistantEntryId: followup.assistant_entry_id
|
|
2529
3404
|
});
|
|
2530
3405
|
}, [eligible, followup.assistant_entry_id, onInteraction, sessionId]);
|
|
2531
|
-
|
|
3406
|
+
useEffect9(() => {
|
|
2532
3407
|
if (!savedFeedback || latestChoice.current) return;
|
|
2533
3408
|
setSaved(savedFeedback);
|
|
2534
3409
|
setHelpful(savedFeedback.helpful);
|
|
2535
3410
|
setReason(savedFeedback.reason);
|
|
2536
3411
|
}, [savedFeedback]);
|
|
2537
|
-
const submit =
|
|
3412
|
+
const submit = useCallback6(
|
|
2538
3413
|
async (nextHelpful, nextReason) => {
|
|
2539
3414
|
if (!sessionId) return;
|
|
2540
3415
|
const choice = { helpful: nextHelpful, reason: nextReason };
|
|
@@ -2640,14 +3515,14 @@ function PostChatFollowupBlock({
|
|
|
2640
3515
|
savedFeedback,
|
|
2641
3516
|
onFeedbackSaved
|
|
2642
3517
|
}) {
|
|
2643
|
-
const [expanded, setExpanded] =
|
|
2644
|
-
const adopted =
|
|
2645
|
-
const reportedSuggestions =
|
|
2646
|
-
const reportedArtifacts =
|
|
2647
|
-
const openedArtifacts =
|
|
3518
|
+
const [expanded, setExpanded] = useState11(false);
|
|
3519
|
+
const adopted = useRef10(/* @__PURE__ */ new Set());
|
|
3520
|
+
const reportedSuggestions = useRef10(false);
|
|
3521
|
+
const reportedArtifacts = useRef10(/* @__PURE__ */ new Set());
|
|
3522
|
+
const openedArtifacts = useRef10(/* @__PURE__ */ new Set());
|
|
2648
3523
|
const artifacts = followup.final_artifacts ?? [];
|
|
2649
3524
|
const visibleArtifacts = expanded ? artifacts : artifacts.slice(0, 3);
|
|
2650
|
-
|
|
3525
|
+
useEffect9(() => {
|
|
2651
3526
|
if (!reportedSuggestions.current && followup.suggestions.length > 0) {
|
|
2652
3527
|
reportedSuggestions.current = true;
|
|
2653
3528
|
emitInteraction(onInteraction, {
|
|
@@ -2677,7 +3552,7 @@ function PostChatFollowupBlock({
|
|
|
2677
3552
|
sessionId,
|
|
2678
3553
|
visibleArtifacts
|
|
2679
3554
|
]);
|
|
2680
|
-
const reportArtifactOpened =
|
|
3555
|
+
const reportArtifactOpened = useCallback6(
|
|
2681
3556
|
(artifactIndex, artifactKind) => {
|
|
2682
3557
|
if (openedArtifacts.current.has(artifactIndex)) return;
|
|
2683
3558
|
openedArtifacts.current.add(artifactIndex);
|
|
@@ -2771,7 +3646,12 @@ function PostChatFollowupBlock({
|
|
|
2771
3646
|
}
|
|
2772
3647
|
|
|
2773
3648
|
// src/components/UserMessageBubble.tsx
|
|
2774
|
-
import {
|
|
3649
|
+
import {
|
|
3650
|
+
chatErrorForDisplay,
|
|
3651
|
+
getFileParts as getFileParts2,
|
|
3652
|
+
getImageParts as getImageParts2,
|
|
3653
|
+
getTextContent as getTextContent2
|
|
3654
|
+
} from "@blade-hq/agent-client";
|
|
2775
3655
|
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2776
3656
|
function isUserMessage(message) {
|
|
2777
3657
|
return message.role === "user";
|
|
@@ -2782,8 +3662,8 @@ function isErrorMessage(message) {
|
|
|
2782
3662
|
var isSending = (message) => message.status === "streaming";
|
|
2783
3663
|
function UserMessageBubble({ message, className }) {
|
|
2784
3664
|
const text = getTextContent2(message.content).trim();
|
|
2785
|
-
const fileParts =
|
|
2786
|
-
const imageParts =
|
|
3665
|
+
const fileParts = getFileParts2(message.content);
|
|
3666
|
+
const imageParts = getImageParts2(message.content);
|
|
2787
3667
|
return /* @__PURE__ */ jsx14("div", { className: cn("blade-chat-user-row flex justify-end", className), children: /* @__PURE__ */ jsxs12("div", { className: "blade-chat-user-col flex max-w-[72%] flex-col items-end gap-3", children: [
|
|
2788
3668
|
imageParts.length > 0 && /* @__PURE__ */ jsx14("div", { className: "grid gap-2", children: imageParts.map((part) => /* @__PURE__ */ jsx14(
|
|
2789
3669
|
"img",
|
|
@@ -2816,8 +3696,8 @@ function ErrorMessageBlock({
|
|
|
2816
3696
|
message,
|
|
2817
3697
|
className
|
|
2818
3698
|
}) {
|
|
2819
|
-
const text = getTextContent2(message.content);
|
|
2820
|
-
return /* @__PURE__ */ jsx14("div", { className: cn("blade-chat-error-row flex justify-
|
|
3699
|
+
const text = chatErrorForDisplay(getTextContent2(message.content));
|
|
3700
|
+
return /* @__PURE__ */ jsx14("div", { className: cn("blade-chat-error-row flex min-w-0 justify-start", className), children: /* @__PURE__ */ jsx14("div", { className: "blade-chat-error-block min-w-0 max-w-full whitespace-pre-wrap break-words border-l-[3px] border-[hsl(var(--border))] px-3 py-1 text-left text-sm leading-7 text-[hsl(var(--muted-foreground))] [overflow-wrap:anywhere]", children: text }) });
|
|
2821
3701
|
}
|
|
2822
3702
|
|
|
2823
3703
|
// src/components/MessageList.tsx
|
|
@@ -2870,10 +3750,14 @@ function MessageList({
|
|
|
2870
3750
|
resultFeedbackByEntry = /* @__PURE__ */ new Map(),
|
|
2871
3751
|
onResultFeedbackSaved
|
|
2872
3752
|
}) {
|
|
3753
|
+
const userMessages = messages.filter((message) => isUserMessage(message));
|
|
3754
|
+
const latestUserMessage = userMessages.at(-1);
|
|
3755
|
+
const shouldPinLatestUser = latestUserMessage != null && (latestUserMessage.entry_id == null || latestUserMessage.entry_id.startsWith("local-user-"));
|
|
2873
3756
|
const renderBlocks = useMemo7(() => {
|
|
2874
3757
|
const visible = messages.filter((message) => {
|
|
2875
3758
|
if ((message.loop_name ?? "root") !== "root") return false;
|
|
2876
3759
|
if (isHiddenInternalMessage(message)) return false;
|
|
3760
|
+
if (message.kind === "context") return false;
|
|
2877
3761
|
if (message.kind === "compaction") return true;
|
|
2878
3762
|
return message.role !== "tool" || getPlanningDividerKind(message) !== null;
|
|
2879
3763
|
});
|
|
@@ -2916,7 +3800,7 @@ function MessageList({
|
|
|
2916
3800
|
blocks.push({
|
|
2917
3801
|
type: "message",
|
|
2918
3802
|
message,
|
|
2919
|
-
key: message.entry_id ?? `${message.role}-${blocks.length}`
|
|
3803
|
+
key: message.render_id ?? message.entry_id ?? `${message.role}-${blocks.length}`
|
|
2920
3804
|
});
|
|
2921
3805
|
}
|
|
2922
3806
|
flushAssistant();
|
|
@@ -2943,98 +3827,151 @@ function MessageList({
|
|
|
2943
3827
|
}
|
|
2944
3828
|
return blocks;
|
|
2945
3829
|
}, [messages, isStreaming]);
|
|
2946
|
-
return /* @__PURE__ */
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
3830
|
+
return /* @__PURE__ */ jsxs13("div", { className: cn("blade-chat-messages relative min-h-0 flex-1", className), children: [
|
|
3831
|
+
isStreaming ? /* @__PURE__ */ jsx15("output", { className: "sr-only", children: "\u6B63\u5728\u751F\u6210\u56DE\u590D" }) : null,
|
|
3832
|
+
/* @__PURE__ */ jsxs13(
|
|
3833
|
+
StickToBottom,
|
|
3834
|
+
{
|
|
3835
|
+
className: "h-full overflow-y-hidden",
|
|
3836
|
+
initial: "instant",
|
|
3837
|
+
resize: "instant",
|
|
3838
|
+
children: [
|
|
3839
|
+
/* @__PURE__ */ jsx15(StickToBottom.Content, { className: "blade-chat-messages-scroll", children: /* @__PURE__ */ jsx15("div", { className: "blade-chat-messages-content mx-auto max-w-[748px]", children: /* @__PURE__ */ jsxs13("div", { className: "flex min-w-0 flex-col", children: [
|
|
3840
|
+
renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs13("div", { className: "blade-chat-empty", children: [
|
|
3841
|
+
/* @__PURE__ */ jsx15(MessageSquare, { size: 40, strokeWidth: 1.5 }),
|
|
3842
|
+
/* @__PURE__ */ jsx15("span", { className: "text-base font-medium", children: "\u5F00\u59CB\u5BF9\u8BDD" }),
|
|
3843
|
+
/* @__PURE__ */ jsx15("span", { className: "text-sm opacity-60", children: "\u5728\u4E0B\u65B9\u8F93\u5165\u6D88\u606F\u5F00\u59CB\u804A\u5929" })
|
|
3844
|
+
] }) : renderBlocks.map((block) => {
|
|
3845
|
+
if (block.type === "message") {
|
|
3846
|
+
return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.message.entry_id, children: isUserMessage(block.message) ? /* @__PURE__ */ jsx15(UserMessageBubble, { message: block.message }) : isErrorMessage(block.message) ? /* @__PURE__ */ jsx15(ErrorMessageBlock, { message: block.message }) : null }, block.key);
|
|
3847
|
+
}
|
|
3848
|
+
if (block.type === "assistant_turn") {
|
|
3849
|
+
const blockFeedback = block.messages.map(
|
|
3850
|
+
(message) => message.entry_id ? resultFeedbackByEntry.get(message.entry_id) : void 0
|
|
3851
|
+
).find((feedback) => feedback != null);
|
|
3852
|
+
const hasActiveFollowup = Boolean(
|
|
3853
|
+
postChatFollowup && block.messages.some(
|
|
3854
|
+
(message) => message.entry_id === postChatFollowup.assistant_entry_id
|
|
3855
|
+
)
|
|
3856
|
+
);
|
|
3857
|
+
return /* @__PURE__ */ jsx15("div", { "data-entry-id": block.messages[0]?.entry_id, children: /* @__PURE__ */ jsxs13(
|
|
3858
|
+
RenderErrorBoundary,
|
|
2974
3859
|
{
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
3860
|
+
label: "\u52A9\u624B\u6D88\u606F",
|
|
3861
|
+
details: block.key,
|
|
3862
|
+
resetKey: getMessageResetSignature(block.messages),
|
|
3863
|
+
children: [
|
|
3864
|
+
/* @__PURE__ */ jsx15(
|
|
3865
|
+
AssistantTurnBlock,
|
|
3866
|
+
{
|
|
3867
|
+
messages: block.messages,
|
|
3868
|
+
isStreaming: block.isStreaming,
|
|
3869
|
+
askAnswers,
|
|
3870
|
+
onAnswer,
|
|
3871
|
+
sessionStatus,
|
|
3872
|
+
toolCallRenderer,
|
|
3873
|
+
sessionId
|
|
3874
|
+
}
|
|
3875
|
+
),
|
|
3876
|
+
blockFeedback && !hasActiveFollowup ? /* @__PURE__ */ jsx15(HistoricalResultFeedback, { feedback: blockFeedback }) : null,
|
|
3877
|
+
hasActiveFollowup && postChatFollowup ? /* @__PURE__ */ jsx15(
|
|
3878
|
+
PostChatFollowupBlock,
|
|
3879
|
+
{
|
|
3880
|
+
followup: postChatFollowup,
|
|
3881
|
+
sessionId,
|
|
3882
|
+
onSuggestion,
|
|
3883
|
+
isViewer,
|
|
3884
|
+
onInteraction: onFollowupInteraction,
|
|
3885
|
+
savedFeedback: blockFeedback,
|
|
3886
|
+
onFeedbackSaved: onResultFeedbackSaved
|
|
3887
|
+
}
|
|
3888
|
+
) : null
|
|
3889
|
+
]
|
|
2982
3890
|
}
|
|
2983
|
-
),
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
3891
|
+
) }, block.key);
|
|
3892
|
+
}
|
|
3893
|
+
if (block.type === "compaction") {
|
|
3894
|
+
return /* @__PURE__ */ jsxs13(
|
|
3895
|
+
"div",
|
|
2987
3896
|
{
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
}
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
"div",
|
|
3897
|
+
className: "flex items-center gap-2 text-xs text-[hsl(var(--muted-foreground))]",
|
|
3898
|
+
children: [
|
|
3899
|
+
/* @__PURE__ */ jsx15(Layers, { size: 12 }),
|
|
3900
|
+
/* @__PURE__ */ jsx15("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
|
|
3901
|
+
]
|
|
3902
|
+
},
|
|
3903
|
+
block.key
|
|
3904
|
+
);
|
|
3905
|
+
}
|
|
3906
|
+
return /* @__PURE__ */ jsx15(PlanningDivider, { kind: block.kind }, block.key);
|
|
3907
|
+
}),
|
|
3908
|
+
sessionStatus === "interrupted" && !isStreaming ? /* @__PURE__ */ jsx15("div", { className: "flex", children: /* @__PURE__ */ jsx15("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
|
|
3909
|
+
] }) }) }),
|
|
3910
|
+
/* @__PURE__ */ jsx15(
|
|
3911
|
+
PinLatestUserMessage,
|
|
3004
3912
|
{
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
/* @__PURE__ */ jsx15("span", { children: "\u4E0A\u4E0B\u6587\u5DF2\u538B\u7F29" })
|
|
3009
|
-
]
|
|
3913
|
+
userMessageCount: userMessages.length,
|
|
3914
|
+
shouldPinLatestUser,
|
|
3915
|
+
targetKey: latestUserMessage?.render_id ?? latestUserMessage?.entry_id ?? (latestUserMessage ? `user:${userMessages.length}` : null)
|
|
3010
3916
|
},
|
|
3011
|
-
|
|
3012
|
-
)
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
}
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
/* @__PURE__ */ jsx15(ScrollToBottomButton, {})
|
|
3020
|
-
] }) });
|
|
3917
|
+
sessionId ?? "no-session"
|
|
3918
|
+
),
|
|
3919
|
+
/* @__PURE__ */ jsx15(ScrollToBottomButton, {})
|
|
3920
|
+
]
|
|
3921
|
+
},
|
|
3922
|
+
sessionId ?? "no-session"
|
|
3923
|
+
)
|
|
3924
|
+
] });
|
|
3021
3925
|
}
|
|
3022
|
-
function
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
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) {
|
|
3027
3964
|
scrollToBottom("instant");
|
|
3028
3965
|
}
|
|
3029
3966
|
previousCountRef.current = userMessageCount;
|
|
3030
|
-
}, [
|
|
3967
|
+
}, [scrollToBottom, shouldPinLatestUser, userMessageCount]);
|
|
3031
3968
|
return null;
|
|
3032
3969
|
}
|
|
3033
3970
|
function ScrollToBottomButton() {
|
|
3034
3971
|
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
3035
|
-
const [visible, setVisible] =
|
|
3036
|
-
const hideTimerRef =
|
|
3037
|
-
|
|
3972
|
+
const [visible, setVisible] = useState12(false);
|
|
3973
|
+
const hideTimerRef = useRef11(null);
|
|
3974
|
+
useEffect10(() => {
|
|
3038
3975
|
if (isAtBottom) {
|
|
3039
3976
|
if (!hideTimerRef.current) {
|
|
3040
3977
|
hideTimerRef.current = setTimeout(() => {
|
|
@@ -3056,7 +3993,7 @@ function ScrollToBottomButton() {
|
|
|
3056
3993
|
}
|
|
3057
3994
|
};
|
|
3058
3995
|
}, [isAtBottom]);
|
|
3059
|
-
const handleClick =
|
|
3996
|
+
const handleClick = useCallback7(() => {
|
|
3060
3997
|
if (hideTimerRef.current) {
|
|
3061
3998
|
clearTimeout(hideTimerRef.current);
|
|
3062
3999
|
hideTimerRef.current = null;
|
|
@@ -3136,7 +4073,7 @@ function ChatSurface({
|
|
|
3136
4073
|
banner,
|
|
3137
4074
|
errorMessage && /* @__PURE__ */ jsxs14("div", { className: "blade-chat-error-bar flex items-start gap-2 border-b px-4 py-3 text-sm", children: [
|
|
3138
4075
|
/* @__PURE__ */ jsx16(CircleAlert, { size: 16, className: "mt-0.5 shrink-0" }),
|
|
3139
|
-
/* @__PURE__ */ jsx16("span", { children: errorMessage })
|
|
4076
|
+
/* @__PURE__ */ jsx16("span", { className: "min-w-0 whitespace-pre-wrap break-words [overflow-wrap:anywhere]", children: chatErrorForDisplay2(errorMessage) })
|
|
3140
4077
|
] }),
|
|
3141
4078
|
slots?.header,
|
|
3142
4079
|
/* @__PURE__ */ jsx16(
|
|
@@ -3185,8 +4122,8 @@ function isUnauthorizedError(error) {
|
|
|
3185
4122
|
return error instanceof BladeApiError && error.status === 401;
|
|
3186
4123
|
}
|
|
3187
4124
|
function LoginCard({ client, onLoggedIn }) {
|
|
3188
|
-
const [loggingIn, setLoggingIn] =
|
|
3189
|
-
const [loginError, setLoginError] =
|
|
4125
|
+
const [loggingIn, setLoggingIn] = useState13(false);
|
|
4126
|
+
const [loginError, setLoginError] = useState13(null);
|
|
3190
4127
|
const handleLogin = async () => {
|
|
3191
4128
|
setLoggingIn(true);
|
|
3192
4129
|
setLoginError(null);
|
|
@@ -3218,8 +4155,8 @@ function LoginCard({ client, onLoggedIn }) {
|
|
|
3218
4155
|
}
|
|
3219
4156
|
function AgentChat(props) {
|
|
3220
4157
|
const client = useBladeClient();
|
|
3221
|
-
const [attempt, setAttempt] =
|
|
3222
|
-
const [needLogin, setNeedLogin] =
|
|
4158
|
+
const [attempt, setAttempt] = useState13(0);
|
|
4159
|
+
const [needLogin, setNeedLogin] = useState13(() => !client.hasToken());
|
|
3223
4160
|
if (needLogin) {
|
|
3224
4161
|
return /* @__PURE__ */ jsx17(
|
|
3225
4162
|
"div",
|
|
@@ -3261,12 +4198,12 @@ function ChatSessionView({
|
|
|
3261
4198
|
onSessionCreated
|
|
3262
4199
|
});
|
|
3263
4200
|
const replay = useReplay(session);
|
|
3264
|
-
const [stopRequested, setStopRequested] =
|
|
3265
|
-
const [inputText, setInputText] =
|
|
3266
|
-
const [resultFeedback, setResultFeedback] =
|
|
4201
|
+
const [stopRequested, setStopRequested] = useState13(false);
|
|
4202
|
+
const [inputText, setInputText] = useState13("");
|
|
4203
|
+
const [resultFeedback, setResultFeedback] = useState13([]);
|
|
3267
4204
|
const resolvedSessionId = session?.sessionId;
|
|
3268
4205
|
const isViewer = state?.viewerRole === "viewer";
|
|
3269
|
-
|
|
4206
|
+
useEffect11(() => {
|
|
3270
4207
|
setResultFeedback([]);
|
|
3271
4208
|
if (!resolvedSessionId || isViewer) return;
|
|
3272
4209
|
let cancelled = false;
|
|
@@ -3295,18 +4232,18 @@ function ChatSessionView({
|
|
|
3295
4232
|
() => new Map(resultFeedback.map((item) => [item.assistant_entry_id, item])),
|
|
3296
4233
|
[resultFeedback]
|
|
3297
4234
|
);
|
|
3298
|
-
const handleResultFeedbackSaved =
|
|
4235
|
+
const handleResultFeedbackSaved = useCallback8((saved) => {
|
|
3299
4236
|
setResultFeedback((current) => [
|
|
3300
4237
|
...current.filter((item) => item.assistant_entry_id !== saved.assistant_entry_id),
|
|
3301
4238
|
saved
|
|
3302
4239
|
]);
|
|
3303
4240
|
}, []);
|
|
3304
|
-
|
|
4241
|
+
useEffect11(() => {
|
|
3305
4242
|
if (session) {
|
|
3306
4243
|
onSessionReady?.(session);
|
|
3307
4244
|
}
|
|
3308
4245
|
}, [session, onSessionReady]);
|
|
3309
|
-
|
|
4246
|
+
useEffect11(() => {
|
|
3310
4247
|
if (!session) return;
|
|
3311
4248
|
const offAttach = session.on("attachRequested", ({ label, content }) => {
|
|
3312
4249
|
setInputText((prev) => `${prev ? `${prev}
|
|
@@ -3322,12 +4259,12 @@ ${content}`);
|
|
|
3322
4259
|
offInsert();
|
|
3323
4260
|
};
|
|
3324
4261
|
}, [session]);
|
|
3325
|
-
|
|
4262
|
+
useEffect11(() => {
|
|
3326
4263
|
if (isUnauthorizedError(error)) {
|
|
3327
4264
|
onUnauthorized();
|
|
3328
4265
|
}
|
|
3329
4266
|
}, [error, onUnauthorized]);
|
|
3330
|
-
|
|
4267
|
+
useEffect11(() => {
|
|
3331
4268
|
if (!session || !commands) return;
|
|
3332
4269
|
const unsubscribes = Object.entries(commands).map(
|
|
3333
4270
|
([action, handler]) => session.onCommand(action, (payload) => handler(payload))
|
|
@@ -3398,10 +4335,10 @@ ${content}`);
|
|
|
3398
4335
|
}
|
|
3399
4336
|
|
|
3400
4337
|
// src/components/LlmChat.tsx
|
|
3401
|
-
import { useEffect as
|
|
4338
|
+
import { useEffect as useEffect12, useMemo as useMemo9, useState as useState15 } from "react";
|
|
3402
4339
|
|
|
3403
4340
|
// src/components/LlmAdvancedSettings.tsx
|
|
3404
|
-
import { useState as
|
|
4341
|
+
import { useState as useState14 } from "react";
|
|
3405
4342
|
import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
3406
4343
|
var FIELDS = [
|
|
3407
4344
|
{ id: "baseURL", label: "\u6A21\u578B\u670D\u52A1\u5730\u5740", placeholder: "http://\u5185\u7F51\u5730\u5740/v1" },
|
|
@@ -3448,8 +4385,8 @@ function writeOverride(settings, baseURL, override) {
|
|
|
3448
4385
|
}
|
|
3449
4386
|
function LlmAdvancedSettingsBar({ settings, defaults, override, onChange }) {
|
|
3450
4387
|
const normalized = normalizeAdvanced(settings);
|
|
3451
|
-
const [open, setOpen] =
|
|
3452
|
-
const [draft, setDraft] =
|
|
4388
|
+
const [open, setOpen] = useState14(false);
|
|
4389
|
+
const [draft, setDraft] = useState14(override);
|
|
3453
4390
|
if (!normalized) return null;
|
|
3454
4391
|
const fields = FIELDS.filter((field) => normalized[field.id]);
|
|
3455
4392
|
const dirty = Object.keys(override).length > 0;
|
|
@@ -3532,11 +4469,11 @@ function LlmChat({
|
|
|
3532
4469
|
onOverrideChange,
|
|
3533
4470
|
...options
|
|
3534
4471
|
}) {
|
|
3535
|
-
const [override, setOverride] =
|
|
4472
|
+
const [override, setOverride] = useState15(() => readOverride(advanced, options.baseURL));
|
|
3536
4473
|
const effective = { ...options, ...override };
|
|
3537
4474
|
const { messages, isStreaming, error, send, stop, reset } = useLlmChat(effective);
|
|
3538
|
-
const [inputText, setInputText] =
|
|
3539
|
-
const [stopRequested, setStopRequested] =
|
|
4475
|
+
const [inputText, setInputText] = useState15("");
|
|
4476
|
+
const [stopRequested, setStopRequested] = useState15(false);
|
|
3540
4477
|
const handle = useMemo9(
|
|
3541
4478
|
() => ({
|
|
3542
4479
|
insertText: (text) => setInputText((prev) => prev ? `${prev}
|
|
@@ -3546,7 +4483,7 @@ ${text}` : text),
|
|
|
3546
4483
|
}),
|
|
3547
4484
|
[send, reset]
|
|
3548
4485
|
);
|
|
3549
|
-
|
|
4486
|
+
useEffect12(() => {
|
|
3550
4487
|
onReady?.(handle);
|
|
3551
4488
|
}, [handle, onReady]);
|
|
3552
4489
|
return /* @__PURE__ */ jsx19(
|
|
@@ -3615,19 +4552,108 @@ function ChatView(props) {
|
|
|
3615
4552
|
return /* @__PURE__ */ jsx20(AgentChat, { ...rest });
|
|
3616
4553
|
}
|
|
3617
4554
|
|
|
4555
|
+
// src/components/ContextCard.tsx
|
|
4556
|
+
import {
|
|
4557
|
+
getContextDisplayState
|
|
4558
|
+
} from "@blade-hq/agent-client";
|
|
4559
|
+
import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
4560
|
+
function ContextCard({ context, className }) {
|
|
4561
|
+
const display = getContextDisplayState(context);
|
|
4562
|
+
return /* @__PURE__ */ jsxs17(
|
|
4563
|
+
"details",
|
|
4564
|
+
{
|
|
4565
|
+
className: `blade-chat-context-card group rounded-xl border border-[hsl(var(--border))] bg-[hsl(var(--card))] text-sm ${className ?? ""}`,
|
|
4566
|
+
children: [
|
|
4567
|
+
/* @__PURE__ */ jsxs17("summary", { className: "blade-chat-context-summary flex cursor-pointer list-none items-center gap-2 px-3 py-2.5 [&::-webkit-details-marker]:hidden", children: [
|
|
4568
|
+
/* @__PURE__ */ jsx21(
|
|
4569
|
+
Layers,
|
|
4570
|
+
{
|
|
4571
|
+
size: 15,
|
|
4572
|
+
className: "blade-chat-context-icon shrink-0 text-[hsl(var(--muted-foreground))]",
|
|
4573
|
+
"aria-hidden": "true"
|
|
4574
|
+
}
|
|
4575
|
+
),
|
|
4576
|
+
/* @__PURE__ */ jsxs17("span", { className: "blade-chat-context-copy min-w-0 flex-1", children: [
|
|
4577
|
+
/* @__PURE__ */ jsx21("span", { className: "blade-chat-context-title block font-medium text-[hsl(var(--foreground))]", children: display.title }),
|
|
4578
|
+
/* @__PURE__ */ jsx21("span", { className: "blade-chat-context-status block truncate text-xs text-[hsl(var(--muted-foreground))]", children: display.summary })
|
|
4579
|
+
] }),
|
|
4580
|
+
/* @__PURE__ */ jsx21(
|
|
4581
|
+
ChevronDown,
|
|
4582
|
+
{
|
|
4583
|
+
size: 14,
|
|
4584
|
+
className: "blade-chat-context-chevron shrink-0 text-[hsl(var(--muted-foreground))] transition-transform group-open:rotate-180",
|
|
4585
|
+
"aria-hidden": "true"
|
|
4586
|
+
}
|
|
4587
|
+
)
|
|
4588
|
+
] }),
|
|
4589
|
+
/* @__PURE__ */ jsx21("div", { className: "blade-chat-context-detail border-t border-[hsl(var(--border))] px-3 py-2.5 text-xs leading-5 text-[hsl(var(--muted-foreground))]", children: display.detail })
|
|
4590
|
+
]
|
|
4591
|
+
}
|
|
4592
|
+
);
|
|
4593
|
+
}
|
|
4594
|
+
|
|
4595
|
+
// src/lib/agent-computer-command.ts
|
|
4596
|
+
var COMPUTER_LAUNCH_COMMAND_PATTERN = /(?:^|[\n;&|(]\s*)computer\s+launch(?:\s|$)/;
|
|
4597
|
+
function isAgentComputerCommand(command) {
|
|
4598
|
+
return COMPUTER_LAUNCH_COMMAND_PATTERN.test(command);
|
|
4599
|
+
}
|
|
4600
|
+
function isAgentComputerToolCall(argumentsJson) {
|
|
4601
|
+
if (!argumentsJson) return false;
|
|
4602
|
+
let command;
|
|
4603
|
+
try {
|
|
4604
|
+
const parsed = JSON.parse(argumentsJson);
|
|
4605
|
+
if (typeof parsed !== "object" || parsed === null) return false;
|
|
4606
|
+
command = parsed.command;
|
|
4607
|
+
} catch {
|
|
4608
|
+
return false;
|
|
4609
|
+
}
|
|
4610
|
+
return typeof command === "string" && isAgentComputerCommand(command);
|
|
4611
|
+
}
|
|
4612
|
+
var LAUNCH_SUCCESS_MARKER = "\u5DF2\u542F\u52A8 ";
|
|
4613
|
+
function resultContainsLaunchSuccessMarker(result, depth = 0) {
|
|
4614
|
+
if (depth > 2) return false;
|
|
4615
|
+
if (typeof result === "string") {
|
|
4616
|
+
if (result.includes(LAUNCH_SUCCESS_MARKER)) return true;
|
|
4617
|
+
try {
|
|
4618
|
+
return resultContainsLaunchSuccessMarker(JSON.parse(result), depth + 1);
|
|
4619
|
+
} catch {
|
|
4620
|
+
return false;
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
if (typeof result === "object" && result !== null) {
|
|
4624
|
+
for (const value of Object.values(result)) {
|
|
4625
|
+
if (typeof value === "string" && value.includes(LAUNCH_SUCCESS_MARKER)) return true;
|
|
4626
|
+
}
|
|
4627
|
+
}
|
|
4628
|
+
return false;
|
|
4629
|
+
}
|
|
4630
|
+
function classifyAgentComputerLaunchOutcome(toolCall) {
|
|
4631
|
+
if (toolCall.status === "error" || toolCall.status === "cancelled") return "failed";
|
|
4632
|
+
if (toolCall.status !== "done") return "pending";
|
|
4633
|
+
if (toolCall.result === void 0 || toolCall.result === null) return "unknown";
|
|
4634
|
+
return resultContainsLaunchSuccessMarker(toolCall.result) ? "succeeded" : "failed";
|
|
4635
|
+
}
|
|
4636
|
+
|
|
3618
4637
|
// src/index.ts
|
|
3619
4638
|
export * from "@blade-hq/agent-client";
|
|
3620
4639
|
export {
|
|
3621
4640
|
AgentChat,
|
|
3622
4641
|
BladeProvider,
|
|
3623
4642
|
ChatView,
|
|
4643
|
+
ContextCard,
|
|
3624
4644
|
LlmChat,
|
|
3625
4645
|
MarkdownContent,
|
|
4646
|
+
MemoryRefsHint,
|
|
3626
4647
|
ReplayBar,
|
|
3627
4648
|
ReplayMismatchPrompt,
|
|
4649
|
+
classifyAgentComputerLaunchOutcome,
|
|
4650
|
+
collectMemoryRefs,
|
|
4651
|
+
isAgentComputerCommand,
|
|
4652
|
+
isAgentComputerToolCall,
|
|
3628
4653
|
useAgentSession,
|
|
3629
4654
|
useBladeClient,
|
|
3630
4655
|
useLlmChat,
|
|
4656
|
+
useMessagePin,
|
|
3631
4657
|
useReplay
|
|
3632
4658
|
};
|
|
3633
4659
|
/*! Bundled license information:
|
|
@@ -3639,17 +4665,16 @@ lucide-react/dist/esm/createLucideIcon.js:
|
|
|
3639
4665
|
lucide-react/dist/esm/icons/arrow-right.js:
|
|
3640
4666
|
lucide-react/dist/esm/icons/arrow-up-right.js:
|
|
3641
4667
|
lucide-react/dist/esm/icons/arrow-up.js:
|
|
4668
|
+
lucide-react/dist/esm/icons/book-open.js:
|
|
3642
4669
|
lucide-react/dist/esm/icons/bot.js:
|
|
3643
|
-
lucide-react/dist/esm/icons/brain.js:
|
|
3644
4670
|
lucide-react/dist/esm/icons/check.js:
|
|
3645
4671
|
lucide-react/dist/esm/icons/chevron-down.js:
|
|
3646
4672
|
lucide-react/dist/esm/icons/chevron-right.js:
|
|
3647
4673
|
lucide-react/dist/esm/icons/circle-alert.js:
|
|
3648
4674
|
lucide-react/dist/esm/icons/copy.js:
|
|
3649
|
-
lucide-react/dist/esm/icons/
|
|
4675
|
+
lucide-react/dist/esm/icons/earth.js:
|
|
4676
|
+
lucide-react/dist/esm/icons/file-pen-line.js:
|
|
3650
4677
|
lucide-react/dist/esm/icons/file-text.js:
|
|
3651
|
-
lucide-react/dist/esm/icons/file.js:
|
|
3652
|
-
lucide-react/dist/esm/icons/film.js:
|
|
3653
4678
|
lucide-react/dist/esm/icons/globe.js:
|
|
3654
4679
|
lucide-react/dist/esm/icons/layers.js:
|
|
3655
4680
|
lucide-react/dist/esm/icons/lightbulb.js:
|
|
@@ -3658,10 +4683,13 @@ lucide-react/dist/esm/icons/lock-keyhole.js:
|
|
|
3658
4683
|
lucide-react/dist/esm/icons/message-square-more.js:
|
|
3659
4684
|
lucide-react/dist/esm/icons/message-square.js:
|
|
3660
4685
|
lucide-react/dist/esm/icons/play.js:
|
|
4686
|
+
lucide-react/dist/esm/icons/search.js:
|
|
3661
4687
|
lucide-react/dist/esm/icons/settings-2.js:
|
|
3662
4688
|
lucide-react/dist/esm/icons/sparkles.js:
|
|
3663
4689
|
lucide-react/dist/esm/icons/square.js:
|
|
4690
|
+
lucide-react/dist/esm/icons/terminal.js:
|
|
3664
4691
|
lucide-react/dist/esm/icons/triangle-alert.js:
|
|
4692
|
+
lucide-react/dist/esm/icons/wrench.js:
|
|
3665
4693
|
lucide-react/dist/esm/icons/x.js:
|
|
3666
4694
|
lucide-react/dist/esm/lucide-react.js:
|
|
3667
4695
|
(**
|