@blade-hq/agent-react 2610.0.0-beta.8 → 2610.0.0-beta.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +313 -8
  2. package/dist/chunk-G3PMV62Z.js +36 -0
  3. package/dist/components/AgentChat.d.ts +3 -0
  4. package/dist/components/AgentLoopBlock.d.ts +3 -5
  5. package/dist/components/AgentTaskCard.d.ts +12 -0
  6. package/dist/components/AskUserQuestionBlock.d.ts +12 -1
  7. package/dist/components/AssistantTurnBlock.d.ts +26 -8
  8. package/dist/components/ChatInput.d.ts +21 -1
  9. package/dist/components/ChatSurface.d.ts +133 -2
  10. package/dist/components/ChatView.d.ts +1 -1
  11. package/dist/components/ConnectorPanel.d.ts +41 -0
  12. package/dist/components/ContextCard.d.ts +20 -0
  13. package/dist/components/MarkdownContent.d.ts +17 -0
  14. package/dist/components/McpAppCard.d.ts +14 -0
  15. package/dist/components/MessageList.d.ts +38 -4
  16. package/dist/components/PlanUpdateBlock.d.ts +32 -0
  17. package/dist/components/PluginConnectorList.d.ts +47 -0
  18. package/dist/components/PluginTriggerStack.d.ts +22 -0
  19. package/dist/components/PostChatFollowupBlock.d.ts +5 -1
  20. package/dist/components/SessionMemoryToggle.d.ts +19 -0
  21. package/dist/components/SessionPluginConfigDialog.d.ts +10 -0
  22. package/dist/components/SessionPluginConfigForm.d.ts +14 -0
  23. package/dist/components/SessionPluginIcon.d.ts +30 -0
  24. package/dist/components/SessionPluginSelector.d.ts +10 -0
  25. package/dist/components/SessionQueuePanel.d.ts +74 -0
  26. package/dist/components/ToolCallBlock.d.ts +9 -3
  27. package/dist/components/ToolUiCardView.d.ts +9 -0
  28. package/dist/components/TurnNavRail.d.ts +23 -0
  29. package/dist/components/UserMessageBubble.d.ts +7 -3
  30. package/dist/components/WhatIfUserBubble.d.ts +7 -0
  31. package/dist/components/connector-panel-types.d.ts +37 -0
  32. package/dist/components/display-utils.d.ts +10 -0
  33. package/dist/components/markdown/heal-incomplete.d.ts +7 -0
  34. package/dist/components/markdown/incremental.d.ts +73 -0
  35. package/dist/components/markdown/index.d.ts +18 -0
  36. package/dist/components/markdown/parse.d.ts +19 -0
  37. package/dist/components/markdown/render.d.ts +79 -0
  38. package/dist/components/markdown/sanitize.d.ts +50 -0
  39. package/dist/components/markdown/types.d.ts +53 -0
  40. package/dist/components/plugin-config-draft.d.ts +13 -0
  41. package/dist/components/plugin-config-schema.d.ts +6 -0
  42. package/dist/components/plugin-connector.d.ts +40 -0
  43. package/dist/components/queue-panel-adjacency.d.ts +1 -0
  44. package/dist/components/use-session-plugin-activation.d.ts +40 -0
  45. package/dist/context.d.ts +1 -0
  46. package/dist/embed/blade-chat-element.d.ts +4 -3
  47. package/dist/embed/entry.d.ts +49 -0
  48. package/dist/hooks/use-agent-session.d.ts +5 -0
  49. package/dist/hooks/use-message-pin.d.ts +28 -0
  50. package/dist/hooks/use-turn-navigation.d.ts +117 -0
  51. package/dist/hooks/use-typewriter-reveal.d.ts +32 -0
  52. package/dist/index.d.ts +53 -1
  53. package/dist/index.js +58278 -2242
  54. package/dist/index.js.map +1 -1
  55. package/dist/lib/agent-computer-command.d.ts +34 -0
  56. package/dist/lib/random-id.d.ts +10 -0
  57. package/dist/lib/utils.d.ts +17 -2
  58. package/dist/lib/whatif-prompt.d.ts +18 -0
  59. package/dist/style.css +506 -401
  60. package/dist/style.full.css +517 -405
  61. package/dist/webapi-W3IB5DO4.js +2481 -0
  62. package/dist/webapi-W3IB5DO4.js.map +1 -0
  63. package/package.json +2 -2
  64. package/public-api.md +1749 -65
  65. package/dist/chunk-ZXXLY4RM.js +0 -28255
  66. package/dist/chunk-ZXXLY4RM.js.map +0 -1
  67. package/dist/highlighted-body-B3W2YXNL-YD7FAP6V.js +0 -27
  68. package/dist/highlighted-body-B3W2YXNL-YD7FAP6V.js.map +0 -1
  69. package/dist/mermaid-3ZIDBTTL-N7YKJ3SM.js +0 -8
  70. /package/dist/{mermaid-3ZIDBTTL-N7YKJ3SM.js.map → chunk-G3PMV62Z.js.map} +0 -0
@@ -1,17 +1,119 @@
1
1
  import type { AskUserAnswerData, ChatMessage, ConnectionStatus, PostChatFollowup } from "@blade-hq/agent-client";
2
- import type { ReactNode } from "react";
2
+ import { type ReactNode } from "react";
3
+ import type { TurnNavigationSource } from "../hooks/use-turn-navigation";
3
4
  import type { ToolCallRenderer } from "./ToolCallBlock";
4
5
  import type { FollowupInteractionEvent } from "./PostChatFollowupBlock";
5
6
  import type { ResultFeedback } from "@blade-hq/agent-client";
