@blade-hq/agent-react 2610.0.0-beta.44 → 2610.0.0-beta.46
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 +3 -0
- package/dist/components/AgentChat.d.ts +3 -0
- package/dist/embed/entry.d.ts +2 -0
- package/dist/hooks/use-message-pin.d.ts +7 -1
- package/dist/index.js +13 -17
- package/dist/index.js.map +1 -1
- package/dist/style.full.css +1 -1
- package/package.json +2 -2
- package/public-api.md +44 -4
package/README.md
CHANGED
|
@@ -208,6 +208,9 @@ Blade。这时把 `ChatView` 切到 `mode="llm"`,或者直接用下层的 `Llm
|
|
|
208
208
|
<ChatView mode="llm" llm={{ baseURL: "/api/llm", model }} /> // 切成纯 LLM
|
|
209
209
|
<LlmChat baseURL="/api/llm" model={model} /> // 或者直接用(LlmChatProps)
|
|
210
210
|
<AgentChat sessionId={id} /> // 智能体那侧同样可以直接用(AgentChatProps)
|
|
211
|
+
|
|
212
|
+
`AgentChat` 还支持 `sessionPluginControls(sessionId)` 插槽,宿主可复用
|
|
213
|
+
`@blade-hq/agent-client` 的会话插件状态与激活控制。
|
|
211
214
|
```
|
|
212
215
|
|
|
213
216
|
**协议就是 OpenAI 的**:`POST {baseURL}/chat/completions`,`stream: true`,读
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AgentSession, CreateSessionRequest } from "@blade-hq/agent-client";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
2
3
|
import { type ChatPresentationProps } from "./ChatSurface";
|
|
3
4
|
import type { FollowupInteractionEvent } from "./PostChatFollowupBlock";
|
|
4
5
|
export interface AgentChatProps extends ChatPresentationProps {
|
|
@@ -13,6 +14,8 @@ export interface AgentChatProps extends ChatPresentationProps {
|
|
|
13
14
|
onSessionReady?: (session: AgentSession) => void;
|
|
14
15
|
/** 智能体下发给宿主页面的指令处理器(action → handler)。 */
|
|
15
16
|
commands?: Record<string, (data: unknown) => void>;
|
|
17
|
+
/** 会话插件状态与激活控件;由宿主注入以复用共享会话 API。 */
|
|
18
|
+
sessionPluginControls?: (sessionId: string) => ReactNode;
|
|
16
19
|
}
|
|
17
20
|
/**
|
|
18
21
|
* 智能体模式的聊天界面:连接(或新建)一个 Blade 会话,渲染消息流与输入框。
|
package/dist/embed/entry.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ declare const BladeAgent: {
|
|
|
41
41
|
computerState: typeof agentClient.computerState;
|
|
42
42
|
sortComputers: typeof agentClient.sortComputers;
|
|
43
43
|
ModelsResource: typeof agentClient.ModelsResource;
|
|
44
|
+
ChatProjectsResource: typeof agentClient.ChatProjectsResource;
|
|
44
45
|
hasChatRunEvent: typeof agentClient.hasChatRunEvent;
|
|
45
46
|
buildMessageContent: typeof agentClient.buildMessageContent;
|
|
46
47
|
chatErrorForDisplay: typeof agentClient.chatErrorForDisplay;
|
|
@@ -94,6 +95,7 @@ declare const BladeAgent: {
|
|
|
94
95
|
workspace_path?: string | null | undefined;
|
|
95
96
|
ba_version?: string | null | undefined;
|
|
96
97
|
sandbox_version?: string | null | undefined;
|
|
98
|
+
chat_project_id?: string | null | undefined;
|
|
97
99
|
match?: unknown;
|
|
98
100
|
}, {}>;
|
|
99
101
|
SessionStatus: import("arktype/internal/variants/string.ts").StringType<"completed" | "created" | "failed" | "interrupted" | "running" | "waiting_for_input", {}>;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export interface UseMessagePinOptions {
|
|
2
2
|
targetKey: string | null;
|
|
3
3
|
pinTarget: boolean;
|
|
4
|
+
/**
|
|
5
|
+
* 省略时按旧行为处理(视为一直流式中,只靠 targetKey 变化释放)——
|
|
6
|
+
* useMessagePin 是导出的公开 hook,加这个字段前如果直接改成必填,
|
|
7
|
+
* 所有已经在调用这个 hook 的外部使用方都会编译不过。
|
|
8
|
+
*/
|
|
9
|
+
isStreaming?: boolean;
|
|
4
10
|
layoutKey?: string;
|
|
5
11
|
getScrollElement: () => HTMLElement | null;
|
|
6
12
|
getContentElement?: () => HTMLElement | null;
|
|
@@ -16,7 +22,7 @@ export interface UseMessagePinOptions {
|
|
|
16
22
|
* 用户发言顶置的共享状态机。SDK 和内置轻量列表只提供 DOM/虚拟列表适配器,
|
|
17
23
|
* 触发、调度、resize、手动接管和跟随交还的决策只在这里维护。
|
|
18
24
|
*/
|
|
19
|
-
export declare function useMessagePin({ targetKey, pinTarget, layoutKey, getScrollElement, getContentElement, getTargetElement, getTargetScrollTop, getSpacerHeight, setSpacerHeight, stopAutoScroll, scrollToBottom, margin, }: UseMessagePinOptions): {
|
|
25
|
+
export declare function useMessagePin({ targetKey, pinTarget, isStreaming, layoutKey, getScrollElement, getContentElement, getTargetElement, getTargetScrollTop, getSpacerHeight, setSpacerHeight, stopAutoScroll, scrollToBottom, margin, }: UseMessagePinOptions): {
|
|
20
26
|
release: () => void;
|
|
21
27
|
isActive: () => boolean;
|
|
22
28
|
};
|
package/dist/index.js
CHANGED
|
@@ -617,6 +617,7 @@ var MANUAL_SCROLL_TOLERANCE_PX = 2;
|
|
|
617
617
|
function useMessagePin({
|
|
618
618
|
targetKey,
|
|
619
619
|
pinTarget,
|
|
620
|
+
isStreaming = true,
|
|
620
621
|
layoutKey,
|
|
621
622
|
getScrollElement,
|
|
622
623
|
getContentElement,
|
|
@@ -625,6 +626,10 @@ function useMessagePin({
|
|
|
625
626
|
getSpacerHeight,
|
|
626
627
|
setSpacerHeight,
|
|
627
628
|
stopAutoScroll,
|
|
629
|
+
// 不再内部调用(见下面 reposition 里的说明),但这是公开的 SDK hook 参数,
|
|
630
|
+
// 删掉会让已经传这个 prop 的调用方在 TS 严格类型下编译不过——留着接口
|
|
631
|
+
// 不动,只是这里不再用它。
|
|
632
|
+
// biome-ignore lint/correctness/noUnusedVariables: 保留公开 API 形状
|
|
628
633
|
scrollToBottom,
|
|
629
634
|
margin = DEFAULT_MARGIN_PX
|
|
630
635
|
}) {
|
|
@@ -674,7 +679,6 @@ function useMessagePin({
|
|
|
674
679
|
scroll.scrollTop = targetScrollTop;
|
|
675
680
|
if (nextSpacerHeight === 0 && previousSpacerHeight > 0) {
|
|
676
681
|
pinActiveRef.current = false;
|
|
677
|
-
scrollToBottom();
|
|
678
682
|
}
|
|
679
683
|
},
|
|
680
684
|
[
|
|
@@ -683,7 +687,6 @@ function useMessagePin({
|
|
|
683
687
|
getTargetElement,
|
|
684
688
|
getTargetScrollTop,
|
|
685
689
|
margin,
|
|
686
|
-
scrollToBottom,
|
|
687
690
|
stopAutoScroll
|
|
688
691
|
]
|
|
689
692
|
);
|
|
@@ -709,7 +712,7 @@ function useMessagePin({
|
|
|
709
712
|
return;
|
|
710
713
|
}
|
|
711
714
|
if (!pinTarget) {
|
|
712
|
-
if (pinActiveRef.current && appliedTargetKeyRef.current != null && appliedTargetKeyRef.current !== targetKey) {
|
|
715
|
+
if (pinActiveRef.current && appliedTargetKeyRef.current != null && (appliedTargetKeyRef.current !== targetKey || !isStreaming)) {
|
|
713
716
|
release();
|
|
714
717
|
}
|
|
715
718
|
observedTargetKeyRef.current = targetKey;
|
|
@@ -730,7 +733,7 @@ function useMessagePin({
|
|
|
730
733
|
retryTimeoutRef.current = null;
|
|
731
734
|
scheduleReposition();
|
|
732
735
|
}, 80);
|
|
733
|
-
}, [pinTarget, release, scheduleReposition, stopAutoScroll, targetKey]);
|
|
736
|
+
}, [isStreaming, pinTarget, release, scheduleReposition, stopAutoScroll, targetKey]);
|
|
734
737
|
useEffect3(() => {
|
|
735
738
|
if (layoutKey !== void 0) scheduleReposition();
|
|
736
739
|
}, [layoutKey, scheduleReposition]);
|
|
@@ -4140,9 +4143,7 @@ function MessageList({
|
|
|
4140
4143
|
});
|
|
4141
4144
|
const userMessages = visibleRootMessages.filter((message) => isUserMessage(message));
|
|
4142
4145
|
const latestUserMessage = userMessages.at(-1);
|
|
4143
|
-
const
|
|
4144
|
-
const latestPromptPreview = latestPromptText.length > 80 ? `${latestPromptText.slice(0, 80)}\u2026` : latestPromptText;
|
|
4145
|
-
const shouldPinLatestUser = latestUserMessage != null && (latestUserMessage.entry_id == null || latestUserMessage.entry_id.startsWith("local-user-"));
|
|
4146
|
+
const shouldPinLatestUser = latestUserMessage != null && (latestUserMessage.entry_id == null ? isStreaming : latestUserMessage.entry_id.startsWith("local-user-"));
|
|
4146
4147
|
const renderBlocks = useMemo7(() => {
|
|
4147
4148
|
const visible = messages.filter((message) => {
|
|
4148
4149
|
if ((message.loop_name ?? "root") !== "root") return false;
|
|
@@ -4228,16 +4229,6 @@ function MessageList({
|
|
|
4228
4229
|
children: [
|
|
4229
4230
|
/* @__PURE__ */ jsx17(StickToBottom.Content, { className: "blade-chat-messages-scroll", children: /* @__PURE__ */ jsxs15("div", { className: "blade-chat-messages-content mx-auto max-w-[748px]", children: [
|
|
4230
4231
|
historyPaging && (historyPaging.hasOlder || historyPaging.loading) ? /* @__PURE__ */ jsx17(LoadOlderSentinel, { ...historyPaging }) : null,
|
|
4231
|
-
isStreaming && latestUserMessage && latestPromptPreview ? /* @__PURE__ */ jsx17(
|
|
4232
|
-
"button",
|
|
4233
|
-
{
|
|
4234
|
-
type: "button",
|
|
4235
|
-
className: "sticky top-0 z-20 mb-4 w-full truncate rounded-2xl border border-[hsl(var(--primary)/0.2)] bg-[hsl(var(--background)/0.92)] px-4 py-3 text-left text-sm font-medium shadow-sm backdrop-blur",
|
|
4236
|
-
onClick: () => Array.from(document.querySelectorAll("[data-entry-id]")).find((el) => el.dataset.entryId === latestUserMessage.entry_id)?.scrollIntoView({ behavior: "smooth", block: "start" }),
|
|
4237
|
-
"aria-label": "\u8DF3\u8F6C\u5230\u5F53\u524D\u63D0\u95EE",
|
|
4238
|
-
children: latestPromptPreview
|
|
4239
|
-
}
|
|
4240
|
-
) : null,
|
|
4241
4232
|
/* @__PURE__ */ jsxs15("div", { className: "flex min-w-0 flex-col", children: [
|
|
4242
4233
|
renderBlocks.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs15("div", { className: "blade-chat-empty", children: [
|
|
4243
4234
|
/* @__PURE__ */ jsx17(MessageSquare, { size: 40, strokeWidth: 1.5 }),
|
|
@@ -4316,6 +4307,7 @@ function MessageList({
|
|
|
4316
4307
|
{
|
|
4317
4308
|
userMessageCount: userMessages.length,
|
|
4318
4309
|
shouldPinLatestUser,
|
|
4310
|
+
isStreaming,
|
|
4319
4311
|
targetKey: latestUserMessage?.render_id ?? latestUserMessage?.entry_id ?? (latestUserMessage ? `user:${userMessages.length}` : null)
|
|
4320
4312
|
},
|
|
4321
4313
|
sessionId ?? "no-session"
|
|
@@ -4400,6 +4392,7 @@ function LoadOlderSentinel({
|
|
|
4400
4392
|
function PinLatestUserMessage({
|
|
4401
4393
|
userMessageCount,
|
|
4402
4394
|
shouldPinLatestUser,
|
|
4395
|
+
isStreaming,
|
|
4403
4396
|
targetKey
|
|
4404
4397
|
}) {
|
|
4405
4398
|
const { contentRef, scrollRef, scrollToBottom, stopScroll } = useStickToBottomContext();
|
|
@@ -4425,6 +4418,7 @@ function PinLatestUserMessage({
|
|
|
4425
4418
|
useMessagePin({
|
|
4426
4419
|
targetKey,
|
|
4427
4420
|
pinTarget: shouldPinLatestUser,
|
|
4421
|
+
isStreaming,
|
|
4428
4422
|
getScrollElement,
|
|
4429
4423
|
getContentElement,
|
|
4430
4424
|
getTargetElement,
|
|
@@ -4682,6 +4676,7 @@ function ChatSessionView({
|
|
|
4682
4676
|
placeholder,
|
|
4683
4677
|
theme,
|
|
4684
4678
|
onFollowupInteraction,
|
|
4679
|
+
sessionPluginControls,
|
|
4685
4680
|
onUnauthorized
|
|
4686
4681
|
}) {
|
|
4687
4682
|
const client = useBladeClient();
|
|
@@ -4805,6 +4800,7 @@ ${content}`);
|
|
|
4805
4800
|
placeholder,
|
|
4806
4801
|
connection: state?.connection ?? "connecting",
|
|
4807
4802
|
banner: /* @__PURE__ */ jsxs17(Fragment4, { children: [
|
|
4803
|
+
resolvedSessionId && !isViewer && sessionPluginControls?.(resolvedSessionId),
|
|
4808
4804
|
/* @__PURE__ */ jsx19(
|
|
4809
4805
|
ReplayBar,
|
|
4810
4806
|
{
|