7
+ import { type PlanUpdateData } from "./PlanUpdateBlock";
8
+ import type { AskUserQuestionData } from "./AskUserQuestionBlock";
9
+ /**
10
+ * 每个可见区块的 className 落点,键名与元素上的 `blade-chat-*` 语义锚点一一对应。
11
+ *
12
+ * 传进来的类会与 SDK 自己的类一起经过 `cn`(clsx + tailwind-merge),放在
13
+ * 最后一个参数,因此 `classNames={{ chatInput: "p-0" }}` 能盖掉组件内部的 `p-4`,
14
+ * 不依赖 CSS 输出顺序、也不需要 `!important`。锚点本身不带视觉规则(清单见
15
+ * README「样式定制」与 src/style.css 顶部),用普通 CSS 选它们同样稳定生效。
16
+ */
6
17
  export interface ChatViewClassNames {
18
+ /** 聊天根容器(`.blade-chat`)。 */
7
19
  root?: string;
20
+ /** 连接状态提示条(`.blade-chat-banner`)。 */
8
21
  banner?: string;
22
+ /** 顶部出错条(`.blade-chat-error-bar`)。 */
23
+ errorBar?: string;
24
+ /** 消息区(`.blade-chat-messages`)。 */
9
25
  messageList?: string;
26
+ /** 消息区滚动内容(`.blade-chat-messages-scroll`):内边距在这里。 */
27
+ messagesScroll?: string;
28
+ /** 消息内容列(`.blade-chat-messages-content`):居中与最大宽度在这里。 */
29
+ messagesContent?: string;
30
+ /** 无消息时的空状态(`.blade-chat-empty`)。 */
31
+ empty?: string;
32
+ /** 「滚动到底部」按钮(`.blade-chat-scroll-bottom`)。 */
33
+ scrollToBottom?: string;
34
+ /** 用户消息行(`.blade-chat-user-row`)。 */
35
+ userMessage?: string;
36
+ /** 用户气泡(`.blade-chat-user-bubble`)。 */
37
+ userBubble?: string;
38
+ /** 消息里的文件附件卡(`.blade-chat-file-card`)。 */
39
+ fileCard?: string;
40
+ /** 消息流里的出错块(`.blade-chat-error-row`)。 */
41
+ errorMessage?: string;
42
+ /** 一轮助手回复(`.blade-chat-assistant-turn`)。 */
43
+ assistantTurn?: string;
44
+ /** 思考过程(`.blade-chat-thinking`)。 */
45
+ thinking?: string;
46
+ /** 助手正文(`.blade-chat-assistant-text`)。 */
47
+ assistantText?: string;
48
+ /** 记忆引用(`.blade-chat-memory-refs`)。 */
49
+ memoryRefs?: string;
50
+ /** 执行过程里的工具行(`.blade-chat-tool-row`):智能体模式下工具调用都走这条路径。 */
51
+ toolRow?: string;
52
+ /** 子智能体行(`.blade-chat-agent-loop`)。 */
53
+ agentLoop?: string;
54
+ /** 提问卡(`.blade-chat-ask-card`)。 */
55
+ askCard?: string;
56
+ /** 提问卡的每个选项(`.blade-chat-ask-option`)。 */
57
+ askOption?: string;
58
+ /** 计划面板(`.blade-chat-plan`)。 */
59
+ planPanel?: string;
60
+ /** 会话队列面板(`.blade-chat-queue`)。 */
61
+ queuePanel?: string;
62
+ /** 本轮小结与跟进区(`.blade-chat-followup`)。 */
63
+ followup?: string;
64
+ /** 跟进建议按钮(`.blade-chat-followup-action`)。 */
65
+ followupAction?: string;
66
+ /** 输入区外层(`.blade-chat-input`)。 */
10
67
  chatInput?: string;
68
+ /** 输入框卡片(`.blade-chat-input-inner`):边框、圆角、底色在这里。 */
69
+ inputInner?: string;
70
+ /** 文本输入框(`.blade-chat-textarea`)。 */
71
+ textarea?: string;
72
+ /** 输入框右侧按钮组(`.blade-chat-input-toolbar`)。 */
73
+ inputToolbar?: string;
74
+ }
75
+ /** 用户消息渲染器:`message` 是投影层给出的用户消息。 */
76
+ export interface UserMessageRendererProps {
77
+ message: ChatMessage;
11
78
  }
79
+ /** 助手正文渲染器:`compact` 为真时正文在执行过程里,字号更小。 */
80
+ export interface AssistantTextRendererProps {
81
+ message: ChatMessage;
82
+ streaming: boolean;
83
+ compact: boolean;
84
+ sessionId?: string;
85
+ }
86
+ /** 提问卡渲染器:字段与 `AskUserQuestionBlock` 的 props 一致。 */
87
+ export interface AskUserQuestionRendererProps {
88
+ data: AskUserQuestionData;
89
+ answered: boolean;
90
+ toolCallId: string;
91
+ sessionStatus: string;
92
+ answerData?: AskUserAnswerData;
93
+ onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void;
94
+ }
95
+ /** 计划面板渲染器:`plan` 是最近一次成功的 update_plan 解析结果。 */
96
+ export interface PlanUpdateRendererProps {
97
+ plan: PlanUpdateData;
98
+ running: boolean;
99
+ sessionId?: string;
100
+ }
101
+ /**
102
+ * 区块级渲染器:每个都返回 `ReactNode`,返回 `null` 走 SDK 默认渲染。
103
+ *
104
+ * 与 `toolCall` 的老签名一致:参数是一个描述该区块的对象,而不是裸数据。
105
+ */
12
106
  export interface ChatViewRenderers {
13
107
  /** 自定义工具调用渲染;返回 null 走默认渲染。 */
14
108
  toolCall?: ToolCallRenderer;
109
+ /** 自定义用户消息渲染;返回 null 走默认渲染。 */
110
+ userMessage?: (props: UserMessageRendererProps) => ReactNode | null;
111
+ /** 自定义助手正文渲染;返回 null 走默认渲染。 */
112
+ assistantText?: (props: AssistantTextRendererProps) => ReactNode | null;
113
+ /** 自定义提问卡渲染;返回 null 走默认渲染。 */
114
+ askUserQuestion?: (props: AskUserQuestionRendererProps) => ReactNode | null;
115
+ /** 自定义计划面板渲染;返回 null 走默认渲染。 */
116
+ planUpdate?: (props: PlanUpdateRendererProps) => ReactNode | null;
15
117
  }
16
118
  export interface ChatViewSlots {
17
119
  /** 消息列表上方的自定义区域。 */
@@ -55,8 +157,37 @@ interface ChatSurfaceProps extends ChatPresentationProps {
55
157
  onResultFeedbackSaved?: (feedback: ResultFeedback) => void;
56
158
  /** 输入框上方的内部区域(纯 LLM 模式的高级设置用)。不传时不渲染任何节点。 */
57
159
  beforeInput?: ReactNode;
160
+ /** 输入框上方的会话队列面板(智能体模式用)。不传时不渲染任何节点。 */
161
+ queuePanel?: ReactNode;
162
+ /**
163
+ * 队列面板此刻是否真的占位(由面板的 `onVisibleChange` 上报)。
164
+ *
165
+ * 不能用 `queuePanel != null` 代替:面板有本地编辑态(编辑到一半、那条等待
166
+ * 消息被服务端拿走时仍留在原地保住草稿),这时它显示但 snapshot 里没有条目,
167
+ * 光看节点是否存在判不出来,输入框上两角就会在面板还开着的时候恢复成圆角。
168
+ */
169
+ queuePanelVisible?: boolean;
170
+ /** 仅 Agent 模式启用 Blade 的 update_plan 面板。 */
171
+ showPlanUpdates?: boolean;
172
+ /** 仅实时成功的 update_plan 递增;历史 hydration 不得改变。 */
173
+ planRevealRevision?: number;
174
+ historyPaging?: {
175
+ hasOlder: boolean;
176
+ loading: boolean;
177
+ /** Resolves true only when the paging cursor advanced. */
178
+ loadOlder: (beforeCommit?: () => void) => Promise<boolean>;
179
+ };
180
+ /** 消息导航的数据源(目录 + 定位);不传就没有导航轨道。 */
181
+ turnNavigation?: TurnNavigationSource;
58
182
  /** 连接状态条下方的横幅(回放模式条用)。不传时不渲染任何节点。 */
59
183
  banner?: ReactNode;
184
+ /**
185
+ * 打字机式显示节流是否还没追平目标全文——调用方需要跟 isStreaming 做
186
+ * `||` 判断输入框"运行中"状态。
187
+ */
188
+ onRevealingChange?: (revealing: boolean) => void;
189
+ /** 是否正在回放(演示 / 彩排)而非真正的实时直播——透传给 MessageList。 */
190
+ isReplay?: boolean;
60
191
  }
61
192
  /**
62
193
  * 消息流 + 输入框:智能体模式与纯 LLM 模式共用同一套外观,差别只在谁喂数据。
@@ -64,5 +195,5 @@ interface ChatSurfaceProps extends ChatPresentationProps {
64
195
  * 这里的 DOM 结构受 tests/chat-view-compat.test.tsx 的快照保护——接入方拿
65
196
  * .blade-chat-* 类名写了自己的样式,结构一动他们的页面就歪。
66
197
  */
67
- export declare function ChatSurface({ theme, classNames, renderers, slots, placeholder, connection, errorMessage, messages, postChatFollowup, isStreaming, isStopping, inputText, onInputChange, onSuggestion, onSend, onStop, sessionStatus, askAnswers, onAnswer, sessionId, isViewer, resultFeedbackByEntry, onResultFeedbackSaved, onFollowupInteraction, beforeInput, banner, }: ChatSurfaceProps): import("react/jsx-runtime").JSX.Element;
198
+ export declare function ChatSurface({ theme, classNames, renderers, slots, placeholder, connection, errorMessage, messages, postChatFollowup, isStreaming, isStopping, inputText, onInputChange, onSuggestion, onSend, onStop, sessionStatus, askAnswers, onAnswer, sessionId, isViewer, resultFeedbackByEntry, onResultFeedbackSaved, onFollowupInteraction, beforeInput, queuePanel, queuePanelVisible, showPlanUpdates, planRevealRevision, historyPaging, turnNavigation, banner, onRevealingChange, isReplay, }: ChatSurfaceProps): import("react/jsx-runtime").JSX.Element;
68
199
  export {};
@@ -2,7 +2,7 @@ import { type AgentChatProps } from "./AgentChat";
2
2
  import { type LlmChatHandle } from "./LlmChat";
3
3
  import type { LlmAdvancedSettings, LlmOverride } from "./LlmAdvancedSettings";
4
4
  import type { LlmChatOptions } from "../hooks/use-llm-chat";
5
- export type { ChatViewClassNames, ChatViewRenderers, ChatViewSlots, } from "./ChatSurface";
5
+ export type { AssistantTextRendererProps, AskUserQuestionRendererProps, ChatViewClassNames, ChatViewRenderers, ChatViewSlots, PlanUpdateRendererProps, UserMessageRendererProps, } from "./ChatSurface";
6
6
  export type { FollowupInteractionEvent } from "./PostChatFollowupBlock";
7
7
  export interface ChatViewProps extends AgentChatProps {
8
8
  /** 挂哪个下层组件。默认 `agent`——不传就是接 Blade 智能体会话的原有行为。 */
@@ -0,0 +1,41 @@
1
+ import type { BladeClient, SessionPluginActivation } from "@blade-hq/agent-client";
2
+ import { type ReactNode } from "react";
3
+ import { type PluginConnectorItem } from "./plugin-connector";
4
+ import { type PluginConnectorSelection } from "./connector-panel-types";
5
+ export interface ConnectorPanelProps {
6
+ client: BladeClient;
7
+ /**
8
+ * `null` 表示首页:插件开关只记下本次输入的待选,不激活任何东西、不建会话、
9
+ * 不启动沙盒或 MCP。待选项随首条消息一起交给宿主处理。
10
+ */
11
+ sessionId: string | null;
12
+ /** 首页待选插件;`sessionId` 为 null 时使用。 */
13
+ pendingPlugins?: PluginConnectorSelection[];
14
+ onPendingPluginsChange?: (next: PluginConnectorSelection[]) => void;
15
+ /** 首页候选开关不可用时的说明(例如软件工厂模式会先建项目再启动)。 */
16
+ pendingDisabledReason?: string | null;
17
+ /** 会话内插件启用/停用结果;宿主据此刷新技能统计。 */
18
+ onPluginsChanged?: (plugin: SessionPluginActivation) => void;
19
+ /**
20
+ * 当前已选(首页待选 / 会话内已启用)的插件,按行原样报给宿主。
21
+ *
22
+ * 触发器要在不打开面板时就能显示「选了哪几个、几个」,它自己拿不到这份状态,
23
+ * 所以由面板单向汇报;面板未挂载时宿主保留上一次的结果。
24
+ */
25
+ onSelectedChange?: (items: PluginConnectorItem[]) => void;
26
+ /** 宿主注入:电脑分区。参数是搜索词,宿主自己决定怎么过滤。 */
27
+ renderComputerSection?: (query: string) => ReactNode;
28
+ /** 宿主注入:外部账号分区(飞书 / GitHub 等固定连接入口)。 */
29
+ renderExternalAccounts?: (query: string) => ReactNode;
30
+ /** 宿主注入:底部操作区;不注入就不渲染这一块。 */
31
+ renderFooterActions?: () => ReactNode;
32
+ className?: string;
33
+ }
34
+ /**
35
+ * 统一连接器面板:顶部搜索、按「电脑 / 插件 / 外部账号」分区、底部添加与管理。
36
+ *
37
+ * 插件分区是这一层唯一的共享实现:首页和会话用同一个列表、同一套启用判定,
38
+ * 判定不各写一份。电脑与外部账号由宿主注入——那些入口需要宿主的路由与 CLI
39
+ * 引导,SDK 不硬编码内部路径,也不替第三方决定跳到哪里。
40
+ */
41
+ export declare function ConnectorPanel({ client, sessionId, pendingPlugins, onPendingPluginsChange, pendingDisabledReason, onPluginsChanged, onSelectedChange, renderComputerSection, renderExternalAccounts, renderFooterActions, className, }: ConnectorPanelProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { type ContextProjectionData } from "@blade-hq/agent-client";
2
+ export interface ContextCardProps {
3
+ context: ContextProjectionData;
4
+ className?: string;
5
+ }
6
+ /** Business-facing Context summary. Native details keeps every card collapsed by default. */
7
+ export declare function ContextCard({ context, className }: ContextCardProps): import("react/jsx-runtime").JSX.Element;
8
+ export interface ContextGroupCardProps {
9
+ contexts: readonly ContextProjectionData[];
10
+ className?: string;
11
+ }
12
+ /**
13
+ * 一批上下文注入折叠成一行。会话首轮会一次注入十几个原子,一条一张卡片会把
14
+ * 真正的对话挤出屏幕。
15
+ *
16
+ * 单条和多条**共用同一个 details 外壳**,不在数量变化时切换组件:流式时第二条
17
+ * 到达会让这一行从单条变成多条,组件类型一换 React 就重挂 <details>,
18
+ * 用户刚展开的行会被收起来。同理,key 由调用方按组内第一条给,不能带数量。
19
+ */
20
+ export declare function ContextGroupCard({ contexts, className }: ContextGroupCardProps): import("react/jsx-runtime").JSX.Element | null;
@@ -1,3 +1,19 @@
1
+ import { type ComponentPropsWithRef } from "react";
2
+ /** 内置 Web(`apps/web/src/agent/components/markdown/MarkdownContent.tsx`)
3
+ * 有一份同名同签名的实现,两处共用同一套"跳过代码区间"的判定逻辑,改动
4
+ * 时要一起确认。这里作为 SDK 侧的规范实现导出,内置 Web 直接复用,不要
5
+ * 各写一份(对齐 CLAUDE.md"判定逻辑不许各写一份")。 */
6
+ export declare function stripSystemReminders(text: string): string;
7
+ /** Keep inline formatting markers from being consumed by URL autolinking. */
8
+ export declare function normalizeAdjacentUrlFormatting(value: string): string;
9
+ type AnchorProps = ComponentPropsWithRef<"a">;
10
+ /** 链接一律新窗口打开并断开 opener,SDK 不接管站内跳转。
11
+ * 页内锚点(脚注引用/回链这类 `href="#fn-note"`)不能套"新窗口打开"这套
12
+ * 逻辑——新开一个标签页访问 `#片段` 只是把整篇文档重新打开一次,不是
13
+ * 滚动到页内位置,会破坏脚注的跳转体验。只有真正的外部链接才需要新窗口
14
+ * + 断开 opener。导出给内置 Web 直接复用——两处原来各写了一份完全相同的
15
+ * 判定逻辑,对齐 CLAUDE.md"判定逻辑不允许两处各写一份"。 */
16
+ export declare function ExternalAnchor({ children, href, ...props }: AnchorProps): import("react/jsx-runtime").JSX.Element;
1
17
  export interface MarkdownContentProps {
2
18
  children: string;
3
19
  className?: string;
@@ -11,3 +27,4 @@ export interface MarkdownContentProps {
11
27
  * mermaid、外链确认弹窗与文件预览。
12
28
  */
13
29
  export declare function MarkdownContent({ children, className, mode, sessionId }: MarkdownContentProps): import("react/jsx-runtime").JSX.Element;
30
+ export {};
@@ -0,0 +1,14 @@
1
+ import type { BladeClient } from "@blade-hq/agent-client";
2
+ export interface McpAppMessage {
3
+ sessionId: string;
4
+ sourceId: string;
5
+ label: string;
6
+ content: string;
7
+ }
8
+ export declare const McpAppMessageContext: import("react").Context<((message: McpAppMessage) => void) | null>;
9
+ export declare function McpAppCard({ sessionId, sourceId, client: suppliedClient, onMessage: suppliedOnMessage }: {
10
+ sessionId: string;
11
+ sourceId: string;
12
+ client?: BladeClient;
13
+ onMessage?: (message: McpAppMessage) => void;
14
+ }): import("react/jsx-runtime").JSX.Element;
@@ -1,7 +1,8 @@
1
1
  import type { AskUserAnswerData, ChatMessage, PostChatFollowup, ResultFeedback } from "@blade-hq/agent-client";
2
2
  import { type ReactNode } from "react";
3
+ import { type TurnNavigationSource } from "../hooks/use-turn-navigation";
3
4
  import { type FollowupInteractionEvent } from "./PostChatFollowupBlock";
4
- import type { ToolCallRenderer } from "./ToolCallBlock";
5
+ import type { ChatViewClassNames, ChatViewRenderers } from "./ChatSurface";
5
6
  interface Props {
6
7
  messages: ChatMessage[];
7
8
  postChatFollowup?: PostChatFollowup | null;
@@ -10,19 +11,52 @@ interface Props {
10
11
  sessionStatus?: string;
11
12
  askAnswers?: Record<string, AskUserAnswerData>;
12
13
  onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void;
13
- toolCallRenderer?: ToolCallRenderer;
14
+ hidePlanUpdateTools?: boolean;
15
+ /** 无消息时替换默认空状态。 */
14
16
  emptyState?: ReactNode;
15
- className?: string;
17
+ /** 各可见区块的 className 落点;键与元素上的 `blade-chat-*` 锚点一一对应。 */
18
+ classNames?: ChatViewClassNames;
19
+ /** 区块级渲染器;返回 null 走默认渲染。 */
20
+ renderers?: ChatViewRenderers;
16
21
  /** 会话 id;传给助手消息的统一上下文。 */
17
22
  sessionId?: string;
18
23
  isViewer?: boolean;
19
24
  onFollowupInteraction?: (event: FollowupInteractionEvent) => void;
20
25
  resultFeedbackByEntry?: ReadonlyMap<string, ResultFeedback>;
21
26
  onResultFeedbackSaved?: (feedback: ResultFeedback) => void;
27
+ historyPaging?: {
28
+ hasOlder: boolean;
29
+ loading: boolean;
30
+ /** Resolves true only when the paging cursor advanced. */
31
+ loadOlder: (beforeCommit?: () => void) => Promise<boolean>;
32
+ };
33
+ /**
34
+ * 消息导航的数据源(目录 + 定位)。不传就没有导航轨道,也不会请求目录——
35
+ * 纯 LLM 会话与自带入口的宿主都属于这种。
36
+ */
37
+ turnNavigation?: TurnNavigationSource;
38
+ /**
39
+ * 用户点了停止(还没等服务端确认)——打字机式显示节流要在这一刻立刻整段
40
+ * 显示已到达内容,不能等 chat:stop 的服务端回包才响应。跟 isStreaming
41
+ * 翻转成 false 不是一回事:那个信号来得晚,点击到服务端确认之间有一段
42
+ * 网络往返,这段时间里节流动画不该继续播。
43
+ */
44
+ isStopping?: boolean;
45
+ /**
46
+ * 打字机式显示节流是否还没追平目标全文——调用方需要跟 isStreaming 做
47
+ * `||` 判断输入框"运行中"状态,否则输入框会先于文字播完就翻回可输入。
48
+ */
49
+ onRevealingChange?: (revealing: boolean) => void;
50
+ /**
51
+ * 是否正在回放(演示 / 彩排)而非真正的实时直播——回放期间即使内容在
52
+ * 增长也不进入打字机式节流,直接整段显示:这是"流式与历史必须产生相同
53
+ * 结果"这条硬规则的一部分,节流只在真正的实时直播里生效。
54
+ */
55
+ isReplay?: boolean;
22
56
  }
23
57
  /**
24
58
  * 消息列表:use-stick-to-bottom 自动滚动 + 按轮次分组渲染。
25
59
  * 相比第一方版本去掉了轮次导航栏、吸顶状态条与规划摘要卡片。
26
60
  */
27
- export declare function MessageList({ messages, postChatFollowup, onSuggestion, isStreaming, sessionStatus, askAnswers, onAnswer, toolCallRenderer, emptyState, className, sessionId, isViewer, onFollowupInteraction, resultFeedbackByEntry, onResultFeedbackSaved, }: Props): import("react/jsx-runtime").JSX.Element;
61
+ export declare function MessageList({ messages, postChatFollowup, onSuggestion, isStreaming, sessionStatus, askAnswers, onAnswer, hidePlanUpdateTools, emptyState, classNames, renderers, sessionId, isViewer, onFollowupInteraction, resultFeedbackByEntry, onResultFeedbackSaved, historyPaging, turnNavigation, isStopping, onRevealingChange, isReplay, }: Props): import("react/jsx-runtime").JSX.Element;
28
62
  export {};
@@ -0,0 +1,32 @@
1
+ import type { ChatMessage, ToolCallInfo } from "@blade-hq/agent-client";
2
+ export type PlanStepStatus = "pending" | "in_progress" | "completed";
3
+ export interface PlanUpdateData {
4
+ plan: Array<{
5
+ step: string;
6
+ status: PlanStepStatus;
7
+ }>;
8
+ }
9
+ export declare const PLAN_AUTO_COLLAPSE_MS = 5000;
10
+ export declare function isPlanUpdateTool(toolCall: Pick<ToolCallInfo, "name">): boolean;
11
+ export interface PlanUpdateDisplayState {
12
+ current: ToolCallInfo | null;
13
+ updating: boolean;
14
+ }
15
+ export declare function getPlanUpdateDisplayState(messages: ChatMessage[]): PlanUpdateDisplayState;
16
+ export declare function parsePlanUpdate(argumentsJson: string): PlanUpdateData | null;
17
+ export declare function pickCurrentPlanStep(plan: PlanUpdateData["plan"]): PlanUpdateData["plan"][number] | null;
18
+ export declare function PlanUpdateBlock({ toolCall, running, autoReveal, className, }: {
19
+ toolCall: ToolCallInfo;
20
+ running?: boolean;
21
+ /** 实时收到的新进度短暂展开;加载历史时保持默认收起。 */
22
+ autoReveal?: boolean;
23
+ className?: string;
24
+ }): import("react/jsx-runtime").JSX.Element | null;
25
+ export declare function CurrentPlanPanel({ messages, running, revealRevision, sessionId, className, }: {
26
+ messages: ChatMessage[];
27
+ running?: boolean;
28
+ /** 仅实时成功的 update_plan 递增;历史 hydration 不得改变。 */
29
+ revealRevision?: number;
30
+ sessionId?: string;
31
+ className?: string;
32
+ }): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,47 @@
1
+ import type { BladeClient, SessionPluginConfigState } from "@blade-hq/agent-client";
2
+ import type { PluginConnectorItem } from "./plugin-connector";
3
+ export interface ConfiguringPlugin {
4
+ name: string;
5
+ /**
6
+ * 这次打开表单是不是「为了启用它」。
7
+ *
8
+ * 只有用户在开关上表达了启用意图时才为 true:已经跑起来的插件再保存配置
9
+ * 不应该顺手重启 / 重新激活(见 SessionPluginSelector 的历史约定)。
10
+ */
11
+ pendingActivation: boolean;
12
+ }
13
+ export interface PluginConnectorListProps {
14
+ client: BladeClient;
15
+ /** 会话内插件用会话接口取图标;首页候选(null)用账号级目录。 */
16
+ sessionId: string | null;
17
+ items: PluginConnectorItem[];
18
+ /** 正在提交意图的行;开关转圈,但仍然可点(点是取消)。 */
19
+ inFlight: Record<string, true>;
20
+ /** 有启用/停用请求在飞,会写服务端的行先锁住次要操作。 */
21
+ busy: boolean;
22
+ /** 上一次操作失败,给出重试入口。 */
23
+ mutationError: boolean;
24
+ onToggle: (item: PluginConnectorItem, next: boolean) => void;
25
+ onRetry: (item: PluginConnectorItem) => void;
26
+ onEditConfig: (item: PluginConnectorItem) => void;
27
+ configuring: ConfiguringPlugin | null;
28
+ onConfigSaved: (state: SessionPluginConfigState) => void;
29
+ onConfigClose: () => void;
30
+ /** 开关不可用时说明原因,而不是静默禁用。 */
31
+ disabledReason?: string | null;
32
+ /**
33
+ * 列表本身不可用(读取失败 / 搜索无结果)时的说明;有值时不再叠加「还没装插件」,
34
+ * 否则会把一次故障或一次没搜到说成「你这个账号没装东西」。
35
+ */
36
+ unavailableText?: string | null;
37
+ emptyText: string;
38
+ /** 首页待选模式:开关的语义是"待选",不是"已经启用"。 */
39
+ draft?: boolean;
40
+ }
41
+ /**
42
+ * 连接器面板里的插件列表。
43
+ *
44
+ * 首页与会话共用同一份渲染:开关是开关,候选是候选,行的存在与状态无关——
45
+ * 没启用、没图标也要显示名称和占位,不隐藏整行。
46
+ */
47
+ export declare function PluginConnectorList({ client, sessionId, items, inFlight, busy, mutationError, onToggle, onRetry, onEditConfig, configuring, onConfigSaved, onConfigClose, disabledReason, unavailableText, emptyText, draft, }: PluginConnectorListProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,22 @@
1
+ import type { BladeClient } from "@blade-hq/agent-client";
2
+ import type { TriggerSummary } from "./connector-panel-types";
3
+ export interface PluginTriggerStackProps {
4
+ client: BladeClient;
5
+ /** 会话内图标走会话接口;首页待选传 `null`(走账号级目录图标)。 */
6
+ sessionId: string | null;
7
+ /** 折叠结果,来自 `triggerSummary`——折叠规则只有那一处。 */
8
+ summary: TriggerSummary;
9
+ className?: string;
10
+ }
11
+ /**
12
+ * 触发器上的插件头像堆叠:前三枚圆形头像 + 其余折成 `+N`。
13
+ *
14
+ * 没有图标 token 的插件也用显示名首字占一枚圆——它和别的插件长得不一样,
15
+ * 「我选过它」就看得见;早先把这类项直接剔掉,选中后入口毫无变化。
16
+ *
17
+ * 圆环色取 `--background`(盖住被压住的那枚),与整站头像堆叠同一约定,见
18
+ * `apps/web/src/components/agent-board/ProjectMembers.tsx`:容器没有自己的底色时,
19
+ * 环就等于容器露出来的那层底。图标裁成圆形:插件 logo 多是方的,套在圆按钮里
20
+ * 「方套圆」才是那个视觉冲突的来源。
21
+ */
22
+ export declare function PluginTriggerStack({ client, sessionId, summary, className }: PluginTriggerStackProps): import("react/jsx-runtime").JSX.Element | null;
@@ -48,7 +48,7 @@ export type FollowupInteractionEvent = {
48
48
  export declare function HistoricalResultFeedback({ feedback }: {
49
49
  feedback: SavedResultFeedback;
50
50
  }): import("react/jsx-runtime").JSX.Element;
51
- export declare function PostChatFollowupBlock({ followup, sessionId, onSuggestion, isViewer, onInteraction, savedFeedback, onFeedbackSaved, }: {
51
+ export declare function PostChatFollowupBlock({ followup, sessionId, onSuggestion, isViewer, onInteraction, savedFeedback, onFeedbackSaved, className, suggestionClassName, }: {
52
52
  followup: PostChatFollowup;
53
53
  sessionId?: string;
54
54
  onSuggestion?: (value: string) => void;
@@ -56,4 +56,8 @@ export declare function PostChatFollowupBlock({ followup, sessionId, onSuggestio
56
56
  onInteraction?: (event: FollowupInteractionEvent) => void;
57
57
  savedFeedback?: SavedResultFeedback;
58
58
  onFeedbackSaved?: (feedback: SavedResultFeedback) => void;
59
+ /** 追加到跟进区根节点的类(`ChatViewClassNames.followup`)。 */
60
+ className?: string;
61
+ /** 追加到每个「下一步」建议按钮的类(`ChatViewClassNames.followupAction`)。 */
62
+ suggestionClassName?: string;
59
63
  }): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,19 @@
1
+ import type { BladeClient } from "@blade-hq/agent-client";
2
+ export interface SessionMemoryToggleProps {
3
+ sessionId: string;
4
+ enabled: boolean;
5
+ /** 不在 BladeProvider 内使用时显式传入,与宿主现有 BladeClient 复用连接和鉴权。 */
6
+ client?: BladeClient;
7
+ disabled?: boolean;
8
+ label?: string;
9
+ className?: string;
10
+ labelClassName?: string;
11
+ inputClassName?: string;
12
+ onSaved?: (sessionId: string, enabled: boolean) => void;
13
+ onError?: (error: unknown) => void;
14
+ }
15
+ /**
16
+ * 当前会话记忆开关。同一 client + sessionId 的所有实例共享保存锁,避免多个聊天入口
17
+ * 或会话切换时发出乱序更新;服务端成功后才通过 onSaved 交还权威值。
18
+ */
19
+ export declare function SessionMemoryToggle({ sessionId, enabled, client: clientProp, disabled, label, className, labelClassName, inputClassName, onSaved, onError, }: SessionMemoryToggleProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import type { BladeClient, SessionPlugin, SessionPluginConfigState } from "@blade-hq/agent-client";
2
+ export interface SessionPluginConfigDialogProps {
3
+ client: BladeClient;
4
+ sessionId: string;
5
+ plugin: Pick<SessionPlugin, "name" | "display_name" | "icon">;
6
+ /** Called after every successful save with the freshly published state. */
7
+ onSaved?: (state: SessionPluginConfigState) => void;
8
+ onClose: () => void;
9
+ }
10
+ export declare function SessionPluginConfigDialog({ client, sessionId, plugin, ...props }: SessionPluginConfigDialogProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import type { SessionPluginConfigError, SessionPluginConfigSet, SessionPluginConfigState } from "@blade-hq/agent-client";
2
+ export interface SessionPluginConfigDraft {
3
+ set: SessionPluginConfigSet[];
4
+ remove: string[];
5
+ }
6
+ export interface SessionPluginConfigFormProps {
7
+ config: SessionPluginConfigState;
8
+ submitting: boolean;
9
+ errors: SessionPluginConfigError[];
10
+ failure: string | null;
11
+ onSubmit: (draft: SessionPluginConfigDraft) => void;
12
+ onCancel: () => void;
13
+ }
14
+ export declare function SessionPluginConfigForm({ config, submitting, errors, failure, onSubmit, onCancel, }: SessionPluginConfigFormProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import type { BladeClient, SessionPlugin } from "@blade-hq/agent-client";
2
+ export interface SessionPluginIconProps {
3
+ client: BladeClient;
4
+ /**
5
+ * 会话内已准备的包。传 `null` 时改用账号级目录图标(`/api/plugins/{name}/icon`),
6
+ * 这样首页这种还没有会话的地方也能显示包内图标。
7
+ */
8
+ sessionId: string | null;
9
+ plugin: Pick<SessionPlugin, "name" | "icon">;
10
+ /** 边长,默认 20。 */
11
+ size?: number;
12
+ /**
13
+ * 外框形状:`rounded`(默认,面板列表里的小方角)或 `circle`(触发器上的头像堆叠)。
14
+ * 默认值不动,避免改到列表与第三方接入方的观感。
15
+ */
16
+ shape?: "rounded" | "circle";
17
+ className?: string;
18
+ }
19
+ export interface SessionPluginLabelProps {
20
+ plugin: Pick<SessionPlugin, "name" | "display_name">;
21
+ }
22
+ /**
23
+ * Business name for a plugin, falling back to the technical ID.
24
+ *
25
+ * The ID stays authoritative for paths, authorization, caching and calls; this
26
+ * helper only decides what a user reads. Keep it shared — one place decides.
27
+ */
28
+ export declare function sessionPluginLabel(plugin: Pick<SessionPlugin, "name" | "display_name">): string;
29
+ /** Package-local plugin icon, with a stable generic placeholder. */
30
+ export declare function SessionPluginIcon({ client, sessionId, plugin, size, shape, className }: SessionPluginIconProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,10 @@
1
+ import type { BladeClient, SessionPluginActivation } from "@blade-hq/agent-client";
2
+ export interface SessionPluginSelectorProps {
3
+ client: BladeClient;
4
+ sessionId: string;
5
+ className?: string;
6
+ side?: "top" | "bottom";
7
+ onChange?: (plugin: SessionPluginActivation) => void;
8
+ }
9
+ /** Shared selector for AgentChat and the built-in Web; no external query provider required. */
10
+ export declare function SessionPluginSelector(props: SessionPluginSelectorProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,74 @@
1
+ import { type QueuedMessage, type SessionQueueSnapshot } from "@blade-hq/agent-client";
2
+ import { type JSX } from "react";
3
+ export interface SessionQueuePanelProps {
4
+ snapshot: SessionQueueSnapshot;
5
+ /** 当前是否有可接收补充的运行任务(决定「立刻发送」是否可用) */
6
+ canDeliver: boolean;
7
+ /** 正在等服务端确认的条目 id:禁用该条目的操作,避免重复提交 */
8
+ pendingItemId?: string | null;
9
+ /** 冲突/失败提示,例如「该消息已开始执行」;展示在面板顶部 */
10
+ notice?: string | null;
11
+ onResume: () => void;
12
+ onCancel: (item: QueuedMessage) => void;
13
+ onEdit: (item: QueuedMessage, content: string) => boolean | undefined | Promise<boolean>;
14
+ onRequeue?: (content: string) => Promise<boolean>;
15
+ onMove: (item: QueuedMessage, delta: -1 | 1) => void;
16
+ onReorder: (orderedIds: string[]) => void;
17
+ onDeliver: (item: QueuedMessage) => void;
18
+ /**
19
+ * 队列内容与外层留白的宽度类,要和下面输入框用同一个值。
20
+ *
21
+ * 队列卡片是"比输入框窄一圈、露在它上方"的形态,所以这里的宽度必须跟着
22
+ * 输入框走:输入框宽度由调用方决定(内置 Web 默认 max-w-3xl,软件工厂用
23
+ * `max-w-[748px]!` 覆盖,SDK 侧是 max-w-[748px])。写死一个值的话,两种
24
+ * 布局里总有一种对不齐——之前写死 max-w-[748px] 且外层 px-4,而输入框
25
+ * 外层是 px-5,软件工厂恰好都是 748 看着还行,普通聊天里输入框是 768,
26
+ * 队列连缩进一起窄了 22px。
27
+ */
28
+ contentWidthClassName?: string;
29
+ /** 根节点附加类。宿主用它在根上重设 `--blade-chat-queue-gutter`(或别的变量)。 */
30
+ rootClassName?: string;
31
+ /**
32
+ * 面板占位状态变化时回调(true = 正在占位)。
33
+ *
34
+ * 面板有本地 `editing` 状态:用户编辑到一半、那条等待消息被服务端拿走后,
35
+ * 面板仍要留在原地保住没保存的正文(见 `detachedEdit`)。这时 `items` 是空的,
36
+ * 光凭 snapshot 判不出面板还在,宿主就会把输入框上两角恢复成圆角、接缝上露出
37
+ * 尖角。让面板自己上报,宿主不必也无法复刻这个判据。
38
+ */
39
+ onVisibleChange?: (visible: boolean) => void;
40
+ /**
41
+ * 面板是否真的紧贴下面的输入框。
42
+ *
43
+ * 中间夹着别的内容时(MCP 暂存内容、内置 Web 的 `composerBanner`)两者并不
44
+ * 相接,这时面板下两角也要圆起来——直角是"被下面的输入框接住"的表示,露出
45
+ * 直角却下面空着一块,看起来像被削掉一角。
46
+ *
47
+ * 不传时读 context(见 `QueuePanelAdjacencyContext`):面板通常由宿主创建、
48
+ * 挂进 `ChatSurface`,而"中间有没有内容"是 `ChatSurface` 内部的 state,
49
+ * 宿主拿不到。内置 Web 自己管布局,直接把值传进来即可。
50
+ */
51
+ mergesWithInputBelow?: boolean;
52
+ }
53
+ /**
54
+ * 队列面板此刻会不会占位。
55
+ *
56
+ * 输入框要据此把自己的上两角改成直角:队列贴在它上方、而且比它窄一圈,
57
+ * 输入框上角若还是 22px 圆弧,那两段弧会从队列两侧露出来,形成两个尖角。
58
+ *
59
+ * 抽成一个函数而不是两边各判一次,是为了让"面板可见"和"输入框顶角改直角"
60
+ * 永远同步——这正是仓库里反复出现的"改了一个入口漏了另一个"。
61
+ * 传入的 `editing` 是面板自己的本地编辑态,默认 false。
62
+ */
63
+ export declare function isQueuePanelVisible(snapshot: Pick<SessionQueueSnapshot, "items" | "paused">, notice: string | null | undefined, editing?: boolean): boolean;
64
+ /**
65
+ * 会话消息队列面板:输入框上方的「待执行 · N」区域。
66
+ *
67
+ * 一行一条:左边三条横向的拖动手柄、中间单行截断的正文、右边 hover 才出现的操作
68
+ * (立刻发送 / 编辑 / 删除)。顺序靠拖动整行调整,手柄上也能用 Alt+↑/↓ 调(键盘
69
+ * 可达性不能因为去掉上下箭头按钮就没了)。
70
+ *
71
+ * 纯展示组件,不持有会话状态;能否编辑/删除/补充全部用 agent-client 的判定函数,
72
+ * 不在本包重写一份。拖动与键盘调整最终都由调用方提交完整的 pending id 顺序。
73
+ */
74
+ export declare function SessionQueuePanel({ snapshot, canDeliver, pendingItemId, notice, onResume, onCancel, onEdit, onRequeue, onMove, onReorder, onDeliver, contentWidthClassName, rootClassName, onVisibleChange, mergesWithInputBelow: mergesWithInputBelowProp, }: SessionQueuePanelProps): JSX.Element | null;